From e0fd611c768d98ef6475f9872aaa08bb533bad82 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 13 Dec 2024 14:35:02 +0100 Subject: [PATCH 001/335] used TbMsgProto instead of ByteString --- .../actors/ruleChain/DefaultTbContext.java | 5 +- .../RuleChainActorMessageProcessor.java | 2 +- .../RuleNodeActorMessageProcessor.java | 2 +- .../device/DeviceProvisionServiceImpl.java | 3 +- ...riginatorIdTbRuleEngineSubmitStrategy.java | 7 +- ...TbRuleEngineProcessingStrategyFactory.java | 6 +- .../TbRuleEngineQueueConsumerManager.java | 14 +++- .../rpc/DefaultTbRuleEngineRpcService.java | 2 +- .../DefaultRuleEngineCallService.java | 2 +- .../actors/rule/DefaultTbContextTest.java | 12 +-- .../controller/TenantControllerTest.java | 3 +- .../TbRuleEngineQueueConsumerManagerTest.java | 2 +- .../ruleengine/TbRuleEngineStrategyTest.java | 2 +- .../DefaultTbRuleEngineRpcServiceTest.java | 2 +- .../DefaultRuleEngineCallServiceTest.java | 2 +- .../thingsboard/server/common/msg/TbMsg.java | 82 ++++++++++--------- common/proto/src/main/proto/queue.proto | 8 +- .../common/TbRuleEngineProducerService.java | 2 +- 18 files changed, 90 insertions(+), 68 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 0070eba4a1..6ebcc1843e 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -230,7 +230,8 @@ public class DefaultTbContext implements TbContext { TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder() .setTenantIdMSB(getTenantId().getId().getMostSignificantBits()) .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits()) - .setTbMsg(TbMsg.toByteString(tbMsg)).build(); + .setTbMsgProto(TbMsg.toProto(tbMsg)) + .build(); mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg, callback); } @@ -309,7 +310,7 @@ public class DefaultTbContext implements TbContext { TransportProtos.ToRuleEngineMsg.Builder msg = TransportProtos.ToRuleEngineMsg.newBuilder() .setTenantIdMSB(getTenantId().getId().getMostSignificantBits()) .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits()) - .setTbMsg(TbMsg.toByteString(tbMsg)) + .setTbMsgProto(TbMsg.toProto(tbMsg)) .addAllRelationTypes(relationTypes); if (failureMessage != null) { msg.setFailureMessage(failureMessage); diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 460da228c3..917066cd2d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -370,7 +370,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor(tbMsg.getId(), msg), callback); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByOriginatorIdTbRuleEngineSubmitStrategy.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByOriginatorIdTbRuleEngineSubmitStrategy.java index 1ee6df8361..3c11095894 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByOriginatorIdTbRuleEngineSubmitStrategy.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByOriginatorIdTbRuleEngineSubmitStrategy.java @@ -34,7 +34,12 @@ public class SequentialByOriginatorIdTbRuleEngineSubmitStrategy extends Sequenti @Override protected EntityId getEntityId(TransportProtos.ToRuleEngineMsg msg) { try { - MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(msg.getTbMsg()); + MsgProtos.TbMsgProto proto; + if (msg.getTbMsg().isEmpty()) { + proto = msg.getTbMsgProto(); + } else { + proto = MsgProtos.TbMsgProto.parseFrom(msg.getTbMsg()); + } return EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB())); } catch (InvalidProtocolBufferException e) { log.warn("[{}] Failed to parse TbMsg: {}", queueName, msg); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java index 4712497524..597bcef098 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java @@ -125,7 +125,7 @@ public class TbRuleEngineProcessingStrategyFactory { } log.debug("[{}] Going to reprocess {} messages", queueName, toReprocess.size()); if (log.isTraceEnabled()) { - toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); + toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromProto(result.getQueueName(), msg.getValue().getTbMsgProto(), msg.getValue().getTbMsg(), TbMsgCallback.EMPTY))); } if (pauseBetweenRetries > 0) { try { @@ -164,10 +164,10 @@ public class TbRuleEngineProcessingStrategyFactory { log.debug("[{}] Reprocessing skipped for {} failed and {} timeout messages", queueName, result.getFailedMap().size(), result.getPendingMap().size()); } if (log.isTraceEnabled()) { - result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); + result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromProto(result.getQueueName(), msg.getValue().getTbMsgProto(), msg.getValue().getTbMsg(), TbMsgCallback.EMPTY))); } if (log.isTraceEnabled()) { - result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromBytes(result.getQueueName(), msg.getValue().getTbMsg().toByteArray(), TbMsgCallback.EMPTY))); + result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromProto(result.getQueueName(), msg.getValue().getTbMsgProto(), msg.getValue().getTbMsg(), TbMsgCallback.EMPTY))); } return new TbRuleEngineProcessingDecision(true, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java index c2823d3c00..231d7b205d 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java @@ -170,7 +170,7 @@ public class TbRuleEngineQueueConsumerManager extends MainQueueConsumerManager relationTypes; @@ -199,7 +199,7 @@ public class TbRuleEngineQueueConsumerManager extends MainQueueConsumerManager> pending : map.entrySet()) { ToRuleEngineMsg tmp = pending.getValue().getValue(); - TbMsg tmpMsg = TbMsg.fromBytes(config.getName(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY); + TbMsg tmpMsg = TbMsg.fromProto(config.getName(), tmp.getTbMsgProto(), tmp.getTbMsg(), TbMsgCallback.EMPTY); RuleNodeInfo ruleNodeInfo = ctx.getLastVisitedRuleNode(pending.getKey()); if (printAll) { log.trace("[{}][{}] {} to process message: {}, Last Rule Node: {}", queueKey, TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); @@ -228,7 +228,13 @@ public class TbRuleEngineQueueConsumerManager extends MainQueueConsumerManager msg : msgs) { try { - MsgProtos.TbMsgProto tbMsgProto = MsgProtos.TbMsgProto.parseFrom(msg.getValue().getTbMsg().toByteArray()); + MsgProtos.TbMsgProto tbMsgProto; + if (msg.getValue().getTbMsg().isEmpty()) { + tbMsgProto = msg.getValue().getTbMsgProto(); + } else { + tbMsgProto = MsgProtos.TbMsgProto.parseFrom(msg.getValue().getTbMsg()); + } + EntityId originator = EntityIdFactory.getByTypeAndUuid(tbMsgProto.getEntityType(), new UUID(tbMsgProto.getEntityIdMSB(), tbMsgProto.getEntityIdLSB())); TopicPartitionInfo tpi = ctx.getPartitionService().resolve(ServiceType.TB_RULE_ENGINE, config.getName(), TenantId.SYS_TENANT_ID, originator); diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java index debc1cb987..80b3bd7a37 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java @@ -132,7 +132,7 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi TransportProtos.RestApiCallResponseMsgProto msg = TransportProtos.RestApiCallResponseMsgProto.newBuilder() .setRequestIdMSB(requestId.getMostSignificantBits()) .setRequestIdLSB(requestId.getLeastSignificantBits()) - .setResponse(TbMsg.toByteString(tbMsg)) + .setResponseProto(TbMsg.toProto(tbMsg)) .build(); clusterService.pushNotificationToCore(serviceId, msg, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallService.java b/application/src/main/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallService.java index 6f4d18b94e..e435c8ad36 100644 --- a/application/src/main/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallService.java +++ b/application/src/main/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallService.java @@ -73,7 +73,7 @@ public class DefaultRuleEngineCallService implements RuleEngineCallService { UUID requestId = new UUID(restApiCallResponseMsg.getRequestIdMSB(), restApiCallResponseMsg.getRequestIdLSB()); Consumer consumer = requests.remove(requestId); if (consumer != null) { - consumer.accept(TbMsg.fromBytes(null, restApiCallResponseMsg.getResponse().toByteArray(), TbMsgCallback.EMPTY)); + consumer.accept(TbMsg.fromProto(null, restApiCallResponseMsg.getResponseProto(), restApiCallResponseMsg.getResponse(), TbMsgCallback.EMPTY)); } else { log.trace("[{}] Unknown or stale rest api call response received", requestId); } diff --git a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java index 21a4a45c6f..4370b98b2d 100644 --- a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java +++ b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java @@ -644,11 +644,11 @@ class DefaultTbContextTest { ToRuleEngineMsg actualToRuleEngineMsg = toRuleEngineMsgCaptor.getValue(); assertThat(actualToRuleEngineMsg).usingRecursiveComparison() - .ignoringFields("tbMsg_") + .ignoringFields("tbMsgProto_") .isEqualTo(ToRuleEngineMsg.newBuilder() .setTenantIdMSB(TENANT_ID.getId().getMostSignificantBits()) .setTenantIdLSB(TENANT_ID.getId().getLeastSignificantBits()) - .setTbMsg(TbMsg.toByteString(expectedTbMsg)) + .setTbMsgProto(TbMsg.toProto(expectedTbMsg)) .addAllRelationTypes(List.of(connectionType)).build()); var simpleTbQueueCallback = simpleTbQueueCallbackCaptor.getValue(); @@ -701,11 +701,11 @@ class DefaultTbContextTest { ToRuleEngineMsg actualToRuleEngineMsg = toRuleEngineMsgCaptor.getValue(); assertThat(actualToRuleEngineMsg).usingRecursiveComparison() - .ignoringFields("tbMsg_") + .ignoringFields("tbMsgProto_") .isEqualTo(ToRuleEngineMsg.newBuilder() .setTenantIdMSB(TENANT_ID.getId().getMostSignificantBits()) .setTenantIdLSB(TENANT_ID.getId().getLeastSignificantBits()) - .setTbMsg(TbMsg.toByteString(expectedTbMsg)) + .setTbMsgProto(TbMsg.toProto(expectedTbMsg)) .build()); var simpleTbQueueCallback = simpleTbQueueCallbackCaptor.getValue(); @@ -883,11 +883,11 @@ class DefaultTbContextTest { ToRuleEngineMsg actualToRuleEngineMsg = toRuleEngineMsgCaptor.getValue(); assertThat(actualToRuleEngineMsg).usingRecursiveComparison() - .ignoringFields("tbMsg_") + .ignoringFields("tbMsgProto_.id_") .isEqualTo(ToRuleEngineMsg.newBuilder() .setTenantIdMSB(TENANT_ID.getId().getMostSignificantBits()) .setTenantIdLSB(TENANT_ID.getId().getLeastSignificantBits()) - .setTbMsg(TbMsg.toByteString(expectedTbMsg)) + .setTbMsgProto(TbMsg.toProto(expectedTbMsg)) .setFailureMessage(EXCEPTION_MSG) .addAllRelationTypes(List.of(TbNodeConnectionType.FAILURE)).build()); diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java index c8cc72c5b5..dbf40c0e30 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java @@ -747,7 +747,8 @@ public class TenantControllerTest extends AbstractControllerTest { TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) - .setTbMsg(TbMsg.toByteString(tbMsg)).build(); + .setTbMsgProto(TbMsg.toProto(tbMsg)) + .build(); tbClusterService.pushMsgToRuleEngine(tpi, tbMsg.getId(), msg, null); return tbMsg; } diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java index 66e3de13d1..bff5d165f4 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java @@ -738,7 +738,7 @@ public class TbRuleEngineQueueConsumerManagerTest { .setTenantIdMSB(tenantId.getMostSignificantBits()) .setTenantIdLSB(tenantId.getLeastSignificantBits()) .addRelationTypes("Success") - .setTbMsg(TbMsg.toByteString(tbMsg)) + .setTbMsgProto(TbMsg.toProto(tbMsg)) .build()); } diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java index 098c622e31..435304fdef 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java @@ -263,7 +263,7 @@ public class TbRuleEngineStrategyTest { .setTenantIdMSB(tenantId.getMostSignificantBits()) .setTenantIdLSB(tenantId.getLeastSignificantBits()) .addRelationTypes("Success") - .setTbMsg(TbMsg.toByteString(tbMsg)) + .setTbMsgProto(TbMsg.toProto(tbMsg)) .build()); } diff --git a/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java b/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java index 2cab5f991a..ea0b3b91f5 100644 --- a/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java @@ -50,7 +50,7 @@ class DefaultTbRuleEngineRpcServiceTest { var restApiCallResponseMsgProto = TransportProtos.RestApiCallResponseMsgProto.newBuilder() .setRequestIdMSB(requestId.getMostSignificantBits()) .setRequestIdLSB(requestId.getLeastSignificantBits()) - .setResponse(TbMsg.toByteString(msg)) + .setResponseProto(TbMsg.toProto(msg)) .build(); // WHEN diff --git a/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java b/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java index c49149bd07..a4ee9cda71 100644 --- a/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java @@ -132,7 +132,7 @@ public class DefaultRuleEngineCallServiceTest { private TransportProtos.RestApiCallResponseMsgProto getResponse(UUID requestId, TbMsg msg) { return TransportProtos.RestApiCallResponseMsgProto.newBuilder() - .setResponse(TbMsg.toByteString(msg)) + .setResponseProto(TbMsg.toProto(msg)) .setRequestIdMSB(requestId.getMostSignificantBits()) .setRequestIdLSB(requestId.getLeastSignificantBits()) .build(); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index 47fb80c90e..88e7c6b691 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -371,11 +371,7 @@ public final class TbMsg implements Serializable { this.callback = Objects.requireNonNullElse(callback, TbMsgCallback.EMPTY); } - public static ByteString toByteString(TbMsg msg) { - return ByteString.copyFrom(toByteArray(msg)); - } - - public static byte[] toByteArray(TbMsg msg) { + public static MsgProtos.TbMsgProto toProto(TbMsg msg) { MsgProtos.TbMsgProto.Builder builder = MsgProtos.TbMsgProto.newBuilder(); builder.setId(msg.getId().toString()); builder.setTs(msg.getTs()); @@ -415,47 +411,55 @@ public final class TbMsg implements Serializable { } builder.setCtx(msg.ctx.toProto()); - return builder.build().toByteArray(); + return builder.build(); } - public static TbMsg fromBytes(String queueName, byte[] data, TbMsgCallback callback) { + //TODO: added for processing old messages from queue, should be removed after release + @Deprecated(forRemoval = true) + public static TbMsg fromProto(String queueName, MsgProtos.TbMsgProto proto, ByteString data, TbMsgCallback callback) { try { - MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(data); - TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); - EntityId entityId = EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB())); - CustomerId customerId = null; - RuleChainId ruleChainId = null; - RuleNodeId ruleNodeId = null; - UUID correlationId = null; - Integer partition = null; - if (proto.getCustomerIdMSB() != 0L && proto.getCustomerIdLSB() != 0L) { - customerId = new CustomerId(new UUID(proto.getCustomerIdMSB(), proto.getCustomerIdLSB())); - } - if (proto.getRuleChainIdMSB() != 0L && proto.getRuleChainIdLSB() != 0L) { - ruleChainId = new RuleChainId(new UUID(proto.getRuleChainIdMSB(), proto.getRuleChainIdLSB())); - } - if (proto.getRuleNodeIdMSB() != 0L && proto.getRuleNodeIdLSB() != 0L) { - ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB())); - } - if (proto.getCorrelationIdMSB() != 0L && proto.getCorrelationIdLSB() != 0L) { - correlationId = new UUID(proto.getCorrelationIdMSB(), proto.getCorrelationIdLSB()); - partition = proto.getPartition(); + if (!data.isEmpty()) { + proto = MsgProtos.TbMsgProto.parseFrom(data); } - - TbMsgProcessingCtx ctx; - if (proto.hasCtx()) { - ctx = TbMsgProcessingCtx.fromProto(proto.getCtx()); - } else { - // Backward compatibility with unprocessed messages fetched from queue after update. - ctx = new TbMsgProcessingCtx(proto.getRuleNodeExecCounter()); - } - - TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; - return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), null, proto.getType(), entityId, customerId, - metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, correlationId, partition, ctx, callback); } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Could not parse protobuf for TbMsg", e); } + return fromProto(queueName, proto, callback); + } + + public static TbMsg fromProto(String queueName, MsgProtos.TbMsgProto proto, TbMsgCallback callback) { + TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); + EntityId entityId = EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB())); + CustomerId customerId = null; + RuleChainId ruleChainId = null; + RuleNodeId ruleNodeId = null; + UUID correlationId = null; + Integer partition = null; + if (proto.getCustomerIdMSB() != 0L && proto.getCustomerIdLSB() != 0L) { + customerId = new CustomerId(new UUID(proto.getCustomerIdMSB(), proto.getCustomerIdLSB())); + } + if (proto.getRuleChainIdMSB() != 0L && proto.getRuleChainIdLSB() != 0L) { + ruleChainId = new RuleChainId(new UUID(proto.getRuleChainIdMSB(), proto.getRuleChainIdLSB())); + } + if (proto.getRuleNodeIdMSB() != 0L && proto.getRuleNodeIdLSB() != 0L) { + ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB())); + } + if (proto.getCorrelationIdMSB() != 0L && proto.getCorrelationIdLSB() != 0L) { + correlationId = new UUID(proto.getCorrelationIdMSB(), proto.getCorrelationIdLSB()); + partition = proto.getPartition(); + } + + TbMsgProcessingCtx ctx; + if (proto.hasCtx()) { + ctx = TbMsgProcessingCtx.fromProto(proto.getCtx()); + } else { + // Backward compatibility with unprocessed messages fetched from queue after update. + ctx = new TbMsgProcessingCtx(proto.getRuleNodeExecCounter()); + } + + TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; + return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), null, proto.getType(), entityId, customerId, + metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, correlationId, partition, ctx, callback); } public TbMsg copyWithRuleChainId(RuleChainId ruleChainId) { diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 228a4039d2..ceef0914a7 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -20,6 +20,8 @@ package transport; option java_package = "org.thingsboard.server.gen.transport"; option java_outer_classname = "TransportProtos"; +import "tbmsg.proto"; + /** * Common data structures */ @@ -108,7 +110,8 @@ message SessionInfoProto { message RestApiCallResponseMsgProto { int64 requestIdMSB = 1; int64 requestIdLSB = 2; - bytes response = 5; + bytes response = 5 [deprecated = true]; + msgqueue.TbMsgProto responseProto = 6; } enum SessionEvent { @@ -1556,9 +1559,10 @@ message ToEdgeEventNotificationMsg { message ToRuleEngineMsg { int64 tenantIdMSB = 1; int64 tenantIdLSB = 2; - bytes tbMsg = 3; + bytes tbMsg = 3 [deprecated = true]; repeated string relationTypes = 4; string failureMessage = 5; + msgqueue.TbMsgProto tbMsgProto = 6; } message ToRuleEngineNotificationMsg { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java index 49b40e3a6d..aefce4e176 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/TbRuleEngineProducerService.java @@ -65,7 +65,7 @@ public class TbRuleEngineProducerService { log.trace("[{}][{}] Pushing to topic {} message {}", tenantId, tbMsg.getOriginator(), tpi.getFullTopicName(), tbMsg); } ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder() - .setTbMsg(TbMsg.toByteString(tbMsg)) + .setTbMsgProto(TbMsg.toProto(tbMsg)) .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()).build(); producer.send(tpi, new TbProtoQueueMsg<>(tbMsg.getId(), msg), callback); From f87aa0b1425f03ce9ec8ce64b34a7ad6b0ff5466 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 28 Jan 2025 14:35:21 +0100 Subject: [PATCH 002/335] Added new relation between rule chains if RuleChainInput node is used --- .../common/data/relation/EntityRelation.java | 1 + .../server/dao/rule/BaseRuleChainService.java | 35 +++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java index 62b5caafd3..ff45c01500 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java @@ -41,6 +41,7 @@ public class EntityRelation implements HasVersion, Serializable { public static final String EDGE_TYPE = "ManagedByEdge"; public static final String CONTAINS_TYPE = "Contains"; public static final String MANAGES_TYPE = "Manages"; + public static final String USES_TYPE = "Uses"; @Setter private EntityId from; diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 0e51cf256a..1ccc5fa95f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -71,11 +71,13 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; @@ -196,11 +198,18 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } } } - + RuleChainId ruleChainId = ruleChain.getId(); List updatedRuleNodes = new ArrayList<>(); List existingRuleNodes = getRuleChainNodes(tenantId, ruleChainMetaData.getRuleChainId()); for (RuleNode existingNode : existingRuleNodes) { relationService.deleteEntityRelations(tenantId, existingNode.getId()); + if (existingNode.getType().equals("org.thingsboard.rule.engine.flow.TbRuleChainInputNode")) { + if (existingNode.getConfiguration().has("ruleChainId")) { + RuleChainId targetRuleChainId = extractRuleChainIdFromInputNode(existingNode); + var relation = createRuleChainInputRelation(ruleChainId, targetRuleChainId); + relationService.deleteRelation(tenantId, relation); + } + } Integer index = ruleNodeIndexMap.get(existingNode.getId()); RuleNode newRuleNode = null; if (index != null) { @@ -212,7 +221,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } updatedRuleNodes.add(new RuleNodeUpdateResult(existingNode, newRuleNode)); } - RuleChainId ruleChainId = ruleChain.getId(); + if (nodes != null) { long now = System.currentTimeMillis(); for (RuleNode node : toAddOrUpdate) { @@ -225,6 +234,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC RuleNode savedNode = ruleNodeDao.save(tenantId, node); relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); + if (node.getType().equals("org.thingsboard.rule.engine.flow.TbRuleChainInputNode")) { + if (node.getConfiguration().has("ruleChainId")) { + RuleChainId targetRuleChainId = extractRuleChainIdFromInputNode(node); + var relation = createRuleChainInputRelation(ruleChainId, targetRuleChainId); + relations.add(relation); + } + } int index = nodes.indexOf(node); nodes.set(index, savedNode); ruleNodeIndexMap.put(savedNode.getId(), index); @@ -295,6 +311,21 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC return RuleChainUpdateResult.successful(updatedRuleNodes); } + private EntityRelation createRuleChainInputRelation(RuleChainId ruleChainId, RuleChainId targetRuleChainId) { + EntityRelation relation = new EntityRelation(); + relation.setFrom(ruleChainId); + relation.setTo(targetRuleChainId); + relation.setType(EntityRelation.USES_TYPE); + relation.setTypeGroup(RelationTypeGroup.COMMON); + return relation; + } + + private RuleChainId extractRuleChainIdFromInputNode(RuleNode node) { + JsonNode configuration = node.getConfiguration(); + UUID targetUuid = UUID.fromString(configuration.get("ruleChainId").asText()); + return new RuleChainId(targetUuid); + } + @Override public RuleChainMetaData loadRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId) { Validator.validateId(ruleChainId, "Incorrect rule chain id."); From f06fc5b27597500919545e09ea2b7b6d1a26b2d5 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 28 Jan 2025 14:38:35 +0100 Subject: [PATCH 003/335] imports optimization --- .../org/thingsboard/server/dao/rule/BaseRuleChainService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 1ccc5fa95f..eb78cf316e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -71,7 +71,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; From 89e7b95d1965cbb2ce251c5c5640d0327ee1856e Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 6 Feb 2025 17:53:46 +0200 Subject: [PATCH 004/335] Resources shortage notification template & rule --- .../install/ThingsboardInstallService.java | 1 + ...esourcesShortageLimitTriggerProcessor.java | 50 +++++++++++++ .../system/DefaultSystemInfoService.java | 22 ++++-- .../data/notification/NotificationType.java | 3 +- .../ResourcesShortageNotificationInfo.java | 42 +++++++++++ .../trigger/NewPlatformVersionTrigger.java | 5 -- .../trigger/ResourcesShortageTrigger.java | 71 +++++++++++++++++++ .../trigger/TaskProcessingFailureTrigger.java | 10 +-- .../config/NotificationRuleTriggerConfig.java | 3 +- .../config/NotificationRuleTriggerType.java | 3 +- ...ShortageNotificationRuleTriggerConfig.java | 48 +++++++++++++ .../DefaultNotificationSettingsService.java | 4 ++ .../notification/DefaultNotifications.java | 16 +++++ .../rule-notification-dialog.component.html | 60 ++++++++++++++++ .../rule-notification-dialog.component.ts | 15 +++- .../template-notification-dialog.component.ts | 3 +- .../app/shared/models/notification.models.ts | 16 ++++- .../en_US/notification/resources_shortage.md | 42 +++++++++++ .../assets/locale/locale.constant-en_US.json | 8 ++- 19 files changed, 398 insertions(+), 24 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageLimitTriggerProcessor.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java create mode 100644 ui-ngx/src/assets/help/en_US/notification/resources_shortage.md diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index ea4c059222..69df861f3a 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -116,6 +116,7 @@ public class ThingsboardInstallService { entityDatabaseSchemaService.createDatabaseIndexes(); // TODO: cleanup update code after each release + systemDataLoaderService.updateDefaultNotificationConfigs(false); // Runs upgrade scripts that are not possible in plain SQL. dataUpdateService.updateData(); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageLimitTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageLimitTriggerProcessor.java new file mode 100644 index 0000000000..c9c9a03f55 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageLimitTriggerProcessor.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.service.notification.rule.trigger; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.notification.info.ResourcesShortageNotificationInfo; +import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.ResourcesShortageNotificationRuleTriggerConfig; + +@Service +@RequiredArgsConstructor +public class ResourcesShortageLimitTriggerProcessor implements NotificationRuleTriggerProcessor { + + @Override + public boolean matchesFilter(ResourcesShortageTrigger trigger, ResourcesShortageNotificationRuleTriggerConfig triggerConfig) { + float usagePercent = trigger.getUsage() / 100.0f; + return switch (trigger.getResource()) { + case CPU -> usagePercent >= triggerConfig.getCpuThreshold(); + case RAM -> usagePercent >= triggerConfig.getRamThreshold(); + case STORAGE -> usagePercent >= triggerConfig.getStorageThreshold(); + }; + } + + @Override + public RuleOriginatedNotificationInfo constructNotificationInfo(ResourcesShortageTrigger trigger) { + return ResourcesShortageNotificationInfo.builder().resource(trigger.getResource().name()).usage(trigger.getUsage()).build(); + } + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.RESOURCES_SHORTAGE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index 42d468575a..53d94af8e4 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -39,6 +39,9 @@ import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger.Resource; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.stats.TbApiUsageStateClient; import org.thingsboard.server.dao.domain.DomainService; @@ -92,6 +95,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener 0; + return !providers.isEmpty(); } } return false; @@ -188,9 +192,18 @@ public class DefaultSystemInfoService extends TbApplicationEventListener tsList = new ArrayList<>(); tsList.add(new BasicTsKvEntry(ts, new BooleanDataEntry("clusterMode", false))); - getCpuUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", (long) v)))); - getMemoryUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", (long) v)))); - getDiscSpaceUsage().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("discUsage", (long) v)))); + getCpuUsage().ifPresent(v -> { + tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", (long) v))); + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(v).build()); + }); + getMemoryUsage().ifPresent(v -> { + tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", (long) v))); + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(v).build()); + }); + getDiscSpaceUsage().ifPresent(v -> { + tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("discUsage", (long) v))); + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(v).build()); + }); getCpuCount().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuCount", (long) v)))); getTotalMemory().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("totalMemory", v)))); @@ -244,4 +257,5 @@ public class DefaultSystemInfoService extends TbApplicationEventListener getTemplateData() { + return Map.of( + "resource", resource, + "usage", String.valueOf(usage) + ); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java index 84b431dd87..4f89cabd18 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java @@ -55,9 +55,4 @@ public class NewPlatformVersionTrigger implements NotificationRuleTrigger { updateInfo.getCurrentVersion(), updateInfo.getLatestVersion()); } - @Override - public long getDefaultDeduplicationDuration() { - return 0; - } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java new file mode 100644 index 0000000000..b35d0b8470 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +import java.io.Serial; +import java.util.concurrent.TimeUnit; + +@Data +@Builder +public class ResourcesShortageTrigger implements NotificationRuleTrigger { + + @Serial + private static final long serialVersionUID = 6024216015202949570L; + + private Resource resource; + private Integer usage; + + @Override + public TenantId getTenantId() { + return TenantId.SYS_TENANT_ID; + } + + @Override + public EntityId getOriginatorEntityId() { + return TenantId.SYS_TENANT_ID; + } + + @Override + public boolean deduplicate() { + return true; + } + + @Override + public String getDeduplicationKey() { + return resource.name(); + } + + @Override + public long getDefaultDeduplicationDuration() { + return TimeUnit.HOURS.toMillis(1); + } + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.RESOURCES_SHORTAGE; + } + + public enum Resource { + CPU, RAM, STORAGE + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/TaskProcessingFailureTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/TaskProcessingFailureTrigger.java index 3626e09c0b..f531aabc46 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/TaskProcessingFailureTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/TaskProcessingFailureTrigger.java @@ -22,10 +22,15 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import java.io.Serial; + @Data @Builder public class TaskProcessingFailureTrigger implements NotificationRuleTrigger { + @Serial + private static final long serialVersionUID = 5606203770553105345L; + private final HousekeeperTask task; private final int attempt; private final Throwable error; @@ -45,9 +50,4 @@ public class TaskProcessingFailureTrigger implements NotificationRuleTrigger { return task.getEntityId(); } - @Override - public boolean deduplicate() { - return false; - } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java index 1621ec8120..c8a69e559f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java @@ -38,7 +38,8 @@ import java.io.Serializable; @Type(value = RateLimitsNotificationRuleTriggerConfig.class, name = "RATE_LIMITS"), @Type(value = EdgeConnectionNotificationRuleTriggerConfig.class, name = "EDGE_CONNECTION"), @Type(value = EdgeCommunicationFailureNotificationRuleTriggerConfig.class, name = "EDGE_COMMUNICATION_FAILURE"), - @Type(value = TaskProcessingFailureNotificationRuleTriggerConfig.class, name = "TASK_PROCESSING_FAILURE") + @Type(value = TaskProcessingFailureNotificationRuleTriggerConfig.class, name = "TASK_PROCESSING_FAILURE"), + @Type(value = ResourcesShortageNotificationRuleTriggerConfig.class, name = "RESOURCES_SHORTAGE") }) public interface NotificationRuleTriggerConfig extends Serializable { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java index 8846ae284d..3be4f4da73 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java @@ -32,7 +32,8 @@ public enum NotificationRuleTriggerType { ENTITIES_LIMIT(false), API_USAGE_LIMIT(false), RATE_LIMITS(false), - TASK_PROCESSING_FAILURE(false); + TASK_PROCESSING_FAILURE(false), + RESOURCES_SHORTAGE(false); private final boolean tenantLevel; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..17de9e9246 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import jakarta.validation.constraints.Max; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ResourcesShortageNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + @Serial + private static final long serialVersionUID = 339395299693241424L; + + private String resource; + @Max(1) + private float cpuThreshold; // in percents + @Max(1) + private float ramThreshold; // in percents + @Max(1) + private float storageThreshold; // in percents + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.RESOURCES_SHORTAGE; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java index 1404eaff67..4ab22ea58d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java @@ -186,6 +186,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId()); defaultNotifications.create(tenantId, DefaultNotifications.newPlatformVersion, sysAdmins.getId()); defaultNotifications.create(tenantId, DefaultNotifications.taskProcessingFailure, tenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.resourcesShortage, sysAdmins.getId()); return; } @@ -221,6 +222,9 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS if (!isNotificationConfigured(tenantId, NotificationType.TASK_PROCESSING_FAILURE)) { defaultNotifications.create(tenantId, DefaultNotifications.taskProcessingFailure, sysAdmins.getId()); } + if (!isNotificationConfigured(tenantId, NotificationType.RESOURCES_SHORTAGE)) { + defaultNotifications.create(tenantId, DefaultNotifications.resourcesShortage, sysAdmins.getId()); + } } else { var requiredNotificationTypes = List.of(NotificationType.EDGE_CONNECTION, NotificationType.EDGE_COMMUNICATION_FAILURE); var existingNotificationTypes = notificationTemplateService.findNotificationTemplatesByTenantIdAndNotificationTypes( diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index 16088e83c9..f830665ba6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.notification.rule.DefaultNotificationR import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger.Resource; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig; @@ -49,6 +50,7 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPl import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.rule.trigger.config.RateLimitsNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.ResourcesShortageNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.TaskProcessingFailureNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; @@ -391,6 +393,20 @@ public class DefaultNotifications { .color(RED_COLOR) .build(); + public static final DefaultNotification resourcesShortage = DefaultNotification.builder() + .name("Resources shortage notification") + .type(NotificationType.RESOURCES_SHORTAGE) + .subject("Warning: ${resource} shortage") + .text("The ${resource} usage is on the rise (currently at ${usage}%). Immediate action required to prevent system instability.") + .icon("warning") + .rule(DefaultRule.builder() + .name("Resources shortage") + .triggerConfig(ResourcesShortageNotificationRuleTriggerConfig.builder().resource(Resource.CPU.name()).cpuThreshold(90).storageThreshold(90).ramThreshold(90).build()) + .description("Send notification to system admins when resources shortage is running low") + .build()) + .color(RED_COLOR) + .build(); + private final NotificationTemplateService templateService; private final NotificationRuleService ruleService; diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html index 2d7ef41bd5..23a7830bab 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html @@ -591,6 +591,66 @@ + + + {{ 'notification.resources-shortage-trigger-settings' | translate }} +
+
+ notification.filter +
+ +
+ + + + + + +
+
+
+ +
+ + + + + + +
+
+
+ +
+ + + + + + +
+
+
+
+
+
+ + notification.description + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts index bc6935b4e7..7c43dbf9fb 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts @@ -102,6 +102,7 @@ export class RuleNotificationDialogComponent extends edgeCommunicationFailureTemplateForm: FormGroup; edgeConnectionTemplateForm: FormGroup; taskProcessingFailureTemplateForm: FormGroup; + resourceUsageShortageTemplateForm: FormGroup; triggerType = TriggerType; triggerTypes: TriggerType[]; @@ -344,6 +345,14 @@ export class RuleNotificationDialogComponent extends }) }); + this.resourceUsageShortageTemplateForm = this.fb.group({ + triggerConfig: this.fb.group({ + cpuThreshold: [.9, [Validators.min(0), Validators.max(1)]], + ramThreshold: [.9, [Validators.min(0), Validators.max(1)]], + storageThreshold: [.9, [Validators.min(0), Validators.max(1)]] + }) + }); + this.triggerTypeFormsMap = new Map([ [TriggerType.ALARM, this.alarmTemplateForm], [TriggerType.ALARM_COMMENT, this.alarmCommentTemplateForm], @@ -357,7 +366,8 @@ export class RuleNotificationDialogComponent extends [TriggerType.RATE_LIMITS, this.rateLimitsTemplateForm], [TriggerType.EDGE_COMMUNICATION_FAILURE, this.edgeCommunicationFailureTemplateForm], [TriggerType.EDGE_CONNECTION, this.edgeConnectionTemplateForm], - [TriggerType.TASK_PROCESSING_FAILURE, this.taskProcessingFailureTemplateForm] + [TriggerType.TASK_PROCESSING_FAILURE, this.taskProcessingFailureTemplateForm], + [TriggerType.RESOURCES_SHORTAGE, this.resourceUsageShortageTemplateForm] ]); if (data.isAdd || data.isCopy) { @@ -497,7 +507,8 @@ export class RuleNotificationDialogComponent extends TriggerType.API_USAGE_LIMIT, TriggerType.NEW_PLATFORM_VERSION, TriggerType.RATE_LIMITS, - TriggerType.TASK_PROCESSING_FAILURE + TriggerType.TASK_PROCESSING_FAILURE, + TriggerType.RESOURCES_SHORTAGE ]); if (this.isSysAdmin()) { diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts index ccbcb6bf22..3495d43338 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts @@ -180,7 +180,8 @@ export class TemplateNotificationDialogComponent NotificationType.API_USAGE_LIMIT, NotificationType.NEW_PLATFORM_VERSION, NotificationType.RATE_LIMITS, - NotificationType.TASK_PROCESSING_FAILURE + NotificationType.TASK_PROCESSING_FAILURE, + NotificationType.RESOURCES_SHORTAGE ]); if (this.isSysAdmin()) { diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts index 365adda774..088ae2555c 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -526,7 +526,8 @@ export enum NotificationType { RATE_LIMITS = 'RATE_LIMITS', EDGE_CONNECTION = 'EDGE_CONNECTION', EDGE_COMMUNICATION_FAILURE = 'EDGE_COMMUNICATION_FAILURE', - TASK_PROCESSING_FAILURE = 'TASK_PROCESSING_FAILURE' + TASK_PROCESSING_FAILURE = 'TASK_PROCESSING_FAILURE', + RESOURCES_SHORTAGE = 'RESOURCES_SHORTAGE' } export const NotificationTypeIcons = new Map([ @@ -538,7 +539,8 @@ export const NotificationTypeIcons = new Map([ [NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'settings_ethernet'], [NotificationType.ENTITIES_LIMIT, 'data_thresholding'], [NotificationType.API_USAGE_LIMIT, 'insert_chart'], - [NotificationType.TASK_PROCESSING_FAILURE, 'warning'] + [NotificationType.TASK_PROCESSING_FAILURE, 'warning'], + [NotificationType.RESOURCES_SHORTAGE, 'warning'] ]); export const AlarmSeverityNotificationColors = new Map( @@ -657,6 +659,12 @@ export const NotificationTemplateTypeTranslateMap = new Map([ @@ -688,7 +697,8 @@ export const TriggerTypeTranslationMap = new Map([ [TriggerType.RATE_LIMITS, 'notification.trigger.rate-limits'], [TriggerType.EDGE_CONNECTION, 'notification.trigger.edge-connection'], [TriggerType.EDGE_COMMUNICATION_FAILURE, 'notification.trigger.edge-communication-failure'], - [TriggerType.TASK_PROCESSING_FAILURE, 'notification.trigger.task-processing-failure'] + [TriggerType.TASK_PROCESSING_FAILURE, 'notification.trigger.task-processing-failure'], + [TriggerType.RESOURCES_SHORTAGE, 'notification.trigger.resources-shortage'] ]); export interface NotificationUserSettings { diff --git a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md new file mode 100644 index 0000000000..eb4ef57f7a --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md @@ -0,0 +1,42 @@ +#### Resource usage shortage notification templatization + +
+
+ +Notification subject and message fields support templatization. +The list of available templatization parameters depends on the template type. +See the available types and parameters below: + +Available template parameters: + +* `cpuThreshold` - the CPU shortage threshold; +* `ramThreshold` - the RAM shortage threshold; +* `storageThreshold` - the Storage shortage threshold; + +Parameter names must be wrapped using `${...}`. For example: `${entityType}`. +You may also modify the value of the parameter with one of the suffixes: + +* `upperCase`, for example - `${entityType:upperCase}` +* `lowerCase`, for example - `${entityType:lowerCase}` +* `capitalize`, for example - `${entityType:capitalize}` + +
+ +##### Examples + +Let's assume there is a resource usage shortage and the system is low on free resources (CPU, RAM, or Storage). +The following template: + +```text +Warning: ${resource} is critically high at ${usage}% +{:copy-code} +``` + +will be transformed to: + +```text +Warning: CPU is critically high at 83% +``` + +
+
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 57df3865e9..124826fc9c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3728,6 +3728,7 @@ "new-platform-version-trigger-settings": "New platform version trigger settings", "rate-limits-trigger-settings": "Exceeded rate limits trigger settings", "task-processing-failure-trigger-settings": "Task processing failure trigger settings", + "resources-shortage-trigger-settings": "Resources shortage trigger settings", "at-least-one-should-be-selected": "At least one should be selected", "basic-settings": "Basic settings", "button-text": "Button text", @@ -3742,6 +3743,7 @@ "create-new": "Create new", "created": "Created", "customize-messages": "Customize messages", + "cpu-threshold": "CPU threshold", "delete-notification-text": "Be careful, after the confirmation the notification will become unrecoverable.", "delete-notification-title": "Are you sure you want to delete the notification?", "delete-notifications-text": "Be careful, after the confirmation notifications will become unrecoverable.", @@ -3857,6 +3859,7 @@ "only-rule-chain-lifecycle-failures": "Only rule chain lifecycle failures", "only-rule-node-lifecycle-failures": "Only rule node lifecycle failures", "platform-users": "Platform users", + "ram-threshold": "RAM threshold", "rate-limits": "Rate limits", "rate-limits-hint": "If the field is empty, the trigger will be applied to all rate limits", "recipient": "Recipient", @@ -3922,6 +3925,7 @@ "start-from-scratch": "Start from scratch", "status": "Status", "stop-escalation-alarm-status-become": "Stop the escalation on the alarm status become:", + "storage-threshold": "Storage threshold", "subject": "Subject", "subject-required": "Subject is required", "subject-max-length": "Subject should be less than or equal to {{ length }} characters", @@ -3943,7 +3947,8 @@ "rate-limits": "Exceeded rate limits", "edge-communication-failure": "Edge communication failure", "edge-connection": "Edge connection", - "task-processing-failure": "Task processing failure" + "task-processing-failure": "Task processing failure", + "resources-shortage": "Resources shortage" }, "templates": "Templates", "notification-templates": "Notifications / Templates", @@ -3967,6 +3972,7 @@ "edge-connection": "Edge connection", "edge-communication-failure": "Edge communication failure", "task-processing-failure": "Task processing failure", + "resources-shortage": "Resources shortage", "trigger": "Trigger", "trigger-required": "Trigger is required" }, From bad6451102396a2ab1431324deee60a838ad1676 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 7 Feb 2025 14:30:42 +0200 Subject: [PATCH 005/335] Improve after review --- ...rocessor.java => ResourcesShortageTriggerProcessor.java} | 2 +- .../server/dao/notification/DefaultNotifications.java | 4 ++-- .../notification/rule/rule-notification-dialog.component.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/{ResourcesShortageLimitTriggerProcessor.java => ResourcesShortageTriggerProcessor.java} (92%) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageLimitTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageLimitTriggerProcessor.java rename to application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java index c9c9a03f55..b7da85e3e0 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageLimitTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.Resou @Service @RequiredArgsConstructor -public class ResourcesShortageLimitTriggerProcessor implements NotificationRuleTriggerProcessor { +public class ResourcesShortageTriggerProcessor implements NotificationRuleTriggerProcessor { @Override public boolean matchesFilter(ResourcesShortageTrigger trigger, ResourcesShortageNotificationRuleTriggerConfig triggerConfig) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index f830665ba6..2716a6492b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -397,11 +397,11 @@ public class DefaultNotifications { .name("Resources shortage notification") .type(NotificationType.RESOURCES_SHORTAGE) .subject("Warning: ${resource} shortage") - .text("The ${resource} usage is on the rise (currently at ${usage}%). Immediate action required to prevent system instability.") + .text("${resource} usage is at ${usage}%).") .icon("warning") .rule(DefaultRule.builder() .name("Resources shortage") - .triggerConfig(ResourcesShortageNotificationRuleTriggerConfig.builder().resource(Resource.CPU.name()).cpuThreshold(90).storageThreshold(90).ramThreshold(90).build()) + .triggerConfig(ResourcesShortageNotificationRuleTriggerConfig.builder().resource(Resource.CPU.name()).cpuThreshold(80).storageThreshold(80).ramThreshold(80).build()) .description("Send notification to system admins when resources shortage is running low") .build()) .color(RED_COLOR) diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts index 7c43dbf9fb..d49cf25d29 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts @@ -347,9 +347,9 @@ export class RuleNotificationDialogComponent extends this.resourceUsageShortageTemplateForm = this.fb.group({ triggerConfig: this.fb.group({ - cpuThreshold: [.9, [Validators.min(0), Validators.max(1)]], - ramThreshold: [.9, [Validators.min(0), Validators.max(1)]], - storageThreshold: [.9, [Validators.min(0), Validators.max(1)]] + cpuThreshold: [.8, [Validators.min(0), Validators.max(1)]], + ramThreshold: [.8, [Validators.min(0), Validators.max(1)]], + storageThreshold: [.8, [Validators.min(0), Validators.max(1)]] }) }); From e14c81ece33c7fcb2f58a7688b0a388a89fcae90 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 24 Feb 2025 16:38:08 +0200 Subject: [PATCH 006/335] Fix default resourcesShortage --- .../server/dao/notification/DefaultNotifications.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index 2716a6492b..a1944f78ca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -401,7 +401,7 @@ public class DefaultNotifications { .icon("warning") .rule(DefaultRule.builder() .name("Resources shortage") - .triggerConfig(ResourcesShortageNotificationRuleTriggerConfig.builder().resource(Resource.CPU.name()).cpuThreshold(80).storageThreshold(80).ramThreshold(80).build()) + .triggerConfig(ResourcesShortageNotificationRuleTriggerConfig.builder().resource(Resource.CPU.name()).cpuThreshold(0.8f).storageThreshold(0.8f).ramThreshold(0.8f).build()) .description("Send notification to system admins when resources shortage is running low") .build()) .color(RED_COLOR) From a4948891b2ad28940ea36ef3dbf94a21325573cd Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 26 Feb 2025 13:13:34 +0200 Subject: [PATCH 007/335] Make notification for microservices --- .../system/DefaultSystemInfoService.java | 17 ++++++++++++----- .../info/ResourcesShortageNotificationInfo.java | 2 +- .../rule/trigger/ResourcesShortageTrigger.java | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index 53d94af8e4..515da7703a 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -184,6 +184,11 @@ public class DefaultSystemInfoService extends TbApplicationEventListener clusterSystemData = getSystemData(serviceInfoProvider.getServiceInfo()); + clusterSystemData.forEach(data -> { + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(data.getCpuUsage()).build()); + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(data.getMemoryUsage()).build()); + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(data.getDiscUsage()).build()); + }); BasicTsKvEntry clusterDataKv = new BasicTsKvEntry(ts, new JsonDataEntry("clusterSystemData", JacksonUtil.toString(clusterSystemData))); doSave(Arrays.asList(new BasicTsKvEntry(ts, new BooleanDataEntry("clusterMode", true)), clusterDataKv)); } @@ -194,15 +199,17 @@ public class DefaultSystemInfoService extends TbApplicationEventListener { tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", (long) v))); - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(v).build()); + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage((long) v).build()); }); getMemoryUsage().ifPresent(v -> { - tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", (long) v))); - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(v).build()); + long value = (long) v; + tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", value))); + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(value).build()); }); getDiscSpaceUsage().ifPresent(v -> { - tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("discUsage", (long) v))); - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(v).build()); + long value = (long) v; + tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("discUsage", value))); + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(value).build()); }); getCpuCount().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuCount", (long) v)))); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java index 1ebbe97f80..c052911b44 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java @@ -29,7 +29,7 @@ import java.util.Map; public class ResourcesShortageNotificationInfo implements RuleOriginatedNotificationInfo { private String resource; - private Integer usage; + private Long usage; @Override public Map getTemplateData() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java index b35d0b8470..0c4722647e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java @@ -32,7 +32,7 @@ public class ResourcesShortageTrigger implements NotificationRuleTrigger { private static final long serialVersionUID = 6024216015202949570L; private Resource resource; - private Integer usage; + private Long usage; @Override public TenantId getTenantId() { From f366e277df134ab8515ad936ba0cd4bbc3ef91ce Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 26 Feb 2025 15:13:24 +0200 Subject: [PATCH 008/335] Add tests for resource shortage --- .../system/DefaultSystemInfoService.java | 5 +- .../notification/NotificationRuleApiTest.java | 59 ++++++++++++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index 515da7703a..f6d9ac158a 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -198,8 +198,9 @@ public class DefaultSystemInfoService extends TbApplicationEventListener tsList = new ArrayList<>(); tsList.add(new BasicTsKvEntry(ts, new BooleanDataEntry("clusterMode", false))); getCpuUsage().ifPresent(v -> { - tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", (long) v))); - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage((long) v).build()); + long value = (long) v; + tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", value))); + notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(value).build()); }); getMemoryUsage().ifPresent(v -> { long value = (long) v; diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 3cfb2dc42b..5fd9b48ea0 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -68,6 +68,8 @@ import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger.Resource; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig; @@ -78,6 +80,7 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.Entit import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPlatformVersionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.rule.trigger.config.RateLimitsNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.ResourcesShortageNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.platform.AffectedTenantAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter; @@ -98,8 +101,10 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.queue.notification.DefaultNotificationDeduplicationService; import org.thingsboard.server.service.notification.rule.cache.DefaultNotificationRulesCache; import org.thingsboard.server.service.state.DeviceStateService; +import org.thingsboard.server.service.system.DefaultSystemInfoService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -132,7 +137,9 @@ import static org.thingsboard.server.common.data.notification.rule.trigger.confi public class NotificationRuleApiTest extends AbstractNotificationApiTest { @SpyBean - private AlarmSubscriptionService alarmSubscriptionService; + private AlarmSubscriptionService alarmSubscriptionService;; + @Autowired + private DefaultSystemInfoService systemInfoService; @Autowired private NotificationRequestService notificationRequestService; @Autowired @@ -780,6 +787,56 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { }); } + @Test + public void testNotificationRuleProcessing_resourcesShortage() throws Exception { + loginSysAdmin(); + ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder() + .resource(Resource.CPU.name()) + .cpuThreshold(0.01f) + .ramThreshold(0.01f) + .storageThreshold(0.01f) + .build(); + createNotificationRule(triggerConfig, "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); + loginTenantAdmin(); + + Method method = DefaultSystemInfoService.class.getDeclaredMethod("saveCurrentMonolithSystemInfo"); + method.setAccessible(true); + method.invoke(systemInfoService); + + TimeUnit.SECONDS.sleep(5); + + assertThat(getMyNotifications(false, 100)).size().isEqualTo(3); + } + + @Test + public void testNotificationsDeduplication_resourcesShortage() throws Exception { + loginSysAdmin(); + ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder() + .resource(Resource.CPU.name()) + .cpuThreshold(0.1f) + .build(); + createNotificationRule(triggerConfig, "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); + loginTenantAdmin(); + + assertThat(getMyNotifications(false, 100)).size().isZero(); + for (int i = 0; i < 10; i++) { + notificationRuleProcessor.process(ResourcesShortageTrigger.builder() + .resource(Resource.CPU) + .usage(15L) + .build()); + TimeUnit.MILLISECONDS.sleep(300); + } + TimeUnit.SECONDS.sleep(5); + assertThat(getMyNotifications(false, 100)).size().isOne(); + + // deduplication is 5 minute, no new message is exp + notificationRuleProcessor.process(ResourcesShortageTrigger.builder() + .resource(Resource.CPU) + .usage(5L) + .build()); + await("").atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne()); + } + @Test public void testNotificationRuleDisabling() throws Exception { EntityActionNotificationRuleTriggerConfig triggerConfig = new EntityActionNotificationRuleTriggerConfig(); From 9f1578c72229230cbcdb9846e3dc581852d09f4c Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 26 Feb 2025 15:52:16 +0200 Subject: [PATCH 009/335] Fix license header --- .../rule/trigger/ResourcesShortageTriggerProcessor.java | 2 +- .../notification/info/ResourcesShortageNotificationInfo.java | 2 +- .../notification/rule/trigger/ResourcesShortageTrigger.java | 2 +- .../config/ResourcesShortageNotificationRuleTriggerConfig.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java index b7da85e3e0..aefb628d2d 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2024 The Thingsboard Authors + * Copyright © 2016-2025 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. diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java index c052911b44..24cb21febd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2024 The Thingsboard Authors + * Copyright © 2016-2025 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. diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java index 0c4722647e..f12c80d5db 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2024 The Thingsboard Authors + * Copyright © 2016-2025 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. diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java index 17de9e9246..d542292113 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2024 The Thingsboard Authors + * Copyright © 2016-2025 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. From a302451a5bd77f3edecffc575525e2648c2de496 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 26 Feb 2025 16:28:28 +0200 Subject: [PATCH 010/335] UI: Trigger settings to persent --- .../rule-notification-dialog.component.html | 32 +++++++++---------- .../rule-notification-dialog.component.scss | 2 +- .../rule-notification-dialog.component.ts | 27 ++++++++++++---- .../app/shared/models/notification.models.ts | 8 ++++- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html index 028796f470..0951acfea8 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html @@ -481,16 +481,16 @@ [allowedEntityTypes]="allowEntityTypeForEntitiesLimit">
- + notification.threshold
- + - + max="100"/>
@@ -599,44 +599,44 @@
notification.filter
- + notification.cpu-threshold
- + - + max="100"/>
- + notification.ram-threshold
- + - + max="100"/>
- + notification.storage-threshold
- + - + max="100"/>
diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss index 5e495655f1..9f1221cad5 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss @@ -81,7 +81,7 @@ min-width: 364px; } .limit-slider-container { - > label { + > span { margin-right: 16px; width: min-content; max-width: 40%; diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts index 1ea55ed106..e0ddefcbe8 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts @@ -316,7 +316,7 @@ export class RuleNotificationDialogComponent extends this.entitiesLimitTemplateForm = this.fb.group({ triggerConfig: this.fb.group({ entityTypes: [], - threshold: [.8, [Validators.min(0), Validators.max(1)]] + threshold: [80, [Validators.min(0), Validators.max(100)]] }) }); @@ -347,9 +347,9 @@ export class RuleNotificationDialogComponent extends this.resourceUsageShortageTemplateForm = this.fb.group({ triggerConfig: this.fb.group({ - cpuThreshold: [.8, [Validators.min(0), Validators.max(1)]], - ramThreshold: [.8, [Validators.min(0), Validators.max(1)]], - storageThreshold: [.8, [Validators.min(0), Validators.max(1)]] + cpuThreshold: [80, [Validators.min(0), Validators.max(100)]], + ramThreshold: [80, [Validators.min(0), Validators.max(100)]], + storageThreshold: [80, [Validators.min(0), Validators.max(100)]] }) }); @@ -390,6 +390,14 @@ export class RuleNotificationDialogComponent extends this.deviceInactivityTemplateForm.get('triggerConfig.filterByDevice') .patchValue(!!this.ruleNotification.triggerConfig.devices, {onlySelf: true}); } + if (this.ruleNotification.triggerType === TriggerType.ENTITIES_LIMIT) { + this.entitiesLimitTemplateForm.get('triggerConfig.threshold').patchValue(this.ruleNotification.triggerConfig.threshold * 100, {emitEvent: false}); + } + if (this.ruleNotification.triggerType === TriggerType.RESOURCES_SHORTAGE) { + this.resourceUsageShortageTemplateForm.get('triggerConfig.cpuThreshold').patchValue(this.ruleNotification.triggerConfig.cpuThreshold * 100, {emitEvent: false}); + this.resourceUsageShortageTemplateForm.get('triggerConfig.ramThreshold').patchValue(this.ruleNotification.triggerConfig.ramThreshold * 100, {emitEvent: false}); + this.resourceUsageShortageTemplateForm.get('triggerConfig.storageThreshold').patchValue(this.ruleNotification.triggerConfig.storageThreshold * 100, {emitEvent: false}); + } } } @@ -438,6 +446,14 @@ export class RuleNotificationDialogComponent extends if (triggerType === TriggerType.DEVICE_ACTIVITY) { delete formValue.triggerConfig.filterByDevice; } + if (triggerType === TriggerType.ENTITIES_LIMIT) { + formValue.triggerConfig.threshold = formValue.triggerConfig.threshold / 100; + } + if (triggerType === TriggerType.RESOURCES_SHORTAGE) { + formValue.triggerConfig.cpuThreshold = formValue.triggerConfig.cpuThreshold / 100; + formValue.triggerConfig.ramThreshold = formValue.triggerConfig.ramThreshold / 100; + formValue.triggerConfig.storageThreshold = formValue.triggerConfig.storageThreshold / 100; + } formValue.recipientsConfig.triggerType = triggerType; formValue.triggerConfig.triggerType = triggerType; if (this.ruleNotification && !this.data.isCopy) { @@ -493,8 +509,7 @@ export class RuleNotificationDialogComponent extends } formatLabel(value: number): string { - const formatValue = (value * 100).toFixed(); - return `${formatValue}%`; + return `${value}%`; } private isSysAdmin(): boolean { diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts index 369bc08c15..63d7edd2a2 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -129,7 +129,7 @@ export interface NotificationRule extends Omit, 'la export type NotificationRuleTriggerConfig = Partial; + ApiUsageLimitNotificationRuleTriggerConfig & RateLimitsNotificationRuleTriggerConfig & ResourceUsageShortageNotificationRuleTriggerConfig>; export interface AlarmNotificationRuleTriggerConfig { alarmTypes?: Array; @@ -183,6 +183,12 @@ export interface EntitiesLimitNotificationRuleTriggerConfig { threshold: number; } +export interface ResourceUsageShortageNotificationRuleTriggerConfig { + cpuThreshold: number; + ramThreshold: number; + storageThreshold: number; +} + export interface ApiUsageLimitNotificationRuleTriggerConfig { apiFeatures: ApiFeature[]; notifyOn: ApiUsageStateValue[]; From f3a0d346bcf75ee94eb6f2df51b0bd8c859dbf7a Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 26 Feb 2025 16:48:30 +0200 Subject: [PATCH 011/335] UI: Refactoring --- .../rule-notification-dialog.component.html | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html index 0951acfea8..68122875c0 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html @@ -480,17 +480,18 @@ ignoreAuthorityFilter [allowedEntityTypes]="allowEntityTypeForEntitiesLimit"> -
+
notification.threshold
- + + %
@@ -598,45 +599,48 @@
notification.filter -
+
notification.cpu-threshold
- + + %
-
+
notification.ram-threshold
- + + %
-
+
notification.storage-threshold
- + + %
From f58608f94cdee03783626260b8261350392cbe7b Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 26 Feb 2025 17:26:52 +0200 Subject: [PATCH 012/335] Fix tupo --- .../server/dao/notification/DefaultNotifications.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index db6c26f483..2f209d8546 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -378,7 +378,7 @@ public class DefaultNotifications { .name("Resources shortage notification") .type(NotificationType.RESOURCES_SHORTAGE) .subject("Warning: ${resource} shortage") - .text("${resource} usage is at ${usage}%).") + .text("${resource} usage is at ${usage}%.") .icon("warning") .rule(DefaultRule.builder() .name("Resources shortage") From f7923bc0095e440c83ed37e0e3b598dfc54cd511 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 27 Feb 2025 10:21:27 +0200 Subject: [PATCH 013/335] Make tests more stable --- .../service/notification/NotificationRuleApiTest.java | 10 +++++----- ...ResourcesShortageNotificationRuleTriggerConfig.java | 1 - .../server/dao/notification/DefaultNotifications.java | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 20800f1bb9..31507758de 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -791,10 +791,9 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { public void testNotificationRuleProcessing_resourcesShortage() throws Exception { loginSysAdmin(); ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder() - .resource(Resource.CPU.name()) .cpuThreshold(0.01f) - .ramThreshold(0.01f) - .storageThreshold(0.01f) + .ramThreshold(1f) + .storageThreshold(1f) .build(); createNotificationRule(triggerConfig, "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); loginTenantAdmin(); @@ -805,15 +804,16 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { TimeUnit.SECONDS.sleep(5); - assertThat(getMyNotifications(false, 100)).size().isEqualTo(3); + assertThat(getMyNotifications(false, 100)).size().isOne(); } @Test public void testNotificationsDeduplication_resourcesShortage() throws Exception { loginSysAdmin(); ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder() - .resource(Resource.CPU.name()) .cpuThreshold(0.1f) + .ramThreshold(1f) + .storageThreshold(1f) .build(); createNotificationRule(triggerConfig, "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); loginTenantAdmin(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java index d542292113..e8ea6c9be5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ResourcesShortageNotificationRuleTriggerConfig.java @@ -32,7 +32,6 @@ public class ResourcesShortageNotificationRuleTriggerConfig implements Notificat @Serial private static final long serialVersionUID = 339395299693241424L; - private String resource; @Max(1) private float cpuThreshold; // in percents @Max(1) diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index 2f209d8546..960cbfcd73 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -382,7 +382,7 @@ public class DefaultNotifications { .icon("warning") .rule(DefaultRule.builder() .name("Resources shortage") - .triggerConfig(ResourcesShortageNotificationRuleTriggerConfig.builder().resource(Resource.CPU.name()).cpuThreshold(0.8f).storageThreshold(0.8f).ramThreshold(0.8f).build()) + .triggerConfig(ResourcesShortageNotificationRuleTriggerConfig.builder().cpuThreshold(0.8f).storageThreshold(0.8f).ramThreshold(0.8f).build()) .description("Send notification to system admins when resources shortage is running low") .build()) .color(RED_COLOR) From 6e6d57e60ab1e9ba4bf375b4eef60166ad66ac4a Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 28 Feb 2025 15:34:21 +0200 Subject: [PATCH 014/335] UI: Add support use Obj19 for OTA updated in LWM2M transport configuration --- ...ile-transport-configuration.component.html | 74 ++++++++++--------- ...ofile-transport-configuration.component.ts | 2 + .../lwm2m/lwm2m-profile-config.models.ts | 7 +- .../assets/locale/locale.constant-en_US.json | 6 +- 4 files changed, 53 insertions(+), 36 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 0ad27b23ef..bb37b5784a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -52,39 +52,47 @@
- device-profile.lwm2m.fw-update - - {{ 'device-profile.lwm2m.fw-update-strategy' | translate }} - - {{ 'device-profile.lwm2m.fw-update-strategy-package' | translate }} - {{ 'device-profile.lwm2m.fw-update-strategy-package-uri' | translate }} - {{ 'device-profile.lwm2m.fw-update-strategy-data' | translate }} - - - - {{ 'device-profile.lwm2m.fw-update-resource' | translate }} - - - {{ 'device-profile.lwm2m.fw-update-resource-required' | translate }} - - -
-
- device-profile.lwm2m.sw-update - - {{ 'device-profile.lwm2m.sw-update-strategy' | translate }} - - {{ 'device-profile.lwm2m.sw-update-strategy-package' | translate }} - {{ 'device-profile.lwm2m.sw-update-strategy-package-uri' | translate }} - - - - {{ 'device-profile.lwm2m.sw-update-resource' | translate }} - - - {{ 'device-profile.lwm2m.sw-update-resource-required' | translate }} - - + device-profile.lwm2m.ota-update + + + {{ 'device-profile.lwm2m.use-object-19-for-ota-update' | translate }} + + +
+ device-profile.lwm2m.fw-update + + {{ 'device-profile.lwm2m.fw-update-strategy' | translate }} + + {{ 'device-profile.lwm2m.fw-update-strategy-package' | translate }} + {{ 'device-profile.lwm2m.fw-update-strategy-package-uri' | translate }} + {{ 'device-profile.lwm2m.fw-update-strategy-data' | translate }} + + + + {{ 'device-profile.lwm2m.fw-update-resource' | translate }} + + + {{ 'device-profile.lwm2m.fw-update-resource-required' | translate }} + + +
+
+ device-profile.lwm2m.sw-update + + {{ 'device-profile.lwm2m.sw-update-strategy' | translate }} + + {{ 'device-profile.lwm2m.sw-update-strategy-package' | translate }} + {{ 'device-profile.lwm2m.sw-update-strategy-package-uri' | translate }} + + + + {{ 'device-profile.lwm2m.sw-update-resource' | translate }} + + + {{ 'device-profile.lwm2m.sw-update-resource-required' | translate }} + + +
device-profile.power-saving-mode diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index a9fbb7b249..46c439cf33 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -104,6 +104,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrap: [[]], clientLwM2mSettings: this.fb.group({ clientOnlyObserveAfterConnect: [1, []], + useObject19ForOta: [false], fwUpdateStrategy: [1, []], swUpdateStrategy: [1, []], fwUpdateResource: [{value: '', disabled: true}, []], @@ -262,6 +263,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServerUpdateEnable: this.configurationValue.bootstrapServerUpdateEnable || false, clientLwM2mSettings: { clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, + useObject19ForOta: this.configurationValue.clientLwM2mSettings.useObject19ForOta ?? false, fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, fwUpdateResource: this.configurationValue.clientLwM2mSettings.fwUpdateResource || '', diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index e5706cf39b..33b358e581 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -124,13 +124,15 @@ export const PowerModeTranslationMap = new Map( export enum ObjectIDVer { V1_0 = '1.0', - V1_1 = '1.1' + V1_1 = '1.1', + V1_2 = '1.2', } export const ObjectIDVerTranslationMap = new Map( [ [ObjectIDVer.V1_0, 'device-profile.lwm2m.default-object-id-ver.v1-0'], - [ObjectIDVer.V1_1, 'device-profile.lwm2m.default-object-id-ver.v1-1'] + [ObjectIDVer.V1_1, 'device-profile.lwm2m.default-object-id-ver.v1-1'], + [ObjectIDVer.V1_2, 'device-profile.lwm2m.default-object-id-ver.v1-2'], ] ); @@ -167,6 +169,7 @@ export interface Lwm2mProfileConfigModels { export interface ClientLwM2mSettings { clientOnlyObserveAfterConnect: number; + useObject19ForOta?: boolean; fwUpdateStrategy: number; swUpdateStrategy: number; fwUpdateResource?: string; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index de97184f5a..67b406fc40 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2077,6 +2077,9 @@ "add-lwm2m-server-config": "Add LwM2M server", "no-config-servers": "No servers configured", "others-tab": "Other settings", + "ota-update": "OTA update", + "use-object-19-for-ota-update": "Use Object 19 for OTA update", + "use-object-19-for-ota-update-hint": "Use Resource ObjectId = 19 for OTA updates: FirmWare → InstanceId = 65534, SoftWare → InstanceId = 65535. The data format is JSON wrapped in Base64. The main field in JSON: \"Checksum\" (SHA256). Additional fields: \"Title\" (OTA name), \"Version\" (OTA version), \"File Name\" (file name for storing OTA on the client), \"File Size\" (OTA size in bytes).", "client-strategy": "Client strategy when connecting", "client-strategy-label": "Strategy", "client-strategy-only-observe": "Only Observe Request to the client after the initial connection", @@ -2107,7 +2110,8 @@ "default-object-id": "Default Object Version (Attribute)", "default-object-id-ver": { "v1-0": "1.0", - "v1-1": "1.1" + "v1-1": "1.1", + "v1-2": "1.2" } }, "snmp": { From bb31dd5c909a5a5817c175722d55e9920b9527b9 Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 28 Feb 2025 15:56:53 +0200 Subject: [PATCH 015/335] lwm2m: backend: add useObject19ForOta --- .../server/service/device/DeviceBulkImportService.java | 2 +- .../common/data/device/profile/lwm2m/OtherConfiguration.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java index b895614bdc..2924f8ddeb 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java @@ -257,7 +257,7 @@ public class DeviceBulkImportService extends AbstractBulkImportService { Lwm2mDeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration(); transportConfiguration.setBootstrap(Collections.emptyList()); - transportConfiguration.setClientLwM2mSettings(new OtherConfiguration(1, 1, 1, PowerMode.DRX, null, null, null, null, null, V1_0.toString())); + transportConfiguration.setClientLwM2mSettings(new OtherConfiguration(false,1, 1, 1, PowerMode.DRX, null, null, null, null, null, V1_0.toString())); transportConfiguration.setObserveAttr(new TelemetryMappingConfiguration(Collections.emptyMap(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptyMap())); DeviceProfileData deviceProfileData = new DeviceProfileData(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java index 8eabff8ed8..0d59ef6f8f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; @JsonIgnoreProperties(ignoreUnknown = true) public class OtherConfiguration extends PowerSavingConfiguration { + private Boolean useObject19ForOta; private Integer fwUpdateStrategy; private Integer swUpdateStrategy; private Integer clientOnlyObserveAfterConnect; From 923704768ff02ce81372bbcef49d3f27a47d4ac4 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 5 Mar 2025 10:25:23 +0200 Subject: [PATCH 016/335] Make tests more stable --- .../notification/NotificationRuleApiTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 31507758de..518df47b34 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -791,8 +791,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { public void testNotificationRuleProcessing_resourcesShortage() throws Exception { loginSysAdmin(); ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder() - .cpuThreshold(0.01f) - .ramThreshold(1f) + .ramThreshold(0.01f) + .cpuThreshold(1f) .storageThreshold(1f) .build(); createNotificationRule(triggerConfig, "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); @@ -811,8 +811,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { public void testNotificationsDeduplication_resourcesShortage() throws Exception { loginSysAdmin(); ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder() - .cpuThreshold(0.1f) - .ramThreshold(1f) + .ramThreshold(0.01f) + .cpuThreshold(1f) .storageThreshold(1f) .build(); createNotificationRule(triggerConfig, "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); @@ -821,7 +821,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { assertThat(getMyNotifications(false, 100)).size().isZero(); for (int i = 0; i < 10; i++) { notificationRuleProcessor.process(ResourcesShortageTrigger.builder() - .resource(Resource.CPU) + .resource(Resource.RAM) .usage(15L) .build()); TimeUnit.MILLISECONDS.sleep(300); @@ -831,7 +831,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { // deduplication is 5 minute, no new message is exp notificationRuleProcessor.process(ResourcesShortageTrigger.builder() - .resource(Resource.CPU) + .resource(Resource.RAM) .usage(5L) .build()); await("").atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne()); From bbdeda95580c5903c1cff4981ed90671168c96c6 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Sun, 9 Mar 2025 19:48:38 +0100 Subject: [PATCH 017/335] added tests --- .../dao/service/RuleChainServiceTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java index 86b76bd5a5..8b5ebd4306 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.service; import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Test; import org.junit.jupiter.api.Assertions; @@ -28,12 +29,14 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import java.io.IOException; @@ -44,6 +47,7 @@ import java.util.UUID; import java.util.function.Function; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.thingsboard.server.common.data.relation.EntityRelation.USES_TYPE; /** * Created by igor on 3/13/18. @@ -55,6 +59,8 @@ public class RuleChainServiceTest extends AbstractServiceTest { EdgeService edgeService; @Autowired RuleChainService ruleChainService; + @Autowired + RelationService relationService; private IdComparator idComparator = new IdComparator<>(); private IdComparator ruleNodeIdComparator = new IdComparator<>(); @@ -354,6 +360,66 @@ public class RuleChainServiceTest extends AbstractServiceTest { Assert.assertTrue(ruleChainById.isRoot()); } + @Test + public void testSaveRuleChainWithInputNode() { + RuleChain toRuleChain = new RuleChain(); + toRuleChain.setName("To Rule Chain"); + toRuleChain.setTenantId(tenantId); + RuleChain savedToRuleChain = ruleChainService.saveRuleChain(toRuleChain); + + RuleChain fromRuleChain = new RuleChain(); + fromRuleChain.setName("From RuleChain"); + fromRuleChain.setTenantId(tenantId); + RuleChain savedFromRuleChain = ruleChainService.saveRuleChain(fromRuleChain); + + RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); + ruleChainMetaData.setRuleChainId(savedFromRuleChain.getId()); + + RuleNode ruleNode = new RuleNode(); + ruleNode.setName("Input node"); + ruleNode.setType("org.thingsboard.rule.engine.flow.TbRuleChainInputNode"); + ObjectNode configuration = JacksonUtil.newObjectNode(); + configuration.put("ruleChainId", savedToRuleChain.getId().toString()); + ruleNode.setConfiguration(configuration); + + List ruleNodes = new ArrayList<>(); + ruleNodes.add(ruleNode); + ruleChainMetaData.setFirstNodeIndex(0); + ruleChainMetaData.setNodes(ruleNodes); + + ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()); + + List relations = relationService.findByFromAndType(tenantId, savedFromRuleChain.getId(), USES_TYPE, RelationTypeGroup.COMMON); + Assert.assertEquals(1, relations.size()); + EntityRelation usesRelation = relations.get(0); + Assert.assertEquals(savedFromRuleChain.getId(), usesRelation.getFrom()); + Assert.assertEquals(savedToRuleChain.getId(), usesRelation.getTo()); + + RuleChain newToRuleChain = new RuleChain(); + newToRuleChain.setName("New To Rule Chain"); + newToRuleChain.setTenantId(tenantId); + RuleChain savedNewToRuleChain = ruleChainService.saveRuleChain(newToRuleChain); + + RuleNode newRuleNode = new RuleNode(); + newRuleNode.setName("Input node"); + newRuleNode.setType("org.thingsboard.rule.engine.flow.TbRuleChainInputNode"); + ObjectNode newConfiguration = JacksonUtil.newObjectNode(); + configuration.put("ruleChainId", savedNewToRuleChain.getId().toString()); + newRuleNode.setConfiguration(newConfiguration); + + List newRuleNodes = new ArrayList<>(); + newRuleNodes.add(newRuleNode); + RuleChainMetaData foundRuleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()); + foundRuleChainMetaData.setNodes(newRuleNodes); + ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()); + + List newRelations = relationService.findByFromAndType(tenantId, savedFromRuleChain.getId(), USES_TYPE, RelationTypeGroup.COMMON); + Assert.assertEquals(1, relations.size()); + EntityRelation newUsesRelation = newRelations.get(0); + Assert.assertEquals(savedFromRuleChain.getId(), newUsesRelation.getFrom()); + Assert.assertEquals(savedNewToRuleChain.getId(), newUsesRelation.getTo()); + } + private RuleChainId saveRuleChainAndSetAutoAssignToEdge(String name) { RuleChain edgeRuleChain = new RuleChain(); edgeRuleChain.setTenantId(tenantId); From 27f493e7973fcaa3375cd99efb57681375c7c543 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 11 Mar 2025 07:32:13 +0200 Subject: [PATCH 018/335] @nickAS21 fix_bug_lwm2m: add resource "tbfw" and "tbsw" to root coap --- .../server/DefaultLwM2mTransportService.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 71290ef0b0..9dcfb62914 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -18,6 +18,8 @@ package org.thingsboard.server.transport.lwm2m.server; import jakarta.annotation.PreDestroy; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapResource; +import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.elements.config.Configuration; import org.eclipse.californium.scandium.config.DtlsConfig; import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; @@ -58,6 +60,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_W import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.thingsboard.server.transport.lwm2m.server.LwM2MNetworkConfig.getCoapConfig; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RESOURCE; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_UPDATE_COAP_RESOURCE; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.setDtlsConnectorConfigCidLength; @Slf4j @@ -85,14 +88,6 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { @AfterStartUp(order = AfterStartUp.AFTER_TRANSPORT_SERVICE) public void init() { this.server = getLhServer(); - /* - * Add a resource to the server. - * CoapResource -> - * path = FW_PACKAGE or SW_PACKAGE - * nameFile = "BC68JAR01A09_TO_BC68JAR01A10.bin" - * "coap://host:port/{path}/{token}/{nameFile}" - */ - new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE); this.context.setServer(server); this.startLhServer(); } @@ -168,7 +163,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { serverCoapConfig.setTransient(DtlsConfig.DTLS_CONNECTION_ID_LENGTH); if (config.getDtlsCidLength() != null) { - setDtlsConnectorConfigCidLength( serverCoapConfig, config.getDtlsCidLength()); + setDtlsConnectorConfigCidLength(serverCoapConfig, config.getDtlsCidLength()); } /* Create DTLS Config */ @@ -191,6 +186,17 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { // Create LWM2M server builder.setEndpointsProviders(endpointsBuilder.build()); + LeshanServer leshanServer = builder.build(); + CoapServer coapServer = ((CaliforniumServerEndpointsProvider) (leshanServer.getEndpointsProvider()).toArray()[0]).getCoapServer(); + if (coapServer != null) { + CoapResource root = (CoapResource) coapServer.getRoot(); + if (root == null) { + root = new CoapResource(""); + coapServer.add(root); + } + root.add(new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE)); + root.add(new LwM2mTransportCoapResource(otaPackageDataCache, SOFTWARE_UPDATE_COAP_RESOURCE)); + } return builder.build(); } From 37482b51f33f7e4003ffddc534c98aa10c1f31e5 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 11 Mar 2025 12:33:05 +0100 Subject: [PATCH 019/335] added upgrade script --- .../update/DefaultDataUpdateService.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index e8622fae37..27e0f6adc2 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -24,23 +24,36 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageDataIterable; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.FilterPredicateValue; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.sql.JpaExecutorService; +import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.RuleNodeClassInfo; +import org.thingsboard.server.service.install.DbUpgradeExecutorService; import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.utils.TbNodeUpgradeUtils; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.concurrent.ExecutionException; +import static org.thingsboard.server.common.data.relation.EntityRelation.USES_TYPE; + @Service @Profile("install") @Slf4j @@ -52,6 +65,12 @@ public class DefaultDataUpdateService implements DataUpdateService { @Autowired private RuleChainService ruleChainService; + @Autowired + private RelationService relationService; + + @Autowired + private TenantService tenantService; + @Autowired private ComponentDiscoveryService componentDiscoveryService; @@ -61,13 +80,66 @@ public class DefaultDataUpdateService implements DataUpdateService { @Autowired private InstallScripts installScripts; + @Autowired + private DbUpgradeExecutorService executorService; + @Override public void updateData() throws Exception { log.info("Updating data ..."); //TODO: should be cleaned after each release + inputNodesUpdater.updateEntities(); log.info("Data updated."); } + //TODO: should be removed after release + private final PaginatedUpdater inputNodesUpdater = new PaginatedUpdater<>() { + @Override + protected String getName() { + return "Input nodes updater"; + } + + @Override + protected PageData findEntities(String type, PageLink pageLink) { + return tenantService.findTenants(pageLink); + } + + @Override + protected void updateEntity(Tenant tenant) { + TenantId tenantId = tenant.getId(); + try { + var inputNodes = ruleChainService.findRuleNodesByTenantIdAndType(tenantId, "org.thingsboard.rule.engine.flow.TbRuleChainInputNode"); + var resultFutures = inputNodes.stream().map(ruleNode -> { + try { + JsonNode id = ruleNode.getConfiguration().get("ruleChainId"); + if (id != null) { + RuleChainId toRuleChainId = new RuleChainId(UUID.fromString(id.asText())); + RuleChainId fromRuleChainId = ruleNode.getRuleChainId(); + var isExistFuture = relationService.checkRelationAsync(null, fromRuleChainId, toRuleChainId, USES_TYPE, RelationTypeGroup.COMMON); + Futures.transformAsync(isExistFuture, isExist -> { + if (!isExist) { + EntityRelation relation = new EntityRelation(); + relation.setFrom(fromRuleChainId); + relation.setTo(toRuleChainId); + relation.setType(EntityRelation.USES_TYPE); + relation.setTypeGroup(RelationTypeGroup.COMMON); + return relationService.saveRelationAsync(tenantId, relation); + } + return Futures.immediateFuture(null); + }, executorService); + } + } catch (Exception e) { + log.error("[{}] Create relation for input node: [{}]", tenantId, ruleNode, e); + } + return Futures.immediateFuture(null); + }).toList(); + + Futures.allAsList(resultFutures).get(); + } catch (Exception e) { + log.error("[{}] Unable to update Tenant input nodes", tenantId, e); + } + } + }; + @Override public void upgradeRuleNodes() { int totalRuleNodesUpgraded = 0; From 6214e848acee2562771b8234a130696f1282f334 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 11 Mar 2025 16:19:43 +0200 Subject: [PATCH 020/335] fix_bug_lwm2m: refactoring --- .../transport/lwm2m/server/DefaultLwM2mTransportService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 9dcfb62914..2ca0249b46 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -197,7 +197,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { root.add(new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE)); root.add(new LwM2mTransportCoapResource(otaPackageDataCache, SOFTWARE_UPDATE_COAP_RESOURCE)); } - return builder.build(); + return leshanServer; } private void setServerWithCredentials(LeshanServerBuilder builder) { From ccd970b0da44092d8adbc817080968ad503dc817 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 12 Mar 2025 17:53:00 +0200 Subject: [PATCH 021/335] fixed resource vc restoring --- .../ie/importing/impl/AssetImportService.java | 2 +- .../impl/AssetProfileImportService.java | 2 +- .../impl/BaseEntityImportService.java | 33 ++++++++++---- .../importing/impl/CustomerImportService.java | 2 +- .../impl/DashboardImportService.java | 8 ++-- .../importing/impl/DeviceImportService.java | 2 +- .../impl/DeviceProfileImportService.java | 2 +- .../impl/EntityViewImportService.java | 2 +- .../impl/NotificationRuleImportService.java | 2 +- .../impl/NotificationTargetImportService.java | 2 +- .../NotificationTemplateImportService.java | 2 +- .../importing/impl/ResourceImportService.java | 11 ++--- .../impl/RuleChainImportService.java | 12 ++--- .../impl/WidgetTypeImportService.java | 6 +-- .../impl/WidgetsBundleImportService.java | 6 +-- .../DefaultEntitiesVersionControlService.java | 2 +- .../server/utils/LwM2mObjectModelUtils.java | 1 - .../server/common/data/TbResourceInfo.java | 45 +++++++++++++++++++ .../common/data/sync/vc/EntityLoadError.java | 6 ++- 19 files changed, 108 insertions(+), 40 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetImportService.java index 7cd4c3aca1..9bf44ffbec 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetImportService.java @@ -46,7 +46,7 @@ public class AssetImportService extends BaseEntityImportService exportData, IdProvider idProvider) { + protected Asset saveOrUpdate(EntitiesImportCtx ctx, Asset asset, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { Asset savedAsset = assetService.saveAsset(asset); if (ctx.isFinalImportAttempt() || ctx.getCurrentImportResult().isUpdatedAllExternalIds()) { importCalculatedFields(ctx, savedAsset, exportData, idProvider); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java index 32a0090a4a..d5663c34a4 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java @@ -49,7 +49,7 @@ public class AssetProfileImportService extends BaseEntityImportService exportData, IdProvider idProvider) { + protected AssetProfile saveOrUpdate(EntitiesImportCtx ctx, AssetProfile assetProfile, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { AssetProfile saved = assetProfileService.saveAssetProfile(assetProfile); if (ctx.isFinalImportAttempt() || ctx.getCurrentImportResult().isUpdatedAllExternalIds()) { importCalculatedFields(ctx, saved, exportData, idProvider); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java index bfa95af83c..83052360b6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java @@ -18,6 +18,8 @@ package org.thingsboard.server.service.sync.ie.importing.impl; import com.fasterxml.jackson.databind.JsonNode; import com.google.api.client.util.Objects; import com.google.common.util.concurrent.FutureCallback; +import lombok.AllArgsConstructor; +import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.checkerframework.checker.nullness.qual.Nullable; @@ -117,10 +119,10 @@ public abstract class BaseEntityImportService importResult, D exportData, IdProvider idProvider) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/CustomerImportService.java index 8774c77870..d4179b639d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/CustomerImportService.java @@ -52,7 +52,7 @@ public class CustomerImportService extends BaseEntityImportService exportData, IdProvider idProvider) { + protected Customer saveOrUpdate(EntitiesImportCtx ctx, Customer customer, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { if (!customer.isPublic()) { return customerService.saveCustomer(customer); } else { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DashboardImportService.java index e9407f2d58..6744b04f55 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DashboardImportService.java @@ -75,7 +75,7 @@ public class DashboardImportService extends BaseEntityImportService exportData, IdProvider idProvider) { + protected Dashboard saveOrUpdate(EntitiesImportCtx ctx, Dashboard dashboard, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { var tenantId = ctx.getTenantId(); Set assignedCustomers = Optional.ofNullable(dashboard.getAssignedCustomers()).orElse(Collections.emptySet()).stream() @@ -116,8 +116,10 @@ public class DashboardImportService extends BaseEntityImportService exportData, Dashboard prepared, Dashboard existing) { - return super.compare(ctx, exportData, prepared, existing) || !prepared.getConfiguration().equals(existing.getConfiguration()); + protected CompareResult compare(EntitiesImportCtx ctx, EntityExportData exportData, Dashboard prepared, Dashboard existing) { + CompareResult result = super.compare(ctx, exportData, prepared, existing); + result.setNeedUpdate(result.isNeedUpdate() || !prepared.getConfiguration().equals(existing.getConfiguration())); + return result; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceImportService.java index 84e264efdd..4ace9ff938 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceImportService.java @@ -63,7 +63,7 @@ public class DeviceImportService extends BaseEntityImportService exportData, IdProvider idProvider) { + protected DeviceProfile saveOrUpdate(EntitiesImportCtx ctx, DeviceProfile deviceProfile, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { DeviceProfile saved = deviceProfileService.saveDeviceProfile(deviceProfile); if (ctx.isFinalImportAttempt() || ctx.getCurrentImportResult().isUpdatedAllExternalIds()) { importCalculatedFields(ctx, saved, exportData, idProvider); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/EntityViewImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/EntityViewImportService.java index 8e8f2e90a6..1479943b08 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/EntityViewImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/EntityViewImportService.java @@ -55,7 +55,7 @@ public class EntityViewImportService extends BaseEntityImportService exportData, IdProvider idProvider) { + protected EntityView saveOrUpdate(EntitiesImportCtx ctx, EntityView entityView, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { return entityViewService.saveEntityView(entityView); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java index 52f91912e6..a2ae7c8a13 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java @@ -135,7 +135,7 @@ public class NotificationRuleImportService extends BaseEntityImportService exportData, IdProvider idProvider) { + protected NotificationRule saveOrUpdate(EntitiesImportCtx ctx, NotificationRule notificationRule, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { ConstraintValidator.validateFields(notificationRule); return notificationRuleService.saveNotificationRule(ctx.getTenantId(), notificationRule); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java index 9bca0f8054..4323aba9cc 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java @@ -80,7 +80,7 @@ public class NotificationTargetImportService extends BaseEntityImportService exportData, IdProvider idProvider) { + protected NotificationTarget saveOrUpdate(EntitiesImportCtx ctx, NotificationTarget notificationTarget, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { ConstraintValidator.validateFields(notificationTarget); return notificationTargetService.saveNotificationTarget(ctx.getTenantId(), notificationTarget); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTemplateImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTemplateImportService.java index 1452321744..09a93e0937 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTemplateImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTemplateImportService.java @@ -48,7 +48,7 @@ public class NotificationTemplateImportService extends BaseEntityImportService exportData, IdProvider idProvider) { + protected NotificationTemplate saveOrUpdate(EntitiesImportCtx ctx, NotificationTemplate notificationTemplate, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { ConstraintValidator.validateFields(notificationTemplate); return notificationTemplateService.saveNotificationTemplate(ctx.getTenantId(), notificationTemplate); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java index bedda621e0..e97a5ad06e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java @@ -58,17 +58,18 @@ public class ResourceImportService extends BaseEntityImportService exportData, TbResource prepared, TbResource existing) { - return true; + protected TbResource deepCopy(TbResource resource) { + return new TbResource(resource); } @Override - protected TbResource deepCopy(TbResource resource) { - return new TbResource(resource); + protected void cleanupForComparison(TbResource resource) { + super.cleanupForComparison(resource); + resource.setSearchText(null); } @Override - protected TbResource saveOrUpdate(EntitiesImportCtx ctx, TbResource resource, EntityExportData exportData, IdProvider idProvider) { + protected TbResource saveOrUpdate(EntitiesImportCtx ctx, TbResource resource, EntityExportData exportData, IdProvider idProvider, CompareResult compareResult) { if (resource.getResourceType() == ResourceType.IMAGE) { return new TbResource(imageService.saveImage(resource)); } else { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java index a7c68af297..6ad2b98953 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java @@ -103,7 +103,7 @@ public class RuleChainImportService extends BaseEntityImportService { String bundleAlias = widgetTypeNode.remove("bundleAlias").asText(); @@ -75,8 +75,8 @@ public class WidgetsBundleImportService extends BaseEntityImportService analyze(Throwable e, EntityId externalId) { diff --git a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java index fb6125bc0b..b1c71c9ded 100644 --- a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java @@ -54,7 +54,6 @@ public class LwM2mObjectModelUtils { if (resource.getId() == null) { resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.version); } - resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name); } else { throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText())); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index a3bf383503..89f5fa38b0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +import java.util.Objects; import java.util.function.UnaryOperator; @Schema @@ -151,4 +152,48 @@ public class TbResourceInfo extends BaseData implements HasName, H this.descriptor = value != null ? mapper.valueToTree(value) : null; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + TbResourceInfo that = (TbResourceInfo) o; + + if (isPublic != that.isPublic) return false; + if (!Objects.equals(tenantId, that.tenantId)) return false; + if (!Objects.equals(title, that.title)) return false; + if (resourceType != that.resourceType) return false; + if (resourceSubType != that.resourceSubType) return false; + if (!Objects.equals(resourceKey, that.resourceKey)) return false; + if (!Objects.equals(publicResourceKey, that.publicResourceKey)) + return false; + if (!Objects.equals(searchText, that.searchText)) return false; + if (!Objects.equals(etag, that.etag)) return false; + if (!Objects.equals(fileName, that.fileName)) return false; + if (!Objects.equals(descriptor, that.descriptor)) { + if (!((descriptor == null || descriptor.isNull()) && (that.descriptor == null || that.descriptor.isNull()))){ + return false; + } + } + return Objects.equals(externalId, that.externalId); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); + result = 31 * result + (title != null ? title.hashCode() : 0); + result = 31 * result + (resourceType != null ? resourceType.hashCode() : 0); + result = 31 * result + (resourceSubType != null ? resourceSubType.hashCode() : 0); + result = 31 * result + (resourceKey != null ? resourceKey.hashCode() : 0); + result = 31 * result + (isPublic ? 1 : 0); + result = 31 * result + (publicResourceKey != null ? publicResourceKey.hashCode() : 0); + result = 31 * result + (searchText != null ? searchText.hashCode() : 0); + result = 31 * result + (etag != null ? etag.hashCode() : 0); + result = 31 * result + (fileName != null ? fileName.hashCode() : 0); + result = 31 * result + (descriptor != null ? descriptor.hashCode() : 0); + result = 31 * result + (externalId != null ? externalId.hashCode() : 0); + return result; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/EntityLoadError.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/EntityLoadError.java index 049a7e5d2a..157f3bf553 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/EntityLoadError.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/EntityLoadError.java @@ -45,11 +45,15 @@ public class EntityLoadError implements Serializable { } public static EntityLoadError runtimeError(Throwable e) { + return runtimeError(e, null); + } + + public static EntityLoadError runtimeError(Throwable e, EntityId externalId) { String message = e.getMessage(); if (StringUtils.isEmpty(message)) { message = "unexpected error (" + ClassUtils.getShortClassName(e.getClass()) + ")"; } - return EntityLoadError.builder().type("RUNTIME").message(message).build(); + return EntityLoadError.builder().type("RUNTIME").message(message).source(externalId).build(); } } From 29e9a3d122e07de10916980c0fa79cf3a6699850 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 12 Mar 2025 18:31:30 +0200 Subject: [PATCH 022/335] added test --- .../importing/impl/ResourceImportService.java | 6 ++- .../service/sync/vc/VersionControlTest.java | 44 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java index e97a5ad06e..f5ca15cfca 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java @@ -73,7 +73,11 @@ public class ResourceImportService extends BaseEntityImportService & HasTenantId> void checkImportedEntity(TenantId tenantId1, E initialEntity, TenantId tenantId2, E importedEntity) { assertThat(initialEntity.getTenantId()).isEqualTo(tenantId1); assertThat(importedEntity.getTenantId()).isEqualTo(tenantId2); @@ -1126,4 +1152,22 @@ public class VersionControlTest extends AbstractControllerTest { return doGetTypedWithPageLink("/api/" + entityId.getEntityType() + "/" + entityId.getId() + "/calculatedFields?", new TypeReference>() {}, new PageLink(100, 0)).getData(); } + private TbResourceInfo createResource(String name) { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle(name); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setEncodedData(TEST_DATA); + + return saveTbResource(resource); + } + + private TbResourceInfo saveTbResource(TbResource tbResource) { + return doPost("/api/resource", tbResource, TbResourceInfo.class); + } + + private TbResource findResource(String name) throws Exception { + return doGetTypedWithPageLink("/api/resource?", new TypeReference>() {}, new PageLink(100, 0, name)).getData().get(0); + } + } From e1dfa3f80329dd3a29d70ddc223c5d9036c512f3 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 12 Mar 2025 19:24:28 +0200 Subject: [PATCH 023/335] refactoring --- .../impl/BaseEntityImportService.java | 28 +++++++++++-------- .../impl/DashboardImportService.java | 2 +- .../impl/RuleChainImportService.java | 4 +-- .../impl/WidgetTypeImportService.java | 2 +- .../impl/WidgetsBundleImportService.java | 2 +- .../controller/TbResourceControllerTest.java | 4 +-- .../service/sync/vc/VersionControlTest.java | 5 ++-- 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java index 83052360b6..27e4209e7c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java @@ -121,7 +121,7 @@ public abstract class BaseEntityImportService exportData, Dashboard prepared, Dashboard existing) { CompareResult result = super.compare(ctx, exportData, prepared, existing); - result.setNeedUpdate(result.isNeedUpdate() || !prepared.getConfiguration().equals(existing.getConfiguration())); + result.setUpdateNeeded(result.isUpdateNeeded() || !prepared.getConfiguration().equals(existing.getConfiguration())); return result; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java index 6ad2b98953..f5b4521629 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java @@ -117,11 +117,11 @@ public class RuleChainImportService extends BaseEntityImportService Date: Thu, 13 Mar 2025 15:08:14 +0200 Subject: [PATCH 024/335] fix_bug_lwm2m: add block2 condition --- .../server/DefaultLwM2mTransportService.java | 5 +-- .../server/LwM2mTransportCoapResource.java | 32 +++++++++++++------ .../lwm2m/utils/LwM2MTransportUtil.java | 7 ++++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 2ca0249b46..52859eab2f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.CoapResource; import org.eclipse.californium.core.CoapServer; +import org.eclipse.californium.core.config.CoapConfig; import org.eclipse.californium.elements.config.Configuration; import org.eclipse.californium.scandium.config.DtlsConfig; import org.eclipse.californium.scandium.dtls.cipher.CipherSuite; @@ -194,8 +195,8 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { root = new CoapResource(""); coapServer.add(root); } - root.add(new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE)); - root.add(new LwM2mTransportCoapResource(otaPackageDataCache, SOFTWARE_UPDATE_COAP_RESOURCE)); + root.add(new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE, serverCoapConfig.get(CoapConfig.PREFERRED_BLOCK_SIZE), serverCoapConfig.get(CoapConfig.MAX_RESOURCE_BODY_SIZE))); + root.add(new LwM2mTransportCoapResource(otaPackageDataCache, SOFTWARE_UPDATE_COAP_RESOURCE,serverCoapConfig.get(CoapConfig.PREFERRED_BLOCK_SIZE), serverCoapConfig.get(CoapConfig.MAX_RESOURCE_BODY_SIZE))); } return leshanServer; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index 7e9841ea9f..c46b57fb3f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -34,16 +34,21 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RESOURCE; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_UPDATE_COAP_RESOURCE; +import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.calculateSzx; @Slf4j public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { private final ConcurrentMap tokenToObserveRelationMap = new ConcurrentHashMap<>(); private final ConcurrentMap tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>(); private final OtaPackageDataCache otaPackageDataCache; + private final int chunkSize; + private final int maxResourceBodySize; - public LwM2mTransportCoapResource(OtaPackageDataCache otaPackageDataCache, String name) { + public LwM2mTransportCoapResource(OtaPackageDataCache otaPackageDataCache, String name, int chunkSize, int maxResourceBodySize) { super(name); this.otaPackageDataCache = otaPackageDataCache; + this.chunkSize = chunkSize; + this.maxResourceBodySize = maxResourceBodySize; this.setObservable(true); // enable observing this.addObserver(new CoapResourceObserver()); } @@ -136,22 +141,29 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { String idStr = exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size() - 1 ); UUID currentId = UUID.fromString(idStr); + log.info("Start Read ota data (path): [{}]", exchange.getRequestOptions().getUriPath().toString()); Response response = new Response(CoAP.ResponseCode.CONTENT); byte[] otaData = this.getOtaData(currentId); if (otaData != null && otaData.length > 0) { - log.debug("Read ota data (length): [{}]", otaData.length); - response.setPayload(otaData); - if (exchange.getRequestOptions().getBlock2() != null) { - int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); - boolean lastFlag = otaData.length <= chunkSize; + if (otaData.length <= this.maxResourceBodySize) { + log.info("Read ota data (length): [{}]", otaData.length); + response.setPayload(otaData); + int chunkSize = calculateSzx(this.chunkSize); + if (exchange.getRequestOptions().hasBlock2()) { + chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); + } else if (exchange.getRequestOptions().hasBlock1()) { + chunkSize = exchange.getRequestOptions().getBlock1().getSzx(); + } + log.info("With block2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), otaData.length, chunkSize, false); + boolean lastFlag = otaData.length <= this.chunkSize; response.getOptions().setBlock2(chunkSize, lastFlag, 0); - log.trace("With block2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), otaData.length, chunkSize, lastFlag); + response.setType(CoAP.Type.CON); + exchange.respond(response); } else { - log.trace("With block1 Send currentId: [{}], length: [{}], ", currentId.toString(), otaData.length); + log.info("Ota package size: [{}] is larger than server's MAX_RESOURCE_BODY_SIZE [{}]", otaData.length, this.maxResourceBodySize); } - exchange.respond(response); } else { - log.trace("Ota packaged currentId: [{}] is not found.", currentId.toString()); + log.info("Ota packaged currentId: [{}] is not found.", currentId.toString()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java index 438bb752aa..3ee83766fe 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java @@ -393,4 +393,11 @@ public class LwM2MTransportUtil { serverCoapConfig.set(DTLS_CONNECTION_ID_NODE_ID, null); } } + + public static int calculateSzx(int size) { + if (size < 16 || size > 1024 || (size & (size - 1)) != 0) { + throw new IllegalArgumentException("Size must be a power of 2 between 16 and 1024."); + } + return (int) (Math.log(size / 16) / Math.log(2)); + } } From ccda83a9a5ba1cc44a44118485a7516d6490cf73 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 13 Mar 2025 18:33:56 +0200 Subject: [PATCH 025/335] compare method refactoring --- .../importing/impl/BaseEntityImportService.java | 16 ++++++++++++---- .../importing/impl/DashboardImportService.java | 6 ++---- .../importing/impl/RuleChainImportService.java | 12 +++++++----- .../server/utils/LwM2mObjectModelUtils.java | 1 + .../service/sync/vc/VersionControlTest.java | 4 ++-- .../server/common/data/TbResourceInfo.java | 2 +- 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java index 27e4209e7c..57b4737be6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java @@ -141,7 +141,7 @@ public abstract class BaseEntityImportService exportData, Dashboard prepared, Dashboard existing) { - CompareResult result = super.compare(ctx, exportData, prepared, existing); - result.setUpdateNeeded(result.isUpdateNeeded() || !prepared.getConfiguration().equals(existing.getConfiguration())); - return result; + protected boolean isUpdateNeeded(EntitiesImportCtx ctx, EntityExportData exportData, Dashboard prepared, Dashboard existing) { + return super.isUpdateNeeded(ctx, exportData, prepared, existing) || !prepared.getConfiguration().equals(existing.getConfiguration()); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java index f5b4521629..baf1b84f82 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.sync.ie.importing.impl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; @@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.sync.ie.EntityExportData; import org.thingsboard.server.common.data.sync.ie.RuleChainExportData; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleNodeDao; @@ -115,15 +117,15 @@ public class RuleChainImportService extends BaseEntityImportService implements HasName, H if (!Objects.equals(resourceKey, that.resourceKey)) return false; if (!Objects.equals(publicResourceKey, that.publicResourceKey)) return false; - if (!Objects.equals(searchText, that.searchText)) return false; + if (!Objects.equals(getSearchText(), that.getSearchText())) return false; if (!Objects.equals(etag, that.etag)) return false; if (!Objects.equals(fileName, that.fileName)) return false; if (!Objects.equals(descriptor, that.descriptor)) { From 753071ea17fbb4eb74af7d88839f525f459683b8 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 14 Mar 2025 14:48:32 +0100 Subject: [PATCH 026/335] minor refactoring --- .../update/DefaultDataUpdateService.java | 24 +++++++------------ .../server/dao/rule/BaseRuleChainService.java | 6 ++--- .../dao/service/RuleChainServiceTest.java | 5 ++-- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index b1f80cb793..4c90258330 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -49,7 +49,7 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; -import static org.thingsboard.server.common.data.relation.EntityRelation.USES_TYPE; +import static org.thingsboard.server.dao.rule.BaseRuleChainService.TB_RULE_CHAIN_INPUT_NODE; @Service @Profile("install") @@ -98,28 +98,22 @@ public class DefaultDataUpdateService implements DataUpdateService { protected void updateEntity(Tenant tenant) { TenantId tenantId = tenant.getId(); try { - var inputNodes = ruleChainService.findRuleNodesByTenantIdAndType(tenantId, "org.thingsboard.rule.engine.flow.TbRuleChainInputNode"); + var inputNodes = ruleChainService.findRuleNodesByTenantIdAndType(tenantId, TB_RULE_CHAIN_INPUT_NODE); var resultFutures = inputNodes.stream().map(ruleNode -> { try { JsonNode id = ruleNode.getConfiguration().get("ruleChainId"); if (id != null) { RuleChainId toRuleChainId = new RuleChainId(UUID.fromString(id.asText())); RuleChainId fromRuleChainId = ruleNode.getRuleChainId(); - var isExistFuture = relationService.checkRelationAsync(null, fromRuleChainId, toRuleChainId, USES_TYPE, RelationTypeGroup.COMMON); - Futures.transformAsync(isExistFuture, isExist -> { - if (!isExist) { - EntityRelation relation = new EntityRelation(); - relation.setFrom(fromRuleChainId); - relation.setTo(toRuleChainId); - relation.setType(EntityRelation.USES_TYPE); - relation.setTypeGroup(RelationTypeGroup.COMMON); - return relationService.saveRelationAsync(tenantId, relation); - } - return Futures.immediateFuture(null); - }, executorService); + EntityRelation relation = new EntityRelation(); + relation.setFrom(fromRuleChainId); + relation.setTo(toRuleChainId); + relation.setType(EntityRelation.USES_TYPE); + relation.setTypeGroup(RelationTypeGroup.COMMON); + return relationService.saveRelationAsync(tenantId, relation); } } catch (Exception e) { - log.error("[{}] Create relation for input node: [{}]", tenantId, ruleNode, e); + log.error("[{}] Failed to save relation for input node: [{}]", tenantId, ruleNode, e); } return Futures.immediateFuture(null); }).toList(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 9d61408199..1ce9bd555b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -210,7 +210,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC List existingRuleNodes = getRuleChainNodes(tenantId, ruleChainMetaData.getRuleChainId()); for (RuleNode existingNode : existingRuleNodes) { relationService.deleteEntityRelations(tenantId, existingNode.getId()); - if (existingNode.getType().equals("org.thingsboard.rule.engine.flow.TbRuleChainInputNode")) { + if (existingNode.getType().equals(TB_RULE_CHAIN_INPUT_NODE)) { if (existingNode.getConfiguration().has("ruleChainId")) { RuleChainId targetRuleChainId = extractRuleChainIdFromInputNode(existingNode); var relation = createRuleChainInputRelation(ruleChainId, targetRuleChainId); @@ -241,7 +241,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC RuleNode savedNode = ruleNodeDao.save(tenantId, node); relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); - if (node.getType().equals("org.thingsboard.rule.engine.flow.TbRuleChainInputNode")) { + if (node.getType().equals(TB_RULE_CHAIN_INPUT_NODE)) { if (node.getConfiguration().has("ruleChainId")) { RuleChainId targetRuleChainId = extractRuleChainIdFromInputNode(node); var relation = createRuleChainInputRelation(ruleChainId, targetRuleChainId); @@ -280,7 +280,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC RuleNode targetNode = new RuleNode(); targetNode.setName(targetRuleChain != null ? targetRuleChain.getName() : "Rule Chain Input"); targetNode.setRuleChainId(ruleChainId); - targetNode.setType("org.thingsboard.rule.engine.flow.TbRuleChainInputNode"); + targetNode.setType(TB_RULE_CHAIN_INPUT_NODE); var configuration = JacksonUtil.newObjectNode(); configuration.put("ruleChainId", targetRuleChainId.getId().toString()); targetNode.setConfiguration(configuration); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java index 54144d5192..a2ca663619 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java @@ -48,6 +48,7 @@ import java.util.function.Function; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.thingsboard.server.common.data.relation.EntityRelation.USES_TYPE; +import static org.thingsboard.server.dao.rule.BaseRuleChainService.TB_RULE_CHAIN_INPUT_NODE; /** * Created by igor on 3/13/18. @@ -377,7 +378,7 @@ public class RuleChainServiceTest extends AbstractServiceTest { RuleNode ruleNode = new RuleNode(); ruleNode.setName("Input node"); - ruleNode.setType("org.thingsboard.rule.engine.flow.TbRuleChainInputNode"); + ruleNode.setType(TB_RULE_CHAIN_INPUT_NODE); ObjectNode configuration = JacksonUtil.newObjectNode(); configuration.put("ruleChainId", savedToRuleChain.getId().toString()); ruleNode.setConfiguration(configuration); @@ -402,7 +403,7 @@ public class RuleChainServiceTest extends AbstractServiceTest { RuleNode newRuleNode = new RuleNode(); newRuleNode.setName("Input node"); - newRuleNode.setType("org.thingsboard.rule.engine.flow.TbRuleChainInputNode"); + newRuleNode.setType(TB_RULE_CHAIN_INPUT_NODE); ObjectNode newConfiguration = JacksonUtil.newObjectNode(); configuration.put("ruleChainId", savedNewToRuleChain.getId().toString()); newRuleNode.setConfiguration(newConfiguration); From da2bada90bc7b8eedc25fcfc691d8ff7eff0bdbc Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 14 Mar 2025 15:52:05 +0200 Subject: [PATCH 027/335] Merge with RC --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/edqs/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- edqs/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/edqs/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 59 files changed, 60 insertions(+), 60 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 7960db3374..6ce77646f8 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 76d000724b..71b7e2eb6e 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 3d549da8e6..27a536dc28 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index 722645a0da..adc77dce22 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index ffe1811b62..3f020b289a 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 4a11a8a574..b1b55a9461 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index 2ece9779b5..5895974d82 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 096dd911be..f992486149 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index 4667b5cbe6..f7106e56fd 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index d8127f6327..380621a5ad 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index aecd6ba841..9a7a836ba5 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 712807dddb..bcfb3dfc85 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 0275d71b86..e28ebfbcf0 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 681750024d..80a8c4664a 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index c398444fa6..fc15448316 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-RC + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 3655692217..3a51b99f35 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-RC + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 5319a40ac1..db8367180a 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index dbfe72c515..887dc014e8 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 358ecb72e2..4354d14a1c 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 2100d5db22..f918cdd167 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 735fe9a2d2..34eeed396d 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 2ee0f4f3c8..27b9bcbc9e 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 48a23b4e91..b971de293c 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.0.0-RC + 4.0.0-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index a671358581..423fc5dc97 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 430a6e7df9..83302c8c80 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 8fc4cb2566..22beed80d9 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 0bc6541ac1..35baa9b13b 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard dao diff --git a/edqs/pom.xml b/edqs/pom.xml index 9faf022d17..07b25102d4 100644 --- a/edqs/pom.xml +++ b/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard edqs diff --git a/monitoring/pom.xml b/monitoring/pom.xml index f89a57e477..b7735aed4c 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 424891328e..c3379cf37a 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml index 49d6fa71c9..f22cb0187c 100644 --- a/msa/edqs/pom.xml +++ b/msa/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 22e553fbb3..9f89413b4b 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index a1208a6258..3de71adb36 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index ce8b422d05..98e820daaf 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 346776698a..b47aec986c 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index ad3ec5f7c2..1e9774cb42 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index e3f51c2044..71c86d50a4 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 193d2e28d1..e6b5611166 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 1722cb906e..9088a9c996 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index c03c22eef3..a61bc62921 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 4d9e341369..46ea6612f9 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 21e1d3c5d0..0664a89d9a 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.0.0-RC + 4.0.0-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 509807600f..c7e3ee5e11 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 8a3d922269..f2e4395766 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index c7e4e3adf4..f6f06c994a 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index b5fd83a54f..bf55ec3083 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard netty-mqtt - 4.0.0-RC + 4.0.0-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 72195b1e4f..c0dc7384f7 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index fd96de9f70..09f7e34c2c 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 8a4fbf7601..b3681c9d69 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 6993789e0f..d036695cd3 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index eb568f9cc7..4edafdf69e 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index bc8e748c6e..26e0543ab1 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 0c6de9ea4e..3cf7d677bf 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 226bab23b3..91fdd39755 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 6f24b35684..cc929a0090 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 4d92c1f0e6..da818e032b 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 8be6fff261..66026fa10b 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 62e1f753e5..0a36ea1f90 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index c6e5018d46..eefea60273 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-RC + 4.0.0-SNAPSHOT thingsboard org.thingsboard From aef21182ee1f25282bb5a048e250538939806c49 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 14 Mar 2025 16:41:38 +0200 Subject: [PATCH 028/335] Remove link to Google Groups --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6bce2db844..60f172d5a8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ Collect and Visualize your IoT data in minutes by following this [guide](https:/ ## Support - - [Q&A forum](https://groups.google.com/forum/#!forum/thingsboard) - [Stackoverflow](http://stackoverflow.com/questions/tagged/thingsboard) ## Licenses From b487eba13acd71bcf86cf5cc96a0b835b0d197db Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 13 Mar 2025 18:39:28 +0100 Subject: [PATCH 029/335] bump Cassandra version to 5.0 for tests and docker compose --- .../java/org/thingsboard/server/dao/AbstractNoSqlContainer.java | 2 +- docker/docker-compose.hybrid.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/AbstractNoSqlContainer.java b/dao/src/test/java/org/thingsboard/server/dao/AbstractNoSqlContainer.java index 07a62a6391..cadf7cc4f1 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/AbstractNoSqlContainer.java +++ b/dao/src/test/java/org/thingsboard/server/dao/AbstractNoSqlContainer.java @@ -41,7 +41,7 @@ public abstract class AbstractNoSqlContainer { ); @ClassRule(order = 0) - public static final CassandraContainer cassandra = (CassandraContainer) new CassandraContainer("cassandra:4.1") { + public static final CassandraContainer cassandra = (CassandraContainer) new CassandraContainer("cassandra:5.0") { @Override protected void containerIsStarted(InspectContainerResponse containerInfo) { super.containerIsStarted(containerInfo); diff --git a/docker/docker-compose.hybrid.yml b/docker/docker-compose.hybrid.yml index dc5e875fc4..3ced5385f1 100644 --- a/docker/docker-compose.hybrid.yml +++ b/docker/docker-compose.hybrid.yml @@ -29,7 +29,7 @@ services: - ./tb-node/postgres:/var/lib/postgresql/data cassandra: restart: always - image: "cassandra:4.1" + image: "cassandra:5.0" ports: - "9042" volumes: From 2867082776416ac35496201ce1711efe69879bb9 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 13 Mar 2025 18:12:33 +0100 Subject: [PATCH 030/335] Testcontainers: valkey images instead of redis --- .../dao/AbstractRedisClusterContainer.java | 42 +++++++++++++------ .../server/dao/AbstractRedisContainer.java | 5 ++- .../server/dao/RedisJUnit5Test.java | 3 +- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisClusterContainer.java b/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisClusterContainer.java index 9a6e1e514e..43a65cb149 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisClusterContainer.java +++ b/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisClusterContainer.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao; import lombok.extern.slf4j.Slf4j; +import org.assertj.core.api.Assertions; import org.junit.ClassRule; import org.junit.rules.ExternalResource; import org.testcontainers.containers.GenericContainer; @@ -23,27 +24,35 @@ import org.testcontainers.containers.Network; import org.testcontainers.containers.output.OutputFrame; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; @Slf4j public class AbstractRedisClusterContainer { static final String nodes = "127.0.0.1:6371,127.0.0.1:6372,127.0.0.1:6373,127.0.0.1:6374,127.0.0.1:6375,127.0.0.1:6376"; - + static Map envs = Map.of( + "VALKEY_CLUSTER_ANNOUNCE_IP", "127.0.0.1", + "VALKEY_CLUSTER_DYNAMIC_IPS", "no", + "ALLOW_EMPTY_PASSWORD", "yes", + "VALKEY_NODES", nodes + + ); + @ClassRule(order = 0) public static Network network = Network.newNetwork(); @ClassRule(order = 1) - public static GenericContainer redis1 = new GenericContainer("bitnami/redis-cluster:latest").withEnv("REDIS_PORT_NUMBER", "6371").withNetworkMode("host").withLogConsumer(x -> log.warn("{}", ((OutputFrame) x).getUtf8StringWithoutLineEnding())).withEnv("ALLOW_EMPTY_PASSWORD", "yes").withEnv("REDIS_NODES", nodes); + public static GenericContainer redis1 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6371").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 2) - public static GenericContainer redis2 = new GenericContainer("bitnami/redis-cluster:latest").withEnv("REDIS_PORT_NUMBER", "6372").withNetworkMode("host").withLogConsumer(x -> log.warn("{}", ((OutputFrame) x).getUtf8StringWithoutLineEnding())).withEnv("ALLOW_EMPTY_PASSWORD", "yes").withEnv("REDIS_NODES", nodes); + public static GenericContainer redis2 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6372").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 3) - public static GenericContainer redis3 = new GenericContainer("bitnami/redis-cluster:latest").withEnv("REDIS_PORT_NUMBER", "6373").withNetworkMode("host").withLogConsumer(x -> log.warn("{}", ((OutputFrame) x).getUtf8StringWithoutLineEnding())).withEnv("ALLOW_EMPTY_PASSWORD", "yes").withEnv("REDIS_NODES", nodes); + public static GenericContainer redis3 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6373").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 4) - public static GenericContainer redis4 = new GenericContainer("bitnami/redis-cluster:latest").withEnv("REDIS_PORT_NUMBER", "6374").withNetworkMode("host").withLogConsumer(x -> log.warn("{}", ((OutputFrame) x).getUtf8StringWithoutLineEnding())).withEnv("ALLOW_EMPTY_PASSWORD", "yes").withEnv("REDIS_NODES", nodes); + public static GenericContainer redis4 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6374").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 5) - public static GenericContainer redis5 = new GenericContainer("bitnami/redis-cluster:latest").withEnv("REDIS_PORT_NUMBER", "6375").withNetworkMode("host").withLogConsumer(x -> log.warn("{}", ((OutputFrame) x).getUtf8StringWithoutLineEnding())).withEnv("ALLOW_EMPTY_PASSWORD", "yes").withEnv("REDIS_NODES", nodes); + public static GenericContainer redis5 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6375").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 6) - public static GenericContainer redis6 = new GenericContainer("bitnami/redis-cluster:latest").withEnv("REDIS_PORT_NUMBER", "6376").withNetworkMode("host").withLogConsumer(x -> log.warn("{}", ((OutputFrame) x).getUtf8StringWithoutLineEnding())).withEnv("ALLOW_EMPTY_PASSWORD", "yes").withEnv("REDIS_NODES", nodes); + public static GenericContainer redis6 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6376").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 100) @@ -59,12 +68,16 @@ public class AbstractRedisClusterContainer { Thread.sleep(TimeUnit.SECONDS.toMillis(5)); // otherwise not all containers have time to start - String clusterCreateCommand = "echo yes | redis-cli --cluster create " + - "127.0.0.1:6371 127.0.0.1:6372 127.0.0.1:6373 127.0.0.1:6374 127.0.0.1:6375 127.0.0.1:6376 " + - "--cluster-replicas 1"; - log.warn("Command to init Redis Cluster: {}", clusterCreateCommand); - var result = redis6.execInContainer("/bin/sh", "-c", clusterCreateCommand); + redis6.execInContainer("/bin/sh", "-c", "printenv | grep VALKEY"); //"sleep infinity" + + String clusterCreateCommand = "" + + "valkey-cli --cluster create " +nodes.replace(","," ") + " --cluster-replicas 1 --cluster-yes" + + ""; + log.warn("Command to init ValKey Cluster: {}", clusterCreateCommand); + var result = redis6.execInContainer("/bin/sh", "-c", clusterCreateCommand); //"sleep infinity" +// result.wait(TimeUnit.SECONDS.toMillis(300)); log.warn("Init cluster result: {}", result); + Assertions.assertThat(result.getExitCode()).isEqualTo(0); Thread.sleep(TimeUnit.SECONDS.toMillis(5)); // otherwise cluster not always ready @@ -83,9 +96,12 @@ public class AbstractRedisClusterContainer { redis4.stop(); redis5.stop(); redis6.stop(); - List.of("cache.type", "redis.connection.type", "redis.cluster.nodes", "redis.cluster.useDefaultPoolConfig\"") + List.of("cache.type", "redis.connection.type", "redis.cluster.nodes", "redis.cluster.useDefaultPoolConfig") .forEach(System.getProperties()::remove); } }; + private static void consumeLog(Object x) { + log.warn("{}", ((OutputFrame) x).getUtf8StringWithoutLineEnding()); + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisContainer.java b/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisContainer.java index 816c6609ec..58843a4651 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisContainer.java +++ b/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisContainer.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.junit.ClassRule; import org.junit.rules.ExternalResource; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.OutputFrame; import java.util.List; @@ -26,7 +27,9 @@ import java.util.List; public class AbstractRedisContainer { @ClassRule(order = 0) - public static GenericContainer redis = new GenericContainer("redis:7.2") + public static GenericContainer redis = new GenericContainer("bitnami/valkey:8.0") + .withEnv("ALLOW_EMPTY_PASSWORD","yes") + .withLogConsumer(s -> log.warn(((OutputFrame) s).getUtf8String().trim())) .withExposedPorts(6379); @ClassRule(order = 1) diff --git a/dao/src/test/java/org/thingsboard/server/dao/RedisJUnit5Test.java b/dao/src/test/java/org/thingsboard/server/dao/RedisJUnit5Test.java index d83e9bd137..5d43ffb4ae 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/RedisJUnit5Test.java +++ b/dao/src/test/java/org/thingsboard/server/dao/RedisJUnit5Test.java @@ -33,7 +33,8 @@ import static org.assertj.core.api.Assertions.assertThat; public class RedisJUnit5Test { @Container - private static final GenericContainer REDIS = new GenericContainer("redis:7.2-bookworm") + private static final GenericContainer REDIS = new GenericContainer("bitnami/valkey:8.0") + .withEnv("ALLOW_EMPTY_PASSWORD","yes") .withLogConsumer(s -> log.error(((OutputFrame) s).getUtf8String().trim())) .withExposedPorts(6379); From 1352475301fd50539eae094ada034fcb41c6509a Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 13 Mar 2025 18:17:33 +0100 Subject: [PATCH 031/335] Docker-compose: valkey images replacement for redis, redis-cluster, redis ssl --- docker/docker-compose.redis-cluster.yml | 46 ++++++++----------- docker/docker-compose.redis.yml | 2 +- .../resources/docker-compose.redis-ssl.yml | 12 ++--- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/docker/docker-compose.redis-cluster.yml b/docker/docker-compose.redis-cluster.yml index e27993bf03..63051176c9 100644 --- a/docker/docker-compose.redis-cluster.yml +++ b/docker/docker-compose.redis-cluster.yml @@ -20,60 +20,55 @@ services: # Redis cluster # The latest version of Redis compatible with ThingsBoard is 7.2 redis-node-0: - image: bitnami/redis-cluster:7.2 + image: bitnami/valkey-cluster:8.0 volumes: - ./tb-node/redis-cluster-data-0:/bitnami/redis/data environment: - - 'REDIS_PASSWORD=thingsboard' - - 'REDISCLI_AUTH=thingsboard' - - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' redis-node-1: - image: bitnami/redis-cluster:7.2 + image: bitnami/valkey-cluster:8.0 volumes: - ./tb-node/redis-cluster-data-1:/bitnami/redis/data depends_on: - redis-node-0 environment: - - 'REDIS_PASSWORD=thingsboard' - - 'REDISCLI_AUTH=thingsboard' - - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' redis-node-2: - image: bitnami/redis-cluster:7.2 + image: bitnami/valkey-cluster:8.0 volumes: - ./tb-node/redis-cluster-data-2:/bitnami/redis/data depends_on: - redis-node-1 environment: - - 'REDIS_PASSWORD=thingsboard' - - 'REDISCLI_AUTH=thingsboard' - - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' redis-node-3: - image: bitnami/redis-cluster:7.2 + image: bitnami/valkey-cluster:8.0 volumes: - ./tb-node/redis-cluster-data-3:/bitnami/redis/data depends_on: - redis-node-2 environment: - - 'REDIS_PASSWORD=thingsboard' - - 'REDISCLI_AUTH=thingsboard' - - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' redis-node-4: - image: bitnami/redis-cluster:7.2 + image: bitnami/valkey-cluster:8.0 volumes: - ./tb-node/redis-cluster-data-4:/bitnami/redis/data depends_on: - redis-node-3 environment: - - 'REDIS_PASSWORD=thingsboard' - - 'REDISCLI_AUTH=thingsboard' - - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' redis-node-5: - image: bitnami/redis-cluster:7.2 + image: bitnami/valkey-cluster:8.0 volumes: - ./tb-node/redis-cluster-data-5:/bitnami/redis/data depends_on: @@ -83,11 +78,10 @@ services: - redis-node-3 - redis-node-4 environment: - - 'REDIS_PASSWORD=thingsboard' - - 'REDISCLI_AUTH=thingsboard' - - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' - - 'REDIS_CLUSTER_REPLICAS=1' - - 'REDIS_CLUSTER_CREATOR=yes' + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + - 'VALKEY_CLUSTER_REPLICAS=1' + - 'VALKEY_CLUSTER_CREATOR=yes' # ThingsBoard setup to use redis-cluster tb-core1: diff --git a/docker/docker-compose.redis.yml b/docker/docker-compose.redis.yml index 8ed361428a..3e8934d9a0 100644 --- a/docker/docker-compose.redis.yml +++ b/docker/docker-compose.redis.yml @@ -21,7 +21,7 @@ services: # The latest version of Redis compatible with ThingsBoard is 7.2 redis: restart: always - image: bitnami/redis:7.2 + image: bitnami/valkey:8.0 environment: # ALLOW_EMPTY_PASSWORD is recommended only for development. ALLOW_EMPTY_PASSWORD: "yes" diff --git a/msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.yml b/msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.yml index 525e8d93dc..6ff24564b7 100644 --- a/msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.yml +++ b/msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.yml @@ -21,15 +21,15 @@ services: # The latest version of Redis compatible with ThingsBoard is 7.2 redis: restart: always - image: bitnami/redis:7.2 + image: bitnami/valkey:8.0 environment: # ALLOW_EMPTY_PASSWORD is recommended only for development. - 'ALLOW_EMPTY_PASSWORD=yes' - - 'REDIS_TLS_ENABLED=yes' - - 'REDIS_TLS_CERT_FILE=/redis/certs/redis.crt' - - 'REDIS_TLS_KEY_FILE=/redis/certs/redis.key' - - 'REDIS_TLS_CA_FILE=/redis/certs/redisCA.crt' - - 'REDIS_TLS_AUTH_CLIENTS=no' + - 'VALKEY_TLS_ENABLED=yes' + - 'VALKEY_TLS_CERT_FILE=/redis/certs/redis.crt' + - 'VALKEY_TLS_KEY_FILE=/redis/certs/redis.key' + - 'VALKEY_TLS_CA_FILE=/redis/certs/redisCA.crt' + - 'VALKEY_TLS_AUTH_CLIENTS=no' ports: - '6379:6379' volumes: From 21c6a77de91d10ccc7732f8dfcf9188589de0920 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 13 Mar 2025 18:28:03 +0100 Subject: [PATCH 032/335] AbstractRedisClusterContainer: code cleanup --- .../dao/AbstractRedisClusterContainer.java | 37 ++++++++----------- .../server/dao/RedisClusterSqlTestSuite.java | 2 +- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisClusterContainer.java b/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisClusterContainer.java index 43a65cb149..a1009cd255 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisClusterContainer.java +++ b/dao/src/test/java/org/thingsboard/server/dao/AbstractRedisClusterContainer.java @@ -30,29 +30,27 @@ import java.util.concurrent.TimeUnit; @Slf4j public class AbstractRedisClusterContainer { - static final String nodes = "127.0.0.1:6371,127.0.0.1:6372,127.0.0.1:6373,127.0.0.1:6374,127.0.0.1:6375,127.0.0.1:6376"; - static Map envs = Map.of( + static final String NODES = "127.0.0.1:6371,127.0.0.1:6372,127.0.0.1:6373,127.0.0.1:6374,127.0.0.1:6375,127.0.0.1:6376"; + static final String IMAGE = "bitnami/valkey-cluster:8.0"; + static final Map ENVS = Map.of( "VALKEY_CLUSTER_ANNOUNCE_IP", "127.0.0.1", "VALKEY_CLUSTER_DYNAMIC_IPS", "no", "ALLOW_EMPTY_PASSWORD", "yes", - "VALKEY_NODES", nodes - + "VALKEY_NODES", NODES ); - - @ClassRule(order = 0) - public static Network network = Network.newNetwork(); + @ClassRule(order = 1) - public static GenericContainer redis1 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6371").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); + public static GenericContainer redis1 = new GenericContainer(IMAGE).withEnv(ENVS).withEnv("VALKEY_PORT_NUMBER", "6371").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 2) - public static GenericContainer redis2 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6372").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); + public static GenericContainer redis2 = new GenericContainer(IMAGE).withEnv(ENVS).withEnv("VALKEY_PORT_NUMBER", "6372").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 3) - public static GenericContainer redis3 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6373").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); + public static GenericContainer redis3 = new GenericContainer(IMAGE).withEnv(ENVS).withEnv("VALKEY_PORT_NUMBER", "6373").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 4) - public static GenericContainer redis4 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6374").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); + public static GenericContainer redis4 = new GenericContainer(IMAGE).withEnv(ENVS).withEnv("VALKEY_PORT_NUMBER", "6374").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 5) - public static GenericContainer redis5 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6375").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); + public static GenericContainer redis5 = new GenericContainer(IMAGE).withEnv(ENVS).withEnv("VALKEY_PORT_NUMBER", "6375").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 6) - public static GenericContainer redis6 = new GenericContainer("bitnami/valkey-cluster:8.0").withEnv(envs).withEnv("VALKEY_PORT_NUMBER", "6376").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); + public static GenericContainer redis6 = new GenericContainer(IMAGE).withEnv(ENVS).withEnv("VALKEY_PORT_NUMBER", "6376").withNetworkMode("host").withLogConsumer(AbstractRedisClusterContainer::consumeLog); @ClassRule(order = 100) @@ -68,23 +66,18 @@ public class AbstractRedisClusterContainer { Thread.sleep(TimeUnit.SECONDS.toMillis(5)); // otherwise not all containers have time to start - redis6.execInContainer("/bin/sh", "-c", "printenv | grep VALKEY"); //"sleep infinity" - - String clusterCreateCommand = "" + - "valkey-cli --cluster create " +nodes.replace(","," ") + " --cluster-replicas 1 --cluster-yes" + - ""; + String clusterCreateCommand = "valkey-cli --cluster create " + NODES.replace(","," ") + " --cluster-replicas 1 --cluster-yes"; log.warn("Command to init ValKey Cluster: {}", clusterCreateCommand); - var result = redis6.execInContainer("/bin/sh", "-c", clusterCreateCommand); //"sleep infinity" -// result.wait(TimeUnit.SECONDS.toMillis(300)); + var result = redis6.execInContainer("/bin/sh", "-c", clusterCreateCommand); log.warn("Init cluster result: {}", result); Assertions.assertThat(result.getExitCode()).isEqualTo(0); Thread.sleep(TimeUnit.SECONDS.toMillis(5)); // otherwise cluster not always ready - log.warn("Connect to nodes: {}", nodes); + log.warn("Connect to nodes: {}", NODES); System.setProperty("cache.type", "redis"); System.setProperty("redis.connection.type", "cluster"); - System.setProperty("redis.cluster.nodes", nodes); + System.setProperty("redis.cluster.nodes", NODES); System.setProperty("redis.cluster.useDefaultPoolConfig", "false"); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/RedisClusterSqlTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/RedisClusterSqlTestSuite.java index 4a14b623f7..d15fc538a1 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/RedisClusterSqlTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/RedisClusterSqlTestSuite.java @@ -21,7 +21,7 @@ import org.junit.runner.RunWith; @RunWith(ClasspathSuite.class) @ClassnameFilters( - //All the same tests using redis instead of caffeine. + //All the same tests using ValKey instead of caffeine. { "org.thingsboard.server.dao.service.*ServiceSqlTest", } From 8813e32910790330ae7c4765f3876dc8866d958c Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 27 Mar 2025 11:59:49 +0200 Subject: [PATCH 033/335] moved resource vc comparison fix to cleanupForComparison method --- .../importing/impl/ResourceImportService.java | 3 ++ .../server/common/data/TbResourceInfo.java | 45 ------------------- 2 files changed, 3 insertions(+), 45 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java index f5ca15cfca..96fcdd5425 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java @@ -66,6 +66,9 @@ public class ResourceImportService extends BaseEntityImportService implements HasName, H this.descriptor = value != null ? mapper.valueToTree(value) : null; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - - TbResourceInfo that = (TbResourceInfo) o; - - if (isPublic != that.isPublic) return false; - if (!Objects.equals(tenantId, that.tenantId)) return false; - if (!Objects.equals(title, that.title)) return false; - if (resourceType != that.resourceType) return false; - if (resourceSubType != that.resourceSubType) return false; - if (!Objects.equals(resourceKey, that.resourceKey)) return false; - if (!Objects.equals(publicResourceKey, that.publicResourceKey)) - return false; - if (!Objects.equals(getSearchText(), that.getSearchText())) return false; - if (!Objects.equals(etag, that.etag)) return false; - if (!Objects.equals(fileName, that.fileName)) return false; - if (!Objects.equals(descriptor, that.descriptor)) { - if (!((descriptor == null || descriptor.isNull()) && (that.descriptor == null || that.descriptor.isNull()))){ - return false; - } - } - return Objects.equals(externalId, that.externalId); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); - result = 31 * result + (title != null ? title.hashCode() : 0); - result = 31 * result + (resourceType != null ? resourceType.hashCode() : 0); - result = 31 * result + (resourceSubType != null ? resourceSubType.hashCode() : 0); - result = 31 * result + (resourceKey != null ? resourceKey.hashCode() : 0); - result = 31 * result + (isPublic ? 1 : 0); - result = 31 * result + (publicResourceKey != null ? publicResourceKey.hashCode() : 0); - result = 31 * result + (searchText != null ? searchText.hashCode() : 0); - result = 31 * result + (etag != null ? etag.hashCode() : 0); - result = 31 * result + (fileName != null ? fileName.hashCode() : 0); - result = 31 * result + (descriptor != null ? descriptor.hashCode() : 0); - result = 31 * result + (externalId != null ? externalId.hashCode() : 0); - return result; - } } From a5ce56fe4c0c53d5004221aef7bc2297e6718a39 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 28 Mar 2025 18:49:45 +0200 Subject: [PATCH 034/335] removed check for timeseries keys existence --- .../rule/engine/profile/AlarmRuleState.java | 6 - .../profile/TbDeviceProfileNodeTest.java | 210 ++++++++++++------ 2 files changed, 145 insertions(+), 71 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java index 81c8f8154c..a2a714a6df 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java @@ -88,12 +88,6 @@ class AlarmRuleState { } public boolean validateAttrUpdate(Set changedKeys) { - //If the attribute was updated, but no new telemetry arrived - we ignore this until new telemetry is there. - for (AlarmConditionFilterKey key : entityKeys) { - if (key.getType().equals(AlarmConditionKeyType.TIME_SERIES)) { - return false; - } - } for (AlarmConditionFilterKey key : changedKeys) { if (entityKeys.contains(key)) { return true; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 7ecb1b2ad8..5163e21c05 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -58,6 +58,8 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.kv.TsKvEntryAggWrapper; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.query.BooleanFilterPredicate; import org.thingsboard.server.common.data.query.DynamicValue; @@ -65,6 +67,7 @@ import org.thingsboard.server.common.data.query.DynamicValueSourceType; import org.thingsboard.server.common.data.query.EntityKeyValueType; import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.NumericFilterPredicate; +import org.thingsboard.server.common.data.query.NumericFilterPredicate.NumericOperation; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -72,6 +75,7 @@ import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; import org.thingsboard.server.dao.model.sql.AttributeKvEntity; +import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; import org.thingsboard.server.dao.timeseries.TimeseriesService; import java.math.BigDecimal; @@ -81,15 +85,22 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType.ATTRIBUTE; +import static org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType.TIME_SERIES; +import static org.thingsboard.server.common.data.query.NumericFilterPredicate.NumericOperation.GREATER; +import static org.thingsboard.server.common.data.query.NumericFilterPredicate.NumericOperation.LESS; @ExtendWith(MockitoExtension.class) public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { @@ -170,32 +181,16 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { DeviceProfile deviceProfile = new DeviceProfile(); DeviceProfileData deviceProfileData = new DeviceProfileData(); - AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); - highTempFilter.setValueType(EntityKeyValueType.NUMERIC); - NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); - highTemperaturePredicate.setValue(new FilterPredicateValue<>(30.0)); - highTempFilter.setPredicate(highTemperaturePredicate); - AlarmCondition alarmCondition = new AlarmCondition(); - alarmCondition.setCondition(Collections.singletonList(highTempFilter)); + AlarmCondition alarmCreateCondition = getNumericAlarmCondition(TIME_SERIES, "temperature", GREATER, 30.0); AlarmRule alarmRule = new AlarmRule(); - alarmRule.setCondition(alarmCondition); + alarmRule.setCondition(alarmCreateCondition); DeviceProfileAlarm dpa = new DeviceProfileAlarm(); dpa.setId("highTemperatureAlarmID"); dpa.setAlarmType("highTemperatureAlarm"); dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); - AlarmConditionFilter lowTempFilter = new AlarmConditionFilter(); - lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); - lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); - NumericFilterPredicate lowTemperaturePredicate = new NumericFilterPredicate(); - lowTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); - lowTemperaturePredicate.setValue(new FilterPredicateValue<>(10.0)); - lowTempFilter.setPredicate(lowTemperaturePredicate); AlarmRule clearRule = new AlarmRule(); - AlarmCondition clearCondition = new AlarmCondition(); - clearCondition.setCondition(Collections.singletonList(lowTempFilter)); + AlarmCondition clearCondition = getNumericAlarmCondition(TIME_SERIES, "temperature", LESS, 10.0); clearRule.setCondition(clearCondition); dpa.setClearRule(clearRule); @@ -261,25 +256,11 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { DeviceProfile deviceProfile = new DeviceProfile(); DeviceProfileData deviceProfileData = new DeviceProfileData(); - AlarmConditionFilter tempFilter = new AlarmConditionFilter(); - tempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); - tempFilter.setValueType(EntityKeyValueType.NUMERIC); - NumericFilterPredicate temperaturePredicate = new NumericFilterPredicate(); - temperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); - temperaturePredicate.setValue(new FilterPredicateValue<>(30.0)); - tempFilter.setPredicate(temperaturePredicate); - AlarmCondition alarmTempCondition = new AlarmCondition(); - alarmTempCondition.setCondition(Collections.singletonList(tempFilter)); + AlarmCondition alarmTempCondition = getNumericAlarmCondition(TIME_SERIES, "temperature", GREATER, 30.0); AlarmRule alarmTempRule = new AlarmRule(); alarmTempRule.setCondition(alarmTempCondition); - AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); - highTempFilter.setValueType(EntityKeyValueType.NUMERIC); - NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); - highTemperaturePredicate.setValue(new FilterPredicateValue<>(50.0)); - highTempFilter.setPredicate(highTemperaturePredicate); + AlarmConditionFilter highTempFilter = getAlarmConditionFilter(TIME_SERIES, "temperature", GREATER, 50.0); AlarmCondition alarmHighTempCondition = new AlarmCondition(); alarmHighTempCondition.setCondition(Collections.singletonList(highTempFilter)); AlarmRule alarmHighTempRule = new AlarmRule(); @@ -401,10 +382,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { alarmEnabledFilter.setPredicate(alarmEnabledPredicate); AlarmConditionFilter temperatureFilter = new AlarmConditionFilter(); - temperatureFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + temperatureFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); temperatureFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate temperaturePredicate = new NumericFilterPredicate(); - temperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + temperaturePredicate.setOperation(GREATER); temperaturePredicate.setValue(new FilterPredicateValue<>(20.0, null, null)); temperatureFilter.setPredicate(temperaturePredicate); @@ -494,10 +475,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { alarmEnabledFilter.setPredicate(alarmEnabledPredicate); AlarmConditionFilter temperatureFilter = new AlarmConditionFilter(); - temperatureFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + temperatureFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); temperatureFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate temperaturePredicate = new NumericFilterPredicate(); - temperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + temperaturePredicate.setOperation(GREATER); temperaturePredicate.setValue(new FilterPredicateValue<>(20.0, null, null)); temperatureFilter.setPredicate(temperaturePredicate); @@ -576,10 +557,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Collections.singletonList(entry)); AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); highTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setOperation(GREATER); highTemperaturePredicate.setValue(new FilterPredicateValue<>( 0.0, null, @@ -670,10 +651,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Arrays.asList(entry, alarmDelayAttributeKvEntry)); AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); highTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setOperation(GREATER); highTemperaturePredicate.setValue(new FilterPredicateValue<>( 0.0, null, @@ -805,10 +786,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Optional.empty()); AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); highTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setOperation(GREATER); highTemperaturePredicate.setValue(new FilterPredicateValue<>( 0.0, null, @@ -937,10 +918,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Arrays.asList(entry, alarmDelayAttributeKvEntry)); AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); highTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setOperation(GREATER); highTemperaturePredicate.setValue(new FilterPredicateValue<>( 0.0, null, @@ -1065,10 +1046,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Optional.empty()); AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); highTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setOperation(GREATER); highTemperaturePredicate.setValue(new FilterPredicateValue<>( 0.0, null, @@ -1182,10 +1163,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Collections.singletonList(entry)); AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); highTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setOperation(GREATER); highTemperaturePredicate.setValue(new FilterPredicateValue<>( 0.0, null, @@ -1299,10 +1280,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Collections.singletonList(entry)); AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); highTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setOperation(GREATER); highTemperaturePredicate.setValue(new FilterPredicateValue<>( 0.0, null, @@ -1395,10 +1376,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Collections.singletonList(entryActiveSchedule)); AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); highTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setOperation(GREATER); highTemperaturePredicate.setValue(new FilterPredicateValue<>( 0.0, null, @@ -1492,10 +1473,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Collections.singletonList(entryInactiveSchedule)); AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); - highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); highTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); - highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setOperation(GREATER); highTemperaturePredicate.setValue(new FilterPredicateValue<>( 0.0, null, @@ -1593,10 +1574,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Optional.of(entry)); AlarmConditionFilter lowTempFilter = new AlarmConditionFilter(); - lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + lowTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); - lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); + lowTempPredicate.setOperation(LESS); lowTempPredicate.setValue( new FilterPredicateValue<>( 20.0, @@ -1679,10 +1660,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Optional.of(entry)); AlarmConditionFilter lowTempFilter = new AlarmConditionFilter(); - lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + lowTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); - lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); + lowTempPredicate.setOperation(LESS); lowTempPredicate.setValue( new FilterPredicateValue<>( 32.0, @@ -1769,10 +1750,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Optional.of(entry)); AlarmConditionFilter lowTempFilter = new AlarmConditionFilter(); - lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + lowTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); - lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + lowTempPredicate.setOperation(GREATER); lowTempPredicate.setValue( new FilterPredicateValue<>( 0.0, @@ -1865,10 +1846,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { Futures.immediateFuture(Optional.of(entry)); AlarmConditionFilter lowTempFilter = new AlarmConditionFilter(); - lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + lowTempFilter.setKey(new AlarmConditionFilterKey(TIME_SERIES, "temperature")); lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); - lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + lowTempPredicate.setOperation(GREATER); lowTempPredicate.setValue( new FilterPredicateValue<>( 0.0, @@ -1942,6 +1923,10 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { } private void registerCreateAlarmMock(AlarmApiCallResult a, boolean created) { + registerCreateAlarmMock(a, created, false); + } + + private void registerCreateAlarmMock(AlarmApiCallResult a, boolean created, boolean cleared) { when(a).thenAnswer(invocationOnMock -> { AlarmInfo alarm = new AlarmInfo(new Alarm(new AlarmId(UUID.randomUUID()))); AlarmModificationRequest request = invocationOnMock.getArgument(0); @@ -1950,6 +1935,7 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { .successful(true) .created(created) .modified(true) + .cleared(cleared) .alarm(alarm) .build(); }); @@ -1981,6 +1967,100 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { } + @Test + public void testAlarmCreateWithAttrAndTsCondition() throws Exception { + init(); + + DeviceProfile deviceProfile = new DeviceProfile(); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + + AlarmConditionFilter filter = getAlarmConditionFilter(TIME_SERIES, "temperature", GREATER, 30.0); + AlarmConditionFilter filter2 = getAlarmConditionFilter(ATTRIBUTE, "battery", LESS, 10.0); + AlarmCondition alarmCondition = new AlarmCondition(); + alarmCondition.setCondition(List.of(filter, filter2)); + AlarmRule createRule = new AlarmRule(); + createRule.setCondition(alarmCondition); + + AlarmConditionFilter filter3 = getAlarmConditionFilter(TIME_SERIES, "temperature", LESS, 10.0); + AlarmConditionFilter filter4 = getAlarmConditionFilter(ATTRIBUTE, "battery", GREATER, 50.0); + AlarmCondition clearCondition = new AlarmCondition(); + clearCondition.setCondition(List.of(filter3, filter4)); + AlarmRule clearRule = new AlarmRule(); + clearRule.setCondition(clearCondition); + + DeviceProfileAlarm dpa = new DeviceProfileAlarm(); + dpa.setId("highTemperatureAlarmID"); + dpa.setAlarmType("highTemperatureAlarm"); + dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, createRule))); + dpa.setClearRule(clearRule); + + deviceProfileData.setAlarms(Collections.singletonList(dpa)); + deviceProfile.setProfileData(deviceProfileData); + + ListenableFuture> tsKvList = + Futures.immediateFuture(Collections.singletonList(getTsKvEntry("temperature", 35L))); + ListenableFuture> attrList = + Futures.immediateFuture(Collections.emptyList()); + + Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); + Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) + .thenReturn(tsKvList); + Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), any(), anySet())) + .thenReturn(attrList); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")).thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); + + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); + + // send attribute + ObjectNode data = JacksonUtil.newObjectNode(); + data.put("battery", 8); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.POST_ATTRIBUTES_REQUEST) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); + node.onMsg(ctx, msg); + verify(ctx).tellSuccess(msg); + verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); + verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); + } + + private TsKvEntry getTsKvEntry(String key, Long value) { + TsKvEntity tsKv = new TsKvEntity(); + tsKv.setKey(10); + tsKv.setLongValue(value); + tsKv.setStrKey(key); + tsKv.setTs(System.currentTimeMillis()); + return tsKv.toData(); + } + + private AlarmCondition getNumericAlarmCondition(AlarmConditionKeyType alarmConditionKeyType, String key, NumericOperation operation, Double value) { + AlarmConditionFilter filter = getAlarmConditionFilter(alarmConditionKeyType, key, operation, value); + AlarmCondition alarmCondition = new AlarmCondition(); + alarmCondition.setCondition(Collections.singletonList(filter)); + return alarmCondition; + } + + private AlarmConditionFilter getAlarmConditionFilter(AlarmConditionKeyType alarmConditionKeyType, String key, NumericOperation operation, Double value) { + AlarmConditionFilter filter = new AlarmConditionFilter(); + filter.setKey(new AlarmConditionFilterKey(alarmConditionKeyType, key)); + filter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); + highTemperaturePredicate.setOperation(operation); + highTemperaturePredicate.setValue(new FilterPredicateValue<>(value)); + filter.setPredicate(highTemperaturePredicate); + return filter; + } + @Override protected TbNode getTestNode() { return node; From aec9fc68f4439c9aad5e165b4338a37b070e1d68 Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 28 Mar 2025 19:19:06 +0200 Subject: [PATCH 035/335] fix_bug_lwm2m: add authorizer to lh server --- .../lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java | 2 +- .../transport/lwm2m/server/DefaultLwM2mTransportService.java | 1 + .../transport/lwm2m/server/client/LwM2mClientContextImpl.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index f518d0176f..783fe62c80 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -88,7 +88,7 @@ public class LwM2mCredentialsSecurityInfoValidator { } TbLwM2MSecurityInfo securityInfo = resultSecurityStore[0]; - if (securityInfo.getSecurityMode() == null) { + if (securityInfo != null && securityInfo.getSecurityMode() == null) { throw new LwM2MAuthException(); } return securityInfo; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 52859eab2f..795f40aa20 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -125,6 +125,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { /* Set securityStore with new registrationStore */ builder.setSecurityStore(securityStore); builder.setRegistrationStore(registrationStore); + builder.setAuthorizer(authorizer); // Create Californium Endpoints Provider: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index 882fe4fa04..6a4bc645d4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -141,7 +141,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { } oldSession = client.getSession(); TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(client.getEndpoint()); - if (securityInfo.getSecurityMode() != null) { + if (securityInfo != null && securityInfo.getSecurityMode() != null) { if (SecurityMode.X509.equals(securityInfo.getSecurityMode())) { securityStore.registerX509(registration.getEndpoint(), registration.getId()); } From 832af90bd904c9c1fa9b04a2ffb8db168bc838cf Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 31 Mar 2025 16:07:42 +0300 Subject: [PATCH 036/335] fixed TIMESERIES_UPDATED message handling --- .../server/common/adaptor/JsonConverter.java | 3 +- .../rule/engine/profile/DeviceState.java | 3 +- .../profile/TbDeviceProfileNodeTest.java | 68 +++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java index 2a208923d9..f1575f8a1c 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java @@ -590,7 +590,8 @@ public class JsonConverter { public static Map> convertToSortedTelemetry(JsonElement jsonElement, long systemTs) throws JsonSyntaxException { - return convertToTelemetry(jsonElement, systemTs, true); + JsonElement timeseriesElement = jsonElement.isJsonObject() ? jsonElement.getAsJsonObject().get("timeseries") : null; + return convertToTelemetry(timeseriesElement != null ? timeseriesElement : jsonElement, systemTs, true); } public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs, boolean sorted) throws diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java index 6cc44e9e8b..bbce5e5dfe 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java @@ -66,6 +66,7 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.ENTITY_UNASSIGNED import static org.thingsboard.server.common.data.msg.TbMsgType.INACTIVITY_EVENT; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; +import static org.thingsboard.server.common.data.msg.TbMsgType.TIMESERIES_UPDATED; @Slf4j class DeviceState { @@ -148,7 +149,7 @@ class DeviceState { latestValues = fetchLatestValues(ctx, deviceId); } boolean stateChanged = false; - if (msg.isTypeOf(POST_TELEMETRY_REQUEST)) { + if (msg.isTypeOf(POST_TELEMETRY_REQUEST) || msg.isTypeOf(TIMESERIES_UPDATED)) { stateChanged = processTelemetry(ctx, msg); } else if (msg.isTypeOf(POST_ATTRIBUTES_REQUEST)) { stateChanged = processAttributesUpdateRequest(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 7ecb1b2ad8..9c87f506b3 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -1981,6 +1981,74 @@ public class TbDeviceProfileNodeTest extends AbstractRuleNodeUpgradeTest { } + @Test + public void testAlarmCreateAfterTimeseriesUpdated() throws Exception { + init(); + + DeviceProfile deviceProfile = new DeviceProfile(); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + + AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); + highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); + highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setValue(new FilterPredicateValue<>(30.0)); + highTempFilter.setPredicate(highTemperaturePredicate); + AlarmCondition alarmCondition = new AlarmCondition(); + alarmCondition.setCondition(Collections.singletonList(highTempFilter)); + AlarmRule alarmRule = new AlarmRule(); + alarmRule.setCondition(alarmCondition); + DeviceProfileAlarm dpa = new DeviceProfileAlarm(); + dpa.setId("highTemperatureAlarmID"); + dpa.setAlarmType("highTemperatureAlarm"); + dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); + + AlarmConditionFilter lowTempFilter = new AlarmConditionFilter(); + lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate lowTemperaturePredicate = new NumericFilterPredicate(); + lowTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); + lowTemperaturePredicate.setValue(new FilterPredicateValue<>(10.0)); + lowTempFilter.setPredicate(lowTemperaturePredicate); + AlarmRule clearRule = new AlarmRule(); + AlarmCondition clearCondition = new AlarmCondition(); + clearCondition.setCondition(Collections.singletonList(lowTempFilter)); + clearRule.setCondition(clearCondition); + dpa.setClearRule(clearRule); + + deviceProfileData.setAlarms(Collections.singletonList(dpa)); + deviceProfile.setProfileData(deviceProfileData); + + Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); + Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) + .thenReturn(Futures.immediateFuture(Collections.emptyList())); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")).thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); + + TbMsg theMsg = TbMsg.newMsg() + .type(TbMsgType.ALARM) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .data(TbMsg.EMPTY_STRING) + .build(); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); + + ObjectNode data = JacksonUtil.newObjectNode(); + data.put("temperature", 42); + TbMsg msg = TbMsg.newMsg() + .type(TbMsgType.TIMESERIES_UPDATED) + .originator(deviceId) + .copyMetaData(TbMsgMetaData.EMPTY) + .dataType(TbMsgDataType.JSON) + .data(JacksonUtil.toString(data)) + .build(); + node.onMsg(ctx, msg); + verify(ctx).tellSuccess(msg); + verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); + verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); + } + @Override protected TbNode getTestNode() { return node; From 671b8dc24433433613efdbc8dde8c40f9ec9ebbb Mon Sep 17 00:00:00 2001 From: sarocs Date: Mon, 7 Apr 2025 13:29:30 -0500 Subject: [PATCH 037/335] Use create coap profile in tests --- .../server/controller/DeviceConnectivityControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java index 04d71b890f..27c551be32 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -176,7 +176,7 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { DeviceProfileData deviceProfileData2 = new DeviceProfileData(); deviceProfileData2.setConfiguration(new DefaultDeviceProfileConfiguration()); deviceProfileData2.setTransportConfiguration(new CoapDeviceProfileTransportConfiguration()); - coapProfile.setProfileData(deviceProfileData); + coapProfile.setProfileData(deviceProfileData2); coapProfile.setDefault(false); coapProfile.setDefaultRuleChainId(null); From 565fa1702800e6724c2a05c52428d5fe1faf084b Mon Sep 17 00:00:00 2001 From: omahurin Date: Wed, 9 Apr 2025 13:23:13 +0300 Subject: [PATCH 038/335] update dashboard title to include TBEL metrics --- .../{core_and_js_metrics.json => core_js_tbel_metrics.json} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docker/monitoring/grafana/provisioning/dashboards/{core_and_js_metrics.json => core_js_tbel_metrics.json} (99%) diff --git a/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json b/docker/monitoring/grafana/provisioning/dashboards/core_js_tbel_metrics.json similarity index 99% rename from docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json rename to docker/monitoring/grafana/provisioning/dashboards/core_js_tbel_metrics.json index 15ff06ab8c..b23171e6c3 100644 --- a/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json +++ b/docker/monitoring/grafana/provisioning/dashboards/core_js_tbel_metrics.json @@ -423,7 +423,7 @@ ] }, "timezone": "", - "title": "Core and JS Metrics", + "title": "Core, JS & TBEL Metrics", "uid": "lewbrlwjerwkj2", "version": 2 } \ No newline at end of file From 5f2bb68bc829dd6276f66fdfaee93ef5cbaf64cc Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 9 Apr 2025 18:48:31 +0300 Subject: [PATCH 039/335] lwm2m: add params FOTA to object 19 --- .../DefaultLwM2mDownlinkMsgHandler.java | 4 +- .../ota/DefaultLwM2MOtaUpdateService.java | 81 +++++++++++++++++-- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 12 ++- .../common/transport/util/JsonUtils.java | 16 +++- 4 files changed, 99 insertions(+), 14 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 12bc176fe3..ed685dfc6c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -113,6 +113,7 @@ import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.SHOR import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.STEP; import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; +import static org.thingsboard.server.common.transport.util.JsonUtils.isBase64; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.createModelsDefault; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fromVersionedIdToObjectId; @@ -399,7 +400,8 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im try { Object valueForMultiResource = request.getValue(); if (resultIds.isResourceInstance()) { - String resourceInstance = "{" + resultIds.getResourceInstanceId() + "=" + request.getValue() + "}"; + String valueStr = isBase64(request.getValue().toString()) ? "\"" + request.getValue() + "\"" : request.getValue().toString(); + String resourceInstance = "{" + resultIds.getResourceInstanceId() + "=" + valueStr + "}"; valueForMultiResource = JsonParser.parseString(resourceInstance); } Map value = convertMultiResourceValuesFromRpcBody(valueForMultiResource, resourceModelWrite.type, request.getObjectId()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 2f2407e451..8ed9c69755 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -15,16 +15,21 @@ */ package org.thingsboard.server.transport.lwm2m.server.ota; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.hash.Hashing; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.model.ObjectModel; +import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.request.ContentFormat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.profile.lwm2m.OtherConfiguration; @@ -37,15 +42,12 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; +import org.thingsboard.server.transport.lwm2m.server.LwM2mVersionedModelProvider; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; -import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.*; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareDeliveryMethod; import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult; @@ -56,6 +58,7 @@ import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MClientSwO import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult; import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState; +import org.thingsboard.server.transport.lwm2m.server.rpc.RpcCreateRequest; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MClientOtaInfoStore; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; @@ -64,6 +67,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.Base64; import java.util.concurrent.ConcurrentHashMap; import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; @@ -104,6 +108,14 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String FW_RESULT_ID = "/5/0/5"; public static final String FW_NAME_ID = "/5/0/6"; public static final String FW_VER_ID = "/5/0/7"; + public static final String FW_INFO_19_INSTANCE_ID = "/19/65534"; + public static final String SW_INFO_19_INSTANCE_ID = "/19/65535"; + public static final String OTA_INFO_19_TITLE = "title"; + public static final String OTA_INFO_19_VERSION = "version"; + public static final String OTA_INFO_19_FILE_CHECKSUM256 = "fileChecksumSHA256"; + public static final String OTA_INFO_19_FILE_SIZE = "fileSize"; + public static final String OTA_INFO_19_FILE_NAME = "fileName"; + /** * Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8 * Revision:BC68JAR01A10 @@ -134,6 +146,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl private final LwM2MTelemetryLogService logService; private final LwM2mTransportServerHelper helper; private final TbLwM2MClientOtaInfoStore otaInfoStore; + private final LwM2mVersionedModelProvider modelProvider; @Autowired @Lazy @@ -510,6 +523,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl } else { strategy = info.getDeliveryMethod() == FirmwareDeliveryMethod.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; } + if (clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOta()){ + sendInfoFwToObject19ForOta(client, convertObjectIdToVersionedId(FW_INFO_19_INSTANCE_ID, client), response, otaPackageId); + } switch (strategy) { case OBJ_5_BINARY: startUpdateUsingBinary(client, convertObjectIdToVersionedId(FW_PACKAGE_5_ID, client), otaPackageId); @@ -641,6 +657,61 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl helper.sendParametersOnThingsboardTelemetry(result, client.getSession(), client.getKeyTsLatestMap()); } + /** + * send to client: versionedId="/19/65534/0/0, value = FwOtaInfo in bas64 -> format json: + * {"title":"BC68JAR01", + * "version":"A10", + * "fileChecksumSHA256":"f2a08d4963e981c78f2a99f62d8439af4437a72ea7267a8c01d013c072c01ded", + * "fileSize":59832} + * @param client + * @param targetIdVer + * @param response + * @param otaPackageId + */ + private void sendInfoFwToObject19ForOta(LwM2mClient client, String targetIdVer, TransportProtos.GetOtaPackageResponseMsg response, UUID otaPackageId) { + log.trace("[{}] Current info fw toObject19ForOta", client.getEndpoint()); + ObjectModel objectModel = client.getObjectModel(targetIdVer, modelProvider); + if (objectModel != null) { + try { + if (client.getRegistration().getSupportedObject().get(19) != null) { + ObjectNode objectNodeInfoFw = JacksonUtil.newObjectNode(); + byte[] firmwareChunk = otaPackageDataCache.get(otaPackageId.toString(), 0, 0); + String fileChecksumSHA256 = Hashing.sha256().hashBytes(firmwareChunk).toString(); + objectNodeInfoFw.put(OTA_INFO_19_TITLE, response.getTitle()); + objectNodeInfoFw.put(OTA_INFO_19_VERSION, response.getVersion()); + objectNodeInfoFw.put(OTA_INFO_19_FILE_CHECKSUM256, fileChecksumSHA256); + objectNodeInfoFw.put(OTA_INFO_19_FILE_SIZE, firmwareChunk.length); + objectNodeInfoFw.put(OTA_INFO_19_FILE_NAME, response.getFileName()); + String objectNodeInfoFwStr = JacksonUtil.toString(objectNodeInfoFw); + assert objectNodeInfoFwStr != null; + String objectNodeInfoFwBase64 = Base64.getEncoder().encodeToString(objectNodeInfoFwStr.getBytes()); + + + LwM2mPath pathFwInstance = new LwM2mPath(FW_INFO_19_INSTANCE_ID); + if (client.getRegistration().getAvailableInstances().contains(pathFwInstance)) { + String versionId = targetIdVer + "/0/0"; + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionId).value(objectNodeInfoFwBase64).timeout(clientContext.getRequestTimeout(client)).build(); + downlinkHandler.sendWriteReplaceRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionId)); + } else { + String valueResourcesStr = "{\"" + 0 + "\":{\"0\":\"" + objectNodeInfoFwBase64 + "\"}}"; + String valueStr = "{\"id\":\"" + targetIdVer + "\",\"value\":" + valueResourcesStr + "}"; + RpcCreateRequest requestBody = JacksonUtil.fromString(valueStr, RpcCreateRequest.class); + assert requestBody != null; + TbLwM2MCreateRequest.TbLwM2MCreateRequestBuilder builder = TbLwM2MCreateRequest.builder().versionedId(targetIdVer); + builder.value(requestBody.getValue()).nodes(requestBody.getNodes()).timeout(clientContext.getRequestTimeout(client)); + downlinkHandler.sendCreateRequest(client, builder.build(), new TbLwM2MCreateResponseCallback(uplinkHandler, logService, client, targetIdVer)); + } + } else { + String errorMsg = "Failed to send Info Fw to object 19. The client does not have object 19."; + log.trace("[{}] {}", client.getEndpoint(), errorMsg); + logService.log(client, errorMsg); + } + } catch (Exception e){ + log.error("", e); + } + } + } + private static Optional toOtaPackageUpdateStatus(FirmwareUpdateResult fwUpdateResult) { switch (fwUpdateResult) { case INITIAL: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java index b5ec94d48b..d1a8052610 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -74,18 +74,16 @@ public abstract class LwM2MClientOtaInfo { } else { if (targetPackageId.equals(currentPackageId)) { return false; - } else if (StringUtils.isNotEmpty(targetTag) && targetTag.equals(currentPackageId)) { - return false; - } else if (StringUtils.isNotEmpty(currentVersion3)) { - if (StringUtils.isNotEmpty(targetTag) && currentVersion3.contains(targetTag)) { + } else{ + if (targetTag.equals(currentPackageId)) { return false; + } else if (StringUtils.isNotEmpty(currentVersion3)) { + return !(currentVersion3.contains(targetTag) || targetTag.contains(currentVersion3)); } - return !currentVersion3.contains(targetPackageId); - } else { - return true; } } } + return true; } @JsonIgnore diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java index 28a904edb9..a79d897daf 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java @@ -23,9 +23,13 @@ import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; public class JsonUtils { + private static final Pattern BASE64_PATTERN = + Pattern.compile("^[A-Za-z0-9+/]+={0,2}$"); + public static JsonObject getJsonObject(List tsKv) { JsonObject json = new JsonObject(); for (KeyValueProto kv : tsKv) { @@ -56,7 +60,14 @@ public class JsonUtils { } else if (value instanceof Long) { return new JsonPrimitive((Long) value); } else if (value instanceof String) { - return JsonParser.parseString((String) value); + try { + return JsonParser.parseString((String) value); + } catch (Exception e) { + if (isBase64(value.toString())) { + value = "\"" + value + "\""; + } + return JsonParser.parseString((String) value); + } } else if (value instanceof Boolean) { return new JsonPrimitive((Boolean) value); } else if (value instanceof Double) { @@ -77,4 +88,7 @@ public class JsonUtils { return jsonObject; } + public static boolean isBase64(String value) { + return value.length() % 4 == 0 && BASE64_PATTERN.matcher(value).matches(); + } } From 381eace2901f59642dd3d17e284a0ab0741c0242 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 10 Apr 2025 12:24:43 +0300 Subject: [PATCH 040/335] lwm2m: fix bug for test testSoftwareUpdateByObject9 and testFirmwareUpdateByObject5_Ok --- .../server/ota/DefaultLwM2MOtaUpdateService.java | 3 ++- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 8ed9c69755..34b5e7c09e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -523,7 +523,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl } else { strategy = info.getDeliveryMethod() == FirmwareDeliveryMethod.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; } - if (clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOta()){ + Boolean useObject19ForOta = clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOta(); + if (useObject19ForOta != null && useObject19ForOta){ sendInfoFwToObject19ForOta(client, convertObjectIdToVersionedId(FW_INFO_19_INSTANCE_ID, client), response, otaPackageId); } switch (strategy) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java index d1a8052610..ee1ca92066 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -74,16 +74,18 @@ public abstract class LwM2MClientOtaInfo { } else { if (targetPackageId.equals(currentPackageId)) { return false; - } else{ - if (targetTag.equals(currentPackageId)) { + } else if (StringUtils.isNotEmpty(targetTag) && targetTag.equals(currentPackageId)) { + return false; + } else if (StringUtils.isNotEmpty(currentVersion3)) { + if (StringUtils.isNotEmpty(targetTag) && (currentVersion3.contains(targetTag) || targetTag.contains(currentVersion3))) { return false; - } else if (StringUtils.isNotEmpty(currentVersion3)) { - return !(currentVersion3.contains(targetTag) || targetTag.contains(currentVersion3)); } + return !currentVersion3.contains(targetPackageId); + } else { + return true; } } } - return true; } @JsonIgnore From c16e99998d576cb81fa4b34bd7a6090249b371de Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 10 Apr 2025 17:21:30 +0300 Subject: [PATCH 041/335] lwm2m: refactoring failed to update profile... --- .../lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index 0f428ad9af..aa263fbb0d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -409,7 +409,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl this.onDeviceProfileUpdate(clients, oldProfile, deviceProfile); } } catch (Exception e) { - log.warn("[{}] failed to update profile: {}", deviceProfile.getId(), deviceProfile); + log.warn("[{}] failed to update profile: {} [{}]", deviceProfile.getId(), e.getMessage(), deviceProfile); } } From f722618aa0fd67db1a1f08ec7323550c0b67a43d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 10 Apr 2025 17:30:27 +0300 Subject: [PATCH 042/335] lwm2m: refactoring failed to update profile... --- .../lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index aa263fbb0d..9976783179 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -424,7 +424,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl this.onDeviceUpdate(client, device, newDeviceProfileOpt); } } catch (Exception e) { - log.warn("[{}] failed to update device: {}", device.getId(), device); + log.warn("[{}] failed to update device: {} [{}]", device.getId(), e.getMessage(), device); } } From 3bd2afad8337cde345072ced0e6835267c3335c5 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 11 Apr 2025 09:27:15 +0300 Subject: [PATCH 043/335] Use consumer-properties-per-topic-inline to configure custom topics --- .../src/main/resources/thingsboard.yml | 5 +++ .../server/common/data/TbProperty.java | 8 +++-- .../server/queue/kafka/TbKafkaSettings.java | 36 +++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 716797c2d2..5d5962554d 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1628,6 +1628,11 @@ queue: - key: max.poll.records # Max poll records for edqs.state topic value: "${TB_QUEUE_KAFKA_EDQS_STATE_MAX_POLL_RECORDS:512}" + # If you override any default Kafka topic name using environment variables, you must also specify the related consumer properties + # for the new topic in `consumer-properties-per-topic-inline`. Otherwise, the topic will not inherit its expected configuration (e.g., max.poll.records, timeouts, etc). + # Format: "topic1:key1=value1,key2=value2;topic2:key=value" + # Example: "tb_core_modified.notifications:max.poll.records=10;tb_edge_modified:max.poll.records=10,enable.auto.commit=true" + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section, you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbProperty.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbProperty.java index 98fd521ea3..a72bd6d085 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbProperty.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbProperty.java @@ -15,14 +15,16 @@ */ package org.thingsboard.server.common.data; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; -/** - * Created by ashvayka on 25.09.18. - */ @Data +@NoArgsConstructor +@AllArgsConstructor public class TbProperty { private String key; private String value; + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java index 82b5af179f..e0682f6364 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.kafka; +import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import lombok.Getter; import lombok.Setter; @@ -36,7 +37,9 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.TbProperty; import org.thingsboard.server.queue.util.PropertyUtils; +import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; @@ -138,6 +141,9 @@ public class TbKafkaSettings { @Value("${queue.kafka.other-inline:}") private String otherInline; + @Value("${queue.kafka.consumer-properties-per-topic-inline:}") + private String consumerPropertiesPerTopicInline; + @Deprecated @Setter private List other; @@ -147,6 +153,14 @@ public class TbKafkaSettings { private volatile AdminClient adminClient; + @PostConstruct + public void initInlineTopicProperties() { + Map> inlineProps = parseTopicPropertyList(consumerPropertiesPerTopicInline); + if (!inlineProps.isEmpty()) { + consumerPropertiesPerTopic.putAll(inlineProps); + } + } + public Properties toConsumerProps(String topic) { Properties props = toProps(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers); @@ -245,6 +259,28 @@ public class TbKafkaSettings { return props; } + private Map> parseTopicPropertyList(String inlineProperties) { + Map> result = new HashMap<>(); + Map rawTopicToPropertyString = PropertyUtils.getProps(inlineProperties); + + for (Map.Entry entry : rawTopicToPropertyString.entrySet()) { + String topic = entry.getKey().trim(); + String propertiesStr = entry.getValue(); + + List tbProperties = Arrays.stream(propertiesStr.split(",")) + .map(kv -> kv.split("=", 2)) + .filter(kvArr -> kvArr.length == 2) + .map(kvArr -> new TbProperty(kvArr[0].trim(), kvArr[1].trim())) + .toList(); + + if (!tbProperties.isEmpty()) { + result.put(topic, tbProperties); + } + } + + return result; + } + @PreDestroy private void destroy() { if (adminClient != null) { From eadb946d9e7615ccc34e0b7a82a25de21e43a066 Mon Sep 17 00:00:00 2001 From: sarocs Date: Fri, 11 Apr 2025 19:18:15 -0500 Subject: [PATCH 044/335] Renamed profile data variables --- .../DeviceConnectivityControllerTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java index 27c551be32..773f9f33f4 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -158,12 +158,12 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { mqttProfile.setName("Mqtt device profile"); mqttProfile.setType(DeviceProfileType.DEFAULT); mqttProfile.setTransportType(DeviceTransportType.MQTT); - DeviceProfileData deviceProfileData = new DeviceProfileData(); - deviceProfileData.setConfiguration(new DefaultDeviceProfileConfiguration()); + DeviceProfileData mqttProfileData = new DeviceProfileData(); + mqttProfileData.setConfiguration(new DefaultDeviceProfileConfiguration()); MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration(); transportConfiguration.setDeviceTelemetryTopic(DEVICE_TELEMETRY_TOPIC); - deviceProfileData.setTransportConfiguration(transportConfiguration); - mqttProfile.setProfileData(deviceProfileData); + mqttProfileData.setTransportConfiguration(transportConfiguration); + mqttProfile.setProfileData(mqttProfileData); mqttProfile.setDefault(false); mqttProfile.setDefaultRuleChainId(null); @@ -173,10 +173,10 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { coapProfile.setName("Coap device profile"); coapProfile.setType(DeviceProfileType.DEFAULT); coapProfile.setTransportType(DeviceTransportType.COAP); - DeviceProfileData deviceProfileData2 = new DeviceProfileData(); - deviceProfileData2.setConfiguration(new DefaultDeviceProfileConfiguration()); - deviceProfileData2.setTransportConfiguration(new CoapDeviceProfileTransportConfiguration()); - coapProfile.setProfileData(deviceProfileData2); + DeviceProfileData coapProfileData = new DeviceProfileData(); + coapProfileData.setConfiguration(new DefaultDeviceProfileConfiguration()); + coapProfileData.setTransportConfiguration(new CoapDeviceProfileTransportConfiguration()); + coapProfile.setProfileData(coapProfileData); coapProfile.setDefault(false); coapProfile.setDefaultRuleChainId(null); From ed25c796558bcbe8b831a934bfd45ef9ce2b3396 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 12 Apr 2025 14:39:29 +0300 Subject: [PATCH 045/335] lwm2m: add test FW with objectId = 19 --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 11 +++ .../client/LwM2mBinaryAppDataContainer.java | 4 +- .../ota/AbstractOtaLwM2MIntegrationTest.java | 90 ++++++++++++++++++- .../ota/sql/Ota5LwM2MIntegrationTest.java | 59 ++++++++++++ .../ota/DefaultLwM2MOtaUpdateService.java | 10 ++- 5 files changed, 166 insertions(+), 8 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 9bfb067bbd..0950d66024 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -102,6 +102,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClient import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_UPDATE_SUCCESS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; +import static org.thingsboard.server.transport.lwm2m.ota.AbstractOtaLwM2MIntegrationTest.CLIENT_LWM2M_SETTINGS_19; @TestPropertySource(properties = { "transport.lwm2m.enabled=true", @@ -382,6 +383,16 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte transportConfiguration.setBootstrap(bootstrapServerCredentials); return transportConfiguration; } + protected Lwm2mDeviceProfileTransportConfiguration getTransportConfiguration19(String observeAttr, List bootstrapServerCredentials) { + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration(); + TelemetryMappingConfiguration observeAttrConfiguration = JacksonUtil.fromString(observeAttr, TelemetryMappingConfiguration.class); + OtherConfiguration clientLwM2mSettings = JacksonUtil.fromString(CLIENT_LWM2M_SETTINGS_19, OtherConfiguration.class); + transportConfiguration.setBootstrapServerUpdateEnable(true); + transportConfiguration.setObserveAttr(observeAttrConfiguration); + transportConfiguration.setClientLwM2mSettings(clientLwM2mSettings); + transportConfiguration.setBootstrap(bootstrapServerCredentials); + return transportConfiguration; + } protected List getBootstrapServerCredentialsNoSec(LwM2MProfileBootstrapConfigType bootstrapConfigType) { List bootstrap = new ArrayList<>(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java index 41a2259790..4f12c6d819 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java @@ -84,8 +84,8 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements try { if (id != null) this.setId(id); executorService.scheduleWithFixedDelay(() -> { - fireResourceChange(0); - fireResourceChange(2); +// fireResourceChange(0); +// fireResourceChange(2); } , 1, 1, TimeUnit.SECONDS); // 1 sec // , 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java index 4d56e891cc..52b3946072 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java @@ -16,10 +16,14 @@ package org.thingsboard.server.transport.lwm2m.ota; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.binary.Hex; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.ResponseCode; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -31,25 +35,29 @@ import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.rest.client.utils.RestJsonConverter.toTimeseries; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_11; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.*; @Slf4j @DaoSqlTest public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { - private final String[] RESOURCES_OTA = new String[]{"3.xml", "5.xml", "9.xml"}; + private final String[] RESOURCES_OTA = new String[]{"3.xml", "5.xml", "9.xml", "19.xml"}; protected static final String CLIENT_ENDPOINT_WITHOUT_FW_INFO = "WithoutFirmwareInfoDevice"; protected static final String CLIENT_ENDPOINT_OTA5 = "Ota5_Device"; protected static final String CLIENT_ENDPOINT_OTA9 = "Ota9_Device"; protected List expectedStatuses; - protected final String OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA5 = " {\n" + @@ -78,6 +86,54 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg " \"attributeLwm2m\": {}\n" + " }"; + protected final String OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA5_19 = + + " {\n" + + " \"keyName\": {\n" + + " \"/5_1.2/0/3\": \"state\",\n" + + " \"/5_1.2/0/5\": \"updateResult\",\n" + + " \"/5_1.2/0/6\": \"pkgname\",\n" + + " \"/5_1.2/0/7\": \"pkgversion\",\n" + + " \"/5_1.2/0/9\": \"firmwareUpdateDeliveryMethod\",\n" + + " \"/19_1.1/0/0\": \"dataRead\",\n" + + " \"/19_1.1/65534/0\": \"dataFotaAttr\"\n" + + " },\n" + + " \"observe\": [\n" + + " \"/5_1.2/0/3\",\n" + + " \"/5_1.2/0/5\",\n" + + " \"/5_1.2/0/6\",\n" + + " \"/5_1.2/0/7\",\n" + + " \"/5_1.2/0/9\",\n" + + " \"/19_1.1/0/0\",\n" + + " \"/19_1.1/65534/0\"\n" + + " ],\n" + + " \"attribute\": [],\n" + + " \"telemetry\": [\n" + + " \"/5_1.2/0/3\",\n" + + " \"/5_1.2/0/5\",\n" + + " \"/5_1.2/0/6\",\n" + + " \"/5_1.2/0/7\",\n" + + " \"/5_1.2/0/9\",\n" + + " \"/19_1.1/0/0\",\n" + + " \"/19_1.1/65534/0\"\n" + + " ],\n" + + " \"attributeLwm2m\": {}\n" + + " }"; + + public static final String CLIENT_LWM2M_SETTINGS_19 = + " {\n" + + " \"useObject19ForOta\": true,\n" + + " \"edrxCycle\": null,\n" + + " \"powerMode\": \"DRX\",\n" + + " \"fwUpdateResource\": null,\n" + + " \"fwUpdateStrategy\": 1,\n" + + " \"psmActivityTimer\": null,\n" + + " \"swUpdateResource\": null,\n" + + " \"swUpdateStrategy\": 1,\n" + + " \"pagingTransmissionWindow\": null,\n" + + " \"clientOnlyObserveAfterConnect\": 1\n" + + " }"; + protected final String OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA9 = " {\n" + @@ -159,6 +215,13 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg log.warn("Fetched telemetry by API for deviceId {}, list size {}, tsKvEntries {}", deviceId, tsKvEntries.size(), tsKvEntries); return tsKvEntries; } + protected List getFwSwParamsObject19TelemetryFromAPI(UUID deviceId) throws Exception { + String type_state = "dataFotaAttr"; + final List tsKvEntries = toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?orderBy=ASC&keys=" + type_state + "&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() { + })); + log.warn("Fetched telemetry by API for deviceId {}, list size {}, tsKvEntries {}", deviceId, tsKvEntries.size(), tsKvEntries); + return tsKvEntries; + } protected boolean predicateForStatuses(List ts) { List statuses = ts.stream() @@ -169,4 +232,27 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg log.warn("{}", statuses); return statuses.containsAll(expectedStatuses); } + + protected void resultReadOtaParams_19(String resourceIdVer, OtaPackageInfo otaPackageInfo) throws Exception { + String actualResult = sendRPCById(resourceIdVer); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String valStr = rpcActualResult.get("value").asText(); + String start = "{ id=0 value="; + String valHexDec = valStr.substring(valStr.indexOf(start) + start.length(), (valStr.indexOf("}"))); + String valNode = new String(Hex.decodeHex((valHexDec).toCharArray())); + ObjectNode actualResultVal = JacksonUtil.fromString(valNode, ObjectNode.class); + assert actualResultVal != null; + assertEquals(otaPackageInfo.getTitle(), actualResultVal.get(OTA_INFO_19_TITLE).asText()); + assertEquals(otaPackageInfo.getVersion(), actualResultVal.get(OTA_INFO_19_VERSION).asText()); + assertEquals(otaPackageInfo.getChecksum(), actualResultVal.get(OTA_INFO_19_FILE_CHECKSUM256).asText()); + assertEquals(otaPackageInfo.getFileName(), actualResultVal.get(OTA_INFO_19_FILE_NAME).asText()); + assertEquals(Optional.of(otaPackageInfo.getDataSize()), Optional.of((long) actualResultVal.get(OTA_INFO_19_FILE_SIZE).asInt())); + } + + private String sendRPCById(String path) throws Exception { + String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"id\": \"" + path + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + lwM2MTestClient.getDeviceIdStr(), setRpcRequest, String.class, status().isOk()); + } + } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java index 587eba6aa3..c5d0720042 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java @@ -16,17 +16,25 @@ package org.thingsboard.server.transport.lwm2m.ota.sql; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.binary.Hex; +import jakarta.validation.constraints.AssertTrue; import lombok.extern.slf4j.Slf4j; +import org.assertj.core.api.Assertions; +import org.eclipse.leshan.core.ResponseCode; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.transport.lwm2m.ota.AbstractOtaLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import java.util.Arrays; import java.util.Collections; @@ -36,6 +44,9 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.rest.client.utils.RestJsonConverter.toTimeseries; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; @@ -44,7 +55,9 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INIT import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.QUEUED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.*; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_INSTANCE_ID; @Slf4j public class Ota5LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { @@ -107,4 +120,50 @@ public class Ota5LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { } + /** + * ObjectId = 19 + * { + * "title" : "BC68J01", + * "version" : "10", + * "fileChecksumSHA256" : "f2a08d4963e981c78f2a99f62d8439af4437a72ea7267a8c01d013c072c01ded", + * "fileSize" : 59832, + * "fileName" : "FW_BC68JAR01A10_TO_BC68JAR01A09_09.bin", + * "packageType" : "FIRMWARE" + * } + * to base64 + * /5/0/5 -> Update Result (Res); 5/0/3 -> State; + * => ((Res>=0 && Res<=9) && State=0) + * => Write to Package/Write to Package URI -> DOWNLOADING ((Res>=0 && Res<=9) && State=1) + * => Download Finished -> DOWNLOADED ((Res==0 || Res=8) && State=2) + * => Executable resource Update is triggered / Initiate Firmware Update -> UPDATING (Res=0 && State=3) + * => Update Successful [Res==1] + * => Start / Res=0 -> "IDLE" .... + * @throws Exception + */ + @Test + public void testFirmwareUpdateByObject5WithObject19_Ok() throws Exception { + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration19(OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA5_19, getBootstrapServerCredentialsNoSec(NONE)); + DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + this.CLIENT_ENDPOINT_OTA5, transportConfiguration); + LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA5)); + final Device device = createLwm2mDevice(deviceCredentials, this.CLIENT_ENDPOINT_OTA5, deviceProfile.getId()); + createNewClient(SECURITY_NO_SEC, null, false, this.CLIENT_ENDPOINT_OTA5, device.getId().getId().toString()); + awaitObserveReadAll(6, device.getId().getId().toString()); + + OtaPackageInfo otaPackageInfo = createFirmware("fw.v.1.5.0-update", deviceProfile.getId()); + device.setFirmwareId(otaPackageInfo.getId()); + final Device savedDevice = doPost("/api/device", device, Device.class); + + assertThat(savedDevice).as("saved device").isNotNull(); + assertThat(getDeviceFromAPI(device.getId().getId())).as("fetched device").isEqualTo(savedDevice); + + expectedStatuses = Arrays.asList(QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, UPDATING, UPDATED); + List ts = await("await on timeseries for FW") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> getFwSwStateTelemetryFromAPI(device.getId().getId(), "fw_state"), this::predicateForStatuses); + + String ver_Id_19 = lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(BINARY_APP_DATA_CONTAINER).version; + String resourceIdVer = "/" + BINARY_APP_DATA_CONTAINER + "_" + ver_Id_19 + "/" + FW_INSTANCE_ID + "/" + RESOURCE_ID_0; + resultReadOtaParams_19(resourceIdVer, otaPackageInfo); + log.warn("Object5: Got the ts: {}", ts); + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 34b5e7c09e..802d79dd68 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -108,12 +108,14 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String FW_RESULT_ID = "/5/0/5"; public static final String FW_NAME_ID = "/5/0/6"; public static final String FW_VER_ID = "/5/0/7"; - public static final String FW_INFO_19_INSTANCE_ID = "/19/65534"; - public static final String SW_INFO_19_INSTANCE_ID = "/19/65535"; + public static final int FW_INSTANCE_ID = 65534; + public static final String FW_INFO_19_INSTANCE_ID = "/19/" + FW_INSTANCE_ID; + public static final int SW_INSTANCE_ID = 65535; + public static final String SW_INFO_19_INSTANCE_ID = "/19/" + SW_INSTANCE_ID; public static final String OTA_INFO_19_TITLE = "title"; public static final String OTA_INFO_19_VERSION = "version"; - public static final String OTA_INFO_19_FILE_CHECKSUM256 = "fileChecksumSHA256"; - public static final String OTA_INFO_19_FILE_SIZE = "fileSize"; + public static final String OTA_INFO_19_FILE_CHECKSUM256 = "checksum"; + public static final String OTA_INFO_19_FILE_SIZE = "dataSize"; public static final String OTA_INFO_19_FILE_NAME = "fileName"; /** From cf69df895f9264a7c58fbd2a465bef3fc8bdbf1c Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 13 Apr 2025 14:51:11 +0300 Subject: [PATCH 046/335] lwm2m: refactoring test FW with objectId = 19 --- .../ota/AbstractOtaLwM2MIntegrationTest.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java index 52b3946072..b32ce1a42d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java @@ -95,8 +95,7 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg " \"/5_1.2/0/6\": \"pkgname\",\n" + " \"/5_1.2/0/7\": \"pkgversion\",\n" + " \"/5_1.2/0/9\": \"firmwareUpdateDeliveryMethod\",\n" + - " \"/19_1.1/0/0\": \"dataRead\",\n" + - " \"/19_1.1/65534/0\": \"dataFotaAttr\"\n" + + " \"/19_1.1/0/0\": \"dataRead\"\n" + " },\n" + " \"observe\": [\n" + " \"/5_1.2/0/3\",\n" + @@ -104,8 +103,7 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg " \"/5_1.2/0/6\",\n" + " \"/5_1.2/0/7\",\n" + " \"/5_1.2/0/9\",\n" + - " \"/19_1.1/0/0\",\n" + - " \"/19_1.1/65534/0\"\n" + + " \"/19_1.1/0/0\"\n" + " ],\n" + " \"attribute\": [],\n" + " \"telemetry\": [\n" + @@ -114,8 +112,7 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg " \"/5_1.2/0/6\",\n" + " \"/5_1.2/0/7\",\n" + " \"/5_1.2/0/9\",\n" + - " \"/19_1.1/0/0\",\n" + - " \"/19_1.1/65534/0\"\n" + + " \"/19_1.1/0/0\"\n" + " ],\n" + " \"attributeLwm2m\": {}\n" + " }"; @@ -215,13 +212,6 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg log.warn("Fetched telemetry by API for deviceId {}, list size {}, tsKvEntries {}", deviceId, tsKvEntries.size(), tsKvEntries); return tsKvEntries; } - protected List getFwSwParamsObject19TelemetryFromAPI(UUID deviceId) throws Exception { - String type_state = "dataFotaAttr"; - final List tsKvEntries = toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?orderBy=ASC&keys=" + type_state + "&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() { - })); - log.warn("Fetched telemetry by API for deviceId {}, list size {}, tsKvEntries {}", deviceId, tsKvEntries.size(), tsKvEntries); - return tsKvEntries; - } protected boolean predicateForStatuses(List ts) { List statuses = ts.stream() From 452699a4fe2fdcaebf821b1614a9e15a688712e2 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 13 Apr 2025 15:55:56 +0300 Subject: [PATCH 047/335] lwm2m: add test SW with objectId = 19 --- .../ota/AbstractOtaLwM2MIntegrationTest.java | 32 +++++++++- .../ota/sql/Ota5LwM2MIntegrationTest.java | 13 ++-- .../ota/sql/Ota9LwM2MIntegrationTest.java | 59 +++++++++++++++++-- .../ota/DefaultLwM2MOtaUpdateService.java | 8 ++- 4 files changed, 96 insertions(+), 16 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java index b32ce1a42d..eb87a9bc3b 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java @@ -56,6 +56,7 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg protected static final String CLIENT_ENDPOINT_WITHOUT_FW_INFO = "WithoutFirmwareInfoDevice"; protected static final String CLIENT_ENDPOINT_OTA5 = "Ota5_Device"; protected static final String CLIENT_ENDPOINT_OTA9 = "Ota9_Device"; + protected static final String CLIENT_ENDPOINT_OTA9_19 = "Ota9_Device_19"; protected List expectedStatuses; protected final String OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA5 = @@ -155,6 +156,33 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg " ],\n" + " \"attributeLwm2m\": {}\n" + " }"; + protected final String OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA9_19 = + + " {\n" + + " \"keyName\": {\n" + + " \"/9_1.1/0/0\": \"pkgname\",\n" + + " \"/9_1.1/0/1\": \"pkgversion\",\n" + + " \"/9_1.1/0/7\": \"updateState\",\n" + + " \"/9_1.1/0/9\": \"updateResult\",\n" + + " \"/19_1.1/0/0\": \"dataRead\"\n" + + " },\n" + + " \"observe\": [\n" + + " \"/9_1.1/0/0\",\n" + + " \"/9_1.1/0/1\",\n" + + " \"/9_1.1/0/7\",\n" + + " \"/9_1.1/0/9\",\n" + + " \"/19_1.1/0/0\"\n" + + " ],\n" + + " \"attribute\": [],\n" + + " \"telemetry\": [\n" + + " \"/9_1.1/0/0\",\n" + + " \"/9_1.1/0/1\",\n" + + " \"/9_1.1/0/7\",\n" + + " \"/9_1.1/0/9\",\n" + + " \"/19_1.1/0/0\"\n" + + " ],\n" + + " \"attributeLwm2m\": {}\n" + + " }"; public AbstractOtaLwM2MIntegrationTest() { setResources(this.RESOURCES_OTA); @@ -176,14 +204,14 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256"); } - protected OtaPackageInfo createSoftware(DeviceProfileId deviceProfileId) throws Exception { + protected OtaPackageInfo createSoftware(DeviceProfileId deviceProfileId, String version) throws Exception { String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; OtaPackageInfo swInfo = new OtaPackageInfo(); swInfo.setDeviceProfileId(deviceProfileId); swInfo.setType(SOFTWARE); swInfo.setTitle("My sw"); - swInfo.setVersion("v1.0"); + swInfo.setVersion(version); OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", swInfo, OtaPackageInfo.class); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java index c5d0720042..66e0319ab8 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java @@ -121,14 +121,13 @@ public class Ota5LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { /** - * ObjectId = 19 + * ObjectId = 19/65533/0 * { - * "title" : "BC68J01", - * "version" : "10", - * "fileChecksumSHA256" : "f2a08d4963e981c78f2a99f62d8439af4437a72ea7267a8c01d013c072c01ded", - * "fileSize" : 59832, - * "fileName" : "FW_BC68JAR01A10_TO_BC68JAR01A09_09.bin", - * "packageType" : "FIRMWARE" + * "title" : "My firmware", + * "version" : "fw.v.1.5.0-update", + * "checksum" : "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + * "fileSize" : 1, + * "fileName" : "filename.txt" * } * to base64 * /5/0/5 -> Update Result (Res); 5/0/3 -> State; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota9LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota9LwM2MIntegrationTest.java index 8bb367f66e..24cb905868 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota9LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota9LwM2MIntegrationTest.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -35,7 +36,11 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INIT import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.QUEUED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_INSTANCE_ID; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_INSTANCE_ID; @Slf4j public class Ota9LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { @@ -50,14 +55,15 @@ public class Ota9LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { * */ @Test public void testSoftwareUpdateByObject9() throws Exception { + String clientEndpoint = this.CLIENT_ENDPOINT_OTA9; Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA9, getBootstrapServerCredentialsNoSec(NONE)); - DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + this.CLIENT_ENDPOINT_OTA9, transportConfiguration); - LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA9)); - final Device device = createLwm2mDevice(deviceCredentials, this.CLIENT_ENDPOINT_OTA9, deviceProfile.getId()); - createNewClient(SECURITY_NO_SEC, null, false, this.CLIENT_ENDPOINT_OTA9, device.getId().getId().toString()); + DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + clientEndpoint, transportConfiguration); + LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); + final Device device = createLwm2mDevice(deviceCredentials, clientEndpoint, deviceProfile.getId()); + createNewClient(SECURITY_NO_SEC, null, false, clientEndpoint, device.getId().getId().toString()); awaitObserveReadAll(4, device.getId().getId().toString()); - device.setSoftwareId(createSoftware(deviceProfile.getId()).getId()); + device.setSoftwareId(createSoftware(deviceProfile.getId(), "v1.0").getId()); final Device savedDevice = doPost("/api/device", device, Device.class); //sync call assertThat(savedDevice).as("saved device").isNotNull(); @@ -70,4 +76,47 @@ public class Ota9LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { .until(() -> getFwSwStateTelemetryFromAPI(device.getId().getId(), "sw_state"), this::predicateForStatuses); log.warn("Object9: Got the ts: {}", ts); } + /** + * ObjectId = 19/65534/0 + * { + * "title" : "My sw", + * "version" : "v1.0.19", + * "checksum" : "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + * "fileSize" : 1, + * "fileName" : "filename.txt" + * } + * => Start -> INITIAL (State=0) -> DOWNLOAD STARTED; + * => PKG / URI Write -> DOWNLOAD STARTED (Res=1 (Downloading) && State=1) -> DOWNLOADED + * => PKG Written -> DOWNLOADED (Res=1 Initial && State=2) -> DELIVERED; + * => PKG integrity verified -> DELIVERED (Res=3 (Successfully Downloaded and package integrity verified) && State=3) -> INSTALLED; + * => Install -> INSTALLED (Res=2 SW successfully installed) && State=4) -> Start + * + * */ + @Test + public void testSoftwareUpdateByObject9WithObject19_Ok() throws Exception { + String clientEndpoint = this.CLIENT_ENDPOINT_OTA9_19; + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration19(OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA9_19, getBootstrapServerCredentialsNoSec(NONE)); + DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + clientEndpoint, transportConfiguration); + LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); + final Device device = createLwm2mDevice(deviceCredentials, clientEndpoint, deviceProfile.getId()); + createNewClient(SECURITY_NO_SEC, null, false, clientEndpoint, device.getId().getId().toString()); + awaitObserveReadAll(5, device.getId().getId().toString()); + OtaPackageInfo otaPackageInfo = createSoftware(deviceProfile.getId(), "v1.0.19"); + device.setSoftwareId(otaPackageInfo.getId()); + final Device savedDevice = doPost("/api/device", device, Device.class); //sync call + + assertThat(savedDevice).as("saved device").isNotNull(); + assertThat(getDeviceFromAPI(device.getId().getId())).as("fetched device").isEqualTo(savedDevice); + + expectedStatuses = List.of( + QUEUED, INITIATED, DOWNLOADING, DOWNLOADING, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATED); + List ts = await("await on timeseries") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> getFwSwStateTelemetryFromAPI(device.getId().getId(), "sw_state"), this::predicateForStatuses); + + String ver_Id_19 = lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(BINARY_APP_DATA_CONTAINER).version; + String resourceIdVer = "/" + BINARY_APP_DATA_CONTAINER + "_" + ver_Id_19 + "/" + SW_INSTANCE_ID + "/" + RESOURCE_ID_0; + resultReadOtaParams_19(resourceIdVer, otaPackageInfo); + log.warn("Object9: Got the ts: {}", ts); + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 802d79dd68..822d712ac5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -108,9 +108,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String FW_RESULT_ID = "/5/0/5"; public static final String FW_NAME_ID = "/5/0/6"; public static final String FW_VER_ID = "/5/0/7"; - public static final int FW_INSTANCE_ID = 65534; + public static final int FW_INSTANCE_ID = 65533; public static final String FW_INFO_19_INSTANCE_ID = "/19/" + FW_INSTANCE_ID; - public static final int SW_INSTANCE_ID = 65535; + public static final int SW_INSTANCE_ID = 65534; public static final String SW_INFO_19_INSTANCE_ID = "/19/" + SW_INSTANCE_ID; public static final String OTA_INFO_19_TITLE = "title"; public static final String OTA_INFO_19_VERSION = "version"; @@ -551,6 +551,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); LwM2MSoftwareUpdateStrategy strategy = info.getStrategy(); + Boolean useObject19ForOta = clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOta(); + if (useObject19ForOta != null && useObject19ForOta){ + sendInfoFwToObject19ForOta(client, convertObjectIdToVersionedId(SW_INFO_19_INSTANCE_ID, client), response, otaPackageId); + } switch (strategy) { case BINARY: startUpdateUsingBinary(client, convertObjectIdToVersionedId(SW_PACKAGE_ID, client), otaPackageId); From 2561ab6eb1b7f170a9f4842d8087f2fc5466c27c Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 14 Apr 2025 11:14:46 +0300 Subject: [PATCH 048/335] lwm2m: add fireResourceChange to LwM2mBinaryAppDataContainer --- .../transport/lwm2m/client/LwM2mBinaryAppDataContainer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java index 4f12c6d819..41a2259790 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java @@ -84,8 +84,8 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements try { if (id != null) this.setId(id); executorService.scheduleWithFixedDelay(() -> { -// fireResourceChange(0); -// fireResourceChange(2); + fireResourceChange(0); + fireResourceChange(2); } , 1, 1, TimeUnit.SECONDS); // 1 sec // , 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN From 984040dd4fdc76e89f322ad174818e5820ddfb68 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 14 Apr 2025 11:39:25 +0300 Subject: [PATCH 049/335] lwm2m: refactoring import --- .../ota/sql/Ota5LwM2MIntegrationTest.java | 10 ---------- .../ota/sql/Ota9LwM2MIntegrationTest.java | 1 - .../ota/DefaultLwM2MOtaUpdateService.java | 18 ++++++++++-------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java index 66e0319ab8..b2d95ec906 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java @@ -16,15 +16,9 @@ package org.thingsboard.server.transport.lwm2m.ota.sql; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.binary.Hex; -import jakarta.validation.constraints.AssertTrue; import lombok.extern.slf4j.Slf4j; -import org.assertj.core.api.Assertions; -import org.eclipse.leshan.core.ResponseCode; import org.junit.Assert; import org.junit.Test; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.OtaPackageInfo; @@ -34,7 +28,6 @@ import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.transport.lwm2m.ota.AbstractOtaLwM2MIntegrationTest; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import java.util.Arrays; import java.util.Collections; @@ -44,9 +37,6 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.rest.client.utils.RestJsonConverter.toTimeseries; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota9LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota9LwM2MIntegrationTest.java index 24cb905868..b2617dd7ba 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota9LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota9LwM2MIntegrationTest.java @@ -39,7 +39,6 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERI import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; -import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_INSTANCE_ID; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_INSTANCE_ID; @Slf4j diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 822d712ac5..92fb2e3853 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -47,7 +47,11 @@ import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesS import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; -import org.thingsboard.server.transport.lwm2m.server.downlink.*; +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareDeliveryMethod; import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult; @@ -118,10 +122,6 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String OTA_INFO_19_FILE_SIZE = "dataSize"; public static final String OTA_INFO_19_FILE_NAME = "fileName"; - /** - * Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8 - * Revision:BC68JAR01A10 - */ public static final String FW_3_VER_ID = "/3/0/3"; public static final String FW_DELIVERY_METHOD = "/5/0/9"; @@ -665,11 +665,13 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl } /** - * send to client: versionedId="/19/65534/0/0, value = FwOtaInfo in bas64 -> format json: + * send to client: versionedId="/19/65533/0/0, value = FwOtaInfo in bas64 -> format json: + * send to client: versionedId="/19/65534/0/0, value = SwOtaInfo in bas64 -> format json: * {"title":"BC68JAR01", * "version":"A10", - * "fileChecksumSHA256":"f2a08d4963e981c78f2a99f62d8439af4437a72ea7267a8c01d013c072c01ded", - * "fileSize":59832} + * "checksum":"f2a08d4963e981c78f2a99f62d8439af4437a72ea7267a8c01d013c072c01ded", + * "fileSize":59832. + * "fileName" : "BC68JAR01A10_TO_BC68JAR01A09_09.bin" } * @param client * @param targetIdVer * @param response From 94be11b45885c77645b5e205f8905a63627262a9 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 14 Apr 2025 11:52:54 +0300 Subject: [PATCH 050/335] lwm2m: refactoring import2 --- .../lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 92fb2e3853..2d9d13d830 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -52,6 +52,8 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCall import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCreateRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCreateResponseCallback; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareDeliveryMethod; import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult; From 757c88703470d72e17c9f7e73b9062bcc921eef8 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 15 Apr 2025 13:06:03 +0300 Subject: [PATCH 051/335] Version 4.1.0-SNAPSHOT --- application/pom.xml | 2 +- application/src/main/resources/thingsboard.yml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/edqs/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- edqs/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/edqs/pom.xml | 2 +- msa/js-executor/package.json | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/package.json | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/package.json | 2 +- ui-ngx/pom.xml | 2 +- 63 files changed, 64 insertions(+), 64 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 6ce77646f8..557179e918 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard application diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index abafd81e7c..2256946723 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -208,7 +208,7 @@ ui: # Help parameters help: # Base URL for UI help assets - base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.0}" + base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.1}" # Database telemetry parameters database: diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 71b7e2eb6e..8ce6cf3761 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 27a536dc28..5ee70e2902 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index adc77dce22..d92b87fbb0 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 3f020b289a..fe6ae3c64c 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index b1b55a9461..cb8e578760 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index 5895974d82..648f3d1086 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index f992486149..b3ec95983f 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index f7106e56fd..09181e796e 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 380621a5ad..83100e11ea 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 9a7a836ba5..6afae4e378 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index bcfb3dfc85..548b2e52c5 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index e28ebfbcf0..2e4b14e282 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 80a8c4664a..61bbddc6c3 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index fc15448316..d247399ce6 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 3a51b99f35..3908326c96 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index db8367180a..90d619c133 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 887dc014e8..314f79833b 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 4354d14a1c..c1e3f77c4a 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index f918cdd167..01d0476c04 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 34eeed396d..d84757981a 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 27b9bcbc9e..7180adc018 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index b971de293c..122696317e 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 423fc5dc97..2cd41064da 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index acb97d3e2e..6719dc628a 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 22beed80d9..53a45a8197 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 35baa9b13b..9dca643b5d 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard dao diff --git a/edqs/pom.xml b/edqs/pom.xml index f466d2c20b..702c606daa 100644 --- a/edqs/pom.xml +++ b/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard edqs diff --git a/monitoring/pom.xml b/monitoring/pom.xml index b7735aed4c..7a55f105a7 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index c3379cf37a..407e4d6079 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml index deceadd06f..b3f584297e 100644 --- a/msa/edqs/pom.xml +++ b/msa/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 350d6029f0..1c16eaa594 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "4.0.0", + "version": "4.1.0", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 9f89413b4b..be52d30d01 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 3de71adb36..909c4ad0ac 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index 98e820daaf..280541452c 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index b47aec986c..4a278f9dd8 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 1e9774cb42..f656f37a93 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 71c86d50a4..004080a542 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index e6b5611166..0d0d70e405 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 9088a9c996..596b2d3bbd 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index a61bc62921..aae7594b27 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 46ea6612f9..9f5a0fdc7f 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 0664a89d9a..118783f98d 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index c7e3ee5e11..088e9ab4a8 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index f2e4395766..b35a30f1b2 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index 3374112096..8df71fc347 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "4.0.0", + "version": "4.1.0", "description": "ThingsBoard Web UI Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index f6f06c994a..4eba1f5e2f 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index bf55ec3083..fb3f88fd9e 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard netty-mqtt - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 8ddd9c5346..3f8631a17b 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 09f7e34c2c..828b33e835 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index b3681c9d69..0f089553b2 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index d036695cd3..d943759257 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index df44ee16c5..9666fa8059 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 26e0543ab1..f9b5d9062f 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 3cf7d677bf..058e0e96ab 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 91fdd39755..03784a855c 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index cc929a0090..df92bde39d 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index da818e032b..d27ab91259 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 66026fa10b..2cfe62ba95 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 0a36ea1f90..e561ae65b5 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT transport diff --git a/ui-ngx/package.json b/ui-ngx/package.json index e927abe7b2..e1cc3a90af 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "4.0.0", + "version": "4.1.0", "scripts": { "ng": "ng", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index eefea60273..79342e2ea8 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 4.1.0-SNAPSHOT thingsboard org.thingsboard From cedc7d2e462a04ea24776693b2377532c2161a91 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 15 Apr 2025 13:14:25 +0300 Subject: [PATCH 052/335] Cleanup upgrade scripts --- .../main/data/upgrade/basic/schema_update.sql | 79 ------------------- .../DefaultDatabaseSchemaSettingsService.java | 2 +- 2 files changed, 1 insertion(+), 80 deletions(-) diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index e91fbb823c..016e786776 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -14,82 +14,3 @@ -- limitations under the License. -- --- UPDATE SAVE TIME SERIES NODES START - -UPDATE rule_node -SET configuration = ( - (configuration::jsonb - 'skipLatestPersistence') - || jsonb_build_object( - 'processingSettings', jsonb_build_object( - 'type', 'ADVANCED', - 'timeseries', jsonb_build_object('type', 'ON_EVERY_MESSAGE'), - 'latest', jsonb_build_object('type', 'SKIP'), - 'webSockets', jsonb_build_object('type', 'ON_EVERY_MESSAGE'), - 'calculatedFields', jsonb_build_object('type', 'ON_EVERY_MESSAGE') - ) - ) - )::text, - configuration_version = 1 -WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode' - AND configuration_version = 0 - AND configuration::jsonb ->> 'skipLatestPersistence' = 'true'; - -UPDATE rule_node -SET configuration = ( - (configuration::jsonb - 'skipLatestPersistence') - || jsonb_build_object( - 'processingSettings', jsonb_build_object( - 'type', 'ON_EVERY_MESSAGE' - ) - ) - )::text, - configuration_version = 1 -WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode' - AND configuration_version = 0 - AND (configuration::jsonb ->> 'skipLatestPersistence' != 'true' OR configuration::jsonb ->> 'skipLatestPersistence' IS NULL); - --- UPDATE SAVE TIME SERIES NODES END - --- UPDATE SAVE ATTRIBUTES NODES START - -UPDATE rule_node -SET configuration = ( - configuration::jsonb - || jsonb_build_object( - 'processingSettings', jsonb_build_object('type', 'ON_EVERY_MESSAGE') - ) - )::text, - configuration_version = 3 -WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode' - AND configuration_version = 2; - --- UPDATE SAVE ATTRIBUTES NODES END - -ALTER TABLE api_usage_state ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1; - --- UPDATE TENANT PROFILE CALCULATED FIELD LIMITS START - -UPDATE tenant_profile -SET profile_data = profile_data - || jsonb_build_object( - 'configuration', profile_data->'configuration' || jsonb_build_object( - 'maxCalculatedFieldsPerEntity', COALESCE(profile_data->'configuration'->>'maxCalculatedFieldsPerEntity', '5')::bigint, - 'maxArgumentsPerCF', COALESCE(profile_data->'configuration'->>'maxArgumentsPerCF', '10')::bigint, - 'maxDataPointsPerRollingArg', COALESCE(profile_data->'configuration'->>'maxDataPointsPerRollingArg', '1000')::bigint, - 'maxStateSizeInKBytes', COALESCE(profile_data->'configuration'->>'maxStateSizeInKBytes', '32')::bigint, - 'maxSingleValueArgumentSizeInKBytes', COALESCE(profile_data->'configuration'->>'maxSingleValueArgumentSizeInKBytes', '2')::bigint - ) - ) -WHERE profile_data->'configuration'->>'maxCalculatedFieldsPerEntity' IS NULL; - --- UPDATE TENANT PROFILE CALCULATED FIELD LIMITS END - --- UPDATE TENANT PROFILE DEBUG DURATION START - -UPDATE tenant_profile -SET profile_data = jsonb_set(profile_data, '{configuration,maxDebugModeDurationMinutes}', '15', true) -WHERE - profile_data->'configuration' ? 'maxDebugModeDurationMinutes' = false - OR (profile_data->'configuration'->>'maxDebugModeDurationMinutes')::int = 0; - --- UPDATE TENANT PROFILE DEBUG DURATION END diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java index a0012a6fb3..6575c4b766 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java @@ -32,7 +32,7 @@ public class DefaultDatabaseSchemaSettingsService implements DatabaseSchemaSetti // This list should include all versions which are compatible for the upgrade. // The compatibility cycle usually breaks when we have some scripts written in Java that may not work after new release. - private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("3.9.0", "3.9.1"); + private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.0.0"); private final ProjectInfo projectInfo; private final JdbcTemplate jdbcTemplate; From fea2d7e4a37343e517619a4c5cb1e67dc598ce32 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 15 Apr 2025 15:04:17 +0300 Subject: [PATCH 053/335] lwm2m: refactoring SW with unInstall (argument is 1) -> "ForUpdate" --- .../ota/DefaultLwM2MOtaUpdateService.java | 73 ++++++++++--------- .../ota/firmware/LwM2MClientFwOtaInfo.java | 9 ++- .../ota/software/LwM2MClientSwOtaInfo.java | 11 ++- .../lwm2m/utils/LwM2mValueConverterImpl.java | 10 ++- 4 files changed, 62 insertions(+), 41 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 2d9d13d830..cfa8feb89f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -114,15 +114,6 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String FW_RESULT_ID = "/5/0/5"; public static final String FW_NAME_ID = "/5/0/6"; public static final String FW_VER_ID = "/5/0/7"; - public static final int FW_INSTANCE_ID = 65533; - public static final String FW_INFO_19_INSTANCE_ID = "/19/" + FW_INSTANCE_ID; - public static final int SW_INSTANCE_ID = 65534; - public static final String SW_INFO_19_INSTANCE_ID = "/19/" + SW_INSTANCE_ID; - public static final String OTA_INFO_19_TITLE = "title"; - public static final String OTA_INFO_19_VERSION = "version"; - public static final String OTA_INFO_19_FILE_CHECKSUM256 = "checksum"; - public static final String OTA_INFO_19_FILE_SIZE = "dataSize"; - public static final String OTA_INFO_19_FILE_NAME = "fileName"; public static final String FW_3_VER_ID = "/3/0/3"; public static final String FW_DELIVERY_METHOD = "/5/0/9"; @@ -138,6 +129,16 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String SW_RESULT_ID = "/9/0/9"; public static final String SW_UN_INSTALL_ID = "/9/0/6"; + public static final int FW_INSTANCE_ID = 65533; + public static final String FW_INFO_19_INSTANCE_ID = "/19/" + FW_INSTANCE_ID; + public static final int SW_INSTANCE_ID = 65534; + public static final String SW_INFO_19_INSTANCE_ID = "/19/" + SW_INSTANCE_ID; + public static final String OTA_INFO_19_TITLE = "title"; + public static final String OTA_INFO_19_VERSION = "version"; + public static final String OTA_INFO_19_FILE_CHECKSUM256 = "checksum"; + public static final String OTA_INFO_19_FILE_SIZE = "dataSize"; + public static final String OTA_INFO_19_FILE_NAME = "fileName"; + private final Map fwStates = new ConcurrentHashMap<>(); private final Map swStates = new ConcurrentHashMap<>(); @@ -529,7 +530,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl } Boolean useObject19ForOta = clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOta(); if (useObject19ForOta != null && useObject19ForOta){ - sendInfoFwToObject19ForOta(client, convertObjectIdToVersionedId(FW_INFO_19_INSTANCE_ID, client), response, otaPackageId); + sendInfoToObject19ForOta(client, FW_INFO_19_INSTANCE_ID, response, otaPackageId); } switch (strategy) { case OBJ_5_BINARY: @@ -555,7 +556,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl LwM2MSoftwareUpdateStrategy strategy = info.getStrategy(); Boolean useObject19ForOta = clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOta(); if (useObject19ForOta != null && useObject19ForOta){ - sendInfoFwToObject19ForOta(client, convertObjectIdToVersionedId(SW_INFO_19_INSTANCE_ID, client), response, otaPackageId); + sendInfoToObject19ForOta(client, SW_INFO_19_INSTANCE_ID, response, otaPackageId); } switch (strategy) { case BINARY: @@ -591,21 +592,24 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl } private void executeFwUpdate(LwM2mClient client) { + log.trace("[{}] Execute SW [{}]", client.getEndpoint(), FW_EXECUTE_ID); String fwExecuteVerId = convertObjectIdToVersionedId(FW_EXECUTE_ID, client); TbLwM2MExecuteRequest request = TbLwM2MExecuteRequest.builder().versionedId(fwExecuteVerId).timeout(clientContext.getRequestTimeout(client)).build(); downlinkHandler.sendExecuteRequest(client, request, new TbLwM2MExecuteCallback(logService, client, fwExecuteVerId)); } private void executeSwInstall(LwM2mClient client) { + log.trace("[{}] Execute SW (install) [{}]", client.getEndpoint(), SW_INSTALL_ID); String swInstallVerId = convertObjectIdToVersionedId(SW_INSTALL_ID, client); TbLwM2MExecuteRequest request = TbLwM2MExecuteRequest.builder().versionedId(swInstallVerId).timeout(clientContext.getRequestTimeout(client)).build(); downlinkHandler.sendExecuteRequest(client, request, new TbLwM2MExecuteCallback(logService, client, swInstallVerId)); } private void executeSwUninstallForUpdate(LwM2mClient client) { - String swInInstallVerId = convertObjectIdToVersionedId(SW_UN_INSTALL_ID, client); - TbLwM2MExecuteRequest request = TbLwM2MExecuteRequest.builder().versionedId(swInInstallVerId).params("1").timeout(clientContext.getRequestTimeout(client)).build(); - downlinkHandler.sendExecuteRequest(client, request, new TbLwM2MExecuteCallback(logService, client, swInInstallVerId)); + log.trace("[{}] Execute SW (uninstall with params(\"1\") ) [{}]", client.getEndpoint(), SW_UN_INSTALL_ID); + String swUnInstallVerId = convertObjectIdToVersionedId(SW_UN_INSTALL_ID, client); + TbLwM2MExecuteRequest request = TbLwM2MExecuteRequest.builder().versionedId(swUnInstallVerId).params("1").timeout(clientContext.getRequestTimeout(client)).build(); + downlinkHandler.sendExecuteRequest(client, request, new TbLwM2MExecuteCallback(logService, client, swUnInstallVerId)); } private Optional getAttributeValue(List attrs, String keyName) { @@ -675,36 +679,37 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl * "fileSize":59832. * "fileName" : "BC68JAR01A10_TO_BC68JAR01A09_09.bin" } * @param client - * @param targetIdVer + * @param targetId * @param response * @param otaPackageId */ - private void sendInfoFwToObject19ForOta(LwM2mClient client, String targetIdVer, TransportProtos.GetOtaPackageResponseMsg response, UUID otaPackageId) { - log.trace("[{}] Current info fw toObject19ForOta", client.getEndpoint()); + private void sendInfoToObject19ForOta(LwM2mClient client, String targetId, TransportProtos.GetOtaPackageResponseMsg response, UUID otaPackageId) { + log.trace("[{}] Current info ota toObject19ForOta [{}]", client.getEndpoint(), targetId); + String targetIdVer = convertObjectIdToVersionedId(targetId, client); ObjectModel objectModel = client.getObjectModel(targetIdVer, modelProvider); if (objectModel != null) { try { if (client.getRegistration().getSupportedObject().get(19) != null) { - ObjectNode objectNodeInfoFw = JacksonUtil.newObjectNode(); + ObjectNode objectNodeInfoOta = JacksonUtil.newObjectNode(); byte[] firmwareChunk = otaPackageDataCache.get(otaPackageId.toString(), 0, 0); String fileChecksumSHA256 = Hashing.sha256().hashBytes(firmwareChunk).toString(); - objectNodeInfoFw.put(OTA_INFO_19_TITLE, response.getTitle()); - objectNodeInfoFw.put(OTA_INFO_19_VERSION, response.getVersion()); - objectNodeInfoFw.put(OTA_INFO_19_FILE_CHECKSUM256, fileChecksumSHA256); - objectNodeInfoFw.put(OTA_INFO_19_FILE_SIZE, firmwareChunk.length); - objectNodeInfoFw.put(OTA_INFO_19_FILE_NAME, response.getFileName()); - String objectNodeInfoFwStr = JacksonUtil.toString(objectNodeInfoFw); - assert objectNodeInfoFwStr != null; - String objectNodeInfoFwBase64 = Base64.getEncoder().encodeToString(objectNodeInfoFwStr.getBytes()); - - - LwM2mPath pathFwInstance = new LwM2mPath(FW_INFO_19_INSTANCE_ID); - if (client.getRegistration().getAvailableInstances().contains(pathFwInstance)) { + objectNodeInfoOta.put(OTA_INFO_19_TITLE, response.getTitle()); + objectNodeInfoOta.put(OTA_INFO_19_VERSION, response.getVersion()); + objectNodeInfoOta.put(OTA_INFO_19_FILE_CHECKSUM256, fileChecksumSHA256); + objectNodeInfoOta.put(OTA_INFO_19_FILE_SIZE, firmwareChunk.length); + objectNodeInfoOta.put(OTA_INFO_19_FILE_NAME, response.getFileName()); + String objectNodeInfoOtaStr = JacksonUtil.toString(objectNodeInfoOta); + assert objectNodeInfoOtaStr != null; + String objectNodeInfoOtaBase64 = Base64.getEncoder().encodeToString(objectNodeInfoOtaStr.getBytes()); + + + LwM2mPath pathOtaInstance = new LwM2mPath(targetId); + if (client.getRegistration().getAvailableInstances().contains(pathOtaInstance)) { String versionId = targetIdVer + "/0/0"; - TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionId).value(objectNodeInfoFwBase64).timeout(clientContext.getRequestTimeout(client)).build(); + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionId).value(objectNodeInfoOtaBase64).timeout(clientContext.getRequestTimeout(client)).build(); downlinkHandler.sendWriteReplaceRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionId)); } else { - String valueResourcesStr = "{\"" + 0 + "\":{\"0\":\"" + objectNodeInfoFwBase64 + "\"}}"; + String valueResourcesStr = "{\"" + 0 + "\":{\"0\":\"" + objectNodeInfoOtaBase64 + "\"}}"; String valueStr = "{\"id\":\"" + targetIdVer + "\",\"value\":" + valueResourcesStr + "}"; RpcCreateRequest requestBody = JacksonUtil.fromString(valueStr, RpcCreateRequest.class); assert requestBody != null; @@ -713,8 +718,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl downlinkHandler.sendCreateRequest(client, builder.build(), new TbLwM2MCreateResponseCallback(uplinkHandler, logService, client, targetIdVer)); } } else { - String errorMsg = "Failed to send Info Fw to object 19. The client does not have object 19."; - log.trace("[{}] {}", client.getEndpoint(), errorMsg); + String errorMsg = String.format("[%s], Failed to send Info Ota to objectInstance [%s]. The client does not have object 19.", client.getEndpoint(), targetId); + log.trace(errorMsg); logService.log(client, errorMsg); } } catch (Exception e){ diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/LwM2MClientFwOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/LwM2MClientFwOtaInfo.java index 6f6aae9c45..0ef082e740 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/LwM2MClientFwOtaInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/LwM2MClientFwOtaInfo.java @@ -23,6 +23,9 @@ import lombok.ToString; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MClientOtaInfo; +import static org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult.UPDATE_SUCCESSFULLY; + + @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @@ -43,6 +46,11 @@ public class LwM2MClientFwOtaInfo extends LwM2MClientOtaInfo UPDATE_SUCCESSFULLY.getCode()) { + failedPackageId = getPackageId(targetName, targetVersion); + } + switch (result) { case INITIAL: break; @@ -50,7 +58,6 @@ public class LwM2MClientFwOtaInfo extends LwM2MClientOtaInfo= NOT_ENOUGH_STORAGE.getCode()) { + failedPackageId = getPackageId(targetName, targetVersion); + } switch (result) { case INITIAL: break; - //TODO: implement + case SUCCESSFULLY_INSTALLED: + retryAttempts = 0; + break; default: - failedPackageId = getPackageId(targetName, targetVersion); break; } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java index 480f4ed778..91d55305da 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java @@ -31,6 +31,7 @@ import java.text.SimpleDateFormat; import java.util.Base64; import java.util.Date; +import static org.eclipse.leshan.core.model.ResourceModel.Type.NONE; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; @Slf4j @@ -53,14 +54,15 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { return value; } - if (currentType == expectedType) { - /** expected type */ - return value; - } if (currentType == null) { currentType = OPAQUE; } + if (currentType == expectedType || currentType == NONE) { + /** expected type */ + return value; + } + switch (expectedType) { case INTEGER: switch (currentType) { From 82d4cb538112a98ed6425cf56c63e27cceb6731f Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 15 Apr 2025 15:12:18 +0300 Subject: [PATCH 054/335] Add monitoring for calculated fields --- .../monitoring/client/WsClient.java | 69 +++++++++++-------- .../config/transport/TransportInfo.java | 11 ++- .../transport/TransportMonitoringTarget.java | 1 + .../monitoring/data/MonitoredServiceKey.java | 1 + .../data/ServiceFailureException.java | 11 ++- .../monitoring/data/cmd/EntityDataUpdate.java | 18 +++-- .../monitoring/service/BaseHealthChecker.java | 44 ++++++++---- .../service/BaseMonitoringService.java | 37 +++++----- .../service/MonitoringReporter.java | 2 +- .../transport/TransportHealthChecker.java | 44 +++++++++++- .../src/main/resources/tb-monitoring.yml | 11 +++ .../thingsboard/rest/client/RestClient.java | 20 +++++- 12 files changed, 189 insertions(+), 80 deletions(-) diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/client/WsClient.java b/monitoring/src/main/java/org/thingsboard/monitoring/client/WsClient.java index 9246feac27..9bacbdfd45 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/client/WsClient.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/client/WsClient.java @@ -36,8 +36,11 @@ import org.thingsboard.server.common.data.query.EntityListFilter; import javax.net.ssl.SSLParameters; import java.net.URI; import java.nio.channels.NotYetConnectedException; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -48,13 +51,13 @@ import java.util.stream.Collectors; @Slf4j public class WsClient extends WebSocketClient implements AutoCloseable { - public volatile JsonNode lastMsg; + public final List lastMsgs = new ArrayList<>(); private CountDownLatch reply; private CountDownLatch update; private final Lock updateLock = new ReentrantLock(); - private long requestTimeoutMs; + private final long requestTimeoutMs; public WsClient(URI serverUri, long requestTimeoutMs) { super(serverUri); @@ -63,7 +66,6 @@ public class WsClient extends WebSocketClient implements AutoCloseable { @Override public void onOpen(ServerHandshake serverHandshake) { - } @Override @@ -73,8 +75,9 @@ public class WsClient extends WebSocketClient implements AutoCloseable { } updateLock.lock(); try { - lastMsg = JacksonUtil.toJsonNode(s); - log.trace("Received new msg: {}", lastMsg.toPrettyString()); + JsonNode msg = JacksonUtil.toJsonNode(s); + lastMsgs.add(msg); + log.trace("Received new msg: {}", msg.toPrettyString()); if (update != null) { update.countDown(); } @@ -96,11 +99,11 @@ public class WsClient extends WebSocketClient implements AutoCloseable { log.error("WebSocket client error:", e); } - public void registerWaitForUpdate() { + public void registerWaitForUpdates(int count) { updateLock.lock(); try { - lastMsg = null; - update = new CountDownLatch(1); + lastMsgs.clear(); + update = new CountDownLatch(count); } finally { updateLock.unlock(); } @@ -111,6 +114,7 @@ public class WsClient extends WebSocketClient implements AutoCloseable { public void send(String text) throws NotYetConnectedException { updateLock.lock(); try { + lastMsgs.clear(); reply = new CountDownLatch(1); } finally { updateLock.unlock(); @@ -118,19 +122,19 @@ public class WsClient extends WebSocketClient implements AutoCloseable { super.send(text); } - public WsClient subscribeForTelemetry(List devices, String key) { + public WsClient subscribeForTelemetry(List devices, List keys) { EntityDataCmd cmd = new EntityDataCmd(); cmd.setCmdId(RandomUtils.nextInt(0, 1000)); EntityListFilter devicesFilter = new EntityListFilter(); devicesFilter.setEntityType(EntityType.DEVICE); devicesFilter.setEntityList(devices.stream().map(UUID::toString).collect(Collectors.toList())); - EntityDataPageLink pageLink = new EntityDataPageLink(100,0, null, null); + EntityDataPageLink pageLink = new EntityDataPageLink(100, 0, null, null); EntityDataQuery devicesQuery = new EntityDataQuery(devicesFilter, pageLink, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); cmd.setQuery(devicesQuery); LatestValueCmd latestCmd = new LatestValueCmd(); - latestCmd.setKeys(List.of(new EntityKey(EntityKeyType.TIME_SERIES, key))); + latestCmd.setKeys(keys.stream().map(key -> new EntityKey(EntityKeyType.TIME_SERIES, key)).toList()); cmd.setLatestCmd(latestCmd); CmdsWrapper wrapper = new CmdsWrapper(); @@ -139,12 +143,12 @@ public class WsClient extends WebSocketClient implements AutoCloseable { return this; } - public JsonNode waitForUpdate(long ms) { + public List waitForUpdates(long ms) { log.trace("update latch count: {}", update.getCount()); try { if (update.await(ms, TimeUnit.MILLISECONDS)) { log.trace("Waited for update"); - return getLastMsg(); + return getLastMsgs(); } } catch (InterruptedException e) { log.debug("Failed to await reply", e); @@ -157,7 +161,8 @@ public class WsClient extends WebSocketClient implements AutoCloseable { try { if (reply.await(requestTimeoutMs, TimeUnit.MILLISECONDS)) { log.trace("Waited for reply"); - return getLastMsg(); + List lastMsgs = getLastMsgs(); + return lastMsgs.isEmpty() ? null : lastMsgs.get(0); } } catch (InterruptedException e) { log.debug("Failed to await reply", e); @@ -166,24 +171,30 @@ public class WsClient extends WebSocketClient implements AutoCloseable { throw new IllegalStateException("No WS reply arrived within " + requestTimeoutMs + " ms"); } - private JsonNode getLastMsg() { - if (lastMsg != null) { - JsonNode errorMsg = lastMsg.get("errorMsg"); - if (errorMsg != null && !errorMsg.isNull() && StringUtils.isNotEmpty(errorMsg.asText())) { - throw new RuntimeException("WS error from server: " + errorMsg.asText()); - } else { - return lastMsg; - } - } else { - return null; + private List getLastMsgs() { + if (lastMsgs.isEmpty()) { + return lastMsgs; + } + List errors = lastMsgs.stream() + .map(msg -> msg.get("errorMsg")) + .filter(errorMsg -> errorMsg != null && !errorMsg.isNull() && StringUtils.isNotEmpty(errorMsg.asText())) + .toList(); + if (!errors.isEmpty()) { + throw new RuntimeException("WS error from server: " + errors.stream() + .map(JsonNode::asText) + .collect(Collectors.joining(", "))); } + return lastMsgs; } - public Object getTelemetryUpdate(UUID deviceId, String key) { - JsonNode lastMsg = getLastMsg(); - if (lastMsg == null || lastMsg.isNull()) return null; - EntityDataUpdate update = JacksonUtil.treeToValue(lastMsg, EntityDataUpdate.class); - return update.getLatest(deviceId, key); + public Map getLatest(UUID deviceId) { + Map updates = new HashMap<>(); + getLastMsgs().forEach(msg -> { + EntityDataUpdate update = JacksonUtil.treeToValue(msg, EntityDataUpdate.class); + Map latest = update.getLatest(deviceId); + updates.putAll(latest); + }); + return updates; } @Override diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportInfo.java b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportInfo.java index 208d28ffee..6b77e1e268 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportInfo.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportInfo.java @@ -20,16 +20,15 @@ import lombok.Data; @Data public class TransportInfo { - private final TransportType transportType; - private final String baseUrl; - private final String queue; + private final TransportType type; + private final TransportMonitoringTarget target; @Override public String toString() { - if (queue.equals("Main")) { - return String.format("*%s* (%s)", transportType.getName(), baseUrl); + if (target.getQueue().equals("Main")) { + return String.format("*%s* (%s)", type.getName(), target.getBaseUrl()); } else { - return String.format("*%s* (%s) _%s_", transportType.getName(), baseUrl, queue); + return String.format("*%s* (%s) _%s_", type.getName(), target.getBaseUrl(), target.getQueue()); } } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java index 1e6a3c8509..e8a9ab03fa 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java @@ -28,6 +28,7 @@ public class TransportMonitoringTarget implements MonitoringTarget { private DeviceConfig device; // set manually during initialization private String queue; private boolean checkDomainIps; + private String namePrefix; @Override public UUID getDeviceId() { diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/MonitoredServiceKey.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/MonitoredServiceKey.java index 9c3ee5b786..7579d75231 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/data/MonitoredServiceKey.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/MonitoredServiceKey.java @@ -19,5 +19,6 @@ public class MonitoredServiceKey { public static final String GENERAL = "Monitoring"; public static final String EDQS = "*EDQS*"; + public static final String CF = "*CF*"; } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/ServiceFailureException.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/ServiceFailureException.java index 5f46514bd1..b2592b8719 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/data/ServiceFailureException.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/ServiceFailureException.java @@ -15,14 +15,21 @@ */ package org.thingsboard.monitoring.data; +import lombok.Getter; + +@Getter public class ServiceFailureException extends RuntimeException { - public ServiceFailureException(Throwable cause) { + private final Object serviceKey; + + public ServiceFailureException(Object serviceKey, Throwable cause) { super(cause.getMessage(), cause); + this.serviceKey = serviceKey; } - public ServiceFailureException(String message) { + public ServiceFailureException(Object serviceKey, String message) { super(message); + this.serviceKey = serviceKey; } } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/cmd/EntityDataUpdate.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/cmd/EntityDataUpdate.java index b82dcbf54d..0706dedcf3 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/data/cmd/EntityDataUpdate.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/cmd/EntityDataUpdate.java @@ -19,9 +19,11 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityKeyType; -import org.thingsboard.server.common.data.query.TsValue; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; @Data @@ -31,14 +33,16 @@ public class EntityDataUpdate { @JsonIgnoreProperties(ignoreUnknown = true) private List update; - public String getLatest(UUID entityId, String key) { - if (update == null) return null; - - return update.stream() + public Map getLatest(UUID entityId) { + if (update == null || update.isEmpty()) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + update.stream() .filter(entityData -> entityData.getEntityId().getId().equals(entityId)).findFirst() .map(EntityData::getLatest).map(latest -> latest.get(EntityKeyType.TIME_SERIES)) - .map(latest -> latest.get(key)).map(TsValue::getValue) - .orElse(null); + .ifPresent(latest -> latest.forEach((key, tsValue) -> result.put(key, tsValue.getValue()))); + return result; } } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java index 482b3a30fb..34e0ebc4c5 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java @@ -52,11 +52,14 @@ public abstract class BaseHealthChecker> associates = new HashMap<>(); public static final String TEST_TELEMETRY_KEY = "testData"; + public static final String TEST_CF_TELEMETRY_KEY = "testDataCf"; @PostConstruct private void init() { @@ -68,7 +71,8 @@ public abstract class BaseHealthChecker latest = wsClient.getLatest(target.getDeviceId()); + if (latest.isEmpty()) { + throw new ServiceFailureException(info, "No WS update arrived within " + resultCheckTimeoutMs + " ms"); + } + String actualValue = latest.get(TEST_TELEMETRY_KEY); + if (!testValue.equals(actualValue)) { + throw new ServiceFailureException(info, "Was expecting value " + testValue + " but got " + actualValue); + } + if (checkCalculatedFields) { + String cfTestValue = testValue + "-cf"; + String actualCfValue = latest.get(TEST_CF_TELEMETRY_KEY); + if (actualCfValue == null) { + throw new ServiceFailureException(MonitoredServiceKey.CF, "No CF value arrived"); + } else if (!cfTestValue.equals(actualCfValue)) { + throw new ServiceFailureException(MonitoredServiceKey.CF, "Was expecting CF value " + cfTestValue + " but got " + actualCfValue); + } else { + reporter.serviceIsOk(MonitoredServiceKey.CF); + } } reporter.reportLatency(Latencies.wsUpdate(getKey()), stopWatch.getTime()); } @@ -121,6 +138,7 @@ public abstract class BaseHealthChecker, T extends MonitoringTarget> { @@ -79,7 +81,9 @@ public abstract class BaseMonitoringService, T ext protected ApplicationContext applicationContext; @Value("${monitoring.edqs.enabled:false}") - private boolean edqsMonitoringEnabled; + private boolean checkEdqs; + @Value("${monitoring.calculated_fields.enabled:true}") + protected boolean checkCalculatedFields; @PostConstruct private void init() { @@ -121,7 +125,7 @@ public abstract class BaseMonitoringService, T ext try (WsClient wsClient = wsClientFactory.createClient(accessToken)) { stopWatch.start(); - wsClient.subscribeForTelemetry(devices, TransportHealthChecker.TEST_TELEMETRY_KEY).waitForReply(); + wsClient.subscribeForTelemetry(devices, getTestTelemetryKeys()).waitForReply(); reporter.reportLatency(Latencies.WS_SUBSCRIBE, stopWatch.getTime()); for (BaseHealthChecker healthChecker : healthCheckers) { @@ -129,22 +133,17 @@ public abstract class BaseMonitoringService, T ext } } - if (edqsMonitoringEnabled) { - try { - stopWatch.start(); - checkEdqs(); - reporter.reportLatency(Latencies.EDQS_QUERY, stopWatch.getTime()); - - reporter.serviceIsOk(MonitoredServiceKey.EDQS); - } catch (ServiceFailureException e) { - reporter.serviceFailure(MonitoredServiceKey.EDQS, e); - } catch (Exception e) { - reporter.serviceFailure(MonitoredServiceKey.GENERAL, e); - } + if (checkEdqs) { + stopWatch.start(); + checkEdqs(); + reporter.reportLatency(Latencies.EDQS_QUERY, stopWatch.getTime()); + reporter.serviceIsOk(MonitoredServiceKey.EDQS); } reporter.reportLatencies(tbClient); log.debug("Finished {}", getName()); + } catch (ServiceFailureException e) { + reporter.serviceFailure(e.getServiceKey(), e); } catch (Throwable error) { try { reporter.serviceFailure(MonitoredServiceKey.GENERAL, error); @@ -199,7 +198,7 @@ public abstract class BaseMonitoringService, T ext .collect(Collectors.toSet()); Set missing = Sets.difference(new HashSet<>(this.devices), devices); if (!missing.isEmpty()) { - throw new ServiceFailureException("Missing devices in the response: " + missing); + throw new ServiceFailureException(MonitoredServiceKey.EDQS, "Missing devices in the response: " + missing); } result.getData().stream() @@ -211,7 +210,7 @@ public abstract class BaseMonitoringService, T ext Stream.of("name", "type", "testData").forEach(key -> { TsValue value = values.get(key); if (value == null || StringUtils.isBlank(value.getValue())) { - throw new ServiceFailureException("Missing " + key + " for device " + entityData.getEntityId()); + throw new ServiceFailureException(MonitoredServiceKey.EDQS, "Missing " + key + " for device " + entityData.getEntityId()); } }); }); @@ -232,6 +231,10 @@ public abstract class BaseMonitoringService, T ext .collect(Collectors.toSet()); } + private List getTestTelemetryKeys() { + return checkCalculatedFields ? List.of(TEST_TELEMETRY_KEY, TEST_CF_TELEMETRY_KEY) : List.of(TEST_TELEMETRY_KEY); + } + private void stopHealthChecker(BaseHealthChecker healthChecker) throws Exception { healthChecker.destroyClient(); devices.remove(healthChecker.getTarget().getDeviceId()); diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java index 3554731e58..62ed0d74aa 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java @@ -113,7 +113,7 @@ public class MonitoringReporter { public void serviceFailure(Object serviceKey, Throwable error) { if (log.isDebugEnabled()) { - log.error("Error occurred", error); + log.error("[{}] Error occurred", serviceKey, error); } int failuresCount = failuresCounters.computeIfAbsent(serviceKey, k -> new AtomicInteger()).incrementAndGet(); ServiceFailureNotification notification = new ServiceFailureNotification(serviceKey, error, failuresCount); diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportHealthChecker.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportHealthChecker.java index f5e6cb5469..b892f5d609 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportHealthChecker.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportHealthChecker.java @@ -32,6 +32,14 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.cf.CalculatedFieldType; +import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.ArgumentType; +import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.OutputType; +import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecBootstrapClientCredential; @@ -47,6 +55,8 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import java.util.Map; + @Slf4j public abstract class TransportHealthChecker extends BaseHealthChecker { @@ -74,7 +84,7 @@ public abstract class TransportHealthChecker getImages(PageLink pageLink, boolean includeSystemImages) { - return this.getImages(pageLink, null, includeSystemImages); + return this.getImages(pageLink, null, includeSystemImages); } public PageData getImages(PageLink pageLink, ResourceSubType imageSubType, boolean includeSystemImages) { @@ -4056,6 +4057,21 @@ public class RestClient implements Closeable { timeout).getBody(); } + public CalculatedField saveCalculatedField(CalculatedField calculatedField) { + return restTemplate.postForEntity(baseURL + "/api/calculatedField", calculatedField, CalculatedField.class).getBody(); + } + + public PageData getCalculatedFieldsByEntityId(EntityId entityId, PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/" + entityId.getEntityType() + "/" + entityId.getId() + "/calculatedFields?" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + + } + private String getTimeUrlParams(TimePageLink pageLink) { String urlParams = getUrlParams(pageLink); if (pageLink.getStartTime() != null) { From a8a0083bb2d430288794badf90501436b9a8cdaf Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 15 Apr 2025 15:16:55 +0300 Subject: [PATCH 055/335] Monitoring: trim device name --- .../service/transport/TransportHealthChecker.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportHealthChecker.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportHealthChecker.java index b892f5d609..d002546335 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportHealthChecker.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportHealthChecker.java @@ -97,7 +97,7 @@ public abstract class TransportHealthChecker Date: Tue, 15 Apr 2025 15:37:20 +0300 Subject: [PATCH 056/335] Monitoring: minor refactoring --- .../monitoring/service/BaseHealthChecker.java | 8 ++++---- .../service/transport/TransportHealthChecker.java | 11 ++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java index 34e0ebc4c5..1e9cdbe191 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java @@ -52,8 +52,6 @@ public abstract class BaseHealthChecker> associates = new HashMap<>(); @@ -71,7 +69,7 @@ public abstract class BaseHealthChecker extends BaseHealthChecker { + @Value("${monitoring.calculated_fields.enabled:true}") + private boolean calculatedFieldsMonitoringEnabled; + public TransportHealthChecker(C config, TransportMonitoringTarget target) { super(config, target); } @@ -100,7 +104,7 @@ public abstract class TransportHealthChecker Date: Wed, 16 Apr 2025 18:55:45 +0200 Subject: [PATCH 057/335] Kafka 4.0 --- docker/docker-compose.kafka.yml | 4 +--- docker/kafka.env | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/docker/docker-compose.kafka.yml b/docker/docker-compose.kafka.yml index 9d33f33941..7e6fcae09c 100644 --- a/docker/docker-compose.kafka.yml +++ b/docker/docker-compose.kafka.yml @@ -19,13 +19,11 @@ version: '3.0' services: kafka: restart: always - image: "bitnami/kafka:3.7.0" + image: "bitnami/kafka:4.0" ports: - "9092:9092" env_file: - kafka.env - depends_on: - - zookeeper tb-js-executor: env_file: - queue-kafka.env diff --git a/docker/kafka.env b/docker/kafka.env index 9c28885252..e5da2aec1a 100644 --- a/docker/kafka.env +++ b/docker/kafka.env @@ -1,11 +1,19 @@ -ALLOW_PLAINTEXT_LISTENER=yes -KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 -KAFKA_CFG_LISTENERS=INSIDE://:9093,OUTSIDE://:9092 -KAFKA_CFG_ADVERTISED_LISTENERS=INSIDE://:9093,OUTSIDE://kafka:9092 -KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT +# KRaft settings +KAFKA_CFG_NODE_ID=0 +KAFKA_CFG_PROCESS_ROLES=controller,broker +KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093 +# Listeners +KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093 +KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://:9092 +KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT +KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER +KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT +# Kafka settings KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=false -KAFKA_CFG_INTER_BROKER_LISTENER_NAME=INSIDE KAFKA_CFG_LOG_RETENTION_BYTES=1073741824 +# default is 1 GB KAFKA_CFG_SEGMENT_BYTES=268435456 -KAFKA_CFG_LOG_RETENTION_MS=300000 -KAFKA_CFG_LOG_CLEANUP_POLICY=delete +# default 7 days +#KAFKA_CFG_LOG_RETENTION_MS=300000 +# default +#KAFKA_CFG_LOG_CLEANUP_POLICY=delete \ No newline at end of file From 1869296ff240df84704d522791b694e564dac66a Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 17 Apr 2025 12:02:45 +0300 Subject: [PATCH 058/335] added ability to preserve last update ts for calculated value --- .../controller/CalculatedFieldController.java | 19 ++++++++- .../ctx/state/BaseCalculatedFieldState.java | 28 +++++++++++-- .../cf/ctx/state/CalculatedFieldCtx.java | 3 ++ .../cf/ctx/state/CalculatedFieldState.java | 2 + .../ctx/state/ScriptCalculatedFieldState.java | 3 +- .../ctx/state/SimpleCalculatedFieldState.java | 39 +++++++++++++------ .../SimpleCalculatedFieldConfiguration.java | 2 + .../script/api/tbel/TbelCfCtx.java | 5 ++- 8 files changed, 82 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java index f899d0f480..7e4e041600 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java @@ -36,6 +36,8 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfCtx; import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; +import org.thingsboard.script.api.tbel.TbelCfTsDoubleVal; +import org.thingsboard.script.api.tbel.TbelCfTsRollingArg; import org.thingsboard.script.api.tbel.TbelInvokeService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EventInfo; @@ -242,9 +244,8 @@ public class CalculatedFieldController extends BaseController { ctxAndArgNames.toArray(String[]::new) ); - Object[] args = new Object[ctxAndArgNames.size()]; - args[0] = new TbelCfCtx(arguments); + args[0] = new TbelCfCtx(arguments, getLastUpdateTimestamp(arguments)); for (int i = 1; i < ctxAndArgNames.size(); i++) { var arg = arguments.get(ctxAndArgNames.get(i)); if (arg instanceof TbelCfSingleValueArg svArg) { @@ -267,6 +268,20 @@ public class CalculatedFieldController extends BaseController { return result; } + private long getLastUpdateTimestamp(Map arguments) { + long lastUpdateTimestamp = -1; + for (TbelCfArg entry : arguments.values()) { + if (entry instanceof TbelCfSingleValueArg singleValueArg) { + long ts = singleValueArg.getTs(); + lastUpdateTimestamp = Math.max(lastUpdateTimestamp, ts); + } else if (entry instanceof TbelCfTsRollingArg tsRollingArg) { + long maxTs = tsRollingArg.getValues().stream().mapToLong(TbelCfTsDoubleVal::getTs).max().orElse(-1); + lastUpdateTimestamp = Math.max(lastUpdateTimestamp, maxTs); + } + } + return lastUpdateTimestamp; + } + private & HasTenantId, I extends EntityId> void checkReferencedEntities(CalculatedFieldConfiguration calculatedFieldConfig, SecurityUser user) throws ThingsboardException { List referencedEntityIds = calculatedFieldConfig.getReferencedEntities(); for (EntityId referencedEntityId : referencedEntityIds) { diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java index 80b003b3cc..84c61661ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java @@ -35,13 +35,15 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { protected Map arguments; protected boolean sizeExceedsLimit; + protected long lastUpdateTimestamp = -1; + public BaseCalculatedFieldState(List requiredArguments) { this.requiredArguments = requiredArguments; this.arguments = new HashMap<>(); } public BaseCalculatedFieldState() { - this(new ArrayList<>(), new HashMap<>(), false); + this(new ArrayList<>(), new HashMap<>(), false, -1); } @Override @@ -59,14 +61,21 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { checkArgumentSize(key, newEntry, ctx); ArgumentEntry existingEntry = arguments.get(key); + boolean entryUpdated; if (existingEntry == null || newEntry.isForceResetPrevious()) { validateNewEntry(newEntry); arguments.put(key, newEntry); - stateUpdated = true; + entryUpdated = true; } else { - stateUpdated = existingEntry.updateEntry(newEntry); + entryUpdated = existingEntry.updateEntry(newEntry); + } + + if (entryUpdated) { + stateUpdated = true; + updateLastUpdateTimestamp(newEntry); } + } return stateUpdated; @@ -100,4 +109,17 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { protected abstract void validateNewEntry(ArgumentEntry newEntry); + private void updateLastUpdateTimestamp(ArgumentEntry entry) { + if (entry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { + long ts = singleValueArgumentEntry.getTs(); + this.lastUpdateTimestamp = Math.max(this.lastUpdateTimestamp, ts); + } else if (entry instanceof TsRollingArgumentEntry tsRollingArgumentEntry) { + Map.Entry lastEntry = tsRollingArgumentEntry.getTsRecords().pollLastEntry(); + if (lastEntry != null) { + long ts = lastEntry.getKey(); + this.lastUpdateTimestamp = Math.max(this.lastUpdateTimestamp, ts); + } + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java index 674ce3726f..25083ed415 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -61,6 +62,7 @@ public class CalculatedFieldCtx { private final List argNames; private Output output; private String expression; + private boolean preserveLatestTs; private TbelInvokeService tbelInvokeService; private CalculatedFieldScriptEngine calculatedFieldScriptEngine; private ThreadLocal customExpression; @@ -94,6 +96,7 @@ public class CalculatedFieldCtx { this.argNames = new ArrayList<>(arguments.keySet()); this.output = configuration.getOutput(); this.expression = configuration.getExpression(); + this.preserveLatestTs = CalculatedFieldType.SIMPLE.equals(calculatedField.getType()) && ((SimpleCalculatedFieldConfiguration) configuration).isPreserveLastUpdateTs(); this.tbelInvokeService = tbelInvokeService; this.maxDataPointsPerRollingArg = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxDataPointsPerRollingArg); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java index fc4ba513d2..6eac3358ba 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java @@ -42,6 +42,8 @@ public interface CalculatedFieldState { Map getArguments(); + long getLastUpdateTimestamp(); + void setRequiredArguments(List requiredArguments); boolean updateState(CalculatedFieldCtx ctx, Map argumentValues); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java index bf00f1b0b1..65ef40330c 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java @@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.service.cf.CalculatedFieldResult; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -67,7 +66,7 @@ public class ScriptCalculatedFieldState extends BaseCalculatedFieldState { args.add(arg); } } - args.set(0, new TbelCfCtx(arguments)); + args.set(0, new TbelCfCtx(arguments, getLastUpdateTimestamp())); ListenableFuture resultFuture = ctx.getCalculatedFieldScriptEngine().executeJsonAsync(args.toArray()); Output output = ctx.getOutput(); return Futures.transform(resultFuture, diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java index 480b334ac3..2ad961f72c 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.service.cf.ctx.state; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.Data; @@ -65,19 +67,34 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { double expressionResult = expr.evaluate(); Output output = ctx.getOutput(); - Object result; - Integer decimals = output.getDecimalsByDefault(); - if (decimals != null) { - if (decimals.equals(0)) { - result = TbUtils.toInt(expressionResult); - } else { - result = TbUtils.toFixed(expressionResult, decimals); - } - } else { - result = expressionResult; + Object result = formatResult(expressionResult, output.getDecimalsByDefault()); + JsonNode outputResult = createResultJson(ctx.isPreserveLatestTs(), output.getName(), result); + + return Futures.immediateFuture(new CalculatedFieldResult(output.getType(), output.getScope(), outputResult)); + } + + private Object formatResult(double expressionResult, Integer decimals) { + if (decimals == null) { + return expressionResult; } + return decimals.equals(0) + ? TbUtils.toInt(expressionResult) + : TbUtils.toFixed(expressionResult, decimals); + } + + private JsonNode createResultJson(boolean preserveLatestTs, String outputName, Object result) { + ObjectNode valuesNode = JacksonUtil.newObjectNode(); + valuesNode.set(outputName, JacksonUtil.valueToTree(result)); - return Futures.immediateFuture(new CalculatedFieldResult(output.getType(), output.getScope(), JacksonUtil.valueToTree(Map.of(output.getName(), result)))); + long lastTimestamp = getLastUpdateTimestamp(); + if (preserveLatestTs && lastTimestamp != -1) { + ObjectNode resultNode = JacksonUtil.newObjectNode(); + resultNode.put("ts", lastTimestamp); + resultNode.set("values", valuesNode); + return resultNode; + } else { + return valuesNode; + } } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java index 5c0ce71e86..c748afdac4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java @@ -23,6 +23,8 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; @EqualsAndHashCode(callSuper = true) public class SimpleCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration { + private boolean preserveLastUpdateTs; + @Override public CalculatedFieldType getType() { return CalculatedFieldType.SIMPLE; diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java index ce42e2cf3b..1a8610cbab 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java @@ -24,9 +24,12 @@ public class TbelCfCtx implements TbelCfObject { @Getter private final Map args; + @Getter + private final long lastTs; - public TbelCfCtx(Map args) { + public TbelCfCtx(Map args, long lastUpdateTs) { this.args = Collections.unmodifiableMap(args); + this.lastTs = lastUpdateTs != -1 ? lastUpdateTs : System.currentTimeMillis(); } @Override From 33ec93c32dc0ddcb5fe1ec60a36763494ce8ac30 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 18 Apr 2025 12:31:15 +0300 Subject: [PATCH 059/335] lwm2m: comments1 --- .../lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java | 3 ++- .../server/downlink/DefaultLwM2mDownlinkMsgHandler.java | 2 +- .../server/common/transport/util/JsonUtils.java | 9 +-------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java index b2d95ec906..3065255687 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java @@ -45,8 +45,9 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INIT import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.QUEUED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.*; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_INSTANCE_ID; @Slf4j diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index ed685dfc6c..787db56411 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -99,6 +99,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; +import static org.apache.commons.codec.binary.Base64.isBase64; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.DIMENSION; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.ENABLER_VERSION; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.EVALUATE_MAXIMUM_PERIOD; @@ -113,7 +114,6 @@ import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.SHOR import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.STEP; import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; -import static org.thingsboard.server.common.transport.util.JsonUtils.isBase64; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.createModelsDefault; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fromVersionedIdToObjectId; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java index a79d897daf..cdbc371c60 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java @@ -23,13 +23,10 @@ import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; import java.util.List; import java.util.Map; -import java.util.regex.Pattern; +import static org.apache.commons.codec.binary.Base64.isBase64; public class JsonUtils { - private static final Pattern BASE64_PATTERN = - Pattern.compile("^[A-Za-z0-9+/]+={0,2}$"); - public static JsonObject getJsonObject(List tsKv) { JsonObject json = new JsonObject(); for (KeyValueProto kv : tsKv) { @@ -87,8 +84,4 @@ public class JsonUtils { return jsonObject; } - - public static boolean isBase64(String value) { - return value.length() % 4 == 0 && BASE64_PATTERN.matcher(value).matches(); - } } From 5bf4bd8510ebe6d4f7b5b58352e17d0e14db951e Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 18 Apr 2025 12:56:47 +0300 Subject: [PATCH 060/335] Improved safety of customTranslation placeholder parsing --- ui-ngx/src/app/core/services/utils.service.ts | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts index bfaad1148d..734e503aeb 100644 --- a/ui-ngx/src/app/core/services/utils.service.ts +++ b/ui-ngx/src/app/core/services/utils.service.ts @@ -51,7 +51,7 @@ import cssjs from '@core/css/css'; import { isNotEmptyTbFunction } from '@shared/models/js-function.models'; import { defaultFormProperties, FormProperty } from '@shared/models/dynamic-form.models'; -const i18nRegExp = new RegExp(`{${i18nPrefix}:[^{}]+}`, 'g'); +const i18nRegExp = new RegExp(`{${i18nPrefix}:([^{}]+)}`, 'g'); const predefinedFunctions: { [func: string]: string } = { Sin: 'return Math.round(1000*Math.sin(time/5000));', @@ -209,21 +209,20 @@ export class UtilsService { } public customTranslation(translationValue: string, defaultValue: string = translationValue): string { - if (translationValue && isString(translationValue)) { - if (translationValue.includes(`{${i18nPrefix}`)) { - const matches = translationValue.match(i18nRegExp); - let result = translationValue; - for (const match of matches) { - const translationId = match.substring(6, match.length - 1); - result = result.replace(match, this.doTranslate(translationId, match)); - } - return result; - } else { - return this.doTranslate(translationValue, defaultValue, customTranslationsPrefix); - } - } else { + if (!translationValue || !isString(translationValue)) { return translationValue; } + if (!translationValue.includes(`{${i18nPrefix}:`)) { + return this.doTranslate(translationValue, defaultValue, customTranslationsPrefix); + } + const matches = translationValue.matchAll(i18nRegExp); + let result = translationValue; + for (const [fullMatch, translationId] of matches) { + if (translationId) { + result = result.replace(fullMatch, this.doTranslate(translationId, fullMatch)); + } + } + return result; } private doTranslate(translationValue: string, defaultValue: string, prefix?: string): string { From d8435246dde65ddffa1f065bf3b7d8a1f56fff9e Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 18 Apr 2025 19:24:45 +0300 Subject: [PATCH 061/335] lwm2m: add Base64 from java.util and add Test FW to objectId=19 jsonNode --- .../rpc/sql/RpcLwm2mIntegrationWriteTest.java | 65 ++++++++++++++++--- .../DefaultLwM2mDownlinkMsgHandler.java | 2 +- .../ota/DefaultLwM2MOtaUpdateService.java | 2 - .../common/transport/util/JsonUtils.java | 9 ++- 4 files changed, 65 insertions(+), 13 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java index 9d64e1a64d..01030c24bd 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java @@ -16,26 +16,22 @@ package org.thingsboard.server.transport.lwm2m.rpc.sql; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.hash.Hashing; +import org.eclipse.leshan.client.resource.LwM2mObjectEnabler; import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.node.LwM2mPath; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.tbel.TbUtils; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; +import java.util.Base64; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_2; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_15; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_14; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_INSTANCE_ID_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.*; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.*; public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTest { @@ -77,6 +73,28 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue + ", type=STRING]"; assertTrue(actualValues.contains(expected)); } + + @Test + public void testWriteReplaceValueMultipleResource_Result_CHANGED_Multi_Instance_Resource_must_One_ValueJsonNodeToBase64() throws Exception { + LwM2mObjectEnabler lwM2mObjectEnabler = lwM2MTestClient.getLeshanClient().getObjectTree().getObjectEnabler(BINARY_APP_DATA_CONTAINER); + if (lwM2mObjectEnabler != null) { + String expectedPath = objectIdVer_19 + "/" + FW_INSTANCE_ID + "/"; + String expectedValueStr = getJsonNodeBase64(); + String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"0\":\"" + expectedValueStr + "\"}}"; + String actualResult = sendRPCreateById(expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CREATED.getName(), rpcActualResult.get("result").asText()); + actualResult = sendRPCReadById(expectedPath); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + byte[] expectedValue0 = TbUtils.base64ToBytes(expectedValueStr); + String expectedInstance = "LwM2mObjectInstance [id=" + FW_INSTANCE_ID; + assertTrue(actualValues.contains(expectedInstance)); + String expected = "{0=LwM2mMultipleResource [id=0, values={0=LwM2mResourceInstance [id=0, value=" + expectedValue0.length + "Bytes, type=OPAQUE]}"; + assertTrue(actualValues.contains(expected)); + } + } + @Test public void testWriteReplaceValueMultipleResource_Result_CHANGED_Multi_Instance_Resource_must_One() throws Exception { int resourceInstanceId0 = 0; @@ -525,4 +543,33 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes String setRpcRequest = "{\"method\": \"WriteComposite\", \"params\": {\"nodes\":" + nodes + "}}"; return doPostAsync("/api/plugins/rpc/twoway/" + lwM2MTestClient.getDeviceIdStr(), setRpcRequest, String.class, status().isOk()); } + + /** + * ObjectId = 19/65533/0 + * { + * "title" : "My firmware", + * "version" : "fw.v.1.5.0-update", + * "checksum" : "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a", + * "fileSize" : 1, + * "fileName" : "filename.txt" + * } + */ + private String getJsonNodeBase64() { + ObjectNode objectNodeInfoOta = JacksonUtil.newObjectNode(); + byte[] firmwareChunk = new byte[]{1}; + String fileChecksumSHA256 = Hashing.sha256().hashBytes(firmwareChunk).toString(); + objectNodeInfoOta.put(OTA_INFO_19_TITLE, "My firmware"); + objectNodeInfoOta.put(OTA_INFO_19_VERSION, "fw.v.1.5.0-update"); + objectNodeInfoOta.put(OTA_INFO_19_FILE_CHECKSUM256, fileChecksumSHA256); + objectNodeInfoOta.put(OTA_INFO_19_FILE_SIZE, firmwareChunk.length); + objectNodeInfoOta.put(OTA_INFO_19_FILE_NAME, "filename.txt"); + String objectNodeInfoOtaStr = JacksonUtil.toString(objectNodeInfoOta); + assert objectNodeInfoOtaStr != null; + return Base64.getEncoder().encodeToString(objectNodeInfoOtaStr.getBytes()); + } + + private String sendRPCreateById(String path, String value) throws Exception { + String setRpcRequest = "{\"method\": \"Create\", \"params\": {\"id\": \"" + path + "\", \"value\": " + value + " }}"; + return doPostAsync("/api/plugins/rpc/twoway/" + lwM2MTestClient.getDeviceIdStr(), setRpcRequest, String.class, status().isOk()); + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 787db56411..ed685dfc6c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -99,7 +99,6 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; -import static org.apache.commons.codec.binary.Base64.isBase64; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.DIMENSION; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.ENABLER_VERSION; import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.EVALUATE_MAXIMUM_PERIOD; @@ -114,6 +113,7 @@ import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.SHOR import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.STEP; import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; +import static org.thingsboard.server.common.transport.util.JsonUtils.isBase64; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.createModelsDefault; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fromVersionedIdToObjectId; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index cfa8feb89f..da94c19422 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -701,8 +701,6 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl String objectNodeInfoOtaStr = JacksonUtil.toString(objectNodeInfoOta); assert objectNodeInfoOtaStr != null; String objectNodeInfoOtaBase64 = Base64.getEncoder().encodeToString(objectNodeInfoOtaStr.getBytes()); - - LwM2mPath pathOtaInstance = new LwM2mPath(targetId); if (client.getRegistration().getAvailableInstances().contains(pathOtaInstance)) { String versionId = targetIdVer + "/0/0"; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java index cdbc371c60..a79d897daf 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java @@ -23,10 +23,13 @@ import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; import java.util.List; import java.util.Map; -import static org.apache.commons.codec.binary.Base64.isBase64; +import java.util.regex.Pattern; public class JsonUtils { + private static final Pattern BASE64_PATTERN = + Pattern.compile("^[A-Za-z0-9+/]+={0,2}$"); + public static JsonObject getJsonObject(List tsKv) { JsonObject json = new JsonObject(); for (KeyValueProto kv : tsKv) { @@ -84,4 +87,8 @@ public class JsonUtils { return jsonObject; } + + public static boolean isBase64(String value) { + return value.length() % 4 == 0 && BASE64_PATTERN.matcher(value).matches(); + } } From a40f9d6ae421b0fe0be051a6a44cd239dbbcc579 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 22 Apr 2025 10:56:47 +0300 Subject: [PATCH 062/335] Ver 4.0.1-RC --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/edqs/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- edqs/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/edqs/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 59 files changed, 60 insertions(+), 60 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 557179e918..2ad4f3a9a6 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 8ce6cf3761..954c44bb41 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 5ee70e2902..61a1b29b11 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index d92b87fbb0..ee9c1eae75 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index fe6ae3c64c..2d1a54456f 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index cb8e578760..d7142e723d 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index 648f3d1086..21cb05cacd 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index b3ec95983f..c110d4ac88 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index 09181e796e..e4aae6d62a 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 83100e11ea..0f1ee39302 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 6afae4e378..e205f2d8e2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 548b2e52c5..60443c3b00 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 2e4b14e282..67e325de33 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 61bbddc6c3..e5bd44f374 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index d247399ce6..728095e298 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.1.0-SNAPSHOT + 4.0.1-RC script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 3908326c96..e3ac9d0b8e 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.1.0-SNAPSHOT + 4.0.1-RC script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 90d619c133..d89891822d 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 314f79833b..4f5c282404 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index c1e3f77c4a..ed1d99600f 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 01d0476c04..f94a53f84f 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index d84757981a..65a2d7d35f 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 7180adc018..0f49cd471d 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 122696317e..b428548b3f 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.1.0-SNAPSHOT + 4.0.1-RC transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 2cd41064da..217b2edfb7 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 6719dc628a..4865eeb131 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 53a45a8197..9552ad511c 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 9dca643b5d..f203343c89 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard dao diff --git a/edqs/pom.xml b/edqs/pom.xml index 702c606daa..08928983f5 100644 --- a/edqs/pom.xml +++ b/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard edqs diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 7a55f105a7..4f59cbc975 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 407e4d6079..0af821a193 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa org.thingsboard.msa diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml index b3f584297e..d4a4c5644a 100644 --- a/msa/edqs/pom.xml +++ b/msa/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index be52d30d01..b49058eeb6 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 909c4ad0ac..76f1a9e2b3 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa diff --git a/msa/pom.xml b/msa/pom.xml index 280541452c..990679abc6 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 4a278f9dd8..bd85c2d4f5 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index f656f37a93..722eb9277e 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 004080a542..91b0aebca2 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 0d0d70e405..41e0421bc5 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 596b2d3bbd..4fcf3fce88 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index aae7594b27..8565872262 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 9f5a0fdc7f..46a21e7693 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 118783f98d..c2bd11cf96 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.1.0-SNAPSHOT + 4.0.1-RC org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 088e9ab4a8..6e112dd98a 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index b35a30f1b2..ffdfc00997 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 4eba1f5e2f..963dc710ed 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index fb3f88fd9e..7e0a00eee6 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard netty-mqtt - 4.1.0-SNAPSHOT + 4.0.1-RC jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 3f8631a17b..6d7981ccbb 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 828b33e835..c4e4c94236 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 0f089553b2..9f3dc788b6 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index d943759257..2348cdcd34 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 9666fa8059..008c422dbf 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index f9b5d9062f..0286c135e4 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 058e0e96ab..e883486767 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 03784a855c..67e5e43b60 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index df92bde39d..cc9eb45961 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index d27ab91259..d31e27486e 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 2cfe62ba95..ea7b65e918 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index e561ae65b5..da9486c4d0 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 79342e2ea8..f595a8572a 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.1.0-SNAPSHOT + 4.0.1-RC thingsboard org.thingsboard From bee6c19a51c995013e9978d2b68a5ed2e8ddc16a Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 22 Apr 2025 10:58:36 +0300 Subject: [PATCH 063/335] Version 4.1.0-SNAPSHOT --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/edqs/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- edqs/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/edqs/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 59 files changed, 60 insertions(+), 60 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 2ad4f3a9a6..557179e918 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 954c44bb41..8ce6cf3761 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 61a1b29b11..5ee70e2902 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index ee9c1eae75..d92b87fbb0 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 2d1a54456f..fe6ae3c64c 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index d7142e723d..cb8e578760 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index 21cb05cacd..648f3d1086 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index c110d4ac88..b3ec95983f 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index e4aae6d62a..09181e796e 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 0f1ee39302..83100e11ea 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index e205f2d8e2..6afae4e378 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 60443c3b00..548b2e52c5 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 67e325de33..2e4b14e282 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index e5bd44f374..61bbddc6c3 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 728095e298..d247399ce6 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.1-RC + 4.1.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index e3ac9d0b8e..3908326c96 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.1-RC + 4.1.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index d89891822d..90d619c133 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 4f5c282404..314f79833b 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index ed1d99600f..c1e3f77c4a 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index f94a53f84f..01d0476c04 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 65a2d7d35f..d84757981a 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 0f49cd471d..7180adc018 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index b428548b3f..122696317e 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.0.1-RC + 4.1.0-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 217b2edfb7..2cd41064da 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 4865eeb131..6719dc628a 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 9552ad511c..53a45a8197 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index f203343c89..9dca643b5d 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard dao diff --git a/edqs/pom.xml b/edqs/pom.xml index 08928983f5..702c606daa 100644 --- a/edqs/pom.xml +++ b/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard edqs diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 4f59cbc975..7a55f105a7 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 0af821a193..407e4d6079 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/edqs/pom.xml b/msa/edqs/pom.xml index d4a4c5644a..b3f584297e 100644 --- a/msa/edqs/pom.xml +++ b/msa/edqs/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index b49058eeb6..be52d30d01 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 76f1a9e2b3..909c4ad0ac 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index 990679abc6..280541452c 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index bd85c2d4f5..4a278f9dd8 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 722eb9277e..f656f37a93 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 91b0aebca2..004080a542 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 41e0421bc5..0d0d70e405 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 4fcf3fce88..596b2d3bbd 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 8565872262..aae7594b27 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 46a21e7693..9f5a0fdc7f 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index c2bd11cf96..118783f98d 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.0.1-RC + 4.1.0-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 6e112dd98a..088e9ab4a8 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index ffdfc00997..b35a30f1b2 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 963dc710ed..4eba1f5e2f 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 7e0a00eee6..fb3f88fd9e 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard netty-mqtt - 4.0.1-RC + 4.1.0-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 6d7981ccbb..3f8631a17b 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index c4e4c94236..828b33e835 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 9f3dc788b6..0f089553b2 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 2348cdcd34..d943759257 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 008c422dbf..9666fa8059 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 0286c135e4..f9b5d9062f 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index e883486767..058e0e96ab 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 67e5e43b60..03784a855c 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index cc9eb45961..df92bde39d 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index d31e27486e..d27ab91259 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index ea7b65e918..2cfe62ba95 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index da9486c4d0..e561ae65b5 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index f595a8572a..79342e2ea8 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.1-RC + 4.1.0-SNAPSHOT thingsboard org.thingsboard From e4e97350742e7e05e598c6bbe866a315535e60dd Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Tue, 22 Apr 2025 11:23:42 +0300 Subject: [PATCH 064/335] Cleanup deprecated API in SubscriptionManagerService --- .../queue/DefaultTbCoreConsumerService.java | 17 ++++---------- .../DefaultSubscriptionManagerService.java | 22 +++++++------------ .../SubscriptionManagerService.java | 9 -------- 3 files changed, 12 insertions(+), 36 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 47a0c473e9..d2fe8a837d 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -552,19 +552,10 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService keys, TbCallback callback) { - onAttributesDelete(tenantId, entityId, scope, keys, false, callback); - } - - @Override - public void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, TbCallback callback) { - processAttributesUpdate(entityId, scope, - keys.stream().map(key -> new BaseAttributeKvEntry(0, new StringDataEntry(key, ""))).collect(Collectors.toList())); - if (entityId.getEntityType() == EntityType.DEVICE && TbAttributeSubscriptionScope.SHARED_SCOPE.name().equalsIgnoreCase(scope) && notifyDevice) { - clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(tenantId, new DeviceId(entityId.getId()), scope, keys), null); + try { + List deletedEntries = keys.stream() + .map(key -> new BaseAttributeKvEntry(0L, new StringDataEntry(key, ""))) + .toList(); + processAttributesUpdate(entityId, scope, deletedEntries); + } catch (Exception e) { + callback.onFailure(e); + return; } callback.onSuccess(); } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java index 57d4fda5f8..3fd58a1243 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java @@ -41,15 +41,6 @@ public interface SubscriptionManagerService extends ApplicationListener keys, TbCallback empty); - /** - * This method is retained solely for backwards compatibility, specifically to handle - * legacy proto messages that include the notifyDevice field. - * - * @deprecated as of 4.0, this method will be removed in future releases. - */ - @Deprecated(forRemoval = true, since = "4.0") - void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, TbCallback empty); - void onTimeSeriesDelete(TenantId tenantId, EntityId entityId, List keys, TbCallback callback); void onAlarmUpdate(TenantId tenantId, EntityId entityId, AlarmInfo alarm, TbCallback callback); From 2a211bbc4518841f6c8bbab870873a75368be0dd Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 22 Apr 2025 15:58:36 +0300 Subject: [PATCH 065/335] lwm2m: fix_bug 404 in profile on UI --- .../java/org/thingsboard/server/controller/Lwm2mController.java | 2 +- .../org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java | 2 +- .../transport/lwm2m/config/LwM2MTransportBootstrapConfig.java | 2 +- .../transport/lwm2m/config/LwM2MTransportServerConfig.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index 57c2011ba3..014872f568 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -42,7 +42,7 @@ import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CU @Slf4j @RestController -@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && '${transport.lwm2m.enabled:false}'=='true'") +@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") @RequestMapping("/api") public class Lwm2mController extends BaseController { diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java index 4976ccc88b..0c412e86d3 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java @@ -31,7 +31,7 @@ import java.util.Optional; @Slf4j @Service @RequiredArgsConstructor -@ConditionalOnExpression("('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && '${transport.lwm2m.enabled:false}'=='true'") +@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") public class LwM2MServiceImpl implements LwM2MService { private final LwM2MTransportServerConfig serverConfig; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java index 9187e679af..e470cb96ec 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java @@ -29,7 +29,7 @@ import org.thingsboard.server.common.transport.config.ssl.SslCredentialsConfig; @Slf4j @Component -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && '${transport.lwm2m.enabled:false}'=='true' && '${transport.lwm2m.bootstrap.enabled:false}'=='true'") +@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") public class LwM2MTransportBootstrapConfig implements LwM2MSecureServerConfig { @Getter diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java index 088934b586..c1af8f553a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java @@ -34,7 +34,7 @@ import java.util.List; @Slf4j @Component -@ConditionalOnExpression("('${service.type:null}'=='tb-transport' || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core') && '${transport.lwm2m.enabled:false}'=='true'") +@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || '${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") @ConfigurationProperties(prefix = "transport.lwm2m") public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { From ddb9cd20a8cab5ac7021e43ada727305668a443d Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 22 Apr 2025 16:28:56 +0300 Subject: [PATCH 066/335] lwm2m: fix_bug 404: comment1 --- .../org/thingsboard/server/controller/Lwm2mController.java | 4 ++-- .../thingsboard/server/service/lwm2m/LwM2MServiceImpl.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index 014872f568..f0ec727896 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -18,7 +18,6 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -33,6 +32,7 @@ import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MSe import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.config.annotations.ApiOperation; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.lwm2m.LwM2MService; import java.util.Map; @@ -42,7 +42,7 @@ import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CU @Slf4j @RestController -@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") +@TbCoreComponent @RequestMapping("/api") public class Lwm2mController extends BaseController { diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java index 0c412e86d3..ed0fe39032 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java @@ -18,10 +18,10 @@ package org.thingsboard.server.service.lwm2m; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfigDefault; import org.thingsboard.server.common.transport.config.ssl.SslCredentials; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; @@ -31,7 +31,7 @@ import java.util.Optional; @Slf4j @Service @RequiredArgsConstructor -@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") +@TbCoreComponent public class LwM2MServiceImpl implements LwM2MService { private final LwM2MTransportServerConfig serverConfig; From 841d3ea33ff47294ca52ff50bfd334b7fafdc2d1 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 24 Apr 2025 15:56:58 +0300 Subject: [PATCH 067/335] lwm2m: update with master --- .../transport/lwm2m/AbstractLwM2MIntegrationTest.java | 1 + .../data/device/profile/lwm2m/OtherConfiguration.java | 2 +- .../lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java | 8 ++++---- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 0950d66024..827112cf2a 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -383,6 +383,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte transportConfiguration.setBootstrap(bootstrapServerCredentials); return transportConfiguration; } + protected Lwm2mDeviceProfileTransportConfiguration getTransportConfiguration19(String observeAttr, List bootstrapServerCredentials) { Lwm2mDeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration(); TelemetryMappingConfiguration observeAttrConfiguration = JacksonUtil.fromString(observeAttr, TelemetryMappingConfiguration.class); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java index 0d59ef6f8f..b056309634 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/OtherConfiguration.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; @JsonIgnoreProperties(ignoreUnknown = true) public class OtherConfiguration extends PowerSavingConfiguration { - private Boolean useObject19ForOta; + private Boolean useObject19ForOtaInfo; private Integer fwUpdateStrategy; private Integer swUpdateStrategy; private Integer clientOnlyObserveAfterConnect; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index da94c19422..ea8c2acb14 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -528,8 +528,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl } else { strategy = info.getDeliveryMethod() == FirmwareDeliveryMethod.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; } - Boolean useObject19ForOta = clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOta(); - if (useObject19ForOta != null && useObject19ForOta){ + Boolean useObject19ForOtaInfo = clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOtaInfo(); + if (useObject19ForOtaInfo != null && useObject19ForOtaInfo){ sendInfoToObject19ForOta(client, FW_INFO_19_INSTANCE_ID, response, otaPackageId); } switch (strategy) { @@ -554,8 +554,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); LwM2MSoftwareUpdateStrategy strategy = info.getStrategy(); - Boolean useObject19ForOta = clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOta(); - if (useObject19ForOta != null && useObject19ForOta){ + Boolean useObject19ForOtaInfo = clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOtaInfo(); + if (useObject19ForOtaInfo != null && useObject19ForOtaInfo){ sendInfoToObject19ForOta(client, SW_INFO_19_INSTANCE_ID, response, otaPackageId); } switch (strategy) { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index a7f231bdb2..b160a44b0e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2175,8 +2175,8 @@ "no-config-servers": "No servers configured", "others-tab": "Other settings", "ota-update": "OTA update", - "use-object-19-for-ota-update": "Use Object 19 for OTA update", - "use-object-19-for-ota-update-hint": "Use Resource ObjectId = 19 for OTA updates: FirmWare → InstanceId = 65534, SoftWare → InstanceId = 65535. The data format is JSON wrapped in Base64. The main field in JSON: \"Checksum\" (SHA256). Additional fields: \"Title\" (OTA name), \"Version\" (OTA version), \"File Name\" (file name for storing OTA on the client), \"File Size\" (OTA size in bytes).", + "use-object-19-for-ota-update": "Use Object 19 for OTA file metadata (checksum, size, version, name)", + "use-object-19-for-ota-update-hint": "Use Resource ObjectId = 19 for OTA updates: FirmWare → InstanceId = 65534, SoftWare → InstanceId = 65535. The data format is JSON wrapped in Base64. This JSON contains OTA file metadata (file info): \"Checksum\" (SHA256). Additional fields: \"Title\" (OTA name), \"Version\" (OTA version), \"File Name\" (file name for storing OTA on the client), \"File Size\" (OTA size in bytes).", "client-strategy": "Client strategy when connecting", "client-strategy-label": "Strategy", "client-strategy-only-observe": "Only Observe Request to the client after the initial connection", From edd7d6392a4fe740f074520a478a52dfe150f408 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 24 Apr 2025 16:52:31 +0300 Subject: [PATCH 068/335] Improvement after review --- .../src/main/resources/thingsboard.yml | 7 ++-- .../server/queue/kafka/TbKafkaSettings.java | 32 +++++++++---------- .../server/queue/util/PropertyUtils.java | 17 ++++++++++ .../queue/kafka/TbKafkaSettingsTest.java | 20 +++++++++++- 4 files changed, 55 insertions(+), 21 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ebd001aa97..e013473f98 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1630,9 +1630,10 @@ queue: value: "${TB_QUEUE_KAFKA_EDQS_STATE_MAX_POLL_RECORDS:512}" # If you override any default Kafka topic name using environment variables, you must also specify the related consumer properties # for the new topic in `consumer-properties-per-topic-inline`. Otherwise, the topic will not inherit its expected configuration (e.g., max.poll.records, timeouts, etc). - # Format: "topic1:key1=value1,key2=value2;topic2:key=value" - # Example: "tb_core_modified.notifications:max.poll.records=10;tb_edge_modified:max.poll.records=10,enable.auto.commit=true" - consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" + # Each entry sets a single property for a specific topic. To define multiple properties for a topic, repeat the topic key. + # Format: "topic1:key=value;topic1:key=value;topic2:key=value" + # Example: tb_core_updated:max.poll.records=10;tb_core_updated:bootstrap.servers=kafka1:9092,kafka2:9092;tb_edge_updated:auto.offset.reset=latest + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:tb_core_updated:max.poll.records=10;tb_core_updated:enable.auto.commit=true;tb_core_updated:bootstrap.servers=kafka1:9092,kafka2:9092;tb_edge_updated:max.poll.records=5;tb_edge_updated:auto.offset.reset=latest}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section, you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java index e0682f6364..06ccfe3a69 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java @@ -37,9 +37,8 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.TbProperty; import org.thingsboard.server.queue.util.PropertyUtils; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; @@ -149,7 +148,7 @@ public class TbKafkaSettings { private List other; @Setter - private Map> consumerPropertiesPerTopic = Collections.emptyMap(); + private Map> consumerPropertiesPerTopic = new HashMap<>(); private volatile AdminClient adminClient; @@ -260,23 +259,22 @@ public class TbKafkaSettings { } private Map> parseTopicPropertyList(String inlineProperties) { + Map> grouped = PropertyUtils.getGroupedProps(inlineProperties); Map> result = new HashMap<>(); - Map rawTopicToPropertyString = PropertyUtils.getProps(inlineProperties); - for (Map.Entry entry : rawTopicToPropertyString.entrySet()) { - String topic = entry.getKey().trim(); - String propertiesStr = entry.getValue(); - - List tbProperties = Arrays.stream(propertiesStr.split(",")) - .map(kv -> kv.split("=", 2)) - .filter(kvArr -> kvArr.length == 2) - .map(kvArr -> new TbProperty(kvArr[0].trim(), kvArr[1].trim())) - .toList(); - - if (!tbProperties.isEmpty()) { - result.put(topic, tbProperties); + grouped.forEach((topic, entries) -> { + Map merged = new LinkedHashMap<>(); + for (String entry : entries) { + String[] kv = entry.split("=", 2); + if (kv.length == 2) { + merged.put(kv[0].trim(), kv[1].trim()); + } } - } + List props = merged.entrySet().stream() + .map(e -> new TbProperty(e.getKey(), e.getValue())) + .toList(); + result.put(topic, props); + }); return result; } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/PropertyUtils.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/PropertyUtils.java index 6030eb278d..629ee29f6f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/util/PropertyUtils.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/PropertyUtils.java @@ -17,7 +17,9 @@ package org.thingsboard.server.queue.util; import org.thingsboard.server.common.data.StringUtils; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Function; @@ -38,6 +40,21 @@ public class PropertyUtils { return configs; } + public static Map> getGroupedProps(String properties) { + Map> configs = new HashMap<>(); + if (StringUtils.isNotEmpty(properties)) { + for (String property : properties.split(";")) { + if (StringUtils.isNotEmpty(property)) { + int delimiterPosition = property.indexOf(":"); + String topic = property.substring(0, delimiterPosition).trim(); + String value = property.substring(delimiterPosition + 1).trim(); + configs.computeIfAbsent(topic, k -> new ArrayList<>()).add(value); + } + } + } + return configs; + } + public static Map getProps(Map defaultProperties, String propertiesStr) { return getProps(defaultProperties, propertiesStr, PropertyUtils::getProps); } diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaSettingsTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaSettingsTest.java index 5cdebc3996..ad026c63aa 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaSettingsTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaSettingsTest.java @@ -33,6 +33,12 @@ import static org.mockito.Mockito.spy; "queue.type=kafka", "queue.kafka.bootstrap.servers=localhost:9092", "queue.kafka.other-inline=metrics.recording.level:INFO;metrics.sample.window.ms:30000", + "queue.kafka.consumer-properties-per-topic-inline=" + + "tb_core_updated:max.poll.records=10;" + + "tb_core_updated:enable.auto.commit=true;" + + "tb_core_updated:bootstrap.servers=kafka1:9092,kafka2:9092;" + + "tb_edge_updated:max.poll.records=5;" + + "tb_edge_updated:auto.offset.reset=latest" }) class TbKafkaSettingsTest { @@ -79,4 +85,16 @@ class TbKafkaSettingsTest { Mockito.verify(settings).configureSSL(any()); } -} \ No newline at end of file + @Test + void givenMultipleTopicsInInlineConfig_whenParsed_thenEachTopicGetsExpectedProperties() { + Properties coreProps = settings.toConsumerProps("tb_core_updated"); + assertThat(coreProps.getProperty("max.poll.records")).isEqualTo("10"); + assertThat(coreProps.getProperty("enable.auto.commit")).isEqualTo("true"); + assertThat(coreProps.getProperty("bootstrap.servers")).isEqualTo("kafka1:9092,kafka2:9092"); + + Properties edgeProps = settings.toConsumerProps("tb_edge_updated"); + assertThat(edgeProps.getProperty("max.poll.records")).isEqualTo("5"); + assertThat(edgeProps.getProperty("auto.offset.reset")).isEqualTo("latest"); + } + +} From b516583c6dbcf75c0cc0afcb390d7316fdf8576b Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 24 Apr 2025 16:53:57 +0300 Subject: [PATCH 069/335] Remove env setup config --- application/src/main/resources/thingsboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index e013473f98..9710f38a3a 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1633,7 +1633,7 @@ queue: # Each entry sets a single property for a specific topic. To define multiple properties for a topic, repeat the topic key. # Format: "topic1:key=value;topic1:key=value;topic2:key=value" # Example: tb_core_updated:max.poll.records=10;tb_core_updated:bootstrap.servers=kafka1:9092,kafka2:9092;tb_edge_updated:auto.offset.reset=latest - consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:tb_core_updated:max.poll.records=10;tb_core_updated:enable.auto.commit=true;tb_core_updated:bootstrap.servers=kafka1:9092,kafka2:9092;tb_edge_updated:max.poll.records=5;tb_edge_updated:auto.offset.reset=latest}" + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section, you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms From 53e8c37646cb0fd0a51bb9daa4c9a3968a5a5ca8 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 24 Apr 2025 17:21:23 +0300 Subject: [PATCH 070/335] lwm2m: fix buf in the front: useObject19ForOta to useObject19ForOtaInfo --- .../transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java | 2 +- ...wm2m-device-profile-transport-configuration.component.html | 2 +- .../lwm2m-device-profile-transport-configuration.component.ts | 4 ++-- .../profile/device/lwm2m/lwm2m-profile-config.models.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java index eb87a9bc3b..e580827d51 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java @@ -120,7 +120,7 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg public static final String CLIENT_LWM2M_SETTINGS_19 = " {\n" + - " \"useObject19ForOta\": true,\n" + + " \"useObject19ForOtaInfo\": true,\n" + " \"edrxCycle\": null,\n" + " \"powerMode\": \"DRX\",\n" + " \"fwUpdateResource\": null,\n" + diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index bb37b5784a..f4dbafb6e8 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -53,7 +53,7 @@
device-profile.lwm2m.ota-update - + {{ 'device-profile.lwm2m.use-object-19-for-ota-update' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 46c439cf33..f7885aa9e5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -104,7 +104,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrap: [[]], clientLwM2mSettings: this.fb.group({ clientOnlyObserveAfterConnect: [1, []], - useObject19ForOta: [false], + useObject19ForOtaInfo: [false], fwUpdateStrategy: [1, []], swUpdateStrategy: [1, []], fwUpdateResource: [{value: '', disabled: true}, []], @@ -263,7 +263,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServerUpdateEnable: this.configurationValue.bootstrapServerUpdateEnable || false, clientLwM2mSettings: { clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, - useObject19ForOta: this.configurationValue.clientLwM2mSettings.useObject19ForOta ?? false, + useObject19ForOtaInfo: this.configurationValue.clientLwM2mSettings.useObject19ForOtaInfo ?? false, fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, fwUpdateResource: this.configurationValue.clientLwM2mSettings.fwUpdateResource || '', diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 33b358e581..3972964a5b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -169,7 +169,7 @@ export interface Lwm2mProfileConfigModels { export interface ClientLwM2mSettings { clientOnlyObserveAfterConnect: number; - useObject19ForOta?: boolean; + useObject19ForOtaInfo?: boolean; fwUpdateStrategy: number; swUpdateStrategy: number; fwUpdateResource?: string; From 876237fede7d12ac1fc9c6cf538576f086a239a5 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Fri, 25 Apr 2025 14:40:16 +0300 Subject: [PATCH 071/335] Send the `ALARM_DELETE` event only after the alarm is successfully deleted --- .../server/controller/AlarmController.java | 2 +- .../entitiy/AbstractTbEntityService.java | 14 -- .../entitiy/alarm/DefaultTbAlarmService.java | 23 ++- .../service/entitiy/alarm/TbAlarmService.java | 3 +- .../DefaultAlarmSubscriptionService.java | 2 +- .../controller/AlarmControllerTest.java | 30 +++- .../alarm/DefaultTbAlarmServiceTest.java | 163 ++++++++++++------ .../engine/api/RuleEngineAlarmService.java | 3 +- 8 files changed, 154 insertions(+), 86 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index a83e9e446e..dd8d883145 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -157,7 +157,7 @@ public class AlarmController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE) @ResponseBody - public Boolean deleteAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { + public boolean deleteAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); Alarm alarm = checkAlarmId(alarmId, Operation.DELETE); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index e9ee7202a7..476a4ef5ca 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -17,10 +17,8 @@ package org.thingsboard.server.service.entitiy; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; import org.thingsboard.server.cluster.TbClusterService; @@ -31,16 +29,10 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.dao.alarm.AlarmService; -import org.thingsboard.server.dao.asset.AssetProfileService; -import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; -import org.thingsboard.server.dao.device.DeviceProfileService; -import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; @@ -55,12 +47,6 @@ public abstract class AbstractTbEntityService { @Autowired private Environment env; - @Value("${server.log_controller_error_stack_trace}") - @Getter - private boolean logControllerErrorStackTrace; - - @Autowired - protected DbCallbackExecutorService dbExecutor; @Autowired(required = false) protected TbLogEntityActionService logEntityActionService; @Autowired(required = false) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index 4766d8e039..5a1dee3bb5 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -190,11 +190,24 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public Boolean delete(Alarm alarm, User user) { - TenantId tenantId = alarm.getTenantId(); - logEntityActionService.logEntityAction(tenantId, alarm.getOriginator(), alarm, alarm.getCustomerId(), - ActionType.ALARM_DELETE, user, alarm.getId()); - return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); + public boolean delete(Alarm alarm, User user) { + var tenantId = alarm.getTenantId(); + var alarmId = alarm.getId(); + var alarmOriginator = alarm.getOriginator(); + + boolean deleted; + try { + deleted = alarmSubscriptionService.deleteAlarm(tenantId, alarmId); + } catch (Exception e) { + logEntityActionService.logEntityAction(tenantId, emptyId(alarmOriginator.getEntityType()), ActionType.ALARM_DELETE, user, e, alarmId); + throw e; + } + + if (deleted) { + logEntityActionService.logEntityAction(tenantId, alarmOriginator, alarm, alarm.getCustomerId(), ActionType.ALARM_DELETE, user, alarmId); + } + + return deleted; } private static long getOrDefault(long ts) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java index 11b5c864ac..c034c39561 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java @@ -43,5 +43,6 @@ public interface TbAlarmService { void unassignDeletedUserAlarms(TenantId tenantId, UserId userId, String userTitle, List alarms, long unassignTs); - Boolean delete(Alarm alarm, User user); + boolean delete(Alarm alarm, User user); + } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index 7a5a563e68..fd9d8b7141 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -115,7 +115,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService } @Override - public Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId) { + public boolean deleteAlarm(TenantId tenantId, AlarmId alarmId) { AlarmApiCallResult result = alarmService.delAlarm(tenantId, alarmId); onAlarmDeleted(result); return result.isSuccessful(); diff --git a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java index d80c8a6ba5..8a49d34d20 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; @@ -57,6 +58,8 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.verifyNoInteractions; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @Slf4j @@ -308,6 +311,21 @@ public class AlarmControllerTest extends AbstractControllerTest { testNotifyEntityNever(alarm.getId(), alarm); } + @Test + public void testDeleteNonExistentAlarm() throws Exception { + loginTenantAdmin(); + + var nonExistentAlarmId = Uuids.timeBased(); + + Mockito.reset(tbClusterService, auditLogService); + + doDelete("/api/alarm/" + nonExistentAlarmId) + .andExpect(status().isNotFound()) + .andExpect(statusReason(is("Alarm with id [" + nonExistentAlarmId + "] is not found"))); + + verifyNoInteractions(tbClusterService, auditLogService); + } + @Test public void testClearAlarmViaCustomer() throws Exception { loginCustomerUser(); @@ -634,12 +652,12 @@ public class AlarmControllerTest extends AbstractControllerTest { doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk()); - Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { - AlarmInfo alarmInfo = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); - Assert.assertNotNull(alarmInfo); - Assert.assertNull(alarmInfo.getAssigneeId()); - Assert.assertTrue(alarmInfo.getAssignTs() >= afterAssignmentTs); - }); + Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + AlarmInfo alarmInfo = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); + Assert.assertNotNull(alarmInfo); + Assert.assertNull(alarmInfo.getAssigneeId()); + Assert.assertTrue(alarmInfo.getAssignTs() >= afterAssignmentTs); + }); } @Test diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java index ac85b48bc1..670ab390ac 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java @@ -15,15 +15,12 @@ */ package org.thingsboard.server.service.entitiy.alarm; +import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.extern.slf4j.Slf4j; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.User; @@ -35,6 +32,9 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.dao.alarm.AlarmService; @@ -47,7 +47,6 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.service.entitiy.TbLogEntityActionService; -import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; @@ -55,58 +54,57 @@ import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; import java.util.List; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -@Slf4j -@RunWith(SpringRunner.class) -@ContextConfiguration(classes = DefaultTbAlarmService.class) -@TestPropertySource(properties = { - "server.log_controller_error_stack_trace=false" -}) -public class DefaultTbAlarmServiceTest { +@SpringJUnitConfig(DefaultTbAlarmService.class) +class DefaultTbAlarmServiceTest { @MockBean - protected DbCallbackExecutorService dbExecutor; + TbLogEntityActionService logEntityActionService; @MockBean - protected TbLogEntityActionService logEntityActionService; + EdgeService edgeService; @MockBean - protected EdgeService edgeService; + AlarmService alarmService; @MockBean - protected AlarmService alarmService; + TbAlarmCommentService alarmCommentService; @MockBean - protected TbAlarmCommentService alarmCommentService; + AlarmSubscriptionService alarmSubscriptionService; @MockBean - protected AlarmSubscriptionService alarmSubscriptionService; + CustomerService customerService; @MockBean - protected CustomerService customerService; + TbClusterService tbClusterService; @MockBean - protected TbClusterService tbClusterService; + EntitiesVersionControlService vcService; @MockBean - private EntitiesVersionControlService vcService; + AccessControlService accessControlService; @MockBean - private AccessControlService accessControlService; + TenantService tenantService; @MockBean - private TenantService tenantService; + AssetService assetService; @MockBean - private AssetService assetService; + DeviceService deviceService; @MockBean - private DeviceService deviceService; + AssetProfileService assetProfileService; @MockBean - private AssetProfileService assetProfileService; + DeviceProfileService deviceProfileService; @MockBean - private DeviceProfileService deviceProfileService; - @MockBean - private EntityService entityService; - @SpyBean + EntityService entityService; + + @Autowired DefaultTbAlarmService service; + TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); + CustomerId customerId = new CustomerId(Uuids.timeBased()); + @Test - public void testSave() throws ThingsboardException { + void testSave() throws ThingsboardException { var alarm = new AlarmInfo(); when(alarmSubscriptionService.createAlarm(any())).thenReturn(AlarmApiCallResult.builder() .successful(true) @@ -115,45 +113,99 @@ public class DefaultTbAlarmServiceTest { .build()); service.save(alarm, new User()); - verify(logEntityActionService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.ADDED), any()); - verify(alarmSubscriptionService, times(1)).createAlarm(any()); + verify(logEntityActionService).logEntityAction(any(), any(), any(), any(), eq(ActionType.ADDED), any()); + verify(alarmSubscriptionService).createAlarm(any()); } @Test - public void testAck() throws ThingsboardException { + void testAck() throws ThingsboardException { var alarm = new Alarm(); when(alarmSubscriptionService.acknowledgeAlarm(any(), any(), anyLong())) .thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).alarm(new AlarmInfo()).build()); service.ack(alarm, new User(new UserId(UUID.randomUUID()))); - verify(alarmCommentService, times(1)).saveAlarmComment(any(), any(), any()); - verify(logEntityActionService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.ALARM_ACK), any()); - verify(alarmSubscriptionService, times(1)).acknowledgeAlarm(any(), any(), anyLong()); + verify(alarmCommentService).saveAlarmComment(any(), any(), any()); + verify(logEntityActionService).logEntityAction(any(), any(), any(), any(), eq(ActionType.ALARM_ACK), any()); + verify(alarmSubscriptionService).acknowledgeAlarm(any(), any(), anyLong()); } @Test - public void testClear() throws ThingsboardException { + void testClear() throws ThingsboardException { var alarm = new Alarm(); alarm.setAcknowledged(true); when(alarmSubscriptionService.clearAlarm(any(), any(), anyLong(), any())) .thenReturn(AlarmApiCallResult.builder().successful(true).cleared(true).alarm(new AlarmInfo()).build()); service.clear(alarm, new User(new UserId(UUID.randomUUID()))); - verify(alarmCommentService, times(1)).saveAlarmComment(any(), any(), any()); - verify(logEntityActionService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.ALARM_CLEAR), any()); - verify(alarmSubscriptionService, times(1)).clearAlarm(any(), any(), anyLong(), any()); + verify(alarmCommentService).saveAlarmComment(any(), any(), any()); + verify(logEntityActionService).logEntityAction(any(), any(), any(), any(), eq(ActionType.ALARM_CLEAR), any()); + verify(alarmSubscriptionService).clearAlarm(any(), any(), anyLong(), any()); } @Test - public void testDelete() { - service.delete(new Alarm(), new User()); + void testDelete_deleteApiReturnsTrue_shouldLogActionAndReturnTrue() { + // GIVEN + var alarmOriginator = new DeviceId(Uuids.timeBased()); + + var alarm = new Alarm(new AlarmId(Uuids.timeBased())); + alarm.setTenantId(tenantId); + alarm.setCustomerId(customerId); + alarm.setOriginator(alarmOriginator); + + var user = new User(); + + when(alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId())).thenReturn(true); - verify(logEntityActionService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.ALARM_DELETE), any(), any()); - verify(alarmSubscriptionService, times(1)).deleteAlarm(any(), any()); + // WHEN + boolean actual = service.delete(alarm, user); + + assertThat(actual).isTrue(); + verify(logEntityActionService).logEntityAction(tenantId, alarmOriginator, alarm, alarm.getCustomerId(), ActionType.ALARM_DELETE, user, alarm.getId()); + verify(alarmSubscriptionService).deleteAlarm(tenantId, alarm.getId()); } @Test - public void testUnassignAlarm() throws ThingsboardException { + void testDelete_deleteApiReturnsFalse_shouldNotLogActionAndReturnFalse() { + // GIVEN + var alarm = new Alarm(new AlarmId(Uuids.timeBased())); + alarm.setTenantId(tenantId); + + var user = new User(); + + // WHEN + boolean actual = service.delete(alarm, user); + + assertThat(actual).isFalse(); + verifyNoInteractions(logEntityActionService); + verify(alarmSubscriptionService).deleteAlarm(tenantId, alarm.getId()); + } + + @Test + void testDelete_deleteApiThrowsException_shouldLogFailedActionAndRethrow() { + // GIVEN + var alarmOriginator = new DeviceId(Uuids.timeBased()); + + var alarm = new Alarm(new AlarmId(Uuids.timeBased())); + alarm.setTenantId(tenantId); + alarm.setOriginator(alarmOriginator); + + var user = new User(); + + var exception = new RuntimeException("failed to delete alarm"); + + when(alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId())).thenThrow(exception); + + // WHEN-THEN + assertThatThrownBy(() -> service.delete(alarm, user)) + .isInstanceOf(RuntimeException.class) + .hasMessage("failed to delete alarm"); + + verify(logEntityActionService).logEntityAction(tenantId, new DeviceId(EntityId.NULL_UUID), ActionType.ALARM_DELETE, user, exception, alarm.getId()); + verify(alarmSubscriptionService).deleteAlarm(tenantId, alarm.getId()); + } + + @Test + void testUnassignAlarm() throws ThingsboardException { AlarmInfo alarm = new AlarmInfo(); alarm.setId(new AlarmId(UUID.randomUUID())); when(alarmSubscriptionService.unassignAlarm(any(), any(), anyLong())) @@ -174,12 +226,11 @@ public class DefaultTbAlarmServiceTest { .comment(commentNode) .build(); - verify(alarmCommentService, times(1)) - .saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(user)); + verify(alarmCommentService).saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(user)); } @Test - public void testUnassignDeletedUserAlarms() throws ThingsboardException { + void testUnassignDeletedUserAlarms() throws ThingsboardException { AlarmInfo alarm = new AlarmInfo(); alarm.setId(new AlarmId(UUID.randomUUID())); @@ -189,7 +240,7 @@ public class DefaultTbAlarmServiceTest { User user = new User(); user.setEmail("testEmail@gmail.com"); user.setId(new UserId(UUID.randomUUID())); - service.unassignDeletedUserAlarms(new TenantId(UUID.randomUUID()), user.getId(), user.getTitle(), List.of(alarm.getUuidId()), System.currentTimeMillis()); + service.unassignDeletedUserAlarms(tenantId, user.getId(), user.getTitle(), List.of(alarm.getUuidId()), System.currentTimeMillis()); ObjectNode commentNode = JacksonUtil.newObjectNode(); commentNode.put("subtype", "ASSIGN"); @@ -200,9 +251,7 @@ public class DefaultTbAlarmServiceTest { .comment(commentNode) .build(); - verify(alarmCommentService, times(1)) - .saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(null)); + verify(alarmCommentService).saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(null)); } - } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java index 20f2342ebe..48fda3b781 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java @@ -70,7 +70,7 @@ public interface RuleEngineAlarmService { AlarmApiCallResult unassignAlarm(TenantId tenantId, AlarmId alarmId, long assignTs); // Other API - Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId); + boolean deleteAlarm(TenantId tenantId, AlarmId alarmId); ListenableFuture findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId); @@ -99,4 +99,5 @@ public interface RuleEngineAlarmService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink); + } From dd493d1f52d4b1f254e947490bb6c297e689919b Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 25 Apr 2025 15:32:03 +0300 Subject: [PATCH 072/335] added tests --- .../cf/ctx/state/CalculatedFieldCtx.java | 4 +- .../ctx/state/SimpleCalculatedFieldState.java | 6 +- .../cf/CalculatedFieldIntegrationTest.java | 82 +++++++++++++++++++ .../SimpleCalculatedFieldConfiguration.java | 2 +- .../script/api/tbel/TbelCfCtx.java | 4 +- 5 files changed, 90 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java index 25083ed415..a3fdae319d 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java @@ -62,7 +62,7 @@ public class CalculatedFieldCtx { private final List argNames; private Output output; private String expression; - private boolean preserveLatestTs; + private boolean preserveMsgTs; private TbelInvokeService tbelInvokeService; private CalculatedFieldScriptEngine calculatedFieldScriptEngine; private ThreadLocal customExpression; @@ -96,7 +96,7 @@ public class CalculatedFieldCtx { this.argNames = new ArrayList<>(arguments.keySet()); this.output = configuration.getOutput(); this.expression = configuration.getExpression(); - this.preserveLatestTs = CalculatedFieldType.SIMPLE.equals(calculatedField.getType()) && ((SimpleCalculatedFieldConfiguration) configuration).isPreserveLastUpdateTs(); + this.preserveMsgTs = CalculatedFieldType.SIMPLE.equals(calculatedField.getType()) && ((SimpleCalculatedFieldConfiguration) configuration).isPreserveMsgTs(); this.tbelInvokeService = tbelInvokeService; this.maxDataPointsPerRollingArg = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxDataPointsPerRollingArg); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java index 2ad961f72c..01091d2999 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java @@ -68,7 +68,7 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { Output output = ctx.getOutput(); Object result = formatResult(expressionResult, output.getDecimalsByDefault()); - JsonNode outputResult = createResultJson(ctx.isPreserveLatestTs(), output.getName(), result); + JsonNode outputResult = createResultJson(ctx.isPreserveMsgTs(), output.getName(), result); return Futures.immediateFuture(new CalculatedFieldResult(output.getType(), output.getScope(), outputResult)); } @@ -82,12 +82,12 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { : TbUtils.toFixed(expressionResult, decimals); } - private JsonNode createResultJson(boolean preserveLatestTs, String outputName, Object result) { + private JsonNode createResultJson(boolean preserveMsgTs, String outputName, Object result) { ObjectNode valuesNode = JacksonUtil.newObjectNode(); valuesNode.set(outputName, JacksonUtil.valueToTree(result)); long lastTimestamp = getLastUpdateTimestamp(); - if (preserveLatestTs && lastTimestamp != -1) { + if (preserveMsgTs && lastTimestamp != -1) { ObjectNode resultNode = JacksonUtil.newObjectNode(); resultNode.put("ts", lastTimestamp); resultNode.set("values", valuesNode); diff --git a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java index 002f668fdc..f65f6bc629 100644 --- a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; +import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration; import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.id.AssetProfileId; @@ -462,6 +463,87 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes }); } + @Test + public void testSimpleCalculatedFieldWhenPreserveMsgTsIsTrue() throws Exception { + Device testDevice = createDevice("Test device", "1234567890"); + long ts = System.currentTimeMillis() - 300000L; + doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"temperature\":30}}", ts))); + + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setEntityId(testDevice.getId()); + calculatedField.setType(CalculatedFieldType.SIMPLE); + calculatedField.setName("C to F"); + calculatedField.setDebugSettings(DebugSettings.all()); + calculatedField.setConfigurationVersion(1); + + SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); + + Argument argument = new Argument(); + ReferencedEntityKey refEntityKey = new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null); + argument.setRefEntityKey(refEntityKey); + config.setArguments(Map.of("T", argument)); + config.setExpression("(T * 9/5) + 32"); + + Output output = new Output(); + output.setName("fahrenheitTemp"); + output.setType(OutputType.TIME_SERIES); + config.setOutput(output); + + config.setPreserveMsgTs(true); + + calculatedField.setConfiguration(config); + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); + + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("ts").asText()).isEqualTo(Long.toString(ts)); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("86.0"); + }); + } + + @Test + public void testScriptCalculatedFieldWhenUsedMsgTsInScript() throws Exception { + Device testDevice = createDevice("Test device", "1234567890"); + long ts = System.currentTimeMillis() - 300000L; + doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"temperature\":30}}", ts))); + + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setEntityId(testDevice.getId()); + calculatedField.setType(CalculatedFieldType.SCRIPT); + calculatedField.setName("C to F"); + calculatedField.setDebugSettings(DebugSettings.all()); + calculatedField.setConfigurationVersion(1); + + ScriptCalculatedFieldConfiguration config = new ScriptCalculatedFieldConfiguration(); + + Argument argument = new Argument(); + ReferencedEntityKey refEntityKey = new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null); + argument.setRefEntityKey(refEntityKey); + config.setArguments(Map.of("T", argument)); + config.setExpression("return {\"ts\": ctx.msgTs, \"values\": {\"fahrenheitTemp\": (T * 1.8) + 32}};"); + + Output output = new Output(); + output.setType(OutputType.TIME_SERIES); + config.setOutput(output); + + calculatedField.setConfiguration(config); + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); + + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); + assertThat(fahrenheitTemp).isNotNull(); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("ts").asText()).isEqualTo(Long.toString(ts)); + assertThat(fahrenheitTemp.get("fahrenheitTemp").get(0).get("value").asText()).isEqualTo("86.0"); + }); + } + private ObjectNode getLatestTelemetry(EntityId entityId, String... keys) throws Exception { return doGetAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() + "/values/timeseries?keys=" + String.join(",", keys), ObjectNode.class); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java index c748afdac4..af3cb4d5cd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; @EqualsAndHashCode(callSuper = true) public class SimpleCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration { - private boolean preserveLastUpdateTs; + private boolean preserveMsgTs; @Override public CalculatedFieldType getType() { diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java index 1a8610cbab..7515cb5269 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java @@ -25,11 +25,11 @@ public class TbelCfCtx implements TbelCfObject { @Getter private final Map args; @Getter - private final long lastTs; + private final long msgTs; public TbelCfCtx(Map args, long lastUpdateTs) { this.args = Collections.unmodifiableMap(args); - this.lastTs = lastUpdateTs != -1 ? lastUpdateTs : System.currentTimeMillis(); + this.msgTs = lastUpdateTs != -1 ? lastUpdateTs : System.currentTimeMillis(); } @Override From 34592ef4b428d11dc7240354e5fabb20cb4af604 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 25 Apr 2025 16:49:29 +0300 Subject: [PATCH 073/335] added description --- .../ctx/state/BaseCalculatedFieldState.java | 6 +- .../en_US/calculated-field/expression_fn.md | 127 +++++++++++++----- 2 files changed, 96 insertions(+), 37 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java index 84c61661ae..35879ac9aa 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java @@ -111,13 +111,11 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { private void updateLastUpdateTimestamp(ArgumentEntry entry) { if (entry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { - long ts = singleValueArgumentEntry.getTs(); - this.lastUpdateTimestamp = Math.max(this.lastUpdateTimestamp, ts); + this.lastUpdateTimestamp = singleValueArgumentEntry.getTs(); } else if (entry instanceof TsRollingArgumentEntry tsRollingArgumentEntry) { Map.Entry lastEntry = tsRollingArgumentEntry.getTsRecords().pollLastEntry(); if (lastEntry != null) { - long ts = lastEntry.getKey(); - this.lastUpdateTimestamp = Math.max(this.lastUpdateTimestamp, ts); + this.lastUpdateTimestamp = lastEntry.getKey(); } } } diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md index 373c50e2c6..9a988df042 100644 --- a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md +++ b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md @@ -1,7 +1,7 @@ ## Calculated Field TBEL Script Function The **calculate()** function is a user-defined script that enables custom calculations using [TBEL](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) on telemetry and attribute data. -It receives arguments configured in the calculated field setup, along with an additional `ctx` object that provides access to all arguments. +It receives arguments configured in the calculated field setup, along with an additional `ctx` object that stores `msgTs` and provides access to all arguments. ### Function Signature @@ -44,7 +44,7 @@ Let's modify the function that converts Fahrenheit to Celsius to also return the var temperatureC = (temperatureF - 32) / 1.8; return { "ts": ctx.args.temperatureF.ts, - "values": { "temperatureC": toFixed(temperatureC, 2) } + "values": {"temperatureC": toFixed(temperatureC, 2)} }; ``` @@ -60,10 +60,22 @@ These contain time series data within a defined time window. Example format: "endTs": 1740644662896 }, "values": [ - { "ts": 1740644350000, "value": 72.32 }, - { "ts": 1740644360000, "value": 72.86 }, - { "ts": 1740644370000, "value": 73.58 }, - { "ts": 1740644380000, "value": "NaN" } + { + "ts": 1740644350000, + "value": 72.32 + }, + { + "ts": 1740644360000, + "value": 72.86 + }, + { + "ts": 1740644370000, + "value": 73.58 + }, + { + "ts": 1740644380000, + "value": "NaN" + } ] } } @@ -81,14 +93,18 @@ var firstItemTs = firstItem.ts; var firstItemValue = firstItem.value; var sum = 0.0; // iterate through all values and calculate the sum using foreach: -foreach(t: temperature) { - if(!isNaN(t.value)) { // check that the value is a valid number; +foreach(t +: +temperature +) +{ + if (!isNaN(t.value)) { // check that the value is a valid number; sum += t.value; } } // iterate through all values and calculate the sum using for loop: sum = 0.0; -for(var i = 0; i < temperature.values.size; i++) { +for (var i = 0; i < temperature.values.size; i++) { sum += temperature.values[i].value; } // use built-in function to calculate the sum @@ -146,12 +162,13 @@ function calculate(ctx, altitude, temperature) { Time series rolling arguments can be **merged** to align timestamps across multiple datasets. -| Method | Description | Returns | Example | -|:-----------------------------|:--------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `merge(other, settings)` | Merges with another rolling argument. Aligns timestamps and filling missing values with the previous available value. | Merged object with `timeWindow` and aligned values. |

| -| `mergeAll(others, settings)` | Merges multiple rolling arguments. Aligns timestamps and filling missing values with the previous available value. | Merged object with `timeWindow` and aligned values. |

| +| Method | Description | Returns | Example | +|:-----------------------------|:----------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `merge(other, settings)` | Merges with another rolling argument. Aligns timestamps and filling missing values with the previous available value. | Merged object with `timeWindow` and aligned values. |

| +| `mergeAll(others, settings)` | Merges multiple rolling arguments. Aligns timestamps and filling missing values with the previous available value. | Merged object with `timeWindow` and aligned values. |

| ##### Parameters + | Parameter | Description | |:---------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `other` or `others` | Another rolling argument or array of rolling arguments to merge with. | @@ -166,7 +183,11 @@ function calculate(ctx, temperature, defrost) { var merged = temperature.merge(defrost); var result = []; - foreach(item: merged) { + foreach(item +: + merged +) + { if (item.v1 > -5.0 && item.v2 == 0) { result.add({ ts: item.ts, @@ -187,29 +208,51 @@ function calculate(ctx, temperature, defrost) { The result is a list of issues that may be used to configure alarm rules: ```json -[{ +[ + { "ts": 1741613833843, "values": { - "issue": { - "temperature": -3.12, - "defrostState": false - } + "issue": { + "temperature": -3.12, + "defrostState": false + } } -}, { + }, + { "ts": 1741613923848, "values": { - "issue": { - "temperature": -4.16, - "defrostState": false - } + "issue": { + "temperature": -4.16, + "defrostState": false + } } -}] + } +] ``` ### Function return format The return format depends on the output type configured in the calculated field settings (default: **Time Series**). +### Message timestamp + +The `ctx` object also includes property `msgTs`, which represents the timestamp of the incoming telemetry message that triggered the calculated field execution in milliseconds. + +You can use `ctx.msgTs` to set the timestamp of the resulting output explicitly when returning a time series object. + +```javascript +var temperatureC = (temperatureF - 32) / 1.8; +return { + ts: ctx.msgTs, + values: { + "temperatureC": toFixed(temperatureC, 2) + } +} + +``` + +This ensures that the calculated data point aligns with the timestamp of the triggering telemetry. + ##### Time Series Output The function must return a JSON object or array with or without a timestamp. @@ -225,8 +268,14 @@ Without timestamp: "hvacState": "IDLE", "configuration": { "someNumber": 42, - "someArray": [1,2,3], - "someNestedObject": {"key": "value"} + "someArray": [ + 1, + 2, + 3 + ], + "someNestedObject": { + "key": "value" + } } } ``` @@ -243,10 +292,16 @@ With timestamp: "hvacState": "IDLE", "configuration": { "someNumber": 42, - "someArray": [1,2,3], - "someNestedObject": {"key": "value"} + "someArray": [ + 1, + 2, + 3 + ], + "someNestedObject": { + "key": "value" + } } - } + } } ``` @@ -265,7 +320,7 @@ Array containing multiple timestamps and different values of the `airDensity` : "values": { "airDensity": 1.07 } - } + } ] ``` @@ -282,8 +337,14 @@ Example below return 5 data points: airDensity (double), humidity (integer), hva "hvacState": "IDLE", "configuration": { "someNumber": 42, - "someArray": [1,2,3], - "someNestedObject": {"key": "value"} + "someArray": [ + 1, + 2, + 3 + ], + "someNestedObject": { + "key": "value" + } } } ``` From 6d2d07da24c43e996f4168a14116e29c30d506d5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 25 Apr 2025 17:56:36 +0300 Subject: [PATCH 074/335] UI: Add init unit convertor and service --- ui-ngx/src/app/app.component.ts | 8 +- .../app/core/services/unit/converter-unit.ts | 444 ++++++++++++++++++ .../app/core/services/unit/definitions/all.ts | 39 ++ .../services/unit/definitions/temperature.ts | 77 +++ .../core/services/unit/definitions/time.ts | 76 +++ .../app/core/services/unit/unit.service.ts | 89 ++++ .../value-card-basic-config.component.html | 2 +- .../lib/cards/value-card-widget.component.ts | 17 +- .../home/pages/profile/profile.component.html | 10 + .../home/pages/profile/profile.component.ts | 20 +- ...convert-unit-settings-panel.component.html | 71 +++ ...convert-unit-settings-panel.component.scss | 49 ++ .../convert-unit-settings-panel.component.ts | 138 ++++++ .../components/unit-input.component.html | 21 +- .../shared/components/unit-input.component.ts | 198 +++++--- ui-ngx/src/app/shared/models/unit.models.ts | 159 ++++++- ui-ngx/src/app/shared/models/user.model.ts | 2 + ui-ngx/src/app/shared/shared.module.ts | 2 + .../assets/locale/locale.constant-en_US.json | 7 + 19 files changed, 1330 insertions(+), 99 deletions(-) create mode 100644 ui-ngx/src/app/core/services/unit/converter-unit.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/all.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/temperature.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/time.ts create mode 100644 ui-ngx/src/app/core/services/unit/unit.service.ts create mode 100644 ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html create mode 100644 ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss create mode 100644 ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index 5e579a759a..6e04735706 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -33,6 +33,7 @@ import { svgIcons, svgIconsUrl } from '@shared/models/icon.models'; import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; import { SETTINGS_KEY } from '@core/settings/settings.effects'; import { initCustomJQueryEvents } from '@shared/models/jquery-event.models'; +import { UnitService } from '@core/services/unit/unit.service'; @Component({ selector: 'tb-root', @@ -46,7 +47,8 @@ export class AppComponent implements OnInit { private translate: TranslateService, private matIconRegistry: MatIconRegistry, private domSanitizer: DomSanitizer, - private authService: AuthService) { + private authService: AuthService, + private unitService: UnitService) { console.log(`ThingsBoard Version: ${env.tbVersion}`); @@ -94,12 +96,14 @@ export class AppComponent implements OnInit { this.store.select(selectUserReady).pipe( filter((data) => data.isUserLoaded), tap((data) => { - let userLang = getCurrentAuthState(this.store).userDetails?.additionalInfo?.lang ?? null; + const userDetails = getCurrentAuthState(this.store).userDetails; + let userLang = userDetails?.additionalInfo?.lang ?? null; if (!userLang && !data.isAuthenticated) { const settings = this.storageService.getItem(SETTINGS_KEY); userLang = settings?.userLang ?? null; } this.notifyUserLang(userLang); + this.unitService.setUnitSystem(userDetails?.additionalInfo?.unitSystem) }), skip(1), ).subscribe((data) => { diff --git a/ui-ngx/src/app/core/services/unit/converter-unit.ts b/ui-ngx/src/app/core/services/unit/converter-unit.ts new file mode 100644 index 0000000000..aa04521b55 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/converter-unit.ts @@ -0,0 +1,444 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbUnitConvertor, Unit, UnitDescription, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures } from '@core/services/unit/definitions/all'; +import { TranslateService } from '@ngx-translate/core'; +import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils'; + +export interface Conversion< + TMeasures extends string, + TSystems extends string, + TUnits extends string, +> { + abbr: TUnits; + measure: TMeasures; + system: TSystems; + unit: Unit; +} + +// export interface BestResult { +// val: number; +// unit: TUnits; +// name: string; +// tags: string[]; +// } + +type Entries = [S, T[keyof T]]; + +export type UnitCache = Map< + string, + { + system: TSystems; + measure: TMeasures; + unit: Unit; + abbr: TUnits; + } +>; + +export class Converter< + TMeasures extends AllMeasures, + TSystems extends UnitSystem, + TUnits extends string, +> { + private measureData: Record>; + private unitCache: Map< + string, + { + system: TSystems; + measure: TMeasures; + unit: Unit; + abbr: TUnits; + } + >; + + constructor( + measures: Record>, + unitCache: UnitCache + ) { + this.measureData = measures; + this.unitCache = unitCache; + } + + convertor(from: TUnits | (string & {}), to: TUnits | (string & {})): TbUnitConvertor{ + const origin = this.getUnit(from); + if (origin === null) { + throw Error(`Unsupported unit ${from}`); + } + const destination = this.getUnit(to); + if (destination === null) { + throw Error(`Unsupported unit ${from}`); + } + if (origin.abbr === destination.abbr) { + return (value: number) => value; + } + if (destination.measure !== origin.measure) { + throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`); + } + return (value: number): number => { + let result = value * origin.unit.to_anchor; + if (origin.unit.anchor_shift) { + result -= origin.unit.anchor_shift; + } + + if (origin.system !== destination.system) { + const measure = this.measureData[origin.measure]; + const anchors = measure.anchors; + if (!anchors) { + throw Error(`Unable to convert units. Anchors are missing for "${origin.measure}" and "${destination.measure}" measures.`); + } + + const anchor = anchors[origin.system]; + if (!anchor) { + throw Error(`Unable to convert units. Anchors are missing for "${origin.measure}" and "${destination.measure}" measures.`); + } + + const transform = anchor[destination.system]?.transform; + const ratio = anchor[destination.system]?.ratio; + + if (typeof transform === 'function') { + result = transform(result); + } else if (typeof ratio === 'number') { + result *= ratio; + } else { + throw Error('A system anchor needs to either have a defined ratio number or a transform function.'); + } + } + + if (destination.unit.anchor_shift) { + result += destination.unit.anchor_shift; + } + return result / destination.unit.to_anchor; + }; + } + + convert(value: number, from: TUnits | (string & {}), to: TUnits | (string & {})): number { + const origin = this.getUnit(from); + if (origin === null) { + throw Error(`Unsupported unit ${from}`); + } + const destination = this.getUnit(to); + if (destination === null) { + throw Error(`Unsupported unit ${from}`); + } + if (origin.abbr === destination.abbr) { + return value; + } + if (destination.measure !== origin.measure) { + throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`); + } + let result = value * origin.unit.to_anchor; + if (origin.unit.anchor_shift) { + result -= origin.unit.anchor_shift; + } + if (origin.system !== destination.system) { + const measure = this.measureData[origin.measure]; + const anchors = measure.anchors; + if (!anchors) { + throw Error(`Unable to convert units. Anchors are missing for "${origin.measure}" and "${destination.measure}" measures.`); + } + const anchor = anchors[origin.system]; + if (!anchor) { + throw Error(`Unable to convert units. Anchors are missing for "${origin.measure}" and "${destination.measure}" measures.`); + } + const transform = anchor[destination.system]?.transform; + const ratio = anchor[destination.system]?.ratio; + if (typeof transform === 'function') { + result = transform(result); + } else if (typeof ratio === 'number') { + result *= ratio; + } else { + throw Error('A system anchor needs to either have a defined ratio number or a transform function.'); + } + } + + if (destination.unit.anchor_shift) { + result += destination.unit.anchor_shift; + } + return result / destination.unit.to_anchor; + } + + // toBest(options?: { + // exclude?: (TUnits | (string & {}))[]; + // cutOffNumber?: number; + // system?: TSystems | (string & {}); + // }): BestResult | null { + // if (this.origin == null) + // throw new OperationOrderError('.toBest must be called after .from'); + // + // const isNegative = this.val < 0; + // + // let exclude: (TUnits | (string & {}))[] = []; + // let cutOffNumber = isNegative ? -1 : 1; + // let system: TSystems | (string & {}) = this.origin.system; + // + // if (typeof options === 'object') { + // exclude = options.exclude ?? []; + // cutOffNumber = options.cutOffNumber ?? cutOffNumber; + // system = options.system ?? this.origin.system; + // } + // + // let best: BestResult | null = null; + // /** + // Looks through every possibility for the 'best' available unit. + // i.e. Where the value has the fewest numbers before the decimal point, + // but is still higher than 1. + // */ + // for (const possibility of this.possibilities()) { + // const unit = this.describe(possibility); + // const isIncluded = exclude.indexOf(possibility) === -1; + // + // if (isIncluded && unit.system === system) { + // const result = this.to(possibility); + // if (isNegative ? result > cutOffNumber : result < cutOffNumber) { + // continue; + // } + // if ( + // best === null || + // (isNegative + // ? result <= cutOffNumber && result > best.val + // : result >= cutOffNumber && result < best.val) + // ) { + // best = { + // val: result, + // unit: possibility, + // name: unit.name, + // tags: unit.tags + // }; + // } + // } + // } + // + // if (best == null) { + // return { + // val: this.val, + // unit: this.origin.abbr, + // name: this.origin.unit.name, + // tags: this.origin.unit.tags + // }; + // } + // + // return best; + // } + + getUnit(abbr: TUnits | (string & {})): Conversion | null { + return this.unitCache.get(abbr) ?? null; + } + + describe(abbr: TUnits | (string & {})): UnitDescription { + const result = this.getUnit(abbr); + + if (result != null) { + return this.describeUnit(result); + } + return null; + } + + private describeUnit(unit: Conversion): UnitDescription { + return { + abbr: unit.abbr, + measure: unit.measure, + system: unit.system, + name: unit.unit.name, + tags: unit.unit.tags + }; + } + + list(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescription[] | never { + const list = []; + + if (isDefinedAndNotNull(measureName)) { + if (!this.isMeasure(measureName)) { + console.log(`Measure "${measureName}" not found.`); + return list; + } + const measure = this.measureData[measureName]; + if (isDefinedAndNotNull(unitSystem)) { + let currentUnitSystem = unitSystem; + let units = measure.systems[currentUnitSystem]; + if (isUndefinedOrNull(units)) { + if (currentUnitSystem === UnitSystem.IMPERIAL) { + currentUnitSystem = UnitSystem.METRIC; + units = measure.systems[currentUnitSystem]; + } + if (!units) { + console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); + return list; + } + } + for (const [abbr, unit] of Object.entries( + units + )) { + list.push( + this.describeUnit({ + abbr: abbr as TUnits, + measure: measureName as TMeasures, + system: currentUnitSystem as TSystems, + unit: unit as Unit, + }) + ); + } + } else { + for (const [systemName, units] of Object.entries( + (measure as TbMeasure).systems + )) { + for (const [abbr, unit] of Object.entries( + units as Partial> + )) { + list.push( + this.describeUnit({ + abbr: abbr as TUnits, + measure: measureName as TMeasures, + system: systemName as TSystems, + unit: unit as Unit, + }) + ); + } + } + } + } else { + for (const [name, measure] of Object.entries(this.measureData)) { + if (isDefinedAndNotNull(unitSystem)) { + let currentUnitSystem = unitSystem; + let units = (measure as TbMeasure).systems[currentUnitSystem]; + if (isUndefinedOrNull(units)) { + if (currentUnitSystem === UnitSystem.IMPERIAL) { + currentUnitSystem = UnitSystem.METRIC; + units = (measure as TbMeasure).systems[currentUnitSystem]; + } + if (!units) { + console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); + continue; + } + } + for (const [abbr, unit] of Object.entries( + units as Partial> + )) { + list.push( + this.describeUnit({ + abbr: abbr as TUnits, + measure: name as TMeasures, + system: currentUnitSystem as TSystems, + unit: unit as Unit, + }) + ); + } + } else { + for (const [systemName, units] of Object.entries( + (measure as TbMeasure).systems + )) { + for (const [abbr, unit] of Object.entries( + units as Partial> + )) { + list.push( + this.describeUnit({ + abbr: abbr as TUnits, + measure: name as TMeasures, + system: systemName as TSystems, + unit: unit as Unit, + }) + ); + } + } + } + } + } + + return list; + } + + private isMeasure(measureName: string): measureName is TMeasures { + return measureName in this.measureData; + } + + // possibilities(forMeasure?: TMeasures | (string & {})): TUnits[] { + // let possibilities: TUnits[] = []; + // let list_measures: TMeasures[] = []; + // + // if (typeof forMeasure == 'string' && this.isMeasure(forMeasure)) { + // list_measures.push(forMeasure); + // } else if (this.origin != null) { + // list_measures.push(this.origin.measure); + // } else { + // list_measures = Object.keys(this.measureData) as TMeasures[]; + // } + // + // for (const measure of list_measures) { + // const systems = this.measureData[measure].systems; + // + // for (const system of Object.values(systems)) { + // possibilities = [ + // ...possibilities, + // ...(Object.keys(system as Record) as TUnits[]), + // ]; + // } + // } + // + // return possibilities; + // } + + // measures(): TMeasures[] { + // return Object.keys(this.measureData) as TMeasures[]; + // } +} + +export function buildUnitCache< + TMeasures extends string, + TSystems extends UnitSystem, + TUnits extends string, +>(measures: Record>, + translate: TranslateService +) { + const unitCache: UnitCache = new Map(); + for (const [measureName, measure] of Object.entries(measures) as Entries< + typeof measures, + TMeasures + >[]) { + for (const [systemName, system] of Object.entries( + measure.systems + ) as Entries>, TSystems>[]) { + for (const [testAbbr, unit] of Object.entries(system) as Entries< + typeof system, + TUnits + >[]) { + unit.name = translate.instant(unit.name); + unitCache.set(testAbbr, { + measure: measureName, + system: systemName, + abbr: testAbbr, + unit, + }); + } + } + } + return unitCache; +} + +export function configureMeasurements< + TMeasures extends AllMeasures, + TSystems extends UnitSystem, + TUnits extends string, +>( + measures: Record>, + translate: TranslateService +): Converter { + if (typeof measures !== 'object') { + throw new TypeError('The measures argument needs to be an object'); + } + + const unitCache = buildUnitCache(measures, translate); + return new Converter(measures, unitCache); +} diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts new file mode 100644 index 0000000000..7ae88112d3 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/all.ts @@ -0,0 +1,39 @@ +/// +/// Copyright © 2016-2025 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 temperature, { + TemperatureUnits, +} from './temperature'; +import time, { TimeUnits } from './time'; +import { TbMeasure, UnitSystem } from '@shared/models/unit.models'; + +export type AllMeasuresUnits = + | TemperatureUnits + | TimeUnits; + +export type AllMeasures = + | 'temperature' + | 'time'; + +const allMeasures: Record< + AllMeasures, + TbMeasure +> = { + temperature, + time, +}; + +export default allMeasures; diff --git a/ui-ngx/src/app/core/services/unit/definitions/temperature.ts b/ui-ngx/src/app/core/services/unit/definitions/temperature.ts new file mode 100644 index 0000000000..23b5831dcb --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/temperature.ts @@ -0,0 +1,77 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, Unit, UnitSystem } from '@shared/models/unit.models'; + +export type TemperatureMetricUnits = '°C' | 'K'; +export type TemperatureImperialUnits = '°F' | '°R'; + +export type TemperatureUnits = + | TemperatureMetricUnits + | TemperatureImperialUnits; + +const METRIC: Record = { + '°C': { + name: 'unit.celsius', + tags: ['temperature','heat','cold','warmth','degrees','celsius','shipment condition','°C'], + to_anchor: 1, + }, + K: { + name: 'unit.kelvin', + tags: ['temperature','heat','cold','warmth','degrees','kelvin','K','color quality','white balance','color temperature'], + to_anchor: 1, + anchor_shift: 273.15, + }, +}; + +const IMPERIAL: Record = { + '°F': { + name: 'unit.fahrenheit', + tags: ['temperature','heat','cold','warmth','degrees','fahrenheit','°F'], + to_anchor: 1, + }, + '°R': { + name: 'unit.rankine', + tags: ['temperature','heat','cold','warmth','Rankine','°R'], + to_anchor: 1, + anchor_shift: 459.67, + }, +}; + +const measure: TbMeasure = { + systems: { + METRIC, + IMPERIAL, + }, + anchors: { + METRIC: { + IMPERIAL: { + transform: function (C: number): number { + return C / (5 / 9) + 32; + }, + }, + }, + IMPERIAL: { + METRIC: { + transform: function (F: number): number { + return (F - 32) * (5 / 9); + }, + }, + }, + }, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/time.ts b/ui-ngx/src/app/core/services/unit/definitions/time.ts new file mode 100644 index 0000000000..aead2db33c --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/time.ts @@ -0,0 +1,76 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, Unit, UnitSystem } from '@shared/models/unit.models'; + +export type TimeUnits = TimeSIUnits; + +export type TimeSIUnits = + | 's' + | 'min' + | 'h' + | 'd' + | 'wk' + | 'mo' + | 'yr'; + +const daysInYear = 365.25; + +const METRIC: Record = { + s: { + name: 'unit.second', + tags: ["time","duration","interval","angle","second","arcsecond","sec"], + to_anchor: 1, + }, + min: { + name: 'unit.minute', + tags: ["time","duration","interval","angle","minute","arcminute","min"], + to_anchor: 60, + }, + h: { + name: 'unit.hour', + tags: ["time","duration","interval","h"], + to_anchor: 60 * 60, + }, + d: { + name: 'unit.day', + tags: ["time","duration","interval","d"], + to_anchor: 60 * 60 * 24, + }, + wk: { + name: 'unit.week', + tags: ["time","duration","interval","wk"], + to_anchor: 60 * 60 * 24 * 7, + }, + mo: { + name: 'unit.month', + tags: ["time","duration","interval","mo"], + to_anchor: (60 * 60 * 24 * daysInYear) / 12, + }, + yr: { + name: 'unit.year', + tags: ["time","duration","interval","yr"], + to_anchor: 60 * 60 * 24 * daysInYear, + }, +}; + +const measure: TbMeasure = { + systems: { + METRIC, + }, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/unit.service.ts b/ui-ngx/src/app/core/services/unit/unit.service.ts new file mode 100644 index 0000000000..4013e71ced --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/unit.service.ts @@ -0,0 +1,89 @@ +/// +/// Copyright © 2016-2025 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 { Injectable } from '@angular/core'; +import moment from 'moment-timezone'; +import { TbUnitConvertor, UnitDescription, UnitSystem } from '@shared/models/unit.models'; +import { isNotEmptyStr } from '@core/utils'; +import { configureMeasurements, Converter } from '@core/services/unit/converter-unit'; +import allMeasures, { AllMeasures, AllMeasuresUnits } from '@core/services/unit/definitions/all'; +import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Injectable({ + providedIn: 'root' +}) +export class UnitService { + + private currentUnitSystem: UnitSystem = UnitSystem.METRIC; + private converter: Converter; + + constructor(private store: Store, + private translate: TranslateService) { + this.translate.onLangChange.pipe( + takeUntilDestroyed() + ).subscribe(() => { + this.converter = configureMeasurements(allMeasures, this.translate); + console.warn(this.converter?.list()); + console.warn(this.converter?.list('temperature')); + console.warn(this.converter?.list('temperature', UnitSystem.METRIC)); + console.warn(this.converter?.list(null, UnitSystem.IMPERIAL)); + }); + } + + getUnitSystem(): UnitSystem { + return this.currentUnitSystem; + } + + setUnitSystem(unitSystem: UnitSystem) { + if (isNotEmptyStr(unitSystem)) { + this.currentUnitSystem = unitSystem; + } else { + this.currentUnitSystem = this.getUnitSystemByTimezone(); + } + console.warn('[Unit system] setUnitSystem', this.currentUnitSystem); + } + + getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitDescription[] { + return this.converter?.list(measure, unitSystem) ?? []; + } + + getUnitDescription(abbr: AllMeasuresUnits | string): UnitDescription { + return this.converter.describe(abbr); + } + + geUnitConvertor(from: string, to: string): TbUnitConvertor { + return this.converter.convertor(from, to); + } + + convertValue(value: number, from: string, to: string): number { + return this.converter.convert(value, from, to); + } + + private getUnitSystemByTimezone(): UnitSystem { + const timeZone = moment.tz.guess(true); + const imperialCountries = ['US', 'LR', 'MM']; + + if (moment.tz.zonesForCountry('GB').includes(timeZone)) { + return UnitSystem.HYBRID; + } + return imperialCountries.some(country => + moment.tz.zonesForCountry(country).includes(timeZone) + ) ? UnitSystem.IMPERIAL : UnitSystem.METRIC; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html index b8a67c744d..644a09fa5b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html @@ -84,7 +84,7 @@
widgets.value-card.value
- +
widget-config.decimals-suffix
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts index 68dc8fb232..7404f95cdf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts @@ -27,7 +27,7 @@ import { ViewChild } from '@angular/core'; import { WidgetContext } from '@home/models/widget-component.models'; -import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { isDefinedAndNotNull } from '@core/utils'; import { backgroundStyle, ColorProcessor, @@ -46,6 +46,7 @@ import { WidgetComponent } from '@home/components/widget/widget.component'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; +import { FormatValueProcessor } from '@shared/models/unit.models'; const squareLayoutSize = 160; const horizontalLayoutHeight = 80; @@ -100,8 +101,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro private panelResize$: ResizeObserver; private horizontal = false; - private decimals = 0; - private units = ''; + private formatValue: FormatValueProcessor; constructor(private imagePipe: ImagePipe, private sanitizer: DomSanitizer, @@ -116,15 +116,16 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro this.ctx.$scope.valueCardWidget = this; this.settings = {...valueCardDefaultSettings(this.horizontal), ...this.ctx.settings}; - this.decimals = this.ctx.decimals; - this.units = this.ctx.units; + let decimals = this.ctx.decimals; + let units = this.ctx.units; const dataKey = getDataKey(this.ctx.datasources); if (isDefinedAndNotNull(dataKey?.decimals)) { - this.decimals = dataKey.decimals; + decimals = dataKey.decimals; } if (dataKey?.units) { - this.units = dataKey.units; + units = dataKey.units; } + this.formatValue = FormatValueProcessor.fromSettings(this.ctx.$injector, {units: units, dec: decimals}); this.layout = this.settings.layout; @@ -187,7 +188,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro if (tsValue && isDefinedAndNotNull(tsValue[1]) && tsValue[0] !== 0) { ts = tsValue[0]; value = tsValue[1]; - this.valueText = formatValue(value, this.decimals, this.units, false); + this.valueText = this.formatValue.format(value); // formatValue(value, this.decimals, this.units, false); } else { this.valueText = 'N/A'; } diff --git a/ui-ngx/src/app/modules/home/pages/profile/profile.component.html b/ui-ngx/src/app/modules/home/pages/profile/profile.component.html index 839af59a5c..44eea5865e 100644 --- a/ui-ngx/src/app/modules/home/pages/profile/profile.component.html +++ b/ui-ngx/src/app/modules/home/pages/profile/profile.component.html @@ -66,6 +66,16 @@
+ + unit.unit-system + + {{ 'unit.unit-system-type.AUTO' | translate }} + @for(unit of UnitSystems; track unit) { + {{ 'unit.unit-system-type.' + unit | translate }} + } + +
, @@ -50,7 +53,8 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir private userService: UserService, private authService: AuthService, private translate: TranslateService, - public fb: UntypedFormBuilder) { + private unitService: UnitService, + private fb: UntypedFormBuilder) { super(store); this.authUser = getCurrentAuthUser(this.store); } @@ -67,6 +71,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir lastName: [''], phone: [''], language: [''], + unitSystem: [''], homeDashboardId: [null], homeDashboardHideToolbar: [true] }); @@ -80,6 +85,11 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir this.user.additionalInfo.lang = this.profile.get('language').value; this.user.additionalInfo.homeDashboardId = this.profile.get('homeDashboardId').value; this.user.additionalInfo.homeDashboardHideToolbar = this.profile.get('homeDashboardHideToolbar').value; + if (isNotEmptyStr(this.profile.get('unitSystem').value)) { + this.user.additionalInfo.unitSystem = this.profile.get('unitSystem').value; + } else { + delete this.user.additionalInfo.unitSystem; + } this.userService.saveUser(this.user).subscribe( (user) => { this.userLoaded(user); @@ -96,6 +106,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir lastName: user.lastName, } })); this.store.dispatch(new ActionSettingsChangeLanguage({ userLang: user.additionalInfo.lang })); + this.unitService.setUnitSystem(this.user.additionalInfo.unitSystem); this.authService.refreshJwtToken(false); } ); @@ -107,6 +118,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir let lang; let homeDashboardId; let homeDashboardHideToolbar = true; + let unitSystem: UnitSystem = null; if (user.additionalInfo) { if (user.additionalInfo.lang) { lang = user.additionalInfo.lang; @@ -115,11 +127,15 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir if (isDefinedAndNotNull(user.additionalInfo.homeDashboardHideToolbar)) { homeDashboardHideToolbar = user.additionalInfo.homeDashboardHideToolbar; } + if (isNotEmptyStr(user.additionalInfo.unitSystem)) { + unitSystem = user.additionalInfo.unitSystem; + } } if (!lang) { lang = this.translate.currentLang; } this.profile.get('language').setValue(lang); + this.profile.get('unitSystem').setValue(unitSystem); this.profile.get('homeDashboardId').setValue(homeDashboardId); this.profile.get('homeDashboardHideToolbar').setValue(homeDashboardHideToolbar); } diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html new file mode 100644 index 0000000000..8ca777ffcf --- /dev/null +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html @@ -0,0 +1,71 @@ + +
+
Unit convertion settings
+
+
+
From
+ +
+
+ + Convert units + +
+ @if(convertUnitForm.get('convertUnit').value) { +
+
Metrical
+ + +
+
+
Imperial
+ + +
+
+
Hybrid
+ + +
+ } +
+
+ + +
+
diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss new file mode 100644 index 0000000000..1613e4135e --- /dev/null +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2025 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'; + +.tb-convert-settings-panel { + width: 320px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-lt-md} { + width: 90vw; + } + .tb-convert-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-convert-settings-panel-content { + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + margin: -10px; + padding: 10px; + } + .tb-convert-settings-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts new file mode 100644 index 0000000000..370cc30985 --- /dev/null +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts @@ -0,0 +1,138 @@ +/// +/// Copyright © 2016-2025 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 { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { TbUnit, UnitDescription, UnitSystem } from '@shared/models/unit.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { FormBuilder, Validators } from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { UnitService } from '@core/services/unit/unit.service'; +import { AllMeasures } from '@core/services/unit/definitions/all'; +import { debounceTime } from 'rxjs/operators'; + +@Component({ + selector: 'tb-covert-unit-settings-panel', + templateUrl: './convert-unit-settings-panel.component.html', + styleUrls: ['./convert-unit-settings-panel.component.scss'], + providers: [], + encapsulation: ViewEncapsulation.None +}) +export class ConvertUnitSettingsPanelComponent implements OnInit { + + @Input() + unit: TbUnit; + + @Input() + required: boolean; + + @Output() + unitSettingsApplied = new EventEmitter(); + + UnitSystem = UnitSystem; + + measure: AllMeasures; + + convertUnitForm = this.fb.group({ + from: [''], + convertUnit: [true], + METRIC: [''], + IMPERIAL: [''], + HYBRID: [''] + }) + + constructor( + private popover: TbPopoverComponent, + private fb: FormBuilder, + private unitService: UnitService + ) { + this.convertUnitForm.get('from').valueChanges.pipe( + debounceTime(200), + takeUntilDestroyed() + ).subscribe(unit => { + const unitDescription = this.unitService.getUnitDescription(unit); + if (unitDescription) { + this.convertUnitForm.get('convertUnit').enable({emitEvent: true}); + this.measure = unitDescription.measure; + if (unitDescription.system === UnitSystem.IMPERIAL) { + this.convertUnitForm.get('IMPERIAL').setValue(unit, {emitEvent: false}); + this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false}); + } else { + this.convertUnitForm.get('METRIC').setValue(unit, {emitEvent: false}); + this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false}); + } + } else { + this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.convertUnitForm.get('convertUnit').disable({emitEvent: false}); + } + }) + + this.convertUnitForm.get('convertUnit').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => { + if (value) { + this.convertUnitForm.get('METRIC').enable({emitEvent: false}); + this.convertUnitForm.get('IMPERIAL').enable({emitEvent: false}); + this.convertUnitForm.get('HYBRID').enable({emitEvent: false}); + } else { + this.convertUnitForm.get('METRIC').disable({emitEvent: false}); + this.convertUnitForm.get('IMPERIAL').disable({emitEvent: false}); + this.convertUnitForm.get('HYBRID').disable({emitEvent: false}); + } + setTimeout(() => { + this.popover.updatePosition(); + }, 0); + }); + } + + ngOnInit() { + let unitDescription: UnitDescription; + if (this.required) { + this.convertUnitForm.get('from').setValidators(Validators.required); + this.convertUnitForm.get('from').updateValueAndValidity({emitEvent: false}); + } + if (typeof this.unit === 'string') { + this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.convertUnitForm.get('from').setValue(this.unit, {emitEvent: true}); + unitDescription = this.unitService.getUnitDescription(this.unit); + } else if (this.unit === null) { + this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.convertUnitForm.get('from').setValue(null, {emitEvent: true}); + } else { + this.convertUnitForm.patchValue(this.unit, {emitEvent: false}); + unitDescription = this.unitService.getUnitDescription(this.unit.from); + } + + if (unitDescription?.measure) { + this.measure = unitDescription.measure; + } else { + this.convertUnitForm.get('convertUnit').disable({emitEvent: false}); + } + } + + cancel() { + this.popover.hide(); + } + + applyUnitSettings() { + if (this.convertUnitForm.value.convertUnit) { + const formValue = this.convertUnitForm.value; + delete formValue.convertUnit; + this.unitSettingsApplied.emit(formValue as TbUnit); + } else { + this.unitSettingsApplied.emit(this.convertUnitForm.value.from); + } + } +} diff --git a/ui-ngx/src/app/shared/components/unit-input.component.html b/ui-ngx/src/app/shared/components/unit-input.component.html index 00286fd422..ab5932dcb9 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.html +++ b/ui-ngx/src/app/shared/components/unit-input.component.html @@ -15,11 +15,18 @@ limitations under the License. --> - + + + [matAutocomplete]="unitsAutocomplete" + [matAutocompleteDisabled]="allowConverted"> warning + + mdi:swap-vertical-circle-outline + @for (group of filteredUnits | async; track group[0]) { - @if ((fetchUnits$ | async).length > 1) { - - @for(unit of group[1]; track unit.abbr) { - - - - - } - - } @else { + @for(unit of group[1]; track unit.abbr) { } - } - } + + } diff --git a/ui-ngx/src/app/shared/components/unit-input.component.scss b/ui-ngx/src/app/shared/components/unit-input.component.scss index e5a4122ef8..7ca8ea86a2 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.scss +++ b/ui-ngx/src/app/shared/components/unit-input.component.scss @@ -15,6 +15,7 @@ */ .tb-autocomplete.tb-unit-input-autocomplete { .mat-mdc-optgroup-label { + min-height: 36px; .mdc-list-item__primary-text { font-size: 14px; font-weight: 500; diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index 312040da2a..9cd044d3dd 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -37,7 +37,7 @@ import { AllMeasures } from '@core/services/unit/definitions/all'; import { UnitService } from '@core/services/unit/unit.service'; import { TbPopoverService } from '@shared/components/popover.service'; import { ConvertUnitSettingsPanelComponent } from '@shared/components/convert-unit-settings-panel.component'; -import { isNotEmptyStr } from '@core/utils'; +import { isNotEmptyStr, isObject } from '@core/utils'; @Component({ selector: 'tb-unit-input', @@ -59,7 +59,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang unitsFormControl: FormControl; - @Input() + @Input({transform: booleanAttribute}) disabled: boolean; @Input({transform: booleanAttribute}) @@ -81,13 +81,13 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang searchText = ''; - isGroupOption = false; + isUnitMapping = false; private dirty = false; private modelValue: TbUnit | null; - fetchUnits$: Observable]>> = null; + private fetchUnits$: Observable]>> = null; private propagateChange = (_val: any) => {}; @@ -109,9 +109,6 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang }), mergeMap(symbol => this.fetchUnits(symbol)) ); - if (!!this.measure || !!this.tagFilter) { - this.isGroupOption = true; - } } ngOnChanges(changes: SimpleChanges) { @@ -121,11 +118,6 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang if (propName === 'measure' || propName === 'unitSystem') { this.fetchUnits$ = null; this.dirty = true; - if (!!this.measure || !!this.tagFilter) { - this.isGroupOption = true; - } else { - this.isGroupOption = false; - } } } } @@ -136,8 +128,10 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang this.modelValue = symbol; if (typeof symbol === 'string') { this.unitsFormControl.patchValue(this.unitService.getUnitDescription(symbol) ?? symbol, {emitEvent: false}); + this.isUnitMapping = false; } else { this.unitsFormControl.patchValue(symbol, {emitEvent: false}); + this.isUnitMapping = symbol !== null; } this.dirty = true; } @@ -172,18 +166,25 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } } - clear() { + clear($event: Event) { + $event.stopPropagation(); this.unitsFormControl.patchValue(null, {emitEvent: true}); - setTimeout(() => { - this.unitInput.nativeElement.blur(); - this.unitInput.nativeElement.focus(); - }, 0); + if (!this.allowConverted) { + setTimeout(() => { + this.unitInput.nativeElement.blur(); + this.unitInput.nativeElement.focus(); + }, 0); + } } openConvertSettingsPopup($event: Event) { + if (!this.allowConverted) { + return; + } if ($event) { $event.stopPropagation(); } + this.unitInput.nativeElement.blur(); const trigger = this.elementRef.nativeElement; if (this.popoverService.hasPopover(trigger)) { this.popoverService.hidePopover(trigger); @@ -196,7 +197,8 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang preferredPlacement: ['left', 'bottom', 'top'], context: { unit: this.getTbUnit(this.unitsFormControl.value), - required: this.required + required: this.required, + disabled: this.disabled, }, isModal: true }); @@ -212,6 +214,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang const res = this.getTbUnit(value); if (this.modelValue !== res) { this.modelValue = res; + this.isUnitMapping = (res !== null && isObject(res)); this.propagateChange(this.modelValue); } } diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 60b9e4f753..8a62b2d9d7 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -15,9 +15,6 @@ /// import { AllMeasures } from '@core/services/unit/definitions/all'; -import { Injector } from '@angular/core'; -import { isDefinedAndNotNull, isNotEmptyStr, isNumeric } from '@core/utils'; -import { UnitService } from '@core/services/unit/unit.service'; export enum UnitsType { capacity = 'capacity' @@ -74,104 +71,3 @@ export const searchUnits = (_units: Array, searchText: string): u.name.toUpperCase().includes(searchText) || searchUnitTags(u, searchText) ); - -export interface FormatValueSettingProcessor { - dec?: number; - units?: TbUnit; - showZeroDecimals?: boolean; -} - -export abstract class FormatValueProcessor { - - static fromSettings($injector: Injector, settings: FormatValueSettingProcessor): FormatValueProcessor { - if (typeof settings.units !== 'string' && isDefinedAndNotNull(settings.units?.from)) { - return new ConvertUnitProcessor($injector, settings) - } else { - return new SimpleUnitProcessor($injector, settings); - } - } - - protected constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { - } - - abstract format(value: any): string; -} - -export class SimpleUnitProcessor extends FormatValueProcessor { - - private readonly isDefinedUnit: boolean; - private readonly isDefinedDec: boolean; - private readonly showZeroDecimals: boolean; - - constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { - super($injector, settings); - this.isDefinedUnit = isNotEmptyStr(settings.units); - this.isDefinedDec = isDefinedAndNotNull(settings.dec); - this.showZeroDecimals = !!settings.showZeroDecimals; - } - - format(value: any): string { - if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDec || this.isDefinedUnit || Number(value).toString() === value)) { - let formatted = value; - if (this.isDefinedDec) { - formatted = Number(formatted).toFixed(this.settings.dec); - } - if (!this.showZeroDecimals) { - formatted = Number(formatted) - } - formatted = formatted.toString(); - if (this.isDefinedUnit) { - formatted += ` ${this.settings.units}`; - } - return formatted; - } - return value ?? ''; - } -} - -export class ConvertUnitProcessor extends FormatValueProcessor { - - private readonly isDefinedDec: boolean; - private readonly showZeroDecimals: boolean; - private readonly unitConvertor: TbUnitConvertor; - private readonly unitAbbr: string; - - constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { - super($injector, settings); - const unitService = this.$injector.get(UnitService); - const userUnitSystem = unitService.getUnitSystem(); - const unit = settings.units as TbUnitMapping; - const fromUnit = unit.from; - this.unitAbbr = isNotEmptyStr(unit[userUnitSystem]) ? unit[userUnitSystem] : fromUnit; - try { - this.unitConvertor = unitService.geUnitConvertor(fromUnit, this.unitAbbr); - } catch (e) {/**/} - - this.isDefinedDec = isDefinedAndNotNull(settings.dec); - this.showZeroDecimals = !!settings.showZeroDecimals; - } - - format(value: any): string { - if (isDefinedAndNotNull(value) && isNumeric(value)) { - let formatted: number | string = Number(value); - if (this.unitConvertor) { - formatted = this.unitConvertor(value); - } - if (this.isDefinedDec) { - formatted = Number(formatted).toFixed(this.settings.dec); - } - if (!this.showZeroDecimals) { - formatted = Number(formatted) - } - formatted = formatted.toString(); - if (this.unitAbbr) { - formatted += ` ${this.unitAbbr}`; - } - return formatted; - } - return value ?? ''; - } -} diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index ed2e769c1e..89c2824545 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -14,7 +14,15 @@ /// limitations under the License. /// -import { isDefinedAndNotNull, isNumber, isNumeric, isUndefinedOrNull, mergeDeep, parseFunction } from '@core/utils'; +import { + isDefinedAndNotNull, + isNotEmptyStr, + isNumber, + isNumeric, + isUndefinedOrNull, + mergeDeep, + parseFunction +} from '@core/utils'; import { DataEntry, DataKey, @@ -45,6 +53,8 @@ import { WidgetSubscriptionCallbacks, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; +import { UnitService } from '@core/services/unit/unit.service'; +import { TbUnit, TbUnitConvertor, TbUnitMapping } from '@shared/models/unit.models'; export type ComponentStyle = {[klass: string]: any}; @@ -852,6 +862,107 @@ export class AutoDateFormatProcessor extends DateFormatProcessor { } } +export interface FormatValueSettingProcessor { + dec?: number; + units?: TbUnit; + showZeroDecimals?: boolean; +} + +export abstract class FormatValueProcessor { + + static fromSettings($injector: Injector, settings: FormatValueSettingProcessor): FormatValueProcessor { + if (typeof settings.units !== 'string' && isDefinedAndNotNull(settings.units?.from)) { + return new ConvertUnitProcessor($injector, settings) + } else { + return new SimpleUnitProcessor($injector, settings); + } + } + + protected constructor(protected $injector: Injector, + protected settings: FormatValueSettingProcessor) { + } + + abstract format(value: any): string; +} + +export class SimpleUnitProcessor extends FormatValueProcessor { + + private readonly isDefinedUnit: boolean; + private readonly isDefinedDec: boolean; + private readonly showZeroDecimals: boolean; + + constructor(protected $injector: Injector, + protected settings: FormatValueSettingProcessor) { + super($injector, settings); + this.isDefinedUnit = isNotEmptyStr(settings.units); + this.isDefinedDec = isDefinedAndNotNull(settings.dec); + this.showZeroDecimals = !!settings.showZeroDecimals; + } + + format(value: any): string { + if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDec || this.isDefinedUnit || Number(value).toString() === value)) { + let formatted = value; + if (this.isDefinedDec) { + formatted = Number(formatted).toFixed(this.settings.dec); + } + if (!this.showZeroDecimals) { + formatted = Number(formatted) + } + formatted = formatted.toString(); + if (this.isDefinedUnit) { + formatted += ` ${this.settings.units}`; + } + return formatted; + } + return value ?? ''; + } +} + +export class ConvertUnitProcessor extends FormatValueProcessor { + + private readonly isDefinedDec: boolean; + private readonly showZeroDecimals: boolean; + private readonly unitConvertor: TbUnitConvertor; + private readonly unitAbbr: string; + + constructor(protected $injector: Injector, + protected settings: FormatValueSettingProcessor) { + super($injector, settings); + const unitService = this.$injector.get(UnitService); + const userUnitSystem = unitService.getUnitSystem(); + const unit = settings.units as TbUnitMapping; + const fromUnit = unit.from; + this.unitAbbr = isNotEmptyStr(unit[userUnitSystem]) ? unit[userUnitSystem] : fromUnit; + try { + this.unitConvertor = unitService.geUnitConvertor(fromUnit, this.unitAbbr); + } catch (e) {/**/} + + this.isDefinedDec = isDefinedAndNotNull(settings.dec); + this.showZeroDecimals = !!settings.showZeroDecimals; + } + + format(value: any): string { + if (isDefinedAndNotNull(value) && isNumeric(value)) { + let formatted: number | string = Number(value); + if (this.unitConvertor) { + formatted = this.unitConvertor(value); + } + if (this.isDefinedDec) { + formatted = Number(formatted).toFixed(this.settings.dec); + } + if (!this.showZeroDecimals) { + formatted = Number(formatted) + } + formatted = formatted.toString(); + if (this.unitAbbr) { + formatted += ` ${this.unitAbbr}`; + } + return formatted; + } + return value ?? ''; + } +} + const intervalToFormatTimeUnit = (interval: Interval): FormatTimeUnit => { const intervalValue = IntervalMath.numberValue(interval); if (intervalValue < SECOND) { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 78cc2d6e2e..68c160a9ca 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5847,6 +5847,16 @@ "background-blur": "Background blur" }, "unit": { + "convert": { + "set-units-conversion-settings": "Set units conversion settings", + "units-conversion-settings": "Units conversion settings", + "convert-from": "Convert from", + "to-metric": "To metric", + "to-imperial": "To imperial", + "to-hybrid": "To hybrid", + "convert-unit": "Convert unit", + "convert-unit-hint": "Work only with the default unit" + }, "unit-system": "Unit system", "unit-system-type": { "AUTO": "Auto", From 82ced843d3a35fae459ea2909aed462b76ed2e07 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 29 Apr 2025 16:07:26 +0300 Subject: [PATCH 088/335] UI: Refactoring convert unit --- .../app/core/services/unit/converter-unit.ts | 429 ++++-------------- ui-ngx/src/app/shared/models/unit.models.ts | 17 + 2 files changed, 116 insertions(+), 330 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit/converter-unit.ts b/ui-ngx/src/app/core/services/unit/converter-unit.ts index 8a6d707ed1..a97951db15 100644 --- a/ui-ngx/src/app/core/services/unit/converter-unit.ts +++ b/ui-ngx/src/app/core/services/unit/converter-unit.ts @@ -15,46 +15,20 @@ /// import { + Conversion, TbMeasure, TbUnitConvertor, Unit, + UnitCache, UnitDescription, UnitDescriptionGroupByMeasure, UnitSystem } from '@shared/models/unit.models'; import { AllMeasures } from '@core/services/unit/definitions/all'; import { TranslateService } from '@ngx-translate/core'; -import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils'; - -export interface Conversion< - TMeasures extends string, - TUnits extends string, -> { - abbr: TUnits; - measure: TMeasures; - system: UnitSystem; - unit: Unit; -} - -// export interface BestResult { -// val: number; -// unit: TUnits; -// name: string; -// tags: string[]; -// } type Entries = [S, T[keyof T]]; -export type UnitCache = Map< - string, - { - system: UnitSystem; - measure: TMeasures; - unit: Unit; - abbr: TUnits; - } ->; - export class Converter< TMeasures extends AllMeasures, TUnits extends string, @@ -78,77 +52,40 @@ export class Converter< this.unitCache = unitCache; } - convertor(from: TUnits | (string & {}), to: TUnits | (string & {})): TbUnitConvertor{ - const origin = this.getUnit(from); - if (origin === null) { - throw Error(`Unsupported unit ${from}`); - } - const destination = this.getUnit(to); - if (destination === null) { - throw Error(`Unsupported unit ${from}`); - } - if (origin.abbr === destination.abbr) { - return (value: number) => value; - } - if (destination.measure !== origin.measure) { - throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`); - } - return (value: number): number => { - let result = value * origin.unit.to_anchor; - if (origin.unit.anchor_shift) { - result -= origin.unit.anchor_shift; - } - - if (origin.system !== destination.system) { - const measure = this.measureData[origin.measure]; - const transform = measure[origin.system]?.transform; - const ratio = measure[origin.system]?.ratio; - - if (typeof transform === 'function') { - result = transform(result); - } else if (typeof ratio === 'number') { - result *= ratio; - } else { - throw Error('A system anchor needs to either have a defined ratio number or a transform function.'); - } - } - - if (destination.unit.anchor_shift) { - result += destination.unit.anchor_shift; - } - return result / destination.unit.to_anchor; - }; + convertor(from: TUnits | string, to: TUnits | string): TbUnitConvertor { + return (value: number) => this.convert(value, from, to); } - convert(value: number, from: TUnits | (string & {}), to: TUnits | (string & {})): number { + convert(value: number, from: TUnits | string, to: TUnits | string): number { const origin = this.getUnit(from); - if (origin === null) { - throw Error(`Unsupported unit ${from}`); - } const destination = this.getUnit(to); - if (destination === null) { - throw Error(`Unsupported unit ${from}`); + + if (!origin) { + throw new Error(`Unsupported unit: ${from}`); + } + if (!destination) { + throw new Error(`Unsupported unit: ${to}`); } if (origin.abbr === destination.abbr) { return value; } if (destination.measure !== origin.measure) { - throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`); + throw Error(`Cannot convert incompatible measures: ${origin.measure} to ${destination.measure}`); } let result = value * origin.unit.to_anchor; if (origin.unit.anchor_shift) { result -= origin.unit.anchor_shift; } if (origin.system !== destination.system) { - const measure = this.measureData[origin.measure]; - const transform = measure[origin.system]?.transform; - const ratio = measure[origin.system]?.ratio; + const measureUnits = this.measureData[origin.measure][origin.system]; + const transform = measureUnits?.transform; + const ratio = measureUnits?.ratio; if (typeof transform === 'function') { result = transform(result); } else if (typeof ratio === 'number') { result *= ratio; } else { - throw Error('A system anchor needs to either have a defined ratio number or a transform function.'); + throw Error('System anchor requires a defined ratio or transform function'); } } @@ -162,26 +99,16 @@ export class Converter< if (!this.isMeasure(measureName)) { return null; } - const measure = this.measureData[measureName]; - let currentUnitSystem = unitSystem; - let units = measure[currentUnitSystem].units; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = measure[currentUnitSystem].units; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - return null; - } + const units = this.getUnitsForMeasure(measureName, unitSystem); + if (!units) { + return null; } - for (const [abbr, unit] of Object.entries( - units as Partial> - ) as [TUnits, Unit][]) { - if (unit.to_anchor === 1 && (isUndefinedOrNull(unit.anchor_shift) || unit.anchor_shift === 0)) { + for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { + if (unit.to_anchor === 1 && (!unit.anchor_shift || unit.anchor_shift === 0)) { return abbr; } } + return null; } getUnit(abbr: TUnits | (string & {})): Conversion | null { @@ -189,271 +116,113 @@ export class Converter< } describe(abbr: TUnits | (string & {})): UnitDescription { - const result = this.getUnit(abbr); - - if (result != null) { - return this.describeUnit(result); - } - return null; + const unit = this.getUnit(abbr); + return unit ? this.describeUnit(unit) : null; } - private describeUnit(unit: Conversion): UnitDescription { - return { - abbr: unit.abbr, - measure: unit.measure, - system: unit.system, - name: unit.unit.name, - tags: unit.unit.tags - }; - } + list(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescription[] { + const results: UnitDescription[] = []; - list(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescription[] | never { - const list = []; + const measures = measureName + ? { [measureName]: this.measureData[measureName] } as Record> + : this.measureData; - if (isDefinedAndNotNull(measureName)) { - if (!this.isMeasure(measureName)) { - console.log(`Measure "${measureName}" not found.`); - return list; + for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; } - const measure = this.measureData[measureName]; - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = measure[currentUnitSystem]; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = measure[currentUnitSystem]; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - return list; - } + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; } - for (const [abbr, unit] of Object.entries( - units.units - )) { - list.push( + + for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { + results.push( this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, + abbr, + measure: name as TMeasures, + system, + unit, }) ); } - } else { - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - list.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: systemName, - unit: unit as Unit, - }) - ); - } - } - } - } else { - for (const [name, measure] of Object.entries(this.measureData)) { - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = (measure as TbMeasure)[currentUnitSystem]?.units; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = (measure as TbMeasure)[currentUnitSystem]?.units; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - continue; - } - } - for (const [abbr, unit] of Object.entries( - units as Partial> - )) { - list.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: name as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, - }) - ); - } - } else { - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - list.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: name as TMeasures, - system: systemName, - unit: unit as Unit, - }) - ); - } - } - } } } - - return list; + return results; } - listGroupByMeasure(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure | never { - const list: UnitDescriptionGroupByMeasure = {}; + listGroupByMeasure(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure | never { + const results: UnitDescriptionGroupByMeasure = {}; - if (isDefinedAndNotNull(measureName)) { - if (!this.isMeasure(measureName)) { - console.log(`Measure "${measureName}" not found.`); - return list; + const measures = measureName + ? { [measureName]: this.measureData[measureName]} as Record> + : this.measureData; + + for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; } - const measure = this.measureData[measureName]; - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = measure[currentUnitSystem]; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = measure[currentUnitSystem]; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - return list; - } + + results[name] = []; + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; } - list[measureName] = []; - const unitsDescription = list[measureName]; - for (const [abbr, unit] of Object.entries( - units.units - )) { - unitsDescription.push( + + for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { + results[name].push( this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, + abbr, + measure: name as TMeasures, + system, + unit, }) ); } - } else { - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - list[measureName] = []; - const unitsDescription = list[measureName]; - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - unitsDescription.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: systemName, - unit: unit as Unit, - }) - ); - } - } - } - } else { - for (const [name, measure] of Object.entries(this.measureData)) { - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = (measure as TbMeasure)[currentUnitSystem]?.units; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = (measure as TbMeasure)[currentUnitSystem]?.units; - } - if (!units) { - console.log(`Measure "${name}" in ${currentUnitSystem} system is not found.`); - continue; - } - } - list[name] = []; - const unitsDescription = list[name]; - for (const [abbr, unit] of Object.entries( - units as Partial> - )) { - unitsDescription.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: name as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, - }) - ); - } - } else { - list[name] = []; - const unitsDescription = list[name]; - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - unitsDescription.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: name as TMeasures, - system: systemName, - unit: unit as Unit, - }) - ); - } - } - } } } + return results; + } - return list; + private describeUnit(unit: Conversion): UnitDescription { + return { + abbr: unit.abbr, + measure: unit.measure, + system: unit.system, + name: unit.unit.name, + tags: unit.unit.tags + }; } private isMeasure(measureName: string): measureName is TMeasures { return measureName in this.measureData; } - // possibilities(forMeasure?: TMeasures | (string & {})): TUnits[] { - // let possibilities: TUnits[] = []; - // let list_measures: TMeasures[] = []; - // - // if (typeof forMeasure == 'string' && this.isMeasure(forMeasure)) { - // list_measures.push(forMeasure); - // } else if (this.origin != null) { - // list_measures.push(this.origin.measure); - // } else { - // list_measures = Object.keys(this.measureData) as TMeasures[]; - // } - // - // for (const measure of list_measures) { - // const systems = this.measureData[measure].systems; - // - // for (const system of Object.values(systems)) { - // possibilities = [ - // ...possibilities, - // ...(Object.keys(system as Record) as TUnits[]), - // ]; - // } - // } - // - // return possibilities; - // } - - // measures(): TMeasures[] { - // return Object.keys(this.measureData) as TMeasures[]; - // } + private getUnitsForMeasure( + measureName: TMeasures, + unitSystem: UnitSystem + ): Partial> | null { + const measure = this.measureData[measureName]; + let system = unitSystem; + let units = measure[system]?.units; + if (!units && unitSystem === UnitSystem.IMPERIAL) { + system = UnitSystem.METRIC; + units = measure[system]?.units; + } + return units ?? null; + } } export function buildUnitCache< diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 8a62b2d9d7..01f88c3406 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -63,6 +63,23 @@ export interface TbMeasureUnits { units?: Partial>; } +export interface Conversion { + abbr: TUnits; + measure: TMeasures; + system: UnitSystem; + unit: Unit; +} + +export type UnitCache = Map< + string, + { + system: UnitSystem; + measure: TMeasures; + unit: Unit; + abbr: TUnits; + } +>; + const searchUnitTags = (unit: UnitDescription, searchText: string): boolean => !!unit.tags.find(t => t.toUpperCase().includes(searchText)); From 3f2fd6f979d0b70db090d7bff48a54ead932867e Mon Sep 17 00:00:00 2001 From: deaflynx Date: Tue, 29 Apr 2025 17:39:54 +0300 Subject: [PATCH 089/335] UI: lwm2m observe strategy feature. --- ...ile-transport-configuration.component.html | 14 +++++++ ...ofile-transport-configuration.component.ts | 26 ++++++++++++- .../lwm2m/lwm2m-profile-config.models.ts | 39 ++++++++++++++++++- .../assets/locale/locale.constant-en_US.json | 9 +++++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index f4dbafb6e8..ac7d0677fa 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -20,6 +20,20 @@
+ + device-profile.lwm2m.observe-strategy.observe-strategy + + + {{ observeStrategyMap.get(lwm2mDeviceProfileFormGroup.get('observeStrategy').value)?.name | translate }} + + + {{ observeStrategyMap.get(strategy).name | translate }} + + {{ observeStrategyMap.get(strategy).description | translate }} + + + + object; @Input() @@ -102,6 +107,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro observeAttrTelemetry: [null], bootstrapServerUpdateEnable: [false], bootstrap: [[]], + observeStrategy: [null, []], clientLwM2mSettings: this.fb.group({ clientOnlyObserveAfterConnect: [1, []], useObject19ForOtaInfo: [false], @@ -173,6 +179,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } }); + this.lwm2mDeviceProfileFormGroup.get('objectIds').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(value => this.updateObserveStrategy(value)); + this.lwm2mDeviceProfileFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe((value) => { @@ -261,6 +271,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value), bootstrap: this.configurationValue.bootstrap, bootstrapServerUpdateEnable: this.configurationValue.bootstrapServerUpdateEnable || false, + observeStrategy: this.configurationValue.observeAttr.observeStrategy || ObserveStrategy.SINGLE, clientLwM2mSettings: { clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, useObject19ForOtaInfo: this.configurationValue.clientLwM2mSettings.useObject19ForOtaInfo ?? false, @@ -283,6 +294,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').updateValueAndValidity({onlySelf: true}); this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').updateValueAndValidity({onlySelf: true}); } + this.updateObserveStrategy(value); this.cd.markForCheck(); } @@ -427,6 +439,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const telemetryArray: Array = []; const attributes: any = {}; const keyNameNew = {}; + const observeStrategyValue = val.length ? this.lwm2mDeviceProfileFormGroup.get('observeStrategy')?.value : ObserveStrategy.SINGLE; const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); observeJson.forEach(obj => { if (isDefinedAndNotNull(obj.attributes) && !isEmpty(obj.attributes)) { @@ -467,7 +480,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro attribute: attributeArray, telemetry: telemetryArray, keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew), - attributeLwm2m: attributes + attributeLwm2m: attributes, + observeStrategy: observeStrategyValue }; } @@ -568,4 +582,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro return this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings') as UntypedFormGroup; } + private updateObserveStrategy(value: ObjectLwM2M[]) { + if (value.length && !this.disabled) { + this.lwm2mDeviceProfileFormGroup.get('observeStrategy').enable({onlySelf: true}); + } else { + this.lwm2mDeviceProfileFormGroup.get('observeStrategy').disable({onlySelf: true}); + } + } + } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 3972964a5b..48b6c3284c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -136,6 +136,41 @@ export const ObjectIDVerTranslationMap = new Map( ] ); +export interface ObserveStrategyData { + name: string; + description: string; +} + +export enum ObserveStrategy { + SINGLE = 'SINGLE', + COMPOSITE_ALL = 'COMPOSITE_ALL', + COMPOSITE_BY_OBJECT = 'COMPOSITE_BY_OBJECT' +} + +export const ObserveStrategyMap = new Map([ + [ + ObserveStrategy.SINGLE, + { + name: 'device-profile.lwm2m.observe-strategy.single', + description: 'device-profile.lwm2m.observe-strategy.single-description' + } + ], + [ + ObserveStrategy.COMPOSITE_ALL, + { + name: 'device-profile.lwm2m.observe-strategy.composite-all', + description: 'device-profile.lwm2m.observe-strategy.composite-all-description' + } + ], + [ + ObserveStrategy.COMPOSITE_BY_OBJECT, + { + name: 'device-profile.lwm2m.observe-strategy.composite-by-object', + description: 'device-profile.lwm2m.observe-strategy.composite-by-object-description' + } + ] +]); + export interface ServerSecurityConfig { host?: string; port?: number; @@ -187,6 +222,7 @@ export interface ObservableAttributes { telemetry: string[]; keyName: {}; attributeLwm2m: AttributesNameValueMap; + observeStrategy: ObserveStrategy; } export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { @@ -195,7 +231,8 @@ export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { attribute: [], telemetry: [], keyName: {}, - attributeLwm2m: {} + attributeLwm2m: {}, + observeStrategy: ObserveStrategy.SINGLE }; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b160a44b0e..91008c69a6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2209,6 +2209,15 @@ "v1-0": "1.0", "v1-1": "1.1", "v1-2": "1.2" + }, + "observe-strategy": { + "observe-strategy": "Observe strategy", + "single": "Single", + "single-description": "One resource equals one single observe request", + "composite-all": "Composite all", + "composite-all-description": "All resources in one composite observe request", + "composite-by-object": "Composite by objects", + "composite-by-object-description": "Grouped composite observe requests by object" } }, "snmp": { From b9706295ae5ee08ae137acd3ad99ad6b8ce672ed Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 29 Apr 2025 18:01:32 +0300 Subject: [PATCH 090/335] lwm2m: add new field: elemetryObserveStrategy observeStrategy -license:format --- .../profile/lwm2m/TelemetryObserveStrategy.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java index 28d571c0b4..56d85c4b6f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2025 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. + */ package org.thingsboard.server.common.data.device.profile.lwm2m; import lombok.Getter; From cff17821c5546e8b48c852f09c408235b66f8e4b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 29 Apr 2025 19:06:29 +0300 Subject: [PATCH 091/335] lwm2m: add observeComposite ti init Client --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 94 +++++++++++++++---- .../client/LwM2mBinaryAppDataContainer.java | 6 +- .../AbstractSecurityLwM2MIntegrationTest.java | 2 +- .../sql/NoSecLwM2MIntegrationTest.java | 26 ++++- .../TransportConfigurationTest.java | 2 +- .../uplink/DefaultLwM2mUplinkMsgHandler.java | 50 ++++++++-- 6 files changed, 143 insertions(+), 37 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 76f1811b2d..022221ac8e 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -34,6 +34,7 @@ import org.springframework.http.HttpStatus; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.script.api.tbel.TbDate; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileProvisionType; @@ -196,6 +197,28 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte " \"attributeLwm2m\": {}\n" + " }"; + public static String TELEMETRY_WITH_COMPOSITE_OBSERVE = + " {\n" + + " \"keyName\": {\n" + + " \"/3_1.2/0/9\": \"batteryLevel\",\n" + + " \"/3_1.2/0/20\": \"batteryStatus\",\n" + + " \"/19_1.1/0/2\": \"dataCreationTime\"\n" + + " },\n" + + " \"observe\": [\n" + + " \"/3_1.2/0/9\",\n" + + " \"/3_1.2/0/20\",\n" + + " \"/19_1.1/0/2\"\n" + + " ],\n" + + " \"attribute\": [],\n" + + " \"telemetry\": [\n" + + " \"/3_1.2/0/9\",\n" + + " \"/3_1.2/0/20\",\n" + + " \"/19_1.1/0/2\"\n" + + " ],\n" + + " \"attributeLwm2m\": {},\n" + + " \"observeStrategy\": 1\n" + + " }"; + public static final String CLIENT_LWM2M_SETTINGS = " {\n" + " \"edrxCycle\": null,\n" + @@ -208,20 +231,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte " \"pagingTransmissionWindow\": null,\n" + " \"clientOnlyObserveAfterConnect\": 1\n" + " }"; - public static String TELEMETRY_WITH_STRATEGY_COMPOSITE_ALL = - " {\n" + - " \"keyName\": {\n" + - " \"/3_1.2/0/9\": \"batteryLevel\"\n" + - " },\n" + - " \"observe\": [],\n" + - " \"attribute\": [\n" + - " ],\n" + - " \"telemetry\": [\n" + - " \"/3_1.2/0/9\"\n" + - " ],\n" + - " \"attributeLwm2m\": {},\n" + - " \"observeStrategy\": 1\n" + - " }"; + protected final Set expectedStatusesRegistrationLwm2mSuccess = new HashSet<>(Arrays.asList(ON_INIT, ON_REGISTRATION_STARTED, ON_REGISTRATION_SUCCESS)); protected final Set expectedStatusesRegistrationLwm2mSuccessUpdate = new HashSet<>(Arrays.asList(ON_INIT, ON_REGISTRATION_STARTED, ON_REGISTRATION_SUCCESS, ON_UPDATE_STARTED, ON_UPDATE_SUCCESS)); protected final Set expectedStatusesRegistrationBsSuccess = new HashSet<>(Arrays.asList(ON_BOOTSTRAP_STARTED, ON_BOOTSTRAP_SUCCESS, ON_REGISTRATION_STARTED, ON_REGISTRATION_SUCCESS)); @@ -261,10 +271,10 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte } } - public void basicTestConnectionObserveTelemetry(Security security, - LwM2MDeviceCredentials deviceCredentials, - String endpoint, - boolean queueMode) throws Exception { + public void basicTestConnectionObserveSingleTelemetry(Security security, + LwM2MDeviceCredentials deviceCredentials, + String endpoint, + boolean queueMode) throws Exception { Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITHOUT_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + endpoint, transportConfiguration); Device device = createLwm2mDevice(deviceCredentials, endpoint, deviceProfile.getId()); @@ -298,8 +308,56 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte int expectedMin = 5; Assert.assertTrue(expectedMax >= Long.parseLong(tsValue.getValue())); Assert.assertTrue(expectedMin <= Long.parseLong(tsValue.getValue())); + } + + public void basicTestConnectionObserveCompositeTelemetry(Security security, + LwM2MDeviceCredentials deviceCredentials, + String endpoint, + Lwm2mDeviceProfileTransportConfiguration transportConfiguration, + int cntObserve) throws Exception { + + DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + endpoint, transportConfiguration); + Device device = createLwm2mDevice(deviceCredentials, endpoint, deviceProfile.getId()); + + SingleEntityFilter sef = new SingleEntityFilter(); + sef.setSingleEntity(device.getId()); + LatestValueCmd latestCmd = new LatestValueCmd(); + String key1 = "batteryLevel"; + String key2 = "dataCreationTime"; + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, key1))); + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, key2))); + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + EntityDataCmd cmd = new EntityDataCmd(2, edq, null, latestCmd, null); + getWsClient().send(cmd); + getWsClient().waitForReply(); + getWsClient().registerWaitForUpdate(); + this.createNewClient(security, null, false, endpoint, null, true, device.getId().getId().toString()); + awaitObserveReadAll(cntObserve, lwM2MTestClient.getDeviceIdStr()); + String msg = getWsClient().waitForUpdate(); + + EntityDataUpdate update = JacksonUtil.fromString(msg, EntityDataUpdate.class); + Assert.assertEquals(2, update.getCmdId()); + List eData = update.getUpdate(); + Assert.assertNotNull(eData); + Assert.assertEquals(1, eData.size()); + Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); + var tsValue1 = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get(key1); + var tsValue2 = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get(key2); + if (tsValue1 != null) { + Assert.assertThat(Long.parseLong(tsValue1.getValue()), instanceOf(Long.class)); + int expectedMax = 50; + int expectedMin = 5; + Assert.assertTrue(expectedMax >= Long.parseLong(tsValue1.getValue())); + Assert.assertTrue(expectedMin <= Long.parseLong(tsValue1.getValue())); + } else { + String pattern = "MMM dd, yyyy HH:mm a"; + TbDate d = new TbDate(tsValue2.getValue(), pattern, "en-US"); + Assert.assertNotNull(d); + } } protected DeviceProfile createLwm2mDeviceProfile(String name, Lwm2mDeviceProfileTransportConfiguration transportConfiguration) throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java index 41a2259790..9bd23caea6 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java @@ -27,9 +27,6 @@ import org.eclipse.leshan.core.response.WriteResponse; import javax.security.auth.Destroyable; import java.sql.Time; -import java.time.Instant; -import java.time.LocalTime; -import java.time.ZoneId; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -187,8 +184,7 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements } private Time getTimestamp() { - LocalTime localTime = LocalTime.ofInstant(Instant.now(), ZoneId.systemDefault()); - this.timestamp = Time.valueOf(localTime); + setTimestamp(); return this.timestamp; } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java index 076371055d..2367670d5f 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java @@ -119,7 +119,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M protected final PrivateKey clientPrivateKeyFromCertTrust; // client private key used for X509 and RPK protected final X509Certificate clientX509CertTrustNo; // client certificate signed by intermediate, rootCA with a good CN ("host name") protected final PrivateKey clientPrivateKeyFromCertTrustNo; // client private key used for X509 and RPK - private final String[] RESOURCES_SECURITY = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"}; + private final String[] RESOURCES_SECURITY = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml", "19.xml"}; private final LwM2MBootstrapClientCredentials defaultBootstrapCredentials; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java index d8e0b28b76..34683ffc71 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java @@ -17,8 +17,10 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; +import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.COMPOSITE_BY_OBJECT; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_REGISTRATION_SUCCESS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOOTSTRAP_ONLY; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH; @@ -32,13 +34,31 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT public void testWithNoSecConnectLwm2mSuccessAndObserveTelemetry() throws Exception { String clientEndpoint = CLIENT_ENDPOINT_NO_SEC; LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); - super.basicTestConnectionObserveTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, false); + super.basicTestConnectionObserveSingleTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, false); } + + @Test + public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveSingleTelemetry() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + "_QueueMode"; + LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); + super.basicTestConnectionObserveSingleTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, true); + } + + @Test + public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveCompositeAllTelemetry() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + "_QueueMode"; + LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = super.getTransportConfiguration(TELEMETRY_WITH_COMPOSITE_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); + super.basicTestConnectionObserveCompositeTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, transportConfiguration, 1); + } + @Test - public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveTelemetry() throws Exception { + public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveCompositeByObjectTelemetry() throws Exception { String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + "_QueueMode"; LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); - super.basicTestConnectionObserveTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, true); + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = super.getTransportConfiguration(TELEMETRY_WITH_COMPOSITE_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); + transportConfiguration.getObserveAttr().setObserveStrategy(COMPOSITE_BY_OBJECT); + super.basicTestConnectionObserveCompositeTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, transportConfiguration, 2); } // Bootstrap + Lwm2m diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/TransportConfigurationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/TransportConfigurationTest.java index 85181c398d..757bc81b41 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/TransportConfigurationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/TransportConfigurationTest.java @@ -35,7 +35,7 @@ public class TransportConfigurationTest extends AbstractSecurityLwM2MIntegration @Test public void testTransportConfigurationObserveStrategyBeforeParseNotNullAfterParseNotNull_STRATEGY_COMPOSITE_ALL() throws Exception { - Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_STRATEGY_COMPOSITE_ALL, getBootstrapServerCredentialsNoSec(NONE)); + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_COMPOSITE_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); Assert.assertNotNull(transportConfiguration.getObserveAttr().getObserveStrategy()); Assert.assertEquals(COMPOSITE_ALL, transportConfiguration.getObserveAttr().getObserveStrategy()); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index 9976783179..b7d6e0e7f5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -60,6 +60,7 @@ import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTrans import org.thingsboard.server.common.data.device.profile.lwm2m.ObjectAttributes; import org.thingsboard.server.common.data.device.profile.lwm2m.OtherConfiguration; import org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryMappingConfiguration; +import org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.OtaPackageUtil; @@ -92,6 +93,8 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallbac import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MObserveCompositeCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MObserveCompositeRequest; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.model.LwM2MModelConfig; import org.thingsboard.server.transport.lwm2m.server.model.LwM2MModelConfigService; @@ -116,9 +119,11 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; +import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.COMPOSITE_ALL; +import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.COMPOSITE_BY_OBJECT; +import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.SINGLE; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.util.CollectionsUtil.diffSets; import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_3_VER_ID; @@ -475,7 +480,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl Set supportedObjects = clientContext.getSupportedIdVerInClient(lwM2MClient); if (supportedObjects != null && supportedObjects.size() > 0) { this.sendReadRequests(lwM2MClient, profile, supportedObjects); - this.sendObserveRequests(lwM2MClient, profile, supportedObjects); + this.sendInitObserveRequests(lwM2MClient, profile, supportedObjects); this.sendWriteAttributeRequests(lwM2MClient, profile, supportedObjects); // Removed. Used only for debug. // this.sendDiscoverRequests(lwM2MClient, profile, supportedObjects); @@ -501,16 +506,27 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl } } - private void sendObserveRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { + private void sendInitObserveRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { try { Set targetIds = profile.getObserveAttr().getObserve(); targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); - - CountDownLatch latch = new CountDownLatch(targetIds.size()); - targetIds.forEach(targetId -> sendObserveRequest(lwM2MClient, targetId, - new TbLwM2MLatchCallback<>(latch, new TbLwM2MObserveCallback(this, logService, lwM2MClient, targetId)))); - - latch.await(config.getTimeout(), TimeUnit.MILLISECONDS); + if (!targetIds.isEmpty()) { + TelemetryObserveStrategy observeStrategy = profile.getObserveAttr().getObserveStrategy(); + if (SINGLE.equals(observeStrategy)) { + CountDownLatch latch = new CountDownLatch(targetIds.size()); + targetIds.forEach(targetId -> sendObserveRequest(lwM2MClient, targetId, + new TbLwM2MLatchCallback<>(latch, new TbLwM2MObserveCallback(this, logService, lwM2MClient, targetId)))); + latch.await(config.getTimeout(), TimeUnit.MILLISECONDS); + } else if (COMPOSITE_ALL.equals(observeStrategy)) { + String[] versionedIds = targetIds.toArray(new String[0]); + sendObserveCompositeRequest(lwM2MClient, versionedIds); + } else if (COMPOSITE_BY_OBJECT.equals(observeStrategy)) { + Map versionedObjectIds = groupByObjectIdVersionedIds(targetIds); + CountDownLatch latch = new CountDownLatch(versionedObjectIds.size()); + versionedObjectIds.forEach((k, v)-> sendObserveCompositeRequest(lwM2MClient, v)); + latch.await(config.getTimeout(), TimeUnit.MILLISECONDS); + } + } } catch (InterruptedException e) { log.error("[{}] Failed to await Observe requests!", lwM2MClient.getEndpoint(), e); } catch (Exception e) { @@ -547,6 +563,12 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(clientContext.getRequestTimeout(lwM2MClient)).build(); defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, request, callback); } + private void sendObserveCompositeRequest(LwM2mClient lwM2MClient, String[] versionedIds) { + + TbLwM2MObserveCompositeRequest request = TbLwM2MObserveCompositeRequest.builder().versionedIds(versionedIds).timeout(clientContext.getRequestTimeout(lwM2MClient)).build(); + var mainCallback = new TbLwM2MObserveCompositeCallback(this, logService, lwM2MClient, versionedIds); + defaultLwM2MDownlinkMsgHandler.sendObserveCompositeRequest(lwM2MClient, request, mainCallback); + } private void sendWriteAttributesRequest(LwM2mClient lwM2MClient, String targetId, ObjectAttributes params) { TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(targetId).attributes(params).timeout(clientContext.getRequestTimeout(lwM2MClient)).build(); @@ -1024,4 +1046,14 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl } } + private Map groupByObjectIdVersionedIds(Set targetIds){ + return targetIds.stream() + .collect(Collectors.groupingBy( + id -> new LwM2mPath(fromVersionedIdToObjectId(id)).getObjectId(), + Collectors.collectingAndThen( + Collectors.toList(), + list -> list.toArray(new String[0]) + ) + )); + } } From d0e56d860ce1ae1c81233d686a7ae5cb9fc9d5a7 Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Tue, 29 Apr 2025 22:04:18 +0300 Subject: [PATCH 092/335] UI: Add unit definitions; add service map unit service --- ui-ngx/src/app/core/services/public-api.ts | 1 + .../app/core/services/unit/definitions/all.ts | 16 ++ .../core/services/unit/definitions/energy.ts | 145 ++++++++++++++++++ .../core/services/unit/definitions/force.ts | 81 ++++++++++ .../services/unit/definitions/frequency.ts | 76 +++++++++ .../services/unit/definitions/illuminance.ts | 61 ++++++++ .../app/modules/home/models/services.map.ts | 2 + .../assets/locale/locale.constant-en_US.json | 11 ++ 8 files changed, 393 insertions(+) create mode 100644 ui-ngx/src/app/core/services/unit/definitions/energy.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/force.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/frequency.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/illuminance.ts diff --git a/ui-ngx/src/app/core/services/public-api.ts b/ui-ngx/src/app/core/services/public-api.ts index 073eefd5e8..f4e770bdb5 100644 --- a/ui-ngx/src/app/core/services/public-api.ts +++ b/ui-ngx/src/app/core/services/public-api.ts @@ -15,6 +15,7 @@ /// export * from './script/node-script-test.service'; +export * from './unit/unit.service'; export * from './broadcast.models'; export * from './broadcast.service'; export * from './dashboard-utils.service'; diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts index 9be86f61e5..7b8d838e45 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/all.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/all.ts @@ -22,6 +22,10 @@ import area, { AreaUnits } from '@core/services/unit/definitions/area'; import charge, { ChargeUnits } from '@core/services/unit/definitions/charge'; import digital, { DigitalUnits } from '@core/services/unit/definitions/digital'; import electricCurrent, { ElectricCurrentUnits } from '@core/services/unit/definitions/electric-current'; +import energy, { EnergyUnits } from '@core/services/unit/definitions/energy'; +import force, { ForceUnits } from '@core/services/unit/definitions/force'; +import frequency, { FrequencyUnits } from '@core/services/unit/definitions/frequency'; +import illuminance,{ IlluminanceUnits } from '@core/services/unit/definitions/illuminance'; import temperature, { TemperatureUnits } from './temperature'; import time, { TimeUnits } from './time'; @@ -33,6 +37,10 @@ export type AllMeasuresUnits = | ChargeUnits | DigitalUnits | ElectricCurrentUnits + | EnergyUnits + | ForceUnits + | FrequencyUnits + | IlluminanceUnits | TemperatureUnits | TimeUnits; @@ -44,6 +52,10 @@ export type AllMeasures = | 'charge' | 'digital' | 'electric-current' + | 'energy' + | 'force' + | 'frequency' + | 'illuminance' | 'temperature' | 'time'; @@ -58,6 +70,10 @@ const allMeasures: Record< charge, digital, 'electric-current': electricCurrent, + energy, + force, + frequency, + illuminance, temperature, time, }; diff --git a/ui-ngx/src/app/core/services/unit/definitions/energy.ts b/ui-ngx/src/app/core/services/unit/definitions/energy.ts new file mode 100644 index 0000000000..1ec341f565 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/energy.ts @@ -0,0 +1,145 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type EnergyUnits = EnergyMetricUnits | EnergyImperialUnits; + +export type EnergyMetricUnits = + | 'Wm' + | 'Wh' + | 'mWh' + | 'kWh' + | 'MWh' + | 'GWh' + | 'μJ' + | 'mJ' + | 'J' + | 'kJ' + | 'MJ' + | 'GJ' + | 'eV'; + +export type EnergyImperialUnits = 'kcal' | 'cal' | 'Cal' | 'BTU' | 'ft·lb'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 4.184, + units: { + Wm: { + name: 'unit.watt-minute', + tags: ['energy', 'watt-minute', 'watt-minutes', 'Wm'], + to_anchor: 60, + }, + Wh: { + name: 'unit.watt-hour', + tags: ['energy', 'watt-hour', 'watt-hours', 'energy usage', 'power consumption', 'energy consumption', 'electricity usage'], + to_anchor: 3600, + }, + mWh: { + name: 'unit.milliwatt-hour', + tags: ['energy', 'milliwatt-hour', 'milliwatt-hours', 'mWh'], + to_anchor: 3.6, + }, + kWh: { + name: 'unit.kilowatt-hour', + tags: ['energy', 'kilowatt-hour', 'kilowatt-hours', 'energy usage', 'power consumption', 'energy consumption', 'electricity usage'], + to_anchor: 3600000, + }, + MWh: { + name: 'unit.megawatt-hour', + tags: ['energy', 'megawatt-hour', 'megawatt-hours', 'MWh'], + to_anchor: 3600000000, + }, + GWh: { + name: 'unit.gigawatt-hour', + tags: ['energy', 'gigawatt-hour', 'gigawatt-hours', 'GWh'], + to_anchor: 3600000000000, + }, + μJ: { + name: 'unit.microjoule', + tags: ['energy', 'microjoule', 'microjoules', 'μJ'], + to_anchor: 0.000001, + }, + mJ: { + name: 'unit.millijoule', + tags: ['energy', 'millijoule', 'millijoules', 'mJ'], + to_anchor: 0.001, + }, + J: { + name: 'unit.joule', + tags: ['joule', 'joules', 'energy', 'work done', 'heat', 'electricity', 'mechanical work'], + to_anchor: 1, + }, + kJ: { + name: 'unit.kilojoule', + tags: ['energy', 'kilojoule', 'kilojoules', 'kJ'], + to_anchor: 1000, + }, + MJ: { + name: 'unit.megajoule', + tags: ['energy', 'megajoule', 'megajoules', 'MJ'], + to_anchor: 1000000, + }, + GJ: { + name: 'unit.gigajoule', + tags: ['energy', 'gigajoule', 'gigajoules', 'GJ'], + to_anchor: 1000000000, + }, + eV: { + name: 'unit.electron-volts', + tags: ['energy', 'subatomic particles', 'radiation'], + to_anchor: 1.602176634e-19, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 4.184, + units: { + cal: { + name: 'unit.small-calorie', + tags: ['energy', 'small calorie', 'calories', 'cal'], + to_anchor: 1, + }, + Cal: { + name: 'unit.calorie', + tags: ['energy', 'food energy', 'Calorie', 'Calories', 'Cal'], + to_anchor: 1000, + }, + kcal: { + name: 'unit.kilocalorie', + tags: ['energy', 'small calorie', 'kilocalories', 'kcal'], + to_anchor: 1000, + }, + BTU: { + name: 'british-thermal-unit', + tags: ['energy', 'heat', 'work done', 'british thermal unit', 'british thermal units', 'BTU'], + to_anchor: 252.164401, + }, + 'ft·lb': { + name: 'unit.foot-pound', + tags: ['energy', 'foot-pound', 'foot-pounds', 'ft·lb', 'ft⋅lbf'], + to_anchor: 0.32404875717017, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/force.ts b/ui-ngx/src/app/core/services/unit/definitions/force.ts new file mode 100644 index 0000000000..46ee193829 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/force.ts @@ -0,0 +1,81 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ForceUnits = ForceMetricUnits | ForceImperialUnits; + +export type ForceMetricUnits = 'N' | 'kN' | 'dyn'; +export type ForceImperialUnits = 'lbf' | 'kgf' | 'klbf' | 'pdl' | 'kip'; + +const METRIC: TbMeasureUnits = { + ratio: 0.224809, + units: { + N: { + name: 'unit.newton', + tags: ['force', 'pressure', 'newton', 'newtons', 'N', 'push', 'pull', 'weight', 'gravity', 'N'], + to_anchor: 1, + }, + kN: { + name: 'unit.kilonewton', + tags: ['force', 'kN'], + to_anchor: 1000, + }, + dyn: { + name: 'unit.dyne', + tags: ['force', 'dyn'], + to_anchor: 0.00001, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 4.44822, + units: { + lbf: { + name: 'unit.pound-force', + tags: ['force', 'lbf'], + to_anchor: 1, + }, + kgf: { + name: 'unit.kilogram-force', + tags: ['force', 'kgf'], + to_anchor: 2.20462, + }, + klbf: { + name: 'unit.kilopound-force', + tags: ['force', 'klbf'], + to_anchor: 1000, + }, + pdl: { + name: 'unit.poundal', + tags: ['force', 'pdl'], + to_anchor: 0.031081, + }, + kip: { + name: 'unit.kip', + tags: ['force', 'kip'], + to_anchor: 1000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/frequency.ts b/ui-ngx/src/app/core/services/unit/definitions/frequency.ts new file mode 100644 index 0000000000..ecff651b78 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/frequency.ts @@ -0,0 +1,76 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type FrequencyUnits = FrequencyMetricUnits; +export type FrequencyMetricUnits = 'mHz' | 'Hz' | 'kHz' | 'MHz' | 'GHz' | 'THz' | 'rpm' | 'deg/s' | 'rad/s'; + +const METRIC: TbMeasureUnits = { + units: { + mHz: { + name: 'unit.millihertz', + tags: ['frequency', 'cycles per second', 'millihertz', 'mHz'], + to_anchor: 1 / 1000, + }, + Hz: { + name: 'unit.hertz', + tags: ['frequency', 'cycles per second', 'hertz', 'Hz'], + to_anchor: 1, + }, + kHz: { + name: 'unit.kilohertz', + tags: ['frequency', 'cycles per second', 'kilohertz', 'kHz'], + to_anchor: 1000, + }, + MHz: { + name: 'unit.megahertz', + tags: ['frequency', 'cycles per second', 'megahertz', 'MHz'], + to_anchor: 1000 * 1000, + }, + GHz: { + name: 'unit.gigahertz', + tags: ['frequency', 'cycles per second', 'gigahertz', 'GHz'], + to_anchor: 1000 * 1000 * 1000, + }, + THz: { + name: 'unit.terahertz', + tags: ['frequency', 'terahertz', 'THz'], + to_anchor: 1000 * 1000 * 1000 * 1000, + }, + rpm: { + name: 'unit.rotation-per-minute', + tags: ['frequency', 'rotation per minute', 'rotations per minute', 'rpm', 'angular velocity'], + to_anchor: 1 / 60, + }, + 'deg/s': { + name: 'unit.deg-per-second', + tags: ['angular velocity', 'degrees per second', 'deg/s'], + to_anchor: 1 / 360, // 1 deg/s = 1/360 Hz + }, + 'rad/s': { + name: 'unit.radian-per-second', + tags: ['angular velocity', 'rotation speed', 'rad/s'], + to_anchor: 1 / (Math.PI * 2), + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/illuminance.ts b/ui-ngx/src/app/core/services/unit/definitions/illuminance.ts new file mode 100644 index 0000000000..6c1ae4d43d --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/illuminance.ts @@ -0,0 +1,61 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type IlluminanceUnits = IlluminanceMetricUnits | IlluminanceImperialUnits; + +export type IlluminanceMetricUnits = 'lx' | 'cd/m²' | 'lm/m²'; +export type IlluminanceImperialUnits = 'fc'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 10.76391, + units: { + lx: { + name: 'unit.lux', + tags: ['illumination', 'light level on a surface', 'illuminance', 'Lux', 'lx'], + to_anchor: 1, + }, + 'cd/m²': { + name: 'unit.candela-per-square-meter', + tags: ['brightness', 'light level', 'Luminance', 'Candela per square meter', 'cd/m²'], + to_anchor: 1, + }, + 'lm/m²': { + name: 'unit.lumen-per-square-meter', + tags: ['illumination', 'light level', 'lumen per square meter', 'lm/m²'], + to_anchor: 1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 10.76391, + units: { + 'fc': { + name: 'unit.foot-candle', + tags: ['illuminance', 'light level', 'foot-candle', 'fc'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index e4bbc15936..481e070b90 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -51,6 +51,7 @@ import { TenantProfileService } from '@core/http/tenant-profile.service'; import { UiSettingsService } from '@core/http/ui-settings.service'; import { UsageInfoService } from '@core/http/usage-info.service'; import { EventService } from '@core/http/event.service'; +import { UnitService } from '@core/services/unit/unit.service'; import { AuditLogService } from '@core/http/audit-log.service'; export const ServicesMap = new Map>( @@ -91,6 +92,7 @@ export const ServicesMap = new Map>( ['usageInfoService', UsageInfoService], ['notificationService', NotificationService], ['eventService', EventService], + ['unitService', UnitService], ['auditLogService', AuditLogService] ] ); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 68c160a9ca..a40481ea51 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5872,6 +5872,10 @@ "charge": "Charge", "digital": "Digital", "electric-current": "Electric current", + "energy": "Energy", + "force": "Force", + "frequency": "Frequency", + "illuminance": "Illuminance", "temperature": "Temperature", "time": "Time" }, @@ -5984,7 +5988,11 @@ "megajoule": "Megajoule", "gigajoule": "Gigajoule", "watt-hour": "Watt-hour", + "watt-minute": "Watt-minute", "kilowatt-hour": "Kilowatt-hour", + "milliwatt-hour": "Milliwatt-hour", + "megawatt-hour": "Megawatt-hour", + "gigawatt-hour": "Gigawatt-hour", "electron-volts": "Electron volts", "joules-per-coulomb": "Joules per Coulomb", "british-thermal-unit": "British Thermal Units", @@ -6087,10 +6095,12 @@ "kilohm": "Kilohm", "megohm": "Megohm", "gigohm": "Gigohm", + "millihertz": "Millihertz", "hertz": "Hertz", "kilohertz": "Kilohertz", "megahertz": "Megahertz", "gigahertz": "Gigahertz", + "terahertz": "Terahertz", "rpm": "Revolutions Per Minute", "candela-per-square-meter": "Candela per square meter", "candela": "Candela", @@ -6290,6 +6300,7 @@ "radian-per-second-squared": "Radian per second squared", "revolutions-per-minute-per-second": "Angular acceleration", "deg-per-second": "deg/s", + "rotation-per-minute": "Rotation per minute", "degrees-brix": "Degrees Brix", "katal": "Katal", "katal-per-cubic-metre": "Katal per Cubic Metre" From 8c3f56da25e686a6c6d981c30c4cf1cad9a4c9ef Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Tue, 29 Apr 2025 23:02:13 +0300 Subject: [PATCH 093/335] UI: Add unit definitions --- .../app/core/services/unit/definitions/all.ts | 8 + .../core/services/unit/definitions/angle.ts | 6 +- .../core/services/unit/definitions/length.ts | 183 ++++++++++++++++++ .../core/services/unit/definitions/mass.ts | 121 ++++++++++++ .../assets/locale/locale.constant-en_US.json | 6 +- 5 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 ui-ngx/src/app/core/services/unit/definitions/length.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/mass.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts index 7b8d838e45..57ca762686 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/all.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/all.ts @@ -26,6 +26,8 @@ import energy, { EnergyUnits } from '@core/services/unit/definitions/energy'; import force, { ForceUnits } from '@core/services/unit/definitions/force'; import frequency, { FrequencyUnits } from '@core/services/unit/definitions/frequency'; import illuminance,{ IlluminanceUnits } from '@core/services/unit/definitions/illuminance'; +import length, { LengthUnits } from '@core/services/unit/definitions/length'; +import mass, { MassUnits } from '@core/services/unit/definitions/mass'; import temperature, { TemperatureUnits } from './temperature'; import time, { TimeUnits } from './time'; @@ -41,6 +43,8 @@ export type AllMeasuresUnits = | ForceUnits | FrequencyUnits | IlluminanceUnits + | LengthUnits + | MassUnits | TemperatureUnits | TimeUnits; @@ -56,6 +60,8 @@ export type AllMeasures = | 'force' | 'frequency' | 'illuminance' + | 'length' + | 'mass' | 'temperature' | 'time'; @@ -74,6 +80,8 @@ const allMeasures: Record< force, frequency, illuminance, + length, + mass, temperature, time, }; diff --git a/ui-ngx/src/app/core/services/unit/definitions/angle.ts b/ui-ngx/src/app/core/services/unit/definitions/angle.ts index 05a7593b03..5898034341 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/angle.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/angle.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type AngleUnits = AngleMetricUnits; -export type AngleMetricUnits = 'rad' | 'deg' | 'grad' | 'arcmin' | 'arcsec' | 'mil' | 'rev'; +export type AngleMetricUnits = 'rad' | 'deg' | 'grad' | 'arcmin' | 'arcsec' | 'mrad' | 'rev'; const METRIC: TbMeasureUnits = { units: { @@ -47,8 +47,8 @@ const METRIC: TbMeasureUnits = { tags: ['angle', 'arcsecond', 'arcseconds', 'arcsec'], to_anchor: 1 / 3600 }, - mil: { - name: 'unit.mil', + mrad: { + name: 'unit.milliradian', tags: ['angle', 'military angle', 'angular mil', 'mil'], to_anchor: 9 / (50 * Math.PI), }, diff --git a/ui-ngx/src/app/core/services/unit/definitions/length.ts b/ui-ngx/src/app/core/services/unit/definitions/length.ts new file mode 100644 index 0000000000..4a13c73bb1 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/length.ts @@ -0,0 +1,183 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LengthUnits = LengthMetricUnits | LengthImperialUnits; + +export type LengthMetricUnits = 'nm' | 'μm' | 'mm' | 'cm' | 'dm' | 'm' | 'km' | 'angstrom'; +export type LengthImperialUnits = + | 'in' + | 'yd' + | 'ft-us' + | 'ft' + | 'fathom' + | 'mi' + | 'nmi' + | 'thou' + | 'barleycorn' + | 'hand' + | 'ch' + | 'fur' + | 'league' + | 'link' + | 'rod' + | 'cable' + | 'AU'; + +const METRIC: TbMeasureUnits = { + ratio: 3.28084, + units: { + nm: { + name: 'unit.nanometer', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'nanoscale', 'atomic scale', 'molecular scale', 'nanometer', 'nanometers', 'nm'], + to_anchor: 1e-9, + }, + μm: { + name: 'unit.micrometer', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'microns', 'micrometer', 'micrometers', 'µm'], + to_anchor: 1e-6, + }, + mm: { + name: 'unit.millimeter', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'millimeter', 'millimeters', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition', 'mm'], + to_anchor: 1e-3, + }, + cm: { + name: 'unit.centimeter', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'centimeter', 'centimeters', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition', 'cm'], + to_anchor: 1e-2, + }, + dm: { + name: 'unit.decimeter', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'decimeter', 'decimeters', 'dm'], + to_anchor: 1e-1, + }, + m: { + name: 'unit.meter', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'meter', 'meters', 'm'], + to_anchor: 1, + }, + km: { + name: 'unit.kilometer', + tags: ['distance', 'height', 'length', 'width', 'gap', 'depth', 'kilometer', 'kilometers', 'km'], + to_anchor: 1e3, + }, + angstrom: { + name: 'unit.angstrom', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'atomic scale', 'atomic distance', 'nanoscale', 'angstrom', 'angstroms', 'Å'], + to_anchor: 1e-10, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 3.28084, + units: { + in: { + name: 'unit.inch', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'inch', 'inches', 'in'], + to_anchor: 1 / 12, + }, + yd: { + name: 'unit.yard', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'yard', 'yards', 'yd'], + to_anchor: 3, + }, + 'ft-us': { + name: 'unit.foot-us', + tags: ['length', 'us survey foot', 'us survey feet', 'ft-us', 'surveying'], + to_anchor: 1.000002, + }, + ft: { + name: 'unit.foot', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'foot', 'feet', 'ft'], + to_anchor: 1, + }, + fathom: { + name: 'unit.fathom', + tags: ['depth', 'nautical measurement', 'fathom'], + to_anchor: 6, + }, + mi: { + name: 'unit.mile', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'mile', 'miles', 'mi'], + to_anchor: 5280, + }, + nmi: { + name: 'unit.nautical-mile', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'nautical mile', 'nmi'], + to_anchor: 6076.12, + }, + thou: { + name: 'unit.thou', + tags: ['length', 'measurement', 'thou'], + to_anchor: 0.001 / 12, + }, + barleycorn: { + name: 'unit.barleycorn', + tags: ['length', 'shoe size', 'barleycorn'], + to_anchor: 1 / 36, + }, + hand: { + name: 'unit.hand', + tags: ['length', 'horse measurement', 'hand'], + to_anchor: 4 / 12, + }, + ch: { + name: 'unit.chain', + tags: ['length', 'land surveying', 'ch'], + to_anchor: 66, + }, + fur: { + name: 'unit.furlong', + tags: ['length', 'land surveying', 'fur'], + to_anchor: 660, + }, + league: { + name: 'unit.league', + tags: ['length', 'historical measurement', 'league'], + to_anchor: 3 * 5280, + }, + link: { + name: 'unit.link', + tags: ['length', 'land surveying', 'link'], + to_anchor: 0.66, + }, + rod: { + name: 'unit.rod', + tags: ['length', 'land surveying', 'rod'], + to_anchor: 16.5, + }, + cable: { + name: 'unit.cable', + tags: ['distance', 'nautical measurement', 'cable'], + to_anchor: 600, + }, + AU: { + name: 'unit.astronomical-unit', + tags: ['distance', 'celestial bodies', 'solar system', 'AU'], + to_anchor: 149597870700 * 3.28084, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/mass.ts b/ui-ngx/src/app/core/services/unit/definitions/mass.ts new file mode 100644 index 0000000000..41a9dde039 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/mass.ts @@ -0,0 +1,121 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MassUnits = MassMetricUnits | MassImperialUnits; + +export type MassMetricUnits = 'ng' | 'mcg' | 'mg' | 'g' | 'kg' | 't' | 'Da'; +export type MassImperialUnits = 'oz' | 'lb' | 'st' | 'short tons' | 'gr' | 'dr' | 'qr' | 'cwt' | 'slug'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 453.59237, + units: { + ng: { + name: 'unit.nanogram', + tags: ['mass', 'weight', 'heaviness', 'load', 'nanogram', 'nanograms', 'ng'], + to_anchor: 1e-9, + }, + mcg: { + name: 'unit.microgram', + tags: ['mass', 'weight', 'heaviness', 'load', 'μg', 'microgram'], + to_anchor: 1e-6, + }, + mg: { + name: 'unit.milligram', + tags: ['mass', 'weight', 'heaviness', 'load', 'milligram', 'miligrams', 'mg'], + to_anchor: 1e-3, + }, + g: { + name: 'unit.gram', + tags: ['mass', 'weight', 'heaviness', 'load', 'gram', 'grams', 'g'], + to_anchor: 1, + }, + kg: { + name: 'unit.kilogram', + tags: ['mass', 'weight', 'heaviness', 'load', 'kilogram', 'kilograms', 'kg'], + to_anchor: 1000, // 1 kg = 1000 g + }, + t: { + name: 'unit.tonne', + tags: ['mass', 'weight', 'heaviness', 'load', 'tonne', 'tons', 't'], + to_anchor: 1000000, + }, + Da: { + name: 'unit.dalton', + tags: ['atomic mass unit', 'AMU', 'unified atomic mass unit', 'dalton', 'Da'], + to_anchor: 1.66053906660e-24, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 453.59237, + units: { + oz: { + name: 'unit.ounce', + tags: ['mass', 'weight', 'heaviness', 'load', 'ounce', 'ounces', 'oz'], + to_anchor: 1 / 16, + }, + lb: { + name: 'unit.pound', + tags: ['mass', 'weight', 'heaviness', 'load', 'pound', 'pounds', 'lb'], + to_anchor: 1, + }, + st: { + name: 'unit.stone', + tags: ['mass', 'weight', 'heaviness', 'load', 'stone', 'stones', 'st'], + to_anchor: 14, + }, + 'short tons': { + name: 'unit.short-tons', + tags: ['mass', 'weight', 'heaviness', 'load', 'short ton', 'short tons'], + to_anchor: 2000, + }, + gr: { + name: 'unit.grain', + tags: ['mass', 'measurement', 'grain', 'gr'], + to_anchor: 1 / 7000, + }, + dr: { + name: 'unit.drachm', + tags: ['mass', 'measurement', 'drachm', 'dr'], + to_anchor: 1 / 256, + }, + qr: { + name: 'unit.quarter', + tags: ['mass', 'measurement', 'quarter', 'qr'], + to_anchor: 28, + }, + cwt: { + name: 'unit.hundredweight-countt', + tags: ['mass', 'weight', 'heaviness', 'load', 'hundredweight count', 'cwt'], + to_anchor: 100, + }, + slug: { + name: 'unit.slug', + tags: ['mass', 'measurement', 'slug'], + to_anchor: 32.174, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index a40481ea51..b0d6454782 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5876,11 +5876,14 @@ "force": "Force", "frequency": "Frequency", "illuminance": "Illuminance", + "length": "Length", + "mass": "Mass", "temperature": "Temperature", "time": "Time" }, "millimeter": "Millimeter", "centimeter": "Centimeter", + "decimeter": "Decimeter", "angstrom": "Angstrom", "nanometer": "Nanometer", "micrometer": "Micrometer", @@ -5888,6 +5891,7 @@ "kilometer": "Kilometer", "inch": "Inch", "foot": "Foot", + "foot-us": "Foot (US survey)", "yard": "Yard", "mile": "Mile", "nautical-mile": "Nautical Mile", @@ -6229,7 +6233,7 @@ "gradian": "Gradian", "arcminute": "Arcminute", "arcsecond": "Arcsecond", - "mil": "Mil", + "milliradian": "Milliradian", "revolution": "Revolution", "siemens": "Siemens", "millisiemens": "Millisiemens", From 215c5dbb966f8783271a5fa6be56073b64d76524 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 23 Apr 2025 15:44:37 +0300 Subject: [PATCH 094/335] Jobs --- .../job/CfReprocessingJobProcessor.java | 83 +++++++++++ .../server/service/job/DefaultJobManager.java | 136 +++++++++++++++++ .../server/service/job/JobManager.java | 24 +++ .../server/service/job/JobProcessor.java | 30 ++++ .../job/task/CfReprocessingTaskProcessor.java | 57 +++++++ .../src/main/resources/thingsboard.yml | 2 + .../src/test/resources/logback-test.xml | 2 +- .../server/dao/task/JobService.java | 35 +++++ .../server/common/data/EntityType.java | 3 +- .../common/data/id/EntityIdFactory.java | 2 + .../server/common/data/id/JobId.java | 38 +++++ .../job/CfReprocessingJobConfiguration.java | 39 +++++ .../data/job/CfReprocessingJobResult.java | 25 ++++ .../common/data/job/CfReprocessingTask.java | 53 +++++++ .../server/common/data/job/Job.java | 56 +++++++ .../common/data/job/JobConfiguration.java | 32 ++++ .../server/common/data/job/JobResult.java | 41 ++++++ .../server/common/data/job/JobStatus.java | 24 +++ .../server/common/data/job/JobType.java | 26 ++++ .../server/common/data/job/Task.java | 50 +++++++ .../server/common/data/job/TaskResult.java | 45 ++++++ common/proto/src/main/proto/queue.proto | 8 + .../queue/kafka/TbKafkaTopicConfigs.java | 5 + .../InMemoryMonolithQueueFactory.java | 22 ++- .../provider/KafkaMonolithQueueFactory.java | 51 +++++++ .../provider/KafkaTbCoreQueueFactory.java | 28 ++++ .../KafkaTbRuleEngineQueueFactory.java | 29 ++++ .../provider/TaskProcessorQueueFactory.java | 31 ++++ .../queue/provider/TbCoreQueueFactory.java | 7 + .../provider/TbRuleEngineQueueFactory.java | 2 +- .../server/queue/task/TaskProcessor.java | 139 ++++++++++++++++++ .../server/dao/model/ModelConstants.java | 10 ++ .../server/dao/model/sql/JobEntity.java | 96 ++++++++++++ .../server/dao/sql/task/JobRepository.java | 69 +++++++++ .../server/dao/sql/task/JpaJobDao.java | 72 +++++++++ .../server/dao/task/DefaultJobService.java | 86 +++++++++++ .../thingsboard/server/dao/task/JobDao.java | 33 +++++ .../main/resources/sql/schema-entities.sql | 11 ++ 38 files changed, 1498 insertions(+), 4 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java create mode 100644 application/src/main/java/org/thingsboard/server/service/job/JobManager.java create mode 100644 application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/JobId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobResult.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java diff --git a/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java new file mode 100644 index 0000000000..3b63d3736f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java @@ -0,0 +1,83 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ProfileEntityIdInfo; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.job.CfReprocessingJobConfiguration; +import org.thingsboard.server.common.data.job.CfReprocessingTask; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.job.Task; +import org.thingsboard.server.common.data.page.PageDataIterable; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.device.DeviceService; + +import java.util.function.Consumer; + +@Component +@RequiredArgsConstructor +public class CfReprocessingJobProcessor extends JobProcessor { + + private final DeviceService deviceService; + private final AssetService assetService; + + @Override + public void process(Job job, Consumer taskConsumer) { + CfReprocessingJobConfiguration configuration = job.getConfiguration(); + + CalculatedField calculatedField = configuration.getCalculatedField(); + EntityId entityId = calculatedField.getEntityId(); + + if (entityId.getEntityType().isOneOf(EntityType.DEVICE, EntityType.ASSET)) { + taskConsumer.accept(createTask(job, configuration, entityId)); + } else { + PageDataIterable entities; + if (entityId.getEntityType() == EntityType.DEVICE_PROFILE) { + entities = new PageDataIterable<>(pageLink -> deviceService.findProfileEntityIdInfosByTenantId(job.getTenantId(), pageLink), 512); + } else if (entityId.getEntityType() == EntityType.ASSET_PROFILE) { + entities = new PageDataIterable<>(pageLink -> assetService.findProfileEntityIdInfosByTenantId(job.getTenantId(), pageLink), 512); + } else { + throw new IllegalArgumentException("Unsupported CF entity type " + entityId.getEntityType()); + } + entities.forEach(device -> { + taskConsumer.accept(createTask(job, configuration, device.getEntityId())); + }); + } + } + + private Task createTask(Job job, CfReprocessingJobConfiguration configuration, EntityId entityId) { + return CfReprocessingTask.builder() + .tenantId(job.getTenantId()) + .jobId(job.getId()) + .key(entityId.getEntityType().getNormalName() + " " + entityId.getId()) + .calculatedField(configuration.getCalculatedField()) + .entityId(entityId) + .startTs(configuration.getStartTs()) + .endTs(configuration.getEndTs()) + .build(); + } + + @Override + public JobType getType() { + return JobType.CF_REPROCESSING; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java new file mode 100644 index 0000000000..73367e2af5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -0,0 +1,136 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job; + +import jakarta.annotation.PreDestroy; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.job.Task; +import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.dao.task.JobService; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; +import org.thingsboard.server.queue.TbQueueCallback; +import org.thingsboard.server.queue.TbQueueConsumer; +import org.thingsboard.server.queue.TbQueueMsgMetadata; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.common.consumer.QueueConsumerManager; +import org.thingsboard.server.queue.provider.TbCoreQueueFactory; +import org.thingsboard.server.queue.util.AfterStartUp; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Function; +import java.util.stream.Collectors; + +@TbCoreComponent +@Component +@Slf4j +public class DefaultJobManager implements JobManager { + + private final JobService jobService; + private final TbCoreQueueFactory queueFactory; + private final Map jobProcessors; + private final Map>> taskProducers; + private final QueueConsumerManager> taskResultConsumer; + private final ExecutorService consumerExecutor; + + public DefaultJobManager(JobService jobService, TbCoreQueueFactory queueFactory, List jobProcessors) { + this.jobService = jobService; + this.queueFactory = queueFactory; + this.jobProcessors = jobProcessors.stream().collect(Collectors.toMap(JobProcessor::getType, Function.identity())); + this.taskProducers = Arrays.stream(JobType.values()).collect(Collectors.toMap(Function.identity(), queueFactory::createTaskProducer)); + this.consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("task-result-consumer")); + this.taskResultConsumer = QueueConsumerManager.>builder() // fixme: should be consumer per partition + .name("tasks-results") + .msgPackProcessor(this::processResults) + .pollInterval(125) + .consumerCreator(queueFactory::createTaskResultConsumer) + .consumerExecutor(consumerExecutor) + .build(); + } + + @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) + public void afterStartUp() { + taskResultConsumer.subscribe(); + taskResultConsumer.launch(); + } + + @Override + public void submitJob(Job job) { + job = jobService.createJob(job.getTenantId(), job); + log.info("Submitting job: {}", job); + jobProcessors.get(job.getType()).process(job, this::submitTask); + } + + private void submitTask(Task task) { + log.info("Submitting task: {}", task); + TaskProto taskProto = TaskProto.newBuilder() + .setValue(JacksonUtil.toString(task)) + .build(); + + TbQueueProducer> producer = taskProducers.get(task.getJobType()); + TbProtoQueueMsg msg = new TbProtoQueueMsg<>(task.getTenantId().getId(), taskProto); // one job at a time for a given tenant + producer.send(TopicPartitionInfo.builder().topic(producer.getDefaultTopic()).build(), msg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + log.trace("Submitted task: {}", task); + } + + @Override + public void onFailure(Throwable t) { + log.warn("Failed to submit task: {}", task, t); + } + }); + } + + @SneakyThrows + private void processResults(List> msgs, TbQueueConsumer> consumer) { + Map> results = msgs.stream() + .map(msg -> JacksonUtil.fromString(msg.getValue().getValue(), TaskResult.class)) + .collect(Collectors.groupingBy(TaskResult::getJobId)); + results.forEach((jobId, taskResults) -> { + try { + log.info("[{}] Processing task results: {}", jobId, taskResults); + jobService.reportTaskResults(jobId, taskResults); + } catch (Exception e) { + log.warn("Failed to report task results for job {}: {}", jobId, taskResults, e); + } + }); + consumer.commit(); + + Thread.sleep(5000); + } + + @PreDestroy + private void destroy() { + taskResultConsumer.stop(); + consumerExecutor.shutdownNow(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java new file mode 100644 index 0000000000..5db52ef448 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job; + +import org.thingsboard.server.common.data.job.Job; + +public interface JobManager { + + void submitJob(Job job); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java new file mode 100644 index 0000000000..cf15209616 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job; + +import org.thingsboard.server.common.data.job.Task; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobType; + +import java.util.function.Consumer; + +public abstract class JobProcessor { + + public abstract void process(Job job, Consumer taskConsumer); + + public abstract JobType getType(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java new file mode 100644 index 0000000000..36899516f9 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job.task; + +import com.google.common.util.concurrent.SettableFuture; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.thingsboard.server.actors.calculatedField.CalculatedFieldReprocessingService; +import org.thingsboard.server.common.data.job.CfReprocessingTask; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.queue.task.TaskProcessor; + +import java.util.concurrent.TimeUnit; + +@Component +@RequiredArgsConstructor +public class CfReprocessingTaskProcessor extends TaskProcessor { + + private final CalculatedFieldReprocessingService cfReprocessingService; + + @Override + protected void process(CfReprocessingTask task) throws Exception { + SettableFuture future = SettableFuture.create(); + cfReprocessingService.reprocess(task, new TbCallback() { + @Override + public void onSuccess() { + future.set(null); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }); + future.get(1, TimeUnit.MINUTES); + } + + @Override + public JobType getJobType() { + return JobType.CF_REPROCESSING; + } + +} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 418263076b..6670140b30 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1667,6 +1667,8 @@ queue: edqs-requests: "${TB_QUEUE_KAFKA_EDQS_REQUESTS_TOPIC_PROPERTIES:retention.ms:180000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" # Kafka properties for EDQS state topic (infinite retention, compaction) edqs-state: "${TB_QUEUE_KAFKA_EDQS_STATE_TOPIC_PROPERTIES:retention.ms:-1;segment.bytes:52428800;retention.bytes:-1;partitions:1;min.insync.replicas:1;cleanup.policy:compact}" + # Kafka properties for tasks topics + tasks: "${TB_QUEUE_KAFKA_TASKS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" consumer-stats: # Prints lag between consumer group offset and last messages offset in Kafka topics enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index 13c93da411..a0efcf52c1 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -9,7 +9,7 @@ - + diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java new file mode 100644 index 0000000000..17e7645605 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.dao.task; + +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.job.Job; + +import java.util.List; + +public interface JobService { + + Job createJob(TenantId tenantId, Job job); + + void reportTaskResults(JobId jobId, List results); + + PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index 93e754eb2c..31cc983168 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -63,7 +63,8 @@ public enum EntityType { MOBILE_APP(37), MOBILE_APP_BUNDLE(38), CALCULATED_FIELD(39), - CALCULATED_FIELD_LINK(40); + CALCULATED_FIELD_LINK(40), + JOB(41); @Getter private final int protoNumber; // Corresponds to EntityTypeProto diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index f5dd4b12a0..dcf59a4ea4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -117,6 +117,8 @@ public class EntityIdFactory { return new CalculatedFieldId(uuid); case CALCULATED_FIELD_LINK: return new CalculatedFieldLinkId(uuid); + case JOB: + return new JobId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/JobId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/JobId.java new file mode 100644 index 0000000000..e6688f0eb0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/JobId.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +public class JobId extends UUIDBased implements EntityId { + + @JsonCreator + public JobId(@JsonProperty("id") UUID id) { + super(id); + } + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "string", example = "TASK", allowableValues = "TASK") + @Override + public EntityType getEntityType() { + return EntityType.JOB; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java new file mode 100644 index 0000000000..4f5a33dacd --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.cf.CalculatedField; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CfReprocessingJobConfiguration implements JobConfiguration { + + private CalculatedField calculatedField; + private long startTs; + private long endTs; + + @Override + public JobType getType() { + return JobType.CF_REPROCESSING; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobResult.java new file mode 100644 index 0000000000..2d756f6d53 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobResult.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +public class CfReprocessingJobResult extends JobResult { + + @Override + public JobType getJobType() { + return JobType.CF_REPROCESSING; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java new file mode 100644 index 0000000000..0e15b9473f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class CfReprocessingTask extends Task { + + private CalculatedField calculatedField; + private EntityId entityId; + private long startTs; + private long endTs; + + @Builder + public CfReprocessingTask(TenantId tenantId, JobId jobId, String key, CalculatedField calculatedField, EntityId entityId, long startTs, long endTs) { + super(tenantId, jobId, key); + this.calculatedField = calculatedField; + this.entityId = entityId; + this.startTs = startTs; + this.endTs = endTs; + } + + @Override + public JobType getJobType() { + return JobType.CF_REPROCESSING; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java new file mode 100644 index 0000000000..e0161cf98e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class Job extends BaseData implements HasTenantId { + + private TenantId tenantId; + private JobType type; + private String key; + private JobStatus status; + private JobConfiguration configuration; + private JobResult result; + + @Builder + public Job(TenantId tenantId, JobType type, String key, JobConfiguration configuration) { + this.tenantId = tenantId; + this.type = type; + this.key = key; + this.configuration = configuration; + this.status = JobStatus.PENDING; + this.result = switch (type) { + case CF_REPROCESSING -> new CfReprocessingJobResult(); + }; + } + + @SuppressWarnings("unchecked") + public C getConfiguration() { + return (C) configuration; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java new file mode 100644 index 0000000000..8899206c49 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @Type(name = "CF_REPROCESSING", value = CfReprocessingJobConfiguration.class), +}) +public interface JobConfiguration { + + JobType getType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java new file mode 100644 index 0000000000..406eb0f65e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") +@JsonSubTypes({ + @JsonSubTypes.Type(name = "CF_REPROCESSING", value = CfReprocessingJobResult.class), +}) +@Data +public abstract class JobResult { + + private int successfulCount; + private int failedCount; + private int totalCount; + private Map failures = new HashMap<>(); + + public abstract JobType getJobType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java new file mode 100644 index 0000000000..026e19c5b2 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +public enum JobStatus { + PENDING, + RUNNING, + COMPLETED, + FAILED, + CANCELLED +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java new file mode 100644 index 0000000000..60cac8173f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +public enum JobType { + + CF_REPROCESSING; + + public String getTasksTopic() { + return "tasks." + name().toLowerCase(); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java new file mode 100644 index 0000000000..afeaeba393 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") +@JsonSubTypes({ + @JsonSubTypes.Type(name = "CF_REPROCESSING", value = CfReprocessingTask.class), +}) +public abstract class Task { + + private TenantId tenantId; + private JobId jobId; + private String key; + + public Task(TenantId tenantId, JobId jobId, String key) { + this.tenantId = tenantId; + this.jobId = jobId; + this.key = key; + } + + public Task() { + } + + private int attempt = 0; + + public abstract JobType getJobType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java new file mode 100644 index 0000000000..bfbef46180 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class TaskResult { + + private TenantId tenantId; + private JobId jobId; + private boolean success; + private TaskFailure failure; + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class TaskFailure { + private String error; + private Task task; + } + +} diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 938a1692ae..de03b11b6a 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -1846,3 +1846,11 @@ message EdqsRequestMsg { message EdqsResponseMsg { string value = 1; } + +message TaskProto { + string value = 1; // fixme: TMP, make more efficient +} + +message TaskResultProto { + string value = 1; // fixme: TMP, make more efficient +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java index aebda5a5bc..5d5834d20a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java @@ -62,6 +62,8 @@ public class TbKafkaTopicConfigs { private String edqsRequestsProperties; @Value("${queue.kafka.topic-properties.edqs-state:}") private String edqsStateProperties; + @Value("${queue.kafka.topic-properties.tasks:}") + private String tasksProperties; @Getter private Map coreConfigs; @@ -99,6 +101,8 @@ public class TbKafkaTopicConfigs { private Map edqsRequestsConfigs; @Getter private Map edqsStateConfigs; + @Getter + private Map tasksConfigs; @PostConstruct private void init() { @@ -122,6 +126,7 @@ public class TbKafkaTopicConfigs { edqsEventsConfigs = PropertyUtils.getProps(edqsEventsProperties); edqsRequestsConfigs = PropertyUtils.getProps(edqsRequestsProperties); edqsStateConfigs = PropertyUtils.getProps(edqsStateProperties); + tasksConfigs = PropertyUtils.getProps(tasksProperties); } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java index 085d04f28c..c8866e52b4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; @@ -258,9 +259,28 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE .build(); } + @Override + public TbQueueProducer> createTaskProducer(JobType jobType) { + return new InMemoryTbQueueProducer<>(storage, jobType.getTasksTopic()); + } + + @Override + public TbQueueConsumer> createTaskConsumer(JobType jobType) { + return new InMemoryTbQueueConsumer<>(storage, jobType.getTasksTopic()); + } + + @Override + public TbQueueProducer> createTaskResultProducer() { + return new InMemoryTbQueueProducer<>(storage, "tasks.results"); + } + + @Override + public TbQueueConsumer> createTaskResultConsumer() { + return new InMemoryTbQueueConsumer<>(storage, "tasks.results"); + } + @Scheduled(fixedRateString = "${queue.in_memory.stats.print-interval-ms:60000}") private void printInMemoryStats() { storage.printStats(); } - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index 2269004f90..809099aa8b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -23,12 +23,15 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -110,6 +113,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi private final TbQueueAdmin cfStateAdmin; private final TbQueueAdmin edqsEventsAdmin; private final TbKafkaAdmin edqsRequestsAdmin; + private final TbQueueAdmin tasksAdmin; private final AtomicLong consumerCount = new AtomicLong(); private final AtomicLong edgeConsumerCount = new AtomicLong(); @@ -158,6 +162,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi this.cfStateAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCalculatedFieldStateConfigs()); this.edqsEventsAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getEdqsEventsConfigs()); this.edqsRequestsAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getEdqsRequestsConfigs()); + this.tasksAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTasksConfigs()); } @Override @@ -641,6 +646,52 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi .build(); } + @Override + public TbQueueProducer> createTaskProducer(JobType jobType) { + return TbKafkaProducerTemplate.>builder() + .clientId(jobType.name().toLowerCase() + "-task-producer-" + serviceInfoProvider.getServiceId()) + .defaultTopic(topicService.buildTopicName(jobType.getTasksTopic())) + .settings(kafkaSettings) + .admin(tasksAdmin) + .build(); + } + + @Override + public TbQueueConsumer> createTaskConsumer(JobType jobType) { + return TbKafkaConsumerTemplate.>builder() + .settings(kafkaSettings) + .topic(topicService.buildTopicName(jobType.getTasksTopic())) + .clientId(jobType.name().toLowerCase() + "-task-consumer-" + serviceInfoProvider.getServiceId()) + .groupId(topicService.buildTopicName(jobType.name().toLowerCase() + "-task-consumer-group")) + .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TaskProto.parseFrom(msg.getData()), msg.getHeaders())) + .admin(tasksAdmin) + .statsService(consumerStatsService) + .build(); + } + + @Override + public TbQueueProducer> createTaskResultProducer() { + return TbKafkaProducerTemplate.>builder() + .clientId("task-result-producer-" + serviceInfoProvider.getServiceId()) + .defaultTopic(topicService.buildTopicName("tasks.results")) + .settings(kafkaSettings) + .admin(tasksAdmin) + .build(); + } + + @Override + public TbQueueConsumer> createTaskResultConsumer() { + return TbKafkaConsumerTemplate.>builder() + .settings(kafkaSettings) + .topic(topicService.buildTopicName("tasks.results")) + .clientId("task-result-consumer-" + serviceInfoProvider.getServiceId()) + .groupId(topicService.buildTopicName("task-result-consumer-group")) + .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TaskResultProto.parseFrom(msg.getData()), msg.getHeaders())) + .admin(tasksAdmin) + .statsService(consumerStatsService) + .build(); + } + @PreDestroy private void destroy() { if (coreAdmin != null) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index ea7c56f0aa..71a9669ba4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -22,9 +22,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -105,6 +108,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { private final TbQueueAdmin cfAdmin; private final TbQueueAdmin edqsEventsAdmin; private final TbKafkaAdmin edqsRequestsAdmin; + private final TbQueueAdmin tasksAdmin; private final AtomicLong consumerCount = new AtomicLong(); private final AtomicLong edgeConsumerCount = new AtomicLong(); @@ -153,6 +157,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { this.cfAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCalculatedFieldConfigs()); this.edqsEventsAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getEdqsEventsConfigs()); this.edqsRequestsAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getEdqsRequestsConfigs()); + this.tasksAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTasksConfigs()); } @Override @@ -520,6 +525,29 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { .build(); } + @Override + public TbQueueProducer> createTaskProducer(JobType jobType) { + return TbKafkaProducerTemplate.>builder() + .clientId(jobType.name().toLowerCase() + "-task-producer-" + serviceInfoProvider.getServiceId()) + .defaultTopic(topicService.buildTopicName(jobType.getTasksTopic())) + .settings(kafkaSettings) + .admin(tasksAdmin) + .build(); + } + + @Override + public TbQueueConsumer> createTaskResultConsumer() { + return TbKafkaConsumerTemplate.>builder() + .settings(kafkaSettings) + .topic(topicService.buildTopicName("tasks.results")) + .clientId("task-result-consumer-" + serviceInfoProvider.getServiceId()) + .groupId(topicService.buildTopicName("task-result-consumer-group")) + .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.TaskResultProto.parseFrom(msg.getData()), msg.getHeaders())) + .admin(tasksAdmin) + .statsService(consumerStatsService) + .build(); + } + @PreDestroy private void destroy() { if (coreAdmin != null) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java index b4884ae72c..d0ac20ae06 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java @@ -22,12 +22,15 @@ import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.js.JsInvokeProtos; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -96,6 +99,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { private final TbQueueAdmin cfAdmin; private final TbQueueAdmin cfStateAdmin; private final TbQueueAdmin edqsEventsAdmin; + private final TbQueueAdmin tasksAdmin; private final AtomicLong consumerCount = new AtomicLong(); public KafkaTbRuleEngineQueueFactory(TopicService topicService, TbKafkaSettings kafkaSettings, @@ -133,6 +137,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { this.cfAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCalculatedFieldConfigs()); this.cfStateAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCalculatedFieldStateConfigs()); this.edqsEventsAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getEdqsEventsConfigs()); + this.tasksAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTasksConfigs()); } @Override @@ -414,6 +419,29 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { throw new UnsupportedOperationException(); } + @Override + public TbQueueConsumer> createTaskConsumer(JobType jobType) { + return TbKafkaConsumerTemplate.>builder() + .settings(kafkaSettings) + .topic(topicService.buildTopicName(jobType.getTasksTopic())) + .clientId(jobType.name().toLowerCase() + "-task-consumer-" + serviceInfoProvider.getServiceId()) + .groupId(topicService.buildTopicName(jobType.name().toLowerCase() + "-task-consumer-group")) + .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TaskProto.parseFrom(msg.getData()), msg.getHeaders())) + .admin(tasksAdmin) + .statsService(consumerStatsService) + .build(); + } + + @Override + public TbQueueProducer> createTaskResultProducer() { + return TbKafkaProducerTemplate.>builder() + .clientId("task-result-producer-" + serviceInfoProvider.getServiceId()) + .defaultTopic(topicService.buildTopicName("tasks.results")) + .settings(kafkaSettings) + .admin(tasksAdmin) + .build(); + } + @PreDestroy private void destroy() { if (coreAdmin != null) { @@ -438,4 +466,5 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { cfAdmin.destroy(); } } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java new file mode 100644 index 0000000000..10b84f0f65 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.queue.provider; + +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; +import org.thingsboard.server.queue.TbQueueConsumer; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; + +public interface TaskProcessorQueueFactory { + + TbQueueConsumer> createTaskConsumer(JobType jobType); + + TbQueueProducer> createTaskResultProducer(); + +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java index 037d1f2087..409348a12a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java @@ -17,7 +17,10 @@ package org.thingsboard.server.queue.provider; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.gen.js.JsInvokeProtos; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -165,4 +168,8 @@ public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory, Hous TbQueueProducer> createToCalculatedFieldNotificationMsgProducer(); + TbQueueProducer> createTaskProducer(JobType jobType); + + TbQueueConsumer> createTaskResultConsumer(); + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java index 18bb6db14a..83c467c992 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java @@ -41,7 +41,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; * Responsible for initialization of various Producers and Consumers used by TB Core Node. * Implementation Depends on the queue queue.type from yml or TB_QUEUE_TYPE environment variable */ -public interface TbRuleEngineQueueFactory extends TbUsageStatsClientQueueFactory, HousekeeperClientQueueFactory, EdqsClientQueueFactory { +public interface TbRuleEngineQueueFactory extends TbUsageStatsClientQueueFactory, HousekeeperClientQueueFactory, EdqsClientQueueFactory, TaskProcessorQueueFactory { /** * Used to push messages to instances of TB Transport Service diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java new file mode 100644 index 0000000000..7123eaf1e0 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -0,0 +1,139 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.queue.task; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.job.Task; +import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.data.job.TaskResult.TaskFailure; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; +import org.thingsboard.server.queue.TbQueueCallback; +import org.thingsboard.server.queue.TbQueueConsumer; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.common.consumer.QueueConsumerManager; +import org.thingsboard.server.queue.provider.TaskProcessorQueueFactory; +import org.thingsboard.server.queue.util.AfterStartUp; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Slf4j +public abstract class TaskProcessor { + + @Autowired + private TaskProcessorQueueFactory queueFactory; + + private QueueConsumerManager> taskConsumer; + private TbQueueProducer> taskResultProducer; + private ExecutorService consumerExecutor; + + @PostConstruct + public void init() { + consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-consumer")); + taskConsumer = QueueConsumerManager.>builder() // fixme: should be consumer per partition + .name(getJobType().name() + "-tasks") + .msgPackProcessor(this::processMsgs) + .pollInterval(125) + .consumerCreator(() -> queueFactory.createTaskConsumer(getJobType())) + .consumerExecutor(consumerExecutor) + .build(); + taskResultProducer = queueFactory.createTaskResultProducer(); + } + + @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) + public void afterStartUp() { + taskConsumer.subscribe(); + taskConsumer.launch(); + } + + @PreDestroy + public void destroy() { + taskConsumer.stop(); + consumerExecutor.shutdownNow(); + } + + private void processMsgs(List> msgs, TbQueueConsumer> consumer) { + for (TbProtoQueueMsg msg : msgs) { + TaskProto taskProto = msg.getValue(); + Task task = JacksonUtil.fromString(taskProto.getValue(), Task.class); + processTask((T) task); + } + consumer.commit(); + } + + private void processTask(T task) { + task.setAttempt(task.getAttempt() + 1); + log.info("Processing task: {}", task); + try { + process(task); + reportSuccess(task); + } catch (Exception e) { + log.error("Failed to process task (attempt {}): {}", task.getAttempt(), task, e); + if (task.getAttempt() < 3) { + processTask(task); + } else { + reportFailure(task, e); + } + } + } + + private void reportSuccess(Task task) { + TaskResult result = TaskResult.builder() + .tenantId(task.getTenantId()) + .jobId(task.getJobId()) + .success(true) + .build(); + reportResult(result); + } + + private void reportFailure(Task task, Throwable error) { + TaskResult result = TaskResult.builder() + .tenantId(task.getTenantId()) + .jobId(task.getJobId()) + .failure(TaskFailure.builder() + .error(error.getMessage()) + .task(task) + .build()) + .build(); + reportResult(result); + } + + private void reportResult(TaskResult result) { + log.info("Reporting result: {}", result); + TaskResultProto resultProto = TaskResultProto.newBuilder() + .setValue(JacksonUtil.toString(result)) + .build(); + TbProtoQueueMsg msg = new TbProtoQueueMsg<>(result.getJobId().getId(), resultProto); + taskResultProducer.send(TopicPartitionInfo.builder() + .topic(taskResultProducer.getDefaultTopic()) + .build(), msg, TbQueueCallback.EMPTY); + } + + protected abstract void process(T task) throws Exception; + + public abstract JobType getJobType(); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 148908d063..1d504b0ab2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -739,6 +739,16 @@ public class ModelConstants { public static final String CALCULATED_FIELD_LINK_ENTITY_ID = ENTITY_ID_COLUMN; public static final String CALCULATED_FIELD_LINK_CALCULATED_FIELD_ID = "calculated_field_id"; + /** + * Tasks constants. + */ + public static final String JOB_TABLE_NAME = "job"; + public static final String JOB_TYPE_PROPERTY = "type"; + public static final String JOB_KEY_PROPERTY = "key"; + public static final String JOB_STATUS_PROPERTY = "status"; + public static final String JOB_CONFIGURATION_PROPERTY = "configuration"; + public static final String JOB_RESULT_PROPERTY = "result"; + protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; protected static final String[] COUNT_AGGREGATION_COLUMNS = new String[]{count(LONG_VALUE_COLUMN), count(DOUBLE_VALUE_COLUMN), count(BOOLEAN_VALUE_COLUMN), count(STRING_VALUE_COLUMN), count(JSON_VALUE_COLUMN), max(TS_COLUMN)}; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java new file mode 100644 index 0000000000..6e8b3e7958 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.dao.model.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.JdbcType; +import org.hibernate.dialect.PostgreSQLJsonPGObjectJsonbType; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobConfiguration; +import org.thingsboard.server.common.data.job.JobResult; +import org.thingsboard.server.common.data.job.JobStatus; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.util.mapping.JsonConverter; + +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@Entity +@Table(name = ModelConstants.JOB_TABLE_NAME) +public class JobEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.TENANT_ID_PROPERTY, nullable = false) + private UUID tenantId; + + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.JOB_TYPE_PROPERTY, nullable = false) + private JobType type; + + @Column(name = ModelConstants.JOB_KEY_PROPERTY, nullable = false) + private String key; + + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.JOB_STATUS_PROPERTY, nullable = false) + private JobStatus status; + + @Convert(converter = JsonConverter.class) + @Column(name = ModelConstants.JOB_CONFIGURATION_PROPERTY, nullable = false) + private JsonNode configuration; + + @Convert(converter = JsonConverter.class) + @JdbcType(PostgreSQLJsonPGObjectJsonbType.class) + @Column(name = ModelConstants.JOB_RESULT_PROPERTY) + private JsonNode result; + + public JobEntity(Job job) { + super(job); + this.tenantId = getTenantUuid(job.getTenantId()); + this.type = job.getType(); + this.key = job.getKey(); + this.status = job.getStatus(); + this.configuration = toJson(job.getConfiguration()); + this.result = toJson(job.getResult()); + } + + @Override + public Job toData() { + Job job = new Job(); + job.setId(new JobId(id)); + job.setCreatedTime(createdTime); + job.setTenantId(getTenantId(tenantId)); + job.setType(type); + job.setKey(key); + job.setStatus(status); + job.setConfiguration(fromJson(configuration, JobConfiguration.class)); + job.setResult(fromJson(result, JobResult.class)); + return job; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java new file mode 100644 index 0000000000..273d8ef78a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.dao.sql.task; + +import jakarta.transaction.Transactional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.thingsboard.server.dao.model.sql.JobEntity; + +import java.util.UUID; + +@Repository +public interface JobRepository extends JpaRepository { + + Page findByTenantId(UUID tenantId, Pageable pageable); + + @Modifying + @Transactional + @Query(value = """ + UPDATE job + SET result = jsonb_set( + result, + '{successfulCount}', + to_jsonb((result->>'successfulCount')::int + :count) + ) + WHERE id = :jobId + RETURNING ((result->>'successfulCount')::int + :count) + + (result->>'failedCount')::int = (result->>'totalCount')::int + """, nativeQuery = true) + boolean reportTaskSuccess(@Param("jobId") UUID jobId, @Param("count") int count); + + @Modifying + @Transactional + @Query(value = """ + UPDATE job + SET result = jsonb_set( + jsonb_set( + result, + '{failedCount}', + to_jsonb((result->>'failedCount')::int + 1) + ), + ARRAY['failures', :taskKey], + to_jsonb(:error) + ) + WHERE id = :jobId + RETURNING ((result->>'failedCount')::int + 1) + (result->>'successfulCount')::int + = (result->>'totalCount')::int + """, nativeQuery = true) + boolean reportTaskFailure(@Param("jobId") UUID jobId, @Param("taskKey") String taskKey, @Param("error") String error); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java new file mode 100644 index 0000000000..d6286e2d77 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.dao.sql.task; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.JobEntity; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.task.JobDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.UUID; + +@Component +@SqlDao +@RequiredArgsConstructor +public class JpaJobDao extends JpaAbstractDao implements JobDao { + + private final JobRepository jobRepository; + + @Override + public PageData findByTenantId(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(jobRepository.findByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); + } + + @Override + public boolean reportTaskSuccess(JobId jobId, int tasksCount) { + return jobRepository.reportTaskSuccess(jobId.getId(), tasksCount); + } + + @Override + public boolean reportTaskFailure(JobId jobId, String taskKey, String error) { + return jobRepository.reportTaskFailure(jobId.getId(), taskKey, error); + } + + @Override + public EntityType getEntityType() { + return EntityType.JOB; + } + + @Override + protected Class getEntityClass() { + return JobEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return jobRepository; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java new file mode 100644 index 0000000000..aa9e48600c --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.dao.task; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobResult; +import org.thingsboard.server.common.data.job.JobStatus; +import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.data.job.TaskResult.TaskFailure; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Slf4j +public class DefaultJobService implements JobService { + + private final JobDao jobDao; + + @Override + public Job createJob(TenantId tenantId, Job job) { + return jobDao.save(tenantId, job); + } + + @Override + public void reportTaskResults(JobId jobId, List results) { + Job job = jobDao.findById(TenantId.SYS_TENANT_ID, jobId.getId()); + switch (job.getStatus()) { + case PENDING -> { + job.setStatus(JobStatus.RUNNING); + } + case CANCELLED, COMPLETED, FAILED -> { + // got some stale stats + return; + } + } + + JobResult jobResult = job.getResult(); + for (TaskResult taskResult : results) { + if (taskResult.isSuccess()) { + jobResult.setSuccessfulCount(jobResult.getSuccessfulCount() + 1); + } else { + TaskFailure failure = taskResult.getFailure(); + String key = failure.getTask().getKey(); + jobResult.setFailedCount(jobResult.getFailedCount() + 1); + jobResult.getFailures().put(key, failure.getError()); + } + } + + if (jobResult.getSuccessfulCount() + jobResult.getFailedCount() >= jobResult.getTotalCount()) { + if (jobResult.getFailures().isEmpty()) { + job.setStatus(JobStatus.COMPLETED); + } else { + job.setStatus(JobStatus.FAILED); + } + } + log.info("Saving job {}", job); + jobDao.save(TenantId.SYS_TENANT_ID, job); + } + + @Override + public PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink) { + return jobDao.findByTenantId(tenantId, pageLink); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java b/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java new file mode 100644 index 0000000000..6a5b002aea --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.dao.task; + +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.Dao; + +public interface JobDao extends Dao { + + PageData findByTenantId(TenantId tenantId, PageLink pageLink); + + boolean reportTaskSuccess(JobId jobId, int tasksCount); + + boolean reportTaskFailure(JobId jobId, String taskKey, String error); + +} diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index b425550e7e..7fa31da5fb 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -948,3 +948,14 @@ CREATE TABLE IF NOT EXISTS cf_debug_event ( e_result varchar, e_error varchar ) PARTITION BY RANGE (ts); + +CREATE TABLE IF NOT EXISTS job ( + id uuid NOT NULL CONSTRAINT job_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid NOT NULL, + type varchar NOT NULL, + key varchar NOT NULL, + status varchar NOT NULL, + configuration varchar(1000) NOT NULL, + result jsonb +); From 290fba4819a807d0275b5fca21b84b520702b8f3 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 24 Apr 2025 15:32:22 +0300 Subject: [PATCH 095/335] Job stats, REST API, tests --- .../server/controller/JobController.java | 73 ++++++++++ .../job/CfReprocessingJobProcessor.java | 37 ++++-- .../server/service/job/DefaultJobManager.java | 64 ++++++--- .../server/service/job/DummyJobProcessor.java | 64 +++++++++ .../server/service/job/JobManager.java | 2 +- .../server/service/job/JobProcessor.java | 2 +- .../service/job/task/DummyTaskProcessor.java | 44 ++++++ .../src/main/resources/thingsboard.yml | 4 + .../server/service/job/JobManagerTest.java | 125 ++++++++++++++++++ .../server/dao/task/JobService.java | 10 +- .../job/CfReprocessingJobConfiguration.java | 2 + .../common/data/job/CfReprocessingTask.java | 16 +-- .../data/job/DummyJobConfiguration.java | 42 ++++++ .../common/data/job/DummyJobResult.java | 25 ++++ .../server/common/data/job/DummyTask.java | 40 ++++++ .../server/common/data/job/Job.java | 12 +- .../common/data/job/JobConfiguration.java | 1 + .../server/common/data/job/JobResult.java | 8 +- .../server/common/data/job/JobStats.java | 29 ++++ .../server/common/data/job/JobType.java | 3 +- .../server/common/data/job/Task.java | 15 ++- .../server/common/data/job/TaskResult.java | 4 - common/proto/src/main/proto/queue.proto | 7 + .../InMemoryMonolithQueueFactory.java | 9 +- .../provider/KafkaMonolithQueueFactory.java | 22 +-- .../provider/KafkaTbCoreQueueFactory.java | 37 +++++- .../KafkaTbRuleEngineQueueFactory.java | 10 +- .../provider/TaskProcessorQueueFactory.java | 4 +- .../queue/provider/TbCoreQueueFactory.java | 6 +- .../provider/TbCoreQueueProducerProvider.java | 8 ++ .../provider/TbQueueProducerProvider.java | 3 + .../TbRuleEngineProducerProvider.java | 8 ++ .../TbTransportQueueProducerProvider.java | 6 + .../TbVersionControlProducerProvider.java | 5 + .../server/queue/task/JobStatsService.java | 63 +++++++++ .../server/queue/task/TaskProcessor.java | 31 +---- .../server/dao/model/ModelConstants.java | 1 + .../server/dao/model/sql/JobEntity.java | 5 + .../server/dao/sql/task/JobRepository.java | 21 ++- .../server/dao/sql/task/JpaJobDao.java | 16 ++- .../server/dao/task/DefaultJobService.java | 40 +++++- .../thingsboard/server/dao/task/JobDao.java | 6 + .../main/resources/sql/schema-entities.sql | 1 + 43 files changed, 795 insertions(+), 136 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/controller/JobController.java create mode 100644 application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java create mode 100644 application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java new file mode 100644 index 0000000000..f7bc5255d3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -0,0 +1,73 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.controller; + +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.task.JobService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.UUID; + +import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; + +@RestController +@TbCoreComponent +@RequestMapping("/api") +@RequiredArgsConstructor +@Slf4j +public class JobController extends BaseController { + + private final JobService jobService; + + @GetMapping("/job/{id}") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public Job getJobById(@PathVariable UUID id) throws ThingsboardException { + return jobService.findJobById(getTenantId(), new JobId(id)); + } + + @GetMapping("/jobs") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public PageData getJobs(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @Parameter(description = "Case-insensitive 'substring' filter based on job's description") + @RequestParam(required = false) String textSearch, + @Parameter(description = SORT_PROPERTY_DESCRIPTION) + @RequestParam(required = false) String sortProperty, + @Parameter(description = SORT_ORDER_DESCRIPTION) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return jobService.findJobsByTenantId(getTenantId(), pageLink); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java index 3b63d3736f..b5f6c4665f 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java @@ -17,9 +17,11 @@ package org.thingsboard.server.service.job; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.ProfileEntityIdInfo; import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.job.CfReprocessingJobConfiguration; import org.thingsboard.server.common.data.job.CfReprocessingTask; @@ -39,28 +41,34 @@ public class CfReprocessingJobProcessor extends JobProcessor { private final DeviceService deviceService; private final AssetService assetService; + // fixme: multiple jobs with single type + @Transactional @Override - public void process(Job job, Consumer taskConsumer) { + public int process(Job job, Consumer taskConsumer) { CfReprocessingJobConfiguration configuration = job.getConfiguration(); CalculatedField calculatedField = configuration.getCalculatedField(); - EntityId entityId = calculatedField.getEntityId(); + EntityId cfEntityId = calculatedField.getEntityId(); - if (entityId.getEntityType().isOneOf(EntityType.DEVICE, EntityType.ASSET)) { - taskConsumer.accept(createTask(job, configuration, entityId)); + int tasksCount = 0; + if (cfEntityId.getEntityType().isOneOf(EntityType.DEVICE, EntityType.ASSET)) { + taskConsumer.accept(createTask(job, configuration, cfEntityId)); + tasksCount++; } else { - PageDataIterable entities; - if (entityId.getEntityType() == EntityType.DEVICE_PROFILE) { - entities = new PageDataIterable<>(pageLink -> deviceService.findProfileEntityIdInfosByTenantId(job.getTenantId(), pageLink), 512); - } else if (entityId.getEntityType() == EntityType.ASSET_PROFILE) { - entities = new PageDataIterable<>(pageLink -> assetService.findProfileEntityIdInfosByTenantId(job.getTenantId(), pageLink), 512); + PageDataIterable entities; + if (cfEntityId.getEntityType() == EntityType.DEVICE_PROFILE) { + entities = new PageDataIterable<>(pageLink -> deviceService.findDeviceIdsByTenantIdAndDeviceProfileId(job.getTenantId(), (DeviceProfileId) cfEntityId, pageLink), 512); + } else if (cfEntityId.getEntityType() == EntityType.ASSET_PROFILE) { + entities = new PageDataIterable<>(pageLink -> assetService.findAssetIdsByTenantIdAndAssetProfileId(job.getTenantId(), (AssetProfileId) cfEntityId, pageLink), 512); } else { - throw new IllegalArgumentException("Unsupported CF entity type " + entityId.getEntityType()); + throw new IllegalArgumentException("Unsupported CF entity type " + cfEntityId.getEntityType()); + } + for (EntityId entityId : entities) { + taskConsumer.accept(createTask(job, configuration, entityId)); + tasksCount++; } - entities.forEach(device -> { - taskConsumer.accept(createTask(job, configuration, device.getEntityId())); - }); } + return tasksCount; } private Task createTask(Job job, CfReprocessingJobConfiguration configuration, EntityId entityId) { @@ -68,6 +76,7 @@ public class CfReprocessingJobProcessor extends JobProcessor { .tenantId(job.getTenantId()) .jobId(job.getId()) .key(entityId.getEntityType().getNormalName() + " " + entityId.getId()) + .retries(2) // 3 attempts in total .calculatedField(configuration.getCalculatedField()) .entityId(entityId) .startTs(configuration.getStartTs()) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 73367e2af5..64c1615310 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -18,18 +18,20 @@ package org.thingsboard.server.service.job; import jakarta.annotation.PreDestroy; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.task.JobService; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; -import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueMsgMetadata; @@ -37,12 +39,15 @@ import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.consumer.QueueConsumerManager; import org.thingsboard.server.queue.provider.TbCoreQueueFactory; +import org.thingsboard.server.queue.task.JobStatsService; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Function; @@ -54,23 +59,26 @@ import java.util.stream.Collectors; public class DefaultJobManager implements JobManager { private final JobService jobService; - private final TbCoreQueueFactory queueFactory; + private final JobStatsService jobStatsService; private final Map jobProcessors; private final Map>> taskProducers; - private final QueueConsumerManager> taskResultConsumer; + private final QueueConsumerManager> taskResultConsumer; private final ExecutorService consumerExecutor; - public DefaultJobManager(JobService jobService, TbCoreQueueFactory queueFactory, List jobProcessors) { + @Value("${queue.tasks.stats.processing_interval_ms:5000}") + private int statsProcessingInterval; + + public DefaultJobManager(JobService jobService, JobStatsService jobStatsService, TbCoreQueueFactory queueFactory, List jobProcessors) { this.jobService = jobService; - this.queueFactory = queueFactory; + this.jobStatsService = jobStatsService; this.jobProcessors = jobProcessors.stream().collect(Collectors.toMap(JobProcessor::getType, Function.identity())); this.taskProducers = Arrays.stream(JobType.values()).collect(Collectors.toMap(Function.identity(), queueFactory::createTaskProducer)); this.consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("task-result-consumer")); - this.taskResultConsumer = QueueConsumerManager.>builder() // fixme: should be consumer per partition - .name("tasks-results") - .msgPackProcessor(this::processResults) + this.taskResultConsumer = QueueConsumerManager.>builder() + .name("job-stats") + .msgPackProcessor(this::processStats) .pollInterval(125) - .consumerCreator(queueFactory::createTaskResultConsumer) + .consumerCreator(queueFactory::createJobStatsConsumer) .consumerExecutor(consumerExecutor) .build(); } @@ -82,10 +90,13 @@ public class DefaultJobManager implements JobManager { } @Override - public void submitJob(Job job) { + public Job submitJob(Job job) { job = jobService.createJob(job.getTenantId(), job); log.info("Submitting job: {}", job); - jobProcessors.get(job.getType()).process(job, this::submitTask); + + int tasksCount = jobProcessors.get(job.getType()).process(job, this::submitTask); + jobStatsService.reportAllTasksSubmitted(job.getId(), tasksCount); + return job; } private void submitTask(Task task) { @@ -110,21 +121,34 @@ public class DefaultJobManager implements JobManager { } @SneakyThrows - private void processResults(List> msgs, TbQueueConsumer> consumer) { - Map> results = msgs.stream() - .map(msg -> JacksonUtil.fromString(msg.getValue().getValue(), TaskResult.class)) - .collect(Collectors.groupingBy(TaskResult::getJobId)); - results.forEach((jobId, taskResults) -> { + private void processStats(List> msgs, TbQueueConsumer> consumer) { + Map stats = new HashMap<>(); + + for (TbProtoQueueMsg msg : msgs) { + JobStatsMsg statsMsg = msg.getValue(); + JobId jobId = new JobId(new UUID(statsMsg.getJobIdMSB(), statsMsg.getJobIdLSB())); + JobStats jobStats = stats.computeIfAbsent(jobId, JobStats::new); + + if (statsMsg.hasTaskResult()) { + TaskResult taskResult = JacksonUtil.fromString(statsMsg.getTaskResult().getValue(), TaskResult.class); + jobStats.getTaskResults().add(taskResult); + } + if (statsMsg.hasTotalTasksCount()) { + jobStats.setTotalTasksCount(statsMsg.getTotalTasksCount()); + } + } + + stats.forEach((jobId, jobStats) -> { try { - log.info("[{}] Processing task results: {}", jobId, taskResults); - jobService.reportTaskResults(jobId, taskResults); + log.info("[{}] Processing job stats: {}", jobId, stats); + jobService.processStats(jobId, jobStats); } catch (Exception e) { - log.warn("Failed to report task results for job {}: {}", jobId, taskResults, e); + log.warn("Failed to process job stats for {}: {}", jobId, jobStats, e); } }); consumer.commit(); - Thread.sleep(5000); + Thread.sleep(statsProcessingInterval); } @PreDestroy diff --git a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java new file mode 100644 index 0000000000..bed8f3f25e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.job.DummyJobConfiguration; +import org.thingsboard.server.common.data.job.DummyTask; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.job.Task; + +import java.util.List; +import java.util.function.Consumer; + +@Component +@RequiredArgsConstructor +public class DummyJobProcessor extends JobProcessor { + + @Override + public int process(Job job, Consumer taskConsumer) { + DummyJobConfiguration configuration = job.getConfiguration(); + for (int number = 1; number <= configuration.getSuccessfulTasksCount(); number++) { + taskConsumer.accept(createTask(job, configuration, number, null)); + } + if (configuration.getErrors() != null) { + for (int number = 1; number <= configuration.getFailedTasksCount(); number++) { + taskConsumer.accept(createTask(job, configuration, number, configuration.getErrors())); + } + } + return configuration.getSuccessfulTasksCount() + configuration.getFailedTasksCount(); + } + + private Task createTask(Job job, DummyJobConfiguration configuration, int number, List errors) { + return DummyTask.builder() + .tenantId(job.getTenantId()) + .jobId(job.getId()) + .key("Task " + number) + .retries(configuration.getRetries()) + .number(number) + .processingTimeMs(configuration.getTaskProcessingTimeMs()) + .errors(errors) + .build(); + } + + @Override + public JobType getType() { + return JobType.DUMMY; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java index 5db52ef448..c78de2a5d9 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java @@ -19,6 +19,6 @@ import org.thingsboard.server.common.data.job.Job; public interface JobManager { - void submitJob(Job job); + Job submitJob(Job job); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java index cf15209616..01f7291dd3 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java @@ -23,7 +23,7 @@ import java.util.function.Consumer; public abstract class JobProcessor { - public abstract void process(Job job, Consumer taskConsumer); + public abstract int process(Job job, Consumer taskConsumer); public abstract JobType getType(); diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java new file mode 100644 index 0000000000..10aafc197d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job.task; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.job.DummyTask; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.queue.task.TaskProcessor; + +@Component +@RequiredArgsConstructor +public class DummyTaskProcessor extends TaskProcessor { + + @Override + protected void process(DummyTask task) throws Exception { + if (task.getProcessingTimeMs() > 0) { + Thread.sleep(task.getProcessingTimeMs()); + } + if (task.getErrors() != null && task.getAttempt() <= task.getErrors().size()) { + String error = task.getErrors().get(task.getAttempt() - 1); + throw new RuntimeException(error); + } + } + + @Override + public JobType getJobType() { + return JobType.DUMMY; + } + +} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 6670140b30..322b037e2d 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1883,6 +1883,10 @@ queue: enabled: "${TB_QUEUE_EDGE_STATS_ENABLED:true}" # Statistics printing interval for Edge services print-interval-ms: "${TB_QUEUE_EDGE_STATS_PRINT_INTERVAL_MS:60000}" + tasks: + stats: + # Interval in milliseconds to process job stats + processing_interval_ms: "${TB_QUEUE_TASKS_STATS_PROCESSING_INTERVAL_MS:5000}" # Event configuration parameters event: diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java new file mode 100644 index 0000000000..f9ee2e2b13 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -0,0 +1,125 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job; + +import com.fasterxml.jackson.core.type.TypeReference; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.job.DummyJobConfiguration; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobStatus; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +@DaoSqlTest +@TestPropertySource(properties = { + "queue.tasks.stats.processing_interval_ms=0" +}) +public class JobManagerTest extends AbstractControllerTest { + + @Autowired + private JobManager jobManager; + + @Before + public void setUp() throws Exception { + loginTenantAdmin(); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testSubmitJob_allTasksSuccessful() { + int tasksCount = 5; + JobId jobId = jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("test job") + .configuration(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(1000) + .build()) + .build()).getId(); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.RUNNING); + assertThat(job.getResult().getSuccessfulCount()).isBetween(1, tasksCount - 1); + assertThat(job.getResult().getTotalCount()).isEqualTo(tasksCount); + }); + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.COMPLETED); + assertThat(job.getResult().getSuccessfulCount()).isEqualTo(tasksCount); + assertThat(job.getResult().getFailures()).isEmpty(); + }); + } + + @Test + public void testSubmitJob_someTasksPermanentlyFailed() { + int successfulTasks = 3; + int failedTasks = 2; + JobId jobId = jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("test job") + .configuration(DummyJobConfiguration.builder() + .successfulTasksCount(successfulTasks) + .failedTasksCount(failedTasks) + .errors(List.of("error1", "error2", "error3")) + .retries(2) + .taskProcessingTimeMs(100) + .build()) + .build()).getId(); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); + assertThat(job.getResult().getSuccessfulCount()).isEqualTo(successfulTasks); + assertThat(job.getResult().getFailedCount()).isEqualTo(failedTasks); + assertThat(job.getResult().getTotalCount()).isEqualTo(successfulTasks + failedTasks); + assertThat(job.getResult().getFailures().get("Task 1")).isEqualTo("error3"); // last error + assertThat(job.getResult().getFailures().get("Task 2")).isEqualTo("error3"); // last error + }); + } + + + + private Job findJobById(JobId jobId) throws Exception { + return doGet("/api/job/" + jobId, Job.class); + } + + private List findJobs() throws Exception { + return doGetTypedWithPageLink("/api/jobs?", new TypeReference>() {}, new PageLink(100, 0)).getData(); + } + +} \ No newline at end of file diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java index 17e7645605..5581f1eac7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java @@ -17,18 +17,18 @@ package org.thingsboard.server.dao.task; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.job.Job; - -import java.util.List; public interface JobService { Job createJob(TenantId tenantId, Job job); - void reportTaskResults(JobId jobId, List results); + Job findJobById(TenantId tenantId, JobId jobId); + + void processStats(JobId jobId, JobStats jobStats); PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java index 4f5a33dacd..797dd0b639 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.job; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.cf.CalculatedField; @Builder public class CfReprocessingJobConfiguration implements JobConfiguration { + @NotNull private CalculatedField calculatedField; private long startTs; private long endTs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java index 0e15b9473f..5c380c4dfa 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java @@ -15,20 +15,17 @@ */ package org.thingsboard.server.common.data.job; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.JobId; -import org.thingsboard.server.common.data.id.TenantId; @Data -@AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode(callSuper = true) +@SuperBuilder public class CfReprocessingTask extends Task { private CalculatedField calculatedField; @@ -36,15 +33,6 @@ public class CfReprocessingTask extends Task { private long startTs; private long endTs; - @Builder - public CfReprocessingTask(TenantId tenantId, JobId jobId, String key, CalculatedField calculatedField, EntityId entityId, long startTs, long endTs) { - super(tenantId, jobId, key); - this.calculatedField = calculatedField; - this.entityId = entityId; - this.startTs = startTs; - this.endTs = endTs; - } - @Override public JobType getJobType() { return JobType.CF_REPROCESSING; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java new file mode 100644 index 0000000000..7fe621c058 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DummyJobConfiguration implements JobConfiguration { + + private long taskProcessingTimeMs; + private int successfulTasksCount; + private int failedTasksCount; + private List errors; + private int retries; + + @Override + public JobType getType() { + return JobType.DUMMY; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java new file mode 100644 index 0000000000..031a733d51 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +public class DummyJobResult extends JobResult { + + @Override + public JobType getJobType() { + return JobType.DUMMY; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java new file mode 100644 index 0000000000..dee97fc3c9 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@SuperBuilder +public class DummyTask extends Task { + + private int number; + private long processingTimeMs; + private List errors; // errors for each attempt + + @Override + public JobType getJobType() { + return JobType.DUMMY; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java index e0161cf98e..5a51a49239 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.job; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; @@ -29,22 +31,30 @@ import org.thingsboard.server.common.data.id.TenantId; @EqualsAndHashCode(callSuper = true) public class Job extends BaseData implements HasTenantId { + @NotNull private TenantId tenantId; + @NotNull private JobType type; + @NotBlank private String key; + @NotBlank + private String description; private JobStatus status; + @NotNull private JobConfiguration configuration; private JobResult result; @Builder - public Job(TenantId tenantId, JobType type, String key, JobConfiguration configuration) { + public Job(TenantId tenantId, JobType type, String key, String description, JobConfiguration configuration) { this.tenantId = tenantId; this.type = type; this.key = key; + this.description = description; this.configuration = configuration; this.status = JobStatus.PENDING; this.result = switch (type) { case CF_REPROCESSING -> new CfReprocessingJobResult(); + case DUMMY -> new DummyJobResult(); }; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java index 8899206c49..eccdfae6bb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @Type(name = "CF_REPROCESSING", value = CfReprocessingJobConfiguration.class), + @Type(name = "DUMMY", value = DummyJobConfiguration.class), }) public interface JobConfiguration { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index 406eb0f65e..07b6c4eadd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -17,8 +17,10 @@ package org.thingsboard.server.common.data.job; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; +import lombok.NoArgsConstructor; import java.util.HashMap; import java.util.Map; @@ -26,14 +28,16 @@ import java.util.Map; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @JsonSubTypes({ - @JsonSubTypes.Type(name = "CF_REPROCESSING", value = CfReprocessingJobResult.class), + @Type(name = "CF_REPROCESSING", value = CfReprocessingJobResult.class), + @Type(name = "DUMMY", value = DummyJobResult.class) }) @Data +@NoArgsConstructor public abstract class JobResult { private int successfulCount; private int failedCount; - private int totalCount; + private Integer totalCount = null; // set when all tasks are submitted private Map failures = new HashMap<>(); public abstract JobType getJobType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java new file mode 100644 index 0000000000..6491d0998a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import lombok.Data; +import org.thingsboard.server.common.data.id.JobId; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class JobStats { + private final JobId jobId; + private final List taskResults = new ArrayList<>(); + private Integer totalTasksCount; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java index 60cac8173f..7c0d9972e1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java @@ -17,7 +17,8 @@ package org.thingsboard.server.common.data.job; public enum JobType { - CF_REPROCESSING; + CF_REPROCESSING, + DUMMY; public String getTasksTopic() { return "tasks." + name().toLowerCase(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java index afeaeba393..5ef735f5d3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java @@ -17,8 +17,11 @@ package org.thingsboard.server.common.data.job; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.experimental.SuperBuilder; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; @@ -26,19 +29,17 @@ import org.thingsboard.server.common.data.id.TenantId; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @JsonSubTypes({ - @JsonSubTypes.Type(name = "CF_REPROCESSING", value = CfReprocessingTask.class), + @Type(name = "CF_REPROCESSING", value = CfReprocessingTask.class), + @Type(name = "DUMMY", value = DummyTask.class) }) +@SuperBuilder +@AllArgsConstructor public abstract class Task { private TenantId tenantId; private JobId jobId; private String key; - - public Task(TenantId tenantId, JobId jobId, String key) { - this.tenantId = tenantId; - this.jobId = jobId; - this.key = key; - } + private int retries; public Task() { } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java index bfbef46180..ae5e6bbba9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java @@ -19,8 +19,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.id.JobId; -import org.thingsboard.server.common.data.id.TenantId; @Data @AllArgsConstructor @@ -28,8 +26,6 @@ import org.thingsboard.server.common.data.id.TenantId; @Builder public class TaskResult { - private TenantId tenantId; - private JobId jobId; private boolean success; private TaskFailure failure; diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index de03b11b6a..032301206f 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -1851,6 +1851,13 @@ message TaskProto { string value = 1; // fixme: TMP, make more efficient } +message JobStatsMsg { + int64 jobIdMSB = 1; + int64 jobIdLSB = 2; + optional TaskResultProto taskResult = 3; + optional int32 totalTasksCount = 4; +} + message TaskResultProto { string value = 1; // fixme: TMP, make more efficient } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java index c8866e52b4..9160818278 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java @@ -28,6 +28,7 @@ import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToEdqsMsg; import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueConsumer; @@ -270,13 +271,13 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE } @Override - public TbQueueProducer> createTaskResultProducer() { - return new InMemoryTbQueueProducer<>(storage, "tasks.results"); + public TbQueueProducer> createJobStatsProducer() { + return new InMemoryTbQueueProducer<>(storage, "jobs.stats"); } @Override - public TbQueueConsumer> createTaskResultConsumer() { - return new InMemoryTbQueueConsumer<>(storage, "tasks.results"); + public TbQueueConsumer> createJobStatsConsumer() { + return new InMemoryTbQueueConsumer<>(storage, "jobs.stats"); } @Scheduled(fixedRateString = "${queue.in_memory.stats.print-interval-ms:60000}") diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index 809099aa8b..e4b51eaa1f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -30,8 +30,8 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; -import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -670,23 +670,23 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi } @Override - public TbQueueProducer> createTaskResultProducer() { - return TbKafkaProducerTemplate.>builder() - .clientId("task-result-producer-" + serviceInfoProvider.getServiceId()) - .defaultTopic(topicService.buildTopicName("tasks.results")) + public TbQueueProducer> createJobStatsProducer() { + return TbKafkaProducerTemplate.>builder() + .clientId("job-stats-producer-" + serviceInfoProvider.getServiceId()) + .defaultTopic(topicService.buildTopicName("jobs.stats")) .settings(kafkaSettings) .admin(tasksAdmin) .build(); } @Override - public TbQueueConsumer> createTaskResultConsumer() { - return TbKafkaConsumerTemplate.>builder() + public TbQueueConsumer> createJobStatsConsumer() { + return TbKafkaConsumerTemplate.>builder() .settings(kafkaSettings) - .topic(topicService.buildTopicName("tasks.results")) - .clientId("task-result-consumer-" + serviceInfoProvider.getServiceId()) - .groupId(topicService.buildTopicName("task-result-consumer-group")) - .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TaskResultProto.parseFrom(msg.getData()), msg.getHeaders())) + .topic(topicService.buildTopicName("jobs.stats")) + .clientId("job-stats-consumer-" + serviceInfoProvider.getServiceId()) + .groupId(topicService.buildTopicName("job-stats-consumer-group")) + .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), JobStatsMsg.parseFrom(msg.getData()), msg.getHeaders())) .admin(tasksAdmin) .statsService(consumerStatsService) .build(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index 71a9669ba4..85d1e8be14 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -25,8 +25,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; @@ -536,13 +536,36 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createTaskResultConsumer() { - return TbKafkaConsumerTemplate.>builder() + public TbQueueConsumer> createTaskConsumer(JobType jobType) { + return TbKafkaConsumerTemplate.>builder() .settings(kafkaSettings) - .topic(topicService.buildTopicName("tasks.results")) - .clientId("task-result-consumer-" + serviceInfoProvider.getServiceId()) - .groupId(topicService.buildTopicName("task-result-consumer-group")) - .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.TaskResultProto.parseFrom(msg.getData()), msg.getHeaders())) + .topic(topicService.buildTopicName(jobType.getTasksTopic())) + .clientId(jobType.name().toLowerCase() + "-task-consumer-" + serviceInfoProvider.getServiceId()) + .groupId(topicService.buildTopicName(jobType.name().toLowerCase() + "-task-consumer-group")) + .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TaskProto.parseFrom(msg.getData()), msg.getHeaders())) + .admin(tasksAdmin) + .statsService(consumerStatsService) + .build(); + } + + @Override + public TbQueueProducer> createJobStatsProducer() { + return TbKafkaProducerTemplate.>builder() + .clientId("job-stats-producer-" + serviceInfoProvider.getServiceId()) + .defaultTopic(topicService.buildTopicName("jobs.stats")) + .settings(kafkaSettings) + .admin(tasksAdmin) + .build(); + } + + @Override + public TbQueueConsumer> createJobStatsConsumer() { + return TbKafkaConsumerTemplate.>builder() + .settings(kafkaSettings) + .topic(topicService.buildTopicName("jobs.stats")) + .clientId("job-stats-consumer-" + serviceInfoProvider.getServiceId()) + .groupId(topicService.buildTopicName("job-stats-consumer-group")) + .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), JobStatsMsg.parseFrom(msg.getData()), msg.getHeaders())) .admin(tasksAdmin) .statsService(consumerStatsService) .build(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java index d0ac20ae06..b2af3671c6 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java @@ -27,9 +27,9 @@ import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; @@ -433,10 +433,10 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { } @Override - public TbQueueProducer> createTaskResultProducer() { - return TbKafkaProducerTemplate.>builder() - .clientId("task-result-producer-" + serviceInfoProvider.getServiceId()) - .defaultTopic(topicService.buildTopicName("tasks.results")) + public TbQueueProducer> createJobStatsProducer() { + return TbKafkaProducerTemplate.>builder() + .clientId("job-stats-producer-" + serviceInfoProvider.getServiceId()) + .defaultTopic(topicService.buildTopicName("jobs.stats")) .settings(kafkaSettings) .admin(tasksAdmin) .build(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java index 10b84f0f65..571b14639c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java @@ -16,8 +16,8 @@ package org.thingsboard.server.queue.provider; import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; -import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; @@ -26,6 +26,6 @@ public interface TaskProcessorQueueFactory { TbQueueConsumer> createTaskConsumer(JobType jobType); - TbQueueProducer> createTaskResultProducer(); + TbQueueProducer> createJobStatsProducer(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java index 409348a12a..823ebea298 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java @@ -19,8 +19,8 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.gen.js.JsInvokeProtos; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; -import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -47,7 +47,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; * Responsible for initialization of various Producers and Consumers used by TB Core Node. * Implementation Depends on the queue queue.type from yml or TB_QUEUE_TYPE environment variable */ -public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory, HousekeeperClientQueueFactory, EdqsClientQueueFactory { +public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory, HousekeeperClientQueueFactory, EdqsClientQueueFactory, TaskProcessorQueueFactory { /** * Used to push messages to instances of TB Transport Service @@ -170,6 +170,6 @@ public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory, Hous TbQueueProducer> createTaskProducer(JobType jobType); - TbQueueConsumer> createTaskResultConsumer(); + TbQueueConsumer> createJobStatsConsumer(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java index 98a3d78304..9900474a10 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.provider; import jakarta.annotation.PostConstruct; import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -53,6 +54,7 @@ public class TbCoreQueueProducerProvider implements TbQueueProducerProvider { private TbQueueProducer> toHousekeeper; private TbQueueProducer> toCalculatedFields; private TbQueueProducer> toCalculatedFieldNotifications; + private TbQueueProducer> jobStatsProducer; public TbCoreQueueProducerProvider(TbCoreQueueFactory tbQueueProvider) { this.tbQueueProvider = tbQueueProvider; @@ -73,6 +75,7 @@ public class TbCoreQueueProducerProvider implements TbQueueProducerProvider { this.toEdgeEvents = tbQueueProvider.createEdgeEventMsgProducer(); this.toCalculatedFields = tbQueueProvider.createToCalculatedFieldMsgProducer(); this.toCalculatedFieldNotifications = tbQueueProvider.createToCalculatedFieldNotificationMsgProducer(); + this.jobStatsProducer = tbQueueProvider.createJobStatsProducer(); } @Override @@ -140,4 +143,9 @@ public class TbCoreQueueProducerProvider implements TbQueueProducerProvider { return toCalculatedFieldNotifications; } + @Override + public TbQueueProducer> getJobStatsProducer() { + return jobStatsProducer; + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbQueueProducerProvider.java index 865637b2ff..428e673fa8 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbQueueProducerProvider.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -97,4 +98,6 @@ public interface TbQueueProducerProvider { TbQueueProducer> getCalculatedFieldsNotificationsMsgProducer(); + TbQueueProducer> getJobStatsProducer(); + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java index 8e1952fc14..9e77a2d4e7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.provider; import jakarta.annotation.PostConstruct; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -51,6 +52,7 @@ public class TbRuleEngineProducerProvider implements TbQueueProducerProvider { private TbQueueProducer> toEdgeEvents; private TbQueueProducer> toCalculatedFields; private TbQueueProducer> toCalculatedFieldNotifications; + private TbQueueProducer> jobStatsProducer; public TbRuleEngineProducerProvider(TbRuleEngineQueueFactory tbQueueProvider) { this.tbQueueProvider = tbQueueProvider; @@ -70,6 +72,7 @@ public class TbRuleEngineProducerProvider implements TbQueueProducerProvider { this.toEdgeEvents = tbQueueProvider.createEdgeEventMsgProducer(); this.toCalculatedFields = tbQueueProvider.createToCalculatedFieldMsgProducer(); this.toCalculatedFieldNotifications = tbQueueProvider.createToCalculatedFieldNotificationMsgProducer(); + this.jobStatsProducer = tbQueueProvider.createJobStatsProducer(); } @Override @@ -137,4 +140,9 @@ public class TbRuleEngineProducerProvider implements TbQueueProducerProvider { return toCalculatedFieldNotifications; } + @Override + public TbQueueProducer> getJobStatsProducer() { + return jobStatsProducer; + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java index a7a34992cd..4472c6157e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java @@ -121,4 +121,10 @@ public class TbTransportQueueProducerProvider implements TbQueueProducerProvider public TbQueueProducer> getCalculatedFieldsNotificationsMsgProducer() { throw new RuntimeException("Not Implemented! Should not be used by Transport!"); } + + @Override + public TbQueueProducer> getJobStatsProducer() { + throw new RuntimeException("Not Implemented! Should not be used by Transport!"); + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java index 85c400d094..0370f8a4af 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java @@ -118,4 +118,9 @@ public class TbVersionControlProducerProvider implements TbQueueProducerProvider throw new RuntimeException("Not Implemented! Should not be used by Version Control Service!"); } + @Override + public TbQueueProducer> getJobStatsProducer() { + throw new RuntimeException("Not Implemented! Should not be used by Version Control Service!"); + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java new file mode 100644 index 0000000000..6c36c573ca --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.queue.task; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; +import org.thingsboard.server.queue.TbQueueCallback; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.provider.TbQueueProducerProvider; + +@Lazy +@Service +@Slf4j +@RequiredArgsConstructor +public class JobStatsService { + + private final TbQueueProducerProvider producerProvider; + + public void reportTaskResult(JobId jobId, TaskResult result) { + report(jobId, JobStatsMsg.newBuilder() + .setTaskResult(TaskResultProto.newBuilder() + .setValue(JacksonUtil.toString(result)) + .build())); + } + + public void reportAllTasksSubmitted(JobId jobId, int tasksCount) { + report(jobId, JobStatsMsg.newBuilder() + .setTotalTasksCount(tasksCount)); + } + + private void report(JobId jobId, JobStatsMsg.Builder statsMsg) { + log.info("[{}] Reporting: {}", jobId, statsMsg); + statsMsg.setJobIdMSB(jobId.getId().getMostSignificantBits()) + .setJobIdLSB(jobId.getId().getLeastSignificantBits()); + + TbProtoQueueMsg msg = new TbProtoQueueMsg<>(jobId.getId(), statsMsg.build()); + TbQueueProducer> producer = producerProvider.getJobStatsProducer(); + producer.send(TopicPartitionInfo.builder().topic(producer.getDefaultTopic()).build(), msg, TbQueueCallback.EMPTY); + } + +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 7123eaf1e0..52fd382c3d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -25,12 +25,8 @@ import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.data.job.TaskResult.TaskFailure; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; -import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; -import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueConsumer; -import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.consumer.QueueConsumerManager; import org.thingsboard.server.queue.provider.TaskProcessorQueueFactory; @@ -45,22 +41,22 @@ public abstract class TaskProcessor { @Autowired private TaskProcessorQueueFactory queueFactory; + @Autowired + private JobStatsService statsService; private QueueConsumerManager> taskConsumer; - private TbQueueProducer> taskResultProducer; private ExecutorService consumerExecutor; @PostConstruct public void init() { consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-consumer")); taskConsumer = QueueConsumerManager.>builder() // fixme: should be consumer per partition - .name(getJobType().name() + "-tasks") + .name(getJobType().name().toLowerCase() + "-tasks") .msgPackProcessor(this::processMsgs) .pollInterval(125) .consumerCreator(() -> queueFactory.createTaskConsumer(getJobType())) .consumerExecutor(consumerExecutor) .build(); - taskResultProducer = queueFactory.createTaskResultProducer(); } @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) @@ -92,7 +88,7 @@ public abstract class TaskProcessor { reportSuccess(task); } catch (Exception e) { log.error("Failed to process task (attempt {}): {}", task.getAttempt(), task, e); - if (task.getAttempt() < 3) { + if (task.getAttempt() <= task.getRetries()) { processTask(task); } else { reportFailure(task, e); @@ -102,34 +98,19 @@ public abstract class TaskProcessor { private void reportSuccess(Task task) { TaskResult result = TaskResult.builder() - .tenantId(task.getTenantId()) - .jobId(task.getJobId()) .success(true) .build(); - reportResult(result); + statsService.reportTaskResult(task.getJobId(), result); } private void reportFailure(Task task, Throwable error) { TaskResult result = TaskResult.builder() - .tenantId(task.getTenantId()) - .jobId(task.getJobId()) .failure(TaskFailure.builder() .error(error.getMessage()) .task(task) .build()) .build(); - reportResult(result); - } - - private void reportResult(TaskResult result) { - log.info("Reporting result: {}", result); - TaskResultProto resultProto = TaskResultProto.newBuilder() - .setValue(JacksonUtil.toString(result)) - .build(); - TbProtoQueueMsg msg = new TbProtoQueueMsg<>(result.getJobId().getId(), resultProto); - taskResultProducer.send(TopicPartitionInfo.builder() - .topic(taskResultProducer.getDefaultTopic()) - .build(), msg, TbQueueCallback.EMPTY); + statsService.reportTaskResult(task.getJobId(), result); } protected abstract void process(T task) throws Exception; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 1d504b0ab2..707dcdc3a5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -745,6 +745,7 @@ public class ModelConstants { public static final String JOB_TABLE_NAME = "job"; public static final String JOB_TYPE_PROPERTY = "type"; public static final String JOB_KEY_PROPERTY = "key"; + public static final String JOB_DESCRIPTION_PROPERTY = "description"; public static final String JOB_STATUS_PROPERTY = "status"; public static final String JOB_CONFIGURATION_PROPERTY = "configuration"; public static final String JOB_RESULT_PROPERTY = "result"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java index 6e8b3e7958..d13c4bbed3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java @@ -56,6 +56,9 @@ public class JobEntity extends BaseSqlEntity { @Column(name = ModelConstants.JOB_KEY_PROPERTY, nullable = false) private String key; + @Column(name = ModelConstants.JOB_DESCRIPTION_PROPERTY, nullable = false) + private String description; + @Enumerated(EnumType.STRING) @Column(name = ModelConstants.JOB_STATUS_PROPERTY, nullable = false) private JobStatus status; @@ -74,6 +77,7 @@ public class JobEntity extends BaseSqlEntity { this.tenantId = getTenantUuid(job.getTenantId()); this.type = job.getType(); this.key = job.getKey(); + this.description = job.getDescription(); this.status = job.getStatus(); this.configuration = toJson(job.getConfiguration()); this.result = toJson(job.getResult()); @@ -87,6 +91,7 @@ public class JobEntity extends BaseSqlEntity { job.setTenantId(getTenantId(tenantId)); job.setType(type); job.setKey(key); + job.setDescription(description); job.setStatus(status); job.setConfiguration(fromJson(configuration, JobConfiguration.class)); job.setResult(fromJson(result, JobResult.class)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java index 273d8ef78a..9fff4d06b7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java @@ -23,14 +23,22 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.thingsboard.server.common.data.job.JobStatus; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.dao.model.sql.JobEntity; +import java.util.List; import java.util.UUID; @Repository public interface JobRepository extends JpaRepository { - Page findByTenantId(UUID tenantId, Pageable pageable); + @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId " + + "AND (:searchText IS NULL OR ilike(j.key, concat('%', :searchText, '%')) = true " + + "OR ilike(j.description, concat('%', :searchText, '%')) = true)") + Page findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, + Pageable pageable); @Modifying @Transactional @@ -45,7 +53,8 @@ public interface JobRepository extends JpaRepository { RETURNING ((result->>'successfulCount')::int + :count) + (result->>'failedCount')::int = (result->>'totalCount')::int """, nativeQuery = true) - boolean reportTaskSuccess(@Param("jobId") UUID jobId, @Param("count") int count); + boolean reportTaskSuccess(@Param("jobId") UUID jobId, + @Param("count") int count); @Modifying @Transactional @@ -64,6 +73,12 @@ public interface JobRepository extends JpaRepository { RETURNING ((result->>'failedCount')::int + 1) + (result->>'successfulCount')::int = (result->>'totalCount')::int """, nativeQuery = true) - boolean reportTaskFailure(@Param("jobId") UUID jobId, @Param("taskKey") String taskKey, @Param("error") String error); + boolean reportTaskFailure(@Param("jobId") UUID jobId, + @Param("taskKey") String taskKey, + @Param("error") String error); + + boolean existsByKeyAndStatusIn(String key, List statuses); + + boolean existsByTenantIdAndTypeAndStatusIn(UUID tenantId, JobType type, List statuses); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java index d6286e2d77..b8b36dbc23 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.sql.task; +import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; @@ -22,6 +23,8 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobStatus; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; @@ -30,6 +33,7 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.task.JobDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.Arrays; import java.util.UUID; @Component @@ -41,7 +45,7 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao @Override public PageData findByTenantId(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData(jobRepository.findByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); + return DaoUtil.toPageData(jobRepository.findByTenantIdAndSearchText(tenantId.getId(), Strings.emptyToNull(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); } @Override @@ -54,6 +58,16 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao return jobRepository.reportTaskFailure(jobId.getId(), taskKey, error); } + @Override + public boolean existsByKeyAndStatusOneOf(String key, JobStatus... statuses) { + return jobRepository.existsByKeyAndStatusIn(key, Arrays.stream(statuses).toList()); + } + + @Override + public boolean existsByTenantIdAndTypeAndStatusOneOf(TenantId tenantId, JobType type, JobStatus... statuses) { + return jobRepository.existsByTenantIdAndTypeAndStatusIn(tenantId.getId(), type, Arrays.stream(statuses).toList()); + } + @Override public EntityType getEntityType() { return EntityType.JOB; diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java index aa9e48600c..12d81c1c2c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java @@ -22,13 +22,14 @@ import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobResult; +import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.data.job.TaskResult.TaskFailure; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; - -import java.util.List; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; @Service @RequiredArgsConstructor @@ -36,14 +37,21 @@ import java.util.List; public class DefaultJobService implements JobService { private final JobDao jobDao; + private final JobValidator validator = new JobValidator(); @Override public Job createJob(TenantId tenantId, Job job) { + validator.validate(job, Job::getTenantId); return jobDao.save(tenantId, job); } @Override - public void reportTaskResults(JobId jobId, List results) { + public Job findJobById(TenantId tenantId, JobId jobId) { + return jobDao.findById(tenantId, jobId.getId()); + } + + @Override + public void processStats(JobId jobId, JobStats jobStats) { Job job = jobDao.findById(TenantId.SYS_TENANT_ID, jobId.getId()); switch (job.getStatus()) { case PENDING -> { @@ -56,7 +64,11 @@ public class DefaultJobService implements JobService { } JobResult jobResult = job.getResult(); - for (TaskResult taskResult : results) { + if (jobStats.getTotalTasksCount() != null) { + jobResult.setTotalCount(jobStats.getTotalTasksCount()); + } + + for (TaskResult taskResult : jobStats.getTaskResults()) { if (taskResult.isSuccess()) { jobResult.setSuccessfulCount(jobResult.getSuccessfulCount() + 1); } else { @@ -67,7 +79,7 @@ public class DefaultJobService implements JobService { } } - if (jobResult.getSuccessfulCount() + jobResult.getFailedCount() >= jobResult.getTotalCount()) { + if (jobResult.getTotalCount() != null && jobResult.getSuccessfulCount() + jobResult.getFailedCount() >= jobResult.getTotalCount()) { if (jobResult.getFailures().isEmpty()) { job.setStatus(JobStatus.COMPLETED); } else { @@ -83,4 +95,22 @@ public class DefaultJobService implements JobService { return jobDao.findByTenantId(tenantId, pageLink); } + // todo: cancellation, reprocessing + + public class JobValidator extends DataValidator { + + @Override + protected void validateCreate(TenantId tenantId, Job job) { + if (jobDao.existsByTenantIdAndTypeAndStatusOneOf(tenantId, job.getType(), JobStatus.PENDING, JobStatus.RUNNING)) { + throw new DataValidationException("Job of this type is already running"); + } + } + + @Override + protected Job validateUpdate(TenantId tenantId, Job job) { + throw new IllegalArgumentException("Job can't be updated externally"); + } + + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java b/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java index 6a5b002aea..5c3c5b977c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java @@ -18,6 +18,8 @@ package org.thingsboard.server.dao.task; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobStatus; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; @@ -30,4 +32,8 @@ public interface JobDao extends Dao { boolean reportTaskFailure(JobId jobId, String taskKey, String error); + boolean existsByKeyAndStatusOneOf(String key, JobStatus... statuses); + + boolean existsByTenantIdAndTypeAndStatusOneOf(TenantId tenantId, JobType type, JobStatus... statuses); + } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 7fa31da5fb..5afc9398d2 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -955,6 +955,7 @@ CREATE TABLE IF NOT EXISTS job ( tenant_id uuid NOT NULL, type varchar NOT NULL, key varchar NOT NULL, + description varchar NOT NULL, status varchar NOT NULL, configuration varchar(1000) NOT NULL, result jsonb From 64f35d2d7b568e61f7956e00364a25795dcb7db9 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 25 Apr 2025 11:16:05 +0300 Subject: [PATCH 096/335] fixed entity service regisrty test --- .../thingsboard/server/dao/task/JobService.java | 3 ++- .../server/dao/task/DefaultJobService.java | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java index 5581f1eac7..a28b0e391a 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java @@ -21,8 +21,9 @@ import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.entity.EntityDaoService; -public interface JobService { +public interface JobService extends EntityDaoService { Job createJob(TenantId tenantId, Job job); diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java index 12d81c1c2c..dba569daa8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java @@ -18,6 +18,9 @@ package org.thingsboard.server.dao.task; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; @@ -31,6 +34,8 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; +import java.util.Optional; + @Service @RequiredArgsConstructor @Slf4j @@ -113,4 +118,14 @@ public class DefaultJobService implements JobService { } + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findJobById(tenantId, new JobId(entityId.getId()))); + } + + @Override + public EntityType getEntityType() { + return EntityType.JOB; + } + } From ee9237c4160ced8d5333148418d7c762035ca44c Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 25 Apr 2025 12:21:20 +0300 Subject: [PATCH 097/335] Implement job cancellation --- .../controller/CalculatedFieldController.java | 3 +- .../server/controller/JobController.java | 12 +++ .../entitiy/EntityStateSourcingListener.java | 14 +++- .../server/service/job/DefaultJobManager.java | 29 ++++--- .../server/service/job/JobManager.java | 4 + .../queue/DefaultTbClusterService.java | 1 + .../server/service/job/JobManagerTest.java | 81 +++++++++++++++++-- .../server/dao/task/JobService.java | 4 +- .../common/data/job/JobConfiguration.java | 4 +- .../server/common/data/job/JobResult.java | 12 ++- .../server/common/data/job/JobStats.java | 2 + .../server/common/data/job/TaskResult.java | 1 + common/proto/src/main/proto/queue.proto | 15 +++- .../server/queue/task/JobStatsService.java | 15 ++-- .../server/queue/task/TaskProcessor.java | 61 +++++++++++--- .../server/dao/sql/task/JobRepository.java | 6 ++ .../server/dao/sql/task/JpaJobDao.java | 9 +-- .../server/dao/task/DefaultJobService.java | 63 ++++++++++++--- .../thingsboard/server/dao/task/JobDao.java | 4 +- 19 files changed, 280 insertions(+), 60 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java index f899d0f480..e5641b6748 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java @@ -97,7 +97,8 @@ public class CalculatedFieldController extends BaseController { public static final int TIMEOUT = 20; - private static final String TEST_SCRIPT_EXPRESSION = "Execute the Script expression and return the result. The format of request: \n\n" + private static final String TEST_SCRIPT_EXPRESSION = + "Execute the Script expression and return the result. The format of request: \n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + " \"expression\": \"var temp = 0; foreach(element: temperature.values) {temp += element.value;} var avgTemperature = temp / temperature.values.size(); var adjustedTemperature = avgTemperature + 0.1 * humidity.value; return {\\\"adjustedTemperature\\\": adjustedTemperature};\",\n" + diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java index f7bc5255d3..d315a522ae 100644 --- a/application/src/main/java/org/thingsboard/server/controller/JobController.java +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.task.JobService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.job.JobManager; import java.util.UUID; @@ -47,10 +49,12 @@ import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERT public class JobController extends BaseController { private final JobService jobService; + private final JobManager jobManager; @GetMapping("/job/{id}") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") public Job getJobById(@PathVariable UUID id) throws ThingsboardException { + // todo check permissions return jobService.findJobById(getTenantId(), new JobId(id)); } @@ -66,8 +70,16 @@ public class JobController extends BaseController { @RequestParam(required = false) String sortProperty, @Parameter(description = SORT_ORDER_DESCRIPTION) @RequestParam(required = false) String sortOrder) throws ThingsboardException { + // todo check permissions PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return jobService.findJobsByTenantId(getTenantId(), pageLink); } + @PostMapping("/job/{id}/cancel") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public void cancelJob(@PathVariable UUID id) throws ThingsboardException { + // todo check permissions + jobManager.cancelJob(getTenantId(), new JobId(id)); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index 648e89adc9..68fb0bb7cf 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -134,6 +135,9 @@ public class EntityStateSourcingListener { case CALCULATED_FIELD -> { onCalculatedFieldUpdate(event.getEntity(), event.getOldEntity()); } + case JOB -> { + onJobUpdate((Job) event.getEntity()); + } default -> { } } @@ -212,8 +216,8 @@ public class EntityStateSourcingListener { public void handleEvent(ActionEntityEvent event) { log.trace("[{}] ActionEntityEvent called: {}", event.getTenantId(), event); if (ActionType.CREDENTIALS_UPDATED.equals(event.getActionType()) && - EntityType.DEVICE.equals(event.getEntityId().getEntityType()) - && event.getEntity() instanceof DeviceCredentials) { + EntityType.DEVICE.equals(event.getEntityId().getEntityType()) + && event.getEntity() instanceof DeviceCredentials) { tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(event.getTenantId(), (DeviceId) event.getEntityId(), (DeviceCredentials) event.getEntity()), null); } else if (ActionType.ASSIGNED_TO_TENANT.equals(event.getActionType()) && event.getEntity() instanceof Device device) { @@ -295,6 +299,12 @@ public class EntityStateSourcingListener { tbClusterService.onCalculatedFieldUpdated(calculatedField, oldCalculatedField, TbQueueCallback.EMPTY); } + private void onJobUpdate(Job job) { + if (job.getResult().getCancellationTs() > 0) { + tbClusterService.broadcastEntityStateChangeEvent(job.getTenantId(), job.getId(), ComponentLifecycleEvent.STOPPED); + } + } + private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) { String data = JacksonUtil.toString(JacksonUtil.valueToTree(assignedDevice)); if (data != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 64c1615310..727beaf859 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -23,6 +23,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobType; @@ -62,7 +63,7 @@ public class DefaultJobManager implements JobManager { private final JobStatsService jobStatsService; private final Map jobProcessors; private final Map>> taskProducers; - private final QueueConsumerManager> taskResultConsumer; + private final QueueConsumerManager> jobStatsConsumer; private final ExecutorService consumerExecutor; @Value("${queue.tasks.stats.processing_interval_ms:5000}") @@ -73,8 +74,8 @@ public class DefaultJobManager implements JobManager { this.jobStatsService = jobStatsService; this.jobProcessors = jobProcessors.stream().collect(Collectors.toMap(JobProcessor::getType, Function.identity())); this.taskProducers = Arrays.stream(JobType.values()).collect(Collectors.toMap(Function.identity(), queueFactory::createTaskProducer)); - this.consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("task-result-consumer")); - this.taskResultConsumer = QueueConsumerManager.>builder() + this.consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("job-stats-consumer")); + this.jobStatsConsumer = QueueConsumerManager.>builder() .name("job-stats") .msgPackProcessor(this::processStats) .pollInterval(125) @@ -85,8 +86,8 @@ public class DefaultJobManager implements JobManager { @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) public void afterStartUp() { - taskResultConsumer.subscribe(); - taskResultConsumer.launch(); + jobStatsConsumer.subscribe(); + jobStatsConsumer.launch(); } @Override @@ -95,10 +96,16 @@ public class DefaultJobManager implements JobManager { log.info("Submitting job: {}", job); int tasksCount = jobProcessors.get(job.getType()).process(job, this::submitTask); - jobStatsService.reportAllTasksSubmitted(job.getId(), tasksCount); + jobStatsService.reportAllTasksSubmitted(job.getTenantId(), job.getId(), tasksCount); return job; } + @Override + public void cancelJob(TenantId tenantId, JobId jobId) { + log.info("Cancelling job: {}", jobId); + jobService.cancelJob(tenantId, jobId); + } + private void submitTask(Task task) { log.info("Submitting task: {}", task); TaskProto taskProto = TaskProto.newBuilder() @@ -126,8 +133,9 @@ public class DefaultJobManager implements JobManager { for (TbProtoQueueMsg msg : msgs) { JobStatsMsg statsMsg = msg.getValue(); + TenantId tenantId = TenantId.fromUUID(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); JobId jobId = new JobId(new UUID(statsMsg.getJobIdMSB(), statsMsg.getJobIdLSB())); - JobStats jobStats = stats.computeIfAbsent(jobId, JobStats::new); + JobStats jobStats = stats.computeIfAbsent(jobId, __ -> new JobStats(tenantId, jobId)); if (statsMsg.hasTaskResult()) { TaskResult taskResult = JacksonUtil.fromString(statsMsg.getTaskResult().getValue(), TaskResult.class); @@ -140,8 +148,9 @@ public class DefaultJobManager implements JobManager { stats.forEach((jobId, jobStats) -> { try { - log.info("[{}] Processing job stats: {}", jobId, stats); - jobService.processStats(jobId, jobStats); + TenantId tenantId = jobStats.getTenantId(); + log.info("[{}][{}] Processing job stats: {}", tenantId, jobId, stats); + jobService.processStats(tenantId, jobId, jobStats); } catch (Exception e) { log.warn("Failed to process job stats for {}: {}", jobId, jobStats, e); } @@ -153,7 +162,7 @@ public class DefaultJobManager implements JobManager { @PreDestroy private void destroy() { - taskResultConsumer.stop(); + jobStatsConsumer.stop(); consumerExecutor.shutdownNow(); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java index c78de2a5d9..71ff3dcaa2 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java @@ -15,10 +15,14 @@ */ package org.thingsboard.server.service.job; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; public interface JobManager { Job submitJob(Job job); + void cancelJob(TenantId tenantId, JobId jobId); + } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 561dc7122a..cdc7abb4d7 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -594,6 +594,7 @@ public class DefaultTbClusterService implements TbClusterService { || entityType.equals(EntityType.ENTITY_VIEW) || entityType.equals(EntityType.NOTIFICATION_RULE) || entityType.equals(EntityType.CALCULATED_FIELD) + || entityType.equals(EntityType.JOB) ) { TbQueueProducer> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); Set tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE); diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index f9ee2e2b13..68d2d96471 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -20,22 +20,28 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.job.DummyJobConfiguration; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.service.job.task.DummyTaskProcessor; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; @DaoSqlTest @TestPropertySource(properties = { @@ -46,6 +52,9 @@ public class JobManagerTest extends AbstractControllerTest { @Autowired private JobManager jobManager; + @SpyBean + private DummyTaskProcessor taskProcessor; + @Before public void setUp() throws Exception { loginTenantAdmin(); @@ -80,6 +89,7 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(job.getStatus()).isEqualTo(JobStatus.COMPLETED); assertThat(job.getResult().getSuccessfulCount()).isEqualTo(tasksCount); assertThat(job.getResult().getFailures()).isEmpty(); + assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount); }); } @@ -104,15 +114,76 @@ public class JobManagerTest extends AbstractControllerTest { await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { Job job = findJobById(jobId); assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); - assertThat(job.getResult().getSuccessfulCount()).isEqualTo(successfulTasks); - assertThat(job.getResult().getFailedCount()).isEqualTo(failedTasks); - assertThat(job.getResult().getTotalCount()).isEqualTo(successfulTasks + failedTasks); - assertThat(job.getResult().getFailures().get("Task 1")).isEqualTo("error3"); // last error - assertThat(job.getResult().getFailures().get("Task 2")).isEqualTo("error3"); // last error + JobResult jobResult = job.getResult(); + assertThat(jobResult.getSuccessfulCount()).isEqualTo(successfulTasks); + assertThat(jobResult.getFailedCount()).isEqualTo(failedTasks); + assertThat(jobResult.getTotalCount()).isEqualTo(successfulTasks + failedTasks); + assertThat(jobResult.getFailures().get("Task 1")).isEqualTo("error3"); // last error + assertThat(jobResult.getFailures().get("Task 2")).isEqualTo("error3"); // last error + assertThat(jobResult.getCompletedCount()).isEqualTo(jobResult.getTotalCount()); + }); + } + + @Test + public void testCancelJob_whileRunning() throws Exception { + int tasksCount = 100; + JobId jobId = jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("test job") + .configuration(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(100) + .build()) + .build()).getId(); + + Thread.sleep(500); + jobManager.cancelJob(tenantId, jobId); + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.CANCELLED); + assertThat(job.getResult().getSuccessfulCount()).isBetween(1, tasksCount - 1); + assertThat(job.getResult().getCancelledCount()).isBetween(1, tasksCount - 1); + assertThat(job.getResult().getTotalCount()).isEqualTo(tasksCount); + assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount); }); } + @Test + public void testCancelJob_simulateTaskProcessorRestart() { + int tasksCount = 10; + JobId jobId = jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("test job") + .configuration(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(100) + .build()) + .build()).getId(); + + // simulate cancelled jobs are forgotten + AtomicInteger cancellationRenotifyAttempt = new AtomicInteger(0); + doAnswer(inv -> { + if (cancellationRenotifyAttempt.incrementAndGet() >= 5) { + inv.callRealMethod(); + } + return null; + }).when(taskProcessor).addToCancelledJobs(any()); // ignoring cancellation event, + jobManager.cancelJob(tenantId, jobId); + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + System.err.println(job); + assertThat(job.getStatus()).isEqualTo(JobStatus.CANCELLED); + assertThat(job.getResult().getSuccessfulCount()).isBetween(1, tasksCount - 1); + assertThat(job.getResult().getCancelledCount()).isBetween(1, tasksCount - 1); + assertThat(job.getResult().getTotalCount()).isEqualTo(tasksCount); + assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount); + }); + } private Job findJobById(JobId jobId) throws Exception { return doGet("/api/job/" + jobId, Job.class); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java index a28b0e391a..9ce802b84f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java @@ -29,7 +29,9 @@ public interface JobService extends EntityDaoService { Job findJobById(TenantId tenantId, JobId jobId); - void processStats(JobId jobId, JobStats jobStats); + void cancelJob(TenantId tenantId, JobId jobId); + + void processStats(TenantId tenantId, JobId jobId, JobStats jobStats); PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java index eccdfae6bb..b541458289 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java @@ -20,13 +20,15 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.io.Serializable; + @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @Type(name = "CF_REPROCESSING", value = CfReprocessingJobConfiguration.class), @Type(name = "DUMMY", value = DummyJobConfiguration.class), }) -public interface JobConfiguration { +public interface JobConfiguration extends Serializable { JobType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index 07b6c4eadd..abaa86facb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.job; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; @@ -22,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.util.HashMap; import java.util.Map; @@ -33,13 +35,21 @@ import java.util.Map; }) @Data @NoArgsConstructor -public abstract class JobResult { +public abstract class JobResult implements Serializable { private int successfulCount; private int failedCount; + private int cancelledCount; private Integer totalCount = null; // set when all tasks are submitted private Map failures = new HashMap<>(); + private long cancellationTs; + + @JsonIgnore + public int getCompletedCount() { + return successfulCount + failedCount + cancelledCount; + } + public abstract JobType getJobType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java index 6491d0998a..9d2c3d9be2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java @@ -17,12 +17,14 @@ package org.thingsboard.server.common.data.job; import lombok.Data; import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; import java.util.ArrayList; import java.util.List; @Data public class JobStats { + private final TenantId tenantId; private final JobId jobId; private final List taskResults = new ArrayList<>(); private Integer totalTasksCount; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java index ae5e6bbba9..57f2f44d7a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java @@ -27,6 +27,7 @@ import lombok.NoArgsConstructor; public class TaskResult { private boolean success; + private boolean cancelled; private TaskFailure failure; @Data diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 032301206f..b4d436a32a 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -61,6 +61,7 @@ enum EntityTypeProto { MOBILE_APP_BUNDLE = 38; CALCULATED_FIELD = 39; CALCULATED_FIELD_LINK = 40; + JOB = 41; } enum ApiUsageRecordKeyProto { @@ -534,6 +535,10 @@ message ToEdqsCoreServiceMsg { bytes value = 1; } +message ToJobManagerMsg { + bytes value = 1; +} + message LwM2MRegistrationRequestMsg { string tenantId = 1; string endpoint = 2; @@ -1852,10 +1857,12 @@ message TaskProto { } message JobStatsMsg { - int64 jobIdMSB = 1; - int64 jobIdLSB = 2; - optional TaskResultProto taskResult = 3; - optional int32 totalTasksCount = 4; + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 jobIdMSB = 3; + int64 jobIdLSB = 4; + optional TaskResultProto taskResult = 5; + optional int32 totalTasksCount = 6; } message TaskResultProto { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java index 6c36c573ca..ceba2645f9 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java @@ -21,6 +21,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; @@ -38,21 +39,23 @@ public class JobStatsService { private final TbQueueProducerProvider producerProvider; - public void reportTaskResult(JobId jobId, TaskResult result) { - report(jobId, JobStatsMsg.newBuilder() + public void reportTaskResult(TenantId tenantId, JobId jobId, TaskResult result) { + report(tenantId, jobId, JobStatsMsg.newBuilder() .setTaskResult(TaskResultProto.newBuilder() .setValue(JacksonUtil.toString(result)) .build())); } - public void reportAllTasksSubmitted(JobId jobId, int tasksCount) { - report(jobId, JobStatsMsg.newBuilder() + public void reportAllTasksSubmitted(TenantId tenantId, JobId jobId, int tasksCount) { + report(tenantId, jobId, JobStatsMsg.newBuilder() .setTotalTasksCount(tasksCount)); } - private void report(JobId jobId, JobStatsMsg.Builder statsMsg) { + private void report(TenantId tenantId, JobId jobId, JobStatsMsg.Builder statsMsg) { log.info("[{}] Reporting: {}", jobId, statsMsg); - statsMsg.setJobIdMSB(jobId.getId().getMostSignificantBits()) + statsMsg.setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setJobIdMSB(jobId.getId().getMostSignificantBits()) .setJobIdLSB(jobId.getId().getLeastSignificantBits()); TbProtoQueueMsg msg = new TbProtoQueueMsg<>(jobId.getId(), statsMsg.build()); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 52fd382c3d..43d2dd74c0 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -17,14 +17,20 @@ package org.thingsboard.server.queue.task; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.EventListener; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.data.job.TaskResult.TaskFailure; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; @@ -33,12 +39,16 @@ import org.thingsboard.server.queue.provider.TaskProcessorQueueFactory; import org.thingsboard.server.queue.util.AfterStartUp; import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -@Slf4j public abstract class TaskProcessor { + protected final Logger log = LoggerFactory.getLogger(getClass()); + @Autowired private TaskProcessorQueueFactory queueFactory; @Autowired @@ -47,12 +57,14 @@ public abstract class TaskProcessor { private QueueConsumerManager> taskConsumer; private ExecutorService consumerExecutor; + private final Set cancelledJobs = ConcurrentHashMap.newKeySet(); // fixme use caffeine + @PostConstruct public void init() { consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-consumer")); taskConsumer = QueueConsumerManager.>builder() // fixme: should be consumer per partition .name(getJobType().name().toLowerCase() + "-tasks") - .msgPackProcessor(this::processMsgs) + .msgPackProcessor(this::processMsgs) // todo: max.poll.records = 1 .pollInterval(125) .consumerCreator(() -> queueFactory.createTaskConsumer(getJobType())) .consumerExecutor(consumerExecutor) @@ -65,16 +77,27 @@ public abstract class TaskProcessor { taskConsumer.launch(); } - @PreDestroy - public void destroy() { - taskConsumer.stop(); - consumerExecutor.shutdownNow(); + @EventListener + public void onJobCancelled(ComponentLifecycleMsg event) { + if (event.getEntityId().getEntityType() != EntityType.JOB) { + return; + } + JobId jobId = (JobId) event.getEntityId(); + if (event.getEvent() == ComponentLifecycleEvent.STOPPED) { + log.info("Adding job {} to cancelled", jobId); + addToCancelledJobs(jobId); + } } private void processMsgs(List> msgs, TbQueueConsumer> consumer) { for (TbProtoQueueMsg msg : msgs) { TaskProto taskProto = msg.getValue(); Task task = JacksonUtil.fromString(taskProto.getValue(), Task.class); + if (cancelledJobs.contains(task.getJobId().getId())) { + log.info("Skipping task '{}' for cancelled job {}", task.getKey(), task.getJobId()); + reportCancelled(task); + continue; + } processTask((T) task); } consumer.commit(); @@ -96,11 +119,13 @@ public abstract class TaskProcessor { } } + protected abstract void process(T task) throws Exception; + private void reportSuccess(Task task) { TaskResult result = TaskResult.builder() .success(true) .build(); - statsService.reportTaskResult(task.getJobId(), result); + statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); } private void reportFailure(Task task, Throwable error) { @@ -110,10 +135,26 @@ public abstract class TaskProcessor { .task(task) .build()) .build(); - statsService.reportTaskResult(task.getJobId(), result); + statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); + } + + private void reportCancelled(Task task) { + TaskResult result = TaskResult.builder() + .cancelled(true) + .build(); + statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); + } + + public void addToCancelledJobs(JobId jobId) { + cancelledJobs.add(jobId.getId()); + } + + @PreDestroy + public void destroy() { + taskConsumer.stop(); + consumerExecutor.shutdownNow(); } - protected abstract void process(T task) throws Exception; public abstract JobType getJobType(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java index 9fff4d06b7..472df05bdd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.dao.sql.task; +import jakarta.persistence.LockModeType; import jakarta.transaction.Transactional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -40,6 +42,10 @@ public interface JobRepository extends JpaRepository { @Param("searchText") String searchText, Pageable pageable); + @Lock(LockModeType.PESSIMISTIC_WRITE) // SELECT FOR UPDATE + @Query("SELECT j FROM JobEntity j WHERE j.id = :id") + JobEntity findByIdForUpdate(UUID id); + @Modifying @Transactional @Query(value = """ diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java index b8b36dbc23..92b9fc8a72 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java @@ -49,13 +49,8 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao } @Override - public boolean reportTaskSuccess(JobId jobId, int tasksCount) { - return jobRepository.reportTaskSuccess(jobId.getId(), tasksCount); - } - - @Override - public boolean reportTaskFailure(JobId jobId, String taskKey, String error) { - return jobRepository.reportTaskFailure(jobId.getId(), taskKey, error); + public Job findByIdForUpdate(TenantId tenantId, JobId jobId) { + return DaoUtil.getData(jobRepository.findByIdForUpdate(jobId.getId())); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java index dba569daa8..1e694508fa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java @@ -21,6 +21,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; @@ -31,6 +32,8 @@ import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.data.job.TaskResult.TaskFailure; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @@ -39,7 +42,7 @@ import java.util.Optional; @Service @RequiredArgsConstructor @Slf4j -public class DefaultJobService implements JobService { +public class DefaultJobService extends AbstractEntityService implements JobService { private final JobDao jobDao; private final JobValidator validator = new JobValidator(); @@ -47,7 +50,7 @@ public class DefaultJobService implements JobService { @Override public Job createJob(TenantId tenantId, Job job) { validator.validate(job, Job::getTenantId); - return jobDao.save(tenantId, job); + return saveJob(tenantId, job, false); } @Override @@ -55,9 +58,21 @@ public class DefaultJobService implements JobService { return jobDao.findById(tenantId, jobId.getId()); } + @Transactional @Override - public void processStats(JobId jobId, JobStats jobStats) { - Job job = jobDao.findById(TenantId.SYS_TENANT_ID, jobId.getId()); + public void cancelJob(TenantId tenantId, JobId jobId) { + Job job = findForUpdate(tenantId, jobId); + if (job.getStatus() != JobStatus.PENDING && job.getStatus() != JobStatus.RUNNING) { + throw new IllegalArgumentException("Job already " + job.getStatus().name().toLowerCase()); + } + job.getResult().setCancellationTs(System.currentTimeMillis()); + saveJob(tenantId, job, true); + } + + @Transactional + @Override + public void processStats(TenantId tenantId, JobId jobId, JobStats jobStats) { + Job job = findForUpdate(tenantId, jobId); switch (job.getStatus()) { case PENDING -> { job.setStatus(JobStatus.RUNNING); @@ -73,26 +88,52 @@ public class DefaultJobService implements JobService { jobResult.setTotalCount(jobStats.getTotalTasksCount()); } + boolean publishEvent = false; for (TaskResult taskResult : jobStats.getTaskResults()) { if (taskResult.isSuccess()) { jobResult.setSuccessfulCount(jobResult.getSuccessfulCount() + 1); + } else if (taskResult.isCancelled()) { + jobResult.setCancelledCount(jobResult.getCancelledCount() + 1); } else { TaskFailure failure = taskResult.getFailure(); String key = failure.getTask().getKey(); jobResult.setFailedCount(jobResult.getFailedCount() + 1); jobResult.getFailures().put(key, failure.getError()); } + + if (jobResult.getCancellationTs() > 0) { + if (!taskResult.isCancelled() && System.currentTimeMillis() > jobResult.getCancellationTs()) { + log.info("Got task result for cancelled job {}: {}, re-notifying processors about cancellation", jobId, taskResult); + // task processor forgot the task is cancelled + publishEvent = true; + } + } } - if (jobResult.getTotalCount() != null && jobResult.getSuccessfulCount() + jobResult.getFailedCount() >= jobResult.getTotalCount()) { - if (jobResult.getFailures().isEmpty()) { - job.setStatus(JobStatus.COMPLETED); - } else { + if (jobResult.getTotalCount() != null && jobResult.getCompletedCount() >= jobResult.getTotalCount()) { + if (jobResult.getCancellationTs() > 0) { + job.setStatus(JobStatus.CANCELLED); + } else if (jobResult.getFailedCount() > 0) { job.setStatus(JobStatus.FAILED); + } else { + job.setStatus(JobStatus.COMPLETED); } } log.info("Saving job {}", job); - jobDao.save(TenantId.SYS_TENANT_ID, job); + saveJob(tenantId, job, publishEvent); + } + + private Job saveJob(TenantId tenantId, Job job, boolean publishEvent) { + job = jobDao.save(tenantId, job); + if (publishEvent) { + eventPublisher.publishEvent(SaveEntityEvent.builder() + .tenantId(tenantId) + .entityId(job.getId()) + .entity(job) + .created(false) + .build()); + } + return job; } @Override @@ -100,6 +141,10 @@ public class DefaultJobService implements JobService { return jobDao.findByTenantId(tenantId, pageLink); } + private Job findForUpdate(TenantId tenantId, JobId jobId) { + return jobDao.findByIdForUpdate(tenantId, jobId); + } + // todo: cancellation, reprocessing public class JobValidator extends DataValidator { diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java b/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java index 5c3c5b977c..3b1d6631dd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java @@ -28,9 +28,7 @@ public interface JobDao extends Dao { PageData findByTenantId(TenantId tenantId, PageLink pageLink); - boolean reportTaskSuccess(JobId jobId, int tasksCount); - - boolean reportTaskFailure(JobId jobId, String taskKey, String error); + Job findByIdForUpdate(TenantId tenantId, JobId jobId); boolean existsByKeyAndStatusOneOf(String key, JobStatus... statuses); From a317b4707a7aa504b994ca9e5e5bb66ed54fe721 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 25 Apr 2025 17:40:49 +0300 Subject: [PATCH 098/335] Discard tasks when tenant is deleted --- .../job/task/CfReprocessingTaskProcessor.java | 2 +- .../service/job/task/DummyTaskProcessor.java | 2 +- .../server/service/job/JobManagerTest.java | 39 ++++++++++++ .../server/queue/task/TaskProcessor.java | 61 ++++++++++++------- .../server/dao/task/DefaultJobService.java | 13 +++- .../server/dao/tenant/TenantServiceImpl.java | 2 +- 6 files changed, 92 insertions(+), 27 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java index 36899516f9..5d4005307c 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java @@ -33,7 +33,7 @@ public class CfReprocessingTaskProcessor extends TaskProcessor future = SettableFuture.create(); cfReprocessingService.reprocess(task, new TbCallback() { @Override diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java index 10aafc197d..dc4a193bb9 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java @@ -26,7 +26,7 @@ import org.thingsboard.server.queue.task.TaskProcessor; public class DummyTaskProcessor extends TaskProcessor { @Override - protected void process(DummyTask task) throws Exception { + public void process(DummyTask task) throws Exception { if (task.getProcessingTimeMs() > 0) { Thread.sleep(task.getProcessingTimeMs()); } diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 68d2d96471..b01d0099df 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -16,13 +16,16 @@ package org.thingsboard.server.service.job; import com.fasterxml.jackson.core.type.TypeReference; +import org.assertj.core.api.Assertions; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.DummyJobConfiguration; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobResult; @@ -32,6 +35,8 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.dao.task.JobService; +import org.thingsboard.server.queue.task.JobStatsService; import org.thingsboard.server.service.job.task.DummyTaskProcessor; import java.util.List; @@ -42,6 +47,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; @DaoSqlTest @TestPropertySource(properties = { @@ -52,9 +59,15 @@ public class JobManagerTest extends AbstractControllerTest { @Autowired private JobManager jobManager; + @Autowired + private JobService jobService; + @SpyBean private DummyTaskProcessor taskProcessor; + @SpyBean + private JobStatsService jobStatsService; + @Before public void setUp() throws Exception { loginTenantAdmin(); @@ -185,6 +198,32 @@ public class JobManagerTest extends AbstractControllerTest { }); } + @Test + public void whenTenantIsDeleted_thenCancelAllTheJobs() throws Exception { + loginSysAdmin(); + createDifferentTenant(); + + TenantId tenantId = this.differentTenantId; + jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("test job") + .configuration(DummyJobConfiguration.builder() + .successfulTasksCount(1000) + .taskProcessingTimeMs(500) + .build()) + .build()); + + Thread.sleep(2000); + deleteDifferentTenant(); + Mockito.reset(jobStatsService); + + Thread.sleep(3000); + verify(jobStatsService, never()).reportTaskResult(any(), any(), any()); + Assertions.assertThat(jobService.findJobsByTenantId(tenantId, new PageLink(100, 0)).getData()).isEmpty(); + } + private Job findJobById(JobId jobId) throws Exception { return doGet("/api/job/" + jobId, Job.class); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 43d2dd74c0..2cf0f22b90 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -23,8 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; import org.thingsboard.server.common.data.job.TaskResult; @@ -57,6 +56,7 @@ public abstract class TaskProcessor { private QueueConsumerManager> taskConsumer; private ExecutorService consumerExecutor; + private final Set deletedTenants = ConcurrentHashMap.newKeySet(); private final Set cancelledJobs = ConcurrentHashMap.newKeySet(); // fixme use caffeine @PostConstruct @@ -78,37 +78,54 @@ public abstract class TaskProcessor { } @EventListener - public void onJobCancelled(ComponentLifecycleMsg event) { - if (event.getEntityId().getEntityType() != EntityType.JOB) { - return; - } - JobId jobId = (JobId) event.getEntityId(); - if (event.getEvent() == ComponentLifecycleEvent.STOPPED) { - log.info("Adding job {} to cancelled", jobId); - addToCancelledJobs(jobId); + public void onComponentLifecycle(ComponentLifecycleMsg event) { + EntityId entityId = event.getEntityId(); + switch (entityId.getEntityType()) { + case JOB -> { + if (event.getEvent() == ComponentLifecycleEvent.STOPPED) { + log.info("Adding job {} to cancelledJobs", entityId); + addToCancelledJobs(entityId.getId()); + } + } + case TENANT -> { + if (event.getEvent() == ComponentLifecycleEvent.DELETED) { + deletedTenants.add(entityId.getId()); + log.info("Adding tenant {} to deletedTenants", entityId); + } + } } } - private void processMsgs(List> msgs, TbQueueConsumer> consumer) { + private void processMsgs(List> msgs, TbQueueConsumer> consumer) throws Exception { for (TbProtoQueueMsg msg : msgs) { - TaskProto taskProto = msg.getValue(); - Task task = JacksonUtil.fromString(taskProto.getValue(), Task.class); - if (cancelledJobs.contains(task.getJobId().getId())) { - log.info("Skipping task '{}' for cancelled job {}", task.getKey(), task.getJobId()); - reportCancelled(task); - continue; + try { + Task task = JacksonUtil.fromString(msg.getValue().getValue(), Task.class); + if (cancelledJobs.contains(task.getJobId().getId())) { + log.info("Skipping task '{}' for cancelled job {}", task.getKey(), task.getJobId()); + reportCancelled(task); + continue; + } else if (deletedTenants.contains(task.getTenantId().getId())) { + log.info("Skipping task '{}' for deleted tenant {}", task.getKey(), task.getTenantId()); + continue; + } + processTask((T) task); + } catch (InterruptedException e) { + throw e; + } catch (Exception e) { + log.error("Failed to process msg: {}", msg, e); } - processTask((T) task); } consumer.commit(); } - private void processTask(T task) { + private void processTask(T task) throws Exception { // todo: timeout and task interruption task.setAttempt(task.getAttempt() + 1); log.info("Processing task: {}", task); try { process(task); reportSuccess(task); + } catch (InterruptedException e) { + throw e; } catch (Exception e) { log.error("Failed to process task (attempt {}): {}", task.getAttempt(), task, e); if (task.getAttempt() <= task.getRetries()) { @@ -119,7 +136,7 @@ public abstract class TaskProcessor { } } - protected abstract void process(T task) throws Exception; + public abstract void process(T task) throws Exception; private void reportSuccess(Task task) { TaskResult result = TaskResult.builder() @@ -145,8 +162,8 @@ public abstract class TaskProcessor { statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); } - public void addToCancelledJobs(JobId jobId) { - cancelledJobs.add(jobId.getId()); + public void addToCancelledJobs(UUID jobId) { + cancelledJobs.add(jobId); } @PreDestroy diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java index 1e694508fa..16b4397d87 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java @@ -18,10 +18,10 @@ package org.thingsboard.server.dao.task; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; -import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; @@ -73,6 +73,10 @@ public class DefaultJobService extends AbstractEntityService implements JobServi @Override public void processStats(TenantId tenantId, JobId jobId, JobStats jobStats) { Job job = findForUpdate(tenantId, jobId); + if (job == null) { + log.info("Got stale stats for job {}: {}", jobId, jobStats); + return; + } switch (job.getStatus()) { case PENDING -> { job.setStatus(JobStatus.RUNNING); @@ -165,7 +169,12 @@ public class DefaultJobService extends AbstractEntityService implements JobServi @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { - return Optional.ofNullable(findJobById(tenantId, new JobId(entityId.getId()))); + return Optional.ofNullable(findJobById(tenantId, (JobId) entityId)); + } + + @Override + public void deleteEntity(TenantId tenantId, EntityId id, boolean force) { + jobDao.removeById(tenantId, id.getId()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 1dbca5af12..de28d047e7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -172,7 +172,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService Date: Mon, 28 Apr 2025 17:24:14 +0300 Subject: [PATCH 099/335] Multiple queued jobs of the same type --- .../server/controller/JobController.java | 2 +- .../entitiy/EntityStateSourcingListener.java | 7 +- .../job/CfReprocessingJobProcessor.java | 7 +- .../server/service/job/DefaultJobManager.java | 48 +++++-- .../server/service/job/DummyJobProcessor.java | 11 +- .../server/service/job/JobManager.java | 2 + .../server/service/job/JobProcessor.java | 8 +- .../server/service/job/JobManagerTest.java | 119 +++++++++++++++++- .../src/test/resources/logback-test.xml | 2 +- .../server/dao/{task => job}/JobService.java | 4 +- .../common/data/job/CfReprocessingTask.java | 2 + .../data/job/DummyJobConfiguration.java | 3 + .../server/common/data/job/DummyTask.java | 2 + .../server/common/data/job/Job.java | 3 +- .../server/common/data/job/JobResult.java | 5 +- .../server/common/data/job/JobStatus.java | 16 ++- .../server/common/data/job/TaskResult.java | 2 +- .../server/queue/task/JobStatsService.java | 2 +- .../server/queue/task/TaskProcessor.java | 16 +-- .../dao/{task => job}/DefaultJobService.java | 119 ++++++++++++------ .../server/dao/{task => job}/JobDao.java | 4 +- .../dao/sql/{task => job}/JobRepository.java | 49 ++------ .../dao/sql/{task => job}/JpaJobDao.java | 10 +- 23 files changed, 314 insertions(+), 129 deletions(-) rename common/dao-api/src/main/java/org/thingsboard/server/dao/{task => job}/JobService.java (92%) rename dao/src/main/java/org/thingsboard/server/dao/{task => job}/DefaultJobService.java (54%) rename dao/src/main/java/org/thingsboard/server/dao/{task => job}/JobDao.java (90%) rename dao/src/main/java/org/thingsboard/server/dao/sql/{task => job}/JobRepository.java (56%) rename dao/src/main/java/org/thingsboard/server/dao/sql/{task => job}/JpaJobDao.java (87%) diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java index d315a522ae..5718d6e388 100644 --- a/application/src/main/java/org/thingsboard/server/controller/JobController.java +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.task.JobService; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.job.JobManager; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index 68fb0bb7cf..f70354c3a8 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -59,6 +60,7 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.queue.TbQueueCallback; +import org.thingsboard.server.service.job.JobManager; import java.util.Set; @@ -70,6 +72,7 @@ public class EntityStateSourcingListener { private final TenantService tenantService; private final TbClusterService tbClusterService; private final EdgeSynchronizationManager edgeSynchronizationManager; + private final JobManager jobManager; @PostConstruct public void init() { @@ -300,7 +303,9 @@ public class EntityStateSourcingListener { } private void onJobUpdate(Job job) { - if (job.getResult().getCancellationTs() > 0) { + jobManager.onJobUpdate(job); + if (job.getResult().getCancellationTs() > 0 || job.getStatus().isOneOf(JobStatus.FAILED)) { + // task processors will add this job to the list of discarded tbClusterService.broadcastEntityStateChangeEvent(job.getTenantId(), job.getId(), ComponentLifecycleEvent.STOPPED); } } diff --git a/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java index b5f6c4665f..edb38da5c2 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.job; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.id.AssetProfileId; @@ -36,15 +35,13 @@ import java.util.function.Consumer; @Component @RequiredArgsConstructor -public class CfReprocessingJobProcessor extends JobProcessor { +public class CfReprocessingJobProcessor implements JobProcessor { private final DeviceService deviceService; private final AssetService assetService; - // fixme: multiple jobs with single type - @Transactional @Override - public int process(Job job, Consumer taskConsumer) { + public int process(Job job, Consumer taskConsumer) throws Exception { CfReprocessingJobConfiguration configuration = job.getConfiguration(); CalculatedField calculatedField = configuration.getCalculatedField(); diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 727beaf859..6554bebe9f 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -18,19 +18,22 @@ package org.thingsboard.server.service.job; import jakarta.annotation.PreDestroy; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobStats; +import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.dao.task.JobService; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.queue.TbQueueCallback; @@ -64,6 +67,7 @@ public class DefaultJobManager implements JobManager { private final Map jobProcessors; private final Map>> taskProducers; private final QueueConsumerManager> jobStatsConsumer; + private final ExecutorService executor; private final ExecutorService consumerExecutor; @Value("${queue.tasks.stats.processing_interval_ms:5000}") @@ -74,6 +78,7 @@ public class DefaultJobManager implements JobManager { this.jobStatsService = jobStatsService; this.jobProcessors = jobProcessors.stream().collect(Collectors.toMap(JobProcessor::getType, Function.identity())); this.taskProducers = Arrays.stream(JobType.values()).collect(Collectors.toMap(Function.identity(), queueFactory::createTaskProducer)); + this.executor = ThingsBoardExecutors.newWorkStealingPool(Math.max(4, Runtime.getRuntime().availableProcessors()), getClass()); this.consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("job-stats-consumer")); this.jobStatsConsumer = QueueConsumerManager.>builder() .name("job-stats") @@ -92,22 +97,40 @@ public class DefaultJobManager implements JobManager { @Override public Job submitJob(Job job) { - job = jobService.createJob(job.getTenantId(), job); - log.info("Submitting job: {}", job); + log.debug("Submitting job: {}", job); + return jobService.createJob(job.getTenantId(), job); + } - int tasksCount = jobProcessors.get(job.getType()).process(job, this::submitTask); - jobStatsService.reportAllTasksSubmitted(job.getTenantId(), job.getId(), tasksCount); - return job; + @Override + public void onJobUpdate(Job job) { + if (job.getStatus() == JobStatus.PENDING) { + executor.execute(() -> { + TenantId tenantId = job.getTenantId(); + JobId jobId = job.getId(); + try { + int tasksCount = jobProcessors.get(job.getType()).process(job, this::submitTask); // todo: think about stopping tb - while tasks are being submitted + log.info("[{}][{}][{}] Submitted {} tasks", tenantId, jobId, job.getType(), tasksCount); + jobStatsService.reportAllTasksSubmitted(tenantId, jobId, tasksCount); + } catch (Throwable e) { + log.error("[{}][{}][{}] Failed to submit tasks", tenantId, jobId, job.getType(), e); + try { + jobService.markAsFailed(tenantId, jobId, ExceptionUtils.getStackTrace(e)); + } catch (Throwable e2) { + log.error("[{}][{}] Failed to mark job as failed", tenantId, jobId, e2); + } + } + }); + } } @Override public void cancelJob(TenantId tenantId, JobId jobId) { - log.info("Cancelling job: {}", jobId); + log.info("[{}][{}] Cancelling job", tenantId, jobId); jobService.cancelJob(tenantId, jobId); } private void submitTask(Task task) { - log.info("Submitting task: {}", task); + log.info("[{}][{}] Submitting task: {}", task.getTenantId(), task.getJobId(), task); TaskProto taskProto = TaskProto.newBuilder() .setValue(JacksonUtil.toString(task)) .build(); @@ -147,22 +170,23 @@ public class DefaultJobManager implements JobManager { } stats.forEach((jobId, jobStats) -> { + TenantId tenantId = jobStats.getTenantId(); try { - TenantId tenantId = jobStats.getTenantId(); - log.info("[{}][{}] Processing job stats: {}", tenantId, jobId, stats); + log.debug("[{}][{}] Processing job stats: {}", tenantId, jobId, stats); jobService.processStats(tenantId, jobId, jobStats); } catch (Exception e) { - log.warn("Failed to process job stats for {}: {}", jobId, jobStats, e); + log.error("[{}][{}] Failed to process job stats: {}", tenantId, jobId, jobStats, e); } }); consumer.commit(); - Thread.sleep(statsProcessingInterval); + Thread.sleep(statsProcessingInterval); // todo: test with bigger interval } @PreDestroy private void destroy() { jobStatsConsumer.stop(); + executor.shutdownNow(); consumerExecutor.shutdownNow(); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java index bed8f3f25e..cda7201e1d 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java @@ -28,11 +28,18 @@ import java.util.function.Consumer; @Component @RequiredArgsConstructor -public class DummyJobProcessor extends JobProcessor { +public class DummyJobProcessor implements JobProcessor { @Override - public int process(Job job, Consumer taskConsumer) { + public int process(Job job, Consumer taskConsumer) throws Exception { DummyJobConfiguration configuration = job.getConfiguration(); + if (configuration.getGeneralError() != null) { + for (int number = 1; number <= configuration.getSubmittedTasksBeforeGeneralError(); number++) { + taskConsumer.accept(createTask(job, configuration, number, null)); + } + Thread.sleep(configuration.getTaskProcessingTimeMs() * (configuration.getSubmittedTasksBeforeGeneralError() / 2)); // sleeping so that some tasks are processed + throw new RuntimeException(configuration.getGeneralError()); + } for (int number = 1; number <= configuration.getSuccessfulTasksCount(); number++) { taskConsumer.accept(createTask(job, configuration, number, null)); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java index 71ff3dcaa2..3932361f3f 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java @@ -25,4 +25,6 @@ public interface JobManager { void cancelJob(TenantId tenantId, JobId jobId); + void onJobUpdate(Job job); + } diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java index 01f7291dd3..2431134e99 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java @@ -15,16 +15,16 @@ */ package org.thingsboard.server.service.job; -import org.thingsboard.server.common.data.job.Task; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.job.Task; import java.util.function.Consumer; -public abstract class JobProcessor { +public interface JobProcessor { - public abstract int process(Job job, Consumer taskConsumer); + int process(Job job, Consumer taskConsumer) throws Exception; - public abstract JobType getType(); + JobType getType(); } diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index b01d0099df..173a528e14 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -33,12 +33,14 @@ import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.dao.task.JobService; import org.thingsboard.server.queue.task.JobStatsService; import org.thingsboard.server.service.job.task.DummyTaskProcessor; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -157,7 +159,7 @@ public class JobManagerTest extends AbstractControllerTest { Job job = findJobById(jobId); assertThat(job.getStatus()).isEqualTo(JobStatus.CANCELLED); assertThat(job.getResult().getSuccessfulCount()).isBetween(1, tasksCount - 1); - assertThat(job.getResult().getCancelledCount()).isBetween(1, tasksCount - 1); + assertThat(job.getResult().getDiscardedCount()).isBetween(1, tasksCount - 1); assertThat(job.getResult().getTotalCount()).isEqualTo(tasksCount); assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount); }); @@ -184,15 +186,14 @@ public class JobManagerTest extends AbstractControllerTest { inv.callRealMethod(); } return null; - }).when(taskProcessor).addToCancelledJobs(any()); // ignoring cancellation event, + }).when(taskProcessor).addToDiscardedJobs(any()); // ignoring cancellation event, jobManager.cancelJob(tenantId, jobId); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { Job job = findJobById(jobId); - System.err.println(job); assertThat(job.getStatus()).isEqualTo(JobStatus.CANCELLED); assertThat(job.getResult().getSuccessfulCount()).isBetween(1, tasksCount - 1); - assertThat(job.getResult().getCancelledCount()).isBetween(1, tasksCount - 1); + assertThat(job.getResult().getDiscardedCount()).isBetween(1, tasksCount - 1); assertThat(job.getResult().getTotalCount()).isEqualTo(tasksCount); assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount); }); @@ -224,12 +225,118 @@ public class JobManagerTest extends AbstractControllerTest { Assertions.assertThat(jobService.findJobsByTenantId(tenantId, new PageLink(100, 0)).getData()).isEmpty(); } + @Test + public void testSubmitMultipleJobs() { + int tasksCount = 3; + int jobsCount = 3; + for (int i = 1; i <= jobsCount; i++) { + Job job = Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job-" + i) + .description("test job") + .configuration(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(1000) + .build()) + .build(); + jobManager.submitJob(job); + } + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + List jobs = findJobs(); + assertThat(jobs).hasSize(jobsCount); + Job firstJob = jobs.get(2); // ordered by createdTime descending + assertThat(firstJob.getStatus()).isEqualTo(JobStatus.RUNNING); + Job secondJob = jobs.get(1); + assertThat(secondJob.getStatus()).isEqualTo(JobStatus.QUEUED); + Job thirdJob = jobs.get(0); + assertThat(thirdJob.getStatus()).isEqualTo(JobStatus.QUEUED); + }); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + List jobs = findJobs(); + for (Job job : jobs) { + assertThat(job.getStatus()).isEqualTo(JobStatus.COMPLETED); + assertThat(job.getResult().getSuccessfulCount()).isEqualTo(tasksCount); + assertThat(job.getResult().getTotalCount()).isEqualTo(tasksCount); + } + }); + } + + @Test + public void testCancelQueuedJob() { + int tasksCount = 3; + int jobsCount = 3; + List jobIds = new ArrayList<>(); + for (int i = 1; i <= jobsCount; i++) { + Job job = Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job-" + i) + .description("test job") + .configuration(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(1000) + .build()) + .build(); + jobIds.add(jobManager.submitJob(job).getId()); + } + + for (int i = 1; i < jobIds.size(); i++) { + jobManager.cancelJob(tenantId, jobIds.get(i)); + } + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + List jobs = findJobs(); + + Job firstJob = jobs.get(2); + assertThat(firstJob.getStatus()).isEqualTo(JobStatus.COMPLETED); + assertThat(firstJob.getResult().getSuccessfulCount()).isEqualTo(tasksCount); + assertThat(firstJob.getResult().getTotalCount()).isEqualTo(tasksCount); + + Job secondJob = jobs.get(1); + assertThat(secondJob.getStatus()).isEqualTo(JobStatus.CANCELLED); + assertThat(secondJob.getResult().getCompletedCount()).isZero(); + + Job thirdJob = jobs.get(0); + assertThat(thirdJob.getStatus()).isEqualTo(JobStatus.CANCELLED); + assertThat(thirdJob.getResult().getCompletedCount()).isZero(); + }); + } + + @Test + public void testGeneralJobError() { + int submittedTasks = 100; + JobId jobId = jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("test job") + .configuration(DummyJobConfiguration.builder() + .generalError("Some error while submitting tasks") + .submittedTasksBeforeGeneralError(submittedTasks) + .taskProcessingTimeMs(10) + .build()) + .build()).getId(); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); + assertThat(job.getResult().getSuccessfulCount()).isBetween(1, submittedTasks); + assertThat(job.getResult().getDiscardedCount()).isBetween(1, submittedTasks); + assertThat(job.getResult().getTotalCount()).isNull(); + }); + } + + // todo: job with zero tasks, reprocessing + private Job findJobById(JobId jobId) throws Exception { return doGet("/api/job/" + jobId, Job.class); } private List findJobs() throws Exception { - return doGetTypedWithPageLink("/api/jobs?", new TypeReference>() {}, new PageLink(100, 0)).getData(); + return doGetTypedWithPageLink("/api/jobs?", new TypeReference>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); } } \ No newline at end of file diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index a0efcf52c1..13c93da411 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -9,7 +9,7 @@ - + diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java similarity index 92% rename from common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java rename to common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java index 9ce802b84f..62fd60eaac 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/task/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.task; +package org.thingsboard.server.dao.job; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; @@ -31,6 +31,8 @@ public interface JobService extends EntityDaoService { void cancelJob(TenantId tenantId, JobId jobId); + void markAsFailed(TenantId tenantId, JobId jobId, String error); + void processStats(TenantId tenantId, JobId jobId, JobStats jobStats); PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java index 5c380c4dfa..8846a6a12f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.job; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import lombok.experimental.SuperBuilder; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.id.EntityId; @@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.id.EntityId; @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder +@ToString(callSuper = true) public class CfReprocessingTask extends Task { private CalculatedField calculatedField; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java index 7fe621c058..70daf6f9c9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java @@ -34,6 +34,9 @@ public class DummyJobConfiguration implements JobConfiguration { private List errors; private int retries; + private String generalError; + private int submittedTasksBeforeGeneralError; + @Override public JobType getType() { return JobType.DUMMY; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java index dee97fc3c9..ae93ed06bb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.job; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import lombok.experimental.SuperBuilder; import java.util.List; @@ -26,6 +27,7 @@ import java.util.List; @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder +@ToString(callSuper = true) public class DummyTask extends Task { private int number; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java index 5a51a49239..e96d42cad1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java @@ -21,6 +21,7 @@ import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.JobId; @@ -28,6 +29,7 @@ import org.thingsboard.server.common.data.id.TenantId; @Data @NoArgsConstructor +@ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) public class Job extends BaseData implements HasTenantId { @@ -51,7 +53,6 @@ public class Job extends BaseData implements HasTenantId { this.key = key; this.description = description; this.configuration = configuration; - this.status = JobStatus.PENDING; this.result = switch (type) { case CF_REPROCESSING -> new CfReprocessingJobResult(); case DUMMY -> new DummyJobResult(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index abaa86facb..b517877906 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -39,15 +39,16 @@ public abstract class JobResult implements Serializable { private int successfulCount; private int failedCount; - private int cancelledCount; + private int discardedCount; private Integer totalCount = null; // set when all tasks are submitted private Map failures = new HashMap<>(); + private String generalError; private long cancellationTs; @JsonIgnore public int getCompletedCount() { - return successfulCount + failedCount + cancelledCount; + return successfulCount + failedCount + discardedCount; } public abstract JobType getJobType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java index 026e19c5b2..17d050a540 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java @@ -16,9 +16,23 @@ package org.thingsboard.server.common.data.job; public enum JobStatus { + QUEUED, PENDING, RUNNING, COMPLETED, FAILED, - CANCELLED + CANCELLED; + + public boolean isOneOf(JobStatus... statuses) { + if (statuses == null) { + return false; + } + for (JobStatus status : statuses) { + if (this == status) { + return true; + } + } + return false; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java index 57f2f44d7a..468173bdf3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java @@ -27,7 +27,7 @@ import lombok.NoArgsConstructor; public class TaskResult { private boolean success; - private boolean cancelled; + private boolean discarded; private TaskFailure failure; @Data diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java index ceba2645f9..8d69f5781b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java @@ -52,7 +52,7 @@ public class JobStatsService { } private void report(TenantId tenantId, JobId jobId, JobStatsMsg.Builder statsMsg) { - log.info("[{}] Reporting: {}", jobId, statsMsg); + log.debug("[{}] Reporting: {}", jobId, statsMsg); statsMsg.setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) .setJobIdMSB(jobId.getId().getMostSignificantBits()) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 2cf0f22b90..f685373f75 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -57,7 +57,7 @@ public abstract class TaskProcessor { private ExecutorService consumerExecutor; private final Set deletedTenants = ConcurrentHashMap.newKeySet(); - private final Set cancelledJobs = ConcurrentHashMap.newKeySet(); // fixme use caffeine + private final Set discardedJobs = ConcurrentHashMap.newKeySet(); // fixme use caffeine @PostConstruct public void init() { @@ -83,14 +83,14 @@ public abstract class TaskProcessor { switch (entityId.getEntityType()) { case JOB -> { if (event.getEvent() == ComponentLifecycleEvent.STOPPED) { - log.info("Adding job {} to cancelledJobs", entityId); - addToCancelledJobs(entityId.getId()); + log.debug("Adding job {} to discarded", entityId); + addToDiscardedJobs(entityId.getId()); } } case TENANT -> { if (event.getEvent() == ComponentLifecycleEvent.DELETED) { deletedTenants.add(entityId.getId()); - log.info("Adding tenant {} to deletedTenants", entityId); + log.debug("Adding tenant {} to deleted", entityId); } } } @@ -100,7 +100,7 @@ public abstract class TaskProcessor { for (TbProtoQueueMsg msg : msgs) { try { Task task = JacksonUtil.fromString(msg.getValue().getValue(), Task.class); - if (cancelledJobs.contains(task.getJobId().getId())) { + if (discardedJobs.contains(task.getJobId().getId())) { log.info("Skipping task '{}' for cancelled job {}", task.getKey(), task.getJobId()); reportCancelled(task); continue; @@ -157,13 +157,13 @@ public abstract class TaskProcessor { private void reportCancelled(Task task) { TaskResult result = TaskResult.builder() - .cancelled(true) + .discarded(true) .build(); statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); } - public void addToCancelledJobs(UUID jobId) { - cancelledJobs.add(jobId); + public void addToDiscardedJobs(UUID jobId) { + discardedJobs.add(jobId); } @PreDestroy diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java similarity index 54% rename from dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java rename to dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index 16b4397d87..c632a7b58b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/task/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.task; +package org.thingsboard.server.dao.job; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -28,17 +28,24 @@ import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobStatus; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.data.job.TaskResult.TaskFailure; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import java.util.Optional; +import static org.thingsboard.server.common.data.job.JobStatus.CANCELLED; +import static org.thingsboard.server.common.data.job.JobStatus.COMPLETED; +import static org.thingsboard.server.common.data.job.JobStatus.FAILED; +import static org.thingsboard.server.common.data.job.JobStatus.PENDING; +import static org.thingsboard.server.common.data.job.JobStatus.QUEUED; +import static org.thingsboard.server.common.data.job.JobStatus.RUNNING; + @Service @RequiredArgsConstructor @Slf4j @@ -47,10 +54,16 @@ public class DefaultJobService extends AbstractEntityService implements JobServi private final JobDao jobDao; private final JobValidator validator = new JobValidator(); + @Transactional @Override public Job createJob(TenantId tenantId, Job job) { validator.validate(job, Job::getTenantId); - return saveJob(tenantId, job, false); + if (jobDao.existsByTenantIdAndTypeAndStatusOneOf(tenantId, job.getType(), PENDING, RUNNING)) { + job.setStatus(QUEUED); + } else { + job.setStatus(PENDING); + } + return saveJob(tenantId, job, true, null); } @Override @@ -62,11 +75,27 @@ public class DefaultJobService extends AbstractEntityService implements JobServi @Override public void cancelJob(TenantId tenantId, JobId jobId) { Job job = findForUpdate(tenantId, jobId); - if (job.getStatus() != JobStatus.PENDING && job.getStatus() != JobStatus.RUNNING) { + if (!job.getStatus().isOneOf(QUEUED, PENDING, RUNNING)) { throw new IllegalArgumentException("Job already " + job.getStatus().name().toLowerCase()); } job.getResult().setCancellationTs(System.currentTimeMillis()); - saveJob(tenantId, job, true); + JobStatus prevStatus = job.getStatus(); + if (job.getStatus() == QUEUED) { + job.setStatus(CANCELLED); // setting cancelled status right away, because we don't expect stats for cancelled tasks + } else if (job.getStatus() == PENDING) { + job.setStatus(RUNNING); + } + saveJob(tenantId, job, true, prevStatus); + } + + @Transactional + @Override + public void markAsFailed(TenantId tenantId, JobId jobId, String error) { + Job job = findForUpdate(tenantId, jobId); + job.getResult().setGeneralError(error); + JobStatus prevStatus = job.getStatus(); + job.setStatus(FAILED); + saveJob(tenantId, job, true, prevStatus); } @Transactional @@ -74,39 +103,36 @@ public class DefaultJobService extends AbstractEntityService implements JobServi public void processStats(TenantId tenantId, JobId jobId, JobStats jobStats) { Job job = findForUpdate(tenantId, jobId); if (job == null) { - log.info("Got stale stats for job {}: {}", jobId, jobStats); + log.debug("[{}][{}] Got stale stats: {}", tenantId, jobId, jobStats); return; } - switch (job.getStatus()) { - case PENDING -> { - job.setStatus(JobStatus.RUNNING); - } - case CANCELLED, COMPLETED, FAILED -> { - // got some stale stats - return; - } + JobStatus prevStatus = job.getStatus(); + if (job.getStatus() == PENDING) { + job.setStatus(RUNNING); } - JobResult jobResult = job.getResult(); + JobResult result = job.getResult(); if (jobStats.getTotalTasksCount() != null) { - jobResult.setTotalCount(jobStats.getTotalTasksCount()); + result.setTotalCount(jobStats.getTotalTasksCount()); } boolean publishEvent = false; for (TaskResult taskResult : jobStats.getTaskResults()) { if (taskResult.isSuccess()) { - jobResult.setSuccessfulCount(jobResult.getSuccessfulCount() + 1); - } else if (taskResult.isCancelled()) { - jobResult.setCancelledCount(jobResult.getCancelledCount() + 1); + result.setSuccessfulCount(result.getSuccessfulCount() + 1); + } else if (taskResult.isDiscarded()) { + result.setDiscardedCount(result.getDiscardedCount() + 1); } else { TaskFailure failure = taskResult.getFailure(); String key = failure.getTask().getKey(); - jobResult.setFailedCount(jobResult.getFailedCount() + 1); - jobResult.getFailures().put(key, failure.getError()); + result.setFailedCount(result.getFailedCount() + 1); + if (result.getFailures().size() < 1000) { // preserving only first 1000 errors, not reprocessing if there are more failures + result.getFailures().put(key, failure.getError()); + } } - if (jobResult.getCancellationTs() > 0) { - if (!taskResult.isCancelled() && System.currentTimeMillis() > jobResult.getCancellationTs()) { + if (result.getCancellationTs() > 0) { + if (!taskResult.isDiscarded() && System.currentTimeMillis() > result.getCancellationTs()) { log.info("Got task result for cancelled job {}: {}, re-notifying processors about cancellation", jobId, taskResult); // task processor forgot the task is cancelled publishEvent = true; @@ -114,32 +140,49 @@ public class DefaultJobService extends AbstractEntityService implements JobServi } } - if (jobResult.getTotalCount() != null && jobResult.getCompletedCount() >= jobResult.getTotalCount()) { - if (jobResult.getCancellationTs() > 0) { - job.setStatus(JobStatus.CANCELLED); - } else if (jobResult.getFailedCount() > 0) { - job.setStatus(JobStatus.FAILED); - } else { - job.setStatus(JobStatus.COMPLETED); + if (job.getStatus() == RUNNING) { + if (result.getTotalCount() != null && result.getCompletedCount() >= result.getTotalCount()) { + if (result.getCancellationTs() > 0) { + job.setStatus(CANCELLED); + } else if (result.getFailedCount() > 0) { + job.setStatus(FAILED); + } else { + job.setStatus(COMPLETED); + } } } - log.info("Saving job {}", job); - saveJob(tenantId, job, publishEvent); + + saveJob(tenantId, job, publishEvent, prevStatus); } - private Job saveJob(TenantId tenantId, Job job, boolean publishEvent) { + private Job saveJob(TenantId tenantId, Job job, boolean publishEvent, JobStatus prevStatus) { job = jobDao.save(tenantId, job); if (publishEvent) { eventPublisher.publishEvent(SaveEntityEvent.builder() .tenantId(tenantId) .entityId(job.getId()) .entity(job) - .created(false) .build()); } + log.info("[{}] Saved job: {}", tenantId, job); + if (prevStatus != null && job.getStatus() != prevStatus) { + log.info("[{}][{}][{}] New job status: {} -> {}", tenantId, job.getId(), job.getType(), prevStatus, job.getStatus()); + if (job.getStatus().isOneOf(CANCELLED, COMPLETED, FAILED) && prevStatus != QUEUED) { // if prev status is QUEUED - means there are already running jobs with this type, no need to check for waiting job + checkWaitingJobs(tenantId, job.getType()); + } + } return job; } + private void checkWaitingJobs(TenantId tenantId, JobType jobType) { + Job queuedJob = jobDao.findOldestByTenantIdAndTypeAndStatusForUpdate(tenantId, jobType, QUEUED); + if (queuedJob == null) { + return; + } + queuedJob.setStatus(PENDING); + saveJob(tenantId, queuedJob, true, QUEUED); + } + @Override public PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink) { return jobDao.findByTenantId(tenantId, pageLink); @@ -149,15 +192,15 @@ public class DefaultJobService extends AbstractEntityService implements JobServi return jobDao.findByIdForUpdate(tenantId, jobId); } - // todo: cancellation, reprocessing +// todo: reprocessing public class JobValidator extends DataValidator { @Override protected void validateCreate(TenantId tenantId, Job job) { - if (jobDao.existsByTenantIdAndTypeAndStatusOneOf(tenantId, job.getType(), JobStatus.PENDING, JobStatus.RUNNING)) { - throw new DataValidationException("Job of this type is already running"); - } +// if (jobDao.existsByTenantIdAndTypeAndStatusOneOf(tenantId, job.getType(), PENDING, RUNNING)) { +// throw new DataValidationException("Job of this type is already running"); +// } } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java similarity index 90% rename from dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java rename to dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java index 3b1d6631dd..799717fea8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/task/JobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.task; +package org.thingsboard.server.dao.job; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; @@ -34,4 +34,6 @@ public interface JobDao extends Dao { boolean existsByTenantIdAndTypeAndStatusOneOf(TenantId tenantId, JobType type, JobStatus... statuses); + Job findOldestByTenantIdAndTypeAndStatusForUpdate(TenantId tenantId, JobType type, JobStatus status); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java similarity index 56% rename from dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java index 472df05bdd..bec5bf5f87 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JobRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java @@ -13,15 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.task; +package org.thingsboard.server.dao.sql.job; import jakarta.persistence.LockModeType; -import jakarta.transaction.Transactional; +import org.springframework.data.domain.Limit; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -36,7 +35,7 @@ import java.util.UUID; public interface JobRepository extends JpaRepository { @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId " + - "AND (:searchText IS NULL OR ilike(j.key, concat('%', :searchText, '%')) = true " + + "AND (:searchText IS NULL OR ilike(j.key, concat('%', :searchText, '%')) = true " + "OR ilike(j.description, concat('%', :searchText, '%')) = true)") Page findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, @@ -46,45 +45,13 @@ public interface JobRepository extends JpaRepository { @Query("SELECT j FROM JobEntity j WHERE j.id = :id") JobEntity findByIdForUpdate(UUID id); - @Modifying - @Transactional - @Query(value = """ - UPDATE job - SET result = jsonb_set( - result, - '{successfulCount}', - to_jsonb((result->>'successfulCount')::int + :count) - ) - WHERE id = :jobId - RETURNING ((result->>'successfulCount')::int + :count) - + (result->>'failedCount')::int = (result->>'totalCount')::int - """, nativeQuery = true) - boolean reportTaskSuccess(@Param("jobId") UUID jobId, - @Param("count") int count); - - @Modifying - @Transactional - @Query(value = """ - UPDATE job - SET result = jsonb_set( - jsonb_set( - result, - '{failedCount}', - to_jsonb((result->>'failedCount')::int + 1) - ), - ARRAY['failures', :taskKey], - to_jsonb(:error) - ) - WHERE id = :jobId - RETURNING ((result->>'failedCount')::int + 1) + (result->>'successfulCount')::int - = (result->>'totalCount')::int - """, nativeQuery = true) - boolean reportTaskFailure(@Param("jobId") UUID jobId, - @Param("taskKey") String taskKey, - @Param("error") String error); - boolean existsByKeyAndStatusIn(String key, List statuses); boolean existsByTenantIdAndTypeAndStatusIn(UUID tenantId, JobType type, List statuses); + @Lock(LockModeType.PESSIMISTIC_WRITE) // SELECT FOR UPDATE + @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId AND j.type = :type " + + "AND j.status = :status ORDER BY j.createdTime ASC, j.id ASC") + JobEntity findOldestByTenantIdAndTypeAndStatusForUpdate(UUID tenantId, JobType type, JobStatus status, Limit limit); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java similarity index 87% rename from dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java index 92b9fc8a72..1b3a394e28 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/task/JpaJobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.task; +package org.thingsboard.server.dao.sql.job; import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Limit; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; @@ -30,7 +31,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.JobEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; -import org.thingsboard.server.dao.task.JobDao; +import org.thingsboard.server.dao.job.JobDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.Arrays; @@ -63,6 +64,11 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao return jobRepository.existsByTenantIdAndTypeAndStatusIn(tenantId.getId(), type, Arrays.stream(statuses).toList()); } + @Override + public Job findOldestByTenantIdAndTypeAndStatusForUpdate(TenantId tenantId, JobType type, JobStatus status) { + return DaoUtil.getData(jobRepository.findOldestByTenantIdAndTypeAndStatusForUpdate(tenantId.getId(), type, status, Limit.of(1))); + } + @Override public EntityType getEntityType() { return EntityType.JOB; From 1562fc19d9440f1fd9d1538b9a22bfda93f4fb4f Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 28 Apr 2025 17:33:52 +0300 Subject: [PATCH 100/335] Test CF reprocessing jobs --- .../server/controller/AbstractWebTest.java | 11 +++++++++++ .../server/service/job/JobManagerTest.java | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 33f2209eca..7e50168786 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -102,10 +102,12 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationType; @@ -128,6 +130,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.security.Authority; @@ -1253,4 +1256,12 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { new PageLink(limit, 0), unreadOnly, deliveryMethod).getData(); } + protected Job findJobById(JobId jobId) throws Exception { + return doGet("/api/job/" + jobId, Job.class); + } + + protected List findJobs() throws Exception { + return doGetTypedWithPageLink("/api/jobs?", new TypeReference>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); + } + } diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 173a528e14..784b5bb2dd 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.job; -import com.fasterxml.jackson.core.type.TypeReference; import org.assertj.core.api.Assertions; import org.junit.After; import org.junit.Before; @@ -31,9 +30,7 @@ import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -331,12 +328,4 @@ public class JobManagerTest extends AbstractControllerTest { // todo: job with zero tasks, reprocessing - private Job findJobById(JobId jobId) throws Exception { - return doGet("/api/job/" + jobId, Job.class); - } - - private List findJobs() throws Exception { - return doGetTypedWithPageLink("/api/jobs?", new TypeReference>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); - } - } \ No newline at end of file From 479ff8e25e1677b006a072eb12a645d052d54fc9 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 30 Apr 2025 11:05:35 +0300 Subject: [PATCH 101/335] Jobs reprocessing --- .../server/controller/JobController.java | 7 ++ .../job/CfReprocessingJobProcessor.java | 28 ++++- .../server/service/job/DefaultJobManager.java | 69 ++++++++--- .../server/service/job/DummyJobProcessor.java | 36 ++++-- .../server/service/job/JobManager.java | 2 + .../server/service/job/JobProcessor.java | 4 + .../service/job/task/DummyTaskProcessor.java | 3 + .../server/controller/AbstractWebTest.java | 8 ++ .../server/service/job/JobManagerTest.java | 115 ++++++++++++++++-- .../server/dao/job/JobService.java | 2 +- .../job/CfReprocessingJobConfiguration.java | 8 +- .../common/data/job/CfReprocessingTask.java | 29 +++++ .../data/job/DummyJobConfiguration.java | 5 +- .../server/common/data/job/DummyTask.java | 32 +++++ .../common/data/job/JobConfiguration.java | 9 +- .../server/common/data/job/JobResult.java | 6 +- .../server/common/data/job/JobStatus.java | 1 + .../server/common/data/job/Task.java | 7 +- .../server/common/data/job/TaskFailure.java | 43 +++++++ .../server/common/data/job/TaskResult.java | 9 -- .../server/queue/task/TaskProcessor.java | 6 +- .../server/dao/job/DefaultJobService.java | 31 +---- 22 files changed, 373 insertions(+), 87 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java index 5718d6e388..9b6627e12a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/JobController.java +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -82,4 +82,11 @@ public class JobController extends BaseController { jobManager.cancelJob(getTenantId(), new JobId(id)); } + @PostMapping("/job/{id}/reprocess") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + public void reprocessJob(@PathVariable UUID id) throws ThingsboardException { + // todo check permissions + jobManager.reprocessJob(getTenantId(), new JobId(id)); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java index edb38da5c2..79a735f6a6 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java @@ -24,19 +24,24 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.job.CfReprocessingJobConfiguration; import org.thingsboard.server.common.data.job.CfReprocessingTask; +import org.thingsboard.server.common.data.job.CfReprocessingTask.CfReprocessingTaskFailure; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; +import org.thingsboard.server.common.data.job.TaskFailure; import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.cf.CalculatedFieldService; import org.thingsboard.server.dao.device.DeviceService; +import java.util.List; import java.util.function.Consumer; @Component @RequiredArgsConstructor public class CfReprocessingJobProcessor implements JobProcessor { + private final CalculatedFieldService calculatedFieldService; private final DeviceService deviceService; private final AssetService assetService; @@ -44,12 +49,12 @@ public class CfReprocessingJobProcessor implements JobProcessor { public int process(Job job, Consumer taskConsumer) throws Exception { CfReprocessingJobConfiguration configuration = job.getConfiguration(); - CalculatedField calculatedField = configuration.getCalculatedField(); + CalculatedField calculatedField = calculatedFieldService.findById(job.getTenantId(), configuration.getCalculatedFieldId()); EntityId cfEntityId = calculatedField.getEntityId(); int tasksCount = 0; if (cfEntityId.getEntityType().isOneOf(EntityType.DEVICE, EntityType.ASSET)) { - taskConsumer.accept(createTask(job, configuration, cfEntityId)); + taskConsumer.accept(createTask(job, configuration, calculatedField, cfEntityId)); tasksCount++; } else { PageDataIterable entities; @@ -61,20 +66,31 @@ public class CfReprocessingJobProcessor implements JobProcessor { throw new IllegalArgumentException("Unsupported CF entity type " + cfEntityId.getEntityType()); } for (EntityId entityId : entities) { - taskConsumer.accept(createTask(job, configuration, entityId)); + taskConsumer.accept(createTask(job, configuration, calculatedField, entityId)); tasksCount++; } } return tasksCount; } - private Task createTask(Job job, CfReprocessingJobConfiguration configuration, EntityId entityId) { + @Override + public void reprocess(Job job, List failures, Consumer taskConsumer) throws Exception { + CfReprocessingJobConfiguration configuration = job.getConfiguration(); + CalculatedField calculatedField = calculatedFieldService.findById(job.getTenantId(), configuration.getCalculatedFieldId()); + + for (TaskFailure failure : failures) { + CfReprocessingTaskFailure taskFailure = (CfReprocessingTaskFailure) failure; + EntityId entityId = taskFailure.getEntityId(); + taskConsumer.accept(createTask(job, job.getConfiguration(), calculatedField, entityId)); + } + } + + private Task createTask(Job job, CfReprocessingJobConfiguration configuration, CalculatedField calculatedField, EntityId entityId) { return CfReprocessingTask.builder() .tenantId(job.getTenantId()) .jobId(job.getId()) - .key(entityId.getEntityType().getNormalName() + " " + entityId.getId()) .retries(2) // 3 attempts in total - .calculatedField(configuration.getCalculatedField()) + .calculatedField(calculatedField) .entityId(entityId) .startTs(configuration.getStartTs()) .endTs(configuration.getEndTs()) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 6554bebe9f..b72974144d 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -27,10 +27,12 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; +import org.thingsboard.server.common.data.job.TaskFailure; import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.job.JobService; @@ -48,6 +50,7 @@ import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -98,37 +101,73 @@ public class DefaultJobManager implements JobManager { @Override public Job submitJob(Job job) { log.debug("Submitting job: {}", job); - return jobService.createJob(job.getTenantId(), job); + return jobService.submitJob(job.getTenantId(), job); } @Override public void onJobUpdate(Job job) { if (job.getStatus() == JobStatus.PENDING) { executor.execute(() -> { - TenantId tenantId = job.getTenantId(); - JobId jobId = job.getId(); - try { - int tasksCount = jobProcessors.get(job.getType()).process(job, this::submitTask); // todo: think about stopping tb - while tasks are being submitted - log.info("[{}][{}][{}] Submitted {} tasks", tenantId, jobId, job.getType(), tasksCount); - jobStatsService.reportAllTasksSubmitted(tenantId, jobId, tasksCount); - } catch (Throwable e) { - log.error("[{}][{}][{}] Failed to submit tasks", tenantId, jobId, job.getType(), e); - try { - jobService.markAsFailed(tenantId, jobId, ExceptionUtils.getStackTrace(e)); - } catch (Throwable e2) { - log.error("[{}][{}] Failed to mark job as failed", tenantId, jobId, e2); - } - } + processJob(job); }); } } + private void processJob(Job job) { + TenantId tenantId = job.getTenantId(); + JobId jobId = job.getId(); + try { + JobProcessor processor = jobProcessors.get(job.getType()); + List toReprocess = job.getConfiguration().getToReprocess(); + if (toReprocess == null) { + int tasksCount = processor.process(job, this::submitTask); // todo: think about stopping tb - while tasks are being submitted + log.info("[{}][{}][{}] Submitted {} tasks", tenantId, jobId, job.getType(), tasksCount); + jobStatsService.reportAllTasksSubmitted(tenantId, jobId, tasksCount); + } else { + processor.reprocess(job, toReprocess, this::submitTask); + log.info("[{}][{}][{}] Submitted {} tasks for reprocessing", tenantId, jobId, job.getType(), toReprocess.size()); + } + } catch (Throwable e) { + log.error("[{}][{}][{}] Failed to submit tasks", tenantId, jobId, job.getType(), e); + try { + jobService.markAsFailed(tenantId, jobId, ExceptionUtils.getStackTrace(e)); + } catch (Throwable e2) { + log.error("[{}][{}] Failed to mark job as failed", tenantId, jobId, e2); + } + } + } + @Override public void cancelJob(TenantId tenantId, JobId jobId) { log.info("[{}][{}] Cancelling job", tenantId, jobId); jobService.cancelJob(tenantId, jobId); } + @Override + public void reprocessJob(TenantId tenantId, JobId jobId) { + log.info("[{}][{}] Reprocessing job", tenantId, jobId); + Job job = jobService.findJobById(tenantId, jobId); + if (job.getStatus() != JobStatus.FAILED) { + throw new IllegalArgumentException("Job is not failed"); + } + + JobResult result = job.getResult(); + if (result.getGeneralError() != null) { + throw new IllegalArgumentException("Reprocessing not allowed since job has general error"); + } + List failures = result.getFailures(); + if (result.getFailedCount() > failures.size()) { + throw new IllegalArgumentException("Reprocessing not allowed since there are too many failures (more than " + failures.size() + ")"); + } + + result.setFailedCount(0); + result.setFailures(Collections.emptyList()); + + job.getConfiguration().setToReprocess(failures); + + jobService.submitJob(tenantId, job); + } + private void submitTask(Task task) { log.info("[{}][{}] Submitting task: {}", task.getTenantId(), task.getJobId(), task); TaskProto taskProto = TaskProto.newBuilder() diff --git a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java index cda7201e1d..900b876c09 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java @@ -19,10 +19,13 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.job.DummyJobConfiguration; import org.thingsboard.server.common.data.job.DummyTask; +import org.thingsboard.server.common.data.job.DummyTask.DummyTaskFailure; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; +import org.thingsboard.server.common.data.job.TaskFailure; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -35,31 +38,48 @@ public class DummyJobProcessor implements JobProcessor { DummyJobConfiguration configuration = job.getConfiguration(); if (configuration.getGeneralError() != null) { for (int number = 1; number <= configuration.getSubmittedTasksBeforeGeneralError(); number++) { - taskConsumer.accept(createTask(job, configuration, number, null)); + taskConsumer.accept(createTask(job, configuration, number, null, false)); } Thread.sleep(configuration.getTaskProcessingTimeMs() * (configuration.getSubmittedTasksBeforeGeneralError() / 2)); // sleeping so that some tasks are processed throw new RuntimeException(configuration.getGeneralError()); } - for (int number = 1; number <= configuration.getSuccessfulTasksCount(); number++) { - taskConsumer.accept(createTask(job, configuration, number, null)); + + int taskNumber = 1; + for (int i = 0; i < configuration.getSuccessfulTasksCount(); i++) { + taskConsumer.accept(createTask(job, configuration, taskNumber, null, false)); + taskNumber++; } if (configuration.getErrors() != null) { - for (int number = 1; number <= configuration.getFailedTasksCount(); number++) { - taskConsumer.accept(createTask(job, configuration, number, configuration.getErrors())); + for (int i = 0; i < configuration.getFailedTasksCount(); i++) { + taskConsumer.accept(createTask(job, configuration, taskNumber, configuration.getErrors(), false)); + taskNumber++; + } + for (int i = 0; i < configuration.getPermanentlyFailedTasksCount(); i++) { + taskConsumer.accept(createTask(job, configuration, taskNumber, configuration.getErrors(), true)); + taskNumber++; } } - return configuration.getSuccessfulTasksCount() + configuration.getFailedTasksCount(); + return configuration.getSuccessfulTasksCount() + configuration.getFailedTasksCount() + configuration.getPermanentlyFailedTasksCount(); + } + + @Override + public void reprocess(Job job, List failures, Consumer taskConsumer) throws Exception { + for (TaskFailure failure : failures) { + DummyTaskFailure taskFailure = (DummyTaskFailure) failure; + taskConsumer.accept(createTask(job, job.getConfiguration(), taskFailure.getNumber(), taskFailure.isFailAlways() ? + List.of(taskFailure.getError()) : Collections.emptyList(), taskFailure.isFailAlways())); + } } - private Task createTask(Job job, DummyJobConfiguration configuration, int number, List errors) { + private Task createTask(Job job, DummyJobConfiguration configuration, int number, List errors, boolean failAlways) { return DummyTask.builder() .tenantId(job.getTenantId()) .jobId(job.getId()) - .key("Task " + number) .retries(configuration.getRetries()) .number(number) .processingTimeMs(configuration.getTaskProcessingTimeMs()) .errors(errors) + .failAlways(failAlways) .build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java index 3932361f3f..8e4858ebe3 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobManager.java @@ -25,6 +25,8 @@ public interface JobManager { void cancelJob(TenantId tenantId, JobId jobId); + void reprocessJob(TenantId tenantId, JobId jobId); + void onJobUpdate(Job job); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java index 2431134e99..da2c75d166 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java @@ -18,13 +18,17 @@ package org.thingsboard.server.service.job; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; +import org.thingsboard.server.common.data.job.TaskFailure; +import java.util.List; import java.util.function.Consumer; public interface JobProcessor { int process(Job job, Consumer taskConsumer) throws Exception; + void reprocess(Job job, List failures, Consumer taskConsumer) throws Exception; + JobType getType(); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java index dc4a193bb9..73a27a0012 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java @@ -30,6 +30,9 @@ public class DummyTaskProcessor extends TaskProcessor { if (task.getProcessingTimeMs() > 0) { Thread.sleep(task.getProcessingTimeMs()); } + if (task.isFailAlways()) { + throw new RuntimeException(task.getErrors().get(0)); + } if (task.getErrors() != null && task.getAttempt() <= task.getErrors().size()) { String error = task.getErrors().get(task.getAttempt() - 1); throw new RuntimeException(error); diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 7e50168786..d580a6b12a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -1264,4 +1264,12 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return doGetTypedWithPageLink("/api/jobs?", new TypeReference>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); } + protected void cancelJob(JobId jobId) throws Exception { + doPost("/api/job/" + jobId + "/cancel").andExpect(status().isOk()); + } + + protected void reprocessJob(JobId jobId) throws Exception { + doPost("/api/job/" + jobId + "/reprocess").andExpect(status().isOk()); + } + } diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 784b5bb2dd..f9881d0248 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -26,6 +26,7 @@ import org.springframework.test.context.TestPropertySource; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.DummyJobConfiguration; +import org.thingsboard.server.common.data.job.DummyTask.DummyTaskFailure; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStatus; @@ -130,8 +131,8 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(jobResult.getSuccessfulCount()).isEqualTo(successfulTasks); assertThat(jobResult.getFailedCount()).isEqualTo(failedTasks); assertThat(jobResult.getTotalCount()).isEqualTo(successfulTasks + failedTasks); - assertThat(jobResult.getFailures().get("Task 1")).isEqualTo("error3"); // last error - assertThat(jobResult.getFailures().get("Task 2")).isEqualTo("error3"); // last error + assertThat(jobResult.getFailures().get(0).getError()).isEqualTo("error3"); // last error + assertThat(jobResult.getFailures().get(1).getError()).isEqualTo("error3"); // last error assertThat(jobResult.getCompletedCount()).isEqualTo(jobResult.getTotalCount()); }); } @@ -151,7 +152,7 @@ public class JobManagerTest extends AbstractControllerTest { .build()).getId(); Thread.sleep(500); - jobManager.cancelJob(tenantId, jobId); + cancelJob(jobId); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { Job job = findJobById(jobId); assertThat(job.getStatus()).isEqualTo(JobStatus.CANCELLED); @@ -163,7 +164,7 @@ public class JobManagerTest extends AbstractControllerTest { } @Test - public void testCancelJob_simulateTaskProcessorRestart() { + public void testCancelJob_simulateTaskProcessorRestart() throws Exception { int tasksCount = 10; JobId jobId = jobManager.submitJob(Job.builder() .tenantId(tenantId) @@ -184,7 +185,7 @@ public class JobManagerTest extends AbstractControllerTest { } return null; }).when(taskProcessor).addToDiscardedJobs(any()); // ignoring cancellation event, - jobManager.cancelJob(tenantId, jobId); + cancelJob(jobId); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { Job job = findJobById(jobId); @@ -262,7 +263,7 @@ public class JobManagerTest extends AbstractControllerTest { } @Test - public void testCancelQueuedJob() { + public void testCancelQueuedJob() throws Exception { int tasksCount = 3; int jobsCount = 3; List jobIds = new ArrayList<>(); @@ -281,7 +282,7 @@ public class JobManagerTest extends AbstractControllerTest { } for (int i = 1; i < jobIds.size(); i++) { - jobManager.cancelJob(tenantId, jobIds.get(i)); + cancelJob(jobIds.get(i)); } await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -326,6 +327,104 @@ public class JobManagerTest extends AbstractControllerTest { }); } - // todo: job with zero tasks, reprocessing + @Test + public void testJobReprocessing() throws Exception { + int successfulTasks = 3; + int failedTasks = 2; + int totalTasksCount = successfulTasks + failedTasks; + JobId jobId = jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("test job") + .configuration(DummyJobConfiguration.builder() + .successfulTasksCount(successfulTasks) + .failedTasksCount(failedTasks) + .errors(List.of("error")) + .taskProcessingTimeMs(100) + .build()) + .build()).getId(); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); + JobResult jobResult = job.getResult(); + assertThat(jobResult.getSuccessfulCount()).isEqualTo(successfulTasks); + assertThat(jobResult.getFailedCount()).isEqualTo(failedTasks); + + for (int i = 0, taskNumber = successfulTasks + 1; taskNumber <= totalTasksCount; i++, taskNumber++) { + DummyTaskFailure failure = (DummyTaskFailure) jobResult.getFailures().get(i); + assertThat(failure.getNumber()).isEqualTo(taskNumber); + assertThat(failure.getError()).isEqualTo("error"); + } + }); + + reprocessJob(jobId); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.COMPLETED); + assertThat(job.getResult().getSuccessfulCount()).isEqualTo(totalTasksCount); + assertThat(job.getResult().getFailedCount()).isZero(); + assertThat(job.getResult().getTotalCount()).isEqualTo(totalTasksCount); + assertThat(job.getResult().getFailures()).isEmpty(); + }); + } + + @Test + public void testJobReprocessing_somePermanentlyFailed() throws Exception { + int successfulTasks = 3; + int failedTasks = 2; + int permanentlyFailedTasks = 1; + int totalTasksCount = successfulTasks + failedTasks + permanentlyFailedTasks; + JobId jobId = jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("test job") + .configuration(DummyJobConfiguration.builder() + .successfulTasksCount(successfulTasks) + .failedTasksCount(failedTasks) + .permanentlyFailedTasksCount(permanentlyFailedTasks) + .errors(List.of("error")) + .taskProcessingTimeMs(100) + .build()) + .build()).getId(); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); + JobResult jobResult = job.getResult(); + assertThat(jobResult.getSuccessfulCount()).isEqualTo(successfulTasks); + assertThat(jobResult.getFailedCount()).isEqualTo(failedTasks + permanentlyFailedTasks); + assertThat(jobResult.getTotalCount()).isEqualTo(totalTasksCount); + + for (int i = 0, taskNumber = successfulTasks + 1; taskNumber <= totalTasksCount; i++, taskNumber++) { + DummyTaskFailure failure = (DummyTaskFailure) jobResult.getFailures().get(i); + assertThat(failure.getNumber()).isEqualTo(taskNumber); + assertThat(failure.getError()).isEqualTo("error"); + } + }); + + reprocessJob(jobId); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); + JobResult jobResult = job.getResult(); + assertThat(jobResult.getSuccessfulCount()).isEqualTo(successfulTasks + failedTasks); + assertThat(jobResult.getFailedCount()).isEqualTo(permanentlyFailedTasks); + assertThat(jobResult.getTotalCount()).isEqualTo(totalTasksCount); + + for (int i = 0, taskNumber = successfulTasks + failedTasks + 1; taskNumber <= totalTasksCount; i++, taskNumber++) { + DummyTaskFailure failure = (DummyTaskFailure) jobResult.getFailures().get(i); + assertThat(failure.getNumber()).isEqualTo(taskNumber); + assertThat(failure.getError()).isEqualTo("error"); + assertThat(failure.isFailAlways()).isTrue(); + } + }); + } + + // todo: job with zero tasks } \ No newline at end of file diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java index 62fd60eaac..dd333c072e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java @@ -25,7 +25,7 @@ import org.thingsboard.server.dao.entity.EntityDaoService; public interface JobService extends EntityDaoService { - Job createJob(TenantId tenantId, Job job); + Job submitJob(TenantId tenantId, Job job); Job findJobById(TenantId tenantId, JobId jobId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java index 797dd0b639..90de047554 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java @@ -19,17 +19,19 @@ import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.cf.CalculatedField; +import org.thingsboard.server.common.data.id.CalculatedFieldId; @Data +@EqualsAndHashCode(callSuper = true) @AllArgsConstructor @NoArgsConstructor @Builder -public class CfReprocessingJobConfiguration implements JobConfiguration { +public class CfReprocessingJobConfiguration extends JobConfiguration { @NotNull - private CalculatedField calculatedField; + private CalculatedFieldId calculatedFieldId; private long startTs; private long endTs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java index 8846a6a12f..3c8b765527 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java @@ -35,9 +35,38 @@ public class CfReprocessingTask extends Task { private long startTs; private long endTs; + @Override + public Object getKey() { + return entityId; + } + + @Override + public TaskFailure toFailure(Throwable error) { + return new CfReprocessingTaskFailure(entityId, error.getMessage()); + } + @Override public JobType getJobType() { return JobType.CF_REPROCESSING; } + @Data + @EqualsAndHashCode(callSuper = true) + @NoArgsConstructor + public static class CfReprocessingTaskFailure extends TaskFailure { + + private EntityId entityId; + + public CfReprocessingTaskFailure(EntityId entityId, String error) { + super(error); + this.entityId = entityId; + } + + @Override + public JobType getJobType() { + return JobType.CF_REPROCESSING; + } + + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java index 70daf6f9c9..62695eb237 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java @@ -18,19 +18,22 @@ package org.thingsboard.server.common.data.job; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import java.util.List; @Data +@EqualsAndHashCode(callSuper = true) @AllArgsConstructor @NoArgsConstructor @Builder -public class DummyJobConfiguration implements JobConfiguration { +public class DummyJobConfiguration extends JobConfiguration { private long taskProcessingTimeMs; private int successfulTasksCount; private int failedTasksCount; + private int permanentlyFailedTasksCount; private List errors; private int retries; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java index ae93ed06bb..ac15dc63cc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java @@ -33,10 +33,42 @@ public class DummyTask extends Task { private int number; private long processingTimeMs; private List errors; // errors for each attempt + private boolean failAlways; + + @Override + public Object getKey() { + return number; + } + + @Override + public TaskFailure toFailure(Throwable error) { + return new DummyTaskFailure(number, failAlways, error.getMessage()); + } @Override public JobType getJobType() { return JobType.DUMMY; } + @Data + @EqualsAndHashCode(callSuper = true) + @NoArgsConstructor + public static class DummyTaskFailure extends TaskFailure { + + private int number; + private boolean failAlways; + + public DummyTaskFailure(int number, boolean failAlways, String error) { + super(error); + this.number = number; + this.failAlways = failAlways; + } + + @Override + public JobType getJobType() { + return JobType.DUMMY; + } + + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java index b541458289..0d3620d9e8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java @@ -19,8 +19,10 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; import java.io.Serializable; +import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @@ -28,8 +30,11 @@ import java.io.Serializable; @Type(name = "CF_REPROCESSING", value = CfReprocessingJobConfiguration.class), @Type(name = "DUMMY", value = DummyJobConfiguration.class), }) -public interface JobConfiguration extends Serializable { +@Data +public abstract class JobConfiguration implements Serializable { - JobType getType(); + private List toReprocess; + + public abstract JobType getType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index b517877906..748d24811c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -24,8 +24,8 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @@ -41,7 +41,7 @@ public abstract class JobResult implements Serializable { private int failedCount; private int discardedCount; private Integer totalCount = null; // set when all tasks are submitted - private Map failures = new HashMap<>(); + private List failures = new ArrayList<>(); private String generalError; private long cancellationTs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java index 17d050a540..5a4a9e35ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStatus.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.job; public enum JobStatus { + QUEUED, PENDING, RUNNING, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java index 5ef735f5d3..6399f6f79a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.job; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; @@ -38,7 +39,6 @@ public abstract class Task { private TenantId tenantId; private JobId jobId; - private String key; private int retries; public Task() { @@ -46,6 +46,11 @@ public abstract class Task { private int attempt = 0; + @JsonIgnore + public abstract Object getKey(); + + public abstract TaskFailure toFailure(Throwable error); + public abstract JobType getJobType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java new file mode 100644 index 0000000000..1e365c6a8f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.job.CfReprocessingTask.CfReprocessingTaskFailure; +import org.thingsboard.server.common.data.job.DummyTask.DummyTaskFailure; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") +@JsonSubTypes({ + @Type(name = "CF_REPROCESSING", value = CfReprocessingTaskFailure.class), + @Type(name = "DUMMY", value = DummyTaskFailure.class) +}) +public abstract class TaskFailure { + + private String error; + + public abstract JobType getJobType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java index 468173bdf3..0ee9a2b477 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java @@ -30,13 +30,4 @@ public class TaskResult { private boolean discarded; private TaskFailure failure; - @Data - @AllArgsConstructor - @NoArgsConstructor - @Builder - public static class TaskFailure { - private String error; - private Task task; - } - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index f685373f75..f0ba542ab9 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -27,7 +27,6 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.Task; import org.thingsboard.server.common.data.job.TaskResult; -import org.thingsboard.server.common.data.job.TaskResult.TaskFailure; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; @@ -147,10 +146,7 @@ public abstract class TaskProcessor { private void reportFailure(Task task, Throwable error) { TaskResult result = TaskResult.builder() - .failure(TaskFailure.builder() - .error(error.getMessage()) - .task(task) - .build()) + .failure(task.toFailure(error)) .build(); statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index c632a7b58b..f2802f4771 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -30,12 +30,11 @@ import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.TaskResult; -import org.thingsboard.server.common.data.job.TaskResult.TaskFailure; +import org.thingsboard.server.common.data.job.TaskFailure; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; -import org.thingsboard.server.dao.service.DataValidator; import java.util.Optional; @@ -52,12 +51,13 @@ import static org.thingsboard.server.common.data.job.JobStatus.RUNNING; public class DefaultJobService extends AbstractEntityService implements JobService { private final JobDao jobDao; - private final JobValidator validator = new JobValidator(); @Transactional @Override - public Job createJob(TenantId tenantId, Job job) { - validator.validate(job, Job::getTenantId); + public Job submitJob(TenantId tenantId, Job job) { + if (jobDao.existsByKeyAndStatusOneOf(job.getKey(), QUEUED, PENDING, RUNNING)) { + throw new IllegalArgumentException("The same job is already queued or running"); + } if (jobDao.existsByTenantIdAndTypeAndStatusOneOf(tenantId, job.getType(), PENDING, RUNNING)) { job.setStatus(QUEUED); } else { @@ -124,10 +124,9 @@ public class DefaultJobService extends AbstractEntityService implements JobServi result.setDiscardedCount(result.getDiscardedCount() + 1); } else { TaskFailure failure = taskResult.getFailure(); - String key = failure.getTask().getKey(); result.setFailedCount(result.getFailedCount() + 1); if (result.getFailures().size() < 1000) { // preserving only first 1000 errors, not reprocessing if there are more failures - result.getFailures().put(key, failure.getError()); + result.getFailures().add(failure); } } @@ -192,24 +191,6 @@ public class DefaultJobService extends AbstractEntityService implements JobServi return jobDao.findByIdForUpdate(tenantId, jobId); } -// todo: reprocessing - - public class JobValidator extends DataValidator { - - @Override - protected void validateCreate(TenantId tenantId, Job job) { -// if (jobDao.existsByTenantIdAndTypeAndStatusOneOf(tenantId, job.getType(), PENDING, RUNNING)) { -// throw new DataValidationException("Job of this type is already running"); -// } - } - - @Override - protected Job validateUpdate(TenantId tenantId, Job job) { - throw new IllegalArgumentException("Job can't be updated externally"); - } - - } - @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { return Optional.ofNullable(findJobById(tenantId, (JobId) entityId)); From 44a4d9d69040b6f9ad36d1591491489cab491a7e Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 30 Apr 2025 11:25:32 +0300 Subject: [PATCH 102/335] Cleanup code --- .../server/actors/ActorSystemContext.java | 6 + .../actors/ruleChain/DefaultTbContext.java | 8 +- .../job/CfReprocessingJobProcessor.java | 105 ------------------ .../job/task/CfReprocessingTaskProcessor.java | 57 ---------- .../job/CfReprocessingJobConfiguration.java | 43 ------- .../data/job/CfReprocessingJobResult.java | 25 ----- .../common/data/job/CfReprocessingTask.java | 72 ------------ .../server/common/data/job/Job.java | 1 - .../common/data/job/JobConfiguration.java | 1 - .../server/common/data/job/JobResult.java | 1 - .../server/common/data/job/JobType.java | 1 - .../server/common/data/job/Task.java | 1 - .../server/common/data/job/TaskFailure.java | 2 - .../main/resources/sql/schema-entities.sql | 2 +- .../rule/engine/api/TbContext.java | 3 + .../rule/engine/util/TenantIdLoader.java | 4 + .../rule/engine/util/TenantIdLoaderTest.java | 10 ++ 17 files changed, 31 insertions(+), 311 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobResult.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 26c82a33de..b2845085fd 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -95,6 +95,7 @@ import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleNodeStateService; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; @@ -551,6 +552,11 @@ public class ActorSystemContext { @Getter private CalculatedFieldQueueService calculatedFieldQueueService; + @Lazy + @Autowired(required = false) + @Getter + private JobService jobService; + @Value("${actors.session.max_concurrent_sessions_per_device:1}") @Getter private int maxConcurrentSessionsPerDevice; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 033e10ca9a..e40453da25 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -23,6 +23,7 @@ import org.bouncycastle.util.Arrays; import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.api.DeviceStateManager; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.rule.engine.api.RuleEngineAlarmService; @@ -30,7 +31,6 @@ import org.thingsboard.rule.engine.api.RuleEngineApiUsageStateService; import org.thingsboard.rule.engine.api.RuleEngineAssetProfileCache; import org.thingsboard.rule.engine.api.RuleEngineCalculatedFieldQueueService; import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; -import org.thingsboard.rule.engine.api.DeviceStateManager; import org.thingsboard.rule.engine.api.RuleEngineRpcService; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.ScriptEngine; @@ -107,6 +107,7 @@ import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.user.UserService; @@ -887,6 +888,11 @@ public class DefaultTbContext implements TbContext { return mainCtx.getCalculatedFieldQueueService(); } + @Override + public JobService getJobService() { + return mainCtx.getJobService(); + } + @Override public boolean isExternalNodeForceAck() { return mainCtx.isExternalNodeForceAck(); diff --git a/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java deleted file mode 100644 index 79a735f6a6..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/job/CfReprocessingJobProcessor.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright © 2016-2025 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. - */ -package org.thingsboard.server.service.job; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.cf.CalculatedField; -import org.thingsboard.server.common.data.id.AssetProfileId; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.job.CfReprocessingJobConfiguration; -import org.thingsboard.server.common.data.job.CfReprocessingTask; -import org.thingsboard.server.common.data.job.CfReprocessingTask.CfReprocessingTaskFailure; -import org.thingsboard.server.common.data.job.Job; -import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.job.Task; -import org.thingsboard.server.common.data.job.TaskFailure; -import org.thingsboard.server.common.data.page.PageDataIterable; -import org.thingsboard.server.dao.asset.AssetService; -import org.thingsboard.server.dao.cf.CalculatedFieldService; -import org.thingsboard.server.dao.device.DeviceService; - -import java.util.List; -import java.util.function.Consumer; - -@Component -@RequiredArgsConstructor -public class CfReprocessingJobProcessor implements JobProcessor { - - private final CalculatedFieldService calculatedFieldService; - private final DeviceService deviceService; - private final AssetService assetService; - - @Override - public int process(Job job, Consumer taskConsumer) throws Exception { - CfReprocessingJobConfiguration configuration = job.getConfiguration(); - - CalculatedField calculatedField = calculatedFieldService.findById(job.getTenantId(), configuration.getCalculatedFieldId()); - EntityId cfEntityId = calculatedField.getEntityId(); - - int tasksCount = 0; - if (cfEntityId.getEntityType().isOneOf(EntityType.DEVICE, EntityType.ASSET)) { - taskConsumer.accept(createTask(job, configuration, calculatedField, cfEntityId)); - tasksCount++; - } else { - PageDataIterable entities; - if (cfEntityId.getEntityType() == EntityType.DEVICE_PROFILE) { - entities = new PageDataIterable<>(pageLink -> deviceService.findDeviceIdsByTenantIdAndDeviceProfileId(job.getTenantId(), (DeviceProfileId) cfEntityId, pageLink), 512); - } else if (cfEntityId.getEntityType() == EntityType.ASSET_PROFILE) { - entities = new PageDataIterable<>(pageLink -> assetService.findAssetIdsByTenantIdAndAssetProfileId(job.getTenantId(), (AssetProfileId) cfEntityId, pageLink), 512); - } else { - throw new IllegalArgumentException("Unsupported CF entity type " + cfEntityId.getEntityType()); - } - for (EntityId entityId : entities) { - taskConsumer.accept(createTask(job, configuration, calculatedField, entityId)); - tasksCount++; - } - } - return tasksCount; - } - - @Override - public void reprocess(Job job, List failures, Consumer taskConsumer) throws Exception { - CfReprocessingJobConfiguration configuration = job.getConfiguration(); - CalculatedField calculatedField = calculatedFieldService.findById(job.getTenantId(), configuration.getCalculatedFieldId()); - - for (TaskFailure failure : failures) { - CfReprocessingTaskFailure taskFailure = (CfReprocessingTaskFailure) failure; - EntityId entityId = taskFailure.getEntityId(); - taskConsumer.accept(createTask(job, job.getConfiguration(), calculatedField, entityId)); - } - } - - private Task createTask(Job job, CfReprocessingJobConfiguration configuration, CalculatedField calculatedField, EntityId entityId) { - return CfReprocessingTask.builder() - .tenantId(job.getTenantId()) - .jobId(job.getId()) - .retries(2) // 3 attempts in total - .calculatedField(calculatedField) - .entityId(entityId) - .startTs(configuration.getStartTs()) - .endTs(configuration.getEndTs()) - .build(); - } - - @Override - public JobType getType() { - return JobType.CF_REPROCESSING; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java deleted file mode 100644 index 5d4005307c..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/job/task/CfReprocessingTaskProcessor.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright © 2016-2025 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. - */ -package org.thingsboard.server.service.job.task; - -import com.google.common.util.concurrent.SettableFuture; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.thingsboard.server.actors.calculatedField.CalculatedFieldReprocessingService; -import org.thingsboard.server.common.data.job.CfReprocessingTask; -import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.queue.task.TaskProcessor; - -import java.util.concurrent.TimeUnit; - -@Component -@RequiredArgsConstructor -public class CfReprocessingTaskProcessor extends TaskProcessor { - - private final CalculatedFieldReprocessingService cfReprocessingService; - - @Override - public void process(CfReprocessingTask task) throws Exception { - SettableFuture future = SettableFuture.create(); - cfReprocessingService.reprocess(task, new TbCallback() { - @Override - public void onSuccess() { - future.set(null); - } - - @Override - public void onFailure(Throwable t) { - future.setException(t); - } - }); - future.get(1, TimeUnit.MINUTES); - } - - @Override - public JobType getJobType() { - return JobType.CF_REPROCESSING; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java deleted file mode 100644 index 90de047554..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobConfiguration.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright © 2016-2025 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. - */ -package org.thingsboard.server.common.data.job; - -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.id.CalculatedFieldId; - -@Data -@EqualsAndHashCode(callSuper = true) -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class CfReprocessingJobConfiguration extends JobConfiguration { - - @NotNull - private CalculatedFieldId calculatedFieldId; - private long startTs; - private long endTs; - - @Override - public JobType getType() { - return JobType.CF_REPROCESSING; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobResult.java deleted file mode 100644 index 2d756f6d53..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingJobResult.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright © 2016-2025 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. - */ -package org.thingsboard.server.common.data.job; - -public class CfReprocessingJobResult extends JobResult { - - @Override - public JobType getJobType() { - return JobType.CF_REPROCESSING; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java deleted file mode 100644 index 3c8b765527..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/CfReprocessingTask.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright © 2016-2025 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. - */ -package org.thingsboard.server.common.data.job; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; -import lombok.experimental.SuperBuilder; -import org.thingsboard.server.common.data.cf.CalculatedField; -import org.thingsboard.server.common.data.id.EntityId; - -@Data -@NoArgsConstructor -@EqualsAndHashCode(callSuper = true) -@SuperBuilder -@ToString(callSuper = true) -public class CfReprocessingTask extends Task { - - private CalculatedField calculatedField; - private EntityId entityId; - private long startTs; - private long endTs; - - @Override - public Object getKey() { - return entityId; - } - - @Override - public TaskFailure toFailure(Throwable error) { - return new CfReprocessingTaskFailure(entityId, error.getMessage()); - } - - @Override - public JobType getJobType() { - return JobType.CF_REPROCESSING; - } - - @Data - @EqualsAndHashCode(callSuper = true) - @NoArgsConstructor - public static class CfReprocessingTaskFailure extends TaskFailure { - - private EntityId entityId; - - public CfReprocessingTaskFailure(EntityId entityId, String error) { - super(error); - this.entityId = entityId; - } - - @Override - public JobType getJobType() { - return JobType.CF_REPROCESSING; - } - - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java index e96d42cad1..237c223a92 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java @@ -54,7 +54,6 @@ public class Job extends BaseData implements HasTenantId { this.description = description; this.configuration = configuration; this.result = switch (type) { - case CF_REPROCESSING -> new CfReprocessingJobResult(); case DUMMY -> new DummyJobResult(); }; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java index 0d3620d9e8..7a2eccd42a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java @@ -27,7 +27,6 @@ import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ - @Type(name = "CF_REPROCESSING", value = CfReprocessingJobConfiguration.class), @Type(name = "DUMMY", value = DummyJobConfiguration.class), }) @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index 748d24811c..bf5e5f2c56 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -30,7 +30,6 @@ import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @JsonSubTypes({ - @Type(name = "CF_REPROCESSING", value = CfReprocessingJobResult.class), @Type(name = "DUMMY", value = DummyJobResult.class) }) @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java index 7c0d9972e1..9e8e9fa7e5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java @@ -17,7 +17,6 @@ package org.thingsboard.server.common.data.job; public enum JobType { - CF_REPROCESSING, DUMMY; public String getTasksTopic() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java index 6399f6f79a..ad7c6b62df 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java @@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.id.TenantId; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @JsonSubTypes({ - @Type(name = "CF_REPROCESSING", value = CfReprocessingTask.class), @Type(name = "DUMMY", value = DummyTask.class) }) @SuperBuilder diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java index 1e365c6a8f..7a04db8188 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.job.CfReprocessingTask.CfReprocessingTaskFailure; import org.thingsboard.server.common.data.job.DummyTask.DummyTaskFailure; @Data @@ -31,7 +30,6 @@ import org.thingsboard.server.common.data.job.DummyTask.DummyTaskFailure; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @JsonSubTypes({ - @Type(name = "CF_REPROCESSING", value = CfReprocessingTaskFailure.class), @Type(name = "DUMMY", value = DummyTaskFailure.class) }) public abstract class TaskFailure { diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 5afc9398d2..ba43a54697 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -957,6 +957,6 @@ CREATE TABLE IF NOT EXISTS job ( key varchar NOT NULL, description varchar NOT NULL, status varchar NOT NULL, - configuration varchar(1000) NOT NULL, + configuration varchar(1000000) NOT NULL, result jsonb ); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index b66c9e13d5..d4d19dd653 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -77,6 +77,7 @@ import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.user.UserService; @@ -362,6 +363,8 @@ public interface TbContext { RuleEngineCalculatedFieldQueueService getCalculatedFieldQueueService(); + JobService getJobService(); + boolean isExternalNodeForceAck(); /** diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java index f12a856567..93fad4c0e7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.id.DomainId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.MobileAppId; import org.thingsboard.server.common.data.id.NotificationRequestId; @@ -175,6 +176,9 @@ public class TenantIdLoader { tenantEntity = null; } break; + case JOB: + tenantEntity = ctx.getJobService().findJobById(ctxTenantId, new JobId(id)); + break; default: throw new RuntimeException("Unexpected entity type: " + entityId.getEntityType()); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java index 38417c3922..4cbc091bdc 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java @@ -54,6 +54,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; +import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.mobile.app.MobileApp; import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle; import org.thingsboard.server.common.data.notification.NotificationRequest; @@ -88,6 +89,7 @@ import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; @@ -160,6 +162,8 @@ public class TenantIdLoaderTest { private MobileAppBundleService mobileAppBundleService; @Mock private CalculatedFieldService calculatedFieldService; + @Mock + private JobService jobService; private TenantId tenantId; private TenantProfileId tenantProfileId; @@ -419,6 +423,12 @@ public class TenantIdLoaderTest { when(ctx.getCalculatedFieldService()).thenReturn(calculatedFieldService); doReturn(calculatedFieldLink).when(calculatedFieldService).findCalculatedFieldLinkById(eq(tenantId), any()); break; + case JOB: + Job job = new Job(); + job.setTenantId(tenantId); + when(ctx.getJobService()).thenReturn(jobService); + doReturn(job).when(jobService).findJobById(eq(tenantId), any()); + break; default: throw new RuntimeException("Unexpected originator EntityType " + entityType); } From 64bd969bee5dd15c3cb18deb53caf7f3d7ad6b9d Mon Sep 17 00:00:00 2001 From: deaflynx Date: Wed, 30 Apr 2025 12:42:47 +0300 Subject: [PATCH 103/335] UI: lwm2m observe strategies updated descriptions. --- ...2m-device-profile-transport-configuration.component.html | 2 +- ui-ngx/src/assets/locale/locale.constant-en_US.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index ac7d0677fa..4897f94e11 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -28,7 +28,7 @@ {{ observeStrategyMap.get(strategy).name | translate }} - + {{ observeStrategyMap.get(strategy).description | translate }} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 91008c69a6..8c8c7dd9f3 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2213,11 +2213,11 @@ "observe-strategy": { "observe-strategy": "Observe strategy", "single": "Single", - "single-description": "One resource equals one single observe request", + "single-description": "One Observe request per resource (higher precision, more network traffic)", "composite-all": "Composite all", - "composite-all-description": "All resources in one composite observe request", + "composite-all-description": "All resources are observed with a single Composite Observe request (more efficient, less flexible)", "composite-by-object": "Composite by objects", - "composite-by-object-description": "Grouped composite observe requests by object" + "composite-by-object-description": "Resources are grouped by object type and observed using separate Composite Observe requests (balanced approach)" } }, "snmp": { From 505dfb2e4d48724ae30ed5acefc85d51405eadcd Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 30 Apr 2025 13:00:48 +0300 Subject: [PATCH 104/335] UI: Add new unit definition --- .../app/core/services/unit/definitions/all.ts | 36 +++- .../core/services/unit/definitions/angle.ts | 2 +- .../core/services/unit/definitions/energy.ts | 2 +- .../core/services/unit/definitions/mass.ts | 2 +- .../services/unit/definitions/parts-per.ts | 41 ++++ .../core/services/unit/definitions/power.ts | 96 ++++++++++ .../services/unit/definitions/pressure.ts | 175 ++++++++++++++++++ .../core/services/unit/definitions/speed.ts | 81 ++++++++ .../core/services/unit/definitions/torque.ts | 56 ++++++ .../core/services/unit/definitions/voltage.ts | 66 +++++++ .../unit/definitions/volume-flow-rate.ts | 114 ++++++++++++ .../core/services/unit/definitions/volume.ts | 168 +++++++++++++++++ .../assets/locale/locale.constant-en_US.json | 14 +- 13 files changed, 845 insertions(+), 8 deletions(-) create mode 100644 ui-ngx/src/app/core/services/unit/definitions/parts-per.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/power.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/pressure.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/speed.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/torque.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/voltage.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/volume.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts index 57ca762686..7b447de453 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/all.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/all.ts @@ -28,8 +28,16 @@ import frequency, { FrequencyUnits } from '@core/services/unit/definitions/frequ import illuminance,{ IlluminanceUnits } from '@core/services/unit/definitions/illuminance'; import length, { LengthUnits } from '@core/services/unit/definitions/length'; import mass, { MassUnits } from '@core/services/unit/definitions/mass'; +import partsPer, { PartsPerUnits } from '@core/services/unit/definitions/parts-per'; +import power, { PowerUnits } from '@core/services/unit/definitions/power'; +import pressure, { PressureUnits } from '@core/services/unit/definitions/pressure'; +import speed, { SpeedUnits } from '@core/services/unit/definitions/speed'; import temperature, { TemperatureUnits } from './temperature'; import time, { TimeUnits } from './time'; +import torque, { TorqueUnits } from '@core/services/unit/definitions/torque'; +import voltage, { VoltageUnits } from '@core/services/unit/definitions/voltage'; +import volume, { VolumeUnits } from '@core/services/unit/definitions/volume'; +import volumeFlowRate, { VolumeFlowRateUnits } from '@core/services/unit/definitions/volume-flow-rate'; export type AllMeasuresUnits = | AccelerationUnits @@ -45,8 +53,16 @@ export type AllMeasuresUnits = | IlluminanceUnits | LengthUnits | MassUnits + | PartsPerUnits + | PowerUnits + | PressureUnits + | SpeedUnits | TemperatureUnits - | TimeUnits; + | TimeUnits + | TorqueUnits + | VoltageUnits + | VolumeUnits + | VolumeFlowRateUnits; export type AllMeasures = | 'acceleration' @@ -62,8 +78,16 @@ export type AllMeasures = | 'illuminance' | 'length' | 'mass' + | 'parts-per' + | 'power' + | 'pressure' + | 'speed' | 'temperature' - | 'time'; + | 'time' + | 'torque' + | 'voltage' + | 'volume' + | 'volume-flow-rate'; const allMeasures: Record< AllMeasures, @@ -82,8 +106,16 @@ const allMeasures: Record< illuminance, length, mass, + 'parts-per': partsPer, + power, + pressure, + speed, temperature, time, + torque, + voltage, + volume, + 'volume-flow-rate': volumeFlowRate, }; export default allMeasures; diff --git a/ui-ngx/src/app/core/services/unit/definitions/angle.ts b/ui-ngx/src/app/core/services/unit/definitions/angle.ts index 5898034341..7082e5a986 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/angle.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/angle.ts @@ -53,7 +53,7 @@ const METRIC: TbMeasureUnits = { to_anchor: 9 / (50 * Math.PI), }, rev: { - name: 'revolution', + name: 'unit.revolution', tags: ['angle', 'revolution', 'full circle', 'complete turn', 'rev'], to_anchor: 360, }, diff --git a/ui-ngx/src/app/core/services/unit/definitions/energy.ts b/ui-ngx/src/app/core/services/unit/definitions/energy.ts index 1ec341f565..06ee394121 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/energy.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/energy.ts @@ -125,7 +125,7 @@ const IMPERIAL: TbMeasureUnits = { to_anchor: 1000, }, BTU: { - name: 'british-thermal-unit', + name: 'unit.british-thermal-unit', tags: ['energy', 'heat', 'work done', 'british thermal unit', 'british thermal units', 'BTU'], to_anchor: 252.164401, }, diff --git a/ui-ngx/src/app/core/services/unit/definitions/mass.ts b/ui-ngx/src/app/core/services/unit/definitions/mass.ts index 41a9dde039..2a52a4c960 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/mass.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/mass.ts @@ -101,7 +101,7 @@ const IMPERIAL: TbMeasureUnits = { to_anchor: 28, }, cwt: { - name: 'unit.hundredweight-countt', + name: 'unit.hundredweight-count', tags: ['mass', 'weight', 'heaviness', 'load', 'hundredweight count', 'cwt'], to_anchor: 100, }, diff --git a/ui-ngx/src/app/core/services/unit/definitions/parts-per.ts b/ui-ngx/src/app/core/services/unit/definitions/parts-per.ts new file mode 100644 index 0000000000..83f72447df --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/parts-per.ts @@ -0,0 +1,41 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PartsPerUnits = PartsPerMetricUnits; +export type PartsPerMetricUnits = 'ppm' | 'ppb'; + +const METRIC: TbMeasureUnits = { + units: { + ppm: { + name: 'unit.ppm', + tags: ['carbon dioxide', 'co²', 'carbon monoxide', 'co', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'ppm'], + to_anchor: 1, + }, + ppb: { + name: 'unit.ppb', + tags: ['ozone', 'o³', 'nitrogen dioxide', 'no²', 'sulfur dioxide', 'so²', 'aqi', 'air quality', 'tvoc', 'ppb'], + to_anchor: 0.001, + } + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/power.ts b/ui-ngx/src/app/core/services/unit/definitions/power.ts new file mode 100644 index 0000000000..8b9c5eda81 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/power.ts @@ -0,0 +1,96 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PowerUnits = PowerMetricUnits | PowerImperialUnits; + +export type PowerMetricUnits = 'W' | 'μW' | 'mW' | 'kW' | 'MW' | 'GW' | 'PS'; +export type PowerImperialUnits = 'BTU/s' | 'ft-lb/s' | 'hp' | 'BTU/h'; + +const METRIC: TbMeasureUnits = { + ratio: 0.737562149, + units: { + W: { + name: 'unit.watt', + tags: ['power', 'horsepower', 'performance', 'watt', 'watts', 'electricity', 'W'], + to_anchor: 1, + }, + μW: { + name: 'unit.microwatt', + tags: ['power', 'horsepower', 'performance', 'microwatt', 'microwatts', 'electricity', 'μW'], + to_anchor: 0.000001, + }, + mW: { + name: 'unit.milliwatt', + tags: ['power', 'horsepower', 'performance', 'milliwatt', 'milliwatts', 'electricity', 'mW'], + to_anchor: 0.001, + }, + kW: { + name: 'unit.kilowatt', + tags: ['power', 'horsepower', 'performance', 'kilowatt', 'kilowatts', 'electricity', 'kW'], + to_anchor: 1000, + }, + MW: { + name: 'unit.megawatt', + tags: ['power', 'horsepower', 'performance', 'megawatt', 'megawatts', 'electricity', 'MW'], + to_anchor: 1000000, + }, + GW: { + name: 'unit.gigawatt', + tags: ['power', 'horsepower', 'performance', 'gigawatt', 'gigawatts', 'electricity', 'GW'], + to_anchor: 1000000000, + }, + PS: { + name: 'unit.metric-horsepower', + tags: ['power', 'performance', 'metric horsepower', 'PS'], + to_anchor: 735.49875, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 0.737562149, + units: { + 'BTU/s': { + name: 'unit.btu-per-second', + tags: ['power', 'heat transfer', 'thermal energy', 'british thermal unit per second', 'Btu/s'], + to_anchor: 778.16937, + }, + 'ft-lb/s': { + name: 'unit.foot-pound-per-second', + tags: ['power', 'foot-pound per second', 'foot-pounds per second', 'ft-lb/s', 'mechanical power'], + to_anchor: 1, + }, + hp: { + name: 'unit.horsepower', + tags: ['power', 'horsepower', 'performance', 'electricity', 'horsepowers', 'hp'], + to_anchor: 550, + }, + 'BTU/h': { + name: 'unit.btu-per-hour', + tags: ['power', 'heat transfer', 'thermal energy', 'HVAC', 'BTU/h'], + to_anchor: 0.216158, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/pressure.ts b/ui-ngx/src/app/core/services/unit/definitions/pressure.ts new file mode 100644 index 0000000000..ad5c9b0dbb --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/pressure.ts @@ -0,0 +1,175 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PressureUnits = PressureMetricUnits | PressureImperialUnits; + +export type PressureMetricUnits = + | 'Pa' + | 'kPa' + | 'MPa' + | 'GPa' + | 'hPa' + | 'mb' + | 'mbar' + | 'bar' + | 'kbar' + | 'Torr' + | 'mmHg' + | 'atm' + | 'Pa/m²' + | 'N/mm²' + | 'N/m²' + | 'kN/m²' + | 'kgf/m²' + | 'Pa/cm²'; + +export type PressureImperialUnits = 'psi' | 'ksi' | 'inHg' | 'psi/in²' | 'tonf/in²'; + +const METRIC: TbMeasureUnits = { + ratio: 0.00014503768078, + units: { + Pa: { + name: 'unit.pascal', + tags: ['pressure', 'force', 'compression', 'tension', 'pascal', 'pascals', 'Pa', 'atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], + to_anchor: 0.001, // 1 Pa = 0.001 kPa + }, + kPa: { + name: 'unit.kilopascal', + tags: ['pressure', 'force', 'compression', 'tension', 'kilopascal', 'kilopascals', 'kPa'], + to_anchor: 1, + }, + MPa: { + name: 'unit.megapascal', + tags: ['pressure', 'force', 'compression', 'tension', 'megapascal', 'megapascals', 'MPa'], + to_anchor: 1000, + }, + GPa: { + name: 'unit.gigapascal', + tags: ['pressure', 'force', 'compression', 'tension', 'gigapascal', 'gigapascals', 'GPa'], + to_anchor: 1000000, + }, + hPa: { + name: 'unit.hectopascal', + tags: ['pressure', 'force', 'compression', 'tension', 'hectopascal', 'hectopascals', 'hPa', 'atmospheric pressure'], + to_anchor: 0.1, + }, + mbar: { + name: 'unit.millibar', + tags: ['pressure', 'force', 'compression', 'tension', 'millibar', 'millibars', 'mbar'], + to_anchor: 0.1, + }, + mb: { + name: 'unit.millibar', + tags: ['atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight', 'mb'], + to_anchor: 0.1, + }, + bar: { + name: 'unit.bar', + tags: ['pressure', 'force', 'compression', 'tension', 'bar', 'bars'], + to_anchor: 100, + }, + kbar: { + name: 'unit.kilobar', + tags: ['pressure', 'force', 'compression', 'tension', 'kilobar', 'kilobars', 'kbar'], + to_anchor: 100000, + }, + Torr: { + name: 'unit.torr', + tags: ['pressure', 'force', 'compression', 'tension', 'vacuum pressure', 'torr'], + to_anchor: 101325 / 760000, + }, + mmHg: { + name: 'unit.millimeters-of-mercury', + tags: ['pressure', 'force', 'compression', 'tension', 'millimeter of mercury', 'millimeters of mercury', 'mmHg', 'vacuum pressure'], + to_anchor: 0.133322, + }, + atm: { + name: 'unit.atmospheres', + tags: ['pressure', 'force', 'compression', 'tension', 'atmosphere', 'atmospheres', 'atmospheric pressure', 'atm'], + to_anchor: 101.325, + }, + 'Pa/m²': { + name: 'unit.pascal-per-square-meter', + tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square meter', 'Pa/m²'], + to_anchor: 0.001, + }, + 'N/mm²': { + name: 'unit.newton-per-square-millimeter', + tags: ['pressure', 'stress', 'mechanical strength', 'newton per square millimeter', 'N/mm²'], + to_anchor: 1000, + }, + 'N/m²': { + name: 'unit.newton-per-square-meter', + tags: ['pressure', 'stress', 'mechanical strength', 'newton per square meter', 'N/m²'], + to_anchor: 0.001, + }, + 'kN/m²': { + name: 'unit.kilonewton-per-square-meter', + tags: ['pressure', 'stress', 'mechanical strength', 'kilonewton per square meter', 'kN/m²'], + to_anchor: 1, + }, + 'kgf/m²': { + name: 'unit.kilogram-force-per-square-meter', + tags: ['pressure', 'stress', 'mechanical strength', 'kilogram-force per square meter', 'kgf/m²'], + to_anchor: 0.00980665, + }, + 'Pa/cm²': { + name: 'unit.pascal-per-square-centimeter', + tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square centimeter', 'Pa/cm²'], + to_anchor: 0.1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 0.00014503768078, + units: { + psi: { + name: 'unit.pounds-per-square-inch', + tags: ['pressure', 'force', 'compression', 'tension', 'pounds per square inch', 'psi'], + to_anchor: 0.001, + }, + ksi: { + name: 'unit.kilopound-per-square-inch', + tags: ['pressure', 'force', 'compression', 'tension', 'kilopound per square inch', 'kilopounds per square inch', 'ksi'], + to_anchor: 1, + }, + inHg: { + name: 'unit.inch-of-mercury', + tags: ['pressure', 'force', 'compression', 'tension', 'vacuum pressure', 'inHg', 'atmospheric pressure', 'barometric pressure'], + to_anchor: 0.000491154, + }, + 'psi/in²': { + name: 'unit.pound-per-square-inch', + tags: ['pressure', 'stress', 'mechanical strength', 'pound per square inch', 'psi/in²'], + to_anchor: 0.001, + }, + 'tonf/in²': { + name: 'unit.ton-force-per-square-inch', + tags: ['pressure', 'stress', 'mechanical strength', 'ton-force per square inch', 'tonf/in²'], + to_anchor: 2, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/speed.ts b/ui-ngx/src/app/core/services/unit/definitions/speed.ts new file mode 100644 index 0000000000..095bb0c759 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/speed.ts @@ -0,0 +1,81 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpeedUnits = SpeedMetricUnits | SpeedImperialUnits; + +export type SpeedMetricUnits = 'm/s' | 'km/h' | 'mm/min'; +export type SpeedImperialUnits = 'mph' | 'kt' | 'ft/s' | 'ft/min' | 'in/h'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 1.609344, + units: { + 'm/s': { + name: 'unit.meter-per-second', + tags: ['speed', 'velocity', 'pace', 'meter per second', 'm/s', 'peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'wind speed', 'weather'], + to_anchor: 3.6, + }, + 'km/h': { + name: 'unit.kilometer-per-hour', + tags: ['speed', 'velocity', 'pace', 'kilometer per hour', 'km/h'], + to_anchor: 1, + }, + 'mm/min': { + name: 'unit.millimeters-per-minute', + tags: ['feed rate', 'cutting feed rate', 'millimeters per minute', 'mm/min'], + to_anchor: 0.06, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1.609344, + units: { + mph: { + name: 'unit.mile-per-hour', + tags: ['speed', 'velocity', 'pace', 'mile per hour', 'mph'], + to_anchor: 1, + }, + kt: { + name: 'unit.knot', + tags: ['speed', 'velocity', 'pace', 'knot', 'knots', 'kt'], + to_anchor: 1.150779, + }, + 'ft/s': { + name: 'unit.foot-per-second', + tags: ['speed', 'velocity', 'pace', 'foot per second', 'ft/s'], + to_anchor: 0.681818, // 1 ft/s ≈ 0.681818 mph + }, + 'ft/min': { + name: 'unit.foot-per-minute', + tags: ['speed', 'velocity', 'pace', 'foot per minute', 'ft/min'], + to_anchor: 0.0113636, + }, + 'in/h': { + name: 'unit.inch-per-hour', + tags: ['speed', 'velocity', 'pace', 'inch per hour', 'in/h'], + to_anchor: 0.00001578, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/torque.ts b/ui-ngx/src/app/core/services/unit/definitions/torque.ts new file mode 100644 index 0000000000..fa830a0a4a --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/torque.ts @@ -0,0 +1,56 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type TorqueUnits = TorqueMetricUnits | TorqueImperialUnits; + +export type TorqueMetricUnits = 'Nm'; +export type TorqueImperialUnits = 'lbf-ft' | 'in·lbf'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 1.355818, + units: { + Nm: { + name: 'unit.newton-meter', + tags: ['torque', 'rotational force', 'newton meter', 'Nm'], + to_anchor: 1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1.355818, + units: { + 'lbf-ft': { + name: 'unit.foot-pounds', + tags: ['torque', 'rotational force', 'foot-pound', 'foot-pounds', 'ft·lbf'], + to_anchor: 1, + }, + 'in·lbf': { + name: 'unit.inch-pounds', + tags: ['torque', 'rotational force', 'inch-pounds', 'inch-pound', 'in·lbf'], + to_anchor: 1 / 12, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/voltage.ts b/ui-ngx/src/app/core/services/unit/definitions/voltage.ts new file mode 100644 index 0000000000..54f0ec2c20 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/voltage.ts @@ -0,0 +1,66 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type VoltageUnits = VoltageMetricUnits; +export type VoltageMetricUnits = 'pV' | 'nV' | 'μV' | 'mV' | 'V' | 'kV' | 'MV'; + +const METRIC: TbMeasureUnits = { + units: { + pV: { + name: 'unit.picovolt', + tags: ['voltage', 'volts', 'picovolt', 'pV'], + to_anchor: 1e-12, + }, + nV: { + name: 'unit.nanovolt', + tags: ['voltage', 'volts', 'nanovolt', 'nV'], + to_anchor: 1e-9, + }, + μV: { + name: 'unit.microvolt', + tags: ['electric potential', 'electric tension', 'voltage', 'microvolt', 'microvolts', 'μV'], + to_anchor: 1e-6, + }, + mV: { + name: 'unit.millivolt', + tags: ['electric potential', 'electric tension', 'voltage', 'millivolt', 'millivolts', 'mV'], + to_anchor: 0.001, // 1 mV = 1e-3 V + }, + V: { + name: 'unit.volt', + tags: ['electric potential', 'electric tension', 'voltage', 'volt', 'volts', 'V', 'power source', 'battery', 'battery level'], + to_anchor: 1, + }, + kV: { + name: 'unit.kilovolt', + tags: ['electric potential', 'electric tension', 'voltage', 'kilovolt', 'kilovolts', 'kV'], + to_anchor: 1000, + }, + MV: { + name: 'unit.megavolt', + tags: ['electric potential', 'electric tension', 'voltage', 'megavolt', 'megavolts', 'MV'], + to_anchor: 1e6, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts b/ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts new file mode 100644 index 0000000000..fd1f25c1f4 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts @@ -0,0 +1,114 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type VolumeFlowRateUnits = VolumeFlowRateMetricUnits | VolumeFlowRateImperialUnits; + +export type VolumeFlowRateMetricUnits = + | 'dm³/s' + | 'mL/min' + | 'L/s' + | 'L/min' + | 'L/hr' + | 'm³/s' + | 'm³/hr'; + +export type VolumeFlowRateImperialUnits = + | 'fl-oz/s' + | 'ft³/s' + | 'ft³/min' + | 'gal/hr' + | 'GPM'; + +const METRIC: TbMeasureUnits = { + ratio: 33.8140227, + units: { + 'dm³/s': { + name: 'unit.cubic-decimeter-per-second', + tags: ['volume flow', 'cubic decimeter per second', 'dm3/s'], + to_anchor: 1, + }, + 'mL/min': { + name: 'unit.milliliters-per-minute', + tags: ['volume flow', 'flow rate', 'fluid dynamics', 'milliliters per minute', 'mL/min'], + to_anchor: 1 / 60000, + }, + 'L/s': { + name: 'unit.liter-per-second', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'liter per second', 'L/s'], + to_anchor: 1, + }, + 'L/min': { + name: 'unit.liter-per-minute', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'liter per minute', 'L/min'], + to_anchor: 1 / 60, + }, + 'L/hr': { + name: 'unit.liters-per-hour', + tags: ['volume flow', 'fuel consumption', 'liter per hour', 'L/hr'], + to_anchor: 1 / 3600, + }, + 'm³/s': { + name: 'unit.cubic-meters-per-second', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'cubic meters per second', 'm³/s'], + to_anchor: 1000, + }, + 'm³/hr': { + name: 'unit.cubic-meters-per-hour', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'cubic meters per hour', 'm³/hr'], + to_anchor: 5 / 18, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 33.8140227, + units: { + 'fl-oz/s': { + name: 'unit.fluid-ounce-per-second', + tags: ['volume flow', 'fluid ounce per second', 'fl-oz/s'], + to_anchor: 1, + }, + 'ft³/s': { + name: 'unit.cubic-foot-per-second', + tags: ['volume flow', 'flow rate', 'fluid flow', 'cubic foot per second', 'cubic feet per second', 'ft³/s'], + to_anchor: 957.506, + }, + 'ft³/min': { + name: 'unit.cubic-foot-per-minute', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'CFM', 'flow rate', 'fluid flow', 'cubic foot per minute', 'ft³/min'], + to_anchor: 957.506 / 60, + }, + 'gal/hr': { + name: 'unit.gallons-per-hour', + tags: ['volume flow', 'fuel consumption', 'gallons per hour', 'gal/hr'], + to_anchor: 128 / 3600, + }, + 'GPM': { + name: 'unit.gallons-per-minute', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'gallons per minute', 'GPM'], + to_anchor: 128 / 60, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/volume.ts b/ui-ngx/src/app/core/services/unit/definitions/volume.ts new file mode 100644 index 0000000000..92e1a2dfa7 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/volume.ts @@ -0,0 +1,168 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type VolumeUnits = VolumeMetricUnits | VolumeImperialUnits; + +export type VolumeMetricUnits = + | 'mm³' + | 'cm³' + | 'µL' + | 'mL' + | 'L' + | 'hL' + | 'm³' + | 'km³'; + +export type VolumeImperialUnits = + | 'tsp' + | 'tbsp' + | 'in³' + | 'fl-oz' + | 'cup' + | 'pt' + | 'qt' + | 'gal' + | 'ft³' + | 'yd³' + | 'bbl' + | 'gi' + | 'hhd'; + +const METRIC: TbMeasureUnits = { + ratio: 33.8140226, + units: { + 'mm³': { + name: 'unit.cubic-millimeter', + tags: ['volume', 'capacity', 'extent', 'cubic millimeter', 'mm³'], + to_anchor: 1 / 1000000, + }, + 'cm³': { + name: 'unit.cubic-centimeter', + tags: ['volume', 'capacity', 'extent', 'cubic centimeter', 'cubic centimeters', 'cm³'], + to_anchor: 1 / 1000, + }, + µL: { + name: 'unit.microliter', + tags: ['volume', 'liquid measurement', 'microliter', 'µL'], + to_anchor: 0.000001, + }, + mL: { + name: 'unit.milliliter', + tags: ['volume', 'capacity', 'extent', 'milliliter', 'milliliters', 'mL'], + to_anchor: 1 / 1000, + }, + L: { + name: 'unit.liter', + tags: ['volume', 'capacity', 'extent', 'liter', 'liters', 'l'], + to_anchor: 1, + }, + hL: { + name: 'unit.hectoliter', + tags: ['volume', 'capacity', 'extent', 'hectoliter', 'hectoliters', 'hl'], + to_anchor: 100, + }, + 'm³': { + name: 'unit.cubic-meter', + tags: ['volume', 'capacity', 'extent', 'cubic meter', 'cubic meters', 'm³'], + to_anchor: 1000, + }, + 'km³': { + name: 'unit.cubic-kilometer', + tags: ['volume', 'capacity', 'extent', 'cubic kilometer', 'cubic kilometers', 'km³'], + to_anchor: 1000000000000, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 33.8140226, + units: { + tsp: { + name: 'unit.teaspoon', + tags: ['volume', 'cooking measurement', 'tsp'], + to_anchor: 1 / 6, + }, + tbsp: { + name: 'unit.tablespoon', + tags: ['volume', 'cooking measurement', 'tbsp'], + to_anchor: 1 / 2, + }, + 'in³': { + name: 'unit.cubic-inch', + tags: ['volume', 'capacity', 'extent', 'cubic inch', 'cubic inches', 'in³'], + to_anchor: 0.55411, + }, + 'fl-oz': { + name: 'unit.fluid-ounce', + tags: ['volume', 'capacity', 'extent', 'fluid ounce', 'fluid ounces', 'fl-oz'], + to_anchor: 1, + }, + cup: { + name: 'unit.cup', + tags: ['volume', 'cooking measurement', 'cup'], + to_anchor: 8, + }, + pt: { + name: 'unit.pint', + tags: ['volume', 'capacity', 'extent', 'pint', 'pints', 'pt'], + to_anchor: 16, + }, + qt: { + name: 'unit.quart', + tags: ['volume', 'capacity', 'extent', 'quart', 'quarts', 'qt'], + to_anchor: 32, + }, + gal: { + name: 'unit.gallon', + tags: ['volume', 'capacity', 'extent', 'gallon', 'gallons', 'gal'], + to_anchor: 128, + }, + 'ft³': { + name: 'unit.cubic-foot', + tags: ['volume', 'capacity', 'extent', 'cubic foot', 'cubic feet', 'ft³'], + to_anchor: 957.506, + }, + 'yd³': { + name: 'unit.cubic-yard', + tags: ['volume', 'capacity', 'extent', 'cubic yard', 'cubic yards', 'yd³'], + to_anchor: 25852.7, + }, + bbl: { + name: 'unit.oil-barrels', + tags: ['volume', 'capacity', 'extent', 'oil barrel', 'oil barrels', 'bbl'], + to_anchor: 5376, + }, + gi: { + name: 'unit.gill', + tags: ['volume', 'liquid measurement', 'gi'], + to_anchor: 4, + }, + hhd: { + name: 'unit.hogshead', + tags: ['volume', 'liquid measurement', 'hhd'], + to_anchor: 8064, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b0d6454782..cebaf71745 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5938,6 +5938,7 @@ "cubic-foot": "Cubic Foot", "cubic-yard": "Cubic Yard", "fluid-ounce": "Fluid Ounce", + "fluid-ounce-per-second": "Fluid Ounce per second", "pint": "Pint", "quart": "Quart", "gallon": "Gallon", @@ -5956,8 +5957,10 @@ "meter-per-second": "Meter per Second", "kilometer-per-hour": "Kilometer per Hour", "foot-per-second": "Foot per Second", + "foot-per-minute": "Foot per Minute", "mile-per-hour": "Mile per Hour", "knot": "Knot", + "inch-per-hour": "Inch per Hour", "millimeters-per-minute": "Millimeters per minute", "kilometer-per-hour-squared": "Kilometer per hour squared", "foot-per-second-squared": "Foot per second squared", @@ -5975,6 +5978,7 @@ "newton-per-meter": "Newton per meter", "atmospheres": "Atmospheres", "pounds-per-square-inch": "Pounds per Square Inch", + "kilopound-per-square-inch": "Kilopound per Square Inch", "torr": "Torr", "inches-of-mercury": "Inches of Mercury", "pascal-per-square-meter": "Pascal per Square Meter", @@ -6031,6 +6035,8 @@ "kilowatt-per-square-inch": "Kilowatts per square inch", "horsepower": "Horsepower", "btu-per-hour": "British thermal units/hour", + "btu-per-second": "British thermal units/second", + "foot-pound-per-second": "foot-pound per second", "coulomb": "Coulomb", "millicoulomb": "Millicoulombs", "microcoulomb": "Microcoulomb", @@ -6081,10 +6087,11 @@ "ampere-meter-tags": "magnetic field, current loop, ampere-meter, A·m", "nanovolt": "Nanovolt", "picovolt": "Picovolt", - "millivolts": "Millivolts", - "microvolts": "Microvolts", + "millivolt": "Millivolts", + "microvolt": "Microvolts", "volt": "Volt", - "kilovolts": "Kilovolts", + "kilovolt": "Kilovolt", + "megavolt": "Megavolt", "dbmV": "dBmV", "dbm": "dBm", "volt-meter": "Volt-Meter", @@ -6209,6 +6216,7 @@ "gallons-per-minute": "Gallons Per Minute", "cubic-foot-per-second": "Cubic foot per second", "milliliters-per-minute": "Milliliters per minute", + "cubic-decimeter-per-second": "Cubic decimeter per second", "bit": "Bit", "byte": "Byte", "kilobyte": "Kilobyte", From 8c840f7c6f0928bc1038cad5460fa75782ce36ec Mon Sep 17 00:00:00 2001 From: deaflynx Date: Tue, 29 Apr 2025 17:39:54 +0300 Subject: [PATCH 105/335] UI: lwm2m observe strategy feature. --- ...ile-transport-configuration.component.html | 14 +++++++ ...ofile-transport-configuration.component.ts | 26 ++++++++++++- .../lwm2m/lwm2m-profile-config.models.ts | 39 ++++++++++++++++++- .../assets/locale/locale.constant-en_US.json | 9 +++++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index f4dbafb6e8..ac7d0677fa 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -20,6 +20,20 @@
+ + device-profile.lwm2m.observe-strategy.observe-strategy + + + {{ observeStrategyMap.get(lwm2mDeviceProfileFormGroup.get('observeStrategy').value)?.name | translate }} + + + {{ observeStrategyMap.get(strategy).name | translate }} + + {{ observeStrategyMap.get(strategy).description | translate }} + + + + object; @Input() @@ -102,6 +107,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro observeAttrTelemetry: [null], bootstrapServerUpdateEnable: [false], bootstrap: [[]], + observeStrategy: [null, []], clientLwM2mSettings: this.fb.group({ clientOnlyObserveAfterConnect: [1, []], useObject19ForOtaInfo: [false], @@ -173,6 +179,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } }); + this.lwm2mDeviceProfileFormGroup.get('objectIds').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(value => this.updateObserveStrategy(value)); + this.lwm2mDeviceProfileFormGroup.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe((value) => { @@ -261,6 +271,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value), bootstrap: this.configurationValue.bootstrap, bootstrapServerUpdateEnable: this.configurationValue.bootstrapServerUpdateEnable || false, + observeStrategy: this.configurationValue.observeAttr.observeStrategy || ObserveStrategy.SINGLE, clientLwM2mSettings: { clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, useObject19ForOtaInfo: this.configurationValue.clientLwM2mSettings.useObject19ForOtaInfo ?? false, @@ -283,6 +294,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').updateValueAndValidity({onlySelf: true}); this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').updateValueAndValidity({onlySelf: true}); } + this.updateObserveStrategy(value); this.cd.markForCheck(); } @@ -427,6 +439,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const telemetryArray: Array = []; const attributes: any = {}; const keyNameNew = {}; + const observeStrategyValue = val.length ? this.lwm2mDeviceProfileFormGroup.get('observeStrategy')?.value : ObserveStrategy.SINGLE; const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); observeJson.forEach(obj => { if (isDefinedAndNotNull(obj.attributes) && !isEmpty(obj.attributes)) { @@ -467,7 +480,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro attribute: attributeArray, telemetry: telemetryArray, keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew), - attributeLwm2m: attributes + attributeLwm2m: attributes, + observeStrategy: observeStrategyValue }; } @@ -568,4 +582,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro return this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings') as UntypedFormGroup; } + private updateObserveStrategy(value: ObjectLwM2M[]) { + if (value.length && !this.disabled) { + this.lwm2mDeviceProfileFormGroup.get('observeStrategy').enable({onlySelf: true}); + } else { + this.lwm2mDeviceProfileFormGroup.get('observeStrategy').disable({onlySelf: true}); + } + } + } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 3972964a5b..48b6c3284c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -136,6 +136,41 @@ export const ObjectIDVerTranslationMap = new Map( ] ); +export interface ObserveStrategyData { + name: string; + description: string; +} + +export enum ObserveStrategy { + SINGLE = 'SINGLE', + COMPOSITE_ALL = 'COMPOSITE_ALL', + COMPOSITE_BY_OBJECT = 'COMPOSITE_BY_OBJECT' +} + +export const ObserveStrategyMap = new Map([ + [ + ObserveStrategy.SINGLE, + { + name: 'device-profile.lwm2m.observe-strategy.single', + description: 'device-profile.lwm2m.observe-strategy.single-description' + } + ], + [ + ObserveStrategy.COMPOSITE_ALL, + { + name: 'device-profile.lwm2m.observe-strategy.composite-all', + description: 'device-profile.lwm2m.observe-strategy.composite-all-description' + } + ], + [ + ObserveStrategy.COMPOSITE_BY_OBJECT, + { + name: 'device-profile.lwm2m.observe-strategy.composite-by-object', + description: 'device-profile.lwm2m.observe-strategy.composite-by-object-description' + } + ] +]); + export interface ServerSecurityConfig { host?: string; port?: number; @@ -187,6 +222,7 @@ export interface ObservableAttributes { telemetry: string[]; keyName: {}; attributeLwm2m: AttributesNameValueMap; + observeStrategy: ObserveStrategy; } export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { @@ -195,7 +231,8 @@ export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { attribute: [], telemetry: [], keyName: {}, - attributeLwm2m: {} + attributeLwm2m: {}, + observeStrategy: ObserveStrategy.SINGLE }; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b160a44b0e..91008c69a6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2209,6 +2209,15 @@ "v1-0": "1.0", "v1-1": "1.1", "v1-2": "1.2" + }, + "observe-strategy": { + "observe-strategy": "Observe strategy", + "single": "Single", + "single-description": "One resource equals one single observe request", + "composite-all": "Composite all", + "composite-all-description": "All resources in one composite observe request", + "composite-by-object": "Composite by objects", + "composite-by-object-description": "Grouped composite observe requests by object" } }, "snmp": { From de589fb41cd130a830839c43fa76076fc98c1b57 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Wed, 30 Apr 2025 12:42:47 +0300 Subject: [PATCH 106/335] UI: lwm2m observe strategies updated descriptions. --- ...2m-device-profile-transport-configuration.component.html | 2 +- ui-ngx/src/assets/locale/locale.constant-en_US.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index ac7d0677fa..4897f94e11 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -28,7 +28,7 @@ {{ observeStrategyMap.get(strategy).name | translate }} - + {{ observeStrategyMap.get(strategy).description | translate }} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 91008c69a6..8c8c7dd9f3 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2213,11 +2213,11 @@ "observe-strategy": { "observe-strategy": "Observe strategy", "single": "Single", - "single-description": "One resource equals one single observe request", + "single-description": "One Observe request per resource (higher precision, more network traffic)", "composite-all": "Composite all", - "composite-all-description": "All resources in one composite observe request", + "composite-all-description": "All resources are observed with a single Composite Observe request (more efficient, less flexible)", "composite-by-object": "Composite by objects", - "composite-by-object-description": "Grouped composite observe requests by object" + "composite-by-object-description": "Resources are grouped by object type and observed using separate Composite Observe requests (balanced approach)" } }, "snmp": { From be323e3c208207507ca44336ef49d38396055f31 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Wed, 30 Apr 2025 17:38:21 +0300 Subject: [PATCH 107/335] UI: lwm2m observe strategies minor updates. --- ...vice-profile-transport-configuration.component.html | 10 +++++----- ...device-profile-transport-configuration.component.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 4897f94e11..94b4b70207 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -20,6 +20,11 @@
+ + device-profile.lwm2m.observe-strategy.observe-strategy @@ -34,11 +39,6 @@ - - diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index dc6016d5c0..c1189103f9 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -439,7 +439,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const telemetryArray: Array = []; const attributes: any = {}; const keyNameNew = {}; - const observeStrategyValue = val.length ? this.lwm2mDeviceProfileFormGroup.get('observeStrategy')?.value : ObserveStrategy.SINGLE; + const observeStrategyValue = this.lwm2mDeviceProfileFormGroup.get('observeStrategy').value; const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); observeJson.forEach(obj => { if (isDefinedAndNotNull(obj.attributes) && !isEmpty(obj.attributes)) { From 8ebf996565321523e22dba1f54a3f86cf48de248 Mon Sep 17 00:00:00 2001 From: deaflynx Date: Wed, 30 Apr 2025 17:38:21 +0300 Subject: [PATCH 108/335] UI: lwm2m observe strategies minor updates. --- ...vice-profile-transport-configuration.component.html | 10 +++++----- ...device-profile-transport-configuration.component.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 4897f94e11..94b4b70207 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -20,6 +20,11 @@
+ + device-profile.lwm2m.observe-strategy.observe-strategy @@ -34,11 +39,6 @@ - - diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index dc6016d5c0..c1189103f9 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -439,7 +439,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const telemetryArray: Array = []; const attributes: any = {}; const keyNameNew = {}; - const observeStrategyValue = val.length ? this.lwm2mDeviceProfileFormGroup.get('observeStrategy')?.value : ObserveStrategy.SINGLE; + const observeStrategyValue = this.lwm2mDeviceProfileFormGroup.get('observeStrategy').value; const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); observeJson.forEach(obj => { if (isDefinedAndNotNull(obj.attributes) && !isEmpty(obj.attributes)) { From 76fd2a3abbbfadeb449fa3835377d046709797ee Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 30 Apr 2025 18:31:57 +0300 Subject: [PATCH 109/335] UI: Add new unit definition --- .../core/services/unit/definitions/charge.ts | 17 ++++++++++++++++- .../unit/definitions/electric-current.ts | 9 +++++++-- .../core/services/unit/definitions/energy.ts | 7 ++++++- .../app/core/services/unit/definitions/mass.ts | 9 +++++++-- .../assets/locale/locale.constant-en_US.json | 10 ---------- 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit/definitions/charge.ts b/ui-ngx/src/app/core/services/unit/definitions/charge.ts index 933e821a6a..b06a0223d5 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/charge.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/charge.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type ChargeUnits = ChargeMetricUnits; -export type ChargeMetricUnits = 'c' | 'mC' | 'μC' | 'nC' | 'pC'; +export type ChargeMetricUnits = 'c' | 'mC' | 'μC' | 'nC' | 'pC' | 'mAh' | 'Ah' | 'kAh'; const METRIC: TbMeasureUnits = { units: { @@ -47,6 +47,21 @@ const METRIC: TbMeasureUnits = { tags: ['charge', 'electricity', 'electrostatics', 'picocoulomb', 'pC'], to_anchor: 1e-12, }, + mAh: { + name: 'unit.milliampere-hour', + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'milliampere-hour', 'milliampere-hours', 'mAh'], + to_anchor: 3.6, + }, + Ah: { + name: 'unit.ampere-hours', + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'ampere', 'ampere-hours', 'Ah'], + to_anchor: 3600, + }, + kAh: { + name: 'unit.kiloampere-hours', + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'kiloampere-hours', 'kiloampere-hour', 'kAh'], + to_anchor: 3600000, + }, } }; diff --git a/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts b/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts index 72a21ccf7f..9025e71c15 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type ElectricCurrentUnits = ElectricCurrentMetricalUnits; -export type ElectricCurrentMetricalUnits = 'A' | 'nA' | 'μA' | 'mA' | 'kA' | 'MA' | 'GA'; +export type ElectricCurrentMetricalUnits = 'A' | 'pA' | 'nA' | 'μA' | 'mA' | 'kA' | 'MA' | 'GA'; const METRIC: TbMeasureUnits = { units: { @@ -27,9 +27,14 @@ const METRIC: TbMeasureUnits = { tags: ['electric current', 'current flow', 'flow of electricity', 'electrical flow', 'ampere', 'amperes', 'amperage', 'A'], to_anchor: 1, }, + pA: { + name: 'unit.picoampere', + tags: ['current', 'amperes', 'picoampere', 'pA'], + to_anchor: 1e-12, + }, nA: { name: 'unit.nanoampere', - tags: ['electric current', 'nanoampere', 'nanoamperes', 'nA'], + tags: ['electric current', 'amperes', 'nanoampere', 'nA'], to_anchor: 1e-9, }, μA: { diff --git a/ui-ngx/src/app/core/services/unit/definitions/energy.ts b/ui-ngx/src/app/core/services/unit/definitions/energy.ts index 06ee394121..2f74091c10 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/energy.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/energy.ts @@ -33,7 +33,7 @@ export type EnergyMetricUnits = | 'GJ' | 'eV'; -export type EnergyImperialUnits = 'kcal' | 'cal' | 'Cal' | 'BTU' | 'ft·lb'; +export type EnergyImperialUnits = 'kcal' | 'cal' | 'Cal' | 'BTU' | 'ft·lb' | 'thm'; const METRIC: TbMeasureUnits = { ratio: 1 / 4.184, @@ -134,6 +134,11 @@ const IMPERIAL: TbMeasureUnits = { tags: ['energy', 'foot-pound', 'foot-pounds', 'ft·lb', 'ft⋅lbf'], to_anchor: 0.32404875717017, }, + thm: { + name: 'unit.therm', + tags: ['energy', 'natural gas consumption', 'BTU', 'therm', 'thm'], + to_anchor: 25219021.687207, + }, }, }; diff --git a/ui-ngx/src/app/core/services/unit/definitions/mass.ts b/ui-ngx/src/app/core/services/unit/definitions/mass.ts index 2a52a4c960..fbed798da3 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/mass.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/mass.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type MassUnits = MassMetricUnits | MassImperialUnits; -export type MassMetricUnits = 'ng' | 'mcg' | 'mg' | 'g' | 'kg' | 't' | 'Da'; +export type MassMetricUnits = 'ng' | 'mcg' | 'mg' | 'g' | 'kg' | 't' | 'Da' | 'ct'; export type MassImperialUnits = 'oz' | 'lb' | 'st' | 'short tons' | 'gr' | 'dr' | 'qr' | 'cwt' | 'slug'; const METRIC: TbMeasureUnits = { @@ -47,7 +47,7 @@ const METRIC: TbMeasureUnits = { kg: { name: 'unit.kilogram', tags: ['mass', 'weight', 'heaviness', 'load', 'kilogram', 'kilograms', 'kg'], - to_anchor: 1000, // 1 kg = 1000 g + to_anchor: 1000, }, t: { name: 'unit.tonne', @@ -59,6 +59,11 @@ const METRIC: TbMeasureUnits = { tags: ['atomic mass unit', 'AMU', 'unified atomic mass unit', 'dalton', 'Da'], to_anchor: 1.66053906660e-24, }, + ct: { + name: 'unit.carat', + tags: ['gemstone', 'pearl', 'jewelry', 'carat', 'ct'], + to_anchor: 0.2, + }, }, }; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index cebaf71745..3001671891 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6059,11 +6059,8 @@ "barn": "Barn", "circular-inch": "Circular Inch", "milliampere-hour": "Milliampere-hour", - "milliampere-hour-tags": "electric current, current flow, electric charge, current capacity, flow of electricity, electrical flow, milliampere-hour, milliampere-hours, mAh", "ampere-hours": "Ampere-hours", - "ampere-hours-tags": "electric current, current flow, electric charge, current capacity, flow of electricity, electrical flow, ampere, ampere-hours, Ah", "kiloampere-hours": "Kiloampere-hours", - "kiloampere-hours-tags": "electric current, current flow, electric charge, current capacity, flow of electricity, electrical flow, kiloampere-hours, kiloampere-hour, kAh", "nanoampere": "Nanoampere", "microampere": "Microampere", "milliampere": "Milliampere", @@ -6072,19 +6069,12 @@ "megaampere": "Megaampere", "gigaampere": "Gigaampere", "microampere-per-square-centimeter": "Microampere per square centimeter", - "microampere-per-square-centimeter-tags": "Current density, microampere per square centimeter, µA/cm²", "ampere-per-square-meter": "Ampere per Square Meter", - "ampere-per-square-meter-tags": "current density, current per unit area, ampere per square meter, A/m²", "ampere-per-meter": "Ampere per Meter", - "ampere-per-meter-tags": "magnetic field strength, magnetic field intensity, ampere per meter, A/m", "oersted": "Oersted", - "oersted-tags": "magnetic field, oersted, Oe", "bohr-magneton": "Bohr Magneton", - "bohr-magneton-tags": "atomic physics, magnetic moment, bohr magneton, μB", "ampere-meter-squared": "Ampere-Meter Squared", - "ampere-meter-squared-tags": "magnetic moment, dipole moment, ampere-meter squared, A·m²", "ampere-meter": "Ampere-Meter", - "ampere-meter-tags": "magnetic field, current loop, ampere-meter, A·m", "nanovolt": "Nanovolt", "picovolt": "Picovolt", "millivolt": "Millivolts", From 5f9afd14d383c3451cea5001edcbb119530ee4a4 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 1 May 2025 08:59:58 +0300 Subject: [PATCH 110/335] added findByKey method to service --- .../java/org/thingsboard/server/dao/job/JobService.java | 2 ++ .../org/thingsboard/server/dao/job/DefaultJobService.java | 7 ++++++- .../main/java/org/thingsboard/server/dao/job/JobDao.java | 2 ++ .../org/thingsboard/server/dao/sql/job/JobRepository.java | 6 ++++-- .../java/org/thingsboard/server/dao/sql/job/JpaJobDao.java | 7 ++++++- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java index dd333c072e..65b21853b8 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java @@ -37,4 +37,6 @@ public interface JobService extends EntityDaoService { PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink); + Job findJobByKey(TenantId tenantId, String key); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index f2802f4771..6cc315c8f8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -29,8 +29,8 @@ import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.data.job.TaskFailure; +import org.thingsboard.server.common.data.job.TaskResult; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; @@ -187,6 +187,11 @@ public class DefaultJobService extends AbstractEntityService implements JobServi return jobDao.findByTenantId(tenantId, pageLink); } + @Override + public Job findJobByKey(TenantId tenantId, String key) { + return jobDao.findByKey(tenantId, key); + } + private Job findForUpdate(TenantId tenantId, JobId jobId) { return jobDao.findByIdForUpdate(tenantId, jobId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java index 799717fea8..a892fb3e22 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java @@ -30,6 +30,8 @@ public interface JobDao extends Dao { Job findByIdForUpdate(TenantId tenantId, JobId jobId); + Job findByKey(TenantId tenantId, String key); + boolean existsByKeyAndStatusOneOf(String key, JobStatus... statuses); boolean existsByTenantIdAndTypeAndStatusOneOf(TenantId tenantId, JobType type, JobStatus... statuses); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java index bec5bf5f87..bbbdbf3a47 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java @@ -35,8 +35,8 @@ import java.util.UUID; public interface JobRepository extends JpaRepository { @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId " + - "AND (:searchText IS NULL OR ilike(j.key, concat('%', :searchText, '%')) = true " + - "OR ilike(j.description, concat('%', :searchText, '%')) = true)") + "AND (:searchText IS NULL OR ilike(j.key, concat('%', :searchText, '%')) = true " + + "OR ilike(j.description, concat('%', :searchText, '%')) = true)") Page findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); @@ -45,6 +45,8 @@ public interface JobRepository extends JpaRepository { @Query("SELECT j FROM JobEntity j WHERE j.id = :id") JobEntity findByIdForUpdate(UUID id); + JobEntity findByTenantIdAndKey(@Param("tenantId") UUID tenantId, @Param("key") String key); + boolean existsByKeyAndStatusIn(String key, List statuses); boolean existsByTenantIdAndTypeAndStatusIn(UUID tenantId, JobType type, List statuses); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java index 1b3a394e28..71bbdb2f79 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java @@ -29,9 +29,9 @@ import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.job.JobDao; import org.thingsboard.server.dao.model.sql.JobEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; -import org.thingsboard.server.dao.job.JobDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.Arrays; @@ -54,6 +54,11 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao return DaoUtil.getData(jobRepository.findByIdForUpdate(jobId.getId())); } + @Override + public Job findByKey(TenantId tenantId, String key) { + return DaoUtil.getData(jobRepository.findByTenantIdAndKey(tenantId.getId(), key)); + } + @Override public boolean existsByKeyAndStatusOneOf(String key, JobStatus... statuses) { return jobRepository.existsByKeyAndStatusIn(key, Arrays.stream(statuses).toList()); From 874d59a706014af8d3e6a2acb1aea69c927f1eb1 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 1 May 2025 11:59:41 +0300 Subject: [PATCH 111/335] updated methods --- .../java/org/thingsboard/server/dao/job/JobService.java | 2 +- .../org/thingsboard/server/dao/job/DefaultJobService.java | 6 +++--- .../main/java/org/thingsboard/server/dao/job/JobDao.java | 4 ++-- .../org/thingsboard/server/dao/sql/job/JobRepository.java | 6 +++--- .../org/thingsboard/server/dao/sql/job/JpaJobDao.java | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java index 65b21853b8..3c00b2fe0e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java @@ -37,6 +37,6 @@ public interface JobService extends EntityDaoService { PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink); - Job findJobByKey(TenantId tenantId, String key); + Job findLatestJobByKey(TenantId tenantId, String key); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index 6cc315c8f8..ce4aa47741 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -55,7 +55,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi @Transactional @Override public Job submitJob(TenantId tenantId, Job job) { - if (jobDao.existsByKeyAndStatusOneOf(job.getKey(), QUEUED, PENDING, RUNNING)) { + if (jobDao.existsByTenantAndKeyAndStatusOneOf(tenantId, job.getKey(), QUEUED, PENDING, RUNNING)) { throw new IllegalArgumentException("The same job is already queued or running"); } if (jobDao.existsByTenantIdAndTypeAndStatusOneOf(tenantId, job.getType(), PENDING, RUNNING)) { @@ -188,8 +188,8 @@ public class DefaultJobService extends AbstractEntityService implements JobServi } @Override - public Job findJobByKey(TenantId tenantId, String key) { - return jobDao.findByKey(tenantId, key); + public Job findLatestJobByKey(TenantId tenantId, String key) { + return jobDao.findLatestByKey(tenantId, key); } private Job findForUpdate(TenantId tenantId, JobId jobId) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java index a892fb3e22..67a234245d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java @@ -30,9 +30,9 @@ public interface JobDao extends Dao { Job findByIdForUpdate(TenantId tenantId, JobId jobId); - Job findByKey(TenantId tenantId, String key); + Job findLatestByKey(TenantId tenantId, String key); - boolean existsByKeyAndStatusOneOf(String key, JobStatus... statuses); + boolean existsByTenantAndKeyAndStatusOneOf(TenantId tenantId, String key, JobStatus... statuses); boolean existsByTenantIdAndTypeAndStatusOneOf(TenantId tenantId, JobType type, JobStatus... statuses); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java index bbbdbf3a47..0cc43375fe 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java @@ -45,15 +45,15 @@ public interface JobRepository extends JpaRepository { @Query("SELECT j FROM JobEntity j WHERE j.id = :id") JobEntity findByIdForUpdate(UUID id); - JobEntity findByTenantIdAndKey(@Param("tenantId") UUID tenantId, @Param("key") String key); + JobEntity findLatestByTenantIdAndKey(UUID tenantId, String key); - boolean existsByKeyAndStatusIn(String key, List statuses); + boolean existsByTenantIdAndKeyAndStatusIn(UUID tenantId, String key, List statuses); boolean existsByTenantIdAndTypeAndStatusIn(UUID tenantId, JobType type, List statuses); @Lock(LockModeType.PESSIMISTIC_WRITE) // SELECT FOR UPDATE @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId AND j.type = :type " + - "AND j.status = :status ORDER BY j.createdTime ASC, j.id ASC") + "AND j.status = :status ORDER BY j.createdTime ASC, j.id ASC") JobEntity findOldestByTenantIdAndTypeAndStatusForUpdate(UUID tenantId, JobType type, JobStatus status, Limit limit); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java index 71bbdb2f79..7d8def5792 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java @@ -55,13 +55,13 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao } @Override - public Job findByKey(TenantId tenantId, String key) { - return DaoUtil.getData(jobRepository.findByTenantIdAndKey(tenantId.getId(), key)); + public Job findLatestByKey(TenantId tenantId, String key) { + return DaoUtil.getData(jobRepository.findLatestByTenantIdAndKey(tenantId.getId(), key)); } @Override - public boolean existsByKeyAndStatusOneOf(String key, JobStatus... statuses) { - return jobRepository.existsByKeyAndStatusIn(key, Arrays.stream(statuses).toList()); + public boolean existsByTenantAndKeyAndStatusOneOf(TenantId tenantId, String key, JobStatus... statuses) { + return jobRepository.existsByTenantIdAndKeyAndStatusIn(tenantId.getId(), key, Arrays.stream(statuses).toList()); } @Override From d357be9209e7ef6826114bc6a839d7a67135ebd6 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 1 May 2025 12:18:10 +0300 Subject: [PATCH 112/335] Minor refactoring --- .../service/job/task/DummyTaskProcessor.java | 5 +- .../server/queue/task/TaskProcessor.java | 4 +- .../thingsboard/rest/client/RestClient.java | 49 +++++++++++++------ 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java index 73a27a0012..361dbfbde1 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java @@ -23,10 +23,10 @@ import org.thingsboard.server.queue.task.TaskProcessor; @Component @RequiredArgsConstructor -public class DummyTaskProcessor extends TaskProcessor { +public class DummyTaskProcessor extends TaskProcessor { @Override - public void process(DummyTask task) throws Exception { + public Void process(DummyTask task) throws Exception { if (task.getProcessingTimeMs() > 0) { Thread.sleep(task.getProcessingTimeMs()); } @@ -37,6 +37,7 @@ public class DummyTaskProcessor extends TaskProcessor { String error = task.getErrors().get(task.getAttempt() - 1); throw new RuntimeException(error); } + return null; } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index f0ba542ab9..57dd46a6e5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -43,7 +43,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -public abstract class TaskProcessor { +public abstract class TaskProcessor { protected final Logger log = LoggerFactory.getLogger(getClass()); @@ -135,7 +135,7 @@ public abstract class TaskProcessor { } } - public abstract void process(T task) throws Exception; + public abstract R process(T task) throws Exception; private void reportSuccess(Task task) { TaskResult result = TaskResult.builder() diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 4f7538d205..eb051b19e5 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -19,7 +19,9 @@ import com.auth0.jwt.JWT; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.base.Strings; +import lombok.SneakyThrows; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.concurrent.LazyInitializer; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; @@ -55,9 +57,9 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.EventInfo; -import org.thingsboard.server.common.data.ResourceExportData; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ResourceExportData; import org.thingsboard.server.common.data.ResourceSubType; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.StringUtils; @@ -205,7 +207,9 @@ public class RestClient implements Closeable { private static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; private static final long AVG_REQUEST_TIMEOUT = TimeUnit.SECONDS.toMillis(30); protected static final String ACTIVATE_TOKEN_REGEX = "/api/noauth/activate?activateToken="; - private final ExecutorService service = ThingsBoardExecutors.newWorkStealingPool(10, getClass()); + private final LazyInitializer executor = LazyInitializer.builder() + .setInitializer(() -> ThingsBoardExecutors.newWorkStealingPool(10, getClass())) + .get(); protected final RestTemplate restTemplate; protected final RestTemplate loginRestTemplate; protected final String baseURL; @@ -223,22 +227,30 @@ public class RestClient implements Closeable { } public RestClient(RestTemplate restTemplate, String baseURL) { + this(restTemplate, baseURL, null); + } + + public RestClient(RestTemplate restTemplate, String baseURL, String accessToken) { this.restTemplate = restTemplate; this.loginRestTemplate = new RestTemplate(restTemplate.getRequestFactory()); this.baseURL = baseURL; this.restTemplate.getInterceptors().add((request, bytes, execution) -> { HttpRequest wrapper = new HttpRequestWrapper(request); - long calculatedTs = System.currentTimeMillis() + clientServerTimeDiff + AVG_REQUEST_TIMEOUT; - if (calculatedTs > mainTokenExpTs) { - synchronized (RestClient.this) { - if (calculatedTs > mainTokenExpTs) { - if (calculatedTs < refreshTokenExpTs) { - refreshToken(); - } else { - doLogin(); + if (accessToken == null) { + long calculatedTs = System.currentTimeMillis() + clientServerTimeDiff + AVG_REQUEST_TIMEOUT; + if (calculatedTs > mainTokenExpTs) { + synchronized (RestClient.this) { + if (calculatedTs > mainTokenExpTs) { + if (calculatedTs < refreshTokenExpTs) { + refreshToken(); + } else { + doLogin(); + } } } } + } else { + mainToken = accessToken; } wrapper.getHeaders().set(JWT_TOKEN_HEADER_PARAM, "Bearer " + mainToken); return execution.execute(wrapper, bytes); @@ -2403,7 +2415,7 @@ public class RestClient implements Closeable { } public Future> getAttributeKvEntriesAsync(EntityId entityId, List keys) { - return service.submit(() -> getAttributeKvEntries(entityId, keys)); + return getExecutor().submit(() -> getAttributeKvEntries(entityId, keys)); } public List getAttributesByScope(EntityId entityId, String scope, List keys) { @@ -2976,7 +2988,7 @@ public class RestClient implements Closeable { addWidgetInfoFiltersToParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList, params); return restTemplate.exchange( baseURL + "/api/widgetTypes?" + getUrlParams(pageLink) + - getWidgetTypeInfoPageRequestUrlParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList), + getWidgetTypeInfoPageRequestUrlParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { @@ -3064,7 +3076,7 @@ public class RestClient implements Closeable { addWidgetInfoFiltersToParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList, params); return restTemplate.exchange( baseURL + "/api/widgetTypesInfos?widgetsBundleId={widgetsBundleId}&" + getUrlParams(pageLink) + - getWidgetTypeInfoPageRequestUrlParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList), + getWidgetTypeInfoPageRequestUrlParams(tenantOnly, fullSearch, deprecatedFilter, widgetTypeList), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { @@ -3765,7 +3777,7 @@ public class RestClient implements Closeable { } public PageData getImages(PageLink pageLink, boolean includeSystemImages) { - return this.getImages(pageLink, null, includeSystemImages); + return this.getImages(pageLink, null, includeSystemImages); } public PageData getImages(PageLink pageLink, ResourceSubType imageSubType, boolean includeSystemImages) { @@ -4175,7 +4187,14 @@ public class RestClient implements Closeable { @Override public void close() { - service.shutdown(); + if (executor.isInitialized()) { + getExecutor().shutdown(); + } + } + + @SneakyThrows + private ExecutorService getExecutor() { + return executor.get(); } } From 97b45a6bf917a00b7e0eba98a88a9522903af8a6 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 1 May 2025 12:23:59 +0300 Subject: [PATCH 113/335] fixed tests --- .../org/thingsboard/server/dao/job/DefaultJobService.java | 5 +++++ .../main/java/org/thingsboard/server/dao/job/JobDao.java | 2 ++ .../org/thingsboard/server/dao/sql/job/JobRepository.java | 6 +++++- .../java/org/thingsboard/server/dao/sql/job/JpaJobDao.java | 5 +++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index ce4aa47741..3e511ccfe4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -206,6 +206,11 @@ public class DefaultJobService extends AbstractEntityService implements JobServi jobDao.removeById(tenantId, id.getId()); } + @Override + public void deleteByTenantId(TenantId tenantId) { + jobDao.deleteByTenantId(tenantId); + } + @Override public EntityType getEntityType() { return EntityType.JOB; diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java index 67a234245d..afe182d8cd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java @@ -38,4 +38,6 @@ public interface JobDao extends Dao { Job findOldestByTenantIdAndTypeAndStatusForUpdate(TenantId tenantId, JobType type, JobStatus status); + void deleteByTenantId(TenantId tenantId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java index 0cc43375fe..a4d280aba4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java @@ -45,7 +45,9 @@ public interface JobRepository extends JpaRepository { @Query("SELECT j FROM JobEntity j WHERE j.id = :id") JobEntity findByIdForUpdate(UUID id); - JobEntity findLatestByTenantIdAndKey(UUID tenantId, String key); + @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId AND j.key = :key " + + "ORDER BY j.createdTime DESC") + JobEntity findLatestByTenantIdAndKey(@Param("tenantId") UUID tenantId, @Param("key") String key); boolean existsByTenantIdAndKeyAndStatusIn(UUID tenantId, String key, List statuses); @@ -56,4 +58,6 @@ public interface JobRepository extends JpaRepository { "AND j.status = :status ORDER BY j.createdTime ASC, j.id ASC") JobEntity findOldestByTenantIdAndTypeAndStatusForUpdate(UUID tenantId, JobType type, JobStatus status, Limit limit); + void deleteByTenantId(UUID tenantId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java index 7d8def5792..0e5ee4683e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java @@ -74,6 +74,11 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao return DaoUtil.getData(jobRepository.findOldestByTenantIdAndTypeAndStatusForUpdate(tenantId.getId(), type, status, Limit.of(1))); } + @Override + public void deleteByTenantId(TenantId tenantId) { + jobRepository.deleteByTenantId(tenantId.getId()); + } + @Override public EntityType getEntityType() { return EntityType.JOB; From 762abebf790f4255e790dca886baa3f8e047740c Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 1 May 2025 12:33:12 +0300 Subject: [PATCH 114/335] added query --- .../org/thingsboard/server/dao/sql/job/JobRepository.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java index a4d280aba4..0ecd517f51 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java @@ -21,9 +21,11 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.dao.model.sql.JobEntity; @@ -58,6 +60,9 @@ public interface JobRepository extends JpaRepository { "AND j.status = :status ORDER BY j.createdTime ASC, j.id ASC") JobEntity findOldestByTenantIdAndTypeAndStatusForUpdate(UUID tenantId, JobType type, JobStatus status, Limit limit); + @Transactional + @Modifying + @Query("DELETE FROM JobEntity j WHERE j.tenantId = :tenantId") void deleteByTenantId(UUID tenantId); } From 9e878923e1297386c0800f6488bddc6a5cb3a132 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 1 May 2025 13:41:36 +0300 Subject: [PATCH 115/335] Refactor job and task results --- .../server/service/job/DefaultJobManager.java | 24 +++++----- .../server/service/job/DummyJobProcessor.java | 23 ++++----- .../server/service/job/JobProcessor.java | 8 ++-- .../service/job/task/DummyTaskProcessor.java | 9 ++-- .../server/service/job/JobManagerTest.java | 17 +++---- .../common/data/job/JobConfiguration.java | 3 +- .../server/common/data/job/JobResult.java | 16 ++++++- .../server/common/data/job/JobStats.java | 1 + .../common/data/job/{ => task}/DummyTask.java | 35 +++++++------- .../common/data/job/task/DummyTaskResult.java | 48 +++++++++++++++++++ .../common/data/job/{ => task}/Task.java | 9 ++-- .../TaskFailure.java} | 12 ++--- .../TaskResult.java} | 17 +++++-- .../server/queue/task/JobStatsService.java | 2 +- .../server/queue/task/TaskProcessor.java | 39 +++++++-------- .../server/dao/job/DefaultJobService.java | 15 +----- 16 files changed, 169 insertions(+), 109 deletions(-) rename common/data/src/main/java/org/thingsboard/server/common/data/job/{ => task}/DummyTask.java (63%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java rename common/data/src/main/java/org/thingsboard/server/common/data/job/{ => task}/Task.java (85%) rename common/data/src/main/java/org/thingsboard/server/common/data/job/{TaskResult.java => task/TaskFailure.java} (79%) rename common/data/src/main/java/org/thingsboard/server/common/data/job/{TaskFailure.java => task/TaskResult.java} (74%) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index b72974144d..3d601a9358 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -31,9 +31,8 @@ import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.job.Task; -import org.thingsboard.server.common.data.job.TaskFailure; -import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.data.job.task.Task; +import org.thingsboard.server.common.data.job.task.TaskResult; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; @@ -50,7 +49,6 @@ import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -118,7 +116,7 @@ public class DefaultJobManager implements JobManager { JobId jobId = job.getId(); try { JobProcessor processor = jobProcessors.get(job.getType()); - List toReprocess = job.getConfiguration().getToReprocess(); + List toReprocess = job.getConfiguration().getToReprocess(); if (toReprocess == null) { int tasksCount = processor.process(job, this::submitTask); // todo: think about stopping tb - while tasks are being submitted log.info("[{}][{}][{}] Submitted {} tasks", tenantId, jobId, job.getType(), tasksCount); @@ -155,20 +153,24 @@ public class DefaultJobManager implements JobManager { if (result.getGeneralError() != null) { throw new IllegalArgumentException("Reprocessing not allowed since job has general error"); } - List failures = result.getFailures(); - if (result.getFailedCount() > failures.size()) { - throw new IllegalArgumentException("Reprocessing not allowed since there are too many failures (more than " + failures.size() + ")"); + List taskFailures = result.getResults().stream() + .filter(taskResult -> !taskResult.isSuccess() && !taskResult.isDiscarded()) + .toList(); + if (result.getFailedCount() > taskFailures.size()) { + throw new IllegalArgumentException("Reprocessing not allowed since there are too many failures (more than " + taskFailures.size() + ")"); } result.setFailedCount(0); - result.setFailures(Collections.emptyList()); + result.setResults(result.getResults().stream() + .filter(TaskResult::isSuccess) + .toList()); - job.getConfiguration().setToReprocess(failures); + job.getConfiguration().setToReprocess(taskFailures); jobService.submitJob(tenantId, job); } - private void submitTask(Task task) { + private void submitTask(Task task) { log.info("[{}][{}] Submitting task: {}", task.getTenantId(), task.getJobId(), task); TaskProto taskProto = TaskProto.newBuilder() .setValue(JacksonUtil.toString(task)) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java index 900b876c09..64148af259 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java @@ -18,12 +18,13 @@ package org.thingsboard.server.service.job; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.job.DummyJobConfiguration; -import org.thingsboard.server.common.data.job.DummyTask; -import org.thingsboard.server.common.data.job.DummyTask.DummyTaskFailure; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.job.Task; -import org.thingsboard.server.common.data.job.TaskFailure; +import org.thingsboard.server.common.data.job.task.DummyTask; +import org.thingsboard.server.common.data.job.task.DummyTask.DummyTaskFailure; +import org.thingsboard.server.common.data.job.task.DummyTaskResult; +import org.thingsboard.server.common.data.job.task.Task; +import org.thingsboard.server.common.data.job.task.TaskResult; import java.util.Collections; import java.util.List; @@ -34,7 +35,7 @@ import java.util.function.Consumer; public class DummyJobProcessor implements JobProcessor { @Override - public int process(Job job, Consumer taskConsumer) throws Exception { + public int process(Job job, Consumer> taskConsumer) throws Exception { DummyJobConfiguration configuration = job.getConfiguration(); if (configuration.getGeneralError() != null) { for (int number = 1; number <= configuration.getSubmittedTasksBeforeGeneralError(); number++) { @@ -63,15 +64,15 @@ public class DummyJobProcessor implements JobProcessor { } @Override - public void reprocess(Job job, List failures, Consumer taskConsumer) throws Exception { - for (TaskFailure failure : failures) { - DummyTaskFailure taskFailure = (DummyTaskFailure) failure; - taskConsumer.accept(createTask(job, job.getConfiguration(), taskFailure.getNumber(), taskFailure.isFailAlways() ? - List.of(taskFailure.getError()) : Collections.emptyList(), taskFailure.isFailAlways())); + public void reprocess(Job job, List taskFailures, Consumer> taskConsumer) throws Exception { + for (TaskResult taskFailure : taskFailures) { + DummyTaskFailure failure = ((DummyTaskResult) taskFailure).getFailure(); + taskConsumer.accept(createTask(job, job.getConfiguration(), failure.getNumber(), failure.isFailAlways() ? + List.of(failure.getError()) : Collections.emptyList(), failure.isFailAlways())); } } - private Task createTask(Job job, DummyJobConfiguration configuration, int number, List errors, boolean failAlways) { + private DummyTask createTask(Job job, DummyJobConfiguration configuration, int number, List errors, boolean failAlways) { return DummyTask.builder() .tenantId(job.getTenantId()) .jobId(job.getId()) diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java index da2c75d166..301d4bf6eb 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java @@ -17,17 +17,17 @@ package org.thingsboard.server.service.job; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.job.Task; -import org.thingsboard.server.common.data.job.TaskFailure; +import org.thingsboard.server.common.data.job.task.Task; +import org.thingsboard.server.common.data.job.task.TaskResult; import java.util.List; import java.util.function.Consumer; public interface JobProcessor { - int process(Job job, Consumer taskConsumer) throws Exception; + int process(Job job, Consumer> taskConsumer) throws Exception; - void reprocess(Job job, List failures, Consumer taskConsumer) throws Exception; + void reprocess(Job job, List taskFailures, Consumer> taskConsumer) throws Exception; JobType getType(); diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java index 361dbfbde1..5178461746 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java @@ -17,16 +17,17 @@ package org.thingsboard.server.service.job.task; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.job.DummyTask; +import org.thingsboard.server.common.data.job.task.DummyTask; import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.job.task.DummyTaskResult; import org.thingsboard.server.queue.task.TaskProcessor; @Component @RequiredArgsConstructor -public class DummyTaskProcessor extends TaskProcessor { +public class DummyTaskProcessor extends TaskProcessor { @Override - public Void process(DummyTask task) throws Exception { + public DummyTaskResult process(DummyTask task) throws Exception { if (task.getProcessingTimeMs() > 0) { Thread.sleep(task.getProcessingTimeMs()); } @@ -37,7 +38,7 @@ public class DummyTaskProcessor extends TaskProcessor { String error = task.getErrors().get(task.getAttempt() - 1); throw new RuntimeException(error); } - return null; + return DummyTaskResult.success(); } @Override diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index f9881d0248..d7d22a2859 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -26,11 +26,12 @@ import org.springframework.test.context.TestPropertySource; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.DummyJobConfiguration; -import org.thingsboard.server.common.data.job.DummyTask.DummyTaskFailure; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.job.task.DummyTask.DummyTaskFailure; +import org.thingsboard.server.common.data.job.task.DummyTaskResult; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.job.JobService; @@ -101,7 +102,7 @@ public class JobManagerTest extends AbstractControllerTest { Job job = findJobById(jobId); assertThat(job.getStatus()).isEqualTo(JobStatus.COMPLETED); assertThat(job.getResult().getSuccessfulCount()).isEqualTo(tasksCount); - assertThat(job.getResult().getFailures()).isEmpty(); + assertThat(job.getResult().getResults()).isEmpty(); assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount); }); } @@ -131,8 +132,8 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(jobResult.getSuccessfulCount()).isEqualTo(successfulTasks); assertThat(jobResult.getFailedCount()).isEqualTo(failedTasks); assertThat(jobResult.getTotalCount()).isEqualTo(successfulTasks + failedTasks); - assertThat(jobResult.getFailures().get(0).getError()).isEqualTo("error3"); // last error - assertThat(jobResult.getFailures().get(1).getError()).isEqualTo("error3"); // last error + assertThat(((DummyTaskResult) jobResult.getResults().get(0)).getFailure().getError()).isEqualTo("error3"); // last error + assertThat(((DummyTaskResult) jobResult.getResults().get(1)).getFailure().getError()).isEqualTo("error3"); // last error assertThat(jobResult.getCompletedCount()).isEqualTo(jobResult.getTotalCount()); }); } @@ -353,7 +354,7 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(jobResult.getFailedCount()).isEqualTo(failedTasks); for (int i = 0, taskNumber = successfulTasks + 1; taskNumber <= totalTasksCount; i++, taskNumber++) { - DummyTaskFailure failure = (DummyTaskFailure) jobResult.getFailures().get(i); + DummyTaskFailure failure = ((DummyTaskResult) jobResult.getResults().get(i)).getFailure(); assertThat(failure.getNumber()).isEqualTo(taskNumber); assertThat(failure.getError()).isEqualTo("error"); } @@ -367,7 +368,7 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(job.getResult().getSuccessfulCount()).isEqualTo(totalTasksCount); assertThat(job.getResult().getFailedCount()).isZero(); assertThat(job.getResult().getTotalCount()).isEqualTo(totalTasksCount); - assertThat(job.getResult().getFailures()).isEmpty(); + assertThat(job.getResult().getResults()).isEmpty(); }); } @@ -400,7 +401,7 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(jobResult.getTotalCount()).isEqualTo(totalTasksCount); for (int i = 0, taskNumber = successfulTasks + 1; taskNumber <= totalTasksCount; i++, taskNumber++) { - DummyTaskFailure failure = (DummyTaskFailure) jobResult.getFailures().get(i); + DummyTaskFailure failure = ((DummyTaskResult) jobResult.getResults().get(i)).getFailure(); assertThat(failure.getNumber()).isEqualTo(taskNumber); assertThat(failure.getError()).isEqualTo("error"); } @@ -417,7 +418,7 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(jobResult.getTotalCount()).isEqualTo(totalTasksCount); for (int i = 0, taskNumber = successfulTasks + failedTasks + 1; taskNumber <= totalTasksCount; i++, taskNumber++) { - DummyTaskFailure failure = (DummyTaskFailure) jobResult.getFailures().get(i); + DummyTaskFailure failure = ((DummyTaskResult) jobResult.getResults().get(i)).getFailure(); assertThat(failure.getNumber()).isEqualTo(taskNumber); assertThat(failure.getError()).isEqualTo("error"); assertThat(failure.isFailAlways()).isTrue(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java index 7a2eccd42a..8aed4adbe5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; +import org.thingsboard.server.common.data.job.task.TaskResult; import java.io.Serializable; import java.util.List; @@ -32,7 +33,7 @@ import java.util.List; @Data public abstract class JobConfiguration implements Serializable { - private List toReprocess; + private List toReprocess; public abstract JobType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index bf5e5f2c56..4e4787bbe5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.job.task.TaskResult; import java.io.Serializable; import java.util.ArrayList; @@ -40,7 +41,7 @@ public abstract class JobResult implements Serializable { private int failedCount; private int discardedCount; private Integer totalCount = null; // set when all tasks are submitted - private List failures = new ArrayList<>(); + private List results = new ArrayList<>(); private String generalError; private long cancellationTs; @@ -50,6 +51,19 @@ public abstract class JobResult implements Serializable { return successfulCount + failedCount + discardedCount; } + public void processTaskResult(TaskResult taskResult) { + if (taskResult.isSuccess()) { + successfulCount++; + } else if (taskResult.isDiscarded()) { + discardedCount++; + } else { + failedCount++; + if (results.size() < 1000) { // preserving only first 1000 errors, not reprocessing if there are more failures + results.add(taskResult); + } + } + } + public abstract JobType getJobType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java index 9d2c3d9be2..dc3e265f2d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.job; import lombok.Data; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.task.TaskResult; import java.util.ArrayList; import java.util.List; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java similarity index 63% rename from common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java rename to common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java index ac15dc63cc..8c3ed31e50 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java @@ -13,22 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.job; +package org.thingsboard.server.common.data.job.task; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import lombok.experimental.SuperBuilder; +import org.thingsboard.server.common.data.job.JobType; import java.util.List; +import java.util.Optional; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @ToString(callSuper = true) -public class DummyTask extends Task { +public class DummyTask extends Task { private int number; private long processingTimeMs; @@ -41,8 +43,17 @@ public class DummyTask extends Task { } @Override - public TaskFailure toFailure(Throwable error) { - return new DummyTaskFailure(number, failAlways, error.getMessage()); + public DummyTaskResult toResult(boolean discarded, Optional error) { + var result = DummyTaskResult.builder(); + result.discarded(discarded); + if (error.isPresent()) { + result.failure(DummyTaskFailure.builder() + .error(error.map(Throwable::getMessage).orElse(null)) + .number(number) + .failAlways(failAlways) + .build()); + } + return result.build(); } @Override @@ -51,24 +62,14 @@ public class DummyTask extends Task { } @Data - @EqualsAndHashCode(callSuper = true) @NoArgsConstructor - public static class DummyTaskFailure extends TaskFailure { + @EqualsAndHashCode(callSuper = true) + @SuperBuilder + public static class DummyTaskFailure extends TaskFailure { // todo: do we need separate structure? private int number; private boolean failAlways; - public DummyTaskFailure(int number, boolean failAlways, String error) { - super(error); - this.number = number; - this.failAlways = failAlways; - } - - @Override - public JobType getJobType() { - return JobType.DUMMY; - } - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java new file mode 100644 index 0000000000..7c36636181 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job.task; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.job.task.DummyTask.DummyTaskFailure; + +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@SuperBuilder +public class DummyTaskResult extends TaskResult { + + private static final DummyTaskResult SUCCESS = new DummyTaskResult(true); + + private DummyTaskFailure failure; + + public DummyTaskResult(boolean success) { + super(success); + } + + public static DummyTaskResult success() { + return SUCCESS; + } + + @Override + public JobType getJobType() { + return JobType.DUMMY; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java similarity index 85% rename from common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java rename to common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java index ad7c6b62df..5cf39ec6fb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Task.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.job; +package org.thingsboard.server.common.data.job.task; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -25,6 +25,9 @@ import lombok.Data; import lombok.experimental.SuperBuilder; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.JobType; + +import java.util.Optional; @Data @JsonIgnoreProperties(ignoreUnknown = true) @@ -34,7 +37,7 @@ import org.thingsboard.server.common.data.id.TenantId; }) @SuperBuilder @AllArgsConstructor -public abstract class Task { +public abstract class Task { private TenantId tenantId; private JobId jobId; @@ -48,7 +51,7 @@ public abstract class Task { @JsonIgnore public abstract Object getKey(); - public abstract TaskFailure toFailure(Throwable error); + public abstract R toResult(boolean discarded, Optional error); public abstract JobType getJobType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskFailure.java similarity index 79% rename from common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java rename to common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskFailure.java index 0ee9a2b477..ce22a08269 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskFailure.java @@ -13,21 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.job; +package org.thingsboard.server.common.data.job.task; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; @Data @AllArgsConstructor @NoArgsConstructor -@Builder -public class TaskResult { +@SuperBuilder +public abstract class TaskFailure { - private boolean success; - private boolean discarded; - private TaskFailure failure; + private String error; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java similarity index 74% rename from common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java rename to common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java index 7a04db8188..40f0763e3a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/TaskFailure.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.job; +package org.thingsboard.server.common.data.job.task; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; @@ -22,19 +22,26 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.job.DummyTask.DummyTaskFailure; +import lombok.experimental.SuperBuilder; +import org.thingsboard.server.common.data.job.JobType; @Data @AllArgsConstructor @NoArgsConstructor +@SuperBuilder @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @JsonSubTypes({ - @Type(name = "DUMMY", value = DummyTaskFailure.class) + @Type(name = "DUMMY", value = DummyTaskResult.class) }) -public abstract class TaskFailure { +public abstract class TaskResult { - private String error; + private boolean success; + private boolean discarded; + + public TaskResult(boolean success) { + this.success = success; + } public abstract JobType getJobType(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java index 8d69f5781b..c3780f15e7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java @@ -22,7 +22,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.data.job.task.TaskResult; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 57dd46a6e5..25e9d8807d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -25,8 +25,8 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.job.Task; -import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.data.job.task.Task; +import org.thingsboard.server.common.data.job.task.TaskResult; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; @@ -37,13 +37,14 @@ import org.thingsboard.server.queue.provider.TaskProcessorQueueFactory; import org.thingsboard.server.queue.util.AfterStartUp; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -public abstract class TaskProcessor { +public abstract class TaskProcessor, R extends TaskResult> { protected final Logger log = LoggerFactory.getLogger(getClass()); @@ -98,16 +99,17 @@ public abstract class TaskProcessor { private void processMsgs(List> msgs, TbQueueConsumer> consumer) throws Exception { for (TbProtoQueueMsg msg : msgs) { try { - Task task = JacksonUtil.fromString(msg.getValue().getValue(), Task.class); + @SuppressWarnings("unchecked") + T task = (T) JacksonUtil.fromString(msg.getValue().getValue(), Task.class); if (discardedJobs.contains(task.getJobId().getId())) { log.info("Skipping task '{}' for cancelled job {}", task.getKey(), task.getJobId()); - reportCancelled(task); + reportTaskDiscarded(task); continue; } else if (deletedTenants.contains(task.getTenantId().getId())) { log.info("Skipping task '{}' for deleted tenant {}", task.getKey(), task.getTenantId()); continue; } - processTask((T) task); + processTask(task); } catch (InterruptedException e) { throw e; } catch (Exception e) { @@ -121,8 +123,8 @@ public abstract class TaskProcessor { task.setAttempt(task.getAttempt() + 1); log.info("Processing task: {}", task); try { - process(task); - reportSuccess(task); + R result = process(task); + reportTaskResult(task, result); } catch (InterruptedException e) { throw e; } catch (Exception e) { @@ -130,31 +132,22 @@ public abstract class TaskProcessor { if (task.getAttempt() <= task.getRetries()) { processTask(task); } else { - reportFailure(task, e); + reportTaskFailure(task, e); } } } public abstract R process(T task) throws Exception; - private void reportSuccess(Task task) { - TaskResult result = TaskResult.builder() - .success(true) - .build(); - statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); + private void reportTaskFailure(T task, Throwable error) { + reportTaskResult(task, task.toResult(false, Optional.of(error))); } - private void reportFailure(Task task, Throwable error) { - TaskResult result = TaskResult.builder() - .failure(task.toFailure(error)) - .build(); - statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); + private void reportTaskDiscarded(T task) { + reportTaskResult(task, task.toResult(true, Optional.empty())); } - private void reportCancelled(Task task) { - TaskResult result = TaskResult.builder() - .discarded(true) - .build(); + private void reportTaskResult(T task, R result) { statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index 3e511ccfe4..07b5aeadf9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -29,8 +29,7 @@ import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.job.TaskFailure; -import org.thingsboard.server.common.data.job.TaskResult; +import org.thingsboard.server.common.data.job.task.TaskResult; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; @@ -118,17 +117,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi boolean publishEvent = false; for (TaskResult taskResult : jobStats.getTaskResults()) { - if (taskResult.isSuccess()) { - result.setSuccessfulCount(result.getSuccessfulCount() + 1); - } else if (taskResult.isDiscarded()) { - result.setDiscardedCount(result.getDiscardedCount() + 1); - } else { - TaskFailure failure = taskResult.getFailure(); - result.setFailedCount(result.getFailedCount() + 1); - if (result.getFailures().size() < 1000) { // preserving only first 1000 errors, not reprocessing if there are more failures - result.getFailures().add(failure); - } - } + result.processTaskResult(taskResult); if (result.getCancellationTs() > 0) { if (!taskResult.isDiscarded() && System.currentTimeMillis() > result.getCancellationTs()) { From 950d1d85c4461546d274dd855707c90af43b6fde Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 1 May 2025 15:02:28 +0300 Subject: [PATCH 116/335] Task results refactoring --- .../server/service/job/DummyJobProcessor.java | 2 +- .../server/service/job/JobManagerTest.java | 2 +- .../common/data/job/task/DummyTask.java | 30 ++++------------- .../common/data/job/task/DummyTaskResult.java | 33 +++++++++++++++---- .../server/common/data/job/task/Task.java | 6 ++-- .../common/data/job/task/TaskResult.java | 4 --- .../server/queue/task/TaskProcessor.java | 7 ++-- 7 files changed, 43 insertions(+), 41 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java index 64148af259..e1b91e606a 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java @@ -21,8 +21,8 @@ import org.thingsboard.server.common.data.job.DummyJobConfiguration; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.DummyTask; -import org.thingsboard.server.common.data.job.task.DummyTask.DummyTaskFailure; import org.thingsboard.server.common.data.job.task.DummyTaskResult; +import org.thingsboard.server.common.data.job.task.DummyTaskResult.DummyTaskFailure; import org.thingsboard.server.common.data.job.task.Task; import org.thingsboard.server.common.data.job.task.TaskResult; diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index d7d22a2859..ee1eee6531 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -30,8 +30,8 @@ import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.job.task.DummyTask.DummyTaskFailure; import org.thingsboard.server.common.data.job.task.DummyTaskResult; +import org.thingsboard.server.common.data.job.task.DummyTaskResult.DummyTaskFailure; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.job.JobService; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java index 8c3ed31e50..39e1306597 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java @@ -23,7 +23,6 @@ import lombok.experimental.SuperBuilder; import org.thingsboard.server.common.data.job.JobType; import java.util.List; -import java.util.Optional; @Data @NoArgsConstructor @@ -43,33 +42,18 @@ public class DummyTask extends Task { } @Override - public DummyTaskResult toResult(boolean discarded, Optional error) { - var result = DummyTaskResult.builder(); - result.discarded(discarded); - if (error.isPresent()) { - result.failure(DummyTaskFailure.builder() - .error(error.map(Throwable::getMessage).orElse(null)) - .number(number) - .failAlways(failAlways) - .build()); - } - return result.build(); + public DummyTaskResult toFailed(Throwable error) { + return DummyTaskResult.failed(this, error); } @Override - public JobType getJobType() { - return JobType.DUMMY; + public DummyTaskResult toDiscarded() { + return DummyTaskResult.discarded(); } - @Data - @NoArgsConstructor - @EqualsAndHashCode(callSuper = true) - @SuperBuilder - public static class DummyTaskFailure extends TaskFailure { // todo: do we need separate structure? - - private int number; - private boolean failAlways; - + @Override + public JobType getJobType() { + return JobType.DUMMY; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java index 7c36636181..cd68b4d248 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java @@ -20,7 +20,6 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.thingsboard.server.common.data.job.JobType; -import org.thingsboard.server.common.data.job.task.DummyTask.DummyTaskFailure; @Data @EqualsAndHashCode(callSuper = true) @@ -28,21 +27,43 @@ import org.thingsboard.server.common.data.job.task.DummyTask.DummyTaskFailure; @SuperBuilder public class DummyTaskResult extends TaskResult { - private static final DummyTaskResult SUCCESS = new DummyTaskResult(true); + private static final DummyTaskResult SUCCESS = DummyTaskResult.builder().success(true).build(); + private static final DummyTaskResult DISCARDED = DummyTaskResult.builder().discarded(true).build(); private DummyTaskFailure failure; - public DummyTaskResult(boolean success) { - super(success); - } - public static DummyTaskResult success() { return SUCCESS; } + public static DummyTaskResult failed(DummyTask task, Throwable error) { + DummyTaskResult result = new DummyTaskResult(); + result.setFailure(DummyTaskFailure.builder() + .error(error.getMessage()) + .number(task.getNumber()) + .failAlways(task.isFailAlways()) + .build()); + return result; + } + + public static DummyTaskResult discarded() { + return DISCARDED; + } + @Override public JobType getJobType() { return JobType.DUMMY; } + @Data + @NoArgsConstructor + @EqualsAndHashCode(callSuper = true) + @SuperBuilder + public static class DummyTaskFailure extends TaskFailure { + + private int number; + private boolean failAlways; + + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java index 5cf39ec6fb..624c5e90f8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java @@ -27,8 +27,6 @@ import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.JobType; -import java.util.Optional; - @Data @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @@ -51,7 +49,9 @@ public abstract class Task { @JsonIgnore public abstract Object getKey(); - public abstract R toResult(boolean discarded, Optional error); + public abstract R toFailed(Throwable error); + + public abstract R toDiscarded(); public abstract JobType getJobType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java index 40f0763e3a..8c4667e1be 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java @@ -39,10 +39,6 @@ public abstract class TaskResult { private boolean success; private boolean discarded; - public TaskResult(boolean success) { - this.success = success; - } - public abstract JobType getJobType(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 25e9d8807d..643e7b27bc 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -37,7 +37,6 @@ import org.thingsboard.server.queue.provider.TaskProcessorQueueFactory; import org.thingsboard.server.queue.util.AfterStartUp; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -140,11 +139,13 @@ public abstract class TaskProcessor, R extends TaskResult> { public abstract R process(T task) throws Exception; private void reportTaskFailure(T task, Throwable error) { - reportTaskResult(task, task.toResult(false, Optional.of(error))); + R taskResult = task.toFailed(error); + reportTaskResult(task, taskResult); } private void reportTaskDiscarded(T task) { - reportTaskResult(task, task.toResult(true, Optional.empty())); + R taskResult = task.toDiscarded(); + reportTaskResult(task, taskResult); } private void reportTaskResult(T task, R result) { From 1734a890e3ec640e465219dcd34a64dcf53df3d3 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 1 May 2025 16:05:35 +0300 Subject: [PATCH 117/335] UI: Refactoring units service --- ui-ngx/src/app/app.component.ts | 2 +- ui-ngx/src/app/core/services/public-api.ts | 2 +- .../core/services/{unit => }/unit.service.ts | 71 ++-- .../app/core/services/unit/converter-unit.ts | 272 -------------- .../app/core/services/unit/definitions/all.ts | 121 ------ .../lib/cards/value-card-widget.component.ts | 10 +- .../app/modules/home/models/services.map.ts | 2 +- .../home/pages/profile/profile.component.ts | 2 +- .../convert-unit-settings-panel.component.ts | 13 +- .../shared/components/unit-input.component.ts | 37 +- ui-ngx/src/app/shared/models/public-api.ts | 1 + ui-ngx/src/app/shared/models/unit.models.ts | 350 +++++++++++++++++- .../models/units}/acceleration.ts | 0 .../models/units}/angle.ts | 0 .../models/units}/angular-acceleration.ts | 0 .../models/units}/area.ts | 0 .../models/units}/charge.ts | 0 .../models/units}/digital.ts | 0 .../models/units}/electric-current.ts | 0 .../models/units}/energy.ts | 0 .../models/units}/force.ts | 0 .../models/units}/frequency.ts | 0 .../models/units}/illuminance.ts | 0 .../models/units}/length.ts | 0 .../models/units}/mass.ts | 0 .../models/units}/parts-per.ts | 0 .../models/units}/power.ts | 0 .../models/units}/pressure.ts | 0 .../models/units}/speed.ts | 0 .../models/units}/temperature.ts | 0 .../models/units}/time.ts | 0 .../models/units}/torque.ts | 0 .../models/units}/voltage.ts | 0 .../models/units}/volume-flow-rate.ts | 0 .../models/units}/volume.ts | 0 .../shared/models/widget-settings.models.ts | 56 ++- 36 files changed, 445 insertions(+), 494 deletions(-) rename ui-ngx/src/app/core/services/{unit => }/unit.service.ts (50%) delete mode 100644 ui-ngx/src/app/core/services/unit/converter-unit.ts delete mode 100644 ui-ngx/src/app/core/services/unit/definitions/all.ts rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/acceleration.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/angle.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/angular-acceleration.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/area.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/charge.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/digital.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/electric-current.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/energy.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/force.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/frequency.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/illuminance.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/length.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/mass.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/parts-per.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/power.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/pressure.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/speed.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/temperature.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/time.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/torque.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/voltage.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/volume-flow-rate.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/volume.ts (100%) diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index 6e04735706..85bddd56cb 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -33,7 +33,7 @@ import { svgIcons, svgIconsUrl } from '@shared/models/icon.models'; import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; import { SETTINGS_KEY } from '@core/settings/settings.effects'; import { initCustomJQueryEvents } from '@shared/models/jquery-event.models'; -import { UnitService } from '@core/services/unit/unit.service'; +import { UnitService } from '@core/services/unit.service'; @Component({ selector: 'tb-root', diff --git a/ui-ngx/src/app/core/services/public-api.ts b/ui-ngx/src/app/core/services/public-api.ts index f4e770bdb5..c728565c8b 100644 --- a/ui-ngx/src/app/core/services/public-api.ts +++ b/ui-ngx/src/app/core/services/public-api.ts @@ -15,7 +15,6 @@ /// export * from './script/node-script-test.service'; -export * from './unit/unit.service'; export * from './broadcast.models'; export * from './broadcast.service'; export * from './dashboard-utils.service'; @@ -29,5 +28,6 @@ export * from './raf.service'; export * from './resources.service'; export * from './time.service'; export * from './title.service'; +export * from './unit.service'; export * from './utils.service'; export * from './window.service'; diff --git a/ui-ngx/src/app/core/services/unit/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts similarity index 50% rename from ui-ngx/src/app/core/services/unit/unit.service.ts rename to ui-ngx/src/app/core/services/unit.service.ts index 6945c89d3a..3d1fc4de0a 100644 --- a/ui-ngx/src/app/core/services/unit/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -17,18 +17,19 @@ import { Injectable } from '@angular/core'; import moment from 'moment-timezone'; import { - TbUnitConvertor, - UnitDescription, - UnitDescriptionGroupByMeasure, + AllMeasures, + AllMeasuresUnits, + Converter, + getUnitConverter, + TbUnitConverter, + TbUnitMapping, + UnitInfo, + UnitInfoGroupByMeasure, UnitSystem } from '@shared/models/unit.models'; -import { isNotEmptyStr } from '@core/utils'; -import { configureMeasurements, Converter } from '@core/services/unit/converter-unit'; -import allMeasures, { AllMeasures, AllMeasuresUnits } from '@core/services/unit/definitions/all'; +import { isNotEmptyStr, isObject } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; @Injectable({ providedIn: 'root' @@ -36,18 +37,17 @@ import { AppState } from '@core/core.state'; export class UnitService { private currentUnitSystem: UnitSystem = UnitSystem.METRIC; - private converter: Converter; + private converter: Converter; - constructor(private store: Store, - private translate: TranslateService) { + constructor(private translate: TranslateService) { this.translate.onLangChange.pipe( takeUntilDestroyed() ).subscribe(() => { - this.converter = configureMeasurements(allMeasures, this.translate); - console.warn(this.converter?.list()); - console.warn(this.converter?.list('temperature')); - console.warn(this.converter?.list('temperature', UnitSystem.METRIC)); - console.warn(this.converter?.list(null, UnitSystem.IMPERIAL)); + this.converter = getUnitConverter(this.translate); + console.warn(this.converter?.listUnits()); + console.warn(this.converter?.listUnits('temperature')); + console.warn(this.converter?.listUnits('temperature', UnitSystem.METRIC)); + console.warn(this.converter?.listUnits(null, UnitSystem.IMPERIAL)); }); } @@ -64,28 +64,47 @@ export class UnitService { console.warn('[Unit system] setUnitSystem', this.currentUnitSystem); } - getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitDescription[] { - return this.converter?.list(measure, unitSystem); + getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfo[] { + return this.converter?.listUnits(measure, unitSystem); } - getUnitsGroupByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure { - return this.converter?.listGroupByMeasure(measure, unitSystem); + getUnitsGroupedByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure { + return this.converter?.unitsGroupByMeasure(measure, unitSystem); } - getUnitDescription(abbr: AllMeasuresUnits | string): UnitDescription { - return this.converter.describe(abbr); + getUnitInfo(symbol: AllMeasuresUnits | string): UnitInfo { + return this.converter.describe(symbol); } getDefaultUnit(measure: AllMeasures, unitSystem: UnitSystem): AllMeasuresUnits { return this.converter.getDefaultUnit(measure, unitSystem); } - geUnitConvertor(from: string, to: string): TbUnitConvertor { - return this.converter.convertor(from, to); + geUnitConverter(unit: TbUnitMapping): TbUnitConverter; + geUnitConverter(from: string, to: string): TbUnitConverter; + geUnitConverter(unit: TbUnitMapping | string, to?: string): TbUnitConverter { + if (unit !== null && typeof unit === 'object') { + const target = this.getTargetUnitSymbol(unit); + return this.converter.getUnitConverter(unit.from, target); + } + return this.converter.getUnitConverter(unit as string, to); } - convertValue(value: number, from: string, to: string): number { - return this.converter.convert(value, from, to); + getTargetUnitSymbol(unit: TbUnitMapping): string { + if (isObject(unit)) { + return isNotEmptyStr(unit[this.currentUnitSystem]) ? unit[this.currentUnitSystem] : unit.from; + } + return null; + } + + convertUnitValue(value: number, unit: TbUnitMapping): number; + convertUnitValue(value: number, from: string, to: string): number; + convertUnitValue(value: number, unit: string | TbUnitMapping, to?: string): number { + if (unit !== null && typeof unit === 'object') { + const target = this.getTargetUnitSymbol(unit); + return this.converter.convert(value, unit.from, target); + } + return this.converter.convert(value, unit as string, to); } private getUnitSystemByTimezone(): UnitSystem { diff --git a/ui-ngx/src/app/core/services/unit/converter-unit.ts b/ui-ngx/src/app/core/services/unit/converter-unit.ts deleted file mode 100644 index a97951db15..0000000000 --- a/ui-ngx/src/app/core/services/unit/converter-unit.ts +++ /dev/null @@ -1,272 +0,0 @@ -/// -/// Copyright © 2016-2025 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 { - Conversion, - TbMeasure, - TbUnitConvertor, - Unit, - UnitCache, - UnitDescription, - UnitDescriptionGroupByMeasure, - UnitSystem -} from '@shared/models/unit.models'; -import { AllMeasures } from '@core/services/unit/definitions/all'; -import { TranslateService } from '@ngx-translate/core'; - -type Entries = [S, T[keyof T]]; - -export class Converter< - TMeasures extends AllMeasures, - TUnits extends string, -> { - private readonly measureData: Record>; - private unitCache: Map< - string, - { - system: UnitSystem; - measure: TMeasures; - unit: Unit; - abbr: TUnits; - } - >; - - constructor( - measures: Record>, - unitCache: UnitCache - ) { - this.measureData = measures; - this.unitCache = unitCache; - } - - convertor(from: TUnits | string, to: TUnits | string): TbUnitConvertor { - return (value: number) => this.convert(value, from, to); - } - - convert(value: number, from: TUnits | string, to: TUnits | string): number { - const origin = this.getUnit(from); - const destination = this.getUnit(to); - - if (!origin) { - throw new Error(`Unsupported unit: ${from}`); - } - if (!destination) { - throw new Error(`Unsupported unit: ${to}`); - } - if (origin.abbr === destination.abbr) { - return value; - } - if (destination.measure !== origin.measure) { - throw Error(`Cannot convert incompatible measures: ${origin.measure} to ${destination.measure}`); - } - let result = value * origin.unit.to_anchor; - if (origin.unit.anchor_shift) { - result -= origin.unit.anchor_shift; - } - if (origin.system !== destination.system) { - const measureUnits = this.measureData[origin.measure][origin.system]; - const transform = measureUnits?.transform; - const ratio = measureUnits?.ratio; - if (typeof transform === 'function') { - result = transform(result); - } else if (typeof ratio === 'number') { - result *= ratio; - } else { - throw Error('System anchor requires a defined ratio or transform function'); - } - } - - if (destination.unit.anchor_shift) { - result += destination.unit.anchor_shift; - } - return result / destination.unit.to_anchor; - } - - getDefaultUnit(measureName: TMeasures | (string & {}), unitSystem: UnitSystem): TUnits { - if (!this.isMeasure(measureName)) { - return null; - } - const units = this.getUnitsForMeasure(measureName, unitSystem); - if (!units) { - return null; - } - for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { - if (unit.to_anchor === 1 && (!unit.anchor_shift || unit.anchor_shift === 0)) { - return abbr; - } - } - return null; - } - - getUnit(abbr: TUnits | (string & {})): Conversion | null { - return this.unitCache.get(abbr) ?? null; - } - - describe(abbr: TUnits | (string & {})): UnitDescription { - const unit = this.getUnit(abbr); - return unit ? this.describeUnit(unit) : null; - } - - list(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescription[] { - const results: UnitDescription[] = []; - - const measures = measureName - ? { [measureName]: this.measureData[measureName] } as Record> - : this.measureData; - - for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { - if (!this.isMeasure(name)) { - continue; - } - - const systems = unitSystem - ? [unitSystem] - : (Object.keys(measure) as UnitSystem[]); - - for (const system of systems) { - const units = this.getUnitsForMeasure(name, system); - if (!units) { - continue; - } - - for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { - results.push( - this.describeUnit({ - abbr, - measure: name as TMeasures, - system, - unit, - }) - ); - } - } - } - return results; - } - - listGroupByMeasure(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure | never { - const results: UnitDescriptionGroupByMeasure = {}; - - const measures = measureName - ? { [measureName]: this.measureData[measureName]} as Record> - : this.measureData; - - for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { - if (!this.isMeasure(name)) { - continue; - } - - results[name] = []; - - const systems = unitSystem - ? [unitSystem] - : (Object.keys(measure) as UnitSystem[]); - - for (const system of systems) { - const units = this.getUnitsForMeasure(name, system); - if (!units) { - continue; - } - - for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { - results[name].push( - this.describeUnit({ - abbr, - measure: name as TMeasures, - system, - unit, - }) - ); - } - } - } - return results; - } - - private describeUnit(unit: Conversion): UnitDescription { - return { - abbr: unit.abbr, - measure: unit.measure, - system: unit.system, - name: unit.unit.name, - tags: unit.unit.tags - }; - } - - private isMeasure(measureName: string): measureName is TMeasures { - return measureName in this.measureData; - } - - private getUnitsForMeasure( - measureName: TMeasures, - unitSystem: UnitSystem - ): Partial> | null { - const measure = this.measureData[measureName]; - let system = unitSystem; - let units = measure[system]?.units; - if (!units && unitSystem === UnitSystem.IMPERIAL) { - system = UnitSystem.METRIC; - units = measure[system]?.units; - } - return units ?? null; - } -} - -export function buildUnitCache< - TMeasures extends string, - TUnits extends string, ->(measures: Record>, - translate: TranslateService -) { - const unitCache: UnitCache = new Map(); - for (const [measureName, measure] of Object.entries(measures) as Entries< - typeof measures, - TMeasures - >[]) { - for (const [systemName, system] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - for (const [testAbbr, unit] of Object.entries(system.units) as Entries< - Record, - TUnits - >[]) { - unit.name = translate.instant(unit.name); - unitCache.set(testAbbr, { - measure: measureName, - system: systemName, - abbr: testAbbr, - unit, - }); - } - } - } - return unitCache; -} - -export function configureMeasurements< - TMeasures extends AllMeasures, - TUnits extends string, ->( - measures: Record>, - translate: TranslateService -): Converter { - if (typeof measures !== 'object') { - throw new TypeError('The measures argument needs to be an object'); - } - - const unitCache = buildUnitCache(measures, translate); - return new Converter(measures, unitCache); -} diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts deleted file mode 100644 index 7b447de453..0000000000 --- a/ui-ngx/src/app/core/services/unit/definitions/all.ts +++ /dev/null @@ -1,121 +0,0 @@ -/// -/// Copyright © 2016-2025 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 { TbMeasure } from '@shared/models/unit.models'; -import acceleration, { AccelerationUnits } from '@core/services/unit/definitions/acceleration'; -import angle, { AngleUnits } from '@core/services/unit/definitions/angle'; -import angularAcceleration, { AngularAccelerationUnits } from '@core/services/unit/definitions/angular-acceleration'; -import area, { AreaUnits } from '@core/services/unit/definitions/area'; -import charge, { ChargeUnits } from '@core/services/unit/definitions/charge'; -import digital, { DigitalUnits } from '@core/services/unit/definitions/digital'; -import electricCurrent, { ElectricCurrentUnits } from '@core/services/unit/definitions/electric-current'; -import energy, { EnergyUnits } from '@core/services/unit/definitions/energy'; -import force, { ForceUnits } from '@core/services/unit/definitions/force'; -import frequency, { FrequencyUnits } from '@core/services/unit/definitions/frequency'; -import illuminance,{ IlluminanceUnits } from '@core/services/unit/definitions/illuminance'; -import length, { LengthUnits } from '@core/services/unit/definitions/length'; -import mass, { MassUnits } from '@core/services/unit/definitions/mass'; -import partsPer, { PartsPerUnits } from '@core/services/unit/definitions/parts-per'; -import power, { PowerUnits } from '@core/services/unit/definitions/power'; -import pressure, { PressureUnits } from '@core/services/unit/definitions/pressure'; -import speed, { SpeedUnits } from '@core/services/unit/definitions/speed'; -import temperature, { TemperatureUnits } from './temperature'; -import time, { TimeUnits } from './time'; -import torque, { TorqueUnits } from '@core/services/unit/definitions/torque'; -import voltage, { VoltageUnits } from '@core/services/unit/definitions/voltage'; -import volume, { VolumeUnits } from '@core/services/unit/definitions/volume'; -import volumeFlowRate, { VolumeFlowRateUnits } from '@core/services/unit/definitions/volume-flow-rate'; - -export type AllMeasuresUnits = - | AccelerationUnits - | AngleUnits - | AngularAccelerationUnits - | AreaUnits - | ChargeUnits - | DigitalUnits - | ElectricCurrentUnits - | EnergyUnits - | ForceUnits - | FrequencyUnits - | IlluminanceUnits - | LengthUnits - | MassUnits - | PartsPerUnits - | PowerUnits - | PressureUnits - | SpeedUnits - | TemperatureUnits - | TimeUnits - | TorqueUnits - | VoltageUnits - | VolumeUnits - | VolumeFlowRateUnits; - -export type AllMeasures = - | 'acceleration' - | 'angle' - | 'angular-acceleration' - | 'area' - | 'charge' - | 'digital' - | 'electric-current' - | 'energy' - | 'force' - | 'frequency' - | 'illuminance' - | 'length' - | 'mass' - | 'parts-per' - | 'power' - | 'pressure' - | 'speed' - | 'temperature' - | 'time' - | 'torque' - | 'voltage' - | 'volume' - | 'volume-flow-rate'; - -const allMeasures: Record< - AllMeasures, - TbMeasure -> = { - acceleration, - angle, - 'angular-acceleration': angularAcceleration, - area, - charge, - digital, - 'electric-current': electricCurrent, - energy, - force, - frequency, - illuminance, - length, - mass, - 'parts-per': partsPer, - power, - pressure, - speed, - temperature, - time, - torque, - voltage, - volume, - 'volume-flow-rate': volumeFlowRate, -}; - -export default allMeasures; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts index 55abf8dad0..ff54db2fc3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts @@ -33,14 +33,14 @@ import { ColorProcessor, ComponentStyle, DateFormatProcessor, - FormatValueProcessor, getDataKey, getLabel, getSingleTsValue, iconStyle, overlayStyle, resolveCssSize, - textStyle + textStyle, + ValueFormatProcessor } from '@shared/models/widget-settings.models'; import { valueCardDefaultSettings, ValueCardLayout, ValueCardWidgetSettings } from './value-card-widget.models'; import { WidgetComponent } from '@home/components/widget/widget.component'; @@ -101,7 +101,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro private panelResize$: ResizeObserver; private horizontal = false; - private formatValue: FormatValueProcessor; + private valueFormat: ValueFormatProcessor; constructor(private imagePipe: ImagePipe, private sanitizer: DomSanitizer, @@ -125,7 +125,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro if (dataKey?.units) { units = dataKey.units; } - this.formatValue = FormatValueProcessor.fromSettings(this.ctx.$injector, {units: units, dec: decimals}); + this.valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, {units: units, dec: decimals}); this.layout = this.settings.layout; @@ -188,7 +188,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro if (tsValue && isDefinedAndNotNull(tsValue[1]) && tsValue[0] !== 0) { ts = tsValue[0]; value = tsValue[1]; - this.valueText = this.formatValue.format(value); // formatValue(value, this.decimals, this.units, false); + this.valueText = this.valueFormat.update(value); // formatValue(value, this.decimals, this.units, false); } else { this.valueText = 'N/A'; } diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index 481e070b90..94c09bb3d3 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -51,7 +51,7 @@ import { TenantProfileService } from '@core/http/tenant-profile.service'; import { UiSettingsService } from '@core/http/ui-settings.service'; import { UsageInfoService } from '@core/http/usage-info.service'; import { EventService } from '@core/http/event.service'; -import { UnitService } from '@core/services/unit/unit.service'; +import { UnitService } from '@core/services/unit.service'; import { AuditLogService } from '@core/http/audit-log.service'; export const ServicesMap = new Map>( diff --git a/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts b/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts index ebac40571e..7d1aa2aa4e 100644 --- a/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts +++ b/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts @@ -32,7 +32,7 @@ import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { AuthService } from '@core/auth/auth.service'; import { UnitSystem, UnitSystems } from '@shared/models/unit.models'; -import { UnitService } from '@core/services/unit/unit.service'; +import { UnitService } from '@core/services/unit.service'; @Component({ selector: 'tb-profile', diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts index e8afedfffa..624bd9ec33 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts @@ -15,12 +15,11 @@ /// import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { TbUnit, UnitDescription, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures, TbUnit, UnitInfo, UnitSystem } from '@shared/models/unit.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { FormBuilder, Validators } from '@angular/forms'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { UnitService } from '@core/services/unit/unit.service'; -import { AllMeasures } from '@core/services/unit/definitions/all'; +import { UnitService } from '@core/services/unit.service'; import { debounceTime, first } from 'rxjs/operators'; import { isEmptyStr } from '@core/utils'; import type { UnitInputComponent } from '@shared/components/unit-input.component'; @@ -69,7 +68,7 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { debounceTime(200), takeUntilDestroyed() ).subscribe(unit => { - const unitDescription = this.unitService.getUnitDescription(unit); + const unitDescription = this.unitService.getUnitInfo(unit); if (unitDescription) { this.convertUnitForm.get('convertUnit').enable({emitEvent: true}); this.measure = unitDescription.measure; @@ -109,7 +108,7 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { } ngOnInit() { - let unitDescription: UnitDescription; + let unitDescription: UnitInfo; if (this.required) { this.convertUnitForm.get('from').setValidators(Validators.required); this.convertUnitForm.get('from').updateValueAndValidity({emitEvent: false}); @@ -117,13 +116,13 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { if (typeof this.unit === 'string') { this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); this.convertUnitForm.get('from').setValue(this.unit, {emitEvent: true}); - unitDescription = this.unitService.getUnitDescription(this.unit); + unitDescription = this.unitService.getUnitInfo(this.unit); } else if (this.unit === null) { this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); this.convertUnitForm.get('from').setValue(null, {emitEvent: true}); } else { this.convertUnitForm.patchValue(this.unit, {emitEvent: false}); - unitDescription = this.unitService.getUnitDescription(this.unit.from); + unitDescription = this.unitService.getUnitInfo(this.unit.from); } if (unitDescription?.measure) { diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index 9cd044d3dd..9b582d1266 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -31,10 +31,9 @@ import { } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormControl, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Observable, of, shareReplay } from 'rxjs'; -import { searchUnits, TbUnit, UnitDescription, UnitsType, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures, searchUnits, TbUnit, UnitInfo, UnitsType, UnitSystem } from '@shared/models/unit.models'; import { map, mergeMap } from 'rxjs/operators'; -import { AllMeasures } from '@core/services/unit/definitions/all'; -import { UnitService } from '@core/services/unit/unit.service'; +import { UnitService } from '@core/services/unit.service'; import { TbPopoverService } from '@shared/components/popover.service'; import { ConvertUnitSettingsPanelComponent } from '@shared/components/convert-unit-settings-panel.component'; import { isNotEmptyStr, isObject } from '@core/utils'; @@ -57,7 +56,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang @HostBinding('style.display') readonly hostDisplay = 'flex'; @ViewChild('unitInput', {static: true}) unitInput: ElementRef; - unitsFormControl: FormControl; + unitsFormControl: FormControl; @Input({transform: booleanAttribute}) disabled: boolean; @@ -77,7 +76,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang @Input({transform: booleanAttribute}) allowConverted = false; - filteredUnits: Observable]>>; + filteredUnits: Observable]>>; searchText = ''; @@ -87,7 +86,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang private modelValue: TbUnit | null; - private fetchUnits$: Observable]>> = null; + private fetchUnits$: Observable]>> = null; private propagateChange = (_val: any) => {}; @@ -100,7 +99,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } ngOnInit() { - this.unitsFormControl = this.fb.control('', this.required ? [Validators.required] : []); + this.unitsFormControl = this.fb.control('', this.required ? [Validators.required] : []); this.filteredUnits = this.unitsFormControl.valueChanges .pipe( map(value => { @@ -127,7 +126,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang this.searchText = ''; this.modelValue = symbol; if (typeof symbol === 'string') { - this.unitsFormControl.patchValue(this.unitService.getUnitDescription(symbol) ?? symbol, {emitEvent: false}); + this.unitsFormControl.patchValue(this.unitService.getUnitInfo(symbol) ?? symbol, {emitEvent: false}); this.isUnitMapping = false; } else { this.unitsFormControl.patchValue(symbol, {emitEvent: false}); @@ -143,7 +142,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } } - displayUnitFn(unit?: TbUnit | UnitDescription): string | undefined { + displayUnitFn(unit?: TbUnit | UnitInfo): string | undefined { if (unit) { return this.getUnitSymbol(unit); } @@ -210,7 +209,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } } - private updateView(value: UnitDescription | TbUnit ) { + private updateView(value: UnitInfo | TbUnit ) { const res = this.getTbUnit(value); if (this.modelValue !== res) { this.modelValue = res; @@ -219,22 +218,22 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } } - private fetchUnits(searchText?: string): Observable]>> { + private fetchUnits(searchText?: string): Observable]>> { this.searchText = searchText; return this.unitsConstant().pipe( map(unit => this.searchUnit(unit, searchText)) ); } - private unitsConstant(): Observable]>> { + private unitsConstant(): Observable]>> { if (this.fetchUnits$ === null) { - this.fetchUnits$ = of(this.unitService.getUnitsGroupByMeasure(this.measure, this.unitSystem)).pipe( + this.fetchUnits$ = of(this.unitService.getUnitsGroupedByMeasure(this.measure, this.unitSystem)).pipe( map(data => { - let objectData = Object.entries(data) as Array<[AllMeasures, UnitDescription[]]>; + let objectData = Object.entries(data) as Array<[AllMeasures, UnitInfo[]]>; if (this.tagFilter) { objectData = objectData - .map((measure) => [measure[0], measure[1].filter(u => u.tags.includes(this.tagFilter))] as [AllMeasures, UnitDescription[]]) + .map((measure) => [measure[0], measure[1].filter(u => u.tags.includes(this.tagFilter))] as [AllMeasures, UnitInfo[]]) .filter((measure) => measure[1].length > 0); } return objectData; @@ -245,17 +244,17 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang return this.fetchUnits$; } - private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { + private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { if (isNotEmptyStr(searchText)) { const filterValue = searchText.trim().toUpperCase() return units - .map(measure => [measure[0], searchUnits(measure[1], filterValue)] as [AllMeasures, UnitDescription[]]) + .map(measure => [measure[0], searchUnits(measure[1], filterValue)] as [AllMeasures, UnitInfo[]]) .filter((measure) => measure[1].length > 0); } return units; } - private getUnitSymbol(value: TbUnit | UnitDescription | null): string { + private getUnitSymbol(value: TbUnit | UnitInfo | null): string { if (value === null) { return ''; } @@ -268,7 +267,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang return value.from; } - private getTbUnit(value: TbUnit | UnitDescription | null): TbUnit { + private getTbUnit(value: TbUnit | UnitInfo | null): TbUnit { if (value === null) { return null; } diff --git a/ui-ngx/src/app/shared/models/public-api.ts b/ui-ngx/src/app/shared/models/public-api.ts index 53e4bb286e..fbee1ec6bb 100644 --- a/ui-ngx/src/app/shared/models/public-api.ts +++ b/ui-ngx/src/app/shared/models/public-api.ts @@ -54,6 +54,7 @@ export * from './rule-node.models'; export * from './settings.models'; export * from './tenant.model'; export * from './user.model'; +export * from './unit.models'; export * from './user-settings.models'; export * from './widget-settings.models'; export * from './widget.models'; diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 01f88c3406..4f73fc4fbc 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -14,16 +14,118 @@ /// limitations under the License. /// -import { AllMeasures } from '@core/services/unit/definitions/all'; +import acceleration, { AccelerationUnits } from '@shared/models/units/acceleration'; +import angle, { AngleUnits } from '@shared/models/units/angle'; +import angularAcceleration, { AngularAccelerationUnits } from '@shared/models/units/angular-acceleration'; +import area, { AreaUnits } from '@shared/models/units/area'; +import charge, { ChargeUnits } from '@shared/models/units/charge'; +import digital, { DigitalUnits } from '@shared/models/units/digital'; +import electricCurrent, { ElectricCurrentUnits } from '@shared/models/units/electric-current'; +import energy, { EnergyUnits } from '@shared/models/units/energy'; +import force, { ForceUnits } from '@shared/models/units/force'; +import frequency, { FrequencyUnits } from '@shared/models/units/frequency'; +import illuminance, { IlluminanceUnits } from '@shared/models/units/illuminance'; +import length, { LengthUnits } from '@shared/models/units/length'; +import mass, { MassUnits } from '@shared/models/units/mass'; +import partsPer, { PartsPerUnits } from '@shared/models/units/parts-per'; +import power, { PowerUnits } from '@shared/models/units/power'; +import pressure, { PressureUnits } from '@shared/models/units/pressure'; +import speed, { SpeedUnits } from '@shared/models/units/speed'; +import temperature, { TemperatureUnits } from '@shared/models/units/temperature'; +import time, { TimeUnits } from '@shared/models/units/time'; +import torque, { TorqueUnits } from '@shared/models/units/torque'; +import voltage, { VoltageUnits } from '@shared/models/units/voltage'; +import volume, { VolumeUnits } from '@shared/models/units/volume'; +import volumeFlowRate, { VolumeFlowRateUnits } from '@shared/models/units/volume-flow-rate'; +import { TranslateService } from '@ngx-translate/core'; + +export type AllMeasuresUnits = + | AccelerationUnits + | AngleUnits + | AngularAccelerationUnits + | AreaUnits + | ChargeUnits + | DigitalUnits + | ElectricCurrentUnits + | EnergyUnits + | ForceUnits + | FrequencyUnits + | IlluminanceUnits + | LengthUnits + | MassUnits + | PartsPerUnits + | PowerUnits + | PressureUnits + | SpeedUnits + | TemperatureUnits + | TimeUnits + | TorqueUnits + | VoltageUnits + | VolumeUnits + | VolumeFlowRateUnits; + +export type AllMeasures = + | 'acceleration' + | 'angle' + | 'angular-acceleration' + | 'area' + | 'charge' + | 'digital' + | 'electric-current' + | 'energy' + | 'force' + | 'frequency' + | 'illuminance' + | 'length' + | 'mass' + | 'parts-per' + | 'power' + | 'pressure' + | 'speed' + | 'temperature' + | 'time' + | 'torque' + | 'voltage' + | 'volume' + | 'volume-flow-rate'; + +const allMeasures: Record< + AllMeasures, + TbMeasure +> = Object.freeze({ + acceleration, + angle, + 'angular-acceleration': angularAcceleration, + area, + charge, + digital, + 'electric-current': electricCurrent, + energy, + force, + frequency, + illuminance, + length, + mass, + 'parts-per': partsPer, + power, + pressure, + speed, + temperature, + time, + torque, + voltage, + volume, + 'volume-flow-rate': volumeFlowRate, +}); export enum UnitsType { capacity = 'capacity' } -export type TbUnitConvertor = (value: number) => number; -export type UnitDescriptionGroupByMeasure = Partial>; +export type TbUnitConverter = (value: number) => number; +export type UnitInfoGroupByMeasure = Partial>; -export interface UnitDescription { +export interface UnitInfo { abbr: string; measure: AllMeasures; system: UnitSystem; @@ -70,21 +172,247 @@ export interface Conversion { unit: Unit; } -export type UnitCache = Map< - string, - { +export type UnitCache = Map; -const searchUnitTags = (unit: UnitDescription, searchText: string): boolean => +const searchUnitTags = (unit: UnitInfo, searchText: string): boolean => !!unit.tags.find(t => t.toUpperCase().includes(searchText)); -export const searchUnits = (_units: Array, searchText: string): Array => _units.filter( +export const searchUnits = (_units: Array, searchText: string): Array => _units.filter( u => u.abbr.toUpperCase().includes(searchText) || u.name.toUpperCase().includes(searchText) || searchUnitTags(u, searchText) ); + +type Entries = [S, T[keyof T]]; + +export class Converter { + private readonly measureData: Record>; + private unitCache: Map< + string, + { + system: UnitSystem; + measure: AllMeasures; + unit: Unit; + abbr: AllMeasuresUnits; + } + >; + + constructor( + measures: Record>, + unitCache: UnitCache + ) { + this.measureData = measures; + this.unitCache = unitCache; + } + + getUnitConverter(from: AllMeasuresUnits | string, to: AllMeasuresUnits | string): TbUnitConverter { + return (value: number) => this.convert(value, from, to); + } + + convert(value: number, from: AllMeasuresUnits | string, to: AllMeasuresUnits | string): number { + const origin = this.getUnit(from); + const destination = this.getUnit(to); + + if (!origin) { + throw new Error(`Unsupported unit: ${from}`); + } + if (!destination) { + throw new Error(`Unsupported unit: ${to}`); + } + if (origin.abbr === destination.abbr) { + return value; + } + if (destination.measure !== origin.measure) { + throw Error(`Cannot convert incompatible measures: ${origin.measure} to ${destination.measure}`); + } + let result = value * origin.unit.to_anchor; + if (origin.unit.anchor_shift) { + result -= origin.unit.anchor_shift; + } + if (origin.system !== destination.system) { + const measureUnits = this.measureData[origin.measure][origin.system]; + const transform = measureUnits?.transform; + const ratio = measureUnits?.ratio; + if (typeof transform === 'function') { + result = transform(result); + } else if (typeof ratio === 'number') { + result *= ratio; + } else { + throw Error('System anchor requires a defined ratio or transform function'); + } + } + + if (destination.unit.anchor_shift) { + result += destination.unit.anchor_shift; + } + return result / destination.unit.to_anchor; + } + + getDefaultUnit(measureName: AllMeasures | (string & {}), unitSystem: UnitSystem): AllMeasuresUnits { + if (!this.isMeasure(measureName)) { + return null; + } + const units = this.getUnitsForMeasure(measureName, unitSystem); + if (!units) { + return null; + } + for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { + if (unit.to_anchor === 1 && (!unit.anchor_shift || unit.anchor_shift === 0)) { + return abbr; + } + } + return null; + } + + getUnit(abbr: AllMeasuresUnits | string): Conversion | null { + return this.unitCache.get(abbr) ?? null; + } + + describe(abbr: AllMeasuresUnits | string): UnitInfo { + const unit = this.getUnit(abbr); + return unit ? this.describeUnit(unit) : null; + } + + listUnits(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfo[] { + const results: UnitInfo[] = []; + + const measures = measureName + ? { [measureName]: this.measureData[measureName] } as Record> + : this.measureData; + + for (const [name, measure] of Object.entries(measures) as [AllMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; + } + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; + } + + for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { + results.push( + this.describeUnit({ + abbr, + measure: name as AllMeasures, + system, + unit, + }) + ); + } + } + } + return results; + } + + unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure | never { + const results: UnitInfoGroupByMeasure = {}; + + const measures = measureName + ? { [measureName]: this.measureData[measureName]} as Record> + : this.measureData; + + for (const [name, measure] of Object.entries(measures) as [AllMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; + } + + results[name] = []; + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; + } + + for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { + results[name].push( + this.describeUnit({ + abbr, + measure: name as AllMeasures, + system, + unit, + }) + ); + } + } + } + return results; + } + + private describeUnit(unit: Conversion): UnitInfo { + return { + abbr: unit.abbr, + measure: unit.measure, + system: unit.system, + name: unit.unit.name, + tags: unit.unit.tags + }; + } + + private isMeasure(measureName: string): boolean { + return measureName in this.measureData; + } + + private getUnitsForMeasure( + measureName: AllMeasures | string, + unitSystem: UnitSystem + ): Partial> | null { + const measure = this.measureData[measureName]; + let system = unitSystem; + let units = measure[system]?.units; + if (!units && unitSystem === UnitSystem.IMPERIAL) { + system = UnitSystem.METRIC; + units = measure[system]?.units; + } + return units ?? null; + } +} + +function buildUnitCache(measures: Record>, + translate: TranslateService +) { + const unitCache: UnitCache = new Map(); + for (const [measureName, measure] of Object.entries(measures) as Entries< + typeof measures, + AllMeasures + >[]) { + for (const [systemName, system] of Object.entries( + measure + ) as Entries, UnitSystem>[]) { + for (const [testAbbr, unit] of Object.entries(system.units) as Entries< + Record, + AllMeasuresUnits + >[]) { + unit.name = translate.instant(unit.name); + unitCache.set(testAbbr, { + measure: measureName, + system: systemName, + abbr: testAbbr, + unit, + }); + } + } + } + return unitCache; +} + +export function getUnitConverter(translate: TranslateService): Converter { + const unitCache = buildUnitCache(allMeasures, translate); + return new Converter(allMeasures, unitCache); +} diff --git a/ui-ngx/src/app/core/services/unit/definitions/acceleration.ts b/ui-ngx/src/app/shared/models/units/acceleration.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/acceleration.ts rename to ui-ngx/src/app/shared/models/units/acceleration.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/angle.ts b/ui-ngx/src/app/shared/models/units/angle.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/angle.ts rename to ui-ngx/src/app/shared/models/units/angle.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/angular-acceleration.ts b/ui-ngx/src/app/shared/models/units/angular-acceleration.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/angular-acceleration.ts rename to ui-ngx/src/app/shared/models/units/angular-acceleration.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/area.ts b/ui-ngx/src/app/shared/models/units/area.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/area.ts rename to ui-ngx/src/app/shared/models/units/area.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/charge.ts b/ui-ngx/src/app/shared/models/units/charge.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/charge.ts rename to ui-ngx/src/app/shared/models/units/charge.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/digital.ts b/ui-ngx/src/app/shared/models/units/digital.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/digital.ts rename to ui-ngx/src/app/shared/models/units/digital.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts b/ui-ngx/src/app/shared/models/units/electric-current.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/electric-current.ts rename to ui-ngx/src/app/shared/models/units/electric-current.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/energy.ts b/ui-ngx/src/app/shared/models/units/energy.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/energy.ts rename to ui-ngx/src/app/shared/models/units/energy.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/force.ts b/ui-ngx/src/app/shared/models/units/force.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/force.ts rename to ui-ngx/src/app/shared/models/units/force.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/frequency.ts b/ui-ngx/src/app/shared/models/units/frequency.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/frequency.ts rename to ui-ngx/src/app/shared/models/units/frequency.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/illuminance.ts b/ui-ngx/src/app/shared/models/units/illuminance.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/illuminance.ts rename to ui-ngx/src/app/shared/models/units/illuminance.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/length.ts b/ui-ngx/src/app/shared/models/units/length.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/length.ts rename to ui-ngx/src/app/shared/models/units/length.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/mass.ts b/ui-ngx/src/app/shared/models/units/mass.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/mass.ts rename to ui-ngx/src/app/shared/models/units/mass.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/parts-per.ts b/ui-ngx/src/app/shared/models/units/parts-per.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/parts-per.ts rename to ui-ngx/src/app/shared/models/units/parts-per.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/power.ts b/ui-ngx/src/app/shared/models/units/power.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/power.ts rename to ui-ngx/src/app/shared/models/units/power.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/pressure.ts b/ui-ngx/src/app/shared/models/units/pressure.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/pressure.ts rename to ui-ngx/src/app/shared/models/units/pressure.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/speed.ts b/ui-ngx/src/app/shared/models/units/speed.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/speed.ts rename to ui-ngx/src/app/shared/models/units/speed.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/temperature.ts b/ui-ngx/src/app/shared/models/units/temperature.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/temperature.ts rename to ui-ngx/src/app/shared/models/units/temperature.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/time.ts b/ui-ngx/src/app/shared/models/units/time.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/time.ts rename to ui-ngx/src/app/shared/models/units/time.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/torque.ts b/ui-ngx/src/app/shared/models/units/torque.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/torque.ts rename to ui-ngx/src/app/shared/models/units/torque.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/voltage.ts b/ui-ngx/src/app/shared/models/units/voltage.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/voltage.ts rename to ui-ngx/src/app/shared/models/units/voltage.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts b/ui-ngx/src/app/shared/models/units/volume-flow-rate.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts rename to ui-ngx/src/app/shared/models/units/volume-flow-rate.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/volume.ts b/ui-ngx/src/app/shared/models/units/volume.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/volume.ts rename to ui-ngx/src/app/shared/models/units/volume.ts diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index 89c2824545..d6f46b0815 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -53,8 +53,8 @@ import { WidgetSubscriptionCallbacks, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { UnitService } from '@core/services/unit/unit.service'; -import { TbUnit, TbUnitConvertor, TbUnitMapping } from '@shared/models/unit.models'; +import { UnitService } from '@core/services/unit.service'; +import { TbUnit, TbUnitConverter, TbUnitMapping } from '@shared/models/unit.models'; export type ComponentStyle = {[klass: string]: any}; @@ -862,50 +862,50 @@ export class AutoDateFormatProcessor extends DateFormatProcessor { } } -export interface FormatValueSettingProcessor { +export interface ValueFormatSettingProcessor { dec?: number; units?: TbUnit; showZeroDecimals?: boolean; } -export abstract class FormatValueProcessor { +export abstract class ValueFormatProcessor { - static fromSettings($injector: Injector, settings: FormatValueSettingProcessor): FormatValueProcessor { - if (typeof settings.units !== 'string' && isDefinedAndNotNull(settings.units?.from)) { - return new ConvertUnitProcessor($injector, settings) + static fromSettings($injector: Injector, settings: ValueFormatSettingProcessor): ValueFormatProcessor { + if (settings.units !== null && typeof settings.units === 'object') { + return new ConverterValueFormatProcessor($injector, settings) } else { - return new SimpleUnitProcessor($injector, settings); + return new SimpleValueFormatProcessor($injector, settings); } } protected constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { + protected settings: ValueFormatSettingProcessor) { } - abstract format(value: any): string; + abstract update(value: any): string; } -export class SimpleUnitProcessor extends FormatValueProcessor { +export class SimpleValueFormatProcessor extends ValueFormatProcessor { private readonly isDefinedUnit: boolean; private readonly isDefinedDec: boolean; - private readonly showZeroDecimals: boolean; + private readonly hideZeroDecimals: boolean; constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { + protected settings: ValueFormatSettingProcessor) { super($injector, settings); this.isDefinedUnit = isNotEmptyStr(settings.units); this.isDefinedDec = isDefinedAndNotNull(settings.dec); - this.showZeroDecimals = !!settings.showZeroDecimals; + this.hideZeroDecimals = !settings.showZeroDecimals; } - format(value: any): string { + update(value: any): string { if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDec || this.isDefinedUnit || Number(value).toString() === value)) { let formatted = value; if (this.isDefinedDec) { formatted = Number(formatted).toFixed(this.settings.dec); } - if (!this.showZeroDecimals) { + if (this.hideZeroDecimals) { formatted = Number(formatted) } formatted = formatted.toString(); @@ -918,39 +918,37 @@ export class SimpleUnitProcessor extends FormatValueProcessor { } } -export class ConvertUnitProcessor extends FormatValueProcessor { +export class ConverterValueFormatProcessor extends ValueFormatProcessor { private readonly isDefinedDec: boolean; - private readonly showZeroDecimals: boolean; - private readonly unitConvertor: TbUnitConvertor; + private readonly hideZeroDecimals: boolean; + private readonly unitConverter: TbUnitConverter; private readonly unitAbbr: string; constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { + protected settings: ValueFormatSettingProcessor) { super($injector, settings); const unitService = this.$injector.get(UnitService); - const userUnitSystem = unitService.getUnitSystem(); const unit = settings.units as TbUnitMapping; - const fromUnit = unit.from; - this.unitAbbr = isNotEmptyStr(unit[userUnitSystem]) ? unit[userUnitSystem] : fromUnit; + this.unitAbbr = unitService.getTargetUnitSymbol(unit); try { - this.unitConvertor = unitService.geUnitConvertor(fromUnit, this.unitAbbr); + this.unitConverter = unitService.geUnitConverter(unit); } catch (e) {/**/} this.isDefinedDec = isDefinedAndNotNull(settings.dec); - this.showZeroDecimals = !!settings.showZeroDecimals; + this.hideZeroDecimals = !settings.showZeroDecimals; } - format(value: any): string { + update(value: any): string { if (isDefinedAndNotNull(value) && isNumeric(value)) { let formatted: number | string = Number(value); - if (this.unitConvertor) { - formatted = this.unitConvertor(value); + if (this.unitConverter) { + formatted = this.unitConverter(value); } if (this.isDefinedDec) { formatted = Number(formatted).toFixed(this.settings.dec); } - if (!this.showZeroDecimals) { + if (this.hideZeroDecimals) { formatted = Number(formatted) } formatted = formatted.toString(); From f4cd471082f864c8ad7f8a1d3c789859d5843d58 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 2 May 2025 12:26:23 +0300 Subject: [PATCH 118/335] Notification on job finish --- .../entitiy/EntityStateSourcingListener.java | 2 +- .../server/service/job/DefaultJobManager.java | 65 +++++++++++++++---- .../server/service/job/JobProcessor.java | 2 + .../server/service/job/JobManagerTest.java | 33 +++++++++- .../common/data/job/DummyJobResult.java | 8 +++ .../server/common/data/job/JobResult.java | 3 + .../server/common/data/job/JobType.java | 9 ++- .../server/dao/job/DefaultJobService.java | 4 +- 8 files changed, 108 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index f70354c3a8..f56cc3e952 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -304,7 +304,7 @@ public class EntityStateSourcingListener { private void onJobUpdate(Job job) { jobManager.onJobUpdate(job); - if (job.getResult().getCancellationTs() > 0 || job.getStatus().isOneOf(JobStatus.FAILED)) { + if (job.getResult().getCancellationTs() > 0 || (job.getStatus().isOneOf(JobStatus.FAILED) && job.getResult().getGeneralError() != null)) { // task processors will add this job to the list of discarded tbClusterService.broadcastEntityStateChangeEvent(job.getTenantId(), job.getId(), ComponentLifecycleEvent.STOPPED); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 3d601a9358..3da2e2a9ab 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -18,12 +18,12 @@ package org.thingsboard.server.service.job; import jakarta.annotation.PreDestroy; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; @@ -33,8 +33,12 @@ import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.Task; import org.thingsboard.server.common.data.job.task.TaskResult; +import org.thingsboard.server.common.data.notification.info.GeneralNotificationInfo; +import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.job.JobService; +import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.queue.TbQueueCallback; @@ -65,6 +69,7 @@ public class DefaultJobManager implements JobManager { private final JobService jobService; private final JobStatsService jobStatsService; + private final NotificationCenter notificationCenter; private final Map jobProcessors; private final Map>> taskProducers; private final QueueConsumerManager> jobStatsConsumer; @@ -74,9 +79,11 @@ public class DefaultJobManager implements JobManager { @Value("${queue.tasks.stats.processing_interval_ms:5000}") private int statsProcessingInterval; - public DefaultJobManager(JobService jobService, JobStatsService jobStatsService, TbCoreQueueFactory queueFactory, List jobProcessors) { + public DefaultJobManager(JobService jobService, JobStatsService jobStatsService, NotificationCenter notificationCenter, + TbCoreQueueFactory queueFactory, List jobProcessors) { this.jobService = jobService; this.jobStatsService = jobStatsService; + this.notificationCenter = notificationCenter; this.jobProcessors = jobProcessors.stream().collect(Collectors.toMap(JobProcessor::getType, Function.identity())); this.taskProducers = Arrays.stream(JobType.values()).collect(Collectors.toMap(Function.identity(), queueFactory::createTaskProducer)); this.executor = ThingsBoardExecutors.newWorkStealingPool(Math.max(4, Runtime.getRuntime().availableProcessors()), getClass()); @@ -104,10 +111,29 @@ public class DefaultJobManager implements JobManager { @Override public void onJobUpdate(Job job) { - if (job.getStatus() == JobStatus.PENDING) { - executor.execute(() -> { - processJob(job); - }); + JobStatus status = job.getStatus(); + switch (status) { + case PENDING -> { + executor.execute(() -> { + try { + processJob(job); + } catch (Throwable e) { + log.error("Failed to process job update: {}", job, e); + } + }); + } + case COMPLETED, FAILED -> { + executor.execute(() -> { + try { + if (status == JobStatus.COMPLETED) { + getJobProcessor(job.getType()).onJobCompleted(job); + } + sendJobFinishedNotification(job); + } catch (Throwable e) { + log.error("Failed to process job update: {}", job, e); + } + }); + } } } @@ -115,7 +141,7 @@ public class DefaultJobManager implements JobManager { TenantId tenantId = job.getTenantId(); JobId jobId = job.getId(); try { - JobProcessor processor = jobProcessors.get(job.getType()); + JobProcessor processor = getJobProcessor(job.getType()); List toReprocess = job.getConfiguration().getToReprocess(); if (toReprocess == null) { int tasksCount = processor.process(job, this::submitTask); // todo: think about stopping tb - while tasks are being submitted @@ -127,11 +153,7 @@ public class DefaultJobManager implements JobManager { } } catch (Throwable e) { log.error("[{}][{}][{}] Failed to submit tasks", tenantId, jobId, job.getType(), e); - try { - jobService.markAsFailed(tenantId, jobId, ExceptionUtils.getStackTrace(e)); - } catch (Throwable e2) { - log.error("[{}][{}] Failed to mark job as failed", tenantId, jobId, e2); - } + jobService.markAsFailed(tenantId, jobId, e.getMessage()); } } @@ -224,6 +246,25 @@ public class DefaultJobManager implements JobManager { Thread.sleep(statsProcessingInterval); // todo: test with bigger interval } + private void sendJobFinishedNotification(Job job) { + NotificationTemplate template = DefaultNotifications.DefaultNotification.builder() + .name("Job finished") + .subject("${type} ${status}") + .text("${description} ${status}: ${result}") + .build().toTemplate(); + GeneralNotificationInfo info = new GeneralNotificationInfo(Map.of( + "type", job.getType().getTitle(), + "description", job.getDescription(), + "status", job.getStatus().name().toLowerCase(), + "result", job.getResult().getDescription() + )); + notificationCenter.sendGeneralWebNotification(job.getTenantId(), new TenantAdministratorsFilter(), template, info); + } + + private JobProcessor getJobProcessor(JobType jobType) { + return jobProcessors.get(jobType); + } + @PreDestroy private void destroy() { jobStatsConsumer.stop(); diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java index 301d4bf6eb..16ef5e404f 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java @@ -29,6 +29,8 @@ public interface JobProcessor { void reprocess(Job job, List taskFailures, Consumer> taskConsumer) throws Exception; + default void onJobCompleted(Job job) {} + JobType getType(); } diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index ee1eee6531..39c87dfd70 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.job; import org.assertj.core.api.Assertions; +import org.assertj.core.api.ThrowingConsumer; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.DummyTaskResult; import org.thingsboard.server.common.data.job.task.DummyTaskResult.DummyTaskFailure; +import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.job.JobService; @@ -85,7 +87,7 @@ public class JobManagerTest extends AbstractControllerTest { .tenantId(tenantId) .type(JobType.DUMMY) .key("test-job") - .description("test job") + .description("Test job") .configuration(DummyJobConfiguration.builder() .successfulTasksCount(tasksCount) .taskProcessingTimeMs(1000) @@ -105,6 +107,11 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(job.getResult().getResults()).isEmpty(); assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount); }); + + checkJobNotification(notification -> { + assertThat(notification.getSubject()).isEqualTo("Dummy job completed"); + assertThat(notification.getText()).isEqualTo("Test job completed: 5/5 successful, 0 failed"); + }); } @Test @@ -115,7 +122,7 @@ public class JobManagerTest extends AbstractControllerTest { .tenantId(tenantId) .type(JobType.DUMMY) .key("test-job") - .description("test job") + .description("Test job") .configuration(DummyJobConfiguration.builder() .successfulTasksCount(successfulTasks) .failedTasksCount(failedTasks) @@ -136,6 +143,11 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(((DummyTaskResult) jobResult.getResults().get(1)).getFailure().getError()).isEqualTo("error3"); // last error assertThat(jobResult.getCompletedCount()).isEqualTo(jobResult.getTotalCount()); }); + + checkJobNotification(notification -> { + assertThat(notification.getSubject()).isEqualTo("Dummy job failed"); + assertThat(notification.getText()).isEqualTo("Test job failed: 3/5 successful, 2 failed"); + }); } @Test @@ -311,7 +323,7 @@ public class JobManagerTest extends AbstractControllerTest { .tenantId(tenantId) .type(JobType.DUMMY) .key("test-job") - .description("test job") + .description("Test job") .configuration(DummyJobConfiguration.builder() .generalError("Some error while submitting tasks") .submittedTasksBeforeGeneralError(submittedTasks) @@ -326,6 +338,11 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(job.getResult().getDiscardedCount()).isBetween(1, submittedTasks); assertThat(job.getResult().getTotalCount()).isNull(); }); + + checkJobNotification(notification -> { + assertThat(notification.getSubject()).isEqualTo("Dummy job failed"); + assertThat(notification.getText()).isEqualTo("Test job failed: Some error while submitting tasks"); + }); } @Test @@ -426,6 +443,16 @@ public class JobManagerTest extends AbstractControllerTest { }); } + private void checkJobNotification(ThrowingConsumer assertFunction) { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Notification notification = getMyNotifications(true, 100).stream() + .findFirst().orElse(null); + assertThat(notification).isNotNull(); + + assertFunction.accept(notification); + }); + } + // todo: job with zero tasks } \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java index 031a733d51..3a9aabae76 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java @@ -17,6 +17,14 @@ package org.thingsboard.server.common.data.job; public class DummyJobResult extends JobResult { + @Override + public String getDescription() { + if (getGeneralError() != null) { + return getGeneralError(); + } + return getSuccessfulCount() + "/" + getTotalCount() + " successful, " + getFailedCount() + " failed"; + } + @Override public JobType getJobType() { return JobType.DUMMY; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index 4e4787bbe5..534e3587bb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -64,6 +64,9 @@ public abstract class JobResult implements Serializable { } } + @JsonIgnore + public abstract String getDescription(); + public abstract JobType getJobType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java index 9e8e9fa7e5..c2c461d12b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobType.java @@ -15,9 +15,16 @@ */ package org.thingsboard.server.common.data.job; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter public enum JobType { - DUMMY; + DUMMY("Dummy job"); + + private final String title; public String getTasksTopic() { return "tasks." + name().toLowerCase(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index 07b5aeadf9..73e0c5dff4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -117,7 +117,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi boolean publishEvent = false; for (TaskResult taskResult : jobStats.getTaskResults()) { - result.processTaskResult(taskResult); + result.processTaskResult(taskResult); if (result.getCancellationTs() > 0) { if (!taskResult.isDiscarded() && System.currentTimeMillis() > result.getCancellationTs()) { @@ -134,8 +134,10 @@ public class DefaultJobService extends AbstractEntityService implements JobServi job.setStatus(CANCELLED); } else if (result.getFailedCount() > 0) { job.setStatus(FAILED); + publishEvent = true; } else { job.setStatus(COMPLETED); + publishEvent = true; } } } From 03845450ef6508c2102beded1eb1cc030d6fce3d Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 2 May 2025 12:40:40 +0300 Subject: [PATCH 119/335] Truncate notification table on test finish --- .../org/thingsboard/server/controller/AbstractWebTest.java | 5 +++++ .../service/notification/AbstractNotificationApiTest.java | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index d580a6b12a..7a96384be5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -48,6 +48,7 @@ import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.mock.http.MockHttpInputMessage; import org.springframework.mock.http.MockHttpOutputMessage; import org.springframework.mock.web.MockMultipartFile; @@ -281,6 +282,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { @Autowired protected InMemoryStorage storage; + protected JdbcTemplate jdbcTemplate; + @MockBean protected CfRocksDb cfRocksDb; @@ -392,6 +395,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); + jdbcTemplate.execute("TRUNCATE TABLE notification"); + log.info("Executed web test teardown"); } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java index b7e4013bb9..f238a1bbc1 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java @@ -21,7 +21,6 @@ import org.junit.After; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.util.Pair; -import org.springframework.jdbc.core.JdbcTemplate; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.notification.SlackService; import org.thingsboard.server.common.data.User; @@ -89,8 +88,6 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest protected SqlPartitioningRepository partitioningRepository; @Autowired protected DefaultNotifications defaultNotifications; - @Autowired - private JdbcTemplate jdbcTemplate; public static final String DEFAULT_NOTIFICATION_SUBJECT = "Just a test"; public static final NotificationType DEFAULT_NOTIFICATION_TYPE = NotificationType.GENERAL; @@ -101,7 +98,6 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest notificationRuleService.deleteNotificationRulesByTenantId(TenantId.SYS_TENANT_ID); notificationTemplateService.deleteNotificationTemplatesByTenantId(TenantId.SYS_TENANT_ID); notificationTargetService.deleteNotificationTargetsByTenantId(TenantId.SYS_TENANT_ID); - jdbcTemplate.execute("TRUNCATE TABLE notification"); partitioningRepository.cleanupPartitionsCache("notification", Long.MAX_VALUE, 0); notificationSettingsService.deleteNotificationSettings(TenantId.SYS_TENANT_ID); } From d6aa53a858e90b66b2f5c9a050308f8a67a0cdc3 Mon Sep 17 00:00:00 2001 From: yuliaklochai Date: Fri, 2 May 2025 12:42:14 +0300 Subject: [PATCH 120/335] UI: added trendz settings tab --- ui-ngx/src/app/core/auth/auth.models.ts | 2 + ui-ngx/src/app/core/auth/auth.reducer.ts | 4 +- .../app/core/http/trendz-settings.service.ts | 39 ++++++++ ui-ngx/src/app/core/services/menu.models.ts | 19 +++- .../home/pages/admin/admin-routing.module.ts | 13 +++ .../modules/home/pages/admin/admin.module.ts | 4 +- .../admin/trendz-settings.component.html | 51 ++++++++++ .../admin/trendz-settings.component.scss | 36 +++++++ .../pages/admin/trendz-settings.component.ts | 95 +++++++++++++++++++ ui-ngx/src/app/shared/models/constants.ts | 1 + ui-ngx/src/app/shared/models/icon.models.ts | 14 ++- .../shared/models/trendz-settings.models.ts | 25 +++++ .../assets/locale/locale.constant-en_US.json | 6 +- 13 files changed, 302 insertions(+), 7 deletions(-) create mode 100644 ui-ngx/src/app/core/http/trendz-settings.service.ts create mode 100644 ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.scss create mode 100644 ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts create mode 100644 ui-ngx/src/app/shared/models/trendz-settings.models.ts diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts index e5cc1424ab..142d845cf4 100644 --- a/ui-ngx/src/app/core/auth/auth.models.ts +++ b/ui-ngx/src/app/core/auth/auth.models.ts @@ -16,6 +16,7 @@ import { AuthUser, User } from '@shared/models/user.model'; import { UserSettings } from '@shared/models/user-settings.models'; +import { TrendzSettings } from '@shared/models/trendz-settings.models'; export interface SysParamsState { userTokenAccessEnabled: boolean; @@ -32,6 +33,7 @@ export interface SysParamsState { maxArgumentsPerCF: number; ruleChainDebugPerTenantLimitsConfiguration?: string; calculatedFieldDebugPerTenantLimitsConfiguration?: string; + trendzSettings: TrendzSettings; } export interface SysParams extends SysParamsState { diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index 3ecf70074c..fde778284d 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -17,6 +17,7 @@ import { AuthPayload, AuthState } from './auth.models'; import { AuthActions, AuthActionTypes } from './auth.actions'; import { initialUserSettings, UserSettings } from '@shared/models/user-settings.models'; +import { initialTrendzSettings } from '@shared/models/trendz-settings.models'; import { unset } from '@core/utils'; const emptyUserAuthState: AuthPayload = { @@ -34,7 +35,8 @@ const emptyUserAuthState: AuthPayload = { maxArgumentsPerCF: 0, maxDataPointsPerRollingArg: 0, maxDebugModeDurationMinutes: 0, - userSettings: initialUserSettings + userSettings: initialUserSettings, + trendzSettings: initialTrendzSettings }; export const initialState: AuthState = { diff --git a/ui-ngx/src/app/core/http/trendz-settings.service.ts b/ui-ngx/src/app/core/http/trendz-settings.service.ts new file mode 100644 index 0000000000..4d965f920e --- /dev/null +++ b/ui-ngx/src/app/core/http/trendz-settings.service.ts @@ -0,0 +1,39 @@ +/// +/// Copyright © 2016-2025 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 { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { HttpClient } from '@angular/common/http'; +import { TrendzSettings } from '@shared/models/trendz-settings.models'; +import { defaultHttpOptionsFromConfig } from '@core/http/http-utils'; + +@Injectable({ + providedIn: 'root' +}) +export class TrendzSettingsService { + + constructor( + private http: HttpClient + ) {} + + public getTrendzSettings(): Observable { + return this.http.get(`/api/trendz/settings`, defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true})) + } + + public saveTrendzSettings(trendzSettings: TrendzSettings): Observable { + return this.http.post(`/api/trendz/settings`, trendzSettings, defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true})) + } +} diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index 4775a3a771..607c5c6dff 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -104,7 +104,8 @@ export enum MenuId { features = 'features', otaUpdates = 'otaUpdates', version_control = 'version_control', - api_usage = 'api_usage' + api_usage = 'api_usage', + trendz_settings = 'trendz_settings' } declare type MenuFilter = (authState: AuthState) => boolean; @@ -684,6 +685,17 @@ export const menuSectionMap = new Map([ path: '/usage', icon: 'insert_chart' } + ], + [ + MenuId.trendz_settings, + { + id: MenuId.trendz_settings, + name: 'admin.trendz', + fullName: 'admin.trendz-settings', + type: 'link', + path: '/settings/trendz', + icon: 'trendz-settings' + } ] ]); @@ -843,7 +855,8 @@ const defaultUserMenuMap = new Map([ {id: MenuId.home_settings}, {id: MenuId.notification_settings}, {id: MenuId.repository_settings}, - {id: MenuId.auto_commit_settings} + {id: MenuId.auto_commit_settings}, + {id: MenuId.trendz_settings} ] }, { @@ -946,7 +959,7 @@ const defaultHomeSectionMap = new Map([ }, { name: 'admin.system-settings', - places: [MenuId.home_settings, MenuId.resources_library, MenuId.repository_settings, MenuId.auto_commit_settings] + places: [MenuId.home_settings, MenuId.resources_library, MenuId.repository_settings, MenuId.auto_commit_settings, MenuId.trendz_settings] } ] ], diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 8bd724de4a..2836224a9a 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -46,6 +46,7 @@ import { ScadaSymbolData } from '@home/pages/scada-symbol/scada-symbol-editor.mo import { MenuId } from '@core/services/menu.models'; import { catchError } from 'rxjs/operators'; import { JsLibraryTableConfigResolver } from '@home/pages/admin/resource/js-library-table-config.resolver'; +import { TrendzSettingsComponent } from '@home/pages/admin/trendz-settings.component'; export const scadaSymbolResolver: ResolveFn = (route: ActivatedRouteSnapshot, @@ -349,6 +350,18 @@ const routes: Routes = [ } } }, + { + path: 'trendz', + component: TrendzSettingsComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + auth: [Authority.TENANT_ADMIN], + title: 'admin.trendz-settings', + breadcrumb: { + menuId: MenuId.trendz_settings + } + } + }, { path: 'security-settings', redirectTo: '/security-settings/general' diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts index 63878ebac9..a5f18122fd 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts @@ -37,6 +37,7 @@ import { OAuth2Module } from '@home/pages/admin/oauth2/oauth2.module'; import { JsLibraryTableHeaderComponent } from '@home/pages/admin/resource/js-library-table-header.component'; import { JsResourceComponent } from '@home/pages/admin/resource/js-resource.component'; import { NgxFlowModule } from '@flowjs/ngx-flow'; +import { TrendzSettingsComponent } from '@home/pages/admin/trendz-settings.component'; @NgModule({ declarations: @@ -55,7 +56,8 @@ import { NgxFlowModule } from '@flowjs/ngx-flow'; QueueComponent, RepositoryAdminSettingsComponent, AutoCommitAdminSettingsComponent, - TwoFactorAuthSettingsComponent + TwoFactorAuthSettingsComponent, + TrendzSettingsComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html new file mode 100644 index 0000000000..45ba62dace --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html @@ -0,0 +1,51 @@ + +
+ + + + admin.trendz-settings + + +
+
+ + +
+ + +
+
+ + admin.trendz-url + + + + {{ 'admin.trendz-enable' | translate }} + +
+
+ +
+
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.scss new file mode 100644 index 0000000000..cbb8e698bd --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.scss @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2025 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"; + +:host { + .mat-mdc-card-header { + min-height: 64px; + } + + .tb-trendz-section { + margin: 16px 0; + } + + .tb-trendz-url { + @media #{$mat-gt-sm} { + padding-right: 12px; + } + + @media #{$mat-lt-md} { + padding-bottom: 12px; + } + } +} diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts new file mode 100644 index 0000000000..ffd2898d2a --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts @@ -0,0 +1,95 @@ +/// +/// Copyright © 2016-2025 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 { Component, OnInit } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; +import { select, Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { TrendzSettingsService } from '@core/http/trendz-settings.service'; +import { TrendzSettings } from '@shared/models/trendz-settings.models'; +import { isDefinedAndNotNull } from '@core/utils'; + +@Component({ + selector: 'tb-trendz-settings', + templateUrl: './trendz-settings.component.html', + styleUrls: ['./trendz-settings.component.scss', './settings-card.scss'] +}) +export class TrendzSettingsComponent extends PageComponent implements OnInit, HasConfirmForm { + + trendzSettingsForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder, + private trendzSettingsService: TrendzSettingsService) { + super(store); + } + + ngOnInit() { + this.trendzSettingsForm = this.fb.group({ + trendzUrl: [null, [Validators.pattern(/^(https?:\/\/)[^\s/$.?#].[^\s]*$/i)]], + isTrendzEnabled: [false] + }); + + this.trendzSettingsService.getTrendzSettings().subscribe((trendzSettings) => { + this.setTrendzSettings(trendzSettings); + }); + + this.trendzSettingsForm.get('isTrendzEnabled').valueChanges + .subscribe((enabled: boolean) => this.toggleUrlRequired(enabled)); + } + + toggleUrlRequired(enabled: boolean) { + const trendzUrlControl = this.trendzSettingsForm.get('trendzUrl')!; + const validators = [Validators.pattern(/^(https?:\/\/)[^\s/$.?#].[^\s]*$/i)]; + + if (enabled) { + validators.push(Validators.required); + } + + trendzUrlControl.setValidators(validators); + trendzUrlControl.updateValueAndValidity(); + } + + setTrendzSettings(trendzSettings: TrendzSettings) { + this.trendzSettingsForm.reset({ + trendzUrl: trendzSettings?.baseUrl, + isTrendzEnabled: isDefinedAndNotNull(trendzSettings?.enabled) ? + trendzSettings?.enabled : false + }); + + this.toggleUrlRequired(this.trendzSettingsForm.get('isTrendzEnabled').value); + } + + confirmForm(): FormGroup { + return this.trendzSettingsForm; + } + + save(): void { + const trendzUrl = this.trendzSettingsForm.get('trendzUrl').value; + const isTrendzEnabled = this.trendzSettingsForm.get('isTrendzEnabled').value; + + const trendzSettings: TrendzSettings = { + baseUrl: trendzUrl, + enabled: isTrendzEnabled + }; + + this.trendzSettingsService.saveTrendzSettings(trendzSettings).subscribe(() => { + this.setTrendzSettings(trendzSettings); + }) + } +} diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index e1306e577c..b2f96409dc 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -200,6 +200,7 @@ export const HelpLinks = { mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, calculatedField: `${helpBaseUrl}/docs${docPlatformPrefix}/`, timewindowSettings: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/dashboards/#time-window`, + trendzSettings: `${helpBaseUrl}/docs/trendz/` } }; /* eslint-enable max-len */ diff --git a/ui-ngx/src/app/shared/models/icon.models.ts b/ui-ngx/src/app/shared/models/icon.models.ts index c9de8ec3da..6643bd9c7f 100644 --- a/ui-ngx/src/app/shared/models/icon.models.ts +++ b/ui-ngx/src/app/shared/models/icon.models.ts @@ -62,7 +62,19 @@ export const svgIcons: {[key: string]: string} = { '4.6760606 4.678212,7.3604329 7.3397982,4.6839955 4.6657413,2.0041717 6.6653477,2.2309572e-4 9.3360035,2.6766286 11.997681,' + '0 14.659287,2.6765011 Z m -5.332255,4.0079963 1.999613,2.003945 -7.99844,8.0158157 -1.9996133,-2.004017 z m 1.676684,4.3522483 ' + '1.999613,2.0039454 -6.6654242,6.679793 -1.9996133,-2.003874 z m 2.988987,7.0033574 -1.999544,-2.003945 -4.6658108,4.675848 ' + - '1.9996128,2.004015 z"/>' + '1.9996128,2.004015 z"/>', + 'trendz-settings': '' + + '' }; export const svgIconsUrl: { [key: string]: string } = { diff --git a/ui-ngx/src/app/shared/models/trendz-settings.models.ts b/ui-ngx/src/app/shared/models/trendz-settings.models.ts new file mode 100644 index 0000000000..e09797bd7e --- /dev/null +++ b/ui-ngx/src/app/shared/models/trendz-settings.models.ts @@ -0,0 +1,25 @@ +/// +/// Copyright © 2016-2025 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. +/// + +export interface TrendzSettings { + baseUrl: string, + enabled: boolean +} + +export const initialTrendzSettings: TrendzSettings = { + baseUrl: null, + enabled: false +} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b160a44b0e..8255d4e10b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -545,7 +545,11 @@ "slack-settings": "Slack settings", "mobile-settings": "Mobile settings", "firebase-service-account-file": "Firebase service account credentials JSON file", - "select-firebase-service-account-file": "Drag and drop your Firebase service account credentials file or " + "select-firebase-service-account-file": "Drag and drop your Firebase service account credentials file or ", + "trendz": "Trendz", + "trendz-settings": "Trendz settings", + "trendz-url": "Trendz URL", + "trendz-enable": "Enable Trendz" }, "alarm": { "alarm": "Alarm", From 09e334660fb70b2b8c5c1f03f2c0f925b0ed6a1a Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 2 May 2025 13:02:21 +0300 Subject: [PATCH 121/335] Truncate notification table on test finish --- .../java/org/thingsboard/server/controller/AbstractWebTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 7a96384be5..054c2fbe59 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -282,6 +282,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { @Autowired protected InMemoryStorage storage; + @Autowired protected JdbcTemplate jdbcTemplate; @MockBean From e59460b42bbcf69289aa2bdf8851e98472e911d1 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 2 May 2025 15:14:00 +0300 Subject: [PATCH 122/335] Task processors partitioning --- .../server/service/job/DefaultJobManager.java | 15 +++-- .../service/job/task/DummyTaskProcessor.java | 4 +- .../src/main/resources/thingsboard.yml | 11 +++- .../server/service/job/JobManagerTest.java | 9 ++- .../server/service/job/TestTaskProcessor.java | 23 ++++++++ .../server/common/msg/queue/ServiceType.java | 3 +- common/proto/src/main/proto/queue.proto | 1 + .../DefaultTbServiceInfoProvider.java | 17 +++++- .../queue/discovery/HashPartitionService.java | 17 +++++- .../server/queue/task/TaskProcessor.java | 47 ++++++++------- .../queue/task/TaskProcessorExecutors.java | 59 +++++++++++++++++++ 11 files changed, 167 insertions(+), 39 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/service/job/TestTaskProcessor.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessorExecutors.java diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 3da2e2a9ab..79312ba608 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.job.task.TaskResult; import org.thingsboard.server.common.data.notification.info.GeneralNotificationInfo; import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.notification.DefaultNotifications; @@ -47,6 +48,7 @@ import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.consumer.QueueConsumerManager; +import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.task.JobStatsService; import org.thingsboard.server.queue.util.AfterStartUp; @@ -70,20 +72,22 @@ public class DefaultJobManager implements JobManager { private final JobService jobService; private final JobStatsService jobStatsService; private final NotificationCenter notificationCenter; + private final PartitionService partitionService; private final Map jobProcessors; private final Map>> taskProducers; private final QueueConsumerManager> jobStatsConsumer; private final ExecutorService executor; private final ExecutorService consumerExecutor; - @Value("${queue.tasks.stats.processing_interval_ms:5000}") + @Value("${queue.tasks.stats.processing_interval_ms:1000}") private int statsProcessingInterval; public DefaultJobManager(JobService jobService, JobStatsService jobStatsService, NotificationCenter notificationCenter, - TbCoreQueueFactory queueFactory, List jobProcessors) { + PartitionService partitionService, TbCoreQueueFactory queueFactory, List jobProcessors) { this.jobService = jobService; this.jobStatsService = jobStatsService; this.notificationCenter = notificationCenter; + this.partitionService = partitionService; this.jobProcessors = jobProcessors.stream().collect(Collectors.toMap(JobProcessor::getType, Function.identity())); this.taskProducers = Arrays.stream(JobType.values()).collect(Collectors.toMap(Function.identity(), queueFactory::createTaskProducer)); this.executor = ThingsBoardExecutors.newWorkStealingPool(Math.max(4, Runtime.getRuntime().availableProcessors()), getClass()); @@ -199,8 +203,8 @@ public class DefaultJobManager implements JobManager { .build(); TbQueueProducer> producer = taskProducers.get(task.getJobType()); - TbProtoQueueMsg msg = new TbProtoQueueMsg<>(task.getTenantId().getId(), taskProto); // one job at a time for a given tenant - producer.send(TopicPartitionInfo.builder().topic(producer.getDefaultTopic()).build(), msg, new TbQueueCallback() { + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TASK_PROCESSOR, task.getJobType().name(), task.getTenantId(), task.getTenantId()); // one job at a time for a given tenant + producer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), taskProto), new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { log.trace("Submitted task: {}", task); @@ -249,7 +253,7 @@ public class DefaultJobManager implements JobManager { private void sendJobFinishedNotification(Job job) { NotificationTemplate template = DefaultNotifications.DefaultNotification.builder() .name("Job finished") - .subject("${type} ${status}") + .subject("${type} task ${status}") .text("${description} ${status}: ${result}") .build().toTemplate(); GeneralNotificationInfo info = new GeneralNotificationInfo(Map.of( @@ -258,6 +262,7 @@ public class DefaultJobManager implements JobManager { "status", job.getStatus().name().toLowerCase(), "result", job.getResult().getDescription() )); + // todo: button to see details (forward to jobs page) notificationCenter.sendGeneralWebNotification(job.getTenantId(), new TenantAdministratorsFilter(), template, info); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java index 5178461746..564142ff2b 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java @@ -16,13 +16,11 @@ package org.thingsboard.server.service.job.task; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.job.task.DummyTask; import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.common.data.job.task.DummyTask; import org.thingsboard.server.common.data.job.task.DummyTaskResult; import org.thingsboard.server.queue.task.TaskProcessor; -@Component @RequiredArgsConstructor public class DummyTaskProcessor extends TaskProcessor { diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 322b037e2d..7a7e117120 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1628,6 +1628,11 @@ queue: - key: max.poll.records # Max poll records for edqs.state topic value: "${TB_QUEUE_KAFKA_EDQS_STATE_MAX_POLL_RECORDS:512}" + tasks: + # Key-value properties for Kafka consumer for tasks topics + - key: max.poll.records + # Max poll records for tasks topics + value: "${TB_QUEUE_KAFKA_TASKS_MAX_POLL_RECORDS:1}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section, you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms @@ -1668,7 +1673,7 @@ queue: # Kafka properties for EDQS state topic (infinite retention, compaction) edqs-state: "${TB_QUEUE_KAFKA_EDQS_STATE_TOPIC_PROPERTIES:retention.ms:-1;segment.bytes:52428800;retention.bytes:-1;partitions:1;min.insync.replicas:1;cleanup.policy:compact}" # Kafka properties for tasks topics - tasks: "${TB_QUEUE_KAFKA_TASKS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" + tasks: "${TB_QUEUE_KAFKA_TASKS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:104857600;partitions:1;min.insync.replicas:1}" consumer-stats: # Prints lag between consumer group offset and last messages offset in Kafka topics enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" @@ -1884,9 +1889,11 @@ queue: # Statistics printing interval for Edge services print-interval-ms: "${TB_QUEUE_EDGE_STATS_PRINT_INTERVAL_MS:60000}" tasks: + # Partitions count for tasks queues + partitions: "${TB_QUEUE_TASKS_PARTITIONS:12}" stats: # Interval in milliseconds to process job stats - processing_interval_ms: "${TB_QUEUE_TASKS_STATS_PROCESSING_INTERVAL_MS:5000}" + processing_interval_ms: "${TB_QUEUE_TASKS_STATS_PROCESSING_INTERVAL_MS:1000}" # Event configuration parameters event: diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 39c87dfd70..796a38ce4f 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -39,7 +39,6 @@ import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.queue.task.JobStatsService; -import org.thingsboard.server.service.job.task.DummyTaskProcessor; import java.util.ArrayList; import java.util.List; @@ -66,7 +65,7 @@ public class JobManagerTest extends AbstractControllerTest { private JobService jobService; @SpyBean - private DummyTaskProcessor taskProcessor; + private TestTaskProcessor taskProcessor; @SpyBean private JobStatsService jobStatsService; @@ -109,7 +108,7 @@ public class JobManagerTest extends AbstractControllerTest { }); checkJobNotification(notification -> { - assertThat(notification.getSubject()).isEqualTo("Dummy job completed"); + assertThat(notification.getSubject()).isEqualTo("Dummy job task completed"); assertThat(notification.getText()).isEqualTo("Test job completed: 5/5 successful, 0 failed"); }); } @@ -145,7 +144,7 @@ public class JobManagerTest extends AbstractControllerTest { }); checkJobNotification(notification -> { - assertThat(notification.getSubject()).isEqualTo("Dummy job failed"); + assertThat(notification.getSubject()).isEqualTo("Dummy job task failed"); assertThat(notification.getText()).isEqualTo("Test job failed: 3/5 successful, 2 failed"); }); } @@ -340,7 +339,7 @@ public class JobManagerTest extends AbstractControllerTest { }); checkJobNotification(notification -> { - assertThat(notification.getSubject()).isEqualTo("Dummy job failed"); + assertThat(notification.getSubject()).isEqualTo("Dummy job task failed"); assertThat(notification.getText()).isEqualTo("Test job failed: Some error while submitting tasks"); }); } diff --git a/application/src/test/java/org/thingsboard/server/service/job/TestTaskProcessor.java b/application/src/test/java/org/thingsboard/server/service/job/TestTaskProcessor.java new file mode 100644 index 0000000000..fdaec648b5 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/job/TestTaskProcessor.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.service.job.task.DummyTaskProcessor; + +@Component +public class TestTaskProcessor extends DummyTaskProcessor { +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceType.java index f31fdfa7a8..8fd535891c 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/ServiceType.java @@ -27,7 +27,8 @@ public enum ServiceType { TB_TRANSPORT("TB Transport"), JS_EXECUTOR("JS Executor"), TB_VC_EXECUTOR("TB VC Executor"), - EDQS("TB Entity Data Query Service"); + EDQS("TB Entity Data Query Service"), + TASK_PROCESSOR("Task Processor"); private final String label; diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index b4d436a32a..1cc507c5b3 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -88,6 +88,7 @@ message ServiceInfo { SystemInfoProto systemInfo = 10; repeated string assignedTenantProfiles = 11; string label = 12; + repeated string taskTypes = 13; } message SystemInfoProto { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java index 609d3f8eee..4325651540 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java @@ -24,11 +24,13 @@ import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbTransportService; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; import org.thingsboard.server.queue.edqs.EdqsConfig; +import org.thingsboard.server.queue.task.TaskProcessor; import org.thingsboard.server.queue.util.AfterContextReady; import java.net.InetAddress; @@ -40,7 +42,12 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; -import static org.thingsboard.common.util.SystemUtil.*; +import static org.thingsboard.common.util.SystemUtil.getCpuCount; +import static org.thingsboard.common.util.SystemUtil.getCpuUsage; +import static org.thingsboard.common.util.SystemUtil.getDiscSpaceUsage; +import static org.thingsboard.common.util.SystemUtil.getMemoryUsage; +import static org.thingsboard.common.util.SystemUtil.getTotalDiscSpace; +import static org.thingsboard.common.util.SystemUtil.getTotalMemory; @Component @@ -65,7 +72,11 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { @Autowired private ApplicationContext applicationContext; + @Autowired + private List> availableTaskProcessors; + private List serviceTypes; + private List taskTypes; private ServiceInfo serviceInfo; @PostConstruct @@ -91,6 +102,9 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { edqsConfig.setLabel(serviceId); } } + taskTypes = availableTaskProcessors.stream() + .map(TaskProcessor::getJobType) + .toList(); generateNewServiceInfoWithCurrentSystemInfo(); } @@ -128,6 +142,7 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { builder.addAllAssignedTenantProfiles(assignedTenantProfiles.stream().map(UUID::toString).collect(Collectors.toList())); } builder.setLabel(edqsConfig.getLabel()); + builder.addAllTaskTypes(taskTypes.stream().map(JobType::name).toList()); return serviceInfo = builder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index 7186bf7055..ccbfbc9c79 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.exception.TenantNotFoundException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; @@ -82,6 +83,8 @@ public class HashPartitionService implements PartitionService { private Integer edgePartitions; @Value("${queue.edqs.partitions:12}") private Integer edqsPartitions; + @Value("${queue.tasks.partitions:12}") + private Integer tasksPartitions; @Value("${queue.partitions.hash_function_name:murmur3_128}") private String hashFunctionName; @@ -140,6 +143,12 @@ public class HashPartitionService implements PartitionService { QueueKey edqsKey = new QueueKey(ServiceType.EDQS); partitionSizesMap.put(edqsKey, edqsPartitions); partitionTopicsMap.put(edqsKey, "edqs"); // placeholder, not used + + for (JobType jobType : JobType.values()) { + QueueKey queueKey = new QueueKey(ServiceType.TASK_PROCESSOR, jobType.name()); + partitionSizesMap.put(queueKey, tasksPartitions); + partitionTopicsMap.put(queueKey, jobType.getTasksTopic()); + } } @AfterStartUp(order = AfterStartUp.QUEUE_INFO_INITIALIZATION) @@ -454,8 +463,8 @@ public class HashPartitionService implements PartitionService { if (serviceInfoProvider.isService(ServiceType.TB_RULE_ENGINE)) { partitionSizesMap.keySet().stream() .filter(queueKey -> queueKey.getType() == ServiceType.TB_RULE_ENGINE && - !queueKey.getTenantId().isSysTenantId() && - !newPartitions.containsKey(queueKey)) + !queueKey.getTenantId().isSysTenantId() && + !newPartitions.containsKey(queueKey)) .forEach(removed::add); } removed.forEach(queueKey -> { @@ -675,6 +684,10 @@ public class HashPartitionService implements PartitionService { for (String transportType : instance.getTransportsList()) { tbTransportServicesByType.computeIfAbsent(transportType, t -> new ArrayList<>()).add(instance); } + for (String taskType : instance.getTaskTypesList()) { + QueueKey queueKey = new QueueKey(ServiceType.TASK_PROCESSOR, taskType); + queueServiceList.computeIfAbsent(queueKey, key -> new ArrayList<>()).add(instance); + } } @NotNull diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 643e7b27bc..82f13ce66b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -22,26 +22,27 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.Task; import org.thingsboard.server.common.data.job.task.TaskResult; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.queue.QueueConfig; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.common.consumer.QueueConsumerManager; +import org.thingsboard.server.queue.common.consumer.MainQueueConsumerManager; +import org.thingsboard.server.queue.discovery.QueueKey; +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.provider.TaskProcessorQueueFactory; -import org.thingsboard.server.queue.util.AfterStartUp; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; public abstract class TaskProcessor, R extends TaskResult> { @@ -51,29 +52,35 @@ public abstract class TaskProcessor, R extends TaskResult> { private TaskProcessorQueueFactory queueFactory; @Autowired private JobStatsService statsService; + @Autowired + private TaskProcessorExecutors executors; - private QueueConsumerManager> taskConsumer; - private ExecutorService consumerExecutor; + private QueueKey queueKey; + private MainQueueConsumerManager, QueueConfig> taskConsumer; private final Set deletedTenants = ConcurrentHashMap.newKeySet(); private final Set discardedJobs = ConcurrentHashMap.newKeySet(); // fixme use caffeine @PostConstruct public void init() { - consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-consumer")); - taskConsumer = QueueConsumerManager.>builder() // fixme: should be consumer per partition - .name(getJobType().name().toLowerCase() + "-tasks") - .msgPackProcessor(this::processMsgs) // todo: max.poll.records = 1 - .pollInterval(125) - .consumerCreator(() -> queueFactory.createTaskConsumer(getJobType())) - .consumerExecutor(consumerExecutor) + queueKey = new QueueKey(ServiceType.TASK_PROCESSOR, getJobType().name()); + taskConsumer = MainQueueConsumerManager., QueueConfig>builder() + .queueKey(queueKey) + .config(QueueConfig.of(true, 500)) + .msgPackProcessor(this::processMsgs) + .consumerCreator((queueConfig, tpi) -> queueFactory.createTaskConsumer(getJobType())) + .consumerExecutor(executors.getConsumersExecutor()) + .scheduler(executors.getScheduler()) + .taskExecutor(executors.getMgmtExecutor()) .build(); } - @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) - public void afterStartUp() { - taskConsumer.subscribe(); - taskConsumer.launch(); + @EventListener + public void onPartitionChangeEvent(PartitionChangeEvent event) { + if (event.getServiceType() == ServiceType.TASK_PROCESSOR) { + Set partitions = event.getNewPartitions().get(queueKey); + taskConsumer.update(partitions); + } } @EventListener @@ -95,7 +102,7 @@ public abstract class TaskProcessor, R extends TaskResult> { } } - private void processMsgs(List> msgs, TbQueueConsumer> consumer) throws Exception { + private void processMsgs(List> msgs, TbQueueConsumer> consumer, QueueConfig queueConfig) throws Exception { for (TbProtoQueueMsg msg : msgs) { try { @SuppressWarnings("unchecked") @@ -159,7 +166,7 @@ public abstract class TaskProcessor, R extends TaskResult> { @PreDestroy public void destroy() { taskConsumer.stop(); - consumerExecutor.shutdownNow(); + taskConsumer.awaitStop(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessorExecutors.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessorExecutors.java new file mode 100644 index 0000000000..3aa6a0f004 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessorExecutors.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.queue.task; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.Getter; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.common.util.ThingsBoardThreadFactory; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@Getter +@Lazy +@Component +public class TaskProcessorExecutors { + + private ExecutorService consumersExecutor; + private ExecutorService mgmtExecutor; + private ScheduledExecutorService scheduler; + + @PostConstruct + private void init() { + consumersExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("task-consumer")); + mgmtExecutor = ThingsBoardExecutors.newWorkStealingPool(4, "task-consumer-mgmt"); + scheduler = ThingsBoardExecutors.newSingleThreadScheduledExecutor("task-consumer-scheduler"); + } + + @PreDestroy + private void destroy() { + if (consumersExecutor != null) { + consumersExecutor.shutdownNow(); + } + if (mgmtExecutor != null) { + mgmtExecutor.shutdownNow(); + } + if (scheduler != null) { + scheduler.shutdownNow(); + } + } + +} From 4c01b3d70a4f504c280696b4fdbadbd44412f7fc Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 2 May 2025 16:23:05 +0300 Subject: [PATCH 123/335] Refactoring for task processor queue factories --- .../discovery/HashPartitionServiceTest.java | 8 +- .../DefaultTbServiceInfoProvider.java | 18 ++-- .../queue/discovery/HashPartitionService.java | 35 ++++---- .../InMemoryMonolithQueueFactory.java | 10 --- .../provider/KafkaMonolithQueueFactory.java | 23 ------ .../provider/KafkaTbCoreQueueFactory.java | 23 ------ .../KafkaTbRuleEngineQueueFactory.java | 28 ------- .../queue/provider/TbCoreQueueFactory.java | 2 +- .../provider/TbCoreQueueProducerProvider.java | 8 -- .../provider/TbQueueProducerProvider.java | 3 - .../TbRuleEngineProducerProvider.java | 8 -- .../provider/TbRuleEngineQueueFactory.java | 2 +- .../TbTransportQueueProducerProvider.java | 5 -- .../TbVersionControlProducerProvider.java | 5 -- .../InMemoryTaskProcessorQueueFactory.java | 48 +++++++++++ .../server/queue/task/JobStatsService.java | 10 +-- .../task/KafkaTaskProcessorQueueFactory.java | 82 +++++++++++++++++++ .../server/queue/task/TaskProcessor.java | 1 - .../TaskProcessorQueueFactory.java | 2 +- .../edqs/DummyQueueRoutingInfoService.java | 33 -------- .../edqs/DummyTenantRoutingInfoService.java | 30 ------- 21 files changed, 172 insertions(+), 212 deletions(-) create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/task/InMemoryTaskProcessorQueueFactory.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/task/KafkaTaskProcessorQueueFactory.java rename common/queue/src/main/java/org/thingsboard/server/queue/{provider => task}/TaskProcessorQueueFactory.java (96%) delete mode 100644 edqs/src/main/java/org/thingsboard/server/edqs/DummyQueueRoutingInfoService.java delete mode 100644 edqs/src/main/java/org/thingsboard/server/edqs/DummyTenantRoutingInfoService.java diff --git a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java index dace159774..a149ca6dfd 100644 --- a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java @@ -49,6 +49,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Random; import java.util.Set; import java.util.UUID; @@ -419,10 +420,11 @@ public class HashPartitionServiceTest { } private HashPartitionService createPartitionService() { - HashPartitionService partitionService = new HashPartitionService(serviceInfoProvider, - routingInfoService, + HashPartitionService partitionService = new HashPartitionService( applicationEventPublisher, - queueRoutingInfoService, + serviceInfoProvider, + Optional.of(routingInfoService), + Optional.of(queueRoutingInfoService), topicService); ReflectionTestUtils.setField(partitionService, "coreTopic", "tb.core"); ReflectionTestUtils.setField(partitionService, "corePartitions", 10); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java index 4325651540..f244f99b6b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java @@ -66,13 +66,13 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { @Value("${service.rule_engine.assigned_tenant_profiles:}") private Set assignedTenantProfiles; - @Autowired + @Autowired(required = false) private EdqsConfig edqsConfig; @Autowired private ApplicationContext applicationContext; - @Autowired + @Autowired(required = false) private List> availableTaskProcessors; private List serviceTypes; @@ -102,9 +102,13 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { edqsConfig.setLabel(serviceId); } } - taskTypes = availableTaskProcessors.stream() - .map(TaskProcessor::getJobType) - .toList(); + if (CollectionsUtil.isNotEmpty(availableTaskProcessors)) { + taskTypes = availableTaskProcessors.stream() + .map(TaskProcessor::getJobType) + .toList(); + } else { + taskTypes = Collections.emptyList(); + } generateNewServiceInfoWithCurrentSystemInfo(); } @@ -141,7 +145,9 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { if (CollectionsUtil.isNotEmpty(assignedTenantProfiles)) { builder.addAllAssignedTenantProfiles(assignedTenantProfiles.stream().map(UUID::toString).collect(Collectors.toList())); } - builder.setLabel(edqsConfig.getLabel()); + if (edqsConfig != null) { + builder.setLabel(edqsConfig.getLabel()); + } builder.addAllTaskTypes(taskTypes.stream().map(JobType::name).toList()); return serviceInfo = builder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index ccbfbc9c79..3f81199488 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -19,6 +19,7 @@ import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; import jakarta.annotation.PostConstruct; import lombok.Data; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Value; @@ -49,6 +50,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -63,6 +65,7 @@ import static org.thingsboard.server.common.data.DataConstants.MAIN_QUEUE_NAME; @Service @Slf4j +@RequiredArgsConstructor public class HashPartitionService implements PartitionService { @Value("${queue.core.topic:tb_core}") @@ -90,8 +93,8 @@ public class HashPartitionService implements PartitionService { private final ApplicationEventPublisher applicationEventPublisher; private final TbServiceInfoProvider serviceInfoProvider; - private final TenantRoutingInfoService tenantRoutingInfoService; - private final QueueRoutingInfoService queueRoutingInfoService; + private final Optional tenantRoutingInfoService; + private final Optional queueRoutingInfoService; private final TopicService topicService; protected volatile ConcurrentMap> myPartitions = new ConcurrentHashMap<>(); @@ -108,18 +111,6 @@ public class HashPartitionService implements PartitionService { private HashFunction hashFunction; - public HashPartitionService(TbServiceInfoProvider serviceInfoProvider, - TenantRoutingInfoService tenantRoutingInfoService, - ApplicationEventPublisher applicationEventPublisher, - QueueRoutingInfoService queueRoutingInfoService, - TopicService topicService) { - this.serviceInfoProvider = serviceInfoProvider; - this.tenantRoutingInfoService = tenantRoutingInfoService; - this.applicationEventPublisher = applicationEventPublisher; - this.queueRoutingInfoService = queueRoutingInfoService; - this.topicService = topicService; - } - @PostConstruct public void init() { this.hashFunction = forName(hashFunctionName); @@ -178,6 +169,10 @@ public class HashPartitionService implements PartitionService { } private List getQueueRoutingInfos() { + if (queueRoutingInfoService.isEmpty()) { + return Collections.emptyList(); + } + List queueRoutingInfoList; String serviceType = serviceInfoProvider.getServiceType(); @@ -188,7 +183,7 @@ public class HashPartitionService implements PartitionService { if (getQueuesRetries > 0) { log.info("Try to get queue routing info."); try { - queueRoutingInfoList = queueRoutingInfoService.getAllQueuesRoutingInfo(); + queueRoutingInfoList = queueRoutingInfoService.get().getAllQueuesRoutingInfo(); break; } catch (Exception e) { log.info("Failed to get queues routing info: {}!", e.getMessage()); @@ -204,7 +199,7 @@ public class HashPartitionService implements PartitionService { } } } else { - queueRoutingInfoList = queueRoutingInfoService.getAllQueuesRoutingInfo(); + queueRoutingInfoList = queueRoutingInfoService.get().getAllQueuesRoutingInfo(); } return queueRoutingInfoList; } @@ -638,7 +633,11 @@ public class HashPartitionService implements PartitionService { } private TenantRoutingInfo getRoutingInfo(TenantId tenantId) { - return tenantRoutingInfoMap.computeIfAbsent(tenantId, tenantRoutingInfoService::getRoutingInfo); + if (tenantRoutingInfoService.isPresent()) { + return tenantRoutingInfoMap.computeIfAbsent(tenantId, __ -> tenantRoutingInfoService.get().getRoutingInfo(tenantId)); + } else { + return new TenantRoutingInfo(tenantId, null, false); + } } protected TenantId getIsolatedOrSystemTenantId(ServiceType serviceType, TenantId tenantId) { @@ -702,7 +701,7 @@ public class HashPartitionService implements PartitionService { if (!responsibleServices.isEmpty()) { // if there are any dedicated servers TenantProfileId profileId; if (tenantId != null && !tenantId.isSysTenantId()) { - TenantRoutingInfo routingInfo = tenantRoutingInfoService.getRoutingInfo(tenantId); + TenantRoutingInfo routingInfo = tenantRoutingInfoService.get().getRoutingInfo(tenantId); profileId = routingInfo.getProfileId(); } else { profileId = null; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java index 9160818278..a164237366 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java @@ -265,16 +265,6 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE return new InMemoryTbQueueProducer<>(storage, jobType.getTasksTopic()); } - @Override - public TbQueueConsumer> createTaskConsumer(JobType jobType) { - return new InMemoryTbQueueConsumer<>(storage, jobType.getTasksTopic()); - } - - @Override - public TbQueueProducer> createJobStatsProducer() { - return new InMemoryTbQueueProducer<>(storage, "jobs.stats"); - } - @Override public TbQueueConsumer> createJobStatsConsumer() { return new InMemoryTbQueueConsumer<>(storage, "jobs.stats"); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index e4b51eaa1f..b15e60f09d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -656,29 +656,6 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi .build(); } - @Override - public TbQueueConsumer> createTaskConsumer(JobType jobType) { - return TbKafkaConsumerTemplate.>builder() - .settings(kafkaSettings) - .topic(topicService.buildTopicName(jobType.getTasksTopic())) - .clientId(jobType.name().toLowerCase() + "-task-consumer-" + serviceInfoProvider.getServiceId()) - .groupId(topicService.buildTopicName(jobType.name().toLowerCase() + "-task-consumer-group")) - .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TaskProto.parseFrom(msg.getData()), msg.getHeaders())) - .admin(tasksAdmin) - .statsService(consumerStatsService) - .build(); - } - - @Override - public TbQueueProducer> createJobStatsProducer() { - return TbKafkaProducerTemplate.>builder() - .clientId("job-stats-producer-" + serviceInfoProvider.getServiceId()) - .defaultTopic(topicService.buildTopicName("jobs.stats")) - .settings(kafkaSettings) - .admin(tasksAdmin) - .build(); - } - @Override public TbQueueConsumer> createJobStatsConsumer() { return TbKafkaConsumerTemplate.>builder() diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index 85d1e8be14..0bf6acd830 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -535,29 +535,6 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { .build(); } - @Override - public TbQueueConsumer> createTaskConsumer(JobType jobType) { - return TbKafkaConsumerTemplate.>builder() - .settings(kafkaSettings) - .topic(topicService.buildTopicName(jobType.getTasksTopic())) - .clientId(jobType.name().toLowerCase() + "-task-consumer-" + serviceInfoProvider.getServiceId()) - .groupId(topicService.buildTopicName(jobType.name().toLowerCase() + "-task-consumer-group")) - .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TaskProto.parseFrom(msg.getData()), msg.getHeaders())) - .admin(tasksAdmin) - .statsService(consumerStatsService) - .build(); - } - - @Override - public TbQueueProducer> createJobStatsProducer() { - return TbKafkaProducerTemplate.>builder() - .clientId("job-stats-producer-" + serviceInfoProvider.getServiceId()) - .defaultTopic(topicService.buildTopicName("jobs.stats")) - .settings(kafkaSettings) - .admin(tasksAdmin) - .build(); - } - @Override public TbQueueConsumer> createJobStatsConsumer() { return TbKafkaConsumerTemplate.>builder() diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java index b2af3671c6..3b67ea4f9f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java @@ -22,15 +22,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; -import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -99,7 +96,6 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { private final TbQueueAdmin cfAdmin; private final TbQueueAdmin cfStateAdmin; private final TbQueueAdmin edqsEventsAdmin; - private final TbQueueAdmin tasksAdmin; private final AtomicLong consumerCount = new AtomicLong(); public KafkaTbRuleEngineQueueFactory(TopicService topicService, TbKafkaSettings kafkaSettings, @@ -137,7 +133,6 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { this.cfAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCalculatedFieldConfigs()); this.cfStateAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCalculatedFieldStateConfigs()); this.edqsEventsAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getEdqsEventsConfigs()); - this.tasksAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTasksConfigs()); } @Override @@ -419,29 +414,6 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { throw new UnsupportedOperationException(); } - @Override - public TbQueueConsumer> createTaskConsumer(JobType jobType) { - return TbKafkaConsumerTemplate.>builder() - .settings(kafkaSettings) - .topic(topicService.buildTopicName(jobType.getTasksTopic())) - .clientId(jobType.name().toLowerCase() + "-task-consumer-" + serviceInfoProvider.getServiceId()) - .groupId(topicService.buildTopicName(jobType.name().toLowerCase() + "-task-consumer-group")) - .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TaskProto.parseFrom(msg.getData()), msg.getHeaders())) - .admin(tasksAdmin) - .statsService(consumerStatsService) - .build(); - } - - @Override - public TbQueueProducer> createJobStatsProducer() { - return TbKafkaProducerTemplate.>builder() - .clientId("job-stats-producer-" + serviceInfoProvider.getServiceId()) - .defaultTopic(topicService.buildTopicName("jobs.stats")) - .settings(kafkaSettings) - .admin(tasksAdmin) - .build(); - } - @PreDestroy private void destroy() { if (coreAdmin != null) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java index 823ebea298..e47354941c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java @@ -47,7 +47,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; * Responsible for initialization of various Producers and Consumers used by TB Core Node. * Implementation Depends on the queue queue.type from yml or TB_QUEUE_TYPE environment variable */ -public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory, HousekeeperClientQueueFactory, EdqsClientQueueFactory, TaskProcessorQueueFactory { +public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory, HousekeeperClientQueueFactory, EdqsClientQueueFactory { /** * Used to push messages to instances of TB Transport Service diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java index 9900474a10..98a3d78304 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java @@ -18,7 +18,6 @@ package org.thingsboard.server.queue.provider; import jakarta.annotation.PostConstruct; import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -54,7 +53,6 @@ public class TbCoreQueueProducerProvider implements TbQueueProducerProvider { private TbQueueProducer> toHousekeeper; private TbQueueProducer> toCalculatedFields; private TbQueueProducer> toCalculatedFieldNotifications; - private TbQueueProducer> jobStatsProducer; public TbCoreQueueProducerProvider(TbCoreQueueFactory tbQueueProvider) { this.tbQueueProvider = tbQueueProvider; @@ -75,7 +73,6 @@ public class TbCoreQueueProducerProvider implements TbQueueProducerProvider { this.toEdgeEvents = tbQueueProvider.createEdgeEventMsgProducer(); this.toCalculatedFields = tbQueueProvider.createToCalculatedFieldMsgProducer(); this.toCalculatedFieldNotifications = tbQueueProvider.createToCalculatedFieldNotificationMsgProducer(); - this.jobStatsProducer = tbQueueProvider.createJobStatsProducer(); } @Override @@ -143,9 +140,4 @@ public class TbCoreQueueProducerProvider implements TbQueueProducerProvider { return toCalculatedFieldNotifications; } - @Override - public TbQueueProducer> getJobStatsProducer() { - return jobStatsProducer; - } - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbQueueProducerProvider.java index 428e673fa8..865637b2ff 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbQueueProducerProvider.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.queue.provider; -import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -98,6 +97,4 @@ public interface TbQueueProducerProvider { TbQueueProducer> getCalculatedFieldsNotificationsMsgProducer(); - TbQueueProducer> getJobStatsProducer(); - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java index 9e77a2d4e7..8e1952fc14 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java @@ -18,7 +18,6 @@ package org.thingsboard.server.queue.provider; import jakarta.annotation.PostConstruct; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -52,7 +51,6 @@ public class TbRuleEngineProducerProvider implements TbQueueProducerProvider { private TbQueueProducer> toEdgeEvents; private TbQueueProducer> toCalculatedFields; private TbQueueProducer> toCalculatedFieldNotifications; - private TbQueueProducer> jobStatsProducer; public TbRuleEngineProducerProvider(TbRuleEngineQueueFactory tbQueueProvider) { this.tbQueueProvider = tbQueueProvider; @@ -72,7 +70,6 @@ public class TbRuleEngineProducerProvider implements TbQueueProducerProvider { this.toEdgeEvents = tbQueueProvider.createEdgeEventMsgProducer(); this.toCalculatedFields = tbQueueProvider.createToCalculatedFieldMsgProducer(); this.toCalculatedFieldNotifications = tbQueueProvider.createToCalculatedFieldNotificationMsgProducer(); - this.jobStatsProducer = tbQueueProvider.createJobStatsProducer(); } @Override @@ -140,9 +137,4 @@ public class TbRuleEngineProducerProvider implements TbQueueProducerProvider { return toCalculatedFieldNotifications; } - @Override - public TbQueueProducer> getJobStatsProducer() { - return jobStatsProducer; - } - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java index 83c467c992..18bb6db14a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java @@ -41,7 +41,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; * Responsible for initialization of various Producers and Consumers used by TB Core Node. * Implementation Depends on the queue queue.type from yml or TB_QUEUE_TYPE environment variable */ -public interface TbRuleEngineQueueFactory extends TbUsageStatsClientQueueFactory, HousekeeperClientQueueFactory, EdqsClientQueueFactory, TaskProcessorQueueFactory { +public interface TbRuleEngineQueueFactory extends TbUsageStatsClientQueueFactory, HousekeeperClientQueueFactory, EdqsClientQueueFactory { /** * Used to push messages to instances of TB Transport Service diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java index 4472c6157e..cb7e6dd1f4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java @@ -122,9 +122,4 @@ public class TbTransportQueueProducerProvider implements TbQueueProducerProvider throw new RuntimeException("Not Implemented! Should not be used by Transport!"); } - @Override - public TbQueueProducer> getJobStatsProducer() { - throw new RuntimeException("Not Implemented! Should not be used by Transport!"); - } - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java index 0370f8a4af..85c400d094 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java @@ -118,9 +118,4 @@ public class TbVersionControlProducerProvider implements TbQueueProducerProvider throw new RuntimeException("Not Implemented! Should not be used by Version Control Service!"); } - @Override - public TbQueueProducer> getJobStatsProducer() { - throw new RuntimeException("Not Implemented! Should not be used by Version Control Service!"); - } - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/InMemoryTaskProcessorQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/InMemoryTaskProcessorQueueFactory.java new file mode 100644 index 0000000000..dbe302dfee --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/InMemoryTaskProcessorQueueFactory.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.queue.task; + +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.queue.TbQueueConsumer; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.memory.InMemoryStorage; +import org.thingsboard.server.queue.memory.InMemoryTbQueueConsumer; +import org.thingsboard.server.queue.memory.InMemoryTbQueueProducer; + +@Component +@ConditionalOnExpression("'${queue.type:null}'=='in-memory'") +@RequiredArgsConstructor +public class InMemoryTaskProcessorQueueFactory implements TaskProcessorQueueFactory { + + private final InMemoryStorage storage; + + @Override + public TbQueueConsumer> createTaskConsumer(JobType jobType) { + return new InMemoryTbQueueConsumer<>(storage, jobType.getTasksTopic()); + } + + @Override + public TbQueueProducer> createJobStatsProducer() { + return new InMemoryTbQueueProducer<>(storage, "jobs.stats"); + } + +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java index c3780f15e7..28d08f593c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.queue.task; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -29,15 +28,17 @@ import org.thingsboard.server.gen.transport.TransportProtos.TaskResultProto; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.provider.TbQueueProducerProvider; @Lazy @Service @Slf4j -@RequiredArgsConstructor public class JobStatsService { - private final TbQueueProducerProvider producerProvider; + private final TbQueueProducer> producer; + + public JobStatsService(TaskProcessorQueueFactory queueFactory) { + this.producer = queueFactory.createJobStatsProducer(); + } public void reportTaskResult(TenantId tenantId, JobId jobId, TaskResult result) { report(tenantId, jobId, JobStatsMsg.newBuilder() @@ -59,7 +60,6 @@ public class JobStatsService { .setJobIdLSB(jobId.getId().getLeastSignificantBits()); TbProtoQueueMsg msg = new TbProtoQueueMsg<>(jobId.getId(), statsMsg.build()); - TbQueueProducer> producer = producerProvider.getJobStatsProducer(); producer.send(TopicPartitionInfo.builder().topic(producer.getDefaultTopic()).build(), msg, TbQueueCallback.EMPTY); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/KafkaTaskProcessorQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/KafkaTaskProcessorQueueFactory.java new file mode 100644 index 0000000000..77a47a20c8 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/KafkaTaskProcessorQueueFactory.java @@ -0,0 +1,82 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.queue.task; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.queue.TbQueueAdmin; +import org.thingsboard.server.queue.TbQueueConsumer; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; +import org.thingsboard.server.queue.kafka.TbKafkaAdmin; +import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; +import org.thingsboard.server.queue.kafka.TbKafkaConsumerTemplate; +import org.thingsboard.server.queue.kafka.TbKafkaProducerTemplate; +import org.thingsboard.server.queue.kafka.TbKafkaSettings; +import org.thingsboard.server.queue.kafka.TbKafkaTopicConfigs; + +@Component +@ConditionalOnExpression("'${queue.type:null}'=='kafka'") +public class KafkaTaskProcessorQueueFactory implements TaskProcessorQueueFactory { + + private final TopicService topicService; + private final TbServiceInfoProvider serviceInfoProvider; + private final TbKafkaSettings kafkaSettings; + private final TbKafkaConsumerStatsService consumerStatsService; + + private final TbQueueAdmin tasksAdmin; + + public KafkaTaskProcessorQueueFactory(TopicService topicService, + TbServiceInfoProvider serviceInfoProvider, + TbKafkaSettings kafkaSettings, + TbKafkaConsumerStatsService consumerStatsService, + TbKafkaTopicConfigs kafkaTopicConfigs) { + this.serviceInfoProvider = serviceInfoProvider; + this.kafkaSettings = kafkaSettings; + this.topicService = topicService; + this.consumerStatsService = consumerStatsService; + this.tasksAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTasksConfigs()); + } + + @Override + public TbQueueConsumer> createTaskConsumer(JobType jobType) { + return TbKafkaConsumerTemplate.>builder() + .settings(kafkaSettings) + .topic(topicService.buildTopicName(jobType.getTasksTopic())) + .clientId(jobType.name().toLowerCase() + "-task-consumer-" + serviceInfoProvider.getServiceId()) + .groupId(topicService.buildTopicName(jobType.name().toLowerCase() + "-task-consumer-group")) + .decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TaskProto.parseFrom(msg.getData()), msg.getHeaders())) + .admin(tasksAdmin) + .statsService(consumerStatsService) + .build(); + } + + @Override + public TbQueueProducer> createJobStatsProducer() { + return TbKafkaProducerTemplate.>builder() + .clientId("job-stats-producer-" + serviceInfoProvider.getServiceId()) + .defaultTopic(topicService.buildTopicName("jobs.stats")) + .settings(kafkaSettings) + .admin(tasksAdmin) + .build(); + } + +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 82f13ce66b..319ddf4ab9 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -37,7 +37,6 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.consumer.MainQueueConsumerManager; import org.thingsboard.server.queue.discovery.QueueKey; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; -import org.thingsboard.server.queue.provider.TaskProcessorQueueFactory; import java.util.List; import java.util.Set; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessorQueueFactory.java similarity index 96% rename from common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java rename to common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessorQueueFactory.java index 571b14639c..c5e8035d74 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TaskProcessorQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessorQueueFactory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.queue.provider; +package org.thingsboard.server.queue.task; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; diff --git a/edqs/src/main/java/org/thingsboard/server/edqs/DummyQueueRoutingInfoService.java b/edqs/src/main/java/org/thingsboard/server/edqs/DummyQueueRoutingInfoService.java deleted file mode 100644 index 1f1152af68..0000000000 --- a/edqs/src/main/java/org/thingsboard/server/edqs/DummyQueueRoutingInfoService.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright © 2016-2025 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. - */ -package org.thingsboard.server.edqs; - -import org.springframework.stereotype.Service; -import org.thingsboard.server.queue.discovery.QueueRoutingInfo; -import org.thingsboard.server.queue.discovery.QueueRoutingInfoService; - -import java.util.Collections; -import java.util.List; - -@Service -public class DummyQueueRoutingInfoService implements QueueRoutingInfoService { - - @Override - public List getAllQueuesRoutingInfo() { - return Collections.emptyList(); - } - -} diff --git a/edqs/src/main/java/org/thingsboard/server/edqs/DummyTenantRoutingInfoService.java b/edqs/src/main/java/org/thingsboard/server/edqs/DummyTenantRoutingInfoService.java deleted file mode 100644 index 4e16e5e16a..0000000000 --- a/edqs/src/main/java/org/thingsboard/server/edqs/DummyTenantRoutingInfoService.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright © 2016-2025 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. - */ -package org.thingsboard.server.edqs; - -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.queue.discovery.TenantRoutingInfo; -import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; - -@Service -public class DummyTenantRoutingInfoService implements TenantRoutingInfoService { - @Override - public TenantRoutingInfo getRoutingInfo(TenantId tenantId) { - return null; - } - -} From ac9e738018f272d165a9971213df0251114762b8 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 2 May 2025 17:43:14 +0300 Subject: [PATCH 124/335] Task processing timeout --- .../housekeeper/HousekeeperService.java | 1 + .../service/job/task/DummyTaskProcessor.java | 5 ++ .../server/service/job/JobManagerTest.java | 22 +++++++++ .../server/queue/task/TaskProcessor.java | 35 +++++++++++--- common/util/pom.xml | 4 ++ .../org/thingsboard/common/util/SetCache.java | 47 +++++++++++++++++++ 6 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 common/util/src/main/java/org/thingsboard/common/util/SetCache.java diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java index 727e27c971..f2d3fad357 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java @@ -165,6 +165,7 @@ public class HousekeeperService { private void stop() { consumer.stop(); consumerExecutor.shutdownNow(); + taskExecutor.shutdownNow(); log.info("Stopped Housekeeper service"); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java index 564142ff2b..1bcf6b36b3 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java @@ -39,6 +39,11 @@ public class DummyTaskProcessor extends TaskProcessor { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); + JobResult jobResult = job.getResult(); + assertThat(jobResult.getFailedCount()).isEqualTo(1); + assertThat(((DummyTaskResult) jobResult.getResults().get(0)).getFailure().getError()).isEqualTo("Timeout after 2000 ms"); // last error + }); + } + @Test public void testCancelJob_whileRunning() throws Exception { int tasksCount = 100; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 319ddf4ab9..d243ef514d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -22,6 +22,8 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.SetCache; +import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.Task; @@ -41,7 +43,12 @@ import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import java.util.List; import java.util.Set; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public abstract class TaskProcessor, R extends TaskResult> { @@ -56,9 +63,10 @@ public abstract class TaskProcessor, R extends TaskResult> { private QueueKey queueKey; private MainQueueConsumerManager, QueueConfig> taskConsumer; + private final ExecutorService taskExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-processor")); - private final Set deletedTenants = ConcurrentHashMap.newKeySet(); - private final Set discardedJobs = ConcurrentHashMap.newKeySet(); // fixme use caffeine + private final SetCache discardedJobs = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); + private final SetCache deletedTenants = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); @PostConstruct public void init() { @@ -124,21 +132,34 @@ public abstract class TaskProcessor, R extends TaskResult> { consumer.commit(); } - private void processTask(T task) throws Exception { // todo: timeout and task interruption + private void processTask(T task) throws InterruptedException { task.setAttempt(task.getAttempt() + 1); log.info("Processing task: {}", task); + Future future = null; try { - R result = process(task); + future = taskExecutor.submit(() -> process(task)); + R result; + try { + result = future.get(getTaskProcessingTimeout(), TimeUnit.MILLISECONDS); + } catch (ExecutionException e) { + throw e.getCause(); + } catch (TimeoutException e) { + throw new TimeoutException("Timeout after " + getTaskProcessingTimeout() + " ms"); + } reportTaskResult(task, result); } catch (InterruptedException e) { throw e; - } catch (Exception e) { + } catch (Throwable e) { log.error("Failed to process task (attempt {}): {}", task.getAttempt(), task, e); if (task.getAttempt() <= task.getRetries()) { processTask(task); } else { reportTaskFailure(task, e); } + } finally { + if (future != null && !future.isDone()) { + future.cancel(true); + } } } @@ -166,8 +187,10 @@ public abstract class TaskProcessor, R extends TaskResult> { public void destroy() { taskConsumer.stop(); taskConsumer.awaitStop(); + taskExecutor.shutdownNow(); } + public abstract long getTaskProcessingTimeout(); public abstract JobType getJobType(); diff --git a/common/util/pom.xml b/common/util/pom.xml index 6719dc628a..f69cf794e9 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -116,6 +116,10 @@ exp4j ${exp4j.version} + + com.github.ben-manes.caffeine + caffeine + diff --git a/common/util/src/main/java/org/thingsboard/common/util/SetCache.java b/common/util/src/main/java/org/thingsboard/common/util/SetCache.java new file mode 100644 index 0000000000..9676434534 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/SetCache.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.common.util; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import java.util.concurrent.TimeUnit; + +public class SetCache { + + private static final Object DUMMY_VALUE = Boolean.TRUE; + + private final Cache cache; + + public SetCache(long valueTtlMs) { + this.cache = Caffeine.newBuilder() + .expireAfterWrite(valueTtlMs, TimeUnit.MILLISECONDS) + .build(); + } + + public void add(K key) { + cache.put(key, DUMMY_VALUE); + } + + public boolean contains(K key) { + return cache.asMap().containsKey(key); + } + + public void remove(K key) { + cache.invalidate(key); + } + +} From 83e5305966eb8756ee1a866759c69bf9f7fbf15c Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 2 May 2025 20:29:10 +0300 Subject: [PATCH 125/335] UI: Add new unit definition --- ui-ngx/src/app/shared/models/unit.models.ts | 7 ++ .../models/units/Magnetic-permeability.ts | 37 ++++++++++ .../shared/models/units/air-quality-index.ts | 19 +++++ .../models/units/amount-of-substance.ts | 41 +++++++++++ ui-ngx/src/app/shared/models/units/area.ts | 7 +- .../app/shared/models/units/blood-glucose.ts | 20 ++++++ .../app/shared/models/units/capacitance.ts | 59 +++++++++++++++ .../shared/models/units/catalytic-activity.ts | 19 +++++ .../models/units/concentration-gradient.ts | 40 +++++++++++ .../models/units/concentration-volume.ts | 63 ++++++++++++++++ .../shared/models/units/current-density.ts | 26 +++++++ .../shared/models/units/data-transfer-rate.ts | 59 +++++++++++++++ ui-ngx/src/app/shared/models/units/density.ts | 50 +++++++++++++ .../models/units/dimensionless-ratio.ts | 21 ++++++ .../shared/models/units/dynamic-viscosity.ts | 60 ++++++++++++++++ .../models/units/earthquake-magnitude.ts | 19 +++++ .../models/units/electric-field-strength.ts | 29 ++++++++ .../app/shared/models/units/electric-flux.ts | 46 ++++++++++++ .../shared/models/units/electric-moment.ts | 26 +++++++ .../models/units/electric-permittivity.ts | 21 ++++++ .../models/units/electrical-conductance.ts | 44 ++++++++++++ .../models/units/electrical-conductivity.ts | 30 ++++++++ .../src/app/shared/models/units/frequency.ts | 17 ++++- .../shared/models/units/fuel-efficiency.ts | 46 ++++++++++++ .../app/shared/models/units/heat-capacity.ts | 21 ++++++ .../src/app/shared/models/units/humidity.ts | 20 ++++++ .../src/app/shared/models/units/inductance.ts | 40 +++++++++++ .../models/units/kinematic-viscosity.ts | 55 ++++++++++++++ .../app/shared/models/units/light-exposure.ts | 21 ++++++ .../models/units/liner-charge-density.ts | 21 ++++++ .../shared/models/units/logarithmic-units.ts | 29 ++++++++ .../shared/models/units/luminous-efficacy.ts | 21 ++++++ .../app/shared/models/units/luminous-flux.ts | 21 ++++++ .../shared/models/units/luminous-intensity.ts | 21 ++++++ .../models/units/magnetic-field-gradient.ts | 25 +++++++ .../app/shared/models/units/magnetic-field.ts | 71 +++++++++++++++++++ .../app/shared/models/units/magnetic-flux.ts | 44 ++++++++++++ .../shared/models/units/magnetic-moment.ts | 26 +++++++ .../app/shared/models/units/molar-energy.ts | 21 ++++++ .../models/units/molar-heat-capacity.ts | 21 ++++++ .../src/app/shared/models/units/molar-mass.ts | 29 ++++++++ .../models/units/particle-concentration.ts | 19 +++++ .../src/app/shared/models/units/percentage.ts | 20 ++++++ ui-ngx/src/app/shared/models/units/ph.ts | 19 +++++ .../app/shared/models/units/polarization.ts | 21 ++++++ .../app/shared/models/units/power-density.ts | 65 +++++++++++++++++ .../src/app/shared/models/units/pressure.ts | 16 ++++- .../src/app/shared/models/units/radiance.ts | 19 +++++ .../shared/models/units/radiant-intensity.ts | 20 ++++++ .../app/shared/models/units/radiation-dose.ts | 50 +++++++++++++ .../models/units/radioactive-decay-rate.ts | 25 +++++++ .../units/radioactivity-concentration.ts | 25 +++++++ .../app/shared/models/units/radioactivity.ts | 40 +++++++++++ .../src/app/shared/models/units/resistance.ts | 46 ++++++++++++ .../shared/models/units/reynolds-number.ts | 21 ++++++ .../shared/models/units/signal-strength.ts | 31 ++++++++ .../app/shared/models/units/solid-angle.ts | 21 ++++++ .../shared/models/units/specific-energy.ts | 21 ++++++ .../models/units/specific-heat-capacity.ts | 21 ++++++ .../shared/models/units/specific-volume.ts | 21 ++++++ ui-ngx/src/app/shared/models/units/speed.ts | 7 +- .../app/shared/models/units/sugar-content.ts | 19 +++++ .../models/units/surface-charge-density.ts | 21 ++++++ .../shared/models/units/surface-tension.ts | 21 ++++++ .../models/units/thermal-conductivity.ts | 21 ++++++ .../src/app/shared/models/units/turbidity.ts | 20 ++++++ .../models/units/volume-charge-density.ts | 21 ++++++ .../src/app/shared/models/units/wavenumber.ts | 21 ++++++ .../assets/locale/locale.constant-en_US.json | 2 - 69 files changed, 1980 insertions(+), 7 deletions(-) create mode 100644 ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts create mode 100644 ui-ngx/src/app/shared/models/units/air-quality-index.ts create mode 100644 ui-ngx/src/app/shared/models/units/amount-of-substance.ts create mode 100644 ui-ngx/src/app/shared/models/units/blood-glucose.ts create mode 100644 ui-ngx/src/app/shared/models/units/capacitance.ts create mode 100644 ui-ngx/src/app/shared/models/units/catalytic-activity.ts create mode 100644 ui-ngx/src/app/shared/models/units/concentration-gradient.ts create mode 100644 ui-ngx/src/app/shared/models/units/concentration-volume.ts create mode 100644 ui-ngx/src/app/shared/models/units/current-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/data-transfer-rate.ts create mode 100644 ui-ngx/src/app/shared/models/units/density.ts create mode 100644 ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts create mode 100644 ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts create mode 100644 ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-field-strength.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-flux.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-moment.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-permittivity.ts create mode 100644 ui-ngx/src/app/shared/models/units/electrical-conductance.ts create mode 100644 ui-ngx/src/app/shared/models/units/electrical-conductivity.ts create mode 100644 ui-ngx/src/app/shared/models/units/fuel-efficiency.ts create mode 100644 ui-ngx/src/app/shared/models/units/heat-capacity.ts create mode 100644 ui-ngx/src/app/shared/models/units/humidity.ts create mode 100644 ui-ngx/src/app/shared/models/units/inductance.ts create mode 100644 ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts create mode 100644 ui-ngx/src/app/shared/models/units/light-exposure.ts create mode 100644 ui-ngx/src/app/shared/models/units/liner-charge-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/logarithmic-units.ts create mode 100644 ui-ngx/src/app/shared/models/units/luminous-efficacy.ts create mode 100644 ui-ngx/src/app/shared/models/units/luminous-flux.ts create mode 100644 ui-ngx/src/app/shared/models/units/luminous-intensity.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-field.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-flux.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-moment.ts create mode 100644 ui-ngx/src/app/shared/models/units/molar-energy.ts create mode 100644 ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts create mode 100644 ui-ngx/src/app/shared/models/units/molar-mass.ts create mode 100644 ui-ngx/src/app/shared/models/units/particle-concentration.ts create mode 100644 ui-ngx/src/app/shared/models/units/percentage.ts create mode 100644 ui-ngx/src/app/shared/models/units/ph.ts create mode 100644 ui-ngx/src/app/shared/models/units/polarization.ts create mode 100644 ui-ngx/src/app/shared/models/units/power-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/radiance.ts create mode 100644 ui-ngx/src/app/shared/models/units/radiant-intensity.ts create mode 100644 ui-ngx/src/app/shared/models/units/radiation-dose.ts create mode 100644 ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts create mode 100644 ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts create mode 100644 ui-ngx/src/app/shared/models/units/radioactivity.ts create mode 100644 ui-ngx/src/app/shared/models/units/resistance.ts create mode 100644 ui-ngx/src/app/shared/models/units/reynolds-number.ts create mode 100644 ui-ngx/src/app/shared/models/units/signal-strength.ts create mode 100644 ui-ngx/src/app/shared/models/units/solid-angle.ts create mode 100644 ui-ngx/src/app/shared/models/units/specific-energy.ts create mode 100644 ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts create mode 100644 ui-ngx/src/app/shared/models/units/specific-volume.ts create mode 100644 ui-ngx/src/app/shared/models/units/sugar-content.ts create mode 100644 ui-ngx/src/app/shared/models/units/surface-charge-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/surface-tension.ts create mode 100644 ui-ngx/src/app/shared/models/units/thermal-conductivity.ts create mode 100644 ui-ngx/src/app/shared/models/units/turbidity.ts create mode 100644 ui-ngx/src/app/shared/models/units/volume-charge-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/wavenumber.ts diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 4f73fc4fbc..27cca5f603 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -146,6 +146,7 @@ export interface Unit { tags: string[]; to_anchor: number; anchor_shift?: number; + transform?: (value: number) => number; } export type TbUnit = string | TbUnitMapping; @@ -235,6 +236,9 @@ export class Converter { if (origin.unit.anchor_shift) { result -= origin.unit.anchor_shift; } + if (typeof origin.unit.transform === 'function') { + result = origin.unit.transform(result); + } if (origin.system !== destination.system) { const measureUnits = this.measureData[origin.measure][origin.system]; const transform = measureUnits?.transform; @@ -251,6 +255,9 @@ export class Converter { if (destination.unit.anchor_shift) { result += destination.unit.anchor_shift; } + if (typeof destination.unit.transform === 'function') { + result = destination.unit.transform(result); + } return result / destination.unit.to_anchor; } diff --git a/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts b/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts new file mode 100644 index 0000000000..be120c4b15 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts @@ -0,0 +1,37 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticPermeabilityMetricUnits = 'H/m'; +export type MagneticPermeabilityImperialUnits = 'G/Oe'; + +export type MagneticPermeabilityUnits = + | MagneticPermeabilityMetricUnits + | MagneticPermeabilityImperialUnits; + +const METRIC: TbMeasureUnits = { + transform: (Hm) => Hm * 795774.715, + units: { + 'H/m': { + name: 'unit.henry-per-meter', + tags: ['magnetic permeability', 'henry per meter', 'H/m'], + to_anchor: 1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + transform: (GOe) => GOe / 795774.715, + units: { + 'G/Oe': { + name: 'unit.gauss-per-oersted', + tags: ['magnetic field', 'Gauss per Oersted', 'G/Oe'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/air-quality-index.ts b/ui-ngx/src/app/shared/models/units/air-quality-index.ts new file mode 100644 index 0000000000..907071b594 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/air-quality-index.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AirQualityIndexUnits = 'aqi'; + +const METRIC: TbMeasureUnits = { + units: { + aqi: { + name: 'unit.aqi', + tags: ['AQI', 'air quality index'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts new file mode 100644 index 0000000000..47f798c388 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts @@ -0,0 +1,41 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AmountOfSubstanceMetricUnits = 'mol' | 'nmol' | 'μmol' | 'mmol' | 'kmol'; + +export type AmountOfSubstanceUnits = AmountOfSubstanceMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'mol': { + name: 'unit.mole', + tags: ['amount of substance', 'chemical amount', 'mole', 'mol'], + to_anchor: 1, + }, + 'nmol': { + name: 'unit.nanomole', + tags: ['amount of substance', 'nanomole', 'nmol'], + to_anchor: 0.000000001, + }, + 'μmol': { + name: 'unit.micromole', + tags: ['amount of substance', 'micromole', 'μmol'], + to_anchor: 0.000001, + }, + 'mmol': { + name: 'unit.millimole', + tags: ['amount of substance', 'millimole', 'mmol'], + to_anchor: 0.001, + }, + 'kmol': { + name: 'unit.kilomole', + tags: ['amount of substance', 'kilomole', 'kmol'], + to_anchor: 1000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/area.ts b/ui-ngx/src/app/shared/models/units/area.ts index 6a364496b5..6c36d6d976 100644 --- a/ui-ngx/src/app/shared/models/units/area.ts +++ b/ui-ngx/src/app/shared/models/units/area.ts @@ -16,7 +16,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type AreaMetricUnits = 'mm²' | 'cm²' | 'm²' | 'a' | 'ha' | 'km²'; +export type AreaMetricUnits = 'mm²' | 'cm²' | 'm²' | 'a' | 'ha' | 'km²' | 'barn'; export type AreaImperialUnits = 'in²' | 'yd²' | 'ft²' | 'ac' | 'ml²' | 'cin'; export type AreaUnits = AreaMetricUnits | AreaImperialUnits; @@ -54,6 +54,11 @@ const METRIC: TbMeasureUnits = { tags: ['area','lot','zone','space','region','square kilometer','square kilometers','km²','sq-km'], to_anchor: 1000000, }, + barn: { + name: 'unit.barn', + tags: ['cross-sectional area', 'particle physics', 'nuclear physics', 'barn'], + to_anchor: 1e-28, + }, } }; diff --git a/ui-ngx/src/app/shared/models/units/blood-glucose.ts b/ui-ngx/src/app/shared/models/units/blood-glucose.ts new file mode 100644 index 0000000000..18b0902591 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/blood-glucose.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type BloodGlucoseUnits = BloodGlucoseMetricUnits; +export type BloodGlucoseMetricUnits = 'mg/dL'; + +const METRIC: TbMeasureUnits = { + units: { + 'mg/dL': { + name: 'unit.milligrams-per-deciliter', + tags: ['glucose', 'blood sugar', 'glucose level', 'mg/dL'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/capacitance.ts b/ui-ngx/src/app/shared/models/units/capacitance.ts new file mode 100644 index 0000000000..4999afd536 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/capacitance.ts @@ -0,0 +1,59 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type CapacitanceUnits = 'F' | 'mF' | 'μF' | 'nF' | 'pF' | 'kF' | 'MF' | 'GF' | 'TF'; + +const METRIC: TbMeasureUnits = { + units: { + 'F': { + name: 'unit.farad', + tags: ['electric capacitance', 'capacitance', 'farad', 'F'], + to_anchor: 1, + }, + 'mF': { + name: 'unit.millifarad', + tags: ['electric capacitance', 'capacitance', 'millifarad', 'mF'], + to_anchor: 1e-3, + }, + 'μF': { + name: 'unit.microfarad', + tags: ['electric capacitance', 'capacitance', 'microfarad', 'μF'], + to_anchor: 1e-6, + }, + 'nF': { + name: 'unit.nanofarad', + tags: ['electric capacitance', 'capacitance', 'nanofarad', 'nF'], + to_anchor: 1e-9, + }, + 'pF': { + name: 'unit.picofarad', + tags: ['electric capacitance', 'capacitance', 'picofarad', 'pF'], + to_anchor: 1e-12, + }, + 'kF': { + name: 'unit.kilofarad', + tags: ['electric capacitance', 'capacitance', 'kilofarad', 'kF'], + to_anchor: 1e3, + }, + 'MF': { + name: 'unit.megafarad', + tags: ['electric capacitance', 'capacitance', 'megafarad', 'MF'], + to_anchor: 1e6, + }, + 'GF': { + name: 'unit.gigafarad', + tags: ['electric capacitance', 'capacitance', 'gigafarad', 'GF'], + to_anchor: 1e9, + }, + 'TF': { + name: 'unit.terafarad', + tags: ['electric capacitance', 'capacitance', 'terafarad', 'TF'], + to_anchor: 1e12, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/catalytic-activity.ts b/ui-ngx/src/app/shared/models/units/catalytic-activity.ts new file mode 100644 index 0000000000..7198bf4d9c --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/catalytic-activity.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AirQualityIndexUnits = 'kat'; + +const METRIC: TbMeasureUnits = { + units: { + kat: { + name: 'unit.katal', + tags: ['catalytic activity', 'enzyme activity', 'kat'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/concentration-gradient.ts b/ui-ngx/src/app/shared/models/units/concentration-gradient.ts new file mode 100644 index 0000000000..ef406445d2 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/concentration-gradient.ts @@ -0,0 +1,40 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ConcentrationMetricUnits = 'mol/m³' | 'mg/mL' | 'mg/m³' | 'µg/m³' | 'particles/mL'; +export type ConcentrationUnits = ConcentrationMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'mol/m³': { + name: 'unit.mole-per-cubic-meter', + tags: ['concentration', 'amount of substance per unit volume', 'mole per cubic meter', 'mol/m³'], + to_anchor: 1, + }, + 'mg/mL': { + name: 'unit.milligram-per-milliliter', + tags: ['concentration', 'mass per unit volume', 'milligram per milliliter', 'mg/mL'], + to_anchor: 1, + }, + 'mg/m³': { + name: 'unit.milligram-per-cubic-meter', + tags: ['concentration', 'mass per unit volume', 'milligram per cubic meter', 'mg/m³'], + to_anchor: 1, + }, + 'µg/m³': { + name: 'unit.micrograms-per-cubic-meter', + tags: ['concentration', 'air quality', 'particulate matter', 'PM2.5', 'PM10', 'micrograms per cubic meter', 'µg/m³'], + to_anchor: 1, + }, + 'particles/mL': { + name: 'unit.particle-density', + tags: ['concentration', 'particle density', 'particles per milliliter', 'particles/mL'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/concentration-volume.ts b/ui-ngx/src/app/shared/models/units/concentration-volume.ts new file mode 100644 index 0000000000..94c8050fee --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/concentration-volume.ts @@ -0,0 +1,63 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ConcentrationVolumeMetricUnits = + 'mol/m³' + | 'µg/m³' + | 'mg/m³' + | 'g/m³' + | 'mg/L' + | 'mg/mL' + | 'kat/m³' + | '°Bx'; +export type ConcentrationVolumeUnits = ConcentrationVolumeMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'mol/m³': { + name: 'unit.mole-per-cubic-meter', + tags: ['concentration', 'amount of substance', 'mole per cubic meter', 'mol/m³'], + to_anchor: 1, + }, + 'µg/m³': { + name: 'unit.micrograms-per-cubic-meter', + tags: ['coarse particulate matter', 'pm10', 'fine particulate matter', 'pm2.5', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'micrograms per cubic meter', 'µg/m³'], + to_anchor: 1e-9, + }, + 'mg/m³': { + name: 'unit.milligram-per-cubic-meter', + tags: ['concentration', 'mass per volume', 'mg/m³'], + to_anchor: 1e-6, + }, + 'g/m³': { + name: 'unit.gram-per-cubic-meter', + tags: ['humidity', 'moisture', 'absolute humidity', 'g/m³'], + to_anchor: 1 / 1000, + }, + 'mg/L': { + name: 'unit.mg-per-liter', + tags: ['dissolved oxygen', 'water quality', 'mg/L'], + to_anchor: 1e-6, + }, + 'mg/mL': { + name: 'unit.milligram-per-milliliter', + tags: ['concentration', 'mass per volume', 'mg/mL'], + to_anchor: 1 / 1000, + }, + 'kat/m³': { + name: 'unit.katal-per-cubic-metre', + tags: ['catalytic activity concentration', 'enzyme concentration', 'kat/m³'], + to_anchor: 1, + }, + '°Bx': { + name: 'unit.degrees-brix', + tags: ['sugar content', 'fruit ripeness', 'Bx'], + to_anchor: 10.04 * 1e-3, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/current-density.ts b/ui-ngx/src/app/shared/models/units/current-density.ts new file mode 100644 index 0000000000..7b332c57f4 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/current-density.ts @@ -0,0 +1,26 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type CurrentDensityMetricUnits = 'µA/cm²' | 'A/m²'; + +export type CurrentDensityUnits = CurrentDensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'µA/cm²': { + name: 'unit.microampere-per-square-centimeter', + tags: ['current density', 'microampere per square centimeter', 'µA/cm²'], + to_anchor: 10000, + }, + 'A/m²': { + name: 'unit.ampere-per-square-meter', + tags: ['current density', 'current per unit area', 'ampere per square meter', 'A/m²'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts new file mode 100644 index 0000000000..e3b9095700 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts @@ -0,0 +1,59 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DataTransferRateUnits = 'bps' | 'kbps' | 'Mbps' | 'Gbps' | 'Tbps' | 'B/s' | 'KB/s' | 'MB/s' | 'GB/s'; + +const METRIC: TbMeasureUnits = { + units: { + 'bps': { + name: 'unit.bit-per-second', + tags: ['data transfer rate', 'bps'], + to_anchor: 1, + }, + 'kbps': { + name: 'unit.kilobit-per-second', + tags: ['data transfer rate', 'kbps'], + to_anchor: 1e3, + }, + 'Mbps': { + name: 'unit.megabit-per-second', + tags: ['data transfer rate', 'Mbps'], + to_anchor: 1e6, + }, + 'Gbps': { + name: 'unit.gigabit-per-second', + tags: ['data transfer rate', 'Gbps'], + to_anchor: 1e9, + }, + 'Tbps': { + name: 'unit.terabit-per-second', + tags: ['data transfer rate', 'Tbps'], + to_anchor: 1e12, + }, + 'B/s': { + name: 'unit.byte-per-second', + tags: ['data transfer rate', 'B/s'], + to_anchor: 8, + }, + 'KB/s': { + name: 'unit.kilobyte-per-second', + tags: ['data transfer rate', 'KB/s'], + to_anchor: 8e3, + }, + 'MB/s': { + name: 'unit.megabyte-per-second', + tags: ['data transfer rate', 'MB/s'], + to_anchor: 8e6, + }, + 'GB/s': { + name: 'unit.gigabyte-per-second', + tags: ['data transfer rate', 'GB/s'], + to_anchor: 8e9, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/density.ts b/ui-ngx/src/app/shared/models/units/density.ts new file mode 100644 index 0000000000..8603de1976 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/density.ts @@ -0,0 +1,50 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DensityMetricUnits = 'kg/m³' | 'g/cm³'; +export type DensityImperialUnits = 'lb/ft³' | 'oz/in³' | 'ton/yd³'; + +export type DensityUnits = DensityMetricUnits | DensityImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 0.062428, + units: { + 'kg/m³': { + name: 'unit.kilogram-per-cubic-meter', + tags: ['density', 'mass per unit volume', 'kg/m³'], + to_anchor: 1, // Base unit: kg/m³ + }, + 'g/cm³': { + name: 'unit.gram-per-cubic-centimeter', + tags: ['density', 'mass per unit volume', 'g/cm³'], + to_anchor: 1000, // 1 g/cm³ = 10³ kg/m³ + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 0.062428, + units: { + 'lb/ft³': { + name: 'unit.pound-per-cubic-foot', + tags: ['density', 'mass per unit volume', 'lb/ft³'], + to_anchor: 1, + }, + 'oz/in³': { + name: 'unit.ounces-per-cubic-inch', + tags: ['density', 'mass per unit volume', 'oz/in³'], + to_anchor: 1728, + }, + 'ton/yd³': { + name: 'unit.tons-per-cubic-yard', + tags: ['density', 'mass per unit volume', 'ton/yd³'], + to_anchor: 74.074, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts b/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts new file mode 100644 index 0000000000..71972aa6e8 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DimensionlessRatioMetricUnits = 'm/m'; + +export type DimensionRatioUnits = DimensionlessRatioMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'm/m': { + name: 'unit.meter-per-meter', + tags: ['ratio of length to length', 'meter per meter', 'm/m'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts new file mode 100644 index 0000000000..272892f9df --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts @@ -0,0 +1,60 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DynamicViscosityMetricUnits = 'Pa·s' | 'cP' | 'P' | 'N·s/m²' | 'dyn·s/cm²' | 'kg/(m·s)'; +export type DynamicViscosityImperialUnits = 'lb/(ft·h)'; + +export type DynamicViscosityUnits = DynamicViscosityMetricUnits | DynamicViscosityImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 2419.0883293091, + units: { + 'Pa·s': { + name: 'unit.pascal-second', + tags: ['dynamic viscosity', 'viscosity', 'fluid mechanics', 'Pa·s'], + to_anchor: 1, + }, + 'cP': { + name: 'unit.centipoise', + tags: ['viscosity', 'dynamic viscosity', 'fluid viscosity', 'centipoise', 'cP'], + to_anchor: 0.001, + }, + 'P': { + name: 'unit.poise', + tags: ['viscosity', 'dynamic viscosity', 'fluid viscosity', 'poise', 'P'], + to_anchor: 0.1, + }, + 'N·s/m²': { + name: 'unit.newton-second-per-square-meter', + tags: ['newton second per square meter', 'N·s/m²'], + to_anchor: 1, + }, + 'dyn·s/cm²': { + name: 'unit.dyne-second-per-square-centimeter', + tags: ['dyne second per square centimeter', 'dyn·s/cm²'], + to_anchor: 0.1, + }, + 'kg/(m·s)': { + name: 'unit.kilogram-per-meter-second', + tags: ['kilogram per meter-second', 'kg/(m·s)'], + to_anchor: 1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 0.00041337887, + units: { + 'lb/(ft·h)': { + name: 'unit.pound-per-foot-hour', + tags: ['pound per foot-hour', 'lb/(ft·h)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts b/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts new file mode 100644 index 0000000000..02658073b5 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type EarthquakeMagnitudeUnits = 'richter'; + +const METRIC: TbMeasureUnits = { + units: { + richter: { + name: 'unit.richter-scale', + tags: ['earthquake', 'seismic activity', 'richter'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-field-strength.ts b/ui-ngx/src/app/shared/models/units/electric-field-strength.ts new file mode 100644 index 0000000000..f8d753c401 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-field-strength.ts @@ -0,0 +1,29 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricFieldStrengthUnits = 'V/m' | 'mV/m' | 'kV/m'; + +const METRIC: TbMeasureUnits = { + units: { + 'V/m': { + name: 'unit.volts-per-meter', + tags: ['electric field strength', 'volts per meter', 'V/m'], + to_anchor: 1, + }, + 'mV/m': { + name: 'unit.millivolts-per-meter', + tags: ['electric field strength', 'millivolts per meter', 'mV/m'], + to_anchor: 1e-3, + }, + 'kV/m': { + name: 'unit.kilovolts-per-meter', + tags: ['electric field strength', 'kilovolts per meter', 'kV/m'], + to_anchor: 1e3, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-flux.ts b/ui-ngx/src/app/shared/models/units/electric-flux.ts new file mode 100644 index 0000000000..ac40a58293 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-flux.ts @@ -0,0 +1,46 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricFluxMetricUnits = 'V·m' | 'kV·m' | 'MV·m' | 'µV·m' | 'mV·m' | 'nV·m'; + +export type ElectricFluxUnits = ElectricFluxMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'V·m': { + name: 'unit.volt-meter', + tags: ['electric flux', 'volt-meter', 'V·m'], + to_anchor: 1, + }, + 'kV·m': { + name: 'unit.kilovolt-meter', + tags: ['electric flux', 'kilovolt-meter', 'kV·m'], + to_anchor: 1000, + }, + 'MV·m': { + name: 'unit.megavolt-meter', + tags: ['electric flux', 'megavolt-meter', 'MV·m'], + to_anchor: 1000000, + }, + 'µV·m': { + name: 'unit.microvolt-meter', + tags: ['electric flux', 'microvolt-meter', 'µV·m'], + to_anchor: 0.000001, + }, + 'mV·m': { + name: 'unit.millivolt-meter', + tags: ['electric flux', 'millivolt-meter', 'mV·m'], + to_anchor: 0.001, + }, + 'nV·m': { + name: 'unit.nanovolt-meter', + tags: ['electric flux', 'nanovolt-meter', 'nV·m'], + to_anchor: 0.000000001, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-moment.ts b/ui-ngx/src/app/shared/models/units/electric-moment.ts new file mode 100644 index 0000000000..47a8e55830 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-moment.ts @@ -0,0 +1,26 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricMomentMetricUnits = 'C·m' | 'D'; + +export type ElectricMomentUnits = ElectricMomentMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C·m': { + name: 'unit.electric-dipole-moment', + tags: ['electric dipole', 'dipole moment', 'coulomb meter', 'C·m'], + to_anchor: 1, + }, + 'D': { + name: 'unit.debye', + tags: ['polarization', 'electric dipole moment', 'debye', 'D'], + to_anchor: 3.33564e-30 + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-permittivity.ts b/ui-ngx/src/app/shared/models/units/electric-permittivity.ts new file mode 100644 index 0000000000..d9bea899a7 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-permittivity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricPermittivityMetricUnits = 'F/m'; + +export type ElectricPermittivityUnits = ElectricPermittivityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'F/m': { + name: 'unit.farad-per-meter', + tags: ['electric permittivity', 'farad per meter', 'F/m'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts new file mode 100644 index 0000000000..0a1fe6bebe --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts @@ -0,0 +1,44 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricalConductanceUnits = 'S' | 'mS' | 'μS' | 'kS' | 'MS' | 'GS'; + +const METRIC: TbMeasureUnits = { + units: { + 'S': { + name: 'unit.siemens', + tags: ['electrical conductance', 'conductance', 'siemens', 'S'], + to_anchor: 1, + }, + 'mS': { + name: 'unit.millisiemens', + tags: ['electrical conductance', 'conductance', 'millisiemens', 'mS'], + to_anchor: 1e-3, + }, + 'μS': { + name: 'unit.microsiemens', + tags: ['electrical conductance', 'conductance', 'microsiemens', 'μS'], + to_anchor: 1e-6, + }, + 'kS': { + name: 'unit.kilosiemens', + tags: ['electrical conductance', 'conductance', 'kilosiemens', 'kS'], + to_anchor: 1e3, + }, + 'MS': { + name: 'unit.megasiemens', + tags: ['electrical conductance', 'conductance', 'megasiemens', 'MS'], + to_anchor: 1e6, + }, + 'GS': { + name: 'unit.gigasiemens', + tags: ['electrical conductance', 'conductance', 'gigasiemens', 'GS'], + to_anchor: 1e9, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts b/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts new file mode 100644 index 0000000000..9bf7245ff1 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts @@ -0,0 +1,30 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricalConductivityMetricUnits = 'µS/cm' | 'mS/m' | 'S/m'; +export type ElectricalConductivityUnits = ElectricalConductivityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'S/m': { + name: 'unit.siemens-per-meter', + tags: ['Electrical conductivity', 'water quality', 'soil quality', 'siemens per meter', 'S/m'], + to_anchor: 1, + }, + 'µS/cm': { + name: 'unit.microsiemens-per-centimeter', + tags: ['Electrical conductivity', 'water quality', 'soil quality', 'microsiemens per centimeter', 'µS/cm'], + to_anchor: 0.0001, + }, + 'mS/m': { + name: 'unit.millisiemens-per-meter', + tags: ['Electrical conductivity', 'water quality', 'soil quality', 'millisiemens per meter', 'mS/m'], + to_anchor: 0.001, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/frequency.ts b/ui-ngx/src/app/shared/models/units/frequency.ts index ecff651b78..f677803fe8 100644 --- a/ui-ngx/src/app/shared/models/units/frequency.ts +++ b/ui-ngx/src/app/shared/models/units/frequency.ts @@ -17,7 +17,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type FrequencyUnits = FrequencyMetricUnits; -export type FrequencyMetricUnits = 'mHz' | 'Hz' | 'kHz' | 'MHz' | 'GHz' | 'THz' | 'rpm' | 'deg/s' | 'rad/s'; +export type FrequencyMetricUnits = 'mHz' | 'Hz' | 'kHz' | 'MHz' | 'GHz' | 'THz' | 'rpm' | 'deg/s' | 'rad/s' | 'RPM' | 'λ' | 'bpm'; const METRIC: TbMeasureUnits = { units: { @@ -56,6 +56,21 @@ const METRIC: TbMeasureUnits = { tags: ['frequency', 'rotation per minute', 'rotations per minute', 'rpm', 'angular velocity'], to_anchor: 1 / 60, }, + RPM: { + name: 'unit.rpm', + tags: ['rotational speed', 'angular velocity', 'revolutions per minute', 'RPM'], + to_anchor: 1 / 60, + }, + 'λ': { + name: 'unit.lambda', + tags: ['wavelength', 'lambda', 'λ'], + to_anchor: 299792458, + }, + bpm: { + name: 'unit.beats-per-minute', + tags: ['heart rate', 'pulse', 'bpm'], + to_anchor: 0.0167 + }, 'deg/s': { name: 'unit.deg-per-second', tags: ['angular velocity', 'degrees per second', 'deg/s'], diff --git a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts new file mode 100644 index 0000000000..f097382089 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts @@ -0,0 +1,46 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type FuelEfficiencyMetricUnits = 'km/L' | 'L/100km'; +export type FuelEfficiencyImperialUnits = 'mpg' | 'gal/mi'; + +export type FuelEfficiencyUnits = FuelEfficiencyMetricUnits | FuelEfficiencyImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 2.35214583, + units: { + 'km/L': { + name: 'unit.kilometers-per-liter', + tags: ['fuel efficiency', 'km/L'], + to_anchor: 1, + }, + 'L/100km': { + name: 'unit.liters-per-100-km', + tags: ['fuel efficiency', 'L/100km'], + to_anchor: 1, + transform: (value) => 100 / value, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 0.425144, + units: { + 'mpg': { + name: 'unit.miles-per-gallon', + tags: ['fuel efficiency', 'mpg'], + to_anchor: 0.425144, + }, + 'gal/mi': { + name: 'unit.gallons-per-mile', + tags: ['fuel efficiency', 'gal/mi'], + to_anchor: 2.35214583, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/heat-capacity.ts b/ui-ngx/src/app/shared/models/units/heat-capacity.ts new file mode 100644 index 0000000000..05b56cd0a7 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/heat-capacity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type HeatCapacityMetricUnits = 'J/K'; + +export type HeatCapacityUnits = HeatCapacityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/K': { + name: 'unit.joule-per-kelvin', + tags: ['specific heat capacity', 'heat capacity per unit temperature', 'joule per kelvin', 'J/K'], + to_anchor: 1, // Base unit: J/K + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/humidity.ts b/ui-ngx/src/app/shared/models/units/humidity.ts new file mode 100644 index 0000000000..22bf1d6902 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/humidity.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type HumidityUnits = HumidityMetricUnits; +export type HumidityMetricUnits = 'g/kg'; + +const METRIC: TbMeasureUnits = { + units: { + 'g/kg': { + name: 'unit.gram-per-kilogram', + tags: ['humidity', 'moisture', 'specific humidity', 'g/kg'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/inductance.ts b/ui-ngx/src/app/shared/models/units/inductance.ts new file mode 100644 index 0000000000..9f0d0bb3eb --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/inductance.ts @@ -0,0 +1,40 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type InductanceMetricUnits = 'H' | 'mH' | 'µH' | 'nH' | 'T·m/A'; +export type InductanceUnits = InductanceMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + H: { + name: 'unit.henry', + tags: ['inductance', 'magnetic induction', 'H'], + to_anchor: 1, + }, + mH: { + name: 'unit.millihenry', + tags: ['inductance', 'millihenry', 'mH'], + to_anchor: 0.001, + }, + µH: { + name: 'unit.microhenry', + tags: ['inductance', 'microhenry', 'µH'], + to_anchor: 1e-6, + }, + nH: { + name: 'unit.nanohenry', + tags: ['inductance', 'nanohenry', 'nH'], + to_anchor: 1e-9, + }, + 'T·m/A': { + name: 'unit.tesla-meter-per-ampere', + tags: ['magnetic field', 'Tesla Meter per Ampere', 'T·m/A', 'magnetic flux'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts new file mode 100644 index 0000000000..6a9104ff9f --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts @@ -0,0 +1,55 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type KinematicViscosityMetricUnits = 'm²/s' | 'cm²/s' | 'St' | 'cSt'; +export type KinematicViscosityImperialUnits = 'ft²/s' | 'in²/s'; + +export type KinematicViscosityUnits = KinematicViscosityMetricUnits | KinematicViscosityImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 10.7639104167097, + units: { + 'm²/s': { + name: 'unit.square-meter-per-second', + tags: ['kinematic viscosity', 'm²/s'], + to_anchor: 1, + }, + 'cm²/s': { + name: 'unit.square-centimeter-per-second', + tags: ['kinematic viscosity', 'cm²/s'], + to_anchor: 1e-4, + }, + 'St': { + name: 'unit.stoke', + tags: ['kinematic viscosity', 'stokes', 'St'], + to_anchor: 1e-4, // St to m²/s + }, + 'cSt': { + name: 'unit.centistokes', + tags: ['kinematic viscosity', 'centistokes', 'cSt'], + to_anchor: 1e-6, // cSt to m²/s + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 0.09290304, + units: { + 'ft²/s': { + name: 'unit.square-foot-per-second', + tags: ['kinematic viscosity', 'ft²/s'], + to_anchor: 0.09290304, + }, + 'in²/s': { + name: 'unit.square-inch-per-second', + tags: ['kinematic viscosity', 'in²/s'], + to_anchor: 0.00064516, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/light-exposure.ts b/ui-ngx/src/app/shared/models/units/light-exposure.ts new file mode 100644 index 0000000000..353cf57a37 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/light-exposure.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LightExposureMetricUnits = 'lx·s'; + +export type LightExposureUnits = LightExposureMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'lx·s': { + name: 'unit.lux-second', + tags: ['light exposure', 'illuminance over time', 'lux-second', 'lx·s'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/liner-charge-density.ts b/ui-ngx/src/app/shared/models/units/liner-charge-density.ts new file mode 100644 index 0000000000..4a2239b1d7 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/liner-charge-density.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LinerChargeDensityMetricUnits = 'C/m'; + +export type LinerChargeDensityUnits = LinerChargeDensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C/m': { + name: 'unit.coulomb-per-meter', + tags: ['electric displacement field per length', 'coulomb per meter', 'C/m'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-units.ts b/ui-ngx/src/app/shared/models/units/logarithmic-units.ts new file mode 100644 index 0000000000..ccbfd9af8e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/logarithmic-units.ts @@ -0,0 +1,29 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LogarithmicUnits = 'dB' | 'B' | 'Np'; + +const METRIC: TbMeasureUnits = { + units: { + 'dB': { + name: 'unit.decibel', + tags: ['noise level', 'sound level', 'volume', 'acoustics', 'decibel', 'dB'], + to_anchor: 1, + }, + 'B': { + name: 'unit.bel', + tags: ['logarithmic unit', 'power ratio', 'intensity ratio', 'bel', 'B'], + to_anchor: 10, + }, + 'Np': { + name: 'unit.neper', + tags: ['logarithmic unit', 'ratio', 'gain', 'loss', 'attenuation', 'neper', 'Np'], + to_anchor: 8.685889638, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts b/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts new file mode 100644 index 0000000000..1aa5c496fb --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LuminousEfficacyMetricUnits = 'lm/W'; + +export type LuminousEfficacyUnits = LuminousEfficacyMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'lm/W': { + name: 'unit.lumens-per-watt', + tags: ['luminous efficacy', 'lighting efficiency', 'lumens per watt', 'lm/W'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/luminous-flux.ts b/ui-ngx/src/app/shared/models/units/luminous-flux.ts new file mode 100644 index 0000000000..ff1b052919 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/luminous-flux.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LuminousFluxMetricUnits = 'lm'; + +export type LuminousFluxUnits = LuminousFluxMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'lm': { + name: 'unit.lumen', + tags: ['luminous flux', 'total light output', 'lumen', 'lm'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts new file mode 100644 index 0000000000..7168aab088 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LuminousIntensityMetricUnits = 'cd'; + +export type LuminousIntensityUnits = LuminousIntensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'cd': { + name: 'unit.candela', + tags: ['luminous intensity', 'light intensity', 'candela', 'cd'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts b/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts new file mode 100644 index 0000000000..41e8fade91 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts @@ -0,0 +1,25 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticFieldGradientUnits = MagneticFieldGradientMetricUnits; +export type MagneticFieldGradientMetricUnits = 'T/m' | 'G/cm'; + +const METRIC: TbMeasureUnits = { + units: { + 'T/m': { + name: 'unit.tesla-per-meter', + tags: ['magnetic field', 'tesla per meter', 'T/m'], + to_anchor: 1, + }, + 'G/cm': { + name: 'unit.gauss-per-centimeter', + tags: ['magnetic field', 'gauss per centimeter', 'G/cm'], + to_anchor: 0.01, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-field.ts b/ui-ngx/src/app/shared/models/units/magnetic-field.ts new file mode 100644 index 0000000000..8996c7911b --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-field.ts @@ -0,0 +1,71 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticFieldMetricUnits = 'T' | 'mT' | 'μT' | 'nT' | 'kT' | 'MT' | 'G' | 'kG' | 'γ' | 'A/m' | 'Oe'; + +export type MagneticFieldUnits = MagneticFieldMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'T': { + name: 'unit.tesla', + tags: ['magnetic field', 'magnetic field strength', 'tesla', 'T', 'magnetic flux density'], + to_anchor: 1, + }, + 'mT': { + name: 'unit.millitesla', + tags: ['magnetic field', 'magnetic field strength', 'millitesla', 'mT'], + to_anchor: 0.001, + }, + 'μT': { + name: 'unit.microtesla', + tags: ['magnetic field', 'magnetic field strength', 'microtesla', 'μT'], + to_anchor: 0.000001, + }, + 'nT': { + name: 'unit.nanotesla', + tags: ['magnetic field', 'magnetic field strength', 'nanotesla', 'nT'], + to_anchor: 0.000000001, + }, + 'kT': { + name: 'unit.kilotesla', + tags: ['magnetic field', 'magnetic field strength', 'kilotesla', 'kT'], + to_anchor: 1000, + }, + 'MT': { + name: 'unit.megatesla', + tags: ['magnetic field', 'magnetic field strength', 'megatesla', 'MT'], + to_anchor: 1000000, + }, + 'G': { + name: 'unit.gauss', + tags: ['magnetic field', 'magnetic field strength', 'gauss', 'G', 'magnetic flux density'], + to_anchor: 0.0001, + }, + 'kG': { + name: 'unit.kilogauss', + tags: ['magnetic field', 'magnetic field strength', 'kilogauss', 'kG', 'magnetic flux density'], + to_anchor: 0.1, + }, + 'γ': { + name: 'unit.gamma', + tags: ['magnetic flux density', 'gamma', 'γ'], + to_anchor: 0.000000001, + }, + 'A/m': { + name: 'unit.ampere-per-meter', + tags: ['magnetic field strength', 'magnetic field intensity', 'ampere per meter', 'A/m'], + to_anchor: 0.00000125663706143591, + }, + 'Oe': { + name: 'unit.oersted', + tags: ['magnetic field', 'oersted', 'Oe'], + to_anchor: 0.0001, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-flux.ts b/ui-ngx/src/app/shared/models/units/magnetic-flux.ts new file mode 100644 index 0000000000..467d317d7a --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-flux.ts @@ -0,0 +1,44 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticFluxUnits = 'Wb' | 'µWb' | 'mWb' | 'Mx' | 'G·cm²' | 'kG·cm²'; + +const METRIC: TbMeasureUnits = { + units: { + 'Wb': { + name: 'unit.weber', + tags: ['magnetic flux', 'weber', 'Wb'], + to_anchor: 1, + }, + 'µWb': { + name: 'unit.microweber', + tags: ['magnetic flux', 'microweber', 'µWb'], + to_anchor: 1e-6, + }, + 'mWb': { + name: 'unit.milliweber', + tags: ['magnetic flux', 'milliweber', 'mWb'], + to_anchor: 1e-3, + }, + 'Mx': { + name: 'unit.maxwell', + tags: ['magnetic flux', 'magnetic field', 'maxwell', 'Mx'], + to_anchor: 1e-8, + }, + 'G·cm²': { + name: 'unit.gauss-square-centimeter', + tags: ['magnetic flux', 'gauss-square centimeter', 'G·cm²'], + to_anchor: 1e-8, + }, + 'kG·cm²': { + name: 'unit.kilogauss-square-centimeter', + tags: ['magnetic flux', 'kilogauss-square centimeter', 'kG·cm²'], + to_anchor: 1e-5, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-moment.ts b/ui-ngx/src/app/shared/models/units/magnetic-moment.ts new file mode 100644 index 0000000000..d7771bdf33 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-moment.ts @@ -0,0 +1,26 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticMomentMetricUnits = 'A·m²' | 'μB'; + +export type MagneticMomentUnits = MagneticMomentMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'A·m²': { + name: 'unit.magnetic-dipole-moment', + tags: ['magnetic dipole', 'dipole moment', 'ampere square meter', 'A·m²'], + to_anchor: 1, + }, + 'μB': { + name: 'unit.bohr-magneton', + tags: ['atomic physics', 'magnetic moment', 'bohr magneton', 'μB'], + to_anchor: 9.274e-24, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/molar-energy.ts b/ui-ngx/src/app/shared/models/units/molar-energy.ts new file mode 100644 index 0000000000..41e7b5a75e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/molar-energy.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MolarEnergyMetricUnits = 'J/mol'; + +export type MolarEnergyUnits = MolarEnergyMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/mol': { + name: 'unit.joule-per-mole', + tags: ['molar energy', 'joule per mole', 'J/mol'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts new file mode 100644 index 0000000000..104a44b3de --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MolarHeatCapacityMetricUnits = 'J/(mol·K)'; + +export type MolarHeatCapacityUnits = MolarHeatCapacityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/(mol·K)': { + name: 'unit.joule-per-mole-kelvin', + tags: ['molar heat capacity', 'joule per mole-kelvin', 'J/(mol·K)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/molar-mass.ts b/ui-ngx/src/app/shared/models/units/molar-mass.ts new file mode 100644 index 0000000000..c9a6b89639 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/molar-mass.ts @@ -0,0 +1,29 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MolarMassUnits = 'kg/mol' | 'g/mol' | 'mg/mol'; + +const METRIC: TbMeasureUnits = { + units: { + 'g/mol': { + name: 'unit.gram-per-mole', + tags: ['molar mass', 'gram per mole', 'g/mol'], + to_anchor: 1, + }, + 'kg/mol': { + name: 'unit.kilogram-per-mole', + tags: ['molar mass', 'kilogram per mole', 'kg/mol'], + to_anchor: 1e3, + }, + 'mg/mol': { + name: 'unit.milligram-per-mole', + tags: ['molar mass', 'milligram per mole', 'mg/mol'], + to_anchor: 1e-3, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/particle-concentration.ts b/ui-ngx/src/app/shared/models/units/particle-concentration.ts new file mode 100644 index 0000000000..991d5a9f78 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/particle-concentration.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ParticleConcentrationUnits = 'particles/mL'; + +const METRIC: TbMeasureUnits = { + units: { + 'particles/mL': { + name: 'unit.particle-density', + tags: ['particle concentration', 'count', 'particles/mL'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/percentage.ts b/ui-ngx/src/app/shared/models/units/percentage.ts new file mode 100644 index 0000000000..1bd796b0f5 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/percentage.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PercentageMetricUnits = '%'; +export type PercentageUnits = PercentageMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + '%': { + name: 'unit.percent', + tags: ['power source', 'state of charge (SoC)', 'battery', 'battery level', 'level', 'humidity', 'moisture', 'percentage', 'relative humidity', 'water content', 'soil moisture', 'irrigation', 'water in soil', 'soil water content', 'VWC', 'Volumetric Water Content', 'Total Harmonic Distortion', 'THD', 'power quality', 'UV Transmittance', '%', 'capacity'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/ph.ts b/ui-ngx/src/app/shared/models/units/ph.ts new file mode 100644 index 0000000000..d394d0f8ec --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/ph.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PHUnits = 'pH'; + +const METRIC: TbMeasureUnits = { + units: { + pH: { + name: 'unit.ph-level', + tags: ['acidity', 'alkalinity', 'neutral', 'acid', 'base', 'pH', 'soil pH', 'water quality', 'water pH'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/polarization.ts b/ui-ngx/src/app/shared/models/units/polarization.ts new file mode 100644 index 0000000000..09528228a0 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/polarization.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PolarizationMetricUnits = 'C·m²/V'; + +export type PolarizationUnits = PolarizationMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C·m²/V': { + name: 'unit.coulomb-per-square-meter-per-volt', + tags: ['polarization', 'electric field', 'coulomb per square meter per volt', 'C·m²/V'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/power-density.ts b/ui-ngx/src/app/shared/models/units/power-density.ts new file mode 100644 index 0000000000..38ec33fd6e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/power-density.ts @@ -0,0 +1,65 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PowerDensityMetricUnits = 'mW/cm²' | 'W/cm²' | 'kW/cm²' | 'mW/m²' | 'W/m²' | 'kW/m²'; +export type PowerDensityImperialUnits = 'W/in²' | 'kW/in²'; + +export type PowerDensityUnits = PowerDensityMetricUnits | PowerDensityImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 0.00064516, + units: { + 'mW/cm²': { + name: 'unit.milliwatt-per-square-centimeter', + tags: ['power density', 'radiation intensity', 'sunlight intensity', 'signal power', 'intensity', 'milliwatts per square centimeter', 'UV Intensity', 'mW/cm²'], + to_anchor: 10000, + }, + 'W/cm²': { + name: 'unit.watt-per-square-centimeter', + tags: ['power density', 'intensity of power', 'watts per square centimeter', 'W/cm²'], + to_anchor: 10000, + }, + 'kW/cm²': { + name: 'unit.kilowatt-per-square-centimeter', + tags: ['power density', 'intensity of power', 'kilowatts per square centimeter', 'kW/cm²'], + to_anchor: 10000000, + }, + 'mW/m²': { + name: 'unit.milliwatt-per-square-meter', + tags: ['power density', 'intensity of power', 'milliwatts per square meter', 'mW/m²'], + to_anchor: 0.001, + }, + 'W/m²': { + name: 'unit.watt-per-square-meter', + tags: ['power density', 'intensity of power', 'watts per square meter', 'W/m²'], + to_anchor: 1, + }, + 'kW/m²': { + name: 'unit.kilowatt-per-square-meter', + tags: ['power density', 'intensity of power', 'kilowatts per square meter', 'kW/m²'], + to_anchor: 1000, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 0.00064516, + units: { + 'W/in²': { + name: 'unit.watt-per-square-inch', + tags: ['power density', 'intensity of power', 'watts per square inch', 'W/in²'], + to_anchor: 1, + }, + 'kW/in²': { + name: 'unit.kilowatt-per-square-inch', + tags: ['power density', 'intensity of power', 'kilowatts per square inch', 'kW/in²'], + to_anchor: 1000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/pressure.ts b/ui-ngx/src/app/shared/models/units/pressure.ts index ad5c9b0dbb..7443763754 100644 --- a/ui-ngx/src/app/shared/models/units/pressure.ts +++ b/ui-ngx/src/app/shared/models/units/pressure.ts @@ -36,7 +36,9 @@ export type PressureMetricUnits = | 'N/m²' | 'kN/m²' | 'kgf/m²' - | 'Pa/cm²'; + | 'Pa/cm²' + | 'J/m³' + | 'kg/m²'; export type PressureImperialUnits = 'psi' | 'ksi' | 'inHg' | 'psi/in²' | 'tonf/in²'; @@ -46,7 +48,7 @@ const METRIC: TbMeasureUnits = { Pa: { name: 'unit.pascal', tags: ['pressure', 'force', 'compression', 'tension', 'pascal', 'pascals', 'Pa', 'atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], - to_anchor: 0.001, // 1 Pa = 0.001 kPa + to_anchor: 0.001, }, kPa: { name: 'unit.kilopascal', @@ -133,6 +135,16 @@ const METRIC: TbMeasureUnits = { tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square centimeter', 'Pa/cm²'], to_anchor: 0.1, }, + 'J/m³': { + name: 'unit.joule-per-cubic-meter', + tags: ['energy density', 'joule per cubic meter', 'J/m³'], + to_anchor: 0.001, + }, + 'kg/m²': { + name: 'unit.kilogram-per-square-meter', + tags: ['density','surface density','areal density','mass per unit area','kg/m²'], + to_anchor: 0.00980665 + } }, }; diff --git a/ui-ngx/src/app/shared/models/units/radiance.ts b/ui-ngx/src/app/shared/models/units/radiance.ts new file mode 100644 index 0000000000..38fb646fab --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radiance.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadianceUnits = 'W/(m²·sr)'; + +const METRIC: TbMeasureUnits = { + units: { + 'W/(m²·sr)': { + name: 'unit.watt-per-square-metre-steradian', + tags: ['radiance', 'radiant flux density', 'wTape per square metre-steradian', 'W/(m²·sr)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radiant-intensity.ts b/ui-ngx/src/app/shared/models/units/radiant-intensity.ts new file mode 100644 index 0000000000..78ee45ee5d --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radiant-intensity.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadiantIntensityUnits = RadiantIntensityMetricUnits; +export type RadiantIntensityMetricUnits = 'W/sr'; + +const METRIC: TbMeasureUnits = { + units: { + 'W/sr': { + name: 'unit.watt-per-steradian', + tags: ['radiant intensity', 'power per unit solid angle', 'watt per steradian', 'W/sr'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radiation-dose.ts b/ui-ngx/src/app/shared/models/units/radiation-dose.ts new file mode 100644 index 0000000000..1619a48780 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radiation-dose.ts @@ -0,0 +1,50 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadiationDoseMetricUnits = 'Gy' | 'Sv' | 'Rad' | 'Rem' | 'R' | 'C/kg' | 'Gy/s'; +export type RadiationDoseUnits = RadiationDoseMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'Sv': { + name: 'unit.sievert', + tags: ['radiation dose', 'sievert', 'radiation dose equivalent', 'Sv'], + to_anchor: 1, + }, + 'Gy': { + name: 'unit.gray', + tags: ['radiation dose', 'absorbed dose', 'gray', 'Gy'], + to_anchor: 1, + }, + 'Rad': { + name: 'unit.rad', + tags: ['radiation dose', 'rad'], + to_anchor: 0.01, + }, + 'Rem': { + name: 'unit.rem', + tags: ['radiation dose equivalent', 'rem'], + to_anchor: 0.01, + }, + 'R': { + name: 'unit.roentgen', + tags: ['radiation exposure', 'roentgen', 'R'], + to_anchor: 0.0093, + }, + 'C/kg': { + name: 'unit.coulombs-per-kilogram', + tags: ['radiation exposure', 'dose', 'coulombs per kilogram', 'electric charge-to-mass ratio', 'C/kg'], + to_anchor: 34, + }, + 'Gy/s': { + name: 'unit.gy-per-second', + tags: ['absorbed dose rate', 'radiation dose rate', 'gray per second', 'Gy/s'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts b/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts new file mode 100644 index 0000000000..d357c11b72 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts @@ -0,0 +1,25 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadioactiveDecayRateUnits = 'Bq/s' | 'Ci/s'; + +const METRIC: TbMeasureUnits = { + transform: (Bq_s) => Bq_s / 3.7e10, // Convert Bq/s to Ci/s + units: { + 'Bq/s': { + name: 'unit.becquerels-per-second', + tags: ['radioactive decay rate', 'becquerels per second', 'Bq/s'], + to_anchor: 1, + }, + 'Ci/s': { + name: 'unit.curies-per-second', + tags: ['radioactive decay rate', 'curies per second', 'Ci/s'], + to_anchor: 3.7e10, + }, + } +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts b/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts new file mode 100644 index 0000000000..8036332f13 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts @@ -0,0 +1,25 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadioactivityConcentrationMetricUnits = 'Bq/m³' | 'Ci/L'; +export type RadioactivityConcentrationUnits = RadioactivityConcentrationMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'Bq/m³': { + name: 'unit.becquerels-per-cubic-meter', + tags: ['radioactivity', 'radiation', 'becquerels per cubic meter', 'Bq/m³'], + to_anchor: 1, + }, + 'Ci/L': { + name: 'unit.curies-per-liter', + tags: ['radioactivity', 'radiation', 'curies per liter', 'Ci/L'], + to_anchor: 3.7e10 * 1000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactivity.ts b/ui-ngx/src/app/shared/models/units/radioactivity.ts new file mode 100644 index 0000000000..8db76da7af --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radioactivity.ts @@ -0,0 +1,40 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadioactivityMetricUnits = 'Bq' | 'Ci' | 'Rd' | 'dps' | 'cps'; +export type RadioactivityUnits = RadioactivityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'Bq': { + name: 'unit.becquerel', + tags: ['radioactivity', 'decay rate', 'becquerel', 'Bq'], + to_anchor: 1, + }, + 'Ci': { + name: 'unit.curie', + tags: ['radioactivity', 'radiation', 'curie', 'Ci'], + to_anchor: 3.7e10, + }, + 'Rd': { + name: 'unit.rutherford', + tags: ['radioactive decay', 'radioactivity', 'rutherford', 'Rd'], + to_anchor: 1e6, + }, + 'dps': { + name: 'unit.dps', + tags: ['radioactive decay', 'radioactivity', 'disintegrations per second', 'dps'], + to_anchor: 1, + }, + cps: { + name: 'unit.cps', + tags: ['radiation detection', 'counts per second', 'cps'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/resistance.ts b/ui-ngx/src/app/shared/models/units/resistance.ts new file mode 100644 index 0000000000..6f14faf4c4 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/resistance.ts @@ -0,0 +1,46 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ResistanceMetricUnits = 'Ω' | 'μΩ' | 'mΩ' | 'kΩ' | 'MΩ' | 'GΩ'; + +export type ResistanceUnits = ResistanceMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'Ω': { + name: 'unit.ohm', + tags: ['electrical resistance', 'resistance', 'impedance', 'ohm'], + to_anchor: 1, + }, + 'μΩ': { + name: 'unit.microohm', + tags: ['electrical resistance', 'resistance', 'microohm', 'μΩ'], + to_anchor: 0.000001, + }, + 'mΩ': { + name: 'unit.milliohm', + tags: ['electrical resistance', 'resistance', 'milliohm', 'mΩ'], + to_anchor: 0.001, + }, + 'kΩ': { + name: 'unit.kilohm', + tags: ['electrical resistance', 'resistance', 'kilohm', 'kΩ'], + to_anchor: 1000, + }, + 'MΩ': { + name: 'unit.megohm', + tags: ['electrical resistance', 'resistance', 'megohm', 'MΩ'], + to_anchor: 1000000, + }, + 'GΩ': { + name: 'unit.gigohm', + tags: ['electrical resistance', 'resistance', 'gigohm', 'GΩ'], + to_anchor: 1000000000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/reynolds-number.ts b/ui-ngx/src/app/shared/models/units/reynolds-number.ts new file mode 100644 index 0000000000..a9e8b8c01e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/reynolds-number.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ReynoldsNumberMetricUnits = 'Re'; + +export type ReynoldsNumberUnits = ReynoldsNumberMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + Re: { + name: 'unit.reynolds', + tags: ['fluid flow regime', 'fluid mechanics', 'reynolds', 'Re'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/signal-strength.ts b/ui-ngx/src/app/shared/models/units/signal-strength.ts new file mode 100644 index 0000000000..ef6a595ea3 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/signal-strength.ts @@ -0,0 +1,31 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SignalStrengthMetricUnits = 'dBmV' | 'dBm' | 'rssi'; + +export type SignalStrengthUnits = SignalStrengthMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'dBmV': { + name: 'unit.dbmV', + tags: ['decibels millivolt', 'voltage level', 'signal', 'dBmV'], + to_anchor: 1, + }, + 'dBm': { + name: 'unit.dbm', + tags: ['decibel milliwatts', 'output power', 'signal', 'dBm'], + to_anchor: 1, + }, + 'rssi': { + name: 'unit.rssi', + tags: ['signal strength', 'signal level', 'received signal strength indicator', 'rssi', 'dBm'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/solid-angle.ts b/ui-ngx/src/app/shared/models/units/solid-angle.ts new file mode 100644 index 0000000000..c339884b65 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/solid-angle.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SolidAngleMetricUnits = 'sr'; + +export type SolidAngleUnits = SolidAngleMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'sr': { + name: 'unit.steradian', + tags: ['solid angle', 'spatial extent', 'steradian', 'sr'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/specific-energy.ts b/ui-ngx/src/app/shared/models/units/specific-energy.ts new file mode 100644 index 0000000000..3ca9d44221 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/specific-energy.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpecificEnergyMetricUnits = 'J/kg'; + +export type SpecificEnergyUnits = SpecificEnergyMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/kg': { + name: 'unit.joule-per-kilogram', + tags: ['specific energy', 'specific energy capacity', 'joule per kilogram', 'J/kg'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts new file mode 100644 index 0000000000..9bdf38e80c --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpecificHeatCapacityMetricUnits = 'J/(kg·K)'; + +export type SpecificHeatCapacityUnits = SpecificHeatCapacityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/(kg·K)': { + name: 'unit.joule-per-kilogram-kelvin', + tags: ['specific heat capacity', 'heat capacity per unit mass and temperature', 'joule per kilogram-kelvin', 'J/(kg·K)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/specific-volume.ts b/ui-ngx/src/app/shared/models/units/specific-volume.ts new file mode 100644 index 0000000000..9968454e41 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/specific-volume.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpecificVolumeMetricUnits = 'm³/kg'; + +export type SpecificVolumeUnits = SpecificVolumeMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'm³/kg': { + name: 'unit.cubic-meter-per-kilogram', + tags: ['specific volume', 'volume per unit mass', 'cubic meter per kilogram', 'm³/kg'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/speed.ts b/ui-ngx/src/app/shared/models/units/speed.ts index 095bb0c759..f44553a7a7 100644 --- a/ui-ngx/src/app/shared/models/units/speed.ts +++ b/ui-ngx/src/app/shared/models/units/speed.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type SpeedUnits = SpeedMetricUnits | SpeedImperialUnits; -export type SpeedMetricUnits = 'm/s' | 'km/h' | 'mm/min'; +export type SpeedMetricUnits = 'm/s' | 'km/h' | 'mm/min' | 'mm/s'; export type SpeedImperialUnits = 'mph' | 'kt' | 'ft/s' | 'ft/min' | 'in/h'; const METRIC: TbMeasureUnits = { @@ -39,6 +39,11 @@ const METRIC: TbMeasureUnits = { tags: ['feed rate', 'cutting feed rate', 'millimeters per minute', 'mm/min'], to_anchor: 0.06, }, + 'mm/s': { + name: 'unit.millimeters-per-second', + tags: ['velocity', 'speed', 'vibration rate', 'millimeters per second', 'mm/s'], + to_anchor: 0.0036, + }, }, }; diff --git a/ui-ngx/src/app/shared/models/units/sugar-content.ts b/ui-ngx/src/app/shared/models/units/sugar-content.ts new file mode 100644 index 0000000000..1badaf3ce0 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/sugar-content.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SugarContentUnits = '°Bx'; + +const METRIC: TbMeasureUnits = { + units: { + '°Bx': { + name: 'unit.degrees-brix', + tags: ['sugar content', 'fruit ripeness', 'Bx'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/surface-charge-density.ts b/ui-ngx/src/app/shared/models/units/surface-charge-density.ts new file mode 100644 index 0000000000..306649db3c --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/surface-charge-density.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SurfaceChargeDensityMetricUnits = 'C/m²'; + +export type SurfaceChargeDensityUnits = SurfaceChargeDensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C/m²': { + name: 'unit.coulomb-per-square-meter', + tags: ['electric surface charge density', 'coulomb per square meter', 'C/m²'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/surface-tension.ts b/ui-ngx/src/app/shared/models/units/surface-tension.ts new file mode 100644 index 0000000000..b1c5593354 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/surface-tension.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SurfaceTensionMetricUnits = 'N/m'; + +export type SurfaceTensionhUnits = SurfaceTensionMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'N/m': { + name: 'unit.newton-per-meter', + tags: ['linear density', 'force per unit length', 'newton per meter', 'N/m'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts b/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts new file mode 100644 index 0000000000..3c27727919 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ThermalConductivityMetricUnits = 'W/(m·K)'; + +export type ThermalConductivityUnits = ThermalConductivityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'W/(m·K)': { + name: 'unit.watt-per-meter-kelvin', + tags: ['thermal conductivity', 'watt per meter-kelvin', 'W/(m·K)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/turbidity.ts b/ui-ngx/src/app/shared/models/units/turbidity.ts new file mode 100644 index 0000000000..92b1122437 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/turbidity.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type TurbidityUnits = TurbidityMetricUnits; +export type TurbidityMetricUnits = 'NTU'; + +const METRIC: TbMeasureUnits = { + units: { + NTU: { + name: 'unit.turbidity', + tags: ['water turbidity', 'water clarity', 'Nephelometric Turbidity Units', 'NTU'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/volume-charge-density.ts b/ui-ngx/src/app/shared/models/units/volume-charge-density.ts new file mode 100644 index 0000000000..0b172962fd --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/volume-charge-density.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type VolumeChargeDensityMetricUnits = 'C/m³'; + +export type VolumeChargeDensityUnits = VolumeChargeDensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C/m³': { + name: 'unit.coulomb-per-cubic-meter', + tags: ['electric charge density', 'coulomb per cubic meter', 'C/m³'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/wavenumber.ts b/ui-ngx/src/app/shared/models/units/wavenumber.ts new file mode 100644 index 0000000000..6745601328 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/wavenumber.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type WavenumberMetricUnits = 'm⁻¹'; + +export type WavenumberUnits = WavenumberMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'm⁻¹': { + name: 'unit.reciprocal-metre', + tags: ['wavenumber', 'wave density', 'wave frequency', 'm⁻¹'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 3001671891..c685b6171e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6074,7 +6074,6 @@ "oersted": "Oersted", "bohr-magneton": "Bohr Magneton", "ampere-meter-squared": "Ampere-Meter Squared", - "ampere-meter": "Ampere-Meter", "nanovolt": "Nanovolt", "picovolt": "Picovolt", "millivolt": "Millivolts", @@ -6112,7 +6111,6 @@ "lux-second": "Lux second", "lumen-second": "Lumen second", "lumens-per-watt": "Lumens per watt", - "absorbance": "Absorbance", "mole": "Mole", "nanomole": "Nanomole", "micromole": "MicroMole", From 3aab8b7197862a81806e374f58435dd6b8c21a08 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 2 May 2025 20:31:36 +0300 Subject: [PATCH 126/335] lwm2m: create updateAttrTelemetry with updateResource - all change in one ArrayList to core --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 2 +- .../rpc/AbstractRpcLwM2MIntegrationTest.java | 3 +- .../sql/RpcLwm2mIntegrationCreateTest.java | 6 +- .../sql/RpcLwm2mIntegrationObserveTest.java | 8 +- .../rpc/sql/RpcLwm2mIntegrationReadTest.java | 14 +- .../sql/NoSecLwM2MIntegrationTest.java | 26 -- ...veStrategyTransportConfigurationTest.java} | 2 +- ...StrategyWithNoSecQueueModeConnectTest.java | 52 ++++ .../lwm2m/server/client/LwM2mClient.java | 16 -- .../server/client/ResultUpdateResource.java | 19 ++ .../TbLwM2MCreateResponseCallback.java | 2 +- .../uplink/DefaultLwM2mUplinkMsgHandler.java | 236 ++++++++++-------- .../server/uplink/LwM2mUplinkMsgHandler.java | 3 +- 13 files changed, 225 insertions(+), 164 deletions(-) rename application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/{TransportConfigurationTest.java => ObserveStrategyTransportConfigurationTest.java} (95%) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/ObserveStrategyWithNoSecQueueModeConnectTest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultUpdateResource.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 022221ac8e..5fec5107bd 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -354,7 +354,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte Assert.assertTrue(expectedMax >= Long.parseLong(tsValue1.getValue())); Assert.assertTrue(expectedMin <= Long.parseLong(tsValue1.getValue())); } else { - String pattern = "MMM dd, yyyy HH:mm a"; + String pattern = "MMM d, yyyy HH:mm a"; TbDate d = new TbDate(tsValue2.getValue(), pattern, "en-US"); Assert.assertNotNull(d); } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index a409ea0491..fe571eb9b3 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -29,6 +29,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; +import org.thingsboard.server.transport.lwm2m.server.client.ResultUpdateResource; import java.util.List; import java.util.Set; @@ -258,7 +259,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg .filter(invocation -> invocation.getMethod().getName().equals("updateAttrTelemetry") && invocation.getArguments().length > 1 && - idVerRez.equals(invocation.getArguments()[1]) + ((ResultUpdateResource)invocation.getArguments()[0]).getPaths().toString().contains(idVerRez) ) .count(); } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java index 6b39b51a65..ec0dcd7949 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java @@ -67,7 +67,7 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); String expected = "instance " + OBJECT_INSTANCE_ID_0 + " already exists"; String actual = rpcActualResult.get("error").asText(); - assertTrue(actual.equals(expected)); + assertEquals(actual, expected); } /** @@ -84,7 +84,7 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); String expected = "Path " + expectedPath + ". Object must be Multiple !"; String actual = rpcActualResult.get("error").asText(); - assertTrue(actual.equals(expected)); + assertEquals(actual, expected); } /** @@ -122,7 +122,7 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe LwM2mPath expectedPathId = new LwM2mPath(expectedObjectId); String expected = "Specified object id " + expectedPathId.getObjectId() + " absent in the list supported objects of the client or is security object!"; String actual = rpcActualResult.get("error").asText(); - assertTrue(actual.equals(expected)); + assertEquals(actual, expected); } private String sendRPCreateById(String path, String value) throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java index b1ab47717b..1ba89720ae 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java @@ -20,11 +20,11 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.LwM2m.Version; import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.node.LwM2mPath; -import org.eclipse.leshan.server.registration.Registration; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.server.client.ResultUpdateResource; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.junit.Assert.assertEquals; @@ -80,7 +80,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT int cntUpdate = 3; verify(defaultUplinkMsgHandlerTest, timeout(10000).atLeast(cntUpdate)) - .updateAttrTelemetry(Mockito.any(Registration.class), eq(idVer_3_0_9), eq(null)); + .updateAttrTelemetry(Mockito.any(ResultUpdateResource.class), eq(null)); } /** @@ -95,7 +95,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT int cntUpdate = 3; verify(defaultUplinkMsgHandlerTest, timeout(10000).atLeast(cntUpdate)) - .updateAttrTelemetry(Mockito.any(Registration.class), eq(idVer_3_0_9), eq(null)); + .updateAttrTelemetry(Mockito.any(ResultUpdateResource.class), eq(null)); } /** @@ -328,7 +328,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT int cntUpdate = 10; verify(defaultUplinkMsgHandlerTest, timeout(50000).atLeast(cntUpdate)) - .updateAttrTelemetry(Mockito.any(Registration.class), eq(idVer_3_0_9), eq(null)); + .updateAttrTelemetry(Mockito.any(ResultUpdateResource.class), eq(null)); } private void sendRpcObserveWithWithTwoResource(String expectedId_1, String expectedId_2) throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java index 9f45f02d7a..90b373b16c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java @@ -19,12 +19,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.node.LwM2mPath; -import org.junit.Before; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; -import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -140,24 +138,24 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest } /** - * ReadComposite {"ids":["/1_1.2", "/3_1.0/0/1", "/3_1.0/0/11"]} + * ReadComposite {"ids":["/19_1.1", "/3_1.0/0/1", "/3_1.0/0/11"]} */ @Test public void testReadCompositeSingleResourceByIds_Result_CONTENT_Value_IsObjectIsLwM2mSingleResourceIsLwM2mMultipleResource() throws Exception { - String expectedIdVer_1 = (String) expectedObjectIdVers.stream().filter(path -> (!((String) path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String) path).contains("/" + SERVER))).findFirst().get(); - String objectId_1 = pathIdVerToObjectId(expectedIdVer_1); + String expectedIdVer_19 = "/" + BINARY_APP_DATA_CONTAINER + "_" + lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(BINARY_APP_DATA_CONTAINER).version; + String objectId_19 = pathIdVerToObjectId(expectedIdVer_19); String expectedIdVer3_0_1 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_1; String expectedIdVer3_0_11 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_11; String objectInstanceId_3 = pathIdVerToObjectId(objectInstanceIdVer_3); - String expectedIds = "[\"" + expectedIdVer_1 + "\", \"" + expectedIdVer3_0_1 + "\", \"" + expectedIdVer3_0_11 + "\"]"; + String expectedIds = "[\"" + expectedIdVer_19 + "\", \"" + expectedIdVer3_0_1 + "\", \"" + expectedIdVer3_0_11 + "\"]"; String actualResult = sendCompositeRPCByIds(expectedIds); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String expected1 = objectId_1 + "=LwM2mObject [id=" + new LwM2mPath(objectId_1).getObjectId() + ", instances={"; + String expected19 = objectId_19 + "=LwM2mObject [id=" + new LwM2mPath(objectId_19).getObjectId() + ", instances={"; String expected3_0_1 = objectInstanceId_3 + "/" + RESOURCE_ID_1 + "=LwM2mSingleResource [id=" + RESOURCE_ID_1 + ", value="; String expected3_0_11 = objectInstanceId_3 + "/" + RESOURCE_ID_11 + "=LwM2mMultipleResource [id=" + RESOURCE_ID_11 + ", values={"; String actualValues = rpcActualResult.get("value").asText(); - assertTrue(actualValues.contains(expected1)); + assertTrue(actualValues.contains(expected19)); assertTrue(actualValues.contains(expected3_0_1)); assertTrue(actualValues.contains(expected3_0_11)); } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java index 34683ffc71..192dc6d1d0 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java @@ -17,10 +17,8 @@ package org.thingsboard.server.transport.lwm2m.security.sql; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; -import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; -import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.COMPOSITE_BY_OBJECT; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_REGISTRATION_SUCCESS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOOTSTRAP_ONLY; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH; @@ -37,30 +35,6 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT super.basicTestConnectionObserveSingleTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, false); } - @Test - public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveSingleTelemetry() throws Exception { - String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + "_QueueMode"; - LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); - super.basicTestConnectionObserveSingleTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, true); - } - - @Test - public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveCompositeAllTelemetry() throws Exception { - String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + "_QueueMode"; - LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); - Lwm2mDeviceProfileTransportConfiguration transportConfiguration = super.getTransportConfiguration(TELEMETRY_WITH_COMPOSITE_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); - super.basicTestConnectionObserveCompositeTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, transportConfiguration, 1); - } - - @Test - public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveCompositeByObjectTelemetry() throws Exception { - String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + "_QueueMode"; - LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); - Lwm2mDeviceProfileTransportConfiguration transportConfiguration = super.getTransportConfiguration(TELEMETRY_WITH_COMPOSITE_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); - transportConfiguration.getObserveAttr().setObserveStrategy(COMPOSITE_BY_OBJECT); - super.basicTestConnectionObserveCompositeTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, transportConfiguration, 2); - } - // Bootstrap + Lwm2m @Test public void testWithNoSecConnectBsSuccess_UpdateTwoSectionsBootstrapAndLm2m_ConnectLwm2mSuccess() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/TransportConfigurationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/ObserveStrategyTransportConfigurationTest.java similarity index 95% rename from application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/TransportConfigurationTest.java rename to application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/ObserveStrategyTransportConfigurationTest.java index 757bc81b41..c6a6bdad6d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/TransportConfigurationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/ObserveStrategyTransportConfigurationTest.java @@ -24,7 +24,7 @@ import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryO import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.SINGLE; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; -public class TransportConfigurationTest extends AbstractSecurityLwM2MIntegrationTest { +public class ObserveStrategyTransportConfigurationTest extends AbstractSecurityLwM2MIntegrationTest { @Test public void testTransportConfigurationObserveStrategyBeforeParseNullAfterParseNotNull_STRATEGY_SINGLE() throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/ObserveStrategyWithNoSecQueueModeConnectTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/ObserveStrategyWithNoSecQueueModeConnectTest.java new file mode 100644 index 0000000000..85b17a0f41 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/transportConfiguration/ObserveStrategyWithNoSecQueueModeConnectTest.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.transport.lwm2m.transportConfiguration; + +import org.junit.Test; +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; +import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; + +import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.COMPOSITE_BY_OBJECT; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; + +public class ObserveStrategyWithNoSecQueueModeConnectTest extends AbstractSecurityLwM2MIntegrationTest { + + @Test + public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveSingleTelemetry() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + "_ObserveSingle"; + LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); + super.basicTestConnectionObserveSingleTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, true); + } + + @Test + public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveCompositeAllTelemetry() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + "_ObserveCompositeAll"; + LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = super.getTransportConfiguration(TELEMETRY_WITH_COMPOSITE_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); + super.basicTestConnectionObserveCompositeTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, transportConfiguration, 1); + } + + @Test + public void testWithNoSecQueueModeConnectLwm2mSuccessAndObserveCompositeByObjectTelemetry() throws Exception { + String clientEndpoint = CLIENT_ENDPOINT_NO_SEC + "_ObserveCompositeByObject"; + LwM2MDeviceCredentials clientCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(clientEndpoint)); + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = super.getTransportConfiguration(TELEMETRY_WITH_COMPOSITE_OBSERVE, getBootstrapServerCredentialsNoSec(NONE)); + transportConfiguration.getObserveAttr().setObserveStrategy(COMPOSITE_BY_OBJECT); + super.basicTestConnectionObserveCompositeTelemetry(SECURITY_NO_SEC, clientCredentials, clientEndpoint, transportConfiguration, 2); + } +} + diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 6c470cf3b0..64597df9c5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -294,22 +294,6 @@ public class LwM2mClient { } - public Collection getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, - LwM2mValueConverter converter) { - LwM2mPath pathIds = getLwM2mPathFromString(pathRezIdVer); - Collection resources = ConcurrentHashMap.newKeySet(); - Map resourceModels = modelProvider.getObjectModel(registration) - .getObjectModel(pathIds.getObjectId()).resources; - resourceModels.forEach((resId, resourceModel) -> { - if (resId.equals(pathIds.getResourceId())) { - resources.add(LwM2mSingleResource.newResource(resId, converter.convertValue(params, - equalsResourceTypeGetSimpleName(params), resourceModel.type, pathIds), resourceModel.type)); - - } - }); - return resources; - } - /** * The instance must have all the resources that have the property * Mandatory diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultUpdateResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultUpdateResource.java new file mode 100644 index 0000000000..9a6c179eea --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultUpdateResource.java @@ -0,0 +1,19 @@ +package org.thingsboard.server.transport.lwm2m.server.client; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.HashSet; +import java.util.Set; + +@Data +@AllArgsConstructor +public class ResultUpdateResource { + LwM2mClient lwM2MClient; + Set paths; + + public ResultUpdateResource(LwM2mClient lwM2MClient) { + this.lwM2MClient = lwM2MClient; + this.paths = new HashSet<>(); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCreateResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCreateResponseCallback.java index f13a8524b8..20e40f6838 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCreateResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCreateResponseCallback.java @@ -30,7 +30,7 @@ public class TbLwM2MCreateResponseCallback extends TbLwM2MUplinkTargetedCallback @Override public void onSuccess(CreateRequest request, CreateResponse response) { super.onSuccess(request, response); - handler.onCreateResponseOk(client, versionedId, request); + handler.onCreatebjectInstancesResponseOk(client, versionedId, request); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index b7d6e0e7f5..8ac8296c38 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -19,7 +19,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import lombok.Getter; @@ -80,6 +79,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateExce import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; +import org.thingsboard.server.transport.lwm2m.server.client.ResultUpdateResource; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; @@ -112,11 +112,11 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Random; import java.util.Set; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -319,40 +319,41 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, modelProvider); if (objectModelVersion != null) { + ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); int responseCode = response.getCode().getCode(); if (content instanceof LwM2mObject) { - LwM2mObject lwM2mObject = (LwM2mObject) content; - this.updateObjectResourceValue(lwM2MClient, lwM2mObject, path, responseCode); + this.updateObjectResourceValue(updateResource, (LwM2mObject) content, path, responseCode); } else if (content instanceof LwM2mObjectInstance) { - LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) content; - this.updateObjectInstanceResourceValue(lwM2MClient, lwM2mObjectInstance, path, responseCode); + this.updateObjectInstanceResourceValue(updateResource, (LwM2mObjectInstance) content, path, responseCode); } else if (content instanceof LwM2mResource) { - LwM2mResource lwM2mResource = (LwM2mResource) content; - this.updateResourcesValue(lwM2MClient, lwM2mResource, path, Mode.UPDATE, responseCode); + this.updateResourcesValue(updateResource, (LwM2mResource) content, path, Mode.UPDATE, responseCode); } + this.updateAttrTelemetry(updateResource, null); } tryAwake(lwM2MClient); } } public void onUpdateValueAfterReadCompositeResponse(Registration registration, ReadCompositeResponse response) { - log.trace("ReadCompositeResponse: [{}]", response); + log.trace("ReadCompositeResponse before onUpdateValueAfterReadCompositeResponse: [{}]", response); if (response.getContent() != null) { LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); + ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); response.getContent().forEach((k, v) -> { if (v != null) { int responseCode = response.getCode().getCode(); if (v instanceof LwM2mObject) { - this.updateObjectResourceValue(lwM2MClient, (LwM2mObject) v, k.toString(), responseCode); + this.updateObjectResourceValue(updateResource, (LwM2mObject) v, k.toString(), responseCode); } else if (v instanceof LwM2mObjectInstance) { - this.updateObjectInstanceResourceValue(lwM2MClient, (LwM2mObjectInstance) v, k.toString(), responseCode); + this.updateObjectInstanceResourceValue(updateResource, (LwM2mObjectInstance) v, k.toString(), responseCode); } else if (v instanceof LwM2mResource) { - this.updateResourcesValue(lwM2MClient, (LwM2mResource) v, k.toString(), Mode.UPDATE, responseCode); + this.updateResourcesValue(updateResource, (LwM2mResource) v, k.toString(), Mode.UPDATE, responseCode); } } else { this.onErrorObservation(registration, k + ": value in composite response is null"); } }); + this.updateAttrTelemetry(updateResource, null); clientContext.update(lwM2MClient); tryAwake(lwM2MClient); } @@ -380,16 +381,15 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path.toString(), modelProvider); if (objectModelVersion != null) { + ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); if (node instanceof LwM2mObject) { - LwM2mObject lwM2mObject = (LwM2mObject) node; - this.updateObjectResourceValue(lwM2MClient, lwM2mObject, path.toString(), 0); + this.updateObjectResourceValue(updateResource, (LwM2mObject) node, path.toString(), 0); } else if (node instanceof LwM2mObjectInstance) { - LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) node; - this.updateObjectInstanceResourceValue(lwM2MClient, lwM2mObjectInstance, path.toString(), 0); + this.updateObjectInstanceResourceValue(updateResource, (LwM2mObjectInstance) node, path.toString(), 0); } else if (node instanceof LwM2mResource) { - LwM2mResource lwM2mResource = (LwM2mResource) node; - this.updateResourcesValueWithTs(lwM2MClient, lwM2mResource, path.toString(), Mode.UPDATE, ts); + this.updateResourcesValue(updateResource, (LwM2mResource) node, path.toString(), Mode.UPDATE, 0); } + this.updateAttrTelemetry(updateResource, ts); } tryAwake(lwM2MClient); } @@ -580,18 +580,18 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveCallback(logService, client, versionedId)); } - private void updateObjectResourceValue(LwM2mClient client, LwM2mObject lwM2mObject, String pathIdVer, int code) { + private void updateObjectResourceValue(ResultUpdateResource updateResource, LwM2mObject lwM2mObject, String pathIdVer, int code) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); lwM2mObject.getInstances().forEach((instanceId, instance) -> { String pathInstance = pathIds.toString() + "/" + instanceId; - this.updateObjectInstanceResourceValue(client, instance, pathInstance, code); + this.updateObjectInstanceResourceValue(updateResource, instance, pathInstance, code); }); } - private void updateObjectInstanceResourceValue(LwM2mClient client, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer, int code) { + private void updateObjectInstanceResourceValue(ResultUpdateResource updateResource, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer, int code) { lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> { String pathRez = pathIdVer + "/" + resourceId; - this.updateResourcesValue(client, resource, pathRez, Mode.UPDATE, code); + this.updateResourcesValue(updateResource, resource, pathRez, Mode.UPDATE, code); }); } @@ -601,56 +601,50 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl * #2 Update new Resources (replace old Resource Value on new Resource Value) * #3 If fr_update -> UpdateFirmware * #4 updateAttrTelemetry - * @param lwM2MClient - Registration LwM2M Client + * @param updateResource - result update resource by LwM2M Client * @param lwM2mResource - LwM2mSingleResource response.getContent() * @param stringPath - resource * @param mode - Replace, Update */ - private void updateResourcesValue(LwM2mClient lwM2MClient, LwM2mResource lwM2mResource, String stringPath, Mode mode, int code) { - Registration registration = lwM2MClient.getRegistration(); + private void updateResourcesValue(ResultUpdateResource updateResource, LwM2mResource lwM2mResource, String stringPath, Mode mode, int code) { + LwM2mClient lwM2MClient = updateResource.getLwM2MClient(); String path = convertObjectIdToVersionedId(stringPath, lwM2MClient); - if (lwM2MClient.saveResourceValue(path, lwM2mResource, modelProvider, mode)) { - if (path.equals(convertObjectIdToVersionedId(FW_NAME_ID, lwM2MClient))) { - otaService.onCurrentFirmwareNameUpdate(lwM2MClient, (String) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(FW_3_VER_ID, lwM2MClient))) { - otaService.onCurrentFirmwareVersion3Update(lwM2MClient, (String) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(FW_VER_ID, lwM2MClient))) { - otaService.onCurrentFirmwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(FW_STATE_ID, lwM2MClient))) { - otaService.onCurrentFirmwareStateUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(FW_RESULT_ID, lwM2MClient))) { - otaService.onCurrentFirmwareResultUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(FW_DELIVERY_METHOD, lwM2MClient))) { - otaService.onCurrentFirmwareDeliveryMethodUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(SW_NAME_ID, lwM2MClient))) { - otaService.onCurrentSoftwareNameUpdate(lwM2MClient, (String) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(SW_VER_ID, lwM2MClient))) { - otaService.onCurrentSoftwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(SW_3_VER_ID, lwM2MClient))) { - otaService.onCurrentSoftwareVersion3Update(lwM2MClient, (String) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(SW_STATE_ID, lwM2MClient))) { - otaService.onCurrentSoftwareStateUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); - } else if (path.equals(convertObjectIdToVersionedId(SW_RESULT_ID, lwM2MClient))) { - otaService.onCurrentSoftwareResultUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); - } + if (path != null && lwM2MClient.saveResourceValue(path, lwM2mResource, modelProvider, mode)) { + this.updateOtaResource(lwM2MClient, lwM2mResource, path); if (ResponseCode.BAD_REQUEST.getCode() > code) { - this.updateAttrTelemetry(registration, path, null); + updateResource.getPaths().add(path); } } else { log.error("Fail update path [{}] Resource [{}]", path, lwM2mResource); } } - private void updateResourcesValueWithTs(LwM2mClient lwM2MClient, LwM2mResource lwM2mResource, String stringPath, Mode mode, Instant ts) { - Registration registration = lwM2MClient.getRegistration(); - String path = convertObjectIdToVersionedId(stringPath, lwM2MClient); - if (lwM2MClient.saveResourceValue(path, lwM2mResource, modelProvider, mode)) { - this.updateAttrTelemetry(registration, path, ts); - } else { - log.error("Fail update path [{}] Resource [{}] with ts.", path, lwM2mResource); + + private void updateOtaResource(LwM2mClient lwM2MClient, LwM2mResource lwM2mResource, String path) { + if (path.equals(convertObjectIdToVersionedId(FW_NAME_ID, lwM2MClient))) { + otaService.onCurrentFirmwareNameUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_3_VER_ID, lwM2MClient))) { + otaService.onCurrentFirmwareVersion3Update(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_VER_ID, lwM2MClient))) { + otaService.onCurrentFirmwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_STATE_ID, lwM2MClient))) { + otaService.onCurrentFirmwareStateUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_RESULT_ID, lwM2MClient))) { + otaService.onCurrentFirmwareResultUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_DELIVERY_METHOD, lwM2MClient))) { + otaService.onCurrentFirmwareDeliveryMethodUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(SW_NAME_ID, lwM2MClient))) { + otaService.onCurrentSoftwareNameUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(SW_VER_ID, lwM2MClient))) { + otaService.onCurrentSoftwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(SW_3_VER_ID, lwM2MClient))) { + otaService.onCurrentSoftwareVersion3Update(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(SW_STATE_ID, lwM2MClient))) { + otaService.onCurrentSoftwareStateUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(SW_RESULT_ID, lwM2MClient))) { + otaService.onCurrentSoftwareResultUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); } } - /** * send Attribute and Telemetry to Thingsboard * #1 - get AttrName/TelemetryName with value from LwM2MClient: @@ -658,20 +652,20 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId) * #2 - set Attribute/Telemetry * - * @param registration - Registration LwM2M Client + * @param updateResource - updateResource resource of LwM2M Client */ - public void updateAttrTelemetry(Registration registration, String path, Instant ts) { - log.trace("UpdateAttrTelemetry paths [{}]", path); + public void updateAttrTelemetry(ResultUpdateResource updateResource, Instant ts) { + log.trace("UpdateAttrTelemetry paths [{}]", updateResource.getPaths()); try { - ResultsAddKeyValueProto results = this.getParametersFromProfile(registration, path); - SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration); + ResultsAddKeyValueProto results = this.getParametersFromProfile(updateResource); + SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(updateResource.getLwM2MClient().getRegistration()); if (results != null && sessionInfo != null) { if (results.getResultAttributes().size() > 0) { - log.trace("UpdateAttribute paths [{}] value [{}]", path, results.getResultAttributes().get(0).toString()); + log.trace("UpdateAttribute paths [{}] value [{}]", updateResource.getPaths(), results.getResultAttributes().get(0).toString()); this.helper.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo); } if (results.getResultTelemetries().size() > 0) { - log.trace("UpdateTelemetry paths [{}] value [{}] ts [{}]", path, results.getResultTelemetries().get(0).toString(), ts == null ? "null" : ts.toEpochMilli()); + log.trace("UpdateTelemetry paths [{}] value [{}] ts [{}]", updateResource.getPaths(), results.getResultTelemetries().get(0).toString(), ts == null ? "null" : ts.toEpochMilli()); this.helper.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo, null, ts); } } @@ -695,13 +689,6 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl return false; } - private ConcurrentHashMap getPathForWriteAttributes(JsonObject objectJson) { - ConcurrentHashMap pathAttributes = new Gson().fromJson(objectJson.toString(), - new TypeToken>() { - }.getType()); - return pathAttributes; - } - private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional deviceProfileOpt) { var oldProfile = clientContext.getProfile(lwM2MClient.getProfileId()); deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singletonList(lwM2MClient), oldProfile, deviceProfile)); @@ -712,31 +699,68 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl * // * @param attributes - new JsonObject * // * @param telemetry - new JsonObject * - * @param registration - Registration LwM2M Client - * @param path - + * @param updateResource - updateResource resource of LwM2M Client */ - private ResultsAddKeyValueProto getParametersFromProfile(Registration registration, String path) { + private ResultsAddKeyValueProto getParametersFromProfile(ResultUpdateResource updateResource) { + Registration registration = updateResource.getLwM2MClient().getRegistration(); + Set paths = updateResource.getPaths(); + ResultsAddKeyValueProto results = new ResultsAddKeyValueProto(); + var profile = clientContext.getProfile(registration); + List resultAttributes = new ArrayList<>(); + Set attributes = profile.getObserveAttr().getAttribute().stream() + .filter(paths::contains) + .collect(Collectors.toSet()); + if (!attributes.isEmpty()){ + attributes.stream() + .map(attr -> this.getKvToThingsBoard(attr, registration)) + .filter(Objects::nonNull) + .forEach(resultAttributes::add); + } + List resultTelemetries = new ArrayList<>(); + Set telemetries = profile.getObserveAttr().getTelemetry().stream() + .filter(paths::contains) + .collect(Collectors.toSet()); + if (!telemetries.isEmpty()){ + telemetries.stream() + .map(telemetry -> this.getKvToThingsBoard(telemetry, registration)) + .filter(Objects::nonNull) + .forEach(resultTelemetries::add); + } + if (resultAttributes.size() > 0) { + results.setResultAttributes(resultAttributes); + } + if (resultTelemetries.size() > 0) { + results.setResultTelemetries(resultTelemetries); + } + return results; + } + + private ResultsAddKeyValueProto getParametersFromProfile(Registration registration, Set path) { if (!path.isEmpty()) { ResultsAddKeyValueProto results = new ResultsAddKeyValueProto(); var profile = clientContext.getProfile(registration); List resultAttributes = new ArrayList<>(); - profile.getObserveAttr().getAttribute().forEach(pathIdVer -> { - if (path.equals(pathIdVer)) { - TransportProtos.KeyValueProto kvAttr = this.getKvToThingsBoard(pathIdVer, registration); - if (kvAttr != null) { - resultAttributes.add(kvAttr); - } - } - }); + Set attributes = profile.getObserveAttr().getAttribute().stream() + .map(LwM2MTransportUtil::fromVersionedIdToObjectId) + .filter(path::contains) + .collect(Collectors.toSet()); + if (!attributes.isEmpty()){ + attributes.stream() + .map(attr -> this.getKvToThingsBoard(attr, registration)) + .filter(Objects::nonNull) + .forEach(resultAttributes::add); + } List resultTelemetries = new ArrayList<>(); - profile.getObserveAttr().getTelemetry().forEach(pathIdVer -> { - if (path.contains(pathIdVer)) { - TransportProtos.KeyValueProto kvAttr = this.getKvToThingsBoard(pathIdVer, registration); - if (kvAttr != null) { - resultTelemetries.add(kvAttr); - } - } - }); + Set telemetries = profile.getObserveAttr().getTelemetry().stream() + .map(LwM2MTransportUtil::fromVersionedIdToObjectId) + .filter(path::contains) + .collect(Collectors.toSet()); + if (!telemetries.isEmpty()){ + telemetries.stream() + .map(telemetry -> this.getKvToThingsBoard(telemetry, registration)) + .filter(Objects::nonNull) + .forEach(resultTelemetries::add); + } if (resultAttributes.size() > 0) { results.setResultAttributes(resultAttributes); } @@ -792,41 +816,49 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl } @Override - public void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request, int code) { + public void onWriteResponseOk(LwM2mClient lwM2MClient, String path, WriteRequest request, int code) { + ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); if (request.getNode() instanceof LwM2mResource) { - this.updateResourcesValue(client, ((LwM2mResource) request.getNode()), path, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE, code); + this.updateResourcesValue(updateResource, ((LwM2mResource) request.getNode()), path, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE, code); } else if (request.getNode() instanceof LwM2mObjectInstance) { ((LwM2mObjectInstance) request.getNode()).getResources().forEach((resId, resource) -> { - this.updateResourcesValue(client, resource, path + "/" + resId, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE, code); + this.updateResourcesValue(updateResource, resource, path + "/" + resId, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE, code); }); } if (request.getNode() instanceof LwM2mResource || request.getNode() instanceof LwM2mObjectInstance) { - clientContext.update(client); + clientContext.update(lwM2MClient); } + this.updateAttrTelemetry(updateResource, null); } @Override - public void onCreateResponseOk(LwM2mClient client, String path, CreateRequest request) { - if (request.getObjectInstances() != null && request.getObjectInstances().size() > 0) { + public void onCreatebjectInstancesResponseOk(LwM2mClient lwM2MClient, String versionId, CreateRequest request) { + if (request.getObjectInstances() != null && !request.getObjectInstances().isEmpty()) { + ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); request.getObjectInstances().forEach(instance -> - instance.getResources() + instance.getResources().forEach((resId, lwM2mResource) ->{ + this.updateResourcesValue(updateResource, lwM2mResource, versionId + "/" + resId, Mode.REPLACE, 0); + }) ); - clientContext.update(client); + clientContext.update(lwM2MClient); + this.updateAttrTelemetry(updateResource, null); } } @Override - public void onWriteCompositeResponseOk(LwM2mClient client, WriteCompositeRequest request, int code) { + public void onWriteCompositeResponseOk(LwM2mClient lwM2MClient, WriteCompositeRequest request, int code) { log.trace("ReadCompositeResponse: [{}]", request.getNodes()); + ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); request.getNodes().forEach((k, v) -> { if (v instanceof LwM2mSingleResource) { - this.updateResourcesValue(client, (LwM2mResource) v, k.toString(), Mode.REPLACE, code); + this.updateResourcesValue(updateResource, (LwM2mResource) v, k.toString(), Mode.REPLACE, code); } else { LwM2mResourceInstance resourceInstance = (LwM2mResourceInstance) v; LwM2mMultipleResource multipleResource = new LwM2mMultipleResource(((LwM2mResourceInstance) v).getId(), resourceInstance.getType(), resourceInstance); - this.updateResourcesValue(client, multipleResource, k.toString(), Mode.REPLACE, code); + this.updateResourcesValue(updateResource, multipleResource, k.toString(), Mode.REPLACE, code); } }); + this.updateAttrTelemetry(updateResource, null); } //TODO: review and optimize the logic to minimize number of the requests to device. diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java index 64154fc7a4..0c28372bce 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java @@ -48,6 +48,7 @@ public interface LwM2mUplinkMsgHandler { void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response); void onUpdateValueAfterReadCompositeResponse(Registration registration, ReadCompositeResponse response); + void onErrorObservation(Registration registration, String errorMsg); void onUpdateValueWithSendRequest(Registration registration, TimestampedLwM2mNodes data); @@ -66,7 +67,7 @@ public interface LwM2mUplinkMsgHandler { void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request, int code); - void onCreateResponseOk(LwM2mClient client, String path, CreateRequest request); + void onCreatebjectInstancesResponseOk(LwM2mClient client, String path, CreateRequest request); void onWriteCompositeResponseOk(LwM2mClient client, WriteCompositeRequest request, int code); From 18916bf693164500f6bdbe317dd4167e2482bc4b Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Mon, 5 May 2025 08:32:31 +0300 Subject: [PATCH 127/335] added check for timestamp --- .../server/controller/CalculatedFieldController.java | 2 +- .../service/cf/ctx/state/BaseCalculatedFieldState.java | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java index 7e4e041600..a28146ab6e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java @@ -279,7 +279,7 @@ public class CalculatedFieldController extends BaseController { lastUpdateTimestamp = Math.max(lastUpdateTimestamp, maxTs); } } - return lastUpdateTimestamp; + return lastUpdateTimestamp == -1 ? System.currentTimeMillis() : lastUpdateTimestamp; } private & HasTenantId, I extends EntityId> void checkReferencedEntities(CalculatedFieldConfiguration calculatedFieldConfig, SecurityUser user) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java index 35879ac9aa..e4b03b4cab 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java @@ -113,10 +113,8 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { if (entry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { this.lastUpdateTimestamp = singleValueArgumentEntry.getTs(); } else if (entry instanceof TsRollingArgumentEntry tsRollingArgumentEntry) { - Map.Entry lastEntry = tsRollingArgumentEntry.getTsRecords().pollLastEntry(); - if (lastEntry != null) { - this.lastUpdateTimestamp = lastEntry.getKey(); - } + Map.Entry lastEntry = tsRollingArgumentEntry.getTsRecords().lastEntry(); + this.lastUpdateTimestamp = (lastEntry != null) ? lastEntry.getKey() : System.currentTimeMillis(); } } From 5993b8b9638606f794295825908d733df27f0ef0 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 5 May 2025 10:50:39 +0300 Subject: [PATCH 128/335] Minor refactoring --- .../server/common/msg/queue/TbCallback.java | 17 ++++++++++++++++- .../server/queue/task/TaskProcessor.java | 9 +++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbCallback.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbCallback.java index ee8990d931..777ce3bf94 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbCallback.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbCallback.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.msg.queue; +import com.google.common.util.concurrent.SettableFuture; import org.thingsboard.server.common.data.id.EntityId; import java.util.UUID; @@ -34,7 +35,7 @@ public interface TbCallback { } }; - default UUID getId(){ + default UUID getId() { return EntityId.NULL_UUID; } @@ -42,4 +43,18 @@ public interface TbCallback { void onFailure(Throwable t); + static TbCallback wrap(SettableFuture future) { + return new TbCallback() { + @Override + public void onSuccess() { + future.set(null); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }; + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index d243ef514d..30a9929dfb 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -183,6 +183,15 @@ public abstract class TaskProcessor, R extends TaskResult> { discardedJobs.add(jobId); } + protected V wait(Future future) throws Exception { + try { + return future.get(); // will be interrupted after task processing timeout + } catch (InterruptedException e) { + future.cancel(true); // interrupting the underlying task + throw e; + } + } + @PreDestroy public void destroy() { taskConsumer.stop(); From f6bb9be7802c1e85cce6850e0ef992e19e4ac496 Mon Sep 17 00:00:00 2001 From: yuliaklochai Date: Mon, 5 May 2025 11:38:16 +0300 Subject: [PATCH 129/335] UI: trendz settings fixes --- .../pages/admin/trendz-settings.component.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts index ffd2898d2a..c7f5846063 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.ts @@ -14,15 +14,14 @@ /// limitations under the License. /// -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, DestroyRef } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; -import { select, Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TrendzSettingsService } from '@core/http/trendz-settings.service'; import { TrendzSettings } from '@shared/models/trendz-settings.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-trendz-settings', @@ -33,10 +32,10 @@ export class TrendzSettingsComponent extends PageComponent implements OnInit, Ha trendzSettingsForm: FormGroup; - constructor(protected store: Store, - private fb: FormBuilder, - private trendzSettingsService: TrendzSettingsService) { - super(store); + constructor(private fb: FormBuilder, + private trendzSettingsService: TrendzSettingsService, + private destroyRef: DestroyRef) { + super(); } ngOnInit() { @@ -50,26 +49,26 @@ export class TrendzSettingsComponent extends PageComponent implements OnInit, Ha }); this.trendzSettingsForm.get('isTrendzEnabled').valueChanges + .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((enabled: boolean) => this.toggleUrlRequired(enabled)); } toggleUrlRequired(enabled: boolean) { const trendzUrlControl = this.trendzSettingsForm.get('trendzUrl')!; - const validators = [Validators.pattern(/^(https?:\/\/)[^\s/$.?#].[^\s]*$/i)]; if (enabled) { - validators.push(Validators.required); + trendzUrlControl.addValidators(Validators.required); + } else { + trendzUrlControl.removeValidators(Validators.required); } - trendzUrlControl.setValidators(validators); trendzUrlControl.updateValueAndValidity(); } setTrendzSettings(trendzSettings: TrendzSettings) { this.trendzSettingsForm.reset({ trendzUrl: trendzSettings?.baseUrl, - isTrendzEnabled: isDefinedAndNotNull(trendzSettings?.enabled) ? - trendzSettings?.enabled : false + isTrendzEnabled: trendzSettings?.enabled ?? false }); this.toggleUrlRequired(this.trendzSettingsForm.get('isTrendzEnabled').value); From 1e5b6cc9a9cfe50bae5fb1db30c09bd25f9b151c Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 5 May 2025 15:45:56 +0300 Subject: [PATCH 130/335] Remove task.getKey() --- .../thingsboard/server/common/data/job/task/DummyTask.java | 5 ----- .../org/thingsboard/server/common/data/job/task/Task.java | 4 ---- .../org/thingsboard/server/queue/task/TaskProcessor.java | 4 ++-- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java index 39e1306597..d7a37b5175 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java @@ -36,11 +36,6 @@ public class DummyTask extends Task { private List errors; // errors for each attempt private boolean failAlways; - @Override - public Object getKey() { - return number; - } - @Override public DummyTaskResult toFailed(Throwable error) { return DummyTaskResult.failed(this, error); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java index 624c5e90f8..6bf287b54a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.common.data.job.task; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; @@ -46,9 +45,6 @@ public abstract class Task { private int attempt = 0; - @JsonIgnore - public abstract Object getKey(); - public abstract R toFailed(Throwable error); public abstract R toDiscarded(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 30a9929dfb..38d3d02b50 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -115,11 +115,11 @@ public abstract class TaskProcessor, R extends TaskResult> { @SuppressWarnings("unchecked") T task = (T) JacksonUtil.fromString(msg.getValue().getValue(), Task.class); if (discardedJobs.contains(task.getJobId().getId())) { - log.info("Skipping task '{}' for cancelled job {}", task.getKey(), task.getJobId()); + log.debug("Skipping task for cancelled job {}: {}", task.getJobId(), task); reportTaskDiscarded(task); continue; } else if (deletedTenants.contains(task.getTenantId().getId())) { - log.info("Skipping task '{}' for deleted tenant {}", task.getKey(), task.getTenantId()); + log.debug("Skipping task for deleted tenant {}: {}", task.getTenantId(), task); continue; } processTask(task); From 26a41b7c32aebbdaf8aa3d95fc8358d112c25dc2 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 5 May 2025 17:41:30 +0300 Subject: [PATCH 131/335] Introduce TbTelemetryService --- .../controller/TelemetryController.java | 23 ++--- .../telemetry/DefaultTbTelemetryService.java | 92 +++++++++++++++++++ .../service/telemetry/TbTelemetryService.java | 43 +++++++++ 3 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTbTelemetryService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/telemetry/TbTelemetryService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index 6ca767dc01..d93e973073 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -92,6 +92,7 @@ import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.telemetry.AttributeData; +import org.thingsboard.server.service.telemetry.TbTelemetryService; import org.thingsboard.server.service.telemetry.TsData; import java.util.ArrayList; @@ -155,6 +156,9 @@ public class TelemetryController extends BaseController { @Autowired private AccessValidator accessValidator; + @Autowired + private TbTelemetryService tbTelemetryService; + @Value("${transport.json.max_string_value_length:0}") private int maxStringValueLength; @@ -323,20 +327,11 @@ public class TelemetryController extends BaseController { @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, @Parameter(description = STRICT_DATA_TYPES_DESCRIPTION) @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> { - AggregationParams params; - Aggregation agg = Aggregation.valueOf(aggStr); - if (Aggregation.NONE.equals(agg)) { - params = AggregationParams.none(); - } else if (intervalType == null || IntervalType.MILLISECONDS.equals(intervalType)) { - params = interval == 0L ? AggregationParams.none() : AggregationParams.milliseconds(agg, interval); - } else { - params = AggregationParams.calendar(agg, intervalType, timeZone); - } - List queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, params, limit, orderBy)).collect(Collectors.toList()); - Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); - }); + DeferredResult response = new DeferredResult<>(); + Futures.addCallback(tbTelemetryService.getTimeseries(EntityIdFactory.getByTypeAndId(entityType, entityIdStr), toKeysList(keys), startTs, endTs, + intervalType, interval, timeZone, limit, Aggregation.valueOf(aggStr), orderBy, useStrictDataTypes, getCurrentUser()), + getTsKvListCallback(response, useStrictDataTypes), MoreExecutors.directExecutor()); + return response; } @ApiOperation(value = "Save device attributes (saveDeviceAttributes)", diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTbTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTbTelemetryService.java new file mode 100644 index 0000000000..d55f44a027 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTbTelemetryService.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.telemetry; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.AggregationParams; +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; +import org.thingsboard.server.common.data.kv.IntervalType; +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.service.security.AccessValidator; +import org.thingsboard.server.service.security.ValidationResult; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Slf4j +@RequiredArgsConstructor +public class DefaultTbTelemetryService implements TbTelemetryService { + + private final TimeseriesService tsService; + private final AccessValidator accessValidator; + + @Override + public ListenableFuture> getTimeseries(EntityId entityId, List keys, Long startTs, Long endTs, IntervalType intervalType, + Long interval, String timeZone, Integer limit, Aggregation agg, String orderBy, + Boolean useStrictDataTypes, SecurityUser currentUser) { + SettableFuture> future = SettableFuture.create(); + accessValidator.validate(currentUser, Operation.READ_TELEMETRY, entityId, new FutureCallback<>() { + @Override + public void onSuccess(ValidationResult validationResult) { + try { + AggregationParams params; + if (Aggregation.NONE.equals(agg)) { + params = AggregationParams.none(); + } else if (intervalType == null || IntervalType.MILLISECONDS.equals(intervalType)) { + params = interval == 0L ? AggregationParams.none() : AggregationParams.milliseconds(agg, interval); + } else { + params = AggregationParams.calendar(agg, intervalType, timeZone); + } + List queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, params, limit, orderBy)).collect(Collectors.toList()); + Futures.addCallback(tsService.findAll(currentUser.getTenantId(), entityId, queries), new FutureCallback<>() { + @Override + public void onSuccess(List result) { + future.set(result); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }, MoreExecutors.directExecutor()); + } catch (Throwable e) { + onFailure(e); + } + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }); + return future; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TbTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TbTelemetryService.java new file mode 100644 index 0000000000..9820e62592 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TbTelemetryService.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.telemetry; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.IntervalType; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.service.security.model.SecurityUser; + +import java.util.List; + +public interface TbTelemetryService { + + ListenableFuture> getTimeseries(EntityId entityId, + List keys, + Long startTs, + Long endTs, + IntervalType intervalType, + Long interval, + String timeZone, + Integer limit, + Aggregation agg, + String orderBy, + Boolean useStrictDataTypes, + SecurityUser currentUser) throws ThingsboardException; + +} From 43488081fc3ea35a394f4ac0b5e3acac978a1bd0 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 5 May 2025 13:46:29 +0300 Subject: [PATCH 132/335] UI: Updated unit definition --- ui-ngx/src/app/shared/models/unit.models.ts | 157 +++++++++++++++++- .../models/units/Magnetic-permeability.ts | 37 ----- .../app/shared/models/units/acceleration.ts | 8 +- .../shared/models/units/air-quality-index.ts | 18 +- .../models/units/amount-of-substance.ts | 32 +++- ui-ngx/src/app/shared/models/units/angle.ts | 20 +-- .../models/units/angular-acceleration.ts | 4 +- ui-ngx/src/app/shared/models/units/area.ts | 26 +-- .../app/shared/models/units/blood-glucose.ts | 20 --- .../app/shared/models/units/capacitance.ts | 34 +++- .../shared/models/units/catalytic-activity.ts | 24 ++- .../models/units/catalytic-concentration.ts | 35 ++++ ui-ngx/src/app/shared/models/units/charge.ts | 16 +- .../models/units/concentration-gradient.ts | 40 ----- .../models/units/concentration-volume.ts | 63 ------- .../shared/models/units/current-density.ts | 26 ++- .../shared/models/units/data-transfer-rate.ts | 25 ++- ui-ngx/src/app/shared/models/units/density.ts | 62 ++++++- ui-ngx/src/app/shared/models/units/digital.ts | 26 ++- .../shared/models/units/dimension-ratio.ts | 35 ++++ .../models/units/dimensionless-ratio.ts | 21 --- .../shared/models/units/dynamic-viscosity.ts | 30 +++- .../models/units/earthquake-magnitude.ts | 18 +- .../shared/models/units/electric-current.ts | 22 ++- .../models/units/electric-dipole-moment.ts | 39 +++++ .../models/units/electric-field-strength.ts | 19 ++- .../app/shared/models/units/electric-flux.ts | 28 ++-- .../shared/models/units/electric-moment.ts | 26 --- .../models/units/electric-permittivity.ts | 23 ++- .../models/units/electrical-conductance.ts | 22 ++- .../models/units/electrical-conductivity.ts | 27 ++- ui-ngx/src/app/shared/models/units/energy.ts | 26 +-- ui-ngx/src/app/shared/models/units/force.ts | 9 +- .../src/app/shared/models/units/frequency.ts | 25 ++- .../shared/models/units/fuel-efficiency.ts | 20 ++- .../app/shared/models/units/heat-capacity.ts | 26 ++- .../src/app/shared/models/units/humidity.ts | 20 --- .../app/shared/models/units/illuminance.ts | 8 +- .../src/app/shared/models/units/inductance.ts | 16 ++ .../models/units/kinematic-viscosity.ts | 26 ++- ui-ngx/src/app/shared/models/units/length.ts | 50 +++--- .../app/shared/models/units/light-exposure.ts | 24 ++- .../models/units/liner-charge-density.ts | 24 ++- .../shared/models/units/logarithmic-ratio.ts | 45 +++++ .../shared/models/units/logarithmic-units.ts | 29 ---- .../shared/models/units/luminous-efficacy.ts | 24 ++- .../app/shared/models/units/luminous-flux.ts | 24 ++- .../shared/models/units/luminous-intensity.ts | 24 ++- .../models/units/magnetic-field-gradient.ts | 23 ++- .../app/shared/models/units/magnetic-field.ts | 71 -------- .../models/units/magnetic-flux-density.ts | 84 ++++++++++ .../app/shared/models/units/magnetic-flux.ts | 31 ++-- .../shared/models/units/magnetic-moment.ts | 28 +++- .../models/units/magnetic-permeability.ts | 40 +++++ .../app/shared/models/units/mass-fraction.ts | 35 ++++ ui-ngx/src/app/shared/models/units/mass.ts | 32 ++-- .../models/units/molar-concentration.ts | 35 ++++ .../app/shared/models/units/molar-energy.ts | 23 ++- .../models/units/molar-heat-capacity.ts | 23 ++- .../src/app/shared/models/units/molar-mass.ts | 19 ++- .../shared/models/units/specific-humidity.ts | 35 ++++ .../assets/locale/locale.constant-en_US.json | 36 ++++ 62 files changed, 1280 insertions(+), 638 deletions(-) delete mode 100644 ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts delete mode 100644 ui-ngx/src/app/shared/models/units/blood-glucose.ts create mode 100644 ui-ngx/src/app/shared/models/units/catalytic-concentration.ts delete mode 100644 ui-ngx/src/app/shared/models/units/concentration-gradient.ts delete mode 100644 ui-ngx/src/app/shared/models/units/concentration-volume.ts create mode 100644 ui-ngx/src/app/shared/models/units/dimension-ratio.ts delete mode 100644 ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts delete mode 100644 ui-ngx/src/app/shared/models/units/electric-moment.ts delete mode 100644 ui-ngx/src/app/shared/models/units/humidity.ts create mode 100644 ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts delete mode 100644 ui-ngx/src/app/shared/models/units/logarithmic-units.ts delete mode 100644 ui-ngx/src/app/shared/models/units/magnetic-field.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-flux-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-permeability.ts create mode 100644 ui-ngx/src/app/shared/models/units/mass-fraction.ts create mode 100644 ui-ngx/src/app/shared/models/units/molar-concentration.ts create mode 100644 ui-ngx/src/app/shared/models/units/specific-humidity.ts diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 27cca5f603..c2763ff632 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -15,21 +15,59 @@ /// import acceleration, { AccelerationUnits } from '@shared/models/units/acceleration'; +import airQualityIndex, { AirQualityIndexUnits } from '@shared/models/units/air-quality-index'; +import amountOfSubstance, { AmountOfSubstanceUnits } from '@shared/models/units/amount-of-substance'; import angle, { AngleUnits } from '@shared/models/units/angle'; import angularAcceleration, { AngularAccelerationUnits } from '@shared/models/units/angular-acceleration'; import area, { AreaUnits } from '@shared/models/units/area'; +import capacitance, { CapacitanceUnits } from '@shared/models/units/capacitance'; +import catalyticActivity, { CatalyticActivityUnits } from '@shared/models/units/catalytic-activity'; +import catalyticConcentration, { CatalyticConcentrationUnits } from '@shared/models/units/catalytic-concentration'; import charge, { ChargeUnits } from '@shared/models/units/charge'; +import currentDensity, { CurrentDensityUnits } from '@shared/models/units/current-density'; +import dataTransferRate, { DataTransferRateUnits } from '@shared/models/units/data-transfer-rate'; +import density, { DensityUnits } from '@shared/models/units/density'; import digital, { DigitalUnits } from '@shared/models/units/digital'; +import dimensionRatio, { DimensionRatioUnits } from '@shared/models/units/dimension-ratio'; +import dynamicViscosity, { DynamicViscosityUnits } from '@shared/models/units/dynamic-viscosity'; +import earthquakeMagnitude, { EarthquakeMagnitudeUnits } from '@shared/models/units/earthquake-magnitude'; import electricCurrent, { ElectricCurrentUnits } from '@shared/models/units/electric-current'; +import electricDipoleMoment, { ElectricDipoleMomentUnits } from '@shared/models/units/electric-dipole-moment'; +import electricFieldStrength, { ElectricFieldStrengthUnits } from '@shared/models/units/electric-field-strength'; +import electricFlux, { ElectricFluxUnits } from '@shared/models/units/electric-flux'; +import electricPermittivity, { ElectricPermittivityUnits } from '@shared/models/units/electric-permittivity'; +import electricalConductance, { ElectricalConductanceUnits } from '@shared/models/units/electrical-conductance'; +import electricalConductivity, { ElectricalConductivityUnits } from '@shared/models/units/electrical-conductivity'; import energy, { EnergyUnits } from '@shared/models/units/energy'; import force, { ForceUnits } from '@shared/models/units/force'; +import fuelEfficiency, { FuelEfficiencyUnits } from '@shared/models/units/fuel-efficiency'; import frequency, { FrequencyUnits } from '@shared/models/units/frequency'; +import heatCapacity, { HeatCapacityUnits } from '@shared/models/units/heat-capacity'; import illuminance, { IlluminanceUnits } from '@shared/models/units/illuminance'; +import inductance, { InductanceUnits } from '@shared/models/units/inductance'; +import kinematicViscosity, { KinematicViscosityUnits } from '@shared/models/units/kinematic-viscosity'; import length, { LengthUnits } from '@shared/models/units/length'; +import lightExposure, { LightExposureUnits } from '@shared/models/units/light-exposure'; +import linerChargeDensity, { LinerChargeDensityUnits } from '@shared/models/units/liner-charge-density'; +import logarithmicRatio, { LogarithmicRatioUnits } from '@shared/models/units/logarithmic-ratio'; +import luminousEfficacy, { LuminousEfficacyUnits } from '@shared/models/units/luminous-efficacy'; +import luminousFlux, { LuminousFluxUnits } from '@shared/models/units/luminous-flux'; +import luminousIntensity, { LuminousIntensityUnits } from '@shared/models/units/luminous-intensity'; +import magneticFieldGradient, { MagneticFieldGradientUnits } from '@shared/models/units/magnetic-field-gradient'; +import magneticFlux, { MagneticFluxUnits } from '@shared/models/units/magnetic-flux'; +import magneticFluxDensity, { MagneticFluxDensityUnits } from '@shared/models/units/magnetic-flux-density'; +import magneticMoment, { MagneticMomentUnits } from '@shared/models/units/magnetic-moment'; +import magneticPermeability, { MagneticPermeabilityUnits } from '@shared/models/units/magnetic-permeability'; import mass, { MassUnits } from '@shared/models/units/mass'; +import massFraction, { MassFractionUnits } from '@shared/models/units/mass-fraction'; +import molarConcentration, { MolarConcentrationUnits } from '@shared/models/units/molar-concentration'; +import molarEnergy, { MolarEnergyUnits } from '@shared/models/units/molar-energy'; +import molarHeatCapacity, { MolarHeatCapacityUnits } from '@shared/models/units/molar-heat-capacity'; +import molarMass, { MolarMassUnits } from '@shared/models/units/molar-mass'; import partsPer, { PartsPerUnits } from '@shared/models/units/parts-per'; import power, { PowerUnits } from '@shared/models/units/power'; import pressure, { PressureUnits } from '@shared/models/units/pressure'; +import specificHumidity, { SpecificHumidityUnits } from '@shared/models/units/specific-humidity'; import speed, { SpeedUnits } from '@shared/models/units/speed'; import temperature, { TemperatureUnits } from '@shared/models/units/temperature'; import time, { TimeUnits } from '@shared/models/units/time'; @@ -41,21 +79,59 @@ import { TranslateService } from '@ngx-translate/core'; export type AllMeasuresUnits = | AccelerationUnits + | AirQualityIndexUnits + | AmountOfSubstanceUnits | AngleUnits | AngularAccelerationUnits | AreaUnits + | CapacitanceUnits + | CatalyticActivityUnits + | CatalyticConcentrationUnits | ChargeUnits + | CurrentDensityUnits + | DataTransferRateUnits + | DensityUnits | DigitalUnits + | DimensionRatioUnits + | DynamicViscosityUnits + | EarthquakeMagnitudeUnits | ElectricCurrentUnits + | ElectricDipoleMomentUnits + | ElectricFieldStrengthUnits + | ElectricFluxUnits + | ElectricPermittivityUnits + | ElectricalConductanceUnits + | ElectricalConductivityUnits | EnergyUnits | ForceUnits | FrequencyUnits + | FuelEfficiencyUnits + | HeatCapacityUnits | IlluminanceUnits + | InductanceUnits + | KinematicViscosityUnits | LengthUnits + | LightExposureUnits + | LinerChargeDensityUnits + | LogarithmicRatioUnits + | LuminousEfficacyUnits + | LuminousFluxUnits + | LuminousIntensityUnits + | MagneticFieldGradientUnits + | MagneticFluxUnits + | MagneticFluxDensityUnits + | MagneticMomentUnits + | MagneticPermeabilityUnits | MassUnits + | MassFractionUnits + | MolarConcentrationUnits + | MolarEnergyUnits + | MolarHeatCapacityUnits + | MolarMassUnits | PartsPerUnits | PowerUnits | PressureUnits + | SpecificHumidityUnits | SpeedUnits | TemperatureUnits | TimeUnits @@ -66,21 +142,59 @@ export type AllMeasuresUnits = export type AllMeasures = | 'acceleration' + | 'air-quality-index' + | 'amount-of-substance' | 'angle' | 'angular-acceleration' | 'area' + | 'capacitance' + | 'catalytic-activity' + | 'catalytic-concentration' | 'charge' + | 'current-density' + | 'data-transfer-rate' + | 'density' | 'digital' + | 'dimension-ratio' + | 'dynamic-viscosity' + | 'earthquake-magnitude' | 'electric-current' + | 'electric-dipole-moment' + | 'electric-field-strength' + | 'electric-flux' + | 'electric-permittivity' + | 'electrical-conductance' + | 'electrical-conductivity' | 'energy' | 'force' | 'frequency' + | 'fuel-efficiency' + | 'heat-capacity' | 'illuminance' + | 'inductance' + | 'kinematic-viscosity' | 'length' + | 'light-exposure' + | 'linear-charge-density' + | 'logarithmic-ratio' + | 'luminous-efficacy' + | 'luminous-flux' + | 'luminous-intensity' + | 'magnetic-field-gradient' + | 'magnetic-flux' + | 'magnetic-flux-density' + | 'magnetic-moment' + | 'magnetic-permeability' | 'mass' + | 'mass-fraction' + | 'molar-concentration' + | 'molar-energy' + | 'molar-heat-capacity' + | 'molar-mass' | 'parts-per' | 'power' | 'pressure' + | 'specific-humidity' | 'speed' | 'temperature' | 'time' @@ -94,21 +208,59 @@ const allMeasures: Record< TbMeasure > = Object.freeze({ acceleration, + 'air-quality-index': airQualityIndex, + 'amount-of-substance': amountOfSubstance, angle, 'angular-acceleration': angularAcceleration, area, + capacitance, + 'catalytic-activity': catalyticActivity, + 'catalytic-concentration': catalyticConcentration, charge, + 'current-density': currentDensity, + 'data-transfer-rate': dataTransferRate, + density, digital, + 'dimension-ratio': dimensionRatio, + 'dynamic-viscosity': dynamicViscosity, + 'earthquake-magnitude': earthquakeMagnitude, 'electric-current': electricCurrent, + 'electric-dipole-moment': electricDipoleMoment, + 'electric-field-strength': electricFieldStrength, + 'electric-flux': electricFlux, + 'electric-permittivity': electricPermittivity, + 'electrical-conductance': electricalConductance, + 'electrical-conductivity': electricalConductivity, energy, force, frequency, + 'fuel-efficiency': fuelEfficiency, + 'heat-capacity': heatCapacity, illuminance, + inductance, + 'kinematic-viscosity': kinematicViscosity, length, + 'light-exposure': lightExposure, + 'linear-charge-density': linerChargeDensity, + 'logarithmic-ratio': logarithmicRatio, + 'luminous-efficacy': luminousEfficacy, + 'luminous-flux': luminousFlux, + 'luminous-intensity': luminousIntensity, + 'magnetic-field-gradient': magneticFieldGradient, + 'magnetic-flux': magneticFlux, + 'magnetic-flux-density': magneticFluxDensity, + 'magnetic-moment': magneticMoment, + 'magnetic-permeability': magneticPermeability, mass, + 'mass-fraction': massFraction, + 'molar-concentration': molarConcentration, + 'molar-energy': molarEnergy, + 'molar-heat-capacity': molarHeatCapacity, + 'molar-mass': molarMass, 'parts-per': partsPer, power, pressure, + 'specific-humidity': specificHumidity, speed, temperature, time, @@ -143,7 +295,7 @@ export const UnitSystems = Object.values(UnitSystem); export interface Unit { name: string; - tags: string[]; + tags?: string[]; to_anchor: number; anchor_shift?: number; transform?: (value: number) => number; @@ -407,6 +559,9 @@ function buildUnitCache(measures: Record[]) { unit.name = translate.instant(unit.name); + const measureNameTranslation = translate.instant('unit.measures.' + measureName); + unit.tags = unit.tags ?? []; + unit.tags.push(testAbbr, unit.name, measureNameTranslation); unitCache.set(testAbbr, { measure: measureName, system: systemName, diff --git a/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts b/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts deleted file mode 100644 index be120c4b15..0000000000 --- a/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type MagneticPermeabilityMetricUnits = 'H/m'; -export type MagneticPermeabilityImperialUnits = 'G/Oe'; - -export type MagneticPermeabilityUnits = - | MagneticPermeabilityMetricUnits - | MagneticPermeabilityImperialUnits; - -const METRIC: TbMeasureUnits = { - transform: (Hm) => Hm * 795774.715, - units: { - 'H/m': { - name: 'unit.henry-per-meter', - tags: ['magnetic permeability', 'henry per meter', 'H/m'], - to_anchor: 1, - }, - }, -}; - -const IMPERIAL: TbMeasureUnits = { - transform: (GOe) => GOe / 795774.715, - units: { - 'G/Oe': { - name: 'unit.gauss-per-oersted', - tags: ['magnetic field', 'Gauss per Oersted', 'G/Oe'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, - IMPERIAL, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/acceleration.ts b/ui-ngx/src/app/shared/models/units/acceleration.ts index 1a39b0b814..a1643020a9 100644 --- a/ui-ngx/src/app/shared/models/units/acceleration.ts +++ b/ui-ngx/src/app/shared/models/units/acceleration.ts @@ -26,22 +26,22 @@ const METRIC: TbMeasureUnits = { units: { 'G': { name: 'unit.g-force', - tags: ['acceleration', 'gravity', 'g-force', 'load', 'G'], + tags: ['gravity', 'g-force', 'load'], to_anchor: 9.80665, }, 'm/s²': { name: 'unit.meters-per-second-squared', - tags: ['peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'meters per second squared', 'm/s²'], + tags: ['peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'meters per second squared'], to_anchor: 1, }, 'Gal': { name: 'unit.gal', - tags: ['acceleration', 'gravity', 'g-force', 'Gal'], + tags: ['gravity', 'g-force'], to_anchor: 1, }, 'km/h²': { name: 'unit.kilometer-per-hour-squared', - tags: ['acceleration', 'rate of change of velocity', 'kilometer per hour squared', 'km/h²'], + tags: ['rate of change of velocity', 'kilometer per hour squared'], to_anchor: 1 / 12960, } } diff --git a/ui-ngx/src/app/shared/models/units/air-quality-index.ts b/ui-ngx/src/app/shared/models/units/air-quality-index.ts index 907071b594..9047f86d94 100644 --- a/ui-ngx/src/app/shared/models/units/air-quality-index.ts +++ b/ui-ngx/src/app/shared/models/units/air-quality-index.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type AirQualityIndexUnits = 'aqi'; @@ -6,7 +22,7 @@ const METRIC: TbMeasureUnits = { units: { aqi: { name: 'unit.aqi', - tags: ['AQI', 'air quality index'], + tags: ['pollutant concentration'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts index 47f798c388..1601f5d80a 100644 --- a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts +++ b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts @@ -1,34 +1,48 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type AmountOfSubstanceMetricUnits = 'mol' | 'nmol' | 'μmol' | 'mmol' | 'kmol'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type AmountOfSubstanceUnits = AmountOfSubstanceMetricUnits; +export type AmountOfSubstanceUnits = 'mol' | 'nmol' | 'μmol' | 'mmol' | 'kmol'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'mol': { name: 'unit.mole', - tags: ['amount of substance', 'chemical amount', 'mole', 'mol'], + tags: ['chemical amount'], to_anchor: 1, }, 'nmol': { name: 'unit.nanomole', - tags: ['amount of substance', 'nanomole', 'nmol'], + tags: ['chemical amount'], to_anchor: 0.000000001, }, 'μmol': { name: 'unit.micromole', - tags: ['amount of substance', 'micromole', 'μmol'], + tags: ['chemical amount'], to_anchor: 0.000001, }, 'mmol': { name: 'unit.millimole', - tags: ['amount of substance', 'millimole', 'mmol'], + tags: ['chemical amount'], to_anchor: 0.001, }, 'kmol': { name: 'unit.kilomole', - tags: ['amount of substance', 'kilomole', 'kmol'], + tags: ['chemical amount'], to_anchor: 1000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/angle.ts b/ui-ngx/src/app/shared/models/units/angle.ts index 7082e5a986..a92c4cb7a5 100644 --- a/ui-ngx/src/app/shared/models/units/angle.ts +++ b/ui-ngx/src/app/shared/models/units/angle.ts @@ -16,45 +16,43 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type AngleUnits = AngleMetricUnits; +export type AngleUnits = 'rad' | 'deg' | 'grad' | 'arcmin' | 'arcsec' | 'mrad' | 'rev'; -export type AngleMetricUnits = 'rad' | 'deg' | 'grad' | 'arcmin' | 'arcsec' | 'mrad' | 'rev'; - -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { rad: { name: 'unit.rad', - tags: ['angle', 'radian', 'radians', 'rad'], + tags: ['radians'], to_anchor: 180 / Math.PI, }, deg: { name: 'unit.degree', - tags: ['angle', 'degree', 'degrees', 'deg'], + tags: ['degrees'], to_anchor: 1, }, grad: { name: 'unit.gradian', - tags: ['angle', 'gradian', 'grades', 'grad'], + tags: ['grades'], to_anchor: 9 / 10, }, arcmin: { name: 'unit.arcminute', - tags: ['angle', 'arcminute', 'arcminutes', 'arcmin'], + tags: ['arcminutes'], to_anchor: 1 / 60 }, arcsec: { name: 'unit.arcsecond', - tags: ['angle', 'arcsecond', 'arcseconds', 'arcsec'], + tags: ['arcseconds'], to_anchor: 1 / 3600 }, mrad: { name: 'unit.milliradian', - tags: ['angle', 'military angle', 'angular mil', 'mil'], + tags: ['military angle', 'angular mil', 'mil'], to_anchor: 9 / (50 * Math.PI), }, rev: { name: 'unit.revolution', - tags: ['angle', 'revolution', 'full circle', 'complete turn', 'rev'], + tags: ['full circle', 'complete turn'], to_anchor: 360, }, } diff --git a/ui-ngx/src/app/shared/models/units/angular-acceleration.ts b/ui-ngx/src/app/shared/models/units/angular-acceleration.ts index 4e5085d7bf..d61957ac27 100644 --- a/ui-ngx/src/app/shared/models/units/angular-acceleration.ts +++ b/ui-ngx/src/app/shared/models/units/angular-acceleration.ts @@ -26,7 +26,7 @@ const METRIC: TbMeasureUnits = { units: { 'rad/s²': { name: 'unit.radian-per-second-squared', - tags: ['angular acceleration', 'rotation rate of change', 'rad/s²'], + tags: ['rotation rate of change'], to_anchor: 1, } } @@ -37,7 +37,7 @@ const IMPERIAL: TbMeasureUnits = { units: { 'rpm/s': { name: 'unit.revolutions-per-minute-per-second', - tags: ['angular acceleration', 'rotation rate of change', 'rpm/s'], + tags: ['rotation rate of change'], to_anchor: 1 } } diff --git a/ui-ngx/src/app/shared/models/units/area.ts b/ui-ngx/src/app/shared/models/units/area.ts index 6c36d6d976..b1df9295e2 100644 --- a/ui-ngx/src/app/shared/models/units/area.ts +++ b/ui-ngx/src/app/shared/models/units/area.ts @@ -26,37 +26,37 @@ const METRIC: TbMeasureUnits = { units: { 'mm²': { name: 'unit.square-millimeter', - tags: ['area','lot','zone','space','region','square millimeter','square millimeters','mm²','sq-mm'], + tags: ['lot', 'zone', 'space', 'region', 'square millimeters', 'sq-mm'], to_anchor: 1 / 1000000, }, 'cm²': { name: 'unit.square-centimeter', - tags: ['area','lot','zone','space','region','square centimeter','square centimeters','cm²','sq-cm'], + tags: ['lot', 'zone', 'space', 'region', 'square centimeters', 'sq-cm'], to_anchor: 1 / 10000, }, 'm²': { name: 'unit.square-meter', - tags: ['area','lot','zone','space','region','square meter','square meters','m²','sq-m'], + tags: ['lot', 'zone', 'space', 'region', 'square meters', 'sq-m'], to_anchor: 1, }, a: { name: 'unit.are', - tags: ['area','land measurement','are'], + tags: ['land measurement'], to_anchor: 100, }, ha: { name: 'unit.hectare', - tags: ['area','lot','zone','space','region','hectare','hectares','ha'], + tags: ['lot', 'zone', 'space', 'region', 'hectares'], to_anchor: 10000, }, 'km²': { name: 'unit.square-kilometer', - tags: ['area','lot','zone','space','region','square kilometer','square kilometers','km²','sq-km'], + tags: ['lot', 'zone', 'space', 'region', 'square kilometers', 'sq-km'], to_anchor: 1000000, }, barn: { name: 'unit.barn', - tags: ['cross-sectional area', 'particle physics', 'nuclear physics', 'barn'], + tags: ['cross-sectional area', 'particle physics', 'nuclear physics'], to_anchor: 1e-28, }, } @@ -67,32 +67,32 @@ const IMPERIAL: TbMeasureUnits = { units: { 'in²': { name: 'unit.square-inch', - tags: ['area','lot','zone','space','region','square inch','square inches','in²','sq-in'], + tags: ['lot', 'zone', 'space', 'region', 'square inches', 'sq-in'], to_anchor: 1 / 144, }, 'yd²': { name: 'unit.square-yard', - tags: ['area','lot','zone','space','region','square yard','square yards','yd²','sq-yd'], + tags: ['lot', 'zone', 'space', 'region', 'square yards', 'sq-yd'], to_anchor: 9, }, 'ft²': { name: 'unit.square-foot', - tags: ['area','lot','zone','space','region','square foot','square feet','ft²','sq-ft'], + tags: ['lot', 'zone', 'space', 'region', 'square feet', 'sq-ft'], to_anchor: 1, }, ac: { name: 'unit.acre', - tags: ['area','lot','zone','space','region','acre','acres','a'], + tags: ['lot', 'zone', 'space', 'region', 'acres', 'a'], to_anchor: 43560, }, 'ml²': { name: 'unit.square-mile', - tags: ['area','lot','zone','space','region','square mile','square miles','ml²','sq-mi'], + tags: ['lot', 'zone', 'space', 'region', 'square mile', 'sq-mi'], to_anchor: 27878400, }, cin: { name: 'unit.circular-inch', - tags: ['area','circular measurement','circular inch','circin'], + tags: ['circular measurement', 'circin'], to_anchor: Math.PI / 576 } } diff --git a/ui-ngx/src/app/shared/models/units/blood-glucose.ts b/ui-ngx/src/app/shared/models/units/blood-glucose.ts deleted file mode 100644 index 18b0902591..0000000000 --- a/ui-ngx/src/app/shared/models/units/blood-glucose.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type BloodGlucoseUnits = BloodGlucoseMetricUnits; -export type BloodGlucoseMetricUnits = 'mg/dL'; - -const METRIC: TbMeasureUnits = { - units: { - 'mg/dL': { - name: 'unit.milligrams-per-deciliter', - tags: ['glucose', 'blood sugar', 'glucose level', 'mg/dL'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/capacitance.ts b/ui-ngx/src/app/shared/models/units/capacitance.ts index 4999afd536..f4dc13dc97 100644 --- a/ui-ngx/src/app/shared/models/units/capacitance.ts +++ b/ui-ngx/src/app/shared/models/units/capacitance.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type CapacitanceUnits = 'F' | 'mF' | 'μF' | 'nF' | 'pF' | 'kF' | 'MF' | 'GF' | 'TF'; @@ -6,47 +22,47 @@ const METRIC: TbMeasureUnits = { units: { 'F': { name: 'unit.farad', - tags: ['electric capacitance', 'capacitance', 'farad', 'F'], + tags: ['electric capacitance'], to_anchor: 1, }, 'mF': { name: 'unit.millifarad', - tags: ['electric capacitance', 'capacitance', 'millifarad', 'mF'], + tags: ['electric capacitance'], to_anchor: 1e-3, }, 'μF': { name: 'unit.microfarad', - tags: ['electric capacitance', 'capacitance', 'microfarad', 'μF'], + tags: ['electric capacitance'], to_anchor: 1e-6, }, 'nF': { name: 'unit.nanofarad', - tags: ['electric capacitance', 'capacitance', 'nanofarad', 'nF'], + tags: ['electric capacitance'], to_anchor: 1e-9, }, 'pF': { name: 'unit.picofarad', - tags: ['electric capacitance', 'capacitance', 'picofarad', 'pF'], + tags: ['electric capacitance'], to_anchor: 1e-12, }, 'kF': { name: 'unit.kilofarad', - tags: ['electric capacitance', 'capacitance', 'kilofarad', 'kF'], + tags: ['electric capacitance'], to_anchor: 1e3, }, 'MF': { name: 'unit.megafarad', - tags: ['electric capacitance', 'capacitance', 'megafarad', 'MF'], + tags: ['electric capacitance'], to_anchor: 1e6, }, 'GF': { name: 'unit.gigafarad', - tags: ['electric capacitance', 'capacitance', 'gigafarad', 'GF'], + tags: ['electric capacitance'], to_anchor: 1e9, }, 'TF': { name: 'unit.terafarad', - tags: ['electric capacitance', 'capacitance', 'terafarad', 'TF'], + tags: ['electric capacitance'], to_anchor: 1e12, }, }, diff --git a/ui-ngx/src/app/shared/models/units/catalytic-activity.ts b/ui-ngx/src/app/shared/models/units/catalytic-activity.ts index 7198bf4d9c..c0dc61c2f5 100644 --- a/ui-ngx/src/app/shared/models/units/catalytic-activity.ts +++ b/ui-ngx/src/app/shared/models/units/catalytic-activity.ts @@ -1,18 +1,34 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type AirQualityIndexUnits = 'kat'; +export type CatalyticActivityUnits = 'kat'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { kat: { name: 'unit.katal', - tags: ['catalytic activity', 'enzyme activity', 'kat'], + tags: ['catalytic activity', 'enzyme activity'], to_anchor: 1, }, }, }; -const measure: TbMeasure = { +const measure: TbMeasure = { METRIC, }; diff --git a/ui-ngx/src/app/shared/models/units/catalytic-concentration.ts b/ui-ngx/src/app/shared/models/units/catalytic-concentration.ts new file mode 100644 index 0000000000..805f4cfc40 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/catalytic-concentration.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type CatalyticConcentrationUnits = 'kat/m³'; + +const METRIC: TbMeasureUnits = { + units: { + 'kat/m³': { + name: 'unit.katal-per-cubic-metre', + tags: ['enzyme concentration'], + to_anchor: 1, + } + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/charge.ts b/ui-ngx/src/app/shared/models/units/charge.ts index b06a0223d5..2abce39f18 100644 --- a/ui-ngx/src/app/shared/models/units/charge.ts +++ b/ui-ngx/src/app/shared/models/units/charge.ts @@ -24,42 +24,42 @@ const METRIC: TbMeasureUnits = { units: { c: { name: 'unit.coulomb', - tags: ['charge', 'electricity', 'electrostatics', 'Coulomb', 'C'], + tags: ['electricity', 'electrostatics'], to_anchor: 1, }, mC: { name: 'unit.millicoulomb', - tags: ['charge', 'electricity', 'electrostatics', 'millicoulombs', 'mC'], + tags: ['electricity', 'electrostatics'], to_anchor: 1 / 1000, }, μC: { name: 'unit.microcoulomb', - tags: ['charge', 'electricity', 'electrostatics', 'microcoulomb', 'µC'], + tags: [ 'electricity', 'electrostatics'], to_anchor: 1 / 1000000, }, nC: { name: 'unit.nanocoulomb', - tags: ['charge', 'electricity', 'electrostatics', 'nanocoulomb', 'nC'], + tags: ['electricity', 'electrostatics',], to_anchor: 1e-9, }, pC: { name: 'unit.picocoulomb', - tags: ['charge', 'electricity', 'electrostatics', 'picocoulomb', 'pC'], + tags: ['electricity', 'electrostatics'], to_anchor: 1e-12, }, mAh: { name: 'unit.milliampere-hour', - tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'milliampere-hour', 'milliampere-hours', 'mAh'], + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'milliampere-hours'], to_anchor: 3.6, }, Ah: { name: 'unit.ampere-hours', - tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'ampere', 'ampere-hours', 'Ah'], + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'ampere'], to_anchor: 3600, }, kAh: { name: 'unit.kiloampere-hours', - tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'kiloampere-hours', 'kiloampere-hour', 'kAh'], + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'kiloampere-hours'], to_anchor: 3600000, }, } diff --git a/ui-ngx/src/app/shared/models/units/concentration-gradient.ts b/ui-ngx/src/app/shared/models/units/concentration-gradient.ts deleted file mode 100644 index ef406445d2..0000000000 --- a/ui-ngx/src/app/shared/models/units/concentration-gradient.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type ConcentrationMetricUnits = 'mol/m³' | 'mg/mL' | 'mg/m³' | 'µg/m³' | 'particles/mL'; -export type ConcentrationUnits = ConcentrationMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'mol/m³': { - name: 'unit.mole-per-cubic-meter', - tags: ['concentration', 'amount of substance per unit volume', 'mole per cubic meter', 'mol/m³'], - to_anchor: 1, - }, - 'mg/mL': { - name: 'unit.milligram-per-milliliter', - tags: ['concentration', 'mass per unit volume', 'milligram per milliliter', 'mg/mL'], - to_anchor: 1, - }, - 'mg/m³': { - name: 'unit.milligram-per-cubic-meter', - tags: ['concentration', 'mass per unit volume', 'milligram per cubic meter', 'mg/m³'], - to_anchor: 1, - }, - 'µg/m³': { - name: 'unit.micrograms-per-cubic-meter', - tags: ['concentration', 'air quality', 'particulate matter', 'PM2.5', 'PM10', 'micrograms per cubic meter', 'µg/m³'], - to_anchor: 1, - }, - 'particles/mL': { - name: 'unit.particle-density', - tags: ['concentration', 'particle density', 'particles per milliliter', 'particles/mL'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/concentration-volume.ts b/ui-ngx/src/app/shared/models/units/concentration-volume.ts deleted file mode 100644 index 94c8050fee..0000000000 --- a/ui-ngx/src/app/shared/models/units/concentration-volume.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type ConcentrationVolumeMetricUnits = - 'mol/m³' - | 'µg/m³' - | 'mg/m³' - | 'g/m³' - | 'mg/L' - | 'mg/mL' - | 'kat/m³' - | '°Bx'; -export type ConcentrationVolumeUnits = ConcentrationVolumeMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'mol/m³': { - name: 'unit.mole-per-cubic-meter', - tags: ['concentration', 'amount of substance', 'mole per cubic meter', 'mol/m³'], - to_anchor: 1, - }, - 'µg/m³': { - name: 'unit.micrograms-per-cubic-meter', - tags: ['coarse particulate matter', 'pm10', 'fine particulate matter', 'pm2.5', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'micrograms per cubic meter', 'µg/m³'], - to_anchor: 1e-9, - }, - 'mg/m³': { - name: 'unit.milligram-per-cubic-meter', - tags: ['concentration', 'mass per volume', 'mg/m³'], - to_anchor: 1e-6, - }, - 'g/m³': { - name: 'unit.gram-per-cubic-meter', - tags: ['humidity', 'moisture', 'absolute humidity', 'g/m³'], - to_anchor: 1 / 1000, - }, - 'mg/L': { - name: 'unit.mg-per-liter', - tags: ['dissolved oxygen', 'water quality', 'mg/L'], - to_anchor: 1e-6, - }, - 'mg/mL': { - name: 'unit.milligram-per-milliliter', - tags: ['concentration', 'mass per volume', 'mg/mL'], - to_anchor: 1 / 1000, - }, - 'kat/m³': { - name: 'unit.katal-per-cubic-metre', - tags: ['catalytic activity concentration', 'enzyme concentration', 'kat/m³'], - to_anchor: 1, - }, - '°Bx': { - name: 'unit.degrees-brix', - tags: ['sugar content', 'fruit ripeness', 'Bx'], - to_anchor: 10.04 * 1e-3, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/current-density.ts b/ui-ngx/src/app/shared/models/units/current-density.ts index 7b332c57f4..aa5c88b0e9 100644 --- a/ui-ngx/src/app/shared/models/units/current-density.ts +++ b/ui-ngx/src/app/shared/models/units/current-density.ts @@ -1,19 +1,33 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type CurrentDensityMetricUnits = 'µA/cm²' | 'A/m²'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type CurrentDensityUnits = CurrentDensityMetricUnits; +export type CurrentDensityUnits = 'µA/cm²' | 'A/m²'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'µA/cm²': { name: 'unit.microampere-per-square-centimeter', - tags: ['current density', 'microampere per square centimeter', 'µA/cm²'], + tags: ['current per unit area'], to_anchor: 10000, }, 'A/m²': { name: 'unit.ampere-per-square-meter', - tags: ['current density', 'current per unit area', 'ampere per square meter', 'A/m²'], + tags: ['current per unit area'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts index e3b9095700..d952eaf727 100644 --- a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts +++ b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type DataTransferRateUnits = 'bps' | 'kbps' | 'Mbps' | 'Gbps' | 'Tbps' | 'B/s' | 'KB/s' | 'MB/s' | 'GB/s'; @@ -6,47 +22,38 @@ const METRIC: TbMeasureUnits = { units: { 'bps': { name: 'unit.bit-per-second', - tags: ['data transfer rate', 'bps'], to_anchor: 1, }, 'kbps': { name: 'unit.kilobit-per-second', - tags: ['data transfer rate', 'kbps'], to_anchor: 1e3, }, 'Mbps': { name: 'unit.megabit-per-second', - tags: ['data transfer rate', 'Mbps'], to_anchor: 1e6, }, 'Gbps': { name: 'unit.gigabit-per-second', - tags: ['data transfer rate', 'Gbps'], to_anchor: 1e9, }, 'Tbps': { name: 'unit.terabit-per-second', - tags: ['data transfer rate', 'Tbps'], to_anchor: 1e12, }, 'B/s': { name: 'unit.byte-per-second', - tags: ['data transfer rate', 'B/s'], to_anchor: 8, }, 'KB/s': { name: 'unit.kilobyte-per-second', - tags: ['data transfer rate', 'KB/s'], to_anchor: 8e3, }, 'MB/s': { name: 'unit.megabyte-per-second', - tags: ['data transfer rate', 'MB/s'], to_anchor: 8e6, }, 'GB/s': { name: 'unit.gigabyte-per-second', - tags: ['data transfer rate', 'GB/s'], to_anchor: 8e9, }, }, diff --git a/ui-ngx/src/app/shared/models/units/density.ts b/ui-ngx/src/app/shared/models/units/density.ts index 8603de1976..8a501d1638 100644 --- a/ui-ngx/src/app/shared/models/units/density.ts +++ b/ui-ngx/src/app/shared/models/units/density.ts @@ -1,6 +1,22 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type DensityMetricUnits = 'kg/m³' | 'g/cm³'; +export type DensityMetricUnits = 'kg/m³' | 'g/cm³' | 'mg/dL' | 'g/m³' | 'mg/mL' | 'mg/L' | 'mg/m³' | 'µg/m³'; export type DensityImperialUnits = 'lb/ft³' | 'oz/in³' | 'ton/yd³'; export type DensityUnits = DensityMetricUnits | DensityImperialUnits; @@ -10,13 +26,43 @@ const METRIC: TbMeasureUnits = { units: { 'kg/m³': { name: 'unit.kilogram-per-cubic-meter', - tags: ['density', 'mass per unit volume', 'kg/m³'], - to_anchor: 1, // Base unit: kg/m³ + tags: ['mass per unit volume'], + to_anchor: 1, }, 'g/cm³': { name: 'unit.gram-per-cubic-centimeter', - tags: ['density', 'mass per unit volume', 'g/cm³'], - to_anchor: 1000, // 1 g/cm³ = 10³ kg/m³ + tags: ['mass per unit volume'], + to_anchor: 1000, + }, + 'mg/dL': { + name: 'unit.milligrams-per-deciliter', + tags: ['glucose', 'blood sugar', 'glucose level', 'concentration'], + to_anchor: 0.01, + }, + 'g/m³': { + name: 'unit.gram-per-cubic-meter', + tags: ['humidity', 'moisture', 'absolute humidity', 'concentration'], + to_anchor: 0.001, + }, + 'mg/L': { + name: 'unit.mg-per-liter', + tags: ['dissolved oxygen', 'water quality', 'mg/L', 'concentration'], + to_anchor: 0.001, + }, + 'mg/mL': { + name: 'unit.milligram-per-milliliter', + tags: ['mass per unit volume', 'concentration'], + to_anchor: 1, + }, + 'mg/m³': { + name: 'unit.milligram-per-cubic-meter', + tags: ['mass per unit volume', 'concentration'], + to_anchor: 1e-6, + }, + 'µg/m³': { + name: 'unit.micrograms-per-cubic-meter', + tags: ['coarse particulate matter', 'pm10', 'fine particulate matter', 'pm2.5', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'concentration'], + to_anchor: 1e-9, }, }, }; @@ -26,17 +72,17 @@ const IMPERIAL: TbMeasureUnits = { units: { 'lb/ft³': { name: 'unit.pound-per-cubic-foot', - tags: ['density', 'mass per unit volume', 'lb/ft³'], + tags: ['mass per unit volume'], to_anchor: 1, }, 'oz/in³': { name: 'unit.ounces-per-cubic-inch', - tags: ['density', 'mass per unit volume', 'oz/in³'], + tags: ['mass per unit volume'], to_anchor: 1728, }, 'ton/yd³': { name: 'unit.tons-per-cubic-yard', - tags: ['density', 'mass per unit volume', 'ton/yd³'], + tags: ['mass per unit volume'], to_anchor: 74.074, }, }, diff --git a/ui-ngx/src/app/shared/models/units/digital.ts b/ui-ngx/src/app/shared/models/units/digital.ts index 54bcceea2f..32f2eed2a7 100644 --- a/ui-ngx/src/app/shared/models/units/digital.ts +++ b/ui-ngx/src/app/shared/models/units/digital.ts @@ -16,60 +16,58 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type DigitalUnits = DigitalMetricUnits; +export type DigitalUnits = 'bit' | 'B' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB' | 'EB' | 'ZB' | 'YB'; -export type DigitalMetricUnits = 'bit' | 'B' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB' | 'EB' | 'ZB' | 'YB'; - -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { bit: { name: 'unit.bit', - tags: ['data', 'binary digit', 'information', 'bit'], + tags: ['data', 'binary digit', 'information'], to_anchor: 1.25e-1, }, B: { name: 'unit.byte', - tags: ['data', 'byte', 'information', 'storage', 'memory', 'B'], + tags: ['data', 'information', 'storage', 'memory'], to_anchor: 1 }, KB: { name: 'unit.kilobyte', - tags: ['data', 'kilobyte', 'KB'], + tags: ['data'], to_anchor: 1024, }, MB: { name: 'unit.megabyte', - tags: ['data', 'megabyte', 'MB'], + tags: ['data'], to_anchor: 1024 ** 2, }, GB: { name: 'unit.gigabyte', - tags: ['data', 'gigabyte', 'GB'], + tags: ['data'], to_anchor: 1024 ** 3, }, TB: { name: 'unit.terabyte', - tags: ['data', 'terabyte', 'TB'], + tags: ['data'], to_anchor: 1024 ** 4, }, PB: { name: 'unit.petabyte', - tags: ['data', 'petabyte', 'PB'], + tags: ['data'], to_anchor: 1024 ** 5, }, EB: { name: 'unit.exabyte', - tags: ['data', 'exabyte', 'EB'], + tags: ['data'], to_anchor: 1024 ** 6, }, ZB: { name: 'unit.zettabyte', - tags: ['data', 'zettabyte', 'EB'], + tags: ['data'], to_anchor: 1024 ** 7, }, YB: { name: 'unit.yottabyte', - tags: ['data', 'yottabyte', 'EB'], + tags: ['data'], to_anchor: 1024 ** 8, }, } diff --git a/ui-ngx/src/app/shared/models/units/dimension-ratio.ts b/ui-ngx/src/app/shared/models/units/dimension-ratio.ts new file mode 100644 index 0000000000..3deb60a38f --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/dimension-ratio.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DimensionRatioUnits = 'm/m'; + +const METRIC: TbMeasureUnits = { + units: { + 'm/m': { + name: 'unit.meter-per-meter', + tags: ['ratio of length to length'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts b/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts deleted file mode 100644 index 71972aa6e8..0000000000 --- a/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type DimensionlessRatioMetricUnits = 'm/m'; - -export type DimensionRatioUnits = DimensionlessRatioMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'm/m': { - name: 'unit.meter-per-meter', - tags: ['ratio of length to length', 'meter per meter', 'm/m'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts index 272892f9df..e3fe8602b3 100644 --- a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts +++ b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type DynamicViscosityMetricUnits = 'Pa·s' | 'cP' | 'P' | 'N·s/m²' | 'dyn·s/cm²' | 'kg/(m·s)'; @@ -10,32 +26,32 @@ const METRIC: TbMeasureUnits = { units: { 'Pa·s': { name: 'unit.pascal-second', - tags: ['dynamic viscosity', 'viscosity', 'fluid mechanics', 'Pa·s'], + tags: ['fluid mechanics'], to_anchor: 1, }, 'cP': { name: 'unit.centipoise', - tags: ['viscosity', 'dynamic viscosity', 'fluid viscosity', 'centipoise', 'cP'], + tags: ['fluid mechanics'], to_anchor: 0.001, }, 'P': { name: 'unit.poise', - tags: ['viscosity', 'dynamic viscosity', 'fluid viscosity', 'poise', 'P'], + tags: ['fluid mechanics'], to_anchor: 0.1, }, 'N·s/m²': { name: 'unit.newton-second-per-square-meter', - tags: ['newton second per square meter', 'N·s/m²'], + tags: ['fluid mechanics'], to_anchor: 1, }, 'dyn·s/cm²': { name: 'unit.dyne-second-per-square-centimeter', - tags: ['dyne second per square centimeter', 'dyn·s/cm²'], + tags: ['fluid mechanics'], to_anchor: 0.1, }, 'kg/(m·s)': { name: 'unit.kilogram-per-meter-second', - tags: ['kilogram per meter-second', 'kg/(m·s)'], + tags: ['fluid mechanics'], to_anchor: 1, }, }, @@ -46,7 +62,7 @@ const IMPERIAL: TbMeasureUnits = { units: { 'lb/(ft·h)': { name: 'unit.pound-per-foot-hour', - tags: ['pound per foot-hour', 'lb/(ft·h)'], + tags: ['fluid mechanics'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts b/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts index 02658073b5..60b40ec01c 100644 --- a/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts +++ b/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type EarthquakeMagnitudeUnits = 'richter'; @@ -6,7 +22,7 @@ const METRIC: TbMeasureUnits = { units: { richter: { name: 'unit.richter-scale', - tags: ['earthquake', 'seismic activity', 'richter'], + tags: ['seismic activity'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electric-current.ts b/ui-ngx/src/app/shared/models/units/electric-current.ts index 9025e71c15..fbfb9b888c 100644 --- a/ui-ngx/src/app/shared/models/units/electric-current.ts +++ b/ui-ngx/src/app/shared/models/units/electric-current.ts @@ -16,50 +16,48 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ElectricCurrentUnits = ElectricCurrentMetricalUnits; +export type ElectricCurrentUnits = 'A' | 'pA' | 'nA' | 'μA' | 'mA' | 'kA' | 'MA' | 'GA'; -export type ElectricCurrentMetricalUnits = 'A' | 'pA' | 'nA' | 'μA' | 'mA' | 'kA' | 'MA' | 'GA'; - -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { A: { name: 'unit.ampere', - tags: ['electric current', 'current flow', 'flow of electricity', 'electrical flow', 'ampere', 'amperes', 'amperage', 'A'], + tags: ['current flow', 'flow of electricity', 'electrical flow', 'amperes', 'amperage'], to_anchor: 1, }, pA: { name: 'unit.picoampere', - tags: ['current', 'amperes', 'picoampere', 'pA'], + tags: ['picoamperes'], to_anchor: 1e-12, }, nA: { name: 'unit.nanoampere', - tags: ['electric current', 'amperes', 'nanoampere', 'nA'], + tags: ['nanoamperes'], to_anchor: 1e-9, }, μA: { name: 'unit.microampere', - tags: ['electric current', 'microampere', 'microamperes', 'μA'], + tags: ['microamperes'], to_anchor: 1e-6, }, mA: { name: 'unit.milliampere', - tags: ['electric current', 'milliampere', 'milliamperes', 'mA'], + tags: ['milliamperes'], to_anchor: 0.001, }, kA: { name: 'unit.kiloampere', - tags: ['electric current', 'kiloampere', 'kiloamperes', 'kA'], + tags: ['kiloamperes'], to_anchor: 1000, }, MA: { name: 'unit.megaampere', - tags: ['electric current', 'megaampere', 'megaamperes', 'MA'], + tags: ['megaamperes'], to_anchor: 1e6, }, GA: { name: 'unit.gigaampere', - tags: ['electric current', 'gigaampere', 'gigaamperes', 'GA'], + tags: ['gigaamperes'], to_anchor: 1e9, }, } diff --git a/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts b/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts new file mode 100644 index 0000000000..b43e08b857 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts @@ -0,0 +1,39 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricDipoleMomentUnits = 'C·m' | 'D'; + +const METRIC: TbMeasureUnits = { + units: { + 'C·m': { + name: 'unit.electric-dipole-moment', + to_anchor: 1, + }, + 'D': { + name: 'unit.debye', + tags: ['polarization'], + to_anchor: 3.33564e-30 + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-field-strength.ts b/ui-ngx/src/app/shared/models/units/electric-field-strength.ts index f8d753c401..16a35b963d 100644 --- a/ui-ngx/src/app/shared/models/units/electric-field-strength.ts +++ b/ui-ngx/src/app/shared/models/units/electric-field-strength.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type ElectricFieldStrengthUnits = 'V/m' | 'mV/m' | 'kV/m'; @@ -6,17 +22,14 @@ const METRIC: TbMeasureUnits = { units: { 'V/m': { name: 'unit.volts-per-meter', - tags: ['electric field strength', 'volts per meter', 'V/m'], to_anchor: 1, }, 'mV/m': { name: 'unit.millivolts-per-meter', - tags: ['electric field strength', 'millivolts per meter', 'mV/m'], to_anchor: 1e-3, }, 'kV/m': { name: 'unit.kilovolts-per-meter', - tags: ['electric field strength', 'kilovolts per meter', 'kV/m'], to_anchor: 1e3, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electric-flux.ts b/ui-ngx/src/app/shared/models/units/electric-flux.ts index ac40a58293..ca4e6c2fe7 100644 --- a/ui-ngx/src/app/shared/models/units/electric-flux.ts +++ b/ui-ngx/src/app/shared/models/units/electric-flux.ts @@ -1,39 +1,47 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type ElectricFluxMetricUnits = 'V·m' | 'kV·m' | 'MV·m' | 'µV·m' | 'mV·m' | 'nV·m'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ElectricFluxUnits = ElectricFluxMetricUnits; +export type ElectricFluxUnits = 'V·m' | 'kV·m' | 'MV·m' | 'µV·m' | 'mV·m' | 'nV·m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'V·m': { name: 'unit.volt-meter', - tags: ['electric flux', 'volt-meter', 'V·m'], to_anchor: 1, }, 'kV·m': { name: 'unit.kilovolt-meter', - tags: ['electric flux', 'kilovolt-meter', 'kV·m'], to_anchor: 1000, }, 'MV·m': { name: 'unit.megavolt-meter', - tags: ['electric flux', 'megavolt-meter', 'MV·m'], to_anchor: 1000000, }, 'µV·m': { name: 'unit.microvolt-meter', - tags: ['electric flux', 'microvolt-meter', 'µV·m'], to_anchor: 0.000001, }, 'mV·m': { name: 'unit.millivolt-meter', - tags: ['electric flux', 'millivolt-meter', 'mV·m'], to_anchor: 0.001, }, 'nV·m': { name: 'unit.nanovolt-meter', - tags: ['electric flux', 'nanovolt-meter', 'nV·m'], to_anchor: 0.000000001, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electric-moment.ts b/ui-ngx/src/app/shared/models/units/electric-moment.ts deleted file mode 100644 index 47a8e55830..0000000000 --- a/ui-ngx/src/app/shared/models/units/electric-moment.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type ElectricMomentMetricUnits = 'C·m' | 'D'; - -export type ElectricMomentUnits = ElectricMomentMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'C·m': { - name: 'unit.electric-dipole-moment', - tags: ['electric dipole', 'dipole moment', 'coulomb meter', 'C·m'], - to_anchor: 1, - }, - 'D': { - name: 'unit.debye', - tags: ['polarization', 'electric dipole moment', 'debye', 'D'], - to_anchor: 3.33564e-30 - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-permittivity.ts b/ui-ngx/src/app/shared/models/units/electric-permittivity.ts index d9bea899a7..e116d9e08a 100644 --- a/ui-ngx/src/app/shared/models/units/electric-permittivity.ts +++ b/ui-ngx/src/app/shared/models/units/electric-permittivity.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type ElectricPermittivityMetricUnits = 'F/m'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ElectricPermittivityUnits = ElectricPermittivityMetricUnits; +export type ElectricPermittivityUnits = 'F/m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'F/m': { name: 'unit.farad-per-meter', - tags: ['electric permittivity', 'farad per meter', 'F/m'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts index 0a1fe6bebe..62241d059f 100644 --- a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts +++ b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type ElectricalConductanceUnits = 'S' | 'mS' | 'μS' | 'kS' | 'MS' | 'GS'; @@ -6,32 +22,26 @@ const METRIC: TbMeasureUnits = { units: { 'S': { name: 'unit.siemens', - tags: ['electrical conductance', 'conductance', 'siemens', 'S'], to_anchor: 1, }, 'mS': { name: 'unit.millisiemens', - tags: ['electrical conductance', 'conductance', 'millisiemens', 'mS'], to_anchor: 1e-3, }, 'μS': { name: 'unit.microsiemens', - tags: ['electrical conductance', 'conductance', 'microsiemens', 'μS'], to_anchor: 1e-6, }, 'kS': { name: 'unit.kilosiemens', - tags: ['electrical conductance', 'conductance', 'kilosiemens', 'kS'], to_anchor: 1e3, }, 'MS': { name: 'unit.megasiemens', - tags: ['electrical conductance', 'conductance', 'megasiemens', 'MS'], to_anchor: 1e6, }, 'GS': { name: 'unit.gigasiemens', - tags: ['electrical conductance', 'conductance', 'gigasiemens', 'GS'], to_anchor: 1e9, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts b/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts index 9bf7245ff1..aa9aab858f 100644 --- a/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts +++ b/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts @@ -1,23 +1,38 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ElectricalConductivityMetricUnits = 'µS/cm' | 'mS/m' | 'S/m'; -export type ElectricalConductivityUnits = ElectricalConductivityMetricUnits; +export type ElectricalConductivityUnits = 'µS/cm' | 'mS/m' | 'S/m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'S/m': { name: 'unit.siemens-per-meter', - tags: ['Electrical conductivity', 'water quality', 'soil quality', 'siemens per meter', 'S/m'], + tags: [ 'water quality', 'soil quality'], to_anchor: 1, }, 'µS/cm': { name: 'unit.microsiemens-per-centimeter', - tags: ['Electrical conductivity', 'water quality', 'soil quality', 'microsiemens per centimeter', 'µS/cm'], + tags: ['water quality', 'soil quality'], to_anchor: 0.0001, }, 'mS/m': { name: 'unit.millisiemens-per-meter', - tags: ['Electrical conductivity', 'water quality', 'soil quality', 'millisiemens per meter', 'mS/m'], + tags: ['water quality', 'soil quality'], to_anchor: 0.001, }, }, diff --git a/ui-ngx/src/app/shared/models/units/energy.ts b/ui-ngx/src/app/shared/models/units/energy.ts index 2f74091c10..8b81101574 100644 --- a/ui-ngx/src/app/shared/models/units/energy.ts +++ b/ui-ngx/src/app/shared/models/units/energy.ts @@ -40,42 +40,36 @@ const METRIC: TbMeasureUnits = { units: { Wm: { name: 'unit.watt-minute', - tags: ['energy', 'watt-minute', 'watt-minutes', 'Wm'], to_anchor: 60, }, Wh: { name: 'unit.watt-hour', - tags: ['energy', 'watt-hour', 'watt-hours', 'energy usage', 'power consumption', 'energy consumption', 'electricity usage'], + tags: ['energy usage', 'power consumption', 'energy consumption', 'electricity usage'], to_anchor: 3600, }, mWh: { name: 'unit.milliwatt-hour', - tags: ['energy', 'milliwatt-hour', 'milliwatt-hours', 'mWh'], to_anchor: 3.6, }, kWh: { name: 'unit.kilowatt-hour', - tags: ['energy', 'kilowatt-hour', 'kilowatt-hours', 'energy usage', 'power consumption', 'energy consumption', 'electricity usage'], + tags: ['energy usage', 'power consumption', 'energy consumption', 'electricity usage'], to_anchor: 3600000, }, MWh: { name: 'unit.megawatt-hour', - tags: ['energy', 'megawatt-hour', 'megawatt-hours', 'MWh'], to_anchor: 3600000000, }, GWh: { name: 'unit.gigawatt-hour', - tags: ['energy', 'gigawatt-hour', 'gigawatt-hours', 'GWh'], to_anchor: 3600000000000, }, μJ: { name: 'unit.microjoule', - tags: ['energy', 'microjoule', 'microjoules', 'μJ'], to_anchor: 0.000001, }, mJ: { name: 'unit.millijoule', - tags: ['energy', 'millijoule', 'millijoules', 'mJ'], to_anchor: 0.001, }, J: { @@ -85,22 +79,19 @@ const METRIC: TbMeasureUnits = { }, kJ: { name: 'unit.kilojoule', - tags: ['energy', 'kilojoule', 'kilojoules', 'kJ'], to_anchor: 1000, }, MJ: { name: 'unit.megajoule', - tags: ['energy', 'megajoule', 'megajoules', 'MJ'], to_anchor: 1000000, }, GJ: { name: 'unit.gigajoule', - tags: ['energy', 'gigajoule', 'gigajoules', 'GJ'], to_anchor: 1000000000, }, eV: { name: 'unit.electron-volts', - tags: ['energy', 'subatomic particles', 'radiation'], + tags: ['subatomic particles', 'radiation'], to_anchor: 1.602176634e-19, }, }, @@ -111,32 +102,31 @@ const IMPERIAL: TbMeasureUnits = { units: { cal: { name: 'unit.small-calorie', - tags: ['energy', 'small calorie', 'calories', 'cal'], to_anchor: 1, }, Cal: { name: 'unit.calorie', - tags: ['energy', 'food energy', 'Calorie', 'Calories', 'Cal'], + tags: ['food energy'], to_anchor: 1000, }, kcal: { name: 'unit.kilocalorie', - tags: ['energy', 'small calorie', 'kilocalories', 'kcal'], + tags: ['small calorie'], to_anchor: 1000, }, BTU: { name: 'unit.british-thermal-unit', - tags: ['energy', 'heat', 'work done', 'british thermal unit', 'british thermal units', 'BTU'], + tags: ['heat', 'work done'], to_anchor: 252.164401, }, 'ft·lb': { name: 'unit.foot-pound', - tags: ['energy', 'foot-pound', 'foot-pounds', 'ft·lb', 'ft⋅lbf'], + tags: ['ft⋅lbf'], to_anchor: 0.32404875717017, }, thm: { name: 'unit.therm', - tags: ['energy', 'natural gas consumption', 'BTU', 'therm', 'thm'], + tags: ['natural gas consumption', 'BTU'], to_anchor: 25219021.687207, }, }, diff --git a/ui-ngx/src/app/shared/models/units/force.ts b/ui-ngx/src/app/shared/models/units/force.ts index 46ee193829..afc98f626d 100644 --- a/ui-ngx/src/app/shared/models/units/force.ts +++ b/ui-ngx/src/app/shared/models/units/force.ts @@ -26,17 +26,15 @@ const METRIC: TbMeasureUnits = { units: { N: { name: 'unit.newton', - tags: ['force', 'pressure', 'newton', 'newtons', 'N', 'push', 'pull', 'weight', 'gravity', 'N'], + tags: ['pressure', 'push', 'pull', 'weight'], to_anchor: 1, }, kN: { name: 'unit.kilonewton', - tags: ['force', 'kN'], to_anchor: 1000, }, dyn: { name: 'unit.dyne', - tags: ['force', 'dyn'], to_anchor: 0.00001, }, }, @@ -47,27 +45,22 @@ const IMPERIAL: TbMeasureUnits = { units: { lbf: { name: 'unit.pound-force', - tags: ['force', 'lbf'], to_anchor: 1, }, kgf: { name: 'unit.kilogram-force', - tags: ['force', 'kgf'], to_anchor: 2.20462, }, klbf: { name: 'unit.kilopound-force', - tags: ['force', 'klbf'], to_anchor: 1000, }, pdl: { name: 'unit.poundal', - tags: ['force', 'pdl'], to_anchor: 0.031081, }, kip: { name: 'unit.kip', - tags: ['force', 'kip'], to_anchor: 1000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/frequency.ts b/ui-ngx/src/app/shared/models/units/frequency.ts index f677803fe8..4baf86405e 100644 --- a/ui-ngx/src/app/shared/models/units/frequency.ts +++ b/ui-ngx/src/app/shared/models/units/frequency.ts @@ -23,62 +23,61 @@ const METRIC: TbMeasureUnits = { units: { mHz: { name: 'unit.millihertz', - tags: ['frequency', 'cycles per second', 'millihertz', 'mHz'], + tags: ['cycles per second'], to_anchor: 1 / 1000, }, Hz: { name: 'unit.hertz', - tags: ['frequency', 'cycles per second', 'hertz', 'Hz'], + tags: ['cycles per second'], to_anchor: 1, }, kHz: { name: 'unit.kilohertz', - tags: ['frequency', 'cycles per second', 'kilohertz', 'kHz'], + tags: ['cycles per second'], to_anchor: 1000, }, MHz: { name: 'unit.megahertz', - tags: ['frequency', 'cycles per second', 'megahertz', 'MHz'], + tags: ['cycles per second'], to_anchor: 1000 * 1000, }, GHz: { name: 'unit.gigahertz', - tags: ['frequency', 'cycles per second', 'gigahertz', 'GHz'], + tags: ['cycles per second'], to_anchor: 1000 * 1000 * 1000, }, THz: { name: 'unit.terahertz', - tags: ['frequency', 'terahertz', 'THz'], to_anchor: 1000 * 1000 * 1000 * 1000, }, rpm: { name: 'unit.rotation-per-minute', - tags: ['frequency', 'rotation per minute', 'rotations per minute', 'rpm', 'angular velocity'], + tags: ['rotations per minute', 'angular velocity'], to_anchor: 1 / 60, }, RPM: { name: 'unit.rpm', - tags: ['rotational speed', 'angular velocity', 'revolutions per minute', 'RPM'], + tags: ['rotational speed', 'angular velocity'], to_anchor: 1 / 60, }, 'λ': { name: 'unit.lambda', - tags: ['wavelength', 'lambda', 'λ'], + tags: ['wavelength'], to_anchor: 299792458, }, bpm: { name: 'unit.beats-per-minute', - tags: ['heart rate', 'pulse', 'bpm'], + tags: ['heart rate', 'pulse'], to_anchor: 0.0167 }, 'deg/s': { name: 'unit.deg-per-second', - tags: ['angular velocity', 'degrees per second', 'deg/s'], - to_anchor: 1 / 360, // 1 deg/s = 1/360 Hz + tags: ['angular velocity'], + to_anchor: 1 / 360, }, 'rad/s': { name: 'unit.radian-per-second', - tags: ['angular velocity', 'rotation speed', 'rad/s'], + tags: ['angular velocity'], to_anchor: 1 / (Math.PI * 2), }, }, diff --git a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts index f097382089..64eb49cfb2 100644 --- a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts +++ b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type FuelEfficiencyMetricUnits = 'km/L' | 'L/100km'; @@ -10,12 +26,10 @@ const METRIC: TbMeasureUnits = { units: { 'km/L': { name: 'unit.kilometers-per-liter', - tags: ['fuel efficiency', 'km/L'], to_anchor: 1, }, 'L/100km': { name: 'unit.liters-per-100-km', - tags: ['fuel efficiency', 'L/100km'], to_anchor: 1, transform: (value) => 100 / value, }, @@ -27,12 +41,10 @@ const IMPERIAL: TbMeasureUnits = { units: { 'mpg': { name: 'unit.miles-per-gallon', - tags: ['fuel efficiency', 'mpg'], to_anchor: 0.425144, }, 'gal/mi': { name: 'unit.gallons-per-mile', - tags: ['fuel efficiency', 'gal/mi'], to_anchor: 2.35214583, }, }, diff --git a/ui-ngx/src/app/shared/models/units/heat-capacity.ts b/ui-ngx/src/app/shared/models/units/heat-capacity.ts index 05b56cd0a7..7c42926bd0 100644 --- a/ui-ngx/src/app/shared/models/units/heat-capacity.ts +++ b/ui-ngx/src/app/shared/models/units/heat-capacity.ts @@ -1,15 +1,29 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type HeatCapacityMetricUnits = 'J/K'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type HeatCapacityUnits = HeatCapacityMetricUnits; +export type HeatCapacityUnits = 'J/K'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/K': { name: 'unit.joule-per-kelvin', - tags: ['specific heat capacity', 'heat capacity per unit temperature', 'joule per kelvin', 'J/K'], - to_anchor: 1, // Base unit: J/K + tags: ['heat capacity per unit temperature'], + to_anchor: 1, }, }, }; diff --git a/ui-ngx/src/app/shared/models/units/humidity.ts b/ui-ngx/src/app/shared/models/units/humidity.ts deleted file mode 100644 index 22bf1d6902..0000000000 --- a/ui-ngx/src/app/shared/models/units/humidity.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type HumidityUnits = HumidityMetricUnits; -export type HumidityMetricUnits = 'g/kg'; - -const METRIC: TbMeasureUnits = { - units: { - 'g/kg': { - name: 'unit.gram-per-kilogram', - tags: ['humidity', 'moisture', 'specific humidity', 'g/kg'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/illuminance.ts b/ui-ngx/src/app/shared/models/units/illuminance.ts index 6c1ae4d43d..286cd4923d 100644 --- a/ui-ngx/src/app/shared/models/units/illuminance.ts +++ b/ui-ngx/src/app/shared/models/units/illuminance.ts @@ -26,17 +26,17 @@ const METRIC: TbMeasureUnits = { units: { lx: { name: 'unit.lux', - tags: ['illumination', 'light level on a surface', 'illuminance', 'Lux', 'lx'], + tags: ['light level on a surface', 'illuminance', 'Lux', 'lx'], to_anchor: 1, }, 'cd/m²': { name: 'unit.candela-per-square-meter', - tags: ['brightness', 'light level', 'Luminance', 'Candela per square meter', 'cd/m²'], + tags: ['brightness', 'light level', 'Luminance'], to_anchor: 1, }, 'lm/m²': { name: 'unit.lumen-per-square-meter', - tags: ['illumination', 'light level', 'lumen per square meter', 'lm/m²'], + tags: ['light level'], to_anchor: 1, }, }, @@ -47,7 +47,7 @@ const IMPERIAL: TbMeasureUnits = { units: { 'fc': { name: 'unit.foot-candle', - tags: ['illuminance', 'light level', 'foot-candle', 'fc'], + tags: ['illuminance', 'light level'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/inductance.ts b/ui-ngx/src/app/shared/models/units/inductance.ts index 9f0d0bb3eb..0acbab4774 100644 --- a/ui-ngx/src/app/shared/models/units/inductance.ts +++ b/ui-ngx/src/app/shared/models/units/inductance.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type InductanceMetricUnits = 'H' | 'mH' | 'µH' | 'nH' | 'T·m/A'; diff --git a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts index 6a9104ff9f..8d2201d456 100644 --- a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts +++ b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type KinematicViscosityMetricUnits = 'm²/s' | 'cm²/s' | 'St' | 'cSt'; @@ -10,23 +26,19 @@ const METRIC: TbMeasureUnits = { units: { 'm²/s': { name: 'unit.square-meter-per-second', - tags: ['kinematic viscosity', 'm²/s'], to_anchor: 1, }, 'cm²/s': { name: 'unit.square-centimeter-per-second', - tags: ['kinematic viscosity', 'cm²/s'], to_anchor: 1e-4, }, 'St': { name: 'unit.stoke', - tags: ['kinematic viscosity', 'stokes', 'St'], - to_anchor: 1e-4, // St to m²/s + to_anchor: 1e-4, }, 'cSt': { name: 'unit.centistokes', - tags: ['kinematic viscosity', 'centistokes', 'cSt'], - to_anchor: 1e-6, // cSt to m²/s + to_anchor: 1e-6, }, }, }; @@ -36,12 +48,10 @@ const IMPERIAL: TbMeasureUnits = { units: { 'ft²/s': { name: 'unit.square-foot-per-second', - tags: ['kinematic viscosity', 'ft²/s'], to_anchor: 0.09290304, }, 'in²/s': { name: 'unit.square-inch-per-second', - tags: ['kinematic viscosity', 'in²/s'], to_anchor: 0.00064516, }, }, diff --git a/ui-ngx/src/app/shared/models/units/length.ts b/ui-ngx/src/app/shared/models/units/length.ts index 4a13c73bb1..549a66c3e6 100644 --- a/ui-ngx/src/app/shared/models/units/length.ts +++ b/ui-ngx/src/app/shared/models/units/length.ts @@ -43,42 +43,42 @@ const METRIC: TbMeasureUnits = { units: { nm: { name: 'unit.nanometer', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'nanoscale', 'atomic scale', 'molecular scale', 'nanometer', 'nanometers', 'nm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'nanoscale', 'atomic scale', 'molecular scale'], to_anchor: 1e-9, }, μm: { name: 'unit.micrometer', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'microns', 'micrometer', 'micrometers', 'µm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'microns'], to_anchor: 1e-6, }, mm: { name: 'unit.millimeter', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'millimeter', 'millimeters', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition', 'mm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition'], to_anchor: 1e-3, }, cm: { name: 'unit.centimeter', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'centimeter', 'centimeters', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition', 'cm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition'], to_anchor: 1e-2, }, dm: { name: 'unit.decimeter', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'decimeter', 'decimeters', 'dm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 1e-1, }, m: { name: 'unit.meter', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'meter', 'meters', 'm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 1, }, km: { name: 'unit.kilometer', - tags: ['distance', 'height', 'length', 'width', 'gap', 'depth', 'kilometer', 'kilometers', 'km'], + tags: ['distance', 'height', 'width', 'gap', 'depth'], to_anchor: 1e3, }, angstrom: { name: 'unit.angstrom', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'atomic scale', 'atomic distance', 'nanoscale', 'angstrom', 'angstroms', 'Å'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'atomic scale', 'atomic distance', 'nanoscale'], to_anchor: 1e-10, }, }, @@ -89,87 +89,87 @@ const IMPERIAL: TbMeasureUnits = { units: { in: { name: 'unit.inch', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'inch', 'inches', 'in'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 1 / 12, }, yd: { name: 'unit.yard', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'yard', 'yards', 'yd'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 3, }, 'ft-us': { name: 'unit.foot-us', - tags: ['length', 'us survey foot', 'us survey feet', 'ft-us', 'surveying'], + tags: ['us survey foot', 'us survey feet', 'ft-us', 'surveying'], to_anchor: 1.000002, }, ft: { name: 'unit.foot', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'foot', 'feet', 'ft'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'feet'], to_anchor: 1, }, fathom: { name: 'unit.fathom', - tags: ['depth', 'nautical measurement', 'fathom'], + tags: ['depth', 'nautical measurement'], to_anchor: 6, }, mi: { name: 'unit.mile', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'mile', 'miles', 'mi'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 5280, }, nmi: { name: 'unit.nautical-mile', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'nautical mile', 'nmi'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'nautical mile'], to_anchor: 6076.12, }, thou: { name: 'unit.thou', - tags: ['length', 'measurement', 'thou'], + tags: ['measurement'], to_anchor: 0.001 / 12, }, barleycorn: { name: 'unit.barleycorn', - tags: ['length', 'shoe size', 'barleycorn'], + tags: ['shoe size'], to_anchor: 1 / 36, }, hand: { name: 'unit.hand', - tags: ['length', 'horse measurement', 'hand'], + tags: ['horse measurement'], to_anchor: 4 / 12, }, ch: { name: 'unit.chain', - tags: ['length', 'land surveying', 'ch'], + tags: ['land surveying'], to_anchor: 66, }, fur: { name: 'unit.furlong', - tags: ['length', 'land surveying', 'fur'], + tags: ['land surveying'], to_anchor: 660, }, league: { name: 'unit.league', - tags: ['length', 'historical measurement', 'league'], + tags: ['historical measurement'], to_anchor: 3 * 5280, }, link: { name: 'unit.link', - tags: ['length', 'land surveying', 'link'], + tags: ['land surveying'], to_anchor: 0.66, }, rod: { name: 'unit.rod', - tags: ['length', 'land surveying', 'rod'], + tags: ['land surveying'], to_anchor: 16.5, }, cable: { name: 'unit.cable', - tags: ['distance', 'nautical measurement', 'cable'], + tags: ['distance', 'nautical measurement'], to_anchor: 600, }, AU: { name: 'unit.astronomical-unit', - tags: ['distance', 'celestial bodies', 'solar system', 'AU'], + tags: ['distance', 'celestial bodies', 'solar system'], to_anchor: 149597870700 * 3.28084, }, }, diff --git a/ui-ngx/src/app/shared/models/units/light-exposure.ts b/ui-ngx/src/app/shared/models/units/light-exposure.ts index 353cf57a37..72dd8a920b 100644 --- a/ui-ngx/src/app/shared/models/units/light-exposure.ts +++ b/ui-ngx/src/app/shared/models/units/light-exposure.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type LightExposureMetricUnits = 'lx·s'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LightExposureUnits = LightExposureMetricUnits; +export type LightExposureUnits = 'lx·s'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'lx·s': { name: 'unit.lux-second', - tags: ['light exposure', 'illuminance over time', 'lux-second', 'lx·s'], + tags: ['illuminance over time'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/liner-charge-density.ts b/ui-ngx/src/app/shared/models/units/liner-charge-density.ts index 4a2239b1d7..904257e328 100644 --- a/ui-ngx/src/app/shared/models/units/liner-charge-density.ts +++ b/ui-ngx/src/app/shared/models/units/liner-charge-density.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type LinerChargeDensityMetricUnits = 'C/m'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LinerChargeDensityUnits = LinerChargeDensityMetricUnits; +export type LinerChargeDensityUnits = 'C/m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'C/m': { name: 'unit.coulomb-per-meter', - tags: ['electric displacement field per length', 'coulomb per meter', 'C/m'], + tags: ['electric displacement field per length'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts new file mode 100644 index 0000000000..cb34632711 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts @@ -0,0 +1,45 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LogarithmicRatioUnits = 'dB' | 'B' | 'Np'; + +const METRIC: TbMeasureUnits = { + units: { + 'dB': { + name: 'unit.decibel', + tags: ['noise level', 'sound level', 'volume', 'acoustics'], + to_anchor: 1, + }, + 'B': { + name: 'unit.bel', + tags: ['power ratio', 'intensity ratio'], + to_anchor: 10, + }, + 'Np': { + name: 'unit.neper', + tags: ['gain', 'loss', 'attenuation'], + to_anchor: 8.685889638, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-units.ts b/ui-ngx/src/app/shared/models/units/logarithmic-units.ts deleted file mode 100644 index ccbfd9af8e..0000000000 --- a/ui-ngx/src/app/shared/models/units/logarithmic-units.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type LogarithmicUnits = 'dB' | 'B' | 'Np'; - -const METRIC: TbMeasureUnits = { - units: { - 'dB': { - name: 'unit.decibel', - tags: ['noise level', 'sound level', 'volume', 'acoustics', 'decibel', 'dB'], - to_anchor: 1, - }, - 'B': { - name: 'unit.bel', - tags: ['logarithmic unit', 'power ratio', 'intensity ratio', 'bel', 'B'], - to_anchor: 10, - }, - 'Np': { - name: 'unit.neper', - tags: ['logarithmic unit', 'ratio', 'gain', 'loss', 'attenuation', 'neper', 'Np'], - to_anchor: 8.685889638, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts b/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts index 1aa5c496fb..cacc51132c 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type LuminousEfficacyMetricUnits = 'lm/W'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LuminousEfficacyUnits = LuminousEfficacyMetricUnits; +export type LuminousEfficacyUnits = 'lm/W'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'lm/W': { name: 'unit.lumens-per-watt', - tags: ['luminous efficacy', 'lighting efficiency', 'lumens per watt', 'lm/W'], + tags: ['lighting efficiency'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/luminous-flux.ts b/ui-ngx/src/app/shared/models/units/luminous-flux.ts index ff1b052919..0d91e372e8 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-flux.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-flux.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type LuminousFluxMetricUnits = 'lm'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LuminousFluxUnits = LuminousFluxMetricUnits; +export type LuminousFluxUnits = 'lm'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'lm': { name: 'unit.lumen', - tags: ['luminous flux', 'total light output', 'lumen', 'lm'], + tags: ['total light output'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts index 7168aab088..da85631822 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type LuminousIntensityMetricUnits = 'cd'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LuminousIntensityUnits = LuminousIntensityMetricUnits; +export type LuminousIntensityUnits = 'cd'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'cd': { name: 'unit.candela', - tags: ['luminous intensity', 'light intensity', 'candela', 'cd'], + tags: ['light intensity'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts b/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts index 41e8fade91..c5136698da 100644 --- a/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts +++ b/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts @@ -1,18 +1,31 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type MagneticFieldGradientUnits = MagneticFieldGradientMetricUnits; -export type MagneticFieldGradientMetricUnits = 'T/m' | 'G/cm'; +export type MagneticFieldGradientUnits = 'T/m' | 'G/cm'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'T/m': { name: 'unit.tesla-per-meter', - tags: ['magnetic field', 'tesla per meter', 'T/m'], to_anchor: 1, }, 'G/cm': { name: 'unit.gauss-per-centimeter', - tags: ['magnetic field', 'gauss per centimeter', 'G/cm'], to_anchor: 0.01, }, }, diff --git a/ui-ngx/src/app/shared/models/units/magnetic-field.ts b/ui-ngx/src/app/shared/models/units/magnetic-field.ts deleted file mode 100644 index 8996c7911b..0000000000 --- a/ui-ngx/src/app/shared/models/units/magnetic-field.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type MagneticFieldMetricUnits = 'T' | 'mT' | 'μT' | 'nT' | 'kT' | 'MT' | 'G' | 'kG' | 'γ' | 'A/m' | 'Oe'; - -export type MagneticFieldUnits = MagneticFieldMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'T': { - name: 'unit.tesla', - tags: ['magnetic field', 'magnetic field strength', 'tesla', 'T', 'magnetic flux density'], - to_anchor: 1, - }, - 'mT': { - name: 'unit.millitesla', - tags: ['magnetic field', 'magnetic field strength', 'millitesla', 'mT'], - to_anchor: 0.001, - }, - 'μT': { - name: 'unit.microtesla', - tags: ['magnetic field', 'magnetic field strength', 'microtesla', 'μT'], - to_anchor: 0.000001, - }, - 'nT': { - name: 'unit.nanotesla', - tags: ['magnetic field', 'magnetic field strength', 'nanotesla', 'nT'], - to_anchor: 0.000000001, - }, - 'kT': { - name: 'unit.kilotesla', - tags: ['magnetic field', 'magnetic field strength', 'kilotesla', 'kT'], - to_anchor: 1000, - }, - 'MT': { - name: 'unit.megatesla', - tags: ['magnetic field', 'magnetic field strength', 'megatesla', 'MT'], - to_anchor: 1000000, - }, - 'G': { - name: 'unit.gauss', - tags: ['magnetic field', 'magnetic field strength', 'gauss', 'G', 'magnetic flux density'], - to_anchor: 0.0001, - }, - 'kG': { - name: 'unit.kilogauss', - tags: ['magnetic field', 'magnetic field strength', 'kilogauss', 'kG', 'magnetic flux density'], - to_anchor: 0.1, - }, - 'γ': { - name: 'unit.gamma', - tags: ['magnetic flux density', 'gamma', 'γ'], - to_anchor: 0.000000001, - }, - 'A/m': { - name: 'unit.ampere-per-meter', - tags: ['magnetic field strength', 'magnetic field intensity', 'ampere per meter', 'A/m'], - to_anchor: 0.00000125663706143591, - }, - 'Oe': { - name: 'unit.oersted', - tags: ['magnetic field', 'oersted', 'Oe'], - to_anchor: 0.0001, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-flux-density.ts b/ui-ngx/src/app/shared/models/units/magnetic-flux-density.ts new file mode 100644 index 0000000000..4f93ead1be --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-flux-density.ts @@ -0,0 +1,84 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticFluxDensityUnits = 'T' | 'mT' | 'μT' | 'nT' | 'kT' | 'MT' | 'G' | 'kG' | 'γ' | 'A/m' | 'Oe'; + +const METRIC: TbMeasureUnits = { + units: { + T: { + name: 'unit.tesla', + tags: ['magnetic field strength'], + to_anchor: 1, + }, + mT: { + name: 'unit.millitesla', + tags: ['magnetic field strength'], + to_anchor: 0.001, + }, + μT: { + name: 'unit.microtesla', + tags: ['magnetic field strength'], + to_anchor: 0.000001, + }, + nT: { + name: 'unit.nanotesla', + tags: ['magnetic field strength'], + to_anchor: 0.000000001, + }, + kT: { + name: 'unit.kilotesla', + tags: ['magnetic field strength'], + to_anchor: 1000, + }, + MT: { + name: 'unit.megatesla', + tags: ['magnetic field strength'], + to_anchor: 1000000, + }, + G: { + name: 'unit.gauss', + tags: ['magnetic field strength'], + to_anchor: 0.0001, + }, + kG: { + name: 'unit.kilogauss', + tags: ['magnetic field strength'], + to_anchor: 0.1, + }, + γ: { + name: 'unit.gamma', + to_anchor: 0.000000001, + }, + 'A/m': { + name: 'unit.ampere-per-meter', + tags: ['magnetic field strength', 'magnetic field intensity'], + to_anchor: 0.00000125663706143591, + }, + Oe: { + name: 'unit.oersted', + tags: ['magnetic field strength'], + to_anchor: 0.0001, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-flux.ts b/ui-ngx/src/app/shared/models/units/magnetic-flux.ts index 467d317d7a..d503f92627 100644 --- a/ui-ngx/src/app/shared/models/units/magnetic-flux.ts +++ b/ui-ngx/src/app/shared/models/units/magnetic-flux.ts @@ -1,37 +1,48 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type MagneticFluxUnits = 'Wb' | 'µWb' | 'mWb' | 'Mx' | 'G·cm²' | 'kG·cm²'; const METRIC: TbMeasureUnits = { units: { - 'Wb': { + Wb: { name: 'unit.weber', - tags: ['magnetic flux', 'weber', 'Wb'], to_anchor: 1, }, - 'µWb': { + µWb: { name: 'unit.microweber', - tags: ['magnetic flux', 'microweber', 'µWb'], to_anchor: 1e-6, }, - 'mWb': { + mWb: { name: 'unit.milliweber', - tags: ['magnetic flux', 'milliweber', 'mWb'], to_anchor: 1e-3, }, - 'Mx': { + Mx: { name: 'unit.maxwell', - tags: ['magnetic flux', 'magnetic field', 'maxwell', 'Mx'], + tags: ['magnetic field'], to_anchor: 1e-8, }, 'G·cm²': { name: 'unit.gauss-square-centimeter', - tags: ['magnetic flux', 'gauss-square centimeter', 'G·cm²'], to_anchor: 1e-8, }, 'kG·cm²': { name: 'unit.kilogauss-square-centimeter', - tags: ['magnetic flux', 'kilogauss-square centimeter', 'kG·cm²'], to_anchor: 1e-5, }, }, diff --git a/ui-ngx/src/app/shared/models/units/magnetic-moment.ts b/ui-ngx/src/app/shared/models/units/magnetic-moment.ts index d7771bdf33..670a88a400 100644 --- a/ui-ngx/src/app/shared/models/units/magnetic-moment.ts +++ b/ui-ngx/src/app/shared/models/units/magnetic-moment.ts @@ -1,19 +1,33 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type MagneticMomentMetricUnits = 'A·m²' | 'μB'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type MagneticMomentUnits = MagneticMomentMetricUnits; +export type MagneticMomentUnits = 'A·m²' | 'μB'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'A·m²': { name: 'unit.magnetic-dipole-moment', - tags: ['magnetic dipole', 'dipole moment', 'ampere square meter', 'A·m²'], + tags: ['magnetic dipole moment'], to_anchor: 1, }, - 'μB': { + μB: { name: 'unit.bohr-magneton', - tags: ['atomic physics', 'magnetic moment', 'bohr magneton', 'μB'], + tags: ['atomic physics'], to_anchor: 9.274e-24, }, }, diff --git a/ui-ngx/src/app/shared/models/units/magnetic-permeability.ts b/ui-ngx/src/app/shared/models/units/magnetic-permeability.ts new file mode 100644 index 0000000000..73052db8d9 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-permeability.ts @@ -0,0 +1,40 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticPermeabilityUnits = 'H/m' | 'G/Oe'; + +const METRIC: TbMeasureUnits = { + units: { + 'H/m': { + name: 'unit.henry-per-meter', + to_anchor: 1, + }, + 'G/Oe': { + name: 'unit.gauss-per-oersted', + tags: ['magnetic field'], + to_anchor: 1/ 795774.715, + }, + }, +}; + + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/mass-fraction.ts b/ui-ngx/src/app/shared/models/units/mass-fraction.ts new file mode 100644 index 0000000000..23136c2447 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/mass-fraction.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MassFractionUnits = '°Bx'; + +const METRIC: TbMeasureUnits = { + units: { + '°Bx': { + name: 'unit.degrees-brix', + tags: ['sugar content', 'fruit ripeness'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/mass.ts b/ui-ngx/src/app/shared/models/units/mass.ts index fbed798da3..75d1d578a6 100644 --- a/ui-ngx/src/app/shared/models/units/mass.ts +++ b/ui-ngx/src/app/shared/models/units/mass.ts @@ -26,37 +26,37 @@ const METRIC: TbMeasureUnits = { units: { ng: { name: 'unit.nanogram', - tags: ['mass', 'weight', 'heaviness', 'load', 'nanogram', 'nanograms', 'ng'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1e-9, }, mcg: { name: 'unit.microgram', - tags: ['mass', 'weight', 'heaviness', 'load', 'μg', 'microgram'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1e-6, }, mg: { name: 'unit.milligram', - tags: ['mass', 'weight', 'heaviness', 'load', 'milligram', 'miligrams', 'mg'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1e-3, }, g: { name: 'unit.gram', - tags: ['mass', 'weight', 'heaviness', 'load', 'gram', 'grams', 'g'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1, }, kg: { name: 'unit.kilogram', - tags: ['mass', 'weight', 'heaviness', 'load', 'kilogram', 'kilograms', 'kg'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1000, }, t: { name: 'unit.tonne', - tags: ['mass', 'weight', 'heaviness', 'load', 'tonne', 'tons', 't'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1000000, }, Da: { name: 'unit.dalton', - tags: ['atomic mass unit', 'AMU', 'unified atomic mass unit', 'dalton', 'Da'], + tags: ['atomic mass unit', 'AMU', 'unified atomic mass unit'], to_anchor: 1.66053906660e-24, }, ct: { @@ -72,47 +72,47 @@ const IMPERIAL: TbMeasureUnits = { units: { oz: { name: 'unit.ounce', - tags: ['mass', 'weight', 'heaviness', 'load', 'ounce', 'ounces', 'oz'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1 / 16, }, lb: { name: 'unit.pound', - tags: ['mass', 'weight', 'heaviness', 'load', 'pound', 'pounds', 'lb'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1, }, st: { name: 'unit.stone', - tags: ['mass', 'weight', 'heaviness', 'load', 'stone', 'stones', 'st'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 14, }, 'short tons': { name: 'unit.short-tons', - tags: ['mass', 'weight', 'heaviness', 'load', 'short ton', 'short tons'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 2000, }, gr: { name: 'unit.grain', - tags: ['mass', 'measurement', 'grain', 'gr'], + tags: ['measurement'], to_anchor: 1 / 7000, }, dr: { name: 'unit.drachm', - tags: ['mass', 'measurement', 'drachm', 'dr'], + tags: ['measurement'], to_anchor: 1 / 256, }, qr: { name: 'unit.quarter', - tags: ['mass', 'measurement', 'quarter', 'qr'], + tags: ['measurement'], to_anchor: 28, }, cwt: { name: 'unit.hundredweight-count', - tags: ['mass', 'weight', 'heaviness', 'load', 'hundredweight count', 'cwt'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 100, }, slug: { name: 'unit.slug', - tags: ['mass', 'measurement', 'slug'], + tags: ['measurement'], to_anchor: 32.174, }, }, diff --git a/ui-ngx/src/app/shared/models/units/molar-concentration.ts b/ui-ngx/src/app/shared/models/units/molar-concentration.ts new file mode 100644 index 0000000000..e0d3c1ef30 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/molar-concentration.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MolarConcentrationUnits = 'mol/m³'; + +const METRIC: TbMeasureUnits = { + units: { + 'mol/m³': { + name: 'unit.mole-per-cubic-meter', + tags: ['amount of substance per unit volume'], + to_anchor: 1, + } + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/molar-energy.ts b/ui-ngx/src/app/shared/models/units/molar-energy.ts index 41e7b5a75e..99162d314f 100644 --- a/ui-ngx/src/app/shared/models/units/molar-energy.ts +++ b/ui-ngx/src/app/shared/models/units/molar-energy.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type MolarEnergyMetricUnits = 'J/mol'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type MolarEnergyUnits = MolarEnergyMetricUnits; +export type MolarEnergyUnits = 'J/mol'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/mol': { name: 'unit.joule-per-mole', - tags: ['molar energy', 'joule per mole', 'J/mol'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts index 104a44b3de..ee2b4b1752 100644 --- a/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts +++ b/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type MolarHeatCapacityMetricUnits = 'J/(mol·K)'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type MolarHeatCapacityUnits = MolarHeatCapacityMetricUnits; +export type MolarHeatCapacityUnits = 'J/(mol·K)'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/(mol·K)': { name: 'unit.joule-per-mole-kelvin', - tags: ['molar heat capacity', 'joule per mole-kelvin', 'J/(mol·K)'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/molar-mass.ts b/ui-ngx/src/app/shared/models/units/molar-mass.ts index c9a6b89639..9bbb4ab9e3 100644 --- a/ui-ngx/src/app/shared/models/units/molar-mass.ts +++ b/ui-ngx/src/app/shared/models/units/molar-mass.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type MolarMassUnits = 'kg/mol' | 'g/mol' | 'mg/mol'; @@ -6,17 +22,14 @@ const METRIC: TbMeasureUnits = { units: { 'g/mol': { name: 'unit.gram-per-mole', - tags: ['molar mass', 'gram per mole', 'g/mol'], to_anchor: 1, }, 'kg/mol': { name: 'unit.kilogram-per-mole', - tags: ['molar mass', 'kilogram per mole', 'kg/mol'], to_anchor: 1e3, }, 'mg/mol': { name: 'unit.milligram-per-mole', - tags: ['molar mass', 'milligram per mole', 'mg/mol'], to_anchor: 1e-3, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-humidity.ts b/ui-ngx/src/app/shared/models/units/specific-humidity.ts new file mode 100644 index 0000000000..82f12b26e2 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/specific-humidity.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpecificHumidityUnits = 'g/kg'; + +const METRIC: TbMeasureUnits = { + units: { + 'g/kg': { + name: 'unit.gram-per-kilogram', + tags: ['humidity', 'moisture', 'specific humidity', 'g/kg'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c685b6171e..c6e2d31cbc 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5866,18 +5866,54 @@ }, "measures": { "acceleration": "Acceleration", + "air-quality-index": "Air quality index", + "amount-of-substance": "Amount of substance", "angle": "Angle", "angular-acceleration": "Angular acceleration", "area": "Area", + "capacitance": "Capacitance", + "catalytic-activity": "Catalytic activity", + "catalytic-concetration": "Catalytic concetration", "charge": "Charge", + "current-density": "Current density", + "data-transfer-rate": "Data transfer rate", "digital": "Digital", + "dimension-ratio": "Dimension ratio", + "dynamic-viscosity": "Dynamic viscosity", + "earthquake-magnitude": "Earthquake magnitude", "electric-current": "Electric current", + "electric-dipole-moment": "Electric dipole moment", + "electric-field-strength": "Electric field strength", + "electric-flux": "Electric flux", + "electric-permittivity": "Electric permittivity", + "electrical-conductance": "Electrical conductance", + "electrical-conductivity": "Electrical conductivity", "energy": "Energy", "force": "Force", "frequency": "Frequency", + "fuel-efficiency": "fuel efficiency", + "heat-capacity": "Heat capacity", "illuminance": "Illuminance", + "inductance": "Inductance", + "kinematic-viscosity": "Kinematic viscosity", "length": "Length", + "light-exposure": "Light exposure", + "logarithmic-ratio": "Logarithmic ratio", + "luminous-efficacy": "Luminous efficacy", + "luminous-flux": "Luminous flux", + "luminous-intensity": "Luminous intensity", + "magnetic-field-gradient": "Magnetic field gradient", + "magnetic-flux": "Magnetic flux", + "magnetic-flux-density": "Magnetic flux density", + "magnetic-moment": "Magnetic moment", + "magnetic-permeability": "Magnetic permeability", "mass": "Mass", + "mass-fraction": "Mass fraction", + "molar-concetration": "Molar concetration", + "molar-energy": "Molar energy", + "molar-heat-capacity": "Molar heat capacity", + "molar-mass": "Molar mass", + "specific-humidity": "Specific humidity", "temperature": "Temperature", "time": "Time" }, From 338d81e6c1ecccbb3db7b22e8775a0170136938f Mon Sep 17 00:00:00 2001 From: yuliaklochai Date: Tue, 6 May 2025 09:27:29 +0300 Subject: [PATCH 133/335] UI: added TrendzSettingsService to Services Map --- ui-ngx/src/app/core/http/public-api.ts | 1 + ui-ngx/src/app/core/http/trendz-settings.service.ts | 10 +++++----- ui-ngx/src/app/modules/home/models/services.map.ts | 4 +++- ui-ngx/src/app/shared/models/public-api.ts | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/core/http/public-api.ts b/ui-ngx/src/app/core/http/public-api.ts index 997689d98e..c28d80d173 100644 --- a/ui-ngx/src/app/core/http/public-api.ts +++ b/ui-ngx/src/app/core/http/public-api.ts @@ -47,3 +47,4 @@ export * from './user.service'; export * from './user-settings.service'; export * from './widget.service'; export * from './usage-info.service'; +export * from './trendz-settings.service' diff --git a/ui-ngx/src/app/core/http/trendz-settings.service.ts b/ui-ngx/src/app/core/http/trendz-settings.service.ts index 4d965f920e..82f6973e25 100644 --- a/ui-ngx/src/app/core/http/trendz-settings.service.ts +++ b/ui-ngx/src/app/core/http/trendz-settings.service.ts @@ -18,7 +18,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { TrendzSettings } from '@shared/models/trendz-settings.models'; -import { defaultHttpOptionsFromConfig } from '@core/http/http-utils'; +import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; @Injectable({ providedIn: 'root' @@ -29,11 +29,11 @@ export class TrendzSettingsService { private http: HttpClient ) {} - public getTrendzSettings(): Observable { - return this.http.get(`/api/trendz/settings`, defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true})) + public getTrendzSettings(config?: RequestConfig): Observable { + return this.http.get(`/api/trendz/settings`, defaultHttpOptionsFromConfig(config)) } - public saveTrendzSettings(trendzSettings: TrendzSettings): Observable { - return this.http.post(`/api/trendz/settings`, trendzSettings, defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true})) + public saveTrendzSettings(trendzSettings: TrendzSettings, config?: RequestConfig): Observable { + return this.http.post(`/api/trendz/settings`, trendzSettings, defaultHttpOptionsFromConfig(config)) } } diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index e4bbc15936..517b219904 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -52,6 +52,7 @@ import { UiSettingsService } from '@core/http/ui-settings.service'; import { UsageInfoService } from '@core/http/usage-info.service'; import { EventService } from '@core/http/event.service'; import { AuditLogService } from '@core/http/audit-log.service'; +import { TrendzSettingsService } from '@core/http/trendz-settings.service'; export const ServicesMap = new Map>( [ @@ -91,6 +92,7 @@ export const ServicesMap = new Map>( ['usageInfoService', UsageInfoService], ['notificationService', NotificationService], ['eventService', EventService], - ['auditLogService', AuditLogService] + ['auditLogService', AuditLogService], + ['trendzSettingsService', TrendzSettingsService] ] ); diff --git a/ui-ngx/src/app/shared/models/public-api.ts b/ui-ngx/src/app/shared/models/public-api.ts index 53e4bb286e..9f7470523e 100644 --- a/ui-ngx/src/app/shared/models/public-api.ts +++ b/ui-ngx/src/app/shared/models/public-api.ts @@ -62,3 +62,4 @@ export * from './window-message.model'; export * from './usage.models'; export * from './query/query.models'; export * from './regex.constants'; +export * from './trendz-settings.models' From ac77910d9aeacda66c635015f467f8774c716d64 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 6 May 2025 14:24:51 +0300 Subject: [PATCH 134/335] Fix HashPartitionServiceTest --- .../server/queue/discovery/HashPartitionServiceTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java index a149ca6dfd..52cc5eb00f 100644 --- a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java @@ -436,6 +436,7 @@ public class HashPartitionServiceTest { ReflectionTestUtils.setField(partitionService, "edgeTopic", "tb.edge"); ReflectionTestUtils.setField(partitionService, "edgePartitions", 10); ReflectionTestUtils.setField(partitionService, "edqsPartitions", 12); + ReflectionTestUtils.setField(partitionService, "tasksPartitions", 12); partitionService.init(); partitionService.partitionsInit(); return partitionService; From b74d88284c0099998fa98a9e71bb2064acd00c3b Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Tue, 6 May 2025 15:37:11 +0300 Subject: [PATCH 135/335] Add TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE to ymls --- edqs/src/main/resources/edqs.yml | 6 +++++- msa/vc-executor/src/main/resources/tb-vc-executor.yml | 5 +++++ transport/coap/src/main/resources/tb-coap-transport.yml | 5 +++++ transport/http/src/main/resources/tb-http-transport.yml | 5 +++++ transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml | 5 +++++ transport/mqtt/src/main/resources/tb-mqtt-transport.yml | 5 +++++ transport/snmp/src/main/resources/tb-snmp-transport.yml | 5 +++++ 7 files changed, 35 insertions(+), 1 deletion(-) diff --git a/edqs/src/main/resources/edqs.yml b/edqs/src/main/resources/edqs.yml index 1cc32a4230..6e6e975d68 100644 --- a/edqs/src/main/resources/edqs.yml +++ b/edqs/src/main/resources/edqs.yml @@ -148,7 +148,11 @@ queue: - key: max.poll.records # Max poll records for edqs.state topic value: "${TB_QUEUE_KAFKA_EDQS_STATE_MAX_POLL_RECORDS:512}" - + # If you override any default Kafka topic name using environment variables, you must also specify the related consumer properties + # for the new topic in `consumer-properties-per-topic-inline`. Otherwise, the topic will not inherit its expected configuration (e.g., max.poll.records, timeouts, etc). + # Format: "topic1:key1=value1,key2=value2;topic2:key=value" + # Example: "tb_core_modified.notifications:max.poll.records=10;tb_edge_modified:max.poll.records=10,enable.auto.commit=true" + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section, you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index bb7f607ca0..7d1166e512 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -124,6 +124,11 @@ queue: # tb_rule_engine.sq: # - key: max.poll.records # value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}" + # If you override any default Kafka topic name using environment variables, you must also specify the related consumer properties + # for the new topic in `consumer-properties-per-topic-inline`. Otherwise, the topic will not inherit its expected configuration (e.g., max.poll.records, timeouts, etc). + # Format: "topic1:key1=value1,key2=value2;topic2:key=value" + # Example: "tb_core_modified.notifications:max.poll.records=10;tb_edge_modified:max.poll.records=10,enable.auto.commit=true" + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index f60a6bd47e..0c5d160625 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -310,6 +310,11 @@ queue: sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" + # If you override any default Kafka topic name using environment variables, you must also specify the related consumer properties + # for the new topic in `consumer-properties-per-topic-inline`. Otherwise, the topic will not inherit its expected configuration (e.g., max.poll.records, timeouts, etc). + # Format: "topic1:key1=value1,key2=value2;topic2:key=value" + # Example: "tb_core_modified.notifications:max.poll.records=10;tb_edge_modified:max.poll.records=10,enable.auto.commit=true" + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index c921f9f9ae..81f4719e45 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -259,6 +259,11 @@ queue: sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" + # If you override any default Kafka topic name using environment variables, you must also specify the related consumer properties + # for the new topic in `consumer-properties-per-topic-inline`. Otherwise, the topic will not inherit its expected configuration (e.g., max.poll.records, timeouts, etc). + # Format: "topic1:key1=value1,key2=value2;topic2:key=value" + # Example: "tb_core_modified.notifications:max.poll.records=10;tb_edge_modified:max.poll.records=10,enable.auto.commit=true" + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 85e865e60e..2c62a35dbb 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -360,6 +360,11 @@ queue: sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" + # If you override any default Kafka topic name using environment variables, you must also specify the related consumer properties + # for the new topic in `consumer-properties-per-topic-inline`. Otherwise, the topic will not inherit its expected configuration (e.g., max.poll.records, timeouts, etc). + # Format: "topic1:key1=value1,key2=value2;topic2:key=value" + # Example: "tb_core_modified.notifications:max.poll.records=10;tb_edge_modified:max.poll.records=10,enable.auto.commit=true" + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index a6ca2f1a6e..51f9a17005 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -293,6 +293,11 @@ queue: sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" + # If you override any default Kafka topic name using environment variables, you must also specify the related consumer properties + # for the new topic in `consumer-properties-per-topic-inline`. Otherwise, the topic will not inherit its expected configuration (e.g., max.poll.records, timeouts, etc). + # Format: "topic1:key1=value1,key2=value2;topic2:key=value" + # Example: "tb_core_modified.notifications:max.poll.records=10;tb_edge_modified:max.poll.records=10,enable.auto.commit=true" + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index 6848e8af26..f4811b3326 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -239,6 +239,11 @@ queue: sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" + # If you override any default Kafka topic name using environment variables, you must also specify the related consumer properties + # for the new topic in `consumer-properties-per-topic-inline`. Otherwise, the topic will not inherit its expected configuration (e.g., max.poll.records, timeouts, etc). + # Format: "topic1:key1=value1,key2=value2;topic2:key=value" + # Example: "tb_core_modified.notifications:max.poll.records=10;tb_edge_modified:max.poll.records=10,enable.auto.commit=true" + consumer-properties-per-topic-inline: "${TB_QUEUE_KAFKA_CONSUMER_PROPERTIES_PER_TOPIC_INLINE:}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms From a34616dd4125ce57009747fa40c5e714cfc1c116 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 6 May 2025 11:13:53 +0300 Subject: [PATCH 136/335] UI: Updated unit definition --- ui-ngx/src/app/shared/models/unit.models.ts | 121 ++++++++++++++++-- .../shared/models/units/absorbed-dose-rate.ts | 35 +++++ .../app/shared/models/units/acceleration.ts | 12 +- ui-ngx/src/app/shared/models/units/acidity.ts | 35 +++++ .../models/units/amount-of-substance.ts | 10 +- .../app/shared/models/units/area-density.ts | 35 +++++ .../app/shared/models/units/capacitance.ts | 20 +-- .../shared/models/units/data-transfer-rate.ts | 10 +- .../shared/models/units/dimension-ratio.ts | 7 +- .../shared/models/units/dynamic-viscosity.ts | 4 +- .../models/units/electric-charge-density.ts | 34 +++++ .../models/units/electric-dipole-moment.ts | 2 +- .../models/units/electric-polarizability.ts | 35 +++++ .../models/units/electrical-conductance.ts | 12 +- .../app/shared/models/units/energy-density.ts | 34 +++++ .../shared/models/units/fuel-efficiency.ts | 2 +- .../app/shared/models/units/illuminance.ts | 2 +- .../models/units/kinematic-viscosity.ts | 4 +- .../shared/models/units/logarithmic-ratio.ts | 6 +- .../app/shared/models/units/luminous-flux.ts | 2 +- .../shared/models/units/luminous-intensity.ts | 2 +- .../models/units/number-concentration.ts | 35 +++++ .../models/units/particle-concentration.ts | 19 --- .../{parts-per.ts => parts-per-million.ts} | 11 +- .../src/app/shared/models/units/percentage.ts | 20 --- ui-ngx/src/app/shared/models/units/ph.ts | 19 --- .../app/shared/models/units/polarization.ts | 21 --- .../app/shared/models/units/power-density.ts | 32 +++-- ui-ngx/src/app/shared/models/units/power.ts | 22 ++-- .../src/app/shared/models/units/pressure.ts | 60 ++++----- .../src/app/shared/models/units/radiance.ts | 18 ++- .../shared/models/units/radiant-intensity.ts | 23 +++- .../app/shared/models/units/radiation-dose.ts | 49 ++++--- .../models/units/radioactive-decay-rate.ts | 25 ---- .../shared/models/units/radioactive-decay.ts | 38 ++++++ .../units/radioactivity-concentration.ts | 25 +++- .../app/shared/models/units/radioactivity.ts | 41 +++--- .../shared/models/units/reciprocal-length.ts | 35 +++++ .../src/app/shared/models/units/resistance.ts | 46 ++++--- .../shared/models/units/reynolds-number.ts | 24 +++- .../app/shared/models/units/signal-level.ts | 45 +++++++ .../shared/models/units/signal-strength.ts | 31 ----- .../app/shared/models/units/solid-angle.ts | 26 +++- .../shared/models/units/specific-energy.ts | 23 +++- .../models/units/specific-heat-capacity.ts | 8 +- .../shared/models/units/specific-humidity.ts | 2 +- .../shared/models/units/specific-volume.ts | 24 +++- ui-ngx/src/app/shared/models/units/speed.ts | 20 +-- .../app/shared/models/units/sugar-content.ts | 19 --- .../models/units/surface-charge-density.ts | 24 +++- .../shared/models/units/surface-tension.ts | 26 +++- .../app/shared/models/units/temperature.ts | 8 +- .../models/units/thermal-conductivity.ts | 23 +++- ui-ngx/src/app/shared/models/units/time.ts | 22 ++-- ui-ngx/src/app/shared/models/units/torque.ts | 6 +- .../src/app/shared/models/units/turbidity.ts | 23 +++- ui-ngx/src/app/shared/models/units/voltage.ts | 16 +-- .../models/units/volume-charge-density.ts | 21 --- .../{volume-flow-rate.ts => volume-flow.ts} | 44 +++---- ui-ngx/src/app/shared/models/units/volume.ts | 42 +++--- .../src/app/shared/models/units/wavenumber.ts | 21 --- .../assets/locale/locale.constant-en_US.json | 43 ++++++- 62 files changed, 1007 insertions(+), 497 deletions(-) create mode 100644 ui-ngx/src/app/shared/models/units/absorbed-dose-rate.ts create mode 100644 ui-ngx/src/app/shared/models/units/acidity.ts create mode 100644 ui-ngx/src/app/shared/models/units/area-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-charge-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-polarizability.ts create mode 100644 ui-ngx/src/app/shared/models/units/energy-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/number-concentration.ts delete mode 100644 ui-ngx/src/app/shared/models/units/particle-concentration.ts rename ui-ngx/src/app/shared/models/units/{parts-per.ts => parts-per-million.ts} (80%) delete mode 100644 ui-ngx/src/app/shared/models/units/percentage.ts delete mode 100644 ui-ngx/src/app/shared/models/units/ph.ts delete mode 100644 ui-ngx/src/app/shared/models/units/polarization.ts delete mode 100644 ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts create mode 100644 ui-ngx/src/app/shared/models/units/radioactive-decay.ts create mode 100644 ui-ngx/src/app/shared/models/units/reciprocal-length.ts create mode 100644 ui-ngx/src/app/shared/models/units/signal-level.ts delete mode 100644 ui-ngx/src/app/shared/models/units/signal-strength.ts delete mode 100644 ui-ngx/src/app/shared/models/units/sugar-content.ts delete mode 100644 ui-ngx/src/app/shared/models/units/volume-charge-density.ts rename ui-ngx/src/app/shared/models/units/{volume-flow-rate.ts => volume-flow.ts} (56%) delete mode 100644 ui-ngx/src/app/shared/models/units/wavenumber.ts diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index c2763ff632..3fcf742cb0 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -14,12 +14,15 @@ /// limitations under the License. /// +import absorbedDoseRate, { AbsorbedDoseRateUnits } from '@shared/models/units/absorbed-dose-rate'; import acceleration, { AccelerationUnits } from '@shared/models/units/acceleration'; +import acidity, { AcidityUnits } from '@shared/models/units/acidity'; import airQualityIndex, { AirQualityIndexUnits } from '@shared/models/units/air-quality-index'; import amountOfSubstance, { AmountOfSubstanceUnits } from '@shared/models/units/amount-of-substance'; import angle, { AngleUnits } from '@shared/models/units/angle'; import angularAcceleration, { AngularAccelerationUnits } from '@shared/models/units/angular-acceleration'; import area, { AreaUnits } from '@shared/models/units/area'; +import areaDensity, { AreaDensityUnits } from '@shared/models/units/area-density'; import capacitance, { CapacitanceUnits } from '@shared/models/units/capacitance'; import catalyticActivity, { CatalyticActivityUnits } from '@shared/models/units/catalytic-activity'; import catalyticConcentration, { CatalyticConcentrationUnits } from '@shared/models/units/catalytic-concentration'; @@ -31,14 +34,17 @@ import digital, { DigitalUnits } from '@shared/models/units/digital'; import dimensionRatio, { DimensionRatioUnits } from '@shared/models/units/dimension-ratio'; import dynamicViscosity, { DynamicViscosityUnits } from '@shared/models/units/dynamic-viscosity'; import earthquakeMagnitude, { EarthquakeMagnitudeUnits } from '@shared/models/units/earthquake-magnitude'; +import electricChargeDensity, { ElectricChargeDensityUnits } from '@shared/models/units/electric-charge-density'; import electricCurrent, { ElectricCurrentUnits } from '@shared/models/units/electric-current'; import electricDipoleMoment, { ElectricDipoleMomentUnits } from '@shared/models/units/electric-dipole-moment'; import electricFieldStrength, { ElectricFieldStrengthUnits } from '@shared/models/units/electric-field-strength'; import electricFlux, { ElectricFluxUnits } from '@shared/models/units/electric-flux'; import electricPermittivity, { ElectricPermittivityUnits } from '@shared/models/units/electric-permittivity'; +import electricPolarizability, { ElectricPolarizabilityUnits } from '@shared/models/units/electric-polarizability'; import electricalConductance, { ElectricalConductanceUnits } from '@shared/models/units/electrical-conductance'; import electricalConductivity, { ElectricalConductivityUnits } from '@shared/models/units/electrical-conductivity'; import energy, { EnergyUnits } from '@shared/models/units/energy'; +import energyDensity, { EnergyDensityUnits } from '@shared/models/units/energy-density'; import force, { ForceUnits } from '@shared/models/units/force'; import fuelEfficiency, { FuelEfficiencyUnits } from '@shared/models/units/fuel-efficiency'; import frequency, { FrequencyUnits } from '@shared/models/units/frequency'; @@ -64,26 +70,51 @@ import molarConcentration, { MolarConcentrationUnits } from '@shared/models/unit import molarEnergy, { MolarEnergyUnits } from '@shared/models/units/molar-energy'; import molarHeatCapacity, { MolarHeatCapacityUnits } from '@shared/models/units/molar-heat-capacity'; import molarMass, { MolarMassUnits } from '@shared/models/units/molar-mass'; -import partsPer, { PartsPerUnits } from '@shared/models/units/parts-per'; +import numberConcentration, { NumberConcentrationUnits } from '@shared/models/units/number-concentration'; +import partsPerMillion, { PartsPerMillionUnits } from '@shared/models/units/parts-per-million'; import power, { PowerUnits } from '@shared/models/units/power'; +import powerDensity, { PowerDensityUnits } from '@shared/models/units/power-density'; import pressure, { PressureUnits } from '@shared/models/units/pressure'; +import radiance, { RadianceUnits } from '@shared/models/units/radiance'; +import radiantIntensity, { RadiantIntensityUnits } from '@shared/models/units/radiant-intensity'; +import radiationDose, { RadiationDoseUnits } from '@shared/models/units/radiation-dose'; +import radioactiveDecay, { RadioactiveDecayUnits } from '@shared/models/units/radioactive-decay'; +import radioactivity, { RadioactivityUnits } from '@shared/models/units/radioactivity'; +import radioactivityConcentration, { + RadioactivityConcentrationUnits +} from '@shared/models/units/radioactivity-concentration'; +import resistance, { ResistanceUnits } from '@shared/models/units/resistance'; +import reynoldsNumber, { ReynoldsNumberUnits } from '@shared/models/units/reynolds-number'; +import signalLevel, { SignalLevelUnits } from '@shared/models/units/signal-level'; +import solidAngle, { SolidAngleUnits } from '@shared/models/units/solid-angle'; +import specificEnergy, { SpecificEnergyUnits } from '@shared/models/units/specific-energy'; +import specificHeatCapacity, { SpecificHeatCapacityUnits } from '@shared/models/units/specific-heat-capacity'; import specificHumidity, { SpecificHumidityUnits } from '@shared/models/units/specific-humidity'; +import specificVolume, { SpecificVolumeUnits } from '@shared/models/units/specific-volume'; import speed, { SpeedUnits } from '@shared/models/units/speed'; +import surfaceChargeDensity, { SurfaceChargeDensityUnits } from '@shared/models/units/surface-charge-density'; +import surfaceTension, { SurfaceTensionUnits } from '@shared/models/units/surface-tension'; import temperature, { TemperatureUnits } from '@shared/models/units/temperature'; +import thermalConductivity, { ThermalConductivityUnits } from '@shared/models/units/thermal-conductivity'; import time, { TimeUnits } from '@shared/models/units/time'; import torque, { TorqueUnits } from '@shared/models/units/torque'; +import turbidity, { TurbidityUnits } from '@shared/models/units/turbidity'; import voltage, { VoltageUnits } from '@shared/models/units/voltage'; import volume, { VolumeUnits } from '@shared/models/units/volume'; -import volumeFlowRate, { VolumeFlowRateUnits } from '@shared/models/units/volume-flow-rate'; +import volumeFlow, { VolumeFlowUnits } from '@shared/models/units/volume-flow'; import { TranslateService } from '@ngx-translate/core'; +import reciprocalLength, { ReciprocalLengthUnits } from '@shared/models/units/reciprocal-length'; export type AllMeasuresUnits = + | AbsorbedDoseRateUnits | AccelerationUnits + | AcidityUnits | AirQualityIndexUnits | AmountOfSubstanceUnits | AngleUnits | AngularAccelerationUnits | AreaUnits + | AreaDensityUnits | CapacitanceUnits | CatalyticActivityUnits | CatalyticConcentrationUnits @@ -95,14 +126,17 @@ export type AllMeasuresUnits = | DimensionRatioUnits | DynamicViscosityUnits | EarthquakeMagnitudeUnits + | ElectricChargeDensityUnits | ElectricCurrentUnits | ElectricDipoleMomentUnits | ElectricFieldStrengthUnits | ElectricFluxUnits | ElectricPermittivityUnits + | ElectricPolarizabilityUnits | ElectricalConductanceUnits | ElectricalConductivityUnits | EnergyUnits + | EnergyDensityUnits | ForceUnits | FrequencyUnits | FuelEfficiencyUnits @@ -128,20 +162,42 @@ export type AllMeasuresUnits = | MolarEnergyUnits | MolarHeatCapacityUnits | MolarMassUnits - | PartsPerUnits + | NumberConcentrationUnits + | PartsPerMillionUnits | PowerUnits + | PowerDensityUnits | PressureUnits + | RadianceUnits + | RadiantIntensityUnits + | RadiationDoseUnits + | RadioactiveDecayUnits + | RadioactivityUnits + | RadioactivityConcentrationUnits + | ReciprocalLengthUnits + | ResistanceUnits + | ReynoldsNumberUnits + | SignalLevelUnits + | SolidAngleUnits + | SpecificEnergyUnits + | SpecificHeatCapacityUnits | SpecificHumidityUnits + | SpecificVolumeUnits | SpeedUnits + | SurfaceChargeDensityUnits + | SurfaceTensionUnits | TemperatureUnits + | ThermalConductivityUnits | TimeUnits | TorqueUnits + | TurbidityUnits | VoltageUnits | VolumeUnits - | VolumeFlowRateUnits; + | VolumeFlowUnits; export type AllMeasures = + | 'absorbed-dose-rate' | 'acceleration' + | 'acidity' | 'air-quality-index' | 'amount-of-substance' | 'angle' @@ -158,14 +214,17 @@ export type AllMeasures = | 'dimension-ratio' | 'dynamic-viscosity' | 'earthquake-magnitude' + | 'electric-charge-density' | 'electric-current' | 'electric-dipole-moment' | 'electric-field-strength' | 'electric-flux' | 'electric-permittivity' + | 'electric-polarizability' | 'electrical-conductance' | 'electrical-conductivity' | 'energy' + | 'energy-density' | 'force' | 'frequency' | 'fuel-efficiency' @@ -191,28 +250,51 @@ export type AllMeasures = | 'molar-energy' | 'molar-heat-capacity' | 'molar-mass' - | 'parts-per' + | 'number-concentration' + | 'parts-per-million' | 'power' + | 'power-density' | 'pressure' + | 'radiance' + | 'radiant-intensity' + | 'radiation-dose' + | 'radioactive-decay' + | 'radioactivity' + | 'radioactivity-concentration' + | 'reciprocal-length' + | 'resistance' + | 'reynolds-number' + | 'signal-level' + | 'solid-angle' + | 'specific-energy' + | 'specific-heat-capacity' | 'specific-humidity' + | 'specific-volume' + | 'surface-charge-density' + | 'surface-tension' | 'speed' | 'temperature' + | 'thermal-conductivity' | 'time' | 'torque' + | 'turbidity' | 'voltage' | 'volume' - | 'volume-flow-rate'; + | 'volume-flow'; const allMeasures: Record< AllMeasures, TbMeasure > = Object.freeze({ + 'absorbed-dose-rate': absorbedDoseRate, acceleration, + acidity, 'air-quality-index': airQualityIndex, 'amount-of-substance': amountOfSubstance, angle, 'angular-acceleration': angularAcceleration, area, + 'area-density': areaDensity, capacitance, 'catalytic-activity': catalyticActivity, 'catalytic-concentration': catalyticConcentration, @@ -224,14 +306,17 @@ const allMeasures: Record< 'dimension-ratio': dimensionRatio, 'dynamic-viscosity': dynamicViscosity, 'earthquake-magnitude': earthquakeMagnitude, + 'electric-charge-density': electricChargeDensity, 'electric-current': electricCurrent, 'electric-dipole-moment': electricDipoleMoment, 'electric-field-strength': electricFieldStrength, 'electric-flux': electricFlux, 'electric-permittivity': electricPermittivity, + 'electric-polarizability': electricPolarizability, 'electrical-conductance': electricalConductance, 'electrical-conductivity': electricalConductivity, energy, + 'energy-density': energyDensity, force, frequency, 'fuel-efficiency': fuelEfficiency, @@ -246,6 +331,7 @@ const allMeasures: Record< 'luminous-efficacy': luminousEfficacy, 'luminous-flux': luminousFlux, 'luminous-intensity': luminousIntensity, + 'number-concentration': numberConcentration, 'magnetic-field-gradient': magneticFieldGradient, 'magnetic-flux': magneticFlux, 'magnetic-flux-density': magneticFluxDensity, @@ -257,17 +343,36 @@ const allMeasures: Record< 'molar-energy': molarEnergy, 'molar-heat-capacity': molarHeatCapacity, 'molar-mass': molarMass, - 'parts-per': partsPer, + 'parts-per-million': partsPerMillion, power, + 'power-density': powerDensity, pressure, + radiance, + 'radiant-intensity': radiantIntensity, + 'radiation-dose': radiationDose, + 'radioactive-decay': radioactiveDecay, + radioactivity, + 'radioactivity-concentration': radioactivityConcentration, + 'reciprocal-length': reciprocalLength, + resistance, + 'reynolds-number': reynoldsNumber, + 'signal-level': signalLevel, + 'solid-angle': solidAngle, + 'specific-energy': specificEnergy, + 'specific-heat-capacity': specificHeatCapacity, 'specific-humidity': specificHumidity, + 'specific-volume': specificVolume, speed, + 'surface-charge-density': surfaceChargeDensity, + 'surface-tension': surfaceTension, temperature, + 'thermal-conductivity': thermalConductivity, time, torque, + turbidity, voltage, volume, - 'volume-flow-rate': volumeFlowRate, + 'volume-flow': volumeFlow, }); export enum UnitsType { diff --git a/ui-ngx/src/app/shared/models/units/absorbed-dose-rate.ts b/ui-ngx/src/app/shared/models/units/absorbed-dose-rate.ts new file mode 100644 index 0000000000..46f52272a1 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/absorbed-dose-rate.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AbsorbedDoseRateUnits = 'Gy/s'; + +const METRIC: TbMeasureUnits = { + units: { + 'Gy/s': { + name: 'unit.gy-per-second', + tags: ['radiation dose rate'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/acceleration.ts b/ui-ngx/src/app/shared/models/units/acceleration.ts index a1643020a9..cef91df465 100644 --- a/ui-ngx/src/app/shared/models/units/acceleration.ts +++ b/ui-ngx/src/app/shared/models/units/acceleration.ts @@ -24,24 +24,24 @@ export type AccelerationUnits = AccelerationMetricUnits | AccelerationImperialUn const METRIC: TbMeasureUnits = { ratio: 3.28084, units: { - 'G': { + G: { name: 'unit.g-force', - tags: ['gravity', 'g-force', 'load'], + tags: ['gravity', 'load'], to_anchor: 9.80665, }, 'm/s²': { name: 'unit.meters-per-second-squared', - tags: ['peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'meters per second squared'], + tags: ['peak to peak', 'root mean square (RMS)', 'vibration'], to_anchor: 1, }, - 'Gal': { + Gal: { name: 'unit.gal', tags: ['gravity', 'g-force'], to_anchor: 1, }, 'km/h²': { name: 'unit.kilometer-per-hour-squared', - tags: ['rate of change of velocity', 'kilometer per hour squared'], + tags: ['rate of change of velocity'], to_anchor: 1 / 12960, } } @@ -52,7 +52,7 @@ const IMPERIAL: TbMeasureUnits = { units: { 'ft/s²': { name: 'unit.foot-per-second-squared', - tags: ['acceleration', 'rate of change of velocity', 'foot per second squared', 'ft/s²'], + tags: ['rate of change of velocity'], to_anchor: 1 } } diff --git a/ui-ngx/src/app/shared/models/units/acidity.ts b/ui-ngx/src/app/shared/models/units/acidity.ts new file mode 100644 index 0000000000..9681084a99 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/acidity.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AcidityUnits = 'pH'; + +const METRIC: TbMeasureUnits = { + units: { + pH: { + name: 'unit.ph-level', + tags: [ 'alkalinity', 'neutral', 'acid', 'base', 'soil pH', 'water quality', 'water pH'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts index 1601f5d80a..6dc38ccad6 100644 --- a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts +++ b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts @@ -20,27 +20,27 @@ export type AmountOfSubstanceUnits = 'mol' | 'nmol' | 'μmol' | 'mmol' | 'kmol'; const METRIC: TbMeasureUnits = { units: { - 'mol': { + mol: { name: 'unit.mole', tags: ['chemical amount'], to_anchor: 1, }, - 'nmol': { + nmol: { name: 'unit.nanomole', tags: ['chemical amount'], to_anchor: 0.000000001, }, - 'μmol': { + μmol: { name: 'unit.micromole', tags: ['chemical amount'], to_anchor: 0.000001, }, - 'mmol': { + mmol: { name: 'unit.millimole', tags: ['chemical amount'], to_anchor: 0.001, }, - 'kmol': { + kmol: { name: 'unit.kilomole', tags: ['chemical amount'], to_anchor: 1000, diff --git a/ui-ngx/src/app/shared/models/units/area-density.ts b/ui-ngx/src/app/shared/models/units/area-density.ts new file mode 100644 index 0000000000..3fd0757a45 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/area-density.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AreaDensityUnits = 'kg/m²'; + +const METRIC: TbMeasureUnits = { + units: { + 'kg/m²': { + name: 'unit.kilogram-per-square-meter', + tags: ['surface density', 'mass per unit area'], + to_anchor: 1 + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/capacitance.ts b/ui-ngx/src/app/shared/models/units/capacitance.ts index f4dc13dc97..8f65fd97b2 100644 --- a/ui-ngx/src/app/shared/models/units/capacitance.ts +++ b/ui-ngx/src/app/shared/models/units/capacitance.ts @@ -20,48 +20,48 @@ export type CapacitanceUnits = 'F' | 'mF' | 'μF' | 'nF' | 'pF' | 'kF' | 'MF' | const METRIC: TbMeasureUnits = { units: { - 'F': { + F: { name: 'unit.farad', tags: ['electric capacitance'], to_anchor: 1, }, - 'mF': { + mF: { name: 'unit.millifarad', tags: ['electric capacitance'], to_anchor: 1e-3, }, - 'μF': { + μF: { name: 'unit.microfarad', tags: ['electric capacitance'], to_anchor: 1e-6, }, - 'nF': { + nF: { name: 'unit.nanofarad', tags: ['electric capacitance'], to_anchor: 1e-9, }, - 'pF': { + pF: { name: 'unit.picofarad', tags: ['electric capacitance'], to_anchor: 1e-12, }, - 'kF': { + kF: { name: 'unit.kilofarad', tags: ['electric capacitance'], to_anchor: 1e3, }, - 'MF': { + MF: { name: 'unit.megafarad', tags: ['electric capacitance'], to_anchor: 1e6, }, - 'GF': { + GF: { name: 'unit.gigafarad', tags: ['electric capacitance'], to_anchor: 1e9, }, - 'TF': { - name: 'unit.terafarad', + TF: { + name: 'unit.terfarad', tags: ['electric capacitance'], to_anchor: 1e12, }, diff --git a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts index d952eaf727..e77576dce2 100644 --- a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts +++ b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts @@ -20,23 +20,23 @@ export type DataTransferRateUnits = 'bps' | 'kbps' | 'Mbps' | 'Gbps' | 'Tbps' | const METRIC: TbMeasureUnits = { units: { - 'bps': { + bps: { name: 'unit.bit-per-second', to_anchor: 1, }, - 'kbps': { + kbps: { name: 'unit.kilobit-per-second', to_anchor: 1e3, }, - 'Mbps': { + Mbps: { name: 'unit.megabit-per-second', to_anchor: 1e6, }, - 'Gbps': { + Gbps: { name: 'unit.gigabit-per-second', to_anchor: 1e9, }, - 'Tbps': { + Tbps: { name: 'unit.terabit-per-second', to_anchor: 1e12, }, diff --git a/ui-ngx/src/app/shared/models/units/dimension-ratio.ts b/ui-ngx/src/app/shared/models/units/dimension-ratio.ts index 3deb60a38f..1df77e13ad 100644 --- a/ui-ngx/src/app/shared/models/units/dimension-ratio.ts +++ b/ui-ngx/src/app/shared/models/units/dimension-ratio.ts @@ -16,10 +16,15 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type DimensionRatioUnits = 'm/m'; +export type DimensionRatioUnits = 'm/m' | '%'; const METRIC: TbMeasureUnits = { units: { + '%': { + name: 'unit.percent', + tags: ['power source', 'state of charge (SoC)', 'battery', 'battery level', 'level', 'humidity', 'moisture', 'relative humidity', 'water content', 'soil moisture', 'irrigation', 'water in soil', 'soil water content', 'VWC', 'Volumetric Water Content', 'Total Harmonic Distortion', 'THD', 'power quality', 'UV Transmittance', 'capacity'], + to_anchor: 1, + }, 'm/m': { name: 'unit.meter-per-meter', tags: ['ratio of length to length'], diff --git a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts index e3fe8602b3..bb8a479931 100644 --- a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts +++ b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts @@ -29,12 +29,12 @@ const METRIC: TbMeasureUnits = { tags: ['fluid mechanics'], to_anchor: 1, }, - 'cP': { + cP: { name: 'unit.centipoise', tags: ['fluid mechanics'], to_anchor: 0.001, }, - 'P': { + P: { name: 'unit.poise', tags: ['fluid mechanics'], to_anchor: 0.1, diff --git a/ui-ngx/src/app/shared/models/units/electric-charge-density.ts b/ui-ngx/src/app/shared/models/units/electric-charge-density.ts new file mode 100644 index 0000000000..0e2e8dfe08 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-charge-density.ts @@ -0,0 +1,34 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricChargeDensityUnits = 'C/m³'; + +const METRIC: TbMeasureUnits = { + units: { + 'C/m³': { + name: 'unit.coulomb-per-cubic-meter', + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts b/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts index b43e08b857..8a7ecc7379 100644 --- a/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts +++ b/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts @@ -24,7 +24,7 @@ const METRIC: TbMeasureUnits = { name: 'unit.electric-dipole-moment', to_anchor: 1, }, - 'D': { + D: { name: 'unit.debye', tags: ['polarization'], to_anchor: 3.33564e-30 diff --git a/ui-ngx/src/app/shared/models/units/electric-polarizability.ts b/ui-ngx/src/app/shared/models/units/electric-polarizability.ts new file mode 100644 index 0000000000..2d92ad4cb1 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-polarizability.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricPolarizabilityUnits = 'C·m²/V'; + +const METRIC: TbMeasureUnits = { + units: { + 'C·m²/V': { + name: 'unit.coulomb-per-square-meter-per-volt', + tags: ['electric field'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts index 62241d059f..bbad5d3f5c 100644 --- a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts +++ b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts @@ -20,27 +20,27 @@ export type ElectricalConductanceUnits = 'S' | 'mS' | 'μS' | 'kS' | 'MS' | 'GS' const METRIC: TbMeasureUnits = { units: { - 'S': { + S: { name: 'unit.siemens', to_anchor: 1, }, - 'mS': { + mS: { name: 'unit.millisiemens', to_anchor: 1e-3, }, - 'μS': { + μS: { name: 'unit.microsiemens', to_anchor: 1e-6, }, - 'kS': { + kS: { name: 'unit.kilosiemens', to_anchor: 1e3, }, - 'MS': { + MS: { name: 'unit.megasiemens', to_anchor: 1e6, }, - 'GS': { + GS: { name: 'unit.gigasiemens', to_anchor: 1e9, }, diff --git a/ui-ngx/src/app/shared/models/units/energy-density.ts b/ui-ngx/src/app/shared/models/units/energy-density.ts new file mode 100644 index 0000000000..9b0e2e4a1e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/energy-density.ts @@ -0,0 +1,34 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type EnergyDensityUnits = 'J/m³'; + +const METRIC: TbMeasureUnits = { + units: { + 'J/m³': { + name: 'unit.joule-per-cubic-meter', + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts index 64eb49cfb2..360b35ed6e 100644 --- a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts +++ b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts @@ -39,7 +39,7 @@ const METRIC: TbMeasureUnits = { const IMPERIAL: TbMeasureUnits = { ratio: 0.425144, units: { - 'mpg': { + mpg: { name: 'unit.miles-per-gallon', to_anchor: 0.425144, }, diff --git a/ui-ngx/src/app/shared/models/units/illuminance.ts b/ui-ngx/src/app/shared/models/units/illuminance.ts index 286cd4923d..4063b61fc5 100644 --- a/ui-ngx/src/app/shared/models/units/illuminance.ts +++ b/ui-ngx/src/app/shared/models/units/illuminance.ts @@ -45,7 +45,7 @@ const METRIC: TbMeasureUnits = { const IMPERIAL: TbMeasureUnits = { ratio: 10.76391, units: { - 'fc': { + fc: { name: 'unit.foot-candle', tags: ['illuminance', 'light level'], to_anchor: 1, diff --git a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts index 8d2201d456..c1203ff75b 100644 --- a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts +++ b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts @@ -32,11 +32,11 @@ const METRIC: TbMeasureUnits = { name: 'unit.square-centimeter-per-second', to_anchor: 1e-4, }, - 'St': { + St: { name: 'unit.stoke', to_anchor: 1e-4, }, - 'cSt': { + cSt: { name: 'unit.centistokes', to_anchor: 1e-6, }, diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts index cb34632711..83a26c3030 100644 --- a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts +++ b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts @@ -20,17 +20,17 @@ export type LogarithmicRatioUnits = 'dB' | 'B' | 'Np'; const METRIC: TbMeasureUnits = { units: { - 'dB': { + dB: { name: 'unit.decibel', tags: ['noise level', 'sound level', 'volume', 'acoustics'], to_anchor: 1, }, - 'B': { + B: { name: 'unit.bel', tags: ['power ratio', 'intensity ratio'], to_anchor: 10, }, - 'Np': { + Np: { name: 'unit.neper', tags: ['gain', 'loss', 'attenuation'], to_anchor: 8.685889638, diff --git a/ui-ngx/src/app/shared/models/units/luminous-flux.ts b/ui-ngx/src/app/shared/models/units/luminous-flux.ts index 0d91e372e8..db9c3bc2b6 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-flux.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-flux.ts @@ -20,7 +20,7 @@ export type LuminousFluxUnits = 'lm'; const METRIC: TbMeasureUnits = { units: { - 'lm': { + lm: { name: 'unit.lumen', tags: ['total light output'], to_anchor: 1, diff --git a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts index da85631822..259e64de6f 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts @@ -20,7 +20,7 @@ export type LuminousIntensityUnits = 'cd'; const METRIC: TbMeasureUnits = { units: { - 'cd': { + cd: { name: 'unit.candela', tags: ['light intensity'], to_anchor: 1, diff --git a/ui-ngx/src/app/shared/models/units/number-concentration.ts b/ui-ngx/src/app/shared/models/units/number-concentration.ts new file mode 100644 index 0000000000..b5ad4692bc --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/number-concentration.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type NumberConcentrationUnits = 'particles/mL'; + +const METRIC: TbMeasureUnits = { + units: { + 'particles/mL': { + name: 'unit.particle-density', + tags: ['particle concentration', 'count'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/particle-concentration.ts b/ui-ngx/src/app/shared/models/units/particle-concentration.ts deleted file mode 100644 index 991d5a9f78..0000000000 --- a/ui-ngx/src/app/shared/models/units/particle-concentration.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type ParticleConcentrationUnits = 'particles/mL'; - -const METRIC: TbMeasureUnits = { - units: { - 'particles/mL': { - name: 'unit.particle-density', - tags: ['particle concentration', 'count', 'particles/mL'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/parts-per.ts b/ui-ngx/src/app/shared/models/units/parts-per-million.ts similarity index 80% rename from ui-ngx/src/app/shared/models/units/parts-per.ts rename to ui-ngx/src/app/shared/models/units/parts-per-million.ts index 83f72447df..ca3de46a52 100644 --- a/ui-ngx/src/app/shared/models/units/parts-per.ts +++ b/ui-ngx/src/app/shared/models/units/parts-per-million.ts @@ -16,25 +16,24 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type PartsPerUnits = PartsPerMetricUnits; -export type PartsPerMetricUnits = 'ppm' | 'ppb'; +export type PartsPerMillionUnits = 'ppm' | 'ppb'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { ppm: { name: 'unit.ppm', - tags: ['carbon dioxide', 'co²', 'carbon monoxide', 'co', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'ppm'], + tags: ['carbon dioxide', 'co²', 'carbon monoxide', 'co', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc'], to_anchor: 1, }, ppb: { name: 'unit.ppb', - tags: ['ozone', 'o³', 'nitrogen dioxide', 'no²', 'sulfur dioxide', 'so²', 'aqi', 'air quality', 'tvoc', 'ppb'], + tags: ['ozone', 'o³', 'nitrogen dioxide', 'no²', 'sulfur dioxide', 'so²', 'aqi', 'air quality', 'tvoc'], to_anchor: 0.001, } }, }; -const measure: TbMeasure = { +const measure: TbMeasure = { METRIC, }; diff --git a/ui-ngx/src/app/shared/models/units/percentage.ts b/ui-ngx/src/app/shared/models/units/percentage.ts deleted file mode 100644 index 1bd796b0f5..0000000000 --- a/ui-ngx/src/app/shared/models/units/percentage.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type PercentageMetricUnits = '%'; -export type PercentageUnits = PercentageMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - '%': { - name: 'unit.percent', - tags: ['power source', 'state of charge (SoC)', 'battery', 'battery level', 'level', 'humidity', 'moisture', 'percentage', 'relative humidity', 'water content', 'soil moisture', 'irrigation', 'water in soil', 'soil water content', 'VWC', 'Volumetric Water Content', 'Total Harmonic Distortion', 'THD', 'power quality', 'UV Transmittance', '%', 'capacity'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/ph.ts b/ui-ngx/src/app/shared/models/units/ph.ts deleted file mode 100644 index d394d0f8ec..0000000000 --- a/ui-ngx/src/app/shared/models/units/ph.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type PHUnits = 'pH'; - -const METRIC: TbMeasureUnits = { - units: { - pH: { - name: 'unit.ph-level', - tags: ['acidity', 'alkalinity', 'neutral', 'acid', 'base', 'pH', 'soil pH', 'water quality', 'water pH'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/polarization.ts b/ui-ngx/src/app/shared/models/units/polarization.ts deleted file mode 100644 index 09528228a0..0000000000 --- a/ui-ngx/src/app/shared/models/units/polarization.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type PolarizationMetricUnits = 'C·m²/V'; - -export type PolarizationUnits = PolarizationMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'C·m²/V': { - name: 'unit.coulomb-per-square-meter-per-volt', - tags: ['polarization', 'electric field', 'coulomb per square meter per volt', 'C·m²/V'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/power-density.ts b/ui-ngx/src/app/shared/models/units/power-density.ts index 38ec33fd6e..ed3ac37a6c 100644 --- a/ui-ngx/src/app/shared/models/units/power-density.ts +++ b/ui-ngx/src/app/shared/models/units/power-density.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type PowerDensityMetricUnits = 'mW/cm²' | 'W/cm²' | 'kW/cm²' | 'mW/m²' | 'W/m²' | 'kW/m²'; @@ -10,32 +26,32 @@ const METRIC: TbMeasureUnits = { units: { 'mW/cm²': { name: 'unit.milliwatt-per-square-centimeter', - tags: ['power density', 'radiation intensity', 'sunlight intensity', 'signal power', 'intensity', 'milliwatts per square centimeter', 'UV Intensity', 'mW/cm²'], + tags: ['radiation intensity', 'sunlight intensity', 'signal power', 'intensity', 'UV Intensity'], to_anchor: 10000, }, 'W/cm²': { name: 'unit.watt-per-square-centimeter', - tags: ['power density', 'intensity of power', 'watts per square centimeter', 'W/cm²'], + tags: ['intensity of power'], to_anchor: 10000, }, 'kW/cm²': { name: 'unit.kilowatt-per-square-centimeter', - tags: ['power density', 'intensity of power', 'kilowatts per square centimeter', 'kW/cm²'], + tags: ['intensity of power'], to_anchor: 10000000, }, 'mW/m²': { name: 'unit.milliwatt-per-square-meter', - tags: ['power density', 'intensity of power', 'milliwatts per square meter', 'mW/m²'], + tags: ['intensity of power'], to_anchor: 0.001, }, 'W/m²': { name: 'unit.watt-per-square-meter', - tags: ['power density', 'intensity of power', 'watts per square meter', 'W/m²'], + tags: ['intensity of power'], to_anchor: 1, }, 'kW/m²': { name: 'unit.kilowatt-per-square-meter', - tags: ['power density', 'intensity of power', 'kilowatts per square meter', 'kW/m²'], + tags: ['intensity of power'], to_anchor: 1000, }, }, @@ -46,12 +62,12 @@ const IMPERIAL: TbMeasureUnits = { units: { 'W/in²': { name: 'unit.watt-per-square-inch', - tags: ['power density', 'intensity of power', 'watts per square inch', 'W/in²'], + tags: ['intensity of power'], to_anchor: 1, }, 'kW/in²': { name: 'unit.kilowatt-per-square-inch', - tags: ['power density', 'intensity of power', 'kilowatts per square inch', 'kW/in²'], + tags: ['intensity of power'], to_anchor: 1000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/power.ts b/ui-ngx/src/app/shared/models/units/power.ts index 8b9c5eda81..937ddde675 100644 --- a/ui-ngx/src/app/shared/models/units/power.ts +++ b/ui-ngx/src/app/shared/models/units/power.ts @@ -26,37 +26,37 @@ const METRIC: TbMeasureUnits = { units: { W: { name: 'unit.watt', - tags: ['power', 'horsepower', 'performance', 'watt', 'watts', 'electricity', 'W'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 1, }, μW: { name: 'unit.microwatt', - tags: ['power', 'horsepower', 'performance', 'microwatt', 'microwatts', 'electricity', 'μW'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 0.000001, }, mW: { name: 'unit.milliwatt', - tags: ['power', 'horsepower', 'performance', 'milliwatt', 'milliwatts', 'electricity', 'mW'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 0.001, }, kW: { name: 'unit.kilowatt', - tags: ['power', 'horsepower', 'performance', 'kilowatt', 'kilowatts', 'electricity', 'kW'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 1000, }, MW: { name: 'unit.megawatt', - tags: ['power', 'horsepower', 'performance', 'megawatt', 'megawatts', 'electricity', 'MW'], + tags: [ 'horsepower', 'performance', 'electricity'], to_anchor: 1000000, }, GW: { name: 'unit.gigawatt', - tags: ['power', 'horsepower', 'performance', 'gigawatt', 'gigawatts', 'electricity', 'GW'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 1000000000, }, PS: { name: 'unit.metric-horsepower', - tags: ['power', 'performance', 'metric horsepower', 'PS'], + tags: ['performance'], to_anchor: 735.49875, }, }, @@ -67,22 +67,22 @@ const IMPERIAL: TbMeasureUnits = { units: { 'BTU/s': { name: 'unit.btu-per-second', - tags: ['power', 'heat transfer', 'thermal energy', 'british thermal unit per second', 'Btu/s'], + tags: ['heat transfer', 'thermal energy'], to_anchor: 778.16937, }, 'ft-lb/s': { name: 'unit.foot-pound-per-second', - tags: ['power', 'foot-pound per second', 'foot-pounds per second', 'ft-lb/s', 'mechanical power'], + tags: ['mechanical power'], to_anchor: 1, }, hp: { name: 'unit.horsepower', - tags: ['power', 'horsepower', 'performance', 'electricity', 'horsepowers', 'hp'], + tags: ['performance', 'electricity'], to_anchor: 550, }, 'BTU/h': { name: 'unit.btu-per-hour', - tags: ['power', 'heat transfer', 'thermal energy', 'HVAC', 'BTU/h'], + tags: ['heat transfer', 'thermal energy', 'HVAC'], to_anchor: 0.216158, }, }, diff --git a/ui-ngx/src/app/shared/models/units/pressure.ts b/ui-ngx/src/app/shared/models/units/pressure.ts index 7443763754..e57bcd6e78 100644 --- a/ui-ngx/src/app/shared/models/units/pressure.ts +++ b/ui-ngx/src/app/shared/models/units/pressure.ts @@ -36,9 +36,7 @@ export type PressureMetricUnits = | 'N/m²' | 'kN/m²' | 'kgf/m²' - | 'Pa/cm²' - | 'J/m³' - | 'kg/m²'; + | 'Pa/cm²'; export type PressureImperialUnits = 'psi' | 'ksi' | 'inHg' | 'psi/in²' | 'tonf/in²'; @@ -47,104 +45,94 @@ const METRIC: TbMeasureUnits = { units: { Pa: { name: 'unit.pascal', - tags: ['pressure', 'force', 'compression', 'tension', 'pascal', 'pascals', 'Pa', 'atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], + tags: ['force', 'compression', 'tension', 'atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], to_anchor: 0.001, }, kPa: { name: 'unit.kilopascal', - tags: ['pressure', 'force', 'compression', 'tension', 'kilopascal', 'kilopascals', 'kPa'], + tags: ['force', 'compression', 'tension'], to_anchor: 1, }, MPa: { name: 'unit.megapascal', - tags: ['pressure', 'force', 'compression', 'tension', 'megapascal', 'megapascals', 'MPa'], + tags: ['force', 'compression', 'tension'], to_anchor: 1000, }, GPa: { name: 'unit.gigapascal', - tags: ['pressure', 'force', 'compression', 'tension', 'gigapascal', 'gigapascals', 'GPa'], + tags: ['force', 'compression', 'tension'], to_anchor: 1000000, }, hPa: { name: 'unit.hectopascal', - tags: ['pressure', 'force', 'compression', 'tension', 'hectopascal', 'hectopascals', 'hPa', 'atmospheric pressure'], + tags: ['force', 'compression', 'tension', 'atmospheric pressure'], to_anchor: 0.1, }, mbar: { name: 'unit.millibar', - tags: ['pressure', 'force', 'compression', 'tension', 'millibar', 'millibars', 'mbar'], + tags: ['force', 'compression', 'tension'], to_anchor: 0.1, }, mb: { name: 'unit.millibar', - tags: ['atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight', 'mb'], + tags: ['atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], to_anchor: 0.1, }, bar: { name: 'unit.bar', - tags: ['pressure', 'force', 'compression', 'tension', 'bar', 'bars'], + tags: ['force', 'compression', 'tension'], to_anchor: 100, }, kbar: { name: 'unit.kilobar', - tags: ['pressure', 'force', 'compression', 'tension', 'kilobar', 'kilobars', 'kbar'], + tags: ['force', 'compression', 'tension'], to_anchor: 100000, }, Torr: { name: 'unit.torr', - tags: ['pressure', 'force', 'compression', 'tension', 'vacuum pressure', 'torr'], + tags: ['force', 'compression', 'tension', 'vacuum pressure'], to_anchor: 101325 / 760000, }, mmHg: { name: 'unit.millimeters-of-mercury', - tags: ['pressure', 'force', 'compression', 'tension', 'millimeter of mercury', 'millimeters of mercury', 'mmHg', 'vacuum pressure'], + tags: ['force', 'compression', 'tension', 'vacuum pressure'], to_anchor: 0.133322, }, atm: { name: 'unit.atmospheres', - tags: ['pressure', 'force', 'compression', 'tension', 'atmosphere', 'atmospheres', 'atmospheric pressure', 'atm'], + tags: ['force', 'compression', 'tension', 'atmospheric pressure'], to_anchor: 101.325, }, 'Pa/m²': { name: 'unit.pascal-per-square-meter', - tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square meter', 'Pa/m²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.001, }, 'N/mm²': { name: 'unit.newton-per-square-millimeter', - tags: ['pressure', 'stress', 'mechanical strength', 'newton per square millimeter', 'N/mm²'], + tags: ['stress', 'mechanical strength'], to_anchor: 1000, }, 'N/m²': { name: 'unit.newton-per-square-meter', - tags: ['pressure', 'stress', 'mechanical strength', 'newton per square meter', 'N/m²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.001, }, 'kN/m²': { name: 'unit.kilonewton-per-square-meter', - tags: ['pressure', 'stress', 'mechanical strength', 'kilonewton per square meter', 'kN/m²'], + tags: ['stress', 'mechanical strength'], to_anchor: 1, }, 'kgf/m²': { name: 'unit.kilogram-force-per-square-meter', - tags: ['pressure', 'stress', 'mechanical strength', 'kilogram-force per square meter', 'kgf/m²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.00980665, }, 'Pa/cm²': { name: 'unit.pascal-per-square-centimeter', - tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square centimeter', 'Pa/cm²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.1, }, - 'J/m³': { - name: 'unit.joule-per-cubic-meter', - tags: ['energy density', 'joule per cubic meter', 'J/m³'], - to_anchor: 0.001, - }, - 'kg/m²': { - name: 'unit.kilogram-per-square-meter', - tags: ['density','surface density','areal density','mass per unit area','kg/m²'], - to_anchor: 0.00980665 - } }, }; @@ -153,27 +141,27 @@ const IMPERIAL: TbMeasureUnits = { units: { psi: { name: 'unit.pounds-per-square-inch', - tags: ['pressure', 'force', 'compression', 'tension', 'pounds per square inch', 'psi'], + tags: ['force', 'compression', 'tension'], to_anchor: 0.001, }, ksi: { name: 'unit.kilopound-per-square-inch', - tags: ['pressure', 'force', 'compression', 'tension', 'kilopound per square inch', 'kilopounds per square inch', 'ksi'], + tags: ['force', 'compression', 'tension'], to_anchor: 1, }, inHg: { name: 'unit.inch-of-mercury', - tags: ['pressure', 'force', 'compression', 'tension', 'vacuum pressure', 'inHg', 'atmospheric pressure', 'barometric pressure'], + tags: ['force', 'compression', 'tension', 'vacuum pressure','atmospheric pressure', 'barometric pressure'], to_anchor: 0.000491154, }, 'psi/in²': { name: 'unit.pound-per-square-inch', - tags: ['pressure', 'stress', 'mechanical strength', 'pound per square inch', 'psi/in²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.001, }, 'tonf/in²': { name: 'unit.ton-force-per-square-inch', - tags: ['pressure', 'stress', 'mechanical strength', 'ton-force per square inch', 'tonf/in²'], + tags: ['stress', 'mechanical strength'], to_anchor: 2, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radiance.ts b/ui-ngx/src/app/shared/models/units/radiance.ts index 38fb646fab..878cc86064 100644 --- a/ui-ngx/src/app/shared/models/units/radiance.ts +++ b/ui-ngx/src/app/shared/models/units/radiance.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type RadianceUnits = 'W/(m²·sr)'; @@ -6,7 +22,7 @@ const METRIC: TbMeasureUnits = { units: { 'W/(m²·sr)': { name: 'unit.watt-per-square-metre-steradian', - tags: ['radiance', 'radiant flux density', 'wTape per square metre-steradian', 'W/(m²·sr)'], + tags: ['radiant flux density'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radiant-intensity.ts b/ui-ngx/src/app/shared/models/units/radiant-intensity.ts index 78ee45ee5d..3b4958b892 100644 --- a/ui-ngx/src/app/shared/models/units/radiant-intensity.ts +++ b/ui-ngx/src/app/shared/models/units/radiant-intensity.ts @@ -1,13 +1,28 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type RadiantIntensityUnits = RadiantIntensityMetricUnits; -export type RadiantIntensityMetricUnits = 'W/sr'; +export type RadiantIntensityUnits = 'W/sr'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'W/sr': { name: 'unit.watt-per-steradian', - tags: ['radiant intensity', 'power per unit solid angle', 'watt per steradian', 'W/sr'], + tags: ['power per unit solid angle'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radiation-dose.ts b/ui-ngx/src/app/shared/models/units/radiation-dose.ts index 1619a48780..c7adc05f57 100644 --- a/ui-ngx/src/app/shared/models/units/radiation-dose.ts +++ b/ui-ngx/src/app/shared/models/units/radiation-dose.ts @@ -1,43 +1,58 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type RadiationDoseMetricUnits = 'Gy' | 'Sv' | 'Rad' | 'Rem' | 'R' | 'C/kg' | 'Gy/s'; -export type RadiationDoseUnits = RadiationDoseMetricUnits; +export type RadiationDoseUnits = 'Gy' | 'Sv' | 'Rad' | 'Rem' | 'R' | 'C/kg' | 'cps'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { - 'Sv': { + Sv: { name: 'unit.sievert', - tags: ['radiation dose', 'sievert', 'radiation dose equivalent', 'Sv'], + tags: ['sievert', 'radiation dose equivalent', 'Sv'], to_anchor: 1, }, - 'Gy': { + Gy: { name: 'unit.gray', - tags: ['radiation dose', 'absorbed dose', 'gray', 'Gy'], + tags: ['absorbed dose', 'gray', 'Gy'], to_anchor: 1, }, - 'Rad': { + Rad: { name: 'unit.rad', - tags: ['radiation dose', 'rad'], + tags: ['rad'], to_anchor: 0.01, }, - 'Rem': { + Rem: { name: 'unit.rem', - tags: ['radiation dose equivalent', 'rem'], + tags: ['radiation dose equivalent'], to_anchor: 0.01, }, - 'R': { + R: { name: 'unit.roentgen', - tags: ['radiation exposure', 'roentgen', 'R'], + tags: ['radiation exposure'], to_anchor: 0.0093, }, 'C/kg': { name: 'unit.coulombs-per-kilogram', - tags: ['radiation exposure', 'dose', 'coulombs per kilogram', 'electric charge-to-mass ratio', 'C/kg'], + tags: ['radiation exposure', 'electric charge-to-mass ratio'], to_anchor: 34, }, - 'Gy/s': { - name: 'unit.gy-per-second', - tags: ['absorbed dose rate', 'radiation dose rate', 'gray per second', 'Gy/s'], + cps: { + name: 'unit.cps', + tags: ['radiation detection'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts b/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts deleted file mode 100644 index d357c11b72..0000000000 --- a/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type RadioactiveDecayRateUnits = 'Bq/s' | 'Ci/s'; - -const METRIC: TbMeasureUnits = { - transform: (Bq_s) => Bq_s / 3.7e10, // Convert Bq/s to Ci/s - units: { - 'Bq/s': { - name: 'unit.becquerels-per-second', - tags: ['radioactive decay rate', 'becquerels per second', 'Bq/s'], - to_anchor: 1, - }, - 'Ci/s': { - name: 'unit.curies-per-second', - tags: ['radioactive decay rate', 'curies per second', 'Ci/s'], - to_anchor: 3.7e10, - }, - } -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactive-decay.ts b/ui-ngx/src/app/shared/models/units/radioactive-decay.ts new file mode 100644 index 0000000000..520b81dab6 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radioactive-decay.ts @@ -0,0 +1,38 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadioactiveDecayUnits = 'Bq/s' | 'Ci/s'; + +const METRIC: TbMeasureUnits = { + units: { + 'Bq/s': { + name: 'unit.becquerels-per-second', + to_anchor: 1, + }, + 'Ci/s': { + name: 'unit.curies-per-second', + to_anchor: 3.7e10, + }, + } +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts b/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts index 8036332f13..4d6c99bf1e 100644 --- a/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts +++ b/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts @@ -1,18 +1,33 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type RadioactivityConcentrationMetricUnits = 'Bq/m³' | 'Ci/L'; -export type RadioactivityConcentrationUnits = RadioactivityConcentrationMetricUnits; +export type RadioactivityConcentrationUnits = 'Bq/m³' | 'Ci/L'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'Bq/m³': { name: 'unit.becquerels-per-cubic-meter', - tags: ['radioactivity', 'radiation', 'becquerels per cubic meter', 'Bq/m³'], + tags: ['radiation'], to_anchor: 1, }, 'Ci/L': { name: 'unit.curies-per-liter', - tags: ['radioactivity', 'radiation', 'curies per liter', 'Ci/L'], + tags: ['radiation'], to_anchor: 3.7e10 * 1000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radioactivity.ts b/ui-ngx/src/app/shared/models/units/radioactivity.ts index 8db76da7af..ed456ca96a 100644 --- a/ui-ngx/src/app/shared/models/units/radioactivity.ts +++ b/ui-ngx/src/app/shared/models/units/radioactivity.ts @@ -1,33 +1,44 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type RadioactivityMetricUnits = 'Bq' | 'Ci' | 'Rd' | 'dps' | 'cps'; +export type RadioactivityMetricUnits = 'Bq' | 'Ci' | 'Rd' | 'dps'; export type RadioactivityUnits = RadioactivityMetricUnits; const METRIC: TbMeasureUnits = { units: { - 'Bq': { + Bq: { name: 'unit.becquerel', - tags: ['radioactivity', 'decay rate', 'becquerel', 'Bq'], + tags: ['radioactivity', 'decay rate'], to_anchor: 1, }, - 'Ci': { + Ci: { name: 'unit.curie', - tags: ['radioactivity', 'radiation', 'curie', 'Ci'], + tags: ['radiation'], to_anchor: 3.7e10, }, - 'Rd': { + Rd: { name: 'unit.rutherford', - tags: ['radioactive decay', 'radioactivity', 'rutherford', 'Rd'], - to_anchor: 1e6, + tags: ['radioactive decay'], + to_anchor: 3.7e4, }, - 'dps': { + dps: { name: 'unit.dps', - tags: ['radioactive decay', 'radioactivity', 'disintegrations per second', 'dps'], - to_anchor: 1, - }, - cps: { - name: 'unit.cps', - tags: ['radiation detection', 'counts per second', 'cps'], + tags: ['radioactive decay'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/reciprocal-length.ts b/ui-ngx/src/app/shared/models/units/reciprocal-length.ts new file mode 100644 index 0000000000..bfa26f57ec --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/reciprocal-length.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ReciprocalLengthUnits = 'm⁻¹'; + +const METRIC: TbMeasureUnits = { + units: { + 'm⁻¹': { + name: 'unit.reciprocal-metre', + tags: ['wavenumber', 'wave density', 'wave frequency'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/resistance.ts b/ui-ngx/src/app/shared/models/units/resistance.ts index 6f14faf4c4..0efef06a0a 100644 --- a/ui-ngx/src/app/shared/models/units/resistance.ts +++ b/ui-ngx/src/app/shared/models/units/resistance.ts @@ -1,39 +1,53 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type ResistanceMetricUnits = 'Ω' | 'μΩ' | 'mΩ' | 'kΩ' | 'MΩ' | 'GΩ'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ResistanceUnits = ResistanceMetricUnits; +export type ResistanceUnits = 'Ω' | 'μΩ' | 'mΩ' | 'kΩ' | 'MΩ' | 'GΩ'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { - 'Ω': { + Ω: { name: 'unit.ohm', - tags: ['electrical resistance', 'resistance', 'impedance', 'ohm'], + tags: ['electrical resistance', 'impedance'], to_anchor: 1, }, - 'μΩ': { + μΩ: { name: 'unit.microohm', - tags: ['electrical resistance', 'resistance', 'microohm', 'μΩ'], + tags: ['electrical resistance'], to_anchor: 0.000001, }, - 'mΩ': { + mΩ: { name: 'unit.milliohm', - tags: ['electrical resistance', 'resistance', 'milliohm', 'mΩ'], + tags: ['electrical resistance'], to_anchor: 0.001, }, - 'kΩ': { + kΩ: { name: 'unit.kilohm', - tags: ['electrical resistance', 'resistance', 'kilohm', 'kΩ'], + tags: ['electrical resistance'], to_anchor: 1000, }, - 'MΩ': { + MΩ: { name: 'unit.megohm', - tags: ['electrical resistance', 'resistance', 'megohm', 'MΩ'], + tags: ['electrical resistance'], to_anchor: 1000000, }, - 'GΩ': { + GΩ: { name: 'unit.gigohm', - tags: ['electrical resistance', 'resistance', 'gigohm', 'GΩ'], + tags: ['electrical resistance'], to_anchor: 1000000000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/reynolds-number.ts b/ui-ngx/src/app/shared/models/units/reynolds-number.ts index a9e8b8c01e..6f3b9a9df3 100644 --- a/ui-ngx/src/app/shared/models/units/reynolds-number.ts +++ b/ui-ngx/src/app/shared/models/units/reynolds-number.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type ReynoldsNumberMetricUnits = 'Re'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ReynoldsNumberUnits = ReynoldsNumberMetricUnits; +export type ReynoldsNumberUnits = 'Re'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { Re: { name: 'unit.reynolds', - tags: ['fluid flow regime', 'fluid mechanics', 'reynolds', 'Re'], + tags: ['fluid flow regime', 'fluid mechanics'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/signal-level.ts b/ui-ngx/src/app/shared/models/units/signal-level.ts new file mode 100644 index 0000000000..0ef0c5ce54 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/signal-level.ts @@ -0,0 +1,45 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SignalLevelUnits = 'dBmV' | 'dBm' | 'rssi'; + +const METRIC: TbMeasureUnits = { + units: { + dBmV: { + name: 'unit.dbmV', + tags: ['decibels millivolt', 'voltage level'], + to_anchor: 1, + }, + dBm: { + name: 'unit.dbm', + tags: ['decibel milliwatts', 'output power'], + to_anchor: 1, + }, + rssi: { + name: 'unit.rssi', + tags: ['signal strength', 'received signal strength indicator'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/signal-strength.ts b/ui-ngx/src/app/shared/models/units/signal-strength.ts deleted file mode 100644 index ef6a595ea3..0000000000 --- a/ui-ngx/src/app/shared/models/units/signal-strength.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type SignalStrengthMetricUnits = 'dBmV' | 'dBm' | 'rssi'; - -export type SignalStrengthUnits = SignalStrengthMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'dBmV': { - name: 'unit.dbmV', - tags: ['decibels millivolt', 'voltage level', 'signal', 'dBmV'], - to_anchor: 1, - }, - 'dBm': { - name: 'unit.dbm', - tags: ['decibel milliwatts', 'output power', 'signal', 'dBm'], - to_anchor: 1, - }, - 'rssi': { - name: 'unit.rssi', - tags: ['signal strength', 'signal level', 'received signal strength indicator', 'rssi', 'dBm'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/solid-angle.ts b/ui-ngx/src/app/shared/models/units/solid-angle.ts index c339884b65..c220c41098 100644 --- a/ui-ngx/src/app/shared/models/units/solid-angle.ts +++ b/ui-ngx/src/app/shared/models/units/solid-angle.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type SolidAngleMetricUnits = 'sr'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SolidAngleUnits = SolidAngleMetricUnits; +export type SolidAngleUnits = 'sr'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { - 'sr': { + sr: { name: 'unit.steradian', - tags: ['solid angle', 'spatial extent', 'steradian', 'sr'], + tags: ['spatial extent'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-energy.ts b/ui-ngx/src/app/shared/models/units/specific-energy.ts index 3ca9d44221..3787f1fb7a 100644 --- a/ui-ngx/src/app/shared/models/units/specific-energy.ts +++ b/ui-ngx/src/app/shared/models/units/specific-energy.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type SpecificEnergyMetricUnits = 'J/kg'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SpecificEnergyUnits = SpecificEnergyMetricUnits; +export type SpecificEnergyUnits = 'J/kg'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/kg': { name: 'unit.joule-per-kilogram', - tags: ['specific energy', 'specific energy capacity', 'joule per kilogram', 'J/kg'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts index 9bdf38e80c..595d1ca7ff 100644 --- a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts +++ b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts @@ -1,14 +1,12 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SpecificHeatCapacityMetricUnits = 'J/(kg·K)'; +export type SpecificHeatCapacityUnits = 'J/(kg·K)'; -export type SpecificHeatCapacityUnits = SpecificHeatCapacityMetricUnits; - -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/(kg·K)': { name: 'unit.joule-per-kilogram-kelvin', - tags: ['specific heat capacity', 'heat capacity per unit mass and temperature', 'joule per kilogram-kelvin', 'J/(kg·K)'], + tags: ['heat capacity per unit mass and temperature'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-humidity.ts b/ui-ngx/src/app/shared/models/units/specific-humidity.ts index 82f12b26e2..9a93f513da 100644 --- a/ui-ngx/src/app/shared/models/units/specific-humidity.ts +++ b/ui-ngx/src/app/shared/models/units/specific-humidity.ts @@ -22,7 +22,7 @@ const METRIC: TbMeasureUnits = { units: { 'g/kg': { name: 'unit.gram-per-kilogram', - tags: ['humidity', 'moisture', 'specific humidity', 'g/kg'], + tags: ['humidity', 'moisture'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-volume.ts b/ui-ngx/src/app/shared/models/units/specific-volume.ts index 9968454e41..adef7b4b00 100644 --- a/ui-ngx/src/app/shared/models/units/specific-volume.ts +++ b/ui-ngx/src/app/shared/models/units/specific-volume.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type SpecificVolumeMetricUnits = 'm³/kg'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SpecificVolumeUnits = SpecificVolumeMetricUnits; +export type SpecificVolumeUnits = 'm³/kg'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'm³/kg': { name: 'unit.cubic-meter-per-kilogram', - tags: ['specific volume', 'volume per unit mass', 'cubic meter per kilogram', 'm³/kg'], + tags: ['volume per unit mass'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/speed.ts b/ui-ngx/src/app/shared/models/units/speed.ts index f44553a7a7..b930b0739c 100644 --- a/ui-ngx/src/app/shared/models/units/speed.ts +++ b/ui-ngx/src/app/shared/models/units/speed.ts @@ -26,22 +26,22 @@ const METRIC: TbMeasureUnits = { units: { 'm/s': { name: 'unit.meter-per-second', - tags: ['speed', 'velocity', 'pace', 'meter per second', 'm/s', 'peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'wind speed', 'weather'], + tags: ['velocity', 'pace', 'peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'wind speed', 'weather'], to_anchor: 3.6, }, 'km/h': { name: 'unit.kilometer-per-hour', - tags: ['speed', 'velocity', 'pace', 'kilometer per hour', 'km/h'], + tags: ['velocity', 'pace'], to_anchor: 1, }, 'mm/min': { name: 'unit.millimeters-per-minute', - tags: ['feed rate', 'cutting feed rate', 'millimeters per minute', 'mm/min'], + tags: ['feed rate', 'cutting feed rate'], to_anchor: 0.06, }, 'mm/s': { name: 'unit.millimeters-per-second', - tags: ['velocity', 'speed', 'vibration rate', 'millimeters per second', 'mm/s'], + tags: ['velocity', 'vibration rate'], to_anchor: 0.0036, }, }, @@ -52,27 +52,27 @@ const IMPERIAL: TbMeasureUnits = { units: { mph: { name: 'unit.mile-per-hour', - tags: ['speed', 'velocity', 'pace', 'mile per hour', 'mph'], + tags: ['velocity', 'pace'], to_anchor: 1, }, kt: { name: 'unit.knot', - tags: ['speed', 'velocity', 'pace', 'knot', 'knots', 'kt'], + tags: ['velocity', 'pace'], to_anchor: 1.150779, }, 'ft/s': { name: 'unit.foot-per-second', - tags: ['speed', 'velocity', 'pace', 'foot per second', 'ft/s'], - to_anchor: 0.681818, // 1 ft/s ≈ 0.681818 mph + tags: ['velocity', 'pace'], + to_anchor: 0.681818, }, 'ft/min': { name: 'unit.foot-per-minute', - tags: ['speed', 'velocity', 'pace', 'foot per minute', 'ft/min'], + tags: ['velocity', 'pace'], to_anchor: 0.0113636, }, 'in/h': { name: 'unit.inch-per-hour', - tags: ['speed', 'velocity', 'pace', 'inch per hour', 'in/h'], + tags: ['velocity', 'pace'], to_anchor: 0.00001578, }, }, diff --git a/ui-ngx/src/app/shared/models/units/sugar-content.ts b/ui-ngx/src/app/shared/models/units/sugar-content.ts deleted file mode 100644 index 1badaf3ce0..0000000000 --- a/ui-ngx/src/app/shared/models/units/sugar-content.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type SugarContentUnits = '°Bx'; - -const METRIC: TbMeasureUnits = { - units: { - '°Bx': { - name: 'unit.degrees-brix', - tags: ['sugar content', 'fruit ripeness', 'Bx'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/surface-charge-density.ts b/ui-ngx/src/app/shared/models/units/surface-charge-density.ts index 306649db3c..d0640bcf4e 100644 --- a/ui-ngx/src/app/shared/models/units/surface-charge-density.ts +++ b/ui-ngx/src/app/shared/models/units/surface-charge-density.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type SurfaceChargeDensityMetricUnits = 'C/m²'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SurfaceChargeDensityUnits = SurfaceChargeDensityMetricUnits; +export type SurfaceChargeDensityUnits = 'C/m²'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'C/m²': { name: 'unit.coulomb-per-square-meter', - tags: ['electric surface charge density', 'coulomb per square meter', 'C/m²'], + tags: ['electric surface charge density'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/surface-tension.ts b/ui-ngx/src/app/shared/models/units/surface-tension.ts index b1c5593354..718b8113c1 100644 --- a/ui-ngx/src/app/shared/models/units/surface-tension.ts +++ b/ui-ngx/src/app/shared/models/units/surface-tension.ts @@ -1,20 +1,34 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type SurfaceTensionMetricUnits = 'N/m'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SurfaceTensionhUnits = SurfaceTensionMetricUnits; +export type SurfaceTensionUnits = 'N/m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'N/m': { name: 'unit.newton-per-meter', - tags: ['linear density', 'force per unit length', 'newton per meter', 'N/m'], + tags: ['linear density', 'force per unit length'], to_anchor: 1, }, }, }; -const measure: TbMeasure = { +const measure: TbMeasure = { METRIC, }; diff --git a/ui-ngx/src/app/shared/models/units/temperature.ts b/ui-ngx/src/app/shared/models/units/temperature.ts index 67f17f0a4b..59d11f1eed 100644 --- a/ui-ngx/src/app/shared/models/units/temperature.ts +++ b/ui-ngx/src/app/shared/models/units/temperature.ts @@ -28,12 +28,12 @@ const METRIC: TbMeasureUnits = { units: { '°C': { name: 'unit.celsius', - tags: ['temperature', 'heat', 'cold', 'warmth', 'degrees', 'celsius', 'shipment condition', '°C'], + tags: ['heat', 'cold', 'warmth', 'degrees', 'shipment condition'], to_anchor: 1, }, K: { name: 'unit.kelvin', - tags: ['temperature', 'heat', 'cold', 'warmth', 'degrees', 'kelvin', 'K', 'color quality', 'white balance', 'color temperature'], + tags: ['heat', 'cold', 'warmth', 'degrees', 'color quality', 'white balance', 'color temperature'], to_anchor: 1, anchor_shift: 273.15, }, @@ -45,12 +45,12 @@ const IMPERIAL: TbMeasureUnits = { units: { '°F': { name: 'unit.fahrenheit', - tags: ['temperature', 'heat', 'cold', 'warmth', 'degrees', 'fahrenheit', '°F'], + tags: ['heat', 'cold', 'warmth', 'degrees'], to_anchor: 1, }, '°R': { name: 'unit.rankine', - tags: ['temperature', 'heat', 'cold', 'warmth', 'Rankine', '°R'], + tags: ['heat', 'cold', 'warmth'], to_anchor: 1, anchor_shift: 459.67, }, diff --git a/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts b/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts index 3c27727919..5407614164 100644 --- a/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts +++ b/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 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. +/// -export type ThermalConductivityMetricUnits = 'W/(m·K)'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ThermalConductivityUnits = ThermalConductivityMetricUnits; +export type ThermalConductivityUnits = 'W/(m·K)'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'W/(m·K)': { name: 'unit.watt-per-meter-kelvin', - tags: ['thermal conductivity', 'watt per meter-kelvin', 'W/(m·K)'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/time.ts b/ui-ngx/src/app/shared/models/units/time.ts index 944bfafbe7..2833080230 100644 --- a/ui-ngx/src/app/shared/models/units/time.ts +++ b/ui-ngx/src/app/shared/models/units/time.ts @@ -36,52 +36,52 @@ const METRIC: TbMeasureUnits = { units: { ns: { name: 'unit.nanosecond', - tags: ['time', 'duration', 'interval', 'ns'], + tags: ['duration', 'interval'], to_anchor: 1 / 1000000000 }, - 'μs': { + μs: { name: 'unit.microsecond', - tags: ['time', 'duration', 'interval', 'h'], + tags: ['duration', 'interval'], to_anchor: 1 / 1000000 }, ms: { name: 'unit.millisecond', - tags: ['time', 'duration', 'interval', 'ms'], + tags: ['duration', 'interval'], to_anchor: 1 / 1000 }, s: { name: 'unit.second', - tags: ['time', 'duration', 'interval', 'second', 'sec'], + tags: ['duration', 'interval'], to_anchor: 1, }, min: { name: 'unit.minute', - tags: ['time', 'duration', 'interval', 'minute', 'min'], + tags: ['duration', 'interval'], to_anchor: 60, }, h: { name: 'unit.hour', - tags: ['time', 'duration', 'interval', 'h'], + tags: ['duration', 'interval'], to_anchor: 60 * 60, }, d: { name: 'unit.day', - tags: ['time', 'duration', 'interval', 'd'], + tags: ['duration', 'interval'], to_anchor: 60 * 60 * 24, }, wk: { name: 'unit.week', - tags: ['time', 'duration', 'interval', 'wk'], + tags: ['duration', 'interval'], to_anchor: 60 * 60 * 24 * 7, }, mo: { name: 'unit.month', - tags: ['time', 'duration', 'interval', 'mo'], + tags: ['duration', 'interval'], to_anchor: (60 * 60 * 24 * daysInYear) / 12, }, yr: { name: 'unit.year', - tags: ['time', 'duration', 'interval', 'yr'], + tags: ['duration', 'interval'], to_anchor: 60 * 60 * 24 * daysInYear, }, } diff --git a/ui-ngx/src/app/shared/models/units/torque.ts b/ui-ngx/src/app/shared/models/units/torque.ts index fa830a0a4a..03ef7e7208 100644 --- a/ui-ngx/src/app/shared/models/units/torque.ts +++ b/ui-ngx/src/app/shared/models/units/torque.ts @@ -26,7 +26,7 @@ const METRIC: TbMeasureUnits = { units: { Nm: { name: 'unit.newton-meter', - tags: ['torque', 'rotational force', 'newton meter', 'Nm'], + tags: ['rotational force', 'newton meter', 'Nm'], to_anchor: 1, }, }, @@ -37,12 +37,12 @@ const IMPERIAL: TbMeasureUnits = { units: { 'lbf-ft': { name: 'unit.foot-pounds', - tags: ['torque', 'rotational force', 'foot-pound', 'foot-pounds', 'ft·lbf'], + tags: ['rotational force'], to_anchor: 1, }, 'in·lbf': { name: 'unit.inch-pounds', - tags: ['torque', 'rotational force', 'inch-pounds', 'inch-pound', 'in·lbf'], + tags: ['rotational force'], to_anchor: 1 / 12, }, }, diff --git a/ui-ngx/src/app/shared/models/units/turbidity.ts b/ui-ngx/src/app/shared/models/units/turbidity.ts index 92b1122437..3d5e752ef7 100644 --- a/ui-ngx/src/app/shared/models/units/turbidity.ts +++ b/ui-ngx/src/app/shared/models/units/turbidity.ts @@ -1,13 +1,28 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type TurbidityUnits = TurbidityMetricUnits; -export type TurbidityMetricUnits = 'NTU'; +export type TurbidityUnits = 'NTU'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { NTU: { name: 'unit.turbidity', - tags: ['water turbidity', 'water clarity', 'Nephelometric Turbidity Units', 'NTU'], + tags: ['water turbidity', 'water clarity', 'Nephelometric Turbidity Units'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/voltage.ts b/ui-ngx/src/app/shared/models/units/voltage.ts index 54f0ec2c20..10c079551c 100644 --- a/ui-ngx/src/app/shared/models/units/voltage.ts +++ b/ui-ngx/src/app/shared/models/units/voltage.ts @@ -23,37 +23,37 @@ const METRIC: TbMeasureUnits = { units: { pV: { name: 'unit.picovolt', - tags: ['voltage', 'volts', 'picovolt', 'pV'], + tags: ['volts'], to_anchor: 1e-12, }, nV: { name: 'unit.nanovolt', - tags: ['voltage', 'volts', 'nanovolt', 'nV'], + tags: ['volts'], to_anchor: 1e-9, }, μV: { name: 'unit.microvolt', - tags: ['electric potential', 'electric tension', 'voltage', 'microvolt', 'microvolts', 'μV'], + tags: ['electric potential', 'electric tension'], to_anchor: 1e-6, }, mV: { name: 'unit.millivolt', - tags: ['electric potential', 'electric tension', 'voltage', 'millivolt', 'millivolts', 'mV'], - to_anchor: 0.001, // 1 mV = 1e-3 V + tags: ['electric potential', 'electric tension'], + to_anchor: 0.001, }, V: { name: 'unit.volt', - tags: ['electric potential', 'electric tension', 'voltage', 'volt', 'volts', 'V', 'power source', 'battery', 'battery level'], + tags: ['electric potential', 'electric tension', 'power source', 'battery', 'battery level'], to_anchor: 1, }, kV: { name: 'unit.kilovolt', - tags: ['electric potential', 'electric tension', 'voltage', 'kilovolt', 'kilovolts', 'kV'], + tags: ['electric potential', 'electric tension'], to_anchor: 1000, }, MV: { name: 'unit.megavolt', - tags: ['electric potential', 'electric tension', 'voltage', 'megavolt', 'megavolts', 'MV'], + tags: ['electric potential', 'electric tension'], to_anchor: 1e6, }, }, diff --git a/ui-ngx/src/app/shared/models/units/volume-charge-density.ts b/ui-ngx/src/app/shared/models/units/volume-charge-density.ts deleted file mode 100644 index 0b172962fd..0000000000 --- a/ui-ngx/src/app/shared/models/units/volume-charge-density.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type VolumeChargeDensityMetricUnits = 'C/m³'; - -export type VolumeChargeDensityUnits = VolumeChargeDensityMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'C/m³': { - name: 'unit.coulomb-per-cubic-meter', - tags: ['electric charge density', 'coulomb per cubic meter', 'C/m³'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/volume-flow-rate.ts b/ui-ngx/src/app/shared/models/units/volume-flow.ts similarity index 56% rename from ui-ngx/src/app/shared/models/units/volume-flow-rate.ts rename to ui-ngx/src/app/shared/models/units/volume-flow.ts index fd1f25c1f4..0f9fa44520 100644 --- a/ui-ngx/src/app/shared/models/units/volume-flow-rate.ts +++ b/ui-ngx/src/app/shared/models/units/volume-flow.ts @@ -16,9 +16,9 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type VolumeFlowRateUnits = VolumeFlowRateMetricUnits | VolumeFlowRateImperialUnits; +export type VolumeFlowUnits = VolumeFlowMetricUnits | VolumeFlowImperialUnits; -export type VolumeFlowRateMetricUnits = +export type VolumeFlowMetricUnits = | 'dm³/s' | 'mL/min' | 'L/s' @@ -27,86 +27,86 @@ export type VolumeFlowRateMetricUnits = | 'm³/s' | 'm³/hr'; -export type VolumeFlowRateImperialUnits = +export type VolumeFlowImperialUnits = | 'fl-oz/s' | 'ft³/s' | 'ft³/min' | 'gal/hr' | 'GPM'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { ratio: 33.8140227, units: { + 'L/s': { + name: 'unit.liter-per-second', + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], + to_anchor: 1, + }, 'dm³/s': { name: 'unit.cubic-decimeter-per-second', - tags: ['volume flow', 'cubic decimeter per second', 'dm3/s'], + tags: ['cubic decimeter per second'], to_anchor: 1, }, 'mL/min': { name: 'unit.milliliters-per-minute', - tags: ['volume flow', 'flow rate', 'fluid dynamics', 'milliliters per minute', 'mL/min'], + tags: ['flow rate', 'fluid dynamics'], to_anchor: 1 / 60000, }, - 'L/s': { - name: 'unit.liter-per-second', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'liter per second', 'L/s'], - to_anchor: 1, - }, 'L/min': { name: 'unit.liter-per-minute', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'liter per minute', 'L/min'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], to_anchor: 1 / 60, }, 'L/hr': { name: 'unit.liters-per-hour', - tags: ['volume flow', 'fuel consumption', 'liter per hour', 'L/hr'], + tags: ['fuel consumption'], to_anchor: 1 / 3600, }, 'm³/s': { name: 'unit.cubic-meters-per-second', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'cubic meters per second', 'm³/s'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], to_anchor: 1000, }, 'm³/hr': { name: 'unit.cubic-meters-per-hour', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'cubic meters per hour', 'm³/hr'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], to_anchor: 5 / 18, }, }, }; -const IMPERIAL: TbMeasureUnits = { +const IMPERIAL: TbMeasureUnits = { ratio: 1 / 33.8140227, units: { 'fl-oz/s': { name: 'unit.fluid-ounce-per-second', - tags: ['volume flow', 'fluid ounce per second', 'fl-oz/s'], + tags: ['fluid ounce per second', 'fl-oz/s'], to_anchor: 1, }, 'ft³/s': { name: 'unit.cubic-foot-per-second', - tags: ['volume flow', 'flow rate', 'fluid flow', 'cubic foot per second', 'cubic feet per second', 'ft³/s'], + tags: ['flow rate', 'fluid flow'], to_anchor: 957.506, }, 'ft³/min': { name: 'unit.cubic-foot-per-minute', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'CFM', 'flow rate', 'fluid flow', 'cubic foot per minute', 'ft³/min'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate', 'CFM', 'flow rate', 'fluid flow'], to_anchor: 957.506 / 60, }, 'gal/hr': { name: 'unit.gallons-per-hour', - tags: ['volume flow', 'fuel consumption', 'gallons per hour', 'gal/hr'], + tags: ['fuel consumption'], to_anchor: 128 / 3600, }, 'GPM': { name: 'unit.gallons-per-minute', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'gallons per minute', 'GPM'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], to_anchor: 128 / 60, }, }, }; -const measure: TbMeasure = { +const measure: TbMeasure = { METRIC, IMPERIAL, }; diff --git a/ui-ngx/src/app/shared/models/units/volume.ts b/ui-ngx/src/app/shared/models/units/volume.ts index 92e1a2dfa7..42b78ad8f2 100644 --- a/ui-ngx/src/app/shared/models/units/volume.ts +++ b/ui-ngx/src/app/shared/models/units/volume.ts @@ -48,42 +48,42 @@ const METRIC: TbMeasureUnits = { units: { 'mm³': { name: 'unit.cubic-millimeter', - tags: ['volume', 'capacity', 'extent', 'cubic millimeter', 'mm³'], + tags: ['capacity', 'extent'], to_anchor: 1 / 1000000, }, 'cm³': { name: 'unit.cubic-centimeter', - tags: ['volume', 'capacity', 'extent', 'cubic centimeter', 'cubic centimeters', 'cm³'], + tags: ['capacity', 'extent'], to_anchor: 1 / 1000, }, µL: { name: 'unit.microliter', - tags: ['volume', 'liquid measurement', 'microliter', 'µL'], + tags: ['liquid measurement'], to_anchor: 0.000001, }, mL: { name: 'unit.milliliter', - tags: ['volume', 'capacity', 'extent', 'milliliter', 'milliliters', 'mL'], + tags: ['capacity', 'extent'], to_anchor: 1 / 1000, }, L: { name: 'unit.liter', - tags: ['volume', 'capacity', 'extent', 'liter', 'liters', 'l'], + tags: ['capacity', 'extent'], to_anchor: 1, }, hL: { name: 'unit.hectoliter', - tags: ['volume', 'capacity', 'extent', 'hectoliter', 'hectoliters', 'hl'], + tags: ['capacity', 'extent'], to_anchor: 100, }, 'm³': { name: 'unit.cubic-meter', - tags: ['volume', 'capacity', 'extent', 'cubic meter', 'cubic meters', 'm³'], + tags: ['capacity', 'extent'], to_anchor: 1000, }, 'km³': { name: 'unit.cubic-kilometer', - tags: ['volume', 'capacity', 'extent', 'cubic kilometer', 'cubic kilometers', 'km³'], + tags: ['capacity', 'extent'], to_anchor: 1000000000000, }, }, @@ -94,67 +94,67 @@ const IMPERIAL: TbMeasureUnits = { units: { tsp: { name: 'unit.teaspoon', - tags: ['volume', 'cooking measurement', 'tsp'], + tags: ['cooking measurement'], to_anchor: 1 / 6, }, tbsp: { name: 'unit.tablespoon', - tags: ['volume', 'cooking measurement', 'tbsp'], + tags: ['cooking measurement'], to_anchor: 1 / 2, }, 'in³': { name: 'unit.cubic-inch', - tags: ['volume', 'capacity', 'extent', 'cubic inch', 'cubic inches', 'in³'], + tags: ['capacity', 'extent'], to_anchor: 0.55411, }, 'fl-oz': { name: 'unit.fluid-ounce', - tags: ['volume', 'capacity', 'extent', 'fluid ounce', 'fluid ounces', 'fl-oz'], + tags: ['capacity', 'extent'], to_anchor: 1, }, cup: { name: 'unit.cup', - tags: ['volume', 'cooking measurement', 'cup'], + tags: ['cooking measurement'], to_anchor: 8, }, pt: { name: 'unit.pint', - tags: ['volume', 'capacity', 'extent', 'pint', 'pints', 'pt'], + tags: ['capacity', 'extent'], to_anchor: 16, }, qt: { name: 'unit.quart', - tags: ['volume', 'capacity', 'extent', 'quart', 'quarts', 'qt'], + tags: ['capacity', 'extent'], to_anchor: 32, }, gal: { name: 'unit.gallon', - tags: ['volume', 'capacity', 'extent', 'gallon', 'gallons', 'gal'], + tags: ['capacity', 'extent'], to_anchor: 128, }, 'ft³': { name: 'unit.cubic-foot', - tags: ['volume', 'capacity', 'extent', 'cubic foot', 'cubic feet', 'ft³'], + tags: ['capacity', 'extent'], to_anchor: 957.506, }, 'yd³': { name: 'unit.cubic-yard', - tags: ['volume', 'capacity', 'extent', 'cubic yard', 'cubic yards', 'yd³'], + tags: ['capacity', 'extent'], to_anchor: 25852.7, }, bbl: { name: 'unit.oil-barrels', - tags: ['volume', 'capacity', 'extent', 'oil barrel', 'oil barrels', 'bbl'], + tags: ['capacity', 'extent'], to_anchor: 5376, }, gi: { name: 'unit.gill', - tags: ['volume', 'liquid measurement', 'gi'], + tags: ['liquid measurement'], to_anchor: 4, }, hhd: { name: 'unit.hogshead', - tags: ['volume', 'liquid measurement', 'hhd'], + tags: ['liquid measurement'], to_anchor: 8064, }, }, diff --git a/ui-ngx/src/app/shared/models/units/wavenumber.ts b/ui-ngx/src/app/shared/models/units/wavenumber.ts deleted file mode 100644 index 6745601328..0000000000 --- a/ui-ngx/src/app/shared/models/units/wavenumber.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type WavenumberMetricUnits = 'm⁻¹'; - -export type WavenumberUnits = WavenumberMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'm⁻¹': { - name: 'unit.reciprocal-metre', - tags: ['wavenumber', 'wave density', 'wave frequency', 'm⁻¹'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c6e2d31cbc..3798e96a9b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5865,30 +5865,37 @@ "HYBRID": "Hybrid" }, "measures": { + "absorbed-dose-rate": "Absorbed dose rate", "acceleration": "Acceleration", + "acidity": "Acidity", "air-quality-index": "Air quality index", "amount-of-substance": "Amount of substance", "angle": "Angle", "angular-acceleration": "Angular acceleration", "area": "Area", + "area-density": "Area density", "capacitance": "Capacitance", "catalytic-activity": "Catalytic activity", - "catalytic-concetration": "Catalytic concetration", + "catalytic-concentration": "Catalytic concentration", "charge": "Charge", "current-density": "Current density", "data-transfer-rate": "Data transfer rate", + "density": "Density", "digital": "Digital", "dimension-ratio": "Dimension ratio", "dynamic-viscosity": "Dynamic viscosity", "earthquake-magnitude": "Earthquake magnitude", + "electric-charge-density": "Electric charge density", "electric-current": "Electric current", "electric-dipole-moment": "Electric dipole moment", "electric-field-strength": "Electric field strength", "electric-flux": "Electric flux", "electric-permittivity": "Electric permittivity", + "electric-polarizability": "Electric polarizability", "electrical-conductance": "Electrical conductance", "electrical-conductivity": "Electrical conductivity", "energy": "Energy", + "energy-density": "Energy density", "force": "Force", "frequency": "Frequency", "fuel-efficiency": "fuel efficiency", @@ -5898,6 +5905,7 @@ "kinematic-viscosity": "Kinematic viscosity", "length": "Length", "light-exposure": "Light exposure", + "linear-charge-density": "Linear charge density", "logarithmic-ratio": "Logarithmic ratio", "luminous-efficacy": "Luminous efficacy", "luminous-flux": "Luminous flux", @@ -5909,13 +5917,41 @@ "magnetic-permeability": "Magnetic permeability", "mass": "Mass", "mass-fraction": "Mass fraction", - "molar-concetration": "Molar concetration", + "molar-concentration": "Molar concentration", "molar-energy": "Molar energy", "molar-heat-capacity": "Molar heat capacity", "molar-mass": "Molar mass", + "number-concentration": "Number concentration", + "parts-per-million": "Parts per million", + "power": "Power", + "power-density": "Power density", + "pressure": "Pressure", + "radiance": "Radiance", + "radiant-intensity": "Radiant intensity", + "radiation-dose": "Radiation dose", + "radioactive-decay": "Radioactive decay", + "radioactivity": "Radioactivity", + "radioactivity-concentration": "Radioactivity concentration", + "reciprocal-length": "Reciprocal length", + "resistance": "Resistance", + "reynolds-number": "Reynolds number", + "signal-level": "Signal level", + "solid-angle": "Solid angle", + "specific-energy": "Specific energy", + "specific-heat-capacity": "Specific heat capacity", "specific-humidity": "Specific humidity", + "specific-volume": "Specific volume", + "speed": "Speed", + "surface-charge-density": "Surface charge density", + "surface-tension": "Surface tension", "temperature": "Temperature", - "time": "Time" + "thermal-conductivity": "Thermal conductivity", + "time": "Time", + "torque": "Torque", + "turbidity": "Turbidity", + "voltage": "Voltage", + "volume": "Volume", + "volume-flow": "Volume flow" }, "millimeter": "Millimeter", "centimeter": "Centimeter", @@ -6098,6 +6134,7 @@ "ampere-hours": "Ampere-hours", "kiloampere-hours": "Kiloampere-hours", "nanoampere": "Nanoampere", + "picoampere": "Picoampere", "microampere": "Microampere", "milliampere": "Milliampere", "ampere": "Ampere", From 2e65c79184e2261451287522a149efb758ce1e77 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 6 May 2025 18:42:38 +0300 Subject: [PATCH 137/335] UI: Updated unit definition --- ui-ngx/src/app/core/services/unit.service.ts | 6 +- .../shared/components/unit-input.component.ts | 12 ++-- ui-ngx/src/app/shared/models/unit.models.ts | 69 +++++-------------- .../app/shared/models/units/acceleration.ts | 4 +- ui-ngx/src/app/shared/models/units/length.ts | 6 ++ .../shared/models/units/logarithmic-ratio.ts | 4 +- .../assets/locale/locale.constant-en_US.json | 3 +- 7 files changed, 40 insertions(+), 64 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index 3d1fc4de0a..9e656907f3 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -44,10 +44,8 @@ export class UnitService { takeUntilDestroyed() ).subscribe(() => { this.converter = getUnitConverter(this.translate); - console.warn(this.converter?.listUnits()); - console.warn(this.converter?.listUnits('temperature')); - console.warn(this.converter?.listUnits('temperature', UnitSystem.METRIC)); - console.warn(this.converter?.listUnits(null, UnitSystem.IMPERIAL)); + console.warn(this.converter.listUnits()); + console.warn(this.converter.listUnits(null, UnitSystem.IMPERIAL)); }); } diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index 9b582d1266..9ec990a0da 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -31,7 +31,7 @@ import { } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormControl, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Observable, of, shareReplay } from 'rxjs'; -import { AllMeasures, searchUnits, TbUnit, UnitInfo, UnitsType, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures, TbUnit, UnitInfo, UnitsType, UnitSystem } from '@shared/models/unit.models'; import { map, mergeMap } from 'rxjs/operators'; import { UnitService } from '@core/services/unit.service'; import { TbPopoverService } from '@shared/components/popover.service'; @@ -247,9 +247,13 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { if (isNotEmptyStr(searchText)) { const filterValue = searchText.trim().toUpperCase() - return units - .map(measure => [measure[0], searchUnits(measure[1], filterValue)] as [AllMeasures, UnitInfo[]]) - .filter((measure) => measure[1].length > 0); + return units.reduce((result: Array<[AllMeasures, Array]>, [measure, unitInfos]) => { + const filteredUnits = unitInfos.filter(unit => unit.searchText.toUpperCase().includes(filterValue)); + if (filteredUnits.length > 0) { + result.push([measure, filteredUnits]); + } + return result; + }, []); } return units; } diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 3fcf742cb0..57a2f26f6e 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -83,6 +83,7 @@ import radioactivity, { RadioactivityUnits } from '@shared/models/units/radioact import radioactivityConcentration, { RadioactivityConcentrationUnits } from '@shared/models/units/radioactivity-concentration'; +import reciprocalLength, { ReciprocalLengthUnits } from '@shared/models/units/reciprocal-length'; import resistance, { ResistanceUnits } from '@shared/models/units/resistance'; import reynoldsNumber, { ReynoldsNumberUnits } from '@shared/models/units/reynolds-number'; import signalLevel, { SignalLevelUnits } from '@shared/models/units/signal-level'; @@ -103,7 +104,6 @@ import voltage, { VoltageUnits } from '@shared/models/units/voltage'; import volume, { VolumeUnits } from '@shared/models/units/volume'; import volumeFlow, { VolumeFlowUnits } from '@shared/models/units/volume-flow'; import { TranslateService } from '@ngx-translate/core'; -import reciprocalLength, { ReciprocalLengthUnits } from '@shared/models/units/reciprocal-length'; export type AllMeasuresUnits = | AbsorbedDoseRateUnits @@ -388,6 +388,7 @@ export interface UnitInfo { system: UnitSystem; name: string; tags: string[]; + searchText: string; } export enum UnitSystem { @@ -423,43 +424,21 @@ export interface TbMeasureUnits { units?: Partial>; } -export interface Conversion { - abbr: TUnits; - measure: TMeasures; +export interface UnitCacheInfo { system: UnitSystem; + measure: AllMeasures; unit: Unit; + abbr: AllMeasuresUnits; + searchText: string; } -export type UnitCache = Map; - -const searchUnitTags = (unit: UnitInfo, searchText: string): boolean => - !!unit.tags.find(t => t.toUpperCase().includes(searchText)); - -export const searchUnits = (_units: Array, searchText: string): Array => _units.filter( - u => u.abbr.toUpperCase().includes(searchText) || - u.name.toUpperCase().includes(searchText) || - searchUnitTags(u, searchText) -); +export type UnitCache = Map; type Entries = [S, T[keyof T]]; export class Converter { private readonly measureData: Record>; - private unitCache: Map< - string, - { - system: UnitSystem; - measure: AllMeasures; - unit: Unit; - abbr: AllMeasuresUnits; - } - >; + private unitCache: UnitCache; constructor( measures: Record>, @@ -534,7 +513,7 @@ export class Converter { return null; } - getUnit(abbr: AllMeasuresUnits | string): Conversion | null { + getUnit(abbr: AllMeasuresUnits | string): UnitCacheInfo | null { return this.unitCache.get(abbr) ?? null; } @@ -565,22 +544,15 @@ export class Converter { continue; } - for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { - results.push( - this.describeUnit({ - abbr, - measure: name as AllMeasures, - system, - unit, - }) - ); + for (const abbr of Object.keys(units) as AllMeasuresUnits[]) { + results.push(this.describe(abbr)); } } } return results; } - unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure | never { + unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure { const results: UnitInfoGroupByMeasure = {}; const measures = measureName @@ -604,28 +576,22 @@ export class Converter { continue; } - for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { - results[name].push( - this.describeUnit({ - abbr, - measure: name as AllMeasures, - system, - unit, - }) - ); + for (const abbr of Object.keys(units) as AllMeasuresUnits[]) { + results[name].push(this.describe(abbr)); } } } return results; } - private describeUnit(unit: Conversion): UnitInfo { + private describeUnit(unit: UnitCacheInfo): UnitInfo { return { abbr: unit.abbr, measure: unit.measure, system: unit.system, name: unit.unit.name, - tags: unit.unit.tags + tags: unit.unit.tags, + searchText: unit.searchText }; } @@ -671,6 +637,7 @@ function buildUnitCache(measures: Record = { ratio: 3.28084, units: { - G: { + 'g₀': { name: 'unit.g-force', tags: ['gravity', 'load'], to_anchor: 9.80665, diff --git a/ui-ngx/src/app/shared/models/units/length.ts b/ui-ngx/src/app/shared/models/units/length.ts index 549a66c3e6..57847fb71d 100644 --- a/ui-ngx/src/app/shared/models/units/length.ts +++ b/ui-ngx/src/app/shared/models/units/length.ts @@ -27,6 +27,7 @@ export type LengthImperialUnits = | 'fathom' | 'mi' | 'nmi' + | 'pouce' | 'thou' | 'barleycorn' | 'hand' @@ -122,6 +123,11 @@ const IMPERIAL: TbMeasureUnits = { tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'nautical mile'], to_anchor: 6076.12, }, + pouce: { + name: 'unit.paris-inch', + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'nautical mile'], + to_anchor: 1.0657, + }, thou: { name: 'unit.thou', tags: ['measurement'], diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts index 83a26c3030..709eb44e35 100644 --- a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts +++ b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts @@ -16,7 +16,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LogarithmicRatioUnits = 'dB' | 'B' | 'Np'; +export type LogarithmicRatioUnits = 'dB' | 'bel' | 'Np'; const METRIC: TbMeasureUnits = { units: { @@ -25,7 +25,7 @@ const METRIC: TbMeasureUnits = { tags: ['noise level', 'sound level', 'volume', 'acoustics'], to_anchor: 1, }, - B: { + bel: { name: 'unit.bel', tags: ['power ratio', 'intensity ratio'], to_anchor: 10, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 3798e96a9b..aa4ef10837 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6376,7 +6376,8 @@ "rotation-per-minute": "Rotation per minute", "degrees-brix": "Degrees Brix", "katal": "Katal", - "katal-per-cubic-metre": "Katal per Cubic Metre" + "katal-per-cubic-metre": "Katal per Cubic Metre", + "paris-inch": "Paris inch" }, "user": { "user": "User", From 381e976d87c252722e488dfd19e0416721823794 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 7 May 2025 12:17:24 +0300 Subject: [PATCH 138/335] Refactoring for input node relations --- .../update/DefaultDataUpdateService.java | 69 ++++----- .../server/dao/rule/BaseRuleChainService.java | 39 +++-- .../dao/service/RuleChainServiceTest.java | 134 ++++++++++-------- 3 files changed, 117 insertions(+), 125 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index 4c90258330..d8ddd7cd9f 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -24,21 +24,18 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageDataIterable; -import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; -import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.RuleNodeClassInfo; import org.thingsboard.server.service.install.DbUpgradeExecutorService; @@ -46,6 +43,7 @@ import org.thingsboard.server.utils.TbNodeUpgradeUtils; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -65,9 +63,6 @@ public class DefaultDataUpdateService implements DataUpdateService { @Autowired private RelationService relationService; - @Autowired - private TenantService tenantService; - @Autowired private ComponentDiscoveryService componentDiscoveryService; @@ -78,52 +73,36 @@ public class DefaultDataUpdateService implements DataUpdateService { public void updateData() throws Exception { log.info("Updating data ..."); //TODO: should be cleaned after each release - inputNodesUpdater.updateEntities(); + updateInputNodes(); log.info("Data updated."); } - //TODO: should be removed after release - private final PaginatedUpdater inputNodesUpdater = new PaginatedUpdater<>() { - @Override - protected String getName() { - return "Input nodes updater"; - } - - @Override - protected PageData findEntities(String type, PageLink pageLink) { - return tenantService.findTenants(pageLink); - } - - @Override - protected void updateEntity(Tenant tenant) { - TenantId tenantId = tenant.getId(); + private void updateInputNodes() { + log.info("Creating relations for input nodes..."); + int n = 0; + var inputNodes = new PageDataIterable<>(pageLink -> ruleChainService.findAllRuleNodesByType(TB_RULE_CHAIN_INPUT_NODE, pageLink), 1024); + for (RuleNode inputNode : inputNodes) { try { - var inputNodes = ruleChainService.findRuleNodesByTenantIdAndType(tenantId, TB_RULE_CHAIN_INPUT_NODE); - var resultFutures = inputNodes.stream().map(ruleNode -> { - try { - JsonNode id = ruleNode.getConfiguration().get("ruleChainId"); - if (id != null) { - RuleChainId toRuleChainId = new RuleChainId(UUID.fromString(id.asText())); - RuleChainId fromRuleChainId = ruleNode.getRuleChainId(); - EntityRelation relation = new EntityRelation(); - relation.setFrom(fromRuleChainId); - relation.setTo(toRuleChainId); - relation.setType(EntityRelation.USES_TYPE); - relation.setTypeGroup(RelationTypeGroup.COMMON); - return relationService.saveRelationAsync(tenantId, relation); - } - } catch (Exception e) { - log.error("[{}] Failed to save relation for input node: [{}]", tenantId, ruleNode, e); - } - return Futures.immediateFuture(null); - }).toList(); + RuleChainId targetRuleChainId = Optional.ofNullable(inputNode.getConfiguration().get("ruleChainId")) + .filter(JsonNode::isTextual).map(JsonNode::asText).map(id -> new RuleChainId(UUID.fromString(id))) + .orElse(null); + if (targetRuleChainId == null) { + continue; + } - Futures.allAsList(resultFutures).get(); + EntityRelation relation = new EntityRelation(); + relation.setFrom(inputNode.getRuleChainId()); + relation.setTo(targetRuleChainId); + relation.setType(EntityRelation.USES_TYPE); + relation.setTypeGroup(RelationTypeGroup.COMMON); + relationService.saveRelation(TenantId.SYS_TENANT_ID, relation); + n++; } catch (Exception e) { - log.error("[{}] Unable to update Tenant input nodes", tenantId, e); + log.error("Failed to save relation for input node: {}", inputNode, e); } } - }; + log.info("Created {} relations for input nodes", n); + } @Override public void upgradeRuleNodes() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 1ce9bd555b..8538bd9492 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -211,9 +211,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC for (RuleNode existingNode : existingRuleNodes) { relationService.deleteEntityRelations(tenantId, existingNode.getId()); if (existingNode.getType().equals(TB_RULE_CHAIN_INPUT_NODE)) { - if (existingNode.getConfiguration().has("ruleChainId")) { - RuleChainId targetRuleChainId = extractRuleChainIdFromInputNode(existingNode); - var relation = createRuleChainInputRelation(ruleChainId, targetRuleChainId); + EntityRelation relation = getRuleChainInputRelation(ruleChainId, existingNode); + if (relation != null) { relationService.deleteRelation(tenantId, relation); } } @@ -242,9 +241,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); if (node.getType().equals(TB_RULE_CHAIN_INPUT_NODE)) { - if (node.getConfiguration().has("ruleChainId")) { - RuleChainId targetRuleChainId = extractRuleChainIdFromInputNode(node); - var relation = createRuleChainInputRelation(ruleChainId, targetRuleChainId); + EntityRelation relation = getRuleChainInputRelation(ruleChainId, node); + if (relation != null) { relations.add(relation); } } @@ -262,7 +260,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC firstRuleNodeId = nodes.get(ruleChainMetaData.getFirstNodeIndex()).getId(); } if ((ruleChain.getFirstRuleNodeId() != null && !ruleChain.getFirstRuleNodeId().equals(firstRuleNodeId)) - || (ruleChain.getFirstRuleNodeId() == null && firstRuleNodeId != null)) { + || (ruleChain.getFirstRuleNodeId() == null && firstRuleNodeId != null)) { ruleChain.setFirstRuleNodeId(firstRuleNodeId); } if (ruleChainMetaData.getConnections() != null) { @@ -317,19 +315,20 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC return RuleChainUpdateResult.successful(updatedRuleNodes); } - private EntityRelation createRuleChainInputRelation(RuleChainId ruleChainId, RuleChainId targetRuleChainId) { - EntityRelation relation = new EntityRelation(); - relation.setFrom(ruleChainId); - relation.setTo(targetRuleChainId); - relation.setType(EntityRelation.USES_TYPE); - relation.setTypeGroup(RelationTypeGroup.COMMON); - return relation; - } - - private RuleChainId extractRuleChainIdFromInputNode(RuleNode node) { - JsonNode configuration = node.getConfiguration(); - UUID targetUuid = UUID.fromString(configuration.get("ruleChainId").asText()); - return new RuleChainId(targetUuid); + private EntityRelation getRuleChainInputRelation(RuleChainId ruleChainId, RuleNode inputNode) { + RuleChainId targetRuleChainId = Optional.ofNullable(inputNode.getConfiguration().get("ruleChainId")) + .filter(JsonNode::isTextual).map(JsonNode::asText).map(id -> new RuleChainId(UUID.fromString(id))) + .orElse(null); + if (targetRuleChainId != null) { + EntityRelation relation = new EntityRelation(); + relation.setFrom(ruleChainId); + relation.setTo(targetRuleChainId); + relation.setType(EntityRelation.USES_TYPE); + relation.setTypeGroup(RelationTypeGroup.COMMON); + return relation; + } else { + return null; + } } @Override diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java index a2ca663619..11fa38dc19 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.dao.service; import com.datastax.oss.driver.api.core.uuid.Uuids; -import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Test; import org.junit.jupiter.api.Assertions; @@ -46,6 +45,7 @@ import java.util.List; import java.util.UUID; import java.util.function.Function; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.thingsboard.server.common.data.relation.EntityRelation.USES_TYPE; import static org.thingsboard.server.dao.rule.BaseRuleChainService.TB_RULE_CHAIN_INPUT_NODE; @@ -277,7 +277,7 @@ public class RuleChainServiceTest extends AbstractServiceTest { List ruleNodes = savedRuleChainMetaData.getNodes(); int name3Index = -1; - for (int i=0;i ruleNodes = new ArrayList<>(); - ruleNodes.add(ruleNode); + ruleNodes.add(toRuleChain1Node); + ruleNodes.add(toRuleChain1Node2); ruleChainMetaData.setFirstNodeIndex(0); ruleChainMetaData.setNodes(ruleNodes); ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()); - List relations = relationService.findByFromAndType(tenantId, savedFromRuleChain.getId(), USES_TYPE, RelationTypeGroup.COMMON); - Assert.assertEquals(1, relations.size()); - EntityRelation usesRelation = relations.get(0); - Assert.assertEquals(savedFromRuleChain.getId(), usesRelation.getFrom()); - Assert.assertEquals(savedToRuleChain.getId(), usesRelation.getTo()); + List relations = relationService.findByFromAndType(tenantId, fromRuleChainId, USES_TYPE, RelationTypeGroup.COMMON); + assertThat(relations).singleElement().satisfies(relationToRuleChain1 -> { + assertThat(relationToRuleChain1.getFrom()).isEqualTo(fromRuleChainId); + assertThat(relationToRuleChain1.getTo()).isEqualTo(toRuleChain1Id); + }); - RuleChain newToRuleChain = new RuleChain(); - newToRuleChain.setName("New To Rule Chain"); - newToRuleChain.setTenantId(tenantId); - RuleChain savedNewToRuleChain = ruleChainService.saveRuleChain(newToRuleChain); + RuleChain toRuleChain2 = new RuleChain(); + toRuleChain2.setName("To Rule Chain 2"); + toRuleChain2.setTenantId(tenantId); + toRuleChain2 = ruleChainService.saveRuleChain(toRuleChain2); + RuleChainId toRuleChain2Id = toRuleChain2.getId(); - RuleNode newRuleNode = new RuleNode(); - newRuleNode.setName("Input node"); - newRuleNode.setType(TB_RULE_CHAIN_INPUT_NODE); - ObjectNode newConfiguration = JacksonUtil.newObjectNode(); - configuration.put("ruleChainId", savedNewToRuleChain.getId().toString()); - newRuleNode.setConfiguration(newConfiguration); + RuleNode toRuleChain2Node = new RuleNode(); + toRuleChain2Node.setName("To Rule Chain 2"); + toRuleChain2Node.setType(TB_RULE_CHAIN_INPUT_NODE); + toRuleChain2Node.setConfiguration(JacksonUtil.newObjectNode() + .put("ruleChainId", toRuleChain2Id.toString())); List newRuleNodes = new ArrayList<>(); - newRuleNodes.add(newRuleNode); - RuleChainMetaData foundRuleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()); - foundRuleChainMetaData.setNodes(newRuleNodes); + newRuleNodes.add(toRuleChain2Node); + newRuleNodes.add(toRuleChain1Node); + ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()); + ruleChainMetaData.setNodes(newRuleNodes); ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()); - List newRelations = relationService.findByFromAndType(tenantId, savedFromRuleChain.getId(), USES_TYPE, RelationTypeGroup.COMMON); - Assert.assertEquals(1, relations.size()); - EntityRelation newUsesRelation = newRelations.get(0); - Assert.assertEquals(savedFromRuleChain.getId(), newUsesRelation.getFrom()); - Assert.assertEquals(savedNewToRuleChain.getId(), newUsesRelation.getTo()); + List newRelations = relationService.findByFromAndType(tenantId, fromRuleChainId, USES_TYPE, RelationTypeGroup.COMMON); + assertThat(newRelations).hasSize(2); + assertThat(newRelations).anySatisfy(relationToRuleChain1 -> { + assertThat(relationToRuleChain1.getFrom()).isEqualTo(fromRuleChainId); + assertThat(relationToRuleChain1.getTo()).isEqualTo(toRuleChain1Id); + }); + assertThat(newRelations).anySatisfy(relationToRuleChain2 -> { + assertThat(relationToRuleChain2.getFrom()).isEqualTo(fromRuleChainId); + assertThat(relationToRuleChain2.getTo()).isEqualTo(toRuleChain2Id); + }); } private RuleChainId saveRuleChainAndSetAutoAssignToEdge(String name) { @@ -462,9 +476,9 @@ public class RuleChainServiceTest extends AbstractServiceTest { ruleChainMetaData.setFirstNodeIndex(0); ruleChainMetaData.setNodes(ruleNodes); - ruleChainMetaData.addConnectionInfo(0,1,"success"); - ruleChainMetaData.addConnectionInfo(0,2,"fail"); - ruleChainMetaData.addConnectionInfo(1,2,"success"); + ruleChainMetaData.addConnectionInfo(0, 1, "success"); + ruleChainMetaData.addConnectionInfo(0, 2, "fail"); + ruleChainMetaData.addConnectionInfo(1, 2, "success"); Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()).isSuccess()); return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()); @@ -501,10 +515,10 @@ public class RuleChainServiceTest extends AbstractServiceTest { ruleChainMetaData.setFirstNodeIndex(0); ruleChainMetaData.setNodes(ruleNodes); - ruleChainMetaData.addConnectionInfo(0,1,"success"); - ruleChainMetaData.addConnectionInfo(0,2,"fail"); - ruleChainMetaData.addConnectionInfo(1,2,"success"); - ruleChainMetaData.addConnectionInfo(2,2,"success"); + ruleChainMetaData.addConnectionInfo(0, 1, "success"); + ruleChainMetaData.addConnectionInfo(0, 2, "fail"); + ruleChainMetaData.addConnectionInfo(1, 2, "success"); + ruleChainMetaData.addConnectionInfo(2, 2, "success"); return ruleChainMetaData; } @@ -540,10 +554,10 @@ public class RuleChainServiceTest extends AbstractServiceTest { ruleChainMetaData.setFirstNodeIndex(0); ruleChainMetaData.setNodes(ruleNodes); - ruleChainMetaData.addConnectionInfo(0,1,"success"); - ruleChainMetaData.addConnectionInfo(0,2,"fail"); - ruleChainMetaData.addConnectionInfo(1,2,"success"); - ruleChainMetaData.addConnectionInfo(2,0,"success"); + ruleChainMetaData.addConnectionInfo(0, 1, "success"); + ruleChainMetaData.addConnectionInfo(0, 2, "fail"); + ruleChainMetaData.addConnectionInfo(1, 2, "success"); + ruleChainMetaData.addConnectionInfo(2, 0, "success"); return ruleChainMetaData; } @@ -649,16 +663,16 @@ public class RuleChainServiceTest extends AbstractServiceTest { private RuleChain getRuleChain() { String ruleChainStr = "{\n" + - " \"name\": \"Root Rule Chain\",\n" + - " \"type\": \"CORE\",\n" + - " \"firstRuleNodeId\": {\n" + - " \"entityType\": \"RULE_NODE\",\n" + - " \"id\": \"91ad0b00-e779-11ee-9cf0-15d8b6079fdb\"\n" + - " },\n" + - " \"debugMode\": false,\n" + - " \"configuration\": null,\n" + - " \"additionalInfo\": null\n" + - "}"; + " \"name\": \"Root Rule Chain\",\n" + + " \"type\": \"CORE\",\n" + + " \"firstRuleNodeId\": {\n" + + " \"entityType\": \"RULE_NODE\",\n" + + " \"id\": \"91ad0b00-e779-11ee-9cf0-15d8b6079fdb\"\n" + + " },\n" + + " \"debugMode\": false,\n" + + " \"configuration\": null,\n" + + " \"additionalInfo\": null\n" + + "}"; return JacksonUtil.fromString(ruleChainStr, RuleChain.class); } } From 1d83a2c9d387e4b629adbc1b8156f62f0dc6b171 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 7 May 2025 13:01:09 +0300 Subject: [PATCH 139/335] Refactoring for TbMsgProto usage --- ...riginatorIdTbRuleEngineSubmitStrategy.java | 8 +--- ...TbRuleEngineProcessingStrategyFactory.java | 8 ++-- .../TbRuleEngineQueueConsumerManager.java | 15 +++---- .../thingsboard/server/common/msg/TbMsg.java | 45 +++++++++---------- common/message/src/main/proto/tbmsg.proto | 4 +- .../server/common/util/ProtoUtils.java | 22 ++++++++- common/proto/src/main/proto/queue.proto | 2 +- 7 files changed, 55 insertions(+), 49 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByOriginatorIdTbRuleEngineSubmitStrategy.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByOriginatorIdTbRuleEngineSubmitStrategy.java index aec9252356..cec2aa19da 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByOriginatorIdTbRuleEngineSubmitStrategy.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/SequentialByOriginatorIdTbRuleEngineSubmitStrategy.java @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.msg.gen.MsgProtos; +import org.thingsboard.server.common.util.ProtoUtils; import org.thingsboard.server.gen.transport.TransportProtos; import java.util.UUID; @@ -34,12 +35,7 @@ public class SequentialByOriginatorIdTbRuleEngineSubmitStrategy extends Sequenti @Override protected EntityId getEntityId(TransportProtos.ToRuleEngineMsg msg) { try { - MsgProtos.TbMsgProto proto; - if (msg.getTbMsg().isEmpty()) { - proto = msg.getTbMsgProto(); - } else { - proto = MsgProtos.TbMsgProto.parseFrom(msg.getTbMsg()); - } + MsgProtos.TbMsgProto proto = ProtoUtils.getTbMsgProto(msg); return EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB())); } catch (InvalidProtocolBufferException e) { log.warn("[{}] Failed to parse TbMsg: {}", queueName, msg); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java index 2adcd9b199..023b4d4cc7 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java @@ -19,8 +19,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.thingsboard.server.common.data.queue.ProcessingStrategy; -import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.queue.TbMsgCallback; +import org.thingsboard.server.common.util.ProtoUtils; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.common.TbProtoQueueMsg; @@ -125,7 +125,7 @@ public class TbRuleEngineProcessingStrategyFactory { } log.debug("[{}] Going to reprocess {} messages", queueName, toReprocess.size()); if (log.isTraceEnabled()) { - toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, TbMsg.fromProto(result.getQueueName(), msg.getValue().getTbMsgProto(), msg.getValue().getTbMsg(), TbMsgCallback.EMPTY))); + toReprocess.forEach((id, msg) -> log.trace("Going to reprocess [{}]: {}", id, ProtoUtils.fromTbMsgProto(result.getQueueName(), msg.getValue(), TbMsgCallback.EMPTY))); } if (pauseBetweenRetries > 0) { try { @@ -164,10 +164,10 @@ public class TbRuleEngineProcessingStrategyFactory { log.debug("[{}] Reprocessing skipped for {} failed and {} timeout messages", queueName, result.getFailedMap().size(), result.getPendingMap().size()); } if (log.isTraceEnabled()) { - result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, TbMsg.fromProto(result.getQueueName(), msg.getValue().getTbMsgProto(), msg.getValue().getTbMsg(), TbMsgCallback.EMPTY))); + result.getFailedMap().forEach((id, msg) -> log.trace("Failed messages [{}]: {}", id, ProtoUtils.fromTbMsgProto(result.getQueueName(), msg.getValue(), TbMsgCallback.EMPTY))); } if (log.isTraceEnabled()) { - result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, TbMsg.fromProto(result.getQueueName(), msg.getValue().getTbMsgProto(), msg.getValue().getTbMsg(), TbMsgCallback.EMPTY))); + result.getPendingMap().forEach((id, msg) -> log.trace("Timeout messages [{}]: {}", id, ProtoUtils.fromTbMsgProto(result.getQueueName(), msg.getValue(), TbMsgCallback.EMPTY))); } return new TbRuleEngineProcessingDecision(true, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java index 01368f780e..3aa0d2eabd 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.msg.queue.RuleNodeInfo; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbMsgCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.common.util.ProtoUtils; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; @@ -178,7 +179,7 @@ public class TbRuleEngineQueueConsumerManager extends MainQueueConsumerManager relationTypes; @@ -207,7 +208,7 @@ public class TbRuleEngineQueueConsumerManager extends MainQueueConsumerManager> pending : map.entrySet()) { ToRuleEngineMsg tmp = pending.getValue().getValue(); - TbMsg tmpMsg = TbMsg.fromProto(config.getName(), tmp.getTbMsgProto(), tmp.getTbMsg(), TbMsgCallback.EMPTY); + TbMsg tmpMsg = ProtoUtils.fromTbMsgProto(config.getName(), tmp, TbMsgCallback.EMPTY); RuleNodeInfo ruleNodeInfo = ctx.getLastVisitedRuleNode(pending.getKey()); if (printAll) { log.trace("[{}][{}] {} to process message: {}, Last Rule Node: {}", queueKey, TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); @@ -236,13 +237,7 @@ public class TbRuleEngineQueueConsumerManager extends MainQueueConsumerManager msg : msgs) { try { - MsgProtos.TbMsgProto tbMsgProto; - if (msg.getValue().getTbMsg().isEmpty()) { - tbMsgProto = msg.getValue().getTbMsgProto(); - } else { - tbMsgProto = MsgProtos.TbMsgProto.parseFrom(msg.getValue().getTbMsg()); - } - + MsgProtos.TbMsgProto tbMsgProto = ProtoUtils.getTbMsgProto(msg.getValue()); EntityId originator = EntityIdFactory.getByTypeAndUuid(tbMsgProto.getEntityType(), new UUID(tbMsgProto.getEntityIdMSB(), tbMsgProto.getEntityIdLSB())); TopicPartitionInfo tpi = ctx.getPartitionService().resolve(ServiceType.TB_RULE_ENGINE, config.getName(), TenantId.SYS_TENANT_ID, originator); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index b7447b14ba..1d8d9497a9 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.gen.MsgProtos; +import org.thingsboard.server.common.msg.gen.MsgProtos.TbMsgProto; import org.thingsboard.server.common.msg.queue.TbMsgCallback; import java.io.Serializable; @@ -151,8 +152,8 @@ public final class TbMsg implements Serializable { this.callback = Objects.requireNonNullElse(callback, TbMsgCallback.EMPTY); } - public static MsgProtos.TbMsgProto toProto(TbMsg msg) { - MsgProtos.TbMsgProto.Builder builder = MsgProtos.TbMsgProto.newBuilder(); + public static TbMsgProto toProto(TbMsg msg) { + TbMsgProto.Builder builder = TbMsgProto.newBuilder(); builder.setId(msg.getId().toString()); builder.setTs(msg.getTs()); builder.setType(msg.getType()); @@ -204,12 +205,11 @@ public final class TbMsg implements Serializable { return builder.build(); } - //TODO: added for processing old messages from queue, should be removed after release - @Deprecated(forRemoval = true) - public static TbMsg fromProto(String queueName, MsgProtos.TbMsgProto proto, ByteString data, TbMsgCallback callback) { + @Deprecated(forRemoval = true, since = "4.1") // to be removed in 4.2 + public static TbMsg fromProto(String queueName, TbMsgProto proto, ByteString data, TbMsgCallback callback) { try { if (!data.isEmpty()) { - proto = MsgProtos.TbMsgProto.parseFrom(data); + proto = TbMsgProto.parseFrom(data); } } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Could not parse protobuf for TbMsg", e); @@ -217,7 +217,7 @@ public final class TbMsg implements Serializable { return fromProto(queueName, proto, callback); } - public static TbMsg fromProto(String queueName, MsgProtos.TbMsgProto proto, TbMsgCallback callback) { + public static TbMsg fromProto(String queueName, TbMsgProto proto, TbMsgCallback callback) { TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); EntityId entityId = EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB())); CustomerId customerId = null; @@ -225,7 +225,8 @@ public final class TbMsg implements Serializable { RuleNodeId ruleNodeId = null; UUID correlationId = null; Integer partition = null; - List calculatedFieldIds = new CopyOnWriteArrayList<>();if (proto.getCustomerIdMSB() != 0L && proto.getCustomerIdLSB() != 0L) { + List calculatedFieldIds = new CopyOnWriteArrayList<>(); + if (proto.getCustomerIdMSB() != 0L && proto.getCustomerIdLSB() != 0L) { customerId = new CustomerId(new UUID(proto.getCustomerIdMSB(), proto.getCustomerIdLSB())); } if (proto.getRuleChainIdMSB() != 0L && proto.getRuleChainIdLSB() != 0L) { @@ -240,19 +241,13 @@ public final class TbMsg implements Serializable { } for (MsgProtos.CalculatedFieldIdProto cfIdProto : proto.getCalculatedFieldsList()) { - CalculatedFieldId calculatedFieldId = new CalculatedFieldId(new UUID( - cfIdProto.getCalculatedFieldIdMSB(), - cfIdProto.getCalculatedFieldIdLSB() - )); - calculatedFieldIds.add(calculatedFieldId); - }TbMsgProcessingCtx ctx; - if (proto.hasCtx()) { - ctx = TbMsgProcessingCtx.fromProto(proto.getCtx()); - } else { - // Backward compatibility with unprocessed messages fetched from queue after update. - ctx = new TbMsgProcessingCtx(proto.getRuleNodeExecCounter()); + CalculatedFieldId calculatedFieldId = new CalculatedFieldId(new UUID( + cfIdProto.getCalculatedFieldIdMSB(), + cfIdProto.getCalculatedFieldIdLSB() + )); + calculatedFieldIds.add(calculatedFieldId); } - + TbMsgProcessingCtx ctx = TbMsgProcessingCtx.fromProto(proto.getCtx()); TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), null, proto.getType(), entityId, customerId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, correlationId, partition, calculatedFieldIds, ctx, callback); @@ -505,11 +500,11 @@ public final class TbMsg implements Serializable { public String toString() { return "TbMsg.TbMsgBuilder(queueName=" + this.queueName + ", id=" + this.id + ", ts=" + this.ts + - ", type=" + this.type + ", internalType=" + this.internalType + ", originator=" + this.originator + - ", customerId=" + this.customerId + ", metaData=" + this.metaData + ", dataType=" + this.dataType + - ", data=" + this.data + ", ruleChainId=" + this.ruleChainId + ", ruleNodeId=" + this.ruleNodeId + - ", correlationId=" + this.correlationId + ", partition=" + this.partition + ", previousCalculatedFields=" + this.previousCalculatedFieldIds + - ", ctx=" + this.ctx + ", callback=" + this.callback + ")"; + ", type=" + this.type + ", internalType=" + this.internalType + ", originator=" + this.originator + + ", customerId=" + this.customerId + ", metaData=" + this.metaData + ", dataType=" + this.dataType + + ", data=" + this.data + ", ruleChainId=" + this.ruleChainId + ", ruleNodeId=" + this.ruleNodeId + + ", correlationId=" + this.correlationId + ", partition=" + this.partition + ", previousCalculatedFields=" + this.previousCalculatedFieldIds + + ", ctx=" + this.ctx + ", callback=" + this.callback + ")"; } } diff --git a/common/message/src/main/proto/tbmsg.proto b/common/message/src/main/proto/tbmsg.proto index 65a967e9e4..e70104d503 100644 --- a/common/message/src/main/proto/tbmsg.proto +++ b/common/message/src/main/proto/tbmsg.proto @@ -59,8 +59,8 @@ message TbMsgProto { string data = 14; int64 ts = 15; - // Will be removed in 3.4. Moved to processing context - int32 ruleNodeExecCounter = 16; + + // ruleNodeExecCounter (16) was removed in 4.1 int64 customerIdMSB = 17; int64 customerIdLSB = 18; diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java index 1ebd753f3c..502884eb31 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java @@ -18,6 +18,8 @@ package org.thingsboard.server.common.util; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.ApiUsageRecordKey; @@ -76,12 +78,15 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.sync.vc.RepositoryAuthMethod; import org.thingsboard.server.common.data.sync.vc.RepositorySettings; +import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg; import org.thingsboard.server.common.msg.edge.EdgeHighPriorityMsg; import org.thingsboard.server.common.msg.edge.FromEdgeSyncResponse; import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest; +import org.thingsboard.server.common.msg.gen.MsgProtos; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.queue.TbMsgCallback; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; @@ -93,8 +98,8 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceDeleteMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; import org.thingsboard.server.gen.transport.TransportProtos.ApiUsageRecordKeyProto; +import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; import java.util.ArrayList; import java.util.Arrays; @@ -1344,6 +1349,21 @@ public class ProtoUtils { return builder.build(); } + @Deprecated(forRemoval = true, since = "4.1") + public static MsgProtos.TbMsgProto getTbMsgProto(TransportProtos.ToRuleEngineMsg ruleEngineMsg) throws InvalidProtocolBufferException { + if (ruleEngineMsg.getTbMsg().isEmpty()) { + return ruleEngineMsg.getTbMsgProto(); + } else { + return MsgProtos.TbMsgProto.parseFrom(ruleEngineMsg.getTbMsg()); + } + } + + @SneakyThrows + @Deprecated(forRemoval = true, since = "4.1") // inline to TbMsg.fromProto(queueName, ruleEngineMsg.getTbMsgProto(), callback) + public static TbMsg fromTbMsgProto(String queueName, TransportProtos.ToRuleEngineMsg ruleEngineMsg, TbMsgCallback callback) { + return TbMsg.fromProto(queueName, getTbMsgProto(ruleEngineMsg), callback); + } + private static boolean isNotNull(Object obj) { return obj != null; } diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 449f662a93..2a97fd35d0 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -1703,7 +1703,7 @@ message ToCalculatedFieldNotificationMsg { message ToRuleEngineMsg { int64 tenantIdMSB = 1; int64 tenantIdLSB = 2; - bytes tbMsg = 3 [deprecated = true]; + bytes tbMsg = 3 [deprecated = true]; // for removal in 4.2 repeated string relationTypes = 4; string failureMessage = 5; msgqueue.TbMsgProto tbMsgProto = 6; From a445364ce370d7336c01777b8979b7a7ac748d3b Mon Sep 17 00:00:00 2001 From: deaflynx Date: Wed, 7 May 2025 13:23:47 +0300 Subject: [PATCH 140/335] UI: mqtt node add protocol version. --- .../external/mqtt-config.component.html | 10 ++++++++++ .../rule-node/external/mqtt-config.component.ts | 5 +++++ ui-ngx/src/app/shared/models/device.models.ts | 16 +++++++++++++++- .../src/assets/locale/locale.constant-en_US.json | 1 + 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html index 090fea41f3..1fb41b4548 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.html @@ -72,6 +72,16 @@ {{ 'rule-node-config.parse-to-plain-text' | translate }}
{{ "rule-node-config.parse-to-plain-text-hint" | translate }}
+ + device-profile.mqtt-protocol-version + + @for (version of mqttVersions; track version) { + + {{ mqttVersionTranslation.get(version) }} + + } + + {{ 'rule-node-config.clean-session' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.ts index 00a95e5ce6..3efeb3d61e 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/mqtt-config.component.ts @@ -18,6 +18,7 @@ import { Component } from '@angular/core'; import { isNotEmptyStr } from '@core/public-api'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; +import { MqttVersions, MqttVersionTranslation } from '@shared/models/device.models'; @Component({ selector: 'tb-external-node-mqtt-config', @@ -28,6 +29,9 @@ export class MqttConfigComponent extends RuleNodeConfigurationComponent { mqttConfigForm: UntypedFormGroup; + mqttVersions = MqttVersions; + mqttVersionTranslation = MqttVersionTranslation; + constructor(private fb: UntypedFormBuilder) { super(); } @@ -52,6 +56,7 @@ export class MqttConfigComponent extends RuleNodeConfigurationComponent { cleanSession: [configuration ? configuration.cleanSession : false, []], retainedMessage: [configuration ? configuration.retainedMessage : false, []], ssl: [configuration ? configuration.ssl : false, []], + protocolVersion: [configuration ? configuration.protocolVersion : null, []], credentials: [configuration ? configuration.credentials : null, []] }); } diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 8298d3a1fe..d0bbdd9b0f 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -36,7 +36,7 @@ import { PowerMode } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; import { PageLink } from '@shared/models/page/page-link'; -import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; +import { isDefinedAndNotNull, isNotEmptyStr, isNumber } from '@core/utils'; import { EdgeId } from '@shared/models/id/edge-id'; export enum DeviceProfileType { @@ -902,3 +902,17 @@ export const getAlarmScheduleRangeText = (startsOn: Date | number, endsOn: Date return `12:00 AM${end.format('hh:mm A')}` + ` and ${start.format('hh:mm A')}12:00 PM`; }; + +export enum MqttVersion { + MQTT_3_1 = 3, + MQTT_3_1_1 = 4, + MQTT_5 = 5 +} + +export const MqttVersions = Object.values(MqttVersion).filter(v => isNumber(v)) as MqttVersion[]; + +export const MqttVersionTranslation = new Map([ + [MqttVersion.MQTT_3_1, 'MQTT 3.1'], + [MqttVersion.MQTT_3_1_1, 'MQTT 3.1.1'], + [MqttVersion.MQTT_5, 'MQTT 5.0'] +]); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 72afcfc0b9..21f430c1ba 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1914,6 +1914,7 @@ "mqtt-use-json-format-for-default-downlink-topics-hint": "When enabled, the platform will use Json payload format to push attributes and RPC via the following topics: v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, v1/devices/me/rpc/request/$request_id, v1/devices/me/rpc/response/$request_id. This setting does not impact attribute and rpc subscriptions sent using new (v2) topics: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id. Where $request_id is an integer request identifier.", "mqtt-send-ack-on-validation-exception": "Send PUBACK on PUBLISH message validation failure", "mqtt-send-ack-on-validation-exception-hint": "By default, the platform will close the MQTT session on message validation failure. When enabled, the platform will send publish acknowledgment instead of closing the session.", + "mqtt-protocol-version": "Protocol version", "snmp-add-mapping": "Add SNMP mapping", "snmp-mapping-not-configured": "No mapping for OID to time series/telemetry configured", "snmp-timseries-or-attribute-name": "Time series/attribute name for mapping", From 9669bb1427be1596bf30739a659cbf372bdd8832 Mon Sep 17 00:00:00 2001 From: idealissst Date: Wed, 7 May 2025 17:24:55 +0300 Subject: [PATCH 141/335] Add Valkey reference in thingsboard.yml and transports .yml files -thingsboard.yml -tb-coap-transport.yml -tb-http-transport.yml -tb-lwm2m-transport.yml -tb-mqtt-transport.yml -tb-snmp-transport.yml --- application/src/main/resources/thingsboard.yml | 2 +- transport/coap/src/main/resources/tb-coap-transport.yml | 2 +- transport/http/src/main/resources/tb-http-transport.yml | 2 +- transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml | 2 +- transport/mqtt/src/main/resources/tb-mqtt-transport.yml | 2 +- transport/snmp/src/main/resources/tb-snmp-transport.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index d7dbc0d6ac..2909e7cb05 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -668,7 +668,7 @@ cache: # Spring data parameters spring.data.redis.repositories.enabled: false # Disable this because it is not required. -# Redis configuration parameters +# Redis/Valkey configuration parameters redis: # standalone or cluster or sentinel connection: diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 0c5d160625..c54bf038f2 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -54,7 +54,7 @@ cache: timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled -# Redis configuration parameters +# Redis/Valkey configuration parameters redis: connection: # standalone or cluster or sentinel diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index 81f4719e45..3a3725edef 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -86,7 +86,7 @@ cache: timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled -# Redis configuration parameters +# Redis/Valkey configuration parameters redis: # standalone or cluster or sentinel connection: diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 2c62a35dbb..60568a6a4e 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -54,7 +54,7 @@ cache: timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled -# Redis configuration parameters +# Redis/Valkey configuration parameters redis: connection: # standalone or cluster or sentinel diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 51f9a17005..55781abeeb 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -54,7 +54,7 @@ cache: timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled -# Redis configuration parameters +# Redis/Valkey configuration parameters redis: # standalone or cluster or sentinel connection: diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index f4811b3326..746e8f2173 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -54,7 +54,7 @@ cache: timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled -# Redis configuration parameters +# Redis/Valkey configuration parameters redis: connection: # standalone or cluster or sentinel From f5e816923d756f5faa780d106c2766b7653d1a84 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 8 May 2025 11:55:13 +0300 Subject: [PATCH 142/335] Tasks partitioning strategies; jobs filter; fixes --- .../server/controller/JobController.java | 14 +++++- .../controller/TelemetryController.java | 25 +++++------ .../server/service/job/DefaultJobManager.java | 20 ++++++--- .../src/main/resources/thingsboard.yml | 5 +++ .../server/controller/AbstractWebTest.java | 7 +++ .../server/service/job/JobManagerTest.java | 32 ++++++++------ ...anagerTest_EntityPartitioningStrategy.java | 43 +++++++++++++++++++ .../server/dao/job/JobService.java | 3 +- .../server/common/data/id/JobId.java | 2 +- .../common/data/job/JobConfiguration.java | 2 + .../server/common/data/job/JobFilter.java | 30 +++++++++++++ .../server/common/data/job/JobResult.java | 3 +- .../common/data/job/task/DummyTask.java | 8 ++++ .../server/common/data/job/task/Task.java | 6 +++ .../common/data/job/task/TaskResult.java | 2 + common/proto/src/main/proto/queue.proto | 4 +- .../queue/discovery/HashPartitionService.java | 21 ++++++--- .../server/queue/task/TaskProcessor.java | 2 +- .../server/dao/job/DefaultJobService.java | 5 ++- .../thingsboard/server/dao/job/JobDao.java | 3 +- .../server/dao/sql/job/JobRepository.java | 28 ++++++------ .../server/dao/sql/job/JpaJobDao.java | 12 ++++-- .../resources/sql/schema-entities-idx.sql | 2 + 23 files changed, 211 insertions(+), 68 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/job/JobFilter.java diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java index 9b6627e12a..1db84593f5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/JobController.java +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -28,12 +28,16 @@ import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobFilter; +import org.thingsboard.server.common.data.job.JobStatus; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.job.JobManager; +import java.util.List; import java.util.UUID; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; @@ -69,10 +73,16 @@ public class JobController extends BaseController { @Parameter(description = SORT_PROPERTY_DESCRIPTION) @RequestParam(required = false) String sortProperty, @Parameter(description = SORT_ORDER_DESCRIPTION) - @RequestParam(required = false) String sortOrder) throws ThingsboardException { + @RequestParam(required = false) String sortOrder, + @RequestParam(required = false) List types, + @RequestParam(required = false) List statuses) throws ThingsboardException { // todo check permissions PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return jobService.findJobsByTenantId(getTenantId(), pageLink); + JobFilter filter = JobFilter.builder() + .types(types) + .statuses(statuses) + .build(); + return jobService.findJobsByFilter(getTenantId(), filter, pageLink); } @PostMapping("/job/{id}/cancel") diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index d93e973073..bf9713f58e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -64,11 +64,9 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.kv.Aggregation; -import org.thingsboard.server.common.data.kv.AggregationParams; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery; -import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DataType; @@ -78,7 +76,6 @@ import org.thingsboard.server.common.data.kv.IntervalType; import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; @@ -317,10 +314,10 @@ public class TelemetryController extends BaseController { @Parameter(description = "A string value representing the timezone that will be used to calculate exact timestamps for 'WEEK', 'WEEK_ISO', 'MONTH' and 'QUARTER' interval types.") @RequestParam(name = "timeZone", required = false) String timeZone, @Parameter(description = "An integer value that represents a max number of time series data points to fetch." + - " This parameter is used only in the case if 'agg' parameter is set to 'NONE'.", schema = @Schema(defaultValue = "100")) + " This parameter is used only in the case if 'agg' parameter is set to 'NONE'.", schema = @Schema(defaultValue = "100")) @RequestParam(name = "limit", defaultValue = "100") Integer limit, @Parameter(description = "A string value representing the aggregation function. " + - "If the interval is not specified, 'agg' parameter will use 'NONE' value.", + "If the interval is not specified, 'agg' parameter will use 'NONE' value.", schema = @Schema(allowableValues = {"MIN", "MAX", "AVG", "SUM", "COUNT", "NONE"})) @RequestParam(name = "agg", defaultValue = "NONE") String aggStr, @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) @@ -340,12 +337,12 @@ public class TelemetryController extends BaseController { + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK + - "Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED', " + - "and also sends event msg to the rule engine with msg type 'ATTRIBUTES_UPDATED'."), + "Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED', " + + "and also sends event msg to the rule engine with msg type 'ATTRIBUTES_UPDATED'."), @ApiResponse(responseCode = "400", description = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST), @ApiResponse(responseCode = "401", description = "User is not authorized to save device attributes for selected device. Most likely, User belongs to different Customer or Tenant."), @ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " + - "Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED' that includes an error stacktrace."), + "Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED' that includes an error stacktrace."), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.POST) @@ -463,11 +460,11 @@ public class TelemetryController extends BaseController { TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Time series for the selected keys in the request was removed. " + - "Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED'."), + "Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED'."), @ApiResponse(responseCode = "400", description = "Platform returns a bad request in case if keys list is empty or start and end timestamp values is empty when deleteAllDataForKeys is set to false."), @ApiResponse(responseCode = "401", description = "User is not authorized to delete entity time series for selected entity. Most likely, User belongs to different Customer or Tenant."), @ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " + - "Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED' that includes an error stacktrace."), + "Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED' that includes an error stacktrace."), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE) @@ -544,11 +541,11 @@ public class TelemetryController extends BaseController { "Referencing a non-existing Device Id will cause an error" + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Device attributes was removed for the selected keys in the request. " + - "Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED'."), + "Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED'."), @ApiResponse(responseCode = "400", description = "Platform returns a bad request in case if keys or scope are not specified."), @ApiResponse(responseCode = "401", description = "User is not authorized to delete device attributes for selected entity. Most likely, User belongs to different Customer or Tenant."), @ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " + - "Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."), + "Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE) @@ -566,11 +563,11 @@ public class TelemetryController extends BaseController { INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Entity attributes was removed for the selected keys in the request. " + - "Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED'."), + "Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED'."), @ApiResponse(responseCode = "400", description = "Platform returns a bad request in case if keys or scope are not specified."), @ApiResponse(responseCode = "401", description = "User is not authorized to delete entity attributes for selected entity. Most likely, User belongs to different Customer or Tenant."), @ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " + - "Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."), + "Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.DELETE) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 79312ba608..8c32de4f45 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -24,6 +24,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.rule.engine.api.NotificationCenter; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; @@ -79,6 +80,8 @@ public class DefaultJobManager implements JobManager { private final ExecutorService executor; private final ExecutorService consumerExecutor; + @Value("${queue.tasks.partitioning_strategy:tenant}") + private String tasksPartitioningStrategy; @Value("${queue.tasks.stats.processing_interval_ms:1000}") private int statsProcessingInterval; @@ -148,7 +151,7 @@ public class DefaultJobManager implements JobManager { JobProcessor processor = getJobProcessor(job.getType()); List toReprocess = job.getConfiguration().getToReprocess(); if (toReprocess == null) { - int tasksCount = processor.process(job, this::submitTask); // todo: think about stopping tb - while tasks are being submitted + int tasksCount = processor.process(job, this::submitTask); log.info("[{}][{}][{}] Submitted {} tasks", tenantId, jobId, job.getType(), tasksCount); jobStatsService.reportAllTasksSubmitted(tenantId, jobId, tasksCount); } else { @@ -197,17 +200,24 @@ public class DefaultJobManager implements JobManager { } private void submitTask(Task task) { - log.info("[{}][{}] Submitting task: {}", task.getTenantId(), task.getJobId(), task); + log.debug("[{}][{}] Submitting task: {}", task.getTenantId(), task.getJobId(), task); TaskProto taskProto = TaskProto.newBuilder() .setValue(JacksonUtil.toString(task)) .build(); TbQueueProducer> producer = taskProducers.get(task.getJobType()); - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TASK_PROCESSOR, task.getJobType().name(), task.getTenantId(), task.getTenantId()); // one job at a time for a given tenant + EntityId entityId = null; + if (tasksPartitioningStrategy.equals("entity")) { + entityId = task.getEntityId(); + } + if (entityId == null) { + entityId = task.getTenantId(); + } + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TASK_PROCESSOR, task.getJobType().name(), task.getTenantId(), entityId); producer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), taskProto), new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { - log.trace("Submitted task: {}", task); + log.trace("Submitted task to {}: {}", tpi, taskProto); } @Override @@ -247,7 +257,7 @@ public class DefaultJobManager implements JobManager { }); consumer.commit(); - Thread.sleep(statsProcessingInterval); // todo: test with bigger interval + Thread.sleep(statsProcessingInterval); } private void sendJobFinishedNotification(Job job) { diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 54db919bbf..7cae5db294 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1900,6 +1900,11 @@ queue: tasks: # Partitions count for tasks queues partitions: "${TB_QUEUE_TASKS_PARTITIONS:12}" + # Custom partitions count for tasks queues per type. Format: 'TYPE1:24;TYPE2:36', e.g. 'CF_REPROCESSING:24;TENANT_EXPORT:6' + partitions_per_type: "${TB_QUEUE_TASKS_PARTITIONS_PER_TYPE:}" + # Tasks partitioning strategy: 'tenant' or 'entity'. By default, using 'tenant' - tasks of a specific tenant are processed in the same partition. + # In a single-tenant environment, use 'entity' strategy to distribute the tasks among multiple partitions. + partitioning_strategy: "${TB_QUEUE_TASKS_PARTITIONING_STRATEGY:tenant}" stats: # Interval in milliseconds to process job stats processing_interval_ms: "${TB_QUEUE_TASKS_STATS_PROCESSING_INTERVAL_MS:1000}" diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 054c2fbe59..ef658fc17a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -109,6 +109,7 @@ import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationType; @@ -168,6 +169,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -1270,6 +1272,11 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return doGetTypedWithPageLink("/api/jobs?", new TypeReference>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); } + protected List findJobs(JobType... types) throws Exception { + return doGetTypedWithPageLink("/api/jobs?types=" + Arrays.stream(types).map(Enum::name).collect(Collectors.joining(",")) + "&", + new TypeReference>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); + } + protected void cancelJob(JobId jobId) throws Exception { doPost("/api/job/" + jobId + "/cancel").andExpect(status().isOk()); } diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 15037470d1..206acc450d 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.job; -import org.assertj.core.api.Assertions; import org.assertj.core.api.ThrowingConsumer; import org.junit.After; import org.junit.Before; @@ -34,13 +33,12 @@ import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.DummyTaskResult; import org.thingsboard.server.common.data.job.task.DummyTaskResult.DummyTaskFailure; import org.thingsboard.server.common.data.notification.Notification; -import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.controller.AbstractControllerTest; -import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.queue.task.JobStatsService; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -61,9 +59,6 @@ public class JobManagerTest extends AbstractControllerTest { @Autowired private JobManager jobManager; - @Autowired - private JobService jobService; - @SpyBean private TestTaskProcessor taskProcessor; @@ -138,8 +133,9 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(jobResult.getSuccessfulCount()).isEqualTo(successfulTasks); assertThat(jobResult.getFailedCount()).isEqualTo(failedTasks); assertThat(jobResult.getTotalCount()).isEqualTo(successfulTasks + failedTasks); - assertThat(((DummyTaskResult) jobResult.getResults().get(0)).getFailure().getError()).isEqualTo("error3"); // last error - assertThat(((DummyTaskResult) jobResult.getResults().get(1)).getFailure().getError()).isEqualTo("error3"); // last error + assertThat(getFailures(jobResult)).hasSize(2).allSatisfy(failure -> { + assertThat(failure.getError()).isEqualTo("error3"); // last error + }); assertThat(jobResult.getCompletedCount()).isEqualTo(jobResult.getTotalCount()); }); @@ -254,7 +250,7 @@ public class JobManagerTest extends AbstractControllerTest { Thread.sleep(3000); verify(jobStatsService, never()).reportTaskResult(any(), any(), any()); - Assertions.assertThat(jobService.findJobsByTenantId(tenantId, new PageLink(100, 0)).getData()).isEmpty(); + assertThat(findJobs()).isEmpty(); } @Test @@ -276,7 +272,7 @@ public class JobManagerTest extends AbstractControllerTest { } await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { - List jobs = findJobs(); + List jobs = findJobs(JobType.DUMMY); assertThat(jobs).hasSize(jobsCount); Job firstJob = jobs.get(2); // ordered by createdTime descending assertThat(firstJob.getStatus()).isEqualTo(JobStatus.RUNNING); @@ -391,8 +387,9 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(jobResult.getSuccessfulCount()).isEqualTo(successfulTasks); assertThat(jobResult.getFailedCount()).isEqualTo(failedTasks); + List failures = getFailures(jobResult); for (int i = 0, taskNumber = successfulTasks + 1; taskNumber <= totalTasksCount; i++, taskNumber++) { - DummyTaskFailure failure = ((DummyTaskResult) jobResult.getResults().get(i)).getFailure(); + DummyTaskFailure failure = failures.get(i); assertThat(failure.getNumber()).isEqualTo(taskNumber); assertThat(failure.getError()).isEqualTo("error"); } @@ -438,8 +435,9 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(jobResult.getFailedCount()).isEqualTo(failedTasks + permanentlyFailedTasks); assertThat(jobResult.getTotalCount()).isEqualTo(totalTasksCount); + List failures = getFailures(jobResult); for (int i = 0, taskNumber = successfulTasks + 1; taskNumber <= totalTasksCount; i++, taskNumber++) { - DummyTaskFailure failure = ((DummyTaskResult) jobResult.getResults().get(i)).getFailure(); + DummyTaskFailure failure = failures.get(i); assertThat(failure.getNumber()).isEqualTo(taskNumber); assertThat(failure.getError()).isEqualTo("error"); } @@ -455,8 +453,9 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(jobResult.getFailedCount()).isEqualTo(permanentlyFailedTasks); assertThat(jobResult.getTotalCount()).isEqualTo(totalTasksCount); + List failures = getFailures(jobResult); for (int i = 0, taskNumber = successfulTasks + failedTasks + 1; taskNumber <= totalTasksCount; i++, taskNumber++) { - DummyTaskFailure failure = ((DummyTaskResult) jobResult.getResults().get(i)).getFailure(); + DummyTaskFailure failure = failures.get(i); assertThat(failure.getNumber()).isEqualTo(taskNumber); assertThat(failure.getError()).isEqualTo("error"); assertThat(failure.isFailAlways()).isTrue(); @@ -474,6 +473,11 @@ public class JobManagerTest extends AbstractControllerTest { }); } - // todo: job with zero tasks + private List getFailures(JobResult jobResult) { + return jobResult.getResults().stream() + .map(taskResult -> ((DummyTaskResult) taskResult).getFailure()) + .sorted(Comparator.comparingInt(DummyTaskFailure::getNumber)) + .toList(); + } } \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java new file mode 100644 index 0000000000..983f30d523 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job; + +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +@TestPropertySource(properties = { + "queue.tasks.stats.processing_interval_ms=0", + "queue.tasks.partitioning_strategy=entity", + "queue.tasks.partitions_per_type=DUMMY:100;DUMMY:50" +}) +public class JobManagerTest_EntityPartitioningStrategy extends JobManagerTest { + + /* + * Some tests are overridden because they are based on + * tenant partitioning strategy (subsequent tasks processing within a tenant) + * */ + + @Override + public void testCancelJob_simulateTaskProcessorRestart() throws Exception { + } + + @Override + public void testGeneralJobError() { + + } + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java index 3c00b2fe0e..3204044880 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.job; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobFilter; import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -35,7 +36,7 @@ public interface JobService extends EntityDaoService { void processStats(TenantId tenantId, JobId jobId, JobStats jobStats); - PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink); + PageData findJobsByFilter(TenantId tenantId, JobFilter filter, PageLink pageLink); Job findLatestJobByKey(TenantId tenantId, String key); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/JobId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/JobId.java index e6688f0eb0..76678b8b31 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/JobId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/JobId.java @@ -29,7 +29,7 @@ public class JobId extends UUIDBased implements EntityId { super(id); } - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "string", example = "TASK", allowableValues = "TASK") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "string", example = "JOB", allowableValues = "JOB") @Override public EntityType getEntityType() { return EntityType.JOB; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java index 8aed4adbe5..35f44cd4be 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.job; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; @@ -35,6 +36,7 @@ public abstract class JobConfiguration implements Serializable { private List toReprocess; + @JsonIgnore public abstract JobType getType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobFilter.java new file mode 100644 index 0000000000..6cc9a636e8 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobFilter.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.job; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class JobFilter { + + private final List types; + private final List statuses; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index 534e3587bb..3af076cd19 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -58,7 +58,7 @@ public abstract class JobResult implements Serializable { discardedCount++; } else { failedCount++; - if (results.size() < 1000) { // preserving only first 1000 errors, not reprocessing if there are more failures + if (results.size() < 100) { // preserving only first 100 errors, not reprocessing if there are more failures results.add(taskResult); } } @@ -67,6 +67,7 @@ public abstract class JobResult implements Serializable { @JsonIgnore public abstract String getDescription(); + @JsonIgnore public abstract JobType getJobType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java index d7a37b5175..7e262ed7b8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java @@ -20,9 +20,12 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import lombok.experimental.SuperBuilder; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.job.JobType; import java.util.List; +import java.util.UUID; @Data @NoArgsConstructor @@ -46,6 +49,11 @@ public class DummyTask extends Task { return DummyTaskResult.discarded(); } + @Override + public EntityId getEntityId() { + return new DeviceId(UUID.randomUUID()); + } + @Override public JobType getJobType() { return JobType.DUMMY; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java index 6bf287b54a..cce32fdad0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.job.task; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; @@ -22,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.experimental.SuperBuilder; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.JobType; @@ -49,6 +51,10 @@ public abstract class Task { public abstract R toDiscarded(); + @JsonIgnore + public abstract EntityId getEntityId(); + + @JsonIgnore public abstract JobType getJobType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java index 8c4667e1be..9416cb8f6b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.job.task; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; @@ -39,6 +40,7 @@ public abstract class TaskResult { private boolean success; private boolean discarded; + @JsonIgnore public abstract JobType getJobType(); } diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 1cc507c5b3..04c41a7f69 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -1854,7 +1854,7 @@ message EdqsResponseMsg { } message TaskProto { - string value = 1; // fixme: TMP, make more efficient + string value = 1; } message JobStatsMsg { @@ -1867,5 +1867,5 @@ message JobStatsMsg { } message TaskResultProto { - string value = 1; // fixme: TMP, make more efficient + string value = 1; } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index 3f81199488..d6580d5c83 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -40,12 +40,14 @@ import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent; import org.thingsboard.server.queue.util.AfterStartUp; +import org.thingsboard.server.queue.util.PropertyUtils; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -87,7 +89,9 @@ public class HashPartitionService implements PartitionService { @Value("${queue.edqs.partitions:12}") private Integer edqsPartitions; @Value("${queue.tasks.partitions:12}") - private Integer tasksPartitions; + private Integer defaultTasksPartitions; + @Value("${queue.tasks.partitions_per_type:}") + private String tasksPartitionsPerType; @Value("${queue.partitions.hash_function_name:murmur3_128}") private String hashFunctionName; @@ -135,11 +139,18 @@ public class HashPartitionService implements PartitionService { partitionSizesMap.put(edqsKey, edqsPartitions); partitionTopicsMap.put(edqsKey, "edqs"); // placeholder, not used - for (JobType jobType : JobType.values()) { - QueueKey queueKey = new QueueKey(ServiceType.TASK_PROCESSOR, jobType.name()); - partitionSizesMap.put(queueKey, tasksPartitions); - partitionTopicsMap.put(queueKey, jobType.getTasksTopic()); + Map tasksPartitions = new EnumMap<>(JobType.class); + PropertyUtils.getProps(tasksPartitionsPerType).forEach((type, partitions) -> { + tasksPartitions.put(JobType.valueOf(type), Integer.parseInt(partitions)); + }); + for (JobType type : JobType.values()) { + tasksPartitions.putIfAbsent(type, defaultTasksPartitions); } + tasksPartitions.forEach((type, partitions) -> { + QueueKey queueKey = new QueueKey(ServiceType.TASK_PROCESSOR, type.name()); + partitionSizesMap.put(queueKey, partitions); + partitionTopicsMap.put(queueKey, type.getTasksTopic()); + }); } @AfterStartUp(order = AfterStartUp.QUEUE_INFO_INITIALIZATION) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 38d3d02b50..fd4367781e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -63,7 +63,7 @@ public abstract class TaskProcessor, R extends TaskResult> { private QueueKey queueKey; private MainQueueConsumerManager, QueueConfig> taskConsumer; - private final ExecutorService taskExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-processor")); + private final ExecutorService taskExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-processor")); private final SetCache discardedJobs = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); private final SetCache deletedTenants = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index 73e0c5dff4..680bc81566 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobFilter; import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobStatus; @@ -174,8 +175,8 @@ public class DefaultJobService extends AbstractEntityService implements JobServi } @Override - public PageData findJobsByTenantId(TenantId tenantId, PageLink pageLink) { - return jobDao.findByTenantId(tenantId, pageLink); + public PageData findJobsByFilter(TenantId tenantId, JobFilter filter, PageLink pageLink) { + return jobDao.findByTenantIdAndFilter(tenantId, filter, pageLink); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java index afe182d8cd..0c70fd102d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.job; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobFilter; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.page.PageData; @@ -26,7 +27,7 @@ import org.thingsboard.server.dao.Dao; public interface JobDao extends Dao { - PageData findByTenantId(TenantId tenantId, PageLink pageLink); + PageData findByTenantIdAndFilter(TenantId tenantId, JobFilter filter, PageLink pageLink); Job findByIdForUpdate(TenantId tenantId, JobId jobId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java index 0ecd517f51..72d569c94b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java @@ -15,12 +15,9 @@ */ package org.thingsboard.server.dao.sql.job; -import jakarta.persistence.LockModeType; -import org.springframework.data.domain.Limit; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -37,28 +34,29 @@ import java.util.UUID; public interface JobRepository extends JpaRepository { @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId " + - "AND (:searchText IS NULL OR ilike(j.key, concat('%', :searchText, '%')) = true " + - "OR ilike(j.description, concat('%', :searchText, '%')) = true)") - Page findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, - @Param("searchText") String searchText, - Pageable pageable); + "AND (:types IS NULL OR j.type IN (:types)) AND (:statuses IS NULL OR j.status IN (:statuses)) " + + "AND (:searchText IS NULL OR ilike(j.key, concat('%', :searchText, '%')) = true " + + "OR ilike(j.description, concat('%', :searchText, '%')) = true)") + Page findByTenantIdAndTypesAndStatusesAndSearchText(@Param("tenantId") UUID tenantId, + @Param("types") List types, + @Param("statuses") List statuses, + @Param("searchText") String searchText, + Pageable pageable); - @Lock(LockModeType.PESSIMISTIC_WRITE) // SELECT FOR UPDATE - @Query("SELECT j FROM JobEntity j WHERE j.id = :id") + @Query(value = "SELECT * FROM job j WHERE j.id = :id FOR UPDATE", nativeQuery = true) JobEntity findByIdForUpdate(UUID id); @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId AND j.key = :key " + - "ORDER BY j.createdTime DESC") + "ORDER BY j.createdTime DESC") JobEntity findLatestByTenantIdAndKey(@Param("tenantId") UUID tenantId, @Param("key") String key); boolean existsByTenantIdAndKeyAndStatusIn(UUID tenantId, String key, List statuses); boolean existsByTenantIdAndTypeAndStatusIn(UUID tenantId, JobType type, List statuses); - @Lock(LockModeType.PESSIMISTIC_WRITE) // SELECT FOR UPDATE - @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId AND j.type = :type " + - "AND j.status = :status ORDER BY j.createdTime ASC, j.id ASC") - JobEntity findOldestByTenantIdAndTypeAndStatusForUpdate(UUID tenantId, JobType type, JobStatus status, Limit limit); + @Query(value = "SELECT * FROM job j WHERE j.tenant_id = :tenantId AND j.type = :type " + + "AND j.status = :status ORDER BY j.created_time ASC, j.id ASC LIMIT 1 FOR UPDATE", nativeQuery = true) + JobEntity findOldestByTenantIdAndTypeAndStatusForUpdate(UUID tenantId, String type, String status); @Transactional @Modifying diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java index 0e5ee4683e..f59898eb73 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java @@ -17,17 +17,18 @@ package org.thingsboard.server.dao.sql.job; import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Limit; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobFilter; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.job.JobDao; import org.thingsboard.server.dao.model.sql.JobEntity; @@ -45,8 +46,11 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao private final JobRepository jobRepository; @Override - public PageData findByTenantId(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData(jobRepository.findByTenantIdAndSearchText(tenantId.getId(), Strings.emptyToNull(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + public PageData findByTenantIdAndFilter(TenantId tenantId, JobFilter filter, PageLink pageLink) { + return DaoUtil.toPageData(jobRepository.findByTenantIdAndTypesAndStatusesAndSearchText(tenantId.getId(), + CollectionsUtil.isEmpty(filter.getTypes()) ? null : filter.getTypes(), + CollectionsUtil.isEmpty(filter.getStatuses()) ? null : filter.getStatuses(), + Strings.emptyToNull(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); } @Override @@ -71,7 +75,7 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao @Override public Job findOldestByTenantIdAndTypeAndStatusForUpdate(TenantId tenantId, JobType type, JobStatus status) { - return DaoUtil.getData(jobRepository.findOldestByTenantIdAndTypeAndStatusForUpdate(tenantId.getId(), type, status, Limit.of(1))); + return DaoUtil.getData(jobRepository.findOldestByTenantIdAndTypeAndStatusForUpdate(tenantId.getId(), type.name(), status.name())); } @Override diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 7f52365e33..ad311f00df 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -129,3 +129,5 @@ CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag); CREATE INDEX IF NOT EXISTS idx_resource_type_public_resource_key ON resource(resource_type, public_resource_key); CREATE INDEX IF NOT EXISTS mobile_app_bundle_tenant_id ON mobile_app_bundle(tenant_id); + +CREATE INDEX IF NOT EXISTS idx_job_tenant_id ON job(tenant_id); From 8c959232d48e00caf5a7e4ad488b3de4ebbe147f Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 8 May 2025 16:49:33 +0300 Subject: [PATCH 143/335] Configurable tasks poll interval; task processing timing --- application/src/main/resources/thingsboard.yml | 2 ++ .../server/queue/task/TaskProcessor.java | 15 +++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 7cae5db294..9d14afb541 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1898,6 +1898,8 @@ queue: # Statistics printing interval for Edge services print-interval-ms: "${TB_QUEUE_EDGE_STATS_PRINT_INTERVAL_MS:60000}" tasks: + # Poll interval in milliseconds for tasks topics + poll_interval: "${TB_QUEUE_TASKS_POLL_INTERVAL_MS:500}" # Partitions count for tasks queues partitions: "${TB_QUEUE_TASKS_PARTITIONS:12}" # Custom partitions count for tasks queues per type. Format: 'TYPE1:24;TYPE2:36', e.g. 'CF_REPROCESSING:24;TENANT_EXPORT:6' diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index fd4367781e..ec113c03a1 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -20,6 +20,7 @@ import jakarta.annotation.PreDestroy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.event.EventListener; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.SetCache; @@ -61,6 +62,9 @@ public abstract class TaskProcessor, R extends TaskResult> { @Autowired private TaskProcessorExecutors executors; + @Value("${queue.tasks.poll_interval:500}") + private int pollInterval; + private QueueKey queueKey; private MainQueueConsumerManager, QueueConfig> taskConsumer; private final ExecutorService taskExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-processor")); @@ -73,7 +77,7 @@ public abstract class TaskProcessor, R extends TaskResult> { queueKey = new QueueKey(ServiceType.TASK_PROCESSOR, getJobType().name()); taskConsumer = MainQueueConsumerManager., QueueConfig>builder() .queueKey(queueKey) - .config(QueueConfig.of(true, 500)) + .config(QueueConfig.of(true, pollInterval)) .msgPackProcessor(this::processMsgs) .consumerCreator((queueConfig, tpi) -> queueFactory.createTaskConsumer(getJobType())) .consumerExecutor(executors.getConsumersExecutor()) @@ -96,14 +100,14 @@ public abstract class TaskProcessor, R extends TaskResult> { switch (entityId.getEntityType()) { case JOB -> { if (event.getEvent() == ComponentLifecycleEvent.STOPPED) { - log.debug("Adding job {} to discarded", entityId); + log.info("Adding job {} to discarded", entityId); addToDiscardedJobs(entityId.getId()); } } case TENANT -> { if (event.getEvent() == ComponentLifecycleEvent.DELETED) { deletedTenants.add(entityId.getId()); - log.debug("Adding tenant {} to deleted", entityId); + log.info("Adding tenant {} to deleted", entityId); } } } @@ -134,9 +138,10 @@ public abstract class TaskProcessor, R extends TaskResult> { private void processTask(T task) throws InterruptedException { task.setAttempt(task.getAttempt() + 1); - log.info("Processing task: {}", task); + log.debug("Processing task: {}", task); Future future = null; try { + long startNs = System.nanoTime(); future = taskExecutor.submit(() -> process(task)); R result; try { @@ -146,6 +151,8 @@ public abstract class TaskProcessor, R extends TaskResult> { } catch (TimeoutException e) { throw new TimeoutException("Timeout after " + getTaskProcessingTimeout() + " ms"); } + long timingNs = System.nanoTime() - startNs; + log.info("Processed task in {} ms: {}", timingNs / 1000000.0, task); reportTaskResult(task, result); } catch (InterruptedException e) { throw e; From 76237337b2daf7ca5a85e97c631d39e48bf80869 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 9 May 2025 09:25:23 +0300 Subject: [PATCH 144/335] UI: Add new widhet type parameters supportsUnitConversion --- .../basic/cards/aggregated-data-key-row.component.html | 4 +++- .../basic/cards/aggregated-data-key-row.component.ts | 7 ++++++- .../widget/config/basic/common/data-key-row.component.html | 1 + .../widget/config/basic/common/data-key-row.component.ts | 7 ++++++- .../components/widget/config/datasource.component.html | 1 + .../home/components/widget/config/datasource.component.ts | 4 ++++ .../common/key/data-key-config-dialog.component.html | 1 + .../common/key/data-key-config-dialog.component.ts | 1 + .../lib/settings/common/key/data-key-config.component.html | 1 + .../lib/settings/common/key/data-key-config.component.ts | 4 ++++ .../widget/lib/settings/common/key/data-keys.component.ts | 7 ++++++- .../home/components/widget/widget-component.service.ts | 3 +++ .../home/components/widget/widget-config.component.html | 1 + ui-ngx/src/app/shared/models/widget.models.ts | 1 + 14 files changed, 39 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html index 58df05162b..9b77abf74f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html @@ -47,7 +47,9 @@ />
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts index d790b1d477..23b700ccbf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts @@ -123,6 +123,10 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn return [DatasourceType.device, DatasourceType.entity].includes(this.datasourceType); } + get supportsUnitConversion(): boolean { + return this.widgetConfigComponent.modelValue?.typeParameters?.supportsUnitConversion ?? false; + } + private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, @@ -222,7 +226,8 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn callbacks: this.callbacks, hideDataKeyName: true, hideDataKeyLabel: true, - hideDataKeyColor: true + hideDataKeyColor: true, + supportsUnitConversion: this.supportsUnitConversion } }).afterClosed().subscribe((updatedDataKey) => { if (updatedDataKey) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html index 2ed3afa3bf..ace382d228 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html @@ -69,6 +69,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts index 7997e332d5..0b3b8107f6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts @@ -225,6 +225,10 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan return this.hasAdditionalLatestDataKeys && this.keyRowFormGroup.get('latest').value === true; } + get supportsUnitConversion(): boolean { + return this.widgetConfigComponent.modelValue?.typeParameters?.supportsUnitConversion ?? false; + } + private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, @@ -337,7 +341,8 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan hideDataKeyLabel: this.hideDataKeyLabel, hideDataKeyColor: this.hideDataKeyColor, hideDataKeyUnits: this.hideDataKeyUnits || !this.displayUnitsOrDigits, - hideDataKeyDecimals: this.hideDataKeyDecimals || !this.displayUnitsOrDigits + hideDataKeyDecimals: this.hideDataKeyDecimals || !this.displayUnitsOrDigits, + supportsUnitConversion: this.supportsUnitConversion } }).afterClosed().subscribe((updatedDataKey) => { if (updatedDataKey) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html index f7bb0662e9..9059decca4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html @@ -71,6 +71,7 @@ [hideDataKeyUnits]="hideDataKeyUnits" [hideDataKeyDecimals]="hideDataKeyDecimals" [maxDataKeys]="maxDataKeys" + [supportsUnitConversion]="supportsUnitConversion" [optDataKeys]="isDataKeysOptional(datasourceFormGroup.get('type').value)" [simpleDataKeysLabel]="!hasAdditionalLatestDataKeys" [aliasController]="aliasController" diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts index 5a519c18d1..48b31439db 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts @@ -135,6 +135,10 @@ export class DatasourceComponent implements ControlValueAccessor, OnInit, Valida return this.widgetConfigComponent.modelValue?.dataKeySettingsFunction; } + public get supportsUnitConversion(): boolean { + return this.widgetConfigComponent.modelValue?.typeParameters?.supportsUnitConversion ?? false; + } + public get dashboard(): Dashboard { return this.widgetConfigComponent.dashboard; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html index 49f578079b..de54ff86d4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html @@ -50,6 +50,7 @@ [hideDataKeyColor]="data.hideDataKeyColor" [hideDataKeyUnits]="data.hideDataKeyUnits" [hideDataKeyDecimals]="data.hideDataKeyDecimals" + [supportsUnitConversion]="data.supportsUnitConversion" formControlName="dataKey">
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts index 2b11c62632..829402a53f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts @@ -56,6 +56,7 @@ export interface DataKeyConfigDialogData { hideDataKeyColor?: boolean; hideDataKeyUnits?: boolean; hideDataKeyDecimals?: boolean; + supportsUnitConversion?: boolean } @Component({ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html index 0af5159846..68a2b5b851 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html @@ -49,6 +49,7 @@
widget-config.units-short
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts index c85a8f0ba6..c7b65bfc83 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts @@ -153,6 +153,10 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con @coerceBoolean() hideDataKeyDecimals = false; + @Input() + @coerceBoolean() + supportsUnitConversion = false; + @ViewChild('keyInput') keyInput: ElementRef; @ViewChild('funcBodyEdit', {static: false}) funcBodyEdit: JsFuncComponent; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts index a9be1fd352..a1c388845f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts @@ -169,6 +169,10 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange @coerceBoolean() simpleDataKeysLabel = false; + @Input() + @coerceBoolean() + supportsUnitConversion = false; + @Input() aliasController: IAliasController; @@ -610,7 +614,8 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange hideDataKeyLabel: this.hideDataKeyLabel, hideDataKeyColor: this.hideDataKeyColor, hideDataKeyUnits: this.hideDataKeyUnits, - hideDataKeyDecimals: this.hideDataKeyDecimals + hideDataKeyDecimals: this.hideDataKeyDecimals, + supportsUnitConversion: this.supportsUnitConversion } }).afterClosed().subscribe((updatedDataKey) => { if (updatedDataKey) { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index c0f37b3a37..c8b9eb963b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -662,6 +662,9 @@ export class WidgetComponentService { if (isUndefined(result.typeParameters.targetDeviceOptional)) { result.typeParameters.targetDeviceOptional = false; } + if (isUndefined(result.typeParameters.supportsUnitConversion)) { + result.typeParameters.supportsUnitConversion = false; + } if (isDefinedAndNotNull(result.typeParameters.additionalWidgetActionTypes)) { if (Array.isArray(result.typeParameters.additionalWidgetActionTypes)) { result.typeParameters.additionalWidgetActionTypes = result.typeParameters.additionalWidgetActionTypes.filter(type => WidgetActionType[type]); diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index efa1dffe0b..77764ba408 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -291,6 +291,7 @@
widget-config.units-by-default
diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 21d7927d16..221358d8c8 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -196,6 +196,7 @@ export interface WidgetTypeParameters { dataKeySettingsFunction?: DataKeySettingsFunction; displayRpcMessageToast?: boolean; targetDeviceOptional?: boolean; + supportsUnitConversion?: boolean; additionalWidgetActionTypes?: WidgetActionType[]; } From 9662b263e4f48934a33e93e7d960c85b6c1ccb1d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 9 May 2025 10:19:37 +0300 Subject: [PATCH 145/335] UI: Refactoring ValueFormatProcessor and fixed unit input component --- ui-ngx/src/app/core/services/unit.service.ts | 6 +- .../common/key/data-key-config.component.html | 2 +- .../components/unit-input.component.html | 8 +- .../shared/components/unit-input.component.ts | 74 ++++++------- ui-ngx/src/app/shared/models/unit.models.ts | 13 +++ .../shared/models/widget-settings.models.ts | 104 ++++++++++-------- 6 files changed, 111 insertions(+), 96 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index 9e656907f3..026ac0f3fe 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -88,11 +88,11 @@ export class UnitService { return this.converter.getUnitConverter(unit as string, to); } - getTargetUnitSymbol(unit: TbUnitMapping): string { + getTargetUnitSymbol(unit: TbUnitMapping | string): string { if (isObject(unit)) { - return isNotEmptyStr(unit[this.currentUnitSystem]) ? unit[this.currentUnitSystem] : unit.from; + return isNotEmptyStr(unit[this.currentUnitSystem]) ? unit[this.currentUnitSystem] : (unit as TbUnitMapping).from; } - return null; + return typeof unit === 'string' ? unit : null; } convertUnitValue(value: number, unit: TbUnitMapping): number; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html index 68a2b5b851..73c1865cc2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html @@ -49,7 +49,7 @@
widget-config.units-short
diff --git a/ui-ngx/src/app/shared/components/unit-input.component.html b/ui-ngx/src/app/shared/components/unit-input.component.html index e577db6395..e1ab29fab3 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.html +++ b/ui-ngx/src/app/shared/components/unit-input.component.html @@ -22,11 +22,11 @@ [class.!pointer-events-none]="disabled" (focusin)="onFocus()" [matAutocomplete]="unitsAutocomplete" - [matAutocompleteDisabled]="allowConverted"> + [matAutocompleteDisabled]="supportsUnitConversion"> +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts index 70e85f26b5..910035a615 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts @@ -47,6 +47,7 @@ import { import { isDefinedAndNotNull, isNumeric } from '@core/utils'; import { WidgetComponent } from '@home/components/widget/widget.component'; import tinycolor from 'tinycolor2'; +import { UnitService } from '@core/services/unit.service'; @Component({ selector: 'tb-slider-widget', @@ -115,6 +116,8 @@ export class SliderWidgetComponent extends showTicks = true; ticksStyle: ComponentStyle = {}; + tickMinText: number; + tickMaxText: number; sliderStep: number = undefined; @@ -137,7 +140,8 @@ export class SliderWidgetComponent extends private utils: UtilsService, private widgetComponent: WidgetComponent, protected cd: ChangeDetectorRef, - private elementRef: ElementRef) { + private elementRef: ElementRef, + private unitService: UnitService) { super(cd); } @@ -180,6 +184,8 @@ export class SliderWidgetComponent extends if (this.showTicks) { this.ticksStyle = textStyle(this.settings.ticksFont); this.ticksStyle.color = this.settings.ticksColor; + this.tickMinText = this.unitService.convertUnitValue(this.settings.tickMin, this.settings.valueUnits); + this.tickMaxText = this.unitService.convertUnitValue(this.settings.tickMax, this.settings.valueUnits); } if (this.settings.showTickMarks) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html index b32c7b5ab1..d1b01c8607 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html @@ -380,6 +380,11 @@
+
+ + {{ 'dynamic-form.property.support-unit-conversion' | translate }} + +
dynamic-form.property.default-value
{ for (const property of properties) { @@ -213,6 +218,9 @@ export const cleanupFormProperty = (property: FormProperty): FormProperty => { delete property.htmlClassList; delete property.htmlContent; } + if (property.type !== FormPropertyType.units) { + delete property.supportsUnitConversion; + } for (const key of Object.keys(property)) { const val = property[key]; if (isUndefinedOrNull(val) || isEmptyStr(val)) { @@ -276,9 +284,9 @@ export const toPropertyGroups = (properties: FormProperty[], customTranslate: CustomTranslatePipe, sanitizer: DomSanitizer): FormPropertyGroup[] => { const groups: {title: string, properties: FormProperty[]}[] = []; - for (let property of properties) { + for (const property of properties) { if (!property.group) { - let group = groups.length ? groups[groups.length - 1] : null; + const group = groups.length ? groups[groups.length - 1] : null; if (group && !group.title) { group.properties.push(property); } else { @@ -311,7 +319,7 @@ const toPropertyContainers = (properties: FormProperty[], customTranslate: CustomTranslatePipe, sanitizer: DomSanitizer): FormPropertyContainer[] => { const result: FormPropertyContainer[] = []; - for (let property of properties) { + for (const property of properties) { if (property.type === FormPropertyType.array) { const propertyArray: FormPropertyArray = { property, @@ -382,7 +390,7 @@ const toPropertyContainers = (properties: FormProperty[], } } } - for (let container of result.filter(c => + for (const container of result.filter(c => c.type === FormPropertyContainerType.row && !c.switch && c.properties?.length === 1)) { const property = container.properties[0]; if (isInputFieldPropertyType(property.type)) { diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index c904f07575..971c888991 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -54,7 +54,7 @@ import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { UnitService } from '@core/services/unit.service'; -import { TbUnit, TbUnitConverter, TbUnitMapping } from '@shared/models/unit.models'; +import { TbUnit, TbUnitConverter } from '@shared/models/unit.models'; export type ComponentStyle = {[klass: string]: any}; @@ -932,7 +932,7 @@ export class UnitConverterValueFormatProcessor extends ValueFormatProcessor { protected settings: ValueFormatSettings) { super($injector, settings); const unitService = this.$injector.get(UnitService); - const unit = settings.units as TbUnitMapping; + const unit = settings.units; this.unitSymbol = settings.ignoreUnitSymbol ? null : unitService.getTargetUnitSymbol(unit); this.unitConverter = unitService.geUnitConverter(unit); @@ -942,10 +942,7 @@ export class UnitConverterValueFormatProcessor extends ValueFormatProcessor { format(value: any): string { if (isDefinedAndNotNull(value) && isNumeric(value)) { - let formatted = Number(value); - if (this.unitConverter) { - formatted = this.unitConverter(value); - } + const formatted = this.unitConverter(Number(value)); return this.formatValue(formatted); } return value ?? ''; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 73946df6f6..5bfc6c0309 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1790,7 +1790,8 @@ "array-item": "Array item", "item-type": "Item type", "item-name": "Item name", - "no-items": "No items" + "no-items": "No items", + "support-unit-conversion": "Support unit conversion" }, "clear-form": "Clear form", "clear-form-prompt": "Are you sure you want to remove all form properties?", From 4fbb6c2e71a080f989900eebf894400caf22fb3e Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 13 May 2025 14:48:46 +0300 Subject: [PATCH 166/335] Jobs: refactoring --- .../server/service/job/DefaultJobManager.java | 22 +++++++++---------- .../src/main/resources/thingsboard.yml | 5 ++++- .../server/service/job/JobManagerTest.java | 4 ++-- ...anagerTest_EntityPartitioningStrategy.java | 2 +- .../server/dao/job/JobService.java | 2 +- .../server/common/data/job/Job.java | 2 ++ .../server/common/data/job/JobResult.java | 2 ++ .../server/common/data/job/JobStats.java | 2 ++ common/proto/src/main/proto/queue.proto | 4 ---- .../common/consumer/QueueConsumerManager.java | 14 +++++++++++- .../queue/settings/TasksQueueConfig.java | 17 ++++++++++---- .../server/queue/task/JobStatsService.java | 3 ++- .../server/dao/job/DefaultJobService.java | 4 +++- 13 files changed, 55 insertions(+), 28 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 8c32de4f45..580dd0ff99 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.job; import jakarta.annotation.PreDestroy; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; @@ -51,6 +50,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.consumer.QueueConsumerManager; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbCoreQueueFactory; +import org.thingsboard.server.queue.settings.TasksQueueConfig; import org.thingsboard.server.queue.task.JobStatsService; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -74,23 +74,21 @@ public class DefaultJobManager implements JobManager { private final JobStatsService jobStatsService; private final NotificationCenter notificationCenter; private final PartitionService partitionService; + private final TasksQueueConfig queueConfig; private final Map jobProcessors; private final Map>> taskProducers; private final QueueConsumerManager> jobStatsConsumer; private final ExecutorService executor; private final ExecutorService consumerExecutor; - @Value("${queue.tasks.partitioning_strategy:tenant}") - private String tasksPartitioningStrategy; - @Value("${queue.tasks.stats.processing_interval_ms:1000}") - private int statsProcessingInterval; - public DefaultJobManager(JobService jobService, JobStatsService jobStatsService, NotificationCenter notificationCenter, - PartitionService partitionService, TbCoreQueueFactory queueFactory, List jobProcessors) { + PartitionService partitionService, TbCoreQueueFactory queueFactory, TasksQueueConfig queueConfig, + List jobProcessors) { this.jobService = jobService; this.jobStatsService = jobStatsService; this.notificationCenter = notificationCenter; this.partitionService = partitionService; + this.queueConfig = queueConfig; this.jobProcessors = jobProcessors.stream().collect(Collectors.toMap(JobProcessor::getType, Function.identity())); this.taskProducers = Arrays.stream(JobType.values()).collect(Collectors.toMap(Function.identity(), queueFactory::createTaskProducer)); this.executor = ThingsBoardExecutors.newWorkStealingPool(Math.max(4, Runtime.getRuntime().availableProcessors()), getClass()); @@ -98,7 +96,7 @@ public class DefaultJobManager implements JobManager { this.jobStatsConsumer = QueueConsumerManager.>builder() .name("job-stats") .msgPackProcessor(this::processStats) - .pollInterval(125) + .pollInterval(queueConfig.getStatsPollInterval()) .consumerCreator(queueFactory::createJobStatsConsumer) .consumerExecutor(consumerExecutor) .build(); @@ -113,7 +111,7 @@ public class DefaultJobManager implements JobManager { @Override public Job submitJob(Job job) { log.debug("Submitting job: {}", job); - return jobService.submitJob(job.getTenantId(), job); + return jobService.saveJob(job.getTenantId(), job); } @Override @@ -196,7 +194,7 @@ public class DefaultJobManager implements JobManager { job.getConfiguration().setToReprocess(taskFailures); - jobService.submitJob(tenantId, job); + jobService.saveJob(tenantId, job); } private void submitTask(Task task) { @@ -207,7 +205,7 @@ public class DefaultJobManager implements JobManager { TbQueueProducer> producer = taskProducers.get(task.getJobType()); EntityId entityId = null; - if (tasksPartitioningStrategy.equals("entity")) { + if (queueConfig.getPartitioningStrategy().equals("entity")) { entityId = task.getEntityId(); } if (entityId == null) { @@ -257,7 +255,7 @@ public class DefaultJobManager implements JobManager { }); consumer.commit(); - Thread.sleep(statsProcessingInterval); + Thread.sleep(queueConfig.getStatsProcessingInterval()); } private void sendJobFinishedNotification(Job job) { diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 4211b4e845..de641e39b2 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1908,9 +1908,12 @@ queue: # In a single-tenant environment, use 'entity' strategy to distribute the tasks among multiple partitions. partitioning_strategy: "${TB_QUEUE_TASKS_PARTITIONING_STRATEGY:tenant}" stats: + # Name for the tasks stats topic topic: "${TB_QUEUE_TASKS_STATS_TOPIC:jobs.stats}" + # Poll interval in milliseconds for tasks stats topic + poll_interval: "${TB_QUEUE_TASKS_STATS_POLL_INTERVAL_MS:500}" # Interval in milliseconds to process job stats - processing_interval_ms: "${TB_QUEUE_TASKS_STATS_PROCESSING_INTERVAL_MS:1000}" + processing_interval: "${TB_QUEUE_TASKS_STATS_PROCESSING_INTERVAL_MS:1000}" # Event configuration parameters event: diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 206acc450d..85b963a196 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -52,7 +52,7 @@ import static org.mockito.Mockito.verify; @DaoSqlTest @TestPropertySource(properties = { - "queue.tasks.stats.processing_interval_ms=0" + "queue.tasks.stats.processing_interval=0" }) public class JobManagerTest extends AbstractControllerTest { @@ -203,7 +203,7 @@ public class JobManagerTest extends AbstractControllerTest { .description("test job") .configuration(DummyJobConfiguration.builder() .successfulTasksCount(tasksCount) - .taskProcessingTimeMs(100) + .taskProcessingTimeMs(500) .build()) .build()).getId(); diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java index 983f30d523..a021603ca6 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java @@ -20,7 +20,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest @TestPropertySource(properties = { - "queue.tasks.stats.processing_interval_ms=0", + "queue.tasks.stats.processing_interval=0", "queue.tasks.partitioning_strategy=entity", "queue.tasks.partitions_per_type=DUMMY:100;DUMMY:50" }) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java index 3204044880..33f9511267 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java @@ -26,7 +26,7 @@ import org.thingsboard.server.dao.entity.EntityDaoService; public interface JobService extends EntityDaoService { - Job submitJob(TenantId tenantId, Job job); + Job saveJob(TenantId tenantId, Job job); Job findJobById(TenantId tenantId, JobId jobId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java index 237c223a92..d4e69761c8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.job; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Builder; @@ -43,6 +44,7 @@ public class Job extends BaseData implements HasTenantId { private String description; private JobStatus status; @NotNull + @Valid private JobConfiguration configuration; private JobResult result; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index 3af076cd19..285143dfa4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -44,6 +44,8 @@ public abstract class JobResult implements Serializable { private List results = new ArrayList<>(); private String generalError; + private long startTs; + private long finishTs; private long cancellationTs; @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java index dc3e265f2d..50a3b1d759 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobStats.java @@ -25,8 +25,10 @@ import java.util.List; @Data public class JobStats { + private final TenantId tenantId; private final JobId jobId; private final List taskResults = new ArrayList<>(); private Integer totalTasksCount; + } diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 04c41a7f69..2a7d28241e 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -536,10 +536,6 @@ message ToEdqsCoreServiceMsg { bytes value = 1; } -message ToJobManagerMsg { - bytes value = 1; -} - message LwM2MRegistrationRequestMsg { string tenantId = 1; string endpoint = 2; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/QueueConsumerManager.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/QueueConsumerManager.java index ffed499d8d..5025d887cb 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/QueueConsumerManager.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/QueueConsumerManager.java @@ -25,7 +25,11 @@ import org.thingsboard.server.queue.TbQueueMsg; import java.util.List; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Supplier; @Slf4j @@ -39,6 +43,7 @@ public class QueueConsumerManager { @Getter private final TbQueueConsumer consumer; + private Future consumerTask; private volatile boolean stopped; @Builder @@ -63,7 +68,7 @@ public class QueueConsumerManager { public void launch() { log.info("[{}] Launching consumer", name); - consumerExecutor.submit(() -> { + consumerTask = consumerExecutor.submit(() -> { if (threadPrefix != null) { ThingsBoardThreadFactory.addThreadNamePrefix(threadPrefix); } @@ -101,6 +106,13 @@ public class QueueConsumerManager { log.debug("[{}] Stopping consumer", name); stopped = true; consumer.unsubscribe(); + try { + if (consumerTask != null) { + consumerTask.get(10, TimeUnit.SECONDS); + } + } catch (InterruptedException | ExecutionException | TimeoutException e) { + log.error("[{}] Failed to await consumer loop stop", name, e); + } } public interface MsgPackProcessor { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TasksQueueConfig.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TasksQueueConfig.java index f4916a411e..7c94596139 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TasksQueueConfig.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TasksQueueConfig.java @@ -15,18 +15,27 @@ */ package org.thingsboard.server.queue.settings; -import lombok.Data; +import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -@Data +@Getter @Component public class TasksQueueConfig { - @Value("${queue.tasks.poll_interval}") + @Value("${queue.tasks.poll_interval:500}") private int pollInterval; - @Value("${queue.tasks.stats.topic}") + @Value("${queue.tasks.partitioning_strategy:tenant}") + private String partitioningStrategy; + + @Value("${queue.tasks.stats.topic:jobs.stats}") private String statsTopic; + @Value("${queue.tasks.stats.poll_interval:500}") + private int statsPollInterval; + + @Value("${queue.tasks.stats.processing_interval:1000}") + private int statsProcessingInterval; + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java index 28d08f593c..0b7d18fde4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/JobStatsService.java @@ -53,12 +53,13 @@ public class JobStatsService { } private void report(TenantId tenantId, JobId jobId, JobStatsMsg.Builder statsMsg) { - log.debug("[{}] Reporting: {}", jobId, statsMsg); + log.debug("[{}][{}] Reporting: {}", tenantId, jobId, statsMsg); statsMsg.setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) .setJobIdMSB(jobId.getId().getMostSignificantBits()) .setJobIdLSB(jobId.getId().getLeastSignificantBits()); + // using job id as msg key so that all stats for a certain job are submitted to the same partition TbProtoQueueMsg msg = new TbProtoQueueMsg<>(jobId.getId(), statsMsg.build()); producer.send(TopicPartitionInfo.builder().topic(producer.getDefaultTopic()).build(), msg, TbQueueCallback.EMPTY); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index ba01b10326..50b3e4bde8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -54,7 +54,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi @Transactional @Override - public Job submitJob(TenantId tenantId, Job job) { + public Job saveJob(TenantId tenantId, Job job) { if (jobDao.existsByTenantAndKeyAndStatusOneOf(tenantId, job.getKey(), QUEUED, PENDING, RUNNING)) { throw new IllegalArgumentException("The same job is already queued or running"); } @@ -62,6 +62,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi job.setStatus(QUEUED); } else { job.setStatus(PENDING); + job.getResult().setStartTs(System.currentTimeMillis()); } return saveJob(tenantId, job, true, null); } @@ -140,6 +141,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi job.setStatus(COMPLETED); publishEvent = true; } + result.setFinishTs(System.currentTimeMillis()); } } From 111ad28816b3b7e192b17f075b7d113fa7bc14f9 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 13 May 2025 17:34:24 +0300 Subject: [PATCH 167/335] UI: Refactoring search in unit component --- ui-ngx/src/app/core/services/unit.service.ts | 3 - ...uid-level-card-basic-config.component.html | 8 +- ...iquid-level-card-basic-config.component.ts | 4 +- ...-level-card-widget-settings.component.html | 8 +- ...id-level-card-widget-settings.component.ts | 4 +- ...convert-unit-settings-panel.component.html | 82 ++++++----- ...convert-unit-settings-panel.component.scss | 5 +- .../convert-unit-settings-panel.component.ts | 4 +- .../components/unit-input.component.html | 2 +- .../shared/components/unit-input.component.ts | 69 +++++++-- ui-ngx/src/app/shared/models/unit.models.ts | 135 +++++++++--------- .../assets/locale/locale.constant-en_US.json | 22 +-- 12 files changed, 200 insertions(+), 146 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index 86f74e78b5..5d60e80390 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -46,8 +46,6 @@ export class UnitService { takeUntilDestroyed() ).subscribe(() => { this.converter = getUnitConverter(this.translate); - console.warn(this.converter.listUnits()); - console.warn(this.converter.listUnits(null, UnitSystem.IMPERIAL)); }); } @@ -61,7 +59,6 @@ export class UnitService { } else { this.currentUnitSystem = this.getUnitSystemByTimezone(); } - console.warn('[Unit system] setUnitSystem', this.currentUnitSystem); } getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfo[] { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html index d2752aebab..c270b17551 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html @@ -125,7 +125,7 @@
widgets.liquid-level-card.datasource-units
@@ -143,7 +143,7 @@ @@ -199,7 +199,7 @@
@@ -270,7 +270,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts index 3a64cf6b1d..6cd016a85d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts @@ -56,7 +56,7 @@ import { ShapesTranslations, updatedFormSettingsValidators } from '@home/components/widget/lib/indicator/liquid-level-widget.models'; -import { getSourceTbUnitSymbol, UnitsType } from '@shared/models/unit.models'; +import { getSourceTbUnitSymbol } from '@shared/models/unit.models'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component'; import { map, share, tap } from 'rxjs/operators'; @@ -116,8 +116,6 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon shapesImageMap: Map = new Map(); ShapesTranslationMap = ShapesTranslations; - unitsType = UnitsType; - levelCardWidgetConfigForm: FormGroup; valuePreviewFn = this._valuePreviewFn.bind(this); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html index 88f62477dd..70eec82e2d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html @@ -83,7 +83,7 @@
widgets.liquid-level-card.datasource-units
@@ -100,7 +100,7 @@ @@ -156,7 +156,7 @@
@@ -239,7 +239,7 @@ {{ 'widgets.liquid-level-card.level' | translate }}
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts index 843c4470b5..386ada1cab 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts @@ -44,7 +44,7 @@ import { ShapesTranslations, updatedFormSettingsValidators } from '@home/components/widget/lib/indicator/liquid-level-widget.models'; -import { getSourceTbUnitSymbol, UnitsType } from '@shared/models/unit.models'; +import { getSourceTbUnitSymbol } from '@shared/models/unit.models'; import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { Observable, of, ReplaySubject } from 'rxjs'; @@ -96,8 +96,6 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon shapesImageMap: Map = new Map(); ShapesTranslationMap = ShapesTranslations; - unitsType = UnitsType; - levelCardWidgetSettingsForm: FormGroup; valuePreviewFn = this._valuePreviewFn.bind(this); diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html index 7c935728a3..3f14ba290c 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html @@ -16,48 +16,60 @@ -->
-
unit.convert.units-conversion-settings
+
unit.conversion.unit-settings
-
unit.convert.convert-from
+
unit.conversion.source-unit
-
- -
- {{ 'unit.convert.convert-unit' | translate }} +
+ +
+ {{ 'unit.conversion.enable-unit-conversion' | translate }}
-
-
-
unit.convert.to-metric
- - -
-
-
unit.convert.to-imperial
- - -
-
-
unit.convert.to-imperial
- - +
+
unit.conversion.target-metric-unit
+ + +
+
+
unit.conversion.target-imperial-unit
+ + +
+
+
unit.conversion.target-hybrid-unit
+ + +
diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss index fb2bf1be09..24df54f839 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss @@ -16,13 +16,16 @@ @import '../scss/constants'; .tb-convert-settings-panel { - width: 320px; + width: 360px; display: flex; flex-direction: column; gap: 16px; max-height: calc(100vh - 24px); @media #{$mat-xs} { width: 90vw; + .tb-form-row tb-unit-input { + width: 90px; + } } .tb-convert-settings-title { font-size: 16px; diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts index 7d3b74e27e..92b8162576 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts @@ -15,7 +15,7 @@ /// import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { AllMeasures, isNotEmptyTbUnits, TbUnit, UnitInfo, UnitsType, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures, isNotEmptyTbUnits, TbUnit, UnitInfo, UnitSystem } from '@shared/models/unit.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { FormBuilder, Validators } from '@angular/forms'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -48,7 +48,7 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { unitSettingsApplied = new EventEmitter(); @Input() - tagFilter: UnitsType; + tagFilter: string; @Input() measure: AllMeasures; diff --git a/ui-ngx/src/app/shared/components/unit-input.component.html b/ui-ngx/src/app/shared/components/unit-input.component.html index 33c77130b9..022c4f15e1 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.html +++ b/ui-ngx/src/app/shared/components/unit-input.component.html @@ -40,7 +40,7 @@ mdi:swap-vertical-circle-outline diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index 510f05826d..1f263772b5 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -37,7 +37,6 @@ import { isTbUnitMapping, TbUnit, UnitInfo, - UnitsType, UnitSystem } from '@shared/models/unit.models'; import { map, mergeMap } from 'rxjs/operators'; @@ -73,7 +72,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang required = false; @Input() - tagFilter: UnitsType; + tagFilter: string; @Input() measure: AllMeasures; @@ -264,18 +263,68 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { if (isNotEmptyStr(searchText)) { - const filterValue = searchText.trim().toUpperCase() - return units.reduce((result: Array<[AllMeasures, Array]>, [measure, unitInfos]) => { - const filteredUnits = unitInfos.filter(unit => unit.searchText.toUpperCase().includes(filterValue)); - if (filteredUnits.length > 0) { - result.push([measure, filteredUnits]); - } - return result; - }, []); + const filterValue = searchText.trim().toUpperCase(); + + const scoredGroups = units + .map(([measure, unitInfos]) => { + const scoredUnits = unitInfos + .map(unit => ({ + unit, + score: this.calculateRelevanceScore(unit, filterValue) + })) + .filter(({ score }) => score > 0) + .sort((a, b) => b.score - a.score) + .map(({ unit }) => unit); + + let groupScore = scoredUnits.length > 0 + ? Math.max(...scoredUnits.map(unit => this.calculateRelevanceScore(unit, filterValue))) + : 0; + + if (measure.toUpperCase() === filterValue) { + groupScore += 200; + } + + return { measure, units: scoredUnits, groupScore }; + }) + .filter(group => group.units.length > 0) + .sort((a, b) => { + if (b.groupScore !== a.groupScore) { + return b.groupScore - a.groupScore; + } + return b.units.length - a.units.length; + }); + + return scoredGroups.map(group => [group.measure, group.units] as [AllMeasures, Array]); } return units; } + private calculateRelevanceScore(unit: UnitInfo, filterValue: string): number { + const name = unit.name.toUpperCase(); + const abbr = unit.abbr.toUpperCase(); + const tags = unit.tags.map(tag => tag.toUpperCase()); + + let score = 0; + + if (name === filterValue || abbr === filterValue) { + score += 100; + } else if (tags.includes(filterValue)) { + score += 80; + } else if (name.startsWith(filterValue) || abbr.startsWith(filterValue)) { + score += 60; + } else if (tags.some(tag => tag.startsWith(filterValue))) { + score += 50; + } else if (tags.some(tag => tag.includes(filterValue))) { + score += 30; + } + + if (score > 0) { + score += Math.max(0, 10 - (name.length + abbr.length) / 2); + } + + return score; + } + private extractTbUnit(value: TbUnit | UnitInfo | null): TbUnit { if (value === null) { return null; diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index f388b6ab81..94a831afa0 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -204,6 +204,7 @@ export type AllMeasures = | 'angle' | 'angular-acceleration' | 'area' + | 'area-density' | 'capacitance' | 'catalytic-activity' | 'catalytic-concentration' @@ -287,28 +288,51 @@ const allMeasures: Record< AllMeasures, TbMeasure > = Object.freeze({ - 'absorbed-dose-rate': absorbedDoseRate, + temperature, + pressure, + voltage, + 'current-density': currentDensity, + 'electric-current': electricCurrent, + power, + energy, + speed, + length, + mass, + time, + area, + volume, + 'volume-flow': volumeFlow, + density, acceleration, - acidity, 'air-quality-index': airQualityIndex, - 'amount-of-substance': amountOfSubstance, - angle, - 'angular-acceleration': angularAcceleration, - area, - 'area-density': areaDensity, + illuminance, + 'signal-level': signalLevel, + 'fuel-efficiency': fuelEfficiency, + frequency, capacitance, + inductance, + resistance, + torque, + force, + 'magnetic-flux-density': magneticFluxDensity, + 'magnetic-flux': magneticFlux, + radioactivity, + 'radioactive-decay': radioactiveDecay, + 'specific-energy': specificEnergy, + 'specific-heat-capacity': specificHeatCapacity, + 'kinematic-viscosity': kinematicViscosity, + 'dynamic-viscosity': dynamicViscosity, + 'thermal-conductivity': thermalConductivity, + turbidity, + 'earthquake-magnitude': earthquakeMagnitude, + 'data-transfer-rate': dataTransferRate, + 'parts-per-million': partsPerMillion, + 'molar-concentration': molarConcentration, + 'number-concentration': numberConcentration, 'catalytic-activity': catalyticActivity, 'catalytic-concentration': catalyticConcentration, charge, - 'current-density': currentDensity, - 'data-transfer-rate': dataTransferRate, - density, - digital, - 'dimension-ratio': dimensionRatio, - 'dynamic-viscosity': dynamicViscosity, - 'earthquake-magnitude': earthquakeMagnitude, 'electric-charge-density': electricChargeDensity, - 'electric-current': electricCurrent, 'electric-dipole-moment': electricDipoleMoment, 'electric-field-strength': electricFieldStrength, 'electric-flux': electricFlux, @@ -316,70 +340,43 @@ const allMeasures: Record< 'electric-polarizability': electricPolarizability, 'electrical-conductance': electricalConductance, 'electrical-conductivity': electricalConductivity, - energy, - 'energy-density': energyDensity, - force, - frequency, - 'fuel-efficiency': fuelEfficiency, - 'heat-capacity': heatCapacity, - illuminance, - inductance, - 'kinematic-viscosity': kinematicViscosity, - length, - 'light-exposure': lightExposure, - 'linear-charge-density': linerChargeDensity, - 'logarithmic-ratio': logarithmicRatio, - 'luminous-efficacy': luminousEfficacy, - 'luminous-flux': luminousFlux, - 'luminous-intensity': luminousIntensity, - 'number-concentration': numberConcentration, 'magnetic-field-gradient': magneticFieldGradient, - 'magnetic-flux': magneticFlux, - 'magnetic-flux-density': magneticFluxDensity, 'magnetic-moment': magneticMoment, 'magnetic-permeability': magneticPermeability, - mass, - 'mass-fraction': massFraction, - 'molar-concentration': molarConcentration, - 'molar-energy': molarEnergy, - 'molar-heat-capacity': molarHeatCapacity, - 'molar-mass': molarMass, - 'parts-per-million': partsPerMillion, - power, - 'power-density': powerDensity, - pressure, radiance, 'radiant-intensity': radiantIntensity, 'radiation-dose': radiationDose, - 'radioactive-decay': radioactiveDecay, - radioactivity, 'radioactivity-concentration': radioactivityConcentration, 'reciprocal-length': reciprocalLength, - resistance, 'reynolds-number': reynoldsNumber, - 'signal-level': signalLevel, - 'solid-angle': solidAngle, - 'specific-energy': specificEnergy, - 'specific-heat-capacity': specificHeatCapacity, - 'specific-humidity': specificHumidity, - 'specific-volume': specificVolume, - speed, 'surface-charge-density': surfaceChargeDensity, 'surface-tension': surfaceTension, - temperature, - 'thermal-conductivity': thermalConductivity, - time, - torque, - turbidity, - voltage, - volume, - 'volume-flow': volumeFlow, + 'specific-volume': specificVolume, + 'specific-humidity': specificHumidity, + 'angular-acceleration': angularAcceleration, + angle, + 'solid-angle': solidAngle, + 'light-exposure': lightExposure, + 'luminous-intensity': luminousIntensity, + 'luminous-flux': luminousFlux, + 'luminous-efficacy': luminousEfficacy, + 'molar-energy': molarEnergy, + 'molar-heat-capacity': molarHeatCapacity, + 'molar-mass': molarMass, + 'mass-fraction': massFraction, + 'logarithmic-ratio': logarithmicRatio, + 'dimension-ratio': dimensionRatio, + 'absorbed-dose-rate': absorbedDoseRate, + acidity, + 'amount-of-substance': amountOfSubstance, + digital, + 'area-density': areaDensity, + 'energy-density': energyDensity, + 'heat-capacity': heatCapacity, + 'linear-charge-density': linerChargeDensity, + 'power-density': powerDensity, }); -export enum UnitsType { - capacity = 'capacity' -} - export type TbUnitConverter = (value: number) => number; export type UnitInfoGroupByMeasure = Partial>; @@ -389,7 +386,6 @@ export interface UnitInfo { system: UnitSystem; name: string; tags: string[]; - searchText: string; } export enum UnitSystem { @@ -430,7 +426,6 @@ export interface UnitCacheInfo { measure: AllMeasures; unit: Unit; abbr: AllMeasuresUnits; - searchText: string; } export type UnitCache = Map; @@ -591,8 +586,7 @@ export class Converter { measure: unit.measure, system: unit.system, name: unit.unit.name, - tags: unit.unit.tags, - searchText: unit.searchText + tags: unit.unit.tags }; } @@ -638,7 +632,6 @@ function buildUnitCache(measures: Record Date: Tue, 13 May 2025 17:52:22 +0300 Subject: [PATCH 168/335] UI: Fixed typo --- .../json/system/widget_types/signal_strength.json | 13 +++++++++++++ .../convert-unit-settings-panel.component.html | 2 +- .../convert-unit-settings-panel.component.ts | 5 ----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/application/src/main/data/json/system/widget_types/signal_strength.json b/application/src/main/data/json/system/widget_types/signal_strength.json index 516f87fe4c..4784808baa 100644 --- a/application/src/main/data/json/system/widget_types/signal_strength.json +++ b/application/src/main/data/json/system/widget_types/signal_strength.json @@ -32,5 +32,18 @@ "wireless", "link", "quality" + ], + "resources": [ + { + "link": "/api/images/system/signal_strength_system_widget_image.png", + "title": "\"Signal strength\" system widget image", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "signal_strength_system_widget_image.png", + "publicResourceKey": "oOkg5kXnhiyzZ2pEe66hRVSLV94D8v4i", + "mediaType": "image/png", + "data": "iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAgVBMVEXg4ODf39/g4OAAAADg4ODf39////9c35Dh4eEhISE9PT3r+/GF56z1/fhw457C89Vm4Zfx8fHHx8fW9+Ou78isrKx0dHSQkJC48c5YWFij7cGCgoIvLy+Z67p65aWenp7M9dyP6bPh+eq6urqZ67lmZmbV1dVLS0tKSkrv7++48c8QruqjAAAABnRSTlPvIL8Ar7DvmsykAAAGE0lEQVR42uzX3WrjMBAF4GRbhjlzNwJJCAl8IWSn7/+CO7F3I+ciLNtQ1w0+UAn9VXxoCMnp7fzrxD897+e30/n0QT8+aox3pRfIh5UVvUROrwLhA7KzHJC95YDsLQdkbzkge8sB2Vueh4yOPhd1vBHEtZS89VmUHkeEPhcPvw0kooSEiWgQ/g/IICP9K06G7SAKUaLcb9O+1Ls1RHXxu76q/ci6d4gLRDeAMC7WjtFRFjZRwdSESEIGmhINYj13CCdgGqkVlExNIuI8lfh6pgGZiHyBRBntaBGDZIH4L4dQwlTpmgAmj4sPABEQfMNAIy51QOoQkeqkqGswe0IJVaUMQ7EtpvAXVGKITcCNGc2Tn0fl6yEagBJ4gUz2Zw0RhIgRiP38Gh1yfcAadSmtBF5qx4YjXb0VkbJhrHG9tCiAN/j4ZZ8gOl+GNL9Rh5ALk2AFiZDgif5C5pecUhL4+bBDtFm19a0h7K4rGcMCudxD5tJy0iFXmWC6h7RgcTdI+xaIR1za+TIpeg/JGO9Ki6ONA8YVZMBgrdINkuG+AaKC7LxgnC+LaC5jBYkINtEhWibnJjANdmyBaCm+ZtEbxEGqF4MwproZhDgAKMOfy0yRZAXRBqQL9FZaVZbdLEgLhHiZukEoFpRkEGrAdhAL90VVJim0ivKD3ayP9qiNA1zfu/2Xxiq+ZmR6KirZRQj1bA/hAJSs9FyiAKlfuCmkh59gPPgnu/o9spMckL3lgOwtB2RvOSC/2a13FYtBIADDnRZzURgLERQNef9nXHa3CAu65maORf4+JF9Gxdl6IbP1QmbrhczWC5mtFzJbL2S2RkAIsg+xJGarf2JGjM7nhdTW3BAwobBuxxjyor6bGLL4YvWeLHqZFUImWn0kWwxMByGP+kxoYCaIbIozlkkg5Ky+Fkf4PERQ3xHmoZDxjC02d0A+ztgo1yATMDbKsxDoMzitq/PmN+9CQda9EB6E9E8qXn2uf9EivqR/KREehAjrZhiEer9BHPbX13gIBd3IhkxqZ7JyZyijIcIthaitSxY2D0Aa40AhdaK86mqBBkMgVYfhQJ0NAleHAkMhYqsMUpcyNYr1AyGuzRhAccMgsckYQyk0BEKpsyevBZVxM42AxMpJ1cXDItkYIyJAJ249ZQAEDu1GWkwobP8+kDB4Oba+4H6I2X29oxySboch0+6h+IuQ/okVWp/iUfdDD/te4+6HfLV3RjuSgkAU7Z7ZxUmKAjbwQEw0anzZ/v8P3J7N7nSPoEiYkcJwX9t0PLEqxb1lov3UhTo5S+mlnwU+lZf8ehB8vgnvPegxMoXotJdk/N4eYe12WQ082RK65WW+dY6YffMsGsUt4zl6IEZMX66D9j0dBWYVSO4SD40IGGPfH4p3t3h40mhUkyyTP2mEPlA4oud3CRG4DDKD3NT6BqSVEzwj66HlKvBQMoHguAIxrk7uSa5Nmw6zgYDwUswaQ0ZdpZVXOkjY93KLbIckb1wpnQXk5sPQEVlQ48pmABkCGGGBB0UfDyI8GOlNxg8Hmbx+MT1zgKNB9LpfTKovnfWJqJQmtXlBmHoaABN7KG0cieOb3T7KCjeyFNt272rNoPec1+RhIK6Pm3dnKX034Lb7NFkmu3y/US7jUgguIfBfWQ6NgP5NgYrfEiJg/ndRvIYxjEL0NSdnwRhEoQwyqIjIgS4IdlGRA1kQEHE5EBAFmWLjFCVJgoBqojVRBPH2h+rn1tw1ci8mpwgS2oJM0pO5ID0QWLayQfca10fRA8F9Y0IK6k+E8eYhg/t2eD3FHhkcoxUeN5IiCDOO0QolrYbkHPlX/8ruXxkRnex3oYb9VobwoTFOFaSCpAgNV41aiRwKAvlYzQnLXJUDYraXhMWA3AJbnFJAFlZLOZOjFJA2tIEuBcTJftlCpYA0Sy1+Lxdk0STlgix+LwbkND1imsCbqaWA4GKOAFuoFBBmTzLZz3PWYsyK/3VV9umXMZB//Yg/6CoJZEMVpIKUogpCTRWEmioINVUQaqog1FRBqOlEIBd2Cr1dXk7w6WnGfv28XC8n+Pj07zvG6/XHW+m6vFxf/wBvcT51JCx2jQAAAABJRU5ErkJggg==", + "public": true + } ] } \ No newline at end of file diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html index 3f14ba290c..7790d185dd 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html @@ -61,7 +61,7 @@
unit.conversion.target-hybrid-unit
{ - this.unitFrom.unitInput.nativeElement.focus(); - }); - } } clearUnit() { From c7ed268f8ce9d05891ecfd66d1fe8796fdcb4586 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 13 May 2025 18:22:44 +0300 Subject: [PATCH 169/335] UI: Clear app component --- ui-ngx/src/app/app.component.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index 85bddd56cb..dc3d539acb 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -16,7 +16,7 @@ import 'hammerjs'; -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { environment as env } from '@env/environment'; @@ -40,7 +40,7 @@ import { UnitService } from '@core/services/unit.service'; templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent implements OnInit { +export class AppComponent { constructor(private store: Store, private storageService: LocalStorageService, @@ -103,7 +103,7 @@ export class AppComponent implements OnInit { userLang = settings?.userLang ?? null; } this.notifyUserLang(userLang); - this.unitService.setUnitSystem(userDetails?.additionalInfo?.unitSystem) + this.unitService.setUnitSystem(userDetails?.additionalInfo?.unitSystem); }), skip(1), ).subscribe((data) => { @@ -112,10 +112,7 @@ export class AppComponent implements OnInit { this.authService.reloadUser(); } - ngOnInit() { - } - - onActivateComponent($event: any) { + onActivateComponent(_$event: any) { const loadingElement = $('div#tb-loading-spinner'); if (loadingElement.length) { loadingElement.remove(); From 55da7ac2b6c6a9c5695d4e2ac3d5822fda5863a7 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 14 May 2025 09:49:27 +0300 Subject: [PATCH 170/335] Fix rule engine startup --- .../org/thingsboard/server/service/edqs/EdqsSyncService.java | 2 +- .../server/service/entitiy/EntityStateSourcingListener.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncService.java b/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncService.java index 871e1fa5e7..88f344d048 100644 --- a/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncService.java +++ b/application/src/main/java/org/thingsboard/server/service/edqs/EdqsSyncService.java @@ -95,7 +95,7 @@ public abstract class EdqsSyncService { syncLatestTimeseries(); counters.clear(); - log.info("Finishing synchronizing data to EDQS in {} ms", (System.currentTimeMillis() - startTs)); + log.info("Finished synchronizing data to EDQS in {} ms", (System.currentTimeMillis() - startTs)); } private void process(TenantId tenantId, ObjectType type, EdqsObject object) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index f56cc3e952..bcb77e7005 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -62,6 +62,7 @@ import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.service.job.JobManager; +import java.util.Optional; import java.util.Set; @Slf4j @@ -72,7 +73,7 @@ public class EntityStateSourcingListener { private final TenantService tenantService; private final TbClusterService tbClusterService; private final EdgeSynchronizationManager edgeSynchronizationManager; - private final JobManager jobManager; + private final Optional jobManager; @PostConstruct public void init() { @@ -303,7 +304,7 @@ public class EntityStateSourcingListener { } private void onJobUpdate(Job job) { - jobManager.onJobUpdate(job); + jobManager.ifPresent(jobManager -> jobManager.onJobUpdate(job)); if (job.getResult().getCancellationTs() > 0 || (job.getStatus().isOneOf(JobStatus.FAILED) && job.getResult().getGeneralError() != null)) { // task processors will add this job to the list of discarded tbClusterService.broadcastEntityStateChangeEvent(job.getTenantId(), job.getId(), ComponentLifecycleEvent.STOPPED); From 19405416bee3babd9fba7410f545c38ddb464684 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 14 May 2025 11:01:03 +0300 Subject: [PATCH 171/335] UI: Fixed license header --- .../models/units/specific-heat-capacity.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts index 595d1ca7ff..0de957396b 100644 --- a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts +++ b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type SpecificHeatCapacityUnits = 'J/(kg·K)'; From 758ed0f18954378b6457e274267f597288c20cdf Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 14 May 2025 13:07:07 +0300 Subject: [PATCH 172/335] tbel: Add utility methods to check for Map, List, and Array types in TbUtils --- .../service/script/TbelInvokeDocsIoTest.java | 80 ++++++++++++++++++ .../thingsboard/script/api/tbel/TbUtils.java | 19 +++++ .../script/api/tbel/TbUtilsTest.java | 81 ++++++++++++------- 3 files changed, 153 insertions(+), 27 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java index bf86f98e6e..36ac8d54dc 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java @@ -33,6 +33,9 @@ import java.util.concurrent.atomic.AtomicReference; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { @@ -2345,6 +2348,83 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { assertEquals(expected, actual); } + @Test + public void isMap_Test() throws ExecutionException, InterruptedException { + msgStr = """ + {} + """; + decoderStr = """ + return isMap(msg); + """; + Object actual = invokeScript(evalScript(decoderStr), msgStr); + assertInstanceOf(Boolean.class, actual); + assertTrue((Boolean) actual); + decoderStr = """ + return isList(msg); + """; + actual = invokeScript(evalScript(decoderStr), msgStr); + assertInstanceOf(Boolean.class, actual); + assertFalse((Boolean) actual); + } + + @Test + public void isList_Test() throws ExecutionException, InterruptedException { + msgStr = """ + {} + """; + decoderStr = String.format(""" + var list = []; + list.add(0x35); + return isList(list); + """); + Object actual = invokeScript(evalScript(decoderStr), msgStr); + assertInstanceOf(Boolean.class, actual); + assertTrue((Boolean) actual); + decoderStr = String.format(""" + var list = []; + list.add(0x35); + return isMap(list); + """); + actual = invokeScript(evalScript(decoderStr), msgStr); + assertInstanceOf(Boolean.class, actual); + assertFalse((Boolean) actual); + decoderStr = String.format(""" + var list = []; + list.add(0x35); + return isArray(list); + """); + actual = invokeScript(evalScript(decoderStr), msgStr); + assertInstanceOf(Boolean.class, actual); + assertFalse((Boolean) actual); + } + + @Test + public void isArray_Test() throws ExecutionException, InterruptedException { + msgStr = """ + {} + """; + decoderStr = """ + var array = new int[3]; + array[0] = 1; + array[1] = 2; + array[2] = 3; + return isArray(array); + """; + Object actual = invokeScript(evalScript(decoderStr), msgStr); + assertInstanceOf(Boolean.class, actual); + assertTrue((Boolean) actual); + decoderStr = """ + var array = new int[3]; + array[0] = 1; + array[1] = 2; + array[2] = 3; + return isList(array); + """; + actual = invokeScript(evalScript(decoderStr), msgStr); + assertInstanceOf(Boolean.class, actual); + assertFalse((Boolean) actual); + } + private List splice(List oldList, int start, int deleteCount, Object... values) { start = initStartIndex(oldList, start); deleteCount = deleteCount < 0 ? 0 : Math.min(deleteCount, (oldList.size() - start)); diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index 72792c1093..b782040a99 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -380,6 +380,12 @@ public class TbUtils { double.class, double.class, String.class))); parserConfig.addImport("isInsideCircle", new MethodStub(TbUtils.class.getMethod("isInsideCircle", double.class, double.class, String.class))); + parserConfig.addImport("isMap", new MethodStub(TbUtils.class.getMethod("isMap", + Object.class))); + parserConfig.addImport("isList", new MethodStub(TbUtils.class.getMethod("isList", + Object.class))); + parserConfig.addImport("isArray", new MethodStub(TbUtils.class.getMethod("isArray", + Object.class))); } public static String btoa(String input) { @@ -1462,6 +1468,19 @@ public class TbUtils { return range > GeoUtil.distance(entityCoordinates, perimeterCoordinates, rangeUnit); } + public static boolean isMap(Object obj) { + return obj instanceof Map; + + } + + public static boolean isList(Object obj) { + return obj instanceof List; + } + + public static boolean isArray(Object obj) { + return obj != null && obj.getClass().isArray(); + } + private static byte isValidIntegerToByte(Integer val) { if (val > 255 || val < -128) { throw new NumberFormatException("The value '" + val + "' could not be correctly converted to a byte. " + diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java index 1063a8e8de..6d793f2c8d 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java @@ -38,11 +38,16 @@ import java.util.Arrays; import java.util.Base64; import java.util.Calendar; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Random; +import java.util.concurrent.ExecutionException; import static java.lang.Character.MAX_RADIX; import static java.lang.Character.MIN_RADIX; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; @Slf4j public class TbUtilsTest { @@ -315,7 +320,7 @@ public class TbUtilsTest { TbUtils.parseBytesToFloat(floatValByte, 0, 4, true); Assertions.fail("Should throw NumberFormatException"); } catch (RuntimeException e) { - Assertions.assertTrue(e.getMessage().contains(message)); + assertTrue(e.getMessage().contains(message)); } // "01752B0367FA000500010488 FFFFFFFF FFFFFFFF 33"; @@ -325,7 +330,7 @@ public class TbUtilsTest { TbUtils.parseBytesToFloat(floatValList, 12, 4, false); Assertions.fail("Should throw NumberFormatException"); } catch (RuntimeException e) { - Assertions.assertTrue(e.getMessage().contains(message)); + assertTrue(e.getMessage().contains(message)); } } @@ -385,7 +390,7 @@ public class TbUtilsTest { TbUtils.parseBytesIntToFloat(byteAT101, byteAT101.size() + 1); Assertions.fail("Should throw NumberFormatException"); } catch (RuntimeException e) { - Assertions.assertTrue(e.getMessage().contains("is out of bounds for array with length:")); + assertTrue(e.getMessage().contains("is out of bounds for array with length:")); } } @@ -499,7 +504,7 @@ public class TbUtilsTest { TbUtils.parseBytesToDouble(doubleValByte, 0, 8, true); Assertions.fail("Should throw NumberFormatException"); } catch (RuntimeException e) { - Assertions.assertTrue(e.getMessage().contains(message)); + assertTrue(e.getMessage().contains(message)); } } @@ -601,20 +606,20 @@ public class TbUtilsTest { String actualStr = TbUtils.bytesToString(listHex); byte[] actualBytes = actualStr.getBytes(); Assertions.assertArrayEquals(expectedBytes, actualBytes); - Assertions.assertTrue(actualStr.isBlank()); + assertTrue(actualStr.isBlank()); listHex = new ArrayList<>(Arrays.asList("0x21", "0x21")); expectedBytes = new byte[]{33, 33}; actualStr = TbUtils.bytesToString(listHex); actualBytes = actualStr.getBytes(); Assertions.assertArrayEquals(expectedBytes, actualBytes); - Assertions.assertFalse(actualStr.isBlank()); + assertFalse(actualStr.isBlank()); Assertions.assertEquals("!!", actualStr); listHex = new ArrayList<>(Arrays.asList("21", "0x21")); expectedBytes = new byte[]{21, 33}; actualStr = TbUtils.bytesToString(listHex); actualBytes = actualStr.getBytes(); Assertions.assertArrayEquals(expectedBytes, actualBytes); - Assertions.assertFalse(actualStr.isBlank()); + assertFalse(actualStr.isBlank()); Assertions.assertEquals("!", actualStr.substring(1)); Assertions.assertEquals('\u0015', actualStr.charAt(0)); Assertions.assertEquals(21, actualStr.charAt(0)); @@ -628,7 +633,7 @@ public class TbUtilsTest { TbUtils.bytesToString(listHex); Assertions.fail("Should throw NumberFormatException"); } catch (NumberFormatException e) { - Assertions.assertTrue(e.getMessage().contains("Value: \"FG\" is not numeric or hexDecimal format!")); + assertTrue(e.getMessage().contains("Value: \"FG\" is not numeric or hexDecimal format!")); } List listIntString = new ArrayList<>(); @@ -637,7 +642,7 @@ public class TbUtilsTest { TbUtils.bytesToString(listIntString); Assertions.fail("Should throw NumberFormatException"); } catch (NumberFormatException e) { - Assertions.assertTrue(e.getMessage().contains("The value '-129' could not be correctly converted to a byte. " + + assertTrue(e.getMessage().contains("The value '-129' could not be correctly converted to a byte. " + "Integer to byte conversion requires the use of only 8 bits (with a range of min/max = -128/255)!")); } @@ -646,7 +651,7 @@ public class TbUtilsTest { TbUtils.bytesToString(listIntString); Assertions.fail("Should throw NumberFormatException"); } catch (NumberFormatException e) { - Assertions.assertTrue(e.getMessage().contains("The value '256' could not be correctly converted to a byte. " + + assertTrue(e.getMessage().contains("The value '256' could not be correctly converted to a byte. " + "Integer to byte conversion requires the use of only 8 bits (with a range of min/max = -128/255)!")); } @@ -656,7 +661,7 @@ public class TbUtilsTest { TbUtils.bytesToString(listIntBytes); Assertions.fail("Should throw NumberFormatException"); } catch (NumberFormatException e) { - Assertions.assertTrue(e.getMessage().contains("The value '-129' could not be correctly converted to a byte. " + + assertTrue(e.getMessage().contains("The value '-129' could not be correctly converted to a byte. " + "Integer to byte conversion requires the use of only 8 bits (with a range of min/max = -128/255)!")); } @@ -665,7 +670,7 @@ public class TbUtilsTest { TbUtils.bytesToString(listIntBytes); Assertions.fail("Should throw NumberFormatException"); } catch (NumberFormatException e) { - Assertions.assertTrue(e.getMessage().contains("The value '256' could not be correctly converted to a byte. " + + assertTrue(e.getMessage().contains("The value '256' could not be correctly converted to a byte. " + "Integer to byte conversion requires the use of only 8 bits (with a range of min/max = -128/255)!")); } @@ -677,7 +682,7 @@ public class TbUtilsTest { TbUtils.bytesToString(listObjects); Assertions.fail("Should throw NumberFormatException"); } catch (NumberFormatException e) { - Assertions.assertTrue(e.getMessage().contains("The value '[0xFD]' could not be correctly converted to a byte. " + + assertTrue(e.getMessage().contains("The value '[0xFD]' could not be correctly converted to a byte. " + "Must be a HexDecimal/String/Integer/Byte format !")); } } @@ -798,25 +803,25 @@ public class TbUtilsTest { input = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF3"; TbUtils.hexToBytes(ctx, input); } catch (IllegalArgumentException e) { - Assertions.assertTrue(e.getMessage().contains("Hex string must be even-length.")); + assertTrue(e.getMessage().contains("Hex string must be even-length.")); } try { input = "0x01752B0367KA000500010488FFFFFFFFFFFFFFFF33"; TbUtils.hexToBytes(ctx, input); } catch (NumberFormatException e) { - Assertions.assertTrue(e.getMessage().contains("Value: \"" + input + "\" is not numeric or hexDecimal format!")); + assertTrue(e.getMessage().contains("Value: \"" + input + "\" is not numeric or hexDecimal format!")); } try { input = ""; TbUtils.hexToBytes(ctx, input); } catch (IllegalArgumentException e) { - Assertions.assertTrue(e.getMessage().contains("Hex string must be not empty")); + assertTrue(e.getMessage().contains("Hex string must be not empty")); } try { input = null; TbUtils.hexToBytes(ctx, input); } catch (IllegalArgumentException e) { - Assertions.assertTrue(e.getMessage().contains("Hex string must be not empty")); + assertTrue(e.getMessage().contains("Hex string must be not empty")); } } @@ -905,14 +910,14 @@ public class TbUtilsTest { TbUtils.raiseError(message); Assertions.fail("Should throw NumberFormatException"); } catch (RuntimeException e) { - Assertions.assertTrue(e.getMessage().contains("frequency_weighting_type must be 0, 1 or 2. A value of 4 is invalid.")); + assertTrue(e.getMessage().contains("frequency_weighting_type must be 0, 1 or 2. A value of 4 is invalid.")); } message = "frequency_weighting_type must be 0, 1 or 2."; try { TbUtils.raiseError(message); Assertions.fail("Should throw NumberFormatException"); } catch (RuntimeException e) { - Assertions.assertTrue(e.getMessage().contains("frequency_weighting_type must be 0, 1 or 2.")); + assertTrue(e.getMessage().contains("frequency_weighting_type must be 0, 1 or 2.")); } } @@ -1114,7 +1119,7 @@ public class TbUtilsTest { String emptyInput = Base64.getEncoder().encodeToString(new byte[]{}); actual = TbUtils.base64ToBytesList(ctx, emptyInput); - Assertions.assertTrue(actual.isEmpty()); + assertTrue(actual.isEmpty()); String invalidInput = "NotAValidBase64String"; Assertions.assertThrows(IllegalArgumentException.class, () -> { TbUtils.base64ToBytesList(ctx, invalidInput); @@ -1146,28 +1151,50 @@ public class TbUtilsTest { @Test public void isNaN() { - Assertions.assertFalse(TbUtils.isNaN(doubleVal)); - Assertions.assertTrue(TbUtils.isNaN(Double.NaN)); + assertFalse(TbUtils.isNaN(doubleVal)); + assertTrue(TbUtils.isNaN(Double.NaN)); } @Test public void isInsidePolygon() { // outside the polygon String perimeter = "[[[50.75581142688204,29.097910166341073],[50.16785158177623,29.35066098977171],[50.164329922384674,29.773743889862114],[50.16785158177623,30.801230932938843],[50.459245308833495,30.92760634465418],[50.486522489629564,30.68548421850448],[50.703612031034005,30.872660513473573]],[[50.606017492632766,29.36165015600782],[50.54317104075835,29.762754723626013],[50.41021974600505,29.455058069014804]]]"; - Assertions.assertFalse(TbUtils.isInsidePolygon(50.50869555168039, 30.80123093293884, perimeter)); + assertFalse(TbUtils.isInsidePolygon(50.50869555168039, 30.80123093293884, perimeter)); // inside the polygon - Assertions.assertTrue(TbUtils.isInsidePolygon(50.50520628167696, 30.339685951022016, perimeter)); + assertTrue(TbUtils.isInsidePolygon(50.50520628167696, 30.339685951022016, perimeter)); // inside the hole - Assertions.assertFalse(TbUtils.isInsidePolygon(50.52265651287081, 29.488025567723156, perimeter)); + assertFalse(TbUtils.isInsidePolygon(50.52265651287081, 29.488025567723156, perimeter)); } @Test public void isInsideCircle() { // outside the circle String perimeter = "{\"latitude\":50.32254778825905,\"longitude\":28.207787701215757,\"radius\":47477.33130420423}"; - Assertions.assertFalse(TbUtils.isInsideCircle(50.81490715736681, 28.05943395702824, perimeter)); + assertFalse(TbUtils.isInsideCircle(50.81490715736681, 28.05943395702824, perimeter)); // inside the circle - Assertions.assertTrue(TbUtils.isInsideCircle(50.599397971892444, 28.086906872618542, perimeter)); + assertTrue(TbUtils.isInsideCircle(50.599397971892444, 28.086906872618542, perimeter)); + } + + @Test + public void isMap() throws ExecutionException, InterruptedException { + LinkedHashMap msg = new LinkedHashMap<>(Map.of("temperature", 42, "nested", "508")); + assertTrue(TbUtils.isMap(msg)); + assertFalse(TbUtils.isList(msg)); + } + + @Test + public void isList() throws ExecutionException, InterruptedException { + List liat = List.of(0x35); + assertTrue(TbUtils.isList(liat)); + assertFalse(TbUtils.isMap(liat)); + assertFalse(TbUtils.isArray(liat)); + } + + @Test + public void isArray() throws ExecutionException, InterruptedException { + byte [] array = new byte[]{1, 2, 3}; + assertTrue(TbUtils.isArray(array)); + assertFalse(TbUtils.isList(array)); } private static List toList(byte[] data) { From 59f23ba25d27fd626a14a738c07d9796752b83ce Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 14 May 2025 13:32:51 +0300 Subject: [PATCH 173/335] UI: Add unit conversion in dashboard filter --- .../filter-user-info-dialog.component.html | 44 ++++++++----- .../filter-user-info-dialog.component.ts | 28 +++++---- .../filter/user-filter-dialog.component.html | 6 +- .../filter/user-filter-dialog.component.scss | 30 +++++++++ .../filter/user-filter-dialog.component.ts | 62 ++++++++++--------- .../app/shared/models/query/query.models.ts | 9 ++- .../assets/locale/locale.constant-en_US.json | 8 ++- 7 files changed, 127 insertions(+), 60 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.scss diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html index 68e3489866..f3e540f3f6 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html @@ -26,24 +26,36 @@
-
- - {{ 'filter.editable' | translate }} - -
- - filter.display-label - +
+
+ +
+ {{ 'filter.editable' | translate }} +
+
+
+
+ +
+ {{ 'filter.custom-label' | translate }} +
+
+ + + +
+
+
filter.order-priority
+ + - - {{ 'filter.autogenerated-label' | translate }} -
- - filter.order-priority - - -
+
+
filter.unit
+ + +
+
@@ -93,7 +93,7 @@ type="button" *ngIf="!disabled" (click)="applyUnitSettings()" - [disabled]="convertUnitForm.invalid || convertUnitForm.pristine"> + [disabled]="unitSettingForm.invalid || unitSettingForm.pristine"> {{ 'action.apply' | translate }}
diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss b/ui-ngx/src/app/shared/components/unit-settings-panel.component.scss similarity index 100% rename from ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss rename to ui-ngx/src/app/shared/components/unit-settings-panel.component.scss diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/unit-settings-panel.component.ts similarity index 56% rename from ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts rename to ui-ngx/src/app/shared/components/unit-settings-panel.component.ts index 4684d2b68d..f378ec59f0 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts +++ b/ui-ngx/src/app/shared/components/unit-settings-panel.component.ts @@ -14,26 +14,23 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { AllMeasures, isNotEmptyTbUnits, TbUnit, UnitInfo, UnitSystem } from '@shared/models/unit.models'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { AllMeasures, TbUnit, UnitInfo, UnitSystem } from '@shared/models/unit.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { FormBuilder, Validators } from '@angular/forms'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { UnitService } from '@core/services/unit.service'; -import { debounceTime, first } from 'rxjs/operators'; +import { debounceTime } from 'rxjs/operators'; import { isUndefinedOrNull } from '@core/utils'; -import type { UnitInputComponent } from '@shared/components/unit-input.component'; @Component({ selector: 'tb-covert-unit-settings-panel', - templateUrl: './convert-unit-settings-panel.component.html', - styleUrls: ['./convert-unit-settings-panel.component.scss'], + templateUrl: './unit-settings-panel.component.html', + styleUrls: ['./unit-settings-panel.component.scss'], providers: [], encapsulation: ViewEncapsulation.None }) -export class ConvertUnitSettingsPanelComponent implements OnInit { - - @ViewChild('unitFrom', {static: true}) unitFrom: UnitInputComponent; +export class UnitSettingsPanelComponent implements OnInit { @Input() unit: TbUnit; @@ -57,7 +54,7 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { targetMeasure: AllMeasures; - convertUnitForm = this.fb.group({ + unitSettingForm = this.fb.group({ from: [''], convertUnit: [true], METRIC: [''], @@ -66,32 +63,32 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { }) constructor( - private popover: TbPopoverComponent, + private popover: TbPopoverComponent, private fb: FormBuilder, private unitService: UnitService ) { - this.convertUnitForm.get('from').valueChanges.pipe( + this.unitSettingForm.get('from').valueChanges.pipe( debounceTime(200), takeUntilDestroyed() ).subscribe(unit => { const unitDescription = this.unitService.getUnitInfo(unit); const units = unitDescription ? this.unitService.getUnits(unitDescription.measure) : []; if (unitDescription && units.length > 1) { - this.convertUnitForm.get('convertUnit').enable({emitEvent: true}); + this.unitSettingForm.get('convertUnit').enable({emitEvent: true}); this.targetMeasure = unitDescription.measure; if (unitDescription.system === UnitSystem.IMPERIAL) { - this.convertUnitForm.get('METRIC').setValue(this.unitService.getDefaultUnit(this.targetMeasure, UnitSystem.METRIC), {emitEvent: false}); - this.convertUnitForm.get('IMPERIAL').setValue(unit, {emitEvent: false}); - this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false}); + this.unitSettingForm.get('METRIC').setValue(this.unitService.getDefaultUnit(this.targetMeasure, UnitSystem.METRIC), {emitEvent: false}); + this.unitSettingForm.get('IMPERIAL').setValue(unit, {emitEvent: false}); + this.unitSettingForm.get('HYBRID').setValue(unit, {emitEvent: false}); } else { - this.convertUnitForm.get('METRIC').setValue(unit, {emitEvent: false}); - this.convertUnitForm.get('IMPERIAL').setValue(this.unitService.getDefaultUnit(this.targetMeasure, UnitSystem.IMPERIAL), {emitEvent: false}); - this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false}); + this.unitSettingForm.get('METRIC').setValue(unit, {emitEvent: false}); + this.unitSettingForm.get('IMPERIAL').setValue(this.unitService.getDefaultUnit(this.targetMeasure, UnitSystem.IMPERIAL), {emitEvent: false}); + this.unitSettingForm.get('HYBRID').setValue(unit, {emitEvent: false}); } } else { - this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); - this.convertUnitForm.get('convertUnit').disable({emitEvent: false}); - this.convertUnitForm.patchValue({ + this.unitSettingForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.unitSettingForm.get('convertUnit').disable({emitEvent: false}); + this.unitSettingForm.patchValue({ METRIC: '', IMPERIAL: '', HYBRID: '' @@ -99,17 +96,17 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { } }) - this.convertUnitForm.get('convertUnit').valueChanges.pipe( + this.unitSettingForm.get('convertUnit').valueChanges.pipe( takeUntilDestroyed() ).subscribe(value => { if (value) { - this.convertUnitForm.get('METRIC').enable({emitEvent: false}); - this.convertUnitForm.get('IMPERIAL').enable({emitEvent: false}); - this.convertUnitForm.get('HYBRID').enable({emitEvent: false}); + this.unitSettingForm.get('METRIC').enable({emitEvent: false}); + this.unitSettingForm.get('IMPERIAL').enable({emitEvent: false}); + this.unitSettingForm.get('HYBRID').enable({emitEvent: false}); } else { - this.convertUnitForm.get('METRIC').disable({emitEvent: false}); - this.convertUnitForm.get('IMPERIAL').disable({emitEvent: false}); - this.convertUnitForm.get('HYBRID').disable({emitEvent: false}); + this.unitSettingForm.get('METRIC').disable({emitEvent: false}); + this.unitSettingForm.get('IMPERIAL').disable({emitEvent: false}); + this.unitSettingForm.get('HYBRID').disable({emitEvent: false}); } }); } @@ -117,27 +114,27 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { ngOnInit() { let unitDescription: UnitInfo; if (this.required) { - this.convertUnitForm.get('from').setValidators(Validators.required); + this.unitSettingForm.get('from').setValidators(Validators.required); } if (typeof this.unit === 'string') { - this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); - this.convertUnitForm.get('from').setValue(this.unit); + this.unitSettingForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.unitSettingForm.get('from').setValue(this.unit); unitDescription = this.unitService.getUnitInfo(this.unit); } else if (isUndefinedOrNull(this.unit)) { - this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); - this.convertUnitForm.get('from').setValue(null); + this.unitSettingForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.unitSettingForm.get('from').setValue(null); } else { - this.convertUnitForm.patchValue(this.unit, {emitEvent: false}); + this.unitSettingForm.patchValue(this.unit, {emitEvent: false}); unitDescription = this.unitService.getUnitInfo(this.unit.from); } if (unitDescription?.measure) { this.targetMeasure = unitDescription.measure; } else { - this.convertUnitForm.get('convertUnit').disable({emitEvent: false}); + this.unitSettingForm.get('convertUnit').disable({emitEvent: false}); } if (this.disabled) { - this.convertUnitForm.disable({emitEvent: false}); + this.unitSettingForm.disable({emitEvent: false}); } } @@ -150,12 +147,12 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { } applyUnitSettings() { - if (this.convertUnitForm.value.convertUnit) { - const formValue = this.convertUnitForm.value; + if (this.unitSettingForm.value.convertUnit) { + const formValue = this.unitSettingForm.value; delete formValue.convertUnit; this.unitSettingsApplied.emit(formValue as TbUnit); } else { - this.unitSettingsApplied.emit(this.convertUnitForm.value.from); + this.unitSettingsApplied.emit(this.unitSettingForm.value.from); } } } diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 1f8ac63926..47a65631db 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -185,7 +185,7 @@ import { ToggleHeaderComponent, ToggleOption } from '@shared/components/toggle-h import { RuleChainSelectComponent } from '@shared/components/rule-chain/rule-chain-select.component'; import { ToggleSelectComponent } from '@shared/components/toggle-select.component'; import { UnitInputComponent } from '@shared/components/unit-input.component'; -import { ConvertUnitSettingsPanelComponent } from '@shared/components/convert-unit-settings-panel.component'; +import { UnitSettingsPanelComponent } from '@shared/components/unit-settings-panel.component'; import { MaterialIconsComponent } from '@shared/components/material-icons.component'; import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component'; import { TbIconComponent } from '@shared/components/icon.component'; @@ -414,7 +414,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) ToggleOption, ToggleSelectComponent, UnitInputComponent, - ConvertUnitSettingsPanelComponent, + UnitSettingsPanelComponent, StringAutocompleteComponent, MaterialIconsComponent, RuleChainSelectComponent, diff --git a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json index 2fd045dad0..61de661ac9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json @@ -3105,7 +3105,6 @@ "filter-user-params": "معلمات مستخدم الفلتر الفعلي", "user-parameters": "معلمات المستخدم", "display-label": "تسمية للعرض", - "autogenerated-label": "إنشاء تسمية تلقائية", "order-priority": "أولوية ترتيب الحقل", "key-filter": "فلتر المفتاح", "key-filters": "فلاتر المفاتيح", diff --git a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json index d986a24970..8633400394 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json @@ -2637,7 +2637,6 @@ "filter-user-params": "Filtre de paràmetres d'usuari (predicado)", "user-parameters": "Paràmetres d'usuari", "display-label": "Etiqueta a mostrar", - "autogenerated-label": "Auto generar etiqueta", "order-priority": "Prioritat orden de campos", "key-filter": "Filtres (clau)", "key-filters": "Filtres (claus)", diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index ab3f6cba56..041e3efe56 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -1972,7 +1972,6 @@ "filter-user-params": "Filtr predikátu parametrů uživatele", "user-parameters": "Parametry uživatele", "display-label": "Zobrazované označení", - "autogenerated-label": "Automaticky vygenerovat označení", "order-priority": "Priority pořadí polí", "key-filter": "Klíčový filtr", "key-filters": "Klíčové filtry", diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index 4759b873b4..e96ec45e3e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -1912,7 +1912,6 @@ "filter-user-params": "Filtrerprædikat for brugerparametre", "user-parameters": "Brugerparametre", "display-label": "Etiket, der skal vises", - "autogenerated-label": "Generer automatisk etiket", "order-priority": "Prioritet af feltrækkefølge", "key-filter": "Nøglefilter", "key-filters": "Nøglefiltre", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b2a772eb93..7163e81240 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2991,7 +2991,6 @@ "filter-user-params": "Filter predicate user parameters", "user-parameters": "User parameters", "display-label": "Label to display", - "autogenerated-label": "Auto generate label", "custom-label": "Custom label", "custom-label-hint": "Enable to set your own label for the filter. When disabled, a label will be generated automatically.", "order-priority": "Display order", @@ -5856,8 +5855,8 @@ "background-blur": "Background blur" }, "unit": { - "conversion": { - "set-unit-conversion": "Set unit conversion", + "set-unit-conversion": "Set unit conversion", + "unit-settings": { "unit-settings": "Unit settings", "source-unit": "Source unit", "source-unit-hint": "This is the unit of the stored value. The unit you’re converting from. Enter the symbol your source data uses (e.g. m, km, ft, in).", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 8b7f07d789..e1329e657d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -2573,7 +2573,6 @@ "filter-user-params": "Filtro de parámetros de usuario (predicado)", "user-parameters": "Parámetros de usuario", "display-label": "Etiqueta a mostrar", - "autogenerated-label": "Auto generar etiqueta", "order-priority": "Prioridad orden de campos", "key-filter": "Filtros (clave)", "key-filters": "Filtros (claves)", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index ef1a09c00f..a122a04ee7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -1535,7 +1535,6 @@ "filter-user-params": "Filter predicate user parameters", "user-parameters": "User parameters", "display-label": "Label to display", - "autogenerated-label": "Auto generate label", "order-priority": "Field order priority", "key-filter": "Key filter", "key-filters": "Key filters", diff --git a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json index 73ed884662..d3cc6f6c47 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json +++ b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json @@ -3073,7 +3073,6 @@ "filter-user-params": "Filter predicate user parameters", "user-parameters": "User parameters", "display-label": "Label to display", - "autogenerated-label": "Auto generate label", "order-priority": "Field order priority", "key-filter": "Key filter", "key-filters": "Key filters", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index e64970bca3..f4951582b5 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -2992,7 +2992,6 @@ "filter-user-params": "Gebruikersparameters voor predicaten filteren", "user-parameters": "Parameters van de gebruiker", "display-label": "Label om weer te geven", - "autogenerated-label": "Automatisch label genereren", "order-priority": "Prioriteit voor veldorders", "key-filter": "Toets filter", "key-filters": "Belangrijkste filters", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 26e7e23d63..80b65b83f9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -3082,7 +3082,6 @@ "filter-user-params": "Filtruj parametry użytkownika predykatu", "user-parameters": "Parametry użytkownika", "display-label": "Etykieta do wyświetlenia", - "autogenerated-label": "Automatyczne generowanie etykiety", "order-priority": "Priorytet zamówienia pola", "key-filter": "Filtr kluczowy", "key-filters": "Kluczowe filtry", diff --git a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json index eb20180447..a528be78eb 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json +++ b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json @@ -1212,7 +1212,6 @@ "edit-filter-user-params": "Editar parâmetros de usuário de predicado de filtro", "user-parameters": "Parâmetros de usuário", "display-label": "Etiqueta para exibição", - "autogenerated-label": "Gerar etiqueta automaticamente", "order-priority": "Prioridade de ordem de campo", "key-filter": "Filtro chave", "key-filters": "Filtros chave", diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index b667fd0b46..41dea2e169 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -1535,7 +1535,6 @@ "filter-user-params": "Filter predicate user parameters", "user-parameters": "Uporabniški parametri", "display-label": "Oznaka za prikaz", - "autogenerated-label": "Samodejno ustvari oznako", "order-priority": "Prednostni vrstni red", "key-filter": "Ključni filter", "key-filters": "Ključni filtri", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index 98998ab312..1af97591a4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -1987,7 +1987,6 @@ "filter-user-params": "Filtre belirteci kullanıcı parametreleri", "user-parameters": "Kullanıcı parametreleri", "display-label": "Görüntülenecek etiket", - "autogenerated-label": "Otomatik etiket oluştur", "order-priority": "Alan sırası önceliği", "key-filter": "Anahtar filtresi", "key-filters": "Anahtar filtreleri", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index aaf69bb984..770f5df355 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -2878,7 +2878,6 @@ "filter-user-params": "过滤谓词用户参数", "user-parameters": "用户参数", "display-label": "要显示的标签", - "autogenerated-label": "自动生成标签", "order-priority": "字段顺序优先级", "key-filter": "键名筛选器", "key-filters": "键名筛选器", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json index 599da1b41e..9e2246b83d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json @@ -2198,7 +2198,6 @@ "filter-user-params": "過濾謂詞用戶參數", "user-parameters": "用戶參數", "display-label": "要顯示的標籤", - "autogenerated-label": "自動生成標籤", "order-priority": "字段順序優先級", "key-filter": "關鍵過濾器", "key-filters": "關鍵過濾器", From cb2ab78186f3a13e4408a0dccd3b0b07e3ab5bbf Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 14 May 2025 17:41:36 +0300 Subject: [PATCH 175/335] lwm2m: comments - 1 --- .../rpc/AbstractRpcLwM2MIntegrationTest.java | 4 +- .../sql/RpcLwm2mIntegrationObserveTest.java | 8 +-- .../lwm2m/TelemetryObserveStrategy.java | 2 +- ...esource.java => ResourceUpdateResult.java} | 8 +-- .../uplink/DefaultLwM2mUplinkMsgHandler.java | 62 +++++++++++-------- 5 files changed, 47 insertions(+), 37 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/{ResultUpdateResource.java => ResourceUpdateResult.java} (84%) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index fe571eb9b3..be85294f08 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -29,7 +29,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; -import org.thingsboard.server.transport.lwm2m.server.client.ResultUpdateResource; +import org.thingsboard.server.transport.lwm2m.server.client.ResourceUpdateResult; import java.util.List; import java.util.Set; @@ -259,7 +259,7 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg .filter(invocation -> invocation.getMethod().getName().equals("updateAttrTelemetry") && invocation.getArguments().length > 1 && - ((ResultUpdateResource)invocation.getArguments()[0]).getPaths().toString().contains(idVerRez) + ((ResourceUpdateResult)invocation.getArguments()[0]).getPaths().toString().contains(idVerRez) ) .count(); } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java index 1ba89720ae..483442382d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java @@ -24,7 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; -import org.thingsboard.server.transport.lwm2m.server.client.ResultUpdateResource; +import org.thingsboard.server.transport.lwm2m.server.client.ResourceUpdateResult; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.junit.Assert.assertEquals; @@ -80,7 +80,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT int cntUpdate = 3; verify(defaultUplinkMsgHandlerTest, timeout(10000).atLeast(cntUpdate)) - .updateAttrTelemetry(Mockito.any(ResultUpdateResource.class), eq(null)); + .updateAttrTelemetry(Mockito.any(ResourceUpdateResult.class), eq(null)); } /** @@ -95,7 +95,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT int cntUpdate = 3; verify(defaultUplinkMsgHandlerTest, timeout(10000).atLeast(cntUpdate)) - .updateAttrTelemetry(Mockito.any(ResultUpdateResource.class), eq(null)); + .updateAttrTelemetry(Mockito.any(ResourceUpdateResult.class), eq(null)); } /** @@ -328,7 +328,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT int cntUpdate = 10; verify(defaultUplinkMsgHandlerTest, timeout(50000).atLeast(cntUpdate)) - .updateAttrTelemetry(Mockito.any(ResultUpdateResource.class), eq(null)); + .updateAttrTelemetry(Mockito.any(ResourceUpdateResult.class), eq(null)); } private void sendRpcObserveWithWithTwoResource(String expectedId_1, String expectedId_2) throws Exception { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java index 264d883353..c3e525633f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/TelemetryObserveStrategy.java @@ -40,7 +40,7 @@ public enum TelemetryObserveStrategy { return strategy; } } - return null; + throw new IllegalArgumentException("Unknown TelemetryObserveStrategy id: " + description); } public static TelemetryObserveStrategy fromId(int id) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultUpdateResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceUpdateResult.java similarity index 84% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultUpdateResource.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceUpdateResult.java index ffbf4595fb..0a47d211b5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultUpdateResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceUpdateResult.java @@ -23,11 +23,11 @@ import java.util.Set; @Data @AllArgsConstructor -public class ResultUpdateResource { - LwM2mClient lwM2MClient; - Set paths; +public class ResourceUpdateResult { + private LwM2mClient lwM2MClient; + private Set paths; - public ResultUpdateResource(LwM2mClient lwM2MClient) { + public ResourceUpdateResult(LwM2mClient lwM2MClient) { this.lwM2MClient = lwM2MClient; this.paths = new HashSet<>(); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index 5897533539..37f7829c12 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -78,7 +78,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; -import org.thingsboard.server.transport.lwm2m.server.client.ResultUpdateResource; +import org.thingsboard.server.transport.lwm2m.server.client.ResourceUpdateResult; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; @@ -124,7 +124,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.COMPOSITE_ALL; import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.COMPOSITE_BY_OBJECT; import static org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryObserveStrategy.SINGLE; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; @@ -324,7 +323,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, modelProvider); if (objectModelVersion != null) { - ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); + ResourceUpdateResult updateResource = new ResourceUpdateResult(lwM2MClient); int responseCode = response.getCode().getCode(); if (content instanceof LwM2mObject) { this.updateObjectResourceValue(updateResource, (LwM2mObject) content, path, responseCode); @@ -343,7 +342,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl log.trace("ReadCompositeResponse before onUpdateValueAfterReadCompositeResponse: [{}]", response); if (response.getContent() != null) { LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); - ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); + ResourceUpdateResult updateResource = new ResourceUpdateResult(lwM2MClient); response.getContent().forEach((k, v) -> { if (v != null) { int responseCode = response.getCode().getCode(); @@ -386,7 +385,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path.toString(), modelProvider); if (objectModelVersion != null) { - ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); + ResourceUpdateResult updateResource = new ResourceUpdateResult(lwM2MClient); if (node instanceof LwM2mObject) { this.updateObjectResourceValue(updateResource, (LwM2mObject) node, path.toString(), 0); } else if (node instanceof LwM2mObjectInstance) { @@ -518,19 +517,30 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); if (!targetIds.isEmpty()) { TelemetryObserveStrategy observeStrategy = profile.getObserveAttr().getObserveStrategy(); - if (SINGLE.equals(observeStrategy)) { - CountDownLatch latch = new CountDownLatch(targetIds.size()); - targetIds.forEach(targetId -> sendObserveRequest(lwM2MClient, targetId, - new TbLwM2MLatchCallback<>(latch, new TbLwM2MObserveCallback(this, logService, lwM2MClient, targetId)))); - latch.await(config.getTimeout(), TimeUnit.MILLISECONDS); - } else if (COMPOSITE_ALL.equals(observeStrategy)) { - String[] versionedIds = targetIds.toArray(new String[0]); - sendObserveCompositeRequest(lwM2MClient, versionedIds); - } else if (COMPOSITE_BY_OBJECT.equals(observeStrategy)) { - Map versionedObjectIds = groupByObjectIdVersionedIds(targetIds); - CountDownLatch latch = new CountDownLatch(versionedObjectIds.size()); - versionedObjectIds.forEach((k, v)-> sendObserveCompositeRequest(lwM2MClient, v)); - latch.await(config.getTimeout(), TimeUnit.MILLISECONDS); + long timeoutMs = config.getTimeout(); + switch (observeStrategy) { + case SINGLE -> { + CountDownLatch latch = new CountDownLatch(targetIds.size()); + targetIds.forEach(targetId -> sendObserveRequest( + lwM2MClient, targetId, + new TbLwM2MLatchCallback<>(latch, new TbLwM2MObserveCallback(this, logService, lwM2MClient, targetId)) + )); + boolean completed = latch.await(timeoutMs, TimeUnit.MILLISECONDS); + if (!completed) log.trace("[{}] Timeout occurred during SINGLE observe init", lwM2MClient.getEndpoint()); + } + case COMPOSITE_ALL -> { + CountDownLatch latch = new CountDownLatch(targetIds.size()); + sendObserveCompositeRequest(lwM2MClient, targetIds.toArray(new String[0])); + boolean completed = latch.await(timeoutMs, TimeUnit.MILLISECONDS); + if (!completed) log.trace("[{}] Timeout occurred during COMPOSITE_ALL observe init", lwM2MClient.getEndpoint()); + } + case COMPOSITE_BY_OBJECT -> { + Map versionedObjectIds = groupByObjectIdVersionedIds(targetIds); + CountDownLatch latch = new CountDownLatch(versionedObjectIds.size()); + versionedObjectIds.forEach((k, v) -> sendObserveCompositeRequest(lwM2MClient, v)); + boolean completed = latch.await(timeoutMs, TimeUnit.MILLISECONDS); + if (!completed) log.trace("[{}] Timeout occurred during COMPOSITE_BY_OBJECT observe init", lwM2MClient.getEndpoint()); + } } } } catch (InterruptedException e) { @@ -586,7 +596,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveCallback(logService, client, versionedId)); } - private void updateObjectResourceValue(ResultUpdateResource updateResource, LwM2mObject lwM2mObject, String pathIdVer, int code) { + private void updateObjectResourceValue(ResourceUpdateResult updateResource, LwM2mObject lwM2mObject, String pathIdVer, int code) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); lwM2mObject.getInstances().forEach((instanceId, instance) -> { String pathInstance = pathIds.toString() + "/" + instanceId; @@ -594,7 +604,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl }); } - private void updateObjectInstanceResourceValue(ResultUpdateResource updateResource, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer, int code) { + private void updateObjectInstanceResourceValue(ResourceUpdateResult updateResource, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer, int code) { lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> { String pathRez = pathIdVer + "/" + resourceId; this.updateResourcesValue(updateResource, resource, pathRez, Mode.UPDATE, code); @@ -612,7 +622,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl * @param stringPath - resource * @param mode - Replace, Update */ - private void updateResourcesValue(ResultUpdateResource updateResource, LwM2mResource lwM2mResource, String stringPath, Mode mode, int code) { + private void updateResourcesValue(ResourceUpdateResult updateResource, LwM2mResource lwM2mResource, String stringPath, Mode mode, int code) { LwM2mClient lwM2MClient = updateResource.getLwM2MClient(); String path = convertObjectIdToVersionedId(stringPath, lwM2MClient); if (path != null && lwM2MClient.saveResourceValue(path, lwM2mResource, modelProvider, mode)) { @@ -660,7 +670,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl * * @param updateResource - updateResource resource of LwM2M Client */ - public void updateAttrTelemetry(ResultUpdateResource updateResource, Instant ts) { + public void updateAttrTelemetry(ResourceUpdateResult updateResource, Instant ts) { log.trace("UpdateAttrTelemetry paths [{}]", updateResource.getPaths()); try { ResultsAddKeyValueProto results = this.getParametersFromProfile(updateResource); @@ -707,7 +717,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl * * @param updateResource - updateResource resource of LwM2M Client */ - private ResultsAddKeyValueProto getParametersFromProfile(ResultUpdateResource updateResource) { + private ResultsAddKeyValueProto getParametersFromProfile(ResourceUpdateResult updateResource) { Registration registration = updateResource.getLwM2MClient().getRegistration(); Set paths = updateResource.getPaths(); ResultsAddKeyValueProto results = new ResultsAddKeyValueProto(); @@ -823,7 +833,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl @Override public void onWriteResponseOk(LwM2mClient lwM2MClient, String path, WriteRequest request, int code) { - ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); + ResourceUpdateResult updateResource = new ResourceUpdateResult(lwM2MClient); if (request.getNode() instanceof LwM2mResource) { this.updateResourcesValue(updateResource, ((LwM2mResource) request.getNode()), path, request.isReplaceRequest() ? Mode.REPLACE : Mode.UPDATE, code); } else if (request.getNode() instanceof LwM2mObjectInstance) { @@ -840,7 +850,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl @Override public void onCreatebjectInstancesResponseOk(LwM2mClient lwM2MClient, String versionId, CreateRequest request) { if (request.getObjectInstances() != null && !request.getObjectInstances().isEmpty()) { - ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); + ResourceUpdateResult updateResource = new ResourceUpdateResult(lwM2MClient); request.getObjectInstances().forEach(instance -> instance.getResources().forEach((resId, lwM2mResource) ->{ this.updateResourcesValue(updateResource, lwM2mResource, versionId + "/" + resId, Mode.REPLACE, 0); @@ -854,7 +864,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl @Override public void onWriteCompositeResponseOk(LwM2mClient lwM2MClient, WriteCompositeRequest request, int code) { log.trace("ReadCompositeResponse: [{}]", request.getNodes()); - ResultUpdateResource updateResource = new ResultUpdateResource(lwM2MClient); + ResourceUpdateResult updateResource = new ResourceUpdateResult(lwM2MClient); request.getNodes().forEach((k, v) -> { if (v instanceof LwM2mSingleResource) { this.updateResourcesValue(updateResource, (LwM2mResource) v, k.toString(), Mode.REPLACE, code); From 8dda445253f744a026ad1d89c1ffaa5eccc6be99 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 15 May 2025 11:11:40 +0300 Subject: [PATCH 176/335] Fix test whenTenantIsDeleted_thenCancelAllTheJobs --- .../thingsboard/server/service/job/JobManagerTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 85b963a196..a6e40333eb 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -27,13 +27,16 @@ import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.DummyJobConfiguration; import org.thingsboard.server.common.data.job.Job; +import org.thingsboard.server.common.data.job.JobFilter; import org.thingsboard.server.common.data.job.JobResult; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.DummyTaskResult; import org.thingsboard.server.common.data.job.task.DummyTaskResult.DummyTaskFailure; import org.thingsboard.server.common.data.notification.Notification; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.queue.task.JobStatsService; @@ -65,6 +68,9 @@ public class JobManagerTest extends AbstractControllerTest { @SpyBean private JobStatsService jobStatsService; + @Autowired + private JobService jobService; + @Before public void setUp() throws Exception { loginTenantAdmin(); @@ -250,7 +256,7 @@ public class JobManagerTest extends AbstractControllerTest { Thread.sleep(3000); verify(jobStatsService, never()).reportTaskResult(any(), any(), any()); - assertThat(findJobs()).isEmpty(); + assertThat(jobService.findJobsByFilter(tenantId, JobFilter.builder().build(), new PageLink(100)).getData()).isEmpty(); } @Test From bb3e828e3600ec89b36f56de464eacff12e6169d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 15 May 2025 11:21:17 +0300 Subject: [PATCH 177/335] UI: Migration from css.js to css.ts --- ui-ngx/src/app/core/css/css.js | 686 ------------------ ui-ngx/src/app/core/css/css.ts | 460 ++++++++++++ .../widget/lib/markdown-widget.component.ts | 4 +- 3 files changed, 462 insertions(+), 688 deletions(-) delete mode 100644 ui-ngx/src/app/core/css/css.js create mode 100644 ui-ngx/src/app/core/css/css.ts diff --git a/ui-ngx/src/app/core/css/css.js b/ui-ngx/src/app/core/css/css.js deleted file mode 100644 index 7490890fcb..0000000000 --- a/ui-ngx/src/app/core/css/css.js +++ /dev/null @@ -1,686 +0,0 @@ -/* - * Copyright © 2016-2025 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. - */ -/* eslint-disable */ - -/* jshint unused:false */ -/* global base64_decode, CSSWizardView, window, console, jQuery */ -var fi = function() { - - this.cssImportStatements = []; - this.cssKeyframeStatements = []; - - this.cssRegex = new RegExp("([\\s\\S]*?){([\\s\\S]*?)}", "gi"); - this.cssMediaQueryRegex = "((@media [\\s\\S]*?){([\\s\\S]*?}\\s*?)})"; - this.cssKeyframeRegex = "((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})"; - this.combinedCSSRegex = "((\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})"; //to match css & media queries together - this.cssCommentsRegex = "(\\/\\*[\\s\\S]*?\\*\\/)"; - this.cssImportStatementRegex = new RegExp("@import .*?;", "gi"); -}; - -/* - Strip outs css comments and returns cleaned css string - - @param css, the original css string to be stipped out of comments - - @return cleanedCSS contains no css comments - */ -fi.prototype.stripComments = function(cssString) { - var regex = new RegExp(this.cssCommentsRegex, "gi"); - - return cssString.replace(regex, ""); -}; - -/* - Parses given css string, and returns css object - keys as selectors and values are css rules - eliminates all css comments before parsing - - @param source css string to be parsed - - @return object css - */ -fi.prototype.parseCSS = function(source) { - - if (source === undefined) { - return []; - } - - var css = []; - //strip out comments - //source = this.stripComments(source); - - //get import statements - - while (true) { - var imports = this.cssImportStatementRegex.exec(source); - if (imports !== null) { - this.cssImportStatements.push(imports[0]); - css.push({ - selector: "@imports", - type: "imports", - styles: imports[0], - }); - } else { - break; - } - } - source = source.replace(this.cssImportStatementRegex, ""); - //get keyframe statements - var keyframesRegex = new RegExp(this.cssKeyframeRegex, "gi"); - var arr; - while (true) { - arr = keyframesRegex.exec(source); - if (arr === null) { - break; - } - css.push({ - selector: "@keyframes", - type: "keyframes", - styles: arr[0], - }); - } - source = source.replace(keyframesRegex, ""); - - //unified regex - var unified = new RegExp(this.combinedCSSRegex, "gi"); - - while (true) { - arr = unified.exec(source); - if (arr === null) { - break; - } - var selector = ""; - if (arr[2] === undefined) { - selector = arr[5].split("\r\n").join("\n").trim(); - } else { - selector = arr[2].split("\r\n").join("\n").trim(); - } - - /* - fetch comments and associate it with current selector - */ - var commentsRegex = new RegExp(this.cssCommentsRegex, "gi"); - var comments = commentsRegex.exec(selector); - if (comments !== null) { - selector = selector.replace(commentsRegex, "").trim(); - } - - //determine the type - if (selector.indexOf("@media") !== -1) { - //we have a media query - var cssObject = { - selector: selector, - type: "media", - subStyles: this.parseCSS(arr[3] + "\n}"), //recursively parse media query inner css - }; - if (comments !== null) { - cssObject.comments = comments[0]; - } - css.push(cssObject); - } else { - //we have standart css - var rules = this.parseRules(arr[6]); - var style = { - selector: selector, - rules: rules, - }; - if (selector === "@font-face") { - style.type = "font-face"; - } - if (comments !== null) { - style.comments = comments[0]; - } - css.push(style); - } - } - - return css; -}; - -/* - parses given string containing css directives - and returns an array of objects containing ruleName:ruleValue pairs - - @param rules, css directive string example - \n\ncolor:white;\n font-size:18px;\n - */ -fi.prototype.parseRules = function(rules) { - //convert all windows style line endings to unix style line endings - rules = rules.split("\r\n").join("\n"); - var ret = []; - - // Split all rules but keep semicolon for base64 url data - rules = rules.split(/;(?![^\(]*\))/); - - //proccess rules line by line - for (var i = 0; i < rules.length; i++) { - var line = rules[i]; - - //determine if line is a valid css directive, ie color:white; - line = line.trim(); - if (line.indexOf(":") !== -1) { - //line contains : - line = line.split(":"); - var cssDirective = line[0].trim(); - var cssValue = line.slice(1).join(":").trim(); - - //more checks - if (cssDirective.length < 1 || cssValue.length < 1) { - continue; //there is no css directive or value that is of length 1 or 0 - // PLAIN WRONG WHAT ABOUT margin:0; ? - } - - //push rule - ret.push({ - directive: cssDirective, - value: cssValue, - }); - } else { - //if there is no ':', but what if it was mis splitted value which starts with base64 - if (line.trim().substr(0, 7) == "base64,") { //hack :) - ret[ret.length - 1].value += line.trim(); - } else { - //add rule, even if it is defective - if (line.length > 0) { - ret.push({ - directive: "", - value: line, - defective: true, - }); - } - } - } - } - - return ret; //we are done! -}; -/* - just returns the rule having given directive - if not found returns false; - */ -fi.prototype.findCorrespondingRule = function(rules, directive, value) { - if (value === undefined) { - value = false; - } - var ret = false; - for (var i = 0; i < rules.length; i++) { - if (rules[i].directive == directive) { - ret = rules[i]; - if (value === rules[i].value) { - break; - } - } - } - return ret; -}; - -/* - Finds styles that have given selector, compress them, - and returns them - */ -fi.prototype.findBySelector = function(cssObjectArray, selector, contains) { - if (contains === undefined) { - contains = false; - } - - var found = []; - for (var i = 0; i < cssObjectArray.length; i++) { - if (contains === false) { - if (cssObjectArray[i].selector === selector) { - found.push(cssObjectArray[i]); - } - } else { - if (cssObjectArray[i].selector.indexOf(selector) !== -1) { - found.push(cssObjectArray[i]); - } - } - - } - if (found.length < 2) { - return found; - } else { - var base = found[0]; - for (i = 1; i < found.length; i++) { - this.intelligentCSSPush([base], found[i]); - } - return [base]; //we are done!! all properties merged into base! - } -}; - -/* - deletes cssObjects having given selector, and returns new array - */ -fi.prototype.deleteBySelector = function(cssObjectArray, selector) { - var ret = []; - for (var i = 0; i < cssObjectArray.length; i++) { - if (cssObjectArray[i].selector !== selector) { - ret.push(cssObjectArray[i]); - } - } - return ret; -}; - -/* - Compresses given cssObjectArray and tries to minimize - selector redundence. - */ -fi.prototype.compressCSS = function(cssObjectArray) { - var compressed = []; - var done = {}; - for (var i = 0; i < cssObjectArray.length; i++) { - var obj = cssObjectArray[i]; - if (done[obj.selector] === true) { - continue; - } - - var found = this.findBySelector(cssObjectArray, obj.selector); //found compressed - if (found.length !== 0) { - compressed.push(found[0]); - done[obj.selector] = true; - } - } - return compressed; -}; - -/* - Received 2 css objects with following structure - { - rules : [{directive:"", value:""}, {directive:"", value:""}, ...] - selector : "SOMESELECTOR" - } - - returns the changed(new,removed,updated) values on css1 parameter, on same structure - - if two css objects are the same, then returns false - - if a css directive exists in css1 and css2, and its value is different, it is included in diff - if a css directive exists in css1 and not css2, it is then included in diff - if a css directive exists in css2 but not css1, then it is deleted in css1, it would be included in diff but will be marked as type='DELETED' - - @object css1 css object - @object css2 css object - - @return diff css object contains changed values in css1 in regards to css2 see test input output in /test/data/css.js - */ -fi.prototype.cssDiff = function(css1, css2) { - if (css1.selector !== css2.selector) { - return false; - } - - //if one of them is media query return false, because diff function can not operate on media queries - if ((css1.type === "media" || css2.type === "media")) { - return false; - } - - var diff = { - selector: css1.selector, - rules: [], - }; - var rule1, rule2; - for (var i = 0; i < css1.rules.length; i++) { - rule1 = css1.rules[i]; - //find rule2 which has the same directive as rule1 - rule2 = this.findCorrespondingRule(css2.rules, rule1.directive, rule1.value); - if (rule2 === false) { - //rule1 is a new rule in css1 - diff.rules.push(rule1); - } else { - //rule2 was found only push if its value is different too - if (rule1.value !== rule2.value) { - diff.rules.push(rule1); - } - } - } - - //now for rules exists in css2 but not in css1, which means deleted rules - for (var ii = 0; ii < css2.rules.length; ii++) { - rule2 = css2.rules[ii]; - //find rule2 which has the same directive as rule1 - rule1 = this.findCorrespondingRule(css1.rules, rule2.directive); - if (rule1 === false) { - //rule1 is a new rule - rule2.type = "DELETED"; //mark it as a deleted rule, so that other merge operations could be true - diff.rules.push(rule2); - } - } - - if (diff.rules.length === 0) { - return false; - } - return diff; -}; - -/* - Merges 2 different css objects together - using intelligentCSSPush, - - @param cssObjectArray, target css object array - @param newArray, source array that will be pushed into cssObjectArray parameter - @param reverse, [optional], if given true, first parameter will be traversed on reversed order - effectively giving priority to the styles in newArray - */ -fi.prototype.intelligentMerge = function(cssObjectArray, newArray, reverse) { - if (reverse === undefined) { - reverse = false; - } - - for (var i = 0; i < newArray.length; i++) { - this.intelligentCSSPush(cssObjectArray, newArray[i], reverse); - } - for (i = 0; i < cssObjectArray.length; i++) { - var cobj = cssObjectArray[i]; - if (cobj.type === "media" || (cobj.type === "keyframes")) { - continue; - } - cobj.rules = this.compactRules(cobj.rules); - } -}; - -/* - inserts new css objects into a bigger css object - with same selectors groupped together - - @param cssObjectArray, array of bigger css object to be pushed into - @param minimalObject, single css object - @param reverse [optional] default is false, if given, cssObjectArray will be reversly traversed - resulting more priority in minimalObject's styles - */ -fi.prototype.intelligentCSSPush = function(cssObjectArray, minimalObject, reverse) { - var pushSelector = minimalObject.selector; - //find correct selector if not found just push minimalObject into cssObject - var cssObject = false; - - if (reverse === undefined) { - reverse = false; - } - - if (reverse === false) { - for (var i = 0; i < cssObjectArray.length; i++) { - if (cssObjectArray[i].selector === minimalObject.selector) { - cssObject = cssObjectArray[i]; - break; - } - } - } else { - for (var j = cssObjectArray.length - 1; j > -1; j--) { - if (cssObjectArray[j].selector === minimalObject.selector) { - cssObject = cssObjectArray[j]; - break; - } - } - } - - if (cssObject === false) { - cssObjectArray.push(minimalObject); //just push, because cssSelector is new - } else { - if (minimalObject.type !== "media") { - for (var ii = 0; ii < minimalObject.rules.length; ii++) { - var rule = minimalObject.rules[ii]; - //find rule inside cssObject - var oldRule = this.findCorrespondingRule(cssObject.rules, rule.directive); - if (oldRule === false) { - cssObject.rules.push(rule); - } else if (rule.type == "DELETED") { - oldRule.type = "DELETED"; - } else { - //rule found just update value - - oldRule.value = rule.value; - } - } - } else { - cssObject.subStyles = minimalObject.subStyles; //TODO, make this intelligent too - } - - } -}; - -/* - filter outs rule objects whose type param equal to DELETED - - @param rules, array of rules - - @returns rules array, compacted by deleting all unneccessary rules - */ -fi.prototype.compactRules = function(rules) { - var newRules = []; - for (var i = 0; i < rules.length; i++) { - if (rules[i].type !== "DELETED") { - newRules.push(rules[i]); - } - } - return newRules; -}; -/* - computes string for ace editor using this.css or given cssBase optional parameter - - @param [optional] cssBase, if given computes cssString from cssObject array - */ -fi.prototype.getCSSForEditor = function(cssBase, depth) { - if (depth === undefined) { - depth = 0; - } - var ret = ""; - if (cssBase === undefined) { - cssBase = this.css; - } - //append imports - for (var i = 0; i < cssBase.length; i++) { - if (cssBase[i].type == "imports") { - ret += cssBase[i].styles + "\n\n"; - } - } - for (i = 0; i < cssBase.length; i++) { - var tmp = cssBase[i]; - if (tmp.selector === undefined) { //temporarily omit media queries - continue; - } - var comments = ""; - if (tmp.comments !== undefined) { - comments = tmp.comments + "\n"; - } - - if (tmp.type == "media") { //also put media queries to output - ret += comments + tmp.selector + "{\n"; - ret += this.getCSSForEditor(tmp.subStyles, depth + 1); - ret += "}\n\n"; - } else if (tmp.type !== "keyframes" && tmp.type !== "imports") { - ret += this.getSpaces(depth) + comments + tmp.selector + " {\n"; - ret += this.getCSSOfRules(tmp.rules, depth + 1); - ret += this.getSpaces(depth) + "}\n\n"; - } - } - - //append keyFrames - for (i = 0; i < cssBase.length; i++) { - if (cssBase[i].type == "keyframes") { - ret += cssBase[i].styles + "\n\n"; - } - } - - return ret; -}; - -fi.prototype.getImports = function(cssObjectArray) { - var imps = []; - for (var i = 0; i < cssObjectArray.length; i++) { - if (cssObjectArray[i].type == "imports") { - imps.push(cssObjectArray[i].styles); - } - } - return imps; -}; -/* - given rules array, returns visually formatted css string - to be used inside editor - */ -fi.prototype.getCSSOfRules = function(rules, depth) { - var ret = ""; - for (var i = 0; i < rules.length; i++) { - if (rules[i] === undefined) { - continue; - } - if (rules[i].defective === undefined) { - ret += this.getSpaces(depth) + rules[i].directive + " : " + rules[i].value + ";\n"; - } else { - ret += this.getSpaces(depth) + rules[i].value + ";\n"; - } - - } - return ret || "\n"; -}; - -/* - A very simple helper function returns number of spaces appended in a single string, - the number depends input parameter, namely input*2 - */ -fi.prototype.getSpaces = function(num) { - var ret = ""; - for (var i = 0; i < num * 4; i++) { - ret += " "; - } - return ret; -}; - -/* - Given css string or objectArray, parses it and then for every selector, - prepends this.cssPreviewNamespace to prevent css collision issues - - @returns css string in which this.cssPreviewNamespace prepended - */ -fi.prototype.applyNamespacing = function(css, forcedNamespace) { - var cssObjectArray = css; - var namespaceClass = "." + this.cssPreviewNamespace; - if (forcedNamespace !== undefined) { - namespaceClass = forcedNamespace; - } - - if (typeof css === "string") { - cssObjectArray = this.parseCSS(css); - } - - for (var i = 0; i < cssObjectArray.length; i++) { - var obj = cssObjectArray[i]; - - //bypass namespacing for @font-face @keyframes @import - if (obj.selector.indexOf("@font-face") > -1 || obj.selector.indexOf("keyframes") > -1 || obj.selector.indexOf("@import") > -1 || obj.selector.indexOf(".form-all") > -1 || obj.selector.indexOf("#stage") > -1) { - continue; - } - - if (obj.type !== "media") { - var selector = obj.selector.split(","); - var newSelector = []; - for (var j = 0; j < selector.length; j++) { - if (selector[j].indexOf(".supernova") === -1) { //do not apply namespacing to selectors including supernova - newSelector.push(namespaceClass + " " + selector[j]); - } else { - newSelector.push(selector[j]); - } - } - obj.selector = newSelector.join(","); - } else { - obj.subStyles = this.applyNamespacing(obj.subStyles, forcedNamespace); //handle media queries as well - } - } - - return cssObjectArray; -}; - -/* - given css string or object array, clears possible namespacing from - all of the selectors inside the css - */ -fi.prototype.clearNamespacing = function(css, returnObj) { - if (returnObj === undefined) { - returnObj = false; - } - var cssObjectArray = css; - var namespaceClass = "." + this.cssPreviewNamespace; - if (typeof css === "string") { - cssObjectArray = this.parseCSS(css); - } - - for (var i = 0; i < cssObjectArray.length; i++) { - var obj = cssObjectArray[i]; - - if (obj.type !== "media") { - var selector = obj.selector.split(","); - var newSelector = []; - for (var j = 0; j < selector.length; j++) { - newSelector.push(selector[j].split(namespaceClass + " ").join("")); - } - obj.selector = newSelector.join(","); - } else { - obj.subStyles = this.clearNamespacing(obj.subStyles, true); //handle media queries as well - } - } - if (returnObj === false) { - return this.getCSSForEditor(cssObjectArray); - } else { - return cssObjectArray; - } - -}; - -/* - creates a new style tag (also destroys the previous one) - and injects given css string into that css tag - */ -fi.prototype.createStyleElement = function(id, css, format) { - if (format === undefined) { - format = false; - } - - if (this.testMode === false && format !== "nonamespace") { - //apply namespacing classes - css = this.applyNamespacing(css); - } - - if (typeof css != "string") { - css = this.getCSSForEditor(css); - } - //apply formatting for css - if (format === true) { - css = this.getCSSForEditor(this.parseCSS(css)); - } - - if (this.testMode !== false) { - return this.testMode("create style #" + id, css); //if test mode, just pass result to callback - } - - var __el = document.getElementById(id); - if (__el) { - __el.parentNode.removeChild(__el); - } - - var head = document.head || document.getElementsByTagName("head")[0], - style = document.createElement("style"); - - style.id = id; - style.type = "text/css"; - - head.appendChild(style); - - if (style.styleSheet && !style.sheet) { - style.styleSheet.cssText = css; - } else { - style.appendChild(document.createTextNode(css)); - } -}; - -export default fi; - -/* eslint-enable */ diff --git a/ui-ngx/src/app/core/css/css.ts b/ui-ngx/src/app/core/css/css.ts new file mode 100644 index 0000000000..ae310fd65e --- /dev/null +++ b/ui-ngx/src/app/core/css/css.ts @@ -0,0 +1,460 @@ +/// +/// Copyright © 2016-2025 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. +/// + +interface CSSRule { + directive: string; + value: string; + defective?: boolean; + type?: string; +} + +interface CSSObject { + selector: string; + type?: 'media' | 'keyframes' | 'imports' | 'font-face'; + rules?: CSSRule[]; + subStyles?: CSSObject[]; + styles?: string; + comments?: string; +} + +export default class CSSParser { + cssPreviewNamespace: string = ''; + testMode: boolean | ((action: string, css: string) => string) = false; + + cssImportStatements: string[] = []; + + private readonly cssKeyframeRegex: string = '((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})'; + private readonly combinedCSSRegex: string = '((\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})'; + private readonly cssCommentsRegex: string = '(\\/\\*[\\s\\S]*?\\*\\/)'; + private readonly cssImportStatementRegex: RegExp = /@import .*?;/gi; + + /** + * Removes CSS comments from the provided CSS string. + * @param cssString - The CSS string to strip comments from. + * @returns The CSS string with comments removed. + */ + stripComments(cssString: string): string { + const regex = new RegExp(this.cssCommentsRegex, 'gi'); + return cssString.replace(regex, ''); + } + + /** + * Parses a CSS string into an array of CSS objects with selectors and rules. + * @param source - The CSS string to parse. + * @returns An array of CSS objects. + */ + parseCSS(source?: string): CSSObject[] { + if (!source) { + return []; + } + + const css: CSSObject[] = []; + let cssSource = source; + + const importStatementRegex = new RegExp(this.cssImportStatementRegex.source, this.cssImportStatementRegex.flags); + cssSource = cssSource.replace(importStatementRegex, (match) => { + this.cssImportStatements.push(match); + css.push({ selector: '@imports', type: 'imports', styles: match }); + return ''; + }); + + // Extract keyframe statements + const keyframesRegex = new RegExp(this.cssKeyframeRegex, 'gi'); + cssSource = cssSource.replace(keyframesRegex, (match) => { + css.push({ selector: '@keyframes', type: 'keyframes', styles: match }); + return ''; + }); + + // Parse remaining CSS + const unifiedRegex = new RegExp(this.combinedCSSRegex, 'gi'); + let match: RegExpExecArray | null; + while ((match = unifiedRegex.exec(cssSource)) !== null) { + const selector = (match[2] ?? match[5]).replace(/\r\n/g, '\n').trim(); + + // Extract comments + const commentsRegex = new RegExp(this.cssCommentsRegex, 'gi'); + const comments = commentsRegex.exec(selector); + const cleanSelector = comments ? selector.replace(commentsRegex, '').trim() : selector; + + if (cleanSelector.includes('@media')) { + css.push({ + selector: cleanSelector, + type: 'media', + subStyles: this.parseCSS(match[3] + '\n}'), + ...(comments && {comments: comments[0]}), + }); + } else { + const rules = this.parseRules(match[6]); + const style: CSSObject = { + selector: cleanSelector, + rules, + ...(cleanSelector === '@font-face' && {type: 'font-face'}), + ...(comments && {comments: comments[0]}), + }; + css.push(style); + } + } + + return css; + } + + /** + * Parses CSS rules into an array of rule objects. + * @param rules - The CSS rules string. + * @returns An array of rule objects with directive and value. + */ + parseRules(rules: string): CSSRule[] { + const normalizedRules = rules.replace(/\r\n/g, '\n'); + const ruleList = normalizedRules.split(/;(?![^(]*\))/); + const result: CSSRule[] = []; + + for (const line of ruleList) { + const trimmedLine = line.trim(); + if (!trimmedLine) continue; + + if (trimmedLine.includes(':')) { + const [directive, ...valueParts] = trimmedLine.split(':'); + const value = valueParts.join(':').trim(); + if (directive.trim() && value) { + result.push({directive: directive.trim(), value}); + } + } else if (trimmedLine.startsWith('base64,')) { + if (result.length > 0) { + result[result.length - 1].value += trimmedLine; + } + } else if (trimmedLine) { + result.push({directive: '', value: trimmedLine, defective: true}); + } + } + + return result; + } + + /** + * Finds a rule matching the given directive in the rules array. + * @param rules - The array of CSS rules. + * @param directive - The directive to search for. + * @param value - Optional value to match. + * @returns The matching rule or false if not found. + */ + findCorrespondingRule(rules: CSSRule[], directive: string, value?: string): CSSRule | false { + return rules.find(rule => rule.directive === directive && (!value || rule.value === value)) || false; + } + + /** + * Finds CSS objects by selector, optionally merging duplicates. + * @param cssObjectArray - The array of CSS objects. + * @param selector - The selector to search for. + * @param contains - If true, matches selectors containing the string. + * @returns An array of matching CSS objects. + */ + findBySelector(cssObjectArray: CSSObject[], selector: string, contains: boolean = false): CSSObject[] { + const found = cssObjectArray.filter(obj => contains ? obj.selector.includes(selector) : obj.selector === selector); + + if (found.length < 2) return found; + + const base = found[0]; + for (let i = 1; i < found.length; i++) { + this.intelligentCSSPush([base], found[i]); + } + return [base]; + } + + /** + * Deletes CSS objects with the given selector. + * @param cssObjectArray - The array of CSS objects. + * @param selector - The selector to delete. + * @returns A new array without the matching CSS objects. + */ + deleteBySelector(cssObjectArray: CSSObject[], selector: string): CSSObject[] { + return cssObjectArray.filter(obj => obj.selector !== selector); + } + + /** + * Compresses CSS objects by merging duplicates. + * @param cssObjectArray - The array of CSS objects to compress. + * @returns A compressed array of CSS objects. + */ + compressCSS(cssObjectArray: CSSObject[]): CSSObject[] { + const compressed: CSSObject[] = []; + const done = new Set(); + + for (const obj of cssObjectArray) { + if (done.has(obj.selector)) continue; + const found = this.findBySelector(cssObjectArray, obj.selector); + if (found.length) { + compressed.push(found[0]); + done.add(obj.selector); + } + } + return compressed; + } + + /** + * Computes the difference between two CSS objects. + * @param css1 - The first CSS object. + * @param css2 - The second CSS object. + * @returns A CSS object with the differences or false if no differences. + */ + cssDiff(css1: CSSObject, css2: CSSObject): CSSObject | false { + if (css1.selector !== css2.selector || css1.type === 'media' || css2.type === 'media') { + return false; + } + + const diff: CSSObject = {selector: css1.selector, rules: []}; + const rules1 = css1.rules ?? []; + const rules2 = css2.rules ?? []; + + for (const rule1 of rules1) { + const rule2 = this.findCorrespondingRule(rules2, rule1.directive, rule1.value); + if (!rule2 || rule1.value !== rule2.value) { + diff.rules!.push(rule1); + } + } + + for (const rule2 of rules2) { + if (!this.findCorrespondingRule(rules1, rule2.directive)) { + diff.rules!.push({...rule2, type: 'DELETED'}); + } + } + + return diff.rules!.length ? diff : false; + } + + /** + * Merges two CSS object arrays intelligently. + * @param cssObjectArray - The target CSS object array. + * @param newArray - The source CSS object array to merge. + * @param reverse - If true, prioritizes styles in newArray. + */ + intelligentMerge(cssObjectArray: CSSObject[], newArray: CSSObject[], reverse: boolean = false): void { + for (const obj of newArray) { + this.intelligentCSSPush(cssObjectArray, obj, reverse); + } + for (const obj of cssObjectArray) { + if (obj.type !== 'media' && obj.type !== 'keyframes') { + obj.rules = this.compactRules(obj.rules ?? []); + } + } + } + + /** + * Pushes a CSS object into an array, merging with existing selectors. + * @param cssObjectArray - The target CSS object array. + * @param minimalObject - The CSS object to push. + * @param reverse - If true, traverses array in reverse for priority. + */ + intelligentCSSPush(cssObjectArray: CSSObject[], minimalObject: CSSObject, reverse: boolean = false): void { + const cssObject = (reverse ? cssObjectArray.slice().reverse() : cssObjectArray) + .find(obj => obj.selector === minimalObject.selector) ?? false; + + if (!cssObject) { + cssObjectArray.push(minimalObject); + return; + } + + if (minimalObject.type !== 'media') { + for (const rule of minimalObject.rules ?? []) { + const oldRule = this.findCorrespondingRule(cssObject.rules ?? [], rule.directive); + if (!oldRule) { + cssObject.rules!.push(rule); + } else if (rule.type === 'DELETED') { + oldRule.type = 'DELETED'; + } else { + oldRule.value = rule.value; + } + } + } else { + cssObject.subStyles = minimalObject.subStyles; + } + } + + /** + * Filters out rules marked as DELETED. + * @param rules - The array of CSS rules. + * @returns A compacted array of rules. + */ + compactRules(rules: CSSRule[]): CSSRule[] { + return rules.filter(rule => rule.type !== 'DELETED'); + } + + /** + * Generates a formatted CSS string for an editor. + * @param cssBase - The CSS object array to format. + * @param depth - The indentation depth. + * @returns A formatted CSS string. + */ + getCSSForEditor(cssBase?: CSSObject[], depth: number = 0): string { + const css = cssBase ?? this.parseCSS(''); + let result = ''; + + // Append imports + for (const obj of css) { + if (obj.type === 'imports') { + result += `${obj.styles}\n\n`; + } + } + + // Append styles + for (const obj of css) { + if (!obj.selector) continue; + const comments = obj.comments ? `${obj.comments}\n` : ''; + + if (obj.type === 'media') { + result += `${comments}${obj.selector} {\n${this.getCSSForEditor(obj.subStyles, depth + 1)}}\n\n`; + } else if (obj.type !== 'keyframes' && obj.type !== 'imports') { + result += `${this.getSpaces(depth)}${comments}${obj.selector} {\n${this.getCSSOfRules(obj.rules ?? [], depth + 1)}${this.getSpaces(depth)}}\n\n`; + } + } + + // Append keyframes + for (const obj of css) { + if (obj.type === 'keyframes') { + result += `${obj.styles}\n\n`; + } + } + + return result; + } + + /** + * Retrieves all import statements from a CSS object array. + * @param cssObjectArray - The CSS object array. + * @returns An array of import statement strings. + */ + getImports(cssObjectArray: CSSObject[]): string[] { + return cssObjectArray.filter(obj => obj.type === 'imports').map(obj => obj.styles!); + } + + /** + * Formats CSS rules into a string for an editor. + * @param rules - The array of CSS rules. + * @param depth - The indentation depth. + * @returns A formatted CSS rules string. + */ + getCSSOfRules(rules: CSSRule[], depth: number): string { + let result = ''; + for (const rule of rules) { + if (!rule) continue; + if (rule.defective) { + result += `${this.getSpaces(depth)}${rule.value};\n`; + } else { + result += `${this.getSpaces(depth)}${rule.directive}: ${rule.value};\n`; + } + } + return result || '\n'; + } + + /** + * Generates indentation spaces based on depth. + * @param num - The indentation level. + * @returns A string of spaces. + */ + getSpaces(num: number): string { + return ' '.repeat(num * 4); + } + + /** + * Applies a namespace to CSS selectors to prevent collisions. + * @param css - The CSS string or object array. + * @param forcedNamespace - Optional custom namespace. + * @returns The namespaced CSS object array. + */ + applyNamespacing(css: string | CSSObject[], forcedNamespace?: string): CSSObject[] { + const namespaceClass = forcedNamespace ?? `.${this.cssPreviewNamespace}`; + const cssObjectArray = typeof css === 'string' ? this.parseCSS(css) : css; + + for (const obj of cssObjectArray) { + if (['@font-face', 'keyframes', '@import', '.form-all', '#stage'].some(s => obj.selector.includes(s))) { + continue; + } + + if (obj.type !== 'media') { + obj.selector = obj.selector.split(',') + .map(sel => sel.includes('.supernova') ? sel : `${namespaceClass} ${sel}`) + .join(','); + } else { + obj.subStyles = this.applyNamespacing(obj.subStyles ?? [], forcedNamespace); + } + } + + return cssObjectArray; + } + + /** + * Removes namespacing from CSS selectors. + * @param css - The CSS string or object array. + * @param returnObj - If true, returns the CSS object array. + * @returns The CSS string or object array with namespacing removed. + */ + clearNamespacing(css: string | CSSObject[], returnObj: boolean = false): string | CSSObject[] { + const namespaceClass = `.${this.cssPreviewNamespace}`; + const cssObjectArray = typeof css === 'string' ? this.parseCSS(css) : css; + + for (const obj of cssObjectArray) { + if (obj.type !== 'media') { + obj.selector = obj.selector + .split(',') + .map(sel => sel.split(namespaceClass + ' ').join('')) + .join(','); + } else { + obj.subStyles = this.clearNamespacing(obj.subStyles ?? [], true) as CSSObject[]; + } + } + + return returnObj ? cssObjectArray : this.getCSSForEditor(cssObjectArray); + } + + /** + * Creates a style element with the provided CSS. + * @param id - The ID for the style element. + * @param css - The CSS string or object array. + * @param format - If true, formats the CSS; if 'nonamespace', skips namespacing. + */ + createStyleElement(id: string, css: string | CSSObject[], format: boolean | 'nonamespace' = false): void | string { + let cssString = typeof css === 'string' ? css : this.getCSSForEditor(css); + + if (this.testMode === false && format !== 'nonamespace') { + cssString = this.getCSSForEditor(this.applyNamespacing(css)); + } + + if (format === true) { + cssString = this.getCSSForEditor(this.parseCSS(cssString)); + } + + if (typeof this.testMode === 'function') { + return this.testMode(`create style #${id}`, cssString); + } + + const existingElement = document.getElementById(id); + existingElement?.remove(); + + if (!css) { + return; + } + + const style = document.createElement('style'); + style.id = id; + + if ('styleSheet' in style && !('sheet' in style)) { + (style as any).styleSheet.cssText = cssString; + } else { + style.appendChild(document.createTextNode(cssString)); + } + + (document.head || document.getElementsByTagName('head')[0]).appendChild(style); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts index 10d5f9b1a0..3138f53c1e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts @@ -90,8 +90,8 @@ export class MarkdownWidgetComponent extends PageComponent implements OnInit { this.markdownClass = 'markdown-widget-' + hashCode(cssString); cssParser.cssPreviewNamespace = this.markdownClass; cssParser.testMode = false; - cssString = cssParser.applyNamespacing(cssString); - cssString = cssParser.getCSSForEditor(cssString); + const cssObjects = cssParser.applyNamespacing(cssString); + cssString = cssParser.getCSSForEditor(cssObjects); this.additionalStyles = [cssString]; } if (isDefinedAndNotNull(this.settings.applyDefaultMarkdownStyle)) { From 0dde9660829bbce65a30123cd954179be54c8650 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 15 May 2025 16:57:27 +0300 Subject: [PATCH 178/335] Support reprocessing for job with general error; job deletion; refactoring --- .../server/controller/JobController.java | 16 +++- .../entitiy/EntityStateSourcingListener.java | 21 ++++- .../server/service/job/DefaultJobManager.java | 34 ++++---- .../server/service/job/DummyJobProcessor.java | 1 + .../service/job/task/DummyTaskProcessor.java | 2 +- .../queue/DefaultTbClusterService.java | 3 +- .../server/service/job/JobManagerTest.java | 80 +++++++++++++++++-- ...anagerTest_EntityPartitioningStrategy.java | 2 +- .../server/cluster/TbClusterService.java | 2 + .../server/dao/job/JobService.java | 2 + .../data/job/DummyJobConfiguration.java | 2 + .../server/common/data/job/Job.java | 11 ++- .../common/data/job/JobConfiguration.java | 5 +- .../common/data/job/task/DummyTask.java | 2 +- .../common/data/job/task/DummyTaskResult.java | 34 ++++---- .../server/common/data/job/task/Task.java | 1 + .../common/data/job/task/TaskResult.java | 1 + .../msg/plugin/ComponentLifecycleMsg.java | 7 +- .../server/common/util/ProtoUtils.java | 6 ++ common/proto/src/main/proto/queue.proto | 1 + .../server/queue/task/TaskProcessor.java | 24 ++++-- .../thingsboard/common/util/JacksonUtil.java | 4 +- .../server/dao/job/DefaultJobService.java | 17 ++++ 23 files changed, 217 insertions(+), 61 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java index 1db84593f5..b301c2e321 100644 --- a/application/src/main/java/org/thingsboard/server/controller/JobController.java +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -19,6 +19,7 @@ import io.swagger.v3.oas.annotations.Parameter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -56,14 +57,14 @@ public class JobController extends BaseController { private final JobManager jobManager; @GetMapping("/job/{id}") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public Job getJobById(@PathVariable UUID id) throws ThingsboardException { // todo check permissions return jobService.findJobById(getTenantId(), new JobId(id)); } @GetMapping("/jobs") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public PageData getJobs(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) @@ -86,17 +87,24 @@ public class JobController extends BaseController { } @PostMapping("/job/{id}/cancel") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public void cancelJob(@PathVariable UUID id) throws ThingsboardException { // todo check permissions jobManager.cancelJob(getTenantId(), new JobId(id)); } @PostMapping("/job/{id}/reprocess") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public void reprocessJob(@PathVariable UUID id) throws ThingsboardException { // todo check permissions jobManager.reprocessJob(getTenantId(), new JobId(id)); } + @DeleteMapping("/job/{id}") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + public void deleteJob(@PathVariable UUID id) throws ThingsboardException { + // todo check permissions + jobService.deleteJob(getTenantId(), new JobId(id)); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index bcb77e7005..83d7d60144 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -41,7 +41,6 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; -import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -305,10 +304,24 @@ public class EntityStateSourcingListener { private void onJobUpdate(Job job) { jobManager.ifPresent(jobManager -> jobManager.onJobUpdate(job)); - if (job.getResult().getCancellationTs() > 0 || (job.getStatus().isOneOf(JobStatus.FAILED) && job.getResult().getGeneralError() != null)) { - // task processors will add this job to the list of discarded - tbClusterService.broadcastEntityStateChangeEvent(job.getTenantId(), job.getId(), ComponentLifecycleEvent.STOPPED); + + ComponentLifecycleEvent event; + if (job.getResult().getCancellationTs() > 0) { + event = ComponentLifecycleEvent.STOPPED; + } else if (job.getResult().getGeneralError() != null) { + event = ComponentLifecycleEvent.FAILED; + } else { + return; } + ComponentLifecycleMsg msg = ComponentLifecycleMsg.builder() + .tenantId(job.getTenantId()) + .entityId(job.getId()) + .event(event) + .info(JacksonUtil.newObjectNode() + .put("tasksKey", job.getConfiguration().getTasksKey())) + .build(); + // task processors will add this job to the list of discarded + tbClusterService.broadcast(msg); } private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) { diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 580dd0ff99..ac237d7310 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.job; import jakarta.annotation.PreDestroy; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; @@ -178,26 +179,29 @@ public class DefaultJobManager implements JobManager { JobResult result = job.getResult(); if (result.getGeneralError() != null) { - throw new IllegalArgumentException("Reprocessing not allowed since job has general error"); - } - List taskFailures = result.getResults().stream() - .filter(taskResult -> !taskResult.isSuccess() && !taskResult.isDiscarded()) - .toList(); - if (result.getFailedCount() > taskFailures.size()) { - throw new IllegalArgumentException("Reprocessing not allowed since there are too many failures (more than " + taskFailures.size() + ")"); + job.presetResult(); + } else { + List taskFailures = result.getResults().stream() + .filter(taskResult -> !taskResult.isSuccess() && !taskResult.isDiscarded()) + .toList(); + if (result.getFailedCount() > taskFailures.size()) { + throw new IllegalArgumentException("Reprocessing not allowed since there are too many failures (more than " + taskFailures.size() + ")"); + } + result.setFailedCount(0); + result.setResults(result.getResults().stream() + .filter(TaskResult::isSuccess) + .toList()); + job.getConfiguration().setToReprocess(taskFailures); } - - result.setFailedCount(0); - result.setResults(result.getResults().stream() - .filter(TaskResult::isSuccess) - .toList()); - - job.getConfiguration().setToReprocess(taskFailures); - + job.getConfiguration().setTasksKey(UUID.randomUUID().toString()); jobService.saveJob(tenantId, job); } private void submitTask(Task task) { + if (ObjectUtils.anyNull(task.getTenantId(), task.getJobId(), task.getKey())) { + throw new IllegalArgumentException("Task " + task + " missing required fields"); + } + log.debug("[{}][{}] Submitting task: {}", task.getTenantId(), task.getJobId(), task); TaskProto taskProto = TaskProto.newBuilder() .setValue(JacksonUtil.toString(task)) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java index e1b91e606a..373bc4afee 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java @@ -76,6 +76,7 @@ public class DummyJobProcessor implements JobProcessor { return DummyTask.builder() .tenantId(job.getTenantId()) .jobId(job.getId()) + .key(configuration.getTasksKey()) .retries(configuration.getRetries()) .number(number) .processingTimeMs(configuration.getTaskProcessingTimeMs()) diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java index 1bcf6b36b3..5c88083146 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java @@ -36,7 +36,7 @@ public class DummyTaskProcessor extends TaskProcessor> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); Set tbRuleEngineServices = partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE); diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index a6e40333eb..3ae3bf5686 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -36,7 +36,7 @@ import org.thingsboard.server.common.data.job.task.DummyTaskResult.DummyTaskFail import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.controller.AbstractControllerTest; -import org.thingsboard.server.dao.job.JobService; +import org.thingsboard.server.dao.job.JobDao; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.queue.task.JobStatsService; @@ -69,7 +69,7 @@ public class JobManagerTest extends AbstractControllerTest { private JobStatsService jobStatsService; @Autowired - private JobService jobService; + private JobDao jobDao; @Before public void setUp() throws Exception { @@ -220,7 +220,7 @@ public class JobManagerTest extends AbstractControllerTest { inv.callRealMethod(); } return null; - }).when(taskProcessor).addToDiscardedJobs(any()); // ignoring cancellation event, + }).when(taskProcessor).addToDiscarded(any()); // ignoring cancellation event, cancelJob(jobId); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -256,7 +256,7 @@ public class JobManagerTest extends AbstractControllerTest { Thread.sleep(3000); verify(jobStatsService, never()).reportTaskResult(any(), any(), any()); - assertThat(jobService.findJobsByFilter(tenantId, JobFilter.builder().build(), new PageLink(100)).getData()).isEmpty(); + assertThat(jobDao.findByTenantIdAndFilter(tenantId, JobFilter.builder().build(), new PageLink(100)).getData()).isEmpty(); } @Test @@ -340,7 +340,7 @@ public class JobManagerTest extends AbstractControllerTest { } @Test - public void testGeneralJobError() { + public void testSubmitJob_generalError() { int submittedTasks = 100; JobId jobId = jobManager.submitJob(Job.builder() .tenantId(tenantId) @@ -358,7 +358,7 @@ public class JobManagerTest extends AbstractControllerTest { Job job = findJobById(jobId); assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); assertThat(job.getResult().getSuccessfulCount()).isBetween(1, submittedTasks); - assertThat(job.getResult().getDiscardedCount()).isBetween(1, submittedTasks); + assertThat(job.getResult().getDiscardedCount()).isZero(); assertThat(job.getResult().getTotalCount()).isNull(); }); @@ -369,7 +369,70 @@ public class JobManagerTest extends AbstractControllerTest { } @Test - public void testJobReprocessing() throws Exception { + public void testSubmitJob_immediateGeneralError() { + JobId jobId = jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("Test job") + .configuration(DummyJobConfiguration.builder() + .generalError("Some error while submitting tasks") + .submittedTasksBeforeGeneralError(0) + .build()) + .build()).getId(); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); + assertThat(job.getResult().getSuccessfulCount()).isZero(); + assertThat(job.getResult().getDiscardedCount()).isZero(); + assertThat(job.getResult().getFailedCount()).isZero(); + assertThat(job.getResult().getTotalCount()).isNull(); + }); + } + + @Test + public void testReprocessJob_generalError() throws Exception { + int submittedTasks = 100; + JobId jobId = jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key("test-job") + .description("Test job") + .configuration(DummyJobConfiguration.builder() + .generalError("Some error while submitting tasks") + .submittedTasksBeforeGeneralError(submittedTasks) + .taskProcessingTimeMs(10) + .build()) + .build()).getId(); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.FAILED); + assertThat(job.getResult().getGeneralError()).isEqualTo("Some error while submitting tasks"); + }); + + Job savedJob = jobDao.findById(tenantId, jobId.getId()); + DummyJobConfiguration configuration = savedJob.getConfiguration(); + configuration.setGeneralError(null); + configuration.setSuccessfulTasksCount(submittedTasks); + jobDao.save(tenantId, savedJob); + + reprocessJob(jobId); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.COMPLETED); + assertThat(job.getResult().getGeneralError()).isNull(); + assertThat(job.getResult().getSuccessfulCount()).isEqualTo(submittedTasks); + assertThat(job.getResult().getTotalCount()).isEqualTo(submittedTasks); + assertThat(job.getResult().getFailedCount()).isZero(); + assertThat(job.getResult().getDiscardedCount()).isZero(); + }); + } + + @Test + public void testReprocessJob() throws Exception { int successfulTasks = 3; int failedTasks = 2; int totalTasksCount = successfulTasks + failedTasks; @@ -410,11 +473,12 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(job.getResult().getFailedCount()).isZero(); assertThat(job.getResult().getTotalCount()).isEqualTo(totalTasksCount); assertThat(job.getResult().getResults()).isEmpty(); + assertThat(job.getConfiguration().getToReprocess()).isNullOrEmpty(); }); } @Test - public void testJobReprocessing_somePermanentlyFailed() throws Exception { + public void testReprocessJob_somePermanentlyFailed() throws Exception { int successfulTasks = 3; int failedTasks = 2; int permanentlyFailedTasks = 1; diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java index a021603ca6..59093ad802 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest_EntityPartitioningStrategy.java @@ -36,7 +36,7 @@ public class JobManagerTest_EntityPartitioningStrategy extends JobManagerTest { } @Override - public void testGeneralJobError() { + public void testSubmitJob_generalError() { } diff --git a/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java b/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java index aed6eb4cf5..6da6fcf8a8 100644 --- a/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java +++ b/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java @@ -87,6 +87,8 @@ public interface TbClusterService extends TbQueueClusterService { void broadcastEntityStateChangeEvent(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state); + void broadcast(ComponentLifecycleMsg componentLifecycleMsg); + void onDeviceProfileChange(DeviceProfile deviceProfile, DeviceProfile oldDeviceProfile, TbQueueCallback callback); void onDeviceProfileDelete(DeviceProfile deviceProfile, TbQueueCallback callback); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java index 33f9511267..7d4c68f6a4 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java @@ -40,4 +40,6 @@ public interface JobService extends EntityDaoService { Job findLatestJobByKey(TenantId tenantId, String key); + void deleteJob(TenantId tenantId, JobId jobId); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java index 62695eb237..a9ff9e7f4f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java @@ -20,6 +20,7 @@ import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import java.util.List; @@ -28,6 +29,7 @@ import java.util.List; @AllArgsConstructor @NoArgsConstructor @Builder +@ToString(callSuper = true) public class DummyJobConfiguration extends JobConfiguration { private long taskProcessingTimeMs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java index d4e69761c8..f914067be3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java @@ -28,6 +28,8 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; +import java.util.UUID; + @Data @NoArgsConstructor @ToString(callSuper = true) @@ -42,19 +44,26 @@ public class Job extends BaseData implements HasTenantId { private String key; @NotBlank private String description; + @NotNull private JobStatus status; @NotNull @Valid private JobConfiguration configuration; + @NotNull private JobResult result; - @Builder + @Builder(toBuilder = true) public Job(TenantId tenantId, JobType type, String key, String description, JobConfiguration configuration) { this.tenantId = tenantId; this.type = type; this.key = key; this.description = description; this.configuration = configuration; + this.configuration.setTasksKey(UUID.randomUUID().toString()); + presetResult(); + } + + public void presetResult() { this.result = switch (type) { case DUMMY -> new DummyJobResult(); }; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java index 35f44cd4be..54d8166a5d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobConfiguration.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import jakarta.validation.constraints.NotBlank; import lombok.Data; import org.thingsboard.server.common.data.job.task.TaskResult; @@ -34,7 +35,9 @@ import java.util.List; @Data public abstract class JobConfiguration implements Serializable { - private List toReprocess; + @NotBlank + private String tasksKey; // internal + private List toReprocess; // internal @JsonIgnore public abstract JobType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java index 7e262ed7b8..e0f670ad9e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java @@ -46,7 +46,7 @@ public class DummyTask extends Task { @Override public DummyTaskResult toDiscarded() { - return DummyTaskResult.discarded(); + return DummyTaskResult.discarded(this); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java index cd68b4d248..1988f13eb0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.job.task; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.ToString; import lombok.experimental.SuperBuilder; import org.thingsboard.server.common.data.job.JobType; @@ -25,29 +26,34 @@ import org.thingsboard.server.common.data.job.JobType; @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @SuperBuilder +@ToString(callSuper = true) public class DummyTaskResult extends TaskResult { - private static final DummyTaskResult SUCCESS = DummyTaskResult.builder().success(true).build(); - private static final DummyTaskResult DISCARDED = DummyTaskResult.builder().discarded(true).build(); - private DummyTaskFailure failure; - public static DummyTaskResult success() { - return SUCCESS; + public static DummyTaskResult success(DummyTask task) { + return DummyTaskResult.builder() + .key(task.getKey()) + .success(true) + .build(); } public static DummyTaskResult failed(DummyTask task, Throwable error) { - DummyTaskResult result = new DummyTaskResult(); - result.setFailure(DummyTaskFailure.builder() - .error(error.getMessage()) - .number(task.getNumber()) - .failAlways(task.isFailAlways()) - .build()); - return result; + return DummyTaskResult.builder() + .key(task.getKey()) + .failure(DummyTaskFailure.builder() + .error(error.getMessage()) + .number(task.getNumber()) + .failAlways(task.isFailAlways()) + .build()) + .build(); } - public static DummyTaskResult discarded() { - return DISCARDED; + public static DummyTaskResult discarded(DummyTask task) { + return DummyTaskResult.builder() + .key(task.getKey()) + .discarded(true) + .build(); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java index cce32fdad0..fb373d9850 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/Task.java @@ -40,6 +40,7 @@ public abstract class Task { private TenantId tenantId; private JobId jobId; + private String key; private int retries; public Task() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java index 9416cb8f6b..21303a55fe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.job.JobType; }) public abstract class TaskResult { + private String key; private boolean success; private boolean discarded; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java index 13fd6159fc..d57301fd10 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.msg.plugin; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Builder; import lombok.Data; import org.thingsboard.server.common.data.EntityType; @@ -45,13 +46,14 @@ public class ComponentLifecycleMsg implements TenantAwareMsg, ToAllNodesMsg { private final String name; private final EntityId oldProfileId; private final EntityId profileId; + private final JsonNode info; public ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event) { - this(tenantId, entityId, event, null, null, null, null); + this(tenantId, entityId, event, null, null, null, null, null); } @Builder - private ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event, String oldName, String name, EntityId oldProfileId, EntityId profileId) { + private ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event, String oldName, String name, EntityId oldProfileId, EntityId profileId, JsonNode info) { this.tenantId = tenantId; this.entityId = entityId; this.event = event; @@ -59,6 +61,7 @@ public class ComponentLifecycleMsg implements TenantAwareMsg, ToAllNodesMsg { this.name = name; this.oldProfileId = oldProfileId; this.profileId = profileId; + this.info = info; } public Optional getRuleChainId() { diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java index 1ebd753f3c..9781cf9a7e 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java @@ -139,6 +139,9 @@ public class ProtoUtils { if (msg.getOldName() != null) { builder.setOldName(msg.getOldName()); } + if (msg.getInfo() != null) { + builder.setInfo(JacksonUtil.toString(msg.getInfo())); + } return builder.build(); } @@ -166,6 +169,9 @@ public class ProtoUtils { var profileType = EntityType.DEVICE.equals(entityId.getEntityType()) ? EntityType.DEVICE_PROFILE : EntityType.ASSET_PROFILE; builder.oldProfileId(EntityIdFactory.getByTypeAndUuid(profileType, new UUID(proto.getOldProfileIdMSB(), proto.getOldProfileIdLSB()))); } + if (proto.hasInfo()) { + builder.info(JacksonUtil.toJsonNode(proto.getInfo())); + } return builder.build(); } diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 2a7d28241e..d03f59143c 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -1261,6 +1261,7 @@ message ComponentLifecycleMsgProto { int64 oldProfileIdLSB = 10; int64 profileIdMSB = 11; int64 profileIdLSB = 12; + optional string info = 13; } message EdgeEventMsgProto { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 947d252442..ef0e48e30a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -68,7 +68,9 @@ public abstract class TaskProcessor, R extends TaskResult> { private MainQueueConsumerManager, QueueConfig> taskConsumer; private final ExecutorService taskExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-processor")); - private final SetCache discardedJobs = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); + private final SetCache discarded = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); + private final SetCache failed = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); + private final SetCache deletedTenants = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); @PostConstruct @@ -98,9 +100,13 @@ public abstract class TaskProcessor, R extends TaskResult> { EntityId entityId = event.getEntityId(); switch (entityId.getEntityType()) { case JOB -> { + String tasksKey = event.getInfo().get("tasksKey").asText(); if (event.getEvent() == ComponentLifecycleEvent.STOPPED) { - log.info("Adding job {} to discarded", entityId); - addToDiscardedJobs(entityId.getId()); + log.info("Adding job {} ({}) to discarded", entityId, tasksKey); + addToDiscarded(tasksKey); + } else if (event.getEvent() == ComponentLifecycleEvent.FAILED) { + log.info("Adding job {} ({}) to failed", entityId, tasksKey); + failed.add(tasksKey); } } case TENANT -> { @@ -117,14 +123,18 @@ public abstract class TaskProcessor, R extends TaskResult> { try { @SuppressWarnings("unchecked") T task = (T) JacksonUtil.fromString(msg.getValue().getValue(), Task.class); - if (discardedJobs.contains(task.getJobId().getId())) { - log.debug("Skipping task for cancelled job {}: {}", task.getJobId(), task); + if (discarded.contains(task.getKey())) { + log.debug("Skipping task for discarded job {}: {}", task.getJobId(), task); reportTaskDiscarded(task); continue; + } else if (failed.contains(task.getKey())) { + log.debug("Skipping task for failed job {}: {}", task.getJobId(), task); + continue; } else if (deletedTenants.contains(task.getTenantId().getId())) { log.debug("Skipping task for deleted tenant {}: {}", task.getTenantId(), task); continue; } + processTask(task); } catch (InterruptedException e) { throw e; @@ -185,8 +195,8 @@ public abstract class TaskProcessor, R extends TaskResult> { statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); } - public void addToDiscardedJobs(UUID jobId) { - discardedJobs.add(jobId); + public void addToDiscarded(String tasksKey) { + discarded.add(tasksKey); } protected V wait(Future future) throws Exception { diff --git a/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java b/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java index 5903c10ac2..d153501b92 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java +++ b/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java @@ -37,9 +37,10 @@ import com.google.common.collect.Lists; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Contract; +import org.thingsboard.server.common.data.Views; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.Views; import java.io.File; import java.io.IOException; @@ -109,6 +110,7 @@ public class JacksonUtil { } } + @Contract("null, _ -> null") // so that IDE doesn't show NPE warning when input is not null public static T fromString(String string, Class clazz) { try { return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null; diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index 50b3e4bde8..cd001b61b4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; +import org.thingsboard.server.dao.service.ConstraintValidator; import java.util.Optional; @@ -119,6 +120,11 @@ public class DefaultJobService extends AbstractEntityService implements JobServi boolean publishEvent = false; for (TaskResult taskResult : jobStats.getTaskResults()) { + if (!taskResult.getKey().equals(job.getConfiguration().getTasksKey())) { + log.debug("Ignoring task result {} with outdated key {}", taskResult, job.getConfiguration().getTasksKey()); + continue; + } + result.processTaskResult(taskResult); if (result.getCancellationTs() > 0) { @@ -142,6 +148,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi publishEvent = true; } result.setFinishTs(System.currentTimeMillis()); + job.getConfiguration().setToReprocess(null); } } @@ -149,6 +156,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi } private Job saveJob(TenantId tenantId, Job job, boolean publishEvent, JobStatus prevStatus) { + ConstraintValidator.validateFields(job); job = jobDao.save(tenantId, job); if (publishEvent) { eventPublisher.publishEvent(SaveEntityEvent.builder() @@ -186,6 +194,15 @@ public class DefaultJobService extends AbstractEntityService implements JobServi return jobDao.findLatestByTenantIdAndKey(tenantId, key); } + @Override + public void deleteJob(TenantId tenantId, JobId jobId) { + Job job = findJobById(tenantId, jobId); + if (!job.getStatus().isOneOf(CANCELLED, COMPLETED, FAILED)) { + throw new IllegalArgumentException("Job must be cancelled, completed or failed"); + } + jobDao.removeById(tenantId, jobId.getId()); + } + private Job findForUpdate(TenantId tenantId, JobId jobId) { return jobDao.findByIdForUpdate(tenantId, jobId); } From 7a0c2b7763e7645dab4b34e9c0c28c6ad08740c7 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 15 May 2025 17:12:17 +0300 Subject: [PATCH 179/335] UI: Add to CF - Use message timestamp --- .../calculated-field-dialog.component.html | 13 ++++++++--- .../calculated-field-dialog.component.scss | 2 +- .../calculated-field-dialog.component.ts | 22 ++++++++++++++++++- .../shared/models/calculated-field.models.ts | 10 +++++++++ .../assets/locale/locale.constant-en_US.json | 4 +++- 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html index 8e9baa6f90..60f33ff005 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html @@ -160,8 +160,8 @@ }
@if (fieldFormGroup.get('type').value === CalculatedFieldType.SIMPLE) { -
- +
+ {{ (outputFormGroup.get('type').value === OutputType.Timeseries ? 'calculated-fields.timeseries-key' @@ -181,7 +181,7 @@ } - + {{ 'calculated-fields.decimals-by-default' | translate }} @if (outputFormGroup.get('decimalsByDefault').errors && outputFormGroup.get('decimalsByDefault').touched) { @@ -189,6 +189,13 @@ }
+
+ +
+ calculated-fields.use-message-timestamp +
+
+
}
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss index 8bc422eed1..efcd62efd4 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss @@ -45,7 +45,7 @@ &-key { color: #c24c1a; } - &-time-window, &-values, &-func, &-value, &-ts { + &-time-window, &-values, &-func, &-value, &-ts, &-msgTs { color: #7214D0; } &-start-ts, &-end-ts { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts index 52051aa6f7..4aa4eca425 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts @@ -77,6 +77,7 @@ export class CalculatedFieldDialogComponent extends DialogComponent Date: Thu, 15 May 2025 17:15:06 +0300 Subject: [PATCH 180/335] Refactor BaseController.checkEntityId --- .../server/controller/BaseController.java | 109 +++++------------- 1 file changed, 30 insertions(+), 79 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 73e278389a..620c773913 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -385,7 +385,7 @@ public abstract class BaseController { public void handleControllerException(Exception e, HttpServletResponse response) { ThingsboardException thingsboardException = handleException(e); if (thingsboardException.getErrorCode() == ThingsboardErrorCode.GENERAL && thingsboardException.getCause() instanceof Exception - && StringUtils.equals(thingsboardException.getCause().getMessage(), thingsboardException.getMessage())) { + && StringUtils.equals(thingsboardException.getCause().getMessage(), thingsboardException.getMessage())) { e = (Exception) thingsboardException.getCause(); } else { e = thingsboardException; @@ -430,7 +430,7 @@ public abstract class BaseController { if (exception instanceof ThingsboardException) { return (ThingsboardException) exception; } else if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException - || exception instanceof DataValidationException || cause instanceof IncorrectParameterException) { + || exception instanceof DataValidationException || cause instanceof IncorrectParameterException) { return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS); } else if (exception instanceof MessagingException) { return new ThingsboardException("Unable to send mail", ThingsboardErrorCode.GENERAL); @@ -602,88 +602,39 @@ public abstract class BaseController { } } - protected void checkEntityId(EntityId entityId, Operation operation) throws ThingsboardException { + protected HasId checkEntityId(EntityId entityId, Operation operation) throws ThingsboardException { try { if (entityId == null) { throw new ThingsboardException("Parameter entityId can't be empty!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } validateId(entityId.getId(), id -> "Incorrect entityId " + id); - switch (entityId.getEntityType()) { - case ALARM: - checkAlarmId(new AlarmId(entityId.getId()), operation); - return; - case DEVICE: - checkDeviceId(new DeviceId(entityId.getId()), operation); - return; - case DEVICE_PROFILE: - checkDeviceProfileId(new DeviceProfileId(entityId.getId()), operation); - return; - case CUSTOMER: - checkCustomerId(new CustomerId(entityId.getId()), operation); - return; - case TENANT: - checkTenantId(TenantId.fromUUID(entityId.getId()), operation); - return; - case TENANT_PROFILE: - checkTenantProfileId(new TenantProfileId(entityId.getId()), operation); - return; - case RULE_CHAIN: - checkRuleChain(new RuleChainId(entityId.getId()), operation); - return; - case RULE_NODE: - checkRuleNode(new RuleNodeId(entityId.getId()), operation); - return; - case ASSET: - checkAssetId(new AssetId(entityId.getId()), operation); - return; - case ASSET_PROFILE: - checkAssetProfileId(new AssetProfileId(entityId.getId()), operation); - return; - case DASHBOARD: - checkDashboardId(new DashboardId(entityId.getId()), operation); - return; - case USER: - checkUserId(new UserId(entityId.getId()), operation); - return; - case ENTITY_VIEW: - checkEntityViewId(new EntityViewId(entityId.getId()), operation); - return; - case EDGE: - checkEdgeId(new EdgeId(entityId.getId()), operation); - return; - case WIDGETS_BUNDLE: - checkWidgetsBundleId(new WidgetsBundleId(entityId.getId()), operation); - return; - case WIDGET_TYPE: - checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation); - return; - case TB_RESOURCE: - checkResourceInfoId(new TbResourceId(entityId.getId()), operation); - return; - case OTA_PACKAGE: - checkOtaPackageId(new OtaPackageId(entityId.getId()), operation); - return; - case QUEUE: - checkQueueId(new QueueId(entityId.getId()), operation); - return; - case OAUTH2_CLIENT: - checkOauth2ClientId(new OAuth2ClientId(entityId.getId()), operation); - return; - case DOMAIN: - checkDomainId(new DomainId(entityId.getId()), operation); - return; - case MOBILE_APP: - checkMobileAppId(new MobileAppId(entityId.getId()), operation); - return; - case MOBILE_APP_BUNDLE: - checkMobileAppBundleId(new MobileAppBundleId(entityId.getId()), operation); - return; - case CALCULATED_FIELD: - checkCalculatedFieldId(new CalculatedFieldId(entityId.getId()), operation); - return; - default: - checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation); - } + return switch (entityId.getEntityType()) { + case ALARM -> checkAlarmId(new AlarmId(entityId.getId()), operation); + case DEVICE -> checkDeviceId(new DeviceId(entityId.getId()), operation); + case DEVICE_PROFILE -> checkDeviceProfileId(new DeviceProfileId(entityId.getId()), operation); + case CUSTOMER -> checkCustomerId(new CustomerId(entityId.getId()), operation); + case TENANT -> checkTenantId(TenantId.fromUUID(entityId.getId()), operation); + case TENANT_PROFILE -> checkTenantProfileId(new TenantProfileId(entityId.getId()), operation); + case RULE_CHAIN -> checkRuleChain(new RuleChainId(entityId.getId()), operation); + case RULE_NODE -> checkRuleNode(new RuleNodeId(entityId.getId()), operation); + case ASSET -> checkAssetId(new AssetId(entityId.getId()), operation); + case ASSET_PROFILE -> checkAssetProfileId(new AssetProfileId(entityId.getId()), operation); + case DASHBOARD -> checkDashboardId(new DashboardId(entityId.getId()), operation); + case USER -> checkUserId(new UserId(entityId.getId()), operation); + case ENTITY_VIEW -> checkEntityViewId(new EntityViewId(entityId.getId()), operation); + case EDGE -> checkEdgeId(new EdgeId(entityId.getId()), operation); + case WIDGETS_BUNDLE -> checkWidgetsBundleId(new WidgetsBundleId(entityId.getId()), operation); + case WIDGET_TYPE -> checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation); + case TB_RESOURCE -> checkResourceInfoId(new TbResourceId(entityId.getId()), operation); + case OTA_PACKAGE -> checkOtaPackageId(new OtaPackageId(entityId.getId()), operation); + case QUEUE -> checkQueueId(new QueueId(entityId.getId()), operation); + case OAUTH2_CLIENT -> checkOauth2ClientId(new OAuth2ClientId(entityId.getId()), operation); + case DOMAIN -> checkDomainId(new DomainId(entityId.getId()), operation); + case MOBILE_APP -> checkMobileAppId(new MobileAppId(entityId.getId()), operation); + case MOBILE_APP_BUNDLE -> checkMobileAppBundleId(new MobileAppBundleId(entityId.getId()), operation); + case CALCULATED_FIELD -> checkCalculatedFieldId(new CalculatedFieldId(entityId.getId()), operation); + default -> (HasId) checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation); + }; } catch (Exception e) { throw handleException(e, false); } From 325c71f2ab7e5fab7419bd31dfed99e0ff570b4b Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 15 May 2025 18:25:17 +0300 Subject: [PATCH 181/335] Fix checkCalculatedFieldId --- .../java/org/thingsboard/server/controller/BaseController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 51735b6a9a..e5ea69dcb5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -908,12 +908,13 @@ public abstract class BaseController { } } - private void checkCalculatedFieldId(CalculatedFieldId calculatedFieldId, Operation operation) throws ThingsboardException { + private CalculatedField checkCalculatedFieldId(CalculatedFieldId calculatedFieldId, Operation operation) throws ThingsboardException { validateId(calculatedFieldId, "Invalid entity id"); SecurityUser user = getCurrentUser(); CalculatedField cf = calculatedFieldService.findById(user.getTenantId(), calculatedFieldId); checkNotNull(cf, calculatedFieldId.getEntityType().getNormalName() + " with id [" + calculatedFieldId + "] is not found"); checkEntityId(cf.getEntityId(), operation); + return cf; } protected HomeDashboardInfo getHomeDashboardInfo(SecurityUser securityUser, JsonNode additionalInfo) { From d1eec11d9d9872940923a5e15ad38fd8065d4951 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 15 May 2025 20:38:58 +0300 Subject: [PATCH 182/335] added tests for tbel doc examples --- .../service/script/TbelInvokeDocsIoTest.java | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java index 36ac8d54dc..a90f9f19b0 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeDocsIoTest.java @@ -1091,6 +1091,24 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { assertEquals(expected, actual); } + @Test + public void toInt_Test() throws ExecutionException, InterruptedException { + msgStr = "{}"; + decoderStr = """ + return{ + toInt1: toInt(0.3), + toInt2: toInt(0.5), + toInt3: toInt(2.7) + } + """; + LinkedHashMap expected = new LinkedHashMap<>(); + expected.put("toInt1", 0); + expected.put("toInt2", 1); + expected.put("toInt3", 3); + Object actual = invokeScript(evalScript(decoderStr), msgStr); + assertEquals(expected, actual); + } + @Test public void stringToBytesBinaryString_Test() throws ExecutionException, InterruptedException { String base64Str = "eyJoZWxsbyI6ICJ3b3JsZCJ9"; @@ -1173,7 +1191,9 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { "decimal": isDecimal("4567039"), "notDecimal": isDecimal("C100110"), "hexadecimal": isHexadecimal("F5D7039"), - "notHexadecimal": isHexadecimal("K100110") + "notHexadecimal": isHexadecimal("K100110"), + "nan": isNaN(0.0 / 0.0), + "number": isNaN(1.0) } """; LinkedHashMap expected = new LinkedHashMap<>(); @@ -1185,6 +1205,8 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { expected.put("notDecimal", -1); expected.put("hexadecimal", 16); expected.put("notHexadecimal", -1); + expected.put("nan", true); + expected.put("number", false); Object actual = invokeScript(evalScript(decoderStr), msgStr); assertEquals(expected, actual); } @@ -1460,7 +1482,6 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { "bytesLongToDouble1": parseBytesLongToDouble(coordinatesasBytes, offsetLatLong, 8, false) / factor, "bytesLongToDouble2": parseBytesLongToDouble(coordinatesasBytes, offsetLatLong + 8, 8, false) / factor, "bytesLongToExecutionArrayList": bytesToExecutionArrayList(bytesExecutionArrayList) - } """; LinkedHashMap expected = new LinkedHashMap<>(); @@ -1512,7 +1533,7 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { expected.put("hexToBytes", bytesToList(new byte[]{1, 117, 43, 3, 103, -6, 0, 5, 0, 1, 4, -120, -1, -1, -1, -1, -1, -1, -1, -1, 51})); // [-86, -69, -52, -35, -18] == new byte[]{(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE} expected.put("hexToBytesArray", bytesToList(new byte[]{(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE})); - assertEquals( expected, actual); + assertEquals(expected, actual); } // parseBinaryArray @@ -1769,6 +1790,7 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { Object actual = invokeScript(evalScript(decoderStr), msgStr); assertEquals(expected, actual); } + @Test public void bitwiseOperationsMix_Test() throws ExecutionException, InterruptedException { msgStr = "{}"; @@ -2425,6 +2447,42 @@ class TbelInvokeDocsIoTest extends AbstractTbelInvokeTest { assertFalse((Boolean) actual); } + @Test + public void isInsidePolygon_Test() throws ExecutionException, InterruptedException { + msgStr = "{}"; + decoderStr = """ + String perimeter = "[[[37.7810,-122.4210],[37.7890,-122.3900],[37.7700,-122.3800],[37.7600,-122.4000],[37.7700,-122.4250],[37.7810,-122.4210]],[[37.7730,-122.4050],[37.7700,-122.3950],[37.7670,-122.3980],[37.7690,-122.4100],[37.7730,-122.4050]]]"; + return{ + outsidePolygon: isInsidePolygon(37.8000, -122.4300, perimeter), + insidePolygon: isInsidePolygon(37.7725, -122.4010, perimeter), + insideHole: isInsidePolygon(37.7700, -122.4030, perimeter) + } + """; + LinkedHashMap expected = new LinkedHashMap<>(); + expected.put("outsidePolygon", false); + expected.put("insidePolygon", true); + expected.put("insideHole", false); + Object actual = invokeScript(evalScript(decoderStr), msgStr); + assertEquals(expected, actual); + } + + @Test + public void isInsideCircle_Test() throws ExecutionException, InterruptedException { + msgStr = "{}"; + decoderStr = """ + String perimeter = "{\\"latitude\\":37.7749,\\"longitude\\":-122.4194,\\"radius\\":3000,\\"radiusUnit\\":\\"METER\\"}"; + return{ + outsideCircle: isInsideCircle(37.8044, -122.2712, perimeter), + insideCircle: isInsideCircle(37.7599, -122.4148, perimeter) + } + """; + LinkedHashMap expected = new LinkedHashMap<>(); + expected.put("outsideCircle", false); + expected.put("insideCircle", true); + Object actual = invokeScript(evalScript(decoderStr), msgStr); + assertEquals(expected, actual); + } + private List splice(List oldList, int start, int deleteCount, Object... values) { start = initStartIndex(oldList, start); deleteCount = deleteCount < 0 ? 0 : Math.min(deleteCount, (oldList.size() - start)); From 8b14e72f8c15c8a3063af11c5c131b53585f6bbd Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 16 May 2025 14:01:44 +0300 Subject: [PATCH 183/335] UI: Refactoring after review --- ui-ngx/src/app/app.component.ts | 8 +- ui-ngx/src/app/core/api/widget-api.models.ts | 5 +- .../src/app/core/api/widget-subscription.ts | 2 +- ui-ngx/src/app/core/services/unit.service.ts | 17 ++- .../aggregated-data-key-row.component.html | 2 +- .../aggregated-data-key-row.component.ts | 6 +- ...rt-with-labels-basic-config.component.html | 2 +- .../range-chart-basic-config.component.html | 2 +- .../components/widget/widget.component.ts | 4 +- .../shared/components/unit-input.component.ts | 103 ++---------------- ui-ngx/src/app/shared/models/unit.models.ts | 95 +++++++++++++++- .../shared/models/widget-settings.models.ts | 22 ++-- 12 files changed, 137 insertions(+), 131 deletions(-) diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index dc3d539acb..17a473c8b8 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -33,7 +33,6 @@ import { svgIcons, svgIconsUrl } from '@shared/models/icon.models'; import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; import { SETTINGS_KEY } from '@core/settings/settings.effects'; import { initCustomJQueryEvents } from '@shared/models/jquery-event.models'; -import { UnitService } from '@core/services/unit.service'; @Component({ selector: 'tb-root', @@ -47,8 +46,7 @@ export class AppComponent { private translate: TranslateService, private matIconRegistry: MatIconRegistry, private domSanitizer: DomSanitizer, - private authService: AuthService, - private unitService: UnitService) { + private authService: AuthService) { console.log(`ThingsBoard Version: ${env.tbVersion}`); @@ -96,14 +94,12 @@ export class AppComponent { this.store.select(selectUserReady).pipe( filter((data) => data.isUserLoaded), tap((data) => { - const userDetails = getCurrentAuthState(this.store).userDetails; - let userLang = userDetails?.additionalInfo?.lang ?? null; + let userLang = getCurrentAuthState(this.store).userDetails?.additionalInfo?.lang ?? null; if (!userLang && !data.isAuthenticated) { const settings = this.storageService.getItem(SETTINGS_KEY); userLang = settings?.userLang ?? null; } this.notifyUserLang(userLang); - this.unitService.setUnitSystem(userDetails?.additionalInfo?.unitSystem); }), skip(1), ).subscribe((data) => { diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index 3338cffa25..8c4120739e 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -62,10 +62,11 @@ import { AlarmDataService } from '@core/api/alarm-data.service'; import { IDashboardController } from '@home/components/dashboard-page/dashboard-page.models'; import { PopoverPlacement } from '@shared/components/popover.models'; import { PersistentRpc } from '@shared/models/rpc.models'; -import { EventEmitter, Injector } from '@angular/core'; +import { EventEmitter } from '@angular/core'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { MatDialogRef } from '@angular/material/dialog'; import { TbUnit } from '@shared/models/unit.models'; +import { UnitService } from '@core/services/unit.service'; export interface TimewindowFunctions { onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void; @@ -234,8 +235,8 @@ export class WidgetSubscriptionContext { utils: UtilsService; dashboardUtils: DashboardUtilsService; raf: RafService; + unitService: UnitService; widgetUtils: IWidgetUtils; - $injector: Injector; getServerTimeDiff: () => Observable; } diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index d3a851016d..e86685bfe1 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -1419,7 +1419,7 @@ export class WidgetSubscription implements IWidgetSubscription { if (this.displayLegend) { const decimals = isDefinedAndNotNull(dataKey.decimals) ? dataKey.decimals : this.decimals; const units = isNotEmptyTbUnits(dataKey.units) ? dataKey.units : this.units; - const valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, {decimals, units}) + const valueFormat = ValueFormatProcessor.fromSettings(this.ctx.unitService, {decimals, units}) const legendKey: LegendKey = { dataKey, dataIndex: dataKeyIndex, diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index 5d60e80390..c68115f1a3 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -32,6 +32,10 @@ import { import { isNotEmptyStr, isObject } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { selectAuth, selectIsAuthenticated } from '@core/auth/auth.selectors'; +import { filter, switchMap, take } from 'rxjs/operators'; @Injectable({ providedIn: 'root' @@ -41,12 +45,19 @@ export class UnitService { private currentUnitSystem: UnitSystem = UnitSystem.METRIC; private converter: Converter; - constructor(private translate: TranslateService) { + constructor(private translate: TranslateService, + private store: Store) { this.translate.onLangChange.pipe( takeUntilDestroyed() ).subscribe(() => { this.converter = getUnitConverter(this.translate); }); + this.store.select(selectIsAuthenticated).pipe( + filter((data) => data), + switchMap(() => this.store.select(selectAuth).pipe(take(1))) + ).subscribe((data) => { + this.setUnitSystem(data.userDetails?.additionalInfo?.unitSystem) + }) } getUnitSystem(): UnitSystem { @@ -65,8 +76,8 @@ export class UnitService { return this.converter?.listUnits(measure, unitSystem); } - getUnitsGroupedByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure { - return this.converter?.unitsGroupByMeasure(measure, unitSystem); + getUnitsGroupedByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem, tagFilter?: string): UnitInfoGroupByMeasure { + return this.converter?.unitsGroupByMeasure(measure, unitSystem, tagFilter); } getUnitInfo(symbol: AllMeasuresUnits | string): UnitInfo { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html index f1c8787fff..9c32e5f74e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html @@ -48,7 +48,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts index 87b2fbbc48..2fee7a3a27 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts @@ -118,10 +118,6 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn return this.widgetConfigComponent.modelValue?.latestDataKeySettingsDirective; } - get supportsUnitConversion(): boolean { - return this.widgetConfigComponent.modelValue?.typeParameters?.supportsUnitConversion ?? false; - } - private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, @@ -220,7 +216,7 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn hideDataKeyName: true, hideDataKeyLabel: true, hideDataKeyColor: true, - supportsUnitConversion: this.supportsUnitConversion + supportsUnitConversion: true } }).afterClosed().subscribe((updatedDataKey) => { if (updatedDataKey) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html index 4b4dcd148d..307645822d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html @@ -100,7 +100,7 @@
widget-config.units-short
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html index f0d36264e9..608284996e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -84,7 +84,7 @@
widget-config.units-short
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index d5a0b78ce5..fed2615bbd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -105,6 +105,7 @@ import { ExceptionData } from '@shared/models/error.models'; import { WidgetComponentService } from './widget-component.service'; import { Timewindow } from '@shared/models/time/time.models'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; +import { UnitService } from '@core/services/unit.service'; import { DashboardService } from '@core/http/dashboard.service'; import { WidgetSubscription } from '@core/api/widget-subscription'; import { EntityService } from '@core/http/entity.service'; @@ -216,6 +217,7 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, private dashboardUtils: DashboardUtilsService, private mobileService: MobileService, private raf: RafService, + private unitService: UnitService, private ngZone: NgZone, private cd: ChangeDetectorRef, private http: HttpClient) { @@ -341,8 +343,8 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, this.subscriptionContext.utils = this.utils; this.subscriptionContext.dashboardUtils = this.dashboardUtils; this.subscriptionContext.raf = this.raf; + this.subscriptionContext.unitService = this.unitService; this.subscriptionContext.widgetUtils = this.widgetContext.utils; - this.subscriptionContext.$injector = this.injector; this.subscriptionContext.getServerTimeDiff = this.dashboardService.getServerTimeDiff.bind(this.dashboardService); this.widgetComponentService.getWidgetInfo(this.widget.typeFullFqn).subscribe({ diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index ff807e0e6f..ad63bee626 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -34,7 +34,9 @@ import { Observable, of, shareReplay } from 'rxjs'; import { AllMeasures, getSourceTbUnitSymbol, + getTbUnitFromSearch, isTbUnitMapping, + searchUnit, TbUnit, UnitInfo, UnitSystem @@ -43,7 +45,7 @@ import { map, mergeMap } from 'rxjs/operators'; import { UnitService } from '@core/services/unit.service'; import { TbPopoverService } from '@shared/components/popover.service'; import { UnitSettingsPanelComponent } from '@shared/components/unit-settings-panel.component'; -import { isDefinedAndNotNull, isEqual, isNotEmptyStr } from '@core/utils'; +import { isDefinedAndNotNull, isEqual } from '@core/utils'; @Component({ selector: 'tb-unit-input', @@ -200,7 +202,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang hostView: this.viewContainerRef, preferredPlacement: ['left', 'bottom', 'top'], context: { - unit: this.extractTbUnit(this.unitsFormControl.value), + unit: getTbUnitFromSearch(this.unitsFormControl.value), required: this.required, disabled: this.disabled, tagFilter: this.tagFilter, @@ -217,7 +219,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } private updateModel(value: UnitInfo | TbUnit ) { - let res = this.extractTbUnit(value); + let res = getTbUnitFromSearch(value); if (this.onlySystemUnits && !isTbUnitMapping(res)) { const unitInfo = this.unitService.getUnitInfo(res as string); if (unitInfo) { @@ -238,106 +240,17 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang private fetchUnits(searchText?: string): Observable]>> { this.searchText = searchText; return this.getGroupedUnits().pipe( - map(unit => this.searchUnit(unit, searchText)) + map(unit => searchUnit(unit, searchText)) ); } private getGroupedUnits(): Observable]>> { if (this.fetchUnits$ === null) { - this.fetchUnits$ = of(this.unitService.getUnitsGroupedByMeasure(this.measure, this.unitSystem)).pipe( - map(data => { - let objectData = Object.entries(data) as Array<[AllMeasures, UnitInfo[]]>; - - if (this.tagFilter) { - objectData = objectData - .map((measure) => [measure[0], measure[1].filter(u => u.tags.includes(this.tagFilter))] as [AllMeasures, UnitInfo[]]) - .filter((measure) => measure[1].length > 0); - } - return objectData; - }), + this.fetchUnits$ = of(this.unitService.getUnitsGroupedByMeasure(this.measure, this.unitSystem, this.tagFilter)).pipe( + map(data => Object.entries(data) as Array<[AllMeasures, UnitInfo[]]>), shareReplay(1) ); } return this.fetchUnits$; } - - private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { - if (isNotEmptyStr(searchText)) { - const filterValue = searchText.trim().toUpperCase(); - - const scoredGroups = units - .map(([measure, unitInfos]) => { - const scoredUnits = unitInfos - .map(unit => ({ - unit, - score: this.calculateRelevanceScore(unit, filterValue) - })) - .filter(({ score }) => score > 0) - .sort((a, b) => b.score - a.score) - .map(({ unit }) => unit); - - let groupScore = scoredUnits.length > 0 - ? Math.max(...scoredUnits.map(unit => this.calculateRelevanceScore(unit, filterValue))) - : 0; - - if (measure.toUpperCase() === filterValue) { - groupScore += 200; - } - - return { measure, units: scoredUnits, groupScore }; - }) - .filter(group => group.units.length > 0) - .sort((a, b) => { - if (b.groupScore !== a.groupScore) { - return b.groupScore - a.groupScore; - } - return b.units.length - a.units.length; - }); - - return scoredGroups.map(group => [group.measure, group.units] as [AllMeasures, Array]); - } - return units; - } - - private calculateRelevanceScore(unit: UnitInfo, filterValue: string): number { - const name = unit.name.toUpperCase(); - const abbr = unit.abbr.toUpperCase(); - const tags = unit.tags.map(tag => tag.toUpperCase()); - - let score = 0; - - if (name === filterValue || abbr === filterValue) { - score += 100; - } else if (tags.includes(filterValue)) { - score += 80; - } else if (name.startsWith(filterValue) || abbr.startsWith(filterValue)) { - score += 60; - } else if (tags.some(tag => tag.startsWith(filterValue))) { - score += 50; - } else if (tags.some(tag => tag.includes(filterValue))) { - score += 30; - } - - if (score > 0) { - score += Math.max(0, 10 - (name.length + abbr.length) / 2); - } - - return score; - } - - private extractTbUnit(value: TbUnit | UnitInfo | null): TbUnit { - if (value === null) { - return null; - } - if (value === undefined) { - return undefined; - } - if (typeof value === 'string') { - return value; - } - if ('abbr' in value) { - return value.abbr; - } - return value; - } } diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 94a831afa0..7d91e59441 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -104,7 +104,7 @@ import voltage, { VoltageUnits } from '@shared/models/units/voltage'; import volume, { VolumeUnits } from '@shared/models/units/volume'; import volumeFlow, { VolumeFlowUnits } from '@shared/models/units/volume-flow'; import { TranslateService } from '@ngx-translate/core'; -import { isNotEmptyStr } from '@core/utils'; +import { deepClone, isNotEmptyStr } from '@core/utils'; export type AllMeasuresUnits = | AbsorbedDoseRateUnits @@ -548,7 +548,7 @@ export class Converter { return results; } - unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure { + unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem, tagFilter?: string): UnitInfoGroupByMeasure { const results: UnitInfoGroupByMeasure = {}; const measures = measureName @@ -573,9 +573,15 @@ export class Converter { } for (const abbr of Object.keys(units) as AllMeasuresUnits[]) { - results[name].push(this.describe(abbr)); + const unitInfo = this.describe(abbr); + if (!tagFilter || unitInfo.tags.includes(tagFilter)) { + results[name].push(unitInfo); + } } } + if (!results[name].length) { + delete results[name]; + } } return results; } @@ -641,7 +647,7 @@ function buildUnitCache(measures: Record { if (typeof unit !== 'object' || unit === null) return false; return isNotEmptyStr(unit.from); }; + + +export const searchUnit = + (units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> => { + if (isNotEmptyStr(searchText)) { + const filterValue = searchText.trim().toUpperCase(); + + const scoredGroups = units + .map(([measure, unitInfos]) => { + const scoredUnits = unitInfos + .map(unit => ({ + unit, + score: calculateRelevanceScore(unit, filterValue) + })) + .filter(({score}) => score > 0) + .sort((a, b) => b.score - a.score) + .map(({unit}) => unit); + + let groupScore = scoredUnits.length > 0 + ? Math.max(...scoredUnits.map(unit => calculateRelevanceScore(unit, filterValue))) + : 0; + + if (measure.toUpperCase() === filterValue) { + groupScore += 200; + } + + return {measure, units: scoredUnits, groupScore}; + }) + .filter(group => group.units.length > 0) + .sort((a, b) => { + if (b.groupScore !== a.groupScore) { + return b.groupScore - a.groupScore; + } + return b.units.length - a.units.length; + }); + + return scoredGroups.map(group => [group.measure, group.units] as [AllMeasures, Array]); + } + return units; + } + +function calculateRelevanceScore (unit: UnitInfo, filterValue: string): number{ + const name = unit.name.toUpperCase(); + const abbr = unit.abbr.toUpperCase(); + const tags = unit.tags.map(tag => tag.toUpperCase()); + let score = 0; + + if (name === filterValue || abbr === filterValue) { + score += 100; + } else if (tags.includes(filterValue)) { + score += 80; + } else if (name.startsWith(filterValue) || abbr.startsWith(filterValue)) { + score += 60; + } else if (tags.some(tag => tag.startsWith(filterValue))) { + score += 50; + } else if (tags.some(tag => tag.includes(filterValue))) { + score += 30; + } + + if (score > 0) { + score += Math.max(0, 10 - (name.length + abbr.length) / 2); + } + + return score; +} + +export const getTbUnitFromSearch = (value: TbUnit | UnitInfo | null): TbUnit => { + if (value === null) { + return null; + } + if (value === undefined) { + return undefined; + } + if (typeof value === 'string') { + return value; + } + if ('abbr' in value) { + return value.abbr; + } + return value; +} diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index 971c888991..193a18cf89 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -875,15 +875,16 @@ export abstract class ValueFormatProcessor { protected hideZeroDecimals: boolean; protected unitSymbol: string; - static fromSettings($injector: Injector, settings: ValueFormatSettings): ValueFormatProcessor { + static fromSettings($injector: Injector, settings: ValueFormatSettings): ValueFormatProcessor; + static fromSettings(unitService: UnitService, settings: ValueFormatSettings): ValueFormatProcessor; + static fromSettings(unitServiceOrInjector: Injector | UnitService, settings: ValueFormatSettings): ValueFormatProcessor { if (settings.units !== null && typeof settings.units === 'object') { - return new UnitConverterValueFormatProcessor($injector, settings) + return new UnitConverterValueFormatProcessor(unitServiceOrInjector, settings) } - return new SimpleValueFormatProcessor($injector, settings); + return new SimpleValueFormatProcessor(settings); } - protected constructor(protected $injector: Injector, - protected settings: ValueFormatSettings) { + protected constructor(protected settings: ValueFormatSettings) { } abstract format(value: any): string; @@ -908,9 +909,8 @@ export class SimpleValueFormatProcessor extends ValueFormatProcessor { private readonly isDefinedUnit: boolean; - constructor(protected $injector: Injector, - protected settings: ValueFormatSettings) { - super($injector, settings); + constructor(protected settings: ValueFormatSettings) { + super(settings); this.unitSymbol = !settings.ignoreUnitSymbol && isNotEmptyStr(settings.units) ? (settings.units as string) : null; this.isDefinedDecimals = isDefinedAndNotNull(settings.decimals); this.hideZeroDecimals = !settings.showZeroDecimals; @@ -928,10 +928,10 @@ export class UnitConverterValueFormatProcessor extends ValueFormatProcessor { private readonly unitConverter: TbUnitConverter; - constructor(protected $injector: Injector, + constructor(protected unitServiceOrInjector: Injector | UnitService, protected settings: ValueFormatSettings) { - super($injector, settings); - const unitService = this.$injector.get(UnitService); + super(settings); + const unitService = this.unitServiceOrInjector instanceof UnitService ? this.unitServiceOrInjector : this.unitServiceOrInjector.get(UnitService); const unit = settings.units; this.unitSymbol = settings.ignoreUnitSymbol ? null : unitService.getTargetUnitSymbol(unit); this.unitConverter = unitService.geUnitConverter(unit); From 9922fba7a171bd8245073fda951ab7d92dc158e3 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 16 May 2025 16:41:52 +0300 Subject: [PATCH 184/335] UI: Refactoring after review --- ui-ngx/src/app/core/services/unit.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index c68115f1a3..1cba587a0c 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -47,6 +47,7 @@ export class UnitService { constructor(private translate: TranslateService, private store: Store) { + this.converter = getUnitConverter(this.translate); this.translate.onLangChange.pipe( takeUntilDestroyed() ).subscribe(() => { From cd5acf433664c401d569b89b214b927582769a71 Mon Sep 17 00:00:00 2001 From: Denys Sumin Date: Mon, 19 May 2025 11:17:06 +0300 Subject: [PATCH 185/335] remove all references of not supported queue providers --- docker/compose-utils.sh | 14 +---- docker/docker-compose.aws-sqs.yml | 61 --------------------- docker/docker-compose.pubsub.yml | 61 --------------------- docker/docker-compose.rabbitmq.yml | 61 --------------------- docker/docker-compose.service-bus.yml | 61 --------------------- docker/queue-aws-sqs.env | 4 -- docker/queue-pubsub.env | 3 - docker/queue-rabbitmq.env | 5 -- docker/queue-service-bus.env | 4 -- docker/tb-node/conf/logback.xml | 2 - docker/tb-transports/coap/conf/logback.xml | 2 - docker/tb-transports/http/conf/logback.xml | 2 - docker/tb-transports/lwm2m/conf/logback.xml | 1 - docker/tb-transports/mqtt/conf/logback.xml | 1 - docker/tb-transports/snmp/conf/logback.xml | 1 - docker/tb-vc-executor/conf/logback.xml | 1 - 16 files changed, 1 insertion(+), 283 deletions(-) delete mode 100644 docker/docker-compose.aws-sqs.yml delete mode 100644 docker/docker-compose.pubsub.yml delete mode 100644 docker/docker-compose.rabbitmq.yml delete mode 100644 docker/docker-compose.service-bus.yml delete mode 100644 docker/queue-aws-sqs.env delete mode 100644 docker/queue-pubsub.env delete mode 100644 docker/queue-rabbitmq.env delete mode 100644 docker/queue-service-bus.env diff --git a/docker/compose-utils.sh b/docker/compose-utils.sh index 995cd5a596..05a91257a2 100755 --- a/docker/compose-utils.sh +++ b/docker/compose-utils.sh @@ -42,20 +42,8 @@ function additionalComposeQueueArgs() { confluent) ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.confluent.yml" ;; - aws-sqs) - ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.aws-sqs.yml" - ;; - pubsub) - ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.pubsub.yml" - ;; - rabbitmq) - ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.rabbitmq.yml" - ;; - service-bus) - ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.service-bus.yml" - ;; *) - echo "Unknown Queue service TB_QUEUE_TYPE value specified in the .env file: '${TB_QUEUE_TYPE}'. Should be either 'kafka' or 'confluent' or 'aws-sqs' or 'pubsub' or 'rabbitmq' or 'service-bus'." >&2 + echo "Unknown Queue service TB_QUEUE_TYPE value specified in the .env file: '${TB_QUEUE_TYPE}'. Should be either 'kafka' or 'confluent'." >&2 exit 1 esac echo $ADDITIONAL_COMPOSE_QUEUE_ARGS diff --git a/docker/docker-compose.aws-sqs.yml b/docker/docker-compose.aws-sqs.yml deleted file mode 100644 index c86c4f73ad..0000000000 --- a/docker/docker-compose.aws-sqs.yml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright © 2016-2025 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. -# - -version: '3.0' - -services: - tb-js-executor: - env_file: - - queue-aws-sqs.env - tb-core1: - env_file: - - queue-aws-sqs.env - tb-core2: - env_file: - - queue-aws-sqs.env - tb-rule-engine1: - env_file: - - queue-aws-sqs.env - tb-rule-engine2: - env_file: - - queue-aws-sqs.env - tb-mqtt-transport1: - env_file: - - queue-aws-sqs.env - tb-mqtt-transport2: - env_file: - - queue-aws-sqs.env - tb-http-transport1: - env_file: - - queue-aws-sqs.env - tb-http-transport2: - env_file: - - queue-aws-sqs.env - tb-coap-transport: - env_file: - - queue-aws-sqs.env - tb-lwm2m-transport: - env_file: - - queue-aws-sqs.env - tb-snmp-transport: - env_file: - - queue-aws-sqs.env - tb-vc-executor1: - env_file: - - queue-aws-sqs.env - tb-vc-executor2: - env_file: - - queue-aws-sqs.env diff --git a/docker/docker-compose.pubsub.yml b/docker/docker-compose.pubsub.yml deleted file mode 100644 index ee276c786c..0000000000 --- a/docker/docker-compose.pubsub.yml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright © 2016-2025 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. -# - -version: '3.0' - -services: - tb-js-executor: - env_file: - - queue-pubsub.env - tb-core1: - env_file: - - queue-pubsub.env - tb-core2: - env_file: - - queue-pubsub.env - tb-rule-engine1: - env_file: - - queue-pubsub.env - tb-rule-engine2: - env_file: - - queue-pubsub.env - tb-mqtt-transport1: - env_file: - - queue-pubsub.env - tb-mqtt-transport2: - env_file: - - queue-pubsub.env - tb-http-transport1: - env_file: - - queue-pubsub.env - tb-http-transport2: - env_file: - - queue-pubsub.env - tb-coap-transport: - env_file: - - queue-pubsub.env - tb-lwm2m-transport: - env_file: - - queue-pubsub.env - tb-snmp-transport: - env_file: - - queue-pubsub.env - tb-vc-executor1: - env_file: - - queue-pubsub.env - tb-vc-executor2: - env_file: - - queue-pubsub.env diff --git a/docker/docker-compose.rabbitmq.yml b/docker/docker-compose.rabbitmq.yml deleted file mode 100644 index ed528ce098..0000000000 --- a/docker/docker-compose.rabbitmq.yml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright © 2016-2025 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. -# - -version: '3.0' - -services: - tb-js-executor: - env_file: - - queue-rabbitmq.env - tb-core1: - env_file: - - queue-rabbitmq.env - tb-core2: - env_file: - - queue-rabbitmq.env - tb-rule-engine1: - env_file: - - queue-rabbitmq.env - tb-rule-engine2: - env_file: - - queue-rabbitmq.env - tb-mqtt-transport1: - env_file: - - queue-rabbitmq.env - tb-mqtt-transport2: - env_file: - - queue-rabbitmq.env - tb-http-transport1: - env_file: - - queue-rabbitmq.env - tb-http-transport2: - env_file: - - queue-rabbitmq.env - tb-coap-transport: - env_file: - - queue-rabbitmq.env - tb-lwm2m-transport: - env_file: - - queue-rabbitmq.env - tb-snmp-transport: - env_file: - - queue-rabbitmq.env - tb-vc-executor1: - env_file: - - queue-rabbitmq.env - tb-vc-executor2: - env_file: - - queue-rabbitmq.env diff --git a/docker/docker-compose.service-bus.yml b/docker/docker-compose.service-bus.yml deleted file mode 100644 index d3e1e0d34c..0000000000 --- a/docker/docker-compose.service-bus.yml +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright © 2016-2025 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. -# - -version: '3.0' - -services: - tb-js-executor: - env_file: - - queue-service-bus.env - tb-core1: - env_file: - - queue-service-bus.env - tb-core2: - env_file: - - queue-service-bus.env - tb-rule-engine1: - env_file: - - queue-service-bus.env - tb-rule-engine2: - env_file: - - queue-service-bus.env - tb-mqtt-transport1: - env_file: - - queue-service-bus.env - tb-mqtt-transport2: - env_file: - - queue-service-bus.env - tb-http-transport1: - env_file: - - queue-service-bus.env - tb-http-transport2: - env_file: - - queue-service-bus.env - tb-coap-transport: - env_file: - - queue-service-bus.env - tb-lwm2m-transport: - env_file: - - queue-service-bus.env - tb-snmp-transport: - env_file: - - queue-service-bus.env - tb-vc-executor1: - env_file: - - queue-service-bus.env - tb-vc-executor2: - env_file: - - queue-service-bus.env diff --git a/docker/queue-aws-sqs.env b/docker/queue-aws-sqs.env deleted file mode 100644 index 1cb9fd65dd..0000000000 --- a/docker/queue-aws-sqs.env +++ /dev/null @@ -1,4 +0,0 @@ -TB_QUEUE_TYPE=aws-sqs -TB_QUEUE_AWS_SQS_ACCESS_KEY_ID=YOUR_KEY -TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY=YOUR_SECRET -TB_QUEUE_AWS_SQS_REGION=YOUR_REGION diff --git a/docker/queue-pubsub.env b/docker/queue-pubsub.env deleted file mode 100644 index 5b4aeba73b..0000000000 --- a/docker/queue-pubsub.env +++ /dev/null @@ -1,3 +0,0 @@ -TB_QUEUE_TYPE=pubsub -TB_QUEUE_PUBSUB_PROJECT_ID=YOUR_PROJECT_ID -TB_QUEUE_PUBSUB_SERVICE_ACCOUNT=YOUR_SERVICE_ACCOUNT diff --git a/docker/queue-rabbitmq.env b/docker/queue-rabbitmq.env deleted file mode 100644 index 7c355a4910..0000000000 --- a/docker/queue-rabbitmq.env +++ /dev/null @@ -1,5 +0,0 @@ -TB_QUEUE_TYPE=rabbitmq -TB_QUEUE_RABBIT_MQ_HOST=localhost -TB_QUEUE_RABBIT_MQ_PORT=5672 -TB_QUEUE_RABBIT_MQ_USERNAME=YOUR_USERNAME -TB_QUEUE_RABBIT_MQ_PASSWORD=YOUR_PASSWORD \ No newline at end of file diff --git a/docker/queue-service-bus.env b/docker/queue-service-bus.env deleted file mode 100644 index f54ce7e2fc..0000000000 --- a/docker/queue-service-bus.env +++ /dev/null @@ -1,4 +0,0 @@ -TB_QUEUE_TYPE=service-bus -TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME=YOUR_NAMESPACE_NAME -TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME=YOUR_SAS_KEY_NAME -TB_QUEUE_SERVICE_BUS_SAS_KEY=YOUR_SAS_KEY diff --git a/docker/tb-node/conf/logback.xml b/docker/tb-node/conf/logback.xml index c17e8a43cf..314f88a406 100644 --- a/docker/tb-node/conf/logback.xml +++ b/docker/tb-node/conf/logback.xml @@ -67,8 +67,6 @@ - - diff --git a/docker/tb-transports/coap/conf/logback.xml b/docker/tb-transports/coap/conf/logback.xml index b63b1e5384..43c6d4c4c8 100644 --- a/docker/tb-transports/coap/conf/logback.xml +++ b/docker/tb-transports/coap/conf/logback.xml @@ -42,8 +42,6 @@ - - diff --git a/docker/tb-transports/http/conf/logback.xml b/docker/tb-transports/http/conf/logback.xml index 2ef46344e4..14fe7faff8 100644 --- a/docker/tb-transports/http/conf/logback.xml +++ b/docker/tb-transports/http/conf/logback.xml @@ -42,8 +42,6 @@ - - diff --git a/docker/tb-transports/lwm2m/conf/logback.xml b/docker/tb-transports/lwm2m/conf/logback.xml index 10e34129df..db0f40bb05 100644 --- a/docker/tb-transports/lwm2m/conf/logback.xml +++ b/docker/tb-transports/lwm2m/conf/logback.xml @@ -42,7 +42,6 @@ - diff --git a/docker/tb-transports/mqtt/conf/logback.xml b/docker/tb-transports/mqtt/conf/logback.xml index e259f9fd56..81433a2d91 100644 --- a/docker/tb-transports/mqtt/conf/logback.xml +++ b/docker/tb-transports/mqtt/conf/logback.xml @@ -42,7 +42,6 @@ - diff --git a/docker/tb-transports/snmp/conf/logback.xml b/docker/tb-transports/snmp/conf/logback.xml index 201049e174..8d3472a9d8 100644 --- a/docker/tb-transports/snmp/conf/logback.xml +++ b/docker/tb-transports/snmp/conf/logback.xml @@ -42,7 +42,6 @@ - diff --git a/docker/tb-vc-executor/conf/logback.xml b/docker/tb-vc-executor/conf/logback.xml index f511b8b932..42420a3e4a 100644 --- a/docker/tb-vc-executor/conf/logback.xml +++ b/docker/tb-vc-executor/conf/logback.xml @@ -42,7 +42,6 @@ - From 5e46608abc718baccc1ecae05dce187655f81f93 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 19 May 2025 15:30:34 +0300 Subject: [PATCH 186/335] Support for job manager on Rule Engine --- .../server/actors/ActorSystemContext.java | 8 +- .../actors/ruleChain/DefaultTbContext.java | 8 +- .../server/controller/JobController.java | 2 +- .../entitiy/EntityStateSourcingListener.java | 7 +- .../server/service/job/DefaultJobManager.java | 65 +--------- .../server/service/job/JobStatsProcessor.java | 115 ++++++++++++++++++ .../server/service/job/JobManagerTest.java | 1 + .../InMemoryMonolithQueueFactory.java | 6 - .../provider/KafkaMonolithQueueFactory.java | 12 -- .../provider/KafkaTbCoreQueueFactory.java | 12 -- .../queue/provider/TbCoreQueueFactory.java | 4 - .../InMemoryTaskProducerQueueFactory.java | 40 ++++++ .../task/KafkaTaskProducerQueueFactory.java | 62 ++++++++++ .../queue/task/TaskProducerQueueFactory.java | 27 ++++ .../rule/engine/api}/JobManager.java | 2 +- .../rule/engine/api/TbContext.java | 4 +- 16 files changed, 269 insertions(+), 106 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/job/JobStatsProcessor.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/task/InMemoryTaskProducerQueueFactory.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/task/KafkaTaskProducerQueueFactory.java create mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProducerQueueFactory.java rename {application/src/main/java/org/thingsboard/server/service/job => rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api}/JobManager.java (95%) diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 228928123d..396ecd3771 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -31,6 +31,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.DeviceStateManager; +import org.thingsboard.rule.engine.api.JobManager; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.MqttClientSettings; import org.thingsboard.rule.engine.api.NotificationCenter; @@ -553,11 +554,14 @@ public class ActorSystemContext { @Getter private CalculatedFieldQueueService calculatedFieldQueueService; - @Lazy - @Autowired(required = false) + @Autowired @Getter private JobService jobService; + @Autowired + @Getter + private JobManager jobManager; + @Value("${actors.session.max_concurrent_sessions_per_device:1}") @Getter private int maxConcurrentSessionsPerDevice; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index d443b7e131..dff0cd4cf1 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -24,6 +24,7 @@ import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; import org.thingsboard.rule.engine.api.DeviceStateManager; +import org.thingsboard.rule.engine.api.JobManager; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.MqttClientSettings; import org.thingsboard.rule.engine.api.NotificationCenter; @@ -93,6 +94,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.mobile.MobileAppBundleService; import org.thingsboard.server.dao.mobile.MobileAppService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; @@ -108,7 +110,6 @@ import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; -import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.user.UserService; @@ -895,6 +896,11 @@ public class DefaultTbContext implements TbContext { return mainCtx.getJobService(); } + @Override + public JobManager getJobManager() { + return mainCtx.getJobManager(); + } + @Override public boolean isExternalNodeForceAck() { return mainCtx.isExternalNodeForceAck(); diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java index b301c2e321..9719306823 100644 --- a/application/src/main/java/org/thingsboard/server/controller/JobController.java +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -36,7 +36,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.job.JobManager; +import org.thingsboard.rule.engine.api.JobManager; import java.util.List; import java.util.UUID; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java index 83d7d60144..03ab77ac09 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/EntityStateSourcingListener.java @@ -59,9 +59,8 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.queue.TbQueueCallback; -import org.thingsboard.server.service.job.JobManager; +import org.thingsboard.rule.engine.api.JobManager; -import java.util.Optional; import java.util.Set; @Slf4j @@ -72,7 +71,7 @@ public class EntityStateSourcingListener { private final TenantService tenantService; private final TbClusterService tbClusterService; private final EdgeSynchronizationManager edgeSynchronizationManager; - private final Optional jobManager; + private final JobManager jobManager; @PostConstruct public void init() { @@ -303,7 +302,7 @@ public class EntityStateSourcingListener { } private void onJobUpdate(Job job) { - jobManager.ifPresent(jobManager -> jobManager.onJobUpdate(job)); + jobManager.onJobUpdate(job); ComponentLifecycleEvent event; if (job.getResult().getCancellationTs() > 0) { diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index ac237d7310..035de3c1d7 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -16,20 +16,18 @@ package org.thingsboard.server.service.job; import jakarta.annotation.PreDestroy; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; -import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.rule.engine.api.JobManager; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobResult; -import org.thingsboard.server.common.data.job.JobStats; import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.Task; @@ -41,28 +39,22 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.notification.DefaultNotifications; -import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.queue.TbQueueCallback; -import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.common.consumer.QueueConsumerManager; import org.thingsboard.server.queue.discovery.PartitionService; -import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.settings.TasksQueueConfig; import org.thingsboard.server.queue.task.JobStatsService; -import org.thingsboard.server.queue.util.AfterStartUp; +import org.thingsboard.server.queue.task.TaskProducerQueueFactory; import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.function.Function; import java.util.stream.Collectors; @@ -78,12 +70,10 @@ public class DefaultJobManager implements JobManager { private final TasksQueueConfig queueConfig; private final Map jobProcessors; private final Map>> taskProducers; - private final QueueConsumerManager> jobStatsConsumer; private final ExecutorService executor; - private final ExecutorService consumerExecutor; public DefaultJobManager(JobService jobService, JobStatsService jobStatsService, NotificationCenter notificationCenter, - PartitionService partitionService, TbCoreQueueFactory queueFactory, TasksQueueConfig queueConfig, + PartitionService partitionService, TaskProducerQueueFactory queueFactory, TasksQueueConfig queueConfig, List jobProcessors) { this.jobService = jobService; this.jobStatsService = jobStatsService; @@ -93,20 +83,6 @@ public class DefaultJobManager implements JobManager { this.jobProcessors = jobProcessors.stream().collect(Collectors.toMap(JobProcessor::getType, Function.identity())); this.taskProducers = Arrays.stream(JobType.values()).collect(Collectors.toMap(Function.identity(), queueFactory::createTaskProducer)); this.executor = ThingsBoardExecutors.newWorkStealingPool(Math.max(4, Runtime.getRuntime().availableProcessors()), getClass()); - this.consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("job-stats-consumer")); - this.jobStatsConsumer = QueueConsumerManager.>builder() - .name("job-stats") - .msgPackProcessor(this::processStats) - .pollInterval(queueConfig.getStatsPollInterval()) - .consumerCreator(queueFactory::createJobStatsConsumer) - .consumerExecutor(consumerExecutor) - .build(); - } - - @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) - public void afterStartUp() { - jobStatsConsumer.subscribe(); - jobStatsConsumer.launch(); } @Override @@ -229,39 +205,6 @@ public class DefaultJobManager implements JobManager { }); } - @SneakyThrows - private void processStats(List> msgs, TbQueueConsumer> consumer) { - Map stats = new HashMap<>(); - - for (TbProtoQueueMsg msg : msgs) { - JobStatsMsg statsMsg = msg.getValue(); - TenantId tenantId = TenantId.fromUUID(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); - JobId jobId = new JobId(new UUID(statsMsg.getJobIdMSB(), statsMsg.getJobIdLSB())); - JobStats jobStats = stats.computeIfAbsent(jobId, __ -> new JobStats(tenantId, jobId)); - - if (statsMsg.hasTaskResult()) { - TaskResult taskResult = JacksonUtil.fromString(statsMsg.getTaskResult().getValue(), TaskResult.class); - jobStats.getTaskResults().add(taskResult); - } - if (statsMsg.hasTotalTasksCount()) { - jobStats.setTotalTasksCount(statsMsg.getTotalTasksCount()); - } - } - - stats.forEach((jobId, jobStats) -> { - TenantId tenantId = jobStats.getTenantId(); - try { - log.debug("[{}][{}] Processing job stats: {}", tenantId, jobId, stats); - jobService.processStats(tenantId, jobId, jobStats); - } catch (Exception e) { - log.error("[{}][{}] Failed to process job stats: {}", tenantId, jobId, jobStats, e); - } - }); - consumer.commit(); - - Thread.sleep(queueConfig.getStatsProcessingInterval()); - } - private void sendJobFinishedNotification(Job job) { NotificationTemplate template = DefaultNotifications.DefaultNotification.builder() .name("Job finished") @@ -284,9 +227,7 @@ public class DefaultJobManager implements JobManager { @PreDestroy private void destroy() { - jobStatsConsumer.stop(); executor.shutdownNow(); - consumerExecutor.shutdownNow(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobStatsProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/JobStatsProcessor.java new file mode 100644 index 0000000000..c5b2577819 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/job/JobStatsProcessor.java @@ -0,0 +1,115 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.job; + +import jakarta.annotation.PreDestroy; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.id.JobId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.JobStats; +import org.thingsboard.server.common.data.job.task.TaskResult; +import org.thingsboard.server.dao.job.JobService; +import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; +import org.thingsboard.server.queue.TbQueueConsumer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.common.consumer.QueueConsumerManager; +import org.thingsboard.server.queue.provider.TbCoreQueueFactory; +import org.thingsboard.server.queue.settings.TasksQueueConfig; +import org.thingsboard.server.queue.util.AfterStartUp; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@TbCoreComponent +@Component +@Slf4j +public class JobStatsProcessor { + + private final JobService jobService; + private final TasksQueueConfig queueConfig; + private final QueueConsumerManager> jobStatsConsumer; + private final ExecutorService consumerExecutor; + + public JobStatsProcessor(JobService jobService, + TasksQueueConfig queueConfig, + TbCoreQueueFactory queueFactory) { + this.jobService = jobService; + this.queueConfig = queueConfig; + this.consumerExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("job-stats-consumer")); + this.jobStatsConsumer = QueueConsumerManager.>builder() + .name("job-stats") + .msgPackProcessor(this::processStats) + .pollInterval(queueConfig.getStatsPollInterval()) + .consumerCreator(queueFactory::createJobStatsConsumer) + .consumerExecutor(consumerExecutor) + .build(); + } + + @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) + public void afterStartUp() { + jobStatsConsumer.subscribe(); + jobStatsConsumer.launch(); + } + + @SneakyThrows + private void processStats(List> msgs, TbQueueConsumer> consumer) { + Map stats = new HashMap<>(); + + for (TbProtoQueueMsg msg : msgs) { + JobStatsMsg statsMsg = msg.getValue(); + TenantId tenantId = TenantId.fromUUID(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); + JobId jobId = new JobId(new UUID(statsMsg.getJobIdMSB(), statsMsg.getJobIdLSB())); + JobStats jobStats = stats.computeIfAbsent(jobId, __ -> new JobStats(tenantId, jobId)); + + if (statsMsg.hasTaskResult()) { + TaskResult taskResult = JacksonUtil.fromString(statsMsg.getTaskResult().getValue(), TaskResult.class); + jobStats.getTaskResults().add(taskResult); + } + if (statsMsg.hasTotalTasksCount()) { + jobStats.setTotalTasksCount(statsMsg.getTotalTasksCount()); + } + } + + stats.forEach((jobId, jobStats) -> { + TenantId tenantId = jobStats.getTenantId(); + try { + log.debug("[{}][{}] Processing job stats: {}", tenantId, jobId, stats); + jobService.processStats(tenantId, jobId, jobStats); + } catch (Exception e) { + log.error("[{}][{}] Failed to process job stats: {}", tenantId, jobId, jobStats, e); + } + }); + consumer.commit(); + + Thread.sleep(queueConfig.getStatsProcessingInterval()); + } + + @PreDestroy + private void destroy() { + jobStatsConsumer.stop(); + consumerExecutor.shutdownNow(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 3ae3bf5686..dd19ddcea1 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -23,6 +23,7 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; +import org.thingsboard.rule.engine.api.JobManager; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.DummyJobConfiguration; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java index ddaf34715f..d3233f8dee 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java @@ -20,7 +20,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; @@ -262,11 +261,6 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE .build(); } - @Override - public TbQueueProducer> createTaskProducer(JobType jobType) { - return new InMemoryTbQueueProducer<>(storage, jobType.getTasksTopic()); - } - @Override public TbQueueConsumer> createJobStatsConsumer() { return new InMemoryTbQueueConsumer<>(storage, tasksQueueConfig.getStatsTopic()); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index 902848f65f..866f8d235e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -23,7 +23,6 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; @@ -31,7 +30,6 @@ import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -650,16 +648,6 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi .build(); } - @Override - public TbQueueProducer> createTaskProducer(JobType jobType) { - return TbKafkaProducerTemplate.>builder() - .clientId(jobType.name().toLowerCase() + "-task-producer-" + serviceInfoProvider.getServiceId()) - .defaultTopic(topicService.buildTopicName(jobType.getTasksTopic())) - .settings(kafkaSettings) - .admin(tasksAdmin) - .build(); - } - @Override public TbQueueConsumer> createJobStatsConsumer() { return TbKafkaConsumerTemplate.>builder() diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index 048b08f15a..70009aa29d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -22,12 +22,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.FromEdqsMsg; import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -529,16 +527,6 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { .build(); } - @Override - public TbQueueProducer> createTaskProducer(JobType jobType) { - return TbKafkaProducerTemplate.>builder() - .clientId(jobType.name().toLowerCase() + "-task-producer-" + serviceInfoProvider.getServiceId()) - .defaultTopic(topicService.buildTopicName(jobType.getTasksTopic())) - .settings(kafkaSettings) - .admin(tasksAdmin) - .build(); - } - @Override public TbQueueConsumer> createJobStatsConsumer() { return TbKafkaConsumerTemplate.>builder() diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java index e47354941c..37c15d5b87 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java @@ -17,10 +17,8 @@ package org.thingsboard.server.queue.provider; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.JobStatsMsg; -import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCalculatedFieldNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -168,8 +166,6 @@ public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory, Hous TbQueueProducer> createToCalculatedFieldNotificationMsgProducer(); - TbQueueProducer> createTaskProducer(JobType jobType); - TbQueueConsumer> createJobStatsConsumer(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/InMemoryTaskProducerQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/InMemoryTaskProducerQueueFactory.java new file mode 100644 index 0000000000..7f0cae7eb1 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/InMemoryTaskProducerQueueFactory.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.queue.task; + +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.memory.InMemoryStorage; +import org.thingsboard.server.queue.memory.InMemoryTbQueueProducer; + +@Component +@ConditionalOnExpression("'${queue.type:null}' == 'in-memory'") +@RequiredArgsConstructor +public class InMemoryTaskProducerQueueFactory implements TaskProducerQueueFactory { + + private final InMemoryStorage storage; + + @Override + public TbQueueProducer> createTaskProducer(JobType jobType) { + return new InMemoryTbQueueProducer<>(storage, jobType.getTasksTopic()); + } + +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/KafkaTaskProducerQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/KafkaTaskProducerQueueFactory.java new file mode 100644 index 0000000000..b19db211fe --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/KafkaTaskProducerQueueFactory.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.queue.task; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.queue.TbQueueAdmin; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; +import org.thingsboard.server.queue.kafka.TbKafkaAdmin; +import org.thingsboard.server.queue.kafka.TbKafkaProducerTemplate; +import org.thingsboard.server.queue.kafka.TbKafkaSettings; +import org.thingsboard.server.queue.kafka.TbKafkaTopicConfigs; + +@Component +@ConditionalOnExpression("'${queue.type:null}' == 'kafka' && ('${service.type:null}' == 'monolith' || " + + "'${service.type:null}' == 'tb-core' || '${service.type:null}' == 'tb-rule-engine')") +public class KafkaTaskProducerQueueFactory implements TaskProducerQueueFactory { + + private final TopicService topicService; + private final TbServiceInfoProvider serviceInfoProvider; + private final TbKafkaSettings kafkaSettings; + private final TbQueueAdmin tasksAdmin; + + KafkaTaskProducerQueueFactory(TopicService topicService, + TbServiceInfoProvider serviceInfoProvider, + TbKafkaSettings kafkaSettings, + TbKafkaTopicConfigs kafkaTopicConfigs) { + this.topicService = topicService; + this.kafkaSettings = kafkaSettings; + this.serviceInfoProvider = serviceInfoProvider; + this.tasksAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getTasksConfigs()); + } + + @Override + public TbQueueProducer> createTaskProducer(JobType jobType) { + return TbKafkaProducerTemplate.>builder() + .clientId(jobType.name().toLowerCase() + "-task-producer-" + serviceInfoProvider.getServiceId()) + .defaultTopic(topicService.buildTopicName(jobType.getTasksTopic())) + .settings(kafkaSettings) + .admin(tasksAdmin) + .build(); + } + +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProducerQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProducerQueueFactory.java new file mode 100644 index 0000000000..ffb64a07ce --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProducerQueueFactory.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.queue.task; + +import org.thingsboard.server.common.data.job.JobType; +import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; + +public interface TaskProducerQueueFactory { + + TbQueueProducer> createTaskProducer(JobType jobType); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/job/JobManager.java rename to rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java index 8e4858ebe3..3ee29dd7c0 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobManager.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.job; +package org.thingsboard.rule.engine.api; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 4b38975504..16f2936964 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -62,6 +62,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.mobile.MobileAppBundleService; import org.thingsboard.server.dao.mobile.MobileAppService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; @@ -77,7 +78,6 @@ import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; -import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.user.UserService; @@ -365,6 +365,8 @@ public interface TbContext { JobService getJobService(); + JobManager getJobManager(); + boolean isExternalNodeForceAck(); /** From df2d8cc895fb3b826afd37aadb760a18996cd4d4 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 19 May 2025 15:35:18 +0300 Subject: [PATCH 187/335] Remove TbCoreComponent from DefaultJobManager --- .../org/thingsboard/server/service/job/DefaultJobManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 035de3c1d7..314581acef 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -48,7 +48,6 @@ import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.settings.TasksQueueConfig; import org.thingsboard.server.queue.task.JobStatsService; import org.thingsboard.server.queue.task.TaskProducerQueueFactory; -import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.Arrays; import java.util.List; @@ -58,7 +57,6 @@ import java.util.concurrent.ExecutorService; import java.util.function.Function; import java.util.stream.Collectors; -@TbCoreComponent @Component @Slf4j public class DefaultJobManager implements JobManager { From 1ff416fc5ae90ce0efa9014937c065871d05c40c Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 19 May 2025 16:40:01 +0300 Subject: [PATCH 188/335] Translation Pruner v1 --- .../client/tools/i18n/TranslationPruner.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java diff --git a/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java new file mode 100644 index 0000000000..0e4516cd63 --- /dev/null +++ b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java @@ -0,0 +1,84 @@ +package org.thingsboard.client.tools.i18n; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + + +public class TranslationPruner { + + /** + * Recursively collect all JSON keys in dot notation from the given node. + */ + private static void collectKeys(JsonNode node, String prefix, Set keys) { + if (!node.isObject()) return; + Iterator> fields = node.fields(); + while (fields.hasNext()) { + Map.Entry entry = fields.next(); + String key = entry.getKey(); + String fullKey = prefix.isEmpty() ? key : prefix + "." + key; + keys.add(fullKey); + collectKeys(entry.getValue(), fullKey, keys); + } + } + + /** + * Prune the translation ObjectNode, keeping only fields whose dot-keys are in the valid set. + */ + private static ObjectNode pruneNode(ObjectNode node, Set keys, String prefix, ObjectMapper mapper) { + ObjectNode pruned = mapper.createObjectNode(); + Iterator> fields = node.fields(); + while (fields.hasNext()) { + Map.Entry entry = fields.next(); + String key = entry.getKey(); + JsonNode value = entry.getValue(); + String fullKey = prefix.isEmpty() ? key : prefix + "." + key; + if (keys.contains(fullKey)) { + if (value.isObject()) { + ObjectNode child = pruneNode((ObjectNode) value, keys, fullKey, mapper); + pruned.set(key, child); + } else { + pruned.set(key, value); + } + } + } + return pruned; + } + + public static void main(String[] args) { + if (args.length < 3) { + System.err.println("Usage: java TranslationPruner [output.json]"); + System.exit(1); + } + try { + File sourceFile = new File(args[0]); + File referenceFile = new File(args[1]); + File outputFile = new File(args[2]); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode usRoot = mapper.readTree(referenceFile); + Set validKeys = new HashSet<>(); + collectKeys(usRoot, "", validKeys); + + JsonNode sourceRoot = mapper.readTree(sourceFile); + if (!sourceRoot.isObject()) { + throw new IllegalArgumentException("Source JSON must be an object at root"); + } + ObjectNode pruned = pruneNode((ObjectNode) sourceRoot, validKeys, "", mapper); + + mapper.writerWithDefaultPrettyPrinter().writeValue(outputFile, pruned); + System.out.println("Pruned translation written to " + outputFile.getPath()); + } catch (IOException e) { + e.printStackTrace(); + System.exit(2); + } + } + +} From a1ad345f86b195ed7c96d8e839d7d8168f409777 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 19 May 2025 16:44:24 +0300 Subject: [PATCH 189/335] TranslationPruner v2 --- .../client/tools/i18n/TranslationPruner.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java index 0e4516cd63..73fe2ce94e 100644 --- a/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java +++ b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java @@ -53,28 +53,30 @@ public class TranslationPruner { } public static void main(String[] args) { - if (args.length < 3) { - System.err.println("Usage: java TranslationPruner [output.json]"); + if (args.length < 2) { + System.err.println("Usage: `java TranslationPruner `, where dest folder must contain the locale.constant-en_US.json for reference structure."); System.exit(1); } try { - File sourceFile = new File(args[0]); - File referenceFile = new File(args[1]); - File outputFile = new File(args[2]); + File sourceFolder = new File(args[0]); + File destFolder = new File(args[1]); + File referenceFile = new File(destFolder, "locale.constant-en_US.json"); ObjectMapper mapper = new ObjectMapper(); JsonNode usRoot = mapper.readTree(referenceFile); Set validKeys = new HashSet<>(); collectKeys(usRoot, "", validKeys); + for (File sourceFile : sourceFolder.listFiles()) { + File destFile = new File(destFolder, sourceFile.getName()); + JsonNode sourceRoot = mapper.readTree(sourceFile); + if (!sourceRoot.isObject()) { + throw new IllegalArgumentException("Source JSON must be an object at root"); + } + ObjectNode pruned = pruneNode((ObjectNode) sourceRoot, validKeys, "", mapper); - JsonNode sourceRoot = mapper.readTree(sourceFile); - if (!sourceRoot.isObject()) { - throw new IllegalArgumentException("Source JSON must be an object at root"); + mapper.writerWithDefaultPrettyPrinter().writeValue(destFile, pruned); + System.out.println("Pruned translation written to " + destFile.getPath()); } - ObjectNode pruned = pruneNode((ObjectNode) sourceRoot, validKeys, "", mapper); - - mapper.writerWithDefaultPrettyPrinter().writeValue(outputFile, pruned); - System.out.println("Pruned translation written to " + outputFile.getPath()); } catch (IOException e) { e.printStackTrace(); System.exit(2); From 09854dcb764470cabf496428824f7de5113d0b22 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 19 May 2025 17:08:13 +0300 Subject: [PATCH 190/335] Fix separator to avoid extra merge conflicts --- .../client/tools/i18n/TranslationPruner.java | 11 +- .../assets/locale/locale.constant-da_DK.json | 13087 +++++++++---- .../assets/locale/locale.constant-de_DE.json | 11343 +++++++++--- .../assets/locale/locale.constant-es_ES.json | 15190 ++++++++++------ .../assets/locale/locale.constant-fr_FR.json | 11509 +++++++++--- .../assets/locale/locale.constant-it_IT.json | 10905 +++++++++-- 6 files changed, 45960 insertions(+), 16085 deletions(-) diff --git a/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java index 73fe2ce94e..f8f9ef18b1 100644 --- a/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java +++ b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java @@ -1,8 +1,14 @@ package org.thingsboard.client.tools.i18n; +import com.fasterxml.jackson.core.FormatFeature; +import com.fasterxml.jackson.core.PrettyPrinter; +import com.fasterxml.jackson.core.util.Separators; +import com.fasterxml.jackson.core.util.Separators.Spacing; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import java.io.File; import java.io.IOException; @@ -73,8 +79,9 @@ public class TranslationPruner { throw new IllegalArgumentException("Source JSON must be an object at root"); } ObjectNode pruned = pruneNode((ObjectNode) sourceRoot, validKeys, "", mapper); - - mapper.writerWithDefaultPrettyPrinter().writeValue(destFile, pruned); + Separators seps = Separators.createDefaultInstance() + .withObjectFieldValueSpacing(Spacing.AFTER); + mapper.writer(new DefaultPrettyPrinter().withSeparators(seps)).writeValue(destFile, pruned); System.out.println("Pruned translation written to " + destFile.getPath()); } } catch (IOException e) { diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index e96ec45e3e..1db706b6d6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -1,3917 +1,9224 @@ { - "access": { - "unauthorized": "Uautoriseret", - "unauthorized-access": "Uautoriseret adgang", - "unauthorized-access-text": "Du skal logge ind for at få adgang til denne ressource!", - "access-forbidden": "Adgang forbudt", - "access-forbidden-text": "Du har ikke adgangsrettigheder til denne placering!
Prøv at logge ind med en anden bruger, hvis du stadig ønsker at få adgang til denne placering.", - "refresh-token-expired": "Sessionen er udløbet", - "refresh-token-failed": "Kunne ikke opdatere session", - "permission-denied": "Tilladelse nægtet", - "permission-denied-text": "Du har ikke tilladelse til at udføre denne handling!" - }, - "action": { - "activate": "Aktivér", - "suspend": "Udsæt", - "save": "Gem", - "saveAs": "Gem som", - "cancel": "Annuller", - "ok": "Okay", - "delete": "Slet", - "add": "Tilføj", - "yes": "Ja", - "no": "Nej", - "update": "Opdatering", - "remove": "Fjern", - "search": "Søg", - "clear-search": "Ryd søgning", - "assign": "Tildel", - "unassign": "Fjern tildeling", - "share": "Del", - "make-private": "Gør privat", - "make-public": "Gør offentlig", - "apply": "Anvend", - "apply-changes": "Anvend ændringer", - "edit-mode": "Redigeringstilstand", - "enter-edit-mode": "Gå til redigeringstilstand", - "decline-changes": "Afvis ændringer", - "open": "Åbn", - "close": "Tæt", - "back": "Tilbage", - "run": "Kør", - "sign-in": "Log på!", - "edit": "Rediger", - "view": "Vis", - "create": "Opret", - "drag": "Træk", - "refresh": "Genopfrisk", - "undo": "Fortryd", - "copy": "Kopiér", - "paste": "Sæt ind", - "copy-reference": "Kopiér reference", - "paste-reference": "Indsæt reference", - "import": "Importér", - "export": "Eksportér", - "share-via": "", - "move": "Flyt", - "select": "Vælg", - "continue": "Fortsæt", - "discard-changes": "Kassér ændringer", - "download": "Download", - "next-with-label": "", - "read-more": "Læs mere", - "hide": "Skjul" - }, - "aggregation": { - "aggregation": "Opsamling", - "function": "Dataopsamlingsfunktion", - "limit": "Maks.-værdier", - "group-interval": "Grupperingsinterval", - "min": "Min", - "max": "Maks", - "avg": "Gennemsnit", - "sum": "I alt", - "count": "Sammentælling", - "none": "Ingen" - }, - "admin": { - "general": "Generelt", - "general-settings": "Generelle indstillinger", - "home-settings": "Hjem-indstillinger", - "outgoing-mail": "Mailserver", - "outgoing-mail-settings": "Udgående mailserverindstillinger", - "system-settings": "Systemindstillinger", - "test-mail-sent": "Test-e-mail blev sendt!", - "base-url": "Basis-URL", - "base-url-required": "Basis-URL er påkrævet.", - "prohibit-different-url": "Det er ikke tilladt at bruge værtsnavn fra overskrifterne for klientanmodninger", - "prohibit-different-url-hint": "Denne indstilling skal aktiveres for produktionsmiljøer. Kan forårsage sikkerhedsproblemer ved deaktivering", - "mail-from": "Afsender", - "mail-from-required": "Afsender er påkrævet.", - "smtp-protocol": "SMTP-protokol", - "smtp-host": "SMTP-vært", - "smtp-host-required": "SMTP-vært er påkrævet.", - "smtp-port": "SMTP-port", - "smtp-port-required": "Du skal angive en smtp-port.", - "smtp-port-invalid": "Det ligner ikke en gyldig smtp-port.", - "timeout-msec": "Timeout (msek.)", - "timeout-required": "Timeout er påkrævet.", - "timeout-invalid": "Det ser ikke ud til at være en gyldig timeout.", - "enable-tls": "Aktivér TLS", - "tls-version": "TLS-version", - "enable-proxy": "Aktivér proxy", - "proxy-host": "Proxy-vært", - "proxy-host-required": "Proxy-vært er påkrævet.", - "proxy-port": "Proxy-port", - "proxy-port-required": "Proxy-port er påkrævet.", - "proxy-port-range": "Proxy-porten skal ligge i området fra 1 til 65535.", - "proxy-user": "Proxy-bruger", - "proxy-password": "Proxy-adgangskode", - "send-test-mail": "Send testmail", - "use-system-mail-settings": "Brug systemets mailserverindstillinger", - "mail-templates": "Mailskabeloner", - "mail-template-settings": "Indstillinger for mailskabeloner", - "use-system-mail-template-settings": "Brug systemmailskabeloner", - "mail-template": { - "mail-template": "Mailskabelon", - "test": "Test-e-mailmeddelelse", - "activation": "Meddelelse om aktivering af konto", - "account-activated": "Meddelelse om aktiveret konto", - "account-lockout": "Meddelelse om kontospærring", - "reset-password": "Meddelelse om nulstilling af adgangskode", - "password-was-reset": "Meddelelse om nulstillet adgangskode", - "user-activated": "Brugeraktiveret-meddelelse", - "user-registered": "Brugerregistreret-meddelelse", - "api-usage-state-enabled": "Api-brugstilstand aktiveret", - "api-usage-state-warning": "Advarsel om Api-brugstilstand", - "api-usage-state-disabled": "Api-brugstilstand deaktiveret" - }, - "mail-subject": "Mailens emne", - "mail-body": "Mailens brødtekst", - "sms-provider": "SMS-udbyder", - "sms-provider-settings": "Indstillinger for SMS-udbyder", - "use-system-sms-settings": "Anvend systemets indstillinger for SMS-udbyder", - "sms-provider-type": "SMS-udbydertype", - "sms-provider-type-required": "SMS-udbydertype er påkrævet.", - "sms-provider-type-aws-sns": "Amazon SNS", - "sms-provider-type-twilio": "Twilio", - "aws-access-key-id": "AWS-adgangsnøgle-id", - "aws-access-key-id-required": "AWS-adgangsnøgle-id er påkrævet", - "aws-secret-access-key": "AWS hemmelig adgangsnøgle", - "aws-secret-access-key-required": "AWS hemmelig adgangsnøgle er påkrævet", - "aws-region": "AWS-region", - "aws-region-required": "AWS-region er påkrævet", - "number-from": "Telefonnummer fra", - "number-from-required": "Telefonnummer fra er påkrævet.", - "number-to": "Telefonnummer til", - "number-to-required": "Telefonnummer til er påkrævet.", - "phone-number-hint": "Telefonnummer i E.164-format, f.eks. +19995550123", - "phone-number-hint-twilio": "Telefonnummer i E.164-format/Telefonnummers SID/Meddelelsesservice SID, f.eks. +19995550123/PNXXX/MGXXX", - "phone-number-pattern": "Ugyldigt telefonnummer. Skal være i E.164-format, f.eks. +19995550123.", - "phone-number-pattern-twilio": "Ugyldigt telefonnummer. Skal være i E.164-format/Telefonnummers SID/Meddelelsesservice SID, f.eks. +19995550123/PNXXX/MGXXX.", - "sms-message": "SMS-besked", - "sms-message-required": "SMS-besked er påkrævet.", - "sms-message-max-length": "SMS-beskeden må ikke være længere end 1600 tegn", - "twilio-account-sid": "Twilio-konto SID", - "twilio-account-sid-required": "Twilio-konto SID er påkrævet", - "twilio-account-token": "Twilio-konto-token", - "twilio-account-token-required": "Twilio-konto-token er påkrævet", - "send-test-sms": "Send test-SMS", - "test-sms-sent": "Test-SMS'en blev sendt!", - "security-settings": "Sikkerhedsindstillinger", - "password-policy": "Adgangskodepolitik", - "minimum-password-length": "Min. adgangskodelængde", - "minimum-password-length-required": "Min. adgangskodelængde er påkrævet", - "minimum-password-length-range": "Min. adgangskodelængde skal være mellem 5 og 50", - "minimum-uppercase-letters": "Min. antal store bogstaver", - "minimum-uppercase-letters-range": "Min. antal store bogstaver kan ikke være negativt", - "minimum-lowercase-letters": "Min. antal små bogstaver", - "minimum-lowercase-letters-range": "Min. antal små bogstaver kan ikke være negativt", - "minimum-digits": "Min. antal cifre", - "minimum-digits-range": "Minimum antal cifre kan ikke være negativt", - "minimum-special-characters": "Minimum antal specialtegn", - "minimum-special-characters-range": "Minimum antal specialtegn kan ikke være negativt", - "password-expiration-period-days": "Adgangskodens udløbsperiode i dage", - "password-expiration-period-days-range": "Adgangskodens udløbsperiode i dage kan ikke være negativ", - "password-reuse-frequency-days": "Hyppighed af genbrug af adgangskode i dage", - "password-reuse-frequency-days-range": "Hyppigheden af genbrug af adgangskode i dage kan ikke være negativ", - "general-policy": "Generelle retningslinjer", - "max-failed-login-attempts": "Maks. antal mislykkede loginforsøg, før kontoen spærres", - "minimum-max-failed-login-attempts-range": "Maks. antal mislykkede loginforsøg kan ikke være negativt", - "user-lockout-notification-email": "Hvis brugerkontoen spærres, sendes en meddelelse til e-mail", - "domain-name": "Domænenavn", - "domain-name-unique": "Domænenavn og protokol skal være entydige.", - "error-verification-url": "Et domænenavn må ikke indeholde symbolerne '/' og ':'. Eksempel: thingsboard.io", - "oauth2": { - "access-token-uri": "Adgangstoken URI", - "access-token-uri-required": "Adgangstoken URI er påkrævet.", - "activate-user": "Aktivér bruger", - "add-domain": "Tilføj domæne", - "delete-domain": "Slet domæne", - "add-provider": "Tilføj udbyder", - "delete-provider": "Slet udbyder", - "allow-user-creation": "Tillad brugeroprettelse", - "always-fullscreen": "Altid fuld skærm", - "authorization-uri": "Godkendelses-URI", - "authorization-uri-required": "Godkendelses-URI er påkrævet.", - "client-authentication-method": "Klientgodkendelsesmetode", - "client-id": "Klient-id", - "client-id-required": "Klient-id er påkrævet.", - "client-secret": "Kunde hemmelig", - "client-secret-required": "Kunde hemmelig er påkrævet.", - "custom-setting": "Brugerdefinerede indstillinger", - "customer-name-pattern": "Kundenavnsmønster", - "parent-customer-name-pattern": "Overordnet kundenavnsmønster", - "user-groups-name-pattern": "Mønster for brugergruppenavn", - "default-dashboard-name": "Standard dashboardnavn", - "delete-domain-text": "Vær forsigtig. Efter bekræftelsen vil et domæne og alle udbyderdata være utilgængelige.", - "delete-domain-title": "", - "delete-registration-text": "Vær forsigtig. Efter bekræftelsen vil udbyderdata være utilgængelige.", - "delete-registration-title": "", - "email-attribute-key": "E-mailattributnøgle", - "email-attribute-key-required": "E-mailattributnøgle er påkrævet.", - "first-name-attribute-key": "Fornavn attributnøgle", - "general": "Generelt", - "jwk-set-uri": "JSON webnøgle-URI", - "last-name-attribute-key": "Efternavn attributnøgle", - "login-button-icon": "Logon-knapikon", - "login-button-label": "Udbyder etiket", - "login-button-label-placeholder": "Log ind med $(Udbyder etiket)", - "login-button-label-required": "Etiket er påkrævet.", - "login-provider": "Log ind udbyder", - "mapper": "Kortlægger", - "new-domain": "Nyt domæne", - "oauth2": "OAuth2", - "redirect-uri-template": "Omdiriger URI-skabelon", - "copy-redirect-uri": "Kopiér omdirigering af URI", - "registration-id": "Registrerings-id", - "registration-id-required": "Registrerings-id er påkrævet.", - "registration-id-unique": "Registrerings-id skal være entydigt for systemet.", - "scope": "Omfang", - "scope-required": "Omfang er påkrævet.", - "tenant-name-pattern": "Mønster for lejernavn", - "tenant-name-pattern-required": "Mønster for lejernavn er påkrævet.", - "tenant-name-strategy": "Strategi for lejernavn", - "type": "Kortlæggertype", - "uri-pattern-error": "Ugyldigt URI-format.", - "url": "URL", - "url-pattern": "Ugyldigt URL-format.", - "url-required": "URL er påkrævet.", - "user-info-uri": "Brugerinfo URI", - "user-info-uri-required": "Brugerinfo URI er påkrævet.", - "user-name-attribute-name": "Attributnøgle for brugernavn", - "user-name-attribute-name-required": "Attributnøgle for brugernavn er påkrævet", - "protocol": "Protokol", - "domain-schema-http": "HTTP", - "domain-schema-https": "HTTPS", - "domain-schema-mixed": "HTTP+HTTPS", - "enable": "Aktivér OAuth2-indstillinger" - } + "access" : { + "unauthorized" : "Uautoriseret", + "unauthorized-access" : "Uautoriseret adgang", + "unauthorized-access-text" : "Du skal logge ind for at få adgang til denne ressource!", + "access-forbidden" : "Adgang forbudt", + "access-forbidden-text" : "Du har ikke adgangsrettigheder til denne placering!
Prøv at logge ind med en anden bruger, hvis du stadig ønsker adgang til denne placering.", + "refresh-token-expired" : "Sessionen er udløbet", + "refresh-token-failed" : "Kan ikke forny sessionen", + "permission-denied" : "Adgang nægtet", + "permission-denied-text" : "Du har ikke tilladelse til at udføre denne handling!" + }, + "account" : { + "account" : "Konto", + "notification-settings" : "Notifikationsindstillinger" + }, + "action" : { + "activate" : "Aktivér", + "suspend" : "Suspender", + "save" : "Gem", + "saveAs" : "Gem som", + "move" : "Flyt", + "cancel" : "Annullér", + "ok" : "OK", + "delete" : "Slet", + "add" : "Tilføj", + "yes" : "Ja", + "no" : "Nej", + "update" : "Opdater", + "remove" : "Fjern", + "search" : "Søg", + "clear-search" : "Ryd søgning", + "assign" : "Tildel", + "unassign" : "Fratildel", + "share" : "Del", + "make-private" : "Gør privat", + "apply" : "Anvend", + "apply-changes" : "Anvend ændringer", + "edit-mode" : "Redigeringstilstand", + "enter-edit-mode" : "Gå til redigeringstilstand", + "decline-changes" : "Afvis ændringer", + "decline" : "Afvis", + "close" : "Luk", + "back" : "Tilbage", + "run" : "Kør", + "sign-in" : "Log ind!", + "edit" : "Redigér", + "view" : "Vis", + "create" : "Opret", + "drag" : "Træk", + "refresh" : "Opdatér", + "undo" : "Fortryd", + "copy" : "Kopiér", + "paste" : "Indsæt", + "copy-reference" : "Kopiér reference", + "paste-reference" : "Indsæt reference", + "import" : "Importér", + "export" : "Eksportér", + "share-via" : "Del via {{provider}}", + "select" : "Vælg", + "continue" : "Fortsæt", + "discard-changes" : "Kassér ændringer", + "download" : "Download", + "next" : "Næste", + "next-with-label" : "Næste: {{label}}", + "read-more" : "Læs mere", + "hide" : "Skjul", + "test" : "Test", + "done" : "Færdig", + "print" : "Udskriv", + "restore" : "Gendan", + "confirm" : "Bekræft", + "more" : "Mere", + "less" : "Mindre", + "skip" : "Spring over", + "send" : "Send", + "reset" : "Nulstil", + "show-more" : "Vis mere", + "dont-show-again" : "Vis ikke igen", + "see-documentation" : "Se dokumentation", + "clear" : "Ryd", + "upload" : "Upload", + "delete-anyway" : "Slet alligevel", + "delete-selected" : "Slet valgte", + "set" : "Angiv" + }, + "aggregation" : { + "aggregation" : "Aggregering", + "function" : "Dataaggregeringsfunktion", + "limit" : "Maks. værdier", + "group-interval" : "Grupperingsinterval", + "min" : "Min", + "max" : "Maks", + "avg" : "Gennemsnit", + "sum" : "Sum", + "count" : "Antal", + "none" : "Ingen" + }, + "admin" : { + "settings" : "Indstillinger", + "general" : "Generelt", + "general-settings" : "Generelle indstillinger", + "home-settings" : "Hjem-indstillinger", + "home" : "Hjem", + "outgoing-mail" : "Mailserver", + "outgoing-mail-settings" : "Indstillinger for udgående mailserver", + "system-settings" : "Systemindstillinger", + "test-mail-sent" : "Testmail blev sendt med succes!", + "base-url" : "Basis-URL", + "base-url-required" : "Basis-URL er påkrævet.", + "prohibit-different-url" : "Forbyd brug af værtsnavn fra klientanmodningens headers", + "prohibit-different-url-hint" : "Denne indstilling bør være aktiveret i produktionsmiljøer. Kan medføre sikkerhedsproblemer, hvis deaktiveret", + "device-connectivity" : { + "device-connectivity" : "Enhedstilslutning", + "http-s" : "HTTP(s)", + "mqtt-s" : "MQTT(s)", + "coap-s" : "COAP(s)", + "http" : "HTTP", + "https" : "HTTPs", + "mqtt" : "MQTT", + "mqtts" : "MQTTs", + "coap" : "COAP", + "coaps" : "COAPs", + "hint" : "Hvis felterne for vært eller port er tomme, anvendes standardprotokolværdien.", + "host" : "Vært", + "port" : "Port", + "port-pattern" : "Porten skal være et positivt heltal.", + "port-range" : "Porten skal være i intervallet fra 1 til 65535." + }, + "mail-from" : "Mail fra", + "mail-from-required" : "Mail fra er påkrævet.", + "smtp-protocol" : "SMTP-protokol", + "smtp-host" : "SMTP-vært", + "smtp-host-required" : "SMTP-vært er påkrævet.", + "smtp-port" : "SMTP-port", + "smtp-port-required" : "Du skal angive en SMTP-port.", + "smtp-port-invalid" : "Det ligner ikke en gyldig SMTP-port.", + "timeout-msec" : "Timeout (ms)", + "timeout-required" : "Timeout er påkrævet.", + "timeout-invalid" : "Det ligner ikke en gyldig timeout.", + "enable-tls" : "Aktivér TLS", + "tls-version" : "TLS-version", + "enable-proxy" : "Aktivér proxy", + "proxy-host" : "Proxy-vært", + "proxy-host-required" : "Proxy-vært er påkrævet.", + "proxy-port" : "Proxy-port", + "proxy-port-required" : "Proxy-port er påkrævet.", + "proxy-port-range" : "Proxy-porten skal være i intervallet fra 1 til 65535.", + "proxy-user" : "Proxy-bruger", + "proxy-password" : "Proxy-adgangskode", + "change-password" : "Skift adgangskode", + "send-test-mail" : "Send testmail", + "sms-provider" : "SMS-udbyder", + "sms-provider-settings" : "Indstillinger for SMS-udbyder", + "sms-provider-type" : "SMS-udbydertype", + "sms-provider-type-required" : "SMS-udbydertype er påkrævet.", + "sms-provider-type-aws-sns" : "Amazon SNS", + "sms-provider-type-twilio" : "Twilio", + "sms-provider-type-smpp" : "SMPP", + "aws-access-key-id" : "AWS Access Key ID", + "aws-access-key-id-required" : "AWS Access Key ID er påkrævet", + "aws-secret-access-key" : "AWS Secret Access Key", + "aws-secret-access-key-required" : "AWS Secret Access Key er påkrævet", + "aws-region" : "AWS-region", + "aws-region-required" : "AWS-region er påkrævet", + "number-from" : "Telefonnummer fra", + "number-from-required" : "Telefonnummer fra er påkrævet.", + "number-to" : "Telefonnummer til", + "number-to-required" : "Telefonnummer til er påkrævet.", + "phone-number-hint" : "Telefonnummer i E.164-format, f.eks. +19995550123", + "phone-number-hint-twilio" : "Telefonnummer i E.164-format/Telefonnummerets SID/Messaging Service SID, f.eks. +19995550123/PNXXX/MGXXX", + "phone-number-pattern" : "Ugyldigt telefonnummer. Skal være i E.164-format, f.eks. +19995550123.", + "phone-number-pattern-twilio" : "Ugyldigt telefonnummer. Skal være i E.164-format/Telefonnummerets SID/Messaging Service SID, f.eks. +19995550123/PNXXX/MGXXX.", + "sms-message" : "SMS-besked", + "sms-message-required" : "SMS-besked er påkrævet.", + "sms-message-max-length" : "SMS-beskeden må ikke være længere end 1600 tegn", + "twilio-account-sid" : "Twilio-konto SID", + "twilio-account-sid-required" : "Twilio-konto SID er påkrævet", + "twilio-account-token" : "Twilio-konto Token", + "twilio-account-token-required" : "Twilio-konto Token er påkrævet", + "send-test-sms" : "Send test-SMS", + "test-sms-sent" : "Test-SMS blev sendt med succes!", + "security-settings" : "Sikkerhedsindstillinger", + "password-policy" : "Adgangskodepolitik", + "minimum-password-length" : "Minimum adgangskodelængde", + "minimum-password-length-required" : "Minimum adgangskodelængde er påkrævet", + "minimum-password-length-range" : "Minimum adgangskodelængde skal være mellem 6 og 50", + "maximum-password-length" : "Maksimal adgangskodelængde", + "maximum-password-length-min" : "Maksimal adgangskodelængde skal være mindst 6", + "maximum-password-length-less-min" : "Maksimal adgangskodelængde skal være større end minimumslængde", + "minimum-uppercase-letters" : "Minimum antal store bogstaver", + "minimum-uppercase-letters-range" : "Minimum antal store bogstaver må ikke være negativt", + "minimum-lowercase-letters" : "Minimum antal små bogstaver", + "minimum-lowercase-letters-range" : "Minimum antal små bogstaver må ikke være negativt", + "minimum-digits" : "Minimum antal cifre", + "minimum-digits-range" : "Minimum antal cifre må ikke være negativt", + "minimum-special-characters" : "Minimum antal specialtegn", + "minimum-special-characters-range" : "Minimum antal specialtegn må ikke være negativt", + "password-expiration-period-days" : "Adgangskodeudløbsperiode i dage", + "password-expiration-period-days-range" : "Adgangskodeudløbsperiode i dage må ikke være negativ", + "password-reuse-frequency-days" : "Adgangskodegenbrug hyppighed i dage", + "password-reuse-frequency-days-range" : "Adgangskodegenbrug hyppighed i dage må ikke være negativ", + "allow-whitespace" : "Tillad mellemrum", + "force-reset-password-if-no-valid" : "Tving nulstilling af adgangskode, hvis ikke gyldig", + "force-reset-password-if-no-valid-hint" : "Vær forsigtig, når denne funktion aktiveres: brugere med ugyldige adgangskoder skal nulstille deres adgangskode via e-mail.", + "general-policy" : "Generel politik", + "max-failed-login-attempts" : "Maksimalt antal mislykkede loginforsøg, før kontoen låses", + "minimum-max-failed-login-attempts-range" : "Maksimalt antal mislykkede loginforsøg må ikke være negativt", + "user-lockout-notification-email" : "Ved kontolåsning, send notifikation til e-mail", + "user-activation-token-ttl" : "Brugeraktiveringslink TTL i timer", + "user-activation-token-ttl-range" : "Brugeraktiveringslink TTL skal være mellem 1 og 24 timer", + "password-reset-token-ttl" : "Link til nulstilling af adgangskode TTL i timer", + "password-reset-token-ttl-range" : "Link til nulstilling af adgangskode TTL skal være mellem 1 og 24 timer", + "mobile-secret-key-length" : "Længde på mobil hemmelig nøgle", + "mobile-secret-key-length-range" : "Længden på mobil hemmelig nøgle skal være positiv", + "domain-name" : "Domænenavn", + "domain-name-unique" : "Domænenavn og protokol skal være unikke.", + "domain-name-max-length" : "Domænenavnet skal være mindre end 256", + "error-verification-url" : "Et domænenavn må ikke indeholde symbolerne '/' og ':'. Eksempel: thingsboard.io", + "connection-settings" : "Forbindelsesindstillinger", + "oauth2" : { + "access-token-uri" : "Adgangstoken-URI", + "access-token-uri-required" : "Adgangstoken-URI er påkrævet.", + "activate-user" : "Aktivér bruger", + "add-domain" : "Tilføj domæne", + "delete-domain" : "Slet domæne", + "add-provider" : "Tilføj udbyder", + "delete-provider" : "Slet udbyder", + "allow-user-creation" : "Tillad oprettelse af bruger", + "always-fullscreen" : "Altid fuldskærm", + "authorization-uri" : "Autoriserings-URI", + "authorization-uri-required" : "Autoriserings-URI er påkrævet.", + "add-client" : "Tilføj OAuth 2.0-klient", + "client-details" : "OAuth 2.0-klientdetaljer", + "client" : "OAuth 2.0-klient", + "clients" : "OAuth 2.0-klienter", + "no-oauth2-clients" : "Ingen OAuth 2.0-klienter fundet", + "search-oauth2-clients" : "Søg OAuth 2.0-klienter", + "delete-client-title" : "Er du sikker på, at du vil slette OAuth 2.0-klienten '{{clientName}}'?", + "delete-client-text" : "Vær forsigtig, efter bekræftelse vil klienten og alle relaterede data være uoprettelige.", + "delete-mobile-app-title" : "Er du sikker på, at du vil slette mobilapplikationen '{{applicationName}}'?", + "delete-mobile-app-text" : "Vær forsigtig, efter bekræftelse vil mobilapplikationen og alle relaterede data være uoprettelige.", + "title" : "Titel", + "client-title-required" : "Titel er påkrævet", + "client-title-max-length" : "Titel skal være mindre end 100 tegn", + "advanced-settings" : "Avancerede indstillinger", + "domain-details" : "Domænedetaljer", + "no-domains" : "Ingen domæner fundet", + "search-domains" : "Søg domæner", + "mobile-app-details" : "Detaljer for mobilapplikation", + "add-mobile-app" : "Tilføj mobilapplikation", + "no-mobile-apps" : "Ingen mobilapplikationer fundet", + "search-mobile-apps" : "Søg mobilapplikationer", + "send-token" : "Send token", + "create-new" : "Opret ny", + "client-authentication-method" : "Klientautentificeringsmetode", + "client-id" : "Klient-ID", + "client-id-required" : "Klient-ID er påkrævet.", + "client-id-max-length" : "Klient-ID skal være mindre end 256 tegn", + "client-secret" : "Klienthemmelighed", + "client-secret-required" : "Klienthemmelighed er påkrævet.", + "client-secret-max-length" : "Klienthemmelighed skal være mindre end 2049 tegn", + "custom-setting" : "Brugerdefinerede indstillinger", + "customer-name-pattern" : "Mønsternavn for kunde", + "customer-name-pattern-max-length" : "Mønsternavn for kunde skal være mindre end 256 tegn", + "default-dashboard-name" : "Standard dashboard-navn", + "default-dashboard-name-max-length" : "Standard dashboard-navn skal være mindre end 256 tegn", + "delete-domain-text" : "Vær forsigtig, efter bekræftelse vil domænet og alle udbyderdata ikke længere være tilgængelige.", + "delete-domain-title" : "Er du sikker på, at du vil slette domænet '{{domainName}}'?", + "delete-registration-text" : "Vær forsigtig, efter bekræftelse vil udbyderdata ikke længere være tilgængelige.", + "delete-registration-title" : "Er du sikker på, at du vil slette udbyderen '{{name}}'?", + "email-attribute-key" : "E-mail-attributnøgle", + "email-attribute-key-required" : "E-mail-attributnøgle er påkrævet.", + "email-attribute-key-max-length" : "E-mail-attributnøgle skal være mindre end 32 tegn", + "first-name-attribute-key" : "Fornavn-attributnøgle", + "first-name-attribute-key-max-length" : "Fornavn-attributnøgle skal være mindre end 32 tegn", + "general" : "Generelt", + "jwk-set-uri" : "JSON Web Key URI", + "last-name-attribute-key" : "Efternavn-attributnøgle", + "last-name-attribute-key-max-length" : "Efternavn-attributnøgle skal være mindre end 32 tegn", + "login-button-icon" : "Login-knap ikon", + "login-button-label" : "Udbydernavn", + "login-button-label-placeholder" : "Log ind med $(Provider label)", + "login-button-label-required" : "Etiket er påkrævet.", + "login-provider" : "Loginudbyder", + "mapper" : "Mapper", + "new-domain" : "Nyt domæne", + "oauth2" : "OAuth 2.0", + "password-max-length" : "Adgangskode skal være mindre end 256 tegn", + "redirect-uri-template" : "Skabelon for omdirigerings-URI", + "copy-redirect-uri" : "Kopiér omdirigerings-URI", + "registration-id" : "Registrerings-ID", + "registration-id-required" : "Registrerings-ID er påkrævet.", + "registration-id-unique" : "Registrerings-ID skal være unikt i systemet.", + "scope" : "Omfang", + "scope-required" : "Omfang er påkrævet.", + "tenant-name-pattern" : "Mønsternavn for lejer", + "tenant-name-pattern-required" : "Mønsternavn for lejer er påkrævet.", + "tenant-name-pattern-max-length" : "Mønsternavn for lejer skal være mindre end 256 tegn", + "tenant-name-strategy" : "Strategi for lejerens navn", + "type" : "Mapper-type", + "uri-pattern-error" : "Ugyldigt URI-format.", + "url" : "URL", + "url-pattern" : "Ugyldigt URL-format.", + "url-required" : "URL er påkrævet.", + "url-max-length" : "URL skal være mindre end 256 tegn", + "user-info-uri" : "Brugerinfo URI", + "user-info-uri-required" : "Brugerinfo URI er påkrævet.", + "username-max-length" : "Brugernavn skal være mindre end 256 tegn", + "user-name-attribute-name" : "Brugernavn-attributnøgle", + "user-name-attribute-name-required" : "Brugernavn-attributnøgle er påkrævet", + "protocol" : "Protokol", + "domain-schema-http" : "HTTP", + "domain-schema-https" : "HTTPS", + "domain-schema-mixed" : "HTTP+HTTPS", + "enable" : "Aktivér OAuth 2.0-indstillinger", + "disable" : "Deaktivér OAuth 2.0-indstillinger", + "edge" : "Distribuér til Edge", + "edge-enable" : "Aktivér distribution til Edge", + "edge-disable" : "Deaktivér distribution til Edge", + "domains" : "Domæner", + "mobile-apps" : "Mobilapplikationer", + "mobile-package" : "Applikationspakke", + "mobile-package-placeholder" : "F.eks.: my.example.app", + "mobile-package-hint" : "For Android: dit eget unikke Application ID. For iOS: Produktets bundle-identifikator.", + "mobile-package-unique" : "Applikationspakken skal være unik.", + "mobile-package-required" : "Applikationspakken er påkrævet.", + "mobile-package-max-length" : "Applikationspakken skal være mindre end 256 tegn", + "mobile-package-spaces" : "Applikationspakken må ikke indeholde mellemrum", + "mobile-app-secret" : "Applikationshemmelighed", + "mobile-app-secret-hint" : "Base64-kodet streng, der repræsenterer mindst 512 bit data.", + "mobile-app-secret-required" : "Applikationshemmelighed er påkrævet.", + "mobile-app-secret-min-length" : "Applikationshemmelighed skal være mindst 512 bit data.", + "mobile-app-secret-base64" : "Applikationshemmelighed skal være i base64-format.", + "invalid-mobile-app-secret" : "Applikationshemmelighed må kun indeholde alfanumeriske tegn og være mellem 16 og 2048 tegn lang.", + "copy-mobile-app-secret" : "Kopiér applikationshemmelighed", + "delete-mobile-app" : "Slet applikationsinformation", + "providers" : "Udbydere", + "platform-web" : "Web", + "platform-android" : "Android", + "platform-ios" : "iOS", + "all-platforms" : "Alle platforme", + "smtp-provider" : "SMTP-udbyder", + "allowed-platforms" : "Tilladte platforme", + "authentication" : "Autentificering", + "basic" : "Basis", + "provider" : "Udbyder", + "redirect-url" : "Omdirigerings-URI", + "domain-name" : "Domænenavn", + "domain-name-required" : "Domænenavn er påkrævet", + "redirect-url-template" : "Skabelon for omdirigerings-URI", + "microsoft-tenant-id" : "Katalog (lejer) ID", + "microsoft-tenant-id-required" : "Katalog (lejer) ID er påkrævet", + "token-uri" : "Token-URI", + "token-uri-required" : "Token-URI er påkrævet", + "redirect-uri" : "Omdirigerings-URI", + "google-provider" : "Google", + "microsoft-provider" : "Office 365", + "sendgrid-provider" : "Sendgrid", + "custom-provider" : "Brugerdefineret", + "generate-access-token" : "Generér adgangstoken", + "update-access-token" : "Opdatér adgangstoken", + "access-token-status" : "Status for adgangstoken:", + "token-status-generated" : "genereret", + "token-status-not-generated" : "ikke genereret" + }, + "smpp-provider" : { + "smpp-version" : "SMPP-version", + "smpp-host" : "SMPP-vært", + "smpp-host-required" : "SMPP-vært er påkrævet", + "smpp-port" : "SMPP-port", + "smpp-port-required" : "SMPP-port er påkrævet", + "system-id" : "System-ID", + "system-id-required" : "System-ID er påkrævet", + "password" : "Adgangskode", + "password-required" : "Adgangskode er påkrævet", + "type-settings" : "Typeindstillinger", + "source-settings" : "Kildeindstillinger", + "destination-settings" : "Destinationsindstillinger", + "additional-settings" : "Yderligere indstillinger", + "system-type" : "Systemtype", + "bind-type" : "Bind-type", + "service-type" : "Tjenestetype", + "source-address" : "Kildeadresse", + "source-ton" : "Kilde TON", + "source-npi" : "Kilde NPI", + "destination-ton" : "Destination TON (Nummerets type)", + "destination-npi" : "Destination NPI (Nummereringsplanidentifikation)", + "address-range" : "Adresseområde", + "coding-scheme" : "Kodningsskema", + "bind-type-tx" : "Afsender", + "bind-type-rx" : "Modtager", + "bind-type-trx" : "Transceiver", + "ton-unknown" : "Ukendt", + "ton-international" : "International", + "ton-national" : "National", + "ton-network-specific" : "Netværksspecifik", + "ton-subscriber-number" : "Abonnentnummer", + "ton-alphanumeric" : "Alfanumerisk", + "ton-abbreviated" : "Forkortet", + "npi-unknown" : "0 - Ukendt", + "npi-isdn" : "1 - ISDN/telefonnummereringsplan (E163/E164)", + "npi-data-numbering-plan" : "3 - Data-nummereringsplan (X.121)", + "npi-telex-numbering-plan" : "4 - Telex-nummereringsplan (F.69)", + "npi-land-mobile" : "6 - Landmobil (E.212)", + "npi-national-numbering-plan" : "8 - National nummereringsplan", + "npi-private-numbering-plan" : "9 - Privat nummereringsplan", + "npi-ermes-numbering-plan" : "10 - ERMES-nummereringsplan (ETSI DE/PS 3 01-3)", + "npi-internet" : "13 - Internet (IP)", + "npi-wap-client-id" : "18 - WAP-klient-ID (defineres af WAP Forum)", + "scheme-smsc" : "0 - SMSC Standard Alfabet (ASCII for korte og lange koder og GSM for gratisnumre)", + "scheme-ia5" : "1 - IA5 (ASCII for korte og lange koder, Latin 9 for gratisnumre (ISO-8859-9))", + "scheme-octet-unspecified-2" : "2 - Oktet uspecificeret (8-bit binær)", + "scheme-latin-1" : "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4" : "4 - Oktet uspecificeret (8-bit binær)", + "scheme-jis" : "5 - JIS (X 0208-1990)", + "scheme-cyrillic" : "6 - Kyrillisk (ISO-8859-5)", + "scheme-latin-hebrew" : "7 - Latin/Hebræisk (ISO-8859-8)", + "scheme-ucs-utf" : "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding" : "9 - Piktogramkodning", + "scheme-music-codes" : "10 - Musik-koder (ISO-2022-JP)", + "scheme-extended-kanji-jis" : "13 - Udvidet Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set" : "14 - Koreansk grafisk tegnsæt (KS C 5601/KS X 1001)" + }, + "queue-select-name" : "Vælg kønavn", + "queue-name" : "Navn", + "queue-name-required" : "Kønavn er påkrævet!", + "queues" : "Køer", + "queue-partitions" : "Partitioner", + "queue-submit-strategy" : "Indsendelsesstrategi", + "queue-processing-strategy" : "Behandlingsstrategi", + "queue-configuration" : "Køkonfiguration", + "repository-settings" : "Versionskontrolindstillinger", + "repository" : "Versionskontrol", + "repository-url" : "Versionskontrol-URL", + "repository-url-required" : "Versionskontrol-URL er påkrævet.", + "default-branch" : "Standard branch-navn", + "repository-read-only" : "Skrivebeskyttet", + "show-merge-commits" : "Vis flette-commits", + "authentication-settings" : "Autentificeringsindstillinger", + "auth-method" : "Autentificeringsmetode", + "auth-method-username-password" : "Adgangskode / adgangstoken", + "auth-method-username-password-hint" : "GitHub-brugere skal bruge tokens med skriverettigheder til depotet.", + "auth-method-private-key" : "Privat nøgle", + "password-access-token" : "Adgangskode / adgangstoken", + "change-password-access-token" : "Skift adgangskode / adgangstoken", + "private-key" : "Privat nøgle", + "drop-private-key-file-or" : "Træk og slip en fil med privat nøgle eller", + "passphrase" : "Adgangsfrase", + "enter-passphrase" : "Indtast adgangsfrase", + "change-passphrase" : "Skift adgangsfrase", + "check-access" : "Tjek adgang", + "check-repository-access-success" : "Adgang til versionskontrol blev bekræftet!", + "delete-repository-settings-title" : "Er du sikker på, at du vil slette versionskontrolindstillingerne?", + "delete-repository-settings-text" : "Vær forsigtig, efter bekræftelse fjernes indstillingerne og versionskontrolfunktionen bliver utilgængelig.", + "auto-commit-settings" : "Indstillinger for automatisk commit", + "auto-commit" : "Automatisk commit", + "auto-commit-entities" : "Automatisk commit-enheder", + "no-auto-commit-entities-prompt" : "Ingen enheder konfigureret til automatisk commit", + "delete-auto-commit-settings-title" : "Er du sikker på, at du vil slette indstillingerne for automatisk commit?", + "delete-auto-commit-settings-text" : "Vær forsigtig, efter bekræftelse fjernes indstillingerne og automatisk commit deaktiveres for alle enheder.", + "mobile-app" : { + "mobile-app" : "Mobilapp", + "mobile-app-qr-code-widget-settings" : "Indstillinger for mobilapp QR-kode-widget", + "applications" : "Applikationer", + "default" : "Standard", + "custom" : "Brugerdefineret", + "android" : "Android", + "ios" : "iOS", + "appearance" : "Udseende", + "appearance-on-home-page" : "Udseende på startsiden", + "enabled" : "Aktiveret", + "disabled" : "Deaktiveret", + "badges" : "Mærker", + "label" : "Etiket", + "label-required" : "Etiket er påkrævet", + "label-max-length" : "Etiketten må ikke overstige 50 tegn", + "right" : "Højre", + "left" : "Venstre", + "set" : "Angiv", + "preview" : "Forhåndsvisning", + "connect-mobile-app" : "Tilslut mobilapp", + "use-system-settings" : "Brug systemindstillinger" + }, + "2fa" : { + "2fa" : "To-faktor autentificering", + "available-providers" : "Tilgængelige udbydere", + "issuer-name" : "Udsteders navn", + "issuer-name-required" : "Udsteders navn er påkrævet.", + "max-verification-failures-before-user-lockout" : "Maks. antal verificeringsfejl før kontolåsning", + "max-verification-failures-before-user-lockout-pattern" : "Maks. antal verificeringsfejl skal være et positivt heltal.", + "number-of-checking-attempts" : "Antal verificeringsforsøg", + "number-of-checking-attempts-pattern" : "Antal verificeringsforsøg skal være et positivt heltal.", + "number-of-checking-attempts-required" : "Antal verificeringsforsøg er påkrævet.", + "number-of-codes" : "Antal koder", + "number-of-codes-pattern" : "Antal koder skal være et positivt heltal.", + "number-of-codes-required" : "Antal koder er påkrævet.", + "provider" : "Udbyder", + "retry-verification-code-period" : "Ventetid før ny verificering (sek)", + "retry-verification-code-period-pattern" : "Mindste ventetid er 5 sek.", + "retry-verification-code-period-required" : "Ventetid før ny verificering er påkrævet.", + "total-allowed-time-for-verification" : "Total tilladt tid til verificering (sek)", + "total-allowed-time-for-verification-pattern" : "Mindste tilladte tid er 60 sek.", + "total-allowed-time-for-verification-required" : "Total tilladt tid er påkrævet.", + "use-system-two-factor-auth-settings" : "Brug systemets to-faktor autentificeringsindstillinger", + "verification-code-check-rate-limit" : "Begrænsning af verificeringsforsøg", + "verification-code-lifetime" : "Verificeringskode levetid (sek)", + "verification-code-lifetime-pattern" : "Verificeringskode levetid skal være et positivt heltal.", + "verification-code-lifetime-required" : "Verificeringskode levetid er påkrævet.", + "verification-message-template" : "Skabelon for verificeringsbesked", + "verification-limitations" : "Begrænsninger for verificering", + "verification-message-template-pattern" : "Verificeringsbeskeden skal indeholde mønsteret: ${code}", + "verification-message-template-required" : "Skabelon for verificeringsbesked er påkrævet.", + "within-time" : "Inden for tid (sek)", + "within-time-pattern" : "Tiden skal være et positivt heltal.", + "within-time-required" : "Tid er påkrævet." + }, + "jwt" : { + "security-settings" : "JWT sikkerhedsindstillinger", + "issuer-name" : "Udsteders navn", + "issuer-name-required" : "Udsteders navn er påkrævet.", + "signings-key" : "Signeringsnøgle", + "signings-key-hint" : "Base64-kodet streng, der repræsenterer mindst 512 bit data.", + "signings-key-required" : "Signeringsnøgle er påkrævet.", + "signings-key-min-length" : "Signeringsnøgle skal være mindst 512 bit data.", + "signings-key-base64" : "Signeringsnøgle skal være i base64-format.", + "expiration-time" : "Udløbstid for token (sek)", + "expiration-time-required" : "Udløbstid for token er påkrævet.", + "expiration-time-max" : "Maksimalt tilladt tid er 2147483647 sekunder (68 år).", + "expiration-time-min" : "Minimumstid er 60 sekunder (1 minut).", + "refresh-expiration-time" : "Udløbstid for opfriskningstoken (sek)", + "refresh-expiration-time-required" : "Udløbstid for opfriskningstoken er påkrævet.", + "refresh-expiration-time-max" : "Maksimalt tilladt tid er 2147483647 sekunder (68 år).", + "refresh-expiration-time-min" : "Minimumstid er 900 sekunder (15 minutter).", + "refresh-expiration-time-less-token" : "Opfriskningstoken-tid skal være længere end token-tid.", + "generate-key" : "Generér nøgle", + "info-header" : "Alle brugere skal logge ind igen", + "info-message" : "Ændring af JWT-signeringsnøgle vil ugyldiggøre alle udstedte tokens. Alle brugere skal logge ind igen. Dette påvirker også scripts, der bruger Rest API/Websockets." + }, + "resources" : "Ressourcer", + "notifications" : "Notifikationer", + "notifications-settings" : "Notifikationsindstillinger", + "slack-api-token" : "Slack API-token", + "slack" : "Slack", + "slack-settings" : "Slack-indstillinger", + "mobile-settings" : "Mobilindstillinger", + "firebase-service-account-file" : "Firebase servicekonto legitimationsoplysninger (JSON-fil)", + "select-firebase-service-account-file" : "Træk og slip din Firebase servicekonto-fil eller " + }, + "alarm" : { + "alarm" : "Alarm", + "alarms" : "Alarmer", + "all-alarms" : "Alle alarmer", + "select-alarm" : "Vælg alarm", + "no-alarms-matching" : "Ingen alarmer matcher '{{entity}}'.", + "alarm-required" : "Alarm er påkrævet", + "alarm-filter" : "Alarmfilter", + "filter" : "Filter", + "alarm-status" : "Alarmstatus", + "alarm-status-list" : "Liste over alarmstatus", + "any-status" : "Enhver status", + "search-status" : { + "ANY" : "Enhver", + "ACTIVE" : "Aktiv", + "CLEARED" : "Ryddet", + "ACK" : "Anerkendt", + "UNACK" : "Ikke anerkendt" + }, + "display-status" : { + "ACTIVE_UNACK" : "Aktiv - ikke anerkendt", + "ACTIVE_ACK" : "Aktiv - anerkendt", + "CLEARED_UNACK" : "Ryddet - ikke anerkendt", + "CLEARED_ACK" : "Ryddet - anerkendt" + }, + "no-alarms-prompt" : "Ingen alarmer fundet", + "created-time" : "Oprettelsestidspunkt", + "type" : "Type", + "severity" : "Alvorlighed", + "originator" : "Ophavsmand", + "originator-type" : "Ophavstype", + "details" : "Detaljer", + "originator-label" : "Etiket for ophavsmand", + "assign" : "Tildel", + "assignments" : "Tildelinger", + "assignee" : "Tildelt person", + "assignee-id" : "Tildelt ID", + "assignee-first-name" : "Tildelt fornavn", + "assignee-last-name" : "Tildelt efternavn", + "assignee-email" : "Tildelt e-mail", + "unassigned" : "Ikke tildelt", + "user-deleted" : "Bruger slettet", + "assignee-not-set" : "Alle", + "status" : "Status", + "alarm-details" : "Alarmdetaljer", + "start-time" : "Starttidspunkt", + "assign-time" : "Tildelingstidspunkt", + "end-time" : "Sluttidspunkt", + "ack-time" : "Tidspunkt for anerkendelse", + "clear-time" : "Tidspunkt for rydning", + "duration" : "Varighed", + "alarm-severity" : "Alarmens alvorlighed", + "alarm-severity-list" : "Liste over alvorlighedsniveauer", + "any-severity" : "Enhver alvorlighed", + "severity-critical" : "Kritisk", + "severity-major" : "Alvorlig", + "severity-minor" : "Mindre", + "severity-warning" : "Advarsel", + "severity-indeterminate" : "Ubetinget", + "acknowledge" : "Anerkend", + "clear" : "Ryd", + "delete" : "Slet", + "search" : "Søg alarmer", + "selected-alarms" : "{ count, plural, =1 {1 alarm} other {# alarmer} } valgt", + "no-data" : "Ingen data at vise", + "polling-interval" : "Interval for alarmopdatering (sek)", + "polling-interval-required" : "Interval for alarmopdatering er påkrævet.", + "min-polling-interval-message" : "Mindste tilladte interval er 1 sek.", + "aknowledge-alarms-title" : "Anerkend { count, plural, =1 {1 alarm} other {# alarmer} }", + "aknowledge-alarms-text" : "Er du sikker på, at du vil anerkende { count, plural, =1 {1 alarm} other {# alarmer} }?", + "aknowledge-alarm-title" : "Anerkend alarm", + "aknowledge-alarm-text" : "Er du sikker på, at du vil anerkende alarmen?", + "selected-alarms-are-acknowledged" : "Valgte alarmer er allerede anerkendt", + "clear-alarms-title" : "Ryd { count, plural, =1 {1 alarm} other {# alarmer} }", + "clear-alarms-text" : "Er du sikker på, at du vil rydde { count, plural, =1 {1 alarm} other {# alarmer} }?", + "clear-alarm-title" : "Ryd alarm", + "clear-alarm-text" : "Er du sikker på, at du vil rydde alarmen?", + "delete-alarms-title" : "Slet { count, plural, =1 {1 alarm} other {# alarmer} }", + "delete-alarms-text" : "Er du sikker på, at du vil slette { count, plural, =1 {1 alarm} other {# alarmer} }?", + "selected-alarms-are-cleared" : "Valgte alarmer er allerede ryddet", + "alarm-status-filter" : "Filter for alarmstatus", + "alarm-filter-title" : "Alarmfilter", + "assigned" : "Tildelt", + "filter-title" : "Filter", + "max-count-load" : "Maksimalt antal alarmer der skal indlæses (0 - ubegrænset)", + "max-count-load-required" : "Maksimalt antal alarmer der skal indlæses er påkrævet.", + "max-count-load-error-min" : "Minimumsværdien er 0.", + "fetch-size" : "Hentningsstørrelse", + "fetch-size-required" : "Hentningsstørrelse er påkrævet.", + "fetch-size-error-min" : "Minimumsværdien er 10.", + "alarm-types" : "Alarmtyper", + "alarm-type-list" : "Liste over alarmtyper", + "any-type" : "Enhver type", + "assigned-to-current-user" : "Tildelt nuværende bruger", + "assigned-to-me" : "Tildelt til mig", + "search-propagated-alarms" : "Søg distribuerede alarmer", + "comments" : "Alarmkommentarer", + "show-more" : "Vis mere", + "additional-info" : "Yderligere information", + "alarm-type" : "Alarmtype", + "enter-alarm-type" : "Indtast alarmtype", + "no-alarm-types-matching" : "Ingen alarmtyper matcher '{{entitySubtype}}'.", + "alarm-type-list-empty" : "Ingen alarmtyper valgt." + }, + "alarm-activity" : { + "add" : "Tilføj en kommentar...", + "alarm-comment" : "Alarmkommentar", + "comments" : "Kommentarer", + "delete-alarm-comment" : "Vil du slette denne kommentar?", + "refresh" : "Opdatér", + "oldest-first" : "Ældste først", + "newest-first" : "Nyeste først", + "activity" : "Aktivitet", + "export" : "Eksportér til CSV", + "author" : "Forfatter", + "created-date" : "Oprettelsesdato", + "edited-date" : "Redigeringsdato", + "text" : "Tekst", + "system" : "System" }, - "alarm": { - "alarm": "Alarm", - "alarms": "Alarmer", - "select-alarm": "Vælg alarm", - "no-alarms-matching": "", - "alarm-required": "Alarm er påkrævet", - "alarm-status": "Alarmstatus", - "alarm-status-list": "Alarmstatusliste", - "any-status": "Enhver status", - "search-status": { - "ANY": "Enhver", - "ACTIVE": "Aktiv", - "CLEARED": "Ryddet", - "ACK": "Kvitteret", - "UNACK": "Ikke kvitteret" - }, - "display-status": { - "ACTIVE_UNACK": "Aktiv Ikke-kvitteret", - "ACTIVE_ACK": "Aktiv Kvitteret", - "CLEARED_UNACK": "Ryddet Ikke-kvitteret", - "CLEARED_ACK": "Ryddet Kvitteret" - }, - "no-alarms-prompt": "Ingen alarmer fundet", - "created-time": "Oprettelsestidspunkt", - "type": "Type", - "severity": "Alvorsgrad", - "originator": "Ophavsmand", - "originator-type": "Ophavsmandtype", - "details": "Oplysninger", - "status": "Status", - "alarm-details": "Alarmoplysninger", - "start-time": "Starttid", - "end-time": "Sluttid", - "ack-time": "Tidspunkt for Kvitteret", - "clear-time": "Tidspunkt for Ryddet", - "alarm-severity-list": "Liste over alvorsgrad for alamer", - "any-severity": "Enhver alvorsgrad", - "severity-critical": "Kritisk", - "severity-major": "Stor", - "severity-minor": "Mindre", - "severity-warning": "Advarsel", - "severity-indeterminate": "Ubestemt", - "acknowledge": "Kvittér", - "clear": "Klar", - "search": "Søg efter alarmer", - "selected-alarms": "", - "no-data": "Ingen data at vise", - "polling-interval": "Alarmer undersøgelsesinterval (sek.)", - "polling-interval-required": "Alarmer undersøgelsesinterval er påkrævet.", - "min-polling-interval-message": "Mindst 1 sek. undersøgelsesinterval er tilladt.", - "aknowledge-alarms-title": "", - "aknowledge-alarms-text": "", - "aknowledge-alarm-title": "Kvittér for alarm", - "aknowledge-alarm-text": "Er du sikker på, du ønsker at kvittere for alarm?", - "clear-alarms-title": "", - "clear-alarms-text": "", - "clear-alarm-title": "Ryd alarm", - "clear-alarm-text": "Er du sikker på, at du vil slette Alarm?", - "alarm-status-filter": "Filter for alarmstatus", - "alarm-filter": "Alarmfilter", - "max-count-load": "Maks. antal alarmer, der skal indlæses (0 – ubegrænset)", - "max-count-load-required": "Maks. antal alarmer, der skal indlæses, er påkrævet.", - "max-count-load-error-min": "Minimumværdien er 0.", - "fetch-size": "Hent størrelse", - "fetch-size-required": "Hent størrelse er påkrævet.", - "fetch-size-error-min": "Minimumværdien er 10.", - "alarm-type-list": "Liste over alarmtyper", - "any-type": "Enhver type", - "search-propagated-alarms": "Søg efter overførte alarmer" - }, - "alias": { - "add": "Tilføj alias", - "edit": "Rediger alias", - "name": "Aliasnavn", - "name-required": "Aliasnavn er påkrævet", - "duplicate-alias": "Alias med samme navn findes allerede.", - "filter-type-single-entity": "Enkelt entitet", - "filter-type-entity-group": "Gruppeentiteter", - "filter-type-entity-list": "Entitetsliste", - "filter-type-entity-name": "Entitetsnavn", - "filter-type-entity-type": "Entitetstype", - "filter-type-entity-group-list": "Entitetsgruppeliste", - "filter-type-entity-group-name": "Entitetsgruppenavn", - "filter-type-entities-by-group-name": "Entiteter efter gruppenavn", - "filter-type-state-entity": "Entitet fra dashboardtilstand", - "filter-type-state-entity-description": "Entitet taget fra dashboardtilstandsparametre", - "filter-type-state-entity-owner": "Ejer af entitet fra dashboardtilstand", - "filter-type-state-entity-owner-description": "Ejer af entitet taget fra dashboardtilstandsparametre", - "filter-type-asset-type": "Aktivtype", - "filter-type-asset-type-description": "", - "filter-type-asset-type-and-name-description": "", - "filter-type-device-type": "Enhedstype", - "filter-type-device-type-description": "", - "filter-type-device-type-and-name-description": "", - "filter-type-entity-view-type": "Entitetsvisningstype", - "filter-type-entity-view-type-description": "", - "filter-type-entity-view-type-and-name-description": "", - "filter-type-relations-query": "Relationsforespørgsel", - "filter-type-relations-query-description": "", - "filter-type-asset-search-query": "Aktivsøgningsforespørgsel", - "filter-type-asset-search-query-description": "", - "filter-type-device-search-query": "Enhedssøgningsforespørgsel", - "filter-type-device-search-query-description": "", - "filter-type-entity-view-search-query": "Entitetsvisning for søgningsforespørgsel", - "filter-type-entity-view-search-query-description": "", - "filter-type-apiUsageState": "Api-brugstilstand", - "entity-filter": "Entitetsfilter", - "resolve-multiple": "Løs som flere entiteter", - "filter-type": "Filtertype", - "filter-type-required": "Filtertype er påkrævet.", - "entity-filter-no-entity-matched": "Der blev ikke fundet nogen entiteter, der matcher det angivne filter.", - "no-entity-filter-specified": "Intet entitetsfilter angivet", - "root-state-entity": "Brug dashboardtilstandsentitet som rod", - "group-state-entity": "Brug dashboardtilstandsentitet som entitetsgruppe", - "group-state-entity-owner": "Brug dashboardtilstandsentitet som entitetsgruppeejer", - "last-level-relation": "Hent kun sidste niveaurelation", - "root-entity": "Rodentitet", - "state-entity-parameter-name": "Parameternavn for tilstandsentitet", - "default-state-entity": "Standard tilstandsentitet", - "default-state-entity-group": "Standard tilstandsentitetsgruppe", - "default-entity-parameter-name": "Som standard", - "max-relation-level": "Maks. niveaurelation", - "unlimited-level": "Ubegrænset niveau", - "state-entity": "Dashboardtilstandsentitet", - "entities-of-group-state-entity": "Entiteter fra dashboardtilstandsentitetsgruppe", - "all-entities": "Alle entiteter", - "any-relation": "enhver" - }, - "asset": { - "asset": "Aktiv", - "assets": "Aktiver", - "management": "Styring af aktiver", - "view-assets": "Vis aktiver", - "add": "Tilføj aktiv", - "assign-to-customer": "Tildel til kunde", - "assign-asset-to-customer": "Tildel aktiv(er) til kunde", - "assign-asset-to-customer-text": "Vælg de aktiver, der skal tildeles til kunden", - "no-assets-text": "Ingen aktiver fundet", - "assign-to-customer-text": "Vælg den kunde, der skal tildeles aktivet/aktiverne", - "public": "Offentlig", - "assignedToCustomer": "Tildelt til kunde", - "make-public": "Gør aktiv offentligt", - "make-private": "Gør aktiv privat", - "unassign-from-customer": "Fjern tildeling fra kunde", - "delete": "Slet aktiv", - "asset-public": "Aktiv er offentligt", - "asset-type": "Aktivtype", - "asset-type-required": "Aktivtype er påkrævet.", - "select-asset-type": "Vælg aktivtype", - "enter-asset-type": "Indtast aktivtype", - "any-asset": "Ethvert aktiv", - "no-asset-types-matching": "", - "asset-type-list-empty": "Ingen aktivtyper valgt.", - "asset-types": "Aktivtyper", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "type": "Type", - "type-required": "Type er påkrævet.", - "details": "Oplysninger", - "events": "Begivenheder", - "add-asset-text": "Tilføj nyt aktiv", - "asset-details": "Oplysninger om aktiv", - "assign-assets": "Tildel aktiver", - "assign-assets-text": "", - "delete-assets": "Slet aktiver", - "unassign-assets": "Fjern tildeling af aktiver", - "unassign-assets-action-title": "", - "assign-new-asset": "Tildel nyt aktiv", - "delete-asset-title": "", - "delete-asset-text": "Vær forsigtig. Efter bekræftelsen vil aktivet og alle relaterede data være uoprettelige.", - "delete-assets-title": "", - "delete-assets-action-title": "", - "delete-assets-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte aktiver blive fjernet, og alle relaterede data vil være uoprettelige.", - "make-public-asset-title": "", - "make-public-asset-text": "Efter bekræftelsen vil aktivet og alle dets data blive gjort offentlige og tilgængelige for andre.", - "make-private-asset-title": "", - "make-private-asset-text": "Efter bekræftelsen vil aktivet og alle dets data blive gjort private og vil ikke være tilgængelige for andre.", - "unassign-asset-title": "", - "unassign-asset-text": "Efter bekræftelsen fjernes tildelingen af aktivet og vil ikke være tilgængeligt for kunden.", - "unassign-asset": "Fjern tildeling af aktiv", - "unassign-assets-title": "", - "unassign-assets-text": "Efter bekræftelsen vil alle valgte aktiver få fjernet tildelingen og ikke være tilgængelige for kunden.", - "copyId": "Kopiér aktiv-id", - "idCopiedMessage": "Aktiv-id er blevet kopieret til udklipsholder", - "select-asset": "Vælg aktiv", - "no-assets-matching": "", - "asset-required": "Aktiv er påkrævet", - "name-starts-with": "Aktivnavn starter med", - "selected-assets": "", - "search": "Søg efter aktiver", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte aktiver", - "select-group-to-move": "Vælg målgruppe for at flytte valgte aktiver", - "remove-assets-from-group": "", - "group": "Aktivgruppe", - "list-of-groups": "", - "group-name-starts-with": "", - "import": "Importér aktiver", - "asset-file": "Aktivfil", - "label": "Mærkning" - }, - "attribute": { - "attributes": "Attributter", - "latest-telemetry": "Seneste telemetri", - "attributes-scope": "Omfang af entitetsattributter", - "scope-telemetry": "Telemetri", - "scope-latest-telemetry": "Seneste telemetri", - "scope-client": "Klientattributter", - "scope-server": "Serverattributter", - "scope-shared": "Delte attributter", - "add": "Tilføj attribut", - "add-attribute-prompt": "Tilføj venligst attribut", - "key": "Nøgle", - "last-update-time": "Seneste opdateringstid", - "key-required": "Attributnøgle er påkrævet.", - "value": "Værdi", - "value-required": "Attributværdi er påkrævet.", - "delete-attributes-title": "", - "delete-attributes-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte attributter blive fjernet.", - "delete-attributes": "Slet attributter", - "enter-attribute-value": "Indtast attributværdi", - "show-on-widget": "Vis på widget", - "widget-mode": "Widget-tilstand", - "next-widget": "Næste widget", - "prev-widget": "Forrige widget", - "add-to-dashboard": "Føj til dashboard", - "add-widget-to-dashboard": "Føj widget til dashboard", - "selected-attributes": "", - "selected-telemetry": "", - "no-attributes-text": "Ingen attributter fundet", - "no-telemetry-text": "Ingen telemetri fundet" - }, - "api-usage": { - "api-usage": "Api-brug", - "data-points": "Datapunkter", - "data-points-storage-days": "Lagringsdage for datapunkter", - "email": "E-mail", - "email-messages": "E-mailbeskeder", - "email-messages-daily-activity": "Daglig aktivitet for e-mailbeskeder", - "email-messages-hourly-activity": "Timeaktivitet for e-mailbeskeder", - "email-messages-monthly-activity": "Månedlig aktivitet for e-mailbeskeder", - "exceptions": "Undtagelser", - "executions": "Udførelser", - "javascript": "JavaScript", - "javascript-executions": "JavaScript-udførelser", - "latest-error": "Seneste fejl", - "messages": "Beskeder", - "permanent-failures": "${entityName} Permanente fejl", - "permanent-timeouts": "${entityName} Permanente timeouts", - "processing-failures": "${entityName} Behandling af fejl", - "processing-failures-and-timeouts": "Behandling af fejl og timeouts", - "processing-timeouts": "${entityName} Behandling af timeouts", - "queue-stats": "Køstatistikker", - "rule-chain": "Regelkæde", - "rule-engine": "Regelprogram", - "rule-engine-daily-activity": "Regelmotors daglige aktivitet", - "rule-engine-executions": "Regelmotorudførelser", - "rule-engine-hourly-activity": "Regelmotors timeaktivitet", - "rule-engine-monthly-activity": "Regelmotors månedlige aktivitet", - "rule-engine-statistics": "Statistik for regelmotor", - "rule-node": "Regelknude", - "sms": "SMS", - "sms-messages": "SMS-beskeder", - "sms-messages-daily-activity": "Daglig aktivitet for SMS-beskeder", - "sms-messages-hourly-activity": "Timeaktivitet for SMS-beskeder", - "sms-messages-monthly-activity": "Månedlig aktivitet for SMS-beskeder", - "successful": "${entityName} Vellykket", - "telemetry": "Telemetri", - "telemetry-persistence": "Telemetri-vedholdenhed", - "telemetry-persistence-daily-activity": "Daglig aktivitet for telemetri-vedholdenhed", - "telemetry-persistence-hourly-activity": "Timeaktivitet for telemetri-vedholdenhed", - "telemetry-persistence-monthly-activity": "Månedlig aktivitet for telemetri-vedholdenhed", - "transport": "Transport", - "transport-daily-activity": "Daglig aktivitet for transport", - "transport-data-points": "Transportdatapunkter", - "transport-hourly-activity": "Timeaktivitet for transport", - "transport-messages": "Transportmeddelelser", - "transport-monthly-activity": "Månedlig aktivitet for transport", - "view-details": "Vis oplysninger", - "view-statistics": "Vis statistik" - }, - "audit-log": { - "audit": "Audit", - "audit-logs": "Auditlogs", - "timestamp": "Tidsstempel", - "entity-type": "Entitetstype", - "entity-name": "Entitetsnavn", - "user": "Bruger", - "type": "Type", - "status": "Status", - "details": "Oplysninger", - "type-added": "Tilføjet", - "type-deleted": "Slettet", - "type-updated": "Opdateret", - "type-attributes-updated": "Attributter opdateret", - "type-attributes-deleted": "Attributter slettet", - "type-rpc-call": "RPC-opkald", - "type-credentials-updated": "Brugeroplysninger opdateret", - "type-assigned-to-customer": "Tildelt til kunde", - "type-unassigned-from-customer": "Tildeling fjernet fra kunde", - "type-activated": "Aktiveret", - "type-suspended": "Indstillet", - "type-credentials-read": "Brugeroplysninger læst", - "type-attributes-read": "Attributter læst", - "type-added-to-entity-group": "Tilføjet til gruppe", - "type-removed-from-entity-group": "Fjernet fra gruppe", - "type-relation-add-or-update": "Relation opdateret", - "type-relation-delete": "Relation slettet", - "type-relations-delete": "Alle relationer slettet", - "type-alarm-ack": "Kvitteret", - "type-alarm-clear": "Ryddet", - "type-rest-api-rule-engine-call": "Regelmotor REST API-opkald", - "type-made-public": "Gjort offentligt", - "type-made-private": "Gjort privat", - "type-login": "Log på", - "type-logout": "Log af", - "type-lockout": "Spærring", - "status-success": "Succes", - "status-failure": "Fejl", - "audit-log-details": "Oplysninger om auditlog", - "no-audit-logs-prompt": "Ingen logs fundet", - "action-data": "Handlingsdata", - "failure-details": "Fejloplysninger", - "search": "Søg efter auditlogfiler", - "clear-search": "Ryd søgning", - "type-assigned-from-tenant": "Tildelt fra lejer", - "type-assigned-to-tenant": "Tildelt til lejer", - "type-provision-success": "Enhed klargjort", - "type-provision-failure": "Enhed klargjort mislykkedes", - "type-timeseries-updated": "Telemetri opdateret", - "type-timeseries-deleted": "Telemetri slettet", - "type-owner-changed": "Ejer ændret" - }, - "confirm-on-exit": { - "message": "Du har ikke-gemte ændringer. Er du sikker på, at du vil forlade denne side?", - "html-message": "Du har ikke-gemte ændringer.
Er du sikker på, at du vil forlade denne side?", - "title": "Ugemte ændringer" - }, - "contact": { - "country": "Land", - "city": "By", - "state": "Region", - "postal-code": "Postnummer", - "postal-code-invalid": "Ugyldigt postnummerformat.", - "address": "Adresse", - "address2": "Adresse 2", - "phone": "Telefon", - "email": "E-mail", - "no-address": "Ingen adresse" - }, - "common": { - "username": "Brugernavn", - "password": "Adgangskode", - "enter-username": "Indtast brugernavn", - "enter-password": "Indtast adgangskode", - "enter-search": "Indtast søgning", - "created-time": "Oprettelsestidspunkt", - "loading": "Indlæser..." - }, - "converter": { - "converter": "Dataomformer", - "converters": "Dataomformere", - "select-converter": "Vælg dataomformer", - "no-converters-matching": "", - "converter-required": "Dataomformer er påkrævet", - "delete": "Slet omformer", - "management": "Styring af dataomformere", - "add-converter-text": "Tilføj ny dataomformer", - "no-converters-text": "Ingen dataomformere fundet", - "selected-converters": "", - "delete-converter-title": "", - "delete-converter-text": "Vær forsigtig. Efter bekræftelsen vil dataomformeren og alle relaterede data være uoprettelige.", - "delete-converters-title": "", - "delete-converters-action-title": "", - "delete-converters-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte dataomformere blive fjernet, og alle relaterede data vil være uoprettelige.", - "events": "Begivenheder", - "add": "Tilføj dataomformer", - "search": "Søg efter dataomformere", - "converter-details": "Oplysninger om dataomformer", - "details": "Oplysninger", - "copyId": "Kopiér omformer-id", - "idCopiedMessage": "Omformer-id er blevet kopieret til udklipsholder", - "debug-mode": "Debug-tilstand", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "decoder": "Dekoder", - "encoder": "Indkoder", - "test-decoder-fuction": "Test af dekoderfunktion", - "test-encoder-fuction": "Test af indkoderfunktion", - "decoder-input-params": "Parametre for dekoderindgang", - "encoder-input-params": "Parametre for indkoderindgang", - "payload": "Data", - "payload-content-type": "Dataindholdstype", - "payload-content": "Dataindhold", - "message": "Meddelelse", - "message-type": "Meddelelsestype", - "message-type-required": "Meddelelsestype er påkrævet", - "test": "Test", - "metadata": "Metadata", - "metadata-required": "Metadataposter må ikke være tomme.", - "integration-metadata": "Integrationsmetadata", - "integration-metadata-required": "Integrationsmetadataposter må ikke være tomme.", - "output": "Output", - "import": "Importér omformer", - "export": "Eksportér omformer", - "export-failed-error": "", - "create-new-converter": "Opret ny omformer", - "converter-file": "Omformerfil", - "invalid-converter-file-error": "Omformeren kunne ikke importeres: Ugyldig omformerdatastruktur.", - "type": "Type", - "type-required": "Type er påkrævet.", - "type-uplink": "Uplink", - "type-downlink": "Downlink" - }, - "content-type": { - "json": "Json", - "text": "Tekst", - "binary": "Binær (Base64)" - }, - "customer": { - "customer": "Kunde", - "customers": "Kunder", - "management": "Kundeadministration", - "dashboard": "Kundens dashboard", - "dashboards": "Kundens dashboards", - "devices": "Kundeenheder", - "entity-views": "Kundeentitetsvisninger", - "assets": "Kundeaktiver", - "public-dashboards": "Offentlige dashboards", - "public-devices": "Offentlige enheder", - "public-assets": "Offentlige aktiver", - "public-entity-views": "Visninger af offentlig entitet", - "add": "Tilføj kunde", - "delete": "Slet kunde", - "manage-customer-user-groups": "Administrer kundebrugergrupper", - "manage-customer-groups": "Administrer kundegrupper", - "manage-customer-device-groups": "Administrer kundeenhedsgrupper", - "manage-customer-asset-groups": "Administrer kundeaktivgrupper", - "manage-customer-entity-view-groups": "Administrer visningsgrupper for kundeentitet", - "manage-customer-dashboard-groups": "Administrer kundedashboardgrupper", - "manage-customer-users": "Administrer kundebrugere", - "manage-customers": "Administrer kunder", - "manage-customer-devices": "Administrer kundeenheder", - "manage-customer-entity-views": "Administrer visninger af kundeentitet", - "manage-customer-dashboards": "Administrer kundedashboards", - "manage-public-devices": "Administrer offentlige enheder", - "manage-public-dashboards": "Administrer offentlige dashboards", - "manage-customer-assets": "Administrer kundeaktiver", - "manage-public-assets": "Administrer offentlige aktiver", - "add-customer-text": "Tilføj ny kunde", - "no-customers-text": "Ingen kunder fundet", - "customer-details": "Kundeinformation", - "delete-customer-title": "", - "delete-customer-text": "Vær forsigtig. Efter bekræftelsen vil kunden og alle relaterede data være uoprettelige.", - "delete-customers-title": "", - "delete-customers-action-title": "", - "delete-customers-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte kunder blive fjernet, og alle relaterede data vil være uoprettelige.", - "manage-user-groups": "Administrer brugergrupper", - "manage-asset-groups": "Administrer aktivgrupper", - "manage-device-groups": "Administrer enhedsgrupper", - "manage-dashboard-groups": "Administrer dashboardgrupper", - "manage-entity-view-groups": "Administrer entitetsvisningsgrupper", - "manage-users": "Administrer brugere", - "manage-assets": "Administrer aktiver", - "manage-devices": "Administrer enheder", - "manage-dashboards": "Administrer dashboards", - "title": "Titel", - "title-required": "Titel er påkrævet.", - "description": "Beskrivelse", - "details": "Oplysninger", - "events": "Begivenheder", - "copyId": "Kopiér kunde-id", - "idCopiedMessage": "Kunde-id er blevet kopieret til udklipsholder", - "select-customer": "Vælg kunde", - "no-customers-matching": "", - "customer-required": "Kunde er påkrævet", - "selected-customers": "", - "search": "Søg efter kunder", - "select-group-to-add": "Vælg målgruppe for at tilføje udvalgte kunder", - "select-group-to-move": "Vælg målgruppe for at flytte udvalgte kunder", - "remove-customers-from-group": "", - "group": "Kundegruppe", - "list-of-groups": "", - "group-name-starts-with": "", - "select-default-customer": "Vælg standardkunde", - "default-customer": "Standardkunde", - "default-customer-required": "Standardkunde er påkrævet for at debugge dashboard på lejerniveau", - "allow-white-labeling": "Tillad hvid mærkning" - }, - "customers-hierarchy": { - "customers-hierarchy": "Kundehierarki", - "open-nav-tree": "Åbn navigationstræ", - "return-to-top-level": "Tilbage til øverste niveau" - }, - "custom-menu": { - "custom-menu": "Brugerdefineret menu", - "custom-menu-hint": "Definer brugerdefineret menu JSON nedenfor. Denne JSON indeholder en liste over brugerdefinerede menupunkter." - }, - "custom-translation": { - "custom-translation": "Brugerdefineret oversættelse", - "translation-map": "Oversættelsestilknytning", - "key": "Oversættelsesnøgle", - "import": "Importér oversættelse", - "export": "Eksportér oversættelse", - "export-data": "Eksportér oversættelsesdata", - "import-data": "Importér oversættelsesdata", - "translation-file": "Oversættelsesfil", - "invalid-translation-file-error": "Kunne ikke importere oversættelsesfil: Ugyldig oversættelsesdatastruktur.", - "custom-translation-hint": "Definer brugerdefineret oversættelse JSON nedenfor. Denne JSON overskriver standardoversættelsen. Klik på 'Download lokalfil' for at hente eksisterende oversættelse. Du kan også bruge den downloadede fil som reference til tilgængelige nøgleværdipar for oversættelse.", - "download-locale-file": "Download lokalfil" - }, - "datetime": { - "date-from": "Dato fra", - "time-from": "Tidspunkt fra", - "date-to": "Dato til", - "time-to": "Tidspunkt til" - }, - "dashboard": { - "dashboard": "Dashboard", - "dashboards": "Dashboards", - "management": "Dashboardadministration", - "view-dashboards": "Vis dashboards", - "add": "Tilføj dashboard", - "assign-dashboard-to-customer": "Tildel dashboard(s) til kunde", - "assign-dashboard-to-customer-text": "Vælg de dashboards, der skal tildeles til kunden", - "assign-to-customer-text": "Vælg den kunde, der skal tildeles dashboard(s)", - "assign-to-customer": "Tildel til kunde", - "unassign-from-customer": "Fjern tildeling fra kunde", - "make-public": "Gør dashboard offentligt", - "make-private": "Gør dashboard privat", - "manage-assigned-customers": "Administrer tildelte kunder", - "assigned-customers": "Tildelte kunder", - "assign-to-customers": "Tildel dashboard(s) til kunder", - "assign-to-customers-text": "Vælg de kunder, der skal tildeles dashboard(s)", - "unassign-from-customers": "Fjern tildeling af dashboard(s) fra kunder", - "unassign-from-customers-text": "Vælg de kunder, hvor tildeling skal fjernes fra dashboard(s)", - "no-dashboards-text": "Ingen dashboards fundet", - "no-widgets": "Ingen widgets konfigureret", - "add-widget": "Tilføj ny widget", - "title": "Titel", - "select-widget-title": "Vælg widget", - "select-widget-value": "", - "select-widget-subtitle": "Liste over tilgængelige widget-typer", - "delete": "Slet dashboard", - "title-required": "Titel er påkrævet.", - "description": "Beskrivelse", - "details": "Oplysninger", - "dashboard-details": "Oplysninger om dashboard", - "add-dashboard-text": "Tilføj nyt dashboard", - "assign-dashboards": "Tildel dashboards", - "assign-new-dashboard": "Tildel nyt dashboard", - "assign-dashboards-text": "", - "unassign-dashboards-action-text": "", - "delete-dashboards": "Slet dashboards", - "unassign-dashboards": "Fjern tildeling af dashboards", - "unassign-dashboards-action-title": "", - "delete-dashboard-title": "", - "delete-dashboard-text": "Vær forsigtig. Efter bekræftelsen vil dashboardet og alle relaterede data være uoprettelige.", - "delete-dashboards-title": "", - "delete-dashboards-action-title": "", - "delete-dashboards-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte dashboards blive fjernet, og alle relaterede data vil være uoprettelige.", - "unassign-dashboard-title": "", - "unassign-dashboard-text": "Efter bekræftelsen vil dashboardets tildeling blive fjernet og vil ikke være tilgængelig for kunden.", - "unassign-dashboard": "Fjern tildeling af dashboard", - "unassign-dashboards-title": "", - "unassign-dashboards-text": "Efter bekræftelsen vil tildelingen af alle valgte dashboards blive fjernet og vil ikke være tilgængelige for kunden.", - "public-dashboard-title": "Dashboard er nu offentligt", - "public-dashboard-text": "", - "public-dashboard-notice": "Bemærk: Glem ikke at gøre relaterede enheder offentlige for at få adgang til deres data.", - "public-dashboard-link": "Link til offentligt dashboard", - "public-dashboard-link-text": "", - "public-dashboard-link-notice": "Bemærk: Glem ikke at gøre relaterede enheder, aktiver og entitetsvisninger offentlige for at få adgang til deres data.", - "make-private-dashboard-title": "", - "make-private-dashboard-text": "Efter bekræftelsen vil dashboardet blive gjort privat og vil ikke være tilgængeligt for andre.", - "make-private-dashboard": "Gør dashboard privat", - "socialshare-text": "", - "socialshare-title": "", - "select-dashboard": "Vælg dashboard", - "no-dashboards-matching": "", - "dashboard-required": "Dashboard er påkrævet.", - "select-existing": "Vælg eksisterende dashboard", - "create-new": "Opret nyt dashboard", - "new-dashboard-title": "Ny dashboardtitel", - "open-dashboard": "Åbn dashboard", - "set-background": "Angiv baggrund", - "background-color": "Baggrundsfarve", - "background-image": "Baggrundsbillede", - "background-size-mode": "Baggrundsstørrelsestilstand", - "no-image": "Intet billede valgt", - "drop-image": "Træk og slip et billede, eller klik for at vælge en fil, der skal uploades.", - "maximum-upload-file-size": "", - "cannot-upload-file": "Filen kan ikke uploades", - "settings": "Indstillinger", - "columns-count": "Kolonneantal", - "columns-count-required": "Kolonneantal er påkrævet.", - "min-columns-count-message": "Kun 10 minimum kolonneantal er tilladt.", - "max-columns-count-message": "Kun 1000 maksimum kolonneantal er tilladt.", - "widgets-margins": "Margin mellem widgets", - "margin-required": "Marginværdi er påkrævet.", - "min-margin-message": "Kun 0 er tilladt som minimummarginværdi.", - "max-margin-message": "Kun 50 er tilladt som maksimummarginværdi.", - "horizontal-margin": "Horisontal margin", - "horizontal-margin-required": "Horisontal marginværdi er påkrævet.", - "min-horizontal-margin-message": "Kun 0 er tilladt som horisontal minimummarginværdi.", - "max-horizontal-margin-message": "Kun 50 er tilladt som horisontal maksimummarginværdi.", - "vertical-margin": "Vertikal margin", - "vertical-margin-required": "Vertikal marginværdi er påkrævet.", - "min-vertical-margin-message": "Kun 0 er tilladt som vertikal minimummarginværdi.", - "max-vertical-margin-message": "Kun 50 er tilladt som vertikal maksimummarginværdi.", - "autofill-height": "Autoudfyld layouthøjde", - "mobile-layout": "Mobile layoutindstillinger", - "mobile-row-height": "Mobil rækkehøjde, px", - "mobile-row-height-required": "Mobil rækkehøjdeværdi er påkrævet.", - "min-mobile-row-height-message": "Kun 5 pixel er tilladt som minimumværdi for mobilrækkehøjde.", - "max-mobile-row-height-message": "Kun 200 pixel er tilladt som maksimumværdi for mobilrækkehøjde.", - "display-title": "Vis dashboardtitel", - "toolbar-always-open": "Hold værktøjslinjen åben", - "title-color": "Titelfarve", - "display-dashboards-selection": "Vis valg af dashboards", - "display-entities-selection": "Vis valg af entiteter", - "display-filters": "Vis filtre", - "display-dashboard-timewindow": "Vis tidsvindue", - "display-dashboard-export": "Vis eksport", - "import": "Importér dashboard", - "export": "Eksportér dashboard", - "export-failed-error": "", - "export-pdf": "Eksportér som PDF", - "export-png": "Eksportér som PNG", - "export-jpg": "Eksportér som JPEG", - "export-json-config": "Eksportér JSON-konfiguration", - "download-dashboard-progress": "", - "create-new-dashboard": "Opret nyt dashboard", - "dashboard-file": "Dashboardfil", - "invalid-dashboard-file-error": "Kunne ikke importere dashboard: Ugyldig dashboarddatastruktur.", - "dashboard-import-missing-aliases-title": "Konfigurer aliasser anvendt af importeret dashboard", - "create-new-widget": "Opret ny widget", - "import-widget": "Importér widget", - "widget-file": "Widget-fil", - "invalid-widget-file-error": "Kan ikke importere widget: Ugyldig widget-datastruktur.", - "widget-import-missing-aliases-title": "Konfigurer aliasser, der bruges af importeret widget", - "open-toolbar": "Skjul dashboardets værktøjslinje", - "close-toolbar": "Luk værktøjslinje", - "configuration-error": "Konfigurationsfejl", - "alias-resolution-error-title": "Konfigurationsfejl i dashboardaliasser", - "invalid-aliases-config": "Kunne ikke finde nogen enheder, der matcher nogle af aliasfiltrene.
Kontakt din administrator for at løse dette problem.", - "select-devices": "Vælg enheder", - "assignedToCustomer": "Tildelt til kunde", - "assignedToCustomers": "Tildelt til kunder", - "public": "Offentlig", - "public-link": "Offentligt link", - "copy-public-link": "Kopiér offentligt link", - "public-link-copied-message": "Dashboardets offentilige link er blevet kopieret til udklipsholderen", - "manage-states": "Administrer dashboardtilstande", - "states": "Dashboardtilstande", - "search-states": "Søg efter dashboardtilstande", - "selected-states": "", - "edit-state": "Rediger dashboardtilstand", - "delete-state": "Slet dashboardtilstand", - "add-state": "Tilføj dashboardtilstand", - "no-states-text": "Ingen tilstande fundet", - "state": "Dashboardtilstand", - "state-name": "Navn", - "state-name-required": "Dashboardtilstandsnavn er påkrævet.", - "state-id": "Tilstands-id", - "state-id-required": "Dashboardtilstands-id er påkrævet.", - "state-id-exists": "Dashboardtilstand med samme id findes allerede.", - "is-root-state": "Rodtilstand", - "delete-state-title": "Slet dashboardtilstand", - "delete-state-text": "", - "show-details": "Vis oplysninger", - "hide-details": "Skjul oplysninger", - "select-state": "Vælg måltilstand", - "state-controller": "Tilstandscontroller", - "selected-dashboards": "", - "search": "Søg efter dashboards", - "home-dashboard": "Startside", - "home-dashboard-hide-toolbar": "Skjul startsidens værktøjslinje", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte dashboards", - "select-group-to-move": "Vælg målgruppe for at flytte valgte dashboards", - "remove-dashboards-from-group": "", - "group": "Gruppe af dashboards", - "list-of-groups": "", - "group-name-starts-with": "" - }, - "datakey": { - "settings": "Indstillinger", - "advanced": "Fremskreden", - "label": "Mærkning", - "color": "Farve", - "units": "Specielt symbol, der vises ved siden af værdi", - "decimals": "Antal cifre efter flydende punkt", - "data-generation-func": "Datagenereringsfunktion", - "use-data-post-processing-func": "Brug dataefterbehandlingsfunktion", - "configuration": "Konfiguration af datanøgle", - "timeseries": "Tidsserier", - "attributes": "Attributter", - "entity-field": "Entitetsfelt", - "alarm": "Alarmfelter", - "timeseries-required": "Entitets tidsserier er påkrævet.", - "timeseries-or-attributes-required": "Entitets tidsserier/attributter er påkrævet.", - "alarm-fields-timeseries-or-attributes-required": "Alarmfelter eller entitets tidsserier/attributter er påkrævet.", - "maximum-timeseries-or-attributes": "", - "alarm-fields-required": "Alarmfelter er påkrævet.", - "function-types": "Funktionstyper", - "function-types-required": "Funktionstyper er påkrævet.", - "maximum-function-types": "", - "time-description": "tidsstempel for den aktuelle værdi", - "value-description": "den aktuelle værdi", - "prev-value-description": "resultat af forrige funktionskald", - "time-prev-description": "tidsstempel for den forrige værdi", - "prev-orig-value-description": "oprindelig tidligere værdi" - }, - "datasource": { - "type": "Datakildetype", - "name": "Navn", - "label": "Mærkning", - "add-datasource-prompt": "Tilføj datakilde" - }, - "details": { - "details": "Oplysninger", - "edit-mode": "Redigeringstilstand", - "edit-json": "Rediger JSON", - "toggle-edit-mode": "Skift redigeringstilstand" - }, - "device": { - "device": "Enhed", - "device-required": "Enhed er påkrævet.", - "devices": "Enheder", - "management": "Enhedsadministration", - "view-devices": "Vis enheder", - "device-alias": "Enhedsalias", - "aliases": "Enhedsaliasser", - "no-alias-matching": "", - "no-aliases-found": "Ingen aliasser fundet.", - "no-key-matching": "", - "no-keys-found": "Ingen nøgler fundet.", - "create-new-alias": "Opret en ny!", - "create-new-key": "Opret en ny!", - "duplicate-alias-error": "", - "configure-alias": "", - "no-devices-matching": "", - "alias": "Alias", - "alias-required": "Enhedsalias er påkrævet.", - "remove-alias": "Fjern enhedsalias", - "add-alias": "Tilføj enhedsalias", - "name-starts-with": "Enhedens navn starter med", - "device-list": "Enhedsliste", - "use-device-name-filter": "Anvend filter", - "device-list-empty": "Ingen enheder valgt.", - "device-name-filter-required": "Enhedsnavnfilter er påkrævet.", - "device-name-filter-no-device-matched": "", - "add": "Tilføj enhed", - "assign-to-customer": "Tildel til kunde", - "assign-device-to-customer": "Tildel enhed(er) til kunde", - "assign-device-to-customer-text": "Vælg de enheder, der skal tildeles kunden", - "make-public": "Gør enheden offentlig", - "make-private": "Gør enheden privat", - "no-devices-text": "Ingen enheder fundet", - "assign-to-customer-text": "Vælg den kunde, der skal tilknyttes enheden/enhederne", - "device-details": "Enhedsoplysninger", - "add-device-text": "Tilføj ny enhed", - "credentials": "Brugeroplysninger", - "manage-credentials": "Administrer brugeroplysninger", - "delete": "Slet enhed", - "assign-devices": "Tildel enheder", - "assign-devices-text": "", - "delete-devices": "Slet enheder", - "unassign-from-customer": "Fjern tildeling fra kunde", - "unassign-devices": "Fjern tildeling af enheder", - "unassign-devices-action-title": "", - "assign-new-device": "Tildel ny enhed", - "make-public-device-title": "", - "make-public-device-text": "Efter bekræftelsen vil enheden og alle dens data blive gjort offentlige og tilgængelige for andre.", - "make-private-device-title": "", - "make-private-device-text": "Efter bekræftelsen vil enheden og alle dens data blive gjort private og vil ikke være tilgængelige for andre.", - "view-credentials": "Vis brugeroplysninger", - "delete-device-title": "", - "delete-device-text": "Vær forsigtig. Efter bekræftelsen vil enheden og alle relaterede data være uoprettelige.", - "delete-devices-title": "", - "delete-devices-action-title": "", - "delete-devices-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte enheder blive fjernet, og alle relaterede data vil være uoprettelige.", - "unassign-device-title": "", - "unassign-device-text": "Efter bekræftelsen vil enhedens tildeling blive fjernet og vil ikke være tilgængelig for kunden.", - "unassign-device": "Fjern tildeling af enhed", - "unassign-devices-title": "", - "unassign-devices-text": "Efter bekræftelsen vil tildelingen af alle valgte enheder blive fjernet og vil ikke være tilgængelige for kunden.", - "device-credentials": "Enhedsbrugeroplysninger", - "credentials-type": "Type af brugeroplysninger", - "access-token": "Adgangstoken", - "access-token-required": "Adgangstoken er påkrævet.", - "access-token-invalid": "Adgangstokenlængden skal være fra 1 til 20 tegn.", - "rsa-key": "RSA offentlig nøgle", - "rsa-key-required": "RSA offentlig nøgle er påkrævet.", - "client-id": "Klient-id", - "client-id-pattern": "Indeholder ugyldigt tegn.", - "user-name": "Brugernavn", - "user-name-required": "Brugernavn er påkrævet.", - "client-id-or-user-name-necessary": "Klient-id og/eller brugernavn er nødvendige", - "password": "Adgangskode", - "secret": "Hemmelig", - "secret-required": "Hemmelig er påkrævet.", - "device-type": "Enhedstype", - "device-type-required": "Enhedstype er påkrævet.", - "select-device-type": "Vælg enhedstype", - "enter-device-type": "Indtast enhedstype", - "any-device": "Enhver enhed", - "no-device-types-matching": "", - "device-type-list-empty": "Ingen enhedstyper valgt.", - "device-types": "Enhedstyper", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "label": "Mærkning", - "events": "Begivenheder", - "details": "Oplysninger", - "copyId": "Kopiér enheds-id", - "copyAccessToken": "Kopiér adgangstoken", - "copy-mqtt-authentication": "Kopiér MQTT-brugeroplysninger", - "idCopiedMessage": "Enheds-id er blevet kopieret til udklipsholder", - "accessTokenCopiedMessage": "Enhedsadgangstoken er blevet kopieret til udklipsholder", - "mqtt-authentication-copied-message": "Enhedens MQTT-godkendelse er blevet kopieret til udklipsholder", - "assignedToCustomer": "Tildelt til kunde", - "unable-delete-device-alias-title": "Kan ikke slette enhedsalias", - "unable-delete-device-alias-text": "", - "is-gateway": "Er gateway", - "overwrite-activity-time": "Overskriv aktivitetstid for tilsluttet enhed", - "public": "Offentlig", - "device-public": "Enheden er offentlig", - "select-device": "Vælg enhed", - "selected-devices": "", - "search": "Søg efter enheder", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte enheder", - "select-group-to-move": "Vælg målgruppe for at flytte valgte enheder", - "remove-devices-from-group": "", - "group": "Gruppe af enheder", - "list-of-groups": "", - "group-name-starts-with": "", - "import": "Importér enhed", - "device-file": "Enhedsfil", - "device-configuration": "Enhedskonfiguration", - "transport-configuration": "Transportkonfiguration", - "wizard": { - "device-details": "Enhedsoplysninger" + "alias" : { + "add" : "Tilføj alias", + "edit" : "Rediger alias", + "name" : "Aliasnavn", + "name-required" : "Aliasnavn er påkrævet", + "duplicate-alias" : "Alias med samme navn findes allerede.", + "filter-type-single-entity" : "Enkelt enhed", + "filter-type-entity-list" : "Enhedslisten", + "filter-type-entity-name" : "Enhedsnavn", + "filter-type-entity-type" : "Enhedstype", + "filter-type-state-entity" : "Enhed fra dashboardtilstand", + "filter-type-state-entity-description" : "Enhed hentet fra dashboardtilstandsparametre", + "filter-type-asset-type" : "Assettype", + "filter-type-asset-type-description" : "Assets af typen '{{assetTypes}}'", + "filter-type-asset-type-and-name-description" : "Assets af typen '{{assetTypes}}' og med navn der begynder med '{{prefix}}'", + "filter-type-device-type" : "Devicetype", + "filter-type-device-type-description" : "Devices af typen '{{deviceTypes}}'", + "filter-type-device-type-and-name-description" : "Devices af typen '{{deviceTypes}}' og med navn der begynder med '{{prefix}}'", + "filter-type-entity-view-type" : "Entity View-type", + "filter-type-entity-view-type-description" : "Entity Views af typen '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description" : "Entity Views af typen '{{entityViewTypes}}' og med navn der begynder med '{{prefix}}'", + "filter-type-edge-type" : "Edge-type", + "filter-type-edge-type-description" : "Edges af typen '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description" : "Edges af typen '{{edgeTypes}}' og med navn der begynder med '{{prefix}}'", + "filter-type-relations-query" : "Relationsforespørgsel", + "filter-type-relations-query-description" : "{{entities}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-edge-search-query" : "Edge-søgeforespørgsel", + "filter-type-edge-search-query-description" : "Edges med typerne {{edgeTypes}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query" : "Asset-søgeforespørgsel", + "filter-type-asset-search-query-description" : "Assets med typerne {{assetTypes}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query" : "Device-søgeforespørgsel", + "filter-type-device-search-query-description" : "Devices med typerne {{deviceTypes}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query" : "Entity View-søgeforespørgsel", + "filter-type-entity-view-search-query-description" : "Entity Views med typerne {{entityViewTypes}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState" : "API-brugstilstand", + "entity-filter" : "Enhedsfilter", + "resolve-multiple" : "Løs som flere enheder", + "resolve-multiple-hint" : "Aktivér for at vise data fra alle filtrerede enheder samtidig.\nHvis deaktiveret, viser widget kun data fra den valgte enhed.", + "filter-type" : "Filtertype", + "filter-type-required" : "Filtertype er påkrævet.", + "entity-filter-no-entity-matched" : "Ingen enheder matchede det angivne filter.", + "no-entity-filter-specified" : "Intet enhedsfilter angivet", + "root-state-entity" : "Brug dashboardtilstandens enhed som rod", + "last-level-relation" : "Hent kun sidste niveau relation", + "root-entity" : "Rodenhed", + "state-entity-parameter-name" : "Navn på tilstandsparameter", + "default-state-entity" : "Standard tilstandsenhed", + "default-entity-parameter-name" : "Som standard", + "max-relation-level" : "Maks. relationsniveau", + "unlimited-level" : "Ubegrænset niveau", + "state-entity" : "Dashboardtilstandsenhed", + "all-entities" : "Alle enheder", + "any-relation" : "enhver" + }, + "asset" : { + "asset" : "Asset", + "assets" : "Assets", + "management" : "Assetadministration", + "view-assets" : "Vis assets", + "add" : "Tilføj asset", + "asset-type-max-length" : "Assettype skal være mindre end 256 tegn", + "assign-to-customer" : "Tildel til kunde", + "assign-asset-to-customer" : "Tildel asset(s) til kunde", + "assign-asset-to-customer-text" : "Vælg venligst de assets, der skal tildeles kunden", + "no-assets-text" : "Ingen assets fundet", + "assign-to-customer-text" : "Vælg venligst kunden, som asset(s) skal tildeles", + "public" : "Offentlig", + "assignedToCustomer" : "Tildelt til kunde", + "make-public" : "Gør asset offentlig", + "make-private" : "Gør asset privat", + "unassign-from-customer" : "Fratildel fra kunde", + "delete" : "Slet asset", + "asset-public" : "Asset er offentlig", + "asset-type" : "Assettype", + "asset-type-required" : "Assettype er påkrævet.", + "select-asset-type" : "Vælg assettype", + "enter-asset-type" : "Indtast assetprofil", + "any-asset" : "Enhver asset", + "no-asset-types-matching" : "Ingen assettyper matcher '{{entitySubtype}}'.", + "asset-type-list-empty" : "Ingen assettyper valgt.", + "asset-types" : "Assettyper", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "name-max-length" : "Navn skal være mindre end 256 tegn", + "label-max-length" : "Etiket skal være mindre end 256 tegn", + "description" : "Beskrivelse", + "type" : "Type", + "type-required" : "Type er påkrævet.", + "details" : "Detaljer", + "events" : "Begivenheder", + "add-asset-text" : "Tilføj nyt asset", + "asset-details" : "Assetdetaljer", + "assign-assets" : "Tildel assets", + "assign-assets-text" : "Tildel { count, plural, =1 {1 asset} other {# assets} } til kunde", + "assign-asset-to-edge-title" : "Tildel asset(s) til edge", + "assign-asset-to-edge-text" : "Vælg venligst de assets, der skal tildeles til edge", + "delete-assets" : "Slet assets", + "unassign-assets" : "Fratildel assets", + "unassign-assets-action-title" : "Fratildel { count, plural, =1 {1 asset} other {# assets} } fra kunde", + "assign-new-asset" : "Tildel nyt asset", + "delete-asset-title" : "Er du sikker på, at du vil slette asset '{{assetName}}'?", + "delete-asset-text" : "Vær forsigtig, efter bekræftelse vil asset og alle relaterede data ikke kunne gendannes.", + "delete-assets-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 asset} other {# assets} }?", + "delete-assets-action-title" : "Slet { count, plural, =1 {1 asset} other {# assets} }", + "delete-assets-text" : "Vær forsigtig, efter bekræftelse vil alle valgte assets blive fjernet og dataene kan ikke gendannes.", + "make-public-asset-title" : "Er du sikker på, at du vil gøre asset '{{assetName}}' offentlig?", + "make-public-asset-text" : "Efter bekræftelse vil asset og alle dens data blive gjort offentlige og tilgængelige for andre.", + "make-private-asset-title" : "Er du sikker på, at du vil gøre asset '{{assetName}}' privat?", + "make-private-asset-text" : "Efter bekræftelse vil asset og alle dens data blive gjort private og ikke være tilgængelige for andre.", + "unassign-asset-title" : "Er du sikker på, at du vil fratilde asset '{{assetName}}'?", + "unassign-asset-text" : "Efter bekræftelse vil asset blive fratildelt og ikke være tilgængelig for kunden.", + "unassign-asset" : "Fratildel asset", + "unassign-assets-title" : "Er du sikker på, at du vil fratilde { count, plural, =1 {1 asset} other {# assets} }?", + "unassign-assets-text" : "Efter bekræftelse vil alle valgte assets blive fratildelt og ikke være tilgængelige for kunden.", + "copyId" : "Kopiér asset-ID", + "idCopiedMessage" : "Asset-ID er blevet kopieret til udklipsholderen", + "select-asset" : "Vælg asset", + "no-assets-matching" : "Ingen assets matcher '{{entity}}'.", + "asset-required" : "Asset er påkrævet", + "name-starts-with" : "Asset-navneudtryk", + "help-text" : "Brug '%' efter behov: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search" : "Søg assets", + "import" : "Importér assets", + "asset-file" : "Assetfil", + "label" : "Etiket", + "assign-asset-to-edge" : "Tildel asset(s) til edge", + "unassign-asset-from-edge" : "Fratildel asset", + "unassign-asset-from-edge-title" : "Er du sikker på, at du vil fratilde asset '{{assetName}}'?", + "unassign-asset-from-edge-text" : "Efter bekræftelse vil asset blive fratildelt og ikke være tilgængelig for edge.", + "unassign-assets-from-edge-title" : "Er du sikker på, at du vil fratilde { count, plural, =1 {1 asset} other {# assets} }?", + "unassign-assets-from-edge-text" : "Efter bekræftelse vil alle valgte assets blive fratildelt og ikke være tilgængelige for edge.", + "selected-assets" : "{ count, plural, =1 {1 asset} other {# assets} } valgt" + }, + "attribute" : { + "attributes" : "Attributter", + "latest-telemetry" : "Seneste telemetri", + "no-latest-telemetry" : "Ingen seneste telemetri", + "attributes-scope" : "Enhedsattributters område", + "scope-telemetry" : "Telemetri", + "scope-latest-telemetry" : "Seneste telemetri", + "scope-client" : "Klientattributter", + "scope-server" : "Serverattributter", + "scope-shared" : "Delte attributter", + "scope-client-short" : "Klient", + "scope-server-short" : "Server", + "scope-shared-short" : "Delt", + "scope-latest-short" : "Seneste", + "scope-any" : "Enhver", + "add" : "Tilføj attribut", + "key" : "Nøgle", + "key-max-length" : "Nøgle skal være mindre end 256 tegn", + "last-update-time" : "Seneste opdateringstidspunkt", + "key-required" : "Attributnøgle er påkrævet.", + "value" : "Værdi", + "value-required" : "Attributværdi er påkrævet.", + "telemetry-key-required" : "Telemetrinøgle er påkrævet", + "telemetry-value-required" : "Telemetriværdi er påkrævet", + "delete-attributes-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 attribut} other {# attributter} }?", + "delete-attributes-text" : "Vær forsigtig, efter bekræftelse vil alle valgte attributter blive fjernet.", + "delete-attributes" : "Slet attributter", + "enter-attribute-value" : "Indtast attributværdi", + "show-on-widget" : "Vis i widget", + "widget-mode" : "Widget-tilstand", + "next-widget" : "Næste widget", + "prev-widget" : "Forrige widget", + "add-to-dashboard" : "Tilføj til dashboard", + "add-widget-to-dashboard" : "Tilføj widget til dashboard", + "selected-attributes" : "{ count, plural, =1 {1 attribut} other {# attributter} } valgt", + "selected-telemetry" : "{ count, plural, =1 {1 telemetri-enhed} other {# telemetri-enheder} } valgt", + "no-attributes-text" : "Ingen attributter fundet", + "no-telemetry-text" : "Ingen telemetri fundet", + "copy-key" : "Kopiér nøgle", + "add-telemetry" : "Tilføj telemetri", + "copy-value" : "Kopiér værdi", + "delete-timeseries" : { + "start-time" : "Starttidspunkt", + "ends-on" : "Slutter den", + "strategy" : "Strategi", + "delete-strategy" : "Sletstrategi", + "all-data" : "Slet alle data", + "all-data-except-latest-value" : "Slet alle data undtagen seneste værdi", + "latest-value" : "Slet seneste værdi", + "all-data-for-time-period" : "Slet alle data for tidsperiode", + "rewrite-latest-value" : "Overskriv seneste værdi" } }, - "device-profile": { - "device-profile": "Enhedsprofil", - "device-profiles": "Enhedsprofiler", - "all-device-profiles": "Alle", - "add": "Tilføj enhedsprofil", - "edit": "Rediger enhedsprofil", - "device-profile-details": "Oplysninger om enhedsprofil", - "no-device-profiles-text": "Ingen enhedsprofiler fundet", - "search": "Søg efter enhedsprofiler", - "selected-device-profiles": "", - "no-device-profiles-matching": "", - "device-profile-required": "Enhedsprofil er påkrævet", - "idCopiedMessage": "Enhedsprofil-id er blevet kopieret til udklipsholder", - "set-default": "Gør enhedsprofil standard", - "delete": "Slet enhedsprofil", - "copyId": "Kopiér enhedsprofil-id", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "type": "Profiltype", - "type-required": "Profiltype er påkrævet.", - "type-default": "Standard", - "transport-type": "Transporttype", - "transport-type-required": "Transporttype er påkrævet.", - "transport-type-default": "Standard", - "transport-type-default-hint": "Understøtter grundlæggende MQTT-, HTTP- og CoAP-transport", - "transport-type-mqtt": "MQTT", - "transport-type-mqtt-hint": "Aktiverer avancerede MQTT-transportindstillinger", - "transport-type-lwm2m": "LWM2M", - "transport-type-lwm2m-hint": "LWM2M-transporttype", - "transport-type-coap": "CoAP", - "transport-type-coap-hint": "Aktiverer avancerede CoAP-transportindstillinger", - "description": "Beskrivelse", - "default": "Standard", - "profile-configuration": "Profilkonfiguration", - "transport-configuration": "Transportkonfiguration", - "default-rule-chain": "Standardregelkæde", - "select-queue-hint": "Vælg fra en rulleliste, eller tilføj et brugerdefineret navn.", - "delete-device-profile-title": "", - "delete-device-profile-text": "Vær forsigtig. Efter bekræftelsen vil enhedsprofilen og alle relaterede data være uoprettelige.", - "delete-device-profiles-title": "", - "delete-device-profiles-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte enhedsprofiler blive fjernet, og alle relaterede data vil være uoprettelige.", - "set-default-device-profile-title": "", - "set-default-device-profile-text": "Efter bekræftelsen vil enhedsprofilen blive markeret som standard og vil blive brugt til nye enheder, hvor der ikke er angivet nogen profil.", - "no-device-profiles-found": "Ingen enhedsprofiler fundet.", - "create-new-device-profile": "Opret en ny!", - "mqtt-device-topic-filters": "MQTT-enhedsemnefiltre", - "mqtt-device-topic-filters-unique": "MQTT-enhedsemnefiltre skal være unikke.", - "mqtt-device-payload-type": "MQTT-enhedsdata", - "transport-device-payload-type-json": "JSON", - "transport-device-payload-type-proto": "Protobuf", - "mqtt-payload-type-required": "Datatype er påkrævet.", - "coap-device-type": "CoAP-enhedstype", - "coap-device-payload-type": "CoAP-enhedsdata", - "coap-device-type-required": "CoAP-enhedstype er påkrævet.", - "coap-device-type-default": "Standard", - "coap-device-type-efento": "Efento NB-IoT", - "support-level-wildcards": "Enkelt [+] jokertegn og [#] jokertegn på flere niveauer understøttes.", - "telemetry-topic-filter": "Telemetriemnefilter", - "telemetry-topic-filter-required": "Telemetriemnefilter er påkrævet.", - "attributes-topic-filter": "Attributemnefilter", - "attributes-topic-filter-required": "Attributemnefilter er påkrævet.", - "telemetry-proto-schema": "Telemetri-protoskema", - "telemetry-proto-schema-required": "Telemetri-protoskema er påkrævet.", - "attributes-proto-schema": "Protoskema for attributter", - "attributes-proto-schema-required": "Protoskema for attributter er påkrævet.", - "rpc-response-topic-filter": "RPC-svaremnefilter", - "rpc-response-topic-filter-required": "RPC-svaremnefilter er påkrævet.", - "not-valid-pattern-topic-filter": "Ikke gyldigt mønsteremnefilter", - "not-valid-single-character": "Ugyldig brug af jokertegn i et enkelt niveau", - "not-valid-multi-character": "Ugyldig brug af jokertegn i flere niveauer", - "single-level-wildcards-hint": "[+] er velegnet til ethvert emnefilterniveau. Eks.: v1/enheder/+/telemetri eller +/enheder/+/attributter.", - "multi-level-wildcards-hint": "[#] kan selv erstatte emnefilteret og skal være det sidste symbol i emnet. Eks.: # eller v1/enheder/me/#.", - "alarm-rules": "Alarmregler", - "alarm-rules-with-count": "", - "no-alarm-rules": "Ingen alarmregler konfigureret", - "add-alarm-rule": "Tilføj alarmregel", - "edit-alarm-rule": "Rediger alarmregel", - "alarm-type": "Alarmtype", - "alarm-type-required": "Alarmtype er påkrævet.", - "alarm-type-unique": "Alarmtypen skal være unik inden for alarmreglerne for enhedsprofilen.", - "create-alarm-pattern": "", - "create-alarm-rules": "Opret alarmregler", - "no-create-alarm-rules": "Ingen opret betingelser konfigureret", - "add-create-alarm-rule-prompt": "Tilføj opret alarmregel", - "clear-alarm-rule": "Ryd alarmregel", - "no-clear-alarm-rule": "Ingen klar betingelse konfigureret", - "add-create-alarm-rule": "Tilføj opret betingelse", - "add-clear-alarm-rule": "Tilføj ryd tilstand", - "select-alarm-severity": "Vælg alarmens alvorsgrad", - "alarm-severity-required": "Alarmens alvorsgrad er påkrævet.", - "condition-duration": "Varighed af betingelse", - "condition-duration-value": "Varighedsværdi", - "condition-duration-time-unit": "Tidsenhed", - "condition-duration-value-range": "Varighedsværdien skal ligge i området fra 1 til 2147483647.", - "condition-duration-value-pattern": "Varighedsværdien skal være heltal.", - "condition-duration-value-required": "Varighedsværdi er påkrævet.", - "condition-duration-time-unit-required": "Tidsenhed er påkrævet.", - "advanced-settings": "Avancerede indstillinger", - "propagate-alarm": "Overfør alarm", - "alarm-rule-relation-types-list": "Relationstyper, der skal overføres", - "alarm-rule-relation-types-list-hint": "Hvis der ikke vælges Overfør relationstyper vil alarmer blive overført uden filtrering på relationstype.", - "alarm-rule-condition": "Alarmregelbetingelse", - "enter-alarm-rule-condition-prompt": "Tilføj alarmregelbetingelse", - "edit-alarm-rule-condition": "Rediger alarmregelbetingelse", - "device-provisioning": "Klargøring af enheden", - "provision-strategy": "Strategi for klargøring", - "provision-strategy-required": "Strategi for klargøring er påkrævet.", - "provision-strategy-disabled": "Deaktiveret", - "provision-strategy-created-new": "Tillad oprettelse af nye enheder", - "provision-strategy-check-pre-provisioned": "Kontrollér, om der er forhåndsklargjorte enheder", - "provision-device-key": "Klargøringsenhedsnøgle", - "provision-device-key-required": "Klargøringsenhedsnøgle er påkrævet.", - "copy-provision-key": "Kopiér klargøringsnøgle", - "provision-key-copied-message": "Klargøringsnøgle er blevet kopieret til udklipsholder", - "provision-device-secret": "Klargøringsenhed hemmelig", - "provision-device-secret-required": "Klargøringsenhed hemmelig er påkrævet.", - "copy-provision-secret": "Kopiér klargøring hemmelig", - "provision-secret-copied-message": "Klargøring hemmelig er blevet kopieret til udklipsholder", - "condition": "Betingelse", - "condition-type": "Betingelsestype", - "condition-type-simple": "Enkel", - "condition-type-duration": "Varighed", - "condition-during": "", - "condition-type-repeating": "Gentager", - "condition-type-required": "Betingelsesnavn er påkrævet.", - "condition-repeating-value": "Antal begivenheder", - "condition-repeating-value-range": "Antallet af begivenheder skal ligge i intervallet fra 1 til 2147483647.", - "condition-repeating-value-pattern": "Antal begivenheder skal være heltal.", - "condition-repeating-value-required": "Antal begivenheder er påkrævet.", - "condition-repeat-times": "", - "schedule-type": "Planlægningstype", - "schedule-type-required": "Planlægningstype er påkrævet.", - "schedule": "Tidsplan", - "edit-schedule": "Rediger alarmtidsplan", - "schedule-any-time": "Aktiv hele tiden", - "schedule-specific-time": "Aktiv på et bestemt tidspunkt", - "schedule-custom": "Brugerdefineret", - "schedule-day": { - "monday": "Mandag", - "tuesday": "Tirsdag", - "wednesday": "Onsdag", - "thursday": "Torsdag", - "friday": "Fredag", - "saturday": "Lørdag", - "sunday": "Søndag" - }, - "schedule-days": "Dage", - "schedule-time": "Tid", - "schedule-time-from": "Fra", - "schedule-time-to": "Til", - "schedule-days-of-week-required": "Der skal vælges mindst én ugedag.", - "create-device-profile": "Opret ny enhedsprofil", - "import": "Importér enhedsprofil", - "export": "Eksportér enhedsprofil", - "export-failed-error": "", - "device-profile-file": "Fil for enhedsprofil", - "invalid-device-profile-file-error": "Kan ikke importere enhedsprofil: Ugyldig datastruktur for enhedsprofil." - }, - "dialog": { - "close": "Tæt" - }, - "direction": { - "column": "Kolonne", - "row": "Række" - }, - "error": { - "unable-to-connect": "Kan ikke oprette forbindelse til serveren! Kontrollér din internetforbindelse.", - "unhandled-error-code": "", - "unknown-error": "Ukendt fejl" - }, - "entity": { - "entity": "Entitet", - "entities": "Entiteter", - "entities-count": "Antal entiteter", - "aliases": "Entitetsaliasser", - "entity-alias": "Entitetsalias", - "unable-delete-entity-alias-title": "Entitetsalias kunne ikke slettes", - "unable-delete-entity-alias-text": "", - "duplicate-alias-error": "", - "missing-entity-filter-error": "", - "configure-alias": "", - "alias": "Alias", - "alias-required": "Entitetsalias er påkrævet.", - "remove-alias": "Fjern entitetsalias", - "add-alias": "Tilføj entitetsalias", - "entity-list": "Entitetsliste", - "entity-type": "Entitetstype", - "entity-types": "Entitetstyper", - "entity-type-list": "Liste over entitetstype", - "any-entity": "Enhver entitet", - "enter-entity-type": "Indtast entitetstype", - "no-entities-matching": "", - "no-entity-types-matching": "", - "name-starts-with": "Navnet begynder med", - "use-entity-name-filter": "Anvend filter", - "entity-list-empty": "Ingen entiteter valgt.", - "entity-name-filter-required": "Entitetsnavnfilter er påkrævet.", - "entity-name-filter-no-entity-matched": "", - "all-subtypes": "Alle", - "select-entities": "Vælg entiteter", - "no-aliases-found": "Ingen aliasser fundet.", - "no-alias-matching": "", - "create-new-alias": "Opret en ny!", - "key": "Nøgle", - "key-name": "Nøglenavn", - "no-keys-found": "Ingen nøgler fundet.", - "no-key-matching": "", - "create-new-key": "Opret en ny!", - "type": "Type", - "type-required": "Entitetstype er påkrævet.", - "type-device": "Enhed", - "type-devices": "Enheder", - "list-of-devices": "", - "device-name-starts-with": "", - "type-device-profile": "Enhedsprofil", - "type-device-profiles": "Enhedsprofiler", - "list-of-device-profiles": "", - "device-profile-name-starts-with": "", - "type-asset": "Aktiv", - "type-assets": "Aktiver", - "list-of-assets": "", - "asset-name-starts-with": "", - "type-entity-view": "Entitetsvisning", - "type-entity-views": "Entitetsvisninger", - "list-of-entity-views": "", - "entity-view-name-starts-with": "", - "type-rule": "Regel", - "type-rules": "Regler", - "list-of-rules": "", - "rule-name-starts-with": "", - "type-plugin": "Plugin", - "type-plugins": "Plugins", - "list-of-plugins": "", - "plugin-name-starts-with": "", - "type-tenant": "Lejer", - "type-tenants": "Lejere", - "list-of-tenants": "", - "tenant-name-starts-with": "", - "type-tenant-profile": "Lejerprofil", - "type-tenant-profiles": "Lejerprofiler", - "list-of-tenant-profiles": "", - "tenant-profile-name-starts-with": "", - "type-customer": "Kunde", - "type-customers": "Kunder", - "list-of-customers": "", - "customer-name-starts-with": "", - "type-user": "Bruger", - "type-users": "Brugere", - "list-of-users": "", - "user-name-starts-with": "", - "type-dashboard": "Dashboard", - "type-dashboards": "Dashboards", - "list-of-dashboards": "", - "dashboard-name-starts-with": "", - "type-alarm": "Alarm", - "type-alarms": "Alarmer", - "list-of-alarms": "", - "alarm-name-starts-with": "", - "type-rulechain": "Regelkæde", - "type-rulechains": "Regelkæder", - "list-of-rulechains": "", - "rulechain-name-starts-with": "", - "type-scheduler-event": "Planlægningsbegivenhed", - "type-scheduler-events": "Planlægningsbegivenheder", - "list-of-scheduler-events": "", - "scheduler-event-name-starts-with": "", - "type-blob-entity": "Blob-entitet", - "type-blob-entities": "Blob-entiteter", - "list-of-blob-entities": "", - "blob-entity-name-starts-with": "", - "type-rulenode": "Regelknude", - "type-rulenodes": "Regelknuder", - "list-of-rulenodes": "", - "rulenode-name-starts-with": "", - "type-current-customer": "Nuværende kunde", - "type-current-tenant": "Nuværende lejer", - "type-current-user": "Nuværende bruger", - "type-current-user-owner": "Nuværende brugerejer", - "search": "Søg efter entiteter", - "selected-entities": "", - "entity-name": "Entitetsnavn", - "entity-label": "Entitetsetiket", - "details": "Entitetsoplysninger", - "no-entities-prompt": "Ingen entiteter fundet", - "no-data": "Ingen data at vise", - "columns-to-display": "Kolonner, der skal vises", - "type-api-usage-state": "Api-brugstilstand", - "type-entity-group": "Entitetsgruppe", - "type-converter": "Dataomformer", - "type-converters": "Dataomformere", - "list-of-converters": "", - "converter-name-starts-with": "", - "type-integration": "Integration", - "type-integrations": "Integrationer", - "list-of-integrations": "", - "integration-name-starts-with": "", - "type-role": "Rolle", - "type-roles": "Roller", - "list-of-roles": "", - "role-name-starts-with": "", - "type-group-permission": "Gruppetilladelse" - }, - "entity-group": { - "entity-group": "Entitetsgruppe", - "details": "Oplysninger", - "columns": "Kolonner", - "add-column": "Tilføj kolonne", - "column-value": "Værdi", - "column-value-required": "Kolonneværdi er påkrævet.", - "column-title": "Titel", - "default-sort-order": "Standardsorteringsrækkefølge", - "default-sort-order-required": "Standardsorteringsrækkefølge er påkrævet.", - "hide-in-mobile-view": "Mobil skjult", - "use-cell-style-function": "Brug celletypefunktion", - "use-cell-content-function": "Brug celleindholdsfunktion", - "edit-column": "Rediger kolonne", - "column-details": "Kolonneoplysninger", - "actions": "Handlinger", - "settings": "Indstillinger", - "search": "Søg entitetsgrupper", - "delete": "Slet entitetsgruppe", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "add": "Tilføj entitetsgruppe", - "open-entity-group": "Åbn entitetsgruppe", - "add-entity-group-text": "Tilføj ny entitetsgruppe", - "no-entity-groups-text": "Ingen entitetsgrupper fundet", - "entity-group-details": "Oplysninger om entitetsgruppe", - "selected-entity-groups": "", - "delete-entity-groups": "Slet entitetsgrupper", - "delete-entity-group-title": "", - "delete-entity-group-text": "Vær forsigtig. Efter bekræftelsen vil entitetsgruppen og alle relaterede data være uoprettelige.", - "delete-entity-groups-title": "", - "delete-entity-groups-action-title": "", - "delete-entity-groups-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte entitetsgrupper blive fjernet, og alle relaterede data vil være uoprettelige.", - "device-groups": "Enhedsgrupper", - "asset-groups": "Aktivgrupper", - "customer-groups": "Kundegrupper", - "device-group": "Enhedsgruppe", - "asset-group": "Aktivgruppe", - "user-group": "Brugergruppe", - "user-groups": "Brugergrupper", - "customer-group": "Kundegruppe", - "entity-view-groups": "Entitetsvisningsgrupper", - "entity-view-group": "Entitetsvisningsgruppe", - "dashboard-groups": "Dashboardgrupper", - "dashboard-group": "Dashboardgruppe", - "fetch-more": "Hent mere", - "column-type": { - "column-type": "Kolonnetype", - "client-attribute": "Klientattribut", - "shared-attribute": "Delt attribut", - "server-attribute": "Serverattribut", - "timeseries": "Tidsserier", - "entity-field": "Entitetsfelt" - }, - "column-type-required": "Kolonnetype er påkrævet.", - "entity-field": { - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "type": "Type", - "device_profile": "Enhedsprofil", - "assigned_customer": "Tildelt kunde", - "authority": "Autoritet", - "first_name": "Fornavn", - "last_name": "Efternavn", - "email": "E-mail", - "title": "Titel", - "country": "Land", - "state": "Region", - "city": "By", - "address": "Adresse", - "address2": "Adresse 2", - "zip": "Postnummer", - "phone": "Telefon", - "label": "Mærkning" - }, - "sort-order": { - "asc": "Stigende", - "desc": "Faldende", - "none": "Ingen" - }, - "details-mode": { - "on-row-click": "Ved klik på række", - "on-action-button-click": "Ved klik på knappen Oplysninger", - "disabled": "Deaktiveret" - }, - "change-owner": "Skift ejer", - "select-target-owner": "Vælg målejer", - "no-owners-matching": "", - "target-owner-required": "Målejer er påkrævet.", - "confirm-change-owner-title": "", - "confirm-change-owner-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte enheder blive fjernet fra den aktuelle ejer og blive placeret i gruppen 'Alle' for målejeren.", - "add-to-group": "Tilføj til gruppe", - "move-to-group": "Flyt til gruppe", - "select-entity-group": "Vælg entitetsgruppe", - "no-entity-groups-matching": "", - "target-entity-group-required": "Målentitetsgruppe er påkrævet.", - "select-user-group": "Vælg brugergruppe", - "no-user-groups-matching": "", - "target-user-group-required": "Målbrugergruppe er påkrævet.", - "remove-from-group": "Fjern fra gruppe", - "group-table-title": "Gruppetabeltitel", - "enable-search": "Aktivér entitetssøgning", - "enable-add": "Aktivér tilføjelse af entiteter", - "enable-delete": "Aktivér sletning af entiteter", - "enable-selection": "Aktivér entitetsvalg", - "enable-group-transfer": "Aktivér gruppeoverførselshandlinger", - "display-pagination": "Vis sidenummerering", - "default-page-size": "Standard sidestørrelse", - "enable-assignment-actions": "Aktivér tildelingshandlinger", - "enable-credentials-management": "Aktivér administration af brugeroplysninger", - "enable-login-as-user": "Aktivér login som brugerhandling", - "enable-users-management": "Aktivér administration af brugere", - "enable-customers-management": "Aktivér administration af kunder", - "enable-assets-management": "Aktivér administration af aktiver", - "enable-devices-management": "Aktivér administration af enheder", - "enable-entity-views-management": "Aktivér administration af entitetsvisninger", - "enable-dashboards-management": "Aktivér administration af dashboards", - "open-details-on": "Åbn entitetsoplysninger på", - "select-existing": "Vælg eksisterende entitetsgruppe", - "create-new": "Opret ny entitetsgruppe", - "new-entity-group-name": "Nyt entitetsgruppenavn", - "entity-group-list": "Entitetsgruppeliste", - "entity-group-list-empty": "Ingen entitetsgrupper valgt.", - "name-starts-with": "Entitetsgruppenavn starter med", - "entity-group-name-filter-required": "Entitetsgruppenavnfilter er påkrævet.", - "roles": "Roller", - "permissions": "Tilladelser", - "public": "Offentlig", - "entity-group-public": "Entitetsgruppe er offentlig", - "make-public": "Gør entitetsgruppen offentlig", - "make-private": "Gør entitetsgruppen privat", - "make-public-entity-group-title": "", - "make-public-entity-group-text": "Efter bekræftelsen vil entitetsgruppen og alle dens entiteter blive gjort offentlige og tilgængelige for andre.", - "make-private-entity-group-title": "", - "make-private-entity-group-text": "Efter bekræftelsen vil entitetsgruppen og alle dens entiteter blive gjort private og vil ikke være tilgængelige for andre.", - "share": "Del entitetsgruppe", - "copyId": "Kopiér entitetsgruppe-id", - "idCopiedMessage": "Entitetsgruppe-id er blevet kopieret til udklipsholder", - "entity-group-name": "Entitetsgruppenavn", - "all-users": "Alle brugere" - }, - "entity-field": { - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "type": "Type", - "first-name": "Fornavn", - "last-name": "Efternavn", - "email": "E-mail", - "title": "Titel", - "country": "Land", - "state": "Region", - "city": "By", - "address": "Adresse", - "address2": "Adresse 2", - "zip": "Postnummer", - "phone": "Telefon", - "label": "Mærkning" - }, - "entity-view": { - "entity-view": "Entitetsvisning", - "entity-view-required": "Entitetsvisning er påkrævet.", - "entity-views": "Entitetsvisninger", - "management": "Administration af entitetsvisning", - "view-entity-views": "Vis entitetsvisninger", - "entity-view-alias": "Alias for entitetsvisning", - "aliases": "Aliasser for entitetsvisning", - "no-alias-matching": "", - "no-aliases-found": "Ingen aliasser fundet.", - "no-key-matching": "", - "no-keys-found": "Ingen nøgler fundet.", - "create-new-alias": "Opret en ny!", - "create-new-key": "Opret en ny!", - "duplicate-alias-error": "", - "configure-alias": "", - "no-entity-views-matching": "", - "public": "Offentlig", - "alias": "Alias", - "alias-required": "Alias for entitetsvisning er påkrævet.", - "remove-alias": "Fjern alias for entitetsvisning", - "add-alias": "Tilføj alias for entitetsvisning", - "name-starts-with": "Entitetsvisningsnavn starter med", - "entity-view-list": "Liste over entitetsvisning", - "use-entity-view-name-filter": "Anvend filter", - "entity-view-list-empty": "Ingen entitetsvisninger valgt.", - "entity-view-name-filter-required": "Entitetsvisningsnavnfilter er påkrævet.", - "entity-view-name-filter-no-entity-view-matched": "", - "add": "Tilføj entitetsvisning", - "entity-view-public": "Entitetsvisning er offentlig", - "assign-to-customer": "Tildel til kunde", - "assign-entity-view-to-customer": "Tildel entitetsvisning(er) til kunde", - "assign-entity-view-to-customer-text": "Vælg de entitetsvisninger, der skal tildeles til kunden", - "no-entity-views-text": "Ingen entitetsvisninger fundet", - "assign-to-customer-text": "Vælg den kunde, der skal tildeles entitetsvisning(er)", - "entity-view-details": "Oplysninger for entitetsvisning", - "add-entity-view-text": "Tilføj ny entitetsvisning", - "delete": "Slet entitetsvisning", - "assign-entity-views": "Tildel entitetsvisninger", - "assign-entity-views-text": "", - "delete-entity-views": "Slet entitetsvisninger", - "make-public": "Gør entitetsvisning offentlig", - "make-private": "Gør entitetsvisning privat", - "unassign-from-customer": "Fjern tildeling fra kunde", - "unassign-entity-views": "Fjern tildeling af entitetsvisninger", - "unassign-entity-views-action-title": "", - "assign-new-entity-view": "Tildel ny entitetsvisning", - "delete-entity-view-title": "", - "delete-entity-view-text": "Vær forsigtig. Efter bekræftelsen vil entitetsvisningen og alle relaterede data være uoprettelige.", - "delete-entity-views-title": "", - "delete-entity-views-action-title": "", - "delete-entity-views-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte entitetsvisninger blive fjernet, og alle relaterede data vil være uoprettelige.", - "make-public-entity-view-title": "", - "make-public-entity-view-text": "Efter bekræftelsen vil entitetsvisningen og alle dens data blive gjort offentlige og tilgængelige for andre.", - "make-private-entity-view-title": "", - "make-private-entity-view-text": "Efter bekræftelsen vil entitetsvisningen og alle dens data blive gjort private og vil ikke være tilgængelige for andre.", - "unassign-entity-view-title": "", - "unassign-entity-view-text": "Efter bekræftelsen vil entitetsvisningens tildeling blive fjernet og vil ikke være tilgængelig for kunden.", - "unassign-entity-view": "Fjern tildeling af entitetsvisning", - "unassign-entity-views-title": "", - "unassign-entity-views-text": "Efter bekræftelsen vil tildelingen af alle valgte entitetsvisninger blive fjernet og vil ikke være tilgængelige for kunden.", - "entity-view-type": "Entitetsvisningstype", - "entity-view-type-required": "Entitetsvisningstype er påkrævet.", - "select-entity-view-type": "Vælg entitetsvisningstype", - "enter-entity-view-type": "Indtast entitetsvisningstype", - "any-entity-view": "Enhver entitetsvisning", - "no-entity-view-types-matching": "", - "entity-view-type-list-empty": "Der er ikke valgt nogen entitetsvisningstyper.", - "entity-view-types": "Entitetsvisningstyper", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "events": "Begivenheder", - "details": "Oplysninger", - "copyId": "Kopiér entitetsvisnings-id", - "idCopiedMessage": "Entitetsvisnings-id er blevet kopieret til udklipsholder", - "assignedToCustomer": "Tildelt til kunde", - "unable-entity-view-device-alias-title": "Kan ikke slette entitetsvisningsalias", - "unable-entity-view-device-alias-text": "", - "select-entity-view": "Vælg entitetsvisning", - "start-date": "Startdato", - "start-ts": "Starttid", - "end-date": "Slutdato", - "end-ts": "Sluttid", - "date-limits": "Datofrister", - "client-attributes": "Klientattributter", - "shared-attributes": "Delte attributter", - "server-attributes": "Serverattributter", - "timeseries": "Tidsserier", - "client-attributes-placeholder": "Klientattributter", - "shared-attributes-placeholder": "Delte attributter", - "server-attributes-placeholder": "Serverattributter", - "timeseries-placeholder": "Tidsserier", - "target-entity": "Målentitet", - "attributes-propagation": "Overførsel af attributter", - "attributes-propagation-hint": "Entitetsvisning vil automatisk kopiere specificerede attributter fra målentitet, hver gang du gemmer eller opdaterer denne entitetsvisning. Af ydelsesmæssige årsager overføres målenhedsattributter ikke til entitetsvisning ved hver attributændring. Du kan aktivere automatisk overførsel ved at konfigurere regelknuden \"Kopiér til visning\" i din regelkæde og knytte meddelelserne \"Postattributter\" og \"Attributter opdateret\" til den nye regelknude.", - "timeseries-data": "Tidsseriedata", - "timeseries-data-hint": "Konfigurer tidsseriedatanøgler for målentiteten, der vil være tilgængelige for entitetsvisningen. Disse tidsseriedata er skrivebeskyttede.", - "selected-entity-views": "", - "search": "Søg efter entitetsvisninger", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte entitetsvisninger", - "select-group-to-move": "Vælg målgruppe for at flytte valgte entitetsvisninger", - "remove-entity-views-from-group": "", - "group": "Gruppe af entitetsvisninger", - "list-of-groups": "", - "group-name-starts-with": "" - }, - "event": { - "events": "Begivenheder", - "event-type": "Begivenhedstype", - "type-error": "Fejl", - "type-lc-event": "Livscyklusbegivenhed", - "type-rw-event": "Rådata", - "type-stats": "Statistikker", - "type-debug-converter": "Debug", - "type-debug-integration": "Debug", - "type-debug-rule-node": "Debug", - "type-debug-rule-chain": "Debug", - "no-events-prompt": "Ingen begivenheder fundet", - "error": "Fejl", - "alarm": "Alarm", - "event-time": "Begivenhedstidspunkt", - "server": "Server", - "body": "Tekst", - "method": "Metode", - "type": "Type", - "in": "Ind", - "out": "Ud", - "metadata": "Metadata", - "message": "Meddelelse", - "entity": "Entitet", - "message-id": "Meddelelses-id", - "message-type": "Meddelelsestype", - "data-type": "Datatype", - "relation-type": "Relationstype", - "data": "Data", - "event": "Begivenhed", - "status": "Status", - "success": "Succes", - "failed": "Mislykket", - "messages-processed": "Behandlede meddelelser", - "errors-occurred": "Der opstod fejl", - "uuid": "UUID" - }, - "extension": { - "extensions": "Udvidelser", - "selected-extensions": "", - "type": "Type", - "key": "Nøgle", - "value": "Værdi", - "id": "Id", - "extension-id": "Udvidelses-id", - "extension-type": "Udvidelsestype", - "transformer-json": "JSON *", - "unique-id-required": "Aktuelt udvidelses-id findes allerede.", - "delete": "Slet udvidelse", - "add": "Tilføj udvidelse", - "edit": "Rediger udvidelse", - "view": "Vis udvidelse", - "delete-extension-title": "", - "delete-extension-text": "Vær forsigtig. Efter bekræftelsen vil udvidelsen og alle relaterede data være uoprettelige.", - "delete-extensions-title": "", - "delete-extensions-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte udvidelser blive fjernet.", - "converters": "Omformere", - "converter-id": "Omformer-id", - "configuration": "Konfiguration", - "converter-configurations": "Omformerkonfigurationer", - "token": "Sikkerhedstoken", - "add-converter": "Tilføj omformer", - "add-config": "Tilføj omformerkonfiguration", - "device-name-expression": "Enhedsnavnudtryk", - "device-type-expression": "Enhedstypeudtryk", - "custom": "Brugerdefineret", - "to-double": "Til dobbelt", - "transformer": "Transformer", - "json-required": "Transformer-json er påkrævet.", - "json-parse": "Kan ikke parse transformer-json.", - "attributes": "Attributter", - "add-attribute": "Tilføj attribut", - "add-map": "Tilføj tilknytningselement", - "timeseries": "Tidsserier", - "add-timeseries": "Tilføj tidsserier", - "field-required": "Felt er påkrævet", - "brokers": "Mæglere", - "add-broker": "Tilføj mægler", - "host": "Vært", - "port": "Port", - "port-range": "Porten skal ligge i området fra 1 til 65535.", - "ssl": "Ssl", - "credentials": "Brugeroplysninger", - "username": "Brugernavn", - "password": "Adgangskode", - "retry-interval": "Forsøg interval igen i millisekunder", - "sas": "Signatur for delt adgang", - "anonymous": "Anonym", - "basic": "Basis", - "pem": "PEM", - "ca-cert": "CA-certifikatfil *", - "private-key": "Privat nøglefil *", - "cert": "Certifikatfil *", - "no-file": "Ingen fil valgt.", - "drop-file": "Træk og slip en fil, eller klik for at vælge en fil, der skal uploades.", - "mapping": "Tilknytning", - "topic-filter": "Emnefilter", - "converter-type": "Omformertype", - "converter-json": "Json", - "json-name-expression": "Udtryk for enhedsnavn json", - "topic-name-expression": "Udtryk for enhedsnavnemne", - "json-type-expression": "Udtryk for enhedstype json", - "topic-type-expression": "Udtryk for enhedstypeemne", - "attribute-key-expression": "Udtryk for attributnøgle", - "attr-json-key-expression": "Udtryk for attributnøgle json", - "attr-topic-key-expression": "Udtryk for attributnøgleemne", - "request-id-expression": "Udtryk for anmodnings-id", - "request-id-json-expression": "Udtryk for anmodnings-id json", - "request-id-topic-expression": "Udtryk for anmodnings-id emne", - "response-topic-expression": "Udtryk for svaremne", - "value-expression": "Udtryk for værdi", - "topic": "Emne", - "timeout": "Timeout i millisekunder", - "converter-json-required": "Omformer-json er påkrævet.", - "converter-json-parse": "Omformeren json kunne ikke parses.", - "filter-expression": "Udtryk for filter", - "connect-requests": "Tilslutningsanmodninger", - "add-connect-request": "Tilføj tilslutningsanmodning", - "disconnect-requests": "Afbryd anmodninger", - "add-disconnect-request": "Tilføj afbrydelsesanmodning", - "attribute-requests": "Attributanmodninger", - "add-attribute-request": "Tilføj attributanmodning", - "attribute-updates": "Attributopdateringer", - "add-attribute-update": "Tilføj attributopdatering", - "server-side-rpc": "Serverside RPC", - "add-server-side-rpc-request": "Tilføj serverside RPC-anmodning", - "device-name-filter": "Enhedsnavnfilter", - "attribute-filter": "Attributfilter", - "method-filter": "Metodefilter", - "request-topic-expression": "Anmod om udtryk for emne", - "response-timeout": "Svartimeout i millisekunder", - "topic-expression": "Udtryk for emne", - "client-scope": "Klientomfang", - "add-device": "Tilføj enhed", - "opc-server": "Servere", - "opc-add-server": "Tilføj server", - "opc-add-server-prompt": "Tilføj venligst server", - "opc-application-name": "Applikationsnavn", - "opc-application-uri": "Applikation uri", - "opc-scan-period-in-seconds": "Scanningsperiode i sekunder", - "opc-security": "Sikkerhed", - "opc-identity": "Identitet", - "opc-keystore": "Keystore", - "opc-type": "Type", - "opc-keystore-type": "Type", - "opc-keystore-location": "Placering *", - "opc-keystore-password": "Adgangskode", - "opc-keystore-alias": "Alias", - "opc-keystore-key-password": "Nøgleadgangskode", - "opc-device-node-pattern": "Mønster for enhedsknude", - "opc-device-name-pattern": "Mønster for enhedsnavn", - "modbus-server": "Servere/slaver", - "modbus-add-server": "Tilføj server/slave", - "modbus-add-server-prompt": "Tilføj venligst server/slave", - "modbus-transport": "Transport", - "modbus-tcp-reconnect": "Automatisk gentilslutning", - "modbus-rtu-over-tcp": "RTU over TCP", - "modbus-port-name": "Serielt portnavn", - "modbus-encoding": "Kodning", - "modbus-parity": "Paritet", - "modbus-baudrate": "Baudrate", - "modbus-databits": "Databits", - "modbus-stopbits": "Stopbits", - "modbus-databits-range": "Databits skal ligge i området fra 7 til 8.", - "modbus-stopbits-range": "Stopbits skal ligge i området fra 1 til 2.", - "modbus-unit-id": "Enheds-id", - "modbus-unit-id-range": "Enheds-id skal ligge i området fra 1 til 247.", - "modbus-device-name": "Enhedsnavn", - "modbus-poll-period": "Undersøgelsesperiode (ms)", - "modbus-attributes-poll-period": "Attributundersøgelsesperiode (ms)", - "modbus-timeseries-poll-period": "Tidsserieundersøgelsesperiode (ms)", - "modbus-poll-period-range": "Undersøgelsesperioden skal have positiv værdi.", - "modbus-tag": "Tag", - "modbus-function": "Funktion", - "modbus-register-address": "Registeradresse", - "modbus-register-address-range": "Registeradresse skal ligge i området fra 0 til 65535.", - "modbus-register-bit-index": "Bitindeks", - "modbus-register-bit-index-range": "Bitindeks skal ligge i området fra 0 til 15.", - "modbus-register-count": "Registeroptælling", - "modbus-register-count-range": "Registeroptælling skal være en positiv værdi.", - "modbus-byte-order": "Byterækkefølge", - "sync": { - "status": "Status", - "sync": "Synkroniseret", - "not-sync": "Ikke synkroniseret", - "last-sync-time": "Sidste synkroniseringstidspunkt", - "not-available": "Ikke til rådighed" - }, - "export-extensions-configuration": "Konfiguration af eksportér udvidelser", - "import-extensions-configuration": "Konfiguration af importér udvidelser", - "import-extensions": "Importér udvidelser", - "import-extension": "Importér udvidelse", - "export-extension": "Eksportér udvidelse", - "file": "Fil for udvidelser", - "invalid-file-error": "Ugyldig udvidelsesfil", - "text": "TEKST", - "json": "JSON", - "binary": "BINÆR", - "hex": "HEX" - }, - "filter": { - "add": "Tilføj filter", - "edit": "Rediger filter", - "name": "Filternavn", - "name-required": "Filternavn er påkrævet.", - "duplicate-filter": "Filter med samme navn findes allerede.", - "filters": "Filtre", - "unable-delete-filter-title": "Filteret kunne ikke slettes", - "unable-delete-filter-text": "", - "duplicate-filter-error": "", - "missing-key-filters-error": "", - "filter": "Filter", - "editable": "Redigerbar", - "no-filters-found": "Ingen filtre fundet.", - "no-filter-text": "Intet filter angivet", - "add-filter-prompt": "Tilføj venligst filter", - "no-filter-matching": "", - "create-new-filter": "Opret en ny!", - "filter-required": "Filter er påkrævet.", - "operation": { - "operation": "Driftsopgave", - "equal": "lig med", - "not-equal": "ikke lig med", - "starts-with": "starter med", - "ends-with": "slutter med", - "contains": "indeholder", - "not-contains": "indeholder ikke", - "greater": "større end", - "less": "mindre end", - "greater-or-equal": "større end eller lig med", - "less-or-equal": "mindre end eller lig med", - "and": "og", - "or": "eller" - }, - "ignore-case": "ignorer versalfølsomhed", - "value": "Værdi", - "remove-filter": "Fjern filter", - "preview": "Visning af filter", - "no-filters": "Ingen filtre konfigureret", - "add-filter": "Tilføj filter", - "add-complex-filter": "Tilføj komplekst filter", - "add-complex": "Tilføj kompleks", - "complex-filter": "Komplekst filter", - "edit-complex-filter": "Rediger komplekst filter", - "edit-filter-user-params": "Rediger filterprædikat for brugerparametre", - "filter-user-params": "Filtrerprædikat for brugerparametre", - "user-parameters": "Brugerparametre", - "display-label": "Etiket, der skal vises", - "order-priority": "Prioritet af feltrækkefølge", - "key-filter": "Nøglefilter", - "key-filters": "Nøglefiltre", - "key-name": "Nøglenavn", - "key-name-required": "Nøglenavn er påkrævet.", - "key-type": { - "key-type": "Nøgletype", - "attribute": "Attribut", - "timeseries": "Tidsserier", - "entity-field": "Entitetsfelt", - "constant": "Konstant" - }, - "value-type": { - "value-type": "Værditype", - "string": "Streng", - "numeric": "Numerisk", - "boolean": "Boolesk", - "date-time": "Datotid" - }, - "value-type-required": "Nøgleværditype er påkrævet.", - "key-value-type-change-title": "Er du sikker på, at du vil ændre nøgleværditypen?", - "key-value-type-change-message": "Hvis du bekræfter ny værditype, fjernes alle indtastede nøglefiltre.", - "no-key-filters": "Ingen nøglefiltre konfigureret", - "add-key-filter": "Tilføj nøglefilter", - "remove-key-filter": "Fjern nøglefilter", - "edit-key-filter": "Rediger nøglefilter", - "date": "Dato", - "time": "Tid", - "current-tenant": "Nuværende lejer", - "current-customer": "Nuværende kunde", - "current-user": "Nuværende bruger", - "current-device": "Nuværende enhed", - "default-value": "Standardværdi", - "dynamic-source-type": "Dynamisk kildetype", - "no-dynamic-value": "Ingen dynamisk værdi", - "source-attribute": "Kildeattribut", - "switch-to-dynamic-value": "Skift til dynamisk værdi", - "switch-to-default-value": "Skift til standardværdi", - "inherit-owner": "Nedarv fra ejer", - "source-attribute-not-set": "Hvis kildeattributten ikke er angivet" - }, - "fullscreen": { - "expand": "Udvid til fuld skærm", - "exit": "Afslut fuldskærm", - "toggle": "Skift mellem fuldskærmstilstand", - "fullscreen": "Fuld skærm" - }, - "function": { - "function": "Funktion" - }, - "gateway": { - "create-new-gateway": "Opret en ny gateway", - "create-new-gateway-text": "", - "gateway-exists": "Enhed med samme navn findes allerede.", - "gateway-name": "Gateway-navn", - "gateway-name-required": "Gateway-navn er påkrævet.", - "gateway-saved": "Gateway-konfigurationen blev gemt.", - "no-gateway-found": "Ingen gateway fundet.", - "no-gateway-matching": "" - }, - "grid": { - "delete-item-title": "Er du sikker på, du ønsker at slette dette element?", - "delete-item-text": "Vær forsigtig. Efter bekræftelsen vil dette element og alle relaterede data være uoprettelige.", - "delete-items-title": "", - "delete-items-action-title": "", - "delete-items-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte elementer blive fjernet, og alle relaterede data vil være uoprettelige.", - "add-item-text": "Tilføj nyt element", - "no-items-text": "Ingen elementer fundet", - "item-details": "Oplysninger om element", - "delete-item": "Slet element", - "delete-items": "Slet elementer", - "scroll-to-top": "Gå til toppen" - }, - "help": { - "goto-help-page": "Gå til hjælpesiden" - }, - "home": { - "home": "Hjem", - "profile": "Profil", - "logout": "Log ud", - "menu": "Menu", - "avatar": "Avatar", - "open-user-menu": "Åbn brugermenu" - }, - "import": { - "no-file": "Ingen fil valgt", - "drop-file": "Træk og slip en JSON-fil, eller klik for at vælge en fil, der skal uploades.", - "drop-csv-file": "Træk og slip en CSV-fil, eller klik for at vælge en fil, der skal uploades.", - "drop-file-csv": "Træk og slip en CSV-fil, eller klik for at vælge en fil, der skal uploades.", - "column-value": "Værdi", - "column-title": "Titel", - "column-example": "Eksempel på værdidata", - "column-key": "Attribut-/telemetrinøgle", - "csv-delimiter": "CSV-skilletegn", - "csv-first-line-header": "Første linje indeholder kolonnenavne", - "csv-update-data": "Opdater attributter/telemetri", - "import-csv-number-columns-error": "En fil skal indeholde mindst to kolonner", - "import-csv-invalid-format-error": "", - "column-type": { - "name": "Navn", - "type": "Type", - "label": "Mærkning", - "column-type": "Kolonnetype", - "client-attribute": "Klientattribut", - "shared-attribute": "Delt attribut", - "server-attribute": "Serverattribut", - "timeseries": "Tidsserier", - "entity-field": "Entitetsfelt", - "access-token": "Adgangstoken", - "isgateway": "Er gateway", - "activity-time-from-gateway-device": "Aktivitetstid fra gateway-enhed", - "description": "Beskrivelse" - }, - "stepper-text": { - "select-file": "Vælg en fil", - "configuration": "Importér konfiguration", - "column-type": "Vælg kolonnetype", - "creat-entities": "Oprettelse af nye entiteter", - "done": "Udført" - }, - "message": { - "create-entities": "", - "update-entities": "", - "error-entities": "" + "api-usage" : { + "api-features" : "API-funktioner", + "api-usage" : "API-forbrug", + "alarm" : "Alarm", + "alarms-created" : "Oprettede alarmer", + "queue-stats" : "Køstatistik", + "processing-failures-and-timeouts" : "Behandlingsfejl og timeouts", + "exceptions" : "Undtagelser", + "alarms-created-daily-activity" : "Daglig aktivitet for oprettede alarmer", + "alarms-created-hourly-activity" : "Timebaseret aktivitet for oprettede alarmer", + "alarms-created-monthly-activity" : "Månedlig aktivitet for oprettede alarmer", + "data-points" : "Datapunkter", + "data-points-storage-days" : "Opbevaringsdage for datapunkter", + "device-api" : "Device API", + "email" : "E-mail", + "email-messages" : "E-mailbeskeder", + "email-messages-daily-activity" : "Daglig aktivitet for e-mailbeskeder", + "email-messages-monthly-activity" : "Månedlig aktivitet for e-mailbeskeder", + "executions" : "Udførelser", + "scripts" : "Scripts", + "scripts-hourly-activity" : "Timebaseret aktivitet for scripts", + "scripts-daily-activity" : "Daglig aktivitet for scripts", + "scripts-monthly-activity" : "Månedlig aktivitet for scripts", + "javascript" : "JavaScript", + "javascript-executions" : "JavaScript-udførelser", + "tbel" : "TBEL", + "tbel-executions" : "TBEL-udførelser", + "latest-error" : "Seneste fejl", + "messages" : "Beskeder", + "notifications" : "Notifikationer", + "notifications-email-sms" : "Notifikationer (E-mail/SMS)", + "notifications-hourly-activity" : "Timebaseret aktivitet for notifikationer", + "permanent-failures" : "${entityName} permanente fejl", + "permanent-timeouts" : "${entityName} permanente timeouts", + "processing-failures" : "${entityName} behandlingsfejl", + "processing-timeouts" : "${entityName} behandlingstimeouts", + "rule-chain" : "Rule Chain", + "rule-engine" : "Rule Engine", + "rule-engine-daily-activity" : "Daglig aktivitet for Rule Engine", + "rule-engine-executions" : "Rule Engine-udførelser", + "rule-engine-hourly-activity" : "Timebaseret aktivitet for Rule Engine", + "rule-engine-monthly-activity" : "Månedlig aktivitet for Rule Engine", + "rule-engine-statistics" : "Statistik for Rule Engine", + "rule-node" : "Rule Node", + "sms" : "SMS", + "sms-messages" : "SMS-beskeder", + "sms-messages-daily-activity" : "Daglig aktivitet for SMS-beskeder", + "sms-messages-monthly-activity" : "Månedlig aktivitet for SMS-beskeder", + "successful" : "${entityName} succesfuld", + "telemetry" : "Telemetri", + "telemetry-persistence" : "Telemetri-persistens", + "telemetry-persistence-daily-activity" : "Daglig aktivitet for telemetri-persistens", + "telemetry-persistence-hourly-activity" : "Timebaseret aktivitet for telemetri-persistens", + "telemetry-persistence-monthly-activity" : "Månedlig aktivitet for telemetri-persistens", + "transport" : "Transport", + "transport-daily-activity" : "Daglig transportaktivitet", + "transport-data-points" : "Transport-datapunkter", + "transport-hourly-activity" : "Timebaseret transportaktivitet", + "transport-messages" : "Transportbeskeder", + "transport-monthly-activity" : "Månedlig transportaktivitet", + "view-details" : "Vis detaljer", + "view-statistics" : "Vis statistik" + }, + "api-limit" : { + "cassandra-queries" : "Cassandra-forespørgsler", + "entity-version-creation" : "Oprettelse af enhedsversion", + "entity-version-load" : "Indlæsning af enhedsversion", + "notification-requests" : "Notifikationsanmodninger", + "notification-requests-per-rule" : "Notifikationsanmodninger pr. regel", + "rest-api-requests" : "REST API-anmodninger", + "rest-api-requests-per-customer" : "REST API-anmodninger pr. kunde", + "transport-messages" : "Transportbeskeder", + "transport-messages-per-device" : "Transportbeskeder pr. device", + "transport-messages-per-gateway" : "Transportbeskeder pr. gateway", + "transport-messages-per-gateway-device" : "Transportbeskeder pr. gateway-enhed", + "ws-updates-per-session" : "WS-opdateringer pr. session", + "edge-events" : "Edge-hændelser", + "edge-events-per-edge" : "Edge-hændelser pr. edge", + "edge-uplink-messages" : "Edge-uplinkbeskeder", + "edge-uplink-messages-per-edge" : "Edge-uplinkbeskeder pr. edge" + }, + "audit-log" : { + "audit" : "Revision", + "audit-logs" : "Revisionslogge", + "timestamp" : "Tidsstempel", + "entity-type" : "Enhedstype", + "entity-name" : "Enhedsnavn", + "user" : "Bruger", + "type" : "Type", + "status" : "Status", + "details" : "Detaljer", + "type-added" : "Tilføjet", + "type-deleted" : "Slettet", + "type-updated" : "Opdateret", + "type-attributes-updated" : "Attributter opdateret", + "type-attributes-deleted" : "Attributter slettet", + "type-rpc-call" : "RPC-kald", + "type-credentials-updated" : "Oplysninger opdateret", + "type-assigned-to-customer" : "Tildelt til kunde", + "type-unassigned-from-customer" : "Fratildelt fra kunde", + "type-assigned-to-edge" : "Tildelt til edge", + "type-unassigned-from-edge" : "Fratildelt fra edge", + "type-activated" : "Aktiveret", + "type-suspended" : "Suspenderet", + "type-credentials-read" : "Oplysninger læst", + "type-attributes-read" : "Attributter læst", + "type-relation-add-or-update" : "Relation opdateret", + "type-relation-delete" : "Relation slettet", + "type-relations-delete" : "Alle relationer slettet", + "type-alarm-ack" : "Alarm anerkendt", + "type-alarm-clear" : "Alarm ryddet", + "type-alarm-delete" : "Alarm slettet", + "type-alarm-assign" : "Alarm tildelt", + "type-alarm-unassign" : "Alarm fratildelt", + "type-added-comment" : "Kommentar tilføjet", + "type-updated-comment" : "Kommentar opdateret", + "type-deleted-comment" : "Kommentar slettet", + "type-login" : "Login", + "type-logout" : "Logout", + "type-lockout" : "Låst ude", + "status-success" : "Succes", + "status-failure" : "Fejl", + "audit-log-details" : "Detaljer for revisionslog", + "no-audit-logs-prompt" : "Ingen logge fundet", + "action-data" : "Handlingsdata", + "failure-details" : "Fejldetaljer", + "search" : "Søg i revisionslogge", + "clear-search" : "Ryd søgning", + "type-assigned-from-tenant" : "Tildelt fra lejer", + "type-assigned-to-tenant" : "Tildelt til lejer", + "type-provision-success" : "Device klargjort", + "type-provision-failure" : "Klargøring af device mislykkedes", + "type-timeseries-updated" : "Telemetri opdateret", + "type-timeseries-deleted" : "Telemetri slettet", + "type-sms-sent" : "SMS sendt" + }, + "debug-settings" : { + "label" : "Fejlsøgningskonfiguration", + "on-failure" : "Kun fejl (24/7)", + "all-messages" : "Alle beskeder ({{time}})", + "failures" : "Fejl", + "entity" : "enhed", + "hint" : { + "main-limited" : "Alle {{entity}} fejlsøgningsbeskeder vil blive hastighedsbegrænset, med maksimalt {{msg}} beskeder tilladt pr. {{time}}.", + "on-failure" : "Gem alle fejlsøgningshændelser uden tidsbegrænsning.", + "all-messages" : "Gem alle fejlsøgningshændelser i en tidsbegrænset periode." } }, - "integration": { - "integration": "Integration", - "integrations": "Integrationer", - "select-integration": "Vælg integration", - "no-integrations-matching": "", - "integration-required": "Integration er påkrævet", - "delete": "Slet integration", - "management": "Integrationsstyring", - "add-integration-text": "Tilføj ny integration", - "no-integrations-text": "Ingen integrationer fundet", - "selected-integrations": "", - "delete-integration-title": "", - "delete-integration-text": "Vær forsigtig. Efter bekræftelsen vil integrationen og alle relaterede data være uoprettelige.", - "delete-integrations-title": "", - "delete-integrations-action-title": "", - "delete-integrations-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte integrationer blive fjernet, og alle relaterede data vil være uoprettelige.", - "events": "Begivenheder", - "enabled": "Aktiveret", - "allow-create-devices-or-assets": "Tillad oprettelse af enheder eller aktiver", - "add": "Tilføj integration", - "search": "Søg efter integrationer", - "integration-details": "Integrationsoplysninger", - "details": "Oplysninger", - "copyId": "Kopiér integrations-id", - "idCopiedMessage": "Integrations-id er blevet kopieret til udklipsholder", - "debug-mode": "Debug-tilstand", - "enable-security": "Aktivér sikkerhed", - "enable-security-new": "Aktivér sikkerhed for automatiske tokenopdateringer", - "headers-filter": "Filter for overskrifter", - "header": "Overskrift", - "no-headers-filter": "Intet filter for overskrifter", - "downlink-url": "Downlink URL", - "downlink-url-required": "Downlink URL er påkrævet", - "create-loriot-output": "Opret Loriot-applikationsoutput", - "send-downlink": "Send downlink", - "server": "Server", - "server-required": "Server er påkrævet", - "app-id": "Applikations-id", - "app-id-required": "Applikations-id er påkrævet", - "app-token": "Applikationsadgangstoken", - "app-token-required": "Applikationsadgangstoken er påkrævet", - "email": "E-mail", - "email-required": "E-mail er påkrævet", - "application-uri": "Applikation URI", - "as-id": "AS-id", - "as-id-required": "AS-id er påkrævet.", - "as-key": "AS-nøgle", - "as-key-required": "AS-nøgle er påkrævet.", - "client-id-new": "Klient-id", - "client-id-new-required": "Klient-id (login) er påkrævet (login).", - "client-secret": "Klient hemmelig", - "client-secret-required": "Klient hemmelig (adgangskode) er påkrævet (adgangskode).", - "max-time-diff-in-seconds": "Maksimal tidsforskel (sekunder)", - "max-time-diff-in-seconds-required": "Maksimal tidsforskel er påkrævet.", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "base-url": "Basis-URL", - "base-url-required": "Basis-URL er påkrævet", - "security-key": "Sikkerhedsnøgle", - "http-endpoint": "HTTP-slutpunkts-URL", - "replace-no-content-to-ok": "Erstat svarstatus fra 'Intet indhold' til 'OK'", - "copy-http-endpoint-url": "Kopiér HTTP-slutpunkts-URL", - "http-endpoint-url-copied-message": "HTTP-slutpunkts-URL er blevet kopieret til udklipsholder", - "host": "Vært", - "host-required": "Vært er påkrævet.", - "host-type": "Værtstype", - "host-type-required": "Værtstype er påkrævet.", - "custom-host": "Brugerdefineret vært", - "custom-host-required": "Brugerdefineret vært er påkrævet.", - "port": "Port", - "port-required": "Port er påkrævet.", - "port-range": "Porten skal ligge i området fra 1 til 65535.", - "connect-timeout": "Forbindelsestimeout (sek.)", - "connect-timeout-required": "Forbindelsestimeout er påkrævet.", - "connect-timeout-range": "Forbindelsestimeout skal ligge i området fra 1 til 200.", - "client-id": "Klient-id", - "client-id-required": "Klient-id er påkrævet.", - "client-id-range": "Klient-id skal ligge i et interval fra 1 til 23 tegn. [MQTT-3.1.3-5]", - "client-id-pattern": "Klient-id skal bestå af tal og store og små bogstaver. [MQTT-3.1.3-5]", - "max-bytes-in-message": "Maksimum bytes i meddelelse", - "max-bytes-in-message-range": "Maksimum bytes i meddelelsen skal ligge i området fra 1 til 256000000.", - "device-id": "Enheds-id", - "device-id-required": "Enheds-id er påkrævet.", - "device-id-range": "Enheds-id'et skal ligge i området fra 1 til 65535 tegn.", - "device-id-pattern": "Enheds-id'et skal bestå af tal og store og små bogstaver. [MQTT-3.1.3-5]", - "group-id": "Gruppe-id", - "group-id-required": "Gruppe-id er påkrævet.", - "topics": "Emner", - "topics-required": "Emne er påkrævet.", - "routing-keys": "Routing-nøgler", - "routing-keys-required": "Routing-nøgle er påkrævet.", - "queues": "Køer", - "queues-required": "Kønavn er påkrævet.", - "exchange-name": "Udskiftningsnavn", - "exchange-name-required": "Udskiftningsnavn er påkrævet.", - "downlink-topic": "Downlink-emne", - "connection-timeout": "Forbindelsestimeout, ms", - "connection-timeout-min": "Ugyldig forbindelsestimeoutværdi.", - "handshake-timeout": "Handshake-timeout, ms", - "handshake-timeout-min": "Ugyldig handshake-timeoutværdi.", - "virtual-host": "Virtuel vært", - "rabbit-mq-poll-interval": "Undersøgelsesinterval, ms", - "rabbit-mq-poll-interval-min": "Ugyldig undersøgelsesintervalværdi.", - "application-server-url": "Applikationsserver URL", - "application-server-api-token": "Applikationsserver API-token", - "application-server-api-token-required": "Applikationsserver API-token er påkrævet.", - "bootstrap-servers": "Bootstrap-servere", - "bootstrap-servers-required": "Bootstrap-servere er påkrævet.", - "poll-interval": "Undersøgelsesinterval", - "poll-interval-required": "Undersøgelsesinterval er påkrævet.", - "auto-create-topics": "Opret emner automatisk", - "clean-session": "Ryd session", - "enable-ssl": "Aktivér SSL", - "credentials": "Brugeroplysninger", - "credentials-type": "Type af brugeroplysninger", - "credentials-type-required": "Type af brugeroplysninger er påkrævet.", - "username": "Brugernavn", - "username-required": "Brugernavn er påkrævet.", - "password": "Adgangskode", - "password-required": "Brugernavn er påkrævet.", - "azure-ca-cert": "CA-certifikatfil", - "ca-cert": "CA-certifikatfil *", - "private-key": "Privat nøglefil *", - "private-key-password": "Adgangskode til privat nøgle", - "cert": "Certifikatfil *", - "no-file": "Ingen fil valgt.", - "drop-file": "Træk og slip en fil, eller klik for at vælge en fil, der skal uploades.", - "check-connection": "Kontrollér forbindelsen", - "check-success": "Forbindelse oprettet.", - "topic-filters": "Emnefiltre", - "remove-topic-filter": "Fjern emnefilter", - "add-topic-filter": "Tilføj emnefilter", - "add-topic-filter-prompt": "Tilføj venligst emnefilter", - "topic": "Emne", - "mqtt-qos": "QoS", - "mqtt-qos-at-most-once": "Højst én gang", - "mqtt-qos-at-least-once": "Mindst én gang", - "mqtt-qos-exactly-once": "Præcis én gang", - "downlink-topic-pattern": "Mønster for downlink-emne", - "downlink-topic-pattern-required": "Mønster for downlink-emne er påkrævet.", - "aws-access-key-id": "AWS-adgangsnøgle-id", - "aws-secret-access-key": "AWS hemmelig adgangsnøgle", - "aws-region": "Region", - "aws-iot-endpoint": "AWS IoT-slutpunkt", - "aws-iot-endpoint-required": "AWS IoT-slutpunkt er påkrævet.", - "aws-iot-credentials": "AWS IoT-brugeroplysninger", - "aws-sqs-polling-period-in-seconds": "Undersøgelsesperiode i sekunder", - "aws-sqs-queue-url": "SQS Kø URL", - "aws-sqs-queue-url-required": "SQS Kø URL er påkrævet", - "aws-sqs-access-key-id-required": "Adgangsnøgle-id er påkrævet", - "aws-sqs-secret-access-key-required": "Hemmelig adgangsnøgle er påkrævet", - "application-credentials": "Applikationsbrugeroplysninger", - "api-key": "API-nøgle", - "api-key-required": "API-nøgle er påkrævet.", - "api-key-format": "Ugyldigt API-nøgleformat.", - "auth-token": "Godkendelsestoken", - "auth-token-required": "Godkendelsestoken er påkrævet.", - "region": "Region", - "region-required": "Region er påkrævet.", - "application-id": "Applikations-id", - "application-id-required": "Applikations-id er påkrævet.", - "access-key": "Adgangsnøgle", - "access-key-required": "Adgangsnøgle er påkrævet.", - "connection-parameters": "Tilslutningsparametre", - "service-bus-namespace-name": "Namespace-navn for servicebus", - "service-bus-namespace-name-required": "Namespace-navn for servicebus er påkrævet.", - "connection-string": "Tilslutningsstreng", - "connection-string-required": "Tilslutningsstreng påkrævet!", - "event-hub-name": "Navn på begivenhedshub", - "event-hub-name-required": "Navn på begivenhedshub er påkrævet.", - "event-iot-hub-name-required": "Iot-hubnavn er påkrævet for downlink", - "sas-key-name": "SAS-nøglenavn", - "sas-key-name-required": "SAS-nøglenavn er påkrævet.", - "sas-key": "SAS-nøgle", - "sas-key-required": "SAS-nøgle er påkrævet.", - "iot-hub-name": "IoT-hubnavn (påkrævet for downlink)", - "hostname": "Værtsnavn", - "hostname-required": "Værtsnavn er påkrævet", - "integration-clazz": "Integrationsklasse", - "integration-clazz-required": "Integrationsklasse er påkrævet", - "integration-configuration": "Konfiguration af integration JSON", - "metadata": "Metadata", - "type": "Type", - "type-required": "Type er påkrævet.", - "uplink-converter": "Uplink-dataomformer", - "uplink-converter-required": "Uplink-dataomformer er påkrævet.", - "downlink-converter": "Downlink-dataomformer", - "type-http": "HTTP", - "type-ocean-connect": "OceanConnect", - "type-sigfox": "SigFox", - "type-thingpark": "ThingPark", - "type-loriot": "Loriot", - "type-thingpark-enterprise": "ThingParkEnterprise", - "type-tmobile-iot-cdp": "T-Mobile – IoT CDP", - "type-mqtt": "MQTT", - "type-aws-iot": "AWS IoT", - "type-aws-sqs": "AWS SQS", - "type-aws-kinesis": "AWS-kinese", - "type-ibm-watson-iot": "IBM Watson IoT", - "type-ttn": "TheThingsNetwork", - "type-tti": "The Things Stack", - "type-chirpstack": "ChirpStack", - "type-azure-event-hub": "Azure-begivenhedshub", - "type-azure-iot-hub": "Azure IoT-hub", - "type-opc-ua": "OPC-UA", - "type-custom": "Brugerdefineret", - "type-udp": "UDP", - "type-tcp": "TCP", - "type-kafka": "Kafka", - "type-rabbitmq": "RabbitMQ", - "type-pubsub": "Pub/Sub", - "opc-ua-application-name": "Applikationsnavn", - "opc-ua-application-uri": "Applikation uri", - "opc-ua-scan-period-in-seconds": "Scanningsperiode i sekunder", - "opc-ua-scan-period-in-seconds-required": "Scanningsperiode er påkrævet", - "opc-ua-timeout": "Timeout i millisekunder", - "opc-ua-timeout-required": "Timeout er påkrævet", - "opc-ua-security": "Sikkerhed", - "opc-ua-security-required": "Sikkerhed er påkrævet", - "opc-ua-identity": "Identitet", - "opc-ua-identity-required": "Identitet er påkrævet", - "opc-ua-keystore": "Keystore", - "add-opc-ua-keystore-prompt": "Tilføj venligst keystore-fil", - "opc-ua-keystore-required": "Keystore er påkrævet", - "opc-ua-type": "Type", - "opc-ua-keystore-type": "Type", - "opc-ua-keystore-type-required": "Type er påkrævet", - "opc-ua-keystore-location": "Placering *", - "opc-ua-keystore-password": "Adgangskode", - "opc-ua-keystore-password-required": "Adgangskode er påkrævet", - "opc-ua-keystore-alias": "Alias", - "opc-ua-keystore-alias-required": "Alias er påkrævet", - "opc-ua-keystore-key-password": "Nøgleadgangskode", - "opc-ua-keystore-key-password-required": "Adgangskode er påkrævet", - "opc-ua-mapping": "Tilknytning", - "add-opc-ua-mapping-prompt": "Tilføj tilknytning", - "opc-ua-mapping-type": "Tilknytningstype", - "opc-ua-mapping-type-required": "Tilknytningstype er påkrævet", - "opc-ua-device-node-pattern": "Enhedsknudemønster", - "opc-ua-device-node-pattern-required": "Enhedsknudemønster er påkrævet", - "opc-ua-namespace": "Namespace", - "opc-ua-add-map": "Tilføj tilknytningselement", - "kinesis-stream-name": "Streamnavn", - "kinesis-stream-name-required": "Streamnavn er påkrævet", - "kinesis-region": "Region", - "kinesis-region-required": "Region er påkrævet", - "kinesis-access-key-id": "Adgangsnøgle-id", - "kinesis-access-key-id-required": "Adgangsnøgle-id er påkrævet", - "kinesis-secret-access-key": "Hemmelig adgangsnøgle", - "kinesis-secret-access-key-required": "Hemmelig adgangsnøgle er påkrævet", - "kinesis-use-consumers-with-enhanced-fan-out": "Anvend forbrugere med forbedret Fan-Out", - "kinesis-use-credentials-from-instance-metadata": "Anvend brugeroplysninger fra Amazon EC2 Instance Metadata Service", - "kinesis-application-name": "Applikationsnavn (som standard lig med Streamnavn)", - "kinesis-initial-position-in-stream": "Startposition i stream", - "kinesis-initial-position-in-stream-required": "Startposition i stream påkrævet", - "kinesis-max-records": "Maks. poster", - "kinesis-max-records-required": "Maks. poster er påkrævet", - "kinesis-max-records-length-range": "Maks. længde for poster skal ligge i et område fra 1 til 10000", - "kinesis-request-timeout": "Anmodningstimeout i sekunder", - "kinesis-request-timeout-required": "Anmodningstimeout er påkrævet", - "other-properties": "Andre egenskaber", - "subscription-tags": "Abonnementstags", - "remove-subscription-tag": "Fjern abonnementstags", - "add-subscription-tag": "Tilføj abonnementstags", - "add-subscription-tag-prompt": "Tilføj venligst abonnementstags", - "key": "Nøgle", - "path": "Sti", - "required": "Påkrævet", - "integration-key": "Integrationsnøgle", - "copy-integration-key": "Kopiér integrationsnøgle", - "integration-key-copied-message": "Integrationsnøgle er blevet kopieret til udklipsholder", - "integration-secret": "Integration hemmelig", - "copy-integration-secret": "Kopiér integration hemmelig", - "integration-secret-copied-message": "Integration hemmelig er kopieret til udklipsholder", - "execute-remotely": "Fjernbetjent udførelse", - "handler-configuration": "Handler-konfiguration", - "handler-configuration-type": "Handler-type", - "so-broadcast": "Aktivér broadcast – integration accepterer adressepakker for broadcast", - "so-keepalive-option": "Muliggør afsendelse af keepalive-meddelelser på tilslutningsorienterede stik", - "so-reuse-addr": "Bind processen til en port", - "tcp-no-delay": "Tvinger et stik til at sende data uden buffer (deaktiver Nagle's bufferalgoritme)", - "fail-fast": "Undtagelse, når dekoderen bemærker, at rammelængden vil overstige maksimumstørrelse", - "strip-delimiter": "Fjern skilletegn", - "length-field-offset": "Feltforskydning for længde", - "length-field-offset-required": "Feltforskydning for længde er påkrævet.", - "length-field-offset-range": "Feltforskydning for længde skal ligge i et område fra 0 til 8.", - "length-field-length": "Længdefeltlængde", - "length-field-length-required": "Længdefeltlængde er påkrævet.", - "length-field-length-range": "Længdefeltlængde skal ligge i et område fra 0 til 8.", - "length-adjustment": "Længdejustering (kompenseringsværdien, der skal lægges til værdien af længdefeltet)", - "length-adjustment-required": "Længdejustering er påkrævet.", - "length-adjustment-range": "Længdejustering skal ligge i et område fra 0 til 8.", - "byte-order": "Byterækkefølge for længdefeltet", - "initial-bytes-to-strip": "Antal første bytes, der skal fjernes fra den afkodede ramme", - "initial-bytes-to-strip-required": "Antal første bytes, der skal fjernes fra den afkodede ramme, er påkrævet.", - "initial-bytes-to-strip-range": "Antal første bytes, der skal fjernes fra den afkodede ramme, skal ligge i området fra 0 til 8.", - "so-backlog-option": "Maks. antal ventende tilslutninger på stikket", - "so-backlog-option-required": "Maks. antal ventende tilslutninger på stikket er påkrævet.", - "so-backlog-option-range": "Maks. antal ventende tilslutninger på stikket skal ligge i området fra 1 til 65535.", - "so-rcv-buf": "Bufferens størrelse til indgående stik (i KB)", - "so-rcv-buf-required": "Bufferens størrelse til indgående stik (i KB) er påkrævet.", - "so-rcv-buf-range": "Bufferens størrelse til indgående stik (i KB) skal ligge i området fra 1 til 65535.", - "so-snd-buf": "Bufferens størrelse til udgående stik (i KB)", - "so-snd-buf-required": "Bufferens størrelse til udgående stik (i KB) er påkrævet.", - "so-snd-buf-range": "Bufferens størrelse til udgående stik (i KB) skal ligge i et område fra 1 til 65535.", - "charset-name": "Tegnsætnavn", - "charset-name-required": "Tegnsætnavn er påkrævet.", - "message-separator": "Meddelelsesseparator", - "message-separator-required": "Meddelelsesseparator er påkrævet.", - "character-sequence": "Tegnsekvens", - "character-sequence-required": "Tegnsekvens er påkrævet.", - "max-frame-length": "Maks. rammelængde (i bytes)", - "max-frame-length-required": "Maks. rammelængde (i bytes) er påkrævet.", - "max-frame-length-range": "Maks. rammelængde (i byte) skal ligge i området fra 1 til 65535.", - "handler-type": "Handler-type", - "message-size": "Meddelelsesstørrelse", - "message-size-required": "Meddelelsesstørrelse er påkrævet.", - "type-apache-pulsar": "Apache Pulsar", - "service-url": "Service-URL", - "service-url-required": "Service-URL er påkrævet.", - "subscription-name": "Abonnementsnavn", - "subscription-name-required": "Abonnementsnavn er påkrævet.", - "max-num-messages": "Maks. antal meddelelser", - "max-num-messages-required": "Maks. antal meddelelser er påkrævet.", - "max-num-bytes": "Maks. antal bytes", - "max-num-bytes-required": "Maks. antal bytes påkrævet.", - "timeout-in-ms": "Timeout i millisekunder", - "timeout-in-ms-required": "Timeout i millisekunder er påkrævet.", - "user-id": "Bruger-id", - "user-id-required": "Bruger-id er påkrævet.", - "token": "Token", - "token-required": "Token er påkrævet.", - "project-id": "Projekt-id", - "project-id-required": "Projekt-id er påkrævet.", - "subscription-id": "Abonnements-id", - "subscription-id-required": "Abonnements-id er påkrævet.", - "service-account-key": "Nøglefil til servicekonto", - "service-account-key-required": "Nøglefil til servicekonto er påkrævet.", - "tcp": { - "system-line-separator": "Systemlinjeseparator", - "nul-delimiter": "Nul-skilletegn", - "byte-order-little-endian": "Little Endian", - "byte-order-big-endian": "Big Endian" - }, - "cache-size": "Cachestørrelse", - "cache-time-to-live": "Cache time-to-live i minutter", - "min-cache-size": "Cachestørrelse kan ikke være lavere 0", - "min-cache-time-to-live": "Cache time-to-live kan ikke være lavere 0", - "max-cache-time-to-live": "Ugyldigt cache time-to-live, vælg mellem 0 og 525600" - }, - "item": { - "selected": "Valgt" - }, - "js-func": { - "no-return-error": "Funktionen skal returnere værdi!", - "return-type-mismatch": "", - "tidy": "Tidy", - "mini": "Mini" - }, - "key-val": { - "key": "Nøgle", - "value": "Værdi", - "remove-entry": "Fjern post", - "add-entry": "Tilføj post", - "no-data": "Ingen poster" - }, - "layout": { - "layout": "Layout", - "manage": "Administrer layouts", - "settings": "Layoutindstillinger", - "color": "Farve", - "main": "Hoved", - "right": "Højre", - "select": "Vælg mållayout" - }, - "legend": { - "direction": "Signaturforklaringens retning", - "position": "Signaturforklaringens placering", - "sort-legend": "Sortér datanøgler i signaturforklaring", - "show-max": "Vis maks. værdi", - "show-min": "Vis min. værdi", - "show-avg": "Vis gennemsnitsværdi", - "show-total": "Vis samlet værdi", - "settings": "Indstillinger for signaturforklaring", - "min": "min.", - "max": "maks.", - "avg": "gns.", - "total": "i alt", - "comparison-time-ago": { - "previousInterval": "(tidligere interval)", - "days": "(dag siden)", - "weeks": "(uge siden)", - "months": "(måned siden)", - "years": "(år siden)" + "calculated-fields" : { + "expression" : "Udtryk", + "no-found" : "Ingen beregnede felter fundet", + "list" : "{ count, plural, =1 {Et beregnet felt} other {Liste over # beregnede felter} }", + "selected-fields" : "{ count, plural, =1 {1 beregnet felt} other {# beregnede felter} } valgt", + "type" : { + "simple" : "Simpel", + "script" : "Script" + }, + "arguments" : "Argumenter", + "decimals-by-default" : "Decimaler som standard", + "debugging" : "Fejlfinding af beregnet felt", + "argument-name" : "Argumentnavn", + "datasource" : "Datakilde", + "add-argument" : "Tilføj argument", + "test-script-function" : "Test scriptfunktion", + "no-arguments" : "Ingen argumenter konfigureret", + "argument-settings" : "Argumentindstillinger", + "argument-current" : "Nuværende enhed", + "argument-current-tenant" : "Nuværende lejer", + "argument-device" : "Enhed", + "argument-asset" : "Aktiv", + "argument-customer" : "Kunde", + "argument-tenant" : "Nuværende lejer", + "argument-type" : "Argumenttype", + "see-debug-events" : "Se fejlsøgningshændelser", + "attribute" : "Attribut", + "copy-argument-name" : "Kopiér argumentnavn", + "timeseries-key" : "Tidsserienøgle", + "device-name" : "Enhedsnavn", + "latest-telemetry" : "Seneste telemetri", + "rolling" : "Tidsserieglidning", + "attribute-scope" : "Attributområde", + "server-attributes" : "Serverattributter", + "client-attributes" : "Klientattributter", + "shared-attributes" : "Delte attributter", + "attribute-key" : "Attributnøgle", + "default-value" : "Standardværdi", + "limit" : "Maks. værdier", + "time-window" : "Tidsvindue", + "customer-name" : "Kundenavn", + "asset-name" : "Aktivnavn", + "timeseries" : "Tidsserie", + "output" : "Output", + "create" : "Opret nyt beregnet felt", + "file" : "Beregnet felt-fil", + "invalid-file-error" : "Ugyldigt filformat. Sørg for, at filen er en gyldig JSON-fil.", + "import" : "Importér beregnet felt", + "export" : "Eksportér beregnet felt", + "export-failed-error" : "Kan ikke eksportere beregnet felt: {{error}}", + "output-type" : "Outputtype", + "delete-title" : "Er du sikker på, at du vil slette det beregnede felt '{{title}}'?", + "delete-text" : "Vær forsigtig, efter bekræftelse vil det beregnede felt og alle relaterede data ikke kunne gendannes.", + "delete-multiple-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 beregnet felt} other {# beregnede felter} }?", + "delete-multiple-text" : "Vær forsigtig, efter bekræftelse vil alle valgte beregnede felter blive fjernet og alle relaterede data ikke kunne gendannes.", + "test-with-this-message" : "Test med denne meddelelse", + "hint" : { + "arguments-simple-with-rolling" : "Beregnet felt af typen simpel må ikke indeholde nøgler med tidsserieglidningstype.", + "arguments-empty" : "Argumenter må ikke være tomme.", + "expression-required" : "Udtryk er påkrævet.", + "expression-invalid" : "Udtryk er ugyldigt", + "expression-max-length" : "Udtrykslængde skal være under 255 tegn.", + "argument-name-required" : "Argumentnavn er påkrævet.", + "argument-name-pattern" : "Argumentnavn er ugyldigt.", + "argument-name-duplicate" : "Argument med dette navn findes allerede.", + "argument-name-max-length" : "Argumentnavn skal være under 256 tegn.", + "argument-name-forbidden" : "Argumentnavn er reserveret og kan ikke anvendes.", + "argument-type-required" : "Argumenttype er påkrævet.", + "max-args" : "Maksimalt antal argumenter er nået.", + "decimals-range" : "Standard decimaler skal være et tal mellem 0 og 15.", + "expression" : "Standardudtryk demonstrerer, hvordan man omregner temperatur fra Fahrenheit til Celsius.", + "arguments-entity-not-found" : "Målentitet for argument ikke fundet." } }, - "login": { - "login": "Log på", - "request-password-reset": "Anmod om nulstilling af adgangskode", - "reset-password": "Nulstil adgangskode", - "create-password": "Opret adgangskode", - "passwords-mismatch-error": "De indtastede adgangskoder skal være ens!", - "password-again": "Gentag adgangskode", - "sign-in": "Log på", - "username": "Brugernavn (e-mail)", - "remember-me": "Husk mig", - "forgot-password": "Glemt adgangskode?", - "password-reset": "Nulstilling af adgangskode", - "expired-password-reset-message": "Dine brugeroplysninger er udløbet! Opret venligst en ny adgangskode.", - "new-password": "Ny adgangskode", - "new-password-again": "Gentag ny adgangskode", - "password-link-sent-message": "Link til nulstilling af adgangskode blev sendt!", - "email": "E-mail", - "no-account": "Har du ikke en konto?", - "create-account": "Opret en konto", - "login-with": "Log ind med din e-mail", - "or": "eller", - "error": "Login-fejl" - }, - "signup": { - "firstname": "Fornavn", - "lastname": "Efternavn", - "email": "E-mail", - "signup": "Tilmeld", - "create-password": "Opret en adgangskode", - "repeat-password": "Gentag din adgangskode", - "have-account": "Har du allerede en konto?", - "signin": "Log på", - "no-captcha-message": "Du skal bekræfte, at du ikke er en robot", - "password-length-message": "Din adgangskode skal bestå af mindst 6 tegn", - "email-verification": "E-mailbekræftelse", - "email-verification-message": "Der er sendt en e-mail med bekræftelsesoplysninger til den angivne e-mailadresse.
Følg instruktionerne i e-mailen for at fuldføre din tilmeldingsprocedure.
Bemærk: Hvis du ikke har set e-mailen efter et stykke tid, skal du tjekke din 'spam'-mappe eller forsøge at sende e-mailen igen ved at klikke på knappen 'Send igen'.", - "account-activation-title": "Aktivering af konto", - "account-activated": "Kontoen er aktiveret!", - "account-activated-text": "Tillykke!
Din konto er blevet aktiveret.", - "resend": "Send igen", - "inactive-user-exists-title": "Inaktiv bruger findes allerede", - "inactive-user-exists-text": "Der er allerede registreret en bruger med en ikke-bekræftet e-mailadresse.
Klik på knappen \"Send igen\", hvis du ønsker at sende bekræftelses-e-mailen igen.", - "activating-account": "Aktiverer konto...", - "activating-account-text": "Din konto er i øjeblikket ved at blive aktiveret. Vent venligst...", - "accept-privacy-policy": "Accepter fortrolighedspolitik", - "accept": "Accepter", - "privacy-policy": "Fortrolighedspolitik" - }, - "position": { - "top": "Top", - "bottom": "Bund", - "left": "Venstre", - "right": "Højre" - }, - "profile": { - "profile": "Profil", - "last-login-time": "Sidste login", - "change-password": "Skift adgangskode", - "current-password": "Nuværende adgangskode" - }, - "relation": { - "relations": "Relationer", - "direction": "Retning", - "search-direction": { - "FROM": "Fra", - "TO": "Til" - }, - "direction-type": { - "FROM": "fra", - "TO": "til" - }, - "from-relations": "Udgående relationer", - "to-relations": "Indgående relationer", - "selected-relations": "", - "type": "Type", - "to-entity-type": "Til entitetstype", - "to-entity-name": "Til entitetsnavn", - "from-entity-type": "Fra entitetstype", - "from-entity-name": "Fra entitetsnavn", - "to-entity": "Til entitet", - "from-entity": "Fra entitet", - "delete": "Slet relation", - "relation-type": "Relationstype", - "relation-type-required": "Relationstype er påkrævet.", - "any-relation-type": "Enhver type", - "add": "Tilføj relation", - "edit": "Rediger relation", - "delete-to-relation-title": "", - "delete-to-relation-text": "", - "delete-to-relations-title": "", - "delete-to-relations-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte relationer blive fjernet, og tilsvarende entiteter vil ikke være relaterede til den aktuelle entitet.", - "delete-from-relation-title": "", - "delete-from-relation-text": "", - "delete-from-relations-title": "", - "delete-from-relations-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte relationer blive fjernet, og den aktuelle entitet vil ikke være relateret til de tilsvarende entiteter.", - "remove-relation-filter": "Fjern relationsfilter", - "add-relation-filter": "Tilføj relationsfilter", - "any-relation": "Enhver relation", - "relation-filters": "Relationsfiltre", - "additional-info": "Yderligere info (JSON)", - "invalid-additional-info": "Kan ikke parse yderligere info-json.", - "no-relations-text": "Ingen relationer fundet" - }, - "rulechain": { - "rulechain": "Regelkæde", - "rulechains": "Regelkæder", - "root": "Rod", - "delete": "Slet regelkæde", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "add": "Tilføj regelkæde", - "set-root": "Lav regelkæde for rod", - "set-root-rulechain-title": "", - "set-root-rulechain-text": "Efter bekræftelsen vil regelkæden være rod og håndtere alle indgående transportmeddelelser.", - "delete-rulechain-title": "", - "delete-rulechain-text": "Vær forsigtig. Efter bekræftelsen vil regelkæden og alle relaterede data være uoprettelige.", - "delete-rulechains-title": "", - "delete-rulechains-action-title": "", - "delete-rulechains-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte regelkæder blive fjernet, og alle relaterede data vil være uoprettelige.", - "add-rulechain-text": "Tilføj ny regelkæde", - "no-rulechains-text": "Ingen regelkæder fundet", - "rulechain-details": "Oplysninger om regelkæde", - "details": "Oplysninger", - "events": "Begivenheder", - "system": "System", - "import": "Importér regelkæde", - "export": "Eksportér regelkæde", - "export-failed-error": "", - "create-new-rulechain": "Opret ny regelkæde", - "rulechain-file": "Regelkædefil", - "invalid-rulechain-file-error": "Regelkæden kunne ikke importeres: Ugyldig datastruktur for regelkæde.", - "copyId": "Kopiér regelkæde-id", - "idCopiedMessage": "Regelkæde-id er blevet kopieret til udklipsholder", - "select-rulechain": "Vælg regelkæde", - "no-rulechains-matching": "", - "rulechain-required": "Regelkæde er påkrævet", - "management": "Regelstyring", - "debug-mode": "Debug-tilstand", - "search": "Søg efter regelkæder", - "selected-rulechains": "", - "open-rulechain": "Åbn regelkæde" - }, - "rulenode": { - "details": "Oplysninger", - "events": "Begivenheder", - "search": "Søg efter knuder", - "open-node-library": "Åbn knudebibliotek", - "add": "Tilføj regelknude", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "type": "Type", - "delete": "Slet regelknude", - "select-all-objects": "Vælg alle knuder og tilslutninger", - "deselect-all-objects": "Fravælg alle knuder og tilslutninger", - "delete-selected-objects": "Slet valgte knuder og tilslutninger", - "delete-selected": "Slet valgte", - "select-all": "Vælg alle", - "copy-selected": "Kopiér valgte", - "deselect-all": "Fravælg alle", - "rulenode-details": "Oplysninger om regelknude", - "debug-mode": "Debug-tilstand", - "configuration": "Konfiguration", - "link": "Link", - "link-details": "Oplysninger om regelknudelink", - "add-link": "Tilføj link", - "link-label": "Link-etiket", - "link-label-required": "Link-etiket er påkrævet.", - "custom-link-label": "Brugerdefineret link-etiket", - "custom-link-label-required": "Brugerdefineret link-etiket er påkrævet.", - "link-labels": "Link-etiketter", - "link-labels-required": "Link-etiketter er påkrævet.", - "no-link-labels-found": "Ingen link-etiketter fundet", - "no-link-label-matching": "", - "create-new-link-label": "Opret en ny!", - "type-filter": "Filter", - "type-filter-details": "Filtrer indgående meddelelser med konfigurerede betingelser", - "type-enrichment": "Berigelse", - "type-enrichment-details": "Tilføj yderligere oplysninger i meddelelsesmetadata", - "type-transformation": "Omdannelse", - "type-transformation-details": "Skift meddelelsesdata og metadata", - "type-action": "Handling", - "type-action-details": "Gennemfør ekstrahandling", - "type-analytics": "Analytik", - "type-analytics-details": "Udfør analyse af streamede eller vedvarende data", - "type-external": "Ekstern", - "type-external-details": "Interagerer med eksternt system", - "type-rule-chain": "Regelkæde", - "type-rule-chain-details": "Videresender indgående meddelelser til specificeret regelkæde", - "type-input": "Input", - "type-input-details": "Logisk input af regelkæde, videresender indgående meddelelser til næste relaterede regelknude", - "type-unknown": "Ukendt", - "type-unknown-details": "Uløst regelknude", - "directive-is-not-loaded": "", - "ui-resources-load-error": "Kunne ikke indlæse konfigurations-UI-ressourcer.", - "invalid-target-rulechain": "Kan ikke løse målregelkæde!", - "test-script-function": "Testscriptfunktion", - "message": "Meddelelse", - "message-type": "Meddelelsestype", - "select-message-type": "Vælg meddelelsestype", - "message-type-required": "Meddelelsestype er påkrævet", - "metadata": "Metadata", - "metadata-required": "Metadataposter må ikke være tomme.", - "output": "Output", - "test": "Test", - "help": "Hjælp" - }, - "role": { - "role": "Rolle", - "roles": "Roller", - "management": "Rollestyring", - "view-roles": "Vis roller", - "no-roles-matching": "", - "role-list": "Rolleliste", - "add": "Tilføj rolle", - "view": "Vis rolle", - "search": "Søg efter roller", - "selected-roles": "", - "no-roles-text": "Ingen roller fundet", - "role-details": "Rolleoplysninger", - "add-role-text": "Tilføj ny rolle", - "delete": "Slet rolle", - "delete-roles": "Slet roller", - "delete-role-title": "", - "delete-role-text": "Vær forsigtig. Efter bekræftelsen vil rollen og alle relaterede data være uoprettelige.", - "delete-roles-title": "", - "delete-roles-action-title": "", - "delete-roles-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte roller blive fjernet, og alle relaterede data vil være uoprettelige.", - "role-type": "Rolletype", - "role-type-required": "Rolletype er påkrævet.", - "select-role-type": "Vælg rolletype", - "enter-role-type": "Indtast rolletype", - "any-role": "Enhver rolle", - "no-role-types-matching": "", - "role-type-list-empty": "Ingen rolletyper valgt.", - "role-types": "Rolletyper", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "events": "Begivenheder", - "details": "Oplysninger", - "copyId": "Kopiér rolle-id", - "idCopiedMessage": "Rolle-id er blevet kopieret til udklipsholder", - "permissions": "Tilladelser", - "role-required": "Rolle påkrævet", - "roles-required": "Roller påkrævet", - "display-type": { - "GENERIC": "Generisk", - "GROUP": "Gruppe" + "confirm-on-exit" : { + "message" : "Du har ikke gemte ændringer. Er du sikker på, at du vil forlade denne side?", + "html-message" : "Du har ikke gemte ændringer.
Er du sikker på, at du vil forlade denne side?", + "title" : "Ikke gemte ændringer" + }, + "contact" : { + "country" : "Land", + "country-required" : "Land er påkrævet.", + "city" : "By", + "state" : "Stat / Provins", + "postal-code" : "Postnummer", + "postal-code-invalid" : "Ugyldigt format for postnummer.", + "address" : "Adresse", + "address2" : "Adresse 2", + "phone" : "Telefon", + "email" : "E-mail", + "no-address" : "Ingen adresse", + "no-country-found" : "Ingen lande fundet.", + "no-country-matching" : "Ingen lande matchede '{{country}}'.", + "state-max-length" : "Statens længde skal være mindre end 256 tegn", + "phone-max-length" : "Telefonnummeret skal være mindre end 256 tegn", + "city-max-length" : "Byens navn skal være mindre end 256 tegn" + }, + "common" : { + "name" : "Navn", + "type" : "Type", + "general" : "Generelt", + "username" : "Brugernavn", + "password" : "Adgangskode", + "data" : "Data", + "timestamp" : "Tidsstempel", + "enter-username" : "Indtast brugernavn", + "enter-password" : "Indtast adgangskode", + "enter-search" : "Indtast søgning", + "created-time" : "Oprettelsestid", + "disabled" : "Deaktiveret", + "loading" : "Indlæser...", + "proceed" : "Fortsæt", + "open-details-page" : "Åbn detaljeside", + "not-found" : "Ikke fundet", + "value" : "Værdi", + "documentation" : "Dokumentation", + "time-left" : "{{time}} tilbage", + "output" : "Output", + "suffix" : { + "s" : "s", + "ms" : "ms" + }, + "hint" : { + "name-required" : "Navn er påkrævet.", + "name-pattern" : "Navn er ugyldigt.", + "name-max-length" : "Navn skal være under 256 tegn.", + "title-required" : "Titel er påkrævet.", + "title-pattern" : "Titel er ugyldig.", + "title-max-length" : "Titel skal være under 256 tegn.", + "key-required" : "Nøgle er påkrævet.", + "key-pattern" : "Nøgle er ugyldig.", + "key-max-length" : "Nøgle skal være under 256 tegn." + }, + "required-fields" : "Mangler påkrævede felter" + }, + "content-type" : { + "json" : "Json", + "text" : "Tekst", + "binary" : "Binær (Base64)" + }, + "color" : { + "color" : "Farve" + }, + "customer" : { + "customer" : "Kunde", + "customers" : "Kunder", + "management" : "Kundeadministration", + "dashboard" : "Kundedashboard", + "dashboards" : "Kundedashboards", + "devices" : "Kundeenheder", + "entity-views" : "Kundeenhedsvisninger", + "assets" : "Kundeaktiver", + "public-dashboards" : "Offentlige dashboards", + "public-devices" : "Offentlige enheder", + "public-assets" : "Offentlige aktiver", + "public-entity-views" : "Offentlige enhedsvisninger", + "add" : "Tilføj kunde", + "delete" : "Slet kunde", + "manage-customer-users" : "Administrér kundens brugere", + "manage-customer-devices" : "Administrér kundens enheder", + "manage-customer-dashboards" : "Administrér kundens dashboards", + "manage-public-devices" : "Administrér offentlige enheder", + "manage-public-dashboards" : "Administrér offentlige dashboards", + "manage-customer-assets" : "Administrér kundens aktiver", + "manage-customer-edges" : "Administrér kundens edge-instanser", + "manage-public-assets" : "Administrér offentlige aktiver", + "add-customer-text" : "Tilføj ny kunde", + "no-customers-text" : "Ingen kunder fundet", + "customer-details" : "Kundedetaljer", + "delete-customer-title" : "Er du sikker på, at du vil slette kunden '{{customerTitle}}'?", + "delete-customer-text" : "Vær forsigtig, efter bekræftelse vil kunden og alle tilknyttede data blive uoprettelige.", + "delete-customers-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 kunde} other {# kunder} }?", + "delete-customers-action-title" : "Slet { count, plural, =1 {1 kunde} other {# kunder} }", + "delete-customers-text" : "Vær forsigtig, efter bekræftelse vil alle valgte kunder blive fjernet og tilhørende data gå tabt.", + "manage-users" : "Administrér brugere", + "manage-assets" : "Administrér aktiver", + "manage-devices" : "Administrér enheder", + "manage-dashboards" : "Administrér dashboards", + "title" : "Titel", + "title-required" : "Titel er påkrævet.", + "title-max-length" : "Titel skal være mindre end 256 tegn", + "description" : "Beskrivelse", + "details" : "Detaljer", + "events" : "Begivenheder", + "copyId" : "Kopiér kunde-ID", + "idCopiedMessage" : "Kunde-ID er kopieret til udklipsholderen", + "select-customer" : "Vælg kunde", + "no-customers-matching" : "Ingen kunder matcher '{{entity}}'.", + "customer-required" : "Kunde er påkrævet", + "select-default-customer" : "Vælg standardkunde", + "default-customer" : "Standardkunde", + "default-customer-required" : "Standardkunde er påkrævet for at fejlfinde dashboard på Tenant-niveau", + "search" : "Søg kunder", + "selected-customers" : "{ count, plural, =1 {1 kunde} other {# kunder} } valgt", + "edges" : "Kundens edge-instanser", + "manage-edges" : "Administrér edge-instanser" + }, + "css-size" : { + "size-value-required" : "Størrelsesværdi er påkrævet", + "invalid-size-value" : "Ugyldig størrelsesværdi" + }, + "date" : { + "last-update-n-ago" : "Seneste opdatering for N siden", + "last-update-n-ago-text" : "Seneste opdatering {{ agoText }}", + "custom-date" : "Brugerdefineret dato", + "format" : "Format", + "preview" : "Forhåndsvisning", + "auto" : "Auto", + "time-granularity-formats" : "Tidsgranitetsformater", + "unit-year" : "År", + "unit-month" : "Måneder", + "unit-day" : "Dage", + "unit-hour" : "Timer", + "unit-minute" : "Minutter", + "unit-second" : "Sekunder", + "unit-millisecond" : "Millisekunder" + }, + "datetime" : { + "date-from" : "Dato fra", + "time-from" : "Tid fra", + "date-to" : "Dato til", + "time-to" : "Tid til", + "from" : "Fra", + "to" : "Til" + }, + "dashboard" : { + "dashboard" : "Dashboard", + "dashboards" : "Dashboards", + "management" : "Dashboardadministration", + "view-dashboards" : "Vis dashboards", + "add" : "Tilføj dashboard", + "assign-dashboard-to-customer" : "Tildel dashboard(s) til kunde", + "assign-dashboard-to-customer-text" : "Vælg venligst dashboards, der skal tildeles kunden", + "assign-to-customer-text" : "Vælg venligst kunde, som dashboard(s) skal tildeles", + "assign-to-customer" : "Tildel til kunde", + "unassign-from-customer" : "Fratildel fra kunde", + "make-public" : "Gør dashboard offentligt", + "make-private" : "Gør dashboard privat", + "manage-assigned-customers" : "Administrér tildelte kunder", + "assigned-customers" : "Tildelte kunder", + "assign-to-customers" : "Tildel dashboard(s) til kunder", + "assign-to-customers-text" : "Vælg venligst kunder, der skal tildeles dashboard(s)", + "unassign-from-customers" : "Fratildel dashboard(s) fra kunder", + "unassign-from-customers-text" : "Vælg venligst kunder, der skal fratildeles dashboard(s)", + "no-dashboards-text" : "Ingen dashboards fundet", + "no-widgets" : "Ingen widgets konfigureret", + "add-widget" : "Tilføj ny widget", + "add-widget-button-text" : "Tilføj widget", + "title" : "Titel", + "image" : "Dashboardbillede", + "mobile-app-settings" : "Indstillinger for mobilapplikation", + "mobile-order" : "Dashboardrækkefølge i mobilapplikation", + "mobile-hide" : "Skjul dashboard i mobilapplikation", + "update-image" : "Opdatér dashboardbillede", + "take-screenshot" : "Tag skærmbillede", + "select-widget-title" : "Vælg widget", + "select-widget-value" : "{{title}}: vælg widget", + "select-widget-subtitle" : "Liste over tilgængelige widgettyper", + "delete" : "Slet dashboard", + "title-required" : "Titel er påkrævet.", + "title-max-length" : "Titel skal være mindre end 256 tegn", + "description" : "Beskrivelse", + "details" : "Detaljer", + "dashboard-details" : "Dashboarddetaljer", + "add-dashboard-text" : "Tilføj nyt dashboard", + "assign-dashboards" : "Tildel dashboards", + "assign-new-dashboard" : "Tildel nyt dashboard", + "assign-dashboards-text" : "Tildel { count, plural, =1 {1 dashboard} other {# dashboards} } til kunder", + "unassign-dashboards-action-text" : "Fratildel { count, plural, =1 {1 dashboard} other {# dashboards} } fra kunder", + "delete-dashboards" : "Slet dashboards", + "unassign-dashboards" : "Fratildel dashboards", + "unassign-dashboards-action-title" : "Fratildel { count, plural, =1 {1 dashboard} other {# dashboards} } fra kunde", + "delete-dashboard-title" : "Er du sikker på, at du vil slette dashboardet '{{dashboardTitle}}'?", + "delete-dashboard-text" : "Vær forsigtig, efter bekræftelse vil dashboardet og alle relaterede data ikke kunne gendannes.", + "delete-dashboards-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 dashboard} other {# dashboards} }?", + "delete-dashboards-action-title" : "Slet { count, plural, =1 {1 dashboard} other {# dashboards} }", + "delete-dashboards-text" : "Vær forsigtig, efter bekræftelse vil alle valgte dashboards blive slettet og dataene ikke kunne gendannes.", + "unassign-dashboard-title" : "Er du sikker på, at du vil fratilde dashboardet '{{dashboardTitle}}'?", + "unassign-dashboard-text" : "Efter bekræftelse vil dashboardet blive fratildelt og ikke længere være tilgængeligt for kunden.", + "unassign-dashboard" : "Fratildel dashboard", + "unassign-dashboards-title" : "Er du sikker på, at du vil fratilde { count, plural, =1 {1 dashboard} other {# dashboards} }?", + "unassign-dashboards-text" : "Efter bekræftelse vil alle valgte dashboards blive fratildelt og ikke længere være tilgængelige for kunden.", + "public-dashboard-title" : "Dashboardet er nu offentligt", + "public-dashboard-text" : "Dit dashboard {{dashboardTitle}} er nu offentligt og tilgængeligt via følgende offentlige link:", + "public-dashboard-notice" : "Bemærk: Glem ikke at gøre relaterede enheder offentlige for at få adgang til deres data.", + "make-private-dashboard-title" : "Er du sikker på, at du vil gøre dashboardet '{{dashboardTitle}}' privat?", + "make-private-dashboard-text" : "Efter bekræftelse vil dashboardet blive gjort privat og ikke længere være tilgængeligt for andre.", + "make-private-dashboard" : "Gør dashboard privat", + "socialshare-text" : "'{{dashboardTitle}}' drevet af ThingsBoard", + "socialshare-title" : "'{{dashboardTitle}}' drevet af ThingsBoard", + "select-dashboard" : "Vælg dashboard", + "no-dashboards-matching" : "Ingen dashboards matcher '{{entity}}'.", + "dashboard-required" : "Dashboard er påkrævet.", + "select-existing" : "Vælg eksisterende dashboard", + "create-new" : "Opret nyt dashboard", + "new-dashboard-title" : "Titel på nyt dashboard", + "open-dashboard" : "Åbn dashboard", + "set-background" : "Angiv baggrund", + "background-color" : "Baggrundsfarve", + "background-image" : "Baggrundsbillede", + "background-size-mode" : "Baggrundsstørrelsestilstand", + "no-image" : "Intet billede valgt", + "empty-image" : "Intet billede", + "drop-image" : "Slip et billede eller klik for at vælge en fil til upload.", + "maximum-upload-file-size" : "Maksimal uploadfilstørrelse: {{ size }}", + "cannot-upload-file" : "Kan ikke uploade fil", + "settings" : "Indstillinger", + "move-all-widgets" : "Flyt alle widgets", + "move-by" : "Flyt med", + "cols" : "kolonner", + "rows" : "rækker", + "layout" : "Layout", + "layout-type-default" : "Standard", + "layout-type-scada" : "SCADA", + "layout-type-divider" : "Opdeler", + "layout-settings-type" : "Layoutindstillinger: {{ type }} breakpoint", + "columns-count" : "Antal kolonner", + "columns-count-required" : "Antal kolonner er påkrævet.", + "min-columns-count-message" : "Mindst 10 kolonner kræves.", + "max-columns-count-message" : "Højst 1000 kolonner tilladt.", + "min-layout-width" : "Minimal layoutbredde", + "columns-suffix" : "kolonner", + "widgets-margins" : "Afstand mellem widgets", + "margin-required" : "Marginværdi er påkrævet.", + "min-margin-message" : "Mindst tilladte marginværdi er 0.", + "max-margin-message" : "Maksimalt tilladte marginværdi er 50.", + "horizontal-margin" : "Vandret margin", + "horizontal-margin-required" : "Værdi for vandret margin er påkrævet.", + "min-horizontal-margin-message" : "Mindst tilladte vandrette marginværdi er 0.", + "max-horizontal-margin-message" : "Maksimalt tilladte vandrette marginværdi er 50.", + "vertical-margin" : "Lodret margin", + "vertical-margin-required" : "Værdi for lodret margin er påkrævet.", + "min-vertical-margin-message" : "Mindst tilladte lodrette marginværdi er 0.", + "max-vertical-margin-message" : "Maksimalt tilladte lodrette marginværdi er 50.", + "apply-outer-margin" : "Anvend margin på layoutets sider", + "autofill-height" : "Autofyld layout-højde", + "mobile-layout" : "Indstillinger for mobillayout", + "mobile-row-height" : "Mobilrækkehøjde", + "mobile-row-height-required" : "Værdi for mobilrækkehøjde er påkrævet.", + "min-mobile-row-height-message" : "Mindst tilladte mobilrækkehøjde er 5 pixels.", + "max-mobile-row-height-message" : "Maksimalt tilladte mobilrækkehøjde er 200 pixels.", + "row-height" : "Rækkehøjde", + "row-height-required" : "Værdi for rækkehøjde er påkrævet.", + "min-row-height-message" : "Mindst tilladte rækkehøjde er 5 pixels.", + "max-row-height-message" : "Maksimalt tilladte rækkehøjde er 200 pixels.", + "display-first-in-mobile-view" : "Vis først i mobilvisning", + "title-settings" : "Titelindstillinger", + "display-title" : "Vis dashboardtitel", + "title-color" : "Titelfarve", + "toolbar-settings" : "Værktøjslinjeindstillinger", + "hide-toolbar" : "Skjul værktøjslinje", + "toolbar-always-open" : "Hold værktøjslinjen åben", + "display-dashboards-selection" : "Vis dashboardvalg", + "display-entities-selection" : "Vis enhedsvalg", + "display-filters" : "Vis filtre", + "display-dashboard-timewindow" : "Vis tidsvindue", + "display-dashboard-export" : "Vis eksport", + "display-update-dashboard-image" : "Vis opdater dashboardbillede", + "dashboard-logo-settings" : "Dashboard logoindstillinger", + "display-dashboard-logo" : "Vis logo i dashboardets fuldskærmstilstand", + "dashboard-logo-image" : "Dashboard logobillede", + "advanced-settings" : "Avancerede indstillinger", + "dashboard-css" : "Dashboard CSS", + "import" : "Importér dashboard", + "export" : "Eksportér dashboard", + "export-failed-error" : "Kunne ikke eksportere dashboard: {{error}}", + "export-prompt" : "Indlejr dashboardbilleder og ressourcer", + "create-new-dashboard" : "Opret nyt dashboard", + "dashboard-file" : "Dashboardfil", + "invalid-dashboard-file-error" : "Kan ikke importere dashboard: Ugyldig datastruktur.", + "dashboard-import-missing-aliases-title" : "Konfigurer aliaser brugt i det importerede dashboard", + "create-new-widget" : "Opret ny widget", + "import-widget" : "Importér widget", + "widget-file" : "Widgetfil", + "invalid-widget-file-error" : "Kan ikke importere widget: Ugyldig widgetdatastruktur.", + "widget-import-missing-aliases-title" : "Konfigurer aliaser brugt i den importerede widget", + "open-toolbar" : "Åbn dashboardværktøjslinje", + "close-toolbar" : "Luk værktøjslinje", + "configuration-error" : "Konfigurationsfejl", + "alias-resolution-error-title" : "Fejl i dashboard-alias-konfiguration", + "invalid-aliases-config" : "Kunne ikke finde nogen enheder, der matcher nogle af aliasfiltrene.
Kontakt venligst din administrator for at løse problemet.", + "select-devices" : "Vælg enheder", + "assignedToCustomer" : "Tildelt kunde", + "assignedToCustomers" : "Tildelt kunder", + "public" : "Offentlig", + "copyId" : "Kopiér dashboard-id", + "idCopiedMessage" : "Dashboard-id er blevet kopieret til udklipsholderen", + "public-link" : "Offentligt link", + "copy-public-link" : "Kopiér offentligt link", + "public-link-copied-message" : "Offentligt dashboardlink er blevet kopieret til udklipsholderen", + "manage-states" : "Administrér dashboardtilstande", + "states" : "Dashboardtilstande", + "states-short" : "Tilstande", + "search-states" : "Søg i dashboardtilstande", + "selected-states" : "{ count, plural, =1 {1 dashboardtilstand} other {# dashboardtilstande} } valgt", + "edit-state" : "Rediger dashboardtilstand", + "delete-state" : "Slet dashboardtilstand", + "add-state" : "Tilføj dashboardtilstand", + "no-states-text" : "Ingen tilstande fundet", + "state" : "Dashboardtilstand", + "state-name" : "Navn", + "state-name-required" : "Dashboardtilstandens navn er påkrævet.", + "state-id" : "Tilstands-id", + "state-id-required" : "Dashboardtilstands-id er påkrævet.", + "state-id-exists" : "Der findes allerede en dashboardtilstand med samme id.", + "is-root-state" : "Rodtilstand", + "delete-state-title" : "Slet dashboardtilstand", + "delete-state-text" : "Er du sikker på, at du vil slette dashboardtilstanden med navnet '{{stateName}}'?", + "show-details" : "Vis detaljer", + "hide-details" : "Skjul detaljer", + "select-state" : "Vælg måldashboardtilstand", + "state-controller" : "Tilstandskontrol", + "state-controller-default" : "statisk (forældet)", + "search" : "Søg dashboards", + "selected-dashboards" : "{ count, plural, =1 {1 dashboard} other {# dashboards} } valgt", + "home-dashboard" : "Startdashboard", + "home-dashboard-hide-toolbar" : "Skjul værktøjslinje i startdashboard", + "unassign-dashboard-from-edge-text" : "Efter bekræftelse vil dashboardet blive fratildelt og ikke være tilgængeligt for edge.", + "unassign-dashboards-from-edge-title" : "Er du sikker på, at du vil fratilde { count, plural, =1 {1 dashboard} other {# dashboards} }?", + "unassign-dashboards-from-edge-text" : "Efter bekræftelse vil alle valgte dashboards blive fratildelt og ikke være tilgængelige for edge.", + "assign-dashboard-to-edge" : "Tildel dashboard(s) til edge", + "assign-dashboard-to-edge-text" : "Vælg venligst dashboards, der skal tildeles til edge", + "non-existent-dashboard-state-error" : "Dashboardtilstand med id \"{{ stateId }}\" blev ikke fundet", + "edit-mode" : "Redigeringstilstand", + "duplicate-state-action" : "Duplikér tilstand", + "breakpoint-value" : "Breakpoint ({{ value }})", + "breakpoints-id" : { + "default" : "Standard", + "xs" : "Mobil (xs)", + "sm" : "Tablet (sm)", + "md" : "Laptop (md)", + "lg" : "Desktop (lg)", + "xl" : "Desktop (xl)" + }, + "view-format-type-grid" : "Gitter", + "view-format-type-list" : "Liste", + "view-format" : "Visningsformat" + }, + "datakey" : { + "settings" : "Indstillinger", + "general" : "Generelt", + "advanced" : "Avanceret", + "key" : "Nøgle", + "keys" : "Nøgler", + "label" : "Etiket", + "color" : "Farve", + "units" : "Specielt symbol, der vises ved siden af værdien", + "decimals" : "Antal decimaler", + "data-generation-func" : "Datagenereringsfunktion", + "use-data-post-processing-func" : "Brug dataefterbehandlingsfunktion", + "configuration" : "Datakey-konfiguration", + "timeseries" : "Tidsserie", + "attributes" : "Attributter", + "entity-field" : "Enhedsfelt", + "alarm" : "Alarmfelter", + "timeseries-required" : "Enhedens tidsserier er påkrævet.", + "timeseries-or-attributes-required" : "Enhedens tidsserier/attributter er påkrævet.", + "alarm-fields-timeseries-or-attributes-required" : "Alarmfelter eller enhedens tidsserier/attributter er påkrævet.", + "maximum-timeseries-or-attributes" : "Maksimalt { count, plural, =1 {1 tidsserie/attribut er tilladt.} other {# tidsserier/attributter er tilladt} }", + "alarm-fields-required" : "Alarmfelter er påkrævet.", + "function-types" : "Funktionstyper", + "function-type" : "Funktionstype", + "function-types-required" : "Funktionstyper er påkrævet.", + "data-keys" : "Datataster", + "data-key" : "Datatast", + "data-keys-required" : "Datataster er påkrævet.", + "data-key-required" : "Datatast er påkrævet.", + "alarm-keys" : "Alarm-datataster", + "alarm-key" : "Alarm-datatast", + "alarm-key-functions" : "Alarm-funktioner", + "alarm-key-function" : "Alarm-funktion", + "latest-keys" : "Seneste datataster", + "latest-key" : "Seneste datatast", + "latest-key-functions" : "Seneste nøglefunktioner", + "latest-key-function" : "Seneste nøglefunktion", + "timeseries-keys" : "Tidsserie-datataster", + "timeseries-key" : "Tidsserie-datatast", + "timeseries-key-functions" : "Tidsserie-funktioner", + "timeseries-key-function" : "Tidsserie-funktion", + "maximum-function-types" : "Maksimalt { count, plural, =1 {1 funktionstype er tilladt.} other {# funktionstyper er tilladt} }", + "time-description" : "tidsstempel for den aktuelle værdi;", + "value-description" : "den aktuelle værdi;", + "prev-value-description" : "resultat af forrige funktionskald;", + "time-prev-description" : "tidsstempel for forrige værdi;", + "prev-orig-value-description" : "oprindelig forrige værdi;", + "aggregation" : "Aggregering", + "aggregation-type-hint-common" : "Af ydeevnehensyn er aggregeringsberegning kun tilgængelig for faste tidsintervaller såsom \"nuværende dag\", \"nuværende måned\" osv., og ikke for glidende vinduer som 'sidste 30 minutter' eller 'sidste 24 timer'.", + "aggregation-type-none-hint" : "Tag seneste værdi.", + "aggregation-type-min-hint" : "Find minimumsværdien blandt datapunkter i det valgte tidsvindue.", + "aggregation-type-max-hint" : "Find maksimumsværdien blandt datapunkter i det valgte tidsvindue.", + "aggregation-type-avg-hint" : "Beregn gennemsnitsværdi blandt datapunkter i det valgte tidsvindue.", + "aggregation-type-sum-hint" : "Summér alle værdier af datapunkterne i det valgte tidsvindue.", + "aggregation-type-count-hint" : "Samlet antal datapunkter i det valgte tidsvindue.", + "delta-calculation" : "Delta-beregning", + "enable-delta-calculation" : "Aktivér delta-beregning", + "enable-delta-calculation-hint" : "Når aktiveret, beregnes nøgleværdien ud fra aggregerede værdier for et valgt tidsvindue og en angivet sammenligningsperiode. Af ydeevnehensyn er delta-beregning kun tilgængelig for historiske tidsvinduer og ikke for realtidsværdier. F.eks. kan du beregne forskellen i energiforbrug i går sammenlignet med dagen før.", + "delta-calculation-result" : "Resultat af delta-beregning", + "delta-calculation-result-previous-value" : "Tidligere værdi", + "delta-calculation-result-delta-absolute" : "Delta (absolut)", + "delta-calculation-result-delta-percent" : "Delta (procent)", + "source" : "Kilde", + "latest" : "Seneste", + "latest-value" : "Seneste værdi", + "delta" : "delta", + "percent" : "procent", + "absolute" : "absolut" + }, + "datasource" : { + "type" : "Datakildetype", + "name" : "Navn", + "label" : "Etiket", + "add-datasource-prompt" : "Tilføj venligst en datakilde" + }, + "details" : { + "details" : "Detaljer", + "edit-mode" : "Redigeringstilstand", + "edit-json" : "Rediger JSON", + "toggle-edit-mode" : "Skift redigeringstilstand" + }, + "device" : { + "device" : "Enhed", + "device-required" : "Enhed er påkrævet.", + "devices" : "Enheder", + "management" : "Enhedsadministration", + "view-devices" : "Vis enheder", + "device-alias" : "Enhedsalias", + "device-type-max-length" : "Enhedstype må være under 256 tegn", + "aliases" : "Enhedsaliasser", + "no-alias-matching" : "'{{alias}}' blev ikke fundet.", + "no-aliases-found" : "Ingen aliasser fundet.", + "no-key-matching" : "'{{key}}' blev ikke fundet.", + "no-keys-found" : "Ingen nøgler fundet.", + "create-new-alias" : "Opret en ny!", + "create-new-key" : "Opret en ny!", + "duplicate-alias-error" : "Dubletalias fundet '{{alias}}'.
Enhedsaliasser skal være unikke i dashboardet.", + "configure-alias" : "Konfigurér alias '{{alias}}'", + "no-devices-matching" : "Ingen enheder matcher '{{entity}}'.", + "alias" : "Alias", + "alias-required" : "Enhedsalias er påkrævet.", + "remove-alias" : "Fjern enhedsalias", + "add-alias" : "Tilføj enhedsalias", + "name-starts-with" : "Udtryk for enhedsnavn", + "help-text" : "Brug '%' efter behov: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list" : "Enhedsliste", + "use-device-name-filter" : "Brug filter", + "device-list-empty" : "Ingen enheder valgt.", + "device-name-filter-required" : "Filter for enhedsnavn er påkrævet.", + "device-name-filter-no-device-matched" : "Ingen enheder der starter med '{{device}}' blev fundet.", + "add" : "Tilføj enhed", + "assign-to-customer" : "Tildel til kunde", + "assign-device-to-customer" : "Tildel enhed(er) til kunde", + "assign-device-to-customer-text" : "Vælg venligst enheder, der skal tildeles kunden", + "make-public" : "Gør enhed offentlig", + "make-private" : "Gør enhed privat", + "no-devices-text" : "Ingen enheder fundet", + "assign-to-customer-text" : "Vælg venligst en kunde, som enheden skal tildeles", + "device-details" : "Enhedsdetaljer", + "add-device-text" : "Tilføj ny enhed", + "credentials" : "Oplysninger", + "manage-credentials" : "Administrér oplysninger", + "delete" : "Slet enhed", + "assign-devices" : "Tildel enheder", + "assign-devices-text" : "Tildel { count, plural, =1 {1 enhed} other {# enheder} } til kunde", + "delete-devices" : "Slet enheder", + "unassign-from-customer" : "Fratildel fra kunde", + "unassign-devices" : "Fratildel enheder", + "unassign-devices-action-title" : "Fratildel { count, plural, =1 {1 enhed} other {# enheder} } fra kunde", + "unassign-device-from-edge-title" : "Er du sikker på, at du vil fratilde enheden '{{deviceName}}'?", + "unassign-device-from-edge-text" : "Efter bekræftelse vil enheden blive fratildelt og ikke længere være tilgængelig for edge.", + "unassign-devices-from-edge" : "Fratildel enheder fra edge", + "assign-new-device" : "Tildel ny enhed", + "make-public-device-title" : "Er du sikker på, at du vil gøre enheden '{{deviceName}}' offentlig?", + "make-public-device-text" : "Efter bekræftelse vil enheden og alle dens data være offentlige og tilgængelige for andre.", + "make-private-device-title" : "Er du sikker på, at du vil gøre enheden '{{deviceName}}' privat?", + "make-private-device-text" : "Efter bekræftelse vil enheden og alle dens data være private og ikke længere tilgængelige for andre.", + "view-credentials" : "Vis oplysninger", + "delete-device-title" : "Er du sikker på, at du vil slette enheden '{{deviceName}}'?", + "delete-device-text" : "Vær forsigtig, efter bekræftelse vil enheden og alle relaterede data ikke kunne gendannes.", + "delete-devices-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 enhed} other {# enheder} }?", + "delete-devices-action-title" : "Slet { count, plural, =1 {1 enhed} other {# enheder} }", + "delete-devices-text" : "Vær forsigtig, efter bekræftelse vil alle valgte enheder blive slettet og alle relaterede data vil gå tabt.", + "unassign-device-title" : "Er du sikker på, at du vil fratilde enheden '{{deviceName}}'?", + "unassign-device-text" : "Efter bekræftelse vil enheden blive fratildelt og ikke længere være tilgængelig for kunden.", + "unassign-device" : "Fratildel enhed", + "unassign-devices-title" : "Er du sikker på, at du vil fratilde { count, plural, =1 {1 enhed} other {# enheder} }?", + "unassign-devices-text" : "Efter bekræftelse vil alle valgte enheder blive fratildelt og ikke længere være tilgængelige for kunden.", + "device-credentials" : "Enhedsoplysninger", + "loading-device-credentials" : "Indlæser enhedsoplysninger...", + "credentials-type" : "Oplysningstype", + "access-token" : "Adgangstoken", + "access-token-required" : "Adgangstoken er påkrævet.", + "access-token-invalid" : "Adgangstoken skal være mellem 1 og 32 tegn.", + "certificate-pem-format" : "Certifikat i PEM-format", + "certificate-pem-format-required" : "Certifikat er påkrævet.", + "copy-access-token" : "Kopiér adgangstoken", + "copy-certificate" : "Kopiér certifikat", + "copy-client-id" : "Kopiér klient-id", + "copy-user-name" : "Kopiér brugernavn", + "copy-password" : "Kopiér adgangskode", + "generate-client-id" : "Generér klient-id", + "generate-user-name" : "Generér brugernavn", + "generate-password" : "Generér adgangskode", + "generate-access-token" : "Generér adgangstoken", + "lwm2m-security-config" : { + "identity" : "Klientidentitet", + "identity-required" : "Klientidentitet er påkrævet.", + "identity-tooltip" : "PSK-identifikatoren er en vilkårlig identifikator op til 128 bytes, som beskrevet i standarden [RFC7925].\nIdentifikatoren SKAL først konverteres til en streng og derefter kodes til oktetter med UTF-8.", + "client-key" : "Klientnøgle", + "client-key-required" : "Klientnøgle er påkrævet.", + "client-key-tooltip-prk" : "RPK offentlig nøgle eller id skal følge standarden [RFC7250] og være kodet i Base64!", + "client-key-tooltip-psk" : "PSK-nøgle skal følge standarden [RFC4279] og være i HexDec-format: 32, 64, 128 tegn!", + "endpoint" : "Endpoint-klientnavn", + "endpoint-required" : "Endpoint-klientnavn er påkrævet.", + "client-public-key" : "Klientens offentlige nøgle", + "client-public-key-hint" : "Hvis den offentlige nøgle er tom, vil det betroede certifikat blive brugt", + "client-public-key-tooltip" : "X509 offentlig nøgle skal være i DER-kodet X509v3-format, udelukkende understøtte EC-algoritmen og derefter være Base64-kodet!", + "mode" : "Sikkerhedskonfigurationstilstand", + "client-tab" : "Klientens sikkerhedskonfiguration", + "client-certificate" : "Klientcertifikat", + "bootstrap-tab" : "Bootstrap-klient", + "bootstrap-server" : "Bootstrap-server", + "lwm2m-server" : "LwM2M-server", + "client-publicKey-or-id" : "Klientens offentlige nøgle eller ID", + "client-publicKey-or-id-required" : "Klientens offentlige nøgle eller ID er påkrævet.", + "client-publicKey-or-id-tooltip-psk" : "PSK-identifikator er en vilkårlig identifikator op til 128 bytes som beskrevet i standarden [RFC7925].\nDen SKAL konverteres til en streng og kodes med UTF-8.", + "client-publicKey-or-id-tooltip-rpk" : "RPK offentlig nøgle eller ID skal være i henhold til standard [RFC7250] og Base64-kodet!", + "client-publicKey-or-id-tooltip-x509" : "X509 offentlig nøgle skal være i DER-kodet X509v3-format og udelukkende understøtte EC-algoritmen og derefter være Base64-kodet", + "client-secret-key" : "Klientens hemmelige nøgle", + "client-secret-key-required" : "Klientens hemmelige nøgle er påkrævet.", + "client-secret-key-tooltip-psk" : "PSK-nøgle skal følge standarden [RFC4279] og være i HexDec-format: 32, 64, 128 tegn!", + "client-secret-key-tooltip-prk" : "RPK hemmelige nøgle skal være i PKCS_8-format (DER-kodning, standard [RFC5958]) og derefter Base64-kodet!", + "client-secret-key-tooltip-x509" : "X509 hemmelige nøgle skal være i PKCS_8-format (DER-kodning, standard [RFC5958]) og derefter Base64-kodet!" + }, + "client-id" : "Klient-ID", + "client-id-pattern" : "Indeholder ugyldigt tegn.", + "user-name" : "Brugernavn", + "user-name-required" : "Brugernavn er påkrævet.", + "client-id-or-user-name-necessary" : "Klient-ID og/eller brugernavn er nødvendige", + "password" : "Adgangskode", + "secret" : "Hemmelighed", + "secret-required" : "Hemmelighed er påkrævet.", + "device-type" : "Enhedsprofil", + "device-type-required" : "Enhedstype er påkrævet.", + "select-device-type" : "Vælg enhedstype", + "enter-device-type" : "Indtast enhedsprofil", + "any-device" : "Enhver enhed", + "no-device-types-matching" : "Ingen enhedsprofiler matcher '{{entitySubtype}}'.", + "device-type-list-empty" : "Ingen enhedsprofiler valgt!", + "device-profile-type-list-empty" : "Mindst én enhedsprofil skal vælges.", + "device-types" : "Enhedstyper", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "name-max-length" : "Navn må være under 256 tegn", + "label-max-length" : "Etiket må være under 256 tegn", + "description" : "Beskrivelse", + "label" : "Etiket", + "events" : "Hændelser", + "details" : "Detaljer", + "copyId" : "Kopiér enheds-ID", + "copyAccessToken" : "Kopiér adgangstoken", + "copy-mqtt-authentication" : "Kopiér MQTT-legitimationsoplysninger", + "idCopiedMessage" : "Enheds-ID er kopieret til udklipsholderen", + "accessTokenCopiedMessage" : "Enheds adgangstoken er kopieret til udklipsholderen", + "mqtt-authentication-copied-message" : "Enhedens MQTT-legitimationsoplysninger er kopieret til udklipsholderen", + "assignedToCustomer" : "Tildelt kunde", + "unable-delete-device-alias-title" : "Kan ikke slette enhedsalias", + "unable-delete-device-alias-text" : "Enhedsalias '{{deviceAlias}}' kan ikke slettes, da det bruges af følgende widget(s):
{{widgetsList}}", + "is-gateway" : "Er gateway", + "overwrite-activity-time" : "Overskriv aktivitetstid for tilsluttet enhed", + "device-filter" : "Enhedsfilter", + "device-filter-title" : "Enhedsfilter", + "filter-title" : "Filter", + "device-state" : "Enhedstilstand", + "state" : "Tilstand", + "any" : "Enhver", + "active" : "Aktiv", + "inactive" : "Inaktiv", + "public" : "Offentlig", + "device-public" : "Enheden er offentlig", + "select-device" : "Vælg enhed", + "import" : "Importér enhed", + "device-file" : "Enhedsfil", + "search" : "Søg enheder", + "selected-devices" : "{ count, plural, =1 {1 enhed} other {# enheder} } valgt", + "device-configuration" : "Enhedskonfiguration", + "transport-configuration" : "Transportkonfiguration", + "wizard" : { + "device-details" : "Enhedsdetaljer" + }, + "unassign-devices-from-edge-title" : "Er du sikker på, at du vil fratilde { count, plural, =1 {1 enhed} other {# enheder} }?", + "unassign-devices-from-edge-text" : "Efter bekræftelse vil alle valgte enheder blive fratildelt og ikke være tilgængelige for edge.", + "time" : "Tid", + "connectivity" : { + "check-connectivity" : "Tjek forbindelsen", + "device-created-check-connectivity" : "Enheden er oprettet. Lad os tjekke forbindelsen!", + "loading-check-connectivity-command" : "Indlæser kommandoer til forbindelsestjek...", + "use-following-instructions" : "Brug følgende instruktioner til at sende telemetri på vegne af enheden via shell", + "execute-following-command" : "Udfør følgende kommando", + "install-curl-windows" : "Fra og med Windows 10 b17063 er cURL tilgængelig som standard", + "install-curl-macos" : "Fra og med Mac OS X 10.2 6C115 (Jaguar) er cURL tilgængelig som standard", + "install-mqtt-windows" : "Brug instruktionerne til at downloade, installere, konfigurere og køre mosquitto_pub", + "install-coap-client" : "Brug instruktionerne til at downloade, installere, konfigurere og køre coap-client", + "install-necessary-client-tools" : "Installer nødvendige klientværktøjer", + "mqtts-x509-command" : "Brug følgende dokumentation til at tilslutte enheden via MQTT med X509-godkendelse", + "coaps-x509-command" : "Brug følgende dokumentation til at tilslutte enheden via CoAP over DTLS med X509-godkendelse", + "snmp-command" : "Brug følgende dokumentation til at tilslutte enheden via SNMP.", + "sparkplug-command" : "Brug følgende dokumentation til at tilslutte enheden via MQTT Sparkplug.", + "lwm2m-command" : "Brug følgende dokumentation til at tilslutte enheden via LWM2M." } }, - "group-permission": { - "user-group-roles": "Brugergrupperoller", - "entity-group-permissions": "Tilladelser for entitetsgruppe", - "role-type": "Rolletype", - "role-name": "Rollenavn", - "group-type": "Gruppetype", - "group-name": "Gruppenavn", - "group-owner": "Gruppeejer", - "user-group-name": "Brugergruppenavn", - "user-group-owner": "Brugergruppeejer", - "edit": "Rediger tilladelser", - "delete": "Slet tilladelser", - "selected-group-permissions": "", - "delete-group-permission-title": "", - "delete-group-permission-text": "Vær forsigtig. Efter bekræftelsen vil gruppetilladelsen og alle relaterede data være uoprettelige.", - "delete-group-permission": "Slet gruppetilladelse", - "delete-group-permissions-title": "", - "delete-group-permissions-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte gruppetilladelser blive fjernet, og tilsvarende brugere vil miste adgang til specificerede ressourcer.", - "delete-group-permissions": "Slet gruppetilladelser", - "add-group-permission": "Tilføj gruppetilladelse", - "edit-group-permission": "Rediger gruppetilladelse", - "entity-group": "Entitetsgruppe", - "user-group": "Brugergruppe", - "no-owners-matching": "", - "target-owner-required": "Entitetsgruppeejer er påkrævet.", - "target-user-group-owner-required": "Brugergruppeejer er påkrævet.", - "no-group-permissions-text": "Ingen gruppetilladelser fundet" - }, - "permission": { - "permissions-required": "Mindst én tilladelsespost skal angives.", - "remove-permission": "Fjern tilladelsespost", - "add-permission": "Tilføj tilladelsespost", - "other": "Andet", - "resource": { - "resource": "Ressource", - "select-resource": "Vælg ressource", - "resource-required": "Ressource er påkrævet", - "no-resources-matching": "", - "display-type": { - "ALL": "Alle", - "PROFILE": "Profil", - "ADMIN_SETTINGS": "Admin-indstillinger", - "ALARM": "Alarm", - "DEVICE": "Enhed", - "DEVICE_PROFILE": "Enhedsprofil", - "ASSET": "Aktiv", - "CUSTOMER": "Kunde", - "DASHBOARD": "Dashboard", - "ENTITY_VIEW": "Entitetsvisning", - "TENANT": "Lejer", - "TENANT_PROFILE": "Lejerprofil", - "RULE_CHAIN": "Regelkæde", - "USER": "Bruger", - "WIDGETS_BUNDLE": "Widgets-bundt", - "WIDGET_TYPE": "Widget-type", - "CONVERTER": "Omformer", - "INTEGRATION": "Integration", - "SCHEDULER_EVENT": "Planlægningsbegivenheder", - "BLOB_ENTITY": "Blob-entitet", - "CUSTOMER_GROUP": "Kundegruppe", - "DEVICE_GROUP": "Enhedsgruppe", - "ASSET_GROUP": "Aktivgruppe", - "USER_GROUP": "Brugergruppe", - "ENTITY_VIEW_GROUP": "Entitetsvisningsgruppe", - "DASHBOARD_GROUP": "Dashboardgruppe", - "ROLE": "Rolle", - "GROUP_PERMISSION": "Gruppetilladelse", - "WHITE_LABELING": "Hvid mærkning", - "AUDIT_LOG": "Auditlog", - "API_USAGE_STATE": "API-brugstilstand" - } + "dynamic-form" : { + "property" : { + "properties" : "Egenskaber", + "property" : "Egenskab", + "id" : "Id", + "name" : "Navn", + "type" : "Type", + "type-text" : "Tekst", + "type-password" : "Adgangskode", + "type-textarea" : "Tekstområde", + "type-number" : "Tal", + "type-switch" : "Kontakt", + "type-select" : "Vælg", + "type-radios" : "Radioknapper", + "type-datetime" : "Dato/tid", + "type-image" : "Billede", + "type-javascript" : "JavaScript", + "type-json" : "JSON", + "type-html" : "HTML", + "type-css" : "CSS", + "type-markdown" : "Markdown", + "type-color" : "Farve", + "type-color-settings" : "Farveindstillinger", + "type-font" : "Skrifttype", + "type-units" : "Enheder", + "type-icon" : "Ikon", + "type-fieldset" : "Feltelement", + "type-array" : "Array", + "type-html-section" : "HTML-sektion", + "group-title" : "Gruppetitel", + "no-properties" : "Ingen egenskaber konfigureret", + "add-property" : "Tilføj egenskab", + "property-settings" : "Egenskabsindstillinger", + "remove-property" : "Fjern egenskab", + "default-value" : "Standardværdi", + "value-required" : "Værdi kræves", + "number-settings" : "Talindstillinger", + "min" : "Min", + "max" : "Maks", + "step" : "Trin", + "selected-options-limit" : "Valgte valgmuligheder grænse", + "advanced-ui-settings" : "Avancerede UI-indstillinger", + "disable-on-property" : "Deaktiver ved egenskab", + "display-condition-function" : "Visningsbetingelsesfunktion", + "sub-label" : "Undertekst", + "vertical-divider-after" : "Lodret skillelinje efter", + "input-field-suffix" : "Suffiks i inputfelt", + "property-row-classes" : "Række-klasser", + "property-field-classes" : "Felt-klasser", + "not-unique-property-ids-error" : "Egenskabs-ID'er skal være unikke!", + "enable-multiple-select" : "Tillad flere valg", + "allow-empty-select-option" : "Tillad tom mulighed", + "select-options" : "Vælg valgmuligheder", + "not-unique-select-option-value-error" : "Valgmulighedsværdier skal være unikke!", + "value" : "Værdi", + "label" : "Etiket", + "add-option" : "Tilføj mulighed", + "no-options" : "Ingen valgmuligheder konfigureret", + "remove-option" : "Fjern mulighed", + "textarea-rows" : "Rækker i tekstområde", + "help-id" : "Hjælp-id", + "buttons-direction" : "Knappers retning", + "direction-row" : "Række", + "direction-column" : "Kolonne", + "radio-button-options" : "Radioknapmuligheder", + "datetime-type" : "Dato/tid felt-type", + "datetime-type-date" : "Dato", + "datetime-type-time" : "Tid", + "datetime-type-datetime" : "Dato/tid", + "enable-clear-button" : "Aktivér ryd-knap", + "html-section-settings" : "HTML-sektionsindstillinger", + "html-section-classes" : "HTML-sektionsklasser", + "html-section-content" : "HTML-sektionsindhold", + "array-item" : "Array-element", + "item-type" : "Elementtype", + "item-name" : "Elementnavn", + "no-items" : "Ingen elementer" + }, + "clear-form" : "Ryd formular", + "clear-form-prompt" : "Er du sikker på, at du vil fjerne alle formularens egenskaber?", + "import-form" : "Importér formular fra JSON", + "export-form" : "Eksportér formular til JSON", + "json-file" : "JSON-fil", + "json-content" : "JSON-indhold", + "invalid-form-json-file-error" : "Kan ikke importere formular fra JSON: Ugyldig JSON-datastruktur for formular." + }, + "asset-profile" : { + "asset-profile" : "Assetprofil", + "asset-profiles" : "Assetprofiler", + "all-asset-profiles" : "Alle", + "add" : "Tilføj assetprofil", + "edit" : "Rediger assetprofil", + "asset-profile-details" : "Assetprofildetaljer", + "no-asset-profiles-text" : "Ingen assetprofiler fundet", + "search" : "Søg assetprofiler", + "selected-asset-profiles" : "{ count, plural, =1 {1 assetprofil} other {# assetprofiler} } valgt", + "no-asset-profiles-matching" : "Ingen assetprofil matcher '{{entity}}'.", + "asset-profile-required" : "Assetprofil er påkrævet", + "idCopiedMessage" : "Assetprofil-ID er blevet kopieret til udklipsholderen", + "set-default" : "Gør assetprofil til standard", + "delete" : "Slet assetprofil", + "copyId" : "Kopiér assetprofil-ID", + "name-max-length" : "Navnet må være under 256 tegn", + "new-device-profile-name" : "Assetprofilnavn", + "new-device-profile-name-required" : "Assetprofilnavn er påkrævet.", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "image" : "Assetprofilbillede", + "description" : "Beskrivelse", + "default" : "Standard", + "default-rule-chain" : "Standardregelkæde", + "default-edge-rule-chain" : "Standard edge-regelkæde", + "default-edge-rule-chain-hint" : "Bruges på edge som regelkæde til behandling af indgående data for aktiver i denne assetprofil", + "mobile-dashboard" : "Mobil-dashboard", + "mobile-dashboard-hint" : "Bruges af mobilappen som dashboard for assetdetaljer", + "select-queue-hint" : "Vælg fra rullelisten.", + "delete-asset-profile-title" : "Er du sikker på, at du vil slette assetprofilen '{{assetProfileName}}'?", + "delete-asset-profile-text" : "Vær forsigtig, efter bekræftelse vil assetprofilen og alle relaterede data ikke kunne gendannes.", + "delete-asset-profiles-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 assetprofil} other {# assetprofiler} }?", + "delete-asset-profiles-text" : "Vær forsigtig, efter bekræftelse vil alle valgte assetprofiler blive fjernet og dataene ikke kunne gendannes.", + "set-default-asset-profile-title" : "Er du sikker på, at du vil gøre assetprofilen '{{assetProfileName}}' til standard?", + "set-default-asset-profile-text" : "Efter bekræftelse vil assetprofilen blive markeret som standard og anvendt til nye aktiver uden specifik profil.", + "no-asset-profiles-found" : "Ingen assetprofiler fundet.", + "create-new-asset-profile" : "Opret en ny!", + "create-asset-profile" : "Opret ny assetprofil", + "import" : "Importér assetprofil", + "export" : "Eksportér assetprofil", + "export-failed-error" : "Kan ikke eksportere assetprofil: {{error}}", + "asset-profile-file" : "Assetprofilfil", + "invalid-asset-profile-file-error" : "Kan ikke importere assetprofil: Ugyldig datastruktur." + }, + "device-profile" : { + "device-profile" : "Enhedsprofil", + "device-profiles" : "Enhedsprofiler", + "all-device-profiles" : "Alle", + "add" : "Tilføj enhedsprofil", + "edit" : "Rediger enhedsprofil", + "device-profile-details" : "Detaljer om enhedsprofil", + "no-device-profiles-text" : "Ingen enhedsprofiler fundet", + "search" : "Søg enhedsprofiler", + "selected-device-profiles" : "{ count, plural, =1 {1 enhedsprofil} other {# enhedsprofiler} } valgt", + "no-device-profiles-matching" : "Ingen enhedsprofil matcher '{{entity}}'.", + "device-profile-required" : "Enhedsprofil er påkrævet", + "idCopiedMessage" : "Enhedsprofil-ID er blevet kopieret til udklipsholderen", + "set-default" : "Gør enhedsprofil til standard", + "delete" : "Slet enhedsprofil", + "copyId" : "Kopiér enhedsprofil-ID", + "name-max-length" : "Navnet må være under 256 tegn", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "type" : "Profiltype", + "type-required" : "Profiltype er påkrævet.", + "type-default" : "Standard", + "image" : "Enhedsprofilbillede", + "transport-type" : "Transporttype", + "transport-type-required" : "Transporttype er påkrævet.", + "transport-type-default" : "Standard", + "transport-type-default-hint" : "Understøtter grundlæggende MQTT, HTTP og CoAP transport", + "transport-type-mqtt" : "MQTT", + "transport-type-mqtt-hint" : "Aktiverer avancerede MQTT transportindstillinger", + "transport-type-coap" : "CoAP", + "transport-type-coap-hint" : "Aktiverer avancerede CoAP transportindstillinger", + "transport-type-lwm2m" : "LWM2M", + "transport-type-lwm2m-hint" : "LWM2M transporttype", + "transport-type-snmp" : "SNMP", + "transport-type-snmp-hint" : "Angiv SNMP transportkonfiguration", + "transport-type-http" : "HTTP", + "description" : "Beskrivelse", + "default" : "Standard", + "profile-configuration" : "Profilkonfiguration", + "transport-configuration" : "Transportkonfiguration", + "default-rule-chain" : "Standardregelkæde", + "default-edge-rule-chain" : "Standard edge-regelkæde", + "default-edge-rule-chain-hint" : "Bruges på edge som regelkæde til at behandle indgående data for enheder med denne enhedsprofil", + "mobile-dashboard" : "Mobil-dashboard", + "mobile-dashboard-hint" : "Bruges af mobilapplikationen som enhedens detaljer-dashboard", + "select-queue-hint" : "Vælg fra rullemenuen.", + "delete-device-profile-title" : "Er du sikker på, at du vil slette enhedsprofilen '{{deviceProfileName}}'?", + "delete-device-profile-text" : "Vær forsigtig, efter bekræftelse vil enhedsprofilen og alle relaterede data, inklusive OTA-opdateringer, ikke kunne gendannes.", + "delete-device-profiles-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 enhedsprofil} other {# enhedsprofiler} }?", + "delete-device-profiles-text" : "Vær forsigtig, efter bekræftelse vil alle valgte enhedsprofiler og relaterede data, inklusiv OTA-opdateringer, ikke kunne gendannes.", + "set-default-device-profile-title" : "Er du sikker på, at du vil gøre enhedsprofilen '{{deviceProfileName}}' til standard?", + "set-default-device-profile-text" : "Efter bekræftelse vil enhedsprofilen blive markeret som standard og brugt til nye enheder uden angivet profil.", + "no-device-profiles-found" : "Ingen enhedsprofiler fundet.", + "create-new-device-profile" : "Opret en ny!", + "mqtt-device-topic-filters" : "MQTT enheds-topic-filtre", + "mqtt-device-topic-filters-unique" : "MQTT enheds-topic-filtre skal være unikke.", + "mqtt-device-topic-filters-spark-plug" : "MQTT Sparkplug B Edge of Network (EoN) node.", + "mqtt-device-topic-filters-spark-plug-hint" : "Tillad forbindelser fra EoN noder med Sparkplug B payload og topic-format.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names" : "SparkPlug-metrikker der skal gemmes som attributter.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint" : "Navne på SparkPlug-metrikker, der vil blive gemt som enhedsattributter. Alle andre metrikker vil blive gemt som telemetri.", + "mqtt-device-payload-type" : "MQTT enhedspayload", + "mqtt-device-payload-type-json" : "JSON", + "mqtt-device-payload-type-proto" : "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format" : "Aktiver kompatibilitet med andre payloadformater.", + "mqtt-enable-compatibility-with-json-payload-format-hint" : "Når aktiveret, bruges Protobuf som standard payloadformat. Hvis parsing fejler, forsøges JSON. Nyttigt til bagudkompatibilitet under firmwareopdateringer. Kompatibilitetstilstand kan reducere ydeevne en smule.", + "mqtt-use-json-format-for-default-downlink-topics" : "Brug JSON-format til standard downlink-topics", + "mqtt-use-json-format-for-default-downlink-topics-hint" : "Ved aktivering bruges JSON-payload til push af attributter og RPC via standard topics. Har ingen effekt på nye (v2) topics.", + "mqtt-send-ack-on-validation-exception" : "Send PUBACK ved valideringsfejl af PUBLISH-besked", + "mqtt-send-ack-on-validation-exception-hint" : "Som standard afsluttes MQTT-session ved valideringsfejl. Ved aktivering sendes bekræftelse i stedet.", + "snmp-add-mapping" : "Tilføj SNMP-kortlægning", + "snmp-mapping-not-configured" : "Ingen kortlægning fra OID til tidsserier/telemetri konfigureret", + "snmp-timseries-or-attribute-name" : "Tidsserie-/attributnavn til kortlægning", + "snmp-timseries-or-attribute-type" : "Tidsserie-/attributtype til kortlægning", + "snmp-method-pdu-type-get-request" : "GetRequest", + "snmp-method-pdu-type-get-next-request" : "GetNextRequest", + "snmp-oid" : "OID", + "transport-device-payload-type-json" : "JSON", + "transport-device-payload-type-proto" : "Protobuf", + "mqtt-payload-type-required" : "Payloadtype er påkrævet.", + "coap-device-type" : "CoAP enhedstype", + "coap-device-payload-type" : "CoAP enhedspayload", + "coap-device-type-required" : "CoAP enhedstype er påkrævet.", + "coap-device-type-default" : "Standard", + "coap-device-type-efento" : "Efento NB-IoT", + "support-level-wildcards" : "Enkelt [+] og multi-niveau [#] jokertegn understøttes.", + "telemetry-topic-filter" : "Telemetri-topicfilter", + "telemetry-topic-filter-required" : "Telemetri-topicfilter er påkrævet.", + "attributes-topic-filter" : "Attributters publicerings-topicfilter", + "attributes-subscribe-topic-filter" : "Attributters abonnements-topicfilter", + "attributes-topic-filter-required" : "Attributters publicerings-topicfilter er påkrævet.", + "attributes-subscribe-topic-filter-required" : "Attributters abonnements-topic er påkrævet", + "telemetry-proto-schema" : "Telemetri proto-skema", + "telemetry-proto-schema-required" : "Telemetri proto-skema er påkrævet.", + "attributes-proto-schema" : "Attributter proto-skema", + "attributes-proto-schema-required" : "Attributter proto-skema er påkrævet.", + "rpc-response-proto-schema" : "RPC-svar proto-skema", + "rpc-response-proto-schema-required" : "RPC-svar proto-skema er påkrævet.", + "rpc-response-topic-filter" : "RPC-svar topicfilter", + "rpc-response-topic-filter-required" : "RPC-svar topicfilter er påkrævet.", + "rpc-request-proto-schema" : "RPC-anmodning proto-skema", + "rpc-request-proto-schema-required" : "RPC-anmodning proto-skema er påkrævet.", + "rpc-request-proto-schema-hint" : "RPC-anmodningsbeskeden skal altid indeholde felterne: string method = 1; int32 requestId = 2; og params = 3 af enhver datatype.", + "not-valid-pattern-topic-filter" : "Ugyldigt mønster i topicfilter", + "not-valid-single-character" : "Ugyldig brug af enkelt-niveau jokertegn", + "not-valid-multi-character" : "Ugyldig brug af multi-niveau jokertegn", + "single-level-wildcards-hint" : "[+] kan bruges på alle niveauer i topicfilter. Eksempel: v1/devices/+/telemetry eller +/devices/+/attributes.", + "multi-level-wildcards-hint" : "[#] kan erstatte hele topicfilteret og skal være det sidste symbol. Eksempel: # eller v1/devices/me/#.", + "alarm-rules" : "Alarmregler", + "alarm-rules-with-count" : "Alarmregler ({{count}})", + "no-alarm-rules" : "Ingen alarmregler konfigureret", + "add-alarm-rule" : "Tilføj alarmregel", + "edit-alarm-rule" : "Rediger alarmregel", + "alarm-type" : "Alarmtype", + "alarm-type-required" : "Alarmtype er påkrævet.", + "alarm-type-unique" : "Alarmtype skal være unik inden for enhedsprofilens alarmregler.", + "alarm-type-max-length" : "Alarmtype må være under 256 tegn", + "create-alarm-pattern" : "Opret {{alarmType}} alarm", + "create-alarm-rules" : "Opret alarmregler", + "no-create-alarm-rules" : "Ingen oprettelsesbetingelser konfigureret", + "add-create-alarm-rule-prompt" : "Tilføj venligst oprettelsesregel for alarm", + "clear-alarm-rule" : "Fjern alarmregel", + "no-clear-alarm-rule" : "Ingen fjernbetingelse konfigureret", + "add-create-alarm-rule" : "Tilføj oprettelsesbetingelse", + "add-clear-alarm-rule" : "Tilføj fjernbetingelse", + "select-alarm-severity" : "Vælg alarmens alvorlighed", + "alarm-severity-required" : "Alvorlighed er påkrævet.", + "condition-duration" : "Betingelsens varighed", + "condition-duration-value" : "Varighedsværdi", + "condition-duration-time-unit" : "Tidsenhed", + "condition-duration-value-range" : "Varighedsværdi skal være mellem 1 og 2147483647.", + "condition-duration-value-pattern" : "Varighedsværdien skal være et heltal.", + "condition-duration-value-required" : "Varighedsværdi er påkrævet.", + "condition-duration-time-unit-required" : "Tidsenhed er påkrævet.", + "advanced-settings" : "Avancerede indstillinger", + "alarm-rule-additional-info" : "Yderligere info", + "edit-alarm-rule-additional-info" : "Rediger yderligere info", + "alarm-rule-additional-info-placeholder" : "Tilføj kommentarer og justeringer, som vises under Yderligere info i alarmdetaljer", + "alarm-rule-additional-info-hint" : "Tip: brug ${keyName} til at erstatte med attribut- eller telemetrinøgleværdier, der bruges i alarmbetingelsen.", + "alarm-rule-mobile-dashboard" : "Mobil-dashboard", + "alarm-rule-mobile-dashboard-hint" : "Bruges af mobilapplikationen som dashboard for alarmdetaljer", + "alarm-rule-no-mobile-dashboard" : "Intet dashboard valgt", + "propagate-alarm" : "Videregiv alarm til relaterede enheder", + "alarm-rule-relation-types-list" : "Relationstyper", + "alarm-rule-relation-types-list-hint" : "Angiver relationstyper til at filtrere relaterede enheder. Hvis tomt, videregives alarmen til alle relaterede enheder.", + "propagate-alarm-to-owner" : "Videregiv alarm til enhedens ejer (Kunde eller Lejer)", + "propagate-alarm-to-tenant" : "Videregiv alarm til Lejer", + "alarm-rule-condition" : "Alarmregelbetingelse", + "enter-alarm-rule-condition-prompt" : "Tilføj venligst alarmregelbetingelse", + "edit-alarm-rule-condition" : "Rediger alarmregelbetingelse", + "device-provisioning" : "Enhedsregistrering", + "provision-strategy" : "Registreringsstrategi", + "provision-strategy-required" : "Registreringsstrategi er påkrævet.", + "provision-strategy-disabled" : "Deaktiveret", + "provision-strategy-created-new" : "Tillad oprettelse af nye enheder", + "provision-strategy-check-pre-provisioned" : "Tjek for forhåndsregistrerede enheder", + "provision-device-key" : "Registreringsnøgle", + "provision-device-key-required" : "Registreringsnøgle er påkrævet.", + "copy-provision-key" : "Kopiér registreringsnøgle", + "provision-key-copied-message" : "Registreringsnøgle er kopieret til udklipsholderen", + "provision-device-secret" : "Registreringshemmelighed", + "provision-device-secret-required" : "Registreringshemmelighed er påkrævet.", + "copy-provision-secret" : "Kopiér registreringshemmelighed", + "provision-secret-copied-message" : "Registreringshemmelighed er kopieret til udklipsholderen", + "provision-strategy-x509" : { + "certificate-chain" : "X509-certifikatkæde", + "certificate-chain-hint" : "X.509-certifikatstrategien bruges til at registrere enheder via klientcertifikater i tovej TLS-kommunikation.", + "allow-create-new-devices" : "Opret nye enheder", + "allow-create-new-devices-hint" : "Hvis valgt, vil nye enheder blive oprettet og klientcertifikatet vil blive brugt som enhedens legitimationsoplysninger.", + "certificate-value" : "Certifikat i PEM-format", + "certificate-value-required" : "Certifikat i PEM-format er påkrævet", + "cn-regex-variable" : "CN regulært udtryk-variabel", + "cn-regex-variable-required" : "CN regulært udtryk-variabel er påkrævet", + "cn-regex-variable-hint" : "Påkrævet for at hente enhedens navn fra Common Name i X509-certifikatet." + }, + "condition" : "Betingelse", + "condition-type" : "Betingelsestype", + "condition-type-simple" : "Simpel", + "condition-type-duration" : "Varighed", + "condition-during" : "I løbet af {{during}}", + "condition-during-dynamic" : "I løbet af \"{{ attribute }}\" ({{during}})", + "condition-type-repeating" : "Gentagelse", + "condition-type-required" : "Betingelsestype er påkrævet.", + "condition-repeating-value" : "Antal hændelser", + "condition-repeating-value-range" : "Antal hændelser skal være mellem 1 og 2147483647.", + "condition-repeating-value-pattern" : "Antal hændelser skal være heltal.", + "condition-repeating-value-required" : "Antal hændelser er påkrævet.", + "condition-repeat-times" : "Gentager { count, plural, =1 {1 gang} other {# gange} }", + "condition-repeat-times-dynamic" : "Gentager \"{ attribute }\" ({ count, plural, =1 {1 gang} other {# gange} })", + "schedule-type" : "Skematype", + "schedule-type-required" : "Skematype er påkrævet.", + "schedule" : "Skema", + "edit-schedule" : "Rediger alarmskema", + "schedule-any-time" : "Aktiv hele tiden", + "schedule-specific-time" : "Aktiv på specifikt tidspunkt", + "schedule-custom" : "Brugerdefineret", + "schedule-day" : { + "monday" : "Mandag", + "tuesday" : "Tirsdag", + "wednesday" : "Onsdag", + "thursday" : "Torsdag", + "friday" : "Fredag", + "saturday" : "Lørdag", + "sunday" : "Søndag" }, - "operation": { - "operation": "Driftsopgave", - "operations": "Driftsopgaver", - "operations-required": "Der skal angives mindst én driftsopgave.", - "enter-operation": "Indtast driftsopgave.", - "no-operations-matching": "", - "display-type": { - "ALL": "Alle", - "CREATE": "Opret", - "READ": "Læs", - "WRITE": "Skriv", - "DELETE": "Slet", - "ASSIGN_TO_CUSTOMER": "Tildel til kunde", - "UNASSIGN_FROM_CUSTOMER": "Fjern tildeling fra kunde", - "RPC_CALL": "RPC-opkald", - "READ_CREDENTIALS": "Læs brugeroplysninger", - "WRITE_CREDENTIALS": "Skriv brugeroplysninger", - "READ_ATTRIBUTES": "Læs attributter", - "WRITE_ATTRIBUTES": "Skriv attributter", - "READ_TELEMETRY": "Læs telemetri", - "WRITE_TELEMETRY": "Skriv telemetri", - "CLAIM_DEVICES": "Gør krav på enheder", - "IMPERSONATE": "Efterlign", - "CHANGE_OWNER": "Skift ejer", - "ADD_TO_GROUP": "Tilføj til gruppe", - "REMOVE_FROM_GROUP": "Fjern fra gruppe", - "SHARE_GROUP": "Del gruppe", - "ASSIGN_TO_TENANT": "Tildel til lejer" + "schedule-days" : "Dage", + "schedule-time" : "Tidspunkt", + "schedule-time-from" : "Fra", + "schedule-time-to" : "Til", + "schedule-days-of-week-required" : "Mindst én ugedag skal vælges.", + "create-device-profile" : "Opret ny enhedsprofil", + "import" : "Importér enhedsprofil", + "export" : "Eksportér enhedsprofil", + "export-failed-error" : "Kan ikke eksportere enhedsprofil: {{error}}", + "device-profile-file" : "Enhedsprofilfil", + "invalid-device-profile-file-error" : "Kan ikke importere enhedsprofil: Ugyldig datastruktur.", + "power-saving-mode" : "Strømbesparende tilstand", + "power-saving-mode-type" : { + "default" : "Brug enhedsprofilens strømbesparende tilstand", + "psm" : "Strømbesparende tilstand (PSM)", + "drx" : "Diskontinuerlig modtagelse (DRX)", + "edrx" : "Udvidet diskontinuerlig modtagelse (eDRX)" + }, + "edrx-cycle" : "eDRX-cyklus", + "edrx-cycle-required" : "eDRX-cyklus er påkrævet.", + "edrx-cycle-pattern" : "eDRX-cyklus skal være et positivt heltal.", + "edrx-cycle-min" : "Minimum antal eDRX-cyklusser er {{ min }} sekunder.", + "paging-transmission-window" : "Paging Transmission Window", + "paging-transmission-window-required" : "Paging transmission window er påkrævet.", + "paging-transmission-window-pattern" : "Paging transmission window skal være et positivt heltal.", + "paging-transmission-window-min" : "Minimum paging transmission window er {{ min }} sekunder.", + "psm-activity-timer" : "PSM-aktivitetstimer", + "psm-activity-timer-required" : "PSM-aktivitetstimer er påkrævet.", + "psm-activity-timer-pattern" : "PSM-aktivitetstimeren skal være et positivt heltal.", + "psm-activity-timer-min" : "Minimum PSM-aktivitetstimer er {{ min }} sekunder.", + "lwm2m" : { + "object-list" : "Objektliste", + "object-list-empty" : "Ingen objekter valgt.", + "no-objects-found" : "Ingen objekter fundet.", + "no-objects-matching" : "Ingen objekter matcher '{{object}}'.", + "model-tab" : "LWM2M-model", + "add-new-instances" : "Tilføj nye instanser", + "instances-list" : "Instansliste", + "instances-list-required" : "Instansliste er påkrævet.", + "instance-id-pattern" : "Instans-ID skal være et positivt heltal.", + "instance-id-max" : "Maksimal instans-ID værdi {{max}}.", + "instance" : "Instans", + "resource-label" : "#ID Ressourcenavn", + "observe-label" : "Observer", + "attribute-label" : "Attribut", + "telemetry-label" : "Telemetri", + "edit-observe-select" : "For at redigere observering, vælg telemetri eller attribut", + "edit-attributes-select" : "For at redigere attributter, vælg telemetri eller attribut", + "no-attributes-set" : "Ingen attributter angivet", + "key-name" : "Nøglenavn", + "key-name-required" : "Nøglenavn er påkrævet", + "attribute-name" : "Attributnavn", + "attribute-name-required" : "Attributnavn er påkrævet.", + "attribute-value" : "Attributværdi", + "attribute-value-required" : "Attributværdi er påkrævet.", + "attribute-value-pattern" : "Attributværdi skal være et positivt heltal.", + "edit-attributes" : "Rediger attributter: {{ name }}", + "view-attributes" : "Vis attributter: {{ name }}", + "add-attribute" : "Tilføj attribut", + "edit-attribute" : "Rediger attribut", + "view-attribute" : "Vis attribut", + "remove-attribute" : "Fjern attribut", + "delete-server-text" : "Vær forsigtig, efter bekræftelse vil serverkonfigurationen ikke kunne gendannes.", + "delete-server-title" : "Er du sikker på, at du vil slette serveren?", + "mode" : "Sikkerhedskonfigurationsmetode", + "bootstrap-tab" : "Bootstrap", + "bootstrap-server-legend" : "Bootstrap-server (ShortId...)", + "lwm2m-server-legend" : "LwM2M-server (ShortId...)", + "server" : "Server", + "short-id" : "Kort server-ID", + "short-id-tooltip" : "Serverens korte ID. Bruges til at linke serverobjektinstans.\nIdentificerer entydigt hver konfigureret LwM2M-server til LwM2M-klienten.\nSkal angives, når Bootstrap-Server ressourcen er 'false'.\nID:0 og ID:65535 må ikke anvendes.", + "short-id-tooltip-bootstrap" : "Skal angives, når Bootstrap-Server ressourcen er 'false'.", + "short-id-required" : "Kort server-ID er påkrævet.", + "short-id-range" : "Kort server-ID skal være mellem {{ min }} og {{ max }}.", + "short-id-pattern" : "Kort server-ID skal være et positivt heltal.", + "lifetime" : "Klientens registreringslevetid", + "lifetime-required" : "Registreringslevetid er påkrævet.", + "lifetime-pattern" : "Registreringslevetid skal være et positivt heltal.", + "default-min-period" : "Minimumsperiode mellem to notifikationer (s)", + "default-min-period-tooltip" : "Standardværdi for Minimumsperiode for observering, hvis parameteren ikke er angivet.", + "default-min-period-required" : "Minimumsperiode er påkrævet.", + "default-min-period-pattern" : "Minimumsperiode skal være et positivt heltal.", + "notification-storing" : "Gem notifikationer ved deaktivering eller offline", + "binding" : "Binding", + "binding-type" : { + "u" : "U: Klient tilgængelig via UDP-binding hele tiden.", + "m" : "M: Klient tilgængelig via MQTT-binding hele tiden.", + "h" : "H: Klient tilgængelig via HTTP-binding hele tiden.", + "t" : "T: Klient tilgængelig via TCP-binding hele tiden.", + "s" : "S: Klient tilgængelig via SMS-binding hele tiden.", + "n" : "N: Klient SKAL svare via Non-IP binding (understøttet fra LWM2M 1.1).", + "uq" : "UQ: UDP i køtilstand (ikke understøttet fra LWM2M 1.1)", + "uqs" : "UQS: Både UDP og SMS aktive; UDP i køtilstand, SMS i standardtilstand (ikke understøttet fra LWM2M 1.1)", + "tq" : "TQ: TCP i køtilstand (ikke understøttet fra LWM2M 1.1)", + "tqs" : "TQS: Både TCP og SMS aktive; TCP i køtilstand, SMS i standardtilstand (ikke understøttet fra LWM2M 1.1)", + "sq" : "SQ: SMS i køtilstand (ikke understøttet fra LWM2M 1.1)" + }, + "binding-tooltip" : "Angiver understøttede bindingstyper i klienten (/1/x/7). Bør matche værdien i “Supported Binding and Modes” i enhedsobjektet (/3/0/16). Kun én transportbinding kan bruges per session.", + "bootstrap-server" : "Bootstrap-server", + "lwm2m-server" : "LwM2M-server", + "include-bootstrap-server" : "Inkluder Bootstrap Server-opdateringer", + "bootstrap-update-title" : "Du har allerede konfigureret Bootstrap-server. Er du sikker på, at du vil udelukke opdateringerne?", + "bootstrap-update-text" : "Vær forsigtig, efter bekræftelse vil Bootstrap Server-konfigurationen ikke kunne gendannes.", + "server-host" : "Host", + "server-host-required" : "Host er påkrævet.", + "server-port" : "Port", + "server-port-required" : "Port er påkrævet.", + "server-port-pattern" : "Port skal være et positivt heltal.", + "server-port-range" : "Port skal være mellem 1 og 65535.", + "server-public-key" : "Serverens offentlige nøgle", + "server-public-key-required" : "Serverens offentlige nøgle er påkrævet.", + "client-hold-off-time" : "Hold off-tid", + "client-hold-off-time-required" : "Hold off-tid er påkrævet.", + "client-hold-off-time-pattern" : "Hold off-tid skal være et positivt heltal.", + "client-hold-off-time-tooltip" : "Klientens hold off-tid til brug med kun Bootstrap-server", + "account-after-timeout" : "Registrer efter timeout", + "account-after-timeout-required" : "Registrer efter timeout er påkrævet.", + "account-after-timeout-pattern" : "Registrer efter timeout skal være et positivt heltal.", + "account-after-timeout-tooltip" : "Bootstrap-server registrerer efter den timeout-værdi, der er angivet af denne ressource.", + "server-type" : "Servertype", + "add-new-server-title" : "Tilføj ny serverkonfiguration", + "add-server-config" : "Tilføj serverkonfiguration", + "add-lwm2m-server-config" : "Tilføj LwM2M-server", + "no-config-servers" : "Ingen servere konfigureret", + "others-tab" : "Andre indstillinger", + "client-strategy" : "Klientstrategi ved tilslutning", + "client-strategy-label" : "Strategi", + "client-strategy-only-observe" : "Send kun Observe-anmodning til klienten efter første forbindelse", + "client-strategy-read-all" : "Læs alle ressourcer og send Observe-anmodning til klienten efter registrering", + "fw-update" : "Firmwareopdatering", + "fw-update-strategy" : "Firmwareopdateringsstrategi", + "fw-update-strategy-data" : "Push firmware som binær fil via Object 19 og Resource 0 (Data)", + "fw-update-strategy-package" : "Push firmware som binær fil via Object 5 og Resource 0 (Package)", + "fw-update-strategy-package-uri" : "Generér automatisk unik CoAP-URL og push firmware via Object 5 og Resource 1 (Package URI)", + "sw-update" : "Softwareopdatering", + "sw-update-strategy" : "Softwareopdateringsstrategi", + "sw-update-strategy-package" : "Push binær fil via Object 9 og Resource 2 (Package)", + "sw-update-strategy-package-uri" : "Generér automatisk unik CoAP-URL og push software via Object 9 og Resource 3 (Package URI)", + "fw-update-resource" : "Firmware CoAP-ressource", + "fw-update-resource-required" : "Firmware CoAP-ressource er påkrævet.", + "sw-update-resource" : "Software CoAP-ressource", + "sw-update-resource-required" : "Software CoAP-ressource er påkrævet.", + "config-json-tab" : "JSON-konfiguration af enhedsprofil", + "attributes-name" : { + "min-period" : "Minimumsperiode", + "max-period" : "Maksimumsperiode", + "greater-than" : "Større end", + "less-than" : "Mindre end", + "step" : "Trin", + "min-evaluation-period" : "Minimum evalueringsperiode", + "max-evaluation-period" : "Maksimum evalueringsperiode" + }, + "default-object-id" : "Standardobjektversion (Attribut)", + "default-object-id-ver" : { + "v1-0" : "1.0", + "v1-1" : "1.1" } + }, + "snmp" : { + "add-communication-config" : "Tilføj kommunikationskonfiguration", + "add-mapping" : "Tilføj mapping", + "authentication-passphrase" : "Autentificeringsadgangskode", + "authentication-passphrase-required" : "Autentificeringsadgangskode er påkrævet.", + "authentication-protocol" : "Autentificeringsprotokol", + "authentication-protocol-required" : "Autentificeringsprotokol er påkrævet.", + "communication-configs" : "Kommunikationskonfigurationer", + "community" : "Community-streng", + "community-required" : "Community-streng er påkrævet.", + "context-name" : "Kontekstnavn", + "data-key" : "Datatast", + "data-key-required" : "Datatast er påkrævet.", + "data-type" : "Datatype", + "data-type-required" : "Datatype er påkrævet.", + "engine-id" : "Engine-ID", + "host" : "Host", + "host-required" : "Host er påkrævet.", + "oid" : "OID", + "oid-pattern" : "Ugyldigt OID-format.", + "oid-required" : "OID er påkrævet.", + "please-add-communication-config" : "Tilføj venligst kommunikationskonfiguration", + "please-add-mapping-config" : "Tilføj venligst mapping-konfiguration", + "port" : "Port", + "port-format" : "Ugyldigt portformat.", + "port-required" : "Port er påkrævet.", + "privacy-passphrase" : "Privatlivs-adgangskode", + "privacy-passphrase-required" : "Privatlivs-adgangskode er påkrævet.", + "privacy-protocol" : "Privatlivsprotokol", + "privacy-protocol-required" : "Privatlivsprotokol er påkrævet.", + "protocol-version" : "Protokolversion", + "protocol-version-required" : "Protokolversion er påkrævet.", + "querying-frequency" : "Forespørgselsfrekvens, ms", + "querying-frequency-invalid-format" : "Forespørgselsfrekvens skal være et positivt heltal.", + "querying-frequency-required" : "Forespørgselsfrekvens er påkrævet.", + "retries" : "Genforsøg", + "retries-invalid-format" : "Genforsøg skal være et positivt heltal.", + "retries-required" : "Genforsøg er påkrævet.", + "scope" : "Scope", + "scope-required" : "Scope er påkrævet.", + "security-name" : "Sikkerhedsnavn", + "security-name-required" : "Sikkerhedsnavn er påkrævet.", + "timeout-ms" : "Timeout, ms", + "timeout-ms-invalid-format" : "Timeout skal være et positivt heltal.", + "timeout-ms-required" : "Timeout er påkrævet.", + "user-name" : "Brugernavn", + "user-name-required" : "Brugernavn er påkrævet." } }, - "scheduler": { - "scheduler": "Planlægger", - "scheduler-event": "Planlægningsbegivenhed", - "select-scheduler-event": "Vælg planlægningsbegivenhed", - "no-scheduler-events-matching": "", - "scheduler-event-required": "Planlægningsbegivenhed er påkrævet", - "management": "Tidsplansstyring", - "scheduler-events": "Planlægningsbegivenheder", - "add-scheduler-event": "Tilfjøj planlægningsbegivenhed", - "search-scheduler-events": "Søg efter planlægningsbegivenheder", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "type": "Type", - "created_customer": "Oprettet af kunde", - "edit-scheduler-event": "Rediger planlægningsbegivenhed", - "view-scheduler-event": "Vis planlægningsbegivenhed", - "delete-scheduler-event": "Slet planlægningsbegivenhed", - "no-scheduler-events": "Ingen planlægningsbegivenheder fundet", - "selected-scheduler-events": "", - "delete-scheduler-event-title": "", - "delete-scheduler-event-text": "Vær forsigtig. Efter bekræftelsen vil planlægningsbegivenheden og alle relaterede data være uoprettelige.", - "delete-scheduler-events-title": "", - "delete-scheduler-events-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte planlægningsbegivenheder blive fjernet, og alle relaterede data vil være uoprettelige.", - "create": "Opret planlægningsbegivenhed", - "edit": "Rediger planlægningsbegivenhed", - "view": "Vis planlægningsbegivenhed", - "name-required": "Navn er påkrævet.", - "configuration": "Konfiguration", - "schedule": "Tidsplan", - "start-time": "Starttid", - "repeat": "Gentag", - "repeats": "Gentagelser", - "daily": "Daglig", - "weekly": "Ugentlig", - "monthly": "Månedlig", - "yearly": "Årlig", - "timer": "Timerbaseret", - "repeats-required": "Gentagelser er påkrævet.", - "repeat-on": "Gentag den", - "repeat-every": "Gentag hver", - "ends-on": "Slutter den", - "sunday-label": "L", - "monday-label": "M", - "tuesday-label": "T", - "wednesday-label": "O", - "thursday-label": "T", - "friday-label": "F", - "saturday-label": "L", - "repeat-on-sunday": "Gentag på søndag", - "repeat-on-monday": "Gentag på mandag", - "repeat-on-tuesday": "Gentag på tirsdag", - "repeat-on-wednesday": "Gentag på onsdag", - "repeat-on-thursday": "Gentag på torsdag", - "repeat-on-friday": "Gentag på fredag", - "repeat-on-saturday": "Gentag på lørdag", - "event-type": "Begivenhedstype", - "select-event-type": "Vælg begivenhedstype", - "event-type-required": "Begivenhedstype er påkrævet.", - "list-mode": "Listevisning", - "calendar-mode": "Kalendervisning", - "calendar-view-type": "Kalendervisningstype", - "month": "Måned", - "week": "Uge", - "day": "Dag", - "agenda-week": "Program for uge", - "agenda-day": "Program for dag", - "list-year": "Liste for år", - "list-month": "Liste for måned", - "list-week": "Liste for uge", - "list-day": "Liste for dag", - "today": "I dag", - "navigate-before": "Naviger før", - "navigate-next": "Naviger næste", - "starting-from": "Starter fra", - "until": "til og med", - "on": "den", - "sunday": "Søndag", - "monday": "Mandag", - "tuesday": "Tirsdag", - "wednesday": "Onsdag", - "thursday": "Torsdag", - "friday": "Fredag", - "saturday": "Lørdag", - "originator": "Ophavsmand", - "single-entity": "Enkelt entitet", - "group-of-entities": "Gruppe af entiteter", - "entities-group-owner": "Ejer af gruppe af entiteter", - "single-device": "Enkelt enhed", - "group-of-devices": "Gruppe af enheder", - "devices-group-owner": "Ejer af gruppe af enheder", - "message-body": "Meddelelsestekst", - "target": "Mål", - "rpc-method": "Metode", - "rpc-method-required": "Metode er påkrævet", - "rpc-params": "Parametre", - "select-dashboard-state": "Vælg dashboardtilstand", - "hours": "Timer", - "minutes": "Minutter", - "seconds": "Sekunder", - "time-interval-required": "Tidsinterval er påkrævet", - "time-unit-required": "Tidsenhed er påkrævet", - "every-hour": "", - "every-minute": "", - "every-second": "" - }, - "report": { - "report-config": "Rapportkonfiguration", - "email-config": "E-mailkonfiguration", - "dashboard-state-param": "Parameterværdi for dashboardtilstand", - "base-url": "Basis-URL", - "base-url-required": "Basis-URL er påkrævet.", - "use-dashboard-timewindow": "Brug dashboardtidsvindue", - "timewindow": "Tidsvindue", - "name-pattern": "Rapportnavnsmønster", - "name-pattern-required": "Rapportnavnsmønster er påkrævet", - "type": "Rapporttype", - "use-current-user-credentials": "Brug aktuelle brugeroplysninger", - "customer-user-credentials": "Kundens brugeroplysninger", - "customer-user-credentials-required": "Kundens brugeroplysninger er påkrævet", - "generate-test-report": "Generer testrapport", - "send-email": "Send e-mail", - "from": "Fra", - "from-required": "Fra er påkrævet.", - "to": "Til", - "to-required": "Til er påkrævet.", - "cc": "Cc", - "bcc": "Bcc", - "subject": "Emne", - "subject-required": "Emne er påkrævet.", - "body": "Tekst", - "body-required": "Tekst er påkrævet." - }, - "blob-entity": { - "blob-entity": "Blob-entitet", - "select-blob-entity": "Vælg blob-entitet", - "no-blob-entities-matching": "", - "blob-entity-required": "Blob-entitet er påkrævet", - "files": "Filer", - "search": "Søg efter filer", - "clear-search": "Ryd søgning", - "no-blob-entities-prompt": "Ingen filer fundet", - "report": "Rapport", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "type": "Type", - "created_customer": "Oprettet af kunde", - "selected-blob-entities": "", - "download-blob-entity": "Download fil", - "delete-blob-entity": "Slet fil", - "delete-blob-entity-title": "", - "delete-blob-entity-text": "Vær forsigtig. Efter bekræftelsen vil fildata være uoprettelige.", - "delete-blob-entities-title": "", - "delete-blob-entities-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte filer blive fjernet, og alle relaterede data vil være uoprettelige." - }, - "timezone": { - "timezone": "Tidszone", - "select-timezone": "Vælg tidszone", - "no-timezones-matching": "", - "timezone-required": "Tidszone er påkrævet.", - "browser-time": "Browsertid" - }, - "queue": { - "select_name": "Vælg kønavn", - "name": "Kønavn", - "name_required": "Kønavn er påkrævet." - }, - "tenant": { - "tenant": "Lejer", - "tenants": "Lejere", - "management": "Administration af lejer", - "add": "Tilføj lejer", - "admins": "Admin.", - "manage-tenant-admins": "Administrer lejeradmin.", - "delete": "Slet lejer", - "add-tenant-text": "Tilføj ny lejer", - "no-tenants-text": "Ingen lejere fundet", - "tenant-details": "Lejeroplysninger", - "delete-tenant-title": "", - "delete-tenant-text": "Vær forsigtig. Efter bekræftelsen vil lejeren og alle relaterede data være uoprettelige.", - "delete-tenants-title": "", - "delete-tenants-action-title": "", - "delete-tenants-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte lejere blive fjernet, og alle relaterede data vil være uoprettelige.", - "title": "Titel", - "title-required": "Titel er påkrævet.", - "description": "Beskrivelse", - "details": "Oplysninger", - "events": "Begivenheder", - "copyId": "Kopiér lejer-id", - "idCopiedMessage": "Lejer-id er blevet kopieret til udklipsholder", - "select-tenant": "Vælg lejer", - "no-tenants-matching": "", - "tenant-required": "Lejer er påkrævet", - "selected-tenants": "", - "search": "Søg efter lejere", - "allow-white-labeling": "Tillad hvid mærkning", - "allow-customer-white-labeling": "Tillad hvid mærkning af kunder", - "isolated-tb-core": "Behandling i isoleret ThingsBoard Core-beholder", - "isolated-tb-rule-engine": "Behandling i isoleret ThingsBoard regelmotor-beholder", - "isolated-tb-core-details": "Kræver separat(e) microserviceydelse(r) pr. isoleret lejer", - "isolated-tb-rule-engine-details": "Kræver separat(e) microserviceydelse(r) pr. isoleret lejer" - }, - "tenant-profile": { - "tenant-profile": "Lejerprofil", - "tenant-profiles": "Lejerprofiler", - "add": "Tilføj lejerprofil", - "edit": "Rediger lejerprofil", - "tenant-profile-details": "Oplysninger om lejerprofil", - "no-tenant-profiles-text": "Ingen lejerprofiler fundet", - "search": "Søg efter lejerprofiler", - "selected-tenant-profiles": "", - "no-tenant-profiles-matching": "", - "tenant-profile-required": "Lejerprofil er påkrævet", - "idCopiedMessage": "Lejerprofil-id er blevet kopieret til udklipsholder", - "set-default": "Gør lejerprofil til standard", - "delete": "Slet lejerprofil", - "copyId": "Kopiér lejerprofil-id", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "data": "Profildata", - "profile-configuration": "Profilkonfiguration", - "description": "Beskrivelse", - "default": "Standard", - "delete-tenant-profile-title": "", - "delete-tenant-profile-text": "Vær forsigtig. Efter bekræftelsen vil lejerprofilen og alle relaterede data være uoprettelige.", - "delete-tenant-profiles-title": "", - "delete-tenant-profiles-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte lejerprofiler blive fjernet, og alle relaterede data vil være uoprettelige.", - "set-default-tenant-profile-title": "", - "set-default-tenant-profile-text": "Efter bekræftelsen vil lejerprofilen blive markeret som standard og vil blive brugt til nye lejere, hvor der ikke er angivet nogen profil.", - "no-tenant-profiles-found": "Ingen lejerprofiler fundet.", - "create-new-tenant-profile": "Opret en ny!", - "maximum-devices": "Maks. antal enheder (0 – ubegrænset)", - "maximum-devices-required": "Maks. antal enheder er påkrævet.", - "maximum-devices-range": "Min. antal enheder kan ikke være negativt", - "maximum-assets": "Maks. antal aktiver (0 – ubegrænset)", - "maximum-assets-required": "Maks. antal aktiver er påkrævet.", - "maximum-assets-range": "Maks. antal aktiver kan ikke være negativt", - "maximum-customers": "Maks. antal kunder (0 – ubegrænset)", - "maximum-customers-required": "Maks. antal kunder er påkrævet.", - "maximum-customers-range": "Maks. antal kunder kan ikke være negativt", - "maximum-users": "Maks. antal brugere (0 – ubegrænset)", - "maximum-users-required": "Maks. antal brugere er påkrævet.", - "maximum-users-range": "Maks. antal brugere kan ikke være negativt", - "maximum-dashboards": "Maks. antal dashboards (0 – ubegrænset)", - "maximum-dashboards-required": "Maks. antal dashboards er påkrævet.", - "maximum-dashboards-range": "Maks. antal dashboards kan ikke være negativt", - "maximum-edges": "Maks. antal edges (0 – ubegrænset)", - "maximum-edges-required": "Maks. antal edges er påkrævet.", - "maximum-edges-range": "Maks. antal edges kan ikke være negativt", - "maximum-rule-chains": "Maks. antal regelkæder (0 – ubegrænset)", - "maximum-rule-chains-required": "Maks. antal regelkæder er påkrævet.", - "maximum-rule-chains-range": "Maks. antal regelkæder kan ikke være negativt", - "maximum-integrations": "Maks. antal integrationer (0 – ubegrænset)", - "maximum-integrations-required": "Maks. antal integrationer er påkrævet.", - "maximum-integrations-range": "Maks. antal integrationer kan ikke være negativt", - "maximum-converters": "Maks. antal omformere (0 – ubegrænset)", - "maximum-converters-required": "Maks. antal omformere er påkrævet.", - "maximum-converters-range": "Maks. antal omformere kan ikke være negativt", - "maximum-scheduler-events": "Maks. antal planlægningsbegivenheder (0 – ubegrænset)", - "maximum-scheduler-events-required": "Maks. antal planlægningsbegivenheder er påkrævet.", - "maximum-scheduler-events-range": "Maks. antal planlægningsbegivenheder kan ikke være negativt", - "transport-tenant-telemetry-msg-rate-limit": "Hastighedsgrænse for transport af lejertelemetrimeddelelser.", - "transport-tenant-telemetry-data-points-rate-limit": "Hastighedsgrænse for transport af lejertelemetridatapunkter.", - "transport-device-msg-rate-limit": "Hastighedsgrænse for transport af enhedsmeddelelser.", - "transport-device-telemetry-msg-rate-limit": "Hastighedsgrænse for transport af enhedstelemetrimeddelelser.", - "transport-device-telemetry-data-points-rate-limit": "Hastighedsgrænse for transport af enhedstelemetridatapunkter.", - "max-transport-messages": "Maks. antal transportmeddelelser (0 – ubegrænset)", - "max-transport-messages-required": "Maks. antal transportmeddelelser er påkrævet.", - "max-transport-messages-range": "Maks. antal transportmeddelelser kan ikke være negativt", - "max-transport-data-points": "Maks. antal transportdatapunkter (0 – ubegrænset)", - "max-transport-data-points-required": "Maks. antal transportdatapunkter er påkrævet.", - "max-transport-data-points-range": "Maks. antal transportdatapunkter kan ikke være negativt", - "max-r-e-executions": "Maks. antal regelmotor-udførelser (0 – ubegrænset)", - "max-r-e-executions-required": "Maks. antal regelmotor-udførelser er påkrævet.", - "max-r-e-executions-range": "Maks. antal regelmotor-udførelser kan ikke være negativt", - "max-j-s-executions": "Maks. antal JavaScript-udførelser (0 – ubegrænset)", - "max-j-s-executions-required": "Maks. antal JavaScript-udførelser er påkrævet.", - "max-j-s-executions-range": "Maks. antal JavaScript-udførelser kan ikke være negativt", - "max-d-p-storage-days": "Maks. antal lagringsdage for datapunkter (0 – ubegrænset)", - "max-d-p-storage-days-required": "Maks. antal lagringsdage for datapunkter er påkrævet.", - "max-d-p-storage-days-range": "Maks. antal lagringsdage for datapunkter kan ikke være negativt", - "default-storage-ttl-days": "Standard lagringsdage for TTL (0 – ubegrænset)", - "default-storage-ttl-days-required": "Standard lagringsdage for TTL er påkrævet.", - "default-storage-ttl-days-range": "Standard lagringsdage for TTL kan ikke være negative", - "max-rule-node-executions-per-message": "Maks. antal regelknudeudførelser pr. meddelelse (0 – ubegrænset)", - "max-rule-node-executions-per-message-required": "Maks. antal regelknudeudførelser pr. meddelelse er påkrævet.", - "max-rule-node-executions-per-message-range": "Maks. antal regelknudeudførelser pr. meddelelse kan ikke være negativt", - "max-emails": "Maks. antal sendte e-mails (0 – ubegrænset)", - "max-emails-required": "Maks. antal sendte e-mails er påkrævet.", - "max-emails-range": "Maks. antal sendte e-mails kan ikke være negativt", - "max-sms": "Maks. antal sendte SMS'er (0 – ubegrænset)", - "max-sms-required": "Maks. antal sendte SMS'er er påkrævet.", - "max-sms-range": "Maks. antal sendte SMS'er kan ikke være negativt" - }, - "timeinterval": { - "seconds-interval": "{ seconds, plural, =1 {1 sekund} other {# sekunder} }", - "minutes-interval": "{ minutes, plural, =1 {1 minut} other {# minutter} }", - "hours-interval": "{ hours, plural, =1 {1 time} other {# timer} }", - "days-interval": "{ days, plural, =1 {1 dag} other {# dage} }", - "days": "Dage", - "hours": "Timer", - "minutes": "Minutter", - "seconds": "Sekunder", - "advanced": "Fremskreden", - "predefined": { - "yesterday": "I går", - "day-before-yesterday": "Dagen før i går", - "this-day-last-week": "Denne dag i sidste uge", - "previous-week": "Forrige uge (søn – lør)", - "previous-week-iso": "Forrige uge (man – søn)", - "previous-month": "Forrige måned", - "previous-year": "Forrige år", - "current-hour": "Aktuel time", - "current-day": "Aktuel dag", - "current-day-so-far": "Aktuel dag indtil nu", - "current-week": "Aktuel uge (søn – lør)", - "current-week-iso": "Aktuel uge (man – søn)", - "current-week-so-far": "Aktuel uge indtil videre (søn – lør)", - "current-week-iso-so-far": "Aktuel uge indtil videre (man– søn)", - "current-month": "Aktuel måned", - "current-month-so-far": "Aktuel måned indtil nu", - "current-year": "Aktuelt år", - "current-year-so-far": "Aktuelt år indtil nu" + "dialog" : { + "close" : "Luk dialog", + "error-message-title" : "Fejlmeddelelse:", + "error-details-title" : "Fejldetaljer" + }, + "direction" : { + "column" : "Kolonne", + "row" : "Række" + }, + "edge" : { + "edge" : "Edge", + "edge-instances" : "Edge-instanser", + "instances" : "Instanser", + "edge-file" : "Edge-fil", + "name-max-length" : "Navnet skal være mindre end 256 tegn", + "label-max-length" : "Etiketten skal være mindre end 256 tegn", + "type-max-length" : "Typen skal være mindre end 256 tegn", + "management" : "Edge-administration", + "no-edges-matching" : "Ingen edges matcher '{{entity}}'.", + "add" : "Tilføj edge", + "no-edges-text" : "Ingen edges fundet", + "edge-details" : "Edge-detaljer", + "add-edge-text" : "Tilføj ny edge", + "delete" : "Slet edge", + "delete-edge-title" : "Er du sikker på, at du vil slette edge '{{edgeName}}'?", + "delete-edge-text" : "Vær forsigtig, efter bekræftelse vil edge og alle relaterede data ikke kunne gendannes.", + "delete-edges-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 edge} other {# edges} }?", + "delete-edges-text" : "Vær forsigtig, efter bekræftelse vil alle valgte edges blive fjernet og alle relaterede data vil gå tabt.", + "name" : "Navn", + "name-starts-with" : "Edge-navn begynder med", + "name-required" : "Navn er påkrævet.", + "description" : "Beskrivelse", + "details" : "Detaljer", + "events" : "Hændelser", + "copy-id" : "Kopiér Edge ID", + "id-copied-message" : "Edge ID er kopieret til udklipsholder", + "sync" : "Synkroniser Edge", + "edge-required" : "Edge er påkrævet", + "edge-type" : "Edge-type", + "edge-type-required" : "Edge-type er påkrævet.", + "event-action" : "Hændelseshandling", + "entity-id" : "Enheds-ID", + "select-edge-type" : "Vælg edge-type", + "assign-to-customer" : "Tildel til kunde", + "assign-to-customer-text" : "Vælg kunden der skal tildeles edge(s)", + "assign-edge-to-customer" : "Tildel edge(s) til kunde", + "assign-edge-to-customer-text" : "Vælg edges der skal tildeles kunden", + "assignedToCustomer" : "Tildelt kunde", + "edge-public" : "Edge er offentlig", + "assigned-to-customer" : "Tildelt til: {{customerTitle}}", + "unassign-from-customer" : "Fjern tildeling fra kunde", + "unassign-edge-title" : "Er du sikker på, at du vil fjerne tildelingen af edge '{{edgeName}}'?", + "unassign-edge-text" : "Efter bekræftelse vil edge blive fjernet og ikke længere være tilgængelig for kunden.", + "unassign-edges-title" : "Er du sikker på, at du vil fjerne tildeling af { count, plural, =1 {1 edge} other {# edges} }?", + "unassign-edges-text" : "Efter bekræftelse vil alle valgte edges blive fjernet og ikke længere være tilgængelige for kunden.", + "make-public" : "Gør edge offentlig", + "make-public-edge-title" : "Er du sikker på, at du vil gøre edge '{{edgeName}}' offentlig?", + "make-public-edge-text" : "Efter bekræftelse vil edge og alle dets data blive offentlige og tilgængelige for andre.", + "make-private" : "Gør edge privat", + "public" : "Offentlig", + "make-private-edge-title" : "Er du sikker på, at du vil gøre edge '{{edgeName}}' privat?", + "make-private-edge-text" : "Efter bekræftelse vil edge og alle dets data blive private og ikke tilgængelige for andre.", + "import" : "Importér edge", + "install-connect-instructions" : "Installér & tilslut instruktioner", + "install-connect-instructions-edge-created" : "Edge oprettet! Tjek Installér & Tilslut instruktioner", + "loading-edge-instructions" : "Indlæser edge-instruktioner...", + "label" : "Etiket", + "load-entity-error" : "Data kunne ikke indlæses. Enheden er blevet slettet.", + "assign-new-edge" : "Tildel ny edge", + "unassign-from-edge" : "Fjern tildeling fra edge", + "edge-key" : "Edge-nøgle", + "copy-edge-key" : "Kopiér edge-nøgle", + "edge-key-copied-message" : "Edge-nøgle er kopieret til udklipsholder", + "edge-secret" : "Edge-hemmelighed", + "copy-edge-secret" : "Kopiér Edge-hemmelighed", + "edge-secret-copied-message" : "Edge-hemmelighed er kopieret til udklipsholder", + "manage-assets" : "Administrer aktiver", + "manage-devices" : "Administrer enheder", + "manage-entity-views" : "Administrer enhedsvisninger", + "manage-dashboards" : "Administrer dashboards", + "manage-rulechains" : "Administrer regelkæder", + "assets" : "Edge-aktiver", + "devices" : "Edge-enheder", + "entity-views" : "Edge-enhedsvisninger", + "dashboard" : "Edge-dashboard", + "dashboards" : "Edge-dashboards", + "rulechain-templates" : "Regelkædeskabeloner", + "edge-rulechain-templates" : "Edge-regelkædeskabeloner", + "rulechains" : "Edge-regelkæder", + "search" : "Søg edges", + "selected-edges" : "{ count, plural, =1 {1 edge} other {# edges} } valgt", + "any-edge" : "Enhver edge", + "no-edge-types-matching" : "Ingen edge-typer matcher '{{entitySubtype}}'.", + "edge-type-list-empty" : "Ingen edge-typer valgt.", + "edge-types" : "Edge-typer", + "enter-edge-type" : "Indtast edge-type", + "deployed" : "Deployet", + "pending" : "Afventer", + "downlinks" : "Downlinks", + "no-downlinks-prompt" : "Ingen downlinks fundet", + "sync-process-started-successfully" : "Synkroniseringsproces startet!", + "missing-related-rule-chains-title" : "Edge mangler tilknyttede regelkæder", + "missing-related-rule-chains-text" : "Tildelte regelkæder bruger regelnoder, som videresender til regelkæder, der ikke er tildelt denne edge.

Manglende regelkæder:
{{missingRuleChains}}", + "widget-datasource-error" : "Denne widget understøtter kun EDGE-enhed som datakilde", + "upgrade-instructions" : "Opgraderingsvejledning", + "connected" : "Forbundet", + "disconnected" : "Afbrudt" + }, + "edge-event" : { + "type-dashboard" : "Dashboard", + "type-asset" : "Aktiv", + "type-device" : "Enhed", + "type-device-profile" : "Enhedsprofil", + "type-asset-profile" : "Aktivprofil", + "type-entity-view" : "Enhedsvisning", + "type-alarm" : "Alarm", + "type-rule-chain" : "Regelkæde", + "type-rule-chain-metadata" : "Regelkæde-metadata", + "type-edge" : "Edge", + "type-user" : "Bruger", + "type-tenant" : "Lejer", + "type-tenant-profile" : "Lejerprofil", + "type-customer" : "Kunde", + "type-relation" : "Relation", + "type-widgets-bundle" : "Widget-pakke", + "type-widgets-type" : "Widget-type", + "type-admin-settings" : "Adminindstillinger", + "type-ota-package" : "OTA-pakke", + "type-queue" : "Kø", + "action-type-added" : "Tilføjet", + "action-type-deleted" : "Slettet", + "action-type-updated" : "Opdateret", + "action-type-post-attributes" : "Send attributter", + "action-type-attributes-updated" : "Attributter opdateret", + "action-type-attributes-deleted" : "Attributter slettet", + "action-type-timeseries-updated" : "Tidsserie opdateret", + "action-type-credentials-updated" : "Legitimationsoplysninger opdateret", + "action-type-assigned-to-customer" : "Tildelt kunde", + "action-type-unassigned-from-customer" : "Fjernet fra kunde", + "action-type-relation-add-or-update" : "Relation tilføjet eller opdateret", + "action-type-relation-deleted" : "Relation slettet", + "action-type-rpc-call" : "RPC-kald", + "action-type-alarm-ack" : "Alarm kvitteret", + "action-type-alarm-clear" : "Alarm ryddet", + "action-type-alarm-assigned" : "Alarm tildelt", + "action-type-alarm-unassigned" : "Alarm ikke tildelt", + "action-type-assigned-to-edge" : "Tildelt edge", + "action-type-unassigned-from-edge" : "Fjernet fra edge", + "action-type-credentials-request" : "Legitimationsanmodning", + "action-type-entity-merge-request" : "Fletningsanmodning for enhed" + }, + "error" : { + "unable-to-connect" : "Kan ikke oprette forbindelse til serveren! Kontrollér din internetforbindelse.", + "unhandled-error-code" : "Ubehandlet fejlkode: {{errorCode}}", + "unknown-error" : "Ukendt fejl" + }, + "entity" : { + "entity" : "Enhed", + "entities" : "Enheder", + "entities-count" : "Antal enheder", + "alarms-count" : "Antal alarmer", + "aliases" : "Enhedsaliasser", + "aliases-short" : "Aliasser", + "entity-alias" : "Enhedsalias", + "unable-delete-entity-alias-title" : "Kan ikke slette enhedsalias", + "unable-delete-entity-alias-text" : "Enhedsalias '{{entityAlias}}' kan ikke slettes, da det bruges af følgende widget(s):
{{widgetsList}}", + "duplicate-alias-error" : "Duplikeret alias fundet '{{alias}}'.
Enhedsaliasser skal være unikke inden for dashboardet.", + "missing-entity-filter-error" : "Filter mangler for alias '{{alias}}'.", + "configure-alias" : "Konfigurer alias '{{alias}}'", + "alias" : "Alias", + "alias-required" : "Enhedsalias er påkrævet.", + "remove-alias" : "Fjern enhedsalias", + "add-alias" : "Tilføj enhedsalias", + "edit-alias" : "Rediger enhed-alias", + "entity-list" : "Liste over enheder", + "entity-type" : "Enhedstype", + "entity-types" : "Enhedstyper", + "entity-type-list" : "Liste over enhedstyper", + "any-entity" : "Enhver enhed", + "add-entity-type" : "Tilføj enhedstype", + "enter-entity-type" : "Indtast enhedstype", + "no-entities-matching" : "Ingen enheder matcher '{{entity}}'.", + "no-entities-text" : "Ingen enheder fundet", + "no-entity-types-matching" : "Ingen enhedstyper matcher '{{entityType}}'.", + "name-starts-with" : "Navneudtryk", + "help-text" : "Brug '%' efter behov: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter" : "Brug filter", + "entity-list-empty" : "Ingen enheder valgt.", + "entity-type-list-required" : "Mindst én enhedstype skal vælges.", + "entity-name-filter-required" : "Filter for enhedsnavn er påkrævet.", + "entity-name-filter-no-entity-matched" : "Ingen enheder, der starter med '{{entity}}', blev fundet.", + "all-subtypes" : "Alle", + "select-entities" : "Vælg enheder", + "no-aliases-found" : "Ingen aliasser fundet.", + "no-alias-matching" : "'{{alias}}' ikke fundet.", + "create-new-alias" : "Opret en ny!", + "create-new" : "Opret ny", + "key" : "Nøgle", + "key-name" : "Nøglenavn", + "no-keys-found" : "Ingen nøgler fundet.", + "no-key-matching" : "'{{key}}' ikke fundet.", + "create-new-key" : "Opret en ny!", + "type" : "Type", + "type-required" : "Enhedstype er påkrævet.", + "type-device" : "Enhed", + "type-devices" : "Enheder", + "list-of-devices" : "{ count, plural, =1 {Én enhed} other {Liste over # enheder} }", + "device-name-starts-with" : "Enheder, hvis navne starter med '{{prefix}}'", + "type-device-profile" : "Enhedsprofil", + "type-device-profiles" : "Enhedsprofiler", + "clear-selected-profiles" : "Ryd valgte profiler", + "list-of-device-profiles" : "{ count, plural, =1 {Én enhedsprofil} other {Liste over # enhedsprofiler} }", + "device-profile-name-starts-with" : "Enhedsprofiler, hvis navne starter med '{{prefix}}'", + "type-asset-profile" : "Aktivprofil", + "type-asset-profiles" : "Aktivprofiler", + "list-of-asset-profiles" : "{ count, plural, =1 {Én aktivprofil} other {Liste over # aktivprofiler} }", + "asset-profile-name-starts-with" : "Aktivprofiler, hvis navne starter med '{{prefix}}'", + "type-asset" : "Aktiv", + "type-assets" : "Aktiver", + "list-of-assets" : "{ count, plural, =1 {Én aktiv} other {Liste over # aktiver} }", + "asset-name-starts-with" : "Aktiver, hvis navne starter med '{{prefix}}'", + "type-entity-view" : "Enhedsvisning", + "type-entity-views" : "Enhedsvisninger", + "list-of-entity-views" : "{ count, plural, =1 {Én enhedsvisning} other {Liste over # enhedsvisninger} }", + "entity-view-name-starts-with" : "Enhedsvisninger, hvis navne starter med '{{prefix}}'", + "type-rule" : "Regel", + "type-rules" : "Regler", + "list-of-rules" : "{ count, plural, =1 {Én regel} other {Liste over # regler} }", + "rule-name-starts-with" : "Regler, hvis navne starter med '{{prefix}}'", + "type-plugin" : "Plugin", + "type-plugins" : "Plugins", + "list-of-plugins" : "{ count, plural, =1 {Én plugin} other {Liste over # plugins} }", + "plugin-name-starts-with" : "Plugins, hvis navne starter med '{{prefix}}'", + "type-tenant" : "Lejer", + "type-tenants" : "Lejere", + "list-of-tenants" : "{ count, plural, =1 {Én lejer} other {Liste over # lejere} }", + "tenant-name-starts-with" : "Lejere, hvis navne starter med '{{prefix}}'", + "type-tenant-profile" : "Lejerprofil", + "type-tenant-profiles" : "Lejerprofiler", + "list-of-tenant-profiles" : "{ count, plural, =1 {Én lejerprofil} other {Liste over # lejerprofiler} }", + "tenant-profile-name-starts-with" : "Lejerprofiler, hvis navne starter med '{{prefix}}'", + "type-customer" : "Kunde", + "type-customers" : "Kunder", + "list-of-customers" : "{ count, plural, =1 {Én kunde} other {Liste over # kunder} }", + "customer-name-starts-with" : "Kunder, hvis navne starter med '{{prefix}}'", + "type-user" : "Bruger", + "type-users" : "Brugere", + "list-of-users" : "{ count, plural, =1 {Én bruger} other {Liste over # brugere} }", + "user-name-starts-with" : "Brugere, hvis navne starter med '{{prefix}}'", + "type-dashboard" : "Dashboard", + "type-dashboards" : "Dashboards", + "list-of-dashboards" : "{ count, plural, =1 {Én dashboard} other {Liste over # dashboards} }", + "dashboard-name-starts-with" : "Dashboards, hvis navne starter med '{{prefix}}'", + "type-alarm" : "Alarm", + "type-alarms" : "Alarmer", + "list-of-alarms" : "{ count, plural, =1 {Én alarm} other {Liste over # alarmer} }", + "alarm-name-starts-with" : "Alarmer, hvis navne starter med '{{prefix}}'", + "type-rulechain" : "Regelkæde", + "type-rulechains" : "Regelkæder", + "list-of-rulechains" : "{ count, plural, =1 {Én regelkæde} other {Liste over # regelkæder} }", + "rulechain-name-starts-with" : "Regelkæder, hvis navne starter med '{{prefix}}'", + "type-rulenode" : "Regelnode", + "type-rulenodes" : "Regelnoder", + "list-of-rulenodes" : "{ count, plural, =1 {Én regelnode} other {Liste over # regelnoder} }", + "rulenode-name-starts-with" : "Regelnoder, hvis navne starter med '{{prefix}}'", + "type-current-customer" : "Aktuel kunde", + "type-current-tenant" : "Aktuel lejer", + "type-current-user" : "Aktuel bruger", + "type-current-user-owner" : "Aktuel bruger-ejer", + "type-calculated-field" : "Beregnet felt", + "type-calculated-fields" : "Beregnet felter", + "type-widgets-bundle" : "Widgetpakke", + "type-widgets-bundles" : "Widgetpakker", + "list-of-widgets-bundles" : "{ count, plural, =1 {Én widgetpakke} other {Liste over # widgetpakker} }", + "type-widget" : "Widget", + "type-widgets" : "Widgets", + "list-of-widgets" : "{ count, plural, =1 {Én widget} other {Liste over # widgets} }", + "search" : "Søg i enheder", + "selected-entities" : "{ count, plural, =1 {1 enhed} other {# enheder} } valgt", + "entity-name" : "Enhedsnavn", + "entity-label" : "Enhedsetiket", + "details" : "Enhedsdetaljer", + "no-entities-prompt" : "Ingen enheder fundet", + "no-data" : "Ingen data at vise", + "columns-to-display" : "Kolonner at vise", + "type-api-usage-state" : "API-brugsstatus", + "type-edge" : "Edge", + "type-edges" : "Edges", + "list-of-edges" : "{ count, plural, =1 {Én edge} other {Liste over # edges} }", + "edge-name-starts-with" : "Edges, hvis navne starter med '{{prefix}}'", + "version-conflict" : { + "message" : "Vil du overskrive eksisterende version eller kassere ændringer og indlæse den nyeste version?", + "link" : "Du kan downloade din version af {{entityType}} ved hjælp af dette", + "overwrite" : "Overskriv version", + "discard" : "Kassér ændringer" + }, + "type-tb-resource" : "Ressource", + "type-tb-resources" : "Ressourcer", + "list-of-tb-resources" : "{ count, plural, =1 {Én ressource} other {Liste over # ressourcer} }", + "type-ota-package" : "OTA-pakke", + "type-rpc" : "RPC", + "type-queue" : "Kø", + "type-queue-stats" : "Køstatistik", + "type-queues-stats" : "Statistik over køer", + "type-notification" : "Notifikation", + "type-notification-rule" : "Notifikationsregel", + "type-notification-rules" : "Notifikationsregler", + "list-of-notification-rules" : "{ count, plural, =1 {Én notifikationsregel} other {Liste over # notifikationsregler} }", + "type-notification-target" : "Notifikationsmodtager", + "type-notification-targets" : "Notifikationsmodtagere", + "list-of-notification-targets" : "{ count, plural, =1 {Én notifikationsmodtager} other {Liste over # notifikationsmodtagere} }", + "type-notification-request" : "Notifikationsanmodning", + "type-notification-template" : "Notifikationsskabelon", + "type-notification-templates" : "Notifikationsskabeloner", + "list-of-notification-templates" : "{ count, plural, =1 {Én notifikationsskabelon} other {Liste over # notifikationsskabeloner} }", + "link" : "link", + "type-oauth2-client" : "OAuth 2.0 klient", + "type-oauth2-clients" : "OAuth 2.0 klienter", + "list-of-oauth2-clients" : "{ count, plural, =1 {Én OAuth 2.0 klient} other {Liste over # OAuth 2.0 klienter} }", + "type-domain" : "Domæne", + "type-domains" : "Domæner", + "list-of-domains" : "{ count, plural, =1 {Ét domæne} other {Liste over # domæner} }", + "type-mobile-app" : "Mobilapplikation", + "type-mobile-apps" : "Mobilapplikationer", + "list-of-mobile-apps" : "{ count, plural, =1 {Én mobilapplikation} other {Liste over # mobilapplikationer} }", + "type-mobile-app-bundle" : "Mobilpakke", + "type-mobile-app-bundles" : "Mobilpakker", + "list-of-mobile-app-bundles" : "{ count, plural, =1 {Én mobilpakke} other {Liste over # mobilpakker} }" + }, + "entity-field" : { + "created-time" : "Oprettelsestidspunkt", + "name" : "Navn", + "type" : "Type", + "first-name" : "Fornavn", + "last-name" : "Efternavn", + "email" : "Email", + "title" : "Titel", + "country" : "Land", + "state" : "Stat / Region", + "city" : "By", + "address" : "Adresse", + "address2" : "Adresse 2", + "zip" : "Postnummer", + "phone" : "Telefon", + "label" : "Etiket", + "queue-name" : "Kønavn", + "service-id" : "Service Id", + "owner-name" : "Ejerens navn", + "owner-type" : "Ejerens type" + }, + "entity-view" : { + "entity-view" : "Entitetsvisning", + "entity-view-required" : "Entitetsvisning er påkrævet.", + "entity-views" : "Entitetsvisninger", + "management" : "Håndtering af entitetsvisning", + "view-entity-views" : "Vis entitetsvisninger", + "entity-view-alias" : "Entitetsvisningsalias", + "aliases" : "Entitetsvisningsaliasser", + "no-alias-matching" : "'{{alias}}' blev ikke fundet.", + "no-aliases-found" : "Ingen aliasser fundet.", + "no-key-matching" : "'{{key}}' blev ikke fundet.", + "no-keys-found" : "Ingen nøgler fundet.", + "create-new-alias" : "Opret en ny!", + "create-new-key" : "Opret en ny!", + "duplicate-alias-error" : "Duplikeret alias fundet '{{alias}}'.
Entitetsvisningsaliasser skal være unikke i dashboardet.", + "configure-alias" : "Konfigurer aliaset '{{alias}}'", + "no-entity-views-matching" : "Ingen entitetsvisninger matchede '{{entity}}'.", + "public" : "Offentlig", + "alias" : "Alias", + "alias-required" : "Alias for entitetsvisning er påkrævet.", + "remove-alias" : "Fjern entitetsvisningsalias", + "add-alias" : "Tilføj entitetsvisningsalias", + "name-starts-with" : "Entitetsvisningsnavn udtryk", + "help-text" : "Brug '%' efter behov: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list" : "Entitetsvisningsliste", + "use-entity-view-name-filter" : "Brug filter", + "entity-view-list-empty" : "Ingen entitetsvisninger valgt.", + "entity-view-name-filter-required" : "Filter for entitetsvisningsnavn er påkrævet.", + "entity-view-name-filter-no-entity-view-matched" : "Ingen entitetsvisninger starter med '{{entityView}}'.", + "add" : "Tilføj entitetsvisning", + "entity-view-public" : "Entitetsvisning er offentlig", + "assign-to-customer" : "Tildel til kunde", + "assign-entity-view-to-customer" : "Tildel entitetsvisning(er) til kunde", + "assign-entity-view-to-customer-text" : "Vælg entitetsvisninger, der skal tildeles kunden", + "no-entity-views-text" : "Ingen entitetsvisninger fundet", + "assign-to-customer-text" : "Vælg den kunde, som entitetsvisning(en) skal tildeles", + "entity-view-details" : "Detaljer for entitetsvisning", + "add-entity-view-text" : "Tilføj ny entitetsvisning", + "delete" : "Slet entitetsvisning", + "assign-entity-views" : "Tildel entitetsvisninger", + "assign-entity-views-text" : "Tildel { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } til kunde", + "delete-entity-views" : "Slet entitetsvisninger", + "make-public" : "Gør entitetsvisning offentlig", + "make-private" : "Gør entitetsvisning privat", + "unassign-from-customer" : "Fjern fra kunde", + "unassign-entity-views" : "Fjern entitetsvisninger", + "unassign-entity-views-action-title" : "Fjern { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } fra kunde", + "assign-new-entity-view" : "Tildel ny entitetsvisning", + "delete-entity-view-title" : "Er du sikker på, at du vil slette entitetsvisningen '{{entityViewName}}'?", + "delete-entity-view-text" : "Vær forsigtig, efter bekræftelse vil entitetsvisningen og alle relaterede data ikke kunne gendannes.", + "delete-entity-views-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }?", + "delete-entity-views-action-title" : "Slet { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }", + "delete-entity-views-text" : "Vær forsigtig, efter bekræftelse vil alle valgte entitetsvisninger blive fjernet, og relaterede data vil ikke kunne gendannes.", + "make-public-entity-view-title" : "Er du sikker på, at du vil gøre entitetsvisningen '{{entityViewName}}' offentlig?", + "make-public-entity-view-text" : "Efter bekræftelse vil entitetsvisningen og alle dens data være offentlige og tilgængelige for andre.", + "make-private-entity-view-title" : "Er du sikker på, at du vil gøre entitetsvisningen '{{entityViewName}}' privat?", + "make-private-entity-view-text" : "Efter bekræftelse vil entitetsvisningen og alle dens data være private og ikke være tilgængelige for andre.", + "unassign-entity-view-title" : "Er du sikker på, at du vil fjerne tildelingen af entitetsvisningen '{{entityViewName}}'?", + "unassign-entity-view-text" : "Efter bekræftelse vil entitetsvisningen blive fjernet og ikke være tilgængelig for kunden.", + "unassign-entity-view" : "Fjern entitetsvisning", + "unassign-entity-views-title" : "Er du sikker på, at du vil fjerne { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }?", + "unassign-entity-views-text" : "Efter bekræftelse vil alle valgte entitetsvisninger blive fjernet og ikke være tilgængelige for kunden.", + "entity-view-type" : "Entitetsvisningstype", + "entity-view-type-required" : "Entitetsvisningstype er påkrævet.", + "select-entity-view-type" : "Vælg entitetsvisningstype", + "enter-entity-view-type" : "Indtast entitetsvisningstype", + "any-entity-view" : "Enhver entitetsvisning", + "no-entity-view-types-matching" : "Ingen entitetsvisningstyper matcher '{{entitySubtype}}'.", + "entity-view-type-list-empty" : "Ingen entitetsvisningstyper valgt.", + "entity-view-types" : "Entitetsvisningstyper", + "created-time" : "Oprettelsestidspunkt", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "name-max-length" : "Navn skal være mindre end 256 tegn", + "type-max-length" : "Entitetsvisningstype skal være mindre end 256 tegn", + "description" : "Beskrivelse", + "events" : "Hændelser", + "details" : "Detaljer", + "copyId" : "Kopiér entitetsvisnings-id", + "idCopiedMessage" : "Entitetsvisnings-id er blevet kopieret til udklipsholderen", + "assignedToCustomer" : "Tildelt til kunde", + "unable-entity-view-device-alias-title" : "Kan ikke slette entitetsvisningsalias", + "unable-entity-view-device-alias-text" : "Enhedsaliaset '{{entityViewAlias}}' kan ikke slettes, da det bruges af følgende widget(s):
{{widgetsList}}", + "select-entity-view" : "Vælg entitetsvisning", + "start-ts" : "Starttid", + "end-ts" : "Sluttid", + "date-limits" : "Datogrænser", + "client-attributes" : "Klientattributter", + "shared-attributes" : "Delte attributter", + "server-attributes" : "Serverattributter", + "timeseries" : "Tidsserier", + "client-attributes-placeholder" : "Klientattributter", + "shared-attributes-placeholder" : "Delte attributter", + "server-attributes-placeholder" : "Serverattributter", + "timeseries-placeholder" : "Tidsserier", + "target-entity" : "Målenhed", + "attributes-propagation" : "Attributter propagation", + "attributes-propagation-hint" : "Entitetsvisningen kopierer automatisk de specificerede attributter fra målenheden, hver gang du gemmer eller opdaterer denne entitetsvisning. Af hensyn til ydeevnen bliver attributterne ikke automatisk overført ved hver ændring. Du kan aktivere automatisk overførsel ved at konfigurere \"copy to view\" regelnode i din regelkæde og forbinde \"Post attributes\" og \"Attributes Updated\" beskeder til den nye regelnode.", + "timeseries-data" : "Tidsseriedata", + "timeseries-data-hint" : "Konfigurer tidsserie-nøgler fra målenheden, som vil være tilgængelige i entitetsvisningen. Disse data er skrivebeskyttede.", + "search" : "Søg i entitetsvisninger", + "selected-entity-views" : "{ count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } valgt", + "assign-entity-view-to-edge" : "Tildel entitetsvisning(er) til Edge", + "assign-entity-view-to-edge-text" : "Vælg de entitetsvisninger, der skal tildeles Edge", + "unassign-entity-view-from-edge-title" : "Er du sikker på, at du vil fjerne tildelingen af entitetsvisningen '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text" : "Efter bekræftelse vil entitetsvisningen blive fjernet og ikke være tilgængelig af Edge.", + "unassign-entity-views-from-edge-action-title" : "Fjern { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } fra Edge", + "unassign-entity-view-from-edge" : "Fjern entitetsvisning", + "unassign-entity-views-from-edge-title" : "Er du sikker på, at du vil fjerne { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }?", + "unassign-entity-views-from-edge-text" : "Efter bekræftelse vil alle valgte entitetsvisninger blive fjernet og ikke være tilgængelige af Edge." + }, + "event" : { + "event-type" : "Hændelsestype", + "events-filter" : "Filtrer hændelser", + "clean-events" : "Ryd hændelser", + "type-error" : "Fejl", + "type-lc-event" : "Livscyklushændelse", + "type-stats" : "Statistik", + "type-debug-rule-node" : "Fejlfinding", + "type-debug-rule-chain" : "Fejlfinding", + "type-debug-calculated-field" : "Fejlfinding", + "arguments" : "Argumenter", + "result" : "Resultat", + "no-events-prompt" : "Ingen hændelser fundet", + "error" : "Fejl", + "alarm" : "Alarm", + "event-time" : "Hændelsestidspunkt", + "server" : "Server", + "body" : "Indhold", + "method" : "Metode", + "type" : "Type", + "metadata" : "Metadata", + "message" : "Besked", + "message-id" : "Besked-id", + "copy-message-id" : "Kopiér besked-id", + "message-type" : "Beskedtype", + "data-type" : "Datatype", + "relation-type" : "Relationstype", + "data" : "Data", + "event" : "Hændelse", + "status" : "Status", + "success" : "Succes", + "failed" : "Mislykket", + "messages-processed" : "Behandlede beskeder", + "max-messages-processed" : "Maksimalt antal behandlede beskeder", + "min-messages-processed" : "Minimalt antal behandlede beskeder", + "errors-occurred" : "Opståede fejl", + "max-errors-occurred" : "Maksimalt antal fejl", + "min-errors-occurred" : "Minimalt antal fejl", + "min-value" : "Mindste værdi er 0.", + "all-events" : "Alle", + "has-error" : "Har fejl", + "entity-id" : "Enheds-id", + "copy-entity-id" : "Kopiér enheds-id", + "entity-type" : "Enhedstype", + "clear-filter" : "Ryd filter", + "clear-request-title" : "Ryd alle hændelser", + "clear-request-text" : "Er du sikker på, at du vil rydde alle hændelser?", + "started" : "Startet", + "updated" : "Opdateret", + "stopped" : "Stoppet" + }, + "extension" : { + "extensions" : "Udvidelser", + "selected-extensions" : "{ count, plural, =1 {1 udvidelse} other {# udvidelser} } valgt", + "type" : "Type", + "key" : "Nøgle", + "value" : "Værdi", + "id" : "Id", + "extension-id" : "Udvidelses-id", + "extension-type" : "Udvidelsestype", + "transformer-json" : "JSON *", + "unique-id-required" : "Nuværende udvidelses-id findes allerede.", + "delete" : "Slet udvidelse", + "add" : "Tilføj udvidelse", + "edit" : "Rediger udvidelse", + "delete-extension-title" : "Er du sikker på, at du vil slette udvidelsen '{{extensionId}}'?", + "delete-extension-text" : "Vær forsigtig, efter bekræftelsen vil udvidelsen og alle relaterede data ikke kunne gendannes.", + "delete-extensions-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 udvidelse} other {# udvidelser} }?", + "delete-extensions-text" : "Vær forsigtig, efter bekræftelsen vil alle valgte udvidelser blive fjernet.", + "converters" : "Konvertere", + "converter-id" : "Konverterings-id", + "configuration" : "Konfiguration", + "converter-configurations" : "Konverteringskonfigurationer", + "token" : "Sikkerhedstoken", + "add-converter" : "Tilføj konverter", + "add-config" : "Tilføj konverterkonfiguration", + "device-name-expression" : "Enhedsnavn udtryk", + "device-type-expression" : "Enhedstype udtryk", + "custom" : "Brugerdefineret", + "to-double" : "Til dobbelt", + "transformer" : "Transformer", + "json-required" : "Transformer JSON er påkrævet.", + "json-parse" : "Kan ikke fortolke transformer JSON.", + "attributes" : "Attributter", + "add-attribute" : "Tilføj attribut", + "add-map" : "Tilføj mappingelement", + "timeseries" : "Tidsserier", + "add-timeseries" : "Tilføj tidsserie", + "field-required" : "Felt er påkrævet", + "brokers" : "Mæglere", + "add-broker" : "Tilføj mægler", + "host" : "Vært", + "port" : "Port", + "port-range" : "Port skal være i området fra 1 til 65535.", + "ssl" : "SSL", + "credentials" : "Legitimationsoplysninger", + "username" : "Brugernavn", + "password" : "Adgangskode", + "retry-interval" : "Genforsøgsinterval i millisekunder", + "anonymous" : "Anonym", + "basic" : "Basis", + "pem" : "PEM", + "ca-cert" : "CA-certifikatfil *", + "private-key" : "Privat nøglefil *", + "cert" : "Certifikatfil *", + "no-file" : "Ingen fil valgt.", + "drop-file" : "Træk en fil her eller klik for at vælge en fil til upload.", + "mapping" : "Kortlægning", + "topic-filter" : "Emnefilter", + "converter-type" : "Konverteringstype", + "converter-json" : "JSON", + "json-name-expression" : "Enhedsnavn JSON-udtryk", + "topic-name-expression" : "Enhedsnavn emneudtryk", + "json-type-expression" : "Enhedstype JSON-udtryk", + "topic-type-expression" : "Enhedstype emneudtryk", + "attribute-key-expression" : "Attributnøgle udtryk", + "attr-json-key-expression" : "Attributnøgle JSON-udtryk", + "attr-topic-key-expression" : "Attributnøgle emneudtryk", + "request-id-expression" : "Anmodnings-id udtryk", + "request-id-json-expression" : "Anmodnings-id JSON-udtryk", + "request-id-topic-expression" : "Anmodnings-id emneudtryk", + "response-topic-expression" : "Svar emneudtryk", + "value-expression" : "Værdinudtryk", + "topic" : "Emne", + "timeout" : "Timeout i millisekunder", + "converter-json-required" : "Konverter JSON er påkrævet.", + "converter-json-parse" : "Kan ikke fortolke konverter JSON.", + "filter-expression" : "Filterudtryk", + "connect-requests" : "Forbindelsesforespørgsler", + "add-connect-request" : "Tilføj forbindelsesforespørgsel", + "disconnect-requests" : "Afbrydelsesforespørgsler", + "add-disconnect-request" : "Tilføj afbrydelsesforespørgsel", + "attribute-requests" : "Attributforespørgsler", + "add-attribute-request" : "Tilføj attributforespørgsel", + "attribute-updates" : "Attributopdateringer", + "add-attribute-update" : "Tilføj attributopdatering", + "server-side-rpc" : "RPC på serversiden", + "add-server-side-rpc-request" : "Tilføj RPC-forespørgsel på serversiden", + "device-name-filter" : "Filter for enhedsnavn", + "attribute-filter" : "Attributfilter", + "method-filter" : "Metodefilter", + "request-topic-expression" : "Forespørgselsemneudtryk", + "response-timeout" : "Svar-timeout i millisekunder", + "topic-expression" : "Emneudtryk", + "client-scope" : "Klientområde", + "add-device" : "Tilføj enhed", + "opc-server" : "Servere", + "opc-add-server" : "Tilføj server", + "opc-add-server-prompt" : "Tilføj venligst server", + "opc-application-name" : "Applikationsnavn", + "opc-application-uri" : "Applikations-URI", + "opc-scan-period-in-seconds" : "Scanperiode i sekunder", + "opc-security" : "Sikkerhed", + "opc-identity" : "Identitet", + "opc-keystore" : "Nøglelager", + "opc-type" : "Type", + "opc-keystore-type" : "Type", + "opc-keystore-location" : "Placering *", + "opc-keystore-password" : "Adgangskode", + "opc-keystore-alias" : "Alias", + "opc-keystore-key-password" : "Nøgle adgangskode", + "opc-device-node-pattern" : "Enhedsnode-mønster", + "opc-device-name-pattern" : "Enhedsnavnmønster", + "modbus-server" : "Servere/slaver", + "modbus-add-server" : "Tilføj server/slave", + "modbus-add-server-prompt" : "Tilføj venligst server/slave", + "modbus-transport" : "Transport", + "modbus-tcp-reconnect" : "Genopret automatisk forbindelse", + "modbus-rtu-over-tcp" : "RTU over TCP", + "modbus-port-name" : "Seriel portnavn", + "modbus-encoding" : "Kodning", + "modbus-parity" : "Paritet", + "modbus-baudrate" : "Baudhastighed", + "modbus-databits" : "Databits", + "modbus-stopbits" : "Stopbits", + "modbus-databits-range" : "Databits skal være i området fra 7 til 8.", + "modbus-stopbits-range" : "Stopbits skal være i området fra 1 til 2.", + "modbus-unit-id" : "Enheds-ID", + "modbus-unit-id-range" : "Enheds-ID skal være i området fra 1 til 247.", + "modbus-device-name" : "Enhedsnavn", + "modbus-poll-period" : "Afspørgningsperiode (ms)", + "modbus-attributes-poll-period" : "Attributafspørgningsperiode (ms)", + "modbus-timeseries-poll-period" : "Tidsserieafspørgningsperiode (ms)", + "modbus-poll-period-range" : "Afspørgningsperioden skal være en positiv værdi.", + "modbus-tag" : "Mærkat", + "modbus-function" : "Funktion", + "modbus-register-address" : "Registeradresse", + "modbus-register-address-range" : "Registeradresse skal være i området fra 0 til 65535.", + "modbus-register-bit-index" : "Bitindeks", + "modbus-register-bit-index-range" : "Bitindeks skal være i området fra 0 til 15.", + "modbus-register-count" : "Antal registre", + "modbus-register-count-range" : "Antal registre skal være en positiv værdi.", + "modbus-byte-order" : "Byte-rækkefølge", + "sync" : { + "status" : "Status", + "sync" : "Synkroniser", + "not-sync" : "Ikke synkroniseret", + "last-sync-time" : "Sidste synkroniseringstidspunkt", + "not-available" : "Ikke tilgængelig" + }, + "export-extensions-configuration" : "Eksportér udvidelseskonfiguration", + "import-extensions-configuration" : "Importér udvidelseskonfiguration", + "import-extensions" : "Importér udvidelser", + "import-extension" : "Importér udvidelse", + "export-extension" : "Eksportér udvidelse", + "file" : "Udvidelsesfil", + "invalid-file-error" : "Ugyldig udvidelsesfil" + }, + "feature" : { + "advanced-features" : "Avancerede funktioner" + }, + "filter" : { + "add" : "Tilføj filter", + "edit" : "Rediger filter", + "name" : "Filternavn", + "name-required" : "Filternavn er påkrævet.", + "duplicate-filter" : "Et filter med samme navn findes allerede.", + "filters" : "Filtre", + "unable-delete-filter-title" : "Kan ikke slette filter", + "unable-delete-filter-text" : "Filteret '{{filter}}' kan ikke slettes, da det bruges af følgende widget(s):
{{widgetsList}}", + "duplicate-filter-error" : "Duplikatfilter fundet '{{filter}}'.
Filtre skal være unikke i dashboardet.", + "missing-key-filters-error" : "Nøglefiltre mangler for filteret '{{filter}}'.", + "filter" : "Filter", + "editable" : "Redigerbar", + "no-filters-found" : "Ingen filtre fundet.", + "no-filter-text" : "Intet filter angivet", + "add-filter-prompt" : "Tilføj venligst et filter", + "no-filter-matching" : "'{{filter}}' blev ikke fundet.", + "create-new-filter" : "Opret et nyt!", + "create-new" : "Opret ny", + "filter-required" : "Filter er påkrævet.", + "operation" : { + "operation" : "Operation", + "equal" : "lig med", + "not-equal" : "ikke lig med", + "starts-with" : "starter med", + "ends-with" : "slutter med", + "contains" : "indeholder", + "not-contains" : "indeholder ikke", + "greater" : "større end", + "less" : "mindre end", + "greater-or-equal" : "større eller lig med", + "less-or-equal" : "mindre eller lig med", + "and" : "og", + "or" : "eller", + "in" : "i", + "not-in" : "ikke i" + }, + "ignore-case" : "ignorér store/små bogstaver", + "value" : "Værdi", + "remove-filter" : "Fjern filter", + "duplicate-filter-action" : "Duplikér filter", + "preview" : "Filterforhåndsvisning", + "no-filters" : "Ingen filtre konfigureret", + "add-filter" : "Tilføj filter", + "add-complex-filter" : "Tilføj komplekst filter", + "add-complex" : "Tilføj kompleks", + "complex-filter" : "Komplekst filter", + "edit-complex-filter" : "Rediger komplekst filter", + "edit-filter-user-params" : "Rediger brugerparametre for filterbetingelse", + "filter-user-params" : "Filterbetingelsens brugerparametre", + "user-parameters" : "Brugerparametre", + "display-label" : "Etiket til visning", + "order-priority" : "Prioritet for feltorden", + "key-filter" : "Nøglefilter", + "key-filters" : "Nøglefiltre", + "key-name" : "Nøglenavn", + "key-name-required" : "Nøglenavn er påkrævet.", + "key-type" : { + "key-type" : "Nøgletype", + "attribute" : "Attribut", + "timeseries" : "Tidsserie", + "entity-field" : "Entitetsfelt", + "constant" : "Konstant", + "client-attribute" : "Klientattribut", + "server-attribute" : "Serverattribut", + "shared-attribute" : "Delt attribut" + }, + "value-type" : { + "value-type" : "Værditype", + "string" : "Streng", + "numeric" : "Numerisk", + "boolean" : "Boolesk", + "date-time" : "Dato og tid" + }, + "value-type-required" : "Værditype for nøgle er påkrævet.", + "key-value-type-change-title" : "Er du sikker på, at du vil ændre værditypen?", + "key-value-type-change-message" : "Hvis du bekræfter den nye værditype, vil alle indtastede nøglefiltre blive fjernet.", + "no-key-filters" : "Ingen nøglefiltre konfigureret", + "add-key-filter" : "Tilføj nøglefilter", + "remove-key-filter" : "Fjern nøglefilter", + "edit-key-filter" : "Rediger nøglefilter", + "date" : "Dato", + "time" : "Tid", + "current-tenant" : "Aktuel udlejer", + "current-customer" : "Aktuel kunde", + "current-user" : "Aktuel bruger", + "current-device" : "Aktuel enhed", + "default-value" : "Standardværdi", + "default-comma-separated-values" : "Standardværdi med komma-adskilte værdier", + "dynamic-source-type" : "Dynamisk kildetype", + "dynamic-value" : "Dynamisk værdi", + "no-dynamic-value" : "Ingen dynamisk værdi", + "source-attribute" : "Kildeattribut", + "switch-to-dynamic-value" : "Skift til dynamisk værdi", + "switch-to-default-value" : "Skift til standardværdi", + "inherit-owner" : "Arv fra ejer", + "source-attribute-not-set" : "Hvis kildeattribut ikke er angivet" + }, + "fullscreen" : { + "expand" : "Udvid til fuld skærm", + "exit" : "Afslut fuld skærm", + "toggle" : "Skift fuldskærmstilstand", + "fullscreen" : "Fuld skærm" + }, + "function" : { + "function" : "Funktion" + }, + "gateway" : { + "gateway-name" : "Gateway navn", + "gateway-name-required" : "Gateway navn er påkrævet.", + "gateways" : "Gateways", + "create-new-gateway" : "Opret en ny gateway", + "create-new-gateway-text" : "Er du sikker på, at du vil oprette en ny gateway med navnet: '{{gatewayName}}'?", + "launch-command" : "Start kommando", + "no-gateway-found" : "Ingen gateway fundet.", + "no-gateway-matching" : " '{{item}}' blev ikke fundet." + }, + "grid" : { + "delete-item-title" : "Er du sikker på, at du vil slette dette element?", + "delete-item-text" : "Vær forsigtig, efter bekræftelsen vil dette element og alle relaterede data være uoprettelige.", + "delete-items-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 element} other {# elementer} }?", + "delete-items-action-title" : "Slet { count, plural, =1 {1 element} other {# elementer} }", + "delete-items-text" : "Vær forsigtig, efter bekræftelsen vil alle valgte elementer blive fjernet, og alle relaterede data vil være uoprettelige.", + "add-item-text" : "Tilføj nyt element", + "no-items-text" : "Ingen elementer fundet", + "item-details" : "Elementdetaljer", + "delete-item" : "Slet element", + "delete-items" : "Slet elementer", + "scroll-to-top" : "Rul til toppen" + }, + "help" : { + "goto-help-page" : "Gå til hjælpeside", + "show-help" : "Vis hjælp" + }, + "home" : { + "home" : "Hjem", + "profile" : "Profil", + "logout" : "Log ud", + "menu" : "Menu", + "avatar" : "Avatar", + "open-user-menu" : "Åbn brugermenu" + }, + "file-input" : { + "browse-file" : "Gennemse fil", + "browse-files" : "Gennemse filer" + }, + "image" : { + "gallery" : "Billedgalleri", + "search" : "Søg billede", + "selected-images" : "{ count, plural, =1 {1 billede} other {# billeder} } valgt", + "created-time" : "Oprettelsestid", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "resolution" : "Opløsning", + "size" : "Størrelse", + "system" : "System", + "download-image" : "Download billede", + "export-image" : "Eksportér billede til JSON", + "import-image" : "Importér billede fra JSON", + "upload-image" : "Upload billede", + "edit-image" : "Rediger billede", + "image-details" : "Billeddetaljer", + "no-images" : "Ingen billeder fundet", + "delete-image" : "Slet billede", + "delete-image-title" : "Er du sikker på, at du vil slette billedet '{{imageTitle}}'?", + "delete-image-text" : "Vær forsigtig, efter bekræftelsen vil billedet være uopretteligt.", + "delete-images-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 billede} other {# billeder} }?", + "delete-images-text" : "Vær forsigtig, efter bekræftelsen vil alle valgte billeder blive fjernet, og alle relaterede data vil være uoprettelige.", + "list-mode" : "Listevisning", + "grid-mode" : "Gittervisning", + "image-preview" : "Billedforhåndsvisning", + "update-image" : "Opdater billede", + "export-failed-error" : "Kan ikke eksportere billede: {{error}}", + "image-json-file" : "Billede JSON-fil", + "invalid-image-json-file-error" : "Kan ikke importere billede fra JSON: Ugyldig billed-JSON-datastruktur.", + "image-is-in-use" : "Billedet bruges af andre enheder", + "images-are-in-use" : "Billeder bruges af andre enheder", + "image-is-in-use-text" : "Billedet '{{title}}' blev ikke slettet, fordi det bruges af følgende enheder:", + "images-are-in-use-text" : "Ikke alle billeder blev slettet, fordi de bruges af andre enheder.
Du kan se refererede enheder ved at klikke på Referencer-knappen i den tilsvarende billedrække.
Hvis du stadig vil slette disse billeder, skal du vælge dem i tabellen nedenfor og klikke på Slet valgte-knappen.", + "delete-image-in-use-text" : "Hvis du stadig vil slette billedet, skal du klikke på Slet alligevel-knappen.", + "system-entities" : "Systemenheder:", + "entities" : "enheder:", + "references" : "Referencer", + "include-system-images" : "Medtag systembilleder", + "clear-image" : "Ryd billede", + "no-image" : "Intet billede", + "no-image-selected" : "Intet billede valgt", + "browse-from-gallery" : "Gennemse fra galleri", + "set-link" : "Angiv link", + "image-link" : "Billedlink", + "link" : "Link", + "copy-image-link" : "Kopiér billedlink", + "embed-image" : "Indlejr billede", + "embed-to-html" : "Indlejr i HTML", + "embed-to-html-hint" : "Denne funktion vil gøre linket tilgængeligt for enhver uautoriseret bruger.", + "embed-to-html-text" : "Ved at bruge følgende kodeeksempel kan du indlejre et billede i komponenter baseret på almindelig HTML.
Sådanne komponenter inkluderer HTML-kort-widgets, celleindholdsfunktioner osv.", + "embed-to-angular-template" : "Indlejr i Angular HTML-skabelon", + "embed-to-angular-template-text" : "Ved at bruge følgende kodeeksempel kan du indlejre et billede i Angular HTML-skabelonen, som vil blive brugt til komponenter.
Sådanne komponenter inkluderer Markdown-widget, HTML-sektion i widget-editor, brugerdefinerede handlinger osv." + }, + "image-input" : { + "drop-images-or" : "Træk og slip billeder eller", + "drag-and-drop" : "Træk og slip", + "or" : "eller", + "browse" : "Gennemse", + "no-images" : "Ingen billeder valgt", + "images" : "billeder" + }, + "import" : { + "no-file" : "Ingen fil valgt", + "drop-file" : "Slip en JSON-fil eller klik for at vælge en fil, der skal uploades.", + "drop-json-file-or" : "Træk og slip en JSON-fil eller", + "drop-file-csv" : "Slip en CSV-fil eller klik for at vælge en fil, der skal uploades.", + "drop-file-csv-or" : "Træk og slip en CSV-fil eller", + "column-value" : "Værdi", + "column-title" : "Titel", + "column-example" : "Eksempel på dataværdi", + "column-key" : "Attribut/telemetrinøgle", + "credentials" : "Oplysninger", + "csv-delimiter" : "CSV-afgrænser", + "csv-first-line-header" : "Første linje indeholder kolonnenavne", + "csv-update-data" : "Opdater attributter/telemetri", + "details" : "Detaljer", + "import-csv-number-columns-error" : "En fil skal indeholde mindst to kolonner", + "import-csv-invalid-format-error" : "Ugyldigt filformat. Linje: '{{line}}'", + "column-type" : { + "name" : "Navn", + "type" : "Type", + "label" : "Etiket", + "column-type" : "Kolonnetype", + "client-attribute" : "Klientattribut", + "shared-attribute" : "Delt attribut", + "server-attribute" : "Serverattribut", + "timeseries" : "Tidsserie", + "entity-field" : "Enhedsfelt", + "access-token" : "Adgangstoken", + "x509" : "X.509", + "mqtt" : { + "client-id" : "MQTT-klient-ID", + "user-name" : "MQTT-brugernavn", + "password" : "MQTT-adgangskode" + }, + "lwm2m" : { + "client-endpoint" : "LwM2M endpoint klientnavn", + "security-config-mode" : "LwM2M sikkerhedskonfigurationstilstand", + "client-identity" : "LwM2M klientidentitet", + "client-key" : "LwM2M klientnøgle", + "client-cert" : "LwM2M klient offentlig nøgle", + "bootstrap-server-security-mode" : "LwM2M bootstrap server sikkerhedstilstand", + "bootstrap-server-secret-key" : "LwM2M bootstrap server hemmelig nøgle", + "bootstrap-server-public-key-id" : "LwM2M bootstrap server offentlig nøgle eller ID", + "lwm2m-server-security-mode" : "LwM2M server sikkerhedstilstand", + "lwm2m-server-secret-key" : "LwM2M server hemmelig nøgle", + "lwm2m-server-public-key-id" : "LwM2M server offentlig nøgle eller ID" + }, + "snmp" : { + "host" : "SNMP-vært", + "port" : "SNMP-port", + "version" : "SNMP-version (v1, v2c eller v3)", + "community-string" : "SNMP community-streng" + }, + "isgateway" : "Er Gateway", + "activity-time-from-gateway-device" : "Aktivitetstid fra gateway-enhed", + "description" : "Beskrivelse", + "routing-key" : "Edge-nøgle", + "secret" : "Edge-hemmelighed" + }, + "stepper-text" : { + "select-file" : "Vælg en fil", + "configuration" : "Importkonfiguration", + "column-type" : "Vælg kolonnernes type", + "creat-entities" : "Opretter nye enheder" + }, + "message" : { + "create-entities" : "{{count}} nye enheder blev oprettet med succes.", + "update-entities" : "{{count}} enheder blev opdateret med succes.", + "error-entities" : "Der opstod en fejl under oprettelsen af {{count}} enheder." } }, - "timeunit": { - "seconds": "Sekunder", - "minutes": "Minutter", - "hours": "Timer", - "days": "Dage" - }, - "timewindow": { - "days": "{ days, plural, =1 { dag } other {# dage } }", - "hours": "{ hours, plural, =0 { time } =1 {1 time } other {# timer } }", - "minutes": "{ minutes, plural, =0 { minut } =1 {1 minut } other {# minutter } }", - "seconds": "{ seconds, plural, =0 { sekund } =1 {1 sekund } other {# sekunder } }", - "realtime": "Realtid", - "history": "Historie", - "last-prefix": "sidst", - "period": "fra {{ startTime }} til {{ endTime }}", - "edit": "Rediger tidsvindue", - "date-range": "Datointerval", - "last": "Sidst", - "time-period": "Tidsperiode", - "hide": "Skjul", - "interval": "Interval" - }, - "user": { - "user": "Bruger", - "users": "Brugere", - "management": "Brugeradministration", - "customer-users": "Kundebrugere", - "tenant-admins": "Lejeradmin.", - "sys-admin": "Systemadministrator", - "tenant-admin": "Bruger", - "customer": "Kunde", - "anonymous": "Anonym", - "add": "Tilføj bruger", - "delete": "Slet bruger", - "add-user-text": "Tilføj ny bruger", - "no-users-text": "Ingen brugere fundet", - "user-details": "Brugerdetaljer", - "delete-users": "Slet brugere", - "delete-user-title": "", - "delete-user-text": "Vær forsigtig. Efter bekræftelsen vil brugeren og alle relaterede data være uoprettelige.", - "delete-users-title": "", - "delete-users-action-title": "", - "delete-users-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte brugere blive fjernet, og alle relaterede data vil være uoprettelige.", - "activation-email-sent-message": "Aktiverings-e-mail blev sendt!", - "resend-activation": "Gensend aktivering", - "email": "E-mail", - "email-required": "E-mail er påkrævet.", - "invalid-email-format": "Ugyldigt e-mailformat.", - "first-name": "Fornavn", - "last-name": "Efternavn", - "description": "Beskrivelse", - "default-dashboard": "Standard dashboard", - "always-fullscreen": "Altid fuld skærm", - "select-user": "Vælg bruger", - "no-users-matching": "", - "user-required": "Bruger er påkrævet", - "activation-method": "Aktiveringsmetode", - "display-activation-link": "Vis aktiveringslink", - "send-activation-mail": "Send aktiverings-e-mail", - "activation-link": "Link til brugeraktivering", - "activation-link-text": "", - "copy-activation-link": "Kopiér aktiveringslink", - "activation-link-copied-message": "Brugeraktiveringslinket er blevet kopieret til udklipsholderen", - "selected-users": "", - "search": "Søg efter brugere", - "details": "Oplysninger", - "login-as-tenant-admin": "Log ind som lejeradministrator", - "login-as-customer-user": "Log ind som kundebruger", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte brugere", - "select-group-to-move": "Vælg målgruppe for at flytte valgte brugere", - "remove-users-from-group": "", - "group": "Gruppe af brugere", - "list-of-groups": "", - "group-name-starts-with": "", - "disable-account": "Deaktiver brugerkonto", - "enable-account": "Aktivér brugerkonto", - "enable-account-message": "Brugerkontoen er blevet aktiveret!", - "disable-account-message": "Brugerkontoen blev deaktiveret!" - }, - "value": { - "type": "Værditype", - "string": "Streng", - "string-value": "Strengværdi", - "string-value-required": "Strengværdi er påkrævet", - "integer": "Heltal", - "integer-value": "Heltalsværdi", - "integer-value-required": "Heltalsværdi er påkrævet", - "invalid-integer-value": "Ugyldig heltalsværdi", - "double": "Dobbelt", - "double-value": "Dobbelt værdi", - "double-value-required": "Dobbelt værdi er påkrævet", - "boolean": "Boolesk", - "boolean-value": "Boolesk værdi", - "false": "FALSK", - "true": "SANDT", - "long": "Lang", - "json": "JSON", - "json-value": "JSON-værdi", - "json-value-invalid": "JSON-værdi har et ugyldigt format", - "json-value-required": "JSON-værdi er påkrævet." - }, - "widget": { - "widget-library": "Widgets-bibliotek", - "widget-bundle": "Widgets-bundt", - "all-bundles": "Alle bundter", - "select-widgets-bundle": "Vælg widgets-bundt", - "management": "Widget-administration", - "editor": "Widget-editor", - "widget-type-not-found": "Problem under indlæsning af widget-konfiguration.
Sandsynligvis tilknyttet\r\n widget-type blev fjernet.", - "widget-type-load-error": "Widget blev ikke indlæst pga. følgende fejl:", - "remove": "Fjern widget", - "edit": "Rediger widget", - "remove-widget-title": "", - "remove-widget-text": "Efter bekræftelsen vil widgeten og alle relaterede data være uoprettelige.", - "timeseries": "Tidsserier", - "search-data": "Søg efter data", - "no-data-found": "Ingen data fundet", - "latest": "Seneste værdier", - "rpc": "Styring af widget", - "alarm": "Alarm-widget", - "static": "Statisk widget", - "select-widget-type": "Vælg widget-type", - "missing-widget-title-error": "Widget-titel skal angives!", - "widget-saved": "Widget gemt", - "unable-to-save-widget-error": "Widget kunne ikke gemmes! Widget har fejl!", - "save": "Gem widget", - "saveAs": "Gem widget som", - "save-widget-type-as": "Gem widget-type som", - "save-widget-type-as-text": "Indtast en ny widget-titel, og/eller vælg målwidget-bundt", - "toggle-fullscreen": "Skift til fuld skærm", - "run": "Kør widget", - "title": "Widget-titel", - "title-required": "Widget-titel er påkrævet.", - "type": "Widget-type", - "resources": "Ressourcer", - "resource-url": "JavaScript/CSS URL", - "remove-resource": "Fjern ressource", - "add-resource": "Tilføj ressource", - "html": "HTML", - "tidy": "Tidy", - "css": "CSS", - "settings-schema": "Indstillingsskema", - "datakey-settings-schema": "Skema over datanøgleindstillinger", - "widget-settings": "Widget-indstillinger", - "description": "Beskrivelse", - "image-preview": "Forhåndsvisning af billede", - "javascript": "Javascript", - "js": "JS", - "add-widget-type": "Tilføj ny widget-type", - "widget-template-load-failed-error": "Kunne ikke indlæse widget-skabelon!", - "add": "Tilføj widget", - "undo": "Fortryd widget-ændringer", - "export": "Eksportér widget", - "export-data": "Eksportér widget-data", - "export-to-csv": "Eksportér data til CSV...", - "export-to-excel": "Eksportér data til XLS...", - "export-to-excel-xlsx": "Eksportér data til XLSX...", - "no-data": "Ingen data at vise på widget", - "data-overflow": "", - "alarm-data-overflow": "", - "search": "Søg efter widget", - "filter": "Widget-filtertype", - "loading-widgets": "Indlæser widgets..." - }, - "widget-action": { - "header-button": "Widget-overskriftsknap", - "open-dashboard-state": "Naviger til ny dashboardtilstand", - "update-dashboard-state": "Opdater aktuel dashboardtilstand", - "open-dashboard": "Naviger til et andet dashboard", - "custom": "Brugerdefineret handling", - "custom-pretty": "Brugerdefineret handling (med HTML-skabelon)", - "target-dashboard-state": "Mål-dashboardtilstand", - "target-dashboard-state-required": "Mål-dashboardtilstand er påkrævet", - "set-entity-from-widget": "Angiv entitet fra widget", - "target-dashboard": "Mål-dashboard", - "open-right-layout": "Åbn højre dashboardlayout (mobilvisning)", - "open-in-separate-dialog": "Åbn i separat dialogboks", - "dialog-title": "Dialogbokstitel", - "dialog-hide-dashboard-toolbar": "Skjul dashboardets værktøjslinje i dialogboks", - "dialog-width": "Dialogboksbredde i procent i forhold til visningsportens bredde", - "dialog-height": "Dialogbokshøjde i procent i forhold til visningsportens højde", - "dialog-size-range-error": "Dialogboksstørrelsens procentværdi skal ligge i området fra 1 til 100.", - "open-new-browser-tab": "Åbn i en ny browserfane" - }, - "widgets-bundle": { - "current": "Nuværende bundt", - "widgets-bundles": "Widgets-bundter", - "add": "Tilføj widgets-bundt", - "delete": "Slet widgets-bundt", - "title": "Titel", - "title-required": "Titel er påkrævet.", - "description": "Beskrivelse", - "image-preview": "Forhåndsvisning af billede", - "add-widgets-bundle-text": "Tilføj nye widgets-bundter", - "no-widgets-bundles-text": "Ingen widgets-bundter fundet", - "empty": "Widgets-bundt er tomt", - "details": "Oplysninger", - "widgets-bundle-details": "Oplysninger om widgets-bundt", - "delete-widgets-bundle-title": "", - "delete-widgets-bundle-text": "Vær forsigtig. Efter bekræftelsen vil widgets-bundtet og alle relaterede data være uoprettelige.", - "delete-widgets-bundles-title": "", - "delete-widgets-bundles-action-title": "", - "delete-widgets-bundles-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte widgets-bundter blive fjernet, og alle relaterede data vil være uoprettelige.", - "no-widgets-bundles-matching": "", - "widgets-bundle-required": "Widgets-bundt er påkrævet.", - "system": "System", - "import": "Importér widgets-bundt", - "export": "Eksportér widgets-bundt", - "export-failed-error": "", - "create-new-widgets-bundle": "Opret nyt widgets-bundt", - "widgets-bundle-file": "Widgets-bundtfil", - "invalid-widgets-bundle-file-error": "Widgets-bundt kunne ikke importeres: Ugyldig datastruktur for widgets-bundt.", - "search": "Søg efter widget-bundter", - "selected-widgets-bundles": "", - "open-widgets-bundle": "Åbn widgets-bundt", - "loading-widgets-bundles": "Indlæser widgets-bundter..." - }, - "widget-config": { - "data": "Data", - "settings": "Indstillinger", - "advanced": "Fremskreden", - "title": "Titel", - "title-tooltip": "Værktøjstip for titel", - "general-settings": "Generelle indstillinger", - "display-title": "Vis titel", - "drop-shadow": "Slip skygge", - "enable-fullscreen": "Aktivér fuldskærm", - "enable-data-export": "Aktivér dataeksport", - "background-color": "Baggrundsfarve", - "text-color": "Tekstfarve", - "padding": "Padding", - "margin": "Margin", - "widget-style": "Widget-stil", - "title-style": "Titeltype", - "mobile-mode-settings": "Indstillinger for mobiltilstand", - "order": "Rækkefølge", - "height": "Højde", - "units": "Specielt symbol, der vises ved siden af værdi", - "decimals": "Antal cifre efter flydende punkt", - "timewindow": "Tidsvindue", - "use-dashboard-timewindow": "Brug dashboardtidsvindue", - "display-timewindow": "Vis tidsvindue", - "display-legend": "Vis siganturforklaring", - "datasources": "Datakilder", - "maximum-datasources": "", - "datasource-type": "Type", - "datasource-parameters": "Parametre", - "remove-datasource": "Fjern datakilde", - "add-datasource": "Tilføj datakilde", - "target-device": "Målenhed", - "alarm-source": "Alarmkilde", - "actions": "Handlinger", - "action": "Handling", - "add-action": "Tilføj handling", - "search-actions": "Søg efter handlinger", - "no-actions-text": "Ingen handlinger fundet", - "action-source": "Handlingskilde", - "action-source-required": "Handlingskilde er påkrævet.", - "action-name": "Navn", - "action-name-required": "Handlingsnavn er påkrævet.", - "action-name-not-unique": "Der findes allerede en anden handling med samme navn.\nHandlingsnavnet skal være unikt inden for den samme handlingskilde.", - "action-icon": "Ikon", - "action-type": "Type", - "action-type-required": "Handlingstype er påkrævet.", - "edit-action": "Rediger handling", - "delete-action": "Slet handling", - "delete-action-title": "Slet widget-handling", - "delete-action-text": "", - "display-icon": "Vis titelikon", - "icon-color": "Ikonfarve", - "icon-size": "Ikonstørrelse" - }, - "widget-type": { - "import": "Importér widget-type", - "export": "Eksportér widget-type", - "export-failed-error": "", - "create-new-widget-type": "Opret ny widget-type", - "widget-type-file": "Widget-typefil", - "invalid-widget-type-file-error": "Kan ikke importere widget-type: Ugyldig datastruktur for widget-type." - }, - "self-registration": { - "self-registration": "Selvregistrering", - "self-registration-url": "Selvregistrerings-URL", - "captcha-site-key": "reCAPTCHA site-nøgle", - "captcha-secret-key": "reCAPTCHA hemmelig nøgle", - "notification-email": "Notifikations-e-mail", - "privacy-policy-text": "Fortrolighedspolitik tekst", - "text-message-page": "Tekstmeddelelse til tilmeldingsside" - }, - "white-labeling": { - "white-labeling": "Hvid mærkning", - "login-white-labeling": "Login for hvid mærkning", - "preview": "Forhåndsvisning", - "app-title": "Applikationstitel", - "favicon": "Website-ikon", - "favicon-description": "", - "favicon-size-error": "", - "favicon-type-error": "Ugyldigt filformat for website-billede. Kun ICO-, GIF- eller PNG-billeder accepteres.", - "drop-favicon-image": "Træk og slip et website-ikonbillede, eller klik for at vælge en fil, der skal uploades.", - "no-favicon-image": "Intet ikon valgt", - "logo": "Logo", - "logo-description": "", - "logo-size-error": "", - "logo-type-error": "Ugyldigt logofilformat. Kun billeder accepteres.", - "drop-logo-image": "Træk og slip et logobillede, eller klik for at vælge en fil, der skal uploades.", - "no-logo-image": "Intet logo valgt", - "logo-height": "Logohøjde, px", - "primary-palette": "Primær palet", - "accent-palette": "Accent palet", - "customize-palette": "Tilpas", - "advanced-css": "Avanceret CSS", - "edit-palette": "Rediger palet", - "save-palette": "Gem palet", - "primary-background": "Primær baggrund", - "secondary-background": "Sekundær baggrund", - "hue1": "HUE 1", - "hue2": "HUE 2", - "hue3": "HUE 3", - "page-background-color": "Baggrundsfarve for side", - "dark-foreground": "Mørk forgrund", - "domain-name": "Domænenavn", - "base-url": "Basis-URL", - "base-url-required": "Basis-URL er påkrævet.", - "prohibit-different-url": "Det er ikke tilladt at bruge værtsnavn fra overskrifterne for klientanmodninger", - "prohibit-different-url-hint": "Denne indstilling skal aktiveres for produktionsmiljøer. Kan forårsage sikkerhedsproblemer ved deaktivering", - "help-link-base-url": "Basis-URL for hjælpelinks", - "enable-help-links": "Aktivér hjælpelinks", - "error-verification-url": "Et domænenavn må ikke indeholde symbolerne '/' og ':'. Eksempel: thingsboard.io", - "show-platform-name-version": "Vis platformnavn og version", - "platform-name": "Platformnavn", - "platform-version": "Platformversion", - "version-mask": "", - "position": { - "label": "Platformnavn og versionsposition", - "under-logo": "Under logoet", - "bottom": "Nederst i login-formularen" + "scada" : { + "symbols" : "SCADA-symboler", + "search" : "Søg symbol", + "selected-symbols" : "{ count, plural, =1 {1 symbol} other {# symboler} } valgt", + "download-symbol" : "Download SCADA-symbol", + "export-symbol" : "Eksporter SCADA-symbol til JSON", + "import-symbol" : "Importer SCADA-symbol fra JSON", + "upload-symbol" : "Upload SCADA-symbol", + "update-symbol" : "Opdater SCADA-symbol", + "edit-symbol" : "Rediger SCADA-symbol", + "symbol-details" : "SCADA-symboldetaljer", + "mode-svg" : "SVG", + "mode-xml" : "XML", + "no-symbols" : "Ingen symboler fundet", + "show-hidden-elements" : "Vis skjulte elementer", + "hide-hidden-elements" : "Skjul skjulte elementer", + "delete-symbol" : "Slet SCADA-symbol", + "delete-symbol-title" : "Er du sikker på, at du vil slette SCADA-symbolet '{{imageTitle}}'?", + "delete-symbol-text" : "Vær forsigtig, efter bekræftelsen vil SCADA-symbolet ikke kunne gendannes.", + "delete-symbols-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 SCADA-symbol} other {# SCADA-symboler} }?", + "delete-symbols-text" : "Vær forsigtig, efter bekræftelsen vil alle valgte SCADA-symboler blive fjernet og al relateret data vil ikke kunne gendannes.", + "include-system-symbols" : "Inkluder systemsymboler", + "symbol-preview" : "Symbol forhåndsvisning", + "general" : "Generelt", + "tags" : "Tags", + "properties" : "Egenskaber", + "title" : "Titel", + "description" : "Beskrivelse", + "search-tags" : "Søg tags", + "widget-size" : "Widget-størrelse", + "cols" : "kolonner", + "rows" : "rækker", + "state-render-function" : "Tilstandsrendreringsfunktion", + "preview" : "Forhåndsvisning", + "preview-widget-action-text" : "Widget-handling '{{type}}' udført med succes!", + "no-symbol" : "Intet SCADA-symbol", + "no-symbol-selected" : "Intet SCADA-symbol valgt", + "clear-symbol" : "Ryd SCADA-symbol", + "browse-symbol-from-gallery" : "Gennemse SCADA-symbol fra galleri", + "zoom-in" : "Zoom ind", + "zoom-out" : "Zoom ud", + "create-widget" : "Opret widget", + "create-widget-from-symbol" : "Opret widget fra SCADA-symbol", + "hidden" : "skjult", + "tag" : { + "tag" : "Tag", + "on-click-action" : "Ved klik-handling", + "no-tags" : "Ingen tags konfigureret", + "delete-tag-text" : "Er du sikker på, at du vil slette tag
{{tag}} fra {{elementType}}-element?", + "update-tag" : "Opdater tag", + "enter-tag" : "Indtast tag", + "tag-settings" : "Tagindstillinger", + "remove-tag" : "Fjern tag", + "add-tag" : "Tilføj tag" + }, + "behavior" : { + "behavior" : "Adfærd", + "id" : "Id", + "name" : "Navn", + "type" : "Type", + "no-behaviors" : "Ingen adfærd konfigureret", + "add-behavior" : "Tilføj adfærd", + "type-action" : "Handling", + "type-value" : "Værdi", + "type-widget-action" : "Widget-handling", + "behavior-settings" : "Adfærdsindstillinger", + "remove-behavior" : "Fjern adfærd", + "hint" : "Tip", + "group-title" : "Gruppe titel", + "value-type" : "Værditype", + "default-value" : "Standardværdi", + "true-label" : "Etiket for sand", + "false-label" : "Etiket for falsk", + "state-label" : "Tilstandsetiket", + "default-payload" : "Standardindhold", + "not-unique-behavior-ids-error" : "Adfærds-id'er skal være unikke!", + "default-settings" : "Standardindstillinger" + }, + "symbol" : { + "symbol" : "SCADA-symbol", + "fluid-presence" : "Væsketilstedeværelse", + "fluid-presence-hint" : "Angiver, om der er væske i røret.", + "fluid-present" : "Væske til stede", + "present" : "Tilstede", + "absent" : "Fraværende", + "flow-presence" : "Strømningstilstedeværelse", + "flow-presence-hint" : "Angiver, om væske strømmer i røret.", + "flow-present" : "Strømning til stede", + "flow-direction" : "Strømningsretning", + "flow-direction-hint" : "Angiver væskens strømningsretning.", + "forward" : "Fremad", + "reverse" : "Baglæns", + "flow-animation-speed" : "Strømningsanimationshastighed", + "flow-animation-speed-hint" : "Double-værdi, der angiver animationshastigheden for strømning. 1 - normal hastighed, 0 - ingen animation, < 1 - langsommere, > 1 - hurtigere.", + "leak" : "Lækage", + "leak-hint" : "Angiver, om der er en lækage i røret.", + "leak-present" : "Lækage til stede", + "fluid-color" : "Væskefarve", + "pipe-color" : "Rørfarve", + "horizontal-pipe" : "Horisontalt rør", + "vertical-pipe" : "Vertikalt rør", + "horizontal-fluid-color" : "Horisontal væskefarve", + "vertical-fluid-color" : "Vertikal væskefarve", + "left-pipe" : "Venstre rør", + "right-pipe" : "Højre rør", + "top-pipe" : "Toprør", + "bottom-pipe" : "Bundrør", + "left-fluid-color" : "Venstre væskefarve", + "right-fluid-color" : "Højre væskefarve", + "top-fluid-color" : "Top væskefarve", + "bottom-fluid-color" : "Bund væskefarve", + "display" : "Display", + "display-format" : "Visningsformat", + "value" : "Værdi", + "decimals" : "Decimaler", + "units" : "Enheder", + "flow-meter-value-hint" : "Double-værdi vist på strømningsmåleren", + "value-hint" : "Double-værdi, der angiver den aktuelle værdi", + "running" : "Kører", + "running-hint" : "Angiver, om komponenten er i drift.", + "warning-state" : "Advarselsstatus", + "warning" : "Advarsel", + "warning-click" : "Klik ved advarsel", + "warning-state-hint" : "Angiver, om komponenten er i advarselsstatus.", + "critical-state" : "Kritisk status", + "critical" : "Kritisk", + "critical-click" : "Klik ved kritisk status", + "critical-state-hint" : "Angiver, om komponenten er i kritisk status.", + "critical-state-animation" : "Kritisk status animation", + "critical-state-animation-hint" : "Aktiverer blinkeanimation, når komponenten er i kritisk status.", + "warning-critical-state-animation" : "Advarsel/Kritisk status animation", + "warning-critical-state-animation-hint" : "Aktiverer blinkeanimation, når komponenten er i advarsels- eller kritisk status.", + "animation" : "Animation", + "broken" : "Ødelagt", + "broken-hint" : "Angiver, om komponenten er ødelagt.", + "on-display-click" : "Ved klik på display", + "on-display-click-hint" : "Handling udføres, når brugeren klikker på displayet.", + "pipe" : "Rør", + "default-border-color" : "Standard kantfarve", + "active-border-color" : "Aktiv kantfarve", + "warning-border-color" : "Advarselskantfarve", + "critical-border-color" : "Kritisk kantfarve", + "background-color" : "Baggrundsfarve", + "rotation-animation-speed" : "Rotationsanimationshastighed", + "rotation-animation-speed-hint" : "Double-værdi, der angiver hastigheden for rotationsanimation. 1 - normal hastighed, 0 - ingen animation, < 1 - langsommere, > 1 - hurtigere.", + "on-click" : "Ved klik", + "on-click-hint" : "Handling udføres, når brugeren klikker på komponenten.", + "connectors-positions" : "Forbinderpositioner", + "right-connector" : "Højre forbindelse", + "right-top-connector" : "Højre topforbinder", + "right-bottom-connector" : "Højre bundforbinder", + "left-connector" : "Venstre forbindelse", + "left-top-connector" : "Venstre topforbinder", + "left-bottom-connector" : "Venstre bundforbinder", + "top-left-connector" : "Top venstre forbinder", + "top-right-connector" : "Top højre forbinder", + "top-connector" : "Øverste forbindelse", + "bottom-connector" : "Nederste forbindelse", + "running-color" : "Kører farve", + "stopped-color" : "Stoppet farve", + "stopped" : "Stoppet", + "warning-color" : "Advarselsfarve", + "critical-color" : "Kritisk farve", + "opened" : "Åben", + "opened-hint" : "Angiver, om komponenten er i åben tilstand.", + "open" : "Åben", + "open-hint" : "Handling udføres, når brugeren klikker for at åbne komponenten.", + "close" : "Luk", + "close-hint" : "Handling udføres, når brugeren klikker for at lukke komponenten.", + "close-state-animation" : "Lukket tilstand animation", + "close-state-animation-hint" : "Aktiverer blinkeanimation, når komponenten er i lukket tilstand.", + "opened-color" : "Åben farve", + "closed-color" : "Lukket farve", + "opened-rotation-angle" : "Åben rotationsvinkel", + "closed-rotation-angle" : "Lukket rotationsvinkel", + "tank-capacity" : "Tankkapacitet", + "tank-capacity-hint" : "Double-værdi, der angiver den samlede tankkapacitet.", + "current-volume" : "Aktuelt volumen", + "current-volume-hint" : "Double-værdi, der angiver det aktuelle optagede volumen.", + "tank-color" : "Tankfarve", + "value-box" : "Værdiboks", + "value-text" : "Værdi tekst", + "scale" : "Skala", + "transparent-mode" : "Gennemsigtig tilstand", + "major-ticks" : "Store markeringer", + "intervals" : "Intervaller", + "major-ticks-color" : "Farve for store markeringer", + "normal" : "Normal", + "minor-ticks" : "Små markeringer", + "minor-ticks-color" : "Farve for små markeringer", + "temperature" : "Temperatur", + "temperature-hint" : "Double-værdi, der angiver den aktuelle temperatur.", + "update-temperature" : "Opdater temperatur", + "update-temperature-hint" : "Handling udføres, når brugeren klikker for at ændre temperaturen.", + "run" : "Kør", + "run-hint" : "Handling udføres, når brugeren klikker for at starte komponenten.", + "stop" : "Stop", + "stop-hint" : "Handling udføres, når brugeren klikker for at stoppe komponenten.", + "temperature-step" : "Temperaturtrin", + "heat-pump-color" : "Varme pumpe farve", + "power-button-background" : "Baggrund for tænd/sluk-knap", + "value-box-background" : "Baggrund for værdiboks", + "value-units" : "Værdi enheder", + "filtration-mode" : "Filtreringstilstand", + "filtration-mode-hint" : "Heltalsværdi, der angiver den aktuelle filtreringstilstand.", + "filtration-mode-update" : "Opdater filtreringstilstand", + "filtration-mode-update-hint" : "Handling udføres, når brugeren klikker for at ændre filtreringstilstanden.", + "filter-mode" : "Filter", + "waste-mode" : "Affald", + "backwash-mode" : "Tilbageskyl", + "recirculate-mode" : "Recirkulation", + "rinse-mode" : "Skyl", + "closed-mode" : "Lukket", + "sand-filter-color" : "Sandfilterfarve", + "mode-box-background" : "Baggrund for tilstandsboks", + "border-color" : "Kantfarve", + "label-color" : "Etiketfarve", + "water-leak-hint" : "Angiver, om der er en lækage.", + "default-color" : "Standardfarve", + "leak-color" : "Lækagefarve", + "full-value" : "Fuldt niveau", + "full-value-hint" : "Double-værdi, der angiver fuldt niveau.", + "label" : "Etiket", + "icon" : "Ikon", + "button-color" : "Knapfarve", + "on-label" : "'Tænd' etikettekst", + "off-label" : "'Sluk' etikettekst", + "arrow-presence" : "Pil til stede", + "arrow-presence-hint" : "Angiver, om der er en pil i forbindelsen.", + "arrow-present" : "Pil til stede", + "arrow-direction" : "Pilretning", + "arrow-direction-hint" : "Angiver strømningsretning.", + "flow-animation" : "Flowtilstedeværelse", + "flow-animation-hint" : "Angiver om der flyder væske i forbindelsen.", + "flow" : "Flow", + "flow-line" : "Linje", + "flow-line-style" : "Linjestil", + "flow-style-hint" : "Angiv værdier for Mellemrum og Mellemrum, så deres sum er delelig med 100 uden rest for perfekt animationssynkronisering.", + "flow-dash-cap" : "Streg afslutning", + "dash-cap-butt" : "Flad", + "dash-cap-round" : "Rund", + "dash-cap-square" : "Firkantet", + "dash" : "Streg", + "gap" : "Mellemrum", + "main-line" : "Hovedledning", + "line" : "Ledning", + "line-color" : "Ledningsfarve", + "arrow-color" : "Pilfarve", + "target-value" : "Målværdi", + "target-value-hint" : "Angiver målpunktet på skalaen.", + "min-max-value" : "Min og maks værdi", + "min-value" : "Min", + "max-value" : "Maks", + "progress-bar" : "Fremskridtsbjælke", + "progress-arrow" : "Fremskridtspil", + "warning-scale-color" : "Advarsels skala farve", + "critical-scale-color" : "Kritisk skala farve", + "scale-color" : "Skalafarve", + "target" : "Mål", + "high-warning-state" : "Høj advarselsstatus", + "show-high-warning-scale" : "Vis høj advarselsskala", + "high-warning-scale" : "Høj advarselsskala", + "high-warning-state-hint" : "Double-værdi angiver et højt advarselsområde op til kritisk eller maks værdi.", + "low-warning-state" : "Lav advarselsstatus", + "show-low-warning-scale" : "Vis lav advarselsskala", + "low-warning-scale" : "Lav advarselsskala", + "low-warning-state-hint" : "Double-værdi angiver et lavt advarselsområde ned til kritisk eller min værdi.", + "high-critical-state" : "Høj kritisk status", + "show-high-critical-scale" : "Vis høj kritisk skala", + "high-critical-scale" : "Høj kritisk skala", + "high-critical-state-hint" : "Double-værdi angiver et højt kritisk område op til maksimum på skalaen.", + "low-critical-state" : "Lav kritisk status", + "show-low-critical-scale" : "Vis lav kritisk status", + "low-critical-scale" : "Lav kritisk skala", + "low-critical-state-hint" : "Double-værdi angiver et lavt kritisk område ned til minimum på skalaen.", + "filter-color" : "Filterfarve", + "colors" : "Farver", + "indicator-colors" : "Indikatorfarver", + "enabled" : "Aktiveret", + "disabled" : "Deaktiveret", + "on" : "TIL", + "off" : "FRA", + "on-off-state" : "Tænd/sluk-tilstand", + "on-off-state-hint" : "Angiver om komponenten er tændt eller slukket.", + "on-update-state" : "Ved opdatering til TIL", + "on-update-state-hint" : "Handling der udføres, når brugeren klikker for at ændre tilstanden til TIL.", + "off-update-state" : "Ved opdatering til FRA", + "off-update-state-hint" : "Handling der udføres, når brugeren klikker for at ændre tilstanden til FRA.", + "voltage" : "Spænding", + "input-voltage" : "Indgangsspænding", + "input-voltage-hint" : "Double-værdi der angiver indgangsspænding.", + "output-voltage" : "Udgangsspænding", + "output-voltage-hint" : "Double-værdi der angiver udgangsspænding.", + "first-phase-voltage" : "Første fase spænding", + "second-phase-voltage" : "Anden fase spænding", + "third-phase-voltage" : "Tredje fase spænding", + "phase-voltage-hint" : "Double-værdi der angiver spænding for den aktuelle fase", + "voltage-hint" : "Double-værdi der angiver den aktuelle spænding", + "current-voltage-color" : "Aktuel spændingsfarve", + "phase-indicator-color" : "Faseindikatorfarve", + "measured" : "Målt", + "measured-hint" : "Double-værdi der angiver energiforbrug i kilowatt-timer", + "day-rate" : "Dagpris", + "night-rate" : "Natpris", + "off-peak-rate" : "Lavsæson-pris", + "peak-rate" : "Spidspris", + "export-rate" : "Eksportpris", + "operating-mode" : "Driftstilstand", + "bypass-mode" : "Bypass", + "operating-mode-hint" : "Integer-værdi der angiver aktuel driftstilstand (0 - FRA, 1 - TIL, 2 - BYPASS)", + "connected" : "Forbundet", + "connected-hint" : "Angiver om komponenten er i forbundet tilstand.", + "disconnected" : "Afbrudt", + "indicator" : "Indikator", + "operation-mode" : "Driftstilstand", + "operation-mode-hint" : "Angiver om inverteren er i nettilstand eller invertertilstand.", + "operation-mode-indicators-color" : "Farve for driftstilstandsindikatorer", + "mains-on-mode" : "Net til", + "inverter-on-mode" : "Inverter til", + "charging-mode" : "Opladningstilstand", + "charging-mode-hint" : "Integer-værdi der angiver opladningstilstand (1 - Bulk, 2 - Absorption, 3 - Float)", + "charging-mode-indicators-color" : "Farve for opladningstilstandsindikatorer", + "inverter-faults" : "Fejl", + "inverter-fault-indicators-color" : "Farve for fejlindikatorer", + "overload-fault" : "Overbelastning", + "overload-fault-hint" : "Angiver om inverteren er overbelastet.", + "low-battery-fault" : "Lavt batteri", + "low-battery-fault-hint" : "Angiver om batteriet er overdrevent afladet.", + "temperature-fault" : "Temperatur", + "temperature-fault-hint" : "Angiver om inverteren har høj temperatur.", + "triangle" : "Trekant", + "socket" : "Stik", + "left-button" : "Venstre knap", + "right-button" : "Højre knap", + "alarm-colors" : "Alarmfarver", + "hook-color" : "Krogfarve" + } + }, + "item" : { + "selected" : "Valgt" + }, + "js-func" : { + "no-return-error" : "Funktionen skal returnere en værdi!", + "return-type-mismatch" : "Funktionen skal returnere en værdi af typen '{{type}}'!", + "tidy" : "Ryd op", + "mini" : "Mini", + "modules" : "Moduler", + "remove-module" : "Fjern modul", + "no-modules" : "Ingen moduler konfigureret", + "add-module" : "Tilføj modul", + "module-alias" : "Alias", + "invalid-module-alias-name" : "Ugyldigt aliasnavn", + "module-resource" : "JS modulressource", + "not-unique-module-aliases-error" : "Modulaliaser skal være unikke!", + "show-module-info" : "Vis modulinfo", + "show-module-source-code" : "Vis modulens kildekode", + "module-members" : "Modulmedlemmer", + "module-no-members" : "Modulet har ingen eksporterede medlemmer", + "module-load-error" : "Fejl ved indlæsning af modul", + "source-code" : "Kildekode", + "source-code-load-error" : "Fejl ved indlæsning af kildekode", + "no-js-module-text" : "Ingen JS-moduler fundet", + "no-js-module-matching" : "Ingen JS-moduler matcher '{{module}}'" + }, + "key-val" : { + "key" : "Nøgle", + "value" : "Værdi", + "remove-entry" : "Fjern post", + "add-entry" : "Tilføj post", + "no-data" : "Ingen poster" + }, + "layout" : { + "layout" : "Layout", + "layouts" : "Layouts", + "manage" : "Administrer layouts", + "settings" : "Layoutindstillinger", + "color" : "Farve", + "main" : "Hoved", + "right" : "Højre", + "left" : "Venstre", + "select" : "Vælg mål-layout", + "percentage-width" : "Procentvis bredde (%)", + "fixed-width" : "Fast bredde (px)", + "left-width" : "Venstre kolonne (%)", + "right-width" : "Højre kolonne (%)", + "pick-fixed-side" : "Fast side: ", + "layout-fixed-width" : "Fast bredde (px)", + "value-min-error" : "Værdien skal være større end {{min}}{{unit}}", + "value-max-error" : "Værdien skal være mindre end {{max}}{{unit}}", + "layout-fixed-width-required" : "Fast bredde er påkrævet", + "right-width-percentage-required" : "Procentdel for højre side er påkrævet", + "left-width-percentage-required" : "Procentdel for venstre side er påkrævet", + "divider" : "Skillelinje", + "right-side" : "Layout for højre side", + "left-side" : "Layout for venstre side", + "add-new-breakpoint" : "Tilføj nyt breakpoint", + "breakpoint" : "Breakpoint", + "breakpoints" : "Breakpoints", + "copy-from" : "Kopiér fra", + "size" : "Størrelse", + "delete-breakpoint-title" : "Er du sikker på, at du vil slette breakpointet '{{name}}'?", + "delete-breakpoint-text" : "Bemærk venligst, efter bekræftelse vil breakpointet ikke kunne gendannes, og indstillingerne vil vende tilbage til standard-breakpointet." + }, + "legend" : { + "direction" : "Retning", + "position" : "Position", + "show-values" : "Vis værdier", + "min-option" : "Min", + "max-option" : "Maks", + "average-option" : "Gennemsnit", + "total-option" : "Total", + "latest-option" : "Seneste", + "sort-legend" : "Sorter datanøgler i forklaring", + "show-max" : "Vis maksimal værdi", + "show-min" : "Vis minimumsværdi", + "show-avg" : "Vis gennemsnitsværdi", + "show-total" : "Vis totalværdi", + "show-latest" : "Vis seneste værdi", + "settings" : "Forklaringsindstillinger", + "min" : "min", + "max" : "maks", + "avg" : "gns", + "total" : "total", + "latest" : "seneste", + "Min" : "Min", + "Max" : "Maks", + "Avg" : "Gns", + "Total" : "Total", + "Latest" : "Seneste", + "comparison-time-ago" : { + "previousInterval" : "(forrige interval)", + "customInterval" : "(brugerdefineret interval)", + "days" : "(dag siden)", + "weeks" : "(uge siden)", + "months" : "(måned siden)", + "years" : "(år siden)" + }, + "column-title" : "Kolonne titel", + "label" : "Etiket", + "value" : "Værdi" + }, + "login" : { + "login" : "Log ind", + "request-password-reset" : "Anmod om nulstilling af adgangskode", + "reset-password" : "Nulstil adgangskode", + "create-password" : "Opret adgangskode", + "two-factor-authentication" : "To-faktor-godkendelse", + "passwords-mismatch-error" : "De indtastede adgangskoder skal være ens!", + "password-again" : "Adgangskode igen", + "sign-in" : "Log venligst ind", + "username" : "Brugernavn (email)", + "remember-me" : "Husk mig", + "forgot-password" : "Glemt adgangskode?", + "password-reset" : "Nulstilling af adgangskode", + "expired-password-reset-message" : "Dine loginoplysninger er udløbet! Opret venligst en ny adgangskode.", + "new-password" : "Ny adgangskode", + "new-password-again" : "Bekræft ny adgangskode", + "password-link-sent-message" : "Nulstillingslink er sendt", + "email" : "Email", + "invalid-email-format" : "Ugyldigt emailformat.", + "login-with" : "Log ind med {{name}}", + "or" : "eller", + "error" : "Loginfejl", + "verify-your-identity" : "Bekræft din identitet", + "select-way-to-verify" : "Vælg en metode til bekræftelse", + "resend-code" : "Send kode igen", + "resend-code-wait" : "Send kode igen om { time, plural, =1 {1 sekund} other {# sekunder} }", + "try-another-way" : "Prøv en anden metode", + "totp-auth-description" : "Indtast sikkerhedskoden fra din autentifikator-app.", + "totp-auth-placeholder" : "Kode", + "sms-auth-description" : "En sikkerhedskode er sendt til din telefon på {{contact}}.", + "sms-auth-placeholder" : "SMS-kode", + "email-auth-description" : "En sikkerhedskode er sendt til din emailadresse på {{contact}}.", + "email-auth-placeholder" : "Email-kode", + "backup-code-auth-description" : "Indtast en af dine backupkoder.", + "backup-code-auth-placeholder" : "Backupkode", + "activation-link-expired" : "Aktiveringslinket er udløbet", + "activation-link-expired-message" : "Linket til at aktivere din profil er udløbet. Du kan vende tilbage til login-siden for at modtage en ny email.", + "reset-password-link-expired" : "Nulstillingslinket for adgangskode er udløbet", + "reset-password-link-expired-message" : "Linket til at nulstille din adgangskode er udløbet. Du kan vende tilbage til login-siden for at modtage en ny email." + }, + "mobile" : { + "add-application" : "Tilføj applikation", + "app-id" : "App-ID", + "app-id-required" : "App-ID er påkrævet", + "app-id-pattern" : "Ugyldigt format for App-ID", + "app-store-link" : "App Store-link", + "app-store-link-required" : "App Store-link er påkrævet", + "application-details" : "Applikationsdetaljer", + "application-package" : "Applikationspakke", + "application-secret" : "Applikationshemmelighed", + "application-secret-required" : "Applikationshemmelighed er påkrævet", + "application" : "Applikation", + "applications" : "Applikationer", + "copy-app-id" : "Kopiér App-ID", + "copy-app-store-link" : "Kopiér App Store-link", + "copy-application-package" : "Kopiér applikationspakke", + "copy-application-secret" : "Kopiér applikationshemmelighed", + "copy-google-play-link" : "Kopiér Google Play-link", + "copy-sha256-certificate-fingerprints" : "Kopiér SHA256-certifikatfingeraftryk", + "delete-application" : "Slet applikation", + "delete-application-button-text" : "Jeg forstår konsekvenserne, slet applikationen", + "delete-application-text" : "Denne handling kan ikke fortrydes. Dette vil permanent slette din applikation.
Hvis du ikke vil slette den permanent, kan du suspendere applikationen midlertidigt.
For at slette applikationen alligevel, indtast venligst \"{{phrase}}\" for at bekræfte.", + "delete-application-title-short" : "Er du sikker på, at du vil slette applikationen '{{name}}'?", + "delete-application-text-short" : "Vær forsigtig, efter bekræftelsen vil applikationen og alle relaterede data ikke kunne gendannes.", + "delete-application-phrase" : "slet applikation", + "delete-applications-bundle-text" : "Vær forsigtig, efter bekræftelsen vil mobilpakken og alle relaterede data ikke kunne gendannes.", + "delete-applications-bundle-title" : "Er du sikker på, at du vil slette mobilpakken '{{bundleName}}'?", + "generate-application-secret" : "Generér applikationshemmelighed", + "google-play-link" : "Google Play-link", + "google-play-link-required" : "Google Play-link er påkrævet", + "latest-version" : "Seneste version", + "min-version" : "Minimumsversion", + "invalid-version-pattern" : "Ugyldigt versionsformat. Brug formatet: major.minor.patch (f.eks. 1.0.0).", + "mobile-center" : "Mobilcenter", + "mobile-package" : "Applikationspakke", + "mobile-package-max-length" : "Applikationspakken skal være mindre end 256 tegn", + "mobile-package-required" : "Applikationspakken er påkrævet.", + "mobile-package-pattern" : "Ugyldigt format for applikationspakke", + "no-application" : "Ingen applikationer fundet", + "no-bundles" : "Ingen pakker fundet", + "platform-type" : "Platformstype", + "search-application" : "Søg applikationer", + "search-bundles" : "Søg pakker", + "set" : "Angiv", + "sha256-certificate-fingerprints" : "SHA256-certifikatfingeraftryk", + "sha256-certificate-fingerprints-required" : "SHA256-certifikatfingeraftryk er påkrævet", + "sha256-certificate-fingerprints-pattern" : "Ugyldigt format for SHA256-certifikatfingeraftryk", + "show-hidden-pages" : "Vis skjulte sider", + "status" : "Status", + "status-type" : { + "deprecated" : "Forældet", + "draft" : "Udkast", + "published" : "Udgivet", + "suspended" : "Suspenderet" + }, + "store-information" : "Butiksinformation", + "version-information" : "Versionsinformation", + "min-version-release-notes" : "Udgivelsesnoter for minimumsversion", + "latest-version-release-notes" : "Udgivelsesnoter for seneste version", + "bundle" : "Pakke", + "bundles" : "Pakker", + "add-bundle" : "Tilføj pakke", + "title" : "Titel", + "title-required" : "Titel er påkrævet", + "title-cannot-contain-only-spaces" : "Titlen må ikke kun indeholde mellemrum", + "title-max-length" : "Titlen skal være under 256 tegn", + "oauth-clients" : "OAuth 2.0-klienter", + "android-app" : "Android-app", + "android-application" : "Android-applikation", + "ios-app" : "iOS-app", + "ios-application" : "iOS-applikation", + "invalid-store-link" : "Ugyldigt butiklink", + "enable-oauth" : "Aktiver OAuth 2.0", + "enable-self-registration" : "Aktiver selvregistrering", + "edit-bundle" : "Rediger pakke", + "description" : "Beskrivelse", + "basic-settings" : "Grundindstillinger", + "no-application-matching" : "Ingen applikation fundet der matcher '{{entity}}'.", + "no-bundle-matching" : "Ingen pakke fundet der matcher '{{entity}}'.", + "application-required" : "Applikation er påkrævet.", + "bundle-required" : "Pakke er påkrævet.", + "no-application-text" : "Ingen applikationer fundet", + "no-bundle-text" : "Ingen pakke fundet", + "layout" : "Layout", + "pages" : "Sider", + "hide-all-pages" : "Skjul alle sider", + "reset-to-default-pages" : "Nulstil til standardsider", + "add-specific-page" : "Tilføj specifik side", + "visible" : "Synlig", + "hidden" : "Skjult", + "reset-to-page-default" : "Nulstil siden til standard", + "mobile-599" : "Mobil (maks 599px)", + "tablet-959" : "Tablet (maks 959px)", + "max-element-number" : "Maksimalt antal elementer", + "page-name" : "Sidenavn", + "page-name-required" : "Sidenavn er påkrævet.", + "page-name-cannot-contain-only-spaces" : "Sidenavn må ikke kun indeholde mellemrum.", + "page-name-max-length" : "Sidenavn skal være under 256 tegn", + "page-type" : "Sidetype", + "pages-types" : { + "dashboard" : "Dashboard", + "web-view" : "Webvisning", + "custom" : "Brugerdefineret" + }, + "url" : "URL", + "invalid-url-format" : "Ugyldigt URL-format", + "path" : "Sti", + "invalid-path-format" : "Ugyldigt stiformat", + "custom-page" : "Brugerdefineret side", + "edit-page" : "Rediger side", + "edit-custom-page" : "Rediger brugerdefineret side", + "delete-page" : "Slet side", + "qr-code-widget" : "QR-kode-widget", + "type-here" : "Skriv her", + "configuration-dialog" : "Konfigurationsdialog", + "configuration-app" : "Konfigurationsapp", + "configuration-step" : { + "prepare-environment-title" : "Forbered udviklingsmiljø", + "prepare-environment-text" : "Flutter ThingsBoard Mobile Application kræver Flutter SDK. Følg vejledningen for at konfigurere Flutter SDK.", + "get-source-code-title" : "Hent appens kildekode", + "get-source-code-text" : "Du kan hente Flutter ThingsBoard Mobile Application kildekode ved at klone den fra GitHub-repositoriet:", + "configure-api-title" : "Konfigurer ThingsBoard API-endepunkt", + "configure-api-text" : "Åbn projektet flutter_thingsboard_pe_app i din editor/IDE. Rediger:", + "configure-api-hint" : "Angiv værdien af konstanten thingsBoardApiEndpoint, så den matcher API-endepunktet på din ThingsBoard-serverinstans. Brug ikke \"localhost\" eller \"127.0.0.1\" som værter.", + "run-app-title" : "Kør appen", + "run-app-text" : "Kør appen som beskrevet i din IDE.\nHvis du bruger terminalen, kan du køre appen med følgende kommando:", + "more-information" : "Detaljeret information findes i vores dokumentation for Kom godt i gang.", + "getting-started" : "Kom godt i gang", + "configure-package-title" : "Konfigurer applikationspakke", + "configure-package-text" : "Du kan manuelt ændre applikationspakken eller bruge et tredjeparts CLI-værktøj.", + "configure-package-text-install" : "For at installere Rename CLI-værktøjet skal du køre følgende kommando:", + "configure-package-run-commands" : "Kør disse kommandoer i projektets rodmappe:" } }, - "widgets": { - "date-range-navigator": { - "localizationMap": { - "Sun": "Søn", - "Mon": "Man", - "Tue": "Tir", - "Wed": "Ons", - "Thu": "Tor", - "Fri": "Fre", - "Sat": "Lør", - "Jan": "Jan", - "Feb": "Feb", - "Mar": "Mar", - "Apr": "Apr", - "May": "Maj", - "Jun": "Jun", - "Jul": "Jul", - "Aug": "Aug", - "Sep": "Sep", - "Oct": "Okt", - "Nov": "Nov", - "Dec": "Dec", - "January": "Januar", - "February": "Februar", - "March": "Marts", - "April": "April", - "June": "Juni", - "July": "Juli", - "August": "August", - "September": "September", - "October": "Oktober", - "November": "November", - "December": "December", - "Custom Date Range": "Brugerdefineret datointerval", - "Date Range Template": "Skabelon for datointerval", - "Today": "I dag", - "Yesterday": "I går", - "This Week": "Denne uge", - "Last Week": "Sidste uge", - "This Month": "Denne måned", - "Last Month": "Sidste måned", - "Year": "År", - "This Year": "I år", - "Last Year": "Sidste år", - "Date picker": "Datovælger", - "Hour": "Time", - "Day": "Dag", - "Week": "Uge", - "2 weeks": "2 uger", - "Month": "Måned", - "3 months": "3 måneder", - "6 months": "6 måneder", - "Custom interval": "Brugerdefineret interval", - "Interval": "Interval", - "Step size": "Trinstørrelse", - "Ok": "Okay" + "notification" : { + "action-button" : "Handlingsknap", + "action-type" : "Handlingstype", + "active" : "Aktiv", + "add-notification-recipients-group" : "Tilføj gruppe af modtagere", + "add-notification-template" : "Tilføj notifikationsskabelon", + "add-recipient" : "Tilføj modtager", + "add-recipients" : "Tilføj modtagere", + "add-rule" : "Tilføj regel", + "add-stage" : "Tilføj trin", + "add-template" : "Tilføj skabelon", + "after" : "Efter", + "alarm-assignment-trigger-settings" : "Indstillinger for alarmtildelingsudløser", + "alarm-comment-trigger-settings" : "Indstillinger for alarmkommentarudløser", + "alarm-trigger-settings" : "Indstillinger for alarmudløser", + "all" : "Alle", + "api-feature-hint" : "Hvis feltet er tomt, anvendes udløseren på alle API-funktioner", + "api-usage-trigger-settings" : "Indstillinger for API-brugsudløser", + "new-platform-version-trigger-settings" : "Indstillinger for udløser af ny platformsversion", + "rate-limits-trigger-settings" : "Indstillinger for udløser ved overskredne hastighedsgrænser", + "task-processing-failure-trigger-settings" : "Indstillinger for udløser ved fejl i opgavebehandling", + "at-least-one-should-be-selected" : "Mindst én skal vælges", + "basic-settings" : "Grundindstillinger", + "button-text" : "Knaptekst", + "button-text-required" : "Knaptekst er påkrævet", + "button-text-max-length" : "Knaptekst skal være mindre end eller lig med {{ length }} tegn", + "compose" : "Sammensæt", + "conversation" : "Samtale", + "conversation-required" : "Samtale er påkrævet", + "copy-notification-template" : "Kopiér notifikationsskabelon", + "copy-rule" : "Kopiér regel", + "copy-template" : "Kopiér skabelon", + "create-new" : "Opret ny", + "created" : "Oprettet", + "customize-messages" : "Tilpas beskeder", + "delete-notification-text" : "Vær forsigtig, efter bekræftelsen vil notifikationen ikke kunne gendannes.", + "delete-notification-title" : "Er du sikker på, at du vil slette notifikationen?", + "delete-notifications-text" : "Vær forsigtig, efter bekræftelsen vil notifikationerne ikke kunne gendannes.", + "delete-notifications-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 notifikation} other {# notifikationer} }?", + "delete-recipient-text" : "Vær forsigtig, efter bekræftelsen vil modtageren ikke kunne gendannes.", + "delete-recipient-title" : "Er du sikker på, at du vil slette modtageren '{{recipientName}}'?", + "delete-recipients-text" : "Vær forsigtig, efter bekræftelsen vil modtagerne ikke kunne gendannes.", + "delete-recipients-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 modtager} other {# modtagere} }?", + "delete-request-text" : "Vær forsigtig, efter bekræftelsen vil anmodningen ikke kunne gendannes.", + "delete-request-title" : "Er du sikker på, at du vil slette anmodningen?", + "delete-requests-text" : "Vær forsigtig, efter bekræftelsen vil anmodningerne ikke kunne gendannes.", + "delete-requests-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 anmodning} other {# anmodninger} }?", + "delete-rule-text" : "Vær forsigtig, efter bekræftelsen vil reglen ikke kunne gendannes.", + "delete-rule-title" : "Er du sikker på, at du vil slette reglen '{{ruleName}}'?", + "delete-rules-text" : "Vær forsigtig, efter bekræftelsen vil reglerne ikke kunne gendannes.", + "delete-rules-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 regel} other {# regler} }?", + "delete-template-text" : "Vær forsigtig, efter bekræftelsen vil skabelonen ikke kunne gendannes.", + "delete-template-title" : "Er du sikker på, at du vil slette skabelonen '{{templateName}}'?", + "delete-templates-text" : "Vær forsigtig, efter bekræftelsen vil skabelonerne ikke kunne gendannes.", + "delete-templates-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 skabelon} other {# skabeloner} }?", + "deleted" : "Slettet", + "delivery-method" : { + "delivery-method" : "Leveringsmetode", + "email" : "Email", + "email-preview" : "Forhåndsvisning af emailnotifikation", + "slack" : "Slack", + "slack-preview" : "Forhåndsvisning af Slack-notifikation", + "microsoft-teams" : "Microsoft Teams", + "microsoft-teams-preview" : "Forhåndsvisning af Microsoft Teams-notifikation", + "sms" : "SMS", + "sms-preview" : "Forhåndsvisning af SMS-notifikation", + "web" : "Web", + "web-preview" : "Forhåndsvisning af webnotifikation", + "mobile-app" : "Mobilapp", + "mobile-app-preview" : "Forhåndsvisning af mobilapp-notifikation" + }, + "delivery-method-not-configure-click" : "Leveringsmetode er ikke konfigureret. Klik for at konfigurere.", + "delivery-method-not-configure-contact" : "Leveringsmetode er ikke konfigureret. Kontakt din systemadministrator.", + "delivery-methods" : "Leveringsmetoder", + "description" : "Beskrivelse", + "device-activity-trigger-settings" : "Indstillinger for udløser af enhedsaktivitet", + "device-list-rule-hint" : "Hvis feltet er tomt, anvendes udløseren på alle enheder", + "device-profiles-list-rule-hint" : "Hvis feltet er tomt, anvendes udløseren på alle enhedsprofiler", + "disabled" : "Deaktiveret", + "edge-trigger-settings" : "Indstillinger for Edge-udløser", + "edge-list-rule-hint" : "Hvis feltet er tomt, anvendes udløseren på alle Edge-instanser", + "edit-notification-recipients-group" : "Rediger gruppe af modtagere", + "edit-notification-template" : "Rediger notifikationsskabelon", + "edit-rule" : "Rediger regel", + "edit-template" : "Rediger skabelon", + "enabled" : "Aktiveret", + "entities-limit-trigger-settings" : "Indstillinger for udløser ved entitetsgrænse", + "entity-action-trigger-settings" : "Indstillinger for udløser af entitetshandling", + "entity-type" : "Enhedstype", + "escalation-chain" : "Eskaleringskæde", + "failed-send" : "Fejl ved afsendelse", + "fails" : "{ count, plural, =1 {1 fejl} other {# fejl} }", + "filter" : "Filter", + "first-recipient" : "Første modtager", + "inactive" : "Inaktiv", + "inbox" : "Indbakke", + "notification-inbox" : "Notifikationer / Indbakke", + "input-field-support-templatization" : "Inputfelt understøtter templatization.", + "input-fields-support-templatization" : "Inputfelter understøtter templatization.", + "link" : "Link", + "link-required" : "Link er påkrævet", + "link-type" : { + "dashboard" : "Åbn dashboard", + "link" : "Åbn URL-link" + }, + "loading-notifications" : "Indlæser notifikationer...", + "management" : "Notifikationsstyring", + "mark-all-as-read" : "Markér alle som læst", + "mark-as-read" : "Markér som læst", + "message" : "Besked", + "message-required" : "Besked er påkrævet", + "message-max-length" : "Besked skal være mindre end eller lig med {{ length }} tegn", + "name" : "Navn", + "name-required" : "Navn er påkrævet", + "new-notification" : "Ny notifikation", + "no-inbox-notification" : "Ingen notifikation fundet", + "no-notification-request" : "Ingen notifikationsanmodning", + "no-notification-templates" : "Ingen notifikationsskabeloner fundet", + "no-notifications-yet" : "Ingen notifikationer endnu", + "no-recipients-notification" : "Ingen modtagere notifikation", + "no-recipients-matching" : "Ingen modtagere matcher '{{entity}}'.", + "no-recipients-text" : "Ingen modtager fundet", + "no-rule" : "Ingen regel konfigureret", + "no-rules-notification" : "Ingen regler notifikation", + "no-severity-found" : "Ingen alvorlighed fundet", + "no-severity-matching" : "'{{severity}}' ikke fundet.", + "no-template-matching" : "Ingen ressource matcher '{{template}}'.", + "not-found-slack-recipient" : "Slack-modtager ikke fundet", + "notification" : "Notifikation", + "notification-center" : "Notifikationscenter", + "notification-tap-action" : "Handling ved tryk på notifikation", + "notification-tap-action-hint" : "Hvis ikke aktiveret, anvendes standard dashboard for alarmer", + "notify" : "notificer", + "notify-again" : "Notificer igen", + "notify-alarm-action" : { + "acknowledged" : "Alarm bekræftet", + "assigned" : "Alarm tildelt", + "cleared" : "Alarm ryddet", + "created" : "Alarm oprettet", + "severity-changed" : "Alvorlighed ændret", + "unassigned" : "Alarm ikke tildelt" + }, + "notify-on" : "Notificer ved", + "notify-on-comment-update" : "Notificer ved kommentaropdatering", + "notify-on-required" : "Notificer ved er påkrævet", + "notify-on-unassign" : "Notificer ved afkobling", + "notify-only-user-comments" : "Notificer kun brugernes kommentarer", + "only-rule-chain-lifecycle-failures" : "Kun fejl i livscyklus for regelkæder", + "only-rule-node-lifecycle-failures" : "Kun fejl i livscyklus for regelnoder", + "platform-users" : "Platformbrugere", + "rate-limits" : "Grænser for frekvens", + "rate-limits-hint" : "Hvis feltet er tomt, anvendes udløseren på alle grænser for frekvens", + "recipient" : "Modtager", + "recipient-group" : "Modtagergruppe", + "recipient-type" : { + "affected-tenant-administrators" : "Berørte tenantadministratorer", + "affected-user" : "Berørt bruger", + "all-users" : "Alle brugere", + "customer-users" : "Kunde-brugere", + "system-administrators" : "Systemadministratorer", + "tenant-administrators" : "Tenantadministratorer", + "user-filters" : "Brugerfilter", + "user-list" : "Brugerliste", + "users-entity-owner" : "Brugere af entitetens ejer" + }, + "recipients" : "Modtagere", + "notification-recipient" : "Notifikationsmodtager", + "notification-recipient-required" : "Notifikationsmodtager er påkrævet.", + "notification-recipients" : "Notifikationer / Modtagere", + "recipients-count" : "{ count, plural, =1 {1 modtager} other {# modtagere} }", + "recipients-required" : "Modtagere er påkrævet", + "refresh-allow-delivery-method" : "Opdater tilladte leveringsmetoder", + "request-search" : "Søg i anmodninger", + "request-status" : { + "processing" : "Behandler", + "scheduled" : "Planlagt", + "sent" : "Sendt" + }, + "review" : "Gennemse", + "rule" : "Regel", + "rule-chain-list-rule-hint" : "Hvis feltet er tomt, anvendes udløseren på alle regelkæder", + "rule-engine-events-trigger-settings" : "Indstillinger for udløser af regelmotorhændelser", + "rule-engine-filter" : "Regelmotorfilter", + "rule-name" : "Regelnavn", + "rule-name-required" : "Navn er påkrævet", + "rule-disable" : "Deaktiver notifikationsregel", + "rule-enable" : "Aktivér notifikationsregel", + "rule-node-filter" : "Filtrér regelnoder", + "rules" : "Regler", + "notification-rules" : "Notifikationer / Regler", + "scheduler-later" : "Planlæg til senere", + "search-notification" : "Søg notifikationer", + "search-recipients" : "Søg modtagere", + "search-rules" : "Søg regler", + "search-templates" : "Søg skabeloner", + "see-documentation" : "Se dokumentation", + "selected-notifications" : "{ count, plural, =1 {1 notifikation} other {# notifikationer} } valgt", + "selected-recipients" : "{ count, plural, =1 {1 modtager} other {# modtagere} } valgt", + "selected-requests" : "{ count, plural, =1 {1 anmodning} other {# anmodninger} } valgt", + "selected-rules" : "{ count, plural, =1 {1 regel} other {# regler} } valgt", + "selected-template" : "{ count, plural, =1 {1 skabelon} other {# skabeloner} } valgt", + "send-notification" : "Send notifikation", + "sent" : "Sendt", + "setup" : "Opsætning", + "notification-sent" : "Notifikationer / Sendt", + "set-entity-from-notification" : "Sæt entitet fra notifikation til dashboard-tilstand", + "slack-chanel-type" : "Slack-kanaltype", + "slack-chanel-types" : { + "direct" : "Direkte besked", + "private-channel" : "Privat kanal", + "public-channel" : "Offentlig kanal" + }, + "start-from-scratch" : "Start fra bunden", + "status" : "Status", + "stop-escalation-alarm-status-become" : "Stop eskaleringen når alarmstatus bliver:", + "subject" : "Emne", + "subject-required" : "Emne er påkrævet", + "subject-max-length" : "Emnet må højst være {{ length }} tegn", + "template" : "Skabelon", + "template-name" : "Skabelonnavn", + "template-required" : "Skabelon er påkrævet", + "template-type" : { + "alarm" : "Alarm", + "alarm-assignment" : "Alarmtildeling", + "alarm-comment" : "Alarmkommentar", + "api-usage-limit" : "API-brugsgrænse", + "device-activity" : "Enhedsaktivitet", + "entities-limit" : "Enhedsgrænse", + "entity-action" : "Entitetshandling", + "general" : "Generel", + "rule-engine-lifecycle-event" : "Livscyklus for regelmotor", + "rule-node" : "Regelnode", + "new-platform-version" : "Ny platformversion", + "rate-limits" : "Overskredet grænse for frekvens", + "edge-communication-failure" : "Edge-kommunikationsfejl", + "edge-connection" : "Edge-forbindelse", + "task-processing-failure" : "Fejl i opgavebehandling" + }, + "templates" : "Skabeloner", + "notification-templates" : "Notifikationer / Skabeloner", + "tenant-profiles-list-rule-hint" : "Hvis feltet er tomt, anvendes udløseren på alle tenant-profiler", + "tenants-list-rule-hint" : "Hvis feltet er tomt, anvendes udløseren på alle tenants", + "threshold" : "Tærskel", + "theme-color" : "Temafarve", + "time" : "Tid", + "track-rule-node-events" : "Spor regelnodehændelser", + "trigger" : { + "alarm" : "Alarm", + "alarm-assignment" : "Alarmtildeling", + "alarm-comment" : "Alarmkommentar", + "api-usage-limit" : "API-brugsgrænse", + "device-activity" : "Enhedsaktivitet", + "entities-limit" : "Entitetsgrænse", + "entity-action" : "Entitetshandling", + "rule-engine-lifecycle-event" : "Regelmotor livscyklusbegivenhed", + "new-platform-version" : "Ny platformversion", + "rate-limits" : "Overskredet frekvensgrænse", + "edge-connection" : "Edge-forbindelse", + "edge-communication-failure" : "Edge-kommunikationsfejl", + "task-processing-failure" : "Fejl i opgavebehandling", + "trigger" : "Udløser", + "trigger-required" : "Udløser er påkrævet" + }, + "type" : "Type", + "unread" : "Ulæst", + "updated" : "Opdateret", + "use-deprecated-webhook-connectors" : "Brug forældede Webhook-forbindelser", + "use-old-api" : "Brug gammel API", + "use-template" : "Brug skabelon", + "view-all" : "Se alle", + "warning" : "Advarsel", + "webhook-url" : "Webhook URL", + "webhook-url-required" : "Webhook URL er påkrævet", + "workflow-url" : "Workflow URL", + "workflow-url-required" : "Workflow URL er påkrævet", + "channel-name" : "Kanalnavn", + "channel-name-required" : "Kanalnavn er påkrævet", + "settings" : { + "notification-settings" : "Notifikationsindstillinger", + "reset-all" : "Nulstil alle indstillinger", + "reset-all-title" : "Er du sikker på, at du vil nulstille formularen?", + "reset-all-text" : "Efter bekræftelse vil indstillingsformularen blive nulstillet til standardværdien og gemt.", + "type" : "Type", + "enable-all" : "Aktivér alle", + "disable-all" : "Deaktiver alle", + "delivery-not-configured" : "Leveringsmetode er ikke konfigureret" + } + }, + "ota-update" : { + "add" : "Tilføj pakke", + "assign-firmware" : "Tildelt firmware", + "assign-firmware-required" : "Tildelt firmware er påkrævet", + "assign-software" : "Tildelt software", + "assign-software-required" : "Tildelt software er påkrævet", + "auto-generate-checksum" : "Generér checksum automatisk", + "checksum" : "Checksum", + "checksum-hint" : "Hvis checksum er tom, vil den blive genereret automatisk", + "checksum-algorithm" : "Checksum-algoritme", + "checksum-copied-message" : "Pakke-checksum er blevet kopieret til udklipsholderen", + "change-firmware" : "Ændring af firmware kan medføre opdatering af { count, plural, =1 {1 enhed} other {# enheder} }.", + "change-software" : "Ændring af software kan medføre opdatering af { count, plural, =1 {1 enhed} other {# enheder} }.", + "chose-compatible-device-profile" : "Den uploadede pakke vil kun være tilgængelig for enheder med den valgte profil.", + "chose-firmware-distributed-device" : "Vælg firmware, der skal distribueres til enhederne", + "chose-software-distributed-device" : "Vælg software, der skal distribueres til enhederne", + "content-type" : "Indholdstype", + "copy-checksum" : "Kopiér checksum", + "copy-direct-url" : "Kopiér direkte URL", + "copyId" : "Kopiér pakke-ID", + "copied" : "Kopieret!", + "delete" : "Slet pakke", + "delete-ota-update-text" : "Vær forsigtig, efter bekræftelse vil OTA-opdateringen ikke kunne gendannes.", + "delete-ota-update-title" : "Er du sikker på, at du vil slette OTA-opdateringen '{{title}}'?", + "delete-ota-updates-text" : "Vær forsigtig, efter bekræftelse vil alle valgte OTA-opdateringer blive fjernet.", + "delete-ota-updates-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 OTA-opdatering} other {# OTA-opdateringer} }?", + "description" : "Beskrivelse", + "direct-url" : "Direkte URL", + "direct-url-copied-message" : "Pakkens direkte URL er blevet kopieret til udklipsholderen", + "direct-url-required" : "Direkte URL er påkrævet", + "download" : "Download pakke", + "drop-file" : "Slip en pakkefil eller klik for at vælge en fil til upload.", + "drop-package-file-or" : "Træk og slip en pakkefil eller", + "file-name" : "Filnavn", + "file-size" : "Filstørrelse", + "file-size-bytes" : "Filstørrelse i bytes", + "idCopiedMessage" : "Pakke-ID er blevet kopieret til udklipsholderen", + "no-firmware-matching" : "Ingen kompatible firmware OTA-opdateringspakker fundet, der matcher '{{entity}}'.", + "no-firmware-text" : "Ingen kompatible firmware OTA-opdateringspakker tilgængelige.", + "no-packages-text" : "Ingen pakker fundet", + "no-software-matching" : "Ingen kompatible software OTA-opdateringspakker fundet, der matcher '{{entity}}'.", + "no-software-text" : "Ingen kompatible software OTA-opdateringspakker tilgængelige.", + "ota-update" : "OTA-opdatering", + "ota-update-details" : "OTA-opdateringsdetaljer", + "ota-updates" : "OTA-opdateringer", + "package-file" : "Pakkefil", + "package-type" : "Pakketype", + "packages-repository" : "Pakkelager", + "search" : "Søg pakker", + "selected-package" : "{ count, plural, =1 {1 pakke} other {# pakker} } valgt", + "title" : "Titel", + "title-required" : "Titel er påkrævet.", + "title-max-length" : "Titel skal være mindre end 256 tegn", + "types" : { + "firmware" : "Firmware", + "software" : "Software" + }, + "upload-binary-file" : "Upload binær fil", + "use-external-url" : "Brug ekstern URL", + "version" : "Version", + "version-required" : "Version er påkrævet.", + "version-tag" : "Versionsmærke", + "version-tag-hint" : "Brugertilpasset mærke skal matche den versionsinformation, som din enhed rapporterer.", + "version-max-length" : "Version skal være mindre end 256 tegn", + "warning-after-save-no-edit" : "Når pakken er uploadet, vil du ikke kunne redigere titel, version, enhedsprofil og pakketype." + }, + "position" : { + "top" : "Top", + "bottom" : "Bund", + "left" : "Venstre", + "right" : "Højre" + }, + "profile" : { + "profile" : "Profil", + "last-login-time" : "Sidste login", + "change-password" : "Skift adgangskode", + "current-password" : "Nuværende adgangskode", + "copy-jwt-token" : "Kopiér JWT-token", + "jwt-token" : "JWT-token", + "token-valid-till" : "Token er gyldig til", + "tokenCopiedSuccessMessage" : "JWT-token er kopieret til udklipsholderen", + "tokenCopiedWarnMessage" : "JWT-token er udløbet! Opdater venligst siden." + }, + "profiles" : { + "profiles" : "Profiler" + }, + "security" : { + "security" : "Sikkerhed", + "general-settings" : "Generelle sikkerhedsindstillinger", + "access-token" : "Adgangstoken", + "access-token-required" : "Adgangstoken er påkrævet", + "clientId" : "Klient-ID", + "clientId-required" : "Klient-ID er påkrævet", + "username" : "Brugernavn", + "username-required" : "Brugernavn er påkrævet", + "ca-cert" : "CA-certifikat", + "2fa" : { + "2fa" : "To-faktor autentificering", + "2fa-description" : "To-faktor autentificering beskytter din konto mod uautoriseret adgang. Du skal blot indtaste en sikkerhedskode ved login.", + "authenticate-with" : "Du kan autentificere med:", + "disable-2fa-provider-text" : "Deaktivering af {{name}} vil gøre din konto mindre sikker", + "disable-2fa-provider-title" : "Er du sikker på, at du vil deaktivere {{name}}?", + "get-new-code" : "Få ny kode", + "main-2fa-method" : "Brug som hovedmetode til to-faktor autentificering", + "dialog" : { + "activation-step-description-email" : "Næste gang du logger ind, vil du blive bedt om at indtaste den sikkerhedskode, der bliver sendt til din e-mailadresse.", + "activation-step-description-sms" : "Næste gang du logger ind, vil du blive bedt om at indtaste den sikkerhedskode, der bliver sendt til telefonnummeret.", + "activation-step-description-totp" : "Næste gang du logger ind, skal du indtaste en to-faktor autentificeringskode.", + "activation-step-label" : "Aktivering", + "backup-code-description" : "Udskriv koderne, så du har dem ved hånden, når du skal logge ind. Du kan kun bruge hver sikkerhedskode én gang.", + "backup-code-warn" : "Når du forlader denne side, kan disse koder ikke vises igen. Gem dem sikkert.", + "download-txt" : "Download (txt)", + "email-step-description" : "Indtast en e-mail, der skal bruges som autentifikator.", + "email-step-label" : "E-mail", + "enable-email-title" : "Aktiver e-mail autentificering", + "enable-sms-title" : "Aktiver SMS autentificering", + "enable-totp-title" : "Aktiver autentificeringsapp", + "enter-verification-code" : "Indtast 6-cifret kode her", + "get-backup-code-title" : "Få sikkerhedskode", + "next" : "Næste", + "scan-qr-code" : "Scan denne QR-kode med din autentificeringsapp", + "send-code" : "Send kode", + "sms-step-description" : "Indtast et telefonnummer til autentificering.", + "sms-step-label" : "Telefonnummer", + "success" : "Succes!", + "totp-step-description-install" : "Du kan installere apps som Google Authenticator, Authy eller Duo.", + "totp-step-description-open" : "Åbn autentificeringsappen på din mobiltelefon.", + "totp-step-label" : "Hent app", + "verification-code" : "6-cifret kode", + "verification-code-invalid" : "Ugyldigt kodeformat", + "verification-code-incorrect" : "Forkert verificeringskode", + "verification-code-many-request" : "For mange forsøg, tjek verificeringskode", + "verification-step-description" : "Indtast en 6-cifret kode, vi netop har sendt til {{address}}", + "verification-step-label" : "Verificering" + }, + "provider" : { + "email" : "E-mail", + "email-description" : "Brug en kode sendt til din e-mail til autentificering.", + "email-hint" : "Autentificeringskoder sendes via e-mail til {{ info }}", + "sms" : "SMS", + "sms-description" : "Brug din telefon til autentificering. Vi sender dig en sikkerhedskode via SMS, når du logger ind.", + "sms-hint" : "Autentificeringskoder sendes som SMS til {{ info }}", + "totp" : "Autentificeringsapp", + "totp-description" : "Brug apps som Google Authenticator, Authy eller Duo til autentificering. De genererer en sikkerhedskode til login.", + "totp-hint" : "Autentificeringsapp er opsat for din konto", + "backup_code" : "Sikkerhedskode", + "backup-code-description" : "Disse udskrivbare engangskoder giver dig mulighed for at logge ind, når du ikke har din telefon.", + "backup-code-hint" : "{{ info }} engangskoder er aktive på nuværende tidspunkt" } }, - "input-widgets": { - "attribute-not-allowed": "Attributparameter kan ikke bruges i denne widget", - "blocked-location": "Geoplaceringen er blokeret i din browser", - "claim-device": "Gør krav på enhed", - "claim-failed": "Kunne ikke gøre krav på enheden!", - "claim-not-found": "Enhed ikke fundet!", - "claim-successful": "Enheden er blevet krævet!", - "date": "Dato", - "device-name": "Enhedsnavn", - "device-name-required": "Enhedsnavn er påkrævet", - "discard-changes": "Kassér ændringer", - "entity-attribute-required": "Entitetsattribut er påkrævet", - "entity-coordinate-required": "Både felter, breddegrad og længdegrad er påkrævet", - "entity-timeseries-required": "Tidsserie for entitet er påkrævet", - "get-location": "Få den aktuelle placering", - "invalid-date": "Ugyldig dato", - "latitude": "Breddegrad", - "longitude": "Længde", - "min-value-error": "", - "max-value-error": "", - "not-allowed-entity": "Den valgte entitet kan ikke have delte attributter", - "no-attribute-selected": "Ingen attribut er valgt", - "no-datakey-selected": "Ingen datanøgle er valgt", - "no-coordinate-specified": "Datanøgle for breddegrad/længdegrad er ikke angivet", - "no-entity-selected": "Ingen entitet valgt", - "no-image": "Intet billede", - "no-support-geolocation": "Din browser understøtter ikke geoplacering", - "no-support-web-camera": "Din browser understøtter ikke kameraer", - "enable-https-use-widget": "Aktivér HTTPS for at bruge denne widget", - "no-found-your-camera": "Kan ikke finde dit kamera", - "no-permission-camera": "Tilladelse blev nægtet af brugeren / Dette website har ikke tilladelse til at bruge kameraet", - "no-timeseries-selected": "Ingen tidsserier valgt", - "secret-key": "Hemmelig nøgle", - "secret-key-required": "Hemmelig nøgle er påkrævet", - "switch-attribute-value": "Skift entitetsattributværdi", - "switch-camera": "Skift kamera", - "switch-timeseries-value": "Skift entitetstidsserieværdi", - "take-photo": "Tag et billede", - "time": "Tid", - "timeseries-not-allowed": "Tidsserieparameter kan ikke bruges i denne widget", - "update-failed": "Opdatering mislykkedes", - "update-successful": "Opdatering lykkedes", - "update-attribute": "Opdatering", - "update-timeseries": "Opdater tidsserier", - "value": "Værdi" + "password-requirement" : { + "at-least" : "Mindst:", + "character" : "{ count, plural, =1 {1 tegn} other {# tegn} }", + "digit" : "{ count, plural, =1 {1 ciffer} other {# cifre} }", + "incorrect-password-try-again" : "Forkert adgangskode. Prøv igen", + "lowercase-letter" : "{ count, plural, =1 {1 lille bogstav} other {# små bogstaver} }", + "new-passwords-not-match" : "De nye adgangskoder matcher ikke", + "password-should-not-contain-spaces" : "Adgangskoden må ikke indeholde mellemrum", + "password-not-meet-requirements" : "Adgangskoden opfylder ikke kravene", + "password-requirements" : "Adgangskodekrav", + "password-should-difference" : "Ny adgangskode skal være forskellig fra den nuværende", + "special-character" : "{ count, plural, =1 {1 specialtegn} other {# specialtegn} }", + "uppercase-letter" : "{ count, plural, =1 {1 stort bogstav} other {# store bogstaver} }", + "at-most" : "Højst:" + } + }, + "relation" : { + "relations" : "Relationer", + "direction" : "Retning", + "clear-relation-type" : "Ryd relationstype", + "search-direction" : { + "FROM" : "Fra", + "TO" : "Til" + }, + "direction-type" : { + "FROM" : "fra", + "TO" : "til" + }, + "from-relations" : "Udgående relationer", + "to-relations" : "Indgående relationer", + "selected-relations" : "{ count, plural, =1 {1 relation} other {# relationer} } valgt", + "type" : "Type", + "to-entity-type" : "Til enhedstype", + "to-entity-name" : "Til enhedsnavn", + "from-entity-type" : "Fra enhedstype", + "from-entity-name" : "Fra enhedsnavn", + "to-entity" : "Til enhed", + "from-entity" : "Fra enhed", + "delete" : "Slet relation", + "relation-type" : "Relationstype", + "relation-type-required" : "Relationstype er påkrævet.", + "relation-type-max-length" : "Relationstype skal være mindre end 256 tegn", + "any-relation-type" : "Enhver type", + "add" : "Tilføj relation", + "edit" : "Rediger relation", + "delete-to-relation-title" : "Er du sikker på, at du vil slette relationen til enheden '{{entityName}}'?", + "delete-to-relation-text" : "Vær forsigtig, efter bekræftelsen vil enheden '{{entityName}}' ikke længere være relateret til den aktuelle enhed.", + "delete-to-relations-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 relation} other {# relationer} }?", + "delete-to-relations-text" : "Vær forsigtig, efter bekræftelsen vil alle valgte relationer blive fjernet, og de tilsvarende enheder vil ikke længere være relateret.", + "delete-from-relation-title" : "Er du sikker på, at du vil slette relationen fra enheden '{{entityName}}'?", + "delete-from-relation-text" : "Vær forsigtig, efter bekræftelsen vil den aktuelle enhed ikke længere være relateret til enheden '{{entityName}}'.", + "delete-from-relations-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 relation} other {# relationer} }?", + "delete-from-relations-text" : "Vær forsigtig, efter bekræftelsen vil alle valgte relationer blive fjernet, og den aktuelle enhed vil ikke længere være relateret til de tilsvarende enheder.", + "remove-relation-filter" : "Fjern relationsfilter", + "remove-filter" : "Fjern filter", + "add-relation-filter" : "Tilføj relationsfilter", + "any-relation" : "Enhver relation", + "relation-filters" : "Relationsfiltre", + "additional-info" : "Yderligere info (JSON)", + "invalid-additional-info" : "Kan ikke analysere yderligere info JSON.", + "no-relations-text" : "Ingen relationer fundet", + "not" : "Ikke" + }, + "resource" : { + "add" : "Tilføj ressource", + "all-types" : "Alle", + "copyId" : "Kopiér ressource-ID", + "delete" : "Slet ressource", + "delete-resource-text" : "Vær forsigtig, efter bekræftelsen vil ressourcen ikke kunne gendannes.", + "delete-resource-title" : "Er du sikker på, at du vil slette ressourcen '{{resourceTitle}}'?", + "delete-resources-action-title" : "Slet { count, plural, =1 {1 ressource} other {# ressourcer} }", + "delete-resources-text" : "Bemærk, at de valgte ressourcer, selv hvis de bruges i enhedsprofiler, vil blive slettet.", + "delete-resources-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 ressource} other {# ressourcer} }?", + "download" : "Download ressource", + "drop-file" : "Slip en ressourcefil eller klik for at vælge en fil, der skal uploades.", + "drop-resource-file-or" : "Træk og slip en ressourcefil eller", + "empty" : "Ressourcen er tom", + "file-name" : "Filnavn", + "idCopiedMessage" : "Ressource-ID er kopieret til udklipsholderen", + "no-resource-matching" : "Ingen ressource matcher '{{widgetsBundle}}'", + "no-resource-text" : "Ingen ressourcer fundet", + "open-widgets-bundle" : "Åbn widgets bundle", + "resource" : "Ressource", + "resource-file" : "Ressourcefil", + "resource-files" : "Ressourcefiler", + "resource-library-details" : "Ressourcedetaljer", + "resource-type" : "Ressourcetype", + "resources-library" : "Ressourcebibliotek", + "search" : "Søg ressourcer", + "selected-resources" : "{ count, plural, =1 {1 ressource} other {# ressourcer} } valgt", + "system" : "System", + "title" : "Titel", + "title-required" : "Titel er påkrævet.", + "title-max-length" : "Titel skal være mindre end 256 tegn", + "type" : { + "jks" : "JKS", + "js-module" : "JS modul", + "lwm2m-model" : "LWM2M model", + "pkcs-12" : "PKCS #12" + }, + "resource-sub-type" : "Undertype", + "sub-type" : { + "image" : "billede", + "scada-symbol" : "Scada symbol", + "extension" : "Udvidelse", + "module" : "Modul" } }, - "icon": { - "icon": "Ikon", - "select-icon": "Vælg ikon", - "material-icons": "Materialeikoner", - "show-all": "Vis alle ikoner" - }, - "subscription": { - "entity-limit-text": "Du kan dog opgradere din abonnementsordning for at øge dine begrænsninger.", - "upgrade-your-plan": "Opgrader abonnementsordning", - "white-labeling-feature": "Funktion til hvid mærkning", - "white-labeling-text-full": "Rebrand ThingsBoard-platformens webinterface med dit virksomheds- eller produktlogo og farveskema på 2 minutter.

Fjern “Powered By” i dashboardets sidefod.
Ingen kodning eller genstart af tjeneste påkrævet. Giv også dine kunder mulighed for at hvidmærke deres interface.", - "enable-white-labeling": "Aktivér hvidmærkningsfunktionen nu ved at opgradere din abonnementsordning!", - "read-more": "Læs mere", - "white-labeling-video-text": "Se videovejledningen nedenfor for at se, hvordan denne funktion fungerer!" - }, - "subscription-error": { - "title": "Misligholdelse af abonnement", - "warning-title": "Abonnementsadvarsel", - "limit-reached": { - "device-count": "", - "asset-count": "" - }, - "feature-disabled": { - "white-labeling": "Hvidmærkningsfunktionen er ikke tilladt af din abonnementsordning!" + "javascript" : { + "add" : "Tilføj JavaScript-ressource", + "delete" : "Slet JavaScript-ressource", + "delete-javascript-resource-text" : "Vær forsigtig, efter bekræftelsen vil JavaScript-ressourcen ikke kunne gendannes.", + "delete-javascript-resource-title" : "Er du sikker på, at du vil slette JavaScript-ressourcen '{{resourceTitle}}'?", + "delete-javascript-resources-action-title" : "Slet JavaScript { count, plural, =1 {1 ressource} other {# ressourcer} }", + "delete-javascript-resources-text" : "Bemærk, at de valgte JavaScript-ressourcer, selv hvis de bruges i JavaScript-funktioner, vil blive slettet.", + "delete-javascript-resources-title" : "Er du sikker på, at du vil slette JavaScript { count, plural, =1 {1 ressource} other {# ressourcer} }?", + "delete-javascript-resource-in-use-text" : "Hvis du stadig ønsker at slette JavaScript-ressourcen, skal du klikke på knappen Slet alligevel.", + "download" : "Download JavaScript-ressource", + "upload-from-file" : "Upload JavaScript fra fil", + "resource-file" : "JavaScript-ressourcefil", + "drop-file" : "Slip en JavaScript-fil eller klik for at vælge en fil, der skal uploades.", + "drop-resource-file-or" : "Træk og slip en JavaScript-fil eller", + "javascript-library" : "JavaScript-bibliotek", + "javascript-type" : "JavaScript-type", + "javascript-resource-details" : "JavaScript-resourcedetaljer", + "javascript-resource-is-in-use" : "JavaScript-ressourcen bruges af andre enheder", + "javascript-resources-are-in-use" : "JavaScript-ressourcer bruges af andre enheder", + "javascript-resource-is-in-use-text" : "JavaScript-ressourcen '{{title}}' blev ikke slettet, fordi den bruges af følgende enheder:", + "javascript-resources-are-in-use-text" : "Ikke alle JavaScript-ressourcer er blevet slettet, fordi de bruges af andre enheder.
Du kan se refererede enheder ved at klikke på knappen Referencer i den tilsvarende ressource-række.
Hvis du stadig ønsker at slette disse JavaScript-ressourcer, skal du vælge dem i tabellen nedenfor og klikke på knappen Slet valgte.", + "search" : "Søg JavaScript-ressourcer", + "selected-javascript-resources" : "{ count, plural, =1 {1 JavaScript-ressource} other {# JavaScript-ressourcer} } valgt", + "no-javascript-resource-text" : "Ingen JavaScript-ressourcer fundet", + "all-types" : "Alle", + "module-script" : "Modulscript" + }, + "rpc" : { + "error" : { + "target-device-is-not-set" : "Målenhed er ikke angivet!", + "invalid-target-entity" : "RPC-kommandoer understøttes ikke af {{entityType}}-enheden.", + "failed-to-resolve-target-device" : "Kunne ikke finde målenhed!", + "request-timeout" : "Forespørgsels-timeout", + "rpc-http-error" : "Fejl: {{status}} - {{statusText}}" } }, - "custom": { - "widget-action": { - "action-cell-button": "Handlingscelleknap", - "row-click": "Ved klik på række", - "polygon-click": "Ved klik på polygon", - "marker-click": "Ved klik på markør", - "tooltip-tag-action": "Værktøjstip for taghandling", - "node-selected": "På valgt knude", - "element-click": "Ved klik på HTML-element", - "pie-slice-click": "Ved klik på udsnit", - "row-double-click": "Ved dobbeltklik på række" - }, - "add-new-facility": { - "add-entity": "Tilføj placering", - "facility-name": "placeringsnavn", - "facility-name-required": "placeringsnavn er påkrævet.", - "title": "Tilføj ny placering" - }, - "delete-facility": { - "are-you-sure-question": "Er du sikker på, at du vil slette placering '{{facilityName}}'?", - "title": "Slet placering" - }, - "facilities": { - "set-location-and-facility-map": "Indstil placering" - }, - "general": { - "account": "Konto", - "accounts": "Konti", - "activation-link-is-sent-to-the-email-address": "Aktiveringslink er sendt til e-mailadressen: '{{email}}'", - "add-ecl": "Tilføj ECL", - "add-ecl-device": "Tilføj ECL-enhed", - "add-edit-ally-email": "Tilføj Ally™ konto", - "add-edit-entity-ally-account": "Tilføj {{ entityName }}'s Ally™ konto", - "add-home": "Tilføj Hjem", - "add-home-area": "Tilføj Hjemmeområde", - "add-new-ally-account": "Tilføj ny konto", - "add-new-location": "Tilføj ny placering", - "add-new-read-only-user": "Tilføj ny skrivebeskyttet bruger", - "add-new-read-only-user-for-facility": "Tilføj ny skrivebeskyttet bruger for '{{facilityName}}'", - "add-vacation-for-zone": "Tilføj ferie for denne zone", - "open-calendar": "Åbn kalender", - "add-zone": "Tilføj zone", - "alarm-code": "Alarmkode", - "alarm-description": "Beskrivelse", - "alarm-message": "Meddelelse", - "alarms-disabled-msg": "Alarmer er deaktiveret.", - "alarms-disabled-state": "Deaktiveret", - "alarms-enabled-msg": "Alarmer er aktiveret.", - "alarms-enabled-state": "Aktiveret", - "all-devices-in-home": "Alle enheder i hjemmet", - "ally-email": "Ally™ e-mail", - "ally-email-access-check-email": "Tjek din e-mail {{ allyEmail }} for at se den 4-cifrede kode fra Ally™ Pro", - "ally-email-access-code-label": "Indtast din adgangskode (4 cifre)", - "ally-email-account-is-missing-or-is-not-valid": "Ally™ e-mailkonto mangler eller er ikke gyldig", - "an-error-occurred-entities-try-again": "Der opstod en fejl under sletning af enhederne. Prøv igen.", - "an-error-occurred-entity-try-again": "Der opstod en fejl under sletning af enheden. Prøv igen.", - "an-error-occurred-please-try-again": "Der opstod en fejl. Prøv igen.", - "app": "App", - "app-version": "Appversion", - "area-name-is-required": "Områdenavn er påkrævet.", - "assign-existing-user-to-facility": "Tildel eksisterende bruger til placering", - "assign-user-to-facility": "Tildel bruger til '{{facilityName}}'", - "available-devices": "Tilgængelige enheder", - "battery": "Batteri", - "calendar": "Kalender", - "cant-remove-yourself": "Du kan ikke fjerne dig selv fra placeringen.", - "change-temperature": "Ændr temperatur", - "change-zone-map": "Skift zonekort", - "child-lock": "Børnesikring", - "child-lock-disabled": "Deaktiveret", - "child-lock-enabled": "Aktiveret", - "circuit": "Kreds", - "comfort": "Komfort", - "comfort-dhw-setpoint": "Sætpunkt for Komfort varmt brugsvand", - "comfort-dhw-setpoint-is-required": "Sætpunkt for Komfort varmt brugsvand er påkrævet.", - "comfort-room": "Komfort rum", - "comfort-room-setpoint": "Sætpunkt for Komfort rum", - "comfort-room-setpoint-is-required": "Sætpunkt for Komfort rum er påkrævet.", - "confirm-btn": "Bekræft", - "confirm-delete-device": "Er du sikker på, du ønsker at slette enhed '{{deviceName}}'?", - "confirm-delete-devices": "Er du sikker på, du ønsker at slette {{numberOfDevices}} enheder?", - "confirm-delete-home": "Er du sikker på, du ønsker at slette hjem '{{homeName}}'; deres zoner og enheder?", - "confirm-delete-zone": "Er du sikker på, du ønsker at slette zonen '{{zoneName}}'?", - "confirm-remove-user": "Er du sikker på, du ønsker at fjerne {{ thisUser }} fra lokation {{ locationName }}", - "constant-comfort-temperature": "Konstant komforttemperatur", - "constant-setback-temperature": "Konstant sænkningstemperatur", - "coordinates": "Koordinater", - "create-ecl-home-without-ally-account": "Opret ECL hjem uden Ally™ konto", - "create-vacation-event": "Opret feriebegivenhed", - "current-humidity": "Luftfugtighed", - "current-temp": "Aktuel temp.", - "current-temperature": "Aktuel temperatur", - "default-flow-temperature-at-minus-30": "Standard fremløbstemperatur ved -30 °C", - "default-flow-temperature-at-minus-15": "Standard fremløbstemperatur ved -15 °C", - "default-flow-temperature-at-minus-5": "Standard fremløbstemperatur ved -5 °C", - "default-flow-temperature-at-zero": "Standard fremløbstemperatur ved 0 °C", - "default-flow-temperature-at-plus-5": "Standard fremløbstemperatur ved 5 °C", - "default-flow-temperature-at-plus-15": "Standard fremløbstemperatur ved 15 °C", - "delete-account": "Slet konto", - "delete-all-devices": "Slet alle enheder", - "delete-device-from-home": "Slet enhed fra hjem", - "delete-devices": "Slet enheder", - "delete-facility-account": "Slet konto", - "delete-home": "Slet hjem", - "delete-user": "Slet bruger", - "delete-zone": "Slet zone", - "device-id": "Enheds-id", - "device-is-now-removed-from-this-zone": "Enheden er nu fjernet fra denne zone.", - "device-manual_mode-updated": "Manuel tilstand er indstillet for denne enhed.", - "device-name": "Enhedsnavn", - "device-status-updated": "Enhedsstatus opdateret!", - "device-type": "Enhedstype", - "devices-found-in-home": "Enheder fundet i {{ homeName }}", - "devices-imported": "Enheder importeret!", - "devices-in-zone": "Enheder i zone", - "devices-placed-into-zone": "{{numberOfDevices}} enhed(er) placeret i zone.", - "dhw-circuit": "Varmt brugsvandskreds", - "domestic-hot-water-circuit-menu": "Varmt brugsvand (DHW), menu for kreds", - "duplicate-devices": "Duplikerede enheder", - "duplicate-devices-list": "Du har allerede importeret enheder: {{listOfDevices}}", - "duplicate-devices-were-not-imported": "Duplikerede enheder: {{listOfDevices}} blev ikke importeret.", - "ecl-access-code": "ECL-adgangskode", - "ecl-device-added": "Din ECL-enhed {{device}} er blevet tilføjet.", - "ecl-diagram-title": "Nyt ECL-diagram", - "ecl-diagram-title-a-230-1": "Nyt ECL-diagram A230.1", - "ecl-diagram-title-a-247-1": "Nyt ECL-diagram A247.1", - "ecl-diagram-title-a-260-1": "Nyt ECL-diagram A260.1", - "ecl-diagram-title-a-266-1": "Nyt ECL-diagram A266.1", - "ecl-diagram-title-a-347-1": "Nyt ECL-diagram A347.1", - "ecl-diagram-title-a-376-1": "Nyt ECL-diagram A376.1", - "ecl-diagram-title-a-390-1": "Nyt ECL-diagram A390.1", - "ecl-heating-circuit-has-been-successfully-set": "ECL-varmekredsen er blevet indstillet.", - "ecl-menu": "ECL-menu", - "ecl-mode": "Tilstand", - "ecl-mvp-menu": "ECL MVP-menu", - "ecl-status": "Status", - "edit-facilities-access": "Rediger adgang til faciliteter", - "edit-facility-permission": "Rediger placeringstilladelse", - "edit-user": "Rediger bruger", - "edit-zone": "Rediger zone", - "eg-read-only": "f.eks. skrivebeskyttet", - "energy": "Energi", - "error-occurred": "Der opstod en fejl", - "etrv-update-temperature": "Opdater temperatur", - "facilities": "Faciliteter", - "facilities-for-entity-as-read-only-user": "Faciliteter for '{{entityName}}' som skrivebeskyttet bruger", - "facility-area-name": "placeringsområdenavn", - "facility-managers": "Facility Managers", - "facility-name-is-required": "placeringsnavn er påkrævet.", - "floor-map": "Plantegning", - "floors-in-the-room": "Gulve i rummet", - "flow-heating-curve-parameters-history": "Historik over parametre for flowvarmekurve", - "flow-temperature": "Fremløbstemperatur", - "flow-temperature-at-minus-15": "Fremløbstemperatur ved -150 °C", - "flow-temperature-at-minus-30": "Fremløbstemperatur ved -30 °C", - "flow-temperature-at-minus-5": "Fremløbstemperatur ved -5 °C", - "flow-temperature-at-plus-15": "Fremløbstemperatur ved 15 °C", - "flow-temperature-at-plus-5": "Fremløbstemperatur ved 5 °C", - "flow-temperature-at-zero": "Fremløbstemperatur ved 0 °C", - "flow-temperature-curve": "Fremløbstemperaturkurve", - "flow-temperature-values": "Fremløbstemperaturværdier", - "flow-temperature-without-optimization": "Fremløbstemperatur uden optimering", - "gateway-subdevices": "Gateway-underenheder", - "get-subdevices": "Hent underenheder", - "global-temp-set-msg": "Global temperatur er indstillet.", - "go-to-manual": "Gå til manuel tilstand", - "hardware": "Hardware", - "heating-circuit": "Varmekreds", - "heating-circuit-menu": "Menu for varmekreds", - "holiday-7-23h-comfort-temperature": "Ferie 7-23 t komforttemperatur", - "holiday-constant-comfort-temperature": "Ferie, konstant komforttemperatur", - "holiday-constant-setback-temperature": "Ferie, konstant sænkningstemperatur", - "holiday-frost-protection-standby": "Ferie, frostsikring/standby", - "home-alarms": "Hjemmealarmer", - "home-map": "Hjemmekort", - "home-map-with-zones": "Zoner", - "home-name": "Hjemmenavn", - "home-name-is-required": "Hjemmenavn er påkrævet", - "homes": "Hjem", - "homes-and-zones": "Hjem og zoner", - "homes-imported": "Hjem importeret!", - "homes-successfully-imported": "Hjem blev importeret!", - "import-ally-homes-from-the-button": "Importér Ally™ hjem fra knappen med ikonet 'hus'.", - "import-devices": "Importér enheder", - "import-devices-from-home": "Importer enheder fra hjem", - "import-homes": "Importer hjem", - "information": "Information", - "information-will-be-sent-via-email-to-this-user": "Information sendes via e-mail til denne bruger", - "invalid-temp": "Ugyldig temperaturværdi", - "link-to-ecl": "Link til ECL", - "linked-devices": "Tilknyttede enheder", - "list-of-subdevices": "Liste over underenheder", - "location-city": "Placering, by", - "location-country": "Placering, landområder", - "location-on-maps": "Placering på kort", - "location-street": "Placering, vej", - "location-street-number": "Placering, vejnummer", - "location-successfully-created": "Placeringen blev oprettet!", - "location-zip": "Placering, postnummer", - "locations": "Placeringer", - "lower-temperature": "Lavere temperatur", - "manage-home": "Administrer hjem", - "manual-operation": "Manuel drift", - "manual-temp": "Manuel temp.", - "manual-temperature": "Manuel temperatur", - "map": "Kort", - "max-flow-temperature": "Maks. Fremløbstemperatur", - "min-flow-temperature": "Min. Fremløbstemperatur", - "mode": "Tilstand", - "mode-at-home": "Hjemme", - "mode-leaving-home": "Ikke til stede", - "mode-manual": "Manuel", - "mode-pause": "Pause", - "mode-vacation": "Ferie", - "new-entities-table": "Tabel over nye enheder", - "new-led-indicator": "Ny LED-indikator", - "new-scheduler-events": "Nye planlægningsbegivenheder", - "no-devices-found": "Ingen enheder fundet.", - "no-ecl-devices-found": "Ingen ECL-enheder fundet", - "no-vacations": "Ingen planlagte begivenheder", - "number-of-devices-imported": "{{numberOfDevices}} enhed(er) importeret.", - "offline-status": "Offline", - "online": "Online", - "online-status": "Online", - "outdoor-temperature": "Udendørstemperatur", - "override-temperature": "Overstyringstemperatur", - "overview": "Oversigt", - "owner-name": "Ejerens navn", - "parent-facility-name": "Overordnet placeringsnavn", - "pi-heating-demand": "Pi Heating Demand", - "pi-heating-demand-history": "Pi Heating Demand History", - "pi-heating-demand-values-history": "Pi Heating Demand Values History", - "place-devices-in-this-zone": "Placer enheder i denne zone", - "place-into-zone": "Placer i zone", - "please-choose-user-role": "Vælg brugerrolle", - "pre-comfort": "Forkomfort", - "pre-setback": "Forsænkning", - "production-week": "Produktionsuge", - "read-only-users": "Skrivebeskyttede brugere", - "real-etrv-current-temp": "Termostattemperatur", - "refresh-device-status": "Opdater enhedsstatus", - "remove-from-zone": "Fjern fra zone", - "remove-user-from-loc": "Fjern bruger fra denne placering", - "remove-user-title": "Fjern bruger", - "resend-code": "Send kode igen", - "resend-code-msg": "Din kode er sendt! Tjek din e-mail.", - "return-temp": "Returløbstemperatur", - "room-map": "Kort over rum", - "save-comfort-dhw-setpoint": "Gem sætpunkt for 'Komfort varmt brugsvand'", - "save-saving-dhw-setpoint": "Gem sætpunktet 'Gemmer varmt brugsvand'", - "saving-dhw-setpoint": "Gemmer sætpunktet varmt brugsvand", - "saving-room": "Gemmer rum", - "saving-room-setpoint": "Gemmer sætpunkt for rum", - "saving-room-setpoint-is-required": "Gemmer sætpunkt for rum er påkrævet.", - "scheduled-operation": "Planlagt drift", - "scheduler-events": "Planlægningsbegivenheder", - "see-advanced-settings-for-details": "Se avancerede indstillinger for at få yderligere oplysninger", - "see-areas": "se områder", - "see-devices-in-home": "Se enheder i hjemmet", - "see-information": "Se oplysninger", - "selected-devices-are-successfully-linked-with-ecl-device": "Selected device(s) are successfully linked with ECL device.", - "serial-number": "Serienummer", - "set-ecl-heating-circuit": "Set ECL heating circuit", - "select-ecl-device": "Vælg ECL-enhed...", - "device-are-linked-to-ecl": "Den eller de valgte enheder er med succes forbundet med ECL-enhed.", - "unlink-all-devices": "Fjern linket til alle enheder fra ECL", - "unlink-device": "Fjern forbindelsen mellem enhed fra ECL", - "see-pi-history": "Se PI-historik", - "device-unlinked-msg": "Enheden er fjernet fra ECL", - "set-global-temp": "Indstil den globale temperatur", - "are-you-sure-unlink-all": "Er du sikker på at fjerne linket til ALLE enheder fra ECL?", - "are-you-sure-unlink-device": "Er du sikker på at fjerne linket mellem enhed {{device}} og ECL-controlleren?", - "set-temp": "Indstil temp.", - "set-temperature": "Ønsket temperatur", - "set-vacation": "Indstil ferie", - "setback": "Sænkning", - "setback-dhw-setpoint-is-required": "Gemmer sætpunktet varmt brugsvand er påkrævet.", - "software": "Software", - "software-build-no": "Softwarebuildnr.", - "something-went-wrong": "Noget gik galt!", - "subdevices-information-saved": "Gateway-underenheder gemt.", - "successfully-inserted-devices": "{{numberOfDevices}} enhed(er) blev indsat.", - "temp-required": "Temperaturværdi er påkrævet", - "temperature": "Temperatur", - "this-user": "denne bruger", - "title-homes": "Hjem", - "title-location": "Placering", - "update-area-map": "Opdater områdekort", - "update-facilities": "Opdater faciliteter", - "update-home-map": "Opdater hjemmekort", - "update-zone-map": "Opdater zonekort", - "upper-temperature": "Øvre temperatur", - "user-activation-link-has-been-copied-to-clipboard": "Brugeraktiveringslinket er blevet kopieret til udklipsholderen", - "user-removed-fm": "Bruger fjernet som Facility Manager fra denne placering.", - "user-removed-ro": "Bruger fjernet som skrivebeskyttet bruger fra denne placering.", - "user-was-successfully-assigned-to-the-facility": "Brugeren blev tildelt {{facilityName}}", - "user-with-email-already-exists": "Bruger med e-mail '{{email}}' findes allerede!", - "user-with-this-email-is-already-assigned-to-the-facility": "Bruger med denne e-mail er allerede tildelt '{{facilityName}}'.", - "user-with-this-email-was-not-found": "Bruger med denne e-mail blev ikke fundet.", - "vacation-plan": "Ferieplan", - "view-details": "Vis detaljer", - "view-device-information": "Vis enhedsoplysninger", - "view-home": "Vis hjem", - "view-information": "Vis oplysninger", - "view-users": "Brugere", - "view-zone": "Vis zone", - "warning-uppercase": "ADVARSEL", - "were-not-imported": "blev ikke importeret", - "window-state": "Vinduestilstand", - "work-state": "Arbejdstilstand", - "you-dont-have-attribute-free": "Du har ikke en attribut, der er ledig til placeringsnavn!", - "you-have-valid-ally-account": "Du har en gyldig Ally™ konto.", - "your-ally-email-account": "Din Ally™ e-mailkonto", - "your-facilities": "Dine faciliteter", - "your-home-was-successfully-deleted": "Dit hjem blev slettet.", - "zone": "zone", - "zone-have-no-devices-msg": "Der er ingen enheder placeret i zonen. Placer venligst nogle enheder.", - "zone-management": "Zonestyring", - "zone-name": "Navn", - "zone-name-is-required": "Zonenavn er påkrævet.", - "zone-temp-not-enabled": " ikke aktiveret.", - "zone-temp-sett": "Indstilling af temperaturen", - "zone-temp-sett-tooltip": "Ændr temperaturen", - "zone-temperature": "Zonetemperatur", - "zone-view": "Zonevisning" + "rulechain" : { + "rulechain" : "Regelkæde", + "rulechain-events" : "Regelkædebegivenheder", + "rulechains" : "Regelkæder", + "root" : "Rod", + "delete" : "Slet regelkæde", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "name-max-length" : "Navnet skal være mindre end 256 tegn", + "description" : "Beskrivelse", + "add" : "Tilføj regelkæde", + "set-root" : "Gør regelkæde til rod", + "set-root-rulechain-title" : "Er du sikker på, at du vil gøre regelkæden '{{ruleChainName}}' til rod?", + "set-root-rulechain-text" : "Efter bekræftelsen bliver regelkæden rod og håndterer alle indkommende transportbeskeder.", + "delete-rulechain-title" : "Er du sikker på, at du vil slette regelkæden '{{ruleChainName}}'?", + "delete-rulechain-text" : "Vær forsigtig, efter bekræftelsen bliver regelkæden og alle relaterede data uoprettelige.", + "delete-rulechains-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 regelkæde} other {# regelkæder} }?", + "delete-rulechains-action-title" : "Slet { count, plural, =1 {1 regelkæde} other {# regelkæder} }", + "delete-rulechains-text" : "Vær forsigtig, efter bekræftelsen bliver alle valgte regelkæder og alle relaterede data uoprettelige.", + "add-rulechain-text" : "Tilføj ny regelkæde", + "no-rulechains-text" : "Ingen regelkæder fundet", + "rulechain-details" : "Regelkæde detaljer", + "details" : "Detaljer", + "events" : "Begivenheder", + "system" : "System", + "import" : "Importer regelkæde", + "export" : "Eksporter regelkæde", + "export-failed-error" : "Kan ikke eksportere regelkæde: {{error}}", + "create-new-rulechain" : "Opret ny regelkæde", + "rulechain-file" : "Regelkædefil", + "invalid-rulechain-file-error" : "Kan ikke importere regelkæde: Ugyldig datastruktur.", + "copyId" : "Kopiér regelkæde-ID", + "idCopiedMessage" : "Regelkæde-ID er kopieret til udklipsholderen", + "select-rulechain" : "Vælg regelkæde", + "no-rulechains-matching" : "Ingen regelkæder, der matcher '{{entity}}', blev fundet.", + "rulechain-required" : "Regelkæde er påkrævet", + "management" : "Regelstyring", + "debug-mode" : "Fejlsøgningstilstand", + "search" : "Søg regelkæder", + "selected-rulechains" : "{ count, plural, =1 {1 regelkæde} other {# regelkæder} } valgt", + "open-rulechain" : "Åbn regelkæde", + "edge-template-root" : "Skabelonrod", + "assign-to-edge" : "Tildel til edge", + "edge-rulechain" : "Edge regelkæde", + "unassign-rulechain-from-edge-text" : "Efter bekræftelsen vil regelkæden blive fjernet og vil ikke være tilgængelig for edge.", + "unassign-rulechains-from-edge-title" : "Er du sikker på, at du vil fjerne tildelingen af { count, plural, =1 {1 regelkæde} other {# regelkæder} }?", + "unassign-rulechains-from-edge-text" : "Efter bekræftelsen vil alle valgte regelkæder blive fjernet og vil ikke være tilgængelige for edge.", + "assign-rulechain-to-edge-title" : "Tildel regelkæde(r) til edge", + "assign-rulechain-to-edge-text" : "Vælg regelkæder, der skal tildeles til edge", + "set-edge-template-root-rulechain" : "Gør regelkæde til edge-skabelonrod", + "set-edge-template-root-rulechain-title" : "Er du sikker på, at du vil gøre regelkæden '{{ruleChainName}}' til edge-skabelonrod?", + "set-edge-template-root-rulechain-text" : "Efter bekræftelsen vil regelkæden blive skabelonrod og være root regelkæde for nyoprettede edges.", + "invalid-rulechain-type-error" : "Kan ikke importere regelkæde: Ugyldig type. Forventet type er {{expectedRuleChainType}}.", + "set-auto-assign-to-edge" : "Tildel regelkæde automatisk til edge ved oprettelse", + "set-auto-assign-to-edge-title" : "Er du sikker på, at du vil tildele regelkæden '{{ruleChainName}}' automatisk til edge ved oprettelse?", + "set-auto-assign-to-edge-text" : "Efter bekræftelsen vil regelkæden automatisk blive tildelt til edge ved oprettelse.", + "unset-auto-assign-to-edge" : "Tildel ikke regelkæde til edge ved oprettelse", + "unset-auto-assign-to-edge-title" : "Er du sikker på, at du ikke vil tildele regelkæden '{{ruleChainName}}' automatisk til edge ved oprettelse?", + "unset-auto-assign-to-edge-text" : "Efter bekræftelsen vil regelkæden ikke længere blive tildelt automatisk til edge ved oprettelse.", + "unassign-rulechain-title" : "Er du sikker på, at du vil fjerne tildelingen af regelkæden '{{ruleChainName}}'?", + "unassign-rulechains" : "Fjern tildeling af regelkæder" + }, + "rulenode" : { + "rule-node-events" : "Regel node begivenheder", + "details" : "Detaljer", + "events" : "Begivenheder", + "search" : "Søg noder", + "open-node-library" : "Åbn nodebibliotek", + "close-node-library" : "Luk nodebibliotek", + "add" : "Tilføj regel node", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "name-max-length" : "Navnet skal være mindre end 256 tegn", + "type" : "Type", + "rule-node-description" : "Beskrivelse af regel node", + "delete" : "Slet regel node", + "select-all-objects" : "Vælg alle noder og forbindelser", + "deselect-all-objects" : "Fravælg alle noder og forbindelser", + "delete-selected-objects" : "Slet valgte noder og forbindelser", + "delete-selected" : "Slet valgte", + "create-nested-rulechain" : "Opret indlejret regelkæde", + "select-all" : "Vælg alle", + "copy-selected" : "Kopiér valgte", + "deselect-all" : "Fravælg alle", + "rulenode-details" : "Regel node detaljer", + "debug-mode" : "Fejlsøgningstilstand", + "singleton" : "Singleton", + "configuration" : "Konfiguration", + "link" : "Forbindelse", + "link-details" : "Detaljer for regel node forbindelse", + "add-link" : "Tilføj forbindelse", + "link-label" : "Forbindelsesetiket", + "link-label-required" : "Forbindelsesetiket er påkrævet.", + "custom-link-label" : "Brugerdefineret etiket", + "custom-link-label-required" : "Brugerdefineret etiket er påkrævet.", + "link-labels" : "Forbindelsesetiketter", + "link-labels-required" : "Forbindelsesetiketter er påkrævede.", + "no-link-labels-found" : "Ingen forbindelsesetiketter fundet", + "no-link-label-matching" : "'{{label}}' blev ikke fundet.", + "create-new-link-label" : "Opret en ny!", + "type-filter" : "Filter", + "type-filter-details" : "Filtrer indkommende beskeder med konfigurerede betingelser", + "type-enrichment" : "Berigelse", + "type-enrichment-details" : "Tilføj yderligere information til metadata", + "type-transformation" : "Transformation", + "type-transformation-details" : "Ændr beskedindhold og metadata", + "type-action" : "Handling", + "type-action-details" : "Udfør specifik handling", + "type-external" : "Ekstern", + "type-external-details" : "Interagerer med eksternt system", + "type-rule-chain" : "Regelkæde", + "type-rule-chain-details" : "Videresender beskeder til specificeret regelkæde", + "type-flow" : "Flow", + "type-flow-details" : "Organiserer beskedflow", + "type-input" : "Input", + "type-input-details" : "Logisk input til regelkæde, videresender beskeder til næste node", + "type-unknown" : "Ukendt", + "type-unknown-details" : "Uafklaret regel node", + "directive-is-not-loaded" : "Konfigurationsdirektiv '{{directiveName}}' er ikke tilgængeligt.", + "ui-resources-load-error" : "Kunne ikke indlæse brugerflade konfigurationsressourcer.", + "invalid-target-rulechain" : "Kunne ikke bestemme målregel kæde!", + "test-script-function" : "Test script funktion", + "script-lang-java-script" : "JavaScript", + "script-lang-tbel" : "TBEL", + "message" : "Besked", + "message-type" : "Beskedtype", + "select-message-type" : "Vælg beskedtype", + "message-type-required" : "Beskedtype er påkrævet", + "metadata" : "Metadata", + "metadata-required" : "Metadata poster må ikke være tomme.", + "output" : "Output", + "test" : "Test", + "help" : "Hjælp", + "reset-debug-settings" : "Nulstil fejlsøgningsindstillinger i alle noder", + "test-with-this-message" : "{{test}} med denne besked", + "queue-hint" : "Vælg en kø til videresendelse. Standardkøen 'Main' anvendes som udgangspunkt.", + "queue-singleton-hint" : "Vælg en kø til videresendelse i miljøer med flere instanser. Standardkøen 'Main' anvendes som udgangspunkt." + }, + "rule-node-config" : { + "id" : "Id", + "additional-info" : "Yderligere info", + "advanced-settings" : "Avancerede indstillinger", + "create-entity-if-not-exists" : "Opret ny enhed hvis den ikke findes", + "create-entity-if-not-exists-hint" : "Hvis aktiveret, oprettes en ny enhed med de angivne parametre, medmindre den allerede eksisterer. Eksisterende enheder bruges som de er til relation.", + "select-device-connectivity-event" : "Vælg enhedens forbindelseshændelse", + "entity-name-pattern" : "Navnmønster", + "device-name-pattern" : "Enhedsnavn", + "asset-name-pattern" : "Aktivnavn", + "entity-view-name-pattern" : "Enhedsvisningsnavn", + "customer-title-pattern" : "Kundens titel", + "dashboard-name-pattern" : "Dashboard titel", + "user-name-pattern" : "Brugerens e-mail", + "edge-name-pattern" : "Edge-navn", + "entity-name-pattern-required" : "Navnmønster er påkrævet", + "entity-name-pattern-hint" : "Navnmønsterfeltet understøtter templatisering. Brug $[messageKey] for at hente værdi fra besked og ${metadataKey} for at hente værdi fra metadata.", + "copy-message-type" : "Kopiér beskedtype", + "entity-type-pattern" : "Typemønster", + "entity-type-pattern-required" : "Typemønster er påkrævet", + "message-type-value" : "Beskedtypeværdi", + "message-type-value-required" : "Beskedtypeværdi er påkrævet", + "message-type-value-max-length" : "Beskedtypeværdi må være under 256 tegn", + "output-message-type" : "Output beskedtype", + "entity-cache-expiration" : "Cache udløbstid for enheder (sek)", + "entity-cache-expiration-hint" : "Angiver maksimumsinterval for at gemme fundne enhedsoptegnelser. 0 betyder, at de aldrig udløber.", + "entity-cache-expiration-required" : "Cache udløbstid for enheder er påkrævet.", + "entity-cache-expiration-range" : "Udløbstiden skal være ≥ 0.", + "customer-name-pattern" : "Kundens titel", + "customer-name-pattern-required" : "Kundens titel er påkrævet", + "customer-name-pattern-hint" : "Brug $[messageKey] eller ${metadataKey} for at hente værdier.", + "create-customer-if-not-exists" : "Opret ny kunde hvis den ikke findes", + "unassign-from-customer" : "Fjern tildeling fra kunde hvis afsender er dashboard", + "unassign-from-customer-tooltip" : "Kun dashboards kan tildeles flere kunder. Angiv kundens titel for at fjerne tildelingen.", + "customer-cache-expiration" : "Cache udløbstid for kunder (sek)", + "customer-cache-expiration-hint" : "Angiver maksimumsinterval for gemte kundedata. 0 = aldrig udløber.", + "customer-cache-expiration-required" : "Cache udløbstid for kunder er påkrævet.", + "customer-cache-expiration-range" : "Udløbstiden skal være ≥ 0.", + "interval-start" : "Interval start", + "interval-end" : "Interval slut", + "time-unit" : "Tidsenhed", + "fetch-mode" : "Hentetilstand", + "order-by-timestamp" : "Sorter efter tidsstempel", + "limit" : "Grænse", + "limit-hint" : "Min: 2, maks: 1000. Vælg 'First' eller 'Last' for én post.", + "limit-required" : "Grænse er påkrævet.", + "limit-range" : "Grænsen skal være mellem 2 og 1000.", + "time-unit-milliseconds" : "Millisekunder", + "time-unit-seconds" : "Sekunder", + "time-unit-minutes" : "Minutter", + "time-unit-hours" : "Timer", + "time-unit-days" : "Dage", + "time-value-range" : "Værdien skal være mellem 1 og 2147483647.", + "start-interval-value-required" : "Interval start er påkrævet.", + "end-interval-value-required" : "Interval slut er påkrævet.", + "filter" : "Filter", + "switch" : "Skift", + "math-templatization-tooltip" : "Understøtter templatisering via $[messageKey] og ${metadataKey}.", + "add-message-type" : "Tilføj beskedtype", + "select-message-types-required" : "Mindst én beskedtype skal vælges.", + "select-message-types" : "Vælg beskedtyper", + "no-message-types-found" : "Ingen beskedtyper fundet", + "no-message-type-matching" : "'{{messageType}}' ikke fundet.", + "create-new-message-type" : "Opret en ny.", + "message-types-required" : "Beskedtyper er påkrævede.", + "client-attributes" : "Klientattributter", + "shared-attributes" : "Delte attributter", + "server-attributes" : "Serverattributter", + "attributes-keys" : "Attributnøgler", + "attributes-keys-required" : "Attributnøgler er påkrævede", + "attributes-scope" : "Attributområde", + "attributes-scope-value" : "Attributområdeværdi", + "attributes-scope-value-copy" : "Kopiér attributområdeværdi", + "attributes-scope-hint" : "Brug metadata 'scope' for at sætte attributområde dynamisk.", + "notify-device" : "Tving besked til enheden", + "send-attributes-updated-notification" : "Send opdateringsbesked for attributter", + "send-attributes-updated-notification-hint" : "Sender besked om opdaterede attributter til Rule Engine køen.", + "send-attributes-deleted-notification" : "Send sletningsbesked for attributter", + "send-attributes-deleted-notification-hint" : "Sender besked om slettede attributter til Rule Engine køen.", + "update-attributes-only-on-value-change" : "Opdatér kun attributter ved værdiændring", + "update-attributes-only-on-value-change-hint" : "Opdaterer altid, hvilket øger API-brug og reducerer ydeevne.", + "update-attributes-only-on-value-change-hint-enabled" : "Opdaterer kun ved ændret værdi. Ellers ingen tidsstempel- eller notifikationsopdatering.", + "fetch-credentials-to-metadata" : "Hent legitimationsoplysninger til metadata", + "notify-device-on-update-hint" : "Styrer tvungen notifikation eller baseret på metadata-parametre.", + "notify-device-on-delete-hint" : "Styrer tvungen notifikation eller baseret på metadata-parametre.", + "latest-timeseries" : "Seneste tidsseriedatanøgler", + "timeseries-keys" : "Tidsserienøgler", + "timeseries-keys-required" : "Mindst én tidsserienøgle skal vælges.", + "add-timeseries-key" : "Tilføj tidsserienøgle", + "add-message-field" : "Tilføj beskedfelt", + "relation-search-parameters" : "Relationssøgningsparametre", + "relation-parameters" : "Relationsparametre", + "add-metadata-field" : "Tilføj metadatafelt", + "data-keys" : "Beskedfeltnavne", + "copy-from" : "Kopiér fra", + "data-to-metadata" : "Data til metadata", + "metadata-to-data" : "Metadata til data", + "use-regular-expression-hint" : "Brug regulært udtryk for at kopiere nøgler efter mønster.\n\nTips:\nTryk 'Enter' for at bekræfte feltinput.\nTryk 'Backspace' for at slette felt. Flere felter understøttes.", + "interval" : "Interval", + "interval-required" : "Interval er påkrævet", + "interval-hint" : "Dedupliceringsinterval i sekunder.", + "interval-min-error" : "Mindste tilladte værdi er 1", + "max-pending-msgs" : "Maks. ventende beskeder", + "max-pending-msgs-hint" : "Maksimalt antal beskeder gemt i hukommelsen for hver unik deduplikerings-id.", + "max-pending-msgs-required" : "Maks. ventende beskeder er påkrævet", + "max-pending-msgs-max-error" : "Maks. værdi er 1000", + "max-pending-msgs-min-error" : "Mindste værdi er 1", + "max-retries" : "Maks. forsøg", + "max-retries-required" : "Maks. forsøg er påkrævet", + "max-retries-hint" : "Maksimalt antal forsøg på at skubbe deduplikerede beskeder til køen. Der bruges 10 sekunders forsinkelse mellem forsøgene", + "max-retries-max-error" : "Maks. værdi er 100", + "max-retries-min-error" : "Mindste værdi er 0", + "strategy" : "Strategi", + "strategy-required" : "Strategi er påkrævet", + "strategy-all-hint" : "Returnér alle beskeder i perioden som én JSON-array. Hvert element indeholder 'msg' og 'metadata'.", + "strategy-first-hint" : "Returnér første besked i perioden.", + "strategy-last-hint" : "Returnér sidste besked i perioden.", + "first" : "Første", + "last" : "Sidste", + "all" : "Alle", + "output-msg-type-hint" : "Beskedtype for deduplikeringsresultatet.", + "queue-name-hint" : "Navn på køen hvor resultatet publiceres.", + "keys" : "Nøgler", + "keys-required" : "Nøgler er påkrævede", + "rename-keys-in" : "Omdøb nøgler i", + "data" : "Data", + "message" : "Besked", + "metadata" : "Metadata", + "current-key-name" : "Aktuel nøgle", + "key-name-required" : "Nøglenavn er påkrævet", + "new-key-name" : "Nyt nøglenavn", + "new-key-name-required" : "Nyt nøglenavn er påkrævet", + "metadata-keys" : "Metadatafeltnavne", + "json-path-expression" : "JSONPath-udtryk", + "json-path-expression-required" : "JSONPath-udtryk er påkrævet", + "json-path-expression-hint" : "JSONPath specificerer sti til element(er) i en JSON-struktur. '$' er rodobjektet.", + "relations-query" : "Relationsforespørgsel", + "device-relations-query" : "Enhedsrelationsforespørgsel", + "max-relation-level" : "Maks. relationsniveau", + "max-relation-level-error" : "Værdien skal være > 0 eller tom.", + "max-relation-level-invalid" : "Værdien skal være et heltal.", + "relation-type" : "Relationstype", + "relation-type-pattern" : "Relationstypemønster", + "relation-type-pattern-required" : "Relationstypemønster er påkrævet", + "relation-types-list" : "Relationstyper til udbredelse", + "relation-types-list-hint" : "Hvis relationstyper ikke vælges, udbredes alarmer uden filtrering.", + "unlimited-level" : "Ubegrænset niveau", + "latest-telemetry" : "Seneste telemetri", + "add-telemetry-key" : "Tilføj telemetrinøgle", + "delete-from" : "Slet fra", + "use-regular-expression-delete-hint" : "Brug regulært udtryk til sletning efter mønster.\n\nTips:\nTryk 'Enter' for at bekræfte.\nTryk 'Backspace' for at slette.", + "fetch-into" : "Hent til", + "attr-mapping" : "Attributmapping:", + "source-attribute" : "Kildeattribut", + "source-attribute-required" : "Kildeattribut er påkrævet.", + "source-telemetry" : "Kildetelemetri", + "source-telemetry-required" : "Kildetelemetri er påkrævet.", + "target-key" : "Målnøgle", + "target-key-required" : "Målnøgle er påkrævet.", + "attr-mapping-required" : "Mindst én mapping skal angives.", + "fields-mapping" : "Feltermapping", + "fields-mapping-hint" : "Brug $entityId for at gemme afsenderens id.", + "relations-query-config-direction-suffix" : "afsender", + "profile-name" : "Profilnavn", + "fetch-circle-parameter-info-from-metadata-hint" : "Metadatafelt '{{perimeterKeyName}}' skal være i formatet: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint" : "Metadatafelt '{{perimeterKeyName}}' skal være i formatet: [[48.19736,24.65235],...]", + "short-templatization-tooltip" : "Brug $[messageKey] og ${metadataKey} til at hente værdier.", + "fields-mapping-required" : "Mindst én feltmapping er påkrævet.", + "at-least-one-field-required" : "Mindst ét inputfelt skal have en værdi.", + "originator-fields-sv-map-hint" : "Målnøgler understøtter templatisering via $[messageKey] og ${metadataKey}.", + "sv-map-hint" : "Kun målnøgler understøtter templatisering via $[messageKey] og ${metadataKey}.", + "source-field" : "Kildefelt", + "source-field-required" : "Kildefelt er påkrævet.", + "originator-source" : "Afsenderkilde", + "new-originator" : "Ny afsender", + "originator-customer" : "Kunde", + "originator-tenant" : "Lejer", + "originator-related" : "Relateret enhed", + "originator-alarm-originator" : "Alarm-afsender", + "originator-entity" : "Enhed efter navnemønster", + "clone-message" : "Klon besked", + "transform" : "Transformér", + "default-ttl" : "Standard TTL", + "default-ttl-required" : "Standard TTL er påkrævet.", + "default-ttl-hint" : "Hvis metadata ikke indeholder TTL, anvendes standard TTL. Hvis 0, bruges TTL fra lejerprofil.", + "default-ttl-zero-hint" : "TTL anvendes ikke hvis værdien er 0.", + "min-default-ttl-message" : "Kun værdi 0 er tilladt som minimum TTL.", + "generation-parameters" : "Genereringsparametre", + "message-count" : "Maks. antal genererede beskeder (0 - ubegrænset)", + "message-count-required" : "Begrænsning for genererede beskeder er påkrævet.", + "min-message-count-message" : "Kun værdi 0 er tilladt som minimum.", + "period-seconds" : "Periode i sekunder", + "period-seconds-required" : "Periode er påkrævet.", + "generation-frequency-seconds" : "Genereringsfrekvens i sekunder", + "generation-frequency-required" : "Genereringsfrekvens er påkrævet.", + "min-generation-frequency-message" : "Minimum tilladte værdi er 60 sekunder.", + "script-lang-tbel" : "TBEL", + "script-lang-js" : "JS", + "use-metadata-period-in-seconds-patterns" : "Brug periode-mønster i sekunder", + "use-metadata-period-in-seconds-patterns-hint" : "Bruger mønster fra metadata til at angive periode i sekunder.", + "period-in-seconds-pattern" : "Periode-mønster i sekunder", + "period-in-seconds-pattern-required" : "Periode-mønster i sekunder er påkrævet", + "min-period-seconds-message" : "Minimum tilladte periode er 60 sekunder.", + "originator" : "Afsender", + "message-body" : "Beskedindhold", + "message-metadata" : "Beskedmetadata", + "generate" : "Generér", + "current-rule-node" : "Aktuel regelnode", + "current-tenant" : "Aktuel lejer", + "generator-function" : "Generatorfunktion", + "test-generator-function" : "Test generatorfunktion", + "generator" : "Generator", + "test-filter-function" : "Test filterfunktion", + "test-switch-function" : "Test skiftefunktion", + "test-transformer-function" : "Test transformerfunktion", + "transformer" : "Transformer", + "alarm-create-condition" : "Alarmoprettelsesbetingelse", + "test-condition-function" : "Test betingelsesfunktion", + "alarm-clear-condition" : "Alarmryddebetingelse", + "alarm-details-builder" : "Alarmdetalje-generator", + "test-details-function" : "Test detaljefunktion", + "alarm-type" : "Alarmtype", + "select-entity-types" : "Vælg enhedstyper", + "alarm-type-required" : "Alarmtype er påkrævet.", + "alarm-severity" : "Alarm alvorlighed", + "alarm-severity-required" : "Alvorlighed er påkrævet", + "alarm-severity-pattern" : "Alvorlighedsmønster", + "alarm-status-filter" : "Alarmstatusfilter", + "alarm-status-list-empty" : "Listen over alarmstatusser er tom", + "no-alarm-status-matching" : "Ingen matchende alarmstatus fundet.", + "propagate" : "Udbred alarm til relaterede enheder", + "propagate-to-owner" : "Udbred alarm til ejer (Kunde eller Lejer)", + "propagate-to-tenant" : "Udbred alarm til lejer", + "condition" : "Betingelse", + "details" : "Detaljer", + "to-string" : "Til streng", + "test-to-string-function" : "Test til-streng funktion", + "from-template" : "Fra", + "from-template-required" : "'Fra' er påkrævet", + "message-to-metadata" : "Besked til metadata", + "metadata-to-message" : "Metadata til besked", + "from-message" : "Fra besked", + "from-metadata" : "Fra metadata", + "to-template" : "Til", + "to-template-required" : "'Til' er påkrævet", + "mail-address-list-template-hint" : "Kommasepareret adresseliste, brug ${metadataKey} for værdi fra metadata, $[messageKey] for værdi fra meddelelsens indhold", + "cc-template" : "Cc", + "bcc-template" : "Bcc", + "subject-template" : "Emne", + "subject-template-required" : "Emne er påkrævet", + "body-template" : "Indhold", + "body-template-required" : "Indhold er påkrævet", + "dynamic-mail-body-type" : "Dynamisk indholdstype", + "mail-body-type" : "Indholdstype", + "body-type-template" : "Indholdstypemønster", + "reply-routing-configuration" : "Svarrute-konfiguration", + "rpc-reply-routing-configuration-hint" : "Konfiguration for at identificere tjeneste, session og anmodning i metadata.", + "reply-routing-configuration-hint" : "Konfiguration for at identificere tjeneste og anmodning i metadata.", + "request-id-metadata-attribute" : "Anmodnings-id", + "service-id-metadata-attribute" : "Tjeneste-id", + "session-id-metadata-attribute" : "Session-id", + "timeout-sec" : "Timeout i sekunder", + "timeout-required" : "Timeout er påkrævet", + "min-timeout-message" : "Kun timeout-værdien 0 er tilladt som minimum.", + "endpoint-url-pattern" : "URL-mønster for slutpunkt", + "endpoint-url-pattern-required" : "URL-mønster for slutpunkt er påkrævet", + "request-method" : "Forespørgselsmetode", + "use-simple-client-http-factory" : "Brug simpel HTTP-klientfabrik", + "ignore-request-body" : "Uden forespørgselsindhold", + "parse-to-plain-text" : "Fortolk som almindelig tekst", + "parse-to-plain-text-hint" : "Hvis valgt, transformeres JSON-strengen i beskedens indhold til almindelig tekst.", + "read-timeout" : "Læs timeout i millisekunder", + "read-timeout-hint" : "Værdien 0 betyder uendelig timeout", + "max-parallel-requests-count" : "Maksimalt antal parallelle forespørgsler", + "max-parallel-requests-count-hint" : "Værdien 0 betyder ingen begrænsning", + "max-response-size" : "Maks. svarstørrelse (i KB)", + "max-response-size-hint" : "Maksimal hukommelse tildelt til buffering ved afkodning/enkodning af HTTP-meddelelser", + "headers" : "Headers", + "headers-hint" : "Brug ${metadataKey} for metadata eller $[messageKey] for beskeddata i header/værdi felter", + "header" : "Header", + "header-required" : "Header er påkrævet", + "value" : "Værdi", + "value-required" : "Værdi er påkrævet", + "topic-pattern" : "Emnemønster", + "key-pattern" : "Nøglemønster", + "key-pattern-hint" : "Valgfri. Angiv partition eller nøgle til brug ved sending.", + "topic-pattern-required" : "Emnemønster er påkrævet", + "topic" : "Emne", + "topic-required" : "Emne er påkrævet", + "bootstrap-servers" : "Bootstrap-servere", + "bootstrap-servers-required" : "Bootstrap-servere er påkrævede", + "other-properties" : "Andre egenskaber", + "key" : "Nøgle", + "key-required" : "Nøgle er påkrævet", + "retries" : "Antal genforsøg ved fejl", + "min-retries-message" : "Kun 0 genforsøg tilladt som minimum", + "batch-size-bytes" : "Batchstørrelse i bytes", + "min-batch-size-bytes-message" : "Minimum batchstørrelse er 0", + "linger-ms" : "Buffer-tid lokalt (ms)", + "min-linger-ms-message" : "Minimum 0 ms tilladt", + "buffer-memory-bytes" : "Maks. bufferstørrelse (bytes)", + "min-buffer-memory-message" : "Minimum bufferstørrelse er 0", + "memory-buffer-size-range" : "Bufferstørrelse skal være mellem 0 og {{max}} KB", + "acks" : "Antal kvitteringer", + "topic-arn-pattern" : "ARN-emnemønster", + "topic-arn-pattern-required" : "ARN-emnemønster er påkrævet", + "aws-access-key-id" : "AWS Access Key ID", + "aws-access-key-id-required" : "AWS Access Key ID er påkrævet", + "aws-secret-access-key" : "AWS Secret Access Key", + "aws-secret-access-key-required" : "AWS Secret Access Key er påkrævet", + "aws-region" : "AWS-region", + "aws-region-required" : "AWS-region er påkrævet", + "exchange-name-pattern" : "Exchange-navnemønster", + "routing-key-pattern" : "Routing-nøglemønster", + "message-properties" : "Beskedegenskaber", + "host" : "Host", + "host-required" : "Host er påkrævet", + "port" : "Port", + "port-required" : "Port er påkrævet", + "port-range" : "Port skal være mellem 1 og 65535", + "virtual-host" : "Virtuel host", + "username" : "Brugernavn", + "password" : "Adgangskode", + "automatic-recovery" : "Automatisk gendannelse", + "connection-timeout-ms" : "Forbindelsestimeout (ms)", + "min-connection-timeout-ms-message" : "Minimum værdi er 0 ms", + "handshake-timeout-ms" : "Handshake-timeout (ms)", + "min-handshake-timeout-ms-message" : "Minimum værdi er 0 ms", + "client-properties" : "Klientegenskaber", + "queue-url-pattern" : "Kø-URL mønster", + "queue-url-pattern-required" : "Kø-URL mønster er påkrævet", + "delay-seconds" : "Forsinkelse (sekunder)", + "min-delay-seconds-message" : "Minimum forsinkelse er 0 sekunder", + "max-delay-seconds-message" : "Maksimal forsinkelse er 900 sekunder", + "name" : "Navn", + "name-required" : "Navn er påkrævet", + "queue-type" : "Køtype", + "sqs-queue-standard" : "Standard", + "sqs-queue-fifo" : "FIFO", + "gcp-project-id" : "GCP-projekt-ID", + "gcp-project-id-required" : "GCP-projekt-ID er påkrævet", + "gcp-service-account-key" : "GCP servicekonto-nøglefil", + "gcp-service-account-key-required" : "GCP servicekonto-nøglefil er påkrævet", + "pubsub-topic-name" : "Emnenavn", + "pubsub-topic-name-required" : "Emnenavn er påkrævet", + "message-attributes" : "Beskedattributter", + "message-attributes-hint" : "Brug ${metadataKey} eller $[messageKey] i navn/værdi felter", + "connect-timeout" : "Forbindelsestimeout (sek)", + "connect-timeout-required" : "Forbindelsestimeout er påkrævet.", + "connect-timeout-range" : "Timeout skal være mellem 1 og 200.", + "client-id" : "Klient-ID", + "client-id-hint" : "Valgfri. Lad være tomt for automatisk generering. For at undgå konflikter i mikrotjenester, brug unikke ID’er.", + "append-client-id-suffix" : "Tilføj service-ID som suffix til klient-ID", + "client-id-suffix-hint" : "Anvendes kun hvis klient-ID er angivet. Hjælper med at undgå konflikter i mikrotjenestetilstand.", + "device-id" : "Enheds-ID", + "device-id-required" : "Enheds-ID er påkrævet.", + "clean-session" : "Ren session", + "enable-ssl" : "Aktiver SSL", + "credentials" : "Legitimationsoplysninger", + "credentials-type" : "Legitimationstype", + "credentials-type-required" : "Legitimationstype er påkrævet.", + "credentials-anonymous" : "Anonym", + "credentials-basic" : "Grundlæggende", + "credentials-pem" : "PEM", + "credentials-pem-hint" : "Mindst serverens CA-certifikat eller et par af klientcertifikat og privat nøgle er påkrævet", + "credentials-sas" : "Delt adgangsnøgle (SAS)", + "sas-key" : "SAS-nøgle", + "sas-key-required" : "SAS-nøgle er påkrævet.", + "hostname" : "Værtsnavn", + "hostname-required" : "Værtsnavn er påkrævet.", + "azure-ca-cert" : "CA-certifikatfil", + "username-required" : "Brugernavn er påkrævet.", + "password-required" : "Adgangskode er påkrævet.", + "ca-cert" : "Serverens CA-certifikatfil", + "private-key" : "Klientens private nøglefil", + "cert" : "Klientcertifikatfil", + "no-file" : "Ingen fil valgt.", + "drop-file" : "Slip en fil eller klik for at vælge en fil til upload.", + "private-key-password" : "Adgangskode til privat nøgle", + "use-system-smtp-settings" : "Brug systemets SMTP-indstillinger", + "use-metadata-dynamic-interval" : "Brug dynamisk interval", + "metadata-dynamic-interval-hint" : "Intervalstart og -slut felterne understøtter skabeloner. Brug $[messageKey] eller ${metadataKey}.", + "use-metadata-interval-patterns-hint" : "Hvis valgt, bruges mønstre fra metadata eller data til interval.", + "use-message-alarm-data" : "Brug alarmdata fra besked", + "overwrite-alarm-details" : "Overskriv alarmdetaljer", + "use-alarm-severity-pattern" : "Brug mønster for alarmens alvorlighed", + "check-all-keys" : "Tjek at alle specificerede felter er til stede", + "check-all-keys-hint" : "Tjekker at alle nøgler findes i data og metadata.", + "check-relation-to-specific-entity" : "Tjek relation til specifik enhed", + "check-relation-to-specific-entity-tooltip" : "Tjekker om relation eksisterer til en specifik eller hvilken som helst enhed baseret på type og retning.", + "check-relation-hint" : "Tjek for relation til bestemt eller vilkårlig enhed.", + "delete-relation-with-specific-entity" : "Slet relation med specifik enhed", + "delete-relation-with-specific-entity-hint" : "Sletter relation med en bestemt enhed. Ellers slettes alle matchende relationer.", + "delete-relation-hint" : "Sletter relation fra oprindelsen til den angivne enhed eller liste af enheder.", + "remove-current-relations" : "Fjern nuværende relationer", + "remove-current-relations-hint" : "Fjerner nuværende relationer baseret på type og retning.", + "change-originator-to-related-entity" : "Skift oprindelse til relateret enhed", + "change-originator-to-related-entity-hint" : "Behandler beskeden som om den kommer fra en anden enhed.", + "start-interval" : "Interval start", + "end-interval" : "Interval slut", + "start-interval-required" : "Interval start er påkrævet.", + "end-interval-required" : "Interval slut er påkrævet.", + "smtp-protocol" : "Protokol", + "smtp-host" : "SMTP-vært", + "smtp-host-required" : "SMTP-vært er påkrævet.", + "smtp-port" : "SMTP-port", + "smtp-port-required" : "SMTP-port er påkrævet.", + "smtp-port-range" : "SMTP-port skal være mellem 1 og 65535.", + "timeout-msec" : "Timeout (ms)", + "min-timeout-msec-message" : "Minimum tilladt værdi er 0 ms.", + "enter-username" : "Indtast brugernavn", + "enter-password" : "Indtast adgangskode", + "enable-tls" : "Aktiver TLS", + "tls-version" : "TLS-version", + "enable-proxy" : "Aktiver proxy", + "use-system-proxy-properties" : "Brug systemets proxy-egenskaber", + "proxy-host" : "Proxy-vært", + "proxy-host-required" : "Proxy-vært er påkrævet.", + "proxy-port" : "Proxy-port", + "proxy-port-required" : "Proxy-port er påkrævet.", + "proxy-port-range" : "Proxy-port skal være mellem 1 og 65535.", + "proxy-user" : "Proxy-bruger", + "proxy-password" : "Proxy-adgangskode", + "proxy-scheme" : "Proxy-skema", + "numbers-to-template" : "Telefonnumre til skabelon", + "numbers-to-template-required" : "Telefonnumre til skabelon er påkrævet", + "numbers-to-template-hint" : "Kommaseparerede telefonnumre, brug ${metadataKey} for værdi fra metadata, $[messageKey] for værdi fra meddelelsens indhold", + "sms-message-template" : "SMS beskedskabelon", + "sms-message-template-required" : "SMS beskedskabelon er påkrævet", + "use-system-sms-settings" : "Brug systemets SMS-udbyderindstillinger", + "min-period-0-seconds-message" : "Kun 0 sekunders minimumsperiode er tilladt.", + "max-pending-messages" : "Maksimalt antal ventende beskeder", + "max-pending-messages-required" : "Maksimalt antal ventende beskeder er påkrævet.", + "max-pending-messages-range" : "Skal være mellem 1 og 100000.", + "originator-types-filter" : "Filter for afsendertyper", + "interval-seconds" : "Interval i sekunder", + "interval-seconds-required" : "Interval er påkrævet.", + "int-range" : "Værdien må ikke overstige det maksimale heltalsgrænse (2147483648)", + "min-interval-seconds-message" : "Kun 1 sekund som minimumsinterval er tilladt.", + "output-timeseries-key-prefix" : "Præfiks for output time series nøgle", + "output-timeseries-key-prefix-required" : "Præfiks for output time series nøgle er påkrævet.", + "separator-hint" : "Tryk på \"Enter\" for at afslutte feltindtastning.", + "select-details" : "Vælg detaljer", + "entity-details-id" : "Id", + "entity-details-title" : "Titel", + "entity-details-country" : "Land", + "entity-details-state" : "Stat", + "entity-details-city" : "By", + "entity-details-zip" : "Postnummer", + "entity-details-address" : "Adresse", + "entity-details-address2" : "Adresse 2", + "entity-details-additional_info" : "Yderligere information", + "entity-details-phone" : "Telefon", + "entity-details-email" : "Email", + "email-sender" : "Email afsender", + "fields-to-check" : "Felter der skal kontrolleres", + "add-detail" : "Tilføj detalje", + "check-all-keys-tooltip" : "Kontrollerer, at alle angivne felter findes i besked og metadata.", + "fields-to-check-hint" : "Tryk på \"Enter\" for at afslutte. Flere felter understøttes.", + "entity-details-list-empty" : "Mindst én detalje skal vælges.", + "alarm-status" : "Alarmstatus", + "alarm-required" : "Mindst én alarmstatus skal vælges.", + "no-entity-details-matching" : "Ingen tilsvarende entitetsdetaljer fundet.", + "custom-table-name" : "Brugertabelnavn", + "custom-table-name-required" : "Tabelnavn er påkrævet", + "custom-table-hint" : "Skal starte med 'cs_tb_'. Angiv uden præfikset.", + "message-field" : "Beskedfelt", + "message-field-required" : "Beskedfelt er påkrævet.", + "table-col" : "Tabelkolonne", + "table-col-required" : "Tabelkolonne er påkrævet.", + "latitude-field-name" : "Navn på breddegradsfelt", + "longitude-field-name" : "Navn på længdegradsfelt", + "latitude-field-name-required" : "Navn på breddegradsfelt er påkrævet.", + "longitude-field-name-required" : "Navn på længdegradsfelt er påkrævet.", + "fetch-perimeter-info-from-metadata" : "Hent perimeteroplysninger fra metadata", + "fetch-perimeter-info-from-metadata-tooltip" : "Parser metadatafelt for cirkel eller polygon afhængigt af perimeter-typen.", + "perimeter-key-name" : "Perimeter nøgle navn", + "perimeter-key-name-hint" : "Metadata felt med perimeter info.", + "perimeter-key-name-required" : "Perimeter nøgle navn er påkrævet.", + "perimeter-circle" : "Cirkel", + "perimeter-polygon" : "Polygon", + "perimeter-type" : "Perimeter type", + "circle-center-latitude" : "Cirkelens center breddegrad", + "circle-center-latitude-required" : "Center breddegrad er påkrævet.", + "circle-center-longitude" : "Cirkelens center længdegrad", + "circle-center-longitude-required" : "Center længdegrad er påkrævet.", + "range-unit-meter" : "Meter", + "range-unit-kilometer" : "Kilometer", + "range-unit-foot" : "Fod", + "range-unit-mile" : "Mil", + "range-unit-nautical-mile" : "Sømil", + "range-units" : "Afstandsenheder", + "range-units-required" : "Afstandsenheder er påkrævet.", + "range" : "Rækkevidde", + "range-required" : "Rækkevidde er påkrævet.", + "polygon-definition" : "Polygondefinition", + "polygon-definition-required" : "Polygondefinition er påkrævet.", + "polygon-definition-hint" : "Format: [[lat1,lon1],[lat2,lon2],...,[latN,lonN]].", + "min-inside-duration" : "Minimal varighed indenfor", + "min-inside-duration-value-required" : "Værdi er påkrævet", + "min-inside-duration-time-unit" : "Tidsenhed for minimal varighed indenfor", + "min-outside-duration" : "Minimal varighed udenfor", + "min-outside-duration-value-required" : "Værdi er påkrævet", + "min-outside-duration-time-unit" : "Tidsenhed for minimal varighed udenfor", + "tell-failure-if-absent" : "Rapporter fejl ved fravær", + "tell-failure-if-absent-hint" : "Hvis en valgt nøgle mangler, returneres fejl.", + "get-latest-value-with-ts" : "Hent seneste værdi med tidsstempel", + "get-latest-value-with-ts-hint" : "Returnerer JSON med både værdi og tidsstempel.", + "ignore-null-strings" : "Ignorér tomme strenge", + "ignore-null-strings-hint" : "Ignorerer felter med tomme værdier.", + "add-metadata-key-values-as-kafka-headers" : "Tilføj nøgle-værdi par fra metadata til Kafka headers", + "add-metadata-key-values-as-kafka-headers-hint" : "Hvis valgt, tilføjes metadataens nøgle-værdi par som byte arrays med foruddefineret tegnkodning.", + "charset-encoding" : "Tegnkodning", + "charset-encoding-required" : "Tegnkodning er påkrævet.", + "charset-us-ascii" : "US-ASCII", + "charset-iso-8859-1" : "ISO-8859-1", + "charset-utf-8" : "UTF-8", + "charset-utf-16be" : "UTF-16BE", + "charset-utf-16le" : "UTF-16LE", + "charset-utf-16" : "UTF-16", + "select-queue-hint" : "Vælg et kønavn fra rullemenu eller angiv et brugerdefineret navn.", + "device-profile-node-hint" : "Nyttigt ved varighed eller gentagne betingelser for at sikre fortsat evaluering af alarmtilstand.", + "persist-alarm-rules" : "Gem tilstand for alarmregler", + "persist-alarm-rules-hint" : "Gemmer behandlingsstatus i databasen.", + "fetch-alarm-rules" : "Hent tilstand for alarmregler", + "fetch-alarm-rules-hint" : "Genskaber tilstand ved opstart for at sikre korrekt alarmstatus.", + "input-value-key" : "Input nøgle", + "input-value-key-required" : "Input nøgle er påkrævet.", + "output-value-key" : "Output nøgle", + "output-value-key-required" : "Output nøgle er påkrævet.", + "number-of-digits-after-floating-point" : "Antal decimaler", + "number-of-digits-after-floating-point-range" : "Antal decimaler skal være mellem 0 og 15.", + "failure-if-delta-negative" : "Rapportér fejl ved negativ delta", + "failure-if-delta-negative-tooltip" : "Fejl ved negativ forskel mellem værdier.", + "use-caching" : "Brug caching", + "use-caching-tooltip" : "Cacher input-værdi for performance.", + "add-time-difference-between-readings" : "Tilføj tidsforskel mellem aflæsninger", + "add-time-difference-between-readings-tooltip" : "Tilføjer \"{{periodValueKey}}\" til beskeden.", + "period-value-key" : "Tidsforskel nøgle", + "period-value-key-required" : "Tidsforskel nøgle er påkrævet.", + "general-pattern-hint" : "Brug ${metadataKey} eller $[messageKey]", + "alarm-severity-pattern-hint" : "Angiv alarmniveau (CRITICAL, MAJOR, etc.) via metadata eller besked.", + "output-node-name-hint" : "Navn på regelnode bruges som relationstype til videresendelse.", + "use-server-ts" : "Brug server-tidsstempel", + "use-server-ts-hint" : "Sikrer korrekt rækkefølge ved manglende tidsstempel.", + "kv-map-pattern-hint" : "Alle felter understøtter templatization.", + "kv-map-single-pattern-hint" : "Felt understøtter templatization.", + "shared-scope" : "Delt område", + "server-scope" : "Serverområde", + "client-scope" : "Klientområde", + "attribute-type" : "Attribute", + "attribute-type-description" : "Hent attributværdi fra database", + "attribute-type-result-description" : "Gem som attribut i database", + "constant-type" : "Konstant", + "constant-type-description" : "Definér konstant værdi", + "time-series-type" : "Tidsserie", + "time-series-type-description" : "Hent seneste værdi fra database", + "time-series-type-result-description" : "Gem som tidsserie i database", + "message-body-type" : "Besked", + "message-body-type-description" : "Hent argumentværdi fra besked", + "message-body-type-result-description" : "Tilføj resultat til besked", + "message-metadata-type" : "Metadata", + "message-metadata-type-description" : "Hent argumentværdi fra metadata", + "message-metadata-result-description" : "Tilføj resultat til metadata", + "argument-tile" : "Argumenter", + "no-arguments-prompt" : "Ingen argumenter konfigureret", + "result-title" : "Resultat", + "functions-field-input" : "Funktioner", + "no-option-found" : "Ingen muligheder fundet", + "argument-source-field-input" : "Kilde", + "argument-source-field-input-required" : "Kilde er påkrævet.", + "argument-key-field-input" : "Nøgle", + "argument-key-field-input-required" : "Nøgle er påkrævet.", + "constant-value-field-input" : "Konstant værdi", + "constant-value-field-input-required" : "Konstant værdi er påkrævet.", + "attribute-scope-field-input" : "Attribut område", + "attribute-scope-field-input-required" : "Område er påkrævet.", + "default-value-field-input" : "Standardværdi", + "type-field-input" : "Type", + "type-field-input-required" : "Type er påkrævet.", + "key-field-input" : "Nøgle", + "add-entity-type" : "Tilføj entitetstype", + "add-device-profile" : "Tilføj enhedsprofil", + "key-field-input-required" : "Nøgle er påkrævet.", + "number-floating-point-field-input" : "Antal decimaler", + "number-floating-point-field-input-hint" : "Brug 0 for heltal", + "add-to-message-field-input" : "Tilføj til besked", + "add-to-metadata-field-input" : "Tilføj til metadata", + "custom-expression-field-input" : "Matematisk udtryk", + "custom-expression-field-input-required" : "Matematisk udtryk er påkrævet", + "custom-expression-field-input-hint" : "F.eks. konverter Fahrenheit til Celsius", + "retained-message" : "Behold besked", + "attributes-mapping" : "Attribut mapping", + "latest-telemetry-mapping" : "Seneste telemetry mapping", + "add-mapped-attribute-to" : "Tilføj mappede attributter til", + "add-mapped-latest-telemetry-to" : "Tilføj mappet telemetry til", + "add-mapped-fields-to" : "Tilføj mappede felter til", + "add-selected-details-to" : "Tilføj valgte detaljer til", + "clear-selected-types" : "Ryd valgte typer", + "clear-selected-details" : "Ryd valgte detaljer", + "clear-selected-fields" : "Ryd valgte felter", + "clear-selected-keys" : "Ryd valgte nøgler", + "geofence-configuration" : "Geofence-konfiguration", + "coordinate-field-names" : "Koordinatfeltnavne", + "coordinate-field-hint" : "Forsøger at hente felter fra besked. Hvis ikke til stede, bruges metadata.", + "presence-monitoring-strategy" : "Strategi for tilstedeværelsesovervågning", + "presence-monitoring-strategy-on-first-message" : "Ved første besked", + "presence-monitoring-strategy-on-each-message" : "Ved hver besked", + "presence-monitoring-strategy-on-first-message-hint" : "Rapporterer status 'Inde' eller 'Ude' ved første besked efter minimumsvarighed siden sidste status.", + "presence-monitoring-strategy-on-each-message-hint" : "Rapporterer status ved hver besked efter statusændring.", + "fetch-credentials-to" : "Hent legitimationsoplysninger til", + "add-originator-attributes-to" : "Tilføj afsenders attributter til", + "originator-attributes" : "Afsenders attributter", + "fetch-latest-telemetry-with-timestamp" : "Hent seneste telemetry med tidsstempel", + "fetch-latest-telemetry-with-timestamp-tooltip" : "Inkluderer tidsstempel i metadata, f.eks.: \"{{latestTsKeyName}}\": \"{ts:1574329385897, value:42}\"", + "tell-failure" : "Rapportér fejl hvis attribut mangler", + "tell-failure-tooltip" : "Rapporterer fejl hvis mindst én valgt nøgle mangler.", + "created-time" : "Oprettelsestid", + "chip-help" : "Tryk 'Enter' for at afslutte {{inputName}}. 'Backspace' sletter. Flere værdier understøttet.", + "detail" : "detalje", + "field-name" : "feltnavn", + "device-profile" : "enhedsprofil", + "entity-type" : "entitetstype", + "message-type" : "beskedtype", + "timeseries-key" : "tidsserienøgle", + "type" : "Type", + "first-name" : "Fornavn", + "last-name" : "Efternavn", + "label" : "Etiket", + "originator-fields-mapping" : "Afsender felttilknytning", + "add-mapped-originator-fields-to" : "Tilføj mappede afsenderfelter til", + "fields" : "Felter", + "skip-empty-fields" : "Spring tomme felter over", + "skip-empty-fields-tooltip" : "Tomme felter tilføjes ikke til uddata.", + "fetch-interval" : "Hent interval", + "fetch-strategy" : "Hentestrategi", + "fetch-timeseries-from-to" : "Hent tidsserie fra {{startInterval}} {{startIntervalTimeUnit}} til {{endInterval}} {{endIntervalTimeUnit}} siden.", + "fetch-timeseries-from-to-invalid" : "\"Start\" skal være mindre end \"Slut\".", + "use-metadata-dynamic-interval-tooltip" : "Bruger dynamisk start og slut interval baseret på besked og metadata.", + "all-mode-hint" : "Ved \"Alle\" hentes alle værdier i intervallet.", + "first-mode-hint" : "Ved \"Første\" hentes nærmeste værdi ved start.", + "last-mode-hint" : "Ved \"Sidste\" hentes nærmeste værdi ved slut.", + "ascending" : "Stigende", + "descending" : "Faldende", + "min" : "Min", + "max" : "Maks", + "average" : "Gennemsnit", + "sum" : "Sum", + "count" : "Antal", + "none" : "Ingen", + "last-level-relation-tooltip" : "Søger kun relationer på det maksimale niveau.", + "last-level-device-relation-tooltip" : "Søger kun enheder på det maksimale niveau.", + "data-to-fetch" : "Data der skal hentes", + "mapping-of-customers" : "Kundemapping", + "map-fields-required" : "Alle felter skal udfyldes.", + "attributes" : "Attributter", + "related-device-attributes" : "Attributter for relateret enhed", + "add-selected-attributes-to" : "Tilføj valgte attributter til", + "device-profiles" : "Enhedsprofiler", + "mapping-of-tenant" : "Tenant-mapping", + "add-attribute-key" : "Tilføj attributnøgle", + "message-template" : "Beskedskabelon", + "message-template-required" : "Beskedskabelon er påkrævet", + "use-system-slack-settings" : "Brug systemets Slack-indstillinger", + "slack-api-token" : "Slack API-token", + "slack-api-token-required" : "Slack API-token er påkrævet", + "keys-mapping" : "Nøgletilknytning", + "add-key" : "Tilføj nøgle", + "recipients" : "Modtagere", + "message-subject-and-content" : "Besked emne og indhold", + "template-rules-hint" : "Brug $[messageKey] eller ${metadataKey} til værdier.", + "originator-customer-desc" : "Brug kundens oprindelige afsender.", + "originator-tenant-desc" : "Brug nuværende tenant.", + "originator-related-entity-desc" : "Brug relateret entitet som afsender.", + "originator-alarm-originator-desc" : "Brug alarmens afsender (kun hvis besked er alarm).", + "originator-entity-by-name-pattern-desc" : "Find og brug entitet ud fra navn og type.", + "email-from-template-hint" : "Brug $[messageKey] eller ${metadataKey} for at trække værdier.", + "recipients-block-main-hint" : "Kommasepareret liste, understøtter templatization.", + "forward-msg-default-rule-chain" : "Videresend besked til standard regelkæde", + "forward-msg-default-rule-chain-tooltip" : "Bruger standard regelkæde for afsender eller konfigureret kæde.", + "exclude-zero-deltas" : "Udelad nul-delta værdier", + "exclude-zero-deltas-hint" : "Tilføj kun nøgle hvis værdi ikke er nul.", + "exclude-zero-deltas-time-difference-hint" : "Tilføj kun hvis outputværdi ≠ 0.", + "search-direction-from" : "Fra afsender til målentitet", + "search-direction-to" : "Fra målentitet til afsender", + "del-relation-direction-from" : "Fra afsender", + "del-relation-direction-to" : "Til afsender", + "target-entity" : "Målenhed", + "function-configuration" : "Funktionskonfiguration", + "function-name" : "Funktionsnavn", + "function-name-required" : "Funktionsnavn er påkrævet.", + "qualifier" : "Kvalifikator", + "qualifier-hint" : "Hvis kvalifikator ikke er angivet, bruges standardværdien \"$LATEST\".", + "aws-credentials" : "AWS-legitimationsoplysninger", + "connection-timeout" : "Forbindelsestimeout", + "connection-timeout-required" : "Forbindelsestimeout er påkrævet.", + "connection-timeout-min" : "Minimum forbindelsestimeout er 0.", + "connection-timeout-hint" : "Ventetid ved etablering af forbindelse. 0 betyder uendelig og frarådes.", + "request-timeout" : "Anmodningstimeout", + "request-timeout-required" : "Anmodningstimeout er påkrævet", + "request-timeout-min" : "Minimum anmodningstimeout er 0", + "request-timeout-hint" : "Ventetid for anmodning, før timeout. 0 betyder uendelig og frarådes.", + "units" : "Enheder", + "tell-failure-aws-lambda" : "Rapportér fejl hvis AWS Lambda-funktion kaster en undtagelse", + "tell-failure-aws-lambda-hint" : "Tvinger fejl hvis Lambda-funktionen fejler.", + "basic-mode" : "Grundlæggende", + "advanced-mode" : "Avanceret", + "save-time-series" : { + "processing-settings" : "Behandlingsindstillinger", + "processing-settings-hint" : "Definér hvordan beskeder behandles. Grundlæggende eller avancerede strategier.", + "advanced-settings-hint" : "Vær forsigtig – visse kombinationer kan føre til uventet adfærd.", + "strategy" : "Strategi", + "deduplication-interval" : "Deduplikeringsinterval", + "deduplication-interval-required" : "Dedupliceringsinterval er påkrævet", + "deduplication-interval-min-max-range" : "Skal være mellem 1 sekund og 1 dag", + "strategy-type" : { + "every-message" : "Ved hver besked", + "skip" : "Spring over", + "deduplicate" : "Dedupliker", + "web-sockets-only" : "Kun WebSockets" + }, + "time-series" : "Tidsserier", + "latest" : "Seneste værdier", + "web-sockets" : "WebSockets", + "calculated-fields" : "Beregnet felter" + }, + "save-attribute" : { + "processing-settings" : "Behandlingsindstillinger", + "processing-settings-hint" : "Definér hvordan beskeder behandles. Grundlæggende eller avancerede strategier.", + "advanced-settings-hint" : "Vær forsigtig – visse kombinationer kan føre til uventet adfærd.", + "strategy" : "Strategi", + "deduplication-interval" : "Deduplikeringsinterval", + "deduplication-interval-required" : "Deduplikeringsinterval er påkrævet", + "deduplication-interval-min-max-range" : "Skal være mellem 1 sekund og 1 dag", + "scope" : "Omfang", + "strategy-type" : { + "every-message" : "Ved hver besked", + "skip" : "Spring over", + "deduplicate" : "Dedupliker", + "web-sockets-only" : "Kun WebSockets" + }, + "attributes" : "Attributter" + }, + "key-val" : { + "key" : "Nøgle", + "value" : "Værdi", + "see-examples" : "Se eksempler.", + "remove-entry" : "Fjern post", + "remove-mapping-entry" : "Fjern kortlægningspost", + "add-mapping-entry" : "Tilføj kortlægning", + "add-entry" : "Tilføj post", + "copy-key-values-from" : "Kopiér nøgle-værdier fra", + "delete-key-values" : "Slet nøgle-værdier", + "delete-key-values-from" : "Slet nøgle-værdier fra", + "at-least-one-key-error" : "Mindst én nøgle skal vælges.", + "unique-key-value-pair-error" : "'{{keyText}}' skal være forskellig fra '{{valText}}'!" + }, + "mail-body-types" : { + "plain-text" : "Almindelig tekst", + "html" : "HTML", + "dynamic" : "Dynamisk", + "use-body-type-template" : "Brug skabelon til brødtype", + "plain-text-description" : "Simpel tekst uden formatering.", + "html-text-description" : "Tillader HTML-tags til formatering, links og billeder.", + "dynamic-text-description" : "Vælg dynamisk mellem almindelig tekst eller HTML.", + "after-template-evaluation-hint" : "Efter evaluering: true = HTML, false = tekst." } }, - "language": { - "language": "Sprog" + "timezone" : { + "timezone" : "Tidszone", + "select-timezone" : "Vælg tidszone", + "no-timezones-matching" : "Ingen tidszoner matcher '{{timezone}}'.", + "timezone-required" : "Tidszone er påkrævet.", + "browser-time" : "Browserens tid" + }, + "queue" : { + "queue-name" : "Kø", + "no-queues-found" : "Ingen køer fundet.", + "no-queues-matching" : "Ingen køer matcher '{{queue}}'.", + "select-name" : "Vælg kønavn", + "name" : "Navn", + "name-required" : "Kønavn er påkrævet!", + "name-unique" : "Kønavnet er ikke unikt!", + "name-pattern" : "Kønavnet indeholder ugyldige tegn! Kun ASCII alfanumeriske tegn, '.', '_' og '-' er tilladt.", + "queue-required" : "Kø er påkrævet!", + "topic-required" : "Køemne er påkrævet!", + "poll-interval-required" : "Poll-interval er påkrævet!", + "poll-interval-min-value" : "Poll-intervalværdien må ikke være mindre end 1", + "partitions-required" : "Partitioner er påkrævet!", + "partitions-min-value" : "Antal partitioner må ikke være mindre end 1", + "pack-processing-timeout-required" : "Behandlingstimeout er påkrævet", + "pack-processing-timeout-min-value" : "Behandlingstimeout må ikke være mindre end 1", + "batch-size-required" : "Batchstørrelse er påkrævet!", + "batch-size-min-value" : "Batchstørrelse må ikke være mindre end 1", + "retries-required" : "Antal forsøg er påkrævet!", + "retries-min-value" : "Antal forsøg må ikke være negativt", + "failure-percentage-required" : "Fejlprocent er påkrævet!", + "failure-percentage-min-value" : "Fejlprocent må ikke være mindre end 0", + "failure-percentage-max-value" : "Fejlprocent må ikke være mere end 100", + "pause-between-retries-required" : "Pause mellem forsøg er påkrævet!", + "pause-between-retries-min-value" : "Pause mellem forsøg må ikke være mindre end 1", + "max-pause-between-retries-required" : "Maks. pause mellem forsøg er påkrævet!", + "max-pause-between-retries-min-value" : "Maks. pause mellem forsøg må ikke være mindre end 1", + "submit-strategy-type-required" : "Afsendelsesstrategi er påkrævet!", + "processing-strategy-type-required" : "Behandlingsstrategi er påkrævet!", + "queues" : "Køer", + "selected-queues" : "{ count, plural, =1 {1 kø} other {# køer} } valgt", + "delete-queue-title" : "Er du sikker på, at du vil slette køen '{{queueName}}'?", + "delete-queues-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 kø} other {# køer} }?", + "delete-queue-text" : "Vær forsigtig, efter bekræftelse vil køen og alle relaterede data være uigenkaldelige.", + "delete-queues-text" : "Efter bekræftelse vil alle valgte køer blive slettet og ikke være tilgængelige.", + "search" : "Søg kø", + "add" : "Tilføj kø", + "details" : "Kødetaljer", + "topic" : "Emne", + "submit-settings" : "Indstillinger for afsendelse", + "submit-strategy" : "Strategitype *", + "grouping-parameter" : "Grupperingsparameter", + "processing-settings" : "Behandlingsindstillinger for forsøg", + "processing-strategy" : "Behandlingstype *", + "retries-settings" : "Indstillinger for forsøg", + "polling-settings" : "Polling-indstillinger", + "batch-processing" : "Batchbehandling", + "poll-interval" : "Polling-interval", + "partitions" : "Partitioner", + "immediate-processing" : "Øjeblikkelig behandling", + "consumer-per-partition" : "Send beskedpolling for hver forbruger", + "consumer-per-partition-hint" : "Aktivér separat forbruger pr. partition", + "duplicate-msg-to-all-partitions" : "Duplikér besked til alle partitioner", + "processing-timeout" : "Behandlingstid, ms", + "batch-size" : "Batchstørrelse", + "retries" : "Antal forsøg (0 – ubegrænset)", + "failure-percentage" : "Fejlbeskeder til at springe forsøg over, %", + "pause-between-retries" : "Forsøg igen efter, sek", + "max-pause-between-retries" : "Yderligere forsøg efter, sek", + "delete" : "Slet kø", + "copyId" : "Kopiér kø-ID", + "idCopiedMessage" : "Kø-ID er kopieret til udklipsholder", + "description" : "Beskrivelse", + "description-hint" : "Denne tekst vises i købeskrivelsen i stedet for den valgte strategi", + "alt-description" : "Afsendelsesstrategi: {{submitStrategy}}, Behandlingsstrategi: {{processingStrategy}}", + "custom-properties" : "Brugerdefinerede egenskaber", + "custom-properties-hint" : "Brugerdefinerede egenskaber til køoprettelse, f.eks. 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies" : { + "sequential-by-originator-label" : "Sekventiel efter afsender", + "sequential-by-originator-hint" : "Ny besked for f.eks. enhed A sendes først, når den tidligere besked for enhed A er bekræftet", + "sequential-by-tenant-label" : "Sekventiel efter lejer", + "sequential-by-tenant-hint" : "Ny besked for f.eks. lejer A sendes først, når den tidligere besked for lejer A er bekræftet", + "sequential-label" : "Sekventiel", + "sequential-hint" : "Ny besked sendes først, når den tidligere er bekræftet", + "burst-label" : "Burst", + "burst-hint" : "Alle beskeder sendes til regelkæder i den rækkefølge, de modtages", + "batch-label" : "Batch", + "batch-hint" : "Ny batch sendes først, når den forrige batch er bekræftet", + "skip-all-failures-label" : "Spring alle fejl over", + "skip-all-failures-hint" : "Ignorer alle fejl", + "skip-all-failures-and-timeouts-label" : "Spring fejl og timeouts over", + "skip-all-failures-and-timeouts-hint" : "Ignorer fejl og timeouts", + "retry-all-label" : "Forsøg alle igen", + "retry-all-hint" : "Forsøg alle beskeder fra pakken igen", + "retry-failed-label" : "Forsøg fejlede igen", + "retry-failed-hint" : "Forsøg alle fejlede beskeder fra pakken igen", + "retry-timeout-label" : "Forsøg timeouts igen", + "retry-timeout-hint" : "Forsøg alle timeout-beskeder fra pakken igen", + "retry-failed-and-timeout-label" : "Forsøg fejl og timeout igen", + "retry-failed-and-timeout-hint" : "Forsøg alle fejl og timeout-beskeder igen" + } + }, + "queue-statistics" : { + "queue-statistics" : "Køstatistik", + "no-queue-statistics-matching" : "Ingen køstatistikker matcher '{{entity}}'.", + "queue-statistics-required" : "Køstatistik er påkrævet.", + "list-of-queue-statistics" : "{ count, plural, =1 {En køstatistik} other {Liste med # køstatistikker} }", + "selected-queue-statistics" : "{ count, plural, =1 {1 køstatistik} other {# køstatistikker} } valgt", + "no-queue-statistics-text" : "Ingen køstatistik fundet", + "queue-statistics-starts-with" : "Køstatistikker hvis navne starter med '{{prefix}}'" + }, + "server-error" : { + "general" : "Generel serverfejl", + "authentication" : "Godkendelsesfejl", + "jwt-token-expired" : "JWT-token er udløbet", + "tenant-trial-expired" : "Lejerens prøveperiode er udløbet", + "credentials-expired" : "Adgangsoplysninger er udløbet", + "permission-denied" : "Adgang nægtet", + "invalid-arguments" : "Ugyldige argumenter", + "bad-request-params" : "Ugyldige forespørgselsparametre", + "item-not-found" : "Element ikke fundet", + "too-many-requests" : "For mange forespørgsler", + "too-many-updates" : "For mange opdateringer" + }, + "tenant" : { + "tenant" : "Lejer", + "tenants" : "Lejere", + "management" : "Lejeradministration", + "add" : "Tilføj lejer", + "admins" : "Administratorer", + "manage-tenant-admins" : "Administrer lejeradministratorer", + "delete" : "Slet lejer", + "add-tenant-text" : "Tilføj ny lejer", + "no-tenants-text" : "Ingen lejere fundet", + "tenant-details" : "Lejerdetaljer", + "title-max-length" : "Titlen skal være under 256 tegn", + "delete-tenant-title" : "Er du sikker på, at du vil slette lejeren '{{tenantTitle}}'?", + "delete-tenant-text" : "Vær forsigtig, efter bekræftelsen vil lejeren og alle relaterede data blive uigenkaldelige.", + "delete-tenants-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 lejer} other {# lejere} }?", + "delete-tenants-action-title" : "Slet { count, plural, =1 {1 lejer} other {# lejere} }", + "delete-tenants-text" : "Vær forsigtig, efter bekræftelsen vil alle valgte lejere og relaterede data blive uigenkaldelige.", + "title" : "Titel", + "title-required" : "Titel er påkrævet.", + "description" : "Beskrivelse", + "details" : "Detaljer", + "events" : "Hændelser", + "copyId" : "Kopiér lejer-ID", + "idCopiedMessage" : "Lejer-ID er kopieret til udklipsholder", + "select-tenant" : "Vælg lejer", + "no-tenants-matching" : "Ingen lejere matcher '{{entity}}'.", + "tenant-required" : "Lejer er påkrævet", + "search" : "Søg lejere", + "selected-tenants" : "{ count, plural, =1 {1 lejer} other {# lejere} } valgt", + "isolated-tb-rule-engine" : "Brug isolerede ThingsBoard Rule Engine-køer", + "isolated-tb-rule-engine-details" : "Hver lejer vil have dedikerede Rule Engine-køer" + }, + "tenant-profile" : { + "tenant-profile" : "Lejerprofil", + "tenant-profiles" : "Lejerprofiler", + "add" : "Tilføj lejerprofil", + "add-profile" : "Tilføj profil", + "debug" : "Fejlfinding", + "edit" : "Rediger lejerprofil", + "tenant-profile-details" : "Detaljer om lejerprofil", + "no-tenant-profiles-text" : "Ingen lejerprofiler fundet", + "name-max-length" : "Navnet skal være under 256 tegn", + "search" : "Søg lejerprofiler", + "selected-tenant-profiles" : "{ count, plural, =1 {1 lejerprofil} other {# lejerprofiler} } valgt", + "no-tenant-profiles-matching" : "Ingen lejerprofil matcher '{{entity}}'.", + "tenant-profile-required" : "Lejerprofil er påkrævet", + "idCopiedMessage" : "Lejerprofil-ID er kopieret til udklipsholder", + "set-default" : "Gør lejerprofil til standard", + "delete" : "Slet lejerprofil", + "copyId" : "Kopiér lejerprofil-ID", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "data" : "Profildata", + "profile-configuration" : "Profilkonfiguration", + "description" : "Beskrivelse", + "default" : "Standard", + "delete-tenant-profile-title" : "Er du sikker på, at du vil slette lejerprofilen '{{tenantProfileName}}'?", + "delete-tenant-profile-text" : "Vær forsigtig, efter bekræftelsen vil lejerprofilen og alle relaterede data blive uigenkaldelige.", + "delete-tenant-profiles-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 lejerprofil} other {# lejerprofiler} }?", + "delete-tenant-profiles-text" : "Vær forsigtig, efter bekræftelsen vil alle valgte lejerprofiler og relaterede data blive uigenkaldelige.", + "set-default-tenant-profile-title" : "Er du sikker på, at du vil gøre lejerprofilen '{{tenantProfileName}}' til standard?", + "set-default-tenant-profile-text" : "Efter bekræftelsen vil lejerprofilen blive markeret som standard og bruges til nye lejere uden specifik profil.", + "no-tenant-profiles-found" : "Ingen lejerprofiler fundet.", + "create-new-tenant-profile" : "Opret en ny!", + "create-tenant-profile" : "Opret ny lejerprofil", + "import" : "Importer lejerprofil", + "export" : "Eksporter lejerprofil", + "export-failed-error" : "Kan ikke eksportere lejerprofil: {{error}}", + "tenant-profile-file" : "Lejerprofilfil", + "invalid-tenant-profile-file-error" : "Kan ikke importere lejerprofil: Ugyldig datastruktur.", + "advanced-settings" : "Avancerede indstillinger", + "entities" : "Entiteter", + "rule-engine" : "Rule Engine", + "time-to-live" : "Levetid", + "calculated-fields" : "Beregnet felter", + "alarms-and-notifications" : "Alarmer og notifikationer", + "ota-files-in-bytes" : "Filer", + "ws-title" : "WS", + "unlimited" : "(0 - ubegrænset)", + "maximum-devices" : "Maks. antal enheder", + "maximum-devices-required" : "Maks. antal enheder er påkrævet.", + "maximum-devices-range" : "Maks. antal enheder må ikke være negativt", + "maximum-assets" : "Maks. antal aktiver", + "maximum-assets-required" : "Maks. antal aktiver er påkrævet.", + "maximum-assets-range" : "Maks. antal aktiver må ikke være negativt", + "maximum-customers" : "Maks. antal kunder", + "maximum-customers-required" : "Maks. antal kunder er påkrævet.", + "maximum-customers-range" : "Maks. antal kunder må ikke være negativt", + "maximum-users" : "Maks. antal brugere", + "maximum-users-required" : "Maks. antal brugere er påkrævet.", + "maximum-users-range" : "Maks. antal brugere må ikke være negativt", + "maximum-dashboards" : "Maks. antal dashboards", + "maximum-dashboards-required" : "Maks. antal dashboards er påkrævet.", + "maximum-dashboards-range" : "Maks. antal dashboards må ikke være negativt", + "maximum-edges" : "Maks. antal edge-enheder", + "maximum-edges-required" : "Maks. antal edge-enheder er påkrævet.", + "maximum-edges-range" : "Maks. antal edge-enheder må ikke være negativt", + "maximum-rule-chains" : "Maks. antal regelkæder", + "maximum-rule-chains-required" : "Maks. antal regelkæder er påkrævet.", + "maximum-rule-chains-range" : "Maks. antal regelkæder må ikke være negativt", + "maximum-resources-sum-data-size" : "Maks. samlet størrelse af ressourcefiler (bytes)", + "maximum-resources-sum-data-size-required" : "Maks. samlet størrelse af ressourcefiler er påkrævet.", + "maximum-resources-sum-data-size-range" : "Maks. samlet størrelse af ressourcefiler må ikke være negativt", + "maximum-resource-size" : "Maks. størrelse på ressourcefil (bytes)", + "maximum-resource-size-required" : "Maks. størrelse på ressourcefil er påkrævet", + "maximum-resource-size-range" : "Maks. størrelse på ressourcefil må ikke være negativt", + "maximum-ota-packages-sum-data-size" : "Maks. samlet størrelse af OTA-pakker (bytes)", + "maximum-ota-package-sum-data-size-required" : "Maks. samlet størrelse af OTA-pakker er påkrævet.", + "maximum-ota-package-sum-data-size-range" : "Maks. samlet størrelse af OTA-pakker må ikke være negativt", + "maximum-debug-duration-min" : "Maks. fejlfindingstid (minutter)", + "maximum-debug-duration-min-range" : "Maks. fejlfindingstid må ikke være negativ", + "rest-requests-for-tenant" : "REST-forespørgsler for lejer", + "transport-tenant-telemetry-msg-rate-limit" : "Transportlejers telemetribeskeder", + "transport-tenant-telemetry-data-points-rate-limit" : "Transportlejers telemetridatapunkter", + "transport-device-msg-rate-limit" : "Transportenhedsbeskeder", + "transport-device-telemetry-msg-rate-limit" : "Transportenheds telemetribeskeder", + "transport-device-telemetry-data-points-rate-limit" : "Transportenheds telemetridatapunkter", + "transport-gateway-msg-rate-limit" : "Transport gateway-beskeder", + "transport-gateway-telemetry-msg-rate-limit" : "Transport gateway telemetribeskeder", + "transport-gateway-telemetry-data-points-rate-limit" : "Transport gateway telemetridatapunkter", + "transport-gateway-device-msg-rate-limit" : "Transport gateway-enhedsbeskeder", + "transport-gateway-device-telemetry-msg-rate-limit" : "Transport gateway-enheds telemetribeskeder", + "transport-gateway-device-telemetry-data-points-rate-limit" : "Transport gateway-enheds telemetridatapunkter", + "tenant-entity-export-rate-limit" : "Versionering af entiteter", + "tenant-entity-import-rate-limit" : "Indlæsning af entitetsversion", + "tenant-notification-request-rate-limit" : "Notifikationsanmodninger", + "tenant-notification-requests-per-rule-rate-limit" : "Notifikationsanmodninger pr. regel", + "max-calculated-fields" : "Maksimalt antal beregnede felter pr. enhed", + "max-calculated-fields-range" : "Maksimalt antal beregnede felter pr. enhed kan ikke være negativt", + "max-calculated-fields-required" : "Maksimalt antal beregnede felter pr. enhed er påkrævet", + "max-data-points-per-rolling-arg" : "Maksimalt antal datapunkter i rullende argumenter", + "max-data-points-per-rolling-arg-range" : "Maksimalt antal datapunkter i rullende argumenter kan ikke være negativt", + "max-data-points-per-rolling-arg-required" : "Maksimalt antal datapunkter i rullende argumenter er påkrævet", + "max-arguments-per-cf" : "Maksimalt antal argumenter pr. beregnet felt", + "max-arguments-per-cf-range" : "Maksimalt antal argumenter pr. beregnet felt kan ikke være negativt", + "max-arguments-per-cf-required" : "Maksimalt antal argumenter pr. beregnet felt er påkrævet", + "max-state-size" : "Maksimal tilstandsstørrelse i KB", + "max-state-size-range" : "Maksimal tilstandsstørrelse i KB kan ikke være negativ", + "max-state-size-required" : "Maksimal tilstandsstørrelse i KB er påkrævet", + "max-value-argument-size" : "Maksimal størrelse af enkeltværdien argument i KB", + "max-value-argument-size-range" : "Maksimal størrelse af enkeltværdien argument i KB kan ikke være negativ", + "max-value-argument-size-required" : "Maksimal størrelse af enkeltværdien argument i KB er påkrævet", + "max-transport-messages" : "Maksimalt antal transportbeskeder", + "max-transport-messages-required" : "Maksimalt antal transportbeskeder er påkrævet.", + "max-transport-messages-range" : "Maksimalt antal transportbeskeder må ikke være negativt", + "max-transport-data-points" : "Maksimalt antal transport datapunkter", + "max-transport-data-points-required" : "Maksimalt antal transport datapunkter er påkrævet.", + "max-transport-data-points-range" : "Maksimalt antal transport datapunkter må ikke være negativt", + "max-r-e-executions" : "Maksimalt antal Rule Engine-udførelser", + "max-r-e-executions-required" : "Maksimalt antal Rule Engine-udførelser er påkrævet.", + "max-r-e-executions-range" : "Maksimalt antal Rule Engine-udførelser må ikke være negativt", + "max-j-s-executions" : "Maksimalt antal JavaScript-udførelser", + "max-j-s-executions-required" : "Maksimalt antal JavaScript-udførelser er påkrævet.", + "max-j-s-executions-range" : "Maksimalt antal JavaScript-udførelser må ikke være negativt", + "max-tbel-executions" : "Maksimalt antal TBEL-udførelser", + "max-tbel-executions-required" : "Maksimalt antal TBEL-udførelser er påkrævet.", + "max-tbel-executions-range" : "Maksimalt antal TBEL-udførelser må ikke være negativt", + "max-d-p-storage-days" : "Maksimale lagringsdage for datapunkter", + "max-d-p-storage-days-required" : "Maksimale lagringsdage for datapunkter er påkrævet.", + "max-d-p-storage-days-range" : "Maksimale lagringsdage for datapunkter må ikke være negativt", + "default-storage-ttl-days" : "Standard lagring TTL dage", + "default-storage-ttl-days-required" : "Standard lagring TTL dage er påkrævet.", + "default-storage-ttl-days-range" : "Standard lagring TTL dage må ikke være negativt", + "alarms-ttl-days" : "Alarmers TTL dage", + "alarms-ttl-days-required" : "Alarmers TTL dage er påkrævet", + "alarms-ttl-days-days-range" : "Alarmers TTL dage må ikke være negativt", + "rpc-ttl-days" : "RPC TTL dage", + "rpc-ttl-days-required" : "RPC TTL dage er påkrævet", + "rpc-ttl-days-days-range" : "RPC TTL dage må ikke være negativt", + "queue-stats-ttl-days" : "Køstatistik TTL dage", + "queue-stats-ttl-days-required" : "Køstatistik TTL dage er påkrævet", + "queue-stats-ttl-days-range" : "Køstatistik TTL dage må ikke være negativt", + "rule-engine-exceptions-ttl-days" : "Rule Engine-undtagelser TTL dage", + "rule-engine-exceptions-ttl-days-required" : "Rule Engine-undtagelser TTL dage er påkrævet", + "rule-engine-exceptions-ttl-days-range" : "Rule Engine-undtagelser TTL dage må ikke være negativt", + "max-rule-node-executions-per-message" : "Maksimalt antal regelnode-udførelser pr. besked", + "max-rule-node-executions-per-message-required" : "Maksimalt antal regelnode-udførelser pr. besked er påkrævet.", + "max-rule-node-executions-per-message-range" : "Maksimalt antal regelnode-udførelser pr. besked må ikke være negativt", + "max-emails" : "Maksimalt antal sendte e-mails", + "max-emails-required" : "Maksimalt antal sendte e-mails er påkrævet.", + "max-emails-range" : "Maksimalt antal sendte e-mails må ikke være negativt", + "sms-enabled" : "SMS aktiveret", + "max-sms" : "Maksimalt antal sendte SMS'er", + "max-sms-required" : "Maksimalt antal sendte SMS'er er påkrævet.", + "max-sms-range" : "Maksimalt antal sendte SMS'er må ikke være negativt", + "max-created-alarms" : "Maksimalt antal oprettede alarmer", + "max-created-alarms-required" : "Maksimalt antal oprettede alarmer er påkrævet.", + "max-created-alarms-range" : "Maksimalt antal oprettede alarmer må ikke være negativt", + "no-queue" : "Ingen kø konfigureret", + "add-queue" : "Tilføj kø", + "queues-with-count" : "Køer ({{count}})", + "tenant-rest-limits" : "REST-anmodninger for lejer", + "customer-rest-limits" : "REST-anmodninger for kunde", + "incorrect-pattern-for-rate-limits" : "Formatet er kommaseparerede par af kapacitet og periode (i sekunder) adskilt af kolon, f.eks. 100:1,2000:60", + "too-small-value-zero" : "Værdien skal være større end 0", + "too-small-value-one" : "Værdien skal være større end 1", + "queue-size-is-limited-by-system-configuration" : "Køens størrelse er også begrænset af systemkonfigurationen.", + "cassandra-tenant-limits-configuration" : "Cassandra-forespørgsel for lejer", + "ws-limit-max-sessions-per-tenant" : "Maksimalt antal sessioner pr. lejer", + "ws-limit-max-sessions-per-customer" : "Maksimalt antal sessioner pr. kunde", + "ws-limit-max-sessions-per-regular-user" : "Maksimalt antal sessioner pr. almindelig bruger", + "ws-limit-max-sessions-per-public-user" : "Maksimalt antal sessioner pr. offentlig bruger", + "ws-limit-queue-per-session" : "Maksimal køstørrelse pr. session", + "ws-limit-max-subscriptions-per-tenant" : "Maksimalt antal abonnementer pr. lejer", + "ws-limit-max-subscriptions-per-customer" : "Maksimalt antal abonnementer pr. kunde", + "ws-limit-max-subscriptions-per-regular-user" : "Maksimalt antal abonnementer pr. almindelig bruger", + "ws-limit-max-subscriptions-per-public-user" : "Maksimalt antal abonnementer pr. offentlig bruger", + "ws-limit-updates-per-session" : "WS-opdateringer pr. session", + "rate-limits" : { + "add-limit" : "Tilføj begrænsning", + "advanced-settings" : "Avancerede indstillinger", + "edit-limit" : "Rediger begrænsning", + "but-less-than" : "men mindre end", + "calculated-field-debug-event-rate-limit" : "Fejlfindingshændelser for beregnede felter", + "edit-calculated-field-debug-event-rate-limit" : "Rediger hastighedsgrænser for fejlfindingshændelser for beregnede felter", + "edit-transport-tenant-msg-title" : "Rediger grænse for transportlejers beskeder", + "edit-transport-tenant-telemetry-msg-title" : "Rediger grænse for transportlejers telemetribeskeder", + "edit-transport-tenant-telemetry-data-points-title" : "Rediger grænse for transportlejers datapunkter", + "edit-transport-device-msg-title" : "Rediger grænse for transportenheds beskeder", + "edit-transport-device-telemetry-msg-title" : "Rediger grænse for transportenheds telemetribeskeder", + "edit-transport-device-telemetry-data-points-title" : "Rediger grænse for transportenheds datapunkter", + "edit-transport-gateway-msg-title" : "Rediger grænse for gateway-beskeder", + "edit-transport-gateway-telemetry-msg-title" : "Rediger grænse for gateway telemetribeskeder", + "edit-transport-gateway-telemetry-data-points-title" : "Rediger grænse for gateway datapunkter", + "edit-transport-gateway-device-msg-title" : "Rediger grænse for gateway-enheds beskeder", + "edit-transport-gateway-device-telemetry-msg-title" : "Rediger grænse for gateway-enheds telemetribeskeder", + "edit-transport-gateway-device-telemetry-data-points-title" : "Rediger grænse for gateway-enheds datapunkter", + "edit-tenant-rest-limits-title" : "Rediger REST-anmodninger for lejer", + "edit-customer-rest-limits-title" : "Rediger REST-anmodninger for kunde", + "edit-ws-limit-updates-per-session-title" : "Rediger grænse for WS-opdateringer pr. session", + "edit-cassandra-tenant-limits-configuration-title" : "Rediger Cassandra-forespørgselsgrænse for lejer", + "edit-tenant-entity-export-rate-limit-title" : "Rediger grænse for versionering af entitet", + "edit-tenant-entity-import-rate-limit-title" : "Rediger grænse for indlæsning af entitet", + "edit-tenant-notification-request-rate-limit-title" : "Rediger grænse for notifikationsanmodninger", + "edit-tenant-notification-requests-per-rule-rate-limit-title" : "Rediger grænse for notifikationsanmodninger pr. regel", + "edit-edge-events-rate-limit" : "Rediger grænse for edge-hændelser", + "edit-edge-events-per-edge-rate-limit" : "Rediger grænse for edge-hændelser pr. edge", + "edge-events-rate-limit" : "Edge-hændelser", + "edge-events-per-edge-rate-limit" : "Edge-hændelser pr. edge", + "edit-edge-uplink-messages-rate-limit" : "Rediger grænse for edge uplink-beskeder", + "edit-edge-uplink-messages-per-edge-rate-limit" : "Rediger grænse for edge uplink-beskeder pr. edge", + "edge-uplink-messages-rate-limit" : "Edge uplink-beskeder", + "edge-uplink-messages-per-edge-rate-limit" : "Edge uplink-beskeder pr. edge", + "messages-per" : "beskeder pr.", + "not-set" : "Ikke angivet", + "number-of-messages" : "Antal beskeder", + "number-of-messages-required" : "Antal beskeder er påkrævet.", + "number-of-messages-min" : "Minimumsværdi er 1.", + "preview" : "Forhåndsvis", + "per-seconds" : "Pr. sekunder", + "per-seconds-required" : "Tidsrate er påkrævet.", + "per-seconds-min" : "Minimumsværdi er 1.", + "rate-limits" : "Grænser", + "remove-limit" : "Fjern begrænsning", + "transport-tenant-msg" : "Transportlejers beskeder", + "transport-tenant-telemetry-msg" : "Transportlejers telemetribeskeder", + "transport-tenant-telemetry-data-points" : "Transportlejers telemetridatapunkter", + "transport-device-msg" : "Transportenheds beskeder", + "transport-device-telemetry-msg" : "Transportenheds telemetribeskeder", + "transport-device-telemetry-data-points" : "Transportenheds telemetridatapunkter", + "transport-gateway-msg" : "Gateway-beskeder", + "transport-gateway-telemetry-msg" : "Gateway telemetribeskeder", + "transport-gateway-telemetry-data-points" : "Gateway telemetridatapunkter", + "transport-gateway-device-msg" : "Gateway-enheds beskeder", + "transport-gateway-device-telemetry-msg" : "Gateway-enheds telemetribeskeder", + "transport-gateway-device-telemetry-data-points" : "Gateway-enheds telemetridatapunkter", + "sec" : "sek" + } + }, + "timeinterval" : { + "seconds-interval" : "{ seconds, plural, =1 {1 sekund} other {# sekunder} }", + "minutes-interval" : "{ minutes, plural, =1 {1 minut} other {# minutter} }", + "hours-interval" : "{ hours, plural, =1 {1 time} other {# timer} }", + "days-interval" : "{ days, plural, =1 {1 dag} other {# dage} }", + "days" : "Dage", + "hours" : "Timer", + "minutes" : "Minutter", + "seconds" : "Sekunder", + "advanced" : "Avanceret", + "custom" : "Brugerdefineret", + "predefined" : { + "yesterday" : "I går", + "day-before-yesterday" : "I forgårs", + "this-day-last-week" : "Denne dag i sidste uge", + "previous-week" : "Forrige uge (søn - lør)", + "previous-week-iso" : "Forrige uge (man - søn)", + "previous-month" : "Forrige måned", + "previous-quarter" : "Forrige kvartal", + "previous-half-year" : "Forrige halvår", + "previous-year" : "Forrige år", + "current-hour" : "Nuværende time", + "current-day" : "Nuværende dag", + "current-day-so-far" : "Nuværende dag indtil nu", + "current-week" : "Nuværende uge (søn - lør)", + "current-week-iso" : "Nuværende uge (man - søn)", + "current-week-so-far" : "Nuværende uge indtil nu (søn - lør)", + "current-week-iso-so-far" : "Nuværende uge indtil nu (man - søn)", + "current-month" : "Nuværende måned", + "current-month-so-far" : "Nuværende måned indtil nu", + "current-quarter" : "Nuværende kvartal", + "current-quarter-so-far" : "Nuværende kvartal indtil nu", + "current-half-year" : "Nuværende halvår", + "current-half-year-so-far" : "Nuværende halvår indtil nu", + "current-year" : "Nuværende år", + "current-year-so-far" : "Nuværende år indtil nu" + }, + "type" : { + "week" : "Uge (søn - lør)", + "week-iso" : "Uge (man - søn)", + "month" : "Måned", + "quarter" : "Kvartal" + } + }, + "timeunit" : { + "milliseconds" : "Millisekunder", + "seconds" : "Sekunder", + "minutes" : "Minutter", + "hours" : "Timer", + "days" : "Dage" + }, + "timewindow" : { + "timewindow" : "Tidsvindue", + "timewindow-settings" : "Indstillinger for tidsvindue", + "years" : "{ years, plural, =1 { år } other {# år } }", + "years-short" : "{{ years }}å", + "months" : "{ months, plural, =1 { måned } other {# måneder } }", + "months-short" : "{{ months }}M", + "weeks" : "{ weeks, plural, =1 { uge } other {# uger } }", + "weeks-short" : "{{ weeks }}u", + "days" : "{ days, plural, =1 { dag } other {# dage } }", + "days-short" : "{{ days }}d", + "hours" : "{ hours, plural, =0 { time } =1 {1 time } other {# timer } }", + "hr" : "{{ hr }} t", + "hr-short" : "{{ hr }}t", + "minutes" : "{ minutes, plural, =0 { minut } =1 {1 minut } other {# minutter } }", + "min" : "{{ min }} min", + "min-short" : "{{ min }}m", + "seconds" : "{ seconds, plural, =0 { sekund } =1 {1 sekund } other {# sekunder } }", + "sec" : "{{ sec }} sek", + "sec-short" : "{{ sec }}s", + "short" : { + "years" : "{ years, plural, =1 {1 år } other {# år } }", + "days" : "{ days, plural, =1 {1 dag } other {# dage } }", + "hours" : "{ hours, plural, =1 {1 time } other {# timer } }", + "minutes" : "{{minutes}} min ", + "seconds" : "{{seconds}} sek " + }, + "realtime" : "Realtime", + "history" : "Historik", + "last-prefix" : "sidste", + "period" : "fra {{ startTime }} til {{ endTime }}", + "edit" : "Rediger tidsvindue", + "date-range" : "Datointerval", + "for-all-time" : "For hele perioden", + "last" : "Sidste", + "time-period" : "Tidsperiode", + "hide" : "Skjul", + "interval" : "Interval", + "just-now" : "Lige nu", + "just-now-lower" : "lige nu", + "ago" : "siden", + "style" : "Tidsvinduets stil", + "icon" : "Ikon", + "icon-position" : "Ikonposition", + "icon-position-left" : "Venstre", + "icon-position-right" : "Højre", + "font" : "Skrifttype", + "color" : "Farve", + "displayTypePrefix" : "Vis Realtime/Historik præfiks", + "preview" : "Forhåndsvisning", + "relative" : "Relativ", + "range" : "Interval", + "hide-timewindow-section" : "Skjul tidsvinduessektion for slutbrugere", + "hide-last-interval" : "Skjul sidste interval for slutbrugere", + "hide-relative-interval" : "Skjul relativt interval for slutbrugere", + "hide-fixed-interval" : "Skjul fast interval for slutbrugere", + "hide-aggregation" : "Skjul aggregering for slutbrugere", + "hide-group-interval" : "Skjul grupperingsinterval for slutbrugere", + "hide-max-values" : "Skjul maksimale værdier for slutbrugere", + "hide-timezone" : "Skjul tidszone for slutbrugere", + "disable-custom-interval" : "Deaktiver brugerdefineret intervalvalg", + "edit-aggregation-functions-list" : "Rediger liste over aggregeringsfunktioner", + "edit-aggregation-functions-list-hint" : "Liste over tilgængelige muligheder kan specificeres.", + "allowed-aggregation-functions" : "Tilladte aggregeringsfunktioner", + "edit-intervals-list" : "Rediger intervalliste", + "allowed-agg-intervals" : "Grupperingsintervaller", + "default-agg-interval" : "Standard grupperingsinterval", + "edit-intervals-list-hint" : "Liste over tilgængelige intervalmuligheder kan specificeres.", + "edit-grouping-intervals-list-hint" : "Det er muligt at konfigurere listen over grupperingsintervaller og standard grupperingsinterval.", + "all" : "Alle" + }, + "tooltip" : { + "trigger" : "Udløser", + "trigger-point" : "Punkt", + "trigger-axis" : "Akse", + "label" : "Etiket", + "value" : "Værdi", + "date" : "Dato", + "show-date-time-interval" : "Vis dato og tidsinterval", + "show-date-time-interval-hint" : "Vis dato og tidsinterval i henhold til dataaggregeringen.", + "background-color" : "Baggrundsfarve", + "background-blur" : "Baggrundssløring" + }, + "unit" : { + "millimeter" : "Millimeter", + "centimeter" : "Centimeter", + "angstrom" : "Ångström", + "nanometer" : "Nanometer", + "micrometer" : "Mikrometer", + "meter" : "Meter", + "kilometer" : "Kilometer", + "inch" : "Tommer", + "foot" : "Fod", + "yard" : "Yard", + "mile" : "Mil", + "nautical-mile" : "Sømil", + "astronomical-unit" : "Astronomisk enhed", + "reciprocal-metre" : "Reciprok meter", + "meter-per-meter" : "Meter pr. meter", + "steradian" : "Steradian", + "thou" : "Tusindedel tomme", + "barleycorn" : "Bygkorn", + "hand" : "Hånd", + "chain" : "Kæde", + "furlong" : "Furlong", + "league" : "League", + "fathom" : "Favn", + "cable" : "Kabellængde", + "link" : "Led", + "rod" : "Stang", + "nanogram" : "Nanogram", + "microgram" : "Mikrogram", + "milligram" : "Milligram", + "gram" : "Gram", + "kilogram" : "Kilogram", + "tonne" : "Tonne", + "ounce" : "Ounce", + "pound" : "Pund", + "stone" : "Stone", + "hundredweight-count" : "Hundredweight", + "short-tons" : "Amerikanske tons", + "dalton" : "Dalton", + "grain" : "Gran", + "drachm" : "Drachm", + "quarter" : "Quarter", + "slug" : "Slug", + "carat" : "Karat", + "cubic-millimeter" : "Kubikmillimeter", + "cubic-centimeter" : "Kubikcentimeter", + "cubic-meter" : "Kubikmeter", + "cubic-kilometer" : "Kubikkilometer", + "microliter" : "Mikroliter", + "milliliter" : "Milliliter", + "liter" : "Liter", + "hectoliter" : "Hektoliter", + "cubic-inch" : "Kubiktomme", + "cubic-foot" : "Kubikfod", + "cubic-yard" : "Kubikyard", + "fluid-ounce" : "Fluid ounce", + "pint" : "Pint", + "quart" : "Quart", + "gallon" : "Gallon", + "oil-barrels" : "Olie tønde", + "cubic-meter-per-kilogram" : "Kubikmeter pr. kilogram", + "gill" : "Gill", + "hogshead" : "Tønde (hogshead)", + "teaspoon" : "Teske", + "tablespoon" : "Spiseske", + "cup" : "Kop", + "celsius" : "Celsius", + "kelvin" : "Kelvin", + "rankine" : "Rankine", + "fahrenheit" : "Fahrenheit", + "percent" : "Procent", + "meter-per-second" : "Meter pr. sekund", + "kilometer-per-hour" : "Kilometer i timen", + "foot-per-second" : "Fod pr. sekund", + "mile-per-hour" : "Mil i timen", + "knot" : "Knob", + "millimeters-per-minute" : "Millimeter pr. minut", + "kilometer-per-hour-squared" : "Kilometer i timen i anden", + "foot-per-second-squared" : "Fod pr. sekund i anden", + "pascal" : "Pascal", + "kilopascal" : "Kilopascal", + "megapascal" : "Megapascal", + "gigapascal" : "Gigapascal", + "millibar" : "Millibar", + "bar" : "Bar", + "kilobar" : "Kilobar", + "newton" : "Newton", + "newton-meter" : "Newtonmeter", + "foot-pounds" : "Fodpund", + "inch-pounds" : "Tommer-pund", + "newton-per-meter" : "Newton pr. meter", + "atmospheres" : "Atmosfærer", + "pounds-per-square-inch" : "Pund pr. kvadrattomme", + "torr" : "Torr", + "inches-of-mercury" : "Tommer kviksølv", + "pascal-per-square-meter" : "Pascal pr. kvadratmeter", + "pound-per-square-inch" : "Pund pr. kvadrattomme", + "newton-per-square-meter" : "Newton pr. kvadratmeter", + "kilogram-force-per-square-meter" : "Kilogramkraft pr. kvadratmeter", + "pascal-per-square-centimeter" : "Pascal pr. kvadratcentimeter", + "ton-force-per-square-inch" : "Ton-kraft pr. kvadrattomme", + "kilonewton-per-square-meter" : "Kilonewton pr. kvadratmeter", + "newton-per-square-millimeter" : "Newton pr. kvadratmillimeter", + "microjoule" : "Mikrojoule", + "millijoule" : "Millijoule", + "joule" : "Joule", + "kilojoule" : "Kilojoule", + "megajoule" : "Megajoule", + "gigajoule" : "Gigajoule", + "watt-hour" : "Watt-time", + "kilowatt-hour" : "Kilowatt-time", + "electron-volts" : "Elektronvolt", + "joules-per-coulomb" : "Joule pr. coulomb", + "british-thermal-unit" : "Britiske termiske enheder", + "foot-pound" : "Fodpund", + "calorie" : "Kalorie", + "small-calorie" : "Lille kalorie", + "kilocalorie" : "Kilokalorie", + "joule-per-kelvin" : "Joule pr. kelvin", + "joule-per-kilogram-kelvin" : "Joule pr. kilogram-kelvin", + "joule-per-kilogram" : "Joule pr. kilogram", + "watt-per-meter-kelvin" : "Watt pr. meter-kelvin", + "joule-per-cubic-meter" : "Joule pr. kubikmeter", + "therm" : "Therm", + "electric-dipole-moment" : "Elektrisk dipolmoment", + "magnetic-dipole-moment" : "Magnetisk dipolmoment", + "debye" : "Debye", + "coulomb-per-square-meter-per-volt" : "Coulomb pr. kvadratmeter pr. volt", + "milliwatt" : "Milliwatt", + "microwatt" : "Mikrowatt", + "watt" : "Watt", + "kilowatt" : "Kilowatt", + "megawatt" : "Megawatt", + "gigawatt" : "Gigawatt", + "metric-horsepower" : "Metrisk hestekraft", + "milliwatt-per-square-centimeter" : "Milliwatt pr. kvadratcentimeter", + "watt-per-square-centimeter" : "Watt pr. kvadratcentimeter", + "kilowatt-per-square-centimeter" : "Kilowatt pr. kvadratcentimeter", + "milliwatt-per-square-meter" : "Milliwatt pr. kvadratmeter", + "watt-per-square-meter" : "Watt pr. kvadratmeter", + "kilowatt-per-square-meter" : "Kilowatt pr. kvadratmeter", + "watt-per-square-inch" : "Watt pr. kvadrattomme", + "kilowatt-per-square-inch" : "Kilowatt pr. kvadrattomme", + "horsepower" : "Hestekræfter", + "btu-per-hour" : "Britiske termiske enheder/time", + "coulomb" : "Coulomb", + "millicoulomb" : "Millicoulomb", + "microcoulomb" : "Mikrocoulomb", + "picocoulomb" : "Picocoulomb", + "coulomb-per-meter" : "Coulomb pr. meter", + "coulomb-per-cubic-meter" : "Coulomb pr. kubikmeter", + "coulomb-per-square-meter" : "Coulomb pr. kvadratmeter", + "square-millimeter" : "Kvadratmillimeter", + "square-centimeter" : "Kvadratcentimeter", + "square-meter" : "Kvadratmeter", + "hectare" : "Hektar", + "square-kilometer" : "Kvadratkilometer", + "square-inch" : "Kvadrattomme", + "square-foot" : "Kvadratfod", + "square-yard" : "Kvadratyard", + "acre" : "Acre", + "square-mile" : "Kvadratmil", + "are" : "Ar", + "barn" : "Barn", + "circular-inch" : "Cirkulær tomme", + "milliampere-hour" : "Milliampere-time", + "ampere-hours" : "Amperetimer", + "kiloampere-hours" : "Kiloamperetimer", + "nanoampere" : "Nanoampere", + "picoampere" : "Picoampere", + "microampere" : "Mikroampere", + "milliampere" : "Milliampere", + "ampere" : "Ampere", + "microampere-per-square-centimeter" : "Mikroampere pr. kvadratcentimeter", + "ampere-per-square-meter" : "Ampere pr. kvadratmeter", + "ampere-per-meter" : "Ampere pr. meter", + "oersted" : "Oersted", + "bohr-magneton" : "Bohr magneton", + "ampere-meter-squared" : "Ampere-meter kvadreret", + "nanovolt" : "Nanovolt", + "picovolt" : "Picovolt", + "volt" : "Volt", + "dbmV" : "dBmV", + "dbm" : "dBm", + "volt-meter" : "Volt-meter", + "kilovolt-meter" : "Kilovolt-meter", + "megavolt-meter" : "Megavolt-meter", + "microvolt-meter" : "Mikrovolt-meter", + "millivolt-meter" : "Millivolt-meter", + "nanovolt-meter" : "Nanovolt-meter", + "ohm" : "Ohm", + "microohm" : "Mikroohm", + "milliohm" : "Milliohm", + "kilohm" : "Kiloohm", + "megohm" : "Megaohm", + "gigohm" : "Gigaohm", + "hertz" : "Hertz", + "kilohertz" : "Kilohertz", + "megahertz" : "Megahertz", + "gigahertz" : "Gigahertz", + "rpm" : "Omdrejninger pr. minut", + "candela-per-square-meter" : "Candela pr. kvadratmeter", + "candela" : "Candela", + "lumen" : "Lumen", + "lux" : "Lux", + "foot-candle" : "Foot-candle", + "lumen-per-square-meter" : "Lumen pr. kvadratmeter", + "lux-second" : "Lux sekund", + "lumen-second" : "Lumen sekund", + "lumens-per-watt" : "Lumen pr. watt", + "mole" : "Mol", + "nanomole" : "Nanomol", + "micromole" : "Mikromol", + "millimole" : "Millimol", + "kilomole" : "Kilomol", + "mole-per-cubic-meter" : "Mol pr. kubikmeter", + "rssi" : "RSSI", + "ppm" : "Dele pr. million", + "ppb" : "Dele pr. milliard", + "micrograms-per-cubic-meter" : "Mikrogram pr. kubikmeter", + "aqi" : "Luftkvalitetsindeks (AQI)", + "gram-per-cubic-meter" : "Gram pr. kubikmeter", + "gram-per-kilogram" : "Specifik fugtighed", + "millimeters-per-second" : "Millimeter pr. sekund", + "neper" : "Neper", + "bel" : "Bel", + "decibel" : "Decibel", + "meters-per-second-squared" : "Meter pr. sekund i anden", + "becquerel" : "Becquerel", + "curie" : "Curie", + "gray" : "Gray", + "sievert" : "Sievert", + "roentgen" : "Röntgen", + "cps" : "Tællinger pr. sekund", + "rad" : "Rad", + "rem" : "Rem", + "dps" : "Henfald pr. sekund", + "rutherford" : "Rutherford", + "coulombs-per-kilogram" : "Coulomb pr. kilogram", + "becquerels-per-cubic-meter" : "Becquerel pr. kubikmeter", + "curies-per-liter" : "Curie pr. liter", + "becquerels-per-second" : "Becquerel pr. sekund", + "curies-per-second" : "Curie pr. sekund", + "gy-per-second" : "Gray pr. sekund", + "watt-per-steradian" : "Watt pr. steradian", + "watt-per-square-metre-steradian" : "Watt pr. kvadratmeter-steradian", + "ph-level" : "pH-niveau", + "turbidity" : "Turbiditet", + "mg-per-liter" : "Milligram pr. liter", + "microsiemens-per-centimeter" : "Mikrosiemens pr. centimeter", + "millisiemens-per-meter" : "Millisiemens pr. meter", + "siemens-per-meter" : "Siemens pr. meter", + "kilogram-per-cubic-meter" : "Kilogram pr. kubikmeter", + "gram-per-cubic-centimeter" : "Gram pr. kubikcentimeter", + "kilogram-per-square-meter" : "Kilogram pr. kvadratmeter", + "milligram-per-milliliter" : "Milligram pr. milliliter", + "milligram-per-cubic-meter" : "Milligram pr. kubikmeter", + "pound-per-cubic-foot" : "Pund pr. kubikfod", + "ounces-per-cubic-inch" : "Unse pr. kubiktomme", + "tons-per-cubic-yard" : "Tons pr. kubikyard", + "particle-density" : "Partikeltæthed", + "kilometers-per-liter" : "Kilometer pr. liter", + "miles-per-gallon" : "Miles pr. gallon", + "liters-per-100-km" : "Liter pr. 100 km", + "gallons-per-mile" : "Gallons pr. mile", + "liters-per-hour" : "Liter pr. time", + "gallons-per-hour" : "Gallons pr. time", + "beats-per-minute" : "Slag pr. minut", + "millimeters-of-mercury" : "Millimeter kviksølv", + "milligrams-per-deciliter" : "Milligram pr. deciliter", + "g-force" : "G-kraft", + "kilonewton" : "Kilonewton", + "kilogram-force" : "Kilogram-kraft", + "pound-force" : "Pund-kraft", + "kilopound-force" : "Kilopund-kraft", + "dyne" : "Dyne", + "poundal" : "Poundal", + "kip" : "Kip", + "gal" : "Gal", + "gravity" : "Tyngdekraft", + "hectopascal" : "Hektopascal", + "atmosphere" : "Atmosfære", + "millibars" : "Millibar", + "inch-of-mercury" : "Tommer kviksølv", + "richter-scale" : "Richterskala", + "second" : "Sekund", + "minute" : "Minut", + "hour" : "Time", + "day" : "Dag", + "week" : "Uge", + "month" : "Måned", + "year" : "År", + "cubic-foot-per-minute" : "Kubikfod pr. minut", + "cubic-meters-per-hour" : "Kubikmeter pr. time", + "cubic-meters-per-second" : "Kubikmeter pr. sekund", + "liter-per-second" : "Liter pr. sekund", + "liter-per-minute" : "Liter pr. minut", + "gallons-per-minute" : "Galloner pr. minut", + "cubic-foot-per-second" : "Kubikfod pr. sekund", + "milliliters-per-minute" : "Milliliter pr. minut", + "bit" : "Bit", + "byte" : "Byte", + "kilobyte" : "Kilobyte", + "megabyte" : "Megabyte", + "gigabyte" : "Gigabyte", + "terabyte" : "Terabyte", + "petabyte" : "Petabyte", + "exabyte" : "Exabyte", + "zettabyte" : "Zettabyte", + "yottabyte" : "Yottabyte", + "bit-per-second" : "Bit pr. sekund", + "kilobit-per-second" : "Kilobit pr. sekund", + "megabit-per-second" : "Megabit pr. sekund", + "gigabit-per-second" : "Gigabit pr. sekund", + "terabit-per-second" : "Terabit pr. sekund", + "byte-per-second" : "Byte pr. sekund", + "kilobyte-per-second" : "Kilobyte pr. sekund", + "megabyte-per-second" : "Megabyte pr. sekund", + "gigabyte-per-second" : "Gigabyte pr. sekund", + "degree" : "Grad", + "radian" : "Radian", + "gradian" : "Gradian", + "revolution" : "Omdrejning", + "siemens" : "Siemens", + "millisiemens" : "Millisiemens", + "microsiemens" : "Mikrosiemens", + "kilosiemens" : "Kilosiemens", + "megasiemens" : "Megasiemens", + "gigasiemens" : "Gigasiemens", + "farad" : "Farad", + "millifarad" : "Millifarad", + "microfarad" : "Mikrofarad", + "nanofarad" : "Nanofarad", + "picofarad" : "Picofarad", + "kilofarad" : "Kilofarad", + "megafarad" : "Megafarad", + "gigafarad" : "Gigafarad", + "terfarad" : "Terfarad", + "farad-per-meter" : "Farad pr. meter", + "tesla" : "Tesla", + "gauss" : "Gauss", + "kilogauss" : "Kilogauss", + "millitesla" : "Millitesla", + "microtesla" : "Mikrotesla", + "nanotesla" : "Nanotesla", + "kilotesla" : "Kilotesla", + "megatesla" : "Megatesla", + "millitesla-square-meters" : "Millitesla kvadratmeter", + "gamma" : "Gamma", + "lambda" : "Lambda", + "square-meter-per-second" : "Kvadratmeter pr. sekund", + "square-centimeter-per-second" : "Kvadratcentimeter pr. sekund", + "stoke" : "Stoke", + "centistokes" : "Centistokes", + "square-foot-per-second" : "Kvadratfod pr. sekund", + "square-inch-per-second" : "Kvadrattomme pr. sekund", + "pascal-second" : "Pascal-sekund", + "centipoise" : "Centipoise", + "poise" : "Poise", + "reynolds" : "Reynolds", + "pound-per-foot-hour" : "Pund pr. fod-time", + "newton-second-per-square-meter" : "Newton-sekund pr. kvadratmeter", + "dyne-second-per-square-centimeter" : "Dyne-sekund pr. kvadratcentimeter", + "kilogram-per-meter-second" : "Kilogram pr. meter-sekund", + "tesla-square-meters" : "Tesla kvadratmeter", + "maxwell" : "Maxwell", + "tesla-per-meter" : "Tesla pr. meter", + "gauss-per-centimeter" : "Gauss pr. centimeter", + "weber" : "Weber", + "microweber" : "Mikroweber", + "milliweber" : "Milliweber", + "gauss-square-centimeter" : "Gauss-kvadratcentimeter", + "kilogauss-square-centimeter" : "Kilogauss-kvadratcentimeter", + "henry" : "Henry", + "millihenry" : "Millihenry", + "microhenry" : "Mikrohenry", + "nanohenry" : "Nanohenry", + "henry-per-meter" : "Henry pr. meter", + "tesla-meter-per-ampere" : "Tesla meter pr. ampere", + "gauss-per-oersted" : "Gauss pr. oersted", + "kilogram-per-mole" : "Kilogram pr. mol", + "gram-per-mole" : "Gram pr. mol", + "milligram-per-mole" : "Milligram pr. mol", + "joule-per-mole" : "Joule pr. mol", + "joule-per-mole-kelvin" : "Joule pr. mol-kelvin", + "millivolts-per-meter" : "Millivolt pr. meter", + "volts-per-meter" : "Volt pr. meter", + "kilovolts-per-meter" : "Kilovolt pr. meter", + "radian-per-second" : "Radian pr. sekund", + "radian-per-second-squared" : "Radian pr. sekund i anden", + "revolutions-per-minute-per-second" : "Vinkelacceleration", + "deg-per-second" : "grader/sekund", + "degrees-brix" : "Grader Brix", + "katal" : "Katal", + "katal-per-cubic-metre" : "Katal pr. kubikmeter" + }, + "user" : { + "user" : "Bruger", + "users" : "Brugere", + "customer-users" : "Kundebrugere", + "tenant-admins" : "Lejeradministratorer", + "sys-admin" : "Systemadministrator", + "tenant-admin" : "Lejeradministrator", + "customer" : "Kunde", + "anonymous" : "Anonym", + "add" : "Tilføj bruger", + "delete" : "Slet bruger", + "add-user-text" : "Tilføj ny bruger", + "no-users-text" : "Ingen brugere fundet", + "user-details" : "Brugerdetaljer", + "delete-user-title" : "Er du sikker på, at du vil slette brugeren '{{userEmail}}'?", + "delete-user-text" : "Vær forsigtig, efter bekræftelse bliver brugeren og alle relaterede data permanent slettet.", + "delete-users-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 bruger} other {# brugere} }?", + "delete-users-action-title" : "Slet { count, plural, =1 {1 bruger} other {# brugere} }", + "delete-users-text" : "Vær forsigtig, efter bekræftelse bliver alle valgte brugere slettet og data vil gå tabt.", + "activation-email-sent-message" : "Aktiveringsmail blev sendt!", + "resend-activation" : "Send aktiveringsmail igen", + "email" : "E-mail", + "email-required" : "E-mail er påkrævet.", + "invalid-email-format" : "Ugyldigt e-mail-format.", + "first-name" : "Fornavn", + "last-name" : "Efternavn", + "description" : "Beskrivelse", + "default-dashboard" : "Standard dashboard", + "always-fullscreen" : "Altid fuldskærm", + "select-user" : "Vælg bruger", + "no-users-matching" : "Ingen brugere matcher '{{entity}}'.", + "user-required" : "Bruger er påkrævet", + "activation-method" : "Aktiveringsmetode", + "display-activation-link" : "Vis aktiveringslink", + "send-activation-mail" : "Send aktiveringsmail", + "activation-link" : "Brugeraktiveringslink", + "activation-link-text" : "For at aktivere brugeren, brug følgende aktiveringslink (udløber om {{activationLinkTtl}}):", + "copy-activation-link" : "Kopiér aktiveringslink", + "activation-link-copied-message" : "Brugeraktiveringslink blev kopieret til udklipsholder", + "details" : "Detaljer", + "login-as-tenant-admin" : "Log ind som Lejeradministrator", + "login-as-customer-user" : "Log ind som Kundebruger", + "search" : "Søg brugere", + "selected-users" : "{ count, plural, =1 {1 bruger} other {# brugere} } valgt", + "disable-account" : "Deaktiver brugerkonto", + "enable-account" : "Aktiver brugerkonto", + "enable-account-message" : "Brugerkonto blev aktiveret!", + "disable-account-message" : "Brugerkonto blev deaktiveret!", + "copyId" : "Kopiér bruger-id", + "idCopiedMessage" : "Bruger-id blev kopieret til udklipsholder", + "user-list" : "Brugerliste", + "user-list-required" : "Brugerliste er påkrævet" + }, + "value" : { + "type" : "Værditype", + "string" : "Streng", + "string-value" : "Strengværdi", + "string-value-required" : "Strengværdi er påkrævet", + "integer" : "Heltal", + "integer-value" : "Heltalsværdi", + "integer-value-required" : "Heltalsværdi er påkrævet", + "invalid-integer-value" : "Ugyldig heltalsværdi", + "double" : "Decimal", + "double-value" : "Decimalværdi", + "double-value-required" : "Decimalværdi er påkrævet", + "boolean" : "Boolesk", + "boolean-value" : "Boolesk værdi", + "false" : "Falsk", + "true" : "Sand", + "long" : "Langt heltal", + "json" : "JSON", + "json-value" : "JSON-værdi", + "json-value-invalid" : "JSON-værdi har et ugyldigt format", + "json-value-required" : "JSON-værdi er påkrævet." + }, + "version-control" : { + "version-control" : "Versionskontrol", + "management" : "Versionskontroladministration", + "search" : "Søg versioner", + "branch" : "Gren", + "default" : "Standard", + "select-branch" : "Vælg gren", + "branch-required" : "Gren er påkrævet", + "create-entity-version" : "Opret entitetsversion", + "version-name" : "Versionsnavn", + "version-name-required" : "Versionsnavn er påkrævet", + "author" : "Forfatter", + "export-relations" : "Eksporter relationer", + "export-attributes" : "Eksporter attributter", + "export-credentials" : "Eksporter legitimationsoplysninger", + "export-calculated-fields" : "Eksporter beregnede felter", + "entity-versions" : "Entitetsversioner", + "versions" : "Versioner", + "created-time" : "Oprettelsestidspunkt", + "version-id" : "Versions-ID", + "no-entity-versions-text" : "Ingen entitetsversioner fundet", + "no-versions-text" : "Ingen versioner fundet", + "copy-full-version-id" : "Kopiér fuldt versions-id", + "create-version" : "Opret version", + "creating-version" : "Opretter version... Vent venligst", + "nothing-to-commit" : "Ingen ændringer at committe", + "restore-version" : "Gendan version", + "restore-entity-from-version" : "Gendan entitet fra version '{{versionName}}'", + "restoring-entity-version" : "Gendanner entitetsversion... Vent venligst", + "load-relations" : "Indlæs relationer", + "load-attributes" : "Indlæs attributter", + "load-credentials" : "Indlæs legitimationsoplysninger", + "load-calculated-fields" : "Indlæs beregnede felter", + "compare-with-current" : "Sammenlign med nuværende", + "diff-entity-with-version" : "Forskel fra entitetsversion '{{versionName}}'", + "previous-difference" : "Forrige forskel", + "next-difference" : "Næste forskel", + "current" : "Nuværende", + "differences" : "{ count, plural, =1 {1 forskel} other {# forskelle} }", + "create-entities-version" : "Opret version af enheder", + "default-sync-strategy" : "Standard synkroniseringsstrategi", + "sync-strategy-merge" : "Flet", + "sync-strategy-overwrite" : "Overskriv", + "entities-to-export" : "Enheder der skal eksporteres", + "entities-to-restore" : "Enheder der skal gendannes", + "sync-strategy" : "Synkroniseringsstrategi", + "all-entities" : "Alle enheder", + "no-entities-to-export-prompt" : "Angiv venligst enheder, der skal eksporteres", + "no-entities-to-restore-prompt" : "Angiv venligst enheder, der skal gendannes", + "add-entity-type" : "Tilføj enhedstype", + "remove-all" : "Fjern alle", + "version-create-result" : "{ added, plural, =0 {Ingen enheder} =1 {1 enhed} other {# enheder} } tilføjet.
{ modified, plural, =0 {Ingen enheder} =1 {1 enhed} other {# enheder} } ændret.
{ removed, plural, =0 {Ingen enheder} =1 {1 enhed} other {# enheder} } fjernet.", + "remove-other-entities" : "Fjern andre enheder", + "find-existing-entity-by-name" : "Find eksisterende enhed efter navn", + "restore-entities-from-version" : "Gendan enheder fra version '{{versionName}}'", + "restoring-entities-from-version" : "Gendanner enheder... Vent venligst", + "no-entities-restored" : "Ingen enheder gendannet", + "created" : "{{created}} oprettet", + "updated" : "{{updated}} opdateret", + "deleted" : "{{deleted}} slettet", + "remove-other-entities-confirm-text" : "Vær forsigtig! Dette vil permanent slette alle nuværende enheder
som ikke er til stede i den version, du ønsker at gendanne.

Indtast \"remove other entities\" for at bekræfte.", + "auto-commit-to-branch" : "auto-commit til {{ branch }} gren", + "default-create-entity-version-name" : "{{entityName}} opdatering", + "sync-strategy-merge-hint" : "Opretter eller opdaterer valgte enheder i arkivet. Alle andre enheder i arkivet ændres ikke.", + "sync-strategy-overwrite-hint" : "Opretter eller opdaterer valgte enheder i arkivet. Alle andre enheder i arkivet bliver slettet.", + "device-credentials-conflict" : "Kunne ikke indlæse enhed med ekstern id {{entityId}}
fordi de samme legitimationsoplysninger allerede findes i databasen for en anden enhed.
Overvej at deaktivere indlæs legitimationsoplysninger i gendannelsesformularen.", + "missing-referenced-entity" : "Kunne ikke indlæse {{sourceEntityTypeName}} med ekstern id {{sourceEntityId}}
fordi den refererer til en manglende {{targetEntityTypeName}} med id {{targetEntityId}}.", + "runtime-failed" : "Mislykkedes: {{message}}", + "auto-commit-settings-read-only-hint" : "Funktionen auto-commit fungerer ikke, når skrivebeskyttelse er aktiveret i arkivindstillingerne.", + "rollback-on-error" : "Tilbagefør ved fejl", + "rollback-on-error-hint" : "Hvis du har mange enheder at gendanne, bør du deaktivere denne mulighed for bedre ydeevne.\n Bemærk, hvis en fejl opstår under versionens indlæsning, forbliver allerede gemte enheder (med relationer, attributter osv.) som de er" + }, + "widget" : { + "widget-library" : "Widgetbibliotek", + "widget-bundle" : "Widgetpakke", + "all-bundles" : "Alle pakker", + "select-widgets-bundle" : "Vælg widgetpakke", + "widgets" : "Widgets", + "all-widgets" : "Alle widgets", + "widget" : "Widget", + "select-widget" : "Vælg widget", + "no-widgets-matching" : "Ingen widgets matcher '{{entity}}'.", + "no-widgets" : "Ingen widgets endnu", + "no-widgets-text" : "Ingen widgets fundet", + "management" : "Widgetadministration", + "editor" : "Widget-editor", + "confirm-to-exit-editor-html" : "Du har ikke gemte widgetindstillinger.
Er du sikker på, at du vil forlade denne side?", + "widget-type-not-found" : "Problem med indlæsning af widgetkonfiguration.
Den tilknyttede widgettype er muligvis blevet fjernet.", + "widget-type-load-error" : "Widget blev ikke indlæst pga. følgende fejl:", + "remove" : "Fjern widget", + "delete" : "Slet widget", + "edit" : "Rediger widget", + "remove-widget-title" : "Er du sikker på, at du vil fjerne widgetten '{{widgetTitle}}'?", + "remove-widget-text" : "Efter bekræftelse vil widgetten og alle relaterede data være uoprettelige.", + "replace-reference-with-widget-copy" : "Erstat reference med kopi af widget", + "timeseries" : "Tidsserie", + "search-data" : "Søg data", + "no-data-found" : "Ingen data fundet", + "latest" : "Seneste værdier", + "rpc" : "Kontrolwidget", + "alarm" : "Alarmwidget", + "static" : "Statisk widget", + "timeseries-short" : "serie", + "latest-short" : "seneste", + "rpc-short" : "kontrol", + "alarm-short" : "alarm", + "static-short" : "statisk", + "select-widget-type" : "Vælg widgettype", + "missing-widget-title-error" : "Widgettitel skal angives!", + "widget-saved" : "Widget gemt", + "unable-to-save-widget-error" : "Kan ikke gemme widget! Widget har fejl!", + "save" : "Gem widget", + "saveAs" : "Gem widget som", + "move" : "Flyt widget", + "save-widget-as" : "Gem widget som", + "save-widget-as-text" : "Indtast ny widgettitel", + "toggle-fullscreen" : "Skift fuldskærm", + "run" : "Kør widget", + "widget-title" : "Widgettitel", + "title" : "Titel", + "title-required" : "Widgettitel er påkrævet.", + "title-max-length" : "Titel må ikke overstige 256 tegn", + "system" : "System", + "type" : "Widgettype", + "resources" : "Ressourcer", + "resource-url" : "JavaScript/CSS URL", + "resource-is-extension" : "Er udvidelse", + "remove-resource" : "Fjern ressource", + "add-resource" : "Tilføj ressource", + "html" : "HTML", + "tidy" : "Tidy", + "css" : "CSS", + "settings-form" : "Indstillingsskema", + "data-key-settings-form" : "Datakey-indstillingsskema", + "latest-data-key-settings-form" : "Seneste datakey-indstillingsskema", + "widget-settings" : "Widgetindstillinger", + "description" : "Beskrivelse", + "tags" : "Tags", + "image-preview" : "Billedforhåndsvisning", + "settings-form-selector" : "Indstillingsformularvælger", + "data-key-settings-form-selector" : "Datakey-indstillingsformularvælger", + "latest-data-key-settings-form-selector" : "Seneste datakey-indstillingsformularvælger", + "all" : "Alle", + "actual" : "Aktuel", + "scada" : "SCADA-symbol", + "deprecated" : "Forældet", + "has-basic-mode" : "Har grundlæggende tilstand", + "basic-mode-form-selector" : "Formularvælger for grundtilstand", + "basic-mode" : "Grundlæggende", + "advanced-mode" : "Avanceret", + "javascript" : "Javascript", + "js" : "JS", + "delete-widget-title" : "Er du sikker på, at du vil slette widgetten '{{widgetName}}'?", + "delete-widget-text" : "Efter bekræftelse vil widgetten og alle relaterede data være uoprettelige.", + "delete-widgets-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 widget} other {# widgets} }?", + "delete-widgets-text" : "Vær forsigtig, efter bekræftelse vil alle valgte widgets blive fjernet og data vil være uoprettelige.", + "delete-widget" : "Slet widget", + "widget-template-load-failed-error" : "Indlæsning af widgetskabelon mislykkedes!", + "details" : "Detaljer", + "widget-details" : "Widgetdetaljer", + "add" : "Tilføj widget", + "add-existing-widget" : "Tilføj eksisterende widget", + "add-new-widget" : "Tilføj ny widget", + "search-widgets" : "Søg widgets", + "selected-widgets" : "{ count, plural, =1 {1 widget} other {# widgets} } valgt", + "undo" : "Fortryd widgetændringer", + "export" : "Eksportér widget", + "export-prompt" : "Indlejr widgetbilleder og ressourcer", + "export-widgets" : "Eksportér widgets", + "export-widgets-prompt" : "Indlejr widgets billeder og ressourcer", + "import" : "Importér widget", + "no-data" : "Ingen data at vise i widget", + "data-overflow" : "Widget viser {{count}} ud af {{total}} enheder", + "alarm-data-overflow" : "Widget viser alarmer for {{allowedEntities}} (maksimalt tilladt) enheder ud af {{totalEntities}} enheder", + "search" : "Søg widget", + "filter" : "Widget filtertype", + "loading-widgets" : "Indlæser widgets...", + "widget-template-error" : "Ugyldig HTML-skabelon for widget.", + "reference" : "Reference" + }, + "widget-action" : { + "header-button" : "Widget header-knap", + "do-nothing" : "Gør ingenting", + "open-dashboard-state" : "Naviger til ny dashboardtilstand", + "update-dashboard-state" : "Opdater aktuel dashboardtilstand", + "open-dashboard" : "Naviger til andet dashboard", + "custom" : "Brugerdefineret handling", + "custom-pretty" : "Brugerdefineret handling (med HTML-skabelon)", + "custom-pretty-error-title" : "Brugerdefineret dialogfejl", + "custom-pretty-template-error" : "Ugyldig skabelon for brugerdefineret dialog.", + "custom-pretty-controller-error" : "Der opstod en fejl ved evaluering af den brugerdefinerede dialogfunktion.", + "mobile-action" : "Mobilhandling", + "target-dashboard-state" : "Mål-dashboardtilstand", + "target-dashboard-state-required" : "Mål-dashboardtilstand er påkrævet", + "set-entity-from-widget" : "Sæt enhed fra widget", + "target-dashboard" : "Mål-dashboard", + "select-target-dashboard" : "Vælg mål-dashboard", + "target-dashboard-required" : "Mål-dashboard er påkrævet.", + "open-right-layout" : "Åbn højre dashboard-layout (mobilvisning)", + "state-display-type" : "Visningsindstilling for dashboardtilstand", + "open-normal" : "Normal", + "open-in-separate-dialog" : "Åbn i separat dialog", + "open-in-popover" : "Åbn i popover", + "dialog-title" : "Dialogtitel", + "dialog-hide-dashboard-toolbar" : "Skjul dashboardværktøjslinje i dialog", + "dialog-width" : "Dialogbredde i procent i forhold til vinduets bredde", + "dialog-height" : "Dialoghøjde i procent i forhold til vinduets højde", + "dialog-size-range-error" : "Dialogens procentværdi skal være mellem 1 og 100.", + "popover-preferred-placement" : "Foretrukken placering af popover", + "popover-placement-top" : "Top", + "popover-placement-topLeft" : "Øverst til venstre", + "popover-placement-topRight" : "Øverst til højre", + "popover-placement-right" : "Højre", + "popover-placement-rightTop" : "Øverst til højre (højre)", + "popover-placement-rightBottom" : "Nederst til højre (højre)", + "popover-placement-bottom" : "Bund", + "popover-placement-bottomLeft" : "Nederst til venstre", + "popover-placement-bottomRight" : "Nederst til højre", + "popover-placement-left" : "Venstre", + "popover-placement-leftTop" : "Øverst til venstre (venstre)", + "popover-placement-leftBottom" : "Nederst til venstre (venstre)", + "popover-hide-on-click-outside" : "Skjul popover ved klik udenfor", + "popover-hide-dashboard-toolbar" : "Skjul dashboardværktøjslinje i popover", + "popover-width" : "Popover bredde", + "popover-height" : "Popover højde", + "popover-style" : "Popover stil", + "open-new-browser-tab" : "Åbn i ny browserfane", + "open-URL" : "Åbn URL", + "URL" : "URL", + "url-required" : "URL er påkrævet.", + "mobile" : { + "device-provision" : "Enhedsprovisionering", + "action-type" : "Mobilhandlingstype", + "select-action-type" : "Vælg mobilhandlingstype", + "action-type-required" : "Mobilhandlingstype er påkrævet", + "take-picture-from-gallery" : "Tag billede fra galleri", + "take-photo" : "Tag foto", + "map-direction" : "Åbn kortvejvisning", + "map-location" : "Åbn kortplacering", + "scan-qr-code" : "Scan QR-kode", + "make-phone-call" : "Foretag telefonopkald", + "get-location" : "Hent telefonplacering", + "take-screenshot" : "Tag skærmbillede" + }, + "custom-action-function" : "Brugerdefineret handlingsfunktion", + "custom-pretty-function" : "Brugerdefineret handling (med HTML-skabelon) funktion", + "map-item-type" : "Kortelementtype", + "map-item" : { + "marker" : "Markør", + "polygon" : "Polygon", + "rectangle" : "Rektangel", + "circle" : "Cirkel" + }, + "place-map-item" : "Placér kortelement", + "map-item-tooltip" : { + "customize-map-item-tooltips" : "Tilpas værktøjstips for kortelement", + "place-marker" : "Placér markør", + "start-draw-rectangle" : "Start med at tegne rektangel", + "finish-draw-rectangle" : "Afslut tegning af rektangel", + "start-draw-polygon" : "Start med at tegne polygon", + "continue-draw-polygon" : "Fortsæt med at tegne polygon", + "finish-draw-polygon" : "Afslut tegning af polygon", + "start-draw-circle" : "Start med at tegne cirkel", + "finish-draw-circle" : "Afslut tegning af cirkel" + } + }, + "widgets-bundle" : { + "current" : "Aktuel pakke", + "widgets-bundles" : "Widget-pakker", + "widgets-bundle-widgets" : "Widgets i pakken", + "add" : "Tilføj widget-pakke", + "delete" : "Slet widget-pakke", + "title" : "Titel", + "title-required" : "Titel er påkrævet.", + "title-max-length" : "Titel skal være mindre end 256 tegn", + "description" : "Beskrivelse", + "image-preview" : "Billedforhåndsvisning", + "scada" : "SCADA widget-pakke", + "order" : "Rækkefølge", + "add-widgets-bundle-text" : "Tilføj ny widget-pakke", + "no-widgets-bundles-text" : "Ingen widget-pakker fundet", + "empty" : "Widget-pakken er tom", + "details" : "Detaljer", + "widgets-bundle-details" : "Detaljer om widget-pakken", + "delete-widgets-bundle-title" : "Er du sikker på, at du vil slette widget-pakken '{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text" : "Vær forsigtig, efter bekræftelsen vil pakken og alle relaterede data ikke kunne gendannes.", + "delete-widgets-bundles-title" : "Er du sikker på, at du vil slette { count, plural, =1 {1 widget-pakke} other {# widget-pakker} }?", + "delete-widgets-bundles-action-title" : "Slet { count, plural, =1 {1 widget-pakke} other {# widget-pakker} }", + "delete-widgets-bundles-text" : "Vær forsigtig, efter bekræftelsen vil alle valgte widget-pakker og relaterede data ikke kunne gendannes.", + "no-widgets-bundles-matching" : "Ingen widget-pakker matcher '{{widgetsBundle}}'.", + "widgets-bundle-required" : "Widget-pakke er påkrævet.", + "system" : "System", + "import" : "Importér widget-pakke", + "export" : "Eksportér widget-pakke", + "export-widgets-bundle-widgets-prompt" : "Medtag widgets i pakken i eksportdataene (ellers eksporteres kun refererede widget FQNs)", + "export-failed-error" : "Kunne ikke eksportere widget-pakke: {{error}}", + "create-new-widgets-bundle" : "Opret ny widget-pakke", + "widgets-bundle-file" : "Widget-pakkefil", + "invalid-widgets-bundle-file-error" : "Kunne ikke importere widget-pakke: Ugyldig datastruktur for widget-pakke.", + "search" : "Søg i widget-pakker", + "selected-widgets-bundles" : "{ count, plural, =1 {1 widget-pakke} other {# widget-pakker} } valgt", + "open-widgets-bundle" : "Åbn widget-pakke", + "loading-widgets-bundles" : "Indlæser widget-pakker...", + "create-new" : "Opret ny widget-pakke" + }, + "widget-config" : { + "data" : "Data", + "settings" : "Indstillinger", + "advanced" : "Avanceret", + "appearance" : "Udseende", + "widget-card" : "Widgetkort", + "mobile" : "Mobil", + "title" : "Titel", + "title-tooltip" : "Titelværktøjstip", + "general-settings" : "Generelle indstillinger", + "display-title" : "Vis widgettitel", + "card-title" : "Korttitel", + "drop-shadow" : "Skyggeeffekt", + "enable-fullscreen" : "Aktivér fuld skærm", + "background-color" : "Baggrundsfarve", + "text-color" : "Tekstfarve", + "border-radius" : "Kantafrunding", + "padding" : "Polstring", + "margin" : "Margin", + "widget-style" : "Widgetstil", + "widget-css" : "Widget CSS", + "title-style" : "Titelstil", + "mobile-mode-settings" : "Mobiltilstand", + "order" : "Rækkefølge", + "height" : "Højde", + "mobile-hide" : "Skjul widget i mobiltilstand", + "desktop-hide" : "Skjul widget i desktop-tilstand", + "units" : "Specielt symbol vist ved siden af værdien", + "units-by-default" : "Enheder som standard", + "decimals" : "Antal cifre efter decimalpunkt", + "decimals-by-default" : "Decimaler som standard", + "default-data-key-parameter-hint" : "Denne parameter gælder for alle widget-værdier, medmindre de overskrives i datanøgle-konfigurationen", + "units-short" : "Enheder", + "decimals-short" : "Decimaler", + "decimals-suffix" : "decimaler", + "digits-suffix" : "cifre", + "timewindow" : "Tidsvindue", + "use-dashboard-timewindow" : "Brug dashboard tidsvindue", + "use-widget-timewindow" : "Brug widget tidsvindue", + "display-timewindow" : "Vis tidsvindue", + "legend" : "Forklaring", + "display-legend" : "Vis forklaring", + "datasources" : "Datakilder", + "datasource" : "Datakilde", + "maximum-datasources" : "Maksimum { count, plural, =1 {1 datakilde tilladt.} other {# datakilder tilladt} }", + "timeseries-key-error" : "Mindst én tidsserie datanøgle skal være angivet", + "datasource-type" : "Type", + "datasource-parameters" : "Parametre", + "remove-datasource" : "Fjern datakilde", + "add-datasource" : "Tilføj datakilde", + "target-device" : "Målenhed", + "alarm-source" : "Alarmkilde", + "actions" : "Handlinger", + "action" : "Handling", + "add-action" : "Tilføj handling", + "search-actions" : "Søg efter handlinger", + "no-actions-text" : "Ingen handlinger fundet", + "action-source" : "Handlingskilde", + "select-action-source" : "Vælg handlingskilde", + "action-source-required" : "Handlingskilde er påkrævet.", + "column-index" : "Kolonneindeks", + "select-column-index" : "Vælg kolonneindeks", + "column-index-required" : "Kolonneindeks er påkrævet.", + "not-set" : "Ikke angivet", + "action-name" : "Navn", + "action-name-required" : "Handlingsnavn er påkrævet.", + "action-name-not-unique" : "Der findes allerede en anden handling med samme navn.\nHandlingsnavn skal være unikt inden for samme handlingskilde.", + "action-icon" : "Ikon", + "header-button" : { + "button-settings" : "Knapindstillinger", + "button-type" : "Knaptype", + "button-type-basic" : "Basis", + "button-type-raised" : "Hævet", + "button-type-stroked" : "Optrukket", + "button-type-flat" : "Flad", + "button-type-icon" : "Ikon", + "button-type-mini-fab" : "FAB", + "colors" : "Farver", + "color" : "Farve", + "background" : "Baggrund", + "border" : "Kant", + "advanced-button-style" : "Avanceret knapstil", + "button-style" : "Knapstil" + }, + "show-hide-action-using-function" : "Vis/skjul handling via funktion", + "show-action-function" : "Vis handlingsfunktion", + "action-type" : "Type", + "action-type-required" : "Handlingstype er påkrævet.", + "edit-action" : "Rediger handling", + "delete-action" : "Slet handling", + "delete-action-title" : "Slet widget-handling", + "delete-action-text" : "Er du sikker på, at du vil slette widget-handlingen med navnet '{{actionName}}'?", + "title-icon" : "Titelikon", + "display-icon" : "Vis titelikon", + "card-icon" : "Kortikon", + "icon" : "Ikon", + "icon-color" : "Ikonfarve", + "icon-size" : "Ikonstørrelse", + "advanced-settings" : "Avancerede indstillinger", + "data-settings" : "Datainstillinger", + "limits" : "Grænser", + "no-data-display-message" : "Alternativ meddelelse for \"Ingen data at vise\"", + "data-page-size" : "Maksimalt antal enheder pr. datakilde", + "settings-component-not-found" : "Indstillingsformular komponent ikke fundet for selector '{{selector}}'", + "preview" : "Forhåndsvisning", + "set" : "Indstil", + "set-message" : "Indstil besked", + "advanced-title-style" : "Avanceret titelstil", + "card-style" : "Kortstil", + "text" : "Tekst", + "background" : "Baggrund", + "advanced-widget-style" : "Avanceret widgetstil", + "card-buttons" : "Kortknapper", + "show-card-buttons" : "Vis kortknapper", + "card-border-radius" : "Kantafrunding på kort", + "card-padding" : "Kortpolstring", + "card-appearance" : "Kortudseende", + "color" : "Farve", + "tooltip" : "Værktøjstip", + "units-required" : "Enhed er påkrævet.", + "list-layout" : "Listeopsætning", + "layout" : "Layout", + "resize-options" : "Tilpasningsmuligheder", + "resizable" : "Skalérbar", + "preserve-aspect-ratio" : "Bevar billedformat" + }, + "widget-type" : { + "import" : "Importer widgettype", + "export" : "Eksporter widgettype", + "export-failed-error" : "Kan ikke eksportere widget: {{error}}", + "widget-file" : "Widgetfil", + "invalid-widget-file-error" : "Kan ikke importere widget: Ugyldig widgetdatastruktur." + }, + "markdown" : { + "edit" : "Rediger", + "preview" : "Forhåndsvisning", + "copy-code" : "Klik for at kopiere", + "copied" : "Kopieret!" + }, + "widgets" : { + "mobile-app-qr-code" : { + "configuration-hint" : "Konfigurationen afhænger af QR-kode widgeten for mobilappen i platformens hovedindstillinger", + "get-it-on-google-play" : "Hent på Google Play", + "download-on-the-app-store" : "Download i App Store" + }, + "action-button" : { + "behavior" : "Opførsel", + "on-click" : "Ved klik", + "on-click-hint" : "Handling udløses, når knappen klikkes", + "first-button-click" : "Første knapklik", + "first-button-click-hint" : "Handling ved tryk på første knap.", + "second-button-click" : "Andet knapklik", + "second-button-click-hint" : "Handling ved tryk på anden knap.", + "button-click-hint" : "Handling ved tryk på widget." + }, + "command-button" : { + "behavior" : "Opførsel", + "on-click" : "Ved klik", + "on-click-hint" : "Handling udføres, når knappen klikkes." + }, + "power-button" : { + "behavior" : "Opførsel", + "power-on" : "Tænd", + "power-on-hint" : "Handling udføres for at tænde komponenten.", + "power-off" : "Sluk", + "power-off-hint" : "Handling udføres for at slukke komponenten.", + "on-label" : "Tænd", + "off-label" : "Sluk", + "layout" : "Layout", + "layout-default" : "Standard", + "layout-simplified" : "Forenklet", + "layout-outlined" : "Optrukket", + "layout-default-volume" : "Standard.Lydstyrke", + "layout-simplified-volume" : "Forenklet.Lydstyrke", + "layout-outlined-volume" : "Optrukket.Lydstyrke", + "layout-default-icon" : "Standard.Ikon", + "layout-simplified-icon" : "Forenklet.Ikon", + "layout-outlined-icon" : "Optrukket.Ikon", + "main" : "Hoved", + "background" : "Baggrund", + "button-icon-on" : "Knapikon 'Tændt'", + "button-icon-off" : "Knapikon 'Slukket'", + "power-on-colors" : "Tænd-farver", + "power-off-colors" : "Sluk-farver", + "disabled-colors" : "Deaktiverede farver", + "button" : "Knap" + }, + "toggle-button" : { + "behavior" : "Opførsel", + "checked" : "Markeret", + "unchecked" : "Ikke markeret", + "check" : "Markér", + "check-hint" : "Handling udføres for at markere komponenten.", + "uncheck" : "Afmarkér", + "uncheck-hint" : "Handling udføres for at afmarkere komponenten.", + "auto-scale" : "Auto-skala", + "horizontal-fill" : "Vandret udfyldning", + "vertical-fill" : "Lodret udfyldning", + "button-appearance" : "Knappens udseende" + }, + "segmented-button" : { + "layout" : "Layout", + "layout-squared" : "Firkantet", + "layout-rounded" : "Afrundet", + "card-border" : "Kortkant", + "button-appearance" : "Knappens udseende", + "first" : "Første", + "second" : "Anden", + "color-styles" : "Farvestile", + "selected" : "Valgt", + "unselected" : "Ikke valgt" + }, + "button" : { + "layout" : "Layout", + "outlined" : "Optrukket", + "filled" : "Udfyldt", + "underlined" : "Understreget", + "basic" : "Grundlæggende", + "auto-scale" : "Auto-skala", + "label" : "Etiket", + "icon" : "Ikon", + "border-radius" : "Kanteradius", + "color-palette" : "Farvepalet", + "main" : "Hoved", + "background" : "Baggrund", + "border" : "Kant", + "custom-styles" : "Brugerdefinerede stilarter", + "clear-style" : "Ryd stil", + "shadow" : "Skygge", + "enabled" : "Aktiveret", + "disabled" : "Deaktiveret", + "preview" : "Forhåndsvisning", + "copy-style-from" : "Kopiér stil fra" + }, + "value-stepper" : { + "behavior" : "Opførsel", + "simplified" : "Forenklet", + "filled" : "Udfyldt", + "outlined" : "Omkredset", + "volume" : "Volumen", + "initial-state" : "Initial tilstand", + "initial-state-hint" : "Handling for at hente den initiale værdi.", + "disabled-state" : "Deaktiveret tilstand", + "disabled-state-hint" : "Konfigurer betingelsen, hvorunder komponenten er deaktiveret.", + "right-button-click" : "Højre knap klik", + "right-button-click-hint" : "Handling ved tryk på højre knap.", + "left-button-click" : "Venstre knap klik", + "left-button-click-hint" : "Handling ved tryk på venstre knap.", + "auto-scale" : "Automatisk skalering", + "value-range" : "Interval", + "min-range" : "Min", + "max-range" : "Maks", + "value-increment-decrement-step" : "Trin for værdi op/ned", + "value" : "Værdi", + "value-box-background" : "Baggrund for værdiboks", + "border" : "Kant", + "button-appearance" : "Knapudseende", + "left" : "Venstre", + "right" : "Højre", + "left-button" : "Venstre knap", + "right-button" : "Højre knap", + "icon" : "Ikon", + "color-palette" : "Farvepalet", + "main" : "Primær", + "background" : "Baggrund", + "button-icon-on" : "Knapikon 'Tændt'", + "button-on-colors" : "Farver for 'Tændt' tilstand", + "disabled-colors" : "Deaktiverede farver" + }, + "button-state" : { + "activated-state" : "Aktiveret tilstand", + "activated-state-hint" : "Konfigurer betingelse for hvornår knappen er aktiv.", + "disabled-state" : "Deaktiveret tilstand", + "disabled-state-hint" : "Konfigurer betingelse for hvornår knappen er deaktiveret.", + "selected-state" : "Valgt tilstand", + "selected-state-hint" : "Konfigurer betingelse for hvornår knappen er valgt.", + "enabled" : "Aktiveret", + "hovered" : "Holdt over", + "pressed" : "Trykket", + "activated" : "Aktiveret", + "disabled" : "Deaktiveret", + "initial" : "Første knap", + "first" : "Første", + "second" : "Anden" + }, + "background" : { + "background" : "Baggrund", + "background-settings" : "Baggrundsindstillinger", + "background-type-image" : "Billede", + "background-type-color" : "Farve", + "image-url" : "Billedets URL", + "overlay" : "Overlay", + "enable-overlay" : "Aktiver overlay", + "blur" : "Sløring", + "preview" : "Forhåndsvisning" + }, + "bar-chart" : { + "bar-appearance" : "Søjleudseende", + "label-on-bar" : "Etiket på søjle", + "value-on-bar" : "Værdi på søjle", + "bar-chart-style" : "Søjlediagramstil", + "bar-axis" : "Søjleafse" + }, + "polar-area-chart" : { + "polar-axis" : "Polarakse", + "start-angle" : "Startvinkel", + "polar-area-chart-style" : "Polar områdediagramstil" + }, + "battery-level" : { + "layout" : "Layout", + "layout-vertical-solid" : "Lodret. Solid", + "layout-horizontal-solid" : "Vandret. Solid", + "layout-vertical-divided" : "Lodret. Opdelt", + "layout-horizontal-divided" : "Vandret. Opdelt", + "icon" : "Ikon", + "value" : "Værdi", + "auto-scale" : "Auto-skala", + "battery-level-color" : "Batteriniveau farve", + "battery-shape-color" : "Batteriform farve", + "battery-level-card-style" : "Batteriniveau kortstil", + "sections-count" : "Sektioner antal" + }, + "signal-strength" : { + "value" : "Værdi", + "last-update" : "Seneste opdatering", + "no-signal" : "Intet signal", + "layout" : "Layout", + "layout-wifi" : "Wi-Fi", + "layout-cellular-bar" : "Mobil søjle", + "icon" : "Ikon", + "date" : "Dato", + "active-bars-color" : "Aktive signalbjælker farve", + "inactive-bars-color" : "Inaktive signalbjælker farve", + "signal-strength-card-style" : "Signalkortstil", + "no-signal-rssi-value" : "\"Intet signal\" rssi-værdi" + }, + "status-widget" : { + "behavior" : "Adfærd", + "layout" : "Layout", + "layout-default" : "Standard", + "layout-center" : "Centreret", + "layout-icon" : "Ikon", + "on" : "Tændt", + "off" : "Slukket", + "label" : "Etiket", + "status" : "Status", + "icon" : "Ikon", + "color-palette" : "Farvepalet", + "disabled-color-palette" : "Deaktiveret farvepalet", + "primary" : "Primær", + "primary-color-hint" : "Farve på ikon og etiket", + "secondary" : "Sekundær", + "secondary-color-hint" : "Farve på status", + "background" : "Baggrund" + }, + "chart" : { + "common-settings" : "Fælles indstillinger", + "enable-stacking-mode" : "Aktiver stabletilstand", + "selection" : "Tidsområdevalg", + "enable-selection-mode" : "Aktiver valgtilstand", + "line-shadow-size" : "Skyggestørrelse på linje", + "display-smooth-lines" : "Vis glatte (buede) linjer", + "default-bar-width" : "Standard søjlebredde for ikke-aggregerede data (millisekunder)", + "bar-alignment" : "Søjlens justering", + "bar-alignment-left" : "Venstre", + "bar-alignment-right" : "Højre", + "bar-alignment-center" : "Centreret", + "default-font" : "Standard skrifttype", + "default-font-size" : "Standard skrifttypestørrelse", + "default-font-color" : "Standard skrifttypefarve", + "thresholds-line-width" : "Standard linjebredde for alle tærskler", + "tooltip-settings" : "Værktøjstip-indstillinger", + "tooltip" : "Værktøjstip", + "show-tooltip" : "Vis værktøjstip", + "hover-individual-points" : "Hold musen over individuelle punkter", + "show-cumulative-values" : "Vis kumulative værdier i stabletilstand", + "hide-zero-false-values" : "Skjul nul/falske værdier fra værktøjstip", + "tooltip-value-format-function" : "Formateringsfunktion for værktøjstipværdi", + "grid-settings" : "Gitterindstillinger", + "show-vertical-lines" : "Vis lodrette linjer", + "show-horizontal-lines" : "Vis vandrette linjer", + "grid-outline-border-width" : "Gitterets kantbredde (px)", + "primary-color" : "Primær farve", + "background-color" : "Baggrundsfarve", + "ticks-color" : "Farve på markeringer", + "xaxis-settings" : "Indstillinger for X-akse", + "axis-title" : "Aksens titel", + "xaxis-tick-labels-settings" : "Indstillinger for X-aksens mærkater", + "show-tick-labels" : "Vis mærkater på akse", + "yaxis-settings" : "Indstillinger for Y-akse", + "min-scale-value" : "Minimumværdi på skala", + "max-scale-value" : "Maksimumværdi på skala", + "yaxis-tick-labels-settings" : "Indstillinger for Y-aksens mærkater", + "tick-step-size" : "Trin mellem markeringer", + "number-of-decimals" : "Antal decimaler", + "ticks-formatter-function" : "Formatteringsfunktion for markeringer", + "comparison-settings" : "Sammenligningsindstillinger", + "enable-comparison" : "Aktiver sammenligning", + "time-for-comparison" : "Sammenligningsperiode", + "time-for-comparison-previous-interval" : "Forrige interval (standard)", + "time-for-comparison-days" : "En dag siden", + "time-for-comparison-weeks" : "En uge siden", + "time-for-comparison-months" : "En måned siden", + "time-for-comparison-years" : "Et år siden", + "time-for-comparison-custom-interval" : "Brugertilpasset interval", + "custom-interval-value" : "Brugerdefineret intervalværdi (ms)", + "comparison-x-axis-settings" : "X-akseindstillinger for sammenligning", + "axis-position" : "Akses placering", + "axis-position-top" : "Øverst (standard)", + "axis-position-bottom" : "Nederst", + "custom-legend-settings" : "Brugerdefinerede signaturindstillinger", + "enable-custom-legend" : "Aktiver brugerdefineret signatur (brug attribut-/tidsserieværdier i nøgleetiketter)", + "key-name" : "Nøglenavn", + "key-name-required" : "Nøglenavn er påkrævet", + "key-type" : "Nøgletype", + "key-type-attribute" : "Attribut", + "key-type-timeseries" : "Tidsserie", + "label-keys-list" : "Liste over nøgler til brug i etiketter", + "no-label-keys" : "Ingen nøgler konfigureret", + "add-label-key" : "Tilføj ny nøgle", + "line-width" : "Linjebredde", + "color" : "Farve", + "data-is-hidden-by-default" : "Data er som standard skjult", + "disable-data-hiding" : "Deaktiver datas skjul", + "remove-from-legend" : "Fjern datanøgle fra signatur", + "exclude-from-stacking" : "Udeluk fra stabling (tilgængelig i \"Stabling\"-tilstand)", + "line-settings" : "Linjens indstillinger", + "show-line" : "Vis linje", + "fill-line" : "Udfyld linje", + "fill-line-opacity" : "Fyld opacitet", + "points-settings" : "Punktindstillinger", + "show-points" : "Vis punkter", + "points-line-width" : "Linjebredde for punkter", + "points-radius" : "Radius for punkter", + "point-shape" : "Punktform", + "point-shape-circle" : "Cirkel", + "point-shape-cross" : "Kryds", + "point-shape-diamond" : "Diamant", + "point-shape-square" : "Firkant", + "point-shape-triangle" : "Trekant", + "point-shape-custom" : "Brugerdefineret funktion", + "point-shape-draw-function" : "Tegnefunktion for punktform", + "show-separate-axis" : "Vis separat akse", + "axis-position-left" : "Venstre", + "axis-position-right" : "Højre", + "thresholds" : "Tærskler", + "no-thresholds" : "Ingen tærskler konfigureret", + "add-threshold" : "Tilføj tærskel", + "show-values-for-comparison" : "Vis historiske værdier til sammenligning", + "comparison-values-label" : "Etiket for historiske værdier", + "comparison-line-color" : "Farve for sammenligningslinje", + "threshold-settings" : "Tærskelindstillinger", + "use-as-threshold" : "Brug nøgleværdi som tærskel", + "threshold-line-width" : "Tærskellinjens bredde", + "threshold-color" : "Tærskelfarve", + "common-pie-settings" : "Fælles indstillinger for lagkage", + "radius" : "Radius", + "inner-radius" : "Indre radius", + "tilt" : "Hældning", + "common-pie-settings-range-error" : "Værdien skal være mellem 0 og 1", + "stroke-settings" : "Stregindstillinger", + "width-pixels" : "Bredde (pixels)", + "show-labels" : "Vis etiketter", + "animation-settings" : "Animationsindstillinger", + "animated-pie" : "Aktiver lagkageanimation (eksperimentelt)", + "border-settings" : "Kantindstillinger", + "border-width" : "Kantbredde", + "border-color" : "Kantfarve", + "legend-settings" : "Signaturindstillinger", + "display-legend" : "Vis signatur", + "labels-font-color" : "Skrifttypefarve for etiketter", + "series" : "Serier", + "add-series" : "Tilføj serie", + "series-settings" : "Serieindstillinger", + "remove-series" : "Fjern serie", + "no-series" : "Ingen serier konfigureret", + "no-series-error" : "Mindst én serie skal angives", + "chart-appearance" : "Diagrammets udseende", + "vertical-grid-lines" : "Lodrette gitterlinjer", + "horizontal-grid-lines" : "Vandrette gitterlinjer", + "chart-background" : "Diagrambaggrund", + "grid-lines-color" : "Gitterlinjefarve", + "border" : "Kant", + "axis" : "Akse", + "vertical-axis" : "Lodret akse", + "ticks" : "Mærker", + "horizontal-axis" : "Vandret akse", + "shape-empty-circle" : "Tom cirkel", + "shape-circle" : "Cirkel", + "shape-rect" : "Rektangel", + "shape-round-rect" : "Afrundet rektangel", + "shape-triangle" : "Trekant", + "shape-diamond" : "Diamant", + "shape-pin" : "Knappenål", + "shape-arrow" : "Pil", + "shape-none" : "Ingen", + "line-type-solid" : "Solid", + "line-type-dashed" : "Stiplet", + "line-type-dotted" : "Prikket", + "label-position-top" : "Top", + "label-position-bottom" : "Bund", + "label-position-outside" : "Udenfor", + "label-position-inside" : "Indenfor", + "fill" : "Fyld", + "fill-type-none" : "Ingen", + "fill-type-solid" : "Solid", + "fill-type-opacity" : "Gennemsigtighed", + "fill-type-gradient" : "Gradient", + "background" : "Baggrund", + "opacity" : "Gennemsigtighed", + "gradient-stops" : "Gradientstop", + "gradient-start" : "start", + "gradient-end" : "slut", + "animation" : { + "animation" : "Animation", + "animation-threshold" : "Animationsgrænse", + "animation-duration" : "Animationsvarighed", + "animation-easing" : "Animationsudjævning", + "animation-delay" : "Animationsforsinkelse", + "update-animation-duration" : "Opdater animationsvarighed", + "update-animation-easing" : "Opdater animationsudjævning", + "update-animation-delay" : "Opdater animationsforsinkelse" + }, + "chart-axis" : { + "scale" : "Skala", + "scale-min" : "min", + "scale-max" : "maks", + "scale-auto" : "Auto" + }, + "bar" : { + "show-border" : "Vis kant", + "border-width" : "Kantbredde", + "border-radius" : "Kantens afrunding", + "bar-width" : "Søjlebredde", + "label" : "Etiket", + "label-hint" : "Vis etiket over søjlen.", + "series-label-hint" : "Vis etiket med værdi over søjlen.", + "label-background" : "Etiketbaggrund" + } + }, + "color" : { + "color-settings" : "Farveindstillinger", + "color-type-constant" : "Konstant", + "color-type-gradient" : "Gradient", + "color-type-range" : "Interval", + "color-type-function" : "Funktion", + "color" : "Farve", + "value-range" : "Værdiparameter", + "from" : "Fra", + "to" : "Til", + "color-function" : "Farvefunktion", + "copy-color-settings-from" : "Kopiér farveindstillinger fra", + "copy-from" : "Kopiér fra", + "settings-type" : "Indstillingstype", + "basic-mode" : "Basis", + "advanced-mode" : "Avanceret", + "entity-alias" : "Enhedsalias", + "entity-attribute" : "Enhedsattribut", + "gradient-color" : "Gradientfarve", + "gradient-color-min" : "Farve", + "gradient-start" : "Startfarve for gradient", + "gradient-start-min" : "Start", + "gradient-end" : "Slutfarve for gradient", + "gradient-end-min" : "Slut", + "start-value" : "Startværdi", + "end-value" : "Slutværdi", + "gradient-type" : "Gradienttype" + }, + "dashboard-state" : { + "dashboard-state-settings" : "Dashboardtilstandsindstillinger", + "dashboard-state" : "Dashboardtilstand ID", + "autofill-state-layout" : "Autoudfyld tilstandslayoutets højde som standard", + "default-margin" : "Standard margen for widgets", + "default-background-color" : "Standard baggrundsfarve", + "sync-parent-state-params" : "Synkroniser tilstandsparametre med overordnet dashboard" + }, + "date-range-navigator" : { + "date-range-picker-settings" : "Indstillinger for datoområdevælger", + "hide-date-range-picker" : "Skjul datoområdevælger", + "picker-one-panel" : "Datoområdevælger med ét panel", + "picker-auto-confirm" : "Datoområdevælger automatisk bekræftelse", + "picker-show-template" : "Vis skabelon for datoområdevælger", + "first-day-of-week" : "Ugens første dag", + "interval-settings" : "Intervalindstillinger", + "hide-interval" : "Skjul interval", + "initial-interval" : "Initialt interval", + "interval-hour" : "Time", + "interval-day" : "Dag", + "interval-week" : "Uge", + "interval-two-weeks" : "2 uger", + "interval-month" : "Måned", + "interval-three-months" : "3 måneder", + "interval-six-months" : "6 måneder", + "step-settings" : "Trinindstillinger", + "hide-step-size" : "Skjul trinlængde", + "initial-step-size" : "Initial trinlængde", + "hide-labels" : "Skjul etiketter", + "use-session-storage" : "Brug sessionslagring", + "localizationMap" : { + "Sun" : "Søn", + "Mon" : "Man", + "Tue" : "Tir", + "Wed" : "Ons", + "Thu" : "Tor", + "Fri" : "Fre", + "Sat" : "Lør", + "Jan" : "Jan", + "Feb" : "Feb", + "Mar" : "Mar", + "Apr" : "Apr", + "May" : "Maj", + "Jun" : "Jun", + "Jul" : "Jul", + "Aug" : "Aug", + "Sep" : "Sep", + "Oct" : "Okt", + "Nov" : "Nov", + "Dec" : "Dec", + "January" : "Januar", + "February" : "Februar", + "March" : "Marts", + "April" : "April", + "June" : "Juni", + "July" : "Juli", + "August" : "August", + "September" : "September", + "October" : "Oktober", + "November" : "November", + "December" : "December", + "Custom Date Range" : "Brugerdefineret datoområde", + "Date Range Template" : "Skabelon for datoområde", + "Today" : "I dag", + "Yesterday" : "I går", + "This Week" : "Denne uge", + "Last Week" : "Sidste uge", + "This Month" : "Denne måned", + "Last Month" : "Sidste måned", + "Year" : "År", + "This Year" : "Dette år", + "Last Year" : "Sidste år", + "Date picker" : "Datovælger", + "Hour" : "Time", + "Day" : "Dag", + "Week" : "Uge", + "2 weeks" : "2 uger", + "Month" : "Måned", + "3 months" : "3 måneder", + "6 months" : "6 måneder", + "Custom interval" : "Brugerdefineret interval", + "Interval" : "Interval", + "Step size" : "Trinstørrelse", + "Ok" : "OK" + } + }, + "doughnut" : { + "doughnut-appearance" : "Udseende af doughnut", + "layout" : "Layout", + "layout-default" : "Standard", + "layout-with-total" : "Med total", + "central-total-value" : "Central totalværdi", + "doughnut-card-style" : "Doughnut kortstil" + }, + "entities-hierarchy" : { + "hierarchy-data-settings" : "Indstillinger for hierarkidata", + "relations-query-function" : "Funktion for forespørgsel på relationer", + "has-children-function" : "Funktion for om noden har børn", + "node-state-settings" : "Indstillinger for node-tilstand", + "node-opened-function" : "Standardfunktion for åben node", + "node-disabled-function" : "Funktion for deaktiveret node", + "display-settings" : "Visningsindstillinger", + "node-icon-function" : "Funktion for nodeikon", + "node-text-function" : "Funktion for nodetekst", + "sort-settings" : "Sorteringsindstillinger", + "nodes-sort-function" : "Funktion for sortering af noder" + }, + "edge" : { + "display-default-title" : "Vis standardtitel" + }, + "gateway" : { + "general-settings" : "Generelle indstillinger", + "widget-title" : "Widget-titel", + "default-archive-file-name" : "Standardnavn for arkivfil", + "device-type-for-new-gateway" : "Enhedstype for ny gateway", + "messages-settings" : "Indstillinger for beskeder", + "save-config-success-message" : "Besked om at gateway-konfigurationen er gemt", + "device-name-exists-message" : "Besked når enhed med angivet navn allerede findes", + "gateway-title" : "Gateway-formular", + "read-only" : "Skrivebeskyttet", + "events-title" : "Titel for gateway-hændelsesformular", + "events-filter" : "Filter for hændelser", + "event-key-contains" : "Hændelsesnøgle indeholder...", + "show-connector" : "Vis for connector", + "connector-state-param-key" : "Parameternøgle for connectorens tilstand", + "message" : "Besked", + "level" : "Niveau", + "created-time" : "Oprettelsestid" + }, + "gauge" : { + "default-color" : "Standardfarve", + "radial-gauge-settings" : "Indstillinger for radialmåler", + "ticks-settings" : "Indstillinger for mærker", + "min-value" : "Minimumværdi", + "max-value" : "Maksimumværdi", + "min-value-short" : "min", + "max-value-short" : "maks", + "start-ticks-angle" : "Startvinkel for mærker", + "ticks-angle" : "Vinkel for mærker", + "major-ticks" : "Primære mærker", + "major-ticks-count" : "Antal primære mærker", + "major-ticks-color" : "Farve på primære mærker", + "minor-ticks" : "Sekundære mærker", + "minor-ticks-count" : "Antal sekundære mærker", + "minor-ticks-color" : "Farve på sekundære mærker", + "tick-numbers-font" : "Skrifttype for mærketal", + "unit-title-settings" : "Indstillinger for enhedstitel", + "show-unit-title" : "Vis enhedstitel", + "unit-title" : "Enhedstitel", + "title-font" : "Skrifttype for titeltekst", + "units-settings" : "Indstillinger for enheder", + "units-font" : "Skrifttype for enhedstekst", + "value-box-settings" : "Indstillinger for værdiboks", + "show-value-box" : "Vis værdiboks", + "value-box" : "Værdiboks", + "value-int" : "Antal cifre i heltalsdelen af værdi", + "value-text" : "Værdi tekst", + "value-text-shadow" : "Skygge for værdi tekst", + "value-font" : "Skrifttype for værdi tekst", + "rect-stroke-color-start" : "Startfarve for rektangelkontur", + "rect-stroke-color-end" : "Slutfarve for rektangelkontur", + "background-color" : "Baggrundsfarve", + "shadow-color" : "Skyggefarve", + "value-box-rect-stroke-color" : "Konturfarve for værdiboks", + "value-box-rect-stroke-color-end" : "Slutfarve for værdiboksens kontur", + "value-box-background-color" : "Baggrundsfarve for værdiboks", + "value-box-shadow-color" : "Skyggefarve for værdiboks", + "plate-settings" : "Pladeindstillinger", + "show-plate-border" : "Vis pladekant", + "plate-color" : "Pladefarve", + "needle-settings" : "Indstillinger for nål", + "needle-circle-size" : "Størrelse på nålcirkel", + "needle-color" : "Nålfarve", + "needle-color-start" : "Startfarve for nål", + "needle-color-end" : "Slutfarve for nål", + "needle-color-shadow-up" : "Skyggefarve for øverste nål", + "needle-color-shadow-down" : "Skyggefarve (nederst)", + "highlights-settings" : "Indstillinger for fremhævning", + "highlights-width" : "Bredde på fremhævning", + "highlights" : "Fremhævninger", + "highlight-from" : "Fra", + "highlight-to" : "Til", + "highlight-color" : "Farve", + "no-highlights" : "Ingen fremhævninger konfigureret", + "add-highlight" : "Tilføj fremhævning", + "animation-settings" : "Indstillinger for animation", + "enable-animation" : "Animation", + "animation-duration-rule" : "Varighed og regel for animation", + "animation-duration" : "Varighed af animation", + "animation-rule" : "Animationsregel", + "animation-linear" : "Lineær", + "animation-quad" : "Kvadratisk", + "animation-quint" : "Kvintisk", + "animation-cycle" : "Cykel", + "animation-bounce" : "Hop", + "animation-elastic" : "Elastisk", + "animation-dequad" : "De-kvadratisk", + "animation-dequint" : "De-kvintisk", + "animation-decycle" : "De-cykel", + "animation-debounce" : "De-hop", + "animation-delastic" : "De-elastisk", + "linear-gauge-settings" : "Indstillinger for lineær måler", + "bar-stroke" : "Streg på bjælke", + "bar-stroke-width" : "Stregbredde på bjælke", + "bar-stroke-color" : "Stregfarve på bjælke", + "bar-background-color" : "Baggrundsfarve på bjælke - start", + "bar-background-color-end" : "Baggrundsfarve på bjælke - slut", + "progress-bar-color" : "Fremskridtsbjælkens farve", + "progress-bar" : "Fremskridtsbjælke", + "progress-bar-color-start" : "Startfarve på fremskridtsbjælke", + "progress-bar-color-end" : "Slutfarve på fremskridtsbjælke", + "major-ticks-names" : "Navne på primære mærker", + "show-stroke-ticks" : "Vis mærkestreg", + "major-ticks-font" : "Skrifttype for primære mærker", + "border-color" : "Kantfarve", + "border-width" : "Kantbredde", + "needle-circle" : "Nål cirkel", + "needle-circle-color" : "Nålcirklens farve", + "animation-target" : "Animationsmål", + "animation-target-needle" : "Nål", + "animation-target-plate" : "Plade", + "common-settings" : "Almindelige målerindstillinger", + "gauge-type" : "Målertype", + "gauge-type-arc" : "Bue", + "gauge-type-donut" : "Donut", + "gauge-type-horizontal-bar" : "Vandret bjælke", + "gauge-type-vertical-bar" : "Lodret bjælke", + "donut-start-angle" : "Startvinkel (grader)", + "bar-settings" : "Bjælkeindstillinger for måler", + "relative-bar-width" : "Relativ bjælkebredde", + "neon-glow-brightness" : "Lysstyrke for neon-glødeeffekt (0-100)", + "neon-glow-brightness-hint" : "0 - deaktiver effekt", + "stripes-thickness" : "Stribebredde", + "stripes-thickness-hint" : "0 - ingen striber", + "rounded-line-cap" : "Afrundet linjekant", + "bar-color-settings" : "Farveindstillinger for bjælke", + "use-precise-level-color-values" : "Brug præcise farveniveauer", + "bar-colors" : "Bjælkefarver, fra lav til høj", + "color" : "Farve", + "no-bar-colors" : "Ingen bjælkefarver konfigureret", + "add-bar-color" : "Tilføj bjælkefarve", + "from" : "Fra", + "to" : "Til", + "fixed-level-colors" : "Bjælkefarver baseret på grænseværdier", + "gauge-title-settings" : "Indstillinger for målertitel", + "show-gauge-title" : "Vis målertitel", + "gauge-title" : "Målertitel", + "gauge-title-font" : "Skrifttype for målertitel", + "unit-title-and-timestamp-settings" : "Indstillinger for enhedstitel og tidsstempel", + "show-timestamp" : "Tidsstempel", + "timestamp-format" : "Tidsstempelformat", + "label-font" : "Skrifttype for etikette under værdi", + "value-settings" : "Værdiindstillinger", + "show-value" : "Vis værditekst", + "min-max-settings" : "Indstillinger for min./maks.-etiketter", + "show-min-max" : "Vis min. og maks.-værdier", + "min-max-font" : "Skrifttype for min. og maks.-etiketter", + "show-ticks" : "Vis mærker", + "tick-width" : "Bredde på mærker", + "tick-color" : "Mærkefarve", + "tick-values" : "Mærkeværdier", + "no-tick-values" : "Ingen mærkeværdier konfigureret", + "add-tick-value" : "Tilføj mærkeværdi", + "gauge-appearance" : "Målerudseende", + "units-title" : "Enhedstitel", + "value" : "Værdi", + "ticks" : "Mærker", + "arrow-and-scale-color" : "Standardfarve for pil og skala", + "scale-settings" : "Skalaindstillinger", + "scale" : "Skala", + "scale-color" : "Skalafarver", + "compass-appearance" : "Kompasudseende", + "label" : "Etiket", + "labels" : "Etiketter", + "label-style" : "Etiketstil", + "simple-gauge-type" : "Type", + "gauge-bar-background" : "Baggrund for målerbjælke", + "bar-color" : "Bjælkefarve", + "min-and-max-value" : "Min. og maks.-værdi", + "min-and-max-label" : "Min. og maks.-etiket", + "font" : "Skrifttype", + "tick-width-and-color" : "Bredde og farve på mærker", + "min-max-validation-text" : "Maks.-værdi skal være større end min.-værdi" + }, + "gpio" : { + "pin" : "Pin", + "label" : "Etiket", + "row" : "Række", + "column" : "Kolonne", + "color" : "Farve", + "panel-settings" : "Panelindstillinger", + "background-color" : "Baggrundsfarve", + "gpio-switches" : "GPIO-kontakter", + "no-gpio-switches" : "Ingen GPIO-kontakter konfigureret", + "add-gpio-switch" : "Tilføj GPIO-kontakt", + "gpio-status-request" : "GPIO-statusanmodning", + "method-name" : "Metodenavn", + "method-body" : "Metodeindhold", + "gpio-status-change-request" : "Anmodning om ændring af GPIO-status", + "parse-gpio-status-function" : "Funktion til at fortolke GPIO-status", + "gpio-leds" : "GPIO-LED'er", + "no-gpio-leds" : "Ingen GPIO-LED'er konfigureret", + "add-gpio-led" : "Tilføj GPIO-LED" + }, + "html-card" : { + "html" : "HTML", + "css" : "CSS" + }, + "input-widgets" : { + "attribute-not-allowed" : "Attributparameter må ikke bruges i denne widget", + "blocked-location" : "Geolokation er blokeret i din browser", + "claim-device" : "Tag enhed i brug", + "claim-failed" : "Det mislykkedes at tage enheden i brug!", + "claim-not-found" : "Enhed ikke fundet!", + "claim-successful" : "Enheden blev taget i brug med succes!", + "date" : "Dato", + "device-name" : "Enhedsnavn", + "device-name-required" : "Enhedsnavn er påkrævet", + "discard-changes" : "Kassér ændringer", + "entity-attribute-required" : "Enhedsattribut er påkrævet", + "entity-coordinate-required" : "Begge felter, breddegrad og længdegrad, er påkrævet", + "entity-timeseries-required" : "Enhedens tidsserie er påkrævet", + "get-location" : "Hent nuværende placering", + "invalid-date" : "Ugyldig dato", + "latitude" : "Breddegrad", + "longitude" : "Længdegrad", + "min-value-error" : "Minimumsværdi er {{value}}", + "max-value-error" : "Maksimumsværdi er {{value}}", + "not-allowed-entity" : "Valgt enhed må ikke have delte attributter", + "no-attribute-selected" : "Ingen attribut er valgt", + "no-datakey-selected" : "Ingen datanøgle er valgt", + "no-coordinate-specified" : "Datanøgle for bredde-/længdegrad er ikke angivet", + "no-entity-selected" : "Ingen enhed valgt", + "no-image" : "Ingen billede", + "no-support-geolocation" : "Din browser understøtter ikke geolokation", + "no-support-web-camera" : "Din browser understøtter ikke kameraer", + "enable-https-use-widget" : "Aktivér HTTPS for at bruge denne widget", + "no-found-your-camera" : "Kan ikke finde dit kamera", + "no-permission-camera" : "Tilladelse blev nægtet af brugeren / Denne side har ikke tilladelse til at bruge kameraet", + "no-timeseries-selected" : "Ingen tidsserie valgt", + "secret-key" : "Hemmelig nøgle", + "secret-key-required" : "Hemmelig nøgle er påkrævet", + "switch-attribute-value" : "Skift enhedsattributværdi", + "switch-camera" : "Skift kamera", + "switch-timeseries-value" : "Skift enhedens tidsserie-værdi", + "take-photo" : "Tag billede", + "time" : "Tid", + "timeseries-not-allowed" : "Tidsserieparameter må ikke bruges i denne widget", + "update-failed" : "Opdatering mislykkedes", + "update-successful" : "Opdatering gennemført", + "update-attribute" : "Opdater attribut", + "update-timeseries" : "Opdater tidsserie", + "value" : "Værdi", + "general-settings" : "Generelle indstillinger", + "widget-title" : "Widgettitel", + "claim-button-label" : "Etiket for brug-knap", + "show-secret-key-field" : "Vis inputfelt for 'Hemmelig nøgle'", + "labels-settings" : "Etiketindstillinger", + "show-labels" : "Vis etiketter", + "device-name-label" : "Etiket for feltet 'Enhedsnavn'", + "secret-key-label" : "Etiket for feltet 'Hemmelig nøgle'", + "messages-settings" : "Beskedindstillinger", + "claim-device-success-message" : "Tekstbesked ved succesfuld enhedsbrug", + "claim-device-not-found-message" : "Tekstbesked når enhed ikke findes", + "claim-device-failed-message" : "Tekstbesked ved fejlslagen enhedsbrug", + "claim-device-name-required-message" : "Fejlbesked: 'Enhedsnavn er påkrævet'", + "claim-device-secret-key-required-message" : "Fejlbesked: 'Hemmelig nøgle er påkrævet'", + "show-label" : "Vis etiket", + "label" : "Etiket", + "required" : "Påkrævet", + "required-error-message" : "Fejlbesked: 'Påkrævet'", + "show-result-message" : "Vis resultatbesked", + "integer-field-settings" : "Indstillinger for heltalsfelt", + "min-value" : "Minimumsværdi", + "max-value" : "Maksimumsværdi", + "double-field-settings" : "Indstillinger for decimaltalsfelt", + "text-field-settings" : "Indstillinger for tekstfelt", + "min-length" : "Minimumslængde", + "max-length" : "Maksimumslængde", + "checkbox-settings" : "Afkrydsningsfeltindstillinger", + "true-label" : "Etiket når markeret", + "false-label" : "Etiket når ikke markeret", + "image-input-settings" : "Indstillinger for billedinput", + "display-preview" : "Vis forhåndsvisning", + "display-clear-button" : "Vis 'Ryd'-knap", + "display-apply-button" : "Vis 'Anvend'-knap", + "display-discard-button" : "Vis 'Kassér'-knap", + "datetime-field-settings" : "Indstillinger for dato/tid-felt", + "display-time-input" : "Vis tid-input", + "latitude-key-name" : "Navn på breddegradsnøgle", + "longitude-key-name" : "Navn på længdegradsnøgle", + "show-get-location-button" : "Vis knappen 'Hent nuværende placering'", + "use-high-accuracy" : "Brug høj præcision", + "location-fields-settings" : "Indstillinger for lokationsfelter", + "latitude-label" : "Etiket for breddegrad", + "longitude-label" : "Etiket for længdegrad", + "input-fields-alignment" : "Justering af inputfelter", + "input-fields-alignment-column" : "Kolonne (standard)", + "input-fields-alignment-row" : "Række", + "layout" : "Layout", + "row-gap" : "Afstand mellem rækker i pixels", + "column-gap" : "Afstand mellem kolonner i pixels", + "latitude-field-required" : "Breddegradsfelt påkrævet", + "longitude-field-required" : "Længdegradsfelt påkrævet", + "attribute-settings" : "Attributindstillinger", + "widget-mode" : "Widgettilstand", + "widget-mode-update-attribute" : "Opdater attribut", + "widget-mode-update-timeseries" : "Opdater tidsserie", + "attribute-scope" : "Attributområde", + "attribute-scope-server" : "Serverattribut", + "attribute-scope-shared" : "Delt attribut", + "value-required" : "Værdi er påkrævet", + "image-settings" : "Billedindstillinger", + "image-format" : "Billedformat", + "image-format-jpeg" : "JPEG", + "image-format-png" : "PNG", + "image-format-webp" : "WEBP", + "image-quality" : "Billedkvalitet der bruger tabsbaseret komprimering som JPEG og WEBP", + "max-image-width" : "Maksimal billedbredde", + "max-image-height" : "Maksimal billedhøjde", + "action-buttons" : "Handlingsknapper", + "show-action-buttons" : "Vis handlingsknapper", + "update-all-values" : "Opdater alle værdier, ikke kun de ændrede", + "save-button-label" : "Etiket for 'GEM'-knap", + "reset-button-label" : "Etiket for 'FORTRYD'-knap", + "group-settings" : "Gruppeindstillinger", + "show-group-title" : "Vis titel for gruppe af felter relateret til forskellige enheder", + "group-title" : "Gruppetitel", + "fields-alignment" : "Justering af felter", + "fields-alignment-row" : "Række (standard)", + "fields-alignment-column" : "Kolonne", + "fields-in-row" : "Antal felter i rækken", + "option-value" : "Værdi (skriv 'null' for at oprette tom mulighed)", + "option-label" : "Etiket", + "hide-input-field" : "Skjul inputfelt", + "datakey-type" : "Datanøgletype", + "datakey-type-server" : "Serverattribut (standard)", + "datakey-type-shared" : "Delt attribut", + "datakey-type-timeseries" : "Tidsserie", + "datakey-value-type" : "Datanøgleværdi-type", + "datakey-value-type-string" : "Streng", + "datakey-value-type-double" : "Decimal", + "datakey-value-type-integer" : "Heltal", + "datakey-value-type-json" : "JSON", + "datakey-value-type-boolean-checkbox" : "Boolesk (Afkrydsningsfelt)", + "datakey-value-type-boolean-switch" : "Boolesk (Skift)", + "datakey-value-type-date-time" : "Dato og tid", + "datakey-value-type-date" : "Dato", + "datakey-value-type-time" : "Tid", + "datakey-value-type-select" : "Vælg", + "datakey-value-type-radio" : "Radioknap", + "datakey-value-type-color" : "Farve", + "value-is-required" : "Værdi er påkrævet", + "ability-to-edit-attribute" : "Mulighed for at redigere attribut", + "ability-to-edit-attribute-editable" : "Redigerbar (standard)", + "ability-to-edit-attribute-disabled" : "Deaktiveret", + "ability-to-edit-attribute-readonly" : "Skrivebeskyttet", + "disable-on-datakey-name" : "Deaktiver baseret på falsk værdi fra anden datanøgle (angiv navn)", + "field-appearance" : "Feltets udseende", + "appearance-fill" : "Fyld", + "appearance-outline" : "Kantlinje", + "subscript-sizing" : "Størrelse på undertekst", + "subscript-sizing-fixed" : "Fast", + "subscript-sizing-dynamic" : "Dynamisk", + "slide-toggle-settings" : "Indstillinger for skiftekontakt", + "slide-toggle-label-position" : "Etiketplacering for skiftekontakt", + "slide-toggle-label-position-after" : "Efter", + "slide-toggle-label-position-before" : "Før", + "select-options" : "Valgmuligheder", + "no-select-options" : "Ingen valgmuligheder konfigureret", + "add-select-option" : "Tilføj valgmulighed", + "numeric-field-settings" : "Indstillinger for numerisk felt", + "step-interval" : "Trininterval mellem værdier", + "error-messages" : "Fejlmeddelelser", + "min-value-error-message" : "Fejlmeddelelse: 'Minimumsværdi'", + "max-value-error-message" : "Fejlmeddelelse: 'Maksimumsværdi'", + "invalid-date-error-message" : "Fejlmeddelelse: 'Ugyldig dato'", + "invalid-JSON-error-message" : "Fejlmeddelelse: 'Ugyldig JSON'", + "icon-settings" : "Ikonindstillinger", + "dialog-editor-settings" : "Dialogeditor-indstillinger", + "use-custom-icon" : "Brug brugerdefineret ikon", + "input-cell-icon" : "Ikon vist før inputfelt", + "value-conversion-settings" : "Indstillinger for værdikonvertering", + "get-value-settings" : "Indstillinger for at hente værdi", + "use-get-value-function" : "Brug getValue-funktion", + "get-value-function" : "getValue-funktion", + "set-value-settings" : "Indstillinger for at sætte værdi", + "use-set-value-function" : "Brug setValue-funktion", + "set-value-function" : "setValue-funktion", + "json-invalid" : "JSON-værdi har et ugyldigt format", + "title" : "Titel", + "cancel-button-label" : "Etiket for 'Annuller'-knap", + "radio-button-settings" : "Indstillinger for radioknap", + "color" : "Farve", + "columns" : "Kolonner", + "radio-options" : "Radiomuligheder", + "no-radio-options" : "Ingen radiomuligheder konfigureret", + "add-radio-option" : "Tilføj radiomulighed", + "radio-label-position" : "Etiketposition", + "radio-label-position-before" : "Før", + "radio-label-position-after" : "Efter" + }, + "invalid-qr-code-text" : "Ugyldig inputtekst til QR-kode. Input skal være af typen streng", + "qr-code" : { + "use-qr-code-text-function" : "Brug QR-kodetekstfunktion", + "qr-code-text-pattern" : "QR-kodetekstmønster (f.eks. '${entityName} | ${keyName} - noget tekst.')", + "qr-code-text-pattern-hint" : "QR-kodetekstmønster bruger værdien af den første fundne nøgle i enhederne i enhedsaliasset.", + "qr-code-text-pattern-required" : "QR-kodetekstmønster er påkrævet.", + "qr-code-text-function" : "QR-kodetekstfunktion" + }, + "label-widget" : { + "label-pattern" : "Mønster", + "label-pattern-hint" : "Tip: f.eks. 'Tekst ${keyName} enheder.' eller ${#<key index>} enheder'", + "label-pattern-required" : "Mønster er påkrævet", + "label-position" : "Position (procent relativt til baggrund)", + "x-pos" : "X", + "y-pos" : "Y", + "background-color" : "Baggrundsfarve", + "font-settings" : "Skrifttypeindstillinger", + "background-image" : "Baggrundsbillede", + "labels" : "Etiketter", + "no-labels" : "Ingen etiketter konfigureret", + "add-label" : "Tilføj etiket" + }, + "navigation" : { + "title" : "Titel", + "navigation-path" : "Navigationssti", + "filter-type" : "Filtertype", + "filter-type-all" : "Alle elementer", + "filter-type-include" : "Inkludér elementer", + "filter-type-exclude" : "Ekskludér elementer", + "items" : "Elementer", + "enter-urls-to-filter" : "Indtast URL'er der skal filtreres..." + }, + "persistent-table" : { + "rpc-id" : "RPC ID", + "message-type" : "Meddelelsestype", + "method" : "Metode", + "params" : "Parametre", + "created-time" : "Oprettelsestidspunkt", + "expiration-time" : "Udløbstidspunkt", + "retries" : "Gentagelser", + "status" : "Status", + "filter" : "Filter", + "refresh" : "Opdater", + "add" : "Tilføj RPC-anmodning", + "details" : "Detaljer", + "delete" : "Slet", + "delete-request-title" : "Slet Persistent RPC-anmodning", + "delete-request-text" : "Er du sikker på, at du vil slette anmodningen?", + "details-title" : "Detaljer RPC ID: ", + "additional-info" : "Yderligere oplysninger", + "response" : "Svar", + "any-status" : "Enhver status", + "rpc-status-list" : "RPC-statusliste", + "no-request-prompt" : "Ingen anmodninger at vise", + "send-request" : "Send anmodning", + "add-title" : "Opret Persistent RPC-anmodning", + "method-error" : "Metode er påkrævet.", + "timeout-error" : "Minimum timeoutværdi er 5000 (5 sekunder).", + "white-space-error" : "Mellemrum er ikke tilladt.", + "rpc-status" : { + "QUEUED" : "I KØ", + "SENT" : "SENDT", + "DELIVERED" : "LEVERET", + "SUCCESSFUL" : "LYKKEDES", + "TIMEOUT" : "TIDSUDLØB", + "EXPIRED" : "UDLØBET", + "FAILED" : "FEJLET" + }, + "rpc-search-status-all" : "ALLE", + "message-types" : { + "false" : "Tovejs", + "true" : "Envejs" + }, + "general-settings" : "Generelle indstillinger", + "enable-filter" : "Aktivér filter", + "enable-sticky-header" : "Vis overskrift under rulning", + "enable-sticky-action" : "Vis handlingskolonne under rulning", + "display-request-details" : "Vis anmodningsdetaljer", + "allow-send-request" : "Tillad RPC-anmodning", + "allow-delete-request" : "Tillad sletning af anmodning", + "columns-settings" : "Kolonneindstillinger", + "display-columns" : "Kolonner der vises", + "column" : "Kolonne", + "no-columns-found" : "Ingen kolonner fundet", + "no-columns-matching" : "'{{column}}' blev ikke fundet." + }, + "range-chart" : { + "chart" : "Diagram", + "data-zoom" : "Datazoom", + "range-chart-appearance" : "Udseende for område-diagram", + "range-colors" : "Områdefarver", + "out-of-range-color" : "Farve uden for området", + "show-range-thresholds" : "Vis områdegrænser", + "range-thresholds-settings" : "Indstillinger for områdegrænser", + "fill-area" : "Udfyld område", + "fill-area-opacity" : "Gennemsigtighed for områdeudfyldning", + "range-chart-style" : "Stil for område-diagram" + }, + "rpc" : { + "value-settings" : "Indstillinger for værdi", + "initial-value" : "Startværdi", + "retrieve-value-settings" : "Indstillinger for hentning af tænd/sluk værdi", + "retrieve-value-method" : "Hent værdi via metode", + "retrieve-value-method-none" : "Hent ikke", + "retrieve-value-method-rpc" : "Kald RPC get-værdimetode", + "retrieve-value-method-attribute" : "Abonner på attribut", + "retrieve-value-method-timeseries" : "Abonner på tidsserier", + "attribute-value-key" : "Attributnøgle", + "timeseries-value-key" : "Tidsserienøgle", + "get-value-method" : "RPC get-værdimetode", + "parse-value-function" : "Fortolk værdi-funktion", + "update-value-settings" : "Opdater værdi-indstillinger", + "set-value-method" : "RPC set-værdimetode", + "convert-value-function" : "Konverter værdi-funktion", + "rpc-settings" : "RPC-indstillinger", + "request-timeout" : "RPC timeout for forespørgsel (ms)", + "persistent-rpc-settings" : "Indstillinger for persistent RPC", + "request-persistent" : "RPC-forespørgsel er persistent", + "persistent-polling-interval" : "Polling-interval (ms) for at hente svar på persistent RPC-kommando", + "common-settings" : "Fælles indstillinger", + "switch-title" : "Titel for kontakt", + "show-on-off-labels" : "Vis tænd/sluk-mærkater", + "slide-toggle-label" : "Glidemærkat", + "label-position" : "Etiketposition", + "label-position-before" : "Før", + "label-position-after" : "Efter", + "slider-color" : "Skydefarve", + "slider-color-primary" : "Primær", + "slider-color-accent" : "Accent", + "slider-color-warn" : "Advarsel", + "button-style" : "Knapstil", + "button-raised" : "Hævet knap", + "button-primary" : "Primær farve", + "button-background-color" : "Baggrundsfarve på knap", + "button-text-color" : "Tekstfarve på knap", + "widget-title" : "Widgettitel", + "button-label" : "Knaptekst", + "device-attribute-scope" : "Omfang for enhedsattribut", + "server-attribute" : "Serverattribut", + "shared-attribute" : "Delt attribut", + "device-attribute-parameters" : "Parametre for enhedsattribut", + "is-one-way-command" : "Er envejs-kommando", + "rpc-method" : "RPC-metode", + "rpc-method-params" : "Parametre for RPC-metode", + "show-rpc-error" : "Vis RPC-kommandofejl", + "led-title" : "LED-titel", + "led-color" : "LED-farve", + "check-status-settings" : "Indstillinger for statuskontrol", + "perform-rpc-status-check" : "Udfør statuskontrol via RPC", + "retrieve-led-status-value-method" : "Hent LED-statusværdi via metode", + "led-status-value-attribute" : "Enhedsattribut der indeholder LED-statusværdi", + "led-status-value-timeseries" : "Enheds tidsserie der indeholder LED-statusværdi", + "check-status-method" : "RPC-metode til kontrol af enhedsstatus", + "parse-led-status-value-function" : "Fortolk LED-statusværdi-funktion", + "knob-title" : "Titel for knap", + "min-value" : "Minimumværdi", + "max-value" : "Maksimumværdi" + }, + "maps" : { + "map-type" : { + "type" : "Korttype", + "map" : "Kort", + "image" : "Billede" + }, + "image" : { + "image-source" : "Billedkilde", + "image-source-image" : "Billede", + "image-source-entity-key" : "Enhedsnøgle", + "source-entity-alias" : "Kildeenhedsalias", + "image-url-key" : "Billede-URL-nøgle", + "image-url-key-required" : "Billede-URL-nøgle er påkrævet" + }, + "control" : { + "map-controls" : "Kortkontroller", + "position" : "Position", + "position-topleft" : "Øverst til venstre", + "position-topright" : "Øverst til højre", + "position-bottomleft" : "Nederst til venstre", + "position-bottomright" : "Nederst til højre", + "zoom-actions" : "Zoomhandlinger", + "zoom-scroll" : "Rul", + "zoom-double-click" : "Dobbeltklik", + "zoom-control-buttons" : "Kontrolknapper", + "scale" : "Skala", + "scale-metric" : "Metrisk", + "scale-imperial" : "Imperial", + "switch-to-drag-mode-using-button" : "Skift til træktilstand med knap" + }, + "timeline" : { + "control-panel" : "Tidslinjekontrolpanel", + "time-step" : "Tidsinterval", + "speed-options" : "Hastighedsmuligheder", + "timestamp" : "Tidsstempel", + "snap-to-real-location" : "Fastgør til reel position", + "location-snap-filter-function" : "Filterfunktion for positionsfastgørelse", + "no-trips-data-available" : "Ingen rejseoplysninger tilgængelige" + }, + "map-action" : { + "map-action-buttons" : "Handlingsknapper på kortet", + "label" : "Etiket", + "icon" : "Ikon", + "color" : "Farve", + "action" : "Handling", + "add-button" : "Tilføj knap", + "no-action-buttons-configured" : "Ingen handlingsknapper konfigureret", + "remove-action-button" : "Fjern handlingsknap", + "map-action-button" : "Handlingsknap på kortet", + "button-requires" : "Knap kræver etiket eller ikon" + }, + "common" : { + "common-map-settings" : "Almindelige kortindstillinger", + "fit-map-bounds" : "Tilpas kortgrænser til at inkludere alle markører", + "default-map-center-position" : "Standardcenterposition for kort", + "default-map-zoom-level" : "Standardzoomniveau for kort", + "entities-limit" : "Grænse for antal enheder der skal indlæses" + }, + "layer" : { + "label" : "Etiket", + "layer" : "Lag", + "layers" : "Lag", + "map-layers" : "Kortlag", + "add-layer" : "Tilføj lag", + "layer-settings" : "Lagindstillinger", + "remove-layer" : "Fjern lag", + "no-layers" : "Ingen lag konfigureret", + "roadmap" : "Vejkort", + "satellite" : "Satellit", + "hybrid" : "Hybrid", + "reference" : { + "reference-layer" : "Referencelag", + "no-layer" : "Intet lag", + "openstreetmap-hybrid" : "OpenStreetMap Hybrid", + "world-edition-hybrid" : "World Edition Hybrid", + "enhanced-contrast-hybrid" : "Forbedret kontrast Hybrid" + }, + "provider" : { + "provider" : "Udbyder", + "openstreet" : { + "title" : "OpenStreet", + "mapnik" : "Mapnik", + "hot" : "HOT", + "esri-street" : "WorldStreetMap", + "esri-topo" : "WorldTopoMap", + "esri-imagery" : "WorldImagery", + "cartodb-positron" : "Positron", + "cartodb-dark-matter" : "DarkMatter" + }, + "google" : { + "title" : "Google", + "roadmap" : "Vejkort", + "satellite" : "Satellit", + "hybrid" : "Hybrid", + "terrain" : "Terræn" + }, + "here" : { + "title" : "HERE", + "normal-day" : "Normal dag", + "normal-night" : "Normal nat", + "hybrid-day" : "Hybrid dag", + "terrain-day" : "Terræn dag" + }, + "tencent" : { + "title" : "Tencent", + "normal" : "Normal", + "satellite" : "Satellit", + "terrain" : "Terræn" + }, + "custom" : { + "title" : "Brugerdefineret", + "tile-url" : "Flise-URL" + } + }, + "credentials" : { + "credentials" : "Legitimationsoplysninger", + "api-key" : "API-nøgle" + } + }, + "overlays" : { + "overlays" : "Overlays", + "overlays-hint" : "Konfigurer datakilder, udseende, adfærd, redigeringsmuligheder og gruppering for kortenheder", + "trips" : "Rejser", + "markers" : "Markører", + "polygons" : "Polygoner", + "circles" : "Cirkler" + }, + "data-layer" : { + "source" : "Kilde", + "filter" : "Filter", + "additional-data-keys" : "Yderligere datanøgler", + "additional-datasources" : "Yderligere datakilder", + "additional-datasources-hint" : "Datakilde til adgang til attributter eller telemetri fra enheder, der ikke vises på kortet, anvendelig i kortoverlay-funktioner.", + "more-datasources" : "Flere datakilder", + "data-keys" : "Datanøgler", + "add-datasource" : "Tilføj datakilde", + "no-datasources" : "Ingen datakilder konfigureret", + "remove-datasource" : "Fjern datakilde", + "behavior" : "Adfærd", + "on-click" : "Ved klik", + "on-click-hint" : "Handling udført når brugeren klikker på kortelementet.", + "groups" : "Grupper", + "groups-hint" : "Liste over gruppenavne tildelt overlayet, bruges til at skifte synlighed på kortet.", + "color" : "Farve", + "color-settings" : "Farveindstillinger", + "color-type-constant" : "Konstant", + "color-type-range" : "Interval", + "color-type-function" : "Funktion", + "color-range-source-key" : "Kilde nøgle for farveinterval", + "color-range-source-key-required" : "Kilde nøgle for farveinterval er påkrævet", + "color-range" : "Farveinterval", + "color-function" : "Farvefunktion", + "label" : "Etiket", + "tooltip" : "Værktøjstip", + "pattern-type-pattern" : "Mønster", + "pattern-type-function" : "Funktion", + "label-pattern" : "Etiket (eksempel: '${entityName}', '${entityName}: (Tekst ${keyName} enheder.)')", + "label-function" : "Etiketfunktion", + "tooltip-pattern" : "Værktøjstip (for eksempel 'Tekst ${keyName} enheder.' eller Linktekst)", + "tooltip-function" : "Værktøjstipfunktion", + "tooltip-trigger" : "Udløser for værktøjstip", + "tooltip-trigger-click" : "Vis værktøjstip ved klik", + "tooltip-trigger-hover" : "Vis værktøjstip ved hover", + "auto-close-tooltips" : "Luk værktøjstip automatisk", + "tooltip-offset" : "Forskydning af værktøjstip", + "tooltip-offset-horizontal" : "Vandret", + "tooltip-offset-vertical" : "Lodret", + "tooltip-tag-actions" : "Mærkehandlinger", + "add-tooltip-tag-action" : "Tilføj mærkehandling", + "edit-tooltip-tag-action" : "Rediger mærkehandling", + "remove-tooltip-tag-action" : "Fjern mærkehandling", + "action-add" : "Tilføj", + "action-edit" : "Rediger", + "action-move" : "Flyt", + "action-remove" : "Fjern", + "edit-instruments" : "Redigeringsværktøjer", + "persist-location-attribute-scope" : "Omfang for attribut til lagring af placering", + "enable-snapping" : "Aktiver tilpasning til andre hjørner for præcis tegning", + "enable-snapping-hint" : "Tilpasser automatisk nye punkter med eksisterende former for nemmere og mere præcis tegning.", + "drag-drop-mode" : "Træk og slip tilstand", + "trip" : { + "no-trips" : "Ingen rejser konfigureret", + "add-trip" : "Tilføj rejse", + "trip-configuration" : "Rejsekonfiguration", + "remove-trip" : "Fjern rejse" + }, + "marker" : { + "marker" : "Markør", + "latitude-key" : "Breddegrad nøgle", + "longitude-key" : "Længdegrad nøgle", + "x-pos-key" : "X-position nøgle", + "y-pos-key" : "Y-position nøgle", + "latitude-key-required" : "Breddegrad nøgle er påkrævet", + "longitude-key-required" : "Længdegrad nøgle er påkrævet", + "x-pos-key-required" : "X-position nøgle er påkrævet", + "y-pos-key-required" : "Y-position nøgle er påkrævet", + "no-markers" : "Ingen markører konfigureret", + "add-marker" : "Tilføj markør", + "marker-configuration" : "Markørkonfiguration", + "remove-marker" : "Fjern markør", + "marker-type" : "Markørtype", + "marker-type-shape" : "Form", + "marker-type-icon" : "Ikon", + "marker-type-image" : "Billede", + "shape" : "Form", + "icon" : "Ikon", + "image" : "Billede", + "marker-shapes" : "Markørformer", + "marker-icon" : "Markørikon", + "marker-appearance" : "Markørens udseende", + "marker-image" : "Markørbillede", + "marker-image-type-image" : "Billede", + "marker-image-type-function" : "Funktion", + "custom-marker-image-size" : "Tilpasset størrelse på markørbillede", + "marker-image-function" : "Markørbilledefunktion", + "marker-images" : "Markørbilleder", + "marker-offset" : "Markørforskydning", + "offset-horizontal" : "Vandret", + "offset-vertical" : "Lodret", + "rotate-marker" : "Rotér markør", + "offset-angle" : "Forskydningsvinkel", + "position-conversion" : "Positionskonvertering", + "position-conversion-function" : "Positionskonverteringsfunktion, skal returnere x,y koordinater som double fra 0 til 1", + "clustering" : { + "use-map-markers-clustering" : "Brug kortmarkørklyngning", + "zoom-on-cluster-click" : "Zoom ved klik på klynge", + "max-zoom" : "Maksimalt zoomniveau hvor markør kan være en del af klynge (0 - 18)", + "max-radius" : "Maksimal radius for en klynge", + "zoom-animation" : "Animation på markører ved zoom", + "bounds-on-cluster-mouse-over" : "Grænser for markører ved mus over klynge", + "spiderfy-max-zoom-level" : "Udvid ved maksimal zoomniveau (for at se alle markører)", + "load-optimization" : "Indlæsningsoptimering", + "chunked-load" : "Brug opdeling til indlæsning af markører for at undgå frysning", + "lazy-load" : "Brug lazy load til tilføjelse af markører", + "use-cluster-marker-color-function" : "Brug farvefunktion for klyngemarkører", + "marker-color-function" : "Markørfarvefunktion" + }, + "edit" : "Rediger markør", + "remove-marker-for" : "Fjern markør for '{{entityName}}'", + "place-marker" : "Placer markør", + "place-marker-hint" : "Klik for at placere markør", + "place-marker-hint-with-entity" : "Klik for at placere enhed '{{entityName}}'" + }, + "path" : { + "path" : "Sti", + "path-decorator" : "Sti dekoration", + "decorator-symbol" : "Dekorationssymbol", + "decorator-symbol-arrow-head" : "Pil", + "decorator-symbol-dash" : "Streg", + "decorator-arrangement" : "Dekorationsarrangement", + "decorator-offset" : "Start", + "decorator-end-offset" : "Slut", + "decorator-repeat" : "Gentag" + }, + "points" : { + "points" : "Punkter", + "point-tooltip" : "Punktværktøjstip" + }, + "shape" : { + "fill" : "Fyld", + "fill-type-color" : "Farve", + "fill-type-stripe" : "Stribe", + "fill-type-image" : "Billede", + "color" : "Farve", + "stripe" : "Stribe", + "image" : "Billede", + "stroke" : "Streg", + "fill-image" : "Fyldbillede", + "fill-image-type-image" : "Billede", + "fill-image-type-function" : "Funktion", + "preserve-aspect-ratio" : "Bevar billedformat", + "opacity" : "Gennemsigtighed", + "angle" : "Rotationsvinkel", + "scale" : "Skalering", + "fill-image-function" : "Form fyldbilledfunktion", + "fill-images" : "Form fyldbilleder", + "stripe-pattern" : "Stribemønster", + "first-stripe" : "Første stribe", + "second-stripe" : "Anden stribe" + }, + "polygon" : { + "polygon-key" : "Polygonnøgle", + "polygon-key-required" : "Polygonnøgle er påkrævet", + "no-polygons" : "Ingen polygoner konfigureret", + "add-polygon" : "Tilføj polygon", + "polygon-configuration" : "Polygonkonfiguration", + "remove-polygon" : "Fjern polygon", + "edit" : "Rediger polygon", + "remove-polygon-for" : "Fjern polygon for '{{entityName}}'", + "cut" : "Klip polygonområde", + "rotate" : "Rotér polygon", + "draw-rectangle" : "Tegn rektangel", + "draw-polygon" : "Tegn polygon", + "polygon-place-first-point-cut-hint" : "Klik for at placere første punkt", + "continue-polygon-cut-hint" : "Klik for at fortsætte tegning", + "finish-polygon-cut-hint" : "Klik på første markør for at afslutte og gemme", + "polygon-place-first-point-hint" : "Polygon: klik for at placere første punkt", + "polygon-place-first-point-hint-with-entity" : "Polygon for '{{entityName}}': klik for at placere første punkt", + "continue-polygon-hint" : "Polygon: klik for at fortsætte tegning", + "continue-polygon-hint-with-entity" : "Polygon for '{{entityName}}': klik for at fortsætte tegning", + "finish-polygon-hint" : "Polygon: klik på første markør for at afslutte tegning", + "finish-polygon-hint-with-entity" : "Polygon for '{{entityName}}': klik på første markør for at afslutte og gemme", + "rectangle-place-first-point-hint" : "Rektangel: klik for at placere første punkt", + "rectangle-place-first-point-hint-with-entity" : "Rektangel for '{{entityName}}': klik for at placere første punkt", + "finish-rectangle-hint" : "Rektangel: klik for at afslutte tegning", + "finish-rectangle-hint-with-entity" : "Rektangel for '{{entityName}}': klik for at afslutte og gemme" + }, + "circle" : { + "circle-key" : "Cirkelnøgle", + "circle-key-required" : "Cirkelnøgle er påkrævet", + "no-circles" : "Ingen cirkler konfigureret", + "add-circle" : "Tilføj cirkel", + "circle-configuration" : "Cirkelkonfiguration", + "remove-circle" : "Fjern cirkel", + "edit" : "Rediger cirkel", + "remove-circle-for" : "Fjern cirkel for '{{entityName}}'", + "draw-circle" : "Tegn cirkel", + "place-circle-center-hint-with-entity" : "Cirkel for '{{entityName}}': klik for at placere centrum", + "place-circle-center-hint" : "Cirkel: klik for at placere centrum", + "finish-circle-hint-with-entity" : "Cirkel for '{{entityName}}': klik for at afslutte og gemme cirkel", + "finish-circle-hint" : "Cirkel: klik for at afslutte tegning" + }, + "select-entity" : "Vælg enhed", + "select-entity-hint" : "Tip: klik på kortet efter valg for at angive position" + }, + "select-entity" : "Vælg enhed", + "select-entity-hint" : "Tip: klik på kortet efter valg for at angive position", + "tooltips" : { + "placeMarker" : "Klik for at placere enheden '{{entityName}}'", + "firstVertex" : "Polygon for '{{entityName}}': klik for at placere første punkt", + "firstVertex-cut" : "Klik for at placere første punkt", + "continueLine" : "Polygon for '{{entityName}}': klik for at fortsætte tegningen", + "continueLine-cut" : "Klik for at fortsætte tegningen", + "finishLine" : "Klik på en eksisterende markør for at afslutte", + "finishPoly" : "Polygon for '{{entityName}}': klik på første markør for at afslutte og gemme", + "finishPoly-cut" : "Klik på første markør for at afslutte og gemme", + "finishRect" : "Polygon for '{{entityName}}': klik for at afslutte og gemme", + "startCircle" : "Cirkel for '{{entityName}}': klik for at placere cirkelens centrum", + "finishCircle" : "Cirkel for '{{entityName}}': klik for at afslutte cirklen", + "placeCircleMarker" : "Klik for at placere cirkelmarkør" + }, + "actions" : { + "finish" : "Afslut", + "cancel" : "Annuller", + "removeLastVertex" : "Fjern sidste punkt" + }, + "buttonTitles" : { + "drawMarkerButton" : "Placer enhed", + "drawPolyButton" : "Opret polygon", + "drawLineButton" : "Opret polylinje", + "drawCircleButton" : "Opret cirkel", + "drawRectButton" : "Opret rektangel", + "editButton" : "Redigeringstilstand", + "dragButton" : "Træk-og-slip-tilstand", + "cutButton" : "Skær polygonområde", + "deleteButton" : "Fjern", + "drawCircleMarkerButton" : "Opret cirkelmarkør", + "rotateButton" : "Roter polygon" + }, + "map-provider-settings" : "Kortudbyder-indstillinger", + "map-provider" : "Kortudbyder", + "map-provider-google" : "Google Maps", + "map-provider-openstreet" : "OpenStreet Maps", + "map-provider-here" : "HERE Maps", + "map-provider-image" : "Billedkort", + "map-provider-tencent" : "Tencent Maps", + "openstreet-provider" : "OpenStreet kortudbyder", + "openstreet-provider-mapnik" : "OpenStreetMap.Mapnik (Standard)", + "openstreet-provider-hot" : "OpenStreetMap.HOT", + "openstreet-provider-esri-street" : "Esri.WorldStreetMap", + "openstreet-provider-esri-topo" : "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery" : "Esri.WorldImagery", + "openstreet-provider-cartodb-positron" : "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter" : "CartoDB.DarkMatter", + "use-custom-provider" : "Brug tilpasset udbyder", + "custom-provider-tile-url" : "Tilpasset udbyder tile URL", + "google-maps-api-key" : "Google Maps API-nøgle", + "default-map-type" : "Standard korttype", + "google-map-type-roadmap" : "Vejkort", + "google-map-type-satelite" : "Satellit", + "google-map-type-hybrid" : "Hybrid", + "google-map-type-terrain" : "Terræn", + "map-layer" : "Kortlag", + "here-map-normal-day" : "HERE.normalDay (Standard)", + "here-map-normal-night" : "HERE.normalNight", + "here-map-hybrid-day" : "HERE.hybridDay", + "here-map-terrain-day" : "HERE.terrainDay", + "credentials" : "Legitimationsoplysninger", + "here-app-id" : "HERE app-id", + "here-app-code" : "HERE app-kode", + "here-api-key" : "HERE API-nøgle", + "here-use-new-version-api-3" : "Brug API version 3", + "tencent-maps-api-key" : "Tencent Maps API-nøgle", + "tencent-map-type-roadmap" : "Vejkort", + "tencent-map-type-satelite" : "Satellit", + "tencent-map-type-hybrid" : "Hybrid", + "image-map-background" : "Baggrundsbillede for kort", + "image-map-background-from-entity-attribute" : "Tag baggrundsbillede fra enhedsattribut", + "image-url-source-entity-alias" : "Billed-URL kilde enhedsalias", + "image-url-source-entity-attribute" : "Billed-URL kilde enhedsattribut", + "common-map-settings" : "Fælles kortindstillinger", + "x-pos-key-name" : "X-positionsnøgle", + "y-pos-key-name" : "Y-positionsnøgle", + "latitude-key-name" : "Breddegradsnøgle", + "longitude-key-name" : "Længdegradsnøgle", + "default-map-zoom-level" : "Standard zoom-niveau (0 - 20)", + "default-map-center-position" : "Standard midtposition (0,0)", + "disable-scroll-zooming" : "Deaktiver zoom med scroll", + "disable-double-click-zooming" : "Deaktiver zoom med dobbeltklik", + "disable-zoom-control-buttons" : "Deaktiver zoomkontrolknapper", + "fit-map-bounds" : "Tilpas kortudsnit til at dække alle markører", + "use-default-map-center-position" : "Brug standard midtposition", + "entities-limit" : "Maks antal enheder at indlæse", + "markers-settings" : "Markørindstillinger", + "marker-offset-x" : "Markørens X-forskydning i forhold til position × bredde", + "marker-offset-y" : "Markørens Y-forskydning i forhold til position × højde", + "position-function" : "Positionskonverteringsfunktion (returnerer x,y mellem 0 og 1)", + "draggable-marker" : "Trækkelig markør", + "label" : "Etiket", + "show-label" : "Vis etiket", + "use-label-function" : "Brug etiketsfunktion", + "label-pattern" : "Etiket (f.eks. '${entityName}', '${entityName}: (Tekst ${keyName} enheder.)')", + "label-function" : "Etiketsfunktion", + "tooltip" : "Værktøjstip", + "show-tooltip" : "Vis værktøjstip", + "show-tooltip-action" : "Handling for at vise værktøjstip", + "show-tooltip-action-click" : "Klik for at vise værktøjstip (Standard)", + "show-tooltip-action-hover" : "Hold musen over for at vise værktøjstip", + "auto-close-tooltips" : "Luk værktøjstip automatisk", + "use-tooltip-function" : "Brug værktøjstipsfunktion", + "tooltip-pattern" : "Værktøjstip (f.eks. 'Tekst ${keyName} enheder.' eller Link tekst)", + "tooltip-function" : "Værktøjstipsfunktion", + "tooltip-offset-x" : "X-forskydning for værktøjstip i forhold til markørens anker × bredde", + "tooltip-offset-y" : "Y-forskydning for værktøjstip i forhold til markørens anker × højde", + "color" : "Farve", + "use-color-function" : "Brug farvefunktion", + "color-function" : "Farvefunktion", + "marker-image" : "Markørbillede", + "use-marker-image-function" : "Brug markørbilledfunktion", + "custom-marker-image" : "Brugerdefineret markørbillede", + "custom-marker-image-size" : "Størrelse på brugerdefineret markørbillede (px)", + "marker-image-function" : "Markørbilledfunktion", + "marker-images" : "Markørbilleder", + "polygon-settings" : "Polygonindstillinger", + "show-polygon" : "Vis polygon", + "polygon-key-name" : "Polygonnøgle", + "enable-polygon-edit" : "Tillad redigering af polygon", + "polygon-label" : "Polygonetiket", + "show-polygon-label" : "Vis polygonetiket", + "use-polygon-label-function" : "Brug polygonetiketsfunktion", + "polygon-label-pattern" : "Polygonetiket (f.eks. '${entityName}', '${entityName}: (Tekst ${keyName} enheder.)')", + "polygon-label-function" : "Polygonetiketsfunktion", + "polygon-tooltip" : "Polygon værktøjstip", + "show-polygon-tooltip" : "Vis polygon værktøjstip", + "auto-close-polygon-tooltips" : "Luk polygon-værktøjstip automatisk", + "use-polygon-tooltip-function" : "Brug polygon værktøjstipsfunktion", + "polygon-tooltip-pattern" : "Værktøjstip (f.eks. 'Tekst ${keyName} enheder.' eller Link tekst)", + "polygon-tooltip-function" : "Polygon værktøjstipsfunktion", + "polygon-color" : "Polygonfarve", + "polygon-opacity" : "Polygon opacitet", + "use-polygon-color-function" : "Brug polygonfarvefunktion", + "polygon-color-function" : "Polygonfarvefunktion", + "polygon-stroke" : "Polygonkant", + "stroke-color" : "Stregfarve", + "stroke-opacity" : "Streg opacitet", + "stroke-weight" : "Stregtykkelse", + "use-polygon-stroke-color-function" : "Brug polygonstregfarvefunktion", + "polygon-stroke-color-function" : "Polygonstregfarvefunktion", + "circle-settings" : "Cirkelindstillinger", + "show-circle" : "Vis cirkel", + "circle-key-name" : "Cirkel nøgle", + "enable-circle-edit" : "Tillad redigering af cirkel", + "circle-label" : "Cirkel etiket", + "show-circle-label" : "Vis cirkeletiket", + "use-circle-label-function" : "Brug cirkeletiketsfunktion", + "circle-label-pattern" : "Cirkeletiket (f.eks. '${entityName}', '${entityName}: (Tekst ${keyName} enheder.)')", + "circle-label-function" : "Cirkeletiketsfunktion", + "circle-tooltip" : "Cirkel værktøjstip", + "show-circle-tooltip" : "Vis cirkel værktøjstip", + "auto-close-circle-tooltips" : "Luk cirkel-værktøjstip automatisk", + "use-circle-tooltip-function" : "Brug cirkel værktøjstipsfunktion", + "circle-tooltip-pattern" : "Værktøjstip (f.eks. 'Tekst ${keyName} enheder.' eller Link tekst)", + "circle-tooltip-function" : "Cirkel værktøjstipsfunktion", + "circle-fill-color" : "Fyldfarve for cirkel", + "circle-fill-color-opacity" : "Fyldfarve opacitet", + "use-circle-fill-color-function" : "Brug fyldfarvefunktion", + "circle-fill-color-function" : "Fyldfarvefunktion for cirkel", + "circle-stroke" : "Cirkelkant", + "use-circle-stroke-color-function" : "Brug stregfarvefunktion for cirkel", + "circle-stroke-color-function" : "Stregfarvefunktion for cirkel", + "markers-clustering-settings" : "Indstillinger for markørgruppering", + "use-map-markers-clustering" : "Brug markørgruppering", + "zoom-on-cluster-click" : "Zoom ved klik på gruppe", + "max-cluster-zoom" : "Maksimalt zoomniveau for at være del af en gruppe (0 - 18)", + "max-cluster-radius-pixels" : "Maksimal radius for en gruppe (i pixels)", + "cluster-zoom-animation" : "Vis animation ved zoom", + "show-markers-bounds-on-cluster-mouse-over" : "Vis markørgrænser ved museovergang på gruppe", + "spiderfy-max-zoom-level" : "Spiderfy ved maks zoomniveau (for at vise alle markører)", + "load-optimization" : "Indlæsningsoptimering", + "cluster-chunked-loading" : "Brug delt indlæsning for at undgå fastfrysning", + "cluster-markers-lazy-load" : "Brug lazy-load til markører", + "editor-settings" : "Editorindstillinger", + "enable-snapping" : "Aktiver snapping til andre hjørner for præcis tegning", + "init-draggable-mode" : "Initialiser kort i trækkemodus", + "hide-all-edit-buttons" : "Skjul alle redigeringsknapper", + "hide-draw-buttons" : "Skjul tegneknapper", + "hide-edit-buttons" : "Skjul redigeringsknapper", + "hide-remove-button" : "Skjul fjern-knap", + "route-map-settings" : "Rutekortindstillinger", + "trip-animation-settings" : "Ruteanimationsindstillinger", + "normalization-step" : "Normaliserings-trin (ms)", + "tooltip-background-color" : "Baggrundsfarve for værktøjstip", + "tooltip-font-color" : "Skriftfarve for værktøjstip", + "tooltip-opacity" : "Værktøjstip opacitet (0-1)", + "auto-close-tooltip" : "Luk værktøjstip automatisk", + "rotation-angle" : "Yderligere rotationsvinkel for markør (grader)", + "path-settings" : "Stiindstillinger", + "path-color" : "Stifarve", + "use-path-color-function" : "Brug stifarvefunktion", + "path-color-function" : "Stifarvefunktion", + "path-decorator" : "Stidekoration", + "use-path-decorator" : "Brug stidekoration", + "decorator-symbol" : "Dekorationssymbol", + "decorator-symbol-arrow-head" : "Pil", + "decorator-symbol-dash" : "Streg", + "decorator-symbol-size" : "Størrelse på dekorationssymbol (px)", + "use-path-decorator-custom-color" : "Brug brugerdefineret farve til dekoration", + "decorator-custom-color" : "Dekorationsfarve", + "decorator-offset" : "Dekorationsforskydning", + "end-decorator-offset" : "Afslut dekorationsforskydning", + "decorator-repeat" : "Dekoration gentagelse", + "points-settings" : "Punktindstillinger", + "show-points" : "Vis punkter", + "point-color" : "Punktfarve", + "point-size" : "Punktstørrelse (px)", + "use-point-color-function" : "Brug punktfarvefunktion", + "point-color-function" : "Punktfarvefunktion", + "use-point-as-anchor" : "Brug punkt som anker", + "point-as-anchor-function" : "Ankerpunkt funktion", + "independent-point-tooltip" : "Uafhængig punkt værktøjstip", + "clustering-markers" : "Gruppering af markører", + "use-icon-create-function" : "Brug markørfarvefunktion", + "marker-color-function" : "Markørfarvefunktion" + }, + "markdown" : { + "use-markdown-text-function" : "Brug markdown/HTML værdifunktion", + "markdown-text-function" : "Markdown/HTML værdifunktion", + "markdown-text-pattern" : "Markdown/HTML mønster (markdown eller HTML med variabler, f.eks. '${entityName} eller ${keyName} - noget tekst.')", + "apply-default-markdown-style" : "Anvend standard markdown-stil", + "markdown-css" : "Markdown/HTML CSS" + }, + "simple-card" : { + "label" : "Etiket", + "label-position" : "Etiketplacering", + "label-position-left" : "Venstre", + "label-position-top" : "Top" + }, + "single-switch" : { + "behavior" : "Adfærd", + "layout" : "Layout", + "layout-right" : "Højre", + "layout-left" : "Venstre", + "layout-centered" : "Centreret", + "auto-scale" : "Auto skala", + "label" : "Etiket", + "icon" : "Ikon", + "switch-color" : "Kontakts farve", + "on" : "Tændt", + "off" : "Slukket", + "disabled" : "Deaktiveret", + "tumbler-color" : "Tumbler-farve", + "on-label" : "Tænd etiket", + "off-label" : "Sluk etiket", + "switch" : "Kontakt" + }, + "slider" : { + "behavior" : "Adfærd", + "initial-value" : "Startværdi", + "initial-value-hint" : "Handling for at hente skyderens startværdi.", + "on-value-change" : "Ved værdiændring", + "on-value-change-hint" : "Handling udløst når skyderens værdi ændres.", + "layout" : "Layout", + "layout-default" : "Standard", + "layout-extended" : "Udvidet", + "layout-simplified" : "Forenklet", + "auto-scale" : "Auto skala", + "icon" : "Ikon", + "value" : "Værdi", + "range" : "Interval", + "min" : "min", + "max" : "maks", + "range-ticks" : "Intervalmærker", + "tick-marks" : "Mærker", + "colors" : "Farver", + "main" : "Hoved", + "background" : "Baggrund", + "left-icon" : "Venstre ikon", + "right-icon" : "Højre ikon", + "slider" : "Skyder" + }, + "value-card" : { + "layout" : "Layout", + "layout-square" : "Firkantet", + "layout-vertical" : "Lodret", + "layout-centered" : "Centreret", + "layout-simplified" : "Forenklet", + "layout-horizontal" : "Vandret", + "layout-horizontal-reversed" : "Vandret (omvendt)", + "label" : "Etiket", + "icon" : "Ikon", + "value" : "Værdi", + "date" : "Dato", + "value-card-style" : "Værdikort stil", + "auto-scale" : "Auto skala" + }, + "label-card" : { + "auto-scale" : "Auto skala", + "label" : "Etiket", + "icon" : "Ikon", + "label-card-style" : "Etikettens kortstil" + }, + "label-value-card" : { + "value" : "Værdi", + "label-value-card-style" : "Etiket & værdi kortstil" + }, + "liquid-level-card" : { + "layout-simple" : "Simpel", + "layout-percentage" : "Procent", + "layout-absolute" : "Absolut", + "layout" : "Layout", + "background-overlay" : "Baggrundsoverlay for værdi", + "total-volume" : "Samlet volumen", + "total-volume-units" : "Volumen enheder", + "tank" : "Tank", + "shape" : "Form", + "datasource-units" : "Kilde enheder", + "widget-units" : "Widget enheder", + "decimals" : "Decimaler", + "liquid" : "Væske", + "liquid-color" : "Væskens farve", + "value" : "Værdi", + "value-font" : "Værdiens skrifttype", + "level" : "Niveau", + "last-update" : "Seneste opdatering", + "shape-by-attribute" : "Angiv tankform ud fra attributnavn", + "tooltip-background" : "Baggrundsfarve", + "background-blur" : "Baggrundssløring", + "tank-color" : "Tankens farve", + "static" : "Statisk", + "see-examples" : "Se eksempler", + "attribute" : "Attribut", + "shape-type" : "Type", + "v-oval" : "Lodret oval", + "v-cylinder" : "Lodret cylinder", + "v-capsule" : "Lodret kapsel", + "rectangle" : "Rektangel", + "h-oval" : "Vandret oval", + "h-ellipse" : "Vandret ellipse", + "h-dish-ends" : "Vandret med skålender", + "h-cylinder" : "Vandret cylinder", + "h-capsule" : "Vandret kapsel", + "h-elliptical_2_1" : "Vandret 2:1 elliptisk", + "icon" : "Kort ikon", + "title" : "Korttitel", + "units" : "Enheder", + "color-and-font" : "Farve og skrifttype", + "shape-attribute-name" : "Attributnavn", + "total-volume-required" : "Samlet volumen er påkrævet.", + "attribute-name-required" : "Attributnavn er påkrævet.", + "attribute-key-not-set" : "Attributnøglen '{{attributeName}}' er ikke angivet", + "attribute-key-invalid" : "Attributnøglen '{{attributeName}}' er ugyldig" + }, + "aggregated-value-card" : { + "subtitle" : "Undertitel", + "chart" : "Diagram", + "values" : "Værdier", + "value-appearance" : "Værdiens udseende", + "position" : "Position", + "position-center" : "Center", + "position-right-top" : "Øverst til højre", + "position-right-bottom" : "Nederst til højre", + "position-left-top" : "Øverst til venstre", + "position-left-bottom" : "Nederst til venstre", + "font" : "Skrifttype", + "color" : "Farve", + "arrow" : "Pil", + "display-up-down-arrow" : "Vis op/ned pil", + "add-value" : "Tilføj værdi", + "remove-value" : "Fjern værdi", + "no-values" : "Ingen værdier konfigureret", + "aggregation" : "Aggregering", + "aggregated-value-card-style" : "Aggregeret værdi kortstil", + "auto-scale" : "Auto skala" + }, + "value-chart-card" : { + "layout" : "Layout", + "layout-left" : "Venstre", + "layout-right" : "Højre", + "auto-scale" : "Auto skala", + "icon" : "Ikon", + "value" : "Værdi", + "chart" : "Diagram", + "value-chart-card-style" : "Værdi-diagram kortstil" + }, + "progress-bar" : { + "layout" : "Layout", + "layout-default" : "Standard", + "layout-simplified" : "Forenklet", + "auto-scale" : "Auto skala", + "icon" : "Ikon", + "value" : "Værdi", + "range" : "Interval", + "min" : "min", + "max" : "max", + "range-ticks" : "Interval-mærker", + "bar" : "Bar", + "bar-color" : "Barens farve", + "bar-background" : "Barens baggrund", + "progress-bar-card-style" : "Fremskridtsbjælke kortstil" + }, + "notification" : { + "max-notification-display" : "Maksimalt antal notifikationer der vises", + "counter" : "Tæller", + "counter-hint" : "Tælleren vises hvis \"Widgettitel\" er aktiveret", + "icon" : "Ikon", + "counter-value" : "Værdi", + "counter-color" : "Farve", + "notification-button" : "Notifikationsknapper", + "button-view-all" : "Se alle", + "button-filter" : "Filter", + "type-filter" : "Typefilter", + "button-mark-read" : "Markér alle som læst", + "notification-types" : "Notifikationstyper", + "notification-type" : "Notifikationstype", + "search-type" : "Søg efter type", + "any-type" : "Alle typer" + }, + "alarm-count" : { + "alarm-count-card-style" : "Alarmtæller kortstil" + }, + "entity-count" : { + "entity-count-card-style" : "Enhedstæller kortstil" + }, + "count" : { + "layout" : "Layout", + "layout-column" : "Kolonne", + "layout-row" : "Række", + "label" : "Etiket", + "icon" : "Ikon", + "icon-background" : "Ikon baggrund", + "value" : "Værdi", + "chevron" : "Pil", + "auto-scale" : "Auto skala" + }, + "table" : { + "common-table-settings" : "Generelle tabelindstillinger", + "enable-search" : "Aktiver søgning", + "enable-sticky-header" : "Vis altid overskrift", + "enable-sticky-action" : "Vis altid handlingskolonne", + "hidden-cell-button-display-mode" : "Visningstilstand for skjulte cellehandlinger", + "show-empty-space-hidden-action" : "Vis tomt mellemrum i stedet for skjult cellehandling", + "dont-reserve-space-hidden-action" : "Reserver ikke plads til skjulte handlingsknapper", + "display-timestamp" : "Tidsstempel", + "display-pagination" : "Vis sidetal", + "default-page-size" : "Standard sidestørrelse", + "page-step-settings" : "Indstillinger for sidetrin", + "page-step-count" : "Antal trin", + "page-step-increment" : "Trinforøgelse", + "page-step-count-format-message" : "Skal være en heltalsværdi i intervallet fra 1 til 100.", + "page-step-increment-format-message" : "Skal være en heltalsværdi, større end eller lig med 1.", + "use-entity-label-tab-name" : "Brug enhedsetiket som fanenavn", + "hide-empty-lines" : "Skjul tomme linjer", + "row-style" : "Rækkestil", + "use-row-style-function" : "Brug rækkestilsfunktion", + "row-style-function" : "Rækkestilsfunktion", + "cell-style" : "Cellestil", + "use-cell-style-function" : "Brug cellestilsfunktion", + "cell-style-function" : "Cellestilsfunktion", + "cell-content" : "Celleindhold", + "use-cell-content-function" : "Brug celleindholdsfunktion", + "cell-content-function" : "Celleindholdsfunktion", + "show-latest-data-column" : "Vis kolonne med seneste data", + "latest-data-column-order" : "Rækkefølge for kolonne med seneste data", + "entities-table-title" : "Tabeltitel for enheder", + "enable-select-column-display" : "Aktiver valg af viste kolonner", + "display-entity-name" : "Vis kolonne for enhedsnavn", + "entity-name-column-title" : "Titel på kolonne for enhedsnavn", + "display-entity-label" : "Vis kolonne for enhedsetiket", + "entity-label-column-title" : "Titel på kolonne for enhedsetiket", + "display-entity-type" : "Vis kolonne for enhedstype", + "default-sort-order" : "Standard sorteringsrækkefølge", + "custom-title" : "Brugerdefineret overskriftstitel", + "column-width" : "Kolonnebredde (px eller %)", + "default-column-visibility" : "Standard kolonnesynlighed", + "column-visibility-visible" : "Synlig", + "column-visibility-hidden" : "Skjult", + "column-visibility-hidden-mobile" : "Skjult i mobiltilstand", + "column-selection-to-display" : "Kolonnevalg i 'Viste kolonner'", + "column-selection-to-display-enabled" : "Aktiveret", + "column-selection-to-display-disabled" : "Deaktiveret", + "alarms-table-title" : "Tabeltitel for alarmer", + "enable-alarms-selection" : "Aktiver valg af alarmer", + "enable-alarms-search" : "Aktiver søgning i alarmer", + "enable-alarm-filter" : "Aktiver alarmfilter", + "display-alarm-details" : "Vis alarmdetaljer", + "allow-alarms-ack" : "Tillad kvittering af alarmer", + "allow-alarms-clear" : "Tillad rydning af alarmer", + "display-alarm-activity" : "Vis alarmaktivitet", + "allow-alarms-assign" : "Tillad tildeling af alarmer", + "columns" : "Kolonner", + "column-settings" : "Kolonneindstillinger", + "remove-column" : "Fjern kolonne", + "add-column" : "Tilføj kolonne", + "no-columns" : "Ingen kolonner konfigureret", + "columns-to-display" : "Kolonner at vise", + "table-header" : "Tabeloverskrift", + "header-buttons" : "Overskriftsknapper", + "table-buttons" : "Tabelknapper", + "pagination" : "Sidetal", + "rows" : "Rækker", + "timeseries-column-error" : "Mindst én tidsserie-kolonne skal angives", + "alarm-column-error" : "Mindst én alarmkolonne skal angives", + "table-tabs" : "Tabeller i faner", + "show-cell-actions-menu-mobile" : "Vis cellehandlingsmenu i mobiltilstand", + "disable-sorting" : "Deaktiver sortering" + }, + "latest-chart" : { + "total" : "Total", + "auto-scale" : "Automatisk skalering", + "clockwise-layout" : "Layout med urets retning", + "sort-series" : "Sorter serier efter etiket", + "tooltip-value-type-absolute" : "Absolut", + "tooltip-value-type-percentage" : "Procent" + }, + "pie-chart" : { + "pie-chart-appearance" : "Udseende for cirkeldiagram", + "label" : "Etiket", + "border" : "Kant", + "radius" : "Radius", + "pie-chart-card-style" : "Kortstil for cirkeldiagram" + }, + "radar-chart" : { + "radar-appearance" : "Udseende for radardiagram", + "shape" : "Form", + "shape-polygon" : "Polygon", + "shape-circle" : "Cirkel", + "color" : "Farve", + "line" : "Linje", + "points" : "Punkter", + "points-label" : "Etiket for punkter", + "radar-axis" : "Radarakse", + "axis-label" : "Aksesetiket", + "ticks-label" : "Etiket for mærker", + "radar-chart-style" : "Stil for radardiagram" + }, + "time-series-chart" : { + "chart" : "Diagram", + "chart-style" : "Diagramstil", + "data-zoom" : "Datavisning (zoom)", + "stack-mode" : "Staktilstand", + "stack-mode-hint" : "Lægger serier oven på hinanden i diagrammet. Serier med samme enhed placeres oven på hinanden.", + "axes" : "Akser", + "y-axes" : "Y-akser", + "line-type" : "Linetype", + "line-width" : "Linjebredde", + "type-line" : "Linje", + "type-bar" : "Søjle", + "type-point" : "Punkt", + "no-aggregation-bar-width-strategy" : "Søjlebredde-strategi for ikke-aggregerede data", + "no-aggregation-bar-width-strategy-group" : "Grupperet", + "no-aggregation-bar-width-strategy-separate" : "Separat", + "bar-group-width" : "Søjlgruppebredde", + "bar-width" : "Søjlens bredde", + "bar-width-relative" : "Procentdel af tidsvindue", + "bar-width-absolute" : "Absolut (ms)", + "comparison" : { + "comparison" : "Sammenligning", + "comparison-hint" : "Sammenligning virker kun med historiske data!", + "show" : "Vis", + "settings" : "Indstillinger for sammenligning", + "show-values-for-comparison" : "Vis historiske data til sammenligning", + "comparison-values-label" : "Etiket for sammenligningsnøgle", + "comparison-values-label-auto" : "Auto", + "comparison-data-color" : "Farve for sammenligningsdata" + }, + "threshold" : { + "thresholds" : "Tærskler", + "source" : "Kilde", + "key-value" : "Nøgle / Værdi", + "no-thresholds" : "Ingen tærskler konfigureret", + "add-threshold" : "Tilføj tærskel", + "type-constant" : "Konstant", + "type-latest-key" : "Nøgle", + "type-entity" : "Enhed", + "threshold-settings" : "Tærskelindstillinger", + "remove-threshold" : "Fjern tærskel", + "threshold-value-required" : "Tærskelværdi er påkrævet.", + "key-required" : "Nøgle er påkrævet.", + "entity-key-required" : "Enhedsnøgle er påkrævet.", + "line-appearance" : "Linjens udseende", + "line-color" : "Linjefarve", + "start-symbol" : "Startsymbol", + "end-symbol" : "Slutsymbol", + "symbol-size" : "Størrelse", + "label" : "Etiket", + "label-position-start" : "Start", + "label-position-middle" : "Midt", + "label-position-end" : "Slut", + "label-position-inside-start" : "Inde i start", + "label-position-inside-start-top" : "Inde i start top", + "label-position-inside-start-bottom" : "Inde i start bund", + "label-position-inside-middle" : "Inde i midten", + "label-position-inside-middle-top" : "Inde i midten top", + "label-position-inside-middle-bottom" : "Inde i midten bund", + "label-position-inside-end" : "Inde i slutning", + "label-position-inside-end-top" : "Inde i slutning top", + "label-position-inside-end-bottom" : "Inde i slutning bund", + "label-background" : "Etiketbaggrund" + }, + "state" : { + "states" : "Tilstande", + "label" : "Etiket", + "ticks-value" : "Mærkeværdi", + "source" : "Kilde", + "value-range" : "Værdi / Interval", + "no-states" : "Ingen tilstande konfigureret", + "add-state" : "Tilføj tilstand", + "type-constant" : "Konstant", + "type-range" : "Interval", + "from" : "Fra", + "to" : "Til", + "remove-state" : "Fjern tilstand" + }, + "grid" : { + "grid" : "Gitter", + "background-color" : "Baggrundsfarve", + "border" : "Kant" + }, + "axis" : { + "axes" : "Akser", + "x-axis" : "X-akse", + "y-axis" : "Y-akse", + "y-axis-settings" : "Indstillinger for Y-akse", + "comparison-x-axis-settings" : "Indstillinger for X-akse (sammenligning)", + "remove-y-axis" : "Fjern Y-akse", + "id" : "Id", + "label" : "Etiket", + "position" : "Position", + "position-left" : "Venstre", + "position-right" : "Højre", + "position-top" : "Top", + "position-bottom" : "Bund", + "tick-labels" : "Mærkeetiketter", + "ticks-formatter-function" : "Formatteringsfunktion for mærker", + "ticks-generator-function" : "Genereringsfunktion for mærker", + "show-ticks" : "Vis mærker", + "show-line" : "Vis linje", + "show-split-lines" : "Vis opdelingslinjer", + "show-split-lines-x-axis-hint" : "Hvis aktiveret, vises de lodrette linjer i diagrammet.", + "show-split-lines-y-axis-hint" : "Hvis aktiveret, vises de vandrette linjer i diagrammet.", + "ticks-interval" : "Mærkeinterval", + "ticks-interval-hint" : "Tvangsindstil segmenteringsinterval for aksen.", + "split-number" : "Opdelingsantal", + "split-number-hint" : "Antal segmenter som aksen opdeles i.", + "min" : "Min", + "max" : "Maks", + "show" : "Vis", + "add-y-axis" : "Tilføj Y-akse" + }, + "series" : { + "legend-settings" : "Indstillinger for forklaring", + "show-in-legend" : "Vis i forklaring", + "show-in-legend-hint" : "Vis seriens navn og data i forklaringen.", + "hidden-by-default" : "Skjult som standard", + "hidden-by-default-hint" : "Gør serien skjult i forklaringen som standard.", + "series-type" : "Serietype", + "type" : "Type", + "type-line" : "Linje", + "type-bar" : "Søjle", + "line" : { + "line" : "Linje", + "show-line" : "Vis linje", + "step-line" : "Trinvis linje", + "step-type-start" : "Start", + "step-type-middle" : "Midte", + "step-type-end" : "Slut", + "smooth-line" : "Glat linje" + }, + "point" : { + "points" : "Punkter", + "show-points" : "Vis punkter", + "point-label" : "Punktetiket", + "point-label-hint" : "Vis etiket med værdi over datapunktet i serien.", + "point-label-background" : "Baggrund for punktetiket", + "point-shape" : "Punktform", + "point-size" : "Punktstørrelse" + } + } + }, + "wind-speed-direction" : { + "layout" : "Layout", + "layout-default" : "Standard", + "layout-advanced" : "Avanceret", + "layout-simplified" : "Forenklet", + "values" : "Værdier", + "wind-direction" : "Vindretning", + "center-value" : "Centrale værdi", + "icon" : "Ikon", + "arrow" : "Pil", + "ticks" : "Mærker", + "labels-type" : "Etikettype", + "directional-names" : "Retningsnavne", + "degrees" : "Grader", + "major-ticks" : "Hovedmærker", + "minor-ticks" : "Undermærker", + "wind-speed-direction-card-style" : "Stil for vindhastighed og -retning", + "ticks-color" : "Farve på mærker", + "ticks-labels-type" : "Etiketter for mærketype", + "arrow-color" : "Pilfarve" + }, + "value-source" : { + "value-source" : "Værdikilde", + "predefined-value" : "Konstant", + "entity-attribute" : "Entitetsattribut", + "value" : "Værdi", + "value-required" : "Værdi er påkrævet.", + "key-required" : "Nøgle er påkrævet.", + "entity-key-required" : "Entitetsnøgle er påkrævet.", + "source-entity-alias" : "Kildeentitetsalias", + "source-entity-attribute" : "Kildeentitetsattribut", + "type-constant" : "Konstant", + "type-latest-key" : "Nøgle", + "type-entity" : "Entitet" + }, + "rpc-state" : { + "initial-state" : "Initial tilstand", + "initial-state-hint" : "Handling for at hente komponentens starttilstand (Tænd/Sluk).", + "disabled-state" : "Deaktiveret tilstand", + "disabled-state-hint" : "Konfigurer betingelse hvorunder komponenten er deaktiveret.", + "turn-on" : "Tænd", + "turn-on-hint" : "Handling udført når skyderen skiftes til 'Tænd'", + "turn-off" : "Sluk", + "turn-off-hint" : "Handling udført når skyderen skiftes til 'Sluk'", + "on" : "Tændt", + "off" : "Slukket", + "disabled" : "Deaktiveret" + }, + "value-action" : { + "do-nothing" : "Gør intet", + "execute-rpc" : "Udfør RPC", + "get-attribute" : "Hent attribut", + "set-attribute" : "Angiv attribut", + "get-time-series" : "Hent tidsserier", + "get-alarm-status" : "Hent alarmstatus", + "get-dashboard-state" : "Hent dashboardtilstands-id", + "get-dashboard-state-object" : "Hent dashboardtilstandsobjekt", + "add-time-series" : "Tilføj tidsserier", + "execute-rpc-text" : "Udfør RPC-metode '{{methodName}}'", + "get-time-series-text" : "Brug tidsserie '{{key}}'", + "get-attribute-text" : "Brug attribut '{{key}}'", + "get-alarm-status-text" : "Brug alarmstatus", + "get-dashboard-state-text" : "Brug dashboardtilstand", + "get-dashboard-state-object-text" : "Brug dashboardtilstandsobjekt", + "when-dashboard-state-is-text" : "Når dashboard-tilstand er '{{state}}'", + "when-dashboard-state-function-is-text" : "Når f(dashboard-tilstand) er '{{state}}'", + "when-dashboard-state-object-function-is-text" : "Når f(dashboardtilstandsobjekt) er '{{state}}'", + "set-attribute-to-value-text" : "Angiv '{{key}}' attribut til: {{value}}", + "add-time-series-value-text" : "Tilføj '{{key}}' tidsserieværdi: {{value}}", + "set-attribute-text" : "Angiv '{{key}}' attribut", + "add-time-series-text" : "Tilføj '{{key}}' tidsserie", + "action" : "Handling", + "value" : "Værdi", + "init-value-hint" : "Værdi der indstilles indtil enhed sender data.", + "method" : "Metode", + "method-name-required" : "Metodenavn er påkrævet.", + "request-timeout-ms" : "RPC anmodning timeout (ms)", + "request-timeout-required" : "Timeout for anmodning er påkrævet.", + "min-request-timeout-error" : "Timeoutværdi skal være mindst 5000 ms (5 sekunder).", + "request-persistent" : "Vedvarende RPC-anmodning", + "persistent-polling-interval" : "Interval for vedvarende polling (ms)", + "persistent-polling-interval-hint" : "Polling-interval (ms) for at hente svar på vedvarende RPC-kommando", + "persistent-polling-interval-required" : "Polling-interval er påkrævet.", + "min-persistent-polling-interval-error" : "Polling-interval skal være mindst 1000 ms (1 sekund).", + "attribute-scope" : "Attribut-område", + "attribute-key" : "Attributnøgle", + "attribute-key-required" : "Attributnøgle er påkrævet.", + "time-series-key" : "Tidsserienøgle", + "time-series-key-required" : "Tidsserienøgle er påkrævet.", + "action-result-converter" : "Resultatomformer for handling", + "converter-none" : "Ingen", + "converter-function" : "Funktion", + "converter-constant" : "Konstant", + "converter-value" : "Værdi", + "parse-value-function" : "Fortolk værdifunktion", + "state-when-result-is" : "'{{state}}' når resultat er", + "parameters" : "Parametre", + "convert-value-function" : "Konverter værdifunktion", + "error" : { + "target-entity-is-not-set" : "Målenhed er ikke angivet!", + "failed-to-perform-action" : "Kunne ikke udføre {{ actionLabel }} handling.", + "invalid-attribute-scope" : "{{scope}} attributområde understøttes ikke af {{entityType}}-entitet." + } + }, + "widget-font" : { + "font-settings" : "Skrifttypeindstillinger", + "font-family" : "Skrifttypefamilie", + "size" : "Størrelse", + "relative-font-size" : "Relativ skriftstørrelse (procent)", + "font-style" : "Stil", + "font-style-normal" : "Normal", + "font-style-italic" : "Kursiv", + "font-style-oblique" : "Skrå", + "font-weight" : "Vægt", + "font-weight-normal" : "Normal", + "font-weight-bold" : "Fed", + "font-weight-bolder" : "Federe", + "font-weight-lighter" : "Tyndere", + "color" : "Farve", + "shadow-color" : "Skyggefarve", + "preview" : "Forhåndsvisning", + "line-height" : "Linjeafstand", + "auto" : "Auto" + }, + "home" : { + "no-data-available" : "Ingen data tilgængelige" + }, + "system-info" : { + "cpu" : "CPU", + "ram" : "RAM", + "disk" : "Disk", + "cpu-warning-text" : "Højt CPU-forbrug. Optimer ydeevnen for at undgå systemfejl.", + "cpu-critical-text" : "Kritisk højt CPU-forbrug. Optimer ydeevnen for at undgå systemfejl.", + "ram-warning-text" : "Lav RAM-reserve. Optimer ydeevnen eller øg RAM-størrelsen for at undgå systemfejl.", + "ram-critical-text" : "Kritisk lav RAM-reserve. Optimer ydeevnen eller øg RAM-størrelsen for at undgå systemfejl.", + "disk-warning-text" : "Lav diskplads. Frigør eller udvid diskpladsen for at undgå datatab.", + "disk-critical-text" : "Kritisk lav diskplads. Frigør eller udvid diskpladsen for at undgå datatab." + }, + "cluster-info" : { + "service-id" : "Service-id", + "service-type" : "Servicetype", + "no-data" : "Ingen data" + }, + "transport-messages" : { + "title" : "Transportbeskeder", + "info" : "Alle beskeder der kommer fra enheder" + }, + "activity" : { + "title" : "Aktivitet" + }, + "documentation" : { + "title" : "Dokumentation", + "add-link" : "Tilføj link", + "add-link-title" : "Tilføj dokumentationslink", + "name" : "Navn", + "name-required" : "Navn er påkrævet.", + "link" : "Link", + "link-required" : "Link er påkrævet.", + "columns" : "Kolonner" + }, + "quick-links" : { + "title" : "Genveje", + "add-link" : "Tilføj link", + "add-link-title" : "Tilføj genvej", + "quick-link" : "Genvej", + "quick-link-required" : "Genvej er påkrævet.", + "no-links-matching" : "Ingen links matcher '{{name}}'.", + "columns" : "Kolonner" + }, + "recent-dashboards" : { + "title" : "Dashboards", + "last" : "Sidst vist", + "starred" : "Favoritter", + "name" : "Navn", + "last-viewed" : "Sidst vist", + "no-last-viewed-dashboards" : "Ingen dashboards er blevet vist endnu" + }, + "configured-features" : { + "title" : "Konfigurerede funktioner", + "info" : "Status for funktioner, der kræver konfiguration", + "email-feature" : "E-mail", + "sms-feature" : "SMS", + "slack-feature" : "Slack", + "oauth2-feature" : "OAuth 2", + "2fa-feature" : "2FA", + "feature-configured" : "Funktionen er konfigureret.\nKlik for at opsætte", + "feature-not-configured" : "Funktionen er ikke konfigureret.\nKlik for at opsætte" + }, + "version-info" : { + "title" : "Version", + "contact-us" : "Kontakt os", + "current-version" : "Nuværende version", + "current" : "Nuværende", + "available-version" : "Tilgængelig version", + "available" : "Tilgængelig", + "upgrade" : "Opgrader", + "version-is-up-to-date" : "Versionen er opdateret" + }, + "usage-info" : { + "title" : "Forbrug", + "entities" : "Entiteter", + "api-calls" : "API-kald" + }, + "functions" : { + "title" : "Funktioner", + "pe-feature-tooltip" : "Kun i ThingsBoard\nProfessional Edition", + "switch-to-pe" : "Skift til PE", + "alarms" : "Alarmer", + "dashboards" : "Dashboards", + "entities-and-relations" : "Entiteter og relationer", + "profiles" : "Profiler", + "advanced-features" : "Avancerede funktioner", + "notification-center" : "Notifikationscenter", + "api-usage" : "API-forbrug", + "customers" : "Kunder", + "customers-hierarchy" : "Kundehierarki", + "roles-and-permissions" : "Roller og tilladelser", + "groups" : "Grupper", + "integrations" : "Integrationer", + "solution-templates" : "Løsningsskabeloner", + "scheduler" : "Tidsplanlægger", + "white-labeling" : "White-labeling" + }, + "devices" : { + "view-docs" : "Se dokumentation", + "inactive" : "Inaktiv", + "active" : "Aktiv", + "total" : "I alt" + }, + "alarms" : { + "critical" : "Kritisk", + "assigned-to-me" : "Tildelt mig", + "total" : "I alt" + }, + "getting-started" : { + "get-started" : "Kom i gang", + "finish" : "Afslut", + "done-welcome-title" : "Velkommen ombord", + "done-welcome-text" : "Du klarede det fantastisk!", + "sys-admin" : { + "step1" : { + "title" : "Opret Lejer & Lejeradministrator", + "content" : "

En lejer er en person eller organisation, der ejer eller producerer enheder og aktiver. Lejeren kan have flere lejeradministratorbrugere, kunder, enheder og aktiver.

Lejeradministratoren kan oprette og administrere enheder, aktiver, kunder og dashboards inden for lejerkontoen.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-create-tenant" : "Sådan opretter du Lejer & Lejeradministrator" + }, + "step2" : { + "title" : "Konfigurer funktion: Mailserver", + "content" : "

Konfiguration af mailserver er vigtig for aktivering af brugere, gendannelse af adgangskoder og levering af alarmnotifikationer.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-configure-mail-server" : "Sådan konfigureres Mailserver" + }, + "step3" : { + "title" : "Konfigurer funktion: SMS-udbyder", + "content" : "

Konfigurer SMS-udbydere for at kunne underrette kunder om alarmer via SMS.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-configure-sms-provider" : "Sådan konfigureres SMS-udbyder" + }, + "step4" : { + "title" : "Konfigurer funktion: White-labeling", + "content" : "

Tilpas nemt din virksomheds eller produkts logo og farveskema uden kodning og uden genstart af tjenesten.

Følg dokumentationen for at lære, hvordan du gør det:

" + }, + "step5" : { + "title" : "Konfigurer funktion: 2FA", + "content" : "

Forbedr sikkerheden på platformens konti med tofaktorautentificering.

Følg dokumentationen for at lære, hvordan du gør det:

" + }, + "step6" : { + "title" : "Konfigurer funktion: OAuth 2", + "content" : "

Gør login lettere for lejer- og kundebrugere med Single Sign-On-funktionalitet via OAuth 2.0.

Følg dokumentationen for at lære, hvordan du gør det:

" + } + }, + "tenant-admin" : { + "step1" : { + "title" : "Opret enhed", + "content" : "

Lad os klargøre din første enhed på platformen via brugerfladen. Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-create-device" : "Sådan oprettes Enhed" + }, + "step2" : { + "title" : "Tilslut enhed", + "content-before" : "

For at tilslutte enheden skal du hente enhedslegitimationsoplysningerne. Vi anbefaler at bruge standard, automatisk genererede legitimationsoplysninger – adgangstoken – til denne vejledning.

  • Gå til enhedstabellen
  • Klik på enhedsrækken for at åbne enhedsdetaljer
  • Tryk på knappen \"Kopiér adgangstoken\"

Brug simple kommandoer til at sende data via HTTP. Husk at erstatte $ACCESS_TOKEN med din enheds adgangstoken:

", + "ubuntu" : { + "install-curl" : "Installer cURL til Ubuntu:" + }, + "macos" : { + "install-curl" : "Installer cURL til MacOS:" + }, + "windows" : { + "install-curl" : "Fra og med Windows 10 b17063 er cURL tilgængelig som standard." + }, + "replace-access-token" : "Erstat $ACCESS_TOKEN med din enheds token:", + "content-after" : "

Du kan også bruge andre protokoller såsom MQTT, CoAP osv.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-connect-device" : "Sådan tilsluttes Enhed" + }, + "step3" : { + "title" : "Opret dashboard", + "content" : "

Opret et dashboard for at visualisere data fra entiteter såsom aktiver, enheder m.m.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-create-dashboard" : "Sådan oprettes Dashboard" + }, + "step4" : { + "title" : "Konfigurer alarmregler", + "alarm-rules" : "Alarmregler", + "content" : "

Lad os udløse en alarm, når temperaturen når 25°C. Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-configure-alarm-rules" : "Sådan konfigureres Alarmregler" + }, + "step5" : { + "title" : "Opret alarm", + "content-before" : "

For at udløse alarmen skal du sende en ny telemetriværdi på 26°C eller højere.

", + "replace-access-token" : "Erstat $ACCESS_TOKEN med din enheds token:", + "content-after" : "

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-create-alarm" : "Sådan oprettes Alarm" + }, + "step6" : { + "title" : "Opret kunde og del dashboard", + "content" : "

Ved at oprette dashboards til slutbrugere kan en kundebruger kun se sine egne enheder, mens data fra andre kunder skjules.

Følg dokumentationen for at lære, hvordan du gør det:

" + } + } + } + }, + "icon" : { + "icon" : "Ikon", + "icons" : "Ikoner", + "select-icon" : "Vælg ikon", + "material-icons" : "Materialeikoner", + "show-all" : "Vis alle ikoner", + "search-icon" : "Søg ikon", + "no-icons-found" : "Ingen ikoner fundet for '{{iconSearch}}'" + }, + "phone-input" : { + "phone-input-label" : "Telefonnummer", + "phone-input-required" : "Telefonnummer er påkrævet", + "phone-input-validation" : "Telefonnummeret er ugyldigt eller ikke muligt", + "phone-input-pattern" : "Ugyldigt telefonnummer. Skal være i E.164-format, f.eks. {{phoneNumber}}", + "phone-input-hint" : "Telefonnummer i E.164-format, f.eks. {{phoneNumber}}" + }, + "custom" : { + "widget-action" : { + "action-cell-button" : "Handlingsknap i celle", + "row-click" : "Ved klik på række", + "cell-click" : "Ved klik på celle", + "polygon-click" : "Ved klik på polygon", + "marker-click" : "Ved klik på markør", + "circle-click" : "Ved klik på cirkel", + "tooltip-tag-action" : "Handlingsmærke i værktøjstip", + "node-selected" : "Ved valg af node", + "element-click" : "Ved klik på HTML-element", + "pie-slice-click" : "Ved klik på lagkageskive", + "row-double-click" : "Ved dobbeltklik på række", + "cell-double-click" : "Ved dobbeltklik på celle", + "card-click" : "Ved klik på kort", + "click" : "Ved klik" + } + }, + "paginator" : { + "items-per-page" : "Elementer pr. side:", + "first-page-label" : "Første side", + "last-page-label" : "Sidste side", + "next-page-label" : "Næste side", + "previous-page-label" : "Forrige side", + "items-per-page-separator" : "af" + }, + "language" : { + "language" : "Language", + "locales" : { + "ar_AE" : "العربية (الإمارات العربية المتحدة)", + "ca_ES" : "català (Espanya)", + "cs_CZ" : "čeština (Česko)", + "da_DK" : "dansk (Danmark)", + "de_DE" : "Deutsch (Deutschland)", + "el_GR" : "Ελληνικά (Ελλάδα)", + "en_US" : "English (United States)", + "es_ES" : "español (España)", + "fa_IR" : "فارسی (ایران)", + "fr_FR" : "français (France)", + "it_IT" : "italiano (Italia)", + "ja_JP" : "日本語 (日本)", + "ka_GE" : "ქართული (საქართველო)", + "ko_KR" : "한국어 (대한민국)", + "lt_LT" : "lietuvių (Lietuva)", + "lv_LV" : "latviešu (Latvija)", + "nl_BE" : "Nederlands (België)", + "pl_PL" : "polski (Polska)", + "pt_BR" : "português (Brasil)", + "ro_RO" : "română (România)", + "sl_SI" : "slovenščina (Slovenija)", + "tr_TR" : "Türkçe (Türkiye)", + "uk_UA" : "українська (Україна)", + "zh_CN" : "中文 (中国)", + "zh_TW" : "中文 (台灣)" + } } -} +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-de_DE.json b/ui-ngx/src/assets/locale/locale.constant-de_DE.json index acc2a5dee7..1a8bfa3ca8 100644 --- a/ui-ngx/src/assets/locale/locale.constant-de_DE.json +++ b/ui-ngx/src/assets/locale/locale.constant-de_DE.json @@ -1,2141 +1,9224 @@ { - "access": { - "unauthorized": "Nicht autorisiert", - "unauthorized-access": "Unautorisierter Zugriff", - "unauthorized-access-text": "Sie sollten sich anmelden, um Zugriff auf diese Daten zu erhalten!", - "access-forbidden": "Keine Zugangsberechtigung", - "access-forbidden-text": "Sie haben keine Zugangsberechtigung für diesen Bereich!
Versuchen Sie sich mit einem anderen Benutzer anzumelden um Zugriff auf diesen Bereich zu bekommen.", - "refresh-token-expired": "Sitzung ist abgelaufen", - "refresh-token-failed": "Sitzung kann nicht aktualisiert werden", - "permission-denied": "Zugriff verweigert", - "permission-denied-text": "Sie haben keine Berechtigung, um diesen Vorgang auszuführen!" - }, - "account": { - "account": "Benutzerkonto", - "notification-settings": "Benachrichtigungseinstellungen" - }, - "action": { - "activate": "Aktivieren", - "suspend": "Unterbrechen", - "save": "Speichern", - "saveAs": "Speichern unter", - "move": "Verschieben", - "cancel": "Abbrechen", - "ok": "OK", - "delete": "Löschen", - "add": "Hinzufügen", - "yes": "Ja", - "no": "Nein", - "update": "Aktualisieren", - "remove": "Löschen", - "search": "Suche", - "clear-search": "Suchanfrage löschen", - "assign": "Zuordnen", - "unassign": "Zuordnung aufheben", - "share": "Teilen", - "make-private": "Privat machen", - "make-public": "Öffentlich machen", - "apply": "Anwenden", - "apply-changes": "Änderungen übernehmen", - "edit-mode": "Bearbeitungsmodus", - "enter-edit-mode": "Zum Bearbeitungsmodus wechseln", - "decline-changes": "Änderungen nicht übernehmen", - "open": "Öffnen", - "decline": "Verwerfen", - "close": "Schließen", - "back": "Zurück", - "run": "Ausführen", - "sign-in": "Anmelden!", - "edit": "Bearbeiten", - "view": "Ansicht", - "create": "Erstellen", - "drag": "Ziehen", - "refresh": "Aktualisieren", - "undo": "Rückgängig machen", - "copy": "Kopieren", - "paste": "Einfügen", - "copy-reference": "Zeichen kopieren", - "paste-reference": "Zeichen einfügen", - "import": "Importieren", - "export": "Exportieren", - "share-via": "Teilen mit {{provider}}", - "select": "Auswählen", - "continue": "Fortsetzen", - "discard-changes": "Änderungen verwerfen", - "download": "Download", - "next": "Nächste", - "next-with-label": "Nächste: {{label}}", - "read-more": "Mehr dazu", - "hide": "Verstecken", - "done": "Erledigt", - "print": "Drucken", - "restore": "Wiederherstellen", - "confirm": "Bestätigen", - "more": "Mehr", - "less": "Weniger", - "skip": "Überspringen", - "send": "Senden", - "reset": "Zurücksetzen", - "show-more": "Zeige mehr", - "dont-show-again": "Nicht wieder anzeigen", - "see-documentation": "Siehe Dokumentation", - "clear": "Leeren" - }, - "aggregation": { - "aggregation": "Aggregation", - "function": "Datenaggregationsfunktion", - "limit": "Höchstwerte", - "group-interval": "Gruppierungsintervall", - "min": "Minimal", - "max": "Maximal", - "avg": "Durchschnitt", - "sum": "Summe", - "count": "Anzahl", - "none": "kein Wert" - }, - "admin": { - "settings": "Einstellungen", - "general": "Allgemein", - "general-settings": "Allgemeine Einstellungen", - "home-settings": "Home Einstellungen", - "home": "Home", - "outgoing-mail": "E-Mail Versand", - "outgoing-mail-settings": "Konfiguration des Postausgangsservers", - "system-settings": "Systemeinstellungen", - "test-mail-sent": "Test E-Mail wurde erfolgreich versendet!", - "base-url": "Basis-URL", - "base-url-required": "Basis-URL ist erforderlich.", - "prohibit-different-url": "Prohibit to use hostname from the client request headers", - "prohibit-different-url-hint": "This setting should be enabled for production environments. May cause security issues when disabled", - "device-connectivity": { - "device-connectivity": "Geräte Konnektivität", - "http-s": "HTTP(s)", - "mqtt-s": "MQTT(s)", - "coap-s": "COAP(s)", - "http": "HTTP", - "https": "HTTPs", - "mqtt": "MQTT", - "mqtts": "MQTTs", - "coap": "COAP", - "coaps": "COAPs", - "hint": "Falls Host und Port leer sind, werden die Standardwerte des Protokolls verwendet", - "host": "Host", - "port": "Port", - "port-pattern": "Port muss einen positiven Wert haben", - "port-range": "Port sollte im Bereich 1 und 65535 liegen." - }, - "mail-from": "E-Mail von", - "mail-from-required": "E-Mail von ist erforderlich.", - "smtp-protocol": "SMTP Protokoll", - "smtp-host": "SMTP Host", - "smtp-host-required": "SMTP Host ist erforderlich.", - "smtp-port": "SMTP Port", - "smtp-port-required": "Sie müssen einen SMTP Port angeben.", - "smtp-port-invalid": "Das ist kein gültiger SMTP Port.", - "timeout-msec": "Wartezeit (msec)", - "timeout-required": "Wartezeit ist erforderlich.", - "timeout-invalid": "Das ist keine gültige Wartezeit.", - "enable-tls": "TLS aktivieren", - "tls-version": "TLS-Version", - "enable-proxy": "Proxy aktivieren", - "proxy-host": "Proxy Host", - "proxy-host-required": "Proxy Host ist erforderlich.", - "proxy-port": "Proxy Port", - "proxy-port-required": "Proxy Port ist erforderlich.", - "proxy-port-range": "Proxy Port sollte im Bereich 1 bis 65535 sein.", - "proxy-user": "Proxy Benutzername", - "proxy-password": "Proxy Passwort", - "change-password": "Passwort ändern", - "send-test-mail": "Test E-Mail senden", - "use-system-mail-settings": "System E-Mail-Server Einstellungen benutzen", - "mail-templates": "E-Mail Vorlagen", - "mail-template-settings": "Einstellungen E-Mail Vorlagen", - "use-system-mail-template-settings": "System E-Mail Vorlagen benutzen", - "mail-template": { - "mail-template": "E-Mail Vorlage", - "test": "E-Mail Nachricht testen", - "activation": "Nachricht Benutzerkontoaktivierung", - "account-activated": "Nachricht Benutzerkonto aktiviert", - "account-lockout": "Nachricht zur Kontosperrung", - "reset-password": "Nachricht zum Zurücksetzen des Passworts", - "password-was-reset": "Nachricht für Passwort wurde zurückgesetzt", - "user-activated": "Nachricht für Benutzerkonto wurde aktiviert", - "user-registered": "Nachricht für Benutzer hat sich registriert", - "api-usage-state-enabled": "API-Nutzungsstatus wurde aktiviert", - "api-usage-state-warning": "Warnung zum API-Nutzungsstatus", - "api-usage-state-disabled": "API-Nutzungsstatus wurde deaktiviert", - "two-fa-verification": "2FA-Bestätigungsnachricht" - }, - "mail-subject": "E-Mail Betreff", - "mail-body": "E-Mail Nachricht", - "sms-provider": "SMS Anbieter", - "sms-provider-settings": "SMS Anbieter Einstellungen", - "use-system-sms-settings": "System SMS Anbieter benutzen", - "sms-provider-type": "SMS Anbieter Typ", - "sms-provider-type-required": "SMS Anbieter Typ ist erforderlich.", - "sms-provider-type-aws-sns": "Amazon SNS", - "sms-provider-type-twilio": "Twilio", - "sms-provider-type-smpp": "SMPP", - "security-settings": "Sicherheitseinstellungen", - "password-policy": "Kennwortrichtlinie", - "minimum-password-length": "Minimale Passwortlänge", - "minimum-password-length-required": "Minimale Passwortlänge ist erforderlich", - "minimum-password-length-range": "Die Mindestlänge des Passworts sollte zwischen 5 und 50 liegen", - "minimum-uppercase-letters": "Mindestanzahl von Großbuchstaben", - "minimum-uppercase-letters-range": "Die Mindestanzahl von Großbuchstaben kann nicht negativ sein", - "minimum-lowercase-letters": "Mindestanzahl von Kleinbuchstaben", - "minimum-lowercase-letters-range": "Die Mindestanzahl von Kleinbuchstaben kann nicht negativ sein", - "minimum-digits": "Mindestanzahl von Ziffern", - "minimum-digits-range": "Die Mindestanzahl von Ziffern kann nicht negativ sein", - "minimum-special-characters": "Mindestanzahl von Sonderzeichen", - "minimum-special-characters-range": "Die Mindestanzahl von Sonderzeichen kann nicht negativ sein", - "password-expiration-period-days": "Gültigkeitsdauer des Passworts in Tagen", - "password-expiration-period-days-range": "Die Gültigkeitsdauer des Passworts in Tagen kann nicht negativ sein", - "password-reuse-frequency-days": "Häufigkeit der Kennwortwiederverwendung in Tagen", - "password-reuse-frequency-days-range": "Die Häufigkeit der Kennwortwiederverwendung in Tagen kann nicht negativ sein", - "allow-whitespace": "Leerzeichen erlauben", - "general-policy": "Allgemeine Politik", - "max-failed-login-attempts": "Maximale Anzahl fehlgeschlagener Anmeldeversuche, bevor das Konto gesperrt wird", - "minimum-max-failed-login-attempts-range": "Die maximale Anzahl fehlgeschlagener Anmeldeversuche kann nicht negativ sein", - "user-lockout-notification-email": "Wenn das Benutzerkonto gesperrt ist, senden Sie eine Benachrichtigung per E-Mail" - }, - "alarm": { - "alarm": "Alarm", - "alarms": "Alarme", - "all-alarms": "Alle Alarme", - "select-alarm": "Alarm auswählen", - "no-alarms-matching": "Keine passenden Alarme zu '{{entity}}' wurden gefunden.", - "alarm-required": "Alarm ist erforderlich", - "alarm-filter": "Alarmfilter", - "filter": "Filter", - "alarm-status": "Alarm Status", - "alarm-status-list": "Alarm Statusliste", - "any-status": "Jeder Status", - "search-status": { - "ANY": "Jeder", - "ACTIVE": "Aktiv", - "CLEARED": "Gelöscht", - "ACK": "Bestätigt", - "UNACK": "Nicht bestätigt" - }, - "display-status": { - "ACTIVE_UNACK": "Nicht bestätigt aktiv", - "ACTIVE_ACK": "Bestätigt aktiv", - "CLEARED_UNACK": "Nicht bestätigt", - "CLEARED_ACK": "Bestätigung gelöscht" - }, - "no-alarms-prompt": "Keine Alarme gefunden", - "created-time": "Erstellungszeit", - "type": "Typ", - "severity": "Schwere", - "originator": "Urheber", - "originator-type": "Urheber-Typ", - "details": "Details", - "status": "Status", - "alarm-details": "Alarm-Details", - "start-time": "Startzeit", - "end-time": "Endzeit", - "ack-time": "Bestätigungszeit", - "clear-time": "Zeit gelöscht", - "duration": "Dauer", - "alarm-severity-list": "Alarm Schwere Liste", - "any-severity": "Jede Schwere", - "severity-critical": "Kritisch", - "severity-major": "Groß", - "severity-minor": "Klein", - "severity-warning": "Warnung", - "severity-indeterminate": "Unbestimmt", - "acknowledge": "Bestätigen", - "clear": "Löschen", - "delete": "Löschen", - "search": "Alarme suchen", - "selected-alarms": "{ count, plural, =1 {1 Alarm} other {# Alarme} } ausgewählt", - "no-data": "Keine Daten zum Anzeigen", - "polling-interval": "Alarmabfrageintervall (sec)", - "polling-interval-required": "Alarmabfrageintervall ist erforderlich.", - "min-polling-interval-message": "Mindestens 1 sec Abrufintervall ist zulässig.", - "aknowledge-alarms-title": "{ count, plural, =1 {1 Alarm} other {# Alarme} } bestätigen", - "aknowledge-alarms-text": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Alarm} other {# Alarme} } bestätigen möchten?", - "aknowledge-alarm-title": "Alarm bestätigen", - "aknowledge-alarm-text": "Möchten Sie den Alarm wirklich bestätigen?", - "selected-alarms-are-acknowledged": "Ausgewählte Alarme wurden bereits bestätigt", - "clear-alarms-title": "{ count, plural, =1 {1 Alarm} other {# Alarme} } löschen", - "clear-alarms-text": "Möchten Sie wirklich { count, plural, =1 {1 Alarm} other {# Alarme} } löschen?", - "clear-alarm-title": "Alarm löschen", - "clear-alarm-text": "Möchten Sie den Alarm wirklich löschen?", - "delete-alarms-title": "Lösche { count, plural, =1 {1 Alarm} other {# Alarme} }", - "delete-alarms-text": "Sind Sie sicher { count, plural, =1 {1 Alarm} other {# Alarme} } zu löschen?", - "selected-alarms-are-cleared": "Ausgewählte Alarme wurden bereits gelöscht", - "alarm-status-filter": "Alarm Status Filter", - "alarm-filter-title": "Alarmfilter", - "assigned": "Zugewiesen", - "filter-title": "Filter" - }, - "alias": { - "add": "Alias hinzufügen", - "edit": "Alias bearbeiten", - "name": "Aliasname", - "name-required": "Aliasname ist erforderlich", - "duplicate-alias": "Ein Alias mit demselben Namen ist bereits vorhanden.", - "filter-type-single-entity": "Einzelne Entität", - "filter-type-entity-list": "Entitätsliste", - "filter-type-entity-name": "Entitätsname", - "filter-type-entity-type": "Entitätstyp", - "filter-type-state-entity": "Entität aus dem Dashboard Status", - "filter-type-state-entity-description": "Entität aus den Dashboard Status Parametern", - "filter-type-asset-type": "Objekttyp", - "filter-type-asset-type-description": "Objekte vom Typ '{{assetTypes}}'", - "filter-type-asset-type-and-name-description": "Objekte vom Typ '{{assetTypes}}' und Name beginnend mit '{{prefix}}'", - "filter-type-device-type": "Gerätetyp", - "filter-type-device-type-description": "Geräte vom Typ '{{deviceTypes}}'", - "filter-type-device-type-and-name-description": "Geräte vom Typ '{{deviceTypes}}' und Name beginnend mit '{{prefix}}'", - "filter-type-entity-view-type": "Entitätsansichtstyp", - "filter-type-entity-view-type-description": "Entitätsansichten vom Typ '{{entityViewTypes}}'", - "filter-type-entity-view-type-and-name-description": "Entitätsansichten vom Typ '{{entityViewTypes}}' und Name beginnend mit '{{prefix}}'", - "filter-type-edge-type": "Randtyp", - "filter-type-edge-type-description": "Rand vom Typ '{{edgeTypes}}'", - "filter-type-relations-query": "Beziehungsabfrage", - "filter-type-relations-query-description": "{{entities}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-edge-search-query": "Edge-Abfrage", - "filter-type-edge-search-query-description": "Edge vom Typ {{edgeTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-asset-search-query": "Objektabfrage", - "filter-type-asset-search-query-description": "Objekte vom Typ {{assetTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-device-search-query": "Geräteabfrage", - "filter-type-device-search-query-description": "Geräte vom Typ {{deviceTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-entity-view-search-query": "Entitätsansichtsabfrage", - "filter-type-entity-view-search-query-description": "Entitätsansichten vom Typ {{entityViewTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "entity-filter": "Entitätsfilter", - "resolve-multiple": "Als mehrere Entitäten auflösen", - "filter-type": "Filtertyp", - "filter-type-required": "Filtertyp ist erforderlich.", - "entity-filter-no-entity-matched": "Es wurden keine Entitäten gefunden, die dem angegebenen Filter entsprechen.", - "no-entity-filter-specified": "Es wurde kein Entitätsfilter angegeben", - "root-state-entity": "Dashboard Status Entität als Wurzel verwenden", - "root-entity": "Wurzelentität", - "state-entity-parameter-name": "Parameter-Name der Statusentität", - "default-state-entity": "Standard Statusentität", - "default-entity-parameter-name": "Standardmäßig", - "max-relation-level": "Maximale Beziehungstiefe", - "unlimited-level": "Unbegrenzte Ebenen", - "state-entity": "Dashboard Status Entität", - "all-entities": "Alle Entitäten", - "any-relation": "Jede Beziehung" - }, - "asset": { - "all": "Alle", - "all-assets": "Alle Objekte", - "groups": "Gruppen", - "shared": "Geteilt", - "asset": "Objekt", - "assets": "Objekte", - "management": "Objektverwaltung", - "view-assets": "Objekte anzeigen", - "add": "Objekt hinzufügen", - "asset-type-max-length": "Objekttyp sollte kürzer als 256 Zeichen sein", - "assign-to-customer": "Einem Kunden zuordnen", - "assign-asset-to-customer": "Objekte dem Kunden zuordnen", - "assign-asset-to-customer-text": "Bitte wählen Sie die Objekte aus, die dem Kunden zugeordnet werden sollen", - "no-assets-text": "Keine Objekte gefunden", - "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Objekte zugeordnet werden sollen", - "public": "Öffentlich", - "assignedToCustomer": "Kunden zugeordnet", - "make-public": "Objekt öffentlich machen", - "make-private": "Objekt privat machen", - "unassign-from-customer": "Kundenzuordnung aufheben", - "delete": "Objekt löschen", - "asset-public": "Objekt ist öffentlich", - "asset-type": "Objekttyp", - "asset-type-required": "Objekttyp ist erforderlich.", - "select-asset-type": "Objekttyp auswählen", - "enter-asset-type": "Objekttyp bestätigen", - "any-asset": "Jedes Objekt", - "no-asset-types-matching": "Es wurden keine zu '{{entitySubtype}}' passenden Objekte gefunden.", - "asset-type-list-empty": "Keine Objekttypen ausgewählt.", - "asset-types": "Objekttypen", - "name": "Name", - "name-required": "Name ist erforderlich.", - "name-max-length": "Name sollte weniger als 256 Zeichen sein", - "label-max-length": "Label sollte weniger als 256 Zeichen sein", - "description": "Beschreibung", - "type": "Typ", - "type-required": "Typ ist erforderlich.", - "details": "Details", - "events": "Ereignisse", - "add-asset-text": "Neues Objekt hinzufügen", - "asset-details": "Objektdetails", - "assign-assets": "Objekte zuordnen", - "assign-assets-text": "Kunden { count, plural, =1 {1 Objekt} other {# Objekte} } zuordnen", - "assign-asset-to-edge-text":"Bitte wählen Sie die Objekte aus, die dem Edge zugeordnet werden sollen", - "delete-assets": "Objekte löschen", - "unassign-assets": "Objektzuordnungen aufheben", - "unassign-assets-action-title": "Kunden { count, plural, =1 {1 Objektzuordnung} other {# Objektzuordnungen} } aufheben", - "assign-new-asset": "Neues Objekt zuordnen", - "delete-asset-title": "Sind Sie sicher, dass Sie das Objekt '{{assetName}}' löschen möchten?", - "delete-asset-text": "Vorsicht, nach Bestätigung wird das Objekt und alle zugehörigen Daten nicht wiederherstellbar gelöscht.", - "delete-assets-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Objekt} other {# Objekte} } löschen möchten?", - "delete-assets-action-title": "{ count, plural, =1 {1 Objekt} other {# Objekte} } löschen", - "delete-assets-text": "Vorsicht, nach Bestätigung werden die ausgewählten Objekte und alle zugehörigen Daten nicht wiederherstellbar gelöscht", - "make-public-asset-title": "Sind Sie sicher, dass Sie das Objekt '{{assetName}}' öffentlich machen möchten?", - "make-public-asset-text": "Nach Bestätigung wird das Objekt und alle zugehörigen Daten anderen zugänglich gemacht.", - "make-private-asset-title": "Sind Sie sicher, dass Sie das Objekt '{{assetName}}' privat machen möchten?", - "make-private-asset-text": "Nach Bestätigung wird das Objekt und alle zugehörigen Daten privat und ist für andere nicht mehr zugänglich.", - "unassign-asset-title": "Sind Sie sicher, dass Sie die Zuordnung für das Objekt '{{assetName}}' aufheben möchten?", - "unassign-asset-text": "Nach Bestätigung wird die Zuordnung des Objekts aufgehoben und es ist für den Kunden nicht mehr zugänglich.", - "unassign-asset": "Zuordnung des Objekts aufheben", - "unassign-assets-title": "Möchten Sie die Zuordnung von { count, plural, =1 {1 Objekt} other {# Objekte} } aufheben?", - "unassign-assets-text": "Nach Bestätigung wird die Zuordnung der ausgewählten Objekte aufgehoben und sie sind für den Kunden nicht mehr zugänglich.", - "copyId": "Objekt-ID kopieren", - "idCopiedMessage": "Objekt-ID wurde in die Zwischenablage kopiert", - "select-asset": "Objekt auswählen", - "no-assets-matching": "Es wurden keine zu '{{entity}}' passenden Objekte gefunden.", - "asset-required": "Objekt ist erforderlich", - "name-starts-with": "Name des Objekts beginnt mit", - "label": "Bezeichnung", - "assign-asset-to-edge": "Objekte dem Rand zuordnen", - "unassign-asset-from-edge": "Objekte von Rand entfernen", - "unassign-asset-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für das Objekt '{{assetName}}' aufheben möchten?", - "unassign-asset-from-edge-text": "Nach Bestätigung wird die Zuordnung des Objekts aufgehoben und es ist für den Kunden nicht mehr zugänglich.", - "unassign-assets-from-edge-action-title": "Rand { count, plural, =1 {1 Objektzuordnung} other {# Objektzuordnungen} } aufheben", - "unassign-assets-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für das Objekt '{{assetName}}' wirklich aufheben möchten?", - "unassign-assets-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Objekte nicht zugewiesen und sind für den Rand nicht zugänglich." - }, - "attribute": { - "attributes": "Eigenschaften", - "latest-telemetry": "Neueste Telemetrie", - "no-latest-telemetry": "Keine Telemetriedaten", - "attributes-scope": "Entitätseigenschaftsbereich", - "scope-telemetry": "Telemetrie", - "scope-latest-telemetry": "Neueste Telemetrie", - "scope-client": "Client Eigenschaften", - "scope-server": "Server Eigenschaften", - "scope-shared": "Gemeinsame Eigenschaften", - "add": "Eigenschaft hinzufügen", - "add-attribute-prompt": "Bitte Attribut hinzufügen", - "key": "Schlüssel", - "key-max-length": "Der Schlüssel sollte weniger als 256 Zeichen haben", - "last-update-time": "Datum der letzten Aktualisierung", - "key-required": "Eigenschaftsschlüssel ist erforderlich.", - "value": "Wert", - "value-required": "Eigenschaftswert ist erforderlich.", - "telemetry-key-required": "Telemetrieschlüssel ist erforderlich", - "telemetry-value-required": "Telemetriewert ist erforderlich", - "delete-attributes-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Eigenschaft} other {# Eigenschaften} } löschen möchten?", - "delete-attributes-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Eigenschaften entfernt.", - "delete-attributes": "Eigenschaften löschen", - "enter-attribute-value": "Geben Sie den Eigenschaftswert ein", - "show-on-widget": "Im Widget anzeigen", - "widget-mode": "Widget-Modus", - "next-widget": "Nächstes Widget", - "prev-widget": "Vorheriges Widget", - "add-to-dashboard": "Zum Dashboard hinzufügen", - "add-widget-to-dashboard": "Widget zum Dashboard hinzufügen", - "selected-attributes": "{ count, plural, =1 {1 Eigenschaft} other {# Eigenschaften} } ausgewählt", - "selected-telemetry": "{ count, plural, =1 {1 Telemetrieeinheit } other {# Telemetrieeinheiten} } ausgewählt", - "no-attributes-text": "Keine Attribute gefunden", - "no-telemetry-text": "Keine Telemetriedaten gefunden", - "copy-key": "Schlüssel kopieren", - "add-telemetry": "Telemetriedaten hinzufügen", - "copy-value": "Wert kopieren" - }, - "audit-log": { - "audit": "Audit", - "audit-logs": "Audit-Protokolle", - "timestamp": "Zeitstempel", - "entity-type": "Entitätstype", - "entity-name": "Entitätsname", - "user": "User", - "type": "Typ", - "status": "Status", - "details": "Details", - "type-added": "Hinzugefügt", - "type-deleted": "Gelöscht", - "type-updated": "Aktualisiert", - "type-attributes-updated": "Eigenschaften aktualisiert", - "type-attributes-deleted": "Eigenschaften gelöscht", - "type-rpc-call": "RPC Aufruf", - "type-credentials-updated": "Anmeldeinformationen wurden aktualisiert", - "type-assigned-to-customer": "Kunden Zuordnung", - "type-unassigned-from-customer": "Kunden Zuordnung aufgehoben", - "type-assigned-to-edge": "Zum Edge hinzugefügt", - "type-unassigned-from-edge": "Vom Edge entfernt", - "type-activated": "Aktiviert", - "type-suspended": "Ausgesetzt", - "type-credentials-read": "Anmeldeinformationen gelesen", - "type-attributes-read": "Eigenschaften gelesen", - "type-added-to-entity-group": "Zur Gruppe hinzugefügt", - "type-removed-from-entity-group": "Aus der Gruppe entfernt", - "type-relation-add-or-update": "Beziehung aktualisiert", - "type-relation-delete": "Beziehung gelöscht", - "type-relations-delete": "Alle Beziehungen gelöscht", - "type-alarm-ack": "Bestätigt", - "type-alarm-clear": "Gelöscht", - "type-alarm-assign": "Zugeweisen", - "type-alarm-unassign": "nicht zugewiesen", - "type-added-comment": "Kommentar hinzugefügt", - "type-updated-comment": "Kommentar aktualisiert", - "type-deleted-comment": "Kommentar gelöscht", - "type-rest-api-rule-engine-call": "Regelkette REST API Aufruf", - "type-made-public": "Öffentlich machen", - "type-made-private": "Privat machen", - "type-login": "Anmeldung", - "type-logout": "Ausloggen", - "type-lockout": "Aussperrung", - "status-success": "Erfolg", - "status-failure": "Fehler", - "audit-log-details": "Audit-Protokoll Details", - "no-audit-logs-prompt": "Keine Protokolle gefunden", - "action-data": "Aktionsdaten", - "failure-details": "Fehlerdetails", - "search": "Audit-Protokolle durchsuchen", - "clear-search": "Suche leeren", - "type-assigned-from-tenant": "Vom Tenant zugewiesen", - "type-assigned-to-tenant": "Dem Tenant zugewiesen", - "type-provision-success": "Gerätebereitstellung erfolgreich", - "type-provision-failure": "Gerätebereitstellung fehlgeschlagen", - "type-timeseries-updated": "Telemetriedaten aktualisiert", - "type-timeseries-deleted": "Telemetriedaten gelöscht", - "type-owner-changed": "Besitzer wurde gewechselt", - "type-sms-sent": "SMS gesendet" - }, - "confirm-on-exit": { - "message": "Sie haben nicht gespeicherte Änderungen. Möchten Sie diese Seite wirklich verlassen?", - "html-message": "Sie haben nicht gespeicherte Änderungen.
Möchten Sie diese Seite wirklich verlassen?", - "title": "Nicht gespeicherte Änderungen" - }, - "contact": { - "country": "Land", - "city": "Stadt", - "state": "Bundesland", - "postal-code": "Postleitzahl", - "postal-code-invalid": "Ungültiges Format der Postleitzahl.", - "address": "Adresse", - "address2": "Adresse 2", - "phone": "Telefon", - "email": "E-Mail", - "no-address": "Keine Adresse", - "state-max-length": "Staat sollte weniger als 256 Zeichen haben", - "phone-max-length": "Telefonnummer sollte weniger als 256 Zeichen haben", - "city-max-length": "Ort sollte weniger als 256 Zeichen haben" - }, - "common": { - "username": "Benutzername", - "password": "Passwort", - "enter-username": "Benutzername eingeben", - "enter-password": "Passwort eingeben", - "enter-search": "Suche eingeben", - "created-time": "Erstellungszeit", - "loading": "wird geladen...", - "proceed": "Fortfahren", - "open-details-page": "Detailseite öffnen", - "not-found": "Nicht gefunden", - "documentation": "Dokumentation" - }, - "content-type": { - "json": "Json", - "text": "Text", - "binary": "Binär (Base64)" - }, - "customer": { - "all": "Alle", - "all-customers": "Alle Kunden", - "groups": "Gruppen", - "shared": "Geteilt", - "hierarchy": "Hierarchie", - "customer": "Kunde", - "customers": "Kunden", - "management": "Kundenverwaltung", - "dashboard": "Kunden Dashboard", - "dashboards": "Kunden Dashboards", - "devices": "Kundengeräte", - "entity-views": "Kunden Entitätsansichten", - "assets": "Kundenobjekte", - "public-dashboards": "Öffentliche Dashboards", - "public-devices": "Öffentliche Geräte", - "public-assets": "Öffentliche Objekte", - "public-entity-views": "Öffentliche Entitätsansichten", - "add": "Kunde hinzufügen", - "delete": "Kunde löschen", - "manage-customer-users": "Kundenbenutzer verwalten", - "manage-customer-devices": "Kundengeräte verwalten", - "manage-customer-dashboards": "Kunden-Dashboards verwalten", - "manage-public-devices": "Öffentliche Geräte verwalten", - "manage-public-dashboards": "Öffentliche Dashboards verwalten", - "manage-customer-assets": "Kundenobjekte verwalten", - "manage-public-assets": "Öffentliche Objekte verwalten", - "manage-public-edges": "Öffentliche Rand verwalten", - "add-customer-text": "Neuen Kunden hinzufügen", - "no-customers-text": "Keine Kunden gefunden", - "customer-details": "Kundendetails", - "delete-customer-title": "Sind Sie sicher, dass der Kunde '{{customerTitle}}' gelöscht werden soll?", - "delete-customer-text": "Vorsicht, nach Bestätigung wird der Kunde und alle zugehörigen Daten nicht wiederherstellbar gelöscht.", - "delete-customers-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Kunde} other {# Kunden} } löschen möchten?", - "delete-customers-action-title": "{ count, plural, =1 {1 Kunde} other {# Kunden} } löschen", - "delete-customers-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Kunden und alle zugehörigen Daten nicht wiederherstellbar gelöscht.", - "manage-users": "User verwalten", - "manage-assets": "Objekte verwalten", - "manage-devices": "Geräte verwalten", - "manage-dashboards": "Dashboards verwalten", - "title": "Titel", - "title-required": "Titel ist erforderlich.", - "title-max-length": "Titel sollte weniger asl 256 Zeichen haben", - "description": "Beschreibung", - "details": "Details", - "events": "Ereignisse", - "copyId": "Kunden-ID kopieren", - "idCopiedMessage": "Kunden-ID wurde in die Zwischenablage kopiert", - "select-customer": "Kunden auswählen", - "no-customers-matching": "Keine Kunden für '{{entity}}' gefunden.", - "customer-required": "Kunde ist erforderlich", - "select-default-customer": "Wählen Sie den Standardkunden aus.", - "default-customer": "Standardkunde", - "edge-instances": "Kunden Rand", - "default-customer-required": "Ein Standardkunde ist erforderlich, um das Dashboard auf Mandantenebene zu testen." - }, - "datetime": { - "date-from": "Datum von", - "time-from": "Zeit von", - "date-to": "Datum bis", - "time-to": "Zeit bis" - }, - "dashboard": { - "dashboard": "Dashboard", - "dashboards": "Dashboards", - "management": "Dashboardverwaltung", - "view-dashboards": "Dashboards anzeigen", - "add": "Dashboard hinzufügen", - "assign-dashboard-to-customer": "Dashboard(s) dem Kunden zuordnen", - "assign-dashboard-to-customer-text": "Bitte wählen Sie die Dashboards aus, die Sie dem Kunden zuordnen möchten", - "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Dashboards zugeordnet werden sollen", - "assign-to-customer": "Kunden zuordnen", - "unassign-from-customer": "Zuordnung zum Kunden aufheben", - "make-public": "Dashboard öffentlich machen", - "make-private": "Dashboard privat machen", - "manage-assigned-customers": "Zugeordnete Kunden verwalten", - "assigned-customers": "Zugeordnete Kunden", - "assign-to-customers": "Dashboard(s) zu Kunden zuweisen", - "assign-to-customers-text": "Bitte wählen Sie den Kunden aus, dem Sie das Dashboard(s) zuweisen möchten", - "unassign-from-customers": "Zuordnung von Dashboard(s) zu Kunden aufheben", - "unassign-from-customers-text": "Bitte wählen Sie die Kunden aus, für die die Zuordnung von Dashboard(s) aufgehoben werden soll", - "no-dashboards-text": "Keine Dashboard(s) gefunden", - "no-widgets": "Keine Widgets konfiguriert", - "add-widget": "Neues Widget hinzufügen", - "title": "Titel", - "select-widget-title": "Widget auswählen", - "select-widget-subtitle": "Liste der verfügbaren Widget-Typen", - "delete": "Dashboard löschen", - "title-required": "Titel ist erforderlich.", - "description": "Beschreibung", - "details": "Details", - "dashboard-details": "Dashboard-Details", - "add-dashboard-text": "Neues Dashboard hinzufügen", - "assign-dashboards": "Dashboards zuweisen", - "assign-new-dashboard": "Neues Dashboard zuweisen", - "assign-dashboards-text": "Zuordnen { count, plural, =1 {1 Dashboard} other {# Dashboards} } zu Kunden", - "unassign-dashboards-action-text": "Zuordnung { count, plural, =1 {1 Dashboard} other {# Dashboards} } vom Kunden aufheben", - "delete-dashboards": "Dashboards löschen", - "unassign-dashboards": "Zuordnung von Dashboards aufheben", - "unassign-dashboards-action-title": "Zuordnung { count, plural, =1 {1 Dashboard} other {# Dashboards} } vom Kunden aufheben", - "delete-dashboard-title": "Sind Sie sicher, dass Sie das Dashboard '{{dashboardTitle}}' löschen möchten?", - "delete-dashboard-text": "Vorsicht, nach Bestätigung werden das Dashboard und alle zugehörigen Daten nicht mehr wiederhergestellt.", - "delete-dashboards-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Dashboard} other {# Dashboards} } löschen möchten?", - "delete-dashboards-action-title": "Löschen { count, plural, =1 {1 Dashboard} other {# Dashboards} }", - "delete-dashboards-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Dashboards entfernt und alle zugehörigen Daten nicht mehr wiederhergestellt.", - "unassign-dashboard-title": "Sind Sie sicher, dass Sie die Zuordnung zum Dashboard '{{dashboardTitle}}' aufheben möchten?", - "unassign-dashboard-text": "Nach der Bestätigung wird die Zuordnung des Dashboards aufgehoben und es ist für den Kunden nicht mehr zugänglich.", - "unassign-dashboard": "Zuordnung zum Kunden aufheben", - "unassign-dashboards-title": "Sind Sie sicher, dass Sie die Zuordug aufheben möchten { count, plural, =1 {1 Dashboard} other {# Dashboards} }?", - "unassign-dashboards-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Dashboards aufgehoben und sie sind für den Kunden nicht mehr zugänglich.", - "public-dashboard-title": "Dashboard wurde veröffentlicht", - "public-dashboard-text": "Ihr Dashboard {{dashboardTitle}} ist jetzt öffentlich und über nächste Öffentlichkeit zugänglich link:", - "public-dashboard-notice": "Note: Vergessen Sie nicht, verwandte Geräte öffentlich zu machen, um auf Ihre Daten zugreifen zu können.", - "make-private-dashboard-title": "Sind Sie sicher, dass Sie das Dashboard '{{dashboardTitle}}' privatisieren möchten?", - "make-private-dashboard-text": "Nach der Bestätigung wird das Dashboard privatisiert und ist für andere nicht zugänglich.", - "make-private-dashboard": "Dashboard privatisieren", - "socialshare-text": "'{{dashboardTitle}}' Bereitgestellt vom ThingsBoard", - "socialshare-title": "'{{dashboardTitle}}' Bereitgestellt vom ThingsBoard", - "select-dashboard": "Dashboard auswählen", - "no-dashboards-matching": "Es wurden keine passenden Dashboards '{{entity}}' gefunden.", - "dashboard-required": "Dashboard ist erforderlich.", - "select-existing": "Existierendes Dashboard auswählen", - "create-new": "Neues Dashboard erstellen", - "new-dashboard-title": "Neuer Dashboard Titel", - "open-dashboard": "Dashboard öffnen", - "set-background": "Hintergrund einstellen", - "background-color": "Hintergrundfarbe", - "background-image": "Hintergrundbild", - "background-size-mode": "Hintergrundgrößenmodus", - "no-image": "Kein Bild ausgewählt", - "drop-image": "Legen Sie ein Bild ab oder klicken Sie, um eine hochzuladende Datei auszuwählen.", - "settings": "Einstellungen", - "columns-count": "Spalten zählen", - "columns-count-required": "Die Anzahl der Spalten ist erforderlich.", - "min-columns-count-message": "Es müssen mindestens 10 Spalten vorhanden sein.", - "max-columns-count-message": "Es sind maximal 100 Spalten zulässig.", - "widgets-margins": "Abstand zwischen den Widgets", - "horizontal-margin": "Horizontaler Abstand", - "horizontal-margin-required": "Horizontaler Abstandswert ist erforderlich.", - "min-horizontal-margin-message": "Der horizontale Abstandswert muss mindestens 0 betragen.", - "max-horizontal-margin-message": "Der horizontale Abstandswert beträgt maximal 50.", - "vertical-margin": "Vertikaler Abstand", - "vertical-margin-required": "Vertikaler Abstandswert ist erforderlich.", - "min-vertical-margin-message": "Der vertikale Abstandswert muss mindestens 0 betragen.", - "max-vertical-margin-message": "Der vertikale Abstandswert beträgt maximal 50.", - "autofill-height": "Layouthöhe automatisch füllen", - "mobile-layout": "Mobile Layouteinstellungen", - "mobile-row-height": "Mobile Zeilenhöhe, px", - "mobile-row-height-required": "Ein mobiler Zeilenhöhenwert ist erforderlich.", - "min-mobile-row-height-message": "Der Mindestwert für die mobile Zeilenhöhe beträgt 5 Pixel.", - "max-mobile-row-height-message": "Der Höchstwert für die mobile Zeilenhöhe beträgt 200 Pixel.", - "display-title": "Display Dashboard Titel", - "toolbar-always-open": "Werkzeugleiste geöffnet lassen", - "title-color": "Titelfarbe ", - "display-dashboards-selection": "Auswahl der Dashboards anzeigen", - "display-entities-selection": "Auswahl der Einheiten zulassen", - "display-dashboard-timewindow": "Zeitfenster anzeigen", - "display-dashboard-export": "Export anzeigen", - "import": "Dashboard importieren", - "export": "Dashboard exportieren", - "export-failed-error": "Dashboard kann nicht exportiert werden: {{error}}", - "create-new-dashboard": "Neues Dashboard erstellen", - "dashboard-file": "Dashboard-Datei", - "invalid-dashboard-file-error": "Dashboard kann nicht importiert werden: Ungültige Dashboard-Datenstruktur.", - "dashboard-import-missing-aliases-title": "Konfigurieren Sie die von importierten Dashboards verwendeten Aliasnamen", - "create-new-widget": "Neues Widget erstellen", - "import-widget": "Widget importieren", - "widget-file": "Widget-Datei", - "invalid-widget-file-error": "Widget kann nicht importiert werden: Ungültige Widget-Datenstruktur.", - "widget-import-missing-aliases-title": "Konfigurieren Sie die von importierten Widgets verwendeten Aliase", - "open-toolbar": "Dashboard-Werkzeugleiste öffnen", - "close-toolbar": "Werkzeugleiste schließen", - "configuration-error": "Konfigurationsfehler", - "alias-resolution-error-title": "Konfigurationsfehler für Dashboard-Aliasnamen", - "invalid-aliases-config": "Es konnten keine Geräte gefunden werden, die mit dem Aliase-Filter übereinstimmen.
Bitte wenden Sie sich an Ihren Administrator, um dieses problem zu beheben.", - "select-devices": "Geräte auswählen", - "assignedToCustomer": "Dem Kunden zugewiesen", - "assignedToCustomers": "Kunden zugwiesen", - "public": "Öffentlich", - "public-link": "Öffentlicher Link", - "copy-public-link": "Öffentlichen Link kopieren", - "public-link-copied-message": "Der öffentliche Link des Dashboards wurde in die Zwischenablage kopiert", - "manage-states": "Dashboard-Status verwalten", - "states": "Dashboard-Status", - "search-states": "Dashboard-Status suchen", - "selected-states": "{ count, plural, =1 {1 dashboard state} other {# dashboard states} } ausgewählt", - "edit-state": "Dashboard-Status bearbeiten", - "delete-state": "Dashboard-Status löschen", - "add-state": "Dashboard-Status hinzufügen", - "state": "Dashboard-Status", - "state-name": "Name", - "state-name-required": "Name des Dashboard-Status ist erforderlich.", - "state-id": "Status-Id", - "state-id-required": "Dashboard-Status-ID ist erforderlich.", - "state-id-exists": "Dashboard-Status mit derselben ID ist bereits vorhanden .", - "is-root-state": "Grundzustand", - "delete-state-title": "Dashboard-Status löschen", - "delete-state-text": "Sind Sie sicher, dass Sie den Dashboard-Status '{{stateName}}' löschen möchten?", - "show-details": "Details anzeigen", - "hide-details": "Details ausblenden", - "select-state": "Soll-Zustand auswählen", - "state-controller": "Zustandssteuerung", - "unassign-dashboard-from-edge-text": "Nach der Bestätigung wird die Zuordnung des Dashboards aufgehoben und es ist für der Rand nicht mehr zugänglich.", - "unassign-dashboards-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Dashboards aufgehoben und sie sind für den Rand nicht mehr zugänglich.", - "assign-dashboard-to-edge": "Dashboard(s) dem Rand zuordnen", - "assign-dashboard-to-edge-text": "Bitte wählen Sie die Dashboards aus, die Sie dem Rand zuordnen möchten" - }, - "datakey": { - "settings": "Einstellungen", - "advanced": "Erweitert", - "label": "Bezeichnung", - "color": "Farbe", - "units": "Maßeinheit die neben dem Wert angezeigt wird", - "decimals": "Anzahl der Stellen nach dem Komma", - "data-generation-func": "Daten generieren", - "use-data-post-processing-func": "Datenverarbeitungsfunktion verwenden", - "configuration": "Datenschlüsselkonfiguration", - "timeseries": "Zeitreihe", - "attributes": "Eigenschaften", - "alarm": "Alarmfelder", - "timeseries-required": "Entity-Zeitreihen sind erforderlich.", - "timeseries-or-attributes-required": "Entity-Zeitreihen/Eigenschaften sind erforderlich.", - "maximum-timeseries-or-attributes": "Maximum { count, plural, =1 {1 Zeitreihe/Eigenschaft ist erlaubt} other {# Zeitreihen/Eigenschaften sind erlaubt} }.", - "alarm-fields-required": "Alarmfelder sind erforderlich.", - "function-types": "Funktionsarten", - "function-types-required": "Funktionstypen sind erforderlich.", - "maximum-function-types": "Maximal { count, plural, =1 {1 Funktionstyp ist erlaubt} other {# Funktionstypen sind erlaubt} }.", - "time-description": "Zeitstempel des aktuellen Wertes;", - "value-description": "Der aktuelle Wert;", - "prev-value-description": "Ergebnis des vorherigen Funktionsaufrufs;", - "time-prev-description": "Zeitmarke des vorherigen Wertes;", - "prev-orig-value-description": "Ursprünglicher vorheriger Wert;" - }, - "datasource": { - "type": "Datenquellentyp", - "name": "Name", - "add-datasource-prompt": "Bitte Datenquelle hinzufügen" - }, - "details": { - "edit-mode": "Bearbeitungsmodus", - "toggle-edit-mode": "Bearbeitungsmodus umschalten" - }, - "device": { - "device": "Gerät", - "device-required": "Gerät ist erforderlich.", - "devices": "Geräte", - "management": "Geräte verwalten", - "view-devices": "Geräte anzeigen", - "device-alias": "Geräte-Alias", - "aliases": "Gerätealiasnamen", - "no-alias-matching": "'{{alias}}' nicht gefunden.", - "no-aliases-found": "Keine Aliase gefunden.", - "no-key-matching": "'{{key}}' nicht gefunden.", - "no-keys-found": "Keine Schlüssel gefunden.", - "create-new-alias": "Neues Alias erstellen!", - "create-new-key": "Neuen Schlüssel erstellen!", - "duplicate-alias-error": "Doppelter Alias gefunden '{{alias}}'.
Gerätealiasnamen müssen innerhalb des Dashboards eindeutig sein.", - "configure-alias": "Alias '{{alias}}' konfigurieren", - "no-devices-matching": "Keine passenden Geräte '{{entity}}' gefunden.", - "alias": "Alias", - "alias-required": "Geräte-Alias ist erforderlich.", - "remove-alias": "Geräte-Alias entfernen", - "add-alias": "Geräte-Alias hinzufügen", - "name-starts-with": "Gerätename beginnt mit", - "device-list": "Geräteliste", - "use-device-name-filter": "Filter verwenden", - "device-list-empty": "Keine Geräte ausgewählt.", - "device-name-filter-required": "Der Gerätenamefilter ist erforderlich.", - "device-name-filter-no-device-matched": "Keine Geräte beginnend mit '{{device}}' gefunden.", - "add": "Gerät hinzufügen", - "assign-to-customer": "Kunden zuordnen", - "assign-device-to-customer": "Gerät(e) dem Kunden zuordnen", - "assign-device-to-customer-text": "Bitte wählen Sie die Geräte aus, die Sie dem Kunden zuordnen möchten", - "make-public": "Gerät veröffentlichen", - "make-private": "Gerät privatisieren", - "no-devices-text": "Keine Geräte gefunden", - "assign-to-customer-text": "Bitte wählen Sie einen Kunden aus, dem die Geräte zugeordnet werden sollen", - "device-details": "Gerätedetails", - "add-device-text": "Neues Gerät hinzufügen", - "credentials": "Zugangsdaten", - "manage-credentials": "Zugangsdaten verwalten", - "delete": "Gerät löschen", - "assign-devices": "Gerät zuordnen", - "assign-devices-text": "{ count, plural, =1 {1 Gerät} other {# Geräte} } dem Kunden zuordnen", - "delete-devices": "Geräte löschen", - "unassign-from-customer": "Zuordnung zum Kunden aufheben", - "unassign-devices": "Nicht zugeordnete Geräte", - "unassign-devices-action-title": "Zuordnung von { count, plural, =1 {1 Gerät} other {# Geräte} } zum Kunden aufheben", - "assign-new-device": "Neues Gerät zuordnen", - "make-public-device-title": "Sind Sie sicher, dass Sie das Gerät '{{deviceName}}' öffentlich machen möchten?", - "make-public-device-text": "Nach der Bestätigung werden das Gerät und dessen Daten öffentlich und für andere zugänglich.", - "make-private-device-title": "Sind Sie sicher, dass Sie das Gerät '{{deviceName}}' privat machen möchten?", - "make-private-device-text": "Nach der Bestätigung werden das Gerät und dessen Daten privat und sind für andere nicht mehr zugänglich.", - "view-credentials": "Zugangsdaten anzeigen", - "delete-device-title": "Möchten Sie das Gerät '{{deviceName}}' wirklich löschen?", - "delete-device-text": "Vorsicht, nach Bestätigung werden das Gerät und alle zugehörigen Daten nicht mehr wiederhergestellt.", - "delete-devices-title": "Sind Sie sicher, dass Sie löschen möchten { count, plural, =1 {1 Gerät} other {# Geräte} }?", - "delete-devices-action-title": "Löschen { count, plural, =1 {1 Gerät} other {# Geräte} }", - "delete-devices-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Geräte entfernt und alle zugehörigen Daten werden nicht mehr wiederhergestellt.", - "unassign-device-title": "Sind Sie sicher, dass Sie die Zuordnung zum Gerät '{{deviceName}}' wirklich aufheben möchten?", - "unassign-device-text": "Nach der Bestätigung ist das Gerät nicht zugeordnet und für den Kunden nicht zugänglich.", - "unassign-device": "Nicht zugeordnete Geräte", - "unassign-devices-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Gerät} other {# Geräte} } nicht mehr zuordnen möchten?", - "unassign-devices-text": "Nach der Bestätigung werden alle ausgewählten Geräte nicht zugewiesen und sind für den Kunden nicht zugänglich.", - "device-credentials": "Geräte Zugangsdaten", - "credentials-type": "Art der Zugangsdaten", - "access-token": "Zugangs-Token", - "access-token-required": "Zugangs-Token ist erforderlich.", - "access-token-invalid": "Die Länge des Zugangs-Tokens muss zwischen 1 und 32 Zeichen betragen.", - "secret": "Geheimnis", - "secret-required": "Geheimnis ist erforderlich.", - "device-type": "Gerätetyp", - "device-type-required": "Gerätetyp ist erforderlich.", - "select-device-type": "Gerätetyp auswählen", - "enter-device-type": "Gerätetyp eingeben", - "any-device": "Jedes Gerät", - "no-device-types-matching": "Keine passenden Gerätetypen '{{entitySubtype}}' gefunden.", - "device-type-list-empty": "Kein Gerätetyp ausgewählt.", - "device-types": "Gerätetypen", - "name": "Name", - "name-required": "Name ist erforderlich.", - "description": "Beschreibung", - "events": "Ereignisse", - "details": "Details", - "copyId": "Geräte-ID kopieren", - "copyAccessToken": "Zugangs-Token kopieren", - "idCopiedMessage": "Geräte-ID wurde in die Zwischenablage kopiert", - "accessTokenCopiedMessage": "Geräte-Zugangs-Token wurde in die Zwischenablage kopiert", - "assignedToCustomer": "Dem Kunden zuordnen", - "unable-delete-device-alias-title": "Geräte-Alias kann nicht gelöscht werden", - "unable-delete-device-alias-text": "Geräte-Alias '{{deviceAlias}}' kann nicht gelöscht werden, da er von den folgenden Widgets verwendet wird:
{{widgetsList}}", - "is-gateway": "Ist ein Gateway", - "public": "Öffentlich", - "device-public": "Gerät ist öffentlich", - "select-device": "Gerät auswählen", - "assign-device-to-edge-text":"Bitte wählen Sie die Geräte aus, die Sie dem Rand zuordnen möchten", - "unassign-device-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung zum Gerät '{{deviceName}}' wirklich aufheben möchten?", - "unassign-device-from-edge-text": "Nach der Bestätigung ist das Gerät nicht zugeordnet und für den Kunden nicht zugänglich.", - "unassign-devices-from-edge-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Gerät} other {# Geräte} } nicht mehr zuordnen möchten?", - "unassign-devices-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Geräte nicht zugewiesen und sind für den Rand nicht zugänglich." - }, - "dialog": { - "close": "Dialog schließen" - }, - "edge": { - "edge": "Edge", - "edge-instances": "Kanteninstanzen", - "edge-file": "Edge-Datei", - "management": "Edge verwalten", - "no-edges-matching": "Keine passenden Edge '{{entity}}' gefunden.", - "add": "Edge hinzufügen", - "no-edges-text": "Keine Edge gefunden.", - "edge-details": "Details der Edge", - "add-edge-text": "Neue Edge hinzufügen", - "delete": "Edge löschen", - "delete-edge-title": "Möchten Sie des Edges wirklich löschen '{{edgeName}}'?", - "delete-edge-text": "Seien Sie vorsichtig, nach der Bestätigung werden der Edge und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-edges-title": "Sind Sie sicher, dass Sie die Edge löschen möchten { count, plural, =1 {1 Edge} other {# Edges} }?", - "delete-edges-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Edge entfernt und alle zugehörigen Daten werden nicht wiederhergestellt.", - "name": "Name", - "name-starts-with": "Der Kantenname beginnt mit", - "name-required": "Name ist erforderlich.", - "description": "Beschreibung", - "details": "Details", - "events": "Ereignisse", - "copy-id": "Regelketten-ID kopieren", - "id-copied-message": "Regelketten-ID wurde in die Zwischenablage kopiert", - "sync": "Edge synchonisieren", - "edge-required": "Edge ist erforderlich.", - "edge-type": "Edgetyp", - "edge-type-required": "Edgetyp ist erforderlich.", - "event-action": "Ereignisaktion", - "entity-id": "Entität ID", - "select-edge-type": "Edgetyp auswählen", - "assign-to-customer": "Einem Kunden zuordnen", - "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Edge zugeordnet werden sollen", - "assign-edge-to-customer": "Edge dem Kunden zuordnen", - "assign-edge-to-customer-text": "Bitte wählen Sie die Edge aus, die dem Kunden zugeordnet werden sollen", - "assignedToCustomer": "Dem Kunden zugewiesen", - "edge-public": "Edge ist öffentlich", - "assigned-to-customer": "Kunden Zuordnung", - "unassign-from-customer": "Kunden Zuordnung aufgehoben", - "unassign-edge-title": "Sind Sie sicher, dass Sie die Zuordnung zum Edge '{{edgeName}}' wirklich aufheben möchten?", - "unassign-edge-text": "Nach der Bestätigung ist der Edge nicht zugeordnet und für den Kunden nicht zugänglich.", - "unassign-edges-title": "Sind Sie sicher, dass Sie die Zuordnung aufheben möchten { count, plural, =1 {1 Edge} other {# Edges} }?", - "unassign-edges-text": "Nach der Bestätigung werden alle ausgewählten Kanten nicht zugewiesen und sind für den Kunden nicht zugänglich.", - "make-public": "Edge öffentlich machen", - "make-public-edge-title": "Sind Sie sicher, dass Sie der Edge '{{edgeName}}' öffentlich machen möchten?", - "make-public-edge-text": "Nach Bestätigung wird der Rabd und alle zugehörigen Daten anderen zugänglich gemacht.", - "make-private": "Edge privat machen", - "public": "Öffentlich", - "make-private-edge-title": "Sind Sie sicher, dass Sie der Edge '{{edgeName}}' privat machen möchten?", - "make-private-edge-text": "Nach der Bestätigung werden der Edge und dessen Daten privat und sind für andere nicht mehr zugänglich.", - "import": "Edge importieren", - "label": "Bezeichnung", - "load-entity-error": "Entität nicht gefunden. Fehler beim Laden der Informationen", - "assign-new-edge": "Neue Edge zuordnen", - "unassign-from-edge": "Edge zuweisen", - "edge-key": "Edge-Schlüssel", - "copy-edge-key": "Edge-Schlüssel kopieren", - "edge-key-copied-message": "Edge-Schlüssel wurde in die Zwischenablage kopiert", - "edge-secret": "Edge-Geheimnis", - "copy-edge-secret": "Edge-Geheimnis kopieren", - "edge-secret-copied-message": "Edge-Geheimnis wurde in die Zwischenablage kopiert", - "edge-assets": "Edge-Objekte verwalten", - "edge-devices": "Edge-Geräte verwalten", - "edge-entity-views": "Edge-Entitätsansichten verwalten", - "edge-dashboards": "Edge-Dashboards verwalten", - "edge-rulechains": "Edge-Regelketten", - "assets": "Edge Objekte", - "devices": "Objekte Geräte", - "entity-views": "Objekte Entitätsansichten", - "dashboard": "Edge-Dashboard", - "dashboards": "Edge-Dashboards", - "rulechain-templates": "Regelkettenvorlagen", - "rulechains": "Edge-Regelketten", - "search": "Edges durchsuchen", - "selected-edges": "{count, plural, =1 {1 Edge} other {# Edges} } ausgewählt", - "any-edge": "Beliebige Kante", - "no-edge-types-matching": "Es wurden keine Edgetypen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", - "edge-type-list-empty": "Keine Edgetypen ausgewählt.", - "edge-types": "Edgetypen", - "enter-edge-type": "Geben Sie den Edgetyp ein", - "deployed": "Bereitgestellt", - "pending": "Steht aus", - "downlinks": "Downlinks", - "no-downlinks-prompt": "Keine Downlinks gefunden", - "sync-process-started-successfully": "Synchronisierungsprozess erfolgreich gestartet!", - "missing-related-rule-chains-title": "In Edge fehlen verwandte Regelketten.", - "missing-related-rule-chains-text": "Randregelkette (n) zugewiesen Verwenden Sie Regelknoten, die Nachrichten an Regelkette (n) weiterleiten, die dieser Kante nicht zugeordnet sind.

Liste der fehlenden Regelketten:
{{missingRuleChains}}", - "widget-datasource-error": "Dieses Widget unterstützt nur EDGE-Entitätsdatenquellen" - }, - "edge-event": { - "type-dashboard": "Dashboard", - "type-asset": "Asset", - "type-device": "Gerät", - "type-device-profile": "Geräteprofile", - "type-entity-view": "Entitätsansicht", - "type-alarm": "Alarm", - "type-rule-chain": "Regelkette", - "type-rule-chain-metadata": "Regelkettenmetadaten", - "type-edge": "Edge", - "type-user": "Benutzer", - "type-customer": "Kunde", - "type-relation": "Beziehung", - "type-widgets-bundle": "Widgetpaket", - "type-widgets-type": "Widgettyp", - "type-admin-settings": "Admineinstellungen", - "action-type-added": "Hinzugefügt", - "action-type-deleted": "Gelöscht", - "action-type-updated": "Aktualisiert", - "action-type-post-attributes": "Attribute posten", - "action-type-attributes-updated": "Attribute Aktualisiert", - "action-type-attributes-deleted": "Attribute Gelöscht", - "action-type-timeseries-updated": "Timeseries Aktualisiert", - "action-type-credentials-updated": "Zugangsdaten Aktualisiert", - "action-type-assigned-to-customer": "Dem Kunden zugewiesen", - "action-type-unassigned-from-customer": "Kundenzuweisung aufgehoben", - "action-type-relation-add-or-update": "Beziehung hinzufügen oder Aktualisieren", - "action-type-relation-deleted": "Beziehung gelöscht", - "action-type-rpc-call": "RPC-Aufruf", - "action-type-alarm-ack": "Alarm zurkenntnisnehmen", - "action-type-alarm-clear": "Alarm löschen", - "action-type-assigned-to-edge": "Der Edge zuweisen", - "action-type-unassigned-from-edge": "Edge-Zuweisung aufheben", - "action-type-credentials-request": "Zugangsdatenabfrage", - "action-type-entity-merge-request": "Entitäts-Merge-Request" - }, - "error": { - "unable-to-connect": "Es konnte keine Verbindung zum Server hergestellt werden! Bitte überprüfen Sie Ihre Internetverbindung.", - "unhandled-error-code": "Unbehandelter Fehlercode: {{errorCode}}", - "unknown-error": "Unbekannter Fehler" - }, - "entity": { - "entity": "Entität", - "entities": "Entitäten", - "aliases": "Entitäts-Aliasnamen", - "entity-alias": "Entitätsalias", - "unable-delete-entity-alias-title": "Alias der Entität kann nicht gelöscht werden", - "unable-delete-entity-alias-text": "Alias der Entität '{{entityAlias}}' kann nicht gelöscht werden, da es von den folgenden Widget(s) verwendet wird:
{{widgetsList}}", - "duplicate-alias-error": "Doppelte Alias gefunden '{{alias}}'.
Die Aliase der Entität müssen innerhalb des Dashboards eindeutig sein.", - "missing-entity-filter-error": "Fehlender Filter für Alias '{{alias}}'.", - "configure-alias": "Alias '{{alias}}' konfigurieren", - "alias": "Alias", - "alias-required": "Alias der Entität ist erforderlich.", - "remove-alias": "Alias der Entität entfernen", - "add-alias": "Alias der Entität erforderlich", - "entity-list": "Entitätsliste", - "entity-type": "Entitätstyp", - "entity-types": "Entitätstypen", - "entity-type-list": "Liste der Entitätstyp", - "any-entity": "Jede Entität", - "enter-entity-type": "Entitätstyp eingeben", - "no-entities-matching": "Keine passenden Entitäten für '{{entity}}' gefunden.", - "no-entity-types-matching": "Keine passende Entitätstypen für '{{entityType}}' gefunden.", - "name-starts-with": "Name beginnt mit", - "use-entity-name-filter": "Filter verwenden", - "entity-list-empty": "Keine Entitäten ausgewählt.", - "entity-name-filter-required": "Entitätsnamenfilter ist erforderlich.", - "entity-name-filter-no-entity-matched": "Keine Entitäten beginnend mit '{{entity}}' gefunden.", - "all-subtypes": "Alle", - "select-entities": "Entitäten auswählen", - "no-aliases-found": "Keine Aliase gefunden.", - "no-alias-matching": "'{{alias}}' nicht gefunden.", - "create-new-alias": "Erstellen Sie einen neuen Alias!", - "key": "Schlüssel", - "key-name": "Name des Schlüssels", - "no-keys-found": "Kein Schlüssel gefunden.", - "no-key-matching": "'{{key}}' nicht gefunden.", - "create-new-key": "Erstellen Sie einen neuen Schlüssel!", - "type": "Typ", - "type-required": "Typ der Entität ist erforderlich.", - "type-device": "Gerät", - "type-devices": "Geräte", - "list-of-devices": "{ count, plural, =1 {Ein Gerät} other {Liste von # Geräten} }", - "device-name-starts-with": "Geräte beginnend mit '{{prefix}}'", - "type-asset": "Objekt", - "type-assets": "Objekte", - "list-of-assets": "{ count, plural, =1 {Ein Objekt} other {Liste von # Objekten} }", - "asset-name-starts-with": "Objekte beginnend mit '{{prefix}}'", - "type-entity-view": "Entitätsansicht", - "type-entity-views": "Entitätsansichten", - "list-of-entity-views": "{ count, plural, =1 {Eine Entitätsansicht} other {Liste von # Entitätsansichten} }", - "entity-view-name-starts-with": "Entitätsansichten beginnend mit'{{prefix}}'", - "type-rule": "Regel", - "type-rules": "Regeln", - "list-of-rules": "{ count, plural, =1 {Eine Regel} other {Liste von # Regeln} }", - "rule-name-starts-with": "Regeln beginnend mit '{{prefix}}'", - "type-plugin": "Plugin", - "type-plugins": "Plugins", - "list-of-plugins": "{ count, plural, =1 {Ein Plugin} other {Liste von # Plugins} }", - "plugin-name-starts-with": "Plugins beginnend mit '{{prefix}}'", - "type-tenant": "Mandant", - "type-tenants": "Mandanten", - "list-of-tenants": "{ count, plural, =1 {Ein Mandant} other {Liste von # Mandanten} }", - "tenant-name-starts-with": "Mandanten beginnend mit '{{prefix}}'", - "type-customer": "Kunde", - "type-customers": "Kunden", - "list-of-customers": "{ count, plural, =1 {Ein Kunde} other {Liste von # Kunden} }", - "customer-name-starts-with": "Kunden beginnend mit '{{prefix}}'", - "type-user": "Benutzer", - "type-users": "Benutzer", - "list-of-users": "{ count, plural, =1 {Ein Benutzer} other {Liste von # Benutzern} }", - "user-name-starts-with": "Benutzer beginnend mit '{{prefix}}'", - "type-dashboard": "Dashboard", - "type-dashboards": "Dashboards", - "list-of-dashboards": "{ count, plural, =1 {Ein Dashboard} other {Liste von # Dashboards} }", - "dashboard-name-starts-with": "Dashboards beginnend mit '{{prefix}}'", - "type-alarm": "Alarm", - "type-alarms": "Alarme", - "list-of-alarms": "{ count, plural, =1 {Ein Alarm} other {Liste von # Alarmen} }", - "alarm-name-starts-with": "Alarme, beginnend mit '{{prefix}}'", - "type-rulechain": "Regelkette", - "type-rulechains": "Regelketten", - "list-of-rulechains": "{ count, plural, =1 {Eine Regelkette} other {Liste von # Regelketten} }", - "rulechain-name-starts-with": "Regelketten beginnend mit '{{prefix}}'", - "type-rulenode": "Regelknoten", - "type-rulenodes": "Regelknoten", - "list-of-rulenodes": "{ count, plural, =1 {Ein Regelknoten} other {Liste von # Regelknoten} }", - "rulenode-name-starts-with": "Regelknoten beginnend mit '{{prefix}}'", - "type-edge": "Edgetyp", - "type-edges": "Eandtyp", - "list-of-edges": "{ count, plural, =1 {1 Edge} other {# Edges} }", - "edge-name-starts-with": "Edge beginnend mit '{{prefix}}'", - "type-current-customer": "Aktueller Kunde", - "search": "Entitäten suchen", - "selected-entities": "{ count, plural, =1 {Entität} other {# Entitäten} } ausgewählt", - "entity-name": "Entitätsname", - "details": "Entitätsdetails", - "no-entities-prompt": "Keine Entitäten gefunden", - "no-data": "Keine Daten zum Anzeigen", - "columns-to-display": "Anzuzeigende Spalten" - }, - "entity-view": { - "entity-view": "Entitätsansicht", - "entity-view-required": "Entitätsansicht ist erforderlich.", - "entity-views": "Entitätsansichten", - "management": "Entitätsansichten verwalten", - "view-entity-views": "Entitätsansichten anzeigen", - "entity-view-alias": "Entitätsansichtsalias", - "aliases": "Entitätsansichten-Aliase", - "no-alias-matching": "'{{alias}}' nicht gefunden.", - "no-aliases-found": "Keine Aliase gefunden.", - "no-key-matching": "'{{key}}' nicht gefunden.", - "no-keys-found": "Keine Schlüssel gefunden.", - "create-new-alias": "Neuen Alias erstellen!", - "create-new-key": "Neuen Schlüssel erstellen!", - "duplicate-alias-error": "Doppelter Alias gefunden '{{alias}}'.
Aliase der Entitätsansicht müssen innerhalb des Dashboards eindeutig sein.", - "configure-alias": "Alias '{{alias}}' konfigurieren", - "no-entity-views-matching": "Keine passenden Entitätsansichten für '{{entity}}' gefunden.", - "alias": "Alias", - "alias-required": "Alias der Entitätsansicht erforderlich.", - "remove-alias": "Alias der Entitätsansicht entfernen", - "add-alias": "Alias für die Entitätsansicht hinzufügen", - "name-starts-with": "Entitätsansichtsname beginnend mit", - "entity-view-list": "Liste der Entitätsansichten", - "use-entity-view-name-filter": "Filter anwenden", - "entity-view-list-empty": "Keine der Entitätsansichten ausgewählt.", - "entity-view-name-filter-required": "Filterung nach Entitätsansichtenname erforderlich.", - "entity-view-name-filter-no-entity-view-matched": "Keine Entitätsansichten beginnend mit '{{entityView}}' wurden gefunden.", - "add": "Entitätsansicht hinzufügen", - "assign-to-customer": "Einem Kunden zuordnen", - "assign-entity-view-to-customer": "Entitätsansichten dem Kunden zuordnen", - "assign-entity-view-to-customer-text": "Bitte wählen Sie die Entitätsansichten aus, die dem Kunden zugeordnet werden sollen", - "no-entity-views-text": "Keine Entitätsansichten gefunden", - "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Entitätsansichten zugeordnet werden sollen", - "entity-view-details": "Details der Entitätsansicht", - "add-entity-view-text": "Neue Entitätsansicht hinzufügen", - "delete": "Entitätsansicht löschen", - "assign-entity-views": "Entitätsansicht zuordnen", - "assign-entity-views-text": "Dem Kunden { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } zuordnen", - "delete-entity-views": "Entitätsansichten löschen", - "unassign-from-customer": "Zuordnung zum Kunden aufheben", - "unassign-entity-views": "Zuordnung der Entitätsansichten aufheben", - "unassign-entity-views-action-title": "Die Zuordnung { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } zum Kunden aufheben", - "assign-new-entity-view": "Neue Entitätsansicht zuordnen", - "delete-entity-view-title": "Möchten Sie die Entitätsansicht wirklich löschen '{{entityViewName}}'?", - "delete-entity-view-text": "Seien Sie vorsichtig, nach der Bestätigung werden die Entitätsansicht und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-entity-views-title": "Sind Sie sicher, dass Sie die Entitätsansichten löschen möchten { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} }?", - "delete-entity-views-action-title": "Löschen { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} }", - "delete-entity-views-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Entitätsansichten entfernt und alle zugehörigen Daten werden nicht wiederhergestellt.", - "unassign-entity-view-title": "Möchten Sie die Zuordnung der Entitätsansicht '{{entityViewName}}' wirklich aufheben?", - "unassign-entity-view-text": "Nach der Bestätigung wird die Zuordnung der Entitätsansicht aufgehoben und ist für den Kunden nicht mehr zugänglich.", - "unassign-entity-view": "Zuordnung der Entitätsansicht aufheben", - "unassign-entity-views-title": "Sind Sie sicher, dass Sie die Zuordnung aufheben möchten { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} }?", - "unassign-entity-views-text": "Nach der Bestätigung werden die Zuordnungen der ausgewählten Entitätsansichten aufgehoben und sind für den Kunden nicht mehr zugänglich.", - "entity-view-type": "Entitätsansichtstyp", - "entity-view-type-required": "Entitätsansichtstyp ist erforderlich.", - "select-entity-view-type": "Entitätsansichtstyp auswählen", - "enter-entity-view-type": "Entitätsansichtstyp eingeben", - "any-entity-view": "Jede Entitätsansicht", - "no-entity-view-types-matching": "Es wurden keine passenden Entitätsansichtstypen für '{{entitySubtype}}' gefunden.", - "entity-view-type-list-empty": "Keine Entitätsansichtstypen ausgewählt.", - "entity-view-types": "Entitätsansichtstypen", - "name": "Name", - "name-required": "Name ist erforderlich.", - "assign-entity-view-to-edge": "Entitätsansicht dem Rand zuordnen", - "assign-entity-view-to-edge-text":"Bitte wählen Sie die Entitätsansicht aus, die dem Rand zugeordnet werden sollen", - "unassign-entity-view-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für Entitätsansicht '{{entityViewName}}' aufheben möchten?", - "unassign-entity-view-from-edge-text": "Nach Bestätigung wird die Zuordnung des Entitätsansichts aufgehoben und es ist für den Kunden nicht mehr zugänglich.", - "unassign-entity-views-from-edge-action-title": "Rand { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichte} } aufheben", - "unassign-entity-view-from-edge": "Entitätsansichtzuordnung aufheben", - "unassign-entity-views-from-edge-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichte} } nicht mehr zuordnen möchten?", - "unassign-entity-views-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Entitätsansicht nicht zugewiesen und sind für den Rand nicht zugänglich.", - "description": "Beschreibung", - "events": "Ereignisse", - "details": "Details", - "copyId": "Entitätsansichts-ID kopieren", - "assignedToCustomer": "Dem Kunden zuordnen", - "unable-entity-view-device-alias-title": "Alias der Entitätsansicht kann nicht gelöscht werden", - "unable-entity-view-device-alias-text": "Geräte-Alias '{{entityViewAlias}}' kann nicht gelöscht werden, da es von den folgenden widget(s):
{{widgetsList}} verwendet wird", - "select-entity-view": "Entitätsansicht auswählen", - "make-public": "Entitätsansicht öffentlich machen", - "start-date": "Start-Datum", - "start-ts": "Start-Zeit", - "end-date": "Ende-Datum", - "end-ts": "Ende-Zeit", - "date-limits": "Datumslimits", - "client-attributes": "Client Eigenschaften", - "shared-attributes": "Gemeinsame Eigenschaften", - "server-attributes": "Server Eigenschaften", - "timeseries": "Zeitreihe", - "client-attributes-placeholder": "Client Eigenschaften", - "shared-attributes-placeholder": "Gemeinsame Eigenschaften", - "server-attributes-placeholder": "Server Eigenschaften", - "timeseries-placeholder": "Zeitreihe", - "target-entity": "Zielentität", - "attributes-propagation": "Eigenschaftsübertragung", - "attributes-propagation-hint": "Die Entitätsansicht kopiert automatisch die angegebenen Eigenschaften der Ziel-Entität, wenn Sie diese Entitätsansicht speichern oder aktualisieren. Aus Performance-Gründen werden die Attribute der Ziel-Entität nicht bei jeder Eigenschaftsänderung in die Entitätsansicht übertragen. Sie können die automatische Weitergabe aktivieren, indem Sie den Regelknoten \"copy to view\" in Ihrer Regelkette konfigurieren und die Nachrichten \"Post attributes\" und \"Attributes updated\" mit dem neuen Regelknoten verknüpfen.", - "timeseries-data": "Zeitreihendaten", - "timeseries-data-hint": "Konfigurieren Sie die Datensatzschlüssel der Zeitreihe der Zielentität, auf die die Entitätsansicht zugreifen kann. Die Daten dieser Zeitreihe sind schreibgeschützt." - }, - "event": { - "event-type": "Ereignistyp", - "type-error": "Fehler", - "type-lc-event": "Lebenszyklusereignis", - "type-stats": "Statistiken", - "type-debug-rule-node": "Fehlersuche", - "type-debug-rule-chain": "Fehlersuche", - "no-events-prompt": "Keine Ereignisse gefunden", - "error": "Fehler", - "type-edge-event": "Downlink", - "alarm": "Alarm", - "event-time": "Ereigniszeit", - "server": "Server", - "body": "Inhalt", - "method": "Methode", - "type": "Typ", - "message-id": "Nachrichten-Id", - "message-type": "Nachrichten-Typ", - "data-type": "Datentyp", - "relation-type": "Beziehungstyp", - "metadata": "Meta-Daten", - "data": "Daten", - "event": "Ereignis", - "status": "Status", - "success": "Erfolg", - "failed": "Fehlgeschlagen", - "messages-processed": "Nachrichten verarbeitet", - "errors-occurred": "Fehler aufgetreten", - "all-events": "Alle", - "entity-type": "Entitätstyp" - }, - "extension": { - "extensions": "Erweiterungen", - "selected-extensions": "{ count, plural, =1 {Erweiterung} other {# Erweiterungen} } ausgewählt", - "type": "Typ", - "key": "Schlüssel", - "value": "Wert", - "id": "ID", - "extension-id": "Erweiterungs-ID", - "extension-type": "Erweiterungstyp", - "transformer-json": "JSON *", - "unique-id-required": "Die aktuelle Erweiterungs-ID ist bereits vorhanden.", - "delete": "Erweiterung löschen", - "add": "Erweiterung hinzufügen", - "edit": "Erweiterung bearbeiten", - "delete-extension-title": "Möchten Sie die Erweiterung '{{extensionId}}' wirklich löschen?", - "delete-extension-text": "Vorsicht, nach Bestätigung werden die Erweiterung und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-extensions-title": "Möchten Sie wirklich { count, plural, =1 {1 Erweiterung} other {# Erweiterungen} } löschen?", - "delete-extensions-text": "Vorsicht, nach der Bestätigung werden alle ausgewählten Erweiterungen entfernt.", - "converters": "Konverter", - "converter-id": "Konverter-ID", - "configuration": "Konfiguration", - "converter-configurations": "Konvertierte Konfigurationen", - "token": "Sicherheitszeichen", - "add-converter": "Konverter hinzufügen", - "add-config": "Konvertierte Konfigurationen hinzufügen", - "device-name-expression": "Angabe des Gerätenamens", - "device-type-expression": "Angabe des Gerätetyps", - "custom": "Regel", - "to-double": "Duplizieren", - "transformer": "Transformator", - "json-required": "Transformer json ist erforderlich.", - "json-parse": "Transformer json kann nicht analysiert werden.", - "attributes": "Eigenschaften", - "add-attribute": "Eigenschaften hinzufügen", - "add-map": "Mapping-Element hinzufügen", - "timeseries": "Zeitreihe", - "add-timeseries": "Zeitreihe hinzufügen", - "field-required": "Feld ist erforderlich", - "brokers": "Vermittler", - "add-broker": "Vermittler hinzufügen", - "host": "Host", - "port": "Port", - "port-range": "Der Port sollte im Bereich von 1 bis 65535 liegen.", - "ssl": "SSL", - "credentials": "Zugangsdaten", - "username": "Benutzername", - "password": "Passwort", - "retry-interval": "Wiederholungsintervall in Millisekunden", - "anonymous": "Anonym", - "basic": "Basic", - "pem": "PEM", - "ca-cert": "CA-Zertifikatsdatei *", - "private-key": "Privatschlüsseldatei *", - "cert": "Zertifikatsdatei *", - "no-file": "Keine Datei ausgewählt.", - "drop-file": "Legen Sie eine Datei ab oder wählen Sie eine Datei aus um diese hochzuladen.", - "mapping": "Mapping", - "topic-filter": "Themenfilter", - "converter-type": "Konvertierungstyp", - "converter-json": "Json", - "json-name-expression": "Angabe des Gerätenamens json", - "topic-name-expression": "Themenangabe des Gerätenamens", - "json-type-expression": "Angabe des Gerätenamens json", - "topic-type-expression": "Themenangabe des Gerätetyps", - "attribute-key-expression": "Angabe des Eigenschaftenschlüssels", - "attr-json-key-expression": "Angabe des Eigenschaftenschlüssels", - "attr-topic-key-expression": "Themenangabe des Eigenschaftenschlüssels", - "request-id-expression": "ID-Angabe anfordern", - "request-id-json-expression": "ID-Angabe anforern json", - "request-id-topic-expression": "Themenangabe der ID anfordern", - "response-topic-expression": "Antwort Themenangabe", - "value-expression": "Wertangabe", - "topic": "Thema", - "timeout": "Unterbrechung in Millisekunden", - "converter-json-required": "Konvertierte json ist erforderlich.", - "converter-json-parse": "Konvertierte json konnte nicht analysiert werden.", - "filter-expression": "Filterangabe", - "connect-requests": "Abfragen verbinden", - "add-connect-request": "Verbindungsabfrage hinzufügen", - "disconnect-requests": "Abfrage trennen", - "add-disconnect-request": "Trennung der Abfrage hinzufügen", - "attribute-requests": "Abfrage der Eigenschaften", - "add-attribute-request": "Abfrage der Eigenschaften hinzufügen", - "attribute-updates": "Aktualisierungen der Eigenschaften", - "add-attribute-update": "Aktualisierung der Eigenschaften hinzufügen", - "server-side-rpc": "Serverseite RPC", - "add-server-side-rpc-request": "Abfrage der Serverseite RPC hinzufügen", - "device-name-filter": "Gerätenamefilter", - "attribute-filter": "Eigenschaftenfilter", - "method-filter": "Methodenfilter", - "request-topic-expression": "Themenabgabe anfordern", - "response-timeout": "Antwortzeit in Millisekunden", - "topic-expression": "Themenangabe", - "client-scope": "Kundenumfrage", - "add-device": "Gerät hinzufügen", - "opc-server": "Servers", - "opc-add-server": "Server hinzufügen", - "opc-add-server-prompt": "Bitte einen Server hinzufügen", - "opc-application-name": "Anwendungsname", - "opc-application-uri": "Anwendung uri", - "opc-scan-period-in-seconds": "Scanzeitraum in Sekunden", - "opc-security": "Sicherheit", - "opc-identity": "Identifizierung", - "opc-keystore": "Schlüsselspeicher", - "opc-type": "Typ", - "opc-keystore-type": "Typ", - "opc-keystore-location": "Speicherort *", - "opc-keystore-password": "Passwort", - "opc-keystore-alias": "Alias", - "opc-keystore-key-password": "Schlüsselpasswort", - "opc-device-node-pattern": "Geräteknotenmuster", - "opc-device-name-pattern": "Gerätenamensmuster", - "modbus-server": "Servers/Folgegerät", - "modbus-add-server": "Server/Folgegerät hinzufügen", - "modbus-add-server-prompt": "Bitte Server/Folgegerät hinzufügen", - "modbus-transport": "Transport", - "modbus-tcp-reconnect": "Verbindung automatisch wiederherstellen", - "modbus-rtu-over-tcp": "RTU über TCP", - "modbus-port-name": "Name des Seriellen Anschlusses", - "modbus-encoding": "Verschlüsselung", - "modbus-parity": "Übereinstimmung", - "modbus-baudrate": "Datenübertragungsgeschwindigkeit", - "modbus-databits": "Daten Bits", - "modbus-stopbits": "Stopp-Bits", - "modbus-databits-range": "Datenbits sollten im Bereich von 7 bis 8 liegen.", - "modbus-stopbits-range": "Stoppbits sollten im Bereich von 1 bis 2 liegen.", - "modbus-unit-id": "ID der Einheit", - "modbus-unit-id-range": "Die Einheiten-ID sollte im Bereich von 1 bis 247 liegen.", - "modbus-device-name": "Gerätename", - "modbus-poll-period": "Abfragezeitraum in Millisekunden", - "modbus-attributes-poll-period": "Abfrageintervall der Eigenschaften in Millisekunden", - "modbus-timeseries-poll-period": "Abfrageintervall der Zeitreihen in Millisekunden", - "modbus-poll-period-range": "Das Abfrageintervall sollte einen positiven Wert haben.", - "modbus-tag": "Kennzeichnung", - "modbus-function": "Funktion", - "modbus-register-address": "Registeradresse", - "modbus-register-address-range": "Die Registeradresse sollte im Bereich zwischen 0 und 65535 liegen.", - "modbus-register-bit-index": "Bitindex", - "modbus-register-bit-index-range": "Der Bitindex sollte im Bereich von 0 bis 15 liegen.", - "modbus-register-count": "Registeranzahl", - "modbus-register-count-range": "Die Registeranzahl sollten einen positiven Wert haben.", - "modbus-byte-order": "Byte-Reihenfolge", - "sync": { - "status": "Status", - "sync": "Synchronisiert", - "not-sync": "Nicht synchronisiert", - "last-sync-time": "Zeit der letzten Synchronisierung", - "not-available": "Nicht verfügbar" - }, - "export-extensions-configuration": "Erweiterungskonfiguration exportieren", - "import-extensions-configuration": "Erweiterungskonfiguration importieren", - "import-extensions": "Erweiterungen importieren", - "import-extension": "Erweiterung importieren", - "export-extension": "Erweiterung exportieren", - "file": "Erweiterungsdatei", - "invalid-file-error": "Ungültige Erweiterungsdatei" - }, - "fullscreen": { - "expand": "Auf Vollbildmodus erweitern", - "exit": "Vollbildmodus verlassen", - "toggle": "Vollbildmodus umschalten", - "fullscreen": "Vollbild" - }, - "function": { - "function": "Funktion" - }, - "grid": { - "delete-item-title": "Möchten Sie dieses Element wirklich löschen?", - "delete-item-text": "Vorsicht, nach Bestätigung wird das Element und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-items-title": "Sind Sie sicher, dass Sie löschen möchten { count, plural, =1 {Symbol} other {Symbole} }?", - "delete-items-action-title": "Löschen { count, plural, =1 {Symbol} other {# Symbole} }", - "delete-items-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Elemente entfernt und alle zugehörigen Daten nicht wiederhergestellt.", - "add-item-text": "Neues Element hinzufügen", - "no-items-text": "Keine Elemente gefunden", - "item-details": "Elementdetails", - "delete-item": "Element löschen", - "delete-items": "Elemente löschen", - "scroll-to-top": "zum Seitenanfang" - }, - "help": { - "goto-help-page": "Gehen Sie zur Hilfeseite" - }, - "home": { - "home": "Startseite", - "profile": "Profil", - "logout": "Abmelden", - "menu": "Menü", - "avatar": "Benutzerbild", - "open-user-menu": "Benutzermenü öffnen" - }, - "import": { - "no-file": "Keine Datei ausgewählt", - "drop-file": "Legen Sie eine JSON-Datei ab oder wählen Sie eine Datei zum hochladen aus." - }, - "item": { - "selected": "Ausgewählt" - }, - "js-func": { - "no-return-error": "Funktion muss einen Wert zurückgeben!", - "return-type-mismatch": "Funktion muss einen Wert vom Typ '{{type}}' zurückgeben!", - "tidy": "Aufräumen" - }, - "key-val": { - "key": "Schlüssel", - "value": "Wert", - "remove-entry": "Eintrag entfernen", - "add-entry": "Eintag hinzufügen", - "no-data": "Keine Einträge" - }, - "layout": { - "layout": "Layout", - "manage": "Layouts verwalten", - "settings": "Layout-Einstellungen", - "color": "Farbe", - "main": "Hauptbereich", - "right": "Recht", - "select": "Wählen Sie das Ziellayout aus" - }, - "legend": { - "position": "Legendenposition", - "show-max": "Maximalwert anzeigen", - "show-min": "Minimalwert anzeigen", - "show-avg": "Durchschnittswert anzeigen", - "show-total": "Gesamtwert anzeigen", - "settings": "Legendeneinstellungen", - "min": "min.", - "max": "max.", - "avg": "mittelw.", - "total": "Gesamt" - }, - "login": { - "login": "Anmelden", - "request-password-reset": "Passwortzurücksetzung anfordern", - "reset-password": "Passwort zurücksetzen", - "create-password": "Passwort erstellen", - "passwords-mismatch-error": "Eingegebene Passwörter müssen identisch sein!", - "password-again": "Passwort wiederholen", - "sign-in": "Bitte anmelden", - "username": "Benutzername (E-Mail)", - "remember-me": "Login speichern", - "forgot-password": "Passwort vergessen?", - "password-reset": "Passwort zurücksetzen", - "new-password": "Neues Passwort", - "new-password-again": "Neues Passwort wiederholen", - "password-link-sent-message": "Der Link zum Zurücksetzen des Passworts wurde erfolgreich versendet!", - "email": "E-Mail", - "login-with": "Mit {{name}} anmelden", - "or": "oder" - }, - "position": { - "top": "Oben", - "bottom": "Unten", - "left": "Links", - "right": "Rechts" - }, - "profile": { - "profile": "Profil", - "last-login-time": "Letzte Anmeldung", - "change-password": "Passwort ändern", - "current-password": "Aktuelles Passwort" - }, - "relation": { - "relations": "Beziehungen", - "direction": "Richtung", - "search-direction": { - "FROM": "Von", - "TO": "Zu" - }, - "direction-type": { - "FROM": "von", - "TO": "zu" - }, - "from-relations": "Ausgehende Verbindungen", - "to-relations": "Eingehende Verbindungen", - "selected-relations": "{ count, plural, =1 {1 Beziehung} other {# Beziehungen} } ausgewählt", - "type": "Typ", - "to-entity-type": "Zum Entitätstyp", - "to-entity-name": "Zum Entitätsnamen", - "from-entity-type": "Vom Entitätstyp", - "from-entity-name": "Vom Entitätsnamen", - "to-entity": "Zur Entität", - "from-entity": "Von Entität", - "delete": "Beziehung löschen", - "relation-type": "Art der Beziehung", - "relation-type-required": "Art der Beziehung erforderlich.", - "any-relation-type": "Jede Art", - "add": "Beziehung hinzufügen", - "edit": "Beziehung bearbeiten", - "delete-to-relation-title": "Möchten Sie die Beziehung zur Einheit'{{entityName}}' wirklich löschen?", - "delete-to-relation-text": "Vorsicht, nach Bestätigung ist die Entität '{{entityName}}' nicht mehr mit der aktuellen Entität verbunden.", - "delete-to-relations-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Beziehung} other {# Beziehungen} } wirklich löschen?", - "delete-to-relations-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Beziehungen entfernt und die entsprechenden Entitäten sind nicht mehr mit der aktuellen Entität verbunden.", - "delete-from-relation-title": "Sind Sie sicher, dass Sie die Verbindung aus der Entität '{{entityName}}' löschen möchten?", - "delete-from-relation-text": "Vorsicht, nach Bestätigung wird die aktuelle Entität '{{entityName}}' von der Entität unabhängig sein.", - "delete-from-relations-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Beziehung} other {# Beziehungen} } löschen möchten?", - "delete-from-relations-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Beziehungen entfernt und die aktuellen Entität wird nicht mehr mit den entsprechenden Entitäten verknüpft sein.", - "remove-relation-filter": "Beziehungsfilter entfernen", - "add-relation-filter": "Beziehungsfilter hinzufügen", - "any-relation": "Jede Beziehung", - "relation-filters": "Beziehungsfilter", - "additional-info": "Zusätzliche Information (JSON)", - "invalid-additional-info": "Json der Zusätzlichen Informationen konnte nicht gelesen werden." - }, - "rulechain": { - "rulechain": "Regelkette", - "rulechains": "Regelketten", - "root": "Wurzel", - "delete": "Regelkette löschen", - "name": "Name", - "name-required": "Name ist erforderlich.", - "description": "Beschreibung", - "add": "Regelkette hinzufügen", - "set-root": "Regelkette zur Wurzel machen", - "set-root-rulechain-title": "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' zur Wurzel machen möchten?", - "set-root-rulechain-text": "Nach der Bestätigung wird die Regelkette zur Wurzel und bearbeitet alle eingehenden Transportnachrichten.", - "delete-rulechain-title": "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' löschen möchten?", - "delete-rulechain-text": "Vorsichtig, nach Bestätigung werden die Regelkette und alle zugehörigen Daten gelöscht.", - "delete-rulechains-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Regelkette} other {# Regelketten} } löschen möchten?", - "delete-rulechains-action-title": "{ count, plural, =1 {1 Regelkette} other {# Regelketten} } löschen", - "delete-rulechains-text": "Vorsichtig, nach Bestätigung werden alle ausgewählten Regelketten entfernt und alle zugehörigen Daten werden gelöscht.", - "add-rulechain-text": "Neue Regelkette hinzufügen", - "no-rulechains-text": "Keine Regelkette gefunden", - "rulechain-details": "Regelketten-Details", - "details": "Details", - "events": "Ereignisse", - "system": "System", - "import": "Regelkette importieren", - "export": "Regelkette exportieren", - "export-failed-error": "Regelkette konnte nicht exportiert werden: {{error}}", - "create-new-rulechain": "Neue Regelkette erstellen", - "rulechain-file": "Regelkettendatei", - "invalid-rulechain-file-error": "Regelkette konnte nicht importiert werden: Ungültige Regelkettendatenstruktur.", - "copyId": "Regelketten-ID kopieren", - "idCopiedMessage": "Regelketten-ID wurde in die Zwischenablage kopiert", - "select-rulechain": "Regelkette auswählen", - "no-rulechains-matching": "Es wurden keine passenden Regelketten für '{{entity}}' gefunden.", - "rulechain-required": "Regelkette ist erforderlich", - "management": "Regelverwaltung", - "debug-mode": "Modus zur Fehlersuche", - "assign-rulechains": "Regelketten zuweisen", - "delete-rulechains": "Regelketten löschen", - "unassign-rulechain": "Nicht zugeordnete Regelkette", - "unassign-rulechains": "Nicht zugeordnete Regelketten", - "unassign-rulechain-title": "Möchten Sie die Zuordnung die Regelkette '{{ruleChainTitle}}' wirklich aufheben?", - "unassign-rulechain-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Regelkette aufgehoben und sie sind für den Rand nicht mehr zugänglich.", - "unassign-rulechains-from-edge-action-title": "Zuordnung { count, plural, =1 {1 Regelkette} other {# Regelketten} } vom Rand aufheben", - "unassign-rulechains-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Regelketten aufgehoben und sie sind für den Rand nicht mehr zugänglich.", - "assign-rulechain-to-edge-title": "Regelkette(n) dem Rand zuordnen", - "assign-rulechain-to-edge-text": "Bitte wählen Sie die Regelketten aus, die Sie dem Rand zuordnen möchten", - "set-edge-template-root-rulechain": "Erstellen Sie den Stamm der Regelkettenkantenvorlage", - "set-edge-template-root-rulechain-title": "Möchten Sie die Kantenvorlage der Regelkette '{{ruleChainName}}' wirklich als Root festlegen?", - "set-edge-template-root-rulechain-text": "Nach der Bestätigung wird die Regelkette zum Stamm der Kantenvorlage und zur Stammregelkette für neu erstellte Kanten.", - "invalid-rulechain-type-error": "Regelkette konnte nicht importiert werden: Ungültige Regelkettentyp. Erwarteter Typ ist {{expectedRuleChainType}}.", - "set-auto-assign-to-edge": "Weisen Sie bei der Erstellung den Kanten die Regelkette zu", - "set-auto-assign-to-edge-title": "Möchten Sie die Kantenregelkette '{{ruleChainName}}' bei der Erstellung automatisch den Kanten zuweisen?", - "set-auto-assign-to-edge-text": "Nach der Bestätigung wird die Kantenregelkette bei der Erstellung automatisch den Kanten zugewiesen.", - "unset-auto-assign-to-edge": "Deaktiviert die Zuordnung der Regelkette zu Kanten bei der Erstellung", - "unset-auto-assign-to-edge-title": "Möchten Sie die Kantenregelkette '{{ruleChainName}}' bei der Erstellung unbedingt den Kanten zuweisen?", - "unset-auto-assign-to-edge-text": "Nach der Bestätigung wird die Kantenregelkette bei der Erstellung nicht mehr automatisch den Kanten zugewiesen.", - "edge-template-root": "Vorlagenstamm", - "search": "Suchen Sie nach Regelketten", - "selected-rulechains": "{count, plural, =1 {1 Regelkette} other {# Regelketten} } ausgewählt", - "open-rulechain": "Regelkette öffnen", - "assign-to-edge": "Rand zuweisen", - "edge-rulechain": "Kantenregelkette" - }, - "rulenode": { - "details": "Details", - "events": "Ereignisse", - "search": "Knoten suchen", - "open-node-library": "Knotenbibliothek öffnen", - "add": "Neuen Regelknoten hinzufügen", - "name": "Name", - "name-required": "Name ist erforderlich.", - "type": "Typ", - "delete": "Regelknoten löschen", - "select-all-objects": "Alle Knoten und Verbindungen auswählen", - "deselect-all-objects": "Auswahl aller Knoten und Verbindungen aufheben", - "delete-selected-objects": "Ausgewählte Knoten und Verbindungen löschen", - "delete-selected": "Auswahl löschen", - "select-all": "Alle auswählen", - "copy-selected": "Auswahl kopieren", - "deselect-all": "Nichts auswählen", - "rulenode-details": "Details der Regelknoten", - "debug-mode": "Modus zur Fehlersuche", - "configuration": "Konfiguration", - "link": "Verbindung", - "link-details": "Verbindungsdetails der Regelknoten", - "add-link": "Verbindung hinzufügen", - "link-label": "Verbindungsbeschriftung", - "link-label-required": "Verbindungsbeschriftung ist erforderlich.", - "custom-link-label": "Benutzerdefinierte Verbindungsbeschriftung", - "custom-link-label-required": "Benutzerdefinierte Verbindungsbeschriftung ist erforderlich.", - "link-labels": "Verbindungsbeschriftungen", - "link-labels-required": "Verbindungsbeschriftungen sind erforderlich.", - "no-link-labels-found": "Keine Verbindungsbeschriftungen gefunden", - "no-link-label-matching": "'{{label}}' nicht gefunden.", - "create-new-link-label": "Bitte erstellen Sie eine neue Verbindungsbeschriftung!", - "type-filter": "Filter", - "type-filter-details": "Eingehende Nachrichten mit konfigurierten Bedingungen filtern", - "type-enrichment": "Anreicherung", - "type-enrichment-details": "Fügen Sie zusätzliche Informationen zu den Nachrichtenmetadaten hinzu", - "type-transformation": "Transformation", - "type-transformation-details": "Ändern Sie die Nutzerdaten und Metadaten der Nachricht", - "type-action": "Aktion", - "type-action-details": "Besondere Aktion ausführen", - "type-external": "Extern", - "type-external-details": "Interagiert mit externem System", - "type-rule-chain": "Regelkette", - "type-rule-chain-details": "Leitet eingehende Nachrichten an die angegebene Regelkette weiter", - "type-input": "Input", - "type-input-details": "Logische Eingabe der Regelkette, leitet eingehende Nachrichten an die nächste zugehörige Regelkette weiter", - "type-unknown": "Unbekannt", - "type-unknown-details": "Nicht aufgelöster Regelknoten", - "directive-is-not-loaded": "Definierte Konfigurationsanweisung '{{directiveName}}' ist nicht verfügbar.", - "ui-resources-load-error": "Fehler beim Laden der Konfigurations-UI-Ressourcen.", - "invalid-target-rulechain": "Zielregelkette kann nicht aufgelöst werden!", - "test-script-function": "Skriptfunktion testen", - "message": "Nachricht", - "message-type": "Nachrichtentyp", - "select-message-type": "Nachrichtentyp auswählen", - "message-type-required": "Nachrichtentyp ist erforderlich", - "metadata": "Metadaten", - "metadata-required": "Metadateneinträge dürfen nicht leer sein.", - "output": "Ausgabe", - "test": "Test", - "help": "Hilfe" - }, - "tenant": { - "tenant": "Mandant", - "tenants": "Mandanten", - "management": "Mandantenverwaltung", - "add": "Mandant hinzufügen", - "admins": "Administratoren", - "manage-tenant-admins": "Mandantenadministratoren verwalten", - "delete": "Mandant löschen", - "add-tenant-text": "Neuen Mandanten hinzufügen", - "no-tenants-text": "Keine Mandanten gefunden", - "tenant-details": "Mandantendetails", - "delete-tenant-title": "Möchten Sie den Mandanten '{{tenantTitle}}' wirklich löschen?", - "delete-tenant-text": "Vorsicht, nach Bestätigung werden der Mandant und alle zugehörigen Daten gelöscht.", - "delete-tenants-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Mandant} other {# Mandanten} } löschen möchten?", - "delete-tenants-action-title": "{ count, plural, =1 {1 Mandant} other {# Mandanten} } löschen", - "delete-tenants-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Mandanten entfernt und alle zugehörigen Daten werden gelöscht.", - "title": "Titel", - "title-required": "Titel ist erforderlich.", - "description": "Beschreibung", - "details": "Details", - "events": "Ereignisse", - "copyId": "Mandanten-ID kopieren", - "idCopiedMessage": "Mandanten-ID wurde in die Zwischenablage kopiert", - "select-tenant": "Mandant auswählen", - "no-tenants-matching": "Es wurden keine passenden Mandanten für '{{entity}}' gefunden.", - "tenant-required": "Mandant ist erforderlich" - }, - "timeinterval": { - "seconds-interval": "{ seconds, plural, =1 {1 Sekunde} other {# Sekunden} }", - "minutes-interval": "{ minutes, plural, =1 {1 Minute} other {# Minuten} }", - "hours-interval": "{ hours, plural, =1 {1 Stunde} other {# Stunden} }", - "days-interval": "{ days, plural, =1 {1 Tag} other {# Tage} }", - "days": "Tage", - "hours": "Stunden", - "minutes": "Minuten", - "seconds": "Sekunden", - "advanced": "Erweitert", - "predefined": { - "yesterday": "Gestern", - "day-before-yesterday": "Vorgestern", - "this-day-last-week": "Dieser Tag letzte Woche", - "previous-week": "Letzte Woche (So - Sa)", - "previous-week-iso": "Letzte Woche (Mo - So)", - "previous-month": "Letzter Monat", - "previous-quarter": "Letztes Quartal", - "previous-half-year": "Letztes Halbjahr", - "previous-year": "Letztes Jahr", - "current-hour": "Aktuelle Stunde", - "current-day": "Aktueller Tag", - "current-day-so-far": "Aktueller Tag bisher", - "current-week": "Aktuelle Woche (So - Sa)", - "current-week-iso": "Aktuelle Woche (Mo - So)", - "current-week-so-far": "Aktuelle Woche bisher (So - Sa)", - "current-week-iso-so-far": "Aktuelle Woche bisher (Mo - So)", - "current-month": "Aktueller Monat", - "current-month-so-far": "Aktueller Monat bisher", - "current-quarter": "Aktuelles Quartal", - "current-quarter-so-far": "Aktuelles Quartal bisher", - "current-half-year": "Aktuelles Halbjahr", - "current-half-year-so-far": "Aktuelles Halbjahr bisher", - "current-year": "Aktuelles Jahr", - "current-year-so-far": "Aktuelles Jahr bisher" + "access" : { + "unauthorized" : "Nicht autorisiert", + "unauthorized-access" : "Unbefugter Zugriff", + "unauthorized-access-text" : "Sie müssen sich anmelden, um Zugriff auf diese Ressource zu erhalten!", + "access-forbidden" : "Zugriff verboten", + "access-forbidden-text" : "Sie haben keine Zugriffsrechte auf diesen Ort!
Versuchen Sie, sich mit einem anderen Benutzer anzumelden, wenn Sie weiterhin Zugriff erhalten möchten.", + "refresh-token-expired" : "Sitzung ist abgelaufen", + "refresh-token-failed" : "Sitzung konnte nicht aktualisiert werden", + "permission-denied" : "Zugriff verweigert", + "permission-denied-text" : "Sie haben keine Berechtigung, diese Operation auszuführen!" + }, + "account" : { + "account" : "Konto", + "notification-settings" : "Benachrichtigungseinstellungen" + }, + "action" : { + "activate" : "Aktivieren", + "suspend" : "Suspendieren", + "save" : "Speichern", + "saveAs" : "Speichern als", + "move" : "Verschieben", + "cancel" : "Abbrechen", + "ok" : "OK", + "delete" : "Löschen", + "add" : "Hinzufügen", + "yes" : "Ja", + "no" : "Nein", + "update" : "Aktualisieren", + "remove" : "Entfernen", + "search" : "Suchen", + "clear-search" : "Suche löschen", + "assign" : "Zuweisen", + "unassign" : "Zuweisung aufheben", + "share" : "Teilen", + "make-private" : "Privat machen", + "apply" : "Anwenden", + "apply-changes" : "Änderungen übernehmen", + "edit-mode" : "Bearbeitungsmodus", + "enter-edit-mode" : "In den Bearbeitungsmodus wechseln", + "decline-changes" : "Änderungen ablehnen", + "decline" : "Ablehnen", + "close" : "Schließen", + "back" : "Zurück", + "run" : "Ausführen", + "sign-in" : "Anmelden!", + "edit" : "Bearbeiten", + "view" : "Anzeigen", + "create" : "Erstellen", + "drag" : "Ziehen", + "refresh" : "Aktualisieren", + "undo" : "Rückgängig", + "copy" : "Kopieren", + "paste" : "Einfügen", + "copy-reference" : "Referenz kopieren", + "paste-reference" : "Referenz einfügen", + "import" : "Importieren", + "export" : "Exportieren", + "share-via" : "Teilen via {{provider}}", + "select" : "Auswählen", + "continue" : "Fortsetzen", + "discard-changes" : "Änderungen verwerfen", + "download" : "Herunterladen", + "next" : "Weiter", + "next-with-label" : "Weiter: {{label}}", + "read-more" : "Mehr lesen", + "hide" : "Verbergen", + "test" : "Testen", + "done" : "Fertig", + "print" : "Drucken", + "restore" : "Wiederherstellen", + "confirm" : "Bestätigen", + "more" : "Mehr", + "less" : "Weniger", + "skip" : "Überspringen", + "send" : "Senden", + "reset" : "Zurücksetzen", + "show-more" : "Mehr anzeigen", + "dont-show-again" : "Nicht erneut anzeigen", + "see-documentation" : "Dokumentation ansehen", + "clear" : "Löschen", + "upload" : "Hochladen", + "delete-anyway" : "Trotzdem löschen", + "delete-selected" : "Ausgewählte löschen", + "set" : "Festlegen" + }, + "aggregation" : { + "aggregation" : "Aggregation", + "function" : "Datenaggregationsfunktion", + "limit" : "Maximale Werte", + "group-interval" : "Gruppierungsintervall", + "min" : "Minimum", + "max" : "Maximum", + "avg" : "Durchschnitt", + "sum" : "Summe", + "count" : "Anzahl", + "none" : "Keine" + }, + "admin" : { + "settings" : "Einstellungen", + "general" : "Allgemein", + "general-settings" : "Allgemeine Einstellungen", + "home-settings" : "Startseiten-Einstellungen", + "home" : "Startseite", + "outgoing-mail" : "Mailserver", + "outgoing-mail-settings" : "Einstellungen des ausgehenden Mailservers", + "system-settings" : "Systemeinstellungen", + "test-mail-sent" : "Test-E-Mail wurde erfolgreich gesendet!", + "base-url" : "Basis-URL", + "base-url-required" : "Basis-URL ist erforderlich.", + "prohibit-different-url" : "Verwendung des Hostnamens aus den Client-Anforderungsheadern untersagen", + "prohibit-different-url-hint" : "Diese Einstellung sollte für Produktionsumgebungen aktiviert sein. Kann zu Sicherheitsproblemen führen, wenn deaktiviert.", + "device-connectivity" : { + "device-connectivity" : "Geräteverbindung", + "http-s" : "HTTP(s)", + "mqtt-s" : "MQTT(s)", + "coap-s" : "COAP(s)", + "http" : "HTTP", + "https" : "HTTPs", + "mqtt" : "MQTT", + "mqtts" : "MQTTs", + "coap" : "COAP", + "coaps" : "COAPs", + "hint" : "Wenn Host- oder Portfelder leer sind, wird der Standardwert des Protokolls verwendet.", + "host" : "Host", + "port" : "Port", + "port-pattern" : "Port muss eine positive Ganzzahl sein.", + "port-range" : "Port muss im Bereich von 1 bis 65535 liegen." + }, + "mail-from" : "Absender-E-Mail", + "mail-from-required" : "Absender-E-Mail ist erforderlich.", + "smtp-protocol" : "SMTP-Protokoll", + "smtp-host" : "SMTP-Host", + "smtp-host-required" : "SMTP-Host ist erforderlich.", + "smtp-port" : "SMTP-Port", + "smtp-port-required" : "SMTP-Port ist erforderlich.", + "smtp-port-invalid" : "Das scheint kein gültiger SMTP-Port zu sein.", + "timeout-msec" : "Timeout (ms)", + "timeout-required" : "Timeout ist erforderlich.", + "timeout-invalid" : "Das scheint kein gültiger Timeout zu sein.", + "enable-tls" : "TLS aktivieren", + "tls-version" : "TLS-Version", + "enable-proxy" : "Proxy aktivieren", + "proxy-host" : "Proxy-Host", + "proxy-host-required" : "Proxy-Host ist erforderlich.", + "proxy-port" : "Proxy-Port", + "proxy-port-required" : "Proxy-Port ist erforderlich.", + "proxy-port-range" : "Proxy-Port muss im Bereich von 1 bis 65535 liegen.", + "proxy-user" : "Proxy-Benutzer", + "proxy-password" : "Proxy-Passwort", + "change-password" : "Passwort ändern", + "send-test-mail" : "Test-E-Mail senden", + "sms-provider" : "SMS-Anbieter", + "sms-provider-settings" : "SMS-Anbieter-Einstellungen", + "sms-provider-type" : "Typ des SMS-Anbieters", + "sms-provider-type-required" : "Typ des SMS-Anbieters ist erforderlich.", + "sms-provider-type-aws-sns" : "Amazon SNS", + "sms-provider-type-twilio" : "Twilio", + "sms-provider-type-smpp" : "SMPP", + "aws-access-key-id" : "AWS Access Key ID", + "aws-access-key-id-required" : "AWS Access Key ID ist erforderlich", + "aws-secret-access-key" : "AWS Secret Access Key", + "aws-secret-access-key-required" : "AWS Secret Access Key ist erforderlich", + "aws-region" : "AWS-Region", + "aws-region-required" : "AWS-Region ist erforderlich", + "number-from" : "Absendernummer", + "number-from-required" : "Absendernummer ist erforderlich.", + "number-to" : "Empfängernummer", + "number-to-required" : "Empfängernummer ist erforderlich.", + "phone-number-hint" : "Telefonnummer im E.164-Format, z. B. +19995550123", + "phone-number-hint-twilio" : "Telefonnummer im E.164-Format/SID der Telefonnummer/Messaging Service SID, z. B. +19995550123/PNXXX/MGXXX", + "phone-number-pattern" : "Ungültige Telefonnummer. Sollte im E.164-Format sein, z. B. +19995550123.", + "phone-number-pattern-twilio" : "Ungültige Telefonnummer. Sollte im E.164-Format/SID der Telefonnummer/Messaging Service SID sein.", + "sms-message" : "SMS-Nachricht", + "sms-message-required" : "SMS-Nachricht ist erforderlich.", + "sms-message-max-length" : "SMS-Nachricht darf nicht länger als 1600 Zeichen sein", + "twilio-account-sid" : "Twilio Account SID", + "twilio-account-sid-required" : "Twilio Account SID ist erforderlich", + "twilio-account-token" : "Twilio Account Token", + "twilio-account-token-required" : "Twilio Account Token ist erforderlich", + "send-test-sms" : "Test-SMS senden", + "test-sms-sent" : "Test-SMS wurde erfolgreich gesendet!", + "security-settings" : "Sicherheitseinstellungen", + "password-policy" : "Passwortrichtlinie", + "minimum-password-length" : "Minimale Passwortlänge", + "minimum-password-length-required" : "Minimale Passwortlänge ist erforderlich", + "minimum-password-length-range" : "Minimale Passwortlänge sollte zwischen 6 und 50 Zeichen liegen", + "maximum-password-length" : "Maximale Passwortlänge", + "maximum-password-length-min" : "Maximale Passwortlänge sollte mindestens 6 betragen", + "maximum-password-length-less-min" : "Maximale Passwortlänge muss größer als die minimale Länge sein", + "minimum-uppercase-letters" : "Minimale Anzahl an Großbuchstaben", + "minimum-uppercase-letters-range" : "Minimale Anzahl an Großbuchstaben darf nicht negativ sein", + "minimum-lowercase-letters" : "Minimale Anzahl an Kleinbuchstaben", + "minimum-lowercase-letters-range" : "Minimale Anzahl an Kleinbuchstaben darf nicht negativ sein", + "minimum-digits" : "Minimale Anzahl an Ziffern", + "minimum-digits-range" : "Minimale Anzahl an Ziffern darf nicht negativ sein", + "minimum-special-characters" : "Minimale Anzahl an Sonderzeichen", + "minimum-special-characters-range" : "Minimale Anzahl an Sonderzeichen darf nicht negativ sein", + "password-expiration-period-days" : "Passwort-Ablaufzeitraum in Tagen", + "password-expiration-period-days-range" : "Passwort-Ablaufzeitraum darf nicht negativ sein", + "password-reuse-frequency-days" : "Häufigkeit der Passwortwiederverwendung in Tagen", + "password-reuse-frequency-days-range" : "Häufigkeit der Passwortwiederverwendung darf nicht negativ sein", + "allow-whitespace" : "Leerzeichen erlauben", + "force-reset-password-if-no-valid" : "Erzwinge Passwortzurücksetzung bei ungültigem Passwort", + "force-reset-password-if-no-valid-hint" : "Seien Sie vorsichtig beim Aktivieren dieser Funktion: Benutzer mit ungültigem Passwort müssen ihr Passwort per E-Mail zurücksetzen.", + "general-policy" : "Allgemeine Richtlinie", + "max-failed-login-attempts" : "Maximale Anzahl fehlgeschlagener Anmeldeversuche, bevor das Konto gesperrt wird", + "minimum-max-failed-login-attempts-range" : "Maximale Anzahl fehlgeschlagener Anmeldeversuche darf nicht negativ sein", + "user-lockout-notification-email" : "Im Falle einer Kontosperrung des Benutzers, Benachrichtigung per E-Mail senden", + "user-activation-token-ttl" : "Gültigkeit des Aktivierungslinks in Stunden", + "user-activation-token-ttl-range" : "Aktivierungslink muss zwischen 1 und 24 Stunden gültig sein", + "password-reset-token-ttl" : "Gültigkeit des Passwort-Reset-Links in Stunden", + "password-reset-token-ttl-range" : "Reset-Link muss zwischen 1 und 24 Stunden gültig sein", + "mobile-secret-key-length" : "Länge des mobilen Geheimschlüssels", + "mobile-secret-key-length-range" : "Länge des mobilen Geheimschlüssels muss positiv sein", + "domain-name" : "Domainname", + "domain-name-unique" : "Domainname und Protokoll müssen eindeutig sein.", + "domain-name-max-length" : "Domainname sollte weniger als 256 Zeichen haben", + "error-verification-url" : "Ein Domainname darf keine Zeichen wie '/' oder ':' enthalten. Beispiel: thingsboard.io", + "connection-settings" : "Verbindungseinstellungen", + "oauth2" : { + "access-token-uri" : "Access Token URI", + "access-token-uri-required" : "Access Token URI ist erforderlich.", + "activate-user" : "Benutzer aktivieren", + "add-domain" : "Domain hinzufügen", + "delete-domain" : "Domain löschen", + "add-provider" : "Anbieter hinzufügen", + "delete-provider" : "Anbieter löschen", + "allow-user-creation" : "Benutzererstellung erlauben", + "always-fullscreen" : "Immer Vollbild", + "authorization-uri" : "Autorisierungs-URI", + "authorization-uri-required" : "Autorisierungs-URI ist erforderlich.", + "add-client" : "OAuth 2.0-Client hinzufügen", + "client-details" : "OAuth 2.0-Clientdetails", + "client" : "OAuth 2.0-Client", + "clients" : "OAuth 2.0-Clients", + "no-oauth2-clients" : "Keine OAuth 2.0-Clients gefunden", + "search-oauth2-clients" : "OAuth 2.0-Clients suchen", + "delete-client-title" : "Sind Sie sicher, dass Sie den OAuth 2.0-Client '{{clientName}}' löschen möchten?", + "delete-client-text" : "Seien Sie vorsichtig: Nach der Bestätigung werden der Client und alle zugehörigen Daten nicht mehr wiederherstellbar sein.", + "delete-mobile-app-title" : "Sind Sie sicher, dass Sie die mobile Anwendung '{{applicationName}}' löschen möchten?", + "delete-mobile-app-text" : "Seien Sie vorsichtig: Nach der Bestätigung werden die Anwendung und alle zugehörigen Daten nicht mehr wiederherstellbar sein.", + "title" : "Titel", + "client-title-required" : "Titel ist erforderlich", + "client-title-max-length" : "Titel sollte weniger als 100 Zeichen enthalten", + "advanced-settings" : "Erweiterte Einstellungen", + "domain-details" : "Domain-Details", + "no-domains" : "Keine Domains gefunden", + "search-domains" : "Domains suchen", + "mobile-app-details" : "Details der mobilen Anwendung", + "add-mobile-app" : "Mobile Anwendung hinzufügen", + "no-mobile-apps" : "Keine mobilen Anwendungen gefunden", + "search-mobile-apps" : "Mobile Anwendungen suchen", + "send-token" : "Token senden", + "create-new" : "Neu erstellen", + "client-authentication-method" : "Authentifizierungsmethode des Clients", + "client-id" : "Client-ID", + "client-id-required" : "Client-ID ist erforderlich.", + "client-id-max-length" : "Client-ID sollte weniger als 256 Zeichen enthalten", + "client-secret" : "Client-Geheimnis", + "client-secret-required" : "Client-Geheimnis ist erforderlich.", + "client-secret-max-length" : "Client-Geheimnis sollte weniger als 2049 Zeichen enthalten", + "custom-setting" : "Benutzerdefinierte Einstellungen", + "customer-name-pattern" : "Kundennamensmuster", + "customer-name-pattern-max-length" : "Kundennamensmuster sollte weniger als 256 Zeichen enthalten", + "default-dashboard-name" : "Standard-Dashboardname", + "default-dashboard-name-max-length" : "Standard-Dashboardname sollte weniger als 256 Zeichen enthalten", + "delete-domain-text" : "Seien Sie vorsichtig, nach der Bestätigung werden Domain und alle Anbieterdaten nicht mehr verfügbar sein.", + "delete-domain-title" : "Sind Sie sicher, dass Sie die Domain '{{domainName}}' löschen möchten?", + "delete-registration-text" : "Seien Sie vorsichtig, nach der Bestätigung werden die Anbieterdaten nicht mehr verfügbar sein.", + "delete-registration-title" : "Sind Sie sicher, dass Sie den Anbieter '{{name}}' löschen möchten?", + "email-attribute-key" : "E-Mail-Attributschlüssel", + "email-attribute-key-required" : "E-Mail-Attributschlüssel ist erforderlich.", + "email-attribute-key-max-length" : "E-Mail-Attributschlüssel sollte weniger als 32 Zeichen enthalten", + "first-name-attribute-key" : "Vorname-Attributschlüssel", + "first-name-attribute-key-max-length" : "Vorname-Attributschlüssel sollte weniger als 32 Zeichen enthalten", + "general" : "Allgemein", + "jwk-set-uri" : "JSON Web Key URI", + "last-name-attribute-key" : "Nachname-Attributschlüssel", + "last-name-attribute-key-max-length" : "Nachname-Attributschlüssel sollte weniger als 32 Zeichen enthalten", + "login-button-icon" : "Login-Button-Symbol", + "login-button-label" : "Anbieterbezeichnung", + "login-button-label-placeholder" : "Anmelden mit $(Provider label)", + "login-button-label-required" : "Bezeichnung ist erforderlich.", + "login-provider" : "Login-Anbieter", + "mapper" : "Mapper", + "new-domain" : "Neue Domain", + "oauth2" : "OAuth 2.0", + "password-max-length" : "Passwort sollte weniger als 256 Zeichen enthalten", + "redirect-uri-template" : "Redirect URI-Vorlage", + "copy-redirect-uri" : "Redirect URI kopieren", + "registration-id" : "Registrierungs-ID", + "registration-id-required" : "Registrierungs-ID ist erforderlich.", + "registration-id-unique" : "Registrierungs-ID muss im System eindeutig sein.", + "scope" : "Scope", + "scope-required" : "Scope ist erforderlich.", + "tenant-name-pattern" : "Mandantennamensmuster", + "tenant-name-pattern-required" : "Mandantennamensmuster ist erforderlich.", + "tenant-name-pattern-max-length" : "Mandantennamensmuster sollte weniger als 256 Zeichen enthalten", + "tenant-name-strategy" : "Strategie für Mandantennamen", + "type" : "Mapper-Typ", + "uri-pattern-error" : "Ungültiges URI-Format.", + "url" : "URL", + "url-pattern" : "Ungültiges URL-Format.", + "url-required" : "URL ist erforderlich.", + "url-max-length" : "URL sollte weniger als 256 Zeichen enthalten", + "user-info-uri" : "Benutzerinfo-URI", + "user-info-uri-required" : "Benutzerinfo-URI ist erforderlich.", + "username-max-length" : "Benutzername sollte weniger als 256 Zeichen enthalten", + "user-name-attribute-name" : "Benutzername-Attributschlüssel", + "user-name-attribute-name-required" : "Benutzername-Attributschlüssel ist erforderlich", + "protocol" : "Protokoll", + "domain-schema-http" : "HTTP", + "domain-schema-https" : "HTTPS", + "domain-schema-mixed" : "HTTP+HTTPS", + "enable" : "OAuth 2.0-Einstellungen aktivieren", + "disable" : "OAuth 2.0-Einstellungen deaktivieren", + "edge" : "Auf Edge übertragen", + "edge-enable" : "Übertragung auf Edge aktivieren", + "edge-disable" : "Übertragung auf Edge deaktivieren", + "domains" : "Domains", + "mobile-apps" : "Mobile Anwendungen", + "mobile-package" : "Anwendungspaket", + "mobile-package-placeholder" : "z. B.: my.example.app", + "mobile-package-hint" : "Für Android: Ihre eigene eindeutige Anwendungs-ID. Für iOS: Produkt-Bundle-Identifier.", + "mobile-package-unique" : "Anwendungspaket muss eindeutig sein.", + "mobile-package-required" : "Anwendungspaket ist erforderlich.", + "mobile-package-max-length" : "Anwendungspaket sollte weniger als 256 Zeichen enthalten", + "mobile-package-spaces" : "Anwendungspaket darf keine Leerzeichen enthalten", + "mobile-app-secret" : "Anwendungsgeheimnis", + "mobile-app-secret-hint" : "Base64-codierter String mit mindestens 512 Bits.", + "mobile-app-secret-required" : "Anwendungsgeheimnis ist erforderlich.", + "mobile-app-secret-min-length" : "Anwendungsgeheimnis muss mindestens 512 Bits lang sein.", + "mobile-app-secret-base64" : "Anwendungsgeheimnis muss im Base64-Format sein.", + "invalid-mobile-app-secret" : "Anwendungsgeheimnis darf nur alphanumerische Zeichen enthalten und muss zwischen 16 und 2048 Zeichen lang sein.", + "copy-mobile-app-secret" : "Anwendungsgeheimnis kopieren", + "delete-mobile-app" : "Anwendungsinfo löschen", + "providers" : "Anbieter", + "platform-web" : "Web", + "platform-android" : "Android", + "platform-ios" : "iOS", + "all-platforms" : "Alle Plattformen", + "smtp-provider" : "SMTP-Anbieter", + "allowed-platforms" : "Zugelassene Plattformen", + "authentication" : "Authentifizierung", + "basic" : "Basis", + "provider" : "Anbieter", + "redirect-url" : "Redirect-URI", + "domain-name" : "Domainname", + "domain-name-required" : "Domainname ist erforderlich", + "redirect-url-template" : "Redirect-URI-Vorlage", + "microsoft-tenant-id" : "Verzeichnis-ID (Mandanten-ID)", + "microsoft-tenant-id-required" : "Verzeichnis-ID (Mandanten-ID) ist erforderlich", + "token-uri" : "Token-URI", + "token-uri-required" : "Token-URI ist erforderlich", + "redirect-uri" : "Redirect-URI", + "google-provider" : "Google", + "microsoft-provider" : "Office 365", + "sendgrid-provider" : "Sendgrid", + "custom-provider" : "Benutzerdefiniert", + "generate-access-token" : "Access Token generieren", + "update-access-token" : "Access Token aktualisieren", + "access-token-status" : "Status des Access Tokens:", + "token-status-generated" : "generiert", + "token-status-not-generated" : "nicht generiert" + }, + "smpp-provider" : { + "smpp-version" : "SMPP-Version", + "smpp-host" : "SMPP-Host", + "smpp-host-required" : "SMPP-Host ist erforderlich", + "smpp-port" : "SMPP-Port", + "smpp-port-required" : "SMPP-Port ist erforderlich", + "system-id" : "System-ID", + "system-id-required" : "System-ID ist erforderlich", + "password" : "Passwort", + "password-required" : "Passwort ist erforderlich", + "type-settings" : "Typ-Einstellungen", + "source-settings" : "Quell-Einstellungen", + "destination-settings" : "Ziel-Einstellungen", + "additional-settings" : "Zusätzliche Einstellungen", + "system-type" : "Systemtyp", + "bind-type" : "Bindetyp", + "service-type" : "Diensttyp", + "source-address" : "Quelladresse", + "source-ton" : "Quell-TON", + "source-npi" : "Quell-NPI", + "destination-ton" : "Ziel-TON (Nummerntyp)", + "destination-npi" : "Ziel-NPI (Nummerierungsplan-Identifikation)", + "address-range" : "Adressbereich", + "coding-scheme" : "Kodierungsschema", + "bind-type-tx" : "Sender", + "bind-type-rx" : "Empfänger", + "bind-type-trx" : "Sender/Empfänger", + "ton-unknown" : "Unbekannt", + "ton-international" : "International", + "ton-national" : "National", + "ton-network-specific" : "Netzwerkspezifisch", + "ton-subscriber-number" : "Teilnehmernummer", + "ton-alphanumeric" : "Alphanumerisch", + "ton-abbreviated" : "Abgekürzt", + "npi-unknown" : "0 - Unbekannt", + "npi-isdn" : "1 - ISDN/Telefonnummernplan (E163/E164)", + "npi-data-numbering-plan" : "3 - Datennummerierungsplan (X.121)", + "npi-telex-numbering-plan" : "4 - Telex-Nummerierungsplan (F.69)", + "npi-land-mobile" : "6 - Land Mobile (E.212)", + "npi-national-numbering-plan" : "8 - Nationaler Nummerierungsplan", + "npi-private-numbering-plan" : "9 - Privater Nummerierungsplan", + "npi-ermes-numbering-plan" : "10 - ERMES-Nummerierungsplan (ETSI DE/PS 3 01-3)", + "npi-internet" : "13 - Internet (IP)", + "npi-wap-client-id" : "18 - WAP Client-ID (durch das WAP-Forum zu definieren)", + "scheme-smsc" : "0 - SMSC Standardschrift (ASCII für kurze/lange Nummer, GSM für gebührenfrei)", + "scheme-ia5" : "1 - IA5 (ASCII für kurze/lange Nummer, Latin 9 für gebührenfrei)", + "scheme-octet-unspecified-2" : "2 - Oktett nicht angegeben (8-Bit binär)", + "scheme-latin-1" : "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4" : "4 - Oktett nicht angegeben (8-Bit binär)", + "scheme-jis" : "5 - JIS (X 0208-1990)", + "scheme-cyrillic" : "6 - Kyrillisch (ISO-8859-5)", + "scheme-latin-hebrew" : "7 - Lateinisch/Hebräisch (ISO-8859-8)", + "scheme-ucs-utf" : "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding" : "9 - Piktogramm-Kodierung", + "scheme-music-codes" : "10 - Musikcodes (ISO-2022-JP)", + "scheme-extended-kanji-jis" : "13 - Erweiterter Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set" : "14 - Koreanischer Zeichensatz (KS C 5601/KS X 1001)" + }, + "queue-select-name" : "Warteschlangenname auswählen", + "queue-name" : "Name", + "queue-name-required" : "Warteschlangenname ist erforderlich!", + "queues" : "Warteschlangen", + "queue-partitions" : "Partitionen", + "queue-submit-strategy" : "Übermittlungsstrategie", + "queue-processing-strategy" : "Verarbeitungsstrategie", + "queue-configuration" : "Warteschlangenkonfiguration", + "repository-settings" : "Repository-Einstellungen", + "repository" : "Repository", + "repository-url" : "Repository-URL", + "repository-url-required" : "Repository-URL ist erforderlich.", + "default-branch" : "Standard-Branch-Name", + "repository-read-only" : "Nur Lesen", + "show-merge-commits" : "Merge-Commits anzeigen", + "authentication-settings" : "Authentifizierungseinstellungen", + "auth-method" : "Authentifizierungsmethode", + "auth-method-username-password" : "Passwort / Zugriffstoken", + "auth-method-username-password-hint" : "GitHub-Nutzer müssen Tokens mit Schreibrechten zum Repository verwenden.", + "auth-method-private-key" : "Privater Schlüssel", + "password-access-token" : "Passwort / Zugriffstoken", + "change-password-access-token" : "Passwort / Zugriffstoken ändern", + "private-key" : "Privater Schlüssel", + "drop-private-key-file-or" : "Ziehen Sie eine private Schlüsseldatei hierher oder", + "passphrase" : "Passphrase", + "enter-passphrase" : "Passphrase eingeben", + "change-passphrase" : "Passphrase ändern", + "check-access" : "Zugriff prüfen", + "check-repository-access-success" : "Repository-Zugriff erfolgreich überprüft!", + "delete-repository-settings-title" : "Möchten Sie die Repository-Einstellungen wirklich löschen?", + "delete-repository-settings-text" : "Achtung, nach der Bestätigung werden die Repository-Einstellungen entfernt und die Versionskontrolle wird nicht mehr verfügbar sein.", + "auto-commit-settings" : "Auto-Commit-Einstellungen", + "auto-commit" : "Auto-Commit", + "auto-commit-entities" : "Auto-Commit-Entitäten", + "no-auto-commit-entities-prompt" : "Keine Entitäten für Auto-Commit konfiguriert", + "delete-auto-commit-settings-title" : "Möchten Sie die Auto-Commit-Einstellungen wirklich löschen?", + "delete-auto-commit-settings-text" : "Achtung, nach der Bestätigung werden die Auto-Commit-Einstellungen entfernt und Auto-Commit wird für alle Entitäten deaktiviert.", + "mobile-app" : { + "mobile-app" : "Mobile App", + "mobile-app-qr-code-widget-settings" : "QR-Code-Widget-Einstellungen für Mobile App", + "applications" : "Anwendungen", + "default" : "Standard", + "custom" : "Benutzerdefiniert", + "android" : "Android", + "ios" : "iOS", + "appearance" : "Erscheinungsbild", + "appearance-on-home-page" : "Erscheinungsbild auf der Startseite", + "enabled" : "Aktiviert", + "disabled" : "Deaktiviert", + "badges" : "Abzeichen", + "label" : "Bezeichnung", + "label-required" : "Bezeichnung ist erforderlich", + "label-max-length" : "Bezeichnung sollte maximal 50 Zeichen lang sein", + "right" : "Rechts", + "left" : "Links", + "set" : "Festlegen", + "preview" : "Vorschau", + "connect-mobile-app" : "Mobile App verbinden", + "use-system-settings" : "Systemeinstellungen verwenden" + }, + "2fa" : { + "2fa" : "Zwei-Faktor-Authentifizierung", + "available-providers" : "Verfügbare Anbieter", + "issuer-name" : "Ausstellername", + "issuer-name-required" : "Ausstellername ist erforderlich.", + "max-verification-failures-before-user-lockout" : "Maximale Verifizierungsfehler vor Kontosperrung", + "max-verification-failures-before-user-lockout-pattern" : "Maximale Verifizierungsfehler müssen eine positive Ganzzahl sein.", + "number-of-checking-attempts" : "Anzahl der Überprüfungsversuche", + "number-of-checking-attempts-pattern" : "Überprüfungsversuche müssen eine positive Ganzzahl sein.", + "number-of-checking-attempts-required" : "Überprüfungsversuche sind erforderlich.", + "number-of-codes" : "Anzahl der Codes", + "number-of-codes-pattern" : "Anzahl der Codes muss eine positive Ganzzahl sein.", + "number-of-codes-required" : "Anzahl der Codes ist erforderlich.", + "provider" : "Anbieter", + "retry-verification-code-period" : "Wiederholungszeitraum für Verifizierungscode (Sek.)", + "retry-verification-code-period-pattern" : "Mindestzeitraum beträgt 5 Sekunden", + "retry-verification-code-period-required" : "Wiederholungszeitraum ist erforderlich.", + "total-allowed-time-for-verification" : "Maximal erlaubte Verifizierungszeit (Sek.)", + "total-allowed-time-for-verification-pattern" : "Mindestzeit beträgt 60 Sekunden", + "total-allowed-time-for-verification-required" : "Maximale Verifizierungszeit ist erforderlich.", + "use-system-two-factor-auth-settings" : "Systemeinstellungen für 2FA verwenden", + "verification-code-check-rate-limit" : "Rate-Limit für Code-Überprüfung", + "verification-code-lifetime" : "Lebensdauer des Verifizierungscodes (Sek.)", + "verification-code-lifetime-pattern" : "Lebensdauer muss eine positive Ganzzahl sein.", + "verification-code-lifetime-required" : "Lebensdauer ist erforderlich.", + "verification-message-template" : "Nachrichtenvorlage für Verifizierung", + "verification-limitations" : "Verifizierungseinschränkungen", + "verification-message-template-pattern" : "Nachricht muss das Muster ${code} enthalten", + "verification-message-template-required" : "Nachrichtenvorlage ist erforderlich.", + "within-time" : "Innerhalb von (Sek.)", + "within-time-pattern" : "Zeit muss eine positive Ganzzahl sein.", + "within-time-required" : "Zeit ist erforderlich." + }, + "jwt" : { + "security-settings" : "JWT-Sicherheitseinstellungen", + "issuer-name" : "Ausstellername", + "issuer-name-required" : "Ausstellername ist erforderlich.", + "signings-key" : "Signierschlüssel", + "signings-key-hint" : "Base64-codierter String mit mindestens 512 Bit Daten.", + "signings-key-required" : "Signierschlüssel ist erforderlich.", + "signings-key-min-length" : "Signierschlüssel muss mindestens 512 Bit Daten enthalten.", + "signings-key-base64" : "Signierschlüssel muss im Base64-Format vorliegen.", + "expiration-time" : "Ablaufzeit des Tokens (Sek.)", + "expiration-time-required" : "Ablaufzeit ist erforderlich.", + "expiration-time-max" : "Maximal zulässige Zeit ist 2147483647 Sekunden (68 Jahre).", + "expiration-time-min" : "Mindestzeit beträgt 60 Sekunden (1 Minute).", + "refresh-expiration-time" : "Ablaufzeit des Refresh-Tokens (Sek.)", + "refresh-expiration-time-required" : "Ablaufzeit des Refresh-Tokens ist erforderlich.", + "refresh-expiration-time-max" : "Maximal zulässige Zeit ist 2147483647 Sekunden (68 Jahre).", + "refresh-expiration-time-min" : "Mindestzeit beträgt 900 Sekunden (15 Minuten).", + "refresh-expiration-time-less-token" : "Refresh-Token-Zeit muss größer als die Token-Zeit sein.", + "generate-key" : "Schlüssel generieren", + "info-header" : "Alle Benutzer müssen sich neu anmelden", + "info-message" : "Die Änderung des JWT-Signierschlüssels macht alle ausgegebenen Tokens ungültig. Alle Benutzer müssen sich erneut anmelden. Dies betrifft auch Skripte, die die REST-API/Websockets verwenden." + }, + "resources" : "Ressourcen", + "notifications" : "Benachrichtigungen", + "notifications-settings" : "Benachrichtigungseinstellungen", + "slack-api-token" : "Slack-API-Token", + "slack" : "Slack", + "slack-settings" : "Slack-Einstellungen", + "mobile-settings" : "Mobile Einstellungen", + "firebase-service-account-file" : "Firebase-Service-Konto-Anmeldeinformationen (JSON-Datei)", + "select-firebase-service-account-file" : "Ziehen Sie Ihre Firebase-Service-Konto-Datei hierher oder " + }, + "alarm" : { + "alarm" : "Alarm", + "alarms" : "Alarme", + "all-alarms" : "Alle Alarme", + "select-alarm" : "Alarm auswählen", + "no-alarms-matching" : "Keine Alarme gefunden, die mit '{{entity}}' übereinstimmen.", + "alarm-required" : "Alarm ist erforderlich", + "alarm-filter" : "Alarmfilter", + "filter" : "Filter", + "alarm-status" : "Alarmstatus", + "alarm-status-list" : "Liste der Alarmstatus", + "any-status" : "Beliebiger Status", + "search-status" : { + "ANY" : "Beliebig", + "ACTIVE" : "Aktiv", + "CLEARED" : "Zurückgesetzt", + "ACK" : "Bestätigt", + "UNACK" : "Nicht bestätigt" + }, + "display-status" : { + "ACTIVE_UNACK" : "Aktiv nicht bestätigt", + "ACTIVE_ACK" : "Aktiv bestätigt", + "CLEARED_UNACK" : "Zurückgesetzt nicht bestätigt", + "CLEARED_ACK" : "Zurückgesetzt bestätigt" + }, + "no-alarms-prompt" : "Keine Alarme gefunden", + "created-time" : "Erstellungszeit", + "type" : "Typ", + "severity" : "Schweregrad", + "originator" : "Auslöser", + "originator-type" : "Typ des Auslösers", + "details" : "Details", + "originator-label" : "Auslöser-Label", + "assign" : "Zuweisen", + "assignments" : "Zuweisungen", + "assignee" : "Zuständiger", + "assignee-id" : "ID des Zuständigen", + "assignee-first-name" : "Vorname des Zuständigen", + "assignee-last-name" : "Nachname des Zuständigen", + "assignee-email" : "E-Mail des Zuständigen", + "unassigned" : "Nicht zugewiesen", + "user-deleted" : "Benutzer gelöscht", + "assignee-not-set" : "Alle", + "status" : "Status", + "alarm-details" : "Alarmdetails", + "start-time" : "Startzeit", + "assign-time" : "Zuweisungszeit", + "end-time" : "Endzeit", + "ack-time" : "Bestätigungszeit", + "clear-time" : "Zurücksetzungszeit", + "duration" : "Dauer", + "alarm-severity" : "Alarmschweregrad", + "alarm-severity-list" : "Liste der Alarmschweregrade", + "any-severity" : "Beliebiger Schweregrad", + "severity-critical" : "Kritisch", + "severity-major" : "Hoch", + "severity-minor" : "Niedrig", + "severity-warning" : "Warnung", + "severity-indeterminate" : "Unbestimmt", + "acknowledge" : "Bestätigen", + "clear" : "Zurücksetzen", + "delete" : "Löschen", + "search" : "Alarme suchen", + "selected-alarms" : "{ count, plural, =1 {1 Alarm} other {# Alarme} } ausgewählt", + "no-data" : "Keine Daten zum Anzeigen", + "polling-interval" : "Alarm-Abfrageintervall (Sek.)", + "polling-interval-required" : "Alarm-Abfrageintervall ist erforderlich.", + "min-polling-interval-message" : "Mindestens 1 Sekunde Abfrageintervall ist erlaubt.", + "aknowledge-alarms-title" : "{ count, plural, =1 {1 Alarm bestätigen} other {# Alarme bestätigen} }", + "aknowledge-alarms-text" : "Möchten Sie { count, plural, =1 {1 Alarm} other {# Alarme} } wirklich bestätigen?", + "aknowledge-alarm-title" : "Alarm bestätigen", + "aknowledge-alarm-text" : "Möchten Sie den Alarm wirklich bestätigen?", + "selected-alarms-are-acknowledged" : "Ausgewählte Alarme sind bereits bestätigt", + "clear-alarms-title" : "{ count, plural, =1 {1 Alarm zurücksetzen} other {# Alarme zurücksetzen} }", + "clear-alarms-text" : "Möchten Sie { count, plural, =1 {1 Alarm} other {# Alarme} } wirklich zurücksetzen?", + "clear-alarm-title" : "Alarm zurücksetzen", + "clear-alarm-text" : "Möchten Sie den Alarm wirklich zurücksetzen?", + "delete-alarms-title" : "{ count, plural, =1 {1 Alarm löschen} other {# Alarme löschen} }", + "delete-alarms-text" : "Möchten Sie { count, plural, =1 {1 Alarm} other {# Alarme} } wirklich löschen?", + "selected-alarms-are-cleared" : "Ausgewählte Alarme sind bereits zurückgesetzt", + "alarm-status-filter" : "Alarmstatus-Filter", + "alarm-filter-title" : "Alarmfilter", + "assigned" : "Zugewiesen", + "filter-title" : "Filter", + "max-count-load" : "Maximale Anzahl zu ladender Alarme (0 - unbegrenzt)", + "max-count-load-required" : "Maximale Anzahl zu ladender Alarme ist erforderlich.", + "max-count-load-error-min" : "Minimalwert ist 0.", + "fetch-size" : "Abrufgröße", + "fetch-size-required" : "Abrufgröße ist erforderlich.", + "fetch-size-error-min" : "Minimalwert ist 10.", + "alarm-types" : "Alarmtypen", + "alarm-type-list" : "Liste der Alarmtypen", + "any-type" : "Beliebiger Typ", + "assigned-to-current-user" : "Dem aktuellen Benutzer zugewiesen", + "assigned-to-me" : "Mir zugewiesen", + "search-propagated-alarms" : "Verbreitete Alarme durchsuchen", + "comments" : "Alarmkommentare", + "show-more" : "Mehr anzeigen", + "additional-info" : "Zusätzliche Informationen", + "alarm-type" : "Alarmtyp", + "enter-alarm-type" : "Alarmtyp eingeben", + "no-alarm-types-matching" : "Keine Alarmtypen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "alarm-type-list-empty" : "Keine Alarmtypen ausgewählt." + }, + "alarm-activity" : { + "add" : "Kommentar hinzufügen...", + "alarm-comment" : "Alarmkommentar", + "comments" : "Kommentare", + "delete-alarm-comment" : "Möchten Sie diesen Kommentar löschen?", + "refresh" : "Aktualisieren", + "oldest-first" : "Älteste zuerst", + "newest-first" : "Neueste zuerst", + "activity" : "Aktivität", + "export" : "Als CSV exportieren", + "author" : "Autor", + "created-date" : "Erstellungsdatum", + "edited-date" : "Bearbeitungsdatum", + "text" : "Text", + "system" : "System" + }, + "alias" : { + "add" : "Alias hinzufügen", + "edit" : "Alias bearbeiten", + "name" : "Aliasname", + "name-required" : "Aliasname ist erforderlich", + "duplicate-alias" : "Ein Alias mit demselben Namen existiert bereits.", + "filter-type-single-entity" : "Einzelne Entität", + "filter-type-entity-list" : "Entitätsliste", + "filter-type-entity-name" : "Entitätsname", + "filter-type-entity-type" : "Entitätstyp", + "filter-type-state-entity" : "Entität aus Dashboard-Zustand", + "filter-type-state-entity-description" : "Entität aus Dashboard-Zustandsparametern", + "filter-type-asset-type" : "Asset-Typ", + "filter-type-asset-type-description" : "Assets vom Typ '{{assetTypes}}'", + "filter-type-asset-type-and-name-description" : "Assets vom Typ '{{assetTypes}}' mit Namen beginnend mit '{{prefix}}'", + "filter-type-device-type" : "Gerätetyp", + "filter-type-device-type-description" : "Geräte vom Typ '{{deviceTypes}}'", + "filter-type-device-type-and-name-description" : "Geräte vom Typ '{{deviceTypes}}' mit Namen beginnend mit '{{prefix}}'", + "filter-type-entity-view-type" : "Entitätsansichtstyp", + "filter-type-entity-view-type-description" : "Entitätsansichten vom Typ '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description" : "Entitätsansichten vom Typ '{{entityViewTypes}}' mit Namen beginnend mit '{{prefix}}'", + "filter-type-edge-type" : "Edge-Typ", + "filter-type-edge-type-description" : "Edges vom Typ '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description" : "Edges vom Typ '{{edgeTypes}}' mit Namen beginnend mit '{{prefix}}'", + "filter-type-relations-query" : "Beziehungsabfrage", + "filter-type-relations-query-description" : "{{entities}}, die eine '{{relationType}}'-Beziehung '{{direction}}' zu '{{rootEntity}}' haben", + "filter-type-edge-search-query" : "Edge-Suchabfrage", + "filter-type-edge-search-query-description" : "Edges vom Typ {{edgeTypes}} mit '{{relationType}}'-Beziehung '{{direction}}' zu {{rootEntity}}", + "filter-type-asset-search-query" : "Asset-Suchabfrage", + "filter-type-asset-search-query-description" : "Assets vom Typ {{assetTypes}} mit '{{relationType}}'-Beziehung '{{direction}}' zu {{rootEntity}}", + "filter-type-device-search-query" : "Geräte-Suchabfrage", + "filter-type-device-search-query-description" : "Geräte vom Typ {{deviceTypes}} mit '{{relationType}}'-Beziehung '{{direction}}' zu {{rootEntity}}", + "filter-type-entity-view-search-query" : "Entitätsansicht-Suchabfrage", + "filter-type-entity-view-search-query-description" : "Entitätsansichten vom Typ {{entityViewTypes}} mit '{{relationType}}'-Beziehung '{{direction}}' zu {{rootEntity}}", + "filter-type-apiUsageState" : "API-Nutzungsstatus", + "entity-filter" : "Entitätsfilter", + "resolve-multiple" : "Als mehrere Entitäten auflösen", + "resolve-multiple-hint" : "Aktivieren, um Daten aller gefilterten Entitäten gleichzeitig anzuzeigen. \nWenn deaktiviert, zeigt das Widget nur die Daten der ausgewählten Entität an.", + "filter-type" : "Filtertyp", + "filter-type-required" : "Filtertyp ist erforderlich.", + "entity-filter-no-entity-matched" : "Keine Entitäten entsprechen dem angegebenen Filter.", + "no-entity-filter-specified" : "Kein Entitätsfilter angegeben", + "root-state-entity" : "Dashboard-Zustandsentität als Root verwenden", + "last-level-relation" : "Nur letzte Beziehungsebene abrufen", + "root-entity" : "Root-Entität", + "state-entity-parameter-name" : "Parametername der Zustandsentität", + "default-state-entity" : "Standard-Zustandsentität", + "default-entity-parameter-name" : "Standardmäßig", + "max-relation-level" : "Maximale Beziehungsebene", + "unlimited-level" : "Unbegrenzte Ebene", + "state-entity" : "Dashboard-Zustandsentität", + "all-entities" : "Alle Entitäten", + "any-relation" : "beliebige" + }, + "asset" : { + "asset" : "Asset", + "assets" : "Assets", + "management" : "Asset-Verwaltung", + "view-assets" : "Assets anzeigen", + "add" : "Asset hinzufügen", + "asset-type-max-length" : "Asset-Typ sollte weniger als 256 Zeichen haben", + "assign-to-customer" : "Dem Kunden zuweisen", + "assign-asset-to-customer" : "Asset(s) dem Kunden zuweisen", + "assign-asset-to-customer-text" : "Bitte wählen Sie die Assets aus, die dem Kunden zugewiesen werden sollen", + "no-assets-text" : "Keine Assets gefunden", + "assign-to-customer-text" : "Bitte wählen Sie den Kunden aus, dem das/die Asset(s) zugewiesen werden sollen", + "public" : "Öffentlich", + "assignedToCustomer" : "Dem Kunden zugewiesen", + "make-public" : "Asset öffentlich machen", + "make-private" : "Asset privat machen", + "unassign-from-customer" : "Von Kunde entfernen", + "delete" : "Asset löschen", + "asset-public" : "Asset ist öffentlich", + "asset-type" : "Asset-Typ", + "asset-type-required" : "Asset-Typ ist erforderlich.", + "select-asset-type" : "Asset-Typ auswählen", + "enter-asset-type" : "Asset-Profil eingeben", + "any-asset" : "Beliebiges Asset", + "no-asset-types-matching" : "Keine Asset-Typen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "asset-type-list-empty" : "Keine Asset-Typen ausgewählt.", + "asset-types" : "Asset-Typen", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "name-max-length" : "Name sollte weniger als 256 Zeichen haben", + "label-max-length" : "Label sollte weniger als 256 Zeichen haben", + "description" : "Beschreibung", + "type" : "Typ", + "type-required" : "Typ ist erforderlich.", + "details" : "Details", + "events" : "Ereignisse", + "add-asset-text" : "Neues Asset hinzufügen", + "asset-details" : "Asset-Details", + "assign-assets" : "Assets zuweisen", + "assign-assets-text" : "{ count, plural, =1 {1 Asset} other {# Assets} } dem Kunden zuweisen", + "assign-asset-to-edge-title" : "Asset(s) einem Edge zuweisen", + "assign-asset-to-edge-text" : "Bitte wählen Sie die Assets aus, die dem Edge zugewiesen werden sollen", + "delete-assets" : "Assets löschen", + "unassign-assets" : "Assets entfernen", + "unassign-assets-action-title" : "{ count, plural, =1 {1 Asset} other {# Assets} } vom Kunden entfernen", + "assign-new-asset" : "Neues Asset zuweisen", + "delete-asset-title" : "Möchten Sie das Asset '{{assetName}}' wirklich löschen?", + "delete-asset-text" : "Vorsicht, nach der Bestätigung wird das Asset und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-assets-title" : "Möchten Sie { count, plural, =1 {1 Asset} other {# Assets} } wirklich löschen?", + "delete-assets-action-title" : "{ count, plural, =1 {1 Asset löschen} other {# Assets löschen} }", + "delete-assets-text" : "Vorsicht, nach der Bestätigung werden alle ausgewählten Assets entfernt und alle zugehörigen Daten werden unwiederbringlich gelöscht.", + "make-public-asset-title" : "Möchten Sie das Asset '{{assetName}}' wirklich öffentlich machen?", + "make-public-asset-text" : "Nach der Bestätigung wird das Asset und alle zugehörigen Daten öffentlich zugänglich sein.", + "make-private-asset-title" : "Möchten Sie das Asset '{{assetName}}' wirklich privat machen?", + "make-private-asset-text" : "Nach der Bestätigung wird das Asset und alle zugehörigen Daten privat sein und nicht für andere zugänglich.", + "unassign-asset-title" : "Möchten Sie das Asset '{{assetName}}' wirklich entfernen?", + "unassign-asset-text" : "Nach der Bestätigung wird das Asset entfernt und ist für den Kunden nicht mehr zugänglich.", + "unassign-asset" : "Asset entfernen", + "unassign-assets-title" : "Möchten Sie { count, plural, =1 {1 Asset} other {# Assets} } wirklich entfernen?", + "unassign-assets-text" : "Nach der Bestätigung werden alle ausgewählten Assets entfernt und sind für den Kunden nicht mehr zugänglich.", + "copyId" : "Asset-ID kopieren", + "idCopiedMessage" : "Asset-ID wurde in die Zwischenablage kopiert", + "select-asset" : "Asset auswählen", + "no-assets-matching" : "Keine Assets gefunden, die mit '{{entity}}' übereinstimmen.", + "asset-required" : "Asset ist erforderlich", + "name-starts-with" : "Ausdruck für Assetnamen", + "help-text" : "Verwenden Sie '%' je nach Bedarf: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search" : "Assets suchen", + "import" : "Assets importieren", + "asset-file" : "Asset-Datei", + "label" : "Label", + "assign-asset-to-edge" : "Asset(s) einem Edge zuweisen", + "unassign-asset-from-edge" : "Asset vom Edge entfernen", + "unassign-asset-from-edge-title" : "Möchten Sie das Asset '{{assetName}}' wirklich vom Edge entfernen?", + "unassign-asset-from-edge-text" : "Nach der Bestätigung wird das Asset entfernt und ist für den Edge nicht mehr zugänglich.", + "unassign-assets-from-edge-title" : "Möchten Sie { count, plural, =1 {1 Asset} other {# Assets} } wirklich vom Edge entfernen?", + "unassign-assets-from-edge-text" : "Nach der Bestätigung werden alle ausgewählten Assets entfernt und sind für den Edge nicht mehr zugänglich.", + "selected-assets" : "{ count, plural, =1 {1 Asset} other {# Assets} } ausgewählt" + }, + "attribute" : { + "attributes" : "Attribute", + "latest-telemetry" : "Neueste Telemetrie", + "no-latest-telemetry" : "Keine neueste Telemetrie", + "attributes-scope" : "Bereich der Entitätsattribute", + "scope-telemetry" : "Telemetrie", + "scope-latest-telemetry" : "Neueste Telemetrie", + "scope-client" : "Client-Attribute", + "scope-server" : "Server-Attribute", + "scope-shared" : "Geteilte Attribute", + "scope-client-short" : "Client", + "scope-server-short" : "Server", + "scope-shared-short" : "Geteilt", + "scope-latest-short" : "Neueste", + "scope-any" : "Beliebig", + "add" : "Attribut hinzufügen", + "key" : "Schlüssel", + "key-max-length" : "Schlüssel sollte weniger als 256 Zeichen enthalten", + "last-update-time" : "Letzte Aktualisierungszeit", + "key-required" : "Attributschlüssel ist erforderlich.", + "value" : "Wert", + "value-required" : "Attributwert ist erforderlich.", + "telemetry-key-required" : "Telemetrieschlüssel ist erforderlich", + "telemetry-value-required" : "Telemetriewert ist erforderlich", + "delete-attributes-title" : "Möchten Sie { count, plural, =1 {1 Attribut} other {# Attribute} } wirklich löschen?", + "delete-attributes-text" : "Vorsicht, nach der Bestätigung werden alle ausgewählten Attribute entfernt.", + "delete-attributes" : "Attribute löschen", + "enter-attribute-value" : "Attributwert eingeben", + "show-on-widget" : "Im Widget anzeigen", + "widget-mode" : "Widget-Modus", + "next-widget" : "Nächstes Widget", + "prev-widget" : "Vorheriges Widget", + "add-to-dashboard" : "Zum Dashboard hinzufügen", + "add-widget-to-dashboard" : "Widget zum Dashboard hinzufügen", + "selected-attributes" : "{ count, plural, =1 {1 Attribut} other {# Attribute} } ausgewählt", + "selected-telemetry" : "{ count, plural, =1 {1 Telemetrieeinheit} other {# Telemetrieeinheiten} } ausgewählt", + "no-attributes-text" : "Keine Attribute gefunden", + "no-telemetry-text" : "Keine Telemetrie gefunden", + "copy-key" : "Schlüssel kopieren", + "add-telemetry" : "Telemetrie hinzufügen", + "copy-value" : "Wert kopieren", + "delete-timeseries" : { + "start-time" : "Startzeit", + "ends-on" : "Endet am", + "strategy" : "Strategie", + "delete-strategy" : "Löschstrategie", + "all-data" : "Alle Daten löschen", + "all-data-except-latest-value" : "Alle Daten außer dem neuesten Wert löschen", + "latest-value" : "Neuester Wert löschen", + "all-data-for-time-period" : "Alle Daten für einen Zeitraum löschen", + "rewrite-latest-value" : "Neuester Wert neu schreiben" + } + }, + "api-usage" : { + "api-features" : "API-Funktionen", + "api-usage" : "API-Nutzung", + "alarm" : "Alarm", + "alarms-created" : "Erstellte Alarme", + "queue-stats" : "Warteschlangenstatistiken", + "processing-failures-and-timeouts" : "Verarbeitungsfehler und Zeitüberschreitungen", + "exceptions" : "Ausnahmen", + "alarms-created-daily-activity" : "Tägliche Aktivität der Alarmerstellung", + "alarms-created-hourly-activity" : "Stündliche Aktivität der Alarmerstellung", + "alarms-created-monthly-activity" : "Monatliche Aktivität der Alarmerstellung", + "data-points" : "Datenpunkte", + "data-points-storage-days" : "Speichertage der Datenpunkte", + "device-api" : "Geräte-API", + "email" : "E-Mail", + "email-messages" : "E-Mail-Nachrichten", + "email-messages-daily-activity" : "Tägliche Aktivität der E-Mail-Nachrichten", + "email-messages-monthly-activity" : "Monatliche Aktivität der E-Mail-Nachrichten", + "executions" : "Ausführungen", + "scripts" : "Skripte", + "scripts-hourly-activity" : "Stündliche Skriptaktivität", + "scripts-daily-activity" : "Tägliche Skriptaktivität", + "scripts-monthly-activity" : "Monatliche Skriptaktivität", + "javascript" : "JavaScript", + "javascript-executions" : "JavaScript-Ausführungen", + "tbel" : "TBEL", + "tbel-executions" : "TBEL-Ausführungen", + "latest-error" : "Letzter Fehler", + "messages" : "Nachrichten", + "notifications" : "Benachrichtigungen", + "notifications-email-sms" : "Benachrichtigungen (E-Mail/SMS)", + "notifications-hourly-activity" : "Stündliche Benachrichtigungsaktivität", + "permanent-failures" : "${entityName} Dauerhafte Fehler", + "permanent-timeouts" : "${entityName} Dauerhafte Zeitüberschreitungen", + "processing-failures" : "${entityName} Verarbeitungsfehler", + "processing-timeouts" : "${entityName} Verarbeitungszeitüberschreitungen", + "rule-chain" : "Regelkette", + "rule-engine" : "Regel-Engine", + "rule-engine-daily-activity" : "Tägliche Aktivität der Regel-Engine", + "rule-engine-executions" : "Ausführungen der Regel-Engine", + "rule-engine-hourly-activity" : "Stündliche Aktivität der Regel-Engine", + "rule-engine-monthly-activity" : "Monatliche Aktivität der Regel-Engine", + "rule-engine-statistics" : "Regel-Engine-Statistiken", + "rule-node" : "Regelknoten", + "sms" : "SMS", + "sms-messages" : "SMS-Nachrichten", + "sms-messages-daily-activity" : "Tägliche Aktivität der SMS-Nachrichten", + "sms-messages-monthly-activity" : "Monatliche Aktivität der SMS-Nachrichten", + "successful" : "${entityName} Erfolgreich", + "telemetry" : "Telemetrie", + "telemetry-persistence" : "Telemetriepersistenz", + "telemetry-persistence-daily-activity" : "Tägliche Aktivität der Telemetriepersistenz", + "telemetry-persistence-hourly-activity" : "Stündliche Aktivität der Telemetriepersistenz", + "telemetry-persistence-monthly-activity" : "Monatliche Aktivität der Telemetriepersistenz", + "transport" : "Transport", + "transport-daily-activity" : "Tägliche Transportaktivität", + "transport-data-points" : "Transportierte Datenpunkte", + "transport-hourly-activity" : "Stündliche Transportaktivität", + "transport-messages" : "Transportnachrichten", + "transport-monthly-activity" : "Monatliche Transportaktivität", + "view-details" : "Details anzeigen", + "view-statistics" : "Statistiken anzeigen" + }, + "api-limit" : { + "cassandra-queries" : "Cassandra-Abfragen", + "entity-version-creation" : "Erstellung von Entitätsversionen", + "entity-version-load" : "Laden von Entitätsversionen", + "notification-requests" : "Benachrichtigungsanforderungen", + "notification-requests-per-rule" : "Benachrichtigungsanforderungen pro Regel", + "rest-api-requests" : "REST-API-Anfragen", + "rest-api-requests-per-customer" : "REST-API-Anfragen pro Kunde", + "transport-messages" : "Transportnachrichten", + "transport-messages-per-device" : "Transportnachrichten pro Gerät", + "transport-messages-per-gateway" : "Transportnachrichten pro Gateway", + "transport-messages-per-gateway-device" : "Transportnachrichten pro Gateway-Gerät", + "ws-updates-per-session" : "WebSocket-Aktualisierungen pro Sitzung", + "edge-events" : "Edge-Ereignisse", + "edge-events-per-edge" : "Edge-Ereignisse pro Edge", + "edge-uplink-messages" : "Edge-Uplink-Nachrichten", + "edge-uplink-messages-per-edge" : "Edge-Uplink-Nachrichten pro Edge" + }, + "audit-log" : { + "audit" : "Audit", + "audit-logs" : "Audit-Protokolle", + "timestamp" : "Zeitstempel", + "entity-type" : "Entitätstyp", + "entity-name" : "Entitätsname", + "user" : "Benutzer", + "type" : "Typ", + "status" : "Status", + "details" : "Details", + "type-added" : "Hinzugefügt", + "type-deleted" : "Gelöscht", + "type-updated" : "Aktualisiert", + "type-attributes-updated" : "Attribute aktualisiert", + "type-attributes-deleted" : "Attribute gelöscht", + "type-rpc-call" : "RPC-Aufruf", + "type-credentials-updated" : "Zugangsdaten aktualisiert", + "type-assigned-to-customer" : "Kunde zugewiesen", + "type-unassigned-from-customer" : "Vom Kunden entfernt", + "type-assigned-to-edge" : "Edge zugewiesen", + "type-unassigned-from-edge" : "Von Edge entfernt", + "type-activated" : "Aktiviert", + "type-suspended" : "Suspendiert", + "type-credentials-read" : "Zugangsdaten gelesen", + "type-attributes-read" : "Attribute gelesen", + "type-relation-add-or-update" : "Beziehung aktualisiert", + "type-relation-delete" : "Beziehung gelöscht", + "type-relations-delete" : "Alle Beziehungen gelöscht", + "type-alarm-ack" : "Alarm bestätigt", + "type-alarm-clear" : "Alarm zurückgesetzt", + "type-alarm-delete" : "Alarm gelöscht", + "type-alarm-assign" : "Alarm zugewiesen", + "type-alarm-unassign" : "Alarm nicht zugewiesen", + "type-added-comment" : "Kommentar hinzugefügt", + "type-updated-comment" : "Kommentar aktualisiert", + "type-deleted-comment" : "Kommentar gelöscht", + "type-login" : "Login", + "type-logout" : "Logout", + "type-lockout" : "Sperrung", + "status-success" : "Erfolgreich", + "status-failure" : "Fehlgeschlagen", + "audit-log-details" : "Audit-Log-Details", + "no-audit-logs-prompt" : "Keine Protokolle gefunden", + "action-data" : "Aktionsdaten", + "failure-details" : "Fehlerdetails", + "search" : "Audit-Logs durchsuchen", + "clear-search" : "Suche zurücksetzen", + "type-assigned-from-tenant" : "Vom Mandanten zugewiesen", + "type-assigned-to-tenant" : "Mandant zugewiesen", + "type-provision-success" : "Gerät bereitgestellt", + "type-provision-failure" : "Gerätebereitstellung fehlgeschlagen", + "type-timeseries-updated" : "Telemetrie aktualisiert", + "type-timeseries-deleted" : "Telemetrie gelöscht", + "type-sms-sent" : "SMS gesendet" + }, + "debug-settings" : { + "label" : "Debug-Konfiguration", + "on-failure" : "Nur Fehler (24/7)", + "all-messages" : "Alle Nachrichten ({{time}})", + "failures" : "Fehler", + "entity" : "Entität", + "hint" : { + "main-limited" : "Nicht mehr als {{msg}} {{entity}} Debug-Nachrichten pro {{time}} werden aufgezeichnet.", + "on-failure" : "Nur Fehlermeldungen protokollieren.", + "all-messages" : "Alle Debug-Nachrichten protokollieren." + } + }, + "calculated-fields" : { + "expression" : "Ausdruck", + "no-found" : "Keine berechneten Felder gefunden", + "list" : "{ count, plural, =1 {Ein berechnetes Feld} other {Liste von # berechneten Feldern} }", + "selected-fields" : "{ count, plural, =1 {1 berechnetes Feld} other {# berechnete Felder} } ausgewählt", + "type" : { + "simple" : "Einfach", + "script" : "Skript" + }, + "arguments" : "Argumente", + "decimals-by-default" : "Standard-Dezimalstellen", + "debugging" : "Berechnetes Feld Debugging", + "argument-name" : "Argumentname", + "datasource" : "Datenquelle", + "add-argument" : "Argument hinzufügen", + "test-script-function" : "Skriptfunktion testen", + "no-arguments" : "Keine Argumente konfiguriert", + "argument-settings" : "Argumenteinstellungen", + "argument-current" : "Aktuelle Entität", + "argument-current-tenant" : "Aktueller Mandant", + "argument-device" : "Gerät", + "argument-asset" : "Asset", + "argument-customer" : "Kunde", + "argument-tenant" : "Aktueller Mandant", + "argument-type" : "Argumenttyp", + "see-debug-events" : "Debug-Ereignisse anzeigen", + "attribute" : "Attribut", + "copy-argument-name" : "Argumentnamen kopieren", + "timeseries-key" : "Zeitreihen-Schlüssel", + "device-name" : "Gerätename", + "latest-telemetry" : "Neueste Telemetrie", + "rolling" : "Zeitreihen-Rollup", + "attribute-scope" : "Attributbereich", + "server-attributes" : "Server-Attribute", + "client-attributes" : "Client-Attribute", + "shared-attributes" : "Geteilte Attribute", + "attribute-key" : "Attribut-Schlüssel", + "default-value" : "Standardwert", + "limit" : "Maximale Werte", + "time-window" : "Zeitfenster", + "customer-name" : "Kundenname", + "asset-name" : "Asset-Name", + "timeseries" : "Zeitreihe", + "output" : "Ausgabe", + "create" : "Neues berechnetes Feld erstellen", + "file" : "Berechnetes Feld-Datei", + "invalid-file-error" : "Ungültiges Dateiformat. Bitte stellen Sie sicher, dass die Datei eine gültige JSON-Datei ist.", + "import" : "Berechnetes Feld importieren", + "export" : "Berechnetes Feld exportieren", + "export-failed-error" : "Berechnetes Feld konnte nicht exportiert werden: {{error}}", + "output-type" : "Ausgabetyp", + "delete-title" : "Möchten Sie das berechnete Feld '{{title}}' wirklich löschen?", + "delete-text" : "Vorsicht, nach der Bestätigung wird das berechnete Feld und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-multiple-title" : "Möchten Sie { count, plural, =1 {1 berechnetes Feld} other {# berechnete Felder} } wirklich löschen?", + "delete-multiple-text" : "Vorsicht, nach der Bestätigung werden alle ausgewählten berechneten Felder entfernt und alle zugehörigen Daten unwiederbringlich gelöscht.", + "test-with-this-message" : "Mit dieser Nachricht testen", + "hint" : { + "arguments-simple-with-rolling" : "Einfacher Feldtyp darf keine Schlüssel mit Zeitreihen-Rollup-Typ enthalten.", + "arguments-empty" : "Argumente dürfen nicht leer sein.", + "expression-required" : "Ausdruck ist erforderlich.", + "expression-invalid" : "Ausdruck ist ungültig", + "expression-max-length" : "Ausdruck sollte weniger als 255 Zeichen enthalten.", + "argument-name-required" : "Argumentname ist erforderlich.", + "argument-name-pattern" : "Argumentname ist ungültig.", + "argument-name-duplicate" : "Ein Argument mit diesem Namen existiert bereits.", + "argument-name-max-length" : "Argumentname sollte weniger als 256 Zeichen enthalten.", + "argument-name-forbidden" : "Argumentname ist reserviert und darf nicht verwendet werden.", + "argument-type-required" : "Argumenttyp ist erforderlich.", + "max-args" : "Maximale Anzahl an Argumenten erreicht.", + "decimals-range" : "Standard-Dezimalstellen sollten eine Zahl zwischen 0 und 15 sein.", + "expression" : "Standardausdruck demonstriert, wie eine Temperatur von Fahrenheit in Celsius umgewandelt wird.", + "arguments-entity-not-found" : "Zielentität des Arguments nicht gefunden." + } + }, + "confirm-on-exit" : { + "message" : "Sie haben ungespeicherte Änderungen. Möchten Sie diese Seite wirklich verlassen?", + "html-message" : "Sie haben ungespeicherte Änderungen.
Möchten Sie diese Seite wirklich verlassen?", + "title" : "Ungespeicherte Änderungen" + }, + "contact" : { + "country" : "Land", + "country-required" : "Land ist erforderlich.", + "city" : "Stadt", + "state" : "Bundesland / Provinz", + "postal-code" : "PLZ / Postleitzahl", + "postal-code-invalid" : "Ungültiges Format der Postleitzahl.", + "address" : "Adresse", + "address2" : "Adresse 2", + "phone" : "Telefon", + "email" : "E-Mail", + "no-address" : "Keine Adresse", + "no-country-found" : "Keine Länder gefunden.", + "no-country-matching" : "Kein Land gefunden, das mit '{{country}}' übereinstimmt.", + "state-max-length" : "Bundesland sollte weniger als 256 Zeichen enthalten", + "phone-max-length" : "Telefonnummer sollte weniger als 256 Zeichen enthalten", + "city-max-length" : "Stadtname sollte weniger als 256 Zeichen enthalten" + }, + "common" : { + "name" : "Name", + "type" : "Typ", + "general" : "Allgemein", + "username" : "Benutzername", + "password" : "Passwort", + "data" : "Daten", + "timestamp" : "Zeitstempel", + "enter-username" : "Benutzernamen eingeben", + "enter-password" : "Passwort eingeben", + "enter-search" : "Suche eingeben", + "created-time" : "Erstellungszeit", + "disabled" : "Deaktiviert", + "loading" : "Wird geladen...", + "proceed" : "Fortfahren", + "open-details-page" : "Detailseite öffnen", + "not-found" : "Nicht gefunden", + "value" : "Wert", + "documentation" : "Dokumentation", + "time-left" : "{{time}} verbleibend", + "output" : "Ausgabe", + "suffix" : { + "s" : "s", + "ms" : "ms" + }, + "hint" : { + "name-required" : "Name ist erforderlich.", + "name-pattern" : "Name ist ungültig.", + "name-max-length" : "Name sollte weniger als 256 Zeichen enthalten.", + "title-required" : "Titel ist erforderlich.", + "title-pattern" : "Titel ist ungültig.", + "title-max-length" : "Titel sollte weniger als 256 Zeichen enthalten.", + "key-required" : "Schlüssel ist erforderlich.", + "key-pattern" : "Schlüssel ist ungültig.", + "key-max-length" : "Schlüssel sollte weniger als 256 Zeichen enthalten." + }, + "required-fields" : "Erforderliche Felder fehlen" + }, + "content-type" : { + "json" : "Json", + "text" : "Text", + "binary" : "Binär (Base64)" + }, + "color" : { + "color" : "Farbe" + }, + "customer" : { + "customer" : "Kunde", + "customers" : "Kunden", + "management" : "Kundenverwaltung", + "dashboard" : "Kundendashboard", + "dashboards" : "Kundendashboards", + "devices" : "Kundengeräte", + "entity-views" : "Kundenansichten", + "assets" : "Kundenassets", + "public-dashboards" : "Öffentliche Dashboards", + "public-devices" : "Öffentliche Geräte", + "public-assets" : "Öffentliche Assets", + "public-entity-views" : "Öffentliche Entitätsansichten", + "add" : "Kunde hinzufügen", + "delete" : "Kunde löschen", + "manage-customer-users" : "Kundennutzer verwalten", + "manage-customer-devices" : "Kundengeräte verwalten", + "manage-customer-dashboards" : "Kundendashboards verwalten", + "manage-public-devices" : "Öffentliche Geräte verwalten", + "manage-public-dashboards" : "Öffentliche Dashboards verwalten", + "manage-customer-assets" : "Kundenassets verwalten", + "manage-customer-edges" : "Kunden-Edges verwalten", + "manage-public-assets" : "Öffentliche Assets verwalten", + "add-customer-text" : "Neuen Kunden hinzufügen", + "no-customers-text" : "Keine Kunden gefunden", + "customer-details" : "Kundendetails", + "delete-customer-title" : "Möchten Sie den Kunden '{{customerTitle}}' wirklich löschen?", + "delete-customer-text" : "Achtung, nach der Bestätigung werden der Kunde und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-customers-title" : "Möchten Sie { count, plural, =1 {1 Kunden} other {# Kunden} } wirklich löschen?", + "delete-customers-action-title" : "{ count, plural, =1 {1 Kunden löschen} other {# Kunden löschen} }", + "delete-customers-text" : "Achtung, nach der Bestätigung werden alle ausgewählten Kunden und alle zugehörigen Daten gelöscht.", + "manage-users" : "Benutzer verwalten", + "manage-assets" : "Assets verwalten", + "manage-devices" : "Geräte verwalten", + "manage-dashboards" : "Dashboards verwalten", + "title" : "Titel", + "title-required" : "Titel ist erforderlich.", + "title-max-length" : "Titel sollte weniger als 256 Zeichen enthalten", + "description" : "Beschreibung", + "details" : "Details", + "events" : "Ereignisse", + "copyId" : "Kunden-ID kopieren", + "idCopiedMessage" : "Kunden-ID wurde in die Zwischenablage kopiert", + "select-customer" : "Kunde auswählen", + "no-customers-matching" : "Keine Kunden gefunden, die mit '{{entity}}' übereinstimmen.", + "customer-required" : "Kunde ist erforderlich", + "select-default-customer" : "Standardkunden auswählen", + "default-customer" : "Standardkunde", + "default-customer-required" : "Standardkunde ist erforderlich, um das Dashboard auf Mandantenebene zu debuggen", + "search" : "Kunden suchen", + "selected-customers" : "{ count, plural, =1 {1 Kunde} other {# Kunden} } ausgewählt", + "edges" : "Edge-Instanzen des Kunden", + "manage-edges" : "Edges verwalten" + }, + "css-size" : { + "size-value-required" : "Größenwert ist erforderlich", + "invalid-size-value" : "Ungültiger Größenwert" + }, + "date" : { + "last-update-n-ago" : "Letzte Aktualisierung vor N", + "last-update-n-ago-text" : "Letzte Aktualisierung {{ agoText }}", + "custom-date" : "Benutzerdefiniertes Datum", + "format" : "Format", + "preview" : "Vorschau", + "auto" : "Automatisch", + "time-granularity-formats" : "Formate nach Zeitgranularität", + "unit-year" : "Jahre", + "unit-month" : "Monate", + "unit-day" : "Tage", + "unit-hour" : "Stunden", + "unit-minute" : "Minuten", + "unit-second" : "Sekunden", + "unit-millisecond" : "Millisekunden" + }, + "datetime" : { + "date-from" : "Datum von", + "time-from" : "Zeit von", + "date-to" : "Datum bis", + "time-to" : "Zeit bis", + "from" : "Von", + "to" : "Bis" + }, + "dashboard" : { + "dashboard" : "Dashboard", + "dashboards" : "Dashboards", + "management" : "Dashboard-Verwaltung", + "view-dashboards" : "Dashboards anzeigen", + "add" : "Dashboard hinzufügen", + "assign-dashboard-to-customer" : "Dashboard(s) dem Kunden zuweisen", + "assign-dashboard-to-customer-text" : "Bitte wählen Sie die Dashboards aus, die dem Kunden zugewiesen werden sollen", + "assign-to-customer-text" : "Bitte wählen Sie den Kunden aus, dem das Dashboard zugewiesen werden soll", + "assign-to-customer" : "Dem Kunden zuweisen", + "unassign-from-customer" : "Von Kunde entfernen", + "make-public" : "Dashboard öffentlich machen", + "make-private" : "Dashboard privat machen", + "manage-assigned-customers" : "Zugewiesene Kunden verwalten", + "assigned-customers" : "Zugewiesene Kunden", + "assign-to-customers" : "Dashboard(s) Kunden zuweisen", + "assign-to-customers-text" : "Bitte wählen Sie die Kunden aus, denen das Dashboard zugewiesen werden soll", + "unassign-from-customers" : "Dashboard(s) von Kunden entfernen", + "unassign-from-customers-text" : "Bitte wählen Sie die Kunden aus, von denen das Dashboard entfernt werden soll", + "no-dashboards-text" : "Keine Dashboards gefunden", + "no-widgets" : "Keine Widgets konfiguriert", + "add-widget" : "Neues Widget hinzufügen", + "add-widget-button-text" : "Widget hinzufügen", + "title" : "Titel", + "image" : "Dashboard-Bild", + "mobile-app-settings" : "Einstellungen für mobile App", + "mobile-order" : "Reihenfolge des Dashboards in mobiler App", + "mobile-hide" : "Dashboard in mobiler App ausblenden", + "update-image" : "Dashboard-Bild aktualisieren", + "take-screenshot" : "Screenshot erstellen", + "select-widget-title" : "Widget auswählen", + "select-widget-value" : "{{title}}: Widget auswählen", + "select-widget-subtitle" : "Liste verfügbarer Widget-Typen", + "delete" : "Dashboard löschen", + "title-required" : "Titel ist erforderlich.", + "title-max-length" : "Titel sollte weniger als 256 Zeichen enthalten", + "description" : "Beschreibung", + "details" : "Details", + "dashboard-details" : "Dashboard-Details", + "add-dashboard-text" : "Neues Dashboard hinzufügen", + "assign-dashboards" : "Dashboards zuweisen", + "assign-new-dashboard" : "Neues Dashboard zuweisen", + "assign-dashboards-text" : "{ count, plural, =1 {1 Dashboard} other {# Dashboards} } Kunden zuweisen", + "unassign-dashboards-action-text" : "{ count, plural, =1 {1 Dashboard} other {# Dashboards} } von Kunden entfernen", + "delete-dashboards" : "Dashboards löschen", + "unassign-dashboards" : "Dashboards entfernen", + "unassign-dashboards-action-title" : "{ count, plural, =1 {1 Dashboard} other {# Dashboards} } von Kunde entfernen", + "delete-dashboard-title" : "Möchten Sie das Dashboard '{{dashboardTitle}}' wirklich löschen?", + "delete-dashboard-text" : "Achtung, nach der Bestätigung wird das Dashboard und alle zugehörigen Daten gelöscht.", + "delete-dashboards-title" : "Möchten Sie { count, plural, =1 {1 Dashboard} other {# Dashboards} } wirklich löschen?", + "delete-dashboards-action-title" : "{ count, plural, =1 {1 Dashboard löschen} other {# Dashboards löschen} }", + "delete-dashboards-text" : "Achtung, nach der Bestätigung werden alle ausgewählten Dashboards und deren Daten gelöscht.", + "unassign-dashboard-title" : "Möchten Sie das Dashboard '{{dashboardTitle}}' wirklich entfernen?", + "unassign-dashboard-text" : "Nach der Bestätigung wird das Dashboard entfernt und ist für den Kunden nicht mehr zugänglich.", + "unassign-dashboard" : "Dashboard entfernen", + "unassign-dashboards-title" : "Möchten Sie { count, plural, =1 {1 Dashboard} other {# Dashboards} } wirklich entfernen?", + "unassign-dashboards-text" : "Nach der Bestätigung werden alle ausgewählten Dashboards entfernt und sind für den Kunden nicht mehr zugänglich.", + "public-dashboard-title" : "Dashboard ist jetzt öffentlich", + "public-dashboard-text" : "Ihr Dashboard {{dashboardTitle}} ist jetzt öffentlich über folgenden Link zugänglich:", + "public-dashboard-notice" : "Hinweis: Vergessen Sie nicht, zugehörige Geräte öffentlich zu machen, damit deren Daten zugänglich sind.", + "make-private-dashboard-title" : "Möchten Sie das Dashboard '{{dashboardTitle}}' wirklich privat machen?", + "make-private-dashboard-text" : "Nach der Bestätigung wird das Dashboard privat und ist für andere nicht mehr zugänglich.", + "make-private-dashboard" : "Dashboard privat machen", + "socialshare-text" : "'{{dashboardTitle}}' bereitgestellt von ThingsBoard", + "socialshare-title" : "'{{dashboardTitle}}' bereitgestellt von ThingsBoard", + "select-dashboard" : "Dashboard auswählen", + "no-dashboards-matching" : "Keine Dashboards gefunden, die mit '{{entity}}' übereinstimmen.", + "dashboard-required" : "Dashboard ist erforderlich.", + "select-existing" : "Vorhandenes Dashboard auswählen", + "create-new" : "Neues Dashboard erstellen", + "new-dashboard-title" : "Titel des neuen Dashboards", + "open-dashboard" : "Dashboard öffnen", + "set-background" : "Hintergrund festlegen", + "background-color" : "Hintergrundfarbe", + "background-image" : "Hintergrundbild", + "background-size-mode" : "Größenmodus des Hintergrunds", + "no-image" : "Kein Bild ausgewählt", + "empty-image" : "Kein Bild", + "drop-image" : "Bild ablegen oder klicken, um eine Datei auszuwählen.", + "maximum-upload-file-size" : "Maximale Dateigröße für Upload: {{ size }}", + "cannot-upload-file" : "Datei kann nicht hochgeladen werden", + "settings" : "Einstellungen", + "move-all-widgets" : "Alle Widgets verschieben", + "move-by" : "Verschieben um", + "cols" : "Spalten", + "rows" : "Zeilen", + "layout" : "Layout", + "layout-type-default" : "Standard", + "layout-type-scada" : "SCADA", + "layout-type-divider" : "Trennlinie", + "layout-settings-type" : "Layout-Einstellungen: {{ type }} Breakpoint", + "columns-count" : "Spaltenanzahl", + "columns-count-required" : "Spaltenanzahl ist erforderlich.", + "min-columns-count-message" : "Mindestens 10 Spalten sind erlaubt.", + "max-columns-count-message" : "Höchstens 1000 Spalten sind erlaubt.", + "min-layout-width" : "Minimale Layout-Breite", + "columns-suffix" : "Spalten", + "widgets-margins" : "Abstand zwischen Widgets", + "margin-required" : "Abstandswert ist erforderlich.", + "min-margin-message" : "Der minimale Abstandswert ist 0.", + "max-margin-message" : "Der maximale Abstandswert ist 50.", + "horizontal-margin" : "Horizontaler Abstand", + "horizontal-margin-required" : "Horizontaler Abstandswert ist erforderlich.", + "min-horizontal-margin-message" : "Der minimale horizontale Abstandswert ist 0.", + "max-horizontal-margin-message" : "Der maximale horizontale Abstandswert ist 50.", + "vertical-margin" : "Vertikaler Abstand", + "vertical-margin-required" : "Vertikaler Abstandswert ist erforderlich.", + "min-vertical-margin-message" : "Der minimale vertikale Abstandswert ist 0.", + "max-vertical-margin-message" : "Der maximale vertikale Abstandswert ist 50.", + "apply-outer-margin" : "Rand auf Layoutseiten anwenden", + "autofill-height" : "Layout-Höhe automatisch ausfüllen", + "mobile-layout" : "Mobile Layout-Einstellungen", + "mobile-row-height" : "Zeilenhöhe für Mobilgeräte", + "mobile-row-height-required" : "Zeilenhöhe für Mobilgeräte ist erforderlich.", + "min-mobile-row-height-message" : "Mindestens 5 Pixel für mobile Zeilenhöhe erlaubt.", + "max-mobile-row-height-message" : "Maximal 200 Pixel für mobile Zeilenhöhe erlaubt.", + "row-height" : "Zeilenhöhe", + "row-height-required" : "Zeilenhöhe ist erforderlich.", + "min-row-height-message" : "Mindestens 5 Pixel als Zeilenhöhe erlaubt.", + "max-row-height-message" : "Maximal 200 Pixel als Zeilenhöhe erlaubt.", + "display-first-in-mobile-view" : "Zuerst in mobiler Ansicht anzeigen", + "title-settings" : "Titel-Einstellungen", + "display-title" : "Dashboard-Titel anzeigen", + "title-color" : "Titelfarbe", + "toolbar-settings" : "Werkzeugleisten-Einstellungen", + "hide-toolbar" : "Werkzeugleiste ausblenden", + "toolbar-always-open" : "Werkzeugleiste immer geöffnet lassen", + "display-dashboards-selection" : "Dashboard-Auswahl anzeigen", + "display-entities-selection" : "Entitätsauswahl anzeigen", + "display-filters" : "Filter anzeigen", + "display-dashboard-timewindow" : "Zeitfenster anzeigen", + "display-dashboard-export" : "Export anzeigen", + "display-update-dashboard-image" : "Aktualisierung des Dashboard-Bilds anzeigen", + "dashboard-logo-settings" : "Dashboard-Logo-Einstellungen", + "display-dashboard-logo" : "Logo im Vollbildmodus anzeigen", + "dashboard-logo-image" : "Dashboard-Logo-Bild", + "advanced-settings" : "Erweiterte Einstellungen", + "dashboard-css" : "Dashboard CSS", + "import" : "Dashboard importieren", + "export" : "Dashboard exportieren", + "export-failed-error" : "Dashboard konnte nicht exportiert werden: {{error}}", + "export-prompt" : "Dashboard-Bilder und -Ressourcen einbetten", + "create-new-dashboard" : "Neues Dashboard erstellen", + "dashboard-file" : "Dashboard-Datei", + "invalid-dashboard-file-error" : "Dashboard konnte nicht importiert werden: Ungültige Datenstruktur.", + "dashboard-import-missing-aliases-title" : "Aliase für importiertes Dashboard konfigurieren", + "create-new-widget" : "Neues Widget erstellen", + "import-widget" : "Widget importieren", + "widget-file" : "Widget-Datei", + "invalid-widget-file-error" : "Widget konnte nicht importiert werden: Ungültige Widget-Datenstruktur.", + "widget-import-missing-aliases-title" : "Aliase für importiertes Widget konfigurieren", + "open-toolbar" : "Dashboard-Werkzeugleiste öffnen", + "close-toolbar" : "Werkzeugleiste schließen", + "configuration-error" : "Konfigurationsfehler", + "alias-resolution-error-title" : "Fehler in der Alias-Konfiguration des Dashboards", + "invalid-aliases-config" : "Für einige Aliase konnten keine passenden Geräte gefunden werden.
Bitte wenden Sie sich an Ihren Administrator.", + "select-devices" : "Geräte auswählen", + "assignedToCustomer" : "Dem Kunden zugewiesen", + "assignedToCustomers" : "Kunden zugewiesen", + "public" : "Öffentlich", + "copyId" : "Dashboard-ID kopieren", + "idCopiedMessage" : "Dashboard-ID wurde in die Zwischenablage kopiert", + "public-link" : "Öffentlicher Link", + "copy-public-link" : "Öffentlichen Link kopieren", + "public-link-copied-message" : "Öffentlicher Dashboard-Link wurde in die Zwischenablage kopiert", + "manage-states" : "Dashboard-Zustände verwalten", + "states" : "Dashboard-Zustände", + "states-short" : "Zustände", + "search-states" : "Dashboard-Zustände suchen", + "selected-states" : "{ count, plural, =1 {1 Dashboard-Zustand} other {# Dashboard-Zustände} } ausgewählt", + "edit-state" : "Dashboard-Zustand bearbeiten", + "delete-state" : "Dashboard-Zustand löschen", + "add-state" : "Dashboard-Zustand hinzufügen", + "no-states-text" : "Keine Zustände gefunden", + "state" : "Dashboard-Zustand", + "state-name" : "Name", + "state-name-required" : "Name des Dashboard-Zustands ist erforderlich.", + "state-id" : "Zustands-ID", + "state-id-required" : "Zustands-ID ist erforderlich.", + "state-id-exists" : "Ein Dashboard-Zustand mit derselben ID existiert bereits.", + "is-root-state" : "Hauptzustand", + "delete-state-title" : "Dashboard-Zustand löschen", + "delete-state-text" : "Möchten Sie den Zustand '{{stateName}}' wirklich löschen?", + "show-details" : "Details anzeigen", + "hide-details" : "Details ausblenden", + "select-state" : "Zielzustand auswählen", + "state-controller" : "Zustandssteuerung", + "state-controller-default" : "statisch (veraltet)", + "search" : "Dashboards suchen", + "selected-dashboards" : "{ count, plural, =1 {1 Dashboard} other {# Dashboards} } ausgewählt", + "home-dashboard" : "Start-Dashboard", + "home-dashboard-hide-toolbar" : "Werkzeugleiste im Start-Dashboard ausblenden", + "unassign-dashboard-from-edge-text" : "Nach der Bestätigung wird das Dashboard entfernt und ist für den Edge nicht mehr zugänglich.", + "unassign-dashboards-from-edge-title" : "Möchten Sie { count, plural, =1 {1 Dashboard} other {# Dashboards} } wirklich entfernen?", + "unassign-dashboards-from-edge-text" : "Nach der Bestätigung werden alle ausgewählten Dashboards entfernt und sind für den Edge nicht mehr zugänglich.", + "assign-dashboard-to-edge" : "Dashboard(s) einem Edge zuweisen", + "assign-dashboard-to-edge-text" : "Bitte wählen Sie die Dashboards aus, die dem Edge zugewiesen werden sollen", + "non-existent-dashboard-state-error" : "Dashboard-Zustand mit ID \"{{ stateId }}\" wurde nicht gefunden", + "edit-mode" : "Bearbeitungsmodus", + "duplicate-state-action" : "Zustand duplizieren", + "breakpoint-value" : "Breakpoint ({{ value }})", + "breakpoints-id" : { + "default" : "Standard", + "xs" : "Mobilgerät (xs)", + "sm" : "Tablet (sm)", + "md" : "Laptop (md)", + "lg" : "Desktop (lg)", + "xl" : "Desktop (xl)" + }, + "view-format-type-grid" : "Raster", + "view-format-type-list" : "Liste", + "view-format" : "Ansichtsformat" + }, + "datakey" : { + "settings" : "Einstellungen", + "general" : "Allgemein", + "advanced" : "Erweitert", + "key" : "Schlüssel", + "keys" : "Schlüssel", + "label" : "Bezeichnung", + "color" : "Farbe", + "units" : "Spezialsymbol neben dem Wert", + "decimals" : "Anzahl der Nachkommastellen", + "data-generation-func" : "Daten-Generierungsfunktion", + "use-data-post-processing-func" : "Daten-Nachbearbeitungsfunktion verwenden", + "configuration" : "Datenkey-Konfiguration", + "timeseries" : "Zeitreihen", + "attributes" : "Attribute", + "entity-field" : "Entitätsfeld", + "alarm" : "Alarmfelder", + "timeseries-required" : "Zeitreihen der Entität sind erforderlich.", + "timeseries-or-attributes-required" : "Zeitreihen/Attribute der Entität sind erforderlich.", + "alarm-fields-timeseries-or-attributes-required" : "Alarmfelder oder Zeitreihen/Attribute der Entität sind erforderlich.", + "maximum-timeseries-or-attributes" : "Maximal { count, plural, =1 {1 Zeitreihe/Attribut erlaubt.} other {# Zeitreihen/Attribute erlaubt} }", + "alarm-fields-required" : "Alarmfelder sind erforderlich.", + "function-types" : "Funktionstypen", + "function-type" : "Funktionstyp", + "function-types-required" : "Funktionstypen sind erforderlich.", + "data-keys" : "Datenkeys", + "data-key" : "Datenkey", + "data-keys-required" : "Datenkeys sind erforderlich.", + "data-key-required" : "Datenkey ist erforderlich.", + "alarm-keys" : "Alarm-Datenkeys", + "alarm-key" : "Alarm-Datenkey", + "alarm-key-functions" : "Funktionen für Alarmkey", + "alarm-key-function" : "Alarmkey-Funktion", + "latest-keys" : "Neueste Datenkeys", + "latest-key" : "Neuester Datenkey", + "latest-key-functions" : "Funktionen für neuesten Key", + "latest-key-function" : "Neueste Key-Funktion", + "timeseries-keys" : "Zeitreihen-Datenkeys", + "timeseries-key" : "Zeitreihen-Datenkey", + "timeseries-key-functions" : "Funktionen für Zeitreihenkey", + "timeseries-key-function" : "Zeitreihenkey-Funktion", + "maximum-function-types" : "Maximal { count, plural, =1 {1 Funktionstyp erlaubt.} other {# Funktionstypen erlaubt} }", + "time-description" : "Zeitstempel des aktuellen Werts;", + "value-description" : "der aktuelle Wert;", + "prev-value-description" : "Ergebnis des vorherigen Funktionsaufrufs;", + "time-prev-description" : "Zeitstempel des vorherigen Werts;", + "prev-orig-value-description" : "ursprünglicher vorheriger Wert;", + "aggregation" : "Aggregation", + "aggregation-type-hint-common" : "Aus Leistungsgründen ist die Berechnung aggregierter Werte nur für feste Zeitintervalle wie 'heutiger Tag', 'aktueller Monat' usw. verfügbar, nicht jedoch für gleitende Zeitfenster wie 'letzte 30 Minuten'.", + "aggregation-type-none-hint" : "Letzten Wert übernehmen.", + "aggregation-type-min-hint" : "Mindestwert innerhalb des ausgewählten Zeitfensters finden.", + "aggregation-type-max-hint" : "Höchstwert innerhalb des ausgewählten Zeitfensters finden.", + "aggregation-type-avg-hint" : "Durchschnittswert innerhalb des ausgewählten Zeitfensters berechnen.", + "aggregation-type-sum-hint" : "Alle Werte der Datenpunkte im Zeitfenster summieren.", + "aggregation-type-count-hint" : "Gesamtanzahl der Datenpunkte im Zeitfenster.", + "delta-calculation" : "Delta-Berechnung", + "enable-delta-calculation" : "Delta-Berechnung aktivieren", + "enable-delta-calculation-hint" : "Wenn aktiviert, wird der Wert basierend auf aggregierten Werten für ein ausgewähltes Zeitfenster und einen Vergleichszeitraum berechnet. Delta-Berechnung ist nur für historische Zeitfenster verfügbar, nicht für Echtzeitwerte.", + "delta-calculation-result" : "Ergebnis der Delta-Berechnung", + "delta-calculation-result-previous-value" : "Vorheriger Wert", + "delta-calculation-result-delta-absolute" : "Delta (absolut)", + "delta-calculation-result-delta-percent" : "Delta (prozentual)", + "source" : "Quelle", + "latest" : "Neueste", + "latest-value" : "Neuester Wert", + "delta" : "delta", + "percent" : "prozent", + "absolute" : "absolut" + }, + "datasource" : { + "type" : "Datenquellentyp", + "name" : "Name", + "label" : "Bezeichnung", + "add-datasource-prompt" : "Bitte Datenquelle hinzufügen" + }, + "details" : { + "details" : "Details", + "edit-mode" : "Bearbeitungsmodus", + "edit-json" : "JSON bearbeiten", + "toggle-edit-mode" : "Bearbeitungsmodus umschalten" + }, + "device" : { + "device" : "Gerät", + "device-required" : "Gerät ist erforderlich.", + "devices" : "Geräte", + "management" : "Geräteverwaltung", + "view-devices" : "Geräte anzeigen", + "device-alias" : "Gerätealias", + "device-type-max-length" : "Gerätetyp sollte weniger als 256 Zeichen enthalten", + "aliases" : "Gerätealiase", + "no-alias-matching" : "'{{alias}}' nicht gefunden.", + "no-aliases-found" : "Keine Aliase gefunden.", + "no-key-matching" : "'{{key}}' nicht gefunden.", + "no-keys-found" : "Keine Schlüssel gefunden.", + "create-new-alias" : "Neuen erstellen!", + "create-new-key" : "Neuen erstellen!", + "duplicate-alias-error" : "Doppelter Alias gefunden '{{alias}}'.
Gerätealiase müssen innerhalb des Dashboards eindeutig sein.", + "configure-alias" : "Alias '{{alias}}' konfigurieren", + "no-devices-matching" : "Keine Geräte gefunden, die mit '{{entity}}' übereinstimmen.", + "alias" : "Alias", + "alias-required" : "Gerätealias ist erforderlich.", + "remove-alias" : "Gerätealias entfernen", + "add-alias" : "Gerätealias hinzufügen", + "name-starts-with" : "Ausdruck für Gerätename", + "help-text" : "Verwenden Sie '%' bei Bedarf: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list" : "Geräteliste", + "use-device-name-filter" : "Filter verwenden", + "device-list-empty" : "Keine Geräte ausgewählt.", + "device-name-filter-required" : "Filter für Gerätenamen ist erforderlich.", + "device-name-filter-no-device-matched" : "Keine Geräte gefunden, die mit '{{device}}' beginnen.", + "add" : "Gerät hinzufügen", + "assign-to-customer" : "Dem Kunden zuweisen", + "assign-device-to-customer" : "Gerät(e) dem Kunden zuweisen", + "assign-device-to-customer-text" : "Bitte wählen Sie die Geräte aus, die dem Kunden zugewiesen werden sollen", + "make-public" : "Gerät öffentlich machen", + "make-private" : "Gerät privat machen", + "no-devices-text" : "Keine Geräte gefunden", + "assign-to-customer-text" : "Bitte wählen Sie den Kunden aus, dem das/die Gerät(e) zugewiesen werden soll(en)", + "device-details" : "Gerätedetails", + "add-device-text" : "Neues Gerät hinzufügen", + "credentials" : "Zugangsdaten", + "manage-credentials" : "Zugangsdaten verwalten", + "delete" : "Gerät löschen", + "assign-devices" : "Geräte zuweisen", + "assign-devices-text" : "{ count, plural, =1 {1 Gerät} other {# Geräte} } dem Kunden zuweisen", + "delete-devices" : "Geräte löschen", + "unassign-from-customer" : "Vom Kunden entfernen", + "unassign-devices" : "Geräte entfernen", + "unassign-devices-action-title" : "{ count, plural, =1 {1 Gerät} other {# Geräte} } vom Kunden entfernen", + "unassign-device-from-edge-title" : "Möchten Sie das Gerät '{{deviceName}}' wirklich vom Edge entfernen?", + "unassign-device-from-edge-text" : "Nach der Bestätigung wird das Gerät entfernt und ist für den Edge nicht mehr zugänglich.", + "unassign-devices-from-edge" : "Geräte vom Edge entfernen", + "assign-new-device" : "Neues Gerät zuweisen", + "make-public-device-title" : "Möchten Sie das Gerät '{{deviceName}}' wirklich öffentlich machen?", + "make-public-device-text" : "Nach der Bestätigung wird das Gerät und alle zugehörigen Daten öffentlich und für andere zugänglich.", + "make-private-device-title" : "Möchten Sie das Gerät '{{deviceName}}' wirklich privat machen?", + "make-private-device-text" : "Nach der Bestätigung wird das Gerät und alle zugehörigen Daten privat und für andere nicht zugänglich.", + "view-credentials" : "Zugangsdaten anzeigen", + "delete-device-title" : "Möchten Sie das Gerät '{{deviceName}}' wirklich löschen?", + "delete-device-text" : "Achtung, nach der Bestätigung wird das Gerät und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-devices-title" : "Möchten Sie { count, plural, =1 {1 Gerät} other {# Geräte} } wirklich löschen?", + "delete-devices-action-title" : "{ count, plural, =1 {1 Gerät löschen} other {# Geräte löschen} }", + "delete-devices-text" : "Achtung, nach der Bestätigung werden alle ausgewählten Geräte und alle zugehörigen Daten unwiederbringlich gelöscht.", + "unassign-device-title" : "Möchten Sie das Gerät '{{deviceName}}' wirklich entfernen?", + "unassign-device-text" : "Nach der Bestätigung wird das Gerät entfernt und ist für den Kunden nicht mehr zugänglich.", + "unassign-device" : "Gerät entfernen", + "unassign-devices-title" : "Möchten Sie { count, plural, =1 {1 Gerät} other {# Geräte} } wirklich entfernen?", + "unassign-devices-text" : "Nach der Bestätigung werden alle ausgewählten Geräte entfernt und sind für den Kunden nicht mehr zugänglich.", + "device-credentials" : "Geräte-Zugangsdaten", + "loading-device-credentials" : "Geräte-Zugangsdaten werden geladen...", + "credentials-type" : "Zugangsdaten-Typ", + "access-token" : "Zugangstoken", + "access-token-required" : "Zugangstoken ist erforderlich.", + "access-token-invalid" : "Die Länge des Zugangstokens muss zwischen 1 und 32 Zeichen liegen.", + "certificate-pem-format" : "Zertifikat im PEM-Format", + "certificate-pem-format-required" : "Zertifikat ist erforderlich.", + "copy-access-token" : "Zugangstoken kopieren", + "copy-certificate" : "Zertifikat kopieren", + "copy-client-id" : "Client-ID kopieren", + "copy-user-name" : "Benutzernamen kopieren", + "copy-password" : "Passwort kopieren", + "generate-client-id" : "Client-ID generieren", + "generate-user-name" : "Benutzernamen generieren", + "generate-password" : "Passwort generieren", + "generate-access-token" : "Zugangstoken generieren", + "lwm2m-security-config" : { + "identity" : "Client-Identität", + "identity-required" : "Client-Identität ist erforderlich.", + "identity-tooltip" : "Die PSK-Kennung ist eine beliebige PSK-Kennung von bis zu 128 Byte, wie im Standard [RFC7925] beschrieben.\nDie PSK-Kennung MUSS zuerst in eine Zeichenkette umgewandelt und dann mittels UTF-8 in Oktetten codiert werden.", + "client-key" : "Client-Schlüssel", + "client-key-required" : "Client-Schlüssel ist erforderlich.", + "client-key-tooltip-prk" : "RPK öffentlicher Schlüssel oder ID muss dem Standard [RFC7250] entsprechen und im Base64-Format codiert sein!", + "client-key-tooltip-psk" : "PSK-Schlüssel muss dem Standard [RFC4279] entsprechen und im HexDec-Format sein: 32, 64, 128 Zeichen!", + "endpoint" : "Endpoint-Clientname", + "endpoint-required" : "Endpoint-Clientname ist erforderlich.", + "client-public-key" : "Öffentlicher Client-Schlüssel", + "client-public-key-hint" : "Wenn kein öffentlicher Client-Schlüssel angegeben ist, wird das vertrauenswürdige Zertifikat verwendet", + "client-public-key-tooltip" : "X509-Schlüssel muss im DER-codierten X509v3-Format vorliegen, ausschließlich den EC-Algorithmus unterstützen und im Base64-Format codiert sein!", + "mode" : "Sicherheitskonfigurationsmodus", + "client-tab" : "Client-Sicherheitskonfiguration", + "client-certificate" : "Client-Zertifikat", + "bootstrap-tab" : "Bootstrap-Client", + "bootstrap-server" : "Bootstrap-Server", + "lwm2m-server" : "LwM2M-Server", + "client-publicKey-or-id" : "Öffentlicher Client-Schlüssel oder ID", + "client-publicKey-or-id-required" : "Öffentlicher Client-Schlüssel oder ID ist erforderlich.", + "client-publicKey-or-id-tooltip-psk" : "Die PSK-Kennung ist eine beliebige PSK-Kennung bis zu 128 Byte, wie im Standard [RFC7925] beschrieben.\nDie PSK-Kennung MUSS zuerst in eine Zeichenkette umgewandelt und dann mittels UTF-8 in Oktetten codiert werden.", + "client-publicKey-or-id-tooltip-rpk" : "RPK öffentlicher Schlüssel oder ID muss dem Standard [RFC7250] entsprechen und im Base64-Format codiert sein!", + "client-publicKey-or-id-tooltip-x509" : "X509-Schlüssel muss im DER-codierten X509v3-Format vorliegen, ausschließlich den EC-Algorithmus unterstützen und im Base64-Format codiert sein", + "client-secret-key" : "Geheimer Client-Schlüssel", + "client-secret-key-required" : "Geheimer Client-Schlüssel ist erforderlich.", + "client-secret-key-tooltip-psk" : "PSK-Schlüssel muss dem Standard [RFC4279] entsprechen und im HexDec-Format sein: 32, 64, 128 Zeichen!", + "client-secret-key-tooltip-prk" : "RPK-Schlüssel muss im PKCS_8-Format (DER-Codierung, Standard [RFC5958]) vorliegen und im Base64-Format codiert sein!", + "client-secret-key-tooltip-x509" : "X509-Schlüssel muss im PKCS_8-Format (DER-Codierung, Standard [RFC5958]) vorliegen und im Base64-Format codiert sein!" + }, + "client-id" : "Client-ID", + "client-id-pattern" : "Enthält ungültige Zeichen.", + "user-name" : "Benutzername", + "user-name-required" : "Benutzername ist erforderlich.", + "client-id-or-user-name-necessary" : "Client-ID und/oder Benutzername sind erforderlich", + "password" : "Passwort", + "secret" : "Geheimnis", + "secret-required" : "Geheimnis ist erforderlich.", + "device-type" : "Geräteprofil", + "device-type-required" : "Gerätetyp ist erforderlich.", + "select-device-type" : "Gerätetyp auswählen", + "enter-device-type" : "Geräteprofil eingeben", + "any-device" : "Beliebiges Gerät", + "no-device-types-matching" : "Keine Geräteprofile gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "device-type-list-empty" : "Keine Geräteprofile ausgewählt!", + "device-profile-type-list-empty" : "Mindestens ein Geräteprofil muss ausgewählt werden.", + "device-types" : "Gerätetypen", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "name-max-length" : "Name sollte weniger als 256 Zeichen enthalten", + "label-max-length" : "Bezeichnung sollte weniger als 256 Zeichen enthalten", + "description" : "Beschreibung", + "label" : "Bezeichnung", + "events" : "Ereignisse", + "details" : "Details", + "copyId" : "Geräte-ID kopieren", + "copyAccessToken" : "Zugangstoken kopieren", + "copy-mqtt-authentication" : "MQTT-Zugangsdaten kopieren", + "idCopiedMessage" : "Geräte-ID wurde in die Zwischenablage kopiert", + "accessTokenCopiedMessage" : "Geräte-Zugangstoken wurde in die Zwischenablage kopiert", + "mqtt-authentication-copied-message" : "Geräte-MQTT-Zugang wurde in die Zwischenablage kopiert", + "assignedToCustomer" : "Dem Kunden zugewiesen", + "unable-delete-device-alias-title" : "Gerätealias kann nicht gelöscht werden", + "unable-delete-device-alias-text" : "Gerätealias '{{deviceAlias}}' kann nicht gelöscht werden, da er in folgenden Widgets verwendet wird:
{{widgetsList}}", + "is-gateway" : "Ist Gateway", + "overwrite-activity-time" : "Aktivitätszeit für verbundenes Gerät überschreiben", + "device-filter" : "Gerätefilter", + "device-filter-title" : "Gerätefilter", + "filter-title" : "Filter", + "device-state" : "Gerätestatus", + "state" : "Status", + "any" : "Beliebig", + "active" : "Aktiv", + "inactive" : "Inaktiv", + "public" : "Öffentlich", + "device-public" : "Gerät ist öffentlich", + "select-device" : "Gerät auswählen", + "import" : "Gerät importieren", + "device-file" : "Gerätedatei", + "search" : "Geräte suchen", + "selected-devices" : "{ count, plural, =1 {1 Gerät} other {# Geräte} } ausgewählt", + "device-configuration" : "Gerätekonfiguration", + "transport-configuration" : "Transportkonfiguration", + "wizard" : { + "device-details" : "Gerätedetails" + }, + "unassign-devices-from-edge-title" : "Möchten Sie { count, plural, =1 {1 Gerät} other {# Geräte} } wirklich vom Edge entfernen?", + "unassign-devices-from-edge-text" : "Nach der Bestätigung werden alle ausgewählten Geräte entfernt und sind für den Edge nicht mehr zugänglich.", + "time" : "Zeit", + "connectivity" : { + "check-connectivity" : "Konnektivität prüfen", + "device-created-check-connectivity" : "Gerät erstellt. Lassen Sie uns die Konnektivität prüfen!", + "loading-check-connectivity-command" : "Befehle zur Konnektivitätsprüfung werden geladen...", + "use-following-instructions" : "Verwenden Sie die folgenden Anweisungen, um Telemetrie im Namen des Geräts über die Shell zu senden", + "execute-following-command" : "Führen Sie den folgenden Befehl aus", + "install-curl-windows" : "Ab Windows 10 Build 17063 ist cURL standardmäßig verfügbar", + "install-curl-macos" : "Ab Mac OS X 10.2 6C115 (Jaguar) ist cURL standardmäßig verfügbar", + "install-mqtt-windows" : "Verwenden Sie die Anweisungen, um mosquitto_pub herunterzuladen, zu installieren, einzurichten und auszuführen", + "install-coap-client" : "Verwenden Sie die Anweisungen, um coap-client herunterzuladen, zu installieren, einzurichten und auszuführen", + "install-necessary-client-tools" : "Notwendige Client-Tools installieren", + "mqtts-x509-command" : "Verwenden Sie die folgende Dokumentation, um das Gerät über MQTT mit X509-Authentifizierung zu verbinden", + "coaps-x509-command" : "Verwenden Sie die folgende Dokumentation, um das Gerät über CoAP über DTLS mit X509-Authentifizierung zu verbinden", + "snmp-command" : "Verwenden Sie die folgende Dokumentation, um das Gerät über SNMP zu verbinden", + "sparkplug-command" : "Verwenden Sie die folgende Dokumentation, um das Gerät über MQTT Sparkplug zu verbinden", + "lwm2m-command" : "Verwenden Sie die folgende Dokumentation, um das Gerät über LWM2M zu verbinden" + } + }, + "dynamic-form" : { + "property" : { + "properties" : "Eigenschaften", + "property" : "Eigenschaft", + "id" : "ID", + "name" : "Name", + "type" : "Typ", + "type-text" : "Text", + "type-password" : "Passwort", + "type-textarea" : "Textbereich", + "type-number" : "Nummer", + "type-switch" : "Schalter", + "type-select" : "Auswahl", + "type-radios" : "Optionsfelder", + "type-datetime" : "Datum/Zeit", + "type-image" : "Bild", + "type-javascript" : "JavaScript", + "type-json" : "JSON", + "type-html" : "HTML", + "type-css" : "CSS", + "type-markdown" : "Markdown", + "type-color" : "Farbe", + "type-color-settings" : "Farbeinstellungen", + "type-font" : "Schriftart", + "type-units" : "Einheiten", + "type-icon" : "Symbol", + "type-fieldset" : "Feldgruppe", + "type-array" : "Array", + "type-html-section" : "HTML-Abschnitt", + "group-title" : "Gruppentitel", + "no-properties" : "Keine Eigenschaften konfiguriert", + "add-property" : "Eigenschaft hinzufügen", + "property-settings" : "Eigenschaftseinstellungen", + "remove-property" : "Eigenschaft entfernen", + "default-value" : "Standardwert", + "value-required" : "Wert erforderlich", + "number-settings" : "Zahleneinstellungen", + "min" : "Min", + "max" : "Max", + "step" : "Schritt", + "selected-options-limit" : "Auswahloptionen Limit", + "advanced-ui-settings" : "Erweiterte UI-Einstellungen", + "disable-on-property" : "Bei Eigenschaft deaktivieren", + "display-condition-function" : "Anzeigebedingungsfunktion", + "sub-label" : "Unterbezeichnung", + "vertical-divider-after" : "Vertikale Trennlinie danach", + "input-field-suffix" : "Suffix des Eingabefeldes", + "property-row-classes" : "Eigenschaftszeilenklassen", + "property-field-classes" : "Eigenschaftsfeldklassen", + "not-unique-property-ids-error" : "Eigenschafts-IDs müssen eindeutig sein!", + "enable-multiple-select" : "Mehrfachauswahl aktivieren", + "allow-empty-select-option" : "Leere Option erlauben", + "select-options" : "Auswahloptionen", + "not-unique-select-option-value-error" : "Werte der Auswahloptionen müssen eindeutig sein!", + "value" : "Wert", + "label" : "Bezeichnung", + "add-option" : "Option hinzufügen", + "no-options" : "Keine Optionen konfiguriert", + "remove-option" : "Option entfernen", + "textarea-rows" : "Textbereich Zeilen", + "help-id" : "Hilfe-ID", + "buttons-direction" : "Richtung der Schaltflächen", + "direction-row" : "Reihe", + "direction-column" : "Spalte", + "radio-button-options" : "Optionsfelder-Optionen", + "datetime-type" : "Datum/Zeit Feldtyp", + "datetime-type-date" : "Datum", + "datetime-type-time" : "Zeit", + "datetime-type-datetime" : "Datum/Zeit", + "enable-clear-button" : "Löschen-Schaltfläche aktivieren", + "html-section-settings" : "HTML-Abschnittseinstellungen", + "html-section-classes" : "HTML-Abschnitt Klassen", + "html-section-content" : "Inhalt des HTML-Abschnitts", + "array-item" : "Array-Element", + "item-type" : "Elementtyp", + "item-name" : "Elementname", + "no-items" : "Keine Elemente" + }, + "clear-form" : "Formular löschen", + "clear-form-prompt" : "Möchten Sie wirklich alle Formulareigenschaften entfernen?", + "import-form" : "Formular aus JSON importieren", + "export-form" : "Formular in JSON exportieren", + "json-file" : "JSON-Datei", + "json-content" : "JSON-Inhalt", + "invalid-form-json-file-error" : "Formularimport aus JSON fehlgeschlagen: Ungültige JSON-Datenstruktur." + }, + "asset-profile" : { + "asset-profile" : "Asset-Profil", + "asset-profiles" : "Asset-Profile", + "all-asset-profiles" : "Alle", + "add" : "Asset-Profil hinzufügen", + "edit" : "Asset-Profil bearbeiten", + "asset-profile-details" : "Asset-Profil-Details", + "no-asset-profiles-text" : "Keine Asset-Profile gefunden", + "search" : "Asset-Profile suchen", + "selected-asset-profiles" : "{ count, plural, =1 {1 Asset-Profil} other {# Asset-Profile} } ausgewählt", + "no-asset-profiles-matching" : "Kein Asset-Profil passend zu '{{entity}}' gefunden.", + "asset-profile-required" : "Asset-Profil ist erforderlich", + "idCopiedMessage" : "Asset-Profil-ID wurde in die Zwischenablage kopiert", + "set-default" : "Asset-Profil als Standard festlegen", + "delete" : "Asset-Profil löschen", + "copyId" : "Asset-Profil-ID kopieren", + "name-max-length" : "Name sollte weniger als 256 Zeichen enthalten", + "new-device-profile-name" : "Name des Asset-Profils", + "new-device-profile-name-required" : "Name des Asset-Profils ist erforderlich.", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "image" : "Asset-Profil-Bild", + "description" : "Beschreibung", + "default" : "Standard", + "default-rule-chain" : "Standard-Regelkette", + "default-edge-rule-chain" : "Standard Edge-Regelkette", + "default-edge-rule-chain-hint" : "Wird auf Edge als Regelkette für eingehende Asset-Daten verwendet", + "mobile-dashboard" : "Mobiles Dashboard", + "mobile-dashboard-hint" : "Wird von der mobilen Anwendung als Asset-Details-Dashboard verwendet", + "select-queue-hint" : "Aus der Dropdown-Liste auswählen.", + "delete-asset-profile-title" : "Möchten Sie das Asset-Profil '{{assetProfileName}}' wirklich löschen?", + "delete-asset-profile-text" : "Achtung, nach der Bestätigung wird das Asset-Profil und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-asset-profiles-title" : "Möchten Sie { count, plural, =1 {1 Asset-Profil} other {# Asset-Profile} } wirklich löschen?", + "delete-asset-profiles-text" : "Achtung, nach der Bestätigung werden alle ausgewählten Asset-Profile und alle zugehörigen Daten unwiederbringlich gelöscht.", + "set-default-asset-profile-title" : "Möchten Sie das Asset-Profil '{{assetProfileName}}' als Standard festlegen?", + "set-default-asset-profile-text" : "Nach der Bestätigung wird das Asset-Profil als Standard festgelegt und für neue Assets ohne spezifiziertes Profil verwendet.", + "no-asset-profiles-found" : "Keine Asset-Profile gefunden.", + "create-new-asset-profile" : "Neues erstellen!", + "create-asset-profile" : "Neues Asset-Profil erstellen", + "import" : "Asset-Profil importieren", + "export" : "Asset-Profil exportieren", + "export-failed-error" : "Asset-Profil konnte nicht exportiert werden: {{error}}", + "asset-profile-file" : "Asset-Profil-Datei", + "invalid-asset-profile-file-error" : "Asset-Profil konnte nicht importiert werden: Ungültige Asset-Profil-Datenstruktur." + }, + "device-profile" : { + "device-profile" : "Geräteprofil", + "device-profiles" : "Geräteprofile", + "all-device-profiles" : "Alle", + "add" : "Geräteprofil hinzufügen", + "edit" : "Geräteprofil bearbeiten", + "device-profile-details" : "Geräteprofil-Details", + "no-device-profiles-text" : "Keine Geräteprofile gefunden", + "search" : "Geräteprofile suchen", + "selected-device-profiles" : "{ count, plural, =1 {1 Geräteprofil} other {# Geräteprofile} } ausgewählt", + "no-device-profiles-matching" : "Kein Geräteprofil passend zu '{{entity}}' gefunden.", + "device-profile-required" : "Geräteprofil ist erforderlich", + "idCopiedMessage" : "Geräteprofil-ID wurde in die Zwischenablage kopiert", + "set-default" : "Geräteprofil als Standard festlegen", + "delete" : "Geräteprofil löschen", + "copyId" : "Geräteprofil-ID kopieren", + "name-max-length" : "Name sollte weniger als 256 Zeichen enthalten", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "type" : "Profiltyp", + "type-required" : "Profiltyp ist erforderlich.", + "type-default" : "Standard", + "image" : "Geräteprofil-Bild", + "transport-type" : "Transporttyp", + "transport-type-required" : "Transporttyp ist erforderlich.", + "transport-type-default" : "Standard", + "transport-type-default-hint" : "Unterstützt grundlegendes MQTT, HTTP und CoAP Transport", + "transport-type-mqtt" : "MQTT", + "transport-type-mqtt-hint" : "Ermöglicht erweiterte MQTT-Transporteinstellungen", + "transport-type-coap" : "CoAP", + "transport-type-coap-hint" : "Ermöglicht erweiterte CoAP-Transporteinstellungen", + "transport-type-lwm2m" : "LWM2M", + "transport-type-lwm2m-hint" : "LWM2M-Transporttyp", + "transport-type-snmp" : "SNMP", + "transport-type-snmp-hint" : "SNMP-Transportkonfiguration angeben", + "transport-type-http" : "HTTP", + "description" : "Beschreibung", + "default" : "Standard", + "profile-configuration" : "Profilkonfiguration", + "transport-configuration" : "Transportkonfiguration", + "default-rule-chain" : "Standard-Regelkette", + "default-edge-rule-chain" : "Standard Edge-Regelkette", + "default-edge-rule-chain-hint" : "Wird auf Edge als Regelkette verwendet, um eingehende Gerätedaten dieses Geräteprofils zu verarbeiten", + "mobile-dashboard" : "Mobiles Dashboard", + "mobile-dashboard-hint" : "Wird von der mobilen Anwendung als Geräte-Details-Dashboard verwendet", + "select-queue-hint" : "Aus der Dropdown-Liste auswählen.", + "delete-device-profile-title" : "Möchten Sie das Geräteprofil '{{deviceProfileName}}' wirklich löschen?", + "delete-device-profile-text" : "Achtung, nach der Bestätigung wird das Geräteprofil und alle zugehörigen Daten einschließlich der OTA-Updates unwiederbringlich gelöscht.", + "delete-device-profiles-title" : "Möchten Sie { count, plural, =1 {1 Geräteprofil} other {# Geräteprofile} } wirklich löschen?", + "delete-device-profiles-text" : "Achtung, nach der Bestätigung werden alle ausgewählten Geräteprofile und alle zugehörigen Daten einschließlich der OTA-Updates unwiederbringlich gelöscht.", + "set-default-device-profile-title" : "Möchten Sie das Geräteprofil '{{deviceProfileName}}' als Standard festlegen?", + "set-default-device-profile-text" : "Nach der Bestätigung wird das Geräteprofil als Standard markiert und für neue Geräte ohne spezifiziertes Profil verwendet.", + "no-device-profiles-found" : "Keine Geräteprofile gefunden.", + "create-new-device-profile" : "Neues erstellen!", + "mqtt-device-topic-filters" : "MQTT-Gerät-Topic-Filter", + "mqtt-device-topic-filters-unique" : "MQTT-Gerät-Topic-Filter müssen eindeutig sein.", + "mqtt-device-topic-filters-spark-plug" : "MQTT Sparkplug B Edge-of-Network (EoN) Node.", + "mqtt-device-topic-filters-spark-plug-hint" : "Erlaubt Verbindungen von EoN-Nodes mit Sparkplug B Nutzlast und Topic-Format.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names" : "SparkPlug-Metriken zur Speicherung als Attribute.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint" : "Namen der SparkPlug-Metriken, die als Geräteattribute gespeichert werden. Alle anderen Metriken werden als Telemetrie gespeichert.", + "mqtt-device-payload-type" : "MQTT-Geräte-Nutzlast", + "mqtt-device-payload-type-json" : "JSON", + "mqtt-device-payload-type-proto" : "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format" : "Kompatibilität mit anderen Nutzlast-Formaten aktivieren.", + "mqtt-enable-compatibility-with-json-payload-format-hint" : "Wenn aktiviert, wird standardmäßig das Protobuf-Format verwendet. Bei Parsing-Fehlern wird JSON genutzt. Nützlich für die Abwärtskompatibilität bei Firmware-Updates. Dieser Modus kann die Leistung leicht beeinträchtigen und sollte nach Abschluss der Updates deaktiviert werden.", + "mqtt-use-json-format-for-default-downlink-topics" : "JSON-Format für Standard-Downlink-Themen verwenden", + "mqtt-use-json-format-for-default-downlink-topics-hint" : "Wenn aktiviert, wird das JSON-Format für die Standard-Downlink-Themen verwendet, z.B.: v1/devices/me/attributes/response/$request_id. Neue v2-Themen sind davon nicht betroffen.", + "mqtt-send-ack-on-validation-exception" : "PUBACK bei Validierungsfehler senden", + "mqtt-send-ack-on-validation-exception-hint" : "Standardmäßig wird die Sitzung bei einem Validierungsfehler geschlossen. Wenn aktiviert, wird stattdessen eine Bestätigung gesendet.", + "snmp-add-mapping" : "SNMP-Zuordnung hinzufügen", + "snmp-mapping-not-configured" : "Keine Zuordnung für OID zu Zeitreihen/Telemetrie konfiguriert", + "snmp-timseries-or-attribute-name" : "Zeitreihe/Attributname für Zuordnung", + "snmp-timseries-or-attribute-type" : "Zeitreihe/Attributtyp für Zuordnung", + "snmp-method-pdu-type-get-request" : "GetRequest", + "snmp-method-pdu-type-get-next-request" : "GetNextRequest", + "snmp-oid" : "OID", + "transport-device-payload-type-json" : "JSON", + "transport-device-payload-type-proto" : "Protobuf", + "mqtt-payload-type-required" : "Nutzlast-Typ ist erforderlich.", + "coap-device-type" : "CoAP-Gerätetyp", + "coap-device-payload-type" : "CoAP-Geräte-Nutzlast", + "coap-device-type-required" : "CoAP-Gerätetyp ist erforderlich.", + "coap-device-type-default" : "Standard", + "coap-device-type-efento" : "Efento NB-IoT", + "support-level-wildcards" : "Einfache [+] und mehrstufige [#] Wildcards werden unterstützt.", + "telemetry-topic-filter" : "Telemetrie-Topic-Filter", + "telemetry-topic-filter-required" : "Telemetrie-Topic-Filter ist erforderlich.", + "attributes-topic-filter" : "Attribut-Veröffentlichungs-Topic-Filter", + "attributes-subscribe-topic-filter" : "Attribut-Abonnement-Topic-Filter", + "attributes-topic-filter-required" : "Attribut-Veröffentlichungs-Topic-Filter ist erforderlich.", + "attributes-subscribe-topic-filter-required" : "Attribut-Abonnement-Topic ist erforderlich", + "telemetry-proto-schema" : "Telemetrie-Protoschema", + "telemetry-proto-schema-required" : "Telemetrie-Protoschema ist erforderlich.", + "attributes-proto-schema" : "Attribut-Protoschema", + "attributes-proto-schema-required" : "Attribut-Protoschema ist erforderlich.", + "rpc-response-proto-schema" : "RPC-Antwort-Protoschema", + "rpc-response-proto-schema-required" : "RPC-Antwort-Protoschema ist erforderlich.", + "rpc-response-topic-filter" : "RPC-Antwort-Topic-Filter", + "rpc-response-topic-filter-required" : "RPC-Antwort-Topic-Filter ist erforderlich.", + "rpc-request-proto-schema" : "RPC-Anfrage-Protoschema", + "rpc-request-proto-schema-required" : "RPC-Anfrage-Protoschema ist erforderlich.", + "rpc-request-proto-schema-hint" : "RPC-Anforderungsnachrichten müssen die Felder 'string method = 1;', 'int32 requestId = 2;' und 'params = 3' enthalten.", + "not-valid-pattern-topic-filter" : "Ungültiges Muster im Topic-Filter", + "not-valid-single-character" : "Ungültige Verwendung eines Single-Level-Wildcards", + "not-valid-multi-character" : "Ungültige Verwendung eines Multi-Level-Wildcards", + "single-level-wildcards-hint" : "[+] kann jedes Topic-Level ersetzen. Beispiel: v1/devices/+/telemetry.", + "multi-level-wildcards-hint" : "[#] ersetzt mehrere Ebenen und muss am Ende des Topics stehen. Beispiel: # oder v1/devices/me/#.", + "alarm-rules" : "Alarmregeln", + "alarm-rules-with-count" : "Alarmregeln ({{count}})", + "no-alarm-rules" : "Keine Alarmregeln konfiguriert", + "add-alarm-rule" : "Alarmregel hinzufügen", + "edit-alarm-rule" : "Alarmregel bearbeiten", + "alarm-type" : "Alarmtyp", + "alarm-type-required" : "Alarmtyp ist erforderlich.", + "alarm-type-unique" : "Alarmtyp muss innerhalb der Alarmregeln des Geräteprofils eindeutig sein.", + "alarm-type-max-length" : "Alarmtyp sollte weniger als 256 Zeichen enthalten", + "create-alarm-pattern" : "{{alarmType}} Alarm erstellen", + "create-alarm-rules" : "Alarmregeln erstellen", + "no-create-alarm-rules" : "Keine Erstellungsbedingungen konfiguriert", + "add-create-alarm-rule-prompt" : "Bitte Erstellungsbedingung für Alarm hinzufügen", + "clear-alarm-rule" : "Alarmregel löschen", + "no-clear-alarm-rule" : "Keine Löschbedingung konfiguriert", + "add-create-alarm-rule" : "Erstellungsbedingung hinzufügen", + "add-clear-alarm-rule" : "Löschbedingung hinzufügen", + "select-alarm-severity" : "Alarmstufe auswählen", + "alarm-severity-required" : "Alarmstufe ist erforderlich.", + "condition-duration" : "Bedingungsdauer", + "condition-duration-value" : "Dauerwert", + "condition-duration-time-unit" : "Zeiteinheit", + "condition-duration-value-range" : "Dauerwert muss zwischen 1 und 2147483647 liegen.", + "condition-duration-value-pattern" : "Dauerwert muss eine ganze Zahl sein.", + "condition-duration-value-required" : "Dauerwert ist erforderlich.", + "condition-duration-time-unit-required" : "Zeiteinheit ist erforderlich.", + "advanced-settings" : "Erweiterte Einstellungen", + "alarm-rule-additional-info" : "Zusätzliche Informationen", + "edit-alarm-rule-additional-info" : "Zusätzliche Informationen bearbeiten", + "alarm-rule-additional-info-placeholder" : "Bitte Kommentare und Anpassungen angeben, die unter 'Zusätzliche Informationen' bei Alarmdetails angezeigt werden.", + "alarm-rule-additional-info-hint" : "Hinweis: Verwenden Sie ${keyName} zur Ersetzung von Attribut- oder Telemetriedaten.", + "alarm-rule-mobile-dashboard" : "Mobiles Dashboard", + "alarm-rule-mobile-dashboard-hint" : "Wird von der mobilen App als Alarminformationen-Dashboard genutzt", + "alarm-rule-no-mobile-dashboard" : "Kein Dashboard ausgewählt", + "propagate-alarm" : "Alarm auf verbundene Entitäten propagieren", + "alarm-rule-relation-types-list" : "Beziehungstypen", + "alarm-rule-relation-types-list-hint" : "Definiert Beziehungstypen zur Filterung der verbundenen Entitäten. Wenn nicht gesetzt, wird der Alarm an alle verbundenen Entitäten propagiert.", + "propagate-alarm-to-owner" : "Alarm an Eigentümer der Entität (Kunde oder Mandant) propagieren", + "propagate-alarm-to-tenant" : "Alarm an Mandant propagieren", + "alarm-rule-condition" : "Alarmregelbedingung", + "enter-alarm-rule-condition-prompt" : "Bitte fügen Sie eine Alarmregelbedingung hinzu", + "edit-alarm-rule-condition" : "Alarmregelbedingung bearbeiten", + "device-provisioning" : "Gerätebereitstellung", + "provision-strategy" : "Bereitstellungsstrategie", + "provision-strategy-required" : "Bereitstellungsstrategie ist erforderlich.", + "provision-strategy-disabled" : "Deaktiviert", + "provision-strategy-created-new" : "Erstellen neuer Geräte zulassen", + "provision-strategy-check-pre-provisioned" : "Nach vorab bereitgestellten Geräten suchen", + "provision-device-key" : "Gerätebereitstellungsschlüssel", + "provision-device-key-required" : "Gerätebereitstellungsschlüssel ist erforderlich.", + "copy-provision-key" : "Bereitstellungsschlüssel kopieren", + "provision-key-copied-message" : "Bereitstellungsschlüssel wurde in die Zwischenablage kopiert", + "provision-device-secret" : "Gerätebereitstellungsgeheimnis", + "provision-device-secret-required" : "Gerätebereitstellungsgeheimnis ist erforderlich.", + "copy-provision-secret" : "Bereitstellungsgeheimnis kopieren", + "provision-secret-copied-message" : "Bereitstellungsgeheimnis wurde in die Zwischenablage kopiert", + "provision-strategy-x509" : { + "certificate-chain" : "X509-Zertifikatskette", + "certificate-chain-hint" : "Die X.509-Zertifikatsstrategie wird verwendet, um Geräte anhand von Client-Zertifikaten in einer Zweiwege-TLS-Kommunikation bereitzustellen.", + "allow-create-new-devices" : "Neue Geräte erstellen", + "allow-create-new-devices-hint" : "Wenn ausgewählt, werden neue Geräte erstellt und das Client-Zertifikat als Geräteanmeldeinformationen verwendet.", + "certificate-value" : "Zertifikat im PEM-Format", + "certificate-value-required" : "Zertifikat im PEM-Format ist erforderlich", + "cn-regex-variable" : "CN regulärer Ausdruck-Variable", + "cn-regex-variable-required" : "CN regulärer Ausdruck-Variable ist erforderlich", + "cn-regex-variable-hint" : "Erforderlich, um den Gerätenamen aus dem Common Name des X509-Zertifikats des Geräts abzurufen." + }, + "condition" : "Bedingung", + "condition-type" : "Bedingungstyp", + "condition-type-simple" : "Einfach", + "condition-type-duration" : "Dauer", + "condition-during" : "Während {{during}}", + "condition-during-dynamic" : "Während \"{{attribute}}\" ({{during}})", + "condition-type-repeating" : "Wiederholend", + "condition-type-required" : "Bedingungstyp ist erforderlich.", + "condition-repeating-value" : "Anzahl der Ereignisse", + "condition-repeating-value-range" : "Anzahl der Ereignisse muss zwischen 1 und 2147483647 liegen.", + "condition-repeating-value-pattern" : "Anzahl der Ereignisse muss eine ganze Zahl sein.", + "condition-repeating-value-required" : "Anzahl der Ereignisse ist erforderlich.", + "condition-repeat-times" : "Wiederholt sich { count, plural, =1 {1 Mal} other {# Mal} }", + "condition-repeat-times-dynamic" : "Wiederholt sich \"{attribute}\" ({ count, plural, =1 {1 Mal} other {# Mal} })", + "schedule-type" : "Zeitplantyp", + "schedule-type-required" : "Zeitplantyp ist erforderlich.", + "schedule" : "Zeitplan", + "edit-schedule" : "Alarmzeitplan bearbeiten", + "schedule-any-time" : "Immer aktiv", + "schedule-specific-time" : "Aktiv zu einer bestimmten Zeit", + "schedule-custom" : "Benutzerdefiniert", + "schedule-day" : { + "monday" : "Montag", + "tuesday" : "Dienstag", + "wednesday" : "Mittwoch", + "thursday" : "Donnerstag", + "friday" : "Freitag", + "saturday" : "Samstag", + "sunday" : "Sonntag" + }, + "schedule-days" : "Tage", + "schedule-time" : "Zeit", + "schedule-time-from" : "Von", + "schedule-time-to" : "Bis", + "schedule-days-of-week-required" : "Mindestens ein Wochentag muss ausgewählt sein.", + "create-device-profile" : "Neues Geräteprofil erstellen", + "import" : "Geräteprofil importieren", + "export" : "Geräteprofil exportieren", + "export-failed-error" : "Geräteprofil konnte nicht exportiert werden: {{error}}", + "device-profile-file" : "Geräteprofil-Datei", + "invalid-device-profile-file-error" : "Geräteprofil konnte nicht importiert werden: Ungültige Geräteprofil-Datenstruktur.", + "power-saving-mode" : "Energiesparmodus", + "power-saving-mode-type" : { + "default" : "Energiesparmodus des Geräteprofils verwenden", + "psm" : "Power Saving Mode (PSM)", + "drx" : "Discontinuous Reception (DRX)", + "edrx" : "Extended Discontinuous Reception (eDRX)" + }, + "edrx-cycle" : "eDRX-Zyklus", + "edrx-cycle-required" : "eDRX-Zyklus ist erforderlich.", + "edrx-cycle-pattern" : "eDRX-Zyklus muss eine positive Ganzzahl sein.", + "edrx-cycle-min" : "Die Mindestanzahl von eDRX-Zyklen beträgt {{ min }} Sekunden.", + "paging-transmission-window" : "Übertragungsfenster für Paging", + "paging-transmission-window-required" : "Übertragungsfenster für Paging ist erforderlich.", + "paging-transmission-window-pattern" : "Übertragungsfenster für Paging muss eine positive Ganzzahl sein.", + "paging-transmission-window-min" : "Die Mindestanzahl des Übertragungsfensters für Paging beträgt {{ min }} Sekunden.", + "psm-activity-timer" : "PSM-Aktivitätstimer", + "psm-activity-timer-required" : "PSM-Aktivitätstimer ist erforderlich.", + "psm-activity-timer-pattern" : "PSM-Aktivitätstimer muss eine positive Ganzzahl sein.", + "psm-activity-timer-min" : "Die Mindestanzahl des PSM-Aktivitätstimers beträgt {{ min }} Sekunden.", + "lwm2m" : { + "object-list" : "Objektliste", + "object-list-empty" : "Keine Objekte ausgewählt.", + "no-objects-found" : "Keine Objekte gefunden.", + "no-objects-matching" : "Keine Objekte gefunden, die mit '{{object}}' übereinstimmen.", + "model-tab" : "LWM2M-Modell", + "add-new-instances" : "Neue Instanzen hinzufügen", + "instances-list" : "Instanzenliste", + "instances-list-required" : "Instanzenliste ist erforderlich.", + "instance-id-pattern" : "Instanz-ID muss eine positive Ganzzahl sein.", + "instance-id-max" : "Maximaler Instanz-ID-Wert {{max}}.", + "instance" : "Instanz", + "resource-label" : "#ID Ressourcenname", + "observe-label" : "Beobachten", + "attribute-label" : "Attribut", + "telemetry-label" : "Telemetrie", + "edit-observe-select" : "Zum Bearbeiten der Beobachtung Telemetrie oder Attribut auswählen", + "edit-attributes-select" : "Zum Bearbeiten der Attribute Telemetrie oder Attribut auswählen", + "no-attributes-set" : "Keine Attribute gesetzt", + "key-name" : "Schlüsselname", + "key-name-required" : "Schlüsselname ist erforderlich", + "attribute-name" : "Attributname", + "attribute-name-required" : "Attributname ist erforderlich.", + "attribute-value" : "Attributwert", + "attribute-value-required" : "Attributwert ist erforderlich.", + "attribute-value-pattern" : "Attributwert muss eine positive Ganzzahl sein.", + "edit-attributes" : "Attribute bearbeiten: {{ name }}", + "view-attributes" : "Attribute anzeigen: {{ name }}", + "add-attribute" : "Attribut hinzufügen", + "edit-attribute" : "Attribut bearbeiten", + "view-attribute" : "Attribut anzeigen", + "remove-attribute" : "Attribut entfernen", + "delete-server-text" : "Vorsicht: Nach der Bestätigung wird die Serverkonfiguration unwiderruflich gelöscht.", + "delete-server-title" : "Sind Sie sicher, dass Sie den Server löschen möchten?", + "mode" : "Sicherheitskonfigurationsmodus", + "bootstrap-tab" : "Bootstrap", + "bootstrap-server-legend" : "Bootstrap-Server (ShortId...)", + "lwm2m-server-legend" : "LwM2M-Server (ShortId...)", + "server" : "Server", + "short-id" : "Kurze Server-ID", + "short-id-tooltip" : "Kurze Server-ID. Wird verwendet, um die Serverobjektinstanz zu verknüpfen.\nDiese Kennung identifiziert jeden LwM2M-Server eindeutig, der für den LwM2M-Client konfiguriert ist.\nDie Ressource MUSS gesetzt werden, wenn die Bootstrap-Server-Ressource den Wert 'false' hat.\nDie Werte ID:0 und ID:65535 dürfen NICHT für die Identifizierung des LwM2M-Servers verwendet werden.", + "short-id-tooltip-bootstrap" : "Kurze Server-ID. Wird verwendet, um die Serverobjektinstanz zu verknüpfen.\nDiese Kennung identifiziert jeden LwM2M-Server eindeutig, der für den LwM2M-Client konfiguriert ist.\nDie Ressource MUSS gesetzt werden, wenn die Bootstrap-Server-Ressource den Wert 'false' hat.", + "short-id-required" : "Kurze Server-ID ist erforderlich.", + "short-id-range" : "Kurze Server-ID sollte im Bereich von {{ min }} bis {{ max }} liegen.", + "short-id-pattern" : "Kurze Server-ID muss eine positive Ganzzahl sein.", + "lifetime" : "Registrierungslebensdauer des Clients", + "lifetime-required" : "Registrierungslebensdauer des Clients ist erforderlich.", + "lifetime-pattern" : "Registrierungslebensdauer muss eine positive Ganzzahl sein.", + "default-min-period" : "Minimale Periode zwischen zwei Benachrichtigungen (s)", + "default-min-period-tooltip" : "Standardwert für die minimale Beobachtungsperiode, wenn dieser Parameter nicht in der Beobachtung enthalten ist.", + "default-min-period-required" : "Minimale Periode ist erforderlich.", + "default-min-period-pattern" : "Minimale Periode muss eine positive Ganzzahl sein.", + "notification-storing" : "Benachrichtigungsspeicherung bei Deaktivierung oder Offline-Modus", + "binding" : "Bindung", + "binding-type" : { + "u" : "U: Client ist jederzeit über UDP-Bindung erreichbar.", + "m" : "M: Client ist jederzeit über MQTT-Bindung erreichbar.", + "h" : "H: Client ist jederzeit über HTTP-Bindung erreichbar.", + "t" : "T: Client ist jederzeit über TCP-Bindung erreichbar.", + "s" : "S: Client ist jederzeit über SMS-Bindung erreichbar.", + "n" : "N: Client MUSS auf solche Anfragen über eine Non-IP-Verbindung antworten (unterstützt seit LWM2M 1.1).", + "uq" : "UQ: UDP-Verbindung im Queue-Modus (nicht unterstützt seit LWM2M 1.1)", + "uqs" : "UQS: Sowohl UDP- als auch SMS-Verbindungen aktiv; UDP im Queue-Modus, SMS im Standardmodus (nicht unterstützt seit LWM2M 1.1)", + "tq" : "TQ: TCP-Verbindung im Queue-Modus (nicht unterstützt seit LWM2M 1.1)", + "tqs" : "TQS: Sowohl TCP- als auch SMS-Verbindungen aktiv; TCP im Queue-Modus, SMS im Standardmodus (nicht unterstützt seit LWM2M 1.1)", + "sq" : "SQ: SMS-Verbindung im Queue-Modus (nicht unterstützt seit LWM2M 1.1)" + }, + "binding-tooltip" : "Liste der unterstützten Bindungsmodi im \"binding\"-Ressourcenelement des LwM2M-Serverobjekts - /1/x/7.\nMehrere Übertragungsmodi sind möglich, aber nur einer wird während einer Transport-Session genutzt.", + "bootstrap-server" : "Bootstrap-Server", + "lwm2m-server" : "LwM2M-Server", + "include-bootstrap-server" : "Bootstrap-Server-Updates einbeziehen", + "bootstrap-update-title" : "Bootstrap-Server ist bereits konfiguriert. Möchten Sie die Updates wirklich ausschließen?", + "bootstrap-update-text" : "Vorsicht: Nach der Bestätigung gehen die Konfigurationsdaten des Bootstrap-Servers verloren.", + "server-host" : "Host", + "server-host-required" : "Host ist erforderlich.", + "server-port" : "Port", + "server-port-required" : "Port ist erforderlich.", + "server-port-pattern" : "Port muss eine positive Ganzzahl sein.", + "server-port-range" : "Port sollte im Bereich von 1 bis 65535 liegen.", + "server-public-key" : "Öffentlicher Schlüssel des Servers", + "server-public-key-required" : "Öffentlicher Schlüssel des Servers ist erforderlich.", + "client-hold-off-time" : "Wartezeit des Clients", + "client-hold-off-time-required" : "Wartezeit des Clients ist erforderlich.", + "client-hold-off-time-pattern" : "Wartezeit des Clients muss eine positive Ganzzahl sein.", + "client-hold-off-time-tooltip" : "Client-Wartezeit nur bei Verwendung eines Bootstrap-Servers", + "account-after-timeout" : "Konto nach Zeitüberschreitung", + "account-after-timeout-required" : "Konto nach Zeitüberschreitung ist erforderlich.", + "account-after-timeout-pattern" : "Konto nach Zeitüberschreitung muss eine positive Ganzzahl sein.", + "account-after-timeout-tooltip" : "Wert für Konto nach Zeitüberschreitung beim Bootstrap-Server.", + "server-type" : "Servertyp", + "add-new-server-title" : "Neue Serverkonfiguration hinzufügen", + "add-server-config" : "Serverkonfiguration hinzufügen", + "add-lwm2m-server-config" : "LwM2M-Server hinzufügen", + "no-config-servers" : "Keine Server konfiguriert", + "others-tab" : "Weitere Einstellungen", + "client-strategy" : "Client-Strategie beim Verbinden", + "client-strategy-label" : "Strategie", + "client-strategy-only-observe" : "Nur Beobachtungsanfragen nach der ersten Verbindung", + "client-strategy-read-all" : "Alle Ressourcen lesen und beobachten nach der Registrierung", + "fw-update" : "Firmware-Update", + "fw-update-strategy" : "Firmware-Update-Strategie", + "fw-update-strategy-data" : "Firmware-Update als Binärdatei mit Objekt 19 und Ressource 0 (Daten) übertragen", + "fw-update-strategy-package" : "Firmware-Update als Binärdatei mit Objekt 5 und Ressource 0 (Paket) übertragen", + "fw-update-strategy-package-uri" : "Automatisch eindeutige CoAP-URL generieren, um das Paket herunterzuladen, und als Objekt 5 und Ressource 1 (Paket-URI) übertragen", + "sw-update" : "Software-Update", + "sw-update-strategy" : "Software-Update-Strategie", + "sw-update-strategy-package" : "Binärdatei mit Objekt 9 und Ressource 2 (Paket) übertragen", + "sw-update-strategy-package-uri" : "Automatisch eindeutige CoAP-URL generieren, um das Paket herunterzuladen, und Software-Update mit Objekt 9 und Ressource 3 (Paket-URI) übertragen", + "fw-update-resource" : "Firmware-Update CoAP-Ressource", + "fw-update-resource-required" : "Firmware-Update CoAP-Ressource ist erforderlich.", + "sw-update-resource" : "Software-Update CoAP-Ressource", + "sw-update-resource-required" : "Software-Update CoAP-Ressource ist erforderlich.", + "config-json-tab" : "JSON-Konfigurationsprofil Gerät", + "attributes-name" : { + "min-period" : "Minimale Periode", + "max-period" : "Maximale Periode", + "greater-than" : "Größer als", + "less-than" : "Kleiner als", + "step" : "Schritt", + "min-evaluation-period" : "Minimale Auswertungsperiode", + "max-evaluation-period" : "Maximale Auswertungsperiode" + }, + "default-object-id" : "Standardobjektversion (Attribut)", + "default-object-id-ver" : { + "v1-0" : "1.0", + "v1-1" : "1.1" + } + }, + "snmp" : { + "add-communication-config" : "Kommunikationskonfiguration hinzufügen", + "add-mapping" : "Zuordnung hinzufügen", + "authentication-passphrase" : "Authentifizierungs-Passphrase", + "authentication-passphrase-required" : "Authentifizierungs-Passphrase ist erforderlich.", + "authentication-protocol" : "Authentifizierungsprotokoll", + "authentication-protocol-required" : "Authentifizierungsprotokoll ist erforderlich.", + "communication-configs" : "Kommunikationskonfigurationen", + "community" : "Community-String", + "community-required" : "Community-String ist erforderlich.", + "context-name" : "Kontextname", + "data-key" : "Datenschlüssel", + "data-key-required" : "Datenschlüssel ist erforderlich.", + "data-type" : "Datentyp", + "data-type-required" : "Datentyp ist erforderlich.", + "engine-id" : "Engine-ID", + "host" : "Host", + "host-required" : "Host ist erforderlich.", + "oid" : "OID", + "oid-pattern" : "Ungültiges OID-Format.", + "oid-required" : "OID ist erforderlich.", + "please-add-communication-config" : "Bitte Kommunikationskonfiguration hinzufügen", + "please-add-mapping-config" : "Bitte Zuordnungskonfiguration hinzufügen", + "port" : "Port", + "port-format" : "Ungültiges Port-Format.", + "port-required" : "Port ist erforderlich.", + "privacy-passphrase" : "Privatsphäre-Passphrase", + "privacy-passphrase-required" : "Privatsphäre-Passphrase ist erforderlich.", + "privacy-protocol" : "Privatsphäreprotokoll", + "privacy-protocol-required" : "Privatsphäreprotokoll ist erforderlich.", + "protocol-version" : "Protokollversion", + "protocol-version-required" : "Protokollversion ist erforderlich.", + "querying-frequency" : "Abfragefrequenz, ms", + "querying-frequency-invalid-format" : "Abfragefrequenz muss eine positive Ganzzahl sein.", + "querying-frequency-required" : "Abfragefrequenz ist erforderlich.", + "retries" : "Wiederholungen", + "retries-invalid-format" : "Wiederholungen müssen eine positive Ganzzahl sein.", + "retries-required" : "Wiederholungen sind erforderlich.", + "scope" : "Bereich", + "scope-required" : "Bereich ist erforderlich.", + "security-name" : "Sicherheitsname", + "security-name-required" : "Sicherheitsname ist erforderlich.", + "timeout-ms" : "Zeitüberschreitung, ms", + "timeout-ms-invalid-format" : "Zeitüberschreitung muss eine positive Ganzzahl sein.", + "timeout-ms-required" : "Zeitüberschreitung ist erforderlich.", + "user-name" : "Benutzername", + "user-name-required" : "Benutzername ist erforderlich." + } + }, + "dialog" : { + "close" : "Dialog schließen", + "error-message-title" : "Fehlermeldung:", + "error-details-title" : "Fehlerdetails" + }, + "direction" : { + "column" : "Spalte", + "row" : "Zeile" + }, + "edge" : { + "edge" : "Edge", + "edge-instances" : "Edge-Instanzen", + "instances" : "Instanzen", + "edge-file" : "Edge-Datei", + "name-max-length" : "Name sollte weniger als 256 Zeichen haben", + "label-max-length" : "Label sollte weniger als 256 Zeichen haben", + "type-max-length" : "Typ sollte weniger als 256 Zeichen haben", + "management" : "Edge-Verwaltung", + "no-edges-matching" : "Keine Edges gefunden, die mit '{{entity}}' übereinstimmen.", + "add" : "Edge hinzufügen", + "no-edges-text" : "Keine Edges gefunden", + "edge-details" : "Edge-Details", + "add-edge-text" : "Neue Edge hinzufügen", + "delete" : "Edge löschen", + "delete-edge-title" : "Sind Sie sicher, dass Sie die Edge '{{edgeName}}' löschen möchten?", + "delete-edge-text" : "Achtung: Nach der Bestätigung wird die Edge und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-edges-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Edge} other {# Edges} } löschen möchten?", + "delete-edges-text" : "Achtung: Nach der Bestätigung werden alle ausgewählten Edges und zugehörige Daten unwiederbringlich gelöscht.", + "name" : "Name", + "name-starts-with" : "Edge-Name beginnt mit", + "name-required" : "Name ist erforderlich.", + "description" : "Beschreibung", + "details" : "Details", + "events" : "Ereignisse", + "copy-id" : "Edge-ID kopieren", + "id-copied-message" : "Edge-ID wurde in die Zwischenablage kopiert", + "sync" : "Edge synchronisieren", + "edge-required" : "Edge ist erforderlich", + "edge-type" : "Edge-Typ", + "edge-type-required" : "Edge-Typ ist erforderlich.", + "event-action" : "Ereignisaktion", + "entity-id" : "Entitäts-ID", + "select-edge-type" : "Edge-Typ auswählen", + "assign-to-customer" : "Einem Kunden zuweisen", + "assign-to-customer-text" : "Bitte wählen Sie den Kunden aus, dem die Edge(s) zugewiesen werden sollen", + "assign-edge-to-customer" : "Edge(s) einem Kunden zuweisen", + "assign-edge-to-customer-text" : "Bitte wählen Sie die Edges aus, die dem Kunden zugewiesen werden sollen", + "assignedToCustomer" : "Kunde zugewiesen", + "edge-public" : "Edge ist öffentlich", + "assigned-to-customer" : "Zugewiesen zu: {{customerTitle}}", + "unassign-from-customer" : "Von Kunde lösen", + "unassign-edge-title" : "Sind Sie sicher, dass Sie die Edge '{{edgeName}}' vom Kunden lösen möchten?", + "unassign-edge-text" : "Nach der Bestätigung wird die Edge vom Kunden gelöst und ist für diesen nicht mehr zugänglich.", + "unassign-edges-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Edge} other {# Edges} } vom Kunden lösen möchten?", + "unassign-edges-text" : "Nach der Bestätigung werden alle ausgewählten Edges vom Kunden gelöst und sind nicht mehr zugänglich.", + "make-public" : "Edge öffentlich machen", + "make-public-edge-title" : "Sind Sie sicher, dass Sie die Edge '{{edgeName}}' öffentlich machen möchten?", + "make-public-edge-text" : "Nach der Bestätigung wird die Edge und alle zugehörigen Daten öffentlich zugänglich sein.", + "make-private" : "Edge privat machen", + "public" : "Öffentlich", + "make-private-edge-title" : "Sind Sie sicher, dass Sie die Edge '{{edgeName}}' privat machen möchten?", + "make-private-edge-text" : "Nach der Bestätigung wird die Edge und alle zugehörigen Daten privat sein und nicht mehr öffentlich zugänglich.", + "import" : "Edge importieren", + "install-connect-instructions" : "Installations- & Verbindungsanleitung", + "install-connect-instructions-edge-created" : "Edge erstellt! Installations- & Verbindungsanleitung prüfen", + "loading-edge-instructions" : "Lade Edge-Anweisungen...", + "label" : "Label", + "load-entity-error" : "Daten konnten nicht geladen werden. Entität wurde gelöscht.", + "assign-new-edge" : "Neue Edge zuweisen", + "unassign-from-edge" : "Von Edge lösen", + "edge-key" : "Edge-Schlüssel", + "copy-edge-key" : "Edge-Schlüssel kopieren", + "edge-key-copied-message" : "Edge-Schlüssel wurde in die Zwischenablage kopiert", + "edge-secret" : "Edge-Geheimnis", + "copy-edge-secret" : "Edge-Geheimnis kopieren", + "edge-secret-copied-message" : "Edge-Geheimnis wurde in die Zwischenablage kopiert", + "manage-assets" : "Assets verwalten", + "manage-devices" : "Geräte verwalten", + "manage-entity-views" : "Entitätsansichten verwalten", + "manage-dashboards" : "Dashboards verwalten", + "manage-rulechains" : "Regelketten verwalten", + "assets" : "Edge-Assets", + "devices" : "Edge-Geräte", + "entity-views" : "Edge-Entitätsansichten", + "dashboard" : "Edge-Dashboard", + "dashboards" : "Edge-Dashboards", + "rulechain-templates" : "Regelkettentemplates", + "edge-rulechain-templates" : "Edge-Regelkettentemplates", + "rulechains" : "Edge-Regelketten", + "search" : "Edges suchen", + "selected-edges" : "{ count, plural, =1 {1 Edge} other {# Edges} } ausgewählt", + "any-edge" : "Beliebige Edge", + "no-edge-types-matching" : "Keine Edge-Typen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "edge-type-list-empty" : "Keine Edge-Typen ausgewählt.", + "edge-types" : "Edge-Typen", + "enter-edge-type" : "Edge-Typ eingeben", + "deployed" : "Bereitgestellt", + "pending" : "Ausstehend", + "downlinks" : "Downlinks", + "no-downlinks-prompt" : "Keine Downlinks gefunden", + "sync-process-started-successfully" : "Synchronisationsprozess erfolgreich gestartet!", + "missing-related-rule-chains-title" : "Edge hat fehlende zugehörige Regelketten", + "missing-related-rule-chains-text" : "Zugewiesene Regelketten verwenden Knoten, die Nachrichten an nicht zugewiesene Regelketten weiterleiten.

Liste fehlender Regelketten:
{{missingRuleChains}}", + "widget-datasource-error" : "Dieses Widget unterstützt nur Edge-Entitätsdatenquellen", + "upgrade-instructions" : "Upgrade-Anleitung", + "connected" : "Verbunden", + "disconnected" : "Getrennt" + }, + "edge-event" : { + "type-dashboard" : "Dashboard", + "type-asset" : "Asset", + "type-device" : "Gerät", + "type-device-profile" : "Geräteprofil", + "type-asset-profile" : "Asset-Profil", + "type-entity-view" : "Entitätsansicht", + "type-alarm" : "Alarm", + "type-rule-chain" : "Regelkette", + "type-rule-chain-metadata" : "Metadaten der Regelkette", + "type-edge" : "Edge", + "type-user" : "Benutzer", + "type-tenant" : "Mieter", + "type-tenant-profile" : "Mieterprofil", + "type-customer" : "Kunde", + "type-relation" : "Beziehung", + "type-widgets-bundle" : "Widget-Bündel", + "type-widgets-type" : "Widget-Typ", + "type-admin-settings" : "Admin-Einstellungen", + "type-ota-package" : "OTA-Paket", + "type-queue" : "Warteschlange", + "action-type-added" : "Hinzugefügt", + "action-type-deleted" : "Gelöscht", + "action-type-updated" : "Aktualisiert", + "action-type-post-attributes" : "Attribute gesendet", + "action-type-attributes-updated" : "Attribute aktualisiert", + "action-type-attributes-deleted" : "Attribute gelöscht", + "action-type-timeseries-updated" : "Zeitreihen aktualisiert", + "action-type-credentials-updated" : "Anmeldeinformationen aktualisiert", + "action-type-assigned-to-customer" : "Kunde zugewiesen", + "action-type-unassigned-from-customer" : "Vom Kunden gelöst", + "action-type-relation-add-or-update" : "Beziehung hinzugefügt oder aktualisiert", + "action-type-relation-deleted" : "Beziehung gelöscht", + "action-type-rpc-call" : "RPC-Aufruf", + "action-type-alarm-ack" : "Alarm bestätigt", + "action-type-alarm-clear" : "Alarm gelöscht", + "action-type-alarm-assigned" : "Alarm zugewiesen", + "action-type-alarm-unassigned" : "Alarm gelöst", + "action-type-assigned-to-edge" : "Edge zugewiesen", + "action-type-unassigned-from-edge" : "Edge gelöst", + "action-type-credentials-request" : "Anmeldeanforderung", + "action-type-entity-merge-request" : "Entitätszusammenführungsanfrage" + }, + "error" : { + "unable-to-connect" : "Verbindung zum Server nicht möglich! Bitte überprüfen Sie Ihre Internetverbindung.", + "unhandled-error-code" : "Unbehandelter Fehlercode: {{errorCode}}", + "unknown-error" : "Unbekannter Fehler" + }, + "entity" : { + "entity" : "Entität", + "entities" : "Entitäten", + "entities-count" : "Anzahl der Entitäten", + "alarms-count" : "Anzahl der Alarme", + "aliases" : "Entitätsalias", + "aliases-short" : "Aliase", + "entity-alias" : "Entitätsalias", + "unable-delete-entity-alias-title" : "Entitätsalias konnte nicht gelöscht werden", + "unable-delete-entity-alias-text" : "Der Entitätsalias '{{entityAlias}}' kann nicht gelöscht werden, da er von folgenden Widget(s) verwendet wird:
{{widgetsList}}", + "duplicate-alias-error" : "Doppelter Alias gefunden '{{alias}}'.
Entitätsaliase müssen innerhalb des Dashboards eindeutig sein.", + "missing-entity-filter-error" : "Filter für Alias '{{alias}}' fehlt.", + "configure-alias" : "Konfiguriere Alias '{{alias}}'", + "alias" : "Alias", + "alias-required" : "Entitätsalias ist erforderlich.", + "remove-alias" : "Entitätsalias entfernen", + "add-alias" : "Entitätsalias hinzufügen", + "edit-alias" : "Entitätsalias bearbeiten", + "entity-list" : "Entitätenliste", + "entity-type" : "Entitätstyp", + "entity-types" : "Entitätstypen", + "entity-type-list" : "Liste der Entitätstypen", + "any-entity" : "Beliebige Entität", + "add-entity-type" : "Entitätstyp hinzufügen", + "enter-entity-type" : "Entitätstyp eingeben", + "no-entities-matching" : "Keine Entitäten gefunden, die mit '{{entity}}' übereinstimmen.", + "no-entities-text" : "Keine Entitäten gefunden", + "no-entity-types-matching" : "Keine Entitätstypen gefunden, die mit '{{entityType}}' übereinstimmen.", + "name-starts-with" : "Namensausdruck", + "help-text" : "Verwenden Sie '%' nach Bedarf: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter" : "Filter verwenden", + "entity-list-empty" : "Keine Entitäten ausgewählt.", + "entity-type-list-required" : "Mindestens ein Entitätstyp sollte ausgewählt werden.", + "entity-name-filter-required" : "Filter für Entitätsname ist erforderlich.", + "entity-name-filter-no-entity-matched" : "Keine Entitäten gefunden, die mit '{{entity}}' beginnen.", + "all-subtypes" : "Alle", + "select-entities" : "Entitäten auswählen", + "no-aliases-found" : "Keine Aliase gefunden.", + "no-alias-matching" : "'{{alias}}' nicht gefunden.", + "create-new-alias" : "Einen neuen erstellen!", + "create-new" : "Neu erstellen", + "key" : "Schlüssel", + "key-name" : "Schlüsselname", + "no-keys-found" : "Keine Schlüssel gefunden.", + "no-key-matching" : "'{{key}}' nicht gefunden.", + "create-new-key" : "Einen neuen erstellen!", + "type" : "Typ", + "type-required" : "Entitätstyp ist erforderlich.", + "type-device" : "Gerät", + "type-devices" : "Geräte", + "list-of-devices" : "{ count, plural, =1 {Ein Gerät} other {Liste von # Geräten} }", + "device-name-starts-with" : "Geräte, deren Name mit '{{prefix}}' beginnt", + "type-device-profile" : "Geräteprofil", + "type-device-profiles" : "Geräteprofile", + "clear-selected-profiles" : "Ausgewählte Profile löschen", + "list-of-device-profiles" : "{ count, plural, =1 {Ein Geräteprofil} other {Liste von # Geräteprofilen} }", + "device-profile-name-starts-with" : "Geräteprofile, deren Name mit '{{prefix}}' beginnt", + "type-asset-profile" : "Asset-Profil", + "type-asset-profiles" : "Asset-Profile", + "list-of-asset-profiles" : "{ count, plural, =1 {Ein Asset-Profil} other {Liste von # Asset-Profilen} }", + "asset-profile-name-starts-with" : "Asset-Profile, deren Name mit '{{prefix}}' beginnt", + "type-asset" : "Asset", + "type-assets" : "Assets", + "list-of-assets" : "{ count, plural, =1 {Ein Asset} other {Liste von # Assets} }", + "asset-name-starts-with" : "Assets, deren Name mit '{{prefix}}' beginnt", + "type-entity-view" : "Entitätsansicht", + "type-entity-views" : "Entitätsansichten", + "list-of-entity-views" : "{ count, plural, =1 {Eine Entitätsansicht} other {Liste von # Entitätsansichten} }", + "entity-view-name-starts-with" : "Entitätsansichten, deren Name mit '{{prefix}}' beginnt", + "type-rule" : "Regel", + "type-rules" : "Regeln", + "list-of-rules" : "{ count, plural, =1 {Eine Regel} other {Liste von # Regeln} }", + "rule-name-starts-with" : "Regeln, deren Name mit '{{prefix}}' beginnt", + "type-plugin" : "Plugin", + "type-plugins" : "Plugins", + "list-of-plugins" : "{ count, plural, =1 {Ein Plugin} other {Liste von # Plugins} }", + "plugin-name-starts-with" : "Plugins, deren Name mit '{{prefix}}' beginnt", + "type-tenant" : "Tenant", + "type-tenants" : "Tenants", + "list-of-tenants" : "{ count, plural, =1 {Ein Tenant} other {Liste von # Tenants} }", + "tenant-name-starts-with" : "Tenants, deren Name mit '{{prefix}}' beginnt", + "type-tenant-profile" : "Tenant-Profil", + "type-tenant-profiles" : "Tenant-Profile", + "list-of-tenant-profiles" : "{ count, plural, =1 {Ein Tenant-Profil} other {Liste von # Tenant-Profilen} }", + "tenant-profile-name-starts-with" : "Tenant-Profile, deren Name mit '{{prefix}}' beginnt", + "type-customer" : "Kunde", + "type-customers" : "Kunden", + "list-of-customers" : "{ count, plural, =1 {Ein Kunde} other {Liste von # Kunden} }", + "customer-name-starts-with" : "Kunden, deren Name mit '{{prefix}}' beginnt", + "type-user" : "Benutzer", + "type-users" : "Benutzer", + "list-of-users" : "{ count, plural, =1 {Ein Benutzer} other {Liste von # Benutzern} }", + "user-name-starts-with" : "Benutzer, deren Name mit '{{prefix}}' beginnt", + "type-dashboard" : "Dashboard", + "type-dashboards" : "Dashboards", + "list-of-dashboards" : "{ count, plural, =1 {Ein Dashboard} other {Liste von # Dashboards} }", + "dashboard-name-starts-with" : "Dashboards, deren Name mit '{{prefix}}' beginnt", + "type-alarm" : "Alarm", + "type-alarms" : "Alarme", + "list-of-alarms" : "{ count, plural, =1 {Ein Alarm} other {Liste von # Alarmen} }", + "alarm-name-starts-with" : "Alarme, deren Name mit '{{prefix}}' beginnt", + "type-rulechain" : "Regelkette", + "type-rulechains" : "Regelketten", + "list-of-rulechains" : "{ count, plural, =1 {Eine Regelkette} other {Liste von # Regelketten} }", + "rulechain-name-starts-with" : "Regelketten, deren Name mit '{{prefix}}' beginnt", + "type-rulenode" : "Regelknoten", + "type-rulenodes" : "Regelknoten", + "list-of-rulenodes" : "{ count, plural, =1 {Ein Regelknoten} other {Liste von # Regelknoten} }", + "rulenode-name-starts-with" : "Regelknoten, deren Name mit '{{prefix}}' beginnt", + "type-current-customer" : "Aktueller Kunde", + "type-current-tenant" : "Aktueller Tenant", + "type-current-user" : "Aktueller Benutzer", + "type-current-user-owner" : "Aktueller Benutzerinhaber", + "type-calculated-field" : "Berechnetes Feld", + "type-calculated-fields" : "Berechnete Felder", + "type-widgets-bundle" : "Widget-Bündel", + "type-widgets-bundles" : "Widget-Bündel", + "list-of-widgets-bundles" : "{ count, plural, =1 {Ein Widget-Bündel} other {Liste von # Widget-Bündeln} }", + "type-widget" : "Widget", + "type-widgets" : "Widgets", + "list-of-widgets" : "{ count, plural, =1 {Ein Widget} other {Liste von # Widgets} }", + "search" : "Entitäten suchen", + "selected-entities" : "{ count, plural, =1 {1 Entität} other {# Entitäten} } ausgewählt", + "entity-name" : "Entitätsname", + "entity-label" : "Entitätslabel", + "details" : "Entitätsdetails", + "no-entities-prompt" : "Keine Entitäten gefunden", + "no-data" : "Keine Daten zur Anzeige vorhanden", + "columns-to-display" : "Anzuzeigende Spalten", + "type-api-usage-state" : "API-Nutzungsstatus", + "type-edge" : "Edge", + "type-edges" : "Edges", + "list-of-edges" : "{ count, plural, =1 {Ein Edge} other {Liste von # Edges} }", + "edge-name-starts-with" : "Edges, deren Name mit '{{prefix}}' beginnt", + "version-conflict" : { + "message" : "Möchten Sie die bestehende Version überschreiben oder Änderungen verwerfen und die neueste Version laden?", + "link" : "Sie können Ihre Version der {{entityType}} mit diesem Link herunterladen", + "overwrite" : "Version überschreiben", + "discard" : "Änderungen verwerfen" + }, + "type-tb-resource" : "Ressource", + "type-tb-resources" : "Ressourcen", + "list-of-tb-resources" : "{ count, plural, =1 {Eine Ressource} other {Liste von # Ressourcen} }", + "type-ota-package" : "OTA-Paket", + "type-rpc" : "RPC", + "type-queue" : "Warteschlange", + "type-queue-stats" : "Warteschlangenstatistiken", + "type-queues-stats" : "Warteschlangenstatistiken", + "type-notification" : "Benachrichtigung", + "type-notification-rule" : "Benachrichtigungsregel", + "type-notification-rules" : "Benachrichtigungsregeln", + "list-of-notification-rules" : "{ count, plural, =1 {Eine Benachrichtigungsregel} other {Liste von # Benachrichtigungsregeln} }", + "type-notification-target" : "Benachrichtigungsempfänger", + "type-notification-targets" : "Benachrichtigungsempfänger", + "list-of-notification-targets" : "{ count, plural, =1 {Ein Benachrichtigungsempfänger} other {Liste von # Benachrichtigungsempfängern} }", + "type-notification-request" : "Benachrichtigungsanfrage", + "type-notification-template" : "Benachrichtigungsvorlage", + "type-notification-templates" : "Benachrichtigungsvorlagen", + "list-of-notification-templates" : "{ count, plural, =1 {Eine Benachrichtigungsvorlage} other {Liste von # Benachrichtigungsvorlagen} }", + "link" : "Link", + "type-oauth2-client" : "OAuth 2.0-Client", + "type-oauth2-clients" : "OAuth 2.0-Clients", + "list-of-oauth2-clients" : "{ count, plural, =1 {Ein OAuth 2.0-Client} other {Liste von # OAuth 2.0-Clients} }", + "type-domain" : "Domain", + "type-domains" : "Domains", + "list-of-domains" : "{ count, plural, =1 {Eine Domain} other {Liste von # Domains} }", + "type-mobile-app" : "Mobile Anwendung", + "type-mobile-apps" : "Mobile Anwendungen", + "list-of-mobile-apps" : "{ count, plural, =1 {Eine mobile Anwendung} other {Liste von # mobilen Anwendungen} }", + "type-mobile-app-bundle" : "Mobile-Bundle", + "type-mobile-app-bundles" : "Mobile-Bundles", + "list-of-mobile-app-bundles" : "{ count, plural, =1 {Ein Mobile-Bundle} other {Liste von # Mobile-Bundles} }" + }, + "entity-field" : { + "created-time" : "Erstellungszeit", + "name" : "Name", + "type" : "Typ", + "first-name" : "Vorname", + "last-name" : "Nachname", + "email" : "E-Mail", + "title" : "Titel", + "country" : "Land", + "state" : "Bundesland", + "city" : "Stadt", + "address" : "Adresse", + "address2" : "Adresse 2", + "zip" : "Postleitzahl", + "phone" : "Telefon", + "label" : "Bezeichnung", + "queue-name" : "Name der Warteschlange", + "service-id" : "Service-ID", + "owner-name" : "Name des Besitzers", + "owner-type" : "Typ des Besitzers" + }, + "entity-view" : { + "entity-view" : "Entitätsansicht", + "entity-view-required" : "Entitätsansicht ist erforderlich.", + "entity-views" : "Entitätsansichten", + "management" : "Entitätsansichtenverwaltung", + "view-entity-views" : "Entitätsansichten anzeigen", + "entity-view-alias" : "Entitätsansicht-Alias", + "aliases" : "Entitätsansicht-Aliase", + "no-alias-matching" : "'{{alias}}' nicht gefunden.", + "no-aliases-found" : "Keine Aliase gefunden.", + "no-key-matching" : "'{{key}}' nicht gefunden.", + "no-keys-found" : "Keine Schlüssel gefunden.", + "create-new-alias" : "Neuen Alias erstellen!", + "create-new-key" : "Neuen Schlüssel erstellen!", + "duplicate-alias-error" : "Doppelter Alias gefunden '{{alias}}'.
Entitätsansicht-Aliase müssen innerhalb des Dashboards eindeutig sein.", + "configure-alias" : "Konfiguriere Alias '{{alias}}'", + "no-entity-views-matching" : "Keine Entitätsansichten gefunden, die '{{entity}}' entsprechen.", + "public" : "Öffentlich", + "alias" : "Alias", + "alias-required" : "Entitätsansicht-Alias ist erforderlich.", + "remove-alias" : "Entitätsansicht-Alias entfernen", + "add-alias" : "Entitätsansicht-Alias hinzufügen", + "name-starts-with" : "Namensausdruck für Entitätsansicht", + "help-text" : "Verwenden Sie '%' nach Bedarf: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list" : "Liste der Entitätsansichten", + "use-entity-view-name-filter" : "Filter verwenden", + "entity-view-list-empty" : "Keine Entitätsansichten ausgewählt.", + "entity-view-name-filter-required" : "Filter für Entitätsansicht-Name ist erforderlich.", + "entity-view-name-filter-no-entity-view-matched" : "Keine Entitätsansichten gefunden, die mit '{{entityView}}' beginnen.", + "add" : "Entitätsansicht hinzufügen", + "entity-view-public" : "Entitätsansicht ist öffentlich", + "assign-to-customer" : "Kunde zuweisen", + "assign-entity-view-to-customer" : "Entitätsansicht(en) einem Kunden zuweisen", + "assign-entity-view-to-customer-text" : "Bitte wählen Sie die Entitätsansichten aus, die dem Kunden zugewiesen werden sollen", + "no-entity-views-text" : "Keine Entitätsansichten gefunden", + "assign-to-customer-text" : "Bitte wählen Sie den Kunden aus, dem die Entitätsansicht(en) zugewiesen werden sollen", + "entity-view-details" : "Details der Entitätsansicht", + "add-entity-view-text" : "Neue Entitätsansicht hinzufügen", + "delete" : "Entitätsansicht löschen", + "assign-entity-views" : "Entitätsansichten zuweisen", + "assign-entity-views-text" : "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } einem Kunden zuweisen", + "delete-entity-views" : "Entitätsansichten löschen", + "make-public" : "Entitätsansicht öffentlich machen", + "make-private" : "Entitätsansicht privat machen", + "unassign-from-customer" : "Zuweisung vom Kunden aufheben", + "unassign-entity-views" : "Entitätsansichtenzuweisung aufheben", + "unassign-entity-views-action-title" : "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } vom Kundenzuweisung aufheben", + "assign-new-entity-view" : "Neue Entitätsansicht zuweisen", + "delete-entity-view-title" : "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' löschen möchten?", + "delete-entity-view-text" : "Seien Sie vorsichtig, nach der Bestätigung wird die Entitätsansicht und alle zugehörigen Daten unwiderruflich gelöscht.", + "delete-entity-views-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } löschen möchten?", + "delete-entity-views-action-title" : "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } löschen", + "delete-entity-views-text" : "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Entitätsansichten und deren Daten unwiderruflich gelöscht.", + "make-public-entity-view-title" : "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' öffentlich machen möchten?", + "make-public-entity-view-text" : "Nach der Bestätigung wird die Entitätsansicht und alle ihre Daten öffentlich und für andere zugänglich gemacht.", + "make-private-entity-view-title" : "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' privat machen möchten?", + "make-private-entity-view-text" : "Nach der Bestätigung wird die Entitätsansicht und alle ihre Daten privat und nicht mehr zugänglich sein.", + "unassign-entity-view-title" : "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' von der Zuweisung lösen möchten?", + "unassign-entity-view-text" : "Nach der Bestätigung wird die Entitätsansicht von der Zuweisung entfernt und ist für den Kunden nicht mehr zugänglich.", + "unassign-entity-view" : "Entitätsansichtzuweisung aufheben", + "unassign-entity-views-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } von der Zuweisung lösen möchten?", + "unassign-entity-views-text" : "Nach der Bestätigung werden alle ausgewählten Entitätsansichten von der Zuweisung gelöst und sind für den Kunden nicht mehr zugänglich.", + "entity-view-type" : "Typ der Entitätsansicht", + "entity-view-type-required" : "Typ der Entitätsansicht ist erforderlich.", + "select-entity-view-type" : "Typ der Entitätsansicht auswählen", + "enter-entity-view-type" : "Typ der Entitätsansicht eingeben", + "any-entity-view" : "Beliebige Entitätsansicht", + "no-entity-view-types-matching" : "Keine Entitätsansichtstypen gefunden, die '{{entitySubtype}}' entsprechen.", + "entity-view-type-list-empty" : "Keine Entitätsansichtstypen ausgewählt.", + "entity-view-types" : "Entitätsansichtstypen", + "created-time" : "Erstellungszeit", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "name-max-length" : "Name sollte weniger als 256 Zeichen haben.", + "type-max-length" : "Entitätsansichtstyp sollte weniger als 256 Zeichen haben.", + "description" : "Beschreibung", + "events" : "Ereignisse", + "details" : "Details", + "copyId" : "Entitätsansicht-ID kopieren", + "idCopiedMessage" : "Entitätsansicht-ID wurde in die Zwischenablage kopiert", + "assignedToCustomer" : "Dem Kunden zugewiesen", + "unable-entity-view-device-alias-title" : "Entitätsansicht-Alias konnte nicht gelöscht werden", + "unable-entity-view-device-alias-text" : "Gerätealias '{{entityViewAlias}}' kann nicht gelöscht werden, da er von folgenden Widget(s) verwendet wird:
{{widgetsList}}", + "select-entity-view" : "Entitätsansicht auswählen", + "start-ts" : "Startzeit", + "end-ts" : "Endzeit", + "date-limits" : "Datumsgrenzen", + "client-attributes" : "Client-Attribute", + "shared-attributes" : "Geteilte Attribute", + "server-attributes" : "Server-Attribute", + "timeseries" : "Zeitreihen", + "client-attributes-placeholder" : "Client-Attribute", + "shared-attributes-placeholder" : "Geteilte Attribute", + "server-attributes-placeholder" : "Server-Attribute", + "timeseries-placeholder" : "Zeitreihen", + "target-entity" : "Zielentität", + "attributes-propagation" : "Attributübertragung", + "attributes-propagation-hint" : "Die Entitätsansicht kopiert automatisch die angegebenen Attribute von der Zielentität jedes Mal, wenn Sie diese Entitätsansicht speichern oder aktualisieren. Aus Leistungsgründen werden Zielentitätsattribute nicht bei jeder Attributänderung auf die Entitätsansicht übertragen. Sie können die automatische Übertragung aktivieren, indem Sie einen 'copy to view'-Regelknoten in Ihrer Regelkette konfigurieren und 'Post attributes' und 'Attributes Updated'-Nachrichten an diesen neuen Regelknoten verlinken.", + "timeseries-data" : "Zeitreihendaten", + "timeseries-data-hint" : "Konfigurieren Sie die Zeitreihendatenschlüssel der Zielentität, die für die Entitätsansicht zugänglich sein werden. Diese Zeitreihendaten sind schreibgeschützt.", + "search" : "Entitätsansichten suchen", + "selected-entity-views" : "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } ausgewählt", + "assign-entity-view-to-edge" : "Entitätsansicht(en) Edge zuweisen", + "assign-entity-view-to-edge-text" : "Bitte wählen Sie die Entitätsansichten aus, die dem Edge zugewiesen werden sollen", + "unassign-entity-view-from-edge-title" : "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' von Edge entfernen möchten?", + "unassign-entity-view-from-edge-text" : "Nach der Bestätigung wird die Entitätsansicht von Edge entfernt und ist nicht mehr zugänglich.", + "unassign-entity-views-from-edge-action-title" : "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } von Edge entfernen", + "unassign-entity-view-from-edge" : "Entitätsansicht von Edge entfernen", + "unassign-entity-views-from-edge-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } von Edge entfernen möchten?", + "unassign-entity-views-from-edge-text" : "Nach der Bestätigung werden alle ausgewählten Entitätsansichten von Edge entfernt und sind nicht mehr zugänglich." + }, + "event" : { + "event-type" : "Ereignistyp", + "events-filter" : "Ereignisfilter", + "clean-events" : "Ereignisse löschen", + "type-error" : "Fehler", + "type-lc-event" : "Lebenszyklusereignis", + "type-stats" : "Statistiken", + "type-debug-rule-node" : "Debug", + "type-debug-rule-chain" : "Debug", + "type-debug-calculated-field" : "Debug", + "arguments" : "Argumente", + "result" : "Ergebnis", + "no-events-prompt" : "Keine Ereignisse gefunden", + "error" : "Fehler", + "alarm" : "Alarm", + "event-time" : "Ereigniszeit", + "server" : "Server", + "body" : "Inhalt", + "method" : "Methode", + "type" : "Typ", + "metadata" : "Metadaten", + "message" : "Nachricht", + "message-id" : "Nachrichten-ID", + "copy-message-id" : "Nachrichten-ID kopieren", + "message-type" : "Nachrichtentyp", + "data-type" : "Datentyp", + "relation-type" : "Relationstyp", + "data" : "Daten", + "event" : "Ereignis", + "status" : "Status", + "success" : "Erfolg", + "failed" : "Fehlgeschlagen", + "messages-processed" : "Verarbeitete Nachrichten", + "max-messages-processed" : "Maximal verarbeitete Nachrichten", + "min-messages-processed" : "Minimal verarbeitete Nachrichten", + "errors-occurred" : "Fehler aufgetreten", + "max-errors-occurred" : "Maximal aufgetretene Fehler", + "min-errors-occurred" : "Minimal aufgetretene Fehler", + "min-value" : "Mindestwert ist 0.", + "all-events" : "Alle", + "has-error" : "Hat Fehler", + "entity-id" : "Entitäts-ID", + "copy-entity-id" : "Entitäts-ID kopieren", + "entity-type" : "Entitätstyp", + "clear-filter" : "Filter löschen", + "clear-request-title" : "Alle Ereignisse löschen", + "clear-request-text" : "Sind Sie sicher, dass Sie alle Ereignisse löschen möchten?", + "started" : "Gestartet", + "updated" : "Aktualisiert", + "stopped" : "Gestoppt" + }, + "extension" : { + "extensions" : "Erweiterungen", + "selected-extensions" : "{ count, plural, =1 {1 Erweiterung} other {# Erweiterungen} } ausgewählt", + "type" : "Typ", + "key" : "Schlüssel", + "value" : "Wert", + "id" : "Id", + "extension-id" : "Erweiterungs-ID", + "extension-type" : "Erweiterungstyp", + "transformer-json" : "JSON *", + "unique-id-required" : "Die aktuelle Erweiterungs-ID existiert bereits.", + "delete" : "Erweiterung löschen", + "add" : "Erweiterung hinzufügen", + "edit" : "Erweiterung bearbeiten", + "delete-extension-title" : "Sind Sie sicher, dass Sie die Erweiterung '{{extensionId}}' löschen möchten?", + "delete-extension-text" : "Seien Sie vorsichtig, nach der Bestätigung wird die Erweiterung und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-extensions-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Erweiterung} other {# Erweiterungen} } löschen möchten?", + "delete-extensions-text" : "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Erweiterungen entfernt.", + "converters" : "Konverter", + "converter-id" : "Konverter-ID", + "configuration" : "Konfiguration", + "converter-configurations" : "Konverterkonfigurationen", + "token" : "Sicherheitstoken", + "add-converter" : "Konverter hinzufügen", + "add-config" : "Konverterkonfiguration hinzufügen", + "device-name-expression" : "Gerätename Ausdruck", + "device-type-expression" : "Gerätetyp Ausdruck", + "custom" : "Benutzerdefiniert", + "to-double" : "In Double umwandeln", + "transformer" : "Transformer", + "json-required" : "Transformer-JSON ist erforderlich.", + "json-parse" : "Transformer-JSON kann nicht geparst werden.", + "attributes" : "Attribute", + "add-attribute" : "Attribut hinzufügen", + "add-map" : "Zuordnungselement hinzufügen", + "timeseries" : "Zeitreihen", + "add-timeseries" : "Zeitreihe hinzufügen", + "field-required" : "Feld ist erforderlich", + "brokers" : "Brokers", + "add-broker" : "Broker hinzufügen", + "host" : "Host", + "port" : "Port", + "port-range" : "Der Port muss zwischen 1 und 65535 liegen.", + "ssl" : "SSL", + "credentials" : "Zugangsdaten", + "username" : "Benutzername", + "password" : "Passwort", + "retry-interval" : "Wiederholungsintervall in Millisekunden", + "anonymous" : "Anonym", + "basic" : "Basis", + "pem" : "PEM", + "ca-cert" : "CA-Zertifikatdatei *", + "private-key" : "Privater Schlüsseldatei *", + "cert" : "Zertifikatdatei *", + "no-file" : "Keine Datei ausgewählt.", + "drop-file" : "Datei ablegen oder klicken, um eine Datei hochzuladen.", + "mapping" : "Zuordnung", + "topic-filter" : "Themenfilter", + "converter-type" : "Konvertertyp", + "converter-json" : "Json", + "json-name-expression" : "Gerätename JSON-Ausdruck", + "topic-name-expression" : "Gerätename Themenausdruck", + "json-type-expression" : "Gerätetyp JSON-Ausdruck", + "topic-type-expression" : "Gerätetyp Themenausdruck", + "attribute-key-expression" : "Attributschlüssel Ausdruck", + "attr-json-key-expression" : "Attributschlüssel JSON-Ausdruck", + "attr-topic-key-expression" : "Attributschlüssel Themenausdruck", + "request-id-expression" : "Anforderungs-ID Ausdruck", + "request-id-json-expression" : "Anforderungs-ID JSON-Ausdruck", + "request-id-topic-expression" : "Anforderungs-ID Themenausdruck", + "response-topic-expression" : "Antwortthemenausdruck", + "value-expression" : "Wertausdruck", + "topic" : "Thema", + "timeout" : "Timeout in Millisekunden", + "converter-json-required" : "Konverter-JSON ist erforderlich.", + "converter-json-parse" : "Konverter-JSON kann nicht geparst werden.", + "filter-expression" : "Filterausdruck", + "connect-requests" : "Verbindungsanfragen", + "add-connect-request" : "Verbindungsanfrage hinzufügen", + "disconnect-requests" : "Trennungsanfragen", + "add-disconnect-request" : "Trennungsanfrage hinzufügen", + "attribute-requests" : "Attributanfragen", + "add-attribute-request" : "Attributanfrage hinzufügen", + "attribute-updates" : "Attributaktualisierungen", + "add-attribute-update" : "Attributaktualisierung hinzufügen", + "server-side-rpc" : "Serverseitige RPC", + "add-server-side-rpc-request" : "Serverseitige RPC-Anfrage hinzufügen", + "device-name-filter" : "Gerätenamenfilter", + "attribute-filter" : "Attributfilter", + "method-filter" : "Methodenfilter", + "request-topic-expression" : "Anforderungsthemenausdruck", + "response-timeout" : "Antwortzeitüberschreitung in Millisekunden", + "topic-expression" : "Themenausdruck", + "client-scope" : "Clientbereich", + "add-device" : "Gerät hinzufügen", + "opc-server" : "Server", + "opc-add-server" : "Server hinzufügen", + "opc-add-server-prompt" : "Bitte Server hinzufügen", + "opc-application-name" : "Anwendungsname", + "opc-application-uri" : "Anwendungs-URI", + "opc-scan-period-in-seconds" : "Scan-Periode in Sekunden", + "opc-security" : "Sicherheit", + "opc-identity" : "Identität", + "opc-keystore" : "Schlüsselablage", + "opc-type" : "Typ", + "opc-keystore-type" : "Typ", + "opc-keystore-location" : "Speicherort *", + "opc-keystore-password" : "Passwort", + "opc-keystore-alias" : "Alias", + "opc-keystore-key-password" : "Schlüsselpasswort", + "opc-device-node-pattern" : "Geräteknoten-Muster", + "opc-device-name-pattern" : "Gerätename-Muster", + "modbus-server" : "Server/Slaves", + "modbus-add-server" : "Server/Slave hinzufügen", + "modbus-add-server-prompt" : "Bitte Server/Slave hinzufügen", + "modbus-transport" : "Transport", + "modbus-tcp-reconnect" : "Automatisch neu verbinden", + "modbus-rtu-over-tcp" : "RTU über TCP", + "modbus-port-name" : "Serieller Portname", + "modbus-encoding" : "Kodierung", + "modbus-parity" : "Parität", + "modbus-baudrate" : "Baudrate", + "modbus-databits" : "Datenbits", + "modbus-stopbits" : "Stoppbits", + "modbus-databits-range" : "Datenbits sollten im Bereich von 7 bis 8 liegen.", + "modbus-stopbits-range" : "Stoppbits sollten im Bereich von 1 bis 2 liegen.", + "modbus-unit-id" : "Einheiten-ID", + "modbus-unit-id-range" : "Einheiten-ID sollte im Bereich von 1 bis 247 liegen.", + "modbus-device-name" : "Gerätename", + "modbus-poll-period" : "Abfrageintervall (ms)", + "modbus-attributes-poll-period" : "Abfrageintervall für Attribute (ms)", + "modbus-timeseries-poll-period" : "Abfrageintervall für Zeitreihen (ms)", + "modbus-poll-period-range" : "Das Abfrageintervall sollte ein positiver Wert sein.", + "modbus-tag" : "Tag", + "modbus-function" : "Funktion", + "modbus-register-address" : "Registeradresse", + "modbus-register-address-range" : "Registeradresse sollte im Bereich von 0 bis 65535 liegen.", + "modbus-register-bit-index" : "Bit-Index", + "modbus-register-bit-index-range" : "Bit-Index sollte im Bereich von 0 bis 15 liegen.", + "modbus-register-count" : "Registeranzahl", + "modbus-register-count-range" : "Registeranzahl sollte ein positiver Wert sein.", + "modbus-byte-order" : "Byte-Reihenfolge", + "sync" : { + "status" : "Status", + "sync" : "Synchronisieren", + "not-sync" : "Nicht synchronisiert", + "last-sync-time" : "Letzte Synchronisationszeit", + "not-available" : "Nicht verfügbar" + }, + "export-extensions-configuration" : "Erweiterungskonfiguration exportieren", + "import-extensions-configuration" : "Erweiterungskonfiguration importieren", + "import-extensions" : "Erweiterungen importieren", + "import-extension" : "Erweiterung importieren", + "export-extension" : "Erweiterung exportieren", + "file" : "Erweiterungsdatei", + "invalid-file-error" : "Ungültige Erweiterungsdatei" + }, + "feature" : { + "advanced-features" : "Erweiterte Funktionen" + }, + "filter" : { + "add" : "Filter hinzufügen", + "edit" : "Filter bearbeiten", + "name" : "Filtername", + "name-required" : "Filtername ist erforderlich.", + "duplicate-filter" : "Ein Filter mit demselben Namen existiert bereits.", + "filters" : "Filter", + "unable-delete-filter-title" : "Filter kann nicht gelöscht werden", + "unable-delete-filter-text" : "Filter '{{filter}}' kann nicht gelöscht werden, da er von folgendem/n Widget(s) verwendet wird:
{{widgetsList}}", + "duplicate-filter-error" : "Doppelter Filter gefunden '{{filter}}'.
Filter müssen innerhalb des Dashboards eindeutig sein.", + "missing-key-filters-error" : "Schlüsselfilter fehlen für Filter '{{filter}}'.", + "filter" : "Filter", + "editable" : "Bearbeitbar", + "no-filters-found" : "Keine Filter gefunden.", + "no-filter-text" : "Kein Filter angegeben", + "add-filter-prompt" : "Bitte Filter hinzufügen", + "no-filter-matching" : "'{{filter}}' nicht gefunden.", + "create-new-filter" : "Neuen Filter erstellen!", + "create-new" : "Neu erstellen", + "filter-required" : "Filter ist erforderlich.", + "operation" : { + "operation" : "Operation", + "equal" : "gleich", + "not-equal" : "ungleich", + "starts-with" : "beginnt mit", + "ends-with" : "endet mit", + "contains" : "enthält", + "not-contains" : "enthält nicht", + "greater" : "größer als", + "less" : "kleiner als", + "greater-or-equal" : "größer oder gleich", + "less-or-equal" : "kleiner oder gleich", + "and" : "und", + "or" : "oder", + "in" : "in", + "not-in" : "nicht in" + }, + "ignore-case" : "Groß-/Kleinschreibung ignorieren", + "value" : "Wert", + "remove-filter" : "Filter entfernen", + "duplicate-filter-action" : "Filter duplizieren", + "preview" : "Filtervorschau", + "no-filters" : "Keine Filter konfiguriert", + "add-filter" : "Filter hinzufügen", + "add-complex-filter" : "Komplexen Filter hinzufügen", + "add-complex" : "Komplex hinzufügen", + "complex-filter" : "Komplexer Filter", + "edit-complex-filter" : "Komplexen Filter bearbeiten", + "edit-filter-user-params" : "Filter-Benutzerparameter bearbeiten", + "filter-user-params" : "Filterprädikat-Benutzerparameter", + "user-parameters" : "Benutzerparameter", + "display-label" : "Anzeigebezeichnung", + "order-priority" : "Priorität der Feldreihenfolge", + "key-filter" : "Schlüsselfilter", + "key-filters" : "Schlüsselfilter", + "key-name" : "Schlüsselname", + "key-name-required" : "Schlüsselname ist erforderlich.", + "key-type" : { + "key-type" : "Schlüsseltyp", + "attribute" : "Attribut", + "timeseries" : "Zeitreihe", + "entity-field" : "Entitätsfeld", + "constant" : "Konstante", + "client-attribute" : "Client-Attribut", + "server-attribute" : "Server-Attribut", + "shared-attribute" : "Geteiltes Attribut" + }, + "value-type" : { + "value-type" : "Werttyp", + "string" : "String", + "numeric" : "Numerisch", + "boolean" : "Boolesch", + "date-time" : "Datum und Zeit" + }, + "value-type-required" : "Werttyp des Schlüssels ist erforderlich.", + "key-value-type-change-title" : "Sind Sie sicher, dass Sie den Werttyp des Schlüssels ändern möchten?", + "key-value-type-change-message" : "Wenn Sie den neuen Werttyp bestätigen, werden alle eingegebenen Schlüsselfilter entfernt.", + "no-key-filters" : "Keine Schlüsselfilter konfiguriert", + "add-key-filter" : "Schlüsselfilter hinzufügen", + "remove-key-filter" : "Schlüsselfilter entfernen", + "edit-key-filter" : "Schlüsselfilter bearbeiten", + "date" : "Datum", + "time" : "Zeit", + "current-tenant" : "Aktueller Mieter", + "current-customer" : "Aktueller Kunde", + "current-user" : "Aktueller Benutzer", + "current-device" : "Aktuelles Gerät", + "default-value" : "Standardwert", + "default-comma-separated-values" : "Standardwerte, komma-getrennt", + "dynamic-source-type" : "Dynamischer Quelltyp", + "dynamic-value" : "Dynamischer Wert", + "no-dynamic-value" : "Kein dynamischer Wert", + "source-attribute" : "Quellattribut", + "switch-to-dynamic-value" : "Auf dynamischen Wert umschalten", + "switch-to-default-value" : "Auf Standardwert umschalten", + "inherit-owner" : "Vom Eigentümer übernehmen", + "source-attribute-not-set" : "Falls Quellattribut nicht gesetzt ist" + }, + "fullscreen" : { + "expand" : "Auf Vollbildmodus erweitern", + "exit" : "Vollbildmodus verlassen", + "toggle" : "Vollbildmodus umschalten", + "fullscreen" : "Vollbild" + }, + "function" : { + "function" : "Funktion" + }, + "gateway" : { + "gateway-name" : "Gateway-Name", + "gateway-name-required" : "Gateway-Name ist erforderlich.", + "gateways" : "Gateways", + "create-new-gateway" : "Neues Gateway erstellen", + "create-new-gateway-text" : "Sind Sie sicher, dass Sie ein neues Gateway mit dem Namen '{{gatewayName}}' erstellen möchten?", + "launch-command" : "Startbefehl", + "no-gateway-found" : "Kein Gateway gefunden.", + "no-gateway-matching" : "'{{item}}' nicht gefunden." + }, + "grid" : { + "delete-item-title" : "Sind Sie sicher, dass Sie dieses Element löschen möchten?", + "delete-item-text" : "Seien Sie vorsichtig, nach der Bestätigung werden dieses Element und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-items-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Element} other {# Elemente} } löschen möchten?", + "delete-items-action-title" : "{ count, plural, =1 {1 Element} other {# Elemente} } löschen", + "delete-items-text" : "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Elemente und alle zugehörigen Daten entfernt.", + "add-item-text" : "Neues Element hinzufügen", + "no-items-text" : "Keine Elemente gefunden", + "item-details" : "Elementdetails", + "delete-item" : "Element löschen", + "delete-items" : "Elemente löschen", + "scroll-to-top" : "Nach oben scrollen" + }, + "help" : { + "goto-help-page" : "Zur Hilfeseite gehen", + "show-help" : "Hilfe anzeigen" + }, + "home" : { + "home" : "Startseite", + "profile" : "Profil", + "logout" : "Abmelden", + "menu" : "Menü", + "avatar" : "Avatar", + "open-user-menu" : "Benutzermenü öffnen" + }, + "file-input" : { + "browse-file" : "Datei durchsuchen", + "browse-files" : "Dateien durchsuchen" + }, + "image" : { + "gallery" : "Bildergalerie", + "search" : "Bild suchen", + "selected-images" : "{ count, plural, =1 {1 Bild} other {# Bilder} } ausgewählt", + "created-time" : "Erstellungszeit", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "resolution" : "Auflösung", + "size" : "Größe", + "system" : "System", + "download-image" : "Bild herunterladen", + "export-image" : "Bild als JSON exportieren", + "import-image" : "Bild aus JSON importieren", + "upload-image" : "Bild hochladen", + "edit-image" : "Bild bearbeiten", + "image-details" : "Bilddetails", + "no-images" : "Keine Bilder gefunden", + "delete-image" : "Bild löschen", + "delete-image-title" : "Sind Sie sicher, dass Sie das Bild '{{imageTitle}}' löschen möchten?", + "delete-image-text" : "Seien Sie vorsichtig, nach der Bestätigung kann das Bild nicht wiederhergestellt werden.", + "delete-images-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Bild} other {# Bilder} } löschen möchten?", + "delete-images-text" : "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Bilder und deren zugehörige Daten gelöscht.", + "list-mode" : "Listenansicht", + "grid-mode" : "Rasteransicht", + "image-preview" : "Bildvorschau", + "update-image" : "Bild aktualisieren", + "export-failed-error" : "Bild konnte nicht exportiert werden: {{error}}", + "image-json-file" : "Bild-JSON-Datei", + "invalid-image-json-file-error" : "Bildimport aus JSON fehlgeschlagen: Ungültige Bild-JSON-Struktur.", + "image-is-in-use" : "Bild wird von anderen Entitäten verwendet", + "images-are-in-use" : "Bilder werden von anderen Entitäten verwendet", + "image-is-in-use-text" : "Das Bild '{{title}}' wurde nicht gelöscht, da es von den folgenden Entitäten verwendet wird:", + "images-are-in-use-text" : "Nicht alle Bilder wurden gelöscht, da sie von anderen Entitäten verwendet werden.
Sie können die referenzierten Entitäten anzeigen, indem Sie auf die Schaltfläche Referenzen in der entsprechenden Zeile klicken.
Wenn Sie diese Bilder dennoch löschen möchten, wählen Sie sie unten aus und klicken Sie auf die Schaltfläche Ausgewählte löschen.", + "delete-image-in-use-text" : "Wenn Sie das Bild trotzdem löschen möchten, klicken Sie auf die Schaltfläche Trotzdem löschen.", + "system-entities" : "Systementitäten:", + "entities" : "Entitäten:", + "references" : "Referenzen", + "include-system-images" : "Systembilder einbeziehen", + "clear-image" : "Bild entfernen", + "no-image" : "Kein Bild", + "no-image-selected" : "Kein Bild ausgewählt", + "browse-from-gallery" : "Aus Galerie auswählen", + "set-link" : "Link festlegen", + "image-link" : "Bildlink", + "link" : "Link", + "copy-image-link" : "Bildlink kopieren", + "embed-image" : "Bild einbetten", + "embed-to-html" : "In HTML einbetten", + "embed-to-html-hint" : "Diese Funktion macht den Link für jeden unautorisierten Benutzer verfügbar.", + "embed-to-html-text" : "Verwenden Sie den folgenden Code-Schnipsel, um ein Bild in komponentenbasierte HTMLs einzubetten.
Solche Komponenten umfassen HTML-Card-Widgets, Zellinhalt-Funktionen usw.", + "embed-to-angular-template" : "In Angular HTML-Template einbetten", + "embed-to-angular-template-text" : "Verwenden Sie den folgenden Code-Schnipsel, um ein Bild in das Angular HTML-Template einzubetten.
Solche Komponenten umfassen das Markdown-Widget, den HTML-Bereich im Widget-Editor, benutzerdefinierte Aktionen usw." + }, + "image-input" : { + "drop-images-or" : "Ziehen Sie Bilder hierher oder", + "drag-and-drop" : "Drag & Drop", + "or" : "oder", + "browse" : "Durchsuchen", + "no-images" : "Keine Bilder ausgewählt", + "images" : "Bilder" + }, + "import" : { + "no-file" : "Keine Datei ausgewählt", + "drop-file" : "JSON-Datei ablegen oder klicken, um eine Datei auszuwählen.", + "drop-json-file-or" : "Ziehen Sie eine JSON-Datei hierher oder", + "drop-file-csv" : "CSV-Datei ablegen oder klicken, um eine Datei auszuwählen.", + "drop-file-csv-or" : "Ziehen Sie eine CSV-Datei hierher oder", + "column-value" : "Wert", + "column-title" : "Titel", + "column-example" : "Beispielwert-Daten", + "column-key" : "Attribut-/Telemetrieschlüssel", + "credentials" : "Zugangsdaten", + "csv-delimiter" : "CSV-Trennzeichen", + "csv-first-line-header" : "Erste Zeile enthält Spaltennamen", + "csv-update-data" : "Attribute/Telemetrie aktualisieren", + "details" : "Details", + "import-csv-number-columns-error" : "Eine Datei sollte mindestens zwei Spalten enthalten", + "import-csv-invalid-format-error" : "Ungültiges Dateiformat. Zeile: '{{line}}'", + "column-type" : { + "name" : "Name", + "type" : "Typ", + "label" : "Bezeichnung", + "column-type" : "Spaltentyp", + "client-attribute" : "Client-Attribut", + "shared-attribute" : "Geteiltes Attribut", + "server-attribute" : "Server-Attribut", + "timeseries" : "Zeitreihe", + "entity-field" : "Entitätsfeld", + "access-token" : "Zugangstoken", + "x509" : "X.509", + "mqtt" : { + "client-id" : "MQTT-Client-ID", + "user-name" : "MQTT-Benutzername", + "password" : "MQTT-Passwort" + }, + "lwm2m" : { + "client-endpoint" : "LwM2M-Endpunkt-Clientname", + "security-config-mode" : "LwM2M-Sicherheitskonfigurationsmodus", + "client-identity" : "LwM2M-Client-Identität", + "client-key" : "LwM2M-Client-Schlüssel", + "client-cert" : "LwM2M-Client-öffentlicher Schlüssel", + "bootstrap-server-security-mode" : "LwM2M-Bootstrap-Server-Sicherheitsmodus", + "bootstrap-server-secret-key" : "LwM2M-Bootstrap-Server-Geheimschlüssel", + "bootstrap-server-public-key-id" : "LwM2M-Bootstrap-Server-öffentlicher Schlüssel oder ID", + "lwm2m-server-security-mode" : "LwM2M-Server-Sicherheitsmodus", + "lwm2m-server-secret-key" : "LwM2M-Server-Geheimschlüssel", + "lwm2m-server-public-key-id" : "LwM2M-Server-öffentlicher Schlüssel oder ID" + }, + "snmp" : { + "host" : "SNMP-Host", + "port" : "SNMP-Port", + "version" : "SNMP-Version (v1, v2c oder v3)", + "community-string" : "SNMP-Community-String" + }, + "isgateway" : "Ist Gateway", + "activity-time-from-gateway-device" : "Aktivitätszeit vom Gateway-Gerät", + "description" : "Beschreibung", + "routing-key" : "Edge-Schlüssel", + "secret" : "Edge-Geheimnis" + }, + "stepper-text" : { + "select-file" : "Datei auswählen", + "configuration" : "Konfiguration importieren", + "column-type" : "Spaltentyp auswählen", + "creat-entities" : "Neue Entitäten erstellen" + }, + "message" : { + "create-entities" : "{{count}} neue Entitäten wurden erfolgreich erstellt.", + "update-entities" : "{{count}} Entitäten wurden erfolgreich aktualisiert.", + "error-entities" : "Beim Erstellen von {{count}} Entitäten ist ein Fehler aufgetreten." + } + }, + "scada" : { + "symbols" : "SCADA-Symbole", + "search" : "Symbol suchen", + "selected-symbols" : "{ count, plural, =1 {1 Symbol} other {# Symbole} } ausgewählt", + "download-symbol" : "SCADA-Symbol herunterladen", + "export-symbol" : "SCADA-Symbol als JSON exportieren", + "import-symbol" : "SCADA-Symbol aus JSON importieren", + "upload-symbol" : "SCADA-Symbol hochladen", + "update-symbol" : "SCADA-Symbol aktualisieren", + "edit-symbol" : "SCADA-Symbol bearbeiten", + "symbol-details" : "SCADA-Symboldetails", + "mode-svg" : "SVG", + "mode-xml" : "XML", + "no-symbols" : "Keine Symbole gefunden", + "show-hidden-elements" : "Versteckte Elemente anzeigen", + "hide-hidden-elements" : "Versteckte Elemente ausblenden", + "delete-symbol" : "SCADA-Symbol löschen", + "delete-symbol-title" : "Möchten Sie das SCADA-Symbol '{{imageTitle}}' wirklich löschen?", + "delete-symbol-text" : "Vorsicht, nach der Bestätigung ist das SCADA-Symbol nicht wiederherstellbar.", + "delete-symbols-title" : "Möchten Sie wirklich { count, plural, =1 {1 SCADA-Symbol} other {# SCADA-Symbole} } löschen?", + "delete-symbols-text" : "Vorsicht, nach der Bestätigung werden alle ausgewählten SCADA-Symbole gelöscht und alle zugehörigen Daten sind nicht wiederherstellbar.", + "include-system-symbols" : "Systemsymbole einbeziehen", + "symbol-preview" : "Symbolvorschau", + "general" : "Allgemein", + "tags" : "Tags", + "properties" : "Eigenschaften", + "title" : "Titel", + "description" : "Beschreibung", + "search-tags" : "Tags durchsuchen", + "widget-size" : "Widget-Größe", + "cols" : "Spalten", + "rows" : "Zeilen", + "state-render-function" : "Status-Render-Funktion", + "preview" : "Vorschau", + "preview-widget-action-text" : "Widget-Aktion '{{type}}' erfolgreich ausgeführt!", + "no-symbol" : "Kein SCADA-Symbol", + "no-symbol-selected" : "Kein SCADA-Symbol ausgewählt", + "clear-symbol" : "SCADA-Symbol löschen", + "browse-symbol-from-gallery" : "SCADA-Symbol aus Galerie auswählen", + "zoom-in" : "Vergrößern", + "zoom-out" : "Verkleinern", + "create-widget" : "Widget erstellen", + "create-widget-from-symbol" : "Widget aus SCADA-Symbol erstellen", + "hidden" : "versteckt", + "tag" : { + "tag" : "Tag", + "on-click-action" : "Aktion bei Klick", + "no-tags" : "Keine Tags konfiguriert", + "delete-tag-text" : "Möchten Sie wirklich den Tag
{{tag}} vom {{elementType}}-Element löschen?", + "update-tag" : "Tag aktualisieren", + "enter-tag" : "Tag eingeben", + "tag-settings" : "Tag-Einstellungen", + "remove-tag" : "Tag entfernen", + "add-tag" : "Tag hinzufügen" + }, + "behavior" : { + "behavior" : "Verhalten", + "id" : "ID", + "name" : "Name", + "type" : "Typ", + "no-behaviors" : "Keine Verhaltensweisen konfiguriert", + "add-behavior" : "Verhalten hinzufügen", + "type-action" : "Aktion", + "type-value" : "Wert", + "type-widget-action" : "Widget-Aktion", + "behavior-settings" : "Verhaltenseinstellungen", + "remove-behavior" : "Verhalten entfernen", + "hint" : "Hinweis", + "group-title" : "Gruppentitel", + "value-type" : "Wertetyp", + "default-value" : "Standardwert", + "true-label" : "Bezeichnung für Wahr", + "false-label" : "Bezeichnung für Falsch", + "state-label" : "Statusbezeichnung", + "default-payload" : "Standard-Nutzlast", + "not-unique-behavior-ids-error" : "Verhaltens-IDs müssen eindeutig sein!", + "default-settings" : "Standardeinstellungen" + }, + "symbol" : { + "symbol" : "SCADA-Symbol", + "fluid-presence" : "Flüssigkeitspräsenz", + "fluid-presence-hint" : "Gibt an, ob Flüssigkeit im Rohr vorhanden ist.", + "fluid-present" : "Flüssigkeit vorhanden", + "present" : "Vorhanden", + "absent" : "Nicht vorhanden", + "flow-presence" : "Flusspräsenz", + "flow-presence-hint" : "Gibt an, ob Flüssigkeit im Rohr fließt.", + "flow-present" : "Fluss vorhanden", + "flow-direction" : "Flussrichtung", + "flow-direction-hint" : "Gibt die Flussrichtung der Flüssigkeit an.", + "forward" : "Vorwärts", + "reverse" : "Rückwärts", + "flow-animation-speed" : "Flussanimationsgeschwindigkeit", + "flow-animation-speed-hint" : "Gleitkommazahl, die die Animationsgeschwindigkeit des Flusses angibt. 1 - normale Geschwindigkeit, 0 - keine Animation, < 1 - langsamere Animation, > 1 - schnellere Animation.", + "leak" : "Leck", + "leak-hint" : "Gibt an, ob ein Leck im Rohr vorhanden ist.", + "leak-present" : "Leck vorhanden", + "fluid-color" : "Flüssigkeitsfarbe", + "pipe-color" : "Rohrfarbe", + "horizontal-pipe" : "Horizontales Rohr", + "vertical-pipe" : "Vertikales Rohr", + "horizontal-fluid-color" : "Horizontale Flüssigkeitsfarbe", + "vertical-fluid-color" : "Vertikale Flüssigkeitsfarbe", + "left-pipe" : "Linkes Rohr", + "right-pipe" : "Rechtes Rohr", + "top-pipe" : "Oberes Rohr", + "bottom-pipe" : "Unteres Rohr", + "left-fluid-color" : "Linke Flüssigkeitsfarbe", + "right-fluid-color" : "Rechte Flüssigkeitsfarbe", + "top-fluid-color" : "Obere Flüssigkeitsfarbe", + "bottom-fluid-color" : "Untere Flüssigkeitsfarbe", + "display" : "Anzeige", + "display-format" : "Anzeigeformat", + "value" : "Wert", + "decimals" : "Dezimalstellen", + "units" : "Einheiten", + "flow-meter-value-hint" : "Gleitkommazahl auf der Durchflussanzeige", + "value-hint" : "Gleitkommazahl, die den aktuellen Wert anzeigt", + "running" : "In Betrieb", + "running-hint" : "Gibt an, ob die Komponente im Betriebszustand ist.", + "warning-state" : "Warnzustand", + "warning" : "Warnung", + "warning-click" : "Warnung Klick", + "warning-state-hint" : "Gibt an, ob die Komponente im Warnzustand ist.", + "critical-state" : "Kritischer Zustand", + "critical" : "Kritisch", + "critical-click" : "Kritisch Klick", + "critical-state-hint" : "Gibt an, ob die Komponente im kritischen Zustand ist.", + "critical-state-animation" : "Animation im kritischen Zustand", + "critical-state-animation-hint" : "Ob die blinkende Animation im kritischen Zustand aktiviert werden soll.", + "warning-critical-state-animation" : "Animation im Warn-/Kritischen Zustand", + "warning-critical-state-animation-hint" : "Ob die blinkende Animation im Warn- oder kritischen Zustand aktiviert werden soll.", + "animation" : "Animation", + "broken" : "Defekt", + "broken-hint" : "Gibt an, ob die Komponente defekt ist.", + "on-display-click" : "Bei Klick auf Anzeige", + "on-display-click-hint" : "Aktion, die beim Klicken auf die Anzeige ausgelöst wird.", + "pipe" : "Rohr", + "default-border-color" : "Standard-Rahmenfarbe", + "active-border-color" : "Aktive Rahmenfarbe", + "warning-border-color" : "Warnrahmenfarbe", + "critical-border-color" : "Kritische Rahmenfarbe", + "background-color" : "Hintergrundfarbe", + "rotation-animation-speed" : "Rotationsanimationsgeschwindigkeit", + "rotation-animation-speed-hint" : "Gleitkommazahl zur Angabe der Rotationsanimationsgeschwindigkeit. 1 - normale Geschwindigkeit, 0 - keine Animation, < 1 - langsamere Animation, > 1 - schnellere Animation.", + "on-click" : "Bei Klick", + "on-click-hint" : "Aktion, die beim Klicken auf die Komponente ausgeführt wird.", + "connectors-positions" : "Positionen der Anschlüsse", + "right-connector" : "Rechter Anschluss", + "right-top-connector" : "Rechter oberer Anschluss", + "right-bottom-connector" : "Rechter unterer Anschluss", + "left-connector" : "Linker Anschluss", + "left-top-connector" : "Linker oberer Anschluss", + "left-bottom-connector" : "Linker unterer Anschluss", + "top-left-connector" : "Oberer linker Anschluss", + "top-right-connector" : "Oberer rechter Anschluss", + "top-connector" : "Oberer Anschluss", + "bottom-connector" : "Unterer Anschluss", + "running-color" : "Farbe im Betriebszustand", + "stopped-color" : "Farbe im Stoppzustand", + "stopped" : "Gestoppt", + "warning-color" : "Warnfarbe", + "critical-color" : "Kritische Farbe", + "opened" : "Geöffnet", + "opened-hint" : "Gibt an, ob sich die Komponente im geöffneten Zustand befindet.", + "open" : "Öffnen", + "open-hint" : "Aktion, die beim Öffnen der Komponente ausgeführt wird.", + "close" : "Schließen", + "close-hint" : "Aktion, die beim Schließen der Komponente ausgeführt wird.", + "close-state-animation" : "Animation im geschlossenen Zustand", + "close-state-animation-hint" : "Ob eine Blinkanimation im geschlossenen Zustand aktiviert werden soll.", + "opened-color" : "Geöffnete Farbe", + "closed-color" : "Geschlossene Farbe", + "opened-rotation-angle" : "Drehwinkel geöffnet", + "closed-rotation-angle" : "Drehwinkel geschlossen", + "tank-capacity" : "Tankkapazität", + "tank-capacity-hint" : "Gleitkommazahl zur Angabe der Gesamtkapazität des Tanks.", + "current-volume" : "Aktuelles Volumen", + "current-volume-hint" : "Gleitkommazahl zur Angabe des aktuell belegten Volumens.", + "tank-color" : "Tankfarbe", + "value-box" : "Wertbox", + "value-text" : "Wertetext", + "scale" : "Skala", + "transparent-mode" : "Transparenter Modus", + "major-ticks" : "Hauptmarkierungen", + "intervals" : "Intervalle", + "major-ticks-color" : "Farbe der Hauptmarkierungen", + "normal" : "Normal", + "minor-ticks" : "Nebenmarkierungen", + "minor-ticks-color" : "Farbe der Nebenmarkierungen", + "temperature" : "Temperatur", + "temperature-hint" : "Gleitkommazahl zur Anzeige der aktuellen Temperatur.", + "update-temperature" : "Temperatur aktualisieren", + "update-temperature-hint" : "Aktion, die beim Klicken zur Änderung der Temperatur ausgeführt wird.", + "run" : "Starten", + "run-hint" : "Aktion, die beim Starten der Komponente ausgeführt wird.", + "stop" : "Stoppen", + "stop-hint" : "Aktion, die beim Stoppen der Komponente ausgeführt wird.", + "temperature-step" : "Temperaturschritt", + "heat-pump-color" : "Farbe der Wärmepumpe", + "power-button-background" : "Hintergrund des Netzschalters", + "value-box-background" : "Hintergrund der Wertbox", + "value-units" : "Werteinheiten", + "filtration-mode" : "Filtrationsmodus", + "filtration-mode-hint" : "Ganzzahlige Angabe des aktuellen Filtrationsmodus.", + "filtration-mode-update" : "Filtrationsmodus-Aktualisierung", + "filtration-mode-update-hint" : "Aktion, die beim Ändern des aktuellen Filtrationsmodus ausgeführt wird.", + "filter-mode" : "Filtern", + "waste-mode" : "Abfall", + "backwash-mode" : "Rückspülen", + "recirculate-mode" : "Rezirkulieren", + "rinse-mode" : "Spülen", + "closed-mode" : "Geschlossen", + "sand-filter-color" : "Sandfilter-Farbe", + "mode-box-background" : "Modus-Box-Hintergrund", + "border-color" : "Rahmenfarbe", + "label-color" : "Beschriftungsfarbe", + "water-leak-hint" : "Gibt an, ob ein Leck vorliegt.", + "default-color" : "Standardfarbe", + "leak-color" : "Leck-Farbe", + "full-value" : "Vollwert", + "full-value-hint" : "Gleitkommazahl, die den Vollwert angibt.", + "label" : "Beschriftung", + "icon" : "Symbol", + "button-color" : "Schaltflächenfarbe", + "on-label" : "Text für 'Ein'", + "off-label" : "Text für 'Aus'", + "arrow-presence" : "Pfeil vorhanden", + "arrow-presence-hint" : "Gibt an, ob ein Pfeil im Anschluss vorhanden ist.", + "arrow-present" : "Pfeil vorhanden", + "arrow-direction" : "Pfeil-/Animationsrichtung", + "arrow-direction-hint" : "Gibt die Flussrichtung an.", + "flow-animation" : "Flussanimation", + "flow-animation-hint" : "Gibt an, ob eine Animation im Anschluss vorhanden ist.", + "flow" : "Fluss", + "flow-line" : "Linie", + "flow-line-style" : "Linienstil", + "flow-style-hint" : "Setzen Sie die Werte für Strich und Lücke so, dass ihre Summe ohne Rest durch 100 teilbar ist, um eine perfekte Animationssynchronisation zu erreichen.", + "flow-dash-cap" : "Fluss-Strich-Endung", + "dash-cap-butt" : "Abgeschnitten", + "dash-cap-round" : "Rund", + "dash-cap-square" : "Quadratisch", + "dash" : "Strich", + "gap" : "Abstand", + "main-line" : "Hauptlinie", + "line" : "Linie", + "line-color" : "Linienfarbe", + "arrow-color" : "Pfeilfarbe", + "target-value" : "Zielwert", + "target-value-hint" : "Gibt den Zielpunkt auf der Skala an.", + "min-max-value" : "Min- und Max-Wert", + "min-value" : "Min", + "max-value" : "Max", + "progress-bar" : "Fortschrittsbalken", + "progress-arrow" : "Fortschrittspfeil", + "warning-scale-color" : "Warnskalenfarbe", + "critical-scale-color" : "Kritische Skalenfarbe", + "scale-color" : "Skalenfarbe", + "target" : "Ziel", + "high-warning-state" : "Zustand hohe Warnung", + "show-high-warning-scale" : "Hohe Warnskala anzeigen", + "high-warning-scale" : "Hohe Warnskala", + "high-warning-state-hint" : "Gleitkommazahl gibt den Bereich der hohen Warnung bis zu kritischem oder Max-Wert an.", + "low-warning-state" : "Zustand niedrige Warnung", + "show-low-warning-scale" : "Niedrige Warnskala anzeigen", + "low-warning-scale" : "Niedrige Warnskala", + "low-warning-state-hint" : "Gleitkommazahl gibt den Bereich der niedrigen Warnung bis zu kritischem oder Min-Wert an.", + "high-critical-state" : "Zustand hohe Kritikalität", + "show-high-critical-scale" : "Hohe kritische Skala anzeigen", + "high-critical-scale" : "Hohe kritische Skala", + "high-critical-state-hint" : "Gleitkommazahl gibt den kritischen Bereich bis zum Max-Wert an.", + "low-critical-state" : "Zustand niedrige Kritikalität", + "show-low-critical-scale" : "Niedrige kritische Skala anzeigen", + "low-critical-scale" : "Niedrige kritische Skala", + "low-critical-state-hint" : "Gleitkommazahl gibt den kritischen Bereich bis zum Min-Wert an.", + "filter-color" : "Filterfarbe", + "colors" : "Farben", + "indicator-colors" : "Indikatorfarben", + "enabled" : "Aktiviert", + "disabled" : "Deaktiviert", + "on" : "EIN", + "off" : "AUS", + "on-off-state" : "Ein-/Aus-Zustand", + "on-off-state-hint" : "Gibt an, ob die Komponente im Ein- oder Aus-Zustand ist.", + "on-update-state" : "Zustand zu Ein aktualisieren", + "on-update-state-hint" : "Aktion, die ausgeführt wird, wenn der Benutzer den Zustand auf Ein aktualisiert.", + "off-update-state" : "Zustand zu Aus aktualisieren", + "off-update-state-hint" : "Aktion, die ausgeführt wird, wenn der Benutzer den Zustand auf Aus aktualisiert.", + "voltage" : "Spannung", + "input-voltage" : "Eingangsspannung", + "input-voltage-hint" : "Gleitkommazahl gibt den Wert der Eingangsspannung an.", + "output-voltage" : "Ausgangsspannung", + "output-voltage-hint" : "Gleitkommazahl gibt den Wert der Ausgangsspannung an.", + "first-phase-voltage" : "Spannung der ersten Phase", + "second-phase-voltage" : "Spannung der zweiten Phase", + "third-phase-voltage" : "Spannung der dritten Phase", + "phase-voltage-hint" : "Gleitkommazahl gibt den Spannungswert der aktuellen Phase an", + "voltage-hint" : "Gleitkommazahl gibt die aktuelle Spannung an", + "current-voltage-color" : "Farbe der aktuellen Spannung", + "phase-indicator-color" : "Phasenanzeigerfarbe", + "measured" : "Gemessen", + "measured-hint" : "Gleitkommazahl zeigt den Energieverbrauch in Kilowattstunden an", + "day-rate" : "Tagestarif", + "night-rate" : "Nachttarif", + "off-peak-rate" : "Niedrigtarif", + "peak-rate" : "Spitzentarif", + "export-rate" : "Exporttarif", + "operating-mode" : "Betriebsmodus", + "bypass-mode" : "Bypass", + "operating-mode-hint" : "Ganzzahliger Wert gibt den aktuellen Betriebsmodus an (0 - AUS, 1 - EIN, 2 - BYPASS)", + "connected" : "Verbunden", + "connected-hint" : "Gibt an, ob sich die Komponente im verbundenen Zustand befindet.", + "disconnected" : "Getrennt", + "indicator" : "Indikator", + "operation-mode" : "Betriebsmodus", + "operation-mode-hint" : "Gibt an, ob sich der Wechselrichter im Netz- oder Wechselrichtermodus befindet.", + "operation-mode-indicators-color" : "Farbe der Betriebsmodus-Indikatoren", + "mains-on-mode" : "Netz eingeschaltet", + "inverter-on-mode" : "Wechselrichter eingeschaltet", + "charging-mode" : "Lademodus", + "charging-mode-hint" : "Ganzzahliger Wert gibt den aktuellen Lademodus an (1 - Bulk, 2 - Absorption, 3 - Float)", + "charging-mode-indicators-color" : "Farbe der Lademodus-Indikatoren", + "inverter-faults" : "Fehler", + "inverter-fault-indicators-color" : "Farbe der Fehlerindikatoren", + "overload-fault" : "Überlastung", + "overload-fault-hint" : "Gibt an, ob sich der Wechselrichter in einem Überlastzustand befindet.", + "low-battery-fault" : "Niedriger Batteriestand", + "low-battery-fault-hint" : "Gibt an, ob die Batterie stark entladen ist.", + "temperature-fault" : "Temperatur", + "temperature-fault-hint" : "Gibt an, ob eine hohe Temperatur im Wechselrichter vorliegt.", + "triangle" : "Dreieck", + "socket" : "Steckdose", + "left-button" : "Linke Taste", + "right-button" : "Rechte Taste", + "alarm-colors" : "Alarmfarben", + "hook-color" : "Hakenfarbe" + } + }, + "item" : { + "selected" : "Ausgewählt" + }, + "js-func" : { + "no-return-error" : "Funktion muss einen Wert zurückgeben!", + "return-type-mismatch" : "Funktion muss einen Wert vom Typ '{{type}}' zurückgeben!", + "tidy" : "Ordentlich", + "mini" : "Mini", + "modules" : "Module", + "remove-module" : "Modul entfernen", + "no-modules" : "Keine Module konfiguriert", + "add-module" : "Modul hinzufügen", + "module-alias" : "Alias", + "invalid-module-alias-name" : "Ungültiger Alias-Name", + "module-resource" : "JS-Modulressource", + "not-unique-module-aliases-error" : "Modul-Aliase müssen eindeutig sein!", + "show-module-info" : "Modulinformationen anzeigen", + "show-module-source-code" : "Modul-Quellcode anzeigen", + "module-members" : "Modulmitglieder", + "module-no-members" : "Modul hat keine exportierten Mitglieder", + "module-load-error" : "Fehler beim Laden des Moduls", + "source-code" : "Quellcode", + "source-code-load-error" : "Fehler beim Laden des Quellcodes", + "no-js-module-text" : "Keine JS-Module gefunden", + "no-js-module-matching" : "Keine JS-Module gefunden, die mit '{{module}}' übereinstimmen." + }, + "key-val" : { + "key" : "Schlüssel", + "value" : "Wert", + "remove-entry" : "Eintrag entfernen", + "add-entry" : "Eintrag hinzufügen", + "no-data" : "Keine Einträge" + }, + "layout" : { + "layout" : "Layout", + "layouts" : "Layouts", + "manage" : "Layouts verwalten", + "settings" : "Layouteinstellungen", + "color" : "Farbe", + "main" : "Hauptbereich", + "right" : "Rechts", + "left" : "Links", + "select" : "Ziel-Layout auswählen", + "percentage-width" : "Prozentuale Breite (%)", + "fixed-width" : "Feste Breite (px)", + "left-width" : "Linke Spalte (%)", + "right-width" : "Rechte Spalte (%)", + "pick-fixed-side" : "Feste Seite:", + "layout-fixed-width" : "Feste Breite (px)", + "value-min-error" : "Wert muss größer als {{min}}{{unit}} sein", + "value-max-error" : "Wert muss kleiner als {{max}}{{unit}} sein", + "layout-fixed-width-required" : "Feste Breite ist erforderlich", + "right-width-percentage-required" : "Prozentangabe für rechte Seite ist erforderlich", + "left-width-percentage-required" : "Prozentangabe für linke Seite ist erforderlich", + "divider" : "Trennlinie", + "right-side" : "Layout rechte Seite", + "left-side" : "Layout linke Seite", + "add-new-breakpoint" : "Neuen Breakpoint hinzufügen", + "breakpoint" : "Breakpoint", + "breakpoints" : "Breakpoints", + "copy-from" : "Kopieren von", + "size" : "Größe", + "delete-breakpoint-title" : "Möchten Sie den Breakpoint '{{name}}' wirklich löschen?", + "delete-breakpoint-text" : "Bitte beachten Sie: Nach der Bestätigung ist der Breakpoint nicht wiederherstellbar, und die Einstellungen werden auf den Standard-Breakpoint zurückgesetzt." + }, + "legend" : { + "direction" : "Richtung", + "position" : "Position", + "show-values" : "Werte anzeigen", + "min-option" : "Min", + "max-option" : "Max", + "average-option" : "Durchschnitt", + "total-option" : "Gesamt", + "latest-option" : "Letzter", + "sort-legend" : "Daten in Legende sortieren", + "show-max" : "Maximalwert anzeigen", + "show-min" : "Minimalwert anzeigen", + "show-avg" : "Durchschnitt anzeigen", + "show-total" : "Gesamt anzeigen", + "show-latest" : "Letzten Wert anzeigen", + "settings" : "Legendeneinstellungen", + "min" : "min", + "max" : "max", + "avg" : "avg", + "total" : "gesamt", + "latest" : "letzter", + "Min" : "Min", + "Max" : "Max", + "Avg" : "Durchschn.", + "Total" : "Gesamt", + "Latest" : "Letzter", + "comparison-time-ago" : { + "previousInterval" : "(vorheriges Intervall)", + "customInterval" : "(benutzerdefiniertes Intervall)", + "days" : "(vor einem Tag)", + "weeks" : "(vor einer Woche)", + "months" : "(vor einem Monat)", + "years" : "(vor einem Jahr)" + }, + "column-title" : "Spaltentitel", + "label" : "Bezeichnung", + "value" : "Wert" + }, + "login" : { + "login" : "Anmelden", + "request-password-reset" : "Passwort-Zurücksetzung anfordern", + "reset-password" : "Passwort zurücksetzen", + "create-password" : "Passwort erstellen", + "two-factor-authentication" : "Zwei-Faktor-Authentifizierung", + "passwords-mismatch-error" : "Die eingegebenen Passwörter müssen übereinstimmen!", + "password-again" : "Passwort wiederholen", + "sign-in" : "Bitte anmelden", + "username" : "Benutzername (E-Mail)", + "remember-me" : "Angemeldet bleiben", + "forgot-password" : "Passwort vergessen?", + "password-reset" : "Passwort zurücksetzen", + "expired-password-reset-message" : "Ihre Anmeldedaten sind abgelaufen! Bitte erstellen Sie ein neues Passwort.", + "new-password" : "Neues Passwort", + "new-password-again" : "Neues Passwort bestätigen", + "password-link-sent-message" : "Link zum Zurücksetzen wurde gesendet", + "email" : "E-Mail", + "invalid-email-format" : "Ungültiges E-Mail-Format.", + "login-with" : "Anmelden mit {{name}}", + "or" : "oder", + "error" : "Anmeldefehler", + "verify-your-identity" : "Identität bestätigen", + "select-way-to-verify" : "Wählen Sie eine Methode zur Verifizierung", + "resend-code" : "Code erneut senden", + "resend-code-wait" : "Code erneut senden in { time, plural, =1 {1 Sekunde} other {# Sekunden} }", + "try-another-way" : "Andere Methode versuchen", + "totp-auth-description" : "Bitte geben Sie den Sicherheitscode aus Ihrer Authentifizierungs-App ein.", + "totp-auth-placeholder" : "Code", + "sms-auth-description" : "Ein Sicherheitscode wurde an Ihr Telefon unter {{contact}} gesendet.", + "sms-auth-placeholder" : "SMS-Code", + "email-auth-description" : "Ein Sicherheitscode wurde an Ihre E-Mail-Adresse unter {{contact}} gesendet.", + "email-auth-placeholder" : "E-Mail-Code", + "backup-code-auth-description" : "Bitte geben Sie einen Ihrer Backup-Codes ein.", + "backup-code-auth-placeholder" : "Backup-Code", + "activation-link-expired" : "Aktivierungslink ist abgelaufen", + "activation-link-expired-message" : "Der Link zur Aktivierung Ihres Profils ist abgelaufen. Sie können zur Anmeldeseite zurückkehren, um eine neue E-Mail zu erhalten.", + "reset-password-link-expired" : "Link zum Zurücksetzen des Passworts ist abgelaufen", + "reset-password-link-expired-message" : "Der Link zum Zurücksetzen Ihres Passworts ist abgelaufen. Sie können zur Anmeldeseite zurückkehren, um eine neue E-Mail zu erhalten." + }, + "mobile" : { + "add-application" : "Anwendung hinzufügen", + "app-id" : "App-ID", + "app-id-required" : "App-ID ist erforderlich", + "app-id-pattern" : "Ungültiges App-ID-Format", + "app-store-link" : "App Store-Link", + "app-store-link-required" : "App Store-Link ist erforderlich", + "application-details" : "Anwendungsdetails", + "application-package" : "Anwendungspaket", + "application-secret" : "Anwendungsgeheimnis", + "application-secret-required" : "Anwendungsgeheimnis ist erforderlich", + "application" : "Anwendung", + "applications" : "Anwendungen", + "copy-app-id" : "App-ID kopieren", + "copy-app-store-link" : "App Store-Link kopieren", + "copy-application-package" : "Anwendungspaket kopieren", + "copy-application-secret" : "Anwendungsgeheimnis kopieren", + "copy-google-play-link" : "Google Play-Link kopieren", + "copy-sha256-certificate-fingerprints" : "SHA256-Zertifikat-Fingerabdrücke kopieren", + "delete-application" : "Anwendung löschen", + "delete-application-button-text" : "Ich verstehe die Konsequenzen, Anwendung löschen", + "delete-application-text" : "Diese Aktion kann nicht rückgängig gemacht werden. Die Anwendung wird dauerhaft gelöscht.
Wenn Sie sie nicht dauerhaft löschen möchten, können Sie die Anwendung vorübergehend aussetzen.
Zum Löschen geben Sie bitte \"{{phrase}}\" zur Bestätigung ein.", + "delete-application-title-short" : "Sind Sie sicher, dass Sie die Anwendung '{{name}}' löschen möchten?", + "delete-application-text-short" : "Seien Sie vorsichtig, nach der Bestätigung werden die Anwendung und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-application-phrase" : "Anwendung löschen", + "delete-applications-bundle-text" : "Seien Sie vorsichtig, nach der Bestätigung werden das Mobile-Bundle und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-applications-bundle-title" : "Sind Sie sicher, dass Sie das Mobile-Bundle '{{bundleName}}' löschen möchten?", + "generate-application-secret" : "Anwendungsgeheimnis generieren", + "google-play-link" : "Google Play-Link", + "google-play-link-required" : "Google Play-Link ist erforderlich", + "latest-version" : "Neueste Version", + "min-version" : "Minimale Version", + "invalid-version-pattern" : "Ungültiges Versionsformat. Bitte verwenden Sie das Format: major.minor.patch (z. B. 1.0.0).", + "mobile-center" : "Mobiles Zentrum", + "mobile-package" : "Anwendungspaket", + "mobile-package-max-length" : "Anwendungspaket sollte weniger als 256 Zeichen enthalten", + "mobile-package-required" : "Anwendungspaket ist erforderlich.", + "mobile-package-pattern" : "Ungültiges Format des Anwendungspakets", + "no-application" : "Keine Anwendungen gefunden", + "no-bundles" : "Keine Bundles gefunden", + "platform-type" : "Plattformtyp", + "search-application" : "Anwendungen suchen", + "search-bundles" : "Bundles suchen", + "set" : "Festlegen", + "sha256-certificate-fingerprints" : "SHA256-Zertifikat-Fingerabdrücke", + "sha256-certificate-fingerprints-required" : "SHA256-Zertifikat-Fingerabdrücke sind erforderlich", + "sha256-certificate-fingerprints-pattern" : "Ungültiges SHA256-Zertifikat-Fingerabdruckformat", + "show-hidden-pages" : "Versteckte Seiten anzeigen", + "status" : "Status", + "status-type" : { + "deprecated" : "Veraltet", + "draft" : "Entwurf", + "published" : "Veröffentlicht", + "suspended" : "Ausgesetzt" + }, + "store-information" : "Shop-Informationen", + "version-information" : "Versionsinformationen", + "min-version-release-notes" : "Versionshinweise zur Mindestversion", + "latest-version-release-notes" : "Versionshinweise zur neuesten Version", + "bundle" : "Paket", + "bundles" : "Pakete", + "add-bundle" : "Paket hinzufügen", + "title" : "Titel", + "title-required" : "Titel ist erforderlich", + "title-cannot-contain-only-spaces" : "Titel darf nicht nur Leerzeichen enthalten", + "title-max-length" : "Titel sollte weniger als 256 Zeichen enthalten", + "oauth-clients" : "OAuth 2.0 Clients", + "android-app" : "Android-App", + "android-application" : "Android-Anwendung", + "ios-app" : "iOS-App", + "ios-application" : "iOS-Anwendung", + "invalid-store-link" : "Ungültiger Store-Link", + "enable-oauth" : "OAuth 2.0 aktivieren", + "enable-self-registration" : "Selbstregistrierung aktivieren", + "edit-bundle" : "Bundle bearbeiten", + "description" : "Beschreibung", + "basic-settings" : "Grundeinstellungen", + "no-application-matching" : "Keine Anwendung gefunden, die '{{entity}}' entspricht.", + "no-bundle-matching" : "Kein Bundle gefunden, das '{{entity}}' entspricht.", + "application-required" : "Anwendung ist erforderlich.", + "bundle-required" : "Bundle ist erforderlich.", + "no-application-text" : "Keine Anwendungen gefunden", + "no-bundle-text" : "Kein Bundle gefunden", + "layout" : "Layout", + "pages" : "Seiten", + "hide-all-pages" : "Alle Seiten ausblenden", + "reset-to-default-pages" : "Auf Standardseiten zurücksetzen", + "add-specific-page" : "Spezifische Seite hinzufügen", + "visible" : "Sichtbar", + "hidden" : "Ausgeblendet", + "reset-to-page-default" : "Seite auf Standard zurücksetzen", + "mobile-599" : "Mobilgerät (max. 599px)", + "tablet-959" : "Tablet (max. 959px)", + "max-element-number" : "Maximale Anzahl von Elementen", + "page-name" : "Seitenname", + "page-name-required" : "Seitenname ist erforderlich.", + "page-name-cannot-contain-only-spaces" : "Seitenname darf nicht nur Leerzeichen enthalten.", + "page-name-max-length" : "Seitenname sollte weniger als 256 Zeichen enthalten", + "page-type" : "Seitentyp", + "pages-types" : { + "dashboard" : "Dashboard", + "web-view" : "Webansicht", + "custom" : "Benutzerdefiniert" + }, + "url" : "URL", + "invalid-url-format" : "Ungültiges URL-Format", + "path" : "Pfad", + "invalid-path-format" : "Ungültiges Pfadformat", + "custom-page" : "Benutzerdefinierte Seite", + "edit-page" : "Seite bearbeiten", + "edit-custom-page" : "Benutzerdefinierte Seite bearbeiten", + "delete-page" : "Seite löschen", + "qr-code-widget" : "QR-Code-Widget", + "type-here" : "Hier eingeben", + "configuration-dialog" : "Konfigurationsdialog", + "configuration-app" : "Konfigurations-App", + "configuration-step" : { + "prepare-environment-title" : "Entwicklungsumgebung vorbereiten", + "prepare-environment-text" : "Für die ThingsBoard Flutter Mobile App wird das Flutter SDK benötigt. Folgen Sie den Anweisungen zur Einrichtung des Flutter SDK.", + "get-source-code-title" : "Quellcode der App erhalten", + "get-source-code-text" : "Sie können den Quellcode der ThingsBoard Flutter Mobile App durch Klonen des GitHub-Repositories erhalten:", + "configure-api-title" : "ThingsBoard API-Endpunkt konfigurieren", + "configure-api-text" : "Öffnen Sie das Projekt 'flutter_thingsboard_pe_app' in Ihrem Editor/IDE. Bearbeiten Sie:", + "configure-api-hint" : "Setzen Sie den Wert der Konstante 'thingsBoardApiEndpoint' entsprechend dem API-Endpunkt Ihrer ThingsBoard-Instanz. Verwenden Sie keine Hostnamen wie „localhost“ oder „127.0.0.1“.", + "run-app-title" : "App ausführen", + "run-app-text" : "Führen Sie die App wie in Ihrer IDE beschrieben aus.\nWenn Sie das Terminal verwenden, führen Sie folgenden Befehl aus:", + "more-information" : "Detaillierte Informationen finden Sie in unserer Einstiegsdokumentation.", + "getting-started" : "Erste Schritte", + "configure-package-title" : "Anwendungspaket konfigurieren", + "configure-package-text" : "Sie können das Anwendungspaket manuell ändern oder ein CLI-Tool eines Drittanbieters verwenden.", + "configure-package-text-install" : "Um das Rename CLI Tool zu installieren, führen Sie folgenden Befehl aus:", + "configure-package-run-commands" : "Führen Sie diese Befehle im Stammverzeichnis Ihres Projekts aus:" + } + }, + "notification" : { + "action-button" : "Aktionsschaltfläche", + "action-type" : "Aktionstyp", + "active" : "Aktiv", + "add-notification-recipients-group" : "Benachrichtigungs-Empfängergruppe hinzufügen", + "add-notification-template" : "Benachrichtigungsvorlage hinzufügen", + "add-recipient" : "Empfänger hinzufügen", + "add-recipients" : "Empfänger hinzufügen", + "add-rule" : "Regel hinzufügen", + "add-stage" : "Stufe hinzufügen", + "add-template" : "Vorlage hinzufügen", + "after" : "Nach", + "alarm-assignment-trigger-settings" : "Alarmazuweisung Auslöser-Einstellungen", + "alarm-comment-trigger-settings" : "Alarmkommentar Auslöser-Einstellungen", + "alarm-trigger-settings" : "Alarm Auslöser-Einstellungen", + "all" : "Alle", + "api-feature-hint" : "Wenn das Feld leer ist, wird der Auslöser auf alle API-Funktionen angewendet", + "api-usage-trigger-settings" : "API-Nutzungs-Auslöser-Einstellungen", + "new-platform-version-trigger-settings" : "Neue Plattformversion Auslöser-Einstellungen", + "rate-limits-trigger-settings" : "Auslöser für überschrittene Ratenlimits", + "task-processing-failure-trigger-settings" : "Aufgabenverarbeitungsfehler Auslöser-Einstellungen", + "at-least-one-should-be-selected" : "Mindestens eine Auswahl muss getroffen werden", + "basic-settings" : "Grundeinstellungen", + "button-text" : "Schaltflächentext", + "button-text-required" : "Schaltflächentext ist erforderlich", + "button-text-max-length" : "Schaltflächentext darf höchstens {{ length }} Zeichen lang sein", + "compose" : "Verfassen", + "conversation" : "Konversation", + "conversation-required" : "Konversation ist erforderlich", + "copy-notification-template" : "Benachrichtigungsvorlage kopieren", + "copy-rule" : "Regel kopieren", + "copy-template" : "Vorlage kopieren", + "create-new" : "Neu erstellen", + "created" : "Erstellt", + "customize-messages" : "Nachrichten anpassen", + "delete-notification-text" : "Seien Sie vorsichtig, nach der Bestätigung ist die Benachrichtigung nicht wiederherstellbar.", + "delete-notification-title" : "Sind Sie sicher, dass Sie die Benachrichtigung löschen möchten?", + "delete-notifications-text" : "Seien Sie vorsichtig, nach der Bestätigung sind die Benachrichtigungen nicht wiederherstellbar.", + "delete-notifications-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Benachrichtigung} other {# Benachrichtigungen} } löschen möchten?", + "delete-recipient-text" : "Seien Sie vorsichtig, nach der Bestätigung ist der Empfänger nicht wiederherstellbar.", + "delete-recipient-title" : "Sind Sie sicher, dass Sie den Empfänger '{{recipientName}}' löschen möchten?", + "delete-recipients-text" : "Seien Sie vorsichtig, nach der Bestätigung sind die Empfänger nicht wiederherstellbar.", + "delete-recipients-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Empfänger} other {# Empfänger} } löschen möchten?", + "delete-request-text" : "Seien Sie vorsichtig, nach der Bestätigung ist die Anfrage nicht wiederherstellbar.", + "delete-request-title" : "Sind Sie sicher, dass Sie die Anfrage löschen möchten?", + "delete-requests-text" : "Seien Sie vorsichtig, nach der Bestätigung sind die Anfragen nicht wiederherstellbar.", + "delete-requests-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Anfrage} other {# Anfragen} } löschen möchten?", + "delete-rule-text" : "Seien Sie vorsichtig, nach der Bestätigung ist die Regel nicht wiederherstellbar.", + "delete-rule-title" : "Sind Sie sicher, dass Sie die Regel '{{ruleName}}' löschen möchten?", + "delete-rules-text" : "Seien Sie vorsichtig, nach der Bestätigung sind die Regeln nicht wiederherstellbar.", + "delete-rules-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Regel} other {# Regeln} } löschen möchten?", + "delete-template-text" : "Seien Sie vorsichtig, nach der Bestätigung ist die Vorlage nicht wiederherstellbar.", + "delete-template-title" : "Sind Sie sicher, dass Sie die Vorlage '{{templateName}}' löschen möchten?", + "delete-templates-text" : "Seien Sie vorsichtig, nach der Bestätigung sind die Vorlagen nicht wiederherstellbar.", + "delete-templates-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Vorlage} other {# Vorlagen} } löschen möchten?", + "deleted" : "Gelöscht", + "delivery-method" : { + "delivery-method" : "Zustellmethode", + "email" : "E-Mail", + "email-preview" : "E-Mail-Benachrichtigungsvorschau", + "slack" : "Slack", + "slack-preview" : "Slack-Benachrichtigungsvorschau", + "microsoft-teams" : "Microsoft Teams", + "microsoft-teams-preview" : "Microsoft Teams-Benachrichtigungsvorschau", + "sms" : "SMS", + "sms-preview" : "SMS-Benachrichtigungsvorschau", + "web" : "Web", + "web-preview" : "Web-Benachrichtigungsvorschau", + "mobile-app" : "Mobile App", + "mobile-app-preview" : "Mobile App-Benachrichtigungsvorschau" + }, + "delivery-method-not-configure-click" : "Liefermethode ist nicht konfiguriert. Klicken Sie, um sie einzurichten.", + "delivery-method-not-configure-contact" : "Liefermethode ist nicht konfiguriert. Wenden Sie sich an Ihren Systemadministrator.", + "delivery-methods" : "Liefermöglichkeiten", + "description" : "Beschreibung", + "device-activity-trigger-settings" : "Einstellungen für Geräteaktivitätsauslöser", + "device-list-rule-hint" : "Wenn das Feld leer ist, wird der Auslöser auf alle Geräte angewendet", + "device-profiles-list-rule-hint" : "Wenn das Feld leer ist, wird der Auslöser auf alle Geräteprofile angewendet", + "disabled" : "Deaktiviert", + "edge-trigger-settings" : "Einstellungen für Edge-Auslöser", + "edge-list-rule-hint" : "Wenn das Feld leer ist, wird der Auslöser auf alle Edge-Instanzen angewendet", + "edit-notification-recipients-group" : "Empfängergruppe der Benachrichtigungen bearbeiten", + "edit-notification-template" : "Benachrichtigungsvorlage bearbeiten", + "edit-rule" : "Regel bearbeiten", + "edit-template" : "Vorlage bearbeiten", + "enabled" : "Aktiviert", + "entities-limit-trigger-settings" : "Einstellungen für Entitäten-Limit-Auslöser", + "entity-action-trigger-settings" : "Einstellungen für Entitätsaktionsauslöser", + "entity-type" : "Entitätstyp", + "escalation-chain" : "Eskalationskette", + "failed-send" : "Sendeausfälle", + "fails" : "{ count, plural, =1 {1 Fehler} other {# Fehler} }", + "filter" : "Filter", + "first-recipient" : "Erster Empfänger", + "inactive" : "Inaktiv", + "inbox" : "Posteingang", + "notification-inbox" : "Benachrichtigungen / Posteingang", + "input-field-support-templatization" : "Eingabefeld unterstützt Templatisierung.", + "input-fields-support-templatization" : "Eingabefelder unterstützen Templatisierung.", + "link" : "Link", + "link-required" : "Link ist erforderlich", + "link-type" : { + "dashboard" : "Dashboard öffnen", + "link" : "URL-Link öffnen" + }, + "loading-notifications" : "Benachrichtigungen werden geladen...", + "management" : "Benachrichtigungsverwaltung", + "mark-all-as-read" : "Alle als gelesen markieren", + "mark-as-read" : "Als gelesen markieren", + "message" : "Nachricht", + "message-required" : "Nachricht ist erforderlich", + "message-max-length" : "Nachricht sollte höchstens {{ length }} Zeichen lang sein", + "name" : "Name", + "name-required" : "Name ist erforderlich", + "new-notification" : "Neue Benachrichtigung", + "no-inbox-notification" : "Keine Benachrichtigung gefunden", + "no-notification-request" : "Keine Benachrichtigungsanfrage", + "no-notification-templates" : "Keine Benachrichtigungsvorlagen gefunden", + "no-notifications-yet" : "Noch keine Benachrichtigungen", + "no-recipients-notification" : "Keine Empfängerbenachrichtigung", + "no-recipients-matching" : "Keine passenden Empfänger für '{{entity}}' gefunden.", + "no-recipients-text" : "Kein Empfänger gefunden", + "no-rule" : "Keine Regel konfiguriert", + "no-rules-notification" : "Keine Regeln zur Benachrichtigung", + "no-severity-found" : "Keine Schwere gefunden", + "no-severity-matching" : "'{{severity}}' nicht gefunden.", + "no-template-matching" : "Keine Ressource passend zu '{{template}}' gefunden.", + "not-found-slack-recipient" : "Slack-Empfänger nicht gefunden", + "notification" : "Benachrichtigung", + "notification-center" : "Benachrichtigungszentrale", + "notification-tap-action" : "Benachrichtigungstipaktion", + "notification-tap-action-hint" : "Wenn nicht aktiviert, wird das Standard-Alarm-Dashboard verwendet", + "notify" : "benachrichtigen", + "notify-again" : "Erneut benachrichtigen", + "notify-alarm-action" : { + "acknowledged" : "Alarm bestätigt", + "assigned" : "Alarm zugewiesen", + "cleared" : "Alarm gelöscht", + "created" : "Alarm erstellt", + "severity-changed" : "Alarm-Schweregrad geändert", + "unassigned" : "Alarm nicht mehr zugewiesen" + }, + "notify-on" : "Benachrichtigen bei", + "notify-on-comment-update" : "Bei Kommentaraktualisierung benachrichtigen", + "notify-on-required" : "Benachrichtigungsauslöser ist erforderlich", + "notify-on-unassign" : "Bei Entzug der Zuweisung benachrichtigen", + "notify-only-user-comments" : "Nur Benutzerkommentare benachrichtigen", + "only-rule-chain-lifecycle-failures" : "Nur Lebenszyklusfehler von Regelketten", + "only-rule-node-lifecycle-failures" : "Nur Lebenszyklusfehler von Regelknoten", + "platform-users" : "Plattformbenutzer", + "rate-limits" : "Ratenbegrenzungen", + "rate-limits-hint" : "Wenn das Feld leer ist, wird der Auslöser auf alle Ratenbegrenzungen angewendet", + "recipient" : "Empfänger", + "recipient-group" : "Empfängergruppe", + "recipient-type" : { + "affected-tenant-administrators" : "Betroffene Mandantenadministratoren", + "affected-user" : "Betroffener Benutzer", + "all-users" : "Alle Benutzer", + "customer-users" : "Kundenbenutzer", + "system-administrators" : "Systemadministratoren", + "tenant-administrators" : "Mandantenadministratoren", + "user-filters" : "Benutzerfilter", + "user-list" : "Benutzerliste", + "users-entity-owner" : "Benutzer des Entitätseigentümers" + }, + "recipients" : "Empfänger", + "notification-recipient" : "Benachrichtigungsempfänger", + "notification-recipient-required" : "Benachrichtigungsempfänger ist erforderlich.", + "notification-recipients" : "Benachrichtigungen / Empfänger", + "recipients-count" : "{ count, plural, =1 {1 Empfänger} other {# Empfänger} }", + "recipients-required" : "Empfänger sind erforderlich", + "refresh-allow-delivery-method" : "Liefermethode aktualisieren", + "request-search" : "Suchanfrage", + "request-status" : { + "processing" : "In Bearbeitung", + "scheduled" : "Geplant", + "sent" : "Gesendet" + }, + "review" : "Überprüfen", + "rule" : "Regel", + "rule-chain-list-rule-hint" : "Wenn das Feld leer ist, wird der Auslöser auf alle Regelketten angewendet", + "rule-engine-events-trigger-settings" : "Einstellungen für Regelmaschinen-Ereignisauslöser", + "rule-engine-filter" : "Regelmaschinenfilter", + "rule-name" : "Regelname", + "rule-name-required" : "Name ist erforderlich", + "rule-disable" : "Benachrichtigungsregel deaktivieren", + "rule-enable" : "Benachrichtigungsregel aktivieren", + "rule-node-filter" : "Regelknotenfilter", + "rules" : "Regeln", + "notification-rules" : "Benachrichtigungen / Regeln", + "scheduler-later" : "Für später planen", + "search-notification" : "Benachrichtigungen suchen", + "search-recipients" : "Empfänger suchen", + "search-rules" : "Regeln suchen", + "search-templates" : "Vorlagen suchen", + "see-documentation" : "Dokumentation ansehen", + "selected-notifications" : "{ count, plural, =1 {1 Benachrichtigung} other {# Benachrichtigungen} } ausgewählt", + "selected-recipients" : "{ count, plural, =1 {1 Empfänger} other {# Empfänger} } ausgewählt", + "selected-requests" : "{ count, plural, =1 {1 Anfrage} other {# Anfragen} } ausgewählt", + "selected-rules" : "{ count, plural, =1 {1 Regel} other {# Regeln} } ausgewählt", + "selected-template" : "{ count, plural, =1 {1 Vorlage} other {# Vorlagen} } ausgewählt", + "send-notification" : "Benachrichtigung senden", + "sent" : "Gesendet", + "setup" : "Einrichtung", + "notification-sent" : "Benachrichtigungen / Gesendet", + "set-entity-from-notification" : "Entität aus Benachrichtigung in Dashboard-Status übernehmen", + "slack-chanel-type" : "Slack-Kanaltyp", + "slack-chanel-types" : { + "direct" : "Direktnachricht", + "private-channel" : "Privater Kanal", + "public-channel" : "Öffentlicher Kanal" + }, + "start-from-scratch" : "Von vorne beginnen", + "status" : "Status", + "stop-escalation-alarm-status-become" : "Eskalation beenden, wenn Alarmstatus wird zu:", + "subject" : "Betreff", + "subject-required" : "Betreff ist erforderlich", + "subject-max-length" : "Betreff sollte höchstens {{ length }} Zeichen lang sein", + "template" : "Vorlage", + "template-name" : "Vorlagenname", + "template-required" : "Vorlage ist erforderlich", + "template-type" : { + "alarm" : "Alarm", + "alarm-assignment" : "Alarmzuweisung", + "alarm-comment" : "Alarmkommentar", + "api-usage-limit" : "API-Nutzungslimit", + "device-activity" : "Geräteaktivität", + "entities-limit" : "Entitätenlimit", + "entity-action" : "Entitätsaktion", + "general" : "Allgemein", + "rule-engine-lifecycle-event" : "Regelmaschinenlebenszyklusereignis", + "rule-node" : "Regelknoten", + "new-platform-version" : "Neue Plattformversion", + "rate-limits" : "Überschrittene Ratenlimits", + "edge-communication-failure" : "Edge-Kommunikationsfehler", + "edge-connection" : "Edge-Verbindung", + "task-processing-failure" : "Fehler bei der Aufgabenverarbeitung" + }, + "templates" : "Vorlagen", + "notification-templates" : "Benachrichtigungen / Vorlagen", + "tenant-profiles-list-rule-hint" : "Wenn das Feld leer ist, wird der Auslöser auf alle Mandantenprofile angewendet", + "tenants-list-rule-hint" : "Wenn das Feld leer ist, wird der Auslöser auf alle Mandanten angewendet", + "threshold" : "Schwellenwert", + "theme-color" : "Designfarbe", + "time" : "Zeit", + "track-rule-node-events" : "Regelknotenereignisse verfolgen", + "trigger" : { + "alarm" : "Alarm", + "alarm-assignment" : "Alarmzuweisung", + "alarm-comment" : "Alarmkommentar", + "api-usage-limit" : "API-Nutzungslimit", + "device-activity" : "Geräteaktivität", + "entities-limit" : "Entitätenlimit", + "entity-action" : "Entitätsaktion", + "rule-engine-lifecycle-event" : "Regelmaschinenlebenszyklusereignis", + "new-platform-version" : "Neue Plattformversion", + "rate-limits" : "Überschrittene Ratenlimits", + "edge-connection" : "Edge-Verbindung", + "edge-communication-failure" : "Edge-Kommunikationsfehler", + "task-processing-failure" : "Fehler bei der Aufgabenverarbeitung", + "trigger" : "Auslöser", + "trigger-required" : "Auslöser ist erforderlich" + }, + "type" : "Typ", + "unread" : "Ungelesen", + "updated" : "Aktualisiert", + "use-deprecated-webhook-connectors" : "Veraltete Webhook-Connectoren verwenden", + "use-old-api" : "Alte API verwenden", + "use-template" : "Vorlage verwenden", + "view-all" : "Alle anzeigen", + "warning" : "Warnung", + "webhook-url" : "Webhook-URL", + "webhook-url-required" : "Webhook-URL ist erforderlich", + "workflow-url" : "Workflow-URL", + "workflow-url-required" : "Workflow-URL ist erforderlich", + "channel-name" : "Kanalname", + "channel-name-required" : "Kanalname ist erforderlich", + "settings" : { + "notification-settings" : "Benachrichtigungseinstellungen", + "reset-all" : "Alle Einstellungen zurücksetzen", + "reset-all-title" : "Sind Sie sicher, dass Sie das Formular zurücksetzen möchten?", + "reset-all-text" : "Nach der Bestätigung wird das Einstellungsformular auf den Standardwert zurückgesetzt und gespeichert.", + "type" : "Typ", + "enable-all" : "Alle aktivieren", + "disable-all" : "Alle deaktivieren", + "delivery-not-configured" : "Liefermethode ist nicht konfiguriert" + } + }, + "ota-update" : { + "add" : "Paket hinzufügen", + "assign-firmware" : "Firmware zugewiesen", + "assign-firmware-required" : "Zugewiesene Firmware ist erforderlich", + "assign-software" : "Software zugewiesen", + "assign-software-required" : "Zugewiesene Software ist erforderlich", + "auto-generate-checksum" : "Prüfsumme automatisch generieren", + "checksum" : "Prüfsumme", + "checksum-hint" : "Wenn die Prüfsumme leer ist, wird sie automatisch generiert", + "checksum-algorithm" : "Prüfsummenalgorithmus", + "checksum-copied-message" : "Paket-Prüfsumme wurde in die Zwischenablage kopiert", + "change-firmware" : "Die Änderung der Firmware kann ein Update von { count, plural, =1 {1 Gerät} other {# Geräten} } verursachen.", + "change-software" : "Die Änderung der Software kann ein Update von { count, plural, =1 {1 Gerät} other {# Geräten} } verursachen.", + "chose-compatible-device-profile" : "Das hochgeladene Paket ist nur für Geräte mit dem ausgewählten Profil verfügbar.", + "chose-firmware-distributed-device" : "Firmware auswählen, die auf die Geräte verteilt wird", + "chose-software-distributed-device" : "Software auswählen, die auf die Geräte verteilt wird", + "content-type" : "Inhaltstyp", + "copy-checksum" : "Prüfsumme kopieren", + "copy-direct-url" : "Direkte URL kopieren", + "copyId" : "Paket-ID kopieren", + "copied" : "Kopiert!", + "delete" : "Paket löschen", + "delete-ota-update-text" : "Achtung, nach der Bestätigung kann das OTA-Update nicht wiederhergestellt werden.", + "delete-ota-update-title" : "Sind Sie sicher, dass Sie das OTA-Update '{{title}}' löschen möchten?", + "delete-ota-updates-text" : "Achtung, nach der Bestätigung werden alle ausgewählten OTA-Updates entfernt.", + "delete-ota-updates-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 OTA-Update} other {# OTA-Updates} } löschen möchten?", + "description" : "Beschreibung", + "direct-url" : "Direkte URL", + "direct-url-copied-message" : "Direkte URL des Pakets wurde in die Zwischenablage kopiert", + "direct-url-required" : "Direkte URL ist erforderlich", + "download" : "Paket herunterladen", + "drop-file" : "Paketdatei ablegen oder klicken, um eine Datei auszuwählen.", + "drop-package-file-or" : "Paketdatei hierher ziehen oder", + "file-name" : "Dateiname", + "file-size" : "Dateigröße", + "file-size-bytes" : "Dateigröße in Bytes", + "idCopiedMessage" : "Paket-ID wurde in die Zwischenablage kopiert", + "no-firmware-matching" : "Keine kompatiblen Firmware-OTA-Update-Pakete für '{{entity}}' gefunden.", + "no-firmware-text" : "Keine kompatiblen Firmware-OTA-Update-Pakete bereitgestellt.", + "no-packages-text" : "Keine Pakete gefunden", + "no-software-matching" : "Keine kompatiblen Software-OTA-Update-Pakete für '{{entity}}' gefunden.", + "no-software-text" : "Keine kompatiblen Software-OTA-Update-Pakete bereitgestellt.", + "ota-update" : "OTA-Update", + "ota-update-details" : "OTA-Update-Details", + "ota-updates" : "OTA-Updates", + "package-file" : "Paketdatei", + "package-type" : "Pakettyp", + "packages-repository" : "Paket-Repository", + "search" : "Pakete suchen", + "selected-package" : "{ count, plural, =1 {1 Paket} other {# Pakete} } ausgewählt", + "title" : "Titel", + "title-required" : "Titel ist erforderlich.", + "title-max-length" : "Titel darf maximal 256 Zeichen lang sein", + "types" : { + "firmware" : "Firmware", + "software" : "Software" + }, + "upload-binary-file" : "Binärdatei hochladen", + "use-external-url" : "Externe URL verwenden", + "version" : "Version", + "version-required" : "Version ist erforderlich.", + "version-tag" : "Versionstag", + "version-tag-hint" : "Benutzerdefinierter Tag muss mit der vom Gerät gemeldeten Paketversion übereinstimmen.", + "version-max-length" : "Version darf maximal 256 Zeichen lang sein", + "warning-after-save-no-edit" : "Nach dem Hochladen des Pakets können Titel, Version, Geräteprofil und Pakettyp nicht mehr geändert werden." + }, + "position" : { + "top" : "Oben", + "bottom" : "Unten", + "left" : "Links", + "right" : "Rechts" + }, + "profile" : { + "profile" : "Profil", + "last-login-time" : "Letzte Anmeldung", + "change-password" : "Passwort ändern", + "current-password" : "Aktuelles Passwort", + "copy-jwt-token" : "JWT-Token kopieren", + "jwt-token" : "JWT-Token", + "token-valid-till" : "Token ist gültig bis", + "tokenCopiedSuccessMessage" : "JWT-Token wurde in die Zwischenablage kopiert", + "tokenCopiedWarnMessage" : "JWT-Token ist abgelaufen! Bitte aktualisieren Sie die Seite." + }, + "profiles" : { + "profiles" : "Profile" + }, + "security" : { + "security" : "Sicherheit", + "general-settings" : "Allgemeine Sicherheitseinstellungen", + "access-token" : "Zugriffstoken", + "access-token-required" : "Zugriffstoken ist erforderlich", + "clientId" : "Client-ID", + "clientId-required" : "Client-ID ist erforderlich", + "username" : "Benutzername", + "username-required" : "Benutzername ist erforderlich", + "ca-cert" : "CA-Zertifikat", + "2fa" : { + "2fa" : "Zwei-Faktor-Authentifizierung", + "2fa-description" : "Die Zwei-Faktor-Authentifizierung schützt Ihr Konto vor unbefugtem Zugriff. Sie müssen lediglich beim Login einen Sicherheitscode eingeben.", + "authenticate-with" : "Sie können sich authentifizieren mit:", + "disable-2fa-provider-text" : "Das Deaktivieren von {{name}} macht Ihr Konto weniger sicher", + "disable-2fa-provider-title" : "Sind Sie sicher, dass Sie {{name}} deaktivieren möchten?", + "get-new-code" : "Neuen Code erhalten", + "main-2fa-method" : "Als primäre Zwei-Faktor-Methode verwenden", + "dialog" : { + "activation-step-description-email" : "Beim nächsten Login müssen Sie den Sicherheitscode eingeben, der an Ihre E-Mail-Adresse gesendet wird.", + "activation-step-description-sms" : "Beim nächsten Login müssen Sie den Sicherheitscode eingeben, der an die Telefonnummer gesendet wird.", + "activation-step-description-totp" : "Beim nächsten Login müssen Sie einen Zwei-Faktor-Authentifizierungscode angeben.", + "activation-step-label" : "Aktivierung", + "backup-code-description" : "Drucken Sie die Codes aus, um sie griffbereit zu haben, wenn Sie sich bei Ihrem Konto anmelden. Jeder Backup-Code kann einmal verwendet werden.", + "backup-code-warn" : "Sobald Sie diese Seite verlassen, können diese Codes nicht erneut angezeigt werden. Bewahren Sie sie sicher auf.", + "download-txt" : "Herunterladen (txt)", + "email-step-description" : "Geben Sie eine E-Mail-Adresse ein, die als Authentifizierer verwendet werden soll.", + "email-step-label" : "E-Mail", + "enable-email-title" : "E-Mail-Authentifizierer aktivieren", + "enable-sms-title" : "SMS-Authentifizierer aktivieren", + "enable-totp-title" : "Authentifizierungs-App aktivieren", + "enter-verification-code" : "6-stelligen Code hier eingeben", + "get-backup-code-title" : "Backup-Code erhalten", + "next" : "Weiter", + "scan-qr-code" : "Scannen Sie diesen QR-Code mit Ihrer Verifizierungs-App", + "send-code" : "Code senden", + "sms-step-description" : "Geben Sie eine Telefonnummer ein, die als Authentifizierer verwendet werden soll.", + "sms-step-label" : "Telefonnummer", + "success" : "Erfolg!", + "totp-step-description-install" : "Sie können Apps wie Google Authenticator, Authy oder Duo installieren.", + "totp-step-description-open" : "Öffnen Sie die Authentifizierungs-App auf Ihrem Mobiltelefon.", + "totp-step-label" : "App erhalten", + "verification-code" : "6-stelliger Code", + "verification-code-invalid" : "Ungültiges Code-Format", + "verification-code-incorrect" : "Verifizierungscode ist falsch", + "verification-code-many-request" : "Zu viele Anfragen zur Überprüfung des Codes", + "verification-step-description" : "Geben Sie den 6-stelligen Code ein, den wir gerade an {{address}} gesendet haben", + "verification-step-label" : "Verifizierung" + }, + "provider" : { + "email" : "E-Mail", + "email-description" : "Verwenden Sie einen Sicherheitscode, der an Ihre E-Mail-Adresse gesendet wird, um sich zu authentifizieren.", + "email-hint" : "Authentifizierungscodes werden per E-Mail an {{ info }} gesendet", + "sms" : "SMS", + "sms-description" : "Verwenden Sie Ihr Telefon zur Authentifizierung. Wir senden Ihnen beim Login einen Sicherheitscode per SMS.", + "sms-hint" : "Authentifizierungscodes werden per SMS an {{ info }} gesendet", + "totp" : "Authentifizierungs-App", + "totp-description" : "Verwenden Sie Apps wie Google Authenticator, Authy oder Duo auf Ihrem Telefon zur Authentifizierung. Diese generieren einen Sicherheitscode für die Anmeldung.", + "totp-hint" : "Eine Authentifizierungs-App ist für Ihr Konto eingerichtet", + "backup_code" : "Backup-Code", + "backup-code-description" : "Diese druckbaren Einmalcodes ermöglichen die Anmeldung, wenn Sie nicht auf Ihr Telefon zugreifen können, z. B. auf Reisen.", + "backup-code-hint" : "{{ info }} Einmalcodes sind derzeit aktiv" + } + }, + "password-requirement" : { + "at-least" : "Mindestens:", + "character" : "{ count, plural, =1 {1 Zeichen} other {# Zeichen} }", + "digit" : "{ count, plural, =1 {1 Ziffer} other {# Ziffern} }", + "incorrect-password-try-again" : "Falsches Passwort. Bitte versuchen Sie es erneut", + "lowercase-letter" : "{ count, plural, =1 {1 Kleinbuchstabe} other {# Kleinbuchstaben} }", + "new-passwords-not-match" : "Die neuen Passwörter stimmen nicht überein", + "password-should-not-contain-spaces" : "Ihr Passwort darf keine Leerzeichen enthalten", + "password-not-meet-requirements" : "Passwort erfüllt die Anforderungen nicht", + "password-requirements" : "Passwortanforderungen", + "password-should-difference" : "Das neue Passwort sollte sich vom aktuellen unterscheiden", + "special-character" : "{ count, plural, =1 {1 Sonderzeichen} other {# Sonderzeichen} }", + "uppercase-letter" : "{ count, plural, =1 {1 Großbuchstabe} other {# Großbuchstaben} }", + "at-most" : "Höchstens:" + } + }, + "relation" : { + "relations" : "Beziehungen", + "direction" : "Richtung", + "clear-relation-type" : "Beziehungstyp löschen", + "search-direction" : { + "FROM" : "Von", + "TO" : "Zu" + }, + "direction-type" : { + "FROM" : "von", + "TO" : "zu" + }, + "from-relations" : "Ausgehende Beziehungen", + "to-relations" : "Eingehende Beziehungen", + "selected-relations" : "{ count, plural, =1 {1 Beziehung} other {# Beziehungen} } ausgewählt", + "type" : "Typ", + "to-entity-type" : "Zielentitätstyp", + "to-entity-name" : "Name der Zielentität", + "from-entity-type" : "Quellentitätstyp", + "from-entity-name" : "Name der Quellentität", + "to-entity" : "Zielentität", + "from-entity" : "Quellentität", + "delete" : "Beziehung löschen", + "relation-type" : "Beziehungstyp", + "relation-type-required" : "Beziehungstyp ist erforderlich.", + "relation-type-max-length" : "Beziehungstyp sollte weniger als 256 Zeichen enthalten", + "any-relation-type" : "Beliebiger Typ", + "add" : "Beziehung hinzufügen", + "edit" : "Beziehung bearbeiten", + "delete-to-relation-title" : "Sind Sie sicher, dass Sie die Beziehung zur Entität '{{entityName}}' löschen möchten?", + "delete-to-relation-text" : "Seien Sie vorsichtig, nach der Bestätigung wird die Entität '{{entityName}}' von der aktuellen Entität getrennt.", + "delete-to-relations-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Beziehung} other {# Beziehungen} } löschen möchten?", + "delete-to-relations-text" : "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Beziehungen entfernt und die entsprechenden Entitäten von der aktuellen Entität getrennt.", + "delete-from-relation-title" : "Sind Sie sicher, dass Sie die Beziehung von der Entität '{{entityName}}' löschen möchten?", + "delete-from-relation-text" : "Seien Sie vorsichtig, nach der Bestätigung wird die aktuelle Entität von der Entität '{{entityName}}' getrennt.", + "delete-from-relations-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Beziehung} other {# Beziehungen} } löschen möchten?", + "delete-from-relations-text" : "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Beziehungen entfernt und die aktuelle Entität wird von den entsprechenden Entitäten getrennt.", + "remove-relation-filter" : "Beziehungsfilter entfernen", + "remove-filter" : "Filter entfernen", + "add-relation-filter" : "Beziehungsfilter hinzufügen", + "any-relation" : "Beliebige Beziehung", + "relation-filters" : "Beziehungsfilter", + "additional-info" : "Zusätzliche Informationen (JSON)", + "invalid-additional-info" : "Zusätzliche Info-JSON kann nicht geparst werden.", + "no-relations-text" : "Keine Beziehungen gefunden", + "not" : "Nicht" + }, + "resource" : { + "add" : "Ressource hinzufügen", + "all-types" : "Alle", + "copyId" : "Ressourcen-ID kopieren", + "delete" : "Ressource löschen", + "delete-resource-text" : "Seien Sie vorsichtig, nach der Bestätigung kann die Ressource nicht wiederhergestellt werden.", + "delete-resource-title" : "Sind Sie sicher, dass Sie die Ressource '{{resourceTitle}}' löschen möchten?", + "delete-resources-action-title" : "{ count, plural, =1 {1 Ressource löschen} other {# Ressourcen löschen} }", + "delete-resources-text" : "Bitte beachten Sie, dass die ausgewählten Ressourcen gelöscht werden, auch wenn sie in Geräteprofilen verwendet werden.", + "delete-resources-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Ressource} other {# Ressourcen} } löschen möchten?", + "download" : "Ressource herunterladen", + "drop-file" : "Datei ablegen oder klicken, um eine Datei zum Hochladen auszuwählen.", + "drop-resource-file-or" : "Datei hierher ziehen oder", + "empty" : "Ressource ist leer", + "file-name" : "Dateiname", + "idCopiedMessage" : "Ressourcen-ID wurde in die Zwischenablage kopiert", + "no-resource-matching" : "Keine passende Ressource für '{{widgetsBundle}}' gefunden.", + "no-resource-text" : "Keine Ressourcen gefunden", + "open-widgets-bundle" : "Widget-Bundle öffnen", + "resource" : "Ressource", + "resource-file" : "Ressourcendatei", + "resource-files" : "Ressourcendateien", + "resource-library-details" : "Ressourcendetails", + "resource-type" : "Ressourcentyp", + "resources-library" : "Ressourcenbibliothek", + "search" : "Ressourcen suchen", + "selected-resources" : "{ count, plural, =1 {1 Ressource} other {# Ressourcen} } ausgewählt", + "system" : "System", + "title" : "Titel", + "title-required" : "Titel ist erforderlich.", + "title-max-length" : "Titel sollte weniger als 256 Zeichen enthalten", + "type" : { + "jks" : "JKS", + "js-module" : "JS-Modul", + "lwm2m-model" : "LWM2M-Modell", + "pkcs-12" : "PKCS #12" + }, + "resource-sub-type" : "Subtyp", + "sub-type" : { + "image" : "Bild", + "scada-symbol" : "SCADA-Symbol", + "extension" : "Erweiterung", + "module" : "Modul" + } + }, + "javascript" : { + "add" : "JavaScript-Ressource hinzufügen", + "delete" : "JavaScript-Ressource löschen", + "delete-javascript-resource-text" : "Seien Sie vorsichtig, nach der Bestätigung kann die JavaScript-Ressource nicht wiederhergestellt werden.", + "delete-javascript-resource-title" : "Sind Sie sicher, dass Sie die JavaScript-Ressource '{{resourceTitle}}' löschen möchten?", + "delete-javascript-resources-action-title" : "JavaScript { count, plural, =1 {1 Ressource} other {# Ressourcen} } löschen", + "delete-javascript-resources-text" : "Bitte beachten Sie, dass die ausgewählten JavaScript-Ressourcen gelöscht werden, auch wenn sie in JavaScript-Funktionen verwendet werden.", + "delete-javascript-resources-title" : "Sind Sie sicher, dass Sie JavaScript { count, plural, =1 {1 Ressource} other {# Ressourcen} } löschen möchten?", + "delete-javascript-resource-in-use-text" : "Wenn Sie die JavaScript-Ressource trotzdem löschen möchten, klicken Sie auf die Schaltfläche Trotzdem löschen.", + "download" : "JavaScript-Ressource herunterladen", + "upload-from-file" : "JavaScript aus Datei hochladen", + "resource-file" : "JavaScript-Ressourcendatei", + "drop-file" : "Legen Sie eine JavaScript-Datei ab oder klicken Sie, um eine Datei auszuwählen.", + "drop-resource-file-or" : "JavaScript-Datei hierher ziehen oder", + "javascript-library" : "JavaScript-Bibliothek", + "javascript-type" : "JavaScript-Typ", + "javascript-resource-details" : "Details der JavaScript-Ressource", + "javascript-resource-is-in-use" : "JavaScript-Ressource wird von anderen Entitäten verwendet", + "javascript-resources-are-in-use" : "JavaScript-Ressourcen werden von anderen Entitäten verwendet", + "javascript-resource-is-in-use-text" : "Die JavaScript-Ressource '{{title}}' wurde nicht gelöscht, da sie von folgenden Entitäten verwendet wird:", + "javascript-resources-are-in-use-text" : "Nicht alle JavaScript-Ressourcen wurden gelöscht, da sie von anderen Entitäten verwendet werden.
Sie können die referenzierten Entitäten über die Schaltfläche Referenzen in der jeweiligen Ressourcenzeile anzeigen.
Wenn Sie diese JavaScript-Ressourcen dennoch löschen möchten, wählen Sie sie in der Tabelle unten aus und klicken Sie auf Ausgewählte löschen.", + "search" : "JavaScript-Ressourcen suchen", + "selected-javascript-resources" : "{ count, plural, =1 {1 JavaScript-Ressource} other {# JavaScript-Ressourcen} } ausgewählt", + "no-javascript-resource-text" : "Keine JavaScript-Ressourcen gefunden", + "all-types" : "Alle", + "module-script" : "Modulskript" + }, + "rpc" : { + "error" : { + "target-device-is-not-set" : "Zielgerät ist nicht festgelegt!", + "invalid-target-entity" : "RPC-Befehle werden von der Entität {{entityType}} nicht unterstützt.", + "failed-to-resolve-target-device" : "Zielgerät konnte nicht aufgelöst werden!", + "request-timeout" : "Anfragezeitüberschreitung", + "rpc-http-error" : "Fehler: {{status}} - {{statusText}}" + } + }, + "rulechain" : { + "rulechain" : "Regelkette", + "rulechain-events" : "Regelkettenereignisse", + "rulechains" : "Regelketten", + "root" : "Wurzel", + "delete" : "Regelkette löschen", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "name-max-length" : "Name sollte weniger als 256 Zeichen enthalten", + "description" : "Beschreibung", + "add" : "Regelkette hinzufügen", + "set-root" : "Regelkette zur Wurzel machen", + "set-root-rulechain-title" : "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' zur Wurzel machen möchten?", + "set-root-rulechain-text" : "Nach der Bestätigung wird diese Regelkette zur Wurzel und verarbeitet alle eingehenden Transportnachrichten.", + "delete-rulechain-title" : "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' löschen möchten?", + "delete-rulechain-text" : "Seien Sie vorsichtig, nach der Bestätigung wird die Regelkette und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-rulechains-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Regelkette} other {# Regelketten} } löschen möchten?", + "delete-rulechains-action-title" : "{ count, plural, =1 {1 Regelkette löschen} other {# Regelketten löschen} }", + "delete-rulechains-text" : "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Regelketten entfernt und alle zugehörigen Daten gelöscht.", + "add-rulechain-text" : "Neue Regelkette hinzufügen", + "no-rulechains-text" : "Keine Regelketten gefunden", + "rulechain-details" : "Details zur Regelkette", + "details" : "Details", + "events" : "Ereignisse", + "system" : "System", + "import" : "Regelkette importieren", + "export" : "Regelkette exportieren", + "export-failed-error" : "Regelkette konnte nicht exportiert werden: {{error}}", + "create-new-rulechain" : "Neue Regelkette erstellen", + "rulechain-file" : "Regelketten-Datei", + "invalid-rulechain-file-error" : "Regelkette konnte nicht importiert werden: Ungültige Datenstruktur.", + "copyId" : "Regelkette-ID kopieren", + "idCopiedMessage" : "Regelkette-ID wurde in die Zwischenablage kopiert", + "select-rulechain" : "Regelkette auswählen", + "no-rulechains-matching" : "Keine Regelketten für '{{entity}}' gefunden.", + "rulechain-required" : "Regelkette ist erforderlich", + "management" : "Regelverwaltung", + "debug-mode" : "Debug-Modus", + "search" : "Regelketten suchen", + "selected-rulechains" : "{ count, plural, =1 {1 Regelkette} other {# Regelketten} } ausgewählt", + "open-rulechain" : "Regelkette öffnen", + "edge-template-root" : "Vorlagen-Wurzel", + "assign-to-edge" : "Edge zuweisen", + "edge-rulechain" : "Edge-Regelkette", + "unassign-rulechain-from-edge-text" : "Nach der Bestätigung wird die Regelkette entfernt und ist für das Edge nicht mehr verfügbar.", + "unassign-rulechains-from-edge-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Regelkette} other {# Regelketten} } entfernen möchten?", + "unassign-rulechains-from-edge-text" : "Nach der Bestätigung werden alle ausgewählten Regelketten entfernt und sind für das Edge nicht mehr verfügbar.", + "assign-rulechain-to-edge-title" : "Regelketten dem Edge zuweisen", + "assign-rulechain-to-edge-text" : "Bitte wählen Sie die Regelketten aus, die dem Edge zugewiesen werden sollen", + "set-edge-template-root-rulechain" : "Regelkette als Edge-Vorlagen-Wurzel festlegen", + "set-edge-template-root-rulechain-title" : "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' zur Edge-Vorlagen-Wurzel machen möchten?", + "set-edge-template-root-rulechain-text" : "Nach der Bestätigung wird diese Regelkette als Wurzel-Regelkette für neu erstellte Edges festgelegt.", + "invalid-rulechain-type-error" : "Regelkette konnte nicht importiert werden: Ungültiger Regelkettentyp. Erwarteter Typ ist {{expectedRuleChainType}}.", + "set-auto-assign-to-edge" : "Regelkette bei Erstellung automatisch Edge(s) zuweisen", + "set-auto-assign-to-edge-title" : "Sind Sie sicher, dass Sie die Edge-Regelkette '{{ruleChainName}}' bei der Erstellung automatisch Edge(s) zuweisen möchten?", + "set-auto-assign-to-edge-text" : "Nach der Bestätigung wird die Edge-Regelkette bei der Erstellung automatisch Edge(s) zugewiesen.", + "unset-auto-assign-to-edge" : "Regelkette bei Erstellung nicht automatisch Edge(s) zuweisen", + "unset-auto-assign-to-edge-title" : "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' bei der Erstellung nicht automatisch Edge(s) zuweisen möchten?", + "unset-auto-assign-to-edge-text" : "Nach der Bestätigung wird die Edge-Regelkette nicht mehr automatisch bei Erstellung zugewiesen.", + "unassign-rulechain-title" : "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' entfernen möchten?", + "unassign-rulechains" : "Regelketten entfernen" + }, + "rulenode" : { + "rule-node-events" : "Regelknotenereignisse", + "details" : "Details", + "events" : "Ereignisse", + "search" : "Knoten suchen", + "open-node-library" : "Knotenbibliothek öffnen", + "close-node-library" : "Knotenbibliothek schließen", + "add" : "Regelknoten hinzufügen", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "name-max-length" : "Name sollte weniger als 256 Zeichen enthalten", + "type" : "Typ", + "rule-node-description" : "Beschreibung des Regelknotens", + "delete" : "Regelknoten löschen", + "select-all-objects" : "Alle Knoten und Verbindungen auswählen", + "deselect-all-objects" : "Alle Knoten und Verbindungen abwählen", + "delete-selected-objects" : "Ausgewählte Knoten und Verbindungen löschen", + "delete-selected" : "Ausgewählte löschen", + "create-nested-rulechain" : "Verschachtelte Regelkette erstellen", + "select-all" : "Alle auswählen", + "copy-selected" : "Ausgewählte kopieren", + "deselect-all" : "Alle abwählen", + "rulenode-details" : "Regelknotendetails", + "debug-mode" : "Debug-Modus", + "singleton" : "Einzelexemplar", + "configuration" : "Konfiguration", + "link" : "Verbindung", + "link-details" : "Details der Regelknotenverbindung", + "add-link" : "Verbindung hinzufügen", + "link-label" : "Verbindungsbezeichnung", + "link-label-required" : "Verbindungsbezeichnung ist erforderlich.", + "custom-link-label" : "Benutzerdefinierte Verbindungsbezeichnung", + "custom-link-label-required" : "Benutzerdefinierte Verbindungsbezeichnung ist erforderlich.", + "link-labels" : "Verbindungsbezeichnungen", + "link-labels-required" : "Verbindungsbezeichnungen sind erforderlich.", + "no-link-labels-found" : "Keine Verbindungsbezeichnungen gefunden", + "no-link-label-matching" : "'{{label}}' nicht gefunden.", + "create-new-link-label" : "Neue erstellen!", + "type-filter" : "Filter", + "type-filter-details" : "Eingehende Nachrichten mit konfigurierten Bedingungen filtern", + "type-enrichment" : "Anreicherung", + "type-enrichment-details" : "Zusätzliche Informationen zu den Nachrichtendaten hinzufügen", + "type-transformation" : "Transformation", + "type-transformation-details" : "Nachrichtennutzlast und Metadaten ändern", + "type-action" : "Aktion", + "type-action-details" : "Spezielle Aktion ausführen", + "type-external" : "Extern", + "type-external-details" : "Interaktion mit externem System", + "type-rule-chain" : "Regelkette", + "type-rule-chain-details" : "Eingehende Nachrichten an definierte Regelkette weiterleiten", + "type-flow" : "Fluss", + "type-flow-details" : "Nachrichtenfluss organisieren", + "type-input" : "Eingang", + "type-input-details" : "Logischer Eingang der Regelkette, leitet eingehende Nachrichten an den nächsten Regelknoten weiter", + "type-unknown" : "Unbekannt", + "type-unknown-details" : "Nicht aufgelöster Regelknoten", + "directive-is-not-loaded" : "Die definierte Konfigurationsdirektive '{{directiveName}}' ist nicht verfügbar.", + "ui-resources-load-error" : "Konfigurations-UI-Ressourcen konnten nicht geladen werden.", + "invalid-target-rulechain" : "Ziel-Regelkette konnte nicht aufgelöst werden!", + "test-script-function" : "Skriptfunktion testen", + "script-lang-java-script" : "JavaScript", + "script-lang-tbel" : "TBEL", + "message" : "Nachricht", + "message-type" : "Nachrichtentyp", + "select-message-type" : "Nachrichtentyp auswählen", + "message-type-required" : "Nachrichtentyp ist erforderlich", + "metadata" : "Metadaten", + "metadata-required" : "Metadateneinträge dürfen nicht leer sein.", + "output" : "Ausgabe", + "test" : "Test", + "help" : "Hilfe", + "reset-debug-settings" : "Debug-Einstellungen in allen Knoten zurücksetzen", + "test-with-this-message" : "{{test}} mit dieser Nachricht", + "queue-hint" : "Wählen Sie eine Warteschlange für die Nachrichtenweiterleitung zu einer anderen Warteschlange. Standardmäßig wird die 'Main'-Warteschlange verwendet.", + "queue-singleton-hint" : "Wählen Sie eine Warteschlange für die Nachrichtenweiterleitung in Mehrinstanzumgebungen. Standardmäßig wird die 'Main'-Warteschlange verwendet." + }, + "rule-node-config" : { + "id" : "ID", + "additional-info" : "Zusätzliche Informationen", + "advanced-settings" : "Erweiterte Einstellungen", + "create-entity-if-not-exists" : "Neue Entität erstellen, wenn nicht vorhanden", + "create-entity-if-not-exists-hint" : "Wenn aktiviert, wird eine neue Entität mit angegebenen Parametern erstellt, sofern sie noch nicht existiert. Existierende Entitäten werden wie vorhanden für die Beziehung verwendet.", + "select-device-connectivity-event" : "Geräteverbindungsereignis auswählen", + "entity-name-pattern" : "Namensmuster", + "device-name-pattern" : "Gerätename", + "asset-name-pattern" : "Asset-Name", + "entity-view-name-pattern" : "Entitätsansichtsname", + "customer-title-pattern" : "Kundentitel", + "dashboard-name-pattern" : "Dashboard-Titel", + "user-name-pattern" : "Benutzer-E-Mail", + "edge-name-pattern" : "Edge-Name", + "entity-name-pattern-required" : "Namensmuster ist erforderlich", + "entity-name-pattern-hint" : "Das Feld 'Namensmuster' unterstützt Templatisierung. Verwenden Sie $[messageKey] für Nachrichtenwerte und ${metadataKey} für Metadatenwerte.", + "copy-message-type" : "Nachrichtentyp kopieren", + "entity-type-pattern" : "Typmuster", + "entity-type-pattern-required" : "Typmuster ist erforderlich", + "message-type-value" : "Nachrichtentyp-Wert", + "message-type-value-required" : "Nachrichtentyp-Wert ist erforderlich", + "message-type-value-max-length" : "Nachrichtentyp-Wert sollte weniger als 256 Zeichen enthalten", + "output-message-type" : "Ausgabe-Nachrichtentyp", + "entity-cache-expiration" : "Ablaufzeit des Entitäten-Caches (Sekunden)", + "entity-cache-expiration-hint" : "Gibt das maximale Intervall an, in dem gefundene Entitätsdatensätze gespeichert werden dürfen. Wert 0 bedeutet, dass Datensätze nie ablaufen.", + "entity-cache-expiration-required" : "Ablaufzeit des Entitäten-Caches ist erforderlich.", + "entity-cache-expiration-range" : "Ablaufzeit des Entitäten-Caches muss größer oder gleich 0 sein.", + "customer-name-pattern" : "Kundentitel", + "customer-name-pattern-required" : "Kundentitel ist erforderlich", + "customer-name-pattern-hint" : "Verwenden Sie $[messageKey] für Nachrichtenwerte und ${metadataKey} für Metadatenwerte.", + "create-customer-if-not-exists" : "Neuen Kunden erstellen, wenn nicht vorhanden", + "unassign-from-customer" : "Von bestimmtem Kunden trennen, wenn Absender ein Dashboard ist", + "unassign-from-customer-tooltip" : "Nur Dashboards können mehreren Kunden gleichzeitig zugewiesen werden.\nWenn der Nachrichtenabsender ein Dashboard ist, müssen Sie den Kundentitel explizit angeben, um die Zuordnung zu entfernen.", + "customer-cache-expiration" : "Ablaufzeit des Kunden-Caches (Sekunden)", + "customer-cache-expiration-hint" : "Gibt das maximale Intervall an, in dem gefundene Kundendatensätze gespeichert werden dürfen. Wert 0 bedeutet, dass Datensätze nie ablaufen.", + "customer-cache-expiration-required" : "Ablaufzeit des Kunden-Caches ist erforderlich.", + "customer-cache-expiration-range" : "Ablaufzeit des Kunden-Caches muss größer oder gleich 0 sein.", + "interval-start" : "Intervallbeginn", + "interval-end" : "Intervallende", + "time-unit" : "Zeiteinheit", + "fetch-mode" : "Abrufmodus", + "order-by-timestamp" : "Nach Zeitstempel sortieren", + "limit" : "Grenzwert", + "limit-hint" : "Min. Wert: 2, Max: 1000. Um einen Eintrag abzurufen, wählen Sie den Abrufmodus 'Erster' oder 'Letzter'.", + "limit-required" : "Grenzwert ist erforderlich.", + "limit-range" : "Grenzwert muss zwischen 2 und 1000 liegen.", + "time-unit-milliseconds" : "Millisekunden", + "time-unit-seconds" : "Sekunden", + "time-unit-minutes" : "Minuten", + "time-unit-hours" : "Stunden", + "time-unit-days" : "Tage", + "time-value-range" : "Zulässiger Bereich: 1 bis 2147483647.", + "start-interval-value-required" : "Intervallbeginn ist erforderlich.", + "end-interval-value-required" : "Intervallende ist erforderlich.", + "filter" : "Filter", + "switch" : "Umschalter", + "math-templatization-tooltip" : "Dieses Feld unterstützt Templatisierung. Verwenden Sie $[messageKey] für Nachrichtenwerte und ${metadataKey} für Metadatenwerte.", + "add-message-type" : "Nachrichtentyp hinzufügen", + "select-message-types-required" : "Mindestens ein Nachrichtentyp muss ausgewählt werden.", + "select-message-types" : "Nachrichtentypen auswählen", + "no-message-types-found" : "Keine Nachrichtentypen gefunden", + "no-message-type-matching" : "'{{messageType}}' nicht gefunden.", + "create-new-message-type" : "Neuen erstellen.", + "message-types-required" : "Nachrichtentypen sind erforderlich.", + "client-attributes" : "Client-Attribute", + "shared-attributes" : "Geteilte Attribute", + "server-attributes" : "Server-Attribute", + "attributes-keys" : "Attributschlüssel", + "attributes-keys-required" : "Attributschlüssel sind erforderlich", + "attributes-scope" : "Attributbereich", + "attributes-scope-value" : "Attributbereichswert", + "attributes-scope-value-copy" : "Attributbereichswert kopieren", + "attributes-scope-hint" : "Verwenden Sie den Metadatenschlüssel 'scope', um den Attributbereich pro Nachricht dynamisch zu setzen. Überschreibt die Konfiguration.", + "notify-device" : "Benachrichtigung an Gerät erzwingen", + "send-attributes-updated-notification" : "Benachrichtigung bei aktualisierten Attributen senden", + "send-attributes-updated-notification-hint" : "Sendet Benachrichtigung über aktualisierte Attribute als separate Nachricht an die Regelmaschinen-Warteschlange.", + "send-attributes-deleted-notification" : "Benachrichtigung bei gelöschten Attributen senden", + "send-attributes-deleted-notification-hint" : "Sendet Benachrichtigung über gelöschte Attribute als separate Nachricht an die Regelmaschinen-Warteschlange.", + "update-attributes-only-on-value-change" : "Attribute nur bei Wertänderung speichern", + "update-attributes-only-on-value-change-hint" : "Aktualisiert Attribute bei jeder eingehenden Nachricht, unabhängig von Wertänderung. Erhöht API-Nutzung und verringert Leistung.", + "update-attributes-only-on-value-change-hint-enabled" : "Aktualisiert Attribute nur bei Wertänderung. Keine Aktualisierung von Zeitstempel oder Änderungsbenachrichtigung bei gleichem Wert.", + "fetch-credentials-to-metadata" : "Zugangsdaten in Metadaten übernehmen", + "notify-device-on-update-hint" : "Wenn aktiviert, erzwingt Benachrichtigung an das Gerät bei Attribut-Update. Bei Deaktivierung erfolgt Steuerung über Metadatenparameter 'notifyDevice'.", + "notify-device-on-delete-hint" : "Wenn aktiviert, erzwingt Benachrichtigung an das Gerät bei Attribut-Entfernung. Bei Deaktivierung erfolgt Steuerung über Metadatenparameter 'notifyDevice'.", + "latest-timeseries" : "Neueste Zeitreihenschlüssel", + "timeseries-keys" : "Zeitreihenschlüssel", + "timeseries-keys-required" : "Mindestens ein Zeitreihenschlüssel muss ausgewählt werden.", + "add-timeseries-key" : "Zeitreihenschlüssel hinzufügen", + "add-message-field" : "Nachrichtenfeld hinzufügen", + "relation-search-parameters" : "Beziehungs-Suchparameter", + "relation-parameters" : "Beziehungsparameter", + "add-metadata-field" : "Metadatenfeld hinzufügen", + "data-keys" : "Feldnamen der Nachricht", + "copy-from" : "Kopieren von", + "data-to-metadata" : "Daten zu Metadaten", + "metadata-to-data" : "Metadaten zu Daten", + "use-regular-expression-hint" : "Verwenden Sie reguläre Ausdrücke zum Kopieren nach Muster.\n\nTipps:\nEnter = Eingabe abschließen\nBackspace = löschen\nMehrere Felder erlaubt.", + "interval" : "Intervall", + "interval-required" : "Intervall ist erforderlich", + "interval-hint" : "Deduplizierungsintervall in Sekunden.", + "interval-min-error" : "Minimal zulässiger Wert ist 1", + "max-pending-msgs" : "Max. wartende Nachrichten", + "max-pending-msgs-hint" : "Maximale Anzahl an Nachrichten, die im Speicher für jede eindeutige Deduplizierungs-ID gespeichert werden.", + "max-pending-msgs-required" : "Max. wartende Nachrichten ist erforderlich", + "max-pending-msgs-max-error" : "Maximal zulässiger Wert ist 1000", + "max-pending-msgs-min-error" : "Minimal zulässiger Wert ist 1", + "max-retries" : "Maximale Wiederholungen", + "max-retries-required" : "Maximale Wiederholungen sind erforderlich", + "max-retries-hint" : "Maximale Anzahl an Wiederholungen zum Einreihen deduplizierter Nachrichten in die Warteschlange. Verzögerung von 10 Sekunden zwischen den Wiederholungen.", + "max-retries-max-error" : "Maximal zulässiger Wert ist 100", + "max-retries-min-error" : "Minimal zulässiger Wert ist 0", + "strategy" : "Strategie", + "strategy-required" : "Strategie ist erforderlich", + "strategy-all-hint" : "Gibt alle Nachrichten, die während des Deduplizierungszeitraums eingetroffen sind, als einzelnes JSON-Array zurück. Jedes Element enthält die Eigenschaften msg und metadata.", + "strategy-first-hint" : "Gibt die erste Nachricht zurück, die während des Deduplizierungszeitraums eingetroffen ist.", + "strategy-last-hint" : "Gibt die letzte Nachricht zurück, die während des Deduplizierungszeitraums eingetroffen ist.", + "first" : "Erste", + "last" : "Letzte", + "all" : "Alle", + "output-msg-type-hint" : "Der Nachrichtentyp des Deduplizierungsergebnisses.", + "queue-name-hint" : "Name der Warteschlange, in die das Deduplizierungsergebnis veröffentlicht wird.", + "keys" : "Schlüssel", + "keys-required" : "Schlüssel sind erforderlich", + "rename-keys-in" : "Schlüssel umbenennen in", + "data" : "Daten", + "message" : "Nachricht", + "metadata" : "Metadaten", + "current-key-name" : "Aktueller Schlüsselname", + "key-name-required" : "Schlüsselname ist erforderlich", + "new-key-name" : "Neuer Schlüsselname", + "new-key-name-required" : "Neuer Schlüsselname ist erforderlich", + "metadata-keys" : "Metadatenfeldnamen", + "json-path-expression" : "JSONPath-Ausdruck", + "json-path-expression-required" : "JSONPath-Ausdruck ist erforderlich", + "json-path-expression-hint" : "JSONPath gibt einen Pfad zu einem Element oder einer Menge von Elementen in einer JSON-Struktur an. '$' steht für das Wurzelelement.", + "relations-query" : "Beziehungsabfrage", + "device-relations-query" : "Gerätebeziehungsabfrage", + "max-relation-level" : "Max. Beziehungsebene", + "max-relation-level-error" : "Wert muss größer als 0 oder leer sein.", + "max-relation-level-invalid" : "Wert muss eine ganze Zahl sein.", + "relation-type" : "Beziehungstyp", + "relation-type-pattern" : "Beziehungstyp-Muster", + "relation-type-pattern-required" : "Beziehungstyp-Muster ist erforderlich", + "relation-types-list" : "Zu propagierende Beziehungstypen", + "relation-types-list-hint" : "Wenn keine Beziehungstypen ausgewählt sind, werden Alarme ohne Filterung nach Typ propagiert.", + "unlimited-level" : "Unbegrenzte Ebene", + "latest-telemetry" : "Neueste Telemetriedaten", + "add-telemetry-key" : "Telemetrieschlüssel hinzufügen", + "delete-from" : "Löschen von", + "use-regular-expression-delete-hint" : "Regulären Ausdruck verwenden, um Schlüssel nach Muster zu löschen.\n\nTipps:\nEnter = Feldname abschließen\nBackspace = löschen\nMehrere Felder erlaubt.", + "fetch-into" : "Abrufen in", + "attr-mapping" : "Attribut-Zuordnung:", + "source-attribute" : "Quell-Attributschlüssel", + "source-attribute-required" : "Quell-Attributschlüssel ist erforderlich.", + "source-telemetry" : "Quell-Telemetrieschlüssel", + "source-telemetry-required" : "Quell-Telemetrieschlüssel ist erforderlich.", + "target-key" : "Zielschlüssel", + "target-key-required" : "Zielschlüssel ist erforderlich.", + "attr-mapping-required" : "Mindestens ein Zuordnungseintrag muss angegeben werden.", + "fields-mapping" : "Feld-Zuordnung", + "fields-mapping-hint" : "Wenn das Nachrichtenfeld auf $entityId gesetzt ist, wird die ID des Absenders in die angegebene Spalte gespeichert.", + "relations-query-config-direction-suffix" : "Absender", + "profile-name" : "Profilname", + "fetch-circle-parameter-info-from-metadata-hint" : "Das Metadatenfeld '{{perimeterKeyName}}' sollte folgendes Format haben: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint" : "Das Metadatenfeld '{{perimeterKeyName}}' sollte folgendes Format haben: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip" : "Verwenden Sie $[messageKey] für Nachrichtenwerte und ${metadataKey} für Metadatenwerte.", + "fields-mapping-required" : "Mindestens eine Feldzuordnung muss angegeben werden.", + "at-least-one-field-required" : "Mindestens ein Eingabefeld muss einen Wert enthalten.", + "originator-fields-sv-map-hint" : "Zielschlüsselfelder unterstützen Templatisierung. Verwenden Sie $[messageKey] oder ${metadataKey}.", + "sv-map-hint" : "Nur Zielschlüsselfelder unterstützen Templatisierung. Verwenden Sie $[messageKey] oder ${metadataKey}.", + "source-field" : "Quellfeld", + "source-field-required" : "Quellfeld ist erforderlich.", + "originator-source" : "Absenderquelle", + "new-originator" : "Neuer Absender", + "originator-customer" : "Kunde", + "originator-tenant" : "Mandant", + "originator-related" : "Zugehörige Entität", + "originator-alarm-originator" : "Alarm-Absender", + "originator-entity" : "Entität nach Namensmuster", + "clone-message" : "Nachricht duplizieren", + "transform" : "Transformieren", + "default-ttl" : "Standard-TTL", + "default-ttl-required" : "Standard-TTL ist erforderlich.", + "default-ttl-hint" : "Der Regelknoten holt die TTL aus den Metadaten der Nachricht. Wenn nicht vorhanden, wird die Konfiguration verwendet. Wert 0 verwendet TTL aus Mandantenprofil.", + "default-ttl-zero-hint" : "TTL wird nicht angewendet, wenn der Wert 0 ist.", + "min-default-ttl-message" : "Nur minimaler TTL-Wert von 0 ist erlaubt.", + "generation-parameters" : "Generierungsparameter", + "message-count" : "Limit für generierte Nachrichten (0 = unbegrenzt)", + "message-count-required" : "Limit für generierte Nachrichten ist erforderlich.", + "min-message-count-message" : "Nur 0 als minimaler Nachrichtenwert erlaubt.", + "period-seconds" : "Zeitraum in Sekunden", + "period-seconds-required" : "Zeitraum ist erforderlich.", + "generation-frequency-seconds" : "Generierungshäufigkeit in Sekunden", + "generation-frequency-required" : "Generierungshäufigkeit ist erforderlich.", + "min-generation-frequency-message" : "Mindestens 60 Sekunden sind erforderlich.", + "script-lang-tbel" : "TBEL", + "script-lang-js" : "JS", + "use-metadata-period-in-seconds-patterns" : "Muster für Zeitraum in Sekunden verwenden", + "use-metadata-period-in-seconds-patterns-hint" : "Wenn ausgewählt, verwendet der Regelknoten ein Zeitraum-Muster in Sekunden aus den Metadaten oder Daten der Nachricht.", + "period-in-seconds-pattern" : "Zeitraum-Muster in Sekunden", + "period-in-seconds-pattern-required" : "Zeitraum-Muster in Sekunden ist erforderlich", + "min-period-seconds-message" : "Mindestens 60 Sekunden als Zeitraum erforderlich.", + "originator" : "Absender", + "message-body" : "Nachrichteninhalt", + "message-metadata" : "Nachrichtenmetadaten", + "generate" : "Generieren", + "current-rule-node" : "Aktueller Regelknoten", + "current-tenant" : "Aktueller Mandant", + "generator-function" : "Generatorfunktion", + "test-generator-function" : "Generatorfunktion testen", + "generator" : "Generator", + "test-filter-function" : "Filterfunktion testen", + "test-switch-function" : "Switch-Funktion testen", + "test-transformer-function" : "Transformationsfunktion testen", + "transformer" : "Transformer", + "alarm-create-condition" : "Bedingung zur Alarmerstellung", + "test-condition-function" : "Bedingungsfunktion testen", + "alarm-clear-condition" : "Bedingung zur Alarmauflösung", + "alarm-details-builder" : "Alarmdetails-Generator", + "test-details-function" : "Detailfunktion testen", + "alarm-type" : "Alarmtyp", + "select-entity-types" : "Entitätstypen auswählen", + "alarm-type-required" : "Alarmtyp ist erforderlich.", + "alarm-severity" : "Alarm-Schweregrad", + "alarm-severity-required" : "Alarm-Schweregrad ist erforderlich", + "alarm-severity-pattern" : "Muster für Alarm-Schweregrad", + "alarm-status-filter" : "Alarmstatus-Filter", + "alarm-status-list-empty" : "Alarmstatusliste ist leer", + "no-alarm-status-matching" : "Kein übereinstimmender Alarmstatus gefunden.", + "propagate" : "Alarm an verknüpfte Entitäten weiterleiten", + "propagate-to-owner" : "Alarm an Eigentümer (Kunde oder Mandant) weiterleiten", + "propagate-to-tenant" : "Alarm an Mandant weiterleiten", + "condition" : "Bedingung", + "details" : "Details", + "to-string" : "In String umwandeln", + "test-to-string-function" : "To-String-Funktion testen", + "from-template" : "Von", + "from-template-required" : "Absender ist erforderlich", + "message-to-metadata" : "Nachricht zu Metadaten", + "metadata-to-message" : "Metadaten zur Nachricht", + "from-message" : "Aus Nachricht", + "from-metadata" : "Aus Metadaten", + "to-template" : "An", + "to-template-required" : "Empfänger-Vorlage ist erforderlich", + "mail-address-list-template-hint" : "Kommagetrennte Adressliste. Verwenden Sie ${metadataKey} für Werte aus den Metadaten und $[messageKey] für Werte aus dem Nachrichtentext.", + "cc-template" : "Cc", + "bcc-template" : "Bcc", + "subject-template" : "Betreff", + "subject-template-required" : "Betreffvorlage ist erforderlich", + "body-template" : "Inhalt", + "body-template-required" : "Inhaltsvorlage ist erforderlich", + "dynamic-mail-body-type" : "Dynamischer Nachrichtentyp", + "mail-body-type" : "Nachrichtentyp", + "body-type-template" : "Vorlage Nachrichtentyp", + "reply-routing-configuration" : "Antwort-Routing-Konfiguration", + "rpc-reply-routing-configuration-hint" : "Diese Parameter geben die Metadaten-Schlüssel an, die zur Identifikation von Dienst, Sitzung und Anfrage verwendet werden.", + "reply-routing-configuration-hint" : "Diese Parameter geben die Metadaten-Schlüssel an, die zur Identifikation von Dienst und Anfrage verwendet werden.", + "request-id-metadata-attribute" : "Anfrage-ID", + "service-id-metadata-attribute" : "Dienst-ID", + "session-id-metadata-attribute" : "Sitzungs-ID", + "timeout-sec" : "Timeout in Sekunden", + "timeout-required" : "Timeout ist erforderlich", + "min-timeout-message" : "Nur Timeout-Wert 0 ist erlaubt.", + "endpoint-url-pattern" : "Endpoint-URL-Muster", + "endpoint-url-pattern-required" : "Endpoint-URL-Muster ist erforderlich", + "request-method" : "Anfragemethode", + "use-simple-client-http-factory" : "Einfache HTTP-Client-Fabrik verwenden", + "ignore-request-body" : "Ohne Nachrichteninhalt", + "parse-to-plain-text" : "In Klartext umwandeln", + "parse-to-plain-text-hint" : "Wenn ausgewählt, wird die JSON-Nutzlast in Klartext umgewandelt, z. B. msg = \"Hello,\\t\"world\"\" → Hello, \"world\"", + "read-timeout" : "Lese-Timeout in Millisekunden", + "read-timeout-hint" : "Wert 0 bedeutet unbegrenztes Timeout", + "max-parallel-requests-count" : "Max. Anzahl paralleler Anfragen", + "max-parallel-requests-count-hint" : "Wert 0 bedeutet unbegrenzte Parallelität", + "max-response-size" : "Maximale Antwortgröße (in KB)", + "max-response-size-hint" : "Maximaler Speicher für das Puffern von Daten beim Kodieren/Decodieren von HTTP-Nachrichten (z. B. JSON, XML)", + "headers" : "Header", + "headers-hint" : "Verwenden Sie ${metadataKey} für Werte aus den Metadaten und $[messageKey] für Werte aus dem Nachrichtentext in Header-/Wert-Feldern.", + "header" : "Header", + "header-required" : "Header ist erforderlich", + "value" : "Wert", + "value-required" : "Wert ist erforderlich", + "topic-pattern" : "Themenmuster", + "key-pattern" : "Schlüsselmuster", + "key-pattern-hint" : "Optional. Wenn eine gültige Partitionsnummer angegeben ist, wird sie beim Senden des Datensatzes verwendet. Wenn keine Partition angegeben ist, wird stattdessen der Schlüssel verwendet. Wenn beides nicht angegeben ist, wird die Partition im Round-Robin-Verfahren zugewiesen.", + "topic-pattern-required" : "Themenmuster ist erforderlich", + "topic" : "Thema", + "topic-required" : "Thema ist erforderlich", + "bootstrap-servers" : "Bootstrap-Server", + "bootstrap-servers-required" : "Bootstrap-Server-Wert ist erforderlich", + "other-properties" : "Weitere Eigenschaften", + "key" : "Schlüssel", + "key-required" : "Schlüssel ist erforderlich", + "retries" : "Automatische Wiederholungen bei Fehlern", + "min-retries-message" : "Nur 0 Mindestanzahl an Wiederholungen ist erlaubt.", + "batch-size-bytes" : "Batchgröße in Bytes", + "min-batch-size-bytes-message" : "Nur 0 minimale Batchgröße ist erlaubt.", + "linger-ms" : "Lokale Pufferdauer (ms)", + "min-linger-ms-message" : "Nur 0 ms als Mindestwert ist erlaubt.", + "buffer-memory-bytes" : "Maximale Client-Puffergröße in Bytes", + "min-buffer-memory-message" : "Nur 0 minimale Puffergröße ist erlaubt.", + "memory-buffer-size-range" : "Die Speicherpuffergröße muss zwischen 0 und {{max}} KB liegen", + "acks" : "Anzahl der Bestätigungen", + "topic-arn-pattern" : "Thema-ARN-Muster", + "topic-arn-pattern-required" : "Thema-ARN-Muster ist erforderlich", + "aws-access-key-id" : "AWS-Zugangsschlüssel-ID", + "aws-access-key-id-required" : "AWS-Zugangsschlüssel-ID ist erforderlich", + "aws-secret-access-key" : "AWS-Geheimer Zugangsschlüssel", + "aws-secret-access-key-required" : "AWS-Geheimer Zugangsschlüssel ist erforderlich", + "aws-region" : "AWS-Region", + "aws-region-required" : "AWS-Region ist erforderlich", + "exchange-name-pattern" : "Exchange-Namensmuster", + "routing-key-pattern" : "Routing-Schlüssel-Muster", + "message-properties" : "Nachrichteneigenschaften", + "host" : "Host", + "host-required" : "Host ist erforderlich", + "port" : "Port", + "port-required" : "Port ist erforderlich", + "port-range" : "Port muss im Bereich von 1 bis 65535 liegen.", + "virtual-host" : "Virtueller Host", + "username" : "Benutzername", + "password" : "Passwort", + "automatic-recovery" : "Automatische Wiederherstellung", + "connection-timeout-ms" : "Verbindungs-Timeout (ms)", + "min-connection-timeout-ms-message" : "Nur 0 ms als Mindestwert ist erlaubt.", + "handshake-timeout-ms" : "Handshake-Timeout (ms)", + "min-handshake-timeout-ms-message" : "Nur 0 ms als Mindestwert ist erlaubt.", + "client-properties" : "Client-Eigenschaften", + "queue-url-pattern" : "Warteschlangen-URL-Muster", + "queue-url-pattern-required" : "Warteschlangen-URL-Muster ist erforderlich", + "delay-seconds" : "Verzögerung (Sekunden)", + "min-delay-seconds-message" : "Nur 0 Sekunden als Mindestwert ist erlaubt.", + "max-delay-seconds-message" : "Nur maximal 900 Sekunden sind erlaubt.", + "name" : "Name", + "name-required" : "Name ist erforderlich", + "queue-type" : "Warteschlangentyp", + "sqs-queue-standard" : "Standard", + "sqs-queue-fifo" : "FIFO", + "gcp-project-id" : "GCP-Projekt-ID", + "gcp-project-id-required" : "GCP-Projekt-ID ist erforderlich", + "gcp-service-account-key" : "GCP-Dienstkonto-Schlüsseldatei", + "gcp-service-account-key-required" : "GCP-Dienstkonto-Schlüsseldatei ist erforderlich", + "pubsub-topic-name" : "Themenname", + "pubsub-topic-name-required" : "Themenname ist erforderlich", + "message-attributes" : "Nachrichtenattribute", + "message-attributes-hint" : "Verwende ${metadataKey} für Werte aus den Metadaten, $[messageKey] für Werte aus dem Nachrichteninhalt in Name/Wert-Feldern", + "connect-timeout" : "Verbindungs-Timeout (Sek)", + "connect-timeout-required" : "Verbindungs-Timeout ist erforderlich.", + "connect-timeout-range" : "Verbindungs-Timeout muss im Bereich von 1 bis 200 liegen.", + "client-id" : "Client-ID", + "client-id-hint" : "Optional. Leer lassen für automatisch generierte Client-ID. Vorsicht beim Festlegen der Client-ID: Die meisten MQTT-Broker erlauben keine mehrfachen Verbindungen mit derselben Client-ID. Um mit solchen Brokern zu verbinden, muss deine MQTT-Client-ID eindeutig sein. Wenn die Plattform im Microservice-Modus betrieben wird, wird eine Kopie des Regelknotens in jedem Microservice gestartet. Dies führt automatisch zu mehreren MQTT-Clients mit derselben ID und kann zu Fehlern führen. Um dies zu vermeiden, aktiviere die Option „Dienst-ID als Suffix zur Client-ID hinzufügen“ unten.", + "append-client-id-suffix" : "Dienst-ID als Suffix zur Client-ID hinzufügen", + "client-id-suffix-hint" : "Optional. Wird angewendet, wenn die „Client-ID“ explizit angegeben wurde. Falls ausgewählt, wird die Dienst-ID als Suffix zur Client-ID hinzugefügt. Hilft Fehler zu vermeiden, wenn die Plattform im Microservice-Modus läuft.", + "device-id" : "Geräte-ID", + "device-id-required" : "Geräte-ID ist erforderlich.", + "clean-session" : "Saubere Sitzung", + "enable-ssl" : "SSL aktivieren", + "credentials" : "Anmeldedaten", + "credentials-type" : "Anmeldedatentyp", + "credentials-type-required" : "Anmeldedatentyp ist erforderlich.", + "credentials-anonymous" : "Anonym", + "credentials-basic" : "Einfach", + "credentials-pem" : "PEM", + "credentials-pem-hint" : "Mindestens Server-CA-Zertifikat oder ein Paar aus Client-Zertifikat und privatem Client-Schlüssel erforderlich", + "credentials-sas" : "Gemeinsam genutzte Zugriffssignatur (SAS)", + "sas-key" : "SAS-Schlüssel", + "sas-key-required" : "SAS-Schlüssel ist erforderlich.", + "hostname" : "Hostname", + "hostname-required" : "Hostname ist erforderlich.", + "azure-ca-cert" : "CA-Zertifikatsdatei", + "username-required" : "Benutzername ist erforderlich.", + "password-required" : "Passwort ist erforderlich.", + "ca-cert" : "Server-CA-Zertifikatsdatei", + "private-key" : "Privater Client-Schlüssel", + "cert" : "Client-Zertifikat", + "no-file" : "Keine Datei ausgewählt.", + "drop-file" : "Datei ablegen oder klicken, um eine Datei hochzuladen.", + "private-key-password" : "Passwort für privaten Schlüssel", + "use-system-smtp-settings" : "Systemweite SMTP-Einstellungen verwenden", + "use-metadata-dynamic-interval" : "Dynamisches Intervall verwenden", + "metadata-dynamic-interval-hint" : "Eingabefelder für Start- und Endzeitpunkt unterstützen Templating. Beachten Sie, dass der ersetzte Template-Wert in Millisekunden angegeben werden muss. Verwenden Sie $[messageKey] für Werte aus der Nachricht und ${metadataKey} für Werte aus den Metadaten.", + "use-metadata-interval-patterns-hint" : "Wenn ausgewählt, verwendet der Regelknoten Start- und Endintervallmuster aus den Nachrichtendaten oder Metadaten, wobei angenommen wird, dass die Intervalle in Millisekunden angegeben sind.", + "use-message-alarm-data" : "Alarminformationen aus Nachricht verwenden", + "overwrite-alarm-details" : "Alarmdetails überschreiben", + "use-alarm-severity-pattern" : "Alarm-Schweregrad-Muster verwenden", + "check-all-keys" : "Überprüfen, ob alle angegebenen Felder vorhanden sind", + "check-all-keys-hint" : "Wenn ausgewählt, wird geprüft, ob alle angegebenen Schlüssel in den Nachrichtendaten und Metadaten vorhanden sind.", + "check-relation-to-specific-entity" : "Beziehung zu einer bestimmten Entität überprüfen", + "check-relation-to-specific-entity-tooltip" : "Wenn aktiviert, wird die Existenz einer Beziehung zu einer bestimmten Entität überprüft. Andernfalls wird das Vorhandensein einer Beziehung zu einer beliebigen Entität überprüft. In beiden Fällen erfolgt die Beziehungssuche anhand von Richtung und Typ.", + "check-relation-hint" : "Überprüft das Vorhandensein einer Beziehung zu einer bestimmten oder beliebigen Entität basierend auf Richtung und Beziehungstyp.", + "delete-relation-with-specific-entity" : "Beziehung mit spezifischer Entität löschen", + "delete-relation-with-specific-entity-hint" : "Wenn aktiviert, wird nur die Beziehung zu einer spezifischen Entität gelöscht. Andernfalls werden alle passenden Beziehungen entfernt.", + "delete-relation-hint" : "Löscht die Beziehung vom Ursprung der eingehenden Nachricht zur angegebenen Entität oder Entitätenliste basierend auf Richtung und Typ.", + "remove-current-relations" : "Aktuelle Beziehungen entfernen", + "remove-current-relations-hint" : "Entfernt aktuelle Beziehungen vom Ursprung der eingehenden Nachricht basierend auf Richtung und Typ.", + "change-originator-to-related-entity" : "Ursprung zur verbundenen Entität ändern", + "change-originator-to-related-entity-hint" : "Verwendet zur Verarbeitung der eingereichten Nachricht als käme sie von einer anderen Entität.", + "start-interval" : "Intervallstart", + "end-interval" : "Intervallende", + "start-interval-required" : "Intervallstart ist erforderlich.", + "end-interval-required" : "Intervallende ist erforderlich.", + "smtp-protocol" : "Protokoll", + "smtp-host" : "SMTP-Host", + "smtp-host-required" : "SMTP-Host ist erforderlich.", + "smtp-port" : "SMTP-Port", + "smtp-port-required" : "SMTP-Port muss angegeben werden.", + "smtp-port-range" : "SMTP-Port muss im Bereich von 1 bis 65535 liegen.", + "timeout-msec" : "Timeout in ms", + "min-timeout-msec-message" : "Nur 0 ms als Mindestwert ist erlaubt.", + "enter-username" : "Benutzernamen eingeben", + "enter-password" : "Passwort eingeben", + "enable-tls" : "TLS aktivieren", + "tls-version" : "TLS-Version", + "enable-proxy" : "Proxy aktivieren", + "use-system-proxy-properties" : "System-Proxy-Eigenschaften verwenden", + "proxy-host" : "Proxy-Host", + "proxy-host-required" : "Proxy-Host ist erforderlich.", + "proxy-port" : "Proxy-Port", + "proxy-port-required" : "Proxy-Port ist erforderlich.", + "proxy-port-range" : "Proxy-Port muss im Bereich von 1 bis 65535 liegen.", + "proxy-user" : "Proxy-Benutzer", + "proxy-password" : "Proxy-Passwort", + "proxy-scheme" : "Proxy-Schema", + "numbers-to-template" : "Telefonnummern zum Template", + "numbers-to-template-required" : "Telefonnummern zum Template sind erforderlich", + "numbers-to-template-hint" : "Kommagetrennte Telefonnummern. Verwenden Sie ${metadataKey} für Werte aus Metadaten und $[messageKey] für Werte aus dem Nachrichtentext", + "sms-message-template" : "SMS-Nachrichtenvorlage", + "sms-message-template-required" : "SMS-Nachrichtenvorlage ist erforderlich", + "use-system-sms-settings" : "Systemweite SMS-Anbietereinstellungen verwenden", + "min-period-0-seconds-message" : "Nur 0 Sekunden Mindestzeitraum ist erlaubt.", + "max-pending-messages" : "Maximale ausstehende Nachrichten", + "max-pending-messages-required" : "Maximale ausstehende Nachrichten sind erforderlich.", + "max-pending-messages-range" : "Maximale ausstehende Nachrichten müssen im Bereich von 1 bis 100000 liegen.", + "originator-types-filter" : "Filter für Ursprungstypen", + "interval-seconds" : "Intervall in Sekunden", + "interval-seconds-required" : "Intervall ist erforderlich.", + "int-range" : "Wert darf den maximalen Integer-Grenzwert (2147483648) nicht überschreiten", + "min-interval-seconds-message" : "Nur 1 Sekunde Mindestintervall ist erlaubt.", + "output-timeseries-key-prefix" : "Präfix des Zeitreihen-Schlüssels für Ausgabe", + "output-timeseries-key-prefix-required" : "Präfix des Zeitreihen-Schlüssels für Ausgabe ist erforderlich.", + "separator-hint" : "Drücken Sie \"Enter\", um die Eingabe abzuschließen.", + "select-details" : "Details auswählen", + "entity-details-id" : "ID", + "entity-details-title" : "Titel", + "entity-details-country" : "Land", + "entity-details-state" : "Bundesland", + "entity-details-city" : "Stadt", + "entity-details-zip" : "PLZ", + "entity-details-address" : "Adresse", + "entity-details-address2" : "Adresse 2", + "entity-details-additional_info" : "Zusätzliche Info", + "entity-details-phone" : "Telefon", + "entity-details-email" : "E-Mail", + "email-sender" : "E-Mail-Absender", + "fields-to-check" : "Zu überprüfende Felder", + "add-detail" : "Detail hinzufügen", + "check-all-keys-tooltip" : "Wenn aktiviert, überprüft das Vorhandensein aller in den Nachrichtendaten und Metadaten aufgeführten Felder innerhalb der eingehenden Nachricht.", + "fields-to-check-hint" : "Drücken Sie \"Enter\", um die Eingabe eines Feldnamens abzuschließen. Mehrere Feldnamen werden unterstützt.", + "entity-details-list-empty" : "Mindestens ein Detail muss ausgewählt werden.", + "alarm-status" : "Alarmstatus", + "alarm-required" : "Mindestens ein Alarmstatus muss ausgewählt werden.", + "no-entity-details-matching" : "Keine passenden Entitätsdetails gefunden.", + "custom-table-name" : "Benutzerdefinierter Tabellenname", + "custom-table-name-required" : "Tabellenname ist erforderlich", + "custom-table-hint" : "Die Tabelle muss in Ihrem Cassandra-Cluster erstellt sein und mit dem Präfix 'cs_tb_' beginnen, um das Einfügen in allgemeine TB-Tabellen zu vermeiden. Geben Sie hier den Tabellennamen ohne das Präfix 'cs_tb_' ein.", + "message-field" : "Nachrichtenfeld", + "message-field-required" : "Nachrichtenfeld ist erforderlich.", + "table-col" : "Tabellenspalte", + "table-col-required" : "Tabellenspalte ist erforderlich.", + "latitude-field-name" : "Breitengrad-Feldname", + "longitude-field-name" : "Längengrad-Feldname", + "latitude-field-name-required" : "Breitengrad-Feldname ist erforderlich.", + "longitude-field-name-required" : "Längengrad-Feldname ist erforderlich.", + "fetch-perimeter-info-from-metadata" : "Perimeterinformationen aus Metadaten abrufen", + "fetch-perimeter-info-from-metadata-tooltip" : "Wenn der Perimetertyp auf 'Polygon' gesetzt ist, wird der Wert des Metadatenfelds '{{perimeterKeyName}}' direkt als Definition verwendet, ohne zusätzliche Analyse. Wenn der Typ auf 'Kreis' gesetzt ist, wird der Wert von '{{perimeterKeyName}}' analysiert, um 'latitude', 'longitude', 'radius' und 'radiusUnit' für die Kreisdefinition zu extrahieren.", + "perimeter-key-name" : "Perimeter-Schlüsselname", + "perimeter-key-name-hint" : "Metadatenfeldname, der die Perimeterinformationen enthält.", + "perimeter-key-name-required" : "Perimeter-Schlüsselname ist erforderlich.", + "perimeter-circle" : "Kreis", + "perimeter-polygon" : "Polygon", + "perimeter-type" : "Perimetertyp", + "circle-center-latitude" : "Breite des Kreismittelpunkts", + "circle-center-latitude-required" : "Breite des Kreismittelpunkts ist erforderlich.", + "circle-center-longitude" : "Länge des Kreismittelpunkts", + "circle-center-longitude-required" : "Länge des Kreismittelpunkts ist erforderlich.", + "range-unit-meter" : "Meter", + "range-unit-kilometer" : "Kilometer", + "range-unit-foot" : "Fuß", + "range-unit-mile" : "Meile", + "range-unit-nautical-mile" : "Seemeile", + "range-units" : "Entfernungseinheiten", + "range-units-required" : "Entfernungseinheiten sind erforderlich.", + "range" : "Entfernung", + "range-required" : "Entfernung ist erforderlich.", + "polygon-definition" : "Polygon-Definition", + "polygon-definition-required" : "Polygon-Definition ist erforderlich.", + "polygon-definition-hint" : "Verwenden Sie folgendes Format zur manuellen Definition eines Polygons: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration" : "Minimale Verweildauer innen", + "min-inside-duration-value-required" : "Minimale Verweildauer innen ist erforderlich", + "min-inside-duration-time-unit" : "Zeiteinheit der minimalen Verweildauer innen", + "min-outside-duration" : "Minimale Verweildauer außen", + "min-outside-duration-value-required" : "Minimale Verweildauer außen ist erforderlich", + "min-outside-duration-time-unit" : "Zeiteinheit der minimalen Verweildauer außen", + "tell-failure-if-absent" : "Fehler melden", + "tell-failure-if-absent-hint" : "Wenn mindestens ein ausgewählter Schlüssel nicht vorhanden ist, wird die ausgehende Nachricht mit \"Fehler\" gekennzeichnet.", + "get-latest-value-with-ts" : "Zeitstempel für letzte Telemetriewerte abrufen", + "get-latest-value-with-ts-hint" : "Wenn ausgewählt, enthalten die letzten Telemetriewerte auch einen Zeitstempel, z. B.: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings" : "Leere Zeichenfolgen ignorieren", + "ignore-null-strings-hint" : "Wenn ausgewählt, ignoriert der Regelknoten Entitätsfelder mit leerem Wert.", + "add-metadata-key-values-as-kafka-headers" : "Metadaten-Schlüssel-Werte-Paare als Kafka-Header hinzufügen", + "add-metadata-key-values-as-kafka-headers-hint" : "Wenn ausgewählt, werden Schlüssel-Wert-Paare aus den Nachrichtemetadaten als Byte-Arrays mit vordefinierter Zeichenkodierung zu den ausgehenden Kafka-Headern hinzugefügt.", + "charset-encoding" : "Zeichenkodierung", + "charset-encoding-required" : "Zeichenkodierung ist erforderlich.", + "charset-us-ascii" : "US-ASCII", + "charset-iso-8859-1" : "ISO-8859-1", + "charset-utf-8" : "UTF-8", + "charset-utf-16be" : "UTF-16BE", + "charset-utf-16le" : "UTF-16LE", + "charset-utf-16" : "UTF-16", + "select-queue-hint" : "Der Warteschlangenname kann aus einer Dropdown-Liste ausgewählt oder benutzerdefiniert eingegeben werden.", + "device-profile-node-hint" : "Nützlich bei Dauer- oder Wiederholungsbedingungen, um die Alarmzustandsbewertung fortlaufend sicherzustellen.", + "persist-alarm-rules" : "Alarmregelzustand speichern", + "persist-alarm-rules-hint" : "Wenn aktiviert, speichert der Regelknoten den Verarbeitungszustand in der Datenbank.", + "fetch-alarm-rules" : "Alarmregelzustand abrufen", + "fetch-alarm-rules-hint" : "Wenn aktiviert, stellt der Regelknoten beim Start den Verarbeitungszustand wieder her, um sicherzustellen, dass Alarme auch nach Neustarts ausgelöst werden. Andernfalls erfolgt die Wiederherstellung beim Eintreffen der ersten Nachricht.", + "input-value-key" : "Eingabewert-Schlüssel", + "input-value-key-required" : "Eingabewert-Schlüssel ist erforderlich.", + "output-value-key" : "Ausgabewert-Schlüssel", + "output-value-key-required" : "Ausgabewert-Schlüssel ist erforderlich.", + "number-of-digits-after-floating-point" : "Anzahl der Stellen nach dem Dezimalpunkt", + "number-of-digits-after-floating-point-range" : "Anzahl der Stellen nach dem Dezimalpunkt muss zwischen 0 und 15 liegen.", + "failure-if-delta-negative" : "Fehler melden, wenn Differenz negativ ist", + "failure-if-delta-negative-tooltip" : "Regelknoten erzwingt Fehler bei der Nachrichtenverarbeitung, wenn der Delta-Wert negativ ist.", + "use-caching" : "Caching verwenden", + "use-caching-tooltip" : "Regelknoten cached den Wert von \"{{inputValueKey}}\" aus der eingehenden Nachricht zur Leistungsverbesserung. Änderungen an \"{{inputValueKey}}\" außerhalb dieses Knotens aktualisieren den Cache nicht.", + "add-time-difference-between-readings" : "Zeitdifferenz zwischen \"{{inputValueKey}}\"-Werten hinzufügen", + "add-time-difference-between-readings-tooltip" : "Wenn aktiviert, fügt der Regelknoten den Schlüssel \"{{periodValueKey}}\" zur ausgehenden Nachricht hinzu.", + "period-value-key" : "Zeitraum-Wert-Schlüssel", + "period-value-key-required" : "Zeitraum-Wert-Schlüssel ist erforderlich.", + "general-pattern-hint" : "Verwenden Sie ${metadataKey} für Metadatenwert, $[messageKey] für Nachrichtenwert.", + "alarm-severity-pattern-hint" : "Verwenden Sie ${metadataKey} für Metadatenwert, $[messageKey] für Nachrichtenwert. Alarm-Schweregrade sollten systemdefiniert sein (CRITICAL, MAJOR usw.)", + "output-node-name-hint" : "Der Name des Regelknotens entspricht dem Beziehungstyp der Ausgabemeldung und wird verwendet, um Nachrichten an andere Knoten in der Regelkette weiterzuleiten.", + "use-server-ts" : "Server-Zeitstempel verwenden", + "use-server-ts-hint" : "Verwenden Sie den aktuellen Zeitstempel des Servers für Zeitreihen ohne expliziten Zeitstempel. Dies hilft, die Reihenfolge bei mehreren Quellen oder verspäteten Nachrichten zu wahren.", + "kv-map-pattern-hint" : "Alle Eingabefelder unterstützen Templating. Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "kv-map-single-pattern-hint" : "Eingabefeld unterstützt Templating. Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "shared-scope" : "Geteilter Bereich", + "server-scope" : "Server-Bereich", + "client-scope" : "Client-Bereich", + "attribute-type" : "Attribut", + "attribute-type-description" : "Attributwert aus der Datenbank abrufen", + "attribute-type-result-description" : "Ergebnis als Entitätsattribut in der Datenbank speichern", + "constant-type" : "Konstante", + "constant-type-description" : "Konstantenwert definieren", + "time-series-type" : "Zeitreihe", + "time-series-type-description" : "Letzten Zeitreihenwert aus Datenbank abrufen", + "time-series-type-result-description" : "Ergebnis als Zeitreihe in der Datenbank speichern", + "message-body-type" : "Nachricht", + "message-body-type-description" : "Argumentwert aus Nachrichtentext abrufen", + "message-body-type-result-description" : "Ergebnis zur Nachricht hinzufügen", + "message-metadata-type" : "Metadaten", + "message-metadata-type-description" : "Argumentwert aus Nachrichtenmetadaten abrufen", + "message-metadata-result-description" : "Ergebnis zu Nachrichtenmetadaten hinzufügen", + "argument-tile" : "Argumente", + "no-arguments-prompt" : "Keine Argumente konfiguriert", + "result-title" : "Ergebnis", + "functions-field-input" : "Funktionen", + "no-option-found" : "Keine Option gefunden", + "argument-source-field-input" : "Quelle", + "argument-source-field-input-required" : "Quellenangabe für Argument ist erforderlich.", + "argument-key-field-input" : "Schlüssel", + "argument-key-field-input-required" : "Argumentschlüssel ist erforderlich.", + "constant-value-field-input" : "Konstantenwert", + "constant-value-field-input-required" : "Konstantenwert ist erforderlich.", + "attribute-scope-field-input" : "Attributbereich", + "attribute-scope-field-input-required" : "Attributbereich ist erforderlich.", + "default-value-field-input" : "Standardwert", + "type-field-input" : "Typ", + "type-field-input-required" : "Typ ist erforderlich.", + "key-field-input" : "Schlüssel", + "add-entity-type" : "Entitätstyp hinzufügen", + "add-device-profile" : "Geräteprofil hinzufügen", + "key-field-input-required" : "Schlüssel ist erforderlich.", + "number-floating-point-field-input" : "Anzahl Dezimalstellen", + "number-floating-point-field-input-hint" : "Verwenden Sie 0, um das Ergebnis in eine Ganzzahl umzuwandeln", + "add-to-message-field-input" : "Zur Nachricht hinzufügen", + "add-to-metadata-field-input" : "Zu Metadaten hinzufügen", + "custom-expression-field-input" : "Mathematischer Ausdruck", + "custom-expression-field-input-required" : "Mathematischer Ausdruck ist erforderlich", + "custom-expression-field-input-hint" : "Geben Sie einen mathematischen Ausdruck zur Auswertung an. Der Standardausdruck zeigt die Umrechnung von Fahrenheit in Celsius.", + "retained-message" : "Beibehalten", + "attributes-mapping" : "Attribut-Zuordnung", + "latest-telemetry-mapping" : "Neueste Telemetrie-Zuordnung", + "add-mapped-attribute-to" : "Zuordnung zu Attributen hinzufügen", + "add-mapped-latest-telemetry-to" : "Zuordnung zu neuester Telemetrie hinzufügen", + "add-mapped-fields-to" : "Zuordnung zu Feldern hinzufügen", + "add-selected-details-to" : "Ausgewählte Details hinzufügen zu", + "clear-selected-types" : "Ausgewählte Typen löschen", + "clear-selected-details" : "Ausgewählte Details löschen", + "clear-selected-fields" : "Ausgewählte Felder löschen", + "clear-selected-keys" : "Ausgewählte Schlüssel löschen", + "geofence-configuration" : "Geofence-Konfiguration", + "coordinate-field-names" : "Koordinaten-Feldnamen", + "coordinate-field-hint" : "Der Regelknoten versucht, die angegebenen Felder aus der Nachricht abzurufen. Wenn diese nicht vorhanden sind, werden sie in den Metadaten gesucht.", + "presence-monitoring-strategy" : "Anwesenheitsüberwachungsstrategie", + "presence-monitoring-strategy-on-first-message" : "Bei erster Nachricht", + "presence-monitoring-strategy-on-each-message" : "Bei jeder Nachricht", + "presence-monitoring-strategy-on-first-message-hint" : "Meldet den Anwesenheitsstatus 'Innen' oder 'Außen' bei der ersten Nachricht, nachdem die konfigurierte Mindestdauer seit dem letzten Status 'Betreten' oder 'Verlassen' vergangen ist.", + "presence-monitoring-strategy-on-each-message-hint" : "Meldet den Anwesenheitsstatus 'Innen' oder 'Außen' bei jeder Nachricht nach dem Statuswechsel 'Betreten' oder 'Verlassen'.", + "fetch-credentials-to" : "Anmeldedaten abrufen zu", + "add-originator-attributes-to" : "Ursprungsattribute hinzufügen zu", + "originator-attributes" : "Ursprungsattribute", + "fetch-latest-telemetry-with-timestamp" : "Neueste Telemetrie mit Zeitstempel abrufen", + "fetch-latest-telemetry-with-timestamp-tooltip" : "Wenn ausgewählt, werden die neuesten Telemetriewerte mit Zeitstempel zu den ausgehenden Metadaten hinzugefügt, z. B.: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure" : "Fehler melden, wenn Attribute fehlen", + "tell-failure-tooltip" : "Wenn mindestens ein ausgewählter Schlüssel fehlt, wird die ausgehende Nachricht mit 'Fehler' markiert.", + "created-time" : "Erstellungszeit", + "chip-help" : "Drücken Sie 'Enter', um die Eingabe von {{inputName}} abzuschließen.\nDrücken Sie 'Backspace', um {{inputName}} zu löschen.\nMehrere Werte werden unterstützt.", + "detail" : "Detail", + "field-name" : "Feldname", + "device-profile" : "Geräteprofil", + "entity-type" : "Entitätstyp", + "message-type" : "Nachrichtentyp", + "timeseries-key" : "Zeitreihen-Schlüssel", + "type" : "Typ", + "first-name" : "Vorname", + "last-name" : "Nachname", + "label" : "Bezeichnung", + "originator-fields-mapping" : "Ursprungsfeld-Zuordnung", + "add-mapped-originator-fields-to" : "Zugeordnete Ursprungsfelder hinzufügen zu", + "fields" : "Felder", + "skip-empty-fields" : "Leere Felder überspringen", + "skip-empty-fields-tooltip" : "Felder mit leerem Wert werden nicht zur Ausgabemeldung/Metadaten hinzugefügt.", + "fetch-interval" : "Abrufintervall", + "fetch-strategy" : "Abrufstrategie", + "fetch-timeseries-from-to" : "Zeitreihe abrufen von {{startInterval}} {{startIntervalTimeUnit}} bis {{endInterval}} {{endIntervalTimeUnit}} zuvor.", + "fetch-timeseries-from-to-invalid" : "Ungültiger Zeitraum für Zeitreihenabruf (\"Intervallstart\" muss kleiner als \"Intervallende\" sein).", + "use-metadata-dynamic-interval-tooltip" : "Wenn aktiviert, verwendet der Regelknoten ein dynamisches Start- und Endintervall basierend auf Nachrichten- und Metadatenmustern.", + "all-mode-hint" : "Bei Auswahl des Modus \"Alle\" ruft der Regelknoten Telemetriedaten im Intervall mit konfigurierbaren Abfrageparametern ab.", + "first-mode-hint" : "Bei Auswahl des Modus \"Erste\" ruft der Regelknoten die Telemetrie am nächsten zum Intervallbeginn ab.", + "last-mode-hint" : "Bei Auswahl des Modus \"Letzte\" ruft der Regelknoten die Telemetrie am nächsten zum Intervallende ab.", + "ascending" : "Aufsteigend", + "descending" : "Absteigend", + "min" : "Minimum", + "max" : "Maximum", + "average" : "Durchschnitt", + "sum" : "Summe", + "count" : "Anzahl", + "none" : "Keine", + "last-level-relation-tooltip" : "Wenn ausgewählt, sucht der Regelknoten nur auf der im maximalen Beziehungsebenenwert angegebenen Ebene nach zugehörigen Entitäten.", + "last-level-device-relation-tooltip" : "Wenn ausgewählt, sucht der Regelknoten nur auf der im maximalen Beziehungsebenenwert angegebenen Ebene nach zugehörigen Geräten.", + "data-to-fetch" : "Daten zum Abrufen", + "mapping-of-customers" : "Zuordnung des Kunden", + "map-fields-required" : "Alle Zuordnungsfelder sind erforderlich.", + "attributes" : "Attribute", + "related-device-attributes" : "Attribute zugehöriger Geräte", + "add-selected-attributes-to" : "Ausgewählte Attribute hinzufügen zu", + "device-profiles" : "Geräteprofile", + "mapping-of-tenant" : "Zuordnung des Mieters", + "add-attribute-key" : "Attributschlüssel hinzufügen", + "message-template" : "Nachrichtenvorlage", + "message-template-required" : "Nachrichtenvorlage ist erforderlich", + "use-system-slack-settings" : "Systemweite Slack-Einstellungen verwenden", + "slack-api-token" : "Slack-API-Token", + "slack-api-token-required" : "Slack-API-Token ist erforderlich", + "keys-mapping" : "Schlüsselzuordnung", + "add-key" : "Schlüssel hinzufügen", + "recipients" : "Empfänger", + "message-subject-and-content" : "Nachrichtenbetreff und -inhalt", + "template-rules-hint" : "Beide Felder unterstützen Templating. Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "originator-customer-desc" : "Verwende Kunde des Nachrichtenursprungs als neuen Ursprung.", + "originator-tenant-desc" : "Verwende aktuellen Mieter als neuen Ursprung.", + "originator-related-entity-desc" : "Verwende zugehörige Entität als neuen Ursprung. Suche basiert auf Relationstyp und Richtung.", + "originator-alarm-originator-desc" : "Verwende Alarmursprung als neuen Ursprung. Gilt nur, wenn Nachrichtenursprung eine Alarmentität ist.", + "originator-entity-by-name-pattern-desc" : "Verwende Entität aus der DB als neuen Ursprung. Suche basiert auf Entitätstyp und Namensmuster.", + "email-from-template-hint" : "Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "recipients-block-main-hint" : "Kommagetrennte Adressliste. Alle Felder unterstützen Templating. Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "forward-msg-default-rule-chain" : "Nachricht an Standard-Regelkette des Ursprungs weiterleiten", + "forward-msg-default-rule-chain-tooltip" : "Wenn aktiviert, wird die Nachricht an die Standard-Regelkette des Ursprungs weitergeleitet, oder eine konfigurierte Kette, wenn keine Standardkette definiert ist.", + "exclude-zero-deltas" : "Null-Deltas von Nachricht ausschließen", + "exclude-zero-deltas-hint" : "Wenn aktiviert, wird der Ausgabeschlüssel \"{{outputValueKey}}\" nur hinzugefügt, wenn der Wert ungleich null ist.", + "exclude-zero-deltas-time-difference-hint" : "Wenn aktiviert, werden \"{{outputValueKey}}\" und \"{{periodValueKey}}\" nur hinzugefügt, wenn der Wert von \"{{outputValueKey}}\" ungleich null ist.", + "search-direction-from" : "Vom Ursprung zur Zielentität", + "search-direction-to" : "Von Zielentität zum Ursprung", + "del-relation-direction-from" : "Vom Ursprung", + "del-relation-direction-to" : "Zum Ursprung", + "target-entity" : "Zielentität", + "function-configuration" : "Funktionskonfiguration", + "function-name" : "Funktionsname", + "function-name-required" : "Funktionsname ist erforderlich.", + "qualifier" : "Qualifier", + "qualifier-hint" : "Wenn kein Qualifier angegeben ist, wird der Standard-Qualifier \"$LATEST\" verwendet.", + "aws-credentials" : "AWS-Zugangsdaten", + "connection-timeout" : "Verbindungs-Timeout", + "connection-timeout-required" : "Verbindungs-Timeout ist erforderlich.", + "connection-timeout-min" : "Minimales Verbindungs-Timeout ist 0.", + "connection-timeout-hint" : "Die Wartezeit in Sekunden beim Verbindungsaufbau, bevor abgebrochen wird. Ein Wert von 0 bedeutet unendlich und wird nicht empfohlen.", + "request-timeout" : "Anfrage-Timeout", + "request-timeout-required" : "Anfrage-Timeout ist erforderlich.", + "request-timeout-min" : "Minimales Anfrage-Timeout ist 0.", + "request-timeout-hint" : "Die Wartezeit in Sekunden, bis die Anfrage abgeschlossen ist, bevor abgebrochen wird. Ein Wert von 0 bedeutet unendlich und wird nicht empfohlen.", + "units" : "Einheiten", + "tell-failure-aws-lambda" : "Fehler melden, wenn AWS Lambda Ausnahme auslöst", + "tell-failure-aws-lambda-hint" : "Der Regelknoten erzwingt einen Fehler, wenn die AWS Lambda-Ausführung eine Ausnahme auslöst.", + "basic-mode" : "Einfach", + "advanced-mode" : "Erweitert", + "save-time-series" : { + "processing-settings" : "Verarbeitungseinstellungen", + "processing-settings-hint" : "Definiert, wie eingehende Nachrichten verarbeitet werden. Einfache Einstellungen bieten vorkonfigurierte Strategien, während erweiterte Einstellungen individuelle Strategien je Aktion erlauben.", + "advanced-settings-hint" : "Seien Sie vorsichtig bei der Konfiguration der Strategien. Bestimmte Kombinationen können zu unerwartetem Verhalten führen.", + "strategy" : "Strategie", + "deduplication-interval" : "Deduplizierungsintervall", + "deduplication-interval-required" : "Deduplizierungsintervall ist erforderlich.", + "deduplication-interval-min-max-range" : "Deduplizierungsintervall muss zwischen 1 Sekunde und 1 Tag liegen.", + "strategy-type" : { + "every-message" : "Bei jeder Nachricht", + "skip" : "Überspringen", + "deduplicate" : "Deduplizieren", + "web-sockets-only" : "Nur WebSockets" + }, + "time-series" : "Zeitreihe", + "latest" : "Neueste Werte", + "web-sockets" : "WebSockets", + "calculated-fields" : "Berechnete Felder" + }, + "save-attribute" : { + "processing-settings" : "Verarbeitungseinstellungen", + "processing-settings-hint" : "Definiert, wie eingehende Nachrichten verarbeitet werden. Einfache Einstellungen bieten vorkonfigurierte Strategien, während erweiterte Einstellungen individuelle Strategien je Aktion erlauben.", + "advanced-settings-hint" : "Seien Sie vorsichtig bei der Konfiguration der Strategien. Bestimmte Kombinationen können zu unerwartetem Verhalten führen.", + "strategy" : "Strategie", + "deduplication-interval" : "Deduplizierungsintervall", + "deduplication-interval-required" : "Deduplizierungsintervall ist erforderlich.", + "deduplication-interval-min-max-range" : "Deduplizierungsintervall muss zwischen 1 Sekunde und 1 Tag liegen.", + "scope" : "Bereich", + "strategy-type" : { + "every-message" : "Bei jeder Nachricht", + "skip" : "Überspringen", + "deduplicate" : "Deduplizieren", + "web-sockets-only" : "Nur WebSockets" + }, + "attributes" : "Attribute" + }, + "key-val" : { + "key" : "Schlüssel", + "value" : "Wert", + "see-examples" : "Beispiele anzeigen.", + "remove-entry" : "Eintrag entfernen", + "remove-mapping-entry" : "Zuordnungseintrag entfernen", + "add-mapping-entry" : "Zuordnung hinzufügen", + "add-entry" : "Eintrag hinzufügen", + "copy-key-values-from" : "Schlüssel-Werte von kopieren", + "delete-key-values" : "Schlüssel-Werte löschen", + "delete-key-values-from" : "Schlüssel-Werte von löschen", + "at-least-one-key-error" : "Mindestens ein Schlüssel muss ausgewählt werden.", + "unique-key-value-pair-error" : "'{{keyText}}' muss sich von '{{valText}}' unterscheiden!" + }, + "mail-body-types" : { + "plain-text" : "Reiner Text", + "html" : "HTML", + "dynamic" : "Dynamisch", + "use-body-type-template" : "Body-Typ-Template verwenden", + "plain-text-description" : "Einfacher, unformatierter Text ohne besondere Gestaltung.", + "html-text-description" : "Ermöglicht HTML-Tags zur Formatierung, für Links und Bilder im Nachrichtentext.", + "dynamic-text-description" : "Erlaubt die dynamische Verwendung von reinem Text oder HTML basierend auf der Templating-Funktion.", + "after-template-evaluation-hint" : "Nach der Template-Auswertung muss der Wert true für HTML und false für reinen Text sein." + } + }, + "timezone" : { + "timezone" : "Zeitzone", + "select-timezone" : "Zeitzone auswählen", + "no-timezones-matching" : "Keine passenden Zeitzonen für '{{timezone}}' gefunden.", + "timezone-required" : "Zeitzone ist erforderlich.", + "browser-time" : "Browserzeit" + }, + "queue" : { + "queue-name" : "Warteschlange", + "no-queues-found" : "Keine Warteschlangen gefunden.", + "no-queues-matching" : "Keine passenden Warteschlangen für '{{queue}}' gefunden.", + "select-name" : "Warteschlangennamen auswählen", + "name" : "Name", + "name-required" : "Warteschlangenname ist erforderlich!", + "name-unique" : "Warteschlangenname ist nicht eindeutig!", + "name-pattern" : "Warteschlangenname enthält ungültige Zeichen (nur ASCII-Buchstaben, Ziffern, '.', '_' und '-' erlaubt)!", + "queue-required" : "Warteschlange ist erforderlich!", + "topic-required" : "Thema der Warteschlange ist erforderlich!", + "poll-interval-required" : "Abfrageintervall ist erforderlich!", + "poll-interval-min-value" : "Abfrageintervall darf nicht kleiner als 1 sein", + "partitions-required" : "Partitionen sind erforderlich!", + "partitions-min-value" : "Anzahl der Partitionen darf nicht kleiner als 1 sein", + "pack-processing-timeout-required" : "Verarbeitungstimeout ist erforderlich", + "pack-processing-timeout-min-value" : "Verarbeitungstimeout darf nicht kleiner als 1 sein", + "batch-size-required" : "Batchgröße ist erforderlich!", + "batch-size-min-value" : "Batchgröße darf nicht kleiner als 1 sein", + "retries-required" : "Wiederholungen sind erforderlich!", + "retries-min-value" : "Anzahl der Wiederholungen darf nicht negativ sein", + "failure-percentage-required" : "Fehlerschwellenwert ist erforderlich!", + "failure-percentage-min-value" : "Fehlerschwellenwert darf nicht kleiner als 0 sein", + "failure-percentage-max-value" : "Fehlerschwellenwert darf nicht größer als 100 sein", + "pause-between-retries-required" : "Pause zwischen Wiederholungen ist erforderlich!", + "pause-between-retries-min-value" : "Pause zwischen Wiederholungen darf nicht kleiner als 1 sein", + "max-pause-between-retries-required" : "Maximale Pause zwischen Wiederholungen ist erforderlich!", + "max-pause-between-retries-min-value" : "Maximale Pause darf nicht kleiner als 1 sein", + "submit-strategy-type-required" : "Übermittlungsstrategie ist erforderlich!", + "processing-strategy-type-required" : "Verarbeitungsstrategie ist erforderlich!", + "queues" : "Warteschlangen", + "selected-queues" : "{ count, plural, =1 {1 Warteschlange} other {# Warteschlangen} } ausgewählt", + "delete-queue-title" : "Sind Sie sicher, dass Sie die Warteschlange '{{queueName}}' löschen möchten?", + "delete-queues-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Warteschlange} other {# Warteschlangen} } löschen möchten?", + "delete-queue-text" : "Achtung! Nach der Bestätigung wird die Warteschlange und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-queues-text" : "Nach der Bestätigung werden alle ausgewählten Warteschlangen gelöscht und sind nicht mehr zugänglich.", + "search" : "Warteschlange suchen", + "add" : "Warteschlange hinzufügen", + "details" : "Details der Warteschlange", + "topic" : "Thema", + "submit-settings" : "Übermittlungseinstellungen", + "submit-strategy" : "Strategietyp *", + "grouping-parameter" : "Gruppierungsparameter", + "processing-settings" : "Einstellungen für Wiederholungsverarbeitung", + "processing-strategy" : "Verarbeitungstyp *", + "retries-settings" : "Wiederholungseinstellungen", + "polling-settings" : "Abfrageeinstellungen", + "batch-processing" : "Batch-Verarbeitung", + "poll-interval" : "Abfrageintervall", + "partitions" : "Partitionen", + "immediate-processing" : "Sofortige Verarbeitung", + "consumer-per-partition" : "Nachricht pro Partition abrufen", + "consumer-per-partition-hint" : "Separaten Verbraucher für jede Partition aktivieren", + "duplicate-msg-to-all-partitions" : "Nachricht an alle Partitionen duplizieren", + "processing-timeout" : "Verarbeitung innerhalb, ms", + "batch-size" : "Batchgröße", + "retries" : "Anzahl der Wiederholungen (0 = unbegrenzt)", + "failure-percentage" : "Fehlermeldungen zum Überspringen von Wiederholungen, %", + "pause-between-retries" : "Wiederholung nach, Sek.", + "max-pause-between-retries" : "Zusätzliche Wiederholung nach, Sek.", + "delete" : "Warteschlange löschen", + "copyId" : "Warteschlangen-ID kopieren", + "idCopiedMessage" : "Warteschlangen-ID wurde in die Zwischenablage kopiert", + "description" : "Beschreibung", + "description-hint" : "Dieser Text wird in der Beschreibung der Warteschlange angezeigt, anstelle der gewählten Strategie", + "alt-description" : "Übermittlungsstrategie: {{submitStrategy}}, Verarbeitungsstrategie: {{processingStrategy}}", + "custom-properties" : "Benutzerdefinierte Eigenschaften", + "custom-properties-hint" : "Eigenschaften für benutzerdefinierte Warteschlangenerstellung (Thema), z. B.: 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies" : { + "sequential-by-originator-label" : "Sequenziell nach Ursprung", + "sequential-by-originator-hint" : "Neue Nachricht z. B. für Gerät A wird erst übermittelt, wenn die vorherige Nachricht für Gerät A bestätigt wurde", + "sequential-by-tenant-label" : "Sequenziell nach Mieter", + "sequential-by-tenant-hint" : "Neue Nachricht z. B. für Mieter A wird erst übermittelt, wenn die vorherige Nachricht für Mieter A bestätigt wurde", + "sequential-label" : "Sequenziell", + "sequential-hint" : "Neue Nachricht wird erst übermittelt, wenn die vorherige bestätigt wurde", + "burst-label" : "Schubweise", + "burst-hint" : "Alle Nachrichten werden in der Reihenfolge ihres Eingangs an die Regelketten übermittelt", + "batch-label" : "Stapel", + "batch-hint" : "Neuer Stapel wird erst übermittelt, wenn der vorherige bestätigt wurde", + "skip-all-failures-label" : "Alle Fehler überspringen", + "skip-all-failures-hint" : "Alle Fehler ignorieren", + "skip-all-failures-and-timeouts-label" : "Alle Fehler und Timeouts überspringen", + "skip-all-failures-and-timeouts-hint" : "Alle Fehler und Timeouts ignorieren", + "retry-all-label" : "Alle wiederholen", + "retry-all-hint" : "Alle Nachrichten aus dem Verarbeitungspaket erneut versuchen", + "retry-failed-label" : "Fehlgeschlagene wiederholen", + "retry-failed-hint" : "Alle fehlgeschlagenen Nachrichten aus dem Verarbeitungspaket erneut versuchen", + "retry-timeout-label" : "Timeouts wiederholen", + "retry-timeout-hint" : "Alle Nachrichten mit Timeout aus dem Verarbeitungspaket erneut versuchen", + "retry-failed-and-timeout-label" : "Fehlgeschlagene und Timeouts wiederholen", + "retry-failed-and-timeout-hint" : "Alle fehlgeschlagenen und mit Timeout versehenen Nachrichten aus dem Verarbeitungspaket erneut versuchen" } }, - "timeunit": { - "milliseconds": "Millisekunden", - "seconds": "Sekunden", - "minutes": "Minuten", - "hours": "Stunden", - "days": "Tage" - }, - "timewindow": { - "timewindow": "Zeitfenster", - "years": "{ years, plural, =1 { Jahr } other {# Jahre } }", - "years-short": "{{ years }}J", - "months": "{ months, plural, =1 { Monat } other {# Monate } }", - "months-short": "{{ months }}M", - "weeks": "{ weeks, plural, =1 { Woche } other {# Wochen } }", - "weeks-short": "{{ weeks }}W", - "days": "{ days, plural, =1 { Tag } other {# Tage } }", - "days-short": "{{ days }}T", - "hours": "{ hours, plural, =0 { Stunde } =1 {1 Stunde } other {# Stunden } }", - "hr": "{{ hr }} Std", - "hr-short": "{{ hr }}S", - "minutes": "{ minutes, plural, =0 { Minute } =1 {1 Minute } other {# Minuten } }", - "min": "{{ min }} Min", - "min-short": "{{ min }}m", - "seconds": "{ seconds, plural, =0 { Sekunde } =1 {1 Sekunde } other {# Sekunden } }", - "sec": "{{ sec }} Sek", - "sec-short": "{{ sec }}s", - "short": { - "days": "{ days, plural, =1 {1 Tag } other {# Tage } }", - "hours": "{ hours, plural, =1 {1 Stunde } other {# Stunden } }", - "minutes": "{{minutes}} Min ", - "seconds": "{{seconds}} Sek " - }, - "realtime": "Echtzeit", - "history": "Historie", - "last-prefix": "letzte", - "period": "von {{ startTime }} bis {{ endTime }}", - "edit": "Zeitfenster bearbeiten", - "date-range": "Datumsbereich", - "for-all-time": "Für immer", - "last": "Letzte", - "time-period": "Zeitfenster", - "hide": "Verstecken", - "interval": "Intervall", - "just-now": "Soeben", - "just-now-lower": "soeben", - "ago": "vor", - "style": "Zeitfenster Style", - "icon": "Symbol", - "icon-position": "Position Symbol", - "icon-position-left": "Links", - "icon-position-right": "Rechts", - "font": "Schriftart", - "color": "Farbe", - "displayTypePrefix": "Echtzeit-/Verlaufspräfix anzeigen", - "preview": "Vorschau" - }, - "user": { - "all": "Alle", - "all-users": "Alle Benutzer", - "groups": "Gruppen", - "user": "Benutzer", - "users": "Benutzer", - "management": "Benutzerverwaltung", - "customer-users": "Kunden-Benutzer", - "tenant-admins": "Mandanten-Administratoren", - "sys-admin": "System-Administrator", - "tenant-admin": "Mandanten-Administrator", - "customer": "Kunde", - "anonymous": "Anonym", - "add": "Benutzer hinzufügen", - "delete": "Benutzer löschen", - "add-user-text": "Neuen Benutzer hinzufügen", - "no-users-text": "Keine Benutzer gefunden", - "user-details": "Benutzer-Details", - "delete-users": "Benutzer löschen", - "delete-user-title": "Möchten Sie den Benutzer '{{userEmail}}' wirklich löschen?", - "delete-user-text": "Vorsicht, nach Bestätigung werden der Benutzer und alle zugehörigen Daten gelöscht.", - "delete-users-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Benutzer} other {# Benutzer} } löschen möchten??", - "delete-users-action-title": "{ count, plural, =1 {1 Benutzer} other {# Benutzer} } löschen", - "delete-users-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Benutzer entfernt und alle zugehörigen Daten werden gelöscht.", - "activation-email-sent-message": "Aktivierungs E-Mail wurde erfolgreich gesendet!", - "resend-activation": "Aktivierung erneut senden", - "email": "E-Mail", - "email-required": "E-Mail ist erforderlich.", - "invalid-email-format": "Ungültiges E-Mail Format.", - "first-name": "Vorname", - "last-name": "Nachname", - "description": "Beschreibung", - "default-dashboard": "Standard-Dashboard", - "always-fullscreen": "Immer Vollbild", - "select-user": "Benutzer auswählen", - "no-users-matching": "Keine passenden Benutzer für '{{entity}}' gefunden.", - "user-required": "Benutzer ist erforderlich", - "activation-method": "Aktivierungsmethode", - "display-activation-link": "Aktivierungslink anzeigen", - "send-activation-mail": "Aktivierungs E-Mail senden", - "activation-link": "Link zur Benutzer-Aktivierung", - "activation-link-text": "Um den Benutzer zu aktivieren, verwenden Sie bitte folgenden Aktivierungslink:", - "copy-activation-link": "Aktivierungslink kopieren", - "activation-link-copied-message": "Der Link zur Benutzer-Aktivierung wurde in die Zwischenablage kopiert ", - "details": "Details", - "login-as-tenant-admin": "Als Mandanten-Administrator anmelden", - "login-as-customer-user": "Als Kunden-Benutzer anmelden", - "disable-account": "Benutzerkonto deaktivieren", - "enable-account": "Benutzerkonto aktivieren", - "enable-account-message": "Benutzerkonto wurde erfolgreich aktiviert!", - "disable-account-message": "Benutzerkonto wurde erfolgreich deaktiviert!", - "copyId": "Benutzer-Id kopieren", - "idCopiedMessage": "Benutzer-Id in die Zwischenablage kopiert", - "user-list": "Benutzerliste", - "user-list-required": "Benutzerliste ist erforderlich" - }, - "value": { - "type": "Wertetyp", - "string": "Text", - "string-value": "Textwert", - "integer": "Ganzzahlig", - "integer-value": "Ganzzahliger Wert", - "invalid-integer-value": "Ungültiger ganzzahliger Wert", - "double": "Gleitkommazahl", - "double-value": "Gleitkomma Wert", - "boolean": "Binär", - "boolean-value": "Binärwert", - "false": "Falsch", - "true": "Wahr", - "long": "Lang" - }, - "widget": { - "widget-library": "Widget-Bibliothek", - "widget-bundle": "Widget-Paket", - "select-widgets-bundle": "Widget-Paket auswählen", - "management": "Widget Verwaltung", - "editor": "Widget Editor", - "widget-type-not-found": "Problem beim Laden der Widget-Konfiguration.
Zugeordneter Widget-Typ wurde entfernt.", - "widget-type-load-error": "Widget wurde aufgrund der folgenden Fehler nicht geladen:", - "remove": "Widget entfernen", - "edit": "Widget bearbeiten", - "remove-widget-title": "Möchten Sie das Widget '{{widgetTitle}}' wirklich entfernen?", - "remove-widget-text": "Nach der Bestätigung werden das Widget und alle zugehörigen Daten nicht wiederhergestellt.", - "timeseries": "Zeitreihe", - "search-data": "Daten suchen", - "no-data-found": "Keine Daten gefunden", - "latest": "Neueste Werte", - "rpc": "Steuerungswidget", - "alarm": "Alarm-Widget", - "static": "Statisches Widget", - "select-widget-type": "Widget-Typ auswählen", - "missing-widget-title-error": "Widget-Titel muss angegeben werden!", - "widget-saved": "Widget gespeichert", - "unable-to-save-widget-error": "Das Widget kann nicht gespeichert werden! Fehlermeldung!", - "save": "Widget speichern", - "saveAs": "Widget speichern unter", - "save-widget-type-as": "Widget-Typ speichern unter", - "save-widget-type-as-text": "Bitte geben Sie den neuen Widget-Titel ein und/oder wählen Sie das Ziel-Widget-Paket aus", - "toggle-fullscreen": "Vollbild umschalten", - "run": "Widget ausführen", - "title": "Widget-Titel", - "title-required": "Widget-Titel ist erforderlich.", - "type": "Widget-Typ", - "resources": "Ressourcen", - "resource-url": "JavaScript/CSS URL", - "remove-resource": "Ressource entfernen", - "add-resource": "Ressource hinzufügen", - "html": "HTML", - "tidy": "Aufgeräumt", - "css": "CSS", - "settings-schema": "Einstellungsschema", - "datakey-settings-schema": "Datenschlüssel-Einstellungsschema", - "javascript": "Javascript", - "add-widget-type": "Neuen Widget-Typ hinzufügen", - "widget-template-load-failed-error": "Widget-Vorlage konnte nicht geladen werden!", - "add": "Widget hinzufügen", - "undo": "Widget-Änderungen widerrufen ", - "export": "Widget exportieren" - }, - "widget-action": { - "header-button": "Widget-Header-Schaltfläche", - "open-dashboard-state": "Zum neuen Dashboard-Status navigieren", - "update-dashboard-state": "Aktuellen Dashboard-Status aktualisieren", - "open-dashboard": "Zu einem anderen Dashboard navigieren", - "custom": "Benutzerdefinierte Aktion", - "target-dashboard-state": "Zielstatus des Dashboards", - "target-dashboard-state-required": "Der Zielstatus ist erforderlich", - "set-entity-from-widget": "Widget-Entität festlegen", - "target-dashboard": "Ziel-Dashboard", - "open-right-layout": "Das rechte Dashboard-Layout öffnen (mobile Ansicht)" - }, - "widgets-bundle": { - "current": "Aktuelles Paket", - "widgets-bundles": "Widget-Pakete", - "add": "Widget-Pakete hinzufügen", - "delete": "Widget-Pakete löschen", - "title": "Titel", - "title-required": "Titel ist erforderlich.", - "add-widgets-bundle-text": "Neues Widget-Paket hinzufügen", - "no-widgets-bundles-text": "Keine Widget-Pakete gefunden", - "empty": "Widget-Paket ist leer ", - "details": "Details", - "widgets-bundle-details": "Widget-Paket-Details", - "delete-widgets-bundle-title": "Möchten Sie das Widget-Paket '{{widgetsBundleTitle}}' wirklich löschen? ", - "delete-widgets-bundle-text": "Seien Sie vorsichtig, nach der Bestätigung werden das Widget-Paket und alle zugehörigen Daten gelöscht.", - "delete-widgets-bundles-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Widget-Paket} other {# Widget-Pakete} } löschen möchten?", - "delete-widgets-bundles-action-title": "{ count, plural, =1 {1 Widgets-Paket} other {# Widget-Pakete} } löschen", - "delete-widgets-bundles-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Widget-Pakete entfernt und alle zugehörigen Daten werden gelöscht.", - "no-widgets-bundles-matching": "Keine passenden Widget-Pakete '{{widgetsBundle}}' gefunden.", - "widgets-bundle-required": "Widget-Paket ist erforderlich.", - "system": "System", - "import": "Widget-Paket importieren", - "export": "Widget-Paket exportieren", - "export-failed-error": "Widget-Paket kann nicht exportiert werden: {{error}}", - "create-new-widgets-bundle": "Neues Widget-Paket erstellen", - "widgets-bundle-file": "Widget-Paket-Datei", - "invalid-widgets-bundle-file-error": "Widget-Paket kann nicht importiert werden: Ungültige Widget-Paket-Datenstruktur." - }, - "widget-config": { - "data": "Daten", - "settings": "Einstellungen", - "advanced": "Erweitert ", - "title": "Titel", - "title-tooltip": "Titel Tooltip", - "general-settings": "Allgemeine Einstellungen", - "display-title": "Titel anzeigen", - "drop-shadow": "Schlagschatten", - "enable-fullscreen": "Vollbild aktivieren", - "background-color": "Hintergrundfarbe", - "text-color": "Textfarbe", - "padding": "Pufferung", - "margin": "Rand", - "widget-style": "Widget-Stil", - "title-style": "Titel-Stil", - "mobile-mode-settings": "Einstellungen für den mobilen Modus", - "order": "Reihenfolge", - "height": "Größe", - "units": "Spezielles Symbol, das neben dem Wert angezeigt wird", - "decimals": "Anzahl der Stellen nach dem Fließkomma", - "timewindow": "Zeitfenster", - "use-dashboard-timewindow": "Dashboard-Zeitfenster verwenden", - "display-timewindow": "Zeitfenster anzeigen", - "display-legend": "Legende anzeigen", - "datasources": "Datenquellen", - "maximum-datasources": "Maximal { count, plural, =1 {1 Datenquelle ist erlaubt} other {# Datenquellen sind erlaubt} }.", - "datasource-type": "Typ", - "datasource-parameters": "Parameter", - "remove-datasource": "Datenquelle entfernen", - "add-datasource": "Datenquelle hinzufügen ", - "target-device": "Zielgerät", - "alarm-source": "Alarmquelle", - "actions": "Aktionen", - "action": "Aktion", - "add-action": "Aktion hinzufügen", - "search-actions": "Aktion suchen", - "action-source": "Aktionsquelle", - "action-source-required": "Aktionsquelle ist erforderlich.", - "action-name": "Name", - "action-name-required": "Aktionsname ist erforderlich.", - "action-name-not-unique": "Eine andere Aktion mit demselben Namen ist bereits vorhanden.\n Der Aktionsname sollte innerhalb derselben Aktionsquelle eindeutig sein.", - "action-icon": "Symbol ", - "action-type": "Art", - "action-type-required": "Aktionsart ist erforderlich.", - "edit-action": "Aktion bearbeiten", - "delete-action": "Aktion löschen", - "delete-action-title": "Widget-Aktion löschen", - "delete-action-text": "Möchten Sie die Widget-Aktion mit Namen '{{actionName}}' wirklich löschen?" - }, - "widget-type": { - "import": "Widget-Typ importieren", - "export": "Widget-Typ exportieren", - "export-failed-error": "Widget-Typ kann nicht exportiert werden: {{error}}", - "create-new-widget-type": "Neuen Widget-Typ erstellen", - "widget-type-file": "Widget-Typdatei", - "invalid-widget-type-file-error": "Widget-Typ kann nicht importiert werden: Ungültige Datenstruktur des Widget-Typs." - }, - "widgets": { - "date-range-navigator": { - "date-range-picker-settings": "Einstellungen Datumsbereichsauswahl", - "hide-date-range-picker": "Datumsbereichsauswahl verstecken", - "picker-one-panel": "Datumsbereichsauswahl in einem Bereich", - "picker-auto-confirm": "Automatische Bestätigung der Datumsbereichsauswahl", - "picker-show-template": "Vorlage für die Anzeige der Datumsbereichsauswahl", - "first-day-of-week": "Erster Tag der Woche", - "interval-settings": "Intervalleinstellungen", - "hide-interval": "Intervall verstecken", - "initial-interval": "Anfangsintervall", - "interval-hour": "Stunde", - "interval-day": "Tag", - "interval-week": "Woche", - "interval-two-weeks": "2 Wochen", - "interval-month": "Monat", - "interval-three-months": "3 Monate", - "interval-six-months": "6 Monate", - "step-settings": "Schritteinstellungen", - "hide-step-size": "Schrittweite verstecken", - "initial-step-size": "Anfangsschrittweite", - "hide-labels": "Beschriftung verstecken", - "use-session-storage": "Sessionspeicher verwenden", - "localizationMap": { - "Sun": "So.", - "Mon": "Mo.", - "Tue": "Di.", - "Wed": "Mi.", - "Thu": "Do.", - "Fri": "Fr.", - "Sat": "Sa.", - "Jan": "Jan.", - "Feb": "Feb.", - "Mar": "März", - "Apr": "Apr.", - "May": "Mai", - "Jun": "Juni", - "Jul": "Juli", - "Aug": "Aug.", - "Sep": "Sep.", - "Oct": "Okt.", - "Nov": "Nov.", - "Dec": "Dez.", - "January": "Januar", - "February": "Februar", - "March": "März", - "April": "April", - "June": "Juni", - "July": "Juli", - "August": "August", - "September": "September", - "October": "Oktober", - "November": "November", - "December": "Dezember", - "Custom Date Range": "Benutzerdefinierter Datumsbereich", - "Date Range Template": "Datumsbereichsvorlage", - "Today": "Heute", - "Yesterday": "Gestern", - "This Week": "Diese Woche", - "Last Week": "Letzte Woche", - "This Month": "Diesen Monat", - "Last Month": "Im vergangenen Monat", - "Year": "Jahr", - "This Year": "Dieses Jahr", - "Last Year": "Vergangenes Jahr", - "Date picker": "Datumsauswahl", - "Hour": "Stunde", - "Day": "Tag", - "Week": "Woche", - "2 weeks": "2 Wochen", - "Month": "Monat", - "3 months": "3 Monate", - "6 months": "6 Monate", - "Custom interval": "Benutzerdefiniertes Intervall", - "Interval": "Intervall", - "Step size": "Schrittlänge", - "Ok": "Ok" + "queue-statistics" : { + "queue-statistics" : "Warteschlangenstatistiken", + "no-queue-statistics-matching" : "Keine passenden Warteschlangenstatistiken für '{{entity}}' gefunden.", + "queue-statistics-required" : "Warteschlangenstatistiken sind erforderlich.", + "list-of-queue-statistics" : "{ count, plural, =1 {Eine Warteschlangenstatistik} other {Liste von # Warteschlangenstatistiken} }", + "selected-queue-statistics" : "{ count, plural, =1 {1 Warteschlangenstatistik} other {# Warteschlangenstatistiken} } ausgewählt", + "no-queue-statistics-text" : "Keine Warteschlangenstatistiken gefunden", + "queue-statistics-starts-with" : "Warteschlangenstatistiken, deren Name mit '{{prefix}}' beginnt" + }, + "server-error" : { + "general" : "Allgemeiner Serverfehler", + "authentication" : "Authentifizierungsfehler", + "jwt-token-expired" : "JWT-Token abgelaufen", + "tenant-trial-expired" : "Testzeitraum des Mieters abgelaufen", + "credentials-expired" : "Anmeldedaten abgelaufen", + "permission-denied" : "Zugriff verweigert", + "invalid-arguments" : "Ungültige Argumente", + "bad-request-params" : "Ungültige Anfrageparameter", + "item-not-found" : "Element nicht gefunden", + "too-many-requests" : "Zu viele Anfragen", + "too-many-updates" : "Zu viele Aktualisierungen" + }, + "tenant" : { + "tenant" : "Mieter", + "tenants" : "Mieter", + "management" : "Mieterverwaltung", + "add" : "Mieter hinzufügen", + "admins" : "Administratoren", + "manage-tenant-admins" : "Mieteradministratoren verwalten", + "delete" : "Mieter löschen", + "add-tenant-text" : "Neuen Mieter hinzufügen", + "no-tenants-text" : "Keine Mieter gefunden", + "tenant-details" : "Mieterdetails", + "title-max-length" : "Titel darf weniger als 256 Zeichen enthalten", + "delete-tenant-title" : "Sind Sie sicher, dass Sie den Mieter '{{tenantTitle}}' löschen möchten?", + "delete-tenant-text" : "Achtung! Nach der Bestätigung werden der Mieter und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-tenants-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Mieter} other {# Mieter} } löschen möchten?", + "delete-tenants-action-title" : "{ count, plural, =1 {1 Mieter löschen} other {# Mieter löschen} }", + "delete-tenants-text" : "Achtung! Nach der Bestätigung werden alle ausgewählten Mieter und ihre zugehörigen Daten gelöscht und können nicht wiederhergestellt werden.", + "title" : "Titel", + "title-required" : "Titel ist erforderlich.", + "description" : "Beschreibung", + "details" : "Details", + "events" : "Ereignisse", + "copyId" : "Mieter-ID kopieren", + "idCopiedMessage" : "Mieter-ID wurde in die Zwischenablage kopiert", + "select-tenant" : "Mieter auswählen", + "no-tenants-matching" : "Keine passenden Mieter für '{{entity}}' gefunden.", + "tenant-required" : "Mieter ist erforderlich", + "search" : "Mieter suchen", + "selected-tenants" : "{ count, plural, =1 {1 Mieter} other {# Mieter} } ausgewählt", + "isolated-tb-rule-engine" : "Isolierte ThingsBoard Rule Engine-Warteschlangen verwenden", + "isolated-tb-rule-engine-details" : "Jeder Mieter erhält dedizierte Rule Engine-Warteschlangen" + }, + "tenant-profile" : { + "tenant-profile" : "Mieterprofil", + "tenant-profiles" : "Mieterprofile", + "add" : "Mieterprofil hinzufügen", + "add-profile" : "Profil hinzufügen", + "debug" : "Debug", + "edit" : "Mieterprofil bearbeiten", + "tenant-profile-details" : "Details zum Mieterprofil", + "no-tenant-profiles-text" : "Keine Mieterprofile gefunden", + "name-max-length" : "Name darf weniger als 256 Zeichen enthalten", + "search" : "Mieterprofile suchen", + "selected-tenant-profiles" : "{ count, plural, =1 {1 Mieterprofil} other {# Mieterprofile} } ausgewählt", + "no-tenant-profiles-matching" : "Keine passenden Mieterprofile für '{{entity}}' gefunden.", + "tenant-profile-required" : "Mieterprofil ist erforderlich", + "idCopiedMessage" : "Mieterprofil-ID wurde in die Zwischenablage kopiert", + "set-default" : "Als Standard-Mieterprofil festlegen", + "delete" : "Mieterprofil löschen", + "copyId" : "Mieterprofil-ID kopieren", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "data" : "Profildaten", + "profile-configuration" : "Profilkonfiguration", + "description" : "Beschreibung", + "default" : "Standard", + "delete-tenant-profile-title" : "Sind Sie sicher, dass Sie das Mieterprofil '{{tenantProfileName}}' löschen möchten?", + "delete-tenant-profile-text" : "Achtung! Nach der Bestätigung wird das Mieterprofil und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-tenant-profiles-title" : "Sind Sie sicher, dass Sie { count, plural, =1 {1 Mieterprofil} other {# Mieterprofile} } löschen möchten?", + "delete-tenant-profiles-text" : "Achtung! Nach der Bestätigung werden alle ausgewählten Mieterprofile gelöscht und zugehörige Daten können nicht wiederhergestellt werden.", + "set-default-tenant-profile-title" : "Sind Sie sicher, dass Sie das Mieterprofil '{{tenantProfileName}}' als Standard festlegen möchten?", + "set-default-tenant-profile-text" : "Nach der Bestätigung wird das Mieterprofil als Standard markiert und für neue Mieter ohne spezifisches Profil verwendet.", + "no-tenant-profiles-found" : "Keine Mieterprofile gefunden.", + "create-new-tenant-profile" : "Neues erstellen!", + "create-tenant-profile" : "Neues Mieterprofil erstellen", + "import" : "Mieterprofil importieren", + "export" : "Mieterprofil exportieren", + "export-failed-error" : "Mieterprofil konnte nicht exportiert werden: {{error}}", + "tenant-profile-file" : "Mieterprofil-Datei", + "invalid-tenant-profile-file-error" : "Import fehlgeschlagen: Ungültige Datenstruktur des Mieterprofils.", + "advanced-settings" : "Erweiterte Einstellungen", + "entities" : "Entitäten", + "rule-engine" : "Rule Engine", + "time-to-live" : "Lebensdauer", + "calculated-fields" : "Berechnete Felder", + "alarms-and-notifications" : "Alarme und Benachrichtigungen", + "ota-files-in-bytes" : "Dateien", + "ws-title" : "WS", + "unlimited" : "(0 – unbegrenzt)", + "maximum-devices" : "Maximale Anzahl Geräte", + "maximum-devices-required" : "Maximale Anzahl Geräte ist erforderlich.", + "maximum-devices-range" : "Maximale Anzahl Geräte darf nicht negativ sein", + "maximum-assets" : "Maximale Anzahl Assets", + "maximum-assets-required" : "Maximale Anzahl Assets ist erforderlich.", + "maximum-assets-range" : "Maximale Anzahl Assets darf nicht negativ sein", + "maximum-customers" : "Maximale Anzahl Kunden", + "maximum-customers-required" : "Maximale Anzahl Kunden ist erforderlich.", + "maximum-customers-range" : "Maximale Anzahl Kunden darf nicht negativ sein", + "maximum-users" : "Maximale Anzahl Benutzer", + "maximum-users-required" : "Maximale Anzahl Benutzer ist erforderlich.", + "maximum-users-range" : "Maximale Anzahl Benutzer darf nicht negativ sein", + "maximum-dashboards" : "Maximale Anzahl Dashboards", + "maximum-dashboards-required" : "Maximale Anzahl Dashboards ist erforderlich.", + "maximum-dashboards-range" : "Maximale Anzahl Dashboards darf nicht negativ sein", + "maximum-edges" : "Maximale Anzahl Edges", + "maximum-edges-required" : "Maximale Anzahl Edges ist erforderlich.", + "maximum-edges-range" : "Maximale Anzahl Edges darf nicht negativ sein", + "maximum-rule-chains" : "Maximale Anzahl Regelketten", + "maximum-rule-chains-required" : "Maximale Anzahl Regelketten ist erforderlich.", + "maximum-rule-chains-range" : "Maximale Anzahl Regelketten darf nicht negativ sein", + "maximum-resources-sum-data-size" : "Maximale Gesamtgröße der Ressourcendateien (Byte)", + "maximum-resources-sum-data-size-required" : "Maximale Gesamtgröße der Ressourcendateien ist erforderlich.", + "maximum-resources-sum-data-size-range" : "Maximale Gesamtgröße der Ressourcendateien darf nicht negativ sein", + "maximum-resource-size" : "Maximale Ressourcendateigröße (Byte)", + "maximum-resource-size-required" : "Maximale Ressourcendateigröße ist erforderlich.", + "maximum-resource-size-range" : "Maximale Ressourcendateigröße darf nicht negativ sein", + "maximum-ota-packages-sum-data-size" : "Maximale Gesamtgröße der OTA-Paketdateien (Byte)", + "maximum-ota-package-sum-data-size-required" : "Maximale Gesamtgröße der OTA-Paketdateien ist erforderlich.", + "maximum-ota-package-sum-data-size-range" : "Maximale Gesamtgröße der OTA-Paketdateien darf nicht negativ sein", + "maximum-debug-duration-min" : "Maximale Debug-Dauer (Minuten)", + "maximum-debug-duration-min-range" : "Maximale Debug-Dauer darf nicht negativ sein", + "rest-requests-for-tenant" : "REST-Anfragen für Mieter", + "transport-tenant-telemetry-msg-rate-limit" : "Transport-Telemetrie-Nachrichten des Mieters", + "transport-tenant-telemetry-data-points-rate-limit" : "Transport-Telemetrie-Datenpunkte des Mieters", + "transport-device-msg-rate-limit" : "Transport-Gerätenachrichten", + "transport-device-telemetry-msg-rate-limit" : "Transport-Gerätetelemetrie-Nachrichten", + "transport-device-telemetry-data-points-rate-limit" : "Transport-Gerätetelemetrie-Datenpunkte", + "transport-gateway-msg-rate-limit" : "Transport-Gateway-Nachrichten", + "transport-gateway-telemetry-msg-rate-limit" : "Transport-Gateway-Telemetrie-Nachrichten", + "transport-gateway-telemetry-data-points-rate-limit" : "Transport-Gateway-Telemetrie-Datenpunkte", + "transport-gateway-device-msg-rate-limit" : "Transport-Gateway-Gerätenachrichten", + "transport-gateway-device-telemetry-msg-rate-limit" : "Transport-Gateway-Gerätetelemetrie-Nachrichten", + "transport-gateway-device-telemetry-data-points-rate-limit" : "Transport-Gateway-Gerätetelemetrie-Datenpunkte", + "tenant-entity-export-rate-limit" : "Export von Entitätsversionen", + "tenant-entity-import-rate-limit" : "Import von Entitätsversionen", + "tenant-notification-request-rate-limit" : "Benachrichtigungsanfragen", + "tenant-notification-requests-per-rule-rate-limit" : "Benachrichtigungsanfragen pro Regel", + "max-calculated-fields" : "Maximale berechnete Felder pro Entität", + "max-calculated-fields-range" : "Maximale Anzahl berechneter Felder darf nicht negativ sein", + "max-calculated-fields-required" : "Maximale Anzahl berechneter Felder ist erforderlich", + "max-data-points-per-rolling-arg" : "Maximale Anzahl Datenpunkte in rollierenden Argumenten", + "max-data-points-per-rolling-arg-range" : "Maximale Anzahl Datenpunkte in rollierenden Argumenten darf nicht negativ sein", + "max-data-points-per-rolling-arg-required" : "Maximale Anzahl Datenpunkte in rollierenden Argumenten ist erforderlich", + "max-arguments-per-cf" : "Maximale Argumente pro berechnetem Feld", + "max-arguments-per-cf-range" : "Maximale Argumente pro berechnetem Feld dürfen nicht negativ sein", + "max-arguments-per-cf-required" : "Maximale Argumente pro berechnetem Feld sind erforderlich", + "max-state-size" : "Maximale Zustandsgröße in KB", + "max-state-size-range" : "Maximale Zustandsgröße darf nicht negativ sein", + "max-state-size-required" : "Maximale Zustandsgröße ist erforderlich", + "max-value-argument-size" : "Maximale Größe eines Einzelwert-Arguments in KB", + "max-value-argument-size-range" : "Größe eines Einzelwert-Arguments darf nicht negativ sein", + "max-value-argument-size-required" : "Größe eines Einzelwert-Arguments ist erforderlich", + "max-transport-messages" : "Maximale Transportnachrichten", + "max-transport-messages-required" : "Maximale Transportnachrichten sind erforderlich.", + "max-transport-messages-range" : "Maximale Transportnachrichten dürfen nicht negativ sein", + "max-transport-data-points" : "Maximale Transportdatenpunkte", + "max-transport-data-points-required" : "Maximale Transportdatenpunkte sind erforderlich.", + "max-transport-data-points-range" : "Maximale Transportdatenpunkte dürfen nicht negativ sein", + "max-r-e-executions" : "Maximale Rule Engine Ausführungen", + "max-r-e-executions-required" : "Maximale Rule Engine Ausführungen sind erforderlich.", + "max-r-e-executions-range" : "Maximale Rule Engine Ausführungen dürfen nicht negativ sein", + "max-j-s-executions" : "Maximale JavaScript-Ausführungen", + "max-j-s-executions-required" : "Maximale JavaScript-Ausführungen sind erforderlich.", + "max-j-s-executions-range" : "Maximale JavaScript-Ausführungen dürfen nicht negativ sein", + "max-tbel-executions" : "Maximale TBEL-Ausführungen", + "max-tbel-executions-required" : "Maximale TBEL-Ausführungen sind erforderlich.", + "max-tbel-executions-range" : "Maximale TBEL-Ausführungen dürfen nicht negativ sein", + "max-d-p-storage-days" : "Maximale Speicherzeit für Datenpunkte (Tage)", + "max-d-p-storage-days-required" : "Maximale Speicherzeit für Datenpunkte ist erforderlich.", + "max-d-p-storage-days-range" : "Maximale Speicherzeit für Datenpunkte darf nicht negativ sein", + "default-storage-ttl-days" : "Standardmäßige Speicherzeit (Tage)", + "default-storage-ttl-days-required" : "Standardmäßige Speicherzeit ist erforderlich.", + "default-storage-ttl-days-range" : "Standardmäßige Speicherzeit darf nicht negativ sein", + "alarms-ttl-days" : "Alarme Speicherzeit (Tage)", + "alarms-ttl-days-required" : "Alarme Speicherzeit ist erforderlich", + "alarms-ttl-days-days-range" : "Alarme Speicherzeit darf nicht negativ sein", + "rpc-ttl-days" : "RPC Speicherzeit (Tage)", + "rpc-ttl-days-required" : "RPC Speicherzeit ist erforderlich", + "rpc-ttl-days-days-range" : "RPC Speicherzeit darf nicht negativ sein", + "queue-stats-ttl-days" : "Warteschlangenstatistik Speicherzeit (Tage)", + "queue-stats-ttl-days-required" : "Warteschlangenstatistik Speicherzeit ist erforderlich", + "queue-stats-ttl-days-range" : "Warteschlangenstatistik Speicherzeit darf nicht negativ sein", + "rule-engine-exceptions-ttl-days" : "Rule Engine Ausnahme Speicherzeit (Tage)", + "rule-engine-exceptions-ttl-days-required" : "Rule Engine Ausnahme Speicherzeit ist erforderlich", + "rule-engine-exceptions-ttl-days-range" : "Rule Engine Ausnahme Speicherzeit darf nicht negativ sein", + "max-rule-node-executions-per-message" : "Maximale Regelknotenausführungen pro Nachricht", + "max-rule-node-executions-per-message-required" : "Maximale Regelknotenausführungen pro Nachricht sind erforderlich.", + "max-rule-node-executions-per-message-range" : "Maximale Regelknotenausführungen pro Nachricht dürfen nicht negativ sein", + "max-emails" : "Maximale gesendete E-Mails", + "max-emails-required" : "Maximale gesendete E-Mails sind erforderlich.", + "max-emails-range" : "Maximale gesendete E-Mails dürfen nicht negativ sein", + "sms-enabled" : "SMS aktiviert", + "max-sms" : "Maximale gesendete SMS", + "max-sms-required" : "Maximale gesendete SMS sind erforderlich.", + "max-sms-range" : "Maximale gesendete SMS dürfen nicht negativ sein", + "max-created-alarms" : "Maximale Anzahl erstellter Alarme", + "max-created-alarms-required" : "Maximale Anzahl erstellter Alarme ist erforderlich.", + "max-created-alarms-range" : "Maximale Anzahl erstellter Alarme darf nicht negativ sein", + "no-queue" : "Keine Warteschlange konfiguriert", + "add-queue" : "Warteschlange hinzufügen", + "queues-with-count" : "Warteschlangen ({{count}})", + "tenant-rest-limits" : "REST-Anfragen für Mieter", + "customer-rest-limits" : "REST-Anfragen für Kunden", + "incorrect-pattern-for-rate-limits" : "Das Format ist durch Kommas getrennte Paare aus Kapazität und Zeitraum (in Sekunden), z. B. 100:1,2000:60", + "too-small-value-zero" : "Der Wert muss größer als 0 sein", + "too-small-value-one" : "Der Wert muss größer als 1 sein", + "queue-size-is-limited-by-system-configuration" : "Die Größe der Warteschlange ist auch durch die Systemkonfiguration begrenzt.", + "cassandra-tenant-limits-configuration" : "Cassandra-Abfrage für Mieter", + "ws-limit-max-sessions-per-tenant" : "Maximale Sitzungen pro Mieter", + "ws-limit-max-sessions-per-customer" : "Maximale Sitzungen pro Kunde", + "ws-limit-max-sessions-per-regular-user" : "Maximale Sitzungen pro normalem Benutzer", + "ws-limit-max-sessions-per-public-user" : "Maximale Sitzungen pro öffentlichem Benutzer", + "ws-limit-queue-per-session" : "Maximale Nachrichtenwarteschlange pro Sitzung", + "ws-limit-max-subscriptions-per-tenant" : "Maximale Abonnements pro Mieter", + "ws-limit-max-subscriptions-per-customer" : "Maximale Abonnements pro Kunde", + "ws-limit-max-subscriptions-per-regular-user" : "Maximale Abonnements pro normalem Benutzer", + "ws-limit-max-subscriptions-per-public-user" : "Maximale Abonnements pro öffentlichem Benutzer", + "ws-limit-updates-per-session" : "WebSocket-Aktualisierungen pro Sitzung", + "rate-limits" : { + "add-limit" : "Limit hinzufügen", + "advanced-settings" : "Erweiterte Einstellungen", + "edit-limit" : "Limit bearbeiten", + "but-less-than" : "aber weniger als", + "calculated-field-debug-event-rate-limit" : "Berechnete Feld-Debug-Ereignisse", + "edit-calculated-field-debug-event-rate-limit" : "Limit für berechnete Feld-Debug-Ereignisse bearbeiten", + "edit-transport-tenant-msg-title" : "Limit für Transport-Mieter-Nachrichten bearbeiten", + "edit-transport-tenant-telemetry-msg-title" : "Limit für Transport-Mieter-Telemetrienachrichten bearbeiten", + "edit-transport-tenant-telemetry-data-points-title" : "Limit für Transport-Mieter-Telemetriedatenpunkte bearbeiten", + "edit-transport-device-msg-title" : "Limit für Transport-Gerätenachrichten bearbeiten", + "edit-transport-device-telemetry-msg-title" : "Limit für Transport-Gerätetelemetrienachrichten bearbeiten", + "edit-transport-device-telemetry-data-points-title" : "Limit für Transport-Gerätetelemetriedatenpunkte bearbeiten", + "edit-transport-gateway-msg-title" : "Limit für Transport-Gateway-Nachrichten bearbeiten", + "edit-transport-gateway-telemetry-msg-title" : "Limit für Transport-Gateway-Telemetrienachrichten bearbeiten", + "edit-transport-gateway-telemetry-data-points-title" : "Limit für Transport-Gateway-Telemetriedatenpunkte bearbeiten", + "edit-transport-gateway-device-msg-title" : "Limit für Transport-Gateway-Gerätenachrichten bearbeiten", + "edit-transport-gateway-device-telemetry-msg-title" : "Limit für Transport-Gateway-Gerätetelemetrienachrichten bearbeiten", + "edit-transport-gateway-device-telemetry-data-points-title" : "Limit für Transport-Gateway-Gerätetelemetriedatenpunkte bearbeiten", + "edit-tenant-rest-limits-title" : "REST-Anfragelimits für Mieter bearbeiten", + "edit-customer-rest-limits-title" : "REST-Anfragelimits für Kunden bearbeiten", + "edit-ws-limit-updates-per-session-title" : "Limit für WebSocket-Aktualisierungen pro Sitzung bearbeiten", + "edit-cassandra-tenant-limits-configuration-title" : "Cassandra-Abfrage-Limits für Mieter bearbeiten", + "edit-tenant-entity-export-rate-limit-title" : "Limit für Entitätsversionserstellung bearbeiten", + "edit-tenant-entity-import-rate-limit-title" : "Limit für Entitätsversionsladevorgang bearbeiten", + "edit-tenant-notification-request-rate-limit-title" : "Limit für Benachrichtigungsanfragen bearbeiten", + "edit-tenant-notification-requests-per-rule-rate-limit-title" : "Limit für Benachrichtigungsanfragen pro Regel bearbeiten", + "edit-edge-events-rate-limit" : "Limit für Edge-Ereignisse bearbeiten", + "edit-edge-events-per-edge-rate-limit" : "Limit für Edge-Ereignisse pro Edge bearbeiten", + "edge-events-rate-limit" : "Edge-Ereignisse", + "edge-events-per-edge-rate-limit" : "Edge-Ereignisse pro Edge", + "edit-edge-uplink-messages-rate-limit" : "Limit für Edge-Uplink-Nachrichten bearbeiten", + "edit-edge-uplink-messages-per-edge-rate-limit" : "Limit für Edge-Uplink-Nachrichten pro Edge bearbeiten", + "edge-uplink-messages-rate-limit" : "Edge-Uplink-Nachrichten", + "edge-uplink-messages-per-edge-rate-limit" : "Edge-Uplink-Nachrichten pro Edge", + "messages-per" : "Nachrichten pro", + "not-set" : "Nicht festgelegt", + "number-of-messages" : "Anzahl der Nachrichten", + "number-of-messages-required" : "Anzahl der Nachrichten ist erforderlich.", + "number-of-messages-min" : "Minimalwert ist 1.", + "preview" : "Vorschau", + "per-seconds" : "Pro Sekunde", + "per-seconds-required" : "Zeitintervall ist erforderlich.", + "per-seconds-min" : "Minimalwert ist 1.", + "rate-limits" : "Ratenlimits", + "remove-limit" : "Limit entfernen", + "transport-tenant-msg" : "Transport-Mieter-Nachrichten", + "transport-tenant-telemetry-msg" : "Transport-Mieter-Telemetrienachrichten", + "transport-tenant-telemetry-data-points" : "Transport-Mieter-Telemetriedatenpunkte", + "transport-device-msg" : "Transport-Gerätenachrichten", + "transport-device-telemetry-msg" : "Transport-Gerätetelemetrienachrichten", + "transport-device-telemetry-data-points" : "Transport-Gerätetelemetriedatenpunkte", + "transport-gateway-msg" : "Transport-Gateway-Nachrichten", + "transport-gateway-telemetry-msg" : "Transport-Gateway-Telemetrienachrichten", + "transport-gateway-telemetry-data-points" : "Transport-Gateway-Telemetriedatenpunkte", + "transport-gateway-device-msg" : "Transport-Gateway-Gerätenachrichten", + "transport-gateway-device-telemetry-msg" : "Transport-Gateway-Gerätetelemetrienachrichten", + "transport-gateway-device-telemetry-data-points" : "Transport-Gateway-Gerätetelemetriedatenpunkte", + "sec" : "Sek." + } + }, + "timeinterval" : { + "seconds-interval" : "{ seconds, plural, =1 {1 Sekunde} other {# Sekunden} }", + "minutes-interval" : "{ minutes, plural, =1 {1 Minute} other {# Minuten} }", + "hours-interval" : "{ hours, plural, =1 {1 Stunde} other {# Stunden} }", + "days-interval" : "{ days, plural, =1 {1 Tag} other {# Tage} }", + "days" : "Tage", + "hours" : "Stunden", + "minutes" : "Minuten", + "seconds" : "Sekunden", + "advanced" : "Erweitert", + "custom" : "Benutzerdefiniert", + "predefined" : { + "yesterday" : "Gestern", + "day-before-yesterday" : "Vorgestern", + "this-day-last-week" : "Dieser Tag letzte Woche", + "previous-week" : "Vorherige Woche (So - Sa)", + "previous-week-iso" : "Vorherige Woche (Mo - So)", + "previous-month" : "Vorheriger Monat", + "previous-quarter" : "Vorheriges Quartal", + "previous-half-year" : "Vorheriges Halbjahr", + "previous-year" : "Vorheriges Jahr", + "current-hour" : "Aktuelle Stunde", + "current-day" : "Aktueller Tag", + "current-day-so-far" : "Bisheriger aktueller Tag", + "current-week" : "Aktuelle Woche (So - Sa)", + "current-week-iso" : "Aktuelle Woche (Mo - So)", + "current-week-so-far" : "Bisherige aktuelle Woche (So - Sa)", + "current-week-iso-so-far" : "Bisherige aktuelle Woche (Mo - So)", + "current-month" : "Aktueller Monat", + "current-month-so-far" : "Bisheriger aktueller Monat", + "current-quarter" : "Aktuelles Quartal", + "current-quarter-so-far" : "Bisheriges aktuelles Quartal", + "current-half-year" : "Aktuelles Halbjahr", + "current-half-year-so-far" : "Bisheriges aktuelles Halbjahr", + "current-year" : "Aktuelles Jahr", + "current-year-so-far" : "Bisheriges aktuelles Jahr" + }, + "type" : { + "week" : "Woche (So - Sa)", + "week-iso" : "Woche (Mo - So)", + "month" : "Monat", + "quarter" : "Quartal" + } + }, + "timeunit" : { + "milliseconds" : "Millisekunden", + "seconds" : "Sekunden", + "minutes" : "Minuten", + "hours" : "Stunden", + "days" : "Tage" + }, + "timewindow" : { + "timewindow" : "Zeitfenster", + "timewindow-settings" : "Zeitfenster-Einstellungen", + "years" : "{ years, plural, =1 { Jahr } other {# Jahre } }", + "years-short" : "{{ years }}J", + "months" : "{ months, plural, =1 { Monat } other {# Monate } }", + "months-short" : "{{ months }}M", + "weeks" : "{ weeks, plural, =1 { Woche } other {# Wochen } }", + "weeks-short" : "{{ weeks }}W", + "days" : "{ days, plural, =1 { Tag } other {# Tage } }", + "days-short" : "{{ days }}T", + "hours" : "{ hours, plural, =0 { Stunde } =1 {1 Stunde } other {# Stunden } }", + "hr" : "{{ hr }} Std.", + "hr-short" : "{{ hr }}h", + "minutes" : "{ minutes, plural, =0 { Minute } =1 {1 Minute } other {# Minuten } }", + "min" : "{{ min }} Min.", + "min-short" : "{{ min }}m", + "seconds" : "{ seconds, plural, =0 { Sekunde } =1 {1 Sekunde } other {# Sekunden } }", + "sec" : "{{ sec }} Sek.", + "sec-short" : "{{ sec }}s", + "short" : { + "years" : "{ years, plural, =1 {1 Jahr } other {# Jahre } }", + "days" : "{ days, plural, =1 {1 Tag } other {# Tage } }", + "hours" : "{ hours, plural, =1 {1 Stunde } other {# Stunden } }", + "minutes" : "{{minutes}} Min.", + "seconds" : "{{seconds}} Sek." + }, + "realtime" : "Echtzeit", + "history" : "Verlauf", + "last-prefix" : "letzte", + "period" : "von {{ startTime }} bis {{ endTime }}", + "edit" : "Zeitfenster bearbeiten", + "date-range" : "Datumsbereich", + "for-all-time" : "Für gesamten Zeitraum", + "last" : "Letzte", + "time-period" : "Zeitraum", + "hide" : "Ausblenden", + "interval" : "Intervall", + "just-now" : "Gerade eben", + "just-now-lower" : "gerade eben", + "ago" : "her", + "style" : "Zeitfenster-Stil", + "icon" : "Symbol", + "icon-position" : "Symbolposition", + "icon-position-left" : "Links", + "icon-position-right" : "Rechts", + "font" : "Schriftart", + "color" : "Farbe", + "displayTypePrefix" : "Echtzeit/Verlauf-Präfix anzeigen", + "preview" : "Vorschau", + "relative" : "Relativ", + "range" : "Bereich", + "hide-timewindow-section" : "Zeitfensterbereich für Endbenutzer ausblenden", + "hide-last-interval" : "Letztes Intervall für Endbenutzer ausblenden", + "hide-relative-interval" : "Relatives Intervall für Endbenutzer ausblenden", + "hide-fixed-interval" : "Festes Intervall für Endbenutzer ausblenden", + "hide-aggregation" : "Aggregation für Endbenutzer ausblenden", + "hide-group-interval" : "Gruppierungsintervall für Endbenutzer ausblenden", + "hide-max-values" : "Maximale Werte für Endbenutzer ausblenden", + "hide-timezone" : "Zeitzone für Endbenutzer ausblenden", + "disable-custom-interval" : "Benutzerdefiniertes Intervall deaktivieren", + "edit-aggregation-functions-list" : "Liste der Aggregationsfunktionen bearbeiten", + "edit-aggregation-functions-list-hint" : "Liste verfügbarer Optionen kann angegeben werden.", + "allowed-aggregation-functions" : "Erlaubte Aggregationsfunktionen", + "edit-intervals-list" : "Intervallliste bearbeiten", + "allowed-agg-intervals" : "Gruppierungsintervalle", + "default-agg-interval" : "Standard-Gruppierungsintervall", + "edit-intervals-list-hint" : "Liste verfügbarer Intervalloptionen kann angegeben werden.", + "edit-grouping-intervals-list-hint" : "Gruppierungsintervallliste und Standardintervall konfigurierbar.", + "all" : "Alle" + }, + "tooltip" : { + "trigger" : "Auslöser", + "trigger-point" : "Punkt", + "trigger-axis" : "Achse", + "label" : "Beschriftung", + "value" : "Wert", + "date" : "Datum", + "show-date-time-interval" : "Datum/Zeit-Intervall anzeigen", + "show-date-time-interval-hint" : "Datum/Zeit-Intervall gemäß Datenaggregation anzeigen.", + "background-color" : "Hintergrundfarbe", + "background-blur" : "Hintergrundunschärfe" + }, + "unit" : { + "millimeter" : "Millimeter", + "centimeter" : "Zentimeter", + "angstrom" : "Angström", + "nanometer" : "Nanometer", + "micrometer" : "Mikrometer", + "meter" : "Meter", + "kilometer" : "Kilometer", + "inch" : "Zoll", + "foot" : "Fuß", + "yard" : "Yard", + "mile" : "Meile", + "nautical-mile" : "Seemeile", + "astronomical-unit" : "Astronomische Einheit", + "reciprocal-metre" : "Reziproker Meter", + "meter-per-meter" : "Meter pro Meter", + "steradian" : "Steradiant", + "thou" : "Thou", + "barleycorn" : "Gerstenkorn", + "hand" : "Handbreit", + "chain" : "Kette", + "furlong" : "Furlong", + "league" : "Leuge", + "fathom" : "Faden", + "cable" : "Kabellänge", + "link" : "Glied", + "rod" : "Rute", + "nanogram" : "Nanogramm", + "microgram" : "Mikrogramm", + "milligram" : "Milligramm", + "gram" : "Gramm", + "kilogram" : "Kilogramm", + "tonne" : "Tonne", + "ounce" : "Unze", + "pound" : "Pfund", + "stone" : "Stone", + "hundredweight-count" : "Zentner", + "short-tons" : "US-Tonnen", + "dalton" : "Dalton", + "grain" : "Grain", + "drachm" : "Drachme", + "quarter" : "Quarter", + "slug" : "Slug", + "carat" : "Karat", + "cubic-millimeter" : "Kubikmillimeter", + "cubic-centimeter" : "Kubikzentimeter", + "cubic-meter" : "Kubikmeter", + "cubic-kilometer" : "Kubikkilometer", + "microliter" : "Mikroliter", + "milliliter" : "Milliliter", + "liter" : "Liter", + "hectoliter" : "Hektoliter", + "cubic-inch" : "Kubikzoll", + "cubic-foot" : "Kubikfuß", + "cubic-yard" : "Kubikyard", + "fluid-ounce" : "Flüssigunze", + "pint" : "Pint", + "quart" : "Quart", + "gallon" : "Gallone", + "oil-barrels" : "Ölfass", + "cubic-meter-per-kilogram" : "Kubikmeter pro Kilogramm", + "gill" : "Gill", + "hogshead" : "Hogshead", + "teaspoon" : "Teelöffel", + "tablespoon" : "Esslöffel", + "cup" : "Tasse", + "celsius" : "Celsius", + "kelvin" : "Kelvin", + "rankine" : "Rankine", + "fahrenheit" : "Fahrenheit", + "percent" : "Prozent", + "meter-per-second" : "Meter pro Sekunde", + "kilometer-per-hour" : "Kilometer pro Stunde", + "foot-per-second" : "Fuß pro Sekunde", + "mile-per-hour" : "Meile pro Stunde", + "knot" : "Knoten", + "millimeters-per-minute" : "Millimeter pro Minute", + "kilometer-per-hour-squared" : "Kilometer pro Stunde zum Quadrat", + "foot-per-second-squared" : "Fuß pro Sekunde zum Quadrat", + "pascal" : "Pascal", + "kilopascal" : "Kilopascal", + "megapascal" : "Megapascal", + "gigapascal" : "Gigapascal", + "millibar" : "Millibar", + "bar" : "Bar", + "kilobar" : "Kilobar", + "newton" : "Newton", + "newton-meter" : "Newtonmeter", + "foot-pounds" : "Fuß-Pfund", + "inch-pounds" : "Zoll-Pfund", + "newton-per-meter" : "Newton pro Meter", + "atmospheres" : "Atmosphären", + "pounds-per-square-inch" : "Pfund pro Quadratzoll", + "torr" : "Torr", + "inches-of-mercury" : "Zoll Quecksilbersäule", + "pascal-per-square-meter" : "Pascal pro Quadratmeter", + "pound-per-square-inch" : "Pfund pro Quadratzoll", + "newton-per-square-meter" : "Newton pro Quadratmeter", + "kilogram-force-per-square-meter" : "Kilopond pro Quadratmeter", + "pascal-per-square-centimeter" : "Pascal pro Quadratzentimeter", + "ton-force-per-square-inch" : "Tonnenkraft pro Quadratzoll", + "kilonewton-per-square-meter" : "Kilonewton pro Quadratmeter", + "newton-per-square-millimeter" : "Newton pro Quadratmillimeter", + "microjoule" : "Mikrojoule", + "millijoule" : "Millijoule", + "joule" : "Joule", + "kilojoule" : "Kilojoule", + "megajoule" : "Megajoule", + "gigajoule" : "Gigajoule", + "watt-hour" : "Wattstunde", + "kilowatt-hour" : "Kilowattstunde", + "electron-volts" : "Elektronenvolt", + "joules-per-coulomb" : "Joule pro Coulomb", + "british-thermal-unit" : "Britische Wärmeeinheit (BTU)", + "foot-pound" : "Fuß-Pfund", + "calorie" : "Kalorie", + "small-calorie" : "Kleine Kalorie", + "kilocalorie" : "Kilokalorie", + "joule-per-kelvin" : "Joule pro Kelvin", + "joule-per-kilogram-kelvin" : "Joule pro Kilogramm-Kelvin", + "joule-per-kilogram" : "Joule pro Kilogramm", + "watt-per-meter-kelvin" : "Watt pro Meter-Kelvin", + "joule-per-cubic-meter" : "Joule pro Kubikmeter", + "therm" : "Therm", + "electric-dipole-moment" : "Elektrisches Dipolmoment", + "magnetic-dipole-moment" : "Magnetisches Dipolmoment", + "debye" : "Debye", + "coulomb-per-square-meter-per-volt" : "Coulomb pro Quadratmeter pro Volt", + "milliwatt" : "Milliwatt", + "microwatt" : "Mikrowatt", + "watt" : "Watt", + "kilowatt" : "Kilowatt", + "megawatt" : "Megawatt", + "gigawatt" : "Gigawatt", + "metric-horsepower" : "Metrische Pferdestärke", + "milliwatt-per-square-centimeter" : "Milliwatt pro Quadratzentimeter", + "watt-per-square-centimeter" : "Watt pro Quadratzentimeter", + "kilowatt-per-square-centimeter" : "Kilowatt pro Quadratzentimeter", + "milliwatt-per-square-meter" : "Milliwatt pro Quadratmeter", + "watt-per-square-meter" : "Watt pro Quadratmeter", + "kilowatt-per-square-meter" : "Kilowatt pro Quadratmeter", + "watt-per-square-inch" : "Watt pro Quadratzoll", + "kilowatt-per-square-inch" : "Kilowatt pro Quadratzoll", + "horsepower" : "Pferdestärke", + "btu-per-hour" : "BTU pro Stunde", + "coulomb" : "Coulomb", + "millicoulomb" : "Millicoulomb", + "microcoulomb" : "Mikrocoulomb", + "picocoulomb" : "Picocoulomb", + "coulomb-per-meter" : "Coulomb pro Meter", + "coulomb-per-cubic-meter" : "Coulomb pro Kubikmeter", + "coulomb-per-square-meter" : "Coulomb pro Quadratmeter", + "square-millimeter" : "Quadratmillimeter", + "square-centimeter" : "Quadratzentimeter", + "square-meter" : "Quadratmeter", + "hectare" : "Hektar", + "square-kilometer" : "Quadratkilometer", + "square-inch" : "Quadratzoll", + "square-foot" : "Quadratfuß", + "square-yard" : "Quadratyard", + "acre" : "Acre", + "square-mile" : "Quadratmeile", + "are" : "Ar", + "barn" : "Barn", + "circular-inch" : "Kreiszoll", + "milliampere-hour" : "Milliampere-Stunde", + "ampere-hours" : "Amperestunden", + "kiloampere-hours" : "Kiloamperestunden", + "nanoampere" : "Nanoampere", + "picoampere" : "Picoampere", + "microampere" : "Mikroampere", + "milliampere" : "Milliampere", + "ampere" : "Ampere", + "microampere-per-square-centimeter" : "Mikroampere pro Quadratzentimeter", + "ampere-per-square-meter" : "Ampere pro Quadratmeter", + "ampere-per-meter" : "Ampere pro Meter", + "oersted" : "Oersted", + "bohr-magneton" : "Bohrsche Magneton", + "ampere-meter-squared" : "Ampere-Quadratmeter", + "nanovolt" : "Nanovolt", + "picovolt" : "Picovolt", + "volt" : "Volt", + "dbmV" : "dBmV", + "dbm" : "dBm", + "volt-meter" : "Volt-Meter", + "kilovolt-meter" : "Kilovolt-Meter", + "megavolt-meter" : "Megavolt-Meter", + "microvolt-meter" : "Mikrovolt-Meter", + "millivolt-meter" : "Millivolt-Meter", + "nanovolt-meter" : "Nanovolt-Meter", + "ohm" : "Ohm", + "microohm" : "Mikroohm", + "milliohm" : "Milliohm", + "kilohm" : "Kiloohm", + "megohm" : "Megaohm", + "gigohm" : "Gigaohm", + "hertz" : "Hertz", + "kilohertz" : "Kilohertz", + "megahertz" : "Megahertz", + "gigahertz" : "Gigahertz", + "rpm" : "Umdrehungen pro Minute", + "candela-per-square-meter" : "Candela pro Quadratmeter", + "candela" : "Candela", + "lumen" : "Lumen", + "lux" : "Lux", + "foot-candle" : "Fußkerze", + "lumen-per-square-meter" : "Lumen pro Quadratmeter", + "lux-second" : "Lux-Sekunde", + "lumen-second" : "Lumen-Sekunde", + "lumens-per-watt" : "Lumen pro Watt", + "mole" : "Mol", + "nanomole" : "Nanomol", + "micromole" : "Mikromol", + "millimole" : "Millimol", + "kilomole" : "Kilomol", + "mole-per-cubic-meter" : "Mol pro Kubikmeter", + "rssi" : "RSSI", + "ppm" : "Teile pro Million", + "ppb" : "Teile pro Milliarde", + "micrograms-per-cubic-meter" : "Mikrogramm pro Kubikmeter", + "aqi" : "Luftqualitätsindex (AQI)", + "gram-per-cubic-meter" : "Gramm pro Kubikmeter", + "gram-per-kilogram" : "Spezifische Feuchtigkeit", + "millimeters-per-second" : "Millimeter pro Sekunde", + "neper" : "Neper", + "bel" : "Bel", + "decibel" : "Dezibel", + "meters-per-second-squared" : "Meter pro Sekunde zum Quadrat", + "becquerel" : "Becquerel", + "curie" : "Curie", + "gray" : "Gray", + "sievert" : "Sievert", + "roentgen" : "Röntgen", + "cps" : "Zählungen pro Sekunde", + "rad" : "Rad", + "rem" : "Rem", + "dps" : "Zerfälle pro Sekunde", + "rutherford" : "Rutherford", + "coulombs-per-kilogram" : "Coulomb pro Kilogramm", + "becquerels-per-cubic-meter" : "Becquerel pro Kubikmeter", + "curies-per-liter" : "Curie pro Liter", + "becquerels-per-second" : "Becquerel pro Sekunde", + "curies-per-second" : "Curie pro Sekunde", + "gy-per-second" : "Gray pro Sekunde", + "watt-per-steradian" : "Watt pro Steradiant", + "watt-per-square-metre-steradian" : "Watt pro Quadratmeter-Steradiant", + "ph-level" : "pH-Wert", + "turbidity" : "Trübung", + "mg-per-liter" : "Milligramm pro Liter", + "microsiemens-per-centimeter" : "Mikrosiemens pro Zentimeter", + "millisiemens-per-meter" : "Millisimens pro Meter", + "siemens-per-meter" : "Siemens pro Meter", + "kilogram-per-cubic-meter" : "Kilogramm pro Kubikmeter", + "gram-per-cubic-centimeter" : "Gramm pro Kubikzentimeter", + "kilogram-per-square-meter" : "Kilogramm pro Quadratmeter", + "milligram-per-milliliter" : "Milligramm pro Milliliter", + "milligram-per-cubic-meter" : "Milligramm pro Kubikmeter", + "pound-per-cubic-foot" : "Pfund pro Kubikfuß", + "ounces-per-cubic-inch" : "Unzen pro Kubikzoll", + "tons-per-cubic-yard" : "Tonnen pro Kubikyard", + "particle-density" : "Partikeldichte", + "kilometers-per-liter" : "Kilometer pro Liter", + "miles-per-gallon" : "Meilen pro Gallone", + "liters-per-100-km" : "Liter pro 100 km", + "gallons-per-mile" : "Gallonen pro Meile", + "liters-per-hour" : "Liter pro Stunde", + "gallons-per-hour" : "Gallonen pro Stunde", + "beats-per-minute" : "Schläge pro Minute", + "millimeters-of-mercury" : "Millimeter Quecksilbersäule", + "milligrams-per-deciliter" : "Milligramm pro Deziliter", + "g-force" : "g-Kraft", + "kilonewton" : "Kilonewton", + "kilogram-force" : "Kilopond", + "pound-force" : "Pfundkraft", + "kilopound-force" : "Kilopfundkraft", + "dyne" : "Dyne", + "poundal" : "Poundal", + "kip" : "Kip", + "gal" : "Gal", + "gravity" : "Schwerkraft", + "hectopascal" : "Hektopascal", + "atmosphere" : "Atmosphäre", + "millibars" : "Millibar", + "inch-of-mercury" : "Zoll Quecksilbersäule", + "richter-scale" : "Richterskala", + "second" : "Sekunde", + "minute" : "Minute", + "hour" : "Stunde", + "day" : "Tag", + "week" : "Woche", + "month" : "Monat", + "year" : "Jahr", + "cubic-foot-per-minute" : "Kubikfuß pro Minute", + "cubic-meters-per-hour" : "Kubikmeter pro Stunde", + "cubic-meters-per-second" : "Kubikmeter pro Sekunde", + "liter-per-second" : "Liter pro Sekunde", + "liter-per-minute" : "Liter pro Minute", + "gallons-per-minute" : "Gallonen pro Minute", + "cubic-foot-per-second" : "Kubikfuß pro Sekunde", + "milliliters-per-minute" : "Milliliter pro Minute", + "bit" : "Bit", + "byte" : "Byte", + "kilobyte" : "Kilobyte", + "megabyte" : "Megabyte", + "gigabyte" : "Gigabyte", + "terabyte" : "Terabyte", + "petabyte" : "Petabyte", + "exabyte" : "Exabyte", + "zettabyte" : "Zettabyte", + "yottabyte" : "Yottabyte", + "bit-per-second" : "Bit pro Sekunde", + "kilobit-per-second" : "Kilobit pro Sekunde", + "megabit-per-second" : "Megabit pro Sekunde", + "gigabit-per-second" : "Gigabit pro Sekunde", + "terabit-per-second" : "Terabit pro Sekunde", + "byte-per-second" : "Byte pro Sekunde", + "kilobyte-per-second" : "Kilobyte pro Sekunde", + "megabyte-per-second" : "Megabyte pro Sekunde", + "gigabyte-per-second" : "Gigabyte pro Sekunde", + "degree" : "Grad", + "radian" : "Radiant", + "gradian" : "Gon", + "revolution" : "Umdrehung", + "siemens" : "Siemens", + "millisiemens" : "Millisimens", + "microsiemens" : "Mikrosiemens", + "kilosiemens" : "Kilosiemens", + "megasiemens" : "Megasiemens", + "gigasiemens" : "Gigasiemens", + "farad" : "Farad", + "millifarad" : "Millifarad", + "microfarad" : "Mikrofarad", + "nanofarad" : "Nanofarad", + "picofarad" : "Pikofarad", + "kilofarad" : "Kilofarad", + "megafarad" : "Megafarad", + "gigafarad" : "Gigafarad", + "terfarad" : "Terafarad", + "farad-per-meter" : "Farad pro Meter", + "tesla" : "Tesla", + "gauss" : "Gauss", + "kilogauss" : "Kilogauss", + "millitesla" : "Millitesla", + "microtesla" : "Mikrotesla", + "nanotesla" : "Nanotesla", + "kilotesla" : "Kilotesla", + "megatesla" : "Megatesla", + "millitesla-square-meters" : "Millitesla-Quadratmeter", + "gamma" : "Gamma", + "lambda" : "Lambda", + "square-meter-per-second" : "Quadratmeter pro Sekunde", + "square-centimeter-per-second" : "Quadratzentimeter pro Sekunde", + "stoke" : "Stoke", + "centistokes" : "Zentistokes", + "square-foot-per-second" : "Quadratfuß pro Sekunde", + "square-inch-per-second" : "Quadratzoll pro Sekunde", + "pascal-second" : "Pascal-Sekunde", + "centipoise" : "Zentipoise", + "poise" : "Poise", + "reynolds" : "Reynolds", + "pound-per-foot-hour" : "Pfund pro Fuß-Stunde", + "newton-second-per-square-meter" : "Newtonsekunde pro Quadratmeter", + "dyne-second-per-square-centimeter" : "Dynesekunde pro Quadratzentimeter", + "kilogram-per-meter-second" : "Kilogramm pro Meter-Sekunde", + "tesla-square-meters" : "Tesla-Quadratmeter", + "maxwell" : "Maxwell", + "tesla-per-meter" : "Tesla pro Meter", + "gauss-per-centimeter" : "Gauss pro Zentimeter", + "weber" : "Weber", + "microweber" : "Mikroweber", + "milliweber" : "Milliweber", + "gauss-square-centimeter" : "Gauss-Quadratzentimeter", + "kilogauss-square-centimeter" : "Kilogauss-Quadratzentimeter", + "henry" : "Henry", + "millihenry" : "Millihenry", + "microhenry" : "Mikrohenry", + "nanohenry" : "Nanohenry", + "henry-per-meter" : "Henry pro Meter", + "tesla-meter-per-ampere" : "Tesla-Meter pro Ampere", + "gauss-per-oersted" : "Gauss pro Oersted", + "kilogram-per-mole" : "Kilogramm pro Mol", + "gram-per-mole" : "Gramm pro Mol", + "milligram-per-mole" : "Milligramm pro Mol", + "joule-per-mole" : "Joule pro Mol", + "joule-per-mole-kelvin" : "Joule pro Mol-Kelvin", + "millivolts-per-meter" : "Millivolt pro Meter", + "volts-per-meter" : "Volt pro Meter", + "kilovolts-per-meter" : "Kilovolt pro Meter", + "radian-per-second" : "Radiant pro Sekunde", + "radian-per-second-squared" : "Radiant pro Sekunde zum Quadrat", + "revolutions-per-minute-per-second" : "Drehbeschleunigung", + "deg-per-second" : "Grad pro Sekunde", + "degrees-brix" : "Grad Brix", + "katal" : "Katal", + "katal-per-cubic-metre" : "Katal pro Kubikmeter" + }, + "user" : { + "user" : "Benutzer", + "users" : "Benutzer", + "customer-users" : "Kundenbenutzer", + "tenant-admins" : "Tenant-Administratoren", + "sys-admin" : "Systemadministrator", + "tenant-admin" : "Tenant-Administrator", + "customer" : "Kunde", + "anonymous" : "Anonym", + "add" : "Benutzer hinzufügen", + "delete" : "Benutzer löschen", + "add-user-text" : "Neuen Benutzer hinzufügen", + "no-users-text" : "Keine Benutzer gefunden", + "user-details" : "Benutzerdetails", + "delete-user-title" : "Möchten Sie den Benutzer '{{userEmail}}' wirklich löschen?", + "delete-user-text" : "Vorsicht! Nach der Bestätigung werden der Benutzer und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-users-title" : "Möchten Sie wirklich { count, plural, =1 {1 Benutzer} other {# Benutzer} } löschen?", + "delete-users-action-title" : "{ count, plural, =1 {1 Benutzer löschen} other {# Benutzer löschen} }", + "delete-users-text" : "Vorsicht! Nach der Bestätigung werden alle ausgewählten Benutzer und zugehörige Daten gelöscht.", + "activation-email-sent-message" : "Aktivierungs-E-Mail wurde erfolgreich gesendet!", + "resend-activation" : "Aktivierung erneut senden", + "email" : "E-Mail", + "email-required" : "E-Mail ist erforderlich.", + "invalid-email-format" : "Ungültiges E-Mail-Format.", + "first-name" : "Vorname", + "last-name" : "Nachname", + "description" : "Beschreibung", + "default-dashboard" : "Standard-Dashboard", + "always-fullscreen" : "Immer Vollbildmodus", + "select-user" : "Benutzer auswählen", + "no-users-matching" : "Keine Benutzer, die mit '{{entity}}' übereinstimmen, gefunden.", + "user-required" : "Benutzer ist erforderlich", + "activation-method" : "Aktivierungsmethode", + "display-activation-link" : "Aktivierungslink anzeigen", + "send-activation-mail" : "Aktivierungsmail senden", + "activation-link" : "Benutzeraktivierungslink", + "activation-link-text" : "Um den Benutzer zu aktivieren, verwenden Sie folgenden Aktivierungslink (gültig für {{activationLinkTtl}}):", + "copy-activation-link" : "Aktivierungslink kopieren", + "activation-link-copied-message" : "Benutzeraktivierungslink wurde in die Zwischenablage kopiert", + "details" : "Details", + "login-as-tenant-admin" : "Als Tenant-Admin anmelden", + "login-as-customer-user" : "Als Kundenbenutzer anmelden", + "search" : "Benutzer suchen", + "selected-users" : "{ count, plural, =1 {1 Benutzer} other {# Benutzer} } ausgewählt", + "disable-account" : "Benutzerkonto deaktivieren", + "enable-account" : "Benutzerkonto aktivieren", + "enable-account-message" : "Benutzerkonto wurde erfolgreich aktiviert!", + "disable-account-message" : "Benutzerkonto wurde erfolgreich deaktiviert!", + "copyId" : "Benutzer-ID kopieren", + "idCopiedMessage" : "Benutzer-ID wurde in die Zwischenablage kopiert", + "user-list" : "Benutzerliste", + "user-list-required" : "Benutzerliste ist erforderlich" + }, + "value" : { + "type" : "Werttyp", + "string" : "Zeichenkette", + "string-value" : "Zeichenkettenwert", + "string-value-required" : "Zeichenkettenwert ist erforderlich", + "integer" : "Ganzzahl", + "integer-value" : "Ganzzahlwert", + "integer-value-required" : "Ganzzahlwert ist erforderlich", + "invalid-integer-value" : "Ungültiger Ganzzahlwert", + "double" : "Dezimalzahl", + "double-value" : "Dezimalwert", + "double-value-required" : "Dezimalwert ist erforderlich", + "boolean" : "Boolesch", + "boolean-value" : "Boolescher Wert", + "false" : "Falsch", + "true" : "Wahr", + "long" : "Lange Ganzzahl", + "json" : "JSON", + "json-value" : "JSON-Wert", + "json-value-invalid" : "JSON-Wert hat ein ungültiges Format", + "json-value-required" : "JSON-Wert ist erforderlich." + }, + "version-control" : { + "version-control" : "Versionskontrolle", + "management" : "Versionskontrollverwaltung", + "search" : "Versionen durchsuchen", + "branch" : "Zweig", + "default" : "Standard", + "select-branch" : "Zweig auswählen", + "branch-required" : "Zweig ist erforderlich", + "create-entity-version" : "Entitätsversion erstellen", + "version-name" : "Versionsname", + "version-name-required" : "Versionsname ist erforderlich", + "author" : "Autor", + "export-relations" : "Beziehungen exportieren", + "export-attributes" : "Attribute exportieren", + "export-credentials" : "Zugangsdaten exportieren", + "export-calculated-fields" : "Berechnete Felder exportieren", + "entity-versions" : "Entitätsversionen", + "versions" : "Versionen", + "created-time" : "Erstellungszeit", + "version-id" : "Versions-ID", + "no-entity-versions-text" : "Keine Entitätsversionen gefunden", + "no-versions-text" : "Keine Versionen gefunden", + "copy-full-version-id" : "Vollständige Versions-ID kopieren", + "create-version" : "Version erstellen", + "creating-version" : "Version wird erstellt... Bitte warten", + "nothing-to-commit" : "Keine Änderungen zum Übernehmen", + "restore-version" : "Version wiederherstellen", + "restore-entity-from-version" : "Entität aus Version '{{versionName}}' wiederherstellen", + "restoring-entity-version" : "Entitätsversion wird wiederhergestellt... Bitte warten", + "load-relations" : "Beziehungen laden", + "load-attributes" : "Attribute laden", + "load-credentials" : "Zugangsdaten laden", + "load-calculated-fields" : "Berechnete Felder laden", + "compare-with-current" : "Mit aktueller vergleichen", + "diff-entity-with-version" : "Unterschiede mit Entitätsversion '{{versionName}}'", + "previous-difference" : "Vorheriger Unterschied", + "next-difference" : "Nächster Unterschied", + "current" : "Aktuell", + "differences" : "{ count, plural, =1 {1 Unterschied} other {# Unterschiede} }", + "create-entities-version" : "Entitätsversion erstellen", + "default-sync-strategy" : "Standard-Synchronisierungsstrategie", + "sync-strategy-merge" : "Zusammenführen", + "sync-strategy-overwrite" : "Überschreiben", + "entities-to-export" : "Zu exportierende Entitäten", + "entities-to-restore" : "Zu wiederherstellende Entitäten", + "sync-strategy" : "Synchronisierungsstrategie", + "all-entities" : "Alle Entitäten", + "no-entities-to-export-prompt" : "Bitte Entitäten zum Export angeben", + "no-entities-to-restore-prompt" : "Bitte Entitäten zur Wiederherstellung angeben", + "add-entity-type" : "Entitätstyp hinzufügen", + "remove-all" : "Alle entfernen", + "version-create-result" : "{ added, plural, =0 {Keine Entitäten} =1 {1 Entität} other {# Entitäten} } hinzugefügt.
{ modified, plural, =0 {Keine Entitäten} =1 {1 Entität} other {# Entitäten} } geändert.
{ removed, plural, =0 {Keine Entitäten} =1 {1 Entität} other {# Entitäten} } entfernt.", + "remove-other-entities" : "Andere Entitäten entfernen", + "find-existing-entity-by-name" : "Vorhandene Entität nach Namen finden", + "restore-entities-from-version" : "Entitäten aus Version '{{versionName}}' wiederherstellen", + "restoring-entities-from-version" : "Entitäten werden wiederhergestellt... Bitte warten", + "no-entities-restored" : "Keine Entitäten wiederhergestellt", + "created" : "{{created}} erstellt", + "updated" : "{{updated}} aktualisiert", + "deleted" : "{{deleted}} gelöscht", + "remove-other-entities-confirm-text" : "Achtung! Dies wird alle aktuellen Entitäten dauerhaft löschen
die nicht in der wiederherzustellenden Version vorhanden sind.

Bitte geben Sie \"andere Entitäten entfernen\" ein, um zu bestätigen.", + "auto-commit-to-branch" : "automatisch in {{ branch }}-Zweig übernehmen", + "default-create-entity-version-name" : "{{entityName}} Aktualisierung", + "sync-strategy-merge-hint" : "Erstellt oder aktualisiert ausgewählte Entitäten im Repository. Alle anderen Repository-Entitäten werden nicht verändert.", + "sync-strategy-overwrite-hint" : "Erstellt oder aktualisiert ausgewählte Entitäten im Repository. Alle anderen Repository-Entitäten werden gelöscht.", + "device-credentials-conflict" : "Gerät mit externer ID {{entityId}} konnte nicht geladen werden,
da dieselben Zugangsdaten bereits für ein anderes Gerät vorhanden sind.
Bitte erwägen Sie, die Einstellung Zugangsdaten laden im Wiederherstellungsformular zu deaktivieren.", + "missing-referenced-entity" : "{{sourceEntityTypeName}} mit externer ID {{sourceEntityId}} konnte nicht geladen werden,
da sie auf eine fehlende {{targetEntityTypeName}} mit ID {{targetEntityId}} verweist.", + "runtime-failed" : "Fehlgeschlagen: {{message}}", + "auto-commit-settings-read-only-hint" : "Auto-Commit funktioniert nicht, wenn die Option 'Nur-Lesen' in den Repository-Einstellungen aktiviert ist.", + "rollback-on-error" : "Bei Fehler zurücksetzen", + "rollback-on-error-hint" : "Wenn Sie eine große Menge an Entitäten wiederherstellen, deaktivieren Sie diese Option zur Leistungssteigerung.\nBeachten Sie, dass bei einem Fehler bereits gespeicherte Entitäten (mit Beziehungen, Attributen etc.) erhalten bleiben." + }, + "widget" : { + "widget-library" : "Widget-Bibliothek", + "widget-bundle" : "Widget-Bundle", + "all-bundles" : "Alle Bundles", + "select-widgets-bundle" : "Widget-Bundle auswählen", + "widgets" : "Widgets", + "all-widgets" : "Alle Widgets", + "widget" : "Widget", + "select-widget" : "Widget auswählen", + "no-widgets-matching" : "Keine Widgets gefunden, die mit '{{entity}}' übereinstimmen.", + "no-widgets" : "Noch keine Widgets", + "no-widgets-text" : "Keine Widgets gefunden", + "management" : "Widget-Verwaltung", + "editor" : "Widget-Editor", + "confirm-to-exit-editor-html" : "Sie haben nicht gespeicherte Widget-Einstellungen.
Möchten Sie diese Seite wirklich verlassen?", + "widget-type-not-found" : "Fehler beim Laden der Widget-Konfiguration.
Vermutlich wurde der zugehörige Widget-Typ entfernt.", + "widget-type-load-error" : "Widget konnte aufgrund folgender Fehler nicht geladen werden:", + "remove" : "Widget entfernen", + "delete" : "Widget löschen", + "edit" : "Widget bearbeiten", + "remove-widget-title" : "Möchten Sie das Widget '{{widgetTitle}}' wirklich entfernen?", + "remove-widget-text" : "Nach der Bestätigung wird das Widget und alle zugehörigen Daten unwiderruflich gelöscht.", + "replace-reference-with-widget-copy" : "Referenz durch Widget-Kopie ersetzen", + "timeseries" : "Zeitreihe", + "search-data" : "Daten durchsuchen", + "no-data-found" : "Keine Daten gefunden", + "latest" : "Aktuelle Werte", + "rpc" : "Steuerungs-Widget", + "alarm" : "Alarm-Widget", + "static" : "Statisches Widget", + "timeseries-short" : "reihe", + "latest-short" : "aktuell", + "rpc-short" : "steuerung", + "alarm-short" : "alarm", + "static-short" : "statisch", + "select-widget-type" : "Widget-Typ auswählen", + "missing-widget-title-error" : "Widget-Titel muss angegeben werden!", + "widget-saved" : "Widget gespeichert", + "unable-to-save-widget-error" : "Widget konnte nicht gespeichert werden! Es liegen Fehler vor!", + "save" : "Widget speichern", + "saveAs" : "Widget speichern unter", + "move" : "Widget verschieben", + "save-widget-as" : "Widget speichern unter", + "save-widget-as-text" : "Bitte geben Sie einen neuen Widget-Titel ein", + "toggle-fullscreen" : "Vollbildmodus umschalten", + "run" : "Widget ausführen", + "widget-title" : "Widget-Titel", + "title" : "Titel", + "title-required" : "Widget-Titel ist erforderlich.", + "title-max-length" : "Titel darf nicht länger als 256 Zeichen sein", + "system" : "System", + "type" : "Widget-Typ", + "resources" : "Ressourcen", + "resource-url" : "JavaScript/CSS-URL", + "resource-is-extension" : "Ist Erweiterung", + "remove-resource" : "Ressource entfernen", + "add-resource" : "Ressource hinzufügen", + "html" : "HTML", + "tidy" : "Ordnen", + "css" : "CSS", + "settings-form" : "Einstellungsformular", + "data-key-settings-form" : "Daten-Schlüssel-Einstellungsformular", + "latest-data-key-settings-form" : "Aktuelle Daten-Schlüssel-Einstellungsformular", + "widget-settings" : "Widget-Einstellungen", + "description" : "Beschreibung", + "tags" : "Tags", + "image-preview" : "Bildvorschau", + "settings-form-selector" : "Einstellungsformularauswahl", + "data-key-settings-form-selector" : "Daten-Schlüssel-Formularauswahl", + "latest-data-key-settings-form-selector" : "Formularauswahl für aktuelle Daten-Schlüssel", + "all" : "Alle", + "actual" : "Aktuell", + "scada" : "SCADA-Symbol", + "deprecated" : "Veraltet", + "has-basic-mode" : "Hat einfachen Modus", + "basic-mode-form-selector" : "Formularauswahl für einfachen Modus", + "basic-mode" : "Einfach", + "advanced-mode" : "Erweitert", + "javascript" : "JavaScript", + "js" : "JS", + "delete-widget-title" : "Möchten Sie das Widget '{{widgetName}}' wirklich löschen?", + "delete-widget-text" : "Nach der Bestätigung wird das Widget und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-widgets-title" : "Möchten Sie wirklich { count, plural, =1 {1 Widget} other {# Widgets} } löschen?", + "delete-widgets-text" : "Nach der Bestätigung werden alle ausgewählten Widgets und alle zugehörigen Daten gelöscht.", + "delete-widget" : "Widget löschen", + "widget-template-load-failed-error" : "Widget-Vorlage konnte nicht geladen werden!", + "details" : "Details", + "widget-details" : "Widget-Details", + "add" : "Widget hinzufügen", + "add-existing-widget" : "Vorhandenes Widget hinzufügen", + "add-new-widget" : "Neues Widget hinzufügen", + "search-widgets" : "Widgets durchsuchen", + "selected-widgets" : "{ count, plural, =1 {1 Widget} other {# Widgets} } ausgewählt", + "undo" : "Widget-Änderungen rückgängig machen", + "export" : "Widget exportieren", + "export-prompt" : "Widget-Bilder und Ressourcen einbetten", + "export-widgets" : "Widgets exportieren", + "export-widgets-prompt" : "Widgets-Bilder und Ressourcen einbetten", + "import" : "Widget importieren", + "no-data" : "Keine Daten zur Anzeige im Widget", + "data-overflow" : "Widget zeigt {{count}} von {{total}} Entitäten an", + "alarm-data-overflow" : "Widget zeigt Alarme für {{allowedEntities}} (maximal erlaubt) von {{totalEntities}} Entitäten an", + "search" : "Widget suchen", + "filter" : "Widget-Filtertyp", + "loading-widgets" : "Widgets werden geladen...", + "widget-template-error" : "Ungültige Widget-HTML-Vorlage.", + "reference" : "Referenz" + }, + "widget-action" : { + "header-button" : "Widget-Kopfzeilen-Schaltfläche", + "do-nothing" : "Nichts tun", + "open-dashboard-state" : "Zu neuem Dashboard-Zustand navigieren", + "update-dashboard-state" : "Aktuellen Dashboard-Zustand aktualisieren", + "open-dashboard" : "Zu anderem Dashboard navigieren", + "custom" : "Benutzerdefinierte Aktion", + "custom-pretty" : "Benutzerdefinierte Aktion (mit HTML-Vorlage)", + "custom-pretty-error-title" : "Benutzerdefinierter Dialogfehler", + "custom-pretty-template-error" : "Ungültige benutzerdefinierte Dialogvorlage.", + "custom-pretty-controller-error" : "Fehler bei der Auswertung der benutzerdefinierten Dialogfunktion.", + "mobile-action" : "Mobile Aktion", + "target-dashboard-state" : "Ziel-Dashboard-Zustand", + "target-dashboard-state-required" : "Ziel-Dashboard-Zustand ist erforderlich", + "set-entity-from-widget" : "Entität vom Widget setzen", + "target-dashboard" : "Ziel-Dashboard", + "select-target-dashboard" : "Ziel-Dashboard auswählen", + "target-dashboard-required" : "Ziel-Dashboard ist erforderlich.", + "open-right-layout" : "Rechtes Dashboard-Layout öffnen (mobile Ansicht)", + "state-display-type" : "Anzeigeoption für Dashboard-Zustand", + "open-normal" : "Normal", + "open-in-separate-dialog" : "In separatem Dialog öffnen", + "open-in-popover" : "In Popover öffnen", + "dialog-title" : "Dialogtitel", + "dialog-hide-dashboard-toolbar" : "Dashboard-Werkzeugleiste im Dialog ausblenden", + "dialog-width" : "Dialogbreite in Prozent relativ zur Fensterbreite", + "dialog-height" : "Dialoghöhe in Prozent relativ zur Fensterhöhe", + "dialog-size-range-error" : "Dialoggrößenwert muss im Bereich von 1 bis 100 liegen.", + "popover-preferred-placement" : "Bevorzugte Popover-Platzierung", + "popover-placement-top" : "Oben", + "popover-placement-topLeft" : "Oben links", + "popover-placement-topRight" : "Oben rechts", + "popover-placement-right" : "Rechts", + "popover-placement-rightTop" : "Rechts oben", + "popover-placement-rightBottom" : "Rechts unten", + "popover-placement-bottom" : "Unten", + "popover-placement-bottomLeft" : "Unten links", + "popover-placement-bottomRight" : "Unten rechts", + "popover-placement-left" : "Links", + "popover-placement-leftTop" : "Links oben", + "popover-placement-leftBottom" : "Links unten", + "popover-hide-on-click-outside" : "Popover beim Klicken außerhalb ausblenden", + "popover-hide-dashboard-toolbar" : "Dashboard-Werkzeugleiste im Popover ausblenden", + "popover-width" : "Popover-Breite", + "popover-height" : "Popover-Höhe", + "popover-style" : "Popover-Stil", + "open-new-browser-tab" : "In neuem Browser-Tab öffnen", + "open-URL" : "URL öffnen", + "URL" : "URL", + "url-required" : "URL ist erforderlich.", + "mobile" : { + "device-provision" : "Gerätekonfiguration", + "action-type" : "Mobiler Aktionstyp", + "select-action-type" : "Mobilen Aktionstyp auswählen", + "action-type-required" : "Mobiler Aktionstyp ist erforderlich", + "take-picture-from-gallery" : "Bild aus Galerie auswählen", + "take-photo" : "Foto aufnehmen", + "map-direction" : "Kartennavigation öffnen", + "map-location" : "Kartenstandort öffnen", + "scan-qr-code" : "QR-Code scannen", + "make-phone-call" : "Anruf tätigen", + "get-location" : "Standort des Telefons abrufen", + "take-screenshot" : "Screenshot erstellen" + }, + "custom-action-function" : "Benutzerdefinierte Aktionsfunktion", + "custom-pretty-function" : "Benutzerdefinierte Aktion (mit HTML-Vorlage) Funktion", + "map-item-type" : "Kartenelementtyp", + "map-item" : { + "marker" : "Markierung", + "polygon" : "Polygon", + "rectangle" : "Rechteck", + "circle" : "Kreis" + }, + "place-map-item" : "Kartenelement platzieren", + "map-item-tooltip" : { + "customize-map-item-tooltips" : "Tooltips für Kartenelemente anpassen", + "place-marker" : "Markierung setzen", + "start-draw-rectangle" : "Rechteck zeichnen starten", + "finish-draw-rectangle" : "Rechteckzeichnen beenden", + "start-draw-polygon" : "Polygon zeichnen starten", + "continue-draw-polygon" : "Polygon weiterzeichnen", + "finish-draw-polygon" : "Polygonzeichnen beenden", + "start-draw-circle" : "Kreis zeichnen starten", + "finish-draw-circle" : "Kreiszeichnen beenden" + } + }, + "widgets-bundle" : { + "current" : "Aktuelles Bundle", + "widgets-bundles" : "Widget-Bundles", + "widgets-bundle-widgets" : "Widgets im Bundle", + "add" : "Widget-Bundle hinzufügen", + "delete" : "Widget-Bundle löschen", + "title" : "Titel", + "title-required" : "Titel ist erforderlich.", + "title-max-length" : "Titel darf nicht länger als 256 Zeichen sein", + "description" : "Beschreibung", + "image-preview" : "Bildvorschau", + "scada" : "SCADA-Widget-Bundle", + "order" : "Reihenfolge", + "add-widgets-bundle-text" : "Neues Widget-Bundle hinzufügen", + "no-widgets-bundles-text" : "Keine Widget-Bundles gefunden", + "empty" : "Widget-Bundle ist leer", + "details" : "Details", + "widgets-bundle-details" : "Details des Widget-Bundles", + "delete-widgets-bundle-title" : "Möchten Sie das Widget-Bundle '{{widgetsBundleTitle}}' wirklich löschen?", + "delete-widgets-bundle-text" : "Seien Sie vorsichtig, nach der Bestätigung werden das Widget-Bundle und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-widgets-bundles-title" : "Möchten Sie wirklich { count, plural, =1 {1 Widget-Bundle} other {# Widget-Bundles} } löschen?", + "delete-widgets-bundles-action-title" : "{ count, plural, =1 {1 Widget-Bundle löschen} other {# Widget-Bundles löschen} }", + "delete-widgets-bundles-text" : "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Widget-Bundles und alle zugehörigen Daten gelöscht.", + "no-widgets-bundles-matching" : "Keine Widget-Bundles gefunden, die mit '{{widgetsBundle}}' übereinstimmen.", + "widgets-bundle-required" : "Widget-Bundle ist erforderlich.", + "system" : "System", + "import" : "Widget-Bundle importieren", + "export" : "Widget-Bundle exportieren", + "export-widgets-bundle-widgets-prompt" : "Bundle-Widgets in exportierte Daten einbeziehen (ansonsten werden nur referenzierte Widget-FQNs exportiert)", + "export-failed-error" : "Widget-Bundle konnte nicht exportiert werden: {{error}}", + "create-new-widgets-bundle" : "Neues Widget-Bundle erstellen", + "widgets-bundle-file" : "Widget-Bundle-Datei", + "invalid-widgets-bundle-file-error" : "Widget-Bundle konnte nicht importiert werden: Ungültige Datenstruktur.", + "search" : "Widget-Bundles durchsuchen", + "selected-widgets-bundles" : "{ count, plural, =1 {1 Widget-Bundle} other {# Widget-Bundles} } ausgewählt", + "open-widgets-bundle" : "Widget-Bundle öffnen", + "loading-widgets-bundles" : "Widget-Bundles werden geladen...", + "create-new" : "Neues Widget-Bundle erstellen" + }, + "widget-config" : { + "data" : "Daten", + "settings" : "Einstellungen", + "advanced" : "Erweitert", + "appearance" : "Erscheinungsbild", + "widget-card" : "Widget-Karte", + "mobile" : "Mobil", + "title" : "Titel", + "title-tooltip" : "Titel-Tooltip", + "general-settings" : "Allgemeine Einstellungen", + "display-title" : "Widget-Titel anzeigen", + "card-title" : "Kartentitel", + "drop-shadow" : "Schatteneffekt", + "enable-fullscreen" : "Vollbild aktivieren", + "background-color" : "Hintergrundfarbe", + "text-color" : "Textfarbe", + "border-radius" : "Rahmenradius", + "padding" : "Innenabstand (Padding)", + "margin" : "Außenabstand (Margin)", + "widget-style" : "Widget-Stil", + "widget-css" : "Widget-CSS", + "title-style" : "Titelstil", + "mobile-mode-settings" : "Mobile Ansicht", + "order" : "Reihenfolge", + "height" : "Höhe", + "mobile-hide" : "Widget in mobiler Ansicht ausblenden", + "desktop-hide" : "Widget in Desktop-Ansicht ausblenden", + "units" : "Sonderzeichen neben dem Wert anzeigen", + "units-by-default" : "Standard-Einheiten", + "decimals" : "Anzahl der Nachkommastellen", + "decimals-by-default" : "Standard-Nachkommastellen", + "default-data-key-parameter-hint" : "Dieser Parameter gilt für alle Widget-Werte, sofern nicht durch die Daten-Schlüsselkonfiguration überschrieben.", + "units-short" : "Einheiten", + "decimals-short" : "Nachkommastellen", + "decimals-suffix" : "Nachkommastellen", + "digits-suffix" : "Ziffern", + "timewindow" : "Zeitfenster", + "use-dashboard-timewindow" : "Dashboard-Zeitfenster verwenden", + "use-widget-timewindow" : "Widget-Zeitfenster verwenden", + "display-timewindow" : "Zeitfenster anzeigen", + "legend" : "Legende", + "display-legend" : "Legende anzeigen", + "datasources" : "Datenquellen", + "datasource" : "Datenquelle", + "maximum-datasources" : "Maximal { count, plural, =1 {1 Datenquelle erlaubt.} other {# Datenquellen erlaubt} }", + "timeseries-key-error" : "Mindestens ein Zeitreihen-Datenschlüssel muss angegeben werden", + "datasource-type" : "Typ", + "datasource-parameters" : "Parameter", + "remove-datasource" : "Datenquelle entfernen", + "add-datasource" : "Datenquelle hinzufügen", + "target-device" : "Zielgerät", + "alarm-source" : "Alarmquelle", + "actions" : "Aktionen", + "action" : "Aktion", + "add-action" : "Aktion hinzufügen", + "search-actions" : "Aktionen suchen", + "no-actions-text" : "Keine Aktionen gefunden", + "action-source" : "Aktionsquelle", + "select-action-source" : "Aktionsquelle auswählen", + "action-source-required" : "Aktionsquelle ist erforderlich.", + "column-index" : "Spaltenindex", + "select-column-index" : "Spaltenindex auswählen", + "column-index-required" : "Spaltenindex ist erforderlich.", + "not-set" : "Nicht gesetzt", + "action-name" : "Name", + "action-name-required" : "Aktionsname ist erforderlich.", + "action-name-not-unique" : "Eine andere Aktion mit demselben Namen existiert bereits.\nDer Aktionsname muss innerhalb derselben Quelle eindeutig sein.", + "action-icon" : "Symbol", + "header-button" : { + "button-settings" : "Schaltflächeneinstellungen", + "button-type" : "Schaltflächentyp", + "button-type-basic" : "Einfach", + "button-type-raised" : "Erhöht", + "button-type-stroked" : "Umrandet", + "button-type-flat" : "Flach", + "button-type-icon" : "Symbol", + "button-type-mini-fab" : "FAB", + "colors" : "Farben", + "color" : "Farbe", + "background" : "Hintergrund", + "border" : "Rahmen", + "advanced-button-style" : "Erweiterter Schaltflächenstil", + "button-style" : "Schaltflächenstil" + }, + "show-hide-action-using-function" : "Aktion über Funktion ein-/ausblenden", + "show-action-function" : "Funktion zum Anzeigen der Aktion", + "action-type" : "Typ", + "action-type-required" : "Aktionstyp ist erforderlich.", + "edit-action" : "Aktion bearbeiten", + "delete-action" : "Aktion löschen", + "delete-action-title" : "Widget-Aktion löschen", + "delete-action-text" : "Möchten Sie die Widget-Aktion mit dem Namen '{{actionName}}' wirklich löschen?", + "title-icon" : "Titelsymbol", + "display-icon" : "Titelsymbol anzeigen", + "card-icon" : "Kartensymbol", + "icon" : "Symbol", + "icon-color" : "Symbolfarbe", + "icon-size" : "Symbolgröße", + "advanced-settings" : "Erweiterte Einstellungen", + "data-settings" : "Daten-Einstellungen", + "limits" : "Grenzwerte", + "no-data-display-message" : "Alternative Nachricht bei fehlenden Daten", + "data-page-size" : "Maximale Anzahl von Entitäten pro Datenquelle", + "settings-component-not-found" : "Einstellungsformular-Komponente für Selector '{{selector}}' nicht gefunden", + "preview" : "Vorschau", + "set" : "Festlegen", + "set-message" : "Nachricht festlegen", + "advanced-title-style" : "Erweiterter Titelstil", + "card-style" : "Kartenstil", + "text" : "Text", + "background" : "Hintergrund", + "advanced-widget-style" : "Erweiterter Widget-Stil", + "card-buttons" : "Karten-Schaltflächen", + "show-card-buttons" : "Karten-Schaltflächen anzeigen", + "card-border-radius" : "Karten-Rahmenradius", + "card-padding" : "Karten-Abstand innen", + "card-appearance" : "Karten-Erscheinungsbild", + "color" : "Farbe", + "tooltip" : "Tooltip", + "units-required" : "Einheit ist erforderlich.", + "list-layout" : "Listenlayout", + "layout" : "Layout", + "resize-options" : "Größenänderungsoptionen", + "resizable" : "Größenänderbar", + "preserve-aspect-ratio" : "Seitenverhältnis beibehalten" + }, + "widget-type" : { + "import" : "Widget-Typ importieren", + "export" : "Widget-Typ exportieren", + "export-failed-error" : "Widget konnte nicht exportiert werden: {{error}}", + "widget-file" : "Widget-Datei", + "invalid-widget-file-error" : "Widget konnte nicht importiert werden: Ungültige Widget-Datenstruktur." + }, + "markdown" : { + "edit" : "Bearbeiten", + "preview" : "Vorschau", + "copy-code" : "Zum Kopieren klicken", + "copied" : "Kopiert!" + }, + "widgets" : { + "mobile-app-qr-code" : { + "configuration-hint" : "Die Konfiguration hängt vom QR-Code-Widget der mobilen App in den Haupteinstellungen der Plattform ab", + "get-it-on-google-play" : "Im Google Play Store herunterladen", + "download-on-the-app-store" : "Im App Store herunterladen" + }, + "action-button" : { + "behavior" : "Verhalten", + "on-click" : "Beim Klicken", + "on-click-hint" : "Aktion, die beim Klicken auf die Schaltfläche ausgelöst wird", + "first-button-click" : "Erster Klick", + "first-button-click-hint" : "Aktion beim Drücken der ersten Schaltfläche", + "second-button-click" : "Zweiter Klick", + "second-button-click-hint" : "Aktion beim Drücken der zweiten Schaltfläche", + "button-click-hint" : "Aktion beim Drücken auf das Widget" + }, + "command-button" : { + "behavior" : "Verhalten", + "on-click" : "Beim Klicken", + "on-click-hint" : "Aktion, die beim Klicken auf die Schaltfläche ausgeführt wird." + }, + "power-button" : { + "behavior" : "Verhalten", + "power-on" : "Einschalten", + "power-on-hint" : "Aktion zum Einschalten der Komponente", + "power-off" : "Ausschalten", + "power-off-hint" : "Aktion zum Ausschalten der Komponente", + "on-label" : "Ein", + "off-label" : "Aus", + "layout" : "Layout", + "layout-default" : "Standard", + "layout-simplified" : "Vereinfacht", + "layout-outlined" : "Umrandet", + "layout-default-volume" : "Standard.Lautstärke", + "layout-simplified-volume" : "Vereinfacht.Lautstärke", + "layout-outlined-volume" : "Umrandet.Lautstärke", + "layout-default-icon" : "Standard.Symbol", + "layout-simplified-icon" : "Vereinfacht.Symbol", + "layout-outlined-icon" : "Umrandet.Symbol", + "main" : "Hauptbereich", + "background" : "Hintergrund", + "button-icon-on" : "Symbol bei 'Ein'", + "button-icon-off" : "Symbol bei 'Aus'", + "power-on-colors" : "Farben für 'Ein'", + "power-off-colors" : "Farben für 'Aus'", + "disabled-colors" : "Farben für deaktiviert", + "button" : "Schaltfläche" + }, + "toggle-button" : { + "behavior" : "Verhalten", + "checked" : "Ausgewählt", + "unchecked" : "Nicht ausgewählt", + "check" : "Auswählen", + "check-hint" : "Aktion zum Auswählen der Komponente", + "uncheck" : "Abwählen", + "uncheck-hint" : "Aktion zum Abwählen der Komponente", + "auto-scale" : "Automatische Skalierung", + "horizontal-fill" : "Horizontal füllen", + "vertical-fill" : "Vertikal füllen", + "button-appearance" : "Schaltflächenstil" + }, + "segmented-button" : { + "layout" : "Layout", + "layout-squared" : "Eckig", + "layout-rounded" : "Abgerundet", + "card-border" : "Kartenrahmen", + "button-appearance" : "Schaltflächenstil", + "first" : "Erste", + "second" : "Zweite", + "color-styles" : "Farbstile", + "selected" : "Ausgewählt", + "unselected" : "Nicht ausgewählt" + }, + "button" : { + "layout" : "Layout", + "outlined" : "Umrandet", + "filled" : "Ausgefüllt", + "underlined" : "Unterstrichen", + "basic" : "Einfach", + "auto-scale" : "Automatisch skalieren", + "label" : "Beschriftung", + "icon" : "Symbol", + "border-radius" : "Randradius", + "color-palette" : "Farbpalette", + "main" : "Hauptbereich", + "background" : "Hintergrund", + "border" : "Rahmen", + "custom-styles" : "Benutzerdefinierte Stile", + "clear-style" : "Stil zurücksetzen", + "shadow" : "Schatten", + "enabled" : "Aktiviert", + "disabled" : "Deaktiviert", + "preview" : "Vorschau", + "copy-style-from" : "Stil kopieren von" + }, + "value-stepper" : { + "behavior" : "Verhalten", + "simplified" : "Vereinfacht", + "filled" : "Ausgefüllt", + "outlined" : "Umrandet", + "volume" : "Lautstärke", + "initial-state" : "Anfangszustand", + "initial-state-hint" : "Aktion zum Abrufen des Anfangswerts.", + "disabled-state" : "Deaktivierter Zustand", + "disabled-state-hint" : "Bedingung, unter der die Komponente deaktiviert ist.", + "right-button-click" : "Klick auf rechte Schaltfläche", + "right-button-click-hint" : "Aktion beim Klicken auf die rechte Schaltfläche.", + "left-button-click" : "Klick auf linke Schaltfläche", + "left-button-click-hint" : "Aktion beim Klicken auf die linke Schaltfläche.", + "auto-scale" : "Automatisch skalieren", + "value-range" : "Wertebereich", + "min-range" : "Min", + "max-range" : "Max", + "value-increment-decrement-step" : "Wertänderungsschritt", + "value" : "Wert", + "value-box-background" : "Hintergrund des Wertfelds", + "border" : "Rahmen", + "button-appearance" : "Schaltflächenstil", + "left" : "Links", + "right" : "Rechts", + "left-button" : "Linke Schaltfläche", + "right-button" : "Rechte Schaltfläche", + "icon" : "Symbol", + "color-palette" : "Farbpalette", + "main" : "Hauptbereich", + "background" : "Hintergrund", + "button-icon-on" : "Symbol bei 'Ein'", + "button-on-colors" : "Farben bei 'Ein'", + "disabled-colors" : "Farben bei 'Deaktiviert'" + }, + "button-state" : { + "activated-state" : "Aktivierter Zustand", + "activated-state-hint" : "Bedingung, unter der die Schaltfläche aktiv ist.", + "disabled-state" : "Deaktivierter Zustand", + "disabled-state-hint" : "Bedingung, unter der die Schaltfläche deaktiviert ist.", + "selected-state" : "Ausgewählter Zustand", + "selected-state-hint" : "Bedingung, unter der die Schaltfläche ausgewählt ist.", + "enabled" : "Aktiviert", + "hovered" : "Hover", + "pressed" : "Gedrückt", + "activated" : "Aktiviert", + "disabled" : "Deaktiviert", + "initial" : "Erste Schaltfläche", + "first" : "Erste", + "second" : "Zweite" + }, + "background" : { + "background" : "Hintergrund", + "background-settings" : "Hintergrundeinstellungen", + "background-type-image" : "Bild", + "background-type-color" : "Farbe", + "image-url" : "Bild-URL", + "overlay" : "Überlagerung", + "enable-overlay" : "Überlagerung aktivieren", + "blur" : "Weichzeichnen", + "preview" : "Vorschau" + }, + "bar-chart" : { + "bar-appearance" : "Balkenanzeige", + "label-on-bar" : "Beschriftung auf dem Balken", + "value-on-bar" : "Wert auf dem Balken", + "bar-chart-style" : "Balkendiagramm-Stil", + "bar-axis" : "Balkenachse" + }, + "polar-area-chart" : { + "polar-axis" : "Polare Achse", + "start-angle" : "Startwinkel", + "polar-area-chart-style" : "Polardiagramm-Stil" + }, + "battery-level" : { + "layout" : "Layout", + "layout-vertical-solid" : "Vertikal. Durchgehend", + "layout-horizontal-solid" : "Horizontal. Durchgehend", + "layout-vertical-divided" : "Vertikal. Geteilt", + "layout-horizontal-divided" : "Horizontal. Geteilt", + "icon" : "Symbol", + "value" : "Wert", + "auto-scale" : "Automatisch skalieren", + "battery-level-color" : "Akkustandfarbe", + "battery-shape-color" : "Akkuformfarbe", + "battery-level-card-style" : "Akkustands-Kartenstil", + "sections-count" : "Anzahl der Abschnitte" + }, + "signal-strength" : { + "value" : "Wert", + "last-update" : "Letzte Aktualisierung", + "no-signal" : "Kein Signal", + "layout" : "Layout", + "layout-wifi" : "Wi-Fi", + "layout-cellular-bar" : "Mobilfunkbalken", + "icon" : "Symbol", + "date" : "Datum", + "active-bars-color" : "Farbe der aktiven Balken", + "inactive-bars-color" : "Farbe der inaktiven Balken", + "signal-strength-card-style" : "Signalstärke-Kartenstil", + "no-signal-rssi-value" : "\"Kein Signal\" RSSI-Wert" + }, + "status-widget" : { + "behavior" : "Verhalten", + "layout" : "Layout", + "layout-default" : "Standard", + "layout-center" : "Zentriert", + "layout-icon" : "Symbol", + "on" : "Ein", + "off" : "Aus", + "label" : "Beschriftung", + "status" : "Status", + "icon" : "Symbol", + "color-palette" : "Farbpalette", + "disabled-color-palette" : "Farbpalette bei Deaktivierung", + "primary" : "Primär", + "primary-color-hint" : "Farbe von Symbol und Beschriftung", + "secondary" : "Sekundär", + "secondary-color-hint" : "Farbe des Status", + "background" : "Hintergrund" + }, + "chart" : { + "common-settings" : "Allgemeine Einstellungen", + "enable-stacking-mode" : "Stacking-Modus aktivieren", + "selection" : "Zeitraum-Auswahl", + "enable-selection-mode" : "Auswahlmodus aktivieren", + "line-shadow-size" : "Linienschatten-Größe", + "display-smooth-lines" : "Glatte (gebogene) Linien anzeigen", + "default-bar-width" : "Standardbalkenbreite für nicht aggregierte Daten (Millisekunden)", + "bar-alignment" : "Balkenausrichtung", + "bar-alignment-left" : "Links", + "bar-alignment-right" : "Rechts", + "bar-alignment-center" : "Zentriert", + "default-font" : "Standardschriftart", + "default-font-size" : "Standardschriftgröße", + "default-font-color" : "Standardschriftfarbe", + "thresholds-line-width" : "Standardlinienbreite für alle Schwellenwerte", + "tooltip-settings" : "Tooltip-Einstellungen", + "tooltip" : "Tooltip", + "show-tooltip" : "Tooltip anzeigen", + "hover-individual-points" : "Einzelne Punkte beim Überfahren anzeigen", + "show-cumulative-values" : "Kumulative Werte im Stacking-Modus anzeigen", + "hide-zero-false-values" : "Null-/Falsch-Werte im Tooltip ausblenden", + "tooltip-value-format-function" : "Tooltip-Wertformatierungsfunktion", + "grid-settings" : "Gittereinstellungen", + "show-vertical-lines" : "Vertikale Linien anzeigen", + "show-horizontal-lines" : "Horizontale Linien anzeigen", + "grid-outline-border-width" : "Gitter-Umriss-/Rahmenbreite (px)", + "primary-color" : "Primärfarbe", + "background-color" : "Hintergrundfarbe", + "ticks-color" : "Tick-Farbe", + "xaxis-settings" : "X-Achse Einstellungen", + "axis-title" : "Achsentitel", + "xaxis-tick-labels-settings" : "Einstellungen für X-Achsenbeschriftung", + "show-tick-labels" : "Tick-Beschriftungen anzeigen", + "yaxis-settings" : "Y-Achse Einstellungen", + "min-scale-value" : "Minimalwert der Skala", + "max-scale-value" : "Maximalwert der Skala", + "yaxis-tick-labels-settings" : "Einstellungen für Y-Achsenbeschriftung", + "tick-step-size" : "Tick-Schrittweite", + "number-of-decimals" : "Anzahl an Dezimalstellen", + "ticks-formatter-function" : "Tick-Formatierungsfunktion", + "comparison-settings" : "Vergleichseinstellungen", + "enable-comparison" : "Vergleich aktivieren", + "time-for-comparison" : "Vergleichszeitraum", + "time-for-comparison-previous-interval" : "Vorheriges Intervall (Standard)", + "time-for-comparison-days" : "Vor einem Tag", + "time-for-comparison-weeks" : "Vor einer Woche", + "time-for-comparison-months" : "Vor einem Monat", + "time-for-comparison-years" : "Vor einem Jahr", + "time-for-comparison-custom-interval" : "Benutzerdefiniertes Intervall", + "custom-interval-value" : "Benutzerdefinierter Intervallwert (ms)", + "comparison-x-axis-settings" : "Vergleichs-X-Achsen-Einstellungen", + "axis-position" : "Achsenposition", + "axis-position-top" : "Oben (Standard)", + "axis-position-bottom" : "Unten", + "custom-legend-settings" : "Benutzerdefinierte Legenden-Einstellungen", + "enable-custom-legend" : "Benutzerdefinierte Legende aktivieren (ermöglicht die Verwendung von Attribut-/Zeitreihenwerten in Schlüsselbeschriftungen)", + "key-name" : "Schlüsselname", + "key-name-required" : "Schlüsselname ist erforderlich", + "key-type" : "Schlüsseltyp", + "key-type-attribute" : "Attribut", + "key-type-timeseries" : "Zeitreihe", + "label-keys-list" : "Liste der Schlüssel zur Verwendung in Beschriftungen", + "no-label-keys" : "Keine Schlüssel konfiguriert", + "add-label-key" : "Neuen Schlüssel hinzufügen", + "line-width" : "Linienbreite", + "color" : "Farbe", + "data-is-hidden-by-default" : "Daten standardmäßig ausgeblendet", + "disable-data-hiding" : "Ausblenden von Daten deaktivieren", + "remove-from-legend" : "Schlüssel aus Legende entfernen", + "exclude-from-stacking" : "Vom Stacking ausschließen (verfügbar im \"Stacking\"-Modus)", + "line-settings" : "Linieneinstellungen", + "show-line" : "Linie anzeigen", + "fill-line" : "Linie ausfüllen", + "fill-line-opacity" : "Füllopazität", + "points-settings" : "Punkteinstellungen", + "show-points" : "Punkte anzeigen", + "points-line-width" : "Linienbreite der Punkte", + "points-radius" : "Punkteradius", + "point-shape" : "Punktform", + "point-shape-circle" : "Kreis", + "point-shape-cross" : "Kreuz", + "point-shape-diamond" : "Diamant", + "point-shape-square" : "Quadrat", + "point-shape-triangle" : "Dreieck", + "point-shape-custom" : "Benutzerdefinierte Funktion", + "point-shape-draw-function" : "Punktzeichnungsfunktion", + "show-separate-axis" : "Separate Achse anzeigen", + "axis-position-left" : "Links", + "axis-position-right" : "Rechts", + "thresholds" : "Schwellenwerte", + "no-thresholds" : "Keine Schwellenwerte konfiguriert", + "add-threshold" : "Schwellenwert hinzufügen", + "show-values-for-comparison" : "Historische Vergleichswerte anzeigen", + "comparison-values-label" : "Beschriftung historischer Werte", + "comparison-line-color" : "Vergleichslinienfarbe", + "threshold-settings" : "Schwellenwert-Einstellungen", + "use-as-threshold" : "Schlüsselwert als Schwellenwert verwenden", + "threshold-line-width" : "Linienbreite des Schwellenwerts", + "threshold-color" : "Schwellenwertfarbe", + "common-pie-settings" : "Allgemeine Einstellungen für Tortendiagramme", + "radius" : "Radius", + "inner-radius" : "Innenradius", + "tilt" : "Neigung", + "common-pie-settings-range-error" : "Wert muss im Bereich von 0 bis 1 liegen", + "stroke-settings" : "Strich-Einstellungen", + "width-pixels" : "Breite (Pixel)", + "show-labels" : "Beschriftungen anzeigen", + "animation-settings" : "Animationseinstellungen", + "animated-pie" : "Tortenanimation aktivieren (experimentell)", + "border-settings" : "Rahmen-Einstellungen", + "border-width" : "Rahmenbreite", + "border-color" : "Rahmenfarbe", + "legend-settings" : "Legenden-Einstellungen", + "display-legend" : "Legende anzeigen", + "labels-font-color" : "Schriftfarbe der Beschriftungen", + "series" : "Reihen", + "add-series" : "Reihe hinzufügen", + "series-settings" : "Reiheneinstellungen", + "remove-series" : "Reihe entfernen", + "no-series" : "Keine Reihen konfiguriert", + "no-series-error" : "Mindestens eine Reihe muss angegeben werden", + "chart-appearance" : "Diagramm-Darstellung", + "vertical-grid-lines" : "Vertikale Gitterlinien", + "horizontal-grid-lines" : "Horizontale Gitterlinien", + "chart-background" : "Diagrammhintergrund", + "grid-lines-color" : "Gitterlinienfarbe", + "border" : "Rahmen", + "axis" : "Achse", + "vertical-axis" : "Vertikale Achse", + "ticks" : "Ticks", + "horizontal-axis" : "Horizontale Achse", + "shape-empty-circle" : "Leerer Kreis", + "shape-circle" : "Kreis", + "shape-rect" : "Rechteck", + "shape-round-rect" : "Abgerundetes Rechteck", + "shape-triangle" : "Dreieck", + "shape-diamond" : "Diamant", + "shape-pin" : "Pin", + "shape-arrow" : "Pfeil", + "shape-none" : "Keine", + "line-type-solid" : "Durchgezogen", + "line-type-dashed" : "Gestrichelt", + "line-type-dotted" : "Gepunktet", + "label-position-top" : "Oben", + "label-position-bottom" : "Unten", + "label-position-outside" : "Außerhalb", + "label-position-inside" : "Innerhalb", + "fill" : "Füllung", + "fill-type-none" : "Keine", + "fill-type-solid" : "Voll", + "fill-type-opacity" : "Deckkraft", + "fill-type-gradient" : "Farbverlauf", + "background" : "Hintergrund", + "opacity" : "Deckkraft", + "gradient-stops" : "Farbverlauf-Stops", + "gradient-start" : "Start", + "gradient-end" : "Ende", + "animation" : { + "animation" : "Animation", + "animation-threshold" : "Animationsschwelle", + "animation-duration" : "Animationsdauer", + "animation-easing" : "Animationseffekt", + "animation-delay" : "Animationsverzögerung", + "update-animation-duration" : "Dauer der Aktualisierungsanimation", + "update-animation-easing" : "Effekt der Aktualisierungsanimation", + "update-animation-delay" : "Verzögerung der Aktualisierungsanimation" + }, + "chart-axis" : { + "scale" : "Skalierung", + "scale-min" : "min", + "scale-max" : "max", + "scale-auto" : "Auto" + }, + "bar" : { + "show-border" : "Rahmen anzeigen", + "border-width" : "Rahmenbreite", + "border-radius" : "Rahmenradius", + "bar-width" : "Balkenbreite", + "label" : "Beschriftung", + "label-hint" : "Beschriftung über dem Balken anzeigen.", + "series-label-hint" : "Beschriftung mit Wert über dem Balken anzeigen.", + "label-background" : "Beschriftungshintergrund" + } + }, + "color" : { + "color-settings" : "Farbeinstellungen", + "color-type-constant" : "Konstant", + "color-type-gradient" : "Farbverlauf", + "color-type-range" : "Bereich", + "color-type-function" : "Funktion", + "color" : "Farbe", + "value-range" : "Wertebereich", + "from" : "Von", + "to" : "Bis", + "color-function" : "Farbfunktion", + "copy-color-settings-from" : "Farbeinstellungen kopieren von", + "copy-from" : "Kopieren von", + "settings-type" : "Einstellungstyp", + "basic-mode" : "Einfach", + "advanced-mode" : "Erweitert", + "entity-alias" : "Entitätsalias", + "entity-attribute" : "Entitätsattribut", + "gradient-color" : "Farbverlauf", + "gradient-color-min" : "Farbe", + "gradient-start" : "Verlaufsstartfarbe", + "gradient-start-min" : "Start", + "gradient-end" : "Verlaufsendfarbe", + "gradient-end-min" : "Ende", + "start-value" : "Startwert", + "end-value" : "Endwert", + "gradient-type" : "Verlaufsart" + }, + "dashboard-state" : { + "dashboard-state-settings" : "Dashboard-Zustandseinstellungen", + "dashboard-state" : "Dashboard-Zustands-ID", + "autofill-state-layout" : "Layout-Höhe des Zustands automatisch ausfüllen", + "default-margin" : "Standard-Widget-Abstand", + "default-background-color" : "Standardhintergrundfarbe", + "sync-parent-state-params" : "Zustandsparameter mit übergeordnetem Dashboard synchronisieren" + }, + "date-range-navigator" : { + "date-range-picker-settings" : "Einstellungen für Datumsbereich-Auswahl", + "hide-date-range-picker" : "Datumsbereich-Auswahl ausblenden", + "picker-one-panel" : "Datumsbereich-Auswahl in einem Panel", + "picker-auto-confirm" : "Datumsbereich-Auswahl automatisch bestätigen", + "picker-show-template" : "Datumsbereich-Auswahl Vorlage anzeigen", + "first-day-of-week" : "Erster Wochentag", + "interval-settings" : "Intervalleinstellungen", + "hide-interval" : "Intervall ausblenden", + "initial-interval" : "Anfängliches Intervall", + "interval-hour" : "Stunde", + "interval-day" : "Tag", + "interval-week" : "Woche", + "interval-two-weeks" : "2 Wochen", + "interval-month" : "Monat", + "interval-three-months" : "3 Monate", + "interval-six-months" : "6 Monate", + "step-settings" : "Schritteinstellungen", + "hide-step-size" : "Schrittgröße ausblenden", + "initial-step-size" : "Anfängliche Schrittgröße", + "hide-labels" : "Beschriftungen ausblenden", + "use-session-storage" : "Sitzungsspeicher verwenden", + "localizationMap" : { + "Sun" : "So", + "Mon" : "Mo", + "Tue" : "Di", + "Wed" : "Mi", + "Thu" : "Do", + "Fri" : "Fr", + "Sat" : "Sa", + "Jan" : "Jan", + "Feb" : "Feb", + "Mar" : "Mär", + "Apr" : "Apr", + "May" : "Mai", + "Jun" : "Jun", + "Jul" : "Jul", + "Aug" : "Aug", + "Sep" : "Sep", + "Oct" : "Okt", + "Nov" : "Nov", + "Dec" : "Dez", + "January" : "Januar", + "February" : "Februar", + "March" : "März", + "April" : "April", + "June" : "Juni", + "July" : "Juli", + "August" : "August", + "September" : "September", + "October" : "Oktober", + "November" : "November", + "December" : "Dezember", + "Custom Date Range" : "Benutzerdefinierter Datumsbereich", + "Date Range Template" : "Datumsbereichsvorlage", + "Today" : "Heute", + "Yesterday" : "Gestern", + "This Week" : "Diese Woche", + "Last Week" : "Letzte Woche", + "This Month" : "Dieser Monat", + "Last Month" : "Letzter Monat", + "Year" : "Jahr", + "This Year" : "Dieses Jahr", + "Last Year" : "Letztes Jahr", + "Date picker" : "Datumswähler", + "Hour" : "Stunde", + "Day" : "Tag", + "Week" : "Woche", + "2 weeks" : "2 Wochen", + "Month" : "Monat", + "3 months" : "3 Monate", + "6 months" : "6 Monate", + "Custom interval" : "Benutzerdefiniertes Intervall", + "Interval" : "Intervall", + "Step size" : "Schrittgröße", + "Ok" : "Ok" } }, - "input-widgets": { - "attribute-not-allowed": "Attributparameter können in diesem Widget nicht verwendet werden", - "date": "Datum", - "discard-changes": "Änderungen verwerfen", - "entity-attribute-required": "Entitätsattribut ist erforderlich", - "entity-timeseries-required": "Zeitreihen für Entitäten sind erforderlich", - "not-allowed-entity": "Die ausgewählte Entität kann keine gemeinsamen Attribute haben", - "no-attribute-selected": "Es ist kein Attribut ausgewählt", - "no-datakey-selected": "Es ist kein Datenschlüssel ausgewählt", - "no-entity-selected": "Keine Entität ausgewählt", - "no-image": "Kein Bild", - "no-support-web-camera": "Keine unterstützte Webcam", - "no-timeseries-selected": "Keine Zeitreihen ausgewählt", - "switch-attribute-value": "Entitätsattributwert wechseln", - "switch-camera": "Kamera wechseln", - "switch-timeseries-value": "Wert für Zeitreihen von Entitäten wechseln", - "take-photo": "Foto machen", - "time": "Zeit", - "timeseries-not-allowed": "Der Timeseries-Parameter kann in diesem Widget nicht verwendet werden", - "update-failed": "Aktualisierung fehlgeschlagen", - "update-successful": "Aktualisierung erfolgreich", - "update-attribute": "Attribut aktualisieren", - "update-timeseries": "Zeitreihen aktualisieren", - "value": "Wert" + "doughnut" : { + "doughnut-appearance" : "Donut-Diagramm Darstellung", + "layout" : "Layout", + "layout-default" : "Standard", + "layout-with-total" : "Mit Gesamtwert", + "central-total-value" : "Zentraler Gesamtwert", + "doughnut-card-style" : "Donut-Kartenstil" + }, + "entities-hierarchy" : { + "hierarchy-data-settings" : "Hierarchiedaten-Einstellungen", + "relations-query-function" : "Beziehungsabfragefunktion der Knoten", + "has-children-function" : "Funktion zur Erkennung von Kindknoten", + "node-state-settings" : "Knotenstatus-Einstellungen", + "node-opened-function" : "Standardfunktion für geöffnete Knoten", + "node-disabled-function" : "Funktion für deaktivierte Knoten", + "display-settings" : "Anzeigeeinstellungen", + "node-icon-function" : "Knotensymbolfunktion", + "node-text-function" : "Knotentextfunktion", + "sort-settings" : "Sortiereinstellungen", + "nodes-sort-function" : "Knotensortierfunktion" + }, + "edge" : { + "display-default-title" : "Standardtitel anzeigen" + }, + "gateway" : { + "general-settings" : "Allgemeine Einstellungen", + "widget-title" : "Widget-Titel", + "default-archive-file-name" : "Standardname für Archivdatei", + "device-type-for-new-gateway" : "Gerätetyp für neues Gateway", + "messages-settings" : "Nachrichteneinstellungen", + "save-config-success-message" : "Nachricht bei erfolgreichem Speichern der Gateway-Konfiguration", + "device-name-exists-message" : "Nachricht, wenn ein Gerät mit dem eingegebenen Namen bereits existiert", + "gateway-title" : "Gateway-Formular", + "read-only" : "Nur lesen", + "events-title" : "Titel des Gateway-Ereignisformulars", + "events-filter" : "Ereignisfilter", + "event-key-contains" : "Ereignisschlüssel enthält...", + "show-connector" : "Für den Konnektor anzeigen", + "connector-state-param-key" : "Statusparameter-Schlüssel des Konnektors", + "message" : "Nachricht", + "level" : "Ebene", + "created-time" : "Erstellungszeit" + }, + "gauge" : { + "default-color" : "Standardfarbe", + "radial-gauge-settings" : "Einstellungen für Radialanzeige", + "ticks-settings" : "Skaleneinstellungen", + "min-value" : "Minimalwert", + "max-value" : "Maximalwert", + "min-value-short" : "min", + "max-value-short" : "max", + "start-ticks-angle" : "Startwinkel der Skalen", + "ticks-angle" : "Skalenwinkel", + "major-ticks" : "Große Skalen", + "major-ticks-count" : "Anzahl der großen Skalen", + "major-ticks-color" : "Farbe der großen Skalen", + "minor-ticks" : "Kleine Skalen", + "minor-ticks-count" : "Anzahl der kleinen Skalen", + "minor-ticks-color" : "Farbe der kleinen Skalen", + "tick-numbers-font" : "Schriftart der Skalenzahlen", + "unit-title-settings" : "Einheitstitel-Einstellungen", + "show-unit-title" : "Einheitstitel anzeigen", + "unit-title" : "Einheitstitel", + "title-font" : "Schriftart des Titels", + "units-settings" : "Einheitseinstellungen", + "units-font" : "Schriftart der Einheit", + "value-box-settings" : "Wertanzeige-Einstellungen", + "show-value-box" : "Wertanzeige anzeigen", + "value-box" : "Wertanzeige", + "value-int" : "Ziffernanzahl für den ganzzahligen Wertanteil", + "value-text" : "Werttext", + "value-text-shadow" : "Werttext-Schatten", + "value-font" : "Werttext-Schriftart", + "rect-stroke-color-start" : "Rahmenfarbe Rechteck - Anfangsverlauf", + "rect-stroke-color-end" : "Rahmenfarbe Rechteck - Endverlauf", + "background-color" : "Hintergrundfarbe", + "shadow-color" : "Schattenfarbe", + "value-box-rect-stroke-color" : "Rahmenfarbe Wertanzeige-Rechteck", + "value-box-rect-stroke-color-end" : "Rahmenfarbe Wertanzeige-Rechteck - Endverlauf", + "value-box-background-color" : "Hintergrundfarbe der Wertanzeige", + "value-box-shadow-color" : "Schattenfarbe der Wertanzeige", + "plate-settings" : "Platteneinstellungen", + "show-plate-border" : "Plattenrand anzeigen", + "plate-color" : "Plattenfarbe", + "needle-settings" : "Zeigereinstellungen", + "needle-circle-size" : "Zeigerkreisgröße", + "needle-color" : "Zeigerfarbe", + "needle-color-start" : "Zeigerfarbe - Anfangsverlauf", + "needle-color-end" : "Zeigerfarbe - Endverlauf", + "needle-color-shadow-up" : "Obere Schattenfarbe des Zeigers", + "needle-color-shadow-down" : "Schlagschatten", + "highlights-settings" : "Hervorhebungseinstellungen", + "highlights-width" : "Hervorhebungsbreite", + "highlights" : "Hervorhebungen", + "highlight-from" : "Von", + "highlight-to" : "Bis", + "highlight-color" : "Farbe", + "no-highlights" : "Keine Hervorhebungen konfiguriert", + "add-highlight" : "Hervorhebung hinzufügen", + "animation-settings" : "Animationseinstellungen", + "enable-animation" : "Animation aktivieren", + "animation-duration-rule" : "Animationsdauer und -regel", + "animation-duration" : "Animationsdauer", + "animation-rule" : "Animationsregel", + "animation-linear" : "Linear", + "animation-quad" : "Quadratisch", + "animation-quint" : "Fünfpolig", + "animation-cycle" : "Zyklus", + "animation-bounce" : "Prallen", + "animation-elastic" : "Elastisch", + "animation-dequad" : "Ent-Quadratisch", + "animation-dequint" : "Ent-Fünfpolig", + "animation-decycle" : "Ent-Zyklus", + "animation-debounce" : "Ent-Prallen", + "animation-delastic" : "Ent-Elastisch", + "linear-gauge-settings" : "Einstellungen für Linearanzeige", + "bar-stroke" : "Balkenrand", + "bar-stroke-width" : "Breite des Balkenrands", + "bar-stroke-color" : "Farbe des Balkenrands", + "bar-background-color" : "Balkenhintergrundfarbe - Anfangsverlauf", + "bar-background-color-end" : "Balkenhintergrundfarbe - Endverlauf", + "progress-bar-color" : "Farbe der Fortschrittsanzeige", + "progress-bar" : "Fortschrittsanzeige", + "progress-bar-color-start" : "Fortschrittsanzeige-Farbe - Anfangsverlauf", + "progress-bar-color-end" : "Fortschrittsanzeige-Farbe - Endverlauf", + "major-ticks-names" : "Bezeichnungen der großen Skalen", + "show-stroke-ticks" : "Skalenstrich anzeigen", + "major-ticks-font" : "Schriftart der großen Skalen", + "border-color" : "Rahmenfarbe", + "border-width" : "Rahmenbreite", + "needle-circle" : "Zeigerkreis", + "needle-circle-color" : "Zeigerkreisfarbe", + "animation-target" : "Animationselement", + "animation-target-needle" : "Zeiger", + "animation-target-plate" : "Platte", + "common-settings" : "Allgemeine Anzeigeeinstellungen", + "gauge-type" : "Anzeigetyp", + "gauge-type-arc" : "Bogen", + "gauge-type-donut" : "Donut", + "gauge-type-horizontal-bar" : "Horizontale Leiste", + "gauge-type-vertical-bar" : "Vertikale Leiste", + "donut-start-angle" : "Startwinkel (Grad)", + "bar-settings" : "Leistenanzeige-Einstellungen", + "relative-bar-width" : "Relative Leistenbreite", + "neon-glow-brightness" : "Helligkeit des Neonlichteffekts (0-100)", + "neon-glow-brightness-hint" : "0 - Effekt deaktivieren", + "stripes-thickness" : "Streifendicke", + "stripes-thickness-hint" : "0 - keine Streifen", + "rounded-line-cap" : "Abgerundete Linienenden", + "bar-color-settings" : "Leistenfarbeneinstellungen", + "use-precise-level-color-values" : "Genaue Farbstufen verwenden", + "bar-colors" : "Leistenfarben, von unten nach oben", + "color" : "Farbe", + "no-bar-colors" : "Keine Leistenfarben konfiguriert", + "add-bar-color" : "Leistenfarbe hinzufügen", + "from" : "Von", + "to" : "Bis", + "fixed-level-colors" : "Leistenfarben mit Grenzwerten", + "gauge-title-settings" : "Titelanzeige-Einstellungen", + "show-gauge-title" : "Titel der Anzeige anzeigen", + "gauge-title" : "Anzeigetitel", + "gauge-title-font" : "Schriftart des Anzeigetitels", + "unit-title-and-timestamp-settings" : "Einheitentitel- und Zeitstempel-Einstellungen", + "show-timestamp" : "Zeitstempel", + "timestamp-format" : "Zeitstempelformat", + "label-font" : "Schriftart der unter dem Wert angezeigten Beschriftung", + "value-settings" : "Wertanzeige-Einstellungen", + "show-value" : "Werttext anzeigen", + "min-max-settings" : "Min./Max.-Beschriftungseinstellungen", + "show-min-max" : "Min. und Max. Werte anzeigen", + "min-max-font" : "Schriftart für Min./Max.-Beschriftungen", + "show-ticks" : "Skalen anzeigen", + "tick-width" : "Skalenbreite", + "tick-color" : "Skalenfarbe", + "tick-values" : "Skalenwerte", + "no-tick-values" : "Keine Skalenwerte konfiguriert", + "add-tick-value" : "Skalenwert hinzufügen", + "gauge-appearance" : "Anzeigestil", + "units-title" : "Einheitentitel", + "value" : "Wert", + "ticks" : "Skalen", + "arrow-and-scale-color" : "Standardfarbe für Zeiger und Skala", + "scale-settings" : "Skaleneinstellungen", + "scale" : "Skala", + "scale-color" : "Skalenfarben", + "compass-appearance" : "Kompass-Darstellung", + "label" : "Beschriftung", + "labels" : "Beschriftungen", + "label-style" : "Beschriftungsstil", + "simple-gauge-type" : "Typ", + "gauge-bar-background" : "Hintergrund der Anzeigeleiste", + "bar-color" : "Leistenfarbe", + "min-and-max-value" : "Minimal- und Maximalwert", + "min-and-max-label" : "Min.- und Max.-Beschriftung", + "font" : "Schriftart", + "tick-width-and-color" : "Skalenbreite und -farbe", + "min-max-validation-text" : "Maximalwert muss größer als Minimalwert sein" + }, + "gpio" : { + "pin" : "Pin", + "label" : "Bezeichnung", + "row" : "Zeile", + "column" : "Spalte", + "color" : "Farbe", + "panel-settings" : "Panel-Einstellungen", + "background-color" : "Hintergrundfarbe", + "gpio-switches" : "GPIO-Schalter", + "no-gpio-switches" : "Keine GPIO-Schalter konfiguriert", + "add-gpio-switch" : "GPIO-Schalter hinzufügen", + "gpio-status-request" : "GPIO-Statusabfrage", + "method-name" : "Methodenname", + "method-body" : "Methodeninhalt", + "gpio-status-change-request" : "GPIO-Statusänderungsanfrage", + "parse-gpio-status-function" : "GPIO-Status-Funktion analysieren", + "gpio-leds" : "GPIO-LEDs", + "no-gpio-leds" : "Keine GPIO-LEDs konfiguriert", + "add-gpio-led" : "GPIO-LED hinzufügen" + }, + "html-card" : { + "html" : "HTML", + "css" : "CSS" + }, + "input-widgets" : { + "attribute-not-allowed" : "Attributparameter kann in diesem Widget nicht verwendet werden", + "blocked-location" : "Geolokalisierung ist in Ihrem Browser blockiert", + "claim-device" : "Gerät beanspruchen", + "claim-failed" : "Gerätebeanspruchung fehlgeschlagen!", + "claim-not-found" : "Gerät nicht gefunden!", + "claim-successful" : "Gerät wurde erfolgreich beansprucht!", + "date" : "Datum", + "device-name" : "Gerätename", + "device-name-required" : "Gerätename ist erforderlich", + "discard-changes" : "Änderungen verwerfen", + "entity-attribute-required" : "Entitätsattribut ist erforderlich", + "entity-coordinate-required" : "Beide Felder, Breitengrad und Längengrad, sind erforderlich", + "entity-timeseries-required" : "Zeitreihe der Entität ist erforderlich", + "get-location" : "Aktuelle Position abrufen", + "invalid-date" : "Ungültiges Datum", + "latitude" : "Breitengrad", + "longitude" : "Längengrad", + "min-value-error" : "Minimalwert ist {{value}}", + "max-value-error" : "Maximalwert ist {{value}}", + "not-allowed-entity" : "Ausgewählte Entität kann keine gemeinsamen Attribute haben", + "no-attribute-selected" : "Kein Attribut ausgewählt", + "no-datakey-selected" : "Kein Daten-Schlüssel ausgewählt", + "no-coordinate-specified" : "Daten-Schlüssel für Breitengrad/Längengrad nicht angegeben", + "no-entity-selected" : "Keine Entität ausgewählt", + "no-image" : "Kein Bild", + "no-support-geolocation" : "Ihr Browser unterstützt keine Geolokalisierung", + "no-support-web-camera" : "Ihr Browser unterstützt keine Kameras", + "enable-https-use-widget" : "Bitte HTTPS aktivieren, um dieses Widget zu verwenden", + "no-found-your-camera" : "Kamera nicht gefunden", + "no-permission-camera" : "Zugriff durch Benutzer verweigert / Diese Website hat keine Berechtigung für die Kamera", + "no-timeseries-selected" : "Keine Zeitreihe ausgewählt", + "secret-key" : "Geheimer Schlüssel", + "secret-key-required" : "Geheimer Schlüssel ist erforderlich", + "switch-attribute-value" : "Attributwert der Entität umschalten", + "switch-camera" : "Kamera wechseln", + "switch-timeseries-value" : "Zeitreihenwert der Entität umschalten", + "take-photo" : "Foto aufnehmen", + "time" : "Uhrzeit", + "timeseries-not-allowed" : "Zeitreihenparameter kann in diesem Widget nicht verwendet werden", + "update-failed" : "Aktualisierung fehlgeschlagen", + "update-successful" : "Erfolgreich aktualisiert", + "update-attribute" : "Attribut aktualisieren", + "update-timeseries" : "Zeitreihe aktualisieren", + "value" : "Wert", + "general-settings" : "Allgemeine Einstellungen", + "widget-title" : "Widget-Titel", + "claim-button-label" : "Bezeichnung der Beanspruchungsschaltfläche", + "show-secret-key-field" : "Feld 'Geheimer Schlüssel' anzeigen", + "labels-settings" : "Beschriftungseinstellungen", + "show-labels" : "Beschriftungen anzeigen", + "device-name-label" : "Beschriftung für das Geräteeingabefeld", + "secret-key-label" : "Beschriftung für das Eingabefeld 'Geheimer Schlüssel'", + "messages-settings" : "Nachrichteneinstellungen", + "claim-device-success-message" : "Textnachricht bei erfolgreicher Gerätebeanspruchung", + "claim-device-not-found-message" : "Textnachricht, wenn das Gerät nicht gefunden wurde", + "claim-device-failed-message" : "Textnachricht bei fehlgeschlagener Gerätebeanspruchung", + "claim-device-name-required-message" : "Fehlermeldung 'Gerätename erforderlich'", + "claim-device-secret-key-required-message" : "Fehlermeldung 'Geheimer Schlüssel erforderlich'", + "show-label" : "Beschriftung anzeigen", + "label" : "Beschriftung", + "required" : "Erforderlich", + "required-error-message" : "Fehlermeldung 'Erforderlich'", + "show-result-message" : "Ergebnismeldung anzeigen", + "integer-field-settings" : "Einstellungen für Ganzzahl-Feld", + "min-value" : "Mindestwert", + "max-value" : "Höchstwert", + "double-field-settings" : "Einstellungen für Dezimalzahl-Feld", + "text-field-settings" : "Einstellungen für Textfeld", + "min-length" : "Minimale Länge", + "max-length" : "Maximale Länge", + "checkbox-settings" : "Einstellungen für Kontrollkästchen", + "true-label" : "Bezeichnung für aktiviert", + "false-label" : "Bezeichnung für deaktiviert", + "image-input-settings" : "Einstellungen für Bildeingabe", + "display-preview" : "Vorschau anzeigen", + "display-clear-button" : "Schaltfläche 'Löschen' anzeigen", + "display-apply-button" : "Schaltfläche 'Übernehmen' anzeigen", + "display-discard-button" : "Schaltfläche 'Verwerfen' anzeigen", + "datetime-field-settings" : "Einstellungen für Datum/Zeit-Feld", + "display-time-input" : "Zeiteingabe anzeigen", + "latitude-key-name" : "Name des Schlüssels für Breitengrad", + "longitude-key-name" : "Name des Schlüssels für Längengrad", + "show-get-location-button" : "Schaltfläche 'Aktuelle Position abrufen' anzeigen", + "use-high-accuracy" : "Hohe Genauigkeit verwenden", + "location-fields-settings" : "Einstellungen für Standortfelder", + "latitude-label" : "Bezeichnung für Breitengrad", + "longitude-label" : "Bezeichnung für Längengrad", + "input-fields-alignment" : "Ausrichtung der Eingabefelder", + "input-fields-alignment-column" : "Spalte (Standard)", + "input-fields-alignment-row" : "Zeile", + "layout" : "Layout", + "row-gap" : "Abstand zwischen Zeilen (in Pixel)", + "column-gap" : "Abstand zwischen Spalten (in Pixel)", + "latitude-field-required" : "Breitengrad-Feld erforderlich", + "longitude-field-required" : "Längengrad-Feld erforderlich", + "attribute-settings" : "Attribut-Einstellungen", + "widget-mode" : "Widget-Modus", + "widget-mode-update-attribute" : "Attribut aktualisieren", + "widget-mode-update-timeseries" : "Zeitreihe aktualisieren", + "attribute-scope" : "Attributbereich", + "attribute-scope-server" : "Serverattribut", + "attribute-scope-shared" : "Geteiltes Attribut", + "value-required" : "Wert erforderlich", + "image-settings" : "Bildeinstellungen", + "image-format" : "Bildformat", + "image-format-jpeg" : "JPEG", + "image-format-png" : "PNG", + "image-format-webp" : "WEBP", + "image-quality" : "Bildqualität bei verlustbehafteter Komprimierung (z. B. JPEG, WEBP)", + "max-image-width" : "Maximale Bildbreite", + "max-image-height" : "Maximale Bildhöhe", + "action-buttons" : "Aktionsschaltflächen", + "show-action-buttons" : "Aktionsschaltflächen anzeigen", + "update-all-values" : "Alle Werte aktualisieren, nicht nur geänderte", + "save-button-label" : "Bezeichnung der Schaltfläche 'SPEICHERN'", + "reset-button-label" : "Bezeichnung der Schaltfläche 'RÜCKGÄNGIG'", + "group-settings" : "Gruppeneinstellungen", + "show-group-title" : "Titel für Gruppe von Feldern anzeigen", + "group-title" : "Gruppentitel", + "fields-alignment" : "Feld-Ausrichtung", + "fields-alignment-row" : "Zeile (Standard)", + "fields-alignment-column" : "Spalte", + "fields-in-row" : "Anzahl Felder pro Zeile", + "option-value" : "Wert (für leere Option 'null' schreiben)", + "option-label" : "Bezeichnung", + "hide-input-field" : "Eingabefeld ausblenden", + "datakey-type" : "Typ des Daten-Schlüssels", + "datakey-type-server" : "Serverattribut (Standard)", + "datakey-type-shared" : "Geteiltes Attribut", + "datakey-type-timeseries" : "Zeitreihe", + "datakey-value-type" : "Datenschlüssel-Werttyp", + "datakey-value-type-string" : "Zeichenkette", + "datakey-value-type-double" : "Dezimalzahl", + "datakey-value-type-integer" : "Ganzzahl", + "datakey-value-type-json" : "JSON", + "datakey-value-type-boolean-checkbox" : "Boolesch (Kontrollkästchen)", + "datakey-value-type-boolean-switch" : "Boolesch (Schalter)", + "datakey-value-type-date-time" : "Datum & Zeit", + "datakey-value-type-date" : "Datum", + "datakey-value-type-time" : "Zeit", + "datakey-value-type-select" : "Auswahl", + "datakey-value-type-radio" : "Radio", + "datakey-value-type-color" : "Farbe", + "value-is-required" : "Wert ist erforderlich", + "ability-to-edit-attribute" : "Fähigkeit, Attribut zu bearbeiten", + "ability-to-edit-attribute-editable" : "Bearbeitbar (Standard)", + "ability-to-edit-attribute-disabled" : "Deaktiviert", + "ability-to-edit-attribute-readonly" : "Nur-Lesen", + "disable-on-datakey-name" : "Deaktivieren bei falschem Wert eines anderen Daten-Schlüssels (Schlüsselname angeben)", + "field-appearance" : "Erscheinungsbild des Feldes", + "appearance-fill" : "Ausfüllen", + "appearance-outline" : "Umriss", + "subscript-sizing" : "Größenanpassung für Tiefstellung", + "subscript-sizing-fixed" : "Fest", + "subscript-sizing-dynamic" : "Dynamisch", + "slide-toggle-settings" : "Einstellungen für Schiebeschalter", + "slide-toggle-label-position" : "Position des Schiebeschalter-Labels", + "slide-toggle-label-position-after" : "Nachher", + "slide-toggle-label-position-before" : "Vorher", + "select-options" : "Auswahloptionen", + "no-select-options" : "Keine Auswahloptionen konfiguriert", + "add-select-option" : "Auswahloption hinzufügen", + "numeric-field-settings" : "Einstellungen für numerisches Feld", + "step-interval" : "Schrittintervall zwischen Werten", + "error-messages" : "Fehlermeldungen", + "min-value-error-message" : "Fehlermeldung 'Mindestwert'", + "max-value-error-message" : "Fehlermeldung 'Höchstwert'", + "invalid-date-error-message" : "Fehlermeldung 'Ungültiges Datum'", + "invalid-JSON-error-message" : "Fehlermeldung 'Ungültiges JSON'", + "icon-settings" : "Symboleinstellungen", + "dialog-editor-settings" : "Einstellungen für Dialogeditor", + "use-custom-icon" : "Benutzerdefiniertes Symbol verwenden", + "input-cell-icon" : "Symbol vor Eingabezelle anzeigen", + "value-conversion-settings" : "Wertkonvertierungseinstellungen", + "get-value-settings" : "Einstellungen für Werteabruf", + "use-get-value-function" : "getValue-Funktion verwenden", + "get-value-function" : "getValue-Funktion", + "set-value-settings" : "Einstellungen zum Setzen des Wertes", + "use-set-value-function" : "setValue-Funktion verwenden", + "set-value-function" : "setValue-Funktion", + "json-invalid" : "JSON-Wert hat ein ungültiges Format", + "title" : "Titel", + "cancel-button-label" : "Bezeichnung der Schaltfläche 'Abbrechen'", + "radio-button-settings" : "Einstellungen für Optionsfelder", + "color" : "Farbe", + "columns" : "Spalten", + "radio-options" : "Optionsfeldoptionen", + "no-radio-options" : "Keine Optionsfeldoptionen konfiguriert", + "add-radio-option" : "Optionsfeldoption hinzufügen", + "radio-label-position" : "Labelposition", + "radio-label-position-before" : "Vorher", + "radio-label-position-after" : "Nachher" + }, + "invalid-qr-code-text" : "Ungültiger Eingabetext für QR-Code. Eingabe sollte vom Typ String sein", + "qr-code" : { + "use-qr-code-text-function" : "QR-Code-Textfunktion verwenden", + "qr-code-text-pattern" : "QR-Code-Textmuster (z. B. '${entityName} | ${keyName} - ein Text.')", + "qr-code-text-pattern-hint" : "QR-Code-Textmuster verwendet den Wert des zuerst gefundenen Schlüssels in den Entitäten im Entitätsalias.", + "qr-code-text-pattern-required" : "QR-Code-Textmuster ist erforderlich.", + "qr-code-text-function" : "QR-Code-Textfunktion" + }, + "label-widget" : { + "label-pattern" : "Muster", + "label-pattern-hint" : "Hinweis: z. B. 'Text ${keyName} Einheiten.' oder ${#<key index>} Einheiten", + "label-pattern-required" : "Muster ist erforderlich", + "label-position" : "Position (Prozent relativ zum Hintergrund)", + "x-pos" : "X", + "y-pos" : "Y", + "background-color" : "Hintergrundfarbe", + "font-settings" : "Schriftarteinstellungen", + "background-image" : "Hintergrundbild", + "labels" : "Beschriftungen", + "no-labels" : "Keine Beschriftungen konfiguriert", + "add-label" : "Beschriftung hinzufügen" + }, + "navigation" : { + "title" : "Titel", + "navigation-path" : "Navigationspfad", + "filter-type" : "Filtertyp", + "filter-type-all" : "Alle Elemente", + "filter-type-include" : "Elemente einbeziehen", + "filter-type-exclude" : "Elemente ausschließen", + "items" : "Elemente", + "enter-urls-to-filter" : "URLs zum Filtern eingeben..." + }, + "persistent-table" : { + "rpc-id" : "RPC-ID", + "message-type" : "Nachrichtentyp", + "method" : "Methode", + "params" : "Parameter", + "created-time" : "Erstellungszeit", + "expiration-time" : "Ablaufzeit", + "retries" : "Wiederholungen", + "status" : "Status", + "filter" : "Filter", + "refresh" : "Aktualisieren", + "add" : "RPC-Anfrage hinzufügen", + "details" : "Details", + "delete" : "Löschen", + "delete-request-title" : "Persistente RPC-Anfrage löschen", + "delete-request-text" : "Sind Sie sicher, dass Sie die Anfrage löschen möchten?", + "details-title" : "Details RPC-ID: ", + "additional-info" : "Zusätzliche Informationen", + "response" : "Antwort", + "any-status" : "Beliebiger Status", + "rpc-status-list" : "RPC-Statusliste", + "no-request-prompt" : "Keine Anfrage zum Anzeigen vorhanden", + "send-request" : "Anfrage senden", + "add-title" : "Persistente RPC-Anfrage erstellen", + "method-error" : "Methode ist erforderlich.", + "timeout-error" : "Mindest-Timeout-Wert ist 5000 (5 Sekunden).", + "white-space-error" : "Leerzeichen sind nicht erlaubt.", + "rpc-status" : { + "QUEUED" : "WARTESCHLANGE", + "SENT" : "GESENDET", + "DELIVERED" : "ZUGESTELLT", + "SUCCESSFUL" : "ERFOLGREICH", + "TIMEOUT" : "ZEITÜBERSCHREITUNG", + "EXPIRED" : "ABGELAUFEN", + "FAILED" : "FEHLGESCHLAGEN" + }, + "rpc-search-status-all" : "ALLE", + "message-types" : { + "false" : "Zweiwege", + "true" : "Einweg" + }, + "general-settings" : "Allgemeine Einstellungen", + "enable-filter" : "Filter aktivieren", + "enable-sticky-header" : "Kopfzeile beim Scrollen anzeigen", + "enable-sticky-action" : "Aktionsspalte beim Scrollen anzeigen", + "display-request-details" : "Anfragedetails anzeigen", + "allow-send-request" : "Senden von RPC-Anfragen erlauben", + "allow-delete-request" : "Löschen von Anfragen erlauben", + "columns-settings" : "Spalteneinstellungen", + "display-columns" : "Anzuzeigende Spalten", + "column" : "Spalte", + "no-columns-found" : "Keine Spalten gefunden", + "no-columns-matching" : "'{{column}}' nicht gefunden." + }, + "range-chart" : { + "chart" : "Diagramm", + "data-zoom" : "Datenzoom", + "range-chart-appearance" : "Darstellung des Bereichsdiagramms", + "range-colors" : "Bereichsfarben", + "out-of-range-color" : "Farbe außerhalb des Bereichs", + "show-range-thresholds" : "Bereichsschwellen anzeigen", + "range-thresholds-settings" : "Bereichsschwellen-Einstellungen", + "fill-area" : "Bereich füllen", + "fill-area-opacity" : "Füllbereichsdeckkraft", + "range-chart-style" : "Stil des Bereichsdiagramms" + }, + "rpc" : { + "value-settings" : "Werteinstellungen", + "initial-value" : "Anfangswert", + "retrieve-value-settings" : "Ein-/Ausschaltwert abrufen", + "retrieve-value-method" : "Wert mit Methode abrufen", + "retrieve-value-method-none" : "Nicht abrufen", + "retrieve-value-method-rpc" : "RPC-Methode zum Abrufen verwenden", + "retrieve-value-method-attribute" : "Für Attribut abonnieren", + "retrieve-value-method-timeseries" : "Für Zeitreihe abonnieren", + "attribute-value-key" : "Attributschlüssel", + "timeseries-value-key" : "Zeitreihenschlüssel", + "get-value-method" : "RPC-Methode zum Abrufen des Werts", + "parse-value-function" : "Funktion zum Parsen des Werts", + "update-value-settings" : "Wertaktualisierungseinstellungen", + "set-value-method" : "RPC-Methode zum Setzen des Werts", + "convert-value-function" : "Wertumwandlungsfunktion", + "rpc-settings" : "RPC-Einstellungen", + "request-timeout" : "RPC-Anforderungszeitlimit (ms)", + "persistent-rpc-settings" : "Persistente RPC-Einstellungen", + "request-persistent" : "Persistente RPC-Anfrage", + "persistent-polling-interval" : "Abfrageintervall (ms) für persistente RPC-Antwort", + "common-settings" : "Allgemeine Einstellungen", + "switch-title" : "Schaltertitel", + "show-on-off-labels" : "Ein-/Aus-Beschriftungen anzeigen", + "slide-toggle-label" : "Schaltflächenbeschriftung", + "label-position" : "Beschriftungsposition", + "label-position-before" : "Vorher", + "label-position-after" : "Nachher", + "slider-color" : "Schiebereglerfarbe", + "slider-color-primary" : "Primär", + "slider-color-accent" : "Akzent", + "slider-color-warn" : "Warnung", + "button-style" : "Schaltflächenstil", + "button-raised" : "Erhöhte Schaltfläche", + "button-primary" : "Primärfarbe", + "button-background-color" : "Hintergrundfarbe der Schaltfläche", + "button-text-color" : "Textfarbe der Schaltfläche", + "widget-title" : "Widget-Titel", + "button-label" : "Schaltflächenbeschriftung", + "device-attribute-scope" : "Attributbereich des Geräts", + "server-attribute" : "Serverattribut", + "shared-attribute" : "Geteiltes Attribut", + "device-attribute-parameters" : "Geräteattributparameter", + "is-one-way-command" : "Ist Einweg-Befehl", + "rpc-method" : "RPC-Methode", + "rpc-method-params" : "RPC-Methodenparameter", + "show-rpc-error" : "RPC-Ausführungsfehler anzeigen", + "led-title" : "LED-Titel", + "led-color" : "LED-Farbe", + "check-status-settings" : "Statusprüfungseinstellungen", + "perform-rpc-status-check" : "RPC-Statusprüfung des Geräts durchführen", + "retrieve-led-status-value-method" : "LED-Statuswert mit Methode abrufen", + "led-status-value-attribute" : "Geräteattribut mit LED-Statuswert", + "led-status-value-timeseries" : "Zeitreihe des Geräts mit LED-Statuswert", + "check-status-method" : "RPC-Methode zur Geräteprüfung", + "parse-led-status-value-function" : "Funktion zum Parsen des LED-Statuswerts", + "knob-title" : "Drehregler-Titel", + "min-value" : "Minimalwert", + "max-value" : "Maximalwert" + }, + "maps" : { + "map-type" : { + "type" : "Kartentyp", + "map" : "Karte", + "image" : "Bild" + }, + "image" : { + "image-source" : "Bildquelle", + "image-source-image" : "Bild", + "image-source-entity-key" : "Entitätsschlüssel", + "source-entity-alias" : "Alias der Quell-Entität", + "image-url-key" : "Bild-URL-Schlüssel", + "image-url-key-required" : "Bild-URL-Schlüssel ist erforderlich" + }, + "control" : { + "map-controls" : "Kartensteuerungen", + "position" : "Position", + "position-topleft" : "Oben links", + "position-topright" : "Oben rechts", + "position-bottomleft" : "Unten links", + "position-bottomright" : "Unten rechts", + "zoom-actions" : "Zoom-Aktionen", + "zoom-scroll" : "Scrollen", + "zoom-double-click" : "Doppelklick", + "zoom-control-buttons" : "Steuerungsknöpfe", + "scale" : "Maßstab", + "scale-metric" : "Metrisch", + "scale-imperial" : "Imperial", + "switch-to-drag-mode-using-button" : "Zum Drag-Modus per Schaltfläche wechseln" + }, + "timeline" : { + "control-panel" : "Zeitleisten-Bedienfeld", + "time-step" : "Zeitschritt", + "speed-options" : "Geschwindigkeitsoptionen", + "timestamp" : "Zeitstempel", + "snap-to-real-location" : "An reale Position ausrichten", + "location-snap-filter-function" : "Filterfunktion zur Positionsausrichtung", + "no-trips-data-available" : "Keine Reisedaten verfügbar" + }, + "map-action" : { + "map-action-buttons" : "Kartenaktionsschaltflächen", + "label" : "Beschriftung", + "icon" : "Symbol", + "color" : "Farbe", + "action" : "Aktion", + "add-button" : "Schaltfläche hinzufügen", + "no-action-buttons-configured" : "Keine Aktionsschaltflächen konfiguriert", + "remove-action-button" : "Aktionsschaltfläche entfernen", + "map-action-button" : "Kartenaktionsschaltfläche", + "button-requires" : "Schaltfläche erfordert Beschriftung oder Symbol" + }, + "common" : { + "common-map-settings" : "Allgemeine Karteneinstellungen", + "fit-map-bounds" : "Kartenbegrenzung an alle Marker anpassen", + "default-map-center-position" : "Standardposition des Kartenmittelpunkts", + "default-map-zoom-level" : "Standard-Zoomstufe der Karte", + "entities-limit" : "Anzahl der zu ladenden Entitäten" + }, + "layer" : { + "label" : "Beschriftung", + "layer" : "Ebene", + "layers" : "Ebenen", + "map-layers" : "Kartenebenen", + "add-layer" : "Ebene hinzufügen", + "layer-settings" : "Ebeneneinstellungen", + "remove-layer" : "Ebene entfernen", + "no-layers" : "Keine Ebenen konfiguriert", + "roadmap" : "Straßenkarte", + "satellite" : "Satellit", + "hybrid" : "Hybrid", + "reference" : { + "reference-layer" : "Referenzebene", + "no-layer" : "Keine Ebene", + "openstreetmap-hybrid" : "OpenStreetMap Hybrid", + "world-edition-hybrid" : "Weltedition Hybrid", + "enhanced-contrast-hybrid" : "Hybrid mit erhöhtem Kontrast" + }, + "provider" : { + "provider" : "Anbieter", + "openstreet" : { + "title" : "OpenStreet", + "mapnik" : "Mapnik", + "hot" : "HOT", + "esri-street" : "WorldStreetMap", + "esri-topo" : "WorldTopoMap", + "esri-imagery" : "WorldImagery", + "cartodb-positron" : "Positron", + "cartodb-dark-matter" : "DarkMatter" + }, + "google" : { + "title" : "Google", + "roadmap" : "Straßenkarte", + "satellite" : "Satellit", + "hybrid" : "Hybrid", + "terrain" : "Gelände" + }, + "here" : { + "title" : "HERE", + "normal-day" : "Normal (Tag)", + "normal-night" : "Normal (Nacht)", + "hybrid-day" : "Hybrid (Tag)", + "terrain-day" : "Gelände (Tag)" + }, + "tencent" : { + "title" : "Tencent", + "normal" : "Normal", + "satellite" : "Satellit", + "terrain" : "Gelände" + }, + "custom" : { + "title" : "Benutzerdefiniert", + "tile-url" : "Kachel-URL" + } + }, + "credentials" : { + "credentials" : "Zugangsdaten", + "api-key" : "API-Schlüssel" + } + }, + "overlays" : { + "overlays" : "Overlays", + "overlays-hint" : "Konfigurieren Sie Datenquellen, Darstellung, Verhalten, Bearbeitungsoptionen und Gruppierung für Kartenentitäten", + "trips" : "Reisen", + "markers" : "Marker", + "polygons" : "Polygone", + "circles" : "Kreise" + }, + "data-layer" : { + "source" : "Quelle", + "filter" : "Filter", + "additional-data-keys" : "Zusätzliche Daten-Schlüssel", + "additional-datasources" : "Zusätzliche Datenquellen", + "additional-datasources-hint" : "Datenquelle für den Zugriff auf Attribute oder Telemetriedaten von Entitäten, die nicht auf der Karte angezeigt werden, nutzbar in Karten-Overlay-Funktionen.", + "more-datasources" : "Weitere Datenquellen", + "data-keys" : "Daten-Schlüssel", + "add-datasource" : "Datenquelle hinzufügen", + "no-datasources" : "Keine Datenquellen konfiguriert", + "remove-datasource" : "Datenquelle entfernen", + "behavior" : "Verhalten", + "on-click" : "Beim Klicken", + "on-click-hint" : "Aktion, die beim Klicken auf das Kartenelement ausgelöst wird.", + "groups" : "Gruppen", + "groups-hint" : "Liste der Gruppennamen, die dem Overlay zugewiesen sind und zur Steuerung der Sichtbarkeit auf der Karte verwendet werden.", + "color" : "Farbe", + "color-settings" : "Farbeinstellungen", + "color-type-constant" : "Konstant", + "color-type-range" : "Bereich", + "color-type-function" : "Funktion", + "color-range-source-key" : "Schlüssel für Farbwertbereich", + "color-range-source-key-required" : "Schlüssel für Farbwertbereich ist erforderlich", + "color-range" : "Farbwertbereich", + "color-function" : "Farbfunktion", + "label" : "Beschriftung", + "tooltip" : "Tooltip", + "pattern-type-pattern" : "Muster", + "pattern-type-function" : "Funktion", + "label-pattern" : "Beschriftung (Musterbeispiele: '${entityName}', '${entityName}: (Text ${keyName} Einheiten.)')", + "label-function" : "Beschriftungsfunktion", + "tooltip-pattern" : "Tooltip (z.B. 'Text ${keyName} Einheiten.' oder Linktext)", + "tooltip-function" : "Tooltip-Funktion", + "tooltip-trigger" : "Tooltip-Auslöser", + "tooltip-trigger-click" : "Tooltip bei Klick anzeigen", + "tooltip-trigger-hover" : "Tooltip bei Hover anzeigen", + "auto-close-tooltips" : "Tooltips automatisch schließen", + "tooltip-offset" : "Tooltip-Abstand", + "tooltip-offset-horizontal" : "Horizontal", + "tooltip-offset-vertical" : "Vertikal", + "tooltip-tag-actions" : "Tag-Aktionen", + "add-tooltip-tag-action" : "Tag-Aktion hinzufügen", + "edit-tooltip-tag-action" : "Tag-Aktion bearbeiten", + "remove-tooltip-tag-action" : "Tag-Aktion entfernen", + "action-add" : "Hinzufügen", + "action-edit" : "Bearbeiten", + "action-move" : "Verschieben", + "action-remove" : "Entfernen", + "edit-instruments" : "Werkzeuge", + "persist-location-attribute-scope" : "Attributbereich zur Speicherung der Position", + "enable-snapping" : "Snapping für genaues Zeichnen aktivieren", + "enable-snapping-hint" : "Richtet neue Punkte automatisch an bestehenden Formen aus, um das Zeichnen zu erleichtern und präziser zu gestalten.", + "drag-drop-mode" : "Drag-and-Drop-Modus", + "trip" : { + "no-trips" : "Keine Fahrten konfiguriert", + "add-trip" : "Fahrt hinzufügen", + "trip-configuration" : "Fahrteinstellungen", + "remove-trip" : "Fahrt entfernen" + }, + "marker" : { + "marker" : "Marker", + "latitude-key" : "Latitude-Schlüssel", + "longitude-key" : "Longitude-Schlüssel", + "x-pos-key" : "X-Positions-Schlüssel", + "y-pos-key" : "Y-Positions-Schlüssel", + "latitude-key-required" : "Latitude-Schlüssel ist erforderlich", + "longitude-key-required" : "Longitude-Schlüssel ist erforderlich", + "x-pos-key-required" : "X-Positions-Schlüssel ist erforderlich", + "y-pos-key-required" : "Y-Positions-Schlüssel ist erforderlich", + "no-markers" : "Keine Marker konfiguriert", + "add-marker" : "Marker hinzufügen", + "marker-configuration" : "Marker-Konfiguration", + "remove-marker" : "Marker entfernen", + "marker-type" : "Markertyp", + "marker-type-shape" : "Form", + "marker-type-icon" : "Symbol", + "marker-type-image" : "Bild", + "shape" : "Form", + "icon" : "Symbol", + "image" : "Bild", + "marker-shapes" : "Markerformen", + "marker-icon" : "Markersymbol", + "marker-appearance" : "Markeraussehen", + "marker-image" : "Markerbild", + "marker-image-type-image" : "Bild", + "marker-image-type-function" : "Funktion", + "custom-marker-image-size" : "Benutzerdefinierte Markerbildgröße", + "marker-image-function" : "Markerbild-Funktion", + "marker-images" : "Markerbilder", + "marker-offset" : "Markerversatz", + "offset-horizontal" : "Horizontal", + "offset-vertical" : "Vertikal", + "rotate-marker" : "Marker drehen", + "offset-angle" : "Versatzwinkel", + "position-conversion" : "Positionsumrechnung", + "position-conversion-function" : "Positionsumrechnungsfunktion, sollte x,y-Koordinaten als Double von 0 bis 1 zurückgeben", + "clustering" : { + "use-map-markers-clustering" : "Marker-Clusterung auf der Karte verwenden", + "zoom-on-cluster-click" : "Beim Klicken auf Cluster zoomen", + "max-zoom" : "Maximaler Zoomlevel, bei dem ein Marker Teil eines Clusters sein kann (0 - 18)", + "max-radius" : "Maximaler Radius, den ein Cluster abdeckt", + "zoom-animation" : "Animation der Marker beim Zoomen", + "bounds-on-cluster-mouse-over" : "Marker-Grenzen bei Mouseover auf Cluster anzeigen", + "spiderfy-max-zoom-level" : "Spiderfy auf maximaler Zoomstufe (um alle Cluster-Marker anzuzeigen)", + "load-optimization" : "Ladeoptimierung", + "chunked-load" : "Chunk-Laden verwenden, um Seitenhänger zu vermeiden", + "lazy-load" : "Lazy Load zum Hinzufügen von Markern verwenden", + "use-cluster-marker-color-function" : "Farb-Funktion für Cluster-Marker verwenden", + "marker-color-function" : "Marker-Farb-Funktion" + }, + "edit" : "Marker bearbeiten", + "remove-marker-for" : "Marker für '{{entityName}}' entfernen", + "place-marker" : "Marker platzieren", + "place-marker-hint" : "Klicken Sie, um den Marker zu platzieren", + "place-marker-hint-with-entity" : "Klicken Sie, um die Entität '{{entityName}}' zu platzieren" + }, + "path" : { + "path" : "Pfad", + "path-decorator" : "Pfad-Dekoration", + "decorator-symbol" : "Dekorationssymbol", + "decorator-symbol-arrow-head" : "Pfeil", + "decorator-symbol-dash" : "Strich", + "decorator-arrangement" : "Dekorationsanordnung", + "decorator-offset" : "Start", + "decorator-end-offset" : "Ende", + "decorator-repeat" : "Wiederholen" + }, + "points" : { + "points" : "Punkte", + "point-tooltip" : "Punkt-Tooltip" + }, + "shape" : { + "fill" : "Füllung", + "fill-type-color" : "Farbe", + "fill-type-stripe" : "Streifen", + "fill-type-image" : "Bild", + "color" : "Farbe", + "stripe" : "Streifen", + "image" : "Bild", + "stroke" : "Rand", + "fill-image" : "Füllbild", + "fill-image-type-image" : "Bild", + "fill-image-type-function" : "Funktion", + "preserve-aspect-ratio" : "Seitenverhältnis beibehalten", + "opacity" : "Transparenz", + "angle" : "Rotationswinkel", + "scale" : "Skalierung", + "fill-image-function" : "Funktion zur Bildfüllung", + "fill-images" : "Bilder zur Füllung", + "stripe-pattern" : "Streifenmuster", + "first-stripe" : "Erster Streifen", + "second-stripe" : "Zweiter Streifen" + }, + "polygon" : { + "polygon-key" : "Polygon-Schlüssel", + "polygon-key-required" : "Polygon-Schlüssel ist erforderlich", + "no-polygons" : "Keine Polygone konfiguriert", + "add-polygon" : "Polygon hinzufügen", + "polygon-configuration" : "Polygon-Konfiguration", + "remove-polygon" : "Polygon entfernen", + "edit" : "Polygon bearbeiten", + "remove-polygon-for" : "Polygon für '{{entityName}}' entfernen", + "cut" : "Polygonbereich ausschneiden", + "rotate" : "Polygon drehen", + "draw-rectangle" : "Rechteck zeichnen", + "draw-polygon" : "Polygon zeichnen", + "polygon-place-first-point-cut-hint" : "Klicken, um ersten Punkt zu platzieren", + "continue-polygon-cut-hint" : "Klicken, um weiter zu zeichnen", + "finish-polygon-cut-hint" : "Ersten Marker anklicken zum Beenden und Speichern", + "polygon-place-first-point-hint" : "Polygon: Klicken, um ersten Punkt zu platzieren", + "polygon-place-first-point-hint-with-entity" : "Polygon für '{{entityName}}': Klicken, um ersten Punkt zu platzieren", + "continue-polygon-hint" : "Polygon: Klicken, um weiter zu zeichnen", + "continue-polygon-hint-with-entity" : "Polygon für '{{entityName}}': Klicken, um weiter zu zeichnen", + "finish-polygon-hint" : "Polygon: Ersten Marker anklicken zum Beenden", + "finish-polygon-hint-with-entity" : "Polygon für '{{entityName}}': Ersten Marker anklicken zum Beenden und Speichern", + "rectangle-place-first-point-hint" : "Rechteck: Klicken, um ersten Punkt zu platzieren", + "rectangle-place-first-point-hint-with-entity" : "Rechteck für '{{entityName}}': Klicken, um ersten Punkt zu platzieren", + "finish-rectangle-hint" : "Rechteck: Klicken, um Zeichnung zu beenden", + "finish-rectangle-hint-with-entity" : "Rechteck für '{{entityName}}': Klicken, um Zeichnung zu beenden und zu speichern" + }, + "circle" : { + "circle-key" : "Kreis-Schlüssel", + "circle-key-required" : "Kreis-Schlüssel ist erforderlich", + "no-circles" : "Keine Kreise konfiguriert", + "add-circle" : "Kreis hinzufügen", + "circle-configuration" : "Kreis-Konfiguration", + "remove-circle" : "Kreis entfernen", + "edit" : "Kreis bearbeiten", + "remove-circle-for" : "Kreis für '{{entityName}}' entfernen", + "draw-circle" : "Kreis zeichnen", + "place-circle-center-hint-with-entity" : "Kreis für '{{entityName}}': Klicken, um Mittelpunkt zu setzen", + "place-circle-center-hint" : "Kreis: Klicken, um Mittelpunkt zu setzen", + "finish-circle-hint-with-entity" : "Kreis für '{{entityName}}': Klicken, um Kreis zu beenden und zu speichern", + "finish-circle-hint" : "Kreis: Klicken, um Zeichnung zu beenden" + }, + "select-entity" : "Entität auswählen", + "select-entity-hint" : "Hinweis: Nach Auswahl auf die Karte klicken, um Position festzulegen" + }, + "select-entity" : "Entität auswählen", + "select-entity-hint" : "Hinweis: Nach der Auswahl auf die Karte klicken, um die Position festzulegen", + "tooltips" : { + "placeMarker" : "Klicken, um Entität '{{entityName}}' zu platzieren", + "firstVertex" : "Polygon für '{{entityName}}': Klicken, um ersten Punkt zu setzen", + "firstVertex-cut" : "Klicken, um ersten Punkt zu setzen", + "continueLine" : "Polygon für '{{entityName}}': Klicken, um weiter zu zeichnen", + "continueLine-cut" : "Klicken, um weiter zu zeichnen", + "finishLine" : "Beliebigen existierenden Marker klicken, um zu beenden", + "finishPoly" : "Polygon für '{{entityName}}': Ersten Marker klicken, um zu beenden und zu speichern", + "finishPoly-cut" : "Ersten Marker klicken, um zu beenden und zu speichern", + "finishRect" : "Polygon für '{{entityName}}': Klicken, um zu beenden und zu speichern", + "startCircle" : "Kreis für '{{entityName}}': Klicken, um Mittelpunkt zu setzen", + "finishCircle" : "Kreis für '{{entityName}}': Klicken, um Kreis zu beenden", + "placeCircleMarker" : "Klicken, um Kreis-Marker zu setzen" + }, + "actions" : { + "finish" : "Fertigstellen", + "cancel" : "Abbrechen", + "removeLastVertex" : "Letzten Punkt entfernen" + }, + "buttonTitles" : { + "drawMarkerButton" : "Entität platzieren", + "drawPolyButton" : "Polygon erstellen", + "drawLineButton" : "Linie erstellen", + "drawCircleButton" : "Kreis erstellen", + "drawRectButton" : "Rechteck erstellen", + "editButton" : "Bearbeitungsmodus", + "dragButton" : "Drag-and-Drop-Modus", + "cutButton" : "Polygonbereich ausschneiden", + "deleteButton" : "Entfernen", + "drawCircleMarkerButton" : "Kreis-Marker erstellen", + "rotateButton" : "Polygon drehen" + }, + "map-provider-settings" : "Kartendienst-Einstellungen", + "map-provider" : "Kartendienst", + "map-provider-google" : "Google Maps", + "map-provider-openstreet" : "OpenStreet Maps", + "map-provider-here" : "HERE Maps", + "map-provider-image" : "Bildkarte", + "map-provider-tencent" : "Tencent Maps", + "openstreet-provider" : "OpenStreet Kartendienst", + "openstreet-provider-mapnik" : "OpenStreetMap.Mapnik (Standard)", + "openstreet-provider-hot" : "OpenStreetMap.HOT", + "openstreet-provider-esri-street" : "Esri.WorldStreetMap", + "openstreet-provider-esri-topo" : "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery" : "Esri.WorldImagery", + "openstreet-provider-cartodb-positron" : "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter" : "CartoDB.DarkMatter", + "use-custom-provider" : "Benutzerdefinierten Dienst verwenden", + "custom-provider-tile-url" : "Kachel-URL des benutzerdefinierten Dienstes", + "google-maps-api-key" : "Google Maps API-Schlüssel", + "default-map-type" : "Standardkartentyp", + "google-map-type-roadmap" : "Straßenkarte", + "google-map-type-satelite" : "Satellit", + "google-map-type-hybrid" : "Hybrid", + "google-map-type-terrain" : "Gelände", + "map-layer" : "Kartenebene", + "here-map-normal-day" : "HERE.normalDay (Standard)", + "here-map-normal-night" : "HERE.normalNight", + "here-map-hybrid-day" : "HERE.hybridDay", + "here-map-terrain-day" : "HERE.terrainDay", + "credentials" : "Zugangsdaten", + "here-app-id" : "HERE App-ID", + "here-app-code" : "HERE App-Code", + "here-api-key" : "HERE API-Schlüssel", + "here-use-new-version-api-3" : "API-Version 3 verwenden", + "tencent-maps-api-key" : "Tencent Maps API-Schlüssel", + "tencent-map-type-roadmap" : "Straßenkarte", + "tencent-map-type-satelite" : "Satellit", + "tencent-map-type-hybrid" : "Hybrid", + "image-map-background" : "Bildkarten-Hintergrund", + "image-map-background-from-entity-attribute" : "Bildkarten-Hintergrund aus Entitätsattribut übernehmen", + "image-url-source-entity-alias" : "Alias der Quell-Entität für Bild-URL", + "image-url-source-entity-attribute" : "Entitätsattribut für Bild-URL", + "common-map-settings" : "Allgemeine Karteneinstellungen", + "x-pos-key-name" : "Schlüsselname für X-Position", + "y-pos-key-name" : "Schlüsselname für Y-Position", + "latitude-key-name" : "Schlüsselname für Breitengrad", + "longitude-key-name" : "Schlüsselname für Längengrad", + "default-map-zoom-level" : "Standard-Zoomstufe der Karte (0 - 20)", + "default-map-center-position" : "Standard-Zentrum der Karte (0,0)", + "disable-scroll-zooming" : "Zoom mit Scrollen deaktivieren", + "disable-double-click-zooming" : "Zoom bei Doppelklick deaktivieren", + "disable-zoom-control-buttons" : "Zoom-Schaltflächen deaktivieren", + "fit-map-bounds" : "Kartenausschnitt anpassen, um alle Marker abzudecken", + "use-default-map-center-position" : "Standard-Zentrum der Karte verwenden", + "entities-limit" : "Grenze für zu ladende Entitäten", + "markers-settings" : "Marker-Einstellungen", + "marker-offset-x" : "X-Versatz des Markers multipliziert mit Breite", + "marker-offset-y" : "Y-Versatz des Markers multipliziert mit Höhe", + "position-function" : "Positionsumrechnungsfunktion, sollte x,y-Koordinaten als Double (0-1) zurückgeben", + "draggable-marker" : "Marker verschiebbar", + "label" : "Beschriftung", + "show-label" : "Beschriftung anzeigen", + "use-label-function" : "Beschriftungsfunktion verwenden", + "label-pattern" : "Beschriftung (z.B.: '${entityName}', '${entityName}: (Text ${keyName} Einheiten.)')", + "label-function" : "Beschriftungsfunktion", + "tooltip" : "Tooltip", + "show-tooltip" : "Tooltip anzeigen", + "show-tooltip-action" : "Aktion zur Anzeige des Tooltips", + "show-tooltip-action-click" : "Tooltip bei Klick anzeigen (Standard)", + "show-tooltip-action-hover" : "Tooltip beim Hover anzeigen", + "auto-close-tooltips" : "Tooltip automatisch schließen", + "use-tooltip-function" : "Tooltip-Funktion verwenden", + "tooltip-pattern" : "Tooltip (z.B.: 'Text ${keyName} Einheiten.' oder Linktext)", + "tooltip-function" : "Tooltip-Funktion", + "tooltip-offset-x" : "Tooltip-X-Versatz multipliziert mit Marker-Breite", + "tooltip-offset-y" : "Tooltip-Y-Versatz multipliziert mit Marker-Höhe", + "color" : "Farbe", + "use-color-function" : "Farbfunktion verwenden", + "color-function" : "Farbfunktion", + "marker-image" : "Markerbild", + "use-marker-image-function" : "Markerbildfunktion verwenden", + "custom-marker-image" : "Benutzerdefiniertes Markerbild", + "custom-marker-image-size" : "Benutzerdefinierte Markerbildgröße (px)", + "marker-image-function" : "Markerbildfunktion", + "marker-images" : "Markerbilder", + "polygon-settings" : "Polygoneinstellungen", + "show-polygon" : "Polygon anzeigen", + "polygon-key-name" : "Polygon-Schlüsselname", + "enable-polygon-edit" : "Bearbeitung des Polygons aktivieren", + "polygon-label" : "Polygonbeschriftung", + "show-polygon-label" : "Polygonbeschriftung anzeigen", + "use-polygon-label-function" : "Polygonbeschriftungsfunktion verwenden", + "polygon-label-pattern" : "Polygonbeschriftung (z.B.: '${entityName}', '${entityName}: (Text ${keyName} Einheiten.)')", + "polygon-label-function" : "Polygonbeschriftungsfunktion", + "polygon-tooltip" : "Polygontooltip", + "show-polygon-tooltip" : "Polygontooltip anzeigen", + "auto-close-polygon-tooltips" : "Polygontooltips automatisch schließen", + "use-polygon-tooltip-function" : "Polygontooltip-Funktion verwenden", + "polygon-tooltip-pattern" : "Tooltip (z.B.: 'Text ${keyName} Einheiten.' oder Linktext)", + "polygon-tooltip-function" : "Polygontooltip-Funktion", + "polygon-color" : "Polygonfarbe", + "polygon-opacity" : "Polygondeckkraft", + "use-polygon-color-function" : "Polygonfarbfunktion verwenden", + "polygon-color-function" : "Polygonfarbfunktion", + "polygon-stroke" : "Polygonkontur", + "stroke-color" : "Konturfarbe", + "stroke-opacity" : "Konturdeckkraft", + "stroke-weight" : "Konturbreite", + "use-polygon-stroke-color-function" : "Polygonkonturfarbfunktion verwenden", + "polygon-stroke-color-function" : "Polygonkonturfarbfunktion", + "circle-settings" : "Kreiseinstellungen", + "show-circle" : "Kreis anzeigen", + "circle-key-name" : "Kreisschlüsselname", + "enable-circle-edit" : "Kreisbearbeitung aktivieren", + "circle-label" : "Kreisbeschriftung", + "show-circle-label" : "Kreisbeschriftung anzeigen", + "use-circle-label-function" : "Kreisbeschriftungsfunktion verwenden", + "circle-label-pattern" : "Kreisbeschriftung (z.B.: '${entityName}', '${entityName}: (Text ${keyName} Einheiten.)')", + "circle-label-function" : "Kreisbeschriftungsfunktion", + "circle-tooltip" : "Kreistooltip", + "show-circle-tooltip" : "Kreistooltip anzeigen", + "auto-close-circle-tooltips" : "Kreistooltips automatisch schließen", + "use-circle-tooltip-function" : "Kreistooltip-Funktion verwenden", + "circle-tooltip-pattern" : "Tooltip (z.B.: 'Text ${keyName} Einheiten.' oder Linktext)", + "circle-tooltip-function" : "Kreistooltip-Funktion", + "circle-fill-color" : "Kreisfüllfarbe", + "circle-fill-color-opacity" : "Kreisfüllfarbdeckkraft", + "use-circle-fill-color-function" : "Kreisfüllfarbfunktion verwenden", + "circle-fill-color-function" : "Kreisfüllfarbfunktion", + "circle-stroke" : "Kreiskontur", + "use-circle-stroke-color-function" : "Kreiskonturfarbfunktion verwenden", + "circle-stroke-color-function" : "Kreiskonturfarbfunktion", + "markers-clustering-settings" : "Markerkonfigurations-Einstellungen", + "use-map-markers-clustering" : "Marker-Clusterung verwenden", + "zoom-on-cluster-click" : "Zoom bei Klick auf Cluster", + "max-cluster-zoom" : "Maximale Zoomstufe für Clusterzugehörigkeit (0 - 18)", + "max-cluster-radius-pixels" : "Maximaler Radius, den ein Cluster abdeckt (in Pixel)", + "cluster-zoom-animation" : "Animation beim Zoomen auf Marker anzeigen", + "show-markers-bounds-on-cluster-mouse-over" : "Grenzen von Markern bei Mauszeiger über Cluster anzeigen", + "spiderfy-max-zoom-level" : "Spiderfy auf maximaler Zoomstufe (alle Marker im Cluster anzeigen)", + "load-optimization" : "Ladeoptimierung", + "cluster-chunked-loading" : "Chunks beim Laden von Markern verwenden, um Einfrieren der Seite zu vermeiden", + "cluster-markers-lazy-load" : "Lazy Load für Marker aktivieren", + "editor-settings" : "Editor-Einstellungen", + "enable-snapping" : "Snapping an andere Punkte aktivieren", + "init-draggable-mode" : "Karte im Drag-Modus initialisieren", + "hide-all-edit-buttons" : "Alle Bearbeitungs-Schaltflächen ausblenden", + "hide-draw-buttons" : "Zeichenschaltflächen ausblenden", + "hide-edit-buttons" : "Bearbeitungsschaltflächen ausblenden", + "hide-remove-button" : "Entfernen-Schaltfläche ausblenden", + "route-map-settings" : "Routenkarte-Einstellungen", + "trip-animation-settings" : "Fahrt-Animations-Einstellungen", + "normalization-step" : "Normalisierungsschritt (ms)", + "tooltip-background-color" : "Tooltip-Hintergrundfarbe", + "tooltip-font-color" : "Tooltip-Schriftfarbe", + "tooltip-opacity" : "Tooltip-Deckkraft (0-1)", + "auto-close-tooltip" : "Tooltip automatisch schließen", + "rotation-angle" : "Zusätzlicher Rotationswinkel für Marker (°)", + "path-settings" : "Pfad-Einstellungen", + "path-color" : "Pfadfarbe", + "use-path-color-function" : "Pfadfarbfunktion verwenden", + "path-color-function" : "Pfadfarbfunktion", + "path-decorator" : "Pfad-Dekorator", + "use-path-decorator" : "Pfad-Dekorator verwenden", + "decorator-symbol" : "Dekorator-Symbol", + "decorator-symbol-arrow-head" : "Pfeil", + "decorator-symbol-dash" : "Strich", + "decorator-symbol-size" : "Größe des Dekorator-Symbols (px)", + "use-path-decorator-custom-color" : "Benutzerdefinierte Dekoratorfarbe verwenden", + "decorator-custom-color" : "Benutzerdefinierte Dekoratorfarbe", + "decorator-offset" : "Dekorator-Startversatz", + "end-decorator-offset" : "Dekorator-Endversatz", + "decorator-repeat" : "Dekorator-Wiederholung", + "points-settings" : "Punkte-Einstellungen", + "show-points" : "Punkte anzeigen", + "point-color" : "Punktfarbe", + "point-size" : "Punktgröße (px)", + "use-point-color-function" : "Punktfarbfunktion verwenden", + "point-color-function" : "Punktfarbfunktion", + "use-point-as-anchor" : "Punkt als Anker verwenden", + "point-as-anchor-function" : "Punkt-als-Anker-Funktion", + "independent-point-tooltip" : "Unabhängiger Punkt-Tooltip", + "clustering-markers" : "Marker-Clustering", + "use-icon-create-function" : "Markierungsfarbfunktion verwenden", + "marker-color-function" : "Markerfarbfunktion" + }, + "markdown" : { + "use-markdown-text-function" : "Markdown/HTML-Wertfunktion verwenden", + "markdown-text-function" : "Markdown/HTML-Wertfunktion", + "markdown-text-pattern" : "Markdown/HTML-Muster (Markdown oder HTML mit Variablen, z. B. '${entityName} oder ${keyName} - etwas Text.')", + "apply-default-markdown-style" : "Standard-Markdown-Stil anwenden", + "markdown-css" : "Markdown/HTML CSS" + }, + "simple-card" : { + "label" : "Bezeichnung", + "label-position" : "Bezeichnungsposition", + "label-position-left" : "Links", + "label-position-top" : "Oben" + }, + "single-switch" : { + "behavior" : "Verhalten", + "layout" : "Layout", + "layout-right" : "Rechts", + "layout-left" : "Links", + "layout-centered" : "Zentriert", + "auto-scale" : "Automatische Skalierung", + "label" : "Bezeichnung", + "icon" : "Symbol", + "switch-color" : "Schalterfarbe", + "on" : "Ein", + "off" : "Aus", + "disabled" : "Deaktiviert", + "tumbler-color" : "Schalterfarbe", + "on-label" : "Ein-Beschriftung", + "off-label" : "Aus-Beschriftung", + "switch" : "Schalter" + }, + "slider" : { + "behavior" : "Verhalten", + "initial-value" : "Anfangswert", + "initial-value-hint" : "Aktion zum Abrufen des Anfangswerts des Sliders.", + "on-value-change" : "Bei Wertänderung", + "on-value-change-hint" : "Aktion, die ausgelöst wird, wenn sich der Wert des Sliders ändert.", + "layout" : "Layout", + "layout-default" : "Standard", + "layout-extended" : "Erweitert", + "layout-simplified" : "Vereinfacht", + "auto-scale" : "Automatische Skalierung", + "icon" : "Symbol", + "value" : "Wert", + "range" : "Bereich", + "min" : "Min", + "max" : "Max", + "range-ticks" : "Bereichsmarkierungen", + "tick-marks" : "Markierungen", + "colors" : "Farben", + "main" : "Hauptfarbe", + "background" : "Hintergrund", + "left-icon" : "Linkes Symbol", + "right-icon" : "Rechtes Symbol", + "slider" : "Slider" + }, + "value-card" : { + "layout" : "Layout", + "layout-square" : "Quadratisch", + "layout-vertical" : "Vertikal", + "layout-centered" : "Zentriert", + "layout-simplified" : "Vereinfacht", + "layout-horizontal" : "Horizontal", + "layout-horizontal-reversed" : "Horizontal umgekehrt", + "label" : "Bezeichnung", + "icon" : "Symbol", + "value" : "Wert", + "date" : "Datum", + "value-card-style" : "Wertkartenstil", + "auto-scale" : "Automatische Skalierung" + }, + "label-card" : { + "auto-scale" : "Automatische Skalierung", + "label" : "Bezeichnung", + "icon" : "Symbol", + "label-card-style" : "Bezeichnungskartenstil" + }, + "label-value-card" : { + "value" : "Wert", + "label-value-card-style" : "Bezeichnungs- & Wertkartenstil" + }, + "liquid-level-card" : { + "layout-simple" : "Einfach", + "layout-percentage" : "Prozentual", + "layout-absolute" : "Absolut", + "layout" : "Layout", + "background-overlay" : "Hintergrundüberlagerung für Wert", + "total-volume" : "Gesamtvolumen", + "total-volume-units" : "Volumeneinheiten", + "tank" : "Tank", + "shape" : "Form", + "datasource-units" : "Einheiten der Datenquelle", + "widget-units" : "Einheiten des Widgets", + "decimals" : "Dezimalstellen", + "liquid" : "Flüssigkeit", + "liquid-color" : "Farbe der Flüssigkeit", + "value" : "Wert", + "value-font" : "Schriftart für Wert", + "level" : "Füllstand", + "last-update" : "Letzte Aktualisierung", + "shape-by-attribute" : "Tankform durch Attributnamen festlegen", + "tooltip-background" : "Hintergrundfarbe", + "background-blur" : "Hintergrundunschärfe", + "tank-color" : "Tankfarbe", + "static" : "Statisch", + "see-examples" : "Beispiele ansehen", + "attribute" : "Attribut", + "shape-type" : "Typ", + "v-oval" : "Vertikal Oval", + "v-cylinder" : "Vertikal Zylinder", + "v-capsule" : "Vertikal Kapsel", + "rectangle" : "Rechteck", + "h-oval" : "Horizontal Oval", + "h-ellipse" : "Horizontal Ellipse", + "h-dish-ends" : "Horizontal Teller-Enden", + "h-cylinder" : "Horizontal Zylinder", + "h-capsule" : "Horizontal Kapsel", + "h-elliptical_2_1" : "Horizontal 2:1 Elliptisch", + "icon" : "Karten-Symbol", + "title" : "Karten-Titel", + "units" : "Einheiten", + "color-and-font" : "Farbe und Schriftart", + "shape-attribute-name" : "Attributname", + "total-volume-required" : "Gesamtvolumen ist erforderlich.", + "attribute-name-required" : "Attributname ist erforderlich.", + "attribute-key-not-set" : "Attributschlüssel '{{attributeName}}' nicht gesetzt", + "attribute-key-invalid" : "Attributschlüssel '{{attributeName}}' ist ungültig" + }, + "aggregated-value-card" : { + "subtitle" : "Untertitel", + "chart" : "Diagramm", + "values" : "Werte", + "value-appearance" : "Darstellung der Werte", + "position" : "Position", + "position-center" : "Zentriert", + "position-right-top" : "Rechts oben", + "position-right-bottom" : "Rechts unten", + "position-left-top" : "Links oben", + "position-left-bottom" : "Links unten", + "font" : "Schriftart", + "color" : "Farbe", + "arrow" : "Pfeil", + "display-up-down-arrow" : "Auf/Ab-Pfeil anzeigen", + "add-value" : "Wert hinzufügen", + "remove-value" : "Wert entfernen", + "no-values" : "Keine Werte konfiguriert", + "aggregation" : "Aggregation", + "aggregated-value-card-style" : "Stil der aggregierten Wertkarte", + "auto-scale" : "Automatische Skalierung" + }, + "value-chart-card" : { + "layout" : "Layout", + "layout-left" : "Links", + "layout-right" : "Rechts", + "auto-scale" : "Automatische Skalierung", + "icon" : "Symbol", + "value" : "Wert", + "chart" : "Diagramm", + "value-chart-card-style" : "Stil der Wertdiagrammkarte" + }, + "progress-bar" : { + "layout" : "Layout", + "layout-default" : "Standard", + "layout-simplified" : "Vereinfacht", + "auto-scale" : "Automatische Skalierung", + "icon" : "Symbol", + "value" : "Wert", + "range" : "Bereich", + "min" : "Min", + "max" : "Max", + "range-ticks" : "Bereichsmarkierungen", + "bar" : "Balken", + "bar-color" : "Balkenfarbe", + "bar-background" : "Balkenhintergrund", + "progress-bar-card-style" : "Stil der Fortschrittsbalkenkarte" + }, + "notification" : { + "max-notification-display" : "Maximale Anzahl anzuzeigender Benachrichtigungen", + "counter" : "Zähler", + "counter-hint" : "Der Zähler wird angezeigt, wenn der \"Widget-Titel\" aktiviert ist", + "icon" : "Symbol", + "counter-value" : "Wert", + "counter-color" : "Farbe", + "notification-button" : "Benachrichtigungsschaltflächen", + "button-view-all" : "Alle anzeigen", + "button-filter" : "Filter", + "type-filter" : "Typfilter", + "button-mark-read" : "Alle als gelesen markieren", + "notification-types" : "Benachrichtigungstypen", + "notification-type" : "Benachrichtigungstyp", + "search-type" : "Typ suchen", + "any-type" : "Beliebiger Typ" + }, + "alarm-count" : { + "alarm-count-card-style" : "Stil der Alarmzählkarte" + }, + "entity-count" : { + "entity-count-card-style" : "Stil der Entitätszählkarte" + }, + "count" : { + "layout" : "Layout", + "layout-column" : "Spalte", + "layout-row" : "Zeile", + "label" : "Beschriftung", + "icon" : "Symbol", + "icon-background" : "Symbolhintergrund", + "value" : "Wert", + "chevron" : "Chevron", + "auto-scale" : "Automatische Skalierung" + }, + "table" : { + "common-table-settings" : "Allgemeine Tabelleneinstellungen", + "enable-search" : "Suche aktivieren", + "enable-sticky-header" : "Kopfzeile immer anzeigen", + "enable-sticky-action" : "Aktionsspalte immer anzeigen", + "hidden-cell-button-display-mode" : "Anzeige-Modus für versteckte Zellaktionen", + "show-empty-space-hidden-action" : "Leeren Platz anstelle der versteckten Zellaktion anzeigen", + "dont-reserve-space-hidden-action" : "Keinen Platz für versteckte Aktionsschaltflächen reservieren", + "display-timestamp" : "Zeitstempel anzeigen", + "display-pagination" : "Seitennummerierung anzeigen", + "default-page-size" : "Standard-Seitengröße", + "page-step-settings" : "Seitenschritteinstellungen", + "page-step-count" : "Anzahl der Schritte", + "page-step-increment" : "Schrittgröße", + "page-step-count-format-message" : "Muss ein ganzzahliger Wert im Bereich von 1 bis 100 sein.", + "page-step-increment-format-message" : "Muss ein ganzzahliger Wert sein, größer oder gleich 1.", + "use-entity-label-tab-name" : "Entitätsbezeichnung im Tabnamen verwenden", + "hide-empty-lines" : "Leere Zeilen ausblenden", + "row-style" : "Zeilenstil", + "use-row-style-function" : "Zeilenstilfunktion verwenden", + "row-style-function" : "Zeilenstilfunktion", + "cell-style" : "Zellenstil", + "use-cell-style-function" : "Zellenstilfunktion verwenden", + "cell-style-function" : "Zellenstilfunktion", + "cell-content" : "Zelleninhalt", + "use-cell-content-function" : "Zelleninhaltsfunktion verwenden", + "cell-content-function" : "Zelleninhaltsfunktion", + "show-latest-data-column" : "Spalte mit neuesten Daten anzeigen", + "latest-data-column-order" : "Reihenfolge der Spalte mit neuesten Daten", + "entities-table-title" : "Titel der Entitätstabelle", + "enable-select-column-display" : "Auswahlspaltenanzeige aktivieren", + "display-entity-name" : "Spalte mit Entitätsnamen anzeigen", + "entity-name-column-title" : "Titel der Entitätsnamenspalte", + "display-entity-label" : "Spalte mit Entitätsbezeichnung anzeigen", + "entity-label-column-title" : "Titel der Entitätsbezeichnungsspalte", + "display-entity-type" : "Spalte mit Entitätstyp anzeigen", + "default-sort-order" : "Standard-Sortierreihenfolge", + "custom-title" : "Benutzerdefinierter Kopfzeilentitel", + "column-width" : "Spaltenbreite (px oder %)", + "default-column-visibility" : "Standardmäßige Spaltensichtbarkeit", + "column-visibility-visible" : "Sichtbar", + "column-visibility-hidden" : "Ausgeblendet", + "column-visibility-hidden-mobile" : "Im mobilen Modus ausgeblendet", + "column-selection-to-display" : "Spaltenauswahl in 'Anzuzeigende Spalten'", + "column-selection-to-display-enabled" : "Aktiviert", + "column-selection-to-display-disabled" : "Deaktiviert", + "alarms-table-title" : "Titel der Alarmtabelle", + "enable-alarms-selection" : "Alarmauswahl aktivieren", + "enable-alarms-search" : "Alarmsuche aktivieren", + "enable-alarm-filter" : "Alarmfilter aktivieren", + "display-alarm-details" : "Alarmdetails anzeigen", + "allow-alarms-ack" : "Alarmerkennung zulassen", + "allow-alarms-clear" : "Alarmquittierung zulassen", + "display-alarm-activity" : "Alarmaktivität anzeigen", + "allow-alarms-assign" : "Alarmzuweisung zulassen", + "columns" : "Spalten", + "column-settings" : "Spalteneinstellungen", + "remove-column" : "Spalte entfernen", + "add-column" : "Spalte hinzufügen", + "no-columns" : "Keine Spalten konfiguriert", + "columns-to-display" : "Anzuzeigende Spalten", + "table-header" : "Tabellenkopf", + "header-buttons" : "Kopfzeilenschaltflächen", + "table-buttons" : "Tabellenschaltflächen", + "pagination" : "Seitennummerierung", + "rows" : "Zeilen", + "timeseries-column-error" : "Mindestens eine Zeitreihenspalte muss angegeben werden", + "alarm-column-error" : "Mindestens eine Alarmspalte muss angegeben werden", + "table-tabs" : "Tabellen-Tabs", + "show-cell-actions-menu-mobile" : "Zellenaktions-Dropdownmenü im mobilen Modus anzeigen", + "disable-sorting" : "Sortierung deaktivieren" + }, + "latest-chart" : { + "total" : "Gesamt", + "auto-scale" : "Automatische Skalierung", + "clockwise-layout" : "Uhrzeigerlayout", + "sort-series" : "Serien nach Bezeichnung sortieren", + "tooltip-value-type-absolute" : "Absolut", + "tooltip-value-type-percentage" : "Prozentual" + }, + "pie-chart" : { + "pie-chart-appearance" : "Erscheinungsbild Kreisdiagramm", + "label" : "Bezeichnung", + "border" : "Rahmen", + "radius" : "Radius", + "pie-chart-card-style" : "Kreisdiagramm-Kartenstil" + }, + "radar-chart" : { + "radar-appearance" : "Radar-Diagramm", + "shape" : "Form", + "shape-polygon" : "Polygon", + "shape-circle" : "Kreis", + "color" : "Farbe", + "line" : "Linie", + "points" : "Punkte", + "points-label" : "Punktebezeichnung", + "radar-axis" : "Radar-Achse", + "axis-label" : "Achsenbeschriftung", + "ticks-label" : "Skalenbeschriftung", + "radar-chart-style" : "Radar-Diagrammstil" + }, + "time-series-chart" : { + "chart" : "Diagramm", + "chart-style" : "Diagrammstil", + "data-zoom" : "Daten-Zoom", + "stack-mode" : "Stapelmodus", + "stack-mode-hint" : "Stapel Serien im Diagramm. Serien mit derselben Einheit werden übereinandergelegt.", + "axes" : "Achsen", + "y-axes" : "Y-Achsen", + "line-type" : "Linientyp", + "line-width" : "Linienstärke", + "type-line" : "Linie", + "type-bar" : "Balken", + "type-point" : "Punkt", + "no-aggregation-bar-width-strategy" : "Balkenbreitenstrategie für nicht aggregierte Daten", + "no-aggregation-bar-width-strategy-group" : "Gruppieren", + "no-aggregation-bar-width-strategy-separate" : "Separat", + "bar-group-width" : "Balkengruppenbreite", + "bar-width" : "Balkenbreite", + "bar-width-relative" : "Prozentsatz des Zeitfensters", + "bar-width-absolute" : "Absolut (ms)", + "comparison" : { + "comparison" : "Vergleich", + "comparison-hint" : "Vergleich funktioniert nur mit historischen Daten!", + "show" : "Anzeigen", + "settings" : "Vergleichseinstellungen", + "show-values-for-comparison" : "Historische Daten für Vergleich anzeigen", + "comparison-values-label" : "Vergleichsschlüssel-Bezeichnung", + "comparison-values-label-auto" : "Automatisch", + "comparison-data-color" : "Farbe der Vergleichsdaten" + }, + "threshold" : { + "thresholds" : "Schwellenwerte", + "source" : "Quelle", + "key-value" : "Schlüssel / Wert", + "no-thresholds" : "Keine Schwellenwerte konfiguriert", + "add-threshold" : "Schwellenwert hinzufügen", + "type-constant" : "Konstant", + "type-latest-key" : "Schlüssel", + "type-entity" : "Entität", + "threshold-settings" : "Schwellenwerteinstellungen", + "remove-threshold" : "Schwellenwert entfernen", + "threshold-value-required" : "Schwellenwert ist erforderlich.", + "key-required" : "Schlüssel ist erforderlich.", + "entity-key-required" : "Entitätsschlüssel ist erforderlich.", + "line-appearance" : "Linienerscheinung", + "line-color" : "Linienfarbe", + "start-symbol" : "Starts Symbol", + "end-symbol" : "Ends Symbol", + "symbol-size" : "Größe", + "label" : "Bezeichnung", + "label-position-start" : "Anfang", + "label-position-middle" : "Mitte", + "label-position-end" : "Ende", + "label-position-inside-start" : "Innen Anfang", + "label-position-inside-start-top" : "Innen Anfang oben", + "label-position-inside-start-bottom" : "Innen Anfang unten", + "label-position-inside-middle" : "Innen Mitte", + "label-position-inside-middle-top" : "Innen Mitte oben", + "label-position-inside-middle-bottom" : "Innen Mitte unten", + "label-position-inside-end" : "Innen Ende", + "label-position-inside-end-top" : "Innen Ende oben", + "label-position-inside-end-bottom" : "Innen Ende unten", + "label-background" : "Bezeichnungshintergrund" + }, + "state" : { + "states" : "Zustände", + "label" : "Bezeichnung", + "ticks-value" : "Tick-Wert", + "source" : "Quelle", + "value-range" : "Wert / Bereich", + "no-states" : "Keine Zustände konfiguriert", + "add-state" : "Zustand hinzufügen", + "type-constant" : "Konstant", + "type-range" : "Bereich", + "from" : "Von", + "to" : "Bis", + "remove-state" : "Zustand entfernen" + }, + "grid" : { + "grid" : "Gitter", + "background-color" : "Hintergrundfarbe", + "border" : "Rahmen" + }, + "axis" : { + "axes" : "Achsen", + "x-axis" : "X-Achse", + "y-axis" : "Y-Achse", + "y-axis-settings" : "Y-Achse Einstellungen", + "comparison-x-axis-settings" : "Vergleich X-Achse Einstellungen", + "remove-y-axis" : "Y-Achse entfernen", + "id" : "ID", + "label" : "Bezeichnung", + "position" : "Position", + "position-left" : "Links", + "position-right" : "Rechts", + "position-top" : "Oben", + "position-bottom" : "Unten", + "tick-labels" : "Tick-Beschriftungen", + "ticks-formatter-function" : "Tick-Formatierungsfunktion", + "ticks-generator-function" : "Tick-Generator-Funktion", + "show-ticks" : "Ticks anzeigen", + "show-line" : "Linie anzeigen", + "show-split-lines" : "Hilfslinien anzeigen", + "show-split-lines-x-axis-hint" : "Wenn aktiviert, werden vertikale Linien im Diagramm angezeigt.", + "show-split-lines-y-axis-hint" : "Wenn aktiviert, werden horizontale Linien im Diagramm angezeigt.", + "ticks-interval" : "Tick-Intervall", + "ticks-interval-hint" : "Segmentierungsintervall der Achse festlegen.", + "split-number" : "Teilungsanzahl", + "split-number-hint" : "Anzahl der Segmente, in die die Achse unterteilt wird.", + "min" : "Min", + "max" : "Max", + "show" : "Anzeigen", + "add-y-axis" : "Y-Achse hinzufügen" + }, + "series" : { + "legend-settings" : "Legenden-Einstellungen", + "show-in-legend" : "In Legende anzeigen", + "show-in-legend-hint" : "Serienname und Daten in der Legende anzeigen.", + "hidden-by-default" : "Standardmäßig ausgeblendet", + "hidden-by-default-hint" : "Serie standardmäßig in der Legende ausblenden.", + "series-type" : "Serientyp", + "type" : "Typ", + "type-line" : "Linie", + "type-bar" : "Balken", + "line" : { + "line" : "Linie", + "show-line" : "Linie anzeigen", + "step-line" : "Treppenlinie", + "step-type-start" : "Start", + "step-type-middle" : "Mitte", + "step-type-end" : "Ende", + "smooth-line" : "Geglättete Linie" + }, + "point" : { + "points" : "Punkte", + "show-points" : "Punkte anzeigen", + "point-label" : "Punktbezeichnung", + "point-label-hint" : "Bezeichnung mit Wert über dem Serienpunkt anzeigen.", + "point-label-background" : "Hintergrund der Punktbezeichnung", + "point-shape" : "Punktform", + "point-size" : "Punktgröße" + } + } + }, + "wind-speed-direction" : { + "layout" : "Layout", + "layout-default" : "Standard", + "layout-advanced" : "Erweitert", + "layout-simplified" : "Vereinfacht", + "values" : "Werte", + "wind-direction" : "Windrichtung", + "center-value" : "Zentralwert", + "icon" : "Symbol", + "arrow" : "Pfeil", + "ticks" : "Ticks", + "labels-type" : "Beschriftungstyp", + "directional-names" : "Richtung Namen", + "degrees" : "Grad", + "major-ticks" : "Große Ticks", + "minor-ticks" : "Kleine Ticks", + "wind-speed-direction-card-style" : "Stil der Windgeschwindigkeits- und Richtungsanzeige", + "ticks-color" : "Tick-Farbe", + "ticks-labels-type" : "Tick-Beschriftungstyp", + "arrow-color" : "Pfeilfarbe" + }, + "value-source" : { + "value-source" : "Wertquelle", + "predefined-value" : "Konstant", + "entity-attribute" : "Entitätsattribut", + "value" : "Wert", + "value-required" : "Wert ist erforderlich.", + "key-required" : "Schlüssel ist erforderlich.", + "entity-key-required" : "Entitätsschlüssel ist erforderlich.", + "source-entity-alias" : "Alias der Quell-Entität", + "source-entity-attribute" : "Attribut der Quell-Entität", + "type-constant" : "Konstant", + "type-latest-key" : "Schlüssel", + "type-entity" : "Entität" + }, + "rpc-state" : { + "initial-state" : "Anfangszustand", + "initial-state-hint" : "Aktion, um den Anfangszustand (Ein/Aus) der Komponente abzurufen.", + "disabled-state" : "Deaktivierter Zustand", + "disabled-state-hint" : "Bedingung konfigurieren, unter der die Komponente deaktiviert ist.", + "turn-on" : "Einschalten", + "turn-on-hint" : "Aktion, wenn der Schalter auf 'Ein' gestellt wird", + "turn-off" : "Ausschalten", + "turn-off-hint" : "Aktion, wenn der Schalter auf 'Aus' gestellt wird", + "on" : "Ein", + "off" : "Aus", + "disabled" : "Deaktiviert" + }, + "value-action" : { + "do-nothing" : "Nichts tun", + "execute-rpc" : "RPC ausführen", + "get-attribute" : "Attribut abrufen", + "set-attribute" : "Attribut setzen", + "get-time-series" : "Zeitreihe abrufen", + "get-alarm-status" : "Alarmstatus abrufen", + "get-dashboard-state" : "Dashboard-Zustands-ID abrufen", + "get-dashboard-state-object" : "Dashboard-Zustandsobjekt abrufen", + "add-time-series" : "Zeitreihe hinzufügen", + "execute-rpc-text" : "Führe RPC-Methode '{{methodName}}' aus", + "get-time-series-text" : "Verwende Zeitreihe '{{key}}'", + "get-attribute-text" : "Verwende Attribut '{{key}}'", + "get-alarm-status-text" : "Verwende Alarmstatus", + "get-dashboard-state-text" : "Verwende Dashboard-Zustand", + "get-dashboard-state-object-text" : "Verwende Dashboard-Zustandsobjekt", + "when-dashboard-state-is-text" : "Wenn Dashboard-Zustand-ID '{{state}}' ist", + "when-dashboard-state-function-is-text" : "Wenn f(Dashboard-Zustand-ID) '{{state}}' ist", + "when-dashboard-state-object-function-is-text" : "Wenn f(Dashboard-Zustandsobjekt) '{{state}}' ist", + "set-attribute-to-value-text" : "Setze Attribut '{{key}}' auf: {{value}}", + "add-time-series-value-text" : "Füge Zeitreihenwert '{{key}}': {{value}} hinzu", + "set-attribute-text" : "Setze Attribut '{{key}}'", + "add-time-series-text" : "Füge Zeitreihe '{{key}}' hinzu", + "action" : "Aktion", + "value" : "Wert", + "init-value-hint" : "Wert, der gesetzt wird, bis das Gerät Daten sendet.", + "method" : "Methode", + "method-name-required" : "Methodenname ist erforderlich.", + "request-timeout-ms" : "RPC-Anfrage-Timeout (ms)", + "request-timeout-required" : "Timeout ist erforderlich.", + "min-request-timeout-error" : "Der Timeout-Wert muss mindestens 5000 ms (5 Sekunden) betragen.", + "request-persistent" : "Persistente RPC-Anfrage", + "persistent-polling-interval" : "Polling-Intervall (ms)", + "persistent-polling-interval-hint" : "Polling-Intervall (ms) zur Abfrage der Antwort auf die persistente RPC-Anfrage", + "persistent-polling-interval-required" : "Polling-Intervall ist erforderlich.", + "min-persistent-polling-interval-error" : "Der Wert für das Polling-Intervall muss mindestens 1000 ms (1 Sekunde) betragen.", + "attribute-scope" : "Attributbereich", + "attribute-key" : "Attributschlüssel", + "attribute-key-required" : "Attributschlüssel ist erforderlich.", + "time-series-key" : "Zeitreihenschlüssel", + "time-series-key-required" : "Zeitreihenschlüssel ist erforderlich.", + "action-result-converter" : "Konverter für Aktionsergebnis", + "converter-none" : "Keiner", + "converter-function" : "Funktion", + "converter-constant" : "Konstante", + "converter-value" : "Wert", + "parse-value-function" : "Funktion zur Wertanalyse", + "state-when-result-is" : "'{{state}}' wenn Ergebnis ist", + "parameters" : "Parameter", + "convert-value-function" : "Funktion zur Wertumwandlung", + "error" : { + "target-entity-is-not-set" : "Zielentität ist nicht gesetzt!", + "failed-to-perform-action" : "Aktion {{ actionLabel }} konnte nicht ausgeführt werden.", + "invalid-attribute-scope" : "{{scope}}-Attributbereich wird von der Entität {{entityType}} nicht unterstützt." + } + }, + "widget-font" : { + "font-settings" : "Schriftarteinstellungen", + "font-family" : "Schriftfamilie", + "size" : "Größe", + "relative-font-size" : "Relative Schriftgröße (Prozent)", + "font-style" : "Stil", + "font-style-normal" : "Normal", + "font-style-italic" : "Kursiv", + "font-style-oblique" : "Schräg", + "font-weight" : "Stärke", + "font-weight-normal" : "Normal", + "font-weight-bold" : "Fett", + "font-weight-bolder" : "Stärker", + "font-weight-lighter" : "Leichter", + "color" : "Farbe", + "shadow-color" : "Schattenfarbe", + "preview" : "Vorschau", + "line-height" : "Zeilenhöhe", + "auto" : "Auto" + }, + "home" : { + "no-data-available" : "Keine Daten verfügbar" + }, + "system-info" : { + "cpu" : "CPU", + "ram" : "RAM", + "disk" : "Festplatte", + "cpu-warning-text" : "Hohe CPU-Auslastung. Optimieren Sie die Systemleistung, um einen Systemausfall zu vermeiden.", + "cpu-critical-text" : "Kritisch hohe CPU-Auslastung. Optimieren Sie die Systemleistung, um einen Systemausfall zu vermeiden.", + "ram-warning-text" : "Wenig verfügbarer RAM. Optimieren Sie die Systemleistung oder erhöhen Sie die RAM-Kapazität, um Systemausfälle zu vermeiden.", + "ram-critical-text" : "Kritisch wenig verfügbarer RAM. Optimieren Sie die Systemleistung oder erhöhen Sie die RAM-Kapazität, um Systemausfälle zu vermeiden.", + "disk-warning-text" : "Wenig Festplattenspeicher. Bereinigen oder erweitern Sie den Speicherplatz, um Datenverlust zu vermeiden.", + "disk-critical-text" : "Kritisch wenig Festplattenspeicher. Bereinigen oder erweitern Sie den Speicherplatz, um Datenverlust zu vermeiden." + }, + "cluster-info" : { + "service-id" : "Dienst-ID", + "service-type" : "Diensttyp", + "no-data" : "Keine Daten" + }, + "transport-messages" : { + "title" : "Transportnachrichten", + "info" : "Alle Nachrichten, die von Geräten empfangen wurden" + }, + "activity" : { + "title" : "Aktivität" + }, + "documentation" : { + "title" : "Dokumentation", + "add-link" : "Link hinzufügen", + "add-link-title" : "Dokumentationslink hinzufügen", + "name" : "Name", + "name-required" : "Name ist erforderlich.", + "link" : "Link", + "link-required" : "Link ist erforderlich.", + "columns" : "Spalten" + }, + "quick-links" : { + "title" : "Schnellzugriffe", + "add-link" : "Link hinzufügen", + "add-link-title" : "Schnelllink hinzufügen", + "quick-link" : "Schnelllink", + "quick-link-required" : "Schnelllink ist erforderlich.", + "no-links-matching" : "Keine Links passend zu '{{name}}' gefunden.", + "columns" : "Spalten" + }, + "recent-dashboards" : { + "title" : "Dashboards", + "last" : "Zuletzt angesehen", + "starred" : "Favorisiert", + "name" : "Name", + "last-viewed" : "Zuletzt angesehen", + "no-last-viewed-dashboards" : "Noch keine zuletzt angesehenen Dashboards" + }, + "configured-features" : { + "title" : "Konfigurierte Funktionen", + "info" : "Status der Funktionen, die eine Konfiguration erfordern", + "email-feature" : "E-Mail", + "sms-feature" : "SMS", + "slack-feature" : "Slack", + "oauth2-feature" : "OAuth 2", + "2fa-feature" : "2FA", + "feature-configured" : "Funktion ist konfiguriert.\nZum Einrichten klicken", + "feature-not-configured" : "Funktion ist nicht konfiguriert.\nZum Einrichten klicken" + }, + "version-info" : { + "title" : "Version", + "contact-us" : "Kontaktieren Sie uns", + "current-version" : "Aktuelle Version", + "current" : "Aktuell", + "available-version" : "Verfügbare Version", + "available" : "Verfügbar", + "upgrade" : "Aktualisieren", + "version-is-up-to-date" : "Version ist aktuell" + }, + "usage-info" : { + "title" : "Nutzung", + "entities" : "Entitäten", + "api-calls" : "API-Aufrufe" + }, + "functions" : { + "title" : "Funktionen", + "pe-feature-tooltip" : "Nur in der ThingsBoard\nProfessional Edition verfügbar", + "switch-to-pe" : "Zur Professional Edition wechseln", + "alarms" : "Alarme", + "dashboards" : "Dashboards", + "entities-and-relations" : "Entitäten & Relationen", + "profiles" : "Profile", + "advanced-features" : "Erweiterte Funktionen", + "notification-center" : "Benachrichtigungszentrale", + "api-usage" : "API-Nutzung", + "customers" : "Kunden", + "customers-hierarchy" : "Kundenhierarchie", + "roles-and-permissions" : "Rollen & Berechtigungen", + "groups" : "Gruppen", + "integrations" : "Integrationen", + "solution-templates" : "Lösungsvorlagen", + "scheduler" : "Zeitplaner", + "white-labeling" : "White Labeling" + }, + "devices" : { + "view-docs" : "Dokumentation anzeigen", + "inactive" : "Inaktiv", + "active" : "Aktiv", + "total" : "Gesamt" + }, + "alarms" : { + "critical" : "Kritisch", + "assigned-to-me" : "Mir zugewiesen", + "total" : "Gesamt" + }, + "getting-started" : { + "get-started" : "Loslegen", + "finish" : "Fertigstellen", + "done-welcome-title" : "Willkommen an Bord", + "done-welcome-text" : "Das hast du großartig gemacht!", + "sys-admin" : { + "step1" : { + "title" : "Erstellen Sie einen Mandanten und einen Mandantenadministrator", + "content" : "

Ein Mandant ist eine Einzelperson oder Organisation, die Geräte und Assets besitzt oder produziert. Ein Mandant kann mehrere Mandantenadministrator-Benutzer, Kunden, Geräte und Assets haben.

Der Mandantenadministrator kann Geräte, Assets, Kunden und Dashboards innerhalb des Mandantenkontos erstellen und verwalten.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-create-tenant" : "Wie man einen Mandanten und Mandantenadministrator erstellt" + }, + "step2" : { + "title" : "Funktion konfigurieren: Mailserver", + "content" : "

Die Konfiguration des Mailservers ist unerlässlich für die Benutzeraktivierung, Passwortwiederherstellung und die Zustellung von Alarmbenachrichtigungen.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-configure-mail-server" : "Wie man den Mailserver konfiguriert" + }, + "step3" : { + "title" : "Funktion konfigurieren: SMS-Anbieter", + "content" : "

Konfigurieren Sie SMS-Anbieter, um Kunden über Alarme per SMS zu benachrichtigen.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-configure-sms-provider" : "Wie man den SMS-Anbieter konfiguriert" + }, + "step4" : { + "title" : "Funktion konfigurieren: White-Labeling", + "content" : "

Passen Sie ganz einfach das Logo und Farbschema Ihres Unternehmens oder Produkts an – ohne Programmierung und ohne Neustart des Dienstes.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

" + }, + "step5" : { + "title" : "Funktion konfigurieren: 2FA", + "content" : "

Verbessern Sie die Sicherheit der Plattformkonten mit Zwei-Faktor-Authentifizierung.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

" + }, + "step6" : { + "title" : "Funktion konfigurieren: OAuth 2", + "content" : "

Vereinfachen Sie die Anmeldung für Mandanten- und Kundenbenutzer mit Single Sign-On über OAuth 2.0.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

" + } + }, + "tenant-admin" : { + "step1" : { + "title" : "Gerät erstellen", + "content" : "

Stellen wir Ihr erstes Gerät über die Benutzeroberfläche auf der Plattform bereit. Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-create-device" : "Wie man ein Gerät erstellt" + }, + "step2" : { + "title" : "Gerät verbinden", + "content-before" : "

Um das Gerät zu verbinden, benötigen Sie die Gerätezugangsdaten. Für diese Anleitung empfehlen wir die Verwendung der standardmäßig generierten Zugangsdaten – eines Zugriffstokens.

  • Gehen Sie zur Gerätetabelle
  • Klicken Sie auf die Gerätezeile, um die Gerätedetails zu öffnen
  • Klicken Sie auf die Schaltfläche „Zugriffstoken kopieren“

Verwenden Sie einfache Befehle, um Daten über HTTP zu senden. Vergessen Sie nicht, $ACCESS_TOKEN mit dem Zugriffstoken Ihres Geräts zu ersetzen:

", + "ubuntu" : { + "install-curl" : "cURL für Ubuntu installieren:" + }, + "macos" : { + "install-curl" : "cURL für MacOS installieren:" + }, + "windows" : { + "install-curl" : "Ab Windows 10 b17063 ist cURL standardmäßig verfügbar." + }, + "replace-access-token" : "Ersetzen Sie $ACCESS_TOKEN durch das Token Ihres Geräts:", + "content-after" : "

Sie können auch andere Protokolle wie MQTT, CoAP usw. verwenden.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-connect-device" : "Wie man ein Gerät verbindet" + }, + "step3" : { + "title" : "Dashboard erstellen", + "content" : "

Erstellen Sie ein Dashboard zur Visualisierung von Daten von Entitäten wie Assets, Geräten usw.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-create-dashboard" : "Wie man ein Dashboard erstellt" + }, + "step4" : { + "title" : "Alarmregeln konfigurieren", + "alarm-rules" : "Alarmregeln", + "content" : "

Lösen wir einen Alarm aus, wenn die Temperatur 25°C erreicht. Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-configure-alarm-rules" : "Wie man Alarmregeln konfiguriert" + }, + "step5" : { + "title" : "Alarm erstellen", + "content-before" : "

Um den Alarm auszulösen, senden Sie einen neuen Telemetriedatenwert von 26°C oder höher.

", + "replace-access-token" : "Ersetzen Sie $ACCESS_TOKEN durch das Token Ihres Geräts:", + "content-after" : "

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-create-alarm" : "Wie man einen Alarm erstellt" + }, + "step6" : { + "title" : "Kunde erstellen und Dashboard teilen", + "content" : "

Durch die Erstellung von Dashboards für Endbenutzer kann ein Kundenbenutzer nur seine eigenen Geräte sehen, während Daten anderer Kunden verborgen bleiben.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

" + } + } } }, - "icon": { - "icon": "Symbol", - "icons": "Symbole", - "select-icon": "Symbol auswählen", - "material-icons": "Material-Symbole", - "show-all": "Alle Symbole anzeigen" - }, - "custom": { - "widget-action": { - "action-cell-button": "Aktionszellenschaltfläche", - "row-click": "Klick auf Zeile", - "polygon-click": "Klick auf Polygon", - "marker-click": "Klick auf Marker", - "tooltip-tag-action": "Tooltip-Tag-Aktion", - "node-selected": "Klick auf Node", - "element-click": "Klick auf HTML element", - "pie-slice-click": "Klicken auf Slice", - "row-double-click": "Doppelklicken auf Zeile" + "icon" : { + "icon" : "Symbol", + "icons" : "Symbole", + "select-icon" : "Symbol auswählen", + "material-icons" : "Materialsymbole", + "show-all" : "Alle Symbole anzeigen", + "search-icon" : "Symbol suchen", + "no-icons-found" : "Keine Symbole für '{{iconSearch}}' gefunden" + }, + "phone-input" : { + "phone-input-label" : "Telefonnummer", + "phone-input-required" : "Telefonnummer ist erforderlich", + "phone-input-validation" : "Telefonnummer ist ungültig oder nicht möglich", + "phone-input-pattern" : "Ungültige Telefonnummer. Muss im E.164-Format sein, z. B. {{phoneNumber}}", + "phone-input-hint" : "Telefonnummer im E.164-Format, z. B. {{phoneNumber}}" + }, + "custom" : { + "widget-action" : { + "action-cell-button" : "Zellenaktionstaste", + "row-click" : "Bei Zeilenklick", + "cell-click" : "Bei Zellenklick", + "polygon-click" : "Bei Polygonklick", + "marker-click" : "Bei Markerklick", + "circle-click" : "Bei Kreis-Klick", + "tooltip-tag-action" : "Tooltip-Tag-Aktion", + "node-selected" : "Bei Knotenauswahl", + "element-click" : "Bei HTML-Element-Klick", + "pie-slice-click" : "Bei Tortenstück-Klick", + "row-double-click" : "Bei Zeilendoppelklick", + "cell-double-click" : "Bei Zellendoppelklick", + "card-click" : "Bei Kartenklick", + "click" : "Beim Klick" } }, "paginator" : { - "items-per-page": "Einträge pro Seite:", - "first-page-label": "Erste Seite", - "last-page-label": "Letzte Seite", - "next-page-label": "Nächste Seite", - "previous-page-label": "Vorherige Seite", - "items-per-page-separator": "von" - }, - "language": { - "language": "Sprache" + "items-per-page" : "Elemente pro Seite:", + "first-page-label" : "Erste Seite", + "last-page-label" : "Letzte Seite", + "next-page-label" : "Nächste Seite", + "previous-page-label" : "Vorherige Seite", + "items-per-page-separator" : "von" + }, + "language" : { + "language" : "Sprache", + "locales" : { + "ar_AE" : "العربية (الإمارات العربية المتحدة)", + "ca_ES" : "katalanisch (Spanien)", + "cs_CZ" : "tschechisch (Tschechien)", + "da_DK" : "dänisch (Dänemark)", + "de_DE" : "Deutsch (Deutschland)", + "el_GR" : "griechisch (Griechenland)", + "en_US" : "Englisch (USA)", + "es_ES" : "spanisch (Spanien)", + "fa_IR" : "persisch (Iran)", + "fr_FR" : "französisch (Frankreich)", + "it_IT" : "italienisch (Italien)", + "ja_JP" : "japanisch (Japan)", + "ka_GE" : "georgisch (Georgien)", + "ko_KR" : "koreanisch (Südkorea)", + "lt_LT" : "litauisch (Litauen)", + "lv_LV" : "lettisch (Lettland)", + "nl_BE" : "niederländisch (Belgien)", + "pl_PL" : "polnisch (Polen)", + "pt_BR" : "portugiesisch (Brasilien)", + "ro_RO" : "rumänisch (Rumänien)", + "sl_SI" : "slowenisch (Slowenien)", + "tr_TR" : "türkisch (Türkei)", + "uk_UA" : "ukrainisch (Ukraine)", + "zh_CN" : "chinesisch (China)", + "zh_TW" : "chinesisch (Taiwan)" + } } -} +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index e1329e657d..b57d99c476 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -1,6176 +1,9224 @@ { - "access": { - "unauthorized": "No autorizado", - "unauthorized-access": "Acceso no autorizado", - "unauthorized-access-text": "Debes iniciar sesión para tener acceso a este recurso!", - "access-forbidden": "Acceso Prohibido", - "access-forbidden-text": "No tienes suficientes derechos para acceder a esta ubicación!
Intenta iniciar sesión con otro usuario si todavía quieres acceder a esta ubicación.", - "refresh-token-expired": "La sesión ha expirado", - "refresh-token-failed": "No se puede actualizar la sesión", - "permission-denied": "Permiso Denegado", - "permission-denied-text": "No tienes suficientes derechos para realizar esta operación!" + "access" : { + "unauthorized" : "No autorizado", + "unauthorized-access" : "Acceso no autorizado", + "unauthorized-access-text" : "¡Debes iniciar sesión para tener acceso a este recurso!", + "access-forbidden" : "Acceso prohibido", + "access-forbidden-text" : "¡No tienes derechos de acceso a esta ubicación!
Intenta iniciar sesión con un usuario diferente si aún deseas acceder a esta ubicación.", + "refresh-token-expired" : "La sesión ha expirado", + "refresh-token-failed" : "No se puede actualizar la sesión", + "permission-denied" : "Permiso denegado", + "permission-denied-text" : "¡No tienes permiso para realizar esta operación!" + }, + "account" : { + "account" : "Cuenta", + "notification-settings" : "Configuración de notificaciones" + }, + "action" : { + "activate" : "Activar", + "suspend" : "Suspender", + "save" : "Guardar", + "saveAs" : "Guardar como", + "move" : "Mover", + "cancel" : "Cancelar", + "ok" : "OK", + "delete" : "Eliminar", + "add" : "Añadir", + "yes" : "Sí", + "no" : "No", + "update" : "Actualizar", + "remove" : "Eliminar", + "search" : "Buscar", + "clear-search" : "Borrar búsqueda", + "assign" : "Asignar", + "unassign" : "Desasignar", + "share" : "Compartir", + "make-private" : "Hacer privado", + "apply" : "Aplicar", + "apply-changes" : "Aplicar cambios", + "edit-mode" : "Modo de edición", + "enter-edit-mode" : "Entrar en modo de edición", + "decline-changes" : "Rechazar cambios", + "decline" : "Rechazar", + "close" : "Cerrar", + "back" : "Atrás", + "run" : "Ejecutar", + "sign-in" : "¡Iniciar sesión!", + "edit" : "Editar", + "view" : "Ver", + "create" : "Crear", + "drag" : "Arrastrar", + "refresh" : "Actualizar", + "undo" : "Deshacer", + "copy" : "Copiar", + "paste" : "Pegar", + "copy-reference" : "Copiar referencia", + "paste-reference" : "Pegar referencia", + "import" : "Importar", + "export" : "Exportar", + "share-via" : "Compartir vía {{provider}}", + "select" : "Seleccionar", + "continue" : "Continuar", + "discard-changes" : "Descartar cambios", + "download" : "Descargar", + "next" : "Siguiente", + "next-with-label" : "Siguiente: {{label}}", + "read-more" : "Leer más", + "hide" : "Ocultar", + "test" : "Probar", + "done" : "Hecho", + "print" : "Imprimir", + "restore" : "Restaurar", + "confirm" : "Confirmar", + "more" : "Más", + "less" : "Menos", + "skip" : "Omitir", + "send" : "Enviar", + "reset" : "Restablecer", + "show-more" : "Mostrar más", + "dont-show-again" : "No mostrar de nuevo", + "see-documentation" : "Ver documentación", + "clear" : "Limpiar", + "upload" : "Subir", + "delete-anyway" : "Eliminar de todos modos", + "delete-selected" : "Eliminar seleccionados", + "set" : "Establecer" + }, + "aggregation" : { + "aggregation" : "Agregación", + "function" : "Función de agregación de datos", + "limit" : "Valores máximos", + "group-interval" : "Intervalo de agrupación", + "min" : "Mín", + "max" : "Máx", + "avg" : "Promedio", + "sum" : "Suma", + "count" : "Conteo", + "none" : "Ninguno" + }, + "admin" : { + "settings" : "Configuraciones", + "general" : "General", + "general-settings" : "Configuración general", + "home-settings" : "Configuración de inicio", + "home" : "Inicio", + "outgoing-mail" : "Servidor de correo", + "outgoing-mail-settings" : "Configuración del servidor de correo saliente", + "system-settings" : "Configuración del sistema", + "test-mail-sent" : "¡El correo de prueba se envió correctamente!", + "base-url" : "URL base", + "base-url-required" : "Se requiere la URL base.", + "prohibit-different-url" : "Prohibir el uso del nombre de host desde las cabeceras de solicitud del cliente", + "prohibit-different-url-hint" : "Esta configuración debe habilitarse en entornos de producción. Puede causar problemas de seguridad si está deshabilitada", + "device-connectivity" : { + "device-connectivity" : "Conectividad del dispositivo", + "http-s" : "HTTP(s)", + "mqtt-s" : "MQTT(s)", + "coap-s" : "COAP(s)", + "http" : "HTTP", + "https" : "HTTPs", + "mqtt" : "MQTT", + "mqtts" : "MQTTs", + "coap" : "COAP", + "coaps" : "COAPs", + "hint" : "Si los campos de host o puerto están vacíos, se utilizará el valor predeterminado del protocolo.", + "host" : "Host", + "port" : "Puerto", + "port-pattern" : "El puerto debe ser un número entero positivo.", + "port-range" : "El puerto debe estar en el rango de 1 a 65535." }, - "account": { - "account": "Cuenta", - "notification-settings": "Ajustes de notificaciones" + "mail-from" : "Correo del remitente", + "mail-from-required" : "Se requiere el correo del remitente.", + "smtp-protocol" : "Protocolo SMTP", + "smtp-host" : "Host SMTP", + "smtp-host-required" : "Se requiere el host SMTP.", + "smtp-port" : "Puerto SMTP", + "smtp-port-required" : "Debes proporcionar un puerto SMTP.", + "smtp-port-invalid" : "Ese puerto SMTP no parece válido.", + "timeout-msec" : "Tiempo de espera (mseg)", + "timeout-required" : "Se requiere un tiempo de espera.", + "timeout-invalid" : "Ese tiempo de espera no parece válido.", + "enable-tls" : "Habilitar TLS", + "tls-version" : "Versión de TLS", + "enable-proxy" : "Habilitar proxy", + "proxy-host" : "Host proxy", + "proxy-host-required" : "Se requiere el host proxy.", + "proxy-port" : "Puerto proxy", + "proxy-port-required" : "Se requiere el puerto proxy.", + "proxy-port-range" : "El puerto proxy debe estar en el rango de 1 a 65535.", + "proxy-user" : "Usuario del proxy", + "proxy-password" : "Contraseña del proxy", + "change-password" : "Cambiar contraseña", + "send-test-mail" : "Enviar correo de prueba", + "sms-provider" : "Proveedor de SMS", + "sms-provider-settings" : "Configuración del proveedor de SMS", + "sms-provider-type" : "Tipo de proveedor de SMS", + "sms-provider-type-required" : "Se requiere el tipo de proveedor de SMS.", + "sms-provider-type-aws-sns" : "Amazon SNS", + "sms-provider-type-twilio" : "Twilio", + "sms-provider-type-smpp" : "SMPP", + "aws-access-key-id" : "ID de clave de acceso de AWS", + "aws-access-key-id-required" : "Se requiere el ID de clave de acceso de AWS", + "aws-secret-access-key" : "Clave de acceso secreta de AWS", + "aws-secret-access-key-required" : "Se requiere la clave de acceso secreta de AWS", + "aws-region" : "Región de AWS", + "aws-region-required" : "Se requiere la región de AWS", + "number-from" : "Número de teléfono de origen", + "number-from-required" : "Se requiere el número de teléfono de origen.", + "number-to" : "Número de teléfono de destino", + "number-to-required" : "Se requiere el número de teléfono de destino.", + "phone-number-hint" : "Número de teléfono en formato E.164, ej. +19995550123", + "phone-number-hint-twilio" : "Número de teléfono en formato E.164/SID del número/SID del servicio de mensajería, ej. +19995550123/PNXXX/MGXXX", + "phone-number-pattern" : "Número de teléfono inválido. Debe estar en formato E.164, ej. +19995550123.", + "phone-number-pattern-twilio" : "Número de teléfono inválido. Debe estar en formato E.164/SID del número/SID del servicio de mensajería, ej. +19995550123/PNXXX/MGXXX.", + "sms-message" : "Mensaje SMS", + "sms-message-required" : "Se requiere el mensaje SMS.", + "sms-message-max-length" : "El mensaje SMS no puede superar los 1600 caracteres", + "twilio-account-sid" : "SID de cuenta de Twilio", + "twilio-account-sid-required" : "Se requiere el SID de cuenta de Twilio", + "twilio-account-token" : "Token de cuenta de Twilio", + "twilio-account-token-required" : "Se requiere el token de cuenta de Twilio", + "send-test-sms" : "Enviar SMS de prueba", + "test-sms-sent" : "¡El SMS de prueba se envió correctamente!", + "security-settings" : "Configuración de seguridad", + "password-policy" : "Política de contraseñas", + "minimum-password-length" : "Longitud mínima de la contraseña", + "minimum-password-length-required" : "Se requiere la longitud mínima de la contraseña", + "minimum-password-length-range" : "La longitud mínima de la contraseña debe estar entre 6 y 50", + "maximum-password-length" : "Longitud máxima de la contraseña", + "maximum-password-length-min" : "La longitud máxima de la contraseña debe ser al menos 6", + "maximum-password-length-less-min" : "La longitud máxima de la contraseña debe ser mayor que la mínima", + "minimum-uppercase-letters" : "Número mínimo de letras mayúsculas", + "minimum-uppercase-letters-range" : "El número mínimo de letras mayúsculas no puede ser negativo", + "minimum-lowercase-letters" : "Número mínimo de letras minúsculas", + "minimum-lowercase-letters-range" : "El número mínimo de letras minúsculas no puede ser negativo", + "minimum-digits" : "Número mínimo de dígitos", + "minimum-digits-range" : "El número mínimo de dígitos no puede ser negativo", + "minimum-special-characters" : "Número mínimo de caracteres especiales", + "minimum-special-characters-range" : "El número mínimo de caracteres especiales no puede ser negativo", + "password-expiration-period-days" : "Periodo de expiración de contraseña (en días)", + "password-expiration-period-days-range" : "El periodo de expiración de la contraseña no puede ser negativo", + "password-reuse-frequency-days" : "Frecuencia de reutilización de contraseña (en días)", + "password-reuse-frequency-days-range" : "La frecuencia de reutilización de la contraseña no puede ser negativa", + "allow-whitespace" : "Permitir espacios en blanco", + "force-reset-password-if-no-valid" : "Forzar restablecimiento de contraseña si no es válida", + "force-reset-password-if-no-valid-hint" : "Ten cuidado al habilitar esta función: requerirá a los usuarios con contraseñas no válidas que las restablezcan por correo electrónico.", + "general-policy" : "Política general", + "max-failed-login-attempts" : "Número máximo de intentos fallidos de inicio de sesión antes de bloquear la cuenta", + "minimum-max-failed-login-attempts-range" : "El número máximo de intentos fallidos no puede ser negativo", + "user-lockout-notification-email" : "En caso de bloqueo de cuenta de usuario, enviar notificación por correo", + "user-activation-token-ttl" : "Duración del enlace de activación de usuario (en horas)", + "user-activation-token-ttl-range" : "La duración del enlace de activación debe estar entre 1 y 24 horas", + "password-reset-token-ttl" : "Duración del enlace de restablecimiento de contraseña (en horas)", + "password-reset-token-ttl-range" : "La duración del enlace de restablecimiento debe estar entre 1 y 24 horas", + "mobile-secret-key-length" : "Longitud de la clave secreta móvil", + "mobile-secret-key-length-range" : "La longitud de la clave secreta móvil debe ser positiva", + "domain-name" : "Nombre de dominio", + "domain-name-unique" : "El nombre de dominio y el protocolo deben ser únicos.", + "domain-name-max-length" : "El nombre de dominio debe tener menos de 256 caracteres", + "error-verification-url" : "Un nombre de dominio no debe contener símbolos '/' ni ':'. Ejemplo: thingsboard.io", + "connection-settings" : "Configuración de conexión", + "oauth2" : { + "access-token-uri" : "URI del token de acceso", + "access-token-uri-required" : "Se requiere la URI del token de acceso.", + "activate-user" : "Activar usuario", + "add-domain" : "Agregar dominio", + "delete-domain" : "Eliminar dominio", + "add-provider" : "Agregar proveedor", + "delete-provider" : "Eliminar proveedor", + "allow-user-creation" : "Permitir creación de usuarios", + "always-fullscreen" : "Siempre en pantalla completa", + "authorization-uri" : "URI de autorización", + "authorization-uri-required" : "Se requiere la URI de autorización.", + "add-client" : "Agregar cliente OAuth 2.0", + "client-details" : "Detalles del cliente OAuth 2.0", + "client" : "Cliente OAuth 2.0", + "clients" : "Clientes OAuth 2.0", + "no-oauth2-clients" : "No se encontraron clientes OAuth 2.0", + "search-oauth2-clients" : "Buscar clientes OAuth 2.0", + "delete-client-title" : "¿Estás seguro de que deseas eliminar el cliente OAuth 2.0 '{{clientName}}'?", + "delete-client-text" : "Ten cuidado, después de la confirmación el cliente y todos los datos relacionados serán irrecuperables.", + "delete-mobile-app-title" : "¿Estás seguro de que deseas eliminar la aplicación móvil '{{applicationName}}'?", + "delete-mobile-app-text" : "Ten cuidado, después de la confirmación la aplicación móvil y todos los datos relacionados serán irrecuperables.", + "title" : "Título", + "client-title-required" : "Se requiere el título", + "client-title-max-length" : "El título debe tener menos de 100 caracteres", + "advanced-settings" : "Configuraciones avanzadas", + "domain-details" : "Detalles del dominio", + "no-domains" : "No se encontraron dominios", + "search-domains" : "Buscar dominios", + "mobile-app-details" : "Detalles de la aplicación móvil", + "add-mobile-app" : "Agregar aplicación móvil", + "no-mobile-apps" : "No se encontraron aplicaciones móviles", + "search-mobile-apps" : "Buscar aplicaciones móviles", + "send-token" : "Enviar token", + "create-new" : "Crear nuevo", + "client-authentication-method" : "Método de autenticación del cliente", + "client-id" : "ID de cliente", + "client-id-required" : "Se requiere el ID de cliente.", + "client-id-max-length" : "El ID de cliente debe tener menos de 256 caracteres", + "client-secret" : "Secreto del cliente", + "client-secret-required" : "Se requiere el secreto del cliente.", + "client-secret-max-length" : "El secreto del cliente debe tener menos de 2049 caracteres", + "custom-setting" : "Configuraciones personalizadas", + "customer-name-pattern" : "Patrón de nombre de cliente", + "customer-name-pattern-max-length" : "El patrón de nombre de cliente debe tener menos de 256 caracteres", + "default-dashboard-name" : "Nombre del tablero predeterminado", + "default-dashboard-name-max-length" : "El nombre del tablero predeterminado debe tener menos de 256 caracteres", + "delete-domain-text" : "Ten cuidado, después de la confirmación el dominio y todos los datos del proveedor dejarán de estar disponibles.", + "delete-domain-title" : "¿Estás seguro de que deseas eliminar el dominio '{{domainName}}'?", + "delete-registration-text" : "Ten cuidado, después de la confirmación los datos del proveedor dejarán de estar disponibles.", + "delete-registration-title" : "¿Estás seguro de que deseas eliminar el proveedor '{{name}}'?", + "email-attribute-key" : "Clave del atributo de correo electrónico", + "email-attribute-key-required" : "Se requiere la clave del atributo de correo electrónico.", + "email-attribute-key-max-length" : "La clave del atributo de correo electrónico debe tener menos de 32 caracteres", + "first-name-attribute-key" : "Clave del atributo del nombre", + "first-name-attribute-key-max-length" : "La clave del atributo del nombre debe tener menos de 32 caracteres", + "general" : "General", + "jwk-set-uri" : "URI del conjunto de claves JSON Web", + "last-name-attribute-key" : "Clave del atributo del apellido", + "last-name-attribute-key-max-length" : "La clave del atributo del apellido debe tener menos de 32 caracteres", + "login-button-icon" : "Icono del botón de inicio de sesión", + "login-button-label" : "Etiqueta del proveedor", + "login-button-label-placeholder" : "Iniciar sesión con $(Provider label)", + "login-button-label-required" : "Se requiere la etiqueta.", + "login-provider" : "Proveedor de inicio de sesión", + "mapper" : "Mapeador", + "new-domain" : "Nuevo dominio", + "oauth2" : "OAuth 2.0", + "password-max-length" : "La contraseña debe tener menos de 256 caracteres", + "redirect-uri-template" : "Plantilla de URI de redirección", + "copy-redirect-uri" : "Copiar URI de redirección", + "registration-id" : "ID de registro", + "registration-id-required" : "Se requiere el ID de registro.", + "registration-id-unique" : "El ID de registro debe ser único en el sistema.", + "scope" : "Alcance", + "scope-required" : "Se requiere el alcance.", + "tenant-name-pattern" : "Patrón de nombre del inquilino", + "tenant-name-pattern-required" : "Se requiere el patrón de nombre del inquilino.", + "tenant-name-pattern-max-length" : "El patrón de nombre del inquilino debe tener menos de 256 caracteres", + "tenant-name-strategy" : "Estrategia de nombre del inquilino", + "type" : "Tipo de mapeador", + "uri-pattern-error" : "Formato de URI inválido.", + "url" : "URL", + "url-pattern" : "Formato de URL inválido.", + "url-required" : "Se requiere la URL.", + "url-max-length" : "La URL debe tener menos de 256 caracteres", + "user-info-uri" : "URI de información del usuario", + "user-info-uri-required" : "Se requiere la URI de información del usuario.", + "username-max-length" : "El nombre de usuario debe tener menos de 256 caracteres", + "user-name-attribute-name" : "Clave del atributo del nombre de usuario", + "user-name-attribute-name-required" : "Se requiere la clave del atributo del nombre de usuario", + "protocol" : "Protocolo", + "domain-schema-http" : "HTTP", + "domain-schema-https" : "HTTPS", + "domain-schema-mixed" : "HTTP+HTTPS", + "enable" : "Habilitar configuración OAuth 2.0", + "disable" : "Deshabilitar configuración OAuth 2.0", + "edge" : "Propagar a Edge", + "edge-enable" : "Habilitar propagación a Edge", + "edge-disable" : "Deshabilitar propagación a Edge", + "domains" : "Dominios", + "mobile-apps" : "Aplicaciones móviles", + "mobile-package" : "Paquete de la aplicación", + "mobile-package-placeholder" : "Ej.: my.example.app", + "mobile-package-hint" : "Para Android: tu propio ID único de aplicación. Para iOS: identificador del paquete del producto.", + "mobile-package-unique" : "El paquete de la aplicación debe ser único.", + "mobile-package-required" : "Se requiere el paquete de la aplicación.", + "mobile-package-max-length" : "El paquete de la aplicación debe tener menos de 256 caracteres", + "mobile-package-spaces" : "El paquete de la aplicación no debe contener espacios", + "mobile-app-secret" : "Secreto de la aplicación", + "mobile-app-secret-hint" : "Cadena codificada en Base64 que representa al menos 512 bits de datos.", + "mobile-app-secret-required" : "Se requiere el secreto de la aplicación.", + "mobile-app-secret-min-length" : "El secreto de la aplicación debe tener al menos 512 bits de datos.", + "mobile-app-secret-base64" : "El secreto de la aplicación debe estar en formato base64.", + "invalid-mobile-app-secret" : "El secreto de la aplicación solo debe contener caracteres alfanuméricos y tener entre 16 y 2048 caracteres.", + "copy-mobile-app-secret" : "Copiar secreto de la aplicación", + "delete-mobile-app" : "Eliminar información de la aplicación", + "providers" : "Proveedores", + "platform-web" : "Web", + "platform-android" : "Android", + "platform-ios" : "iOS", + "all-platforms" : "Todas las plataformas", + "smtp-provider" : "Proveedor SMTP", + "allowed-platforms" : "Plataformas permitidas", + "authentication" : "Autenticación", + "basic" : "Básico", + "provider" : "Proveedor", + "redirect-url" : "URI de redirección", + "domain-name" : "Nombre de dominio", + "domain-name-required" : "Se requiere el nombre de dominio", + "redirect-url-template" : "Plantilla de URI de redirección", + "microsoft-tenant-id" : "ID de directorio (inquilino)", + "microsoft-tenant-id-required" : "Se requiere el ID de directorio (inquilino)", + "token-uri" : "URI del token", + "token-uri-required" : "Se requiere la URI del token", + "redirect-uri" : "URI de redirección", + "google-provider" : "Google", + "microsoft-provider" : "Office 365", + "sendgrid-provider" : "Sendgrid", + "custom-provider" : "Personalizado", + "generate-access-token" : "Generar token de acceso", + "update-access-token" : "Actualizar token de acceso", + "access-token-status" : "Estado del token de acceso:", + "token-status-generated" : "generado", + "token-status-not-generated" : "no generado" }, - "action": { - "activate": "Activar", - "suspend": "Suspender", - "save": "Guardar", - "saveAs": "Guardar como", - "move": "Mover", - "cancel": "Cancelar", - "ok": "OK", - "delete": "Borrar", - "add": "Agregar", - "yes": "Si", - "no": "No", - "update": "Actualizar", - "remove": "Eliminar", - "select": "Seleccionar", - "search": "Buscar", - "clear-search": "Borrar búsqueda", - "assign": "Asignar", - "unassign": "Anular asignación", - "share": "Compartir", - "make-private": "Hacer privado", - "apply": "Aplicar", - "apply-changes": "Aplicar cambios", - "edit-mode": "Modo Edición", - "enter-edit-mode": "Modo Edición", - "decline-changes": "Descartar cambios", - "decline": "Descartar", - "close": "Cerrar", - "back": "Atrás", - "run": "Ejecutar", - "sign-in": "Entrar!", - "edit": "Editar", - "view": "Ver", - "create": "Crear", - "drag": "Arrastrar", - "refresh": "Actualizar", - "undo": "Deshacer", - "copy": "Copiar", - "paste": "Pegar", - "copy-reference": "Copiar referencia", - "paste-reference": "Pegar referencia", - "import": "Importar", - "export": "Exportar", - "share-via": "Compartir vía {{provider}}", - "continue": "Continuar", - "discard-changes": "Cancelar cambios", - "download": "Descargar", - "next": "Siguiente", - "next-with-label": "Siguiente: {{label}}", - "read-more": "Leer más", - "hide": "Ocultar", - "done": "Terminado", - "print": "Imprimir", - "restore": "Restaurar", - "confirm": "Confirmar", - "more": "Más", - "less": "Menos", - "skip": "Saltar", - "send": "Enviar", - "reset": "Reset", - "show-more": "Mostrar más", - "dont-show-again": "No volver a mostrar", - "see-documentation": "Ver documentación", - "clear": "Borrar" + "smpp-provider" : { + "smpp-version" : "Versión SMPP", + "smpp-host" : "Host SMPP", + "smpp-host-required" : "Se requiere el host SMPP", + "smpp-port" : "Puerto SMPP", + "smpp-port-required" : "Se requiere el puerto SMPP", + "system-id" : "ID del sistema", + "system-id-required" : "Se requiere el ID del sistema", + "password" : "Contraseña", + "password-required" : "Se requiere la contraseña", + "type-settings" : "Configuración del tipo", + "source-settings" : "Configuración de origen", + "destination-settings" : "Configuración de destino", + "additional-settings" : "Configuración adicional", + "system-type" : "Tipo de sistema", + "bind-type" : "Tipo de enlace", + "service-type" : "Tipo de servicio", + "source-address" : "Dirección de origen", + "source-ton" : "TON de origen", + "source-npi" : "NPI de origen", + "destination-ton" : "TON de destino (Tipo de número)", + "destination-npi" : "NPI de destino (Identificación del plan de numeración)", + "address-range" : "Rango de direcciones", + "coding-scheme" : "Esquema de codificación", + "bind-type-tx" : "Transmisor", + "bind-type-rx" : "Receptor", + "bind-type-trx" : "Transceptor", + "ton-unknown" : "Desconocido", + "ton-international" : "Internacional", + "ton-national" : "Nacional", + "ton-network-specific" : "Específico de red", + "ton-subscriber-number" : "Número de suscriptor", + "ton-alphanumeric" : "Alfanumérico", + "ton-abbreviated" : "Abreviado", + "npi-unknown" : "0 - Desconocido", + "npi-isdn" : "1 - ISDN/plan de numeración telefónica (E163/E164)", + "npi-data-numbering-plan" : "3 - Plan de numeración de datos (X.121)", + "npi-telex-numbering-plan" : "4 - Plan de numeración de télex (F.69)", + "npi-land-mobile" : "6 - Móvil terrestre (E.212)", + "npi-national-numbering-plan" : "8 - Plan de numeración nacional", + "npi-private-numbering-plan" : "9 - Plan de numeración privado", + "npi-ermes-numbering-plan" : "10 - Plan de numeración ERMES (ETSI DE/PS 3 01-3)", + "npi-internet" : "13 - Internet (IP)", + "npi-wap-client-id" : "18 - ID de cliente WAP (definido por el Foro WAP)", + "scheme-smsc" : "0 - Alfabeto predeterminado SMSC (ASCII para códigos cortos y largos y GSM para números gratuitos)", + "scheme-ia5" : "1 - IA5 (ASCII para códigos cortos y largos, Latin 9 para números gratuitos (ISO-8859-9))", + "scheme-octet-unspecified-2" : "2 - Octeto no especificado (binario de 8 bits)", + "scheme-latin-1" : "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4" : "4 - Octeto no especificado (binario de 8 bits)", + "scheme-jis" : "5 - JIS (X 0208-1990)", + "scheme-cyrillic" : "6 - Cirílico (ISO-8859-5)", + "scheme-latin-hebrew" : "7 - Latín/Hebreo (ISO-8859-8)", + "scheme-ucs-utf" : "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding" : "9 - Codificación de pictogramas", + "scheme-music-codes" : "10 - Códigos de música (ISO-2022-JP)", + "scheme-extended-kanji-jis" : "13 - Kanji extendido JIS (X 0212-1990)", + "scheme-korean-graphic-character-set" : "14 - Conjunto gráfico de caracteres coreanos (KS C 5601/KS X 1001)" }, - "aggregation": { - "aggregation": "Agrupación", - "function": "Función de Agrupación", - "limit": "Valores Max", - "group-interval": "Intervalo de agrupación", - "min": "Min", - "max": "Max", - "avg": "Promedio", - "sum": "Suma", - "count": "Contar", - "none": "Ninguno" + "queue-select-name" : "Seleccionar nombre de la cola", + "queue-name" : "Nombre", + "queue-name-required" : "¡Se requiere el nombre de la cola!", + "queues" : "Colas", + "queue-partitions" : "Particiones", + "queue-submit-strategy" : "Estrategia de envío", + "queue-processing-strategy" : "Estrategia de procesamiento", + "queue-configuration" : "Configuración de la cola", + "repository-settings" : "Configuración del repositorio", + "repository" : "Repositorio", + "repository-url" : "URL del repositorio", + "repository-url-required" : "Se requiere la URL del repositorio.", + "default-branch" : "Nombre de la rama predeterminada", + "repository-read-only" : "Solo lectura", + "show-merge-commits" : "Mostrar commits de fusión", + "authentication-settings" : "Configuración de autenticación", + "auth-method" : "Método de autenticación", + "auth-method-username-password" : "Contraseña / token de acceso", + "auth-method-username-password-hint" : "Los usuarios de GitHub deben usar tokens con permisos de escritura para el repositorio.", + "auth-method-private-key" : "Clave privada", + "password-access-token" : "Contraseña / token de acceso", + "change-password-access-token" : "Cambiar contraseña / token de acceso", + "private-key" : "Clave privada", + "drop-private-key-file-or" : "Arrastra y suelta un archivo de clave privada o", + "passphrase" : "Frase de contraseña", + "enter-passphrase" : "Introduce la frase de contraseña", + "change-passphrase" : "Cambiar frase de contraseña", + "check-access" : "Verificar acceso", + "check-repository-access-success" : "¡Acceso al repositorio verificado con éxito!", + "delete-repository-settings-title" : "¿Estás seguro de que deseas eliminar la configuración del repositorio?", + "delete-repository-settings-text" : "Ten cuidado, después de la confirmación la configuración del repositorio se eliminará y la función de control de versiones no estará disponible.", + "auto-commit-settings" : "Configuración de auto-commit", + "auto-commit" : "Auto-commit", + "auto-commit-entities" : "Entidades de auto-commit", + "no-auto-commit-entities-prompt" : "No hay entidades configuradas para auto-commit", + "delete-auto-commit-settings-title" : "¿Estás seguro de que deseas eliminar la configuración de auto-commit?", + "delete-auto-commit-settings-text" : "Ten cuidado, después de la confirmación la configuración de auto-commit se eliminará y se deshabilitará para todas las entidades.", + "mobile-app" : { + "mobile-app" : "Aplicación móvil", + "mobile-app-qr-code-widget-settings" : "Configuración del widget de código QR de la aplicación móvil", + "applications" : "Aplicaciones", + "default" : "Predeterminado", + "custom" : "Personalizado", + "android" : "Android", + "ios" : "iOS", + "appearance" : "Apariencia", + "appearance-on-home-page" : "Apariencia en la página de inicio", + "enabled" : "Habilitado", + "disabled" : "Deshabilitado", + "badges" : "Insignias", + "label" : "Etiqueta", + "label-required" : "Se requiere la etiqueta", + "label-max-length" : "La etiqueta debe tener 50 caracteres o menos", + "right" : "Derecha", + "left" : "Izquierda", + "set" : "Establecer", + "preview" : "Vista previa", + "connect-mobile-app" : "Conectar aplicación móvil", + "use-system-settings" : "Usar configuración del sistema" }, - "admin": { - "settings": "Ajustes", - "general": "General", - "general-settings": "Configuración general", - "home-settings": "Ajustes Home", - "home": "Home", - "outgoing-mail": "Servidor de correo", - "outgoing-mail-settings": "Configuración del servidor de correo de salida", - "system-settings": "Sistema", - "test-mail-sent": "Mail de prueba enviado correctamente!", - "base-url": "URL Base", - "base-url-required": "URL Base requerida.", - "prohibit-different-url": "Prohibir el uso de hostname en cabeceras de request del cliente", - "prohibit-different-url-hint": "Este ajuste debe ser activado en entornos de producción. Puede causar fallos de seguridad si está desactivado", - "mail-from": "Mail Desde", - "mail-from-required": "Mail Desde requerido.", - "smtp-protocol": "Protocolo SMTP", - "smtp-host": "Host SMTP", - "smtp-host-required": "Host SMTP requerido.", - "smtp-port": "Puerto SMTP", - "smtp-port-required": "Debe ingresar un Puerto SMTP.", - "smtp-port-invalid": "No parece un Puerto SMTP valido.", - "timeout-msec": "Timeout (ms)", - "timeout-required": "Timeout requerido.", - "timeout-invalid": "No parece un Timeout valido.", - "enable-tls": "Habilitar TLS", - "tls-version": "Versión TLS", - "enable-proxy": "Habilitar proxy", - "proxy-host": "Host proxy", - "proxy-host-required": "Se requiere host Proxy.", - "proxy-port": "Puerto proxy", - "proxy-port-required": "Se requiere puerto proxy.", - "proxy-port-range": "El puerto proxy debe estar en un rango de 1 a 65535.", - "proxy-user": "Usuario proxy", - "proxy-password": "Contraseña proxy", - "change-password": "Cambiar contraseña", - "send-test-mail": "Enviar correo de prueba", - "sms-provider": "Proveedor SMS", - "sms-provider-settings": "Ajustes proveedor SMS", - "sms-provider-type": "Tipo de proveedor SMS", - "sms-provider-type-required": "Se requiere proveedor SMS.", - "sms-provider-type-aws-sns": "Amazon SNS", - "sms-provider-type-twilio": "Twilio", - "sms-provider-type-smpp": "SMPP", - "aws-access-key-id": "AWS Access Key ID", - "aws-access-key-id-required": "AWS Access Key ID requerido", - "aws-secret-access-key": "AWS Secret Access Key", - "aws-secret-access-key-required": "AWS Secret Access Key requerido", - "aws-region": "Región AWS", - "aws-region-required": "Se requere región AWS", - "number-from": "Nº de teléfono Origen", - "number-from-required": "Se requere Nº de teléfono origen.", - "number-to": "Nº de teléfono de destino", - "number-to-required": "Se requere Nº de teléfono de destino.", - "phone-number-hint": "Nº de teléfono en formato E.164, ej. +19995550123", - "phone-number-hint-twilio": "Nº de teléfono en formato E.164 SID/SID de servicio de mensajería, ej. +19995550123/PNXXX/MGXXX", - "phone-number-pattern": "Nº Inválido. Debe estar en formato E.164, ej. +19995550123.", - "phone-number-pattern-twilio": "Nº Inválido. Debe estar en formato E.164 , ej. +19995550123/PNXXX/MGXXX.", - "sms-message": "Mensaje SMS", - "sms-message-required": "Se requeriere mensaje SMS.", - "sms-message-max-length": "Los SMS no pueden ser más largos de 1600 caracteres", - "twilio-account-sid": "SID de cuenta Twilio", - "twilio-account-sid-required": "Se requere SID de cuenta Twilio", - "twilio-account-token": "Token de cuenta Twilio", - "twilio-account-token-required": "Se requiere Token Twilio", - "send-test-sms": "Enviar SMS de prueba", - "test-sms-sent": "SMS enviado con éxito!", - "security-settings": "Configuraciones de seguridad", - "password-policy": "Política de contraseñas", - "minimum-password-length": "Longitud mínima de contraseña", - "minimum-password-length-required": "Se requiere una longitud mínima de contraseña", - "minimum-password-length-range": "La longitud mínima de la contraseña debe estar en un rango de 5 a 50", - "minimum-uppercase-letters": "Número mínimo de letras mayúsculas", - "minimum-uppercase-letters-range": "El número mínimo de letras mayúsculas no puede ser negativo", - "minimum-lowercase-letters": "Número mínimo de letras minúsculas", - "minimum-lowercase-letters-range": "El número mínimo de letras minúsculas no puede ser negativo", - "minimum-digits": "Número mínimo de dígitos", - "minimum-digits-range": "El número mínimo de dígitos no puede ser negativo", - "minimum-special-characters": "Número mínimo de caracteres especiales.", - "minimum-special-characters-range": "El número mínimo de caracteres especiales no puede ser negativo.", - "password-expiration-period-days": "Periodo de caducidad de contraseña en días", - "password-expiration-period-days-range": "El período de caducidad de la contraseña en días no puede ser negativo", - "password-reuse-frequency-days": "Frecuencia de reutilización de contraseña en días", - "password-reuse-frequency-days-range": "La frecuencia de reutilización de contraseña en días no puede ser negativa", - "allow-whitespace": "Permitir espacios en blanco", - "general-policy": "Política general", - "max-failed-login-attempts": "Número máximo de intentos fallidos de inicio de sesión, antes de que la cuenta esté bloqueada", - "minimum-max-failed-login-attempts-range": "El número máximo de intentos fallidos de inicio de sesión no puede ser negativo", - "user-lockout-notification-email": "En caso de bloqueo de la cuenta del usuario, envíe una notificación por correo electrónico", - "domain-name": "Nombre de dominio", - "domain-name-unique": "El nombre de dominio y protocolo debe ser único.", - "domain-name-max-length": "El nombre de dominio debe ser menor que 256", - "error-verification-url": "Un nombre de dominio no debe contener símbolos '/' y ':'. Ejemplo: thingsboard.io", - "connection-settings": "Ajustes de conexión", - "oauth2": { - "access-token-uri": "URI Access token", - "access-token-uri-required": "Se requere URI Access token.", - "activate-user": "Activar usuario", - "add-domain": "Añadir dominio", - "delete-domain": "Borrar dominio", - "add-provider": "Añadir proveedor", - "delete-provider": "Borrar proveedor", - "allow-user-creation": "Permitir creación de usuario", - "always-fullscreen": "Siempre pantalla completa", - "authorization-uri": "URI Autorización", - "authorization-uri-required": "Se requiere URI de Autorización.", - "client-authentication-method": "Método de autenticación", - "client-id": "ID Cliente", - "client-id-required": "Se requere ID Cliente.", - "client-id-max-length": "El ID Cliente debe ser menor de 256", - "client-secret": "Secreto de Cliente", - "client-secret-required": "Se requiere Secreto de Cliente.", - "client-secret-max-length": "El secreto de cliente debe ser menor de 2049", - "custom-setting": "Ajustes personalizados", - "customer-name-pattern": "Patrón nombre de cliente", - "customer-name-pattern-max-length": "El patrón del nombre de cliente debe ser menor de 256", - "default-dashboard-name": "Nombre de panel por defecto", - "default-dashboard-name-max-length": "El nombre del panel debe ser menor de 256", - "delete-domain-text": "Atención, tras la confirmación el dominio y todos los datos del proveedor no estarán disponibles.", - "delete-domain-title": "Eliminar los ajustes del dominio '{{domainName}}'?", - "delete-registration-text": "Atención, tras la confirmación los datos del proveedor no estarán disponibles.", - "delete-registration-title": "Eliminar el proveedor '{{name}}'?", - "email-attribute-key": "Clave de atributos email", - "email-attribute-key-required": "Se requiere clave de atributos de email.", - "email-attribute-key-max-length": "La clave de atributos de email debe ser menor de 32", - "first-name-attribute-key": "Clave de atributos de nombre", - "first-name-attribute-key-max-length": "La clave de atributos de nombre debe ser menor de 32", - "general": "General", - "jwk-set-uri": "URI web key JSON", - "last-name-attribute-key": "Clave de atributos de apellido", - "last-name-attribute-key-max-length": "La clave de atributos de apellido debe ser menor de 32", - "login-button-icon": "Icono de botón login", - "login-button-label": "Etiqueta de proveedor", - "login-button-label-placeholder": "Login con $(Provider label)", - "login-button-label-required": "Clave de etiqueta requerida.", - "login-provider": "Proveedor de login", - "mapper": "Mapeador", - "new-domain": "Nuevo dominio", - "oauth2": "OAuth2", - "password-max-length": "La contraseña debe ser menor de 256", - "redirect-uri-template": "Plantilla de redirección URI", - "copy-redirect-uri": "Copiar URI de redirección", - "registration-id": "ID de registro", - "registration-id-required": "Se requiere ID de registro.", - "registration-id-unique": "El ID de registro debe ser único en el sistema.", - "scope": "Alcance", - "scope-required": "Se requiere alcance.", - "tenant-name-pattern": "Patrón de nombre de propietario", - "tenant-name-pattern-required": "Se requiere patrón de nombre de propietario.", - "tenant-name-pattern-max-length": "EL patrón de nombre de propietario debe ser menor de 256", - "tenant-name-strategy": "Estrategia de Nombre de Propietario", - "type": "Tipo de mapeador", - "uri-pattern-error": "Formato de URI inválido.", - "url": "URL", - "url-pattern": "Formato URL inválido.", - "url-required": "Se requiere URL.", - "url-max-length": "URL debe ser menor de 256", - "user-info-uri": "URI Información de usuario", - "user-info-uri-required": "Se requiere URI de información usuario.", - "username-max-length": "El usuario debe ser menor de 256", - "user-name-attribute-name": "Clave de atributos de nombre de usuario", - "user-name-attribute-name-required": "Se requiere clave de atributos de nombre de usuario", - "protocol": "Protocolo", - "domain-schema-http": "HTTP", - "domain-schema-https": "HTTPS", - "domain-schema-mixed": "HTTP+HTTPS", - "enable": "Activar ajustes OAuth2", - "domains": "Dominios", - "mobile-apps": "Aplicaciones móviles", - "no-mobile-apps": "No hay aplicaciones configuradas", - "mobile-package": "Paquete de aplicación", - "mobile-package-placeholder": "Ej.: mi.ejemplo.app", - "mobile-package-hint": "Para Android: El ID único de aplicación. Para iOS: Identificador Product bundle.", - "mobile-package-unique": "El paquete de aplicación debe ser único.", - "mobile-app-secret": "Secreto de aplicación", - "invalid-mobile-app-secret": "El secreto de aplicación sólo puede contener carácteres alfanuméricos y debe tener entre 16 y 2048 carácteres de longitud.", - "copy-mobile-app-secret": "Copiar secreto de aplicación", - "add-mobile-app": "Añadir aplicación", - "delete-mobile-app": "Borrar información de aplicación", - "providers": "Proveedores", - "platform-web": "Web", - "platform-android": "Android", - "platform-ios": "iOS", - "all-platforms": "Todas las plataformas", - "smtp-provider": "Proveedor SMTP", - "allowed-platforms": "Plataformas permitidas", - "authentication": "Autenticación", - "basic": "Básica", - "provider": "Proveedor", - "redirect-url": "URL Redirección", - "domain-name": "Nombre de dominio", - "redirect-url-template": "Plantilla de redirección (URI)", - "microsoft-tenant-id": "Microsoft tenant ID", - "microsoft-tenant-id-required": "Se requiere tenant ID", - "token-uri": "URI Token", - "token-uri-required": "Se requiere URI token", - "redirect-uri": "URI Redirección", - "google-provider": "Google", - "microsoft-provider": "Office 365", - "sendgrid-provider": "Sendgrid", - "custom-provider": "Personalizado", - "generate-access-token": "Generar token de acceso", - "update-access-token": "Actualizar token de acceso", - "access-token-status": "Estado token:", - "token-status-generated": "Generado", - "token-status-not-generated": "No generado" - }, - "smpp-provider": { - "smpp-version": "Versión SMPP", - "smpp-host": "Host SMPP", - "smpp-host-required": "Host SMPP requerido", - "smpp-port": "Puerto SMPP", - "smpp-port-required": "Puerto SMPP requerido", - "system-id": "System ID", - "system-id-required": "System ID requerido", - "password": "Contraseña", - "password-required": "Contraseña requerida", - "type-settings": "Ajustes de tipo", - "source-settings": "Ajustes de origen", - "destination-settings": "Ajustes de destino", - "additional-settings": "Ajustes adicionales", - "system-type": "Tipo de sistema", - "bind-type": "Tipo de enlace", - "service-type": "Tipo de servicio", - "source-address": "Dirección de origen", - "source-ton": "Origen TON", - "source-npi": "Origen NPI", - "destination-ton": "Destino TON (Tipo de número)", - "destination-npi": "Destino NPI (Numbering Plan Identification)", - "address-range": "Rango de dirección", - "coding-scheme": "Esquema de codificación", - "bind-type-tx": "Transmisor", - "bind-type-rx": "Receptor", - "bind-type-trx": "Transceptor", - "ton-unknown": "Desconocido", - "ton-international": "Internacional", - "ton-national": "Nacional", - "ton-network-specific": "Específico de red", - "ton-subscriber-number": "Número de suscriptor", - "ton-alphanumeric": "Alfanumérico", - "ton-abbreviated": "Abreviado", - "npi-unknown": "0 - Desconocido", - "npi-isdn": "1 - RDSI/Teléfono plan numérico (E163/E164)", - "npi-data-numbering-plan": "3 - Datos plan numérico (X.121)", - "npi-telex-numbering-plan": "4 - Telex plan numérico (F.69)", - "npi-land-mobile": "6 - Línea Móvil (E.212)", - "npi-national-numbering-plan": "8 - Nacional", - "npi-private-numbering-plan": "9 - Privado", - "npi-ermes-numbering-plan": "10 - ERMES (ETSI DE/PS 3 01-3)", - "npi-internet": "13 - Internet (IP)", - "npi-wap-client-id": "18 - WAP Client Id (a definir por WAP Forum)", - "scheme-smsc": "0 - Alfabeto por defecto SMSC (ASCII para códigos cortos y largos y GSM para gratuitos)", - "scheme-ia5": "1 - IA5 (ASCII para códigos cortos y largos, Latin 9 para gratuitos (ISO-8859-9))", - "scheme-octet-unspecified-2": "2 - Octetos sin especificar (binario 8-bit)", - "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", - "scheme-octet-unspecified-4": "4 - Octetos sin especificar (binario 8-bit)", - "scheme-jis": "5 - JIS (X 0208-1990)", - "scheme-cyrillic": "6 - Ciríllico (ISO-8859-5)", - "scheme-latin-hebrew": "7 - Latin/Hebreo (ISO-8859-8)", - "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", - "scheme-pictogram-encoding": "9 - Codificación por Pictograma", - "scheme-music-codes": "10 - Códigos musicales (ISO-2022-JP)", - "scheme-extended-kanji-jis": "13 - Kanji extendido JIS (X 0212-1990)", - "scheme-korean-graphic-character-set": "14 - Set de carácteres Koreanos (KS C 5601/KS X 1001)" - }, - "queue-select-name": "Seleccionar nombre de cola", - "queue-name": "Nombre", - "queue-name-required": "Nombre de cola requerido!", - "queues": "Colas", - "queue-partitions": "Particiones", - "queue-submit-strategy": "Estrategia de envíos", - "queue-processing-strategy": "Estrategia de procesamiento", - "queue-configuration": "Configuración Cola", - "repository-settings": "Ajustes del repositorio", - "repository": "Repositorio", - "repository-url": "URL del repositorio", - "repository-url-required": "Se requiere la URL del repositorio.", - "default-branch": "Nombre de rama por defecto", - "repository-read-only": "Sólo lectura", - "show-merge-commits": "Mostrar commits merge", - "authentication-settings": "Ajustes de autenticación", - "auth-method": "Método de autenticación", - "auth-method-username-password": "Usuario / Contraseña", - "auth-method-username-password-hint": "Los usuarios de GitHub deben usar access tokens con permisos de escritura en el repositorio.", - "auth-method-private-key": "Clave privada", - "password-access-token": "Contraseña / Tóken de acceso", - "change-password-access-token": "Cambiar contraseña / Tóken de acceso", - "private-key": "Clave privada", - "drop-private-key-file-or": "Arrastrar y soltar un fichero de llave privada o", - "passphrase": "Frase de contraseña", - "enter-passphrase": "Entrar frase de contraseña", - "change-passphrase": "Cambiar frase de contraseña", - "check-access": "Verificar acceso", - "check-repository-access-success": "Acceso al repositorio verificado satisfactoriamente!", - "delete-repository-settings-title": "Estás seguro de borrar los ajustes del repositorio?", - "delete-repository-settings-text": "Atención, tras la confirmación los ajustes del repositorio serán eliminados y la característica de control de versiones no estará disponible.", - "auto-commit-settings": "Ajustes Auto-commit", - "auto-commit": "Auto-commit", - "auto-commit-entities": "Entidades Auto-commit", - "no-auto-commit-entities-prompt": "No hay entidades configuradas para auto-publicar", - "delete-auto-commit-settings-title": "Estás seguro de borrar los ajustes de auto-publicar?", - "delete-auto-commit-settings-text": "Atención, tras la confirmación los ajustes de auto-publicar serán borrados y la característica de auto-publicar se desactivará para todas las entidades.", - "2fa": { - "2fa": "Autenticación de dos factores (2FA)", - "available-providers": "Proveedores disponibles", - "issuer-name": "Nombre de emisor", - "issuer-name-required": "Nombre de emisor requerido.", - "max-verification-failures-before-user-lockout": "Máximo de fallos de verificación antes de bloquear cuenta", - "max-verification-failures-before-user-lockout-pattern": "Máximo de fallos debe ser un número entero positivo.", - "number-of-checking-attempts": "Número de intentos de verificación", - "number-of-checking-attempts-pattern": "Número de intentos debe ser un número entero positivo.", - "number-of-checking-attempts-required": "Número de intentos requerido.", - "number-of-codes": "Número de códigos", - "number-of-codes-pattern": "Número de códigos debe ser un número entero positivo.", - "number-of-codes-required": "Número de códigos requerido.", - "provider": "Proveedor", - "retry-verification-code-period": "Reintentos de código de verificación (sec)", - "retry-verification-code-period-pattern": "El período mínimo es de 5 sec", - "retry-verification-code-period-required": "Reintentos requerido.", - "total-allowed-time-for-verification": "Total de tiempo permitido para verificación (sec)", - "total-allowed-time-for-verification-pattern": "El mínimo del total de tiempo perminito es de 60 sec", - "total-allowed-time-for-verification-required": "Total de tiempo requerido.", - "use-system-two-factor-auth-settings": "Usar ajustes 2FA del sistema", - "verification-code-check-rate-limit": "Límite de chequeo del código de verificación", - "verification-code-lifetime": "Tiempo de vida del código de verificación (sec)", - "verification-code-lifetime-pattern": "Tiempo de vida del código de verificación debe ser un número entero positivo.", - "verification-code-lifetime-required": "Tiempo de vida requerido.", - "verification-message-template": "Plantilla del mensaje de verificación", - "verification-limitations": "Límites de verificación", - "verification-message-template-pattern": "El mensaje de verificación debe contener el patrón: ${code}", - "verification-message-template-required": "Plantilla de mensaje de verificación requerida.", - "within-time": "Rango de tiempo (sec)", - "within-time-pattern": "Tiempo debe ser un número entero positivo.", - "within-time-required": "Tiempo requerido." - }, - "jwt": { - "security-settings": "Ajustes de seguridad JWT", - "issuer-name": "Nombre del emisor", - "issuer-name-required": "Se requiere nombre del emisor.", - "signings-key": "Clave de firma", - "signings-key-hint": "Una string codificada en Base64 representando por lo menos 512 bits de datos.", - "signings-key-required": "Se requiere clave de firma.", - "signings-key-min-length": "La clave de firma debe tener al menos 512 bits de datos.", - "signings-key-base64": "La clave de firma debe estar en formato base64.", - "expiration-time": "Caducidad del token (en segundos)", - "expiration-time-required": "Se requiere caducidad del token.", - "expiration-time-min": "El tiempo mínimo debe ser al menos de 60 segundos (1 minuto).", - "refresh-expiration-time": "Caducidad del token de actualización (en segundos)", - "refresh-expiration-time-required": "Se requiere especificar caducidad del token de actualización.", - "refresh-expiration-time-min": "El tiempo mínimo es de 900 segundos (15 minutos).", - "refresh-expiration-time-less-token": "El tiempo de actualización debe ser mayor al de caducidad.", - "generate-key": "Generar clave", - "info-header": "Todos los usuarios necesitarán volver a logearse en la plataforma", - "info-message": "El cambio en la clave de firma del JWT causará que todos los tokens emitidos sean inválidos. Todos los usuarios deberán hacer login de nuevo. Esto también afectará a los scripts que usen la API REST o Websockets." - }, - "resources": "Recursos", - "notifications": "Notificaciones", - "notifications-settings": "Ajustes de notificaciones", - "slack-api-token": "Token de API Slack", - "slack": "Slack", - "slack-settings": "Ajustes de Slack" - }, - "alarm": { - "alarm": "Alarma", - "alarms": "Alarmas", - "all-alarms": "Todas las alarmas", - "select-alarm": "Seleccionar alarma", - "no-alarms-matching": "No se han encontrado alarmas coincidentes con '{{entity}}' .", - "alarm-required": "Alarma requerida", - "alarm-filter": "Filtro de alarma", - "filter": "Filtro", - "alarm-status": "Estado de Alarma", - "alarm-status-list": "Lista de estados de Alarmas", - "any-status": "Cualquier estado", - "search-status": { - "ANY": "Todas", - "ACTIVE": "Activas", - "CLEARED": "Borradas", - "ACK": "Reconocidas", - "UNACK": "Ignoradas" - }, - "display-status": { - "ACTIVE_UNACK": "Activa No reconocida", - "ACTIVE_ACK": "Activa Reconocida", - "CLEARED_UNACK": "Normalizada no reconocida", - "CLEARED_ACK": "Normalizada reconocida" - }, - "no-alarms-prompt": "No se encontraron alarmas", - "created-time": "Hora de creación", - "type": "Tipo", - "severity": "Gravedad", - "originator": "Origen", - "originator-type": "Tipo de origen", - "details": "Detalles", - "originator-label": "Etiqueta de autor", - "assign": "Asignar", - "assignments": "Asignaciones", - "assignee": "Asignado", - "assignee-id": "ID de asignado", - "assignee-first-name": "Nombre de asignado", - "assignee-last-name": "Apellido de asignado", - "assignee-email": "Email del Assignee email", - "unassigned": "Sin asignar", - "assignee-not-set": "Todas", - "status": "Estado", - "alarm-details": "Detalles Alarma", - "start-time": "Hora de inicio", - "assign-time": "Hora de asignación", - "end-time": "Hora fin", - "ack-time": "Hora de reconocimiento", - "clear-time": "Hora de normalización", - "duration": "Duración", - "alarm-severity-list": "Lista de gravedad de alarmas", - "any-severity": "Cualquier gravedad", - "severity-critical": "Crítica", - "severity-major": "Mayor", - "severity-minor": "Menor", - "severity-warning": "Peligro", - "severity-indeterminate": "Indeterminada", - "acknowledge": "Reconocer", - "clear": "Normalizar", - "delete": "Borrar", - "search": "Buscar alarmas", - "selected-alarms": "{ count, plural, =1 {1 alarma} other {# alarmas} } seleccionadas", - "no-data": "Sin datos que mostrar", - "polling-interval": "Ciclo de refresco de alarmas (seg)", - "polling-interval-required": "Se requiere un ciclo de refresco válido.", - "min-polling-interval-message": "El ciclo debe ser por lo menos de 1 segundo.", - "aknowledge-alarms-title": "Reconocer { count, plural, =1 {1 alarma} other {# alarmas} }", - "aknowledge-alarms-text": "Estas seguro de reconocer { count, plural, =1 {1 alarma} other {# alarmas} }?", - "aknowledge-alarm-title": "Recononcer Alarma", - "aknowledge-alarm-text": "Estas seguro de reconocer Alarma?", - "selected-alarms-are-acknowledged": "Las alarmas seleccionadas ya están reconocidas", - "clear-alarms-title": "Normalizar { count, plural, =1 {1 alarma} other {# alarmas} }", - "clear-alarms-text": "Limpiar { count, plural, =1 {1 alarma} other {# alarmas} }?", - "clear-alarm-title": "Limpiar Alarma", - "clear-alarm-text": "Limpiar Alarma?", - "delete-alarms-title": "Delete { count, plural, =1 {1 alarm} other {# alarms} }", - "delete-alarms-text": "Are you sure you want to delete { count, plural, =1 {1 alarm} other {# alarms} }?", - "selected-alarms-are-cleared": "Selected alarms are already cleared", - "alarm-status-filter": "Filtro de estados de Alarmas", - "alarm-filter-title": "Filtros de alarmas", - "assigned": "Asignadas", - "filter-title": "Filtrar", - "max-count-load": "Número máximo de alarmas a cargar (0 - ilimitado)", - "max-count-load-required": "Se requiere número máximo de alarmas.", - "max-count-load-error-min": "El valor mínimo es 0.", - "fetch-size": "Tamaño de búsqueda (Fetch)", - "fetch-size-required": "Se requiere tamaño de búsqueda.", - "fetch-size-error-min": "El valor mínimo es 10.", - "alarm-type-list": "Lista de tipos de alarma", - "any-type": "Cualquier tipo", - "assigned-to-current-user": "Asignadas al ususario actual", - "assigned-to-me": "Asignadas a mí", - "search-propagated-alarms": "Buscar alarmas propagadas", - "comments": "Comentarios de alarma", - "show-more": "Mostrar más", - "additional-info": "Información adicional", - "alarm-type": "Tipo de alarma", - "enter-alarm-type": "Introduce tipo de alarma", - "no-alarm-types-matching": "No se han encontrado tipos de alarma que coincidan con '{{entitySubtype}}'.", - "alarm-type-list-empty": "No se han seleccionado tipos de alarma." + "2fa" : { + "2fa" : "Autenticación de dos factores", + "available-providers" : "Proveedores disponibles", + "issuer-name" : "Nombre del emisor", + "issuer-name-required" : "Se requiere el nombre del emisor.", + "max-verification-failures-before-user-lockout" : "Máximo de fallos de verificación antes de bloquear al usuario", + "max-verification-failures-before-user-lockout-pattern" : "El número máximo de fallos de verificación debe ser un número entero positivo.", + "number-of-checking-attempts" : "Número de intentos de verificación", + "number-of-checking-attempts-pattern" : "El número de intentos debe ser un número entero positivo.", + "number-of-checking-attempts-required" : "Se requiere el número de intentos de verificación.", + "number-of-codes" : "Número de códigos", + "number-of-codes-pattern" : "El número de códigos debe ser un número entero positivo.", + "number-of-codes-required" : "Se requiere el número de códigos.", + "provider" : "Proveedor", + "retry-verification-code-period" : "Periodo de reintento del código de verificación (seg)", + "retry-verification-code-period-pattern" : "El tiempo mínimo del periodo es de 5 segundos", + "retry-verification-code-period-required" : "Se requiere el periodo de reintento del código de verificación.", + "total-allowed-time-for-verification" : "Tiempo total permitido para la verificación (seg)", + "total-allowed-time-for-verification-pattern" : "El tiempo total permitido debe ser al menos 60 segundos", + "total-allowed-time-for-verification-required" : "Se requiere el tiempo total permitido.", + "use-system-two-factor-auth-settings" : "Usar configuración del sistema de autenticación en dos pasos", + "verification-code-check-rate-limit" : "Límite de velocidad de comprobación del código de verificación", + "verification-code-lifetime" : "Duración del código de verificación (seg)", + "verification-code-lifetime-pattern" : "La duración debe ser un número entero positivo.", + "verification-code-lifetime-required" : "Se requiere la duración del código de verificación.", + "verification-message-template" : "Plantilla del mensaje de verificación", + "verification-limitations" : "Limitaciones de verificación", + "verification-message-template-pattern" : "El mensaje de verificación debe contener el patrón: ${code}", + "verification-message-template-required" : "Se requiere la plantilla del mensaje de verificación.", + "within-time" : "Dentro del tiempo (seg)", + "within-time-pattern" : "El tiempo debe ser un número entero positivo.", + "within-time-required" : "Se requiere el tiempo." }, - "alarm-activity": { - "add": "Añadir comentario...", - "alarm-comment": "Comentario", - "comments": "Commentarios", - "delete-alarm-comment": "Quieres borrar este comentario?", - "refresh": "Actualizar", - "oldest-first": "Antíguos primero", - "newest-first": "Nuevos primero", - "activity": "Actividad", - "export": "Exportar a CSV", - "author": "Autor", - "created-date": "Fecha de creación", - "edited-date": "Fecha de edición", - "text": "Texto", - "system": "Sistema" + "jwt" : { + "security-settings" : "Configuración de seguridad JWT", + "issuer-name" : "Nombre del emisor", + "issuer-name-required" : "Se requiere el nombre del emisor.", + "signings-key" : "Clave de firma", + "signings-key-hint" : "Cadena codificada en Base64 que representa al menos 512 bits de datos.", + "signings-key-required" : "Se requiere la clave de firma.", + "signings-key-min-length" : "La clave de firma debe tener al menos 512 bits de datos.", + "signings-key-base64" : "La clave de firma debe estar en formato base64.", + "expiration-time" : "Tiempo de expiración del token (seg)", + "expiration-time-required" : "Se requiere el tiempo de expiración del token.", + "expiration-time-max" : "El tiempo máximo permitido es de 2147483647 segundos (68 años).", + "expiration-time-min" : "El tiempo mínimo es de 60 segundos (1 minuto).", + "refresh-expiration-time" : "Tiempo de expiración del token de actualización (seg)", + "refresh-expiration-time-required" : "Se requiere el tiempo de expiración del token de actualización.", + "refresh-expiration-time-max" : "El tiempo máximo permitido es de 2147483647 segundos (68 años).", + "refresh-expiration-time-min" : "El tiempo mínimo es de 900 segundos (15 minutos).", + "refresh-expiration-time-less-token" : "El tiempo del token de actualización debe ser mayor que el del token.", + "generate-key" : "Generar clave", + "info-header" : "Todos los usuarios tendrán que volver a iniciar sesión", + "info-message" : "El cambio de la clave de firma JWT invalidará todos los tokens emitidos. Todos los usuarios tendrán que volver a iniciar sesión. Esto también afectará a los scripts que utilizan la API Rest/Websockets." }, - "alias": { - "add": "Añadir alias", - "edit": "Editar alias", - "name": "Nombre de Alias", - "name-required": "Se requiere nombre de alias", - "duplicate-alias": "Ya existe ese nombre de alias.", - "filter-type-single-entity": "Única entidad", - "filter-type-entity-list": "Lista de entidades", - "filter-type-entity-name": "Nombre de entidad", - "filter-type-entity-type": "Tipo de entidad", - "filter-type-state-entity": "Entidad desde estado de panel", - "filter-type-state-entity-description": "Entidad tomada de los parámetros de panel", - "filter-type-asset-type": "Tipo de activo", - "filter-type-asset-type-description": "Activos del tipo '{{assetTypes}}'", - "filter-type-asset-type-and-name-description": "Activos del tipo '{{assetTypes}}' y cuyo nombre comience por '{{prefix}}'", - "filter-type-device-type": "Tipo de dispositivo", - "filter-type-device-type-description": "Dispositivos de tipo '{{deviceTypes}}'", - "filter-type-device-type-and-name-description": "Dispositivos de tipo '{{deviceTypes}}' y cuyo nombre comience por '{{prefix}}'", - "filter-type-entity-view-type": "Tipo de vista de entidad", - "filter-type-entity-view-type-description": "Vistas de entidad del tipo '{{entityViewTypes}}'", - "filter-type-entity-view-type-and-name-description": "Vistas de entidad del tipo '{{entityViewTypes}}' y cuyo nombre comience por '{{prefix}}'", - "filter-type-edge-type": "Tipo de Edge", - "filter-type-edge-type-description": "Edges del tipo '{{edgeTypes}}'", - "filter-type-edge-type-and-name-description": "Vistas de entidad del tipo '{{edgeTypes}}' y cuyo nombre comience por '{{prefix}}'", - "filter-type-relations-query": "Consulta de relaciones", - "filter-type-relations-query-description": "{{entities}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", - "filter-type-asset-search-query": "Búsqueda de activos", - "filter-type-asset-search-query-description": "Activos con tipos {{assetTypes}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", - "filter-type-device-search-query": "Búsqueda de dispositivos", - "filter-type-device-search-query-description": "Dispositivos con tipos {{deviceTypes}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", - "filter-type-entity-view-search-query": "Consulta de búsqueda de vista de entidad", - "filter-type-entity-view-search-query-description": "Vistas de entidad con tipos {{entityViewTypes}} que tienen tipo de relación {{relationType}} con dirección {{direction}} {{rootEntity}}", - "filter-type-apiUsageState": "Uso de API", - "filter-type-edge-search-query": "Consultar búsqueda de Edge", - "filter-type-edge-search-query-description": "Edges con tipos {{edgeTypes}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", - "entity-filter": "Filtro de entidad", - "resolve-multiple": "Resolver como múltiples entidades", - "filter-type": "Tipo de filtro", - "filter-type-required": "Se requiere tipo de filtro.", - "entity-filter-no-entity-matched": "No se encontraron entidades que coincidan con el filtro especificado.", - "no-entity-filter-specified": "No se ha especificado filtro de entidad", - "root-state-entity": "Usar entidad de estado del panel como raíz", - "last-level-relation": "Obtener sólo el último nivel de relación", - "root-entity": "Entidad raíz", - "state-entity-parameter-name": "Parámetro de estado de entidad", - "default-state-entity": "Entidad de estado por defecto", - "default-entity-parameter-name": "Por defecto", - "max-relation-level": "Máximo nivel de relación", - "unlimited-level": "Nivel ilimitado", - "state-entity": "Entidad de estado del panel", - "all-entities": "Todas las entidades", - "any-relation": "cualquiera" + "resources" : "Recursos", + "notifications" : "Notificaciones", + "notifications-settings" : "Configuración de notificaciones", + "slack-api-token" : "Token de API de Slack", + "slack" : "Slack", + "slack-settings" : "Configuración de Slack", + "mobile-settings" : "Configuración móvil", + "firebase-service-account-file" : "Archivo JSON de credenciales de cuenta de servicio de Firebase", + "select-firebase-service-account-file" : "Arrastra y suelta tu archivo de credenciales de cuenta de servicio de Firebase o " + }, + "alarm" : { + "alarm" : "Alarma", + "alarms" : "Alarmas", + "all-alarms" : "Todas las alarmas", + "select-alarm" : "Seleccionar alarma", + "no-alarms-matching" : "No se encontraron alarmas que coincidan con '{{entity}}'.", + "alarm-required" : "Se requiere una alarma", + "alarm-filter" : "Filtro de alarma", + "filter" : "Filtro", + "alarm-status" : "Estado de la alarma", + "alarm-status-list" : "Lista de estados de alarma", + "any-status" : "Cualquier estado", + "search-status" : { + "ANY" : "Cualquiera", + "ACTIVE" : "Activa", + "CLEARED" : "Despejada", + "ACK" : "Reconocida", + "UNACK" : "No reconocida" }, - "asset": { - "asset": "Activo", - "assets": "Activos", - "management": "Gestión de Activos", - "view-assets": "Ver Activos", - "add": "Añadir Activo", - "asset-type-max-length": "El tipo de activo debe ser menor de 256", - "assign-to-customer": "Asignar a cliente", - "assign-asset-to-customer": "Asignar Activo(s) A Cliente", - "assign-asset-to-customer-text": "Selecciona los activos a asignar al cliente", - "no-assets-text": "No se encontraron activos", - "assign-to-customer-text": "Selecciona el cliente al que asignar los activos", - "public": "Público", - "assignedToCustomer": "Asignado a cliente", - "make-public": "Hacer activo público", - "make-private": "Hacer activo privado", - "unassign-from-customer": "Cancelar la asignación de activo del cliente", - "delete": "Borrar activo", - "asset-public": "El activo es público", - "asset-type": "Tipo de activo", - "asset-type-required": "Se requiere tipo de activo.", - "select-asset-type": "Selecciona tipo de activo", - "enter-asset-type": "Entrar tipo de activo", - "any-asset": "Cualquier activo", - "no-asset-types-matching": "No se han encontrado activos coincidiendo con '{{entitySubtype}}' .", - "asset-type-list-empty": "No hay ningun tipo de activo seleccionado.", - "asset-types": "Tipos de activo", - "name": "Nombre", - "name-required": "Nombre requerido.", - "name-max-length": "El nombre debe tener menos de 256", - "label-max-length": "La etiqueta debe tener menos de 256", - "description": "Descripción", - "type": "Tipo", - "type-required": "Tipo requerido.", - "details": "Detalles", - "events": "Eventos", - "add-asset-text": "Añadir nuevo activo", - "asset-details": "Detalles de activo", - "assign-assets": "Asignar activos", - "assign-assets-text": "Asignar { count, plural, =1 {1 activo} other {# activos} } a cliente", - "assign-asset-to-edge-title": "Asignar activo(s) al Edge", - "assign-asset-to-edge-text": "Por favor, selecciona lo activos a asignar al Edge", - "delete-assets": "Borrar activos", - "unassign-assets": "Cancelar asignación de activo", - "unassign-assets-action-title": "Cancelar asignación de { count, plural, =1 {1 activo} other {# activos} } del cliente", - "assign-new-asset": "Asignar nuevo activo", - "delete-asset-title": "Eliminar el activo '{{assetName}}'?", - "delete-asset-text": "Atención, tras la confirmación el activo y sus datos serán borrados e irrecuperables.", - "delete-assets-title": "Eliminar los activos { count, plural, =1 {1 activo} other {# activos} }?", - "delete-assets-action-title": "Borrar { count, plural, =1 {1 activo} other {# activos} }", - "delete-assets-text": "Atención, tras la confirmación todos los activos seleccionados y sus datos serán borrados e irrecuperables.", - "make-public-asset-title": "Hacer el activo '{{assetName}}' público?", - "make-public-asset-text": "Tras la confirmación, el activo y sus datos se harán públicos y accesibles por otros.", - "make-private-asset-title": "Hacer el activo '{{assetName}}' privado?", - "make-private-asset-text": "Tras la confirmación, el activo y sus datos se harán privados y no serán accesibles por otros.", - "unassign-asset-title": "Cancelar la asignación del activo '{{assetName}}'?", - "unassign-asset-text": "Tras la confirmación, el activo será desasignado y no será accesible por el cliente.", - "unassign-asset": "Cancelar asignación de activo", - "unassign-assets-title": "Cancelar las asignaciones { count, plural, =1 {1 activo} other {# activos} }?", - "unassign-assets-text": "Tras la confirmación todos los activos seleccionados serán desasignados y no serán accesibles por el cliente.", - "unassign-assets-from-edge": "Anular activos de Edge", - "copyId": "Copiar ID de activo", - "idCopiedMessage": "El ID ha sido copiado al portapapeles", - "select-asset": "Seleccionar activo", - "no-assets-matching": "No se han encontrado activos que coincidan con '{{entity}}' .", - "asset-required": "Nombre de activo requerido", - "name-starts-with": "El nombre de activo comienza con", - "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%nombre_activo_contiene%', '%nombre_activo_acaba_en', 'nombre_activo_comienza_con'.", - "import": "Importar activos", - "asset-file": "Archivo del activo", - "label": "Etiqueta", - "search": "Buscar activos", - "assign-asset-to-edge": "Asignar activo(s) al Edge", - "unassign-asset-from-edge": "Anular activo de Edge", - "unassign-asset-from-edge-title": "¿Está seguro de que desea desasignar el activo '{{assetName}}'?", - "unassign-asset-from-edge-text": "Después de la confirmación, el activo no será asignado y el Edge no podrá acceder a él", - "unassign-assets-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, =1 {1 activo} other {# activos} }?", - "unassign-assets-from-edge-text": "Después de la confirmación, todos los activos seleccionados quedarán sin asignar y el Edge no podrá acceder a ellos.", - "selected-assets": "{ count, plural, =1 {1 activo} other {# activos} } seleccionados" + "display-status" : { + "ACTIVE_UNACK" : "Activa no reconocida", + "ACTIVE_ACK" : "Activa reconocida", + "CLEARED_UNACK" : "Despejada no reconocida", + "CLEARED_ACK" : "Despejada reconocida" }, - "attribute": { - "attributes": "Atributos", - "latest-telemetry": "Última telemetría", - "no-latest-telemetry": "No hay última telemetría", - "attributes-scope": "Alcance de los atributos del dispositivo", - "scope-telemetry": "Telemetría", - "scope-latest-telemetry": "Última telemetría", - "scope-client": "Atributos de Cliente", - "scope-server": "Atributos de Servidor", - "scope-shared": "Atributos Compartidos", - "add": "Agregar atributo", - "key": "Clave", - "key-max-length": "La clave debe ser menor de 256", - "last-update-time": "Hora de última actualización", - "key-required": "Clave del atributo requerida.", - "value": "Valor", - "value-required": "Valor del atributo requerido.", - "telemetry-key-required": "Se requiere clave de telemetría", - "telemetry-value-required": "Se requiere valor Telemetry value is required", - "delete-attributes-title": "¿Eliminar { count, plural, =1 {1 atributo} other {# atributos} }?", - "delete-attributes-text": "Atención, tras la confirmación el atributo será eliminado, y la información relacionada será irrecuperable.", - "delete-attributes": "Borrar atributo", - "enter-attribute-value": "Ingresar valor del atributo", - "show-on-widget": "Mostrar en Widget", - "widget-mode": "Widget", - "next-widget": "Widget siguiente", - "prev-widget": "Widget anterior", - "add-to-dashboard": "Agregar al Panel", - "add-widget-to-dashboard": "Agregar widget al Panel", - "selected-attributes": "{ count, plural, =1 {1 atributo} other {# atributos} } seleccionados", - "selected-telemetry": "{ count, plural, =1 {1 telemetría} other {# telemetrías} } seleccionadas", - "no-attributes-text": "No se encontró ningún atributo", - "no-telemetry-text": "No se encontró ninguna telemetría", - "copy-key": "Copiar clave", - "add-telemetry": "Añadir telemetría", - "copy-value": "Copiar valor", - "delete-timeseries": { - "start-time": "Hora de inicio", - "ends-on": "Fin", - "strategy": "Estrategia", - "delete-strategy": "Estrategia de borrado", - "all-data": "Borrar todos los datos", - "all-data-except-latest-value": "Borrar todos los datos salvo los últimos valores", - "latest-value": "Borrar último valor", - "all-data-for-time-period": "Borrar todos los datos del periodo seleccionado", - "rewrite-latest-value": "Reescribir último valor" - } + "no-alarms-prompt" : "No se encontraron alarmas", + "created-time" : "Hora de creación", + "type" : "Tipo", + "severity" : "Severidad", + "originator" : "Originador", + "originator-type" : "Tipo de originador", + "details" : "Detalles", + "originator-label" : "Etiqueta del originador", + "assign" : "Asignar", + "assignments" : "Asignaciones", + "assignee" : "Asignado", + "assignee-id" : "ID del asignado", + "assignee-first-name" : "Nombre del asignado", + "assignee-last-name" : "Apellido del asignado", + "assignee-email" : "Correo del asignado", + "unassigned" : "No asignado", + "user-deleted" : "Usuario eliminado", + "assignee-not-set" : "Todos", + "status" : "Estado", + "alarm-details" : "Detalles de la alarma", + "start-time" : "Hora de inicio", + "assign-time" : "Hora de asignación", + "end-time" : "Hora de finalización", + "ack-time" : "Hora de reconocimiento", + "clear-time" : "Hora de despeje", + "duration" : "Duración", + "alarm-severity" : "Severidad de la alarma", + "alarm-severity-list" : "Lista de severidades de alarma", + "any-severity" : "Cualquier severidad", + "severity-critical" : "Crítica", + "severity-major" : "Mayor", + "severity-minor" : "Menor", + "severity-warning" : "Advertencia", + "severity-indeterminate" : "Indeterminada", + "acknowledge" : "Reconocer", + "clear" : "Despejar", + "delete" : "Eliminar", + "search" : "Buscar alarmas", + "selected-alarms" : "{ count, plural, =1 {1 alarma} other {# alarmas} } seleccionadas", + "no-data" : "No hay datos para mostrar", + "polling-interval" : "Intervalo de sondeo de alarmas (seg)", + "polling-interval-required" : "Se requiere el intervalo de sondeo de alarmas.", + "min-polling-interval-message" : "Se permite un intervalo mínimo de sondeo de 1 segundo.", + "aknowledge-alarms-title" : "Reconocer { count, plural, =1 {1 alarma} other {# alarmas} }", + "aknowledge-alarms-text" : "¿Estás seguro de que deseas reconocer { count, plural, =1 {1 alarma} other {# alarmas} }?", + "aknowledge-alarm-title" : "Reconocer alarma", + "aknowledge-alarm-text" : "¿Estás seguro de que deseas reconocer la alarma?", + "selected-alarms-are-acknowledged" : "Las alarmas seleccionadas ya están reconocidas", + "clear-alarms-title" : "Despejar { count, plural, =1 {1 alarma} other {# alarmas} }", + "clear-alarms-text" : "¿Estás seguro de que deseas despejar { count, plural, =1 {1 alarma} other {# alarmas} }?", + "clear-alarm-title" : "Despejar alarma", + "clear-alarm-text" : "¿Estás seguro de que deseas despejar la alarma?", + "delete-alarms-title" : "Eliminar { count, plural, =1 {1 alarma} other {# alarmas} }", + "delete-alarms-text" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 alarma} other {# alarmas} }?", + "selected-alarms-are-cleared" : "Las alarmas seleccionadas ya están despejadas", + "alarm-status-filter" : "Filtro de estado de alarma", + "alarm-filter-title" : "Filtro de alarma", + "assigned" : "Asignado", + "filter-title" : "Filtro", + "max-count-load" : "Número máximo de alarmas a cargar (0 - ilimitado)", + "max-count-load-required" : "Se requiere el número máximo de alarmas a cargar.", + "max-count-load-error-min" : "El valor mínimo es 0.", + "fetch-size" : "Tamaño de carga", + "fetch-size-required" : "Se requiere el tamaño de carga.", + "fetch-size-error-min" : "El valor mínimo es 10.", + "alarm-types" : "Tipos de alarma", + "alarm-type-list" : "Lista de tipos de alarma", + "any-type" : "Cualquier tipo", + "assigned-to-current-user" : "Asignadas al usuario actual", + "assigned-to-me" : "Asignadas a mí", + "search-propagated-alarms" : "Buscar alarmas propagadas", + "comments" : "Comentarios de la alarma", + "show-more" : "Mostrar más", + "additional-info" : "Información adicional", + "alarm-type" : "Tipo de alarma", + "enter-alarm-type" : "Introducir tipo de alarma", + "no-alarm-types-matching" : "No se encontraron tipos de alarma que coincidan con '{{entitySubtype}}'.", + "alarm-type-list-empty" : "No se han seleccionado tipos de alarma." + }, + "alarm-activity" : { + "add" : "Agregar un comentario...", + "alarm-comment" : "Comentario de la alarma", + "comments" : "Comentarios", + "delete-alarm-comment" : "¿Deseas eliminar este comentario?", + "refresh" : "Actualizar", + "oldest-first" : "Más antiguos primero", + "newest-first" : "Más recientes primero", + "activity" : "Actividad", + "export" : "Exportar a CSV", + "author" : "Autor", + "created-date" : "Fecha de creación", + "edited-date" : "Fecha de edición", + "text" : "Texto", + "system" : "Sistema" + }, + "alias" : { + "add" : "Agregar alias", + "edit" : "Editar alias", + "name" : "Nombre del alias", + "name-required" : "Se requiere el nombre del alias", + "duplicate-alias" : "Ya existe un alias con el mismo nombre.", + "filter-type-single-entity" : "Entidad única", + "filter-type-entity-list" : "Lista de entidades", + "filter-type-entity-name" : "Nombre de la entidad", + "filter-type-entity-type" : "Tipo de entidad", + "filter-type-state-entity" : "Entidad del estado del tablero", + "filter-type-state-entity-description" : "Entidad tomada de los parámetros del estado del tablero", + "filter-type-asset-type" : "Tipo de activo", + "filter-type-asset-type-description" : "Activos del tipo '{{assetTypes}}'", + "filter-type-asset-type-and-name-description" : "Activos del tipo '{{assetTypes}}' con nombre que comienza con '{{prefix}}'", + "filter-type-device-type" : "Tipo de dispositivo", + "filter-type-device-type-description" : "Dispositivos del tipo '{{deviceTypes}}'", + "filter-type-device-type-and-name-description" : "Dispositivos del tipo '{{deviceTypes}}' con nombre que comienza con '{{prefix}}'", + "filter-type-entity-view-type" : "Tipo de vista de entidad", + "filter-type-entity-view-type-description" : "Vistas de entidad del tipo '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description" : "Vistas de entidad del tipo '{{entityViewTypes}}' con nombre que comienza con '{{prefix}}'", + "filter-type-edge-type" : "Tipo de Edge", + "filter-type-edge-type-description" : "Edges del tipo '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description" : "Edges del tipo '{{edgeTypes}}' con nombre que comienza con '{{prefix}}'", + "filter-type-relations-query" : "Consulta de relaciones", + "filter-type-relations-query-description" : "{{entities}} que tienen una relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-edge-search-query" : "Consulta de búsqueda de Edge", + "filter-type-edge-search-query-description" : "Edges de tipo {{edgeTypes}} que tienen una relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query" : "Consulta de búsqueda de activos", + "filter-type-asset-search-query-description" : "Activos de tipo {{assetTypes}} con relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query" : "Consulta de búsqueda de dispositivos", + "filter-type-device-search-query-description" : "Dispositivos de tipo {{deviceTypes}} con relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query" : "Consulta de búsqueda de vistas de entidad", + "filter-type-entity-view-search-query-description" : "Vistas de entidad de tipo {{entityViewTypes}} con relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState" : "Estado de uso de la API", + "entity-filter" : "Filtro de entidad", + "resolve-multiple" : "Resolver como múltiples entidades", + "resolve-multiple-hint" : "Activa esta opción para mostrar datos de todas las entidades filtradas simultáneamente.\nSi se desactiva, el widget solo mostrará datos de la entidad seleccionada.", + "filter-type" : "Tipo de filtro", + "filter-type-required" : "Se requiere el tipo de filtro.", + "entity-filter-no-entity-matched" : "No se encontraron entidades que coincidan con el filtro especificado.", + "no-entity-filter-specified" : "No se especificó ningún filtro de entidad", + "root-state-entity" : "Usar entidad del estado del tablero como raíz", + "last-level-relation" : "Obtener solo relaciones del último nivel", + "root-entity" : "Entidad raíz", + "state-entity-parameter-name" : "Nombre del parámetro de entidad del estado", + "default-state-entity" : "Entidad de estado predeterminada", + "default-entity-parameter-name" : "Por defecto", + "max-relation-level" : "Nivel máximo de relación", + "unlimited-level" : "Nivel ilimitado", + "state-entity" : "Entidad del estado del tablero", + "all-entities" : "Todas las entidades", + "any-relation" : "cualquiera" + }, + "asset" : { + "asset" : "Activo", + "assets" : "Activos", + "management" : "Gestión de activos", + "view-assets" : "Ver activos", + "add" : "Agregar activo", + "asset-type-max-length" : "El tipo de activo debe tener menos de 256 caracteres", + "assign-to-customer" : "Asignar a cliente", + "assign-asset-to-customer" : "Asignar activo(s) al cliente", + "assign-asset-to-customer-text" : "Selecciona los activos que deseas asignar al cliente", + "no-assets-text" : "No se encontraron activos", + "assign-to-customer-text" : "Selecciona el cliente al que deseas asignar el/los activo(s)", + "public" : "Público", + "assignedToCustomer" : "Asignado a cliente", + "make-public" : "Hacer público el activo", + "make-private" : "Hacer privado el activo", + "unassign-from-customer" : "Desasignar del cliente", + "delete" : "Eliminar activo", + "asset-public" : "El activo es público", + "asset-type" : "Tipo de activo", + "asset-type-required" : "Se requiere el tipo de activo.", + "select-asset-type" : "Seleccionar tipo de activo", + "enter-asset-type" : "Introducir perfil de activo", + "any-asset" : "Cualquier activo", + "no-asset-types-matching" : "No se encontraron tipos de activo que coincidan con '{{entitySubtype}}'.", + "asset-type-list-empty" : "No se seleccionaron tipos de activo.", + "asset-types" : "Tipos de activo", + "name" : "Nombre", + "name-required" : "Se requiere el nombre.", + "name-max-length" : "El nombre debe tener menos de 256 caracteres", + "label-max-length" : "La etiqueta debe tener menos de 256 caracteres", + "description" : "Descripción", + "type" : "Tipo", + "type-required" : "Se requiere el tipo.", + "details" : "Detalles", + "events" : "Eventos", + "add-asset-text" : "Agregar nuevo activo", + "asset-details" : "Detalles del activo", + "assign-assets" : "Asignar activos", + "assign-assets-text" : "Asignar { count, plural, =1 {1 activo} other {# activos} } al cliente", + "assign-asset-to-edge-title" : "Asignar activo(s) a Edge", + "assign-asset-to-edge-text" : "Selecciona los activos que deseas asignar a Edge", + "delete-assets" : "Eliminar activos", + "unassign-assets" : "Desasignar activos", + "unassign-assets-action-title" : "Desasignar { count, plural, =1 {1 activo} other {# activos} } del cliente", + "assign-new-asset" : "Asignar nuevo activo", + "delete-asset-title" : "¿Estás seguro de que deseas eliminar el activo '{{assetName}}'?", + "delete-asset-text" : "Ten cuidado, después de la confirmación el activo y todos los datos relacionados serán irrecuperables.", + "delete-assets-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 activo} other {# activos} }?", + "delete-assets-action-title" : "Eliminar { count, plural, =1 {1 activo} other {# activos} }", + "delete-assets-text" : "Ten cuidado, después de la confirmación todos los activos seleccionados se eliminarán y todos los datos relacionados serán irrecuperables.", + "make-public-asset-title" : "¿Estás seguro de que deseas hacer público el activo '{{assetName}}'?", + "make-public-asset-text" : "Después de la confirmación, el activo y todos sus datos serán públicos y accesibles por otros.", + "make-private-asset-title" : "¿Estás seguro de que deseas hacer privado el activo '{{assetName}}'?", + "make-private-asset-text" : "Después de la confirmación, el activo y todos sus datos serán privados y no serán accesibles por otros.", + "unassign-asset-title" : "¿Estás seguro de que deseas desasignar el activo '{{assetName}}'?", + "unassign-asset-text" : "Después de la confirmación, el activo se desasignará y no será accesible por el cliente.", + "unassign-asset" : "Desasignar activo", + "unassign-assets-title" : "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 activo} other {# activos} }?", + "unassign-assets-text" : "Después de la confirmación, todos los activos seleccionados se desasignarán y no serán accesibles por el cliente.", + "copyId" : "Copiar ID del activo", + "idCopiedMessage" : "El ID del activo ha sido copiado al portapapeles", + "select-asset" : "Seleccionar activo", + "no-assets-matching" : "No se encontraron activos que coincidan con '{{entity}}'.", + "asset-required" : "Se requiere un activo", + "name-starts-with" : "Expresión del nombre del activo", + "help-text" : "Usa '%' según sea necesario: '%nombre_activo_contiene%', '%nombre_activo_termina', 'activo_empieza_con'.", + "search" : "Buscar activos", + "import" : "Importar activos", + "asset-file" : "Archivo de activos", + "label" : "Etiqueta", + "assign-asset-to-edge" : "Asignar activo(s) a Edge", + "unassign-asset-from-edge" : "Desasignar activo", + "unassign-asset-from-edge-title" : "¿Estás seguro de que deseas desasignar el activo '{{assetName}}'?", + "unassign-asset-from-edge-text" : "Después de la confirmación, el activo se desasignará y no será accesible por Edge.", + "unassign-assets-from-edge-title" : "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 activo} other {# activos} }?", + "unassign-assets-from-edge-text" : "Después de la confirmación, todos los activos seleccionados se desasignarán y no serán accesibles por Edge.", + "selected-assets" : "{ count, plural, =1 {1 activo} other {# activos} } seleccionado" + }, + "attribute" : { + "attributes" : "Atributos", + "latest-telemetry" : "Última telemetría", + "no-latest-telemetry" : "No se encontró última telemetría", + "attributes-scope" : "Alcance de atributos de entidad", + "scope-telemetry" : "Telemetría", + "scope-latest-telemetry" : "Última telemetría", + "scope-client" : "Atributos del cliente", + "scope-server" : "Atributos del servidor", + "scope-shared" : "Atributos compartidos", + "scope-client-short" : "Cliente", + "scope-server-short" : "Servidor", + "scope-shared-short" : "Compartido", + "scope-latest-short" : "Última", + "scope-any" : "Cualquiera", + "add" : "Agregar atributo", + "key" : "Clave", + "key-max-length" : "La clave debe tener menos de 256 caracteres", + "last-update-time" : "Última hora de actualización", + "key-required" : "Se requiere la clave del atributo.", + "value" : "Valor", + "value-required" : "Se requiere el valor del atributo.", + "telemetry-key-required" : "Se requiere la clave de telemetría", + "telemetry-value-required" : "Se requiere el valor de telemetría", + "delete-attributes-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 atributo} other {# atributos} }?", + "delete-attributes-text" : "Ten cuidado, después de la confirmación todos los atributos seleccionados se eliminarán.", + "delete-attributes" : "Eliminar atributos", + "enter-attribute-value" : "Ingresar valor del atributo", + "show-on-widget" : "Mostrar en el widget", + "widget-mode" : "Modo de widget", + "next-widget" : "Siguiente widget", + "prev-widget" : "Widget anterior", + "add-to-dashboard" : "Agregar al tablero", + "add-widget-to-dashboard" : "Agregar widget al tablero", + "selected-attributes" : "{ count, plural, =1 {1 atributo} other {# atributos} } seleccionado", + "selected-telemetry" : "{ count, plural, =1 {1 unidad de telemetría} other {# unidades de telemetría} } seleccionada", + "no-attributes-text" : "No se encontraron atributos", + "no-telemetry-text" : "No se encontró telemetría", + "copy-key" : "Copiar clave", + "add-telemetry" : "Agregar telemetría", + "copy-value" : "Copiar valor", + "delete-timeseries" : { + "start-time" : "Hora de inicio", + "ends-on" : "Finaliza en", + "strategy" : "Estrategia", + "delete-strategy" : "Estrategia de eliminación", + "all-data" : "Eliminar todos los datos", + "all-data-except-latest-value" : "Eliminar todos los datos excepto el último valor", + "latest-value" : "Eliminar el último valor", + "all-data-for-time-period" : "Eliminar todos los datos del período de tiempo", + "rewrite-latest-value" : "Reescribir el último valor" + } + }, + "api-usage" : { + "api-features" : "Características de la API", + "api-usage" : "Uso de la API", + "alarm" : "Alarma", + "alarms-created" : "Alarmas creadas", + "queue-stats" : "Estadísticas de la cola", + "processing-failures-and-timeouts" : "Fallos y tiempos de espera en el procesamiento", + "exceptions" : "Excepciones", + "alarms-created-daily-activity" : "Actividad diaria de alarmas creadas", + "alarms-created-hourly-activity" : "Actividad por hora de alarmas creadas", + "alarms-created-monthly-activity" : "Actividad mensual de alarmas creadas", + "data-points" : "Puntos de datos", + "data-points-storage-days" : "Días de almacenamiento de puntos de datos", + "device-api" : "API de dispositivo", + "email" : "Correo electrónico", + "email-messages" : "Mensajes de correo", + "email-messages-daily-activity" : "Actividad diaria de mensajes de correo", + "email-messages-monthly-activity" : "Actividad mensual de mensajes de correo", + "executions" : "Ejecuciones", + "scripts" : "Scripts", + "scripts-hourly-activity" : "Actividad por hora de scripts", + "scripts-daily-activity" : "Actividad diaria de scripts", + "scripts-monthly-activity" : "Actividad mensual de scripts", + "javascript" : "JavaScript", + "javascript-executions" : "Ejecuciones de JavaScript", + "tbel" : "TBEL", + "tbel-executions" : "Ejecuciones de TBEL", + "latest-error" : "Último error", + "messages" : "Mensajes", + "notifications" : "Notificaciones", + "notifications-email-sms" : "Notificaciones (Correo/SMS)", + "notifications-hourly-activity" : "Actividad por hora de notificaciones", + "permanent-failures" : "Fallos permanentes de ${entityName}", + "permanent-timeouts" : "Tiempos de espera permanentes de ${entityName}", + "processing-failures" : "Fallos de procesamiento de ${entityName}", + "processing-timeouts" : "Tiempos de espera de procesamiento de ${entityName}", + "rule-chain" : "Cadena de reglas", + "rule-engine" : "Motor de reglas", + "rule-engine-daily-activity" : "Actividad diaria del motor de reglas", + "rule-engine-executions" : "Ejecuciones del motor de reglas", + "rule-engine-hourly-activity" : "Actividad por hora del motor de reglas", + "rule-engine-monthly-activity" : "Actividad mensual del motor de reglas", + "rule-engine-statistics" : "Estadísticas del motor de reglas", + "rule-node" : "Nodo de regla", + "sms" : "SMS", + "sms-messages" : "Mensajes SMS", + "sms-messages-daily-activity" : "Actividad diaria de mensajes SMS", + "sms-messages-monthly-activity" : "Actividad mensual de mensajes SMS", + "successful" : "${entityName} exitoso", + "telemetry" : "Telemetría", + "telemetry-persistence" : "Persistencia de telemetría", + "telemetry-persistence-daily-activity" : "Actividad diaria de persistencia de telemetría", + "telemetry-persistence-hourly-activity" : "Actividad por hora de persistencia de telemetría", + "telemetry-persistence-monthly-activity" : "Actividad mensual de persistencia de telemetría", + "transport" : "Transporte", + "transport-daily-activity" : "Actividad diaria de transporte", + "transport-data-points" : "Puntos de datos de transporte", + "transport-hourly-activity" : "Actividad por hora de transporte", + "transport-messages" : "Mensajes de transporte", + "transport-monthly-activity" : "Actividad mensual de transporte", + "view-details" : "Ver detalles", + "view-statistics" : "Ver estadísticas" + }, + "api-limit" : { + "cassandra-queries" : "Consultas Cassandra", + "entity-version-creation" : "Creación de versión de entidad", + "entity-version-load" : "Carga de versión de entidad", + "notification-requests" : "Solicitudes de notificación", + "notification-requests-per-rule" : "Solicitudes de notificación por regla", + "rest-api-requests" : "Solicitudes de API REST", + "rest-api-requests-per-customer" : "Solicitudes de API REST por cliente", + "transport-messages" : "Mensajes de transporte", + "transport-messages-per-device" : "Mensajes de transporte por dispositivo", + "transport-messages-per-gateway" : "Mensajes de transporte por gateway", + "transport-messages-per-gateway-device" : "Mensajes de transporte por dispositivo de gateway", + "ws-updates-per-session" : "Actualizaciones WS por sesión", + "edge-events" : "Eventos de Edge", + "edge-events-per-edge" : "Eventos de Edge por Edge", + "edge-uplink-messages" : "Mensajes uplink de Edge", + "edge-uplink-messages-per-edge" : "Mensajes uplink de Edge por Edge" + }, + "audit-log" : { + "audit" : "Auditoría", + "audit-logs" : "Registros de auditoría", + "timestamp" : "Marca de tiempo", + "entity-type" : "Tipo de entidad", + "entity-name" : "Nombre de la entidad", + "user" : "Usuario", + "type" : "Tipo", + "status" : "Estado", + "details" : "Detalles", + "type-added" : "Agregado", + "type-deleted" : "Eliminado", + "type-updated" : "Actualizado", + "type-attributes-updated" : "Atributos actualizados", + "type-attributes-deleted" : "Atributos eliminados", + "type-rpc-call" : "Llamada RPC", + "type-credentials-updated" : "Credenciales actualizadas", + "type-assigned-to-customer" : "Asignado al cliente", + "type-unassigned-from-customer" : "Desasignado del cliente", + "type-assigned-to-edge" : "Asignado a Edge", + "type-unassigned-from-edge" : "Desasignado de Edge", + "type-activated" : "Activado", + "type-suspended" : "Suspendido", + "type-credentials-read" : "Credenciales leídas", + "type-attributes-read" : "Atributos leídos", + "type-relation-add-or-update" : "Relación actualizada", + "type-relation-delete" : "Relación eliminada", + "type-relations-delete" : "Todas las relaciones eliminadas", + "type-alarm-ack" : "Alarma reconocida", + "type-alarm-clear" : "Alarma despejada", + "type-alarm-delete" : "Alarma eliminada", + "type-alarm-assign" : "Alarma asignada", + "type-alarm-unassign" : "Alarma desasignada", + "type-added-comment" : "Comentario agregado", + "type-updated-comment" : "Comentario actualizado", + "type-deleted-comment" : "Comentario eliminado", + "type-login" : "Inicio de sesión", + "type-logout" : "Cierre de sesión", + "type-lockout" : "Bloqueo", + "status-success" : "Éxito", + "status-failure" : "Fallo", + "audit-log-details" : "Detalles del registro de auditoría", + "no-audit-logs-prompt" : "No se encontraron registros", + "action-data" : "Datos de la acción", + "failure-details" : "Detalles del fallo", + "search" : "Buscar registros de auditoría", + "clear-search" : "Borrar búsqueda", + "type-assigned-from-tenant" : "Asignado desde inquilino", + "type-assigned-to-tenant" : "Asignado a inquilino", + "type-provision-success" : "Dispositivo aprovisionado", + "type-provision-failure" : "El aprovisionamiento del dispositivo falló", + "type-timeseries-updated" : "Telemetría actualizada", + "type-timeseries-deleted" : "Telemetría eliminada", + "type-sms-sent" : "SMS enviado" + }, + "debug-settings" : { + "label" : "Configuración de depuración", + "on-failure" : "Solo fallos (24/7)", + "all-messages" : "Todos los mensajes ({{time}})", + "failures" : "Fallos", + "entity" : "entidad", + "hint" : { + "main-limited" : "No se registrarán más de {{msg}} mensajes de depuración de {{entity}} por {{time}}.", + "on-failure" : "Registrar solo mensajes de error.", + "all-messages" : "Registrar todos los mensajes de depuración." + } + }, + "calculated-fields" : { + "expression" : "Expresión", + "no-found" : "No se encontraron campos calculados", + "list" : "{ count, plural, =1 {Un campo calculado} other {Lista de # campos calculados} }", + "selected-fields" : "{ count, plural, =1 {1 campo calculado} other {# campos calculados} } seleccionado(s)", + "type" : { + "simple" : "Simple", + "script" : "Script" }, - "api-usage": { - "api-features": "Características API", - "api-usage": "Uso de API", - "alarm": "Alarma", - "alarms-created": "Alarmas creadas", - "alarms-created-daily-activity": "Actividad diaria de Alarmas creadas", - "alarms-created-hourly-activity": "Actividad horaria de Alarmas creadas", - "alarms-created-monthly-activity": "Actividad mensual de Alarmas creadas", - "data-points": "Puntos de datos", - "data-points-storage-days": "Días de guardado de puntos de datos", - "device-api": "API Dispositivos", - "email": "Email", - "email-messages": "Mensajes de Email", - "email-messages-daily-activity": "Actividad diaria de Emails", - "email-messages-monthly-activity": "Actividad mensual de Emails", - "exceptions": "Excepciones", - "executions": "Ejecuciones", - "javascript": "JavaScript", - "javascript-executions": "Ejecuciones JavaScript", - "latest-error": "Último error", - "messages": "Mensajes", - "notifications": "Notificaciones", - "notifications-email-sms": "Notificaciones (Email/SMS)", - "notifications-hourly-activity": "Notificaciones actividad horaria", - "permanent-failures": "${entityName} Fallos permanentes", - "permanent-timeouts": "${entityName} Timeouts permanentes", - "processing-failures": "${entityName} Fallos de procesamiento", - "processing-failures-and-timeouts": "Fallos de procesamiento y timeouts", - "processing-timeouts": "${entityName} Timeouts de procesamiento", - "queue-stats": "Estadísticas de colas", - "rule-chain": "Cadena de reglas", - "rule-engine": "Motor de reglas", - "rule-engine-daily-activity": "Actividad diaria de motor de reglas", - "rule-engine-executions": "Ejecuciones de motor de reglas", - "rule-engine-hourly-activity": "Actividad horaria de motor de reglas", - "rule-engine-monthly-activity": "Actividad mensual de motor de reglas", - "rule-engine-statistics": "Estadisticas del motor de reglas", - "rule-node": "Nodo de reglas", - "sms": "SMS", - "sms-messages": "Mensajes SMS", - "sms-messages-daily-activity": "Actividad diaria de mensajes SMS", - "sms-messages-monthly-activity": "Actividad mensual de mensajes SMS", - "successful": "${entityName} Exitoso", - "telemetry": "Telemetría", - "telemetry-persistence": "Persistencia de telemetría", - "telemetry-persistence-daily-activity": "Actividad diaria de persistencia de telemetría", - "telemetry-persistence-hourly-activity": "Actividad horaria de persistencia de telemetría", - "telemetry-persistence-monthly-activity": "Actividad mensual de persistencia de telemetría", - "transport": "Transporte", - "transport-daily-activity": "Actividad diaria de transporte", - "transport-data-points": "Puntos de datos de transporte", - "transport-hourly-activity": "Actividad horaria de transporte", - "transport-messages": "Mensajes de transporte", - "transport-monthly-activity": "Actividad mensual de transporte", - "view-details": "Ver detalles", - "view-statistics": "Ver estadísticas" + "arguments" : "Argumentos", + "decimals-by-default" : "Decimales por defecto", + "debugging" : "Depuración de campos calculados", + "argument-name" : "Nombre del argumento", + "datasource" : "Origen de datos", + "add-argument" : "Agregar argumento", + "test-script-function" : "Probar función de script", + "no-arguments" : "No hay argumentos configurados", + "argument-settings" : "Configuración del argumento", + "argument-current" : "Entidad actual", + "argument-current-tenant" : "Inquilino actual", + "argument-device" : "Dispositivo", + "argument-asset" : "Activo", + "argument-customer" : "Cliente", + "argument-tenant" : "Inquilino actual", + "argument-type" : "Tipo de argumento", + "see-debug-events" : "Ver eventos de depuración", + "attribute" : "Atributo", + "copy-argument-name" : "Copiar nombre del argumento", + "timeseries-key" : "Clave de serie temporal", + "device-name" : "Nombre del dispositivo", + "latest-telemetry" : "Última telemetría", + "rolling" : "Acumulación de serie temporal", + "attribute-scope" : "Ámbito del atributo", + "server-attributes" : "Atributos del servidor", + "client-attributes" : "Atributos del cliente", + "shared-attributes" : "Atributos compartidos", + "attribute-key" : "Clave del atributo", + "default-value" : "Valor por defecto", + "limit" : "Máx. valores", + "time-window" : "Ventana de tiempo", + "customer-name" : "Nombre del cliente", + "asset-name" : "Nombre del activo", + "timeseries" : "Serie temporal", + "output" : "Salida", + "create" : "Crear nuevo campo calculado", + "file" : "Archivo de campo calculado", + "invalid-file-error" : "Formato de archivo inválido. Asegúrate de que el archivo sea un JSON válido.", + "import" : "Importar campo calculado", + "export" : "Exportar campo calculado", + "export-failed-error" : "No se pudo exportar el campo calculado: {{error}}", + "output-type" : "Tipo de salida", + "delete-title" : "¿Estás seguro de que deseas eliminar el campo calculado '{{title}}'?", + "delete-text" : "Ten cuidado, después de la confirmación el campo calculado y todos los datos relacionados serán irrecuperables.", + "delete-multiple-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 campo calculado} other {# campos calculados} }?", + "delete-multiple-text" : "Ten cuidado, después de la confirmación todos los campos calculados seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "test-with-this-message" : "Probar con este mensaje", + "hint" : { + "arguments-simple-with-rolling" : "Un campo calculado de tipo simple no debe contener claves con tipo de serie temporal acumulativa.", + "arguments-empty" : "Los argumentos no deben estar vacíos.", + "expression-required" : "Se requiere una expresión.", + "expression-invalid" : "La expresión es inválida", + "expression-max-length" : "La longitud de la expresión debe ser menor a 255 caracteres.", + "argument-name-required" : "Se requiere el nombre del argumento.", + "argument-name-pattern" : "El nombre del argumento es inválido.", + "argument-name-duplicate" : "Ya existe un argumento con ese nombre.", + "argument-name-max-length" : "El nombre del argumento debe tener menos de 256 caracteres.", + "argument-name-forbidden" : "El nombre del argumento está reservado y no puede ser utilizado.", + "argument-type-required" : "Se requiere el tipo de argumento.", + "max-args" : "Se alcanzó el número máximo de argumentos.", + "decimals-range" : "Los decimales por defecto deben ser un número entre 0 y 15.", + "expression" : "La expresión por defecto muestra cómo transformar una temperatura de Fahrenheit a Celsius.", + "arguments-entity-not-found" : "No se encontró la entidad objetivo del argumento." + } + }, + "confirm-on-exit" : { + "message" : "Tienes cambios sin guardar. ¿Estás seguro de que deseas salir de esta página?", + "html-message" : "Tienes cambios sin guardar.
¿Estás seguro de que deseas salir de esta página?", + "title" : "Cambios sin guardar" + }, + "contact" : { + "country" : "País", + "country-required" : "Se requiere el país.", + "city" : "Ciudad", + "state" : "Estado / Provincia", + "postal-code" : "Código postal", + "postal-code-invalid" : "Formato de código postal inválido.", + "address" : "Dirección", + "address2" : "Dirección 2", + "phone" : "Teléfono", + "email" : "Correo electrónico", + "no-address" : "Sin dirección", + "no-country-found" : "No se encontraron países.", + "no-country-matching" : "No se encontraron países que coincidan con '{{country}}'.", + "state-max-length" : "La longitud del estado debe ser menor a 256", + "phone-max-length" : "El número de teléfono debe tener menos de 256 caracteres", + "city-max-length" : "La ciudad especificada debe tener menos de 256 caracteres" + }, + "common" : { + "name" : "Nombre", + "type" : "Tipo", + "general" : "General", + "username" : "Nombre de usuario", + "password" : "Contraseña", + "data" : "Datos", + "timestamp" : "Marca de tiempo", + "enter-username" : "Introducir nombre de usuario", + "enter-password" : "Introducir contraseña", + "enter-search" : "Introducir búsqueda", + "created-time" : "Hora de creación", + "disabled" : "Deshabilitado", + "loading" : "Cargando...", + "proceed" : "Continuar", + "open-details-page" : "Abrir página de detalles", + "not-found" : "No encontrado", + "value" : "Valor", + "documentation" : "Documentación", + "time-left" : "{{time}} restante", + "output" : "Salida", + "suffix" : { + "s" : "s", + "ms" : "ms" }, - "api-limit": { - "cassandra-queries": "Consultas Cassandra", - "entity-version-creation": "Creación de versiones de entidad", - "entity-version-load": "Carga de versiones de entidad", - "notification-requests": "Solicitudes de notificación", - "notification-requests-per-rule": "Solicitudes de notificación por regla", - "rest-api-requests": "Solicitudes REST API", - "rest-api-requests-per-customer": "Solicitudes REST API por cliente", - "transport-messages": "Mensajes de transporte", - "transport-messages-per-device": "Mensajes de transporte por dispositivo", - "ws-updates-per-session": "Actualizaciones WS por sesión" + "hint" : { + "name-required" : "Se requiere el nombre.", + "name-pattern" : "El nombre no es válido.", + "name-max-length" : "El nombre debe tener menos de 256 caracteres.", + "title-required" : "Se requiere el título.", + "title-pattern" : "El título no es válido.", + "title-max-length" : "El título debe tener menos de 256 caracteres.", + "key-required" : "Se requiere la clave.", + "key-pattern" : "La clave no es válida.", + "key-max-length" : "La clave debe tener menos de 256 caracteres." }, - "audit-log": { - "audit": "Auditoría", - "audit-logs": "Registro Auditoría", - "timestamp": "Timestamp", - "entity-type": "Tipo Entidad", - "entity-name": "Nombre Entidad", - "user": "Usuario", - "type": "Tipo", - "status": "Estado", - "details": "Detalles", - "type-added": "Añadido", - "type-deleted": "Borrado", - "type-updated": "Actualizado", - "type-attributes-updated": "Atributos actualizados", - "type-attributes-deleted": "Atributos borrados", - "type-rpc-call": "Llamada RPC", - "type-credentials-updated": "Credenciales actualizados", - "type-assigned-to-customer": "Asignado a Cliente", - "type-unassigned-from-customer": "Deasignado a Cliente", - "type-assigned-to-edge": "Asignado a Edge", - "type-unassigned-from-edge": "Deasignado de Edge", - "type-activated": "Activado", - "type-suspended": "Suspendido", - "type-credentials-read": "Lectura de credenciales", - "type-attributes-read": "Lectura de atributos", - "type-relation-add-or-update": "Relación actualizada", - "type-relation-delete": "Relación borrada", - "type-relations-delete": "Borradas todas las relaciones", - "type-alarm-ack": "Alarma Acusada", - "type-alarm-clear": "Alarma Limpiada", - "type-alarm-assign": "Asignada", - "type-alarm-unassign": "Deasignado", - "type-added-comment": "Comentario añadido", - "type-updated-comment": "Comentario actualizado", - "type-deleted-comment": "Comentario borrado", - "type-login": "Inicio de sesión", - "type-logout": "Cierre de sesión", - "type-lockout": "Cierre por bloqueo", - "status-success": "Éxito", - "status-failure": "Fallo", - "audit-log-details": "Detalle del registro de auditoría", - "no-audit-logs-prompt": "No se encontraron registros", - "action-data": "Datos de acción", - "failure-details": "Detalles del error", - "search": "Buscar registros de auditoría", - "clear-search": "Borrar búsqueda", - "type-assigned-from-tenant": "Asignado desde el administrador", - "type-assigned-to-tenant": "Asignado al administrador", - "type-provision-success": "Dispositivo aprovisionado", - "type-provision-failure": "Aprovisionamiento fallido", - "type-timeseries-updated": "Telemetría actualizada", - "type-timeseries-deleted": "Telemetría borrada", - "type-sms-sent": "SMS enviado" + "required-fields" : "Faltan campos obligatorios" + }, + "content-type" : { + "json" : "Json", + "text" : "Texto", + "binary" : "Binario (Base64)" + }, + "color" : { + "color" : "Color" + }, + "customer" : { + "customer" : "Cliente", + "customers" : "Clientes", + "management" : "Gestión de clientes", + "dashboard" : "Tablero del cliente", + "dashboards" : "Tableros del cliente", + "devices" : "Dispositivos del cliente", + "entity-views" : "Vistas de entidad del cliente", + "assets" : "Activos del cliente", + "public-dashboards" : "Tableros públicos", + "public-devices" : "Dispositivos públicos", + "public-assets" : "Activos públicos", + "public-entity-views" : "Vistas de entidad públicas", + "add" : "Agregar cliente", + "delete" : "Eliminar cliente", + "manage-customer-users" : "Gestionar usuarios del cliente", + "manage-customer-devices" : "Gestionar dispositivos del cliente", + "manage-customer-dashboards" : "Gestionar tableros del cliente", + "manage-public-devices" : "Gestionar dispositivos públicos", + "manage-public-dashboards" : "Gestionar tableros públicos", + "manage-customer-assets" : "Gestionar activos del cliente", + "manage-customer-edges" : "Gestionar edge del cliente", + "manage-public-assets" : "Gestionar activos públicos", + "add-customer-text" : "Agregar nuevo cliente", + "no-customers-text" : "No se encontraron clientes", + "customer-details" : "Detalles del cliente", + "delete-customer-title" : "¿Estás seguro de que deseas eliminar el cliente '{{customerTitle}}'?", + "delete-customer-text" : "Ten cuidado, después de la confirmación el cliente y todos los datos relacionados serán irrecuperables.", + "delete-customers-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 cliente} other {# clientes} }?", + "delete-customers-action-title" : "Eliminar { count, plural, =1 {1 cliente} other {# clientes} }", + "delete-customers-text" : "Ten cuidado, después de la confirmación todos los clientes seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "manage-users" : "Gestionar usuarios", + "manage-assets" : "Gestionar activos", + "manage-devices" : "Gestionar dispositivos", + "manage-dashboards" : "Gestionar tableros", + "title" : "Título", + "title-required" : "Se requiere el título.", + "title-max-length" : "El título debe tener menos de 256 caracteres", + "description" : "Descripción", + "details" : "Detalles", + "events" : "Eventos", + "copyId" : "Copiar ID del cliente", + "idCopiedMessage" : "ID del cliente copiado al portapapeles", + "select-customer" : "Seleccionar cliente", + "no-customers-matching" : "No se encontraron clientes que coincidan con '{{entity}}'.", + "customer-required" : "Se requiere un cliente", + "select-default-customer" : "Seleccionar cliente por defecto", + "default-customer" : "Cliente por defecto", + "default-customer-required" : "Se requiere el cliente por defecto para depurar el tablero a nivel de inquilino", + "search" : "Buscar clientes", + "selected-customers" : "{ count, plural, =1 {1 cliente} other {# clientes} } seleccionado(s)", + "edges" : "Instancias de edge del cliente", + "manage-edges" : "Gestionar edge" + }, + "css-size" : { + "size-value-required" : "Se requiere un valor de tamaño", + "invalid-size-value" : "Valor de tamaño inválido" + }, + "date" : { + "last-update-n-ago" : "Última actualización hace N", + "last-update-n-ago-text" : "Última actualización hace {{ agoText }}", + "custom-date" : "Fecha personalizada", + "format" : "Formato", + "preview" : "Vista previa", + "auto" : "Auto", + "time-granularity-formats" : "Formatos de granularidad de tiempo", + "unit-year" : "Años", + "unit-month" : "Meses", + "unit-day" : "Días", + "unit-hour" : "Horas", + "unit-minute" : "Minutos", + "unit-second" : "Segundos", + "unit-millisecond" : "Milisegundos" + }, + "datetime" : { + "date-from" : "Fecha desde", + "time-from" : "Hora desde", + "date-to" : "Fecha hasta", + "time-to" : "Hora hasta", + "from" : "Desde", + "to" : "Hasta" + }, + "dashboard" : { + "dashboard" : "Tablero", + "dashboards" : "Tableros", + "management" : "Gestión de tableros", + "view-dashboards" : "Ver tableros", + "add" : "Añadir tablero", + "assign-dashboard-to-customer" : "Asignar tablero(s) al cliente", + "assign-dashboard-to-customer-text" : "Por favor, seleccione los tableros para asignar al cliente", + "assign-to-customer-text" : "Por favor, seleccione el cliente para asignar el/los tablero(s)", + "assign-to-customer" : "Asignar al cliente", + "unassign-from-customer" : "Desasignar del cliente", + "make-public" : "Hacer público el tablero", + "make-private" : "Hacer privado el tablero", + "manage-assigned-customers" : "Gestionar clientes asignados", + "assigned-customers" : "Clientes asignados", + "assign-to-customers" : "Asignar tablero(s) a clientes", + "assign-to-customers-text" : "Selecciona los clientes a los que deseas asignar el/los tablero(s)", + "unassign-from-customers" : "Desasignar tablero(s) de clientes", + "unassign-from-customers-text" : "Selecciona los clientes de los que deseas desasignar el/los tablero(s)", + "no-dashboards-text" : "No se encontraron tableros", + "no-widgets" : "No hay widgets configurados", + "add-widget" : "Agregar nuevo widget", + "add-widget-button-text" : "Agregar widget", + "title" : "Título", + "image" : "Imagen del tablero", + "mobile-app-settings" : "Configuración de la aplicación móvil", + "mobile-order" : "Orden del tablero en la aplicación móvil", + "mobile-hide" : "Ocultar tablero en la aplicación móvil", + "update-image" : "Actualizar imagen del tablero", + "take-screenshot" : "Tomar captura de pantalla", + "select-widget-title" : "Seleccionar widget", + "select-widget-value" : "{{title}}: seleccionar widget", + "select-widget-subtitle" : "Lista de tipos de widget disponibles", + "delete" : "Eliminar tablero", + "title-required" : "Se requiere el título.", + "title-max-length" : "El título debe tener menos de 256 caracteres", + "description" : "Descripción", + "details" : "Detalles", + "dashboard-details" : "Detalles del tablero", + "add-dashboard-text" : "Agregar nuevo tablero", + "assign-dashboards" : "Asignar tableros", + "assign-new-dashboard" : "Asignar nuevo tablero", + "assign-dashboards-text" : "Asignar { count, plural, =1 {1 tablero} other {# tableros} } a clientes", + "unassign-dashboards-action-text" : "Desasignar { count, plural, =1 {1 tablero} other {# tableros} } de clientes", + "delete-dashboards" : "Eliminar tableros", + "unassign-dashboards" : "Desasignar tableros", + "unassign-dashboards-action-title" : "Desasignar { count, plural, =1 {1 tablero} other {# tableros} } del cliente", + "delete-dashboard-title" : "¿Estás seguro de que deseas eliminar el tablero '{{dashboardTitle}}'?", + "delete-dashboard-text" : "Ten cuidado, después de la confirmación el tablero y todos los datos relacionados serán irrecuperables.", + "delete-dashboards-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 tablero} other {# tableros} }?", + "delete-dashboards-action-title" : "Eliminar { count, plural, =1 {1 tablero} other {# tableros} }", + "delete-dashboards-text" : "Ten cuidado, después de la confirmación todos los tableros seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "unassign-dashboard-title" : "¿Estás seguro de que deseas desasignar el tablero '{{dashboardTitle}}'?", + "unassign-dashboard-text" : "Después de la confirmación, el tablero será desasignado y no será accesible por el cliente.", + "unassign-dashboard" : "Desasignar tablero", + "unassign-dashboards-title" : "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 tablero} other {# tableros} }?", + "unassign-dashboards-text" : "Después de la confirmación, todos los tableros seleccionados serán desasignados y no serán accesibles por el cliente.", + "public-dashboard-title" : "El tablero ahora es público", + "public-dashboard-text" : "Tu tablero {{dashboardTitle}} ahora es público y accesible mediante el siguiente enlace:", + "public-dashboard-notice" : "Nota: No olvides hacer públicos los dispositivos relacionados para acceder a sus datos.", + "make-private-dashboard-title" : "¿Estás seguro de que deseas hacer privado el tablero '{{dashboardTitle}}'?", + "make-private-dashboard-text" : "Después de la confirmación, el tablero será privado y no será accesible por otros.", + "make-private-dashboard" : "Hacer privado el tablero", + "socialshare-text" : "'{{dashboardTitle}}' impulsado por ThingsBoard", + "socialshare-title" : "'{{dashboardTitle}}' impulsado por ThingsBoard", + "select-dashboard" : "Seleccionar tablero", + "no-dashboards-matching" : "No se encontraron tableros que coincidan con '{{entity}}'.", + "dashboard-required" : "Se requiere un tablero.", + "select-existing" : "Seleccionar tablero existente", + "create-new" : "Crear nuevo tablero", + "new-dashboard-title" : "Título del nuevo tablero", + "open-dashboard" : "Abrir tablero", + "set-background" : "Establecer fondo", + "background-color" : "Color de fondo", + "background-image" : "Imagen de fondo", + "background-size-mode" : "Modo de tamaño del fondo", + "no-image" : "Ninguna imagen seleccionada", + "empty-image" : "Sin imagen", + "drop-image" : "Suelta una imagen o haz clic para seleccionar un archivo para subir.", + "maximum-upload-file-size" : "Tamaño máximo del archivo de carga: {{ size }}", + "cannot-upload-file" : "No se puede cargar el archivo", + "settings" : "Configuraciones", + "move-all-widgets" : "Mover todos los widgets", + "move-by" : "Mover por", + "cols" : "columnas", + "rows" : "filas", + "layout" : "Disposición", + "layout-type-default" : "Predeterminado", + "layout-type-scada" : "SCADA", + "layout-type-divider" : "Divisor", + "layout-settings-type" : "Configuración de disposición: punto de interrupción {{ type }}", + "columns-count" : "Cantidad de columnas", + "columns-count-required" : "Se requiere la cantidad de columnas.", + "min-columns-count-message" : "Solo se permite un mínimo de 10 columnas.", + "max-columns-count-message" : "Solo se permite un máximo de 1000 columnas.", + "min-layout-width" : "Ancho mínimo de disposición", + "columns-suffix" : "columnas", + "widgets-margins" : "Margen entre widgets", + "margin-required" : "Se requiere un valor de margen.", + "min-margin-message" : "Solo se permite 0 como valor mínimo de margen.", + "max-margin-message" : "Solo se permite 50 como valor máximo de margen.", + "horizontal-margin" : "Margen horizontal", + "horizontal-margin-required" : "Se requiere el valor del margen horizontal.", + "min-horizontal-margin-message" : "Solo se permite 0 como valor mínimo de margen horizontal.", + "max-horizontal-margin-message" : "Solo se permite 50 como valor máximo de margen horizontal.", + "vertical-margin" : "Margen vertical", + "vertical-margin-required" : "Se requiere el valor del margen vertical.", + "min-vertical-margin-message" : "Solo se permite 0 como valor mínimo de margen vertical.", + "max-vertical-margin-message" : "Solo se permite 50 como valor máximo de margen vertical.", + "apply-outer-margin" : "Aplicar margen a los lados de la disposición", + "autofill-height" : "Altura de disposición automática", + "mobile-layout" : "Configuración de disposición móvil", + "mobile-row-height" : "Altura de fila móvil", + "mobile-row-height-required" : "Se requiere la altura de fila móvil.", + "min-mobile-row-height-message" : "Solo se permite 5 píxeles como valor mínimo de altura de fila móvil.", + "max-mobile-row-height-message" : "Solo se permite 200 píxeles como valor máximo de altura de fila móvil.", + "row-height" : "Altura de fila", + "row-height-required" : "Se requiere el valor de altura de fila.", + "min-row-height-message" : "Solo se permite 5 píxeles como valor mínimo de altura de fila.", + "max-row-height-message" : "Solo se permite 200 píxeles como valor máximo de altura de fila.", + "display-first-in-mobile-view" : "Mostrar primero en vista móvil", + "title-settings" : "Configuración del título", + "display-title" : "Mostrar título del tablero", + "title-color" : "Color del título", + "toolbar-settings" : "Configuración de la barra de herramientas", + "hide-toolbar" : "Ocultar barra de herramientas", + "toolbar-always-open" : "Mantener la barra de herramientas abierta", + "display-dashboards-selection" : "Mostrar selección de tableros", + "display-entities-selection" : "Mostrar selección de entidades", + "display-filters" : "Mostrar filtros", + "display-dashboard-timewindow" : "Mostrar ventana de tiempo del tablero", + "display-dashboard-export" : "Mostrar exportación", + "display-update-dashboard-image" : "Mostrar actualización de imagen del tablero", + "dashboard-logo-settings" : "Configuración del logo del tablero", + "display-dashboard-logo" : "Mostrar logo en modo de pantalla completa del tablero", + "dashboard-logo-image" : "Imagen del logo del tablero", + "advanced-settings" : "Configuraciones avanzadas", + "dashboard-css" : "CSS del tablero", + "import" : "Importar tablero", + "export" : "Exportar tablero", + "export-failed-error" : "No se pudo exportar el tablero: {{error}}", + "export-prompt" : "Incluir imágenes y recursos del tablero", + "create-new-dashboard" : "Crear nuevo tablero", + "dashboard-file" : "Archivo del tablero", + "invalid-dashboard-file-error" : "No se pudo importar el tablero: estructura de datos inválida.", + "dashboard-import-missing-aliases-title" : "Configurar alias usados por el tablero importado", + "create-new-widget" : "Crear nuevo widget", + "import-widget" : "Importar widget", + "widget-file" : "Archivo del widget", + "invalid-widget-file-error" : "No se pudo importar el widget: estructura de datos inválida.", + "widget-import-missing-aliases-title" : "Configurar alias usados por el widget importado", + "open-toolbar" : "Abrir barra de herramientas del tablero", + "close-toolbar" : "Cerrar barra de herramientas", + "configuration-error" : "Error de configuración", + "alias-resolution-error-title" : "Error de configuración de alias del tablero", + "invalid-aliases-config" : "No se pudieron encontrar dispositivos que coincidan con algunos de los filtros de alias.
Por favor, contacta con tu administrador para resolver este problema.", + "select-devices" : "Seleccionar dispositivos", + "assignedToCustomer" : "Asignado al cliente", + "assignedToCustomers" : "Asignado a clientes", + "public" : "Público", + "copyId" : "Copiar ID del tablero", + "idCopiedMessage" : "El ID del tablero ha sido copiado al portapapeles", + "public-link" : "Enlace público", + "copy-public-link" : "Copiar enlace público", + "public-link-copied-message" : "El enlace público del tablero ha sido copiado al portapapeles", + "manage-states" : "Gestionar estados del tablero", + "states" : "Estados del tablero", + "states-short" : "Estados", + "search-states" : "Buscar estados del tablero", + "selected-states" : "{ count, plural, =1 {1 estado del tablero} other {# estados del tablero} } seleccionado(s)", + "edit-state" : "Editar estado del tablero", + "delete-state" : "Eliminar estado del tablero", + "add-state" : "Agregar estado del tablero", + "no-states-text" : "No se encontraron estados", + "state" : "Estado del tablero", + "state-name" : "Nombre", + "state-name-required" : "Se requiere el nombre del estado del tablero.", + "state-id" : "ID del estado", + "state-id-required" : "Se requiere el ID del estado del tablero.", + "state-id-exists" : "Ya existe un estado del tablero con el mismo ID.", + "is-root-state" : "Estado raíz", + "delete-state-title" : "Eliminar estado del tablero", + "delete-state-text" : "¿Estás seguro de que deseas eliminar el estado del tablero con nombre '{{stateName}}'?", + "show-details" : "Mostrar detalles", + "hide-details" : "Ocultar detalles", + "select-state" : "Seleccionar estado objetivo", + "state-controller" : "Controlador de estado", + "state-controller-default" : "estático (obsoleto)", + "search" : "Buscar tableros", + "selected-dashboards" : "{ count, plural, =1 {1 tablero} other {# tableros} } seleccionado(s)", + "home-dashboard" : "Tablero de inicio", + "home-dashboard-hide-toolbar" : "Ocultar barra de herramientas del tablero de inicio", + "unassign-dashboard-from-edge-text" : "Después de la confirmación, el tablero será desasignado y no será accesible por el edge.", + "unassign-dashboards-from-edge-title" : "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 tablero} other {# tableros} }?", + "unassign-dashboards-from-edge-text" : "Después de la confirmación, todos los tableros seleccionados serán desasignados y no serán accesibles por el edge.", + "assign-dashboard-to-edge" : "Asignar tablero(s) al edge", + "assign-dashboard-to-edge-text" : "Selecciona los tableros que deseas asignar al edge", + "non-existent-dashboard-state-error" : "No se encontró el estado del tablero con ID \"{{ stateId }}\"", + "edit-mode" : "Modo de edición", + "duplicate-state-action" : "Duplicar estado", + "breakpoint-value" : "Punto de interrupción ({{ value }})", + "breakpoints-id" : { + "default" : "Predeterminado", + "xs" : "Móvil (xs)", + "sm" : "Tablet (sm)", + "md" : "Portátil (md)", + "lg" : "Escritorio (lg)", + "xl" : "Escritorio (xl)" }, - "confirm-on-exit": { - "message": "Tienes cambios sin guardar. ¿Abandonar la página?", - "html-message": "Tienes cambios sin guardar.
¿Abandonar la página?", - "title": "Cambios sin guardar" + "view-format-type-grid" : "Cuadrícula", + "view-format-type-list" : "Lista", + "view-format" : "Formato de vista" + }, + "datakey" : { + "settings" : "Configuraciones", + "general" : "General", + "advanced" : "Avanzado", + "key" : "Clave", + "keys" : "Claves", + "label" : "Etiqueta", + "color" : "Color", + "units" : "Símbolo especial a mostrar junto al valor", + "decimals" : "Número de dígitos después del punto decimal", + "data-generation-func" : "Función de generación de datos", + "use-data-post-processing-func" : "Usar función de post-procesamiento de datos", + "configuration" : "Configuración de claves de datos", + "timeseries" : "Serie temporal", + "attributes" : "Atributos", + "entity-field" : "Campo de entidad", + "alarm" : "Campos de alarma", + "timeseries-required" : "Se requiere la serie temporal de la entidad.", + "timeseries-or-attributes-required" : "Se requiere la serie temporal o atributos de la entidad.", + "alarm-fields-timeseries-or-attributes-required" : "Se requieren campos de alarma o serie temporal/atributos de la entidad.", + "maximum-timeseries-or-attributes" : "Máximo { count, plural, =1 {1 serie temporal/atributo permitido.} other {# series temporales/atributos permitidos} }", + "alarm-fields-required" : "Se requieren campos de alarma.", + "function-types" : "Tipos de función", + "function-type" : "Tipo de función", + "function-types-required" : "Se requieren tipos de función.", + "data-keys" : "Claves de datos", + "data-key" : "Clave de datos", + "data-keys-required" : "Se requieren claves de datos.", + "data-key-required" : "Se requiere una clave de datos.", + "alarm-keys" : "Claves de datos de alarma", + "alarm-key" : "Clave de datos de alarma", + "alarm-key-functions" : "Funciones de clave de alarma", + "alarm-key-function" : "Función de clave de alarma", + "latest-keys" : "Últimas claves de datos", + "latest-key" : "Última clave de datos", + "latest-key-functions" : "Funciones de última clave", + "latest-key-function" : "Función de última clave", + "timeseries-keys" : "Claves de datos de serie temporal", + "timeseries-key" : "Clave de serie temporal", + "timeseries-key-functions" : "Funciones de clave de serie temporal", + "timeseries-key-function" : "Función de clave de serie temporal", + "maximum-function-types" : "Máximo { count, plural, =1 {1 tipo de función permitido.} other {# tipos de función permitidos} }", + "time-description" : "marca de tiempo del valor actual;", + "value-description" : "el valor actual;", + "prev-value-description" : "resultado de la llamada anterior a la función;", + "time-prev-description" : "marca de tiempo del valor anterior;", + "prev-orig-value-description" : "valor original anterior;", + "aggregation" : "Agregación", + "aggregation-type-hint-common" : "Por razones de rendimiento, el cálculo de valores agregados está disponible solo para intervalos de tiempo fijos como \"día actual\", \"mes actual\", etc., y no está disponible para ventanas de tiempo deslizantes como 'últimos 30 minutos' o 'últimas 24 horas'.", + "aggregation-type-none-hint" : "Tomar el valor más reciente.", + "aggregation-type-min-hint" : "Encontrar el valor mínimo entre los puntos de datos dentro de una ventana de tiempo seleccionada.", + "aggregation-type-max-hint" : "Encontrar el valor máximo entre los puntos de datos dentro de una ventana de tiempo seleccionada.", + "aggregation-type-avg-hint" : "Calcular un valor promedio entre los puntos de datos dentro de una ventana de tiempo seleccionada.", + "aggregation-type-sum-hint" : "Suma de todos los valores de los puntos de datos dentro de una ventana de tiempo seleccionada.", + "aggregation-type-count-hint" : "Número total de puntos de datos dentro de una ventana de tiempo seleccionada.", + "delta-calculation" : "Cálculo delta", + "enable-delta-calculation" : "Habilitar cálculo delta", + "enable-delta-calculation-hint" : "Cuando está habilitado, el valor de la clave de datos se calcula en función de los valores agregados para una ventana de tiempo seleccionada y un período de comparación especificado. Por razones de rendimiento, el cálculo delta está disponible solo para ventanas de tiempo históricas y no para valores en tiempo real. Por ejemplo, puedes calcular el delta entre el consumo de energía de ayer comparado con el del día anterior.", + "delta-calculation-result" : "Resultado del cálculo delta", + "delta-calculation-result-previous-value" : "Valor anterior", + "delta-calculation-result-delta-absolute" : "Delta (absoluto)", + "delta-calculation-result-delta-percent" : "Delta (porcentaje)", + "source" : "Fuente", + "latest" : "Último", + "latest-value" : "Último valor", + "delta" : "delta", + "percent" : "porcentaje", + "absolute" : "absoluto" + }, + "datasource" : { + "type" : "Tipo de fuente de datos", + "name" : "Nombre", + "label" : "Etiqueta", + "add-datasource-prompt" : "Por favor, agrega una fuente de datos" + }, + "details" : { + "details" : "Detalles", + "edit-mode" : "Modo de edición", + "edit-json" : "Editar JSON", + "toggle-edit-mode" : "Alternar modo de edición" + }, + "device" : { + "device" : "Dispositivo", + "device-required" : "Se requiere un dispositivo.", + "devices" : "Dispositivos", + "management" : "Gestión de dispositivos", + "view-devices" : "Ver dispositivos", + "device-alias" : "Alias del dispositivo", + "device-type-max-length" : "El tipo de dispositivo debe tener menos de 256 caracteres", + "aliases" : "Alias de dispositivos", + "no-alias-matching" : "'{{alias}}' no encontrado.", + "no-aliases-found" : "No se encontraron alias.", + "no-key-matching" : "'{{key}}' no encontrado.", + "no-keys-found" : "No se encontraron claves.", + "create-new-alias" : "¡Crear uno nuevo!", + "create-new-key" : "¡Crear uno nuevo!", + "duplicate-alias-error" : "Se encontró un alias duplicado '{{alias}}'.
Los alias de dispositivos deben ser únicos dentro del tablero.", + "configure-alias" : "Configurar alias '{{alias}}'", + "no-devices-matching" : "No se encontraron dispositivos que coincidan con '{{entity}}'.", + "alias" : "Alias", + "alias-required" : "Se requiere un alias de dispositivo.", + "remove-alias" : "Eliminar alias de dispositivo", + "add-alias" : "Agregar alias de dispositivo", + "name-starts-with" : "Expresión del nombre del dispositivo", + "help-text" : "Usa '%' según sea necesario: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list" : "Lista de dispositivos", + "use-device-name-filter" : "Usar filtro", + "device-list-empty" : "No se seleccionaron dispositivos.", + "device-name-filter-required" : "Se requiere el filtro de nombre del dispositivo.", + "device-name-filter-no-device-matched" : "No se encontraron dispositivos que comiencen con '{{device}}'.", + "add" : "Agregar dispositivo", + "assign-to-customer" : "Asignar al cliente", + "assign-device-to-customer" : "Asignar dispositivo(s) al cliente", + "assign-device-to-customer-text" : "Selecciona los dispositivos que deseas asignar al cliente", + "make-public" : "Hacer público el dispositivo", + "make-private" : "Hacer privado el dispositivo", + "no-devices-text" : "No se encontraron dispositivos", + "assign-to-customer-text" : "Selecciona el cliente al que deseas asignar el/los dispositivo(s)", + "device-details" : "Detalles del dispositivo", + "add-device-text" : "Agregar nuevo dispositivo", + "credentials" : "Credenciales", + "manage-credentials" : "Gestionar credenciales", + "delete" : "Eliminar dispositivo", + "assign-devices" : "Asignar dispositivos", + "assign-devices-text" : "Asignar { count, plural, =1 {1 dispositivo} other {# dispositivos} } al cliente", + "delete-devices" : "Eliminar dispositivos", + "unassign-from-customer" : "Desasignar del cliente", + "unassign-devices" : "Desasignar dispositivos", + "unassign-devices-action-title" : "Desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} } del cliente", + "unassign-device-from-edge-title" : "¿Estás seguro de que deseas desasignar el dispositivo '{{deviceName}}'?", + "unassign-device-from-edge-text" : "Después de la confirmación, el dispositivo será desasignado y no será accesible por el edge.", + "unassign-devices-from-edge" : "Desasignar dispositivos del edge", + "assign-new-device" : "Asignar nuevo dispositivo", + "make-public-device-title" : "¿Estás seguro de que deseas hacer público el dispositivo '{{deviceName}}'?", + "make-public-device-text" : "Después de la confirmación, el dispositivo y todos sus datos serán públicos y accesibles por otros.", + "make-private-device-title" : "¿Estás seguro de que deseas hacer privado el dispositivo '{{deviceName}}'?", + "make-private-device-text" : "Después de la confirmación, el dispositivo y todos sus datos serán privados y no serán accesibles por otros.", + "view-credentials" : "Ver credenciales", + "delete-device-title" : "¿Estás seguro de que deseas eliminar el dispositivo '{{deviceName}}'?", + "delete-device-text" : "Ten cuidado, después de la confirmación el dispositivo y todos los datos relacionados serán irrecuperables.", + "delete-devices-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", + "delete-devices-action-title" : "Eliminar { count, plural, =1 {1 dispositivo} other {# dispositivos} }", + "delete-devices-text" : "Ten cuidado, después de la confirmación todos los dispositivos seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "unassign-device-title" : "¿Estás seguro de que deseas desasignar el dispositivo '{{deviceName}}'?", + "unassign-device-text" : "Después de la confirmación, el dispositivo será desasignado y no será accesible por el cliente.", + "unassign-device" : "Desasignar dispositivo", + "unassign-devices-title" : "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", + "unassign-devices-text" : "Después de la confirmación, todos los dispositivos seleccionados serán desasignados y no serán accesibles por el cliente.", + "device-credentials" : "Credenciales del dispositivo", + "loading-device-credentials" : "Cargando credenciales del dispositivo...", + "credentials-type" : "Tipo de credenciales", + "access-token" : "Token de acceso", + "access-token-required" : "Se requiere el token de acceso.", + "access-token-invalid" : "La longitud del token de acceso debe ser de 1 a 32 caracteres.", + "certificate-pem-format" : "Certificado en formato PEM", + "certificate-pem-format-required" : "Se requiere un certificado.", + "copy-access-token" : "Copiar token de acceso", + "copy-certificate" : "Copiar certificado", + "copy-client-id" : "Copiar ID del cliente", + "copy-user-name" : "Copiar nombre de usuario", + "copy-password" : "Copiar contraseña", + "generate-client-id" : "Generar ID del cliente", + "generate-user-name" : "Generar nombre de usuario", + "generate-password" : "Generar contraseña", + "generate-access-token" : "Generar token de acceso", + "lwm2m-security-config" : { + "identity" : "Identidad del cliente", + "identity-required" : "Se requiere la identidad del cliente.", + "identity-tooltip" : "El identificador PSK es un identificador arbitrario de hasta 128 bytes, como se describe en la norma [RFC7925].\nEl identificador PSK DEBE convertirse primero en una cadena de caracteres y luego codificarse en octetos usando UTF-8.", + "client-key" : "Clave del cliente", + "client-key-required" : "Se requiere la clave del cliente.", + "client-key-tooltip-prk" : "¡La clave pública o ID RPK debe cumplir con la norma [RFC7250] y estar codificada en formato Base64!", + "client-key-tooltip-psk" : "¡La clave PSK debe cumplir con la norma [RFC4279] y estar en formato HexDec: 32, 64 o 128 caracteres!", + "endpoint" : "Nombre del cliente endpoint", + "endpoint-required" : "Se requiere el nombre del cliente endpoint.", + "client-public-key" : "Clave pública del cliente", + "client-public-key-hint" : "Si la clave pública del cliente está vacía, se usará el certificado confiable", + "client-public-key-tooltip" : "La clave pública X509 debe estar en formato DER-encoded X509v3, soportar exclusivamente el algoritmo EC y luego codificarse en Base64.", + "mode" : "Modo de configuración de seguridad", + "client-tab" : "Configuración de seguridad del cliente", + "client-certificate" : "Certificado del cliente", + "bootstrap-tab" : "Cliente Bootstrap", + "bootstrap-server" : "Servidor Bootstrap", + "lwm2m-server" : "Servidor LwM2M", + "client-publicKey-or-id" : "Clave pública o ID del cliente", + "client-publicKey-or-id-required" : "Se requiere la clave pública o el ID del cliente.", + "client-publicKey-or-id-tooltip-psk" : "El identificador PSK es arbitrario hasta 128 bytes, conforme a la norma [RFC7925].\nDebe convertirse a cadena y luego a octetos en UTF-8.", + "client-publicKey-or-id-tooltip-rpk" : "La clave pública o ID RPK debe cumplir con [RFC7250] y estar en Base64.", + "client-publicKey-or-id-tooltip-x509" : "La clave pública X509 debe cumplir con X509v3 DER-encoded, usar algoritmo EC y estar en Base64.", + "client-secret-key" : "Clave secreta del cliente", + "client-secret-key-required" : "Se requiere la clave secreta del cliente.", + "client-secret-key-tooltip-psk" : "La clave PSK debe cumplir con [RFC4279] y estar en HexDec (32, 64, 128 caracteres).", + "client-secret-key-tooltip-prk" : "La clave secreta RPK debe estar en formato PKCS_8 (codificación DER, estándar [RFC5958]) y en Base64.", + "client-secret-key-tooltip-x509" : "La clave secreta X509 debe estar en formato PKCS_8 (codificación DER, estándar [RFC5958]) y en Base64." }, - "contact": { - "country": "País", - "city": "Ciudad", - "state": "Estado / Provincia", - "postal-code": "Código Postal", - "postal-code-invalid": "Formato de código postal inválido.", - "address": "Dirección", - "address2": "Dirección 2", - "phone": "Teléfono", - "email": "Email", - "no-address": "Sin Dirección", - "state-max-length": "Longitud de provincia debe ser menor que 256", - "phone-max-length": "Teléfono debe ser menor que 256", - "city-max-length": "Ciudad debe ser menor que 256" + "client-id" : "ID del cliente", + "client-id-pattern" : "Contiene carácter inválido.", + "user-name" : "Nombre de usuario", + "user-name-required" : "Se requiere el nombre de usuario.", + "client-id-or-user-name-necessary" : "Se requiere el ID del cliente y/o el nombre de usuario", + "password" : "Contraseña", + "secret" : "Secreto", + "secret-required" : "Se requiere un secreto.", + "device-type" : "Perfil del dispositivo", + "device-type-required" : "Se requiere el tipo de dispositivo.", + "select-device-type" : "Seleccionar tipo de dispositivo", + "enter-device-type" : "Ingresar perfil del dispositivo", + "any-device" : "Cualquier dispositivo", + "no-device-types-matching" : "No se encontraron perfiles de dispositivo que coincidan con '{{entitySubtype}}'.", + "device-type-list-empty" : "¡No se seleccionaron perfiles de dispositivo!", + "device-profile-type-list-empty" : "Debe seleccionarse al menos un perfil de dispositivo.", + "device-types" : "Tipos de dispositivos", + "name" : "Nombre", + "name-required" : "Se requiere el nombre.", + "name-max-length" : "El nombre debe tener menos de 256 caracteres", + "label-max-length" : "La etiqueta debe tener menos de 256 caracteres", + "description" : "Descripción", + "label" : "Etiqueta", + "events" : "Eventos", + "details" : "Detalles", + "copyId" : "Copiar ID del dispositivo", + "copyAccessToken" : "Copiar token de acceso", + "copy-mqtt-authentication" : "Copiar credenciales MQTT", + "idCopiedMessage" : "ID del dispositivo copiado al portapapeles", + "accessTokenCopiedMessage" : "Token de acceso del dispositivo copiado al portapapeles", + "mqtt-authentication-copied-message" : "Autenticación MQTT del dispositivo copiada al portapapeles", + "assignedToCustomer" : "Asignado al cliente", + "unable-delete-device-alias-title" : "No se puede eliminar el alias del dispositivo", + "unable-delete-device-alias-text" : "No se puede eliminar el alias del dispositivo '{{deviceAlias}}' porque lo utilizan los siguientes widget(s):
{{widgetsList}}", + "is-gateway" : "Es un gateway", + "overwrite-activity-time" : "Sobrescribir tiempo de actividad del dispositivo conectado", + "device-filter" : "Filtro de dispositivo", + "device-filter-title" : "Filtro de dispositivo", + "filter-title" : "Filtro", + "device-state" : "Estado del dispositivo", + "state" : "Estado", + "any" : "Cualquiera", + "active" : "Activo", + "inactive" : "Inactivo", + "public" : "Público", + "device-public" : "El dispositivo es público", + "select-device" : "Seleccionar dispositivo", + "import" : "Importar dispositivo", + "device-file" : "Archivo del dispositivo", + "search" : "Buscar dispositivos", + "selected-devices" : "{ count, plural, =1 {1 dispositivo} other {# dispositivos} } seleccionado(s)", + "device-configuration" : "Configuración del dispositivo", + "transport-configuration" : "Configuración de transporte", + "wizard" : { + "device-details" : "Detalles del dispositivo" }, - "common": { - "username": "Usuario", - "password": "Contraseña", - "enter-username": "Introduce el nombre de usuario.", - "enter-password": "Introduce la contraseña", - "enter-search": "Introduce búsqueda", - "created-time": "Fecha de creación", - "loading": "Cargando...", - "proceed": "Proceder", - "open-details-page": "Abrir detalles", - "not-found": "No encontrado", - "documentation": "Documentación" + "unassign-devices-from-edge-title" : "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", + "unassign-devices-from-edge-text" : "Después de la confirmación, todos los dispositivos seleccionados serán desasignados y no serán accesibles por el edge.", + "time" : "Tiempo", + "connectivity" : { + "check-connectivity" : "Verificar conectividad", + "device-created-check-connectivity" : "Dispositivo creado. ¡Vamos a verificar la conectividad!", + "loading-check-connectivity-command" : "Cargando comandos para verificar conectividad...", + "use-following-instructions" : "Usa las siguientes instrucciones para enviar telemetría en nombre del dispositivo usando shell", + "execute-following-command" : "Ejecuta el siguiente comando", + "install-curl-windows" : "A partir de Windows 10 b17063, cURL está disponible por defecto", + "install-curl-macos" : "Desde Mac OS X 10.2 6C115 (Jaguar), cURL está disponible por defecto", + "install-mqtt-windows" : "Usa las instrucciones para descargar, instalar, configurar y ejecutar mosquitto_pub", + "install-coap-client" : "Usa las instrucciones para descargar, instalar, configurar y ejecutar coap-client", + "install-necessary-client-tools" : "Instalar las herramientas de cliente necesarias", + "mqtts-x509-command" : "Usa la siguiente documentación para conectar el dispositivo mediante MQTT con autorización X509", + "coaps-x509-command" : "Usa la siguiente documentación para conectar el dispositivo mediante CoAP sobre DTLS con autorización X509", + "snmp-command" : "Usa la siguiente documentación para conectar el dispositivo a través de SNMP.", + "sparkplug-command" : "Usa la siguiente documentación para conectar el dispositivo a través de MQTT Sparkplug.", + "lwm2m-command" : "Usa la siguiente documentación para conectar el dispositivo mediante LWM2M." + } + }, + "dynamic-form" : { + "property" : { + "properties" : "Propiedades", + "property" : "Propiedad", + "id" : "Id", + "name" : "Nombre", + "type" : "Tipo", + "type-text" : "Texto", + "type-password" : "Contraseña", + "type-textarea" : "Área de texto", + "type-number" : "Número", + "type-switch" : "Interruptor", + "type-select" : "Seleccionar", + "type-radios" : "Botones de opción", + "type-datetime" : "Fecha/Hora", + "type-image" : "Imagen", + "type-javascript" : "JavaScript", + "type-json" : "JSON", + "type-html" : "HTML", + "type-css" : "CSS", + "type-markdown" : "Markdown", + "type-color" : "Color", + "type-color-settings" : "Configuración de color", + "type-font" : "Fuente", + "type-units" : "Unidades", + "type-icon" : "Ícono", + "type-fieldset" : "Conjunto de campos", + "type-array" : "Arreglo", + "type-html-section" : "Sección HTML", + "group-title" : "Título del grupo", + "no-properties" : "No hay propiedades configuradas", + "add-property" : "Agregar propiedad", + "property-settings" : "Configuración de propiedad", + "remove-property" : "Eliminar propiedad", + "default-value" : "Valor por defecto", + "value-required" : "Se requiere valor", + "number-settings" : "Configuración de número", + "min" : "Mín", + "max" : "Máx", + "step" : "Paso", + "selected-options-limit" : "Límite de opciones seleccionadas", + "advanced-ui-settings" : "Configuración avanzada de la interfaz", + "disable-on-property" : "Deshabilitar en propiedad", + "display-condition-function" : "Función de condición de visualización", + "sub-label" : "Subetiqueta", + "vertical-divider-after" : "Divisor vertical después", + "input-field-suffix" : "Sufijo del campo de entrada", + "property-row-classes" : "Clases de fila de propiedad", + "property-field-classes" : "Clases de campo de propiedad", + "not-unique-property-ids-error" : "¡Los Ids de propiedad deben ser únicos!", + "enable-multiple-select" : "Habilitar selección múltiple", + "allow-empty-select-option" : "Permitir opción vacía", + "select-options" : "Opciones de selección", + "not-unique-select-option-value-error" : "¡Los valores de las opciones de selección deben ser únicos!", + "value" : "Valor", + "label" : "Etiqueta", + "add-option" : "Agregar opción", + "no-options" : "No hay opciones configuradas", + "remove-option" : "Eliminar opción", + "textarea-rows" : "Filas del área de texto", + "help-id" : "Id de ayuda", + "buttons-direction" : "Dirección de los botones", + "direction-row" : "Fila", + "direction-column" : "Columna", + "radio-button-options" : "Opciones de botones de radio", + "datetime-type" : "Tipo de campo Fecha/Hora", + "datetime-type-date" : "Fecha", + "datetime-type-time" : "Hora", + "datetime-type-datetime" : "Fecha/Hora", + "enable-clear-button" : "Habilitar botón de limpiar", + "html-section-settings" : "Configuración de la sección HTML", + "html-section-classes" : "Clases de la sección HTML", + "html-section-content" : "Contenido de la sección HTML", + "array-item" : "Elemento del arreglo", + "item-type" : "Tipo de elemento", + "item-name" : "Nombre del elemento", + "no-items" : "No hay elementos" }, - "content-type": { - "json": "Json", - "text": "Texto", - "binary": "Binario (Base64)" + "clear-form" : "Limpiar formulario", + "clear-form-prompt" : "¿Estás seguro de que deseas eliminar todas las propiedades del formulario?", + "import-form" : "Importar formulario desde JSON", + "export-form" : "Exportar formulario a JSON", + "json-file" : "Archivo JSON", + "json-content" : "Contenido JSON", + "invalid-form-json-file-error" : "No se pudo importar el formulario desde JSON: estructura de datos JSON del formulario inválida." + }, + "asset-profile" : { + "asset-profile" : "Perfil de activo", + "asset-profiles" : "Perfiles de activos", + "all-asset-profiles" : "Todos", + "add" : "Agregar perfil de activo", + "edit" : "Editar perfil de activo", + "asset-profile-details" : "Detalles del perfil de activo", + "no-asset-profiles-text" : "No se encontraron perfiles de activos", + "search" : "Buscar perfiles de activos", + "selected-asset-profiles" : "{ count, plural, =1 {1 perfil de activo} other {# perfiles de activos} } seleccionado(s)", + "no-asset-profiles-matching" : "No se encontraron perfiles de activos que coincidan con '{{entity}}'.", + "asset-profile-required" : "Se requiere un perfil de activo", + "idCopiedMessage" : "El ID del perfil de activo ha sido copiado al portapapeles", + "set-default" : "Establecer como perfil de activo predeterminado", + "delete" : "Eliminar perfil de activo", + "copyId" : "Copiar ID del perfil de activo", + "name-max-length" : "El nombre debe tener menos de 256 caracteres", + "new-device-profile-name" : "Nombre del perfil de activo", + "new-device-profile-name-required" : "Se requiere el nombre del perfil de activo.", + "name" : "Nombre", + "name-required" : "Se requiere el nombre.", + "image" : "Imagen del perfil de activo", + "description" : "Descripción", + "default" : "Predeterminado", + "default-rule-chain" : "Cadena de reglas predeterminada", + "default-edge-rule-chain" : "Cadena de reglas predeterminada para el edge", + "default-edge-rule-chain-hint" : "Utilizada en edge como cadena de reglas para procesar datos entrantes de activos con este perfil", + "mobile-dashboard" : "Tablero móvil", + "mobile-dashboard-hint" : "Utilizado por la aplicación móvil como tablero de detalles del activo", + "select-queue-hint" : "Seleccionar de la lista desplegable.", + "delete-asset-profile-title" : "¿Estás seguro de que deseas eliminar el perfil de activo '{{assetProfileName}}'?", + "delete-asset-profile-text" : "Ten cuidado, después de la confirmación el perfil de activo y todos los datos relacionados serán irrecuperables.", + "delete-asset-profiles-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 perfil de activo} other {# perfiles de activos} }?", + "delete-asset-profiles-text" : "Ten cuidado, después de la confirmación todos los perfiles de activos seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "set-default-asset-profile-title" : "¿Estás seguro de que deseas establecer el perfil de activo '{{assetProfileName}}' como predeterminado?", + "set-default-asset-profile-text" : "Después de la confirmación, el perfil de activo se marcará como predeterminado y se utilizará para nuevos activos sin perfil especificado.", + "no-asset-profiles-found" : "No se encontraron perfiles de activos.", + "create-new-asset-profile" : "¡Crear uno nuevo!", + "create-asset-profile" : "Crear nuevo perfil de activo", + "import" : "Importar perfil de activo", + "export" : "Exportar perfil de activo", + "export-failed-error" : "No se pudo exportar el perfil de activo: {{error}}", + "asset-profile-file" : "Archivo del perfil de activo", + "invalid-asset-profile-file-error" : "No se pudo importar el perfil de activo: estructura de datos inválida." + }, + "device-profile" : { + "device-profile" : "Perfil de dispositivo", + "device-profiles" : "Perfiles de dispositivos", + "all-device-profiles" : "Todos", + "add" : "Agregar perfil de dispositivo", + "edit" : "Editar perfil de dispositivo", + "device-profile-details" : "Detalles del perfil de dispositivo", + "no-device-profiles-text" : "No se encontraron perfiles de dispositivos", + "search" : "Buscar perfiles de dispositivos", + "selected-device-profiles" : "{ count, plural, =1 {1 perfil de dispositivo} other {# perfiles de dispositivos} } seleccionado(s)", + "no-device-profiles-matching" : "No se encontraron perfiles de dispositivos que coincidan con '{{entity}}'.", + "device-profile-required" : "Se requiere un perfil de dispositivo", + "idCopiedMessage" : "El ID del perfil de dispositivo ha sido copiado al portapapeles", + "set-default" : "Establecer como perfil predeterminado", + "delete" : "Eliminar perfil de dispositivo", + "copyId" : "Copiar ID del perfil de dispositivo", + "name-max-length" : "El nombre debe tener menos de 256 caracteres", + "name" : "Nombre", + "name-required" : "Se requiere el nombre.", + "type" : "Tipo de perfil", + "type-required" : "Se requiere el tipo de perfil.", + "type-default" : "Predeterminado", + "image" : "Imagen del perfil de dispositivo", + "transport-type" : "Tipo de transporte", + "transport-type-required" : "Se requiere el tipo de transporte.", + "transport-type-default" : "Predeterminado", + "transport-type-default-hint" : "Soporta transporte básico MQTT, HTTP y CoAP", + "transport-type-mqtt" : "MQTT", + "transport-type-mqtt-hint" : "Habilita configuraciones avanzadas para transporte MQTT", + "transport-type-coap" : "CoAP", + "transport-type-coap-hint" : "Habilita configuraciones avanzadas para transporte CoAP", + "transport-type-lwm2m" : "LWM2M", + "transport-type-lwm2m-hint" : "Tipo de transporte LWM2M", + "transport-type-snmp" : "SNMP", + "transport-type-snmp-hint" : "Especificar configuración de transporte SNMP", + "transport-type-http" : "HTTP", + "description" : "Descripción", + "default" : "Predeterminado", + "profile-configuration" : "Configuración del perfil", + "transport-configuration" : "Configuración de transporte", + "default-rule-chain" : "Cadena de reglas predeterminada", + "default-edge-rule-chain" : "Cadena de reglas predeterminada para el edge", + "default-edge-rule-chain-hint" : "Utilizado en el edge para procesar datos entrantes de dispositivos con este perfil", + "mobile-dashboard" : "Tablero móvil", + "mobile-dashboard-hint" : "Utilizado por la aplicación móvil como tablero de detalles del dispositivo", + "select-queue-hint" : "Seleccionar desde la lista desplegable.", + "delete-device-profile-title" : "¿Estás seguro de que deseas eliminar el perfil de dispositivo '{{deviceProfileName}}'?", + "delete-device-profile-text" : "Ten cuidado, después de la confirmación el perfil de dispositivo y todos los datos relacionados, incluyendo actualizaciones OTA asociadas, serán irrecuperables.", + "delete-device-profiles-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 perfil de dispositivo} other {# perfiles de dispositivos} }?", + "delete-device-profiles-text" : "Ten cuidado, después de la confirmación todos los perfiles de dispositivos seleccionados serán eliminados y todos los datos relacionados, incluyendo actualizaciones OTA asociadas, serán irrecuperables.", + "set-default-device-profile-title" : "¿Estás seguro de que deseas establecer el perfil de dispositivo '{{deviceProfileName}}' como predeterminado?", + "set-default-device-profile-text" : "Después de la confirmación, el perfil de dispositivo se marcará como predeterminado y se usará para nuevos dispositivos sin perfil especificado.", + "no-device-profiles-found" : "No se encontraron perfiles de dispositivos.", + "create-new-device-profile" : "¡Crear uno nuevo!", + "mqtt-device-topic-filters" : "Filtros de temas MQTT del dispositivo", + "mqtt-device-topic-filters-unique" : "Los filtros de temas MQTT deben ser únicos.", + "mqtt-device-topic-filters-spark-plug" : "Nodo EoN de Sparkplug B para MQTT.", + "mqtt-device-topic-filters-spark-plug-hint" : "Permite conexiones de nodos EoN con formato de carga útil y tema de Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names" : "Métricas SparkPlug para almacenar como atributos.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint" : "Nombres de métricas SparkPlug que se almacenarán como atributos del dispositivo. Todas las demás métricas se almacenarán como telemetría.", + "mqtt-device-payload-type" : "Carga útil del dispositivo MQTT", + "mqtt-device-payload-type-json" : "JSON", + "mqtt-device-payload-type-proto" : "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format" : "Habilitar compatibilidad con otros formatos de carga útil.", + "mqtt-enable-compatibility-with-json-payload-format-hint" : "Cuando está habilitado, la plataforma usará por defecto Protobuf. Si falla el análisis, intentará usar JSON. Útil para compatibilidad durante actualizaciones de firmware. Puede degradar el rendimiento, se recomienda deshabilitarlo cuando todos los dispositivos estén actualizados.", + "mqtt-use-json-format-for-default-downlink-topics" : "Usar formato Json para temas de bajada predeterminados", + "mqtt-use-json-format-for-default-downlink-topics-hint" : "Cuando está habilitado, se usa formato Json para enviar atributos y RPC en temas como: v1/devices/me/attributes/response/$request_id, etc. No afecta suscripciones en temas v2: v2/a/res/$request_id, etc.", + "mqtt-send-ack-on-validation-exception" : "Enviar PUBACK en error de validación de mensaje PUBLISH", + "mqtt-send-ack-on-validation-exception-hint" : "Por defecto se cierra la sesión MQTT ante errores. Con esta opción, se envía un ACK en su lugar.", + "snmp-add-mapping" : "Agregar mapeo SNMP", + "snmp-mapping-not-configured" : "No hay mapeo de OID a serie temporal/telemetría configurado", + "snmp-timseries-or-attribute-name" : "Nombre de serie temporal/atributo para mapeo", + "snmp-timseries-or-attribute-type" : "Tipo de serie temporal/atributo para mapeo", + "snmp-method-pdu-type-get-request" : "GetRequest", + "snmp-method-pdu-type-get-next-request" : "GetNextRequest", + "snmp-oid" : "OID", + "transport-device-payload-type-json" : "JSON", + "transport-device-payload-type-proto" : "Protobuf", + "mqtt-payload-type-required" : "Se requiere tipo de carga útil.", + "coap-device-type" : "Tipo de dispositivo CoAP", + "coap-device-payload-type" : "Carga útil del dispositivo CoAP", + "coap-device-type-required" : "Se requiere el tipo de dispositivo CoAP.", + "coap-device-type-default" : "Predeterminado", + "coap-device-type-efento" : "Efento NB-IoT", + "support-level-wildcards" : "Se admiten comodines de un solo nivel [+] y de múltiples niveles [#].", + "telemetry-topic-filter" : "Filtro de tema de telemetría", + "telemetry-topic-filter-required" : "Se requiere filtro de tema de telemetría.", + "attributes-topic-filter" : "Filtro de tema para publicación de atributos", + "attributes-subscribe-topic-filter" : "Filtro de tema para suscripción de atributos", + "attributes-topic-filter-required" : "Se requiere filtro de tema para publicación de atributos.", + "attributes-subscribe-topic-filter-required" : "Se requiere tema de suscripción de atributos", + "telemetry-proto-schema" : "Esquema proto de telemetría", + "telemetry-proto-schema-required" : "Se requiere esquema proto de telemetría.", + "attributes-proto-schema" : "Esquema proto de atributos", + "attributes-proto-schema-required" : "Se requiere esquema proto de atributos.", + "rpc-response-proto-schema" : "Esquema proto de respuesta RPC", + "rpc-response-proto-schema-required" : "Se requiere esquema proto de respuesta RPC.", + "rpc-response-topic-filter" : "Filtro de tema de respuesta RPC", + "rpc-response-topic-filter-required" : "Se requiere filtro de tema de respuesta RPC.", + "rpc-request-proto-schema" : "Esquema proto de solicitud RPC", + "rpc-request-proto-schema-required" : "Se requiere esquema proto de solicitud RPC.", + "rpc-request-proto-schema-hint" : "El mensaje de solicitud RPC siempre debe tener los campos: string method = 1; int32 requestId = 2; y params = 3 de cualquier tipo de dato.", + "not-valid-pattern-topic-filter" : "Filtro de tema con patrón no válido", + "not-valid-single-character" : "Uso inválido del comodín de un solo nivel", + "not-valid-multi-character" : "Uso inválido del comodín de múltiples niveles", + "single-level-wildcards-hint" : "[+] es adecuado para cualquier nivel del filtro de tema. Ej.: v1/devices/+/telemetry o +/devices/+/attributes.", + "multi-level-wildcards-hint" : "[#] puede reemplazar todo el filtro de tema y debe ser el último símbolo. Ej.: # o v1/devices/me/#.", + "alarm-rules" : "Reglas de alarma", + "alarm-rules-with-count" : "Reglas de alarma ({{count}})", + "no-alarm-rules" : "No hay reglas de alarma configuradas", + "add-alarm-rule" : "Agregar regla de alarma", + "edit-alarm-rule" : "Editar regla de alarma", + "alarm-type" : "Tipo de alarma", + "alarm-type-required" : "Se requiere tipo de alarma.", + "alarm-type-unique" : "El tipo de alarma debe ser único dentro de las reglas de alarma del perfil.", + "alarm-type-max-length" : "El tipo de alarma debe tener menos de 256 caracteres", + "create-alarm-pattern" : "Crear alarma {{alarmType}}", + "create-alarm-rules" : "Crear reglas de alarma", + "no-create-alarm-rules" : "No hay condiciones de creación configuradas", + "add-create-alarm-rule-prompt" : "Por favor, añade una regla de creación de alarma", + "clear-alarm-rule" : "Condición de limpieza de alarma", + "no-clear-alarm-rule" : "No hay condición de limpieza configurada", + "add-create-alarm-rule" : "Agregar condición de creación", + "add-clear-alarm-rule" : "Agregar condición de limpieza", + "select-alarm-severity" : "Seleccionar severidad de la alarma", + "alarm-severity-required" : "Se requiere severidad de la alarma.", + "condition-duration" : "Duración de la condición", + "condition-duration-value" : "Valor de duración", + "condition-duration-time-unit" : "Unidad de tiempo", + "condition-duration-value-range" : "El valor debe estar entre 1 y 2147483647.", + "condition-duration-value-pattern" : "El valor debe ser un número entero.", + "condition-duration-value-required" : "Se requiere valor de duración.", + "condition-duration-time-unit-required" : "Se requiere unidad de tiempo.", + "advanced-settings" : "Configuración avanzada", + "alarm-rule-additional-info" : "Información adicional", + "edit-alarm-rule-additional-info" : "Editar información adicional", + "alarm-rule-additional-info-placeholder" : "Proporcione sus comentarios y ajustes para que aparezcan en los detalles de la alarma bajo Información adicional", + "alarm-rule-additional-info-hint" : "Sugerencia: use ${keyName} para sustituir valores de atributos o claves de telemetría en la condición.", + "alarm-rule-mobile-dashboard" : "Tablero móvil", + "alarm-rule-mobile-dashboard-hint" : "Usado por la aplicación móvil como tablero de detalles de la alarma", + "alarm-rule-no-mobile-dashboard" : "No hay tablero seleccionado", + "propagate-alarm" : "Propagar alarma a entidades relacionadas", + "alarm-rule-relation-types-list" : "Tipos de relación", + "alarm-rule-relation-types-list-hint" : "Define tipos de relación para filtrar las entidades relacionadas. Si no se define, la alarma se propagará a todas las entidades relacionadas.", + "propagate-alarm-to-owner" : "Propagar alarma al propietario de la entidad (Cliente o Inquilino)", + "propagate-alarm-to-tenant" : "Propagar alarma al inquilino", + "alarm-rule-condition" : "Condición de la regla de alarma", + "enter-alarm-rule-condition-prompt" : "Por favor, agregue condición para la regla de alarma", + "edit-alarm-rule-condition" : "Editar condición de la regla de alarma", + "device-provisioning" : "Aprovisionamiento del dispositivo", + "provision-strategy" : "Estrategia de aprovisionamiento", + "provision-strategy-required" : "Se requiere estrategia de aprovisionamiento.", + "provision-strategy-disabled" : "Deshabilitada", + "provision-strategy-created-new" : "Permitir creación de nuevos dispositivos", + "provision-strategy-check-pre-provisioned" : "Verificar dispositivos preaprobados", + "provision-device-key" : "Clave de aprovisionamiento del dispositivo", + "provision-device-key-required" : "Se requiere clave de aprovisionamiento del dispositivo.", + "copy-provision-key" : "Copiar clave de aprovisionamiento", + "provision-key-copied-message" : "La clave de aprovisionamiento ha sido copiada al portapapeles", + "provision-device-secret" : "Secreto de aprovisionamiento del dispositivo", + "provision-device-secret-required" : "Se requiere secreto de aprovisionamiento del dispositivo.", + "copy-provision-secret" : "Copiar secreto de aprovisionamiento", + "provision-secret-copied-message" : "El secreto de aprovisionamiento ha sido copiado al portapapeles", + "provision-strategy-x509" : { + "certificate-chain" : "Cadena de certificados X509", + "certificate-chain-hint" : "La estrategia de certificados X.509 se utiliza para aprovisionar dispositivos mediante certificados de cliente en comunicación TLS bidireccional.", + "allow-create-new-devices" : "Crear nuevos dispositivos", + "allow-create-new-devices-hint" : "Si se selecciona, se crearán nuevos dispositivos y el certificado del cliente se usará como credencial del dispositivo.", + "certificate-value" : "Certificado en formato PEM", + "certificate-value-required" : "Se requiere certificado en formato PEM", + "cn-regex-variable" : "Variable de expresión regular CN", + "cn-regex-variable-required" : "Se requiere variable de expresión regular CN", + "cn-regex-variable-hint" : "Necesario para obtener el nombre del dispositivo desde el nombre común del certificado X509 del dispositivo." }, - "customer": { - "customer": "Cliente", - "customers": "Clientes", - "management": "Gestión de Clientes", - "dashboard": "Panel del Cliente", - "dashboards": "Paneles del Cliente", - "devices": "Dispositivos del cliente", - "entity-views": "Vistas de Entidad del cliente", - "assets": "Activos de Cliente", - "public-dashboards": "Paneles Públicos", - "public-devices": "Dispositivos Públicos", - "public-assets": "Activos Públicos", - "public-entity-views": "Vistas de Entidad Públicas", - "public-edges": "Edges públicos", - "add": "Agregar cliente", - "delete": "Borrar cliente", - "manage-customer-users": "Gestionar usuarios del cliente", - "manage-customer-devices": "Gestionar dispositivos del cliente", - "manage-customer-dashboards": "Gestionar paneles del cliente", - "manage-public-devices": "Gestionar dispositivos públicos", - "manage-public-dashboards": "Gestionar paneles públicos", - "manage-customer-assets": "Gestionar activos del cliente", - "manage-public-assets": "Gestionar activos públicos", - "manage-customer-edges": "Administrar Edges de clientes", - "manage-public-edges": "Administrar Edges públicos", - "add-customer-text": "Agregar nuevo cliente", - "no-customers-text": "No se encontraron clientes", - "customer-details": "Detalles del cliente", - "delete-customer-title": "¿Eliminar el cliente '{{customerTitle}}'?", - "delete-customer-text": "Atención, tras la confirmación el cliente será eliminado y toda la información relacionada será irrecuperable.", - "delete-customers-title": "¿Eliminar { count, plural, =1 {1 cliente} other {# clientes} }?", - "delete-customers-action-title": "Borrar { count, plural, =1 {1 cliente} other {# clientes} }", - "delete-customers-text": "Atención, tras la confirmación todos los clientes seleccionados serán eliminados y su información relacionada será irrecuperable.", - "manage-users": "Gestionar usuarios", - "manage-assets": "Gestionar activos", - "manage-devices": "Gestionar dispositivos", - "manage-dashboards": "Gestionar paneles", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "Título debe ser menor de 256", - "description": "Descripción", - "details": "Detalles", - "events": "Eventos", - "copyId": "Copiar ID de cliente", - "idCopiedMessage": "El ID de cliente se ha copiado al portapapeles", - "select-customer": "Seleccionar Cliente", - "no-customers-matching": "No se han encontrado clientes que coincidan con '{{entity}}' .", - "customer-required": "Cliente requerido", - "select-default-customer": "Seleccionar cliente por defecto", - "default-customer": "Cliente por defecto", - "default-customer-required": "Se requiere cliente por defecto para realizar debu a nivel de propietario", - "search": "Buscar clientes", - "selected-customers": "{ count, plural, =1 {1 cliente} other {# clientes} } seleccionados", - "edges": "Instancias de Edge del cliente", - "manage-edges": "Administrar Edges" + "condition" : "Condición", + "condition-type" : "Tipo de condición", + "condition-type-simple" : "Simple", + "condition-type-duration" : "Duración", + "condition-during" : "Durante {{during}}", + "condition-during-dynamic" : "Durante \"{{ attribute }}\" ({{during}})", + "condition-type-repeating" : "Repetitiva", + "condition-type-required" : "Se requiere tipo de condición.", + "condition-repeating-value" : "Cantidad de eventos", + "condition-repeating-value-range" : "La cantidad de eventos debe estar entre 1 y 2147483647.", + "condition-repeating-value-pattern" : "La cantidad de eventos debe ser un número entero.", + "condition-repeating-value-required" : "Se requiere la cantidad de eventos.", + "condition-repeat-times" : "Se repite { count, plural, =1 {1 vez} other {# veces} }", + "condition-repeat-times-dynamic" : "Se repite \"{ attribute }\" ({ count, plural, =1 {1 vez} other {# veces} })", + "schedule-type" : "Tipo de programación", + "schedule-type-required" : "Se requiere tipo de programación.", + "schedule" : "Horario", + "edit-schedule" : "Editar programación de la alarma", + "schedule-any-time" : "Activo todo el tiempo", + "schedule-specific-time" : "Activo en un horario específico", + "schedule-custom" : "Personalizado", + "schedule-day" : { + "monday" : "Lunes", + "tuesday" : "Martes", + "wednesday" : "Miércoles", + "thursday" : "Jueves", + "friday" : "Viernes", + "saturday" : "Sábado", + "sunday" : "Domingo" }, - "date": { - "last-update-n-ago": "Última actualización hace N", - "last-update-n-ago-text": "Última actualización {{ agoText }}", - "custom-date": "Fecha personalizada", - "format": "Formato", - "preview": "Previsualizar" + "schedule-days" : "Días", + "schedule-time" : "Hora", + "schedule-time-from" : "Desde", + "schedule-time-to" : "Hasta", + "schedule-days-of-week-required" : "Se debe seleccionar al menos un día de la semana.", + "create-device-profile" : "Crear nuevo perfil de dispositivo", + "import" : "Importar perfil de dispositivo", + "export" : "Exportar perfil de dispositivo", + "export-failed-error" : "No se pudo exportar el perfil de dispositivo: {{error}}", + "device-profile-file" : "Archivo del perfil de dispositivo", + "invalid-device-profile-file-error" : "No se pudo importar el perfil de dispositivo: estructura de datos inválida.", + "power-saving-mode" : "Modo de ahorro de energía", + "power-saving-mode-type" : { + "default" : "Usar modo de ahorro de energía del perfil del dispositivo", + "psm" : "Modo de ahorro de energía (PSM)", + "drx" : "Recepción discontinua (DRX)", + "edrx" : "Recepción discontinua extendida (eDRX)" }, - "datetime": { - "date-from": "Fecha desde", - "time-from": "Hora desde", - "date-to": "Fecha hasta", - "time-to": "Hora hasta" + "edrx-cycle" : "Ciclo eDRX", + "edrx-cycle-required" : "Se requiere el ciclo eDRX.", + "edrx-cycle-pattern" : "El ciclo eDRX debe ser un número entero positivo.", + "edrx-cycle-min" : "El número mínimo del ciclo eDRX es {{ min }} segundos.", + "paging-transmission-window" : "Ventana de transmisión de paginación", + "paging-transmission-window-required" : "Se requiere la ventana de transmisión de paginación.", + "paging-transmission-window-pattern" : "La ventana de transmisión debe ser un número entero positivo.", + "paging-transmission-window-min" : "El mínimo de la ventana de transmisión de paginación es {{ min }} segundos.", + "psm-activity-timer" : "Temporizador de actividad PSM", + "psm-activity-timer-required" : "Se requiere el temporizador de actividad PSM.", + "psm-activity-timer-pattern" : "El temporizador de actividad PSM debe ser un número entero positivo.", + "psm-activity-timer-min" : "El número mínimo del temporizador de actividad PSM es {{ min }} segundos.", + "lwm2m" : { + "object-list" : "Lista de objetos", + "object-list-empty" : "No se han seleccionado objetos.", + "no-objects-found" : "No se encontraron objetos.", + "no-objects-matching" : "No se encontraron objetos que coincidan con '{{object}}'.", + "model-tab" : "Modelo LWM2M", + "add-new-instances" : "Agregar nuevas instancias", + "instances-list" : "Lista de instancias", + "instances-list-required" : "Se requiere la lista de instancias.", + "instance-id-pattern" : "El ID de instancia debe ser un número entero positivo.", + "instance-id-max" : "Valor máximo de ID de instancia {{max}}.", + "instance" : "Instancia", + "resource-label" : "#ID Nombre del recurso", + "observe-label" : "Observar", + "attribute-label" : "Atributo", + "telemetry-label" : "Telemetría", + "edit-observe-select" : "Para editar observación seleccione telemetría o atributo", + "edit-attributes-select" : "Para editar atributos seleccione telemetría o atributo", + "no-attributes-set" : "No se han configurado atributos", + "key-name" : "Nombre de la clave", + "key-name-required" : "Se requiere el nombre de la clave", + "attribute-name" : "Nombre del atributo", + "attribute-name-required" : "Se requiere el nombre del atributo.", + "attribute-value" : "Valor del atributo", + "attribute-value-required" : "Se requiere el valor del atributo.", + "attribute-value-pattern" : "El valor del atributo debe ser un número entero positivo.", + "edit-attributes" : "Editar atributos: {{ name }}", + "view-attributes" : "Ver atributos: {{ name }}", + "add-attribute" : "Agregar atributo", + "edit-attribute" : "Editar atributo", + "view-attribute" : "Ver atributo", + "remove-attribute" : "Eliminar atributo", + "delete-server-text" : "Ten cuidado, después de la confirmación la configuración del servidor será irrecuperable.", + "delete-server-title" : "¿Estás seguro de que deseas eliminar el servidor?", + "mode" : "Modo de configuración de seguridad", + "bootstrap-tab" : "Bootstrap", + "bootstrap-server-legend" : "Servidor Bootstrap (ShortId...)", + "lwm2m-server-legend" : "Servidor LwM2M (ShortId...)", + "server" : "Servidor", + "short-id" : "ID corto del servidor", + "short-id-tooltip" : "ID corto del servidor. Se utiliza como enlace para asociar una instancia del objeto servidor.\nEste identificador identifica de forma única cada servidor LwM2M configurado para el cliente LwM2M.\nEl recurso DEBE establecerse cuando el recurso Bootstrap-Server tenga el valor 'false'.\nLos valores ID:0 y ID:65535 NO DEBEN usarse para identificar al servidor LwM2M.", + "short-id-tooltip-bootstrap" : "ID corto del servidor. Se utiliza como enlace para asociar una instancia del objeto servidor.\nEste identificador identifica de forma única cada servidor LwM2M configurado para el cliente LwM2M.\nEl recurso DEBE establecerse cuando el recurso Bootstrap-Server tenga el valor 'false'.", + "short-id-required" : "Se requiere el ID corto del servidor.", + "short-id-range" : "El ID corto del servidor debe estar en un rango de {{ min }} a {{ max }}.", + "short-id-pattern" : "El ID corto del servidor debe ser un número entero positivo.", + "lifetime" : "Tiempo de vida del registro del cliente", + "lifetime-required" : "Se requiere el tiempo de vida del registro del cliente.", + "lifetime-pattern" : "El tiempo de vida del registro debe ser un número entero positivo.", + "default-min-period" : "Período mínimo entre dos notificaciones (s)", + "default-min-period-tooltip" : "El valor predeterminado que debe usar el cliente LwM2M para el Período Mínimo de una Observación en ausencia de este parámetro.", + "default-min-period-required" : "Se requiere el período mínimo.", + "default-min-period-pattern" : "El período mínimo debe ser un número entero positivo.", + "notification-storing" : "Almacenamiento de notificaciones cuando está deshabilitado o fuera de línea", + "binding" : "Asociación", + "binding-type" : { + "u" : "U: El cliente es accesible a través de UDP en cualquier momento.", + "m" : "M: El cliente es accesible a través de MQTT en cualquier momento.", + "h" : "H: El cliente es accesible a través de HTTP en cualquier momento.", + "t" : "T: El cliente es accesible a través de TCP en cualquier momento.", + "s" : "S: El cliente es accesible a través de SMS en cualquier momento.", + "n" : "N: El cliente DEBE enviar la respuesta a dicha solicitud a través de una vinculación Non-IP (admitido desde LWM2M 1.1).", + "uq" : "UQ: Conexión UDP en modo en cola (no admitido desde LWM2M 1.1)", + "uqs" : "UQS: conexiones UDP y SMS activas; UDP en modo en cola, SMS en modo estándar (no admitido desde LWM2M 1.1)", + "tq" : "TQ: Conexión TCP en modo en cola (no admitido desde LWM2M 1.1)", + "tqs" : "TQS: conexiones TCP y SMS activas; TCP en modo en cola, SMS en modo estándar (no admitido desde LWM2M 1.1)", + "sq" : "SQ: Conexión SMS en modo en cola (no admitido desde LWM2M 1.1)" + }, + "binding-tooltip" : "Esta es la lista en el recurso \"binding\" del objeto del servidor LwM2M - /1/x/7.\nIndica los modos de asociación admitidos en el Cliente LwM2M.\nEste valor DEBERÍA ser igual al valor en el recurso “Supported Binding and Modes” en el Objeto de Dispositivo (/3/0/16).\nAunque se admiten múltiples transportes, solo uno puede usarse durante toda la sesión de transporte.\nPor ejemplo, si se admiten UDP y SMS, el Cliente y el Servidor LwM2M pueden elegir comunicarse a través de UDP o SMS durante toda la sesión de transporte.", + "bootstrap-server" : "Servidor Bootstrap", + "lwm2m-server" : "Servidor LwM2M", + "include-bootstrap-server" : "Incluir actualizaciones del servidor Bootstrap", + "bootstrap-update-title" : "Ya tienes configurado un Servidor Bootstrap. ¿Estás seguro de que deseas excluir las actualizaciones?", + "bootstrap-update-text" : "Ten cuidado, después de la confirmación los datos de configuración del servidor Bootstrap serán irrecuperables.", + "server-host" : "Host", + "server-host-required" : "Se requiere el host.", + "server-port" : "Puerto", + "server-port-required" : "Se requiere el puerto.", + "server-port-pattern" : "El puerto debe ser un número entero positivo.", + "server-port-range" : "El puerto debe estar en un rango de 1 a 65535.", + "server-public-key" : "Clave pública del servidor", + "server-public-key-required" : "Se requiere la clave pública del servidor.", + "client-hold-off-time" : "Tiempo de espera del cliente", + "client-hold-off-time-required" : "Se requiere el tiempo de espera del cliente.", + "client-hold-off-time-pattern" : "El tiempo de espera debe ser un número entero positivo.", + "client-hold-off-time-tooltip" : "Tiempo de espera del cliente para usar solo con el Servidor Bootstrap", + "account-after-timeout" : "Cuenta después del tiempo de espera", + "account-after-timeout-required" : "Se requiere la cuenta después del tiempo de espera.", + "account-after-timeout-pattern" : "La cuenta después del tiempo de espera debe ser un número entero positivo.", + "account-after-timeout-tooltip" : "Cuenta del Servidor Bootstrap después del valor de tiempo de espera proporcionado por este recurso.", + "server-type" : "Tipo de servidor", + "add-new-server-title" : "Agregar nueva configuración de servidor", + "add-server-config" : "Agregar configuración de servidor", + "add-lwm2m-server-config" : "Agregar servidor LwM2M", + "no-config-servers" : "No hay servidores configurados", + "others-tab" : "Otras configuraciones", + "client-strategy" : "Estrategia del cliente al conectarse", + "client-strategy-label" : "Estrategia", + "client-strategy-only-observe" : "Solo enviar solicitud Observe al cliente después de la conexión inicial", + "client-strategy-read-all" : "Leer todos los recursos y enviar solicitud Observe al cliente después del registro", + "fw-update" : "Actualización de firmware", + "fw-update-strategy" : "Estrategia de actualización de firmware", + "fw-update-strategy-data" : "Enviar actualización de firmware como archivo binario usando Objeto 19 y Recurso 0 (Data)", + "fw-update-strategy-package" : "Enviar actualización de firmware como archivo binario usando Objeto 5 y Recurso 0 (Package)", + "fw-update-strategy-package-uri" : "Generar automáticamente URL única de CoAP para descargar el paquete y enviar actualización usando Objeto 5 y Recurso 1 (Package URI)", + "sw-update" : "Actualización de software", + "sw-update-strategy" : "Estrategia de actualización de software", + "sw-update-strategy-package" : "Enviar archivo binario usando Objeto 9 y Recurso 2 (Package)", + "sw-update-strategy-package-uri" : "Generar automáticamente URL única de CoAP para descargar el paquete y enviar actualización usando Objeto 9 y Recurso 3 (Package URI)", + "fw-update-resource" : "Recurso CoAP para actualización de firmware", + "fw-update-resource-required" : "Se requiere el recurso CoAP para actualización de firmware.", + "sw-update-resource" : "Recurso CoAP para actualización de software", + "sw-update-resource-required" : "Se requiere el recurso CoAP para actualización de software.", + "config-json-tab" : "Configuración Json del perfil del dispositivo", + "attributes-name" : { + "min-period" : "Período mínimo", + "max-period" : "Período máximo", + "greater-than" : "Mayor que", + "less-than" : "Menor que", + "step" : "Paso", + "min-evaluation-period" : "Período mínimo de evaluación", + "max-evaluation-period" : "Período máximo de evaluación" + }, + "default-object-id" : "Versión de objeto predeterminado (Atributo)", + "default-object-id-ver" : { + "v1-0" : "1.0", + "v1-1" : "1.1" + } }, - "dashboard": { - "dashboard": "Panel", - "dashboards": "Paneles", - "management": "Gestión de Paneles", - "view-dashboards": "Ver Paneles", - "add": "Agregar Panel", - "assign-dashboard-to-customer": "Asignar panel(es) a cliente", - "assign-dashboard-to-customer-text": "Por favor, seleccione algún panel para asignar al Cliente.", - "assign-dashboard-to-edge-title": "Asignar panel(es) a Edge", - "assign-to-customer-text": "Por favor, seleccione algún cliente para asignar al(los) panel(es).", - "assign-to-customer": "Asignar a cliente", - "unassign-from-customer": "Desasignar del cliente", - "make-public": "Hacer panel público", - "make-private": "Hacer panel privado", - "manage-assigned-customers": "Administrar clientes asignados", - "assigned-customers": "Clientes asignados", - "assign-to-customers": "Asignar Panel / Paneles a Clientes", - "assign-to-customers-text": "Selecciona los clientes para asignar los paneles", - "unassign-from-customers": "Desasignar Panel / Paneles de clientes", - "unassign-from-customers-text": "Selecciona los clientes para desasignar los paneles", - "no-dashboards-text": "Ningún panel encontrado", - "no-widgets": "Ningún widget configurado", - "add-widget": "Agregar nuevo widget", - "add-widget-button-text": "Agregar widget", - "title": "Título", - "image": "Imagen de panel", - "mobile-app-settings": "Ajustes de aplicación móvil", - "mobile-order": "Órden de paneles en aplicación móvil", - "mobile-hide": "Ocultar panel en aplicación móvil", - "update-image": "Actualizar imagen de panel", - "take-screenshot": "Captura de pantalla", - "select-widget-title": "Seleccionar widget", - "select-widget-value": "{{title}}: seleccionar widget", - "select-widget-subtitle": "Lista de tipos de widgets disponibles", - "delete": "Eliminar panel", - "title-required": "Título requerido.", - "title-max-length": "Título debe ser menor de 256", - "description": "Descripción", - "details": "Detalles", - "dashboard-details": "Detalles del panel", - "add-dashboard-text": "Agregar nuevo panel", - "assign-dashboards": "Asignar paneles", - "assign-new-dashboard": "Asignar nuevo panel", - "assign-dashboards-text": "Asignar { count, plural, =1 {1 panel} other {# paneles} } al cliente", - "unassign-dashboards-action-text": "Desasignar { count, plural, =1 {1 panel} other {# paneles} } a los clientes", - "delete-dashboards": "Eliminar paneles", - "unassign-dashboards": "Desasignar paneles", - "unassign-dashboards-action-title": "Desasignar { count, plural, =1 {1 paneles} other {# paneles} } del cliente", - "delete-dashboard-title": "¿Eliminar el panel '{{dashboardTitle}}'?", - "delete-dashboard-text": "Atención, el panel seleccionado será eliminado y la información relacionada sera irrecuperable.", - "delete-dashboards-title": "¿Eliminar { count, plural, =1 {1 panel} other {# paneles} }?", - "delete-dashboards-action-title": "Eliminar { count, plural, =1 {1 panel} other {# paneles} }", - "delete-dashboards-text": "Atención, los paneles seleccionados serán eliminados y la información relacionada será irrecuperable.", - "unassign-dashboard-title": "¿Desasignar el panel '{{dashboardTitle}}'?", - "unassign-dashboard-text": "Tras la confirmación, el panel será desasignado y no podrá ser accesible por el cliente.", - "unassign-dashboard": "Desasignar panel", - "unassign-dashboards-title": "¿Desasignar { count, plural, =1 {1 panel} other {# paneles} }?", - "unassign-dashboards-text": "Atención, tras la confirmación los paneles seleccionados serán desasignados y no podrán ser accesibles por el cliente.", - "public-dashboard-title": "El panel ahora es público", - "public-dashboard-text": "Tu panel {{dashboardTitle}} es ahora público y podrá ser accedido desde: aquí:", - "public-dashboard-notice": "Nota: No olvides hacer públicos los dispositivos relacionados para acceder a sus datos.", - "make-private-dashboard-title": "¿Hacer el panel '{{dashboardTitle}}' privado?", - "make-private-dashboard-text": "Tras la confirmación, el panel será privado y no podrá ser accesible por otros.", - "make-private-dashboard": "Hacer panel privado", - "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", - "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", - "select-dashboard": "Seleccionar panel", - "no-dashboards-matching": "Panel '{{entity}}' no encontrado.", - "dashboard-required": "Panel requerido.", - "select-existing": "Seleccionar paneles existentes", - "create-new": "Crear nuevo panel", - "new-dashboard-title": "Nuevo título", - "open-dashboard": "Abrir panel", - "set-background": "Definir fondo", - "background-color": "Color de fondo", - "background-image": "Imagen de fondo", - "background-size-mode": "Modo tamaño de fondo", - "no-image": "No se ha seleccionado ningúna imagen", - "empty-image": "Sin imagen", - "drop-image": "Suelte una imagen o haga clic para seleccionar un archivo para cargar.", - "maximum-upload-file-size": "Tamaño máximo de fichero: {{ size }}", - "cannot-upload-file": "No es posible subir el fichero", - "settings": "Ajustes", - "columns-count": "Número de columnas", - "columns-count-required": "Número de columnas requerido.", - "min-columns-count-message": "Solo se permite un número mínimo de 10 columnas.", - "max-columns-count-message": "Solo se permite un número máximo de 1000 columnas.", - "widgets-margins": "Margen entre widgets", - "margin-required": "Valor de margen requerido.", - "min-margin-message": "0 es el valor de margen mínimo permitido.", - "max-margin-message": "50 es el valor de margen máximo permitido.", - "horizontal-margin": "Margen horizontal", - "horizontal-margin-required": "Margen horizontal requerido.", - "min-horizontal-margin-message": "Solo se permite margen horizontal mínimo de 0.", - "max-horizontal-margin-message": "Solo se permite margen horizontal máximo de 50.", - "vertical-margin": "Margen vertical", - "vertical-margin-required": "Margen vertical requerido.", - "min-vertical-margin-message": "Solo se permite margen vertical mínimo de 0.", - "max-vertical-margin-message": "Solo se permite margen vertical máximo de 50.", - "apply-outer-margin": "Aplicar márgen en los lados del diseño", - "autofill-height": "Altura diseño auto relleno", - "mobile-layout": "Ajustes del diseño móvil", - "mobile-row-height": "Altura de fila para móvil, px", - "mobile-row-height-required": "Altura de fila requerida.", - "min-mobile-row-height-message": "Sólo se permiten 5 píxeles como altura mínima de fila (móvil).", - "max-mobile-row-height-message": "Sólo se permiten 200 píxeles como altura máxima de fila (móvil).", - "title-settings": "Ajustes de título", - "display-title": "Mostrar título del panel", - "title-color": "Color del título", - "toolbar-settings": "Ajustes de la barra de herramientas", - "hide-toolbar": "Ocultar barra de herramientas", - "toolbar-always-open": "Mantener la barra de herramientas abierta", - "display-dashboards-selection": "Mostrar selección de paneles", - "display-entities-selection": "Mostrar selección de entidades", - "display-filters": "Mostrar filtros", - "display-dashboard-timewindow": "Mostrar ventana de tiempo", - "display-dashboard-export": "Mostrar exportar", - "display-update-dashboard-image": "Mostrar actualización de imagen", - "dashboard-logo-settings": "Ajustes del logotipo del panel", - "display-dashboard-logo": "Mostrar logotipo en pantalla completa", - "dashboard-logo-image": "Imagen logotipo", - "advanced-settings": "Ajustes avanzados", - "dashboard-css": "CSS del panel", - "import": "Importar panel", - "export": "Exportar panel", - "export-failed-error": "Imposible exportar panel: {{error}}", - "create-new-dashboard": "Crear nuevo panel", - "dashboard-file": "Archivar panel", - "invalid-dashboard-file-error": "Imposible importar panel: Estructura de datos inválida.", - "dashboard-import-missing-aliases-title": "Configurar alias utilizados por el panel importado", - "create-new-widget": "Crear nuevo widget", - "import-widget": "Importar widget", - "widget-file": "Archivo de widget", - "invalid-widget-file-error": "Imposible importar widget: Estructura de datos inválida.", - "widget-import-missing-aliases-title": "Configurar alias utilizados por el widget", - "open-toolbar": "Abrir toolbar del panel", - "close-toolbar": "Cerrar toolbar", - "configuration-error": "Error de configuración", - "alias-resolution-error-title": "Error de configuración de alias del panel", - "invalid-aliases-config": "No se puede encontrar ningún dispositivo que coincida con algunos de los alias de filtro.
Póngase en contacto con su administrador para resolver este problema.", - "select-devices": "Seleccionar dispositivos", - "assignedToCustomer": "Asignado al cliente", - "assignedToCustomers": "Asignado a los clientes", - "public": "Público", - "copyId": "Copiar ID del panel", - "idCopiedMessage": "El ID del panel ha sido copiado al portapapeles", - "public-link": "Link público", - "copy-public-link": "Copiar link público", - "public-link-copied-message": "El link público del panel se ha copiado al portapapeles", - "manage-states": "Administrar estados de paneles", - "states": "Estados de paneles", - "states-short": "Estados", - "search-states": "Buscar estados de paneles", - "selected-states": "{ count, plural, =1 {1 estado panel} other {# estado paneles} } seleccionados", - "edit-state": "Editar estado panel", - "delete-state": "Borrar estado panel", - "add-state": "Añadir estado panel", - "no-states-text": "No se han encontrado estados", - "state": "Estado de panel", - "state-name": "Nombre", - "state-name-required": "Se requiere nombre del estado.", - "state-id": "ID Estado", - "state-id-required": "Se requiere el ID de estado.", - "state-id-exists": "Ya existe un ID de estado.", - "is-root-state": "Estado raiz(Root)", - "delete-state-title": "Borrar estado de panel", - "delete-state-text": "Eliminar el estado de panel con nombre: '{{stateName}}'?", - "show-details": "Mostrar detalles", - "hide-details": "Ocultar detalles", - "select-state": "Seleccionar estado destino (target state)", - "state-controller": "Controlador de estados", - "search": "Buscar paneles", - "selected-dashboards": "{count, plural, =1 {1 panel} other {# paneles} } seleccionados", - "home-dashboard": "Panel principal", - "home-dashboard-hide-toolbar": "Ocultar barra de herramientas en panel principal", - "unassign-dashboard-from-edge-text": "Después de la confirmación, el tablero no será asignado y el Edge no podrá acceder a él", - "unassign-dashboards-from-edge-title": "Estas seguro de desasignar { count, plural, =1 {1 panel} other {# paneles} }?", - "unassign-dashboards-from-edge-text": "Después de la confirmación, se anulará la asignación de todos los paneles seleccionados y no serán accesibles por de Edge", - "assign-dashboard-to-edge": "Asignar panel(es) al Edge", - "assign-dashboard-to-edge-text": "Por favor selecciona los paneles para asignar al Edge", - "non-existent-dashboard-state-error": "El panel con id de estado \"{{ stateId }}\" no se ha encontrado", - "edit-mode": "Modo Edición" + "snmp" : { + "add-communication-config" : "Agregar configuración de comunicación", + "add-mapping" : "Agregar mapeo", + "authentication-passphrase" : "Frase de autenticación", + "authentication-passphrase-required" : "Se requiere la frase de autenticación.", + "authentication-protocol" : "Protocolo de autenticación", + "authentication-protocol-required" : "Se requiere el protocolo de autenticación.", + "communication-configs" : "Configuraciones de comunicación", + "community" : "Cadena de comunidad", + "community-required" : "Se requiere la cadena de comunidad.", + "context-name" : "Nombre de contexto", + "data-key" : "Clave de datos", + "data-key-required" : "Se requiere la clave de datos.", + "data-type" : "Tipo de datos", + "data-type-required" : "Se requiere el tipo de datos.", + "engine-id" : "ID del motor", + "host" : "Host", + "host-required" : "Se requiere el host.", + "oid" : "OID", + "oid-pattern" : "Formato de OID inválido.", + "oid-required" : "Se requiere el OID.", + "please-add-communication-config" : "Por favor, agregue una configuración de comunicación", + "please-add-mapping-config" : "Por favor, agregue una configuración de mapeo", + "port" : "Puerto", + "port-format" : "Formato de puerto inválido.", + "port-required" : "Se requiere el puerto.", + "privacy-passphrase" : "Frase de privacidad", + "privacy-passphrase-required" : "Se requiere la frase de privacidad.", + "privacy-protocol" : "Protocolo de privacidad", + "privacy-protocol-required" : "Se requiere el protocolo de privacidad.", + "protocol-version" : "Versión del protocolo", + "protocol-version-required" : "Se requiere la versión del protocolo.", + "querying-frequency" : "Frecuencia de consulta, ms", + "querying-frequency-invalid-format" : "La frecuencia de consulta debe ser un número entero positivo.", + "querying-frequency-required" : "Se requiere la frecuencia de consulta.", + "retries" : "Reintentos", + "retries-invalid-format" : "Los reintentos deben ser un número entero positivo.", + "retries-required" : "Se requieren los reintentos.", + "scope" : "Alcance", + "scope-required" : "Se requiere el alcance.", + "security-name" : "Nombre de seguridad", + "security-name-required" : "Se requiere el nombre de seguridad.", + "timeout-ms" : "Tiempo de espera, ms", + "timeout-ms-invalid-format" : "El tiempo de espera debe ser un número entero positivo.", + "timeout-ms-required" : "Se requiere el tiempo de espera.", + "user-name" : "Nombre de usuario", + "user-name-required" : "Se requiere el nombre de usuario." + } + }, + "dialog" : { + "close" : "Cerrar diálogo", + "error-message-title" : "Mensaje de error:", + "error-details-title" : "Detalles del error" + }, + "direction" : { + "column" : "Columna", + "row" : "Fila" + }, + "edge" : { + "edge" : "Edge", + "edge-instances" : "Instancias de edge", + "instances" : "Instancias", + "edge-file" : "Archivo de edge", + "name-max-length" : "El nombre debe tener menos de 256 caracteres", + "label-max-length" : "La etiqueta debe tener menos de 256 caracteres", + "type-max-length" : "El tipo debe tener menos de 256 caracteres", + "management" : "Gestión de Edge", + "no-edges-matching" : "No se encontraron edges que coincidan con '{{entity}}'.", + "add" : "Agregar edge", + "no-edges-text" : "No se encontraron edges", + "edge-details" : "Detalles del edge", + "add-edge-text" : "Agregar nuevo edge", + "delete" : "Eliminar edge", + "delete-edge-title" : "¿Está seguro de que desea eliminar el edge '{{edgeName}}'?", + "delete-edge-text" : "Tenga cuidado, después de la confirmación el edge y todos los datos relacionados serán irrecuperables.", + "delete-edges-title" : "¿Está seguro de que desea eliminar { count, plural, =1 {1 edge} other {# edges} }?", + "delete-edges-text" : "Tenga cuidado, después de la confirmación todos los edges seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "name" : "Nombre", + "name-starts-with" : "Nombre del edge empieza con", + "name-required" : "El nombre es obligatorio.", + "description" : "Descripción", + "details" : "Detalles", + "events" : "Eventos", + "copy-id" : "Copiar ID del edge", + "id-copied-message" : "El ID del edge se ha copiado al portapapeles", + "sync" : "Sincronizar edge", + "edge-required" : "El edge es obligatorio", + "edge-type" : "Tipo de edge", + "edge-type-required" : "Se requiere el tipo de edge.", + "event-action" : "Acción del evento", + "entity-id" : "ID de entidad", + "select-edge-type" : "Seleccionar tipo de edge", + "assign-to-customer" : "Asignar al cliente", + "assign-to-customer-text" : "Por favor, seleccione el cliente para asignar el(los) edge(s)", + "assign-edge-to-customer" : "Asignar edge(s) al cliente", + "assign-edge-to-customer-text" : "Por favor, seleccione los edges para asignar al cliente", + "assignedToCustomer" : "Asignado al cliente", + "edge-public" : "El edge es público", + "assigned-to-customer" : "Asignado a: {{customerTitle}}", + "unassign-from-customer" : "Desasignar del cliente", + "unassign-edge-title" : "¿Está seguro de que desea desasignar el edge '{{edgeName}}'?", + "unassign-edge-text" : "Después de la confirmación, el edge se desasignará y no será accesible por el cliente.", + "unassign-edges-title" : "¿Está seguro de que desea desasignar { count, plural, =1 {1 edge} other {# edges} }?", + "unassign-edges-text" : "Después de la confirmación, todos los edges seleccionados se desasignarán y no serán accesibles por el cliente.", + "make-public" : "Hacer público el edge", + "make-public-edge-title" : "¿Está seguro de que desea hacer público el edge '{{edgeName}}'?", + "make-public-edge-text" : "Después de la confirmación, el edge y todos sus datos se harán públicos y serán accesibles por otros.", + "make-private" : "Hacer privado el edge", + "public" : "Público", + "make-private-edge-title" : "¿Está seguro de que desea hacer privado el edge '{{edgeName}}'?", + "make-private-edge-text" : "Después de la confirmación, el edge y todos sus datos se harán privados y no serán accesibles por otros.", + "import" : "Importar edge", + "install-connect-instructions" : "Instrucciones de instalación y conexión", + "install-connect-instructions-edge-created" : "¡Edge creado! Revise las instrucciones de instalación y conexión", + "loading-edge-instructions" : "Cargando instrucciones del edge...", + "label" : "Etiqueta", + "load-entity-error" : "Error al cargar los datos. La entidad ha sido eliminada.", + "assign-new-edge" : "Asignar nuevo edge", + "unassign-from-edge" : "Desasignar del edge", + "edge-key" : "Clave del edge", + "copy-edge-key" : "Copiar clave del edge", + "edge-key-copied-message" : "La clave del edge se ha copiado al portapapeles", + "edge-secret" : "Secreto del edge", + "copy-edge-secret" : "Copiar secreto del edge", + "edge-secret-copied-message" : "El secreto del edge se ha copiado al portapapeles", + "manage-assets" : "Gestionar activos", + "manage-devices" : "Gestionar dispositivos", + "manage-entity-views" : "Gestionar vistas de entidad", + "manage-dashboards" : "Gestionar tableros", + "manage-rulechains" : "Gestionar cadenas de reglas", + "assets" : "Activos del edge", + "devices" : "Dispositivos del edge", + "entity-views" : "Vistas de entidad del edge", + "dashboard" : "Tablero del edge", + "dashboards" : "Tableros del edge", + "rulechain-templates" : "Plantillas de cadenas de reglas", + "edge-rulechain-templates" : "Plantillas de cadenas de reglas del edge", + "rulechains" : "Cadenas de reglas del edge", + "search" : "Buscar edges", + "selected-edges" : "{ count, plural, =1 {1 edge} other {# edges} } seleccionados", + "any-edge" : "Cualquier edge", + "no-edge-types-matching" : "No se encontraron tipos de edge que coincidan con '{{entitySubtype}}'.", + "edge-type-list-empty" : "No se seleccionaron tipos de edge.", + "edge-types" : "Tipos de edge", + "enter-edge-type" : "Introducir tipo de edge", + "deployed" : "Desplegado", + "pending" : "Pendiente", + "downlinks" : "Downlinks", + "no-downlinks-prompt" : "No se encontraron downlinks", + "sync-process-started-successfully" : "¡Proceso de sincronización iniciado con éxito!", + "missing-related-rule-chains-title" : "El edge tiene cadenas de reglas relacionadas faltantes", + "missing-related-rule-chains-text" : "Las cadenas de reglas asignadas al edge usan nodos de reglas que envían mensajes a cadenas de reglas que no están asignadas a este edge.

Lista de cadenas de reglas faltantes:
{{missingRuleChains}}", + "widget-datasource-error" : "Este widget solo admite fuente de datos de entidad EDGE", + "upgrade-instructions" : "Instrucciones de actualización", + "connected" : "Conectado", + "disconnected" : "Desconectado" + }, + "edge-event" : { + "type-dashboard" : "Tablero", + "type-asset" : "Activo", + "type-device" : "Dispositivo", + "type-device-profile" : "Perfil de dispositivo", + "type-asset-profile" : "Perfil de activo", + "type-entity-view" : "Vista de entidad", + "type-alarm" : "Alarma", + "type-rule-chain" : "Cadena de reglas", + "type-rule-chain-metadata" : "Metadatos de cadena de reglas", + "type-edge" : "Edge", + "type-user" : "Usuario", + "type-tenant" : "Inquilino", + "type-tenant-profile" : "Perfil de inquilino", + "type-customer" : "Cliente", + "type-relation" : "Relación", + "type-widgets-bundle" : "Paquete de widgets", + "type-widgets-type" : "Tipo de widget", + "type-admin-settings" : "Configuraciones de administrador", + "type-ota-package" : "Paquete OTA", + "type-queue" : "Cola", + "action-type-added" : "Agregado", + "action-type-deleted" : "Eliminado", + "action-type-updated" : "Actualizado", + "action-type-post-attributes" : "Publicar atributos", + "action-type-attributes-updated" : "Atributos actualizados", + "action-type-attributes-deleted" : "Atributos eliminados", + "action-type-timeseries-updated" : "Serie temporal actualizada", + "action-type-credentials-updated" : "Credenciales actualizadas", + "action-type-assigned-to-customer" : "Asignado al cliente", + "action-type-unassigned-from-customer" : "Desasignado del cliente", + "action-type-relation-add-or-update" : "Relación agregada o actualizada", + "action-type-relation-deleted" : "Relación eliminada", + "action-type-rpc-call" : "Llamada RPC", + "action-type-alarm-ack" : "Reconocimiento de alarma", + "action-type-alarm-clear" : "Alarma borrada", + "action-type-alarm-assigned" : "Alarma asignada", + "action-type-alarm-unassigned" : "Alarma desasignada", + "action-type-assigned-to-edge" : "Asignado al edge", + "action-type-unassigned-from-edge" : "Desasignado del edge", + "action-type-credentials-request" : "Solicitud de credenciales", + "action-type-entity-merge-request" : "Solicitud de fusión de entidad" + }, + "error" : { + "unable-to-connect" : "¡No se puede conectar al servidor! Por favor, verifique su conexión a Internet.", + "unhandled-error-code" : "Código de error no gestionado: {{errorCode}}", + "unknown-error" : "Error desconocido" + }, + "entity" : { + "entity" : "Entidad", + "entities" : "Entidades", + "entities-count" : "Cantidad de entidades", + "alarms-count" : "Cantidad de alarmas", + "aliases" : "Alias de entidad", + "aliases-short" : "Alias", + "entity-alias" : "Alias de entidad", + "unable-delete-entity-alias-title" : "No se puede eliminar el alias de entidad", + "unable-delete-entity-alias-text" : "El alias de entidad '{{entityAlias}}' no se puede eliminar ya que lo utilizan los siguientes widgets:
{{widgetsList}}", + "duplicate-alias-error" : "Alias duplicado encontrado '{{alias}}'.
Los alias de entidad deben ser únicos dentro del tablero.", + "missing-entity-filter-error" : "Falta el filtro para el alias '{{alias}}'.", + "configure-alias" : "Configurar alias '{{alias}}'", + "alias" : "Alias", + "alias-required" : "El alias de entidad es obligatorio.", + "remove-alias" : "Eliminar alias de entidad", + "add-alias" : "Agregar alias de entidad", + "edit-alias" : "Editar alias de entidad", + "entity-list" : "Lista de entidades", + "entity-type" : "Tipo de entidad", + "entity-types" : "Tipos de entidad", + "entity-type-list" : "Lista de tipos de entidad", + "any-entity" : "Cualquier entidad", + "add-entity-type" : "Agregar tipo de entidad", + "enter-entity-type" : "Ingrese el tipo de entidad", + "no-entities-matching" : "No se encontraron entidades que coincidan con '{{entity}}'.", + "no-entities-text" : "No se encontraron entidades", + "no-entity-types-matching" : "No se encontraron tipos de entidad que coincidan con '{{entityType}}'.", + "name-starts-with" : "Expresión de nombre", + "help-text" : "Use '%' según sea necesario: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter" : "Usar filtro", + "entity-list-empty" : "No hay entidades seleccionadas.", + "entity-type-list-required" : "Se debe seleccionar al menos un tipo de entidad.", + "entity-name-filter-required" : "Se requiere el filtro de nombre de entidad.", + "entity-name-filter-no-entity-matched" : "No se encontraron entidades que comiencen con '{{entity}}'.", + "all-subtypes" : "Todos", + "select-entities" : "Seleccionar entidades", + "no-aliases-found" : "No se encontraron alias.", + "no-alias-matching" : "'{{alias}}' no encontrado.", + "create-new-alias" : "¡Cree uno nuevo!", + "create-new" : "Crear nuevo", + "key" : "Clave", + "key-name" : "Nombre de la clave", + "no-keys-found" : "No se encontraron claves.", + "no-key-matching" : "'{{key}}' no encontrado.", + "create-new-key" : "¡Cree uno nuevo!", + "type" : "Tipo", + "type-required" : "El tipo de entidad es obligatorio.", + "type-device" : "Dispositivo", + "type-devices" : "Dispositivos", + "list-of-devices" : "{ count, plural, =1 {Un dispositivo} other {Lista de # dispositivos} }", + "device-name-starts-with" : "Dispositivos cuyos nombres comienzan con '{{prefix}}'", + "type-device-profile" : "Perfil de dispositivo", + "type-device-profiles" : "Perfiles de dispositivo", + "clear-selected-profiles" : "Borrar perfiles seleccionados", + "list-of-device-profiles" : "{ count, plural, =1 {Un perfil de dispositivo} other {Lista de # perfiles de dispositivo} }", + "device-profile-name-starts-with" : "Perfiles de dispositivo cuyos nombres comienzan con '{{prefix}}'", + "type-asset-profile" : "Perfil de activo", + "type-asset-profiles" : "Perfiles de activo", + "list-of-asset-profiles" : "{ count, plural, =1 {Un perfil de activo} other {Lista de # perfiles de activo} }", + "asset-profile-name-starts-with" : "Perfiles de activo cuyos nombres comienzan con '{{prefix}}'", + "type-asset" : "Activo", + "type-assets" : "Activos", + "list-of-assets" : "{ count, plural, =1 {Un activo} other {Lista de # activos} }", + "asset-name-starts-with" : "Activos cuyos nombres comienzan con '{{prefix}}'", + "type-entity-view" : "Vista de entidad", + "type-entity-views" : "Vistas de entidad", + "list-of-entity-views" : "{ count, plural, =1 {Una vista de entidad} other {Lista de # vistas de entidad} }", + "entity-view-name-starts-with" : "Vistas de entidad cuyos nombres comienzan con '{{prefix}}'", + "type-rule" : "Regla", + "type-rules" : "Reglas", + "list-of-rules" : "{ count, plural, =1 {Una regla} other {Lista de # reglas} }", + "rule-name-starts-with" : "Reglas cuyos nombres comienzan con '{{prefix}}'", + "type-plugin" : "Plugin", + "type-plugins" : "Plugins", + "list-of-plugins" : "{ count, plural, =1 {Un plugin} other {Lista de # plugins} }", + "plugin-name-starts-with" : "Plugins cuyos nombres comienzan con '{{prefix}}'", + "type-tenant" : "Inquilino", + "type-tenants" : "Inquilinos", + "list-of-tenants" : "{ count, plural, =1 {Un inquilino} other {Lista de # inquilinos} }", + "tenant-name-starts-with" : "Inquilinos cuyos nombres comienzan con '{{prefix}}'", + "type-tenant-profile" : "Perfil de inquilino", + "type-tenant-profiles" : "Perfiles de inquilino", + "list-of-tenant-profiles" : "{ count, plural, =1 {Un perfil de inquilino} other {Lista de # perfiles de inquilino} }", + "tenant-profile-name-starts-with" : "Perfiles de inquilino cuyos nombres comienzan con '{{prefix}}'", + "type-customer" : "Cliente", + "type-customers" : "Clientes", + "list-of-customers" : "{ count, plural, =1 {Un cliente} other {Lista de # clientes} }", + "customer-name-starts-with" : "Clientes cuyos nombres comienzan con '{{prefix}}'", + "type-user" : "Usuario", + "type-users" : "Usuarios", + "list-of-users" : "{ count, plural, =1 {Un usuario} other {Lista de # usuarios} }", + "user-name-starts-with" : "Usuarios cuyos nombres comienzan con '{{prefix}}'", + "type-dashboard" : "Tablero", + "type-dashboards" : "tableros", + "list-of-dashboards" : "{ count, plural, =1 {Un tablero} other {Lista de # tableros} }", + "dashboard-name-starts-with" : "Tableros cuyos nombres comienzan con '{{prefix}}'", + "type-alarm" : "Alarma", + "type-alarms" : "Alarmas", + "list-of-alarms" : "{ count, plural, =1 {Una alarma} other {Lista de # alarmas} }", + "alarm-name-starts-with" : "Alarmas cuyos nombres comienzan con '{{prefix}}'", + "type-rulechain" : "Cadena de reglas", + "type-rulechains" : "Cadenas de reglas", + "list-of-rulechains" : "{ count, plural, =1 {Una cadena de reglas} other {Lista de # cadenas de reglas} }", + "rulechain-name-starts-with" : "Cadenas de reglas cuyos nombres comienzan con '{{prefix}}'", + "type-rulenode" : "Nodo de regla", + "type-rulenodes" : "Nodos de regla", + "list-of-rulenodes" : "{ count, plural, =1 {Un nodo de regla} other {Lista de # nodos de regla} }", + "rulenode-name-starts-with" : "Nodos de regla cuyos nombres comienzan con '{{prefix}}'", + "type-current-customer" : "Cliente actual", + "type-current-tenant" : "Inquilino actual", + "type-current-user" : "Usuario actual", + "type-current-user-owner" : "Propietario del usuario actual", + "type-calculated-field" : "Campo calculado", + "type-calculated-fields" : "Campos calculados", + "type-widgets-bundle" : "Paquete de widgets", + "type-widgets-bundles" : "Paquetes de widgets", + "list-of-widgets-bundles" : "{ count, plural, =1 {Un paquete de widgets} other {Lista de # paquetes de widgets} }", + "type-widget" : "Widget", + "type-widgets" : "Widgets", + "list-of-widgets" : "{ count, plural, =1 {Un widget} other {Lista de # widgets} }", + "search" : "Buscar entidades", + "selected-entities" : "{ count, plural, =1 {1 entidad} other {# entidades} } seleccionadas", + "entity-name" : "Nombre de la entidad", + "entity-label" : "Etiqueta de la entidad", + "details" : "Detalles de la entidad", + "no-entities-prompt" : "No se encontraron entidades", + "no-data" : "No hay datos para mostrar", + "columns-to-display" : "Columnas para mostrar", + "type-api-usage-state" : "Estado de uso de API", + "type-edge" : "Edge", + "type-edges" : "Edges", + "list-of-edges" : "{ count, plural, =1 {Un edge} other {Lista de # edges} }", + "edge-name-starts-with" : "Edges cuyos nombres comienzan con '{{prefix}}'", + "version-conflict" : { + "message" : "¿Desea sobrescribir la versión existente o descartar los cambios y cargar la última versión?", + "link" : "Puede descargar su versión de {{entityType}} usando este", + "overwrite" : "Sobrescribir versión", + "discard" : "Descartar cambios" }, - "datakey": { - "settings": "Ajustes", - "general": "General", - "advanced": "Avanzado", - "key": "Clave", - "label": "Etiqueta", - "color": "Color", - "units": "Símbolo especial para mostrar junto con el valor", - "decimals": "Número de dígitos después de la coma", - "data-generation-func": "Función de generación de datos", - "use-data-post-processing-func": "Usar funcíon de post-procesamiendo de datos", - "configuration": "Ajustes de clave de datos", - "timeseries": "Serie de tiempos", - "attributes": "Atributos", - "entity-field" : "Campo de entidad", - "alarm": "Campos de alarma", - "timeseries-required": "Series de tiempo del dispositivo requerido.", - "timeseries-or-attributes-required": "Series de tiempo/Atributos requeridos.", - "alarm-fields-timeseries-or-attributes-required": "Se requieren campos de alarma o series de tiempo/atributos.", - "maximum-timeseries-or-attributes": "Máximo { count, plural, =1 {1 timeseries/atributo es permitido.} other {# timeseries/atributos son permitidos} }", - "alarm-fields-required": "Campos de alarma requeridos.", - "function-types": "Tipos de funciones", - "function-type": "Tipos de función", - "function-types-required": "Tipos de funciones requerido.", - "data-keys": "Claves de datos", - "data-key": "Clave de datos", - "data-keys-required": "Se requieren claves de datos.", - "data-key-required": "Se requiere clave de datos.", - "alarm-keys": "Claves de Alarmas", - "alarm-key": "Clave de Slarma", - "alarm-key-functions": "Funciones de claves de Alarmas", - "alarm-key-function": "Función de clave de Alarma", - "latest-keys": "Últimas claves", - "latest-key": "Última clave", - "latest-key-functions": "Funciones de últimas claves", - "latest-key-function": "Función de última clave", - "timeseries-keys": "Claves de series de tiempo", - "timeseries-key": "Clave de series de tiempo", - "timeseries-key-functions": "Funciones de series de tiempo", - "timeseries-key-function": "Función de serie de tiempo", - "maximum-function-types": "Máximo { count, plural, =1 {1 tipo de función está permitida.} other {# tipos de funciones están permitidos} }", - "time-description": "hora del valor actual", - "value-description": "el valor actual", - "prev-value-description": "resultado de la llamada anterior de la función", - "time-prev-description": "hora del valor previo", - "prev-orig-value-description": "valor original previo", - "aggregation": "Agregación", - "aggregation-type-hint-common": "Por motivos de rendimiento, el cálculo de valores agregados solamente está disponible para intervalos de tiempo fijos como \"día actual\", \"mes actual\", etc, y no está disponible para ventanas de tiempo móviles, como 'últimos 30 minutos' o 'últimas 24 horas'.", - "aggregation-type-none-hint": "Tomar último valor.", - "aggregation-type-min-hint": "Encuentra el valor mínimo de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "aggregation-type-max-hint": "Encuentra el valor máximo de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "aggregation-type-avg-hint": "Calcula el valor medio de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "aggregation-type-sum-hint": "Suma todos los valores de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "aggregation-type-count-hint": "Cuenta total de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "delta-calculation": "Cálculo delta", - "enable-delta-calculation": "Activar cálculo delta", - "enable-delta-calculation-hint": "Cuando se activa, el valor se calcula basado en los valores agregados de la ventana de tiempo seleccionada y el período de comparación seleccionado. Por razones de rendimiento, el cálculo delta solamente está disponible en las series históricas y no para los valores en tiempo real. Por ejemplo, puedes calcular el delta entre la energía consumida ayer y la energía consumida anteayer.", - "delta-calculation-result": "Resultado de cálculo delta", - "delta-calculation-result-previous-value": "Valor anterior", - "delta-calculation-result-delta-absolute": "Delta (absoluto)", - "delta-calculation-result-delta-percent": "Delta (percentil)", - "source": "Origen", - "latest": "Último", - "latest-value": "Último valor", - "delta": "delta", - "percent": "porcentaje", - "absolute": "absoluto" + "type-tb-resource" : "Recurso", + "type-tb-resources" : "Recursos", + "list-of-tb-resources" : "{ count, plural, =1 {Un recurso} other {Lista de # recursos} }", + "type-ota-package" : "Paquete OTA", + "type-rpc" : "RPC", + "type-queue" : "Cola", + "type-queue-stats" : "Estadísticas de cola", + "type-queues-stats" : "Estadísticas de colas", + "type-notification" : "Notificación", + "type-notification-rule" : "Regla de notificación", + "type-notification-rules" : "Reglas de notificación", + "list-of-notification-rules" : "{ count, plural, =1 {Una regla de notificación} other {Lista de # reglas de notificación} }", + "type-notification-target" : "Destinatario de notificación", + "type-notification-targets" : "Destinatarios de notificación", + "list-of-notification-targets" : "{ count, plural, =1 {Un destinatario de notificación} other {Lista de # destinatarios de notificación} }", + "type-notification-request" : "Solicitud de notificación", + "type-notification-template" : "Plantilla de notificación", + "type-notification-templates" : "Plantillas de notificación", + "list-of-notification-templates" : "{ count, plural, =1 {Una plantilla de notificación} other {Lista de # plantillas de notificación} }", + "link" : "enlace", + "type-oauth2-client" : "Cliente OAuth 2.0", + "type-oauth2-clients" : "Clientes OAuth 2.0", + "list-of-oauth2-clients" : "{ count, plural, =1 {Un cliente OAuth 2.0} other {Lista de # clientes OAuth 2.0} }", + "type-domain" : "Dominio", + "type-domains" : "Dominios", + "list-of-domains" : "{ count, plural, =1 {Un dominio} other {Lista de # dominios} }", + "type-mobile-app" : "Aplicación móvil", + "type-mobile-apps" : "Aplicaciones móviles", + "list-of-mobile-apps" : "{ count, plural, =1 {Una aplicación móvil} other {Lista de # aplicaciones móviles} }", + "type-mobile-app-bundle" : "Paquete móvil", + "type-mobile-app-bundles" : "Paquetes móviles", + "list-of-mobile-app-bundles" : "{ count, plural, =1 {Un paquete móvil} other {Lista de # paquetes móviles} }" + }, + "entity-field" : { + "created-time" : "Hora de creación", + "name" : "Nombre", + "type" : "Tipo", + "first-name" : "Nombre", + "last-name" : "Apellido", + "email" : "Correo electrónico", + "title" : "Título", + "country" : "País", + "state" : "Estado", + "city" : "Ciudad", + "address" : "Dirección", + "address2" : "Dirección 2", + "zip" : "Código postal", + "phone" : "Teléfono", + "label" : "Etiqueta", + "queue-name" : "Nombre de la cola", + "service-id" : "ID del servicio", + "owner-name" : "Nombre del propietario", + "owner-type" : "Tipo de propietario" + }, + "entity-view" : { + "entity-view" : "Vista de entidad", + "entity-view-required" : "La vista de entidad es obligatoria.", + "entity-views" : "Vistas de entidad", + "management" : "Gestión de vistas de entidad", + "view-entity-views" : "Ver vistas de entidad", + "entity-view-alias" : "Alias de vista de entidad", + "aliases" : "Alias de vistas de entidad", + "no-alias-matching" : "'{{alias}}' no encontrado.", + "no-aliases-found" : "No se encontraron alias.", + "no-key-matching" : "'{{key}}' no encontrado.", + "no-keys-found" : "No se encontraron claves.", + "create-new-alias" : "¡Crear uno nuevo!", + "create-new-key" : "¡Crear uno nuevo!", + "duplicate-alias-error" : "Se encontró un alias duplicado '{{alias}}'.
Los alias de vistas de entidad deben ser únicos dentro del tablero.", + "configure-alias" : "Configurar alias '{{alias}}'", + "no-entity-views-matching" : "No se encontraron vistas de entidad que coincidan con '{{entity}}'.", + "public" : "Público", + "alias" : "Alias", + "alias-required" : "El alias de vista de entidad es obligatorio.", + "remove-alias" : "Eliminar alias de vista de entidad", + "add-alias" : "Agregar alias de vista de entidad", + "name-starts-with" : "Expresión del nombre de la vista de entidad", + "help-text" : "Use '%' según sea necesario: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list" : "Lista de vistas de entidad", + "use-entity-view-name-filter" : "Usar filtro", + "entity-view-list-empty" : "No se seleccionaron vistas de entidad.", + "entity-view-name-filter-required" : "Se requiere un filtro de nombre de vista de entidad.", + "entity-view-name-filter-no-entity-view-matched" : "No se encontraron vistas de entidad que comiencen con '{{entityView}}'.", + "add" : "Agregar vista de entidad", + "entity-view-public" : "La vista de entidad es pública", + "assign-to-customer" : "Asignar al cliente", + "assign-entity-view-to-customer" : "Asignar vista(s) de entidad al cliente", + "assign-entity-view-to-customer-text" : "Seleccione las vistas de entidad que desea asignar al cliente", + "no-entity-views-text" : "No se encontraron vistas de entidad", + "assign-to-customer-text" : "Seleccione el cliente al que asignar la(s) vista(s) de entidad", + "entity-view-details" : "Detalles de la vista de entidad", + "add-entity-view-text" : "Agregar nueva vista de entidad", + "delete" : "Eliminar vista de entidad", + "assign-entity-views" : "Asignar vistas de entidad", + "assign-entity-views-text" : "Asignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } al cliente", + "delete-entity-views" : "Eliminar vistas de entidad", + "make-public" : "Hacer pública la vista de entidad", + "make-private" : "Hacer privada la vista de entidad", + "unassign-from-customer" : "Desasignar del cliente", + "unassign-entity-views" : "Desasignar vistas de entidad", + "unassign-entity-views-action-title" : "Desasignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } del cliente", + "assign-new-entity-view" : "Asignar nueva vista de entidad", + "delete-entity-view-title" : "¿Está seguro de que desea eliminar la vista de entidad '{{entityViewName}}'?", + "delete-entity-view-text" : "Tenga cuidado, después de la confirmación, la vista de entidad y todos los datos relacionados serán irrecuperables.", + "delete-entity-views-title" : "¿Está seguro de que desea eliminar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", + "delete-entity-views-action-title" : "Eliminar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }", + "delete-entity-views-text" : "Tenga cuidado, después de la confirmación, todas las vistas de entidad seleccionadas serán eliminadas y todos los datos relacionados serán irrecuperables.", + "make-public-entity-view-title" : "¿Está seguro de que desea hacer pública la vista de entidad '{{entityViewName}}'?", + "make-public-entity-view-text" : "Después de la confirmación, la vista de entidad y todos sus datos serán públicos y accesibles por otros.", + "make-private-entity-view-title" : "¿Está seguro de que desea hacer privada la vista de entidad '{{entityViewName}}'?", + "make-private-entity-view-text" : "Después de la confirmación, la vista de entidad y todos sus datos serán privados y no estarán accesibles por otros.", + "unassign-entity-view-title" : "¿Está seguro de que desea desasignar la vista de entidad '{{entityViewName}}'?", + "unassign-entity-view-text" : "Después de la confirmación, la vista de entidad será desasignada y no estará accesible por el cliente.", + "unassign-entity-view" : "Desasignar vista de entidad", + "unassign-entity-views-title" : "¿Está seguro de que desea desasignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", + "unassign-entity-views-text" : "Después de la confirmación, todas las vistas de entidad seleccionadas serán desasignadas y no estarán accesibles por el cliente.", + "entity-view-type" : "Tipo de vista de entidad", + "entity-view-type-required" : "El tipo de vista de entidad es obligatorio.", + "select-entity-view-type" : "Seleccionar tipo de vista de entidad", + "enter-entity-view-type" : "Ingresar tipo de vista de entidad", + "any-entity-view" : "Cualquier vista de entidad", + "no-entity-view-types-matching" : "No se encontraron tipos de vista de entidad que coincidan con '{{entitySubtype}}'.", + "entity-view-type-list-empty" : "No se seleccionaron tipos de vista de entidad.", + "entity-view-types" : "Tipos de vista de entidad", + "created-time" : "Hora de creación", + "name" : "Nombre", + "name-required" : "El nombre es obligatorio.", + "name-max-length" : "El nombre debe tener menos de 256 caracteres.", + "type-max-length" : "El tipo de vista de entidad debe tener menos de 256 caracteres.", + "description" : "Descripción", + "events" : "Eventos", + "details" : "Detalles", + "copyId" : "Copiar ID de la vista de entidad", + "idCopiedMessage" : "El ID de la vista de entidad se ha copiado al portapapeles", + "assignedToCustomer" : "Asignado al cliente", + "unable-entity-view-device-alias-title" : "No se puede eliminar el alias de la vista de entidad", + "unable-entity-view-device-alias-text" : "El alias del dispositivo '{{entityViewAlias}}' no puede eliminarse porque lo usan los siguientes widgets:
{{widgetsList}}", + "select-entity-view" : "Seleccionar vista de entidad", + "start-ts" : "Hora de inicio", + "end-ts" : "Hora de fin", + "date-limits" : "Límites de fecha", + "client-attributes" : "Atributos del cliente", + "shared-attributes" : "Atributos compartidos", + "server-attributes" : "Atributos del servidor", + "timeseries" : "Series temporales", + "client-attributes-placeholder" : "Atributos del cliente", + "shared-attributes-placeholder" : "Atributos compartidos", + "server-attributes-placeholder" : "Atributos del servidor", + "timeseries-placeholder" : "Series temporales", + "target-entity" : "Entidad objetivo", + "attributes-propagation" : "Propagación de atributos", + "attributes-propagation-hint" : "La vista de entidad copiará automáticamente los atributos especificados desde la entidad objetivo cada vez que guarde o actualice esta vista de entidad. Por razones de rendimiento, los atributos de la entidad objetivo no se propagan a la vista de entidad en cada cambio. Puede habilitar la propagación automática configurando un nodo de reglas \"copy to view\" y vinculando los mensajes \"Post attributes\" y \"Attributes Updated\" al nuevo nodo de reglas.", + "timeseries-data" : "Datos de series temporales", + "timeseries-data-hint" : "Configure las claves de datos de series temporales de la entidad objetivo que estarán accesibles para la vista de entidad. Estos datos de series temporales son de solo lectura.", + "search" : "Buscar vistas de entidad", + "selected-entity-views" : "{ count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } seleccionadas", + "assign-entity-view-to-edge" : "Asignar vista(s) de entidad al Edge", + "assign-entity-view-to-edge-text" : "Seleccione las vistas de entidad para asignar al Edge", + "unassign-entity-view-from-edge-title" : "¿Está seguro de que desea desasignar la vista de entidad '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text" : "Después de la confirmación, la vista de entidad será desasignada y no estará accesible por el Edge.", + "unassign-entity-views-from-edge-action-title" : "Desasignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } del Edge", + "unassign-entity-view-from-edge" : "Desasignar vista de entidad", + "unassign-entity-views-from-edge-title" : "¿Está seguro de que desea desasignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", + "unassign-entity-views-from-edge-text" : "Después de la confirmación, todas las vistas de entidad seleccionadas serán desasignadas y no estarán accesibles por el Edge." + }, + "event" : { + "event-type" : "Tipo de evento", + "events-filter" : "Filtro de eventos", + "clean-events" : "Borrar eventos", + "type-error" : "Error", + "type-lc-event" : "Evento de ciclo de vida", + "type-stats" : "Estadísticas", + "type-debug-rule-node" : "Depuración", + "type-debug-rule-chain" : "Depuración", + "type-debug-calculated-field" : "Depuración", + "arguments" : "Argumentos", + "result" : "Resultado", + "no-events-prompt" : "No se encontraron eventos", + "error" : "Error", + "alarm" : "Alarma", + "event-time" : "Hora del evento", + "server" : "Servidor", + "body" : "Cuerpo", + "method" : "Método", + "type" : "Tipo", + "metadata" : "Metadatos", + "message" : "Mensaje", + "message-id" : "ID del mensaje", + "copy-message-id" : "Copiar ID del mensaje", + "message-type" : "Tipo de mensaje", + "data-type" : "Tipo de datos", + "relation-type" : "Tipo de relación", + "data" : "Datos", + "event" : "Evento", + "status" : "Estado", + "success" : "Éxito", + "failed" : "Fallido", + "messages-processed" : "Mensajes procesados", + "max-messages-processed" : "Máximo de mensajes procesados", + "min-messages-processed" : "Mínimo de mensajes procesados", + "errors-occurred" : "Errores ocurridos", + "max-errors-occurred" : "Máximo de errores ocurridos", + "min-errors-occurred" : "Mínimo de errores ocurridos", + "min-value" : "El valor mínimo es 0.", + "all-events" : "Todos", + "has-error" : "Con error", + "entity-id" : "ID de la entidad", + "copy-entity-id" : "Copiar ID de la entidad", + "entity-type" : "Tipo de entidad", + "clear-filter" : "Limpiar filtro", + "clear-request-title" : "Borrar todos los eventos", + "clear-request-text" : "¿Está seguro de que desea borrar todos los eventos?", + "started" : "Iniciado", + "updated" : "Actualizado", + "stopped" : "Detenido" + }, + "extension" : { + "extensions" : "Extensiones", + "selected-extensions" : "{ count, plural, =1 {1 extensión} other {# extensiones} } seleccionadas", + "type" : "Tipo", + "key" : "Clave", + "value" : "Valor", + "id" : "Id", + "extension-id" : "Id de la extensión", + "extension-type" : "Tipo de extensión", + "transformer-json" : "JSON *", + "unique-id-required" : "El id de extensión actual ya existe.", + "delete" : "Eliminar extensión", + "add" : "Agregar extensión", + "edit" : "Editar extensión", + "delete-extension-title" : "¿Está seguro de que desea eliminar la extensión '{{extensionId}}'?", + "delete-extension-text" : "Tenga cuidado, después de la confirmación la extensión y todos los datos relacionados serán irrecuperables.", + "delete-extensions-title" : "¿Está seguro de que desea eliminar { count, plural, =1 {1 extensión} other {# extensiones} }?", + "delete-extensions-text" : "Tenga cuidado, después de la confirmación todas las extensiones seleccionadas serán eliminadas.", + "converters" : "Convertidores", + "converter-id" : "Id del convertidor", + "configuration" : "Configuración", + "converter-configurations" : "Configuraciones de convertidor", + "token" : "Token de seguridad", + "add-converter" : "Agregar convertidor", + "add-config" : "Agregar configuración de convertidor", + "device-name-expression" : "Expresión de nombre de dispositivo", + "device-type-expression" : "Expresión de tipo de dispositivo", + "custom" : "Personalizado", + "to-double" : "A doble", + "transformer" : "Transformador", + "json-required" : "Se requiere JSON del transformador.", + "json-parse" : "No se puede analizar el JSON del transformador.", + "attributes" : "Atributos", + "add-attribute" : "Agregar atributo", + "add-map" : "Agregar elemento de mapeo", + "timeseries" : "Series temporales", + "add-timeseries" : "Agregar serie temporal", + "field-required" : "El campo es obligatorio", + "brokers" : "Brokers", + "add-broker" : "Agregar broker", + "host" : "Host", + "port" : "Puerto", + "port-range" : "El puerto debe estar en el rango de 1 a 65535.", + "ssl" : "Ssl", + "credentials" : "Credenciales", + "username" : "Nombre de usuario", + "password" : "Contraseña", + "retry-interval" : "Intervalo de reintento en milisegundos", + "anonymous" : "Anónimo", + "basic" : "Básico", + "pem" : "PEM", + "ca-cert" : "Archivo del certificado CA *", + "private-key" : "Archivo de clave privada *", + "cert" : "Archivo del certificado *", + "no-file" : "Ningún archivo seleccionado.", + "drop-file" : "Suelte un archivo o haga clic para seleccionar un archivo para subir.", + "mapping" : "Mapeo", + "topic-filter" : "Filtro de tema", + "converter-type" : "Tipo de convertidor", + "converter-json" : "Json", + "json-name-expression" : "Expresión JSON del nombre del dispositivo", + "topic-name-expression" : "Expresión del tema del nombre del dispositivo", + "json-type-expression" : "Expresión JSON del tipo de dispositivo", + "topic-type-expression" : "Expresión del tema del tipo de dispositivo", + "attribute-key-expression" : "Expresión de la clave del atributo", + "attr-json-key-expression" : "Expresión JSON de la clave del atributo", + "attr-topic-key-expression" : "Expresión del tema de la clave del atributo", + "request-id-expression" : "Expresión del ID de solicitud", + "request-id-json-expression" : "Expresión JSON del ID de solicitud", + "request-id-topic-expression" : "Expresión del tema del ID de solicitud", + "response-topic-expression" : "Expresión del tema de respuesta", + "value-expression" : "Expresión de valor", + "topic" : "Tema", + "timeout" : "Tiempo de espera en milisegundos", + "converter-json-required" : "Se requiere JSON del convertidor.", + "converter-json-parse" : "No se puede analizar el JSON del convertidor.", + "filter-expression" : "Expresión de filtro", + "connect-requests" : "Solicitudes de conexión", + "add-connect-request" : "Agregar solicitud de conexión", + "disconnect-requests" : "Solicitudes de desconexión", + "add-disconnect-request" : "Agregar solicitud de desconexión", + "attribute-requests" : "Solicitudes de atributos", + "add-attribute-request" : "Agregar solicitud de atributo", + "attribute-updates" : "Actualizaciones de atributos", + "add-attribute-update" : "Agregar actualización de atributo", + "server-side-rpc" : "RPC del lado del servidor", + "add-server-side-rpc-request" : "Agregar solicitud de RPC del servidor", + "device-name-filter" : "Filtro de nombre de dispositivo", + "attribute-filter" : "Filtro de atributo", + "method-filter" : "Filtro de método", + "request-topic-expression" : "Expresión del tema de solicitud", + "response-timeout" : "Tiempo de espera de respuesta en milisegundos", + "topic-expression" : "Expresión del tema", + "client-scope" : "Alcance del cliente", + "add-device" : "Agregar dispositivo", + "opc-server" : "Servidores", + "opc-add-server" : "Agregar servidor", + "opc-add-server-prompt" : "Por favor agregue un servidor", + "opc-application-name" : "Nombre de la aplicación", + "opc-application-uri" : "URI de la aplicación", + "opc-scan-period-in-seconds" : "Periodo de escaneo en segundos", + "opc-security" : "Seguridad", + "opc-identity" : "Identidad", + "opc-keystore" : "Almacén de claves", + "opc-type" : "Tipo", + "opc-keystore-type" : "Tipo", + "opc-keystore-location" : "Ubicación *", + "opc-keystore-password" : "Contraseña", + "opc-keystore-alias" : "Alias", + "opc-keystore-key-password" : "Contraseña de la clave", + "opc-device-node-pattern" : "Patrón de nodo del dispositivo", + "opc-device-name-pattern" : "Patrón de nombre del dispositivo", + "modbus-server" : "Servidores/esclavos", + "modbus-add-server" : "Agregar servidor/esclavo", + "modbus-add-server-prompt" : "Por favor agregue servidor/esclavo", + "modbus-transport" : "Transporte", + "modbus-tcp-reconnect" : "Reconectar automáticamente", + "modbus-rtu-over-tcp" : "RTU sobre TCP", + "modbus-port-name" : "Nombre del puerto serie", + "modbus-encoding" : "Codificación", + "modbus-parity" : "Paridad", + "modbus-baudrate" : "Velocidad en baudios", + "modbus-databits" : "Bits de datos", + "modbus-stopbits" : "Bits de parada", + "modbus-databits-range" : "Los bits de datos deben estar en el rango de 7 a 8.", + "modbus-stopbits-range" : "Los bits de parada deben estar en el rango de 1 a 2.", + "modbus-unit-id" : "ID de unidad", + "modbus-unit-id-range" : "El ID de unidad debe estar en el rango de 1 a 247.", + "modbus-device-name" : "Nombre del dispositivo", + "modbus-poll-period" : "Periodo de sondeo (ms)", + "modbus-attributes-poll-period" : "Periodo de sondeo de atributos (ms)", + "modbus-timeseries-poll-period" : "Periodo de sondeo de series temporales (ms)", + "modbus-poll-period-range" : "El periodo de sondeo debe ser un valor positivo.", + "modbus-tag" : "Etiqueta", + "modbus-function" : "Función", + "modbus-register-address" : "Dirección de registro", + "modbus-register-address-range" : "La dirección del registro debe estar en el rango de 0 a 65535.", + "modbus-register-bit-index" : "Índice de bit del registro", + "modbus-register-bit-index-range" : "El índice de bit debe estar en el rango de 0 a 15.", + "modbus-register-count" : "Cantidad de registros", + "modbus-register-count-range" : "La cantidad de registros debe ser un valor positivo.", + "modbus-byte-order" : "Orden de bytes", + "sync" : { + "status" : "Estado", + "sync" : "Sincronizar", + "not-sync" : "No sincronizado", + "last-sync-time" : "Última sincronización", + "not-available" : "No disponible" }, - "datasource": { - "type": "Típo de fuente de datos", - "name": "Nombre", - "label": "Etiqueta", - "add-datasource-prompt": "Por favor, agrega una fuente de datos" + "export-extensions-configuration" : "Exportar configuración de extensiones", + "import-extensions-configuration" : "Importar configuración de extensiones", + "import-extensions" : "Importar extensiones", + "import-extension" : "Importar extensión", + "export-extension" : "Exportar extensión", + "file" : "Archivo de extensiones", + "invalid-file-error" : "Archivo de extensión no válido" + }, + "feature" : { + "advanced-features" : "Funciones avanzadas" + }, + "filter" : { + "add" : "Agregar filtro", + "edit" : "Editar filtro", + "name" : "Nombre del filtro", + "name-required" : "El nombre del filtro es obligatorio.", + "duplicate-filter" : "Ya existe un filtro con el mismo nombre.", + "filters" : "Filtros", + "unable-delete-filter-title" : "No se puede eliminar el filtro", + "unable-delete-filter-text" : "El filtro '{{filter}}' no puede ser eliminado ya que está en uso por los siguientes widgets:
{{widgetsList}}", + "duplicate-filter-error" : "Filtro duplicado '{{filter}}'.
Los filtros deben ser únicos dentro del tablero.", + "missing-key-filters-error" : "Faltan filtros clave para el filtro '{{filter}}'.", + "filter" : "Filtro", + "editable" : "Editable", + "no-filters-found" : "No se encontraron filtros.", + "no-filter-text" : "Ningún filtro especificado", + "add-filter-prompt" : "Por favor, agregue un filtro", + "no-filter-matching" : "'{{filter}}' no encontrado.", + "create-new-filter" : "¡Crear uno nuevo!", + "create-new" : "Crear nuevo", + "filter-required" : "El filtro es obligatorio.", + "operation" : { + "operation" : "Operación", + "equal" : "igual", + "not-equal" : "no igual", + "starts-with" : "empieza con", + "ends-with" : "termina con", + "contains" : "contiene", + "not-contains" : "no contiene", + "greater" : "mayor que", + "less" : "menor que", + "greater-or-equal" : "mayor o igual", + "less-or-equal" : "menor o igual", + "and" : "y", + "or" : "o", + "in" : "en", + "not-in" : "no en" }, - "details": { - "details": "Detalles", - "edit-mode": "Modo Edición", - "edit-json": "Editar JSON", - "toggle-edit-mode": "Ir a Modo Edición" + "ignore-case" : "ignorar mayúsculas/minúsculas", + "value" : "Valor", + "remove-filter" : "Eliminar filtro", + "duplicate-filter-action" : "Duplicar filtro", + "preview" : "Vista previa del filtro", + "no-filters" : "No hay filtros configurados", + "add-filter" : "Agregar filtro", + "add-complex-filter" : "Agregar filtro complejo", + "add-complex" : "Agregar complejo", + "complex-filter" : "Filtro complejo", + "edit-complex-filter" : "Editar filtro complejo", + "edit-filter-user-params" : "Editar parámetros del filtro del usuario", + "filter-user-params" : "Parámetros del predicado del filtro", + "user-parameters" : "Parámetros del usuario", + "display-label" : "Etiqueta para mostrar", + "order-priority" : "Prioridad de orden del campo", + "key-filter" : "Filtro de clave", + "key-filters" : "Filtros de clave", + "key-name" : "Nombre de la clave", + "key-name-required" : "El nombre de la clave es obligatorio.", + "key-type" : { + "key-type" : "Tipo de clave", + "attribute" : "Atributo", + "timeseries" : "Serie temporal", + "entity-field" : "Campo de entidad", + "constant" : "Constante", + "client-attribute" : "Atributo del cliente", + "server-attribute" : "Atributo del servidor", + "shared-attribute" : "Atributo compartido" }, - "device": { - "device": "Dispositivo", - "device-required": "Dispositivo requerido.", - "devices": "Dispositivos", - "management": "Gestión de Dispositivos", - "view-devices": "Ver Dispositivos", - "device-alias": "Alias de dispositivo", - "device-type-max-length": "El tipo de dispositivo debe ser menor de 256", - "aliases": "Alias de dispositivos", - "no-alias-matching": "'{{alias}}' no encontrado.", - "no-aliases-found": "Ningún alias encontrado.", - "no-key-matching": "'{{key}}' no encontrado.", - "no-keys-found": "Ninguna clave encontrada.", - "create-new-alias": "Crear nuevo alias!", - "create-new-key": "Crear nueva clave!", - "duplicate-alias-error": "Alias duplicado '{{alias}}'.
El alias de los dispositivos deben ser únicos dentro del panel.", - "configure-alias": "Configurar alias '{{alias}}'", - "no-devices-matching": "No se encontró dispositivo '{{entity}}'", - "alias": "Alias", - "alias-required": "Alias de dispositivo requerido.", - "remove-alias": "Eliminar alias", - "add-alias": "Agregar alias", - "name-starts-with": "Nombre empieza con", - "help-text": "Usar '%' de acuerdo a las necesidades: '%nombre_dispositivo_contiene%', '%nombre_dispositivo_termina_en', 'nombre_dispositivo_empieza_con'.", - "device-list": "Lista de dispositivos", - "use-device-name-filter": "Usar filtro", - "device-list-empty": "Ningún dispositivo seleccionado.", - "device-name-filter-required": "Nombre de filtro requerido.", - "device-name-filter-no-device-matched": "Ningún dispositivo encontrado que comience con '{{device}}'.", - "add": "Agregar dispositivo", - "assign-to-customer": "Asignar a cliente", - "assign-device-to-customer": "Asignar dispositivo(s) a Cliente", - "assign-device-to-customer-text": "Por favor, seleccione los dispositivos que serán asignados al cliente", - "assign-device-to-edge-title": "Asignar Dispositivo(s) a Edge", - "assign-device-to-edge-text": "Selecciona los dispositivos a asignar al Edge", - "make-public": "Hacer dispositivo público", - "make-private": "Hacer dispositivo privado", - "no-devices-text": "Ningún dispositivo encontrado", - "assign-to-customer-text": "Por favor, seleccione el cliente para asignar el(los) dispositivo(s)", - "device-details": "Detalles del dispositivo", - "add-device-text": "Agregar nuevo dispositivo", - "credentials": "Credenciales", - "manage-credentials": "Gestionar credenciales", - "delete": "Eliminar dispositivo", - "assign-devices": "Asignar dispositivo", - "assign-devices-text": "Asignar { count, plural, =1 {1 dispositivo} other {# dispositivos} } al cliente", - "delete-devices": "Eliminar dispositivo", - "unassign-from-customer": "Desasignar del cliente", - "unassign-devices": "Desasignar dispositivos", - "unassign-devices-action-title": "Desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} } del cliente", - "unassign-device-from-edge-title": "¿Está seguro de que desea desasignar el dispositivo '{{deviceName}}'?", - "unassign-device-from-edge-text": "Después de la confirmación, el dispositivo no será asignado y el Edge no podrá acceder a él", - "unassign-devices-from-edge": "Desasignar dispositivos del Edge", - "assign-new-device": "Asignar nuevo dispositivo", - "make-public-device-title": "¿Hacer el dispositivo '{{deviceName}}' público?", - "make-public-device-text": "Tras la confirmación, el dispositivo y la información relacionada serán públicos y podrá ser accesible por otros.", - "make-private-device-title": "¿Hacer el dispositivo '{{deviceName}}' privado?", - "make-private-device-text": "Tras la confirmación, el dispositivo y la información relacionada serán privados y no podrá ser accesible por otros.", - "view-credentials": "Ver credenciales", - "delete-device-title": "¿Eliminar el dispositivo '{{deviceName}}'?", - "delete-device-text": "Atención, tras la confirmación los dispositivos serán eliminados y la información relacionada será irrecuperable.", - "delete-devices-title": "¿Eliminar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", - "delete-devices-action-title": "Eliminar { count, plural, =1 {1 dispositivo} other {# dispositivos} }", - "delete-devices-text": "Atención, tras la confirmación los dispositivos seleccionados serán eliminados y la información relacionada será irrecuperable.", - "unassign-device-title": "¿Desasignar el dispositivo '{{deviceName}}'?", - "unassign-device-text": "Tras la confirmación, el dispositivo será desasignado y no podrá ser accesible por el cliente.", - "unassign-device": "Desasignar dispositivo", - "unassign-devices-title": "¿Desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", - "unassign-devices-text": "Tras la confirmación, los dispositivos seleccionados serán desasignados y no podrán ser accedidos por el cliente.", - "device-credentials": "Credenciales del dispositivo", - "loading-device-credentials": "Cargando credenciales del dispositivo...", - "credentials-type": "Tipo de credenciales", - "access-token": "Tóken de acceso", - "access-token-required": "Tóken de acceso requerido.", - "access-token-invalid": "Tóken de acceso debe tener entre 1 a 32 caracteres.", - "certificate-pem-format": "Certificado en formato PEM", - "certificate-pem-format-required": "Certificado requerido.", - "copy-access-token": "Copiar Access token", - "copy-certificate": "Copiar Certificado", - "copy-client-id": "Copiar ID Cliente", - "copy-user-name": "Copiar Nombre de usuario", - "copy-password": "Copiar Contraseña", - "generate-client-id": "Generar ID Cliente", - "generate-user-name": "Generar Nombre de usuario", - "generate-password": "Generar contraseña", - "generate-access-token": "Generar Access Token", - "lwm2m-security-config": { - "identity": "Identidad Cliente", - "identity-required": "Identidad Cliente requerida.", - "identity-tooltip": "La identidad PSK es un identificador PSK arbitrario hasta 128 bytes, como se describe en el estándar [RFC7925].\nEl identificador PSK DEBE ser convertido a un string y después codificado en octetos usando UTF-8.", - "client-key": "Clave Cliente", - "client-key-required": "Clave Cliente requerida.", - "client-key-tooltip-prk": "La clave RPK o id DEBE estar conforme al estándar [RFC7250] y codificada a un formato Base64!", - "client-key-tooltip-psk": "La clave PSK DEBE estar conforme al estándar [RFC4279] y en formato HexDec de: 32, 64 o 128 caracteres!", - "endpoint": "Nombre Endpoint Cliente", - "endpoint-required": "Nombre Endpoint requerido.", - "client-public-key": "Clave pública Cliente", - "client-public-key-hint": "Si la clave pública está vacía, se usará el certificado de confianza", - "client-public-key-tooltip": "La clave pública X509 debe estar codificada en formato DER X509v3 y soportar exclusivamente el algoritmo EC y codificada en formato Base64!", - "mode": "Modo de Seguridad", - "client-tab": "Configuración de Seguridad del cliente", - "client-certificate": "Certificado de Cliente", - "bootstrap-tab": "Cliente Bootstrap", - "bootstrap-server": "Servidor Bootstrap", - "lwm2m-server": "Servidor LwM2M", - "client-publicKey-or-id": "Id o clave pública de cliente", - "client-publicKey-or-id-required": "Id o clave pública requerida.", - "client-publicKey-or-id-tooltip-psk": "La clave pública PSK es un identificador PSK arbitrario hasta 128 bytes, como se describe en el estándar [RFC7925].\nEl identificador PSK DEBE ser convertido a un string y después codificado en octetos usando UTF-8.", - "client-publicKey-or-id-tooltip-rpk": "La clave pública RPK o id DEBE estar conforme al estándar [RFC7250] y codificada a un formato Base64!", - "client-publicKey-or-id-tooltip-x509": "La clave pública X509 debe estar codificada en formato DER X509v3 y soportar exclusivamente el algoritmo EC y codificada en formato Base64", - "client-secret-key": "Clave secreta de Cliente", - "client-secret-key-required": "Clave secreta requerida.", - "client-secret-key-tooltip-psk": "La clave PSK debe ser en el estándar [RFC4279] y en formato HexDec de: 32, 64 o 128 caracteres!", - "client-secret-key-tooltip-prk": "La clave RPK debe estar en formato PKCS_8 (Codificación DER, estándar [RFC5958]) y luego codificada en formato Base64!", - "client-secret-key-tooltip-x509": "La clave X509 debe estar en formato PKCS_8 (Codificación DER, estándar [RFC5958]) y luego codificada en formato Base64!" - }, - "client-id": "ID Cliente", - "client-id-pattern": "Contiene carácter inválido.", - "user-name": "Nombre Usuario", - "user-name-required": "Se requiere nombre de usuario.", - "client-id-or-user-name-necessary": "El ID Cliente y/o el Nombre de usuario son necesarios", - "password": "Contraseña", - "secret": "Secreto", - "secret-required": "Secreto requerido.", - "device-type": "Tipo de dispositivo", - "device-type-required": "Tipo de dispositivo requerido.", - "select-device-type": "Seleccionar tipo de dispositivo", - "enter-device-type": "Entrar tipo de dispositivo", - "any-device": "Cualquier dispositivo", - "no-device-types-matching": "No hay tipos de dispositivo que coincidan con '{{entitySubtype}}' .", - "device-type-list-empty": "No hay tipos de dispositivo seleccionados.", - "device-types": "Tipos de dispositivo", - "name": "Nombre", - "name-required": "El nombre es requerido.", - "name-max-length": "El nombre debe ser menor de 256", - "label-max-length": "La etiqueta debe ser menor de 256", - "description": "Descripción", - "label": "Etiqueta", - "events": "Eventos", - "details": "Detalles", - "copyId": "Copiar ID de dispositivo", - "copyAccessToken": "Copiar access token", - "copy-mqtt-authentication": "Copiar credenciales MQTT", - "idCopiedMessage": "Id del dispositivo copiado al portapapeles", - "accessTokenCopiedMessage": "Access token del dispositivo copiado al portapapeles", - "mqtt-authentication-copied-message": "Los datos de autenticación MQTT se han copiado al portapapeles", - "assignedToCustomer": "Asignado al cliente", - "unable-delete-device-alias-title": "Imposible eliminar alias del dispositivo", - "unable-delete-device-alias-text": "Alias '{{deviceAlias}}' no puede ser eliminado. Esta siendo usado por el(los) widget(s):
{{widgetsList}}", - "is-gateway": "Es gateway", - "overwrite-activity-time": "Sobreescribir hora de actividad para el dispositivo conectado", - "device-filter": "Filtro de dispositivo", - "device-filter-title": "Filtro de dispositivo", - "filter-title": "Filtro", - "device-state": "Estado de dispositivo", - "state": "Estado", - "any": "Cualquier", - "active": "Activo", - "inactive": "Inactivo", - "public": "Público", - "device-public": "El dispositivo es público", - "select-device": "Seleccionar dispositivo", - "import": "Importar dispositivo", - "device-file": "Archivo de dispositivo", - "search": "Buscar dispositivos", - "selected-devices": "{ count, plural, =1 {1 dispositivo} other {# dispositivos} } seleccionados", - "device-configuration": "Configuración del dispositivo", - "transport-configuration": "Configuración del transporte", - "wizard": { - "device-details": "Detalles del dispositivo" - }, - "unassign-devices-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, =1 {1 dispositivo} other {# dispositivos} }?", - "unassign-devices-from-edge-text": "Después de la confirmación, todos los dispositivos seleccionados quedarán sin asignar y el Edge no podrá acceder a ellos.", - "time": "Hora", - "connectivity": { - "check-connectivity": "Probar conectividad", - "device-created-check-connectivity": "Dispositivo creado. Prueba de conectividad.", - "loading-check-connectivity-command": "Cargando comandos...", - "use-following-instructions": "Usa las siguientes instrucciones para enviar telemetrías usando comandos shell", - "execute-following-command": "Ejecuta el comando siguietne", - "install-curl-windows": "Desde Windows 10 b17063, el comando cURL está disponible por defecto", - "install-curl-macos": "Desde Mac OS X 10.2 6C115 (Jaguar), el comando cURL está disponible por defecto", - "install-mqtt-windows": "Usa las instrucciones para descargar, instalar y configurar mosquitto_pub", - "install-coap-client": "Usa las instrucciones para descargar, instalar y configurar coap-client", - "install-necessary-client-tools": "Instala las herramientas de cliente", - "mqtts-x509-command": "Usa la documentación para conectar el dispositivo vía MQTT con autorización X509", - "coaps-x509-command": "Usa la documentación para conectar el dispositivo vía CoAP sobre DTLS con autorización X509", - "snmp-command": "Usa la siguiente documentación para conectar el dispositivo vía SNMP.", - "sparkplug-command": "Usa la siguiente documentación para conectar el dispositivo vía MQTT Sparkplug.", - "lwm2m-command": "Usa la siguiente documentación para conectar el dispositivo vía LWM2M." - } + "value-type" : { + "value-type" : "Tipo de valor", + "string" : "Cadena", + "numeric" : "Numérico", + "boolean" : "Booleano", + "date-time" : "Fecha y hora" }, - "asset-profile": { - "asset-profile": "Perfil de activo", - "asset-profiles": "Perfiles de activos", - "all-asset-profiles": "Todos", - "add": "Añadir perfil de activo", - "edit": "Editar perfil de activo", - "asset-profile-details": "Detalles de perfil de activo", - "no-asset-profiles-text": "No se encontraron perfiles de activo", - "search": "Buscar perfiles de activo", - "selected-asset-profiles": "{ count, plural, =1 {1 perfil} other {# perfiles} } seleccionados", - "no-asset-profiles-matching": "No se encontraron perfiles que coincidan con '{{entity}}'.", - "asset-profile-required": "Se requiere perfil de activo", - "idCopiedMessage": "El ID de perfil, ha sido copiado al portapapeles", - "set-default": "Hace perfil de activo por defecto", - "delete": "Borrar perfil de activo", - "copyId": "Copiar ID de perfil de activo", - "name-max-length": "El nombre debe ser menor de 256", - "new-device-profile-name": "Nombre de perfil de activo", - "new-device-profile-name-required": "Se requiere nombre de perfil", - "name": "Nombre", - "name-required": "Se requiere nombre", - "image": "Imagen de perfil de activo", - "description": "Descripción", - "default": "Por defecto", - "default-rule-chain": "Cadena de reglas por defecto", - "default-edge-rule-chain": "Cadena de reglas por defecto en Edge", - "default-edge-rule-chain-hint": "Usado en Edge, como cadena de reglas para procesas los datos recibidos para el perfil de activo.", - "mobile-dashboard": "Panel móvil", - "mobile-dashboard-hint": "Usado en la aplicación móvil, como un panel para los detalles del activo", - "select-queue-hint": "Selecciona desde el desplegable.", - "delete-asset-profile-title": "Quieres borrar el perfil de activo '{{assetProfileName}}'?", - "delete-asset-profile-text": "Atención, tras la confirmación, el perfil de activo será borrado y todas su información relacionada será irrecuperable.", - "delete-asset-profiles-title": "Quieres borrar { count, plural, =1 {1 perfil} other {# perfiles} }?", - "delete-asset-profiles-text": "Atención, tras la confirmación, todos los perfiles seleccionados serán borrados y su información relacionada será irrecuperable.", - "set-default-asset-profile-title": "Quieres establecer el perfil '{{assetProfileName}}' como perfil por defecto?", - "set-default-asset-profile-text": "Tras la confirmación, el perfil se marcara por defecto y será usada por los nuevos activos en los que no se especifique perfil.", - "no-asset-profiles-found": "No se han encontrado perfiles de activos.", - "create-new-asset-profile": "Crear uno nuevo!", - "create-asset-profile": "Crear un nuevo perfil de activo", - "import": "Importar perfil de activo", - "export": "Exportar perfil de activo", - "export-failed-error": "No ha sido posible exportar el perfil de activo: {{error}}", - "asset-profile-file": "Fichero de perfil de activo", - "invalid-asset-profile-file-error": "No ha sido posible importar el perfil de activo: Estructura de datos inválida." + "value-type-required" : "Se requiere el tipo de valor de la clave.", + "key-value-type-change-title" : "¿Está seguro de que desea cambiar el tipo de valor de la clave?", + "key-value-type-change-message" : "Si confirma, todos los filtros de clave ingresados se eliminarán.", + "no-key-filters" : "No hay filtros de clave configurados", + "add-key-filter" : "Agregar filtro de clave", + "remove-key-filter" : "Eliminar filtro de clave", + "edit-key-filter" : "Editar filtro de clave", + "date" : "Fecha", + "time" : "Hora", + "current-tenant" : "Tenant actual", + "current-customer" : "Cliente actual", + "current-user" : "Usuario actual", + "current-device" : "Dispositivo actual", + "default-value" : "Valor predeterminado", + "default-comma-separated-values" : "Valores predeterminados separados por comas", + "dynamic-source-type" : "Tipo de fuente dinámica", + "dynamic-value" : "Valor dinámico", + "no-dynamic-value" : "Sin valor dinámico", + "source-attribute" : "Atributo de origen", + "switch-to-dynamic-value" : "Cambiar a valor dinámico", + "switch-to-default-value" : "Cambiar a valor predeterminado", + "inherit-owner" : "Heredar del propietario", + "source-attribute-not-set" : "Si el atributo de origen no está establecido" + }, + "fullscreen" : { + "expand" : "Expandir a pantalla completa", + "exit" : "Salir de pantalla completa", + "toggle" : "Alternar modo de pantalla completa", + "fullscreen" : "Pantalla completa" + }, + "function" : { + "function" : "Función" + }, + "gateway" : { + "gateway-name" : "Nombre del gateway", + "gateway-name-required" : "Se requiere el nombre del gateway.", + "gateways" : "Gateways", + "create-new-gateway" : "Crear nuevo gateway", + "create-new-gateway-text" : "¿Está seguro de que desea crear un nuevo gateway con el nombre: '{{gatewayName}}'?", + "launch-command" : "Comando de lanzamiento", + "no-gateway-found" : "No se encontró ningún gateway.", + "no-gateway-matching" : "'{{item}}' no encontrado." + }, + "grid" : { + "delete-item-title" : "¿Está seguro de que desea eliminar este elemento?", + "delete-item-text" : "Tenga cuidado, después de la confirmación este elemento y todos los datos relacionados serán irrecuperables.", + "delete-items-title" : "¿Está seguro de que desea eliminar { count, plural, =1 {1 elemento} other {# elementos} }?", + "delete-items-action-title" : "Eliminar { count, plural, =1 {1 elemento} other {# elementos} }", + "delete-items-text" : "Tenga cuidado, después de la confirmación todos los elementos seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "add-item-text" : "Agregar nuevo elemento", + "no-items-text" : "No se encontraron elementos", + "item-details" : "Detalles del elemento", + "delete-item" : "Eliminar elemento", + "delete-items" : "Eliminar elementos", + "scroll-to-top" : "Desplazar hacia arriba" + }, + "help" : { + "goto-help-page" : "Ir a la página de ayuda", + "show-help" : "Mostrar ayuda" + }, + "home" : { + "home" : "Inicio", + "profile" : "Perfil", + "logout" : "Cerrar sesión", + "menu" : "Menú", + "avatar" : "Avatar", + "open-user-menu" : "Abrir menú de usuario" + }, + "file-input" : { + "browse-file" : "Buscar archivo", + "browse-files" : "Buscar archivos" + }, + "image" : { + "gallery" : "Galería de imágenes", + "search" : "Buscar imagen", + "selected-images" : "{ count, plural, =1 {1 imagen} other {# imágenes} } seleccionadas", + "created-time" : "Hora de creación", + "name" : "Nombre", + "name-required" : "El nombre es obligatorio.", + "resolution" : "Resolución", + "size" : "Tamaño", + "system" : "Sistema", + "download-image" : "Descargar imagen", + "export-image" : "Exportar imagen a JSON", + "import-image" : "Importar imagen desde JSON", + "upload-image" : "Subir imagen", + "edit-image" : "Editar imagen", + "image-details" : "Detalles de la imagen", + "no-images" : "No se encontraron imágenes", + "delete-image" : "Eliminar imagen", + "delete-image-title" : "¿Está seguro de que desea eliminar la imagen '{{imageTitle}}'?", + "delete-image-text" : "Tenga cuidado, después de la confirmación la imagen será irrecuperable.", + "delete-images-title" : "¿Está seguro de que desea eliminar { count, plural, =1 {1 imagen} other {# imágenes} }?", + "delete-images-text" : "Tenga cuidado, después de la confirmación todas las imágenes seleccionadas serán eliminadas y todos los datos relacionados serán irrecuperables.", + "list-mode" : "Vista de lista", + "grid-mode" : "Vista de cuadrícula", + "image-preview" : "Vista previa de imagen", + "update-image" : "Actualizar imagen", + "export-failed-error" : "No se pudo exportar la imagen: {{error}}", + "image-json-file" : "Archivo JSON de imagen", + "invalid-image-json-file-error" : "No se pudo importar la imagen desde JSON: estructura de datos inválida.", + "image-is-in-use" : "La imagen está siendo utilizada por otras entidades", + "images-are-in-use" : "Las imágenes están siendo utilizadas por otras entidades", + "image-is-in-use-text" : "La imagen '{{title}}' no fue eliminada porque está siendo utilizada por las siguientes entidades:", + "images-are-in-use-text" : "No se eliminaron todas las imágenes porque están siendo utilizadas por otras entidades.
Puede ver las entidades referenciadas haciendo clic en el botón Referencias en la fila correspondiente.
Si aún desea eliminar estas imágenes, selecciónelas en la tabla inferior y haga clic en el botón Eliminar seleccionadas.", + "delete-image-in-use-text" : "Si aún desea eliminar la imagen, haga clic en el botón Eliminar de todos modos.", + "system-entities" : "Entidades del sistema:", + "entities" : "entidades:", + "references" : "Referencias", + "include-system-images" : "Incluir imágenes del sistema", + "clear-image" : "Borrar imagen", + "no-image" : "Sin imagen", + "no-image-selected" : "Ninguna imagen seleccionada", + "browse-from-gallery" : "Buscar en la galería", + "set-link" : "Establecer enlace", + "image-link" : "Enlace de imagen", + "link" : "Enlace", + "copy-image-link" : "Copiar enlace de imagen", + "embed-image" : "Incrustar imagen", + "embed-to-html" : "Incrustar en HTML", + "embed-to-html-hint" : "Esta función hará que el enlace esté disponible para cualquier usuario no autorizado.", + "embed-to-html-text" : "Utilizando el siguiente fragmento de código, puede incrustar una imagen en componentes basados en HTML plano.
Estos componentes incluyen widgets de tarjeta HTML, funciones de contenido de celda, etc.", + "embed-to-angular-template" : "Incrustar en plantilla HTML de Angular", + "embed-to-angular-template-text" : "Utilizando el siguiente fragmento de código, puede incrustar una imagen en la plantilla HTML de Angular que será utilizada por los componentes.
Estos componentes incluyen el widget de Markdown, sección HTML en el editor de widgets, acciones personalizadas, etc." + }, + "image-input" : { + "drop-images-or" : "Arrastra y suelta imágenes o", + "drag-and-drop" : "Arrastrar y soltar", + "or" : "o", + "browse" : "Buscar", + "no-images" : "No hay imágenes seleccionadas", + "images" : "imágenes" + }, + "import" : { + "no-file" : "No se ha seleccionado ningún archivo", + "drop-file" : "Suelta un archivo JSON o haz clic para seleccionar un archivo para subir.", + "drop-json-file-or" : "Arrastra y suelta un archivo JSON o", + "drop-file-csv" : "Suelta un archivo CSV o haz clic para seleccionar un archivo para subir.", + "drop-file-csv-or" : "Arrastra y suelta un archivo CSV o", + "column-value" : "Valor", + "column-title" : "Título", + "column-example" : "Valor de ejemplo", + "column-key" : "Clave de atributo/telemetría", + "credentials" : "Credenciales", + "csv-delimiter" : "Delimitador CSV", + "csv-first-line-header" : "La primera línea contiene nombres de columnas", + "csv-update-data" : "Actualizar atributos/telemetría", + "details" : "Detalles", + "import-csv-number-columns-error" : "Un archivo debe contener al menos dos columnas", + "import-csv-invalid-format-error" : "Formato de archivo inválido. Línea: '{{line}}'", + "column-type" : { + "name" : "Nombre", + "type" : "Tipo", + "label" : "Etiqueta", + "column-type" : "Tipo de columna", + "client-attribute" : "Atributo del cliente", + "shared-attribute" : "Atributo compartido", + "server-attribute" : "Atributo del servidor", + "timeseries" : "Serie temporal", + "entity-field" : "Campo de entidad", + "access-token" : "Token de acceso", + "x509" : "X.509", + "mqtt" : { + "client-id" : "ID del cliente MQTT", + "user-name" : "Nombre de usuario MQTT", + "password" : "Contraseña MQTT" + }, + "lwm2m" : { + "client-endpoint" : "Nombre del cliente endpoint LwM2M", + "security-config-mode" : "Modo de configuración de seguridad LwM2M", + "client-identity" : "Identidad del cliente LwM2M", + "client-key" : "Clave del cliente LwM2M", + "client-cert" : "Clave pública del cliente LwM2M", + "bootstrap-server-security-mode" : "Modo de seguridad del servidor bootstrap LwM2M", + "bootstrap-server-secret-key" : "Clave secreta del servidor bootstrap LwM2M", + "bootstrap-server-public-key-id" : "Clave pública o ID del servidor bootstrap LwM2M", + "lwm2m-server-security-mode" : "Modo de seguridad del servidor LwM2M", + "lwm2m-server-secret-key" : "Clave secreta del servidor LwM2M", + "lwm2m-server-public-key-id" : "Clave pública o ID del servidor LwM2M" + }, + "snmp" : { + "host" : "Host SNMP", + "port" : "Puerto SNMP", + "version" : "Versión SNMP (v1, v2c o v3)", + "community-string" : "Cadena de comunidad SNMP" + }, + "isgateway" : "Es Gateway", + "activity-time-from-gateway-device" : "Hora de actividad desde dispositivo gateway", + "description" : "Descripción", + "routing-key" : "Clave del Edge", + "secret" : "Secreto del Edge" }, - "device-profile": { - "device-profile": "Perfil de dispositivo", - "device-profiles": "Perfiles de dispositivo", - "all-device-profiles": "Todos", - "add": "Añadir perfil de dispositivo", - "edit": "Editar perfil de dispositivo", - "device-profile-details": "Detalles de perfil de dispositivo", - "no-device-profiles-text": "No se encontraron perfiles", - "search": "Buscar perfiles", - "selected-device-profiles": "{ count, plural, =1 {1 perfil} other {# perfiles} } seleccionados", - "no-device-profiles-matching": "No existe perfil que conincida con '{{entity}}'.", - "device-profile-required": "Se requiere perfil de dispositivo", - "idCopiedMessage": "Se ha copiado el ID de perfil al portapapeles", - "set-default": "Hacer perfil por defecto", - "delete": "Borrar perfil de dispositivo", - "copyId": "Copiar ID de perfil", - "name-max-length": "El nombre debe ser menor de 256", - "name": "Nombre", - "name-required": "Se requiere nombre.", - "type": "Tipo de perfil", - "type-required": "Se requiere tipo de perfil.", - "type-default": "Por defecto", - "image": "Imagen del perfil de dispositivo", - "transport-type": "Tipo de transporte", - "transport-type-required": "Se requiere tipo de transporte.", - "transport-type-default": "Por defecto", - "transport-type-default-hint": "Soporta transportes por MQTT básico, HTTP y CoAP", - "transport-type-mqtt": "MQTT", - "transport-type-mqtt-hint": "Activa ajustes avanzados de transporte MQTT", - "transport-type-coap": "CoAP", - "transport-type-coap-hint": "Activa ajustes avanzados del transporte CoAP", - "transport-type-lwm2m": "LWM2M", - "transport-type-lwm2m-hint": "Transporte LWM2M", - "transport-type-snmp": "SNMP", - "transport-type-snmp-hint": "Configuración transporte SNMP", - "transport-type-http": "HTTP", - "description": "Descripción", - "default": "Defecto", - "profile-configuration": "Configuración de perfil", - "transport-configuration": "Configuración de transporte", - "default-rule-chain": "Cadena de reglas por defecto", - "default-edge-rule-chain": "Cadena de reglas por defecto en Edge", - "default-edge-rule-chain-hint": "Usado en Edge, como cadena de reglas para procesas los datos recibidos para el perfil de dispositivo.", - "mobile-dashboard": "Panel móvil", - "mobile-dashboard-hint": "Usado por la aplicación móvil como panel de detalle", - "select-queue-hint": "Selecciona desde el desplegable o añade un nombre personalizado.", - "delete-device-profile-title": "Eliminar el perfil '{{deviceProfileName}}'?", - "delete-device-profile-text": "Atención, tras la confirmación el perfil y todos sus datos serán borrados e irrecuperables.", - "delete-device-profiles-title": "Eliminar { count, plural, =1 {1 perfil} other {# perfiles} }?", - "delete-device-profiles-text": "Atención, tras la confirmación los perfiles seleccionados y todos sus datos serán borrados e irrecuperables.", - "set-default-device-profile-title": "Establecer el perfil '{{deviceProfileName}}' como perfil por defecto?", - "set-default-device-profile-text": "Tras la confirmación, el perfil será marcado como por defecto y será usado por todos los nuevos dispositivos que no tengan perfil especificado.", - "no-device-profiles-found": "No se encontraron perfiles.", - "create-new-device-profile": "Crear un nuevo perfil!", - "mqtt-device-topic-filters": "Filtros de topic MQTT", - "mqtt-device-topic-filters-unique": "Los filtros de topic de dispositivo MQTT deben ser únicos.", - "mqtt-device-topic-filters-spark-plug": "Nodo MQTT Sparkplug B Edge of Network (EoN).", - "mqtt-device-topic-filters-spark-plug-hint": "Permitir conexiones desde los nodos EoN con payload B y su formato de temas (topics).", - "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "Métricas SparkPlug para grabar como atributos.", - "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Nombre de las métricas SparkPlug que se grabarán como atributos de dispositivo. Las métricas restantes, se grabarán como telemetría.", - "mqtt-device-payload-type": "Payload de dispositivo MQTT", - "mqtt-device-payload-type-json": "JSON", - "mqtt-device-payload-type-proto": "Protobuf", - "mqtt-enable-compatibility-with-json-payload-format": "Activar compatibilidad con otros formatos de payload.", - "mqtt-enable-compatibility-with-json-payload-format-hint": "Si se activa, la plataforma usará un formato de payload Protobuf por defecto. Si el parseo falla, la plataforma intentará usar el formato JSON. Útil para retrocompatibilidad durante actualizaciones de firmware. Por ejemplo, si la versión inicial de firmware usa JSON y una versión posterior usa Protobuf. Durante el proceso de actualización de firmware a los dispositivos, se requiere soportar Protobuf y JSON al mismo tiempo. El modo de retrocompatibilidad introduce una pequeña degradación en el rendimiento, es recomendable desactivarlo una vez que todos los dispositivos estén actualizados.", - "mqtt-use-json-format-for-default-downlink-topics": "Usar formato JSON para los downlink", - "mqtt-use-json-format-for-default-downlink-topics-hint": "Cuando esté activado, la plataforma usará el formato JSON para enviar atributos y llamadas RPC a través de los siguientes temas (topics): v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, v1/devices/me/rpc/request/$request_id, v1/devices/me/rpc/response/$request_id. Este ajuste, no afectará a las subscripciones de atributos y a los mensajes RPC que se envíen usando los nuevos temas v2: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id. Donde $request_id es un número entero para identificar la transacción.", - "mqtt-send-ack-on-validation-exception": "Enviar PUBACK en error de validación al publicar (PUBLISH)", - "mqtt-send-ack-on-validation-exception-hint": "Por defecto, la plataforma cerrará la sesión MQTT cuando haya un fallo de validación. Si está activado, la plataforma enviará un mensaje tipo PUBACK, en lugar de cerrar la sesión.", - "snmp-add-mapping": "Añadir mapeado SNMP", - "snmp-mapping-not-configured": "No hay mapeado OID a series de tiempo/telemetría configurado", - "snmp-timseries-or-attribute-name": "Nombre Series de tiempo/nombre atributo para mapeado", - "snmp-timseries-or-attribute-type": "Tipo Series de tiempo/nombre atributo para mapeado", - "snmp-method-pdu-type-get-request": "GetRequest", - "snmp-method-pdu-type-get-next-request": "GetNextRequest", - "snmp-oid": "OID", - "transport-device-payload-type-json": "JSON", - "transport-device-payload-type-proto": "Protobuf", - "mqtt-payload-type-required": "Se requiere tipo de Payload.", - "coap-device-type": "Tipo de dispositivo CoAP", - "coap-device-payload-type": "Payload dispositivo CoAP", - "coap-device-type-required": "Se requiere tipo de dispositivo CoAP.", - "coap-device-type-default": "Por defecto", - "coap-device-type-efento": "Efento NB-IoT", - "support-level-wildcards": "Se soportan los wilcards únicos [+] y multi-nivel [#].", - "telemetry-topic-filter": "Filtro de topic en telemetría", - "telemetry-topic-filter-required": "Se requiere filtro de topic (telemetría).", - "attributes-topic-filter": "Filtro de tema (topic) en publicación de atributos", - "attributes-subscribe-topic-filter": "Filtro de tema (topic) en suscripción de atributos", - "attributes-topic-filter-required": "Se requiere filtro de publicación.", - "attributes-subscribe-topic-filter-required": "Se requiere filtro de suscripción", - "telemetry-proto-schema": "Proto esquema de telemetría", - "telemetry-proto-schema-required": "Se requiere proto esquema de telemetría.", - "attributes-proto-schema": "Proto esquema de atributos", - "attributes-proto-schema-required": "Se requiere proto esquema de atributos.", - "rpc-response-proto-schema": "Proto esquema de respuesta RPC", - "rpc-response-proto-schema-required": "Se requiere proto esquema de respuesta RPC.", - "rpc-response-topic-filter": "Filtro de topic de respuesta RPC", - "rpc-response-topic-filter-required": "Se requiere filtro de topic respuesta RPC.", - "rpc-request-proto-schema": "Proto esquema de petición RPC", - "rpc-request-proto-schema-required": "Se requiere proto esquema de petición RPC.", - "rpc-request-proto-schema-hint": "Las peticiones RPC deben contener siempre los siguientes campos: string method = 1; int32 requestId = 2; y params = 3 para cualquier tipo de datoas.", - "not-valid-pattern-topic-filter": "No es un patrón de filtro válido", - "not-valid-single-character": "Uso inválido de wildcard único", - "not-valid-multi-character": "Uso inválido de wildcard multi-nivel", - "single-level-wildcards-hint": "[+] es adecuado para cualquier nivel. Ej.: v1/devices/+/telemetry o +/devices/+/attributes.", - "multi-level-wildcards-hint": "[#] puede reemplazar el mismo filtro y debe ser el último símbolo del topic. Ej.: # o v1/devices/me/#.", - "alarm-rules": "Reglas de alarma", - "alarm-rules-with-count": "Reglas de alarma ({{count}})", - "no-alarm-rules": "No hay reglas de alarma configuradas", - "add-alarm-rule": "Añadir regla de alarma", - "edit-alarm-rule": "Editar regla de alarma", - "alarm-type": "Tipo de alarma", - "alarm-type-required": "Se requiere tipo de alarma.", - "alarm-type-unique": "El tipo de alarma, debe ser único dentro de las reglas de alarma del perfil de dispositivo.", - "alarm-type-max-length": "El tipo de alarma debe ser menor de 256", - "create-alarm-pattern": "Crear alarma {{alarmType}}", - "create-alarm-rules": "Crear reglas de alarma", - "no-create-alarm-rules": "No hay condiciones de creación de alarma configuradas", - "add-create-alarm-rule-prompt": "Por favor, añade una regla de alarma", - "clear-alarm-rule": "Borrar regla de alarma", - "no-clear-alarm-rule": "No hay condiciones de borrado de alarma configuradas", - "add-create-alarm-rule": "Añadir crear condición (activar alarma)", - "add-clear-alarm-rule": "Añair borrar condición (limpiar alarma)", - "select-alarm-severity": "Selecciona severidad de alarma", - "alarm-severity-required": "Se requiere especificar severidad de alarma.", - "condition-duration": "Duración de condición", - "condition-duration-value": "Valor de duración", - "condition-duration-time-unit": "Unidad de tiempo", - "condition-duration-value-range": "El valor debe estar en un rango desde 1 a 2147483647.", - "condition-duration-value-pattern": "El valor de duración debe ser un número entero.", - "condition-duration-value-required": "Se requiere valor de duración.", - "condition-duration-time-unit-required": "Se requiere una unidad de tiempo.", - "advanced-settings": "Ajustes avanzados", - "alarm-rule-additional-info-hint": "Ayuda: usa ${nombredeClave} para sustituir los valores de atributos o telemetrías usadas en la condición de la regla de alarma.", - "alarm-rule-mobile-dashboard": "Panel Móvil", - "alarm-rule-mobile-dashboard-hint": "Usado por la aplicación móvil como panel de detalle de alarmas", - "alarm-rule-no-mobile-dashboard": "No hay panel seleccionado", - "propagate-alarm": "Propagar alarma", - "alarm-rule-relation-types-list": "Tipos de relación para propagar", - "alarm-rule-relation-types-list-hint": "Si no está seleccionado 'propagar relaciones', las alarmas serán propagadas sin filtrar por relación.", - "propagate-alarm-to-owner": "Propagar alarma al propietario de la entidad (Cliente o Administrador)", - "propagate-alarm-to-tenant": "Propagar alarma a Administrador", - "alarm-rule-condition": "Condiciones de regla de alarma", - "enter-alarm-rule-condition-prompt": "Por favor, añade una condición de alarma", - "edit-alarm-rule-condition": "Editar condición de alarma", - "device-provisioning": "Aprovisionamiento de dispositivos", - "provision-strategy": "Estrategia de aprovisionamiento", - "provision-strategy-required": "Se requiere estrategia de aprovisionamiento.", - "provision-strategy-disabled": "Desactivado", - "provision-strategy-created-new": "Permitir crear nuevos dispositivos", - "provision-strategy-check-pre-provisioned": "Revisar dispositivos pre-aprovisionados", - "provision-device-key": "Clave de aprovisionamiento", - "provision-device-key-required": "Se requiere clave de aprovisionamiento.", - "copy-provision-key": "Copiar clave de aprovisionamiento", - "provision-key-copied-message": "La clave de aprovisionamiento se ha copiado al portapapeles", - "provision-device-secret": "Secreto de aprovisionamiento", - "provision-device-secret-required": "Se requiere secreto de aprovisionamiento.", - "copy-provision-secret": "Copiar secreto de aprovisionamiento", - "provision-secret-copied-message": "Se ha copiado el secreto de aprovisionamiento al portapapeles", - "provision-strategy-x509": { - "certificate-chain": "Cadena de certificados X509", - "certificate-chain-hint": "La estrategia de certificados X.509 se utiliza para aprovisionar dispositivos mediante certificados de cliente en comunicación TLS bidireccional.", - "allow-create-new-devices": "Crear nuevos dispositivos", - "allow-create-new-devices-hint": "Si se selecciona, los nuevos dispositivos se crearán y el certificado del cliente se usara como credenciales del dispositivo.", - "certificate-value": "Certificado en formato PEM", - "certificate-value-required": "Se requiere certificado en formato PEM", - "cn-regex-variable": "Variable de expresión regular CN", - "cn-regex-variable-required": "Se requiere una variable de expresión regular", - "cn-regex-variable-hint": "Requerido para obtener el nombre del dispositivo desde el CN del certificado." - }, - "condition": "Condición", - "condition-type": "Tipo de condición", - "condition-type-simple": "Simple", - "condition-type-duration": "Duración", - "condition-during": "Durante {{during}}", - "condition-during-dynamic": "Durante \"{{ attribute }}\" ({{during}})", - "condition-type-repeating": "Repetitiva", - "condition-type-required": "Se requiere tipo de condición.", - "condition-repeating-value": "Nº de eventos", - "condition-repeating-value-range": "El Nº de eventos debe estar en un rango de 1 a 2147483647.", - "condition-repeating-value-pattern": "Nº de eventos debe ser un número entero.", - "condition-repeating-value-required": "Se requiere Nº de eventos.", - "condition-repeat-times": "Repetición { count, plural, =1 {1 vez} other {# veces} }", - "condition-repeat-times-dynamic": "Repetición \"{ atributo }\" ({ count, plural, =1 {1 vez} other {# veces} })", - "schedule-type": "Tipo de horario", - "schedule-type-required": "Tipo de horario requerido.", - "schedule": "Horario", - "edit-schedule": "Editar horario de alarma", - "schedule-any-time": "Siempre activo", - "schedule-specific-time": "Activo en una hora específica", - "schedule-custom": "Personalizado", - "schedule-day": { - "monday": "Lunes", - "tuesday": "Martes", - "wednesday": "Miércoles", - "thursday": "Jueves", - "friday": "Viernes", - "saturday": "Sábado", - "sunday": "Domingo" - }, - "schedule-days": "Días", - "schedule-time": "Hora", - "schedule-time-from": "Desde", - "schedule-time-to": "Hasta", - "schedule-days-of-week-required": "Seleccionar por lo menos un día de la semana.", - "create-device-profile": "Crear un nuevo perfil de dispositivo", - "import": "Importar perfil de dispositivo", - "export": "Exportar perfil de dispositivo", - "export-failed-error": "No ha sido posible exportar el perfil de dispositivo: {{error}}", - "device-profile-file": "Archivo de perfil de dispositivo", - "invalid-device-profile-file-error": "No ha sido posible importar perfil de dispositivo: Estructura de datos inválida.", - "power-saving-mode": "Modo de ahorro de energía (PSM)", - "power-saving-mode-type": { - "default": "Usar el del perfil del dispositivo", - "psm": "Power Saving Mode (PSM)", - "drx": "Discontinuous Reception (DRX)", - "edrx": "Extended Discontinuous Reception (eDRX)" - }, - "edrx-cycle": "Ciclo eDRX", - "edrx-cycle-required": "Se requiere ciclo eDRX.", - "edrx-cycle-pattern": "El ciclo eDRX debe ser un número entero positivo.", - "edrx-cycle-min": "El mínimo de ciclo eDRX es de {{ min }} segundos.", - "paging-transmission-window": "Ventana de transmisión (Paging Transmission Window)", - "paging-transmission-window-required": "Se requiere ventana de transmisión (Paging transmission window).", - "paging-transmission-window-pattern": "Ventana de transmision debe ser un número entero positivo.", - "paging-transmission-window-min": "El mínimo de ventana de transmisión es de {{ min }} segundos.", - "psm-activity-timer": "Tiempo Actividad PSM (PSM Activity Timer)", - "psm-activity-timer-required": "Se requiere el tiempo de actividad PSM.", - "psm-activity-timer-pattern": "Tiempo de actividad PSM debe ser un número entero positivo.", - "psm-activity-timer-min": "El tiempo de actividad PSM mínimo es de {{ min }} segundos.", - "lwm2m": { - "object-list": "Lista de objetos", - "object-list-empty": "No hay objetos seleccionados.", - "no-objects-found": "No se encontraron objetos.", - "no-objects-matching": "No hay objetos que coincidan con '{{object}}'.", - "model-tab": "Modelo LWM2M", - "add-new-instances": "Añadir nueva instancia", - "instances-list": "Lista de Instancias", - "instances-list-required": "Se requiere lista de instancias.", - "instance-id-pattern": "El id de instancia debe ser un número entero positivo.", - "instance-id-max": "El id máximo de instancia es {{max}}.", - "instance": "Instancia", - "resource-label": "#ID Nombre Recurso", - "observe-label": "Observar", - "attribute-label": "Atributo", - "telemetry-label": "Telemetría", - "edit-observe-select": "Para editar observar, selecciona telemetría o atributo", - "edit-attributes-select": "Para editar atributo, selecciona telemetría o atributo", - "no-attributes-set": "No hay atributos ajustados", - "key-name": "Nombre de clave", - "key-name-required": "Se requiere nombre de clave", - "attribute-name": "Nombre Atributo", - "attribute-name-required": "Se requiere nombre de atributo.", - "attribute-value": "Valor de atributo", - "attribute-value-required": "Se requiere valor de atributo.", - "attribute-value-pattern": "El valor del atributo debe ser un número entero positivo.", - "edit-attributes": "Edita atributos: {{ name }}", - "view-attributes": "Ver atributos: {{ name }}", - "add-attribute": "Añadir atributo", - "edit-attribute": "Editar atributo", - "view-attribute": "Ver atributos", - "remove-attribute": "Borrar atributos", - "delete-server-text": "Atención, tras la confirmación la configuración del servidor se borrará y será irrecuperable.", - "delete-server-title": "Estas seguro de borrar el servidor?", - "mode": "Configuración de seguridad", - "bootstrap-tab": "Bootstrap", - "bootstrap-server-legend": "Servidor Bootstrap (ShortId...)", - "lwm2m-server-legend": "Servidor LwM2M (ShortId...)", - "server": "Servidor", - "short-id": "Short server ID", - "short-id-tooltip": "Id corto del servidor. Usado como enlace para asociar las instancias de objetos del servidor.\nEste identificador sirve para identificar únicamente cada servidor LwM2M configurado para el cliente LwM2M.\nLos recursos DEBEN ser ajustados cuando el servidor Bootstrap tenga un valor de 'false'.\nLos valores ID:0 and ID:65535 NO DEBEN ser usados para identificar al servidor LwM2M.", - "short-id-required": "Se requiere Short server ID.", - "short-id-range": "Short server ID debe estar en un rango de {{ min }} a {{ max }}.", - "short-id-pattern": "Short server ID debe ser un número entero positivo.", - "lifetime": "Ciclo de vida registro de cliente (Registration Lifetime)", - "lifetime-required": "Se requiere ciclo de vida.", - "lifetime-pattern": "Ciclo de vida debe ser un número entero positivo.", - "default-min-period": "Periodo mínimo entre notificaciones (s)", - "default-min-period-tooltip": "Valor predeterminado que el cliente LwM2M debe usar para el período mínimo de una Observación en ausencia de éste parámetro incluido en una observación.", - "default-min-period-required": "Período mímino requerido.", - "default-min-period-pattern": "El período mínimo debe ser un número entero positivo.", - "notification-storing": "Grabado de notificaciones cuando esté desactivado u offline", - "binding": "Binding", - "binding-type": { - "u": "U: El cliente es alcanzable por UDP en cualquier momento.", - "m": "M: El cliente es alcanzable por MQTT en cualquier momento.", - "h": "H: El cliente es alcanzable por HTTP en cualquier momento.", - "t": "T: El cliente es alcanzable por TCP en cualquier momento.", - "s": "S: El cliente es alcanzable por SMS en cualquier momento.", - "n": "N: El cliente DEBE enviar las respuestas a las peticiones sobre el modo Non-IP (soportado desde la versión LWM2M 1.1).", - "uq": "UQ: Conexión UDP en modo de cola (no soportado desde la versión LWM2M 1.1)", - "uqs": "UQS: Conexiones UDP y SMS activas, UDP en modo cola, SMS en modo estándar (no soportado desde la versión LWM2M 1.1)", - "tq": "TQ: Conexión TCP en modo de cola (no soportado desde la versión LWM2M 1.1)", - "tqs": "TQS: Conexiones TCP y SMS activas, TCP en modo cola, SMS en modo estándar (no soportado desde la versión LWM2M 1.1)", - "sq": "SQ: Conexión SMS en modo de cola (no soportado desde la versión LWM2M 1.1)" - }, - "binding-tooltip": "Esta es la lista del recurso \"binding\" del objeto de servidor LwM2M - /1/x/7.\nIndica los modos de enlace admitidos por el cliente LwM2M.\nEste valor DEBE ser el mismo que el valor reflejado en el recurso “Supported Binding and Modes” en el objeto (/3/0/16) del dispositivo.\nAunque se admiten varios transportes, solo se puede utilizar un tipo de binding durante toda la sesión.\nPor ejemplo, si UDP y SMS son admitidos, el servidor o el cliente LwM2M, pueden optar por comunicarse a través de UDP o SMS durante toda la sesión de transporte.", - "bootstrap-server": "Servidor Bootstrap", - "lwm2m-server": "Servidor LwM2M", - "include-bootstrap-server": "Incluir actualizaciónes del servidor Bootstrap", - "bootstrap-update-title": "Ya has configurado un servidor Bootstrap. Estás seguro de que quieres excluir las actualizaciones?", - "bootstrap-update-text": "Atención, tras la confirmación la configuración del servidor Bootstrap será irrecuperable.", - "server-host": "Host", - "server-host-required": "Se requiere Host.", - "server-port": "Puerto", - "server-port-required": "Se requiere Puerto.", - "server-port-pattern": "El puerto debe ser un número entero positivo.", - "server-port-range": "El puerto debe comprender en el rango 1 a 65535.", - "server-public-key": "Clave Pública del Servidor", - "server-public-key-required": "Se requiere la Clave Pública del servidor.", - "client-hold-off-time": "Tiempo de espera (Hold Off Time)", - "client-hold-off-time-required": "Se requiere tiempo de espera.", - "client-hold-off-time-pattern": "El tiempo de espera debe ser un número entero positivo.", - "client-hold-off-time-tooltip": "El tiempo de espera se usa sólamente con un servidor Bootstrap", - "account-after-timeout": "Cuenta tras el tiempo de espera", - "account-after-timeout-required": "Se requiere Cuenta tras el tiempo de espera.", - "account-after-timeout-pattern": "Cuenta tras el tiempo de espera debe ser un número entero positivo.", - "account-after-timeout-tooltip": "Cuenta tras el tiempo de espera otorgado por este recurso.", - "server-type": "Tipo de servidor", - "add-new-server-title": "Añadir nueva configuración", - "add-server-config": "Añadir configuración de servidor", - "add-lwm2m-server-config": "Añadir Servidor LwM2M", - "no-config-servers": "No hay servidores configurados", - "others-tab": "Otros ajustes", - "client-strategy": "Estrategia del cliente en la conexión", - "client-strategy-label": "Estrategia", - "client-strategy-only-observe": "Realizar un request para observar al cliente tras la conexión inicial", - "client-strategy-read-all": "Leer todos los recursos y realizar un request para observar al cliente tras el registro", - "fw-update": "Actualización de Firmware", - "fw-update-strategy": "Estrategia de actualización de Firmware", - "fw-update-strategy-data": "Enviar actualización de firmware como un fichero binario usando el Objeto 19 y el Recurso 0 (Data)", - "fw-update-strategy-package": "Enviar actualización de firmware como un fichero binario usando el Objeto 5 y el Recurso 0 (Package)", - "fw-update-strategy-package-uri": "Auto-generar una URL CoAP única para la descarga del paquete y enviarla usando el Objeto 5 y el Recurso 1 (Package URI)", - "sw-update": "Actualización de Software", - "sw-update-strategy": "Estrategia de Actualización de Software", - "sw-update-strategy-package": "Enviar como un fichero binario usando el Objeto 9 y el Recurso 2 (Package)", - "sw-update-strategy-package-uri": "Auto-generar una URL CoAP única para la descarga del paquete y enviarla usando el Objeto 9 y el Recurso 3 (Package URI)", - "fw-update-resource": "Recurso CoAP Actualización de Firmware", - "fw-update-resource-required": "Se requiere el Recurso CoAP Actualización de Firmware.", - "sw-update-resource": "Recurso CoAP Actualización de Software", - "sw-update-resource-required": "Se requiere el Recurso CoAP Actualización de Software.", - "config-json-tab": "Configuracion Json Perfil de dispositivo", - "attributes-name": { - "min-period": "Período mínimo", - "max-period": "Período máximo", - "greater-than": "Mayor que", - "less-than": "Menor que", - "step": "Paso", - "min-evaluation-period": "Período mínimo de evaluación", - "max-evaluation-period": "Período máximo de evaluación" - } - }, - "snmp": { - "add-communication-config": "Añadir configuración de comunicaciones", - "add-mapping": "Añadir mapeado", - "authentication-passphrase": "Frase de contraseña", - "authentication-passphrase-required": "Se requiere frase de contraseña.", - "authentication-protocol": "Protocolo de autenticación", - "authentication-protocol-required": "Se requiere Protocolo de autenticación.", - "communication-configs": "Configuracion de comunicaciones", - "community": "Community", - "community-required": "Se requiere Community.", - "context-name": "Nombre de contexto", - "data-key": "Clave de datos", - "data-key-required": "Se requiere clave de datos.", - "data-type": "Tipo de datos", - "data-type-required": "Se requiere tipo de datos.", - "engine-id": "Engine ID", - "host": "Host", - "host-required": "Se requiere Host.", - "oid": "OID", - "oid-pattern": "Formato OID inválido.", - "oid-required": "Se requiere OID.", - "please-add-communication-config": "Por favor, añadir configuración de comunicación", - "please-add-mapping-config": "Por favor, añadir configuración de mapeado", - "port": "Puerto", - "port-format": "Formato de puerto inválido.", - "port-required": "Se requiere puerto.", - "privacy-passphrase": "Frase de clave de privacidad", - "privacy-passphrase-required": "Se requiere Frase de clave de privacidad.", - "privacy-protocol": "Protocolo privacidad", - "privacy-protocol-required": "Se requiere Protocolo privacidad.", - "protocol-version": "Versión protocolo", - "protocol-version-required": "Se requiere versión protocolo.", - "querying-frequency": "Frecuencia de peticiones, ms", - "querying-frequency-invalid-format": "Frecuencia de peticiones debe ser un número entero positivo.", - "querying-frequency-required": "Se requiere frecuencia de peticiones.", - "retries": "Reintentos", - "retries-invalid-format": "Reintentos debe ser un número entero positivo.", - "retries-required": "Se requiere reintentos.", - "scope": "Alcance", - "scope-required": "Se requiere alcance.", - "security-name": "Nombre seguridad", - "security-name-required": "Se requiere Nombre seguridad.", - "timeout-ms": "Timeout, ms", - "timeout-ms-invalid-format": "Timeout debe ser un número entero positivo.", - "timeout-ms-required": "Se requiere timeout.", - "user-name": "Usuario", - "user-name-required": "Se requiere usuario." - } + "stepper-text" : { + "select-file" : "Seleccionar un archivo", + "configuration" : "Configuración de importación", + "column-type" : "Seleccionar tipo de columnas", + "creat-entities" : "Creando nuevas entidades" }, - "dialog": { - "close": "Cerrar diálogo", - "error-message-title": "Mensaje de error:", - "error-details-title": "Detalles de error" + "message" : { + "create-entities" : "{{count}} nuevas entidades fueron creadas exitosamente.", + "update-entities" : "{{count}} entidades fueron actualizadas exitosamente.", + "error-entities" : "Hubo un error al crear {{count}} entidades." + } + }, + "scada" : { + "symbols" : "Símbolos SCADA", + "search" : "Buscar símbolo", + "selected-symbols" : "{ count, plural, =1 {1 símbolo} other {# símbolos} } seleccionados", + "download-symbol" : "Descargar símbolo SCADA", + "export-symbol" : "Exportar símbolo SCADA a JSON", + "import-symbol" : "Importar símbolo SCADA desde JSON", + "upload-symbol" : "Subir símbolo SCADA", + "update-symbol" : "Actualizar símbolo SCADA", + "edit-symbol" : "Editar símbolo SCADA", + "symbol-details" : "Detalles del símbolo SCADA", + "mode-svg" : "SVG", + "mode-xml" : "XML", + "no-symbols" : "No se encontraron símbolos", + "show-hidden-elements" : "Mostrar elementos ocultos", + "hide-hidden-elements" : "Ocultar elementos ocultos", + "delete-symbol" : "Eliminar símbolo SCADA", + "delete-symbol-title" : "¿Estás seguro de que deseas eliminar el símbolo SCADA '{{imageTitle}}'?", + "delete-symbol-text" : "Ten cuidado, después de la confirmación el símbolo SCADA será irrecuperable.", + "delete-symbols-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 símbolo SCADA} other {# símbolos SCADA} }?", + "delete-symbols-text" : "Ten cuidado, después de la confirmación todos los símbolos SCADA seleccionados serán eliminados y todos los datos relacionados se volverán irrecuperables.", + "include-system-symbols" : "Incluir símbolos del sistema", + "symbol-preview" : "Vista previa del símbolo", + "general" : "General", + "tags" : "Etiquetas", + "properties" : "Propiedades", + "title" : "Título", + "description" : "Descripción", + "search-tags" : "Buscar etiquetas", + "widget-size" : "Tamaño del widget", + "cols" : "columnas", + "rows" : "filas", + "state-render-function" : "Función de renderizado de estado", + "preview" : "Vista previa", + "preview-widget-action-text" : "¡Acción del widget '{{type}}' invocada con éxito!", + "no-symbol" : "Sin símbolo SCADA", + "no-symbol-selected" : "Ningún símbolo SCADA seleccionado", + "clear-symbol" : "Limpiar símbolo SCADA", + "browse-symbol-from-gallery" : "Buscar símbolo SCADA en la galería", + "zoom-in" : "Acercar", + "zoom-out" : "Alejar", + "create-widget" : "Crear widget", + "create-widget-from-symbol" : "Crear widget desde símbolo SCADA", + "hidden" : "oculto", + "tag" : { + "tag" : "Etiqueta", + "on-click-action" : "Acción al hacer clic", + "no-tags" : "No hay etiquetas configuradas", + "delete-tag-text" : "¿Estás seguro de que deseas eliminar la etiqueta
{{tag}} del elemento {{elementType}}?", + "update-tag" : "Actualizar etiqueta", + "enter-tag" : "Introducir etiqueta", + "tag-settings" : "Configuración de etiqueta", + "remove-tag" : "Eliminar etiqueta", + "add-tag" : "Agregar etiqueta" }, - "direction": { - "column": "Columna", - "row": "Fila" + "behavior" : { + "behavior" : "Comportamiento", + "id" : "Id", + "name" : "Nombre", + "type" : "Tipo", + "no-behaviors" : "No hay comportamientos configurados", + "add-behavior" : "Agregar comportamiento", + "type-action" : "Acción", + "type-value" : "Valor", + "type-widget-action" : "Acción de widget", + "behavior-settings" : "Configuración del comportamiento", + "remove-behavior" : "Eliminar comportamiento", + "hint" : "Pista", + "group-title" : "Título del grupo", + "value-type" : "Tipo de valor", + "default-value" : "Valor por defecto", + "true-label" : "Etiqueta verdadero", + "false-label" : "Etiqueta falso", + "state-label" : "Etiqueta de estado", + "default-payload" : "Carga útil por defecto", + "not-unique-behavior-ids-error" : "¡Los Ids de comportamiento deben ser únicos!", + "default-settings" : "Configuración predeterminada" }, - "edge": { - "edge": "Edge", - "edge-instances": "Instancias de Edge", - "instances": "Instancias", - "edge-file": "Archivo de Edge", - "name-max-length": "El nombre debe ser menor de 256", - "label-max-length": "La etiqueta debe ser menor de 256", - "type-max-length": "El tipo debe ser menor de 256", - "management": "Gestión de Edges", - "no-edges-matching": "No se encontraron Edges que coincidan con '{{entity}}'", - "add": "Agregar Edge", - "no-edges-text": "No se encontraron Edges", - "edge-details": "Detalles del Edge", - "add-edge-text": "Agregar nuevo Edge", - "delete": "Eliminar Edge", - "delete-edge-title": "¿Está seguro de que desea eliminar el Edge '{{edgeName}}'?", - "delete-edge-text": "Tenga cuidado, después de la confirmación, el Edge y todos los datos relacionados serán irrecuperables", - "delete-edges-title": "¿Está seguro de que desea edge {count, plural, =1 {1 Edge} other {# Edges} }?", - "delete-edges-text": "Tenga cuidado, después de la confirmación se eliminarán todos los Edges seleccionados y todos los datos relacionados se volverán irrecuperables", - "name": "Nombre", - "name-starts-with": "Nombre de Edge comienza con", - "name-required": "Se requiere nombre", - "description": "Descripción", - "details": "Detalles", - "events": "Eventos", - "copy-id": "Copiar ID de Edge", - "id-copied-message": "El ID de Edge se ha copiado al portapapeles", - "sync": "Sinc Edge", - "edge-required": "Edge Requerido", - "edge-type": "Tipo de Edge", - "edge-type-required": "El tipo de Edge es requerido.", - "event-action": "Información de la entidad", - "entity-id": "ID de entidad", - "select-edge-type": "Seleccionar tipo de Edge", - "assign-to-customer": "Asignar al cliente", - "assign-to-customer-text": "Seleccione el cliente para asignar los Edges", - "assign-edge-to-customer": "Asignar Edge(s) al cliente", - "assign-edge-to-customer-text": "Seleccione los Edges para asignar al cliente", - "assignedToCustomer": "Asignada a la cliente", - "edge-public": "Edge es pública", - "assigned-to-customer": "Asignado al cliente", - "unassign-from-customer": "Anular asignación del cliente", - "unassign-edge-title": "¿Está seguro de que desea desasignar el Edge '{{edgeName}}'?", - "unassign-edge-text": "Después de la confirmación, el Edge quedará sin asignar y el cliente no podrá acceder a él", - "unassign-edges-title": "¿Está seguro de que desea anular la asignación de {count, plural, =1 {1 Edge} other {# Edges} }?", - "unassign-edges-text": "Después de la confirmación de todos los Edges seleccionados, se anulará la asignación y el cliente no podrá acceder a ellos.", - "make-public": "Hacer público el Edge", - "make-public-edge-title": "¿Estás seguro de que quieres hacer público el edge '{{edgeName}}'?", - "make-public-edge-text": "Después de la confirmación, el Edge y todos sus datos serán públicos y accesibles para otros", - "make-private": "Hacer que edge sea privado", - "public": "Público", - "make-private-edge-title": "¿Está seguro de que desea que el Edge '{{edgeName}}' sea privado?", - "make-private-edge-text": "Después de la confirmación, el Edge y todos sus datos se harán privados y otros no podrán acceder a ellos", - "import": "Importar Edge", - "install-connect-instructions": "Instrucciones de instalación y conexión", - "install-connect-instructions-edge-created": "Edge creado! Revisa las instrucciones de instalación y conexión", - "loading-edge-instructions": "Cargando instrucciones edge...", - "label": "Etiqueta", - "load-entity-error": "Entidad no encontrada. No se pudo cargar la información", - "assign-new-edge": "Asignar nuevo Edge", - "unassign-from-edge": "Anular asignación de Edge", - "edge-key": "Clave de Edge", - "copy-edge-key": "Copiar clave de Edge", - "edge-key-copied-message": "La clave de Edge se ha copiado al portapapeles", - "edge-secret": "Secreto del Edge", - "copy-edge-secret": "Copiar secreto del Edge", - "edge-secret-copied-message": "El secreto de Edge se ha copiado al portapapeles", - "manage-assets": "Gestionar activos", - "manage-devices": "Gestionar dispositivos", - "manage-entity-views": "Gestionar vistas de entidad", - "manage-dashboards": "Administrar paneles", - "manage-rulechains": "Administrar cadenas de reglas", - "assets": "Activos de Edge", - "devices": "Dispositivos de Edge", - "entity-views": "Vistas de entidad de Edge", - "dashboard": "Panel de control Edge", - "dashboards": "Paneles de Edge", - "rulechain-templates": "Plantillas, de cadena de reglas", - "edge-rulechain-templates": "Plantillas de cadena de reglas (Edge)", - "rulechains": "Cadenas de regla (Edge)", - "search": "Edges de búsqueda", - "selected-edges": "{count, plural, =1 {1 Edge} other {# Edges} } seleccionadas", - "any-edge": "Cualquier Edge", - "no-edge-types-matching": "No se encontraron tipos de aristas que coincidan con '{{entitySubtype}}'.", - "edge-type-list-empty": "No se seleccionó ningún tipo de Edge.", - "edge-types": "Tipos de Edges", - "enter-edge-type": "Ingrese el tipo de Edge", - "deployed": "Desplegada", - "pending": "Pendiente", - "downlinks": "Enlaces descendentes", - "no-downlinks-prompt": "No se encontraron enlaces descendentes", - "sync-process-started-successfully": "¡El proceso de sincronización se inició correctamente!", - "missing-related-rule-chains-title": "Al Edge le faltan cadenas de reglas relacionadas", - "missing-related-rule-chains-text": "Asignado a la (s) cadena (s) de reglas de Edge usa nodos de reglas que reenvían mensajes a cadenas de reglas que no están asignadas a este Edge.

Lista de cadenas de reglas faltantes:
{{missingRuleChains}}", - "widget-datasource-error": "Este widget solo admite la fuente de datos de la entidad EDGE" + "symbol" : { + "symbol" : "Símbolo SCADA", + "fluid-presence" : "Presencia de fluido", + "fluid-presence-hint" : "Indica si hay fluido presente en la tubería.", + "fluid-present" : "Fluido presente", + "present" : "Presente", + "absent" : "Ausente", + "flow-presence" : "Presencia de flujo", + "flow-presence-hint" : "Indica si el fluido está fluyendo en la tubería.", + "flow-present" : "Flujo presente", + "flow-direction" : "Dirección del flujo", + "flow-direction-hint" : "Indica la dirección del flujo del fluido.", + "forward" : "Hacia adelante", + "reverse" : "Reversa", + "flow-animation-speed" : "Velocidad de animación del flujo", + "flow-animation-speed-hint" : "Valor decimal que indica la velocidad de animación del flujo. 1 - velocidad normal, 0 - sin animación, < 1 - animación más lenta, > 1 - animación más rápida.", + "leak" : "Fuga", + "leak-hint" : "Indica si hay una fuga en la tubería.", + "leak-present" : "Fuga presente", + "fluid-color" : "Color del fluido", + "pipe-color" : "Color de la tubería", + "horizontal-pipe" : "Tubería horizontal", + "vertical-pipe" : "Tubería vertical", + "horizontal-fluid-color" : "Color del fluido horizontal", + "vertical-fluid-color" : "Color del fluido vertical", + "left-pipe" : "Tubería izquierda", + "right-pipe" : "Tubería derecha", + "top-pipe" : "Tubería superior", + "bottom-pipe" : "Tubería inferior", + "left-fluid-color" : "Color del fluido izquierdo", + "right-fluid-color" : "Color del fluido derecho", + "top-fluid-color" : "Color del fluido superior", + "bottom-fluid-color" : "Color del fluido inferior", + "display" : "Pantalla", + "display-format" : "Formato de pantalla", + "value" : "Valor", + "decimals" : "Decimales", + "units" : "Unidades", + "flow-meter-value-hint" : "Valor decimal mostrado en el medidor de flujo", + "value-hint" : "Valor decimal que indica el valor actual", + "running" : "En ejecución", + "running-hint" : "Indica si el componente está en estado de ejecución.", + "warning-state" : "Estado de advertencia", + "warning" : "Advertencia", + "warning-click" : "Clic de advertencia", + "warning-state-hint" : "Indica si el componente está en estado de advertencia.", + "critical-state" : "Estado crítico", + "critical" : "Crítico", + "critical-click" : "Clic crítico", + "critical-state-hint" : "Indica si el componente está en estado crítico.", + "critical-state-animation" : "Animación de estado crítico", + "critical-state-animation-hint" : "Indica si se habilita la animación parpadeante cuando el componente está en estado crítico.", + "warning-critical-state-animation" : "Animación de estado de advertencia/crítico", + "warning-critical-state-animation-hint" : "Indica si se habilita la animación parpadeante cuando el componente está en estado de advertencia o crítico.", + "animation" : "Animación", + "broken" : "Roto", + "broken-hint" : "Indica si el componente está roto.", + "on-display-click" : "Al hacer clic en la pantalla", + "on-display-click-hint" : "Acción invocada cuando el usuario hace clic en la pantalla.", + "pipe" : "Tubería", + "default-border-color" : "Color de borde por defecto", + "active-border-color" : "Color de borde activo", + "warning-border-color" : "Color de borde de advertencia", + "critical-border-color" : "Color de borde crítico", + "background-color" : "Color de fondo", + "rotation-animation-speed" : "Velocidad de animación de rotación", + "rotation-animation-speed-hint" : "Valor decimal que indica la velocidad de la animación de rotación. 1 - velocidad normal, 0 - sin animación, < 1 - más lenta, > 1 - más rápida.", + "on-click" : "Al hacer clic", + "on-click-hint" : "Acción invocada cuando el usuario hace clic en el componente.", + "connectors-positions" : "Posiciones de conectores", + "right-connector" : "Conector derecho", + "right-top-connector" : "Conector superior derecho", + "right-bottom-connector" : "Conector inferior derecho", + "left-connector" : "Conector izquierdo", + "left-top-connector" : "Conector superior izquierdo", + "left-bottom-connector" : "Conector inferior izquierdo", + "top-left-connector" : "Conector izquierdo superior", + "top-right-connector" : "Conector derecho superior", + "top-connector" : "Conector superior", + "bottom-connector" : "Conector inferior", + "running-color" : "Color en funcionamiento", + "stopped-color" : "Color detenido", + "stopped" : "Detenido", + "warning-color" : "Color de advertencia", + "critical-color" : "Color crítico", + "opened" : "Abierto", + "opened-hint" : "Indica si el componente está en estado abierto.", + "open" : "Abrir", + "open-hint" : "Acción invocada cuando el usuario hace clic para abrir el componente.", + "close" : "Cerrar", + "close-hint" : "Acción invocada cuando el usuario hace clic para cerrar el componente.", + "close-state-animation" : "Animación de estado cerrado", + "close-state-animation-hint" : "Si se debe habilitar la animación parpadeante cuando el componente está en estado cerrado.", + "opened-color" : "Color de abierto", + "closed-color" : "Color de cerrado", + "opened-rotation-angle" : "Ángulo de rotación abierto", + "closed-rotation-angle" : "Ángulo de rotación cerrado", + "tank-capacity" : "Capacidad del tanque", + "tank-capacity-hint" : "Valor decimal que indica la capacidad total del tanque.", + "current-volume" : "Volumen actual", + "current-volume-hint" : "Valor decimal que indica el volumen ocupado actual.", + "tank-color" : "Color del tanque", + "value-box" : "Caja de valor", + "value-text" : "Texto del valor", + "scale" : "Escala", + "transparent-mode" : "Modo transparente", + "major-ticks" : "Marcas mayores", + "intervals" : "Intervalos", + "major-ticks-color" : "Color de marcas mayores", + "normal" : "Normal", + "minor-ticks" : "Marcas menores", + "minor-ticks-color" : "Color de marcas menores", + "temperature" : "Temperatura", + "temperature-hint" : "Valor decimal que indica la temperatura actual.", + "update-temperature" : "Actualizar temperatura", + "update-temperature-hint" : "Acción invocada cuando el usuario hace clic para cambiar la temperatura actual.", + "run" : "Ejecutar", + "run-hint" : "Acción invocada cuando el usuario hace clic para ejecutar el componente.", + "stop" : "Detener", + "stop-hint" : "Acción invocada cuando el usuario hace clic para detener el componente.", + "temperature-step" : "Incremento de paso de temperatura", + "heat-pump-color" : "Color de la bomba de calor", + "power-button-background" : "Fondo del botón de encendido", + "value-box-background" : "Fondo de la caja de valor", + "value-units" : "Unidades del valor", + "filtration-mode" : "Modo de filtración", + "filtration-mode-hint" : "Valor entero que indica el modo de filtración actual.", + "filtration-mode-update" : "Estado de actualización del modo de filtración", + "filtration-mode-update-hint" : "Acción invocada cuando el usuario hace clic para cambiar el modo de filtración actual.", + "filter-mode" : "Filtrar", + "waste-mode" : "Desecho", + "backwash-mode" : "Contralavado", + "recirculate-mode" : "Recircular", + "rinse-mode" : "Enjuague", + "closed-mode" : "Cerrado", + "sand-filter-color" : "Color del filtro de arena", + "mode-box-background" : "Fondo de la caja de modo", + "border-color" : "Color del borde", + "label-color" : "Color de la etiqueta", + "water-leak-hint" : "Indica si hay una fuga.", + "default-color" : "Color predeterminado", + "leak-color" : "Color de fuga", + "full-value" : "Valor máximo", + "full-value-hint" : "Valor decimal que indica el valor completo.", + "label" : "Etiqueta", + "icon" : "Icono", + "button-color" : "Color del botón", + "on-label" : "Texto de la etiqueta 'Encendido'", + "off-label" : "Texto de la etiqueta 'Apagado'", + "arrow-presence" : "Presencia de flecha", + "arrow-presence-hint" : "Indica si hay una flecha presente en el conector.", + "arrow-present" : "Flecha presente", + "arrow-direction" : "Dirección de flecha/animación", + "arrow-direction-hint" : "Indica la dirección del flujo.", + "flow-animation" : "Animación de flujo", + "flow-animation-hint" : "Indica si hay animación presente en el conector.", + "flow" : "Flujo", + "flow-line" : "Línea", + "flow-line-style" : "Estilo de línea", + "flow-style-hint" : "Establece los valores de Trazo y Espacio de manera que su suma sea divisible por 100 sin residuo para una sincronización perfecta de la animación.", + "flow-dash-cap" : "Terminación de línea discontinua del flujo", + "dash-cap-butt" : "Final recto", + "dash-cap-round" : "Final redondeado", + "dash-cap-square" : "Final cuadrado", + "dash" : "Guion", + "gap" : "Espacio", + "main-line" : "Línea principal", + "line" : "Línea", + "line-color" : "Color de la línea", + "arrow-color" : "Color de la flecha", + "target-value" : "Valor objetivo", + "target-value-hint" : "Indica el punto objetivo en la escala.", + "min-max-value" : "Valor mínimo y máximo", + "min-value" : "Mínimo", + "max-value" : "Máximo", + "progress-bar" : "Barra de progreso", + "progress-arrow" : "Flecha de progreso", + "warning-scale-color" : "Color de escala de advertencia", + "critical-scale-color" : "Color de escala crítica", + "scale-color" : "Color de la escala", + "target" : "Objetivo", + "high-warning-state" : "Estado de advertencia alta", + "show-high-warning-scale" : "Mostrar escala de advertencia alta", + "high-warning-scale" : "Escala de advertencia alta", + "high-warning-state-hint" : "Valor decimal que indica un rango de advertencia alta hasta un valor crítico alto o máximo.", + "low-warning-state" : "Estado de advertencia baja", + "show-low-warning-scale" : "Mostrar escala de advertencia baja", + "low-warning-scale" : "Escala de advertencia baja", + "low-warning-state-hint" : "Valor decimal que indica un rango de advertencia baja hasta un valor crítico bajo o mínimo.", + "high-critical-state" : "Estado crítico alto", + "show-high-critical-scale" : "Mostrar escala crítica alta", + "high-critical-scale" : "Escala crítica alta", + "high-critical-state-hint" : "Valor decimal que indica un rango crítico alto hasta el valor máximo de la escala.", + "low-critical-state" : "Estado crítico bajo", + "show-low-critical-scale" : "Mostrar escala crítica baja", + "low-critical-scale" : "Escala crítica baja", + "low-critical-state-hint" : "Valor decimal que indica un rango crítico bajo hasta el valor mínimo de la escala.", + "filter-color" : "Color del filtro", + "colors" : "Colores", + "indicator-colors" : "Colores del indicador", + "enabled" : "Habilitado", + "disabled" : "Deshabilitado", + "on" : "ENCENDIDO", + "off" : "APAGADO", + "on-off-state" : "Estado encendido/apagado", + "on-off-state-hint" : "Indica si el componente está en estado Encendido o Apagado.", + "on-update-state" : "Actualizar estado a encendido", + "on-update-state-hint" : "Acción invocada cuando el usuario hace clic para actualizar el estado a Encendido.", + "off-update-state" : "Actualizar estado a apagado", + "off-update-state-hint" : "Acción invocada cuando el usuario hace clic para actualizar el estado a Apagado.", + "voltage" : "Voltaje", + "input-voltage" : "Voltaje de entrada", + "input-voltage-hint" : "Valor decimal que indica el voltaje de entrada.", + "output-voltage" : "Voltaje de salida", + "output-voltage-hint" : "Valor decimal que indica el voltaje de salida.", + "first-phase-voltage" : "Voltaje de la primera fase", + "second-phase-voltage" : "Voltaje de la segunda fase", + "third-phase-voltage" : "Voltaje de la tercera fase", + "phase-voltage-hint" : "Valor decimal que indica el voltaje para la fase actual.", + "voltage-hint" : "Valor decimal que indica el voltaje actual.", + "current-voltage-color" : "Color del voltaje actual", + "phase-indicator-color" : "Color del indicador de fase", + "measured" : "Medido", + "measured-hint" : "Valor decimal que indica el uso de energía en kilovatios-hora.", + "day-rate" : "Tarifa diurna", + "night-rate" : "Tarifa nocturna", + "off-peak-rate" : "Tarifa valle", + "peak-rate" : "Tarifa punta", + "export-rate" : "Tarifa de exportación", + "operating-mode" : "Modo de funcionamiento", + "bypass-mode" : "Bypass", + "operating-mode-hint" : "Valor entero que indica el modo de funcionamiento actual (0 - APAGADO, 1 - ENCENDIDO, 2 - BYPASS).", + "connected" : "Conectado", + "connected-hint" : "Indica si el componente está en estado conectado.", + "disconnected" : "Desconectado", + "indicator" : "Indicador", + "operation-mode" : "Modo de operación", + "operation-mode-hint" : "Indica si el inversor está en modo Red eléctrica o Inversor.", + "operation-mode-indicators-color" : "Color de los indicadores del modo de operación", + "mains-on-mode" : "Red eléctrica activa", + "inverter-on-mode" : "Inversor activo", + "charging-mode" : "Modo de carga", + "charging-mode-hint" : "Valor entero que indica el modo de carga actual (1 - Carga rápida, 2 - Absorción, 3 - Flotación).", + "charging-mode-indicators-color" : "Color de los indicadores del modo de carga", + "inverter-faults" : "Fallas", + "inverter-fault-indicators-color" : "Color de los indicadores de fallas", + "overload-fault" : "Sobrecarga", + "overload-fault-hint" : "Indica si el inversor está en condición de sobrecarga.", + "low-battery-fault" : "Batería baja", + "low-battery-fault-hint" : "Indica si la batería está excesivamente descargada.", + "temperature-fault" : "Temperatura", + "temperature-fault-hint" : "Indica si hay alta temperatura en un inversor.", + "triangle" : "Triángulo", + "socket" : "Enchufe", + "left-button" : "Botón izquierdo", + "right-button" : "Botón derecho", + "alarm-colors" : "Colores de alarma", + "hook-color" : "Color del gancho" + } + }, + "item" : { + "selected" : "Seleccionado" + }, + "js-func" : { + "no-return-error" : "¡La función debe devolver un valor!", + "return-type-mismatch" : "¡La función debe devolver un valor del tipo '{{type}}'!", + "tidy" : "Ordenar", + "mini" : "Mini", + "modules" : "Módulos", + "remove-module" : "Eliminar módulo", + "no-modules" : "No hay módulos configurados", + "add-module" : "Agregar módulo", + "module-alias" : "Alias", + "invalid-module-alias-name" : "Nombre de alias inválido", + "module-resource" : "Recurso del módulo JS", + "not-unique-module-aliases-error" : "¡Los alias de los módulos deben ser únicos!", + "show-module-info" : "Mostrar información del módulo", + "show-module-source-code" : "Mostrar código fuente del módulo", + "module-members" : "Miembros del módulo", + "module-no-members" : "El módulo no tiene miembros exportados", + "module-load-error" : "Error al cargar el módulo", + "source-code" : "Código fuente", + "source-code-load-error" : "Error al cargar el código fuente", + "no-js-module-text" : "No se encontraron módulos JS", + "no-js-module-matching" : "No se encontraron módulos JS que coincidan con '{{module}}'." + }, + "key-val" : { + "key" : "Clave", + "value" : "Valor", + "remove-entry" : "Eliminar entrada", + "add-entry" : "Agregar entrada", + "no-data" : "No hay entradas" + }, + "layout" : { + "layout" : "Diseño", + "layouts" : "Diseños", + "manage" : "Gestionar diseños", + "settings" : "Configuración del diseño", + "color" : "Color", + "main" : "Principal", + "right" : "Derecha", + "left" : "Izquierda", + "select" : "Seleccionar diseño objetivo", + "percentage-width" : "Ancho porcentual (%)", + "fixed-width" : "Ancho fijo (px)", + "left-width" : "Columna izquierda (%)", + "right-width" : "Columna derecha (%)", + "pick-fixed-side" : "Lado fijo:", + "layout-fixed-width" : "Ancho fijo (px)", + "value-min-error" : "El valor debe ser mayor que {{min}}{{unit}}", + "value-max-error" : "El valor debe ser menor que {{max}}{{unit}}", + "layout-fixed-width-required" : "Se requiere ancho fijo", + "right-width-percentage-required" : "Se requiere porcentaje derecho", + "left-width-percentage-required" : "Se requiere porcentaje izquierdo", + "divider" : "Divisor", + "right-side" : "Diseño del lado derecho", + "left-side" : "Diseño del lado izquierdo", + "add-new-breakpoint" : "Agregar nuevo punto de interrupción", + "breakpoint" : "Punto de interrupción", + "breakpoints" : "Puntos de interrupción", + "copy-from" : "Copiar desde", + "size" : "Tamaño", + "delete-breakpoint-title" : "¿Estás seguro de que deseas eliminar el punto de interrupción '{{name}}'?", + "delete-breakpoint-text" : "Ten en cuenta que, después de la confirmación, el punto de interrupción será irrecuperable y la configuración volverá al punto de interrupción predeterminado." + }, + "legend" : { + "direction" : "Dirección", + "position" : "Posición", + "show-values" : "Mostrar valores", + "min-option" : "Mínimo", + "max-option" : "Máximo", + "average-option" : "Promedio", + "total-option" : "Total", + "latest-option" : "Último", + "sort-legend" : "Ordenar claves de datos en la leyenda", + "show-max" : "Mostrar valor máximo", + "show-min" : "Mostrar valor mínimo", + "show-avg" : "Mostrar valor promedio", + "show-total" : "Mostrar valor total", + "show-latest" : "Mostrar último valor", + "settings" : "Configuración de leyenda", + "min" : "mín", + "max" : "máx", + "avg" : "prom", + "total" : "total", + "latest" : "último", + "Min" : "Mínimo", + "Max" : "Máximo", + "Avg" : "Promedio", + "Total" : "Total", + "Latest" : "Último", + "comparison-time-ago" : { + "previousInterval" : "(intervalo anterior)", + "customInterval" : "(intervalo personalizado)", + "days" : "(hace un día)", + "weeks" : "(hace una semana)", + "months" : "(hace un mes)", + "years" : "(hace un año)" }, - "edge-event": { - "type-dashboard": "Panel", - "type-asset": "Activo", - "type-device": "Dispositivo", - "type-device-profile": "Perfil de dispositivo", - "type-asset-profile": "Perfil de activo", - "type-entity-view": "Vista de entidad", - "type-alarm": "Alarma", - "type-rule-chain": "Cadena de reglas", - "type-rule-chain-metadata": "Metadatos de Cadena de Reglas", - "type-edge": "Edge", - "type-user": "Usuario", - "type-tenant": "Propietario", - "type-tenant-profile": "Perfil de Propietario", - "type-customer": "Cliente", - "type-relation": "Relación", - "type-widgets-bundle": "Paquete de Widgets", - "type-widgets-type": "Tipos de Widgets", - "type-admin-settings": "Ajustes de Administración", - "type-ota-package": "Paquete OTA", - "type-queue": "Cola", - "action-type-added": "Añadido", - "action-type-deleted": "Borrado", - "action-type-updated": "Actualizado", - "action-type-post-attributes": "Envío de Atributos", - "action-type-attributes-updated": "Atributos Actualizados", - "action-type-attributes-deleted": "Atributos Borrados", - "action-type-timeseries-updated": "Series de tiempo Actualizadas", - "action-type-credentials-updated": "Credenciales Actualizadas", - "action-type-assigned-to-customer": "Asignado a Cliente", - "action-type-unassigned-from-customer": "Desasignado de Cliente", - "action-type-relation-add-or-update": "Añadir o Actualizar relación", - "action-type-relation-deleted": "Relación borrada", - "action-type-rpc-call": "Llamada RPC", - "action-type-alarm-ack": "ACK Alarma", - "action-type-alarm-clear": "Borrado de Alarma", - "action-type-alarm-assigned": "Alarma Asignada", - "action-type-alarm-unassigned": "Alarma Desasignada", - "action-type-assigned-to-edge": "Asignado a Edge", - "action-type-unassigned-from-edge": "Desasignado de Edge", - "action-type-credentials-request": "Obtención de Credenciales", - "action-type-entity-merge-request": "Unión de entidades" + "column-title" : "Título de columna", + "label" : "Etiqueta", + "value" : "Valor" + }, + "login" : { + "login" : "Iniciar sesión", + "request-password-reset" : "Solicitar restablecimiento de contraseña", + "reset-password" : "Restablecer contraseña", + "create-password" : "Crear contraseña", + "two-factor-authentication" : "Autenticación de dos factores", + "passwords-mismatch-error" : "¡Las contraseñas ingresadas deben coincidir!", + "password-again" : "Repetir contraseña", + "sign-in" : "Por favor inicia sesión", + "username" : "Nombre de usuario (correo electrónico)", + "remember-me" : "Recordarme", + "forgot-password" : "¿Olvidaste tu contraseña?", + "password-reset" : "Restablecimiento de contraseña", + "expired-password-reset-message" : "¡Tus credenciales han expirado! Por favor crea una nueva contraseña.", + "new-password" : "Nueva contraseña", + "new-password-again" : "Confirmar nueva contraseña", + "password-link-sent-message" : "Enlace de restablecimiento enviado", + "email" : "Correo electrónico", + "invalid-email-format" : "Formato de correo electrónico inválido.", + "login-with" : "Iniciar sesión con {{name}}", + "or" : "o", + "error" : "Error de inicio de sesión", + "verify-your-identity" : "Verifica tu identidad", + "select-way-to-verify" : "Selecciona una forma de verificación", + "resend-code" : "Reenviar código", + "resend-code-wait" : "Reenviar código en { time, plural, =1 {1 segundo} other {# segundos} }", + "try-another-way" : "Intentar otra forma", + "totp-auth-description" : "Por favor ingresa el código de seguridad desde tu aplicación de autenticación.", + "totp-auth-placeholder" : "Código", + "sms-auth-description" : "Se ha enviado un código de seguridad a tu teléfono en {{contact}}.", + "sms-auth-placeholder" : "Código SMS", + "email-auth-description" : "Se ha enviado un código de seguridad a tu correo electrónico en {{contact}}.", + "email-auth-placeholder" : "Código de correo", + "backup-code-auth-description" : "Por favor ingresa uno de tus códigos de respaldo.", + "backup-code-auth-placeholder" : "Código de respaldo", + "activation-link-expired" : "El enlace de activación ha expirado", + "activation-link-expired-message" : "El enlace para activar tu perfil ha expirado. Puedes regresar a la página de inicio de sesión para recibir un nuevo correo.", + "reset-password-link-expired" : "El enlace para restablecer la contraseña ha expirado", + "reset-password-link-expired-message" : "El enlace para restablecer tu contraseña ha expirado. Puedes regresar a la página de inicio de sesión para recibir un nuevo correo." + }, + "mobile" : { + "add-application" : "Agregar aplicación", + "app-id" : "ID de la aplicación", + "app-id-required" : "Se requiere el ID de la aplicación", + "app-id-pattern" : "Formato de ID de aplicación inválido", + "app-store-link" : "Enlace de App Store", + "app-store-link-required" : "Se requiere el enlace de App Store", + "application-details" : "Detalles de la aplicación", + "application-package" : "Paquete de la aplicación", + "application-secret" : "Secreto de la aplicación", + "application-secret-required" : "Se requiere el secreto de la aplicación", + "application" : "Aplicación", + "applications" : "Aplicaciones", + "copy-app-id" : "Copiar ID de la aplicación", + "copy-app-store-link" : "Copiar enlace de App Store", + "copy-application-package" : "Copiar paquete de la aplicación", + "copy-application-secret" : "Copiar secreto de la aplicación", + "copy-google-play-link" : "Copiar enlace de Google Play", + "copy-sha256-certificate-fingerprints" : "Copiar huellas digitales SHA256 del certificado", + "delete-application" : "Eliminar aplicación", + "delete-application-button-text" : "Entiendo las consecuencias, eliminar aplicación", + "delete-application-text" : "Esta acción no se puede deshacer. Esto eliminará permanentemente tu aplicación.
Si no deseas eliminarla permanentemente puedes suspender temporalmente la aplicación.
Para eliminar la aplicación de todos modos, escribe \"{{phrase}}\" para confirmar.", + "delete-application-title-short" : "¿Estás seguro de que deseas eliminar la aplicación '{{name}}'?", + "delete-application-text-short" : "Ten cuidado, después de la confirmación la aplicación y todos los datos relacionados serán irrecuperables.", + "delete-application-phrase" : "eliminar aplicación", + "delete-applications-bundle-text" : "Ten cuidado, después de la confirmación el paquete móvil y todos los datos relacionados serán irrecuperables.", + "delete-applications-bundle-title" : "¿Estás seguro de que deseas eliminar el paquete móvil '{{bundleName}}'?", + "generate-application-secret" : "Generar secreto de la aplicación", + "google-play-link" : "Enlace de Google Play", + "google-play-link-required" : "Se requiere el enlace de Google Play", + "latest-version" : "Última versión", + "min-version" : "Versión mínima", + "invalid-version-pattern" : "Formato de versión inválido. Usa el formato: mayor.menor.revisión (ej., 1.0.0).", + "mobile-center" : "Centro móvil", + "mobile-package" : "Paquete de la aplicación", + "mobile-package-max-length" : "El paquete de la aplicación debe tener menos de 256 caracteres", + "mobile-package-required" : "Se requiere el paquete de la aplicación", + "mobile-package-pattern" : "Formato inválido del paquete de la aplicación", + "no-application" : "No se encontraron aplicaciones", + "no-bundles" : "No se encontraron paquetes", + "platform-type" : "Tipo de plataforma", + "search-application" : "Buscar aplicaciones", + "search-bundles" : "Buscar paquetes", + "set" : "Establecer", + "sha256-certificate-fingerprints" : "Huellas digitales SHA256 del certificado", + "sha256-certificate-fingerprints-required" : "Se requieren las huellas digitales SHA256 del certificado", + "sha256-certificate-fingerprints-pattern" : "Formato inválido de huella digital SHA256 del certificado", + "show-hidden-pages" : "Mostrar páginas ocultas", + "status" : "Estado", + "status-type" : { + "deprecated" : "Obsoleto", + "draft" : "Borrador", + "published" : "Publicado", + "suspended" : "Suspendido" }, - "error": { - "unable-to-connect": "Imposible conectar con el servidor! Por favor, revise su conexión a internet.", - "unhandled-error-code": "Código de error no controlado: {{errorCode}}", - "unknown-error": "Error desconocido" + "store-information" : "Información de la tienda", + "version-information" : "Información de la versión", + "min-version-release-notes" : "Notas de la versión mínima", + "latest-version-release-notes" : "Notas de la última versión", + "bundle" : "Paquete", + "bundles" : "Paquetes", + "add-bundle" : "Agregar paquete", + "title" : "Título", + "title-required" : "Se requiere título", + "title-cannot-contain-only-spaces" : "El título no puede contener solo espacios", + "title-max-length" : "El título debe tener menos de 256 caracteres", + "oauth-clients" : "Clientes OAuth 2.0", + "android-app" : "Aplicación Android", + "android-application" : "Aplicación Android", + "ios-app" : "Aplicación iOS", + "ios-application" : "Aplicación iOS", + "invalid-store-link" : "Enlace de tienda inválido", + "enable-oauth" : "Habilitar OAuth 2.0", + "enable-self-registration" : "Habilitar auto registro", + "edit-bundle" : "Editar paquete", + "description" : "Descripción", + "basic-settings" : "Configuraciones básicas", + "no-application-matching" : "No se encontró ninguna aplicación que coincida con '{{entity}}'.", + "no-bundle-matching" : "No se encontró ningún paquete que coincida con '{{entity}}'.", + "application-required" : "Se requiere la aplicación.", + "bundle-required" : "Se requiere el paquete.", + "no-application-text" : "No se encontraron aplicaciones", + "no-bundle-text" : "No se encontró paquete", + "layout" : "Diseño", + "pages" : "Páginas", + "hide-all-pages" : "Ocultar todas las páginas", + "reset-to-default-pages" : "Restablecer a las páginas predeterminadas", + "add-specific-page" : "Agregar página específica", + "visible" : "Visible", + "hidden" : "Oculto", + "reset-to-page-default" : "Restablecer página a predeterminado", + "mobile-599" : "Móvil (máx. 599px)", + "tablet-959" : "Tableta (máx. 959px)", + "max-element-number" : "Número máximo de elementos", + "page-name" : "Nombre de la página", + "page-name-required" : "Se requiere el nombre de la página.", + "page-name-cannot-contain-only-spaces" : "El nombre de la página no puede contener solo espacios.", + "page-name-max-length" : "El nombre de la página debe tener menos de 256 caracteres", + "page-type" : "Tipo de página", + "pages-types" : { + "dashboard" : "Tablero", + "web-view" : "Vista web", + "custom" : "Personalizado" }, - "entity": { - "entity": "Entidad", - "entities": "Entidades", - "entities-count": "Nº de entidades", - "alarms-count": "Nº de alarmas", - "aliases": "Alias de entidad", - "aliases-short": "Alias", - "entity-alias": "Alias de entidad", - "unable-delete-entity-alias-title": "No ha sido posible eliminar el alias de entidad", - "unable-delete-entity-alias-text": "El alias de entidad '{{entityAlias}}' no puede ser eliminado ya que se esta usando por los siguientes widgets:
{{widgetsList}}", - "duplicate-alias-error": "Encontrado un alias duplicado '{{alias}}'.
Loas alias de entidad tienen que ser únicos para cada panel.", - "missing-entity-filter-error": "Falta el filtro para el alias '{{alias}}'.", - "configure-alias": "Configurar alias '{{alias}}' ", - "alias": "Alias", - "alias-required": "Alias de entidad requerido.", - "remove-alias": "Eliminar alias de entidad", - "add-alias": "Añadir alias de entidad", - "entity-list": "Lista de entidades", - "entity-type": "Tipo de entidad", - "entity-types": "Tipos de entidades", - "entity-type-list": "Lista de tipos de entidad", - "any-entity": "Cualquier entdad", - "add-entity-type": "Añadir tipo de entidad", - "enter-entity-type": "Introducir tipo de entidad", - "no-entities-matching": "No se han encontrado entidades que coincidan con '{{entity}}' .", - "no-entity-types-matching": "No se han encontrado tipos de entidad que coincidan con '{{entityType}}' .", - "name-starts-with": "Nombre empieza con", - "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", - "use-entity-name-filter": "Usar filtro", - "entity-list-empty": "No hay entidades seleccionadas.", - "entity-type-list-required": "Al menos debe seleccionar un tipo de entidad", - "entity-name-filter-required": "Filtro de nombre de entidad requerido.", - "entity-name-filter-no-entity-matched": "No hay entidades que comiencen con '{{entity}}' .", - "all-subtypes": "Todos", - "select-entities": "Seleccionar entidades", - "no-aliases-found": "No se han encontrado alias.", - "no-alias-matching": "'{{alias}}' no encontrado.", - "create-new-alias": "Crear nuevo alias!", - "create-new": "Crear nuevo", - "key": "Clave", - "key-name": "Nombre de clave", - "no-keys-found": "No se han encontrado claves.", - "no-key-matching": "'{{key}}' no encontrada.", - "create-new-key": "Crear nueva clave!", - "type": "Tipo", - "type-required": "Tipo de entidad requerido.", - "type-device": "Dispositivo", - "type-devices": "Dispositivos", - "list-of-devices": "{ count, plural, =1 {Un dispositivo} other {Lista de # Dispositivos} }", - "device-name-starts-with": "Dispositivos cuyos nombres comiencen por '{{prefix}}'", - "type-device-profile": "Perfil de dispositivo", - "type-device-profiles": "Perfiles de dispositivo", - "clear-selected-profiles": "Borrar perfiles seleccionados", - "list-of-device-profiles": "{ count, plural, =1 {un perfil} other {Lista de # perfiles} }", - "device-profile-name-starts-with": "Perfiles cuyo nombre empiece por '{{prefix}}'", - "type-asset-profile": "Perfil de activo", - "type-asset-profiles": "Perfiles de activo", - "list-of-asset-profiles": "{ count, plural, =1 {Un perfil de activo} other {Lista de # perfiles de activos} }", - "asset-profile-name-starts-with": "Perfiles de activo cuyo nombre comience por '{{prefix}}'", - "type-asset": "Activo", - "type-assets": "Activos", - "list-of-assets": "{ count, plural, =1 {Un activo} other {Lista de # activos} }", - "asset-name-starts-with": "Activos cuyos nombres comiencen por '{{prefix}}'", - "type-entity-view": "Vista Entidad", - "type-entity-views": "Vistas Entidades", - "list-of-entity-views": "{ count, plural, =1 {Una vista de entidad} other {Lista de # Vistas de Entidades} }", - "entity-view-name-starts-with": "Vistas de Entidades cuyo nombre comiencen por '{{prefix}}'", - "type-rule": "Regla", - "type-rules": "Reglas", - "list-of-rules": "{ count, plural, =1 {Una regla} other {Lista de # reglas} }", - "rule-name-starts-with": "Reglas cuyos nombres comiencen por '{{prefix}}'", - "type-plugin": "Plugin", - "type-plugins": "Plugins", - "list-of-plugins": "{ count, plural, =1 {Un plugin} other {Lista de # plugins} }", - "plugin-name-starts-with": "Plugins cuyos nombres comiencen por '{{prefix}}'", - "type-tenant": "Propietario", - "type-tenants": "Propietarios", - "list-of-tenants": "{ count, plural, =1 {Un propietario} other {Lista de # propietarios} }", - "tenant-name-starts-with": "Propietarios cuyo nombre comience por '{{prefix}}'", - "type-tenant-profile": "Perfil de Propietario", - "type-tenant-profiles": "Perfiles de propietario", - "list-of-tenant-profiles": "{ count, plural, =1 {Un perfil de propietario} other {Lista de # perfiles de propietario} }", - "tenant-profile-name-starts-with": "Pefiles de propietario cuyo nombre empiece por '{{prefix}}'", - "type-customer": "Cliente", - "type-customers": "Clientes", - "list-of-customers": "{ count, plural, =1 {Un cliente} other {Lista de # clientes} }", - "customer-name-starts-with": "Clientes cuyos nombres comiencen por '{{prefix}}'", - "type-user": "Usuario", - "type-users": "Usuarios", - "list-of-users": "{ count, plural, =1 {Un usuario} other {Lista de # usuarios} }", - "user-name-starts-with": "Usuarios cuyos nombres comiencen por '{{prefix}}'", - "type-dashboard": "Panel", - "type-dashboards": "Paneles", - "list-of-dashboards": "{ count, plural, =1 {Un panel} other {Lista de # paneles} }", - "dashboard-name-starts-with": "Paneles cuyos nombres comiencen por '{{prefix}}'", - "type-alarm": "Alarma", - "type-alarms": "Alarmas", - "list-of-alarms": "{ count, plural, =1 {Una alarma} other {Lista de # alarmas} }", - "alarm-name-starts-with": "Alarmas cuyos nombres comienzan con '{{prefix}}'", - "type-rulechain": "Cadena de reglas", - "type-rulechains": "Cadenas de reglas", - "list-of-rulechains": "{ count, plural, =1 {Una cadena de reglas} other {Lista de # cadenas de reglas} }", - "rulechain-name-starts-with": "Cadenas de reglas cuyos nombres comienzan con '{{prefix}}'", - "type-rulenode": "Nodo de reglas", - "type-rulenodes": "Nodos de reglas", - "list-of-rulenodes": "{ count, plural, =1 {Un nodo de reglas} other {Lista de # nodos de reglas} }", - "rulenode-name-starts-with": "Nodos de reglas cuyos nombres comienzan con '{{prefix}}'", - "type-current-customer": "Cliente Actual", - "type-current-tenant": "Propietario Actual", - "type-current-user": "Usuario Actual", - "type-current-user-owner": "Usuario Propietario Actual", - "type-widgets-bundle": "Paquete de widgets", - "type-widgets-bundles": "Paquetes de widgets", - "list-of-widgets-bundles": "{ count, plural, =1 {Un paquete de widget} other {Lista de # paquetes de widgets} }", - "type-widget-type": "Tipo de widget", - "type-widget-types": "Tipos de widget", - "list-of-widget-types": "{ count, plural, =1 {Un tipo de widget} other {Lista de # tipos de widgets} }", - "search": "Buscar entidades", - "selected-entities": "{ count, plural, =1 {1 entidad} other {# entidades} } seleccionadas", - "entity-label": "Etiqueta de entidad", - "entity-name": "Nombre de entidad", - "details": "Detalles de entidad", - "no-entities-prompt": "No se han encontrado entidades", - "no-data": "No hay datos que mostrar", - "columns-to-display": "Columnas a Mostrar", - "type-api-usage-state": "Estado de uso de la API", - "type-edge": "Edge", - "type-edges": "Edges", - "list-of-edges": "{count, plural, =1 {Un Edge} other {Lista de # Edges} }", - "edge-name-starts-with": "Edges cuyos nombres comienzan con '{{prefijo}}'", - "type-tb-resource": "Recurso", - "type-ota-package": "Paquete OTA", - "type-rpc": "RPC", - "type-queue": "Cola", - "type-notification": "Notificación", - "type-notification-rule": "Regla de notificación", - "type-notification-rules": "Reglas de notificación", - "list-of-notification-rules": "{ count, plural, =1 {Una regla de notificación} other {Lista de # reglas de notificación} }", - "type-notification-target": "Destinatario de notificación", - "type-notification-targets": "Destinatarios de notificación", - "list-of-notification-targets": "{ count, plural, =1 {Un destinatario} other {Lista de # destinatarios} }", - "type-notification-request": "Solicitud de notificaciones", - "type-notification-template": "Plantilla de notificaciones", - "type-notification-templates": "Plantillas de notificación", - "list-of-notification-templates": "{ count, plural, =1 {Una plantilla de notificación} other {Lista de # plantillas de notificación} }" + "url" : "URL", + "invalid-url-format" : "Formato de URL inválido", + "path" : "Ruta", + "invalid-path-format" : "Formato de ruta inválido", + "custom-page" : "Página personalizada", + "edit-page" : "Editar página", + "edit-custom-page" : "Editar página personalizada", + "delete-page" : "Eliminar página", + "qr-code-widget" : "Widget de código QR", + "type-here" : "Escribe aquí", + "configuration-dialog" : "Diálogo de configuración", + "configuration-app" : "Aplicación de configuración", + "configuration-step" : { + "prepare-environment-title" : "Preparar entorno de desarrollo", + "prepare-environment-text" : "La aplicación móvil ThingsBoard Flutter requiere el SDK de Flutter. Sigue las instrucciones para configurar el SDK.", + "get-source-code-title" : "Obtener código fuente de la aplicación", + "get-source-code-text" : "Puedes obtener el código fuente de la aplicación móvil ThingsBoard Flutter clonándolo desde el repositorio de GitHub:", + "configure-api-title" : "Configurar el endpoint de la API de ThingsBoard", + "configure-api-text" : "Abre el proyecto flutter_thingsboard_pe_app en tu editor/IDE. Edita:", + "configure-api-hint" : "Establece el valor de la constante thingsBoardApiEndpoint para que coincida con el endpoint API de tu instancia de servidor ThingsBoard. No uses los nombres de host “localhost” o “127.0.0.1”.", + "run-app-title" : "Ejecutar la aplicación", + "run-app-text" : "Ejecuta la aplicación según lo descrito en tu IDE.\nSi usas la terminal, ejecuta la aplicación con el siguiente comando:", + "more-information" : "Información detallada se encuentra en nuestra documentación de introducción.", + "getting-started" : "Guía de inicio", + "configure-package-title" : "Configurar paquete de la aplicación", + "configure-package-text" : "Puedes cambiar manualmente el paquete de la aplicación o usar una herramienta CLI de terceros.", + "configure-package-text-install" : "Para instalar la herramienta Rename CLI, ejecuta el siguiente comando:", + "configure-package-run-commands" : "Ejecuta estos comandos en el directorio raíz de tu proyecto:" + } + }, + "notification" : { + "action-button" : "Botón de acción", + "action-type" : "Tipo de acción", + "active" : "Activo", + "add-notification-recipients-group" : "Agregar grupo de destinatarios de notificaciones", + "add-notification-template" : "Agregar plantilla de notificación", + "add-recipient" : "Agregar destinatario", + "add-recipients" : "Agregar destinatarios", + "add-rule" : "Agregar regla", + "add-stage" : "Agregar etapa", + "add-template" : "Agregar plantilla", + "after" : "Después", + "alarm-assignment-trigger-settings" : "Configuración del disparador de asignación de alarma", + "alarm-comment-trigger-settings" : "Configuración del disparador de comentario de alarma", + "alarm-trigger-settings" : "Configuración del disparador de alarma", + "all" : "Todos", + "api-feature-hint" : "Si el campo está vacío, el disparador se aplicará a todas las funciones de la API", + "api-usage-trigger-settings" : "Configuración del disparador de uso de API", + "new-platform-version-trigger-settings" : "Configuración del disparador de nueva versión de plataforma", + "rate-limits-trigger-settings" : "Configuración del disparador por límites de tasa excedidos", + "task-processing-failure-trigger-settings" : "Configuración del disparador por error en procesamiento de tareas", + "at-least-one-should-be-selected" : "Debe seleccionarse al menos uno", + "basic-settings" : "Configuraciones básicas", + "button-text" : "Texto del botón", + "button-text-required" : "Se requiere el texto del botón", + "button-text-max-length" : "El texto del botón debe tener menos o igual a {{ length }} caracteres", + "compose" : "Redactar", + "conversation" : "Conversación", + "conversation-required" : "Se requiere una conversación", + "copy-notification-template" : "Copiar plantilla de notificación", + "copy-rule" : "Copiar regla", + "copy-template" : "Copiar plantilla", + "create-new" : "Crear nuevo", + "created" : "Creado", + "customize-messages" : "Personalizar mensajes", + "delete-notification-text" : "Ten cuidado, después de la confirmación, la notificación será irrecuperable.", + "delete-notification-title" : "¿Estás seguro de que deseas eliminar la notificación?", + "delete-notifications-text" : "Ten cuidado, después de la confirmación, las notificaciones serán irrecuperables.", + "delete-notifications-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 notificación} other {# notificaciones} }?", + "delete-recipient-text" : "Ten cuidado, después de la confirmación, el destinatario será irrecuperable.", + "delete-recipient-title" : "¿Estás seguro de que deseas eliminar al destinatario '{{recipientName}}'?", + "delete-recipients-text" : "Ten cuidado, después de la confirmación, los destinatarios serán irrecuperables.", + "delete-recipients-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 destinatario} other {# destinatarios} }?", + "delete-request-text" : "Ten cuidado, después de la confirmación, la solicitud será irrecuperable.", + "delete-request-title" : "¿Estás seguro de que deseas eliminar la solicitud?", + "delete-requests-text" : "Ten cuidado, después de la confirmación, las solicitudes serán irrecuperables.", + "delete-requests-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 solicitud} other {# solicitudes} }?", + "delete-rule-text" : "Ten cuidado, después de la confirmación, la regla será irrecuperable.", + "delete-rule-title" : "¿Estás seguro de que deseas eliminar la regla '{{ruleName}}'?", + "delete-rules-text" : "Ten cuidado, después de la confirmación, las reglas serán irrecuperables.", + "delete-rules-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 regla} other {# reglas} }?", + "delete-template-text" : "Ten cuidado, después de la confirmación, la plantilla será irrecuperable.", + "delete-template-title" : "¿Estás seguro de que deseas eliminar la plantilla '{{templateName}}'?", + "delete-templates-text" : "Ten cuidado, después de la confirmación, las plantillas serán irrecuperables.", + "delete-templates-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 plantilla} other {# plantillas} }?", + "deleted" : "Eliminado", + "delivery-method" : { + "delivery-method" : "Método de entrega", + "email" : "Correo electrónico", + "email-preview" : "Vista previa de notificación por correo", + "slack" : "Slack", + "slack-preview" : "Vista previa de notificación por Slack", + "microsoft-teams" : "Microsoft Teams", + "microsoft-teams-preview" : "Vista previa de notificación por Microsoft Teams", + "sms" : "SMS", + "sms-preview" : "Vista previa de notificación por SMS", + "web" : "Web", + "web-preview" : "Vista previa de notificación web", + "mobile-app" : "Aplicación móvil", + "mobile-app-preview" : "Vista previa de notificación por aplicación móvil" }, - "entity-field": { - "created-time": "Hora de creación", - "name": "Nombre", - "type": "Tipo", - "first-name": "Nombre", - "last-name": "Apellido", - "email": "Correo electrónico", - "title": "Título", - "country": "País", - "state": "Estado", - "city": "Ciudad", - "address": "Dirección", - "address2": "Dirección 2", - "zip": "Código postal", - "phone": "Teléfono", - "label": "Etiqueta" + "delivery-method-not-configure-click" : "El método de entrega no está configurado. Haz clic para configurar.", + "delivery-method-not-configure-contact" : "El método de entrega no está configurado. Contacta con tu administrador del sistema.", + "delivery-methods" : "Métodos de entrega", + "description" : "Descripción", + "device-activity-trigger-settings" : "Configuración del disparador de actividad del dispositivo", + "device-list-rule-hint" : "Si el campo está vacío, el disparador se aplicará a todos los dispositivos", + "device-profiles-list-rule-hint" : "Si el campo está vacío, el disparador se aplicará a todos los perfiles de dispositivo", + "disabled" : "Deshabilitado", + "edge-trigger-settings" : "Configuración del disparador de Edge", + "edge-list-rule-hint" : "Si el campo está vacío, el disparador se aplicará a todas las instancias de Edge", + "edit-notification-recipients-group" : "Editar grupo de destinatarios de notificaciones", + "edit-notification-template" : "Editar plantilla de notificación", + "edit-rule" : "Editar regla", + "edit-template" : "Editar plantilla", + "enabled" : "Habilitado", + "entities-limit-trigger-settings" : "Configuración del disparador por límite de entidades", + "entity-action-trigger-settings" : "Configuración del disparador de acción de entidad", + "entity-type" : "Tipo de entidad", + "escalation-chain" : "Cadena de escalado", + "failed-send" : "Fallos de envío", + "fails" : "{ count, plural, =1 {1 fallo} other {# fallos} }", + "filter" : "Filtro", + "first-recipient" : "Primer destinatario", + "inactive" : "Inactivo", + "inbox" : "Bandeja de entrada", + "notification-inbox" : "Notificaciones / Bandeja de entrada", + "input-field-support-templatization" : "El campo de entrada admite plantillas.", + "input-fields-support-templatization" : "Los campos de entrada admiten plantillas.", + "link" : "Enlace", + "link-required" : "El enlace es obligatorio", + "link-type" : { + "dashboard" : "Abrir tablero", + "link" : "Abrir enlace URL" }, - "entity-view": { - "entity-view": "Vista de entidad", - "entity-view-required": "Vista de entidad es requerido.", - "entity-views": "Vistas de entidad", - "management": "Gestión de vistas de entidad", - "view-entity-views": "Ver vista de entidad", - "entity-view-alias": "Alias de vista de entidad", - "aliases": "Alias de vista de entidad", - "no-alias-matching": "'{{alias}}' no encontrado.", - "no-aliases-found": "No se encontraron alias.", - "no-key-matching": "'{{key}}' no encontrada.", - "no-keys-found": "No se encontraron claves.", - "create-new-alias": "¡Crear un nuevo!", - "create-new-key": "¡Crear una nueva!", - "duplicate-alias-error": "Alias duplicado'{{alias}}'.
Los alias de Entity View deben ser únicos en el panel.", - "configure-alias": "Configurar alias '{{alias}}'", - "no-entity-views-matching": "No se encontraron vistas que coincidan con '{{entity}}'.", - "public": "Público", - "alias": "Alias", - "alias-required": "Alias de vista de entidad es requerido.", - "remove-alias": "Borrar alias de la vista de entidad", - "add-alias": "Añadir alias a la vista de entidad", - "name-starts-with": "Nombre de vista de entidad comienza con", - "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", - "entity-view-list": "Lista de vistas de entidad", - "use-entity-view-name-filter": "Usar el filtro", - "entity-view-list-empty": "No hay vistas de entidad seleccionadas.", - "entity-view-name-filter-required": "Nombre del filtro de vista de entidad es requerido.", - "entity-view-name-filter-no-entity-view-matched": "No se encontraron vistas de entidad que comiencen con '{{entityView}}'.", - "add": "Añadir vista de entidad", - "entity-view-public": "Vista de entidad es pública", - "assign-to-customer": "Asignar a cliente", - "assign-entity-view-to-customer": "Asignar vista de entidad a cliente", - "assign-entity-view-to-customer-text": "Por favor, seleccione las vistas de entidad para asignar al cliente", - "assign-entity-view-to-edge-title": "Asignar vista(s) de entidad a Edge", - "no-entity-views-text": "No se encontraron vistas de entidad", - "assign-to-customer-text": "Por favor, seleccione el cliente para asignar la vista de entidad", - "entity-view-details": "Detalles de la vista de entidad", - "add-entity-view-text": "Añadir nueva vista de entidad", - "delete": "Borrar vista de entidad", - "assign-entity-views": "Asignar vistas de entidad", - "assign-entity-views-text": "Asignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } a cliente", - "delete-entity-views": "Borrar vistas de entidad", - "unassign-from-customer": "Anular asignación a cliente", - "unassign-entity-views": "Anular asignación de vistas de entidad", - "unassign-entity-views-action-title": "Anular asignación { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } al cliente", - "assign-new-entity-view": "Asignar nueva vista de entidad", - "delete-entity-view-title": "¿Está seguro que quiere borrar la vista de entidad '{{entityViewName}}'?", - "delete-entity-view-text": "¡Cuidado! Tras la confirmación, la vista de la entidad y todos los datos relacionados serán irrecuperables.", - "delete-entity-views-title": "¿Está seguro que quiere borrar las vistas de entidad { count, plural, =1 {1 entityView} other {# entityViews} }?", - "delete-entity-views-action-title": "Borrar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }", - "delete-entity-views-text": "¡Cuidado! Tras la confirmación, todas las vistas de entidades seleccionadas se eliminarán y todos los datos relacionados serán irrecuperables.", - "unassign-entity-view-title": "¿Está seguro que quiere anular la asignación de la vista de entidad '{{entityViewName}}'?", - "unassign-entity-view-text": "Tras la confirmación, la vista de la entidad quedará sin asignar y el cliente no podrá acceder a ella.", - "unassign-entity-view": "Anular asignación de la vista de entidad", - "unassign-entity-views-title": "¿Está seguro que quiere anular la asignación de { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", - "unassign-entity-views-text": "Tras la confirmación, todas las vistas de entidades seleccionadas quedarán sin asignar y el cliente no podrá acceder a ellas.", - "entity-view-type": "Tipo de vista de entidad", - "entity-view-type-required": "Tipo de vista de entidad es requerido.", - "select-entity-view-type": "Seleccione el tipo de vista de entidad", - "enter-entity-view-type": "Teclee el tipo de vista de entidad", - "any-entity-view": "Cualquier vista de entidad", - "no-entity-view-types-matching": "No se encontraron tipos de vista de entidad que coincidan con '{{entitySubtype}}'.", - "entity-view-type-list-empty": "No hay tipos de vista de entidad seleccionados.", - "entity-view-types": "Tipos de vista de entidad", - "created-time": "Fecha de creación", - "name": "Nombre", - "name-required": "Nombre Requerido.", - "name-max-length": "Nombre debe ser menor de 256", - "type-max-length": "Tipo de vista de entidad debe ser menor de 256", - "description": "Descripción", - "events": "Eventos", - "details": "Detalles", - "copyId": "Copiar el Id de la vista de entidad", - "idCopiedMessage": "El Id de la vista de entidad se ha copiado al portapapeles", - "assignedToCustomer": "Asignado a cliente", - "unable-entity-view-device-alias-title": "No se puede eliminar el alias de vista de entidad", - "unable-entity-view-device-alias-text": "El alias del dispositivo '{{entityViewAlias}}' no se puede borrar porque está siendo usado por el widget(s):
{{widgetsList}}", - "select-entity-view": "Seleccionar vista de entidad", - "make-public": "Hacer pública la vista de entidad", - "make-private": "Hacer que la vista de entidad sea privada", - "start-date": "Fecha de inicio", - "start-ts": "Tiempo de inicio", - "end-date": "Fecha de finalización", - "end-ts": "Tiempo de finalización", - "date-limits": "Limites de fecha", - "client-attributes": "Atributos de cliente", - "shared-attributes": "Atributos compartidos", - "server-attributes": "Atributos de servidor", - "timeseries": "Series temporales", - "client-attributes-placeholder": "Atributos de cliente", - "shared-attributes-placeholder": "Atributos compartidos", - "server-attributes-placeholder": "Atributos de servidor", - "timeseries-placeholder": "Series temporales", - "target-entity": "Entidad objetivo", - "attributes-propagation": "Propagación de atributos", - "attributes-propagation-hint": "La vista de entidad copiará automáticamente los atributos especificados de la entidad de destino cada vez que guarde o actualice esta vista de entidad. Por razones de rendimiento, los atributos de entidad objetivo no se propagan a la vista de entidad en cada cambio de atributo. Puede habilitar la propagación automática configurando el nodo de la regla \"copiar a la vista\" en su cadena de reglas y vincular los mensajes \"Atributos de la publicación\" y \"Atributos actualizados\" al nuevo nodo de la regla.", - "timeseries-data": "Datos de series temporales", - "timeseries-data-hint": "Configure las claves de los datos de las series temporales de la entidad de destino que serán accesibles para la vista de la entidad. Los datos de esta serie temporal son de solo lectura.", - "search": "Buscar vistas de entidad", - "selected-entity-views": "{ count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } seleccionadas", - "make-public-entity-view-title": "¿Está seguro de que desea que la vista de entidad '{{entityViewName}}' sea pública?", - "make-public-entity-view-text": "Tras la confirmación, la vista de la entidad y todos sus datos se harán públicos y accesibles para otros.", - "make-private-entity-view-title": "¿Está seguro de que desea que la vista de entidad '{{entityViewName}}' sea privada?", - "make-private-entity-view-text": "Tras la confirmación, la vista de la entidad y todos sus datos se harán privados y no serán accesibles para otros.", - "assign-entity-view-to-edge": "Asignar vista (s) de entidad a Edge", - "assign-entity-view-to-edge-text": "Seleccione las vistas de entidad para asignar al Edge", - "unassign-entity-view-from-edge-title": "¿Está seguro de que desea anular la asignación de la vista de entidad '{{entityViewName}}'?", - "unassign-entity-view-from-edge-text": "Después de la confirmación, la vista de entidad quedará sin asignar y el Edge no podrá acceder a ella", - "unassign-entity-views-from-edge-action-title": "Anular asignación {count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } del Edge", - "unassign-entity-view-from-edge": "Anular asignación de vista de entidad", - "unassign-entity-views-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", - "unassign-entity-views-from-edge-text": "Después de la confirmación, todas las vistas de entidad seleccionadas no serán asignadas y el Edge no podrá acceder a ellas" + "loading-notifications" : "Cargando notificaciones...", + "management" : "Gestión de notificaciones", + "mark-all-as-read" : "Marcar todo como leído", + "mark-as-read" : "Marcar como leído", + "message" : "Mensaje", + "message-required" : "El mensaje es obligatorio", + "message-max-length" : "El mensaje debe tener menos o igual a {{ length }} caracteres", + "name" : "Nombre", + "name-required" : "El nombre es obligatorio", + "new-notification" : "Nueva notificación", + "no-inbox-notification" : "No se encontraron notificaciones", + "no-notification-request" : "No hay solicitudes de notificación", + "no-notification-templates" : "No se encontraron plantillas de notificación", + "no-notifications-yet" : "Aún no hay notificaciones", + "no-recipients-notification" : "No hay destinatarios para la notificación", + "no-recipients-matching" : "No se encontraron destinatarios que coincidan con '{{entity}}'.", + "no-recipients-text" : "No se encontraron destinatarios", + "no-rule" : "No hay reglas configuradas", + "no-rules-notification" : "No hay reglas para la notificación", + "no-severity-found" : "No se encontró severidad", + "no-severity-matching" : "'{{severity}}' no encontrado.", + "no-template-matching" : "No se encontraron recursos que coincidan con '{{template}}'.", + "not-found-slack-recipient" : "Destinatario de Slack no encontrado", + "notification" : "Notificación", + "notification-center" : "Centro de notificaciones", + "notification-tap-action" : "Acción al tocar notificación", + "notification-tap-action-hint" : "Si no está habilitado, se utilizará el tablero de alarmas predeterminado", + "notify" : "notificar", + "notify-again" : "Notificar de nuevo", + "notify-alarm-action" : { + "acknowledged" : "Alarma reconocida", + "assigned" : "Alarma asignada", + "cleared" : "Alarma aclarada", + "created" : "Alarma creada", + "severity-changed" : "Severidad de la alarma cambiada", + "unassigned" : "Alarma desasignada" }, - "event": { - "event-type": "Tipo de evento", - "events-filter": "Filtro de Eventos", - "clean-events": "Borrar Eventos", - "type-error": "Error", - "type-lc-event": "Ciclo de vida del evento", - "type-stats": "Estadísticas", - "type-debug-rule-node": "Debug", - "type-debug-rule-chain": "Debug", - "no-events-prompt": "Ningún evento encontrado.", - "error": "Error", - "alarm": "Alarma", - "event-time": "Hora del evento", - "server": "Servidor", - "body": "Cuerpo", - "method": "Método", - "type": "Tipo", - "message": "Mensaje", - "message-id": "Id Mensaje", - "copy-message-id": "Copiar Id de mensaje", - "message-type": "Tipo Mensaje", - "data-type": "Tipo de Datos", - "relation-type": "Tipo de relación", - "metadata": "Metadatos", - "data": "Datos", - "event": "Evento", - "status": "Estado", - "success": "Éxito", - "failed": "Fallo", - "messages-processed": "Mensajes procesados", - "max-messages-processed": "Máximo de mensajes procesados", - "min-messages-processed": "Mínimo de mensajes procesados", - "errors-occurred": "Ocurrieron errores", - "max-errors-occurred": "Errores máximos ocurridos", - "min-errors-occurred": "Errores mínimos ocurridos", - "min-value": "El valor mínimo es 0.", - "all-events": "Todos", - "has-error": "Tiene error", - "entity-id": "Id de Entidad", - "copy-entity-id": "Copiar Id de entidad", - "entity-type": "Tipo de entidad", - "clear-filter": "Limpiar Filtro", - "clear-request-title": "Borrar todos los eventos", - "clear-request-text": "Estás seguro de borrar todos los eventos?", - "started": "Arrancado", - "updated": "Actualizado", - "stopped": "Parado" + "notify-on" : "Notificar en", + "notify-on-comment-update" : "Notificar en actualización de comentario", + "notify-on-required" : "Se requiere 'Notificar en'", + "notify-on-unassign" : "Notificar en desasignación", + "notify-only-user-comments" : "Notificar solo comentarios de usuario", + "only-rule-chain-lifecycle-failures" : "Solo fallos en el ciclo de vida de la cadena de reglas", + "only-rule-node-lifecycle-failures" : "Solo fallos en el ciclo de vida del nodo de regla", + "platform-users" : "Usuarios de la plataforma", + "rate-limits" : "Límites de tasa", + "rate-limits-hint" : "Si el campo está vacío, el disparador se aplicará a todos los límites de tasa", + "recipient" : "Destinatario", + "recipient-group" : "Grupo de destinatarios", + "recipient-type" : { + "affected-tenant-administrators" : "Administradores del inquilino afectados", + "affected-user" : "Usuario afectado", + "all-users" : "Todos los usuarios", + "customer-users" : "Usuarios del cliente", + "system-administrators" : "Administradores del sistema", + "tenant-administrators" : "Administradores del inquilino", + "user-filters" : "Filtro de usuarios", + "user-list" : "Lista de usuarios", + "users-entity-owner" : "Usuarios del propietario de la entidad" }, - "extension": { - "extensions": "Extensiones", - "selected-extensions": "{ count, plural, =1 {1 extensión} other {# extensiones} } seleccionadas", - "type": "Tipo", - "key": "Clave", - "value": "Valor", - "id": "ID", - "extension-id": "ID de extensión", - "extension-type": "Tipo de extensión", - "transformer-json": "JSON *", - "unique-id-required": "El id de extensión ya existe.", - "delete": "Borrar Extensión", - "add": "Añadir Extensión", - "edit": "Editar Extensión", - "delete-extension-title": "Eliminar la extensión '{{extensionId}}'?", - "delete-extension-text": "Atención, tras la confirmación la extensión y sus datos serán borrados e irrecuperables.", - "delete-extensions-title": "Eliminar las extensiones { count, plural, =1 {1 extensión} other {# extensiones} }?", - "delete-extensions-text": "Atención, tras la confirmación todas las extensiones seleccionadas y sus datos serán borrados e irrecuperables.", - "converters": "Convertidores", - "converter-id": "Id de convertidor", - "configuration": "Configuración", - "converter-configurations": "Ajustes de convertidor", - "token": "Tóken de seguridad", - "add-converter": "Añadir convertidor", - "add-config": "Añadir configuración de convertidor", - "device-name-expression": "Expresión del nombre de dispositivo", - "device-type-expression": "Expresión del tipo de dispositivo", - "custom": "Personalizado", - "to-double": "Para duplicar", - "transformer": "Transformador", - "json-required": "Se requiere el JSON del transformador.", - "json-parse": "No ha sido posible analizar el JSON del transformador.", - "attributes": "Atributos", - "add-attribute": "Añadir Atributo", - "add-map": "Agregar elemento de mapeado", - "timeseries": "Series de tiempo", - "add-timeseries": "Añadir series de tiempo", - "field-required": "Campo requerido", - "brokers": "Brokers", - "add-broker": "Añadir broker", - "host": "Host", - "port": "Puerto", - "port-range": "El puerto debe estar en un rango de 1 a 65535.", - "ssl": "SSL", - "credentials": "Credenciales", - "username": "Usuario", - "password": "Contraseña", - "retry-interval": "Intervalo de reintento en milisegundos", - "anonymous": "Anónimo", - "basic": "Básico", - "pem": "PEM", - "ca-cert": "Archivo de certificado CA *", - "private-key": "Archivo de clave privado *", - "cert": "Archivo de certificado *", - "no-file": "Ningún archivo seleccionado.", - "drop-file": "Colocar un archivo o hacer clic para seleccionar un archivo para cargar.", - "mapping": "Mapeo", - "topic-filter": "Filtro de tema", - "converter-type": "Tipo de conversor", - "converter-json": "Json", - "json-name-expression": "Expresión json para nombre del dispositivo", - "topic-name-expression": "Expresión temática para nombre del dispositivo", - "json-type-expression": "Expresión json para tipo de dispositivo", - "topic-type-expression": "Expresión temática para tipo de dispositivo", - "attribute-key-expression": "Expresión para clave de atributo", - "attr-json-key-expression": "Expresión json para clave de atributo", - "attr-topic-key-expression": "Expresión temática para clave de atributo", - "request-id-expression": "Expresión para solicitud de ID", - "request-id-json-expression": "Expresión json para solicitud de ID", - "request-id-topic-expression": "Expresión temática para solicitud de ID", - "response-topic-expression": "Expresión temática para respuesta", - "value-expression": "Expresión para valor", - "topic": "Tema", - "timeout": "Tiempo de espera en milisegundos", - "converter-json-required": "Conversor json es requerido.", - "converter-json-parse": "No se puede analizar el conversor json.", - "filter-expression": "Expresión para filtro", - "connect-requests": "Solicitudes de conexión", - "add-connect-request": "Agregar solicitudes de conexión", - "disconnect-requests": "Solicitudes de desconexión", - "add-disconnect-request": "Agregar solicitud de desconexión", - "attribute-requests": "Solicitudes de atributo", - "add-attribute-request": "Agregar solicitudes de atributo", - "attribute-updates": "Actualizaciones de atributo", - "add-attribute-update": "Agregar actualizaciones de atributo", - "server-side-rpc": "RPC lado servidor", - "add-server-side-rpc-request": "Agregar solicitud RPC lado servidor", - "device-name-filter": "Filtro de nombre de dispositivo", - "attribute-filter": "Filtro de atributo", - "method-filter": "Filtro de método", - "request-topic-expression": "Expresión temática para solicitud", - "response-timeout": "Tiempo de espera de respuesta en milisegundos", - "topic-expression": "Expresión temática", - "client-scope": "Alcance del cliente", - "add-device": "Agregar dispositivo", - "opc-server": "Servidores", - "opc-add-server": "Agregar servidor", - "opc-add-server-prompt": "Por favor agregar servidor", - "opc-application-name": "Nombre de aplicación", - "opc-application-uri": "Aplicación URI", - "opc-scan-period-in-seconds": "Período de exploración en segundos", - "opc-security": "Seguridad", - "opc-identity": "Identidad", - "opc-keystore": "Almacén de claves", - "opc-type": "Tipo", - "opc-keystore-type": "Tipo", - "opc-keystore-location": "Ubicación *", - "opc-keystore-password": "Contraseña", - "opc-keystore-alias": "Alias", - "opc-keystore-key-password": "Clave de contraseña", - "opc-device-node-pattern": "Patrón de nodo de dispositivo", - "opc-device-name-pattern": "Patrón de nombre de dispositivo", - "modbus-server": "Servidores/esclavos", - "modbus-add-server": "Agregar servidor/esclavo", - "modbus-add-server-prompt": "Por favor agregar servidor/esclavo", - "modbus-transport": "Transporte", - "modbus-tcp-reconnect": "Reconexión automática", - "modbus-rtu-over-tcp": "RTU sobre TCP", - "modbus-port-name": "Nombre del puerto serial", - "modbus-encoding": "Codificación", - "modbus-parity": "Paridad", - "modbus-baudrate": "Tasa de baudios", - "modbus-databits": "Bits de datos", - "modbus-stopbits": "Bits de parada", - "modbus-databits-range": "Bits de datos deben estar en un rango entre 7 y 8.", - "modbus-stopbits-range": "Bits de parada deben estar en un rango entre 1 a 2.", - "modbus-unit-id": "ID de unidad", - "modbus-unit-id-range": "ID de unidad debe estar en un rango entre 1 a 247.", - "modbus-device-name": "Nombre del dispositivo", - "modbus-poll-period": "Período de sondeo (ms)", - "modbus-attributes-poll-period": "Atributos del período de sondeo (ms)", - "modbus-timeseries-poll-period": "Período de sondeo de las series temporales (ms)", - "modbus-poll-period-range": "El período de sondeo debe ser una valor positivo.", - "modbus-tag": "Etiqueta", - "modbus-function": "Función", - "modbus-register-address": "Dirección del registro", - "modbus-register-address-range": "Dirección del registro debe estar en un rango entre 0 y 65535.", - "modbus-register-bit-index": "Índice de bit", - "modbus-register-bit-index-range": "Índice de bit debe estar en un rango entre 0 y 15.", - "modbus-register-count": "Contador del registro", - "modbus-register-count-range": "Contador del registro debe ser un valor positivo.", - "modbus-byte-order": "Orden del byte", - "sync": { - "status": "Estado", - "sync": "Sincronizado", - "not-sync": "No Sincronizado", - "last-sync-time": "Hora de última sincronización", - "not-available": "No disponible" - }, - "export-extensions-configuration": "Exportar configuración de extensiones", - "import-extensions-configuration": "Importar configuración de extensiones", - "import-extensions": "Importar extensiones", - "import-extension": "Importar extensión", - "export-extension": "Exportar extensión", - "file": "Fichero de extensiones", - "invalid-file-error": "Fichero de extensiones inválido" + "recipients" : "Destinatarios", + "notification-recipient" : "Destinatario de la notificación", + "notification-recipient-required" : "El destinatario de la notificación es obligatorio.", + "notification-recipients" : "Notificaciones / Destinatarios", + "recipients-count" : "{ count, plural, =1 {1 destinatario} other {# destinatarios} }", + "recipients-required" : "Se requieren destinatarios", + "refresh-allow-delivery-method" : "Actualizar método de entrega permitido", + "request-search" : "Buscar solicitud", + "request-status" : { + "processing" : "Procesando", + "scheduled" : "Programado", + "sent" : "Enviado" }, - "feature": { - "advanced-features": "Características avanzadas" + "review" : "Revisar", + "rule" : "Regla", + "rule-chain-list-rule-hint" : "Si el campo está vacío, el disparador se aplicará a todas las cadenas de reglas", + "rule-engine-events-trigger-settings" : "Configuración de disparadores de eventos del motor de reglas", + "rule-engine-filter" : "Filtro del motor de reglas", + "rule-name" : "Nombre de la regla", + "rule-name-required" : "El nombre es obligatorio", + "rule-disable" : "Desactivar regla de notificación", + "rule-enable" : "Activar regla de notificación", + "rule-node-filter" : "Filtro de nodo de regla", + "rules" : "Reglas", + "notification-rules" : "Notificaciones / Reglas", + "scheduler-later" : "Programar para más tarde", + "search-notification" : "Buscar notificaciones", + "search-recipients" : "Buscar destinatarios", + "search-rules" : "Buscar reglas", + "search-templates" : "Buscar plantillas", + "see-documentation" : "Ver documentación", + "selected-notifications" : "{ count, plural, =1 {1 notificación} other {# notificaciones} } seleccionada(s)", + "selected-recipients" : "{ count, plural, =1 {1 destinatario} other {# destinatarios} } seleccionado(s)", + "selected-requests" : "{ count, plural, =1 {1 solicitud} other {# solicitudes} } seleccionada(s)", + "selected-rules" : "{ count, plural, =1 {1 regla} other {# reglas} } seleccionada(s)", + "selected-template" : "{ count, plural, =1 {1 plantilla} other {# plantillas} } seleccionada(s)", + "send-notification" : "Enviar notificación", + "sent" : "Enviado", + "setup" : "Configuración", + "notification-sent" : "Notificaciones / Enviadas", + "set-entity-from-notification" : "Establecer entidad de la notificación en el estado del tablero", + "slack-chanel-type" : "Tipo de canal de Slack", + "slack-chanel-types" : { + "direct" : "Mensaje directo", + "private-channel" : "Canal privado", + "public-channel" : "Canal público" }, - "filter": { - "add": "Añadir filtro", - "edit": "Editar filtro", - "name": "Nombre de filtro", - "name-required": "Se requiere nombre de filtro.", - "duplicate-filter": "Ya existe un filtro con el mismo nombre.", - "filters": "Filtros", - "unable-delete-filter-title": "Error borrando filtro", - "unable-delete-filter-text": "El filtro '{{filter}}' no puede ser borrado debido a que está siendo usado actualmente por los siguientes widgets:
{{widgetsList}}", - "duplicate-filter-error": "Se ha encontrado un filtro duplicado '{{filter}}'.
Los filtros deben ser únicos en el panel.", - "missing-key-filters-error": "No se encontró la clave de filtros para el filtro '{{filter}}'.", - "filter": "Filtro", - "editable": "Editable", - "no-filters-found": "No se encontraron filtros.", - "no-filter-text": "No se ha especificado filtro", - "add-filter-prompt": "Por favos, añadir filtro", - "no-filter-matching": "'{{filter}}' no encontrado.", - "create-new-filter": "Crear un filtro nuevo!", - "create-new": "Crear nuevo", - "filter-required": "Se requiere filtro.", - "operation": { - "operation": "Operación", - "equal": "igual", - "not-equal": "no igual", - "starts-with": "comienza con", - "ends-with": "acaba con", - "contains": "contiene", - "not-contains": "no contiene", - "greater": "mayor que", - "less": "menor que", - "greater-or-equal": "mayor o igual", - "less-or-equal": "menor o igual", - "and": "y", - "or": "o", - "in": "en", - "not-in": "no en" - }, - "ignore-case": "Ignorar mayús/minus", - "value": "Valor", - "remove-filter": "Borrar filtro", - "duplicate-filter-action": "Duplicar filtro", - "preview": "Vista previa de filtro", - "no-filters": "No hay filtros configurados", - "add-filter": "Añadir filtro", - "add-complex-filter": "Añadir filtro complejo", - "add-complex": "Agregar filtro complejo", - "complex-filter": "Filtro complejo", - "edit-complex-filter": "Editar filtro complejo", - "edit-filter-user-params": "Editar parámetros de usuario del filtro", - "filter-user-params": "Filtro de parámetros de usuario (predicado)", - "user-parameters": "Parámetros de usuario", - "display-label": "Etiqueta a mostrar", - "order-priority": "Prioridad orden de campos", - "key-filter": "Filtros (clave)", - "key-filters": "Filtros (claves)", - "key-name": "Nombre de clave", - "key-name-required": "Se requiere nombre de clave.", - "key-type": { - "key-type": "Tipo de clave", - "attribute": "Atributo", - "timeseries": "Timeseries", - "entity-field": "Campo de entidad", - "constant": "Constante" - }, - "value-type": { - "value-type": "Tipo de valor", - "string": "Cadena", - "numeric": "Numerico", - "boolean": "Booleano", - "date-time": "Fecha/Hora" - }, - "value-type-required": "Se requiere tipo de valor.", - "key-value-type-change-title": "Cambiar el tipo de valor de la clave?", - "key-value-type-change-message": "Si confirmas el nuevo tipo, todos los filtros se borrarán.", - "no-key-filters": "No hay filtros claves configurados", - "add-key-filter": "Añadir filtro clave", - "remove-key-filter": "Borrar filtro clave", - "edit-key-filter": "Editar filtro clave", - "date": "Fecha", - "time": "Hora", - "current-tenant": "Admin actual", - "current-customer": "Cliente actual", - "current-user": "Usuario actual", - "current-device": "Dispositivo actual", - "default-value": "Valor por defecto", - "dynamic-source-type": "Tipo de origen dinámico", - "dynamic-value": "Valor dinámico", - "no-dynamic-value": "Sin valor dinámico", - "source-attribute": "Atributo de origen", - "switch-to-dynamic-value": "Cambiar a valor dinámico", - "switch-to-default-value": "Cambiar a valor por defecto", - "inherit-owner": "Heredar de propietario", - "source-attribute-not-set": "Si los atributos de origen no están seleccionados" + "start-from-scratch" : "Empezar desde cero", + "status" : "Estado", + "stop-escalation-alarm-status-become" : "Detener la escalada al cambiar el estado de la alarma a:", + "subject" : "Asunto", + "subject-required" : "El asunto es obligatorio", + "subject-max-length" : "El asunto debe tener menos o igual a {{ length }} caracteres", + "template" : "Plantilla", + "template-name" : "Nombre de la plantilla", + "template-required" : "La plantilla es obligatoria", + "template-type" : { + "alarm" : "Alarma", + "alarm-assignment" : "Asignación de alarma", + "alarm-comment" : "Comentario de alarma", + "api-usage-limit" : "Límite de uso de API", + "device-activity" : "Actividad del dispositivo", + "entities-limit" : "Límite de entidades", + "entity-action" : "Acción sobre entidad", + "general" : "General", + "rule-engine-lifecycle-event" : "Evento del ciclo de vida del motor de reglas", + "rule-node" : "Nodo de regla", + "new-platform-version" : "Nueva versión de la plataforma", + "rate-limits" : "Límites de tasa excedidos", + "edge-communication-failure" : "Fallo de comunicación con el edge", + "edge-connection" : "Conexión con el edge", + "task-processing-failure" : "Fallo de procesamiento de tarea" }, - "fullscreen": { - "expand": "Expandir a Pantalla Completa", - "exit": "Salir de Pantalla Completa", - "toggle": "Cambiar el modo de Pantalla Completa", - "fullscreen": "Pantalla Completa" + "templates" : "Plantillas", + "notification-templates" : "Notificaciones / Plantillas", + "tenant-profiles-list-rule-hint" : "Si el campo está vacío, el disparador se aplicará a todos los perfiles de inquilino", + "tenants-list-rule-hint" : "Si el campo está vacío, el disparador se aplicará a todos los inquilinos", + "threshold" : "Umbral", + "theme-color" : "Color del tema", + "time" : "Hora", + "track-rule-node-events" : "Rastrear eventos del nodo de regla", + "trigger" : { + "alarm" : "Alarma", + "alarm-assignment" : "Asignación de alarma", + "alarm-comment" : "Comentario de alarma", + "api-usage-limit" : "Límite de uso de API", + "device-activity" : "Actividad del dispositivo", + "entities-limit" : "Límite de entidades", + "entity-action" : "Acción sobre entidad", + "rule-engine-lifecycle-event" : "Evento del ciclo de vida del motor de reglas", + "new-platform-version" : "Nueva versión de la plataforma", + "rate-limits" : "Límites de tasa excedidos", + "edge-connection" : "Conexión con el edge", + "edge-communication-failure" : "Fallo de comunicación con el edge", + "task-processing-failure" : "Fallo de procesamiento de tarea", + "trigger" : "Disparador", + "trigger-required" : "El disparador es obligatorio" }, - "function": { - "function": "Función" + "type" : "Tipo", + "unread" : "No leído", + "updated" : "Actualizado", + "use-deprecated-webhook-connectors" : "Usar conectores Webhook obsoletos", + "use-old-api" : "Usar API antigua", + "use-template" : "Usar plantilla", + "view-all" : "Ver todo", + "warning" : "Advertencia", + "webhook-url" : "URL del Webhook", + "webhook-url-required" : "La URL del Webhook es obligatoria", + "workflow-url" : "URL del flujo de trabajo", + "workflow-url-required" : "La URL del flujo de trabajo es obligatoria", + "channel-name" : "Nombre del canal", + "channel-name-required" : "El nombre del canal es obligatorio", + "settings" : { + "notification-settings" : "Configuración de notificaciones", + "reset-all" : "Restablecer todas las configuraciones", + "reset-all-title" : "¿Estás seguro de que deseas restablecer el formulario?", + "reset-all-text" : "Después de la confirmación, el formulario de configuración se restablecerá al valor predeterminado y se guardará.", + "type" : "Tipo", + "enable-all" : "Habilitar todo", + "disable-all" : "Deshabilitar todo", + "delivery-not-configured" : "El método de entrega no está configurado" + } + }, + "ota-update" : { + "add" : "Agregar paquete", + "assign-firmware" : "Firmware asignado", + "assign-firmware-required" : "El firmware asignado es obligatorio", + "assign-software" : "Software asignado", + "assign-software-required" : "El software asignado es obligatorio", + "auto-generate-checksum" : "Generar checksum automáticamente", + "checksum" : "Checksum", + "checksum-hint" : "Si el checksum está vacío, se generará automáticamente", + "checksum-algorithm" : "Algoritmo de checksum", + "checksum-copied-message" : "El checksum del paquete se ha copiado al portapapeles", + "change-firmware" : "El cambio de firmware puede provocar la actualización de { count, plural, =1 {1 dispositivo} other {# dispositivos} }.", + "change-software" : "El cambio de software puede provocar la actualización de { count, plural, =1 {1 dispositivo} other {# dispositivos} }.", + "chose-compatible-device-profile" : "El paquete cargado solo estará disponible para dispositivos con el perfil seleccionado.", + "chose-firmware-distributed-device" : "Seleccione el firmware que se distribuirá a los dispositivos", + "chose-software-distributed-device" : "Seleccione el software que se distribuirá a los dispositivos", + "content-type" : "Tipo de contenido", + "copy-checksum" : "Copiar checksum", + "copy-direct-url" : "Copiar URL directa", + "copyId" : "Copiar ID del paquete", + "copied" : "¡Copiado!", + "delete" : "Eliminar paquete", + "delete-ota-update-text" : "Ten cuidado, después de la confirmación la actualización OTA será irrecuperable.", + "delete-ota-update-title" : "¿Estás seguro de que deseas eliminar la actualización OTA '{{title}}'?", + "delete-ota-updates-text" : "Ten cuidado, después de la confirmación todas las actualizaciones OTA seleccionadas serán eliminadas.", + "delete-ota-updates-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 actualización OTA} other {# actualizaciones OTA} }?", + "description" : "Descripción", + "direct-url" : "URL directa", + "direct-url-copied-message" : "La URL directa del paquete se ha copiado al portapapeles", + "direct-url-required" : "La URL directa es obligatoria", + "download" : "Descargar paquete", + "drop-file" : "Suelta un archivo de paquete o haz clic para seleccionar un archivo para subir.", + "drop-package-file-or" : "Arrastra y suelta un archivo de paquete o", + "file-name" : "Nombre del archivo", + "file-size" : "Tamaño del archivo", + "file-size-bytes" : "Tamaño del archivo en bytes", + "idCopiedMessage" : "El ID del paquete se ha copiado al portapapeles", + "no-firmware-matching" : "No se encontraron paquetes OTA de firmware compatibles que coincidan con '{{entity}}'.", + "no-firmware-text" : "No se han aprovisionado paquetes OTA de firmware compatibles.", + "no-packages-text" : "No se encontraron paquetes", + "no-software-matching" : "No se encontraron paquetes OTA de software compatibles que coincidan con '{{entity}}'.", + "no-software-text" : "No se han aprovisionado paquetes OTA de software compatibles.", + "ota-update" : "Actualización OTA", + "ota-update-details" : "Detalles de la actualización OTA", + "ota-updates" : "Actualizaciones OTA", + "package-file" : "Archivo del paquete", + "package-type" : "Tipo de paquete", + "packages-repository" : "Repositorio de paquetes", + "search" : "Buscar paquetes", + "selected-package" : "{ count, plural, =1 {1 paquete} other {# paquetes} } seleccionado", + "title" : "Título", + "title-required" : "El título es obligatorio.", + "title-max-length" : "El título debe tener menos de 256 caracteres", + "types" : { + "firmware" : "Firmware", + "software" : "Software" }, - "gateway": { - "gateway-exists": "Ya existe un dispositivo con el mismo nombre.", - "gateway-name": "Nombre de Gateway", - "gateway-name-required": "Se requiere un nombre de gateway.", - "gateway-saved": "Configuración de gateway grabada satisfactoriamente.", - "gateway": "Gateway", - "launch-gateway": "Iniciar gateway", - "launch-command": "Comando de lanzamiento", - "create-new-gateway": "Crear un gateway nuevo", - "create-new-gateway-text": "¿Está seguro de que desea crear un nuevo gateway con el nombre: '{{gatewayName}}'?", - "no-gateway-found": "No se ha encontrado ningún gateway.", - "no-gateway-matching": "No se encontró '{{item}}'." + "upload-binary-file" : "Subir archivo binario", + "use-external-url" : "Usar URL externa", + "version" : "Versión", + "version-required" : "La versión es obligatoria.", + "version-tag" : "Etiqueta de versión", + "version-tag-hint" : "La etiqueta personalizada debe coincidir con la versión del paquete informada por su dispositivo.", + "version-max-length" : "La versión debe tener menos de 256 caracteres", + "warning-after-save-no-edit" : "Una vez que el paquete esté subido, no podrás modificar el título, versión, perfil de dispositivo y tipo de paquete." + }, + "position" : { + "top" : "Superior", + "bottom" : "Inferior", + "left" : "Izquierda", + "right" : "Derecha" + }, + "profile" : { + "profile" : "Perfil", + "last-login-time" : "Último acceso", + "change-password" : "Cambiar contraseña", + "current-password" : "Contraseña actual", + "copy-jwt-token" : "Copiar token JWT", + "jwt-token" : "Token JWT", + "token-valid-till" : "El token es válido hasta", + "tokenCopiedSuccessMessage" : "El token JWT se ha copiado al portapapeles", + "tokenCopiedWarnMessage" : "¡El token JWT ha expirado! Por favor, actualiza la página." + }, + "profiles" : { + "profiles" : "Perfiles" + }, + "security" : { + "security" : "Seguridad", + "general-settings" : "Configuraciones generales de seguridad", + "access-token" : "Token de acceso", + "access-token-required" : "El token de acceso es obligatorio", + "clientId" : "ID de Cliente", + "clientId-required" : "El ID de Cliente es obligatorio", + "username" : "Nombre de usuario", + "username-required" : "El nombre de usuario es obligatorio", + "ca-cert" : "Certificado CA", + "2fa" : { + "2fa" : "Autenticación de dos factores", + "2fa-description" : "La autenticación de dos factores protege tu cuenta contra accesos no autorizados. Solo tienes que ingresar un código de seguridad al iniciar sesión.", + "authenticate-with" : "Puedes autenticarte con:", + "disable-2fa-provider-text" : "Desactivar {{name}} hará que tu cuenta sea menos segura", + "disable-2fa-provider-title" : "¿Estás seguro de que deseas desactivar {{name}}?", + "get-new-code" : "Obtener nuevo código", + "main-2fa-method" : "Usar como método principal de autenticación de dos factores", + "dialog" : { + "activation-step-description-email" : "La próxima vez que inicies sesión, se te pedirá ingresar el código de seguridad que será enviado a tu dirección de correo electrónico.", + "activation-step-description-sms" : "La próxima vez que inicies sesión, se te pedirá ingresar el código de seguridad que será enviado a tu número de teléfono.", + "activation-step-description-totp" : "La próxima vez que inicies sesión, deberás proporcionar un código de autenticación de dos factores.", + "activation-step-label" : "Activación", + "backup-code-description" : "Imprime los códigos para tenerlos a mano cuando necesites usarlos para iniciar sesión en tu cuenta. Puedes usar cada código de respaldo una vez.", + "backup-code-warn" : "Una vez que salgas de esta página, estos códigos no podrán mostrarse de nuevo. Guárdalos de forma segura usando las opciones siguientes.", + "download-txt" : "Descargar (txt)", + "email-step-description" : "Ingresa un correo electrónico para usar como autenticador.", + "email-step-label" : "Correo electrónico", + "enable-email-title" : "Habilitar autenticador por correo", + "enable-sms-title" : "Habilitar autenticador por SMS", + "enable-totp-title" : "Habilitar aplicación de autenticación", + "enter-verification-code" : "Introduce aquí el código de 6 dígitos", + "get-backup-code-title" : "Obtener código de respaldo", + "next" : "Siguiente", + "scan-qr-code" : "Escanea este código QR con tu aplicación de verificación", + "send-code" : "Enviar código", + "sms-step-description" : "Ingresa un número de teléfono para usar como autenticador.", + "sms-step-label" : "Número de teléfono", + "success" : "¡Éxito!", + "totp-step-description-install" : "Puedes instalar aplicaciones como Google Authenticator, Authy o Duo.", + "totp-step-description-open" : "Abre la aplicación de autenticación en tu teléfono móvil.", + "totp-step-label" : "Obtener aplicación", + "verification-code" : "Código de 6 dígitos", + "verification-code-invalid" : "Formato de código de verificación inválido", + "verification-code-incorrect" : "Código de verificación incorrecto", + "verification-code-many-request" : "Demasiadas solicitudes, revisa el código de verificación", + "verification-step-description" : "Introduce un código de 6 dígitos que acabamos de enviar a {{address}}", + "verification-step-label" : "Verificación" + }, + "provider" : { + "email" : "Correo electrónico", + "email-description" : "Usa un código de seguridad enviado a tu dirección de correo electrónico para autenticarte.", + "email-hint" : "Los códigos de autenticación se envían por correo a {{ info }}", + "sms" : "SMS", + "sms-description" : "Usa tu teléfono para autenticarte. Te enviaremos un código de seguridad por mensaje SMS al iniciar sesión.", + "sms-hint" : "Los códigos de autenticación se envían por mensaje de texto a {{ info }}", + "totp" : "Aplicación de autenticación", + "totp-description" : "Usa aplicaciones como Google Authenticator, Authy o Duo en tu teléfono para autenticarte. Generará un código de seguridad para iniciar sesión.", + "totp-hint" : "La aplicación de autenticación está configurada para tu cuenta", + "backup_code" : "Código de respaldo", + "backup-code-description" : "Estos códigos de un solo uso e imprimibles te permiten iniciar sesión cuando no tienes acceso a tu teléfono, como cuando estás viajando.", + "backup-code-hint" : "{{ info }} códigos de un solo uso están activos actualmente" + } }, - "grid": { - "delete-item-title": "¿Quieres eliminar este item?", - "delete-item-text": "Atención, tras la confirmación el item será eliminado y la información relacionada será irrecuperable.", - "delete-items-title": "¿Quieres eliminar { count, plural, =1 {1 item} other {# items} }?", - "delete-items-action-title": "Eliminar { count, plural, =1 {1 item} other {# items} }", - "delete-items-text": "Atención, tras la confirmación los items seleccionados serán eliminados y la información relacionada será irrecuperable.", - "add-item-text": "Agregar nuevo item", - "no-items-text": "Ningún item encontrado", - "item-details": "Detalles del item", - "delete-item": "Borrar Item", - "delete-items": "Borrar Items", - "scroll-to-top": "Ir hacia arriba" + "password-requirement" : { + "at-least" : "Al menos:", + "character" : "{ count, plural, =1 {1 carácter} other {# caracteres} }", + "digit" : "{ count, plural, =1 {1 dígito} other {# dígitos} }", + "incorrect-password-try-again" : "Contraseña incorrecta. Inténtalo de nuevo", + "lowercase-letter" : "{ count, plural, =1 {1 letra minúscula} other {# letras minúsculas} }", + "new-passwords-not-match" : "La nueva contraseña no coincide", + "password-should-not-contain-spaces" : "Tu contraseña no debe contener espacios", + "password-not-meet-requirements" : "La contraseña no cumple con los requisitos", + "password-requirements" : "Requisitos de la contraseña", + "password-should-difference" : "La nueva contraseña debe ser diferente de la actual", + "special-character" : "{ count, plural, =1 {1 carácter especial} other {# caracteres especiales} }", + "uppercase-letter" : "{ count, plural, =1 {1 letra mayúscula} other {# letras mayúsculas} }", + "at-most" : "Como máximo:" + } + }, + "relation" : { + "relations" : "Relaciones", + "direction" : "Dirección", + "clear-relation-type" : "Borrar tipo de relación", + "search-direction" : { + "FROM" : "Desde", + "TO" : "Hacia" }, - "help": { - "goto-help-page": "Ir a la página de ayuda", - "show-help": "Mostrar ayuda" + "direction-type" : { + "FROM" : "desde", + "TO" : "hacia" }, - "home": { - "home": "Principal", - "profile": "Perfil", - "logout": "Salir", - "menu": "Menu", - "avatar": "Avatar", - "open-user-menu": "Abrir menú de usuario" + "from-relations" : "Relaciones salientes", + "to-relations" : "Relaciones entrantes", + "selected-relations" : "{ count, plural, =1 {1 relación} other {# relaciones} } seleccionadas", + "type" : "Tipo", + "to-entity-type" : "Tipo de entidad de destino", + "to-entity-name" : "Nombre de entidad de destino", + "from-entity-type" : "Tipo de entidad de origen", + "from-entity-name" : "Nombre de entidad de origen", + "to-entity" : "Entidad de destino", + "from-entity" : "Entidad de origen", + "delete" : "Eliminar relación", + "relation-type" : "Tipo de relación", + "relation-type-required" : "Se requiere tipo de relación.", + "relation-type-max-length" : "El tipo de relación debe tener menos de 256 caracteres", + "any-relation-type" : "Cualquier tipo", + "add" : "Agregar relación", + "edit" : "Editar relación", + "delete-to-relation-title" : "¿Estás seguro de que deseas eliminar la relación con la entidad '{{entityName}}'?", + "delete-to-relation-text" : "Ten cuidado, tras la confirmación la entidad '{{entityName}}' quedará desvinculada de la entidad actual.", + "delete-to-relations-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 relación} other {# relaciones} }?", + "delete-to-relations-text" : "Ten cuidado, tras la confirmación todas las relaciones seleccionadas serán eliminadas y las entidades correspondientes quedarán desvinculadas de la entidad actual.", + "delete-from-relation-title" : "¿Estás seguro de que deseas eliminar la relación desde la entidad '{{entityName}}'?", + "delete-from-relation-text" : "Ten cuidado, tras la confirmación la entidad actual quedará desvinculada de la entidad '{{entityName}}'.", + "delete-from-relations-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 relación} other {# relaciones} }?", + "delete-from-relations-text" : "Ten cuidado, tras la confirmación todas las relaciones seleccionadas serán eliminadas y la entidad actual quedará desvinculada de las entidades correspondientes.", + "remove-relation-filter" : "Eliminar filtro de relación", + "remove-filter" : "Eliminar filtro", + "add-relation-filter" : "Agregar filtro de relación", + "any-relation" : "Cualquier relación", + "relation-filters" : "Filtros de relación", + "additional-info" : "Información adicional (JSON)", + "invalid-additional-info" : "No se pudo analizar el JSON de información adicional.", + "no-relations-text" : "No se encontraron relaciones", + "not" : "No" + }, + "resource" : { + "add" : "Agregar recurso", + "all-types" : "Todos", + "copyId" : "Copiar ID del recurso", + "delete" : "Eliminar recurso", + "delete-resource-text" : "Ten cuidado, tras la confirmación el recurso será irrecuperable.", + "delete-resource-title" : "¿Estás seguro de que deseas eliminar el recurso '{{resourceTitle}}'?", + "delete-resources-action-title" : "Eliminar { count, plural, =1 {1 recurso} other {# recursos} }", + "delete-resources-text" : "Ten en cuenta que los recursos seleccionados, aunque estén siendo utilizados en perfiles de dispositivos, serán eliminados.", + "delete-resources-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 recurso} other {# recursos} }?", + "download" : "Descargar recurso", + "drop-file" : "Suelta un archivo de recurso o haz clic para seleccionar un archivo para subir.", + "drop-resource-file-or" : "Arrastra y suelta un archivo de recurso o", + "empty" : "El recurso está vacío", + "file-name" : "Nombre del archivo", + "idCopiedMessage" : "ID del recurso copiado al portapapeles", + "no-resource-matching" : "No se encontraron recursos que coincidan con '{{widgetsBundle}}'.", + "no-resource-text" : "No se encontraron recursos", + "open-widgets-bundle" : "Abrir paquete de widgets", + "resource" : "Recurso", + "resource-file" : "Archivo de recurso", + "resource-files" : "Archivos de recurso", + "resource-library-details" : "Detalles del recurso", + "resource-type" : "Tipo de recurso", + "resources-library" : "Biblioteca de recursos", + "search" : "Buscar recursos", + "selected-resources" : "{ count, plural, =1 {1 recurso} other {# recursos} } seleccionados", + "system" : "Sistema", + "title" : "Título", + "title-required" : "El título es obligatorio.", + "title-max-length" : "El título debe tener menos de 256 caracteres", + "type" : { + "jks" : "JKS", + "js-module" : "Módulo JS", + "lwm2m-model" : "Modelo LWM2M", + "pkcs-12" : "PKCS #12" }, - "file-input": { - "browse-file": "Navegar fichero", - "browse-files": "Navegar ficheros" + "resource-sub-type" : "Subtipo", + "sub-type" : { + "image" : "imagen", + "scada-symbol" : "Símbolo Scada", + "extension" : "Extensión", + "module" : "Módulo" + } + }, + "javascript" : { + "add" : "Agregar recurso JavaScript", + "delete" : "Eliminar recurso JavaScript", + "delete-javascript-resource-text" : "Ten cuidado, después de la confirmación el recurso JavaScript será irrecuperable.", + "delete-javascript-resource-title" : "¿Estás seguro de que deseas eliminar el recurso JavaScript '{{resourceTitle}}'?", + "delete-javascript-resources-action-title" : "Eliminar JavaScript { count, plural, =1 {1 recurso} other {# recursos} }", + "delete-javascript-resources-text" : "Ten en cuenta que los recursos JavaScript seleccionados, incluso si se usan en funciones JavaScript, serán eliminados.", + "delete-javascript-resources-title" : "¿Estás seguro de que deseas eliminar JavaScript { count, plural, =1 {1 recurso} other {# recursos} }?", + "delete-javascript-resource-in-use-text" : "Si aún deseas eliminar el recurso JavaScript, haz clic en el botón Eliminar de todos modos.", + "download" : "Descargar recurso JavaScript", + "upload-from-file" : "Subir JavaScript desde archivo", + "resource-file" : "Archivo de recurso JavaScript", + "drop-file" : "Suelta un archivo JavaScript o haz clic para seleccionar uno para subir.", + "drop-resource-file-or" : "Arrastra y suelta un archivo JavaScript o", + "javascript-library" : "Biblioteca JavaScript", + "javascript-type" : "Tipo de JavaScript", + "javascript-resource-details" : "Detalles del recurso JavaScript", + "javascript-resource-is-in-use" : "El recurso JavaScript está siendo usado por otras entidades", + "javascript-resources-are-in-use" : "Los recursos JavaScript están siendo usados por otras entidades", + "javascript-resource-is-in-use-text" : "El recurso JavaScript '{{title}}' no fue eliminado porque está siendo utilizado por las siguientes entidades:", + "javascript-resources-are-in-use-text" : "No todos los recursos JavaScript han sido eliminados porque están siendo utilizados por otras entidades.
Puedes ver las entidades referenciadas haciendo clic en el botón Referencias en la fila correspondiente del recurso.
Si aún deseas eliminar estos recursos JavaScript, selecciónalos en la tabla a continuación y haz clic en el botón Eliminar seleccionados.", + "search" : "Buscar recursos JavaScript", + "selected-javascript-resources" : "{ count, plural, =1 {1 recurso JavaScript} other {# recursos JavaScript} } seleccionados", + "no-javascript-resource-text" : "No se encontraron recursos JavaScript", + "all-types" : "Todos", + "module-script" : "Script de módulo" + }, + "rpc" : { + "error" : { + "target-device-is-not-set" : "¡El dispositivo de destino no está definido!", + "invalid-target-entity" : "Los comandos RPC no son compatibles con la entidad {{entityType}}.", + "failed-to-resolve-target-device" : "¡No se pudo resolver el dispositivo de destino!", + "request-timeout" : "Tiempo de espera de la solicitud agotado", + "rpc-http-error" : "Error: {{status}} - {{statusText}}" + } + }, + "rulechain" : { + "rulechain" : "Cadena de reglas", + "rulechain-events" : "Eventos de cadena de reglas", + "rulechains" : "Cadenas de reglas", + "root" : "Raíz", + "delete" : "Eliminar cadena de reglas", + "name" : "Nombre", + "name-required" : "El nombre es obligatorio.", + "name-max-length" : "El nombre debe tener menos de 256 caracteres", + "description" : "Descripción", + "add" : "Agregar cadena de reglas", + "set-root" : "Hacer raíz esta cadena de reglas", + "set-root-rulechain-title" : "¿Estás seguro de que deseas hacer la cadena de reglas '{{ruleChainName}}' raíz?", + "set-root-rulechain-text" : "Después de la confirmación, la cadena de reglas se convertirá en la raíz y gestionará todos los mensajes de transporte entrantes.", + "delete-rulechain-title" : "¿Estás seguro de que deseas eliminar la cadena de reglas '{{ruleChainName}}'?", + "delete-rulechain-text" : "Ten cuidado, después de la confirmación la cadena de reglas y todos los datos relacionados serán irrecuperables.", + "delete-rulechains-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }?", + "delete-rulechains-action-title" : "Eliminar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }", + "delete-rulechains-text" : "Ten cuidado, después de la confirmación todas las cadenas de reglas seleccionadas serán eliminadas y todos los datos relacionados serán irrecuperables.", + "add-rulechain-text" : "Agregar nueva cadena de reglas", + "no-rulechains-text" : "No se encontraron cadenas de reglas", + "rulechain-details" : "Detalles de la cadena de reglas", + "details" : "Detalles", + "events" : "Eventos", + "system" : "Sistema", + "import" : "Importar cadena de reglas", + "export" : "Exportar cadena de reglas", + "export-failed-error" : "No se pudo exportar la cadena de reglas: {{error}}", + "create-new-rulechain" : "Crear nueva cadena de reglas", + "rulechain-file" : "Archivo de cadena de reglas", + "invalid-rulechain-file-error" : "No se pudo importar la cadena de reglas: estructura de datos inválida.", + "copyId" : "Copiar ID de cadena de reglas", + "idCopiedMessage" : "El ID de la cadena de reglas ha sido copiado al portapapeles", + "select-rulechain" : "Seleccionar cadena de reglas", + "no-rulechains-matching" : "No se encontraron cadenas de reglas que coincidan con '{{entity}}'.", + "rulechain-required" : "La cadena de reglas es obligatoria", + "management" : "Gestión de reglas", + "debug-mode" : "Modo depuración", + "search" : "Buscar cadenas de reglas", + "selected-rulechains" : "{ count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} } seleccionadas", + "open-rulechain" : "Abrir cadena de reglas", + "edge-template-root" : "Raíz de plantilla", + "assign-to-edge" : "Asignar a borde", + "edge-rulechain" : "Cadena de reglas de borde", + "unassign-rulechain-from-edge-text" : "Después de la confirmación, la cadena de reglas será desasignada y no estará accesible desde el borde.", + "unassign-rulechains-from-edge-title" : "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }?", + "unassign-rulechains-from-edge-text" : "Después de la confirmación, todas las cadenas de reglas seleccionadas serán desasignadas y no estarán accesibles desde el borde.", + "assign-rulechain-to-edge-title" : "Asignar cadena(s) de reglas al borde", + "assign-rulechain-to-edge-text" : "Por favor, selecciona las cadenas de reglas para asignar al borde", + "set-edge-template-root-rulechain" : "Establecer como raíz de plantilla de borde", + "set-edge-template-root-rulechain-title" : "¿Estás seguro de que deseas establecer la cadena de reglas '{{ruleChainName}}' como raíz de plantilla de borde?", + "set-edge-template-root-rulechain-text" : "Después de la confirmación, la cadena de reglas se convertirá en raíz de plantilla de borde y será la cadena raíz para los bordes recién creados.", + "invalid-rulechain-type-error" : "No se pudo importar la cadena de reglas: tipo de cadena inválido. El tipo esperado es {{expectedRuleChainType}}.", + "set-auto-assign-to-edge" : "Asignar cadena de reglas automáticamente al crear borde(s)", + "set-auto-assign-to-edge-title" : "¿Estás seguro de que deseas asignar la cadena de reglas de borde '{{ruleChainName}}' automáticamente al crear borde(s)?", + "set-auto-assign-to-edge-text" : "Después de la confirmación, la cadena de reglas de borde se asignará automáticamente al crear borde(s).", + "unset-auto-assign-to-edge" : "No asignar cadena de reglas a borde(s) al crearlos", + "unset-auto-assign-to-edge-title" : "¿Estás seguro de que no deseas asignar la cadena de reglas de borde '{{ruleChainName}}' al crear borde(s)?", + "unset-auto-assign-to-edge-text" : "Después de la confirmación, la cadena de reglas de borde ya no se asignará automáticamente al crear borde(s).", + "unassign-rulechain-title" : "¿Estás seguro de que deseas desasignar la cadena de reglas '{{ruleChainName}}'?", + "unassign-rulechains" : "Desasignar cadenas de reglas" + }, + "rulenode" : { + "rule-node-events" : "Eventos del nodo de reglas", + "details" : "Detalles", + "events" : "Eventos", + "search" : "Buscar nodos", + "open-node-library" : "Abrir biblioteca de nodos", + "close-node-library" : "Cerrar biblioteca de nodos", + "add" : "Agregar nodo de reglas", + "name" : "Nombre", + "name-required" : "El nombre es obligatorio.", + "name-max-length" : "El nombre debe tener menos de 256 caracteres", + "type" : "Tipo", + "rule-node-description" : "Descripción del nodo de reglas", + "delete" : "Eliminar nodo de reglas", + "select-all-objects" : "Seleccionar todos los nodos y conexiones", + "deselect-all-objects" : "Deseleccionar todos los nodos y conexiones", + "delete-selected-objects" : "Eliminar nodos y conexiones seleccionados", + "delete-selected" : "Eliminar seleccionados", + "create-nested-rulechain" : "Crear cadena de reglas anidada", + "select-all" : "Seleccionar todo", + "copy-selected" : "Copiar seleccionados", + "deselect-all" : "Deseleccionar todo", + "rulenode-details" : "Detalles del nodo de reglas", + "debug-mode" : "Modo depuración", + "singleton" : "Instancia única", + "configuration" : "Configuración", + "link" : "Enlace", + "link-details" : "Detalles del enlace del nodo de reglas", + "add-link" : "Agregar enlace", + "link-label" : "Etiqueta del enlace", + "link-label-required" : "La etiqueta del enlace es obligatoria.", + "custom-link-label" : "Etiqueta personalizada del enlace", + "custom-link-label-required" : "La etiqueta personalizada del enlace es obligatoria.", + "link-labels" : "Etiquetas del enlace", + "link-labels-required" : "Las etiquetas del enlace son obligatorias.", + "no-link-labels-found" : "No se encontraron etiquetas del enlace", + "no-link-label-matching" : "'{{label}}' no encontrado.", + "create-new-link-label" : "¡Crear una nueva!", + "type-filter" : "Filtro", + "type-filter-details" : "Filtra mensajes entrantes con condiciones configuradas", + "type-enrichment" : "Enriquecimiento", + "type-enrichment-details" : "Agrega información adicional en los metadatos del mensaje", + "type-transformation" : "Transformación", + "type-transformation-details" : "Cambia el contenido del mensaje y metadatos", + "type-action" : "Acción", + "type-action-details" : "Realiza una acción especial", + "type-external" : "Externo", + "type-external-details" : "Interactúa con un sistema externo", + "type-rule-chain" : "Cadena de reglas", + "type-rule-chain-details" : "Reenvía mensajes entrantes a la cadena de reglas especificada", + "type-flow" : "Flujo", + "type-flow-details" : "Organiza el flujo de mensajes", + "type-input" : "Entrada", + "type-input-details" : "Entrada lógica de la cadena de reglas, reenvía mensajes entrantes al siguiente nodo relacionado", + "type-unknown" : "Desconocido", + "type-unknown-details" : "Nodo de reglas no resuelto", + "directive-is-not-loaded" : "La directiva de configuración definida '{{directiveName}}' no está disponible.", + "ui-resources-load-error" : "Error al cargar los recursos de interfaz de configuración.", + "invalid-target-rulechain" : "¡No se pudo resolver la cadena de reglas de destino!", + "test-script-function" : "Probar función de script", + "script-lang-java-script" : "JavaScript", + "script-lang-tbel" : "TBEL", + "message" : "Mensaje", + "message-type" : "Tipo de mensaje", + "select-message-type" : "Seleccionar tipo de mensaje", + "message-type-required" : "El tipo de mensaje es obligatorio", + "metadata" : "Metadatos", + "metadata-required" : "Las entradas de metadatos no pueden estar vacías.", + "output" : "Salida", + "test" : "Probar", + "help" : "Ayuda", + "reset-debug-settings" : "Restablecer configuración de depuración en todos los nodos", + "test-with-this-message" : "{{test}} con este mensaje", + "queue-hint" : "Selecciona una cola para reenviar el mensaje a otra cola. Por defecto se usa la cola 'Main'.", + "queue-singleton-hint" : "Selecciona una cola para reenvío de mensajes en entornos multi-instancia. Por defecto se usa la cola 'Main'." + }, + "rule-node-config" : { + "id" : "Id", + "additional-info" : "Información adicional", + "advanced-settings" : "Configuraciones avanzadas", + "create-entity-if-not-exists" : "Crear nueva entidad si no existe", + "create-entity-if-not-exists-hint" : "Si está habilitado, se creará una nueva entidad con los parámetros especificados a menos que ya exista. Las entidades existentes se usarán tal cual para la relación.", + "select-device-connectivity-event" : "Seleccionar evento de conectividad del dispositivo", + "entity-name-pattern" : "Patrón de nombre", + "device-name-pattern" : "Nombre del dispositivo", + "asset-name-pattern" : "Nombre del activo", + "entity-view-name-pattern" : "Nombre de vista de entidad", + "customer-title-pattern" : "Título del cliente", + "dashboard-name-pattern" : "Título del tablero", + "user-name-pattern" : "Correo del usuario", + "edge-name-pattern" : "Nombre del Edge", + "entity-name-pattern-required" : "El patrón de nombre es obligatorio", + "entity-name-pattern-hint" : "El campo patrón de nombre admite plantillas. Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraerlo de los metadatos.", + "copy-message-type" : "Copiar tipo de mensaje", + "entity-type-pattern" : "Patrón de tipo", + "entity-type-pattern-required" : "El patrón de tipo es obligatorio", + "message-type-value" : "Valor del tipo de mensaje", + "message-type-value-required" : "El valor del tipo de mensaje es obligatorio", + "message-type-value-max-length" : "El valor del tipo de mensaje debe tener menos de 256 caracteres", + "output-message-type" : "Tipo de mensaje de salida", + "entity-cache-expiration" : "Tiempo de expiración de la caché de entidades (seg)", + "entity-cache-expiration-hint" : "Especifica el intervalo de tiempo máximo permitido para almacenar registros de entidades encontrados. El valor 0 significa que los registros nunca expirarán.", + "entity-cache-expiration-required" : "El tiempo de expiración de la caché de entidades es obligatorio.", + "entity-cache-expiration-range" : "El tiempo de expiración debe ser mayor o igual a 0.", + "customer-name-pattern" : "Título del cliente", + "customer-name-pattern-required" : "El título del cliente es obligatorio", + "customer-name-pattern-hint" : "Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraerlo de los metadatos.", + "create-customer-if-not-exists" : "Crear nuevo cliente si no existe", + "unassign-from-customer" : "Desasignar de cliente específico si el originador es un tablero", + "unassign-from-customer-tooltip" : "Solo los tableros pueden asignarse a múltiples clientes al mismo tiempo.\nSi el originador del mensaje es un tablero, debes especificar explícitamente el título del cliente del que se desea desasignar.", + "customer-cache-expiration" : "Tiempo de expiración de la caché de clientes (seg)", + "customer-cache-expiration-hint" : "Especifica el intervalo de tiempo máximo permitido para almacenar registros de clientes encontrados. El valor 0 significa que los registros nunca expirarán.", + "customer-cache-expiration-required" : "El tiempo de expiración de la caché de clientes es obligatorio.", + "customer-cache-expiration-range" : "El tiempo de expiración debe ser mayor o igual a 0.", + "interval-start" : "Inicio del intervalo", + "interval-end" : "Fin del intervalo", + "time-unit" : "Unidad de tiempo", + "fetch-mode" : "Modo de obtención", + "order-by-timestamp" : "Ordenar por marca de tiempo", + "limit" : "Límite", + "limit-hint" : "El valor mínimo es 2, máximo - 1000. Si deseas obtener una sola entrada, selecciona el modo 'Primero' o 'Último'.", + "limit-required" : "El límite es obligatorio.", + "limit-range" : "El límite debe estar en el rango de 2 a 1000.", + "time-unit-milliseconds" : "Milisegundos", + "time-unit-seconds" : "Segundos", + "time-unit-minutes" : "Minutos", + "time-unit-hours" : "Horas", + "time-unit-days" : "Días", + "time-value-range" : "Rango permitido de 1 a 2147483647.", + "start-interval-value-required" : "El inicio del intervalo es obligatorio.", + "end-interval-value-required" : "El fin del intervalo es obligatorio.", + "filter" : "Filtro", + "switch" : "Conmutador", + "math-templatization-tooltip" : "Este campo admite plantillas. Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraerlo de los metadatos.", + "add-message-type" : "Agregar tipo de mensaje", + "select-message-types-required" : "Debe seleccionarse al menos un tipo de mensaje.", + "select-message-types" : "Seleccionar tipos de mensaje", + "no-message-types-found" : "No se encontraron tipos de mensaje", + "no-message-type-matching" : "'{{messageType}}' no encontrado.", + "create-new-message-type" : "Crear uno nuevo.", + "message-types-required" : "Los tipos de mensaje son obligatorios.", + "client-attributes" : "Atributos del cliente", + "shared-attributes" : "Atributos compartidos", + "server-attributes" : "Atributos del servidor", + "attributes-keys" : "Claves de atributos", + "attributes-keys-required" : "Las claves de atributos son obligatorias", + "attributes-scope" : "Ámbito de los atributos", + "attributes-scope-value" : "Valor del ámbito de los atributos", + "attributes-scope-value-copy" : "Copiar valor del ámbito de los atributos", + "attributes-scope-hint" : "Usa la clave de metadatos 'scope' para definir dinámicamente el ámbito por mensaje. Si se proporciona, sobrescribe el ámbito definido en la configuración.", + "notify-device" : "Forzar notificación al dispositivo", + "send-attributes-updated-notification" : "Enviar notificación de atributos actualizados", + "send-attributes-updated-notification-hint" : "Enviar notificación sobre los atributos actualizados como mensaje separado a la cola del motor de reglas.", + "send-attributes-deleted-notification" : "Enviar notificación de atributos eliminados", + "send-attributes-deleted-notification-hint" : "Enviar notificación sobre los atributos eliminados como mensaje separado a la cola del motor de reglas.", + "update-attributes-only-on-value-change" : "Guardar atributos solo si el valor cambia", + "update-attributes-only-on-value-change-hint" : "Actualiza los atributos en cada mensaje entrante sin importar si su valor ha cambiado. Aumenta el uso de la API y reduce el rendimiento.", + "update-attributes-only-on-value-change-hint-enabled" : "Actualiza los atributos solo si su valor ha cambiado. Si el valor no cambia, no se actualiza la marca de tiempo ni se envía notificación de cambio.", + "fetch-credentials-to-metadata" : "Obtener credenciales en metadatos", + "notify-device-on-update-hint" : "Si está habilitado, forzar notificación al dispositivo sobre la actualización de atributos compartidos. Si está deshabilitado, el comportamiento se controla con el parámetro 'notifyDevice' desde los metadatos del mensaje. Para desactivar la notificación, los metadatos deben contener 'notifyDevice' con valor 'false'. En cualquier otro caso, se enviará la notificación al dispositivo.", + "notify-device-on-delete-hint" : "Si está habilitado, forzar notificación al dispositivo sobre la eliminación de atributos compartidos. Si está deshabilitado, el comportamiento se controla con el parámetro 'notifyDevice' desde los metadatos del mensaje. Para activar la notificación, los metadatos deben contener 'notifyDevice' con valor 'true'. En cualquier otro caso, no se enviará notificación al dispositivo.", + "latest-timeseries" : "Claves de datos de series temporales más recientes", + "timeseries-keys" : "Claves de series temporales", + "timeseries-keys-required" : "Debe seleccionarse al menos una clave de series temporales.", + "add-timeseries-key" : "Agregar clave de series temporales", + "add-message-field" : "Agregar campo de mensaje", + "relation-search-parameters" : "Parámetros de búsqueda de relaciones", + "relation-parameters" : "Parámetros de relación", + "add-metadata-field" : "Agregar campo de metadatos", + "data-keys" : "Nombres de campos del mensaje", + "copy-from" : "Copiar desde", + "data-to-metadata" : "Datos a metadatos", + "metadata-to-data" : "Metadatos a datos", + "use-regular-expression-hint" : "Usa expresión regular para copiar claves por patrón.\n\nConsejos y trucos:\nPresiona 'Enter' para completar el nombre del campo.\nPresiona 'Retroceso' para eliminar el nombre del campo. Se admiten múltiples nombres de campos.", + "interval" : "Intervalo", + "interval-required" : "El intervalo es obligatorio", + "interval-hint" : "Intervalo de desduplicación en segundos.", + "interval-min-error" : "El valor mínimo permitido es 1", + "max-pending-msgs" : "Máximo de mensajes pendientes", + "max-pending-msgs-hint" : "Número máximo de mensajes que se almacenan en memoria para cada id único de desduplicación.", + "max-pending-msgs-required" : "El máximo de mensajes pendientes es obligatorio", + "max-pending-msgs-max-error" : "El valor máximo permitido es 1000", + "max-pending-msgs-min-error" : "El valor mínimo permitido es 1", + "max-retries" : "Máximo de reintentos", + "max-retries-required" : "El número máximo de reintentos es obligatorio", + "max-retries-hint" : "Número máximo de reintentos para insertar los mensajes desduplicados en la cola. Se usa un retraso de 10 segundos entre reintentos.", + "max-retries-max-error" : "El valor máximo permitido es 100", + "max-retries-min-error" : "El valor mínimo permitido es 0", + "strategy" : "Estrategia", + "strategy-required" : "La estrategia es obligatoria", + "strategy-all-hint" : "Devuelve todos los mensajes que llegaron durante el periodo de desduplicación como un único mensaje JSON en forma de array. Cada elemento representa un objeto con propiedades internas msg y metadata.", + "strategy-first-hint" : "Devuelve el primer mensaje que llegó durante el periodo de desduplicación.", + "strategy-last-hint" : "Devuelve el último mensaje que llegó durante el periodo de desduplicación.", + "first" : "Primero", + "last" : "Último", + "all" : "Todos", + "output-msg-type-hint" : "El tipo de mensaje del resultado de la desduplicación.", + "queue-name-hint" : "El nombre de la cola donde se publicará el resultado de la desduplicación.", + "keys" : "Claves", + "keys-required" : "Las claves son obligatorias", + "rename-keys-in" : "Renombrar claves en", + "data" : "Datos", + "message" : "Mensaje", + "metadata" : "Metadatos", + "current-key-name" : "Nombre actual de la clave", + "key-name-required" : "El nombre de la clave es obligatorio", + "new-key-name" : "Nuevo nombre de la clave", + "new-key-name-required" : "El nuevo nombre de la clave es obligatorio", + "metadata-keys" : "Nombres de campos de metadatos", + "json-path-expression" : "Expresión de ruta JSON", + "json-path-expression-required" : "La expresión de ruta JSON es obligatoria", + "json-path-expression-hint" : "JSONPath especifica una ruta a un elemento o conjunto de elementos en una estructura JSON. '$' representa el objeto raíz o el array.", + "relations-query" : "Consulta de relaciones", + "device-relations-query" : "Consulta de relaciones del dispositivo", + "max-relation-level" : "Nivel máximo de relación", + "max-relation-level-error" : "El valor debe ser mayor que 0 o no estar especificado.", + "max-relation-level-invalid" : "El valor debe ser un número entero.", + "relation-type" : "Tipo de relación", + "relation-type-pattern" : "Patrón de tipo de relación", + "relation-type-pattern-required" : "El patrón de tipo de relación es obligatorio", + "relation-types-list" : "Tipos de relación para propagar", + "relation-types-list-hint" : "Si no se seleccionan tipos de relación para propagar, las alarmas se propagarán sin filtrar por tipo de relación.", + "unlimited-level" : "Nivel ilimitado", + "latest-telemetry" : "Última telemetría", + "add-telemetry-key" : "Agregar clave de telemetría", + "delete-from" : "Eliminar de", + "use-regular-expression-delete-hint" : "Usa expresión regular para eliminar claves por patrón.\n\nConsejos y trucos:\nPresiona 'Enter' para completar el nombre del campo.\nPresiona 'Retroceso' para eliminar el nombre del campo.\nSe admiten múltiples nombres de campos.", + "fetch-into" : "Obtener en", + "attr-mapping" : "Mapeo de atributos:", + "source-attribute" : "Clave del atributo de origen", + "source-attribute-required" : "La clave del atributo de origen es obligatoria.", + "source-telemetry" : "Clave de telemetría de origen", + "source-telemetry-required" : "La clave de telemetría de origen es obligatoria.", + "target-key" : "Clave de destino", + "target-key-required" : "La clave de destino es obligatoria.", + "attr-mapping-required" : "Debe especificarse al menos una entrada de mapeo.", + "fields-mapping" : "Mapeo de campos", + "fields-mapping-hint" : "Si el campo del mensaje se establece en $entityId, se guardará el ID del originador del mensaje en la columna correspondiente de la tabla.", + "relations-query-config-direction-suffix" : "originador", + "profile-name" : "Nombre del perfil", + "fetch-circle-parameter-info-from-metadata-hint" : "El campo de metadatos '{{perimeterKeyName}}' debe definirse en el siguiente formato: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint" : "El campo de metadatos '{{perimeterKeyName}}' debe definirse en el siguiente formato: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip" : "Usa $[messageKey] para extraer valores del mensaje y ${metadataKey} para extraer valores de los metadatos.", + "fields-mapping-required" : "Se debe especificar al menos un mapeo de campo.", + "at-least-one-field-required" : "Al menos un campo de entrada debe tener un valor proporcionado.", + "originator-fields-sv-map-hint" : "Los campos de clave de destino admiten plantillas. Usa $[messageKey] para extraer valores del mensaje y ${metadataKey} para extraer valores de los metadatos.", + "sv-map-hint" : "Solo los campos de clave de destino admiten plantillas. Usa $[messageKey] para extraer valores del mensaje y ${metadataKey} para extraer valores de los metadatos.", + "source-field" : "Campo fuente", + "source-field-required" : "El campo fuente es obligatorio.", + "originator-source" : "Fuente del originador", + "new-originator" : "Nuevo originador", + "originator-customer" : "Cliente", + "originator-tenant" : "Tenant", + "originator-related" : "Entidad relacionada", + "originator-alarm-originator" : "Originador de la alarma", + "originator-entity" : "Entidad por patrón de nombre", + "clone-message" : "Clonar mensaje", + "transform" : "Transformar", + "default-ttl" : "TTL por defecto", + "default-ttl-required" : "El TTL por defecto es obligatorio.", + "default-ttl-hint" : "El nodo de regla obtendrá el valor de TTL del mensaje metadata. Si no hay ningún valor, usará el TTL especificado en la configuración. Si se establece en 0, se usará el TTL del perfil del tenant.", + "default-ttl-zero-hint" : "No se aplicará TTL si el valor es 0.", + "min-default-ttl-message" : "Solo se permite un TTL mínimo de 0.", + "generation-parameters" : "Parámetros de generación", + "message-count" : "Límite de mensajes generados (0 - ilimitado)", + "message-count-required" : "El límite de mensajes generados es obligatorio.", + "min-message-count-message" : "Solo se permite un mínimo de 0 mensajes.", + "period-seconds" : "Periodo en segundos", + "period-seconds-required" : "El periodo es obligatorio.", + "generation-frequency-seconds" : "Frecuencia de generación en segundos", + "generation-frequency-required" : "La frecuencia de generación es obligatoria.", + "min-generation-frequency-message" : "Se permite un mínimo de 60 segundos.", + "script-lang-tbel" : "TBEL", + "script-lang-js" : "JS", + "use-metadata-period-in-seconds-patterns" : "Usar patrón de periodo en segundos", + "use-metadata-period-in-seconds-patterns-hint" : "Si se selecciona, el nodo de regla usará el patrón de intervalo de segundos del mensaje metadata o data suponiendo que los intervalos están en segundos.", + "period-in-seconds-pattern" : "Patrón de periodo en segundos", + "period-in-seconds-pattern-required" : "El patrón de periodo en segundos es obligatorio", + "min-period-seconds-message" : "Solo se permite un mínimo de 60 segundos.", + "originator" : "Originador", + "message-body" : "Cuerpo del mensaje", + "message-metadata" : "Metadatos del mensaje", + "generate" : "Generar", + "current-rule-node" : "Nodo de regla actual", + "current-tenant" : "Tenant actual", + "generator-function" : "Función generadora", + "test-generator-function" : "Probar función generadora", + "generator" : "Generador", + "test-filter-function" : "Probar función de filtro", + "test-switch-function" : "Probar función de switch", + "test-transformer-function" : "Probar función transformadora", + "transformer" : "Transformador", + "alarm-create-condition" : "Condición de creación de alarma", + "test-condition-function" : "Probar función de condición", + "alarm-clear-condition" : "Condición de limpieza de alarma", + "alarm-details-builder" : "Constructor de detalles de alarma", + "test-details-function" : "Probar función de detalles", + "alarm-type" : "Tipo de alarma", + "select-entity-types" : "Seleccionar tipos de entidad", + "alarm-type-required" : "El tipo de alarma es obligatorio.", + "alarm-severity" : "Severidad de la alarma", + "alarm-severity-required" : "La severidad de la alarma es obligatoria", + "alarm-severity-pattern" : "Patrón de severidad de la alarma", + "alarm-status-filter" : "Filtro de estado de la alarma", + "alarm-status-list-empty" : "La lista de estados de alarma está vacía", + "no-alarm-status-matching" : "No se encontró ningún estado de alarma coincidente.", + "propagate" : "Propagar alarma a entidades relacionadas", + "propagate-to-owner" : "Propagar alarma al propietario de la entidad (Cliente o Tenant)", + "propagate-to-tenant" : "Propagar alarma al Tenant", + "condition" : "Condición", + "details" : "Detalles", + "to-string" : "A cadena", + "test-to-string-function" : "Probar función a cadena", + "from-template" : "Desde", + "from-template-required" : "El campo 'Desde' es obligatorio", + "message-to-metadata" : "Mensaje a metadatos", + "metadata-to-message" : "Metadatos a mensaje", + "from-message" : "Desde el mensaje", + "from-metadata" : "Desde metadatos", + "to-template" : "Hacia", + "to-template-required" : "El campo 'Hacia' es obligatorio", + "mail-address-list-template-hint" : "Lista de direcciones separadas por comas, usa ${metadataKey} para valores desde metadatos, $[messageKey] para valores desde el cuerpo del mensaje", + "cc-template" : "Cc", + "bcc-template" : "Bcc", + "subject-template" : "Asunto", + "subject-template-required" : "El campo de Asunto es obligatorio", + "body-template" : "Cuerpo", + "body-template-required" : "El campo de Cuerpo es obligatorio", + "dynamic-mail-body-type" : "Tipo de cuerpo dinámico", + "mail-body-type" : "Tipo de cuerpo de correo", + "body-type-template" : "Plantilla de tipo de cuerpo", + "reply-routing-configuration" : "Configuración de enrutamiento de respuesta", + "rpc-reply-routing-configuration-hint" : "Estos parámetros de configuración especifican las claves de metadatos utilizadas para identificar el servicio, la sesión y la solicitud para enviar una respuesta.", + "reply-routing-configuration-hint" : "Estos parámetros de configuración especifican las claves de metadatos utilizadas para identificar el servicio y la solicitud para enviar una respuesta.", + "request-id-metadata-attribute" : "Id de solicitud", + "service-id-metadata-attribute" : "Id de servicio", + "session-id-metadata-attribute" : "Id de sesión", + "timeout-sec" : "Tiempo de espera en segundos", + "timeout-required" : "El tiempo de espera es obligatorio", + "min-timeout-message" : "Solo se permite un valor mínimo de tiempo de espera de 0.", + "endpoint-url-pattern" : "Patrón de URL del endpoint", + "endpoint-url-pattern-required" : "El patrón de URL del endpoint es obligatorio", + "request-method" : "Método de solicitud", + "use-simple-client-http-factory" : "Usar cliente HTTP simple", + "ignore-request-body" : "Sin cuerpo de solicitud", + "parse-to-plain-text" : "Parsear a texto plano", + "parse-to-plain-text-hint" : "Si está seleccionado, el cuerpo del mensaje de solicitud será transformado de cadena JSON a texto plano, por ejemplo: msg = \"Hello,\\t\\\"world\\\"\" será convertido a Hello, \"world\"", + "read-timeout" : "Tiempo de espera de lectura en milisegundos", + "read-timeout-hint" : "El valor de 0 significa espera infinita", + "max-parallel-requests-count" : "Número máximo de solicitudes paralelas", + "max-parallel-requests-count-hint" : "El valor 0 especifica que no hay límite en el procesamiento paralelo", + "max-response-size" : "Tamaño máximo de respuesta (en KB)", + "max-response-size-hint" : "Cantidad máxima de memoria asignada para almacenar datos al decodificar o codificar mensajes HTTP como cargas JSON o XML", + "headers" : "Encabezados", + "headers-hint" : "Usa ${metadataKey} para valores desde metadatos, $[messageKey] para valores desde el cuerpo del mensaje en campos de encabezado/valor", + "header" : "Encabezado", + "header-required" : "El encabezado es obligatorio", + "value" : "Valor", + "value-required" : "El valor es obligatorio", + "topic-pattern" : "Patrón de tópico", + "key-pattern" : "Patrón de clave", + "key-pattern-hint" : "Opcional. Si se especifica un número de partición válido, se usará al enviar el registro. Si no se especifica partición, se usará la clave. Si no se especifica ninguno, se asignará una partición en orden circular.", + "topic-pattern-required" : "El patrón de tópico es obligatorio", + "topic" : "Tópico", + "topic-required" : "El tópico es obligatorio", + "bootstrap-servers" : "Servidores bootstrap", + "bootstrap-servers-required" : "El valor de servidores bootstrap es obligatorio", + "other-properties" : "Otras propiedades", + "key" : "Clave", + "key-required" : "La clave es obligatoria", + "retries" : "Reintentos automáticos en caso de fallo", + "min-retries-message" : "Solo se permite un mínimo de 0 reintentos.", + "batch-size-bytes" : "Tamaño del lote en bytes", + "min-batch-size-bytes-message" : "Solo se permite un tamaño mínimo de lote de 0.", + "linger-ms" : "Tiempo de retención local (ms)", + "min-linger-ms-message" : "Solo se permite un valor mínimo de 0 ms.", + "buffer-memory-bytes" : "Tamaño máximo del buffer del cliente en bytes", + "min-buffer-memory-message" : "Solo se permite un tamaño mínimo de buffer de 0.", + "memory-buffer-size-range" : "El tamaño del buffer de memoria debe estar entre 0 y {{max}} KB", + "acks" : "Número de confirmaciones", + "topic-arn-pattern" : "Patrón del ARN del tópico", + "topic-arn-pattern-required" : "El patrón del ARN del tópico es obligatorio", + "aws-access-key-id" : "ID de clave de acceso AWS", + "aws-access-key-id-required" : "El ID de clave de acceso AWS es obligatorio", + "aws-secret-access-key" : "Clave secreta de acceso AWS", + "aws-secret-access-key-required" : "La clave secreta de acceso AWS es obligatoria", + "aws-region" : "Región AWS", + "aws-region-required" : "La región AWS es obligatoria", + "exchange-name-pattern" : "Patrón de nombre de exchange", + "routing-key-pattern" : "Patrón de clave de enrutamiento", + "message-properties" : "Propiedades del mensaje", + "host" : "Host", + "host-required" : "El host es obligatorio", + "port" : "Puerto", + "port-required" : "El puerto es obligatorio", + "port-range" : "El puerto debe estar en el rango de 1 a 65535.", + "virtual-host" : "Host virtual", + "username" : "Nombre de usuario", + "password" : "Contraseña", + "automatic-recovery" : "Recuperación automática", + "connection-timeout-ms" : "Tiempo de espera de conexión (ms)", + "min-connection-timeout-ms-message" : "Solo se permite un valor mínimo de 0 ms.", + "handshake-timeout-ms" : "Tiempo de espera de apretón de manos (ms)", + "min-handshake-timeout-ms-message" : "Solo se permite un valor mínimo de 0 ms.", + "client-properties" : "Propiedades del cliente", + "queue-url-pattern" : "Patrón de URL de la cola", + "queue-url-pattern-required" : "El patrón de URL de la cola es obligatorio", + "delay-seconds" : "Retardo (segundos)", + "min-delay-seconds-message" : "Solo se permite un valor mínimo de 0 segundos.", + "max-delay-seconds-message" : "Solo se permite un valor máximo de 900 segundos.", + "name" : "Nombre", + "name-required" : "El nombre es obligatorio", + "queue-type" : "Tipo de cola", + "sqs-queue-standard" : "Estándar", + "sqs-queue-fifo" : "FIFO", + "gcp-project-id" : "ID del proyecto de GCP", + "gcp-project-id-required" : "El ID del proyecto de GCP es obligatorio", + "gcp-service-account-key" : "Archivo de clave de cuenta de servicio de GCP", + "gcp-service-account-key-required" : "El archivo de clave de cuenta de servicio de GCP es obligatorio", + "pubsub-topic-name" : "Nombre del tópico", + "pubsub-topic-name-required" : "El nombre del tópico es obligatorio", + "message-attributes" : "Atributos del mensaje", + "message-attributes-hint" : "Usa ${metadataKey} para valores desde metadatos, $[messageKey] para valores desde el cuerpo del mensaje en campos nombre/valor", + "connect-timeout" : "Tiempo de espera de conexión (seg)", + "connect-timeout-required" : "El tiempo de espera de conexión es obligatorio.", + "connect-timeout-range" : "El tiempo de espera de conexión debe estar en el rango de 1 a 200.", + "client-id" : "ID del cliente", + "client-id-hint" : "Opcional. Déjelo vacío para un ID de cliente autogenerado. Cuidado al especificar el ID del cliente. La mayoría de los brokers MQTT no permiten múltiples conexiones con el mismo ID. Para evitar errores en entornos de microservicios, habilite la opción \"Agregar ID de servicio como sufijo al ID de cliente\".", + "append-client-id-suffix" : "Agregar ID de servicio como sufijo al ID del cliente", + "client-id-suffix-hint" : "Opcional. Se aplica cuando se especifica un \"ID de cliente\" explícitamente. Si está seleccionado, el ID del servicio se agregará como sufijo al ID del cliente.", + "device-id" : "ID del dispositivo", + "device-id-required" : "El ID del dispositivo es obligatorio.", + "clean-session" : "Sesión limpia", + "enable-ssl" : "Habilitar SSL", + "credentials" : "Credenciales", + "credentials-type" : "Tipo de credenciales", + "credentials-type-required" : "El tipo de credenciales es obligatorio.", + "credentials-anonymous" : "Anónimo", + "credentials-basic" : "Básico", + "credentials-pem" : "PEM", + "credentials-pem-hint" : "Se requiere al menos el archivo del certificado CA del servidor o un par de archivos de certificado de cliente y clave privada del cliente", + "credentials-sas" : "Firma de acceso compartido (SAS)", + "sas-key" : "Clave SAS", + "sas-key-required" : "La clave SAS es obligatoria.", + "hostname" : "Nombre del host", + "hostname-required" : "El nombre del host es obligatorio.", + "azure-ca-cert" : "Archivo de certificado CA", + "username-required" : "El nombre de usuario es obligatorio.", + "password-required" : "La contraseña es obligatoria.", + "ca-cert" : "Archivo del certificado CA del servidor", + "private-key" : "Archivo de clave privada del cliente", + "cert" : "Archivo de certificado del cliente", + "no-file" : "No se seleccionó ningún archivo.", + "drop-file" : "Suelte un archivo o haga clic para seleccionar uno para cargar.", + "private-key-password" : "Contraseña de la clave privada", + "use-system-smtp-settings" : "Usar configuración SMTP del sistema", + "use-metadata-dynamic-interval" : "Usar intervalo dinámico", + "metadata-dynamic-interval-hint" : "Los campos de entrada de intervalo de inicio y fin soportan plantillas. El valor sustituido debe estar en milisegundos. Usa $[messageKey] o ${metadataKey}.", + "use-metadata-interval-patterns-hint" : "Si está seleccionado, el nodo de reglas usará los patrones de intervalo desde metadatos o datos, asumiendo que están en milisegundos.", + "use-message-alarm-data" : "Usar datos de alarma del mensaje", + "overwrite-alarm-details" : "Sobrescribir detalles de alarma", + "use-alarm-severity-pattern" : "Usar patrón de severidad de alarma", + "check-all-keys" : "Verificar que todos los campos especificados estén presentes", + "check-all-keys-hint" : "Si está seleccionado, se verifica que todas las claves especificadas estén presentes en los datos del mensaje y los metadatos.", + "check-relation-to-specific-entity" : "Verificar relación con una entidad específica", + "check-relation-to-specific-entity-tooltip" : "Si está habilitado, verifica la relación con una entidad específica, de lo contrario, verifica con cualquier entidad.", + "check-relation-hint" : "Verifica la existencia de relación con una entidad específica o cualquier entidad según la dirección y tipo de relación.", + "delete-relation-with-specific-entity" : "Eliminar relación con una entidad específica", + "delete-relation-with-specific-entity-hint" : "Si está habilitado, eliminará la relación con solo una entidad específica. Si no, se eliminará con todas las entidades coincidentes.", + "delete-relation-hint" : "Elimina relaciones del originador del mensaje entrante con una entidad o lista de entidades según dirección y tipo.", + "remove-current-relations" : "Eliminar relaciones actuales", + "remove-current-relations-hint" : "Elimina relaciones actuales del originador del mensaje entrante basadas en dirección y tipo.", + "change-originator-to-related-entity" : "Cambiar originador a entidad relacionada", + "change-originator-to-related-entity-hint" : "Se utiliza para procesar el mensaje como si viniera de otra entidad.", + "start-interval" : "Inicio del intervalo", + "end-interval" : "Fin del intervalo", + "start-interval-required" : "El inicio del intervalo es obligatorio.", + "end-interval-required" : "El fin del intervalo es obligatorio.", + "smtp-protocol" : "Protocolo", + "smtp-host" : "Host SMTP", + "smtp-host-required" : "El host SMTP es obligatorio.", + "smtp-port" : "Puerto SMTP", + "smtp-port-required" : "Debe proporcionar un puerto SMTP.", + "smtp-port-range" : "El puerto SMTP debe estar en un rango de 1 a 65535.", + "timeout-msec" : "Tiempo de espera ms", + "min-timeout-msec-message" : "Solo se permite un mínimo de 0 ms.", + "enter-username" : "Ingrese nombre de usuario", + "enter-password" : "Ingrese contraseña", + "enable-tls" : "Habilitar TLS", + "tls-version" : "Versión de TLS", + "enable-proxy" : "Habilitar proxy", + "use-system-proxy-properties" : "Usar propiedades de proxy del sistema", + "proxy-host" : "Host del proxy", + "proxy-host-required" : "El host del proxy es obligatorio.", + "proxy-port" : "Puerto del proxy", + "proxy-port-required" : "El puerto del proxy es obligatorio.", + "proxy-port-range" : "El puerto del proxy debe estar en el rango de 1 a 65535.", + "proxy-user" : "Usuario del proxy", + "proxy-password" : "Contraseña del proxy", + "proxy-scheme" : "Esquema del proxy", + "numbers-to-template" : "Plantilla de números de teléfono de destino", + "numbers-to-template-required" : "La plantilla de números de teléfono de destino es obligatoria", + "numbers-to-template-hint" : "Números de teléfono separados por comas, usa ${metadataKey} para valores desde metadatos, $[messageKey] para valores desde el cuerpo del mensaje", + "sms-message-template" : "Plantilla de mensaje SMS", + "sms-message-template-required" : "La plantilla de mensaje SMS es obligatoria", + "use-system-sms-settings" : "Usar configuración del proveedor SMS del sistema", + "min-period-0-seconds-message" : "Solo se permite un período mínimo de 0 segundos.", + "max-pending-messages" : "Máximo de mensajes pendientes", + "max-pending-messages-required" : "El número máximo de mensajes pendientes es obligatorio.", + "max-pending-messages-range" : "El número máximo de mensajes pendientes debe estar en el rango de 1 a 100000.", + "originator-types-filter" : "Filtro por tipos de originador", + "interval-seconds" : "Intervalo en segundos", + "interval-seconds-required" : "El intervalo es obligatorio.", + "int-range" : "El valor no debe exceder el límite máximo de enteros (2147483648)", + "min-interval-seconds-message" : "Solo se permite un intervalo mínimo de 1 segundo.", + "output-timeseries-key-prefix" : "Prefijo de clave de serie temporal de salida", + "output-timeseries-key-prefix-required" : "Se requiere el prefijo de clave de serie temporal de salida.", + "separator-hint" : "Debes presionar \"Enter\" para completar el ingreso del campo.", + "select-details" : "Seleccionar detalles", + "entity-details-id" : "Id", + "entity-details-title" : "Título", + "entity-details-country" : "País", + "entity-details-state" : "Estado", + "entity-details-city" : "Ciudad", + "entity-details-zip" : "Código postal", + "entity-details-address" : "Dirección", + "entity-details-address2" : "Dirección 2", + "entity-details-additional_info" : "Información adicional", + "entity-details-phone" : "Teléfono", + "entity-details-email" : "Correo electrónico", + "email-sender" : "Remitente del correo", + "fields-to-check" : "Campos a verificar", + "add-detail" : "Agregar detalle", + "check-all-keys-tooltip" : "Si está habilitado, verifica la presencia de todos los campos listados en los nombres de campo del mensaje y de metadatos dentro del mensaje entrante y sus metadatos.", + "fields-to-check-hint" : "Presiona \"Enter\" para completar la entrada del nombre del campo. Se admiten múltiples nombres de campo.", + "entity-details-list-empty" : "Debe seleccionarse al menos un detalle.", + "alarm-status" : "Estado de alarma", + "alarm-required" : "Debe seleccionarse al menos un estado de alarma.", + "no-entity-details-matching" : "No se encontraron detalles de entidad coincidentes.", + "custom-table-name" : "Nombre de tabla personalizado", + "custom-table-name-required" : "El nombre de la tabla es obligatorio", + "custom-table-hint" : "La tabla debe estar creada en su clúster de Cassandra y su nombre debe comenzar con el prefijo 'cs_tb_' para evitar la inserción de datos en las tablas comunes de TB. Introduzca aquí el nombre de la tabla sin el prefijo 'cs_tb_'.", + "message-field" : "Campo del mensaje", + "message-field-required" : "El campo del mensaje es obligatorio.", + "table-col" : "Columna de tabla", + "table-col-required" : "La columna de tabla es obligatoria.", + "latitude-field-name" : "Nombre del campo de latitud", + "longitude-field-name" : "Nombre del campo de longitud", + "latitude-field-name-required" : "El nombre del campo de latitud es obligatorio.", + "longitude-field-name-required" : "El nombre del campo de longitud es obligatorio.", + "fetch-perimeter-info-from-metadata" : "Obtener información del perímetro desde metadatos", + "fetch-perimeter-info-from-metadata-tooltip" : "Si el tipo de perímetro está configurado como 'Polígono', el valor del campo de metadatos '{{perimeterKeyName}}' se establecerá como definición de perímetro sin análisis adicional. Si el tipo de perímetro está configurado como 'Círculo', el valor del campo de metadatos '{{perimeterKeyName}}' se analizará para extraer los campos 'latitude', 'longitude', 'radius', 'radiusUnit' usados en la definición de perímetro circular.", + "perimeter-key-name" : "Nombre de clave de perímetro", + "perimeter-key-name-hint" : "Nombre del campo de metadatos que contiene la información del perímetro.", + "perimeter-key-name-required" : "El nombre de la clave de perímetro es obligatorio.", + "perimeter-circle" : "Círculo", + "perimeter-polygon" : "Polígono", + "perimeter-type" : "Tipo de perímetro", + "circle-center-latitude" : "Latitud del centro", + "circle-center-latitude-required" : "La latitud del centro es obligatoria.", + "circle-center-longitude" : "Longitud del centro", + "circle-center-longitude-required" : "La longitud del centro es obligatoria.", + "range-unit-meter" : "Metro", + "range-unit-kilometer" : "Kilómetro", + "range-unit-foot" : "Pie", + "range-unit-mile" : "Milla", + "range-unit-nautical-mile" : "Milla náutica", + "range-units" : "Unidades de distancia", + "range-units-required" : "Las unidades de distancia son obligatorias.", + "range" : "Distancia", + "range-required" : "La distancia es obligatoria.", + "polygon-definition" : "Definición de polígono", + "polygon-definition-required" : "La definición de polígono es obligatoria.", + "polygon-definition-hint" : "Use el siguiente formato para la definición manual de un polígono: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration" : "Duración mínima dentro", + "min-inside-duration-value-required" : "La duración mínima dentro es obligatoria", + "min-inside-duration-time-unit" : "Unidad de tiempo de duración mínima dentro", + "min-outside-duration" : "Duración mínima fuera", + "min-outside-duration-value-required" : "La duración mínima fuera es obligatoria", + "min-outside-duration-time-unit" : "Unidad de tiempo de duración mínima fuera", + "tell-failure-if-absent" : "Informar fallo", + "tell-failure-if-absent-hint" : "Si al menos una clave seleccionada no existe, el mensaje saliente indicará \"Fallo\".", + "get-latest-value-with-ts" : "Obtener marca de tiempo para los valores más recientes de telemetría", + "get-latest-value-with-ts-hint" : "Si está seleccionado, los valores más recientes de telemetría también incluirán una marca de tiempo, p. ej.: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings" : "Ignorar cadenas vacías", + "ignore-null-strings-hint" : "Si está seleccionado, el nodo de reglas ignorará los campos de entidad con valores vacíos.", + "add-metadata-key-values-as-kafka-headers" : "Agregar pares clave-valor de metadatos como encabezados de registros Kafka", + "add-metadata-key-values-as-kafka-headers-hint" : "Si está seleccionado, los pares clave-valor de los metadatos del mensaje se agregarán como encabezados en los registros salientes como matrices de bytes con codificación de caracteres predefinida.", + "charset-encoding" : "Codificación de caracteres", + "charset-encoding-required" : "La codificación de caracteres es obligatoria.", + "charset-us-ascii" : "US-ASCII", + "charset-iso-8859-1" : "ISO-8859-1", + "charset-utf-8" : "UTF-8", + "charset-utf-16be" : "UTF-16BE", + "charset-utf-16le" : "UTF-16LE", + "charset-utf-16" : "UTF-16", + "select-queue-hint" : "El nombre de la cola puede seleccionarse de una lista desplegable o introducirse de forma personalizada.", + "device-profile-node-hint" : "Útil si tiene condiciones de duración o repetición para garantizar la continuidad en la evaluación del estado de la alarma.", + "persist-alarm-rules" : "Persistir estado de reglas de alarma", + "persist-alarm-rules-hint" : "Si está habilitado, el nodo de reglas almacenará el estado del procesamiento en la base de datos.", + "fetch-alarm-rules" : "Recuperar estado de reglas de alarma", + "fetch-alarm-rules-hint" : "Si está habilitado, el nodo de reglas restaurará el estado del procesamiento durante la inicialización y garantizará que las alarmas se activen incluso después de reinicios del servidor. De lo contrario, el estado se restaurará cuando llegue el primer mensaje del dispositivo.", + "input-value-key" : "Clave de valor de entrada", + "input-value-key-required" : "La clave de valor de entrada es obligatoria.", + "output-value-key" : "Clave de valor de salida", + "output-value-key-required" : "La clave de valor de salida es obligatoria.", + "number-of-digits-after-floating-point" : "Número de dígitos después del punto decimal", + "number-of-digits-after-floating-point-range" : "El número de dígitos después del punto decimal debe estar entre 0 y 15.", + "failure-if-delta-negative" : "Informar fallo si la diferencia es negativa", + "failure-if-delta-negative-tooltip" : "El nodo de reglas fuerza el fallo del procesamiento del mensaje si el valor de la diferencia es negativo.", + "use-caching" : "Usar caché", + "use-caching-tooltip" : "El nodo de reglas almacenará en caché el valor de \"{{inputValueKey}}\" que llega desde el mensaje entrante para mejorar el rendimiento. Nota: la caché no se actualizará si se modifica el valor de \"{{inputValueKey}}\" en otro lugar.", + "add-time-difference-between-readings" : "Agregar la diferencia de tiempo entre lecturas de \"{{inputValueKey}}\"", + "add-time-difference-between-readings-tooltip" : "Si está habilitado, el nodo de reglas añadirá \"{{periodValueKey}}\" al mensaje de salida.", + "period-value-key" : "Clave del valor del período", + "period-value-key-required" : "La clave del valor del período es obligatoria.", + "general-pattern-hint" : "Usa ${metadataKey} para valores de metadatos, $[messageKey] para valores del cuerpo del mensaje.", + "alarm-severity-pattern-hint" : "Usa ${metadataKey} para metadatos y $[messageKey] para cuerpo del mensaje. La severidad debe ser del sistema (CRITICAL, MAJOR, etc.)", + "output-node-name-hint" : "El nombre del nodo de reglas corresponde al tipo de relación del mensaje de salida, y se usa para reenviar mensajes a otros nodos de reglas en la misma cadena.", + "use-server-ts" : "Usar marca de tiempo del servidor", + "use-server-ts-hint" : "Usar la marca de tiempo actual del servidor para datos de series temporales que no la incluyen. Ayuda a mantener el orden cuando los mensajes llegan fuera de secuencia o desde múltiples fuentes.", + "kv-map-pattern-hint" : "Todos los campos de entrada admiten plantillas. Usa $[messageKey] para extraer valores del mensaje y ${metadataKey} de los metadatos.", + "kv-map-single-pattern-hint" : "El campo de entrada admite plantillas. Usa $[messageKey] para valores del mensaje y ${metadataKey} para valores de metadatos.", + "shared-scope" : "Ámbito compartido", + "server-scope" : "Ámbito del servidor", + "client-scope" : "Ámbito del cliente", + "attribute-type" : "Atributo", + "attribute-type-description" : "Obtener valor del atributo desde la base de datos", + "attribute-type-result-description" : "Guardar resultado como atributo de la entidad en la base de datos", + "constant-type" : "Constante", + "constant-type-description" : "Definir valor constante", + "time-series-type" : "Serie temporal", + "time-series-type-description" : "Obtener último valor de serie temporal desde la base de datos", + "time-series-type-result-description" : "Guardar resultado como serie temporal de la entidad en la base de datos", + "message-body-type" : "Mensaje", + "message-body-type-description" : "Obtener valor del argumento desde el mensaje entrante", + "message-body-type-result-description" : "Agregar resultado al mensaje de salida", + "message-metadata-type" : "Metadatos", + "message-metadata-type-description" : "Obtener valor del argumento desde los metadatos del mensaje", + "message-metadata-result-description" : "Agregar resultado a los metadatos del mensaje de salida", + "argument-tile" : "Argumentos", + "no-arguments-prompt" : "No hay argumentos configurados", + "result-title" : "Resultado", + "functions-field-input" : "Funciones", + "no-option-found" : "No se encontraron opciones", + "argument-source-field-input" : "Fuente", + "argument-source-field-input-required" : "La fuente del argumento es obligatoria.", + "argument-key-field-input" : "Clave", + "argument-key-field-input-required" : "La clave del argumento es obligatoria.", + "constant-value-field-input" : "Valor constante", + "constant-value-field-input-required" : "El valor constante es obligatorio.", + "attribute-scope-field-input" : "Ámbito del atributo", + "attribute-scope-field-input-required" : "El ámbito del atributo es obligatorio.", + "default-value-field-input" : "Valor por defecto", + "type-field-input" : "Tipo", + "type-field-input-required" : "El tipo es obligatorio.", + "key-field-input" : "Clave", + "key-field-input-required" : "La clave es obligatoria.", + "add-entity-type" : "Agregar tipo de entidad", + "add-device-profile" : "Agregar perfil de dispositivo", + "number-floating-point-field-input" : "Dígitos después del punto decimal", + "number-floating-point-field-input-hint" : "Usa 0 para convertir el resultado en entero", + "add-to-message-field-input" : "Agregar al mensaje", + "add-to-metadata-field-input" : "Agregar a metadatos", + "custom-expression-field-input" : "Expresión matemática", + "custom-expression-field-input-required" : "La expresión matemática es obligatoria", + "custom-expression-field-input-hint" : "Especifica una expresión matemática para evaluar. La expresión por defecto demuestra cómo convertir Fahrenheit a Celsius", + "retained-message" : "Retenido", + "attributes-mapping" : "Mapeo de atributos", + "latest-telemetry-mapping" : "Mapeo de última telemetría", + "add-mapped-attribute-to" : "Agregar atributos mapeados a", + "add-mapped-latest-telemetry-to" : "Agregar última telemetría mapeada a", + "add-mapped-fields-to" : "Agregar campos mapeados a", + "add-selected-details-to" : "Agregar detalles seleccionados a", + "clear-selected-types" : "Borrar tipos seleccionados", + "clear-selected-details" : "Borrar detalles seleccionados", + "clear-selected-fields" : "Borrar campos seleccionados", + "clear-selected-keys" : "Borrar claves seleccionadas", + "geofence-configuration" : "Configuración de geocerca", + "coordinate-field-names" : "Nombres de campos de coordenadas", + "coordinate-field-hint" : "El nodo de reglas intenta obtener los campos especificados desde el mensaje. Si no están presentes, los buscará en los metadatos.", + "presence-monitoring-strategy" : "Estrategia de monitoreo de presencia", + "presence-monitoring-strategy-on-first-message" : "En el primer mensaje", + "presence-monitoring-strategy-on-each-message" : "En cada mensaje", + "presence-monitoring-strategy-on-first-message-hint" : "Informa el estado de presencia 'Dentro' o 'Fuera' en el primer mensaje después de que haya pasado la duración mínima configurada desde la última actualización de estado de presencia 'Entró' o 'Salió'.", + "presence-monitoring-strategy-on-each-message-hint" : "Informa el estado de presencia 'Dentro' o 'Fuera' en cada mensaje después de una actualización de estado de presencia 'Entró' o 'Salió'.", + "fetch-credentials-to" : "Obtener credenciales en", + "add-originator-attributes-to" : "Agregar atributos del originador a", + "originator-attributes" : "Atributos del originador", + "fetch-latest-telemetry-with-timestamp" : "Obtener la última telemetría con marca de tiempo", + "fetch-latest-telemetry-with-timestamp-tooltip" : "Si está seleccionado, los últimos valores de telemetría se agregarán a los metadatos de salida con marca de tiempo, por ejemplo: \"{{latestTsKeyName}}\": \"{\\\"ts\\\":1574329385897, \\\"value\\\":42}\"", + "tell-failure" : "Informar fallo si falta algún atributo", + "tell-failure-tooltip" : "Si falta al menos una clave seleccionada, el mensaje de salida informará 'Fallo'.", + "created-time" : "Hora de creación", + "chip-help" : "Presiona 'Enter' para completar la entrada de {{inputName}}. \nPresiona 'Backspace' para borrar {{inputName}}. \nSe admiten múltiples valores.", + "detail" : "detalle", + "field-name" : "nombre del campo", + "device-profile" : "perfil de dispositivo", + "entity-type" : "tipo de entidad", + "message-type" : "tipo de mensaje", + "timeseries-key" : "clave de serie temporal", + "type" : "Tipo", + "first-name" : "Nombre", + "last-name" : "Apellido", + "label" : "Etiqueta", + "originator-fields-mapping" : "Mapeo de campos del originador", + "add-mapped-originator-fields-to" : "Agregar campos mapeados del originador a", + "fields" : "Campos", + "skip-empty-fields" : "Omitir campos vacíos", + "skip-empty-fields-tooltip" : "Los campos con valores vacíos no se agregarán al mensaje/metadatos de salida.", + "fetch-interval" : "Intervalo de obtención", + "fetch-strategy" : "Estrategia de obtención", + "fetch-timeseries-from-to" : "Obtener serie temporal desde hace {{startInterval}} {{startIntervalTimeUnit}} hasta hace {{endInterval}} {{endIntervalTimeUnit}}.", + "fetch-timeseries-from-to-invalid" : "Obtención de serie temporal no válida (\"Inicio del intervalo\" debe ser menor que \"Fin del intervalo\").", + "use-metadata-dynamic-interval-tooltip" : "Si está seleccionado, el nodo de reglas usará intervalos dinámicos de inicio y fin basados en patrones de mensaje y metadatos.", + "all-mode-hint" : "Si se selecciona el modo de obtención 'Todo', el nodo de reglas recuperará telemetría desde el intervalo de obtención con parámetros configurables.", + "first-mode-hint" : "Si se selecciona el modo de obtención 'Primero', el nodo recuperará la telemetría más cercana al inicio del intervalo.", + "last-mode-hint" : "Si se selecciona el modo de obtención 'Último', el nodo recuperará la telemetría más cercana al final del intervalo.", + "ascending" : "Ascendente", + "descending" : "Descendente", + "min" : "Mínimo", + "max" : "Máximo", + "average" : "Promedio", + "sum" : "Suma", + "count" : "Conteo", + "none" : "Ninguno", + "last-level-relation-tooltip" : "Si se selecciona, el nodo buscará entidades relacionadas solo en el nivel definido en el máximo nivel de relación.", + "last-level-device-relation-tooltip" : "Si se selecciona, el nodo buscará dispositivos relacionados solo en el nivel definido en el máximo nivel de relación.", + "data-to-fetch" : "Datos a obtener", + "mapping-of-customers" : "Mapeo de clientes", + "map-fields-required" : "Todos los campos de mapeo son obligatorios.", + "attributes" : "Atributos", + "related-device-attributes" : "Atributos de dispositivos relacionados", + "add-selected-attributes-to" : "Agregar atributos seleccionados a", + "device-profiles" : "Perfiles de dispositivo", + "mapping-of-tenant" : "Mapeo de inquilino", + "add-attribute-key" : "Agregar clave de atributo", + "message-template" : "Plantilla de mensaje", + "message-template-required" : "La plantilla de mensaje es obligatoria", + "use-system-slack-settings" : "Usar configuración de Slack del sistema", + "slack-api-token" : "Token de API de Slack", + "slack-api-token-required" : "El token de API de Slack es obligatorio", + "keys-mapping" : "Mapeo de claves", + "add-key" : "Agregar clave", + "recipients" : "Destinatarios", + "message-subject-and-content" : "Asunto y contenido del mensaje", + "template-rules-hint" : "Ambos campos de entrada admiten tematización. Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraer el valor de los metadatos.", + "originator-customer-desc" : "Usar el cliente del originador del mensaje entrante como nuevo originador.", + "originator-tenant-desc" : "Usar el inquilino actual como nuevo originador.", + "originator-related-entity-desc" : "Usar entidad relacionada como nuevo originador. La búsqueda se basa en el tipo y dirección de relación configurados.", + "originator-alarm-originator-desc" : "Usar el originador de la alarma como nuevo originador. Solo si el originador del mensaje entrante es una entidad de alarma.", + "originator-entity-by-name-pattern-desc" : "Usar entidad obtenida desde la base de datos como nuevo originador. La búsqueda se basa en el tipo de entidad y el patrón de nombre especificado.", + "email-from-template-hint" : "Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraer el valor de los metadatos.", + "recipients-block-main-hint" : "Lista de direcciones separadas por comas. Todos los campos de entrada admiten tematización. Usa $[messageKey] para extraer valor del mensaje y ${metadataKey} para extraer valor de metadatos.", + "forward-msg-default-rule-chain" : "Redirigir mensaje a la cadena de reglas predeterminada del originador", + "forward-msg-default-rule-chain-tooltip" : "Si está habilitado, el mensaje se redirigirá a la cadena de reglas predeterminada del originador, o a la definida en la configuración si no tiene una definida en el perfil de la entidad.", + "exclude-zero-deltas" : "Excluir deltas cero del mensaje saliente", + "exclude-zero-deltas-hint" : "Si está habilitado, la clave de salida \"{{outputValueKey}}\" se añadirá al mensaje de salida solo si su valor no es cero.", + "exclude-zero-deltas-time-difference-hint" : "Si está habilitado, las claves de salida \"{{outputValueKey}}\" y \"{{periodValueKey}}\" se añadirán al mensaje de salida solo si el valor de \"{{outputValueKey}}\" no es cero.", + "search-direction-from" : "Del originador a la entidad destino", + "search-direction-to" : "De la entidad destino al originador", + "del-relation-direction-from" : "Desde el originador", + "del-relation-direction-to" : "Hacia el originador", + "target-entity" : "Entidad destino", + "function-configuration" : "Configuración de la función", + "function-name" : "Nombre de la función", + "function-name-required" : "El nombre de la función es obligatorio.", + "qualifier" : "Calificador", + "qualifier-hint" : "Si no se especifica calificador, se usará el calificador predeterminado \"$LATEST\".", + "aws-credentials" : "Credenciales de AWS", + "connection-timeout" : "Tiempo de espera de conexión", + "connection-timeout-required" : "El tiempo de espera de conexión es obligatorio.", + "connection-timeout-min" : "El tiempo mínimo de conexión es 0.", + "connection-timeout-hint" : "Tiempo en segundos que se espera al establecer la conexión antes de agotar el tiempo. 0 significa infinito, pero no se recomienda.", + "request-timeout" : "Tiempo de espera de la solicitud", + "request-timeout-required" : "El tiempo de espera de la solicitud es obligatorio", + "request-timeout-min" : "El tiempo mínimo de solicitud es 0", + "request-timeout-hint" : "Tiempo en segundos que se espera para completar la solicitud antes de agotar el tiempo. 0 significa infinito, pero no se recomienda.", + "units" : "Unidades", + "tell-failure-aws-lambda" : "Informar fallo si la ejecución de AWS Lambda lanza una excepción", + "tell-failure-aws-lambda-hint" : "El nodo de reglas forzará el fallo del procesamiento si la función AWS Lambda lanza una excepción.", + "basic-mode" : "Básico", + "advanced-mode" : "Avanzado", + "save-time-series" : { + "processing-settings" : "Configuraciones de procesamiento", + "processing-settings-hint" : "Define cómo se procesan los mensajes entrantes. Las configuraciones básicas permiten seleccionar estrategias preconfiguradas, mientras que las avanzadas permiten seleccionar estrategias individuales por acción.", + "advanced-settings-hint" : "Ten cuidado al configurar estrategias de procesamiento. Algunas combinaciones pueden llevar a comportamientos inesperados.", + "strategy" : "Estrategia", + "deduplication-interval" : "Intervalo de desduplicación", + "deduplication-interval-required" : "El intervalo de desduplicación es obligatorio", + "deduplication-interval-min-max-range" : "El intervalo de desduplicación debe ser al menos 1 segundo y como máximo 1 día", + "strategy-type" : { + "every-message" : "En cada mensaje", + "skip" : "Omitir", + "deduplicate" : "Desduplicar", + "web-sockets-only" : "Solo WebSockets" + }, + "time-series" : "Serie temporal", + "latest" : "Últimos valores", + "web-sockets" : "WebSockets", + "calculated-fields" : "Campos calculados" }, - "image-input": { - "drop-images-or": "Arrastrar y soltar imagenes o", - "drag-and-drop": "Arrastrar y soltar", - "or": "O", - "browse": "Navegar", - "no-images": "No hay imágenes seleccionadas", - "images": "imágenes" + "save-attribute" : { + "processing-settings" : "Configuraciones de procesamiento", + "processing-settings-hint" : "Define cómo se procesan los mensajes entrantes. Las configuraciones básicas permiten seleccionar estrategias preconfiguradas, mientras que las avanzadas permiten seleccionar estrategias individuales por acción.", + "advanced-settings-hint" : "Ten cuidado al configurar estrategias de procesamiento. Algunas combinaciones pueden llevar a comportamientos inesperados.", + "strategy" : "Estrategia", + "deduplication-interval" : "Intervalo de desduplicación", + "deduplication-interval-required" : "El intervalo de desduplicación es obligatorio", + "deduplication-interval-min-max-range" : "El intervalo de desduplicación debe ser al menos 1 segundo y como máximo 1 día", + "scope" : "Ámbito", + "strategy-type" : { + "every-message" : "En cada mensaje", + "skip" : "Omitir", + "deduplicate" : "Desduplicar", + "web-sockets-only" : "Solo WebSockets" + }, + "attributes" : "Atributos" }, - "import": { - "no-file": "Ningún archivo seleccionado", - "drop-file": "Suelte un archivo JSON o haga clic para seleccionar un archivo para cargar.", - "drop-json-file-or": "Suele un archivo JSON o", - "drop-file-csv": "Suelte un archivo CSV o haga clic para seleccionar un archivo para cargar.", - "drop-file-csv-or": "Suelte un archivo CSV o", - "column-value": "Valor", - "column-title": "Título", - "column-example": "Datos de ejemplo", - "column-key": "Clave de atributo/telemetría", - "credentials": "Credenciales", - "csv-delimiter": "Delimitador CSV", - "csv-first-line-header": "La primera línea contiene nombres de columna.", - "csv-update-data": "Actualizar atributos/telemetría", - "details": "Detalles", - "import-csv-number-columns-error": "Un archivo debe contener al menos dos columnas", - "import-csv-invalid-format-error": "Formato de archivo inválido. Línea: '{{line}}'", - "column-type": { - "name": "Nombre", - "type": "Tipo", - "label": "Etiqueta", - "column-type": "Tipo de columna", - "client-attribute": "Atributo de cliente", - "shared-attribute": "Atributo compartido", - "server-attribute": "Atributo de servidor", - "timeseries": "Series de tiempo", - "entity-field": "Campo de entidad", - "access-token": "Token de acceso", - "x509": "X.509", - "mqtt": { - "client-id": "Client ID MQTT", - "user-name": "Usuario MQTT", - "password": "Contraseña MQTT" - }, - "lwm2m": { - "client-endpoint": "Nombre endpoint cliente LwM2M", - "security-config-mode": "Configuración de seguridad LwM2M", - "client-identity": "Identidad cliente LwM2M", - "client-key": "Clave cliente LwM2M", - "client-cert": "Clave pública cliente LwM2M", - "bootstrap-server-security-mode": "Modo seguridad servidor Bootstrap LwM2M", - "bootstrap-server-secret-key": "Clave secreta servidor Bootstrap LwM2M", - "bootstrap-server-public-key-id": "Clave pública o id servidor Bootstrap LwM2M", - "lwm2m-server-security-mode": "Modo seguridad servidor LwM2M", - "lwm2m-server-secret-key": "Clave secreta servidor LwM2M", - "lwm2m-server-public-key-id": "Clave pública servidor LwM2M" - }, - "snmp": { - "host": "Host SNMP", - "port": "Puerto SNMP", - "version": "Versión SNMP (v1, v2c or v3)", - "community-string": "String de SNMP community" - }, - "isgateway": "Es Gateway", - "activity-time-from-gateway-device": "Fecha de actividad desde el dispositivo gateway", - "description": "Descripción", - "routing-key": "Clave Edge", - "secret": "Secreto Edge" - }, - "stepper-text": { - "select-file": "Seleccione un archivo", - "configuration": "Importar configuración", - "column-type": "Seleccionar tipo de columnas", - "creat-entities": "Creando nuevas entidades" - }, - "message": { - "create-entities": "Se crearon {{count}} nuevas entidades correctamente.", - "update-entities": "{{count}} entidades se actualizaron correctamente.", - "error-entities": "Se produjo un error al crear {{count}} entidades." - } + "key-val" : { + "key" : "Clave", + "value" : "Valor", + "see-examples" : "Ver ejemplos.", + "remove-entry" : "Eliminar entrada", + "remove-mapping-entry" : "Eliminar asignación", + "add-mapping-entry" : "Agregar asignación", + "add-entry" : "Agregar entrada", + "copy-key-values-from" : "Copiar clave-valor desde", + "delete-key-values" : "Eliminar clave-valor", + "delete-key-values-from" : "Eliminar clave-valor desde", + "at-least-one-key-error" : "Se debe seleccionar al menos una clave.", + "unique-key-value-pair-error" : "¡'{{keyText}}' debe ser diferente de '{{valText}}'!" }, - "item": { - "selected": "Seleccionado" + "mail-body-types" : { + "plain-text" : "Texto plano", + "html" : "HTML", + "dynamic" : "Dinámico", + "use-body-type-template" : "Usar plantilla de tipo de cuerpo", + "plain-text-description" : "Texto simple sin formato con estilo o formato especial.", + "html-text-description" : "Permite el uso de etiquetas HTML para formato, enlaces e imágenes en el cuerpo del correo.", + "dynamic-text-description" : "Permite usar texto plano o HTML dinámicamente según la función de tematización.", + "after-template-evaluation-hint" : "Después de la evaluación de la plantilla, el valor debe ser true para HTML y false para texto plano." + } + }, + "timezone" : { + "timezone" : "Zona horaria", + "select-timezone" : "Seleccionar zona horaria", + "no-timezones-matching" : "No se encontraron zonas horarias que coincidan con '{{timezone}}'.", + "timezone-required" : "La zona horaria es obligatoria.", + "browser-time" : "Hora del navegador" + }, + "queue" : { + "queue-name" : "Cola", + "no-queues-found" : "No se encontraron colas.", + "no-queues-matching" : "No se encontraron colas que coincidan con '{{queue}}'.", + "select-name" : "Seleccionar nombre de la cola", + "name" : "Nombre", + "name-required" : "¡El nombre de la cola es obligatorio!", + "name-unique" : "¡El nombre de la cola no es único!", + "name-pattern" : "¡El nombre de la cola contiene caracteres no permitidos! Solo se permiten caracteres alfanuméricos ASCII, '.', '_' y '-'.", + "queue-required" : "¡La cola es obligatoria!", + "topic-required" : "¡El tópico de la cola es obligatorio!", + "poll-interval-required" : "¡El intervalo de sondeo es obligatorio!", + "poll-interval-min-value" : "El valor del intervalo de sondeo no puede ser menor que 1", + "partitions-required" : "¡Las particiones son obligatorias!", + "partitions-min-value" : "El valor de las particiones no puede ser menor que 1", + "pack-processing-timeout-required" : "El tiempo de procesamiento es obligatorio", + "pack-processing-timeout-min-value" : "El tiempo de procesamiento no puede ser menor que 1", + "batch-size-required" : "¡El tamaño del lote es obligatorio!", + "batch-size-min-value" : "El tamaño del lote no puede ser menor que 1", + "retries-required" : "¡Los reintentos son obligatorios!", + "retries-min-value" : "El número de reintentos no puede ser negativo", + "failure-percentage-required" : "¡El porcentaje de fallos es obligatorio!", + "failure-percentage-min-value" : "El porcentaje de fallos no puede ser menor que 0", + "failure-percentage-max-value" : "El porcentaje de fallos no puede ser mayor que 100", + "pause-between-retries-required" : "¡La pausa entre reintentos es obligatoria!", + "pause-between-retries-min-value" : "La pausa entre reintentos no puede ser menor que 1", + "max-pause-between-retries-required" : "¡La pausa máxima entre reintentos es obligatoria!", + "max-pause-between-retries-min-value" : "La pausa máxima entre reintentos no puede ser menor que 1", + "submit-strategy-type-required" : "¡El tipo de estrategia de envío es obligatorio!", + "processing-strategy-type-required" : "¡El tipo de estrategia de procesamiento es obligatorio!", + "queues" : "Colas", + "selected-queues" : "{ count, plural, =1 {1 cola} other {# colas} } seleccionada(s)", + "delete-queue-title" : "¿Estás seguro de que deseas eliminar la cola '{{queueName}}'?", + "delete-queues-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 cola} other {# colas} }?", + "delete-queue-text" : "Ten cuidado, tras la confirmación la cola y todos los datos relacionados serán irrecuperables.", + "delete-queues-text" : "Tras la confirmación, todas las colas seleccionadas serán eliminadas y dejarán de estar disponibles.", + "search" : "Buscar cola", + "add" : "Agregar cola", + "details" : "Detalles de la cola", + "topic" : "Tópico", + "submit-settings" : "Configuración de envío", + "submit-strategy" : "Tipo de estrategia *", + "grouping-parameter" : "Parámetro de agrupación", + "processing-settings" : "Configuración de reintentos", + "processing-strategy" : "Tipo de procesamiento *", + "retries-settings" : "Configuración de reintentos", + "polling-settings" : "Configuración de sondeo", + "batch-processing" : "Procesamiento por lotes", + "poll-interval" : "Intervalo de sondeo", + "partitions" : "Particiones", + "immediate-processing" : "Procesamiento inmediato", + "consumer-per-partition" : "Enviar mensaje por consumidor", + "consumer-per-partition-hint" : "Habilitar consumidores separados por cada partición", + "duplicate-msg-to-all-partitions" : "Duplicar mensaje en todas las particiones", + "processing-timeout" : "Procesar en, ms", + "batch-size" : "Tamaño del lote", + "retries" : "Número de reintentos (0 – ilimitado)", + "failure-percentage" : "Mensajes fallidos para omitir reintentos, %", + "pause-between-retries" : "Reintentar en, seg", + "max-pause-between-retries" : "Reintento adicional en, seg", + "delete" : "Eliminar cola", + "copyId" : "Copiar ID de la cola", + "idCopiedMessage" : "ID de la cola copiado al portapapeles", + "description" : "Descripción", + "description-hint" : "Este texto se mostrará en la descripción de la cola en lugar de la estrategia seleccionada", + "alt-description" : "Estrategia de envío: {{submitStrategy}}, Estrategia de procesamiento: {{processingStrategy}}", + "custom-properties" : "Propiedades personalizadas", + "custom-properties-hint" : "Propiedades personalizadas para la creación de la cola (tópico), por ejemplo: 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies" : { + "sequential-by-originator-label" : "Secuencial por originador", + "sequential-by-originator-hint" : "No se envía un nuevo mensaje para p.ej. dispositivo A hasta que se confirme el mensaje anterior para ese dispositivo", + "sequential-by-tenant-label" : "Secuencial por inquilino", + "sequential-by-tenant-hint" : "No se envía un nuevo mensaje para p.ej. inquilino A hasta que se confirme el mensaje anterior para ese inquilino", + "sequential-label" : "Secuencial", + "sequential-hint" : "No se envía un nuevo mensaje hasta que el anterior sea confirmado", + "burst-label" : "Explosión (burst)", + "burst-hint" : "Todos los mensajes se envían a las cadenas de reglas en el orden en que llegan", + "batch-label" : "Por lotes", + "batch-hint" : "No se envía un nuevo lote hasta que se confirme el anterior", + "skip-all-failures-label" : "Omitir todos los fallos", + "skip-all-failures-hint" : "Ignorar todos los fallos", + "skip-all-failures-and-timeouts-label" : "Omitir todos los fallos y tiempos de espera", + "skip-all-failures-and-timeouts-hint" : "Ignorar todos los fallos y tiempos de espera", + "retry-all-label" : "Reintentar todos", + "retry-all-hint" : "Reintentar todos los mensajes del paquete de procesamiento", + "retry-failed-label" : "Reintentar fallidos", + "retry-failed-hint" : "Reintentar todos los mensajes fallidos del paquete", + "retry-timeout-label" : "Reintentar tiempos de espera", + "retry-timeout-hint" : "Reintentar todos los mensajes con tiempo de espera del paquete", + "retry-failed-and-timeout-label" : "Reintentar fallidos y con tiempo de espera", + "retry-failed-and-timeout-hint" : "Reintentar todos los mensajes fallidos y con tiempo de espera del paquete" + } + }, + "queue-statistics" : { + "queue-statistics" : "Estadísticas de la cola", + "no-queue-statistics-matching" : "No se encontraron estadísticas de cola que coincidan con '{{entity}}'.", + "queue-statistics-required" : "Las estadísticas de la cola son obligatorias.", + "list-of-queue-statistics" : "{ count, plural, =1 {Una estadística de cola} other {Lista de # estadísticas de cola} }", + "selected-queue-statistics" : "{ count, plural, =1 {1 estadística de cola} other {# estadísticas de cola} } seleccionada(s)", + "no-queue-statistics-text" : "No se encontraron estadísticas de cola", + "queue-statistics-starts-with" : "Estadísticas de cola cuyos nombres comienzan con '{{prefix}}'" + }, + "server-error" : { + "general" : "Error general del servidor", + "authentication" : "Error de autenticación", + "jwt-token-expired" : "Token JWT expirado", + "tenant-trial-expired" : "Prueba del inquilino expirada", + "credentials-expired" : "Credenciales expiradas", + "permission-denied" : "Permiso denegado", + "invalid-arguments" : "Argumentos inválidos", + "bad-request-params" : "Parámetros de solicitud incorrectos", + "item-not-found" : "Elemento no encontrado", + "too-many-requests" : "Demasiadas solicitudes", + "too-many-updates" : "Demasiadas actualizaciones" + }, + "tenant" : { + "tenant" : "Inquilino", + "tenants" : "Inquilinos", + "management" : "Gestión de inquilinos", + "add" : "Agregar inquilino", + "admins" : "Administradores", + "manage-tenant-admins" : "Gestionar administradores del inquilino", + "delete" : "Eliminar inquilino", + "add-tenant-text" : "Agregar nuevo inquilino", + "no-tenants-text" : "No se encontraron inquilinos", + "tenant-details" : "Detalles del inquilino", + "title-max-length" : "El título debe tener menos de 256 caracteres", + "delete-tenant-title" : "¿Estás seguro de que deseas eliminar el inquilino '{{tenantTitle}}'?", + "delete-tenant-text" : "Ten cuidado, después de la confirmación el inquilino y todos los datos relacionados serán irrecuperables.", + "delete-tenants-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 inquilino} other {# inquilinos} }?", + "delete-tenants-action-title" : "Eliminar { count, plural, =1 {1 inquilino} other {# inquilinos} }", + "delete-tenants-text" : "Ten cuidado, después de la confirmación todos los inquilinos seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "title" : "Título", + "title-required" : "El título es obligatorio.", + "description" : "Descripción", + "details" : "Detalles", + "events" : "Eventos", + "copyId" : "Copiar ID del inquilino", + "idCopiedMessage" : "ID del inquilino copiado al portapapeles", + "select-tenant" : "Seleccionar inquilino", + "no-tenants-matching" : "No se encontraron inquilinos que coincidan con '{{entity}}'.", + "tenant-required" : "El inquilino es obligatorio", + "search" : "Buscar inquilinos", + "selected-tenants" : "{ count, plural, =1 {1 inquilino} other {# inquilinos} } seleccionado(s)", + "isolated-tb-rule-engine" : "Usar colas aisladas del motor de reglas de ThingsBoard", + "isolated-tb-rule-engine-details" : "Cada inquilino tendrá colas del motor de reglas dedicadas" + }, + "tenant-profile" : { + "tenant-profile" : "Perfil del inquilino", + "tenant-profiles" : "Perfiles del inquilino", + "add" : "Agregar perfil de inquilino", + "add-profile" : "Agregar perfil", + "debug" : "Depurar", + "edit" : "Editar perfil de inquilino", + "tenant-profile-details" : "Detalles del perfil del inquilino", + "no-tenant-profiles-text" : "No se encontraron perfiles de inquilino", + "name-max-length" : "El nombre debe tener menos de 256 caracteres", + "search" : "Buscar perfiles de inquilino", + "selected-tenant-profiles" : "{ count, plural, =1 {1 perfil de inquilino} other {# perfiles de inquilino} } seleccionado(s)", + "no-tenant-profiles-matching" : "No se encontró ningún perfil de inquilino que coincida con '{{entity}}'.", + "tenant-profile-required" : "El perfil de inquilino es obligatorio", + "idCopiedMessage" : "El ID del perfil de inquilino ha sido copiado al portapapeles", + "set-default" : "Hacer perfil de inquilino predeterminado", + "delete" : "Eliminar perfil de inquilino", + "copyId" : "Copiar ID del perfil de inquilino", + "name" : "Nombre", + "name-required" : "El nombre es obligatorio.", + "data" : "Datos del perfil", + "profile-configuration" : "Configuración del perfil", + "description" : "Descripción", + "default" : "Predeterminado", + "delete-tenant-profile-title" : "¿Estás seguro de que deseas eliminar el perfil de inquilino '{{tenantProfileName}}'?", + "delete-tenant-profile-text" : "Ten cuidado, después de la confirmación el perfil de inquilino y todos los datos relacionados serán irrecuperables.", + "delete-tenant-profiles-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 perfil de inquilino} other {# perfiles de inquilino} }?", + "delete-tenant-profiles-text" : "Ten cuidado, después de la confirmación todos los perfiles de inquilino seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "set-default-tenant-profile-title" : "¿Estás seguro de que deseas hacer predeterminado el perfil de inquilino '{{tenantProfileName}}'?", + "set-default-tenant-profile-text" : "Después de la confirmación, el perfil de inquilino se marcará como predeterminado y se usará para nuevos inquilinos sin un perfil especificado.", + "no-tenant-profiles-found" : "No se encontraron perfiles de inquilino.", + "create-new-tenant-profile" : "¡Crear uno nuevo!", + "create-tenant-profile" : "Crear nuevo perfil de inquilino", + "import" : "Importar perfil de inquilino", + "export" : "Exportar perfil de inquilino", + "export-failed-error" : "No se pudo exportar el perfil de inquilino: {{error}}", + "tenant-profile-file" : "Archivo del perfil de inquilino", + "invalid-tenant-profile-file-error" : "No se pudo importar el perfil de inquilino: estructura de datos inválida.", + "advanced-settings" : "Configuraciones avanzadas", + "entities" : "Entidades", + "rule-engine" : "Motor de reglas", + "time-to-live" : "Tiempo de vida", + "calculated-fields" : "Campos calculados", + "alarms-and-notifications" : "Alarmas y notificaciones", + "ota-files-in-bytes" : "Archivos", + "ws-title" : "WS", + "unlimited" : "(0 - ilimitado)", + "maximum-devices" : "Máximo número de dispositivos", + "maximum-devices-required" : "Se requiere el número máximo de dispositivos.", + "maximum-devices-range" : "El número máximo de dispositivos no puede ser negativo", + "maximum-assets" : "Máximo número de activos", + "maximum-assets-required" : "Se requiere el número máximo de activos.", + "maximum-assets-range" : "El número máximo de activos no puede ser negativo", + "maximum-customers" : "Máximo número de clientes", + "maximum-customers-required" : "Se requiere el número máximo de clientes.", + "maximum-customers-range" : "El número máximo de clientes no puede ser negativo", + "maximum-users" : "Máximo número de usuarios", + "maximum-users-required" : "Se requiere el número máximo de usuarios.", + "maximum-users-range" : "El número máximo de usuarios no puede ser negativo", + "maximum-dashboards" : "Máximo número de tableros", + "maximum-dashboards-required" : "Se requiere el número máximo de tableros.", + "maximum-dashboards-range" : "El número máximo de tableros no puede ser negativo", + "maximum-edges" : "Máximo número de edges", + "maximum-edges-required" : "Se requiere el número máximo de edges.", + "maximum-edges-range" : "El número máximo de edges no puede ser negativo", + "maximum-rule-chains" : "Máximo número de cadenas de reglas", + "maximum-rule-chains-required" : "Se requiere el número máximo de cadenas de reglas.", + "maximum-rule-chains-range" : "El número máximo de cadenas de reglas no puede ser negativo", + "maximum-resources-sum-data-size" : "Tamaño total máximo de archivos de recursos (bytes)", + "maximum-resources-sum-data-size-required" : "Se requiere el tamaño total máximo de archivos de recursos.", + "maximum-resources-sum-data-size-range" : "El tamaño total máximo de archivos de recursos no puede ser negativo", + "maximum-resource-size" : "Tamaño máximo de archivo de recurso (bytes)", + "maximum-resource-size-required" : "Se requiere el tamaño máximo de archivo de recurso", + "maximum-resource-size-range" : "El tamaño máximo de archivo de recurso no puede ser negativo", + "maximum-ota-packages-sum-data-size" : "Tamaño total máximo de archivos OTA (bytes)", + "maximum-ota-package-sum-data-size-required" : "Se requiere el tamaño total máximo de archivos OTA.", + "maximum-ota-package-sum-data-size-range" : "El tamaño total máximo de archivos OTA no puede ser negativo", + "maximum-debug-duration-min" : "Duración máxima de depuración (min)", + "maximum-debug-duration-min-range" : "La duración máxima de depuración no puede ser negativa", + "rest-requests-for-tenant" : "Solicitudes REST para el inquilino", + "transport-tenant-telemetry-msg-rate-limit" : "Mensajes de telemetría del inquilino por transporte", + "transport-tenant-telemetry-data-points-rate-limit" : "Puntos de datos de telemetría del inquilino por transporte", + "transport-device-msg-rate-limit" : "Mensajes del dispositivo por transporte", + "transport-device-telemetry-msg-rate-limit" : "Límite de mensajes de telemetría del dispositivo por transporte", + "transport-device-telemetry-data-points-rate-limit" : "Límite de puntos de datos de telemetría del dispositivo por transporte", + "transport-gateway-msg-rate-limit" : "Límite de mensajes del gateway por transporte", + "transport-gateway-telemetry-msg-rate-limit" : "Límite de mensajes de telemetría del gateway por transporte", + "transport-gateway-telemetry-data-points-rate-limit" : "Límite de puntos de datos de telemetría del gateway por transporte", + "transport-gateway-device-msg-rate-limit" : "Límite de mensajes del dispositivo del gateway por transporte", + "transport-gateway-device-telemetry-msg-rate-limit" : "Límite de mensajes de telemetría del dispositivo del gateway por transporte", + "transport-gateway-device-telemetry-data-points-rate-limit" : "Límite de puntos de datos de telemetría del dispositivo del gateway por transporte", + "tenant-entity-export-rate-limit" : "Límite de creación de versiones de entidad", + "tenant-entity-import-rate-limit" : "Límite de carga de versiones de entidad", + "tenant-notification-request-rate-limit" : "Límite de solicitudes de notificación", + "tenant-notification-requests-per-rule-rate-limit" : "Límite de solicitudes de notificación por regla", + "max-calculated-fields" : "Número máximo de campos calculados por entidad", + "max-calculated-fields-range" : "El número máximo de campos calculados por entidad no puede ser negativo", + "max-calculated-fields-required" : "Se requiere el número máximo de campos calculados por entidad", + "max-data-points-per-rolling-arg" : "Número máximo de puntos de datos en argumentos deslizantes", + "max-data-points-per-rolling-arg-range" : "El número máximo de puntos de datos en argumentos deslizantes no puede ser negativo", + "max-data-points-per-rolling-arg-required" : "Se requiere el número máximo de puntos de datos en argumentos deslizantes", + "max-arguments-per-cf" : "Número máximo de argumentos por campo calculado", + "max-arguments-per-cf-range" : "El número máximo de argumentos por campo calculado no puede ser negativo", + "max-arguments-per-cf-required" : "Se requiere el número máximo de argumentos por campo calculado", + "max-state-size" : "Tamaño máximo del estado en KB", + "max-state-size-range" : "El tamaño máximo del estado en KB no puede ser negativo", + "max-state-size-required" : "Se requiere el tamaño máximo del estado en KB", + "max-value-argument-size" : "Tamaño máximo de argumento de valor único en KB", + "max-value-argument-size-range" : "El tamaño máximo de argumento de valor único en KB no puede ser negativo", + "max-value-argument-size-required" : "Se requiere el tamaño máximo de argumento de valor único en KB", + "max-transport-messages" : "Número máximo de mensajes por transporte", + "max-transport-messages-required" : "Se requiere el número máximo de mensajes por transporte", + "max-transport-messages-range" : "El número máximo de mensajes por transporte no puede ser negativo", + "max-transport-data-points" : "Número máximo de puntos de datos por transporte", + "max-transport-data-points-required" : "Se requiere el número máximo de puntos de datos por transporte", + "max-transport-data-points-range" : "El número máximo de puntos de datos por transporte no puede ser negativo", + "max-r-e-executions" : "Número máximo de ejecuciones del motor de reglas", + "max-r-e-executions-required" : "Se requiere el número máximo de ejecuciones del motor de reglas", + "max-r-e-executions-range" : "El número máximo de ejecuciones del motor de reglas no puede ser negativo", + "max-j-s-executions" : "Número máximo de ejecuciones de JavaScript", + "max-j-s-executions-required" : "Se requiere el número máximo de ejecuciones de JavaScript", + "max-j-s-executions-range" : "El número máximo de ejecuciones de JavaScript no puede ser negativo", + "max-tbel-executions" : "Número máximo de ejecuciones de TBEL", + "max-tbel-executions-required" : "Se requiere el número máximo de ejecuciones de TBEL", + "max-tbel-executions-range" : "El número máximo de ejecuciones de TBEL no puede ser negativo", + "max-d-p-storage-days" : "Número máximo de días de almacenamiento de puntos de datos", + "max-d-p-storage-days-required" : "Se requiere el número máximo de días de almacenamiento de puntos de datos", + "max-d-p-storage-days-range" : "El número máximo de días de almacenamiento de puntos de datos no puede ser negativo", + "default-storage-ttl-days" : "Días TTL de almacenamiento por defecto", + "default-storage-ttl-days-required" : "Se requiere el valor de días TTL de almacenamiento por defecto", + "default-storage-ttl-days-range" : "El valor de días TTL de almacenamiento por defecto no puede ser negativo", + "alarms-ttl-days" : "Días TTL de las alarmas", + "alarms-ttl-days-required" : "Se requiere los días TTL de las alarmas", + "alarms-ttl-days-days-range" : "Los días TTL de las alarmas no pueden ser negativos", + "rpc-ttl-days" : "Días TTL de RPC", + "rpc-ttl-days-required" : "Se requiere los días TTL de RPC", + "rpc-ttl-days-days-range" : "Los días TTL de RPC no pueden ser negativos", + "queue-stats-ttl-days" : "Días TTL de estadísticas de colas", + "queue-stats-ttl-days-required" : "Se requiere los días TTL de estadísticas de colas", + "queue-stats-ttl-days-range" : "Los días TTL de estadísticas de colas no pueden ser negativos", + "rule-engine-exceptions-ttl-days" : "Días TTL de excepciones del motor de reglas", + "rule-engine-exceptions-ttl-days-required" : "Se requiere los días TTL de excepciones del motor de reglas", + "rule-engine-exceptions-ttl-days-range" : "Los días TTL de excepciones del motor de reglas no pueden ser negativos", + "max-rule-node-executions-per-message" : "Número máximo de ejecuciones de nodo de reglas por mensaje", + "max-rule-node-executions-per-message-required" : "Se requiere el número máximo de ejecuciones de nodo de reglas por mensaje", + "max-rule-node-executions-per-message-range" : "El número máximo de ejecuciones de nodo de reglas por mensaje no puede ser negativo", + "max-emails" : "Número máximo de correos electrónicos enviados", + "max-emails-required" : "Se requiere el número máximo de correos electrónicos enviados", + "max-emails-range" : "El número máximo de correos electrónicos enviados no puede ser negativo", + "sms-enabled" : "SMS habilitado", + "max-sms" : "Número máximo de SMS enviados", + "max-sms-required" : "Se requiere el número máximo de SMS enviados", + "max-sms-range" : "El número máximo de SMS enviados no puede ser negativo", + "max-created-alarms" : "Número máximo de alarmas creadas", + "max-created-alarms-required" : "Se requiere el número máximo de alarmas creadas", + "max-created-alarms-range" : "El número máximo de alarmas creadas no puede ser negativo", + "no-queue" : "Ninguna cola configurada", + "add-queue" : "Agregar cola", + "queues-with-count" : "Colas ({{count}})", + "tenant-rest-limits" : "Solicitudes REST para el inquilino", + "customer-rest-limits" : "Solicitudes REST para el cliente", + "incorrect-pattern-for-rate-limits" : "El formato es una lista separada por comas de pares de capacidad y período (en segundos) con dos puntos entre ellos, por ejemplo: 100:1,2000:60", + "too-small-value-zero" : "El valor debe ser mayor que 0", + "too-small-value-one" : "El valor debe ser mayor que 1", + "queue-size-is-limited-by-system-configuration" : "El tamaño de la cola también está limitado por la configuración del sistema.", + "cassandra-tenant-limits-configuration" : "Consulta Cassandra para el inquilino", + "ws-limit-max-sessions-per-tenant" : "Número máximo de sesiones por inquilino", + "ws-limit-max-sessions-per-customer" : "Número máximo de sesiones por cliente", + "ws-limit-max-sessions-per-regular-user" : "Número máximo de sesiones por usuario regular", + "ws-limit-max-sessions-per-public-user" : "Número máximo de sesiones por usuario público", + "ws-limit-queue-per-session" : "Tamaño máximo de la cola de mensajes por sesión", + "ws-limit-max-subscriptions-per-tenant" : "Número máximo de suscripciones por inquilino", + "ws-limit-max-subscriptions-per-customer" : "Número máximo de suscripciones por cliente", + "ws-limit-max-subscriptions-per-regular-user" : "Número máximo de suscripciones por usuario regular", + "ws-limit-max-subscriptions-per-public-user" : "Número máximo de suscripciones por usuario público", + "ws-limit-updates-per-session" : "Actualizaciones WS por sesión", + "rate-limits" : { + "add-limit" : "Agregar límite", + "advanced-settings" : "Configuraciones avanzadas", + "edit-limit" : "Editar límite", + "but-less-than" : "pero menor que", + "calculated-field-debug-event-rate-limit" : "Eventos de depuración de campo calculado", + "edit-calculated-field-debug-event-rate-limit" : "Editar límites de eventos de depuración de campo calculado", + "edit-transport-tenant-msg-title" : "Editar límites de velocidad de mensajes de transporte del inquilino", + "edit-transport-tenant-telemetry-msg-title" : "Editar límites de velocidad de mensajes de telemetría del inquilino", + "edit-transport-tenant-telemetry-data-points-title" : "Editar límites de velocidad de puntos de datos de telemetría del inquilino", + "edit-transport-device-msg-title" : "Editar límites de velocidad de mensajes de transporte del dispositivo", + "edit-transport-device-telemetry-msg-title" : "Editar límites de velocidad de mensajes de telemetría del dispositivo", + "edit-transport-device-telemetry-data-points-title" : "Editar límites de velocidad de puntos de datos de telemetría del dispositivo", + "edit-transport-gateway-msg-title" : "Editar límites de velocidad de mensajes del gateway de transporte", + "edit-transport-gateway-telemetry-msg-title" : "Editar límites de velocidad de mensajes de telemetría del gateway", + "edit-transport-gateway-telemetry-data-points-title" : "Editar límites de velocidad de puntos de datos de telemetría del gateway", + "edit-transport-gateway-device-msg-title" : "Editar límites de velocidad de mensajes del dispositivo del gateway", + "edit-transport-gateway-device-telemetry-msg-title" : "Editar límites de velocidad de mensajes de telemetría del dispositivo del gateway", + "edit-transport-gateway-device-telemetry-data-points-title" : "Editar límites de velocidad de puntos de datos de telemetría del dispositivo del gateway", + "edit-tenant-rest-limits-title" : "Editar límites de solicitudes REST para el inquilino", + "edit-customer-rest-limits-title" : "Editar límites de solicitudes REST para el cliente", + "edit-ws-limit-updates-per-session-title" : "Editar límites de actualizaciones WS por sesión", + "edit-cassandra-tenant-limits-configuration-title" : "Editar límites de consulta de Cassandra para el inquilino", + "edit-tenant-entity-export-rate-limit-title" : "Editar límites de velocidad de creación de versión de entidad", + "edit-tenant-entity-import-rate-limit-title" : "Editar límites de velocidad de carga de versión de entidad", + "edit-tenant-notification-request-rate-limit-title" : "Editar límites de velocidad de solicitudes de notificación", + "edit-tenant-notification-requests-per-rule-rate-limit-title" : "Editar límites de velocidad de solicitudes por regla de notificación", + "edit-edge-events-rate-limit" : "Editar límites de velocidad de eventos del edge", + "edit-edge-events-per-edge-rate-limit" : "Editar límites de eventos por edge", + "edge-events-rate-limit" : "Eventos del edge", + "edge-events-per-edge-rate-limit" : "Eventos por edge", + "edit-edge-uplink-messages-rate-limit" : "Editar límites de velocidad de mensajes de subida del edge", + "edit-edge-uplink-messages-per-edge-rate-limit" : "Editar límites de mensajes de subida por edge", + "edge-uplink-messages-rate-limit" : "Mensajes de subida del edge", + "edge-uplink-messages-per-edge-rate-limit" : "Mensajes de subida por edge", + "messages-per" : "mensajes por", + "not-set" : "No establecido", + "number-of-messages" : "Número de mensajes", + "number-of-messages-required" : "El número de mensajes es obligatorio.", + "number-of-messages-min" : "El valor mínimo es 1.", + "preview" : "Vista previa", + "per-seconds" : "Por segundos", + "per-seconds-required" : "La tasa de tiempo es obligatoria.", + "per-seconds-min" : "El valor mínimo es 1.", + "rate-limits" : "Límites de velocidad", + "remove-limit" : "Eliminar límite", + "transport-tenant-msg" : "Mensajes de transporte del inquilino", + "transport-tenant-telemetry-msg" : "Mensajes de telemetría del inquilino", + "transport-tenant-telemetry-data-points" : "Puntos de datos de telemetría del inquilino", + "transport-device-msg" : "Mensajes de transporte del dispositivo", + "transport-device-telemetry-msg" : "Mensajes de telemetría del dispositivo", + "transport-device-telemetry-data-points" : "Puntos de datos de telemetría del dispositivo", + "transport-gateway-msg" : "Mensajes de gateway de transporte", + "transport-gateway-telemetry-msg" : "Mensajes de telemetría del gateway", + "transport-gateway-telemetry-data-points" : "Puntos de datos de telemetría del gateway", + "transport-gateway-device-msg" : "Mensajes del dispositivo del gateway", + "transport-gateway-device-telemetry-msg" : "Mensajes de telemetría del dispositivo del gateway", + "transport-gateway-device-telemetry-data-points" : "Puntos de datos de telemetría del dispositivo del gateway", + "sec" : "seg" + } + }, + "timeinterval" : { + "seconds-interval" : "{ seconds, plural, =1 {1 segundo} other {# segundos} }", + "minutes-interval" : "{ minutes, plural, =1 {1 minuto} other {# minutos} }", + "hours-interval" : "{ hours, plural, =1 {1 hora} other {# horas} }", + "days-interval" : "{ days, plural, =1 {1 día} other {# días} }", + "days" : "Días", + "hours" : "Horas", + "minutes" : "Minutos", + "seconds" : "Segundos", + "advanced" : "Avanzado", + "custom" : "Personalizado", + "predefined" : { + "yesterday" : "Ayer", + "day-before-yesterday" : "Anteayer", + "this-day-last-week" : "Este día la semana pasada", + "previous-week" : "Semana anterior (Dom - Sáb)", + "previous-week-iso" : "Semana anterior (Lun - Dom)", + "previous-month" : "Mes anterior", + "previous-quarter" : "Trimestre anterior", + "previous-half-year" : "Semestre anterior", + "previous-year" : "Año anterior", + "current-hour" : "Hora actual", + "current-day" : "Día actual", + "current-day-so-far" : "Día actual hasta ahora", + "current-week" : "Semana actual (Dom - Sáb)", + "current-week-iso" : "Semana actual (Lun - Dom)", + "current-week-so-far" : "Semana actual hasta ahora (Dom - Sáb)", + "current-week-iso-so-far" : "Semana actual hasta ahora (Lun - Dom)", + "current-month" : "Mes actual", + "current-month-so-far" : "Mes actual hasta ahora", + "current-quarter" : "Trimestre actual", + "current-quarter-so-far" : "Trimestre actual hasta ahora", + "current-half-year" : "Semestre actual", + "current-half-year-so-far" : "Semestre actual hasta ahora", + "current-year" : "Año actual", + "current-year-so-far" : "Año actual hasta ahora" }, - "js-func": { - "no-return-error": "La función debe retornar un valor!", - "return-type-mismatch": "La función debe retornar un valor de tipo: '{{type}}'!", - "tidy": "Tidy", - "mini": "Mini" + "type" : { + "week" : "Semana (Dom - Sáb)", + "week-iso" : "Semana (Lun - Dom)", + "month" : "Mes", + "quarter" : "Trimestre" + } + }, + "timeunit" : { + "milliseconds" : "Milisegundos", + "seconds" : "Segundos", + "minutes" : "Minutos", + "hours" : "Horas", + "days" : "Días" + }, + "timewindow" : { + "timewindow" : "Ventana de tiempo", + "timewindow-settings" : "Configuraciones de ventana de tiempo", + "years" : "{ years, plural, =1 { año } other {# años } }", + "years-short" : "{{ years }}a", + "months" : "{ months, plural, =1 { mes } other {# meses } }", + "months-short" : "{{ months }}M", + "weeks" : "{ weeks, plural, =1 { semana } other {# semanas } }", + "weeks-short" : "{{ weeks }}s", + "days" : "{ days, plural, =1 { día } other {# días } }", + "days-short" : "{{ days }}d", + "hours" : "{ hours, plural, =0 { hora } =1 {1 hora } other {# horas } }", + "hr" : "{{ hr }} h", + "hr-short" : "{{ hr }}h", + "minutes" : "{ minutes, plural, =0 { minuto } =1 {1 minuto } other {# minutos } }", + "min" : "{{ min }} min", + "min-short" : "{{ min }}m", + "seconds" : "{ seconds, plural, =0 { segundo } =1 {1 segundo } other {# segundos } }", + "sec" : "{{ sec }} seg", + "sec-short" : "{{ sec }}s", + "short" : { + "years" : "{ years, plural, =1 {1 año } other {# años } }", + "days" : "{ days, plural, =1 {1 día } other {# días } }", + "hours" : "{ hours, plural, =1 {1 hora } other {# horas } }", + "minutes" : "{{minutes}} min", + "seconds" : "{{seconds}} seg" }, - "key-val": { - "key": "Clave", - "value": "Valor", - "remove-entry": "Borrar entrada", - "add-entry": "Añadir entrada", - "no-data": "Sin datos" + "realtime" : "Tiempo real", + "history" : "Historial", + "last-prefix" : "últimos", + "period" : "desde {{ startTime }} hasta {{ endTime }}", + "edit" : "Editar ventana de tiempo", + "date-range" : "Rango de fechas", + "for-all-time" : "Para todo el tiempo", + "last" : "Últimos", + "time-period" : "Período de tiempo", + "hide" : "Ocultar", + "interval" : "Intervalo", + "just-now" : "Justo ahora", + "just-now-lower" : "justo ahora", + "ago" : "atrás", + "style" : "Estilo de la ventana de tiempo", + "icon" : "Ícono", + "icon-position" : "Posición del ícono", + "icon-position-left" : "Izquierda", + "icon-position-right" : "Derecha", + "font" : "Fuente", + "color" : "Color", + "displayTypePrefix" : "Mostrar prefijo Tiempo real/Historial", + "preview" : "Vista previa", + "relative" : "Relativo", + "range" : "Rango", + "hide-timewindow-section" : "Ocultar la sección de ventana de tiempo para los usuarios finales", + "hide-last-interval" : "Ocultar el último intervalo para los usuarios finales", + "hide-relative-interval" : "Ocultar el intervalo relativo para los usuarios finales", + "hide-fixed-interval" : "Ocultar el intervalo fijo para los usuarios finales", + "hide-aggregation" : "Ocultar agregación para los usuarios finales", + "hide-group-interval" : "Ocultar intervalo de agrupación para los usuarios finales", + "hide-max-values" : "Ocultar valores máximos para los usuarios finales", + "hide-timezone" : "Ocultar zona horaria para los usuarios finales", + "disable-custom-interval" : "Desactivar selección de intervalo personalizado", + "edit-aggregation-functions-list" : "Editar lista de funciones de agregación", + "edit-aggregation-functions-list-hint" : "Se puede especificar una lista de opciones disponibles.", + "allowed-aggregation-functions" : "Funciones de agregación permitidas", + "edit-intervals-list" : "Editar lista de intervalos", + "allowed-agg-intervals" : "Intervalos de agrupación", + "default-agg-interval" : "Intervalo de agrupación predeterminado", + "edit-intervals-list-hint" : "Se puede especificar una lista de opciones de intervalo disponibles.", + "edit-grouping-intervals-list-hint" : "Es posible configurar la lista de intervalos de agrupación y el intervalo de agrupación predeterminado.", + "all" : "Todos" + }, + "tooltip" : { + "trigger" : "Disparador", + "trigger-point" : "Punto", + "trigger-axis" : "Eje", + "label" : "Etiqueta", + "value" : "Valor", + "date" : "Fecha", + "show-date-time-interval" : "Mostrar intervalo de fecha y hora", + "show-date-time-interval-hint" : "Mostrar intervalo de fecha y hora de acuerdo con la agregación de datos.", + "background-color" : "Color de fondo", + "background-blur" : "Desenfoque de fondo" + }, + "unit" : { + "millimeter" : "Milímetro", + "centimeter" : "Centímetro", + "angstrom" : "Ångström", + "nanometer" : "Nanómetro", + "micrometer" : "Micrómetro", + "meter" : "Metro", + "kilometer" : "Kilómetro", + "inch" : "Pulgada", + "foot" : "Pie", + "yard" : "Yarda", + "mile" : "Milla", + "nautical-mile" : "Milla náutica", + "astronomical-unit" : "Unidad astronómica", + "reciprocal-metre" : "Metro recíproco", + "meter-per-meter" : "Metro por metro", + "steradian" : "Estereorradián", + "thou" : "Milésima de pulgada", + "barleycorn" : "Barleycorn", + "hand" : "Mano", + "chain" : "Cadena", + "furlong" : "Furlong", + "league" : "Legua", + "fathom" : "Braza", + "cable" : "Cable", + "link" : "Eslabón", + "rod" : "Vara", + "nanogram" : "Nanogramo", + "microgram" : "Microgramo", + "milligram" : "Miligramo", + "gram" : "Gramo", + "kilogram" : "Kilogramo", + "tonne" : "Tonelada", + "ounce" : "Onza", + "pound" : "Libra", + "stone" : "Stone", + "hundredweight-count" : "Centena de peso (hundredweight)", + "short-tons" : "Toneladas cortas", + "dalton" : "Dalton", + "grain" : "Grano", + "drachm" : "Dracma", + "quarter" : "Cuarto", + "slug" : "Slug", + "carat" : "Quilate", + "cubic-millimeter" : "Milímetro cúbico", + "cubic-centimeter" : "Centímetro cúbico", + "cubic-meter" : "Metro cúbico", + "cubic-kilometer" : "Kilómetro cúbico", + "microliter" : "Microlitro", + "milliliter" : "Mililitro", + "liter" : "Litro", + "hectoliter" : "Hectolitro", + "cubic-inch" : "Pulgada cúbica", + "cubic-foot" : "Pie cúbico", + "cubic-yard" : "Yarda cúbica", + "fluid-ounce" : "Onza líquida", + "pint" : "Pinta", + "quart" : "Cuarto", + "gallon" : "Galón", + "oil-barrels" : "Barril de petróleo", + "cubic-meter-per-kilogram" : "Metro cúbico por kilogramo", + "gill" : "Gill", + "hogshead" : "Hogshead", + "teaspoon" : "Cucharadita", + "tablespoon" : "Cucharada", + "cup" : "Taza", + "celsius" : "Celsius", + "kelvin" : "Kelvin", + "rankine" : "Rankine", + "fahrenheit" : "Fahrenheit", + "percent" : "Porcentaje", + "meter-per-second" : "Metro por segundo", + "kilometer-per-hour" : "Kilómetro por hora", + "foot-per-second" : "Pie por segundo", + "mile-per-hour" : "Milla por hora", + "knot" : "Nudo", + "millimeters-per-minute" : "Milímetros por minuto", + "kilometer-per-hour-squared" : "Kilómetro por hora al cuadrado", + "foot-per-second-squared" : "Pie por segundo al cuadrado", + "pascal" : "Pascal", + "kilopascal" : "Kilopascal", + "megapascal" : "Megapascal", + "gigapascal" : "Gigapascal", + "millibar" : "Milibar", + "bar" : "Bar", + "kilobar" : "Kilobar", + "newton" : "Newton", + "newton-meter" : "Newton metro", + "foot-pounds" : "Pies-libra", + "inch-pounds" : "Pulgadas-libra", + "newton-per-meter" : "Newton por metro", + "atmospheres" : "Atmósferas", + "pounds-per-square-inch" : "Libras por pulgada cuadrada", + "torr" : "Torr", + "inches-of-mercury" : "Pulgadas de mercurio", + "pascal-per-square-meter" : "Pascal por metro cuadrado", + "pound-per-square-inch" : "Libra por pulgada cuadrada", + "newton-per-square-meter" : "Newton por metro cuadrado", + "kilogram-force-per-square-meter" : "Kilogramo-fuerza por metro cuadrado", + "pascal-per-square-centimeter" : "Pascal por centímetro cuadrado", + "ton-force-per-square-inch" : "Tonelada-fuerza por pulgada cuadrada", + "kilonewton-per-square-meter" : "Kilonewton por metro cuadrado", + "newton-per-square-millimeter" : "Newton por milímetro cuadrado", + "microjoule" : "Microjulio", + "millijoule" : "Milijulio", + "joule" : "Julio", + "kilojoule" : "Kilojulio", + "megajoule" : "Megajulio", + "gigajoule" : "Gigajulio", + "watt-hour" : "Vatio-hora", + "kilowatt-hour" : "Kilovatio-hora", + "electron-volts" : "Electrón-voltios", + "joules-per-coulomb" : "Julios por culombio", + "british-thermal-unit" : "Unidad térmica británica", + "foot-pound" : "Pie-libra", + "calorie" : "Caloría", + "small-calorie" : "Caloría pequeña", + "kilocalorie" : "Kilocaloría", + "joule-per-kelvin" : "Julio por kelvin", + "joule-per-kilogram-kelvin" : "Julio por kilogramo-kelvin", + "joule-per-kilogram" : "Julio por kilogramo", + "watt-per-meter-kelvin" : "Vatio por metro-kelvin", + "joule-per-cubic-meter" : "Julio por metro cúbico", + "therm" : "Termia", + "electric-dipole-moment" : "Momento dipolar eléctrico", + "magnetic-dipole-moment" : "Momento dipolar magnético", + "debye" : "Debye", + "coulomb-per-square-meter-per-volt" : "Culombio por metro cuadrado por voltio", + "milliwatt" : "Miliwatio", + "microwatt" : "Microwatio", + "watt" : "Vatio", + "kilowatt" : "Kilovatio", + "megawatt" : "Megavatio", + "gigawatt" : "Gigavatio", + "metric-horsepower" : "Caballo de fuerza métrico", + "milliwatt-per-square-centimeter" : "Miliwatios por centímetro cuadrado", + "watt-per-square-centimeter" : "Vatios por centímetro cuadrado", + "kilowatt-per-square-centimeter" : "Kilovatios por centímetro cuadrado", + "milliwatt-per-square-meter" : "Miliwatios por metro cuadrado", + "watt-per-square-meter" : "Vatios por metro cuadrado", + "kilowatt-per-square-meter" : "Kilovatios por metro cuadrado", + "watt-per-square-inch" : "Vatios por pulgada cuadrada", + "kilowatt-per-square-inch" : "Kilovatios por pulgada cuadrada", + "horsepower" : "Caballo de fuerza", + "btu-per-hour" : "Unidades térmicas británicas por hora", + "coulomb" : "Culombio", + "millicoulomb" : "Miliculombios", + "microcoulomb" : "Microculombio", + "picocoulomb" : "Picoculombio", + "coulomb-per-meter" : "Culombio por metro", + "coulomb-per-cubic-meter" : "Culombio por metro cúbico", + "coulomb-per-square-meter" : "Culombio por metro cuadrado", + "square-millimeter" : "Milímetro cuadrado", + "square-centimeter" : "Centímetro cuadrado", + "square-meter" : "Metro cuadrado", + "hectare" : "Hectárea", + "square-kilometer" : "Kilómetro cuadrado", + "square-inch" : "Pulgada cuadrada", + "square-foot" : "Pie cuadrado", + "square-yard" : "Yarda cuadrada", + "acre" : "Acre", + "square-mile" : "Milla cuadrada", + "are" : "Área", + "barn" : "Barn", + "circular-inch" : "Pulgada circular", + "milliampere-hour" : "Miliamperio-hora", + "ampere-hours" : "Amperios-hora", + "kiloampere-hours" : "Kiloamperios-hora", + "nanoampere" : "Nanoamperio", + "picoampere" : "Picoamperio", + "microampere" : "Microamperio", + "milliampere" : "Miliamperio", + "ampere" : "Amperio", + "microampere-per-square-centimeter" : "Microamperio por centímetro cuadrado", + "ampere-per-square-meter" : "Amperio por metro cuadrado", + "ampere-per-meter" : "Amperio por metro", + "oersted" : "Oersted", + "bohr-magneton" : "Magnetón de Bohr", + "ampere-meter-squared" : "Amperio-metro cuadrado", + "nanovolt" : "Nanovoltio", + "picovolt" : "Picovoltio", + "volt" : "Voltio", + "dbmV" : "dBmV", + "dbm" : "dBm", + "volt-meter" : "Voltímetro", + "kilovolt-meter" : "Kilovoltímetro", + "megavolt-meter" : "Megavoltímetro", + "microvolt-meter" : "Microvoltímetro", + "millivolt-meter" : "Milivoltímetro", + "nanovolt-meter" : "Nanovoltímetro", + "ohm" : "Ohmio", + "microohm" : "Microohmio", + "milliohm" : "Miliohmio", + "kilohm" : "Kiloohmio", + "megohm" : "Megaohmio", + "gigohm" : "Gigaohmio", + "hertz" : "Hercio", + "kilohertz" : "Kilohertz", + "megahertz" : "Megahertz", + "gigahertz" : "Gigahertz", + "rpm" : "Revoluciones por minuto", + "candela-per-square-meter" : "Candela por metro cuadrado", + "candela" : "Candela", + "lumen" : "Lumen", + "lux" : "Lux", + "foot-candle" : "Pie-candela", + "lumen-per-square-meter" : "Lumen por metro cuadrado", + "lux-second" : "Lux segundo", + "lumen-second" : "Lumen segundo", + "lumens-per-watt" : "Lúmenes por vatio", + "mole" : "Mol", + "nanomole" : "Nanomol", + "micromole" : "Micromol", + "millimole" : "Milimol", + "kilomole" : "Kilomol", + "mole-per-cubic-meter" : "Mol por metro cúbico", + "rssi" : "RSSI", + "ppm" : "Partes por millón", + "ppb" : "Partes por mil millones", + "micrograms-per-cubic-meter" : "Microgramos por metro cúbico", + "aqi" : "Índice de calidad del aire (AQI)", + "gram-per-cubic-meter" : "Gramo por metro cúbico", + "gram-per-kilogram" : "Humedad específica", + "millimeters-per-second" : "Milímetros por segundo", + "neper" : "Neper", + "bel" : "Bel", + "decibel" : "Decibelio", + "meters-per-second-squared" : "Metros por segundo al cuadrado", + "becquerel" : "Becquerel", + "curie" : "Curie", + "gray" : "Gray", + "sievert" : "Sievert", + "roentgen" : "Roentgen", + "cps" : "Cuentas por segundo", + "rad" : "Rad", + "rem" : "Rem", + "dps" : "Desintegraciones por segundo", + "rutherford" : "Rutherford", + "coulombs-per-kilogram" : "Culombios por kilogramo", + "becquerels-per-cubic-meter" : "Becquereles por metro cúbico", + "curies-per-liter" : "Curies por litro", + "becquerels-per-second" : "Becquereles por segundo", + "curies-per-second" : "Curies por segundo", + "gy-per-second" : "Gray por segundo", + "watt-per-steradian" : "Vatio por estereorradián", + "watt-per-square-metre-steradian" : "Vatio por metro cuadrado-estereorradián", + "ph-level" : "Nivel de pH", + "turbidity" : "Turbidez", + "mg-per-liter" : "Miligramos por litro", + "microsiemens-per-centimeter" : "Microsiemens por centímetro", + "millisiemens-per-meter" : "Milisiemens por metro", + "siemens-per-meter" : "Siemens por metro", + "kilogram-per-cubic-meter" : "Kilogramo por metro cúbico", + "gram-per-cubic-centimeter" : "Gramo por centímetro cúbico", + "kilogram-per-square-meter" : "Kilogramo por metro cuadrado", + "milligram-per-milliliter" : "Miligramo por mililitro", + "milligram-per-cubic-meter" : "Miligramo por metro cúbico", + "pound-per-cubic-foot" : "Libra por pie cúbico", + "ounces-per-cubic-inch" : "Onzas por pulgada cúbica", + "tons-per-cubic-yard" : "Toneladas por yarda cúbica", + "particle-density" : "Densidad de partículas", + "kilometers-per-liter" : "Kilómetros por litro", + "miles-per-gallon" : "Millas por galón", + "liters-per-100-km" : "Litros por 100 km", + "gallons-per-mile" : "Galones por milla", + "liters-per-hour" : "Litros por hora", + "gallons-per-hour" : "Galones por hora", + "beats-per-minute" : "Pulsaciones por minuto", + "millimeters-of-mercury" : "Milímetros de mercurio", + "milligrams-per-deciliter" : "Miligramos por decilitro", + "g-force" : "Fuerza G", + "kilonewton" : "Kilonewton", + "kilogram-force" : "Kilogramo-fuerza", + "pound-force" : "Libra-fuerza", + "kilopound-force" : "Kilolibra-fuerza", + "dyne" : "Dina", + "poundal" : "Poundal", + "kip" : "Kip", + "gal" : "Gal", + "gravity" : "Gravedad", + "hectopascal" : "Hectopascal", + "atmosphere" : "Atmósfera", + "millibars" : "Milibares", + "inch-of-mercury" : "Pulgada de mercurio", + "richter-scale" : "Escala de Richter", + "second" : "Segundo", + "minute" : "Minuto", + "hour" : "Hora", + "day" : "Día", + "week" : "Semana", + "month" : "Mes", + "year" : "Año", + "cubic-foot-per-minute" : "Pie cúbico por minuto", + "cubic-meters-per-hour" : "Metros cúbicos por hora", + "cubic-meters-per-second" : "Metros cúbicos por segundo", + "liter-per-second" : "Litro por segundo", + "liter-per-minute" : "Litro por minuto", + "gallons-per-minute" : "Galones por minuto", + "cubic-foot-per-second" : "Pie cúbico por segundo", + "milliliters-per-minute" : "Mililitros por minuto", + "bit" : "Bit", + "byte" : "Byte", + "kilobyte" : "Kilobyte", + "megabyte" : "Megabyte", + "gigabyte" : "Gigabyte", + "terabyte" : "Terabyte", + "petabyte" : "Petabyte", + "exabyte" : "Exabyte", + "zettabyte" : "Zettabyte", + "yottabyte" : "Yottabyte", + "bit-per-second" : "Bit por segundo", + "kilobit-per-second" : "Kilobit por segundo", + "megabit-per-second" : "Megabit por segundo", + "gigabit-per-second" : "Gigabit por segundo", + "terabit-per-second" : "Terabit por segundo", + "byte-per-second" : "Byte por segundo", + "kilobyte-per-second" : "Kilobyte por segundo", + "megabyte-per-second" : "Megabyte por segundo", + "gigabyte-per-second" : "Gigabyte por segundo", + "degree" : "Grado", + "radian" : "Radián", + "gradian" : "Gradián", + "revolution" : "Revolución", + "siemens" : "Siemens", + "millisiemens" : "Milisiemens", + "microsiemens" : "Microsiemens", + "kilosiemens" : "Kilosiemens", + "megasiemens" : "Megasiemens", + "gigasiemens" : "Gigasiemens", + "farad" : "Faradio", + "millifarad" : "Milifaradio", + "microfarad" : "Microfaradio", + "nanofarad" : "Nanofaradio", + "picofarad" : "Picofaradio", + "kilofarad" : "Kilofaradio", + "megafarad" : "Megafaradio", + "gigafarad" : "Gigafaradio", + "terfarad" : "Terafaradio", + "farad-per-meter" : "Faradio por metro", + "tesla" : "Tesla", + "gauss" : "Gauss", + "kilogauss" : "Kilogauss", + "millitesla" : "Militesla", + "microtesla" : "Microtesla", + "nanotesla" : "Nanotesla", + "kilotesla" : "Kilotesla", + "megatesla" : "Megatesla", + "millitesla-square-meters" : "Militesla por metro cuadrado", + "gamma" : "Gamma", + "lambda" : "Lambda", + "square-meter-per-second" : "Metro cuadrado por segundo", + "square-centimeter-per-second" : "Centímetro cuadrado por segundo", + "stoke" : "Stoke", + "centistokes" : "Centistokes", + "square-foot-per-second" : "Pie cuadrado por segundo", + "square-inch-per-second" : "Pulgada cuadrada por segundo", + "pascal-second" : "Pascal-segundo", + "centipoise" : "Centipoise", + "poise" : "Poise", + "reynolds" : "Reynolds", + "pound-per-foot-hour" : "Libra por pie-hora", + "newton-second-per-square-meter" : "Newton segundo por metro cuadrado", + "dyne-second-per-square-centimeter" : "Dina segundo por centímetro cuadrado", + "kilogram-per-meter-second" : "Kilogramo por metro-segundo", + "tesla-square-meters" : "Tesla por metro cuadrado", + "maxwell" : "Maxwell", + "tesla-per-meter" : "Tesla por metro", + "gauss-per-centimeter" : "Gauss por centímetro", + "weber" : "Weber", + "microweber" : "Microweber", + "milliweber" : "Milliweber", + "gauss-square-centimeter" : "Gauss por centímetro cuadrado", + "kilogauss-square-centimeter" : "Kilogauss por centímetro cuadrado", + "henry" : "Henry", + "millihenry" : "Milihenry", + "microhenry" : "Microhenry", + "nanohenry" : "Nanohenry", + "henry-per-meter" : "Henry por metro", + "tesla-meter-per-ampere" : "Tesla metro por amperio", + "gauss-per-oersted" : "Gauss por Oersted", + "kilogram-per-mole" : "Kilogramo por mol", + "gram-per-mole" : "Gramo por mol", + "milligram-per-mole" : "Miligramo por mol", + "joule-per-mole" : "Julio por mol", + "joule-per-mole-kelvin" : "Julio por mol-kelvin", + "millivolts-per-meter" : "Milivoltios por metro", + "volts-per-meter" : "Voltios por metro", + "kilovolts-per-meter" : "Kilovoltios por metro", + "radian-per-second" : "Radián por segundo", + "radian-per-second-squared" : "Radián por segundo cuadrado", + "revolutions-per-minute-per-second" : "Aceleración angular", + "deg-per-second" : "grados/segundo", + "degrees-brix" : "Grados Brix", + "katal" : "Katal", + "katal-per-cubic-metre" : "Katal por metro cúbico" + }, + "user" : { + "user" : "Usuario", + "users" : "Usuarios", + "customer-users" : "Usuarios del cliente", + "tenant-admins" : "Administradores del inquilino", + "sys-admin" : "Administrador del sistema", + "tenant-admin" : "Administrador del inquilino", + "customer" : "Cliente", + "anonymous" : "Anónimo", + "add" : "Agregar usuario", + "delete" : "Eliminar usuario", + "add-user-text" : "Agregar nuevo usuario", + "no-users-text" : "No se encontraron usuarios", + "user-details" : "Detalles del usuario", + "delete-user-title" : "¿Está seguro de que desea eliminar al usuario '{{userEmail}}'?", + "delete-user-text" : "Tenga cuidado, después de la confirmación el usuario y todos los datos relacionados serán irrecuperables.", + "delete-users-title" : "¿Está seguro de que desea eliminar { count, plural, =1 {1 usuario} other {# usuarios} }?", + "delete-users-action-title" : "Eliminar { count, plural, =1 {1 usuario} other {# usuarios} }", + "delete-users-text" : "Tenga cuidado, después de la confirmación todos los usuarios seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "activation-email-sent-message" : "¡Correo de activación enviado con éxito!", + "resend-activation" : "Reenviar activación", + "email" : "Correo electrónico", + "email-required" : "El correo electrónico es obligatorio.", + "invalid-email-format" : "Formato de correo electrónico no válido.", + "first-name" : "Nombre", + "last-name" : "Apellido", + "description" : "Descripción", + "default-dashboard" : "Tablero predeterminado", + "always-fullscreen" : "Siempre pantalla completa", + "select-user" : "Seleccionar usuario", + "no-users-matching" : "No se encontraron usuarios que coincidan con '{{entity}}'.", + "user-required" : "Se requiere un usuario", + "activation-method" : "Método de activación", + "display-activation-link" : "Mostrar enlace de activación", + "send-activation-mail" : "Enviar correo de activación", + "activation-link" : "Enlace de activación del usuario", + "activation-link-text" : "Para activar el usuario use el siguiente enlace de activación (expira en {{activationLinkTtl}}) :", + "copy-activation-link" : "Copiar enlace de activación", + "activation-link-copied-message" : "El enlace de activación del usuario ha sido copiado al portapapeles", + "details" : "Detalles", + "login-as-tenant-admin" : "Iniciar sesión como administrador del inquilino", + "login-as-customer-user" : "Iniciar sesión como usuario del cliente", + "search" : "Buscar usuarios", + "selected-users" : "{ count, plural, =1 {1 usuario} other {# usuarios} } seleccionados", + "disable-account" : "Desactivar cuenta de usuario", + "enable-account" : "Activar cuenta de usuario", + "enable-account-message" : "¡La cuenta de usuario ha sido activada con éxito!", + "disable-account-message" : "¡La cuenta de usuario ha sido desactivada con éxito!", + "copyId" : "Copiar ID del usuario", + "idCopiedMessage" : "El ID del usuario ha sido copiado al portapapeles", + "user-list" : "Lista de usuarios", + "user-list-required" : "Se requiere la lista de usuarios" + }, + "value" : { + "type" : "Tipo de valor", + "string" : "Cadena", + "string-value" : "Valor de cadena", + "string-value-required" : "Se requiere un valor de cadena", + "integer" : "Entero", + "integer-value" : "Valor entero", + "integer-value-required" : "Se requiere un valor entero", + "invalid-integer-value" : "Valor entero no válido", + "double" : "Doble", + "double-value" : "Valor doble", + "double-value-required" : "Se requiere un valor doble", + "boolean" : "Booleano", + "boolean-value" : "Valor booleano", + "false" : "Falso", + "true" : "Verdadero", + "long" : "Long", + "json" : "JSON", + "json-value" : "Valor JSON", + "json-value-invalid" : "El valor JSON tiene un formato no válido", + "json-value-required" : "Se requiere un valor JSON." + }, + "version-control" : { + "version-control" : "Control de versiones", + "management" : "Gestión de control de versiones", + "search" : "Buscar versiones", + "branch" : "Rama", + "default" : "Predeterminado", + "select-branch" : "Seleccionar rama", + "branch-required" : "Se requiere una rama", + "create-entity-version" : "Crear versión de entidad", + "version-name" : "Nombre de la versión", + "version-name-required" : "Se requiere el nombre de la versión", + "author" : "Autor", + "export-relations" : "Exportar relaciones", + "export-attributes" : "Exportar atributos", + "export-credentials" : "Exportar credenciales", + "export-calculated-fields" : "Exportar campos calculados", + "entity-versions" : "Versiones de entidad", + "versions" : "Versiones", + "created-time" : "Fecha de creación", + "version-id" : "ID de versión", + "no-entity-versions-text" : "No se encontraron versiones de entidad", + "no-versions-text" : "No se encontraron versiones", + "copy-full-version-id" : "Copiar ID de versión completo", + "create-version" : "Crear versión", + "creating-version" : "Creando versión... Por favor espera", + "nothing-to-commit" : "No hay cambios para confirmar", + "restore-version" : "Restaurar versión", + "restore-entity-from-version" : "Restaurar entidad desde versión '{{versionName}}'", + "restoring-entity-version" : "Restaurando versión de entidad... Por favor espera", + "load-relations" : "Cargar relaciones", + "load-attributes" : "Cargar atributos", + "load-credentials" : "Cargar credenciales", + "load-calculated-fields" : "Cargar campos calculados", + "compare-with-current" : "Comparar con actual", + "diff-entity-with-version" : "Diferencias con versión de entidad '{{versionName}}'", + "previous-difference" : "Diferencia anterior", + "next-difference" : "Siguiente diferencia", + "current" : "Actual", + "differences" : "{ count, plural, =1 {1 diferencia} other {# diferencias} }", + "create-entities-version" : "Crear versión de entidades", + "default-sync-strategy" : "Estrategia de sincronización predeterminada", + "sync-strategy-merge" : "Combinar", + "sync-strategy-overwrite" : "Sobrescribir", + "entities-to-export" : "Entidades a exportar", + "entities-to-restore" : "Entidades a restaurar", + "sync-strategy" : "Estrategia de sincronización", + "all-entities" : "Todas las entidades", + "no-entities-to-export-prompt" : "Por favor especifica las entidades a exportar", + "no-entities-to-restore-prompt" : "Por favor especifica las entidades a restaurar", + "add-entity-type" : "Agregar tipo de entidad", + "remove-all" : "Eliminar todo", + "version-create-result" : "{ added, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } añadida.
{ modified, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } modificada.
{ removed, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } eliminada.", + "remove-other-entities" : "Eliminar otras entidades", + "find-existing-entity-by-name" : "Buscar entidad existente por nombre", + "restore-entities-from-version" : "Restaurar entidades desde la versión '{{versionName}}'", + "restoring-entities-from-version" : "Restaurando entidades... Por favor espera", + "no-entities-restored" : "No se restauraron entidades", + "created" : "{{created}} creada(s)", + "updated" : "{{updated}} actualizada(s)", + "deleted" : "{{deleted}} eliminada(s)", + "remove-other-entities-confirm-text" : "¡Cuidado! Esto eliminará permanentemente todas las entidades actuales
que no estén presentes en la versión que deseas restaurar.

Escribe \"remove other entities\" para confirmar.", + "auto-commit-to-branch" : "auto-confirmar en la rama {{ branch }}", + "default-create-entity-version-name" : "Actualización de {{entityName}}", + "sync-strategy-merge-hint" : "Crea o actualiza las entidades seleccionadas en el repositorio. Todas las demás entidades del repositorio no se modifican.", + "sync-strategy-overwrite-hint" : "Crea o actualiza las entidades seleccionadas en el repositorio. Todas las demás entidades del repositorio se eliminan.", + "device-credentials-conflict" : "No se pudo cargar el dispositivo con ID externo {{entityId}}
porque las mismas credenciales ya están presentes en la base de datos para otro dispositivo.
Considera desactivar la opción cargar credenciales en el formulario de restauración.", + "missing-referenced-entity" : "No se pudo cargar el {{sourceEntityTypeName}} con ID externo {{sourceEntityId}}
porque hace referencia a un {{targetEntityTypeName}} faltante con ID {{targetEntityId}}.", + "runtime-failed" : "Error: {{message}}", + "auto-commit-settings-read-only-hint" : "La función de auto-confirmación no funciona con la opción de solo lectura habilitada en la configuración del repositorio.", + "rollback-on-error" : "Reversión en caso de error", + "rollback-on-error-hint" : "Si tienes una gran cantidad de entidades para restaurar, considera desactivar esta opción para aumentar el rendimiento.\n Nota: si ocurre un error durante la carga de la versión, las entidades ya persistidas (con relaciones, atributos, etc.) permanecerán como están." + }, + "widget" : { + "widget-library" : "Biblioteca de widgets", + "widget-bundle" : "Paquete de widgets", + "all-bundles" : "Todos los paquetes", + "select-widgets-bundle" : "Seleccionar paquete de widgets", + "widgets" : "Widgets", + "all-widgets" : "Todos los widgets", + "widget" : "Widget", + "select-widget" : "Seleccionar widget", + "no-widgets-matching" : "No se encontraron widgets que coincidan con '{{entity}}'.", + "no-widgets" : "Aún no hay widgets", + "no-widgets-text" : "No se encontraron widgets", + "management" : "Gestión de widgets", + "editor" : "Editor de widgets", + "confirm-to-exit-editor-html" : "Tienes configuraciones del widget sin guardar.
¿Estás seguro de que deseas salir de esta página?", + "widget-type-not-found" : "Problema al cargar la configuración del widget.
Probablemente se haya eliminado el tipo de widget asociado.", + "widget-type-load-error" : "El widget no se cargó debido a los siguientes errores:", + "remove" : "Eliminar widget", + "delete" : "Eliminar widget", + "edit" : "Editar widget", + "remove-widget-title" : "¿Estás seguro de que deseas eliminar el widget '{{widgetTitle}}'?", + "remove-widget-text" : "Después de la confirmación, el widget y todos los datos relacionados serán irrecuperables.", + "replace-reference-with-widget-copy" : "Reemplazar referencia con copia del widget", + "timeseries" : "Serie temporal", + "search-data" : "Buscar datos", + "no-data-found" : "No se encontraron datos", + "latest" : "Valores más recientes", + "rpc" : "Widget de control", + "alarm" : "Widget de alarma", + "static" : "Widget estático", + "timeseries-short" : "serie", + "latest-short" : "recientes", + "rpc-short" : "control", + "alarm-short" : "alarma", + "static-short" : "estático", + "select-widget-type" : "Seleccionar tipo de widget", + "missing-widget-title-error" : "¡Debe especificarse un título para el widget!", + "widget-saved" : "Widget guardado", + "unable-to-save-widget-error" : "¡No se pudo guardar el widget! ¡El widget contiene errores!", + "save" : "Guardar widget", + "saveAs" : "Guardar widget como", + "move" : "Mover widget", + "save-widget-as" : "Guardar widget como", + "save-widget-as-text" : "Por favor, introduce un nuevo título para el widget", + "toggle-fullscreen" : "Activar/desactivar pantalla completa", + "run" : "Ejecutar widget", + "widget-title" : "Título del widget", + "title" : "Título", + "title-required" : "Se requiere el título del widget.", + "title-max-length" : "El título debe tener menos de 256 caracteres", + "system" : "Sistema", + "type" : "Tipo de widget", + "resources" : "Recursos", + "resource-url" : "URL de JavaScript/CSS", + "resource-is-extension" : "Es extensión", + "remove-resource" : "Eliminar recurso", + "add-resource" : "Agregar recurso", + "html" : "HTML", + "tidy" : "Ordenar", + "css" : "CSS", + "settings-form" : "Formulario de configuración", + "data-key-settings-form" : "Formulario de configuración de clave de datos", + "latest-data-key-settings-form" : "Formulario de configuración de clave de datos más reciente", + "widget-settings" : "Configuraciones del widget", + "description" : "Descripción", + "tags" : "Etiquetas", + "image-preview" : "Vista previa de imagen", + "settings-form-selector" : "Selector de formulario de configuración", + "data-key-settings-form-selector" : "Selector de formulario de clave de datos", + "latest-data-key-settings-form-selector" : "Selector de formulario de clave de datos más reciente", + "all" : "Todos", + "actual" : "Actual", + "scada" : "Símbolo SCADA", + "deprecated" : "Obsoleto", + "has-basic-mode" : "Tiene modo básico", + "basic-mode-form-selector" : "Selector de formulario de modo básico", + "basic-mode" : "Básico", + "advanced-mode" : "Avanzado", + "javascript" : "Javascript", + "js" : "JS", + "delete-widget-title" : "¿Estás seguro de que deseas eliminar el widget '{{widgetName}}'?", + "delete-widget-text" : "Después de la confirmación, el widget y todos los datos relacionados serán irrecuperables.", + "delete-widgets-title" : "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 widget} other {# widgets} }?", + "delete-widgets-text" : "Ten cuidado, después de la confirmación, todos los widgets seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "delete-widget" : "Eliminar widget", + "widget-template-load-failed-error" : "¡Error al cargar la plantilla del widget!", + "details" : "Detalles", + "widget-details" : "Detalles del widget", + "add" : "Agregar widget", + "add-existing-widget" : "Agregar widget existente", + "add-new-widget" : "Agregar nuevo widget", + "search-widgets" : "Buscar widgets", + "selected-widgets" : "{ count, plural, =1 {1 widget} other {# widgets} } seleccionado(s)", + "undo" : "Deshacer cambios del widget", + "export" : "Exportar widget", + "export-prompt" : "Incluir imágenes y recursos del widget", + "export-widgets" : "Exportar widgets", + "export-widgets-prompt" : "Incluir imágenes y recursos de los widgets", + "import" : "Importar widget", + "no-data" : "No hay datos para mostrar en el widget", + "data-overflow" : "El widget muestra {{count}} de {{total}} entidades", + "alarm-data-overflow" : "El widget muestra alarmas para {{allowedEntities}} entidades (máximo permitido) de un total de {{totalEntities}} entidades", + "search" : "Buscar widget", + "filter" : "Tipo de filtro del widget", + "loading-widgets" : "Cargando widgets...", + "widget-template-error" : "Plantilla HTML del widget no válida.", + "reference" : "Referencia" + }, + "widget-action" : { + "header-button" : "Botón de encabezado del widget", + "do-nothing" : "No hacer nada", + "open-dashboard-state" : "Navegar a un nuevo estado del tablero", + "update-dashboard-state" : "Actualizar el estado actual del tablero", + "open-dashboard" : "Navegar a otro tablero", + "custom" : "Acción personalizada", + "custom-pretty" : "Acción personalizada (con plantilla HTML)", + "custom-pretty-error-title" : "Error en el diálogo personalizado", + "custom-pretty-template-error" : "Plantilla del diálogo personalizado no válida.", + "custom-pretty-controller-error" : "Se produjo un error al evaluar la función del diálogo personalizado.", + "mobile-action" : "Acción móvil", + "target-dashboard-state" : "Estado objetivo del tablero", + "target-dashboard-state-required" : "Se requiere el estado objetivo del tablero", + "set-entity-from-widget" : "Establecer entidad desde el widget", + "target-dashboard" : "Tablero objetivo", + "select-target-dashboard" : "Seleccionar tablero objetivo", + "target-dashboard-required" : "Se requiere un tablero objetivo.", + "open-right-layout" : "Abrir diseño derecho del tablero (vista móvil)", + "state-display-type" : "Opción de visualización del estado del tablero", + "open-normal" : "Normal", + "open-in-separate-dialog" : "Abrir en diálogo separado", + "open-in-popover" : "Abrir en ventana emergente (popover)", + "dialog-title" : "Título del diálogo", + "dialog-hide-dashboard-toolbar" : "Ocultar barra de herramientas del tablero en el diálogo", + "dialog-width" : "Ancho del diálogo en porcentaje relativo al ancho de la ventana", + "dialog-height" : "Altura del diálogo en porcentaje relativo a la altura de la ventana", + "dialog-size-range-error" : "El valor del tamaño del diálogo debe estar en un rango de 1 a 100.", + "popover-preferred-placement" : "Ubicación preferida del popover", + "popover-placement-top" : "Arriba", + "popover-placement-topLeft" : "Arriba a la izquierda", + "popover-placement-topRight" : "Arriba a la derecha", + "popover-placement-right" : "Derecha", + "popover-placement-rightTop" : "Arriba a la derecha", + "popover-placement-rightBottom" : "Abajo a la derecha", + "popover-placement-bottom" : "Abajo", + "popover-placement-bottomLeft" : "Abajo a la izquierda", + "popover-placement-bottomRight" : "Abajo a la derecha", + "popover-placement-left" : "Izquierda", + "popover-placement-leftTop" : "Arriba a la izquierda", + "popover-placement-leftBottom" : "Abajo a la izquierda", + "popover-hide-on-click-outside" : "Ocultar popover al hacer clic fuera", + "popover-hide-dashboard-toolbar" : "Ocultar barra de herramientas del tablero en el popover", + "popover-width" : "Ancho del popover", + "popover-height" : "Altura del popover", + "popover-style" : "Estilo del popover", + "open-new-browser-tab" : "Abrir en una nueva pestaña del navegador", + "open-URL" : "Abrir URL", + "URL" : "URL", + "url-required" : "Se requiere la URL.", + "mobile" : { + "device-provision" : "Provisión del dispositivo", + "action-type" : "Tipo de acción móvil", + "select-action-type" : "Seleccionar tipo de acción móvil", + "action-type-required" : "Se requiere el tipo de acción móvil", + "take-picture-from-gallery" : "Seleccionar imagen de la galería", + "take-photo" : "Tomar foto", + "map-direction" : "Abrir direcciones en el mapa", + "map-location" : "Abrir ubicación en el mapa", + "scan-qr-code" : "Escanear código QR", + "make-phone-call" : "Hacer llamada telefónica", + "get-location" : "Obtener ubicación del teléfono", + "take-screenshot" : "Tomar captura de pantalla" }, - "layout": { - "layout": "Diseño", - "layouts": "Diseños", - "manage": "Administrar diseños", - "settings": "Ajustes de diseño", - "color": "Color", - "main": "Principal", - "right": "Derecho", - "left": "Izquierdo", - "select": "Seleccionar diseño de destino", - "percentage-width": "Ancho en porcentaje (%)", - "fixed-width": "Ancho fijo (px)", - "left-width": "Columna izquierda (%)", - "right-width": "Columna derecha (%)", - "pick-fixed-side": "Lado fijo: ", - "layout-fixed-width": "Ancho fijo (px)", - "value-min-error": "El valor debe ser mayor que {{min}}{{unit}}", - "value-max-error": "El valor debe ser menor que {{max}}{{unit}}", - "layout-fixed-width-required": "Se requiere ancho fijo", - "right-width-percentage-required": "Se requiere porcentaje derecha", - "left-width-percentage-required": "Se requiere porcentaje izquierda", - "divider": "Divisor", - "right-side": "Diseño derecho", - "left-side": "Diseño izquierdo" + "custom-action-function" : "Función de acción personalizada", + "custom-pretty-function" : "Función de acción personalizada (con plantilla HTML)", + "map-item-type" : "Tipo de elemento del mapa", + "map-item" : { + "marker" : "Marcador", + "polygon" : "Polígono", + "rectangle" : "Rectángulo", + "circle" : "Círculo" }, - "legend": { - "direction": "Dirección de la leyenda", - "position": "Posición de la leyenda", - "show-values": "Mostrar valores", - "min-option": "Min", - "max-option": "Max", - "average-option": "Media", - "total-option": "Total", - "latest-option": "Últimos", - "sort-legend": "Ordenar claves en leyenda", - "show-max": "Mostrar valor máximo", - "show-min": "Mostrar valor mínimo", - "show-avg": "Mostrar valor promedio", - "show-total": "Mostrar valor total", - "show-latest": "Mostrar último valor", - "settings": "Configuración de la leyenda", - "min": "mínimo", - "max": "máximo", - "avg": "promedio", - "total": "total", - "latest": "último", - "comparison-time-ago": { - "previousInterval": "(intervalo anterior)", - "customInterval": "(intervalo personalizado)", - "days": "(hace un día)", - "weeks": "(hace una semana)", - "months": "(hace un mes)", - "years": "(hace un año)" - } + "place-map-item" : "Colocar elemento en el mapa", + "map-item-tooltip" : { + "customize-map-item-tooltips" : "Personalizar los tooltips de los elementos del mapa", + "place-marker" : "Colocar marcador", + "start-draw-rectangle" : "Iniciar dibujo del rectángulo", + "finish-draw-rectangle" : "Finalizar dibujo del rectángulo", + "start-draw-polygon" : "Iniciar dibujo del polígono", + "continue-draw-polygon" : "Continuar dibujo del polígono", + "finish-draw-polygon" : "Finalizar dibujo del polígono", + "start-draw-circle" : "Iniciar dibujo del círculo", + "finish-draw-circle" : "Finalizar dibujo del círculo" + } + }, + "widgets-bundle" : { + "current" : "Paquete actual", + "widgets-bundles" : "Paquetes de widgets", + "widgets-bundle-widgets" : "Widgets del paquete", + "add" : "Agregar paquete de widgets", + "delete" : "Eliminar paquete de widgets", + "title" : "Título", + "title-required" : "El título es obligatorio.", + "title-max-length" : "El título debe tener menos de 256 caracteres", + "description" : "Descripción", + "image-preview" : "Vista previa de la imagen", + "scada" : "Paquete de widgets SCADA", + "order" : "Orden", + "add-widgets-bundle-text" : "Agregar nuevo paquete de widgets", + "no-widgets-bundles-text" : "No se encontraron paquetes de widgets", + "empty" : "El paquete de widgets está vacío", + "details" : "Detalles", + "widgets-bundle-details" : "Detalles del paquete de widgets", + "delete-widgets-bundle-title" : "¿Está seguro de que desea eliminar el paquete de widgets '{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text" : "Tenga cuidado, después de la confirmación, el paquete de widgets y todos los datos relacionados serán irrecuperables.", + "delete-widgets-bundles-title" : "¿Está seguro de que desea eliminar { count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} }?", + "delete-widgets-bundles-action-title" : "Eliminar { count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} }", + "delete-widgets-bundles-text" : "Tenga cuidado, después de la confirmación, todos los paquetes de widgets seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "no-widgets-bundles-matching" : "No se encontraron paquetes de widgets que coincidan con '{{widgetsBundle}}'.", + "widgets-bundle-required" : "Se requiere un paquete de widgets.", + "system" : "Sistema", + "import" : "Importar paquete de widgets", + "export" : "Exportar paquete de widgets", + "export-widgets-bundle-widgets-prompt" : "Incluir widgets del paquete en los datos exportados (de lo contrario, solo se exportarán los FQN de los widgets referenciados)", + "export-failed-error" : "No se pudo exportar el paquete de widgets: {{error}}", + "create-new-widgets-bundle" : "Crear nuevo paquete de widgets", + "widgets-bundle-file" : "Archivo del paquete de widgets", + "invalid-widgets-bundle-file-error" : "No se pudo importar el paquete de widgets: estructura de datos del paquete no válida.", + "search" : "Buscar paquetes de widgets", + "selected-widgets-bundles" : "{ count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} } seleccionado(s)", + "open-widgets-bundle" : "Abrir paquete de widgets", + "loading-widgets-bundles" : "Cargando paquetes de widgets...", + "create-new" : "Crear nuevo paquete de widgets" + }, + "widget-config" : { + "data" : "Datos", + "settings" : "Configuración", + "advanced" : "Avanzado", + "appearance" : "Apariencia", + "widget-card" : "Tarjeta del widget", + "mobile" : "Móvil", + "title" : "Título", + "title-tooltip" : "Tooltip del título", + "general-settings" : "Configuración general", + "display-title" : "Mostrar título del widget", + "card-title" : "Título de la tarjeta", + "drop-shadow" : "Sombra", + "enable-fullscreen" : "Habilitar pantalla completa", + "background-color" : "Color de fondo", + "text-color" : "Color del texto", + "border-radius" : "Radio del borde", + "padding" : "Relleno", + "margin" : "Margen", + "widget-style" : "Estilo del widget", + "widget-css" : "CSS del widget", + "title-style" : "Estilo del título", + "mobile-mode-settings" : "Modo móvil", + "order" : "Orden", + "height" : "Altura", + "mobile-hide" : "Ocultar widget en modo móvil", + "desktop-hide" : "Ocultar widget en modo escritorio", + "units" : "Símbolo especial a mostrar junto al valor", + "units-by-default" : "Unidades por defecto", + "decimals" : "Número de dígitos después del punto decimal", + "decimals-by-default" : "Decimales por defecto", + "default-data-key-parameter-hint" : "Este parámetro se aplica a todos los valores del widget, a menos que se anule mediante la configuración de la clave de datos", + "units-short" : "Unidades", + "decimals-short" : "Decimales", + "decimals-suffix" : "decimales", + "digits-suffix" : "dígitos", + "timewindow" : "Ventana de tiempo", + "use-dashboard-timewindow" : "Usar ventana de tiempo del tablero", + "use-widget-timewindow" : "Usar ventana de tiempo del widget", + "display-timewindow" : "Mostrar ventana de tiempo", + "legend" : "Leyenda", + "display-legend" : "Mostrar leyenda", + "datasources" : "Orígenes de datos", + "datasource" : "Origen de datos", + "maximum-datasources" : "Máximo { count, plural, =1 {1 origen de datos permitido.} other {# orígenes de datos permitidos} }", + "timeseries-key-error" : "Debe especificarse al menos una clave de datos de serie temporal", + "datasource-type" : "Tipo", + "datasource-parameters" : "Parámetros", + "remove-datasource" : "Eliminar origen de datos", + "add-datasource" : "Agregar origen de datos", + "target-device" : "Dispositivo objetivo", + "alarm-source" : "Fuente de alarma", + "actions" : "Acciones", + "action" : "Acción", + "add-action" : "Agregar acción", + "search-actions" : "Buscar acciones", + "no-actions-text" : "No se encontraron acciones", + "action-source" : "Fuente de la acción", + "select-action-source" : "Seleccionar fuente de la acción", + "action-source-required" : "La fuente de la acción es obligatoria.", + "column-index" : "Índice de columna", + "select-column-index" : "Seleccionar índice de columna", + "column-index-required" : "El índice de columna es obligatorio.", + "not-set" : "No establecido", + "action-name" : "Nombre", + "action-name-required" : "El nombre de la acción es obligatorio.", + "action-name-not-unique" : "Ya existe otra acción con el mismo nombre.\nEl nombre de la acción debe ser único dentro de la misma fuente.", + "action-icon" : "Ícono", + "header-button" : { + "button-settings" : "Configuración del botón", + "button-type" : "Tipo de botón", + "button-type-basic" : "Básico", + "button-type-raised" : "Elevado", + "button-type-stroked" : "Delimitado", + "button-type-flat" : "Plano", + "button-type-icon" : "Ícono", + "button-type-mini-fab" : "FAB", + "colors" : "Colores", + "color" : "Color", + "background" : "Fondo", + "border" : "Borde", + "advanced-button-style" : "Estilo avanzado del botón", + "button-style" : "Estilo del botón" }, - "login": { - "login": "Entrar", - "request-password-reset": "Solicitar restablecer contraseña", - "reset-password": "Restablecer contraseña", - "create-password": "Crear contraseña", - "two-factor-authentication": "Autenticado de dos factores", - "passwords-mismatch-error": "¡Las contraseñas introducidas deben ser iguales!", - "password-again": "Repita la contraseña de nuevo", - "sign-in": "Por favor, inicie sesión", - "username": "Nombre de usuario (correo electrónico)", - "remember-me": "Recordarme", - "forgot-password": "¿Olvidó la contraseña?", - "password-reset": "Restablecer contraseña", - "expired-password-reset-message": "Tus credenciales han expirado! Por favor, crea una nueva contraseña.", - "new-password": "Nueva contraseña", - "new-password-again": "Repita la nueva contraseña", - "password-link-sent-message": "Se ha enviado el enlace de restablecimiento de contraseña con éxito!", - "email": "Email", - "login-with": "Login con {{name}}", - "or": "o", - "error": "Error de Login", - "verify-your-identity": "Verificar identidad", - "select-way-to-verify": "Selecciona el modo de verificación", - "resend-code": "Reenviar código", - "resend-code-wait": "Reenviar código en { time, plural, =1 {1 segundo} other {# segundos} }", - "try-another-way": "Otros modos de verificación", - "totp-auth-description": "Por favor, introduce el código de seguridad de tu aplicación de autenticación.", - "totp-auth-placeholder": "Código", - "sms-auth-description": "Se ha enviado un código de verificación a tu número: {{contact}} proporcionado.", - "sms-auth-placeholder": "Código SMS", - "email-auth-description": "Se ha enviado un código de verificación al email {{contact}} proporcionado.", - "email-auth-placeholder": "Código Email", - "backup-code-auth-description": "Por favor, introduce el código de backup.", - "backup-code-auth-placeholder": "Código de Backup" + "show-hide-action-using-function" : "Mostrar/ocultar acción usando función", + "show-action-function" : "Función para mostrar la acción", + "action-type" : "Tipo", + "action-type-required" : "El tipo de acción es obligatorio.", + "edit-action" : "Editar acción", + "delete-action" : "Eliminar acción", + "delete-action-title" : "Eliminar acción del widget", + "delete-action-text" : "¿Está seguro de que desea eliminar la acción del widget con nombre '{{actionName}}'?", + "title-icon" : "Ícono del título", + "display-icon" : "Mostrar ícono del título", + "card-icon" : "Ícono de la tarjeta", + "icon" : "Ícono", + "icon-color" : "Color del ícono", + "icon-size" : "Tamaño del ícono", + "advanced-settings" : "Configuraciones avanzadas", + "data-settings" : "Configuración de datos", + "limits" : "Límites", + "no-data-display-message" : "Mensaje alternativo \"Sin datos para mostrar\"", + "data-page-size" : "Entidades máximas por origen de datos", + "settings-component-not-found" : "Componente del formulario de configuración no encontrado para el selector '{{selector}}'", + "preview" : "Previsualizar", + "set" : "Establecer", + "set-message" : "Establecer mensaje", + "advanced-title-style" : "Estilo avanzado del título", + "card-style" : "Estilo de la tarjeta", + "text" : "Texto", + "background" : "Fondo", + "advanced-widget-style" : "Estilo avanzado del widget", + "card-buttons" : "Botones de la tarjeta", + "show-card-buttons" : "Mostrar botones de la tarjeta", + "card-border-radius" : "Radio del borde de la tarjeta", + "card-padding" : "Relleno de la tarjeta", + "card-appearance" : "Apariencia de la tarjeta", + "color" : "Color", + "tooltip" : "Información sobre herramientas", + "units-required" : "La unidad es obligatoria.", + "list-layout" : "Diseño de lista", + "layout" : "Diseño", + "resize-options" : "Opciones de redimensionamiento", + "resizable" : "Redimensionable", + "preserve-aspect-ratio" : "Preservar la relación de aspecto" + }, + "widget-type" : { + "import" : "Importar tipo de widget", + "export" : "Exportar tipo de widget", + "export-failed-error" : "No se pudo exportar el widget: {{error}}", + "widget-file" : "Archivo de widget", + "invalid-widget-file-error" : "No se pudo importar el widget: Estructura de datos de widget inválida." + }, + "markdown" : { + "edit" : "Editar", + "preview" : "Vista previa", + "copy-code" : "Haga clic para copiar", + "copied" : "¡Copiado!" + }, + "widgets" : { + "mobile-app-qr-code" : { + "configuration-hint" : "La configuración depende del widget de código QR de la aplicación móvil en la configuración principal de la plataforma", + "get-it-on-google-play" : "Consíguelo en Google Play", + "download-on-the-app-store" : "Descárgalo en el App Store" }, - "signup": { - "firstname": "Nombre", - "lastname": "Apellido", - "email": "Correo electrónico", - "signup": "Registrarse", - "create-password": "Crear una contraseña", - "repeat-password": "Repite tu contraseña", - "have-account": "¿Ya tienes una cuenta?", - "signin": "Iniciar sesión", - "no-captcha-message": "Debes confirmar que no eres un robot", - "password-length-message": "Tu contraseña debe tener al menos 6 caracteres", - "email-verification": "Verificación de correo electrónico", - "email-verification-message": "Se envió un correo electrónico con detalles de verificación a la dirección de correo electrónico especificada. Por favor, sigue las instrucciones proporcionadas en el correo electrónico para completar tu proceso de registro. Nota: si no has visto el correo electrónico durante un tiempo, por favor revisa tu carpeta de 'spam' o intenta reenviar el correo electrónico haciendo clic en el botón 'Reenviar'.", - "account-activation-title": "Activación de cuenta", - "account-activated": "¡Cuenta activada con éxito!", - "account-activated-text": "¡Felicidades! Tu cuenta ha sido activada.", - "resend": "Reenviar", - "inactive-user-exists-title": "Ya existe un usuario inactivo", - "inactive-user-exists-text": "Ya hay un usuario registrado con dirección de correo electrónico no verificada. Haz clic en el botón 'Reenviar' si deseas reenviar el correo electrónico de verificación.", - "activating-account": "Activando cuenta...", - "activating-account-text": "Tu cuenta se está activando actualmente. Por favor espera...", - "accept-privacy-policy": "Aceptar la Política de Privacidad", - "accept": "Aceptar", - "privacy-policy": "Política de Privacidad", - "accept-privacy-policy-message": "Debes aceptar nuestra Política de Privacidad", - "recaptcha-title": "reCAPTCHA", - "terms-of-use": "Términos de Uso", - "accept-terms-of-use-message": "Debes aceptar nuestros Términos de Uso" + "action-button" : { + "behavior" : "Comportamiento", + "on-click" : "Al hacer clic", + "on-click-hint" : "Acción que se activa al hacer clic en el botón", + "first-button-click" : "Primer clic del botón", + "first-button-click-hint" : "Acción al presionar el primer botón.", + "second-button-click" : "Segundo clic del botón", + "second-button-click-hint" : "Acción al presionar el segundo botón.", + "button-click-hint" : "Acción al presionar en el widget." }, - "markdown": { - "edit": "Editar", - "preview": "Previsualizar", - "copy-code": "Click para copiar", - "copied": "Copiado!" + "command-button" : { + "behavior" : "Comportamiento", + "on-click" : "Al hacer clic", + "on-click-hint" : "Acción realizada al hacer clic en el botón." }, - "notification": { - "action-button": "Botón de acción", - "action-type": "Tipo de acción", - "active": "Activo", - "add-notification-recipients-group": "Añadir grupo de destinatarios", - "add-notification-template": "Añadir plantilla de notificaciones", - "add-recipient": "Añadir destinatario", - "add-recipients": "Añadir destinatarios", - "add-rule": "Añadir regla", - "add-stage": "Añadir etapa", - "add-template": "Añadir plantilla", - "after": "Después", - "alarm-assignment-trigger-settings": "Ajustes de activación de asignación de alarmas", - "alarm-comment-trigger-settings": "Ajustes de activación comentarios de alarma", - "alarm-trigger-settings": "Ajustes de activación de alarma", - "all": "Todas", - "api-feature-hint": "Si el campo está vacío, la activación se aplicará a todas las características API", - "api-usage-trigger-settings": "Ajustes de activación de uso API", - "new-platform-version-trigger-settings": "Ajustes de activación de nueva versión de plataforma", - "rate-limits-trigger-settings": "Ajustes de límites excedidos", - "at-least-one-should-be-selected": "Se debe seleccionar por lo menos uno", - "basic-settings": "Ajustes básicos", - "button-text": "Texto del botón", - "button-text-required": "Se requiere texto del botón", - "button-text-max-length": "El texto del botón debe ser menor o igual que {{ length }} caracteres", - "compose": "Redactar", - "conversation": "Conversación", - "conversation-required": "Se requiere conversación", - "copy-notification-template": "Copiar plantilla de notificación", - "copy-rule": "Copiar regla", - "copy-template": "Copiar plantilla", - "create-new": "Crear nueva", - "created": "Creada", - "delete-notification-text": "Atención, tras la confirmación, la notificación no será recuperable.", - "delete-notification-title": "Borrar la notificación, ¿Estás seguro?", - "delete-notifications-text": "Atención, tras la confirmación, las notificaciones no serán recuperables.", - "delete-notifications-title": "Estás seguro de eliminar { count, plural, =1 {1 notificación} other {# notificaciones} }?", - "delete-recipient-text": "Atención, tras la confirmación, el destinatario no será recuperable.", - "delete-recipient-title": "Estás seguro de eliminar el destinatario '{{recipientName}}'?", - "delete-recipients-text": "Atención, tras la confirmación, los destinatarios no serán recuperables.", - "delete-recipients-title": "Estás seguro de eliminar { count, plural, =1 {1 destinatario} other {# destinatarios} }?", - "delete-request-text": "Atención, tras la confirmación, la solicitud no será recuperable.", - "delete-request-title": "Estás seguro de eliminar la solicitud?", - "delete-requests-text": "Atención, tras la confirmación, las solicitudes no serán recuperables.", - "delete-requests-title": "Estás seguro de eliminar { count, plural, =1 {1 solicitud} other {# solicitudes} }?", - "delete-rule-text": "Atención, tras la confirmación, la regla no será recuperable.", - "delete-rule-title": "Estás seguro de eliminar la regla '{{ruleName}}'?", - "delete-rules-text": "Atención, tras la confirmación, las reglas no serán recuperables.", - "delete-rules-title": "Estás seguro de eliminar { count, plural, =1 {1 regla} other {# reglas} }?", - "delete-template-text": "Atención, tras la confirmación, la plantilla no será recuperable.", - "delete-template-title": "Estás seguro de eliminar la plantilla '{{templateName}}'?", - "delete-templates-text": "Atención, tras la confirmación, las plantillas no serán recuperables.", - "delete-templates-title": "Estás seguro de eliminar { count, plural, =1 {1 plantilla} other {# plantillas} }?", - "deleted": "Borrada", - "delivery-method": { - "delivery-method": "Método de entrega", - "email": "Email", - "email-preview": "Vista previa de notificación eMail", - "slack": "Slack", - "slack-preview": "Vista previa de notificación Slack", - "microsoft-teams": "Microsoft Teams", - "microsoft-teams-preview": "Vista previa de notificación por Microsoft Teams", - "sms": "SMS", - "sms-preview": "Vista previa de notificación SMS", - "web": "Web", - "web-preview": "Vista previa de notificación Web" - }, - "delivery-method-not-configure-click": "El método de entrega no está configurado. Click para configurar.", - "delivery-method-not-configure-contact": "El método de entrega no está configurado. Contacta al administrador del sistema.", - "delivery-methods": "Métodos de entrega", - "description": "Descripción", - "device-activity-trigger-settings": "Ajustes de activación de dispositivo activo", - "device-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los dispositivos", - "device-profiles-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los perfiles de dispositivo", - "disabled": "Desactivado", - "edit-notification-recipients-group": "Editar grupo de destinatarios para notificaciones", - "edit-notification-template": "Editar plantilla de notificación", - "edit-rule": "Editar regla", - "edit-template": "Editar plantilla", - "enabled": "Activado", - "entities-limit-trigger-settings": "Ajustes de disparo en límites de entidad", - "entity-action-trigger-settings": "Ajustes de disparo en acciones de entidad", - "entity-type": "Tipo de entidad", - "escalation-chain": "Cadena de escalado", - "failed-send": "Fallos en el envío", - "fails": "{ count, plural, =1 {1 fallo} other {# fallos} }", - "filter": "Filtro", - "first-recipient": "Primer destinatario", - "inactive": "Inactivo", - "inbox": "Bandeja de entrada", - "notification-inbox": "Notificaciones / Bandeja de entrada", - "input-field-support-templatization": "El campo soporta plantillas.", - "input-fields-support-templatization": "Los campos soportan plantillas.", - "link": "Enlace", - "link-required": "Se requiere enlace", - "link-type": { - "dashboard": "Abrir panel", - "link": "Abrir enlace URL" - }, - "loading-notifications": "Cargando notificaciones...", - "management": "Administración de notificaciones", - "mark-all-as-read": "Marcar todas como leídas", - "mark-as-read": "Marcar como leída", - "message": "Mensaje", - "message-required": "Se requiere mensaje", - "message-max-length": "El mensaje debe ser menor o igual que {{ length }} caracteres", - "name": "Nombre", - "name-required": "Se requiere nombre", - "new-notification": "Nueva notificación", - "no-inbox-notification": "No se encontraron notificaciones", - "no-notification-request": "Sin solicitud de notificación", - "no-notification-templates": "No se encontraron plantillas", - "no-notifications-yet": "No hay notificaciones", - "no-recipients-notification": "No hay destinatarios", - "no-rule": "No hay regla configurada", - "no-rules-notification": "Sin reglas de notificación", - "no-severity-found": "No se encontró gravedad", - "no-severity-matching": "'{{severity}}' no encontrada.", - "no-template-matching": "No hay recursos que coincidan con '{{template}}'.", - "not-found-slack-recipient": "No se ha encontrado destinatario Slack", - "notification": "Notificación", - "notification-center": "Centro de notificaciones", - "notify": "notificar", - "notify-again": "Notificar de nuevo", - "notify-alarm-action": { - "acknowledged": "Alarma acusada", - "assigned": "Alarma asignada", - "cleared": "Alarma borrada", - "created": "Alarma creada", - "severity-changed": "Gravedad cambiada", - "unassigned": "Alarma no asignada" - }, - "notify-on": "Notificar en", - "notify-on-comment-update": "Notificar cuando haya un comentario nuevo", - "notify-on-required": "Se requiere notificar en", - "notify-on-unassign": "Notificar en deasignación", - "notify-only-user-comments": "Notificar sólo cuando haya comentarios de usuario", - "only-rule-chain-lifecycle-failures": "Sólo fallos de ciclo de vida en cadena de reglas", - "only-rule-node-lifecycle-failures": "Sólo fallos de ciclo de vida en nodos de reglas", - "platform-users": "Usuarios de la plataforma", - "rate-limits": "Límites de tasa", - "rate-limits-hint": "Si el campo está vacío, el activador se aplicará a todos los límites de tasa", - "recipient": "Destinatario", - "recipient-group": "Grupo de destinatarios", - "recipient-type": { - "affected-tenant-administrators": "Administradores afectados", - "affected-user": "Usuario afectado", - "all-users": "Todos los usuarios", - "customer-users": "Usuarios del cliente", - "system-administrators": "Administradores del sistema", - "tenant-administrators": "Administradores de propietarios", - "user-filters": "Filtro de usuarios", - "user-list": "Lista de usuarios", - "users-entity-owner": "Usuarios que sean propietarios de la entidad" - }, - "recipients": "Destinatarios", - "notification-recipients": "Notificaciones / Destinatarios", - "recipients-count": "{ count, plural, =1 {1 destinatario} other {# destinatarios} }", - "recipients-required": "Se requieren destinatarios", - "refresh-allow-delivery-method": "Actualiza permitir método de entrega", - "request-search": "Búsqueda de solicitudes", - "request-status": { - "processing": "Procesando", - "scheduled": "En cola", - "sent": "Enviado" - }, - "review": "Examinar", - "rule": "Regla", - "rule-chain-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todas las cadenas de reglas", - "rule-engine-events-trigger-settings": "Ajustes de disparo en motor de reglas", - "rule-engine-filter": "Filtro de motor de reglas", - "rule-name": "Nombre de regla", - "rule-name-required": "Se requiere nombre", - "rule-disable": "Desactivar regla de notificación", - "rule-enable": "Activar regla de notificación", - "rule-node-filter": "Filtro de regla", - "rules": "Reglas", - "notification-rules": "Notificaciones / Reglas", - "scheduler-later": "Programar para más tarde", - "search-notification": "Buscar notificaciones", - "search-recipients": "Buscar destinatarios", - "search-rules": "Buscar reglas", - "search-templates": "Buscar plantillas", - "see-documentation": "Ver documentación", - "selected-notifications": "{ count, plural, =1 {1 notificación} other {# notificaciones} } seleccionadas", - "selected-recipients": "{ count, plural, =1 {1 destinatario} other {# destinatarios} } seleccionados", - "selected-requests": "{ count, plural, =1 {1 solicitud} other {# solicitudes} } seleccionados", - "selected-rules": "{ count, plural, =1 {1 regla} other {# reglas} } seleccionados", - "selected-template": "{ count, plural, =1 {1 plantilla} other {# plantillas} } seleccionados", - "send-notification": "Enviar notificación", - "sent": "Enviado", - "notification-sent": "Notificaciones / Enviadas", - "set-entity-from-notification": "Establecer entidad de notificación a estado de panel", - "slack-chanel-type": "Tipo de canal Slack", - "slack-chanel-types": { - "direct": "Mensaje directo", - "private-channel": "Canal privado", - "public-channel": "Canal público" - }, - "start-from-scratch": "Empezar de 0", - "status": "Estado", - "stop-escalation-alarm-status-become": "Detener la escalada cuando el estado de alarma se convierta en:", - "subject": "Asunto", - "subject-required": "Se requiere asunto", - "template": "Plantilla", - "template-name": "Nombre de plantilla", - "template-required": "Se requiere nombre de plantilla", - "template-type": { - "alarm": "Alarma", - "alarm-assignment": "Asignación de alarma", - "alarm-comment": "Comentario de alarma", - "api-usage-limit": "Límite en uso de API", - "device-activity": "Actividad de dispositivo", - "entities-limit": "Límites de entidades", - "entity-action": "Acciones de entidad", - "general": "General", - "rule-engine-lifecycle-event": "Evento de ciclo de vida en motor de reglas", - "rule-node": "Nodo de regla", - "new-platform-version": "Nueva versión de plataforma", - "rate-limits": "Se excedieron los límites de tasa" - }, - "templates": "Plantillas", - "notification-templates": "Notificaciones / Plantillas", - "tenant-profiles-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los perfiles de administradores propietarios", - "tenants-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los administradores propietarios", - "threshold": "Umbral", - "theme-color": "Color del tema", - "time": "Hora", - "track-rule-node-events": "Hacer seguimiento de eventos en nodos de reglas", - "trigger": { - "alarm": "Alarma", - "alarm-assignment": "Asignación de alarma", - "alarm-comment": "Comentario de alarma", - "api-usage-limit": "Límite en uso de API", - "device-activity": "Actividad de dispositivo", - "entities-limit": "Límites de entidades", - "entity-action": "Actiones de entidades", - "rule-engine-lifecycle-event": "Evento de ciclo de vida en Motor de reglas", - "new-platform-version": "Nueva versión de plataforma", - "rate-limits": "Se excedieron los límites de tasa", - "trigger": "Disparo", - "trigger-required": "Se requiere disparo" - }, - "type": "Tipo", - "unread": "No leído", - "updated": "Actualizado", - "use-template": "Usar plantilla", - "view-all": "Ver todos", - "warning": "Alerta", - "webhook-url": "URL Webhook", - "webhook-url-required": "Se requiere URL Webhook", - "channel-name": "Nombre del canal", - "channel-name-required": "Se requiere nombre del canal", - "settings": { - "notification-settings": "Ajustes de notificaciones", - "reset-all": "Restablecer ajustes", - "reset-all-title": "Estás seguro de borrar el formulario?", - "reset-all-text": "Atención, tras la confirmación, el formulario de ajustes se restablecerá a los valores por defecto.", - "type": "Tipo", - "enable-all": "Activar todos", - "disable-all": "Desactivar todos", - "delivery-not-configured": "No hay configurado un método de entrega" - } + "power-button" : { + "behavior" : "Comportamiento", + "power-on" : "Encender", + "power-on-hint" : "Acción realizada para encender el componente.", + "power-off" : "Apagar", + "power-off-hint" : "Acción realizada para apagar el componente.", + "on-label" : "Encendido", + "off-label" : "Apagado", + "layout" : "Diseño", + "layout-default" : "Predeterminado", + "layout-simplified" : "Simplificado", + "layout-outlined" : "Contorneado", + "layout-default-volume" : "Predeterminado.Volumen", + "layout-simplified-volume" : "Simplificado.Volumen", + "layout-outlined-volume" : "Contorneado.Volumen", + "layout-default-icon" : "Predeterminado.Icono", + "layout-simplified-icon" : "Simplificado.Icono", + "layout-outlined-icon" : "Contorneado.Icono", + "main" : "Principal", + "background" : "Fondo", + "button-icon-on" : "Icono del botón 'Encendido'", + "button-icon-off" : "Icono del botón 'Apagado'", + "power-on-colors" : "Colores de encendido", + "power-off-colors" : "Colores de apagado", + "disabled-colors" : "Colores deshabilitados", + "button" : "Botón" }, - "ota-update": { - "add": "Añadir paquete", - "assign-firmware": "Firmware asignado", - "assign-firmware-required": "Firmware asignado requerido", - "assign-software": "Software asignado", - "assign-software-required": "Software asignado requerido", - "auto-generate-checksum": "Auto-generar checksum", - "checksum": "Checksum", - "checksum-hint": "Si el checksum está vacío, se generará automáticamente", - "checksum-algorithm": "Algoritmo checksum", - "checksum-copied-message": "El checksum del paquete se ha copiado al portapapeles", - "change-firmware": "El cambio del firmware provocará la actualización de { count, plural, =1 {1 dispositivo} other {# dispositivos} }.", - "change-software": "El cambio del software provocará la actualización de { count, plural, =1 {1 dispositivo} other {# dispositivos} }.", - "chose-compatible-device-profile": "El paquete subido, sólamente estará disponible para los dispositivos con el perfil seleccionado.", - "chose-firmware-distributed-device": "Elige el firmware que se distribuirá a los dispositivos", - "chose-software-distributed-device": "Elige el software que se distribuirá a los dispositivos", - "content-type": "Tipo de contenido", - "copy-checksum": "Copiar checksum", - "copy-direct-url": "Copiar URL directa", - "copyId": "Copiar Id de paquete", - "copied": "Copiado!", - "delete": "Borrar paquete", - "delete-ota-update-text": "Atención, tras la confirmación la actualización OTA será irrecuperable.", - "delete-ota-update-title": "Estás seguro de borrar la actualización OTA '{{title}}'?", - "delete-ota-updates-text": "Atención, tras la confirmación todas las actualizaciones OTA seleccionadas se borrarán.", - "delete-ota-updates-title": "Estás seguro de borrar { count, plural, =1 {1 Actualización OTA} other {# Actualizaciones OTA} }?", - "description": "Descripción", - "direct-url": "URL Directa", - "direct-url-copied-message": "La URL directa del paquete se ha copiado al portapapeles", - "direct-url-required": "URL Directa requerida", - "download": "Descargar paquete", - "drop-file": "Arrastra un fichero o haz click para seleccionar un fichero a subir.", - "drop-package-file-or": "Arrastrar y soltar un fichero o", - "file-name": "Nombre del fichero", - "file-size": "Tamaño del fichero", - "file-size-bytes": "Tamaño del fichero en bytes", - "idCopiedMessage": "El Id del paquete se ha copiado al portapapeles", - "no-firmware-matching": "No se ha encontrado ningún paquete de firmware OTA compatible que coincidan con '{{entity}}'.", - "no-firmware-text": "No hay aprovisionado ningún paquete de firmware OTA.", - "no-packages-text": "No se han encontrado paquetes", - "no-software-matching": "No se ha encontrado ningún paquete de software OTA compatible que coincidan con '{{entity}}'.", - "no-software-text": "No hay aprovisionado ningún paquete de software OTA.", - "ota-update": "Actualización OTA", - "ota-update-details": "Detalles de actualización OTA", - "ota-updates": "Actualizaciones OTA", - "package-type": "Tipo de paquete", - "packages-repository": "Repositorio de paquetes", - "search": "Buscar paquetes", - "selected-package": "{ count, plural, =1 {1 paquete} other {# paquetes} } selected", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256", - "types": { - "firmware": "Firmware", - "software": "Software" - }, - "upload-binary-file": "Subir fichero binario", - "use-external-url": "Usar una URL externa", - "version": "Versión", - "version-required": "Versión requerida.", - "version-tag": "Tag de Versión", - "version-tag-hint": "El tag de versión debe coincidir con la versión reportada por el dispositivo.", - "version-max-length": "Versión debe ser menor que 256", - "warning-after-save-no-edit": "Una vez que el paquete se haya subido, no se podrá modificar el título, versión, perfil de dispositivo y tipo de paquete." + "toggle-button" : { + "behavior" : "Comportamiento", + "checked" : "Seleccionado", + "unchecked" : "No seleccionado", + "check" : "Seleccionar", + "check-hint" : "Acción realizada para seleccionar el componente.", + "uncheck" : "Deseleccionar", + "uncheck-hint" : "Acción realizada para deseleccionar el componente.", + "auto-scale" : "Escalado automático", + "horizontal-fill" : "Relleno horizontal", + "vertical-fill" : "Relleno vertical", + "button-appearance" : "Apariencia del botón" }, - "position": { - "top": "Superior", - "bottom": "Inferior", - "left": "Izquierda", - "right": "Derecha" + "segmented-button" : { + "layout" : "Diseño", + "layout-squared" : "Cuadrado", + "layout-rounded" : "Redondeado", + "card-border" : "Borde de la tarjeta", + "button-appearance" : "Apariencia del botón", + "first" : "Primero", + "second" : "Segundo", + "color-styles" : "Estilos de color", + "selected" : "Seleccionado", + "unselected" : "No seleccionado" }, - "profile": { - "profile": "Perfil", - "last-login-time": "Último acceso", - "change-password": "Cambiar contraseña", - "current-password": "Contraseña actual", - "copy-jwt-token": "Copiar JWT", - "jwt-token": "Tóken JWT", - "token-valid-till": "Válido hasta {{expirationData}}", - "tokenCopiedSuccessMessage": "JWT copiado al portapapeles", - "tokenCopiedWarnMessage": "JWT caducado, por favor actualiza la página." + "button" : { + "layout" : "Diseño", + "outlined" : "Contorneado", + "filled" : "Relleno", + "underlined" : "Subrayado", + "basic" : "Básico", + "auto-scale" : "Escalado automático", + "label" : "Etiqueta", + "icon" : "Icono", + "border-radius" : "Radio del borde", + "color-palette" : "Paleta de colores", + "main" : "Principal", + "background" : "Fondo", + "border" : "Borde", + "custom-styles" : "Estilos personalizados", + "clear-style" : "Borrar estilo", + "shadow" : "Sombra", + "enabled" : "Habilitado", + "disabled" : "Deshabilitado", + "preview" : "Vista previa", + "copy-style-from" : "Copiar estilo desde" }, - "profiles": { - "profiles": "Perfiles" + "value-stepper" : { + "behavior" : "Comportamiento", + "simplified" : "Simplificado", + "filled" : "Relleno", + "outlined" : "Contorneado", + "volume" : "Volumen", + "initial-state" : "Estado inicial", + "initial-state-hint" : "Acción para obtener el valor inicial.", + "disabled-state" : "Estado deshabilitado", + "disabled-state-hint" : "Configurar condición bajo la cual el componente está deshabilitado.", + "right-button-click" : "Clic en botón derecho", + "right-button-click-hint" : "Acción al presionar el botón derecho.", + "left-button-click" : "Clic en botón izquierdo", + "left-button-click-hint" : "Acción al presionar el botón izquierdo.", + "auto-scale" : "Escalado automático", + "value-range" : "Rango", + "min-range" : "Mínimo", + "max-range" : "Máximo", + "value-increment-decrement-step" : "Paso de incremento/decremento de valor", + "value" : "Valor", + "value-box-background" : "Fondo del cuadro de valor", + "border" : "Borde", + "button-appearance" : "Apariencia del botón", + "left" : "Izquierda", + "right" : "Derecha", + "left-button" : "Botón izquierdo", + "right-button" : "Botón derecho", + "icon" : "Icono", + "color-palette" : "Paleta de colores", + "main" : "Principal", + "background" : "Fondo", + "button-icon-on" : "Icono del botón 'Encendido'", + "button-on-colors" : "Colores de 'Encendido'", + "disabled-colors" : "Colores deshabilitados" }, - "security": { - "security": "Seguridad", - "general-settings": "Ajustes generales de seguridad", - "access-token": "Tóken de acceso", - "access-token-required": "Tóken de acceso requerido.", - "clientId": "ID cliente", - "clientId-required": "Se requiere ID cliente", - "username": "Usuario", - "username-required": "Se requiere usuario", - "ca-cert": "Certificado CA", - "2fa": { - "2fa": "Autenticación de doble factor (2FA)", - "2fa-description": "La autenticación de doble factor proteje tu cuenta de accesos no autorizados. Lo único que tienes que hacer es entrar un código de seguridad al hacer login.", - "authenticate-with": "Puedes autenticarte con:", - "disable-2fa-provider-text": "Desactivar {{name}} hará tu cuenta menos segura", - "disable-2fa-provider-title": "Estás seguro de desactivar {{name}}?", - "get-new-code": "Obtener un nuevo código", - "main-2fa-method": "Usar como método de autenticación de doble factor principal", - "dialog": { - "activation-step-description-email": "La próxima vez que hagas log-in, se deberá introducir el código de seguridad que se enviará a tu dirección de email.", - "activation-step-description-sms": "La próxima vez que hagas log-in, se deberá introducir el código de seguridad que se enviará a tu número de teléfono.", - "activation-step-description-totp": "La próxima vez que hagas log-in, se deberá introducir el código de seguridad de doble factor.", - "activation-step-label": "Activación", - "backup-code-description": "Imprime los códigos y guárdalos en un lugar seguro, se necesitarán para hacer login en tu cuenta. Puedes usar cada código de backup sólo una vez.", - "backup-code-warn": "Una vez que salgas de esta página, estos códigos no se volverán a mostrar. Guárdalos en un lugar seguro usando las opciones a continuación.", - "download-txt": "Descargar (txt)", - "email-step-description": "Introduce tu email para usar como autenticador.", - "email-step-label": "Email", - "enable-email-title": "Activar autenticador por email", - "enable-sms-title": "Activar autenticador por SMS", - "enable-totp-title": "Activar autenticador por app", - "enter-verification-code": "Entra el código de 6-dígitos", - "get-backup-code-title": "Obtener copia de seguridad", - "next": "Siguiente", - "scan-qr-code": "Escanea el código QR con tu aplicación de autenticación", - "send-code": "Enviar código", - "sms-step-description": "Introduce tu número de teléfono para usar como autenticador.", - "sms-step-label": "Número de teléfono", - "success": "Éxito!", - "totp-step-description-install": "Puedes instalar la aplicación Google Authenticator, Authy, o Duo.", - "totp-step-description-open": "Abre la aplicación de autenticación en tu teléfono móvil.", - "totp-step-label": "Obtener app", - "verification-code": "Código de 6-dígitos", - "verification-code-invalid": "Formáto de código inválido", - "verification-code-incorrect": "El código de verificación es incorrecto", - "verification-code-many-request": "Demasiadas solicitudes, revisa el código de verificación", - "verification-step-description": "Introduce el código de 6-dígitos que acabamos de enviar a {{address}}", - "verification-step-label": "Verificación" - }, - "provider": { - "email": "Email", - "email-description": "Usar un código de seguridad enviado a tu email para autenticarte.", - "email-hint": "Los códigos de autenticación se han enviado por email a {{ info }}", - "sms": "SMS", - "sms-description": "Usar tu teléfono para autenticarte. Enviaremos un código de seguridad vía SMS.", - "sms-hint": "Los códigos de autenticación se han enviado por mensaje de texto SMS a {{ info }}", - "totp": "App de autenticación", - "totp-description": "Usar aplicaciones como Google Authenticator, Authy, o Duo en tu teléfono para autenticarte. Se generará un código de seguridad para hacer login.", - "totp-hint": "La aplicación de autenticación se ha configurado en tu cuenta", - "backup_code": "Código Backup", - "backup-code-description": "Estos códigos de seguridad imprimibles son de un solo uso, te permiten identificarte cuando no tengas el teléfono a mano, útil cuando se viaja.", - "backup-code-hint": "{{ info }} códigos de un solo uso activos en este momento" - } - }, - "password-requirement": { - "at-least": "Al menos:", - "character": "{ count, plural, =1 {1 caracter} other {# caracteres} }", - "digit": "{ count, plural, =1 {1 dígito} other {# dígitos} }", - "incorrect-password-try-again": "Contraseña incorrecta. Prueba otra vez", - "lowercase-letter": "{ count, plural, =1 {1 minúscula} other {# mínusculas} }", - "new-passwords-not-match": "Las contraseñas no coinciden", - "password-should-not-contain-spaces": "La contraseña no puede contener espacios", - "password-not-meet-requirements": "La contraseña no reune los requisitos necesarios", - "password-requirements": "Requisitos de contraseña", - "password-should-difference": "La nueva contraseña debe ser diferente a la actual", - "special-character": "{ count, plural, =1 {1 caracter especial} other {# caracteres especiales} }", - "uppercase-letter": "{ count, plural, =1 {1 mayúscula} other {# mayúsculas} }" - } + "button-state" : { + "activated-state" : "Estado activado", + "activated-state-hint" : "Configurar condición bajo la cual el botón está activo.", + "disabled-state" : "Estado deshabilitado", + "disabled-state-hint" : "Configurar condición bajo la cual el botón está deshabilitado.", + "selected-state" : "Seleccionar botón", + "selected-state-hint" : "Configurar condición bajo la cual el botón está seleccionado.", + "enabled" : "Habilitado", + "hovered" : "Con cursor encima", + "pressed" : "Presionado", + "activated" : "Activado", + "disabled" : "Deshabilitado", + "initial" : "Primer botón", + "first" : "Primero", + "second" : "Segundo" }, - "relation": { - "relations": "Relaciones", - "direction": "Dirección", - "clear-relation-type": "Borrar tipo de relación", - "search-direction": { - "FROM": "Desde", - "TO": "Hacia" - }, - "direction-type": { - "FROM": "desde", - "TO": "hacia" - }, - "from-relations": "Relaciones salientes (outbound)", - "to-relations": "Relaciones entrantes (inbound)", - "selected-relations": "{ count, plural, =1 {1 relación} other {# relaciones} } seleccionadas", - "type": "Tipo", - "to-entity-type": "Hacia tipo de entidad", - "to-entity-name": "Hacia nombre de entidad", - "from-entity-type": "Desde tipo de entidad", - "from-entity-name": "Desde nombre de entidad", - "to-entity": "Hacia entidad", - "from-entity": "Desde entidad", - "delete": "Borrar relación", - "relation-type": "Tipo de relación", - "relation-type-required": "Tipo de relación requerido.", - "relation-type-max-length": "Tipo de relación debe ser menor de 256", - "any-relation-type": "Cualquier tipo", - "add": "Añadir relación", - "edit": "Editar relación", - "delete-to-relation-title": "¿Quieres eliminar la relación con la entidad '{{entityName}}'?", - "delete-to-relation-text": "Atención, tras la confirmación la entidad '{{entityName}}' no estará relacionada con la entidad actual.", - "delete-to-relations-title": "¿Quieres eliminar { count, plural, =1 {1 relación} other {# relaciones} }?", - "delete-to-relations-text": "Atención, tras la confirmación todas las relaciones seleccionadas se eliminarán y sus entidades correspondientes no estarán relacionadas con la entidad actual.", - "delete-from-relation-title": "¿Quieres eliminar la relación con la entidad '{{entityName}}'?", - "delete-from-relation-text": "Atención, tras la confirmación la entidad actual no estará relacionada con la entidad '{{entityName}}'.", - "delete-from-relations-title": "¿Quieres eliminar { count, plural, =1 {1 relación} other {# relaciones} }?", - "delete-from-relations-text": "Atención, tras la confirmación todas las relaciones seleccionadas se eliminarán y sus entidades correspondientes no estarán relacionadas con sus entidades correspondientes.", - "remove-relation-filter": "Borrar filtro de relación", - "remove-filter": "Borrar filtro", - "add-relation-filter": "Añadir filtro de relación", - "any-relation": "Cualquier relación", - "relation-filters": "Filtro de relación", - "additional-info": "Información adicional (JSON)", - "invalid-additional-info": "Error al analizar el fichero JSON de información adicional.", - "no-relations-text": "No se encontraron relaciones" + "background" : { + "background" : "Fondo", + "background-settings" : "Configuración de fondo", + "background-type-image" : "Imagen", + "background-type-color" : "Color", + "image-url" : "URL de imagen", + "overlay" : "Superposición", + "enable-overlay" : "Habilitar superposición", + "blur" : "Desenfoque", + "preview" : "Vista previa" }, - "resource": { - "add": "Añadir Recurso", - "all-types": "Todos", - "copyId": "Copiar Id de recurso", - "delete": "Borrar recurso", - "delete-resource-text": "Atención, tras la confirmación el recurso será irrecuperable.", - "delete-resource-title": "Estás seguro de borrar el recurso '{{resourceTitle}}'?", - "delete-resources-action-title": "Borrar { count, plural, =1 {1 recurso} other {# recursos} }", - "delete-resources-text": "Los recursos serán borrados, incluso si están siendo usados en los perfiles de dispositivo.", - "delete-resources-title": "Estás seguro de borrar { count, plural, =1 {1 recurso} other {# recursos} }?", - "download": "Descargar recurso", - "drop-file": "Arrastra un fichero o haz click para seleccionar un fichero a subir.", - "drop-resource-file-or": "Arrastrar y soltar un fichero o", - "empty": "El recurso está vacío", - "file-name": "Nombre de fichero", - "idCopiedMessage": "El Id de recurso ha sido copiado al portapapeles", - "no-resource-matching": "No se han encontrado recursos que coincidan con '{{widgetsBundle}}'.", - "no-resource-text": "No se encontraron recursos", - "open-widgets-bundle": "Abrir paquete de widgets", - "resource": "Recurso", - "resource-library-details": "Detalles de recurso", - "resource-type": "Tipo de recurso", - "resources-library": "Librería de recursos", - "search": "Buscar recursos", - "selected-resources": "{ count, plural, =1 {1 recurso} other {# recursos} } seleccionados", - "system": "Sistema", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256", - "type": { - "jks": "JKS", - "js-module": "Módulo JS", - "lwm2m-model": "Modelo LWM2M", - "pkcs-12": "PKCS #12" - } + "bar-chart" : { + "bar-appearance" : "Apariencia de la barra", + "label-on-bar" : "Etiqueta en la barra", + "value-on-bar" : "Valor en la barra", + "bar-chart-style" : "Estilo de gráfico de barras", + "bar-axis" : "Eje de barra" }, - "rulechain": { - "rulechain": "Cadena de Regla", - "rulechain-events": "Eventos de cadena de reglas", - "rulechains": "Cadenas de Reglas", - "root": "Raíz", - "delete": "Borrar cadena de reglas", - "name": "Nombre", - "name-required": "Nombre requerido.", - "name-max-length": "Nombre debe ser menor de 256", - "description": "Descripción", - "add": "Añadir Cadena", - "set-root": "Hacer la cadena de reglas Raíz", - "set-root-rulechain-title": "¿Desea hacer la cadena de reglas '{{ruleChainName}}' de tipo raíz?", - "set-root-rulechain-text": "Tras la confirmación, la cadena de reglas se volverá raíz y manejará todos los mensajes de transporte entrantes.", - "delete-rulechain-title": "¿Quieres eliminar la cadena de reglas '{{ruleChainName}}'?", - "delete-rulechain-text": "Atención, tras la confirmación la cadena de reglas y todos los datos serán irrecuperables.", - "delete-rulechains-title": "¿Está seguro que quieres eliminar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }?", - "delete-rulechains-action-title": "Eliminar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }", - "delete-rulechains-text": "Atención, tras la confirmación todas las cadena de reglas seleccionadas y todos sus datos serán irrecuperables.", - "add-rulechain-text": "Añadir nueva cadena de reglas", - "no-rulechains-text": "Cadenas de reglas no encontradas", - "rulechain-details": "Detalles de la cadena de reglas", - "details": "Detalles", - "events": "Eventos", - "system": "Sistema", - "import": "Importar cadena de reglas", - "export": "Exportar cadena de reglas", - "export-failed-error": "No se puede exportar la cadena de reglas: {{error}}", - "create-new-rulechain": "Crear nueva cadena de reglas", - "rulechain-file": "Fichero de cadena de reglas", - "invalid-rulechain-file-error": "No se puede importar la cadena de reglas: Estructura de datos de la cadena de reglas inválida.", - "copyId": "Copiar ID de la cadena de reglas", - "idCopiedMessage": "ID de la cadena de reglas ha sido copiada al portapapeles", - "select-rulechain": "Seleccionar cadena de reglas", - "no-rulechains-matching": "No se encontraron cadenas de reglas que coincidan con '{{entity}}' .", - "rulechain-required": "Cadena de reglas requerida", - "management": "Gestión de reglas", - "debug-mode": "Modo Debug", - "search": "Buscar cadenas de reglas", - "selected-rulechains": "{ count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} } seleccionadas", - "open-rulechain": "Abrir cadena de reglas", - "edge-template-root": "Raíz de plantilla", - "assign-to-edge": "Asignar a Edge", - "edge-rulechain": "Cadena de reglas de Edge", - "unassign-rulechain-from-edge-text": "Después de la confirmación, la cadena de reglas quedará sin asignar y el Edge no podrá acceder a ella", - "unassign-rulechains-from-edge-title": "Estás seguro de desasignar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }?", - "unassign-rulechains-from-edge-text": "Después de la confirmación, todas las cadenas de reglas seleccionadas quedarán sin asignar y el Edge no podrá acceder a ellas", - "assign-rulechain-to-edge-title": "Asignar cadena (s) de reglas a Edge", - "assign-rulechain-to-edge-text": "Seleccione las cadenas de reglas para asignar al Edge", - "set-edge-template-root-rulechain": "Hacer raíz de plantilla de Edge de cadena de reglas", - "set-edge-template-root-rulechain-title": "¿Está seguro de que desea que la cadena de reglas '{{ruleChainName}}' sea la raíz de la plantilla de Edge?", - "set-edge-template-root-rulechain-text": "Después de la confirmación, la cadena de reglas se convertirá en la raíz de la plantilla de Edge y será la cadena de reglas raíz para los Edges recién creados.", - "invalid-rulechain-type-error": "No se puede importar la cadena de reglas: Tipo de cadena de reglas no válido. El tipo esperado es {{expectedRuleChainType}}", - "set-auto-assign-to-edge": "Asignar cadena de reglas a los Edges en la creación", - "set-auto-assign-to-edge-title": "¿Está seguro de que desea asignar automáticamente la cadena de reglas de Edge '{{ruleChainName}}' a los Edges en la creación?", - "set-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de Edge se asignará automáticamente a los Edges en la creación.", - "unset-auto-assign-to-edge": "Desmarcar asignar cadena de reglas a los Edges en la creación", - "unset-auto-assign-to-edge-title": "¿Está seguro de que desea anular la asignación de la cadena de reglas de Edge '{{ruleChainName}}' a los Edges en la creación?", - "unset-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de Edge ya no se asignará automáticamente a los Edges en la creación.", - "unassign-rulechain-title": "¿Está seguro de que desea desasignar la cadena de reglas '{{ruleChainName}}'?", - "unassign-rulechains": "Anular asignación de cadenas de reglas" + "polar-area-chart" : { + "polar-axis" : "Eje polar", + "start-angle" : "Ángulo de inicio", + "polar-area-chart-style" : "Estilo de gráfico de área polar" }, - "rulenode": { - "rule-node-events": "Eventos de nodo de reglas", - "details": "Detalles", - "events": "Eventos", - "search": "Buscar nodos", - "open-node-library": "Abrir librería de nodos", - "close-node-library": "Cerrar librería de nodos", - "add": "Añadir nodo de reglas", - "name": "Nombre", - "name-required": "El nombre es requerido.", - "name-max-length": "Nombre debe ser menor de 256", - "type": "Tipo", - "rule-node-description": "Descripción de nodo de reglas", - "delete": "Eliminar nodo de reglas", - "select-all-objects": "Seleccionar todos los nodos y conexiones", - "deselect-all-objects": "Deshacer selección de todos los nodos y conexiones", - "delete-selected-objects": "Eliminar nodos y conexiones seleccionados", - "delete-selected": "Eliminar seleccionado", - "create-nested-rulechain": "Crear cadena de reglas anidada", - "select-all": "Seleccionar todos", - "copy-selected": "Copiar seleccionado", - "deselect-all": "Deshacer selección de todos", - "rulenode-details": "Detalles del nodo de reglas", - "debug-mode": "Modo Debug", - "configuration": "Configuración", - "link": "Enlace", - "link-details": "Detalles del enlace del nodo de reglas", - "add-link": "Agregar enlace", - "link-label": "Etiqueta del enlace", - "link-label-required": "Etiqueta del enlace es requerida.", - "custom-link-label": "Etiqueta del enlace personalizada", - "custom-link-label-required": "Etiqueta del enlace personalizado es requerida.", - "link-labels": "Etiquetas del enlace", - "link-labels-required": "Etiquetas del enlace son requeridas.", - "no-link-labels-found": "Etiquetas de enlaces no encontradas", - "no-link-label-matching": "'{{label}}' no encontrada.", - "create-new-link-label": "Crear una nueva!", - "type-filter": "Filtro", - "type-filter-details": "Filtrar mensajes entrantes con las condiciones configuradas", - "type-enrichment": "Enriquecimiento", - "type-enrichment-details": "Agregar información adicional en mensajes de metadatos", - "type-transformation": "Transformación", - "type-transformation-details": "Cambiar carga útil del Mensaje y Metadatos", - "type-action": "Acción", - "type-action-details": "Ejecutar acción especial", - "type-external": "Externo", - "type-external-details": "Interactuar con sistemas externos", - "type-rule-chain": "Cadena de reglas", - "type-rule-chain-details": "Reenvíar los mensajes entrantes a la cadena de reglas especificada", - "type-flow": "Flujo", - "type-flow-details": "Organiza el flujo de mensajes", - "type-input": "Entrada", - "type-input-details": "Entrada lógica de la Cadena de Reglas, reenvíar los mensajes entrantes al siguiente nodo de regla relacionado.", - "type-unknown": "Desconocido", - "type-unknown-details": "Regla de nodo no resuelta", - "directive-is-not-loaded": "La directiva de configuración definida '{{directiveName}}' no está disponible.", - "ui-resources-load-error": "Error al cargar los recursos de configuración UI.", - "invalid-target-rulechain": "No se puede resolver la cadena de reglas objetivo!", - "test-script-function": "Probar Script de función", - "script-lang-java-script": "JavaScript", - "script-lang-tbel": "TBEL", - "message": "Mensaje", - "message-type": "Tipo de mensaje", - "select-message-type": "Seleccionar tipo de mensaje", - "message-type-required": "Tipo de mensaje es requerido", - "metadata": "Metadatos", - "metadata-required": "La entradas de metadatos no pueden estar vacías.", - "output": "Salida", - "test": "Test", - "help": "Ayuda", - "test-with-this-message": "{{test}} con este mensaje" + "battery-level" : { + "layout" : "Disposición", + "layout-vertical-solid" : "Vertical. Sólido", + "layout-horizontal-solid" : "Horizontal. Sólido", + "layout-vertical-divided" : "Vertical. Dividido", + "layout-horizontal-divided" : "Horizontal. Dividido", + "icon" : "Icono", + "value" : "Valor", + "auto-scale" : "Escalado automático", + "battery-level-color" : "Color del nivel de batería", + "battery-shape-color" : "Color de la forma de la batería", + "battery-level-card-style" : "Estilo de tarjeta de nivel de batería", + "sections-count" : "Número de secciones" }, - "timezone": { - "timezone": "Zona Horaria", - "select-timezone": "Seleccionar zona horaria", - "no-timezones-matching": "No hay zonas horarias que coincidan con '{{timezone}}'.", - "timezone-required": "Se requiere zona horaria.", - "browser-time": "Hora del navegador" + "signal-strength" : { + "value" : "Valor", + "last-update" : "Última actualización", + "no-signal" : "Sin señal", + "layout" : "Disposición", + "layout-wifi" : "Wi-Fi", + "layout-cellular-bar" : "Barra celular", + "icon" : "Icono", + "date" : "Fecha", + "active-bars-color" : "Color de barras activas", + "inactive-bars-color" : "Color de barras inactivas", + "signal-strength-card-style" : "Estilo de tarjeta de intensidad de señal", + "no-signal-rssi-value" : "Valor RSSI de \"Sin señal\"" }, - "queue": { - "queue-name": "Cola", - "no-queues-found": "No se encontraron colas.", - "no-queues-matching": "No se encontraron colas que coincidan con '{{queue}}'.", - "select-name": "Selecciona el nombre de la cola", - "name": "Nombre Cola", - "name-required": "Necesario especificar el nombre de cola", - "name-unique": "El nombre de cola ya existe!", - "name-pattern": "El nombre de cola contiene un carácter no ASCII, '.', '_' y '-'!", - "queue-required": "Cola requerida!", - "topic-required": "Topic cola requerido!", - "poll-interval-required": "Intervalo de obtención requerido!", - "poll-interval-min-value": "El intervalo no debe ser menor de 1", - "partitions-required": "Particiones requeridas!", - "partitions-min-value": "El valor de particion no debe ser menor de 1", - "pack-processing-timeout-required": "Timeout de procesamiento", - "pack-processing-timeout-min-value": "Timeout de procesamiento no puede ser menor de 1", - "batch-size-required": "Tamaño del lote requerido!", - "batch-size-min-value": "El valor de tamaño de lote no puede ser menor de 1", - "retries-required": "Reintentos requerido!", - "retries-min-value": "El valor de reintentos no puede ser negativo", - "failure-percentage-required": "Porcentaje de fallos requerido!", - "failure-percentage-min-value": "Porcentaje de fallos no puede ser menor de 0", - "failure-percentage-max-value": "Porcentaje de fallos no puede ser mayor de 100", - "pause-between-retries-required": "Pausa entre reintentos requerido!", - "pause-between-retries-min-value": "Pausa mínima entre reintentos no puede ser menor de 1", - "max-pause-between-retries-required": "Pausa máxima entre reintentos requerido!", - "max-pause-between-retries-min-value": "Pausa máxima entre reintentos no puede ser menor de 1", - "submit-strategy-type-required": "Estrategia de envío requerida!", - "processing-strategy-type-required": "Estrategia de procesamiento requerida!", - "queues": "Colas", - "selected-queues": "{ count, plural, =1 {1 cola} other {# colas} } seleccionadas", - "delete-queue-title": "Estás seguro de borrar la cola '{{queueName}}'?", - "delete-queues-title": "Estás seguro de borrar { count, plural, =1 {1 cola} other {# colas} }?", - "delete-queue-text": "Atención, tras la confirmacion la cola y todos sus datos relacionados serán irrecuperables.", - "delete-queues-text": "Atención, tras la confirmación todas las colas se borrarán y no serán accesibles.", - "search": "Buscar cola", - "add" : "Añadir cola", - "details": "Detalles cola", - "topic": "Topic", - "submit-settings": "Ajustes de envío", - "submit-strategy": "Estrategia de envío", - "grouping-parameter": "Parámetros de agrupado", - "processing-settings": "Ajustes de procesamiento", - "processing-strategy": "Estrategia de procesamiento", - "retries-settings": "Ajustes de reintentos", - "polling-settings": "Ajustes de obtención", - "batch-processing": "Procesado de lotes", - "poll-interval": "Intervalo de obtención", - "partitions": "Particiones", - "immediate-processing": "Procesado inmediato", - "consumer-per-partition": "Consumidores por partición", - "consumer-per-partition-hint": "Activar consumidores separados para cada partición", - "processing-timeout": "Timeout de procesamiento, ms", - "batch-size": "Tamaño de lote", - "retries": "Reintentos (0 - ilimitados)", - "failure-percentage": "Porcentaje de fallos", - "pause-between-retries": "Pausa entre reintentos", - "max-pause-between-retries": "Pausa máxima entre reintentos", - "delete": "Borrar cola", - "copyId": "Copiar Id de cola", - "idCopiedMessage": "La Id de cola se ha copiado al portapapeles", - "description": "Descripción", - "description-hint": "Este texto se mostrará en la descripción de la cola, en lugar de la estrategia seleccionada", - "alt-description": "Estrategia de envío: {{submitStrategy}}, Estrategia de procesamiento: {{processingStrategy}}", - "custom-properties": "Propiedades personalizadas", - "custom-properties-hint": "Propiedades de creación de tema en colas personalizadas, p.e. 'retention.ms:604800000;retention.bytes:1048576000'", - "strategies": { - "sequential-by-originator-label": "Secuencial por iniciador", - "sequential-by-originator-hint": "El nuevo mensaje por ejemplo dispositivo A, no se enviará hasta que el mensaje anterior del dispositivo A sea admitido/procesado", - "sequential-by-tenant-label": "Secuencial por propietario", - "sequential-by-tenant-hint": "El nuevo mensaje, por ejemplo Propietario A, no se enviará hasta que el mensaje anterior del Propietario A sea admitido/procesado", - "sequential-label": "Secuencial", - "sequential-hint": "El nuevo mensaje no se enviará hasta que el mensaje anterior sea admitido/procesado", - "burst-label": "Ráfaga (Burst)", - "burst-hint": "Todos los mensajes se enviarán hacia las cadenas de reglas en el órden que lleguen", - "batch-label": "Lotes (Batch)", - "batch-hint": "El nuevo lote, no se enviará hasta que el anterior lote sea admitido/procesado", - "skip-all-failures-label": "Omitir todos los fallos", - "skip-all-failures-hint": "Ignorar todos los fallos", - "skip-all-failures-and-timeouts-label": "Omitir todos los fallos y timeouts", - "skip-all-failures-and-timeouts-hint": "Ignorar todos los fallos y timeouts", - "retry-all-label": "Reintentar todos", - "retry-all-hint": "Reintentar todos los mensajes del lote de procesamiento", - "retry-failed-label": "Reintentar fallidos", - "retry-failed-hint": "Reintentar todos los mensajes fallidos del lote de procesamiento", - "retry-timeout-label": "Timeout de reintentos", - "retry-timeout-hint": "Reintentar todos los mensajes que den timeout del lote de procesamiento", - "retry-failed-and-timeout-label": "Reintentar fallidos y timeouts", - "retry-failed-and-timeout-hint": "Reintentar todos los mensajes fallidos y que den timeout del lote de procesamiento" - } + "status-widget" : { + "behavior" : "Comportamiento", + "layout" : "Disposición", + "layout-default" : "Por defecto", + "layout-center" : "Centro", + "layout-icon" : "Icono", + "on" : "Encendido", + "off" : "Apagado", + "label" : "Etiqueta", + "status" : "Estado", + "icon" : "Icono", + "color-palette" : "Paleta de colores", + "disabled-color-palette" : "Paleta de colores deshabilitada", + "primary" : "Primario", + "primary-color-hint" : "Color del icono y la etiqueta", + "secondary" : "Secundario", + "secondary-color-hint" : "Color del estado", + "background" : "Fondo" }, - "server-error": { - "general": "Error general de servidor", - "authentication": "Error de autenticación", - "jwt-token-expired": "Token JWT expirado", - "tenant-trial-expired": "Prueba propietario expirada", - "credentials-expired": "Credenciales expirados", - "permission-denied": "Permiso denegado", - "invalid-arguments": "Argumentos inválidos", - "bad-request-params": "Parámetros de consulta no válidos", - "item-not-found": "Item no encontrado", - "too-many-requests": "Demasiadas solicitudes", - "too-many-updates": "Demasiadas actualizaciones" + "chart" : { + "common-settings" : "Configuraciones comunes", + "enable-stacking-mode" : "Habilitar modo de apilamiento", + "selection" : "Selección de rango de tiempo", + "enable-selection-mode" : "Habilitar modo de selección", + "line-shadow-size" : "Tamaño de sombra de línea", + "display-smooth-lines" : "Mostrar líneas suaves (curvas)", + "default-bar-width" : "Ancho de barra por defecto para datos no agregados (milisegundos)", + "bar-alignment" : "Alineación de barras", + "bar-alignment-left" : "Izquierda", + "bar-alignment-right" : "Derecha", + "bar-alignment-center" : "Centro", + "default-font" : "Fuente por defecto", + "default-font-size" : "Tamaño de fuente por defecto", + "default-font-color" : "Color de fuente por defecto", + "thresholds-line-width" : "Ancho de línea por defecto para todos los umbrales", + "tooltip-settings" : "Configuraciones del tooltip", + "tooltip" : "Tooltip", + "show-tooltip" : "Mostrar tooltip", + "hover-individual-points" : "Pasar el cursor sobre puntos individuales", + "show-cumulative-values" : "Mostrar valores acumulados en modo apilado", + "hide-zero-false-values" : "Ocultar valores cero/falsos del tooltip", + "tooltip-value-format-function" : "Función de formato de valor del tooltip", + "grid-settings" : "Configuraciones de cuadrícula", + "show-vertical-lines" : "Mostrar líneas verticales", + "show-horizontal-lines" : "Mostrar líneas horizontales", + "grid-outline-border-width" : "Ancho del borde de la cuadrícula (px)", + "primary-color" : "Color primario", + "background-color" : "Color de fondo", + "ticks-color" : "Color de las marcas", + "xaxis-settings" : "Configuraciones del eje X", + "axis-title" : "Título del eje", + "xaxis-tick-labels-settings" : "Configuraciones de etiquetas de marcas del eje X", + "show-tick-labels" : "Mostrar etiquetas del eje", + "yaxis-settings" : "Configuraciones del eje Y", + "min-scale-value" : "Valor mínimo en la escala", + "max-scale-value" : "Valor máximo en la escala", + "yaxis-tick-labels-settings" : "Configuraciones de etiquetas de marcas del eje Y", + "tick-step-size" : "Tamaño del paso entre marcas", + "number-of-decimals" : "Número de decimales a mostrar", + "ticks-formatter-function" : "Función de formateo de marcas", + "comparison-settings" : "Configuraciones de comparación", + "enable-comparison" : "Habilitar comparación", + "time-for-comparison" : "Periodo de comparación", + "time-for-comparison-previous-interval" : "Intervalo anterior (por defecto)", + "time-for-comparison-days" : "Hace un día", + "time-for-comparison-weeks" : "Hace una semana", + "time-for-comparison-months" : "Hace un mes", + "time-for-comparison-years" : "Hace un año", + "time-for-comparison-custom-interval" : "Intervalo personalizado", + "custom-interval-value" : "Valor de intervalo personalizado (ms)", + "comparison-x-axis-settings" : "Configuraciones del eje X para comparación", + "axis-position" : "Posición del eje", + "axis-position-top" : "Superior (por defecto)", + "axis-position-bottom" : "Inferior", + "custom-legend-settings" : "Configuraciones personalizadas de la leyenda", + "enable-custom-legend" : "Habilitar leyenda personalizada (permite usar atributos/series temporales en etiquetas de clave)", + "key-name" : "Nombre de la clave", + "key-name-required" : "El nombre de la clave es obligatorio", + "key-type" : "Tipo de clave", + "key-type-attribute" : "Atributo", + "key-type-timeseries" : "Serie temporal", + "label-keys-list" : "Lista de claves para usar en etiquetas", + "no-label-keys" : "No se configuraron claves", + "add-label-key" : "Agregar nueva clave", + "line-width" : "Ancho de línea", + "color" : "Color", + "data-is-hidden-by-default" : "Los datos están ocultos por defecto", + "disable-data-hiding" : "Desactivar ocultamiento de datos", + "remove-from-legend" : "Eliminar clave de la leyenda", + "exclude-from-stacking" : "Excluir del apilamiento (disponible en modo \"Apilamiento\")", + "line-settings" : "Configuraciones de línea", + "show-line" : "Mostrar línea", + "fill-line" : "Rellenar línea", + "fill-line-opacity" : "Opacidad del relleno", + "points-settings" : "Configuraciones de puntos", + "show-points" : "Mostrar puntos", + "points-line-width" : "Ancho de línea de los puntos", + "points-radius" : "Radio de los puntos", + "point-shape" : "Forma del punto", + "point-shape-circle" : "Círculo", + "point-shape-cross" : "Cruz", + "point-shape-diamond" : "Diamante", + "point-shape-square" : "Cuadrado", + "point-shape-triangle" : "Triángulo", + "point-shape-custom" : "Función personalizada", + "point-shape-draw-function" : "Función de dibujo de forma de punto", + "show-separate-axis" : "Mostrar eje separado", + "axis-position-left" : "Izquierda", + "axis-position-right" : "Derecha", + "thresholds" : "Umbrales", + "no-thresholds" : "No se configuraron umbrales", + "add-threshold" : "Agregar umbral", + "show-values-for-comparison" : "Mostrar valores históricos para comparación", + "comparison-values-label" : "Etiqueta de valores históricos", + "comparison-line-color" : "Color de la línea de comparación", + "threshold-settings" : "Configuraciones de umbral", + "use-as-threshold" : "Usar el valor de la clave como umbral", + "threshold-line-width" : "Ancho de línea del umbral", + "threshold-color" : "Color del umbral", + "common-pie-settings" : "Configuraciones comunes del gráfico circular", + "radius" : "Radio", + "inner-radius" : "Radio interno", + "tilt" : "Inclinación", + "common-pie-settings-range-error" : "El valor debe estar entre 0 y 1", + "stroke-settings" : "Configuraciones de contorno", + "width-pixels" : "Ancho (píxeles)", + "show-labels" : "Mostrar etiquetas", + "animation-settings" : "Configuraciones de animación", + "animated-pie" : "Habilitar animación del gráfico circular (experimental)", + "border-settings" : "Configuraciones de borde", + "border-width" : "Ancho del borde", + "border-color" : "Color del borde", + "legend-settings" : "Configuraciones de la leyenda", + "display-legend" : "Mostrar leyenda", + "labels-font-color" : "Color de fuente de las etiquetas", + "series" : "Series", + "add-series" : "Agregar serie", + "series-settings" : "Configuraciones de la serie", + "remove-series" : "Eliminar serie", + "no-series" : "No se han configurado series", + "no-series-error" : "Se debe especificar al menos una serie", + "chart-appearance" : "Apariencia del gráfico", + "vertical-grid-lines" : "Líneas de cuadrícula verticales", + "horizontal-grid-lines" : "Líneas de cuadrícula horizontales", + "chart-background" : "Fondo del gráfico", + "grid-lines-color" : "Color de las líneas de cuadrícula", + "border" : "Borde", + "axis" : "Eje", + "vertical-axis" : "Eje vertical", + "ticks" : "Marcas", + "horizontal-axis" : "Eje horizontal", + "shape-empty-circle" : "Círculo vacío", + "shape-circle" : "Círculo", + "shape-rect" : "Rectángulo", + "shape-round-rect" : "Rectángulo redondeado", + "shape-triangle" : "Triángulo", + "shape-diamond" : "Diamante", + "shape-pin" : "Pin", + "shape-arrow" : "Flecha", + "shape-none" : "Ninguno", + "line-type-solid" : "Sólido", + "line-type-dashed" : "Discontinuo", + "line-type-dotted" : "Punteado", + "label-position-top" : "Arriba", + "label-position-bottom" : "Abajo", + "label-position-outside" : "Fuera", + "label-position-inside" : "Dentro", + "fill" : "Relleno", + "fill-type-none" : "Ninguno", + "fill-type-solid" : "Sólido", + "fill-type-opacity" : "Opacidad", + "fill-type-gradient" : "Gradiente", + "background" : "Fondo", + "opacity" : "Opacidad", + "gradient-stops" : "Puntos de detención del gradiente", + "gradient-start" : "inicio", + "gradient-end" : "fin", + "animation" : { + "animation" : "Animación", + "animation-threshold" : "Umbral de animación", + "animation-duration" : "Duración de la animación", + "animation-easing" : "Facilitación de la animación", + "animation-delay" : "Retardo de la animación", + "update-animation-duration" : "Duración de la animación de actualización", + "update-animation-easing" : "Facilitación de la animación de actualización", + "update-animation-delay" : "Retardo de la animación de actualización" + }, + "chart-axis" : { + "scale" : "Escala", + "scale-min" : "mín", + "scale-max" : "máx", + "scale-auto" : "Auto" + }, + "bar" : { + "show-border" : "Mostrar borde", + "border-width" : "Ancho del borde", + "border-radius" : "Radio del borde", + "bar-width" : "Ancho de la barra", + "label" : "Etiqueta", + "label-hint" : "Mostrar etiqueta sobre la barra.", + "series-label-hint" : "Mostrar etiqueta con valor sobre la barra.", + "label-background" : "Fondo de la etiqueta" + } }, - "tenant": { - "tenant": "Propietario", - "tenants": "Propietarios", - "management": "Gestión de Propietarios", - "add": "Agregar propietario", - "admins": "Admins", - "manage-tenant-admins": "Gestionar administradores de propietario", - "delete": "Eliminar propietario", - "add-tenant-text": "Agregar nuevo propietario", - "no-tenants-text": "Ningún propietario encontrado", - "tenant-details": "Detalles del propietario", - "title-max-length": "Título debe ser menor de 256", - "delete-tenant-title": "¿Quieres eliminar el propietario '{{tenantTitle}}'?", - "delete-tenant-text": "Atención, tras la confirmación el propietario será eliminado y la información relacionada será irrecuperable.", - "delete-tenants-title": "¿Quieres eliminar { count, plural, =1 {1 propietario} other {# propietarios} }?", - "delete-tenants-action-title": "Eliminar { count, plural, =1 {1 propietario} other {# propietarios} }", - "delete-tenants-text": "Atención, tras la confirmación los propietarios seleccionados serán eliminados y la información relacionada será irrecuperable.", - "title": "Título", - "title-required": "Título requerido.", - "description": "Descripción", - "details": "Detalles", - "events": "Eventos", - "copyId": "Copiar ID de propietario", - "idCopiedMessage": "El ID de propietario se ha copiado al portapapeles", - "select-tenant": "Seleccionar propietario", - "no-tenants-matching": "No hay propietarios que coincidan con '{{entity}}' .", - "tenant-required": "Propietario requerido", - "search": "Buscar propietarios", - "selected-tenants": "{ count, plural, =1 {1 propietario} other {# propietarios} } seleccionados", - "isolated-tb-rule-engine": "Procesando en contenedor Motor de Reglas aislado", - "isolated-tb-rule-engine-details": "Requiere microservicios separados por propietario aislado" + "color" : { + "color-settings" : "Configuración de color", + "color-type-constant" : "Constante", + "color-type-gradient" : "Gradiente", + "color-type-range" : "Rango", + "color-type-function" : "Función", + "color" : "Color", + "value-range" : "Rango de valores", + "from" : "Desde", + "to" : "Hasta", + "color-function" : "Función de color", + "copy-color-settings-from" : "Copiar configuración de color desde", + "copy-from" : "Copiar desde", + "settings-type" : "Tipo de configuración", + "basic-mode" : "Básico", + "advanced-mode" : "Avanzado", + "entity-alias" : "Alias de entidad", + "entity-attribute" : "Atributo de entidad", + "gradient-color" : "Color de gradiente", + "gradient-color-min" : "Color", + "gradient-start" : "Color inicial del gradiente", + "gradient-start-min" : "Inicio", + "gradient-end" : "Color final del gradiente", + "gradient-end-min" : "Fin", + "start-value" : "Valor inicial", + "end-value" : "Valor final", + "gradient-type" : "Tipo de gradiente" }, - "tenant-profile": { - "tenant-profile": "Perfil de propietario", - "tenant-profiles": "Perfiles de propietarios", - "add": "Añadir perfil de propietario", - "add-profile": "Añadir perfil", - "edit": "Editar perfil de propietario", - "tenant-profile-details": "Detalles perfil de propietario", - "no-tenant-profiles-text": "No se encontraron perfiles de propietario", - "name-max-length": "El nombre debe ser menor de 256", - "search": "Buscar perfiles de propietario", - "selected-tenant-profiles": "{ count, plural, =1 {1 perfil de propietario} other {# perfiles de propietario} } seleccionados", - "no-tenant-profiles-matching": "No se han encontrado perfiles de propietario que coincidan con '{{entity}}'.", - "tenant-profile-required": "Se requiere perfil de propietario", - "idCopiedMessage": "El ID de perfil de propietario se ha copiado al portapapeles", - "set-default": "Hacer perfil propietario por defecto", - "delete": "Borrar perfil", - "copyId": "Copiar ID de perfil", - "name": "Nombre", - "name-required": "Se requiere nombre.", - "data": "Datos de perfil", - "profile-configuration": "Configuración de perfil", - "description": "Descripción", - "default": "Defecto", - "delete-tenant-profile-title": "Eliminar el perfil propietario '{{tenantProfileName}}'?", - "delete-tenant-profile-text": "Atención, tras la confirmación, el perfil de propietario será borrado y su información relacionada será irrecuperable.", - "delete-tenant-profiles-title": "Eliminar { count, plural, =1 {1 perfil propietario} other {# perfiles propietarios} }?", - "delete-tenant-profiles-text": "Atención, tras la confirmación, los perfiles seleccionados se eliminarán y su información relacionada será irrecuperable.", - "set-default-tenant-profile-title": "Quieres hacer el perfil propietario '{{tenantProfileName}}' por defecto?", - "set-default-tenant-profile-text": "Tras la confirmación, el perfil propietario será marcado por defecto y será usado por los nuevos perfiles propietarios que no tengan perfil específico.", - "no-tenant-profiles-found": "No se encontraron perfiles de propietario.", - "create-new-tenant-profile": "Crear un nuevo perfil!", - "create-tenant-profile": "Crear un nuevo perfil de propietario", - "import": "Importar perfil de propietario", - "export": "Exportar perfil de propietario", - "export-failed-error": "No se ha podido exportar el perfil de propietario: {{error}}", - "tenant-profile-file": "Archivo de perfil de propietario", - "invalid-tenant-profile-file-error": "No se ha podido importar el perfil de propietario: Estructura de datos inválida.", - "advanced-settings": "Ajustes avanzados", - "entities": "Entidades", - "rule-engine": "Cadena de reglas", - "time-to-live": "Time-to-live", - "alarms-and-notifications": "Alarmas y notificaciones", - "ota-files-in-bytes": "Tamaño de fichero OTA en bytes", - "ws-title": "WS", - "unlimited": "(0 - ilimitado)", - "maximum-devices": "Nº Máximo de dispositivos (0 - sin límite)", - "maximum-devices-required": "Nº Máximo de dispositivos requerido.", - "maximum-devices-range": "Nº Máximo de dispositivos no puede ser negativo", - "maximum-assets": "Nº Máximo de activos (0 - sin límite)", - "maximum-assets-required": "Nº Máximo de activos requerido.", - "maximum-assets-range": "Nº Máximo de activos no puede ser negativo", - "maximum-customers": "Nº Máximo de clientes (0 - sin límite)", - "maximum-customers-required": "Nº Máximo de clientes requerido.", - "maximum-customers-range": "Nº Máximo de clientes no puede ser negativo", - "maximum-users": "Nº Máximo de usuarios (0 - sin límite)", - "maximum-users-required": "Nº Máximo de usuarios requerido.", - "maximum-users-range": "Nº Máximo de usuarios no puede ser negativo", - "maximum-dashboards": "Nº Máximo de paneles (0 - sin límite)", - "maximum-dashboards-required": "Nº Máximo de paneles requerido.", - "maximum-dashboards-range": "Nº Máximo de paneles no puede ser negativo", - "maximum-edges": "Nº Máximo de bordes (0 - sin límite)", - "maximum-edges-required": "Nº Máximo de bordes requerido.", - "maximum-edges-range": "Nº Máximo de bordes no puede ser negativo", - "maximum-rule-chains": "Nº Máximo de cadenas de reglas (0 - sin límite)", - "maximum-rule-chains-required": "Nº Máximo de cadenas de reglas requerido.", - "maximum-rule-chains-range": "Nº Máximo de cadenas de reglas no puede ser negativo", - "maximum-resources-sum-data-size": "Tamaño máximo de ficheros de recursos en bytes (0 - sin límite)", - "maximum-resources-sum-data-size-required": "Tamaño máximo de ficheros de recursos requerido.", - "maximum-resources-sum-data-size-range": "Tamaño máximo de ficheros de recursos no puede ser negativo", - "maximum-ota-packages-sum-data-size": "Tamaño máximo de paquetes OTA en bytes (0 - sin límite)", - "maximum-ota-package-sum-data-size-required": "Tamaño máximo de paquetes OTA requerido.", - "maximum-ota-package-sum-data-size-range": "Tamaño máximo de paquetes OTA no puede ser negativo", - "rest-requests-for-tenant": "Consultas REST por propietario", - "transport-tenant-telemetry-msg-rate-limit": "Tasa de mensajes de telemetría por propietario.", - "transport-tenant-telemetry-data-points-rate-limit": "Tasa de datapoints por propietario.", - "transport-device-msg-rate-limit": "Tasa de mensajes de dispositivo.", - "transport-device-telemetry-msg-rate-limit": "Tasa de mensajes de telemetría de dispositivo.", - "transport-device-telemetry-data-points-rate-limit": "Tasa de datapoints de telemetría de dispositivo.", - "tenant-entity-export-rate-limit": "Tasa de creación de versión de entidades", - "tenant-entity-import-rate-limit": "Tasa de carga de versión de entidades", - "tenant-notification-request-rate-limit": "Consultas de notificaciones", - "tenant-notification-requests-per-rule-rate-limit": "Consultas de notificaciones por regla de notificación", - "max-transport-messages": "Nº Máximo de mensajes de transporte (0 - sin límite)", - "max-transport-messages-required": "Nº Máximo de mensajes de transporte requerido.", - "max-transport-messages-range": "Nº Máximo de mensajes de transporte no puede ser negativo", - "max-transport-data-points": "Nº Máximo de datapoints transporte (0 - sin límite)", - "max-transport-data-points-required": "Nº Máximo de datapoints transporte requerido.", - "max-transport-data-points-range": "Nº Máximo de datapoints transporte no puede ser negativo", - "max-r-e-executions": "Nº Máximo de ejecuciones de motor de reglas (0 - sin límite)", - "max-r-e-executions-required": "Nº Máximo de ejecuciones de motor de reglas requerido.", - "max-r-e-executions-range": "Nº Máximo de ejecuciones de motor de reglas no puede ser negativo", - "max-j-s-executions": "Nº Máximo de ejecuciones JavaScript (0 - sin límite)", - "max-j-s-executions-required": "Nº Máximo de ejecuciones JavaScript requerido.", - "max-j-s-executions-range": "Nº Máximo de ejecuciones JavaScript no puede ser negativo", - "max-d-p-storage-days": "Nº Máximo de días a grabar en datapoints (0 - sin límite)", - "max-d-p-storage-days-required": "Nº Máximo de días requerido.", - "max-d-p-storage-days-range": "Nº Máximo de días no puede ser negativo", - "default-storage-ttl-days": "Días por defecto grabado TTL (0 - sin límite)", - "default-storage-ttl-days-required": "Días por defecto TTL requerido.", - "default-storage-ttl-days-range": "Días por defecto TTL no puede ser negativo", - "alarms-ttl-days": "Días de TTL alarmas (0 - sin límite)", - "alarms-ttl-days-required": "Días de TTL alarmas requerido", - "alarms-ttl-days-days-range": "Días de TTL alarmas no puede ser negativo", - "rpc-ttl-days": "Días TTL RPC (0 - sin límite)", - "rpc-ttl-days-required": "Días TTL RPC requerido", - "rpc-ttl-days-days-range": "Días TTL RPC no puede ser negativo", - "max-rule-node-executions-per-message": "Nº Máximo de ejecuciones (cadena de reglas) por mensaje (0 - sin límite)", - "max-rule-node-executions-per-message-required": "Nº Máximo de ejecuciones por mensaje requerido.", - "max-rule-node-executions-per-message-range": "Nº Máximo de ejecuciones por mensaje no puede ser negativo", - "max-emails": "Nº Máximo de emails (0 - sin límite)", - "max-emails-required": "Nº Máximo de emails requerido.", - "max-emails-range": "Nº Máximo de emails no puede ser negativo", - "sms-enabled": "SMS Activados", - "max-sms": "Nº Máximo de mensajes SMS (0 - sin límite)", - "max-sms-required": "Nº Máximo de mensajes SMS requerido.", - "max-sms-range": "Nº Máximo de mensajes SMS no puede ser negativo", - "max-created-alarms": "Nº Máximo de alarmas creadas (0 - sin límite)", - "max-created-alarms-required": "Nº Máximo de alarmas creadas requerido.", - "max-created-alarms-range": "Nº Máximo de alarmas creadas no puede ser negativo", - "no-queue": "No hay cola configurada", - "add-queue": "Añadir cola", - "queues-with-count": "({{count}}) Colas", - "tenant-rest-limits": "Límite de frecuencia de solicitudes REST por propietario", - "customer-rest-limits": "Límite de frecuencia de solicitudes REST por cliente", - "incorrect-pattern-for-rate-limits": "El formato correcto son pares de valores separados por el símbolo : capacidad y período (en segundos), ejemplo: 100:1,2000:60", - "too-small-value-zero": "El valor debe ser mayor de 0", - "too-small-value-one": "El valor debe ser mayor de 1", - "queue-size-is-limited-by-system-configuration": "El tamaño de la cola, también se limita por la configuración del sistema.", - "cassandra-tenant-limits-configuration": "Límite en tasa de consulta de Cassandra por propietario", - "ws-limit-max-sessions-per-tenant": "Número máximo de sesiones WS por propietario", - "ws-limit-max-sessions-per-customer": "Número máximo de sesiones WS por cliente", - "ws-limit-max-sessions-per-regular-user": "Número máximo de sesiones por usuario regular", - "ws-limit-max-sessions-per-public-user": "Número máximo de sesiones WS por usuario público", - "ws-limit-queue-per-session": "Tamaño máximo de cola de mensaje WS por sesión", - "ws-limit-max-subscriptions-per-tenant": "Número máximo de subscripciones WS por administrador propietario", - "ws-limit-max-subscriptions-per-customer": "Número máximo de subscripciones de WS por cliente", - "ws-limit-max-subscriptions-per-regular-user": "Número máximo de subscripciones de WS por usuario regular", - "ws-limit-max-subscriptions-per-public-user": "Número máximo de subscripciones de WS por usuario público", - "ws-limit-updates-per-session": "Frecuencia de actualizaciones WS por sesión", - "rate-limits": { - "add-limit": "Añadir límite", - "advanced-settings": "Ajustes avanzados", - "edit-limit": "Editar límite", - "but-less-than": "pero menor que", - "edit-transport-tenant-msg-title": "Editar límite de mensajes de transporte (Propietario)", - "edit-transport-tenant-telemetry-msg-title": "Editar límite de mensajes de telemetría (Propietario)", - "edit-transport-tenant-telemetry-data-points-title": "Editar límite de puntos de datos de transporte (Propietario)", - "edit-transport-device-msg-title": "Editar tasa de mensajes de dispositivo", - "edit-transport-device-telemetry-msg-title": "Editar tasa de mensajes de transporte de dispositivo", - "edit-transport-device-telemetry-data-points-title": "Editar límite de puntos de datos de transporte (Dispositivo)", - "edit-tenant-rest-limits-title": "Editar límite de solicitudes REST por propietario", - "edit-customer-rest-limits-title": "Editar límite de solicitudes REST por cliente", - "edit-ws-limit-updates-per-session-title": "Editar límite de actualizaciones WS por sesión", - "edit-cassandra-tenant-limits-configuration-title": "Editar límites de consultas en BD Cassandra por propietario", - "edit-tenant-entity-export-rate-limit-title": "Editar tasa límite de creación de versiones por entidad", - "edit-tenant-entity-import-rate-limit-title": "Editar tasa límite de carga de versión de entidad", - "edit-tenant-notification-request-rate-limit-title": "Editar tasa límite de solicitudes de notificaciones", - "edit-tenant-notification-requests-per-rule-rate-limit-title": "Editar tasa límite de solicitudes en reglas por noticación", - "messages-per": "mensajes por", - "not-set": "No configurado", - "number-of-messages": "Número de mensajes", - "number-of-messages-required": "Se requiere número de mensajes.", - "number-of-messages-min": "El valor mínimo es 1.", - "preview": "Previsualizar", - "per-seconds": "Por segundos", - "per-seconds-required": "Se requiere tasa de tiempo.", - "per-seconds-min": "El valor mínimo es 1.", - "rate-limits": "Límites de tasa", - "remove-limit": "Quitar límite", - "transport-tenant-msg": "Mensajes de transporte (propietario)", - "transport-tenant-telemetry-msg": "Mensajes de telemetría (propietario)", - "transport-tenant-telemetry-data-points": "Puntos de datos de telemetría (propietario)", - "transport-device-msg": "Mensajes de transporte (dispositivo)", - "transport-device-telemetry-msg": "Mensajes de telemetría (dispositivo)", - "transport-device-telemetry-data-points": "Puntos de datos de telemetría (dispositivo)", - "sec": "sec" - } + "dashboard-state" : { + "dashboard-state-settings" : "Configuración del estado del tablero", + "dashboard-state" : "ID del estado del tablero", + "autofill-state-layout" : "Autocompletar la altura del diseño del estado por defecto", + "default-margin" : "Margen por defecto de los widgets", + "default-background-color" : "Color de fondo por defecto", + "sync-parent-state-params" : "Sincronizar parámetros de estado con el tablero principal" }, - "timeinterval": { - "seconds-interval": "{ seconds, plural, =1 {1 segundo} other {# segundos} }", - "minutes-interval": "{ minutes, plural, =1 {1 minuto} other {# minutos} }", - "hours-interval": "{ hours, plural, =1 {1 hora} other {# horas} }", - "days-interval": "{ days, plural, =1 {1 día} other {# días} }", - "days": "Días", - "hours": "Horas", - "minutes": "Minutos", - "seconds": "Segundos", - "advanced": "Avanzado", - "predefined": { - "yesterday": "Ayer", - "day-before-yesterday": "Anteayer", - "this-day-last-week": "Hoy hace una semana", - "previous-week": "Semana anterior (Dom - Sáb)", - "previous-week-iso": "Semana anterior (Lun - Dom)", - "previous-month": "Mes anterior", - "previous-quarter": "Anterior trimestre", - "previous-half-year": "Anterior semestre", - "previous-year": "Año anterior", - "current-hour": "Hora actual", - "current-day": "Día actual", - "current-day-so-far": "Día actual hasta ahora", - "current-week": "Semana actual (Dom - Sáb)", - "current-week-iso": "Semana actual (Lun - Dom)", - "current-week-so-far": "Semana actual hasta hoy (Dom - Sáb)", - "current-week-iso-so-far": "Semana actual hasta hoy (Lun - Dom)", - "current-month": "Mes actual", - "current-month-so-far": "Mes actual hasta hoy", - "current-quarter": "Trimestre actual", - "current-quarter-so-far": "Trimestre actual hasta hoy", - "current-half-year": "Semestre en curso", - "current-half-year-so-far": "Semestre en curso hasta hoy", - "current-year": "Año actual", - "current-year-so-far": "Año actual hasta ahora" - } + "date-range-navigator" : { + "date-range-picker-settings" : "Configuración del selector de rango de fechas", + "hide-date-range-picker" : "Ocultar selector de rango de fechas", + "picker-one-panel" : "Selector de rango de fechas con un panel", + "picker-auto-confirm" : "Confirmación automática del selector de rango de fechas", + "picker-show-template" : "Mostrar plantilla del selector de rango de fechas", + "first-day-of-week" : "Primer día de la semana", + "interval-settings" : "Configuración del intervalo", + "hide-interval" : "Ocultar intervalo", + "initial-interval" : "Intervalo inicial", + "interval-hour" : "Hora", + "interval-day" : "Día", + "interval-week" : "Semana", + "interval-two-weeks" : "2 semanas", + "interval-month" : "Mes", + "interval-three-months" : "3 meses", + "interval-six-months" : "6 meses", + "step-settings" : "Configuración del paso", + "hide-step-size" : "Ocultar tamaño del paso", + "initial-step-size" : "Tamaño de paso inicial", + "hide-labels" : "Ocultar etiquetas", + "use-session-storage" : "Usar almacenamiento de sesión", + "localizationMap" : { + "Sun" : "Dom", + "Mon" : "Lun", + "Tue" : "Mar", + "Wed" : "Mié", + "Thu" : "Jue", + "Fri" : "Vie", + "Sat" : "Sáb", + "Jan" : "Ene", + "Feb" : "Feb", + "Mar" : "Mar", + "Apr" : "Abr", + "May" : "May", + "Jun" : "Jun", + "Jul" : "Jul", + "Aug" : "Ago", + "Sep" : "Sep", + "Oct" : "Oct", + "Nov" : "Nov", + "Dec" : "Dic", + "January" : "Enero", + "February" : "Febrero", + "March" : "Marzo", + "April" : "Abril", + "June" : "Junio", + "July" : "Julio", + "August" : "Agosto", + "September" : "Septiembre", + "October" : "Octubre", + "November" : "Noviembre", + "December" : "Diciembre", + "Custom Date Range" : "Rango de fechas personalizado", + "Date Range Template" : "Plantilla de rango de fechas", + "Today" : "Hoy", + "Yesterday" : "Ayer", + "This Week" : "Esta semana", + "Last Week" : "La semana pasada", + "This Month" : "Este mes", + "Last Month" : "El mes pasado", + "Year" : "Año", + "This Year" : "Este año", + "Last Year" : "El año pasado", + "Date picker" : "Selector de fecha", + "Hour" : "Hora", + "Day" : "Día", + "Week" : "Semana", + "2 weeks" : "2 semanas", + "Month" : "Mes", + "3 months" : "3 meses", + "6 months" : "6 meses", + "Custom interval" : "Intervalo personalizado", + "Interval" : "Intervalo", + "Step size" : "Tamaño del paso", + "Ok" : "Aceptar" + } }, - "timeunit": { - "milliseconds": "Milisegundos", - "seconds": "Segundos", - "minutes": "Minutos", - "hours": "Horas", - "days": "Días" + "doughnut" : { + "doughnut-appearance" : "Apariencia de gráfico de anillo", + "layout" : "Disposición", + "layout-default" : "Predeterminado", + "layout-with-total" : "Con total", + "central-total-value" : "Valor total central", + "doughnut-card-style" : "Estilo de tarjeta del gráfico de anillo" }, - "timewindow": { - "timewindow": "Ventana de tiempo", - "years": "{ years, plural, =1 { año } other {# años } }", - "years-short": "{{ years }}a", - "months": "{ months, plural, =1 { mes } other {# meses } }", - "months-short": "{{ months }}M", - "weeks": "{ weeks, plural, =1 { semana } other {# semanas } }", - "weeks-short": "{{ weeks }}sem", - "days": "{ days, plural, =1 { día } other {# días } }", - "days-short": "{{ days }}d", - "hours": "{ hours, plural, =0 { horas } =1 {1 hora } other {# horas } }", - "hr": "{{ hr }} hr", - "hr-short": "{{ hr }}h", - "minutes": "{ minutes, plural, =0 { minutos } =1 {1 minuto } other {# minutos } }", - "min": "{{ min }} min", - "min-short": "{{ min }}m", - "seconds": "{ seconds, plural, =0 { segundos } =1 {1 segundo } other {# segundos } }", - "sec": "{{ sec }} sec", - "sec-short": "{{ sec }}s", - "short": { - "days": "{ days, plural, =1 {1 día } other {# días } }", - "hours": "{ hours, plural, =1 {1 hora } other {# horas } }", - "minutes": "{{minutes}} min ", - "seconds": "{{seconds}} sec " - }, - "realtime": "Tiempo-real", - "history": "Histórico", - "last-prefix": "último(s)", - "period": "desde {{ startTime }} hasta {{ endTime }}", - "edit": "Editar ventana de tiempo", - "date-range": "Rango de fechas", - "for-all-time": "Desde siempre", - "last": "Últimos(s)", - "time-period": "Período de tiempo", - "hide": "Ocultar", - "interval": "Intervalo", - "just-now": "Ahora mismo", - "just-now-lower": "ahora mismo", - "ago": "hace", - "style": "Estilo de ventana de tiempo", - "icon": "Icono", - "icon-position": "Posición de icono", - "icon-position-left": "Izquierda", - "icon-position-right": "Derecha", - "font": "Fuente", - "color": "Color", - "displayTypePrefix": "Mostrar prefijo en tiempo real/histórico", - "preview": "Previsualizar" + "entities-hierarchy" : { + "hierarchy-data-settings" : "Configuración de datos de jerarquía", + "relations-query-function" : "Función de consulta de relaciones del nodo", + "has-children-function" : "Función que indica si el nodo tiene hijos", + "node-state-settings" : "Configuración del estado del nodo", + "node-opened-function" : "Función de nodo abierto por defecto", + "node-disabled-function" : "Función de nodo deshabilitado", + "display-settings" : "Configuración de visualización", + "node-icon-function" : "Función de icono del nodo", + "node-text-function" : "Función de texto del nodo", + "sort-settings" : "Configuración de ordenación", + "nodes-sort-function" : "Función de ordenación de nodos" }, - "unit": { - "millimeter": "Milímetro", - "centimeter": "Centímetro", - "angstrom": "Angstrom", - "nanometer": "Nanómetro", - "micrometer": "Micrómetro", - "meter": "Metro", - "kilometer": "Kilómetro", - "inch": "Pulgada", - "foot": "Pie", - "yard": "Yarda", - "mile": "Milla", - "nautical-mile": "Milla Náutica", - "astronomical-unit": "Unidad Astronómica", - "reciprocal-metre": "Metro recíproco", - "meter-per-meter": "Metro por metro", - "steradian": "Estereorradián", - "thou": "Tú", - "barleycorn": "Grano de cebada", - "hand": "Hand", - "chain": "Cadena", - "furlong": "Furlong", - "league": "League", - "fathom": "Fathom", - "cable": "Cable", - "link": "Enlace", - "rod": "Barra", - "nanogram": "Nanogramo", - "microgram": "Microgramo", - "milligram": "Miligramo", - "gram": "Gramo", - "kilogram": "Kilogramo", - "tonne": "Tonelada", - "ounce": "Onza", - "pound": "Libra", - "stone": "Piedra", - "hundredweight-count": "Hundredweight", - "short-tons": "Tonelada corta", - "dalton": "Dalton", - "grain": "Grano", - "drachm": "Dracma", - "quarter": "Cuarto", - "slug": "Lingote", - "carat": "Quilate", - "cubic-millimeter": "Milímetro Cúbico", - "cubic-centimeter": "Centímetro Cúbico", - "cubic-meter": "Metro Cúbico/s", - "cubic-kilometer": "Kilómetro Cúbico", - "microliter": "Microlitro", - "milliliter": "Mililitro", - "liter": "Litro", - "hectoliter": "Hectolitro", - "cubic-inch": "Pulgada Cúbica", - "cubic-foot": "Pié Cúbico", - "cubic-yard": "Yarda Cúbica", - "fluid-ounce": "Onza Fluida", - "pint": "Pinta", - "quart": "Cuarta", - "gallon": "Galón", - "oil-barrels": "Barriles de Aceite", - "cubic-meter-per-kilogram": "Metro Cúbico por Kilogramo", - "gill": "Cuarta parte de una pinta", - "hogshead": "Tonel", - "teaspoon": "Cucharilla", - "tablespoon": "Cucharada", - "cup": "Taza", - "celsius": "Celsius", - "kelvin": "Kelvin", - "rankine": "Rankine", - "fahrenheit": "Fahrenheit", - "percent": "%", - "meter-per-second": "Metros por Segundo", - "kilometer-per-hour": "Kilómetros por Hora", - "foot-per-second": "Pies por Segundo", - "mile-per-hour": "Millas por Hora", - "knot": "Nudo", - "millimeters-per-minute": "Milímetros por minuto", - "kilometer-per-hour-squared": "Kilómetros al cuadrado por hora", - "foot-per-second-squared": "Pie al cuadrado por segundo", - "pascal": "Pascal", - "kilopascal": "Kilopascale", - "megapascal": "Megapascal", - "gigapascal": "Gigapascal", - "millibar": "Milibar", - "bar": "Bar", - "kilobar": "Kilobar", - "newton": "Newton", - "newton-meter": "Newton metro", - "foot-pounds": "Libras-pie", - "inch-pounds": "Pulgadas-pie", - "newton-per-meter": "Newton por metro", - "atmospheres": "Atmósferas", - "pounds-per-square-inch": "Libras por pulgada cuadrada", - "torr": "Torr", - "inches-of-mercury": "Pulgadas de mercurio", - "pascal-per-square-meter": "Pascales por metro cuadrado", - "pound-per-square-inch": "Pie por pulgada cuadrada", - "newton-per-square-meter": "Newtons por metro cuadrado", - "kilogram-force-per-square-meter": "Kilogramo de fuerza por metro cuadrado", - "pascal-per-square-centimeter": "Pascales por centímetro cuadrado", - "ton-force-per-square-inch": "Tonelada de fuerza por pulgada cuadrada", - "kilonewton-per-square-meter": "Kilonewtons por metro cuadrado", - "newton-per-square-millimeter": "Newton por milímetro cuadrado", - - "microjoule": "Microjulio", - "millijoule": "Milijulio", - "joule": "Julio", - "kilojoule": "Kilojulio", - "megajoule": "Megajulio", - "gigajoule": "Gigajulio", - "watt-hour": "Watios-hora", - "kilowatt-hour": "Kilowatios-hora", - "electron-volts": "Electronvoltios", - "joules-per-coulomb": "Julios por Coulomb", - "british-thermal-unit": "British Thermal Units (BTU)", - "foot-pound": "Foot-pound", - "calorie": "Caloría", - "small-calorie": "Caloría-pequeña", - "kilocalorie": "Kilocaloría", - "joule-per-kelvin": "Julios por Kelvin", - "joule-per-kilogram-kelvin": "Julio por Kilogramo-Kelvin", - "joule-per-kilogram": "Julio por Kilogramo", - "watt-per-meter-kelvin": "Watios por metro Kelvin", - "joule-per-cubic-meter": "Julios por metro cúbico", - "therm": "Termia", - "electric-dipole-moment": "Momento dipolar eléctrico", - "magnetic-dipole-moment": "Momento dipolar magnético", - "debye": "Debyé", - "coulomb-per-square-meter-per-volt": "Culombio por metro cuadrado por Voltio", - "milliwatt": "Miliwatio", - "microwatt": "Microwatio", - "watt": "Watio", - "kilowatt": "Kilowatio", - "megawatt": "Megawatio", - "gigawatt": "Gigawatio", - "metric-horsepower": "Caballos métricos", - "milliwatt-per-square-centimeter": "Miliwatios por centímetro cuadrado", - "watt-per-square-centimeter": "Watios por centímetro cuadrado", - "kilowatt-per-square-centimeter": "Kilowatios por centímetro cuadrado", - "milliwatt-per-square-meter": "Miliwatios por metro cuadrado", - "watt-per-square-meter": "Watios por metro cuadrado", - "kilowatt-per-square-meter": "Kilowatios por metro cuadrado", - "watt-per-square-inch": "Watios por pulgada cuadrada", - "kilowatt-per-square-inch": "Kilowatios por pulgada cuadrada", - "horsepower": "Caballos", - "btu-per-hour": "BTU/h", - "coulomb": "Culombio", - "millicoulomb": "Miliculombios", - "microcoulomb": "Microculombios", - "picocoulomb": "Picoculombios", - "coulomb-per-meter": "Culombio por metro", - "coulomb-per-cubic-meter": "Culombio por metro cúbico", - "coulomb-per-square-meter": "Culombio por metro cuadrado", - "square-millimeter": "Milímetro cuadrado", - "square-centimeter": "Centímetro cuadrado", - "square-meter": "Metro Cuadrado", - "hectare": "Hectárea", - "square-kilometer": "Kilómetro Cuadrado", - "square-inch": "Pulgada Cuadrada", - "square-foot": "Pie Cuadrada", - "square-yard": "Yarda Cuadrada", - "acre": "Hectárea", - "square-mile": "Milla Cuadrada", - "are": "Área", - "barn": "Barnio", - "circular-inch": "Pulgada circular", - "milliampere-hour": "Miliamperios hora", - "milliampere-hour-tags": "intensidad eléctrica, flujo de corriente, carga eléctrica, capacidad actual, flujo de electricidad, flujo eléctrico, miliamperio hora, miliamperios hora, mAh", - "ampere-hours": "Amperios hora", - "ampere-hours-tags": "intensidad eléctrica, flujo de corriente, carga eléctrica, capacidad actual, flujo de electricidad, flujo eléctrico, ampere, amperio hora, Ah", - "kiloampere-hours": "Kiloamperios hora", - "kiloampere-hours-tags": "intensidad eléctrica, flujo de corriente, carga eléctrica, capacidad actual, flujo de electricidad, flujo eléctrico, kiloamperio hora, kiloampreios hora, kAh", - "nanoampere": "Nanoamperio", - "nanoampere-tags": "intensidad, amperios, nanoamperio, nA", - "picoampere": "Picoamperio", - "picoampere-tags": "intensidad, amperios, picoamperio, pA", - "microampere": "Microamperio", - "microampere-tags": "intensidad eléctrica, microamperio, microamperios, μA", - "milliampere": "Miliamperio", - "milliampere-tags": "intensidad eléctrica, miliamperio, miliamperios, mA", - "ampere": "Amperio", - "ampere-tags": "intensidad eléctrica, flujo de corriente, flujo de electricidad, flujo eléctrico, amperio, amperios, amperaje, A", - "kiloamperes": "Kiloamperios", - "kiloamperes-tags": "intensidad eléctrica, flujo de corriente, kiloamperios, kA", - "microampere-per-square-centimeter": "Microamperio por centímetro cuadrado", - "microampere-per-square-centimeter-tags": "Densidad de intensidad, microamperio por centímetro cuadrado, µA/cm²", - "ampere-per-square-meter": "Amperio por metro cuadrado", - "ampere-per-square-meter-tags": "Densidad de intensidad, intensidad por área de unidad, amperio por metro cuadrado, A/m²", - "ampere-per-meter": "Amperio por Metro", - "ampere-per-meter-tags": "fuerza del campo magnético, intensidad del campo magnético, amperios por metro, A/m", - "oersted": "Oersted", - "oersted-tags": "campo magnético, oersted, Oe", - "bohr-magneton": "Magneton de Bohr", - "bohr-magneton-tags": "física atómica, momento magnético, magnetón de bohr, μB", - "ampere-meter-squared": "Amperios metro al cuadrado", - "ampere-meter-squared-tags": "momento magnético, momento dipolar, ampero metro cuadrado, A·m²", - "ampere-meter": "Amperio Metro", - "ampere-meter-tags": "campo magnético, bucle de corriente, ampero metro, A·m", - "nanovolt": "Nanovoltio", - "picovolt": "Picovoltio", - "millivolts": "Milivoltios", - "microvolts": "Microvoltios", - "volt": "Voltio", - "kilovolts": "Kilovoltios", - "dbmV": "dBmV", - "volt-meter": "Voltio-Metro", - "kilovolt-meter": "Kilovoltio-Metro", - "megavolt-meter": "Megavoltio-Metro", - "microvolt-meter": "Microvoltio-Metro", - "millivolt-meter": "Milivoltio-Metro", - "nanovolt-meter": "Nanovoltio-Metro", - "ohm": "Ohmio", - "microohm": "Microohmio", - "milliohm": "Miliohmio", - "kilohm": "Kiloohmio", - "megohm": "Megaohmio", - "gigohm": "Gigaohmio", - "hertz": "Hertzio", - "kilohertz": "Kilohertzio", - "megahertz": "Megahertzio", - "gigahertz": "Gigahertzio", - "rpm": "Revoluciones por minuto", - "candela-per-square-meter": "Candela por metro cuadrado", - "candela": "Candela", - "lumen": "Lúmenes", - "lux": "Lux", - "foot-candle": "Candela", - "lumen-per-square-meter": "Lúmenes por metro cuadrado", - "lux-second": "Lux segundo", - "lumen-second": "Lumen segundo", - "lumens-per-watt": "Lumens por watio", - "absorbance": "Absorbancia", - "mole": "Mole", - "nanomole": "Nanomole", - "micromole": "MicroMole", - "millimole": "Milimole", - "kilomole": "Kilomole", - "mole-per-cubic-meter": "Mole por metro cúbico", - "rssi": "RSSI", - "ppm": "Partes Por Millón", - "ppb": "Partes Por Billión", - "micrograms-per-cubic-meter": "Microgramos por metro cúbico", - "aqi": "AQI", - "gram-per-cubic-meter": "Gramos por metro cúbico", - "gram-per-kilogram": "Humedad Específica", - "millimeters-per-second": "Milimetros por segundo", - "neper": "Neper", - "bel": "Belio", - "decibel": "Decibelio", - "meters-per-second-squared": "Metros por segundo al cuadrado", - "becquerel": "Becquerel", - "curie": "Curie", - "gray": "Gray", - "sievert": "Sievert", - "roentgen": "Roentgen", - "cps": "Cuentas por segundo", - "rad": "Rad", - "rem": "Rem", - "dps": "Desintegraciones por segundo", - "rutherford": "Rutherford", - "coulombs-per-kilogram": "Culombios por kilogramo", - "becquerels-per-cubic-meter": "Becquerels por metro cúbico", - "curies-per-liter": "Curies por litro", - "becquerels-per-second": "Becquerels por segundo", - "curies-per-second": "Curies por segundo", - "gy-per-second": "Gray por segundo", - "watt-per-steradian": "Watio por estereorradián", - "watt-per-square-metre-steradian": "Watios por metro cuadrado de estereorradián", - "ph-level": "Nivel de pH", - "turbidity": "Turbidez", - "mg-per-liter": "Miligramos por litro", - "microsiemens-per-centimeter": "Microsiemens por centímetro", - "millisiemens-per-meter": "Milisiemens por metro", - "siemens-per-meter": "Siemens por metro", - "kilogram-per-cubic-meter": "Kilogramos por metro cúbico", - "gram-per-cubic-centimeter": "Gramos por centímetro cúbico", - "kilogram-per-square-meter": "Kilogramo por metro cuadrado", - "milligram-per-milliliter": "Miligramo por mililitro", - "pound-per-cubic-foot": "Libra por pie cúbico", - "ounces-per-cubic-inch": "Onzas por pulgada cúbica", - "tons-per-cubic-yard": "Toneladas por yarda cúbica", - "particle-density": "Densidad de partícula", - "kilometers-per-liter": "Kilometros por litro", - "miles-per-gallon": "Millas por galón", - "liters-per-100-km": "Litros por 100 km", - "gallons-per-mile": "Galones por milla", - "liters-per-hour": "Litros por hora", - "gallons-per-hour": "Galones por hora", - "beats-per-minute": "Pulsaciones por minuto", - "millimeters-of-mercury": "Milímetros de mercurio", - "milligrams-per-deciliter": "Miligramos por decilitro", - "g-force": "Fuerza-G", - "kilonewton": "Kilonewton", - "kilogram-force": "Kilogramo-Fuerza", - "pound-force": "Pondio-Fuerza", - "kilopound-force": "Kilopondio-Fuerza", - "dyne": "Dina", - "poundal": "Poundal", - "kip": "Kip", - "gal": "Gal", - "gravity": "Gravedad", - "hectopascal": "Hectopascal", - "atmosphere": "Atmósfera", - "millibars": "Milibares", - "inch-of-mercury": "Pulgada de mercurio", - "richter-scale": "Escala de Richter", - "second": "Segundo", - "minute": "Minuto", - "hour": "Hora", - "day": "Día", - "week": "Semana", - "month": "Mes", - "year": "Año", - "cubic-foot-per-minute": "Pie cúbico por minuto", - "cubic-meters-per-hour": "Metros cúbicos por hora", - "cubic-meters-per-second": "Metros cúbicos por segundo", - "liter-per-second": "Litros por segundo", - "liter-per-minute": "Litros por minuto", - "gallons-per-minute": "Galones por minuto", - "cubic-foot-per-second": "Pie cúbico por segundo", - "milliliters-per-minute": "Mililitros por minuto", - "bit": "Bit", - "byte": "Byte", - "kilobyte": "Kilobyte", - "megabyte": "Megabyte", - "gigabyte": "Gigabyte", - "terabyte": "Terabyte", - "petabyte": "Petabyte", - "exabyte": "Exabyte", - "zettabyte": "Zettabyte", - "yottabyte": "Yottabyte", - "bit-per-second": "Bit por segundo", - "kilobit-per-second": "Kilobit por segundo", - "megabit-per-second": "Megabit por segundo", - "gigabit-per-second": "Gigabit por segundo", - "terabit-per-second": "Terabit por segundo", - "byte-per-second": "Byte por segundo", - "kilobyte-per-second": "Kilobyte por segundo", - "megabyte-per-second": "Megabyte por segundo", - "gigabyte-per-second": "Gigabyte por segundo", - "degree": "Grado", - "radian": "Radian", - "gradian": "Gradian", - "mil": "Mil", - "revolution": "Revolución", - "siemens": "Siemens", - "millisiemens": "Milisiemens", - "microsiemens": "Microsiemens", - "kilosiemens": "Kilosiemens", - "megasiemens": "Megasiemens", - "gigasiemens": "Gigasiemens", - "farad": "Faradio", - "millifarad": "Milifaradio", - "microfarad": "Microfaradio", - "nanofarad": "Nanofaradio", - "picofarad": "Picofaradio", - "kilofarad": "Kilofaradio", - "megafarad": "Megafaradio", - "gigafarad": "Gigafaradio", - "terfarad": "Terfaradio", - "farad-per-meter": "Faradio por metro", - "tesla": "Tesla", - "gauss": "Gauss", - "kilogauss": "Kilogauss", - "millitesla": "Militesla", - "microtesla": "Microtesla", - "nanotesla": "Nanotesla", - "kilotesla": "Kilotesla", - "megatesla": "Megatesla", - "millitesla-square-meters": "militesla por metro cuadrado", - "gamma": "Gamma", - "lambda": "Lambda", - "square-meter-per-second": "Metros cuadrados por segundo", - "square-centimeter-per-second": "Centímetros cuadrados por segundo", - "stoke": "Stoke", - "centistokes": "Centistokes", - "square-foot-per-second": "Pie cuadrado por segundo", - "square-inch-per-second": "Pulgada cuadrada por segundo", - "pascal-second": "Pascal-segundo", - "centipoise": "Centipoise", - "poise": "Poise", - "reynolds": "Reynolds", - "pound-per-foot-hour": "Libra por pie-hora", - "newton-second-per-square-meter": "Newton segundo por metro cuadrado", - "dyne-second-per-square-centimeter": "Dyne segundo por centímetro cuadrado", - "kilogram-per-meter-second": "Kilogramo por metro-segundo", - "tesla-square-meters": "Tesla metros cuadrados", - "maxwell": "Maxwell", - "tesla-per-meter": "Tesla por metro", - "gauss-per-centimeter": "Gauss por centímetro", - "weber": "Weber", - "microweber": "Microweber", - "milliweber": "Miliweber", - "gauss-square-centimeter": "Gauss-centímetro cuadrado", - "kilogauss-square-centimeter": "Kilogauss-centímetro cuadrado", - "henry": "Henrio", - "millihenry": "Milihenrio", - "microhenry": "Microhenrio", - "nanohenry": "Nanohenrio", - "henry-per-meter": "Henrio por metro", - "tesla-meter-per-ampere": "Tesla metro por amperio", - "gauss-per-oersted": "Gauss por Oersted", - "kilogram-per-mole": "Kilogramo por mole", - "gram-per-mole": "Gramo por mole", - "milligram-per-mole": "Miligramo por mole", - "joule-per-mole": "Julios por mole", - "joule-per-mole-kelvin": "Julios por mole-Kelvin", - "millivolts-per-meter": "Milivoltios por metro", - "volts-per-meter": "Voltios por metro", - "kilovolts-per-meter": "Kilovoltios por metro", - "radian-per-second": "Radianes por segundo", - "radian-per-second-squared": "Radianes por segundo al cuadrado", - "revolutions-per-minute-per-second": "Aceleración angular", - "revolutions-per-minute-per-second-squared": "Aceleración angular", - "deg-per-second": "Grados/s", - "degrees-brix": "Grados Brix", - "katal": "Katal", - "katal-per-cubic-metre": "Katal por metro cúbico" + "edge" : { + "display-default-title" : "Mostrar título por defecto" }, - "user": { - "user": "Usuario", - "users": "Usuarios", - "customer-users": "Usuarios del Cliente", - "tenant-admins": "Admins propietarios", - "sys-admin": "Administrador del Sistema", - "tenant-admin": "Administrador Propietario", - "customer": "Cliente", - "anonymous": "Anónimo", - "add": "Agregar usuario", - "delete": "Eliminar usuario", - "add-user-text": "Agregar nuevo usuario", - "no-users-text": "Ningún usuario encontrado", - "user-details": "Detalles del usuario", - "delete-user-title": "¿Eliminar el usuario '{{userEmail}}'?", - "delete-user-text": "Atención, tras la confirmación el usuario seleccionado será eliminado y la información relacionada será irrecuperable.", - "delete-users-title": "¿Eliminar { count, plural, =1 {1 usuario} other {# usuarios} }?", - "delete-users-action-title": "Borrar { count, plural, =1 {1 usuario} other {# usuarios} }", - "delete-users-text": "Atención, tras la confirmación los usuarios seleccionados serán eliminados y la información relacionada será irrecuperable.", - "activation-email-sent-message": "Mail de activación enviado con éxito!", - "resend-activation": "Reenviar activación", - "email": "Email", - "email-required": "Email requerido.", - "invalid-email-format": "Formato de email no válido.", - "first-name": "Nombre", - "last-name": "Apellido", - "description": "Descripción", - "default-dashboard": "Panel por defecto", - "always-fullscreen": "Siempre en pantalla completa", - "select-user": "Seleccionar usuario", - "no-users-matching": "No se han encontrado usuarios coindiendo con '{{entity}}' .", - "user-required": "Usuario requerido", - "activation-method": "Método de activación", - "display-activation-link": "Mostrar enlace de activación", - "send-activation-mail": "Enviar mail de activación", - "activation-link": "Enlace de activacion de usuario", - "activation-link-text": "Para activar el usuario, usa el siguiente enlace: Activar Usuario :", - "copy-activation-link": "Copiar enlace de activación", - "activation-link-copied-message": "El enlace de activación se ha copiado al portapapeles", - "details": "Detalles", - "login-as-tenant-admin": "Iniciar sesión como Administrador Propietario", - "login-as-customer-user": "Iniciar sesión como Usuario Cliente", - "search": "Buscar usuarios", - "selected-users": "{ count, plural, =1 {1 usuario} other {# usuarios} } seleccionados", - "disable-account": "Deshabilitar cuenta de usuario", - "enable-account": "Habilitar cuenta de usuario", - "enable-account-message": "¡La cuenta de usuario se ha habilitado correctamente!", - "disable-account-message": "¡La cuenta de usuario se deshabilitó correctamente!", - "copyId": "Copiar Id de usuario", - "idCopiedMessage": "El Id de usuario se ha copiado al portapapeles", - "user-list": "Lista de usuarios", - "user-list-required": "Se requiere lista de usuarios" + "gateway" : { + "general-settings" : "Configuración general", + "widget-title" : "Título del widget", + "default-archive-file-name" : "Nombre de archivo de archivo por defecto", + "device-type-for-new-gateway" : "Tipo de dispositivo para nuevo gateway", + "messages-settings" : "Configuración de mensajes", + "save-config-success-message" : "Mensaje sobre la configuración del gateway guardada correctamente", + "device-name-exists-message" : "Mensaje cuando ya existe un dispositivo con el nombre ingresado", + "gateway-title" : "Formulario del gateway", + "read-only" : "Solo lectura", + "events-title" : "Título del formulario de eventos del gateway", + "events-filter" : "Filtro de eventos", + "event-key-contains" : "La clave del evento contiene...", + "show-connector" : "Mostrar para el conector", + "connector-state-param-key" : "Clave del parámetro de estado del conector", + "message" : "Mensaje", + "level" : "Nivel", + "created-time" : "Hora de creación" }, - "value": { - "type": "Tipo de valor", - "string": "Cadena de texto", - "string-value": "Valor de cadena de texto", - "string-value-required": "Se requiere valor de cadena de texto", - "integer": "Nro entero", - "integer-value": "Valor de nro entero", - "integer-value-required": "Se requiere valor entero", - "invalid-integer-value": "Valor de entero inválido", - "double": "Nro decimal", - "double-value": "Valor nro decimal", - "double-value-required": "Se requiere valor nro decimal", - "boolean": "Booleano", - "boolean-value": "Valor booleano", - "false": "Falso", - "true": "Verdadero", - "long": "Nro Largo", - "json": "JSON", - "json-value": "Valor JSON", - "json-value-invalid": "El valor JSON tiene un formato inválido", - "json-value-required": "Se requiere valor JSON" + "gauge" : { + "default-color" : "Color predeterminado", + "radial-gauge-settings" : "Configuración del medidor radial", + "ticks-settings" : "Configuración de marcas", + "min-value" : "Valor mínimo", + "max-value" : "Valor máximo", + "min-value-short" : "mín", + "max-value-short" : "máx", + "start-ticks-angle" : "Ángulo de inicio de marcas", + "ticks-angle" : "Ángulo de marcas", + "major-ticks" : "Marcas principales", + "major-ticks-count" : "Cantidad de marcas principales", + "major-ticks-color" : "Color de marcas principales", + "minor-ticks" : "Marcas secundarias", + "minor-ticks-count" : "Cantidad de marcas secundarias", + "minor-ticks-color" : "Color de marcas secundarias", + "tick-numbers-font" : "Fuente de números de marcas", + "unit-title-settings" : "Configuración del título de unidades", + "show-unit-title" : "Mostrar título de unidades", + "unit-title" : "Título de unidad", + "title-font" : "Fuente del texto del título", + "units-settings" : "Configuración de unidades", + "units-font" : "Fuente del texto de unidades", + "value-box-settings" : "Configuración del cuadro de valor", + "show-value-box" : "Mostrar cuadro de valor", + "value-box" : "Cuadro de valor", + "value-int" : "Cantidad de dígitos para la parte entera del valor", + "value-text" : "Texto del valor", + "value-text-shadow" : "Sombra del texto del valor", + "value-font" : "Fuente del texto del valor", + "rect-stroke-color-start" : "Color de trazo del rectángulo - inicio del gradiente", + "rect-stroke-color-end" : "Color de trazo del rectángulo - fin del gradiente", + "background-color" : "Color de fondo", + "shadow-color" : "Color de la sombra", + "value-box-rect-stroke-color" : "Color de trazo del rectángulo del cuadro de valor", + "value-box-rect-stroke-color-end" : "Color de trazo del rectángulo del cuadro de valor - fin del gradiente", + "value-box-background-color" : "Color de fondo del cuadro de valor", + "value-box-shadow-color" : "Color de la sombra del cuadro de valor", + "plate-settings" : "Configuración de la placa", + "show-plate-border" : "Borde de la placa", + "plate-color" : "Color de la placa", + "needle-settings" : "Configuración de la aguja", + "needle-circle-size" : "Tamaño del círculo de la aguja", + "needle-color" : "Color de la aguja", + "needle-color-start" : "Color de la aguja - inicio del gradiente", + "needle-color-end" : "Color de la aguja - fin del gradiente", + "needle-color-shadow-up" : "Color de la sombra superior de la aguja", + "needle-color-shadow-down" : "Sombra inferior", + "highlights-settings" : "Configuración de resaltados", + "highlights-width" : "Ancho de resaltados", + "highlights" : "Resaltados", + "highlight-from" : "Desde", + "highlight-to" : "Hasta", + "highlight-color" : "Color", + "no-highlights" : "No hay resaltados configurados", + "add-highlight" : "Agregar resaltado", + "animation-settings" : "Configuración de animación", + "enable-animation" : "Animación", + "animation-duration-rule" : "Duración y regla de animación", + "animation-duration" : "Duración de la animación", + "animation-rule" : "Regla de animación", + "animation-linear" : "Lineal", + "animation-quad" : "Cuadrática", + "animation-quint" : "Quíntica", + "animation-cycle" : "Cíclica", + "animation-bounce" : "Rebote", + "animation-elastic" : "Elástica", + "animation-dequad" : "De-cuadrática", + "animation-dequint" : "De-quíntica", + "animation-decycle" : "De-cíclica", + "animation-debounce" : "De-rebote", + "animation-delastic" : "De-elástica", + "linear-gauge-settings" : "Configuración del medidor lineal", + "bar-stroke" : "Trazo de la barra", + "bar-stroke-width" : "Ancho del trazo de la barra", + "bar-stroke-color" : "Color del trazo de la barra", + "bar-background-color" : "Color de fondo de la barra - inicio del gradiente", + "bar-background-color-end" : "Color de fondo de la barra - fin del gradiente", + "progress-bar-color" : "Color de la barra de progreso", + "progress-bar" : "Barra de progreso", + "progress-bar-color-start" : "Color de la barra de progreso - inicio del gradiente", + "progress-bar-color-end" : "Color de la barra de progreso - fin del gradiente", + "major-ticks-names" : "Nombres de marcas principales", + "show-stroke-ticks" : "Mostrar trazo de marcas", + "major-ticks-font" : "Fuente de marcas principales", + "border-color" : "Color del borde", + "border-width" : "Ancho del borde", + "needle-circle" : "Círculo de la aguja", + "needle-circle-color" : "Color del círculo de la aguja", + "animation-target" : "Objetivo de la animación", + "animation-target-needle" : "Aguja", + "animation-target-plate" : "Placa", + "common-settings" : "Configuración común del medidor", + "gauge-type" : "Tipo de medidor", + "gauge-type-arc" : "Arco", + "gauge-type-donut" : "Rosquilla", + "gauge-type-horizontal-bar" : "Barra horizontal", + "gauge-type-vertical-bar" : "Barra vertical", + "donut-start-angle" : "Ángulo de inicio (grados)", + "bar-settings" : "Configuración de la barra del medidor", + "relative-bar-width" : "Ancho relativo de la barra", + "neon-glow-brightness" : "Brillo del efecto neón (0-100)", + "neon-glow-brightness-hint" : "0 - desactivar efecto", + "stripes-thickness" : "Grosor de las franjas", + "stripes-thickness-hint" : "0 - sin franjas", + "rounded-line-cap" : "Extremos redondeados", + "bar-color-settings" : "Configuración de color de la barra", + "use-precise-level-color-values" : "Usar niveles de color precisos", + "bar-colors" : "Colores de barra, de menor a mayor", + "color" : "Color", + "no-bar-colors" : "No hay colores de barra configurados", + "add-bar-color" : "Agregar color de barra", + "from" : "Desde", + "to" : "Hasta", + "fixed-level-colors" : "Colores de barra usando valores límite", + "gauge-title-settings" : "Configuración del título del medidor", + "show-gauge-title" : "Mostrar título del medidor", + "gauge-title" : "Título del medidor", + "gauge-title-font" : "Fuente del título del medidor", + "unit-title-and-timestamp-settings" : "Configuración del título de unidad y marca de tiempo", + "show-timestamp" : "Marca de tiempo", + "timestamp-format" : "Formato de marca de tiempo", + "label-font" : "Fuente de la etiqueta debajo del valor", + "value-settings" : "Configuración del valor", + "show-value" : "Mostrar texto del valor", + "min-max-settings" : "Configuración de etiquetas mínimo/máximo", + "show-min-max" : "Mostrar valores mínimo y máximo", + "min-max-font" : "Fuente de valores mínimo y máximo", + "show-ticks" : "Mostrar marcas", + "tick-width" : "Ancho de las marcas", + "tick-color" : "Color de las marcas", + "tick-values" : "Valores de las marcas", + "no-tick-values" : "No hay valores de marcas configurados", + "add-tick-value" : "Agregar valor de marca", + "gauge-appearance" : "Apariencia del medidor", + "units-title" : "Título de unidades", + "value" : "Valor", + "ticks" : "Marcas", + "arrow-and-scale-color" : "Color predeterminado de flecha y escala", + "scale-settings" : "Configuración de la escala", + "scale" : "Escala", + "scale-color" : "Colores de escala", + "compass-appearance" : "Apariencia de la brújula", + "label" : "Etiqueta", + "labels" : "Etiquetas", + "label-style" : "Estilo de etiqueta", + "simple-gauge-type" : "Tipo", + "gauge-bar-background" : "Fondo de la barra del medidor", + "bar-color" : "Color de la barra", + "min-and-max-value" : "Valor mínimo y máximo", + "min-and-max-label" : "Etiqueta mínima y máxima", + "font" : "Fuente", + "tick-width-and-color" : "Ancho y color de marcas", + "min-max-validation-text" : "El valor máximo debe ser mayor que el valor mínimo" }, - "version-control": { - "version-control": "Control de Versión", - "management": "Administrador de versiones", - "search": "Buscar versiones", - "branch": "Rama", - "default": "Por defecto", - "select-branch": "Seleccionar rama", - "branch-required": "Se requiere rama", - "create-entity-version": "Versión creación de entidad", - "version-name": "Nombre de versión", - "version-name-required": "Se requiere nombre de versión", - "author": "Autor", - "export-relations": "Exportar relaciones", - "export-attributes": "Exportar atributos", - "export-credentials": "Exportar credenciales", - "entity-versions": "Versiones de entidad", - "versions": "Versiones", - "created-time": "Hora de creación", - "version-id": "ID de versión", - "no-entity-versions-text": "No se han encontrado versiones de entidad", - "no-versions-text": "No se han encontrado versiones", - "copy-full-version-id": "Copiar el ID de versión", - "create-version": "Crear versión", - "creating-version": "Creando versión... Por favor, espere", - "nothing-to-commit": "No hay cambios a publicar", - "restore-version": "Restaurar versión", - "restore-entity-from-version": "Restaurar entidad desde versión '{{versionName}}'", - "restoring-entity-version": "Restaurando versión de entidad... Por favor, espere", - "load-relations": "Cargar relaciones", - "load-attributes": "Cargar atributos", - "load-credentials": "Cargar credencialess", - "compare-with-current": "Comparar con actual", - "diff-entity-with-version": "Diff de la versión de entidad '{{versionName}}'", - "previous-difference": "Anterior diferencia", - "next-difference": "Siguiente diferencia", - "current": "Actual", - "differences": "{ count, plural, =1 {1 diferencia} other {# diferencias} }", - "create-entities-version": "Crear versión de entidades", - "default-sync-strategy": "Estrategia de sincronización por defecto", - "sync-strategy-merge": "Combinar (Merge)", - "sync-strategy-overwrite": "Sobreescribir", - "entities-to-export": "Entidades a exportar", - "entities-to-restore": "Entidades a restaurar", - "sync-strategy": "Estrategia de sincronización", - "all-entities": "Todas las entidades", - "no-entities-to-export-prompt": "Por favor, especifica las entidades a exportar", - "no-entities-to-restore-prompt": "Por favor, especifica las entidades a restaurar", - "add-entity-type": "Añadir tipo de entidad", - "remove-all": "Borrar todo", - "version-create-result": "{ added, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } añadidas.
{ modified, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } modificadas.
{ removed, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } borradas.", - "remove-other-entities": "Borrar otras entidades", - "find-existing-entity-by-name": "Buscar entidad existente por nombre", - "restore-entities-from-version": "Restaurar entidades desde la versión '{{versionName}}'", - "restoring-entities-from-version": "Restaurando entidades... Por favor, espere", - "no-entities-restored": "No se restauraron entidades", - "created": "{{created}} creadas", - "updated": "{{updated}} actualizadas", - "deleted": "{{deleted}} borradas", - "remove-other-entities-confirm-text": "Atención! Esta acción borrará permanentemente todas las entidades actuales
no presentes en la versión a restaurar.

Escribe \"remove other entities\" para confirmar.", - "auto-commit-to-branch": "auto-publicar a la rama {{ branch }}", - "default-create-entity-version-name": "{{entityName}} actualización", - "sync-strategy-merge-hint": "Crea o actualiza las entidades seleccionadas en el repositorio. Las demás entidades no serán modificadas.", - "sync-strategy-overwrite-hint": "Crea o actualiza las entidades seleccionadas en el repositorio. Las demás entidades serán borradas.", - "device-credentials-conflict": "Fallo al cargar el dispositivo con ID externo {{entityId}}
debido a que las mismas credenciales están ya presentes en la base de datos para otro dispositivo.
Por favor, considera desactivar el ajuste cargar credenciales en el formulario de restauración.", - "missing-referenced-entity": "Fallo al cargar {{sourceEntityTypeName}} con ID externo {{sourceEntityId}}
porque hace referencia al tipo de entidad {{targetEntityTypeName}} con el ID {{targetEntityId}}.", - "runtime-failed": "Fallo: {{message}}", - "auto-commit-settings-read-only-hint": "Los ajustes de Auto-commit no funcionan, si la opción de solo lectura está activada en los ajustes del repositorio." + "gpio" : { + "pin" : "Pin", + "label" : "Etiqueta", + "row" : "Fila", + "column" : "Columna", + "color" : "Color", + "panel-settings" : "Configuración del panel", + "background-color" : "Color de fondo", + "gpio-switches" : "Interruptores GPIO", + "no-gpio-switches" : "No hay interruptores GPIO configurados", + "add-gpio-switch" : "Agregar interruptor GPIO", + "gpio-status-request" : "Solicitud de estado GPIO", + "method-name" : "Nombre del método", + "method-body" : "Cuerpo del método", + "gpio-status-change-request" : "Solicitud de cambio de estado GPIO", + "parse-gpio-status-function" : "Función para analizar estado GPIO", + "gpio-leds" : "LEDs GPIO", + "no-gpio-leds" : "No hay LEDs GPIO configurados", + "add-gpio-led" : "Agregar LED GPIO" }, - "widget": { - "widget-library": "Bibloteca de Widgets", - "widget-bundle": "Paquetes de Widgets", - "all-bundles": "Todos los paquetes", - "select-widgets-bundle": "Seleccionar paquete de widgets", - "widgets": "Widgets", - "all-widgets": "Todos los widgets", - "widget": "Widget", - "select-widget": "Seleccionar widget", - "no-widgets-matching": "No se han encontrado widgets que coincidan con '{{entity}}'.", - "no-widgets": "No hay widgets todavía", - "no-widgets-text": "No se han encontrado widgets", - "management": "Gestión de Widgets", - "editor": "Editor de widgets", - "widget-type-not-found": "Problema al cargar la configuración del widget.
Probablemente asociado\n El tipo de widget fue eliminado.", - "widget-type-load-error": "El widget no pudo ser cargado debido a estos errores:", - "remove": "Eliminar widget", - "delete": "Borrar widget", - "edit": "Editar widget", - "edit-widget-type": "Editar tipo de widget", - "remove-widget-title": "¿Eliminar el widget '{{widgetTitle}}'?", - "remove-widget-text": "Atención, tras la confirmación el widget será eliminado y toda la información relacionada será irrecuperable..", - "timeseries": "Series de tiempo", - "search-data": "Buscar datos", - "no-data-found": "No se han encontrado datos", - "latest": "Últimos valores", - "rpc": "Widget de control", - "alarm": "Widget de Alarma", - "static": "Widget estático", - "select-widget-type": "Seleccionar tipo de widget", - "missing-widget-title-error": "El titulo del widget debe ser especificado!", - "widget-saved": "Widget guardado", - "unable-to-save-widget-error": "Imposible guardar widget! Tiene errores!", - "save": "Guardar widget", - "saveAs": "Guardar widget como", - "move": "Mover widget", - "save-widget-type-as": "Guardar tipo de widget como", - "save-widget-type-as-text": "Por favor, ingrese un nuevo titulo y/o seleccione un paquete de destino.", - "toggle-fullscreen": "Cambiar a pantalla completa", - "run": "Ejecutar widget", - "widget-title": "Título del widget", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256", - "system": "Sistema", - "type": "Tipo de widget", - "resources": "Recursos", - "resource-url": "URL JavaScript/CSS", - "remove-resource": "Eliminar recurso", - "add-resource": "Agregar recurso", - "html": "HTML", - "tidy": "Tidy", - "css": "CSS", - "settings-schema": "Esquema de configuración", - "datakey-settings-schema": "Esquema de configuración de clave de datos", - "latest-datakey-settings-schema": "Esquema de últimos valores", - "widget-settings": "Ajustes de widget", - "description": "Descripción", - "tags": "Etiquetas", - "image-preview": "Imagen previsualización", - "settings-form-selector": "Selector formulario de ajustes", - "data-key-settings-form-selector": "Selector formulario de ajustes claves de datos", - "all": "Todos", - "actual": "Actuales", - "deprecated": "Obsoletos", - "has-basic-mode": "Tienen modo básico", - "basic-mode-form-selector": "Selector formulario en modo básico", - "basic-mode": "Básico", - "advanced-mode": "Avanzado", - "latest-data-key-settings-form-selector": "Selector formulario de últimos valores", - "javascript": "Javascript", - "js": "JS", - "widget-types": "Tipos de Widget", - "delete-widget-title": "¿Eliminar de widget '{{widgetName}}'?", - "delete-widget-text": "Atención, tras la confirmación será eliminado y la información relacionada será irrecuperable.", - "delete-widgets-title": "¿Eliminar { count, plural, =1 {1 de widget} other {# de widgets} }?", - "delete-widgets-text": "Atención, tras la confirmación los de widget seleccionados serán eliminados y la información relacionada será irrecuperable.", - "delete-widget": "Borrar de widget", - "widget-template-load-failed-error": "Error al cargar la plantilla del widget!", - "details": "Detalles", - "widget-details": "Detalles de widget", - "add": "Agregar Widget", - "search-widget": "Buscar de widget", - "selected-widgets": "{ count, plural, =1 {1 de widget} other {# de widgets} } seleccionados", - "undo": "Deshacer cambios", - "export": "Exportar widget", - "no-data": "No hay datos para mostrar en widget", - "data-overflow": "El widget muestra {{count}} de {{total}} entidades", - "alarm-data-overflow": "El widget muestra alarmas para {{allowedEntities}} entidades (máximo permitido) de {{totalEntities}} entidades", - "search": "Buscar widget", - "filter": "Filtro tipo de widget", - "loading-widgets": "Cargando widgets...", - "widget-template-error": "Plantilla HTML inválida." + "html-card" : { + "html" : "HTML", + "css" : "CSS" }, - "widget-action": { - "header-button": "Botón de encabezado widget", - "open-dashboard-state": "Navegar a un nuevo estado de panel", - "update-dashboard-state": "Actualizar el estado del panel actual", - "open-dashboard": "Navegar hacia otro panel", - "custom": "Acción personalizada", - "custom-pretty": "Acción personalizada (con plantilla HTML)", - "custom-pretty-error-title": "Diálogo de error personalizado", - "custom-pretty-template-error": "Plantilla de diálogo de error inválida.", - "custom-pretty-controller-error": "Ha ocurrido un error mientras se evaluaba la función.", - "mobile-action": "Acción en dispositivo móvil", - "target-dashboard-state": "Estado de panel de destino", - "target-dashboard-state-required": "Se requiere estado de panel de destino", - "set-entity-from-widget": "Establecer entidad desde widget", - "target-dashboard": "Panel de destino", - "open-right-layout": "Abrir diseño de panel (derecho)(vista móvil)", - "state-display-type": "Opciones de display de panel", - "open-normal": "Normal", - "open-in-separate-dialog": "Abrir en un diálogo separado", - "open-in-popover": "Abrir en popover", - "dialog-title": "Título del diálogo", - "dialog-hide-dashboard-toolbar": "Ocultar barra de herramientas en el diálogo", - "dialog-width": "Ancho de diálogo en porcentaje relativo al ancho del viewport", - "dialog-height": "Alto de diálogo en porcentaje relativo al alto del viewport", - "dialog-size-range-error": "El tamaño del diálogo debe ser entre un rango de 1 a 100", - "popover-preferred-placement": "Ubicación preferida popover", - "popover-placement-top": "Superior", - "popover-placement-topLeft": "Superior izquierda", - "popover-placement-topRight": "Superior derecha", - "popover-placement-right": "Derecha", - "popover-placement-rightTop": "Derecha superior", - "popover-placement-rightBottom": "Derecha inferior", - "popover-placement-bottom": "Inferior", - "popover-placement-bottomLeft": "Inferior izquierda", - "popover-placement-bottomRight": "Inferior derecha", - "popover-placement-left": "Izquierda", - "popover-placement-leftTop": "Izquierda superior", - "popover-placement-leftBottom": "Izquierda inferior", - "popover-hide-on-click-outside": "Ocultar en click fuera del popover", - "popover-hide-dashboard-toolbar": "Ocultar caja de herramientas en popover", - "popover-width": "Ancho de popover", - "popover-height": "Altura de popover", - "popover-style": "Estilo de popover", - "open-new-browser-tab": "Abrir en una nueva pestaña", - "mobile": { - "action-type": "Tipo de acción móvil", - "action-type-required": "Tipo de acción móvil requerida", - "take-picture-from-gallery": "Tomar foto de galería", - "take-photo": "Tomar foto", - "map-direction": "Abrir indicaciones en mapa", - "map-location": "Abrir localización en mapa", - "scan-qr-code": "Escanear código QR", - "make-phone-call": "Hacer llamada telefónica", - "get-location": "Obtener localización del teléfono", - "take-screenshot": "Obtener captura de pantalla" - } + "input-widgets" : { + "attribute-not-allowed" : "El parámetro de atributo no se puede usar en este widget", + "blocked-location" : "La geolocalización está bloqueada en tu navegador", + "claim-device" : "Reclamar dispositivo", + "claim-failed" : "¡No se pudo reclamar el dispositivo!", + "claim-not-found" : "¡Dispositivo no encontrado!", + "claim-successful" : "¡El dispositivo fue reclamado con éxito!", + "date" : "Fecha", + "device-name" : "Nombre del dispositivo", + "device-name-required" : "El nombre del dispositivo es obligatorio", + "discard-changes" : "Descartar cambios", + "entity-attribute-required" : "Se requiere el atributo de la entidad", + "entity-coordinate-required" : "Se requieren ambos campos, latitud y longitud", + "entity-timeseries-required" : "Se requiere la serie temporal de la entidad", + "get-location" : "Obtener ubicación actual", + "invalid-date" : "Fecha inválida", + "latitude" : "Latitud", + "longitude" : "Longitud", + "min-value-error" : "El valor mínimo es {{value}}", + "max-value-error" : "El valor máximo es {{value}}", + "not-allowed-entity" : "La entidad seleccionada no puede tener atributos compartidos", + "no-attribute-selected" : "No se ha seleccionado ningún atributo", + "no-datakey-selected" : "No se ha seleccionado ninguna clave de datos", + "no-coordinate-specified" : "No se ha especificado la clave de datos para latitud/longitud", + "no-entity-selected" : "No se ha seleccionado ninguna entidad", + "no-image" : "Sin imagen", + "no-support-geolocation" : "Tu navegador no admite geolocalización", + "no-support-web-camera" : "Tu navegador no admite cámaras", + "enable-https-use-widget" : "Activa HTTPS para usar este widget", + "no-found-your-camera" : "No se pudo encontrar tu cámara", + "no-permission-camera" : "Permiso denegado por el usuario / Este sitio no tiene permiso para usar la cámara", + "no-timeseries-selected" : "No se ha seleccionado ninguna serie temporal", + "secret-key" : "Clave secreta", + "secret-key-required" : "La clave secreta es obligatoria", + "switch-attribute-value" : "Cambiar el valor del atributo de la entidad", + "switch-camera" : "Cambiar cámara", + "switch-timeseries-value" : "Cambiar el valor de la serie temporal de la entidad", + "take-photo" : "Tomar foto", + "time" : "Hora", + "timeseries-not-allowed" : "El parámetro de serie temporal no se puede usar en este widget", + "update-failed" : "Actualización fallida", + "update-successful" : "Actualización exitosa", + "update-attribute" : "Actualizar atributo", + "update-timeseries" : "Actualizar serie temporal", + "value" : "Valor", + "general-settings" : "Configuración general", + "widget-title" : "Título del widget", + "claim-button-label" : "Etiqueta del botón de reclamación", + "show-secret-key-field" : "Mostrar campo de 'Clave secreta'", + "labels-settings" : "Configuración de etiquetas", + "show-labels" : "Mostrar etiquetas", + "device-name-label" : "Etiqueta para el campo de nombre del dispositivo", + "secret-key-label" : "Etiqueta para el campo de clave secreta", + "messages-settings" : "Configuración de mensajes", + "claim-device-success-message" : "Mensaje de éxito al reclamar el dispositivo", + "claim-device-not-found-message" : "Mensaje cuando el dispositivo no se encuentra", + "claim-device-failed-message" : "Mensaje de error al reclamar el dispositivo", + "claim-device-name-required-message" : "Mensaje de error 'Se requiere nombre del dispositivo'", + "claim-device-secret-key-required-message" : "Mensaje de error 'Se requiere clave secreta'", + "show-label" : "Mostrar etiqueta", + "label" : "Etiqueta", + "required" : "Requerido", + "required-error-message" : "Mensaje de error 'Requerido'", + "show-result-message" : "Mostrar mensaje de resultado", + "integer-field-settings" : "Configuración del campo entero", + "min-value" : "Valor mínimo", + "max-value" : "Valor máximo", + "double-field-settings" : "Configuración del campo doble", + "text-field-settings" : "Configuración del campo de texto", + "min-length" : "Longitud mínima", + "max-length" : "Longitud máxima", + "checkbox-settings" : "Configuración de la casilla de verificación", + "true-label" : "Etiqueta cuando está marcado", + "false-label" : "Etiqueta cuando no está marcado", + "image-input-settings" : "Configuración de entrada de imagen", + "display-preview" : "Mostrar vista previa", + "display-clear-button" : "Mostrar botón de limpiar", + "display-apply-button" : "Mostrar botón de aplicar", + "display-discard-button" : "Mostrar botón de descartar", + "datetime-field-settings" : "Configuración del campo fecha/hora", + "display-time-input" : "Mostrar entrada de hora", + "latitude-key-name" : "Nombre de la clave de latitud", + "longitude-key-name" : "Nombre de la clave de longitud", + "show-get-location-button" : "Mostrar botón 'Obtener ubicación actual'", + "use-high-accuracy" : "Usar alta precisión", + "location-fields-settings" : "Configuración de campos de ubicación", + "latitude-label" : "Etiqueta para latitud", + "longitude-label" : "Etiqueta para longitud", + "input-fields-alignment" : "Alineación de los campos de entrada", + "input-fields-alignment-column" : "Columna (por defecto)", + "input-fields-alignment-row" : "Fila", + "layout" : "Disposición", + "row-gap" : "Espacio entre filas en píxeles", + "column-gap" : "Espacio entre columnas en píxeles", + "latitude-field-required" : "Campo de latitud requerido", + "longitude-field-required" : "Campo de longitud requerido", + "attribute-settings" : "Configuración del atributo", + "widget-mode" : "Modo del widget", + "widget-mode-update-attribute" : "Actualizar atributo", + "widget-mode-update-timeseries" : "Actualizar serie temporal", + "attribute-scope" : "Ámbito del atributo", + "attribute-scope-server" : "Atributo del servidor", + "attribute-scope-shared" : "Atributo compartido", + "value-required" : "Valor requerido", + "image-settings" : "Configuración de imagen", + "image-format" : "Formato de imagen", + "image-format-jpeg" : "JPEG", + "image-format-png" : "PNG", + "image-format-webp" : "WEBP", + "image-quality" : "Calidad de la imagen que usa compresión con pérdida como JPEG y WEBP", + "max-image-width" : "Ancho máximo de la imagen", + "max-image-height" : "Altura máxima de la imagen", + "action-buttons" : "Botones de acción", + "show-action-buttons" : "Mostrar botones de acción", + "update-all-values" : "Actualizar todos los valores, no solo los modificados", + "save-button-label" : "Etiqueta del botón 'GUARDAR'", + "reset-button-label" : "Etiqueta del botón 'DESHACER'", + "group-settings" : "Configuración del grupo", + "show-group-title" : "Mostrar título para grupo de campos relacionados con diferentes entidades", + "group-title" : "Título del grupo", + "fields-alignment" : "Alineación de campos", + "fields-alignment-row" : "Fila (por defecto)", + "fields-alignment-column" : "Columna", + "fields-in-row" : "Número de campos en la fila", + "option-value" : "Valor (escriba 'null' para crear opción vacía)", + "option-label" : "Etiqueta", + "hide-input-field" : "Ocultar campo de entrada", + "datakey-type" : "Tipo de clave de datos", + "datakey-type-server" : "Atributo del servidor (por defecto)", + "datakey-type-shared" : "Atributo compartido", + "datakey-type-timeseries" : "Serie temporal", + "datakey-value-type" : "Tipo de valor de la clave de datos", + "datakey-value-type-string" : "Cadena", + "datakey-value-type-double" : "Doble", + "datakey-value-type-integer" : "Entero", + "datakey-value-type-json" : "JSON", + "datakey-value-type-boolean-checkbox" : "Booleano (Casilla)", + "datakey-value-type-boolean-switch" : "Booleano (Interruptor)", + "datakey-value-type-date-time" : "Fecha y hora", + "datakey-value-type-date" : "Fecha", + "datakey-value-type-time" : "Hora", + "datakey-value-type-select" : "Seleccionar", + "datakey-value-type-radio" : "Radio", + "datakey-value-type-color" : "Color", + "value-is-required" : "El valor es obligatorio", + "ability-to-edit-attribute" : "Capacidad para editar atributo", + "ability-to-edit-attribute-editable" : "Editable (por defecto)", + "ability-to-edit-attribute-disabled" : "Deshabilitado", + "ability-to-edit-attribute-readonly" : "Solo lectura", + "disable-on-datakey-name" : "Deshabilitar si el valor de otra clave de datos es falso (especifique el nombre)", + "field-appearance" : "Apariencia del campo", + "appearance-fill" : "Relleno", + "appearance-outline" : "Contorno", + "subscript-sizing" : "Tamaño del subíndice", + "subscript-sizing-fixed" : "Fijo", + "subscript-sizing-dynamic" : "Dinámico", + "slide-toggle-settings" : "Configuración de alternancia deslizante", + "slide-toggle-label-position" : "Posición de la etiqueta", + "slide-toggle-label-position-after" : "Después", + "slide-toggle-label-position-before" : "Antes", + "select-options" : "Opciones de selección", + "no-select-options" : "No se han configurado opciones de selección", + "add-select-option" : "Agregar opción de selección", + "numeric-field-settings" : "Configuración del campo numérico", + "step-interval" : "Intervalo de paso entre valores", + "error-messages" : "Mensajes de error", + "min-value-error-message" : "Mensaje de error 'Valor mínimo'", + "max-value-error-message" : "Mensaje de error 'Valor máximo'", + "invalid-date-error-message" : "Mensaje de error 'Fecha inválida'", + "invalid-JSON-error-message" : "Mensaje de error 'JSON inválido'", + "icon-settings" : "Configuración de icono", + "dialog-editor-settings" : "Configuración del editor de diálogos", + "use-custom-icon" : "Usar icono personalizado", + "input-cell-icon" : "Icono para mostrar antes del campo de entrada", + "value-conversion-settings" : "Configuración de conversión de valores", + "get-value-settings" : "Configuración para obtener valor", + "use-get-value-function" : "Usar función getValue", + "get-value-function" : "Función getValue", + "set-value-settings" : "Configuración para establecer valor", + "use-set-value-function" : "Usar función setValue", + "set-value-function" : "Función setValue", + "json-invalid" : "El valor JSON tiene un formato inválido", + "title" : "Título", + "cancel-button-label" : "Etiqueta del botón 'CANCELAR'", + "radio-button-settings" : "Configuración de botón de opción", + "color" : "Color", + "columns" : "Columnas", + "radio-options" : "Opciones de radio", + "no-radio-options" : "No se han configurado opciones de radio", + "add-radio-option" : "Agregar opción de radio", + "radio-label-position" : "Posición de la etiqueta", + "radio-label-position-before" : "Antes", + "radio-label-position-after" : "Después" }, - "widgets-bundle": { - "current": "Paquete actual", - "widgets-bundles": "Paquete de Widgets", - "widgets-bundle-widgets": "Widgets del paquete de widgets", - "add": "Agregar paquete de widgets", - "delete": "Eliminar paquete de widgets", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256", - "description": "Descripción", - "image-preview": "Imagen previsualización", - "add-widgets-bundle-text": "Agregar nuevo paquete de widgets", - "no-widgets-bundles-text": "Ningún paquete de widgets encontrado", - "empty": "Paquete de widgets vacío.", - "details": "Detalles", - "widgets-bundle-details": "Detalles del paquete de Widgets", - "delete-widgets-bundle-title": "¿Eliminar el paquete de widgets '{{widgetsBundleTitle}}'?", - "delete-widgets-bundle-text": "Atención, tras la confirmación todos los paquetes seleccionados serán eliminados y su información relacionada será irrecuperable.", - "delete-widgets-bundles-title": "¿Eliminar { count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} }?", - "delete-widgets-bundles-action-title": "Eliminar { count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} }", - "delete-widgets-bundles-text": "Atención, tras la confirmación todos los paquetes seleccionados serán eliminados y la información relacionada será irrecuperable.", - "no-widgets-bundles-matching": "Ningún paquete '{{widgetsBundle}}' encontrado.", - "widgets-bundle-required": "Paquete de widget requerido.", - "system": "Widget de Sistema", - "import": "Importar paquete de widgets", - "export": "Exportar paquete de widgets", - "export-widgets-bundle-widgets-prompt": "Incluir de widget en los datos exportados (de lo contrario, solo se exportarán los FQN de los tipos de widget a los que hace referencia)", - "export-failed-error": "Imposible exportar paquete de widgets: {{error}}", - "create-new-widgets-bundle": "Crear nuevo paquete de widgets", - "widgets-bundle-file": "Archivo de paquete de widgets", - "invalid-widgets-bundle-file-error": "Imposible importar paquete de widgets: Estructura de datos inválida.", - "search": "Buscar paquete de widgets", - "selected-widgets-bundles": "{ count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} } seleccionados", - "open-widgets-bundle": "Abrir paquete de widgets", - "loading-widgets-bundles": "Cargando paquete de widgets..." + "invalid-qr-code-text" : "Texto de entrada no válido para el código QR. La entrada debe ser de tipo cadena", + "qr-code" : { + "use-qr-code-text-function" : "Usar función de texto del código QR", + "qr-code-text-pattern" : "Patrón de texto del código QR (por ejemplo, '${entityName} | ${keyName} - algún texto.')", + "qr-code-text-pattern-hint" : "El patrón de texto del código QR utiliza el valor de la primera clave encontrada en las entidades del alias de entidad.", + "qr-code-text-pattern-required" : "Se requiere el patrón de texto del código QR.", + "qr-code-text-function" : "Función de texto del código QR" }, - "widget-config": { - "data": "Datos", - "settings": "Ajustes", - "advanced": "Avanzado", - "appearance": "Apariencia", - "widget-card": "Tarjeta de widget", - "mobile": "Móvil", - "title": "Título", - "title-tooltip": "Tooltip Título", - "general-settings": "Ajustes generales", - "display-title": "Mostrar titulo", - "card-title": "Título de la tarjeta", - "drop-shadow": "Sombra", - "enable-fullscreen": "Habilitar pantalla completa", - "background-color": "Color de fondo", - "text-color": "Color del texto", - "border-radius": "Radio del borde", - "padding": "Relleno", - "margin": "Margen", - "widget-style": "Estilo de widget", - "widget-css": "CSS de widget", - "title-style": "Estilo de título", - "mobile-mode-settings": "Ajustes móvil.", - "order": "Orden", - "height": "Altura", - "mobile-hide": "Ocultar widget en modo móvil", - "desktop-hide": "Ocultar widget en modo escritorio", - "units": "Caracter especial a mostrar en el siguiente valor", - "units-by-default": "Unidades por defecto", - "decimals": "Decimales", - "decimals-by-default": "Decimales por defecto", - "default-data-key-parameter-hint": "Este parámetro aplica a todos los valores del widget a menos que la configuración de la clave de datos lo anule", - "units-short": "Unidades", - "decimals-short": "Decimales", - "decimals-suffix": "decimales", - "timewindow": "Ventana de tiempo", - "use-dashboard-timewindow": "Usar ventana de tiempo del Panel", - "use-widget-timewindow": "Usar ventana de tiempo del widget", - "display-timewindow": "Mostrar ventana de tiempo", - "legend": "Leyenda", - "display-legend": "Mostrar leyenda", - "datasources": "Origen de datos", - "datasource": "Origen de dato", - "maximum-datasources": "Un máximo de { count, plural, =1 {1 set de datos es permitido.} other {# set de datos son permitidos} }", - "timeseries-key-error": "Se debe especificar al menos un datakey tipo timeseries", - "datasource-type": "Tipo", - "datasource-parameters": "Parámetros", - "remove-datasource": "Eliminar set de datos", - "add-datasource": "Agregar set de datos", - "target-device": "Dispositivo destino", - "alarm-source": "Origen de alarmas", - "actions": "Acciones", - "action": "Acción", - "add-action": "Añadir acción", - "search-actions": "Buscar acciones", - "no-actions-text": "No se encontraron actiones", - "action-source": "Origen de acción", - "action-source-required": "Origen de acción requerido.", - "action-name": "Nombre", - "action-name-required": "Nombre de accion requerido.", - "action-name-not-unique": "Existe una acción con el mismo nombre.\nEl nombre de acción debe ser único dentro de la misma fuente de acción (origen).", - "action-icon": "Icono", - "show-hide-action-using-function": "Mostrar/Ocultar acción usando función", - "action-type": "Tipo", - "action-type-required": "Tipo de acción requerido.", - "edit-action": "Editar acción", - "delete-action": "Borrar acción", - "delete-action-title": "Borrar acción de widget", - "delete-action-text": "Eliminar la acción de widget con el nombre '{{actionName}}'?", - "title-icon": "Título de icono", - "display-icon": "Mostrar título de icono", - "card-icon": "Card icon", - "icon-color": "Color del icono", - "icon-size": "Tamaño del icono", - "advanced-settings": "Ajustes avanzados", - "data-settings": "Ajustes de datos", - "limits": "Limits", - "no-data-display-message": "\"No hay datos que mostrar\" mensaje alternativo", - "data-page-size": "Nº Máximo de entidades por origen de datos", - "settings-component-not-found": "Componente de ajustes no encontrado para el selector '{{selector}}'", - "preview": "Previsualizar", - "set": "Establecer", - "set-message": "Establecer mensaje", - "advanced-title-style": "Estilo de título avanzado", - "card-style": "Estilo de tarjeta", - "text": "Texto", - "background": "Fondo", - "advanced-widget-style": "Estilo avanzado de widget", - "card-buttons": "Botones de tarjeta", - "show-card-buttons": "Mostrar botones", - "card-border-radius": "Radio del borde", - "card-appearance": "Apariencia", - "color": "Color" + "label-widget" : { + "label-pattern" : "Patrón", + "label-pattern-hint" : "Sugerencia: por ejemplo, 'Texto ${keyName} unidades.' o ${#<key index>} unidades'", + "label-pattern-required" : "Se requiere un patrón", + "label-position" : "Posición (porcentaje relativo al fondo)", + "x-pos" : "X", + "y-pos" : "Y", + "background-color" : "Color de fondo", + "font-settings" : "Configuración de fuente", + "background-image" : "Imagen de fondo", + "labels" : "Etiquetas", + "no-labels" : "No hay etiquetas configuradas", + "add-label" : "Agregar etiqueta" }, - "widget-type": { - "import": "Importar tipo de widget", - "export": "Exportar tipo de widget", - "export-failed-error": "Imposible exportar de widget: {{error}}", - "widget-file": "Archivo de widget", - "invalid-widget-type-file-error": "No se puede importar tipo de widget: Estructura de datos del tipo de widget es inválida." + "navigation" : { + "title" : "Título", + "navigation-path" : "Ruta de navegación", + "filter-type" : "Tipo de filtro", + "filter-type-all" : "Todos los elementos", + "filter-type-include" : "Incluir elementos", + "filter-type-exclude" : "Excluir elementos", + "items" : "Elementos", + "enter-urls-to-filter" : "Introduzca las URLs para filtrar..." }, - "widgets": { - "background": { - "background": "Fondo", - "background-settings": "Ajustes de fondo", - "background-type-image": "Subir imagen", - "background-type-image-url": "URL imagen", - "background-type-color": "Color sólido", - "image-url": "URL imagen", - "overlay": "Capa superpuesta", - "enable-overlay": "Activar capa", - "blur": "Difuminar", - "preview": "Previsualizar" - }, - "battery-level": { - "layout": "Diseño", - "layout-vertical-solid": "Vertical. Sólido", - "layout-horizontal-solid": "Horizontal. Sólido", - "layout-vertical-divided": "Vertical. Dividido", - "layout-horizontal-divided": "Horizontal. Dividido", - "icon": "Icono", - "value": "Valor", - "auto-scale": "Auto escalar", - "battery-level-color": "Color del nivel de batería", - "battery-shape-color": "Color de la forma", - "battery-level-card-style": "Estilo de tarjeta batería" - }, - "chart": { - "common-settings": "Ajustes comunes", - "enable-stacking-mode": "Activar modo de apilamiento (stacking)", - "selection": "Selección de rango de tiempo", - "enable-selection-mode": "Habilitar modo de selección", - "line-shadow-size": "Tamaño de sombra (línea)", - "display-smooth-lines": "Mostrar líneas suaves (curvas)", - "default-bar-width": "Ancho de barra por defecto para datos no agregados (millisegundos)", - "bar-alignment": "Alineación barra", - "bar-alignment-left": "Izquierda", - "bar-alignment-right": "Derecha", - "bar-alignment-center": "Centro", - "default-font": "Fuente por defecto", - "default-font-size": "Tamaño de fuente por defecto", - "default-font-color": "Color de fuente por defecto", - "thresholds-line-width": "Ancho de línea por defecto para todos los umbrales", - "tooltip-settings": "Ajustes de sugerencias (tooltip)", - "tooltip": "Tooltip (sugerencias)", - "show-tooltip": "Mostrar sugerencias", - "hover-individual-points": "Hover sobre puntos individuales", - "show-cumulative-values": "Mostrar valores acumulados en modo apilado", - "hide-zero-false-values": "Ocultar valores cero/false en sugerencias", - "tooltip-value-format-function": "Función de formateo de valores en sugerencias (tooltip)", - "grid-settings": "Ajustes de cuadrícula", - "show-vertical-lines": "Mostrar líneas verticales", - "show-horizontal-lines": "Mostrar líneas horizontales", - "grid-outline-border-width": "Ancho de cuadrícula/contorno en px", - "primary-color": "Color primario", - "background-color": "Color de fondo", - "ticks-color": "Color de ticks", - "xaxis-settings": "Ajustes eje X", - "axis-title": "Título de eje", - "xaxis-tick-labels-settings": "Ajustes de etiquetas en ticks eje X", - "show-tick-labels": "Mostrar etiquetas en ticks", - "yaxis-settings": "Ajustes eje Y", - "min-scale-value": "Valor mínimo en la escala", - "max-scale-value": "Valor máximo en la escala", - "yaxis-tick-labels-settings": "Ajustes de etiquetas en ticks eje Y", - "tick-step-size": "Tamaño de paso entre ticks", - "number-of-decimals": "Número de decimales", - "ticks-formatter-function": "Función de formateo de ticks", - "comparison-settings": "Ajustes de comparación", - "enable-comparison": "Activar comparación", - "time-for-comparison": "Período de comparación", - "time-for-comparison-previous-interval": "Intervalo anterior (por defecto)", - "time-for-comparison-days": "Hace un día", - "time-for-comparison-weeks": "Hace una semana", - "time-for-comparison-months": "Hace un mes", - "time-for-comparison-years": "Hace un año", - "time-for-comparison-custom-interval": "Intervalo personalizado", - "custom-interval-value": "Valor de intervalo personalizado (ms)", - "comparison-x-axis-settings": "Ajustes comparación eje X", - "axis-position": "Posición eje", - "axis-position-top": "Superior (por defecto)", - "axis-position-bottom": "Inferior", - "custom-legend-settings": "Ajustes leyenda", - "enable-custom-legend": "Activar leyenda personalizada (habilita poder usar valores de atributos/timeseries en las etiquetas)", - "key-name": "Nombre clave", - "key-name-required": "Nombre clave requerido", - "key-type": "Tipo clave", - "key-type-attribute": "Atributo", - "key-type-timeseries": "Timeseries", - "label-keys-list": "Lista de claves para usar en etiquetas", - "no-label-keys": "No hay claves configuradas", - "add-label-key": "Añadir nueva clave", - "line-width": "Ancho de línea", - "color": "Color", - "data-is-hidden-by-default": "Ocultar datos por defecto", - "disable-data-hiding": "Desactivar ocultación de datos", - "remove-from-legend": "Quitar clave de la leyenda", - "exclude-from-stacking": "Excluir del modo apilado(disponible en el modo \"Apilado\")", - "line-settings": "Ajustes de línea", - "show-line": "Mostrar línea", - "fill-line": "Rellenar línea", - "fill-line-opacity": "Opacidad de línea", - "points-settings": "Ajustes de puntos", - "show-points": "Mostrar puntos", - "points-line-width": "Ancho de línea en puntos", - "points-radius": "Radio de los puntos", - "point-shape": "Forma del punto", - "point-shape-circle": "Círculo", - "point-shape-cross": "Cruz", - "point-shape-diamond": "Diamante", - "point-shape-square": "Cuadrado", - "point-shape-triangle": "Triángulo", - "point-shape-custom": "Función personalizada", - "point-shape-draw-function": "Función de forma del punto", - "show-separate-axis": "Mostrar eje separado", - "axis-position-left": "Izquieda", - "axis-position-right": "Derecha", - "thresholds": "Umbrales", - "no-thresholds": "No hay umbrales configurados", - "add-threshold": "Añadir nuevo umbral", - "show-values-for-comparison": "Mostrar valores históricos para su comparación", - "comparison-values-label": "Etiqueta de valores históricos", - "comparison-line-color": "Color de línea en comparación", - "threshold-settings": "Ajustes de umbrales", - "use-as-threshold": "Usar valor de clave como umbral", - "threshold-line-width": "Ancho de línea (para umbral)", - "threshold-color": "Color umbral", - "common-pie-settings": "Ajustes comunes diagrama de sectores", - "radius": "Radio", - "inner-radius": "Radio interior", - "tilt": "Inclinación", - "common-pie-settings-range-error": "El valor debe estar en el rango de 0 a 1", - "stroke-settings": "Ajustes de trazo", - "width-pixels": "Ancho (pixels)", - "show-labels": "Mostrar etiquetas", - "animation-settings": "Ajustes de animación", - "animated-pie": "Activar animación (experimental)", - "border-settings": "Ajustes de bordes", - "border-width": "Ancho de borde", - "border-color": "Color de borde", - "legend-settings": "Ajustes de leyenda", - "display-legend": "Mostrar leyenda", - "labels-font-color": "Color de texto en leyenda", - "series": "Series", - "add-series": "Añadir series", - "series-settings": "Ajustes de series", - "remove-series": "Eliminar serie", - "no-series": "No hay series configuradas", - "no-series-error": "Al menos se debe especificar una serie", - "chart-appearance": "Apariencia del gráfico", - "vertical-grid-lines": "Líneas de cuadrícula verticales", - "horizontal-grid-lines": "Líneas de cuadrícula horizontales", - "chart-background": "Fondo del gráfico", - "grid-lines-color": "Color de cuadrícula", - "border": "Borde", - "axis": "Eje", - "vertical-axis": "Eje vertical", - "ticks": "Ticks", - "horizontal-axis": "Eje horizontal" - }, - "color": { - "color-settings": "Ajustes de color", - "color-type-constant": "Constante", - "color-type-range": "Rango", - "color-type-function": "Función", - "color": "Color", - "value-range": "Rango de valor", - "from": "Desde", - "to": "A", - "color-function": "Función de color" - }, - "dashboard-state": { - "dashboard-state-settings": "Ajustes estado de panel", - "dashboard-state": "Id de estado panel", - "autofill-state-layout": "Auto-rellenar altura por defecto (obtenida del estado)", - "default-margin": "Márgen entre widgets por defecto", - "default-background-color": "Color de fondo por defecto", - "sync-parent-state-params": "Sincronizar parámetros de estado con el panel padre" - }, - "date-range-navigator": { - "date-range-picker-settings": "Ajustes del selector de fechas", - "hide-date-range-picker": "Ocultar el selector de fechas", - "picker-one-panel": "Selector de fechas para un panel", - "picker-auto-confirm": "Auto-confirmar en selector de fechas", - "picker-show-template": "Mostrar plantilla de selector de fechas", - "first-day-of-week": "Primer día de la semana", - "interval-settings": "Ajustes de intervalo", - "hide-interval": "Ocultar intervalo", - "initial-interval": "Intervalo inicial", - "interval-hour": "Hora", - "interval-day": "Día", - "interval-week": "Semana", - "interval-two-weeks": "2 semanas", - "interval-month": "Mes", - "interval-three-months": "3 meses", - "interval-six-months": "6 meses", - "step-settings": "Ajustes de paso", - "hide-step-size": "Ocultar tamaño de paso", - "initial-step-size": "Tamaño de paso inicial", - "hide-labels": "Ocultar etiquetas", - "use-session-storage": "Usar almacenamiento de sesión", - "localizationMap": { - "Sun": "Dom.", - "Mon": "Lun.", - "Tue": "Mar.", - "Wed": "Mié", - "Thu": "Jue.", - "Fri": "Vie.", - "Sat": "Sáb.", - "Jan": "Ene.", - "Feb": "Feb.", - "Mar": "Mar.", - "Apr": "Abr.", - "May": "May.", - "Jun": "Jun.", - "Jul": "Jul.", - "Aug": "Ago.", - "Sep": "Sept.", - "Oct": "Oct.", - "Nov": "Nov.", - "Dec": "Dic.", - "January": "Enero", - "February": "Febrero", - "March": "Marzo", - "April": "Abril", - "June": "Junio", - "July": "Julio", - "August": "Agosto", - "September": "Septiembre", - "October": "Octubre", - "November": "Noviembre", - "December": "Diciembre", - "Custom Date Range": "Intervalo de fechas personalizado", - "Date Range Template": "Plantilla de rango de fechas", - "Today": "Hoy", - "Yesterday": "Ayer", - "This Week": "Esta semana", - "Last Week": "La semana pasada", - "This Month": "Este mes", - "Last Month": "El mes pasado", - "Year": "Año", - "This Year": "Este año", - "Last Year": "Último", - "Date picker": "Selector de fecha", - "Hour": "Hora", - "Day": "Día", - "Week": "Semana", - "2 weeks": "2 Semanas", - "Month": "Mes", - "3 months": "3 Meses", - "6 months": "6 Meses", - "Custom interval": "Intervalo personalizado", - "Interval": "Intervalo", - "Step size": "Número de pasos", - "Ok": "Ok" - } - }, - "entities-hierarchy": { - "hierarchy-data-settings": "Ajustes de datos de jerarquía", - "relations-query-function": "Función de obtención de relaciones", - "has-children-function": "El nodo tiene una función hija", - "node-state-settings": "Ajustes estado nodo", - "node-opened-function": "Función por defecto al abrir nodo", - "node-disabled-function": "Función con nodo desactivado", - "display-settings": "Mostrar ajustes", - "node-icon-function": "Función de icono nodo", - "node-text-function": "Función de texto nodo", - "sort-settings": "Ajustes de ordenación", - "nodes-sort-function": "Función de ordenación" - }, - "edge": { - "display-default-title": "Mostrar título por defecto" - }, - "gateway": { - "general-settings": "Ajustes generales", - "widget-title": "Título del widget", - "default-archive-file-name": "Nombre del fichero por defecto", - "device-type-for-new-gateway": "Tipo de dispositivo para nuevo gateway", - "messages-settings": "Ajustes de mensajes", - "save-config-success-message": "Mensaje de éxito grabando configuración", - "device-name-exists-message": "Mensaje de texto cuando el nombre del dispositivo ya exista", - "gateway-title": "Formulario Gateway", - "read-only": "Solo lectura", - "events-title": "Título del formulario de eventos", - "events-filter": "Filtro de eventos", - "event-key-contains": "La clave de evento contiene...", - "show-connector": "Mostrar para el conector", - "connector-state-param-key": "Clave del parámetro de estado del conector", - "status": "Estado", - "message": "Mensaje", - "created-time": "Hora de creación" - }, - "gauge": { - "default-color": "Color por defecto", - "radial-gauge-settings": "Ajustes de indicador radial", - "ticks-settings": "Ajustes de ticks", - "min-value": "Valor mínimo", - "max-value": "Valor máximo", - "start-ticks-angle": "Ángulo de inicio ticks", - "ticks-angle": "Ángulo Ticks", - "major-ticks-count": "Nº de ticks principales", - "major-ticks-color": "Color Nº de ticks principales", - "minor-ticks-count": "Nº de ticks secundarios", - "minor-ticks-color": "Color Nº de ticks secundarios", - "tick-numbers-font": "Fuente del tick", - "unit-title-settings": "Ajustes de unidad (título)", - "show-unit-title": "Mostrar unidades (título)", - "unit-title": "Título de unidad", - "title-font": "Fuente de texto del título", - "units-settings": "Ajustes de unidades", - "units-font": "Fuente de texto de las unidades", - "value-box-settings": "Ajustes de valor", - "show-value-box": "Mostrar valor", - "value-int": "Nº de dígitos para la parte entera del valor", - "value-font": "Fuente de texto del valor", - "value-box-rect-stroke-color": "Color del trazo del rectángulo (valor)", - "value-box-rect-stroke-color-end": "Color del trazo del rectángulo (valor) - gradiente final", - "value-box-background-color": "Color de fondo del valor", - "value-box-shadow-color": "Color de sombra del valor", - "plate-settings": "Ajustes de la placa", - "show-plate-border": "Mostrar borde de la placa", - "plate-color": "Color de la placa", - "needle-settings": "Ajustes de la aguja", - "needle-circle-size": "Tamaño del círculo de la aguja", - "needle-color": "Color de la aguja", - "needle-color-end": "Color de la aguja - gradiente final", - "needle-color-shadow-up": "Color de sombreado de la mitad superior de la aguja", - "needle-color-shadow-down": "Color de sombra de la aguja", - "highlights-settings": "Ajustes de resalto", - "highlights-width": "Ancho del resalto", - "highlights": "Resaltos", - "highlight-from": "De", - "highlight-to": "A", - "highlight-color": "Color", - "no-highlights": "No hay resaltos configurados", - "add-highlight": "Añadir resalto", - "animation-settings": "Ajustes de animación", - "enable-animation": "Activar animación", - "animation-duration": "Duración de animación", - "animation-rule": "Regla de animación", - "animation-linear": "Lineal", - "animation-quad": "Quad", - "animation-quint": "Quint", - "animation-cycle": "Ciclo (cycle)", - "animation-bounce": "Rebote (bounce)", - "animation-elastic": "Elastic", - "animation-dequad": "Dequad", - "animation-dequint": "Dequint", - "animation-decycle": "Decycle", - "animation-debounce": "Debounce", - "animation-delastic": "Delastic", - "linear-gauge-settings": "Ajustes de indicador lineal", - "bar-stroke-width": "Ancho del trazo", - "bar-stroke-color": "Color del trazo", - "bar-background-color": "Color de fondo del indicador", - "bar-background-color-end": "Color de fondo del indicador - gradiente final", - "progress-bar-color": "Color de la barra de progreso", - "progress-bar-color-end": "Color de la barra de progreso - gradiente final", - "major-ticks-names": "Nombre de ticks principales", - "show-stroke-ticks": "Mostrar trazo de ticks", - "major-ticks-font": "Fuente de ticks principales", - "border-color": "Color del borde", - "border-width": "Ancho del borde", - "needle-circle-color": "Color del círculo de la aguja", - "animation-target": "Destino de la animación", - "animation-target-needle": "Aguja", - "animation-target-plate": "Placa", - "common-settings": "Ajustes comunes del indicador", - "gauge-type": "Tipo de indicador", - "gauge-type-arc": "Arco", - "gauge-type-donut": "Donut", - "gauge-type-horizontal-bar": "Barra horizontal", - "gauge-type-vertical-bar": "Barra vertical", - "donut-start-angle": "Ángulo de inicio", - "bar-settings": "Ajustes de barra indicadora", - "relative-bar-width": "Ancho relativo", - "neon-glow-brightness": "Efecto brillo de neon, (0-100), 0 - desactiva efecto", - "stripes-thickness": "Grosor de las rayas, 0 - sin rayas", - "rounded-line-cap": "Mostrar tapa de línea redondeada", - "bar-color-settings": "Ajustes de color de barra", - "use-precise-level-color-values": "Usar niveles precisos de color", - "bar-colors": "Colores de la barra, del más bajo al más alto", - "color": "Color", - "no-bar-colors": "No hay colores configurados", - "add-bar-color": "Añadir color", - "from": "De", - "to": "A", - "fixed-level-colors": "Colores de barra usando valores límite", - "gauge-title-settings": "Ajustes de título del indicador", - "show-gauge-title": "Mostrar título de indicador", - "gauge-title": "Título de indicador", - "gauge-title-font": "Fuente del título del indicador", - "unit-title-and-timestamp-settings": "Ajustes del título de unidades y timestamp", - "show-timestamp": "Mostrar valor timestamp", - "timestamp-format": "Formato timestamp", - "label-font": "Fuente de la etiqueta que se muestra bajo el valor", - "value-settings": "Ajustes del valor", - "show-value": "Mostrar texto del valor", - "min-max-settings": "Etiqueta mínimo/máximo", - "show-min-max": "Mostrar valores mínimos y máximos", - "min-max-font": "Fuente de los valores mínimo y máximo", - "show-ticks": "Mostrar ticks", - "tick-width": "Ancho de tick", - "tick-color": "Color de tick", - "tick-values": "Valores de tick", - "no-tick-values": "No hay valores configurados", - "add-tick-value": "Añadir valor de tick" - }, - "gpio": { - "pin": "Pin", - "label": "Etiqueta", - "row": "Fila", - "column": "Columna", - "color": "Color", - "panel-settings": "Ajustes de panel", - "background-color": "Color de fondo", - "gpio-switches": "switches GPIO", - "no-gpio-switches": "No hay switches GPIO configurados", - "add-gpio-switch": "Añadir switch GPIO", - "gpio-status-request": "Solicitud estado GPIO", - "method-name": "Nombre del método RPC", - "method-body": "Cuerpo del método RPC", - "gpio-status-change-request": "Solicitud de cambio de estado GPIO", - "parse-gpio-status-function": "Función de parseado de estado GPIO", - "gpio-leds": "Leds GPIO", - "no-gpio-leds": "No hay Leds GPIO configurados", - "add-gpio-led": "Añadir led GPIO" - }, - "html-card": { - "html": "HTML", - "css": "CSS" - }, - "input-widgets": { - "attribute-not-allowed": "El parámetro de atributo no se puede usar en este widget", - "blocked-location": "La función de geolocalización está bloqueada en tu navegador", - "claim-device": "Reclamar dispositivo", - "claim-failed": "Error al reclamar dispositivo!", - "claim-not-found": "Dispositivo no encontrado!", - "claim-successful": "Dispositivo reclamado correctamente!", - "date": "Fecha", - "device-name": "Nombre del dispositivo", - "device-name-required": "Se requere nombre de dispositivo", - "discard-changes": "Descartar los cambios", - "entity-attribute-required": "Se requiere atributo de entidad", - "entity-coordinate-required": "Se requieren ambos campos (latitud y longitud)", - "entity-timeseries-required": "Se requiere la serie de tiempo de la entidad", - "get-location": "Obtener localización actual", - "invalid-date": "Fecha inválida", - "latitude": "Latitud", - "longitude": "Longitud", - "min-value-error": "El valor mínimo es {{value}}", - "max-value-error": "El valor máximo es {{value}}", - "not-allowed-entity": "La entidad seleccionada no puede tener atributos compartidos", - "no-attribute-selected": "No se seleccionó ningún atributo", - "no-datakey-selected": "No se seleccionó ninguna clave de datos", - "no-coordinate-specified": "No se ha especificado la clave para latitud/longitud", - "no-entity-selected": "Ninguna entidad seleccionada", - "no-image": "Sin imagen", - "no-support-geolocation": "Tu navegador no soporta geolocalización", - "no-support-web-camera": "No hay cámara web compatible", - "enable-https-use-widget": "Por favor, activa HTTPS para poder usar este widget", - "no-found-your-camera": "No es posible encontrar la cámara", - "no-permission-camera": "Permiso denegado por el usuario / Esta página no tiene permisos para usar la cámara", - "no-timeseries-selected": "No hay series de tiempo seleccionadas", - "secret-key": "Clave", - "secret-key-required": "Clave requerida", - "switch-attribute-value": "Cambiar el valor del atributo de entidad", - "switch-camera": "Cambiar de cámara", - "switch-timeseries-value": "Cambiar el valor de la serie de tiempo de la entidad", - "take-photo": "Tomar foto", - "time": "Tiempo", - "timeseries-not-allowed": "El parámetro Timeseries no se puede usar en este widget", - "update-failed": "Actualización fallida", - "update-successful": "Actualización exitosa", - "update-attribute": "Actualizar atributo", - "update-timeseries": "Actualizar series de tiempo", - "value": "Valor", - "general-settings": "Ajustes Generales", - "widget-title": "Título del widget", - "claim-button-label": "Etiqueta del botón de reclamar", - "show-secret-key-field": "Mostrar el campo 'Clave Secreta'", - "labels-settings": "Ajustes de etiquetas", - "show-labels": "Mostrar etiquetas", - "device-name-label": "Etiqueta para el campo de entrada 'Nombre de Dispositivo'", - "secret-key-label": "Etiqueta para el campo de entrara 'Clave Secreta'", - "messages-settings": "Ajustes de mensajes", - "claim-device-success-message": "Mensaje a mostrar cuando el dispositivo se haya reclamado ok", - "claim-device-not-found-message": "Mensaje a mostrar cuando el dispositivo no se encuentre", - "claim-device-failed-message": "Mensaje a mostrar cuando ocurra un error reclamando el dispositivo", - "claim-device-name-required-message": "Mensaje de error a mostrar con 'Nombre de dispositivo requerido'", - "claim-device-secret-key-required-message": "Mensaje de error a mostrar con 'Clave Secreta requerida'", - "show-label": "Mostrar etiqueta", - "label": "Etiqueta", - "required": "Requerido", - "required-error-message": "Error a mostrar con campo 'Requerido'", - "show-result-message": "Mensaje para mostrar resultado", - "integer-field-settings": "Ajustes de campos tipo entero", - "min-value": "Valor Mínimo", - "max-value": "Valor Máximo", - "double-field-settings": "Ajustes de campos tipo double", - "text-field-settings": "Ajustes de campos tipo texto", - "min-length": "Longitud mínima", - "max-length": "Longitud máxima", - "checkbox-settings": "Ajustes de campos tipo checkbox", - "true-label": "Etiqueta checked", - "false-label": "Etiqueta unchecked", - "image-input-settings": "Ajustes de campos tipo entrada de imagen", - "display-preview": "Mostrar previsualización", - "display-clear-button": "Mostrar botón de borrar", - "display-apply-button": "Mostrar botón de aplicar", - "display-discard-button": "Mostrar botón de descartar", - "datetime-field-settings": "Ajustes de campos tipo Fecha/Hora", - "display-time-input": "Mostrar entrada de hora", - "latitude-key-name": "Nombre clave latitud", - "longitude-key-name": "Nombre clave longitud", - "show-get-location-button": "Mostrar botón 'Obtener localización actual'", - "use-high-accuracy": "Usar alta precisión", - "location-fields-settings": "Ajustes de campos de localización", - "latitude-label": "Etiqueta para latitud", - "longitude-label": "Etiqueta para longitud", - "input-fields-alignment": "Alineado de campos de entrada", - "input-fields-alignment-column": "Columna (por defecto)", - "input-fields-alignment-row": "Fila", - "layout": "Diseño", - "row-gap": "Espacio entre filas en píxeles", - "column-gap": "Espacio entre columnas en píxeles", - "latitude-field-required": "Campo latitud requerido", - "longitude-field-required": "Campo longitud requerido", - "attribute-settings": "Ajustes de atributos", - "widget-mode": "Modo del widget", - "widget-mode-update-attribute": "Actualizar atributo", - "widget-mode-update-timeseries": "Actualizar timeseries", - "attribute-scope": "Alcance de atributos", - "attribute-scope-server": "Atributos de servidor", - "attribute-scope-shared": "Atributos compartidos", - "value-required": "Valor requerido", - "image-settings": "Ajustes de imagen", - "image-format": "Formato de imagen", - "image-format-jpeg": "JPEG", - "image-format-png": "PNG", - "image-format-webp": "WEBP", - "image-quality": "Calidad de imagen que usa compresión con pérdida como jpeg y webp", - "max-image-width": "Máximo ancho de imagen", - "max-image-height": "Máximo alto de imagen", - "action-buttons": "Botones de acción", - "show-action-buttons": "Mostrar botones de acción", - "update-all-values": "Actualizar todos los valores, no sólo los modificados", - "save-button-label": "Etiqueta de botón 'GRABAR'", - "reset-button-label": "Etiqueta de botón 'DESHACER'", - "group-settings": "Ajustes de grupo", - "show-group-title": "Mostrar título para el grupo de campos relacionados con diferentes entidades", - "group-title": "Título del grupo", - "fields-alignment": "Alineado de campos", - "fields-alignment-row": "Fila (por defecto)", - "fields-alignment-column": "Columna", - "fields-in-row": "Número de campos en la fila", - "option-value": "Valor (escribe 'null' para crear una opción vacía)", - "option-label": "Etiqueta", - "hide-input-field": "Ocultar campo de entrada", - "datakey-type": "Tipo de clave de datos", - "datakey-type-server": "Atributo de servidor (por defecto)", - "datakey-type-shared": "Atributo compartido", - "datakey-type-timeseries": "Timeseries", - "datakey-value-type": "Tipo de valor de la clave de datos", - "datakey-value-type-string": "String", - "datakey-value-type-double": "Double", - "datakey-value-type-integer": "Entero", - "datakey-value-type-json": "JSON", - "datakey-value-type-boolean-checkbox": "Booleano (Checkbox)", - "datakey-value-type-boolean-switch": "Booleano (Switch)", - "datakey-value-type-date-time": "Fecha & Hora", - "datakey-value-type-date": "Fecha", - "datakey-value-type-time": "Hora", - "datakey-value-type-select": "Selección", - "datakey-value-type-color": "Color", - "value-is-required": "Valor requerido", - "ability-to-edit-attribute": "Posibilidad de editar atributo", - "ability-to-edit-attribute-editable": "Editable (por defecto)", - "ability-to-edit-attribute-disabled": "Desactivado", - "ability-to-edit-attribute-readonly": "Sólo lectura", - "disable-on-datakey-name": "Desactivar cuando otra clave de datos sea false (especificar nombre de clave de datos)", - "field-appearance": "Apariencia de los campos", - "appearance-fill": "Relleno", - "appearance-outline": "Contorno", - "subscript-sizing": "Tamaño subscript", - "subscript-sizing-fixed": "Fijo", - "subscript-sizing-dynamic": "Dinámico", - "slide-toggle-settings": "Ajustes de deslizador", - "slide-toggle-label-position": "Posición de etiqueta", - "slide-toggle-label-position-after": "Después", - "slide-toggle-label-position-before": "Antes", - "select-options": "Seleccionar opciones", - "no-select-options": "No hay opciones configuradas", - "add-select-option": "Añadir opción", - "numeric-field-settings": "Ajustes de campos numéricos", - "step-interval": "Pasos entre valores (intervalo)", - "error-messages": "Mensajes de erro", - "min-value-error-message": "Mensaje de error de 'Valor Mínimo'", - "max-value-error-message": "Mensaje de error de 'Valor Máximo'", - "invalid-date-error-message": "Mensaje de error de 'Fecha Inválida'", - "invalid-JSON-error-message": "Mensaje de error de 'JSON inválido'", - "icon-settings": "Ajustes de iconos", - "dialog-editor-settings": "Ajustes de editor de diálogo", - "use-custom-icon": "Usar icono personalizado", - "input-cell-icon": "Icono a mostrar antes del campo de entrada", - "value-conversion-settings": "Ajustes de conversión de valor", - "get-value-settings": "Ajustes de obtención de valores", - "use-get-value-function": "Usar función getValue", - "get-value-function": "Función getValue", - "set-value-settings": "Ajustes de establecimiento de valores", - "use-set-value-function": "Usar función setValue", - "set-value-function": "Función setValue", - "json-invalid": "El valor JSON tiene un formato inválido", - "title": "Título", - "cancel-button-label": "Etiqueta del botón 'Cancelar'" - }, - "invalid-qr-code-text": "Texto de entrara inválido para el código QR. La entrada debe ser de tipo string", - "qr-code": { - "use-qr-code-text-function": "Usar función de texto QR", - "qr-code-text-pattern": "Patrón del código QR (por ej. '${entityName} | ${keyName} - texto adicional.')", - "qr-code-text-pattern-hint": "El patrón del código QR usa el valor de la primera clave encontrada en las entidades del alias.", - "qr-code-text-pattern-required": "Se requiere patrón del código QR.", - "qr-code-text-function": "Función del código QR" - }, - "label-widget": { - "label-pattern": "Patrón", - "label-pattern-hint": "Ayuda: por ej. 'Texto ${keyName} unidades.' o ${#<key index>} unidades'", - "label-pattern-required": "Se requiere patrón", - "label-position": "Posición (Porcentaje relativo al fondo)", - "x-pos": "X", - "y-pos": "Y", - "background-color": "Color de fondo", - "font-settings": "Ajustes de fuente", - "background-image": "Imagen de fondo", - "labels": "Etiquetas", - "no-labels": "No hay etiquetas configuradas", - "add-label": "Añadir etiqueta" - }, - "navigation": { - "title": "Título", - "navigation-path": "Ruta de navegación", - "filter-type": "Tipo de filtro", - "filter-type-all": "Todos los objetos", - "filter-type-include": "Incluir objetos", - "filter-type-exclude": "Excluir objetos", - "items": "Objetos", - "enter-urls-to-filter": "Especificar URLs a filtrar..." - }, - "persistent-table": { - "rpc-id": "ID RPC", - "message-type": "Tipo de mensaje", - "method": "Método", - "params": "Parémetros", - "created-time": "Hora de creación", - "expiration-time": "Hora de expiración", - "retries": "Reintentos", - "status": "Estado", - "filter": "Filtro", - "refresh": "Actualizar", - "add": "Añadir petición RPC", - "details": "Detalles", - "delete": "Borrar", - "delete-request-title": "Borrar petición RPC persistente", - "delete-request-text": "Estas seguro de borrar la petición?", - "details-title": "Detalles ID RPC: ", - "additional-info": "Información adicional", - "response": "Respuesta", - "any-status": "Cualquier estado", - "rpc-status-list": "Lista de estados RPC", - "no-request-prompt": "No hay peticiones a mostrar", - "send-request": "Enviar petición", - "add-title": "Crear una petición RPC persistente", - "method-error": "Se requiere método.", - "timeout-error": "El valor mínimo de timeout es 5000 (5 segundos).", - "white-space-error": "No se permiten espacios en blanco.", - "rpc-status": { - "QUEUED": "EN COLA", - "SENT": "ENVIADO", - "DELIVERED": "ENTREGADO", - "SUCCESSFUL": "CORRECTO", - "TIMEOUT": "TIMEOUT", - "EXPIRED": "EXPIRADO", - "FAILED": "FALLO" - }, - "rpc-search-status-all": "TODOS", - "message-types": { - "false": "Bidireccional (Two-way)", - "true": "Unidireccional (One-way)" - }, - "general-settings": "Ajustes Generales", - "enable-filter": "Activar filtro", - "enable-sticky-header": "Mostrar encabezado mientras se hace scroll", - "enable-sticky-action": "Mostrar columna de acciones mientras se hace scroll", - "display-request-details": "Mostrar detalles de petición", - "allow-send-request": "Permitir enviar petición RPC", - "allow-delete-request": "Permitir borrar petición", - "columns-settings": "Ajustes de columnas", - "display-columns": "Columnas a mostrar", - "column": "Columna", - "no-columns-found": "No se encontraron columnas", - "no-columns-matching": "'{{column}}' no encontrada." - }, - "rpc": { - "value-settings": "Ajustes de valor", - "initial-value": "Valor inicial", - "retrieve-value-settings": "Obtener ajustes valores on/off", - "retrieve-value-method": "Obtener valor usando método", - "retrieve-value-method-none": "No obtener", - "retrieve-value-method-rpc": "Método para llamada RPC de obtención de valor", - "retrieve-value-method-attribute": "Suscribir por atributo", - "retrieve-value-method-timeseries": "Suscribir por timeseries", - "attribute-value-key": "Clave de atributo", - "timeseries-value-key": "Clave de timeseries", - "get-value-method": "Metodo para obtener valor vía RPC", - "parse-value-function": "Función de parseo de valor", - "update-value-settings": "Ajustes de actualización de valor", - "set-value-method": "Método para establecer valor vía RPC", - "convert-value-function": "Función de conversión", - "rpc-settings": "Ajustes RPC", - "request-timeout": "Timeout de petición RPC (ms)", - "persistent-rpc-settings": "Ajustes de RPC persistente", - "request-persistent": "Petición RPC persistente", - "persistent-polling-interval": "Intervalo de sondeo (ms) para obtener respuesta del comando RPC persistente", - "common-settings": "Ajustes comunes", - "switch-title": "Título del switch", - "show-on-off-labels": "Mostrar etiquetas on/off", - "slide-toggle-label": "Etiqueta de interruptor deslizante", - "label-position": "Posición de etiqueta", - "label-position-before": "Antes", - "label-position-after": "Después", - "slider-color": "Color del deslizador", - "slider-color-primary": "Primario", - "slider-color-accent": "Acento", - "slider-color-warn": "Aviso", - "button-style": "Estilo de botón", - "button-raised": "Botón levantado", - "button-primary": "Color primario", - "button-background-color": "Color de fondo", - "button-text-color": "Color de texto", - "widget-title": "Título del widget", - "button-label": "Etiqueta del botón", - "device-attribute-scope": "Alcance de atributos del dispositivo", - "server-attribute": "Atributos de servidor", - "shared-attribute": "Atributos compartidos", - "device-attribute-parameters": "Parámetros de atributos del dispositivo", - "is-one-way-command": "Es un comando de una vía (one way)", - "rpc-method": "Método RPC", - "rpc-method-params": "Parámetros método RPC", - "show-rpc-error": "Mostrar errores de ejecución de RPC", - "led-title": "Título LED", - "led-color": "Color LED", - "check-status-settings": "Ajustes de comprobación de estado", - "perform-rpc-status-check": "Realizar comprobación del dispositivo RPC", - "retrieve-led-status-value-method": "Obtener estado del led usando método", - "led-status-value-attribute": "Atributo del dispositivo que contiene el valor del estado del led", - "led-status-value-timeseries": "Timeseries del dispositivo que contiene el valor del estado del led", - "check-status-method": "Método RPC para chequear el estado del dispositivo", - "parse-led-status-value-function": "Función de parseo para el estado del led", - "knob-title": "Título de mando", - "min-value": "Valor mínimo", - "max-value": "Valor máximo" - }, - "maps": { - "select-entity": "Seleccionar entidad", - "select-entity-hint": "Ayuda: tras la selección haz click en el mapa para establecer la posición", - "tooltips": { - "placeMarker": "Click para colocar la entidad '{{entityName}}'", - "firstVertex": "Polígono para la entidad '{{entityName}}': click para colocar el primer punto", - "firstVertex-cut": "Click para colocar el primer punto", - "continueLine": "Polígono para la entidad '{{entityName}}': click para continuar dibujando", - "continueLine-cut": "Click para continuar dibujando", - "finishLine": "Click en cualquier marcador existente para finalizar", - "finishPoly": "Polígono para la entidad '{{entityName}}': click en el primer marcador para finalizar y grabar los cambios", - "finishPoly-cut": "Click en el primer marcador para finalizar y grabar los cambios", - "finishRect": "Polígono para la entidad '{{entityName}}': click para finalizar y grabar los cambios", - "startCircle": "Círculo para la entidad '{{entityName}}': click para establecer el centro del círculo", - "finishCircle": "Círculo para la entidad '{{entityName}}': click para finalizar el círculo", - "placeCircleMarker": "Click para colocar el marcador del círculo" - }, - "actions": { - "finish": "Finalizar", - "cancel": "Cancelar", - "removeLastVertex": "Quitar último punto" - }, - "buttonTitles": { - "drawMarkerButton": "Establecer entidad", - "drawPolyButton": "Crear polígono", - "drawLineButton": "Crear polilínea", - "drawCircleButton": "Crear círculo", - "drawRectButton": "Crear rectángulo", - "editButton": "Modo de edición", - "dragButton": "Modo Arrastrar-soltar", - "cutButton": "Cortar el área del polígono", - "deleteButton": "Borrar", - "drawCircleMarkerButton": "Crear marcador de círculo", - "rotateButton": "Rotar polígono" - }, - "map-provider-settings": "Ajustes proveedor de mapas", - "map-provider": "Proveedor de mapas", - "map-provider-google": "Google maps", - "map-provider-openstreet": "OpenStreet maps", - "map-provider-here": "HERE maps", - "map-provider-image": "Mapa de imagen", - "map-provider-tencent": "Tencent maps", - "openstreet-provider": "Proveedor OpenStreet map", - "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Defecto)", - "openstreet-provider-hot": "OpenStreetMap.HOT", - "openstreet-provider-esri-street": "Esri.WorldStreetMap", - "openstreet-provider-esri-topo": "Esri.WorldTopoMap", - "openstreet-provider-esri-imagery": "Esri.WorldImagery", - "openstreet-provider-cartodb-positron": "CartoDB.Positron", - "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", - "use-custom-provider": "Usar proveedor personalizado", - "custom-provider-tile-url": "URL de proveedor personalizado", - "google-maps-api-key": "API Key Google Maps", - "default-map-type": "Tipo de mapa por defecto", - "google-map-type-roadmap": "Carretera", - "google-map-type-satelite": "Satelite", - "google-map-type-hybrid": "Híbrido", - "google-map-type-terrain": "Terreno", - "map-layer": "Capa de mapa", - "here-map-normal-day": "HERE.normalDay (Defecto)", - "here-map-normal-night": "HERE.normalNight", - "here-map-hybrid-day": "HERE.hybridDay", - "here-map-terrain-day": "HERE.terrainDay", - "credentials": "Credenciales", - "here-app-id": "HERE app id", - "here-app-code": "HERE app code", - "here-api-key": "HERE API key", - "here-use-new-version-api-3": "Usar API versión 3", - "tencent-maps-api-key": "API Key Tencent Maps", - "tencent-map-type-roadmap": "Carretera", - "tencent-map-type-satelite": "Satelite", - "tencent-map-type-hybrid": "Híbrido", - "image-map-background": "Fondo de mapa de imagen", - "image-map-background-from-entity-attribute": "Obtener imagen de fondo desde un atributo de la entidad", - "image-url-source-entity-alias": "Alias de entidad para URL de imagen", - "image-url-source-entity-attribute": "Atributo de entidad para URL de imagen", - "common-map-settings": "Ajustes comunes de mapas", - "x-pos-key-name": "Clave para posición X", - "y-pos-key-name": "Clave para posición Y", - "latitude-key-name": "Clave para latitud", - "longitude-key-name": "Clave para longitud", - "default-map-zoom-level": "Nivel de zoom por defecto (0 - 20)", - "default-map-center-position": "Posición central del mapa por defecto (0,0)", - "disable-scroll-zooming": "Desactivar zoom en scroll", - "disable-double-click-zooming": "Desactivar zoom en doble click", - "disable-zoom-control-buttons": "Desactivar botones de zoom", - "fit-map-bounds": "Ajustar los límites del mapa para cubrir todos los marcadores", - "use-default-map-center-position": "Usar posición central por defecto", - "entities-limit": "Límite de entidades a cargar", - "markers-settings": "Ajustes de los marcadores", - "marker-offset-x": "Offset X relativo a la posición multiplicado por el ancho del marcador", - "marker-offset-y": "Offset Y relativo a la posición multiplicado por el alto del marcador", - "position-function": "Función de conversión de posición, debe retornar coordenadas x,y como valor double de 0 a 1", - "draggable-marker": "Marcador arrastrable", - "label": "Etiqueta", - "show-label": "Mostrar etiqueta", - "use-label-function": "Usar función de etiqueta", - "label-pattern": "Etiqueta (Ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", - "label-function": "Función de etiqueta", - "tooltip": "Sugerencias (tooltip)", - "show-tooltip": "Mostrar sugerencias", - "show-tooltip-action": "Acción para mostrar las sugerencias", - "show-tooltip-action-click": "Mostrar sugerencias tooltip en click (por defecto)", - "show-tooltip-action-hover": "Mostrar sugerencias on hover", - "auto-close-tooltips": "Auto-cerrar sugerencias", - "use-tooltip-function": "Función de sugerencias", - "tooltip-pattern": "Sugerencia (Por ej. 'Texto ${keyName} unidades.' o Texto Link')", - "tooltip-function": "Función de sugerencia", - "tooltip-offset-x": "Offset X relativo al ancla del marcador multiplicado por el ancho", - "tooltip-offset-y": "Offset Y relativo al ancla del marcador multiplicado por la altura", - "color": "Color", - "use-color-function": "Usar función de color", - "color-function": "Función de color", - "marker-image": "Imagen de marcador", - "use-marker-image-function": "Usar función de imagen de marcador", - "custom-marker-image": "Imagen de marcador personalizada", - "custom-marker-image-size": "Tamaño de imagen personalizada (px)", - "marker-image-function": "Función de imagen de marcador", - "marker-images": "Imagenes de marcador", - "polygon-settings": "Ajustes de polígono", - "show-polygon": "Mostrar polígono", - "polygon-key-name": "Clave del polígono", - "enable-polygon-edit": "Polígono editable", - "polygon-label": "Etiqueta del polígono", - "show-polygon-label": "Mostrar etiqueta del polígono", - "use-polygon-label-function": "Usar funciones de etiqueta en el polígono", - "polygon-label-pattern": "Etiqueta del polígono (Ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", - "polygon-label-function": "Función de etiqueta del polígono", - "polygon-tooltip": "Sugerencia polígono", - "show-polygon-tooltip": "Mostrar sugerencias", - "auto-close-polygon-tooltips": "Auto-cerrar sugerencias polígono", - "use-polygon-tooltip-function": "Usar función de sugerencias en el polígono", - "polygon-tooltip-pattern": "Sugerencias (por ej. 'Texto ${keyName} unidades.' o Texto Link')", - "polygon-tooltip-function": "Función de sugerencia de polígono", - "polygon-color": "Color de polígono", - "polygon-opacity": "Opacidad de polígono", - "use-polygon-color-function": "Usar función de color en polígono", - "polygon-color-function": "Función de color de polígono", - "polygon-stroke": "Trazo de polígono", - "stroke-color": "Color de trazo", - "stroke-opacity": "Opacidad de trazo", - "stroke-weight": "Peso de trazo", - "use-polygon-stroke-color-function": "Usar función de color de trazo en polígono", - "polygon-stroke-color-function": "Función de color de trazo", - "circle-settings": "Ajustes de círculo", - "show-circle": "Mostrar círculo", - "circle-key-name": "Clave de círculo", - "enable-circle-edit": "Activar edición de círculo", - "circle-label": "Etiqueta de círculo", - "show-circle-label": "Mostrar etiqueta de círculo", - "use-circle-label-function": "Usar función para etiqueta de círculo", - "circle-label-pattern": "Etiqueta de círculo (Ejemplos patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", - "circle-label-function": "Funcion de etiqueta de círculo", - "circle-tooltip": "Sugerencias de círculo", - "show-circle-tooltip": "Mostrar sugerencias de círculo", - "auto-close-circle-tooltips": "Auto-cerrar sugerencias", - "use-circle-tooltip-function": "Usar función de sugerencias en círculo", - "circle-tooltip-pattern": "Sugerencia (por ej. 'Texto ${keyName} unidades.' o Texto Link')", - "circle-tooltip-function": "Función de sugerencia círculo", - "circle-fill-color": "Color de relleno círculo", - "circle-fill-color-opacity": "Opacidad color de relleno", - "use-circle-fill-color-function": "Usar función de color de relleno", - "circle-fill-color-function": "Función de color de relleno", - "circle-stroke": "Trazo del círculo", - "use-circle-stroke-color-function": "Usar función de color de trazo", - "circle-stroke-color-function": "Función de color de trazo", - "markers-clustering-settings": "Ajustes de agrupación de marcadores", - "use-map-markers-clustering": "Usar agrupación de marcadores en mapa", - "zoom-on-cluster-click": "Zoom cuando se haga click en un grupo", - "max-cluster-zoom": "Nivel máximo de zoom en la que un marcador puede ser parte de un grupo (0 - 18)", - "max-cluster-radius-pixels": "Radio máximo que un grupo cubre en píxeles", - "cluster-zoom-animation": "Mostrar animacion en marcadores cuando se haga zoom", - "show-markers-bounds-on-cluster-mouse-over": "Mostrar los limites de los marcadores cuando el raton pase por encima de un grupo", - "spiderfy-max-zoom-level": "Spiderfy al máximo nivel de zoom (para ver todos los marcadores)", - "load-optimization": "Optimización de carga", - "cluster-chunked-loading": "Usar fragmentos para añadir marcadores para que la página no se congele", - "cluster-markers-lazy-load": "Usar lazy load al añadir marcadores", - "editor-settings": "Ajustes del editor", - "enable-snapping": "Habilitar ajuste a otros vértices para dibujar con precisión", - "init-draggable-mode": "Inicializar mapa en modo arrastre", - "hide-all-edit-buttons": "Ocultar todos los botones de edición", - "hide-draw-buttons": "Ocultar botones de dibujo", - "hide-edit-buttons": "Ocultar botones de edición", - "hide-remove-button": "Ocultar botones de borrado", - "route-map-settings": "Ajustes de mapa de ruta", - "trip-animation-settings": "Ajustes de animación de ruta", - "normalization-step": "Pasos de normalización (ms)", - "tooltip-background-color": "Color de fondo de la sugerencia", - "tooltip-font-color": "Color de fuente de las sugerencias", - "tooltip-opacity": "Opacidad de las sugerencias (0-1)", - "auto-close-tooltip": "Auto-cerrar sugerencias", - "rotation-angle": "Ángulo de rotación adicional para el marcador (grados)", - "path-settings": "Ajustes de ruta", - "path-color": "Color de ruta", - "use-path-color-function": "Usar función de color de ruta", - "path-color-function": "Función de color de ruta", - "path-decorator": "Decorador de ruta", - "use-path-decorator": "Usar decorador de ruta", - "decorator-symbol": "Símbolo del decorador", - "decorator-symbol-arrow-head": "Flecha", - "decorator-symbol-dash": "Estrella", - "decorator-symbol-size": "Tamaño del decorador (px)", - "use-path-decorator-custom-color": "Usar color personalizado en el decorador", - "decorator-custom-color": "Color personalizado del decorador", - "decorator-offset": "Offset decorador", - "end-decorator-offset": "Offset final del decorador", - "decorator-repeat": "Repetición del decorador", - "points-settings": "Ajustes de puntos", - "show-points": "Mostrar puntos", - "point-color": "Color de puntos", - "point-size": "Tamaño de puntos (px)", - "use-point-color-function": "Usar función de color de puntos", - "point-color-function": "Función de color de puntos", - "use-point-as-anchor": "Usar punto como ancla", - "point-as-anchor-function": "Función de punto como ancla", - "independent-point-tooltip": "Sugerencia independiente en punto", - "clustering-markers": "Marcadores agrupados", - "use-icon-create-function": "Usar función de color en marcadores", - "marker-color-function": "Función de color del marcador" - }, - "markdown": { - "use-markdown-text-function": "Usar función markdown/HTML", - "markdown-text-function": "Función de valor Markdown/HTML", - "markdown-text-pattern": "Patrón de Markdown/HTML (markdown o HTML con variables, por ej. '${entityName} o ${keyName} - texto.')", - "apply-default-markdown-style": "Aplicar estilo por defecto en markdown", - "markdown-css": "Markdown/HTML CSS" - }, - "simple-card": { - "label": "Etiqueta", - "label-position": "Posición etiqueta", - "label-position-left": "Izquierda", - "label-position-top": "Superior" - }, - "value-card": { - "layout": "Diseño", - "layout-square": "Cuadrado", - "layout-vertical": "Vertical", - "layout-centered": "Centrado", - "layout-simplified": "Simplificado", - "layout-horizontal": "Horizontal", - "layout-horizontal-reversed": "Horizontal inverso", - "label": "Etiqueta", - "icon": "Icono", - "value": "Valor", - "date": "Fecha", - "value-card-style": "Estilo de tarjeta valores" - }, - "aggregated-value-card": { - "subtitle": "Subtítulo", - "chart": "Gráfico", - "values": "Valores", - "value-appearance": "Apariencia de valores", - "position": "Posición", - "position-center": "Centro", - "position-right-top": "Arriba a la derecha", - "position-right-bottom": "Abajo a la derecha", - "position-left-top": "Arriba a la izquierda", - "position-left-bottom": "Abajo a la izquierda", - "font": "Fuente", - "color": "Color", - "arrow": "Flecha", - "display-up-down-arrow": "Mostrar flecha Arriba/Abajo", - "add-value": "Añadir valor", - "remove-value": "Quitar valor", - "no-values": "No hay valores configurados", - "aggregation": "Agregación", - "aggregated-value-card-style": "Estilo de tarjeta valores agregados" - }, - "alarm-count": { - "alarm-count-card-style": "Estilo de tarjeta contaje de alarmas" + "persistent-table" : { + "rpc-id" : "ID de RPC", + "message-type" : "Tipo de mensaje", + "method" : "Método", + "params" : "Parámetros", + "created-time" : "Fecha de creación", + "expiration-time" : "Fecha de expiración", + "retries" : "Reintentos", + "status" : "Estado", + "filter" : "Filtro", + "refresh" : "Actualizar", + "add" : "Agregar solicitud RPC", + "details" : "Detalles", + "delete" : "Eliminar", + "delete-request-title" : "Eliminar solicitud RPC persistente", + "delete-request-text" : "¿Está seguro de que desea eliminar la solicitud?", + "details-title" : "Detalles del RPC ID: ", + "additional-info" : "Información adicional", + "response" : "Respuesta", + "any-status" : "Cualquier estado", + "rpc-status-list" : "Lista de estados RPC", + "no-request-prompt" : "No hay solicitudes para mostrar", + "send-request" : "Enviar solicitud", + "add-title" : "Crear solicitud RPC persistente", + "method-error" : "El método es obligatorio.", + "timeout-error" : "El valor mínimo de espera es 5000 (5 segundos).", + "white-space-error" : "No se permiten espacios en blanco.", + "rpc-status" : { + "QUEUED" : "EN COLA", + "SENT" : "ENVIADO", + "DELIVERED" : "ENTREGADO", + "SUCCESSFUL" : "EXITOSO", + "TIMEOUT" : "TIEMPO DE ESPERA AGOTADO", + "EXPIRED" : "EXPIRADO", + "FAILED" : "FALLIDO" + }, + "rpc-search-status-all" : "TODOS", + "message-types" : { + "false" : "Bidireccional", + "true" : "Unidireccional" + }, + "general-settings" : "Configuraciones generales", + "enable-filter" : "Habilitar filtro", + "enable-sticky-header" : "Mostrar encabezado durante el desplazamiento", + "enable-sticky-action" : "Mostrar columna de acciones durante el desplazamiento", + "display-request-details" : "Mostrar detalles de la solicitud", + "allow-send-request" : "Permitir envío de solicitud RPC", + "allow-delete-request" : "Permitir eliminación de la solicitud", + "columns-settings" : "Configuración de columnas", + "display-columns" : "Columnas a mostrar", + "column" : "Columna", + "no-columns-found" : "No se encontraron columnas", + "no-columns-matching" : "'{{column}}' no encontrado." + }, + "range-chart" : { + "chart" : "Gráfico", + "data-zoom" : "Zoom de datos", + "range-chart-appearance" : "Apariencia del gráfico de rango", + "range-colors" : "Colores de rango", + "out-of-range-color" : "Color fuera de rango", + "show-range-thresholds" : "Mostrar umbrales de rango", + "range-thresholds-settings" : "Configuraciones de umbrales de rango", + "fill-area" : "Rellenar área", + "fill-area-opacity" : "Opacidad del área rellena", + "range-chart-style" : "Estilo del gráfico de rango" + }, + "rpc" : { + "value-settings" : "Configuración de valor", + "initial-value" : "Valor inicial", + "retrieve-value-settings" : "Configuración para recuperar valor de encendido/apagado", + "retrieve-value-method" : "Recuperar valor usando método", + "retrieve-value-method-none" : "No recuperar", + "retrieve-value-method-rpc" : "Llamar método RPC para obtener valor", + "retrieve-value-method-attribute" : "Suscribirse al atributo", + "retrieve-value-method-timeseries" : "Suscribirse a la serie temporal", + "attribute-value-key" : "Clave del atributo", + "timeseries-value-key" : "Clave de la serie temporal", + "get-value-method" : "Método RPC para obtener valor", + "parse-value-function" : "Función para analizar valor", + "update-value-settings" : "Configuración para actualizar valor", + "set-value-method" : "Método RPC para establecer valor", + "convert-value-function" : "Función para convertir valor", + "rpc-settings" : "Configuración RPC", + "request-timeout" : "Tiempo de espera de solicitud RPC (ms)", + "persistent-rpc-settings" : "Configuración de RPC persistente", + "request-persistent" : "Solicitud RPC persistente", + "persistent-polling-interval" : "Intervalo de sondeo (ms) para obtener respuesta de comando RPC persistente", + "common-settings" : "Configuraciones comunes", + "switch-title" : "Título del interruptor", + "show-on-off-labels" : "Mostrar etiquetas encendido/apagado", + "slide-toggle-label" : "Etiqueta de interruptor deslizante", + "label-position" : "Posición de la etiqueta", + "label-position-before" : "Antes", + "label-position-after" : "Después", + "slider-color" : "Color del control deslizante", + "slider-color-primary" : "Primario", + "slider-color-accent" : "Acento", + "slider-color-warn" : "Advertencia", + "button-style" : "Estilo del botón", + "button-raised" : "Botón elevado", + "button-primary" : "Color primario", + "button-background-color" : "Color de fondo del botón", + "button-text-color" : "Color del texto del botón", + "widget-title" : "Título del widget", + "button-label" : "Etiqueta del botón", + "device-attribute-scope" : "Alcance del atributo del dispositivo", + "server-attribute" : "Atributo del servidor", + "shared-attribute" : "Atributo compartido", + "device-attribute-parameters" : "Parámetros del atributo del dispositivo", + "is-one-way-command" : "Es un comando unidireccional", + "rpc-method" : "Método RPC", + "rpc-method-params" : "Parámetros del método RPC", + "show-rpc-error" : "Mostrar error de ejecución del comando RPC", + "led-title" : "Título del LED", + "led-color" : "Color del LED", + "check-status-settings" : "Configuración de verificación de estado", + "perform-rpc-status-check" : "Realizar verificación de estado del dispositivo mediante RPC", + "retrieve-led-status-value-method" : "Recuperar valor de estado del LED usando método", + "led-status-value-attribute" : "Atributo del dispositivo que contiene el estado del LED", + "led-status-value-timeseries" : "Serie temporal del dispositivo que contiene el estado del LED", + "check-status-method" : "Método RPC para verificar estado del dispositivo", + "parse-led-status-value-function" : "Función para analizar el valor del estado del LED", + "knob-title" : "Título del control giratorio", + "min-value" : "Valor mínimo", + "max-value" : "Valor máximo" + }, + "maps" : { + "map-type" : { + "type" : "Tipo de mapa", + "map" : "Mapa", + "image" : "Imagen" + }, + "image" : { + "image-source" : "Fuente de imagen", + "image-source-image" : "Imagen", + "image-source-entity-key" : "Clave de entidad", + "source-entity-alias" : "Alias de entidad fuente", + "image-url-key" : "Clave de URL de imagen", + "image-url-key-required" : "Se requiere la clave de URL de imagen" + }, + "control" : { + "map-controls" : "Controles del mapa", + "position" : "Posición", + "position-topleft" : "Superior izquierda", + "position-topright" : "Superior derecha", + "position-bottomleft" : "Inferior izquierda", + "position-bottomright" : "Inferior derecha", + "zoom-actions" : "Acciones de zoom", + "zoom-scroll" : "Desplazamiento", + "zoom-double-click" : "Doble clic", + "zoom-control-buttons" : "Botones de control", + "scale" : "Escala", + "scale-metric" : "Métrica", + "scale-imperial" : "Imperial", + "switch-to-drag-mode-using-button" : "Cambiar al modo de arrastre usando botón" + }, + "timeline" : { + "control-panel" : "Panel de control de línea de tiempo", + "time-step" : "Paso de tiempo", + "speed-options" : "Opciones de velocidad", + "timestamp" : "Marca de tiempo", + "snap-to-real-location" : "Ajustar a la ubicación real", + "location-snap-filter-function" : "Función de filtrado de ajuste de ubicación", + "no-trips-data-available" : "No hay datos de viajes disponibles" + }, + "map-action" : { + "map-action-buttons" : "Botones de acción del mapa", + "label" : "Etiqueta", + "icon" : "Ícono", + "color" : "Color", + "action" : "Acción", + "add-button" : "Agregar botón", + "no-action-buttons-configured" : "No hay botones de acción configurados", + "remove-action-button" : "Eliminar botón de acción", + "map-action-button" : "Botón de acción del mapa", + "button-requires" : "El botón requiere una etiqueta o ícono" + }, + "common" : { + "common-map-settings" : "Configuraciones comunes del mapa", + "fit-map-bounds" : "Ajustar límites del mapa para cubrir todos los marcadores", + "default-map-center-position" : "Posición central predeterminada del mapa", + "default-map-zoom-level" : "Nivel de zoom predeterminado del mapa", + "entities-limit" : "Límite de entidades a cargar" + }, + "layer" : { + "label" : "Etiqueta", + "layer" : "Capa", + "layers" : "Capas", + "map-layers" : "Capas del mapa", + "add-layer" : "Añadir capa", + "layer-settings" : "Configuraciones de la capa", + "remove-layer" : "Eliminar capa", + "no-layers" : "No hay capas configuradas", + "roadmap" : "Mapa de carreteras", + "satellite" : "Satélite", + "hybrid" : "Híbrido", + "reference" : { + "reference-layer" : "Capa de referencia", + "no-layer" : "Sin capa", + "openstreetmap-hybrid" : "OpenStreetMap Híbrido", + "world-edition-hybrid" : "Edición Mundial Híbrida", + "enhanced-contrast-hybrid" : "Híbrido de Contraste Mejorado" }, - "entity-count": { - "entity-count-card-style": "Estilo de tarjeta contaje de entidades" + "provider" : { + "provider" : "Proveedor", + "openstreet" : { + "title" : "OpenStreet", + "mapnik" : "Mapnik", + "hot" : "HOT", + "esri-street" : "WorldStreetMap", + "esri-topo" : "WorldTopoMap", + "esri-imagery" : "WorldImagery", + "cartodb-positron" : "Positron", + "cartodb-dark-matter" : "DarkMatter" + }, + "google" : { + "title" : "Google", + "roadmap" : "Mapa de carreteras", + "satellite" : "Satélite", + "hybrid" : "Híbrido", + "terrain" : "Terreno" + }, + "here" : { + "title" : "HERE", + "normal-day" : "Día normal", + "normal-night" : "Noche normal", + "hybrid-day" : "Día híbrido", + "terrain-day" : "Día de terreno" + }, + "tencent" : { + "title" : "Tencent", + "normal" : "Normal", + "satellite" : "Satélite", + "terrain" : "Terreno" + }, + "custom" : { + "title" : "Personalizado", + "tile-url" : "URL del mosaico" + } }, - "count": { - "layout": "Diseño", - "layout-column": "Columna", - "layout-row": "Fila", - "label": "Etiqueta", - "icon": "Icono", - "icon-background": "Fondo de icono", - "value": "Valor", - "chevron": "Chevron" + "credentials" : { + "credentials" : "Credenciales", + "api-key" : "Clave API" + } + }, + "overlays" : { + "overlays" : "Superposiciones", + "overlays-hint" : "Configura orígenes de datos, apariencia, comportamiento, opciones de edición y agrupación para entidades del mapa", + "trips" : "Viajes", + "markers" : "Marcadores", + "polygons" : "Polígonos", + "circles" : "Círculos" + }, + "data-layer" : { + "source" : "Fuente", + "filter" : "Filtro", + "additional-data-keys" : "Claves de datos adicionales", + "additional-datasources" : "Orígenes de datos adicionales", + "additional-datasources-hint" : "Origen de datos para acceder a atributos o telemetría de entidades que no se muestran en el mapa, utilizable en funciones de superposición de mapa.", + "more-datasources" : "Más orígenes de datos", + "data-keys" : "Claves de datos", + "add-datasource" : "Añadir origen de datos", + "no-datasources" : "No hay orígenes de datos configurados", + "remove-datasource" : "Eliminar origen de datos", + "behavior" : "Comportamiento", + "on-click" : "Al hacer clic", + "on-click-hint" : "Acción invocada cuando el usuario hace clic en el elemento del mapa.", + "groups" : "Grupos", + "groups-hint" : "Lista de nombres de grupo asignados a la superposición, utilizados para alternar su visibilidad en el mapa.", + "color" : "Color", + "color-settings" : "Configuraciones de color", + "color-type-constant" : "Constante", + "color-type-range" : "Rango", + "color-type-function" : "Función", + "color-range-source-key" : "Clave fuente del rango de color", + "color-range-source-key-required" : "Se requiere la clave fuente del rango de color", + "color-range" : "Rango de color", + "color-function" : "Función de color", + "label" : "Etiqueta", + "tooltip" : "Información sobre herramientas", + "pattern-type-pattern" : "Patrón", + "pattern-type-function" : "Función", + "label-pattern" : "Etiqueta (ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)')", + "label-function" : "Función de etiqueta", + "tooltip-pattern" : "Tooltip (por ejemplo: 'Texto ${keyName} unidades.' o Texto del enlace)", + "tooltip-function" : "Función de tooltip", + "tooltip-trigger" : "Disparador de tooltip", + "tooltip-trigger-click" : "Mostrar tooltip al hacer clic", + "tooltip-trigger-hover" : "Mostrar tooltip al pasar el ratón", + "auto-close-tooltips" : "Cerrar tooltips automáticamente", + "tooltip-offset" : "Desplazamiento del tooltip", + "tooltip-offset-horizontal" : "Horizontal", + "tooltip-offset-vertical" : "Vertical", + "tooltip-tag-actions" : "Acciones de etiqueta", + "add-tooltip-tag-action" : "Añadir acción de etiqueta", + "edit-tooltip-tag-action" : "Editar acción de etiqueta", + "remove-tooltip-tag-action" : "Eliminar acción de etiqueta", + "action-add" : "Añadir", + "action-edit" : "Editar", + "action-move" : "Mover", + "action-remove" : "Eliminar", + "edit-instruments" : "Instrumentos", + "persist-location-attribute-scope" : "Ámbito del atributo para persistir la ubicación", + "enable-snapping" : "Habilitar ajuste a otros vértices para dibujo preciso", + "enable-snapping-hint" : "Alinea automáticamente nuevos puntos con formas existentes para facilitar y hacer más preciso el dibujo.", + "drag-drop-mode" : "Modo de arrastrar y soltar", + "trip" : { + "no-trips" : "No hay viajes configurados", + "add-trip" : "Añadir viaje", + "trip-configuration" : "Configuración del viaje", + "remove-trip" : "Eliminar viaje" }, - "table": { - "common-table-settings": "Ajustes comunes en tablas", - "enable-search": "Activar búsqueda", - "enable-sticky-header": "Mostrar siempre el encabezado", - "enable-sticky-action": "Mostrar siempre la columna de acciones", - "hidden-cell-button-display-mode": "Visualización de botones de acción oculta", - "show-empty-space-hidden-action": "Mostrar espacio vacío en lugar de celda oculta", - "dont-reserve-space-hidden-action": "No reservar espacio para los botones en celda oculta", - "display-timestamp": "Mostrar columna timestamp", - "display-pagination": "Mostrar páginas", - "default-page-size": "Tamaño de página por defecto", - "use-entity-label-tab-name": "Usar etiqueta de entidad en el nombre de la tabla", - "hide-empty-lines": "Ocultar líneas vacías", - "row-style": "Estilo de fila", - "use-row-style-function": "Usar función de estilo de fila", - "row-style-function": "Función de estilo de fila", - "cell-style": "Estilo de celda", - "use-cell-style-function": "Usar función de estilo de celda", - "cell-style-function": "Función de estilo de celda", - "cell-content": "Contenido de celda", - "use-cell-content-function": "Usar función de contenido de celda", - "cell-content-function": "Función de contenido de celda", - "show-latest-data-column": "Mostrar columna de últimos datos", - "latest-data-column-order": "Órden de columna de últimos datos", - "entities-table-title": "Título de tabla de entidades", - "enable-select-column-display": "Activar posibilidad de seleccionar columnas a mostrar", - "display-entity-name": "Mostrar columna de nombre de entidad", - "entity-name-column-title": "Título de columna en nombre de entidad", - "display-entity-label": "Mostrar columna de etiqueta de entidad", - "entity-label-column-title": "Título de columna en etiqueta de entidad", - "display-entity-type": "Mostrar columna de tipo de entidad", - "default-sort-order": "Ordenación por defecto", - "custom-title": "Título de encabezado", - "column-width": "Ancho de columna (px o %)", - "default-column-visibility": "Visibilidad por defecto en columna", - "column-visibility-visible": "Visible", - "column-visibility-hidden": "Oculta", - "column-visibility-hidden-mobile": "Oculto en modo móvil", - "column-selection-to-display": "Selección de columnas en 'Columnas a Mostrar'", - "column-selection-to-display-enabled": "Activada", - "column-selection-to-display-disabled": "Desactivada", - "alarms-table-title": "Título de tabla de alarmas", - "enable-alarms-selection": "Activar selección de alarmas", - "enable-alarms-search": "Activar búsqueda de alarmas", - "enable-alarm-filter": "Activar filtro de alarmas", - "display-alarm-details": "Mostrar detalles de alarma", - "allow-alarms-ack": "Permitir reconocimiento de alarmas", - "allow-alarms-clear": "Permitir borrado de alarmas", - "display-alarm-activity": "Mostrar actividad de alarmas", - "allow-alarms-assign": "Permitir asignar alarmas", - "columns": "Columnas", - "column-settings": "Ajustes de columnas", - "remove-column": "Quitar columna", - "add-column": "Añadir columna", - "no-columns": "No hay columnas configuradas", - "columns-to-display": "Columnas a Mostrar", - "table-header": "Encabezado de tabla", - "header-buttons": "Botones encabezado", - "table-buttons": "Botones de tabla", - "pagination": "Paginación", - "rows": "Filas", - "timeseries-column-error": "Se debe especificar al menos una columna tipo timeseries", - "table-tabs": "Pestañas de la tabla", - "show-cell-actions-menu-mobile": "Mostrar menú desplegable de acciones de celda en modo móvil" + "marker" : { + "marker" : "Marcador", + "latitude-key" : "Clave de latitud", + "longitude-key" : "Clave de longitud", + "x-pos-key" : "Clave de posición X", + "y-pos-key" : "Clave de posición Y", + "latitude-key-required" : "Se requiere la clave de latitud", + "longitude-key-required" : "Se requiere la clave de longitud", + "x-pos-key-required" : "Se requiere la clave de posición X", + "y-pos-key-required" : "Se requiere la clave de posición Y", + "no-markers" : "No hay marcadores configurados", + "add-marker" : "Añadir marcador", + "marker-configuration" : "Configuración del marcador", + "remove-marker" : "Eliminar marcador", + "marker-type" : "Tipo de marcador", + "marker-type-shape" : "Forma", + "marker-type-icon" : "Ícono", + "marker-type-image" : "Imagen", + "shape" : "Forma", + "icon" : "Ícono", + "image" : "Imagen", + "marker-shapes" : "Formas de marcador", + "marker-icon" : "Ícono del marcador", + "marker-appearance" : "Apariencia del marcador", + "marker-image" : "Imagen del marcador", + "marker-image-type-image" : "Imagen", + "marker-image-type-function" : "Función", + "custom-marker-image-size" : "Tamaño personalizado de imagen del marcador", + "marker-image-function" : "Función de imagen del marcador", + "marker-images" : "Imágenes del marcador", + "marker-offset" : "Desplazamiento del marcador", + "offset-horizontal" : "Horizontal", + "offset-vertical" : "Vertical", + "rotate-marker" : "Rotar marcador", + "offset-angle" : "Ángulo de desplazamiento", + "position-conversion" : "Conversión de posición", + "position-conversion-function" : "Función de conversión de posición, debe devolver coordenadas x,y como double de 0 a 1 cada una", + "clustering" : { + "use-map-markers-clustering" : "Usar agrupamiento de marcadores en el mapa", + "zoom-on-cluster-click" : "Acercar al hacer clic en un clúster", + "max-zoom" : "Nivel máximo de zoom para que un marcador pueda formar parte de un clúster (0 - 18)", + "max-radius" : "Radio máximo que cubrirá un clúster", + "zoom-animation" : "Animación en los marcadores al hacer zoom", + "bounds-on-cluster-mouse-over" : "Límites de los marcadores al pasar el ratón sobre un clúster", + "spiderfy-max-zoom-level" : "Expandir al máximo nivel de zoom (para ver todos los marcadores del clúster)", + "load-optimization" : "Optimización de carga", + "chunked-load" : "Usar fragmentos para añadir marcadores y evitar que la página se congele", + "lazy-load" : "Usar carga diferida para añadir marcadores", + "use-cluster-marker-color-function" : "Usar función de color para los clústeres de marcadores", + "marker-color-function" : "Función de color del marcador" + }, + "edit" : "Editar marcador", + "remove-marker-for" : "Eliminar marcador para '{{entityName}}'", + "place-marker" : "Colocar marcador", + "place-marker-hint" : "Haz clic para colocar marcador", + "place-marker-hint-with-entity" : "Haz clic para colocar la entidad '{{entityName}}'" }, - "value-source": { - "value-source": "Origen valor", - "predefined-value": "Valor predefinido", - "entity-attribute": "Valor tomado de un atributo de entidad", - "value": "Valor", - "source-entity-alias": "Alias entidad de origen", - "source-entity-attribute": "Atributo entidad de origen" + "path" : { + "path" : "Ruta", + "path-decorator" : "Decorador de ruta", + "decorator-symbol" : "Símbolo del decorador", + "decorator-symbol-arrow-head" : "Flecha", + "decorator-symbol-dash" : "Guion", + "decorator-arrangement" : "Disposición del decorador", + "decorator-offset" : "Inicio", + "decorator-end-offset" : "Fin", + "decorator-repeat" : "Repetir" }, - "widget-font": { - "font-settings": "Ajustes de fuente", - "font-family": "Familia (font family)", - "size": "Tamaño", - "relative-font-size": "Tamaño fuente relativo (porcentaje)", - "font-style": "Estilo", - "font-style-normal": "Normal", - "font-style-italic": "Cursiva", - "font-style-oblique": "Subrayada", - "font-weight": "Peso", - "font-weight-normal": "Normal", - "font-weight-bold": "Negrita", - "font-weight-bolder": "Negrita+", - "font-weight-lighter": "Lighter", - "color": "Color", - "shadow-color": "Color de sombra", - "preview": "Previsualizar", - "line-height": "Altura de línea", - "auto": "Auto" + "points" : { + "points" : "Puntos", + "point-tooltip" : "Tooltip del punto" }, - "home": { - "no-data-available": "No hay datos disponibles" + "shape" : { + "fill" : "Relleno", + "fill-type-color" : "Color", + "fill-type-stripe" : "Franja", + "fill-type-image" : "Imagen", + "color" : "Color", + "stripe" : "Franja", + "image" : "Imagen", + "stroke" : "Contorno", + "fill-image" : "Imagen de relleno", + "fill-image-type-image" : "Imagen", + "fill-image-type-function" : "Función", + "preserve-aspect-ratio" : "Preservar proporción", + "opacity" : "Opacidad", + "angle" : "Ángulo de rotación", + "scale" : "Escala", + "fill-image-function" : "Función de imagen de relleno de forma", + "fill-images" : "Imágenes de relleno de forma", + "stripe-pattern" : "Patrón de franjas", + "first-stripe" : "Primera franja", + "second-stripe" : "Segunda franja" }, - "system-info": { - "cpu": "CPU", - "ram": "RAM", - "disk": "Disco", - "cpu-warning-text": "Uso de CPU alto. Para evitar fallos en el sistema, optimiza el rendimiento.", - "cpu-critical-text": "Uso de CPU crítico. Para evitar fallos en el sistema, optimiza el rendimiento.", - "ram-warning-text": "Baja reserva de RAM. Para evitar fallos en el sistema, optimiza el rendimiento o incrementa el tamaño de RAM.", - "ram-critical-text": "Reserva de RAM crítica. Para evitar fallos en el sistema, optimiza el rendimiento o incrementa el tamaño de RAM.", - "disk-warning-text": "Espacio libre en disco bajo. Para evitar pérdida de datos, libera o expande el disco.", - "disk-critical-text": "Espacio libre en disco crítico. Para evitar pérdida de datos, libera o expande el disco." + "polygon" : { + "polygon-key" : "Clave de polígono", + "polygon-key-required" : "Se requiere clave de polígono", + "no-polygons" : "No hay polígonos configurados", + "add-polygon" : "Añadir polígono", + "polygon-configuration" : "Configuración del polígono", + "remove-polygon" : "Eliminar polígono", + "edit" : "Editar polígono", + "remove-polygon-for" : "Eliminar polígono para '{{entityName}}'", + "cut" : "Cortar área del polígono", + "rotate" : "Rotar polígono", + "draw-rectangle" : "Dibujar rectángulo", + "draw-polygon" : "Dibujar polígono", + "polygon-place-first-point-cut-hint" : "Haz clic para colocar el primer punto", + "continue-polygon-cut-hint" : "Haz clic para continuar dibujando", + "finish-polygon-cut-hint" : "Haz clic en el primer marcador para finalizar y guardar", + "polygon-place-first-point-hint" : "Polígono: haz clic para colocar el primer punto", + "polygon-place-first-point-hint-with-entity" : "Polígono para '{{entityName}}': haz clic para colocar el primer punto", + "continue-polygon-hint" : "Polígono: haz clic para continuar dibujando", + "continue-polygon-hint-with-entity" : "Polígono para '{{entityName}}': haz clic para continuar dibujando", + "finish-polygon-hint" : "Polígono: haz clic en el primer marcador para finalizar el dibujo", + "finish-polygon-hint-with-entity" : "Polígono para '{{entityName}}': haz clic en el primer marcador para finalizar y guardar", + "rectangle-place-first-point-hint" : "Rectángulo: haz clic para colocar el primer punto", + "rectangle-place-first-point-hint-with-entity" : "Rectángulo para '{{entityName}}': haz clic para colocar el primer punto", + "finish-rectangle-hint" : "Rectángulo: haz clic para finalizar el dibujo", + "finish-rectangle-hint-with-entity" : "Rectángulo para '{{entityName}}': haz clic para finalizar y guardar" }, - "cluster-info": { - "service-id": "Id. Servicio", - "service-type": "Tipo de servicio", - "no-data": "Sin datos" + "circle" : { + "circle-key" : "Clave del círculo", + "circle-key-required" : "Se requiere clave del círculo", + "no-circles" : "No hay círculos configurados", + "add-circle" : "Añadir círculo", + "circle-configuration" : "Configuración del círculo", + "remove-circle" : "Eliminar círculo", + "edit" : "Editar círculo", + "remove-circle-for" : "Eliminar círculo para '{{entityName}}'", + "draw-circle" : "Dibujar círculo", + "place-circle-center-hint-with-entity" : "Círculo para '{{entityName}}': haz clic para colocar el centro del círculo", + "place-circle-center-hint" : "Círculo: haz clic para colocar el centro del círculo", + "finish-circle-hint-with-entity" : "Círculo para '{{entityName}}': haz clic para finalizar y guardar el círculo", + "finish-circle-hint" : "Círculo: haz clic para finalizar el dibujo" }, - "transport-messages": { - "title": "Mensajes de transporte", - "info": "Todos los mensajes que llegan desde los dispositivos" + "select-entity" : "Seleccionar entidad", + "select-entity-hint" : "Sugerencia: después de seleccionar, haz clic en el mapa para establecer la posición" + }, + "select-entity" : "Seleccionar entidad", + "select-entity-hint" : "Sugerencia: después de seleccionar, haz clic en el mapa para establecer la posición", + "tooltips" : { + "placeMarker" : "Haz clic para colocar la entidad '{{entityName}}'", + "firstVertex" : "Polígono para '{{entityName}}': haz clic para colocar el primer punto", + "firstVertex-cut" : "Haz clic para colocar el primer punto", + "continueLine" : "Polígono para '{{entityName}}': haz clic para continuar el dibujo", + "continueLine-cut" : "Haz clic para continuar el dibujo", + "finishLine" : "Haz clic en cualquier marcador existente para finalizar", + "finishPoly" : "Polígono para '{{entityName}}': haz clic en el primer marcador para finalizar y guardar", + "finishPoly-cut" : "Haz clic en el primer marcador para finalizar y guardar", + "finishRect" : "Polígono para '{{entityName}}': haz clic para finalizar y guardar", + "startCircle" : "Círculo para '{{entityName}}': haz clic para colocar el centro del círculo", + "finishCircle" : "Círculo para '{{entityName}}': haz clic para finalizar el círculo", + "placeCircleMarker" : "Haz clic para colocar el marcador del círculo" + }, + "actions" : { + "finish" : "Finalizar", + "cancel" : "Cancelar", + "removeLastVertex" : "Eliminar último punto" + }, + "buttonTitles" : { + "drawMarkerButton" : "Colocar entidad", + "drawPolyButton" : "Crear polígono", + "drawLineButton" : "Crear polilínea", + "drawCircleButton" : "Crear círculo", + "drawRectButton" : "Crear rectángulo", + "editButton" : "Modo de edición", + "dragButton" : "Modo arrastrar y soltar", + "cutButton" : "Cortar área del polígono", + "deleteButton" : "Eliminar", + "drawCircleMarkerButton" : "Crear marcador circular", + "rotateButton" : "Rotar polígono" + }, + "map-provider-settings" : "Configuración del proveedor de mapas", + "map-provider" : "Proveedor de mapas", + "map-provider-google" : "Google Maps", + "map-provider-openstreet" : "Mapas de OpenStreet", + "map-provider-here" : "Mapas de HERE", + "map-provider-image" : "Mapa de imagen", + "map-provider-tencent" : "Mapas de Tencent", + "openstreet-provider" : "Proveedor de mapas OpenStreet", + "openstreet-provider-mapnik" : "OpenStreetMap.Mapnik (Predeterminado)", + "openstreet-provider-hot" : "OpenStreetMap.HOT", + "openstreet-provider-esri-street" : "Esri.WorldStreetMap", + "openstreet-provider-esri-topo" : "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery" : "Esri.WorldImagery", + "openstreet-provider-cartodb-positron" : "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter" : "CartoDB.DarkMatter", + "use-custom-provider" : "Usar proveedor personalizado", + "custom-provider-tile-url" : "URL de mosaico del proveedor personalizado", + "google-maps-api-key" : "Clave API de Google Maps", + "default-map-type" : "Tipo de mapa predeterminado", + "google-map-type-roadmap" : "Mapa de carreteras", + "google-map-type-satelite" : "Satélite", + "google-map-type-hybrid" : "Híbrido", + "google-map-type-terrain" : "Terreno", + "map-layer" : "Capa del mapa", + "here-map-normal-day" : "HERE.normalDay (Predeterminado)", + "here-map-normal-night" : "HERE.normalNight", + "here-map-hybrid-day" : "HERE.hybridDay", + "here-map-terrain-day" : "HERE.terrainDay", + "credentials" : "Credenciales", + "here-app-id" : "ID de la aplicación HERE", + "here-app-code" : "Código de la aplicación HERE", + "here-api-key" : "Clave API de HERE", + "here-use-new-version-api-3" : "Usar la versión 3 de la API", + "tencent-maps-api-key" : "Clave API de Tencent Maps", + "tencent-map-type-roadmap" : "Mapa de carreteras", + "tencent-map-type-satelite" : "Satélite", + "tencent-map-type-hybrid" : "Híbrido", + "image-map-background" : "Fondo del mapa de imagen", + "image-map-background-from-entity-attribute" : "Tomar fondo del mapa de imagen desde el atributo de la entidad", + "image-url-source-entity-alias" : "Alias de entidad fuente para URL de imagen", + "image-url-source-entity-attribute" : "Atributo de entidad fuente para URL de imagen", + "common-map-settings" : "Configuración común del mapa", + "x-pos-key-name" : "Nombre de la clave de posición X", + "y-pos-key-name" : "Nombre de la clave de posición Y", + "latitude-key-name" : "Nombre de la clave de latitud", + "longitude-key-name" : "Nombre de la clave de longitud", + "default-map-zoom-level" : "Nivel de zoom del mapa por defecto (0 - 20)", + "default-map-center-position" : "Posición central por defecto del mapa (0,0)", + "disable-scroll-zooming" : "Desactivar zoom con desplazamiento", + "disable-double-click-zooming" : "Desactivar zoom con doble clic", + "disable-zoom-control-buttons" : "Desactivar botones de control de zoom", + "fit-map-bounds" : "Ajustar límites del mapa para cubrir todos los marcadores", + "use-default-map-center-position" : "Usar la posición central por defecto del mapa", + "entities-limit" : "Límite de entidades a cargar", + "markers-settings" : "Configuración de marcadores", + "marker-offset-x" : "Desplazamiento X del marcador relativo a la posición multiplicado por el ancho del marcador", + "marker-offset-y" : "Desplazamiento Y del marcador relativo a la posición multiplicado por la altura del marcador", + "position-function" : "Función de conversión de posición, debe devolver coordenadas x,y como dobles entre 0 y 1", + "draggable-marker" : "Marcador arrastrable", + "label" : "Etiqueta", + "show-label" : "Mostrar etiqueta", + "use-label-function" : "Usar función de etiqueta", + "label-pattern" : "Etiqueta (ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)')", + "label-function" : "Función de etiqueta", + "tooltip" : "Tooltip", + "show-tooltip" : "Mostrar tooltip", + "show-tooltip-action" : "Acción para mostrar el tooltip", + "show-tooltip-action-click" : "Mostrar tooltip al hacer clic (Predeterminado)", + "show-tooltip-action-hover" : "Mostrar tooltip al pasar el cursor", + "auto-close-tooltips" : "Cerrar automáticamente los tooltips", + "use-tooltip-function" : "Usar función de tooltip", + "tooltip-pattern" : "Tooltip (por ejemplo: 'Texto ${keyName} unidades.' o Texto del enlace)", + "tooltip-function" : "Función de tooltip", + "tooltip-offset-x" : "Desplazamiento X del tooltip relativo al ancla del marcador multiplicado por el ancho del marcador", + "tooltip-offset-y" : "Desplazamiento Y del tooltip relativo al ancla del marcador multiplicado por la altura del marcador", + "color" : "Color", + "use-color-function" : "Usar función de color", + "color-function" : "Función de color", + "marker-image" : "Imagen del marcador", + "use-marker-image-function" : "Usar función de imagen del marcador", + "custom-marker-image" : "Imagen de marcador personalizada", + "custom-marker-image-size" : "Tamaño de imagen de marcador personalizado (px)", + "marker-image-function" : "Función de imagen del marcador", + "marker-images" : "Imágenes de marcador", + "polygon-settings" : "Configuración del polígono", + "show-polygon" : "Mostrar polígono", + "polygon-key-name" : "Nombre de clave del polígono", + "enable-polygon-edit" : "Habilitar edición del polígono", + "polygon-label" : "Etiqueta del polígono", + "show-polygon-label" : "Mostrar etiqueta del polígono", + "use-polygon-label-function" : "Usar función de etiqueta del polígono", + "polygon-label-pattern" : "Etiqueta del polígono (ejemplos: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", + "polygon-label-function" : "Función de etiqueta del polígono", + "polygon-tooltip" : "Tooltip del polígono", + "show-polygon-tooltip" : "Mostrar tooltip del polígono", + "auto-close-polygon-tooltips" : "Cerrar automáticamente los tooltips del polígono", + "use-polygon-tooltip-function" : "Usar función de tooltip del polígono", + "polygon-tooltip-pattern" : "Tooltip (por ejemplo: 'Texto ${keyName} unidades.' o Texto del enlace)", + "polygon-tooltip-function" : "Función de tooltip del polígono", + "polygon-color" : "Color del polígono", + "polygon-opacity" : "Opacidad del polígono", + "use-polygon-color-function" : "Usar función de color del polígono", + "polygon-color-function" : "Función de color del polígono", + "polygon-stroke" : "Contorno del polígono", + "stroke-color" : "Color del contorno", + "stroke-opacity" : "Opacidad del contorno", + "stroke-weight" : "Grosor del contorno", + "use-polygon-stroke-color-function" : "Usar función de color del contorno del polígono", + "polygon-stroke-color-function" : "Función de color del contorno del polígono", + "circle-settings" : "Configuración del círculo", + "show-circle" : "Mostrar círculo", + "circle-key-name" : "Nombre de clave del círculo", + "enable-circle-edit" : "Habilitar edición del círculo", + "circle-label" : "Etiqueta del círculo", + "show-circle-label" : "Mostrar etiqueta del círculo", + "use-circle-label-function" : "Usar función de etiqueta del círculo", + "circle-label-pattern" : "Etiqueta del círculo (ejemplos: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", + "circle-label-function" : "Función de etiqueta del círculo", + "circle-tooltip" : "Tooltip del círculo", + "show-circle-tooltip" : "Mostrar tooltip del círculo", + "auto-close-circle-tooltips" : "Cerrar automáticamente los tooltips del círculo", + "use-circle-tooltip-function" : "Usar función de tooltip del círculo", + "circle-tooltip-pattern" : "Tooltip (por ejemplo: 'Texto ${keyName} unidades.' o Texto del enlace)", + "circle-tooltip-function" : "Función de tooltip del círculo", + "circle-fill-color" : "Color de relleno del círculo", + "circle-fill-color-opacity" : "Opacidad del color de relleno del círculo", + "use-circle-fill-color-function" : "Usar función de color de relleno del círculo", + "circle-fill-color-function" : "Función de color de relleno del círculo", + "circle-stroke" : "Contorno del círculo", + "use-circle-stroke-color-function" : "Usar función de color del contorno del círculo", + "circle-stroke-color-function" : "Función de color del contorno del círculo", + "markers-clustering-settings" : "Configuración de agrupación de marcadores", + "use-map-markers-clustering" : "Usar agrupación de marcadores en el mapa", + "zoom-on-cluster-click" : "Hacer zoom al hacer clic en un clúster", + "max-cluster-zoom" : "Nivel máximo de zoom en el que un marcador puede ser parte de un clúster (0 - 18)", + "max-cluster-radius-pixels" : "Radio máximo que cubrirá un clúster en píxeles", + "cluster-zoom-animation" : "Mostrar animación en los marcadores al hacer zoom", + "show-markers-bounds-on-cluster-mouse-over" : "Mostrar los límites de los marcadores al pasar el cursor sobre un clúster", + "spiderfy-max-zoom-level" : "Mostrar marcadores del clúster en el nivel de zoom máximo", + "load-optimization" : "Optimización de carga", + "cluster-chunked-loading" : "Usar fragmentos para agregar marcadores y evitar que la página se congele", + "cluster-markers-lazy-load" : "Usar carga diferida para agregar marcadores", + "editor-settings" : "Configuración del editor", + "enable-snapping" : "Habilitar ajuste a otros vértices para dibujo de precisión", + "init-draggable-mode" : "Inicializar el mapa en modo arrastrable", + "hide-all-edit-buttons" : "Ocultar todos los botones de control de edición", + "hide-draw-buttons" : "Ocultar botones de dibujo", + "hide-edit-buttons" : "Ocultar botones de edición", + "hide-remove-button" : "Ocultar botón de eliminar", + "route-map-settings" : "Configuración del mapa de rutas", + "trip-animation-settings" : "Configuración de animación de viaje", + "normalization-step" : "Paso de normalización de datos (ms)", + "tooltip-background-color" : "Color de fondo del tooltip", + "tooltip-font-color" : "Color de fuente del tooltip", + "tooltip-opacity" : "Opacidad del tooltip (0-1)", + "auto-close-tooltip" : "Cerrar automáticamente el tooltip", + "rotation-angle" : "Establecer ángulo de rotación adicional para el marcador (grados)", + "path-settings" : "Configuración del recorrido", + "path-color" : "Color del recorrido", + "use-path-color-function" : "Usar función de color del recorrido", + "path-color-function" : "Función de color del recorrido", + "path-decorator" : "Decorador del recorrido", + "use-path-decorator" : "Usar decorador del recorrido", + "decorator-symbol" : "Símbolo del decorador", + "decorator-symbol-arrow-head" : "Flecha", + "decorator-symbol-dash" : "Guion", + "decorator-symbol-size" : "Tamaño del símbolo del decorador (px)", + "use-path-decorator-custom-color" : "Usar color personalizado del decorador", + "decorator-custom-color" : "Color personalizado del decorador", + "decorator-offset" : "Desplazamiento del decorador", + "end-decorator-offset" : "Desplazamiento final del decorador", + "decorator-repeat" : "Repetición del decorador", + "points-settings" : "Configuración de puntos", + "show-points" : "Mostrar puntos", + "point-color" : "Color del punto", + "point-size" : "Tamaño del punto (px)", + "use-point-color-function" : "Usar función de color del punto", + "point-color-function" : "Función de color del punto", + "use-point-as-anchor" : "Usar punto como ancla", + "point-as-anchor-function" : "Función de punto como ancla", + "independent-point-tooltip" : "Tooltip de punto independiente", + "clustering-markers" : "Agrupación de marcadores", + "use-icon-create-function" : "Usar función de color de los marcadores", + "marker-color-function" : "Función de color del marcador" + }, + "markdown" : { + "use-markdown-text-function" : "Usar función de valor Markdown/HTML", + "markdown-text-function" : "Función de valor Markdown/HTML", + "markdown-text-pattern" : "Patrón Markdown/HTML (Markdown o HTML con variables, por ejemplo: '${entityName} o ${keyName} - algún texto.')", + "apply-default-markdown-style" : "Aplicar estilo Markdown predeterminado", + "markdown-css" : "CSS Markdown/HTML" + }, + "simple-card" : { + "label" : "Etiqueta", + "label-position" : "Posición de la etiqueta", + "label-position-left" : "Izquierda", + "label-position-top" : "Superior" + }, + "single-switch" : { + "behavior" : "Comportamiento", + "layout" : "Diseño", + "layout-right" : "Derecha", + "layout-left" : "Izquierda", + "layout-centered" : "Centrado", + "auto-scale" : "Escalado automático", + "label" : "Etiqueta", + "icon" : "Ícono", + "switch-color" : "Color del interruptor", + "on" : "Encendido", + "off" : "Apagado", + "disabled" : "Desactivado", + "tumbler-color" : "Color del conmutador", + "on-label" : "Etiqueta de encendido", + "off-label" : "Etiqueta de apagado", + "switch" : "Interruptor" + }, + "slider" : { + "behavior" : "Comportamiento", + "initial-value" : "Valor inicial", + "initial-value-hint" : "Acción para obtener el valor inicial del deslizador.", + "on-value-change" : "Al cambiar el valor", + "on-value-change-hint" : "Acción al cambiar el valor del deslizador.", + "layout" : "Diseño", + "layout-default" : "Predeterminado", + "layout-extended" : "Extendido", + "layout-simplified" : "Simplificado", + "auto-scale" : "Escalado automático", + "icon" : "Ícono", + "value" : "Valor", + "range" : "Rango", + "min" : "mín", + "max" : "máx", + "range-ticks" : "Marcas del rango", + "tick-marks" : "Marcas de verificación", + "colors" : "Colores", + "main" : "Principal", + "background" : "Fondo", + "left-icon" : "Ícono izquierdo", + "right-icon" : "Ícono derecho", + "slider" : "Deslizador" + }, + "value-card" : { + "layout" : "Diseño", + "layout-square" : "Cuadrado", + "layout-vertical" : "Vertical", + "layout-centered" : "Centrado", + "layout-simplified" : "Simplificado", + "layout-horizontal" : "Horizontal", + "layout-horizontal-reversed" : "Horizontal invertido", + "label" : "Etiqueta", + "icon" : "Ícono", + "value" : "Valor", + "date" : "Fecha", + "value-card-style" : "Estilo de tarjeta de valor", + "auto-scale" : "Escalado automático" + }, + "label-card" : { + "auto-scale" : "Escalado automático", + "label" : "Etiqueta", + "icon" : "Ícono", + "label-card-style" : "Estilo de tarjeta de etiqueta" + }, + "label-value-card" : { + "value" : "Valor", + "label-value-card-style" : "Estilo de tarjeta de etiqueta y valor" + }, + "liquid-level-card" : { + "layout-simple" : "Simple", + "layout-percentage" : "Porcentaje", + "layout-absolute" : "Absoluto", + "layout" : "Diseño", + "background-overlay" : "Superposición de fondo del valor", + "total-volume" : "Volumen total", + "total-volume-units" : "Unidades de volumen total", + "tank" : "Tanque", + "shape" : "Forma", + "datasource-units" : "Unidades de origen", + "widget-units" : "Unidades del widget", + "decimals" : "Decimales", + "liquid" : "Líquido", + "liquid-color" : "Color del líquido", + "value" : "Valor", + "value-font" : "Fuente del valor", + "level" : "Nivel", + "last-update" : "Última actualización", + "shape-by-attribute" : "Establecer forma del tanque por nombre de atributo", + "tooltip-background" : "Color de fondo", + "background-blur" : "Desenfoque del fondo", + "tank-color" : "Color del tanque", + "static" : "Estático", + "see-examples" : "Ver ejemplos", + "attribute" : "Atributo", + "shape-type" : "Tipo", + "v-oval" : "Óvalo vertical", + "v-cylinder" : "Cilindro vertical", + "v-capsule" : "Cápsula vertical", + "rectangle" : "Rectángulo", + "h-oval" : "Óvalo horizontal", + "h-ellipse" : "Elipse horizontal", + "h-dish-ends" : "Extremos cóncavos horizontales", + "h-cylinder" : "Cilindro horizontal", + "h-capsule" : "Cápsula horizontal", + "h-elliptical_2_1" : "Horizontal 2:1 Elíptico", + "icon" : "Ícono de tarjeta", + "title" : "Título de tarjeta", + "units" : "Unidades", + "color-and-font" : "Color y fuente", + "shape-attribute-name" : "Nombre del atributo", + "total-volume-required" : "El volumen total es obligatorio.", + "attribute-name-required" : "El nombre del atributo es obligatorio.", + "attribute-key-not-set" : "La clave del atributo '{{attributeName}}' no está definida", + "attribute-key-invalid" : "La clave del atributo '{{attributeName}}' no es válida" + }, + "aggregated-value-card" : { + "subtitle" : "Subtítulo", + "chart" : "Gráfico", + "values" : "Valores", + "value-appearance" : "Apariencia del valor", + "position" : "Posición", + "position-center" : "Centro", + "position-right-top" : "Arriba a la derecha", + "position-right-bottom" : "Abajo a la derecha", + "position-left-top" : "Arriba a la izquierda", + "position-left-bottom" : "Abajo a la izquierda", + "font" : "Fuente", + "color" : "Color", + "arrow" : "Flecha", + "display-up-down-arrow" : "Mostrar flecha de subida/bajada", + "add-value" : "Agregar valor", + "remove-value" : "Eliminar valor", + "no-values" : "No hay valores configurados", + "aggregation" : "Agregación", + "aggregated-value-card-style" : "Estilo de tarjeta de valor agregado", + "auto-scale" : "Escalado automático" + }, + "value-chart-card" : { + "layout" : "Diseño", + "layout-left" : "Izquierda", + "layout-right" : "Derecha", + "auto-scale" : "Escalado automático", + "icon" : "Ícono", + "value" : "Valor", + "chart" : "Gráfico", + "value-chart-card-style" : "Estilo de tarjeta de gráfico de valor" + }, + "progress-bar" : { + "layout" : "Diseño", + "layout-default" : "Predeterminado", + "layout-simplified" : "Simplificado", + "auto-scale" : "Escalado automático", + "icon" : "Ícono", + "value" : "Valor", + "range" : "Rango", + "min" : "mín", + "max" : "máx", + "range-ticks" : "Marcas de rango", + "bar" : "Barra", + "bar-color" : "Color de la barra", + "bar-background" : "Fondo de la barra", + "progress-bar-card-style" : "Estilo de tarjeta de barra de progreso" + }, + "notification" : { + "max-notification-display" : "Máximo de notificaciones a mostrar", + "counter" : "Contador", + "counter-hint" : "El contador se mostrará si el \"Título del widget\" está habilitado", + "icon" : "Ícono", + "counter-value" : "Valor", + "counter-color" : "Color", + "notification-button" : "Botones de notificación", + "button-view-all" : "Ver todo", + "button-filter" : "Filtrar", + "type-filter" : "Filtro por tipo", + "button-mark-read" : "Marcar todo como leído", + "notification-types" : "Tipos de notificación", + "notification-type" : "Tipo de notificación", + "search-type" : "Tipo de búsqueda", + "any-type" : "Cualquier tipo" + }, + "alarm-count" : { + "alarm-count-card-style" : "Estilo de tarjeta de conteo de alarmas" + }, + "entity-count" : { + "entity-count-card-style" : "Estilo de tarjeta de conteo de entidades" + }, + "count" : { + "layout" : "Diseño", + "layout-column" : "Columna", + "layout-row" : "Fila", + "label" : "Etiqueta", + "icon" : "Ícono", + "icon-background" : "Fondo del ícono", + "value" : "Valor", + "chevron" : "Chevron", + "auto-scale" : "Escalado automático" + }, + "table" : { + "common-table-settings" : "Configuraciones comunes de la tabla", + "enable-search" : "Habilitar búsqueda", + "enable-sticky-header" : "Mostrar encabezado siempre", + "enable-sticky-action" : "Mostrar columna de acciones siempre", + "hidden-cell-button-display-mode" : "Modo de visualización del botón de acción oculto", + "show-empty-space-hidden-action" : "Mostrar espacio vacío en lugar del botón de acción oculto", + "dont-reserve-space-hidden-action" : "No reservar espacio para botones de acción ocultos", + "display-timestamp" : "Marca de tiempo", + "display-pagination" : "Mostrar paginación", + "default-page-size" : "Tamaño de página predeterminado", + "page-step-settings" : "Configuraciones de pasos de página", + "page-step-count" : "Número de pasos", + "page-step-increment" : "Incremento de paso", + "page-step-count-format-message" : "Debe ser un número entero, en el rango de 1 a 100.", + "page-step-increment-format-message" : "Debe ser un número entero, mayor o igual a 1.", + "use-entity-label-tab-name" : "Usar etiqueta de entidad como nombre de pestaña", + "hide-empty-lines" : "Ocultar líneas vacías", + "row-style" : "Estilo de fila", + "use-row-style-function" : "Usar función de estilo de fila", + "row-style-function" : "Función de estilo de fila", + "cell-style" : "Estilo de celda", + "use-cell-style-function" : "Usar función de estilo de celda", + "cell-style-function" : "Función de estilo de celda", + "cell-content" : "Contenido de la celda", + "use-cell-content-function" : "Usar función de contenido de celda", + "cell-content-function" : "Función de contenido de celda", + "show-latest-data-column" : "Mostrar columna de datos más recientes", + "latest-data-column-order" : "Orden de la columna de datos más recientes", + "entities-table-title" : "Título de la tabla de entidades", + "enable-select-column-display" : "Habilitar selección de columnas para mostrar", + "display-entity-name" : "Mostrar columna de nombre de entidad", + "entity-name-column-title" : "Título de columna de nombre de entidad", + "display-entity-label" : "Mostrar columna de etiqueta de entidad", + "entity-label-column-title" : "Título de columna de etiqueta de entidad", + "display-entity-type" : "Mostrar columna de tipo de entidad", + "default-sort-order" : "Orden de clasificación predeterminado", + "custom-title" : "Título personalizado del encabezado", + "column-width" : "Ancho de columna (px o %)", + "default-column-visibility" : "Visibilidad de columna predeterminada", + "column-visibility-visible" : "Visible", + "column-visibility-hidden" : "Oculto", + "column-visibility-hidden-mobile" : "Oculto en modo móvil", + "column-selection-to-display" : "Selección de columna en 'Columnas para mostrar'", + "column-selection-to-display-enabled" : "Habilitado", + "column-selection-to-display-disabled" : "Deshabilitado", + "alarms-table-title" : "Título de la tabla de alarmas", + "enable-alarms-selection" : "Habilitar selección de alarmas", + "enable-alarms-search" : "Habilitar búsqueda de alarmas", + "enable-alarm-filter" : "Habilitar filtro de alarmas", + "display-alarm-details" : "Mostrar detalles de alarma", + "allow-alarms-ack" : "Permitir reconocimiento de alarmas", + "allow-alarms-clear" : "Permitir limpiar alarmas", + "display-alarm-activity" : "Mostrar actividad de la alarma", + "allow-alarms-assign" : "Permitir asignación de alarmas", + "columns" : "Columnas", + "column-settings" : "Configuraciones de columna", + "remove-column" : "Eliminar columna", + "add-column" : "Agregar columna", + "no-columns" : "No hay columnas configuradas", + "columns-to-display" : "Columnas para mostrar", + "table-header" : "Encabezado de la tabla", + "header-buttons" : "Botones del encabezado", + "table-buttons" : "Botones de la tabla", + "pagination" : "Paginación", + "rows" : "Filas", + "timeseries-column-error" : "Debe especificarse al menos una columna de serie temporal", + "alarm-column-error" : "Debe especificarse al menos una columna de alarma", + "table-tabs" : "Pestañas de la tabla", + "show-cell-actions-menu-mobile" : "Mostrar menú desplegable de acciones de celda en modo móvil", + "disable-sorting" : "Deshabilitar ordenamiento" + }, + "latest-chart" : { + "total" : "Total", + "auto-scale" : "Escalado automático", + "clockwise-layout" : "Disposición en sentido horario", + "sort-series" : "Ordenar series por etiqueta", + "tooltip-value-type-absolute" : "Absoluto", + "tooltip-value-type-percentage" : "Porcentaje" + }, + "pie-chart" : { + "pie-chart-appearance" : "Apariencia del gráfico circular", + "label" : "Etiqueta", + "border" : "Borde", + "radius" : "Radio", + "pie-chart-card-style" : "Estilo de tarjeta de gráfico circular" + }, + "radar-chart" : { + "radar-appearance" : "Apariencia del gráfico de radar", + "shape" : "Forma", + "shape-polygon" : "Polígono", + "shape-circle" : "Círculo", + "color" : "Color", + "line" : "Línea", + "points" : "Puntos", + "points-label" : "Etiqueta de puntos", + "radar-axis" : "Eje de radar", + "axis-label" : "Etiqueta del eje", + "ticks-label" : "Etiqueta de marcas", + "radar-chart-style" : "Estilo de gráfico de radar" + }, + "time-series-chart" : { + "chart" : "Gráfico", + "chart-style" : "Estilo de gráfico", + "data-zoom" : "Zoom de datos", + "stack-mode" : "Modo de apilamiento", + "stack-mode-hint" : "Apila las series en el gráfico. Las series con la misma unidad se superponen.", + "axes" : "Ejes", + "y-axes" : "Ejes Y", + "line-type" : "Tipo de línea", + "line-width" : "Ancho de línea", + "type-line" : "Línea", + "type-bar" : "Barra", + "type-point" : "Punto", + "no-aggregation-bar-width-strategy" : "Estrategia de ancho de barra para datos no agregados", + "no-aggregation-bar-width-strategy-group" : "Agrupar", + "no-aggregation-bar-width-strategy-separate" : "Separado", + "bar-group-width" : "Ancho del grupo de barras", + "bar-width" : "Ancho de barra", + "bar-width-relative" : "Porcentaje de la ventana temporal", + "bar-width-absolute" : "Absoluto (ms)", + "comparison" : { + "comparison" : "Comparación", + "comparison-hint" : "¡La comparación solo funciona con datos históricos!", + "show" : "Mostrar", + "settings" : "Configuración de comparación", + "show-values-for-comparison" : "Mostrar datos históricos para la comparación", + "comparison-values-label" : "Etiqueta de clave de comparación", + "comparison-values-label-auto" : "Automático", + "comparison-data-color" : "Color de los datos de comparación" + }, + "threshold" : { + "thresholds" : "Umbrales", + "source" : "Fuente", + "key-value" : "Clave / Valor", + "no-thresholds" : "No hay umbrales configurados", + "add-threshold" : "Agregar umbral", + "type-constant" : "Constante", + "type-latest-key" : "Clave", + "type-entity" : "Entidad", + "threshold-settings" : "Configuración del umbral", + "remove-threshold" : "Eliminar umbral", + "threshold-value-required" : "Se requiere el valor del umbral.", + "key-required" : "Se requiere clave.", + "entity-key-required" : "Se requiere clave de entidad.", + "line-appearance" : "Apariencia de la línea", + "line-color" : "Color de la línea", + "start-symbol" : "Símbolo inicial", + "end-symbol" : "Símbolo final", + "symbol-size" : "tamaño", + "label" : "Etiqueta", + "label-position-start" : "Inicio", + "label-position-middle" : "Medio", + "label-position-end" : "Final", + "label-position-inside-start" : "Interior inicio", + "label-position-inside-start-top" : "Interior inicio superior", + "label-position-inside-start-bottom" : "Interior inicio inferior", + "label-position-inside-middle" : "Interior medio", + "label-position-inside-middle-top" : "Interior medio superior", + "label-position-inside-middle-bottom" : "Interior medio inferior", + "label-position-inside-end" : "Interior final", + "label-position-inside-end-top" : "Interior final superior", + "label-position-inside-end-bottom" : "Interior final inferior", + "label-background" : "Fondo de etiqueta" + }, + "state" : { + "states" : "Estados", + "label" : "Etiqueta", + "ticks-value" : "Valor de ticks", + "source" : "Fuente", + "value-range" : "Valor / Rango", + "no-states" : "No hay estados configurados", + "add-state" : "Agregar estado", + "type-constant" : "Constante", + "type-range" : "Rango", + "from" : "Desde", + "to" : "Hasta", + "remove-state" : "Eliminar estado" + }, + "grid" : { + "grid" : "Cuadrícula", + "background-color" : "Color de fondo", + "border" : "Borde" + }, + "axis" : { + "axes" : "Ejes", + "x-axis" : "Eje X", + "y-axis" : "Eje Y", + "y-axis-settings" : "Configuración del eje Y", + "comparison-x-axis-settings" : "Configuración del eje X para comparación", + "remove-y-axis" : "Eliminar eje Y", + "id" : "Id", + "label" : "Etiqueta", + "position" : "Posición", + "position-left" : "Izquierda", + "position-right" : "Derecha", + "position-top" : "Arriba", + "position-bottom" : "Abajo", + "tick-labels" : "Etiquetas de ticks", + "ticks-formatter-function" : "Función de formato de ticks", + "ticks-generator-function" : "Función generadora de ticks", + "show-ticks" : "Mostrar ticks", + "show-line" : "Mostrar línea", + "show-split-lines" : "Mostrar líneas divididas", + "show-split-lines-x-axis-hint" : "Si está habilitado, se mostrarán las líneas verticales en el gráfico.", + "show-split-lines-y-axis-hint" : "Si está habilitado, se mostrarán las líneas horizontales en el gráfico.", + "ticks-interval" : "Intervalo de ticks", + "ticks-interval-hint" : "Establecer intervalo de segmentación para el eje.", + "split-number" : "Número de divisiones", + "split-number-hint" : "Número de segmentos en que se divide el eje.", + "min" : "Mínimo", + "max" : "Máximo", + "show" : "Mostrar", + "add-y-axis" : "Agregar eje Y" + }, + "series" : { + "legend-settings" : "Configuración de leyenda", + "show-in-legend" : "Mostrar en la leyenda", + "show-in-legend-hint" : "Mostrar nombre de la serie y datos en la leyenda.", + "hidden-by-default" : "Ocultar por defecto", + "hidden-by-default-hint" : "Ocultar la serie en la leyenda por defecto.", + "series-type" : "Tipo de serie", + "type" : "Tipo", + "type-line" : "Línea", + "type-bar" : "Barra", + "line" : { + "line" : "Línea", + "show-line" : "Mostrar línea", + "step-line" : "Línea escalonada", + "step-type-start" : "Inicio", + "step-type-middle" : "Medio", + "step-type-end" : "Fin", + "smooth-line" : "Línea suave" }, - "activity": { - "title": "Actividad" + "point" : { + "points" : "Puntos", + "show-points" : "Mostrar puntos", + "point-label" : "Etiqueta del punto", + "point-label-hint" : "Mostrar etiqueta con valor sobre el punto de la serie.", + "point-label-background" : "Fondo de etiqueta del punto", + "point-shape" : "Forma del punto", + "point-size" : "Tamaño del punto" + } + } + }, + "wind-speed-direction" : { + "layout" : "Diseño", + "layout-default" : "Por defecto", + "layout-advanced" : "Avanzado", + "layout-simplified" : "Simplificado", + "values" : "Valores", + "wind-direction" : "Dirección del viento", + "center-value" : "Valor central", + "icon" : "Ícono", + "arrow" : "Flecha", + "ticks" : "Marcas", + "labels-type" : "Tipo de etiquetas", + "directional-names" : "Nombres direccionales", + "degrees" : "Grados", + "major-ticks" : "Marcas mayores", + "minor-ticks" : "Marcas menores", + "wind-speed-direction-card-style" : "Estilo de tarjeta de velocidad y dirección del viento", + "ticks-color" : "Color de las marcas", + "ticks-labels-type" : "Tipo de etiquetas de marcas", + "arrow-color" : "Color de la flecha" + }, + "value-source" : { + "value-source" : "Fuente de valor", + "predefined-value" : "Constante", + "entity-attribute" : "Atributo de entidad", + "value" : "Valor", + "value-required" : "El valor es obligatorio.", + "key-required" : "La clave es obligatoria.", + "entity-key-required" : "La clave de la entidad es obligatoria.", + "source-entity-alias" : "Alias de entidad de origen", + "source-entity-attribute" : "Atributo de entidad de origen", + "type-constant" : "Constante", + "type-latest-key" : "Clave", + "type-entity" : "Entidad" + }, + "rpc-state" : { + "initial-state" : "Estado inicial", + "initial-state-hint" : "Acción para obtener el estado inicial (Encendido/Apagado) del componente.", + "disabled-state" : "Estado deshabilitado", + "disabled-state-hint" : "Configurar condición bajo la cual el componente está deshabilitado.", + "turn-on" : "Encender", + "turn-on-hint" : "Acción al cambiar el control deslizante a 'Encendido'", + "turn-off" : "Apagar", + "turn-off-hint" : "Acción al cambiar el control deslizante a 'Apagado'", + "on" : "Encendido", + "off" : "Apagado", + "disabled" : "Deshabilitado" + }, + "value-action" : { + "do-nothing" : "No hacer nada", + "execute-rpc" : "Ejecutar RPC", + "get-attribute" : "Obtener atributo", + "set-attribute" : "Establecer atributo", + "get-time-series" : "Obtener serie temporal", + "get-alarm-status" : "Obtener estado de alarma", + "get-dashboard-state" : "Obtener ID del estado del tablero", + "get-dashboard-state-object" : "Obtener objeto de estado del tablero", + "add-time-series" : "Agregar serie temporal", + "execute-rpc-text" : "Ejecutar método RPC '{{methodName}}'", + "get-time-series-text" : "Usar serie temporal '{{key}}'", + "get-attribute-text" : "Usar atributo '{{key}}'", + "get-alarm-status-text" : "Usar estado de la alarma", + "get-dashboard-state-text" : "Usar estado del tablero", + "get-dashboard-state-object-text" : "Usar objeto de estado del tablero", + "when-dashboard-state-is-text" : "Cuando el ID del estado del tablero es '{{state}}'", + "when-dashboard-state-function-is-text" : "Cuando f(ID del estado del tablero) es '{{state}}'", + "when-dashboard-state-object-function-is-text" : "Cuando f(objeto del estado del tablero) es '{{state}}'", + "set-attribute-to-value-text" : "Establecer el atributo '{{key}}' a: {{value}}", + "add-time-series-value-text" : "Agregar valor '{{value}}' a la serie temporal '{{key}}'", + "set-attribute-text" : "Establecer atributo '{{key}}'", + "add-time-series-text" : "Agregar serie temporal '{{key}}'", + "action" : "Acción", + "value" : "Valor", + "init-value-hint" : "Valor que se establecerá hasta que el dispositivo envíe datos.", + "method" : "Método", + "method-name-required" : "El nombre del método es obligatorio.", + "request-timeout-ms" : "Tiempo de espera de la solicitud RPC (ms)", + "request-timeout-required" : "El tiempo de espera de la solicitud es obligatorio.", + "min-request-timeout-error" : "El tiempo de espera debe ser mayor o igual a 5000 ms (5 segundos).", + "request-persistent" : "Solicitud RPC persistente", + "persistent-polling-interval" : "Intervalo de sondeo persistente (ms)", + "persistent-polling-interval-hint" : "Intervalo de sondeo (ms) para obtener la respuesta del comando RPC persistente", + "persistent-polling-interval-required" : "El intervalo de sondeo persistente es obligatorio.", + "min-persistent-polling-interval-error" : "El valor del intervalo debe ser mayor o igual a 1000 ms (1 segundo).", + "attribute-scope" : "Ámbito del atributo", + "attribute-key" : "Clave del atributo", + "attribute-key-required" : "La clave del atributo es obligatoria.", + "time-series-key" : "Clave de la serie temporal", + "time-series-key-required" : "La clave de la serie temporal es obligatoria.", + "action-result-converter" : "Convertidor de resultados de la acción", + "converter-none" : "Ninguno", + "converter-function" : "Función", + "converter-constant" : "Constante", + "converter-value" : "Valor", + "parse-value-function" : "Función para analizar el valor", + "state-when-result-is" : "'{{state}}' cuando el resultado es", + "parameters" : "Parámetros", + "convert-value-function" : "Función para convertir valor", + "error" : { + "target-entity-is-not-set" : "¡Entidad de destino no definida!", + "failed-to-perform-action" : "No se pudo realizar la acción {{ actionLabel }}.", + "invalid-attribute-scope" : "El ámbito '{{scope}}' no es compatible con la entidad {{entityType}}." + } + }, + "widget-font" : { + "font-settings" : "Configuración de fuente", + "font-family" : "Familia de fuente", + "size" : "Tamaño", + "relative-font-size" : "Tamaño de fuente relativo (porcentaje)", + "font-style" : "Estilo", + "font-style-normal" : "Normal", + "font-style-italic" : "Cursiva", + "font-style-oblique" : "Oblicua", + "font-weight" : "Peso", + "font-weight-normal" : "Normal", + "font-weight-bold" : "Negrita", + "font-weight-bolder" : "Más negrita", + "font-weight-lighter" : "Más ligera", + "color" : "Color", + "shadow-color" : "Color de sombra", + "preview" : "Vista previa", + "line-height" : "Altura de línea", + "auto" : "Auto" + }, + "home" : { + "no-data-available" : "No hay datos disponibles" + }, + "system-info" : { + "cpu" : "CPU", + "ram" : "RAM", + "disk" : "Disco", + "cpu-warning-text" : "Uso elevado de CPU. Para evitar fallos del sistema, optimice el rendimiento.", + "cpu-critical-text" : "Uso crítico de CPU. Para evitar fallos del sistema, optimice el rendimiento.", + "ram-warning-text" : "Baja reserva de RAM. Para evitar fallos del sistema, optimice el rendimiento o aumente la memoria.", + "ram-critical-text" : "Reserva crítica de RAM. Para evitar fallos del sistema, optimice el rendimiento o aumente la memoria.", + "disk-warning-text" : "Espacio en disco reducido. Para evitar pérdida de datos, libere o expanda el espacio.", + "disk-critical-text" : "Espacio en disco críticamente bajo. Para evitar pérdida de datos, libere o expanda el espacio." + }, + "cluster-info" : { + "service-id" : "ID del servicio", + "service-type" : "Tipo de servicio", + "no-data" : "Sin datos" + }, + "transport-messages" : { + "title" : "Mensajes de transporte", + "info" : "Todos los mensajes que llegaron desde los dispositivos" + }, + "activity" : { + "title" : "Actividad" + }, + "documentation" : { + "title" : "Documentación", + "add-link" : "Agregar enlace", + "add-link-title" : "Agregar enlace de documentación", + "name" : "Nombre", + "name-required" : "El nombre es obligatorio.", + "link" : "Enlace", + "link-required" : "El enlace es obligatorio.", + "columns" : "Columnas" + }, + "quick-links" : { + "title" : "Enlaces rápidos", + "add-link" : "Agregar enlace", + "add-link-title" : "Agregar enlace rápido", + "quick-link" : "Enlace rápido", + "quick-link-required" : "El enlace rápido es obligatorio.", + "no-links-matching" : "No se encontraron enlaces coincidentes con '{{name}}'.", + "columns" : "Columnas" + }, + "recent-dashboards" : { + "title" : "Tableros", + "last" : "Vistos recientemente", + "starred" : "Favoritos", + "name" : "Nombre", + "last-viewed" : "Última visualización", + "no-last-viewed-dashboards" : "Aún no se han visualizado tableros recientemente" + }, + "configured-features" : { + "title" : "Características configuradas", + "info" : "Estado de las características que requieren configuración", + "email-feature" : "Correo electrónico", + "sms-feature" : "SMS", + "slack-feature" : "Slack", + "oauth2-feature" : "OAuth 2", + "2fa-feature" : "2FA", + "feature-configured" : "Función configurada.\nHaga clic para configurar", + "feature-not-configured" : "Función no configurada.\nHaga clic para configurar" + }, + "version-info" : { + "title" : "Versión", + "contact-us" : "Contáctenos", + "current-version" : "Versión actual", + "current" : "Actual", + "available-version" : "Versión disponible", + "available" : "Disponible", + "upgrade" : "Actualizar", + "version-is-up-to-date" : "La versión está actualizada" + }, + "usage-info" : { + "title" : "Uso", + "entities" : "Entidades", + "api-calls" : "Llamadas API" + }, + "functions" : { + "title" : "Funciones", + "pe-feature-tooltip" : "Disponible solo en ThingsBoard\nProfessional Edition", + "switch-to-pe" : "Cambiar a PE", + "alarms" : "Alarmas", + "dashboards" : "Tableros", + "entities-and-relations" : "Entidades y Relaciones", + "profiles" : "Perfiles", + "advanced-features" : "Funciones avanzadas", + "notification-center" : "Centro de notificaciones", + "api-usage" : "Uso de API", + "customers" : "Clientes", + "customers-hierarchy" : "Jerarquía de clientes", + "roles-and-permissions" : "Roles y permisos", + "groups" : "Grupos", + "integrations" : "Integraciones", + "solution-templates" : "Plantillas de solución", + "scheduler" : "Planificador", + "white-labeling" : "Marca blanca" + }, + "devices" : { + "view-docs" : "Ver documentación", + "inactive" : "Inactivo", + "active" : "Activo", + "total" : "Total" + }, + "alarms" : { + "critical" : "Crítico", + "assigned-to-me" : "Asignado a mí", + "total" : "Total" + }, + "getting-started" : { + "get-started" : "Comenzar", + "finish" : "Finalizar", + "done-welcome-title" : "Bienvenido a bordo", + "done-welcome-text" : "¡Lo hiciste genial!", + "sys-admin" : { + "step1" : { + "title" : "Crear Tenant y Administrador del Tenant", + "content" : "

Un tenant es una persona u organización que posee o produce dispositivos y activos. El tenant puede tener múltiples usuarios administradores, clientes, dispositivos y activos.

El Administrador del Tenant puede crear y gestionar dispositivos, activos, clientes y tableros dentro de la cuenta del tenant.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-create-tenant" : "Cómo crear Tenant y Administrador del Tenant" }, - "documentation": { - "title": "Documentación", - "add-link": "Añadir enlace", - "add-link-title": "Añadir enlace de documentación", - "name": "Nombre", - "name-required": "Se requiere nombre.", - "link": "Enlace", - "link-required": "Se requiere enlace.", - "columns": "Columnas" + "step2" : { + "title" : "Configurar función: Servidor de correo", + "content" : "

La configuración del servidor de correo es esencial para la activación de usuarios, recuperación de contraseñas y entrega de notificaciones de alarmas.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-configure-mail-server" : "Cómo configurar el servidor de correo" }, - "quick-links": { - "title": "Enlaces rápidos", - "add-link": "Añadir enlace", - "add-link-title": "Añadir enlace rápido", - "quick-link": "Enlace rápido", - "quick-link-required": "Se requiere enlace rápido.", - "no-links-matching": "No se encontraron enlaces que coincidan con '{{name}}'.", - "columns": "Columnas" + "step3" : { + "title" : "Configurar función: Proveedor de SMS", + "content" : "

Configura proveedores de SMS para notificar a los clientes sobre las alarmas vía SMS.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-configure-sms-provider" : "Cómo configurar el proveedor de SMS" }, - "recent-dashboards": { - "title": "Paneles", - "last": "Última visualización", - "starred": "Favorito", - "name": "Nombre", - "last-viewed": "Último visto", - "no-last-viewed-dashboards": "No hay paneles aún" + "step4" : { + "title" : "Configurar función: White-labeling", + "content" : "

Personaliza fácilmente el logotipo y el esquema de colores de tu empresa o producto sin necesidad de codificación y sin reiniciar el servicio.

Sigue la documentación para saber cómo hacerlo:

" }, - "configured-features": { - "title": "Características configuradas", - "info": "Estado de las características que requieren configuración", - "email-feature": "Email", - "sms-feature": "SMS", - "slack-feature": "Slack", - "oauth2-feature": "OAuth 2", - "2fa-feature": "2FA", - "feature-configured": "Característica configurada.\nClick para acceder a configuración", - "feature-not-configured": "Característica no configurada.\nClick para configurar" + "step5" : { + "title" : "Configurar función: 2FA", + "content" : "

Mejora la seguridad de las cuentas de la plataforma con autenticación de dos factores.

Sigue la documentación para saber cómo hacerlo:

" }, - "version-info": { - "title": "Versión", - "contact-us": "Contáctanos", - "current-version": "Versión actual", - "current": "Actual", - "available-version": "Versión disponible", - "available": "Disponible", - "upgrade": "Actualizar", - "version-is-up-to-date": "La versión está actualizada" + "step6" : { + "title" : "Configurar función: OAuth 2", + "content" : "

Simplifica el inicio de sesión para los usuarios de tenant y clientes con inicio de sesión único a través de OAuth 2.0.

Sigue la documentación para saber cómo hacerlo:

" + } + }, + "tenant-admin" : { + "step1" : { + "title" : "Crear dispositivo", + "content" : "

Vamos a aprovisionar tu primer dispositivo en la plataforma mediante la interfaz de usuario. Sigue la documentación para saber cómo hacerlo:

", + "how-to-create-device" : "Cómo crear un dispositivo" }, - "usage-info": { - "title": "Uso", - "entities": "Entidades", - "api-calls": "Llamadas API" + "step2" : { + "title" : "Conectar dispositivo", + "content-before" : "

Para conectar el dispositivo necesitas obtener las credenciales del dispositivo. Recomendamos usar las credenciales autogeneradas por defecto, que en esta guía es un token de acceso.

  • Ve a la tabla de dispositivos
  • Haz clic en la fila del dispositivo para abrir los detalles
  • Presiona el botón \"Copiar token de acceso\"

Utiliza comandos simples para publicar datos por HTTP. No olvides reemplazar $ACCESS_TOKEN con el token de acceso de tu dispositivo:

", + "ubuntu" : { + "install-curl" : "Instalar cURL en Ubuntu:" + }, + "macos" : { + "install-curl" : "Instalar cURL en MacOS:" + }, + "windows" : { + "install-curl" : "Desde Windows 10 b17063, cURL está disponible por defecto." + }, + "replace-access-token" : "Reemplaza $ACCESS_TOKEN con el token de tu dispositivo:", + "content-after" : "

También puedes usar otros protocolos como MQTT, CoAP, etc.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-connect-device" : "Cómo conectar un dispositivo" }, - "functions": { - "title": "Funciones", - "pe-feature-tooltip": "Sólo en ThingsBoard\nProfessional Edition", - "switch-to-pe": "Cambiar a PE", - "alarms": "Alarmas", - "dashboards": "Paneles", - "entities-and-relations": "Entidades y relaciones", - "profiles": "Perfiles", - "advanced-features": "Características avanzadas", - "notification-center": "Centro de notificaciones", - "api-usage": "Uso de API", - "customers": "Clientes", - "customers-hierarchy": "Jerarquía de clientes", - "roles-and-permissions": "Reglas y permisos", - "groups": "Grupos", - "integrations": "Integraciones", - "solution-templates": "Plantillas de solución", - "scheduler": "Programador", - "white-labeling": "Marca-blanca" + "step3" : { + "title" : "Crear tablero", + "content" : "

Crea un tablero para visualizar datos de entidades como activos, dispositivos, etc.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-create-dashboard" : "Cómo crear un tablero" }, - "devices": { - "view-docs": "Ver documentación", - "inactive": "Inactivos", - "active": "Activos", - "total": "Total" + "step4" : { + "title" : "Configurar reglas de alarma", + "alarm-rules" : "Reglas de alarma", + "content" : "

Vamos a generar una alarma cuando la temperatura alcance los 25°C. Sigue la documentación para saber cómo hacerlo:

", + "how-to-configure-alarm-rules" : "Cómo configurar reglas de alarma" }, - "alarms": { - "critical": "Críticas", - "assigned-to-me": "Asignadas a mí", - "total": "Total" + "step5" : { + "title" : "Crear alarma", + "content-before" : "

Para activar la alarma, envía un nuevo valor de telemetría de 26°C o más.

", + "replace-access-token" : "Reemplaza $ACCESS_TOKEN con el token de tu dispositivo:", + "content-after" : "

Sigue la documentación para saber cómo hacerlo:

", + "how-to-create-alarm" : "Cómo crear una alarma" }, - "getting-started": { - "get-started": "Empezar", - "finish": "Finalizar", - "done-welcome-title": "Bienvenido a bordo", - "done-welcome-text": "Fantástico, lo has terminado!", - "sys-admin": { - "step1": { - "title": "Crear un propietario y un administrador propietario", - "content": "

Un propietario es un individual o una organización, que tiene o produce dispositivos o activos. Los Propietarios, pueden crear usuarios tipo Administrador Propietario.

Sigue la documentación para proceder:

", - "how-to-create-tenant": "Cómo crear un Propietario" - }, - "step2": { - "title": "Configurar característica: Servidor emails", - "content": "

Los usuarios reciben emails de activación y reset de contraseña a través de un servidor SMTP.

Sigue la documentación para proceder:

", - "how-to-configure-mail-server": "Cómo configurar el Servidor eMail" - }, - "step3": { - "title": "Configurar característica: Proveedor SMS", - "content": "

Para distribuir mensajes SMS sobre alarmas, el administrador del sistema debe configurar proveedores SMS.

Sigue la documentación para proceder:

", - "how-to-configure-sms-provider": "Cómo configurar el proveedor SMS" - }, - "step4": { - "title": "Configurar característica: 2FA", - "content": "

Añadiendo autenticación 2FA (doble factor) a tu cuenta, incrementa la seguridad y previene accesos maliciosos.

Sigue la documentación para proceder:

", - "how-to-configure-2fa": "Cómo configurar 2FA" - }, - "step5": { - "title": "Configurar característica: OAuth 2", - "content": "

Puedes proveer a tus clientes con una solución 'Single Sign-On' y crear automáticamente administradores, clientes o subclientes usando una plataforma externa que permita OAuth 2.0.

Sigue la documentación para proceder:

", - "how-to-configure-oauth2": "Cómo configurar OAuth 2" - }, - "step6": { - "title": "Configurar característica: Slack", - "content": "

Los usuarios, seran capaces de recibir notificaciones en Slack, de los eventos que ocurran en el sistema, de acuerdo a las reglas de notificación que hayas configurado.

Sigue la documentación para proceder:

", - "how-to-configure-notifications": "Cómo configurar Slack" - } - }, - "tenant-admin": { - "step1": { - "title": "Crear dispositivo", - "content": "

Aprovisionar el dispositivo de forma manual, usando la interfaz. Sigue la documentación para proceder:

", - "how-to-create-device": "Cómo crear un dispositivo" - }, - "step2": { - "title": "Conectar dispositivo", - "content-before": "

Para conectar un dispositivo, necesitas obtener sus credenciales. Recomendamos usar las credenciales generadas por defecto.

  • Ir a la lista de dispositivos
  • Haz click en el dispositivo, para abrir los detalles
  • Presiona el botón\"Copiar access token\"

Usa comandos simples para publicar datos sobre HTTP:

", - "ubuntu": { - "install-curl": "Instalar cURL en Ubuntu:" - }, - "macos": { - "install-curl": "Instalar cURL en MacOS:" - }, - "windows": { - "install-curl": "En Windows 10 b17063, cURL está disponible por defecto." - }, - "replace-access-token": "Reemplazar $ACCESS_TOKEN con el token de tu dispositivo:", - "content-after": "

Puedes usar también otros protocolos, como MQTT, CoAP, etc.

Sigue la documentación para proceder:

", - "how-to-connect-device": "Cómo conectar un dispositivo" - }, - "step3": { - "title": "Crear un panel", - "content": "

Crear un panel para visualizar los datos de las entidades, como activos, dispositivos, etc..

Sigue la documentación para proceder:

", - "how-to-create-dashboard": "Cómo crear un panel" - }, - "step4": { - "title": "Configurar reglas de alarma", - "alarm-rules": "Reglas de alarma", - "content": "

Cuando la temperatura alcance 25°C, dispararemos una alarma. Sigue la documentación para proceder:

", - "how-to-configure-alarm-rules": "Cómo configurar reglas de alarma" - }, - "step5": { - "title": "Crear alarma", - "content-before": "

Para disparar la alarma, envía una nueva telemetría con un valor de 26°C o superior.

", - "replace-access-token": "Reemplazar $ACCESS_TOKEN con el token de tu dispositivo:", - "content-after": "

Sigue la documentación para proceder:

", - "how-to-create-alarm": "Cómo crear una alarma" - }, - "step6": { - "title": "Crear cliente y asignarle un panel", - "content": "

Creando un panel para un cliente, permitirá al cliente ver sus propios dispositivos. Los datos de otros clientes estarán ocultos.

Sigue la documentación para proceder:

", - "how-to-create-customer-and-assign-dashboard": "Cómo crear un cliente y asignarle un panel" - } - } - } - }, - "color": { - "color": "Color" - }, - "icon": { - "icon": "Icono", - "icons": "Iconos", - "select-icon": "Seleccionar icono", - "material-icons": "Iconos material-design", - "show-all": "Mostrar todos los iconos", - "search-icon": "Buscar icono", - "no-icons-found": "No se han encontrado iconos que coincidan con '{{iconSearch}}'" - }, - "phone-input": { - "phone-input-label": "Número de teléfono", - "phone-input-required": "Número de teléfono requerido", - "phone-input-validation": "El número es inválido o erróneo", - "phone-input-pattern": "Número inválido. Debe cumplir el formato E.164, ej. {{phoneNumber}}", - "phone-input-hint": "Número en el formato E.164, ej. {{phoneNumber}}" - }, - "custom": { - "widget-action": { - "action-cell-button": "Acción botón de celda", - "row-click": "En click de fila", - "polygon-click": "Clic en polígono", - "marker-click": "En click de marcador", - "circle-click": "En click de círculo", - "tooltip-tag-action": "Acción de la etiqueta Tooltip", - "node-selected": "Clic en el nodo seleccionado", - "element-click": "Clic en el elemento HTML", - "pie-slice-click": "Clic en la rebanada", - "row-double-click": "Doble clic en la fila", - "card-click": "En click de tarjeta" + "step6" : { + "title" : "Crear cliente y compartir tablero", + "content" : "

Al crear tableros para el usuario final, un usuario cliente solo podrá ver sus propios dispositivos y los datos de otro cliente estarán ocultos.

Sigue la documentación para saber cómo hacerlo:

" } - }, - "paginator" : { - "items-per-page": "Ítems por página:", - "first-page-label": "Primera página", - "last-page-label": "Última página", - "next-page-label": "Siguiente", - "previous-page-label": "Anterior", - "items-per-page-separator": "de" - }, - "language": { - "language": "Idioma" + } + } + }, + "icon" : { + "icon" : "Icono", + "icons" : "Iconos", + "select-icon" : "Seleccionar icono", + "material-icons" : "Iconos de Material", + "show-all" : "Mostrar todos los iconos", + "search-icon" : "Buscar icono", + "no-icons-found" : "No se encontraron iconos para '{{iconSearch}}'" + }, + "phone-input" : { + "phone-input-label" : "Número de teléfono", + "phone-input-required" : "Se requiere el número de teléfono", + "phone-input-validation" : "El número de teléfono no es válido o no es posible", + "phone-input-pattern" : "Número de teléfono inválido. Debe estar en formato E.164, ej. {{phoneNumber}}", + "phone-input-hint" : "Número de teléfono en formato E.164, ej. {{phoneNumber}}" + }, + "custom" : { + "widget-action" : { + "action-cell-button" : "Botón de acción en celda", + "row-click" : "Al hacer clic en la fila", + "cell-click" : "Al hacer clic en la celda", + "polygon-click" : "Al hacer clic en el polígono", + "marker-click" : "Al hacer clic en el marcador", + "circle-click" : "Al hacer clic en el círculo", + "tooltip-tag-action" : "Acción de etiqueta de tooltip", + "node-selected" : "Al seleccionar el nodo", + "element-click" : "Al hacer clic en el elemento HTML", + "pie-slice-click" : "Al hacer clic en una porción", + "row-double-click" : "Al hacer doble clic en la fila", + "cell-double-click" : "Al hacer doble clic en la celda", + "card-click" : "Al hacer clic en la tarjeta", + "click" : "Al hacer clic" + } + }, + "paginator" : { + "items-per-page" : "Elementos por página:", + "first-page-label" : "Primera página", + "last-page-label" : "Última página", + "next-page-label" : "Página siguiente", + "previous-page-label" : "Página anterior", + "items-per-page-separator" : "de" + }, + "language" : { + "language" : "Language", + "locales" : { + "ar_AE" : "العربية (الإمارات العربية المتحدة)", + "ca_ES" : "català (Espanya)", + "cs_CZ" : "čeština (Česko)", + "da_DK" : "dansk (Danmark)", + "de_DE" : "Deutsch (Deutschland)", + "el_GR" : "Ελληνικά (Ελλάδα)", + "en_US" : "English (United States)", + "es_ES" : "español (España)", + "fa_IR" : "فارسی (ایران)", + "fr_FR" : "français (France)", + "it_IT" : "italiano (Italia)", + "ja_JP" : "日本語 (日本)", + "ka_GE" : "ქართული (საქართველო)", + "ko_KR" : "한국어 (대한민국)", + "lt_LT" : "lietuvių (Lietuva)", + "lv_LV" : "latviešu (Latvija)", + "nl_BE" : "Nederlands (België)", + "pl_PL" : "polski (Polska)", + "pt_BR" : "português (Brasil)", + "ro_RO" : "română (România)", + "sl_SI" : "slovenščina (Slovenija)", + "tr_TR" : "Türkçe (Türkiye)", + "uk_UA" : "українська (Україна)", + "zh_CN" : "中文 (中国)", + "zh_TW" : "中文 (台灣)" } -} + } +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json index 70f348a769..9b3d764ca0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json @@ -1,2315 +1,9224 @@ { - "access": { - "access-forbidden": "Accès interdit", - "access-forbidden-text": "Vous n'avez pas accès à cet emplacement!
Essayez de vous connecter avec un autre utilisateur si vous souhaitez toujours accéder à cet emplacement.", - "refresh-token-expired": "La session a expiré", - "refresh-token-failed": "Impossible de rafraîchir la session", - "unauthorized": "Non autorisé", - "unauthorized-access": "Accès non autorisé", - "unauthorized-access-text": "Vous devez vous connecter pour avoir accès à cette ressource!", - "permission-denied": "Permission refusée", - "permission-denied-text": "Vous n'avez pas la permission de faire l'opération demandée!" - }, - "action": { - "activate": "Activer", - "add": "Ajouter", - "apply": "Appliquer", - "apply-changes": "Appliquer les modifications", - "assign": "Attribuer", - "back": "Retour", - "cancel": "Annuler", - "clear-search": "Effacer la recherche", - "close": "Fermer", - "continue": "Continuer", - "copy": "Copier", - "copy-reference": "Copier la référence", - "create": "Créer", - "decline-changes": "Refuser les modifications", - "delete": "Supprimer", - "discard-changes": "Annuler les modifications", - "drag": "Drag", - "edit": "Modifier", - "edit-mode": "Mode édition", - "enter-edit-mode": "Entrer en mode édition", - "export": "Exporter", - "import": "Importer", - "make-private": "Rendre privé", - "no": "Non", - "ok": "OK", - "paste": "coller", - "paste-reference": "Coller référence", - "refresh": "Rafraîchir", - "remove": "Supprimer", - "run": "Exécuter", - "save": "Enregistrer", - "saveAs": "Enregistrer sous", - "search": "Rechercher", - "share": "Partager", - "share-via": "Partager via {{provider}}", - "sign-in": "Connectez-vous!", - "suspend": "Suspendre", - "unassign": "Retirer", - "undo": "Annuler", - "update": "Mise à jour", - "view": "Afficher", - "yes": "Oui", - "select": "Sélectionner", - "download": "Télécharger", - "next-with-label": "Suivant: {{label}}", - "read-more": "En savoir plus", - "hide": "Masquer" - }, - "admin": { - "base-url": "URL de base", - "base-url-required": "L'URL de base est requis.", - "enable-tls": "Activer TLS", - "tls-version": "Version TLS", - "general": "Général", - "general-settings": "Paramètres généraux", - "mail-from": "Courriel de", - "mail-from-required": "Courriel de est requis.", - "outgoing-mail": "courrier sortant", - "outgoing-mail-settings": "Paramètres de courrier sortant", - "send-test-mail": "Envoyer un courriel de test", - "smtp-host": "Hôte SMTP", - "smtp-host-required": "L'hôte SMTP est requis.", - "smtp-port": "Port SMTP", - "smtp-port-invalid": "Cela ne ressemble pas à un port smtp valide.", - "smtp-port-required": "Vous devez fournir un port smtp.", - "smtp-protocol": "Protocole SMTP", - "system-settings": "Paramètres système", - "test-mail-sent": "Le courrier de test a été envoyé avec succès!", - "timeout-invalid": "Cela ne ressemble pas à un délai d'expiration valide.", - "timeout-msec": "Délai (msec)", - "timeout-required": "Le délai est requis.", - "security-settings": "Les paramètres de sécurité", - "password-policy": "Politique de mot de passe", - "minimum-password-length": "Longueur minimale du mot de passe", - "minimum-password-length-required": "La longueur minimale du mot de passe est requise", - "minimum-password-length-range": "La longueur minimale du mot de passe doit être comprise entre 5 et 50.", - "minimum-uppercase-letters": "Nombre minimum de lettres majuscules", - "minimum-uppercase-letters-range": "Le nombre minimum de lettres majuscules ne peut pas être négatif", - "minimum-lowercase-letters": "Nombre minimum de lettres minuscules", - "minimum-lowercase-letters-range": "Le nombre minimum de lettres minuscules ne peut pas être négatif", - "minimum-digits": "Nombre minimum de chiffres", - "minimum-digits-range": "Le nombre minimum de chiffres ne peut pas être négatif", - "minimum-special-characters": "Nombre minimum de caractères spéciaux", - "minimum-special-characters-range": "Le nombre minimum de caractères spéciaux ne peut pas être négatif", - "password-expiration-period-days": "Délai d'expiration du mot de passe en jours", - "password-expiration-period-days-range": "La période d'expiration du mot de passe en jours ne peut pas être négative", - "password-reuse-frequency-days": "Fréquence de réutilisation du mot de passe en jours", - "password-reuse-frequency-days-range": "La fréquence de réutilisation du mot de passe en jours ne peut être négative", - "general-policy": "Politique générale", - "max-failed-login-attempts": "Nombre maximal de tentatives de connexion infructueuses avant que le compte ne soit verrouillé", - "minimum-max-failed-login-attempts-range": "Le nombre maximal de tentatives de connexion ayant échoué ne peut pas être négatif", - "user-lockout-notification-email": "En cas de verrouillage du compte d'utilisateur, envoyez une notification par courrier électronique.", - "prohibit-different-url": "Interdire d'utiliser le nom d'hôte à partir des en-têtes de requête client", - "prohibit-different-url-hint": "Ce paramètre doit être activé pour les environnements de production. Peut causer des problèmes de sécurité lorsqu'il est désactivé.", - "enable-proxy": "Activer proxy", - "proxy-host": "Hôte proxy", - "proxy-host-required": "L'hôte proxy est requis.", - "proxy-port": "Port du proxy", - "proxy-port-required": "Port du proxy est requis.", - "proxy-port-range": "Le port proxy doit être compris entre 1 et 65535.", - "proxy-user": "Utilisateur proxy", - "proxy-password": "Mot de passe proxy", - "change-password": "Changer mot de passe", - "sms-provider": "Fournisseur SMS", - "sms-provider-settings": "Paramètres du fournisseur de SMS", - "sms-provider-type": "Type de fournisseur de SMS", - "sms-provider-type-required": "Le type de fournisseur de SMS est requis.", - "aws-access-key-id": "ID de clé d'accès AWS", - "aws-access-key-id-required": "L'ID de clé d'accès AWS est requis", - "aws-secret-access-key": "Clé d'accès secrète AWS", - "aws-secret-access-key-required": "La clé d'accès secrète AWS est requise", - "aws-region": "Région AWS", - "aws-region-required": "La région AWS est obligatoire", - "number-from": "Numéro de téléphone de", - "number-from-required": "Numéro de téléphone de est requis", - "number-to": "Numéro de téléphone à", - "number-to-required": "Numéro de téléphone à est requis.", - "phone-number-hint": "Numéro de téléphone au format E.164, ex. +19995550123", - "phone-number-hint-twilio": "Numéro de téléphone au format E.164/SID du numéro de téléphone/SID du service de messagerie, ex. +19995550123/PNXXX/MGXXX", - "phone-number-pattern": "Numéro de téléphone invalide. Doit être au format E.164, ex. +19995550123.", - "phone-number-pattern-twilio": "Numéro de téléphone invalide. Doit être au format E.164/SID du numéro de téléphone/SID du service de messagerie, ex. +19995550123/PNXXX/MGXXX.", - "sms-message": "Message SMS", - "sms-message-required": "Message SMS requis.", - "sms-message-max-length": "Le message SMS ne peut pas contenir plus de 1600 caractères", - "twilio-account-sid": "SID du compte Twilio", - "twilio-account-sid-required": "SID du compte Twilio requis", - "twilio-account-token": "Jeton du compte Twilio", - "twilio-account-token-required": "Jeton du compte Twilio est requis", - "send-test-sms": "Envoyer SMS test", - "test-sms-sent": "Le SMS de test a été envoyé avec succès !", - "allow-whitespace": "Autoriser les espaces", - "domain-name": "Nom de domaine", - "domain-name-unique": "Le nom de domaine et le protocole doivent être uniques.", - "domain-name-max-length": "Le nom de domaine doit être inférieur à 256", - "error-verification-url": "Un nom de domaine ne doit pas contenir les symboles '/' et ':'. Exemple : Thingsboard.io", - "oauth2": { - "access-token-uri": "URI du jeton d'accès", - "access-token-uri-required": "URI du jeton d'accès requis.", - "activate-user": "Activer l'utilisateur", - "add-domain": "Ajouter un domaine", - "delete-domain": "Supprimer un domaine", - "add-provider": "Ajouter un fournisseur", - "delete-provider": "Supprimer un fournisseur", - "allow-user-creation": "Autoriser la création d'utilisateurs", - "always-fullscreen": "Toujours plein écran", - "authorization-uri": "URI d'autorisation", - "authorization-uri-required": "L'URI d'autorisation est obligatoire.", - "client-authentication-method": "Méthode d'authentification client", - "client-id": "Identifiant du client", - "client-id-required": "L'identifiant client est requis.", - "client-id-max-length": "L'ID client doit être inférieur à 256", - "client-secret": "Secret client", - "client-secret-required": "Secret client requis.", - "client-secret-max-length": "Le secret client doit être inférieur à 2049", - "custom-setting": "Paramètres personnalisés", - "customer-name-pattern": "Modèle de nom de client", - "customer-name-pattern-max-length": "Le modèle de nom de client doit être inférieur à 256", - "default-dashboard-name": "Nom du tableau de bord par défaut", - "default-dashboard-name-max-length": "Le nom du tableau de bord par défaut doit être inférieur à 256", - "delete-domain-text": "Attention, après la confirmation un domaine et toutes les données du fournisseur seront indisponibles.", - "delete-domain-title": "Voulez-vous vraiment supprimer les paramètres du domaine '{{domainName}}'?", - "delete-registration-text": "Attention, après la confirmation, les données d'un fournisseur seront indisponibles.", - "delete-registration-title": "Êtes-vous sûr de vouloir supprimer le fournisseur'{{name}}'?", - "email-attribute-key": "Clé d'attribut de courriel", - "email-attribute-key-required": "La clé d'attribut de courriel est requise.", - "email-attribute-key-max-length": "La clé d'attribut de courriel doit être inférieure à 32", - "first-name-attribute-key": "Clé d'attribut du prénom", - "general": "Général", - "jwk-set-uri": "URI de la clé Web JSON", - "last-name-attribute-key": "Clé d'attribut du nom de famille", - "login-button-icon": "Icône du bouton de connexion", - "login-button-label": "Libellé du fournisseur", - "login-button-label-placeholder": "Connectez-vous avec $(Provider label)", - "login-button-label-required": "L'étiquette est obligatoire.", - "login-provider": "Fournisseur de connexion", - "new-domain": "Nouveau domaine", - "password-max-length": "Le mot de passe doit être inférieur à 256", - "redirect-uri-template": "Modèle d'URI de redirection", - "copy-redirect-uri": "Copier l'URI de redirection", - "registration-id": "ID d'enregistrement", - "registration-id-required": "L'identifiant d'enregistrement est requis.", - "registration-id-unique": "L'ID d'enregistrement doit être unique pour le système.", - "scope": "Portée", - "scope-required": "La portée est requise.", - "tenant-name-pattern": "Modèle de nom du Tenant ", - "tenant-name-pattern-required": "Un modèle de nom de Tenant est requis.", - "tenant-name-pattern-max-length": "Le modèle de nom de Tenant doit être inférieur à 256", - "tenant-name-strategy": "Stratégie de nom de Tenant", - "type": "Type de Mapper", - "uri-pattern-error": "Format d'URI invalide.", - "url-pattern": "Format d'URL non valide.", - "url-required": "L'URL est requis.", - "url-max-length": "L'URL doit être inférieure à 256", - "user-info-uri": "URI des informations utilisateur", - "user-info-uri-required": "L'URI des informations utilisateur est requise.", - "username-max-length": "Le nom d'utilisateur doit être inférieur à 256", - "user-name-attribute-name": "Clé d'attribut de nom d'utilisateur", - "user-name-attribute-name-required": "La clé d'attribut du nom d'utilisateur est requise", - "protocol": "Protocole", - "enable": "Activer les paramètres OAuth2", - "domains": "Domainse", - "mobile-apps": "Applications mobiles", - "no-mobile-apps": "Aucune application configurée", - "mobile-package": "Package d'application", - "mobile-package-placeholder": "Ex. : mon.exemple.app", - "mobile-package-hint": "Pour Android : votre propre ID d'application unique. Pour iOS : identifiant du groupe de produits.", - "mobile-package-unique": "Le package d'application doit être unique.", - "mobile-app-secret": "Secret d'application", - "invalid-mobile-app-secret": "Le secret d'application ne doit contenir que des caractères alphanumériques et doit comporter entre 16 et 2 048 caractères.", - "copy-mobile-app-secret": "Copier le secret de l'application", - "add-mobile-app": "Ajouter une application", - "delete-mobile-app": "Supprimer les informations sur l'application", - "providers": "Fournisseurs", - "all-platforms": "Toutes les plateformes", - "allowed-platforms": "Plates-formes autorisées" - } + "access" : { + "unauthorized" : "Non autorisé", + "unauthorized-access" : "Accès non autorisé", + "unauthorized-access-text" : "Vous devez vous connecter pour accéder à cette ressource !", + "access-forbidden" : "Accès interdit", + "access-forbidden-text" : "Vous n'avez pas les droits d'accès à cet emplacement !
Essayez de vous connecter avec un autre utilisateur si vous souhaitez toujours y accéder.", + "refresh-token-expired" : "La session a expiré", + "refresh-token-failed" : "Impossible de renouveler la session", + "permission-denied" : "Permission refusée", + "permission-denied-text" : "Vous n'avez pas la permission d'effectuer cette opération !" + }, + "account" : { + "account" : "Compte", + "notification-settings" : "Paramètres de notification" + }, + "action" : { + "activate" : "Activer", + "suspend" : "Suspendre", + "save" : "Enregistrer", + "saveAs" : "Enregistrer sous", + "move" : "Déplacer", + "cancel" : "Annuler", + "ok" : "OK", + "delete" : "Supprimer", + "add" : "Ajouter", + "yes" : "Oui", + "no" : "Non", + "update" : "Mettre à jour", + "remove" : "Retirer", + "search" : "Rechercher", + "clear-search" : "Effacer la recherche", + "assign" : "Assigner", + "unassign" : "Désassigner", + "share" : "Partager", + "make-private" : "Rendre privé", + "apply" : "Appliquer", + "apply-changes" : "Appliquer les modifications", + "edit-mode" : "Mode édition", + "enter-edit-mode" : "Entrer en mode édition", + "decline-changes" : "Refuser les modifications", + "decline" : "Refuser", + "close" : "Fermer", + "back" : "Retour", + "run" : "Exécuter", + "sign-in" : "Connectez-vous !", + "edit" : "Modifier", + "view" : "Voir", + "create" : "Créer", + "drag" : "Glisser", + "refresh" : "Rafraîchir", + "undo" : "Annuler", + "copy" : "Copier", + "paste" : "Coller", + "copy-reference" : "Copier la référence", + "paste-reference" : "Coller la référence", + "import" : "Importer", + "export" : "Exporter", + "share-via" : "Partager via {{provider}}", + "select" : "Sélectionner", + "continue" : "Continuer", + "discard-changes" : "Ignorer les modifications", + "download" : "Télécharger", + "next" : "Suivant", + "next-with-label" : "Suivant : {{label}}", + "read-more" : "Lire plus", + "hide" : "Masquer", + "test" : "Tester", + "done" : "Terminé", + "print" : "Imprimer", + "restore" : "Restaurer", + "confirm" : "Confirmer", + "more" : "Plus", + "less" : "Moins", + "skip" : "Passer", + "send" : "Envoyer", + "reset" : "Réinitialiser", + "show-more" : "Afficher plus", + "dont-show-again" : "Ne plus afficher", + "see-documentation" : "Voir la documentation", + "clear" : "Effacer", + "upload" : "Téléverser", + "delete-anyway" : "Supprimer quand même", + "delete-selected" : "Supprimer la sélection", + "set" : "Définir" + }, + "aggregation" : { + "aggregation" : "Agrégation", + "function" : "Fonction d'agrégation des données", + "limit" : "Valeurs max", + "group-interval" : "Intervalle de regroupement", + "min" : "Min", + "max" : "Max", + "avg" : "Moyenne", + "sum" : "Somme", + "count" : "Compte", + "none" : "Aucun" + }, + "admin" : { + "settings" : "Paramètres", + "general" : "Général", + "general-settings" : "Paramètres généraux", + "home-settings" : "Paramètres d'accueil", + "home" : "Accueil", + "outgoing-mail" : "Serveur de messagerie", + "outgoing-mail-settings" : "Paramètres du serveur de messagerie sortant", + "system-settings" : "Paramètres système", + "test-mail-sent" : "E-mail de test envoyé avec succès !", + "base-url" : "URL de base", + "base-url-required" : "L'URL de base est requise.", + "prohibit-different-url" : "Interdire l'utilisation du nom d'hôte des en-têtes de requête client", + "prohibit-different-url-hint" : "Ce paramètre doit être activé pour les environnements de production. Peut entraîner des problèmes de sécurité lorsqu'il est désactivé", + "device-connectivity" : { + "device-connectivity" : "Connectivité des appareils", + "http-s" : "HTTP(s)", + "mqtt-s" : "MQTT(s)", + "coap-s" : "COAP(s)", + "http" : "HTTP", + "https" : "HTTPs", + "mqtt" : "MQTT", + "mqtts" : "MQTTs", + "coap" : "COAP", + "coaps" : "COAPs", + "hint" : "Si les champs hôte ou port sont vides, la valeur du protocole par défaut sera utilisée.", + "host" : "Hôte", + "port" : "Port", + "port-pattern" : "Le port doit être un entier positif.", + "port-range" : "Le port doit être compris entre 1 et 65535." + }, + "mail-from" : "Adresse expéditrice", + "mail-from-required" : "L'adresse expéditrice est requise.", + "smtp-protocol" : "Protocole SMTP", + "smtp-host" : "Hôte SMTP", + "smtp-host-required" : "L'hôte SMTP est requis.", + "smtp-port" : "Port SMTP", + "smtp-port-required" : "Vous devez fournir un port SMTP.", + "smtp-port-invalid" : "Ce port SMTP semble invalide.", + "timeout-msec" : "Délai d'attente (ms)", + "timeout-required" : "Le délai d'attente est requis.", + "timeout-invalid" : "Ce délai d'attente semble invalide.", + "enable-tls" : "Activer TLS", + "tls-version" : "Version TLS", + "enable-proxy" : "Activer le proxy", + "proxy-host" : "Hôte proxy", + "proxy-host-required" : "L'hôte proxy est requis.", + "proxy-port" : "Port proxy", + "proxy-port-required" : "Le port proxy est requis.", + "proxy-port-range" : "Le port proxy doit être compris entre 1 et 65535.", + "proxy-user" : "Utilisateur proxy", + "proxy-password" : "Mot de passe proxy", + "change-password" : "Changer le mot de passe", + "send-test-mail" : "Envoyer un e-mail de test", + "sms-provider" : "Fournisseur SMS", + "sms-provider-settings" : "Paramètres du fournisseur SMS", + "sms-provider-type" : "Type de fournisseur SMS", + "sms-provider-type-required" : "Le type de fournisseur SMS est requis.", + "sms-provider-type-aws-sns" : "Amazon SNS", + "sms-provider-type-twilio" : "Twilio", + "sms-provider-type-smpp" : "SMPP", + "aws-access-key-id" : "ID de clé d'accès AWS", + "aws-access-key-id-required" : "L'ID de clé d'accès AWS est requis", + "aws-secret-access-key" : "Clé d'accès secrète AWS", + "aws-secret-access-key-required" : "La clé d'accès secrète AWS est requise", + "aws-region" : "Région AWS", + "aws-region-required" : "La région AWS est requise", + "number-from" : "Numéro de téléphone expéditeur", + "number-from-required" : "Le numéro de téléphone expéditeur est requis.", + "number-to" : "Numéro de téléphone destinataire", + "number-to-required" : "Le numéro de téléphone destinataire est requis.", + "phone-number-hint" : "Numéro de téléphone au format E.164, ex. +19995550123", + "phone-number-hint-twilio" : "Numéro de téléphone au format E.164 / SID du numéro / SID du service de messagerie, ex. +19995550123/PNXXX/MGXXX", + "phone-number-pattern" : "Numéro de téléphone invalide. Doit être au format E.164, ex. +19995550123.", + "phone-number-pattern-twilio" : "Numéro de téléphone invalide. Doit être au format E.164 / SID du numéro / SID du service de messagerie, ex. +19995550123/PNXXX/MGXXX.", + "sms-message" : "Message SMS", + "sms-message-required" : "Le message SMS est requis.", + "sms-message-max-length" : "Le message SMS ne peut pas dépasser 1600 caractères", + "twilio-account-sid" : "SID du compte Twilio", + "twilio-account-sid-required" : "Le SID du compte Twilio est requis", + "twilio-account-token" : "Jeton du compte Twilio", + "twilio-account-token-required" : "Le jeton du compte Twilio est requis", + "send-test-sms" : "Envoyer un SMS de test", + "test-sms-sent" : "Le SMS de test a été envoyé avec succès !", + "security-settings" : "Paramètres de sécurité", + "password-policy" : "Politique de mot de passe", + "minimum-password-length" : "Longueur minimale du mot de passe", + "minimum-password-length-required" : "La longueur minimale du mot de passe est requise", + "minimum-password-length-range" : "La longueur minimale du mot de passe doit être comprise entre 6 et 50", + "maximum-password-length" : "Longueur maximale du mot de passe", + "maximum-password-length-min" : "La longueur maximale du mot de passe doit être d'au moins 6", + "maximum-password-length-less-min" : "La longueur maximale du mot de passe doit être supérieure à la longueur minimale", + "minimum-uppercase-letters" : "Nombre minimum de lettres majuscules", + "minimum-uppercase-letters-range" : "Le nombre minimum de lettres majuscules ne peut pas être négatif", + "minimum-lowercase-letters" : "Nombre minimum de lettres minuscules", + "minimum-lowercase-letters-range" : "Le nombre minimum de lettres minuscules ne peut pas être négatif", + "minimum-digits" : "Nombre minimum de chiffres", + "minimum-digits-range" : "Le nombre minimum de chiffres ne peut pas être négatif", + "minimum-special-characters" : "Nombre minimum de caractères spéciaux", + "minimum-special-characters-range" : "Le nombre minimum de caractères spéciaux ne peut pas être négatif", + "password-expiration-period-days" : "Période d'expiration du mot de passe (en jours)", + "password-expiration-period-days-range" : "La période d'expiration ne peut pas être négative", + "password-reuse-frequency-days" : "Fréquence de réutilisation du mot de passe (en jours)", + "password-reuse-frequency-days-range" : "La fréquence de réutilisation ne peut pas être négative", + "allow-whitespace" : "Autoriser les espaces", + "force-reset-password-if-no-valid" : "Forcer la réinitialisation du mot de passe si invalide", + "force-reset-password-if-no-valid-hint" : "Attention : cette option obligera les utilisateurs avec un mot de passe invalide à le réinitialiser par e-mail.", + "general-policy" : "Politique générale", + "max-failed-login-attempts" : "Nombre maximal de tentatives de connexion échouées avant verrouillage du compte", + "minimum-max-failed-login-attempts-range" : "Le nombre maximal de tentatives échouées ne peut pas être négatif", + "user-lockout-notification-email" : "En cas de verrouillage du compte utilisateur, envoyer une notification par e-mail", + "user-activation-token-ttl" : "Durée de validité du lien d'activation (en heures)", + "user-activation-token-ttl-range" : "La durée doit être comprise entre 1 et 24 heures", + "password-reset-token-ttl" : "Durée de validité du lien de réinitialisation (en heures)", + "password-reset-token-ttl-range" : "La durée doit être comprise entre 1 et 24 heures", + "mobile-secret-key-length" : "Longueur de la clé secrète mobile", + "mobile-secret-key-length-range" : "La longueur de la clé doit être positive", + "domain-name" : "Nom de domaine", + "domain-name-unique" : "Le nom de domaine et le protocole doivent être uniques.", + "domain-name-max-length" : "Le nom de domaine doit contenir moins de 256 caractères", + "error-verification-url" : "Un nom de domaine ne doit pas contenir les caractères '/' et ':'. Exemple : thingsboard.io", + "connection-settings" : "Paramètres de connexion", + "oauth2" : { + "access-token-uri" : "URI du jeton d'accès", + "access-token-uri-required" : "L'URI du jeton d'accès est requis.", + "activate-user" : "Activer l'utilisateur", + "add-domain" : "Ajouter un domaine", + "delete-domain" : "Supprimer le domaine", + "add-provider" : "Ajouter un fournisseur", + "delete-provider" : "Supprimer le fournisseur", + "allow-user-creation" : "Autoriser la création d'utilisateurs", + "always-fullscreen" : "Toujours en plein écran", + "authorization-uri" : "URI d'autorisation", + "authorization-uri-required" : "L'URI d'autorisation est requis.", + "add-client" : "Ajouter un client OAuth 2.0", + "client-details" : "Détails du client OAuth 2.0", + "client" : "Client OAuth 2.0", + "clients" : "Clients OAuth 2.0", + "no-oauth2-clients" : "Aucun client OAuth 2.0 trouvé", + "search-oauth2-clients" : "Rechercher des clients OAuth 2.0", + "delete-client-title" : "Êtes-vous sûr de vouloir supprimer le client OAuth 2.0 '{{clientName}}' ?", + "delete-client-text" : "Attention, après confirmation, le client et toutes les données associées seront irrécupérables.", + "delete-mobile-app-title" : "Êtes-vous sûr de vouloir supprimer l'application mobile '{{applicationName}}' ?", + "delete-mobile-app-text" : "Attention, après confirmation, l'application mobile et toutes les données associées seront irrécupérables.", + "title" : "Titre", + "client-title-required" : "Le titre est requis", + "client-title-max-length" : "Le titre doit contenir moins de 100 caractères", + "advanced-settings" : "Paramètres avancés", + "domain-details" : "Détails du domaine", + "no-domains" : "Aucun domaine trouvé", + "search-domains" : "Rechercher des domaines", + "mobile-app-details" : "Détails de l'application mobile", + "add-mobile-app" : "Ajouter une application mobile", + "no-mobile-apps" : "Aucune application mobile trouvée", + "search-mobile-apps" : "Rechercher des applications mobiles", + "send-token" : "Envoyer le jeton", + "create-new" : "Créer nouveau", + "client-authentication-method" : "Méthode d'authentification du client", + "client-id" : "ID du client", + "client-id-required" : "L'ID du client est requis.", + "client-id-max-length" : "L'ID du client doit contenir moins de 256 caractères", + "client-secret" : "Secret du client", + "client-secret-required" : "Le secret du client est requis.", + "client-secret-max-length" : "Le secret du client doit contenir moins de 2049 caractères", + "custom-setting" : "Paramètres personnalisés", + "customer-name-pattern" : "Modèle de nom de client", + "customer-name-pattern-max-length" : "Le modèle de nom de client doit contenir moins de 256 caractères", + "default-dashboard-name" : "Nom du tableau de bord par défaut", + "default-dashboard-name-max-length" : "Le nom du tableau de bord par défaut doit contenir moins de 256 caractères", + "delete-domain-text" : "Attention, après confirmation, le domaine et toutes les données du fournisseur seront indisponibles.", + "delete-domain-title" : "Êtes-vous sûr de vouloir supprimer le domaine '{{domainName}}' ?", + "delete-registration-text" : "Attention, après confirmation, les données du fournisseur seront indisponibles.", + "delete-registration-title" : "Êtes-vous sûr de vouloir supprimer le fournisseur '{{name}}' ?", + "email-attribute-key" : "Clé de l'attribut e-mail", + "email-attribute-key-required" : "La clé de l'attribut e-mail est requise.", + "email-attribute-key-max-length" : "La clé de l'attribut e-mail doit contenir moins de 32 caractères", + "first-name-attribute-key" : "Clé de l'attribut prénom", + "first-name-attribute-key-max-length" : "La clé de l'attribut prénom doit contenir moins de 32 caractères", + "general" : "Général", + "jwk-set-uri" : "URI de la clé Web JSON", + "last-name-attribute-key" : "Clé de l'attribut nom", + "last-name-attribute-key-max-length" : "La clé de l'attribut nom doit contenir moins de 32 caractères", + "login-button-icon" : "Icône du bouton de connexion", + "login-button-label" : "Libellé du fournisseur", + "login-button-label-placeholder" : "Connexion avec $(Provider label)", + "login-button-label-required" : "Le libellé est requis.", + "login-provider" : "Fournisseur de connexion", + "mapper" : "Mappeur", + "new-domain" : "Nouveau domaine", + "oauth2" : "OAuth 2.0", + "password-max-length" : "Le mot de passe doit contenir moins de 256 caractères", + "redirect-uri-template" : "Modèle d'URI de redirection", + "copy-redirect-uri" : "Copier l'URI de redirection", + "registration-id" : "ID d'enregistrement", + "registration-id-required" : "L'ID d'enregistrement est requis.", + "registration-id-unique" : "L'ID d'enregistrement doit être unique dans le système.", + "scope" : "Portée", + "scope-required" : "La portée est requise.", + "tenant-name-pattern" : "Modèle de nom du locataire", + "tenant-name-pattern-required" : "Le modèle de nom du locataire est requis.", + "tenant-name-pattern-max-length" : "Le modèle de nom du locataire doit contenir moins de 256 caractères", + "tenant-name-strategy" : "Stratégie de nom de locataire", + "type" : "Type de mappeur", + "uri-pattern-error" : "Format d'URI invalide.", + "url" : "URL", + "url-pattern" : "Format d'URL invalide.", + "url-required" : "L'URL est requise.", + "url-max-length" : "L'URL doit contenir moins de 256 caractères", + "user-info-uri" : "URI des informations utilisateur", + "user-info-uri-required" : "L'URI des informations utilisateur est requise.", + "username-max-length" : "Le nom d'utilisateur doit contenir moins de 256 caractères", + "user-name-attribute-name" : "Clé de l'attribut nom d'utilisateur", + "user-name-attribute-name-required" : "La clé de l'attribut nom d'utilisateur est requise", + "protocol" : "Protocole", + "domain-schema-http" : "HTTP", + "domain-schema-https" : "HTTPS", + "domain-schema-mixed" : "HTTP+HTTPS", + "enable" : "Activer les paramètres OAuth 2.0", + "disable" : "Désactiver les paramètres OAuth 2.0", + "edge" : "Propager vers Edge", + "edge-enable" : "Activer la propagation vers Edge", + "edge-disable" : "Désactiver la propagation vers Edge", + "domains" : "Domaines", + "mobile-apps" : "Applications mobiles", + "mobile-package" : "Package de l'application", + "mobile-package-placeholder" : "Ex. : my.example.app", + "mobile-package-hint" : "Pour Android : votre identifiant d'application unique. Pour iOS : identifiant du bundle du produit.", + "mobile-package-unique" : "Le package de l'application doit être unique.", + "mobile-package-required" : "Le package de l'application est requis.", + "mobile-package-max-length" : "Le package de l'application doit contenir moins de 256 caractères", + "mobile-package-spaces" : "Le package de l'application ne doit pas contenir d'espaces", + "mobile-app-secret" : "Secret de l'application", + "mobile-app-secret-hint" : "Chaîne encodée en base64 représentant au moins 512 bits de données.", + "mobile-app-secret-required" : "Le secret de l'application est requis.", + "mobile-app-secret-min-length" : "Le secret de l'application doit contenir au moins 512 bits de données.", + "mobile-app-secret-base64" : "Le secret de l'application doit être au format base64.", + "invalid-mobile-app-secret" : "Le secret de l'application doit contenir uniquement des caractères alphanumériques et mesurer entre 16 et 2048 caractères.", + "copy-mobile-app-secret" : "Copier le secret de l'application", + "delete-mobile-app" : "Supprimer les informations de l'application", + "providers" : "Fournisseurs", + "platform-web" : "Web", + "platform-android" : "Android", + "platform-ios" : "iOS", + "all-platforms" : "Toutes les plateformes", + "smtp-provider" : "Fournisseur SMTP", + "allowed-platforms" : "Plateformes autorisées", + "authentication" : "Authentification", + "basic" : "Basique", + "provider" : "Fournisseur", + "redirect-url" : "URI de redirection", + "domain-name" : "Nom de domaine", + "domain-name-required" : "Le nom de domaine est requis", + "redirect-url-template" : "Modèle d'URI de redirection", + "microsoft-tenant-id" : "ID du répertoire (locataire)", + "microsoft-tenant-id-required" : "L'ID du répertoire (locataire) est requis", + "token-uri" : "URI du jeton", + "token-uri-required" : "L'URI du jeton est requis", + "redirect-uri" : "URI de redirection", + "google-provider" : "Google", + "microsoft-provider" : "Office 365", + "sendgrid-provider" : "Sendgrid", + "custom-provider" : "Personnalisé", + "generate-access-token" : "Générer le jeton d'accès", + "update-access-token" : "Mettre à jour le jeton d'accès", + "access-token-status" : "Statut du jeton d'accès :", + "token-status-generated" : "généré", + "token-status-not-generated" : "non généré" + }, + "smpp-provider" : { + "smpp-version" : "Version SMPP", + "smpp-host" : "Hôte SMPP", + "smpp-host-required" : "L'hôte SMPP est requis", + "smpp-port" : "Port SMPP", + "smpp-port-required" : "Le port SMPP est requis", + "system-id" : "ID système", + "system-id-required" : "L'ID système est requis", + "password" : "Mot de passe", + "password-required" : "Le mot de passe est requis", + "type-settings" : "Paramètres de type", + "source-settings" : "Paramètres source", + "destination-settings" : "Paramètres de destination", + "additional-settings" : "Paramètres supplémentaires", + "system-type" : "Type de système", + "bind-type" : "Type de liaison", + "service-type" : "Type de service", + "source-address" : "Adresse source", + "source-ton" : "TON source", + "source-npi" : "NPI source", + "destination-ton" : "TON de destination (Type de numéro)", + "destination-npi" : "NPI de destination (Identification du plan de numérotation)", + "address-range" : "Plage d'adresses", + "coding-scheme" : "Schéma de codage", + "bind-type-tx" : "Émetteur", + "bind-type-rx" : "Récepteur", + "bind-type-trx" : "Émetteur-récepteur", + "ton-unknown" : "Inconnu", + "ton-international" : "International", + "ton-national" : "National", + "ton-network-specific" : "Spécifique au réseau", + "ton-subscriber-number" : "Numéro d'abonné", + "ton-alphanumeric" : "Alphanumérique", + "ton-abbreviated" : "Abrégé", + "npi-unknown" : "0 - Inconnu", + "npi-isdn" : "1 - Plan de numérotation ISDN/téléphonique (E163/E164)", + "npi-data-numbering-plan" : "3 - Plan de numérotation des données (X.121)", + "npi-telex-numbering-plan" : "4 - Plan de numérotation Télex (F.69)", + "npi-land-mobile" : "6 - Réseau mobile terrestre (E.212)", + "npi-national-numbering-plan" : "8 - Plan de numérotation national", + "npi-private-numbering-plan" : "9 - Plan de numérotation privé", + "npi-ermes-numbering-plan" : "10 - Plan de numérotation ERMES (ETSI DE/PS 3 01-3)", + "npi-internet" : "13 - Internet (IP)", + "npi-wap-client-id" : "18 - Identifiant client WAP (défini par le WAP Forum)", + "scheme-smsc" : "0 - Alphabet par défaut du SMSC (ASCII pour code court/long, GSM pour numéro vert)", + "scheme-ia5" : "1 - IA5 (ASCII pour code court/long, Latin 9 pour numéro vert (ISO-8859-9))", + "scheme-octet-unspecified-2" : "2 - Octet non spécifié (binaire 8 bits)", + "scheme-latin-1" : "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4" : "4 - Octet non spécifié (binaire 8 bits)", + "scheme-jis" : "5 - JIS (X 0208-1990)", + "scheme-cyrillic" : "6 - Cyrillique (ISO-8859-5)", + "scheme-latin-hebrew" : "7 - Latin/Hébreu (ISO-8859-8)", + "scheme-ucs-utf" : "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding" : "9 - Encodage pictogramme", + "scheme-music-codes" : "10 - Codes musicaux (ISO-2022-JP)", + "scheme-extended-kanji-jis" : "13 - Kanji JIS étendu (X 0212-1990)", + "scheme-korean-graphic-character-set" : "14 - Jeu de caractères graphiques coréens (KS C 5601/KS X 1001)" + }, + "queue-select-name" : "Sélectionner le nom de la file", + "queue-name" : "Nom", + "queue-name-required" : "Le nom de la file est requis !", + "queues" : "Files", + "queue-partitions" : "Partitions", + "queue-submit-strategy" : "Stratégie de soumission", + "queue-processing-strategy" : "Stratégie de traitement", + "queue-configuration" : "Configuration de la file", + "repository-settings" : "Paramètres du dépôt", + "repository" : "Dépôt", + "repository-url" : "URL du dépôt", + "repository-url-required" : "L'URL du dépôt est requise.", + "default-branch" : "Nom de la branche par défaut", + "repository-read-only" : "Lecture seule", + "show-merge-commits" : "Afficher les commits de fusion", + "authentication-settings" : "Paramètres d'authentification", + "auth-method" : "Méthode d'authentification", + "auth-method-username-password" : "Mot de passe / jeton d'accès", + "auth-method-username-password-hint" : "Les utilisateurs GitHub doivent utiliser des jetons avec des permissions d'écriture pour le dépôt.", + "auth-method-private-key" : "Clé privée", + "password-access-token" : "Mot de passe / jeton d'accès", + "change-password-access-token" : "Modifier le mot de passe / jeton d'accès", + "private-key" : "Clé privée", + "drop-private-key-file-or" : "Glissez-déposez un fichier de clé privée ou", + "passphrase" : "Phrase secrète", + "enter-passphrase" : "Entrer la phrase secrète", + "change-passphrase" : "Changer la phrase secrète", + "check-access" : "Vérifier l'accès", + "check-repository-access-success" : "Accès au dépôt vérifié avec succès !", + "delete-repository-settings-title" : "Êtes-vous sûr de vouloir supprimer les paramètres du dépôt ?", + "delete-repository-settings-text" : "Attention, après confirmation les paramètres du dépôt seront supprimés et la fonctionnalité de contrôle de version ne sera plus disponible.", + "auto-commit-settings" : "Paramètres de commit automatique", + "auto-commit" : "Commit automatique", + "auto-commit-entities" : "Entités de commit automatique", + "no-auto-commit-entities-prompt" : "Aucune entité configurée pour le commit automatique", + "delete-auto-commit-settings-title" : "Êtes-vous sûr de vouloir supprimer les paramètres de commit automatique ?", + "delete-auto-commit-settings-text" : "Attention, après confirmation, les paramètres de commit automatique seront supprimés et la fonctionnalité sera désactivée pour toutes les entités.", + "mobile-app" : { + "mobile-app" : "Application mobile", + "mobile-app-qr-code-widget-settings" : "Paramètres du widget QR code de l'application mobile", + "applications" : "Applications", + "default" : "Par défaut", + "custom" : "Personnalisé", + "android" : "Android", + "ios" : "iOS", + "appearance" : "Apparence", + "appearance-on-home-page" : "Apparence sur la page d'accueil", + "enabled" : "Activé", + "disabled" : "Désactivé", + "badges" : "Badges", + "label" : "Étiquette", + "label-required" : "L'étiquette est requise", + "label-max-length" : "L'étiquette doit contenir au maximum 50 caractères", + "right" : "Droite", + "left" : "Gauche", + "set" : "Définir", + "preview" : "Aperçu", + "connect-mobile-app" : "Connecter l'application mobile", + "use-system-settings" : "Utiliser les paramètres système" + }, + "2fa" : { + "2fa" : "Authentification à deux facteurs", + "available-providers" : "Fournisseurs disponibles", + "issuer-name" : "Nom de l'émetteur", + "issuer-name-required" : "Le nom de l'émetteur est requis.", + "max-verification-failures-before-user-lockout" : "Nombre maximal d'échecs de vérification avant verrouillage de l'utilisateur", + "max-verification-failures-before-user-lockout-pattern" : "Le nombre maximal d'échecs doit être un entier positif.", + "number-of-checking-attempts" : "Nombre de tentatives de vérification", + "number-of-checking-attempts-pattern" : "Le nombre de tentatives doit être un entier positif.", + "number-of-checking-attempts-required" : "Le nombre de tentatives de vérification est requis.", + "number-of-codes" : "Nombre de codes", + "number-of-codes-pattern" : "Le nombre de codes doit être un entier positif.", + "number-of-codes-required" : "Le nombre de codes est requis.", + "provider" : "Fournisseur", + "retry-verification-code-period" : "Délai de réessai du code de vérification (sec)", + "retry-verification-code-period-pattern" : "Le délai minimal est de 5 secondes", + "retry-verification-code-period-required" : "Le délai de réessai est requis.", + "total-allowed-time-for-verification" : "Durée totale autorisée pour la vérification (sec)", + "total-allowed-time-for-verification-pattern" : "La durée minimale autorisée est de 60 secondes", + "total-allowed-time-for-verification-required" : "La durée totale autorisée est requise.", + "use-system-two-factor-auth-settings" : "Utiliser les paramètres système d'authentification à deux facteurs", + "verification-code-check-rate-limit" : "Limite de fréquence de vérification du code", + "verification-code-lifetime" : "Durée de vie du code de vérification (sec)", + "verification-code-lifetime-pattern" : "La durée de vie du code doit être un entier positif.", + "verification-code-lifetime-required" : "La durée de vie du code est requise.", + "verification-message-template" : "Modèle de message de vérification", + "verification-limitations" : "Limitations de la vérification", + "verification-message-template-pattern" : "Le message de vérification doit contenir le modèle : ${code}", + "verification-message-template-required" : "Le modèle de message de vérification est requis.", + "within-time" : "Délai (sec)", + "within-time-pattern" : "Le délai doit être un entier positif.", + "within-time-required" : "Le délai est requis." + }, + "jwt" : { + "security-settings" : "Paramètres de sécurité JWT", + "issuer-name" : "Nom de l'émetteur", + "issuer-name-required" : "Le nom de l'émetteur est requis.", + "signings-key" : "Clé de signature", + "signings-key-hint" : "Chaîne encodée en base64 représentant au moins 512 bits de données.", + "signings-key-required" : "La clé de signature est requise.", + "signings-key-min-length" : "La clé de signature doit contenir au moins 512 bits de données.", + "signings-key-base64" : "La clé de signature doit être au format base64.", + "expiration-time" : "Durée de validité du jeton (sec)", + "expiration-time-required" : "La durée de validité du jeton est requise.", + "expiration-time-max" : "La durée maximale autorisée est de 2147483647 secondes (68 ans).", + "expiration-time-min" : "Le temps minimum est de 60 secondes (1 minute).", + "refresh-expiration-time" : "Durée de validité du jeton de rafraîchissement (sec)", + "refresh-expiration-time-required" : "La durée de validité du jeton de rafraîchissement est requise.", + "refresh-expiration-time-max" : "La durée maximale autorisée est de 2147483647 secondes (68 ans).", + "refresh-expiration-time-min" : "Le temps minimum est de 900 secondes (15 minutes).", + "refresh-expiration-time-less-token" : "Le temps du jeton de rafraîchissement doit être supérieur à celui du jeton principal.", + "generate-key" : "Générer une clé", + "info-header" : "Tous les utilisateurs devront se reconnecter", + "info-message" : "La modification de la clé de signature JWT invalidera tous les jetons émis. Tous les utilisateurs devront se reconnecter. Cela affectera également les scripts utilisant l'API REST/Websockets." + }, + "resources" : "Ressources", + "notifications" : "Notifications", + "notifications-settings" : "Paramètres des notifications", + "slack-api-token" : "Jeton API Slack", + "slack" : "Slack", + "slack-settings" : "Paramètres Slack", + "mobile-settings" : "Paramètres mobiles", + "firebase-service-account-file" : "Fichier JSON des identifiants du compte de service Firebase", + "select-firebase-service-account-file" : "Glissez-déposez votre fichier d'identifiants de compte de service Firebase ou " + }, + "alarm" : { + "alarm" : "Alarme", + "alarms" : "Alarmes", + "all-alarms" : "Toutes les alarmes", + "select-alarm" : "Sélectionner une alarme", + "no-alarms-matching" : "Aucune alarme correspondant à '{{entity}}' n'a été trouvée.", + "alarm-required" : "L'alarme est requise", + "alarm-filter" : "Filtre d'alarme", + "filter" : "Filtre", + "alarm-status" : "Statut de l'alarme", + "alarm-status-list" : "Liste des statuts d'alarme", + "any-status" : "N'importe quel statut", + "search-status" : { + "ANY" : "N'importe lequel", + "ACTIVE" : "Active", + "CLEARED" : "Réinitialisée", + "ACK" : "Reconnu", + "UNACK" : "Non reconnu" + }, + "display-status" : { + "ACTIVE_UNACK" : "Active non reconnue", + "ACTIVE_ACK" : "Active reconnue", + "CLEARED_UNACK" : "Réinitialisée non reconnue", + "CLEARED_ACK" : "Réinitialisée reconnue" + }, + "no-alarms-prompt" : "Aucune alarme trouvée", + "created-time" : "Heure de création", + "type" : "Type", + "severity" : "Gravité", + "originator" : "Initiateur", + "originator-type" : "Type d'initiateur", + "details" : "Détails", + "originator-label" : "Étiquette de l'initiateur", + "assign" : "Assigner", + "assignments" : "Affectations", + "assignee" : "Assigné", + "assignee-id" : "ID de l'assigné", + "assignee-first-name" : "Prénom de l'assigné", + "assignee-last-name" : "Nom de l'assigné", + "assignee-email" : "Email de l'assigné", + "unassigned" : "Non assigné", + "user-deleted" : "Utilisateur supprimé", + "assignee-not-set" : "Tous", + "status" : "Statut", + "alarm-details" : "Détails de l'alarme", + "start-time" : "Heure de début", + "assign-time" : "Heure d'assignation", + "end-time" : "Heure de fin", + "ack-time" : "Heure de reconnaissance", + "clear-time" : "Heure de réinitialisation", + "duration" : "Durée", + "alarm-severity" : "Gravité de l'alarme", + "alarm-severity-list" : "Liste des niveaux de gravité", + "any-severity" : "N'importe quelle gravité", + "severity-critical" : "Critique", + "severity-major" : "Majeure", + "severity-minor" : "Mineure", + "severity-warning" : "Avertissement", + "severity-indeterminate" : "Indéterminée", + "acknowledge" : "Reconnaître", + "clear" : "Réinitialiser", + "delete" : "Supprimer", + "search" : "Rechercher des alarmes", + "selected-alarms" : "{ count, plural, =1 {1 alarme} other {# alarmes} } sélectionnée(s)", + "no-data" : "Aucune donnée à afficher", + "polling-interval" : "Intervalle de sondage des alarmes (sec)", + "polling-interval-required" : "L'intervalle de sondage des alarmes est requis.", + "min-polling-interval-message" : "L'intervalle minimum autorisé est de 1 seconde.", + "aknowledge-alarms-title" : "Reconnaître { count, plural, =1 {1 alarme} other {# alarmes} }", + "aknowledge-alarms-text" : "Êtes-vous sûr de vouloir reconnaître { count, plural, =1 {1 alarme} other {# alarmes} } ?", + "aknowledge-alarm-title" : "Reconnaître l'alarme", + "aknowledge-alarm-text" : "Êtes-vous sûr de vouloir reconnaître l'alarme ?", + "selected-alarms-are-acknowledged" : "Les alarmes sélectionnées sont déjà reconnues", + "clear-alarms-title" : "Réinitialiser { count, plural, =1 {1 alarme} other {# alarmes} }", + "clear-alarms-text" : "Êtes-vous sûr de vouloir réinitialiser { count, plural, =1 {1 alarme} other {# alarmes} } ?", + "clear-alarm-title" : "Réinitialiser l'alarme", + "clear-alarm-text" : "Êtes-vous sûr de vouloir réinitialiser l'alarme ?", + "delete-alarms-title" : "Supprimer { count, plural, =1 {1 alarme} other {# alarmes} }", + "delete-alarms-text" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 alarme} other {# alarmes} } ?", + "selected-alarms-are-cleared" : "Les alarmes sélectionnées sont déjà réinitialisées", + "alarm-status-filter" : "Filtre de statut d'alarme", + "alarm-filter-title" : "Filtre d'alarme", + "assigned" : "Assigné", + "filter-title" : "Filtre", + "max-count-load" : "Nombre maximum d'alarmes à charger (0 - illimité)", + "max-count-load-required" : "Le nombre maximum d'alarmes à charger est requis.", + "max-count-load-error-min" : "La valeur minimale est 0.", + "fetch-size" : "Taille de récupération", + "fetch-size-required" : "La taille de récupération est requise.", + "fetch-size-error-min" : "La valeur minimale est 10.", + "alarm-types" : "Types d'alarmes", + "alarm-type-list" : "Liste des types d'alarmes", + "any-type" : "N'importe quel type", + "assigned-to-current-user" : "Assigné à l'utilisateur actuel", + "assigned-to-me" : "Assigné à moi", + "search-propagated-alarms" : "Rechercher les alarmes propagées", + "comments" : "Commentaires de l'alarme", + "show-more" : "Afficher plus", + "additional-info" : "Informations supplémentaires", + "alarm-type" : "Type d'alarme", + "enter-alarm-type" : "Entrer le type d'alarme", + "no-alarm-types-matching" : "Aucun type d'alarme correspondant à '{{entitySubtype}}' n'a été trouvé.", + "alarm-type-list-empty" : "Aucun type d'alarme sélectionné." + }, + "alarm-activity" : { + "add" : "Ajouter un commentaire...", + "alarm-comment" : "Commentaire d'alarme", + "comments" : "Commentaires", + "delete-alarm-comment" : "Voulez-vous supprimer ce commentaire ?", + "refresh" : "Rafraîchir", + "oldest-first" : "Le plus ancien en premier", + "newest-first" : "Le plus récent en premier", + "activity" : "Activité", + "export" : "Exporter au format CSV", + "author" : "Auteur", + "created-date" : "Date de création", + "edited-date" : "Date de modification", + "text" : "Texte", + "system" : "Système" + }, + "alias" : { + "add" : "Ajouter un alias", + "edit" : "Modifier l'alias", + "name" : "Nom de l'alias", + "name-required" : "Le nom de l'alias est requis", + "duplicate-alias" : "Un alias portant le même nom existe déjà.", + "filter-type-single-entity" : "Entité unique", + "filter-type-entity-list" : "Liste d'entités", + "filter-type-entity-name" : "Nom de l'entité", + "filter-type-entity-type" : "Type d'entité", + "filter-type-state-entity" : "Entité à partir de l'état du tableau de bord", + "filter-type-state-entity-description" : "Entité extraite des paramètres d'état du tableau de bord", + "filter-type-asset-type" : "Type d'actif", + "filter-type-asset-type-description" : "Actifs de type '{{assetTypes}}'", + "filter-type-asset-type-and-name-description" : "Actifs de type '{{assetTypes}}' dont le nom commence par '{{prefix}}'", + "filter-type-device-type" : "Type d'appareil", + "filter-type-device-type-description" : "Appareils de type '{{deviceTypes}}'", + "filter-type-device-type-and-name-description" : "Appareils de type '{{deviceTypes}}' dont le nom commence par '{{prefix}}'", + "filter-type-entity-view-type" : "Type de vue d'entité", + "filter-type-entity-view-type-description" : "Vues d'entité de type '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description" : "Vues d'entité de type '{{entityViewTypes}}' dont le nom commence par '{{prefix}}'", + "filter-type-edge-type" : "Type d'Edge", + "filter-type-edge-type-description" : "Edges de type '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description" : "Edges de type '{{edgeTypes}}' dont le nom commence par '{{prefix}}'", + "filter-type-relations-query" : "Requête de relations", + "filter-type-relations-query-description" : "{{entities}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-edge-search-query" : "Requête de recherche d'Edge", + "filter-type-edge-search-query-description" : "Edges de types {{edgeTypes}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query" : "Requête de recherche d'actifs", + "filter-type-asset-search-query-description" : "Actifs de types {{assetTypes}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query" : "Requête de recherche d'appareils", + "filter-type-device-search-query-description" : "Appareils de types {{deviceTypes}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query" : "Requête de recherche de vue d'entité", + "filter-type-entity-view-search-query-description" : "Vues d'entité de types {{entityViewTypes}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState" : "État d'utilisation de l'API", + "entity-filter" : "Filtre d'entité", + "resolve-multiple" : "Résoudre en tant que plusieurs entités", + "resolve-multiple-hint" : "Activer pour afficher les données de toutes les entités filtrées simultanément.\nSi désactivé, le widget affiche uniquement les données de l'entité sélectionnée.", + "filter-type" : "Type de filtre", + "filter-type-required" : "Le type de filtre est requis.", + "entity-filter-no-entity-matched" : "Aucune entité ne correspond au filtre spécifié.", + "no-entity-filter-specified" : "Aucun filtre d'entité spécifié", + "root-state-entity" : "Utiliser l'entité d'état du tableau de bord comme racine", + "last-level-relation" : "Ne récupérer que le dernier niveau de relation", + "root-entity" : "Entité racine", + "state-entity-parameter-name" : "Nom du paramètre d'entité d'état", + "default-state-entity" : "Entité d'état par défaut", + "default-entity-parameter-name" : "Par défaut", + "max-relation-level" : "Niveau maximum de relation", + "unlimited-level" : "Niveau illimité", + "state-entity" : "Entité d'état du tableau de bord", + "all-entities" : "Toutes les entités", + "any-relation" : "n'importe laquelle" + }, + "asset" : { + "asset" : "Actif", + "assets" : "Actifs", + "management" : "Gestion des actifs", + "view-assets" : "Voir les actifs", + "add" : "Ajouter un actif", + "asset-type-max-length" : "Le type d'actif doit contenir moins de 256 caractères", + "assign-to-customer" : "Assigner à un client", + "assign-asset-to-customer" : "Assigner des actifs à un client", + "assign-asset-to-customer-text" : "Veuillez sélectionner les actifs à assigner au client", + "no-assets-text" : "Aucun actif trouvé", + "assign-to-customer-text" : "Veuillez sélectionner le client auquel assigner les actifs", + "public" : "Public", + "assignedToCustomer" : "Assigné au client", + "make-public" : "Rendre l'actif public", + "make-private" : "Rendre l'actif privé", + "unassign-from-customer" : "Désassigner du client", + "delete" : "Supprimer l'actif", + "asset-public" : "L'actif est public", + "asset-type" : "Type d'actif", + "asset-type-required" : "Le type d'actif est requis.", + "select-asset-type" : "Sélectionner le type d'actif", + "enter-asset-type" : "Saisir le profil d'actif", + "any-asset" : "N'importe quel actif", + "no-asset-types-matching" : "Aucun type d'actif correspondant à '{{entitySubtype}}' trouvé.", + "asset-type-list-empty" : "Aucun type d'actif sélectionné.", + "asset-types" : "Types d'actifs", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "name-max-length" : "Le nom doit contenir moins de 256 caractères", + "label-max-length" : "L'étiquette doit contenir moins de 256 caractères", + "description" : "Description", + "type" : "Type", + "type-required" : "Le type est requis.", + "details" : "Détails", + "events" : "Événements", + "add-asset-text" : "Ajouter un nouvel actif", + "asset-details" : "Détails de l'actif", + "assign-assets" : "Assigner des actifs", + "assign-assets-text" : "Assigner { count, plural, =1 {1 actif} other {# actifs} } à un client", + "assign-asset-to-edge-title" : "Assigner des actifs à une Edge", + "assign-asset-to-edge-text" : "Veuillez sélectionner les actifs à assigner à la Edge", + "delete-assets" : "Supprimer des actifs", + "unassign-assets" : "Désassigner des actifs", + "unassign-assets-action-title" : "Désassigner { count, plural, =1 {1 actif} other {# actifs} } du client", + "assign-new-asset" : "Assigner un nouvel actif", + "delete-asset-title" : "Êtes-vous sûr de vouloir supprimer l'actif '{{assetName}}' ?", + "delete-asset-text" : "Attention, après confirmation l'actif et toutes les données associées seront irrécupérables.", + "delete-assets-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 actif} other {# actifs} } ?", + "delete-assets-action-title" : "Supprimer { count, plural, =1 {1 actif} other {# actifs} }", + "delete-assets-text" : "Attention, après confirmation tous les actifs sélectionnés seront supprimés ainsi que toutes les données associées.", + "make-public-asset-title" : "Êtes-vous sûr de vouloir rendre l'actif '{{assetName}}' public ?", + "make-public-asset-text" : "Après confirmation, l'actif et toutes ses données seront rendus publics et accessibles à d'autres.", + "make-private-asset-title" : "Êtes-vous sûr de vouloir rendre l'actif '{{assetName}}' privé ?", + "make-private-asset-text" : "Après confirmation, l'actif et toutes ses données seront rendus privés et ne seront plus accessibles à d'autres.", + "unassign-asset-title" : "Êtes-vous sûr de vouloir désassigner l'actif '{{assetName}}' ?", + "unassign-asset-text" : "Après confirmation, l'actif sera désassigné et ne sera plus accessible par le client.", + "unassign-asset" : "Désassigner l'actif", + "unassign-assets-title" : "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 actif} other {# actifs} } ?", + "unassign-assets-text" : "Après confirmation, tous les actifs sélectionnés seront désassignés et ne seront plus accessibles par le client.", + "copyId" : "Copier l'identifiant de l'actif", + "idCopiedMessage" : "L'identifiant de l'actif a été copié dans le presse-papiers", + "select-asset" : "Sélectionner un actif", + "no-assets-matching" : "Aucun actif correspondant à '{{entity}}' trouvé.", + "asset-required" : "L'actif est requis", + "name-starts-with" : "Expression du nom de l'actif", + "help-text" : "Utilisez '%' selon le besoin : '%nom_actif_contient%', '%nom_actif_termine', 'actif_commence_par'.", + "search" : "Rechercher des actifs", + "import" : "Importer des actifs", + "asset-file" : "Fichier d'actifs", + "label" : "Étiquette", + "assign-asset-to-edge" : "Assigner des actifs à une Edge", + "unassign-asset-from-edge" : "Désassigner l'actif", + "unassign-asset-from-edge-title" : "Êtes-vous sûr de vouloir désassigner l'actif '{{assetName}}' ?", + "unassign-asset-from-edge-text" : "Après confirmation, l'actif sera désassigné et ne sera plus accessible par la Edge.", + "unassign-assets-from-edge-title" : "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 actif} other {# actifs} } ?", + "unassign-assets-from-edge-text" : "Après confirmation, tous les actifs sélectionnés seront désassignés et ne seront plus accessibles par la Edge.", + "selected-assets" : "{ count, plural, =1 {1 actif} other {# actifs} } sélectionné(s)" + }, + "attribute" : { + "attributes" : "Attributs", + "latest-telemetry" : "Dernière télémétrie", + "no-latest-telemetry" : "Aucune télémétrie récente", + "attributes-scope" : "Portée des attributs d'entité", + "scope-telemetry" : "Télémétrie", + "scope-latest-telemetry" : "Dernière télémétrie", + "scope-client" : "Attributs client", + "scope-server" : "Attributs serveur", + "scope-shared" : "Attributs partagés", + "scope-client-short" : "Client", + "scope-server-short" : "Serveur", + "scope-shared-short" : "Partagé", + "scope-latest-short" : "Dernier", + "scope-any" : "N'importe quel", + "add" : "Ajouter un attribut", + "key" : "Clé", + "key-max-length" : "La clé doit contenir moins de 256 caractères", + "last-update-time" : "Dernière mise à jour", + "key-required" : "La clé de l'attribut est requise.", + "value" : "Valeur", + "value-required" : "La valeur de l'attribut est requise.", + "telemetry-key-required" : "La clé de télémétrie est requise", + "telemetry-value-required" : "La valeur de télémétrie est requise", + "delete-attributes-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 attribut} other {# attributs} } ?", + "delete-attributes-text" : "Attention, après confirmation, tous les attributs sélectionnés seront supprimés.", + "delete-attributes" : "Supprimer les attributs", + "enter-attribute-value" : "Entrer la valeur de l'attribut", + "show-on-widget" : "Afficher sur le widget", + "widget-mode" : "Mode widget", + "next-widget" : "Widget suivant", + "prev-widget" : "Widget précédent", + "add-to-dashboard" : "Ajouter au tableau de bord", + "add-widget-to-dashboard" : "Ajouter le widget au tableau de bord", + "selected-attributes" : "{ count, plural, =1 {1 attribut} other {# attributs} } sélectionné(s)", + "selected-telemetry" : "{ count, plural, =1 {1 unité de télémétrie} other {# unités de télémétrie} } sélectionnée(s)", + "no-attributes-text" : "Aucun attribut trouvé", + "no-telemetry-text" : "Aucune télémétrie trouvée", + "copy-key" : "Copier la clé", + "add-telemetry" : "Ajouter une télémétrie", + "copy-value" : "Copier la valeur", + "delete-timeseries" : { + "start-time" : "Heure de début", + "ends-on" : "Se termine le", + "strategy" : "Stratégie", + "delete-strategy" : "Stratégie de suppression", + "all-data" : "Supprimer toutes les données", + "all-data-except-latest-value" : "Supprimer toutes les données sauf la dernière valeur", + "latest-value" : "Supprimer la dernière valeur", + "all-data-for-time-period" : "Supprimer toutes les données pour une période donnée", + "rewrite-latest-value" : "Réécrire la dernière valeur" + } + }, + "api-usage" : { + "api-features" : "Fonctionnalités de l'API", + "api-usage" : "Utilisation de l'API", + "alarm" : "Alarme", + "alarms-created" : "Alarmes créées", + "queue-stats" : "Statistiques de file", + "processing-failures-and-timeouts" : "Échecs de traitement et expirations", + "exceptions" : "Exceptions", + "alarms-created-daily-activity" : "Activité quotidienne des alarmes créées", + "alarms-created-hourly-activity" : "Activité horaire des alarmes créées", + "alarms-created-monthly-activity" : "Activité mensuelle des alarmes créées", + "data-points" : "Points de données", + "data-points-storage-days" : "Durée de stockage des points de données (jours)", + "device-api" : "API des appareils", + "email" : "Email", + "email-messages" : "Messages email", + "email-messages-daily-activity" : "Activité quotidienne des emails", + "email-messages-monthly-activity" : "Activité mensuelle des emails", + "executions" : "Exécutions", + "scripts" : "Scripts", + "scripts-hourly-activity" : "Activité horaire des scripts", + "scripts-daily-activity" : "Activité quotidienne des scripts", + "scripts-monthly-activity" : "Activité mensuelle des scripts", + "javascript" : "JavaScript", + "javascript-executions" : "Exécutions JavaScript", + "tbel" : "TBEL", + "tbel-executions" : "Exécutions TBEL", + "latest-error" : "Dernière erreur", + "messages" : "Messages", + "notifications" : "Notifications", + "notifications-email-sms" : "Notifications (Email/SMS)", + "notifications-hourly-activity" : "Activité horaire des notifications", + "permanent-failures" : "Échecs permanents de ${entityName}", + "permanent-timeouts" : "Expirations permanentes de ${entityName}", + "processing-failures" : "Échecs de traitement de ${entityName}", + "processing-timeouts" : "Expirations de traitement de ${entityName}", + "rule-chain" : "Chaîne de règles", + "rule-engine" : "Moteur de règles", + "rule-engine-daily-activity" : "Activité quotidienne du moteur de règles", + "rule-engine-executions" : "Exécutions du moteur de règles", + "rule-engine-hourly-activity" : "Activité horaire du moteur de règles", + "rule-engine-monthly-activity" : "Activité mensuelle du moteur de règles", + "rule-engine-statistics" : "Statistiques du moteur de règles", + "rule-node" : "Nœud de règle", + "sms" : "SMS", + "sms-messages" : "Messages SMS", + "sms-messages-daily-activity" : "Activité quotidienne des SMS", + "sms-messages-monthly-activity" : "Activité mensuelle des SMS", + "successful" : "${entityName} Réussis", + "telemetry" : "Télémétrie", + "telemetry-persistence" : "Persistance de la télémétrie", + "telemetry-persistence-daily-activity" : "Activité quotidienne de persistance", + "telemetry-persistence-hourly-activity" : "Activité horaire de persistance", + "telemetry-persistence-monthly-activity" : "Activité mensuelle de persistance", + "transport" : "Transport", + "transport-daily-activity" : "Activité quotidienne de transport", + "transport-data-points" : "Points de données de transport", + "transport-hourly-activity" : "Activité horaire de transport", + "transport-messages" : "Messages de transport", + "transport-monthly-activity" : "Activité mensuelle de transport", + "view-details" : "Voir les détails", + "view-statistics" : "Voir les statistiques" + }, + "api-limit" : { + "cassandra-queries" : "Requêtes Cassandra", + "entity-version-creation" : "Création de version d'entité", + "entity-version-load" : "Chargement de version d'entité", + "notification-requests" : "Requêtes de notification", + "notification-requests-per-rule" : "Requêtes de notification par règle", + "rest-api-requests" : "Requêtes REST API", + "rest-api-requests-per-customer" : "Requêtes REST API par client", + "transport-messages" : "Messages de transport", + "transport-messages-per-device" : "Messages de transport par appareil", + "transport-messages-per-gateway" : "Messages de transport par passerelle", + "transport-messages-per-gateway-device" : "Messages de transport par appareil de passerelle", + "ws-updates-per-session" : "Mises à jour WS par session", + "edge-events" : "Événements Edge", + "edge-events-per-edge" : "Événements Edge par instance", + "edge-uplink-messages" : "Messages montants Edge", + "edge-uplink-messages-per-edge" : "Messages montants Edge par instance" + }, + "audit-log" : { + "audit" : "Audit", + "audit-logs" : "Journaux d'audit", + "timestamp" : "Horodatage", + "entity-type" : "Type d'entité", + "entity-name" : "Nom de l'entité", + "user" : "Utilisateur", + "type" : "Type", + "status" : "Statut", + "details" : "Détails", + "type-added" : "Ajouté", + "type-deleted" : "Supprimé", + "type-updated" : "Mis à jour", + "type-attributes-updated" : "Attributs mis à jour", + "type-attributes-deleted" : "Attributs supprimés", + "type-rpc-call" : "Appel RPC", + "type-credentials-updated" : "Identifiants mis à jour", + "type-assigned-to-customer" : "Assigné à un client", + "type-unassigned-from-customer" : "Désassigné d'un client", + "type-assigned-to-edge" : "Assigné à une Edge", + "type-unassigned-from-edge" : "Désassigné d'une Edge", + "type-activated" : "Activé", + "type-suspended" : "Suspendu", + "type-credentials-read" : "Identifiants lus", + "type-attributes-read" : "Attributs lus", + "type-relation-add-or-update" : "Relation mise à jour", + "type-relation-delete" : "Relation supprimée", + "type-relations-delete" : "Toutes les relations supprimées", + "type-alarm-ack" : "Alarme reconnue", + "type-alarm-clear" : "Alarme réinitialisée", + "type-alarm-delete" : "Alarme supprimée", + "type-alarm-assign" : "Alarme assignée", + "type-alarm-unassign" : "Alarme désassignée", + "type-added-comment" : "Commentaire ajouté", + "type-updated-comment" : "Commentaire mis à jour", + "type-deleted-comment" : "Commentaire supprimé", + "type-login" : "Connexion", + "type-logout" : "Déconnexion", + "type-lockout" : "Verrouillage", + "status-success" : "Succès", + "status-failure" : "Échec", + "audit-log-details" : "Détails du journal d'audit", + "no-audit-logs-prompt" : "Aucun journal trouvé", + "action-data" : "Données d'action", + "failure-details" : "Détails de l'échec", + "search" : "Rechercher dans les journaux d'audit", + "clear-search" : "Effacer la recherche", + "type-assigned-from-tenant" : "Assigné depuis un tenant", + "type-assigned-to-tenant" : "Assigné à un tenant", + "type-provision-success" : "Appareil provisionné", + "type-provision-failure" : "Le provisionnement de l'appareil a échoué", + "type-timeseries-updated" : "Télémétrie mise à jour", + "type-timeseries-deleted" : "Télémétrie supprimée", + "type-sms-sent" : "SMS envoyé" + }, + "debug-settings" : { + "label" : "Configuration de débogage", + "on-failure" : "Échecs uniquement (24/7)", + "all-messages" : "Tous les messages ({{time}})", + "failures" : "Échecs", + "entity" : "entité", + "hint" : { + "main-limited" : "Pas plus de {{msg}} messages de débogage de {{entity}} par {{time}} ne seront enregistrés.", + "on-failure" : "Enregistrer uniquement les messages d'erreur.", + "all-messages" : "Enregistrer tous les messages de débogage." + } + }, + "calculated-fields" : { + "expression" : "Expression", + "no-found" : "Aucun champ calculé trouvé", + "list" : "{ count, plural, =1 {Un champ calculé} other {Liste de # champs calculés} }", + "selected-fields" : "{ count, plural, =1 {1 champ calculé} other {# champs calculés} } sélectionné(s)", + "type" : { + "simple" : "Simple", + "script" : "Script" + }, + "arguments" : "Arguments", + "decimals-by-default" : "Décimales par défaut", + "debugging" : "Débogage du champ calculé", + "argument-name" : "Nom de l'argument", + "datasource" : "Source de données", + "add-argument" : "Ajouter un argument", + "test-script-function" : "Tester la fonction script", + "no-arguments" : "Aucun argument configuré", + "argument-settings" : "Paramètres de l'argument", + "argument-current" : "Entité actuelle", + "argument-current-tenant" : "Tenant actuel", + "argument-device" : "Appareil", + "argument-asset" : "Actif", + "argument-customer" : "Client", + "argument-tenant" : "Tenant actuel", + "argument-type" : "Type d'argument", + "see-debug-events" : "Voir les événements de débogage", + "attribute" : "Attribut", + "copy-argument-name" : "Copier le nom de l'argument", + "timeseries-key" : "Clé de série temporelle", + "device-name" : "Nom de l'appareil", + "latest-telemetry" : "Dernière télémétrie", + "rolling" : "Rolling des séries temporelles", + "attribute-scope" : "Portée de l'attribut", + "server-attributes" : "Attributs serveur", + "client-attributes" : "Attributs client", + "shared-attributes" : "Attributs partagés", + "attribute-key" : "Clé d'attribut", + "default-value" : "Valeur par défaut", + "limit" : "Valeurs max", + "time-window" : "Fenêtre temporelle", + "customer-name" : "Nom du client", + "asset-name" : "Nom de l'actif", + "timeseries" : "Séries temporelles", + "output" : "Résultat", + "create" : "Créer un nouveau champ calculé", + "file" : "Fichier de champ calculé", + "invalid-file-error" : "Format de fichier invalide. Veuillez vérifier que le fichier est bien un fichier JSON valide.", + "import" : "Importer un champ calculé", + "export" : "Exporter un champ calculé", + "export-failed-error" : "Impossible d'exporter le champ calculé : {{error}}", + "output-type" : "Type de résultat", + "delete-title" : "Êtes-vous sûr de vouloir supprimer le champ calculé '{{title}}' ?", + "delete-text" : "Attention, après confirmation le champ calculé et toutes les données associées seront irrécupérables.", + "delete-multiple-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 champ calculé} other {# champs calculés} } ?", + "delete-multiple-text" : "Attention, après confirmation tous les champs calculés sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "test-with-this-message" : "Tester avec ce message", + "hint" : { + "arguments-simple-with-rolling" : "Le type simple de champ calculé ne doit pas contenir de clés avec un rolling de séries temporelles.", + "arguments-empty" : "Les arguments ne doivent pas être vides.", + "expression-required" : "L'expression est requise.", + "expression-invalid" : "L'expression est invalide", + "expression-max-length" : "La longueur de l'expression doit être inférieure à 255 caractères.", + "argument-name-required" : "Le nom de l'argument est requis.", + "argument-name-pattern" : "Le nom de l'argument est invalide.", + "argument-name-duplicate" : "Un argument portant ce nom existe déjà.", + "argument-name-max-length" : "Le nom de l'argument doit contenir moins de 256 caractères.", + "argument-name-forbidden" : "Le nom de l'argument est réservé et ne peut pas être utilisé.", + "argument-type-required" : "Le type d'argument est requis.", + "max-args" : "Nombre maximum d'arguments atteint.", + "decimals-range" : "Les décimales par défaut doivent être un nombre entre 0 et 15.", + "expression" : "L'expression par défaut montre comment transformer une température de Fahrenheit en Celsius.", + "arguments-entity-not-found" : "L'entité cible de l'argument est introuvable." + } + }, + "confirm-on-exit" : { + "message" : "Vous avez des modifications non enregistrées. Êtes-vous sûr de vouloir quitter cette page ?", + "html-message" : "Vous avez des modifications non enregistrées.
Êtes-vous sûr de vouloir quitter cette page ?", + "title" : "Modifications non enregistrées" + }, + "contact" : { + "country" : "Pays", + "country-required" : "Le pays est requis.", + "city" : "Ville", + "state" : "État / Province", + "postal-code" : "Code postal / ZIP", + "postal-code-invalid" : "Format de code postal / ZIP invalide.", + "address" : "Adresse", + "address2" : "Adresse 2", + "phone" : "Téléphone", + "email" : "Email", + "no-address" : "Aucune adresse", + "no-country-found" : "Aucun pays trouvé.", + "no-country-matching" : "Aucun pays correspondant à '{{country}}' n'a été trouvé.", + "state-max-length" : "La longueur de l'état doit être inférieure à 256 caractères", + "phone-max-length" : "Le numéro de téléphone doit contenir moins de 256 caractères", + "city-max-length" : "La ville spécifiée doit contenir moins de 256 caractères" + }, + "common" : { + "name" : "Nom", + "type" : "Type", + "general" : "Général", + "username" : "Nom d'utilisateur", + "password" : "Mot de passe", + "data" : "Données", + "timestamp" : "Horodatage", + "enter-username" : "Saisir le nom d'utilisateur", + "enter-password" : "Saisir le mot de passe", + "enter-search" : "Saisir une recherche", + "created-time" : "Date de création", + "disabled" : "Désactivé", + "loading" : "Chargement...", + "proceed" : "Continuer", + "open-details-page" : "Ouvrir la page de détails", + "not-found" : "Introuvable", + "value" : "Valeur", + "documentation" : "Documentation", + "time-left" : "{{time}} restantes", + "output" : "Sortie", + "suffix" : { + "s" : "s", + "ms" : "ms" + }, + "hint" : { + "name-required" : "Le nom est requis.", + "name-pattern" : "Le nom est invalide.", + "name-max-length" : "Le nom doit contenir moins de 256 caractères.", + "title-required" : "Le titre est requis.", + "title-pattern" : "Le titre est invalide.", + "title-max-length" : "Le titre doit contenir moins de 256 caractères.", + "key-required" : "La clé est requise.", + "key-pattern" : "La clé est invalide.", + "key-max-length" : "La clé doit contenir moins de 256 caractères." + }, + "required-fields" : "Champs obligatoires manquants" + }, + "content-type" : { + "json" : "Json", + "text" : "Texte", + "binary" : "Binaire (Base64)" + }, + "color" : { + "color" : "Couleur" + }, + "customer" : { + "customer" : "Client", + "customers" : "Clients", + "management" : "Gestion des clients", + "dashboard" : "Tableau de bord client", + "dashboards" : "Tableaux de bord client", + "devices" : "Appareils client", + "entity-views" : "Vues d'entité client", + "assets" : "Actifs client", + "public-dashboards" : "Tableaux de bord publics", + "public-devices" : "Appareils publics", + "public-assets" : "Actifs publics", + "public-entity-views" : "Vues d'entité publiques", + "add" : "Ajouter un client", + "delete" : "Supprimer le client", + "manage-customer-users" : "Gérer les utilisateurs client", + "manage-customer-devices" : "Gérer les appareils client", + "manage-customer-dashboards" : "Gérer les tableaux de bord client", + "manage-public-devices" : "Gérer les appareils publics", + "manage-public-dashboards" : "Gérer les tableaux de bord publics", + "manage-customer-assets" : "Gérer les actifs client", + "manage-customer-edges" : "Gérer les instances Edge client", + "manage-public-assets" : "Gérer les actifs publics", + "add-customer-text" : "Ajouter un nouveau client", + "no-customers-text" : "Aucun client trouvé", + "customer-details" : "Détails du client", + "delete-customer-title" : "Êtes-vous sûr de vouloir supprimer le client '{{customerTitle}}' ?", + "delete-customer-text" : "Attention, après confirmation, le client et toutes les données associées seront irrécupérables.", + "delete-customers-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 client} other {# clients} } ?", + "delete-customers-action-title" : "Supprimer { count, plural, =1 {1 client} other {# clients} }", + "delete-customers-text" : "Attention, après confirmation, tous les clients sélectionnés seront supprimés ainsi que toutes les données associées.", + "manage-users" : "Gérer les utilisateurs", + "manage-assets" : "Gérer les actifs", + "manage-devices" : "Gérer les appareils", + "manage-dashboards" : "Gérer les tableaux de bord", + "title" : "Titre", + "title-required" : "Le titre est requis.", + "title-max-length" : "Le titre doit contenir moins de 256 caractères", + "description" : "Description", + "details" : "Détails", + "events" : "Événements", + "copyId" : "Copier l'identifiant du client", + "idCopiedMessage" : "Identifiant du client copié dans le presse-papiers", + "select-customer" : "Sélectionner un client", + "no-customers-matching" : "Aucun client correspondant à '{{entity}}' trouvé.", + "customer-required" : "Client requis", + "select-default-customer" : "Sélectionner le client par défaut", + "default-customer" : "Client par défaut", + "default-customer-required" : "Le client par défaut est requis pour déboguer le tableau de bord au niveau du tenant", + "search" : "Rechercher des clients", + "selected-customers" : "{ count, plural, =1 {1 client} other {# clients} } sélectionné(s)", + "edges" : "Instances Edge du client", + "manage-edges" : "Gérer les instances Edge" + }, + "css-size" : { + "size-value-required" : "La valeur de taille est requise", + "invalid-size-value" : "Valeur de taille invalide" + }, + "date" : { + "last-update-n-ago" : "Dernière mise à jour il y a N", + "last-update-n-ago-text" : "Dernière mise à jour {{ agoText }}", + "custom-date" : "Date personnalisée", + "format" : "Format", + "preview" : "Aperçu", + "auto" : "Auto", + "time-granularity-formats" : "Formats de granularité temporelle", + "unit-year" : "Années", + "unit-month" : "Mois", + "unit-day" : "Jours", + "unit-hour" : "Heures", + "unit-minute" : "Minutes", + "unit-second" : "Secondes", + "unit-millisecond" : "Millisecondes" + }, + "datetime" : { + "date-from" : "Date de début", + "time-from" : "Heure de début", + "date-to" : "Date de fin", + "time-to" : "Heure de fin", + "from" : "De", + "to" : "À" + }, + "dashboard" : { + "dashboard" : "Tableau de bord", + "dashboards" : "Tableaux de bord", + "management" : "Gestion des tableaux de bord", + "view-dashboards" : "Voir les tableaux de bord", + "add" : "Ajouter un tableau de bord", + "assign-dashboard-to-customer" : "Assigner le(s) tableau(x) de bord au client", + "assign-dashboard-to-customer-text" : "Veuillez sélectionner les tableaux de bord à assigner au client", + "assign-to-customer-text" : "Veuillez sélectionner le client à qui assigner le(s) tableau(x) de bord", + "assign-to-customer" : "Assigner au client", + "unassign-from-customer" : "Désassigner du client", + "make-public" : "Rendre public le tableau de bord", + "make-private" : "Rendre privé le tableau de bord", + "manage-assigned-customers" : "Gérer les clients assignés", + "assigned-customers" : "Clients assignés", + "assign-to-customers" : "Assigner le(s) tableau(x) de bord aux clients", + "assign-to-customers-text" : "Veuillez sélectionner les clients à qui assigner le(s) tableau(x) de bord", + "unassign-from-customers" : "Désassigner le(s) tableau(x) de bord des clients", + "unassign-from-customers-text" : "Veuillez sélectionner les clients à désassigner du tableau(x) de bord", + "no-dashboards-text" : "Aucun tableau de bord trouvé", + "no-widgets" : "Aucun widget configuré", + "add-widget" : "Ajouter un nouveau widget", + "add-widget-button-text" : "Ajouter un widget", + "title" : "Titre", + "image" : "Image du tableau de bord", + "mobile-app-settings" : "Paramètres de l'application mobile", + "mobile-order" : "Ordre du tableau de bord dans l'application mobile", + "mobile-hide" : "Masquer le tableau de bord dans l'application mobile", + "update-image" : "Mettre à jour l'image du tableau de bord", + "take-screenshot" : "Prendre une capture d’écran", + "select-widget-title" : "Sélectionner un widget", + "select-widget-value" : "{{title}} : sélectionner un widget", + "select-widget-subtitle" : "Liste des types de widgets disponibles", + "delete" : "Supprimer le tableau de bord", + "title-required" : "Le titre est requis.", + "title-max-length" : "Le titre doit contenir moins de 256 caractères", + "description" : "Description", + "details" : "Détails", + "dashboard-details" : "Détails du tableau de bord", + "add-dashboard-text" : "Ajouter un nouveau tableau de bord", + "assign-dashboards" : "Assigner les tableaux de bord", + "assign-new-dashboard" : "Assigner un nouveau tableau de bord", + "assign-dashboards-text" : "Assigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } aux clients", + "unassign-dashboards-action-text" : "Désassigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } des clients", + "delete-dashboards" : "Supprimer les tableaux de bord", + "unassign-dashboards" : "Désassigner les tableaux de bord", + "unassign-dashboards-action-title" : "Désassigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } du client", + "delete-dashboard-title" : "Êtes-vous sûr de vouloir supprimer le tableau de bord '{{dashboardTitle}}' ?", + "delete-dashboard-text" : "Attention, après confirmation, le tableau de bord et toutes les données associées seront irrécupérables.", + "delete-dashboards-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } ?", + "delete-dashboards-action-title" : "Supprimer { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} }", + "delete-dashboards-text" : "Attention, après confirmation, tous les tableaux de bord sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "unassign-dashboard-title" : "Êtes-vous sûr de vouloir désassigner le tableau de bord '{{dashboardTitle}}' ?", + "unassign-dashboard-text" : "Après confirmation, le tableau de bord sera désassigné et ne sera plus accessible par le client.", + "unassign-dashboard" : "Désassigner le tableau de bord", + "unassign-dashboards-title" : "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } ?", + "unassign-dashboards-text" : "Après confirmation, tous les tableaux de bord sélectionnés seront désassignés et ne seront plus accessibles par le client.", + "public-dashboard-title" : "Le tableau de bord est maintenant public", + "public-dashboard-text" : "Votre tableau de bord {{dashboardTitle}} est maintenant public et accessible via le lien suivant :", + "public-dashboard-notice" : "Remarque : N'oubliez pas de rendre publics les appareils concernés afin d'accéder à leurs données.", + "make-private-dashboard-title" : "Êtes-vous sûr de vouloir rendre le tableau de bord '{{dashboardTitle}}' privé ?", + "make-private-dashboard-text" : "Après confirmation, le tableau de bord sera rendu privé et ne sera plus accessible par d'autres.", + "make-private-dashboard" : "Rendre privé le tableau de bord", + "socialshare-text" : "'{{dashboardTitle}}' propulsé par ThingsBoard", + "socialshare-title" : "'{{dashboardTitle}}' propulsé par ThingsBoard", + "select-dashboard" : "Sélectionner un tableau de bord", + "no-dashboards-matching" : "Aucun tableau de bord correspondant à '{{entity}}' trouvé.", + "dashboard-required" : "Le tableau de bord est requis.", + "select-existing" : "Sélectionner un tableau de bord existant", + "create-new" : "Créer un nouveau tableau de bord", + "new-dashboard-title" : "Titre du nouveau tableau de bord", + "open-dashboard" : "Ouvrir le tableau de bord", + "set-background" : "Définir l'arrière-plan", + "background-color" : "Couleur d'arrière-plan", + "background-image" : "Image d'arrière-plan", + "background-size-mode" : "Mode de taille d’arrière-plan", + "no-image" : "Aucune image sélectionnée", + "empty-image" : "Aucune image", + "drop-image" : "Déposez une image ou cliquez pour sélectionner un fichier à téléverser.", + "maximum-upload-file-size" : "Taille maximale de fichier : {{ size }}", + "cannot-upload-file" : "Impossible de téléverser le fichier", + "settings" : "Paramètres", + "move-all-widgets" : "Déplacer tous les widgets", + "move-by" : "Déplacer de", + "cols" : "colonnes", + "rows" : "lignes", + "layout" : "Disposition", + "layout-type-default" : "Par défaut", + "layout-type-scada" : "SCADA", + "layout-type-divider" : "Séparateur", + "layout-settings-type" : "Paramètres de disposition : point de rupture {{ type }}", + "columns-count" : "Nombre de colonnes", + "columns-count-required" : "Le nombre de colonnes est requis.", + "min-columns-count-message" : "Un minimum de 10 colonnes est requis.", + "max-columns-count-message" : "Un maximum de 1000 colonnes est autorisé.", + "min-layout-width" : "Largeur minimale de la disposition", + "columns-suffix" : "colonnes", + "widgets-margins" : "Marge entre les widgets", + "margin-required" : "La valeur de la marge est requise.", + "min-margin-message" : "0 est la seule valeur minimale autorisée pour la marge.", + "max-margin-message" : "50 est la seule valeur maximale autorisée pour la marge.", + "horizontal-margin" : "Marge horizontale", + "horizontal-margin-required" : "La valeur de la marge horizontale est requise.", + "min-horizontal-margin-message" : "0 est la seule valeur minimale autorisée pour la marge horizontale.", + "max-horizontal-margin-message" : "50 est la seule valeur maximale autorisée pour la marge horizontale.", + "vertical-margin" : "Marge verticale", + "vertical-margin-required" : "La valeur de la marge verticale est requise.", + "min-vertical-margin-message" : "0 est la seule valeur minimale autorisée pour la marge verticale.", + "max-vertical-margin-message" : "50 est la seule valeur maximale autorisée pour la marge verticale.", + "apply-outer-margin" : "Appliquer une marge sur les côtés de la disposition", + "autofill-height" : "Remplissage automatique de la hauteur de disposition", + "mobile-layout" : "Paramètres de disposition mobile", + "mobile-row-height" : "Hauteur de ligne mobile", + "mobile-row-height-required" : "La hauteur de ligne mobile est requise.", + "min-mobile-row-height-message" : "5 pixels est la seule valeur minimale autorisée pour la hauteur de ligne mobile.", + "max-mobile-row-height-message" : "200 pixels est la seule valeur maximale autorisée pour la hauteur de ligne mobile.", + "row-height" : "Hauteur de ligne", + "row-height-required" : "La hauteur de ligne est requise.", + "min-row-height-message" : "5 pixels est la seule valeur minimale autorisée pour la hauteur de ligne.", + "max-row-height-message" : "200 pixels est la seule valeur maximale autorisée pour la hauteur de ligne.", + "display-first-in-mobile-view" : "Afficher en premier dans la vue mobile", + "title-settings" : "Paramètres du titre", + "display-title" : "Afficher le titre du tableau de bord", + "title-color" : "Couleur du titre", + "toolbar-settings" : "Paramètres de la barre d’outils", + "hide-toolbar" : "Masquer la barre d’outils", + "toolbar-always-open" : "Garder la barre d’outils ouverte", + "display-dashboards-selection" : "Afficher la sélection des tableaux de bord", + "display-entities-selection" : "Afficher la sélection des entités", + "display-filters" : "Afficher les filtres", + "display-dashboard-timewindow" : "Afficher la fenêtre temporelle", + "display-dashboard-export" : "Afficher l'exportation", + "display-update-dashboard-image" : "Afficher la mise à jour de l'image du tableau de bord", + "dashboard-logo-settings" : "Paramètres du logo du tableau de bord", + "display-dashboard-logo" : "Afficher le logo en mode plein écran", + "dashboard-logo-image" : "Image du logo du tableau de bord", + "advanced-settings" : "Paramètres avancés", + "dashboard-css" : "CSS du tableau de bord", + "import" : "Importer le tableau de bord", + "export" : "Exporter le tableau de bord", + "export-failed-error" : "Impossible d’exporter le tableau de bord : {{error}}", + "export-prompt" : "Inclure les images et ressources du tableau de bord", + "create-new-dashboard" : "Créer un nouveau tableau de bord", + "dashboard-file" : "Fichier du tableau de bord", + "invalid-dashboard-file-error" : "Impossible d’importer le tableau de bord : structure de données invalide.", + "dashboard-import-missing-aliases-title" : "Configurer les alias utilisés par le tableau de bord importé", + "create-new-widget" : "Créer un nouveau widget", + "import-widget" : "Importer un widget", + "widget-file" : "Fichier du widget", + "invalid-widget-file-error" : "Impossible d’importer le widget : structure de données invalide.", + "widget-import-missing-aliases-title" : "Configurer les alias utilisés par le widget importé", + "open-toolbar" : "Ouvrir la barre d’outils du tableau de bord", + "close-toolbar" : "Fermer la barre d’outils", + "configuration-error" : "Erreur de configuration", + "alias-resolution-error-title" : "Erreur de configuration des alias du tableau de bord", + "invalid-aliases-config" : "Aucun appareil ne correspond à certains filtres d'alias.
Veuillez contacter votre administrateur pour résoudre ce problème.", + "select-devices" : "Sélectionner les appareils", + "assignedToCustomer" : "Assigné à un client", + "assignedToCustomers" : "Assigné à des clients", + "public" : "Public", + "copyId" : "Copier l'identifiant du tableau de bord", + "idCopiedMessage" : "Identifiant du tableau de bord copié dans le presse-papiers", + "public-link" : "Lien public", + "copy-public-link" : "Copier le lien public", + "public-link-copied-message" : "Lien public du tableau de bord copié dans le presse-papiers", + "manage-states" : "Gérer les états du tableau de bord", + "states" : "États du tableau de bord", + "states-short" : "États", + "search-states" : "Rechercher dans les états du tableau de bord", + "selected-states" : "{ count, plural, =1 {1 état du tableau de bord} other {# états du tableau de bord} } sélectionné(s)", + "edit-state" : "Modifier l'état du tableau de bord", + "delete-state" : "Supprimer l'état du tableau de bord", + "add-state" : "Ajouter un état du tableau de bord", + "no-states-text" : "Aucun état trouvé", + "state" : "État du tableau de bord", + "state-name" : "Nom", + "state-name-required" : "Le nom de l'état est requis.", + "state-id" : "ID de l'état", + "state-id-required" : "L'ID de l'état est requis.", + "state-id-exists" : "Un état de tableau de bord avec le même ID existe déjà.", + "is-root-state" : "État racine", + "delete-state-title" : "Supprimer l'état du tableau de bord", + "delete-state-text" : "Êtes-vous sûr de vouloir supprimer l'état du tableau de bord nommé '{{stateName}}' ?", + "show-details" : "Afficher les détails", + "hide-details" : "Masquer les détails", + "select-state" : "Sélectionner l'état cible", + "state-controller" : "Contrôleur d'état", + "state-controller-default" : "statique (obsolète)", + "search" : "Rechercher des tableaux de bord", + "selected-dashboards" : "{ count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } sélectionné(s)", + "home-dashboard" : "Tableau de bord d'accueil", + "home-dashboard-hide-toolbar" : "Masquer la barre d'outils du tableau de bord d'accueil", + "unassign-dashboard-from-edge-text" : "Après confirmation, le tableau de bord sera désassigné et ne sera plus accessible par l'instance edge.", + "unassign-dashboards-from-edge-title" : "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } ?", + "unassign-dashboards-from-edge-text" : "Après confirmation, tous les tableaux de bord sélectionnés seront désassignés et ne seront plus accessibles par l'instance edge.", + "assign-dashboard-to-edge" : "Assigner le(s) tableau(x) de bord à l'instance Edge", + "assign-dashboard-to-edge-text" : "Veuillez sélectionner les tableaux de bord à assigner à l'instance Edge", + "non-existent-dashboard-state-error" : "L'état du tableau de bord avec l'identifiant \"{{ stateId }}\" est introuvable", + "edit-mode" : "Mode édition", + "duplicate-state-action" : "Dupliquer l'état", + "breakpoint-value" : "Point de rupture ({{ value }})", + "breakpoints-id" : { + "default" : "Par défaut", + "xs" : "Mobile (xs)", + "sm" : "Tablette (sm)", + "md" : "Ordinateur portable (md)", + "lg" : "Bureau (lg)", + "xl" : "Bureau (xl)" + }, + "view-format-type-grid" : "Grille", + "view-format-type-list" : "Liste", + "view-format" : "Format d'affichage" + }, + "datakey" : { + "settings" : "Paramètres", + "general" : "Général", + "advanced" : "Avancé", + "key" : "Clé", + "keys" : "Clés", + "label" : "Étiquette", + "color" : "Couleur", + "units" : "Symbole spécial à afficher à côté de la valeur", + "decimals" : "Nombre de chiffres après la virgule", + "data-generation-func" : "Fonction de génération de données", + "use-data-post-processing-func" : "Utiliser une fonction de post-traitement des données", + "configuration" : "Configuration de la clé de données", + "timeseries" : "Séries temporelles", + "attributes" : "Attributs", + "entity-field" : "Champ de l'entité", + "alarm" : "Champs d'alarme", + "timeseries-required" : "Les séries temporelles de l'entité sont requises.", + "timeseries-or-attributes-required" : "Séries temporelles/attributs de l'entité requis.", + "alarm-fields-timeseries-or-attributes-required" : "Champs d'alarme ou séries temporelles/attributs de l'entité requis.", + "maximum-timeseries-or-attributes" : "Maximum { count, plural, =1 {1 série temporelle/attribut autorisé.} other {# séries temporelles/attributs autorisés} }", + "alarm-fields-required" : "Les champs d'alarme sont requis.", + "function-types" : "Types de fonctions", + "function-type" : "Type de fonction", + "function-types-required" : "Les types de fonctions sont requis.", + "data-keys" : "Clés de données", + "data-key" : "Clé de données", + "data-keys-required" : "Les clés de données sont requises.", + "data-key-required" : "La clé de données est requise.", + "alarm-keys" : "Clés de données d'alarme", + "alarm-key" : "Clé de données d'alarme", + "alarm-key-functions" : "Fonctions de clé d'alarme", + "alarm-key-function" : "Fonction de clé d'alarme", + "latest-keys" : "Dernières clés de données", + "latest-key" : "Dernière clé de données", + "latest-key-functions" : "Fonctions de dernière clé", + "latest-key-function" : "Fonction de la dernière clé", + "timeseries-keys" : "Clés de séries temporelles", + "timeseries-key" : "Clé de série temporelle", + "timeseries-key-functions" : "Fonctions de clé de série temporelle", + "timeseries-key-function" : "Fonction de clé de série temporelle", + "maximum-function-types" : "Maximum { count, plural, =1 {1 type de fonction autorisé.} other {# types de fonctions autorisés} }", + "time-description" : "horodatage de la valeur actuelle ;", + "value-description" : "la valeur actuelle ;", + "prev-value-description" : "résultat de l'appel précédent de la fonction ;", + "time-prev-description" : "horodatage de la valeur précédente ;", + "prev-orig-value-description" : "valeur d'origine précédente ;", + "aggregation" : "Agrégation", + "aggregation-type-hint-common" : "Pour des raisons de performance, le calcul des valeurs agrégées est disponible uniquement pour des intervalles de temps fixes comme « jour actuel », « mois actuel », etc., et n'est pas disponible pour des fenêtres glissantes comme « 30 dernières minutes » ou « dernières 24 heures ».", + "aggregation-type-none-hint" : "Prendre la dernière valeur.", + "aggregation-type-min-hint" : "Trouver la valeur minimale parmi les points de données dans la fenêtre temporelle sélectionnée.", + "aggregation-type-max-hint" : "Trouver la valeur maximale parmi les points de données dans la fenêtre temporelle sélectionnée.", + "aggregation-type-avg-hint" : "Calculer la moyenne des valeurs dans la fenêtre temporelle sélectionnée.", + "aggregation-type-sum-hint" : "Additionner toutes les valeurs dans la fenêtre temporelle sélectionnée.", + "aggregation-type-count-hint" : "Nombre total de points de données dans la fenêtre temporelle sélectionnée.", + "delta-calculation" : "Calcul du delta", + "enable-delta-calculation" : "Activer le calcul du delta", + "enable-delta-calculation-hint" : "Lorsqu'il est activé, la valeur de la clé de données est calculée sur la base des valeurs agrégées pour une fenêtre temporelle sélectionnée et une période de comparaison spécifiée. Pour des raisons de performance, le calcul du delta est disponible uniquement pour des fenêtres temporelles historiques et non pour des valeurs en temps réel. Par exemple, vous pouvez calculer le delta entre la consommation d'énergie d'hier et celle de l'avant-hier.", + "delta-calculation-result" : "Résultat du calcul du delta", + "delta-calculation-result-previous-value" : "Valeur précédente", + "delta-calculation-result-delta-absolute" : "Delta (absolu)", + "delta-calculation-result-delta-percent" : "Delta (pourcentage)", + "source" : "Source", + "latest" : "Dernier", + "latest-value" : "Dernière valeur", + "delta" : "delta", + "percent" : "pourcentage", + "absolute" : "absolu" + }, + "datasource" : { + "type" : "Type de source de données", + "name" : "Nom", + "label" : "Étiquette", + "add-datasource-prompt" : "Veuillez ajouter une source de données" + }, + "details" : { + "details" : "Détails", + "edit-mode" : "Mode édition", + "edit-json" : "Éditer JSON", + "toggle-edit-mode" : "Basculer le mode édition" + }, + "device" : { + "device" : "Appareil", + "device-required" : "L'appareil est requis.", + "devices" : "Appareils", + "management" : "Gestion des appareils", + "view-devices" : "Voir les appareils", + "device-alias" : "Alias de l'appareil", + "device-type-max-length" : "Le type d'appareil doit comporter moins de 256 caractères", + "aliases" : "Alias d'appareils", + "no-alias-matching" : "'{{alias}}' introuvable.", + "no-aliases-found" : "Aucun alias trouvé.", + "no-key-matching" : "'{{key}}' introuvable.", + "no-keys-found" : "Aucune clé trouvée.", + "create-new-alias" : "Créez-en un nouveau !", + "create-new-key" : "Créez-en un nouveau !", + "duplicate-alias-error" : "Alias en double trouvé '{{alias}}'.
Les alias d'appareil doivent être uniques dans le tableau de bord.", + "configure-alias" : "Configurer l'alias '{{alias}}'", + "no-devices-matching" : "Aucun appareil correspondant à '{{entity}}' trouvé.", + "alias" : "Alias", + "alias-required" : "L'alias de l'appareil est requis.", + "remove-alias" : "Supprimer l'alias de l'appareil", + "add-alias" : "Ajouter un alias d'appareil", + "name-starts-with" : "Expression du nom de l'appareil", + "help-text" : "Utilisez '%' selon le besoin : '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list" : "Liste des appareils", + "use-device-name-filter" : "Utiliser un filtre", + "device-list-empty" : "Aucun appareil sélectionné.", + "device-name-filter-required" : "Le filtre de nom d'appareil est requis.", + "device-name-filter-no-device-matched" : "Aucun appareil commençant par '{{device}}' trouvé.", + "add" : "Ajouter un appareil", + "assign-to-customer" : "Attribuer au client", + "assign-device-to-customer" : "Attribuer le(s) appareil(s) au client", + "assign-device-to-customer-text" : "Veuillez sélectionner les appareils à attribuer au client", + "make-public" : "Rendre public l'appareil", + "make-private" : "Rendre privé l'appareil", + "no-devices-text" : "Aucun appareil trouvé", + "assign-to-customer-text" : "Veuillez sélectionner le client à qui attribuer le(s) appareil(s)", + "device-details" : "Détails de l'appareil", + "add-device-text" : "Ajouter un nouvel appareil", + "credentials" : "Identifiants", + "manage-credentials" : "Gérer les identifiants", + "delete" : "Supprimer l'appareil", + "assign-devices" : "Attribuer les appareils", + "assign-devices-text" : "Attribuer { count, plural, =1 {1 appareil} other {# appareils} } au client", + "delete-devices" : "Supprimer les appareils", + "unassign-from-customer" : "Désattribuer du client", + "unassign-devices" : "Désattribuer les appareils", + "unassign-devices-action-title" : "Désattribuer { count, plural, =1 {1 appareil} other {# appareils} } du client", + "unassign-device-from-edge-title" : "Êtes-vous sûr de vouloir désattribuer l'appareil '{{deviceName}}' ?", + "unassign-device-from-edge-text" : "Après confirmation, l'appareil sera désattribué et ne sera plus accessible par l'edge.", + "unassign-devices-from-edge" : "Désattribuer les appareils de l'edge", + "assign-new-device" : "Attribuer un nouvel appareil", + "make-public-device-title" : "Êtes-vous sûr de vouloir rendre l'appareil '{{deviceName}}' public ?", + "make-public-device-text" : "Après confirmation, l'appareil et toutes ses données seront rendus publics et accessibles par d'autres.", + "make-private-device-title" : "Êtes-vous sûr de vouloir rendre l'appareil '{{deviceName}}' privé ?", + "make-private-device-text" : "Après confirmation, l'appareil et toutes ses données seront rendus privés et ne seront plus accessibles par d'autres.", + "view-credentials" : "Voir les identifiants", + "delete-device-title" : "Êtes-vous sûr de vouloir supprimer l'appareil '{{deviceName}}' ?", + "delete-device-text" : "Attention, après confirmation, l'appareil et toutes les données associées seront irrécupérables.", + "delete-devices-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 appareil} other {# appareils} } ?", + "delete-devices-action-title" : "Supprimer { count, plural, =1 {1 appareil} other {# appareils} }", + "delete-devices-text" : "Attention, après confirmation, tous les appareils sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "unassign-device-title" : "Êtes-vous sûr de vouloir désattribuer l'appareil '{{deviceName}}' ?", + "unassign-device-text" : "Après confirmation, l'appareil sera désattribué et ne sera plus accessible par le client.", + "unassign-device" : "Désattribuer l'appareil", + "unassign-devices-title" : "Êtes-vous sûr de vouloir désattribuer { count, plural, =1 {1 appareil} other {# appareils} } ?", + "unassign-devices-text" : "Après confirmation, tous les appareils sélectionnés seront désattribués et ne seront plus accessibles par le client.", + "device-credentials" : "Identifiants de l'appareil", + "loading-device-credentials" : "Chargement des identifiants de l'appareil...", + "credentials-type" : "Type d'identifiants", + "access-token" : "Jeton d'accès", + "access-token-required" : "Le jeton d'accès est requis.", + "access-token-invalid" : "La longueur du jeton d'accès doit être comprise entre 1 et 32 caractères.", + "certificate-pem-format" : "Certificat au format PEM", + "certificate-pem-format-required" : "Le certificat est requis.", + "copy-access-token" : "Copier le jeton d'accès", + "copy-certificate" : "Copier le certificat", + "copy-client-id" : "Copier l'ID client", + "copy-user-name" : "Copier le nom d'utilisateur", + "copy-password" : "Copier le mot de passe", + "generate-client-id" : "Générer l'ID client", + "generate-user-name" : "Générer le nom d'utilisateur", + "generate-password" : "Générer le mot de passe", + "generate-access-token" : "Générer un jeton d'accès", + "lwm2m-security-config" : { + "identity" : "Identité du client", + "identity-required" : "L'identité du client est requise.", + "identity-tooltip" : "L'identifiant PSK est un identifiant arbitraire pouvant aller jusqu'à 128 octets, tel que décrit dans la norme [RFC7925].\nL'identifiant PSK DOIT d'abord être converti en chaîne de caractères puis encodé en octets à l'aide de l'encodage UTF-8.", + "client-key" : "Clé du client", + "client-key-required" : "La clé du client est requise.", + "client-key-tooltip-prk" : "La clé publique RPK ou l'identifiant doit être conforme à la norme [RFC7250] et encodée en Base64 !", + "client-key-tooltip-psk" : "La clé PSK doit être conforme à la norme [RFC4279] et au format HexDec : 32, 64, 128 caractères !", + "endpoint" : "Nom du client Endpoint", + "endpoint-required" : "Le nom du client Endpoint est requis.", + "client-public-key" : "Clé publique du client", + "client-public-key-hint" : "Si la clé publique du client est vide, le certificat de confiance sera utilisé", + "client-public-key-tooltip" : "La clé publique X509 doit être au format DER-encodé X509v3, uniquement avec l’algorithme EC, et encodée ensuite en Base64 !", + "mode" : "Mode de configuration de sécurité", + "client-tab" : "Configuration de sécurité du client", + "client-certificate" : "Certificat du client", + "bootstrap-tab" : "Client Bootstrap", + "bootstrap-server" : "Serveur Bootstrap", + "lwm2m-server" : "Serveur LwM2M", + "client-publicKey-or-id" : "Clé publique ou identifiant du client", + "client-publicKey-or-id-required" : "Clé publique ou identifiant du client est requis.", + "client-publicKey-or-id-tooltip-psk" : "L'identifiant PSK est un identifiant arbitraire jusqu'à 128 octets, selon la norme [RFC7925].\nIl DOIT être d'abord converti en chaîne puis encodé en UTF-8.", + "client-publicKey-or-id-tooltip-rpk" : "La clé publique ou l'identifiant RPK doit respecter la norme [RFC7250] et être encodé en Base64 !", + "client-publicKey-or-id-tooltip-x509" : "La clé publique X509 doit être au format DER-encodé X509v3, supportant exclusivement l’algorithme EC, et encodée en Base64.", + "client-secret-key" : "Clé secrète du client", + "client-secret-key-required" : "La clé secrète du client est requise.", + "client-secret-key-tooltip-psk" : "La clé PSK doit respecter la norme [RFC4279] et être au format HexDec : 32, 64, 128 caractères !", + "client-secret-key-tooltip-prk" : "La clé secrète RPK doit être au format PKCS_8 (encodage DER, norme [RFC5958]) puis encodée en Base64 !", + "client-secret-key-tooltip-x509" : "La clé secrète X509 doit être au format PKCS_8 (encodage DER, norme [RFC5958]) puis encodée en Base64 !" + }, + "client-id" : "ID du client", + "client-id-pattern" : "Contient un caractère non valide.", + "user-name" : "Nom d'utilisateur", + "user-name-required" : "Le nom d'utilisateur est requis.", + "client-id-or-user-name-necessary" : "L'ID du client et/ou le nom d'utilisateur sont nécessaires", + "password" : "Mot de passe", + "secret" : "Secret", + "secret-required" : "Le secret est requis.", + "device-type" : "Profil de l'appareil", + "device-type-required" : "Le type d'appareil est requis.", + "select-device-type" : "Sélectionner le type d'appareil", + "enter-device-type" : "Saisir le profil de l'appareil", + "any-device" : "N'importe quel appareil", + "no-device-types-matching" : "Aucun profil d'appareil correspondant à '{{entitySubtype}}' n'a été trouvé.", + "device-type-list-empty" : "Aucun profil d'appareil sélectionné !", + "device-profile-type-list-empty" : "Au moins un profil d'appareil doit être sélectionné.", + "device-types" : "Types d'appareils", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "name-max-length" : "Le nom doit comporter moins de 256 caractères", + "label-max-length" : "L'étiquette doit comporter moins de 256 caractères", + "description" : "Description", + "label" : "Étiquette", + "events" : "Événements", + "details" : "Détails", + "copyId" : "Copier l'ID de l'appareil", + "copyAccessToken" : "Copier le jeton d'accès", + "copy-mqtt-authentication" : "Copier les identifiants MQTT", + "idCopiedMessage" : "L'ID de l'appareil a été copié dans le presse-papiers", + "accessTokenCopiedMessage" : "Le jeton d'accès de l'appareil a été copié dans le presse-papiers", + "mqtt-authentication-copied-message" : "Les identifiants MQTT de l'appareil ont été copiés dans le presse-papiers", + "assignedToCustomer" : "Attribué au client", + "unable-delete-device-alias-title" : "Impossible de supprimer l'alias de l'appareil", + "unable-delete-device-alias-text" : "L'alias de l'appareil '{{deviceAlias}}' ne peut pas être supprimé car il est utilisé par le(s) widget(s) suivant(s) :
{{widgetsList}}", + "is-gateway" : "Est une passerelle", + "overwrite-activity-time" : "Écraser l'heure d'activité de l'appareil connecté", + "device-filter" : "Filtre d'appareil", + "device-filter-title" : "Filtre d'appareil", + "filter-title" : "Filtre", + "device-state" : "État de l'appareil", + "state" : "État", + "any" : "N'importe quel", + "active" : "Actif", + "inactive" : "Inactif", + "public" : "Public", + "device-public" : "L'appareil est public", + "select-device" : "Sélectionner l'appareil", + "import" : "Importer l'appareil", + "device-file" : "Fichier de l'appareil", + "search" : "Rechercher des appareils", + "selected-devices" : "{ count, plural, =1 {1 appareil} other {# appareils} } sélectionné(s)", + "device-configuration" : "Configuration de l'appareil", + "transport-configuration" : "Configuration de transport", + "wizard" : { + "device-details" : "Détails de l'appareil" + }, + "unassign-devices-from-edge-title" : "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 appareil} other {# appareils} } ?", + "unassign-devices-from-edge-text" : "Après confirmation, tous les appareils sélectionnés seront désassignés et ne seront plus accessibles par l’edge.", + "time" : "Temps", + "connectivity" : { + "check-connectivity" : "Vérifier la connectivité", + "device-created-check-connectivity" : "Appareil créé. Vérifions sa connectivité !", + "loading-check-connectivity-command" : "Chargement des commandes de vérification de connectivité...", + "use-following-instructions" : "Utilisez les instructions suivantes pour envoyer des télémesures au nom de l'appareil via le terminal", + "execute-following-command" : "Exécutez la commande suivante", + "install-curl-windows" : "Depuis Windows 10 version b17063, cURL est disponible par défaut", + "install-curl-macos" : "Depuis Mac OS X 10.2 6C115 (Jaguar), cURL est disponible par défaut", + "install-mqtt-windows" : "Utilisez les instructions pour télécharger, installer, configurer et exécuter mosquitto_pub", + "install-coap-client" : "Utilisez les instructions pour télécharger, installer, configurer et exécuter coap-client", + "install-necessary-client-tools" : "Installer les outils clients nécessaires", + "mqtts-x509-command" : "Utilisez la documentation suivante pour connecter l'appareil via MQTT avec authentification X509", + "coaps-x509-command" : "Utilisez la documentation suivante pour connecter l'appareil via CoAP sur DTLS avec authentification X509", + "snmp-command" : "Utilisez la documentation suivante pour connecter l'appareil via SNMP.", + "sparkplug-command" : "Utilisez la documentation suivante pour connecter l'appareil via MQTT Sparkplug.", + "lwm2m-command" : "Utilisez la documentation suivante pour connecter l'appareil via LWM2M." + } + }, + "dynamic-form" : { + "property" : { + "properties" : "Propriétés", + "property" : "Propriété", + "id" : "Identifiant", + "name" : "Nom", + "type" : "Type", + "type-text" : "Texte", + "type-password" : "Mot de passe", + "type-textarea" : "Zone de texte", + "type-number" : "Nombre", + "type-switch" : "Interrupteur", + "type-select" : "Sélection", + "type-radios" : "Boutons radio", + "type-datetime" : "Date/Heure", + "type-image" : "Image", + "type-javascript" : "JavaScript", + "type-json" : "JSON", + "type-html" : "HTML", + "type-css" : "CSS", + "type-markdown" : "Markdown", + "type-color" : "Couleur", + "type-color-settings" : "Paramètres de couleur", + "type-font" : "Police", + "type-units" : "Unités", + "type-icon" : "Icône", + "type-fieldset" : "Ensemble de champs", + "type-array" : "Tableau", + "type-html-section" : "Section HTML", + "group-title" : "Titre du groupe", + "no-properties" : "Aucune propriété configurée", + "add-property" : "Ajouter une propriété", + "property-settings" : "Paramètres de la propriété", + "remove-property" : "Supprimer la propriété", + "default-value" : "Valeur par défaut", + "value-required" : "Valeur requise", + "number-settings" : "Paramètres numériques", + "min" : "Min", + "max" : "Max", + "step" : "Pas", + "selected-options-limit" : "Limite d'options sélectionnées", + "advanced-ui-settings" : "Paramètres UI avancés", + "disable-on-property" : "Désactiver en fonction de la propriété", + "display-condition-function" : "Fonction de condition d'affichage", + "sub-label" : "Sous-étiquette", + "vertical-divider-after" : "Séparateur vertical après", + "input-field-suffix" : "Suffixe du champ de saisie", + "property-row-classes" : "Classes de ligne de propriété", + "property-field-classes" : "Classes de champ de propriété", + "not-unique-property-ids-error" : "Les identifiants des propriétés doivent être uniques !", + "enable-multiple-select" : "Activer la sélection multiple", + "allow-empty-select-option" : "Autoriser l'option vide", + "select-options" : "Options de sélection", + "not-unique-select-option-value-error" : "Les valeurs des options doivent être uniques !", + "value" : "Valeur", + "label" : "Étiquette", + "add-option" : "Ajouter une option", + "no-options" : "Aucune option configurée", + "remove-option" : "Supprimer l'option", + "textarea-rows" : "Lignes de la zone de texte", + "help-id" : "Identifiant d'aide", + "buttons-direction" : "Direction des boutons", + "direction-row" : "Ligne", + "direction-column" : "Colonne", + "radio-button-options" : "Options des boutons radio", + "datetime-type" : "Type de champ Date/Heure", + "datetime-type-date" : "Date", + "datetime-type-time" : "Heure", + "datetime-type-datetime" : "Date/Heure", + "enable-clear-button" : "Activer le bouton de réinitialisation", + "html-section-settings" : "Paramètres de section HTML", + "html-section-classes" : "Classes de section HTML", + "html-section-content" : "Contenu de la section HTML", + "array-item" : "Élément du tableau", + "item-type" : "Type d'élément", + "item-name" : "Nom de l'élément", + "no-items" : "Aucun élément" + }, + "clear-form" : "Effacer le formulaire", + "clear-form-prompt" : "Êtes-vous sûr de vouloir supprimer toutes les propriétés du formulaire ?", + "import-form" : "Importer un formulaire depuis JSON", + "export-form" : "Exporter le formulaire en JSON", + "json-file" : "Fichier JSON", + "json-content" : "Contenu JSON", + "invalid-form-json-file-error" : "Impossible d'importer le formulaire depuis JSON : Structure de données JSON invalide." + }, + "asset-profile" : { + "asset-profile" : "Profil d'actif", + "asset-profiles" : "Profils d'actifs", + "all-asset-profiles" : "Tous", + "add" : "Ajouter un profil d'actif", + "edit" : "Modifier le profil d'actif", + "asset-profile-details" : "Détails du profil d'actif", + "no-asset-profiles-text" : "Aucun profil d'actif trouvé", + "search" : "Rechercher des profils d'actifs", + "selected-asset-profiles" : "{ count, plural, =1 {1 profil d'actif} other {# profils d'actifs} } sélectionné(s)", + "no-asset-profiles-matching" : "Aucun profil d'actif correspondant à '{{entity}}' trouvé.", + "asset-profile-required" : "Le profil d'actif est requis", + "idCopiedMessage" : "L'identifiant du profil d'actif a été copié dans le presse-papiers", + "set-default" : "Définir comme profil d'actif par défaut", + "delete" : "Supprimer le profil d'actif", + "copyId" : "Copier l'identifiant du profil d'actif", + "name-max-length" : "Le nom doit contenir moins de 256 caractères", + "new-device-profile-name" : "Nom du profil d'actif", + "new-device-profile-name-required" : "Le nom du profil d'actif est requis.", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "image" : "Image du profil d'actif", + "description" : "Description", + "default" : "Par défaut", + "default-rule-chain" : "Chaîne de règles par défaut", + "default-edge-rule-chain" : "Chaîne de règles Edge par défaut", + "default-edge-rule-chain-hint" : "Utilisé sur l'Edge comme chaîne de règles pour traiter les données entrantes des actifs de ce profil", + "mobile-dashboard" : "Tableau de bord mobile", + "mobile-dashboard-hint" : "Utilisé par l'application mobile comme tableau de bord des détails de l'actif", + "select-queue-hint" : "Sélectionnez dans une liste déroulante.", + "delete-asset-profile-title" : "Êtes-vous sûr de vouloir supprimer le profil d'actif '{{assetProfileName}}' ?", + "delete-asset-profile-text" : "Attention, après confirmation, le profil d'actif et toutes les données associées seront irrécupérables.", + "delete-asset-profiles-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 profil d'actif} other {# profils d'actifs} } ?", + "delete-asset-profiles-text" : "Attention, après confirmation, tous les profils d'actifs sélectionnés seront supprimés ainsi que toutes les données associées.", + "set-default-asset-profile-title" : "Êtes-vous sûr de vouloir définir le profil d'actif '{{assetProfileName}}' comme par défaut ?", + "set-default-asset-profile-text" : "Après confirmation, ce profil d'actif sera marqué comme par défaut et utilisé pour les nouveaux actifs sans profil spécifié.", + "no-asset-profiles-found" : "Aucun profil d'actif trouvé.", + "create-new-asset-profile" : "Créer un nouveau !", + "create-asset-profile" : "Créer un nouveau profil d'actif", + "import" : "Importer un profil d'actif", + "export" : "Exporter le profil d'actif", + "export-failed-error" : "Impossible d'exporter le profil d'actif : {{error}}", + "asset-profile-file" : "Fichier de profil d'actif", + "invalid-asset-profile-file-error" : "Impossible d'importer le profil d'actif : structure de données invalide." + }, + "device-profile" : { + "device-profile" : "Profil de dispositif", + "device-profiles" : "Profils de dispositifs", + "all-device-profiles" : "Tous", + "add" : "Ajouter un profil de dispositif", + "edit" : "Modifier le profil de dispositif", + "device-profile-details" : "Détails du profil de dispositif", + "no-device-profiles-text" : "Aucun profil de dispositif trouvé", + "search" : "Rechercher des profils de dispositifs", + "selected-device-profiles" : "{ count, plural, =1 {1 profil de dispositif} other {# profils de dispositifs} } sélectionné(s)", + "no-device-profiles-matching" : "Aucun profil de dispositif correspondant à '{{entity}}' trouvé.", + "device-profile-required" : "Le profil de dispositif est requis", + "idCopiedMessage" : "L'identifiant du profil de dispositif a été copié dans le presse-papiers", + "set-default" : "Définir comme profil de dispositif par défaut", + "delete" : "Supprimer le profil de dispositif", + "copyId" : "Copier l'identifiant du profil de dispositif", + "name-max-length" : "Le nom doit contenir moins de 256 caractères", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "type" : "Type de profil", + "type-required" : "Le type de profil est requis.", + "type-default" : "Par défaut", + "image" : "Image du profil de dispositif", + "transport-type" : "Type de transport", + "transport-type-required" : "Le type de transport est requis.", + "transport-type-default" : "Par défaut", + "transport-type-default-hint" : "Prend en charge les transports MQTT, HTTP et CoAP basiques", + "transport-type-mqtt" : "MQTT", + "transport-type-mqtt-hint" : "Active les paramètres avancés de transport MQTT", + "transport-type-coap" : "CoAP", + "transport-type-coap-hint" : "Active les paramètres avancés de transport CoAP", + "transport-type-lwm2m" : "LWM2M", + "transport-type-lwm2m-hint" : "Type de transport LWM2M", + "transport-type-snmp" : "SNMP", + "transport-type-snmp-hint" : "Spécifiez la configuration du transport SNMP", + "transport-type-http" : "HTTP", + "description" : "Description", + "default" : "Par défaut", + "profile-configuration" : "Configuration du profil", + "transport-configuration" : "Configuration du transport", + "default-rule-chain" : "Chaîne de règles par défaut", + "default-edge-rule-chain" : "Chaîne de règles Edge par défaut", + "default-edge-rule-chain-hint" : "Utilisé sur Edge comme chaîne de règles pour traiter les données entrantes des dispositifs de ce profil", + "mobile-dashboard" : "Tableau de bord mobile", + "mobile-dashboard-hint" : "Utilisé par l'application mobile comme tableau de bord des détails du dispositif", + "select-queue-hint" : "Sélectionnez dans une liste déroulante.", + "delete-device-profile-title" : "Êtes-vous sûr de vouloir supprimer le profil de dispositif '{{deviceProfileName}}' ?", + "delete-device-profile-text" : "Attention, après confirmation, le profil de dispositif et toutes les données associées, y compris les mises à jour OTA, seront irrécupérables.", + "delete-device-profiles-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 profil de dispositif} other {# profils de dispositifs} } ?", + "delete-device-profiles-text" : "Attention, après confirmation, tous les profils de dispositifs sélectionnés seront supprimés ainsi que toutes les données associées, y compris les mises à jour OTA.", + "set-default-device-profile-title" : "Êtes-vous sûr de vouloir définir le profil de dispositif '{{deviceProfileName}}' comme par défaut ?", + "set-default-device-profile-text" : "Après confirmation, ce profil de dispositif sera marqué comme par défaut et utilisé pour les nouveaux dispositifs sans profil spécifié.", + "no-device-profiles-found" : "Aucun profil de dispositif trouvé.", + "create-new-device-profile" : "Créer un nouveau !", + "mqtt-device-topic-filters" : "Filtres de sujet MQTT du dispositif", + "mqtt-device-topic-filters-unique" : "Les filtres de sujet MQTT doivent être uniques.", + "mqtt-device-topic-filters-spark-plug" : "Nœud EoN MQTT Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-hint" : "Permet les connexions de nœuds EoN avec format de charge utile et de sujet Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names" : "Métriques SparkPlug à stocker comme attributs.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint" : "Noms des métriques SparkPlug qui seront stockées comme attributs de l'appareil. Toutes les autres seront stockées comme télémétrie.", + "mqtt-device-payload-type" : "Charge utile MQTT du dispositif", + "mqtt-device-payload-type-json" : "JSON", + "mqtt-device-payload-type-proto" : "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format" : "Activer la compatibilité avec d'autres formats de charge utile.", + "mqtt-enable-compatibility-with-json-payload-format-hint" : "Lorsque cette option est activée, la plateforme utilisera le format Protobuf par défaut. En cas d'échec de l'analyse, elle utilisera le format JSON. Utile pour les mises à jour de firmware. À désactiver une fois tous les dispositifs mis à jour.", + "mqtt-use-json-format-for-default-downlink-topics" : "Utiliser le format JSON pour les sujets descendants par défaut", + "mqtt-use-json-format-for-default-downlink-topics-hint" : "Utilise JSON pour les sujets : v1/devices/me/attributes/response/$request_id, etc. Ne s'applique pas aux sujets v2.", + "mqtt-send-ack-on-validation-exception" : "Envoyer PUBACK en cas d'échec de validation", + "mqtt-send-ack-on-validation-exception-hint" : "Par défaut, la session MQTT est fermée sur erreur. Si activé, envoie un accusé de réception PUBACK à la place.", + "snmp-add-mapping" : "Ajouter un mappage SNMP", + "snmp-mapping-not-configured" : "Aucun mappage OID vers série temporelle ou attribut configuré", + "snmp-timseries-or-attribute-name" : "Nom de série temporelle/attribut pour le mappage", + "snmp-timseries-or-attribute-type" : "Type de série temporelle/attribut pour le mappage", + "snmp-method-pdu-type-get-request" : "GetRequest", + "snmp-method-pdu-type-get-next-request" : "GetNextRequest", + "snmp-oid" : "OID", + "transport-device-payload-type-json" : "JSON", + "transport-device-payload-type-proto" : "Protobuf", + "mqtt-payload-type-required" : "Le type de charge utile est requis.", + "coap-device-type" : "Type d'appareil CoAP", + "coap-device-payload-type" : "Charge utile de l'appareil CoAP", + "coap-device-type-required" : "Le type d'appareil CoAP est requis.", + "coap-device-type-default" : "Par défaut", + "coap-device-type-efento" : "Efento NB-IoT", + "support-level-wildcards" : "Jokers à un niveau [+] et à plusieurs niveaux [#] pris en charge.", + "telemetry-topic-filter" : "Filtre de sujet de télémétrie", + "telemetry-topic-filter-required" : "Le filtre de sujet de télémétrie est requis.", + "attributes-topic-filter" : "Filtre de publication des attributs", + "attributes-subscribe-topic-filter" : "Filtre d’abonnement des attributs", + "attributes-topic-filter-required" : "Le filtre de publication des attributs est requis.", + "attributes-subscribe-topic-filter-required" : "Le filtre d’abonnement des attributs est requis.", + "telemetry-proto-schema" : "Schéma proto de télémétrie", + "telemetry-proto-schema-required" : "Le schéma proto de télémétrie est requis.", + "attributes-proto-schema" : "Schéma proto des attributs", + "attributes-proto-schema-required" : "Le schéma proto des attributs est requis.", + "rpc-response-proto-schema" : "Schéma proto de réponse RPC", + "rpc-response-proto-schema-required" : "Le schéma proto de réponse RPC est requis.", + "rpc-response-topic-filter" : "Filtre de sujet de réponse RPC", + "rpc-response-topic-filter-required" : "Le filtre de sujet de réponse RPC est requis.", + "rpc-request-proto-schema" : "Schéma proto de requête RPC", + "rpc-request-proto-schema-required" : "Le schéma proto de requête RPC est requis.", + "rpc-request-proto-schema-hint" : "Le message de requête RPC doit toujours contenir les champs : string method = 1; int32 requestId = 2; et params = 3 de n'importe quel type.", + "not-valid-pattern-topic-filter" : "Filtre de sujet avec motif non valide", + "not-valid-single-character" : "Utilisation invalide du caractère joker à un seul niveau", + "not-valid-multi-character" : "Utilisation invalide du caractère joker à plusieurs niveaux", + "single-level-wildcards-hint" : "[+] convient à tout niveau de filtre de sujet. Ex. : v1/devices/+/telemetry ou +/devices/+/attributes.", + "multi-level-wildcards-hint" : "[#] peut remplacer le filtre de sujet lui-même et doit être le dernier symbole du sujet. Ex. : # ou v1/devices/me/#.", + "alarm-rules" : "Règles d'alarme", + "alarm-rules-with-count" : "Règles d'alarme ({{count}})", + "no-alarm-rules" : "Aucune règle d'alarme configurée", + "add-alarm-rule" : "Ajouter une règle d'alarme", + "edit-alarm-rule" : "Modifier la règle d'alarme", + "alarm-type" : "Type d'alarme", + "alarm-type-required" : "Le type d'alarme est requis.", + "alarm-type-unique" : "Le type d'alarme doit être unique dans les règles d'alarme du profil.", + "alarm-type-max-length" : "Le type d'alarme doit comporter moins de 256 caractères", + "create-alarm-pattern" : "Créer une alarme {{alarmType}}", + "create-alarm-rules" : "Créer des règles d'alarme", + "no-create-alarm-rules" : "Aucune condition de création configurée", + "add-create-alarm-rule-prompt" : "Veuillez ajouter une règle de création d'alarme", + "clear-alarm-rule" : "Effacer la règle d'alarme", + "no-clear-alarm-rule" : "Aucune condition d'effacement configurée", + "add-create-alarm-rule" : "Ajouter une condition de création", + "add-clear-alarm-rule" : "Ajouter une condition d'effacement", + "select-alarm-severity" : "Sélectionner la gravité de l'alarme", + "alarm-severity-required" : "La gravité de l'alarme est requise.", + "condition-duration" : "Durée de la condition", + "condition-duration-value" : "Valeur de la durée", + "condition-duration-time-unit" : "Unité de temps", + "condition-duration-value-range" : "La durée doit être comprise entre 1 et 2147483647.", + "condition-duration-value-pattern" : "La durée doit être un entier.", + "condition-duration-value-required" : "La valeur de la durée est requise.", + "condition-duration-time-unit-required" : "L'unité de temps est requise.", + "advanced-settings" : "Paramètres avancés", + "alarm-rule-additional-info" : "Informations supplémentaires", + "edit-alarm-rule-additional-info" : "Modifier les informations supplémentaires", + "alarm-rule-additional-info-placeholder" : "Ajoutez ici vos commentaires ou ajustements à afficher dans les détails de l'alarme", + "alarm-rule-additional-info-hint" : "Astuce : utilisez code>${keyName} pour insérer les valeurs des clés utilisées dans la condition.", + "alarm-rule-mobile-dashboard" : "Tableau de bord mobile", + "alarm-rule-mobile-dashboard-hint" : "Utilisé comme tableau de bord des détails de l'alarme dans l'application mobile", + "alarm-rule-no-mobile-dashboard" : "Aucun tableau de bord sélectionné", + "propagate-alarm" : "Propager l'alarme aux entités liées", + "alarm-rule-relation-types-list" : "Types de relations", + "alarm-rule-relation-types-list-hint" : "Définit les types de relations pour filtrer les entités liées. Si non défini, l'alarme sera propagée à toutes les entités liées.", + "propagate-alarm-to-owner" : "Propager l'alarme au propriétaire de l'entité (Client ou Locataire)", + "propagate-alarm-to-tenant" : "Propager l'alarme au Locataire", + "alarm-rule-condition" : "Condition de la règle d'alarme", + "enter-alarm-rule-condition-prompt" : "Veuillez ajouter une condition de règle d'alarme", + "edit-alarm-rule-condition" : "Modifier la condition de la règle d'alarme", + "device-provisioning" : "Approvisionnement de l'appareil", + "provision-strategy" : "Stratégie d'approvisionnement", + "provision-strategy-required" : "La stratégie d'approvisionnement est requise.", + "provision-strategy-disabled" : "Désactivée", + "provision-strategy-created-new" : "Permettre de créer de nouveaux appareils", + "provision-strategy-check-pre-provisioned" : "Vérifier les appareils préapprovisionnés", + "provision-device-key" : "Clé d'approvisionnement de l'appareil", + "provision-device-key-required" : "La clé d'approvisionnement de l'appareil est requise.", + "copy-provision-key" : "Copier la clé d'approvisionnement", + "provision-key-copied-message" : "La clé d'approvisionnement a été copiée dans le presse-papiers", + "provision-device-secret" : "Secret d'approvisionnement de l'appareil", + "provision-device-secret-required" : "Le secret d'approvisionnement de l'appareil est requis.", + "copy-provision-secret" : "Copier le secret d'approvisionnement", + "provision-secret-copied-message" : "Le secret d'approvisionnement a été copié dans le presse-papiers", + "provision-strategy-x509" : { + "certificate-chain" : "Chaîne de certificats X509", + "certificate-chain-hint" : "La stratégie de certificats X.509 est utilisée pour approvisionner les appareils via les certificats client en communication TLS bidirectionnelle.", + "allow-create-new-devices" : "Créer de nouveaux appareils", + "allow-create-new-devices-hint" : "Si sélectionné, de nouveaux appareils seront créés et le certificat client sera utilisé comme identifiants de l'appareil.", + "certificate-value" : "Certificat au format PEM", + "certificate-value-required" : "Le certificat au format PEM est requis", + "cn-regex-variable" : "Variable d'expression régulière CN", + "cn-regex-variable-required" : "La variable d'expression régulière CN est requise", + "cn-regex-variable-hint" : "Requis pour récupérer le nom de l'appareil depuis le nom commun du certificat X509 de l'appareil." + }, + "condition" : "Condition", + "condition-type" : "Type de condition", + "condition-type-simple" : "Simple", + "condition-type-duration" : "Durée", + "condition-during" : "Pendant {{during}}", + "condition-during-dynamic" : "Pendant \"{{ attribute }}\" ({{during}})", + "condition-type-repeating" : "Répétition", + "condition-type-required" : "Le type de condition est requis.", + "condition-repeating-value" : "Nombre d'événements", + "condition-repeating-value-range" : "Le nombre d'événements doit être compris entre 1 et 2147483647.", + "condition-repeating-value-pattern" : "Le nombre d'événements doit être un entier.", + "condition-repeating-value-required" : "Le nombre d'événements est requis.", + "condition-repeat-times" : "Se répète { count, plural, =1 {1 fois} other {# fois} }", + "condition-repeat-times-dynamic" : "Se répète \"{ attribute }\" ({ count, plural, =1 {1 fois} other {# fois} })", + "schedule-type" : "Type de planification", + "schedule-type-required" : "Le type de planification est requis.", + "schedule" : "Planification", + "edit-schedule" : "Modifier la planification de l'alarme", + "schedule-any-time" : "Actif en tout temps", + "schedule-specific-time" : "Actif à une heure spécifique", + "schedule-custom" : "Personnalisé", + "schedule-day" : { + "monday" : "Lundi", + "tuesday" : "Mardi", + "wednesday" : "Mercredi", + "thursday" : "Jeudi", + "friday" : "Vendredi", + "saturday" : "Samedi", + "sunday" : "Dimanche" + }, + "schedule-days" : "Jours", + "schedule-time" : "Heure", + "schedule-time-from" : "De", + "schedule-time-to" : "À", + "schedule-days-of-week-required" : "Au moins un jour de la semaine doit être sélectionné.", + "create-device-profile" : "Créer un nouveau profil d'appareil", + "import" : "Importer le profil d'appareil", + "export" : "Exporter le profil d'appareil", + "export-failed-error" : "Impossible d'exporter le profil d'appareil : {{error}}", + "device-profile-file" : "Fichier de profil d'appareil", + "invalid-device-profile-file-error" : "Impossible d'importer le profil d'appareil : structure de données invalide.", + "power-saving-mode" : "Mode d'économie d'énergie", + "power-saving-mode-type" : { + "default" : "Utiliser le mode d'économie d'énergie du profil d'appareil", + "psm" : "Mode d'économie d'énergie (PSM)", + "drx" : "Réception discontinue (DRX)", + "edrx" : "Réception discontinue étendue (eDRX)" + }, + "edrx-cycle" : "Cycle eDRX", + "edrx-cycle-required" : "Le cycle eDRX est requis.", + "edrx-cycle-pattern" : "Le cycle eDRX doit être un entier positif.", + "edrx-cycle-min" : "Le cycle eDRX minimum est de {{ min }} secondes.", + "paging-transmission-window" : "Fenêtre de transmission de pagination", + "paging-transmission-window-required" : "La fenêtre de transmission de pagination est requise.", + "paging-transmission-window-pattern" : "La fenêtre de transmission doit être un entier positif.", + "paging-transmission-window-min" : "Le minimum pour la fenêtre de transmission est de {{ min }} secondes.", + "psm-activity-timer" : "Minuteur d'activité PSM", + "psm-activity-timer-required" : "Le minuteur d'activité PSM est requis.", + "psm-activity-timer-pattern" : "Le minuteur PSM doit être un entier positif.", + "psm-activity-timer-min" : "Le minuteur PSM doit être au minimum de {{ min }} secondes.", + "lwm2m" : { + "object-list" : "Liste des objets", + "object-list-empty" : "Aucun objet sélectionné.", + "no-objects-found" : "Aucun objet trouvé.", + "no-objects-matching" : "Aucun objet correspondant à '{{object}}' trouvé.", + "model-tab" : "Modèle LWM2M", + "add-new-instances" : "Ajouter de nouvelles instances", + "instances-list" : "Liste des instances", + "instances-list-required" : "La liste des instances est requise.", + "instance-id-pattern" : "L'identifiant de l'instance doit être un entier positif.", + "instance-id-max" : "La valeur maximale de l'identifiant de l'instance est {{max}}.", + "instance" : "Instance", + "resource-label" : "#ID Nom de la ressource", + "observe-label" : "Observer", + "attribute-label" : "Attribut", + "telemetry-label" : "Télémétrie", + "edit-observe-select" : "Pour modifier, sélectionnez télémétrie ou attribut", + "edit-attributes-select" : "Pour modifier les attributs, sélectionnez télémétrie ou attribut", + "no-attributes-set" : "Aucun attribut défini", + "key-name" : "Nom de la clé", + "key-name-required" : "Le nom de la clé est requis", + "attribute-name" : "Nom de l'attribut", + "attribute-name-required" : "Le nom de l'attribut est requis.", + "attribute-value" : "Valeur de l'attribut", + "attribute-value-required" : "La valeur de l'attribut est requise.", + "attribute-value-pattern" : "La valeur doit être un entier positif.", + "edit-attributes" : "Modifier les attributs : {{ name }}", + "view-attributes" : "Voir les attributs : {{ name }}", + "add-attribute" : "Ajouter un attribut", + "edit-attribute" : "Modifier l'attribut", + "view-attribute" : "Voir l'attribut", + "remove-attribute" : "Supprimer l'attribut", + "delete-server-text" : "Attention, après confirmation, la configuration du serveur sera irrécupérable.", + "delete-server-title" : "Êtes-vous sûr de vouloir supprimer ce serveur ?", + "mode" : "Mode de configuration de sécurité", + "bootstrap-tab" : "Bootstrap", + "bootstrap-server-legend" : "Serveur Bootstrap (ShortId...)", + "lwm2m-server-legend" : "Serveur LwM2M (ShortId...)", + "server" : "Serveur", + "short-id" : "ID court du serveur", + "short-id-tooltip" : "ID court du serveur. Utilisé pour lier une instance d’objet serveur.\nCet identifiant identifie de manière unique chaque serveur LwM2M configuré pour le client.\nLa ressource DOIT être définie si Bootstrap-Server est à 'false'.\nLes ID :0 et ID :65535 ne DOIVENT PAS être utilisés.", + "short-id-tooltip-bootstrap" : "ID court du serveur. Utilisé pour lier une instance d’objet serveur.\nCet identifiant identifie de manière unique chaque serveur LwM2M configuré pour le client.\nLa ressource DOIT être définie si Bootstrap-Server est à 'false'.", + "short-id-required" : "L’ID court du serveur est requis.", + "short-id-range" : "L’ID court du serveur doit être compris entre {{ min }} et {{ max }}.", + "short-id-pattern" : "L’ID court doit être un entier positif.", + "lifetime" : "Durée d’enregistrement du client", + "lifetime-required" : "La durée d’enregistrement du client est requise.", + "lifetime-pattern" : "La durée d’enregistrement doit être un entier positif.", + "default-min-period" : "Période minimale entre deux notifications (s)", + "default-min-period-tooltip" : "Valeur par défaut que le client LwM2M doit utiliser comme période minimale d'observation en l'absence de paramètre.", + "default-min-period-required" : "La période minimale est requise.", + "default-min-period-pattern" : "La période minimale doit être un entier positif.", + "notification-storing" : "Stockage des notifications en cas de désactivation ou hors ligne", + "binding" : "Liaison", + "binding-type" : { + "u" : "U : Le client est joignable via la liaison UDP à tout moment.", + "m" : "M : Le client est joignable via la liaison MQTT à tout moment.", + "h" : "H : Le client est joignable via la liaison HTTP à tout moment.", + "t" : "T : Le client est joignable via la liaison TCP à tout moment.", + "s" : "S : Le client est joignable via la liaison SMS à tout moment.", + "n" : "N : Le client DOIT envoyer la réponse via la liaison Non-IP (prise en charge depuis LWM2M 1.1).", + "uq" : "UQ : Connexion UDP en mode file d'attente (non pris en charge depuis LWM2M 1.1)", + "uqs" : "UQS : Connexions UDP et SMS actives ; UDP en mode file d'attente, SMS en mode standard (non pris en charge depuis LWM2M 1.1)", + "tq" : "TQ : Connexion TCP en mode file d'attente (non pris en charge depuis LWM2M 1.1)", + "tqs" : "TQS : Connexions TCP et SMS actives ; TCP en mode file d'attente, SMS en mode standard (non pris en charge depuis LWM2M 1.1)", + "sq" : "SQ : Connexion SMS en mode file d'attente (non pris en charge depuis LWM2M 1.1)" + }, + "binding-tooltip" : "Il s'agit de la liste dans la ressource \"binding\" de l'objet serveur LwM2M - /1/x/7.\nIndique les modes de liaison pris en charge par le client LwM2M.\nCette valeur DOIT être identique à celle de la ressource “Supported Binding and Modes” dans l’objet Device (/3/0/16).\nBien que plusieurs transports soient pris en charge, un seul peut être utilisé pendant toute la session de transport.\nPar exemple, si UDP et SMS sont disponibles, le client et le serveur peuvent communiquer uniquement via l’un des deux durant la session.", + "bootstrap-server" : "Serveur Bootstrap", + "lwm2m-server" : "Serveur LwM2M", + "include-bootstrap-server" : "Inclure les mises à jour du serveur Bootstrap", + "bootstrap-update-title" : "Un serveur Bootstrap est déjà configuré. Êtes-vous sûr de vouloir exclure les mises à jour ?", + "bootstrap-update-text" : "Attention, après confirmation, la configuration du serveur Bootstrap sera irrécupérable.", + "server-host" : "Hôte", + "server-host-required" : "L'hôte est requis.", + "server-port" : "Port", + "server-port-required" : "Le port est requis.", + "server-port-pattern" : "Le port doit être un entier positif.", + "server-port-range" : "Le port doit être compris entre 1 et 65535.", + "server-public-key" : "Clé publique du serveur", + "server-public-key-required" : "La clé publique du serveur est requise.", + "client-hold-off-time" : "Temps d'attente du client", + "client-hold-off-time-required" : "Le temps d'attente du client est requis.", + "client-hold-off-time-pattern" : "Le temps d'attente doit être un entier positif.", + "client-hold-off-time-tooltip" : "Temps d'attente utilisé uniquement avec le serveur Bootstrap", + "account-after-timeout" : "Connexion après expiration", + "account-after-timeout-required" : "La connexion après expiration est requise.", + "account-after-timeout-pattern" : "La valeur doit être un entier positif.", + "account-after-timeout-tooltip" : "Valeur de délai pour l’inscription au serveur Bootstrap.", + "server-type" : "Type de serveur", + "add-new-server-title" : "Ajouter une nouvelle configuration de serveur", + "add-server-config" : "Ajouter la configuration du serveur", + "add-lwm2m-server-config" : "Ajouter un serveur LwM2M", + "no-config-servers" : "Aucun serveur configuré", + "others-tab" : "Autres paramètres", + "client-strategy" : "Stratégie client lors de la connexion", + "client-strategy-label" : "Stratégie", + "client-strategy-only-observe" : "Envoyer uniquement la requête d’observation après connexion initiale", + "client-strategy-read-all" : "Lire toutes les ressources & envoyer la requête d’observation après enregistrement", + "fw-update" : "Mise à jour du firmware", + "fw-update-strategy" : "Stratégie de mise à jour du firmware", + "fw-update-strategy-data" : "Pousser le firmware en tant que fichier binaire via Objet 19 Ressource 0 (Data)", + "fw-update-strategy-package" : "Pousser le firmware en tant que fichier binaire via Objet 5 Ressource 0 (Package)", + "fw-update-strategy-package-uri" : "Générer une URL CoAP unique et pousser le firmware via Objet 5 Ressource 1 (Package URI)", + "sw-update" : "Mise à jour du logiciel", + "sw-update-strategy" : "Stratégie de mise à jour du logiciel", + "sw-update-strategy-package" : "Pousser le fichier binaire via Objet 9 Ressource 2 (Package)", + "sw-update-strategy-package-uri" : "Générer une URL CoAP unique et pousser la mise à jour via Objet 9 Ressource 3 (Package URI)", + "fw-update-resource" : "Ressource CoAP pour mise à jour du firmware", + "fw-update-resource-required" : "La ressource de mise à jour du firmware est requise.", + "sw-update-resource" : "Ressource CoAP pour mise à jour du logiciel", + "sw-update-resource-required" : "La ressource de mise à jour du logiciel est requise.", + "config-json-tab" : "Configuration JSON du profil d’appareil", + "attributes-name" : { + "min-period" : "Période minimale", + "max-period" : "Période maximale", + "greater-than" : "Supérieur à", + "less-than" : "Inférieur à", + "step" : "Pas", + "min-evaluation-period" : "Période d’évaluation minimale", + "max-evaluation-period" : "Période d’évaluation maximale" + }, + "default-object-id" : "Version par défaut de l'objet (Attribut)", + "default-object-id-ver" : { + "v1-0" : "1.0", + "v1-1" : "1.1" + } + }, + "snmp" : { + "add-communication-config" : "Ajouter une configuration de communication", + "add-mapping" : "Ajouter une correspondance", + "authentication-passphrase" : "Phrase secrète d'authentification", + "authentication-passphrase-required" : "La phrase secrète d'authentification est requise.", + "authentication-protocol" : "Protocole d'authentification", + "authentication-protocol-required" : "Le protocole d'authentification est requis.", + "communication-configs" : "Configurations de communication", + "community" : "Chaîne de communauté", + "community-required" : "La chaîne de communauté est requise.", + "context-name" : "Nom du contexte", + "data-key" : "Clé de données", + "data-key-required" : "La clé de données est requise.", + "data-type" : "Type de données", + "data-type-required" : "Le type de données est requis.", + "engine-id" : "ID du moteur", + "host" : "Hôte", + "host-required" : "L'hôte est requis.", + "oid" : "OID", + "oid-pattern" : "Format OID invalide.", + "oid-required" : "L'OID est requis.", + "please-add-communication-config" : "Veuillez ajouter une configuration de communication", + "please-add-mapping-config" : "Veuillez ajouter une configuration de correspondance", + "port" : "Port", + "port-format" : "Format de port invalide.", + "port-required" : "Le port est requis.", + "privacy-passphrase" : "Phrase secrète de confidentialité", + "privacy-passphrase-required" : "La phrase secrète de confidentialité est requise.", + "privacy-protocol" : "Protocole de confidentialité", + "privacy-protocol-required" : "Le protocole de confidentialité est requis.", + "protocol-version" : "Version du protocole", + "protocol-version-required" : "La version du protocole est requise.", + "querying-frequency" : "Fréquence de requête, ms", + "querying-frequency-invalid-format" : "La fréquence de requête doit être un entier positif.", + "querying-frequency-required" : "La fréquence de requête est requise.", + "retries" : "Réessais", + "retries-invalid-format" : "Les réessais doivent être un entier positif.", + "retries-required" : "Les réessais sont requis.", + "scope" : "Portée", + "scope-required" : "La portée est requise.", + "security-name" : "Nom de sécurité", + "security-name-required" : "Le nom de sécurité est requis.", + "timeout-ms" : "Délai d'expiration, ms", + "timeout-ms-invalid-format" : "Le délai d'expiration doit être un entier positif.", + "timeout-ms-required" : "Le délai d'expiration est requis.", + "user-name" : "Nom d'utilisateur", + "user-name-required" : "Le nom d'utilisateur est requis." + } + }, + "dialog" : { + "close" : "Fermer la boîte de dialogue", + "error-message-title" : "Message d'erreur :", + "error-details-title" : "Détails de l'erreur" + }, + "direction" : { + "column" : "Colonne", + "row" : "Ligne" + }, + "edge" : { + "edge" : "Edge", + "edge-instances" : "Instances Edge", + "instances" : "Instances", + "edge-file" : "Fichier Edge", + "name-max-length" : "Le nom doit contenir moins de 256 caractères", + "label-max-length" : "Le label doit contenir moins de 256 caractères", + "type-max-length" : "Le type doit contenir moins de 256 caractères", + "management" : "Gestion des Edge", + "no-edges-matching" : "Aucun Edge correspondant à '{{entity}}' trouvé.", + "add" : "Ajouter un Edge", + "no-edges-text" : "Aucun Edge trouvé", + "edge-details" : "Détails du Edge", + "add-edge-text" : "Ajouter un nouveau Edge", + "delete" : "Supprimer le Edge", + "delete-edge-title" : "Êtes-vous sûr de vouloir supprimer le Edge '{{edgeName}}' ?", + "delete-edge-text" : "Attention, après confirmation, le Edge et toutes les données associées seront irrécupérables.", + "delete-edges-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 Edge} other {# Edge} } ?", + "delete-edges-text" : "Attention, après confirmation, tous les Edge sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "name" : "Nom", + "name-starts-with" : "Le nom du Edge commence par", + "name-required" : "Le nom est requis.", + "description" : "Description", + "details" : "Détails", + "events" : "Événements", + "copy-id" : "Copier l'ID du Edge", + "id-copied-message" : "L'ID du Edge a été copié dans le presse-papiers", + "sync" : "Synchroniser Edge", + "edge-required" : "Edge requis", + "edge-type" : "Type de Edge", + "edge-type-required" : "Le type de Edge est requis.", + "event-action" : "Action d'événement", + "entity-id" : "ID de l'entité", + "select-edge-type" : "Sélectionner le type de Edge", + "assign-to-customer" : "Assigner à un client", + "assign-to-customer-text" : "Veuillez sélectionner le client à qui assigner le(s) Edge", + "assign-edge-to-customer" : "Assigner le(s) Edge à un client", + "assign-edge-to-customer-text" : "Veuillez sélectionner les Edge à assigner au client", + "assignedToCustomer" : "Assigné à un client", + "edge-public" : "Le Edge est public", + "assigned-to-customer" : "Assigné à : {{customerTitle}}", + "unassign-from-customer" : "Désassigner du client", + "unassign-edge-title" : "Êtes-vous sûr de vouloir désassigner le Edge '{{edgeName}}' ?", + "unassign-edge-text" : "Après confirmation, le Edge sera désassigné et ne sera plus accessible au client.", + "unassign-edges-title" : "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 Edge} other {# Edge} } ?", + "unassign-edges-text" : "Après confirmation, tous les Edge sélectionnés seront désassignés et ne seront plus accessibles au client.", + "make-public" : "Rendre public", + "make-public-edge-title" : "Êtes-vous sûr de vouloir rendre le Edge '{{edgeName}}' public ?", + "make-public-edge-text" : "Après confirmation, le Edge et toutes ses données seront rendus publics et accessibles à tous.", + "make-private" : "Rendre privé", + "public" : "Public", + "make-private-edge-title" : "Êtes-vous sûr de vouloir rendre le Edge '{{edgeName}}' privé ?", + "make-private-edge-text" : "Après confirmation, le Edge et toutes ses données seront rendus privés et ne seront plus accessibles à d'autres.", + "import" : "Importer un Edge", + "install-connect-instructions" : "Instructions d'installation et de connexion", + "install-connect-instructions-edge-created" : "Edge créé ! Consultez les instructions d'installation et de connexion", + "loading-edge-instructions" : "Chargement des instructions pour Edge...", + "label" : "Label", + "load-entity-error" : "Échec du chargement des données. L'entité a été supprimée.", + "assign-new-edge" : "Attribuer une nouvelle passerelle", + "unassign-from-edge" : "Désattribuer de la passerelle", + "edge-key" : "Clé de la passerelle", + "copy-edge-key" : "Copier la clé de la passerelle", + "edge-key-copied-message" : "La clé de la passerelle a été copiée dans le presse-papiers", + "edge-secret" : "Secret de la passerelle", + "copy-edge-secret" : "Copier le secret de la passerelle", + "edge-secret-copied-message" : "Le secret de la passerelle a été copié dans le presse-papiers", + "manage-assets" : "Gérer les actifs", + "manage-devices" : "Gérer les appareils", + "manage-entity-views" : "Gérer les vues d'entité", + "manage-dashboards" : "Gérer les tableaux de bord", + "manage-rulechains" : "Gérer les chaînes de règles", + "assets" : "Actifs de la passerelle", + "devices" : "Appareils de la passerelle", + "entity-views" : "Vues d'entité de la passerelle", + "dashboard" : "Tableau de bord de la passerelle", + "dashboards" : "Tableaux de bord de la passerelle", + "rulechain-templates" : "Modèles de chaînes de règles", + "edge-rulechain-templates" : "Modèles de chaînes de règles pour passerelle", + "rulechains" : "Chaînes de règles de la passerelle", + "search" : "Rechercher des passerelles", + "selected-edges" : "{ count, plural, =1 {1 passerelle} other {# passerelles} } sélectionnée(s)", + "any-edge" : "Toute passerelle", + "no-edge-types-matching" : "Aucun type de passerelle correspondant à '{{entitySubtype}}' n'a été trouvé.", + "edge-type-list-empty" : "Aucun type de passerelle sélectionné.", + "edge-types" : "Types de passerelles", + "enter-edge-type" : "Saisir un type de passerelle", + "deployed" : "Déployée", + "pending" : "En attente", + "downlinks" : "Liaisons descendantes", + "no-downlinks-prompt" : "Aucune liaison descendante trouvée", + "sync-process-started-successfully" : "Le processus de synchronisation a démarré avec succès !", + "missing-related-rule-chains-title" : "La passerelle a des chaînes de règles liées manquantes", + "missing-related-rule-chains-text" : "Les chaînes de règles attribuées à la passerelle utilisent des nœuds de règle qui transfèrent des messages vers des chaînes de règles non attribuées à cette passerelle.

Liste des chaînes de règles manquantes :
{{missingRuleChains}}", + "widget-datasource-error" : "Ce widget ne prend en charge que les sources de données de type ENTITÉ PASSERELLE", + "upgrade-instructions" : "Instructions de mise à jour", + "connected" : "Connectée", + "disconnected" : "Déconnectée" + }, + "edge-event" : { + "type-dashboard" : "Tableau de bord", + "type-asset" : "Actif", + "type-device" : "Appareil", + "type-device-profile" : "Profil de l'appareil", + "type-asset-profile" : "Profil de l'actif", + "type-entity-view" : "Vue d'entité", + "type-alarm" : "Alarme", + "type-rule-chain" : "Chaîne de règles", + "type-rule-chain-metadata" : "Métadonnées de chaîne de règles", + "type-edge" : "Passerelle", + "type-user" : "Utilisateur", + "type-tenant" : "Locataire", + "type-tenant-profile" : "Profil de locataire", + "type-customer" : "Client", + "type-relation" : "Relation", + "type-widgets-bundle" : "Paquet de widgets", + "type-widgets-type" : "Type de widget", + "type-admin-settings" : "Paramètres d'administration", + "type-ota-package" : "Package OTA", + "type-queue" : "File d'attente", + "action-type-added" : "Ajouté", + "action-type-deleted" : "Supprimé", + "action-type-updated" : "Mis à jour", + "action-type-post-attributes" : "Publier des attributs", + "action-type-attributes-updated" : "Attributs mis à jour", + "action-type-attributes-deleted" : "Attributs supprimés", + "action-type-timeseries-updated" : "Série temporelle mise à jour", + "action-type-credentials-updated" : "Identifiants mis à jour", + "action-type-assigned-to-customer" : "Attribué à un client", + "action-type-unassigned-from-customer" : "Désattribué d'un client", + "action-type-relation-add-or-update" : "Ajout ou mise à jour d'une relation", + "action-type-relation-deleted" : "Relation supprimée", + "action-type-rpc-call" : "Appel RPC", + "action-type-alarm-ack" : "Accusé de réception d'alarme", + "action-type-alarm-clear" : "Réinitialisation de l'alarme", + "action-type-alarm-assigned" : "Alarme attribuée", + "action-type-alarm-unassigned" : "Alarme désattribuée", + "action-type-assigned-to-edge" : "Attribué à une passerelle", + "action-type-unassigned-from-edge" : "Désattribué d'une passerelle", + "action-type-credentials-request" : "Demande d'identifiants", + "action-type-entity-merge-request" : "Demande de fusion d'entités" + }, + "error" : { + "unable-to-connect" : "Impossible de se connecter au serveur ! Veuillez vérifier votre connexion Internet.", + "unhandled-error-code" : "Code d'erreur non géré : {{errorCode}}", + "unknown-error" : "Erreur inconnue" + }, + "entity" : { + "entity" : "Entité", + "entities" : "Entités", + "entities-count" : "Nombre d'entités", + "alarms-count" : "Nombre d'alarmes", + "aliases" : "Alias d'entités", + "aliases-short" : "Alias", + "entity-alias" : "Alias d'entité", + "unable-delete-entity-alias-title" : "Impossible de supprimer l'alias d'entité", + "unable-delete-entity-alias-text" : "L'alias d'entité '{{entityAlias}}' ne peut pas être supprimé car il est utilisé par le(s) widget(s) suivant(s) :
{{widgetsList}}", + "duplicate-alias-error" : "Alias en double trouvé '{{alias}}'.
Les alias d'entité doivent être uniques dans le tableau de bord.", + "missing-entity-filter-error" : "Filtre manquant pour l'alias '{{alias}}'.", + "configure-alias" : "Configurer l'alias '{{alias}}'", + "alias" : "Alias", + "alias-required" : "L'alias d'entité est requis.", + "remove-alias" : "Supprimer l'alias d'entité", + "add-alias" : "Ajouter un alias d'entité", + "edit-alias" : "Modifier l'alias d'entité", + "entity-list" : "Liste des entités", + "entity-type" : "Type d'entité", + "entity-types" : "Types d'entités", + "entity-type-list" : "Liste des types d'entités", + "any-entity" : "Toute entité", + "add-entity-type" : "Ajouter un type d'entité", + "enter-entity-type" : "Saisir un type d'entité", + "no-entities-matching" : "Aucune entité correspondant à '{{entity}}' n'a été trouvée.", + "no-entities-text" : "Aucune entité trouvée", + "no-entity-types-matching" : "Aucun type d'entité correspondant à '{{entityType}}' n'a été trouvé.", + "name-starts-with" : "Expression du nom", + "help-text" : "Utilisez '%' selon le besoin : '%nom_entité_contient%', '%nom_entité_se_termine', 'entité_commence_par'.", + "use-entity-name-filter" : "Utiliser le filtre", + "entity-list-empty" : "Aucune entité sélectionnée.", + "entity-type-list-required" : "Au moins un type d'entité doit être sélectionné.", + "entity-name-filter-required" : "Le filtre de nom d'entité est requis.", + "entity-name-filter-no-entity-matched" : "Aucune entité commençant par '{{entity}}' n'a été trouvée.", + "all-subtypes" : "Tous", + "select-entities" : "Sélectionner des entités", + "no-aliases-found" : "Aucun alias trouvé.", + "no-alias-matching" : "'{{alias}}' introuvable.", + "create-new-alias" : "Créer un nouveau !", + "create-new" : "Créer un nouveau", + "key" : "Clé", + "key-name" : "Nom de la clé", + "no-keys-found" : "Aucune clé trouvée.", + "no-key-matching" : "'{{key}}' introuvable.", + "create-new-key" : "Créer une nouvelle !", + "type" : "Type", + "type-required" : "Le type d'entité est requis.", + "type-device" : "Appareil", + "type-devices" : "Appareils", + "list-of-devices" : "{ count, plural, =1 {Un appareil} other {Liste de # appareils} }", + "device-name-starts-with" : "Appareils dont les noms commencent par '{{prefix}}'", + "type-device-profile" : "Profil de l'appareil", + "type-device-profiles" : "Profils d'appareils", + "clear-selected-profiles" : "Effacer les profils sélectionnés", + "list-of-device-profiles" : "{ count, plural, =1 {Un profil d'appareil} other {Liste de # profils d'appareils} }", + "device-profile-name-starts-with" : "Profils d'appareils dont les noms commencent par '{{prefix}}'", + "type-asset-profile" : "Profil d'actif", + "type-asset-profiles" : "Profils d'actifs", + "list-of-asset-profiles" : "{ count, plural, =1 {Un profil d'actif} other {Liste de # profils d'actifs} }", + "asset-profile-name-starts-with" : "Profils d'actifs dont les noms commencent par '{{prefix}}'", + "type-asset" : "Actif", + "type-assets" : "Actifs", + "list-of-assets" : "{ count, plural, =1 {Un actif} other {Liste de # actifs} }", + "asset-name-starts-with" : "Actifs dont les noms commencent par '{{prefix}}'", + "type-entity-view" : "Vue d'entité", + "type-entity-views" : "Vues d'entité", + "list-of-entity-views" : "{ count, plural, =1 {Une vue d'entité} other {Liste de # vues d'entités} }", + "entity-view-name-starts-with" : "Vues d'entités dont les noms commencent par '{{prefix}}'", + "type-rule" : "Règle", + "type-rules" : "Règles", + "list-of-rules" : "{ count, plural, =1 {Une règle} other {Liste de # règles} }", + "rule-name-starts-with" : "Règles dont les noms commencent par '{{prefix}}'", + "type-plugin" : "Plugin", + "type-plugins" : "Plugins", + "list-of-plugins" : "{ count, plural, =1 {Un plugin} other {Liste de # plugins} }", + "plugin-name-starts-with" : "Plugins dont les noms commencent par '{{prefix}}'", + "type-tenant" : "Locataire", + "type-tenants" : "Locataires", + "list-of-tenants" : "{ count, plural, =1 {Un locataire} other {Liste de # locataires} }", + "tenant-name-starts-with" : "Locataires dont les noms commencent par '{{prefix}}'", + "type-tenant-profile" : "Profil de locataire", + "type-tenant-profiles" : "Profils de locataire", + "list-of-tenant-profiles" : "{ count, plural, =1 {Un profil de locataire} other {Liste de # profils de locataire} }", + "tenant-profile-name-starts-with" : "Profils de locataires dont les noms commencent par '{{prefix}}'", + "type-customer" : "Client", + "type-customers" : "Clients", + "list-of-customers" : "{ count, plural, =1 {Un client} other {Liste de # clients} }", + "customer-name-starts-with" : "Clients dont les noms commencent par '{{prefix}}'", + "type-user" : "Utilisateur", + "type-users" : "Utilisateurs", + "list-of-users" : "{ count, plural, =1 {Un utilisateur} other {Liste de # utilisateurs} }", + "user-name-starts-with" : "Utilisateurs dont les noms commencent par '{{prefix}}'", + "type-dashboard" : "Tableau de bord", + "type-dashboards" : "Tableaux de bord", + "list-of-dashboards" : "{ count, plural, =1 {Un tableau de bord} other {Liste de # tableaux de bord} }", + "dashboard-name-starts-with" : "Tableaux de bord dont les noms commencent par '{{prefix}}'", + "type-alarm" : "Alarme", + "type-alarms" : "Alarmes", + "list-of-alarms" : "{ count, plural, =1 {Une alarme} other {Liste de # alarmes} }", + "alarm-name-starts-with" : "Alarmes dont les noms commencent par '{{prefix}}'", + "type-rulechain" : "Chaîne de règles", + "type-rulechains" : "Chaînes de règles", + "list-of-rulechains" : "{ count, plural, =1 {Une chaîne de règles} other {Liste de # chaînes de règles} }", + "rulechain-name-starts-with" : "Chaînes de règles dont les noms commencent par '{{prefix}}'", + "type-rulenode" : "Nœud de règle", + "type-rulenodes" : "Nœuds de règle", + "list-of-rulenodes" : "{ count, plural, =1 {Un nœud de règle} other {Liste de # nœuds de règle} }", + "rulenode-name-starts-with" : "Nœuds de règle dont les noms commencent par '{{prefix}}'", + "type-current-customer" : "Client actuel", + "type-current-tenant" : "Locataire actuel", + "type-current-user" : "Utilisateur actuel", + "type-current-user-owner" : "Propriétaire utilisateur actuel", + "type-calculated-field" : "Champ calculé", + "type-calculated-fields" : "Champs calculés", + "type-widgets-bundle" : "Pack de widgets", + "type-widgets-bundles" : "Packs de widgets", + "list-of-widgets-bundles" : "{ count, plural, =1 {Un pack de widgets} other {Liste de # packs de widgets} }", + "type-widget" : "Widget", + "type-widgets" : "Widgets", + "list-of-widgets" : "{ count, plural, =1 {Un widget} other {Liste de # widgets} }", + "search" : "Rechercher des entités", + "selected-entities" : "{ count, plural, =1 {1 entité} other {# entités} } sélectionnée(s)", + "entity-name" : "Nom de l'entité", + "entity-label" : "Étiquette de l'entité", + "details" : "Détails de l'entité", + "no-entities-prompt" : "Aucune entité trouvée", + "no-data" : "Aucune donnée à afficher", + "columns-to-display" : "Colonnes à afficher", + "type-api-usage-state" : "État d'utilisation de l'API", + "type-edge" : "Edge", + "type-edges" : "Edges", + "list-of-edges" : "{ count, plural, =1 {Un edge} other {Liste de # edges} }", + "edge-name-starts-with" : "Edges dont les noms commencent par '{{prefix}}'", + "version-conflict" : { + "message" : "Souhaitez-vous écraser la version existante ou annuler les modifications et charger la dernière version ?", + "link" : "Vous pouvez télécharger votre version de {{entityType}} en utilisant ce", + "overwrite" : "Écraser la version", + "discard" : "Annuler les modifications" + }, + "type-tb-resource" : "Ressource", + "type-tb-resources" : "Ressources", + "list-of-tb-resources" : "{ count, plural, =1 {Une ressource} other {Liste de # ressources} }", + "type-ota-package" : "Paquet OTA", + "type-rpc" : "RPC", + "type-queue" : "File d'attente", + "type-queue-stats" : "Statistiques de file d'attente", + "type-queues-stats" : "Statistiques des files d'attente", + "type-notification" : "Notification", + "type-notification-rule" : "Règle de notification", + "type-notification-rules" : "Règles de notification", + "list-of-notification-rules" : "{ count, plural, =1 {Une règle de notification} other {Liste de # règles de notification} }", + "type-notification-target" : "Destinataire de notification", + "type-notification-targets" : "Destinataires de notification", + "list-of-notification-targets" : "{ count, plural, =1 {Un destinataire de notification} other {Liste de # destinataires de notification} }", + "type-notification-request" : "Demande de notification", + "type-notification-template" : "Modèle de notification", + "type-notification-templates" : "Modèles de notification", + "list-of-notification-templates" : "{ count, plural, =1 {Un modèle de notification} other {Liste de # modèles de notification} }", + "link" : "lien", + "type-oauth2-client" : "Client OAuth 2.0", + "type-oauth2-clients" : "Clients OAuth 2.0", + "list-of-oauth2-clients" : "{ count, plural, =1 {Un client OAuth 2.0} other {Liste de # clients OAuth 2.0} }", + "type-domain" : "Domaine", + "type-domains" : "Domaines", + "list-of-domains" : "{ count, plural, =1 {Un domaine} other {Liste de # domaines} }", + "type-mobile-app" : "Application mobile", + "type-mobile-apps" : "Applications mobiles", + "list-of-mobile-apps" : "{ count, plural, =1 {Une application mobile} other {Liste de # applications mobiles} }", + "type-mobile-app-bundle" : "Pack mobile", + "type-mobile-app-bundles" : "Packs mobiles", + "list-of-mobile-app-bundles" : "{ count, plural, =1 {Un pack mobile} other {Liste de # packs mobiles} }" + }, + "entity-field" : { + "created-time" : "Date de création", + "name" : "Nom", + "type" : "Type", + "first-name" : "Prénom", + "last-name" : "Nom", + "email" : "Email", + "title" : "Titre", + "country" : "Pays", + "state" : "État", + "city" : "Ville", + "address" : "Adresse", + "address2" : "Adresse 2", + "zip" : "Code postal", + "phone" : "Téléphone", + "label" : "Étiquette", + "queue-name" : "Nom de la file", + "service-id" : "ID du service", + "owner-name" : "Nom du propriétaire", + "owner-type" : "Type de propriétaire" + }, + "entity-view" : { + "entity-view" : "Vue d'entité", + "entity-view-required" : "La vue d'entité est requise.", + "entity-views" : "Vues d'entité", + "management" : "Gestion des vues d'entité", + "view-entity-views" : "Voir les vues d'entité", + "entity-view-alias" : "Alias de la vue d'entité", + "aliases" : "Alias des vues d'entité", + "no-alias-matching" : "'{{alias}}' introuvable.", + "no-aliases-found" : "Aucun alias trouvé.", + "no-key-matching" : "'{{key}}' introuvable.", + "no-keys-found" : "Aucune clé trouvée.", + "create-new-alias" : "Créer un nouveau !", + "create-new-key" : "Créer un nouveau !", + "duplicate-alias-error" : "Alias en double '{{alias}}'.
Les alias des vues d'entité doivent être uniques dans le tableau de bord.", + "configure-alias" : "Configurer l'alias '{{alias}}'", + "no-entity-views-matching" : "Aucune vue d'entité correspondant à '{{entity}}' trouvée.", + "public" : "Public", + "alias" : "Alias", + "alias-required" : "L'alias de la vue d'entité est requis.", + "remove-alias" : "Supprimer l'alias de la vue d'entité", + "add-alias" : "Ajouter un alias de vue d'entité", + "name-starts-with" : "Expression du nom de la vue d'entité", + "help-text" : "Utilisez '%' selon le besoin : '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list" : "Liste des vues d'entité", + "use-entity-view-name-filter" : "Utiliser un filtre", + "entity-view-list-empty" : "Aucune vue d'entité sélectionnée.", + "entity-view-name-filter-required" : "Le filtre de nom de vue d'entité est requis.", + "entity-view-name-filter-no-entity-view-matched" : "Aucune vue d'entité commençant par '{{entityView}}' trouvée.", + "add" : "Ajouter une vue d'entité", + "entity-view-public" : "La vue d'entité est publique", + "assign-to-customer" : "Assigner à un client", + "assign-entity-view-to-customer" : "Assigner des vues d'entité au client", + "assign-entity-view-to-customer-text" : "Veuillez sélectionner les vues d'entité à assigner au client", + "no-entity-views-text" : "Aucune vue d'entité trouvée", + "assign-to-customer-text" : "Veuillez sélectionner le client à qui assigner la/les vue(s) d'entité", + "entity-view-details" : "Détails de la vue d'entité", + "add-entity-view-text" : "Ajouter une nouvelle vue d'entité", + "delete" : "Supprimer la vue d'entité", + "assign-entity-views" : "Assigner des vues d'entité", + "assign-entity-views-text" : "Assigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } au client", + "delete-entity-views" : "Supprimer les vues d'entité", + "make-public" : "Rendre la vue publique", + "make-private" : "Rendre la vue privée", + "unassign-from-customer" : "Désassigner du client", + "unassign-entity-views" : "Désassigner les vues d'entité", + "unassign-entity-views-action-title" : "Désassigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } du client", + "assign-new-entity-view" : "Assigner une nouvelle vue d'entité", + "delete-entity-view-title" : "Êtes-vous sûr de vouloir supprimer la vue d'entité '{{entityViewName}}' ?", + "delete-entity-view-text" : "Attention, après confirmation, la vue d'entité et toutes ses données seront irrécupérables.", + "delete-entity-views-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } ?", + "delete-entity-views-action-title" : "Supprimer { count, plural, =1 {1 vue d'entité} other {# vues d'entité} }", + "delete-entity-views-text" : "Attention, après confirmation, toutes les vues d'entité sélectionnées seront supprimées avec leurs données.", + "make-public-entity-view-title" : "Êtes-vous sûr de vouloir rendre la vue d'entité '{{entityViewName}}' publique ?", + "make-public-entity-view-text" : "Après confirmation, la vue d'entité et ses données seront rendues publiques.", + "make-private-entity-view-title" : "Êtes-vous sûr de vouloir rendre la vue d'entité '{{entityViewName}}' privée ?", + "make-private-entity-view-text" : "Après confirmation, la vue d'entité et ses données seront rendues privées.", + "unassign-entity-view-title" : "Êtes-vous sûr de vouloir désassigner la vue d'entité '{{entityViewName}}' ?", + "unassign-entity-view-text" : "Après confirmation, la vue d'entité ne sera plus accessible au client.", + "unassign-entity-view" : "Désassigner la vue d'entité", + "unassign-entity-views-title" : "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } ?", + "unassign-entity-views-text" : "Après confirmation, toutes les vues d'entité sélectionnées seront désassignées.", + "entity-view-type" : "Type de vue d'entité", + "entity-view-type-required" : "Le type de vue d'entité est requis.", + "select-entity-view-type" : "Sélectionner le type de vue d'entité", + "enter-entity-view-type" : "Entrer le type de vue d'entité", + "any-entity-view" : "Toute vue d'entité", + "no-entity-view-types-matching" : "Aucun type de vue d'entité correspondant à '{{entitySubtype}}' trouvé.", + "entity-view-type-list-empty" : "Aucun type de vue d'entité sélectionné.", + "entity-view-types" : "Types de vues d'entité", + "created-time" : "Date de création", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "name-max-length" : "Le nom doit contenir moins de 256 caractères", + "type-max-length" : "Le type de vue d'entité doit contenir moins de 256 caractères", + "description" : "Description", + "events" : "Événements", + "details" : "Détails", + "copyId" : "Copier l'ID de la vue d'entité", + "idCopiedMessage" : "L'ID de la vue d'entité a été copié dans le presse-papiers", + "assignedToCustomer" : "Assigné à un client", + "unable-entity-view-device-alias-title" : "Impossible de supprimer l'alias de la vue d'entité", + "unable-entity-view-device-alias-text" : "L'alias de l'appareil '{{entityViewAlias}}' ne peut pas être supprimé car il est utilisé par les widgets suivants :
{{widgetsList}}", + "select-entity-view" : "Sélectionner une vue d'entité", + "start-ts" : "Heure de début", + "end-ts" : "Heure de fin", + "date-limits" : "Limites de date", + "client-attributes" : "Attributs client", + "shared-attributes" : "Attributs partagés", + "server-attributes" : "Attributs serveur", + "timeseries" : "Séries temporelles", + "client-attributes-placeholder" : "Attributs client", + "shared-attributes-placeholder" : "Attributs partagés", + "server-attributes-placeholder" : "Attributs serveur", + "timeseries-placeholder" : "Séries temporelles", + "target-entity" : "Entité cible", + "attributes-propagation" : "Propagation des attributs", + "attributes-propagation-hint" : "La vue d'entité copiera automatiquement les attributs spécifiés de l'entité cible chaque fois que vous enregistrez ou mettez à jour cette vue d'entité. Pour des raisons de performance, les attributs ne sont pas propagés automatiquement à chaque changement. Activez cette propagation via le nœud de règle « copy to view » lié aux messages « Post attributes » et « Attributes Updated ».", + "timeseries-data" : "Données de séries temporelles", + "timeseries-data-hint" : "Configurez les clés de séries temporelles de l'entité cible qui seront accessibles dans la vue d'entité. Ces données sont en lecture seule.", + "search" : "Rechercher des vues d'entité", + "selected-entity-views" : "{ count, plural, =1 {1 vue d'entité} other {# vues d'entité} } sélectionnée(s)", + "assign-entity-view-to-edge" : "Assigner des vues d'entité à l'Edge", + "assign-entity-view-to-edge-text" : "Veuillez sélectionner les vues d'entité à assigner à l'Edge", + "unassign-entity-view-from-edge-title" : "Êtes-vous sûr de vouloir désassigner la vue d'entité '{{entityViewName}}' ?", + "unassign-entity-view-from-edge-text" : "Après confirmation, la vue d'entité sera désassignée et ne sera plus accessible par l'Edge.", + "unassign-entity-views-from-edge-action-title" : "Désassigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } de l'Edge", + "unassign-entity-view-from-edge" : "Désassigner la vue d'entité", + "unassign-entity-views-from-edge-title" : "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } ?", + "unassign-entity-views-from-edge-text" : "Après confirmation, toutes les vues d'entité sélectionnées seront désassignées et ne seront plus accessibles par l'Edge." + }, + "event" : { + "event-type" : "Type d'événement", + "events-filter" : "Filtre d'événements", + "clean-events" : "Effacer les événements", + "type-error" : "Erreur", + "type-lc-event" : "Événement de cycle de vie", + "type-stats" : "Statistiques", + "type-debug-rule-node" : "Débogage", + "type-debug-rule-chain" : "Débogage", + "type-debug-calculated-field" : "Débogage", + "arguments" : "Arguments", + "result" : "Résultat", + "no-events-prompt" : "Aucun événement trouvé", + "error" : "Erreur", + "alarm" : "Alarme", + "event-time" : "Heure de l'événement", + "server" : "Serveur", + "body" : "Contenu", + "method" : "Méthode", + "type" : "Type", + "metadata" : "Métadonnées", + "message" : "Message", + "message-id" : "ID du message", + "copy-message-id" : "Copier l'ID du message", + "message-type" : "Type de message", + "data-type" : "Type de données", + "relation-type" : "Type de relation", + "data" : "Données", + "event" : "Événement", + "status" : "Statut", + "success" : "Succès", + "failed" : "Échec", + "messages-processed" : "Messages traités", + "max-messages-processed" : "Messages traités max.", + "min-messages-processed" : "Messages traités min.", + "errors-occurred" : "Erreurs survenues", + "max-errors-occurred" : "Erreurs max.", + "min-errors-occurred" : "Erreurs min.", + "min-value" : "La valeur minimale est 0.", + "all-events" : "Tous", + "has-error" : "Contient une erreur", + "entity-id" : "ID de l'entité", + "copy-entity-id" : "Copier l'ID de l'entité", + "entity-type" : "Type d'entité", + "clear-filter" : "Réinitialiser le filtre", + "clear-request-title" : "Effacer tous les événements", + "clear-request-text" : "Êtes-vous sûr de vouloir effacer tous les événements ?", + "started" : "Démarré", + "updated" : "Mis à jour", + "stopped" : "Arrêté" + }, + "extension" : { + "extensions" : "Extensions", + "selected-extensions" : "{ count, plural, =1 {1 extension} other {# extensions} } sélectionnée(s)", + "type" : "Type", + "key" : "Clé", + "value" : "Valeur", + "id" : "ID", + "extension-id" : "ID de l'extension", + "extension-type" : "Type d'extension", + "transformer-json" : "JSON *", + "unique-id-required" : "L'ID de l'extension existe déjà.", + "delete" : "Supprimer l'extension", + "add" : "Ajouter une extension", + "edit" : "Modifier l'extension", + "delete-extension-title" : "Êtes-vous sûr de vouloir supprimer l'extension '{{extensionId}}' ?", + "delete-extension-text" : "Attention, après confirmation, l'extension et toutes les données associées seront irrécupérables.", + "delete-extensions-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 extension} other {# extensions} } ?", + "delete-extensions-text" : "Attention, après confirmation, toutes les extensions sélectionnées seront supprimées.", + "converters" : "Convertisseurs", + "converter-id" : "ID du convertisseur", + "configuration" : "Configuration", + "converter-configurations" : "Configurations de convertisseur", + "token" : "Jeton de sécurité", + "add-converter" : "Ajouter un convertisseur", + "add-config" : "Ajouter une configuration de convertisseur", + "device-name-expression" : "Expression du nom de l'appareil", + "device-type-expression" : "Expression du type d'appareil", + "custom" : "Personnalisé", + "to-double" : "Convertir en double", + "transformer" : "Transformateur", + "json-required" : "Le JSON du transformateur est requis.", + "json-parse" : "Impossible d'analyser le JSON du transformateur.", + "attributes" : "Attributs", + "add-attribute" : "Ajouter un attribut", + "add-map" : "Ajouter un élément de mappage", + "timeseries" : "Séries temporelles", + "add-timeseries" : "Ajouter une série temporelle", + "field-required" : "Champ requis", + "brokers" : "Courtiers", + "add-broker" : "Ajouter un courtier", + "host" : "Hôte", + "port" : "Port", + "port-range" : "Le port doit être compris entre 1 et 65535.", + "ssl" : "Ssl", + "credentials" : "Identifiants", + "username" : "Nom d'utilisateur", + "password" : "Mot de passe", + "retry-interval" : "Intervalle de réessai en millisecondes", + "anonymous" : "Anonyme", + "basic" : "Basique", + "pem" : "PEM", + "ca-cert" : "Fichier de certificat CA *", + "private-key" : "Fichier de clé privée *", + "cert" : "Fichier de certificat *", + "no-file" : "Aucun fichier sélectionné.", + "drop-file" : "Déposez un fichier ou cliquez pour en sélectionner un à télécharger.", + "mapping" : "Mappage", + "topic-filter" : "Filtre de sujet", + "converter-type" : "Type de convertisseur", + "converter-json" : "Json", + "json-name-expression" : "Expression JSON du nom de l'appareil", + "topic-name-expression" : "Expression du nom de l'appareil dans le sujet", + "json-type-expression" : "Expression JSON du type de l'appareil", + "topic-type-expression" : "Expression du type de l'appareil dans le sujet", + "attribute-key-expression" : "Expression de la clé d'attribut", + "attr-json-key-expression" : "Expression JSON de la clé d'attribut", + "attr-topic-key-expression" : "Expression de la clé d'attribut dans le sujet", + "request-id-expression" : "Expression de l'ID de requête", + "request-id-json-expression" : "Expression JSON de l'ID de requête", + "request-id-topic-expression" : "Expression de l'ID de requête dans le sujet", + "response-topic-expression" : "Expression du sujet de réponse", + "value-expression" : "Expression de la valeur", + "topic" : "Sujet", + "timeout" : "Délai d'attente en millisecondes", + "converter-json-required" : "Le JSON du convertisseur est requis.", + "converter-json-parse" : "Impossible d'analyser le JSON du convertisseur.", + "filter-expression" : "Expression de filtre", + "connect-requests" : "Requêtes de connexion", + "add-connect-request" : "Ajouter une requête de connexion", + "disconnect-requests" : "Requêtes de déconnexion", + "add-disconnect-request" : "Ajouter une requête de déconnexion", + "attribute-requests" : "Requêtes d'attribut", + "add-attribute-request" : "Ajouter une requête d'attribut", + "attribute-updates" : "Mises à jour des attributs", + "add-attribute-update" : "Ajouter une mise à jour d'attribut", + "server-side-rpc" : "RPC côté serveur", + "add-server-side-rpc-request" : "Ajouter une requête RPC côté serveur", + "device-name-filter" : "Filtre de nom de l'appareil", + "attribute-filter" : "Filtre d'attribut", + "method-filter" : "Filtre de méthode", + "request-topic-expression" : "Expression du sujet de requête", + "response-timeout" : "Délai de réponse en millisecondes", + "topic-expression" : "Expression du sujet", + "client-scope" : "Portée du client", + "add-device" : "Ajouter un appareil", + "opc-server" : "Serveurs", + "opc-add-server" : "Ajouter un serveur", + "opc-add-server-prompt" : "Veuillez ajouter un serveur", + "opc-application-name" : "Nom de l'application", + "opc-application-uri" : "URI de l'application", + "opc-scan-period-in-seconds" : "Période de scan en secondes", + "opc-security" : "Sécurité", + "opc-identity" : "Identité", + "opc-keystore" : "Keystore", + "opc-type" : "Type", + "opc-keystore-type" : "Type", + "opc-keystore-location" : "Emplacement *", + "opc-keystore-password" : "Mot de passe", + "opc-keystore-alias" : "Alias", + "opc-keystore-key-password" : "Mot de passe de la clé", + "opc-device-node-pattern" : "Motif de nœud de l'appareil", + "opc-device-name-pattern" : "Motif du nom de l'appareil", + "modbus-server" : "Serveurs/esclaves", + "modbus-add-server" : "Ajouter un serveur/esclave", + "modbus-add-server-prompt" : "Veuillez ajouter un serveur/esclave", + "modbus-transport" : "Transport", + "modbus-tcp-reconnect" : "Reconnecter automatiquement", + "modbus-rtu-over-tcp" : "RTU sur TCP", + "modbus-port-name" : "Nom du port série", + "modbus-encoding" : "Encodage", + "modbus-parity" : "Parité", + "modbus-baudrate" : "Vitesse de transmission", + "modbus-databits" : "Bits de données", + "modbus-stopbits" : "Bits de stop", + "modbus-databits-range" : "Les bits de données doivent être compris entre 7 et 8.", + "modbus-stopbits-range" : "Les bits de stop doivent être compris entre 1 et 2.", + "modbus-unit-id" : "ID d'unité", + "modbus-unit-id-range" : "L'ID d'unité doit être compris entre 1 et 247.", + "modbus-device-name" : "Nom de l'appareil", + "modbus-poll-period" : "Période d'interrogation (ms)", + "modbus-attributes-poll-period" : "Période d'interrogation des attributs (ms)", + "modbus-timeseries-poll-period" : "Période d'interrogation des séries temporelles (ms)", + "modbus-poll-period-range" : "La période d'interrogation doit être une valeur positive.", + "modbus-tag" : "Étiquette", + "modbus-function" : "Fonction", + "modbus-register-address" : "Adresse du registre", + "modbus-register-address-range" : "L'adresse du registre doit être comprise entre 0 et 65535.", + "modbus-register-bit-index" : "Index de bit", + "modbus-register-bit-index-range" : "L'index de bit doit être compris entre 0 et 15.", + "modbus-register-count" : "Nombre de registres", + "modbus-register-count-range" : "Le nombre de registres doit être une valeur positive.", + "modbus-byte-order" : "Ordre des octets", + "sync" : { + "status" : "Statut", + "sync" : "Synchronisé", + "not-sync" : "Non synchronisé", + "last-sync-time" : "Dernière synchronisation", + "not-available" : "Non disponible" + }, + "export-extensions-configuration" : "Exporter la configuration des extensions", + "import-extensions-configuration" : "Importer la configuration des extensions", + "import-extensions" : "Importer des extensions", + "import-extension" : "Importer une extension", + "export-extension" : "Exporter une extension", + "file" : "Fichier d’extensions", + "invalid-file-error" : "Fichier d’extension invalide" + }, + "feature" : { + "advanced-features" : "Fonctionnalités avancées" + }, + "filter" : { + "add" : "Ajouter un filtre", + "edit" : "Modifier le filtre", + "name" : "Nom du filtre", + "name-required" : "Le nom du filtre est requis.", + "duplicate-filter" : "Un filtre portant le même nom existe déjà.", + "filters" : "Filtres", + "unable-delete-filter-title" : "Impossible de supprimer le filtre", + "unable-delete-filter-text" : "Le filtre '{{filter}}' ne peut pas être supprimé car il est utilisé par les widgets suivants :
{{widgetsList}}", + "duplicate-filter-error" : "Filtre en double trouvé '{{filter}}'.
Les filtres doivent être uniques au sein du tableau de bord.", + "missing-key-filters-error" : "Les filtres clés sont manquants pour le filtre '{{filter}}'.", + "filter" : "Filtre", + "editable" : "Éditable", + "no-filters-found" : "Aucun filtre trouvé.", + "no-filter-text" : "Aucun filtre spécifié", + "add-filter-prompt" : "Veuillez ajouter un filtre", + "no-filter-matching" : "'{{filter}}' introuvable.", + "create-new-filter" : "Créer un nouveau !", + "create-new" : "Créer nouveau", + "filter-required" : "Le filtre est requis.", + "operation" : { + "operation" : "Opération", + "equal" : "égal à", + "not-equal" : "différent de", + "starts-with" : "commence par", + "ends-with" : "se termine par", + "contains" : "contient", + "not-contains" : "ne contient pas", + "greater" : "supérieur à", + "less" : "inférieur à", + "greater-or-equal" : "supérieur ou égal à", + "less-or-equal" : "inférieur ou égal à", + "and" : "et", + "or" : "ou", + "in" : "dans", + "not-in" : "pas dans" + }, + "ignore-case" : "ignorer la casse", + "value" : "Valeur", + "remove-filter" : "Supprimer le filtre", + "duplicate-filter-action" : "Dupliquer le filtre", + "preview" : "Aperçu du filtre", + "no-filters" : "Aucun filtre configuré", + "add-filter" : "Ajouter un filtre", + "add-complex-filter" : "Ajouter un filtre complexe", + "add-complex" : "Ajouter complexe", + "complex-filter" : "Filtre complexe", + "edit-complex-filter" : "Modifier le filtre complexe", + "edit-filter-user-params" : "Modifier les paramètres utilisateur du prédicat du filtre", + "filter-user-params" : "Paramètres utilisateur du prédicat du filtre", + "user-parameters" : "Paramètres utilisateur", + "display-label" : "Libellé à afficher", + "order-priority" : "Priorité d'ordre des champs", + "key-filter" : "Filtre par clé", + "key-filters" : "Filtres par clé", + "key-name" : "Nom de la clé", + "key-name-required" : "Le nom de la clé est requis.", + "key-type" : { + "key-type" : "Type de clé", + "attribute" : "Attribut", + "timeseries" : "Séries temporelles", + "entity-field" : "Champ d'entité", + "constant" : "Constante", + "client-attribute" : "Attribut client", + "server-attribute" : "Attribut serveur", + "shared-attribute" : "Attribut partagé" + }, + "value-type" : { + "value-type" : "Type de valeur", + "string" : "Chaîne", + "numeric" : "Numérique", + "boolean" : "Booléen", + "date-time" : "Date/Heure" + }, + "value-type-required" : "Le type de valeur de la clé est requis.", + "key-value-type-change-title" : "Êtes-vous sûr de vouloir changer le type de valeur de la clé ?", + "key-value-type-change-message" : "Si vous confirmez le nouveau type de valeur, tous les filtres de clés saisis seront supprimés.", + "no-key-filters" : "Aucun filtre de clé configuré", + "add-key-filter" : "Ajouter un filtre de clé", + "remove-key-filter" : "Supprimer le filtre de clé", + "edit-key-filter" : "Modifier le filtre de clé", + "date" : "Date", + "time" : "Heure", + "current-tenant" : "Locataire actuel", + "current-customer" : "Client actuel", + "current-user" : "Utilisateur actuel", + "current-device" : "Appareil actuel", + "default-value" : "Valeur par défaut", + "default-comma-separated-values" : "Valeurs par défaut séparées par des virgules", + "dynamic-source-type" : "Type de source dynamique", + "dynamic-value" : "Valeur dynamique", + "no-dynamic-value" : "Aucune valeur dynamique", + "source-attribute" : "Attribut source", + "switch-to-dynamic-value" : "Basculer vers la valeur dynamique", + "switch-to-default-value" : "Basculer vers la valeur par défaut", + "inherit-owner" : "Hériter du propriétaire", + "source-attribute-not-set" : "Si l'attribut source n'est pas défini" + }, + "fullscreen" : { + "expand" : "Agrandir en plein écran", + "exit" : "Quitter le plein écran", + "toggle" : "Basculer en mode plein écran", + "fullscreen" : "Plein écran" + }, + "function" : { + "function" : "Fonction" + }, + "gateway" : { + "gateway-name" : "Nom de la passerelle", + "gateway-name-required" : "Le nom de la passerelle est requis.", + "gateways" : "Passerelles", + "create-new-gateway" : "Créer une nouvelle passerelle", + "create-new-gateway-text" : "Êtes-vous sûr de vouloir créer une nouvelle passerelle avec le nom : '{{gatewayName}}' ?", + "launch-command" : "Commande de lancement", + "no-gateway-found" : "Aucune passerelle trouvée.", + "no-gateway-matching" : "'{{item}}' introuvable." + }, + "grid" : { + "delete-item-title" : "Êtes-vous sûr de vouloir supprimer cet élément ?", + "delete-item-text" : "Attention, après confirmation, cet élément et toutes les données associées seront irrécupérables.", + "delete-items-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 élément} other {# éléments} } ?", + "delete-items-action-title" : "Supprimer { count, plural, =1 {1 élément} other {# éléments} }", + "delete-items-text" : "Attention, après confirmation, tous les éléments sélectionnés seront supprimés ainsi que toutes les données associées.", + "add-item-text" : "Ajouter un nouvel élément", + "no-items-text" : "Aucun élément trouvé", + "item-details" : "Détails de l’élément", + "delete-item" : "Supprimer l’élément", + "delete-items" : "Supprimer les éléments", + "scroll-to-top" : "Retour en haut" + }, + "help" : { + "goto-help-page" : "Aller à la page d'aide", + "show-help" : "Afficher l’aide" + }, + "home" : { + "home" : "Accueil", + "profile" : "Profil", + "logout" : "Déconnexion", + "menu" : "Menu", + "avatar" : "Avatar", + "open-user-menu" : "Ouvrir le menu utilisateur" + }, + "file-input" : { + "browse-file" : "Parcourir un fichier", + "browse-files" : "Parcourir des fichiers" + }, + "image" : { + "gallery" : "Galerie d'images", + "search" : "Rechercher une image", + "selected-images" : "{ count, plural, =1 {1 image} other {# images} } sélectionnée(s)", + "created-time" : "Date de création", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "resolution" : "Résolution", + "size" : "Taille", + "system" : "Système", + "download-image" : "Télécharger l'image", + "export-image" : "Exporter l'image au format JSON", + "import-image" : "Importer une image depuis un fichier JSON", + "upload-image" : "Téléverser une image", + "edit-image" : "Modifier l'image", + "image-details" : "Détails de l'image", + "no-images" : "Aucune image trouvée", + "delete-image" : "Supprimer l'image", + "delete-image-title" : "Êtes-vous sûr de vouloir supprimer l'image '{{imageTitle}}' ?", + "delete-image-text" : "Attention, après confirmation, l'image ne pourra pas être récupérée.", + "delete-images-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 image} other {# images} } ?", + "delete-images-text" : "Attention, après confirmation, toutes les images sélectionnées seront supprimées ainsi que toutes les données associées.", + "list-mode" : "Vue en liste", + "grid-mode" : "Vue en grille", + "image-preview" : "Aperçu de l'image", + "update-image" : "Mettre à jour l'image", + "export-failed-error" : "Impossible d'exporter l'image : {{error}}", + "image-json-file" : "Fichier JSON d'image", + "invalid-image-json-file-error" : "Impossible d'importer l'image : structure JSON invalide.", + "image-is-in-use" : "L'image est utilisée par d'autres entités", + "images-are-in-use" : "Certaines images sont utilisées par d'autres entités", + "image-is-in-use-text" : "L'image '{{title}}' n'a pas été supprimée car elle est utilisée par les entités suivantes :", + "images-are-in-use-text" : "Toutes les images n'ont pas été supprimées car elles sont utilisées par d'autres entités.
Vous pouvez voir les entités référencées en cliquant sur le bouton Références dans la ligne de l'image concernée.
Si vous souhaitez tout de même les supprimer, sélectionnez-les dans le tableau ci-dessous puis cliquez sur le bouton Supprimer la sélection.", + "delete-image-in-use-text" : "Si vous souhaitez tout de même supprimer l'image, cliquez sur le bouton Supprimer quand même.", + "system-entities" : "Entités système :", + "entities" : "entités :", + "references" : "Références", + "include-system-images" : "Inclure les images système", + "clear-image" : "Effacer l'image", + "no-image" : "Aucune image", + "no-image-selected" : "Aucune image sélectionnée", + "browse-from-gallery" : "Parcourir la galerie", + "set-link" : "Définir le lien", + "image-link" : "Lien de l'image", + "link" : "Lien", + "copy-image-link" : "Copier le lien de l'image", + "embed-image" : "Intégrer l'image", + "embed-to-html" : "Intégrer dans le HTML", + "embed-to-html-hint" : "Cette fonction rendra le lien accessible à tout utilisateur non autorisé.", + "embed-to-html-text" : "En utilisant le code suivant, vous pouvez intégrer une image dans des composants basés sur du HTML simple.
Cela inclut les widgets de carte HTML, les fonctions de contenu de cellule, etc.", + "embed-to-angular-template" : "Intégrer dans un modèle HTML Angular", + "embed-to-angular-template-text" : "En utilisant le code suivant, vous pouvez intégrer une image dans un modèle HTML Angular utilisé pour les composants.
Cela inclut le widget Markdown, la section HTML dans l'éditeur de widget, les actions personnalisées, etc." + }, + "image-input" : { + "drop-images-or" : "Glissez-déposez des images ou", + "drag-and-drop" : "Glisser & Déposer", + "or" : "ou", + "browse" : "Parcourir", + "no-images" : "Aucune image sélectionnée", + "images" : "images" + }, + "import" : { + "no-file" : "Aucun fichier sélectionné", + "drop-file" : "Déposez un fichier JSON ou cliquez pour sélectionner un fichier à téléverser.", + "drop-json-file-or" : "Glissez-déposez un fichier JSON ou", + "drop-file-csv" : "Déposez un fichier CSV ou cliquez pour sélectionner un fichier à téléverser.", + "drop-file-csv-or" : "Glissez-déposez un fichier CSV ou", + "column-value" : "Valeur", + "column-title" : "Titre", + "column-example" : "Exemple de valeur", + "column-key" : "Clé attribut/télémétrie", + "credentials" : "Identifiants", + "csv-delimiter" : "Délimiteur CSV", + "csv-first-line-header" : "La première ligne contient les noms de colonnes", + "csv-update-data" : "Mettre à jour les attributs/télémétrie", + "details" : "Détails", + "import-csv-number-columns-error" : "Un fichier doit contenir au moins deux colonnes", + "import-csv-invalid-format-error" : "Format de fichier invalide. Ligne : '{{line}}'", + "column-type" : { + "name" : "Nom", + "type" : "Type", + "label" : "Étiquette", + "column-type" : "Type de colonne", + "client-attribute" : "Attribut client", + "shared-attribute" : "Attribut partagé", + "server-attribute" : "Attribut serveur", + "timeseries" : "Série temporelle", + "entity-field" : "Champ de l'entité", + "access-token" : "Jeton d'accès", + "x509" : "X.509", + "mqtt" : { + "client-id" : "ID client MQTT", + "user-name" : "Nom d'utilisateur MQTT", + "password" : "Mot de passe MQTT" + }, + "lwm2m" : { + "client-endpoint" : "Nom du client endpoint LwM2M", + "security-config-mode" : "Mode de configuration de sécurité LwM2M", + "client-identity" : "Identité client LwM2M", + "client-key" : "Clé du client LwM2M", + "client-cert" : "Clé publique du client LwM2M", + "bootstrap-server-security-mode" : "Mode de sécurité du serveur bootstrap LwM2M", + "bootstrap-server-secret-key" : "Clé secrète du serveur bootstrap LwM2M", + "bootstrap-server-public-key-id" : "Clé publique ou ID du serveur bootstrap LwM2M", + "lwm2m-server-security-mode" : "Mode de sécurité du serveur LwM2M", + "lwm2m-server-secret-key" : "Clé secrète du serveur LwM2M", + "lwm2m-server-public-key-id" : "Clé publique ou ID du serveur LwM2M" + }, + "snmp" : { + "host" : "Hôte SNMP", + "port" : "Port SNMP", + "version" : "Version SNMP (v1, v2c ou v3)", + "community-string" : "Chaîne de communauté SNMP" + }, + "isgateway" : "Est une passerelle", + "activity-time-from-gateway-device" : "Heure d'activité depuis le périphérique passerelle", + "description" : "Description", + "routing-key" : "Clé Edge", + "secret" : "Secret Edge" + }, + "stepper-text" : { + "select-file" : "Sélectionner un fichier", + "configuration" : "Configuration d'import", + "column-type" : "Sélectionner le type de colonnes", + "creat-entities" : "Création de nouvelles entités" + }, + "message" : { + "create-entities" : "{{count}} nouvelles entités ont été créées avec succès.", + "update-entities" : "{{count}} entités ont été mises à jour avec succès.", + "error-entities" : "Une erreur est survenue lors de la création de {{count}} entités." + } + }, + "scada" : { + "symbols" : "Symboles SCADA", + "search" : "Rechercher un symbole", + "selected-symbols" : "{ count, plural, =1 {1 symbole} other {# symboles} } sélectionné(s)", + "download-symbol" : "Télécharger le symbole SCADA", + "export-symbol" : "Exporter le symbole SCADA en JSON", + "import-symbol" : "Importer un symbole SCADA depuis un JSON", + "upload-symbol" : "Téléverser un symbole SCADA", + "update-symbol" : "Mettre à jour le symbole SCADA", + "edit-symbol" : "Éditer le symbole SCADA", + "symbol-details" : "Détails du symbole SCADA", + "mode-svg" : "SVG", + "mode-xml" : "XML", + "no-symbols" : "Aucun symbole trouvé", + "show-hidden-elements" : "Afficher les éléments masqués", + "hide-hidden-elements" : "Masquer les éléments masqués", + "delete-symbol" : "Supprimer le symbole SCADA", + "delete-symbol-title" : "Êtes-vous sûr de vouloir supprimer le symbole SCADA '{{imageTitle}}' ?", + "delete-symbol-text" : "Attention, après confirmation le symbole SCADA sera irrécupérable.", + "delete-symbols-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 symbole SCADA} other {# symboles SCADA} } ?", + "delete-symbols-text" : "Attention, après confirmation tous les symboles SCADA sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "include-system-symbols" : "Inclure les symboles système", + "symbol-preview" : "Aperçu du symbole", + "general" : "Général", + "tags" : "Tags", + "properties" : "Propriétés", + "title" : "Titre", + "description" : "Description", + "search-tags" : "Rechercher des tags", + "widget-size" : "Taille du widget", + "cols" : "colonnes", + "rows" : "rangées", + "state-render-function" : "Fonction de rendu d’état", + "preview" : "Aperçu", + "preview-widget-action-text" : "Action du widget '{{type}}' exécutée avec succès !", + "no-symbol" : "Aucun symbole SCADA", + "no-symbol-selected" : "Aucun symbole SCADA sélectionné", + "clear-symbol" : "Effacer le symbole SCADA", + "browse-symbol-from-gallery" : "Parcourir un symbole SCADA dans la galerie", + "zoom-in" : "Agrandir", + "zoom-out" : "Réduire", + "create-widget" : "Créer un widget", + "create-widget-from-symbol" : "Créer un widget à partir du symbole SCADA", + "hidden" : "masqué", + "tag" : { + "tag" : "Tag", + "on-click-action" : "Action au clic", + "no-tags" : "Aucun tag configuré", + "delete-tag-text" : "Êtes-vous sûr de vouloir supprimer le tag
{{tag}} de l’élément {{elementType}} ?", + "update-tag" : "Mettre à jour le tag", + "enter-tag" : "Saisir le tag", + "tag-settings" : "Paramètres du tag", + "remove-tag" : "Supprimer le tag", + "add-tag" : "Ajouter un tag" + }, + "behavior" : { + "behavior" : "Comportement", + "id" : "Id", + "name" : "Nom", + "type" : "Type", + "no-behaviors" : "Aucun comportement configuré", + "add-behavior" : "Ajouter un comportement", + "type-action" : "Action", + "type-value" : "Valeur", + "type-widget-action" : "Action de widget", + "behavior-settings" : "Paramètres de comportement", + "remove-behavior" : "Supprimer le comportement", + "hint" : "Indice", + "group-title" : "Titre du groupe", + "value-type" : "Type de valeur", + "default-value" : "Valeur par défaut", + "true-label" : "Étiquette vraie", + "false-label" : "Étiquette fausse", + "state-label" : "Étiquette d’état", + "default-payload" : "Charge utile par défaut", + "not-unique-behavior-ids-error" : "Les identifiants de comportement doivent être uniques !", + "default-settings" : "Paramètres par défaut" + }, + "symbol" : { + "symbol" : "Symbole SCADA", + "fluid-presence" : "Présence de fluide", + "fluid-presence-hint" : "Indique si un fluide est présent dans le tuyau.", + "fluid-present" : "Fluide présent", + "present" : "Présent", + "absent" : "Absent", + "flow-presence" : "Présence d'écoulement", + "flow-presence-hint" : "Indique si un fluide s'écoule dans le tuyau.", + "flow-present" : "Écoulement présent", + "flow-direction" : "Direction de l'écoulement", + "flow-direction-hint" : "Indique la direction de l'écoulement du fluide.", + "forward" : "Avant", + "reverse" : "Arrière", + "flow-animation-speed" : "Vitesse de l'animation de l'écoulement", + "flow-animation-speed-hint" : "Valeur décimale indiquant la vitesse de l'animation de l'écoulement. 1 - vitesse normale, 0 - pas d'animation, < 1 - animation plus lente, > 1 - animation plus rapide.", + "leak" : "Fuite", + "leak-hint" : "Indique s'il y a une fuite dans le tuyau.", + "leak-present" : "Fuite présente", + "fluid-color" : "Couleur du fluide", + "pipe-color" : "Couleur du tuyau", + "horizontal-pipe" : "Tuyau horizontal", + "vertical-pipe" : "Tuyau vertical", + "horizontal-fluid-color" : "Couleur du fluide horizontal", + "vertical-fluid-color" : "Couleur du fluide vertical", + "left-pipe" : "Tuyau gauche", + "right-pipe" : "Tuyau droit", + "top-pipe" : "Tuyau supérieur", + "bottom-pipe" : "Tuyau inférieur", + "left-fluid-color" : "Couleur du fluide gauche", + "right-fluid-color" : "Couleur du fluide droit", + "top-fluid-color" : "Couleur du fluide supérieur", + "bottom-fluid-color" : "Couleur du fluide inférieur", + "display" : "Affichage", + "display-format" : "Format d'affichage", + "value" : "Valeur", + "decimals" : "Décimales", + "units" : "Unités", + "flow-meter-value-hint" : "Valeur décimale affichée sur le débitmètre", + "value-hint" : "Valeur décimale indiquant la valeur actuelle", + "running" : "En fonctionnement", + "running-hint" : "Indique si le composant est en état de fonctionnement.", + "warning-state" : "État d'avertissement", + "warning" : "Avertissement", + "warning-click" : "Clic d'avertissement", + "warning-state-hint" : "Indique si le composant est en état d'avertissement.", + "critical-state" : "État critique", + "critical" : "Critique", + "critical-click" : "Clic critique", + "critical-state-hint" : "Indique si le composant est en état critique.", + "critical-state-animation" : "Animation en état critique", + "critical-state-animation-hint" : "Permet d'activer l'animation clignotante lorsque le composant est en état critique.", + "warning-critical-state-animation" : "Animation état avertissement/critique", + "warning-critical-state-animation-hint" : "Permet d'activer l'animation clignotante lorsque le composant est en état d'avertissement ou critique.", + "animation" : "Animation", + "broken" : "Cassé", + "broken-hint" : "Indique si le composant est cassé.", + "on-display-click" : "Au clic sur l'affichage", + "on-display-click-hint" : "Action déclenchée lorsque l'utilisateur clique sur l'affichage.", + "pipe" : "Tuyau", + "default-border-color" : "Couleur de bordure par défaut", + "active-border-color" : "Couleur de bordure active", + "warning-border-color" : "Couleur de bordure d'avertissement", + "critical-border-color" : "Couleur de bordure critique", + "background-color" : "Couleur d'arrière-plan", + "rotation-animation-speed" : "Vitesse de l'animation de rotation", + "rotation-animation-speed-hint" : "Valeur décimale indiquant la vitesse de l'animation de rotation. 1 - vitesse normale, 0 - pas d'animation, < 1 - animation plus lente, > 1 - animation plus rapide.", + "on-click" : "Au clic", + "on-click-hint" : "Action déclenchée lorsque l'utilisateur clique sur le composant.", + "connectors-positions" : "Positions des connecteurs", + "right-connector" : "Connecteur droit", + "right-top-connector" : "Connecteur supérieur droit", + "right-bottom-connector" : "Connecteur inférieur droit", + "left-connector" : "Connecteur gauche", + "left-top-connector" : "Connecteur supérieur gauche", + "left-bottom-connector" : "Connecteur inférieur gauche", + "top-left-connector" : "Connecteur en haut à gauche", + "top-right-connector" : "Connecteur en haut à droite", + "top-connector" : "Connecteur supérieur", + "bottom-connector" : "Connecteur inférieur", + "running-color" : "Couleur en fonctionnement", + "stopped-color" : "Couleur à l'arrêt", + "stopped" : "À l'arrêt", + "warning-color" : "Couleur d'avertissement", + "critical-color" : "Couleur critique", + "opened" : "Ouvert", + "opened-hint" : "Indique si le composant est à l'état ouvert.", + "open" : "Ouvrir", + "open-hint" : "Action déclenchée lorsque l'utilisateur clique pour ouvrir le composant.", + "close" : "Fermer", + "close-hint" : "Action déclenchée lorsque l'utilisateur clique pour fermer le composant.", + "close-state-animation" : "Animation en état fermé", + "close-state-animation-hint" : "Permet d'activer l'animation clignotante lorsque le composant est en état fermé.", + "opened-color" : "Couleur d'ouverture", + "closed-color" : "Couleur de fermeture", + "opened-rotation-angle" : "Angle de rotation à l'ouverture", + "closed-rotation-angle" : "Angle de rotation à la fermeture", + "tank-capacity" : "Capacité du réservoir", + "tank-capacity-hint" : "Valeur décimale indiquant la capacité totale du réservoir.", + "current-volume" : "Volume actuel", + "current-volume-hint" : "Valeur décimale indiquant le volume actuellement occupé.", + "tank-color" : "Couleur du réservoir", + "value-box" : "Boîte de valeur", + "value-text" : "Texte de valeur", + "scale" : "Échelle", + "transparent-mode" : "Mode transparent", + "major-ticks" : "Graduations principales", + "intervals" : "Intervalles", + "major-ticks-color" : "Couleur des graduations principales", + "normal" : "Normal", + "minor-ticks" : "Graduations secondaires", + "minor-ticks-color" : "Couleur des graduations secondaires", + "temperature" : "Température", + "temperature-hint" : "Valeur décimale indiquant la température actuelle.", + "update-temperature" : "Mettre à jour la température", + "update-temperature-hint" : "Action déclenchée lorsque l'utilisateur clique pour modifier la température actuelle.", + "run" : "Démarrer", + "run-hint" : "Action déclenchée lorsque l'utilisateur clique pour démarrer le composant.", + "stop" : "Arrêter", + "stop-hint" : "Action déclenchée lorsque l'utilisateur clique pour arrêter le composant.", + "temperature-step" : "Incrément de température", + "heat-pump-color" : "Couleur de la pompe à chaleur", + "power-button-background" : "Arrière-plan du bouton de mise sous tension", + "value-box-background" : "Arrière-plan de la boîte de valeur", + "value-units" : "Unités de valeur", + "filtration-mode" : "Mode de filtration", + "filtration-mode-hint" : "Valeur entière indiquant le mode de filtration actuel.", + "filtration-mode-update" : "État de mise à jour du mode de filtration", + "filtration-mode-update-hint" : "Action déclenchée lorsque l'utilisateur clique pour modifier le mode de filtration actuel.", + "filter-mode" : "Filtration", + "waste-mode" : "Déchet", + "backwash-mode" : "Contre-lavage", + "recirculate-mode" : "Recirculation", + "rinse-mode" : "Rinçage", + "closed-mode" : "Fermé", + "sand-filter-color" : "Couleur du filtre à sable", + "mode-box-background" : "Arrière-plan de la boîte de mode", + "border-color" : "Couleur de bordure", + "label-color" : "Couleur de l'étiquette", + "water-leak-hint" : "Indique s'il y a une fuite.", + "default-color" : "Couleur par défaut", + "leak-color" : "Couleur de fuite", + "full-value" : "Valeur maximale", + "full-value-hint" : "Valeur décimale indiquant la valeur maximale.", + "label" : "Étiquette", + "icon" : "Icône", + "button-color" : "Couleur du bouton", + "on-label" : "Texte de l'étiquette 'On'", + "off-label" : "Texte de l'étiquette 'Off'", + "arrow-presence" : "Présence de flèche", + "arrow-presence-hint" : "Indique si une flèche est présente dans le connecteur.", + "arrow-present" : "Flèche présente", + "arrow-direction" : "Direction de la flèche/animation", + "arrow-direction-hint" : "Indique la direction de l'écoulement.", + "flow-animation" : "Animation d'écoulement", + "flow-animation-hint" : "Indique si une animation est présente dans le connecteur.", + "flow" : "Écoulement", + "flow-line" : "Ligne", + "flow-line-style" : "Style de ligne", + "flow-style-hint" : "Définissez les valeurs de Tiret et d’Espace de manière à ce que leur somme soit divisible par 100 sans reste pour une synchronisation parfaite de l’animation.", + "flow-dash-cap" : "Extrémité des tirets d'écoulement", + "dash-cap-butt" : "Butée", + "dash-cap-round" : "Arrondi", + "dash-cap-square" : "Carré", + "dash" : "Tiret", + "gap" : "Espace", + "main-line" : "Ligne principale", + "line" : "Ligne", + "line-color" : "Couleur de ligne", + "arrow-color" : "Couleur de la flèche", + "target-value" : "Valeur cible", + "target-value-hint" : "Indique le point cible sur l'échelle.", + "min-max-value" : "Valeur min et max", + "min-value" : "Min", + "max-value" : "Max", + "progress-bar" : "Barre de progression", + "progress-arrow" : "Flèche de progression", + "warning-scale-color" : "Couleur d'échelle d'avertissement", + "critical-scale-color" : "Couleur d'échelle critique", + "scale-color" : "Couleur de l'échelle", + "target" : "Cible", + "high-warning-state" : "État d'avertissement élevé", + "show-high-warning-scale" : "Afficher l'échelle d'avertissement élevé", + "high-warning-scale" : "Échelle d'avertissement élevé", + "high-warning-state-hint" : "Une valeur décimale indique une plage d'avertissement élevé jusqu'à une valeur critique élevée ou maximale.", + "low-warning-state" : "État d'avertissement faible", + "show-low-warning-scale" : "Afficher l'échelle d'avertissement faible", + "low-warning-scale" : "Échelle d'avertissement faible", + "low-warning-state-hint" : "Une valeur décimale indique une plage d'avertissement faible jusqu'à une valeur critique faible ou minimale.", + "high-critical-state" : "État critique élevé", + "show-high-critical-scale" : "Afficher l'échelle critique élevée", + "high-critical-scale" : "Échelle critique élevée", + "high-critical-state-hint" : "Une valeur décimale indique une plage critique élevée jusqu'à la valeur maximale de l'échelle.", + "low-critical-state" : "État critique faible", + "show-low-critical-scale" : "Afficher l'état critique faible", + "low-critical-scale" : "État critique faible", + "low-critical-state-hint" : "Une valeur décimale indique une plage critique faible jusqu'à la valeur minimale de l'échelle.", + "filter-color" : "Couleur du filtre", + "colors" : "Couleurs", + "indicator-colors" : "Couleurs des indicateurs", + "enabled" : "Activé", + "disabled" : "Désactivé", + "on" : "ON", + "off" : "OFF", + "on-off-state" : "État On/Off", + "on-off-state-hint" : "Indique si le composant est en état activé ou désactivé.", + "on-update-state" : "État de mise à jour - ON", + "on-update-state-hint" : "Action déclenchée lorsque l'utilisateur clique pour passer à l'état ON.", + "off-update-state" : "État de mise à jour - OFF", + "off-update-state-hint" : "Action déclenchée lorsque l'utilisateur clique pour passer à l'état OFF.", + "voltage" : "Tension", + "input-voltage" : "Tension d'entrée", + "input-voltage-hint" : "Une valeur décimale indique la tension d'entrée.", + "output-voltage" : "Tension de sortie", + "output-voltage-hint" : "Une valeur décimale indique la tension de sortie.", + "first-phase-voltage" : "Tension de première phase", + "second-phase-voltage" : "Tension de deuxième phase", + "third-phase-voltage" : "Tension de troisième phase", + "phase-voltage-hint" : "Une valeur décimale indique la tension de la phase en cours", + "voltage-hint" : "Une valeur décimale indique la tension actuelle", + "current-voltage-color" : "Couleur de la tension actuelle", + "phase-indicator-color" : "Couleur de l'indicateur de phase", + "measured" : "Mesuré", + "measured-hint" : "Une valeur décimale indique la consommation d'énergie en kilowattheures", + "day-rate" : "Tarif jour", + "night-rate" : "Tarif nuit", + "off-peak-rate" : "Tarif heures creuses", + "peak-rate" : "Tarif de pointe", + "export-rate" : "Tarif d'exportation", + "operating-mode" : "Mode de fonctionnement", + "bypass-mode" : "Contournement", + "operating-mode-hint" : "Valeur entière indiquant le mode de fonctionnement actuel (0 - OFF, 1 - ON, 2 - BYPASS)", + "connected" : "Connecté", + "connected-hint" : "Indique si le composant est connecté.", + "disconnected" : "Déconnecté", + "indicator" : "Indicateur", + "operation-mode" : "Mode opératoire", + "operation-mode-hint" : "Indique si l'onduleur est en mode secteur ou onduleur.", + "operation-mode-indicators-color" : "Couleur des indicateurs de mode opératoire", + "mains-on-mode" : "Secteur activé", + "inverter-on-mode" : "Onduleur activé", + "charging-mode" : "Mode de charge", + "charging-mode-hint" : "Valeur entière indiquant le mode de charge actuel (1 - Bulk, 2 - Absorption, 3 - Float)", + "charging-mode-indicators-color" : "Couleur des indicateurs de charge", + "inverter-faults" : "Défauts", + "inverter-fault-indicators-color" : "Couleur des indicateurs de défaut", + "overload-fault" : "Surcharge", + "overload-fault-hint" : "Indique si l'onduleur est en surcharge.", + "low-battery-fault" : "Batterie faible", + "low-battery-fault-hint" : "Indique si la batterie est excessivement déchargée.", + "temperature-fault" : "Température", + "temperature-fault-hint" : "Indique une température élevée dans l'onduleur.", + "triangle" : "Triangle", + "socket" : "Prise", + "left-button" : "Bouton gauche", + "right-button" : "Bouton droit", + "alarm-colors" : "Couleurs d'alarme", + "hook-color" : "Couleur du crochet" + } + }, + "item" : { + "selected" : "Sélectionné" + }, + "js-func" : { + "no-return-error" : "La fonction doit retourner une valeur !", + "return-type-mismatch" : "La fonction doit retourner une valeur de type '{{type}}' !", + "tidy" : "Propre", + "mini" : "Mini", + "modules" : "Modules", + "remove-module" : "Supprimer le module", + "no-modules" : "Aucun module configuré", + "add-module" : "Ajouter un module", + "module-alias" : "Alias", + "invalid-module-alias-name" : "Nom d'alias invalide", + "module-resource" : "Ressource du module JS", + "not-unique-module-aliases-error" : "Les alias des modules doivent être uniques !", + "show-module-info" : "Afficher les infos du module", + "show-module-source-code" : "Afficher le code source du module", + "module-members" : "Membres du module", + "module-no-members" : "Le module n'a aucun membre exporté", + "module-load-error" : "Erreur de chargement du module", + "source-code" : "Code source", + "source-code-load-error" : "Erreur de chargement du code source", + "no-js-module-text" : "Aucun module JS trouvé", + "no-js-module-matching" : "Aucun module JS correspondant à '{{module}}' trouvé." + }, + "key-val" : { + "key" : "Clé", + "value" : "Valeur", + "remove-entry" : "Supprimer l'entrée", + "add-entry" : "Ajouter une entrée", + "no-data" : "Aucune entrée" + }, + "layout" : { + "layout" : "Disposition", + "layouts" : "Dispositions", + "manage" : "Gérer les dispositions", + "settings" : "Paramètres de disposition", + "color" : "Couleur", + "main" : "Principal", + "right" : "Droite", + "left" : "Gauche", + "select" : "Sélectionner la disposition cible", + "percentage-width" : "Largeur en pourcentage (%)", + "fixed-width" : "Largeur fixe (px)", + "left-width" : "Colonne gauche (%)", + "right-width" : "Colonne droite (%)", + "pick-fixed-side" : "Côté fixe : ", + "layout-fixed-width" : "Largeur fixe (px)", + "value-min-error" : "La valeur doit être supérieure à {{min}}{{unit}}", + "value-max-error" : "La valeur doit être inférieure à {{max}}{{unit}}", + "layout-fixed-width-required" : "La largeur fixe est requise", + "right-width-percentage-required" : "Le pourcentage pour la colonne de droite est requis", + "left-width-percentage-required" : "Le pourcentage pour la colonne de gauche est requis", + "divider" : "Séparateur", + "right-side" : "Disposition côté droit", + "left-side" : "Disposition côté gauche", + "add-new-breakpoint" : "Ajouter un nouveau point de rupture", + "breakpoint" : "Point de rupture", + "breakpoints" : "Points de rupture", + "copy-from" : "Copier depuis", + "size" : "Taille", + "delete-breakpoint-title" : "Êtes-vous sûr de vouloir supprimer le point de rupture '{{name}}' ?", + "delete-breakpoint-text" : "Veuillez noter qu'après confirmation, le point de rupture sera irrécupérable et les paramètres reviendront à ceux du point de rupture par défaut." + }, + "legend" : { + "direction" : "Direction", + "position" : "Position", + "show-values" : "Afficher les valeurs", + "min-option" : "Min", + "max-option" : "Max", + "average-option" : "Moyenne", + "total-option" : "Total", + "latest-option" : "Dernière", + "sort-legend" : "Trier les clés de données dans la légende", + "show-max" : "Afficher la valeur maximale", + "show-min" : "Afficher la valeur minimale", + "show-avg" : "Afficher la moyenne", + "show-total" : "Afficher le total", + "show-latest" : "Afficher la dernière valeur", + "settings" : "Paramètres de la légende", + "min" : "min", + "max" : "max", + "avg" : "moy", + "total" : "total", + "latest" : "dernière", + "Min" : "Min", + "Max" : "Max", + "Avg" : "Moy", + "Total" : "Total", + "Latest" : "Dernière", + "comparison-time-ago" : { + "previousInterval" : "(intervalle précédent)", + "customInterval" : "(intervalle personnalisé)", + "days" : "(jour précédent)", + "weeks" : "(semaine précédente)", + "months" : "(mois précédent)", + "years" : "(année précédente)" + }, + "column-title" : "Titre de la colonne", + "label" : "Étiquette", + "value" : "Valeur" + }, + "login" : { + "login" : "Connexion", + "request-password-reset" : "Demander une réinitialisation du mot de passe", + "reset-password" : "Réinitialiser le mot de passe", + "create-password" : "Créer un mot de passe", + "two-factor-authentication" : "Authentification à deux facteurs", + "passwords-mismatch-error" : "Les mots de passe saisis doivent être identiques !", + "password-again" : "Ressaisir le mot de passe", + "sign-in" : "Veuillez vous connecter", + "username" : "Nom d'utilisateur (email)", + "remember-me" : "Se souvenir de moi", + "forgot-password" : "Mot de passe oublié ?", + "password-reset" : "Réinitialisation du mot de passe", + "expired-password-reset-message" : "Vos identifiants ont expiré ! Veuillez créer un nouveau mot de passe.", + "new-password" : "Nouveau mot de passe", + "new-password-again" : "Confirmer le nouveau mot de passe", + "password-link-sent-message" : "Le lien de réinitialisation a été envoyé", + "email" : "Email", + "invalid-email-format" : "Format d'email invalide.", + "login-with" : "Connexion avec {{name}}", + "or" : "ou", + "error" : "Erreur de connexion", + "verify-your-identity" : "Vérifiez votre identité", + "select-way-to-verify" : "Sélectionnez une méthode de vérification", + "resend-code" : "Renvoyer le code", + "resend-code-wait" : "Renvoyer le code dans { time, plural, =1 {1 seconde} other {# secondes} }", + "try-another-way" : "Essayer une autre méthode", + "totp-auth-description" : "Veuillez saisir le code de sécurité depuis votre application d'authentification.", + "totp-auth-placeholder" : "Code", + "sms-auth-description" : "Un code de sécurité a été envoyé sur votre téléphone au {{contact}}.", + "sms-auth-placeholder" : "Code SMS", + "email-auth-description" : "Un code de sécurité a été envoyé à votre adresse email à {{contact}}.", + "email-auth-placeholder" : "Code email", + "backup-code-auth-description" : "Veuillez saisir l’un de vos codes de secours.", + "backup-code-auth-placeholder" : "Code de secours", + "activation-link-expired" : "Le lien d'activation a expiré", + "activation-link-expired-message" : "Le lien d’activation de votre profil a expiré. Vous pouvez revenir à la page de connexion pour recevoir un nouvel email.", + "reset-password-link-expired" : "Le lien de réinitialisation du mot de passe a expiré", + "reset-password-link-expired-message" : "Le lien de réinitialisation du mot de passe a expiré. Vous pouvez revenir à la page de connexion pour recevoir un nouvel email." + }, + "mobile" : { + "add-application" : "Ajouter une application", + "app-id" : "ID de l'application", + "app-id-required" : "L'ID de l'application est requis", + "app-id-pattern" : "Format d'ID de l'application invalide", + "app-store-link" : "Lien App Store", + "app-store-link-required" : "Le lien App Store est requis", + "application-details" : "Détails de l'application", + "application-package" : "Paquet de l'application", + "application-secret" : "Clé secrète de l'application", + "application-secret-required" : "La clé secrète de l'application est requise", + "application" : "Application", + "applications" : "Applications", + "copy-app-id" : "Copier l'ID de l'application", + "copy-app-store-link" : "Copier le lien App Store", + "copy-application-package" : "Copier le paquet de l'application", + "copy-application-secret" : "Copier la clé secrète de l'application", + "copy-google-play-link" : "Copier le lien Google Play", + "copy-sha256-certificate-fingerprints" : "Copier les empreintes de certificat SHA256", + "delete-application" : "Supprimer l'application", + "delete-application-button-text" : "Je comprends les conséquences, supprimer l'application", + "delete-application-text" : "Cette action est irréversible. Cela supprimera définitivement votre application.
Si vous ne souhaitez pas la supprimer définitivement, vous pouvez suspendre temporairement l'application.
Pour supprimer l'application quand même, veuillez saisir \"{{phrase}}\" pour confirmer.", + "delete-application-title-short" : "Êtes-vous sûr de vouloir supprimer l'application '{{name}}' ?", + "delete-application-text-short" : "Attention, après confirmation l'application et toutes ses données associées seront irrécupérables.", + "delete-application-phrase" : "supprimer l'application", + "delete-applications-bundle-text" : "Attention, après confirmation le bundle mobile et toutes les données associées seront irrécupérables.", + "delete-applications-bundle-title" : "Êtes-vous sûr de vouloir supprimer le bundle mobile '{{bundleName}}' ?", + "generate-application-secret" : "Générer une clé secrète", + "google-play-link" : "Lien Google Play", + "google-play-link-required" : "Le lien Google Play est requis", + "latest-version" : "Dernière version", + "min-version" : "Version minimale", + "invalid-version-pattern" : "Format de version invalide. Veuillez utiliser le format : majeur.mineur.correctif (ex. : 1.0.0).", + "mobile-center" : "Centre mobile", + "mobile-package" : "Paquet de l'application", + "mobile-package-max-length" : "Le paquet de l'application doit contenir moins de 256 caractères", + "mobile-package-required" : "Le paquet de l'application est requis.", + "mobile-package-pattern" : "Format du paquet de l'application invalide", + "no-application" : "Aucune application trouvée", + "no-bundles" : "Aucun bundle trouvé", + "platform-type" : "Type de plateforme", + "search-application" : "Rechercher des applications", + "search-bundles" : "Rechercher des bundles", + "set" : "Définir", + "sha256-certificate-fingerprints" : "Empreintes de certificat SHA256", + "sha256-certificate-fingerprints-required" : "Les empreintes de certificat SHA256 sont requises", + "sha256-certificate-fingerprints-pattern" : "Format d'empreinte SHA256 invalide", + "show-hidden-pages" : "Afficher les pages cachées", + "status" : "Statut", + "status-type" : { + "deprecated" : "Obsolète", + "draft" : "Brouillon", + "published" : "Publié", + "suspended" : "Suspendu" + }, + "store-information" : "Informations sur la boutique", + "version-information" : "Informations sur la version", + "min-version-release-notes" : "Notes de version minimale", + "latest-version-release-notes" : "Notes de la dernière version", + "bundle" : "Bundle", + "bundles" : "Bundles", + "add-bundle" : "Ajouter un bundle", + "title" : "Titre", + "title-required" : "Le titre est requis", + "title-cannot-contain-only-spaces" : "Le titre ne peut pas contenir uniquement des espaces", + "title-max-length" : "Le titre doit contenir moins de 256 caractères", + "oauth-clients" : "Clients OAuth 2.0", + "android-app" : "Application Android", + "android-application" : "Application Android", + "ios-app" : "Application iOS", + "ios-application" : "Application iOS", + "invalid-store-link" : "Lien de boutique invalide", + "enable-oauth" : "Activer OAuth 2.0", + "enable-self-registration" : "Activer l'auto-enregistrement", + "edit-bundle" : "Modifier le bundle", + "description" : "Description", + "basic-settings" : "Paramètres de base", + "no-application-matching" : "Aucune application correspondant à '{{entity}}' trouvée.", + "no-bundle-matching" : "Aucun bundle correspondant à '{{entity}}' trouvé.", + "application-required" : "L'application est requise.", + "bundle-required" : "Le bundle est requis.", + "no-application-text" : "Aucune application trouvée", + "no-bundle-text" : "Aucun bundle trouvé", + "layout" : "Disposition", + "pages" : "Pages", + "hide-all-pages" : "Masquer toutes les pages", + "reset-to-default-pages" : "Réinitialiser aux pages par défaut", + "add-specific-page" : "Ajouter une page spécifique", + "visible" : "Visible", + "hidden" : "Masqué", + "reset-to-page-default" : "Réinitialiser la page à sa valeur par défaut", + "mobile-599" : "Mobile (max 599px)", + "tablet-959" : "Tablette (max 959px)", + "max-element-number" : "Nombre maximal d'éléments", + "page-name" : "Nom de la page", + "page-name-required" : "Le nom de la page est requis.", + "page-name-cannot-contain-only-spaces" : "Le nom de la page ne peut contenir uniquement des espaces.", + "page-name-max-length" : "Le nom de la page doit contenir moins de 256 caractères", + "page-type" : "Type de page", + "pages-types" : { + "dashboard" : "Tableau de bord", + "web-view" : "Vue Web", + "custom" : "Personnalisé" + }, + "url" : "URL", + "invalid-url-format" : "Format d'URL invalide", + "path" : "Chemin", + "invalid-path-format" : "Format de chemin invalide", + "custom-page" : "Page personnalisée", + "edit-page" : "Modifier la page", + "edit-custom-page" : "Modifier la page personnalisée", + "delete-page" : "Supprimer la page", + "qr-code-widget" : "Widget code QR", + "type-here" : "Tapez ici", + "configuration-dialog" : "Dialogue de configuration", + "configuration-app" : "Application de configuration", + "configuration-step" : { + "prepare-environment-title" : "Préparer l'environnement de développement", + "prepare-environment-text" : "L'application mobile Flutter ThingsBoard nécessite le SDK Flutter. Suivez les instructions pour configurer le SDK Flutter.", + "get-source-code-title" : "Obtenir le code source de l'application", + "get-source-code-text" : "Vous pouvez obtenir le code source de l'application mobile Flutter ThingsBoard en le clonant depuis le dépôt GitHub :", + "configure-api-title" : "Configurer le point de terminaison de l'API ThingsBoard", + "configure-api-text" : "Ouvrez le projet flutter_thingsboard_pe_app dans votre éditeur/IDE. Modifiez :", + "configure-api-hint" : "Définissez la valeur de la constante thingsBoardApiEndpoint pour correspondre au point de terminaison API de votre instance ThingsBoard. N'utilisez pas les noms d'hôte “localhost” ou “127.0.0.1”.", + "run-app-title" : "Lancer l'application", + "run-app-text" : "Lancez l'application comme décrit dans votre IDE.\nSi vous utilisez le terminal, exécutez l'application avec la commande suivante :", + "more-information" : "Des informations détaillées sont disponibles dans notre documentation de démarrage.", + "getting-started" : "Commencer", + "configure-package-title" : "Configurer le paquet de l'application", + "configure-package-text" : "Vous pouvez modifier manuellement le paquet de l'application ou utiliser un outil CLI tiers.", + "configure-package-text-install" : "Pour installer l'outil Rename CLI, exécutez la commande suivante :", + "configure-package-run-commands" : "Exécutez ces commandes dans le répertoire racine de votre projet :" + } + }, + "notification" : { + "action-button" : "Bouton d'action", + "action-type" : "Type d'action", + "active" : "Actif", + "add-notification-recipients-group" : "Ajouter un groupe de destinataires de notification", + "add-notification-template" : "Ajouter un modèle de notification", + "add-recipient" : "Ajouter un destinataire", + "add-recipients" : "Ajouter des destinataires", + "add-rule" : "Ajouter une règle", + "add-stage" : "Ajouter une étape", + "add-template" : "Ajouter un modèle", + "after" : "Après", + "alarm-assignment-trigger-settings" : "Paramètres du déclencheur d'affectation d'alarme", + "alarm-comment-trigger-settings" : "Paramètres du déclencheur de commentaire d'alarme", + "alarm-trigger-settings" : "Paramètres du déclencheur d'alarme", + "all" : "Tous", + "api-feature-hint" : "Si le champ est vide, le déclencheur s'appliquera à toutes les fonctionnalités API", + "api-usage-trigger-settings" : "Paramètres du déclencheur d'utilisation de l'API", + "new-platform-version-trigger-settings" : "Paramètres du déclencheur de nouvelle version de la plateforme", + "rate-limits-trigger-settings" : "Paramètres du déclencheur de dépassement de limites", + "task-processing-failure-trigger-settings" : "Paramètres du déclencheur d'échec du traitement de tâche", + "at-least-one-should-be-selected" : "Au moins un élément doit être sélectionné", + "basic-settings" : "Paramètres de base", + "button-text" : "Texte du bouton", + "button-text-required" : "Le texte du bouton est requis", + "button-text-max-length" : "Le texte du bouton doit contenir au maximum {{ length }} caractères", + "compose" : "Composer", + "conversation" : "Conversation", + "conversation-required" : "La conversation est requise", + "copy-notification-template" : "Copier le modèle de notification", + "copy-rule" : "Copier la règle", + "copy-template" : "Copier le modèle", + "create-new" : "Créer nouveau", + "created" : "Créé", + "customize-messages" : "Personnaliser les messages", + "delete-notification-text" : "Attention, après confirmation, la notification sera irrécupérable.", + "delete-notification-title" : "Êtes-vous sûr de vouloir supprimer la notification ?", + "delete-notifications-text" : "Attention, après confirmation, les notifications seront irrécupérables.", + "delete-notifications-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 notification} other {# notifications} } ?", + "delete-recipient-text" : "Attention, après confirmation, le destinataire sera irrécupérable.", + "delete-recipient-title" : "Êtes-vous sûr de vouloir supprimer le destinataire '{{recipientName}}' ?", + "delete-recipients-text" : "Attention, après confirmation, les destinataires seront irrécupérables.", + "delete-recipients-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 destinataire} other {# destinataires} } ?", + "delete-request-text" : "Attention, après confirmation, la requête sera irrécupérable.", + "delete-request-title" : "Êtes-vous sûr de vouloir supprimer la requête ?", + "delete-requests-text" : "Attention, après confirmation, les requêtes seront irrécupérables.", + "delete-requests-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 requête} other {# requêtes} } ?", + "delete-rule-text" : "Attention, après confirmation, la règle sera irrécupérable.", + "delete-rule-title" : "Êtes-vous sûr de vouloir supprimer la règle '{{ruleName}}' ?", + "delete-rules-text" : "Attention, après confirmation, les règles seront irrécupérables.", + "delete-rules-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 règle} other {# règles} } ?", + "delete-template-text" : "Attention, après confirmation, le modèle sera irrécupérable.", + "delete-template-title" : "Êtes-vous sûr de vouloir supprimer le modèle '{{templateName}}' ?", + "delete-templates-text" : "Attention, après confirmation, les modèles seront irrécupérables.", + "delete-templates-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 modèle} other {# modèles} } ?", + "deleted" : "Supprimé", + "delivery-method" : { + "delivery-method" : "Méthode de livraison", + "email" : "Email", + "email-preview" : "Aperçu de la notification email", + "slack" : "Slack", + "slack-preview" : "Aperçu de la notification Slack", + "microsoft-teams" : "Microsoft Teams", + "microsoft-teams-preview" : "Aperçu de la notification Microsoft Teams", + "sms" : "SMS", + "sms-preview" : "Aperçu de la notification SMS", + "web" : "Web", + "web-preview" : "Aperçu de la notification Web", + "mobile-app" : "Application mobile", + "mobile-app-preview" : "Aperçu de la notification mobile" + }, + "delivery-method-not-configure-click" : "Méthode de livraison non configurée. Cliquez pour configurer.", + "delivery-method-not-configure-contact" : "Méthode de livraison non configurée. Veuillez contacter votre administrateur système.", + "delivery-methods" : "Méthodes de livraison", + "description" : "Description", + "device-activity-trigger-settings" : "Paramètres du déclencheur d'activité du dispositif", + "device-list-rule-hint" : "Si le champ est vide, le déclencheur s'appliquera à tous les dispositifs", + "device-profiles-list-rule-hint" : "Si le champ est vide, le déclencheur s'appliquera à tous les profils de dispositifs", + "disabled" : "Désactivé", + "edge-trigger-settings" : "Paramètres du déclencheur Edge", + "edge-list-rule-hint" : "Si le champ est vide, le déclencheur s'appliquera à toutes les instances Edge", + "edit-notification-recipients-group" : "Modifier le groupe de destinataires de notification", + "edit-notification-template" : "Modifier le modèle de notification", + "edit-rule" : "Modifier la règle", + "edit-template" : "Modifier le modèle", + "enabled" : "Activé", + "entities-limit-trigger-settings" : "Paramètres du déclencheur de limite d'entités", + "entity-action-trigger-settings" : "Paramètres du déclencheur d'action sur entité", + "entity-type" : "Type d'entité", + "escalation-chain" : "Chaîne d'escalade", + "failed-send" : "Échecs d'envoi", + "fails" : "{ count, plural, =1 {1 échec} other {# échecs} }", + "filter" : "Filtre", + "first-recipient" : "Premier destinataire", + "inactive" : "Inactif", + "inbox" : "Boîte de réception", + "notification-inbox" : "Notifications / Boîte de réception", + "input-field-support-templatization" : "Le champ de saisie prend en charge la templatisation.", + "input-fields-support-templatization" : "Les champs de saisie prennent en charge la templatisation.", + "link" : "Lien", + "link-required" : "Le lien est requis", + "link-type" : { + "dashboard" : "Ouvrir le tableau de bord", + "link" : "Ouvrir un lien URL" + }, + "loading-notifications" : "Chargement des notifications...", + "management" : "Gestion des notifications", + "mark-all-as-read" : "Tout marquer comme lu", + "mark-as-read" : "Marquer comme lu", + "message" : "Message", + "message-required" : "Le message est requis", + "message-max-length" : "Le message doit contenir au maximum {{ length }} caractères", + "name" : "Nom", + "name-required" : "Le nom est requis", + "new-notification" : "Nouvelle notification", + "no-inbox-notification" : "Aucune notification trouvée", + "no-notification-request" : "Aucune demande de notification", + "no-notification-templates" : "Aucun modèle de notification trouvé", + "no-notifications-yet" : "Aucune notification pour l’instant", + "no-recipients-notification" : "Aucun destinataire pour cette notification", + "no-recipients-matching" : "Aucun destinataire correspondant à '{{entity}}' trouvé.", + "no-recipients-text" : "Aucun destinataire trouvé", + "no-rule" : "Aucune règle configurée", + "no-rules-notification" : "Aucune règle de notification", + "no-severity-found" : "Aucune sévérité trouvée", + "no-severity-matching" : "'{{severity}}' introuvable.", + "no-template-matching" : "Aucune ressource correspondant à '{{template}}' trouvée.", + "not-found-slack-recipient" : "Destinataire Slack introuvable", + "notification" : "Notification", + "notification-center" : "Centre de notifications", + "notification-tap-action" : "Action au clic sur la notification", + "notification-tap-action-hint" : "Si non activé, le tableau de bord d’alarme par défaut sera utilisé", + "notify" : "notifier", + "notify-again" : "Notifier de nouveau", + "notify-alarm-action" : { + "acknowledged" : "Alarme reconnue", + "assigned" : "Alarme assignée", + "cleared" : "Alarme effacée", + "created" : "Alarme créée", + "severity-changed" : "Sévérité de l’alarme modifiée", + "unassigned" : "Alarme désassignée" + }, + "notify-on" : "Notifier lors de", + "notify-on-comment-update" : "Notifier lors de la mise à jour d’un commentaire", + "notify-on-required" : "Le champ 'Notifier lors de' est requis", + "notify-on-unassign" : "Notifier lors du désassignement", + "notify-only-user-comments" : "Notifier uniquement les commentaires des utilisateurs", + "only-rule-chain-lifecycle-failures" : "Uniquement les échecs de cycle de vie de chaîne de règles", + "only-rule-node-lifecycle-failures" : "Uniquement les échecs de cycle de vie de nœud de règles", + "platform-users" : "Utilisateurs de la plateforme", + "rate-limits" : "Limites de taux", + "rate-limits-hint" : "Si le champ est vide, le déclencheur s’appliquera à toutes les limites de taux", + "recipient" : "Destinataire", + "recipient-group" : "Groupe de destinataires", + "recipient-type" : { + "affected-tenant-administrators" : "Administrateurs du locataire concerné", + "affected-user" : "Utilisateur concerné", + "all-users" : "Tous les utilisateurs", + "customer-users" : "Utilisateurs client", + "system-administrators" : "Administrateurs système", + "tenant-administrators" : "Administrateurs du locataire", + "user-filters" : "Filtre d'utilisateur", + "user-list" : "Liste d’utilisateurs", + "users-entity-owner" : "Utilisateurs du propriétaire de l'entité" + }, + "recipients" : "Destinataires", + "notification-recipient" : "Destinataire de la notification", + "notification-recipient-required" : "Le destinataire de la notification est requis.", + "notification-recipients" : "Notifications / Destinataires", + "recipients-count" : "{ count, plural, =1 {1 destinataire} other {# destinataires} }", + "recipients-required" : "Les destinataires sont requis", + "refresh-allow-delivery-method" : "Actualiser les méthodes de livraison autorisées", + "request-search" : "Rechercher une requête", + "request-status" : { + "processing" : "En traitement", + "scheduled" : "Planifiée", + "sent" : "Envoyée" + }, + "review" : "Vérifier", + "rule" : "Règle", + "rule-chain-list-rule-hint" : "Si le champ est vide, le déclencheur s’appliquera à toutes les chaînes de règles", + "rule-engine-events-trigger-settings" : "Paramètres du déclencheur d’événements du moteur de règles", + "rule-engine-filter" : "Filtre du moteur de règles", + "rule-name" : "Nom de la règle", + "rule-name-required" : "Le nom est requis", + "rule-disable" : "Désactiver la règle de notification", + "rule-enable" : "Activer la règle de notification", + "rule-node-filter" : "Filtre de nœud de règle", + "rules" : "Règles", + "notification-rules" : "Notifications / Règles", + "scheduler-later" : "Planifier plus tard", + "search-notification" : "Rechercher des notifications", + "search-recipients" : "Rechercher des destinataires", + "search-rules" : "Rechercher des règles", + "search-templates" : "Rechercher des modèles", + "see-documentation" : "Voir la documentation", + "selected-notifications" : "{ count, plural, =1 {1 notification} other {# notifications} } sélectionnée(s)", + "selected-recipients" : "{ count, plural, =1 {1 destinataire} other {# destinataires} } sélectionné(s)", + "selected-requests" : "{ count, plural, =1 {1 requête} other {# requêtes} } sélectionnée(s)", + "selected-rules" : "{ count, plural, =1 {1 règle} other {# règles} } sélectionnée(s)", + "selected-template" : "{ count, plural, =1 {1 modèle} other {# modèles} } sélectionné(s)", + "send-notification" : "Envoyer une notification", + "sent" : "Envoyée", + "setup" : "Configuration", + "notification-sent" : "Notifications / Envoyées", + "set-entity-from-notification" : "Définir l'entité à partir de la notification dans l'état du tableau de bord", + "slack-chanel-type" : "Type de canal Slack", + "slack-chanel-types" : { + "direct" : "Message direct", + "private-channel" : "Canal privé", + "public-channel" : "Canal public" + }, + "start-from-scratch" : "Commencer de zéro", + "status" : "Statut", + "stop-escalation-alarm-status-become" : "Arrêter l'escalade lorsque le statut de l'alarme devient :", + "subject" : "Sujet", + "subject-required" : "Le sujet est requis", + "subject-max-length" : "Le sujet doit contenir au maximum {{ length }} caractères", + "template" : "Modèle", + "template-name" : "Nom du modèle", + "template-required" : "Le modèle est requis", + "template-type" : { + "alarm" : "Alarme", + "alarm-assignment" : "Attribution d'alarme", + "alarm-comment" : "Commentaire d'alarme", + "api-usage-limit" : "Limite d'utilisation de l'API", + "device-activity" : "Activité de l'appareil", + "entities-limit" : "Limite d'entités", + "entity-action" : "Action sur l'entité", + "general" : "Général", + "rule-engine-lifecycle-event" : "Événement du cycle de vie du moteur de règles", + "rule-node" : "Nœud de règle", + "new-platform-version" : "Nouvelle version de la plateforme", + "rate-limits" : "Limites de taux dépassées", + "edge-communication-failure" : "Échec de communication avec l'Edge", + "edge-connection" : "Connexion Edge", + "task-processing-failure" : "Échec de traitement de tâche" + }, + "templates" : "Modèles", + "notification-templates" : "Notifications / Modèles", + "tenant-profiles-list-rule-hint" : "Si le champ est vide, le déclencheur s’appliquera à tous les profils de locataire", + "tenants-list-rule-hint" : "Si le champ est vide, le déclencheur s’appliquera à tous les locataires", + "threshold" : "Seuil", + "theme-color" : "Couleur du thème", + "time" : "Temps", + "track-rule-node-events" : "Suivre les événements des nœuds de règle", + "trigger" : { + "alarm" : "Alarme", + "alarm-assignment" : "Attribution d'alarme", + "alarm-comment" : "Commentaire d'alarme", + "api-usage-limit" : "Limite d'utilisation de l'API", + "device-activity" : "Activité de l'appareil", + "entities-limit" : "Limite d'entités", + "entity-action" : "Action sur l'entité", + "rule-engine-lifecycle-event" : "Événement du cycle de vie du moteur de règles", + "new-platform-version" : "Nouvelle version de la plateforme", + "rate-limits" : "Limites de taux dépassées", + "edge-connection" : "Connexion Edge", + "edge-communication-failure" : "Échec de communication avec l'Edge", + "task-processing-failure" : "Échec de traitement de tâche", + "trigger" : "Déclencheur", + "trigger-required" : "Le déclencheur est requis" + }, + "type" : "Type", + "unread" : "Non lu", + "updated" : "Mis à jour", + "use-deprecated-webhook-connectors" : "Utiliser les connecteurs Webhook obsolètes", + "use-old-api" : "Utiliser l’ancienne API", + "use-template" : "Utiliser le modèle", + "view-all" : "Voir tout", + "warning" : "Avertissement", + "webhook-url" : "URL du Webhook", + "webhook-url-required" : "L’URL du Webhook est requise", + "workflow-url" : "URL du workflow", + "workflow-url-required" : "L’URL du workflow est requise", + "channel-name" : "Nom du canal", + "channel-name-required" : "Le nom du canal est requis", + "settings" : { + "notification-settings" : "Paramètres de notification", + "reset-all" : "Réinitialiser tous les paramètres", + "reset-all-title" : "Êtes-vous sûr de vouloir réinitialiser le formulaire ?", + "reset-all-text" : "Après confirmation, le formulaire de paramètres sera réinitialisé aux valeurs par défaut et sauvegardé.", + "type" : "Type", + "enable-all" : "Tout activer", + "disable-all" : "Tout désactiver", + "delivery-not-configured" : "Méthode de livraison non configurée" + } + }, + "ota-update" : { + "add" : "Ajouter un paquet", + "assign-firmware" : "Micrologiciel assigné", + "assign-firmware-required" : "Le micrologiciel assigné est requis", + "assign-software" : "Logiciel assigné", + "assign-software-required" : "Le logiciel assigné est requis", + "auto-generate-checksum" : "Générer automatiquement la somme de contrôle", + "checksum" : "Somme de contrôle", + "checksum-hint" : "Si la somme de contrôle est vide, elle sera générée automatiquement", + "checksum-algorithm" : "Algorithme de somme de contrôle", + "checksum-copied-message" : "La somme de contrôle du paquet a été copiée dans le presse-papiers", + "change-firmware" : "Changer le micrologiciel peut entraîner la mise à jour de { count, plural, =1 {1 appareil} other {# appareils} }.", + "change-software" : "Changer le logiciel peut entraîner la mise à jour de { count, plural, =1 {1 appareil} other {# appareils} }.", + "chose-compatible-device-profile" : "Le paquet téléchargé ne sera disponible que pour les appareils ayant le profil choisi.", + "chose-firmware-distributed-device" : "Choisissez le micrologiciel à distribuer aux appareils", + "chose-software-distributed-device" : "Choisissez le logiciel à distribuer aux appareils", + "content-type" : "Type de contenu", + "copy-checksum" : "Copier la somme de contrôle", + "copy-direct-url" : "Copier l'URL directe", + "copyId" : "Copier l'identifiant du paquet", + "copied" : "Copié !", + "delete" : "Supprimer le paquet", + "delete-ota-update-text" : "Attention, après confirmation, la mise à jour OTA deviendra irrécupérable.", + "delete-ota-update-title" : "Êtes-vous sûr de vouloir supprimer la mise à jour OTA '{{title}}' ?", + "delete-ota-updates-text" : "Attention, après confirmation, toutes les mises à jour OTA sélectionnées seront supprimées.", + "delete-ota-updates-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 mise à jour OTA} other {# mises à jour OTA} } ?", + "description" : "Description", + "direct-url" : "URL directe", + "direct-url-copied-message" : "L'URL directe du paquet a été copiée dans le presse-papiers", + "direct-url-required" : "L'URL directe est requise", + "download" : "Télécharger le paquet", + "drop-file" : "Déposez un fichier de paquet ou cliquez pour en sélectionner un à téléverser.", + "drop-package-file-or" : "Glissez-déposez un fichier de paquet ou", + "file-name" : "Nom du fichier", + "file-size" : "Taille du fichier", + "file-size-bytes" : "Taille du fichier en octets", + "idCopiedMessage" : "L'identifiant du paquet a été copié dans le presse-papiers", + "no-firmware-matching" : "Aucun paquet OTA de micrologiciel compatible correspondant à '{{entity}}' trouvé.", + "no-firmware-text" : "Aucun paquet OTA de micrologiciel compatible provisionné.", + "no-packages-text" : "Aucun paquet trouvé", + "no-software-matching" : "Aucun paquet OTA de logiciel compatible correspondant à '{{entity}}' trouvé.", + "no-software-text" : "Aucun paquet OTA de logiciel compatible provisionné.", + "ota-update" : "Mise à jour OTA", + "ota-update-details" : "Détails de la mise à jour OTA", + "ota-updates" : "Mises à jour OTA", + "package-file" : "Fichier du paquet", + "package-type" : "Type de paquet", + "packages-repository" : "Dépôt de paquets", + "search" : "Rechercher des paquets", + "selected-package" : "{ count, plural, =1 {1 paquet} other {# paquets} } sélectionné", + "title" : "Titre", + "title-required" : "Le titre est requis.", + "title-max-length" : "Le titre doit comporter moins de 256 caractères", + "types" : { + "firmware" : "Micrologiciel", + "software" : "Logiciel" + }, + "upload-binary-file" : "Téléverser un fichier binaire", + "use-external-url" : "Utiliser une URL externe", + "version" : "Version", + "version-required" : "La version est requise.", + "version-tag" : "Étiquette de version", + "version-tag-hint" : "L'étiquette personnalisée doit correspondre à la version du paquet rapportée par votre appareil.", + "version-max-length" : "La version doit comporter moins de 256 caractères", + "warning-after-save-no-edit" : "Une fois le paquet téléversé, vous ne pourrez plus modifier le titre, la version, le profil de l'appareil ni le type de paquet." + }, + "position" : { + "top" : "Haut", + "bottom" : "Bas", + "left" : "Gauche", + "right" : "Droite" + }, + "profile" : { + "profile" : "Profil", + "last-login-time" : "Dernière connexion", + "change-password" : "Changer le mot de passe", + "current-password" : "Mot de passe actuel", + "copy-jwt-token" : "Copier le jeton JWT", + "jwt-token" : "Jeton JWT", + "token-valid-till" : "Jeton valide jusqu'à", + "tokenCopiedSuccessMessage" : "Le jeton JWT a été copié dans le presse-papiers", + "tokenCopiedWarnMessage" : "Le jeton JWT est expiré ! Veuillez actualiser la page." + }, + "profiles" : { + "profiles" : "Profils" + }, + "security" : { + "security" : "Sécurité", + "general-settings" : "Paramètres de sécurité généraux", + "access-token" : "Jeton d'accès", + "access-token-required" : "Le jeton d'accès est requis", + "clientId" : "ID client", + "clientId-required" : "L'ID client est requis", + "username" : "Nom d'utilisateur", + "username-required" : "Le nom d'utilisateur est requis", + "ca-cert" : "Certificat CA", + "2fa" : { + "2fa" : "Authentification à deux facteurs", + "2fa-description" : "L'authentification à deux facteurs protège votre compte contre les accès non autorisés. Il vous suffit de saisir un code de sécurité lors de votre connexion.", + "authenticate-with" : "Vous pouvez vous authentifier avec :", + "disable-2fa-provider-text" : "Désactiver {{name}} rendra votre compte moins sécurisé", + "disable-2fa-provider-title" : "Êtes-vous sûr de vouloir désactiver {{name}} ?", + "get-new-code" : "Obtenir un nouveau code", + "main-2fa-method" : "Utiliser comme méthode principale d'authentification à deux facteurs", + "dialog" : { + "activation-step-description-email" : "Lors de votre prochaine connexion, un code de sécurité sera envoyé à votre adresse email.", + "activation-step-description-sms" : "Lors de votre prochaine connexion, un code de sécurité sera envoyé au numéro de téléphone.", + "activation-step-description-totp" : "Lors de votre prochaine connexion, vous devrez fournir un code d'authentification à deux facteurs.", + "activation-step-label" : "Activation", + "backup-code-description" : "Imprimez ces codes pour les avoir à portée de main lorsque vous devrez vous connecter. Chaque code peut être utilisé une seule fois.", + "backup-code-warn" : "Une fois que vous quittez cette page, ces codes ne pourront plus être affichés. Conservez-les en toute sécurité à l'aide des options ci-dessous.", + "download-txt" : "Télécharger (txt)", + "email-step-description" : "Saisissez une adresse email à utiliser comme méthode d'authentification.", + "email-step-label" : "Email", + "enable-email-title" : "Activer l'authentification par email", + "enable-sms-title" : "Activer l'authentification par SMS", + "enable-totp-title" : "Activer l'application d'authentification", + "enter-verification-code" : "Entrez le code à 6 chiffres ici", + "get-backup-code-title" : "Obtenir le code de secours", + "next" : "Suivant", + "scan-qr-code" : "Scannez ce code QR avec votre application d'authentification", + "send-code" : "Envoyer le code", + "sms-step-description" : "Saisissez un numéro de téléphone à utiliser comme méthode d'authentification.", + "sms-step-label" : "Numéro de téléphone", + "success" : "Succès !", + "totp-step-description-install" : "Vous pouvez installer des applications comme Google Authenticator, Authy ou Duo.", + "totp-step-description-open" : "Ouvrez l'application d'authentification sur votre téléphone mobile.", + "totp-step-label" : "Obtenir l'application", + "verification-code" : "Code à 6 chiffres", + "verification-code-invalid" : "Format de code de vérification invalide", + "verification-code-incorrect" : "Code de vérification incorrect", + "verification-code-many-request" : "Trop de requêtes. Vérifiez le code de vérification", + "verification-step-description" : "Entrez un code à 6 chiffres que nous venons d'envoyer à {{address}}", + "verification-step-label" : "Vérification" + }, + "provider" : { + "email" : "Email", + "email-description" : "Utilisez un code de sécurité envoyé à votre adresse email pour vous authentifier.", + "email-hint" : "Les codes d'authentification sont envoyés par email à {{ info }}", + "sms" : "SMS", + "sms-description" : "Utilisez votre téléphone pour vous authentifier. Nous vous enverrons un code de sécurité par SMS lors de votre connexion.", + "sms-hint" : "Les codes d'authentification sont envoyés par message texte à {{ info }}", + "totp" : "Application d'authentification", + "totp-description" : "Utilisez des applications comme Google Authenticator, Authy ou Duo sur votre téléphone pour vous authentifier. Elles génèrent un code de sécurité pour vous connecter.", + "totp-hint" : "L'application d'authentification est configurée pour votre compte", + "backup_code" : "Code de secours", + "backup-code-description" : "Ces codes d'accès imprimables à usage unique vous permettent de vous connecter lorsque vous n'avez pas votre téléphone, par exemple en voyage.", + "backup-code-hint" : "{{ info }} codes à usage unique sont actifs pour le moment" + } + }, + "password-requirement" : { + "at-least" : "Au moins :", + "character" : "{ count, plural, =1 {1 caractère} other {# caractères} }", + "digit" : "{ count, plural, =1 {1 chiffre} other {# chiffres} }", + "incorrect-password-try-again" : "Mot de passe incorrect. Veuillez réessayer", + "lowercase-letter" : "{ count, plural, =1 {1 lettre minuscule} other {# lettres minuscules} }", + "new-passwords-not-match" : "Les nouveaux mots de passe ne correspondent pas", + "password-should-not-contain-spaces" : "Votre mot de passe ne doit pas contenir d'espaces", + "password-not-meet-requirements" : "Le mot de passe ne respecte pas les exigences", + "password-requirements" : "Exigences du mot de passe", + "password-should-difference" : "Le nouveau mot de passe doit être différent de l'actuel", + "special-character" : "{ count, plural, =1 {1 caractère spécial} other {# caractères spéciaux} }", + "uppercase-letter" : "{ count, plural, =1 {1 lettre majuscule} other {# lettres majuscules} }", + "at-most" : "Au plus :" + } + }, + "relation" : { + "relations" : "Relations", + "direction" : "Direction", + "clear-relation-type" : "Effacer le type de relation", + "search-direction" : { + "FROM" : "Depuis", + "TO" : "Vers" + }, + "direction-type" : { + "FROM" : "de", + "TO" : "vers" + }, + "from-relations" : "Relations sortantes", + "to-relations" : "Relations entrantes", + "selected-relations" : "{ count, plural, =1 {1 relation} other {# relations} } sélectionnée(s)", + "type" : "Type", + "to-entity-type" : "Type d'entité cible", + "to-entity-name" : "Nom de l'entité cible", + "from-entity-type" : "Type d'entité source", + "from-entity-name" : "Nom de l'entité source", + "to-entity" : "Vers l'entité", + "from-entity" : "Depuis l'entité", + "delete" : "Supprimer la relation", + "relation-type" : "Type de relation", + "relation-type-required" : "Le type de relation est requis.", + "relation-type-max-length" : "Le type de relation doit comporter moins de 256 caractères", + "any-relation-type" : "Tout type", + "add" : "Ajouter une relation", + "edit" : "Modifier la relation", + "delete-to-relation-title" : "Êtes-vous sûr de vouloir supprimer la relation vers l'entité '{{entityName}}' ?", + "delete-to-relation-text" : "Attention, après confirmation, l'entité '{{entityName}}' ne sera plus liée à l'entité actuelle.", + "delete-to-relations-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 relation} other {# relations} } ?", + "delete-to-relations-text" : "Attention, après confirmation, toutes les relations sélectionnées seront supprimées et les entités correspondantes ne seront plus liées à l'entité actuelle.", + "delete-from-relation-title" : "Êtes-vous sûr de vouloir supprimer la relation depuis l'entité '{{entityName}}' ?", + "delete-from-relation-text" : "Attention, après confirmation, l'entité actuelle ne sera plus liée à l'entité '{{entityName}}'.", + "delete-from-relations-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 relation} other {# relations} } ?", + "delete-from-relations-text" : "Attention, après confirmation, toutes les relations sélectionnées seront supprimées et l'entité actuelle ne sera plus liée aux entités correspondantes.", + "remove-relation-filter" : "Supprimer le filtre de relation", + "remove-filter" : "Supprimer le filtre", + "add-relation-filter" : "Ajouter un filtre de relation", + "any-relation" : "Toute relation", + "relation-filters" : "Filtres de relation", + "additional-info" : "Infos supplémentaires (JSON)", + "invalid-additional-info" : "Impossible d'analyser le JSON des infos supplémentaires.", + "no-relations-text" : "Aucune relation trouvée", + "not" : "Non" + }, + "resource" : { + "add" : "Ajouter une ressource", + "all-types" : "Tous", + "copyId" : "Copier l'identifiant de la ressource", + "delete" : "Supprimer la ressource", + "delete-resource-text" : "Attention, après confirmation, la ressource ne pourra pas être récupérée.", + "delete-resource-title" : "Êtes-vous sûr de vouloir supprimer la ressource '{{resourceTitle}}' ?", + "delete-resources-action-title" : "Supprimer { count, plural, =1 {1 ressource} other {# ressources} }", + "delete-resources-text" : "Veuillez noter que les ressources sélectionnées, même si elles sont utilisées dans les profils de périphériques, seront supprimées.", + "delete-resources-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 ressource} other {# ressources} } ?", + "download" : "Télécharger la ressource", + "drop-file" : "Déposez un fichier ressource ou cliquez pour en sélectionner un à télécharger.", + "drop-resource-file-or" : "Glissez et déposez un fichier ressource ou", + "empty" : "Ressource vide", + "file-name" : "Nom du fichier", + "idCopiedMessage" : "L'identifiant de la ressource a été copié dans le presse-papiers", + "no-resource-matching" : "Aucune ressource correspondant à '{{widgetsBundle}}' n'a été trouvée.", + "no-resource-text" : "Aucune ressource trouvée", + "open-widgets-bundle" : "Ouvrir la collection de widgets", + "resource" : "Ressource", + "resource-file" : "Fichier ressource", + "resource-files" : "Fichiers ressource", + "resource-library-details" : "Détails de la ressource", + "resource-type" : "Type de ressource", + "resources-library" : "Bibliothèque de ressources", + "search" : "Rechercher des ressources", + "selected-resources" : "{ count, plural, =1 {1 ressource} other {# ressources} } sélectionnée(s)", + "system" : "Système", + "title" : "Titre", + "title-required" : "Le titre est requis.", + "title-max-length" : "Le titre doit comporter moins de 256 caractères", + "type" : { + "jks" : "JKS", + "js-module" : "Module JS", + "lwm2m-model" : "Modèle LWM2M", + "pkcs-12" : "PKCS #12" + }, + "resource-sub-type" : "Sous-type", + "sub-type" : { + "image" : "Image", + "scada-symbol" : "Symbole SCADA", + "extension" : "Extension", + "module" : "Module" + } + }, + "javascript" : { + "add" : "Ajouter une ressource JavaScript", + "delete" : "Supprimer la ressource JavaScript", + "delete-javascript-resource-text" : "Attention, après confirmation, la ressource JavaScript ne pourra pas être récupérée.", + "delete-javascript-resource-title" : "Êtes-vous sûr de vouloir supprimer la ressource JavaScript '{{resourceTitle}}' ?", + "delete-javascript-resources-action-title" : "Supprimer { count, plural, =1 {1 ressource JavaScript} other {# ressources JavaScript} }", + "delete-javascript-resources-text" : "Veuillez noter que les ressources JavaScript sélectionnées, même si elles sont utilisées dans des fonctions JavaScript, seront supprimées.", + "delete-javascript-resources-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 ressource JavaScript} other {# ressources JavaScript} } ?", + "delete-javascript-resource-in-use-text" : "Si vous souhaitez quand même supprimer la ressource JavaScript, cliquez sur le bouton Supprimer quand même.", + "download" : "Télécharger la ressource JavaScript", + "upload-from-file" : "Téléverser JavaScript depuis un fichier", + "resource-file" : "Fichier ressource JavaScript", + "drop-file" : "Déposez un fichier JavaScript ou cliquez pour en sélectionner un à téléverser.", + "drop-resource-file-or" : "Glissez-déposez un fichier JavaScript ou", + "javascript-library" : "Bibliothèque JavaScript", + "javascript-type" : "Type JavaScript", + "javascript-resource-details" : "Détails de la ressource JavaScript", + "javascript-resource-is-in-use" : "La ressource JavaScript est utilisée par d'autres entités", + "javascript-resources-are-in-use" : "Les ressources JavaScript sont utilisées par d'autres entités", + "javascript-resource-is-in-use-text" : "La ressource JavaScript '{{title}}' n'a pas été supprimée car elle est utilisée par les entités suivantes :", + "javascript-resources-are-in-use-text" : "Toutes les ressources JavaScript n'ont pas été supprimées car elles sont utilisées par d'autres entités.
Vous pouvez consulter les entités référencées en cliquant sur le bouton Références dans la ligne correspondante de la ressource.
Si vous souhaitez quand même supprimer ces ressources JavaScript, sélectionnez-les dans le tableau ci-dessous et cliquez sur le bouton Supprimer la sélection.", + "search" : "Rechercher des ressources JavaScript", + "selected-javascript-resources" : "{ count, plural, =1 {1 ressource JavaScript} other {# ressources JavaScript} } sélectionnée(s)", + "no-javascript-resource-text" : "Aucune ressource JavaScript trouvée", + "all-types" : "Tous", + "module-script" : "Script module" + }, + "rpc" : { + "error" : { + "target-device-is-not-set" : "Le périphérique cible n'est pas défini !", + "invalid-target-entity" : "Les commandes RPC ne sont pas prises en charge par l'entité {{entityType}}.", + "failed-to-resolve-target-device" : "Échec de résolution du périphérique cible !", + "request-timeout" : "Délai d'attente de la requête dépassé", + "rpc-http-error" : "Erreur : {{status}} - {{statusText}}" + } + }, + "rulechain" : { + "rulechain" : "Chaîne de règles", + "rulechain-events" : "Événements de la chaîne de règles", + "rulechains" : "Chaînes de règles", + "root" : "Racine", + "delete" : "Supprimer la chaîne de règles", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "name-max-length" : "Le nom doit contenir moins de 256 caractères", + "description" : "Description", + "add" : "Ajouter une chaîne de règles", + "set-root" : "Définir comme racine", + "set-root-rulechain-title" : "Êtes-vous sûr de vouloir définir la chaîne de règles '{{ruleChainName}}' comme racine ?", + "set-root-rulechain-text" : "Après confirmation, la chaîne de règles deviendra la racine et gérera tous les messages de transport entrants.", + "delete-rulechain-title" : "Êtes-vous sûr de vouloir supprimer la chaîne de règles '{{ruleChainName}}' ?", + "delete-rulechain-text" : "Attention, après confirmation, la chaîne de règles et toutes les données associées ne pourront pas être récupérées.", + "delete-rulechains-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} } ?", + "delete-rulechains-action-title" : "Supprimer { count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} }", + "delete-rulechains-text" : "Attention, après confirmation, toutes les chaînes de règles sélectionnées seront supprimées ainsi que toutes les données associées.", + "add-rulechain-text" : "Ajouter une nouvelle chaîne de règles", + "no-rulechains-text" : "Aucune chaîne de règles trouvée", + "rulechain-details" : "Détails de la chaîne de règles", + "details" : "Détails", + "events" : "Événements", + "system" : "Système", + "import" : "Importer une chaîne de règles", + "export" : "Exporter la chaîne de règles", + "export-failed-error" : "Impossible d'exporter la chaîne de règles : {{error}}", + "create-new-rulechain" : "Créer une nouvelle chaîne de règles", + "rulechain-file" : "Fichier de chaîne de règles", + "invalid-rulechain-file-error" : "Impossible d'importer la chaîne de règles : structure de données invalide.", + "copyId" : "Copier l'identifiant de la chaîne de règles", + "idCopiedMessage" : "Identifiant de la chaîne de règles copié dans le presse-papiers", + "select-rulechain" : "Sélectionner une chaîne de règles", + "no-rulechains-matching" : "Aucune chaîne de règles correspondant à '{{entity}}' n'a été trouvée.", + "rulechain-required" : "La chaîne de règles est requise", + "management" : "Gestion des règles", + "debug-mode" : "Mode débogage", + "search" : "Rechercher des chaînes de règles", + "selected-rulechains" : "{ count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} } sélectionnée(s)", + "open-rulechain" : "Ouvrir la chaîne de règles", + "edge-template-root" : "Racine du modèle Edge", + "assign-to-edge" : "Attribuer à une Edge", + "edge-rulechain" : "Chaîne de règles Edge", + "unassign-rulechain-from-edge-text" : "Après confirmation, la chaîne de règles sera désattribuée et ne sera plus accessible depuis la Edge.", + "unassign-rulechains-from-edge-title" : "Êtes-vous sûr de vouloir désattribuer { count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} } ?", + "unassign-rulechains-from-edge-text" : "Après confirmation, toutes les chaînes de règles sélectionnées seront désattribuées et ne seront plus accessibles depuis la Edge.", + "assign-rulechain-to-edge-title" : "Attribuer des chaînes de règles à la Edge", + "assign-rulechain-to-edge-text" : "Veuillez sélectionner les chaînes de règles à attribuer à la Edge", + "set-edge-template-root-rulechain" : "Définir comme racine de modèle Edge", + "set-edge-template-root-rulechain-title" : "Êtes-vous sûr de vouloir définir la chaîne de règles '{{ruleChainName}}' comme racine du modèle Edge ?", + "set-edge-template-root-rulechain-text" : "Après confirmation, cette chaîne de règles sera définie comme racine de modèle pour toutes les nouvelles Edges créées.", + "invalid-rulechain-type-error" : "Impossible d'importer la chaîne de règles : type invalide. Type attendu : {{expectedRuleChainType}}.", + "set-auto-assign-to-edge" : "Attribuer automatiquement aux Edge à la création", + "set-auto-assign-to-edge-title" : "Êtes-vous sûr de vouloir attribuer automatiquement la chaîne de règles '{{ruleChainName}}' aux Edge à la création ?", + "set-auto-assign-to-edge-text" : "Après confirmation, la chaîne de règles Edge sera automatiquement attribuée aux Edge lors de leur création.", + "unset-auto-assign-to-edge" : "Ne pas attribuer automatiquement aux Edge", + "unset-auto-assign-to-edge-title" : "Êtes-vous sûr de ne pas vouloir attribuer automatiquement la chaîne de règles '{{ruleChainName}}' aux Edge à la création ?", + "unset-auto-assign-to-edge-text" : "Après confirmation, la chaîne de règles Edge ne sera plus automatiquement attribuée aux Edge lors de leur création.", + "unassign-rulechain-title" : "Êtes-vous sûr de vouloir désattribuer la chaîne de règles '{{ruleChainName}}' ?", + "unassign-rulechains" : "Désattribuer les chaînes de règles" + }, + "rulenode" : { + "rule-node-events" : "Événements du nœud de règle", + "details" : "Détails", + "events" : "Événements", + "search" : "Rechercher des nœuds", + "open-node-library" : "Ouvrir la bibliothèque de nœuds", + "close-node-library" : "Fermer la bibliothèque de nœuds", + "add" : "Ajouter un nœud de règle", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "name-max-length" : "Le nom doit contenir moins de 256 caractères", + "type" : "Type", + "rule-node-description" : "Description du nœud de règle", + "delete" : "Supprimer le nœud de règle", + "select-all-objects" : "Sélectionner tous les nœuds et connexions", + "deselect-all-objects" : "Désélectionner tous les nœuds et connexions", + "delete-selected-objects" : "Supprimer les nœuds et connexions sélectionnés", + "delete-selected" : "Supprimer la sélection", + "create-nested-rulechain" : "Créer une chaîne de règles imbriquée", + "select-all" : "Tout sélectionner", + "copy-selected" : "Copier la sélection", + "deselect-all" : "Tout désélectionner", + "rulenode-details" : "Détails du nœud de règle", + "debug-mode" : "Mode débogage", + "singleton" : "Singleton", + "configuration" : "Configuration", + "link" : "Lien", + "link-details" : "Détails du lien du nœud de règle", + "add-link" : "Ajouter un lien", + "link-label" : "Étiquette du lien", + "link-label-required" : "L’étiquette du lien est requise.", + "custom-link-label" : "Étiquette de lien personnalisée", + "custom-link-label-required" : "L’étiquette de lien personnalisée est requise.", + "link-labels" : "Étiquettes de lien", + "link-labels-required" : "Les étiquettes de lien sont requises.", + "no-link-labels-found" : "Aucune étiquette de lien trouvée", + "no-link-label-matching" : "'{{label}}' introuvable.", + "create-new-link-label" : "Créer une nouvelle étiquette !", + "type-filter" : "Filtre", + "type-filter-details" : "Filtrer les messages entrants selon des conditions configurées", + "type-enrichment" : "Enrichissement", + "type-enrichment-details" : "Ajouter des informations supplémentaires dans les métadonnées du message", + "type-transformation" : "Transformation", + "type-transformation-details" : "Modifier la charge utile et les métadonnées du message", + "type-action" : "Action", + "type-action-details" : "Effectuer une action spéciale", + "type-external" : "Externe", + "type-external-details" : "Interagit avec un système externe", + "type-rule-chain" : "Chaîne de règles", + "type-rule-chain-details" : "Transmet les messages entrants à la chaîne de règles spécifiée", + "type-flow" : "Flux", + "type-flow-details" : "Organise le flux des messages", + "type-input" : "Entrée", + "type-input-details" : "Entrée logique de la chaîne de règles, transmet les messages entrants au nœud suivant", + "type-unknown" : "Inconnu", + "type-unknown-details" : "Nœud de règle non résolu", + "directive-is-not-loaded" : "La directive de configuration définie '{{directiveName}}' n'est pas disponible.", + "ui-resources-load-error" : "Échec du chargement des ressources UI de configuration.", + "invalid-target-rulechain" : "Impossible de résoudre la chaîne de règles cible !", + "test-script-function" : "Tester la fonction du script", + "script-lang-java-script" : "JavaScript", + "script-lang-tbel" : "TBEL", + "message" : "Message", + "message-type" : "Type de message", + "select-message-type" : "Sélectionner un type de message", + "message-type-required" : "Le type de message est requis", + "metadata" : "Métadonnées", + "metadata-required" : "Les entrées de métadonnées ne peuvent pas être vides.", + "output" : "Sortie", + "test" : "Test", + "help" : "Aide", + "reset-debug-settings" : "Réinitialiser les paramètres de débogage dans tous les nœuds", + "test-with-this-message" : "{{test}} avec ce message", + "queue-hint" : "Sélectionner une file pour le transfert de message vers une autre file. La file 'Main' est utilisée par défaut.", + "queue-singleton-hint" : "Sélectionner une file pour le transfert de message dans des environnements multi-instances. La file 'Main' est utilisée par défaut." + }, + "rule-node-config" : { + "id" : "Identifiant", + "additional-info" : "Informations supplémentaires", + "advanced-settings" : "Paramètres avancés", + "create-entity-if-not-exists" : "Créer une entité si elle n'existe pas", + "create-entity-if-not-exists-hint" : "Si activé, une nouvelle entité avec les paramètres spécifiés sera créée à moins qu'elle n'existe déjà. Les entités existantes seront utilisées telles quelles pour la relation.", + "select-device-connectivity-event" : "Sélectionner un événement de connectivité du dispositif", + "entity-name-pattern" : "Modèle de nom", + "device-name-pattern" : "Nom du dispositif", + "asset-name-pattern" : "Nom de l'actif", + "entity-view-name-pattern" : "Nom de la vue d'entité", + "customer-title-pattern" : "Titre du client", + "dashboard-name-pattern" : "Titre du tableau de bord", + "user-name-pattern" : "Email de l'utilisateur", + "edge-name-pattern" : "Nom de l'Edge", + "entity-name-pattern-required" : "Le modèle de nom est requis", + "entity-name-pattern-hint" : "Le champ modèle de nom prend en charge la templatisation. Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour une valeur des métadonnées.", + "copy-message-type" : "Copier le type de message", + "entity-type-pattern" : "Modèle de type", + "entity-type-pattern-required" : "Le modèle de type est requis", + "message-type-value" : "Valeur du type de message", + "message-type-value-required" : "La valeur du type de message est requise", + "message-type-value-max-length" : "La valeur du type de message doit contenir moins de 256 caractères", + "output-message-type" : "Type de message de sortie", + "entity-cache-expiration" : "Temps d’expiration du cache des entités (s)", + "entity-cache-expiration-hint" : "Spécifie l'intervalle de temps maximal pour stocker les enregistrements d'entités trouvées. Une valeur de 0 signifie que les enregistrements n'expireront jamais.", + "entity-cache-expiration-required" : "Le temps d’expiration du cache des entités est requis.", + "entity-cache-expiration-range" : "Le temps d’expiration du cache des entités doit être supérieur ou égal à 0.", + "customer-name-pattern" : "Titre du client", + "customer-name-pattern-required" : "Le titre du client est requis", + "customer-name-pattern-hint" : "Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour une valeur des métadonnées.", + "create-customer-if-not-exists" : "Créer un client s’il n’existe pas", + "unassign-from-customer" : "Détacher d’un client spécifique si l’initiateur est un tableau de bord", + "unassign-from-customer-tooltip" : "Seuls les tableaux de bord peuvent être attribués à plusieurs clients à la fois.\nSi l’initiateur du message est un tableau de bord, vous devez spécifier explicitement le titre du client pour le détacher.", + "customer-cache-expiration" : "Durée d’expiration du cache des clients (s)", + "customer-cache-expiration-hint" : "Spécifie la durée maximale pour stocker les enregistrements clients trouvés. Une valeur de 0 signifie que les enregistrements n’expireront jamais.", + "customer-cache-expiration-required" : "La durée d’expiration du cache des clients est requise.", + "customer-cache-expiration-range" : "La durée d’expiration du cache des clients doit être supérieure ou égale à 0.", + "interval-start" : "Début de l’intervalle", + "interval-end" : "Fin de l’intervalle", + "time-unit" : "Unité de temps", + "fetch-mode" : "Mode de récupération", + "order-by-timestamp" : "Trier par horodatage", + "limit" : "Limite", + "limit-hint" : "Valeur minimale de limite : 2, maximale : 1000. Pour récupérer une seule entrée, sélectionnez le mode de récupération 'Premier' ou 'Dernier'.", + "limit-required" : "La limite est requise.", + "limit-range" : "La limite doit être comprise entre 2 et 1000.", + "time-unit-milliseconds" : "Millisecondes", + "time-unit-seconds" : "Secondes", + "time-unit-minutes" : "Minutes", + "time-unit-hours" : "Heures", + "time-unit-days" : "Jours", + "time-value-range" : "Plage autorisée de 1 à 2147483647.", + "start-interval-value-required" : "Le début de l’intervalle est requis.", + "end-interval-value-required" : "La fin de l’intervalle est requise.", + "filter" : "Filtre", + "switch" : "Commutateur", + "math-templatization-tooltip" : "Ce champ prend en charge la templatisation. Utilisez $[messageKey] pour une valeur du message et ${metadataKey} pour les métadonnées.", + "add-message-type" : "Ajouter un type de message", + "select-message-types-required" : "Au moins un type de message doit être sélectionné.", + "select-message-types" : "Sélectionner des types de message", + "no-message-types-found" : "Aucun type de message trouvé", + "no-message-type-matching" : "'{{messageType}}' introuvable.", + "create-new-message-type" : "Créer un nouveau.", + "message-types-required" : "Les types de message sont requis.", + "client-attributes" : "Attributs client", + "shared-attributes" : "Attributs partagés", + "server-attributes" : "Attributs serveur", + "attributes-keys" : "Clés d’attributs", + "attributes-keys-required" : "Les clés d’attributs sont requises", + "attributes-scope" : "Portée des attributs", + "attributes-scope-value" : "Valeur de la portée des attributs", + "attributes-scope-value-copy" : "Copier la valeur de portée des attributs", + "attributes-scope-hint" : "Utilisez la clé de métadonnée 'scope' pour définir dynamiquement la portée par message. Cela remplace la portée définie dans la configuration.", + "notify-device" : "Forcer la notification au dispositif", + "send-attributes-updated-notification" : "Envoyer une notification d’attributs mis à jour", + "send-attributes-updated-notification-hint" : "Envoyer une notification sur les attributs mis à jour comme message séparé à la file du moteur de règles.", + "send-attributes-deleted-notification" : "Envoyer une notification d’attributs supprimés", + "send-attributes-deleted-notification-hint" : "Envoyer une notification sur les attributs supprimés comme message séparé à la file du moteur de règles.", + "update-attributes-only-on-value-change" : "Mettre à jour uniquement si la valeur change", + "update-attributes-only-on-value-change-hint" : "Met à jour les attributs à chaque message entrant, même si leur valeur n’a pas changé. Cela augmente l’usage de l’API et réduit les performances.", + "update-attributes-only-on-value-change-hint-enabled" : "Met à jour les attributs uniquement si leur valeur a changé. Si aucune modification, aucune mise à jour ni notification ne sera envoyée.", + "fetch-credentials-to-metadata" : "Transférer les identifiants dans les métadonnées", + "notify-device-on-update-hint" : "Si activé, force la notification au dispositif lors de la mise à jour des attributs partagés. Sinon, contrôlé via le paramètre 'notifyDevice' dans les métadonnées du message.", + "notify-device-on-delete-hint" : "Si activé, force la notification au dispositif lors de la suppression des attributs partagés. Sinon, contrôlé via 'notifyDevice' dans les métadonnées du message.", + "latest-timeseries" : "Clés de données de séries temporelles récentes", + "timeseries-keys" : "Clés de séries temporelles", + "timeseries-keys-required" : "Au moins une clé de série temporelle doit être sélectionnée.", + "add-timeseries-key" : "Ajouter une clé de série temporelle", + "add-message-field" : "Ajouter un champ de message", + "relation-search-parameters" : "Paramètres de recherche de relations", + "relation-parameters" : "Paramètres de relation", + "add-metadata-field" : "Ajouter un champ de métadonnée", + "data-keys" : "Noms des champs du message", + "copy-from" : "Copier depuis", + "data-to-metadata" : "Données vers métadonnées", + "metadata-to-data" : "Métadonnées vers données", + "use-regular-expression-hint" : "Utilisez une expression régulière pour copier des clés par motif.\n\nAstuces :\nAppuyez sur 'Entrée' pour valider un champ.\nAppuyez sur 'Retour arrière' pour le supprimer. Plusieurs champs sont pris en charge.", + "interval" : "Intervalle", + "interval-required" : "L’intervalle est requis", + "interval-hint" : "Intervalle de déduplication en secondes.", + "interval-min-error" : "La valeur minimale autorisée est 1", + "max-pending-msgs" : "Nombre max. de messages en attente", + "max-pending-msgs-hint" : "Nombre maximum de messages stockés en mémoire pour chaque ID de déduplication unique.", + "max-pending-msgs-required" : "Le nombre max. de messages en attente est requis", + "max-pending-msgs-max-error" : "Valeur max. autorisée : 1000", + "max-pending-msgs-min-error" : "Valeur min. autorisée : 1", + "max-retries" : "Nombre max. de tentatives", + "max-retries-required" : "Le nombre max. de tentatives est requis", + "max-retries-hint" : "Nombre maximum de tentatives pour pousser les messages dédupliqués dans la file. Un délai de 10 secondes est appliqué entre les tentatives.", + "max-retries-max-error" : "Valeur max. autorisée : 100", + "max-retries-min-error" : "Valeur min. autorisée : 0", + "strategy" : "Stratégie", + "strategy-required" : "La stratégie est requise", + "strategy-all-hint" : "Retourne tous les messages arrivés pendant la période de déduplication sous forme de tableau JSON. Chaque élément contient les propriétés msg et metadata.", + "strategy-first-hint" : "Retourne le premier message arrivé pendant la période de déduplication.", + "strategy-last-hint" : "Retourne le dernier message arrivé pendant la période de déduplication.", + "first" : "Premier", + "last" : "Dernier", + "all" : "Tous", + "output-msg-type-hint" : "Type de message du résultat de la déduplication.", + "queue-name-hint" : "Nom de la file où publier le résultat de la déduplication.", + "keys" : "Clés", + "keys-required" : "Les clés sont requises", + "rename-keys-in" : "Renommer les clés dans", + "data" : "Données", + "message" : "Message", + "metadata" : "Métadonnées", + "current-key-name" : "Nom actuel de la clé", + "key-name-required" : "Le nom de la clé est requis", + "new-key-name" : "Nouveau nom de la clé", + "new-key-name-required" : "Le nouveau nom de la clé est requis", + "metadata-keys" : "Noms des champs de métadonnées", + "json-path-expression" : "Expression JSONPath", + "json-path-expression-required" : "L'expression JSONPath est requise", + "json-path-expression-hint" : "JSONPath indique un chemin vers un ou plusieurs éléments dans une structure JSON. '$' représente la racine.", + "relations-query" : "Requête sur les relations", + "device-relations-query" : "Requête sur les relations du dispositif", + "max-relation-level" : "Niveau de relation max.", + "max-relation-level-error" : "La valeur doit être supérieure à 0 ou non définie.", + "max-relation-level-invalid" : "La valeur doit être un entier.", + "relation-type" : "Type de relation", + "relation-type-pattern" : "Modèle du type de relation", + "relation-type-pattern-required" : "Le modèle du type de relation est requis", + "relation-types-list" : "Types de relation à propager", + "relation-types-list-hint" : "Si aucun type de propagation n’est sélectionné, les alarmes seront propagées sans filtrage.", + "unlimited-level" : "Niveau illimité", + "latest-telemetry" : "Dernières télémétries", + "add-telemetry-key" : "Ajouter une clé de télémétrie", + "delete-from" : "Supprimer de", + "use-regular-expression-delete-hint" : "Utiliser des expressions régulières pour supprimer les clés par motif.\n\nAstuces :\nAppuyez sur 'Entrée' pour valider un champ.\nAppuyez sur 'Retour arrière' pour le supprimer. Plusieurs champs sont pris en charge.", + "fetch-into" : "Récupérer dans", + "attr-mapping" : "Correspondance des attributs :", + "source-attribute" : "Clé de l’attribut source", + "source-attribute-required" : "La clé de l’attribut source est requise.", + "source-telemetry" : "Clé de télémétrie source", + "source-telemetry-required" : "La clé de télémétrie source est requise.", + "target-key" : "Clé cible", + "target-key-required" : "La clé cible est requise.", + "attr-mapping-required" : "Au moins une correspondance d'attribut doit être spécifiée.", + "fields-mapping" : "Correspondance des champs", + "fields-mapping-hint" : "Si le champ de message est défini sur $entityId, l’ID de l’initiateur du message sera enregistré dans la colonne correspondante.", + "relations-query-config-direction-suffix" : "initiateur", + "profile-name" : "Nom du profil", + "fetch-circle-parameter-info-from-metadata-hint" : "Le champ de métadonnée '{{perimeterKeyName}}' doit être défini au format : {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint" : "Le champ de métadonnée '{{perimeterKeyName}}' doit être défini au format : [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip" : "Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour une des métadonnées.", + "fields-mapping-required" : "Au moins une correspondance de champ doit être spécifiée.", + "at-least-one-field-required" : "Au moins un champ d’entrée doit avoir une valeur fournie.", + "originator-fields-sv-map-hint" : "Les champs de la clé cible prennent en charge la templatisation. Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour les métadonnées.", + "sv-map-hint" : "Seuls les champs de clé cible prennent en charge la templatisation. Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour les métadonnées.", + "source-field" : "Champ source", + "source-field-required" : "Le champ source est requis.", + "originator-source" : "Source de l'initiateur", + "new-originator" : "Nouvel initiateur", + "originator-customer" : "Client", + "originator-tenant" : "Tenant", + "originator-related" : "Entité liée", + "originator-alarm-originator" : "Origine de l’alarme", + "originator-entity" : "Entité par modèle de nom", + "clone-message" : "Cloner le message", + "transform" : "Transformer", + "default-ttl" : "Durée de vie (TTL) par défaut", + "default-ttl-required" : "La durée de vie (TTL) par défaut est requise.", + "default-ttl-hint" : "Le nœud de règle utilisera la valeur TTL des métadonnées du message. Si aucune valeur n'est présente, celle spécifiée dans la configuration sera utilisée. Si la valeur est 0, la valeur TTL du profil de locataire sera appliquée.", + "default-ttl-zero-hint" : "Aucun TTL ne sera appliqué si la valeur est 0.", + "min-default-ttl-message" : "Seule la valeur 0 est autorisée comme minimum TTL.", + "generation-parameters" : "Paramètres de génération", + "message-count" : "Limite de messages générés (0 - illimité)", + "message-count-required" : "La limite de messages générés est requise.", + "min-message-count-message" : "Seule la valeur minimale de 0 est autorisée.", + "period-seconds" : "Période en secondes", + "period-seconds-required" : "La période est requise.", + "generation-frequency-seconds" : "Fréquence de génération (en secondes)", + "generation-frequency-required" : "La fréquence de génération est requise.", + "min-generation-frequency-message" : "60 secondes minimum sont requises.", + "script-lang-tbel" : "TBEL", + "script-lang-js" : "JS", + "use-metadata-period-in-seconds-patterns" : "Utiliser le motif de période en secondes", + "use-metadata-period-in-seconds-patterns-hint" : "Si activé, le nœud de règle utilisera le motif de période en secondes depuis les métadonnées ou les données du message.", + "period-in-seconds-pattern" : "Motif de période en secondes", + "period-in-seconds-pattern-required" : "Le motif de période en secondes est requis", + "min-period-seconds-message" : "La période minimale autorisée est de 60 secondes.", + "originator" : "Initiateur", + "message-body" : "Corps du message", + "message-metadata" : "Métadonnées du message", + "generate" : "Générer", + "current-rule-node" : "Nœud de règle actuel", + "current-tenant" : "Locataire actuel", + "generator-function" : "Fonction génératrice", + "test-generator-function" : "Tester la fonction génératrice", + "generator" : "Générateur", + "test-filter-function" : "Tester la fonction de filtre", + "test-switch-function" : "Tester la fonction de commutation", + "test-transformer-function" : "Tester la fonction de transformation", + "transformer" : "Transformateur", + "alarm-create-condition" : "Condition de création d’alarme", + "test-condition-function" : "Tester la fonction conditionnelle", + "alarm-clear-condition" : "Condition de suppression de l’alarme", + "alarm-details-builder" : "Constructeur de détails d’alarme", + "test-details-function" : "Tester la fonction de détails", + "alarm-type" : "Type d’alarme", + "select-entity-types" : "Sélectionner les types d’entités", + "alarm-type-required" : "Le type d’alarme est requis.", + "alarm-severity" : "Gravité de l’alarme", + "alarm-severity-required" : "La gravité de l’alarme est requise", + "alarm-severity-pattern" : "Motif de gravité d’alarme", + "alarm-status-filter" : "Filtre d’état d’alarme", + "alarm-status-list-empty" : "La liste des états d’alarme est vide", + "no-alarm-status-matching" : "Aucun état d’alarme correspondant trouvé.", + "propagate" : "Propager l’alarme aux entités liées", + "propagate-to-owner" : "Propager l’alarme au propriétaire (Client ou Locataire)", + "propagate-to-tenant" : "Propager l’alarme au locataire", + "condition" : "Condition", + "details" : "Détails", + "to-string" : "Vers chaîne de caractères", + "test-to-string-function" : "Tester la fonction toString", + "from-template" : "De", + "from-template-required" : "Le champ De est requis", + "message-to-metadata" : "Du message vers les métadonnées", + "metadata-to-message" : "Des métadonnées vers le message", + "from-message" : "Depuis le message", + "from-metadata" : "Depuis les métadonnées", + "to-template" : "À", + "to-template-required" : "Le champ À est requis", + "mail-address-list-template-hint" : "Liste d’adresses séparées par des virgules, utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message.", + "cc-template" : "Cc", + "bcc-template" : "Cci", + "subject-template" : "Objet", + "subject-template-required" : "Le champ Objet est requis", + "body-template" : "Corps", + "body-template-required" : "Le corps du message est requis", + "dynamic-mail-body-type" : "Type dynamique de corps de mail", + "mail-body-type" : "Type de corps de mail", + "body-type-template" : "Modèle de type de corps", + "reply-routing-configuration" : "Configuration du routage de réponse", + "rpc-reply-routing-configuration-hint" : "Ces paramètres spécifient les clés des métadonnées pour identifier le service, la session et la requête pour renvoyer la réponse.", + "reply-routing-configuration-hint" : "Ces paramètres spécifient les clés des métadonnées pour identifier le service et la requête pour renvoyer la réponse.", + "request-id-metadata-attribute" : "ID de requête", + "service-id-metadata-attribute" : "ID du service", + "session-id-metadata-attribute" : "ID de session", + "timeout-sec" : "Délai d'expiration en secondes", + "timeout-required" : "Le délai d'expiration est requis", + "min-timeout-message" : "Seule une valeur minimale de 0 est autorisée.", + "endpoint-url-pattern" : "Motif d'URL du point de terminaison", + "endpoint-url-pattern-required" : "Le motif d'URL du point de terminaison est requis", + "request-method" : "Méthode de requête", + "use-simple-client-http-factory" : "Utiliser une fabrique HTTP client simple", + "ignore-request-body" : "Sans corps de requête", + "parse-to-plain-text" : "Analyser en texte brut", + "parse-to-plain-text-hint" : "Si sélectionné, le corps du message sera converti de chaîne JSON en texte brut, par exemple msg = \"Hello,\\t\"world\"\" devient Hello, \"world\"", + "read-timeout" : "Délai de lecture (ms)", + "read-timeout-hint" : "Une valeur de 0 signifie un délai d'expiration infini", + "max-parallel-requests-count" : "Nombre maximal de requêtes parallèles", + "max-parallel-requests-count-hint" : "Une valeur de 0 signifie qu'il n'y a pas de limite de traitement parallèle", + "max-response-size" : "Taille maximale de réponse (en Ko)", + "max-response-size-hint" : "Quantité maximale de mémoire utilisée pour le tampon lors du décodage/encodage des messages HTTP comme les charges JSON ou XML", + "headers" : "En-têtes", + "headers-hint" : "Utilisez ${metadataKey} pour une valeur des métadonnées, $[messageKey] pour une valeur du corps du message", + "header" : "En-tête", + "header-required" : "L'en-tête est requis", + "value" : "Valeur", + "value-required" : "La valeur est requise", + "topic-pattern" : "Motif de sujet", + "key-pattern" : "Motif de clé", + "key-pattern-hint" : "Facultatif. Si une partition est précisée, elle sera utilisée. Sinon, la clé est utilisée. Sans clé, un round-robin sera appliqué.", + "topic-pattern-required" : "Le motif de sujet est requis", + "topic" : "Sujet", + "topic-required" : "Le sujet est requis", + "bootstrap-servers" : "Serveurs bootstrap", + "bootstrap-servers-required" : "Les serveurs bootstrap sont requis", + "other-properties" : "Autres propriétés", + "key" : "Clé", + "key-required" : "La clé est requise", + "retries" : "Réessais automatiques en cas d’échec", + "min-retries-message" : "Seule une valeur minimale de 0 est autorisée.", + "batch-size-bytes" : "Taille de lot produit (en octets)", + "min-batch-size-bytes-message" : "Seule une taille minimale de 0 est autorisée.", + "linger-ms" : "Temps de mise en tampon (ms)", + "min-linger-ms-message" : "Seule une valeur minimale de 0 ms est autorisée.", + "buffer-memory-bytes" : "Taille maximale du tampon client (en octets)", + "min-buffer-memory-message" : "Seule une taille minimale de 0 est autorisée.", + "memory-buffer-size-range" : "La taille du tampon doit être comprise entre 0 et {{max}} Ko", + "acks" : "Nombre d'accusés de réception", + "topic-arn-pattern" : "Motif ARN de sujet", + "topic-arn-pattern-required" : "Le motif ARN de sujet est requis", + "aws-access-key-id" : "ID de clé d'accès AWS", + "aws-access-key-id-required" : "L'ID de clé d'accès AWS est requis", + "aws-secret-access-key" : "Clé d'accès secrète AWS", + "aws-secret-access-key-required" : "La clé d'accès secrète AWS est requise", + "aws-region" : "Région AWS", + "aws-region-required" : "La région AWS est requise", + "exchange-name-pattern" : "Motif de nom d'échange", + "routing-key-pattern" : "Motif de clé de routage", + "message-properties" : "Propriétés du message", + "host" : "Hôte", + "host-required" : "L’hôte est requis", + "port" : "Port", + "port-required" : "Le port est requis", + "port-range" : "Le port doit être compris entre 1 et 65535.", + "virtual-host" : "Hôte virtuel", + "username" : "Nom d'utilisateur", + "password" : "Mot de passe", + "automatic-recovery" : "Récupération automatique", + "connection-timeout-ms" : "Délai de connexion (ms)", + "min-connection-timeout-ms-message" : "Seule une valeur minimale de 0 ms est autorisée.", + "handshake-timeout-ms" : "Délai de poignée de main (ms)", + "min-handshake-timeout-ms-message" : "Seule une valeur minimale de 0 ms est autorisée.", + "client-properties" : "Propriétés du client", + "queue-url-pattern" : "Motif d'URL de file d'attente", + "queue-url-pattern-required" : "Le motif d'URL de file d'attente est requis", + "delay-seconds" : "Délai (secondes)", + "min-delay-seconds-message" : "Seule une valeur minimale de 0 s est autorisée.", + "max-delay-seconds-message" : "La valeur maximale autorisée est de 900 secondes.", + "name" : "Nom", + "name-required" : "Le nom est requis", + "queue-type" : "Type de file d’attente", + "sqs-queue-standard" : "Standard", + "sqs-queue-fifo" : "FIFO", + "gcp-project-id" : "ID de projet GCP", + "gcp-project-id-required" : "L’ID de projet GCP est requis", + "gcp-service-account-key" : "Clé de compte de service GCP", + "gcp-service-account-key-required" : "La clé de compte de service GCP est requise", + "pubsub-topic-name" : "Nom du sujet", + "pubsub-topic-name-required" : "Le nom du sujet est requis", + "message-attributes" : "Attributs du message", + "message-attributes-hint" : "Utilisez ${metadataKey} pour une valeur depuis les métadonnées, $[messageKey] depuis le message", + "connect-timeout" : "Délai de connexion (s)", + "connect-timeout-required" : "Le délai de connexion est requis.", + "connect-timeout-range" : "Le délai de connexion doit être compris entre 1 et 200.", + "client-id" : "ID client", + "client-id-hint" : "Optionnel. Laissez vide pour un ID client généré automatiquement. Si vous utilisez un ID explicite, veillez à ce qu'il soit unique, surtout en mode micro-services.", + "append-client-id-suffix" : "Ajouter l'ID du service comme suffixe à l'ID client", + "client-id-suffix-hint" : "Optionnel. Appliqué si un ID client est défini. Ajoute l'ID de service comme suffixe pour éviter les conflits en mode micro-services.", + "device-id" : "ID de l'appareil", + "device-id-required" : "L'ID de l'appareil est requis.", + "clean-session" : "Session propre", + "enable-ssl" : "Activer SSL", + "credentials" : "Identifiants", + "credentials-type" : "Type d'identifiants", + "credentials-type-required" : "Le type d'identifiants est requis.", + "credentials-anonymous" : "Anonyme", + "credentials-basic" : "Basique", + "credentials-pem" : "PEM", + "credentials-pem-hint" : "Au moins un fichier de certificat CA serveur ou une paire certificat client + clé privée client est requis", + "credentials-sas" : "Signature d'accès partagé", + "sas-key" : "Clé SAS", + "sas-key-required" : "La clé SAS est requise.", + "hostname" : "Nom d'hôte", + "hostname-required" : "Le nom d'hôte est requis.", + "azure-ca-cert" : "Fichier de certificat CA", + "username-required" : "Le nom d'utilisateur est requis.", + "password-required" : "Le mot de passe est requis.", + "ca-cert" : "Fichier certificat CA du serveur", + "private-key" : "Fichier de clé privée du client", + "cert" : "Fichier de certificat client", + "no-file" : "Aucun fichier sélectionné.", + "drop-file" : "Déposez un fichier ou cliquez pour en sélectionner un à téléverser.", + "private-key-password" : "Mot de passe de la clé privée", + "use-system-smtp-settings" : "Utiliser les paramètres SMTP système", + "use-metadata-dynamic-interval" : "Utiliser un intervalle dynamique", + "metadata-dynamic-interval-hint" : "Les champs de début et fin d’intervalle supportent la templatization. Valeurs en millisecondes. Utilisez $[messageKey] ou ${metadataKey}.", + "use-metadata-interval-patterns-hint" : "Si sélectionné, le nœud utilise les motifs d’intervalle de début/fin du message ou des métadonnées en millisecondes.", + "use-message-alarm-data" : "Utiliser les données d'alarme du message", + "overwrite-alarm-details" : "Écraser les détails de l'alarme", + "use-alarm-severity-pattern" : "Utiliser un motif de sévérité d'alarme", + "check-all-keys" : "Vérifier que tous les champs sont présents", + "check-all-keys-hint" : "Vérifie la présence de toutes les clés spécifiées dans les données et les métadonnées.", + "check-relation-to-specific-entity" : "Vérifier la relation avec une entité spécifique", + "check-relation-to-specific-entity-tooltip" : "Si activé, vérifie la relation avec une entité spécifique, sinon avec n'importe quelle entité selon direction/type.", + "check-relation-hint" : "Vérifie la relation avec une entité (spécifique ou non) selon direction/type.", + "delete-relation-with-specific-entity" : "Supprimer la relation avec une entité spécifique", + "delete-relation-with-specific-entity-hint" : "Supprime uniquement la relation avec une entité spécifique. Sinon, supprime toutes les relations correspondantes.", + "delete-relation-hint" : "Supprime la relation de l'origine du message vers une ou plusieurs entités selon direction/type.", + "remove-current-relations" : "Supprimer les relations actuelles", + "remove-current-relations-hint" : "Supprime les relations actuelles de l'origine du message selon direction/type.", + "change-originator-to-related-entity" : "Changer l'origine en une entité liée", + "change-originator-to-related-entity-hint" : "Permet de traiter un message comme venant d'une autre entité.", + "start-interval" : "Début de l'intervalle", + "end-interval" : "Fin de l'intervalle", + "start-interval-required" : "Le début de l'intervalle est requis.", + "end-interval-required" : "La fin de l'intervalle est requise.", + "smtp-protocol" : "Protocole", + "smtp-host" : "Hôte SMTP", + "smtp-host-required" : "L’hôte SMTP est requis.", + "smtp-port" : "Port SMTP", + "smtp-port-required" : "Le port SMTP est requis.", + "smtp-port-range" : "Le port SMTP doit être compris entre 1 et 65535.", + "timeout-msec" : "Délai d'attente (ms)", + "min-timeout-msec-message" : "Seule une valeur minimale de 0 ms est autorisée.", + "enter-username" : "Saisir le nom d'utilisateur", + "enter-password" : "Saisir le mot de passe", + "enable-tls" : "Activer TLS", + "tls-version" : "Version TLS", + "enable-proxy" : "Activer le proxy", + "use-system-proxy-properties" : "Utiliser les paramètres proxy système", + "proxy-host" : "Hôte proxy", + "proxy-host-required" : "L’hôte proxy est requis.", + "proxy-port" : "Port proxy", + "proxy-port-required" : "Le port proxy est requis.", + "proxy-port-range" : "Le port proxy doit être entre 1 et 65535.", + "proxy-user" : "Utilisateur proxy", + "proxy-password" : "Mot de passe proxy", + "proxy-scheme" : "Schéma de proxy", + "numbers-to-template" : "Numéros de téléphone vers modèle", + "numbers-to-template-required" : "Le modèle de numéros de téléphone est requis", + "numbers-to-template-hint" : "Liste de numéros séparés par des virgules. Utilisez ${metadataKey} ou $[messageKey].", + "sms-message-template" : "Modèle de message SMS", + "sms-message-template-required" : "Le modèle de message SMS est requis", + "use-system-sms-settings" : "Utiliser les paramètres SMS système", + "min-period-0-seconds-message" : "Seule une valeur minimale de 0 seconde est autorisée.", + "max-pending-messages" : "Nombre maximal de messages en attente", + "max-pending-messages-required" : "Le nombre maximal de messages en attente est requis.", + "max-pending-messages-range" : "La valeur doit être comprise entre 1 et 100000.", + "originator-types-filter" : "Filtre de types d’origine", + "interval-seconds" : "Intervalle en secondes", + "interval-seconds-required" : "L'intervalle est requis.", + "int-range" : "La valeur ne doit pas dépasser la limite entière maximale (2147483648)", + "min-interval-seconds-message" : "Seule une valeur minimale de 1 seconde est autorisée.", + "output-timeseries-key-prefix" : "Préfixe de clé des séries temporelles de sortie", + "output-timeseries-key-prefix-required" : "Le préfixe de clé de sortie est requis.", + "separator-hint" : "Appuyez sur \"Entrée\" pour valider la saisie.", + "select-details" : "Sélectionner les détails", + "entity-details-id" : "Id", + "entity-details-title" : "Titre", + "entity-details-country" : "Pays", + "entity-details-state" : "État", + "entity-details-city" : "Ville", + "entity-details-zip" : "Code postal", + "entity-details-address" : "Adresse", + "entity-details-address2" : "Adresse 2", + "entity-details-additional_info" : "Informations supplémentaires", + "entity-details-phone" : "Téléphone", + "entity-details-email" : "Email", + "email-sender" : "Expéditeur de l'email", + "fields-to-check" : "Champs à vérifier", + "add-detail" : "Ajouter un détail", + "check-all-keys-tooltip" : "Si activé, vérifie la présence de tous les champs listés dans les données et métadonnées du message entrant.", + "fields-to-check-hint" : "Appuyez sur \"Entrée\" pour valider la saisie. Champs multiples supportés.", + "entity-details-list-empty" : "Au moins un détail doit être sélectionné.", + "alarm-status" : "Statut d'alarme", + "alarm-required" : "Au moins un statut d'alarme doit être sélectionné.", + "no-entity-details-matching" : "Aucun détail d'entité correspondant trouvé.", + "custom-table-name" : "Nom de table personnalisé", + "custom-table-name-required" : "Le nom de la table est requis", + "custom-table-hint" : "La table doit être créée dans votre cluster Cassandra et commencer par 'cs_tb_'. Entrez ici le nom sans le préfixe.", + "message-field" : "Champ du message", + "message-field-required" : "Le champ du message est requis.", + "table-col" : "Colonne de la table", + "table-col-required" : "La colonne de la table est requise.", + "latitude-field-name" : "Nom du champ de latitude", + "longitude-field-name" : "Nom du champ de longitude", + "latitude-field-name-required" : "Le nom du champ de latitude est requis.", + "longitude-field-name-required" : "Le nom du champ de longitude est requis.", + "fetch-perimeter-info-from-metadata" : "Extraire les infos de périmètre depuis les métadonnées", + "fetch-perimeter-info-from-metadata-tooltip" : "Si le type de périmètre est 'Polygone', la valeur du champ '{{perimeterKeyName}}' est utilisée telle quelle. Pour 'Cercle', elle est analysée pour extraire latitude, longitude, rayon et unité.", + "perimeter-key-name" : "Nom de clé de périmètre", + "perimeter-key-name-hint" : "Nom du champ de métadonnées contenant les infos de périmètre.", + "perimeter-key-name-required" : "Le nom de la clé de périmètre est requis.", + "perimeter-circle" : "Cercle", + "perimeter-polygon" : "Polygone", + "perimeter-type" : "Type de périmètre", + "circle-center-latitude" : "Latitude du centre", + "circle-center-latitude-required" : "Latitude du centre requise.", + "circle-center-longitude" : "Longitude du centre", + "circle-center-longitude-required" : "Longitude du centre requise.", + "range-unit-meter" : "Mètre", + "range-unit-kilometer" : "Kilomètre", + "range-unit-foot" : "Pied", + "range-unit-mile" : "Mille", + "range-unit-nautical-mile" : "Mille marin", + "range-units" : "Unités de distance", + "range-units-required" : "Les unités de distance sont requises.", + "range" : "Portée", + "range-required" : "La portée est requise.", + "polygon-definition" : "Définition du polygone", + "polygon-definition-required" : "La définition du polygone est requise.", + "polygon-definition-hint" : "Format : [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]]", + "min-inside-duration" : "Durée minimale à l’intérieur", + "min-inside-duration-value-required" : "Durée minimale à l’intérieur requise", + "min-inside-duration-time-unit" : "Unité de temps de la durée intérieure", + "min-outside-duration" : "Durée minimale à l’extérieur", + "min-outside-duration-value-required" : "Durée minimale à l’extérieur requise", + "min-outside-duration-time-unit" : "Unité de temps de la durée extérieure", + "tell-failure-if-absent" : "Indiquer un échec", + "tell-failure-if-absent-hint" : "Si au moins une clé sélectionnée est absente, le message sortant signalera une \"Échec\".", + "get-latest-value-with-ts" : "Récupérer l’horodatage des dernières valeurs", + "get-latest-value-with-ts-hint" : "Inclura l'horodatage dans les valeurs télémétriques, ex : \"temp\": {\"ts\":1574329385897, \"value\":42}", + "ignore-null-strings" : "Ignorer les chaînes nulles", + "ignore-null-strings-hint" : "Ignore les champs avec valeur vide.", + "add-metadata-key-values-as-kafka-headers" : "Ajouter les métadonnées comme en-têtes Kafka", + "add-metadata-key-values-as-kafka-headers-hint" : "Les métadonnées seront ajoutées comme en-têtes en tant que tableau d'octets avec un encodage prédéfini.", + "charset-encoding" : "Encodage de caractères", + "charset-encoding-required" : "L'encodage de caractères est requis.", + "charset-us-ascii" : "US-ASCII", + "charset-iso-8859-1" : "ISO-8859-1", + "charset-utf-8" : "UTF-8", + "charset-utf-16be" : "UTF-16BE", + "charset-utf-16le" : "UTF-16LE", + "charset-utf-16" : "UTF-16", + "select-queue-hint" : "Sélectionnez un nom de file ou entrez un nom personnalisé.", + "device-profile-node-hint" : "Utile pour conserver la continuité des alarmes en cas de redémarrage.", + "persist-alarm-rules" : "Persister l'état des règles d'alarme", + "persist-alarm-rules-hint" : "Stocke l'état des règles dans la base de données.", + "fetch-alarm-rules" : "Récupérer l'état des règles d'alarme", + "fetch-alarm-rules-hint" : "Restaure l’état à l’initialisation pour gérer les alarmes même après redémarrage.", + "input-value-key" : "Clé de valeur d'entrée", + "input-value-key-required" : "La clé de valeur d'entrée est requise.", + "output-value-key" : "Clé de valeur de sortie", + "output-value-key-required" : "La clé de valeur de sortie est requise.", + "number-of-digits-after-floating-point" : "Nombre de chiffres après la virgule", + "number-of-digits-after-floating-point-range" : "Doit être compris entre 0 et 15.", + "failure-if-delta-negative" : "Signaler un échec si le delta est négatif", + "failure-if-delta-negative-tooltip" : "Force l’échec du traitement si la valeur delta est négative.", + "use-caching" : "Utiliser le cache", + "use-caching-tooltip" : "Met en cache la valeur de \"{{inputValueKey}}\" depuis le message entrant pour améliorer la performance. Le cache ne sera pas mis à jour si la valeur est modifiée ailleurs.", + "add-time-difference-between-readings" : "Ajouter la différence de temps entre les lectures de \"{{inputValueKey}}\"", + "add-time-difference-between-readings-tooltip" : "Ajoute la clé \"{{periodValueKey}}\" au message sortant si activé.", + "period-value-key" : "Clé de la période", + "period-value-key-required" : "La clé de la période est requise.", + "general-pattern-hint" : "Utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message.", + "alarm-severity-pattern-hint" : "Utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message. Les niveaux doivent être CRITICAL, MAJOR, etc.", + "output-node-name-hint" : "Le nom du nœud correspond au type de relation du message de sortie.", + "use-server-ts" : "Utiliser l’horodatage serveur", + "use-server-ts-hint" : "Utilise le temps actuel du serveur pour les séries temporelles sans horodatage explicite.", + "kv-map-pattern-hint" : "Tous les champs d’entrée supportent la templatization.", + "kv-map-single-pattern-hint" : "Le champ d’entrée supporte la templatization.", + "shared-scope" : "Portée partagée", + "server-scope" : "Portée serveur", + "client-scope" : "Portée client", + "attribute-type" : "Attribut", + "attribute-type-description" : "Récupère l’attribut depuis la base de données", + "attribute-type-result-description" : "Stocke le résultat comme attribut", + "constant-type" : "Constante", + "constant-type-description" : "Définit une valeur constante", + "time-series-type" : "Série temporelle", + "time-series-type-description" : "Récupère la dernière valeur de série temporelle", + "time-series-type-result-description" : "Stocke le résultat comme série temporelle", + "message-body-type" : "Message", + "message-body-type-description" : "Valeur depuis le corps du message entrant", + "message-body-type-result-description" : "Ajoute le résultat au corps du message sortant", + "message-metadata-type" : "Métadonnée", + "message-metadata-type-description" : "Valeur depuis les métadonnées du message entrant", + "message-metadata-result-description" : "Ajoute le résultat aux métadonnées sortantes", + "argument-tile" : "Arguments", + "no-arguments-prompt" : "Aucun argument configuré", + "result-title" : "Résultat", + "functions-field-input" : "Fonctions", + "no-option-found" : "Aucune option trouvée", + "argument-source-field-input" : "Source", + "argument-source-field-input-required" : "La source de l'argument est requise.", + "argument-key-field-input" : "Clé", + "argument-key-field-input-required" : "La clé de l'argument est requise.", + "constant-value-field-input" : "Valeur constante", + "constant-value-field-input-required" : "La valeur constante est requise.", + "attribute-scope-field-input" : "Portée de l'attribut", + "attribute-scope-field-input-required" : "La portée est requise.", + "default-value-field-input" : "Valeur par défaut", + "type-field-input" : "Type", + "type-field-input-required" : "Le type est requis.", + "key-field-input" : "Clé", + "add-entity-type" : "Ajouter un type d'entité", + "add-device-profile" : "Ajouter un profil de périphérique", + "key-field-input-required" : "La clé est requise.", + "number-floating-point-field-input" : "Nombre de chiffres après la virgule", + "number-floating-point-field-input-hint" : "Utilisez 0 pour convertir en entier", + "add-to-message-field-input" : "Ajouter au message", + "add-to-metadata-field-input" : "Ajouter aux métadonnées", + "custom-expression-field-input" : "Expression mathématique", + "custom-expression-field-input-required" : "L’expression mathématique est requise", + "custom-expression-field-input-hint" : "Spécifiez une expression mathématique à évaluer.", + "retained-message" : "Retenu", + "attributes-mapping" : "Correspondance des attributs", + "latest-telemetry-mapping" : "Correspondance des dernières télémétries", + "add-mapped-attribute-to" : "Ajouter les attributs mappés à", + "add-mapped-latest-telemetry-to" : "Ajouter la télémétrie mappée à", + "add-mapped-fields-to" : "Ajouter les champs mappés à", + "add-selected-details-to" : "Ajouter les détails sélectionnés à", + "clear-selected-types" : "Effacer les types sélectionnés", + "clear-selected-details" : "Effacer les détails sélectionnés", + "clear-selected-fields" : "Effacer les champs sélectionnés", + "clear-selected-keys" : "Effacer les clés sélectionnées", + "geofence-configuration" : "Configuration de la géofence", + "coordinate-field-names" : "Noms des champs de coordonnées", + "coordinate-field-hint" : "Le nœud tente de récupérer ces champs depuis le message, sinon depuis les métadonnées.", + "presence-monitoring-strategy" : "Stratégie de suivi de présence", + "presence-monitoring-strategy-on-first-message" : "Au premier message", + "presence-monitoring-strategy-on-each-message" : "À chaque message", + "presence-monitoring-strategy-on-first-message-hint" : "Signale 'Inside' ou 'Outside' au premier message après une durée minimale depuis la dernière mise à jour.", + "presence-monitoring-strategy-on-each-message-hint" : "Signale 'Inside' ou 'Outside' à chaque message après une mise à jour.", + "fetch-credentials-to" : "Extraire les identifiants vers", + "add-originator-attributes-to" : "Ajouter les attributs de l’origine à", + "originator-attributes" : "Attributs de l’origine", + "fetch-latest-telemetry-with-timestamp" : "Extraire la dernière télémétrie avec horodatage", + "fetch-latest-telemetry-with-timestamp-tooltip" : "Ajoute l'horodatage dans les métadonnées, ex: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure" : "Signaler un échec si un attribut est manquant", + "tell-failure-tooltip" : "Si une des clés n’existe pas, le message signalera un échec.", + "created-time" : "Date de création", + "chip-help" : "Appuyez sur 'Entrée' pour valider {{inputName}}. \nAppuyez sur 'Retour arrière' pour supprimer. \nValeurs multiples supportées.", + "detail" : "détail", + "field-name" : "Nom du champ", + "device-profile" : "Profil de périphérique", + "entity-type" : "Type d'entité", + "message-type" : "Type de message", + "timeseries-key" : "Clé de série temporelle", + "type" : "Type", + "first-name" : "Prénom", + "last-name" : "Nom de famille", + "label" : "Étiquette", + "originator-fields-mapping" : "Mappage des champs de l’origine", + "add-mapped-originator-fields-to" : "Ajouter les champs de l’origine mappés à", + "fields" : "Champs", + "skip-empty-fields" : "Ignorer les champs vides", + "skip-empty-fields-tooltip" : "Les champs vides ne seront pas ajoutés au message de sortie ou aux métadonnées de sortie.", + "fetch-interval" : "Intervalle de récupération", + "fetch-strategy" : "Stratégie de récupération", + "fetch-timeseries-from-to" : "Récupérer les séries temporelles de {{startInterval}} {{startIntervalTimeUnit}} à {{endInterval}} {{endIntervalTimeUnit}}.", + "fetch-timeseries-from-to-invalid" : "Récupération des séries invalide (« Début de l'intervalle » doit être inférieur à « Fin de l'intervalle »).", + "use-metadata-dynamic-interval-tooltip" : "Utilise un intervalle dynamique basé sur le message et les métadonnées si activé.", + "all-mode-hint" : "En mode « Tout », récupère la télémétrie selon l'intervalle et les paramètres définis.", + "first-mode-hint" : "Récupère la télémétrie la plus proche du début de l'intervalle.", + "last-mode-hint" : "Récupère la télémétrie la plus proche de la fin de l'intervalle.", + "ascending" : "Croissant", + "descending" : "Décroissant", + "min" : "Minimum", + "max" : "Maximum", + "average" : "Moyenne", + "sum" : "Somme", + "count" : "Nombre", + "none" : "Aucun", + "last-level-relation-tooltip" : "Recherche des entités liées uniquement au niveau de relation maximum défini si activé.", + "last-level-device-relation-tooltip" : "Recherche des périphériques liés uniquement au niveau spécifié si activé.", + "data-to-fetch" : "Données à récupérer", + "mapping-of-customers" : "Mappage des clients", + "map-fields-required" : "Tous les champs de mappage sont requis.", + "attributes" : "Attributs", + "related-device-attributes" : "Attributs du périphérique lié", + "add-selected-attributes-to" : "Ajouter les attributs sélectionnés à", + "device-profiles" : "Profils de périphérique", + "mapping-of-tenant" : "Mappage du locataire", + "add-attribute-key" : "Ajouter une clé d’attribut", + "message-template" : "Modèle de message", + "message-template-required" : "Le modèle de message est requis", + "use-system-slack-settings" : "Utiliser les paramètres Slack système", + "slack-api-token" : "Jeton API Slack", + "slack-api-token-required" : "Le jeton API Slack est requis", + "keys-mapping" : "Mappage des clés", + "add-key" : "Ajouter une clé", + "recipients" : "Destinataires", + "message-subject-and-content" : "Objet et contenu du message", + "template-rules-hint" : "Les champs supportent la templatization avec $[messageKey] ou ${metadataKey}.", + "originator-customer-desc" : "Utilise le client de l’origine du message comme nouvelle origine.", + "originator-tenant-desc" : "Utilise le locataire actuel comme origine.", + "originator-related-entity-desc" : "Utilise une entité liée comme origine, selon le type et la direction de relation.", + "originator-alarm-originator-desc" : "Utilise l’origine de l’alarme si le message provient d’une entité d’alarme.", + "originator-entity-by-name-pattern-desc" : "Utilise une entité récupérée par type et modèle de nom.", + "email-from-template-hint" : "Utilisez $[messageKey] ou ${metadataKey} pour remplir dynamiquement.", + "recipients-block-main-hint" : "Liste d’adresses séparées par des virgules. Supporte la templatization.", + "forward-msg-default-rule-chain" : "Transférer au rule chain par défaut de l’origine", + "forward-msg-default-rule-chain-tooltip" : "Transfère au rule chain par défaut ou configuré si aucun n’est défini dans le profil.", + "exclude-zero-deltas" : "Exclure les deltas nuls du message sortant", + "exclude-zero-deltas-hint" : "Ajoute la clé {{outputValueKey}} uniquement si sa valeur est différente de zéro.", + "exclude-zero-deltas-time-difference-hint" : "Ajoute les clés {{outputValueKey}} et {{periodValueKey}} uniquement si {{outputValueKey}} ≠ 0.", + "search-direction-from" : "De l’origine vers l’entité cible", + "search-direction-to" : "De l’entité cible vers l’origine", + "del-relation-direction-from" : "Depuis l’origine", + "del-relation-direction-to" : "Vers l’origine", + "target-entity" : "Entité cible", + "function-configuration" : "Configuration de la fonction", + "function-name" : "Nom de la fonction", + "function-name-required" : "Le nom de la fonction est requis.", + "qualifier" : "Qualificateur", + "qualifier-hint" : "Par défaut, \"$LATEST\" sera utilisé si vide.", + "aws-credentials" : "Identifiants AWS", + "connection-timeout" : "Délai de connexion", + "connection-timeout-required" : "Le délai de connexion est requis.", + "connection-timeout-min" : "Valeur minimale : 0", + "connection-timeout-hint" : "Temps d’attente (en sec) pour établir la connexion. 0 = infini (non recommandé).", + "request-timeout" : "Délai de requête", + "request-timeout-required" : "Le délai de requête est requis", + "request-timeout-min" : "Valeur minimale : 0", + "request-timeout-hint" : "Temps d’attente (en sec) pour exécution de la requête. 0 = infini (non recommandé).", + "units" : "Unités", + "tell-failure-aws-lambda" : "Signaler un échec si l’exécution AWS Lambda échoue", + "tell-failure-aws-lambda-hint" : "Déclenche un échec du traitement si AWS Lambda lève une exception.", + "basic-mode" : "Mode basique", + "advanced-mode" : "Mode avancé", + "save-time-series" : { + "processing-settings" : "Paramètres de traitement", + "processing-settings-hint" : "Définissez comment les messages entrants sont traités. Les paramètres de base permettent de sélectionner des stratégies préconfigurées, tandis que les paramètres avancés permettent de choisir des stratégies individuelles pour chaque action.", + "advanced-settings-hint" : "Soyez prudent lors de la configuration des stratégies de traitement. Certaines combinaisons peuvent entraîner un comportement inattendu.", + "strategy" : "Stratégie", + "deduplication-interval" : "Intervalle de déduplication", + "deduplication-interval-required" : "L'intervalle de déduplication est requis", + "deduplication-interval-min-max-range" : "L'intervalle de déduplication doit être d'au moins 1 seconde et d'au plus 1 jour", + "strategy-type" : { + "every-message" : "À chaque message", + "skip" : "Ignorer", + "deduplicate" : "Dédupliquer", + "web-sockets-only" : "WebSockets uniquement" + }, + "time-series" : "Séries temporelles", + "latest" : "Dernières valeurs", + "web-sockets" : "WebSockets", + "calculated-fields" : "Champs calculés" + }, + "save-attribute" : { + "processing-settings" : "Paramètres de traitement", + "processing-settings-hint" : "Définissez comment les messages entrants sont traités. Les paramètres de base permettent de sélectionner des stratégies préconfigurées, tandis que les paramètres avancés permettent de choisir des stratégies individuelles pour chaque action.", + "advanced-settings-hint" : "Soyez prudent lors de la configuration des stratégies de traitement. Certaines combinaisons peuvent entraîner un comportement inattendu.", + "strategy" : "Stratégie", + "deduplication-interval" : "Intervalle de déduplication", + "deduplication-interval-required" : "L'intervalle de déduplication est requis", + "deduplication-interval-min-max-range" : "L'intervalle de déduplication doit être d'au moins 1 seconde et d'au plus 1 jour", + "scope" : "Portée", + "strategy-type" : { + "every-message" : "À chaque message", + "skip" : "Ignorer", + "deduplicate" : "Dédupliquer", + "web-sockets-only" : "WebSockets uniquement" + }, + "attributes" : "Attributs" + }, + "key-val" : { + "key" : "Clé", + "value" : "Valeur", + "see-examples" : "Voir des exemples.", + "remove-entry" : "Supprimer l'entrée", + "remove-mapping-entry" : "Supprimer le mappage", + "add-mapping-entry" : "Ajouter un mappage", + "add-entry" : "Ajouter une entrée", + "copy-key-values-from" : "Copier les paires clé-valeur depuis", + "delete-key-values" : "Supprimer les paires clé-valeur", + "delete-key-values-from" : "Supprimer les paires clé-valeur depuis", + "at-least-one-key-error" : "Au moins une clé doit être sélectionnée.", + "unique-key-value-pair-error" : "'{{keyText}}' doit être différent de '{{valText}}' !" + }, + "mail-body-types" : { + "plain-text" : "Texte brut", + "html" : "HTML", + "dynamic" : "Dynamique", + "use-body-type-template" : "Utiliser le modèle de type de corps", + "plain-text-description" : "Texte simple, non formaté, sans style ou mise en forme particulière.", + "html-text-description" : "Permet d'utiliser des balises HTML pour la mise en forme, les liens et les images dans le corps du message.", + "dynamic-text-description" : "Permet d'utiliser dynamiquement le texte brut ou HTML selon le modèle.", + "after-template-evaluation-hint" : "Après évaluation du modèle, la valeur doit être true pour HTML, et false pour Texte brut." + } + }, + "timezone" : { + "timezone" : "Fuseau horaire", + "select-timezone" : "Sélectionner un fuseau horaire", + "no-timezones-matching" : "Aucun fuseau horaire correspondant à '{{timezone}}' trouvé.", + "timezone-required" : "Le fuseau horaire est requis.", + "browser-time" : "Heure du navigateur" + }, + "queue" : { + "queue-name" : "File d’attente", + "no-queues-found" : "Aucune file d’attente trouvée.", + "no-queues-matching" : "Aucune file d’attente correspondant à '{{queue}}' trouvée.", + "select-name" : "Sélectionner le nom de la file d’attente", + "name" : "Nom", + "name-required" : "Le nom de la file d’attente est requis !", + "name-unique" : "Le nom de la file d’attente n’est pas unique !", + "name-pattern" : "Le nom de la file d’attente contient un caractère non autorisé (seuls les caractères ASCII alphanumériques, '.', '_' et '-' sont permis) !", + "queue-required" : "La file d’attente est requise !", + "topic-required" : "Le sujet de la file d’attente est requis !", + "poll-interval-required" : "L’intervalle d’interrogation est requis !", + "poll-interval-min-value" : "L’intervalle d’interrogation ne peut pas être inférieur à 1", + "partitions-required" : "Les partitions sont requises !", + "partitions-min-value" : "La valeur des partitions ne peut pas être inférieure à 1", + "pack-processing-timeout-required" : "Le délai de traitement est requis", + "pack-processing-timeout-min-value" : "Le délai de traitement ne peut pas être inférieur à 1", + "batch-size-required" : "La taille du lot est requise !", + "batch-size-min-value" : "La taille du lot ne peut pas être inférieure à 1", + "retries-required" : "Le nombre de tentatives est requis !", + "retries-min-value" : "Le nombre de tentatives ne peut pas être négatif", + "failure-percentage-required" : "Le pourcentage d’échec est requis !", + "failure-percentage-min-value" : "Le pourcentage d’échec ne peut pas être inférieur à 0", + "failure-percentage-max-value" : "Le pourcentage d’échec ne peut pas être supérieur à 100", + "pause-between-retries-required" : "Le délai entre les tentatives est requis !", + "pause-between-retries-min-value" : "Le délai entre les tentatives ne peut pas être inférieur à 1", + "max-pause-between-retries-required" : "Le délai maximal entre les tentatives est requis !", + "max-pause-between-retries-min-value" : "Le délai maximal entre les tentatives ne peut pas être inférieur à 1", + "submit-strategy-type-required" : "Le type de stratégie d’envoi est requis !", + "processing-strategy-type-required" : "Le type de stratégie de traitement est requis !", + "queues" : "Files d’attente", + "selected-queues" : "{ count, plural, =1 {1 file d’attente} other {# files d’attente} } sélectionnée(s)", + "delete-queue-title" : "Êtes-vous sûr de vouloir supprimer la file d’attente '{{queueName}}' ?", + "delete-queues-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 file d’attente} other {# files d’attente} } ?", + "delete-queue-text" : "Attention, après confirmation, la file d’attente et toutes les données associées seront irrécupérables.", + "delete-queues-text" : "Après confirmation, toutes les files d’attente sélectionnées seront supprimées et ne seront plus accessibles.", + "search" : "Rechercher une file d’attente", + "add" : "Ajouter une file d’attente", + "details" : "Détails de la file d’attente", + "topic" : "Sujet", + "submit-settings" : "Paramètres d’envoi", + "submit-strategy" : "Type de stratégie *", + "grouping-parameter" : "Paramètre de regroupement", + "processing-settings" : "Paramètres de traitement des tentatives", + "processing-strategy" : "Type de traitement *", + "retries-settings" : "Paramètres de tentatives", + "polling-settings" : "Paramètres d’interrogation", + "batch-processing" : "Traitement par lots", + "poll-interval" : "Intervalle d’interrogation", + "partitions" : "Partitions", + "immediate-processing" : "Traitement immédiat", + "consumer-per-partition" : "Interroger pour chaque consommateur", + "consumer-per-partition-hint" : "Activer un consommateur distinct pour chaque partition", + "duplicate-msg-to-all-partitions" : "Dupliquer le message vers toutes les partitions", + "processing-timeout" : "Délai de traitement, ms", + "batch-size" : "Taille du lot", + "retries" : "Nombre de tentatives (0 – illimité)", + "failure-percentage" : "Échecs pour ignorer les tentatives, %", + "pause-between-retries" : "Réessayer dans, sec", + "max-pause-between-retries" : "Réessayer à nouveau dans, sec", + "delete" : "Supprimer la file d’attente", + "copyId" : "Copier l’ID de la file d’attente", + "idCopiedMessage" : "L’ID de la file d’attente a été copié dans le presse-papiers", + "description" : "Description", + "description-hint" : "Ce texte sera affiché dans la description de la file d’attente à la place de la stratégie sélectionnée", + "alt-description" : "Stratégie d’envoi : {{submitStrategy}}, Stratégie de traitement : {{processingStrategy}}", + "custom-properties" : "Propriétés personnalisées", + "custom-properties-hint" : "Propriétés personnalisées de création de file (topic), ex : 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies" : { + "sequential-by-originator-label" : "Séquentiel par initiateur", + "sequential-by-originator-hint" : "Un nouveau message, par exemple pour l'appareil A, n’est pas soumis tant que le message précédent pour l'appareil A n’a pas été accusé de réception", + "sequential-by-tenant-label" : "Séquentiel par locataire", + "sequential-by-tenant-hint" : "Un nouveau message, par exemple pour le locataire A, n’est pas soumis tant que le message précédent pour le locataire A n’a pas été accusé de réception", + "sequential-label" : "Séquentiel", + "sequential-hint" : "Un nouveau message n’est pas soumis tant que le précédent n’a pas été accusé de réception", + "burst-label" : "Rafale", + "burst-hint" : "Tous les messages sont soumis aux chaînes de règles dans l’ordre de leur arrivée", + "batch-label" : "Par lot", + "batch-hint" : "Un nouveau lot n’est pas soumis tant que le lot précédent n’a pas été accusé de réception", + "skip-all-failures-label" : "Ignorer tous les échecs", + "skip-all-failures-hint" : "Ignorer tous les échecs", + "skip-all-failures-and-timeouts-label" : "Ignorer tous les échecs et expirations", + "skip-all-failures-and-timeouts-hint" : "Ignorer tous les échecs et expirations de délai", + "retry-all-label" : "Réessayer tous", + "retry-all-hint" : "Réessayer tous les messages du lot de traitement", + "retry-failed-label" : "Réessayer les échecs", + "retry-failed-hint" : "Réessayer tous les messages échoués du lot de traitement", + "retry-timeout-label" : "Réessayer les expirations", + "retry-timeout-hint" : "Réessayer tous les messages ayant expiré dans le lot de traitement", + "retry-failed-and-timeout-label" : "Réessayer les échecs et expirations", + "retry-failed-and-timeout-hint" : "Réessayer tous les messages échoués et ayant expiré dans le lot de traitement" + } + }, + "queue-statistics" : { + "queue-statistics" : "Statistiques de file d'attente", + "no-queue-statistics-matching" : "Aucune statistique de file d'attente correspondant à '{{entity}}' n'a été trouvée.", + "queue-statistics-required" : "Les statistiques de file d'attente sont requises.", + "list-of-queue-statistics" : "{ count, plural, =1 {Une statistique de file d'attente} other {Liste de # statistiques de file d'attente} }", + "selected-queue-statistics" : "{ count, plural, =1 {1 statistique de file d'attente} other {# statistiques de file d'attente} } sélectionnée(s)", + "no-queue-statistics-text" : "Aucune statistique de file d'attente trouvée", + "queue-statistics-starts-with" : "Statistiques de file d'attente dont les noms commencent par '{{prefix}}'" + }, + "server-error" : { + "general" : "Erreur générale du serveur", + "authentication" : "Erreur d'authentification", + "jwt-token-expired" : "Jeton JWT expiré", + "tenant-trial-expired" : "Période d'essai du locataire expirée", + "credentials-expired" : "Identifiants expirés", + "permission-denied" : "Permission refusée", + "invalid-arguments" : "Arguments invalides", + "bad-request-params" : "Paramètres de requête incorrects", + "item-not-found" : "Élément introuvable", + "too-many-requests" : "Trop de requêtes", + "too-many-updates" : "Trop de mises à jour" + }, + "tenant" : { + "tenant" : "Locataire", + "tenants" : "Locataires", + "management" : "Gestion des locataires", + "add" : "Ajouter un locataire", + "admins" : "Administrateurs", + "manage-tenant-admins" : "Gérer les administrateurs du locataire", + "delete" : "Supprimer le locataire", + "add-tenant-text" : "Ajouter un nouveau locataire", + "no-tenants-text" : "Aucun locataire trouvé", + "tenant-details" : "Détails du locataire", + "title-max-length" : "Le titre doit contenir moins de 256 caractères", + "delete-tenant-title" : "Êtes-vous sûr de vouloir supprimer le locataire '{{tenantTitle}}' ?", + "delete-tenant-text" : "Attention, après confirmation, le locataire et toutes les données associées seront irrécupérables.", + "delete-tenants-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 locataire} other {# locataires} } ?", + "delete-tenants-action-title" : "Supprimer { count, plural, =1 {1 locataire} other {# locataires} }", + "delete-tenants-text" : "Attention, après confirmation, tous les locataires sélectionnés seront supprimés ainsi que toutes les données associées.", + "title" : "Titre", + "title-required" : "Le titre est requis.", + "description" : "Description", + "details" : "Détails", + "events" : "Événements", + "copyId" : "Copier l'identifiant du locataire", + "idCopiedMessage" : "Identifiant du locataire copié dans le presse-papiers", + "select-tenant" : "Sélectionner un locataire", + "no-tenants-matching" : "Aucun locataire correspondant à '{{entity}}' trouvé.", + "tenant-required" : "Le locataire est requis", + "search" : "Rechercher des locataires", + "selected-tenants" : "{ count, plural, =1 {1 locataire} other {# locataires} } sélectionné(s)", + "isolated-tb-rule-engine" : "Utiliser des files d’attente isolées pour le moteur de règles ThingsBoard", + "isolated-tb-rule-engine-details" : "Chaque locataire aura des files d’attente dédiées pour le moteur de règles" + }, + "tenant-profile" : { + "tenant-profile" : "Profil de locataire", + "tenant-profiles" : "Profils de locataire", + "add" : "Ajouter un profil de locataire", + "add-profile" : "Ajouter un profil", + "debug" : "Déboguer", + "edit" : "Modifier le profil de locataire", + "tenant-profile-details" : "Détails du profil de locataire", + "no-tenant-profiles-text" : "Aucun profil de locataire trouvé", + "name-max-length" : "Le nom doit contenir moins de 256 caractères", + "search" : "Rechercher des profils de locataire", + "selected-tenant-profiles" : "{ count, plural, =1 {1 profil de locataire} other {# profils de locataire} } sélectionné(s)", + "no-tenant-profiles-matching" : "Aucun profil de locataire correspondant à '{{entity}}' trouvé.", + "tenant-profile-required" : "Le profil de locataire est requis", + "idCopiedMessage" : "L'identifiant du profil de locataire a été copié dans le presse-papiers", + "set-default" : "Définir comme profil par défaut", + "delete" : "Supprimer le profil de locataire", + "copyId" : "Copier l'identifiant du profil de locataire", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "data" : "Données du profil", + "profile-configuration" : "Configuration du profil", + "description" : "Description", + "default" : "Défaut", + "delete-tenant-profile-title" : "Êtes-vous sûr de vouloir supprimer le profil de locataire '{{tenantProfileName}}' ?", + "delete-tenant-profile-text" : "Attention, après confirmation, le profil de locataire et toutes les données associées seront irrécupérables.", + "delete-tenant-profiles-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 profil de locataire} other {# profils de locataire} } ?", + "delete-tenant-profiles-text" : "Attention, après confirmation, tous les profils de locataire sélectionnés seront supprimés ainsi que toutes les données associées.", + "set-default-tenant-profile-title" : "Êtes-vous sûr de vouloir définir le profil de locataire '{{tenantProfileName}}' comme profil par défaut ?", + "set-default-tenant-profile-text" : "Après confirmation, le profil sera défini comme profil par défaut et sera utilisé pour les nouveaux locataires sans profil spécifié.", + "no-tenant-profiles-found" : "Aucun profil de locataire trouvé.", + "create-new-tenant-profile" : "Créer un nouveau !", + "create-tenant-profile" : "Créer un nouveau profil de locataire", + "import" : "Importer un profil de locataire", + "export" : "Exporter un profil de locataire", + "export-failed-error" : "Impossible d’exporter le profil de locataire : {{error}}", + "tenant-profile-file" : "Fichier de profil de locataire", + "invalid-tenant-profile-file-error" : "Impossible d’importer le profil : structure de données invalide.", + "advanced-settings" : "Paramètres avancés", + "entities" : "Entités", + "rule-engine" : "Moteur de règles", + "time-to-live" : "Durée de vie", + "calculated-fields" : "Champs calculés", + "alarms-and-notifications" : "Alarmes et notifications", + "ota-files-in-bytes" : "Fichiers", + "ws-title" : "WS", + "unlimited" : "(0 - illimité)", + "maximum-devices" : "Nombre maximum d'appareils", + "maximum-devices-required" : "Le nombre maximum d'appareils est requis.", + "maximum-devices-range" : "Le nombre maximum d'appareils ne peut pas être négatif", + "maximum-assets" : "Nombre maximum d'actifs", + "maximum-assets-required" : "Le nombre maximum d'actifs est requis.", + "maximum-assets-range" : "Le nombre maximum d'actifs ne peut pas être négatif", + "maximum-customers" : "Nombre maximum de clients", + "maximum-customers-required" : "Le nombre maximum de clients est requis.", + "maximum-customers-range" : "Le nombre maximum de clients ne peut pas être négatif", + "maximum-users" : "Nombre maximum d'utilisateurs", + "maximum-users-required" : "Le nombre maximum d'utilisateurs est requis.", + "maximum-users-range" : "Le nombre maximum d'utilisateurs ne peut pas être négatif", + "maximum-dashboards" : "Nombre maximum de tableaux de bord", + "maximum-dashboards-required" : "Le nombre maximum de tableaux de bord est requis.", + "maximum-dashboards-range" : "Le nombre maximum de tableaux de bord ne peut pas être négatif", + "maximum-edges" : "Nombre maximum d'edges", + "maximum-edges-required" : "Le nombre maximum d'edges est requis.", + "maximum-edges-range" : "Le nombre maximum d'edges ne peut pas être négatif", + "maximum-rule-chains" : "Nombre maximum de chaînes de règles", + "maximum-rule-chains-required" : "Le nombre maximum de chaînes de règles est requis.", + "maximum-rule-chains-range" : "Le nombre maximum de chaînes de règles ne peut pas être négatif", + "maximum-resources-sum-data-size" : "Taille maximale totale des fichiers de ressources (octets)", + "maximum-resources-sum-data-size-required" : "La taille totale maximale des fichiers de ressources est requise.", + "maximum-resources-sum-data-size-range" : "La taille totale maximale des fichiers de ressources ne peut pas être négative", + "maximum-resource-size" : "Taille maximale du fichier ressource (octets)", + "maximum-resource-size-required" : "La taille maximale du fichier ressource est requise", + "maximum-resource-size-range" : "La taille maximale du fichier ressource ne peut pas être négative", + "maximum-ota-packages-sum-data-size" : "Taille maximale totale des fichiers OTA (octets)", + "maximum-ota-package-sum-data-size-required" : "La taille maximale totale des fichiers OTA est requise.", + "maximum-ota-package-sum-data-size-range" : "La taille maximale totale des fichiers OTA ne peut pas être négative", + "maximum-debug-duration-min" : "Durée maximale de débogage (min)", + "maximum-debug-duration-min-range" : "La durée maximale de débogage ne peut pas être négative", + "rest-requests-for-tenant" : "Requêtes REST pour le locataire", + "transport-tenant-telemetry-msg-rate-limit" : "Messages de télémétrie du locataire (transport)", + "transport-tenant-telemetry-data-points-rate-limit" : "Points de données de télémétrie du locataire (transport)", + "transport-device-msg-rate-limit" : "Messages de l'appareil (transport)", + "transport-device-telemetry-msg-rate-limit" : "Messages de télémétrie de l'appareil (transport)", + "transport-device-telemetry-data-points-rate-limit" : "Points de données de télémétrie de l'appareil (transport)", + "transport-gateway-msg-rate-limit" : "Messages de la passerelle (transport)", + "transport-gateway-telemetry-msg-rate-limit" : "Messages de télémétrie de la passerelle (transport)", + "transport-gateway-telemetry-data-points-rate-limit" : "Points de données de télémétrie de la passerelle (transport)", + "transport-gateway-device-msg-rate-limit" : "Messages de l'appareil de la passerelle (transport)", + "transport-gateway-device-telemetry-msg-rate-limit" : "Messages de télémétrie de l'appareil de la passerelle (transport)", + "transport-gateway-device-telemetry-data-points-rate-limit" : "Points de données de télémétrie de l'appareil de la passerelle (transport)", + "tenant-entity-export-rate-limit" : "Création de version d'entité", + "tenant-entity-import-rate-limit" : "Chargement de version d'entité", + "tenant-notification-request-rate-limit" : "Requêtes de notification", + "tenant-notification-requests-per-rule-rate-limit" : "Requêtes de notification par règle", + "max-calculated-fields" : "Nombre maximal de champs calculés par entité", + "max-calculated-fields-range" : "Le nombre maximal de champs calculés ne peut pas être négatif", + "max-calculated-fields-required" : "Le nombre maximal de champs calculés est requis", + "max-data-points-per-rolling-arg" : "Nombre maximal de points de données dans les arguments glissants", + "max-data-points-per-rolling-arg-range" : "Le nombre ne peut pas être négatif", + "max-data-points-per-rolling-arg-required" : "Le nombre maximal de points de données est requis", + "max-arguments-per-cf" : "Nombre maximal d’arguments par champ calculé", + "max-arguments-per-cf-range" : "Le nombre maximal d’arguments ne peut pas être négatif", + "max-arguments-per-cf-required" : "Le nombre maximal d’arguments est requis", + "max-state-size" : "Taille maximale de l'état (en Ko)", + "max-state-size-range" : "La taille maximale ne peut pas être négative", + "max-state-size-required" : "La taille maximale de l'état est requise", + "max-value-argument-size" : "Taille maximale d'un argument de valeur unique (en Ko)", + "max-value-argument-size-range" : "La taille maximale ne peut pas être négative", + "max-value-argument-size-required" : "La taille maximale de la valeur est requise", + "max-transport-messages" : "Nombre maximal de messages de transport", + "max-transport-messages-required" : "Le nombre maximal de messages de transport est requis.", + "max-transport-messages-range" : "Le nombre maximal de messages de transport ne peut pas être négatif", + "max-transport-data-points" : "Nombre maximal de points de données de transport", + "max-transport-data-points-required" : "Le nombre maximal de points de données est requis.", + "max-transport-data-points-range" : "Le nombre maximal de points de données ne peut pas être négatif", + "max-r-e-executions" : "Nombre maximal d'exécutions du moteur de règles", + "max-r-e-executions-required" : "Le nombre d'exécutions du moteur de règles est requis.", + "max-r-e-executions-range" : "Le nombre d'exécutions ne peut pas être négatif", + "max-j-s-executions" : "Nombre maximal d'exécutions JavaScript", + "max-j-s-executions-required" : "Le nombre maximal d'exécutions JavaScript est requis.", + "max-j-s-executions-range" : "Le nombre maximal d'exécutions JavaScript ne peut pas être négatif", + "max-tbel-executions" : "Nombre maximal d'exécutions TBEL", + "max-tbel-executions-required" : "Le nombre maximal d'exécutions TBEL est requis.", + "max-tbel-executions-range" : "Le nombre maximal d'exécutions TBEL ne peut pas être négatif", + "max-d-p-storage-days" : "Nombre maximal de jours de stockage des points de données", + "max-d-p-storage-days-required" : "Le nombre de jours est requis.", + "max-d-p-storage-days-range" : "Le nombre ne peut pas être négatif", + "default-storage-ttl-days" : "Durée de vie par défaut du stockage (en jours)", + "default-storage-ttl-days-required" : "La durée par défaut est requise.", + "default-storage-ttl-days-range" : "La durée ne peut pas être négative", + "alarms-ttl-days" : "Durée de vie des alarmes (en jours)", + "alarms-ttl-days-required" : "La durée de vie des alarmes est requise", + "alarms-ttl-days-days-range" : "La durée ne peut pas être négative", + "rpc-ttl-days" : "Durée de vie RPC (en jours)", + "rpc-ttl-days-required" : "La durée de vie RPC est requise", + "rpc-ttl-days-days-range" : "La durée ne peut pas être négative", + "queue-stats-ttl-days" : "Durée de vie des statistiques de file d'attente", + "queue-stats-ttl-days-required" : "La durée est requise", + "queue-stats-ttl-days-range" : "La durée ne peut pas être négative", + "rule-engine-exceptions-ttl-days" : "Durée de vie des exceptions du moteur de règles", + "rule-engine-exceptions-ttl-days-required" : "La durée est requise", + "rule-engine-exceptions-ttl-days-range" : "La durée ne peut pas être négative", + "max-rule-node-executions-per-message" : "Nombre maximal d'exécutions par message d'un nœud de règle", + "max-rule-node-executions-per-message-required" : "Le nombre est requis.", + "max-rule-node-executions-per-message-range" : "Le nombre ne peut pas être négatif", + "max-emails" : "Nombre maximal d'e-mails envoyés", + "max-emails-required" : "Le nombre d'e-mails est requis.", + "max-emails-range" : "Le nombre ne peut pas être négatif", + "sms-enabled" : "SMS activé", + "max-sms" : "Nombre maximal de SMS envoyés", + "max-sms-required" : "Le nombre de SMS est requis.", + "max-sms-range" : "Le nombre ne peut pas être négatif", + "max-created-alarms" : "Nombre maximal d'alarmes créées", + "max-created-alarms-required" : "Le nombre est requis.", + "max-created-alarms-range" : "Le nombre ne peut pas être négatif", + "no-queue" : "Aucune file d’attente configurée", + "add-queue" : "Ajouter une file d’attente", + "queues-with-count" : "Files d’attente ({{count}})", + "tenant-rest-limits" : "Requêtes REST pour le locataire", + "customer-rest-limits" : "Requêtes REST pour le client", + "incorrect-pattern-for-rate-limits" : "Le format est une liste de paires séparées par des virgules, contenant une capacité et une période (en secondes), séparées par deux-points, par ex. 100:1,2000:60", + "too-small-value-zero" : "La valeur doit être supérieure à 0", + "too-small-value-one" : "La valeur doit être supérieure à 1", + "queue-size-is-limited-by-system-configuration" : "La taille de la file d’attente est également limitée par la configuration système.", + "cassandra-tenant-limits-configuration" : "Requête Cassandra pour le locataire", + "ws-limit-max-sessions-per-tenant" : "Nombre maximal de sessions par locataire", + "ws-limit-max-sessions-per-customer" : "Nombre maximal de sessions par client", + "ws-limit-max-sessions-per-regular-user" : "Nombre maximal de sessions par utilisateur régulier", + "ws-limit-max-sessions-per-public-user" : "Nombre maximal de sessions par utilisateur public", + "ws-limit-queue-per-session" : "Taille maximale de la file de messages par session", + "ws-limit-max-subscriptions-per-tenant" : "Nombre maximal d’abonnements par locataire", + "ws-limit-max-subscriptions-per-customer" : "Nombre maximal d’abonnements par client", + "ws-limit-max-subscriptions-per-regular-user" : "Nombre maximal d’abonnements par utilisateur régulier", + "ws-limit-max-subscriptions-per-public-user" : "Nombre maximal d’abonnements par utilisateur public", + "ws-limit-updates-per-session" : "Mises à jour WS par session", + "rate-limits" : { + "add-limit" : "Ajouter une limite", + "advanced-settings" : "Paramètres avancés", + "edit-limit" : "Modifier la limite", + "but-less-than" : "mais inférieur à", + "calculated-field-debug-event-rate-limit" : "Événements de débogage des champs calculés", + "edit-calculated-field-debug-event-rate-limit" : "Modifier la limite des événements de débogage de champ calculé", + "edit-transport-tenant-msg-title" : "Modifier les limites de messages de transport du locataire", + "edit-transport-tenant-telemetry-msg-title" : "Modifier les limites des messages de télémétrie du locataire", + "edit-transport-tenant-telemetry-data-points-title" : "Modifier les limites des points de données de télémétrie du locataire", + "edit-transport-device-msg-title" : "Modifier les limites des messages de l’appareil", + "edit-transport-device-telemetry-msg-title" : "Modifier les limites des messages de télémétrie de l’appareil", + "edit-transport-device-telemetry-data-points-title" : "Modifier les limites des points de données de télémétrie de l’appareil", + "edit-transport-gateway-msg-title" : "Modifier les limites des messages de la passerelle", + "edit-transport-gateway-telemetry-msg-title" : "Modifier les limites des messages de télémétrie de la passerelle", + "edit-transport-gateway-telemetry-data-points-title" : "Modifier les limites des points de données de télémétrie de la passerelle", + "edit-transport-gateway-device-msg-title" : "Modifier les limites des messages des appareils de la passerelle", + "edit-transport-gateway-device-telemetry-msg-title" : "Modifier les limites des messages de télémétrie des appareils de la passerelle", + "edit-transport-gateway-device-telemetry-data-points-title" : "Modifier les limites des points de données de télémétrie des appareils de la passerelle", + "edit-tenant-rest-limits-title" : "Modifier les limites des requêtes REST du locataire", + "edit-customer-rest-limits-title" : "Modifier les limites des requêtes REST du client", + "edit-ws-limit-updates-per-session-title" : "Modifier les limites des mises à jour WS par session", + "edit-cassandra-tenant-limits-configuration-title" : "Modifier les limites des requêtes Cassandra du locataire", + "edit-tenant-entity-export-rate-limit-title" : "Modifier les limites de création de version d’entité", + "edit-tenant-entity-import-rate-limit-title" : "Modifier les limites de chargement de version d’entité", + "edit-tenant-notification-request-rate-limit-title" : "Modifier les limites des requêtes de notification", + "edit-tenant-notification-requests-per-rule-rate-limit-title" : "Modifier les limites des requêtes de notification par règle", + "edit-edge-events-rate-limit" : "Modifier les limites des événements Edge", + "edit-edge-events-per-edge-rate-limit" : "Modifier les limites des événements Edge par instance", + "edge-events-rate-limit" : "Événements Edge", + "edge-events-per-edge-rate-limit" : "Événements Edge par instance", + "edit-edge-uplink-messages-rate-limit" : "Modifier les limites des messages montants Edge", + "edit-edge-uplink-messages-per-edge-rate-limit" : "Modifier les limites des messages montants Edge par instance", + "edge-uplink-messages-rate-limit" : "Messages montants Edge", + "edge-uplink-messages-per-edge-rate-limit" : "Messages montants Edge par instance", + "messages-per" : "messages par", + "not-set" : "Non défini", + "number-of-messages" : "Nombre de messages", + "number-of-messages-required" : "Le nombre de messages est requis.", + "number-of-messages-min" : "La valeur minimale est 1.", + "preview" : "Aperçu", + "per-seconds" : "Par secondes", + "per-seconds-required" : "La durée est requise.", + "per-seconds-min" : "La valeur minimale est 1.", + "rate-limits" : "Limites de débit", + "remove-limit" : "Supprimer la limite", + "transport-tenant-msg" : "Messages de transport du locataire", + "transport-tenant-telemetry-msg" : "Messages de télémétrie du locataire", + "transport-tenant-telemetry-data-points" : "Points de données de télémétrie du locataire", + "transport-device-msg" : "Messages de l’appareil", + "transport-device-telemetry-msg" : "Messages de télémétrie de l’appareil", + "transport-device-telemetry-data-points" : "Points de données de télémétrie de l’appareil", + "transport-gateway-msg" : "Messages de la passerelle", + "transport-gateway-telemetry-msg" : "Messages de télémétrie de la passerelle", + "transport-gateway-telemetry-data-points" : "Points de données de télémétrie de la passerelle", + "transport-gateway-device-msg" : "Messages des appareils de la passerelle", + "transport-gateway-device-telemetry-msg" : "Messages de télémétrie des appareils de la passerelle", + "transport-gateway-device-telemetry-data-points" : "Points de données de télémétrie des appareils de la passerelle", + "sec" : "sec" + } + }, + "timeinterval" : { + "seconds-interval" : "{ seconds, plural, =1 {1 seconde} other {# secondes} }", + "minutes-interval" : "{ minutes, plural, =1 {1 minute} other {# minutes} }", + "hours-interval" : "{ hours, plural, =1 {1 heure} other {# heures} }", + "days-interval" : "{ days, plural, =1 {1 jour} other {# jours} }", + "days" : "Jours", + "hours" : "Heures", + "minutes" : "Minutes", + "seconds" : "Secondes", + "advanced" : "Avancé", + "custom" : "Personnalisé", + "predefined" : { + "yesterday" : "Hier", + "day-before-yesterday" : "Avant-hier", + "this-day-last-week" : "Ce jour la semaine dernière", + "previous-week" : "Semaine précédente (dim. - sam.)", + "previous-week-iso" : "Semaine précédente (lun. - dim.)", + "previous-month" : "Mois précédent", + "previous-quarter" : "Trimestre précédent", + "previous-half-year" : "Semestre précédent", + "previous-year" : "Année précédente", + "current-hour" : "Heure en cours", + "current-day" : "Jour en cours", + "current-day-so-far" : "Jour en cours (jusqu'à présent)", + "current-week" : "Semaine en cours (dim. - sam.)", + "current-week-iso" : "Semaine en cours (lun. - dim.)", + "current-week-so-far" : "Semaine en cours (jusqu'à présent, dim. - sam.)", + "current-week-iso-so-far" : "Semaine en cours (jusqu'à présent, lun. - dim.)", + "current-month" : "Mois en cours", + "current-month-so-far" : "Mois en cours (jusqu'à présent)", + "current-quarter" : "Trimestre en cours", + "current-quarter-so-far" : "Trimestre en cours (jusqu'à présent)", + "current-half-year" : "Semestre en cours", + "current-half-year-so-far" : "Semestre en cours (jusqu'à présent)", + "current-year" : "Année en cours", + "current-year-so-far" : "Année en cours (jusqu'à présent)" + }, + "type" : { + "week" : "Semaine (dim. - sam.)", + "week-iso" : "Semaine (lun. - dim.)", + "month" : "Mois", + "quarter" : "Trimestre" + } + }, + "timeunit" : { + "milliseconds" : "Millisecondes", + "seconds" : "Secondes", + "minutes" : "Minutes", + "hours" : "Heures", + "days" : "Jours" + }, + "timewindow" : { + "timewindow" : "Fenêtre temporelle", + "timewindow-settings" : "Paramètres de la fenêtre temporelle", + "years" : "{ years, plural, =1 { année } other {# années } }", + "years-short" : "{{ years }}a", + "months" : "{ months, plural, =1 { mois } other {# mois } }", + "months-short" : "{{ months }}M", + "weeks" : "{ weeks, plural, =1 { semaine } other {# semaines } }", + "weeks-short" : "{{ weeks }}s", + "days" : "{ days, plural, =1 { jour } other {# jours } }", + "days-short" : "{{ days }}j", + "hours" : "{ hours, plural, =0 { heure } =1 {1 heure } other {# heures } }", + "hr" : "{{ hr }} h", + "hr-short" : "{{ hr }}h", + "minutes" : "{ minutes, plural, =0 { minute } =1 {1 minute } other {# minutes } }", + "min" : "{{ min }} min", + "min-short" : "{{ min }}m", + "seconds" : "{ seconds, plural, =0 { seconde } =1 {1 seconde } other {# secondes } }", + "sec" : "{{ sec }} sec", + "sec-short" : "{{ sec }}s", + "short" : { + "years" : "{ years, plural, =1 {1 année } other {# années } }", + "days" : "{ days, plural, =1 {1 jour } other {# jours } }", + "hours" : "{ hours, plural, =1 {1 heure } other {# heures } }", + "minutes" : "{{minutes}} min", + "seconds" : "{{seconds}} sec" + }, + "realtime" : "Temps réel", + "history" : "Historique", + "last-prefix" : "dernier", + "period" : "du {{ startTime }} au {{ endTime }}", + "edit" : "Modifier la fenêtre temporelle", + "date-range" : "Plage de dates", + "for-all-time" : "Tout le temps", + "last" : "Dernier", + "time-period" : "Période de temps", + "hide" : "Cacher", + "interval" : "Intervalle", + "just-now" : "À l’instant", + "just-now-lower" : "à l’instant", + "ago" : "il y a", + "style" : "Style de la fenêtre temporelle", + "icon" : "Icône", + "icon-position" : "Position de l’icône", + "icon-position-left" : "Gauche", + "icon-position-right" : "Droite", + "font" : "Police", + "color" : "Couleur", + "displayTypePrefix" : "Afficher le préfixe Temps réel / Historique", + "preview" : "Aperçu", + "relative" : "Relatif", + "range" : "Intervalle", + "hide-timewindow-section" : "Cacher la section de la fenêtre temporelle aux utilisateurs finaux", + "hide-last-interval" : "Cacher le dernier intervalle aux utilisateurs finaux", + "hide-relative-interval" : "Cacher l’intervalle relatif aux utilisateurs finaux", + "hide-fixed-interval" : "Cacher l’intervalle fixe aux utilisateurs finaux", + "hide-aggregation" : "Cacher l’agrégation aux utilisateurs finaux", + "hide-group-interval" : "Cacher l’intervalle de regroupement aux utilisateurs finaux", + "hide-max-values" : "Cacher les valeurs max aux utilisateurs finaux", + "hide-timezone" : "Cacher le fuseau horaire aux utilisateurs finaux", + "disable-custom-interval" : "Désactiver la sélection d’intervalle personnalisé", + "edit-aggregation-functions-list" : "Modifier la liste des fonctions d’agrégation", + "edit-aggregation-functions-list-hint" : "La liste des options disponibles peut être spécifiée.", + "allowed-aggregation-functions" : "Fonctions d’agrégation autorisées", + "edit-intervals-list" : "Modifier la liste des intervalles", + "allowed-agg-intervals" : "Intervalles de regroupement", + "default-agg-interval" : "Intervalle de regroupement par défaut", + "edit-intervals-list-hint" : "Il est possible de spécifier la liste des options d’intervalle disponibles.", + "edit-grouping-intervals-list-hint" : "Il est possible de configurer la liste des intervalles de regroupement et l’intervalle par défaut.", + "all" : "Tous" + }, + "tooltip" : { + "trigger" : "Déclencheur", + "trigger-point" : "Point", + "trigger-axis" : "Axe", + "label" : "Étiquette", + "value" : "Valeur", + "date" : "Date", + "show-date-time-interval" : "Afficher l’intervalle date/heure", + "show-date-time-interval-hint" : "Afficher l’intervalle date/heure selon l’agrégation des données.", + "background-color" : "Couleur d’arrière-plan", + "background-blur" : "Flou d’arrière-plan" + }, + "unit" : { + "millimeter" : "Millimètre", + "centimeter" : "Centimètre", + "angstrom" : "Angström", + "nanometer" : "Nanomètre", + "micrometer" : "Micromètre", + "meter" : "Mètre", + "kilometer" : "Kilomètre", + "inch" : "Pouce", + "foot" : "Pied", + "yard" : "Yard", + "mile" : "Mille", + "nautical-mile" : "Mille nautique", + "astronomical-unit" : "Unité astronomique", + "reciprocal-metre" : "Mètre réciproque", + "meter-per-meter" : "Mètre par mètre", + "steradian" : "Stéradian", + "thou" : "Thou", + "barleycorn" : "Barleycorn", + "hand" : "Main", + "chain" : "Chaîne", + "furlong" : "Furlong", + "league" : "Lieue", + "fathom" : "Brasse", + "cable" : "Câble", + "link" : "Maillon", + "rod" : "Perche", + "nanogram" : "Nanogramme", + "microgram" : "Microgramme", + "milligram" : "Milligramme", + "gram" : "Gramme", + "kilogram" : "Kilogramme", + "tonne" : "Tonne", + "ounce" : "Once", + "pound" : "Livre", + "stone" : "Stone", + "hundredweight-count" : "Quintal", + "short-tons" : "Tonnes courtes", + "dalton" : "Dalton", + "grain" : "Grain", + "drachm" : "Drachme", + "quarter" : "Quart", + "slug" : "Slug", + "carat" : "Carat", + "cubic-millimeter" : "Millimètre cube", + "cubic-centimeter" : "Centimètre cube", + "cubic-meter" : "Mètre cube", + "cubic-kilometer" : "Kilomètre cube", + "microliter" : "Microlitre", + "milliliter" : "Millilitre", + "liter" : "Litre", + "hectoliter" : "Hectolitre", + "cubic-inch" : "Pouce cube", + "cubic-foot" : "Pied cube", + "cubic-yard" : "Yard cube", + "fluid-ounce" : "Once liquide", + "pint" : "Pinte", + "quart" : "Quart", + "gallon" : "Gallon", + "oil-barrels" : "Baril de pétrole", + "cubic-meter-per-kilogram" : "Mètre cube par kilogramme", + "gill" : "Gill", + "hogshead" : "Hogshead", + "teaspoon" : "Cuillère à café", + "tablespoon" : "Cuillère à soupe", + "cup" : "Tasse", + "celsius" : "Celsius", + "kelvin" : "Kelvin", + "rankine" : "Rankine", + "fahrenheit" : "Fahrenheit", + "percent" : "Pourcentage", + "meter-per-second" : "Mètre par seconde", + "kilometer-per-hour" : "Kilomètre par heure", + "foot-per-second" : "Pied par seconde", + "mile-per-hour" : "Mille par heure", + "knot" : "Nœud", + "millimeters-per-minute" : "Millimètres par minute", + "kilometer-per-hour-squared" : "Kilomètre par heure carrée", + "foot-per-second-squared" : "Pied par seconde carrée", + "pascal" : "Pascal", + "kilopascal" : "Kilopascal", + "megapascal" : "Mégapascal", + "gigapascal" : "Gigapascal", + "millibar" : "Millibar", + "bar" : "Bar", + "kilobar" : "Kilobar", + "newton" : "Newton", + "newton-meter" : "Newton-mètre", + "foot-pounds" : "Pieds-livres", + "inch-pounds" : "Pouces-livres", + "newton-per-meter" : "Newton par mètre", + "atmospheres" : "Atmosphères", + "pounds-per-square-inch" : "Livres par pouce carré", + "torr" : "Torr", + "inches-of-mercury" : "Pouces de mercure", + "pascal-per-square-meter" : "Pascal par mètre carré", + "pound-per-square-inch" : "Livre par pouce carré", + "newton-per-square-meter" : "Newton par mètre carré", + "kilogram-force-per-square-meter" : "Kilogramme-force par mètre carré", + "pascal-per-square-centimeter" : "Pascal par centimètre carré", + "ton-force-per-square-inch" : "Tonne-force par pouce carré", + "kilonewton-per-square-meter" : "Kilonewton par mètre carré", + "newton-per-square-millimeter" : "Newton par millimètre carré", + "microjoule" : "Microjoule", + "millijoule" : "Millijoule", + "joule" : "Joule", + "kilojoule" : "Kilojoule", + "megajoule" : "Mégajoule", + "gigajoule" : "Gigajoule", + "watt-hour" : "Watt-heure", + "kilowatt-hour" : "Kilowatt-heure", + "electron-volts" : "Électronvolt", + "joules-per-coulomb" : "Joules par coulomb", + "british-thermal-unit" : "British Thermal Unit", + "foot-pound" : "Pied-livre", + "calorie" : "Calorie", + "small-calorie" : "Petite calorie", + "kilocalorie" : "Kilocalorie", + "joule-per-kelvin" : "Joule par kelvin", + "joule-per-kilogram-kelvin" : "Joule par kilogramme-kelvin", + "joule-per-kilogram" : "Joule par kilogramme", + "watt-per-meter-kelvin" : "Watt par mètre-kelvin", + "joule-per-cubic-meter" : "Joule par mètre cube", + "therm" : "Thermie", + "electric-dipole-moment" : "Moment dipolaire électrique", + "magnetic-dipole-moment" : "Moment dipolaire magnétique", + "debye" : "Debye", + "coulomb-per-square-meter-per-volt" : "Coulomb par mètre carré par volt", + "milliwatt" : "Milliwatt", + "microwatt" : "Microwatt", + "watt" : "Watt", + "kilowatt" : "Kilowatt", + "megawatt" : "Mégawatt", + "gigawatt" : "Gigawatt", + "metric-horsepower" : "Cheval-vapeur métrique", + "milliwatt-per-square-centimeter" : "Milliwatt par centimètre carré", + "watt-per-square-centimeter" : "Watt par centimètre carré", + "kilowatt-per-square-centimeter" : "Kilowatt par centimètre carré", + "milliwatt-per-square-meter" : "Milliwatt par mètre carré", + "watt-per-square-meter" : "Watt par mètre carré", + "kilowatt-per-square-meter" : "Kilowatt par mètre carré", + "watt-per-square-inch" : "Watt par pouce carré", + "kilowatt-per-square-inch" : "Kilowatt par pouce carré", + "horsepower" : "Cheval-vapeur", + "btu-per-hour" : "BTU/heure", + "coulomb" : "Coulomb", + "millicoulomb" : "Millicoulomb", + "microcoulomb" : "Microcoulomb", + "picocoulomb" : "Picocoulomb", + "coulomb-per-meter" : "Coulomb par mètre", + "coulomb-per-cubic-meter" : "Coulomb par mètre cube", + "coulomb-per-square-meter" : "Coulomb par mètre carré", + "square-millimeter" : "Millimètre carré", + "square-centimeter" : "Centimètre carré", + "square-meter" : "Mètre carré", + "hectare" : "Hectare", + "square-kilometer" : "Kilomètre carré", + "square-inch" : "Pouce carré", + "square-foot" : "Pied carré", + "square-yard" : "Yard carré", + "acre" : "Acre", + "square-mile" : "Mille carré", + "are" : "Are", + "barn" : "Barn", + "circular-inch" : "Pouce circulaire", + "milliampere-hour" : "Milliampère-heure", + "ampere-hours" : "Ampères-heures", + "kiloampere-hours" : "Kiloampères-heures", + "nanoampere" : "Nanoampère", + "picoampere" : "Picoampère", + "microampere" : "Microampère", + "milliampere" : "Milliampère", + "ampere" : "Ampère", + "microampere-per-square-centimeter" : "Microampère par centimètre carré", + "ampere-per-square-meter" : "Ampère par mètre carré", + "ampere-per-meter" : "Ampère par mètre", + "oersted" : "Oersted", + "bohr-magneton" : "Magnéton de Bohr", + "ampere-meter-squared" : "Ampère-mètre carré", + "nanovolt" : "Nanovolt", + "picovolt" : "Picovolt", + "volt" : "Volt", + "dbmV" : "dBmV", + "dbm" : "dBm", + "volt-meter" : "Voltmètre", + "kilovolt-meter" : "Kilovolt-mètre", + "megavolt-meter" : "Mégavolt-mètre", + "microvolt-meter" : "Microvolt-mètre", + "millivolt-meter" : "Millivolt-mètre", + "nanovolt-meter" : "Nanovolt-mètre", + "ohm" : "Ohm", + "microohm" : "Microohm", + "milliohm" : "Milliohm", + "kilohm" : "Kilohm", + "megohm" : "Mégohm", + "gigohm" : "Gigohm", + "hertz" : "Hertz", + "kilohertz" : "Kilohertz", + "megahertz" : "Mégahertz", + "gigahertz" : "Gigahertz", + "rpm" : "Tours par minute", + "candela-per-square-meter" : "Candela par mètre carré", + "candela" : "Candela", + "lumen" : "Lumen", + "lux" : "Lux", + "foot-candle" : "Pied-bougie", + "lumen-per-square-meter" : "Lumen par mètre carré", + "lux-second" : "Lux-seconde", + "lumen-second" : "Lumen-seconde", + "lumens-per-watt" : "Lumens par watt", + "mole" : "Mole", + "nanomole" : "Nanomole", + "micromole" : "Micromole", + "millimole" : "Millimole", + "kilomole" : "Kilomole", + "mole-per-cubic-meter" : "Mole par mètre cube", + "rssi" : "RSSI", + "ppm" : "Parties par million", + "ppb" : "Parties par milliard", + "micrograms-per-cubic-meter" : "Microgrammes par mètre cube", + "aqi" : "Indice de qualité de l’air (AQI)", + "gram-per-cubic-meter" : "Grammes par mètre cube", + "gram-per-kilogram" : "Humidité spécifique", + "millimeters-per-second" : "Millimètres par seconde", + "neper" : "Néper", + "bel" : "Bel", + "decibel" : "Décibel", + "meters-per-second-squared" : "Mètres par seconde carrée", + "becquerel" : "Becquerel", + "curie" : "Curie", + "gray" : "Gray", + "sievert" : "Sievert", + "roentgen" : "Roentgen", + "cps" : "Comptes par seconde", + "rad" : "Rad", + "rem" : "Rem", + "dps" : "Désintégrations par seconde", + "rutherford" : "Rutherford", + "coulombs-per-kilogram" : "Coulombs par kilogramme", + "becquerels-per-cubic-meter" : "Becquerels par mètre cube", + "curies-per-liter" : "Curies par litre", + "becquerels-per-second" : "Becquerels par seconde", + "curies-per-second" : "Curies par seconde", + "gy-per-second" : "Gray par seconde", + "watt-per-steradian" : "Watt par stéradian", + "watt-per-square-metre-steradian" : "Watt par mètre carré-stéradian", + "ph-level" : "Niveau de pH", + "turbidity" : "Turbidité", + "mg-per-liter" : "Milligrammes par litre", + "microsiemens-per-centimeter" : "Microsiemens par centimètre", + "millisiemens-per-meter" : "Millisiemens par mètre", + "siemens-per-meter" : "Siemens par mètre", + "kilogram-per-cubic-meter" : "Kilogramme par mètre cube", + "gram-per-cubic-centimeter" : "Gramme par centimètre cube", + "kilogram-per-square-meter" : "Kilogramme par mètre carré", + "milligram-per-milliliter" : "Milligramme par millilitre", + "milligram-per-cubic-meter" : "Milligramme par mètre cube", + "pound-per-cubic-foot" : "Livre par pied cube", + "ounces-per-cubic-inch" : "Onces par pouce cube", + "tons-per-cubic-yard" : "Tonnes par yard cube", + "particle-density" : "Densité des particules", + "kilometers-per-liter" : "Kilomètres par litre", + "miles-per-gallon" : "Miles par gallon", + "liters-per-100-km" : "Litres par 100 km", + "gallons-per-mile" : "Gallons par mile", + "liters-per-hour" : "Litres par heure", + "gallons-per-hour" : "Gallons par heure", + "beats-per-minute" : "Battements par minute", + "millimeters-of-mercury" : "Millimètres de mercure", + "milligrams-per-deciliter" : "Milligrammes par décilitre", + "g-force" : "Force g", + "kilonewton" : "Kilonewton", + "kilogram-force" : "Kilogramme-force", + "pound-force" : "Livre-force", + "kilopound-force" : "Kilolivre-force", + "dyne" : "Dyne", + "poundal" : "Poundal", + "kip" : "Kip", + "gal" : "Gal", + "gravity" : "Gravité", + "hectopascal" : "Hectopascal", + "atmosphere" : "Atmosphère", + "millibars" : "Millibars", + "inch-of-mercury" : "Pouce de mercure", + "richter-scale" : "Échelle de Richter", + "second" : "Seconde", + "minute" : "Minute", + "hour" : "Heure", + "day" : "Jour", + "week" : "Semaine", + "month" : "Mois", + "year" : "Année", + "cubic-foot-per-minute" : "Pied cube par minute", + "cubic-meters-per-hour" : "Mètres cubes par heure", + "cubic-meters-per-second" : "Mètres cubes par seconde", + "liter-per-second" : "Litre par seconde", + "liter-per-minute" : "Litre par minute", + "gallons-per-minute" : "Gallons par minute", + "cubic-foot-per-second" : "Pied cube par seconde", + "milliliters-per-minute" : "Millilitres par minute", + "bit" : "Bit", + "byte" : "Octet", + "kilobyte" : "Kilooctet", + "megabyte" : "Mégaoctet", + "gigabyte" : "Gigaoctet", + "terabyte" : "Téraoctet", + "petabyte" : "Pétaoctet", + "exabyte" : "Exaoctet", + "zettabyte" : "Zettaoctet", + "yottabyte" : "Yottaoctet", + "bit-per-second" : "Bit par seconde", + "kilobit-per-second" : "Kilobit par seconde", + "megabit-per-second" : "Mégabit par seconde", + "gigabit-per-second" : "Gigabit par seconde", + "terabit-per-second" : "Térabit par seconde", + "byte-per-second" : "Octet par seconde", + "kilobyte-per-second" : "Kilooctets par seconde", + "megabyte-per-second" : "Mégaoctets par seconde", + "gigabyte-per-second" : "Gigaoctets par seconde", + "degree" : "Degré", + "radian" : "Radian", + "gradian" : "Gradian", + "revolution" : "Révolution", + "siemens" : "Siemens", + "millisiemens" : "Millisiemens", + "microsiemens" : "Microsiemens", + "kilosiemens" : "Kilosiemens", + "megasiemens" : "Mégasiemens", + "gigasiemens" : "Gigasiemens", + "farad" : "Farad", + "millifarad" : "Millifarad", + "microfarad" : "Microfarad", + "nanofarad" : "Nanofarad", + "picofarad" : "Picofarad", + "kilofarad" : "Kilofarad", + "megafarad" : "Mégafarad", + "gigafarad" : "Gigafarad", + "terfarad" : "Térafarad", + "farad-per-meter" : "Farad par mètre", + "tesla" : "Tesla", + "gauss" : "Gauss", + "kilogauss" : "Kilogauss", + "millitesla" : "Millitesla", + "microtesla" : "Microtesla", + "nanotesla" : "Nanotesla", + "kilotesla" : "Kilotesla", + "megatesla" : "Mégatesla", + "millitesla-square-meters" : "Millitesla mètre carré", + "gamma" : "Gamma", + "lambda" : "Lambda", + "square-meter-per-second" : "Mètre carré par seconde", + "square-centimeter-per-second" : "Centimètre carré par seconde", + "stoke" : "Stoke", + "centistokes" : "Centistokes", + "square-foot-per-second" : "Pied carré par seconde", + "square-inch-per-second" : "Pouce carré par seconde", + "pascal-second" : "Pascal-seconde", + "centipoise" : "Centipoise", + "poise" : "Poise", + "reynolds" : "Reynolds", + "pound-per-foot-hour" : "Livre par pied-heure", + "newton-second-per-square-meter" : "Newton-seconde par mètre carré", + "dyne-second-per-square-centimeter" : "Dyne-seconde par centimètre carré", + "kilogram-per-meter-second" : "Kilogramme par mètre-seconde", + "tesla-square-meters" : "Tesla mètre carré", + "maxwell" : "Maxwell", + "tesla-per-meter" : "Tesla par mètre", + "gauss-per-centimeter" : "Gauss par centimètre", + "weber" : "Weber", + "microweber" : "Microweber", + "milliweber" : "Milliweber", + "gauss-square-centimeter" : "Gauss centimètre carré", + "kilogauss-square-centimeter" : "Kilogauss centimètre carré", + "henry" : "Henry", + "millihenry" : "Millihenry", + "microhenry" : "Microhenry", + "nanohenry" : "Nanohenry", + "henry-per-meter" : "Henry par mètre", + "tesla-meter-per-ampere" : "Tesla mètre par ampère", + "gauss-per-oersted" : "Gauss par Oersted", + "kilogram-per-mole" : "Kilogramme par mole", + "gram-per-mole" : "Gramme par mole", + "milligram-per-mole" : "Milligramme par mole", + "joule-per-mole" : "Joule par mole", + "joule-per-mole-kelvin" : "Joule par mole-kelvin", + "millivolts-per-meter" : "Millivolts par mètre", + "volts-per-meter" : "Volts par mètre", + "kilovolts-per-meter" : "Kilovolts par mètre", + "radian-per-second" : "Radian par seconde", + "radian-per-second-squared" : "Radian par seconde carrée", + "revolutions-per-minute-per-second" : "Accélération angulaire", + "deg-per-second" : "Degrés/seconde", + "degrees-brix" : "Degrés Brix", + "katal" : "Katal", + "katal-per-cubic-metre" : "Katal par mètre cube" + }, + "user" : { + "user" : "Utilisateur", + "users" : "Utilisateurs", + "customer-users" : "Utilisateurs client", + "tenant-admins" : "Administrateurs locataire", + "sys-admin" : "Administrateur système", + "tenant-admin" : "Administrateur locataire", + "customer" : "Client", + "anonymous" : "Anonyme", + "add" : "Ajouter un utilisateur", + "delete" : "Supprimer l'utilisateur", + "add-user-text" : "Ajouter un nouvel utilisateur", + "no-users-text" : "Aucun utilisateur trouvé", + "user-details" : "Détails de l'utilisateur", + "delete-user-title" : "Êtes-vous sûr de vouloir supprimer l'utilisateur '{{userEmail}}' ?", + "delete-user-text" : "Attention, après confirmation, l'utilisateur et toutes les données associées seront irrécupérables.", + "delete-users-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 utilisateur} other {# utilisateurs} } ?", + "delete-users-action-title" : "Supprimer { count, plural, =1 {1 utilisateur} other {# utilisateurs} }", + "delete-users-text" : "Attention, après confirmation, tous les utilisateurs sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "activation-email-sent-message" : "L'email d'activation a été envoyé avec succès !", + "resend-activation" : "Renvoyer l'activation", + "email" : "Email", + "email-required" : "L'email est requis.", + "invalid-email-format" : "Format d'email invalide.", + "first-name" : "Prénom", + "last-name" : "Nom", + "description" : "Description", + "default-dashboard" : "Tableau de bord par défaut", + "always-fullscreen" : "Toujours en plein écran", + "select-user" : "Sélectionner un utilisateur", + "no-users-matching" : "Aucun utilisateur correspondant à '{{entity}}' trouvé.", + "user-required" : "L'utilisateur est requis", + "activation-method" : "Méthode d'activation", + "display-activation-link" : "Afficher le lien d'activation", + "send-activation-mail" : "Envoyer l'email d'activation", + "activation-link" : "Lien d'activation utilisateur", + "activation-link-text" : "Pour activer l'utilisateur, utilisez le lien d'activation suivant (expire dans {{activationLinkTtl}}) :", + "copy-activation-link" : "Copier le lien d'activation", + "activation-link-copied-message" : "Le lien d'activation de l'utilisateur a été copié dans le presse-papiers", + "details" : "Détails", + "login-as-tenant-admin" : "Se connecter en tant qu'administrateur locataire", + "login-as-customer-user" : "Se connecter en tant qu'utilisateur client", + "search" : "Rechercher des utilisateurs", + "selected-users" : "{ count, plural, =1 {1 utilisateur} other {# utilisateurs} } sélectionné(s)", + "disable-account" : "Désactiver le compte utilisateur", + "enable-account" : "Activer le compte utilisateur", + "enable-account-message" : "Le compte utilisateur a été activé avec succès !", + "disable-account-message" : "Le compte utilisateur a été désactivé avec succès !", + "copyId" : "Copier l'identifiant de l'utilisateur", + "idCopiedMessage" : "L'identifiant de l'utilisateur a été copié dans le presse-papiers", + "user-list" : "Liste des utilisateurs", + "user-list-required" : "La liste des utilisateurs est requise" + }, + "value" : { + "type" : "Type de valeur", + "string" : "Chaîne", + "string-value" : "Valeur chaîne", + "string-value-required" : "La valeur chaîne est requise", + "integer" : "Entier", + "integer-value" : "Valeur entière", + "integer-value-required" : "La valeur entière est requise", + "invalid-integer-value" : "Valeur entière invalide", + "double" : "Double", + "double-value" : "Valeur double", + "double-value-required" : "La valeur double est requise", + "boolean" : "Booléen", + "boolean-value" : "Valeur booléenne", + "false" : "Faux", + "true" : "Vrai", + "long" : "Long", + "json" : "JSON", + "json-value" : "Valeur JSON", + "json-value-invalid" : "Le format de la valeur JSON est invalide", + "json-value-required" : "La valeur JSON est requise." + }, + "version-control" : { + "version-control" : "Contrôle de version", + "management" : "Gestion du contrôle de version", + "search" : "Rechercher des versions", + "branch" : "Branche", + "default" : "Par défaut", + "select-branch" : "Sélectionner une branche", + "branch-required" : "La branche est requise", + "create-entity-version" : "Créer une version d'entité", + "version-name" : "Nom de la version", + "version-name-required" : "Le nom de la version est requis", + "author" : "Auteur", + "export-relations" : "Exporter les relations", + "export-attributes" : "Exporter les attributs", + "export-credentials" : "Exporter les identifiants", + "export-calculated-fields" : "Exporter les champs calculés", + "entity-versions" : "Versions des entités", + "versions" : "Versions", + "created-time" : "Date de création", + "version-id" : "ID de version", + "no-entity-versions-text" : "Aucune version d'entité trouvée", + "no-versions-text" : "Aucune version trouvée", + "copy-full-version-id" : "Copier l'ID complet de la version", + "create-version" : "Créer une version", + "creating-version" : "Création de la version... Veuillez patienter", + "nothing-to-commit" : "Aucune modification à valider", + "restore-version" : "Restaurer la version", + "restore-entity-from-version" : "Restaurer l'entité depuis la version '{{versionName}}'", + "restoring-entity-version" : "Restauration de la version de l'entité... Veuillez patienter", + "load-relations" : "Charger les relations", + "load-attributes" : "Charger les attributs", + "load-credentials" : "Charger les identifiants", + "load-calculated-fields" : "Charger les champs calculés", + "compare-with-current" : "Comparer avec l'actuel", + "diff-entity-with-version" : "Comparer avec la version d'entité '{{versionName}}'", + "previous-difference" : "Différence précédente", + "next-difference" : "Différence suivante", + "current" : "Actuel", + "differences" : "{ count, plural, =1 {1 différence} other {# différences} }", + "create-entities-version" : "Créer une version d'entités", + "default-sync-strategy" : "Stratégie de synchronisation par défaut", + "sync-strategy-merge" : "Fusionner", + "sync-strategy-overwrite" : "Écraser", + "entities-to-export" : "Entités à exporter", + "entities-to-restore" : "Entités à restaurer", + "sync-strategy" : "Stratégie de synchronisation", + "all-entities" : "Toutes les entités", + "no-entities-to-export-prompt" : "Veuillez spécifier les entités à exporter", + "no-entities-to-restore-prompt" : "Veuillez spécifier les entités à restaurer", + "add-entity-type" : "Ajouter un type d'entité", + "remove-all" : "Tout supprimer", + "version-create-result" : "{ added, plural, =0 {Aucune entité} =1 {1 entité} other {# entités} } ajoutée(s).
{ modified, plural, =0 {Aucune entité} =1 {1 entité} other {# entités} } modifiée(s).
{ removed, plural, =0 {Aucune entité} =1 {1 entité} other {# entités} } supprimée(s).", + "remove-other-entities" : "Supprimer les autres entités", + "find-existing-entity-by-name" : "Trouver une entité existante par nom", + "restore-entities-from-version" : "Restaurer les entités depuis la version '{{versionName}}'", + "restoring-entities-from-version" : "Restauration des entités... Veuillez patienter", + "no-entities-restored" : "Aucune entité restaurée", + "created" : "{{created}} créée(s)", + "updated" : "{{updated}} mise(s) à jour", + "deleted" : "{{deleted}} supprimée(s)", + "remove-other-entities-confirm-text" : "Attention ! Cela supprimera de manière permanente toutes les entités actuelles
non présentes dans la version que vous souhaitez restaurer.

Veuillez taper \"remove other entities\" pour confirmer.", + "auto-commit-to-branch" : "auto-validation sur la branche {{ branch }}", + "default-create-entity-version-name" : "Mise à jour de {{entityName}}", + "sync-strategy-merge-hint" : "Crée ou met à jour les entités sélectionnées dans le dépôt. Toutes les autres entités du dépôt ne sont pas modifiées.", + "sync-strategy-overwrite-hint" : "Crée ou met à jour les entités sélectionnées dans le dépôt. Toutes les autres entités du dépôt sont supprimées.", + "device-credentials-conflict" : "Échec du chargement de l'appareil avec l'id externe {{entityId}}
car les mêmes identifiants sont déjà présents dans la base de données pour un autre appareil.
Veuillez envisager de désactiver l'option charger les identifiants dans le formulaire de restauration.", + "missing-referenced-entity" : "Échec du chargement du {{sourceEntityTypeName}} avec l'id externe {{sourceEntityId}}
car il fait référence à un {{targetEntityTypeName}} manquant avec l'id {{targetEntityId}}.", + "runtime-failed" : "Échec : {{message}}", + "auto-commit-settings-read-only-hint" : "La fonctionnalité d'auto-validation ne fonctionne pas avec l'option lecture seule activée dans les paramètres du dépôt.", + "rollback-on-error" : "Annuler en cas d’erreur", + "rollback-on-error-hint" : "Si vous avez un grand nombre d’entités à restaurer, envisagez de désactiver cette option pour améliorer les performances.\n Note : en cas d’erreur pendant le chargement de la version, les entités déjà persistées (avec relations, attributs, etc.) resteront telles quelles." + }, + "widget" : { + "widget-library" : "Bibliothèque de widgets", + "widget-bundle" : "Pack de widgets", + "all-bundles" : "Tous les packs", + "select-widgets-bundle" : "Sélectionner un pack de widgets", + "widgets" : "Widgets", + "all-widgets" : "Tous les widgets", + "widget" : "Widget", + "select-widget" : "Sélectionner un widget", + "no-widgets-matching" : "Aucun widget correspondant à '{{entity}}' trouvé.", + "no-widgets" : "Aucun widget pour l’instant", + "no-widgets-text" : "Aucun widget trouvé", + "management" : "Gestion des widgets", + "editor" : "Éditeur de widgets", + "confirm-to-exit-editor-html" : "Vous avez des paramètres de widget non enregistrés.
Êtes-vous sûr de vouloir quitter cette page ?", + "widget-type-not-found" : "Problème de chargement de la configuration du widget.
Le type de widget associé a probablement été supprimé.", + "widget-type-load-error" : "Le widget n’a pas été chargé en raison des erreurs suivantes :", + "remove" : "Supprimer le widget", + "delete" : "Supprimer le widget", + "edit" : "Modifier le widget", + "remove-widget-title" : "Êtes-vous sûr de vouloir supprimer le widget '{{widgetTitle}}' ?", + "remove-widget-text" : "Après confirmation, le widget et toutes les données associées seront irrécupérables.", + "replace-reference-with-widget-copy" : "Remplacer la référence par une copie du widget", + "timeseries" : "Séries temporelles", + "search-data" : "Rechercher des données", + "no-data-found" : "Aucune donnée trouvée", + "latest" : "Valeurs récentes", + "rpc" : "Widget de contrôle", + "alarm" : "Widget d'alarme", + "static" : "Widget statique", + "timeseries-short" : "séries", + "latest-short" : "récent", + "rpc-short" : "contrôle", + "alarm-short" : "alarme", + "static-short" : "statique", + "select-widget-type" : "Sélectionner le type de widget", + "missing-widget-title-error" : "Le titre du widget doit être spécifié !", + "widget-saved" : "Widget enregistré", + "unable-to-save-widget-error" : "Impossible d’enregistrer le widget ! Le widget contient des erreurs !", + "save" : "Enregistrer le widget", + "saveAs" : "Enregistrer le widget sous", + "move" : "Déplacer le widget", + "save-widget-as" : "Enregistrer le widget sous", + "save-widget-as-text" : "Veuillez saisir un nouveau titre pour le widget", + "toggle-fullscreen" : "Basculer en plein écran", + "run" : "Exécuter le widget", + "widget-title" : "Titre du widget", + "title" : "Titre", + "title-required" : "Le titre du widget est requis.", + "title-max-length" : "Le titre doit contenir moins de 256 caractères", + "system" : "Système", + "type" : "Type de widget", + "resources" : "Ressources", + "resource-url" : "URL JavaScript/CSS", + "resource-is-extension" : "Est une extension", + "remove-resource" : "Supprimer la ressource", + "add-resource" : "Ajouter une ressource", + "html" : "HTML", + "tidy" : "Nettoyer", + "css" : "CSS", + "settings-form" : "Formulaire de paramètres", + "data-key-settings-form" : "Formulaire de paramètres de clé de données", + "latest-data-key-settings-form" : "Formulaire de paramètres des dernières données", + "widget-settings" : "Paramètres du widget", + "description" : "Description", + "tags" : "Étiquettes", + "image-preview" : "Aperçu de l’image", + "settings-form-selector" : "Sélecteur de formulaire de paramètres", + "data-key-settings-form-selector" : "Sélecteur de formulaire de clé de données", + "latest-data-key-settings-form-selector" : "Sélecteur de formulaire des dernières données", + "all" : "Tous", + "actual" : "Actuel", + "scada" : "Symbole SCADA", + "deprecated" : "Obsolète", + "has-basic-mode" : "Mode basique disponible", + "basic-mode-form-selector" : "Sélecteur de formulaire du mode basique", + "basic-mode" : "Basique", + "advanced-mode" : "Avancé", + "javascript" : "Javascript", + "js" : "JS", + "delete-widget-title" : "Êtes-vous sûr de vouloir supprimer le widget '{{widgetName}}' ?", + "delete-widget-text" : "Après confirmation, le widget et toutes les données associées seront irrécupérables.", + "delete-widgets-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 widget} other {# widgets} } ?", + "delete-widgets-text" : "Attention, après confirmation tous les widgets sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", + "delete-widget" : "Supprimer le widget", + "widget-template-load-failed-error" : "Échec du chargement du modèle de widget !", + "details" : "Détails", + "widget-details" : "Détails du widget", + "add" : "Ajouter un widget", + "add-existing-widget" : "Ajouter un widget existant", + "add-new-widget" : "Ajouter un nouveau widget", + "search-widgets" : "Rechercher des widgets", + "selected-widgets" : "{ count, plural, =1 {1 widget} other {# widgets} } sélectionné(s)", + "undo" : "Annuler les modifications du widget", + "export" : "Exporter le widget", + "export-prompt" : "Intégrer les images et ressources du widget", + "export-widgets" : "Exporter les widgets", + "export-widgets-prompt" : "Intégrer les images et ressources des widgets", + "import" : "Importer un widget", + "no-data" : "Aucune donnée à afficher sur le widget", + "data-overflow" : "Le widget affiche {{count}} sur {{total}} entités", + "alarm-data-overflow" : "Le widget affiche les alarmes pour {{allowedEntities}} entités (maximum autorisé) sur un total de {{totalEntities}} entités", + "search" : "Rechercher un widget", + "filter" : "Type de filtre de widget", + "loading-widgets" : "Chargement des widgets...", + "widget-template-error" : "Modèle HTML du widget invalide.", + "reference" : "Référence" + }, + "widget-action" : { + "header-button" : "Bouton d'en-tête du widget", + "do-nothing" : "Ne rien faire", + "open-dashboard-state" : "Naviguer vers un nouvel état du tableau de bord", + "update-dashboard-state" : "Mettre à jour l'état actuel du tableau de bord", + "open-dashboard" : "Naviguer vers un autre tableau de bord", + "custom" : "Action personnalisée", + "custom-pretty" : "Action personnalisée (avec modèle HTML)", + "custom-pretty-error-title" : "Erreur de dialogue personnalisé", + "custom-pretty-template-error" : "Modèle de dialogue personnalisé invalide.", + "custom-pretty-controller-error" : "Une erreur s'est produite lors de l'évaluation de la fonction de dialogue personnalisé.", + "mobile-action" : "Action mobile", + "target-dashboard-state" : "État cible du tableau de bord", + "target-dashboard-state-required" : "L'état cible du tableau de bord est requis", + "set-entity-from-widget" : "Définir l'entité à partir du widget", + "target-dashboard" : "Tableau de bord cible", + "select-target-dashboard" : "Sélectionner un tableau de bord cible", + "target-dashboard-required" : "Le tableau de bord cible est requis.", + "open-right-layout" : "Ouvrir la disposition droite du tableau de bord (vue mobile)", + "state-display-type" : "Option d'affichage de l'état du tableau de bord", + "open-normal" : "Normal", + "open-in-separate-dialog" : "Ouvrir dans une boîte de dialogue séparée", + "open-in-popover" : "Ouvrir dans une infobulle", + "dialog-title" : "Titre de la boîte de dialogue", + "dialog-hide-dashboard-toolbar" : "Masquer la barre d'outils du tableau de bord dans la boîte de dialogue", + "dialog-width" : "Largeur de la boîte de dialogue en pourcentage de la largeur de l'écran", + "dialog-height" : "Hauteur de la boîte de dialogue en pourcentage de la hauteur de l'écran", + "dialog-size-range-error" : "La taille de la boîte de dialogue doit être comprise entre 1 et 100 %.", + "popover-preferred-placement" : "Placement préféré de l'infobulle", + "popover-placement-top" : "Haut", + "popover-placement-topLeft" : "Haut gauche", + "popover-placement-topRight" : "Haut droit", + "popover-placement-right" : "Droite", + "popover-placement-rightTop" : "Haut droite", + "popover-placement-rightBottom" : "Bas droite", + "popover-placement-bottom" : "Bas", + "popover-placement-bottomLeft" : "Bas gauche", + "popover-placement-bottomRight" : "Bas droite", + "popover-placement-left" : "Gauche", + "popover-placement-leftTop" : "Haut gauche", + "popover-placement-leftBottom" : "Bas gauche", + "popover-hide-on-click-outside" : "Masquer l'infobulle en cliquant à l'extérieur", + "popover-hide-dashboard-toolbar" : "Masquer la barre d'outils du tableau de bord dans l'infobulle", + "popover-width" : "Largeur de l'infobulle", + "popover-height" : "Hauteur de l'infobulle", + "popover-style" : "Style de l'infobulle", + "open-new-browser-tab" : "Ouvrir dans un nouvel onglet", + "open-URL" : "Ouvrir une URL", + "URL" : "URL", + "url-required" : "L'URL est requise.", + "mobile" : { + "device-provision" : "Provisionnement de l'appareil", + "action-type" : "Type d'action mobile", + "select-action-type" : "Sélectionner le type d'action mobile", + "action-type-required" : "Le type d'action mobile est requis", + "take-picture-from-gallery" : "Prendre une photo depuis la galerie", + "take-photo" : "Prendre une photo", + "map-direction" : "Ouvrir les directions sur la carte", + "map-location" : "Ouvrir l'emplacement sur la carte", + "scan-qr-code" : "Scanner le code QR", + "make-phone-call" : "Passer un appel téléphonique", + "get-location" : "Obtenir la localisation du téléphone", + "take-screenshot" : "Faire une capture d'écran" + }, + "custom-action-function" : "Fonction d'action personnalisée", + "custom-pretty-function" : "Fonction d'action personnalisée (avec modèle HTML)", + "map-item-type" : "Type d'élément cartographique", + "map-item" : { + "marker" : "Marqueur", + "polygon" : "Polygone", + "rectangle" : "Rectangle", + "circle" : "Cercle" + }, + "place-map-item" : "Placer un élément cartographique", + "map-item-tooltip" : { + "customize-map-item-tooltips" : "Personnaliser les infobulles des éléments cartographiques", + "place-marker" : "Placer un marqueur", + "start-draw-rectangle" : "Commencer à dessiner un rectangle", + "finish-draw-rectangle" : "Terminer le dessin du rectangle", + "start-draw-polygon" : "Commencer à dessiner un polygone", + "continue-draw-polygon" : "Continuer le dessin du polygone", + "finish-draw-polygon" : "Terminer le dessin du polygone", + "start-draw-circle" : "Commencer à dessiner un cercle", + "finish-draw-circle" : "Terminer le dessin du cercle" + } + }, + "widgets-bundle" : { + "current" : "Bundle actuel", + "widgets-bundles" : "Bundles de widgets", + "widgets-bundle-widgets" : "Widgets du bundle", + "add" : "Ajouter un bundle de widgets", + "delete" : "Supprimer le bundle de widgets", + "title" : "Titre", + "title-required" : "Le titre est requis.", + "title-max-length" : "Le titre doit contenir moins de 256 caractères", + "description" : "Description", + "image-preview" : "Aperçu de l'image", + "scada" : "Bundle de widgets SCADA", + "order" : "Ordre", + "add-widgets-bundle-text" : "Ajouter un nouveau bundle de widgets", + "no-widgets-bundles-text" : "Aucun bundle de widgets trouvé", + "empty" : "Le bundle de widgets est vide", + "details" : "Détails", + "widgets-bundle-details" : "Détails du bundle de widgets", + "delete-widgets-bundle-title" : "Êtes-vous sûr de vouloir supprimer le bundle de widgets '{{widgetsBundleTitle}}' ?", + "delete-widgets-bundle-text" : "Attention, après confirmation, le bundle et toutes ses données associées seront définitivement supprimés.", + "delete-widgets-bundles-title" : "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 bundle de widgets} other {# bundles de widgets} } ?", + "delete-widgets-bundles-action-title" : "Supprimer { count, plural, =1 {1 bundle de widgets} other {# bundles de widgets} }", + "delete-widgets-bundles-text" : "Attention, après confirmation, tous les bundles sélectionnés seront supprimés ainsi que toutes les données associées.", + "no-widgets-bundles-matching" : "Aucun bundle de widgets correspondant à '{{widgetsBundle}}' trouvé.", + "widgets-bundle-required" : "Le bundle de widgets est requis.", + "system" : "Système", + "import" : "Importer un bundle de widgets", + "export" : "Exporter un bundle de widgets", + "export-widgets-bundle-widgets-prompt" : "Inclure les widgets du bundle dans les données exportées (sinon seuls les FQN de widgets référencés seront exportés)", + "export-failed-error" : "Impossible d’exporter le bundle de widgets : {{error}}", + "create-new-widgets-bundle" : "Créer un nouveau bundle de widgets", + "widgets-bundle-file" : "Fichier du bundle de widgets", + "invalid-widgets-bundle-file-error" : "Impossible d'importer le bundle de widgets : structure de données invalide.", + "search" : "Rechercher des bundles de widgets", + "selected-widgets-bundles" : "{ count, plural, =1 {1 bundle de widgets} other {# bundles de widgets} } sélectionné(s)", + "open-widgets-bundle" : "Ouvrir le bundle de widgets", + "loading-widgets-bundles" : "Chargement des bundles de widgets...", + "create-new" : "Créer un nouveau bundle de widgets" + }, + "widget-config" : { + "data" : "Données", + "settings" : "Paramètres", + "advanced" : "Avancé", + "appearance" : "Apparence", + "widget-card" : "Carte du widget", + "mobile" : "Mobile", + "title" : "Titre", + "title-tooltip" : "Info-bulle du titre", + "general-settings" : "Paramètres généraux", + "display-title" : "Afficher le titre du widget", + "card-title" : "Titre de la carte", + "drop-shadow" : "Ombre portée", + "enable-fullscreen" : "Activer le mode plein écran", + "background-color" : "Couleur de fond", + "text-color" : "Couleur du texte", + "border-radius" : "Rayon de la bordure", + "padding" : "Espacement interne (padding)", + "margin" : "Marge", + "widget-style" : "Style du widget", + "widget-css" : "CSS du widget", + "title-style" : "Style du titre", + "mobile-mode-settings" : "Mode mobile", + "order" : "Ordre", + "height" : "Hauteur", + "mobile-hide" : "Masquer le widget en mode mobile", + "desktop-hide" : "Masquer le widget en mode bureau", + "units" : "Symbole spécial à afficher à côté de la valeur", + "units-by-default" : "Unités par défaut", + "decimals" : "Nombre de chiffres après la virgule", + "decimals-by-default" : "Décimales par défaut", + "default-data-key-parameter-hint" : "Ce paramètre s’applique à toutes les valeurs du widget sauf si redéfini par la configuration de la clé de données", + "units-short" : "Unités", + "decimals-short" : "Décimales", + "decimals-suffix" : "décimales", + "digits-suffix" : "chiffres", + "timewindow" : "Fenêtre temporelle", + "use-dashboard-timewindow" : "Utiliser la fenêtre temporelle du tableau de bord", + "use-widget-timewindow" : "Utiliser la fenêtre temporelle du widget", + "display-timewindow" : "Afficher la fenêtre temporelle", + "legend" : "Légende", + "display-legend" : "Afficher la légende", + "datasources" : "Sources de données", + "datasource" : "Source de données", + "maximum-datasources" : "Maximum { count, plural, =1 {1 source de données autorisée.} other {# sources de données autorisées} }", + "timeseries-key-error" : "Au moins une clé de série temporelle doit être spécifiée", + "datasource-type" : "Type", + "datasource-parameters" : "Paramètres", + "remove-datasource" : "Supprimer la source de données", + "add-datasource" : "Ajouter une source de données", + "target-device" : "Appareil cible", + "alarm-source" : "Source d'alarme", + "actions" : "Actions", + "action" : "Action", + "add-action" : "Ajouter une action", + "search-actions" : "Rechercher des actions", + "no-actions-text" : "Aucune action trouvée", + "action-source" : "Source de l'action", + "select-action-source" : "Sélectionner la source de l'action", + "action-source-required" : "La source de l'action est requise.", + "column-index" : "Index de la colonne", + "select-column-index" : "Sélectionner l'index de la colonne", + "column-index-required" : "L’index de colonne est requis.", + "not-set" : "Non défini", + "action-name" : "Nom", + "action-name-required" : "Le nom de l’action est requis.", + "action-name-not-unique" : "Une autre action avec le même nom existe déjà.\nLe nom de l’action doit être unique pour une même source.", + "action-icon" : "Icône", + "header-button" : { + "button-settings" : "Paramètres du bouton", + "button-type" : "Type de bouton", + "button-type-basic" : "Basique", + "button-type-raised" : "Surélevé", + "button-type-stroked" : "Avec contour", + "button-type-flat" : "Plat", + "button-type-icon" : "Icône", + "button-type-mini-fab" : "FAB", + "colors" : "Couleurs", + "color" : "Couleur", + "background" : "Arrière-plan", + "border" : "Bordure", + "advanced-button-style" : "Style de bouton avancé", + "button-style" : "Style du bouton" + }, + "show-hide-action-using-function" : "Afficher/masquer l’action via une fonction", + "show-action-function" : "Fonction d’affichage de l’action", + "action-type" : "Type", + "action-type-required" : "Le type d’action est requis.", + "edit-action" : "Modifier l’action", + "delete-action" : "Supprimer l’action", + "delete-action-title" : "Supprimer l’action du widget", + "delete-action-text" : "Êtes-vous sûr de vouloir supprimer l’action du widget nommée '{{actionName}}' ?", + "title-icon" : "Icône du titre", + "display-icon" : "Afficher l’icône du titre", + "card-icon" : "Icône de la carte", + "icon" : "Icône", + "icon-color" : "Couleur de l’icône", + "icon-size" : "Taille de l’icône", + "advanced-settings" : "Paramètres avancés", + "data-settings" : "Paramètres de données", + "limits" : "Limites", + "no-data-display-message" : "Message alternatif pour « Aucune donnée à afficher »", + "data-page-size" : "Nombre maximal d'entités par source de données", + "settings-component-not-found" : "Composant du formulaire de paramètres introuvable pour le sélecteur '{{selector}}'", + "preview" : "Aperçu", + "set" : "Définir", + "set-message" : "Définir le message", + "advanced-title-style" : "Style avancé du titre", + "card-style" : "Style de la carte", + "text" : "Texte", + "background" : "Arrière-plan", + "advanced-widget-style" : "Style avancé du widget", + "card-buttons" : "Boutons de la carte", + "show-card-buttons" : "Afficher les boutons de la carte", + "card-border-radius" : "Rayon de bordure de la carte", + "card-padding" : "Espacement interne de la carte", + "card-appearance" : "Apparence de la carte", + "color" : "Couleur", + "tooltip" : "Info-bulle", + "units-required" : "L’unité est requise.", + "list-layout" : "Disposition en liste", + "layout" : "Disposition", + "resize-options" : "Options de redimensionnement", + "resizable" : "Redimensionnable", + "preserve-aspect-ratio" : "Préserver les proportions" + }, + "widget-type" : { + "import" : "Importer le type de widget", + "export" : "Exporter le type de widget", + "export-failed-error" : "Impossible d’exporter le widget : {{error}}", + "widget-file" : "Fichier de widget", + "invalid-widget-file-error" : "Impossible d’importer le widget : structure de données invalide." + }, + "markdown" : { + "edit" : "Éditer", + "preview" : "Aperçu", + "copy-code" : "Cliquez pour copier", + "copied" : "Copié !" + }, + "widgets" : { + "mobile-app-qr-code" : { + "configuration-hint" : "La configuration dépend du widget de code QR de l'application mobile dans les paramètres principaux de la plateforme", + "get-it-on-google-play" : "Disponible sur Google Play", + "download-on-the-app-store" : "Télécharger sur l'App Store" + }, + "action-button" : { + "behavior" : "Comportement", + "on-click" : "Au clic", + "on-click-hint" : "Action déclenchée lors du clic sur le bouton", + "first-button-click" : "Premier clic sur le bouton", + "first-button-click-hint" : "Action lors de l’appui sur le premier bouton.", + "second-button-click" : "Deuxième clic sur le bouton", + "second-button-click-hint" : "Action lors de l’appui sur le deuxième bouton.", + "button-click-hint" : "Action lors de l’appui sur le widget." + }, + "command-button" : { + "behavior" : "Comportement", + "on-click" : "Au clic", + "on-click-hint" : "Action effectuée lors du clic sur le bouton." + }, + "power-button" : { + "behavior" : "Comportement", + "power-on" : "Allumer", + "power-on-hint" : "Action effectuée pour allumer le composant.", + "power-off" : "Éteindre", + "power-off-hint" : "Action effectuée pour éteindre le composant.", + "on-label" : "Marche", + "off-label" : "Arrêt", + "layout" : "Disposition", + "layout-default" : "Par défaut", + "layout-simplified" : "Simplifié", + "layout-outlined" : "Contour", + "layout-default-volume" : "Par défaut.Volume", + "layout-simplified-volume" : "Simplifié.Volume", + "layout-outlined-volume" : "Contour.Volume", + "layout-default-icon" : "Par défaut.Icône", + "layout-simplified-icon" : "Simplifié.Icône", + "layout-outlined-icon" : "Contour.Icône", + "main" : "Principal", + "background" : "Arrière-plan", + "button-icon-on" : "Icône du bouton 'Marche'", + "button-icon-off" : "Icône du bouton 'Arrêt'", + "power-on-colors" : "Couleurs 'Marche'", + "power-off-colors" : "Couleurs 'Arrêt'", + "disabled-colors" : "Couleurs désactivées", + "button" : "Bouton" + }, + "toggle-button" : { + "behavior" : "Comportement", + "checked" : "Coché", + "unchecked" : "Décoché", + "check" : "Cocher", + "check-hint" : "Action effectuée pour cocher le composant.", + "uncheck" : "Décocher", + "uncheck-hint" : "Action effectuée pour décocher le composant.", + "auto-scale" : "Redimensionnement automatique", + "horizontal-fill" : "Remplissage horizontal", + "vertical-fill" : "Remplissage vertical", + "button-appearance" : "Apparence du bouton" + }, + "segmented-button" : { + "layout" : "Disposition", + "layout-squared" : "Carrée", + "layout-rounded" : "Arrondie", + "card-border" : "Bordure de carte", + "button-appearance" : "Apparence du bouton", + "first" : "Premier", + "second" : "Deuxième", + "color-styles" : "Styles de couleur", + "selected" : "Sélectionné", + "unselected" : "Non sélectionné" + }, + "button" : { + "layout" : "Disposition", + "outlined" : "Contour", + "filled" : "Rempli", + "underlined" : "Souligné", + "basic" : "Basique", + "auto-scale" : "Redimensionnement automatique", + "label" : "Libellé", + "icon" : "Icône", + "border-radius" : "Rayon de bordure", + "color-palette" : "Palette de couleurs", + "main" : "Principal", + "background" : "Arrière-plan", + "border" : "Bordure", + "custom-styles" : "Styles personnalisés", + "clear-style" : "Effacer le style", + "shadow" : "Ombre", + "enabled" : "Activé", + "disabled" : "Désactivé", + "preview" : "Aperçu", + "copy-style-from" : "Copier le style de" + }, + "value-stepper" : { + "behavior" : "Comportement", + "simplified" : "Simplifié", + "filled" : "Rempli", + "outlined" : "Contour", + "volume" : "Volume", + "initial-state" : "État initial", + "initial-state-hint" : "Action pour obtenir la valeur initiale.", + "disabled-state" : "État désactivé", + "disabled-state-hint" : "Configurer la condition de désactivation du composant.", + "right-button-click" : "Clic bouton droit", + "right-button-click-hint" : "Action lors de l’appui sur le bouton droit.", + "left-button-click" : "Clic bouton gauche", + "left-button-click-hint" : "Action lors de l’appui sur le bouton gauche.", + "auto-scale" : "Redimensionnement automatique", + "value-range" : "Plage de valeurs", + "min-range" : "Min", + "max-range" : "Max", + "value-increment-decrement-step" : "Pas d'incrément/décrément", + "value" : "Valeur", + "value-box-background" : "Arrière-plan de la boîte de valeur", + "border" : "Bordure", + "button-appearance" : "Apparence du bouton", + "left" : "Gauche", + "right" : "Droite", + "left-button" : "Bouton gauche", + "right-button" : "Bouton droit", + "icon" : "Icône", + "color-palette" : "Palette de couleurs", + "main" : "Principal", + "background" : "Arrière-plan", + "button-icon-on" : "Icône du bouton 'Marche'", + "button-on-colors" : "Couleurs 'Marche'", + "disabled-colors" : "Couleurs désactivées" + }, + "button-state" : { + "activated-state" : "État activé", + "activated-state-hint" : "Configurer la condition d’activation du bouton.", + "disabled-state" : "État désactivé", + "disabled-state-hint" : "Configurer la condition de désactivation du bouton.", + "selected-state" : "Sélection du bouton", + "selected-state-hint" : "Configurer la condition de sélection du bouton.", + "enabled" : "Activé", + "hovered" : "Survolé", + "pressed" : "Pressé", + "activated" : "Activé", + "disabled" : "Désactivé", + "initial" : "Premier bouton", + "first" : "Premier", + "second" : "Deuxième" + }, + "background" : { + "background" : "Arrière-plan", + "background-settings" : "Paramètres de l'arrière-plan", + "background-type-image" : "Image", + "background-type-color" : "Couleur", + "image-url" : "URL de l'image", + "overlay" : "Superposition", + "enable-overlay" : "Activer la superposition", + "blur" : "Flou", + "preview" : "Aperçu" }, - "aggregation": { - "aggregation": "agrégation", - "avg": "Moyenne", - "count": "Compte", - "function": "Fonction d'agrégation de données", - "group-interval": "Intervalle de regroupement", - "limit": "Valeurs maximales", - "max": "Max", - "min": "Min", - "none": "Aucune", - "sum": "Somme" - }, - "alarm": { - "ack-time": "Heure d'acquittement", - "acknowledge": "Acquitter", - "aknowledge-alarm-text": "Êtes-vous sûr de vouloir reconnaître l'alarme?", - "aknowledge-alarm-title": "Reconnaître l'alarme", - "aknowledge-alarms-text": "Êtes-vous sûr de vouloir acquitter { count, plural, =1 {1 alarme} other {# alarmes} }?", - "aknowledge-alarms-title": "Acquitter { count, plural, =1 {1 alarme} other {# alarmes} }", - "alarm": "Alarme", - "alarm-details": "Détails de l'alarme", - "alarm-required": "Une alarme est requise", - "alarm-status": "État d'alarme", - "alarm-status-filter": "Filtre d'état d'alarme", - "alarms": "Alarmes", - "clear": "Effacer", - "clear-alarm-text": "Êtes-vous sûr de vouloir effacer l'alarme?", - "clear-alarm-title": "Effacer l'alarme", - "clear-alarms-text": "Êtes-vous sûr de vouloir effacer {count, plural, =1 {1 alarme} other {# alarmes} }?", - "clear-alarms-title": "Effacer {count, plural, =1 {1 alarme} other {# alarmes} }", - "clear-time": "Heure d'éffacement", - "created-time": "Heure de création", - "details": "Détails", - "display-status": { - "ACTIVE_ACK": "Active acquittée", - "ACTIVE_UNACK": "Active non acquittée", - "CLEARED_ACK": "effacée acquittée", - "CLEARED_UNACK": "effacée non acquittée" + "bar-chart" : { + "bar-appearance" : "Apparence de la barre", + "label-on-bar" : "Étiquette sur la barre", + "value-on-bar" : "Valeur sur la barre", + "bar-chart-style" : "Style du diagramme à barres", + "bar-axis" : "Axe de la barre" + }, + "polar-area-chart" : { + "polar-axis" : "Axe polaire", + "start-angle" : "Angle de départ", + "polar-area-chart-style" : "Style du diagramme en aires polaires" + }, + "battery-level" : { + "layout" : "Disposition", + "layout-vertical-solid" : "Vertical. Plein", + "layout-horizontal-solid" : "Horizontal. Plein", + "layout-vertical-divided" : "Vertical. Divisé", + "layout-horizontal-divided" : "Horizontal. Divisé", + "icon" : "Icône", + "value" : "Valeur", + "auto-scale" : "Redimensionnement automatique", + "battery-level-color" : "Couleur du niveau de batterie", + "battery-shape-color" : "Couleur de la forme de la batterie", + "battery-level-card-style" : "Style de la carte du niveau de batterie", + "sections-count" : "Nombre de sections" + }, + "signal-strength" : { + "value" : "Valeur", + "last-update" : "Dernière mise à jour", + "no-signal" : "Pas de signal", + "layout" : "Disposition", + "layout-wifi" : "Wi-Fi", + "layout-cellular-bar" : "Barre cellulaire", + "icon" : "Icône", + "date" : "Date", + "active-bars-color" : "Couleur des barres actives", + "inactive-bars-color" : "Couleur des barres inactives", + "signal-strength-card-style" : "Style de la carte de signal", + "no-signal-rssi-value" : "Valeur RSSI pour \"Pas de signal\"" + }, + "status-widget" : { + "behavior" : "Comportement", + "layout" : "Disposition", + "layout-default" : "Défaut", + "layout-center" : "Centré", + "layout-icon" : "Icône", + "on" : "Activé", + "off" : "Désactivé", + "label" : "Libellé", + "status" : "Statut", + "icon" : "Icône", + "color-palette" : "Palette de couleurs", + "disabled-color-palette" : "Palette de couleurs désactivée", + "primary" : "Principal", + "primary-color-hint" : "Couleur de l’icône et du libellé", + "secondary" : "Secondaire", + "secondary-color-hint" : "Couleur du statut", + "background" : "Arrière-plan" + }, + "chart" : { + "common-settings" : "Paramètres communs", + "enable-stacking-mode" : "Activer le mode empilé", + "selection" : "Sélection de la plage temporelle", + "enable-selection-mode" : "Activer le mode de sélection", + "line-shadow-size" : "Taille de l’ombre de la ligne", + "display-smooth-lines" : "Afficher les lignes lisses (courbes)", + "default-bar-width" : "Largeur de barre par défaut pour les données non agrégées (en millisecondes)", + "bar-alignment" : "Alignement des barres", + "bar-alignment-left" : "Gauche", + "bar-alignment-right" : "Droite", + "bar-alignment-center" : "Centre", + "default-font" : "Police par défaut", + "default-font-size" : "Taille de police par défaut", + "default-font-color" : "Couleur de police par défaut", + "thresholds-line-width" : "Épaisseur de ligne par défaut pour tous les seuils", + "tooltip-settings" : "Paramètres de l'infobulle", + "tooltip" : "Infobulle", + "show-tooltip" : "Afficher l’infobulle", + "hover-individual-points" : "Survoler les points individuels", + "show-cumulative-values" : "Afficher les valeurs cumulées en mode empilé", + "hide-zero-false-values" : "Masquer les valeurs nulles/faux de l’infobulle", + "tooltip-value-format-function" : "Fonction de formatage des valeurs de l’infobulle", + "grid-settings" : "Paramètres de la grille", + "show-vertical-lines" : "Afficher les lignes verticales", + "show-horizontal-lines" : "Afficher les lignes horizontales", + "grid-outline-border-width" : "Épaisseur du contour/de la bordure de la grille (px)", + "primary-color" : "Couleur principale", + "background-color" : "Couleur d’arrière-plan", + "ticks-color" : "Couleur des graduations", + "xaxis-settings" : "Paramètres de l’axe X", + "axis-title" : "Titre de l’axe", + "xaxis-tick-labels-settings" : "Paramètres des étiquettes de graduation de l’axe X", + "show-tick-labels" : "Afficher les étiquettes des graduations", + "yaxis-settings" : "Paramètres de l’axe Y", + "min-scale-value" : "Valeur minimale de l’échelle", + "max-scale-value" : "Valeur maximale de l’échelle", + "yaxis-tick-labels-settings" : "Paramètres des étiquettes de graduation de l’axe Y", + "tick-step-size" : "Pas entre les graduations", + "number-of-decimals" : "Nombre de décimales à afficher", + "ticks-formatter-function" : "Fonction de formatage des graduations", + "comparison-settings" : "Paramètres de comparaison", + "enable-comparison" : "Activer la comparaison", + "time-for-comparison" : "Période de comparaison", + "time-for-comparison-previous-interval" : "Intervalle précédent (par défaut)", + "time-for-comparison-days" : "Il y a un jour", + "time-for-comparison-weeks" : "Il y a une semaine", + "time-for-comparison-months" : "Il y a un mois", + "time-for-comparison-years" : "Il y a un an", + "time-for-comparison-custom-interval" : "Intervalle personnalisé", + "custom-interval-value" : "Valeur d’intervalle personnalisé (ms)", + "comparison-x-axis-settings" : "Paramètres de l’axe X pour la comparaison", + "axis-position" : "Position de l’axe", + "axis-position-top" : "En haut (par défaut)", + "axis-position-bottom" : "En bas", + "custom-legend-settings" : "Paramètres personnalisés de la légende", + "enable-custom-legend" : "Activer la légende personnalisée (permet d'utiliser des attributs/valeurs de séries temporelles dans les libellés)", + "key-name" : "Nom de la clé", + "key-name-required" : "Le nom de la clé est requis", + "key-type" : "Type de clé", + "key-type-attribute" : "Attribut", + "key-type-timeseries" : "Série temporelle", + "label-keys-list" : "Liste des clés à utiliser dans les libellés", + "no-label-keys" : "Aucune clé configurée", + "add-label-key" : "Ajouter une nouvelle clé", + "line-width" : "Épaisseur de la ligne", + "color" : "Couleur", + "data-is-hidden-by-default" : "Les données sont masquées par défaut", + "disable-data-hiding" : "Désactiver le masquage des données", + "remove-from-legend" : "Supprimer la clé de la légende", + "exclude-from-stacking" : "Exclure de l'empilement (disponible en mode \"Empilé\")", + "line-settings" : "Paramètres de la ligne", + "show-line" : "Afficher la ligne", + "fill-line" : "Remplir la ligne", + "fill-line-opacity" : "Opacité du remplissage", + "points-settings" : "Paramètres des points", + "show-points" : "Afficher les points", + "points-line-width" : "Épaisseur de la ligne des points", + "points-radius" : "Rayon des points", + "point-shape" : "Forme du point", + "point-shape-circle" : "Cercle", + "point-shape-cross" : "Croix", + "point-shape-diamond" : "Losange", + "point-shape-square" : "Carré", + "point-shape-triangle" : "Triangle", + "point-shape-custom" : "Fonction personnalisée", + "point-shape-draw-function" : "Fonction de dessin de la forme du point", + "show-separate-axis" : "Afficher un axe séparé", + "axis-position-left" : "Gauche", + "axis-position-right" : "Droite", + "thresholds" : "Seuils", + "no-thresholds" : "Aucun seuil configuré", + "add-threshold" : "Ajouter un seuil", + "show-values-for-comparison" : "Afficher les valeurs historiques pour la comparaison", + "comparison-values-label" : "Étiquette des valeurs historiques", + "comparison-line-color" : "Couleur de la ligne de comparaison", + "threshold-settings" : "Paramètres des seuils", + "use-as-threshold" : "Utiliser la valeur de la clé comme seuil", + "threshold-line-width" : "Épaisseur de la ligne du seuil", + "threshold-color" : "Couleur du seuil", + "common-pie-settings" : "Paramètres communs du diagramme circulaire", + "radius" : "Rayon", + "inner-radius" : "Rayon intérieur", + "tilt" : "Inclinaison", + "common-pie-settings-range-error" : "La valeur doit être comprise entre 0 et 1", + "stroke-settings" : "Paramètres du contour", + "width-pixels" : "Largeur (pixels)", + "show-labels" : "Afficher les étiquettes", + "animation-settings" : "Paramètres d'animation", + "animated-pie" : "Activer l’animation du diagramme (expérimental)", + "border-settings" : "Paramètres de bordure", + "border-width" : "Épaisseur de la bordure", + "border-color" : "Couleur de la bordure", + "legend-settings" : "Paramètres de la légende", + "display-legend" : "Afficher la légende", + "labels-font-color" : "Couleur de la police des étiquettes", + "series" : "Séries", + "add-series" : "Ajouter une série", + "series-settings" : "Paramètres de la série", + "remove-series" : "Supprimer la série", + "no-series" : "Aucune série configurée", + "no-series-error" : "Au moins une série doit être spécifiée", + "chart-appearance" : "Apparence du graphique", + "vertical-grid-lines" : "Lignes de la grille verticale", + "horizontal-grid-lines" : "Lignes de la grille horizontale", + "chart-background" : "Arrière-plan du graphique", + "grid-lines-color" : "Couleur des lignes de la grille", + "border" : "Bordure", + "axis" : "Axe", + "vertical-axis" : "Axe vertical", + "ticks" : "Graduations", + "horizontal-axis" : "Axe horizontal", + "shape-empty-circle" : "Cercle vide", + "shape-circle" : "Cercle", + "shape-rect" : "Rectangle", + "shape-round-rect" : "Rectangle arrondi", + "shape-triangle" : "Triangle", + "shape-diamond" : "Losange", + "shape-pin" : "Épingle", + "shape-arrow" : "Flèche", + "shape-none" : "Aucune", + "line-type-solid" : "Solide", + "line-type-dashed" : "Tiretée", + "line-type-dotted" : "Pointillée", + "label-position-top" : "Haut", + "label-position-bottom" : "Bas", + "label-position-outside" : "Extérieur", + "label-position-inside" : "Intérieur", + "fill" : "Remplissage", + "fill-type-none" : "Aucun", + "fill-type-solid" : "Solide", + "fill-type-opacity" : "Opacité", + "fill-type-gradient" : "Dégradé", + "background" : "Arrière-plan", + "opacity" : "Opacité", + "gradient-stops" : "Points d’arrêt du dégradé", + "gradient-start" : "début", + "gradient-end" : "fin", + "animation" : { + "animation" : "Animation", + "animation-threshold" : "Seuil d’animation", + "animation-duration" : "Durée de l’animation", + "animation-easing" : "Courbe d’animation", + "animation-delay" : "Délai de l’animation", + "update-animation-duration" : "Durée de l’animation de mise à jour", + "update-animation-easing" : "Courbe de l’animation de mise à jour", + "update-animation-delay" : "Délai de l’animation de mise à jour" + }, + "chart-axis" : { + "scale" : "Échelle", + "scale-min" : "min", + "scale-max" : "max", + "scale-auto" : "Auto" + }, + "bar" : { + "show-border" : "Afficher la bordure", + "border-width" : "Épaisseur de la bordure", + "border-radius" : "Rayon de la bordure", + "bar-width" : "Largeur de la barre", + "label" : "Étiquette", + "label-hint" : "Afficher l’étiquette au-dessus de la barre.", + "series-label-hint" : "Afficher l’étiquette avec la valeur au-dessus de la barre.", + "label-background" : "Arrière-plan de l’étiquette" + } + }, + "color" : { + "color-settings" : "Paramètres de couleur", + "color-type-constant" : "Constante", + "color-type-gradient" : "Dégradé", + "color-type-range" : "Plage", + "color-type-function" : "Fonction", + "color" : "Couleur", + "value-range" : "Plage de valeurs", + "from" : "De", + "to" : "À", + "color-function" : "Fonction de couleur", + "copy-color-settings-from" : "Copier les paramètres de couleur depuis", + "copy-from" : "Copier depuis", + "settings-type" : "Type de paramètres", + "basic-mode" : "Basique", + "advanced-mode" : "Avancé", + "entity-alias" : "Alias d'entité", + "entity-attribute" : "Attribut d'entité", + "gradient-color" : "Couleur du dégradé", + "gradient-color-min" : "Couleur", + "gradient-start" : "Couleur de début du dégradé", + "gradient-start-min" : "Début", + "gradient-end" : "Couleur de fin du dégradé", + "gradient-end-min" : "Fin", + "start-value" : "Valeur de départ", + "end-value" : "Valeur de fin", + "gradient-type" : "Type de dégradé" + }, + "dashboard-state" : { + "dashboard-state-settings" : "Paramètres de l'état du tableau de bord", + "dashboard-state" : "ID de l'état du tableau de bord", + "autofill-state-layout" : "Remplir automatiquement la hauteur de la disposition de l'état par défaut", + "default-margin" : "Marge par défaut des widgets", + "default-background-color" : "Couleur de fond par défaut", + "sync-parent-state-params" : "Synchroniser les paramètres d'état avec le tableau de bord parent" + }, + "date-range-navigator" : { + "date-range-picker-settings" : "Paramètres du sélecteur de plage de dates", + "hide-date-range-picker" : "Masquer le sélecteur de plage de dates", + "picker-one-panel" : "Sélecteur de plage de dates à un panneau", + "picker-auto-confirm" : "Confirmation automatique du sélecteur de plage de dates", + "picker-show-template" : "Afficher le modèle de sélecteur de plage de dates", + "first-day-of-week" : "Premier jour de la semaine", + "interval-settings" : "Paramètres d'intervalle", + "hide-interval" : "Masquer l'intervalle", + "initial-interval" : "Intervalle initial", + "interval-hour" : "Heure", + "interval-day" : "Jour", + "interval-week" : "Semaine", + "interval-two-weeks" : "2 semaines", + "interval-month" : "Mois", + "interval-three-months" : "3 mois", + "interval-six-months" : "6 mois", + "step-settings" : "Paramètres du pas", + "hide-step-size" : "Masquer la taille du pas", + "initial-step-size" : "Taille du pas initiale", + "hide-labels" : "Masquer les étiquettes", + "use-session-storage" : "Utiliser le stockage de session", + "localizationMap" : { + "Sun" : "Dim", + "Mon" : "Lun", + "Tue" : "Mar", + "Wed" : "Mer", + "Thu" : "Jeu", + "Fri" : "Ven", + "Sat" : "Sam", + "Jan" : "Jan", + "Feb" : "Fév", + "Mar" : "Mar", + "Apr" : "Avr", + "May" : "Mai", + "Jun" : "Juin", + "Jul" : "Juil", + "Aug" : "Août", + "Sep" : "Sep", + "Oct" : "Oct", + "Nov" : "Nov", + "Dec" : "Déc", + "January" : "Janvier", + "February" : "Février", + "March" : "Mars", + "April" : "Avril", + "June" : "Juin", + "July" : "Juillet", + "August" : "Août", + "September" : "Septembre", + "October" : "Octobre", + "November" : "Novembre", + "December" : "Décembre", + "Custom Date Range" : "Plage de dates personnalisée", + "Date Range Template" : "Modèle de plage de dates", + "Today" : "Aujourd'hui", + "Yesterday" : "Hier", + "This Week" : "Cette semaine", + "Last Week" : "Semaine dernière", + "This Month" : "Ce mois-ci", + "Last Month" : "Mois dernier", + "Year" : "Année", + "This Year" : "Cette année", + "Last Year" : "Année dernière", + "Date picker" : "Sélecteur de date", + "Hour" : "Heure", + "Day" : "Jour", + "Week" : "Semaine", + "2 weeks" : "2 semaines", + "Month" : "Mois", + "3 months" : "3 mois", + "6 months" : "6 mois", + "Custom interval" : "Intervalle personnalisé", + "Interval" : "Intervalle", + "Step size" : "Taille du pas", + "Ok" : "OK" + } + }, + "doughnut" : { + "doughnut-appearance" : "Apparence du diagramme en anneau", + "layout" : "Disposition", + "layout-default" : "Par défaut", + "layout-with-total" : "Avec total", + "central-total-value" : "Valeur totale centrale", + "doughnut-card-style" : "Style de carte du diagramme en anneau" + }, + "entities-hierarchy" : { + "hierarchy-data-settings" : "Paramètres des données hiérarchiques", + "relations-query-function" : "Fonction de requête des relations des nœuds", + "has-children-function" : "Fonction indiquant si le nœud a des enfants", + "node-state-settings" : "Paramètres d'état du nœud", + "node-opened-function" : "Fonction pour ouvrir le nœud par défaut", + "node-disabled-function" : "Fonction désactivant le nœud", + "display-settings" : "Paramètres d'affichage", + "node-icon-function" : "Fonction d'icône de nœud", + "node-text-function" : "Fonction de texte de nœud", + "sort-settings" : "Paramètres de tri", + "nodes-sort-function" : "Fonction de tri des nœuds" + }, + "edge" : { + "display-default-title" : "Afficher le titre par défaut" + }, + "gateway" : { + "general-settings" : "Paramètres généraux", + "widget-title" : "Titre du widget", + "default-archive-file-name" : "Nom de fichier d'archive par défaut", + "device-type-for-new-gateway" : "Type d'appareil pour une nouvelle passerelle", + "messages-settings" : "Paramètres des messages", + "save-config-success-message" : "Message texte sur la configuration de passerelle enregistrée avec succès", + "device-name-exists-message" : "Message texte indiquant qu'un appareil portant ce nom existe déjà", + "gateway-title" : "Formulaire de passerelle", + "read-only" : "Lecture seule", + "events-title" : "Titre du formulaire des événements de passerelle", + "events-filter" : "Filtre des événements", + "event-key-contains" : "Clé d'événement contient...", + "show-connector" : "Afficher pour le connecteur", + "connector-state-param-key" : "Clé de paramètre d'état du connecteur", + "message" : "Message", + "level" : "Niveau", + "created-time" : "Date de création" + }, + "gauge" : { + "default-color" : "Couleur par défaut", + "radial-gauge-settings" : "Paramètres du jauge radial", + "ticks-settings" : "Paramètres des graduations", + "min-value" : "Valeur minimale", + "max-value" : "Valeur maximale", + "min-value-short" : "min", + "max-value-short" : "max", + "start-ticks-angle" : "Angle de départ des graduations", + "ticks-angle" : "Angle des graduations", + "major-ticks" : "Graduations majeures", + "major-ticks-count" : "Nombre de graduations majeures", + "major-ticks-color" : "Couleur des graduations majeures", + "minor-ticks" : "Graduations mineures", + "minor-ticks-count" : "Nombre de graduations mineures", + "minor-ticks-color" : "Couleur des graduations mineures", + "tick-numbers-font" : "Police des nombres de graduation", + "unit-title-settings" : "Paramètres du titre de l'unité", + "show-unit-title" : "Titre des unités", + "unit-title" : "Titre de l'unité", + "title-font" : "Police du titre", + "units-settings" : "Paramètres des unités", + "units-font" : "Police du texte des unités", + "value-box-settings" : "Paramètres de la boîte de valeur", + "show-value-box" : "Afficher la boîte de valeur", + "value-box" : "Boîte de valeur", + "value-int" : "Nombre de chiffres pour la partie entière", + "value-text" : "Texte de la valeur", + "value-text-shadow" : "Ombre du texte de valeur", + "value-font" : "Police du texte de valeur", + "rect-stroke-color-start" : "Couleur de début du contour du rectangle", + "rect-stroke-color-end" : "Couleur de fin du contour du rectangle", + "background-color" : "Couleur de fond", + "shadow-color" : "Couleur de l'ombre", + "value-box-rect-stroke-color" : "Couleur du contour de la boîte de valeur", + "value-box-rect-stroke-color-end" : "Couleur de fin du contour de la boîte de valeur", + "value-box-background-color" : "Couleur de fond de la boîte de valeur", + "value-box-shadow-color" : "Couleur de l'ombre de la boîte de valeur", + "plate-settings" : "Paramètres de la plaque", + "show-plate-border" : "Bordure de la plaque", + "plate-color" : "Couleur de la plaque", + "needle-settings" : "Paramètres de l'aiguille", + "needle-circle-size" : "Taille du cercle de l'aiguille", + "needle-color" : "Couleur de l'aiguille", + "needle-color-start" : "Couleur de l'aiguille - début du gradient", + "needle-color-end" : "Couleur de l'aiguille - fin du gradient", + "needle-color-shadow-up" : "Couleur de l'ombre supérieure de l'aiguille", + "needle-color-shadow-down" : "Ombre portée", + "highlights-settings" : "Paramètres des surlignages", + "highlights-width" : "Largeur des surlignages", + "highlights" : "Surlignages", + "highlight-from" : "De", + "highlight-to" : "À", + "highlight-color" : "Couleur", + "no-highlights" : "Aucun surlignage configuré", + "add-highlight" : "Ajouter un surlignage", + "animation-settings" : "Paramètres d'animation", + "enable-animation" : "Animation", + "animation-duration-rule" : "Durée et règle d'animation", + "animation-duration" : "Durée de l'animation", + "animation-rule" : "Règle d'animation", + "animation-linear" : "Linéaire", + "animation-quad" : "Quad", + "animation-quint" : "Quint", + "animation-cycle" : "Cycle", + "animation-bounce" : "Rebond", + "animation-elastic" : "Élastique", + "animation-dequad" : "Déquad", + "animation-dequint" : "Déquint", + "animation-decycle" : "Décyle", + "animation-debounce" : "Dérebon", + "animation-delastic" : "Délastic", + "linear-gauge-settings" : "Paramètres de jauge linéaire", + "bar-stroke" : "Contour de la barre", + "bar-stroke-width" : "Largeur du contour de la barre", + "bar-stroke-color" : "Couleur du contour de la barre", + "bar-background-color" : "Couleur de fond de la barre - début du gradient", + "bar-background-color-end" : "Couleur de fond de la barre - fin du gradient", + "progress-bar-color" : "Couleur de la barre de progression", + "progress-bar" : "Barre de progression", + "progress-bar-color-start" : "Couleur de la barre de progression - début du gradient", + "progress-bar-color-end" : "Couleur de la barre de progression - fin du gradient", + "major-ticks-names" : "Noms des graduations majeures", + "show-stroke-ticks" : "Afficher les traits des graduations", + "major-ticks-font" : "Police des graduations majeures", + "border-color" : "Couleur de bordure", + "border-width" : "Largeur de bordure", + "needle-circle" : "Cercle de l'aiguille", + "needle-circle-color" : "Couleur du cercle de l'aiguille", + "animation-target" : "Cible d'animation", + "animation-target-needle" : "Aiguille", + "animation-target-plate" : "Plaque", + "common-settings" : "Paramètres communs de jauge", + "gauge-type" : "Type de jauge", + "gauge-type-arc" : "Arc", + "gauge-type-donut" : "Donut", + "gauge-type-horizontal-bar" : "Barre horizontale", + "gauge-type-vertical-bar" : "Barre verticale", + "donut-start-angle" : "Angle de départ (degrés)", + "bar-settings" : "Paramètres de barre de jauge", + "relative-bar-width" : "Largeur relative de la barre", + "neon-glow-brightness" : "Luminosité de l'effet néon (0-100)", + "neon-glow-brightness-hint" : "0 - désactiver l'effet", + "stripes-thickness" : "Épaisseur des rayures", + "stripes-thickness-hint" : "0 - pas de rayures", + "rounded-line-cap" : "Extrémités arrondies", + "bar-color-settings" : "Paramètres de couleur de barre", + "use-precise-level-color-values" : "Utiliser des niveaux de couleur précis", + "bar-colors" : "Couleurs de la barre, du plus bas au plus haut", + "color" : "Couleur", + "no-bar-colors" : "Aucune couleur de barre configurée", + "add-bar-color" : "Ajouter une couleur de barre", + "from" : "De", + "to" : "À", + "fixed-level-colors" : "Couleurs de barre utilisant des valeurs limites", + "gauge-title-settings" : "Paramètres du titre de jauge", + "show-gauge-title" : "Afficher le titre de la jauge", + "gauge-title" : "Titre de la jauge", + "gauge-title-font" : "Police du titre de la jauge", + "unit-title-and-timestamp-settings" : "Paramètres du titre d'unité et de l'horodatage", + "show-timestamp" : "Horodatage", + "timestamp-format" : "Format d'horodatage", + "label-font" : "Police de l'étiquette sous la valeur", + "value-settings" : "Paramètres de valeur", + "show-value" : "Afficher le texte de valeur", + "min-max-settings" : "Paramètres des étiquettes min/max", + "show-min-max" : "Afficher les valeurs min et max", + "min-max-font" : "Police des étiquettes min et max", + "show-ticks" : "Afficher les graduations", + "tick-width" : "Largeur des graduations", + "tick-color" : "Couleur des graduations", + "tick-values" : "Valeurs de graduation", + "no-tick-values" : "Aucune valeur de graduation configurée", + "add-tick-value" : "Ajouter une valeur de graduation", + "gauge-appearance" : "Apparence de la jauge", + "units-title" : "Titre des unités", + "value" : "Valeur", + "ticks" : "Graduations", + "arrow-and-scale-color" : "Couleur par défaut de la flèche et de l'échelle", + "scale-settings" : "Paramètres de l'échelle", + "scale" : "Échelle", + "scale-color" : "Couleurs de l'échelle", + "compass-appearance" : "Apparence de la boussole", + "label" : "Étiquette", + "labels" : "Étiquettes", + "label-style" : "Style de l'étiquette", + "simple-gauge-type" : "Type", + "gauge-bar-background" : "Fond de la barre de jauge", + "bar-color" : "Couleur de la barre", + "min-and-max-value" : "Valeur min et max", + "min-and-max-label" : "Étiquette min et max", + "font" : "Police", + "tick-width-and-color" : "Largeur et couleur des graduations", + "min-max-validation-text" : "La valeur maximale doit être supérieure à la valeur minimale" + }, + "gpio" : { + "pin" : "Broche", + "label" : "Étiquette", + "row" : "Ligne", + "column" : "Colonne", + "color" : "Couleur", + "panel-settings" : "Paramètres du panneau", + "background-color" : "Couleur de fond", + "gpio-switches" : "Interrupteurs GPIO", + "no-gpio-switches" : "Aucun interrupteur GPIO configuré", + "add-gpio-switch" : "Ajouter un interrupteur GPIO", + "gpio-status-request" : "Requête de statut GPIO", + "method-name" : "Nom de la méthode", + "method-body" : "Corps de la méthode", + "gpio-status-change-request" : "Requête de changement de statut GPIO", + "parse-gpio-status-function" : "Fonction de traitement du statut GPIO", + "gpio-leds" : "LEDs GPIO", + "no-gpio-leds" : "Aucune LED GPIO configurée", + "add-gpio-led" : "Ajouter une LED GPIO" + }, + "html-card" : { + "html" : "HTML", + "css" : "CSS" + }, + "input-widgets" : { + "attribute-not-allowed" : "Le paramètre d'attribut ne peut pas être utilisé dans ce widget", + "blocked-location" : "La géolocalisation est bloquée dans votre navigateur", + "claim-device" : "Réclamer l'appareil", + "claim-failed" : "Échec de la réclamation de l'appareil !", + "claim-not-found" : "Appareil introuvable !", + "claim-successful" : "L'appareil a été réclamé avec succès !", + "date" : "Date", + "device-name" : "Nom de l'appareil", + "device-name-required" : "Le nom de l'appareil est requis", + "discard-changes" : "Annuler les modifications", + "entity-attribute-required" : "L'attribut d'entité est requis", + "entity-coordinate-required" : "Les deux champs, latitude et longitude, sont requis", + "entity-timeseries-required" : "La série temporelle de l'entité est requise", + "get-location" : "Obtenir la position actuelle", + "invalid-date" : "Date invalide", + "latitude" : "Latitude", + "longitude" : "Longitude", + "min-value-error" : "Valeur minimale : {{value}}", + "max-value-error" : "Valeur maximale : {{value}}", + "not-allowed-entity" : "L'entité sélectionnée ne peut pas avoir d'attributs partagés", + "no-attribute-selected" : "Aucun attribut sélectionné", + "no-datakey-selected" : "Aucune clé de données sélectionnée", + "no-coordinate-specified" : "La clé de données pour latitude/longitude n'est pas spécifiée", + "no-entity-selected" : "Aucune entité sélectionnée", + "no-image" : "Aucune image", + "no-support-geolocation" : "Votre navigateur ne prend pas en charge la géolocalisation", + "no-support-web-camera" : "Votre navigateur ne prend pas en charge les caméras", + "enable-https-use-widget" : "Veuillez activer HTTPS pour utiliser ce widget", + "no-found-your-camera" : "Impossible de trouver votre caméra", + "no-permission-camera" : "Permission refusée / Ce site n'a pas l'autorisation d'utiliser la caméra", + "no-timeseries-selected" : "Aucune série temporelle sélectionnée", + "secret-key" : "Clé secrète", + "secret-key-required" : "La clé secrète est requise", + "switch-attribute-value" : "Basculer la valeur de l'attribut de l'entité", + "switch-camera" : "Changer de caméra", + "switch-timeseries-value" : "Basculer la valeur de la série temporelle de l'entité", + "take-photo" : "Prendre une photo", + "time" : "Heure", + "timeseries-not-allowed" : "Le paramètre de série temporelle ne peut pas être utilisé dans ce widget", + "update-failed" : "Échec de la mise à jour", + "update-successful" : "Mise à jour réussie", + "update-attribute" : "Mettre à jour l'attribut", + "update-timeseries" : "Mettre à jour la série temporelle", + "value" : "Valeur", + "general-settings" : "Paramètres généraux", + "widget-title" : "Titre du widget", + "claim-button-label" : "Étiquette du bouton de réclamation", + "show-secret-key-field" : "Afficher le champ de saisie 'Clé secrète'", + "labels-settings" : "Paramètres des étiquettes", + "show-labels" : "Afficher les étiquettes", + "device-name-label" : "Étiquette pour le champ de nom d'appareil", + "secret-key-label" : "Étiquette pour le champ de clé secrète", + "messages-settings" : "Paramètres des messages", + "claim-device-success-message" : "Message de succès pour la réclamation de l'appareil", + "claim-device-not-found-message" : "Message lorsque l'appareil est introuvable", + "claim-device-failed-message" : "Message d'échec pour la réclamation de l'appareil", + "claim-device-name-required-message" : "Message d'erreur 'Nom de l'appareil requis'", + "claim-device-secret-key-required-message" : "Message d'erreur 'Clé secrète requise'", + "show-label" : "Afficher l'étiquette", + "label" : "Étiquette", + "required" : "Requis", + "required-error-message" : "Message d'erreur 'Requis'", + "show-result-message" : "Afficher le message de résultat", + "integer-field-settings" : "Paramètres du champ entier", + "min-value" : "Valeur minimale", + "max-value" : "Valeur maximale", + "double-field-settings" : "Paramètres du champ double", + "text-field-settings" : "Paramètres du champ texte", + "min-length" : "Longueur minimale", + "max-length" : "Longueur maximale", + "checkbox-settings" : "Paramètres de la case à cocher", + "true-label" : "Étiquette cochée", + "false-label" : "Étiquette décochée", + "image-input-settings" : "Paramètres d'entrée d'image", + "display-preview" : "Afficher l’aperçu", + "display-clear-button" : "Afficher le bouton de réinitialisation", + "display-apply-button" : "Afficher le bouton d’application", + "display-discard-button" : "Afficher le bouton d’annulation", + "datetime-field-settings" : "Paramètres du champ date/heure", + "display-time-input" : "Afficher le champ d'heure", + "latitude-key-name" : "Nom de la clé latitude", + "longitude-key-name" : "Nom de la clé longitude", + "show-get-location-button" : "Afficher le bouton 'Obtenir la position actuelle'", + "use-high-accuracy" : "Utiliser une haute précision", + "location-fields-settings" : "Paramètres des champs de localisation", + "latitude-label" : "Étiquette de latitude", + "longitude-label" : "Étiquette de longitude", + "input-fields-alignment" : "Alignement des champs de saisie", + "input-fields-alignment-column" : "Colonne (par défaut)", + "input-fields-alignment-row" : "Ligne", + "layout" : "Disposition", + "row-gap" : "Espace entre les lignes (en pixels)", + "column-gap" : "Espace entre les colonnes (en pixels)", + "latitude-field-required" : "Le champ latitude est requis", + "longitude-field-required" : "Le champ longitude est requis", + "attribute-settings" : "Paramètres des attributs", + "widget-mode" : "Mode du widget", + "widget-mode-update-attribute" : "Mettre à jour l’attribut", + "widget-mode-update-timeseries" : "Mettre à jour la série temporelle", + "attribute-scope" : "Portée de l’attribut", + "attribute-scope-server" : "Attribut serveur", + "attribute-scope-shared" : "Attribut partagé", + "value-required" : "Valeur requise", + "image-settings" : "Paramètres de l’image", + "image-format" : "Format d’image", + "image-format-jpeg" : "JPEG", + "image-format-png" : "PNG", + "image-format-webp" : "WEBP", + "image-quality" : "Qualité d’image (utilise la compression avec perte pour jpeg/webp)", + "max-image-width" : "Largeur maximale de l’image", + "max-image-height" : "Hauteur maximale de l’image", + "action-buttons" : "Boutons d'action", + "show-action-buttons" : "Afficher les boutons d'action", + "update-all-values" : "Mettre à jour toutes les valeurs, pas seulement les modifiées", + "save-button-label" : "Étiquette du bouton 'ENREGISTRER'", + "reset-button-label" : "Étiquette du bouton 'ANNULER'", + "group-settings" : "Paramètres de groupe", + "show-group-title" : "Afficher le titre du groupe de champs liés à différentes entités", + "group-title" : "Titre du groupe", + "fields-alignment" : "Alignement des champs", + "fields-alignment-row" : "Ligne (par défaut)", + "fields-alignment-column" : "Colonne", + "fields-in-row" : "Nombre de champs par ligne", + "option-value" : "Valeur (écrire 'null' pour créer une option vide)", + "option-label" : "Étiquette", + "hide-input-field" : "Masquer le champ de saisie", + "datakey-type" : "Type de clé de données", + "datakey-type-server" : "Attribut serveur (par défaut)", + "datakey-type-shared" : "Attribut partagé", + "datakey-type-timeseries" : "Série temporelle", + "datakey-value-type" : "Type de valeur de la clé de données", + "datakey-value-type-string" : "Chaîne", + "datakey-value-type-double" : "Double", + "datakey-value-type-integer" : "Entier", + "datakey-value-type-json" : "JSON", + "datakey-value-type-boolean-checkbox" : "Booléen (Case à cocher)", + "datakey-value-type-boolean-switch" : "Booléen (Interrupteur)", + "datakey-value-type-date-time" : "Date et heure", + "datakey-value-type-date" : "Date", + "datakey-value-type-time" : "Heure", + "datakey-value-type-select" : "Liste déroulante", + "datakey-value-type-radio" : "Boutons radio", + "datakey-value-type-color" : "Couleur", + "value-is-required" : "La valeur est requise", + "ability-to-edit-attribute" : "Capacité à modifier l’attribut", + "ability-to-edit-attribute-editable" : "Modifiable (par défaut)", + "ability-to-edit-attribute-disabled" : "Désactivé", + "ability-to-edit-attribute-readonly" : "Lecture seule", + "disable-on-datakey-name" : "Désactiver selon la valeur 'false' d’une autre clé (spécifiez le nom de la clé)", + "field-appearance" : "Apparence du champ", + "appearance-fill" : "Remplissage", + "appearance-outline" : "Contour", + "subscript-sizing" : "Taille des indices", + "subscript-sizing-fixed" : "Fixe", + "subscript-sizing-dynamic" : "Dynamique", + "slide-toggle-settings" : "Paramètres de l’interrupteur coulissant", + "slide-toggle-label-position" : "Position de l’étiquette de l’interrupteur", + "slide-toggle-label-position-after" : "Après", + "slide-toggle-label-position-before" : "Avant", + "select-options" : "Options de sélection", + "no-select-options" : "Aucune option de sélection configurée", + "add-select-option" : "Ajouter une option de sélection", + "numeric-field-settings" : "Paramètres du champ numérique", + "step-interval" : "Intervalle entre les valeurs", + "error-messages" : "Messages d’erreur", + "min-value-error-message" : "Message d’erreur de la 'valeur minimale'", + "max-value-error-message" : "Message d’erreur de la 'valeur maximale'", + "invalid-date-error-message" : "Message d’erreur de 'date invalide'", + "invalid-JSON-error-message" : "Message d’erreur de 'JSON invalide'", + "icon-settings" : "Paramètres de l’icône", + "dialog-editor-settings" : "Paramètres de l’éditeur de dialogue", + "use-custom-icon" : "Utiliser une icône personnalisée", + "input-cell-icon" : "Icône à afficher avant le champ de saisie", + "value-conversion-settings" : "Paramètres de conversion de valeur", + "get-value-settings" : "Paramètres de récupération de valeur", + "use-get-value-function" : "Utiliser la fonction getValue", + "get-value-function" : "Fonction getValue", + "set-value-settings" : "Paramètres de définition de valeur", + "use-set-value-function" : "Utiliser la fonction setValue", + "set-value-function" : "Fonction setValue", + "json-invalid" : "Le format de la valeur JSON est invalide", + "title" : "Titre", + "cancel-button-label" : "Étiquette du bouton 'Annuler'", + "radio-button-settings" : "Paramètres des boutons radio", + "color" : "Couleur", + "columns" : "Colonnes", + "radio-options" : "Options radio", + "no-radio-options" : "Aucune option radio configurée", + "add-radio-option" : "Ajouter une option radio", + "radio-label-position" : "Position de l’étiquette", + "radio-label-position-before" : "Avant", + "radio-label-position-after" : "Après" + }, + "invalid-qr-code-text" : "Texte saisi invalide pour le code QR. L'entrée doit être une chaîne de caractères", + "qr-code" : { + "use-qr-code-text-function" : "Utiliser une fonction de texte pour le code QR", + "qr-code-text-pattern" : "Modèle de texte du code QR (par ex. '${entityName} | ${keyName} - texte.')", + "qr-code-text-pattern-hint" : "Le modèle de texte du code QR utilise la valeur de la première clé trouvée dans les entités de l'alias d'entité.", + "qr-code-text-pattern-required" : "Le modèle de texte du code QR est requis.", + "qr-code-text-function" : "Fonction de texte du code QR" + }, + "label-widget" : { + "label-pattern" : "Modèle", + "label-pattern-hint" : "Astuce : par ex. 'Texte ${keyName} unités.' ou ${#<key index>} unités'", + "label-pattern-required" : "Le modèle est requis", + "label-position" : "Position (pourcentage par rapport à l’arrière-plan)", + "x-pos" : "X", + "y-pos" : "Y", + "background-color" : "Couleur de fond", + "font-settings" : "Paramètres de la police", + "background-image" : "Image de fond", + "labels" : "Étiquettes", + "no-labels" : "Aucune étiquette configurée", + "add-label" : "Ajouter une étiquette" + }, + "navigation" : { + "title" : "Titre", + "navigation-path" : "Chemin de navigation", + "filter-type" : "Type de filtre", + "filter-type-all" : "Tous les éléments", + "filter-type-include" : "Inclure les éléments", + "filter-type-exclude" : "Exclure les éléments", + "items" : "Éléments", + "enter-urls-to-filter" : "Saisissez les URLs à filtrer..." + }, + "persistent-table" : { + "rpc-id" : "ID RPC", + "message-type" : "Type de message", + "method" : "Méthode", + "params" : "Paramètres", + "created-time" : "Heure de création", + "expiration-time" : "Date d’expiration", + "retries" : "Tentatives", + "status" : "Statut", + "filter" : "Filtrer", + "refresh" : "Actualiser", + "add" : "Ajouter une requête RPC", + "details" : "Détails", + "delete" : "Supprimer", + "delete-request-title" : "Supprimer la requête RPC persistante", + "delete-request-text" : "Êtes-vous sûr de vouloir supprimer cette requête ?", + "details-title" : "Détails ID RPC : ", + "additional-info" : "Informations supplémentaires", + "response" : "Réponse", + "any-status" : "Tous les statuts", + "rpc-status-list" : "Liste des statuts RPC", + "no-request-prompt" : "Aucune requête à afficher", + "send-request" : "Envoyer la requête", + "add-title" : "Créer une requête RPC persistante", + "method-error" : "La méthode est requise.", + "timeout-error" : "La valeur minimale du délai d’attente est de 5000 (5 secondes).", + "white-space-error" : "Les espaces ne sont pas autorisés.", + "rpc-status" : { + "QUEUED" : "EN FILE", + "SENT" : "ENVOYÉ", + "DELIVERED" : "LIVRÉ", + "SUCCESSFUL" : "RÉUSSI", + "TIMEOUT" : "DÉLAI DÉPASSÉ", + "EXPIRED" : "EXPIRÉ", + "FAILED" : "ÉCHOUÉ" + }, + "rpc-search-status-all" : "TOUS", + "message-types" : { + "false" : "Bidirectionnel", + "true" : "Unidirectionnel" + }, + "general-settings" : "Paramètres généraux", + "enable-filter" : "Activer le filtre", + "enable-sticky-header" : "Afficher l’en-tête pendant le défilement", + "enable-sticky-action" : "Afficher la colonne des actions pendant le défilement", + "display-request-details" : "Afficher les détails de la requête", + "allow-send-request" : "Autoriser l’envoi de requêtes RPC", + "allow-delete-request" : "Autoriser la suppression de requêtes", + "columns-settings" : "Paramètres des colonnes", + "display-columns" : "Colonnes à afficher", + "column" : "Colonne", + "no-columns-found" : "Aucune colonne trouvée", + "no-columns-matching" : "'{{column}}' introuvable." + }, + "range-chart" : { + "chart" : "Graphique", + "data-zoom" : "Zoom des données", + "range-chart-appearance" : "Apparence du graphique à plages", + "range-colors" : "Couleurs de plage", + "out-of-range-color" : "Couleur hors plage", + "show-range-thresholds" : "Afficher les seuils de plage", + "range-thresholds-settings" : "Paramètres des seuils de plage", + "fill-area" : "Remplir la zone", + "fill-area-opacity" : "Opacité de la zone remplie", + "range-chart-style" : "Style du graphique à plages" + }, + "rpc" : { + "value-settings" : "Paramètres de la valeur", + "initial-value" : "Valeur initiale", + "retrieve-value-settings" : "Paramètres de récupération de la valeur marche/arrêt", + "retrieve-value-method" : "Récupérer la valeur via la méthode", + "retrieve-value-method-none" : "Ne pas récupérer", + "retrieve-value-method-rpc" : "Appeler la méthode RPC de récupération", + "retrieve-value-method-attribute" : "S'abonner à un attribut", + "retrieve-value-method-timeseries" : "S'abonner à une série temporelle", + "attribute-value-key" : "Clé de l'attribut", + "timeseries-value-key" : "Clé de la série temporelle", + "get-value-method" : "Méthode RPC de récupération", + "parse-value-function" : "Fonction d'analyse de la valeur", + "update-value-settings" : "Paramètres de mise à jour de la valeur", + "set-value-method" : "Méthode RPC de mise à jour", + "convert-value-function" : "Fonction de conversion de la valeur", + "rpc-settings" : "Paramètres RPC", + "request-timeout" : "Délai d'attente de la requête RPC (ms)", + "persistent-rpc-settings" : "Paramètres RPC persistants", + "request-persistent" : "Requête RPC persistante", + "persistent-polling-interval" : "Intervalle de sondage (ms) pour la réponse RPC persistante", + "common-settings" : "Paramètres communs", + "switch-title" : "Titre de l'interrupteur", + "show-on-off-labels" : "Afficher les étiquettes marche/arrêt", + "slide-toggle-label" : "Étiquette du curseur", + "label-position" : "Position de l’étiquette", + "label-position-before" : "Avant", + "label-position-after" : "Après", + "slider-color" : "Couleur du curseur", + "slider-color-primary" : "Principal", + "slider-color-accent" : "Accent", + "slider-color-warn" : "Avertissement", + "button-style" : "Style de bouton", + "button-raised" : "Bouton en relief", + "button-primary" : "Couleur principale", + "button-background-color" : "Couleur d’arrière-plan du bouton", + "button-text-color" : "Couleur du texte du bouton", + "widget-title" : "Titre du widget", + "button-label" : "Étiquette du bouton", + "device-attribute-scope" : "Portée de l'attribut de l'appareil", + "server-attribute" : "Attribut serveur", + "shared-attribute" : "Attribut partagé", + "device-attribute-parameters" : "Paramètres d’attribut de l’appareil", + "is-one-way-command" : "Commande à sens unique", + "rpc-method" : "Méthode RPC", + "rpc-method-params" : "Paramètres de la méthode RPC", + "show-rpc-error" : "Afficher les erreurs d’exécution RPC", + "led-title" : "Titre LED", + "led-color" : "Couleur LED", + "check-status-settings" : "Paramètres de vérification de statut", + "perform-rpc-status-check" : "Effectuer une vérification de statut de l’appareil via RPC", + "retrieve-led-status-value-method" : "Méthode de récupération de l'état LED", + "led-status-value-attribute" : "Attribut de l’appareil contenant l’état LED", + "led-status-value-timeseries" : "Série temporelle contenant l’état LED", + "check-status-method" : "Méthode RPC de vérification de l’état de l’appareil", + "parse-led-status-value-function" : "Fonction d’analyse de l’état LED", + "knob-title" : "Titre du bouton rotatif", + "min-value" : "Valeur minimale", + "max-value" : "Valeur maximale" + }, + "maps" : { + "map-type" : { + "type" : "Type de carte", + "map" : "Carte", + "image" : "Image" + }, + "image" : { + "image-source" : "Source de l’image", + "image-source-image" : "Image", + "image-source-entity-key" : "Clé d’entité", + "source-entity-alias" : "Alias d'entité source", + "image-url-key" : "Clé de l’URL de l’image", + "image-url-key-required" : "La clé de l’URL de l’image est requise" + }, + "control" : { + "map-controls" : "Contrôles de la carte", + "position" : "Position", + "position-topleft" : "En haut à gauche", + "position-topright" : "En haut à droite", + "position-bottomleft" : "En bas à gauche", + "position-bottomright" : "En bas à droite", + "zoom-actions" : "Actions de zoom", + "zoom-scroll" : "Défilement", + "zoom-double-click" : "Double-clic", + "zoom-control-buttons" : "Boutons de contrôle", + "scale" : "Échelle", + "scale-metric" : "Métrique", + "scale-imperial" : "Impérial", + "switch-to-drag-mode-using-button" : "Basculer en mode glisser via le bouton" + }, + "timeline" : { + "control-panel" : "Panneau de contrôle de la chronologie", + "time-step" : "Intervalle de temps", + "speed-options" : "Options de vitesse", + "timestamp" : "Horodatage", + "snap-to-real-location" : "Aligner sur la position réelle", + "location-snap-filter-function" : "Fonction de filtrage pour alignement de position", + "no-trips-data-available" : "Aucune donnée de trajet disponible" + }, + "map-action" : { + "map-action-buttons" : "Boutons d’action sur la carte", + "label" : "Étiquette", + "icon" : "Icône", + "color" : "Couleur", + "action" : "Action", + "add-button" : "Ajouter un bouton", + "no-action-buttons-configured" : "Aucun bouton d'action configuré", + "remove-action-button" : "Supprimer le bouton d'action", + "map-action-button" : "Bouton d’action sur la carte", + "button-requires" : "Le bouton nécessite une étiquette ou une icône" + }, + "common" : { + "common-map-settings" : "Paramètres de carte communs", + "fit-map-bounds" : "Ajuster les limites de la carte pour couvrir tous les marqueurs", + "default-map-center-position" : "Position centrale par défaut de la carte", + "default-map-zoom-level" : "Niveau de zoom par défaut", + "entities-limit" : "Limite d’entités à charger" + }, + "layer" : { + "label" : "Étiquette", + "layer" : "Couche", + "layers" : "Couches", + "map-layers" : "Couches de carte", + "add-layer" : "Ajouter une couche", + "layer-settings" : "Paramètres de la couche", + "remove-layer" : "Supprimer la couche", + "no-layers" : "Aucune couche configurée", + "roadmap" : "Plan", + "satellite" : "Satellite", + "hybrid" : "Hybride", + "reference" : { + "reference-layer" : "Couche de référence", + "no-layer" : "Aucune couche", + "openstreetmap-hybrid" : "Hybride OpenStreetMap", + "world-edition-hybrid" : "Hybride Édition Mondiale", + "enhanced-contrast-hybrid" : "Hybride à contraste amélioré" }, - "end-time": "Heure de fin", - "min-polling-interval-message": "Un intervalle d'interrogation d'au moins 1 seconde est autorisé.", - "no-alarms-matching": "Aucune alarme correspondant à {{entity}} n'a été trouvée. ", - "no-alarms-prompt": "Aucune alarme", - "no-data": "Aucune donnée à afficher", - "originator": "Source", - "originator-type": "Type de Source", - "polling-interval": "Intervalle d'interrogation des alarmes (sec)", - "polling-interval-required": "L'intervalle d'interrogation des alarmes est requis.", - "search": "Rechercher des alarmes", - "search-status": { - "ACK": "acquitté", - "ACTIVE": "active", - "ANY": "Toutes", - "CLEARED": "effacée", - "UNACK": "non acquittée" + "provider" : { + "provider" : "Fournisseur", + "openstreet" : { + "title" : "OpenStreet", + "mapnik" : "Mapnik", + "hot" : "HOT", + "esri-street" : "WorldStreetMap", + "esri-topo" : "WorldTopoMap", + "esri-imagery" : "WorldImagery", + "cartodb-positron" : "Positron", + "cartodb-dark-matter" : "DarkMatter" + }, + "google" : { + "title" : "Google", + "roadmap" : "Plan", + "satellite" : "Satellite", + "hybrid" : "Hybride", + "terrain" : "Relief" + }, + "here" : { + "title" : "HERE", + "normal-day" : "Jour normal", + "normal-night" : "Nuit normale", + "hybrid-day" : "Hybride jour", + "terrain-day" : "Relief jour" + }, + "tencent" : { + "title" : "Tencent", + "normal" : "Normal", + "satellite" : "Satellite", + "terrain" : "Relief" + }, + "custom" : { + "title" : "Personnalisé", + "tile-url" : "URL des tuiles" + } }, - "select-alarm": "Sélectionnez une alarme", - "selected-alarms": "{count, plural, =1 {1 alarme} other {# alarmes} } sélectionnées", - "severity": "Gravité", - "severity-critical": "Critique", - "severity-indeterminate": "indéterminée", - "severity-major": "Majeure", - "severity-minor": "mineure", - "severity-warning": "Avertissement", - "start-time": "Heure de début", - "status": "État", - "type": "Type", - "alarm-status-list": "Liste d'état des alarmes", - "any-status": "Tout statut", - "alarm-severity-list": "Liste de gravité des alarmes", - "any-severity": "Toute gravité", - "alarm-filter": "Filtre d'alarme", - "max-count-load": "Nombre maximum d'alarmes à charger (0 - illimité)", - "max-count-load-required": "Le nombre maximum d'alarmes à charger est requis.", - "max-count-load-error-min": "La valeur minimale est 0.", - "fetch-size": "Taille de la requête", - "fetch-size-required": "La taille de la requête est requise.", - "fetch-size-error-min": "La valeur minimale est 10.", - "alarm-type-list": "Liste des types d'alarme", - "any-type": "N'importe quel type", - "search-propagated-alarms": "Rechercher les alarmes propagées" - }, - "alias": { - "add": "Ajouter un alias", - "all-entities": "Toutes les entités", - "any-relation": "toutes", - "default-entity-parameter-name": "Par défaut", - "default-state-entity": "Entité d'état par défaut", - "duplicate-alias": "Un alias portant le même nom existe déjà.", - "edit": "Modifier l'alias", - "entity-filter": "Filtre d'entité", - "entity-filter-no-entity-matched": "Aucune entité correspondant au filtre spécifié n'a été trouvée.", - "filter-type": "Type de filtre", - "filter-type-asset-search-query": "Requête de recherche d'actifs", - "filter-type-asset-search-query-description": "Actifs de types {{assetTypes}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-asset-type": "type d'actif", - "filter-type-asset-type-and-name-description": "Actifs de type '{{assetTypes}}' et dont le nom commence par '{{prefix}}'", - "filter-type-asset-type-description": "Actifs de type '{{assetTypes}}'", - "filter-type-device-search-query": "Requête de recherche de dispositif", - "filter-type-device-search-query-description": "Dispositifs de types {{deviceTypes}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-device-type": "Type de dispositif", - "filter-type-device-type-and-name-description": "Dispositifs de type '{{deviceTypes}}' et dont le nom commence par '{{prefix}}'", - "filter-type-device-type-description": "Dispositifs de type '{{deviceTypes}}'", - "filter-type-entity-list": "Liste d'entités", - "filter-type-entity-name": "Nom d'entité", - "filter-type-entity-view-search-query": "Requête de recherche vue d'entité", - "filter-type-entity-view-search-query-description": "Vues d'entité avec les types {{entityViewTypes}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-edge-search-query": "Requête de recherche de Edge", - "filter-type-edge-search-query-description": "Edges avec types {{edgeTypes}} qui ont {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-entity-view-type": "Type de vue d'entité", - "filter-type-entity-view-type-and-name-description": "Vues d'entité de type '{{entityViewTypes}}' et dont le nom commence par '{{prefix}}'", - "filter-type-entity-view-type-description": "Vues d'entité de type '{{entityViewTypes}}'", - "filter-type-edge-type": "Types de Edge", - "filter-type-edge-type-description": "Edges de type '{{edgeTypes}}'", - "filter-type-relations-query": "Interrogation des relations", - "filter-type-relations-query-description": "{{entities}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-required": "Le type de filtre est requis.", - "filter-type-single-entity": "Entité unique", - "filter-type-state-entity": "Entité de l'état du tableau de bord", - "filter-type-state-entity-description": "Entité extraite des paramétres d'état du tableau de bord", - "max-relation-level": "Niveau de relation maximum", - "name": "Nom de l'alias", - "name-required": "Le nom d'alias est requis", - "no-entity-filter-specified": "Aucun filtre d'entité spécifié", - "resolve-multiple": "Résoudre en plusieurs entités", - "root-entity": "Entité racine", - "root-state-entity": "Utiliser l'entité d'état du tableau de bord en tant que racine", - "state-entity": "Entité d'état du tableau de bord", - "state-entity-parameter-name": "Nom du paramétre d'entité d'état", - "unlimited-level": "Niveau illimité", - "filter-type-entity-type": "Type d'entité", - "filter-type-edge-type-and-name-description": "Edges de type '{{edgeTypes}}' et dont le nom commence par '{{prefix}}'", - "filter-type-apiUsageState": "État d'utilisation de l'API", - "last-level-relation": "Récupérer uniquement la relation de dernier niveau" - }, - "asset": { - "add": "Ajouter un actif", - "add-asset-text": "Ajouter un nouvel actif", - "any-asset": "Tout actif", - "asset": "Actif", - "asset-details": "Détails de l'actif", - "asset-file": "Fichier d'actif", - "asset-public": "L'actif est public", - "asset-required": "Actif requis", - "asset-type": "Type d'actif", - "asset-type-list-empty": "Aucun type d'actif sélectionné.", - "asset-type-required": "Le type d'actif est requis.", - "asset-types": "Types d'actif", - "assets": "Actifs", - "assign-asset-to-customer": "Attribuer des actifs au client", - "assign-asset-to-customer-text": "Veuillez sélectionner les actifs à attribuer au client", - "assign-assets": "Attribuer des actifs", - "assign-assets-text": "Attribuer {count, plural, =1 {1 asset} other {# assets} } au client", - "assign-new-asset": "Attribuer un nouvel Asset", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner le client pour attribuer le ou les actifs", - "assignedToCustomer": "attribué au client", - "copyId": "Copier l'Id de l'actif", - "delete": "Supprimer un actif", - "delete-asset-text": "Faites attention, après la confirmation, l'actif et toutes les données associées deviendront irrécupérables.", - "delete-asset-title": "Êtes-vous sûr de vouloir supprimer l'actif '{{assetName}}'?", - "delete-assets": "Supprimer des actifs", - "delete-assets-action-title": "Supprimer {count, plural, =1 {1 asset} other {# assets} }", - "delete-assets-text": "Attention, après la confirmation, tous les actifs sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-assets-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 asset} other {# assets} }?", - "description": "Description", - "details": "Détails", - "enter-asset-type": "Entrez le type d'actif", - "events": "Evénements", - "idCopiedMessage": "L'Id d'actif a été copié dans le presse-papier", - "import": "Importer des actifs", - "make-private": "Rendre l'actif privé", - "make-private-asset-text": "Après la confirmation, l'actif et toutes ses données seront rendus privés et ne seront pas accessibles par d'autres.", - "make-private-asset-title": "Êtes-vous sûr de vouloir rendre l'actif '{{assetName}}' privé '?", - "make-public": "Rendre l'actif public", - "make-public-asset-text": "Après la confirmation, l'asset et toutes ses données seront rendus publics et accessibles aux autres.", - "make-public-asset-title": "Êtes-vous sûr de vouloir rendre l'actif '{{assetName}}' public '?", - "management": "Gestion d'actifs", - "name": "Nom", - "name-required": "Nom est requis.", - "name-starts-with": "Le nom de l'actif commence par", - "no-asset-types-matching": "Aucun type d'actif correspondant à {{entitySubtype}} n'a été trouvé. ", - "no-assets-matching": "Aucun actif correspondant à {{entity}} n'a été trouvé. ", - "no-assets-text": "Aucun actif trouvé", - "public": "Public", - "select-asset": "Sélectionner un actif", - "select-asset-type": "Sélectionner le type d'actif", - "type": "Type", - "type-required": "Le type est requis.", - "unassign-asset": "Retirer l'actif", - "unassign-asset-text": "Après la confirmation, l'actif sera non attribué et ne sera pas accessible au client.", - "unassign-asset-title": "Êtes-vous sûr de vouloir retirer l'attribution de l'actif '{{assetName}}'?", - "unassign-assets": "Retirer les actifs", - "unassign-assets-action-title": "Retirer {count, plural, =1 {1 asset} other {# assets} } du client", - "unassign-assets-text": "Après la confirmation, tous les actifs sélectionnés ne seront pas attribués et ne seront pas accessibles au client.", - "unassign-assets-title": "Êtes-vous sûr de vouloir retirer l'attribution de {count, plural, =1 {1 asset} other {# assets} }?", - "unassign-from-customer": "Retirer du client", - "view-assets": "Afficher les actifs", - "label": "Étiquette (label)", - "assign-asset-to-edge": "Attribuer des actifs à Edge", - "assign-asset-to-edge-text": "Veuillez sélectionner les actifs à attribuer a la bordure", - "unassign-asset-from-edge": "Désattribuer l'actif", - "unassign-asset-from-edge-title": "Voulez-vous vraiment annuler l'attribution de l'actif '{{assetName}}'?", - "unassign-asset-from-edge-text": "Après la confirmation, l'actif sera désaffecté et ne sera pas accessible par le edge.", - "unassign-assets-from-edge-action-title": "Retirer {count, plural, =1 {1 asset} other {# assets} } de la bordure", - "unassign-assets-from-edge-title": "Êtes-vous sûr de vouloir désattribuer { count, plural, =1 {1 asset} other {# assets} }?", - "unassign-assets-from-edge-text": "Après la confirmation, tous les actifs sélectionnés seront désaffectés et ne seront pas accessibles par le edge.", - "asset-type-max-length": "Le type d'actif doit être inférieur à 256", - "name-max-length": "Le nom doit être inférieur à 256", - "label-max-length": "L'étiquette doit être inférieure à 256", - "help-text": "Use '%' selon besoin : '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", - "search": "Rechercher des actifs", - "selected-assets": "{ count, plural, =1 {1 asset} other {# assets} } sélectionnés" - }, - "attribute": { - "add": "Ajouter un attribut", - "add-to-dashboard": "Ajouter au tableau de bord", - "add-widget-to-dashboard": "Ajouter un widget au tableau de bord", - "attributes": "Attributs", - "attributes-scope": "Étendue des attributs d'entité", - "delete-attributes": "Supprimer les attributs", - "delete-attributes-text": "Attention, après la confirmation, tous les attributs sélectionnés seront supprimés.", - "delete-attributes-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 attribut} other {# attributs} }?", - "enter-attribute-value": "Entrez la valeur de l'attribut", - "key": "Clé", - "key-required": "La Clé d'attribut est requise.", - "last-update-time": "Dernière mise à jour", - "latest-telemetry": "Dernière télémétrie", - "next-widget": "Widget suivant", - "prev-widget": "Widget précédent", - "scope-client": "Attributs du client", - "scope-telemetry": "Télémétrie", - "scope-latest-telemetry": "Dernière télémétrie", - "scope-server": "Attributs du serveur", - "scope-shared": "Attributs partagés", - "selected-attributes": "{count, plural, =1 {1 attribut} other {# attributs} } sélectionnés", - "selected-telemetry": "{count, plural, =1 {1 unité de télémétrie} other {# unités de télémétrie} } sélectionnées", - "show-on-widget": "Afficher sur le widget", - "value": "Valeur", - "value-required": "La valeur d'attribut est obligatoire.", - "widget-mode": "Mode du widget", - "key-max-length": "La clé doit être inférieure à 256", - "no-attributes-text": "Aucun attribut trouvé", - "no-telemetry-text": "Aucune télémétrie trouvée" - }, - "api-usage": { - "api-usage": "Usage de l'Api", - "alarm": "Alarme", - "alarms-created": "Alarmes créées", - "alarms-created-daily-activity": "Activité hebdomadaire d'alarmes créées", - "alarms-created-hourly-activity": "Activité horaire d'alarmes créées", - "alarms-created-monthly-activity": "Activités mensuelle d'alarmes créées", - "data-points": "Données", - "data-points-storage-days": "Jours de storage des données", - "email": "Courriel", - "email-messages": "Messages courriel", - "email-messages-daily-activity": "Activité hebdomadaire de courriels", - "email-messages-monthly-activity": "Activité menuselle de courriels", - "executions": "Exécutions", - "javascript-executions": "Exécutions JavaScript", - "latest-error": "Dernière erreur", - "notifications-email-sms": "Notifications (Coourriel/SMS)", - "notifications-hourly-activity": "Activité horaire de notifications", - "permanent-failures": "${entityName} Échecs permanents", - "permanent-timeouts": "${entityName} Temps d'arrêt permanents", - "processing-failures": "${entityName} Erreurs d'exécution", - "processing-failures-and-timeouts": "Erreurs d'exécution et temps d'arrêt", - "processing-timeouts": "${entityName} Temps d'arrêt d'exécution", - "queue-stats": "Stats de queue", - "rule-chain": "Chaîne de règles", - "rule-engine": "Engin de règles", - "rule-engine-daily-activity": "Activité hebdomadaire de l'engin de règles", - "rule-engine-executions": "Exécutions de l'engin de règles", - "rule-engine-hourly-activity": "Activité horaire de l'engin de règles", - "rule-engine-monthly-activity": "Activité mensuelle de l'engin de règles", - "rule-engine-statistics": "Statistiques de l'engin de règles", - "rule-node": "Node de règle", - "sms-messages": "Messages texte", - "sms-messages-daily-activity": "Activité hebdomadaire de messages texte", - "sms-messages-monthly-activity": "Activité mensuelle de messages texte", - "successful": "${entityName} réussi", - "telemetry": "Télémétrie", - "telemetry-persistence": "Persistance de télémétrie", - "telemetry-persistence-daily-activity": "Activité hebdomadaire de persistance de télémétrie", - "telemetry-persistence-hourly-activity": "Activité horaire de persistance de télémétrie", - "telemetry-persistence-monthly-activity": "Activité mensuelle de persistance de télémétrie", - "transport-daily-activity": "Activité hebdomadaire de transport", - "transport-data-points": "Données de transport", - "transport-hourly-activity": "Activité horaire de transport", - "transport-messages": "Messages de transport", - "transport-monthly-activity": "Activité mensuelle de transport", - "view-details": "Voir détails", - "view-statistics": "Voir statistiques" - }, - "audit-log": { - "action-data": "Donnée d'action", - "audit": "Audit", - "audit-log-details": "Détails du journal d'audit", - "audit-logs": "Journaux d'audit", - "clear-search": "Effacer la recherche", - "details": "Détails", - "entity-name": "Nom de l'entité", - "entity-type": "Type d'entité", - "failure-details": "Détails de l'échec", - "no-audit-logs-prompt": "Aucun journal trouvé", - "search": "Rechercher les journaux d'audit", - "status": "État", - "status-failure": "Échec", - "status-success": "Succès", - "timestamp": "Horodatage", - "type": "Type", - "type-activated": "Activé", - "type-added": "Ajouté", - "type-alarm-ack": "Acquitté", - "type-alarm-clear": "Effacé", - "type-assigned-to-customer": "Attribué au client", - "type-assigned-to-edge": "Assigné au Edge", - "type-unassigned-from-edge": "Attribution retirée du Edge", - "type-attributes-deleted": "Attributs supprimés", - "type-attributes-read": "Attributs lus", - "type-attributes-updated": "Attributs mis à jour", - "type-credentials-read": "Lecture des informations d'identification", - "type-credentials-updated": "Informations d'identification actualisées", - "type-deleted": "Supprimé", - "type-login": "Connexion", - "type-logout": "Déconnexion", - "type-lockout": "Verrouillage", - "type-relation-add-or-update": "Relation mise à jour", - "type-relation-delete": "Relation supprimée", - "type-relations-delete": "Toutes les relations ont été supprimées", - "type-rpc-call": "Appel RPC", - "type-suspended": "Suspendu", - "type-unassigned-from-customer": "Attribution retirée du client", - "type-updated": "Mise à jour", - "user": "Utilisateur", - "type-assigned-from-tenant": "Assigné par le Tenant", - "type-assigned-to-tenant": "Assigné au Tenant", - "type-provision-success": "Dispositif mis en service", - "type-provision-failure": "La mise en service du dispositif a échoué", - "type-timeseries-updated": "Telemetrie mise à jour", - "type-timeseries-deleted": "Telemetrie supprimée" - }, - "common": { - "enter-password": "Entrez le mot de passe", - "enter-search": "Entrez la recherche", - "enter-username": "Entrez le nom d'utilisateur", - "password": "Mot de passe", - "username": "Nom d'utilisateur", - "created-time": "Heure de création", - "loading": "Chargement en cours...", - "proceed": "Procéder", - "open-details-page": "Ouvrir la page détails" - }, - "confirm-on-exit": { - "html-message": "Vous avez des modifications non enregistrées.
Êtes-vous sûr de vouloir quitter cette page?", - "message": "Vous avez des modifications non enregistrées. Êtes-vous sûr de vouloir quitter cette page?", - "title": "Modifications non enregistrées" - }, - "contact": { - "address": "Adresse", - "address2": "Adresse 2", - "city": "Ville", - "country": "Pays", - "email": "Courriel", - "no-address": "Pas d'adresse", - "phone": "Téléphone", - "postal-code": "Code postal", - "postal-code-invalid": "Format de code postal / code postal invalide", - "state": "Province", - "state-max-length": "La longueur de l'état doit être moins que 256", - "phone-max-length": "La longueur du téléphone doit être moins que 256", - "city-max-length": "La ville spécifiée doit être moins que 256" - }, - "content-type": { - "binary": "Binaire (Base64)", - "json": "Json", - "text": "Texte" - }, - "custom": { - "widget-action": { - "action-cell-button": "Bouton de cellule d'action", - "marker-click": "Sur le marqueur cliquez", - "row-click": "Au rang, cliquez", - "polygon-click": "Cliquez sur le polygone", - "tooltip-tag-action": "Action de balise d'info-bulle", - "node-selected": "Sur le noeud sélectionné", - "element-click": "Sur l'élément HTML, cliquez sur", - "pie-slice-click": "Sur tranche cliquez", - "row-double-click": "Sur la ligne double clic" + "credentials" : { + "credentials" : "Identifiants", + "api-key" : "Clé API" } + }, + "overlays" : { + "overlays" : "Superpositions", + "overlays-hint" : "Configurer les sources de données, l’apparence, le comportement, les options d’édition et le regroupement pour les entités cartographiques", + "trips" : "Trajets", + "markers" : "Marqueurs", + "polygons" : "Polygones", + "circles" : "Cercles" + }, + "data-layer" : { + "source" : "Source", + "filter" : "Filtre", + "additional-data-keys" : "Clés de données supplémentaires", + "additional-datasources" : "Sources de données supplémentaires", + "additional-datasources-hint" : "Source de données pour accéder aux attributs ou à la télémétrie d'entités non affichées sur la carte, utilisable dans les fonctions de superposition.", + "more-datasources" : "Plus de sources de données", + "data-keys" : "Clés de données", + "add-datasource" : "Ajouter une source de données", + "no-datasources" : "Aucune source de données configurée", + "remove-datasource" : "Supprimer la source de données", + "behavior" : "Comportement", + "on-click" : "Au clic", + "on-click-hint" : "Action déclenchée lors d’un clic sur l’élément de la carte.", + "groups" : "Groupes", + "groups-hint" : "Liste des noms de groupes attribués à la superposition, utilisée pour basculer sa visibilité sur la carte.", + "color" : "Couleur", + "color-settings" : "Paramètres de couleur", + "color-type-constant" : "Constante", + "color-type-range" : "Plage", + "color-type-function" : "Fonction", + "color-range-source-key" : "Clé source de la plage de couleurs", + "color-range-source-key-required" : "La clé source de la plage de couleurs est requise", + "color-range" : "Plage de couleurs", + "color-function" : "Fonction de couleur", + "label" : "Étiquette", + "tooltip" : "Infobulle", + "pattern-type-pattern" : "Motif", + "pattern-type-function" : "Fonction", + "label-pattern" : "Étiquette (exemples de motifs : '${entityName}', '${entityName}: (Texte ${keyName} unités.)' )", + "label-function" : "Fonction d’étiquette", + "tooltip-pattern" : "Infobulle (ex. 'Texte ${keyName} unités.' ou Texte du lien)", + "tooltip-function" : "Fonction d’infobulle", + "tooltip-trigger" : "Déclencheur d’infobulle", + "tooltip-trigger-click" : "Afficher l’infobulle au clic", + "tooltip-trigger-hover" : "Afficher l’infobulle au survol", + "auto-close-tooltips" : "Fermeture automatique des infobulles", + "tooltip-offset" : "Décalage de l’infobulle", + "tooltip-offset-horizontal" : "Horizontal", + "tooltip-offset-vertical" : "Vertical", + "tooltip-tag-actions" : "Actions de balise", + "add-tooltip-tag-action" : "Ajouter une action de balise", + "edit-tooltip-tag-action" : "Modifier l’action de balise", + "remove-tooltip-tag-action" : "Supprimer l’action de balise", + "action-add" : "Ajouter", + "action-edit" : "Modifier", + "action-move" : "Déplacer", + "action-remove" : "Supprimer", + "edit-instruments" : "Outils d’édition", + "persist-location-attribute-scope" : "Portée de l’attribut pour la persistance de la position", + "enable-snapping" : "Activer l’alignement sur d’autres sommets pour un dessin précis", + "enable-snapping-hint" : "Aligne automatiquement les nouveaux points avec les formes existantes pour faciliter le dessin.", + "drag-drop-mode" : "Mode glisser-déposer", + "trip" : { + "no-trips" : "Aucun trajet configuré", + "add-trip" : "Ajouter un trajet", + "trip-configuration" : "Configuration du trajet", + "remove-trip" : "Supprimer le trajet" + }, + "marker" : { + "marker" : "Marqueur", + "latitude-key" : "Clé de latitude", + "longitude-key" : "Clé de longitude", + "x-pos-key" : "Clé de position X", + "y-pos-key" : "Clé de position Y", + "latitude-key-required" : "Clé de latitude requise", + "longitude-key-required" : "Clé de longitude requise", + "x-pos-key-required" : "Clé de position X requise", + "y-pos-key-required" : "Clé de position Y requise", + "no-markers" : "Aucun marqueur configuré", + "add-marker" : "Ajouter un marqueur", + "marker-configuration" : "Configuration du marqueur", + "remove-marker" : "Supprimer le marqueur", + "marker-type" : "Type de marqueur", + "marker-type-shape" : "Forme", + "marker-type-icon" : "Icône", + "marker-type-image" : "Image", + "shape" : "Forme", + "icon" : "Icône", + "image" : "Image", + "marker-shapes" : "Formes de marqueur", + "marker-icon" : "Icône du marqueur", + "marker-appearance" : "Apparence du marqueur", + "marker-image" : "Image du marqueur", + "marker-image-type-image" : "Image", + "marker-image-type-function" : "Fonction", + "custom-marker-image-size" : "Taille personnalisée de l’image du marqueur", + "marker-image-function" : "Fonction d’image du marqueur", + "marker-images" : "Images du marqueur", + "marker-offset" : "Décalage du marqueur", + "offset-horizontal" : "Horizontal", + "offset-vertical" : "Vertical", + "rotate-marker" : "Faire pivoter le marqueur", + "offset-angle" : "Angle de décalage", + "position-conversion" : "Conversion de position", + "position-conversion-function" : "Fonction de conversion de position, doit renvoyer les coordonnées x, y entre 0 et 1", + "clustering" : { + "use-map-markers-clustering" : "Utiliser le regroupement des marqueurs sur la carte", + "zoom-on-cluster-click" : "Zoom lors du clic sur un cluster", + "max-zoom" : "Niveau de zoom maximal pour faire partie d’un cluster (0 - 18)", + "max-radius" : "Rayon maximal couvert par un cluster", + "zoom-animation" : "Animation des marqueurs lors du zoom", + "bounds-on-cluster-mouse-over" : "Délimitation des marqueurs lors du survol du cluster", + "spiderfy-max-zoom-level" : "Déplier au niveau de zoom max (afficher tous les marqueurs du cluster)", + "load-optimization" : "Optimisation du chargement", + "chunked-load" : "Utiliser un chargement en morceaux pour éviter le gel de la page", + "lazy-load" : "Utiliser un chargement différé des marqueurs", + "use-cluster-marker-color-function" : "Utiliser une fonction de couleur pour les clusters", + "marker-color-function" : "Fonction de couleur du marqueur" + }, + "edit" : "Modifier le marqueur", + "remove-marker-for" : "Supprimer le marqueur pour '{{entityName}}'", + "place-marker" : "Placer un marqueur", + "place-marker-hint" : "Cliquez pour placer un marqueur", + "place-marker-hint-with-entity" : "Cliquez pour placer l’entité '{{entityName}}'" + }, + "path" : { + "path" : "Chemin", + "path-decorator" : "Décorateur de chemin", + "decorator-symbol" : "Symbole décoratif", + "decorator-symbol-arrow-head" : "Flèche", + "decorator-symbol-dash" : "Tiret", + "decorator-arrangement" : "Disposition du décorateur", + "decorator-offset" : "Début", + "decorator-end-offset" : "Fin", + "decorator-repeat" : "Répétition" + }, + "points" : { + "points" : "Points", + "point-tooltip" : "Infobulle du point" + }, + "shape" : { + "fill" : "Remplissage", + "fill-type-color" : "Couleur", + "fill-type-stripe" : "Rayure", + "fill-type-image" : "Image", + "color" : "Couleur", + "stripe" : "Rayure", + "image" : "Image", + "stroke" : "Contour", + "fill-image" : "Image de remplissage", + "fill-image-type-image" : "Image", + "fill-image-type-function" : "Fonction", + "preserve-aspect-ratio" : "Préserver le ratio", + "opacity" : "Opacité", + "angle" : "Angle de rotation", + "scale" : "Échelle", + "fill-image-function" : "Fonction d’image de remplissage", + "fill-images" : "Images de remplissage", + "stripe-pattern" : "Motif de rayure", + "first-stripe" : "Première rayure", + "second-stripe" : "Deuxième rayure" + }, + "polygon" : { + "polygon-key" : "Clé du polygone", + "polygon-key-required" : "Clé du polygone requise", + "no-polygons" : "Aucun polygone configuré", + "add-polygon" : "Ajouter un polygone", + "polygon-configuration" : "Configuration du polygone", + "remove-polygon" : "Supprimer le polygone", + "edit" : "Modifier le polygone", + "remove-polygon-for" : "Supprimer le polygone pour '{{entityName}}'", + "cut" : "Découper la zone du polygone", + "rotate" : "Faire pivoter le polygone", + "draw-rectangle" : "Dessiner un rectangle", + "draw-polygon" : "Dessiner un polygone", + "polygon-place-first-point-cut-hint" : "Cliquez pour placer le premier point", + "continue-polygon-cut-hint" : "Cliquez pour continuer à dessiner", + "finish-polygon-cut-hint" : "Cliquez sur le premier marqueur pour terminer et enregistrer", + "polygon-place-first-point-hint" : "Polygone : cliquez pour placer le premier point", + "polygon-place-first-point-hint-with-entity" : "Polygone pour '{{entityName}}' : cliquez pour placer le premier point", + "continue-polygon-hint" : "Polygone : cliquez pour continuer à dessiner", + "continue-polygon-hint-with-entity" : "Polygone pour '{{entityName}}' : cliquez pour continuer à dessiner", + "finish-polygon-hint" : "Polygone : cliquez sur le premier marqueur pour terminer le dessin", + "finish-polygon-hint-with-entity" : "Polygone pour '{{entityName}}' : cliquez sur le premier marqueur pour terminer et enregistrer", + "rectangle-place-first-point-hint" : "Rectangle : cliquez pour placer le premier point", + "rectangle-place-first-point-hint-with-entity" : "Rectangle pour '{{entityName}}' : cliquez pour placer le premier point", + "finish-rectangle-hint" : "Rectangle : cliquez pour terminer le dessin", + "finish-rectangle-hint-with-entity" : "Rectangle pour '{{entityName}}' : cliquez pour terminer et enregistrer" + }, + "circle" : { + "circle-key" : "Clé du cercle", + "circle-key-required" : "Clé du cercle requise", + "no-circles" : "Aucun cercle configuré", + "add-circle" : "Ajouter un cercle", + "circle-configuration" : "Configuration du cercle", + "remove-circle" : "Supprimer le cercle", + "edit" : "Modifier le cercle", + "remove-circle-for" : "Supprimer le cercle pour '{{entityName}}'", + "draw-circle" : "Dessiner un cercle", + "place-circle-center-hint-with-entity" : "Cercle pour '{{entityName}}' : cliquez pour placer le centre", + "place-circle-center-hint" : "Cercle : cliquez pour placer le centre", + "finish-circle-hint-with-entity" : "Cercle pour '{{entityName}}' : cliquez pour terminer et enregistrer le cercle", + "finish-circle-hint" : "Cercle : cliquez pour terminer le dessin" + }, + "select-entity" : "Sélectionner une entité", + "select-entity-hint" : "Astuce : après sélection, cliquez sur la carte pour définir la position" + }, + "select-entity" : "Sélectionner une entité", + "select-entity-hint" : "Astuce : après la sélection, cliquez sur la carte pour définir la position", + "tooltips" : { + "placeMarker" : "Cliquez pour placer l’entité '{{entityName}}'", + "firstVertex" : "Polygone pour '{{entityName}}' : cliquez pour placer le premier point", + "firstVertex-cut" : "Cliquez pour placer le premier point", + "continueLine" : "Polygone pour '{{entityName}}' : cliquez pour continuer à dessiner", + "continueLine-cut" : "Cliquez pour continuer à dessiner", + "finishLine" : "Cliquez sur un marqueur existant pour terminer", + "finishPoly" : "Polygone pour '{{entityName}}' : cliquez sur le premier marqueur pour terminer et enregistrer", + "finishPoly-cut" : "Cliquez sur le premier marqueur pour terminer et enregistrer", + "finishRect" : "Polygone pour '{{entityName}}' : cliquez pour terminer et enregistrer", + "startCircle" : "Cercle pour '{{entityName}}' : cliquez pour placer le centre", + "finishCircle" : "Cercle pour '{{entityName}}' : cliquez pour terminer le cercle", + "placeCircleMarker" : "Cliquez pour placer le marqueur de cercle" + }, + "actions" : { + "finish" : "Terminer", + "cancel" : "Annuler", + "removeLastVertex" : "Supprimer le dernier point" + }, + "buttonTitles" : { + "drawMarkerButton" : "Placer une entité", + "drawPolyButton" : "Créer un polygone", + "drawLineButton" : "Créer une ligne", + "drawCircleButton" : "Créer un cercle", + "drawRectButton" : "Créer un rectangle", + "editButton" : "Mode édition", + "dragButton" : "Mode glisser-déposer", + "cutButton" : "Découper la zone du polygone", + "deleteButton" : "Supprimer", + "drawCircleMarkerButton" : "Créer un marqueur circulaire", + "rotateButton" : "Faire pivoter le polygone" + }, + "map-provider-settings" : "Paramètres du fournisseur de cartes", + "map-provider" : "Fournisseur de cartes", + "map-provider-google" : "Google Maps", + "map-provider-openstreet" : "OpenStreet Maps", + "map-provider-here" : "HERE Maps", + "map-provider-image" : "Carte image", + "map-provider-tencent" : "Tencent Maps", + "openstreet-provider" : "Fournisseur de cartes OpenStreet", + "openstreet-provider-mapnik" : "OpenStreetMap.Mapnik (Par défaut)", + "openstreet-provider-hot" : "OpenStreetMap.HOT", + "openstreet-provider-esri-street" : "Esri.WorldStreetMap", + "openstreet-provider-esri-topo" : "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery" : "Esri.WorldImagery", + "openstreet-provider-cartodb-positron" : "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter" : "CartoDB.DarkMatter", + "use-custom-provider" : "Utiliser un fournisseur personnalisé", + "custom-provider-tile-url" : "URL des tuiles du fournisseur personnalisé", + "google-maps-api-key" : "Clé API Google Maps", + "default-map-type" : "Type de carte par défaut", + "google-map-type-roadmap" : "Plan", + "google-map-type-satelite" : "Satellite", + "google-map-type-hybrid" : "Hybride", + "google-map-type-terrain" : "Relief", + "map-layer" : "Couche de carte", + "here-map-normal-day" : "HERE.normalDay (Par défaut)", + "here-map-normal-night" : "HERE.normalNight", + "here-map-hybrid-day" : "HERE.hybridDay", + "here-map-terrain-day" : "HERE.terrainDay", + "credentials" : "Identifiants", + "here-app-id" : "ID de l'application HERE", + "here-app-code" : "Code de l'application HERE", + "here-api-key" : "Clé API HERE", + "here-use-new-version-api-3" : "Utiliser l'API version 3", + "tencent-maps-api-key" : "Clé API Tencent Maps", + "tencent-map-type-roadmap" : "Plan", + "tencent-map-type-satelite" : "Satellite", + "tencent-map-type-hybrid" : "Hybride", + "image-map-background" : "Fond de carte image", + "image-map-background-from-entity-attribute" : "Utiliser l'attribut d'entité comme fond de carte image", + "image-url-source-entity-alias" : "Alias d'entité source de l'URL de l'image", + "image-url-source-entity-attribute" : "Attribut d'entité source de l'URL de l'image", + "common-map-settings" : "Paramètres communs de la carte", + "x-pos-key-name" : "Nom de la clé de position X", + "y-pos-key-name" : "Nom de la clé de position Y", + "latitude-key-name" : "Nom de la clé de latitude", + "longitude-key-name" : "Nom de la clé de longitude", + "default-map-zoom-level" : "Niveau de zoom par défaut (0 - 20)", + "default-map-center-position" : "Position centrale de la carte par défaut (0,0)", + "disable-scroll-zooming" : "Désactiver le zoom à la molette", + "disable-double-click-zooming" : "Désactiver le zoom au double-clic", + "disable-zoom-control-buttons" : "Désactiver les boutons de zoom", + "fit-map-bounds" : "Ajuster les limites de la carte pour couvrir tous les marqueurs", + "use-default-map-center-position" : "Utiliser la position centrale par défaut de la carte", + "entities-limit" : "Limite d'entités à charger", + "markers-settings" : "Paramètres des marqueurs", + "marker-offset-x" : "Décalage X du marqueur par rapport à la position, multiplié par la largeur du marqueur", + "marker-offset-y" : "Décalage Y du marqueur par rapport à la position, multiplié par la hauteur du marqueur", + "position-function" : "Fonction de conversion de position, doit retourner les coordonnées x,y entre 0 et 1", + "draggable-marker" : "Marqueur déplaçable", + "label" : "Étiquette", + "show-label" : "Afficher l'étiquette", + "use-label-function" : "Utiliser une fonction d'étiquette", + "label-pattern" : "Étiquette (exemples de modèles : '${entityName}', '${entityName}: (Texte ${keyName} unités.)')", + "label-function" : "Fonction d'étiquette", + "tooltip" : "Info-bulle", + "show-tooltip" : "Afficher l'info-bulle", + "show-tooltip-action" : "Action d'affichage de l'info-bulle", + "show-tooltip-action-click" : "Afficher l'info-bulle au clic (Par défaut)", + "show-tooltip-action-hover" : "Afficher l'info-bulle au survol", + "auto-close-tooltips" : "Fermeture automatique des info-bulles", + "use-tooltip-function" : "Utiliser une fonction d'info-bulle", + "tooltip-pattern" : "Info-bulle (ex. : 'Texte ${keyName} unités.' ou Texte du lien)", + "tooltip-function" : "Fonction d'info-bulle", + "tooltip-offset-x" : "Décalage X de l'info-bulle par rapport à l'ancrage du marqueur, multiplié par la largeur du marqueur", + "tooltip-offset-y" : "Décalage Y de l'info-bulle par rapport à l'ancrage du marqueur, multiplié par la hauteur du marqueur", + "color" : "Couleur", + "use-color-function" : "Utiliser une fonction de couleur", + "color-function" : "Fonction de couleur", + "marker-image" : "Image du marqueur", + "use-marker-image-function" : "Utiliser une fonction d'image du marqueur", + "custom-marker-image" : "Image personnalisée du marqueur", + "custom-marker-image-size" : "Taille de l'image personnalisée du marqueur (px)", + "marker-image-function" : "Fonction d'image du marqueur", + "marker-images" : "Images du marqueur", + "polygon-settings" : "Paramètres du polygone", + "show-polygon" : "Afficher le polygone", + "polygon-key-name" : "Nom de la clé du polygone", + "enable-polygon-edit" : "Activer la modification du polygone", + "polygon-label" : "Étiquette du polygone", + "show-polygon-label" : "Afficher l'étiquette du polygone", + "use-polygon-label-function" : "Utiliser une fonction pour l'étiquette du polygone", + "polygon-label-pattern" : "Étiquette du polygone (exemples de modèles : '${entityName}', '${entityName}: (Texte ${keyName} unités.)')", + "polygon-label-function" : "Fonction de l'étiquette du polygone", + "polygon-tooltip" : "Info-bulle du polygone", + "show-polygon-tooltip" : "Afficher l'info-bulle du polygone", + "auto-close-polygon-tooltips" : "Fermeture automatique des info-bulles du polygone", + "use-polygon-tooltip-function" : "Utiliser une fonction pour l'info-bulle du polygone", + "polygon-tooltip-pattern" : "Info-bulle (ex. : 'Texte ${keyName} unités.' ou Texte du lien)", + "polygon-tooltip-function" : "Fonction d'info-bulle du polygone", + "polygon-color" : "Couleur du polygone", + "polygon-opacity" : "Opacité du polygone", + "use-polygon-color-function" : "Utiliser une fonction pour la couleur du polygone", + "polygon-color-function" : "Fonction de couleur du polygone", + "polygon-stroke" : "Contour du polygone", + "stroke-color" : "Couleur du contour", + "stroke-opacity" : "Opacité du contour", + "stroke-weight" : "Épaisseur du contour", + "use-polygon-stroke-color-function" : "Utiliser une fonction pour la couleur du contour", + "polygon-stroke-color-function" : "Fonction de couleur du contour du polygone", + "circle-settings" : "Paramètres du cercle", + "show-circle" : "Afficher le cercle", + "circle-key-name" : "Nom de la clé du cercle", + "enable-circle-edit" : "Activer la modification du cercle", + "circle-label" : "Étiquette du cercle", + "show-circle-label" : "Afficher l'étiquette du cercle", + "use-circle-label-function" : "Utiliser une fonction pour l'étiquette du cercle", + "circle-label-pattern" : "Étiquette du cercle (exemples de modèles : '${entityName}', '${entityName}: (Texte ${keyName} unités.)')", + "circle-label-function" : "Fonction de l'étiquette du cercle", + "circle-tooltip" : "Info-bulle du cercle", + "show-circle-tooltip" : "Afficher l'info-bulle du cercle", + "auto-close-circle-tooltips" : "Fermeture automatique des info-bulles du cercle", + "use-circle-tooltip-function" : "Utiliser une fonction pour l'info-bulle du cercle", + "circle-tooltip-pattern" : "Info-bulle (ex. : 'Texte ${keyName} unités.' ou Texte du lien)", + "circle-tooltip-function" : "Fonction d'info-bulle du cercle", + "circle-fill-color" : "Couleur de remplissage du cercle", + "circle-fill-color-opacity" : "Opacité de la couleur de remplissage du cercle", + "use-circle-fill-color-function" : "Utiliser une fonction pour la couleur de remplissage", + "circle-fill-color-function" : "Fonction de couleur de remplissage du cercle", + "circle-stroke" : "Contour du cercle", + "use-circle-stroke-color-function" : "Utiliser une fonction pour la couleur du contour", + "circle-stroke-color-function" : "Fonction de couleur du contour du cercle", + "markers-clustering-settings" : "Paramètres de regroupement des marqueurs", + "use-map-markers-clustering" : "Utiliser le regroupement des marqueurs sur la carte", + "zoom-on-cluster-click" : "Zoomer lors du clic sur un cluster", + "max-cluster-zoom" : "Niveau de zoom maximum pour inclure un marqueur dans un cluster (0 - 18)", + "max-cluster-radius-pixels" : "Rayon maximal d’un cluster en pixels", + "cluster-zoom-animation" : "Afficher une animation lors du zoom sur les marqueurs", + "show-markers-bounds-on-cluster-mouse-over" : "Afficher les limites des marqueurs au survol d’un cluster", + "spiderfy-max-zoom-level" : "Affichage de type araignée au niveau de zoom max (pour voir tous les marqueurs du cluster)", + "load-optimization" : "Optimisation du chargement", + "cluster-chunked-loading" : "Utiliser des lots pour l’ajout de marqueurs afin d’éviter le gel de la page", + "cluster-markers-lazy-load" : "Utiliser le chargement paresseux pour l’ajout de marqueurs", + "editor-settings" : "Paramètres de l’éditeur", + "enable-snapping" : "Activer l'accrochage aux autres sommets pour un dessin précis", + "init-draggable-mode" : "Initialiser la carte en mode déplaçable", + "hide-all-edit-buttons" : "Masquer tous les boutons de contrôle d’édition", + "hide-draw-buttons" : "Masquer les boutons de dessin", + "hide-edit-buttons" : "Masquer les boutons d’édition", + "hide-remove-button" : "Masquer le bouton de suppression", + "route-map-settings" : "Paramètres de la carte des trajets", + "trip-animation-settings" : "Paramètres de l’animation des trajets", + "normalization-step" : "Étape de normalisation des données (ms)", + "tooltip-background-color" : "Couleur de fond de l'info-bulle", + "tooltip-font-color" : "Couleur de la police de l’info-bulle", + "tooltip-opacity" : "Opacité de l’info-bulle (0-1)", + "auto-close-tooltip" : "Fermeture automatique de l’info-bulle", + "rotation-angle" : "Définir un angle de rotation supplémentaire pour le marqueur (degrés)", + "path-settings" : "Paramètres du chemin", + "path-color" : "Couleur du chemin", + "use-path-color-function" : "Utiliser une fonction de couleur pour le chemin", + "path-color-function" : "Fonction de couleur du chemin", + "path-decorator" : "Décorateur de chemin", + "use-path-decorator" : "Utiliser un décorateur de chemin", + "decorator-symbol" : "Symbole du décorateur", + "decorator-symbol-arrow-head" : "Flèche", + "decorator-symbol-dash" : "Tiret", + "decorator-symbol-size" : "Taille du symbole du décorateur (px)", + "use-path-decorator-custom-color" : "Utiliser une couleur personnalisée pour le décorateur", + "decorator-custom-color" : "Couleur personnalisée du décorateur", + "decorator-offset" : "Décalage du décorateur", + "end-decorator-offset" : "Décalage de fin du décorateur", + "decorator-repeat" : "Répétition du décorateur", + "points-settings" : "Paramètres des points", + "show-points" : "Afficher les points", + "point-color" : "Couleur du point", + "point-size" : "Taille du point (px)", + "use-point-color-function" : "Utiliser une fonction de couleur pour les points", + "point-color-function" : "Fonction de couleur des points", + "use-point-as-anchor" : "Utiliser le point comme ancre", + "point-as-anchor-function" : "Fonction de point comme ancre", + "independent-point-tooltip" : "Info-bulle de point indépendante", + "clustering-markers" : "Regroupement des marqueurs", + "use-icon-create-function" : "Utiliser une fonction de couleur pour les marqueurs", + "marker-color-function" : "Fonction de couleur du marqueur" + }, + "markdown" : { + "use-markdown-text-function" : "Utiliser une fonction de valeur markdown/HTML", + "markdown-text-function" : "Fonction de valeur markdown/HTML", + "markdown-text-pattern" : "Modèle markdown/HTML (markdown ou HTML avec variables, par ex. '${entityName} ou ${keyName} - du texte.')", + "apply-default-markdown-style" : "Appliquer le style markdown par défaut", + "markdown-css" : "CSS pour markdown/HTML" + }, + "simple-card" : { + "label" : "Étiquette", + "label-position" : "Position de l’étiquette", + "label-position-left" : "Gauche", + "label-position-top" : "Haut" + }, + "single-switch" : { + "behavior" : "Comportement", + "layout" : "Disposition", + "layout-right" : "Droite", + "layout-left" : "Gauche", + "layout-centered" : "Centré", + "auto-scale" : "Ajustement automatique", + "label" : "Étiquette", + "icon" : "Icône", + "switch-color" : "Couleur de l’interrupteur", + "on" : "Activé", + "off" : "Désactivé", + "disabled" : "Désactivé", + "tumbler-color" : "Couleur du curseur", + "on-label" : "Étiquette pour Activé", + "off-label" : "Étiquette pour Désactivé", + "switch" : "Interrupteur" + }, + "slider" : { + "behavior" : "Comportement", + "initial-value" : "Valeur initiale", + "initial-value-hint" : "Action pour obtenir la valeur initiale du curseur.", + "on-value-change" : "Au changement de valeur", + "on-value-change-hint" : "Action déclenchée lorsque la valeur du curseur change.", + "layout" : "Disposition", + "layout-default" : "Par défaut", + "layout-extended" : "Étendu", + "layout-simplified" : "Simplifié", + "auto-scale" : "Ajustement automatique", + "icon" : "Icône", + "value" : "Valeur", + "range" : "Plage", + "min" : "min", + "max" : "max", + "range-ticks" : "Graduations", + "tick-marks" : "Repères", + "colors" : "Couleurs", + "main" : "Principal", + "background" : "Arrière-plan", + "left-icon" : "Icône gauche", + "right-icon" : "Icône droite", + "slider" : "Curseur" + }, + "value-card" : { + "layout" : "Disposition", + "layout-square" : "Carré", + "layout-vertical" : "Vertical", + "layout-centered" : "Centré", + "layout-simplified" : "Simplifié", + "layout-horizontal" : "Horizontal", + "layout-horizontal-reversed" : "Horizontal inversé", + "label" : "Étiquette", + "icon" : "Icône", + "value" : "Valeur", + "date" : "Date", + "value-card-style" : "Style de la carte de valeur", + "auto-scale" : "Ajustement automatique" + }, + "label-card" : { + "auto-scale" : "Ajustement automatique", + "label" : "Étiquette", + "icon" : "Icône", + "label-card-style" : "Style de la carte d’étiquette" + }, + "label-value-card" : { + "value" : "Valeur", + "label-value-card-style" : "Style de la carte étiquette & valeur" + }, + "liquid-level-card" : { + "layout-simple" : "Simple", + "layout-percentage" : "Pourcentage", + "layout-absolute" : "Absolu", + "layout" : "Disposition", + "background-overlay" : "Superposition de l'arrière-plan de la valeur", + "total-volume" : "Volume total", + "total-volume-units" : "Unités du volume total", + "tank" : "Réservoir", + "shape" : "Forme", + "datasource-units" : "Unités de la source", + "widget-units" : "Unités du widget", + "decimals" : "Décimales", + "liquid" : "Liquide", + "liquid-color" : "Couleur du liquide", + "value" : "Valeur", + "value-font" : "Police de la valeur", + "level" : "Niveau", + "last-update" : "Dernière mise à jour", + "shape-by-attribute" : "Définir la forme du réservoir via le nom d'attribut", + "tooltip-background" : "Couleur d’arrière-plan", + "background-blur" : "Flou d’arrière-plan", + "tank-color" : "Couleur du réservoir", + "static" : "Statique", + "see-examples" : "Voir des exemples", + "attribute" : "Attribut", + "shape-type" : "Type", + "v-oval" : "Ovale vertical", + "v-cylinder" : "Cylindre vertical", + "v-capsule" : "Capsule verticale", + "rectangle" : "Rectangle", + "h-oval" : "Ovale horizontal", + "h-ellipse" : "Ellipse horizontale", + "h-dish-ends" : "Extrémités concaves horizontales", + "h-cylinder" : "Cylindre horizontal", + "h-capsule" : "Capsule horizontale", + "h-elliptical_2_1" : "Ellipse horizontale 2:1", + "icon" : "Icône de la carte", + "title" : "Titre de la carte", + "units" : "Unités", + "color-and-font" : "Couleur et police", + "shape-attribute-name" : "Nom de l'attribut", + "total-volume-required" : "Le volume total est requis.", + "attribute-name-required" : "Le nom de l'attribut est requis.", + "attribute-key-not-set" : "La clé de l'attribut '{{attributeName}}' n'est pas définie", + "attribute-key-invalid" : "La clé de l'attribut '{{attributeName}}' est invalide" + }, + "aggregated-value-card" : { + "subtitle" : "Sous-titre", + "chart" : "Graphique", + "values" : "Valeurs", + "value-appearance" : "Apparence de la valeur", + "position" : "Position", + "position-center" : "Centre", + "position-right-top" : "En haut à droite", + "position-right-bottom" : "En bas à droite", + "position-left-top" : "En haut à gauche", + "position-left-bottom" : "En bas à gauche", + "font" : "Police", + "color" : "Couleur", + "arrow" : "Flèche", + "display-up-down-arrow" : "Afficher les flèches haut/bas", + "add-value" : "Ajouter une valeur", + "remove-value" : "Supprimer la valeur", + "no-values" : "Aucune valeur configurée", + "aggregation" : "Agrégation", + "aggregated-value-card-style" : "Style de carte de valeur agrégée", + "auto-scale" : "Mise à l'échelle automatique" }, - "customer": { - "add": "Ajouter un client", - "add-customer-text": "Ajouter un nouveau client", - "assets": "Actifs du client", - "copyId": "Copier l'id du client", - "customer": "Client", - "customer-details": "Détails du client", - "customer-required": "Le client est requis", - "customers": "Clients", - "dashboard": "Tableau de bord du client", - "dashboards": "tableaux de bord du client", - "default-customer": "Client par défaut", - "default-customer-required": "Le client par défaut est requis pour déboguer le tableau de bord au niveau du Tenant", - "delete": "Supprimer le client", - "delete-customer-text": "Faites attention, après la confirmation, le client et toutes les données associées deviendront irrécupérables.", - "delete-customer-title": "Êtes-vous sûr de vouloir supprimer le client '{{customerTitle}}'?", - "delete-customers-action-title": "Supprimer {count, plural, =1 {1 customer} other {# customers} }", - "delete-customers-text": "Faites attention, après la confirmation, tous les clients sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-customers-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 customer} other {# customers} }?", - "description": "Description", - "details": "Détails", - "devices": "Dispositifs du client", - "edges": "Instances Edge du client", - "entity-views": "Vues de l'entité client", - "events": "Événements", - "idCopiedMessage": "L'Id du client a été copié dans le presse-papier", - "manage-assets": "Gérer les actifs", - "manage-customer-assets": "Gérer les actifs du client", - "manage-customer-edges": "Gérer les bordures du client", - "manage-customer-dashboards": "Gérer les tableaux de bord du client", - "manage-customer-devices": "Gérer les dispositifs du client", - "manage-customer-users": "Gérer les utilisateurs du client", - "manage-dashboards": "Gérer les tableaux de bord", - "manage-devices": "Gérer les dispositifs", - "manage-public-assets": "Gérer les actifs publics", - "manage-public-dashboards": "Gérer les tableaux de bord publics", - "manage-public-devices": "Gérer les dispositifs publics", - "manage-public-edges": "Gérer les bordures publics", - "manage-users": "Gérer les utilisateurs", - "management": "Gestion des clients", - "no-customers-matching": "Aucun client correspondant à '{{entity}} n'a été trouvé.", - "no-customers-text": "Aucun client trouvé", - "public-assets": "Actifs publics", - "public-dashboards": "Tableaux de bord publics", - "public-devices": "Dispositifs publics", - "public-entity-views": "Vues d'entités publiques", - "public-edges": "Bordures publics", - "select-customer": "Sélectionner un client", - "select-default-customer": "Sélectionnez le client par défaut", - "title": "Titre", - "title-required": "Le titre est requis.", - "title-max-length": "La longueur du titre doit être moins que 256", - "search": "Rechercher clients", - "selected-customers": "{ count, plural, =1 {1 customer} other {# customers} } sélectionnés", - "manage-edges": "Gérer les edges" - }, - "dashboard": { - "add": "Ajouter un tableau de bord", - "add-dashboard-text": "Ajouter un nouveau tableau de bord", - "add-state": "Ajouter un état du tableau de bord", - "add-widget": "Ajouter un nouveau widget", - "alias-resolution-error-title": "Erreur de configuration des alias de tableau de bord", - "assign-dashboard-to-customer": "Attribuer des tableaux de bord au client", - "assign-dashboard-to-customer-text": "Veuillez sélectionner les tableaux de bord à attribuer au client", - "assign-dashboards": "Attribuer des tableaux de bord", - "assign-dashboards-text": "Attribuer {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } aux clients", - "assign-new-dashboard": "Attribuer un nouveau tableau de bord", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner le client pour attribuer le ou les tableaux de bord", - "assign-to-customers": "Attribuer des tableaux de bord aux clients", - "assign-to-customers-text": "Veuillez sélectionner les clients pour attribuer les tableaux de bord", - "assigned-customers": "clients attribués", - "assignedToCustomer": "Attribué au client", - "assignedToCustomers": "Attribué aux clients", - "autofill-height": "Hauteur de remplissage automatique", - "background-color": "Couleur de fond", - "background-image": "Image d'arriére-plan", - "background-size-mode": "Mode de taille d'arriére-plan", - "close-toolbar": "Fermer la barre d'outils", - "columns-count": "Nombre de colonnes", - "columns-count-required": "Le nombre de colonnes est requis.", - "configuration-error": "Erreur de configuration", - "copy-public-link": "Copier le lien public", - "create-new": "Créer un nouveau tableau de bord", - "create-new-dashboard": "Créer un nouveau tableau de bord", - "create-new-widget": "Créer un nouveau widget", - "dashboard": "Tableau de bord", - "dashboard-details": "Détails du tableau de bord", - "dashboard-file": "Fichier du tableau de bord", - "dashboard-import-missing-aliases-title": "Configurer les alias utilisés par le tableau de bord importé", - "dashboard-required": "Le tableau de bord est requis.", - "dashboards": "Tableaux de bord", - "delete": "Supprimer le tableau de bord", - "delete-dashboard-text": "Faites attention, après la confirmation, le tableau de bord et toutes les données associées deviendront irrécupérables.", - "delete-dashboard-title": "Êtes-vous sûr de vouloir supprimer le tableau de bord '{{dashboardTitle}}'?", - "delete-dashboards": "Supprimer les tableaux de bord", - "delete-dashboards-action-title": "Supprimer {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} }", - "delete-dashboards-text": "Attention, après la confirmation, tous les tableaux de bord sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-dashboards-title": "Voulez-vous vraiment supprimer {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} }?", - "delete-state": "Supprimer l'état du tableau de bord", - "delete-state-text": "Etes-vous sûr de vouloir supprimer l'état du tableau de bord avec le nom '{{stateName}}'?", - "delete-state-title": "Supprimer l'état du tableau de bord", - "description": "Description", - "details": "Détails", - "display-dashboard-export": "Afficher l'exportation", - "display-dashboard-timewindow": "Afficher la fenêtre de temps", - "display-dashboards-selection": "Afficher la sélection des tableaux de bord", - "display-entities-selection": "Afficher la sélection des entités", - "display-title": "Afficher le titre du tableau de bord", - "drop-image": "Déposer une image ou cliquez pour sélectionner un fichier à télécharger.", - "edit-state": "Modifier l'état du tableau de bord", - "export": "Exporter le tableau de bord", - "export-failed-error": "Impossible d'exporter le tableau de bord: {{error}}", - "hide-details": "Masquer les détails", - "horizontal-margin": "Marge horizontale", - "horizontal-margin-required": "Une valeur de marge horizontale est requise.", - "import": "Importer le tableau de bord", - "import-widget": "Importer un widget", - "invalid-aliases-config": "Impossible de trouver des dispositifs correspondant à certains filtres d'alias.
Veuillez contacter votre administrateur pour résoudre ce problème.", - "invalid-dashboard-file-error": "Impossible d'importer le tableau de bord: structure de données du tableau de bord non valide", - "invalid-widget-file-error": "Impossible d'importer le widget: structure de données de widget invalide.", - "is-root-state": "État racine", - "make-private": "Rendre privé le tableau de bord", - "make-private-dashboard": "Rendre privé le tableau de bord", - "make-private-dashboard-text": "Après la confirmation, le tableau de bord sera rendu privé et ne sera plus accessible aux autres.", - "make-private-dashboard-title": "Êtes-vous sûr de vouloir rendre le tableau de bord '{{dashboardTitle}}' privé?", - "make-public": "Rendre public le tableau de bord", - "manage-assigned-customers": "Gérer les clients attribués", - "manage-states": "Gérer les états du tableau de bord", - "management": "Gestion du tableau de bord", - "max-columns-count-message": "Seulement 1000 colonnes maximum sont autorisées.", - "max-horizontal-margin-message": "50 est la valeur de marge horizontale maximale.", - "max-mobile-row-height-message": "200 pixels est la valeur maximale de hauteur de ligne mobile.", - "max-vertical-margin-message": "50 est la valeur de marge verticale maximale.", - "min-columns-count-message": "Seul un nombre minimum de 10 colonnes est autorisé.", - "min-horizontal-margin-message": "0 est la valeur de marge horizontale minimale.", - "min-mobile-row-height-message": "5 pixels est la valeur minimale de hauteur de ligne mobile.", - "min-vertical-margin-message": "0 est la valeur de marge verticale minimale.", - "mobile-layout": "Paramètres de mise en page mobiles", - "mobile-row-height": "Hauteur de ligne mobile, px", - "mobile-row-height-required": "Une valeur de hauteur de ligne mobile est requise.", - "new-dashboard-title": "Nouveau titre du tableau de bord", - "no-dashboards-matching": "Aucun tableau de bord correspondant à {{entity}} n'a été trouvé. ", - "no-dashboards-text": "Aucun tableau de bord trouvé", - "no-image": "Aucune image sélectionnée", - "no-widgets": "Aucun widget configuré", - "open-dashboard": "Ouvrir le tableau de bord", - "open-toolbar": "Ouvrir la barre d'outils du tableau de bord", - "public": "Public", - "public-dashboard-notice": " Remarque: N'oubliez pas de rendre publics les dispositifs associés pour accéder à leurs données.", - "public-dashboard-text": "Votre tableau de bord {{dashboardTitle}} est maintenant public et accessible via le lien public : ", - "public-dashboard-title": "Le tableau de bord est maintenant public", - "public-link": "Lien public", - "public-link-copied-message": "Le lien public du tableau de bord a été copié dans le presse-papier", - "search-states": "Recherche des états du tableau de bord", - "select-dashboard": "Sélectionner le tableau de bord", - "select-devices": "Selectionner les dispositifs", - "select-existing": "Sélectionnez un tableau de bord existant", - "select-state": "Sélectionnez l'état cible", - "select-widget-subtitle": "Liste des types de widgets disponibles", - "select-widget-title": "Sélectionner un widget", - "selected-states": "{count, plural, =1 {1 état du tableau de bord} other {# états du tableau de bord} } sélectionnés", - "set-background": "Définir l'arrière-plan", - "settings": "Paramètres", - "show-details": "Afficher les détails", - "socialshare-text": "'{{dashboardTitle}}' propulsé par ThingsBoard", - "socialshare-title": "'{{dashboardTitle}}' propulsé par ThingsBoard", - "state": "État du tableau de bord", - "state-controller": "Contrôleur d'état", - "state-id": "ID d'état", - "state-id-exists": "L'état du tableau de bord avec le même Id existe déjà.", - "state-id-required": "L'Id d'état du tableau de bord est requis.", - "state-name": "Nom", - "state-name-required": "Le nom de l'état du tableau de bord est requis", - "states": "États du tableau de bord", - "title": "Titre", - "title-color": "Couleur du titre", - "title-required": "Le titre est requis.", - "toolbar-always-open": "Garder la barre d'outils ouverte", - "unassign-dashboard": "Retirer le tableau de bord", - "unassign-dashboard-text": "Après la confirmation, le tableau de bord ne sera pas attribué et ne sera pas accessible au client.", - "unassign-dashboard-title": "Êtes-vous sûr de vouloir annuler l'affectation du tableau de bord '{{dashboardTitle}}'?", - "unassign-dashboards": "Retirer les tableaux de bord", - "unassign-dashboards-action-text": "Annuler l'affectation {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } des clients", - "unassign-dashboards-action-title": "Annuler l'affectation {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } du client", - "unassign-dashboards-text": "Après la confirmation, tous les tableaux de bord sélectionnés ne seront pas attribués et ne seront pas accessibles au client.", - "unassign-dashboards-title": "Etes-vous sûr de vouloir annuler l'affectation {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} }?", - "unassign-from-customer": "Retirer du client", - "unassign-from-customers": "Retirer les tableaux de bord des clients", - "unassign-from-customers-text": "Veuillez sélectionner les clients à annuler l'attribution du ou des tableaux de bord", - "vertical-margin": "Marge verticale", - "vertical-margin-required": "Une valeur de marge verticale est requise", - "view-dashboards": "Afficher les tableaux de bord", - "widget-file": "Fichier du Widget", - "widget-import-missing-aliases-title": "Configurer les alias utilisés par le widget importé", - "widgets-margins": "Marge entre les widgets", - "unassign-dashboard-from-edge-text": "Après la confirmation, tableau de bord sera non attribué et ne sera pas accessible a la bordure.", - "unassign-dashboards-from-edge-text": "Après la confirmation, tous les tableaux de bord sélectionnés ne seront pas attribués et ne seront pas accessibles a la bordure.", - "assign-dashboard-to-edge": "Attribuer des tableaux de bord a la bordure", - "assign-dashboard-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les tableaux de bord", - "image": "Image du tableau de bord", - "mobile-app-settings": "paramètres de l'application mobile", - "mobile-order": "Ordre du tableau de bord dans l'application mobile", - "mobile-hide": "Cacher le tableau de bord dans l'application mobile", - "update-image": "Mettre à jour l'image du tableau de bord", - "take-screenshot": "Prendre une capture d'écran", - "select-widget-value": "{{title}}: sélectionner widget", - "title-max-length": "La longueur du titre doit être mpoins de 256", - "empty-image": "Aucune image", - "maximum-upload-file-size": "Taille de fichier maximum: {{ size }}", - "cannot-upload-file": "Le téléchargement a échoué", - "margin-required": "Valeur de marge requise.", - "min-margin-message": "0 est la valeur minimum permise.", - "max-margin-message": "50 est la valeur maximum permise.", - "title-settings": "Paramètres du titre", - "toolbar-settings": "Paramètres de la barre d'outils", - "hide-toolbar": "Masquer la barre d'outils", - "display-filters": "Afficher les filtres", - "display-update-dashboard-image": "Afficher l'image du tableau de bord de mise à jour", - "dashboard-logo-settings": "Paramètres du logo du tableau de bord", - "display-dashboard-logo": "Afficher le logo en mode plein écran", - "dashboard-logo-image": "Image du logo du tableau de bord", - "advanced-settings": "Paramètres avancés", - "dashboard-css": "CSS du tableau de bord", - "no-states-text": "Aucun état trouvé", - "search": "Rechercher des tableaux de bord", - "selected-dashboards": "{ count, plural, =1 {1 dashboard} other {# dashboards} } sélectionné", - "home-dashboard": "Tableau de bord d'accueil", - "home-dashboard-hide-toolbar": "Masquer la barre d'outils du tableau de bord d'accueil", - "unassign-dashboards-from-edge-title": "Êtes-vous certain de vouloir désattribuer { count, plural, =1 {1 dashboard} other {# dashboards} }?", - "non-existent-dashboard-state-error": "L'état du tableau de bord avec ID \"{{ stateId }}\" non trouvé" - }, - "datakey": { - "advanced": "Avancé", - "alarm": "Champs d'alarme", - "alarm-fields-required": "Les champs d'alarme sont obligatoires.", - "attributes": "Attributs", - "color": "Couleur", - "configuration": "Configuration de la clé de données", - "data-generation-func": "Fonction de génération de données", - "decimals": "Nombre de chiffres après virgule flottante", - "function-types": "Types de fonctions", - "function-types-required": "Les types de fonctions sont obligatoires", - "label": "Label", - "maximum-function-types": "Maximum {count, plural, =1 {1 type de fonction est autorisé.} other {# types de fonctions sont autorisés} }", - "maximum-timeseries-or-attributes": "Maximum {count, plural, =1 {1 timeseries / attribut est autorisé.} other {# timeseries / attributs sont autorisés} }", - "prev-orig-value-description": "valeur précédente d'origine;", - "prev-value-description": "résultat de l'appel de fonction précédent;", - "settings": "Paramètres", - "time-description": "horodatage de la valeur actuelle;", - "time-prev-description": "horodatage de la valeur précédente;", - "timeseries": "Timeseries", - "timeseries-or-attributes-required": "Les timeseries / attributs d'entité sont obligatoires.", - "timeseries-required": "Les Timeseries de l'entité sont obligatoires.", - "units": "Symbole spécial à afficher à côté de la valeur", - "use-data-post-processing-func": "Utiliser la fonction de post-traitement des données", - "value-description": "la valeur actuelle;", - "entity-field": "Champs d'entité", - "alarm-fields-timeseries-or-attributes-required": "Les champs d'alarmes ou l'entité timeseries/attributs sont requis." - }, - "datasource": { - "add-datasource-prompt": "Veuillez ajouter une source de données", - "name": "Nom", - "type": "Type de source de données" - }, - "datetime": { - "date-from": "Date de", - "date-to": "Date à", - "time-from": "Heure de", - "time-to": "Heure à" - }, - "details": { - "edit-mode": "Mode édition", - "toggle-edit-mode": "Activer le mode édition", - "details": "Détails", - "edit-json": "Éditer JSON" - }, - "device": { - "access-token": "Jeton d'accès", - "access-token-invalid": "La longueur du jeton d'accès doit être comprise entre 1 et 32 caractéres.", - "access-token-required": "Le jeton d'accès est requis.", - "accessTokenCopiedMessage": "Le jeton d'accès au dispositif a été copié dans le presse-papier", - "add": "Ajouter un dispositif", - "add-alias": "Ajouter un alias de dispositif", - "add-device-text": "Ajouter un nouveau dispositif", - "alias": "Alias", - "alias-required": "Un alias du dispositif est requis.", - "aliases": "Alias des dispositifs", - "any-device": "N'importe quel dispositif", - "assign-device-to-customer": "Attribuer des dispositifs au client", - "assign-device-to-customer-text": "Veuillez sélectionner les dispositif à affecter au client", - "assign-devices": "Attribuer des dispositifs", - "assign-devices-text": "Attribuer {count, plural, =1 {1 dispositif} other {# dispositifs} } au client", - "assign-new-device": "Attribuer un nouveau dispositif", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner le client pour attribuer le ou les dispositifs", - "assignedToCustomer": "Attribué au client", - "configure-alias": "Configurer alias '{{alias}}'", - "copyAccessToken": "Copier le jeton d'accès", - "copyId": "Copier l'Id du dispositif", - "create-new-alias": "Créez un nouveau!", - "create-new-key": "Créez un nouveau!", - "credentials": "Informations d'identification", - "credentials-type": "Type d'identification", - "delete": "Supprimer le dispositif", - "delete-device-text": "Faites attention, après la confirmation, le dispositif et toutes les données associées deviendront irrécupérables.", - "delete-device-title": "Êtes-vous sûr de vouloir supprimer le dispositif '{{deviceName}}'?", - "delete-devices": "Supprimer les dispositifs", - "delete-devices-action-title": "Supprimer {count, plural, =1 {1 device} other {# devices} }", - "delete-devices-text": "Faites attention, après la confirmation, tous les dispositifs sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-devices-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 device} other {# devices} }?", - "description": "Description", - "details": "Détails", - "device": "Dispositif", - "device-alias": "Alias ​​du dispositif", - "device-credentials": "Informations d'identification du dispositif", - "device-details": "Détails du dispositif", - "device-list": "Liste des dispositifs", - "device-list-empty": "Aucun dispositif sélectionné.", - "device-name-filter-no-device-matched": "Aucun dispositif commençant par '{{device}} n'a été trouvé.", - "device-name-filter-required": "Le filtre de nom de dispositif est requis.", - "device-public": "Le dispositif est public", - "device-required": "Le dispositif est requis.", - "device-type": "Type de dispositif", - "device-type-list-empty": "Aucun type de dispositif sélectionné.", - "device-type-required": "Le type de dispositif est requis.", - "device-types": "Types de dispositif", - "devices": "Dispositifs", - "duplicate-alias-error": "Alias '{{alias}}' existe déjà.
Les alias de dispositifs doivent être uniques dans le tableau de bord.", - "enter-device-type": "Entrez le type de dispositif", - "events": "Événements", - "idCopiedMessage": "l'Id du dispositif a été copié dans le presse-papiers", - "is-gateway": "Est une passerelle", - "label": "Label", - "make-private": "Rendre le dispositif privé", - "make-private-device-text": "Après la confirmation, le dispositif et toutes ses données seront rendues privées et ne seront pas accessibles par d'autres.", - "make-private-device-title": "Êtes-vous sûr de vouloir rendre le dispositif {{deviceName}} privé?", - "make-public": "Rendre le dispositif public", - "make-public-device-text": "Après la confirmation, le dispositif et toutes ses données seront rendus publics et accessibles par d'autres.", - "make-public-device-title": "Êtes-vous sûr de vouloir rendre le dispositif {{deviceName}} 'public?", - "manage-credentials": "Gérer les informations d'identification", - "management": "Gestion des dispositifs", - "name": "Nom", - "name-required": "Le nom est requis.", - "name-starts-with": "Le nom du dispositif commence par", - "no-alias-matching": "'{{alias}}' introuvable.", - "no-aliases-found": "Aucun alias trouvé.", - "no-device-types-matching": "Aucun type de dispositif correspondant à {{entitySubtype}} n'a été trouvé.", - "no-devices-matching": "Aucun dispositif correspondant à '{{entity}} n'a été trouvé.", - "no-devices-text": "Aucun dispositif trouvé", - "no-key-matching": "'{{key}}' introuvable.", - "no-keys-found": "Aucune clé trouvée", - "public": "Public", - "remove-alias": "Supprimer l'alias du dispositif", - "secret": "Secret", - "secret-required": "Code secret est requis.", - "select-device": "Selectionner un dispositif", - "select-device-type": "Sélectionner le type d'appareil", - "unable-delete-device-alias-text": "L'alias du dispositif '{{deviceAlias}}' ne peut pas être supprimé car il est utilisé par les widgets suivants:
{{widgetsList}}", - "unable-delete-device-alias-title": "Impossible de supprimer l'alias du dispositif", - "unassign-device": "Annuler l'affectation du dispositif", - "unassign-device-text": "Après la confirmation, le dispositif ne sera pas attribué et ne sera pas accessible au client.", - "unassign-device-title": "Êtes-vous sûr de vouloir annuler l'affection du dispositif {{deviceName}} '?", - "unassign-devices": "Annuler l'affectation des dispositifs", - "unassign-devices-action-title": "Annuler l'affectation de {count, plural, =1 {1 device} other {#devices} } du client", - "unassign-devices-text": "Après la confirmation, tous les dispositifs sélectionnés ne seront pas attribues et ne seront pas accessibles par le client.", - "unassign-devices-title": "Voulez-vous vraiment annuler l'affectation de {count, plural, =1 {1 device} other {# devices} }?", - "unassign-from-customer": "Retirer du client", - "use-device-name-filter": "Utiliser le filtre", - "view-credentials": "Afficher les informations d'identification", - "view-devices": "Afficher les dispositifs", - "assign-device-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", - "unassign-device-from-edge-title": "Êtes-vous sûr de vouloir annuler l'affection du dispositif {{deviceName}} '?", - "unassign-device-from-edge-text": "Après la confirmation, dispositif sera non attribué et ne sera pas accessible a la bordure.", - "unassign-devices-from-edge-title": "Voulez-vous vraiment annuler l'attribution de {count, plural, =1 {1 device} other {# devices} }?", - "unassign-devices-from-edge-text": "Après la confirmation, tous les dispositifs sélectionnés ne seront pas attribues et ne seront pas accessibles par la bordure.", - "device-type-max-length": "La longueur du type de dispositif doit être moins de 256", - "help-text": "Utilisez '%' au besoin: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", - "unassign-devices-from-edge": "Désattribuer les dispositifs du edge", - "loading-device-credentials": "Chargement des informations d'identification du dispositf...", - "certificate-pem-format": "Certificat en format PEM", - "certificate-pem-format-required": "Certificat requis.", - "lwm2m-security-config": { - "identity": "Identité du client", - "identity-required": "Identité du client requise.", - "identity-tooltip": "L'identifiant PSK est un identifiant PSK arbitraire d'une longueur maximum de 128 octets, tel qu'indiqué au standard [RFC7925].\nL'identifiant PSK DOIT d'abord être converti en format texte puis encodé en octets utilisant la norme UTF-8.", - "client-key": "Clé du client", - "client-key-required": "Clé du client est requise.", - "client-key-tooltip-prk": "Clé publique RPK ou ID doivent être en format standard [RFC7250] et encodés en format Base64!", - "client-key-tooltip-psk": "Clé PSK doit être dans le standard [RFC4279] et en format HexDec: 32, 64, 128 caractères!", - "endpoint": "Nom du client Endpoint", - "endpoint-required": "Nom du client Endpoint est requis.", - "client-public-key": "Clé publique du client", - "client-public-key-hint": "Si la clé publique du client est vide, le certificat sera utilisé", - "client-public-key-tooltip": "La clé publique X509 doit être en format X509v3 DES-encodé et supporter exclusivement l'algorithme EC puis être encodé en format Base64!", - "mode": "Mode configuration de sécurité", - "client-tab": "Config de la sécutité du client", - "client-certificate": "Certificat du client", - "bootstrap-tab": "Client Bootstrap", - "bootstrap-server": "Serveur Bootstrap", - "lwm2m-server": "Serveur LwM2M", - "client-publicKey-or-id": "Clé publique du client ou ID", - "client-publicKey-or-id-required": "Clé publique du client ou ID requise", - "client-publicKey-or-id-tooltip-psk": "L'identifiant PSK est un identifiant PSK arbitraire d'une longueur maximum de 128 octets, tel qu'indiqué au standard [RFC7925].\nL'identifiant PSK DOIT d'abord être converti en format texte puis encodé en octets utilisant la norme UTF-8.", - "client-publicKey-or-id-tooltip-rpk": "Clé publique RPK ou ID doivent être en format standard [RFC7250] et encodés en format Base64!", - "client-publicKey-or-id-tooltip-x509": "La clé publique X509 doit être en format X509v3 DES-encodé et supporter exclusivement l'algorithme EC puis être encodé en format Base64!", - "client-secret-key": "Clé secrète du client", - "client-secret-key-required": "Clé secrète du client est requise.", - "client-secret-key-tooltip-psk": "La clé PSK doit être en format stadard [RFC4279] et en format HexDec: 32, 64, 128 caractères!", - "client-secret-key-tooltip-prk": "La clé secrète RPK doit être en format PKCS_8 (DER encodée, standard [RFC5958]) puis encodée en format Base64!", - "client-secret-key-tooltip-x509": "La clé secrète X509 doit être en format PKCS_8 (DER encodée, standard [RFC5958]) puis encodée en format Base64!" + "value-chart-card" : { + "layout" : "Disposition", + "layout-left" : "Gauche", + "layout-right" : "Droite", + "auto-scale" : "Mise à l'échelle automatique", + "icon" : "Icône", + "value" : "Valeur", + "chart" : "Graphique", + "value-chart-card-style" : "Style de carte de tableau de valeurs" + }, + "progress-bar" : { + "layout" : "Disposition", + "layout-default" : "Par défaut", + "layout-simplified" : "Simplifiée", + "auto-scale" : "Mise à l'échelle automatique", + "icon" : "Icône", + "value" : "Valeur", + "range" : "Plage", + "min" : "min", + "max" : "max", + "range-ticks" : "Graduations de la plage", + "bar" : "Barre", + "bar-color" : "Couleur de la barre", + "bar-background" : "Fond de la barre", + "progress-bar-card-style" : "Style de carte barre de progression" + }, + "notification" : { + "max-notification-display" : "Nombre maximum de notifications à afficher", + "counter" : "Compteur", + "counter-hint" : "Le compteur sera affiché si le \"Titre du widget\" est activé", + "icon" : "Icône", + "counter-value" : "Valeur", + "counter-color" : "Couleur", + "notification-button" : "Boutons de notification", + "button-view-all" : "Tout voir", + "button-filter" : "Filtrer", + "type-filter" : "Filtre de type", + "button-mark-read" : "Tout marquer comme lu", + "notification-types" : "Types de notification", + "notification-type" : "Type de notification", + "search-type" : "Type de recherche", + "any-type" : "Tout type" + }, + "alarm-count" : { + "alarm-count-card-style" : "Style de carte de compteur d'alarmes" + }, + "entity-count" : { + "entity-count-card-style" : "Style de carte de compteur d'entités" + }, + "count" : { + "layout" : "Disposition", + "layout-column" : "Colonne", + "layout-row" : "Ligne", + "label" : "Étiquette", + "icon" : "Icône", + "icon-background" : "Fond de l'icône", + "value" : "Valeur", + "chevron" : "Chevron", + "auto-scale" : "Mise à l'échelle automatique" + }, + "table" : { + "common-table-settings" : "Paramètres généraux du tableau", + "enable-search" : "Activer la recherche", + "enable-sticky-header" : "Toujours afficher l'en-tête", + "enable-sticky-action" : "Toujours afficher la colonne des actions", + "hidden-cell-button-display-mode" : "Mode d'affichage des actions des boutons de cellule masqués", + "show-empty-space-hidden-action" : "Afficher un espace vide à la place du bouton d'action masqué", + "dont-reserve-space-hidden-action" : "Ne pas réserver d'espace pour les boutons d'action masqués", + "display-timestamp" : "Horodatage", + "display-pagination" : "Afficher la pagination", + "default-page-size" : "Taille de page par défaut", + "page-step-settings" : "Paramètres de pas de page", + "page-step-count" : "Nombre d'étapes", + "page-step-increment" : "Incrément de pas", + "page-step-count-format-message" : "Doit être une valeur entière, comprise entre 1 et 100.", + "page-step-increment-format-message" : "Doit être une valeur entière supérieure ou égale à 1.", + "use-entity-label-tab-name" : "Utiliser le libellé de l'entité dans le nom de l'onglet", + "hide-empty-lines" : "Masquer les lignes vides", + "row-style" : "Style de ligne", + "use-row-style-function" : "Utiliser une fonction de style de ligne", + "row-style-function" : "Fonction de style de ligne", + "cell-style" : "Style de cellule", + "use-cell-style-function" : "Utiliser une fonction de style de cellule", + "cell-style-function" : "Fonction de style de cellule", + "cell-content" : "Contenu de la cellule", + "use-cell-content-function" : "Utiliser une fonction de contenu de cellule", + "cell-content-function" : "Fonction de contenu de cellule", + "show-latest-data-column" : "Afficher la colonne de données les plus récentes", + "latest-data-column-order" : "Ordre de la colonne de données les plus récentes", + "entities-table-title" : "Titre du tableau des entités", + "enable-select-column-display" : "Activer la sélection des colonnes à afficher", + "display-entity-name" : "Afficher la colonne du nom de l'entité", + "entity-name-column-title" : "Titre de la colonne du nom de l'entité", + "display-entity-label" : "Afficher la colonne du libellé de l'entité", + "entity-label-column-title" : "Titre de la colonne du libellé de l'entité", + "display-entity-type" : "Afficher la colonne du type d'entité", + "default-sort-order" : "Ordre de tri par défaut", + "custom-title" : "Titre personnalisé de l'en-tête", + "column-width" : "Largeur de colonne (px ou %)", + "default-column-visibility" : "Visibilité par défaut de la colonne", + "column-visibility-visible" : "Visible", + "column-visibility-hidden" : "Masquée", + "column-visibility-hidden-mobile" : "Masquée en mode mobile", + "column-selection-to-display" : "Sélection de colonne dans 'Colonnes à afficher'", + "column-selection-to-display-enabled" : "Activée", + "column-selection-to-display-disabled" : "Désactivée", + "alarms-table-title" : "Titre du tableau des alarmes", + "enable-alarms-selection" : "Activer la sélection des alarmes", + "enable-alarms-search" : "Activer la recherche d'alarmes", + "enable-alarm-filter" : "Activer le filtre d'alarme", + "display-alarm-details" : "Afficher les détails de l'alarme", + "allow-alarms-ack" : "Autoriser l'accusé de réception des alarmes", + "allow-alarms-clear" : "Autoriser l'effacement des alarmes", + "display-alarm-activity" : "Afficher l'activité de l'alarme", + "allow-alarms-assign" : "Autoriser l'assignation des alarmes", + "columns" : "Colonnes", + "column-settings" : "Paramètres des colonnes", + "remove-column" : "Supprimer la colonne", + "add-column" : "Ajouter une colonne", + "no-columns" : "Aucune colonne configurée", + "columns-to-display" : "Colonnes à afficher", + "table-header" : "En-tête du tableau", + "header-buttons" : "Boutons d'en-tête", + "table-buttons" : "Boutons du tableau", + "pagination" : "Pagination", + "rows" : "Lignes", + "timeseries-column-error" : "Au moins une colonne de séries temporelles doit être spécifiée", + "alarm-column-error" : "Au moins une colonne d'alarme doit être spécifiée", + "table-tabs" : "Onglets du tableau", + "show-cell-actions-menu-mobile" : "Afficher le menu déroulant d'actions des cellules en mode mobile", + "disable-sorting" : "Désactiver le tri" + }, + "latest-chart" : { + "total" : "Total", + "auto-scale" : "Mise à l'échelle automatique", + "clockwise-layout" : "Disposition dans le sens horaire", + "sort-series" : "Trier les séries par libellé", + "tooltip-value-type-absolute" : "Absolu", + "tooltip-value-type-percentage" : "Pourcentage" + }, + "pie-chart" : { + "pie-chart-appearance" : "Apparence du graphique en secteurs", + "label" : "Libellé", + "border" : "Bordure", + "radius" : "Rayon", + "pie-chart-card-style" : "Style de la carte graphique en secteurs" + }, + "radar-chart" : { + "radar-appearance" : "Apparence du radar", + "shape" : "Forme", + "shape-polygon" : "Polygone", + "shape-circle" : "Cercle", + "color" : "Couleur", + "line" : "Ligne", + "points" : "Points", + "points-label" : "Libellé des points", + "radar-axis" : "Axe radar", + "axis-label" : "Libellé de l'axe", + "ticks-label" : "Libellé des graduations", + "radar-chart-style" : "Style du graphique radar" + }, + "time-series-chart" : { + "chart" : "Graphique", + "chart-style" : "Style du graphique", + "data-zoom" : "Zoom sur les données", + "stack-mode" : "Mode empilé", + "stack-mode-hint" : "Empile les séries sur le graphique. Les séries ayant la même unité seront superposées.", + "axes" : "Axes", + "y-axes" : "Axes Y", + "line-type" : "Type de ligne", + "line-width" : "Épaisseur de ligne", + "type-line" : "Ligne", + "type-bar" : "Barre", + "type-point" : "Point", + "no-aggregation-bar-width-strategy" : "Stratégie de largeur de barre pour les données non agrégées", + "no-aggregation-bar-width-strategy-group" : "Grouper", + "no-aggregation-bar-width-strategy-separate" : "Séparer", + "bar-group-width" : "Largeur du groupe de barres", + "bar-width" : "Largeur de la barre", + "bar-width-relative" : "Pourcentage de la fenêtre temporelle", + "bar-width-absolute" : "Absolue (ms)", + "comparison" : { + "comparison" : "Comparaison", + "comparison-hint" : "La comparaison fonctionne uniquement avec les données historiques !", + "show" : "Afficher", + "settings" : "Paramètres de comparaison", + "show-values-for-comparison" : "Afficher les données historiques pour la comparaison", + "comparison-values-label" : "Libellé de la clé de comparaison", + "comparison-values-label-auto" : "Auto", + "comparison-data-color" : "Couleur des données de comparaison" + }, + "threshold" : { + "thresholds" : "Seuils", + "source" : "Source", + "key-value" : "Clé / Valeur", + "no-thresholds" : "Aucun seuil configuré", + "add-threshold" : "Ajouter un seuil", + "type-constant" : "Constante", + "type-latest-key" : "Clé", + "type-entity" : "Entité", + "threshold-settings" : "Paramètres des seuils", + "remove-threshold" : "Supprimer le seuil", + "threshold-value-required" : "La valeur du seuil est requise.", + "key-required" : "La clé est requise.", + "entity-key-required" : "La clé de l'entité est requise.", + "line-appearance" : "Apparence de la ligne", + "line-color" : "Couleur de la ligne", + "start-symbol" : "Symbole de début", + "end-symbol" : "Symbole de fin", + "symbol-size" : "Taille", + "label" : "Libellé", + "label-position-start" : "Début", + "label-position-middle" : "Milieu", + "label-position-end" : "Fin", + "label-position-inside-start" : "Intérieur début", + "label-position-inside-start-top" : "Intérieur début haut", + "label-position-inside-start-bottom" : "Intérieur début bas", + "label-position-inside-middle" : "Intérieur milieu", + "label-position-inside-middle-top" : "Intérieur milieu haut", + "label-position-inside-middle-bottom" : "Intérieur milieu bas", + "label-position-inside-end" : "Intérieur fin", + "label-position-inside-end-top" : "Intérieur fin haut", + "label-position-inside-end-bottom" : "Intérieur fin bas", + "label-background" : "Fond du libellé" + }, + "state" : { + "states" : "États", + "label" : "Libellé", + "ticks-value" : "Valeur des graduations", + "source" : "Source", + "value-range" : "Valeur / Plage", + "no-states" : "Aucun état configuré", + "add-state" : "Ajouter un état", + "type-constant" : "Constante", + "type-range" : "Plage", + "from" : "De", + "to" : "À", + "remove-state" : "Supprimer l'état" + }, + "grid" : { + "grid" : "Grille", + "background-color" : "Couleur de fond", + "border" : "Bordure" + }, + "axis" : { + "axes" : "Axes", + "x-axis" : "Axe X", + "y-axis" : "Axe Y", + "y-axis-settings" : "Paramètres de l'axe Y", + "comparison-x-axis-settings" : "Paramètres de l'axe X de comparaison", + "remove-y-axis" : "Supprimer l'axe Y", + "id" : "ID", + "label" : "Libellé", + "position" : "Position", + "position-left" : "Gauche", + "position-right" : "Droite", + "position-top" : "Haut", + "position-bottom" : "Bas", + "tick-labels" : "Libellés des graduations", + "ticks-formatter-function" : "Fonction de formatage des graduations", + "ticks-generator-function" : "Fonction de génération des graduations", + "show-ticks" : "Afficher les graduations", + "show-line" : "Afficher la ligne", + "show-split-lines" : "Afficher les lignes de séparation", + "show-split-lines-x-axis-hint" : "Si activé, les lignes verticales seront affichées sur le graphique.", + "show-split-lines-y-axis-hint" : "Si activé, les lignes horizontales seront affichées sur le graphique.", + "ticks-interval" : "Intervalle des graduations", + "ticks-interval-hint" : "Définit de manière forcée l'intervalle de segmentation de l'axe.", + "split-number" : "Nombre de divisions", + "split-number-hint" : "Nombre de segments dans lesquels l'axe est divisé.", + "min" : "Min", + "max" : "Max", + "show" : "Afficher", + "add-y-axis" : "Ajouter un axe Y" + }, + "series" : { + "legend-settings" : "Paramètres de la légende", + "show-in-legend" : "Afficher dans la légende", + "show-in-legend-hint" : "Afficher le nom de la série et les données dans la légende.", + "hidden-by-default" : "Masqué par défaut", + "hidden-by-default-hint" : "Masquer la série dans la légende par défaut.", + "series-type" : "Type de série", + "type" : "Type", + "type-line" : "Ligne", + "type-bar" : "Barre", + "line" : { + "line" : "Ligne", + "show-line" : "Afficher la ligne", + "step-line" : "Ligne par étapes", + "step-type-start" : "Début", + "step-type-middle" : "Milieu", + "step-type-end" : "Fin", + "smooth-line" : "Ligne lissée" }, - "client-id": "ID client", - "client-id-pattern": "Contient des caractères invalides.", - "user-name": "Nom d'utilisateur", - "user-name-required": "Nom d'utilisateur requis.", - "client-id-or-user-name-necessary": "ID client et/ou identifiant sont requis", - "password": "Mot de passe", - "name-max-length": "La longueur du nom doit être moins de 256", - "label-max-length": "La longueur du Label doit être moins de 256", - "copy-mqtt-authentication": "Copier les authentifiants MQTT", - "mqtt-authentication-copied-message": "L'authentifiant MQTT du dispositif a été copié au presse-papier", - "overwrite-activity-time": "Passer par dessus le temps de l'activité pour dispositif connecté", - "import": "Importer dispositif", - "device-file": "Fichier du dispositif", - "search": "Rechercher des dispositifs", - "selected-devices": "{ count, plural, =1 {1 device} other {# devices} } sélectionnés", - "device-configuration": "Configuration du dipositif", - "transport-configuration": "Configuration du transport", - "wizard": { - "device-details": "Détails du dispositif" + "point" : { + "points" : "Points", + "show-points" : "Afficher les points", + "point-label" : "Libellé du point", + "point-label-hint" : "Afficher un libellé avec la valeur sur le point de la série.", + "point-label-background" : "Fond du libellé du point", + "point-shape" : "Forme du point", + "point-size" : "Taille du point" } + } + }, + "wind-speed-direction" : { + "layout" : "Disposition", + "layout-default" : "Par défaut", + "layout-advanced" : "Avancé", + "layout-simplified" : "Simplifié", + "values" : "Valeurs", + "wind-direction" : "Direction du vent", + "center-value" : "Valeur centrale", + "icon" : "Icône", + "arrow" : "Flèche", + "ticks" : "Graduations", + "labels-type" : "Type de libellés", + "directional-names" : "Noms directionnels", + "degrees" : "Degrés", + "major-ticks" : "Graduations majeures", + "minor-ticks" : "Graduations mineures", + "wind-speed-direction-card-style" : "Style de carte vitesse et direction du vent", + "ticks-color" : "Couleur des graduations", + "ticks-labels-type" : "Type de libellés des graduations", + "arrow-color" : "Couleur de la flèche" + }, + "value-source" : { + "value-source" : "Source de valeur", + "predefined-value" : "Constante", + "entity-attribute" : "Attribut de l'entité", + "value" : "Valeur", + "value-required" : "La valeur est requise.", + "key-required" : "La clé est requise.", + "entity-key-required" : "La clé de l'entité est requise.", + "source-entity-alias" : "Alias de l'entité source", + "source-entity-attribute" : "Attribut de l'entité source", + "type-constant" : "Constante", + "type-latest-key" : "Clé", + "type-entity" : "Entité" + }, + "rpc-state" : { + "initial-state" : "État initial", + "initial-state-hint" : "Action pour obtenir l'état initial (Activé/Désactivé) du composant.", + "disabled-state" : "État désactivé", + "disabled-state-hint" : "Configurer la condition dans laquelle le composant est désactivé.", + "turn-on" : "Allumer", + "turn-on-hint" : "Action déclenchée lorsque le curseur passe sur 'Activé'", + "turn-off" : "Éteindre", + "turn-off-hint" : "Action déclenchée lorsque le curseur passe sur 'Désactivé'", + "on" : "Activé", + "off" : "Désactivé", + "disabled" : "Désactivé" + }, + "value-action" : { + "do-nothing" : "Ne rien faire", + "execute-rpc" : "Exécuter RPC", + "get-attribute" : "Obtenir un attribut", + "set-attribute" : "Définir un attribut", + "get-time-series" : "Obtenir une série temporelle", + "get-alarm-status" : "Obtenir le statut de l'alarme", + "get-dashboard-state" : "Obtenir l'identifiant de l'état du tableau de bord", + "get-dashboard-state-object" : "Obtenir l'objet d'état du tableau de bord", + "add-time-series" : "Ajouter une série temporelle", + "execute-rpc-text" : "Exécuter la méthode RPC '{{methodName}}'", + "get-time-series-text" : "Utiliser la série temporelle '{{key}}'", + "get-attribute-text" : "Utiliser l'attribut '{{key}}'", + "get-alarm-status-text" : "Utiliser le statut de l'alarme", + "get-dashboard-state-text" : "Utiliser l'état du tableau de bord", + "get-dashboard-state-object-text" : "Utiliser l'objet d'état du tableau de bord", + "when-dashboard-state-is-text" : "Lorsque l'identifiant d'état du tableau de bord est '{{state}}'", + "when-dashboard-state-function-is-text" : "Lorsque f(identifiant d'état du tableau de bord) est '{{state}}'", + "when-dashboard-state-object-function-is-text" : "Lorsque f(objet d'état du tableau de bord) est '{{state}}'", + "set-attribute-to-value-text" : "Définir l'attribut '{{key}}' sur : {{value}}", + "add-time-series-value-text" : "Ajouter la valeur {{value}} à la série temporelle '{{key}}'", + "set-attribute-text" : "Définir l'attribut '{{key}}'", + "add-time-series-text" : "Ajouter la série temporelle '{{key}}'", + "action" : "Action", + "value" : "Valeur", + "init-value-hint" : "Valeur qui sera définie jusqu'à ce que l'appareil envoie des données.", + "method" : "Méthode", + "method-name-required" : "Le nom de la méthode est requis.", + "request-timeout-ms" : "Délai d'attente de la requête RPC (ms)", + "request-timeout-required" : "Le délai d'attente est requis.", + "min-request-timeout-error" : "La valeur du délai d'attente doit être supérieure ou égale à 5000 ms (5 secondes).", + "request-persistent" : "Requête RPC persistante", + "persistent-polling-interval" : "Intervalle d'interrogation persistant (ms)", + "persistent-polling-interval-hint" : "Intervalle (ms) pour obtenir une réponse de commande RPC persistante", + "persistent-polling-interval-required" : "L'intervalle d'interrogation persistant est requis.", + "min-persistent-polling-interval-error" : "L'intervalle d'interrogation persistant doit être supérieur ou égal à 1000 ms (1 seconde).", + "attribute-scope" : "Portée de l'attribut", + "attribute-key" : "Clé de l'attribut", + "attribute-key-required" : "La clé de l'attribut est requise.", + "time-series-key" : "Clé de la série temporelle", + "time-series-key-required" : "La clé de la série temporelle est requise.", + "action-result-converter" : "Convertisseur de résultat d'action", + "converter-none" : "Aucun", + "converter-function" : "Fonction", + "converter-constant" : "Constante", + "converter-value" : "Valeur", + "parse-value-function" : "Fonction d'analyse de la valeur", + "state-when-result-is" : "'{{state}}' lorsque le résultat est", + "parameters" : "Paramètres", + "convert-value-function" : "Fonction de conversion de la valeur", + "error" : { + "target-entity-is-not-set" : "L'entité cible n'est pas définie !", + "failed-to-perform-action" : "Échec de l'exécution de l'action {{ actionLabel }}.", + "invalid-attribute-scope" : "La portée de l'attribut '{{scope}}' n'est pas prise en charge pour l'entité {{entityType}}." + } + }, + "widget-font" : { + "font-settings" : "Paramètres de police", + "font-family" : "Famille de police", + "size" : "Taille", + "relative-font-size" : "Taille de police relative (pourcentage)", + "font-style" : "Style", + "font-style-normal" : "Normal", + "font-style-italic" : "Italique", + "font-style-oblique" : "Oblique", + "font-weight" : "Poids", + "font-weight-normal" : "Normal", + "font-weight-bold" : "Gras", + "font-weight-bolder" : "Plus gras", + "font-weight-lighter" : "Plus léger", + "color" : "Couleur", + "shadow-color" : "Couleur de l'ombre", + "preview" : "Aperçu", + "line-height" : "Hauteur de ligne", + "auto" : "Auto" + }, + "home" : { + "no-data-available" : "Aucune donnée disponible" + }, + "system-info" : { + "cpu" : "CPU", + "ram" : "RAM", + "disk" : "Disque", + "cpu-warning-text" : "Utilisation élevée du processeur. Pour éviter une panne du système, optimisez les performances.", + "cpu-critical-text" : "Utilisation critique du processeur. Pour éviter une panne du système, optimisez les performances.", + "ram-warning-text" : "Réserve de RAM faible. Pour éviter une panne du système, optimisez les performances ou augmentez la taille de la RAM.", + "ram-critical-text" : "Réserve critique de RAM. Pour éviter une panne du système, optimisez les performances ou augmentez la taille de la RAM.", + "disk-warning-text" : "Espace disque faible. Pour éviter une perte de données, libérez ou augmentez l'espace disque.", + "disk-critical-text" : "Espace disque critique. Pour éviter une perte de données, libérez ou augmentez l'espace disque." + }, + "cluster-info" : { + "service-id" : "ID du service", + "service-type" : "Type de service", + "no-data" : "Aucune donnée" + }, + "transport-messages" : { + "title" : "Messages de transport", + "info" : "Tous les messages provenant des appareils" + }, + "activity" : { + "title" : "Activité" }, - "device-profile": { - "device-profile": "Profile du dispositif", - "device-profiles": "Profiles du dispositif", - "all-device-profiles": "Tous", - "add": "Ajouter un nouveau profile de dispositif", - "edit": "Modifier le profile de dispositif", - "device-profile-details": "Détails du profile de dispositif", - "no-device-profiles-text": "Aucun profil de dispositif trouvé", - "search": "Rechercher profil de dispositif", - "selected-device-profiles": "{ count, plural, =1 {1 device profile} other {# device profiles} } sélectionné", - "no-device-profiles-matching": "Aucun dispositif correspondant à '{{entity}}' trouvé.", - "device-profile-required": "Un profil de dispositif est requis.", - "idCopiedMessage": "L'Id du profil de dispositif a été copié au presse-papier", - "set-default": "Rendre le profil de dispositif par défaut", - "delete": "Supprimer le profil de dispositif", - "copyId": "Copier l'Identifiant du profil de dispositif", - "name-max-length": "La longueur du nom devrait être moins de 256", - "name": "Nom", - "name-required": "Nom est requis.", - "type": "Type de profile", - "type-required": "Type de profile est requis.", - "type-default": "Défault", - "image": "Image du profile de dispositif", - "transport-type": "Type de transport", - "transport-type-required": "Type de transport est requis.", - "transport-type-default": "Défault", - "transport-type-default-hint": "Supporte comme transport MQTT de base, HTTP et CoAP", - "transport-type-mqtt-hint": "Active les configurations MQTT avancées", - "transport-type-coap-hint": "Active les configurations CoAP avancées", - "transport-type-lwm2m-hint": "Type de transport LWM2M", - "transport-type-snmp-hint": "Spécifiez la configuration du transport SNMP", - "default": "Défault", - "profile-configuration": "Configuration du profile", - "transport-configuration": "Configuration du transport", - "default-rule-chain": "Chaine de règles par défaut", - "mobile-dashboard": "Tableau de bord mobile", - "mobile-dashboard-hint": "Utilisé par les applications mobiles comme tableau de bord de détails de dispositifs.", - "select-queue-hint": "Choisissez d'une liste déroulante.", - "delete-device-profile-title": "Êtes-vous certain de vouloir supprimer le profile de dispositif '{{deviceProfileName}}'?", - "delete-device-profile-text": "Attention, après confirmation le profil du dispositif et toutes les données associées deviendront irrécupérables.", - "delete-device-profiles-title": "Êtes-vous certainde vouloir supprimer { count, plural, =1 {1 device profile} other {# device profiles} }?", - "delete-device-profiles-text": "Attention, après confirmation les profils des dispositifs sélectionnés et toutes les données associées deviendront irrécupérables.", - "set-default-device-profile-title": "Êtes-vous certain de vouloir faire du profil de dispositif '{{deviceProfileName}}' le profil par défaut?", - "set-default-device-profile-text": "Après confirmation, le profil de dispositif sera le profil par défaut et sera utilisé pour les nouveaux dispositifs n'ayant pas de profil.", - "no-device-profiles-found": "Aucun profil de dispositif trouvé.", - "create-new-device-profile": "En créer un nouveau!", - "mqtt-device-topic-filters": "Filtres de sujets de dispositifs MQTT", - "mqtt-device-topic-filters-unique": "Filtres de sujets de dispositifs MQTT doivent être uniques. ", - "mqtt-device-payload-type": "Payload de dispositif MQTT", - "mqtt-enable-compatibility-with-json-payload-format": "Activer la compatibilité avec d'autres formats de payloads.", - "mqtt-enable-compatibility-with-json-payload-format-hint": "Lorsqu'activé, la plateforme utilisera un format de payload Protobuf par défaut. Si le parsing échoue, la pateforme tentera d'utiliser le format JSON. Utile pour compatibilité avec version antérieures lors de mises à jour du firmware. Par exemple, la version originale du firmware utilisait JSON, alors que la nouvelle version du firmware utilise Protobuf. Durant le processus de mise à jour du firmware pour une flotte de dispositifs, il est requis de supporter à la fois JSON et Protobuf simultanément. Ce mode de compatibilité est légèrement moins performant alors il est recommandé de le désactivé une fois tous les dispositifs mis à jour.", - "mqtt-use-json-format-for-default-downlink-topics": "Utilisez le format JSON pouir les sujets de downlink par défaut", - "mqtt-use-json-format-for-default-downlink-topics-hint": "Lorsqu'activé, la plateforme utilisera le format de payload JSON pour pousser des attributs et des RPC via les sujets suivants: v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, v1/devices/me/rpc/request/$request_id, v1/devices/me/rpc/response/$request_id. This setting does not impact attribute and rpc subscriptions sent using new (v2) topics: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id. Où $request_id est un identifiant de requête entier.", - "snmp-add-mapping": "Ajouter un mapping SNMP", - "snmp-mapping-not-configured": "Aucun mapping pour OID vers timeseries/télémétrie n'est configuré", - "snmp-timseries-or-attribute-name": "Nom timeseries/attribut pour mapping", - "snmp-timseries-or-attribute-type": "Type timeseries/attribut pour mapping", - "mqtt-payload-type-required": "Type de payload est requis.", - "coap-device-type": "Type de dispositif CoAP", - "coap-device-payload-type": "Payload de dispositif CoAP", - "coap-device-type-required": "Type de dispositif CoAP est requis.", - "coap-device-type-default": "Défaut", - "support-level-wildcards": "[+] unique et wildcards de [#] multi-niveaux supportés.", - "telemetry-topic-filter": "Filtre de sujets de télémétrie", - "telemetry-topic-filter-required": "Filtre de sujets de télémétrie est requis.", - "attributes-topic-filter": "Attributes publish topic filter", - "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Attributes publish topic filter is required.", - "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", - "telemetry-proto-schema": "Schéma proto de télémétrie", - "telemetry-proto-schema-required": "Schéma proto de télémétrie est requis.", - "attributes-proto-schema": "Schéma proto d'attributs", - "attributes-proto-schema-required": "Schéma proto d'attributs est requis.", - "rpc-response-proto-schema": "Schéma proto de réponse RPC", - "rpc-response-proto-schema-required": "Schéma proto de réponse RPC est requis.", - "rpc-response-topic-filter": "Filtre de sujets de réponse RPC", - "rpc-response-topic-filter-required": "Filtre de sujets de réponse RPC est requis.", - "rpc-request-proto-schema": "Schéma proto de requête RPC", - "rpc-request-proto-schema-required": "Schéma proto de requête RPC est requis.", - "rpc-request-proto-schema-hint": "Une requête RPC devrait toujours avoir les champs: string method = 1; int32 requestId = 2; et params = 3 de n'importe quel type de données." - }, - "dialog": { - "close": "Fermer le dialogue" - }, - "edge": { - "edge": "Bordure", - "edge-instances": "Instances de Bord", - "edge-file": "Fichier Edge", - "management": "Gestion des bordures", - "no-edges-matching": "Aucun bordure correspondant à {{entity}} n'a été trouvé.", - "add": "Ajouter un bordure", - "no-edges-text": "Aucun bordure trouvé", - "edge-details": "Détails de la bordure", - "add-edge-text": "Ajouter une nouveau bordure", - "delete": "Supprimer la bordure", - "delete-edge-title": "Êtes-vous sûr de vouloir supprimer la bordure '{{edgeName}}'?", - "delete-edge-text": "Faites attention, après la confirmation, la bordure et toutes les données associées deviendront irrécupérables", - "delete-edges-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 bordure} other {# bordure} }?", - "delete-edges-text": "Faites attention, après la confirmation, tous les bordures sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "name": "Nom", - "name-starts-with": "Le nom du bord commence par", - "name-required": "Le nom de la bordure est requis", - "description": "Dispositifs", - "details": "Détails de l'entité", - "events": "Événements", - "copy-id": "Copier borudre Id", - "id-copied-message": "Id de la bordure a été copié dans le presse-papier", - "sync": "Sync Edge", - "edge-required": "Bordure est requise", - "edge-type": "Type de la bordure", - "edge-type-required": "Type de la bordure est requise.", - "event-action": "Action d'événement", - "entity-id": "ID d'entité", - "select-edge-type": "Selectionner un type de la bordure", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", - "assign-edge-to-customer": "Attribuer la bordure au client", - "assign-edge-to-customer-text": "Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", - "assignedToCustomer": "Attribué au client", - "edge-public": "Edge est public", - "assigned-to-customer": "Attribué au client", - "unassign-from-customer": "Retirer du client", - "unassign-edge-title": "Êtes-vous sûr de vouloir annuler l'affection du dispositif {{edgeName}}", - "unassign-edge-text": "Après la confirmation, le dispositif ne sera pas attribué et ne sera pas accessible au client", - "unassign-edges-title": "Voulez-vous vraiment annuler l'attribution de {count, plural, =1 {1 bordure} other {# bordures} }?", - "unassign-edges-text": "Après la confirmation, tous les bordures sélectionnés ne seront plus attribués et ne seront pas accessibles par le client.", - "make-public": "Make edge public", - "make-public-edge-title": "Are you sure you want to make the edge '{{edgeName}}' public?", - "make-public-edge-text": "After the confirmation the edge and all its data will be made public and accessible by others.", - "make-private": "Rendre public Edge", - "public": "Public", - "make-private-edge-title": "Are you sure you want to make the edge '{{edgeName}}' private?", - "make-private-edge-text": "Après la confirmation, la bordure et toutes ses données seront rendues privées et ne seront pas accessibles par d'autres", - "import": "Importer bordure", - "label": "Etiquette", - "load-entity-error": "Entité introuvable. Échec du chargement des informations", - "assign-new-edge": "Attribuer un nouvel bordure", - "unassign-from-edge": "Retirer de la bordure", - "edge-key": "Clé de la bordure", - "copy-edge-key": "Copier clé de la bordure", - "edge-key-copied-message": "Clé de la bordure a été copié dans le presse-papier", - "edge-secret": "Secret de la bordure", - "copy-edge-secret": "Copier secret de la bordure", - "edge-secret-copied-message": "Secret de la bordure a été copié dans le presse-papier", - "edge-assets": "Gérer les actifs de la bordure", - "edge-devices": "Gérer les dispositifs de la bordure", - "edge-entity-views": "Vues de l'entité vues de l'entité", - "edge-dashboards": "Gérer les tableaux de bord", - "edge-rulechains": "Chaînes de règles Edge", - "assets": "Actifs de la bordure", - "devices": "Dispositifs de la bordure", - "entity-views": "Vues de l'entité bordure", - "dashboard": "Tableau de bord Edge", - "dashboards": "Tableau de bord de la bordure", - "rulechain-templates": "Modèles de chaîne de règles", - "rulechains": "Chaînes de règles de la bordure", - "search": "Rechercher les bords", - "selected-edges": "{count, plural, =1 {1 bordure} other {# bords} } sélectionné", - "any-edge": "Tout bord", - "no-edge-types-matching": "Aucun type d'arête correspondant à \"{{entitySubtype}}\" n'a été trouvé.", - "edge-type-list-empty": "Aucun type d'arête sélectionné.", - "edge-types": "Types de bords", - "enter-edge-type": "Entrez le type d'arête", - "deployed": "Déployé", - "pending": "En attente", - "downlinks": "Liens descendants", - "no-downlinks-prompt": "Aucun lien descendant trouvé", - "sync-process-started-successfully": "Le processus de synchronisation a démarré avec succès!", - "missing-related-rule-chains-title": "Edge n'a pas de chaîne (s) de règles associées", - "missing-related-rule-chains-text": "Les chaînes de règles affectées aux tronçons utilisent des nœuds de règles qui transfèrent les messages vers les chaînes de règles non affectées à ce tronçon.

Liste des chaînes de règles manquantes:
{{missingRuleChains}}", - "widget-datasource-error": "Ce widget prend en charge uniquement la source de données d'entité EDGE" - }, - "edge-event": { - "type-dashboard": "Dashboard", - "type-asset": "Asset", - "type-device": "Device", - "type-device-profile": "Device Profile", - "type-entity-view": "Entity View", - "type-alarm": "Alarm", - "type-rule-chain": "Rule Chain", - "type-rule-chain-metadata": "Rule Chain Metadata", - "type-edge": "Edge", - "type-user": "User", - "type-customer": "Customer", - "type-relation": "Relation", - "type-widgets-bundle": "Widgets Bundle", - "type-widgets-type": "Widgets Type", - "type-admin-settings": "Admin Settings", - "action-type-added": "Added", - "action-type-deleted": "Deleted", - "action-type-updated": "Updated", - "action-type-post-attributes": "Post Attributes", - "action-type-attributes-updated": "Attributes Updated", - "action-type-attributes-deleted": "Attributes Deleted", - "action-type-timeseries-updated": "Timeseries Updated", - "action-type-credentials-updated": "Credentials Updated", - "action-type-assigned-to-customer": "Assigned to Customer", - "action-type-unassigned-from-customer": "Unassigned from Customer", - "action-type-relation-add-or-update": "Relation Add or Update", - "action-type-relation-deleted": "Relation Deleted", - "action-type-rpc-call": "RPC Call", - "action-type-alarm-ack": "Alarm Ack", - "action-type-alarm-clear": "Alarm Clear", - "action-type-assigned-to-edge": "Assigned to Edge", - "action-type-unassigned-from-edge": "Unassigned from Edge", - "action-type-credentials-request": "Credentials Request", - "action-type-entity-merge-request": "Entity Merge Request" - }, - "entity": { - "add-alias": "Ajouter un alias d'entité", - "alarm-name-starts-with": "Les alarmes dont le nom commence par '{{prefix}}'", - "alias": "Alias", - "alias-required": "Un alias d'entité est requis.", - "aliases": "alias d'entité", - "all-subtypes": "Tout", - "any-entity": "Toute entité", - "asset-name-starts-with": "Les actifs dont le nom commence par '{{prefix}}'", - "columns-to-display": "Colonnes à afficher", - "configure-alias": "Configurer '{{alias}}' alias", - "create-new-alias": "Créez un nouveau!", - "create-new-key": "Créez un nouveau!", - "customer-name-starts-with": "Les clients dont les noms commencent par '{{prefix}}'", - "dashboard-name-starts-with": "Les tableaux de bord dont les noms commencent par '{{prefix}}'", - "details": "Détails de l'entité", - "device-name-starts-with": "Dispositifs dont le nom commence par '{{prefix}}'", - "duplicate-alias-error": "Alias ​​en double trouvé '{{alias}}'.
Les alias d'entité doivent être uniques dans le tableau de bord.", - "enter-entity-type": "Entrez le type d'entité", - "entities": "Entités", - "entity": "Entité", - "entity-alias": "Alias de l'entité", - "entity-list": "Liste d'entités", - "entity-list-empty": "Aucune entité sélectionnée.", - "entity-name": "Nom de l'entité", - "entity-name-filter-no-entity-matched": "Aucune entité commençant par '{{entity}}' n'a été trouvée.", - "entity-name-filter-required": "Le filtre de nom d'entité est requis.", - "entity-type": "Type d'entité", - "entity-type-list": "Liste de types d'entités", - "entity-types": "Types d'entité", - "entity-view-name-starts-with": "Les vues d'entité dont le nom commence par '{{prefix}}'", - "key": "Clé", - "key-name": "Nom de la clé", - "list-of-alarms": "{count, plural, =1 {Une alarme} other {Liste de # alarmes} }", - "list-of-assets": "{count, plural, =1 {Un Asset} other {Liste de # Assets} }", - "list-of-customers": "{count, plural, =1 {Un client} other {Liste de # clients} }", - "list-of-dashboards": "{count, plural, =1 {Un tableau de bord} other {Liste de # tableaux de bord} }", - "list-of-devices": "{count, plural, =1 {Un dispositif} other {Liste de # dispositifs} }", - "list-of-plugins": "{count, plural, =1 {Un plugin} other {Liste de # plugins} }", - "list-of-rulechains": "{count, plural, =1 {Une chaîne de règles} other {Liste de # chaînes de règles} }", - "list-of-rulenodes": "{count, plural, =1 {Un noeud de règles} other {Liste de # noeuds de règles} }", - "list-of-rules": "{count, plural, =1 {Une règle} other {Liste de # règles} }", - "list-of-tenants": "{count, plural, =1 {Un tenant} other {Liste de # tenants} }", - "list-of-users": "{count, plural, =1 {Un utilisateur} other {Liste de # utilisateurs} }", - "missing-entity-filter-error": "Le filtre est manquant pour l'alias '{{alias}}'.", - "name-starts-with": "Nom commence par", - "no-alias-matching": "'{{alias}}' introuvable.", - "no-aliases-found": "Aucun alias trouvé.", - "no-data": "Aucune donnée à afficher", - "no-entities-matching": "Aucune entité correspondant à '{{entity}}' n'a été trouvée.", - "no-entities-prompt": "Aucune entité trouvée", - "no-entity-types-matching": "Aucun type d'entité correspondant à {{entityType}} n'a été trouvé. ", - "no-key-matching": "'{{key}}' introuvable.", - "no-keys-found": "Aucune clé trouvée", - "plugin-name-starts-with": "Plugins dont les noms commencent par '{{prefix}}'", - "remove-alias": "Supprimer l'alias d'entité", - "rule-name-starts-with": "Régles dont les noms commencent par '{{prefix}}'", - "rulechain-name-starts-with": "Chaînes de régles dont les noms commencent par '{{prefix}}'", - "rulenode-name-starts-with": "Les noeuds de régles dont le nom commence par '{{prefix}}'", - "type-edge": "Bordure", - "type-edges": "Bordures", - "list-of-edges": "{ count, plural, =1 {Une bordure} other {Liste de # bordures} }", - "edge-name-starts-with": "Bordures dont les noms commencent par '{{prefix}}'", - "search": "Recherche d'entités", - "select-entities": "Sélectionner des entités", - "selected-entities": "{count, plural, =1 {1 entité} other {# entités} } sélectionnées", - "tenant-name-starts-with": "Les Tenant dont le nom commence par '{{prefix}}'", - "type": "Type", - "type-alarm": "Alarme", - "type-alarms": "Alarmes", - "type-asset": "Actif", - "type-assets": "Actifs", - "type-current-customer": "Client actuel", - "type-customer": "Client", - "type-customers": "Clients", - "type-dashboard": "Tableau de bord", - "type-dashboards": "Tableaux de bord", - "type-device": "Dispositif", - "type-devices": "Dispositifs", - "type-entity-view": "Vue d'entité", - "type-entity-views": "Vues d'entités", - "type-plugin": "Plugin", - "type-plugins": "Plugins", - "type-required": "Le type d'entité est obligatoire.", - "type-rule": "Régle", - "type-rulechain": "Chaîne de régles", - "type-rulechains": "Chaînes de régles", - "type-rulenode": "Noeud de régle", - "type-rulenodes": "Noeuds de régle", - "type-rules": "Régles", - "type-tenant": "Tenant", - "type-tenants": "Tenants", - "type-user": "Utilisateur", - "type-users": "Utilisateurs", - "unable-delete-entity-alias-text": "L'alias d'entité '{{entityAlias}}' ne peut pas être supprimé car il est utilisé par les widgets suivants:
{{widgetsList}}", - "unable-delete-entity-alias-title": "Impossible de supprimer l'alias d'entité", - "use-entity-name-filter": "Utiliser un filtre", - "user-name-starts-with": "Utilisateurs dont les noms commencent par '{{prefix}}'" - }, - "entity-field": { - "address": "Adresse", - "address2": "Adresse 2", - "city": "Ville", - "country": "Pays", - "created-time": "Heure de création", - "email": "Email", - "first-name": "Prénom", - "last-name": "Nom de famille", - "name": "Nom", - "phone": "Téléphone", - "state": "Prov", - "title": "Titre", - "type": "Type", - "zip": "Code postal" - }, - "entity-view": { - "add": "Ajouter une vue d'entité", - "add-alias": "Ajouter un alias de vue d'entité", - "add-entity-view-text": "Ajouter une nouvelle vue d'entité", - "alias": "Alias", - "alias-required": "Un alias de vue d'entité est requis.", - "aliases": "Alias de vue d'entité", - "any-entity-view": "Toute vue d'entité", - "assign-entity-view-to-customer": "Attribuer une (des) vue (s) d'entité à un client", - "assign-entity-view-to-customer-text": "Veuillez sélectionner les vues d'entité à affecter au client", - "assign-entity-views": "Attribuer des vues d'entité", - "assign-entity-views-text": "Attribuer { count, plural, =1 {1 entityView} other {# entityViews} } au client", - "assign-new-entity-view": "Attribuer une nouvelle vue d'entité", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner le client auquel attribuer la ou les vues d'entité.", - "assignedToCustomer": "Assigné au client", - "attributes-propagation": "Propagation des attributs", - "attributes-propagation-hint": "La vue d'entité copiera automatiquement les attributs spécifiés de l'entité cible chaque fois que vous enregistrez ou mettez à jour cette vue d'entité. Pour des raisons de performances, les attributs d'entité cible ne sont pas propagés à la vue d'entité à chaque changement d'attribut. Vous pouvez activer la propagation automatique en configurant le noeud de règle \" copier pour afficher \" dans votre chaîne de règles et en liant les messages \"Post attributs \" et \"attributs mis à jour \" au nouveau noeud de règle.", - "client-attributes": "Attributs du client", - "client-attributes-placeholder": "Attributs du client", - "configure-alias": "Configurez l'alias '{{alias}}'", - "copyId": "Copier l'ID de la vue d'entité", - "create-new-alias": "Créer un nouveau!", - "create-new-key": "Créer un nouveau!", - "date-limits": "Limites de date", - "delete": "Supprimer la vue d'entité", - "delete-entity-view-text": "Attention, après la confirmation, la vue de l'entité et toutes les données associées deviendront irrécupérables.", - "delete-entity-view-title": "Êtes-vous sûr de vouloir supprimer la vue de l'entité '{{entityViewName}}'?", - "delete-entity-views": "Supprimer les vues d'entité", - "delete-entity-views-action-title": "Supprimer { count, plural, =1 {1 entityView} other {# entityViews} }", - "delete-entity-views-text": "Attention, après la confirmation, toutes les vues d'entité sélectionnées seront supprimées et toutes les données associées deviendront irrécupérables.", - "delete-entity-views-title": "Êtes-vous sûr de vouloir voir l'entité { count, plural, =1 {1 entityView} other {# entityViews} }?", - "description": "Description", - "details": "Détails", - "duplicate-alias-error": "Alias '{{alias}}' existe déjà.
Les alias de vue d'entité doivent être uniques dans le tableau de bord.", - "end-date": "Date de fin", - "end-ts": "Heure de fin", - "enter-entity-view-type": "Entrer le type de vue d'entité", - "entity-view": "Vue d'entité", - "entity-view-alias": "Alias de vue d'entité", - "entity-view-details": "Détails de la vue d'entité", - "entity-view-list": "Liste de vues d'entités", - "entity-view-list-empty": "Aucune vue d'entité sélectionnée.", - "entity-view-name-filter-no-entity-view-matched": "Aucune vue d'entité commençant par '{{entityView}}' n'a été trouvée.", - "entity-view-name-filter-required": "Un filtre de nom de vue d'entité est requis.", - "entity-view-required": "Une vue d'entité est requise.", - "entity-view-type": "Type de vue d'entité", - "entity-view-type-list-empty": "Aucun type de vue d'entité sélectionné.", - "entity-view-type-required": "Le type d'entité est requis.", - "entity-view-types": "Types de vues d'entité", - "entity-views": "Vues d'entité", - "events": "Événements", - "make-private": "Rendre la vue d'entité privée", - "make-private-entity-view-text": "Après la confirmation, la vue de l'entité et toutes ses données seront rendues privées et ne seront pas accessibles par d'autres", - "make-private-entity-view-title": "Êtes-vous sûr de vouloir rendre la vue d'entité '{{entityViewName}}' privée?", - "make-public": "Rendre la vue d'entité publique", - "make-public-entity-view-text": "Après la confirmation, la vue de l'entité et toutes ses données seront rendues publiques et accessibles à d'autres", - "make-public-entity-view-title": "Voulez-vous vraiment que la vue de l'entité '{{entityViewName}}' soit publique?", - "assign-entity-view-to-edge": "Attribuer a la bordure", - "assign-entity-view-to-edge-text": "Veuillez sélectionner la bordure auquel attribuer la ou les vues d'entité.", - "unassign-entity-view-from-edge-title": "Voulez-vous vraiment annuler l'attribution de la vue d'entité '{{entityViewName}}'?", - "unassign-entity-view-from-edge-text": "Après la confirmation, la vue de l'entité sera non attribuée et ne sera pas accessible par la bordure.", - "unassign-entity-views-from-edge-action-title": "Annuler l'attribution { count, plural, =1 {1 entityView} other {# entityViews} } de la bordure", - "unassign-entity-view-from-edge": "Annuler l'attribution des vues d'entité", - "unassign-entity-views-from-edge-title": "Êtes-vous sûr de vouloir annuler l'attribution { count, plural, =1 {1 entityView} other {# entityViews} }?", - "unassign-entity-views-from-edge-text": "Après la confirmation, toutes les vues des entités sélectionnées seront non attribuées et ne seront pas accessibles par la bordure.", - "management": "Gestion de vue d'entité", - "name": "Nom", - "name-required": "Un nom est requis.", - "name-starts-with": "Le nom de la vue d'entité commence par", - "no-alias-matching": "'{{alias}}' non trouvé.", - "no-aliases-found": "Aucun alias trouvé.", - "no-entity-view-types-matching": "Aucun type de vue d'entité correspondant à '{{entitySubtype}}' n'a été trouvé.", - "no-entity-views-matching": "Aucune vue d'entité correspondant à '{{entity}}' n'a été trouvée.", - "no-entity-views-text": "Aucune vue d'entité trouvée.", - "no-key-matching": "'{{key}}' non trouvé.", - "no-keys-found": "Aucune clé trouvée.", - "remove-alias": "Supprimer un alias de vue d'entité", - "select-entity-view": "Sélectionner une vue d'entité", - "select-entity-view-type": "Sélectionner le type de vue d'entité", - "server-attributes": "Attributs du serveur", - "server-attributes-placeholder": "Attributs du serveur", - "shared-attributes": "Attributs partagés", - "shared-attributes-placeholder": "Attributs partagés", - "start-date": "Date de début", - "start-ts": "Heure de début", - "target-entity": "Entité cible", - "timeseries": "Séries chronologiques", - "timeseries-data": "Données de séries chronologiques", - "timeseries-data-hint": "Configurez les clés de données de séries chronologiques de l'entité cible qui seront accessibles à la vue de l'entité. Ces données temporelles sont en lecture seule.", - "timeseries-placeholder": "Séries chronologiques", - "unable-entity-view-device-alias-text": "L'alias de dispositif '{{entityViewAlias}}' ne peut pas être supprimé car il est utilisé par les widgets suivants:
{{widgetsList}}", - "unable-entity-view-device-alias-title": "Impossible de supprimer l'alias de la vue d'entité.", - "unassign-entity-view": "Annuler l'affectation de la vue d'entité", - "unassign-entity-view-text": "Après la confirmation, la vue de l'entité sera non attribuée et ne sera pas accessible par le client.", - "unassign-entity-view-title": "Voulez-vous vraiment annuler l'attribution de la vue d'entité '{{entityViewName}}'?", - "unassign-entity-views": "Annuler l'attribution des vues d'entité", - "unassign-entity-views-action-title": "Annuler l'attribution { count, plural, =1 {1 entityView} other {# entityViews} } du client", - "unassign-entity-views-text": "Après la confirmation, toutes les vues des entités sélectionnées seront non attribuées et ne seront pas accessibles par le client.", - "unassign-entity-views-title": "Êtes-vous sûr de vouloir annuler l'attribution { count, plural, =1 {1 entityView} other {# entityViews} }?", - "unassign-from-customer": "Annuler l'attribution au client", - "use-entity-view-name-filter": "Use filter", - "view-entity-views": "Voir les vues d'entité", - "idCopiedMessage": "L'ID de la vue d'entité a été copiée dans le presse-papier", - "search": "Rechercher des vues d'entité", - "selected-entity-views": "{ count, plural, =1 {1 entity view} other {# entity views} } sélectionnés" - }, - "error": { - "unable-to-connect": "Impossible de se connecter au serveur! Veuillez vérifier votre connexion Internet.", - "unhandled-error-code": "Code d'erreur non géré: {{errorCode}}", - "unknown-error": "Erreur inconnue" - }, - "event": { - "alarm": "Alarme", - "body": "Corps", - "data": "Données", - "data-type": "Type de données", - "error": "erreur", - "type-edge-event": "Downlink", - "errors-occurred": "Des erreurs sont survenues", - "event": "événement", - "event-time": "Heure de l'événement", - "event-type": "Type d'événement", - "failed": "Échec", - "message-id": "Message Id", - "message-type": "Type de message", - "messages-processed": "Messages traités", - "metadata": "Métadonnées", - "method": "Méthode", - "no-events-prompt": "Aucun événement trouvé", - "relation-type": "Type de relation", - "server": "Serveur", - "status": "État", - "success": "Succès", - "type": "Type", - "type-debug-rule-chain": "Debug", - "type-debug-rule-node": "Debug", - "type-error": "Erreur", - "type-lc-event": "Evénement du cycle de vie", - "type-stats": "Statistiques", - "all-events": "Tout", - "entity-type": "Type d'entité" - }, - "extension": { - "add": "Ajouter une extension", - "add-attribute": "Ajouter un attribut", - "add-attribute-request": "Ajouter une demande d'attribut", - "add-attribute-update": "Ajouter une mise à jour d'attribut", - "add-broker": "Ajouter un Broker", - "add-config": "Ajouter une configuration de convertisseur", - "add-connect-request": "Ajouter une demande de connexion", - "add-converter": "Ajouter un convertisseur", - "add-device": "Ajouter un dispositif", - "add-disconnect-request": "Ajouter une demande de déconnexion", - "add-map": "Ajouter un élément de mappage", - "add-server-side-rpc-request": "Ajouter une requête RPC côté serveur", - "add-timeseries": "Ajouter des timeseries", - "anonymous": "Anonyme", - "attr-json-key-expression": "Expression json de la clé d'attribut", - "attr-topic-key-expression": "Expression du topic de la clé d'attribut", - "attribute-filter": "Filtre d'attribut", - "attribute-key-expression": "Expression de clé d'attribut", - "attribute-requests": "Demandes d'attributs", - "attribute-updates": "Mises à jour des attributs", - "attributes": "Attributs", - "basic": "Basic", - "brokers": "Brokers", - "ca-cert": "Fichier de certificat CA", - "cert": "Fichier de certificat *", - "client-scope": "Portée client", - "configuration": "Configuration", - "connect-requests": "Demandes de connexion", - "converter-configurations": "Configurations du convertisseur", - "converter-id": "ID du convertisseur", - "converter-json": "Json", - "converter-json-parse": "Impossible d'analyser le convertisseur json.", - "converter-json-required": "Le convertisseur json est requis.", - "converter-type": "Type de convertisseur", - "converters": "Convertisseurs", - "credentials": "Informations d'identification", - "custom": "Sur mesure", - "delete": "Supprimer l'extension", - "delete-extension-text": "Attention, après la confirmation, l'extension et toutes les données associées deviendront irrécupérables.", - "delete-extension-title": "Êtes-vous sûr de vouloir supprimer l'extension '{{extensionId}}'?", - "delete-extensions-text": "Attention, après la confirmation, toutes les extensions sélectionnées seront supprimées.", - "delete-extensions-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 extension} other {# extensions} }?", - "device-name-expression": "expression du nom du dispositif", - "device-name-filter": "Filtre de nom de dispositif", - "device-type-expression": "expression de type de dispositif", - "disconnect-requests": "Demandes de déconnection", - "drop-file": "Déposez un fichier ou cliquez pour sélectionner un fichier à télécharger.", - "edit": "Modifier l'extension", - "export-extension": "Exporter l'extension", - "export-extensions-configuration": "Exporter la configuration des extensions", - "extension-id": "Id de l'extension", - "extension-type": "Type d'extension", - "extensions": "Extensions", - "field-required": "Le champ est obligatoire", - "file": "Fichier d'extensions", - "filter-expression": "Expression du filtre", - "host": "Hôte", - "id": "Id", - "import-extension": "Importer une extension", - "import-extensions": "Importer des extensions", - "import-extensions-configuration": "Importer la configuration des extensions", - "invalid-file-error": "Fichier d'extension non valide", - "json-name-expression": "Expression json du nom du dispositif", - "json-parse": "Impossible d'analyser json transformer.", - "json-required": "Transformer json est requis.", - "json-type-expression": "Expression json du type de dispositif", - "key": "Clé", - "mapping": "Mappage", - "method-filter": "Filtre de méthode", - "modbus-add-server": "Ajouter serveur/esclave", - "modbus-add-server-prompt": "Veuillez ajouter serveur/esclave", - "modbus-attributes-poll-period": "Période d'interrogation des attributs (ms)", - "modbus-baudrate": "Débit en bauds", - "modbus-byte-order": "Ordre des octets", - "modbus-databits": "Bits de données", - "modbus-databits-range": "Les bits de données doivent être compris entre 7 et 8.", - "modbus-device-name": "Nom du dispositif", - "modbus-encoding": "Encodage", - "modbus-function": "Fonction", - "modbus-parity": "parité", - "modbus-poll-period": "Période d'interrogation (ms)", - "modbus-poll-period-range": "La période d'interrogation doit être une valeur positive.", - "modbus-port-name": "Nom du port série", - "modbus-register-address": "Adresse du registre", - "modbus-register-address-range": "L'adresse du registre doit être comprise entre 0 et 65535.", - "modbus-register-bit-index": "Bit index", - "modbus-register-bit-index-range": "L'index de bit doit être compris entre 0 et 15.", - "modbus-register-count": "Nombre de registre", - "modbus-register-count-range": "Le nombre de registres doit être une valeur positive.", - "modbus-server": "Serveurs / esclaves", - "modbus-stopbits": "Bits d'arrêt", - "modbus-stopbits-range": "Les bits d'arrêt doivent être compris entre 1 et 2.", - "modbus-tag": "Tag", - "modbus-timeseries-poll-period": "Période d'interrogation des Timeseries (ms)", - "modbus-transport": "Transport", - "modbus-unit-id": "Id de l'unité", - "modbus-unit-id-range": "L'ID de l'unité doit être compris entre 1 et 247.", - "no-file": "Aucun fichier sélectionné.", - "opc-add-server": "Ajouter un serveur", - "opc-add-server-prompt": "Veuillez ajouter un serveur", - "opc-application-name": "Nom de l'application", - "opc-application-uri": "Uri de l'application", - "opc-device-name-pattern": "modèle de nom du dispositif", - "opc-device-node-pattern": "modèle de noeud de dispositif", - "opc-identity": "Identité", - "opc-keystore": "Magasin de clés", - "opc-keystore-alias": "Alias", - "opc-keystore-key-password": "Mot de passe de la clé", - "opc-keystore-location": "Emplacement *", - "opc-keystore-password": "Mot de passe", - "opc-keystore-type": "Type", - "opc-scan-period-in-seconds": "Période d'analyse en secondes", - "opc-security": "Sécurité", - "opc-server": "Serveurs", - "opc-type": "Type", - "password": "Mot de passe", - "pem": "PEM", - "port": "Port", - "port-range": "Le port doit être compris entre 1 et 65535.", - "private-key": "Fichier de clé privée *", - "request-id-expression": "Expression de demande d'id", - "request-id-json-expression": "Expression json de la demande d'id", - "request-id-topic-expression": "Expression de la demande d'id du topic", - "request-topic-expression": "Expression de la demande du topic", - "response-timeout": "Délai de réponse en millisecondes", - "response-topic-expression": "Expression du topic de la réponse", - "retry-interval": "Intervalle de nouvelle tentative en millisecondes", - "selected-extensions": "{count, plural, =1 {1 extension} other {# extensions} } sélectionné", - "server-side-rpc": "RPC côté serveur", - "ssl": "Ssl", - "sync": { - "last-sync-time": "Dernière heure de synchronisation", - "not-available": "Non disponible", - "not-sync": "Non sync", - "status": "Status", - "sync": "Sync" + "documentation" : { + "title" : "Documentation", + "add-link" : "Ajouter un lien", + "add-link-title" : "Ajouter un lien de documentation", + "name" : "Nom", + "name-required" : "Le nom est requis.", + "link" : "Lien", + "link-required" : "Le lien est requis.", + "columns" : "Colonnes" + }, + "quick-links" : { + "title" : "Liens rapides", + "add-link" : "Ajouter un lien", + "add-link-title" : "Ajouter un lien rapide", + "quick-link" : "Lien rapide", + "quick-link-required" : "Le lien rapide est requis.", + "no-links-matching" : "Aucun lien correspondant à '{{name}}' trouvé.", + "columns" : "Colonnes" + }, + "recent-dashboards" : { + "title" : "Tableaux de bord", + "last" : "Dernière consultation", + "starred" : "Favoris", + "name" : "Nom", + "last-viewed" : "Dernière consultation", + "no-last-viewed-dashboards" : "Aucun tableau de bord consulté récemment" + }, + "configured-features" : { + "title" : "Fonctionnalités configurées", + "info" : "Statut des fonctionnalités nécessitant une configuration", + "email-feature" : "E-mail", + "sms-feature" : "SMS", + "slack-feature" : "Slack", + "oauth2-feature" : "OAuth 2", + "2fa-feature" : "Authentification à deux facteurs", + "feature-configured" : "Fonctionnalité configurée.\nCliquez pour configurer", + "feature-not-configured" : "Fonctionnalité non configurée.\nCliquez pour configurer" + }, + "version-info" : { + "title" : "Version", + "contact-us" : "Contactez-nous", + "current-version" : "Version actuelle", + "current" : "Actuelle", + "available-version" : "Version disponible", + "available" : "Disponible", + "upgrade" : "Mettre à jour", + "version-is-up-to-date" : "La version est à jour" + }, + "usage-info" : { + "title" : "Utilisation", + "entities" : "Entités", + "api-calls" : "Appels API" + }, + "functions" : { + "title" : "Fonctions", + "pe-feature-tooltip" : "Disponible uniquement dans ThingsBoard\nÉdition Professionnelle", + "switch-to-pe" : "Passer à l'Édition Pro", + "alarms" : "Alarmes", + "dashboards" : "Tableaux de bord", + "entities-and-relations" : "Entités et relations", + "profiles" : "Profils", + "advanced-features" : "Fonctionnalités avancées", + "notification-center" : "Centre de notifications", + "api-usage" : "Utilisation de l'API", + "customers" : "Clients", + "customers-hierarchy" : "Hiérarchie des clients", + "roles-and-permissions" : "Rôles et permissions", + "groups" : "Groupes", + "integrations" : "Intégrations", + "solution-templates" : "Modèles de solution", + "scheduler" : "Planificateur", + "white-labeling" : "Personnalisation de marque" + }, + "devices" : { + "view-docs" : "Voir la documentation", + "inactive" : "Inactif", + "active" : "Actif", + "total" : "Total" + }, + "alarms" : { + "critical" : "Critique", + "assigned-to-me" : "Assignées à moi", + "total" : "Total" + }, + "getting-started" : { + "get-started" : "Commencer", + "finish" : "Terminer", + "done-welcome-title" : "Bienvenue à bord", + "done-welcome-text" : "Vous avez réussi avec brio !", + "sys-admin" : { + "step1" : { + "title" : "Créer un Locataire et un Administrateur de Locataire", + "content" : "

Un locataire est une personne ou une organisation qui possède ou produit des dispositifs et des actifs. Le locataire peut avoir plusieurs administrateurs, clients, dispositifs et actifs.

L'administrateur de locataire peut créer et gérer des dispositifs, actifs, clients et tableaux de bord dans le compte du locataire.

Suivez la documentation pour savoir comment procéder :

", + "how-to-create-tenant" : "Comment créer un Locataire et un Administrateur de Locataire" + }, + "step2" : { + "title" : "Configurer la fonctionnalité : Serveur de messagerie", + "content" : "

La configuration du serveur de messagerie est essentielle pour l'activation des utilisateurs, la récupération de mot de passe et la livraison des notifications d'alarme.

Suivez la documentation pour savoir comment procéder :

", + "how-to-configure-mail-server" : "Comment configurer le serveur de messagerie" }, - "timeout": "Délai d'attente en millisecondes", - "timeseries": "Timeseries", - "to-double": "Au double", - "token": "Jeton de sécurité", - "topic": "Topic", - "topic-expression": "Expression du topic", - "topic-filter": "Filtre du topic", - "topic-name-expression": "Expression du nom du dispositif (topic)", - "topic-type-expression": "Expression de type de dispositif (topic)", - "transformer": "Transformer", - "transformer-json": "JSON *", - "type": "Type", - "unique-id-required": "L'identifiant d'extension actuel existe déjà.", - "username": "Nom d'utilisateur", - "value": "Valeur", - "value-expression": "Expression de la valeur" - }, - "fullscreen": { - "exit": "Quitter le plein écran", - "expand": "Afficher en plein écran", - "fullscreen": "Plein écran", - "toggle": "Activer le mode plein écran" - }, - "function": { - "function": "Fonction" - }, - "grid": { - "add-item-text": "Ajouter un nouvel élément", - "delete-item": "Supprimer l'élément", - "delete-item-text": "Faites attention, après la confirmation, cet élément et toutes les données associées deviendront irrécupérables.", - "delete-item-title": "Êtes-vous sûr de vouloir supprimer cet élément?", - "delete-items": "Supprimer les éléments", - "delete-items-action-title": "Supprimer {count, plural, =1 {1 élément} other {# éléments} }", - "delete-items-text": "Attention, après la confirmation, tous les éléments sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-items-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 élément} other {# éléments} }?", - "item-details": "Détails de l'élément", - "no-items-text": "Aucun élément trouvé", - "scroll-to-top": "Défiler vers le haut" - }, - "help": { - "goto-help-page": "Aller à la page d'aide" - }, - "home": { - "avatar": "Avatar", - "home": "Accueil", - "logout": "Déconnexion", - "menu": "Menu", - "open-user-menu": "Ouvrir le menu utilisateur", - "profile": "Profile" - }, - "icon": { - "icon": "Icône", - "material-icons": "Icônes matérielles", - "select-icon": "Sélectionner l'icône", - "show-all": "Afficher toutes les icônes" - }, - "import": { - "drop-file": "Déposez un fichier JSON ou cliquez pour sélectionner un fichier à télécharger.", - "no-file": "Aucun fichier sélectionné" - }, - "item": { - "selected": "Sélectionné" - }, - "js-func": { - "no-return-error": "La fonction doit renvoyer une valeur!", - "return-type-mismatch": "La fonction doit renvoyer une valeur de type '{{type}}' !", - "tidy": "Nettoyer" - }, - "key-val": { - "add-entry": "Ajouter une entrée", - "key": "Clé", - "no-data": "Aucune entrée", - "remove-entry": "Supprimer l'entrée", - "value": "Valeur" - }, - "language": { - "language": "Language" - }, - "layout": { - "color": "Couleur", - "layout": "Mise en page", - "main": "Principal", - "manage": "Gérer les mises en page", - "right": "Droite", - "select": "Sélectionner la mise en page cible", - "settings": "Paramètres de mise en page" - }, - "legend": { - "avg": "moy", - "max": "max", - "min": "min", - "position": "Position de la légende", - "settings": "Paramètres de la légende", - "show-avg": "Afficher la valeur moyenne", - "show-max": "Afficher la valeur maximale", - "show-min": "Afficher la valeur min", - "show-total": "Afficher la valeur totale", - "total": "total" - }, - "login": { - "create-password": "Créer un mot de passe", - "email": "Email", - "forgot-password": "Mot de passe oublié?", - "login": "Connexion", - "new-password": "Nouveau mot de passe", - "new-password-again": "nouveau mot de passe", - "password-again": "Mot de passe à nouveau", - "password-link-sent-message": "Le lien de réinitialisation du mot de passe a été envoyé avec succès!", - "password-reset": "Mot de passe réinitialisé", - "passwords-mismatch-error": "Les mots de passe saisis doivent être identiques!", - "remember-me": "Se souvenir de moi", - "request-password-reset": "Demander la réinitialisation du mot de passe", - "reset-password": "Réinitialiser le mot de passe", - "sign-in": "Veuillez vous connecter", - "username": "Nom d'utilisateur (courriel)", - "login-with": "Se connecter avec {{name}}", - "or": "ou" - }, - "position": { - "bottom": "Bas", - "left": "Gauche", - "right": "Droite", - "top": "Haut" - }, - "profile": { - "change-password": "Modifier le mot de passe", - "current-password": "Mot de passe actuel", - "last-login-time": "Dernière connexion", - "profile": "Profile" - }, - "relation": { - "add": "Ajouter une relation", - "add-relation-filter": "Ajouter un filtre de relation", - "additional-info": "Informations supplémentaires (JSON)", - "any-relation": "toute relation", - "any-relation-type": "N'importe quel type", - "delete": "Supprimer la relation", - "delete-from-relation-text": "Attention, après la confirmation, l'entité actuelle ne sera pas liée à l'entité '{{entityName}}'.", - "delete-from-relation-title": "Êtes-vous sûr de vouloir supprimer la relation de l'entité '{{entityName}}'?", - "delete-from-relations-text": "Attention, après la confirmation, toutes les relations sélectionnées seront supprimées et l'entité actuelle ne sera pas liée aux entités correspondantes.", - "delete-from-relations-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 relation} other {# relations} }?", - "delete-to-relation-text": "Attention, après la confirmation, l'entité '{{entityName}} ne sera plus liée à l'entité actuelle.", - "delete-to-relation-title": "Êtes-vous sûr de vouloir supprimer la relation avec l'entité '{{entityName}}'?", - "delete-to-relations-text": "Attention, après la confirmation, toutes les relations sélectionnées seront supprimées et les entités correspondantes ne seront pas liées à l'entité en cours.", - "delete-to-relations-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 relation} other {# relations} }?", - "direction": "Sens", - "direction-type": { - "FROM": "de", - "TO": "à" + "step3" : { + "title" : "Configurer la fonctionnalité : Fournisseur SMS", + "content" : "

Configurez les fournisseurs SMS pour notifier les clients des alarmes par SMS.

Suivez la documentation pour savoir comment procéder :

", + "how-to-configure-sms-provider" : "Comment configurer le fournisseur SMS" }, - "edit": "Modifier la relation", - "from-entity": "De l'entité", - "from-entity-name": "Du nom d'entité", - "from-entity-type": "Du type d'entité", - "from-relations": "Relations sortantes", - "invalid-additional-info": "Impossible d'analyser les informations supplémentaires json.", - "relation-filters": "Filtres de relation", - "relation-type": "Type de relation", - "relation-type-required": "Le type de relation est requis.", - "relations": "Relations", - "remove-relation-filter": "Supprimer le filtre de relation", - "search-direction": { - "FROM": "De", - "TO": "Vers" + "step4" : { + "title" : "Configurer la fonctionnalité : Marque blanche", + "content" : "

Personnalisez facilement le logo et le schéma de couleurs de votre entreprise ou produit, sans codage et sans redémarrer le service.

Suivez la documentation pour savoir comment procéder :

" }, - "selected-relations": "{count, plural, =1 {1 relation} other {# relations} } sélectionné", - "to-entity": "Vers l'entité", - "to-entity-name": "vers le nom de l'entité", - "to-entity-type": "Vers le type d'entité", - "to-relations": "Relations entrantes", - "type": "Type" - }, - "rulechain": { - "add": "Ajouter une chaîne de règles", - "add-rulechain-text": "Ajouter une nouvelle chaîne de règles", - "copyId": "Copier l'identifiant de la chaîne de règles", - "create-new-rulechain": "Créer une nouvelle chaîne de règles", - "debug-mode": "Mode de débogage", - "delete": "Supprimer la chaîne de règles", - "delete-rulechain-text": "Attention, après la confirmation, la chaîne de règles et toutes les données associées deviendront irrécupérables.", - "delete-rulechain-title": "Voulez-vous vraiment supprimer la chaîne de règles '{{ruleChainName}}'?", - "delete-rulechains-action-title": "Supprimer {count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} }", - "delete-rulechains-text": "Attention, après la confirmation, toutes les chaînes de règles sélectionnées seront supprimées et toutes les données associées deviendront irrécupérables.", - "delete-rulechains-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} }?", - "description": "Description", - "details": "Détails", - "events": "Evénements", - "export": "Exporter la chaîne de règles", - "export-failed-error": "Impossible d'exporter la chaîne de règles: {{error}}", - "idCopiedMessage": "L'ID de la chaîne de règles a été copié dans le presse-papier", - "import": "Importer la chaîne de règles", - "invalid-rulechain-file-error": "Impossible d'importer la chaîne de règles: structure de données de la chaîne de règles non valide", - "management": "Gestion des règles", - "name": "Nom", - "name-required": "Le nom est requis.", - "no-rulechains-matching": "Aucune chaîne de règles correspondant à {{entity}} n'a été trouvée.", - "no-rulechains-text": "Aucune chaîne de règles trouvée", - "root": "Racine", - "rulechain": "Chaîne de règles", - "rulechain-details": "Détails de la chaîne de règles", - "rulechain-file": "Fichier de chaîne de règles", - "rulechain-required": "Chaîne de règles requise", - "rulechains": "Chaînes de règles", - "select-rulechain": "Sélectionner la chaîne de règles", - "set-root": "Rendre la chaîne de règles racine (root) ", - "set-root-rulechain-text": "Après la confirmation, la chaîne de règles deviendra racine (root) et gérera tous les messages de transport entrants.", - "set-root-rulechain-title": "Voulez-vous vraiment que la chaîne de règles '{{ruleChainName}} soit racine (root) ?", - "system": "Système", - "assign-rulechains": "Attribuer aux chaînes de règles", - "delete-rulechains": "Supprimer une chaînes de règles", - "unassign-rulechain": "Retirer chaîne de règles", - "unassign-rulechains": "Retirer chaînes de règles", - "unassign-rulechain-title": "AÊtes-vous sûr de vouloir retirer l'attribution de chaînes de règles '{{ruleChainName}}'?", - "unassign-rulechain-from-edge-text": "Après la confirmation, l'actif sera non attribué et ne sera pas accessible a la bordure.", - "unassign-rulechains-from-edge-action-title": "Retirer {count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} } de la bordure", - "unassign-rulechains-from-edge-text": "Après la confirmation, tous les chaînes de règles sélectionnés ne seront pas attribués et ne seront pas accessibles a la bordure.", - "assign-rulechain-to-edge-title": "Attribuer les chaînes de règles a la bordure", - "assign-rulechain-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les chaînes de règles", - "set-edge-template-root-rulechain": "Rendre le modèle de bord de chaîne de règles racine", - "set-edge-template-root-rulechain-title": "Voulez-vous vraiment définir la racine du modèle d'arête de la chaîne de règles '{{ruleChainName}}'?", - "set-edge-template-root-rulechain-text": "Après la confirmation, la chaîne de règles deviendra la racine du modèle d'arête et sera la chaîne de règles racine pour les arêtes nouvellement créées.", - "invalid-rulechain-type-error": "Impossible d'importer la chaîne de règles: type de chaîne de règles non valide. Le type attendu est {{attenduRuleChainType}}.", - "set-auto-assign-to-edge": "Attribuer une chaîne de règles aux arêtes lors de la création", - "set-auto-assign-to-edge-title": "Voulez-vous vraiment attribuer automatiquement la chaîne de règles d'arête '{{ruleChainName}}' à l'arête (s) lors de la création?", - "set-auto-assign-to-edge-text": "Après la confirmation, la chaîne de règles d'arête sera automatiquement affectée à l'arête (s) lors de la création.", - "unset-auto-assign-to-edge": "Non défini, attribuer une chaîne de règles aux arêtes lors de la création", - "unset-auto-assign-to-edge-title": "Voulez-vous vraiment annuler l'attribution de la chaîne de règles d'arête \"{{ruleChainName}}\" aux arêtes lors de la création?", - "unset-auto-assign-to-edge-text": "Après la confirmation, la chaîne de règles d'arêtes ne sera plus automatiquement affectée aux arêtes lors de la création.", - "edge-template-root": "Racine du modèle", - "search": "Rechercher des chaînes de règles", - "selected-rulechains": "{count, plural, =1 {1 rule chain} other {# rule chains} } sélectionné", - "open-rulechain": "Ouvrir la Chaîne de règles", - "assign-to-edge": "Attribuer à Bordure", - "edge-rulechain": "Chaîne de règles Bordure", - "unassign-rulechains-from-edge-title": "Voulez-vous vraiment annuler l'attribution de {count, plural, =1 {1 rulechain} other {# rulechains} }?" - }, - "rulenode": { - "add": "Ajouter un noeud de règle", - "add-link": "Ajouter un lien", - "configuration": "Configuration", - "copy-selected": "Copier les éléments sélectionnés", - "create-new-link-label": "Créez un nouveau!", - "custom-link-label": "Etiquette de lien personnalisée", - "custom-link-label-required": "Une étiquette de lien personnalisée est requise", - "debug-mode": "Mode de débogage", - "delete": "Supprimer le noeud de règle", - "delete-selected": "Supprimer les éléments sélectionnés", - "delete-selected-objects": "Supprimer les nœuds et les connexions sélectionnés", - "deselect-all": "Désélectionner tout", - "deselect-all-objects": "Désélectionnez tous les nœuds et toutes les connexions", - "details": "Détails", - "directive-is-not-loaded": "La directive de configuration définie '{{directiveName}} n'est pas disponible.", - "events": "Événements", - "help": "Aide", - "invalid-target-rulechain": "Impossible de résoudre la chaîne de règles cible!", - "link": "Lien", - "link-details": "Détails du lien du noeud de la règle", - "link-label": "Étiquette du lien", - "link-label-required": "L'étiquette du lien est obligatoire", - "link-labels": "Étiquettes de lien", - "link-labels-required": "Les étiquettes de lien sont obligatoires", - "message": "Message", - "message-type": "Type de message", - "message-type-required": "Le type de message est obligatoire", - "metadata": "Métadonnées", - "metadata-required": "Les entrées de métadonnées ne peuvent pas être vides.", - "name": "Nom", - "name-required": "Le nom est requis.", - "no-link-label-matching": "'{{label}}' introuvable.", - "no-link-labels-found": "Aucune étiquette de lien trouvée", - "open-node-library": "Ouvrir la bibliothèque de noeud", - "output": "Output", - "rulenode-details": "Détails du noeud de la régle", - "search": "Recherche de noeuds", - "select-all": "Tout sélectionner", - "select-all-objects": "Sélectionnez tous les noeuds et connexions", - "select-message-type": "Sélectionner le type de message", - "test": "Test", - "test-script-function": "Tester le script", - "type": "Type", - "type-action": "Action", - "type-action-details": "Effectuer une action spéciale", - "type-enrichment": "Enrichissement", - "type-enrichment-details": "Ajouter des informations supplémentaires dans les métadonnées de message", - "type-external": "Externe", - "type-external-details": "Interagit avec le systéme externe", - "type-filter": "Filtre", - "type-filter-details": "Filtrer les messages entrants avec des conditions configurées", - "type-input": "Input", - "type-input-details": "Entrée logique de la chaîne de règles, transmet les messages entrants au prochain nœud de règle associé", - "type-rule-chain": "Chaîne de régles", - "type-rule-chain-details": "Transmet les messages entrants à la chaîne de régles spécifiée", - "type-transformation": "Transformation", - "type-transformation-details": "Modifier le payload du message et les métadonnées ", - "type-unknown": "Inconnu", - "type-unknown-details": "Noeud de règle non résolu", - "ui-resources-load-error": "Impossible de charger les ressources de configuration de l'interface utilisateur." - }, - "timezone": { - "timezone": "Fuseau horaire", - "select-timezone": "Sélectionnez le fuseau horaire", - "no-timezones-matching": "Aucun fuseau horaire correspondant à '{{timezone}}' n'a été trouvé.", - "timezone-required": "Le fuseau horaire est requis." - }, - "tenant": { - "add": "Ajouter un Tenant", - "add-tenant-text": "Ajouter un nouveau Tenant", - "admins": "Admins", - "copyId": "Copier l'Id du Tenant", - "delete": "Supprimer le Tenant", - "delete-tenant-text": "Attention, après la confirmation, le Tenant et toutes les données associées deviendront irrécupérables.", - "delete-tenant-title": "Êtes-vous sûr de vouloir supprimer le tenant '{{tenantTitle}}'?", - "delete-tenants-action-title": "Supprimer {count, plural, =1 {1 tenant} other {# tenants} }", - "delete-tenants-text": "Attention, après la confirmation, tous les Tenants sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-tenants-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 tenant} other {# tenants} }?", - "description": "Description", - "details": "Détails", - "events": "Événements", - "idCopiedMessage": "L'Id du Tenant a été copié dans le Presse-papiers", - "manage-tenant-admins": "Gérer les administrateurs du Tenant", - "management": "Gestion des Tenants", - "no-tenants-matching": "Aucun Tenant correspondant à {{entity}} n'a été trouvé. ", - "no-tenants-text": "Aucun Tenant trouvé", - "select-tenant": "Sélectionner un Tenant", - "tenant": "Tenant", - "tenant-details": "Détails du Tenant", - "tenant-required": "Tenant requis", - "tenants": "Tenants", - "title": "Titre", - "title-required": "Le titre est requis.", - "search": "Rechercher les Tenants", - "selected-tenants": "{ count, plural, =1 {1 tenant} other {# tenants} } sélectionnés" - }, - "timeinterval": { - "advanced": "Avancé", - "days": "Jours", - "days-interval": "{days, plural, =1 {1 jour} other {# jours} }", - "hours": "Heures", - "hours-interval": "{hours, plural, =1 {1 heure} other {# heures} }", - "minutes": "Minutes", - "minutes-interval": "{minutes, plural, =1 {1 minute} other {# minutes} }", - "seconds": "Secondes", - "seconds-interval": "{seconds, plural, =1 {1 seconde} other {# secondes} }" - }, - "timewindow": { - "date-range": "Plage de dates", - "days": "{days, plural, =1 {jour} other {# jours} }", - "edit": "Modifier timewindow", - "history": "Historique", - "hours": "{hours, plural, =0 {heure} =1 {1 heure} other {# heures} }", - "last": "Dernier", - "last-prefix": "dernier", - "minutes": "{minutes, plural, =0 {minute} =1 {1 minute} other {# minutes} }", - "period": "de {{startTime}} à {{endTime}}", - "realtime": "Temps réel", - "seconds": "{seconds, plural, =0 {second} =1 {1 second} other {# seconds} }", - "time-period": "Période", - "hide": "Masquer" - }, - "user": { - "activation-email-sent-message": "Le courriel d'activation a été envoyé avec succès!", - "activation-link": "Lien d'activation utilisateur", - "activation-link-copied-message": "le lien d'activation de l'utilisateur a été copié dans le presse-papier", - "activation-link-text": "Pour activer l'utilisateur, utilisez le lien d'activation suivant: ", - "activation-method": "Méthode d'activation", - "add": "Ajouter un utilisateur", - "add-user-text": "Ajouter un nouvel utilisateur", - "always-fullscreen": "Toujours en plein écran", - "anonymous": "Anonyme", - "copy-activation-link": "Copier le lien d'activation", - "customer": "Client", - "customer-users": "Utilisateurs du client", - "default-dashboard": "Tableau de bord par défaut", - "delete": "Supprimer l'utilisateur", - "delete-user-text": "Attention, après la confirmation, l'utilisateur et toutes les données associées deviendront irrécupérables.", - "delete-user-title": "Êtes-vous sûr de vouloir supprimer l'utilisateur '{{userEmail}}'?", - "delete-users-action-title": "Supprimer {count, plural, =1 {1 utilisateur} other {# utilisateurs} }", - "delete-users-text": "Attention, après la confirmation, tous les utilisateurs sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-users-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 utilisateur} other {# utilisateurs} }?", - "description": "Description", - "details": "Détails", - "disable-account": "Désactiver le compte d'utilisateur", - "disable-account-message": "Le compte d'utilisateur a été désactivé avec succès!", - "display-activation-link": "Afficher le lien d'activation", - "email": "Email", - "email-required": "Email est requis.", - "enable-account": "Activer le compte d'utilisateur", - "enable-account-message": "Le compte d'utilisateur a été activé avec succès!", - "first-name": "Prénom", - "invalid-email-format": "Format de courrier électronique non valide", - "last-name": "Nom de famille", - "login-as-customer-user": "Se connecter en tant qu'utilisateur client", - "login-as-tenant-admin": "Connectez-vous en tant qu'administrateur Tenant", - "no-users-matching": "Aucun utilisateur correspondant à '{{entity}}' n'a été trouvé.", - "no-users-text": "Aucun utilisateur trouvé", - "resend-activation": "Renvoyer l'activation", - "select-user": "Sélectionner l'utilisateur", - "send-activation-mail": "Envoyer un mail d'activation", - "sys-admin": "Administrateur du système", - "tenant-admin": "Administrateur du Tenant", - "tenant-admins": "administrateurs du Tenant", - "user": "utilisateur", - "user-details": "Détails de l'utilisateur", - "user-required": "L'utilisateur est requis", - "users": "Utilisateurs", - "search": "Rechercher des utilisateurs", - "selected-users": "{ count, plural, =1 {1 user} other {# users} } sélectionnés" - }, - "value": { - "boolean": "booléen", - "boolean-value": "Valeur booléenne", - "double": "Double", - "double-value": "Valeur double", - "false": "Faux", - "integer": "Entier", - "integer-value": "Valeur entière", - "invalid-integer-value": "Valeur entière invalide", - "long": "Long", - "string": "String", - "string-value": "Valeur String", - "true": "Vrai", - "type": "Type de valeur" - }, - "widget": { - "add": "Ajouter un widget", - "add-resource": "Ajouter une ressource", - "add-widget-type": "Ajouter un nouveau type de widget", - "alarm": "Widget d'alarme", - "css": "CSS", - "datakey-settings-schema": "Schéma des paramètres de Data key", - "edit": "Modifier le widget", - "editor": " Editeur de widget", - "export": "Exporter widget", - "html": "HTML", - "javascript": "Javascript", - "latest": "Dernières valeurs", - "management": "Gestion des widgets", - "missing-widget-title-error": "Le titre du widget doit être spécifié!", - "no-data-found": "Aucune donnée trouvée", - "remove": "Supprimer le widget", - "remove-resource": "Supprimer une ressource", - "remove-widget-text": "Après la confirmation, le widget et toutes les données associées deviendront irrécupérables.", - "remove-widget-title": "Êtes-vous sûr de vouloir supprimer le widget '{{widgetTitle}}'?", - "resource-url": "URL JavaScript / CSS", - "resources": "Ressources", - "rpc": "Widget de contrôle", - "run": "Exécuter un widget", - "save": "Enregistrer le widget", - "save-widget-type-as": "Enregistrer le type de widget sous", - "save-widget-type-as-text": "Veuillez saisir un nouveau titre de widget et / ou sélectionner un ensemble de widgets cibles", - "saveAs": "Enregistrer le widget sous", - "search-data": "Rechercher des données", - "select-widget-type": "Sélectionnez le type de widget", - "select-widgets-bundle": "Sélectionner un ensemble de widgets", - "settings-schema": "Schéma des paramétres", - "static": "Widget statique", - "tidy": "Nettoyer", - "timeseries": "Séries chronologiques", - "title": "Titre du widget", - "title-required": "Le titre du widget est requis.", - "toggle-fullscreen": "Basculer le mode plein écran", - "type": "Type de widget", - "unable-to-save-widget-error": "Impossible de sauvegarder le widget! Le widget a des erreurs!", - "undo": "Annuler les modifications du widget", - "widget-bundle": "Ensemble de widget", - "widget-library": "Bibliothèque de widgets", - "widget-saved": "Widget enregistré", - "widget-template-load-failed-error": "Impossible de charger le modéle de widget!", - "widget-type-load-error": "Le widget n'a pas été chargé à cause des erreurs suivantes:", - "widget-type-not-found": "Problème de chargement de la configuration du widget.
Le type de widget associé a probablement été supprimé.", - "no-data": "Aucune donnée à afficher sur le widget" - }, - "widget-action": { - "custom": "Action personnalisée", - "header-button": "Bouton d'en-tête de widget", - "open-dashboard": "Naviguer vers un autre tableau de bord", - "open-dashboard-state": "Naviguer vers un nouvel état du tableau de bord", - "open-right-layout": "Ouvrir la disposition du tableau de bord droite (vue mobile)", - "set-entity-from-widget": "Définir l'entité à partir du widget", - "target-dashboard": "Tableau de bord cible", - "target-dashboard-state": "État du tableau de bord cible", - "target-dashboard-state-required": "L'état du tableau de bord cible est requis", - "update-dashboard-state": "Mettre à jour l'état actuel du tableau de bord" - }, - "widget-config": { - "action": "Action", - "action-icon": "Icône", - "action-name": "Nom", - "action-name-not-unique": "Une autre action portant le même nom existe déjà. \n Le nom de l'action doit être unique dans la même source d'action.", - "action-name-required": "Le nom de l'action est requis", - "action-source": "Source de l'action", - "action-source-required": "Une source d'action est requise.", - "action-type": "Type", - "action-type-required": "Le type d'action est requis.", - "actions": "Actions", - "add-action": "Ajouter une action", - "add-datasource": "Ajouter une source de données", - "advanced": "Avancé", - "alarm-source": "Source d'alarme", - "background-color": "couleur de fond", - "data": "Données", - "datasource-parameters": "Paramètres", - "datasource-type": "Type", - "datasources": "Sources de données", - "decimals": "Nombre de chiffres après virgule flottante", - "delete-action": "Supprimer l'action", - "delete-action-text": "Êtes-vous sûr de vouloir supprimer l'action du widget nommé '{{actionName}}'?", - "delete-action-title": "Supprimer l'action du widget", - "display-timewindow": "Afficher fenêtre de temps", - "display-legend": "Afficher la légende", - "display-title": "Afficher le titre", - "drop-shadow": "Ombre portée", - "edit-action": "Modifier l'action", - "enable-fullscreen": "Activer le plein écran", - "general-settings": "Paramètres généraux", - "height": "Hauteur", - "margin": "Marge", - "maximum-datasources": "Maximum {count, plural, =1 {1 datasource est autorisé.} other {# datasources sont autorisés} }", - "mobile-mode-settings": "Paramètres du mode mobile", - "order": "Ordre", - "padding": "Padding", - "remove-datasource": "Supprimer la source de données", - "search-actions": "Recherche d'actions", - "settings": "Paramètres", - "target-device": "Dispositif cible", - "text-color": "Couleur du texte", - "timewindow": "Fenêtre de temps", - "title": "Titre", - "title-style": "Style de titre", - "title-tooltip": "Tooltip de titre", - "units": "Symbole spécial à afficher à côté de la valeur", - "use-dashboard-timewindow": "Utiliser la fenêtre de temps du tableau de bord", - "widget-style": "Style du widget", - "display-icon": "Afficher l'icône du titre", - "icon-color": "Couleur de l'icône", - "icon-size": "Taille de l'icône" - }, - "widget-type": { - "create-new-widget-type": "Créer un nouveau type de widget", - "export": "Exporter le type de widget", - "export-failed-error": "Impossible d'exporter le type de widget: {{error}}", - "import": "Importer le type de widget", - "invalid-widget-type-file-error": "Impossible d'importer le type de widget: structure de données de type widget invalide.", - "widget-type-file": "Fichier de type Widget" - }, - "widgets": { - "date-range-navigator": { - "localizationMap": { - "Sun": "Dim.", - "Mon": "Lun.", - "Tue": "Mar.", - "Wed": "Mer.", - "Thu": "Jeu.", - "Fri": "Ven.", - "Sat": "Sam.", - "Jan": "Janv.", - "Feb": "Févr.", - "Mar": "Mars", - "Apr": "Avr.", - "May": "Mai", - "Jun": "Juin", - "Jul": "Juil.", - "Aug": "Août", - "Sep": "Sept.", - "Oct": "Oct.", - "Nov": "Nov.", - "Dec": "Déc.", - "January": "Janvier", - "February": "Février", - "March": "Mars", - "April": "Avril", - "June": "Juin", - "July": "Juillet", - "August": "Août", - "September": "Septembre", - "October": "Octobre", - "November": "Novembre", - "December": "Décembre", - "Custom Date Range": "Plage de dates personnalisée", - "Date Range Template": "Modèle de plage de dates", - "Today": "Aujourd'hui", - "Yesterday": "Hier", - "This Week": "Cette semaine", - "Last Week": "La semaine dernière", - "This Month": "Ce mois-ci", - "Last Month": "Le mois dernier", - "Year": "Année", - "This Year": "Cette année", - "Last Year": "L'année dernière", - "Date picker": "Sélecteur de date", - "Hour": "Heure", - "Day": "Journée", - "Week": "La semaine", - "2 weeks": "2 Semaines", - "Month": "Mois", - "3 months": "3 Mois", - "6 months": "6 Mois", - "Custom interval": "Intervalle personnalisé", - "Interval": "Intervalle", - "Step size": "Taille de pas", - "Ok": "Ok" - } + "step5" : { + "title" : "Configurer la fonctionnalité : 2FA", + "content" : "

Améliorez la sécurité des comptes de la plateforme avec l'authentification à deux facteurs.

Suivez la documentation pour savoir comment procéder :

" }, - "input-widgets": { - "attribute-not-allowed": "Le paramètre d'attribut ne peut pas être utilisé dans ce widget", - "date": "Date", - "discard-changes": "Annuler les modifications", - "entity-attribute-required": "L'attribut d'entité est requis", - "entity-timeseries-required": "Entité timeseries est requis", - "not-allowed-entity": "L'entité sélectionnée ne peut pas avoir d'attributs partagés", - "no-attribute-selected": "Aucun attribut n'est sélectionné", - "no-datakey-selected": "Aucune date n'est sélectionnée", - "no-entity-selected": "Aucune entité sélectionnée", - "no-image": "Pas d'image", - "no-support-web-camera": "Pas de webcam supportée", - "no-timeseries-selected": "Aucune série temporelle sélectionnée", - "switch-attribute-value": "Changer la valeur de l'attribut d'entité", - "switch-camera": "Changer de caméra", - "switch-timeseries-value": "Changer la valeur de l'entité série temporelle", - "take-photo": "Prendre une photo", - "time": "Temps", - "timeseries-not-allowed": "Le paramètre série temporelle ne peut pas être utilisé dans ce widget", - "update-failed": "Mise à jour a échoué", - "update-successful": "Mise à jour réussie", - "update-attribute": "Attribut de mise à jour", - "update-timeseries": "Mise à jour de la série temporelle", - "value": "Valeur" + "step6" : { + "title" : "Configurer la fonctionnalité : OAuth 2", + "content" : "

Simplifiez la connexion pour les utilisateurs locataires et clients avec la fonctionnalité Single Sign-On via OAuth 2.0.

Suivez la documentation pour savoir comment procéder :

" } - }, - "widgets-bundle": { - "add": "Ajouter un groupe de widgets", - "add-widgets-bundle-text": "Ajouter un nouveau groupe de widgets", - "create-new-widgets-bundle": "Créer un nouveau groupe de widgets", - "current": "Groupe actuel", - "delete": "Supprimer le groupe de widgets", - "delete-widgets-bundle-text": "Attention, après la confirmation, le groupe de widgets et toutes les données associées deviendront irrécupérables.", - "delete-widgets-bundle-title": "Êtes-vous sûr de vouloir supprimer le groupe de widgets '{{widgetsBundleTitle}}'?", - "delete-widgets-bundles-action-title": "Supprimer {count, plural, =1 {1 groupe de widgets} other {# groupes de widgets} }", - "delete-widgets-bundles-text": "Attention, après la confirmation, tous les groupes de widgets sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-widgets-bundles-title": "Voulez-vous vraiment supprimer {count, plural, =1 {1 groupe de widgets} other {# groupes de widgets} }?", - "details": "Détails", - "empty": "Le groupe de widgets est vide", - "export": "Exporter le groupe de widgets", - "export-failed-error": "Impossible d'exporter le groupe de widgets: {{error}}", - "import": "Importer un groupe de widgets", - "invalid-widgets-bundle-file-error": "Impossible d'importer un groupe de widgets: structure de données du groupe de widgets non valides.", - "no-widgets-bundles-matching": "Aucun groupe de widgets correspondant à {{widgetsBundle}} n'a été trouvé.", - "no-widgets-bundles-text": "Aucun groupe de widgets trouvé", - "system": "Système", - "title": "Titre", - "title-required": "Le titre est requis.", - "widgets-bundle-details": "Détails des groupes de widgets", - "widgets-bundle-file": "Fichier de groupe de widgets", - "widgets-bundle-required": "Un groupe de widgets est requis.", - "widgets-bundles": "Groupes de widgets" + }, + "tenant-admin" : { + "step1" : { + "title" : "Créer un dispositif", + "content" : "

Approvisionnons votre premier dispositif sur la plateforme via l'interface utilisateur. Suivez la documentation pour savoir comment procéder :

", + "how-to-create-device" : "Comment créer un dispositif" + }, + "step2" : { + "title" : "Connecter le dispositif", + "content-before" : "

Pour connecter le dispositif, vous devez obtenir ses identifiants. Nous recommandons d'utiliser les identifiants auto-générés par défaut, à savoir le jeton d'accès pour ce guide.

  • Allez dans le tableau des dispositifs
  • Cliquez sur la ligne du dispositif pour ouvrir les détails
  • Appuyez sur le bouton \"Copier le jeton d'accès\"

Utilisez les commandes simples suivantes pour publier des données via HTTP. N'oubliez pas de remplacer $ACCESS_TOKEN par le jeton d'accès de votre dispositif :

", + "ubuntu" : { + "install-curl" : "Installer cURL pour Ubuntu :" + }, + "macos" : { + "install-curl" : "Installer cURL pour macOS :" + }, + "windows" : { + "install-curl" : "Depuis Windows 10 b17063, cURL est disponible par défaut." + }, + "replace-access-token" : "Remplacez $ACCESS_TOKEN par le jeton de votre dispositif :", + "content-after" : "

Vous pouvez également utiliser d'autres protocoles tels que MQTT, CoAP, etc.

Suivez la documentation pour savoir comment procéder :

", + "how-to-connect-device" : "Comment connecter un dispositif" + }, + "step3" : { + "title" : "Créer un tableau de bord", + "content" : "

Créez un tableau de bord pour visualiser les données des entités telles que les dispositifs, les actifs, etc.

Suivez la documentation pour savoir comment procéder :

", + "how-to-create-dashboard" : "Comment créer un tableau de bord" + }, + "step4" : { + "title" : "Configurer des règles d'alarme", + "alarm-rules" : "Règles d'alarme", + "content" : "

Déclenchons une alarme lorsque la température atteint 25°C. Suivez la documentation pour savoir comment procéder :

", + "how-to-configure-alarm-rules" : "Comment configurer des règles d'alarme" + }, + "step5" : { + "title" : "Créer une alarme", + "content-before" : "

Pour déclencher l'alarme, envoyez une nouvelle valeur de télémétrie de 26°C ou plus.

", + "replace-access-token" : "Remplacez $ACCESS_TOKEN par le jeton de votre dispositif :", + "content-after" : "

Suivez la documentation pour savoir comment procéder :

", + "how-to-create-alarm" : "Comment créer une alarme" + }, + "step6" : { + "title" : "Créer un client et partager le tableau de bord", + "content" : "

En créant des tableaux de bord pour les utilisateurs finaux, un utilisateur client ne peut voir que ses propres dispositifs. Les données d'autres clients lui seront invisibles.

Suivez la documentation pour savoir comment procéder :

" + } + } + } + }, + "icon" : { + "icon" : "Icône", + "icons" : "Icônes", + "select-icon" : "Sélectionner une icône", + "material-icons" : "Icônes Material", + "show-all" : "Afficher toutes les icônes", + "search-icon" : "Rechercher une icône", + "no-icons-found" : "Aucune icône trouvée pour '{{iconSearch}}'" + }, + "phone-input" : { + "phone-input-label" : "Numéro de téléphone", + "phone-input-required" : "Le numéro de téléphone est requis", + "phone-input-validation" : "Le numéro de téléphone est invalide ou impossible", + "phone-input-pattern" : "Numéro de téléphone invalide. Doit être au format E.164, ex. {{phoneNumber}}", + "phone-input-hint" : "Numéro de téléphone au format E.164, ex. {{phoneNumber}}" + }, + "custom" : { + "widget-action" : { + "action-cell-button" : "Bouton d'action de cellule", + "row-click" : "Au clic sur la ligne", + "cell-click" : "Au clic sur la cellule", + "polygon-click" : "Au clic sur le polygone", + "marker-click" : "Au clic sur le marqueur", + "circle-click" : "Au clic sur le cercle", + "tooltip-tag-action" : "Action de balise info-bulle", + "node-selected" : "Lorsqu'un nœud est sélectionné", + "element-click" : "Au clic sur l'élément HTML", + "pie-slice-click" : "Au clic sur une tranche", + "row-double-click" : "Double clic sur la ligne", + "cell-double-click" : "Double clic sur la cellule", + "card-click" : "Au clic sur la carte", + "click" : "Au clic" + } + }, + "paginator" : { + "items-per-page" : "Éléments par page :", + "first-page-label" : "Première page", + "last-page-label" : "Dernière page", + "next-page-label" : "Page suivante", + "previous-page-label" : "Page précédente", + "items-per-page-separator" : "sur" + }, + "language" : { + "language" : "Langue", + "locales" : { + "ar_AE" : "العربية (الإمارات العربية المتحدة)", + "ca_ES" : "català (Espanya)", + "cs_CZ" : "čeština (Česko)", + "da_DK" : "dansk (Danmark)", + "de_DE" : "Deutsch (Deutschland)", + "el_GR" : "Ελληνικά (Ελλάδα)", + "en_US" : "English (United States)", + "es_ES" : "español (España)", + "fa_IR" : "فارسی (ایران)", + "fr_FR" : "français (France)", + "it_IT" : "italiano (Italia)", + "ja_JP" : "日本語 (日本)", + "ka_GE" : "ქართული (საქართველო)", + "ko_KR" : "한국어 (대한민국)", + "lt_LT" : "lietuvių (Lietuva)", + "lv_LV" : "latviešu (Latvija)", + "nl_BE" : "Nederlands (België)", + "pl_PL" : "polski (Polska)", + "pt_BR" : "português (Brasil)", + "ro_RO" : "română (România)", + "sl_SI" : "slovenščina (Slovenija)", + "tr_TR" : "Türkçe (Türkiye)", + "uk_UA" : "українська (Україна)", + "zh_CN" : "中文 (中国)", + "zh_TW" : "中文 (台灣)" } -} + } +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-it_IT.json b/ui-ngx/src/assets/locale/locale.constant-it_IT.json index 9b00f36842..1a902061b9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-it_IT.json +++ b/ui-ngx/src/assets/locale/locale.constant-it_IT.json @@ -1,1703 +1,9224 @@ { - "access": { - "unauthorized": "Non autorizzato", - "unauthorized-access": "Accesso non autorizzato", - "unauthorized-access-text": "Devi effettuare il login per accedere a questa risorsa!", - "access-forbidden": "Accesso Vietato", - "access-forbidden-text": "Non hai i diritti di accesso a questa posizione!
Prova ad effettuare il login con un diverso account.", - "refresh-token-expired": "Sessione scaduta", - "refresh-token-failed": "Impossibile aggiornare la sessione" - }, - "action": { - "activate": "Attiva", - "suspend": "Sospendi", - "save": "Salva", - "saveAs": "Salva come", - "cancel": "Cancella", - "ok": "OK", - "delete": "Elimina", - "add": "Aggiungi", - "yes": "Sì", - "no": "No", - "update": "Aggiorna", - "remove": "Rimuovi", - "search": "Cerca", - "clear-search": "Cancella ricerca", - "assign": "Assegna", - "unassign": "Annulla assegnazione", - "share": "Condividi", - "make-private": "Rendi privato", - "apply": "Applica", - "apply-changes": "Applica modifiche", - "edit-mode": "Modalità modifica", - "enter-edit-mode": "Attiva modalità di modifica", - "decline-changes": "Annulla modifiche", - "close": "Chiudi", - "back": "Indietro", - "run": "Esegui", - "sign-in": "Registrati!", - "edit": "Modifica", - "view": "Visualizza", - "create": "Crea", - "drag": "Trascina", - "refresh": "Aggiorna", - "undo": "Annulla", - "copy": "Copia", - "paste": "Incolla", - "copy-reference": "Copia riferimento", - "paste-reference": "Incolla riferimento", - "import": "Importa", - "export": "Esporta", - "share-via": "Condividi con {{provider}}", - "discard-changes": "Annulla le modifiche" - }, - "aggregation": { - "aggregation": "Aggregazione", - "function": "Funzione di aggregazione dati", - "limit": "Valori max", - "group-interval": "Intervallo di raggruppamento", - "min": "Min", - "max": "Max", - "avg": "Media", - "sum": "Somma", - "count": "Conteggio", - "none": "Nessuna" - }, - "admin": { - "general": "Generale", - "general-settings": "Impostazioni Generali", - "outgoing-mail": "Posta in uscita", - "outgoing-mail-settings": "Impostazioni Posta in uscita", - "system-settings": "Impostazioni di sistema", - "test-mail-sent": "Mail di test inviata con successo!", - "base-url": "URL di base", - "base-url-required": "URL di base obbligatoria.", - "mail-from": "Mittente", - "mail-from-required": "Mittente obbligatorio.", - "smtp-protocol": "Protocollo SMTP", - "smtp-host": "Host SMTP", - "smtp-host-required": "Host SMTP obbligatorio.", - "smtp-port": "Porta SMTP", - "smtp-port-required": "Porta SMTP obbligatoria.", - "smtp-port-invalid": "Numero di porta SMTP non valido.", - "timeout-msec": "Timeout (msec)", - "timeout-required": "Timeout obbligatorio.", - "timeout-invalid": "Timeout non valido.", - "enable-tls": "Abilita TLS", - "tls-version" : "Versione TLS", - "send-test-mail": "Invia mail di test", - "security-settings": "Settaggi di sicurezza", - "password-policy": "Politica password", - "minimum-password-length": "Lunghezza minima password", - "minimum-password-length-required": "È richiesta una lunghezza minima della password", - "minimum-password-length-range": "La lunghezza minima della password deve essere compresa tra 5 e 50", - "minimum-uppercase-letters": "Numero minimo di lettere maiuscole", - "minimum-uppercase-letters-range": "Il numero minimo di lettere maiuscole non può essere negativo", - "minimum-lowercase-letters": "Numero minimo di lettere minuscole", - "minimum-lowercase-letters-range": "Il numero minimo di lettere minuscole non può essere negativo", - "minimum-digits": "Numero minimo di cifre", - "minimum-digits-range": "Il numero minimo di cifre non può essere negativo", - "minimum-special-characters": "Numero minimo di caratteri speciali", - "minimum-special-characters-range": "Il numero minimo di caratteri speciali non può essere negativo", - "password-expiration-period-days": "Periodo di scadenza della password in giorni", - "password-expiration-period-days-range": "Il periodo di scadenza della password in giorni non può essere negativo", - "password-reuse-frequency-days": "Frequenza di riutilizzo della password in giorni", - "password-reuse-frequency-days-range": "La frequenza di riutilizzo della password in giorni non può essere negativa", - "general-policy": "Politica generale", - "max-failed-login-attempts": "Numero massimo di tentativi di accesso non riusciti, prima che l'account sia bloccato", - "minimum-max-failed-login-attempts-range": "Il numero massimo di tentativi di accesso non riusciti non può essere negativo", - "user-lockout-notification-email": "In caso di blocco dell'account utente, inviare una notifica via e-mail" - }, - "alarm": { - "alarm": "Allarme", - "alarms": "Allarmi", - "select-alarm": "Seleziona un allarme", - "no-alarms-matching": "Nessun allarme corrispondente a '{{entity}}' è stato trovato.", - "alarm-required": "Allarme richiesto", - "alarm-status": "Stato Allarme", - "search-status": { - "ANY": "Qualsiasi", - "ACTIVE": "Attivo", - "CLEARED": "Cancellato", - "ACK": "Riconosciuto", - "UNACK": "Non riconosciuto" + "access" : { + "unauthorized" : "Non autorizzato", + "unauthorized-access" : "Accesso non autorizzato", + "unauthorized-access-text" : "Devi accedere per avere accesso a questa risorsa!", + "access-forbidden" : "Accesso vietato", + "access-forbidden-text" : "Non hai i diritti di accesso a questa posizione!
Prova ad accedere con un altro utente se desideri comunque accedere a questa posizione.", + "refresh-token-expired" : "La sessione è scaduta", + "refresh-token-failed" : "Impossibile aggiornare la sessione", + "permission-denied" : "Permesso negato", + "permission-denied-text" : "Non hai i permessi per eseguire questa operazione!" + }, + "account" : { + "account" : "Account", + "notification-settings" : "Impostazioni di notifica" + }, + "action" : { + "activate" : "Attiva", + "suspend" : "Sospendi", + "save" : "Salva", + "saveAs" : "Salva come", + "move" : "Sposta", + "cancel" : "Annulla", + "ok" : "OK", + "delete" : "Elimina", + "add" : "Aggiungi", + "yes" : "Sì", + "no" : "No", + "update" : "Aggiorna", + "remove" : "Rimuovi", + "search" : "Cerca", + "clear-search" : "Cancella ricerca", + "assign" : "Assegna", + "unassign" : "Revoca assegnazione", + "share" : "Condividi", + "make-private" : "Rendi privato", + "apply" : "Applica", + "apply-changes" : "Applica modifiche", + "edit-mode" : "Modalità modifica", + "enter-edit-mode" : "Entra in modalità modifica", + "decline-changes" : "Rifiuta modifiche", + "decline" : "Rifiuta", + "close" : "Chiudi", + "back" : "Indietro", + "run" : "Esegui", + "sign-in" : "Accedi!", + "edit" : "Modifica", + "view" : "Visualizza", + "create" : "Crea", + "drag" : "Trascina", + "refresh" : "Aggiorna", + "undo" : "Annulla", + "copy" : "Copia", + "paste" : "Incolla", + "copy-reference" : "Copia riferimento", + "paste-reference" : "Incolla riferimento", + "import" : "Importa", + "export" : "Esporta", + "share-via" : "Condividi tramite {{provider}}", + "select" : "Seleziona", + "continue" : "Continua", + "discard-changes" : "Ignora modifiche", + "download" : "Scarica", + "next" : "Avanti", + "next-with-label" : "Avanti: {{label}}", + "read-more" : "Leggi di più", + "hide" : "Nascondi", + "test" : "Test", + "done" : "Fatto", + "print" : "Stampa", + "restore" : "Ripristina", + "confirm" : "Conferma", + "more" : "Altro", + "less" : "Meno", + "skip" : "Salta", + "send" : "Invia", + "reset" : "Resetta", + "show-more" : "Mostra di più", + "dont-show-again" : "Non mostrare più", + "see-documentation" : "Vedi documentazione", + "clear" : "Pulisci", + "upload" : "Carica", + "delete-anyway" : "Elimina comunque", + "delete-selected" : "Elimina selezionati", + "set" : "Imposta" + }, + "aggregation" : { + "aggregation" : "Aggregazione", + "function" : "Funzione di aggregazione dei dati", + "limit" : "Valori massimi", + "group-interval" : "Intervallo di raggruppamento", + "min" : "Minimo", + "max" : "Massimo", + "avg" : "Media", + "sum" : "Somma", + "count" : "Conteggio", + "none" : "Nessuno" + }, + "admin" : { + "settings" : "Impostazioni", + "general" : "Generale", + "general-settings" : "Impostazioni generali", + "home-settings" : "Impostazioni home", + "home" : "Home", + "outgoing-mail" : "Server di posta", + "outgoing-mail-settings" : "Impostazioni del server di posta in uscita", + "system-settings" : "Impostazioni di sistema", + "test-mail-sent" : "La mail di test è stata inviata con successo!", + "base-url" : "URL base", + "base-url-required" : "L'URL base è obbligatorio.", + "prohibit-different-url" : "Proibisci l'utilizzo del nome host dall'intestazione della richiesta del client", + "prohibit-different-url-hint" : "Questa impostazione dovrebbe essere abilitata negli ambienti di produzione. Può causare problemi di sicurezza se disabilitata", + "device-connectivity" : { + "device-connectivity" : "Connettività del dispositivo", + "http-s" : "HTTP(s)", + "mqtt-s" : "MQTT(s)", + "coap-s" : "COAP(s)", + "http" : "HTTP", + "https" : "HTTPs", + "mqtt" : "MQTT", + "mqtts" : "MQTTs", + "coap" : "COAP", + "coaps" : "COAPs", + "hint" : "Se i campi host o porta sono vuoti, verrà utilizzato il valore predefinito del protocollo.", + "host" : "Host", + "port" : "Porta", + "port-pattern" : "La porta deve essere un numero intero positivo.", + "port-range" : "La porta deve essere compresa tra 1 e 65535." + }, + "mail-from" : "Mittente", + "mail-from-required" : "Il campo Mittente è obbligatorio.", + "smtp-protocol" : "Protocollo SMTP", + "smtp-host" : "Host SMTP", + "smtp-host-required" : "L'host SMTP è obbligatorio.", + "smtp-port" : "Porta SMTP", + "smtp-port-required" : "È necessario fornire una porta SMTP.", + "smtp-port-invalid" : "Non sembra una porta SMTP valida.", + "timeout-msec" : "Timeout (msec)", + "timeout-required" : "Il timeout è obbligatorio.", + "timeout-invalid" : "Non sembra un valore di timeout valido.", + "enable-tls" : "Abilita TLS", + "tls-version" : "Versione TLS", + "enable-proxy" : "Abilita proxy", + "proxy-host" : "Host proxy", + "proxy-host-required" : "L'host proxy è obbligatorio.", + "proxy-port" : "Porta proxy", + "proxy-port-required" : "La porta proxy è obbligatoria.", + "proxy-port-range" : "La porta proxy deve essere compresa tra 1 e 65535.", + "proxy-user" : "Utente proxy", + "proxy-password" : "Password proxy", + "change-password" : "Cambia password", + "send-test-mail" : "Invia mail di test", + "sms-provider" : "Provider SMS", + "sms-provider-settings" : "Impostazioni provider SMS", + "sms-provider-type" : "Tipo di provider SMS", + "sms-provider-type-required" : "Il tipo di provider SMS è obbligatorio.", + "sms-provider-type-aws-sns" : "Amazon SNS", + "sms-provider-type-twilio" : "Twilio", + "sms-provider-type-smpp" : "SMPP", + "aws-access-key-id" : "ID Chiave di Accesso AWS", + "aws-access-key-id-required" : "L'ID Chiave di Accesso AWS è obbligatorio", + "aws-secret-access-key" : "Chiave di Accesso Segreta AWS", + "aws-secret-access-key-required" : "La Chiave di Accesso Segreta AWS è obbligatoria", + "aws-region" : "Regione AWS", + "aws-region-required" : "La regione AWS è obbligatoria", + "number-from" : "Numero di telefono mittente", + "number-from-required" : "Il numero di telefono mittente è obbligatorio.", + "number-to" : "Numero di telefono destinatario", + "number-to-required" : "Il numero di telefono destinatario è obbligatorio.", + "phone-number-hint" : "Numero di telefono nel formato E.164, es. +19995550123", + "phone-number-hint-twilio" : "Numero di telefono nel formato E.164/SID del numero/SID del servizio di messaggistica, es. +19995550123/PNXXX/MGXXX", + "phone-number-pattern" : "Numero di telefono non valido. Deve essere nel formato E.164, es. +19995550123.", + "phone-number-pattern-twilio" : "Numero di telefono non valido. Deve essere nel formato E.164/SID del numero/SID del servizio di messaggistica, es. +19995550123/PNXXX/MGXXX.", + "sms-message" : "Messaggio SMS", + "sms-message-required" : "Il messaggio SMS è obbligatorio.", + "sms-message-max-length" : "Il messaggio SMS non può superare i 1600 caratteri", + "twilio-account-sid" : "SID Account Twilio", + "twilio-account-sid-required" : "Il SID dell'account Twilio è obbligatorio", + "twilio-account-token" : "Token Account Twilio", + "twilio-account-token-required" : "Il token dell'account Twilio è obbligatorio", + "send-test-sms" : "Invia SMS di test", + "test-sms-sent" : "L'SMS di test è stato inviato con successo!", + "security-settings" : "Impostazioni di sicurezza", + "password-policy" : "Politica sulla password", + "minimum-password-length" : "Lunghezza minima della password", + "minimum-password-length-required" : "La lunghezza minima della password è obbligatoria", + "minimum-password-length-range" : "La lunghezza minima della password deve essere compresa tra 6 e 50", + "maximum-password-length" : "Lunghezza massima della password", + "maximum-password-length-min" : "La lunghezza massima della password deve essere almeno 6", + "maximum-password-length-less-min" : "La lunghezza massima della password deve essere maggiore della lunghezza minima", + "minimum-uppercase-letters" : "Numero minimo di lettere maiuscole", + "minimum-uppercase-letters-range" : "Il numero minimo di lettere maiuscole non può essere negativo", + "minimum-lowercase-letters" : "Numero minimo di lettere minuscole", + "minimum-lowercase-letters-range" : "Il numero minimo di lettere minuscole non può essere negativo", + "minimum-digits" : "Numero minimo di cifre", + "minimum-digits-range" : "Il numero minimo di cifre non può essere negativo", + "minimum-special-characters" : "Numero minimo di caratteri speciali", + "minimum-special-characters-range" : "Il numero minimo di caratteri speciali non può essere negativo", + "password-expiration-period-days" : "Periodo di scadenza della password (in giorni)", + "password-expiration-period-days-range" : "Il periodo di scadenza della password non può essere negativo", + "password-reuse-frequency-days" : "Frequenza di riutilizzo della password (in giorni)", + "password-reuse-frequency-days-range" : "La frequenza di riutilizzo della password non può essere negativa", + "allow-whitespace" : "Consenti spazi", + "force-reset-password-if-no-valid" : "Forza il reset della password se non valida", + "force-reset-password-if-no-valid-hint" : "Prestare attenzione quando si abilita questa funzione: richiederà agli utenti con password non valide di reimpostarla tramite email.", + "general-policy" : "Politica generale", + "max-failed-login-attempts" : "Numero massimo di tentativi di accesso falliti prima del blocco dell'account", + "minimum-max-failed-login-attempts-range" : "Il numero massimo di tentativi di accesso falliti non può essere negativo", + "user-lockout-notification-email" : "In caso di blocco dell'account utente, inviare una notifica via email", + "user-activation-token-ttl" : "Durata TTL del link di attivazione utente (in ore)", + "user-activation-token-ttl-range" : "La durata TTL del link di attivazione deve essere compresa tra 1 e 24 ore", + "password-reset-token-ttl" : "Durata TTL del link di reimpostazione password (in ore)", + "password-reset-token-ttl-range" : "La durata TTL del link di reimpostazione password deve essere compresa tra 1 e 24 ore", + "mobile-secret-key-length" : "Lunghezza della chiave segreta mobile", + "mobile-secret-key-length-range" : "La lunghezza della chiave segreta mobile deve essere positiva", + "domain-name" : "Nome dominio", + "domain-name-unique" : "Nome dominio e protocollo devono essere univoci.", + "domain-name-max-length" : "Il nome dominio deve essere inferiore a 256 caratteri", + "error-verification-url" : "Un nome dominio non deve contenere i simboli '/' e ':'. Esempio: thingsboard.io", + "connection-settings" : "Impostazioni di connessione", + "oauth2" : { + "access-token-uri" : "URI del token di accesso", + "access-token-uri-required" : "L'URI del token di accesso è obbligatorio.", + "activate-user" : "Attiva utente", + "add-domain" : "Aggiungi dominio", + "delete-domain" : "Elimina dominio", + "add-provider" : "Aggiungi provider", + "delete-provider" : "Elimina provider", + "allow-user-creation" : "Consenti creazione utente", + "always-fullscreen" : "Sempre a schermo intero", + "authorization-uri" : "URI di autorizzazione", + "authorization-uri-required" : "L'URI di autorizzazione è obbligatorio.", + "add-client" : "Aggiungi client OAuth 2.0", + "client-details" : "Dettagli client OAuth 2.0", + "client" : "Client OAuth 2.0", + "clients" : "Client OAuth 2.0", + "no-oauth2-clients" : "Nessun client OAuth 2.0 trovato", + "search-oauth2-clients" : "Cerca client OAuth 2.0", + "delete-client-title" : "Sei sicuro di voler eliminare il client OAuth 2.0 '{{clientName}}'?", + "delete-client-text" : "Attenzione, dopo la conferma il client e tutti i dati correlati saranno irrimediabilmente persi.", + "delete-mobile-app-title" : "Sei sicuro di voler eliminare l'applicazione mobile '{{applicationName}}'?", + "delete-mobile-app-text" : "Attenzione, dopo la conferma l'applicazione mobile e tutti i dati correlati saranno irrimediabilmente persi.", + "title" : "Titolo", + "client-title-required" : "Il titolo è obbligatorio", + "client-title-max-length" : "Il titolo deve essere inferiore a 100 caratteri", + "advanced-settings" : "Impostazioni avanzate", + "domain-details" : "Dettagli dominio", + "no-domains" : "Nessun dominio trovato", + "search-domains" : "Cerca domini", + "mobile-app-details" : "Dettagli applicazione mobile", + "add-mobile-app" : "Aggiungi applicazione mobile", + "no-mobile-apps" : "Nessuna applicazione mobile trovata", + "search-mobile-apps" : "Cerca applicazioni mobili", + "send-token" : "Invia token", + "create-new" : "Crea nuovo", + "client-authentication-method" : "Metodo di autenticazione client", + "client-id" : "ID client", + "client-id-required" : "L'ID client è obbligatorio.", + "client-id-max-length" : "L'ID client deve essere inferiore a 256 caratteri", + "client-secret" : "Segreto client", + "client-secret-required" : "Il segreto client è obbligatorio.", + "client-secret-max-length" : "Il segreto client deve essere inferiore a 2049 caratteri", + "custom-setting" : "Impostazioni personalizzate", + "customer-name-pattern" : "Pattern nome cliente", + "customer-name-pattern-max-length" : "Il pattern del nome cliente deve essere inferiore a 256 caratteri", + "default-dashboard-name" : "Nome dashboard predefinita", + "default-dashboard-name-max-length" : "Il nome della dashboard predefinita deve essere inferiore a 256 caratteri", + "delete-domain-text" : "Attenzione, dopo la conferma il dominio e tutti i dati del provider non saranno più disponibili.", + "delete-domain-title" : "Sei sicuro di voler eliminare il dominio '{{domainName}}'?", + "delete-registration-text" : "Attenzione, dopo la conferma i dati del provider non saranno più disponibili.", + "delete-registration-title" : "Sei sicuro di voler eliminare il provider '{{name}}'?", + "email-attribute-key" : "Chiave attributo email", + "email-attribute-key-required" : "La chiave attributo email è obbligatoria.", + "email-attribute-key-max-length" : "La chiave attributo email deve essere inferiore a 32 caratteri", + "first-name-attribute-key" : "Chiave attributo nome", + "first-name-attribute-key-max-length" : "La chiave attributo nome deve essere inferiore a 32 caratteri", + "general" : "Generale", + "jwk-set-uri" : "URI JSON Web Key", + "last-name-attribute-key" : "Chiave attributo cognome", + "last-name-attribute-key-max-length" : "La chiave attributo cognome deve essere inferiore a 32 caratteri", + "login-button-icon" : "Icona del pulsante di accesso", + "login-button-label" : "Etichetta provider", + "login-button-label-placeholder" : "Accedi con $(Provider label)", + "login-button-label-required" : "L'etichetta è obbligatoria.", + "login-provider" : "Provider di accesso", + "mapper" : "Mapper", + "new-domain" : "Nuovo dominio", + "oauth2" : "OAuth 2.0", + "password-max-length" : "La password deve essere inferiore a 256 caratteri", + "redirect-uri-template" : "Template URI di reindirizzamento", + "copy-redirect-uri" : "Copia URI di reindirizzamento", + "registration-id" : "ID registrazione", + "registration-id-required" : "L'ID di registrazione è obbligatorio.", + "registration-id-unique" : "L'ID di registrazione deve essere univoco per il sistema.", + "scope" : "Scope", + "scope-required" : "Lo scope è obbligatorio.", + "tenant-name-pattern" : "Pattern nome tenant", + "tenant-name-pattern-required" : "Il pattern nome tenant è obbligatorio.", + "tenant-name-pattern-max-length" : "Il pattern nome tenant deve essere inferiore a 256 caratteri", + "tenant-name-strategy" : "Strategia nome tenant", + "type" : "Tipo di mapper", + "uri-pattern-error" : "Formato URI non valido.", + "url" : "URL", + "url-pattern" : "Formato URL non valido.", + "url-required" : "L'URL è obbligatorio.", + "url-max-length" : "L'URL deve essere inferiore a 256 caratteri", + "user-info-uri" : "URI informazioni utente", + "user-info-uri-required" : "L'URI informazioni utente è obbligatorio.", + "username-max-length" : "Il nome utente deve essere inferiore a 256 caratteri", + "user-name-attribute-name" : "Chiave attributo nome utente", + "user-name-attribute-name-required" : "La chiave attributo nome utente è obbligatoria", + "protocol" : "Protocollo", + "domain-schema-http" : "HTTP", + "domain-schema-https" : "HTTPS", + "domain-schema-mixed" : "HTTP+HTTPS", + "enable" : "Abilita impostazioni OAuth 2.0", + "disable" : "Disabilita impostazioni OAuth 2.0", + "edge" : "Propaga a Edge", + "edge-enable" : "Abilita propagazione su Edge", + "edge-disable" : "Disabilita propagazione su Edge", + "domains" : "Domini", + "mobile-apps" : "Applicazioni mobili", + "mobile-package" : "Pacchetto applicazione", + "mobile-package-placeholder" : "Es.: my.example.app", + "mobile-package-hint" : "Per Android: il tuo Application ID univoco. Per iOS: identificatore del bundle di prodotto.", + "mobile-package-unique" : "Il pacchetto applicazione deve essere univoco.", + "mobile-package-required" : "Il pacchetto applicazione è obbligatorio.", + "mobile-package-max-length" : "Il pacchetto applicazione deve essere inferiore a 256 caratteri", + "mobile-package-spaces" : "Il pacchetto applicazione non deve contenere spazi", + "mobile-app-secret" : "Segreto applicazione", + "mobile-app-secret-hint" : "Stringa codificata in Base64 che rappresenta almeno 512 bit di dati.", + "mobile-app-secret-required" : "Il segreto dell'applicazione è obbligatorio.", + "mobile-app-secret-min-length" : "Il segreto dell'applicazione deve essere di almeno 512 bit di dati.", + "mobile-app-secret-base64" : "Il segreto dell'applicazione deve essere in formato base64.", + "invalid-mobile-app-secret" : "Il segreto dell'applicazione deve contenere solo caratteri alfanumerici e avere una lunghezza compresa tra 16 e 2048 caratteri.", + "copy-mobile-app-secret" : "Copia segreto applicazione", + "delete-mobile-app" : "Elimina informazioni applicazione", + "providers" : "Provider", + "platform-web" : "Web", + "platform-android" : "Android", + "platform-ios" : "iOS", + "all-platforms" : "Tutte le piattaforme", + "smtp-provider" : "Provider SMTP", + "allowed-platforms" : "Piattaforme consentite", + "authentication" : "Autenticazione", + "basic" : "Base", + "provider" : "Provider", + "redirect-url" : "URI di reindirizzamento", + "domain-name" : "Nome dominio", + "domain-name-required" : "Il nome dominio è obbligatorio", + "redirect-url-template" : "Template URI di reindirizzamento", + "microsoft-tenant-id" : "ID Directory (tenant)", + "microsoft-tenant-id-required" : "L'ID della Directory (tenant) è obbligatorio", + "token-uri" : "URI token", + "token-uri-required" : "L'URI token è obbligatorio", + "redirect-uri" : "URI di reindirizzamento", + "google-provider" : "Google", + "microsoft-provider" : "Office 365", + "sendgrid-provider" : "Sendgrid", + "custom-provider" : "Personalizzato", + "generate-access-token" : "Genera token di accesso", + "update-access-token" : "Aggiorna token di accesso", + "access-token-status" : "Stato del token di accesso:", + "token-status-generated" : "generato", + "token-status-not-generated" : "non generato" + }, + "smpp-provider" : { + "smpp-version" : "Versione SMPP", + "smpp-host" : "Host SMPP", + "smpp-host-required" : "L'host SMPP è obbligatorio", + "smpp-port" : "Porta SMPP", + "smpp-port-required" : "La porta SMPP è obbligatoria", + "system-id" : "ID sistema", + "system-id-required" : "L'ID sistema è obbligatorio", + "password" : "Password", + "password-required" : "La password è obbligatoria", + "type-settings" : "Impostazioni tipo", + "source-settings" : "Impostazioni sorgente", + "destination-settings" : "Impostazioni destinazione", + "additional-settings" : "Impostazioni aggiuntive", + "system-type" : "Tipo di sistema", + "bind-type" : "Tipo di bind", + "service-type" : "Tipo di servizio", + "source-address" : "Indirizzo sorgente", + "source-ton" : "TON sorgente", + "source-npi" : "NPI sorgente", + "destination-ton" : "TON destinazione (Tipo di Numero)", + "destination-npi" : "NPI destinazione (Identificazione del Piano di Numerazione)", + "address-range" : "Intervallo indirizzi", + "coding-scheme" : "Schema di codifica", + "bind-type-tx" : "Trasmettitore", + "bind-type-rx" : "Ricevitore", + "bind-type-trx" : "Ricetrasmettitore", + "ton-unknown" : "Sconosciuto", + "ton-international" : "Internazionale", + "ton-national" : "Nazionale", + "ton-network-specific" : "Specifico della rete", + "ton-subscriber-number" : "Numero abbonato", + "ton-alphanumeric" : "Alfanumerico", + "ton-abbreviated" : "Abbreviato", + "npi-unknown" : "0 - Sconosciuto", + "npi-isdn" : "1 - ISDN/piano di numerazione telefonica (E163/E164)", + "npi-data-numbering-plan" : "3 - Piano di numerazione dati (X.121)", + "npi-telex-numbering-plan" : "4 - Piano di numerazione Telex (F.69)", + "npi-land-mobile" : "6 - Mobile terrestre (E.212)", + "npi-national-numbering-plan" : "8 - Piano di numerazione nazionale", + "npi-private-numbering-plan" : "9 - Piano di numerazione privato", + "npi-ermes-numbering-plan" : "10 - Piano di numerazione ERMES (ETSI DE/PS 3 01-3)", + "npi-internet" : "13 - Internet (IP)", + "npi-wap-client-id" : "18 - ID client WAP (definito dal WAP Forum)", + "scheme-smsc" : "0 - Alfabeto predefinito SMSC (ASCII per short/long code e GSM per numero verde)", + "scheme-ia5" : "1 - IA5 (ASCII per short/long code, Latin 9 per numero verde (ISO-8859-9))", + "scheme-octet-unspecified-2" : "2 - Ottetto non specificato (8-bit binario)", + "scheme-latin-1" : "3 - Latino 1 (ISO-8859-1)", + "scheme-octet-unspecified-4" : "4 - Ottetto non specificato (8-bit binario)", + "scheme-jis" : "5 - JIS (X 0208-1990)", + "scheme-cyrillic" : "6 - Cirillico (ISO-8859-5)", + "scheme-latin-hebrew" : "7 - Latino/Ebraico (ISO-8859-8)", + "scheme-ucs-utf" : "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding" : "9 - Codifica pittogrammi", + "scheme-music-codes" : "10 - Codici musicali (ISO-2022-JP)", + "scheme-extended-kanji-jis" : "13 - Kanji JIS esteso (X 0212-1990)", + "scheme-korean-graphic-character-set" : "14 - Set grafico coreano (KS C 5601/KS X 1001)" + }, + "queue-select-name" : "Seleziona nome coda", + "queue-name" : "Nome", + "queue-name-required" : "Il nome della coda è obbligatorio!", + "queues" : "Code", + "queue-partitions" : "Partizioni", + "queue-submit-strategy" : "Strategia di invio", + "queue-processing-strategy" : "Strategia di elaborazione", + "queue-configuration" : "Configurazione della coda", + "repository-settings" : "Impostazioni del repository", + "repository" : "Repository", + "repository-url" : "URL del repository", + "repository-url-required" : "L'URL del repository è obbligatorio.", + "default-branch" : "Nome del branch predefinito", + "repository-read-only" : "Sola lettura", + "show-merge-commits" : "Mostra commit di merge", + "authentication-settings" : "Impostazioni di autenticazione", + "auth-method" : "Metodo di autenticazione", + "auth-method-username-password" : "Password / token di accesso", + "auth-method-username-password-hint" : "Gli utenti GitHub devono usare token di accesso con permessi di scrittura al repository.", + "auth-method-private-key" : "Chiave privata", + "password-access-token" : "Password / token di accesso", + "change-password-access-token" : "Cambia password / token di accesso", + "private-key" : "Chiave privata", + "drop-private-key-file-or" : "Trascina e rilascia un file con chiave privata oppure", + "passphrase" : "Passphrase", + "enter-passphrase" : "Inserisci passphrase", + "change-passphrase" : "Cambia passphrase", + "check-access" : "Verifica accesso", + "check-repository-access-success" : "Accesso al repository verificato con successo!", + "delete-repository-settings-title" : "Sei sicuro di voler eliminare le impostazioni del repository?", + "delete-repository-settings-text" : "Attenzione, dopo la conferma le impostazioni del repository verranno rimosse e la funzionalità di controllo versione non sarà più disponibile.", + "auto-commit-settings" : "Impostazioni auto-commit", + "auto-commit" : "Auto-commit", + "auto-commit-entities" : "Entità auto-commit", + "no-auto-commit-entities-prompt" : "Nessuna entità configurata per l'auto-commit", + "delete-auto-commit-settings-title" : "Sei sicuro di voler eliminare le impostazioni di auto-commit?", + "delete-auto-commit-settings-text" : "Attenzione, dopo la conferma le impostazioni di auto-commit verranno rimosse e l'auto-commit sarà disabilitato per tutte le entità.", + "mobile-app" : { + "mobile-app" : "App mobile", + "mobile-app-qr-code-widget-settings" : "Impostazioni widget QR code app mobile", + "applications" : "Applicazioni", + "default" : "Predefinito", + "custom" : "Personalizzato", + "android" : "Android", + "ios" : "iOS", + "appearance" : "Aspetto", + "appearance-on-home-page" : "Aspetto sulla pagina Home", + "enabled" : "Abilitato", + "disabled" : "Disabilitato", + "badges" : "Badge", + "label" : "Etichetta", + "label-required" : "L'etichetta è obbligatoria", + "label-max-length" : "L'etichetta deve essere lunga al massimo 50 caratteri", + "right" : "Destra", + "left" : "Sinistra", + "set" : "Imposta", + "preview" : "Anteprima", + "connect-mobile-app" : "Collega app mobile", + "use-system-settings" : "Usa impostazioni di sistema" + }, + "2fa" : { + "2fa" : "Autenticazione a due fattori", + "available-providers" : "Provider disponibili", + "issuer-name" : "Nome dell'emittente", + "issuer-name-required" : "Il nome dell'emittente è obbligatorio.", + "max-verification-failures-before-user-lockout" : "Numero massimo di verifiche fallite prima del blocco dell'utente", + "max-verification-failures-before-user-lockout-pattern" : "Il numero massimo di verifiche deve essere un numero intero positivo.", + "number-of-checking-attempts" : "Numero di tentativi di verifica", + "number-of-checking-attempts-pattern" : "Il numero di tentativi di verifica deve essere un numero intero positivo.", + "number-of-checking-attempts-required" : "Il numero di tentativi di verifica è obbligatorio.", + "number-of-codes" : "Numero di codici", + "number-of-codes-pattern" : "Il numero di codici deve essere un numero intero positivo.", + "number-of-codes-required" : "Il numero di codici è obbligatorio.", + "provider" : "Provider", + "retry-verification-code-period" : "Periodo per il reinvio del codice di verifica (sec)", + "retry-verification-code-period-pattern" : "Il tempo minimo è di 5 secondi", + "retry-verification-code-period-required" : "Il periodo per il reinvio del codice di verifica è obbligatorio.", + "total-allowed-time-for-verification" : "Tempo totale consentito per la verifica (sec)", + "total-allowed-time-for-verification-pattern" : "Il tempo minimo totale consentito è di 60 secondi", + "total-allowed-time-for-verification-required" : "Il tempo totale consentito è obbligatorio.", + "use-system-two-factor-auth-settings" : "Usa le impostazioni di autenticazione a due fattori di sistema", + "verification-code-check-rate-limit" : "Limite di frequenza dei controlli del codice di verifica", + "verification-code-lifetime" : "Durata del codice di verifica (sec)", + "verification-code-lifetime-pattern" : "La durata del codice di verifica deve essere un numero intero positivo.", + "verification-code-lifetime-required" : "La durata del codice di verifica è obbligatoria.", + "verification-message-template" : "Template del messaggio di verifica", + "verification-limitations" : "Limitazioni della verifica", + "verification-message-template-pattern" : "Il messaggio di verifica deve contenere il pattern: ${code}", + "verification-message-template-required" : "Il template del messaggio di verifica è obbligatorio.", + "within-time" : "Entro il tempo (sec)", + "within-time-pattern" : "Il tempo deve essere un numero intero positivo.", + "within-time-required" : "Il tempo è obbligatorio." + }, + "jwt" : { + "security-settings" : "Impostazioni di sicurezza JWT", + "issuer-name" : "Nome dell'emittente", + "issuer-name-required" : "Il nome dell'emittente è obbligatorio.", + "signings-key" : "Chiave di firma", + "signings-key-hint" : "Stringa codificata in Base64 che rappresenta almeno 512 bit di dati.", + "signings-key-required" : "La chiave di firma è obbligatoria.", + "signings-key-min-length" : "La chiave di firma deve essere di almeno 512 bit di dati.", + "signings-key-base64" : "La chiave di firma deve essere in formato base64.", + "expiration-time" : "Tempo di scadenza del token (sec)", + "expiration-time-required" : "Il tempo di scadenza del token è obbligatorio.", + "expiration-time-max" : "Il tempo massimo consentito è di 2147483647 secondi (68 anni).", + "expiration-time-min" : "Il tempo minimo è di 60 secondi (1 minuto).", + "refresh-expiration-time" : "Tempo di scadenza del token di aggiornamento (sec)", + "refresh-expiration-time-required" : "Il tempo di scadenza del token di aggiornamento è obbligatorio.", + "refresh-expiration-time-max" : "Il tempo massimo consentito è di 2147483647 secondi (68 anni).", + "refresh-expiration-time-min" : "Il tempo minimo è di 900 secondi (15 minuti).", + "refresh-expiration-time-less-token" : "Il tempo del token di aggiornamento deve essere maggiore di quello del token.", + "generate-key" : "Genera chiave", + "info-header" : "Tutti gli utenti dovranno effettuare nuovamente il login", + "info-message" : "La modifica della chiave di firma JWT invaliderà tutti i token emessi. Tutti gli utenti dovranno effettuare nuovamente l'accesso. Questo influenzerà anche gli script che utilizzano l'API REST/Websockets." + }, + "resources" : "Risorse", + "notifications" : "Notifiche", + "notifications-settings" : "Impostazioni notifiche", + "slack-api-token" : "Token API Slack", + "slack" : "Slack", + "slack-settings" : "Impostazioni Slack", + "mobile-settings" : "Impostazioni mobile", + "firebase-service-account-file" : "File JSON delle credenziali dell'account di servizio Firebase", + "select-firebase-service-account-file" : "Trascina e rilascia il file delle credenziali dell'account di servizio Firebase oppure " + }, + "alarm" : { + "alarm" : "Allarme", + "alarms" : "Allarmi", + "all-alarms" : "Tutti gli allarmi", + "select-alarm" : "Seleziona allarme", + "no-alarms-matching" : "Nessun allarme corrispondente a '{{entity}}' trovato.", + "alarm-required" : "Allarme obbligatorio", + "alarm-filter" : "Filtro allarmi", + "filter" : "Filtro", + "alarm-status" : "Stato allarme", + "alarm-status-list" : "Elenco stati allarme", + "any-status" : "Qualsiasi stato", + "search-status" : { + "ANY" : "Qualsiasi", + "ACTIVE" : "Attivo", + "CLEARED" : "Ripristinato", + "ACK" : "Confermato", + "UNACK" : "Non confermato" + }, + "display-status" : { + "ACTIVE_UNACK" : "Attivo non confermato", + "ACTIVE_ACK" : "Attivo confermato", + "CLEARED_UNACK" : "Ripristinato non confermato", + "CLEARED_ACK" : "Ripristinato confermato" + }, + "no-alarms-prompt" : "Nessun allarme trovato", + "created-time" : "Ora di creazione", + "type" : "Tipo", + "severity" : "Gravità", + "originator" : "Originatore", + "originator-type" : "Tipo originatore", + "details" : "Dettagli", + "originator-label" : "Etichetta originatore", + "assign" : "Assegna", + "assignments" : "Assegnazioni", + "assignee" : "Assegnatario", + "assignee-id" : "ID assegnatario", + "assignee-first-name" : "Nome assegnatario", + "assignee-last-name" : "Cognome assegnatario", + "assignee-email" : "Email assegnatario", + "unassigned" : "Non assegnato", + "user-deleted" : "Utente eliminato", + "assignee-not-set" : "Tutti", + "status" : "Stato", + "alarm-details" : "Dettagli allarme", + "start-time" : "Ora inizio", + "assign-time" : "Ora assegnazione", + "end-time" : "Ora fine", + "ack-time" : "Ora conferma", + "clear-time" : "Ora ripristino", + "duration" : "Durata", + "alarm-severity" : "Gravità allarme", + "alarm-severity-list" : "Elenco gravità allarme", + "any-severity" : "Qualsiasi gravità", + "severity-critical" : "Critica", + "severity-major" : "Maggiore", + "severity-minor" : "Minore", + "severity-warning" : "Avviso", + "severity-indeterminate" : "Indeterminata", + "acknowledge" : "Conferma", + "clear" : "Ripristina", + "delete" : "Elimina", + "search" : "Cerca allarmi", + "selected-alarms" : "{ count, plural, =1 {1 allarme} other {# allarmi} } selezionato/i", + "no-data" : "Nessun dato da visualizzare", + "polling-interval" : "Intervallo di polling allarmi (sec)", + "polling-interval-required" : "L'intervallo di polling degli allarmi è obbligatorio.", + "min-polling-interval-message" : "L'intervallo minimo consentito è 1 secondo.", + "aknowledge-alarms-title" : "Conferma { count, plural, =1 {1 allarme} other {# allarmi} }", + "aknowledge-alarms-text" : "Sei sicuro di voler confermare { count, plural, =1 {1 allarme} other {# allarmi} }?", + "aknowledge-alarm-title" : "Conferma Allarme", + "aknowledge-alarm-text" : "Sei sicuro di voler confermare l'allarme?", + "selected-alarms-are-acknowledged" : "Gli allarmi selezionati sono già stati confermati", + "clear-alarms-title" : "Ripristina { count, plural, =1 {1 allarme} other {# allarmi} }", + "clear-alarms-text" : "Sei sicuro di voler ripristinare { count, plural, =1 {1 allarme} other {# allarmi} }?", + "clear-alarm-title" : "Ripristina Allarme", + "clear-alarm-text" : "Sei sicuro di voler ripristinare l'allarme?", + "delete-alarms-title" : "Elimina { count, plural, =1 {1 allarme} other {# allarmi} }", + "delete-alarms-text" : "Sei sicuro di voler eliminare { count, plural, =1 {1 allarme} other {# allarmi} }?", + "selected-alarms-are-cleared" : "Gli allarmi selezionati sono già stati ripristinati", + "alarm-status-filter" : "Filtro stato allarme", + "alarm-filter-title" : "Filtro allarme", + "assigned" : "Assegnato", + "filter-title" : "Filtro", + "max-count-load" : "Numero massimo di allarmi da caricare (0 - illimitato)", + "max-count-load-required" : "Il numero massimo di allarmi da caricare è obbligatorio.", + "max-count-load-error-min" : "Il valore minimo è 0.", + "fetch-size" : "Dimensione fetch", + "fetch-size-required" : "La dimensione del fetch è obbligatoria.", + "fetch-size-error-min" : "Il valore minimo è 10.", + "alarm-types" : "Tipi di allarme", + "alarm-type-list" : "Elenco tipi di allarme", + "any-type" : "Qualsiasi tipo", + "assigned-to-current-user" : "Assegnato all'utente corrente", + "assigned-to-me" : "Assegnato a me", + "search-propagated-alarms" : "Cerca allarmi propagati", + "comments" : "Commenti allarme", + "show-more" : "Mostra di più", + "additional-info" : "Informazioni aggiuntive", + "alarm-type" : "Tipo di allarme", + "enter-alarm-type" : "Inserisci tipo di allarme", + "no-alarm-types-matching" : "Nessun tipo di allarme corrispondente a '{{entitySubtype}}' trovato.", + "alarm-type-list-empty" : "Nessun tipo di allarme selezionato." + }, + "alarm-activity" : { + "add" : "Aggiungi un commento...", + "alarm-comment" : "Commento allarme", + "comments" : "Commenti", + "delete-alarm-comment" : "Vuoi eliminare questo commento?", + "refresh" : "Aggiorna", + "oldest-first" : "Più vecchi prima", + "newest-first" : "Più recenti prima", + "activity" : "Attività", + "export" : "Esporta in CSV", + "author" : "Autore", + "created-date" : "Data di creazione", + "edited-date" : "Data di modifica", + "text" : "Testo", + "system" : "Sistema" + }, + "alias" : { + "add" : "Aggiungi alias", + "edit" : "Modifica alias", + "name" : "Nome alias", + "name-required" : "Il nome dell'alias è obbligatorio", + "duplicate-alias" : "Esiste già un alias con lo stesso nome.", + "filter-type-single-entity" : "Singola entità", + "filter-type-entity-list" : "Lista di entità", + "filter-type-entity-name" : "Nome entità", + "filter-type-entity-type" : "Tipo entità", + "filter-type-state-entity" : "Entità da stato della dashboard", + "filter-type-state-entity-description" : "Entità presa dai parametri dello stato della dashboard", + "filter-type-asset-type" : "Tipo di asset", + "filter-type-asset-type-description" : "Asset di tipo '{{assetTypes}}'", + "filter-type-asset-type-and-name-description" : "Asset di tipo '{{assetTypes}}' con nome che inizia con '{{prefix}}'", + "filter-type-device-type" : "Tipo di dispositivo", + "filter-type-device-type-description" : "Dispositivi di tipo '{{deviceTypes}}'", + "filter-type-device-type-and-name-description" : "Dispositivi di tipo '{{deviceTypes}}' con nome che inizia con '{{prefix}}'", + "filter-type-entity-view-type" : "Tipo di Entity View", + "filter-type-entity-view-type-description" : "Entity View di tipo '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description" : "Entity View di tipo '{{entityViewTypes}}' con nome che inizia con '{{prefix}}'", + "filter-type-edge-type" : "Tipo Edge", + "filter-type-edge-type-description" : "Edge di tipo '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description" : "Edge di tipo '{{edgeTypes}}' con nome che inizia con '{{prefix}}'", + "filter-type-relations-query" : "Query relazioni", + "filter-type-relations-query-description" : "{{entities}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-edge-search-query" : "Query ricerca Edge", + "filter-type-edge-search-query-description" : "Edge di tipo {{edgeTypes}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query" : "Query ricerca asset", + "filter-type-asset-search-query-description" : "Asset di tipo {{assetTypes}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query" : "Query ricerca dispositivo", + "filter-type-device-search-query-description" : "Dispositivi di tipo {{deviceTypes}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query" : "Query ricerca Entity View", + "filter-type-entity-view-search-query-description" : "Entity View di tipo {{entityViewTypes}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState" : "Stato utilizzo API", + "entity-filter" : "Filtro entità", + "resolve-multiple" : "Risolvi come entità multiple", + "resolve-multiple-hint" : "Abilita per visualizzare dati da tutte le entità filtrate simultaneamente.\nSe disabilitato, il widget mostra i dati della sola entità selezionata.", + "filter-type" : "Tipo di filtro", + "filter-type-required" : "Il tipo di filtro è obbligatorio.", + "entity-filter-no-entity-matched" : "Nessuna entità corrispondente al filtro specificato trovata.", + "no-entity-filter-specified" : "Nessun filtro entità specificato", + "root-state-entity" : "Usa entità stato dashboard come radice", + "last-level-relation" : "Recupera solo ultima relazione di livello", + "root-entity" : "Entità radice", + "state-entity-parameter-name" : "Nome parametro entità stato", + "default-state-entity" : "Entità stato predefinita", + "default-entity-parameter-name" : "Predefinito", + "max-relation-level" : "Livello massimo relazione", + "unlimited-level" : "Livello illimitato", + "state-entity" : "Entità stato della dashboard", + "all-entities" : "Tutte le entità", + "any-relation" : "qualsiasi" + }, + "asset" : { + "asset" : "Asset", + "assets" : "Asset", + "management" : "Gestione asset", + "view-assets" : "Visualizza asset", + "add" : "Aggiungi asset", + "asset-type-max-length" : "Il tipo di asset deve essere inferiore a 256 caratteri", + "assign-to-customer" : "Assegna al cliente", + "assign-asset-to-customer" : "Assegna asset al cliente", + "assign-asset-to-customer-text" : "Seleziona gli asset da assegnare al cliente", + "no-assets-text" : "Nessun asset trovato", + "assign-to-customer-text" : "Seleziona il cliente a cui assegnare l'asset", + "public" : "Pubblico", + "assignedToCustomer" : "Assegnato al cliente", + "make-public" : "Rendi pubblico l'asset", + "make-private" : "Rendi privato l'asset", + "unassign-from-customer" : "Rimuovi assegnazione dal cliente", + "delete" : "Elimina asset", + "asset-public" : "L'asset è pubblico", + "asset-type" : "Tipo asset", + "asset-type-required" : "Il tipo di asset è obbligatorio.", + "select-asset-type" : "Seleziona tipo asset", + "enter-asset-type" : "Inserisci profilo asset", + "any-asset" : "Qualsiasi asset", + "no-asset-types-matching" : "Nessun tipo di asset corrispondente a '{{entitySubtype}}' trovato.", + "asset-type-list-empty" : "Nessun tipo di asset selezionato.", + "asset-types" : "Tipi di asset", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio.", + "name-max-length" : "Il nome deve essere inferiore a 256 caratteri", + "label-max-length" : "L'etichetta deve essere inferiore a 256 caratteri", + "description" : "Descrizione", + "type" : "Tipo", + "type-required" : "Il tipo è obbligatorio.", + "details" : "Dettagli", + "events" : "Eventi", + "add-asset-text" : "Aggiungi nuovo asset", + "asset-details" : "Dettagli asset", + "assign-assets" : "Assegna asset", + "assign-assets-text" : "Assegna { count, plural, =1 {1 asset} other {# asset} } al cliente", + "assign-asset-to-edge-title" : "Assegna asset all'edge", + "assign-asset-to-edge-text" : "Seleziona gli asset da assegnare all'edge", + "delete-assets" : "Elimina asset", + "unassign-assets" : "Rimuovi assegnazione asset", + "unassign-assets-action-title" : "Rimuovi assegnazione di { count, plural, =1 {1 asset} other {# asset} } dal cliente", + "assign-new-asset" : "Assegna nuovo asset", + "delete-asset-title" : "Sei sicuro di voler eliminare l'asset '{{assetName}}'?", + "delete-asset-text" : "Attenzione, dopo la conferma l'asset e tutti i dati correlati saranno irrimediabilmente persi.", + "delete-assets-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 asset} other {# asset} }?", + "delete-assets-action-title" : "Elimina { count, plural, =1 {1 asset} other {# asset} }", + "delete-assets-text" : "Attenzione, dopo la conferma tutti gli asset selezionati saranno rimossi e tutti i dati correlati saranno irrimediabilmente persi.", + "make-public-asset-title" : "Sei sicuro di voler rendere pubblico l'asset '{{assetName}}'?", + "make-public-asset-text" : "Dopo la conferma l'asset e tutti i suoi dati saranno resi pubblici e accessibili ad altri.", + "make-private-asset-title" : "Sei sicuro di voler rendere privato l'asset '{{assetName}}'?", + "make-private-asset-text" : "Dopo la conferma l'asset e tutti i suoi dati saranno resi privati e non saranno accessibili ad altri.", + "unassign-asset-title" : "Sei sicuro di voler rimuovere l'assegnazione dell'asset '{{assetName}}'?", + "unassign-asset-text" : "Dopo la conferma l'asset sarà disassegnato e non sarà più accessibile dal cliente.", + "unassign-asset" : "Disassegna asset", + "unassign-assets-title" : "Sei sicuro di voler disassegnare { count, plural, =1 {1 asset} other {# asset} }?", + "unassign-assets-text" : "Dopo la conferma tutti gli asset selezionati saranno disassegnati e non saranno accessibili dal cliente.", + "copyId" : "Copia ID asset", + "idCopiedMessage" : "ID asset copiato negli appunti", + "select-asset" : "Seleziona asset", + "no-assets-matching" : "Nessun asset corrispondente a '{{entity}}' trovato.", + "asset-required" : "L'asset è obbligatorio", + "name-starts-with" : "Espressione nome asset", + "help-text" : "Usa '%' secondo necessità: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search" : "Cerca asset", + "import" : "Importa asset", + "asset-file" : "File asset", + "label" : "Etichetta", + "assign-asset-to-edge" : "Assegna asset all'edge", + "unassign-asset-from-edge" : "Disassegna asset", + "unassign-asset-from-edge-title" : "Sei sicuro di voler disassegnare l'asset '{{assetName}}'?", + "unassign-asset-from-edge-text" : "Dopo la conferma l'asset sarà disassegnato e non sarà più accessibile dall'edge.", + "unassign-assets-from-edge-title" : "Sei sicuro di voler disassegnare { count, plural, =1 {1 asset} other {# asset} }?", + "unassign-assets-from-edge-text" : "Dopo la conferma tutti gli asset selezionati saranno disassegnati e non saranno più accessibili dall'edge.", + "selected-assets" : "{ count, plural, =1 {1 asset} other {# asset} } selezionato/i" + }, + "attribute" : { + "attributes" : "Attributi", + "latest-telemetry" : "Ultima telemetria", + "no-latest-telemetry" : "Nessuna ultima telemetria", + "attributes-scope" : "Ambito attributi entità", + "scope-telemetry" : "Telemetria", + "scope-latest-telemetry" : "Ultima telemetria", + "scope-client" : "Attributi client", + "scope-server" : "Attributi server", + "scope-shared" : "Attributi condivisi", + "scope-client-short" : "Client", + "scope-server-short" : "Server", + "scope-shared-short" : "Condivisi", + "scope-latest-short" : "Ultimi", + "scope-any" : "Qualsiasi", + "add" : "Aggiungi attributo", + "key" : "Chiave", + "key-max-length" : "La chiave deve essere inferiore a 256 caratteri", + "last-update-time" : "Ultimo aggiornamento", + "key-required" : "La chiave dell'attributo è obbligatoria.", + "value" : "Valore", + "value-required" : "Il valore dell'attributo è obbligatorio.", + "telemetry-key-required" : "La chiave della telemetria è obbligatoria", + "telemetry-value-required" : "Il valore della telemetria è obbligatorio", + "delete-attributes-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 attributo} other {# attributi} }?", + "delete-attributes-text" : "Attenzione, dopo la conferma tutti gli attributi selezionati verranno rimossi.", + "delete-attributes" : "Elimina attributi", + "enter-attribute-value" : "Inserisci valore attributo", + "show-on-widget" : "Mostra nel widget", + "widget-mode" : "Modalità widget", + "next-widget" : "Widget successivo", + "prev-widget" : "Widget precedente", + "add-to-dashboard" : "Aggiungi alla dashboard", + "add-widget-to-dashboard" : "Aggiungi widget alla dashboard", + "selected-attributes" : "{ count, plural, =1 {1 attributo} other {# attributi} } selezionato/i", + "selected-telemetry" : "{ count, plural, =1 {1 unità di telemetria} other {# unità di telemetria} } selezionata/e", + "no-attributes-text" : "Nessun attributo trovato", + "no-telemetry-text" : "Nessuna telemetria trovata", + "copy-key" : "Copia chiave", + "add-telemetry" : "Aggiungi telemetria", + "copy-value" : "Copia valore", + "delete-timeseries" : { + "start-time" : "Ora inizio", + "ends-on" : "Ora fine", + "strategy" : "Strategia", + "delete-strategy" : "Strategia di eliminazione", + "all-data" : "Elimina tutti i dati", + "all-data-except-latest-value" : "Elimina tutti i dati tranne l'ultimo valore", + "latest-value" : "Elimina ultimo valore", + "all-data-for-time-period" : "Elimina tutti i dati per il periodo di tempo", + "rewrite-latest-value" : "Riscrivi ultimo valore" + } + }, + "api-usage" : { + "api-features" : "Funzionalità API", + "api-usage" : "Utilizzo API", + "alarm" : "Allarme", + "alarms-created" : "Allarmi creati", + "queue-stats" : "Statistiche coda", + "processing-failures-and-timeouts" : "Errori e timeout di elaborazione", + "exceptions" : "Eccezioni", + "alarms-created-daily-activity" : "Attività giornaliera allarmi creati", + "alarms-created-hourly-activity" : "Attività oraria allarmi creati", + "alarms-created-monthly-activity" : "Attività mensile allarmi creati", + "data-points" : "Punti dati", + "data-points-storage-days" : "Giorni di conservazione dei punti dati", + "device-api" : "API dispositivo", + "email" : "Email", + "email-messages" : "Messaggi email", + "email-messages-daily-activity" : "Attività giornaliera messaggi email", + "email-messages-monthly-activity" : "Attività mensile messaggi email", + "executions" : "Esecuzioni", + "scripts" : "Script", + "scripts-hourly-activity" : "Attività oraria script", + "scripts-daily-activity" : "Attività giornaliera script", + "scripts-monthly-activity" : "Attività mensile script", + "javascript" : "JavaScript", + "javascript-executions" : "Esecuzioni JavaScript", + "tbel" : "TBEL", + "tbel-executions" : "Esecuzioni TBEL", + "latest-error" : "Ultimo errore", + "messages" : "Messaggi", + "notifications" : "Notifiche", + "notifications-email-sms" : "Notifiche (Email/SMS)", + "notifications-hourly-activity" : "Attività oraria notifiche", + "permanent-failures" : "Errori permanenti ${entityName}", + "permanent-timeouts" : "Timeout permanenti ${entityName}", + "processing-failures" : "Errori di elaborazione ${entityName}", + "processing-timeouts" : "Timeout di elaborazione ${entityName}", + "rule-chain" : "Catena di regole", + "rule-engine" : "Motore di regole", + "rule-engine-daily-activity" : "Attività giornaliera motore di regole", + "rule-engine-executions" : "Esecuzioni motore di regole", + "rule-engine-hourly-activity" : "Attività oraria motore di regole", + "rule-engine-monthly-activity" : "Attività mensile motore di regole", + "rule-engine-statistics" : "Statistiche motore di regole", + "rule-node" : "Nodo regola", + "sms" : "SMS", + "sms-messages" : "Messaggi SMS", + "sms-messages-daily-activity" : "Attività giornaliera messaggi SMS", + "sms-messages-monthly-activity" : "Attività mensile messaggi SMS", + "successful" : "${entityName} riusciti", + "telemetry" : "Telemetria", + "telemetry-persistence" : "Persistenza telemetria", + "telemetry-persistence-daily-activity" : "Attività giornaliera persistenza telemetria", + "telemetry-persistence-hourly-activity" : "Attività oraria persistenza telemetria", + "telemetry-persistence-monthly-activity" : "Attività mensile persistenza telemetria", + "transport" : "Trasporto", + "transport-daily-activity" : "Attività giornaliera trasporto", + "transport-data-points" : "Punti dati trasporto", + "transport-hourly-activity" : "Attività oraria trasporto", + "transport-messages" : "Messaggi trasporto", + "transport-monthly-activity" : "Attività mensile trasporto", + "view-details" : "Visualizza dettagli", + "view-statistics" : "Visualizza statistiche" + }, + "api-limit" : { + "cassandra-queries" : "Query Cassandra", + "entity-version-creation" : "Creazione versione entità", + "entity-version-load" : "Caricamento versione entità", + "notification-requests" : "Richieste di notifica", + "notification-requests-per-rule" : "Richieste di notifica per regola", + "rest-api-requests" : "Richieste REST API", + "rest-api-requests-per-customer" : "Richieste REST API per cliente", + "transport-messages" : "Messaggi trasporto", + "transport-messages-per-device" : "Messaggi trasporto per dispositivo", + "transport-messages-per-gateway" : "Messaggi trasporto per gateway", + "transport-messages-per-gateway-device" : "Messaggi trasporto per dispositivo gateway", + "ws-updates-per-session" : "Aggiornamenti WS per sessione", + "edge-events" : "Eventi edge", + "edge-events-per-edge" : "Eventi per edge", + "edge-uplink-messages" : "Messaggi uplink edge", + "edge-uplink-messages-per-edge" : "Messaggi uplink per edge" + }, + "audit-log" : { + "audit" : "Audit", + "audit-logs" : "Log di audit", + "timestamp" : "Timestamp", + "entity-type" : "Tipo entità", + "entity-name" : "Nome entità", + "user" : "Utente", + "type" : "Tipo", + "status" : "Stato", + "details" : "Dettagli", + "type-added" : "Aggiunto", + "type-deleted" : "Eliminato", + "type-updated" : "Aggiornato", + "type-attributes-updated" : "Attributi aggiornati", + "type-attributes-deleted" : "Attributi eliminati", + "type-rpc-call" : "Chiamata RPC", + "type-credentials-updated" : "Credenziali aggiornate", + "type-assigned-to-customer" : "Assegnato al cliente", + "type-unassigned-from-customer" : "Rimosso dal cliente", + "type-assigned-to-edge" : "Assegnato all'Edge", + "type-unassigned-from-edge" : "Rimosso dall'Edge", + "type-activated" : "Attivato", + "type-suspended" : "Sospeso", + "type-credentials-read" : "Credenziali lette", + "type-attributes-read" : "Attributi letti", + "type-relation-add-or-update" : "Relazione aggiornata", + "type-relation-delete" : "Relazione eliminata", + "type-relations-delete" : "Tutte le relazioni eliminate", + "type-alarm-ack" : "Allarme confermato", + "type-alarm-clear" : "Allarme ripristinato", + "type-alarm-delete" : "Allarme eliminato", + "type-alarm-assign" : "Allarme assegnato", + "type-alarm-unassign" : "Allarme rimosso", + "type-added-comment" : "Commento aggiunto", + "type-updated-comment" : "Commento aggiornato", + "type-deleted-comment" : "Commento eliminato", + "type-login" : "Accesso", + "type-logout" : "Disconnessione", + "type-lockout" : "Blocco", + "status-success" : "Successo", + "status-failure" : "Fallimento", + "audit-log-details" : "Dettagli log di audit", + "no-audit-logs-prompt" : "Nessun log trovato", + "action-data" : "Dati azione", + "failure-details" : "Dettagli errore", + "search" : "Cerca nei log di audit", + "clear-search" : "Cancella ricerca", + "type-assigned-from-tenant" : "Assegnato dal Tenant", + "type-assigned-to-tenant" : "Assegnato al Tenant", + "type-provision-success" : "Dispositivo fornito", + "type-provision-failure" : "Provisioning dispositivo fallito", + "type-timeseries-updated" : "Telemetria aggiornata", + "type-timeseries-deleted" : "Telemetria eliminata", + "type-sms-sent" : "SMS inviato" + }, + "debug-settings" : { + "label" : "Configurazione debug", + "on-failure" : "Solo fallimenti (24/7)", + "all-messages" : "Tutti i messaggi ({{time}})", + "failures" : "Fallimenti", + "entity" : "entità", + "hint" : { + "main-limited" : "Tutti i messaggi di debug di {{entity}} saranno limitati nel tempo, con un massimo di {{msg}} messaggi consentiti ogni {{time}}.", + "on-failure" : "Salva tutti gli eventi di debug in caso di errore senza limite di tempo.", + "all-messages" : "Salva tutti gli eventi di debug entro il limite di tempo." + } + }, + "calculated-fields" : { + "expression" : "Espressione", + "no-found" : "Nessun campo calcolato trovato", + "list" : "{ count, plural, =1 {Un campo calcolato} other {Elenco di # campi calcolati} }", + "selected-fields" : "{ count, plural, =1 {1 campo calcolato} other {# campi calcolati} } selezionato/i", + "type" : { + "simple" : "Semplice", + "script" : "Script" + }, + "arguments" : "Argomenti", + "decimals-by-default" : "Decimali di default", + "debugging" : "Debug del campo calcolato", + "argument-name" : "Nome argomento", + "datasource" : "Fonte dati", + "add-argument" : "Aggiungi argomento", + "test-script-function" : "Testa funzione script", + "no-arguments" : "Nessun argomento configurato", + "argument-settings" : "Impostazioni argomento", + "argument-current" : "Entità corrente", + "argument-current-tenant" : "Tenant corrente", + "argument-device" : "Dispositivo", + "argument-asset" : "Asset", + "argument-customer" : "Cliente", + "argument-tenant" : "Tenant corrente", + "argument-type" : "Tipo argomento", + "see-debug-events" : "Vedi eventi di debug", + "attribute" : "Attributo", + "copy-argument-name" : "Copia nome argomento", + "timeseries-key" : "Chiave serie temporale", + "device-name" : "Nome dispositivo", + "latest-telemetry" : "Ultima telemetria", + "rolling" : "Rolling serie temporale", + "attribute-scope" : "Ambito attributo", + "server-attributes" : "Attributi server", + "client-attributes" : "Attributi client", + "shared-attributes" : "Attributi condivisi", + "attribute-key" : "Chiave attributo", + "default-value" : "Valore di default", + "limit" : "Valori massimi", + "time-window" : "Finestra temporale", + "customer-name" : "Nome cliente", + "asset-name" : "Nome asset", + "timeseries" : "Serie temporale", + "output" : "Output", + "create" : "Crea nuovo campo calcolato", + "file" : "File campo calcolato", + "invalid-file-error" : "Formato file non valido. Assicurati che il file sia un file JSON valido.", + "import" : "Importa campo calcolato", + "export" : "Esporta campo calcolato", + "export-failed-error" : "Impossibile esportare il campo calcolato: {{error}}", + "output-type" : "Tipo output", + "delete-title" : "Sei sicuro di voler eliminare il campo calcolato '{{title}}'?", + "delete-text" : "Attenzione, dopo la conferma il campo calcolato e tutti i dati correlati saranno irrimediabilmente persi.", + "delete-multiple-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 campo calcolato} other {# campi calcolati} }?", + "delete-multiple-text" : "Attenzione, dopo la conferma tutti i campi calcolati selezionati saranno eliminati e tutti i dati correlati saranno irrimediabilmente persi.", + "test-with-this-message" : "Testa con questo messaggio", + "hint" : { + "arguments-simple-with-rolling" : "Il campo calcolato di tipo semplice non deve contenere chiavi con tipo rolling su serie temporale.", + "arguments-empty" : "Gli argomenti non devono essere vuoti.", + "expression-required" : "L'espressione è obbligatoria.", + "expression-invalid" : "Espressione non valida", + "expression-max-length" : "La lunghezza dell'espressione deve essere inferiore a 255 caratteri.", + "argument-name-required" : "Il nome dell'argomento è obbligatorio.", + "argument-name-pattern" : "Il nome dell'argomento non è valido.", + "argument-name-duplicate" : "Esiste già un argomento con questo nome.", + "argument-name-max-length" : "Il nome dell'argomento deve essere inferiore a 256 caratteri.", + "argument-name-forbidden" : "Il nome dell'argomento è riservato e non può essere utilizzato.", + "argument-type-required" : "Il tipo dell'argomento è obbligatorio.", + "max-args" : "Numero massimo di argomenti raggiunto.", + "decimals-range" : "I decimali di default devono essere un numero compreso tra 0 e 15.", + "expression" : "L'espressione di default dimostra come trasformare una temperatura da Fahrenheit a Celsius.", + "arguments-entity-not-found" : "Entità di destinazione dell'argomento non trovata." + } + }, + "confirm-on-exit" : { + "message" : "Hai modifiche non salvate. Sei sicuro di voler lasciare questa pagina?", + "html-message" : "Hai modifiche non salvate.
Sei sicuro di voler lasciare questa pagina?", + "title" : "Modifiche non salvate" + }, + "contact" : { + "country" : "Paese", + "country-required" : "Il paese è obbligatorio.", + "city" : "Città", + "state" : "Stato / Provincia", + "postal-code" : "CAP / Codice postale", + "postal-code-invalid" : "Formato CAP / Codice postale non valido.", + "address" : "Indirizzo", + "address2" : "Indirizzo 2", + "phone" : "Telefono", + "email" : "Email", + "no-address" : "Nessun indirizzo", + "no-country-found" : "Nessun paese trovato.", + "no-country-matching" : "Nessun paese corrispondente a '{{country}}' trovato.", + "state-max-length" : "La lunghezza dello stato deve essere inferiore a 256 caratteri", + "phone-max-length" : "Il numero di telefono deve essere inferiore a 256 caratteri", + "city-max-length" : "La città specificata deve essere inferiore a 256 caratteri" + }, + "common" : { + "name" : "Nome", + "type" : "Tipo", + "general" : "Generale", + "username" : "Nome utente", + "password" : "Password", + "data" : "Dati", + "timestamp" : "Timestamp", + "enter-username" : "Inserisci nome utente", + "enter-password" : "Inserisci password", + "enter-search" : "Inserisci ricerca", + "created-time" : "Ora di creazione", + "disabled" : "Disabilitato", + "loading" : "Caricamento...", + "proceed" : "Procedi", + "open-details-page" : "Apri pagina dettagli", + "not-found" : "Non trovato", + "value" : "Valore", + "documentation" : "Documentazione", + "time-left" : "{{time}} rimanenti", + "output" : "Output", + "suffix" : { + "s" : "s", + "ms" : "ms" + }, + "hint" : { + "name-required" : "Il nome è obbligatorio.", + "name-pattern" : "Il nome non è valido.", + "name-max-length" : "Il nome deve essere inferiore a 256 caratteri.", + "title-required" : "Il titolo è obbligatorio.", + "title-pattern" : "Il titolo non è valido.", + "title-max-length" : "Il titolo deve essere inferiore a 256 caratteri.", + "key-required" : "La chiave è obbligatoria.", + "key-pattern" : "La chiave non è valida.", + "key-max-length" : "La chiave deve essere inferiore a 256 caratteri." + }, + "required-fields" : "Campi obbligatori mancanti" + }, + "content-type" : { + "json" : "Json", + "text" : "Testo", + "binary" : "Binario (Base64)" + }, + "color" : { + "color" : "Colore" + }, + "customer" : { + "customer" : "Cliente", + "customers" : "Clienti", + "management" : "Gestione clienti", + "dashboard" : "Dashboard cliente", + "dashboards" : "Dashboard clienti", + "devices" : "Dispositivi cliente", + "entity-views" : "Viste entità cliente", + "assets" : "Asset cliente", + "public-dashboards" : "Dashboard pubbliche", + "public-devices" : "Dispositivi pubblici", + "public-assets" : "Asset pubblici", + "public-entity-views" : "Viste entità pubbliche", + "add" : "Aggiungi cliente", + "delete" : "Elimina cliente", + "manage-customer-users" : "Gestisci utenti cliente", + "manage-customer-devices" : "Gestisci dispositivi cliente", + "manage-customer-dashboards" : "Gestisci dashboard cliente", + "manage-public-devices" : "Gestisci dispositivi pubblici", + "manage-public-dashboards" : "Gestisci dashboard pubbliche", + "manage-customer-assets" : "Gestisci asset cliente", + "manage-customer-edges" : "Gestisci edge cliente", + "manage-public-assets" : "Gestisci asset pubblici", + "add-customer-text" : "Aggiungi nuovo cliente", + "no-customers-text" : "Nessun cliente trovato", + "customer-details" : "Dettagli cliente", + "delete-customer-title" : "Sei sicuro di voler eliminare il cliente '{{customerTitle}}'?", + "delete-customer-text" : "Attenzione, dopo la conferma il cliente e tutti i dati associati saranno irrimediabilmente persi.", + "delete-customers-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 cliente} other {# clienti} }?", + "delete-customers-action-title" : "Elimina { count, plural, =1 {1 cliente} other {# clienti} }", + "delete-customers-text" : "Attenzione, dopo la conferma tutti i clienti selezionati saranno eliminati e tutti i dati associati saranno irrimediabilmente persi.", + "manage-users" : "Gestisci utenti", + "manage-assets" : "Gestisci asset", + "manage-devices" : "Gestisci dispositivi", + "manage-dashboards" : "Gestisci dashboard", + "title" : "Titolo", + "title-required" : "Il titolo è obbligatorio.", + "title-max-length" : "Il titolo deve essere inferiore a 256", + "description" : "Descrizione", + "details" : "Dettagli", + "events" : "Eventi", + "copyId" : "Copia ID cliente", + "idCopiedMessage" : "ID cliente copiato negli appunti", + "select-customer" : "Seleziona cliente", + "no-customers-matching" : "Nessun cliente corrispondente a '{{entity}}' trovato.", + "customer-required" : "Il cliente è obbligatorio", + "select-default-customer" : "Seleziona cliente predefinito", + "default-customer" : "Cliente predefinito", + "default-customer-required" : "Il cliente predefinito è obbligatorio per eseguire il debug del dashboard a livello di Tenant", + "search" : "Cerca clienti", + "selected-customers" : "{ count, plural, =1 {1 cliente} other {# clienti} } selezionato/i", + "edges" : "Istanza edge cliente", + "manage-edges" : "Gestisci edge" + }, + "css-size" : { + "size-value-required" : "Il valore della dimensione è obbligatorio", + "invalid-size-value" : "Valore della dimensione non valido" + }, + "date" : { + "last-update-n-ago" : "Ultimo aggiornamento N fa", + "last-update-n-ago-text" : "Ultimo aggiornamento {{ agoText }}", + "custom-date" : "Data personalizzata", + "format" : "Formato", + "preview" : "Anteprima", + "auto" : "Auto", + "time-granularity-formats" : "Formati di granularità temporale", + "unit-year" : "Anni", + "unit-month" : "Mesi", + "unit-day" : "Giorni", + "unit-hour" : "Ore", + "unit-minute" : "Minuti", + "unit-second" : "Secondi", + "unit-millisecond" : "Millisecondi" + }, + "datetime" : { + "date-from" : "Data da", + "time-from" : "Ora da", + "date-to" : "Data a", + "time-to" : "Ora a", + "from" : "Da", + "to" : "A" + }, + "dashboard" : { + "dashboard" : "Dashboard", + "dashboards" : "Dashboard", + "management" : "Gestione dashboard", + "view-dashboards" : "Visualizza dashboard", + "add" : "Aggiungi dashboard", + "assign-dashboard-to-customer" : "Assegna dashboard al cliente", + "assign-dashboard-to-customer-text" : "Seleziona le dashboard da assegnare al cliente", + "assign-to-customer-text" : "Seleziona il cliente a cui assegnare la dashboard", + "assign-to-customer" : "Assegna al cliente", + "unassign-from-customer" : "Disassegna dal cliente", + "make-public" : "Rendi dashboard pubblica", + "make-private" : "Rendi dashboard privata", + "manage-assigned-customers" : "Gestisci clienti assegnati", + "assigned-customers" : "Clienti assegnati", + "assign-to-customers" : "Assegna dashboard ai clienti", + "assign-to-customers-text" : "Seleziona i clienti a cui assegnare la dashboard", + "unassign-from-customers" : "Disassegna dashboard dai clienti", + "unassign-from-customers-text" : "Seleziona i clienti da cui disassegnare la dashboard", + "no-dashboards-text" : "Nessuna dashboard trovata", + "no-widgets" : "Nessun widget configurato", + "add-widget" : "Aggiungi nuovo widget", + "add-widget-button-text" : "Aggiungi widget", + "title" : "Titolo", + "image" : "Immagine dashboard", + "mobile-app-settings" : "Impostazioni applicazione mobile", + "mobile-order" : "Ordine della dashboard nell'app mobile", + "mobile-hide" : "Nascondi dashboard nell'app mobile", + "update-image" : "Aggiorna immagine dashboard", + "take-screenshot" : "Cattura screenshot", + "select-widget-title" : "Seleziona widget", + "select-widget-value" : "{{title}}: seleziona widget", + "select-widget-subtitle" : "Elenco dei tipi di widget disponibili", + "delete" : "Elimina dashboard", + "title-required" : "Il titolo è obbligatorio.", + "title-max-length" : "Il titolo deve essere inferiore a 256", + "description" : "Descrizione", + "details" : "Dettagli", + "dashboard-details" : "Dettagli dashboard", + "add-dashboard-text" : "Aggiungi nuova dashboard", + "assign-dashboards" : "Assegna dashboard", + "assign-new-dashboard" : "Assegna nuova dashboard", + "assign-dashboards-text" : "Assegna { count, plural, =1 {1 dashboard} other {# dashboard} } ai clienti", + "unassign-dashboards-action-text" : "Disassegna { count, plural, =1 {1 dashboard} other {# dashboard} } dai clienti", + "delete-dashboards" : "Elimina dashboard", + "unassign-dashboards" : "Disassegna dashboard", + "unassign-dashboards-action-title" : "Disassegna { count, plural, =1 {1 dashboard} other {# dashboard} } dal cliente", + "delete-dashboard-title" : "Sei sicuro di voler eliminare la dashboard '{{dashboardTitle}}'?", + "delete-dashboard-text" : "Attenzione, dopo la conferma la dashboard e tutti i dati associati saranno irrimediabilmente persi.", + "delete-dashboards-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 dashboard} other {# dashboard} }?", + "delete-dashboards-action-title" : "Elimina { count, plural, =1 {1 dashboard} other {# dashboard} }", + "delete-dashboards-text" : "Attenzione, dopo la conferma tutte le dashboard selezionate saranno eliminate e tutti i dati correlati saranno irrimediabilmente persi.", + "unassign-dashboard-title" : "Sei sicuro di voler disassegnare la dashboard '{{dashboardTitle}}'?", + "unassign-dashboard-text" : "Dopo la conferma la dashboard sarà disassegnata e non sarà accessibile dal cliente.", + "unassign-dashboard" : "Disassegna dashboard", + "unassign-dashboards-title" : "Sei sicuro di voler disassegnare { count, plural, =1 {1 dashboard} other {# dashboard} }?", + "unassign-dashboards-text" : "Dopo la conferma tutte le dashboard selezionate saranno disassegnate e non saranno accessibili dal cliente.", + "public-dashboard-title" : "La dashboard è ora pubblica", + "public-dashboard-text" : "La tua dashboard {{dashboardTitle}} è ora pubblica e accessibile tramite il seguente link:", + "public-dashboard-notice" : "Nota: Non dimenticare di rendere pubblici i dispositivi correlati per accedere ai loro dati.", + "make-private-dashboard-title" : "Sei sicuro di voler rendere privata la dashboard '{{dashboardTitle}}'?", + "make-private-dashboard-text" : "Dopo la conferma la dashboard sarà resa privata e non sarà accessibile da altri.", + "make-private-dashboard" : "Rendi dashboard privata", + "socialshare-text" : "'{{dashboardTitle}}' powered by ThingsBoard", + "socialshare-title" : "'{{dashboardTitle}}' powered by ThingsBoard", + "select-dashboard" : "Seleziona dashboard", + "no-dashboards-matching" : "Nessuna dashboard corrispondente a '{{entity}}' trovata.", + "dashboard-required" : "La dashboard è obbligatoria.", + "select-existing" : "Seleziona dashboard esistente", + "create-new" : "Crea nuova dashboard", + "new-dashboard-title" : "Titolo nuova dashboard", + "open-dashboard" : "Apri dashboard", + "set-background" : "Imposta sfondo", + "background-color" : "Colore sfondo", + "background-image" : "Immagine di sfondo", + "background-size-mode" : "Modalità dimensione sfondo", + "no-image" : "Nessuna immagine selezionata", + "empty-image" : "Nessuna immagine", + "drop-image" : "Trascina un'immagine o fai clic per selezionare un file da caricare.", + "maximum-upload-file-size" : "Dimensione massima file caricabile: {{ size }}", + "cannot-upload-file" : "Impossibile caricare il file", + "settings" : "Impostazioni", + "move-all-widgets" : "Sposta tutti i widget", + "move-by" : "Sposta di", + "cols" : "colonne", + "rows" : "righe", + "layout" : "Layout", + "layout-type-default" : "Predefinito", + "layout-type-scada" : "SCADA", + "layout-type-divider" : "Divisore", + "layout-settings-type" : "Impostazioni layout: breakpoint {{ type }}", + "columns-count" : "Numero colonne", + "columns-count-required" : "Il numero di colonne è obbligatorio.", + "min-columns-count-message" : "Il numero minimo consentito di colonne è 10.", + "max-columns-count-message" : "Il numero massimo consentito di colonne è 1000.", + "min-layout-width" : "Larghezza minima layout", + "columns-suffix" : "colonne", + "widgets-margins" : "Margine tra i widget", + "margin-required" : "Il valore del margine è obbligatorio.", + "min-margin-message" : "Il valore minimo del margine è 0.", + "max-margin-message" : "Il valore massimo del margine è 50.", + "horizontal-margin" : "Margine orizzontale", + "horizontal-margin-required" : "Il valore del margine orizzontale è obbligatorio.", + "min-horizontal-margin-message" : "Il valore minimo del margine orizzontale è 0.", + "max-horizontal-margin-message" : "Il valore massimo del margine orizzontale è 50.", + "vertical-margin" : "Margine verticale", + "vertical-margin-required" : "Il valore del margine verticale è obbligatorio.", + "min-vertical-margin-message" : "Il valore minimo del margine verticale è 0.", + "max-vertical-margin-message" : "Il valore massimo del margine verticale è 50.", + "apply-outer-margin" : "Applica margine ai bordi del layout", + "autofill-height" : "Altezza layout automatica", + "mobile-layout" : "Impostazioni layout mobile", + "mobile-row-height" : "Altezza riga mobile", + "mobile-row-height-required" : "L'altezza della riga mobile è obbligatoria.", + "min-mobile-row-height-message" : "Il valore minimo per l'altezza della riga mobile è 5 pixel.", + "max-mobile-row-height-message" : "Il valore massimo per l'altezza della riga mobile è 200 pixel.", + "row-height" : "Altezza riga", + "row-height-required" : "Il valore dell'altezza riga è obbligatorio.", + "min-row-height-message" : "Il valore minimo per l'altezza riga è 5 pixel.", + "max-row-height-message" : "Il valore massimo per l'altezza riga è 200 pixel.", + "display-first-in-mobile-view" : "Visualizza per primo nella vista mobile", + "title-settings" : "Impostazioni titolo", + "display-title" : "Visualizza titolo dashboard", + "title-color" : "Colore titolo", + "toolbar-settings" : "Impostazioni barra strumenti", + "hide-toolbar" : "Nascondi barra strumenti", + "toolbar-always-open" : "Mantieni barra strumenti aperta", + "display-dashboards-selection" : "Visualizza selezione dashboard", + "display-entities-selection" : "Visualizza selezione entità", + "display-filters" : "Visualizza filtri", + "display-dashboard-timewindow" : "Visualizza intervallo temporale", + "display-dashboard-export" : "Visualizza esportazione", + "display-update-dashboard-image" : "Visualizza aggiornamento immagine dashboard", + "dashboard-logo-settings" : "Impostazioni logo dashboard", + "display-dashboard-logo" : "Visualizza logo in modalità schermo intero", + "dashboard-logo-image" : "Immagine logo dashboard", + "advanced-settings" : "Impostazioni avanzate", + "dashboard-css" : "CSS dashboard", + "import" : "Importa dashboard", + "export" : "Esporta dashboard", + "export-failed-error" : "Impossibile esportare la dashboard: {{error}}", + "export-prompt" : "Incorpora immagini e risorse della dashboard", + "create-new-dashboard" : "Crea nuova dashboard", + "dashboard-file" : "File dashboard", + "invalid-dashboard-file-error" : "Impossibile importare la dashboard: struttura dati della dashboard non valida.", + "dashboard-import-missing-aliases-title" : "Configura alias utilizzati dalla dashboard importata", + "create-new-widget" : "Crea nuovo widget", + "import-widget" : "Importa widget", + "widget-file" : "File widget", + "invalid-widget-file-error" : "Impossibile importare il widget: struttura dati del widget non valida.", + "widget-import-missing-aliases-title" : "Configura alias utilizzati dal widget importato", + "open-toolbar" : "Apri barra strumenti dashboard", + "close-toolbar" : "Chiudi barra strumenti", + "configuration-error" : "Errore di configurazione", + "alias-resolution-error-title" : "Errore configurazione alias dashboard", + "invalid-aliases-config" : "Impossibile trovare dispositivi corrispondenti ad alcuni filtri alias.
Contatta l'amministratore per risolvere il problema.", + "select-devices" : "Seleziona dispositivi", + "assignedToCustomer" : "Assegnato al cliente", + "assignedToCustomers" : "Assegnato ai clienti", + "public" : "Pubblico", + "copyId" : "Copia ID dashboard", + "idCopiedMessage" : "ID dashboard copiato negli appunti", + "public-link" : "Link pubblico", + "copy-public-link" : "Copia link pubblico", + "public-link-copied-message" : "Link pubblico della dashboard copiato negli appunti", + "manage-states" : "Gestisci stati della dashboard", + "states" : "Stati dashboard", + "states-short" : "Stati", + "search-states" : "Cerca stati dashboard", + "selected-states" : "{ count, plural, =1 {1 stato dashboard} other {# stati dashboard} } selezionati", + "edit-state" : "Modifica stato dashboard", + "delete-state" : "Elimina stato dashboard", + "add-state" : "Aggiungi stato dashboard", + "no-states-text" : "Nessuno stato trovato", + "state" : "Stato dashboard", + "state-name" : "Nome", + "state-name-required" : "Il nome dello stato dashboard è obbligatorio.", + "state-id" : "ID Stato", + "state-id-required" : "L'ID dello stato dashboard è obbligatorio.", + "state-id-exists" : "Uno stato dashboard con lo stesso ID esiste già.", + "is-root-state" : "Stato radice", + "delete-state-title" : "Elimina stato dashboard", + "delete-state-text" : "Sei sicuro di voler eliminare lo stato della dashboard con nome '{{stateName}}'?", + "show-details" : "Mostra dettagli", + "hide-details" : "Nascondi dettagli", + "select-state" : "Seleziona stato di destinazione", + "state-controller" : "Controllore stato", + "state-controller-default" : "statico (deprecato)", + "search" : "Cerca dashboard", + "selected-dashboards" : "{ count, plural, =1 {1 dashboard} other {# dashboard} } selezionate", + "home-dashboard" : "Dashboard principale", + "home-dashboard-hide-toolbar" : "Nascondi barra strumenti della dashboard principale", + "unassign-dashboard-from-edge-text" : "Dopo la conferma la dashboard sarà disassegnata e non sarà più accessibile dall'edge.", + "unassign-dashboards-from-edge-title" : "Sei sicuro di voler disassegnare { count, plural, =1 {1 dashboard} other {# dashboard} }?", + "unassign-dashboards-from-edge-text" : "Dopo la conferma tutte le dashboard selezionate saranno disassegnate e non saranno più accessibili dall'edge.", + "assign-dashboard-to-edge" : "Assegna Dashboard a Edge", + "assign-dashboard-to-edge-text" : "Seleziona le dashboard da assegnare all'edge", + "non-existent-dashboard-state-error" : "Stato dashboard con ID \"{{ stateId }}\" non trovato", + "edit-mode" : "Modalità modifica", + "duplicate-state-action" : "Duplica stato", + "breakpoint-value" : "Breakpoint ({{ value }})", + "breakpoints-id" : { + "default" : "Predefinito", + "xs" : "Mobile (xs)", + "sm" : "Tablet (sm)", + "md" : "Laptop (md)", + "lg" : "Desktop (lg)", + "xl" : "Desktop (xl)" + }, + "view-format-type-grid" : "Griglia", + "view-format-type-list" : "Elenco", + "view-format" : "Formato visualizzazione" + }, + "datakey" : { + "settings" : "Impostazioni", + "general" : "Generale", + "advanced" : "Avanzate", + "key" : "Chiave", + "keys" : "Chiavi", + "label" : "Etichetta", + "color" : "Colore", + "units" : "Simbolo speciale da mostrare accanto al valore", + "decimals" : "Numero di cifre dopo il punto decimale", + "data-generation-func" : "Funzione di generazione dati", + "use-data-post-processing-func" : "Usa funzione di post-elaborazione dati", + "configuration" : "Configurazione chiave dati", + "timeseries" : "Serie temporale", + "attributes" : "Attributi", + "entity-field" : "Campo entità", + "alarm" : "Campi allarme", + "timeseries-required" : "Le serie temporali dell'entità sono richieste.", + "timeseries-or-attributes-required" : "Serie temporali o attributi dell'entità richiesti.", + "alarm-fields-timeseries-or-attributes-required" : "Campi allarme o serie temporali/attributi dell'entità richiesti.", + "maximum-timeseries-or-attributes" : "Massimo { count, plural, =1 {1 serie temporale/attributo consentito.} other {# serie temporali/attributi consentiti} }", + "alarm-fields-required" : "I campi allarme sono richiesti.", + "function-types" : "Tipi di funzione", + "function-type" : "Tipo di funzione", + "function-types-required" : "Tipi di funzione richiesti.", + "data-keys" : "Chiavi dati", + "data-key" : "Chiave dati", + "data-keys-required" : "Chiavi dati richieste.", + "data-key-required" : "Chiave dati richiesta.", + "alarm-keys" : "Chiavi dati allarme", + "alarm-key" : "Chiave dati allarme", + "alarm-key-functions" : "Funzioni chiave allarme", + "alarm-key-function" : "Funzione chiave allarme", + "latest-keys" : "Ultime chiavi dati", + "latest-key" : "Ultima chiave dati", + "latest-key-functions" : "Funzioni ultima chiave", + "latest-key-function" : "Funzione ultima chiave", + "timeseries-keys" : "Chiavi dati serie temporale", + "timeseries-key" : "Chiave dati serie temporale", + "timeseries-key-functions" : "Funzioni chiave serie temporale", + "timeseries-key-function" : "Funzione chiave serie temporale", + "maximum-function-types" : "Massimo { count, plural, =1 {1 tipo di funzione consentito.} other {# tipi di funzione consentiti} }", + "time-description" : "timestamp del valore corrente;", + "value-description" : "valore corrente;", + "prev-value-description" : "risultato della chiamata precedente della funzione;", + "time-prev-description" : "timestamp del valore precedente;", + "prev-orig-value-description" : "valore originale precedente;", + "aggregation" : "Aggregazione", + "aggregation-type-hint-common" : "Per motivi di prestazioni, il calcolo dei valori aggregati è disponibile solo per intervalli di tempo fissi come \"oggi\", \"mese corrente\", ecc., e non per finestre temporali mobili come 'ultimi 30 minuti' o 'ultime 24 ore'.", + "aggregation-type-none-hint" : "Prendi l'ultimo valore.", + "aggregation-type-min-hint" : "Trova il valore minimo tra i punti dati nella finestra temporale selezionata.", + "aggregation-type-max-hint" : "Trova il valore massimo tra i punti dati nella finestra temporale selezionata.", + "aggregation-type-avg-hint" : "Calcola il valore medio tra i punti dati nella finestra temporale selezionata.", + "aggregation-type-sum-hint" : "Somma tutti i valori dei punti dati nella finestra temporale selezionata.", + "aggregation-type-count-hint" : "Numero totale di punti dati nella finestra temporale selezionata.", + "delta-calculation" : "Calcolo delta", + "enable-delta-calculation" : "Abilita calcolo delta", + "enable-delta-calculation-hint" : "Quando abilitato, il valore della chiave dati è calcolato in base ai valori aggregati per una finestra temporale selezionata e un periodo di confronto specificato. Per motivi di prestazioni, il calcolo delta è disponibile solo per finestre storiche e non per valori in tempo reale. Ad esempio, è possibile calcolare il delta tra il consumo energetico di ieri rispetto al giorno prima.", + "delta-calculation-result" : "Risultato calcolo delta", + "delta-calculation-result-previous-value" : "Valore precedente", + "delta-calculation-result-delta-absolute" : "Delta (assoluto)", + "delta-calculation-result-delta-percent" : "Delta (percentuale)", + "source" : "Fonte", + "latest" : "Ultimo", + "latest-value" : "Ultimo valore", + "delta" : "delta", + "percent" : "percentuale", + "absolute" : "assoluto" + }, + "datasource" : { + "type" : "Tipo sorgente dati", + "name" : "Nome", + "label" : "Etichetta", + "add-datasource-prompt" : "Aggiungi una sorgente dati" + }, + "details" : { + "details" : "Dettagli", + "edit-mode" : "Modalità modifica", + "edit-json" : "Modifica JSON", + "toggle-edit-mode" : "Attiva/disattiva modalità modifica" + }, + "device" : { + "device" : "Dispositivo", + "device-required" : "Dispositivo richiesto.", + "devices" : "Dispositivi", + "management" : "Gestione dispositivi", + "view-devices" : "Visualizza dispositivi", + "device-alias" : "Alias del dispositivo", + "device-type-max-length" : "Il tipo di dispositivo deve essere inferiore a 256 caratteri", + "aliases" : "Alias dei dispositivi", + "no-alias-matching" : "'{{alias}}' non trovato.", + "no-aliases-found" : "Nessun alias trovato.", + "no-key-matching" : "'{{key}}' non trovato.", + "no-keys-found" : "Nessuna chiave trovata.", + "create-new-alias" : "Creane uno nuovo!", + "create-new-key" : "Creane uno nuovo!", + "duplicate-alias-error" : "Alias duplicato trovato '{{alias}}'.
Gli alias dei dispositivi devono essere univoci all'interno della dashboard.", + "configure-alias" : "Configura alias '{{alias}}'", + "no-devices-matching" : "Nessun dispositivo corrispondente a '{{entity}}' trovato.", + "alias" : "Alias", + "alias-required" : "Alias del dispositivo richiesto.", + "remove-alias" : "Rimuovi alias del dispositivo", + "add-alias" : "Aggiungi alias del dispositivo", + "name-starts-with" : "Espressione nome dispositivo", + "help-text" : "Usa '%' secondo necessità: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list" : "Elenco dispositivi", + "use-device-name-filter" : "Usa filtro", + "device-list-empty" : "Nessun dispositivo selezionato.", + "device-name-filter-required" : "Filtro nome dispositivo richiesto.", + "device-name-filter-no-device-matched" : "Nessun dispositivo trovato con nome che inizia per '{{device}}'.", + "add" : "Aggiungi dispositivo", + "assign-to-customer" : "Assegna al cliente", + "assign-device-to-customer" : "Assegna dispositivo(i) al cliente", + "assign-device-to-customer-text" : "Seleziona i dispositivi da assegnare al cliente", + "make-public" : "Rendi pubblico il dispositivo", + "make-private" : "Rendi privato il dispositivo", + "no-devices-text" : "Nessun dispositivo trovato", + "assign-to-customer-text" : "Seleziona il cliente a cui assegnare i dispositivi", + "device-details" : "Dettagli dispositivo", + "add-device-text" : "Aggiungi nuovo dispositivo", + "credentials" : "Credenziali", + "manage-credentials" : "Gestisci credenziali", + "delete" : "Elimina dispositivo", + "assign-devices" : "Assegna dispositivi", + "assign-devices-text" : "Assegna { count, plural, =1 {1 dispositivo} other {# dispositivi} } al cliente", + "delete-devices" : "Elimina dispositivi", + "unassign-from-customer" : "Rimuovi assegnazione dal cliente", + "unassign-devices" : "Rimuovi assegnazione dispositivi", + "unassign-devices-action-title" : "Rimuovi assegnazione di { count, plural, =1 {1 dispositivo} other {# dispositivi} } dal cliente", + "unassign-device-from-edge-title" : "Sei sicuro di voler rimuovere il dispositivo '{{deviceName}}' dall'edge?", + "unassign-device-from-edge-text" : "Dopo la conferma il dispositivo non sarà più accessibile dall'edge.", + "unassign-devices-from-edge" : "Rimuovi dispositivi dall'edge", + "assign-new-device" : "Assegna nuovo dispositivo", + "make-public-device-title" : "Sei sicuro di voler rendere pubblico il dispositivo '{{deviceName}}'?", + "make-public-device-text" : "Dopo la conferma il dispositivo e tutti i suoi dati saranno pubblici e accessibili.", + "make-private-device-title" : "Sei sicuro di voler rendere privato il dispositivo '{{deviceName}}'?", + "make-private-device-text" : "Dopo la conferma il dispositivo e tutti i suoi dati saranno privati e non accessibili da altri.", + "view-credentials" : "Visualizza credenziali", + "delete-device-title" : "Sei sicuro di voler eliminare il dispositivo '{{deviceName}}'?", + "delete-device-text" : "Attenzione, dopo la conferma il dispositivo e tutti i dati correlati non saranno più recuperabili.", + "delete-devices-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", + "delete-devices-action-title" : "Elimina { count, plural, =1 {1 dispositivo} other {# dispositivi} }", + "delete-devices-text" : "Attenzione, dopo la conferma tutti i dispositivi selezionati verranno rimossi e i dati correlati non saranno più recuperabili.", + "unassign-device-title" : "Sei sicuro di voler rimuovere l'assegnazione del dispositivo '{{deviceName}}'?", + "unassign-device-text" : "Dopo la conferma il dispositivo non sarà più accessibile dal cliente.", + "unassign-device" : "Rimuovi assegnazione dispositivo", + "unassign-devices-title" : "Sei sicuro di voler rimuovere l'assegnazione di { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", + "unassign-devices-text" : "Dopo la conferma tutti i dispositivi selezionati saranno rimossi dal cliente.", + "device-credentials" : "Credenziali del dispositivo", + "loading-device-credentials" : "Caricamento credenziali del dispositivo...", + "credentials-type" : "Tipo di credenziali", + "access-token" : "Token di accesso", + "access-token-required" : "Token di accesso richiesto.", + "access-token-invalid" : "La lunghezza del token di accesso deve essere compresa tra 1 e 32 caratteri.", + "certificate-pem-format" : "Certificato in formato PEM", + "certificate-pem-format-required" : "Certificato richiesto.", + "copy-access-token" : "Copia token di accesso", + "copy-certificate" : "Copia certificato", + "copy-client-id" : "Copia ID client", + "copy-user-name" : "Copia nome utente", + "copy-password" : "Copia password", + "generate-client-id" : "Genera ID client", + "generate-user-name" : "Genera nome utente", + "generate-password" : "Genera password", + "generate-access-token" : "Genera token di accesso", + "lwm2m-security-config" : { + "identity" : "Identità del client", + "identity-required" : "L'identità del client è obbligatoria.", + "identity-tooltip" : "L'identificatore PSK è un identificatore arbitrario fino a 128 byte, come descritto nello standard [RFC7925].\nL'identificatore PSK DEVE essere prima convertito in una stringa di caratteri e poi codificato in ottetti usando UTF-8.", + "client-key" : "Chiave del client", + "client-key-required" : "La chiave del client è obbligatoria.", + "client-key-tooltip-prk" : "La chiave pubblica o l'id RPK devono essere conformi allo standard [RFC7250] e codificati in formato Base64!", + "client-key-tooltip-psk" : "La chiave PSK deve rispettare lo standard [RFC4279] e deve essere in formato HexDec: 32, 64, 128 caratteri!", + "endpoint" : "Nome endpoint del client", + "endpoint-required" : "Il nome endpoint del client è obbligatorio.", + "client-public-key" : "Chiave pubblica del client", + "client-public-key-hint" : "Se la chiave pubblica del client è vuota, verrà utilizzato il certificato attendibile", + "client-public-key-tooltip" : "La chiave pubblica X509 deve essere in formato DER-encoded X509v3, supportare esclusivamente l'algoritmo EC e deve essere codificata in Base64!", + "mode" : "Modalità configurazione sicurezza", + "client-tab" : "Configurazione sicurezza client", + "client-certificate" : "Certificato del client", + "bootstrap-tab" : "Client bootstrap", + "bootstrap-server" : "Server Bootstrap", + "lwm2m-server" : "Server LwM2M", + "client-publicKey-or-id" : "Chiave pubblica o ID del client", + "client-publicKey-or-id-required" : "Chiave pubblica o ID del client è obbligatoria.", + "client-publicKey-or-id-tooltip-psk" : "L'identificatore PSK è un identificatore arbitrario fino a 128 byte, come descritto nello standard [RFC7925].\nL'identificatore PSK DEVE essere prima convertito in una stringa e poi codificato in UTF-8.", + "client-publicKey-or-id-tooltip-rpk" : "La chiave pubblica o l'id RPK devono essere nello standard [RFC7250] e codificati in formato Base64!", + "client-publicKey-or-id-tooltip-x509" : "La chiave pubblica X509 deve essere in formato DER-encoded X509v3, supportare solo l'algoritmo EC e codificata in Base64", + "client-secret-key" : "Chiave segreta del client", + "client-secret-key-required" : "Chiave segreta del client è obbligatoria.", + "client-secret-key-tooltip-psk" : "La chiave PSK deve rispettare lo standard [RFC4279] e deve essere in formato HexDec: 32, 64, 128 caratteri!", + "client-secret-key-tooltip-prk" : "La chiave segreta RPK deve essere in formato PKCS_8 (codifica DER, standard [RFC5958]) e codificata in Base64!", + "client-secret-key-tooltip-x509" : "La chiave segreta X509 deve essere in formato PKCS_8 (codifica DER, standard [RFC5958]) e codificata in Base64!" + }, + "client-id" : "ID client", + "client-id-pattern" : "Contiene carattere non valido.", + "user-name" : "Nome utente", + "user-name-required" : "Nome utente richiesto.", + "client-id-or-user-name-necessary" : "ID client e/o nome utente sono obbligatori", + "password" : "Password", + "secret" : "Segreto", + "secret-required" : "Segreto richiesto.", + "device-type" : "Profilo dispositivo", + "device-type-required" : "Tipo di dispositivo richiesto.", + "select-device-type" : "Seleziona tipo di dispositivo", + "enter-device-type" : "Inserisci profilo dispositivo", + "any-device" : "Qualsiasi dispositivo", + "no-device-types-matching" : "Nessun profilo dispositivo corrispondente a '{{entitySubtype}}' trovato.", + "device-type-list-empty" : "Nessun profilo dispositivo selezionato!", + "device-profile-type-list-empty" : "Deve essere selezionato almeno un profilo dispositivo.", + "device-types" : "Tipi di dispositivo", + "name" : "Nome", + "name-required" : "Nome richiesto.", + "name-max-length" : "Il nome deve essere inferiore a 256 caratteri", + "label-max-length" : "L'etichetta deve essere inferiore a 256 caratteri", + "description" : "Descrizione", + "label" : "Etichetta", + "events" : "Eventi", + "details" : "Dettagli", + "copyId" : "Copia ID dispositivo", + "copyAccessToken" : "Copia token di accesso", + "copy-mqtt-authentication" : "Copia credenziali MQTT", + "idCopiedMessage" : "ID dispositivo copiato negli appunti", + "accessTokenCopiedMessage" : "Token di accesso del dispositivo copiato negli appunti", + "mqtt-authentication-copied-message" : "Credenziali MQTT del dispositivo copiate negli appunti", + "assignedToCustomer" : "Assegnato al cliente", + "unable-delete-device-alias-title" : "Impossibile eliminare alias dispositivo", + "unable-delete-device-alias-text" : "L'alias del dispositivo '{{deviceAlias}}' non può essere eliminato poiché è utilizzato dai seguenti widget:
{{widgetsList}}", + "is-gateway" : "È un gateway", + "overwrite-activity-time" : "Sovrascrivi tempo di attività per il dispositivo connesso", + "device-filter" : "Filtro dispositivo", + "device-filter-title" : "Filtro dispositivo", + "filter-title" : "Filtro", + "device-state" : "Stato dispositivo", + "state" : "Stato", + "any" : "Qualsiasi", + "active" : "Attivo", + "inactive" : "Inattivo", + "public" : "Pubblico", + "device-public" : "Il dispositivo è pubblico", + "select-device" : "Seleziona dispositivo", + "import" : "Importa dispositivo", + "device-file" : "File del dispositivo", + "search" : "Cerca dispositivi", + "selected-devices" : "{ count, plural, =1 {1 dispositivo} other {# dispositivi} } selezionati", + "device-configuration" : "Configurazione dispositivo", + "transport-configuration" : "Configurazione trasporto", + "wizard" : { + "device-details" : "Dettagli del dispositivo" + }, + "unassign-devices-from-edge-title" : "Sei sicuro di voler disassociare { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", + "unassign-devices-from-edge-text" : "Dopo la conferma, tutti i dispositivi selezionati saranno disassociati e non saranno più accessibili dall'edge.", + "time" : "Tempo", + "connectivity" : { + "check-connectivity" : "Verifica connettività", + "device-created-check-connectivity" : "Dispositivo creato. Verifichiamo la connettività!", + "loading-check-connectivity-command" : "Caricamento dei comandi per la verifica della connettività...", + "use-following-instructions" : "Usa le seguenti istruzioni per inviare la telemetria per conto del dispositivo tramite shell", + "execute-following-command" : "Esegui il seguente comando", + "install-curl-windows" : "A partire da Windows 10 b17063, cURL è disponibile di default", + "install-curl-macos" : "A partire da Mac OS X 10.2 6C115 (Jaguar), cURL è disponibile di default", + "install-mqtt-windows" : "Utilizza le istruzioni per scaricare, installare, configurare ed eseguire mosquitto_pub", + "install-coap-client" : "Utilizza le istruzioni per scaricare, installare, configurare ed eseguire coap-client", + "install-necessary-client-tools" : "Installa gli strumenti client necessari", + "mqtts-x509-command" : "Consulta la seguente documentazione per connettere il dispositivo tramite MQTT con autorizzazione X509", + "coaps-x509-command" : "Consulta la seguente documentazione per connettere il dispositivo tramite CoAP su DTLS con autorizzazione X509", + "snmp-command" : "Consulta la seguente documentazione per connettere il dispositivo tramite SNMP.", + "sparkplug-command" : "Consulta la seguente documentazione per connettere il dispositivo tramite MQTT Sparkplug.", + "lwm2m-command" : "Consulta la seguente documentazione per connettere il dispositivo tramite LWM2M." + } + }, + "dynamic-form" : { + "property" : { + "properties" : "Proprietà", + "property" : "Proprietà", + "id" : "Id", + "name" : "Nome", + "type" : "Tipo", + "type-text" : "Testo", + "type-password" : "Password", + "type-textarea" : "Area di testo", + "type-number" : "Numero", + "type-switch" : "Interruttore", + "type-select" : "Selezione", + "type-radios" : "Pulsanti radio", + "type-datetime" : "Data/Ora", + "type-image" : "Immagine", + "type-javascript" : "JavaScript", + "type-json" : "JSON", + "type-html" : "HTML", + "type-css" : "CSS", + "type-markdown" : "Markdown", + "type-color" : "Colore", + "type-color-settings" : "Impostazioni colore", + "type-font" : "Carattere", + "type-units" : "Unità", + "type-icon" : "Icona", + "type-fieldset" : "Fieldset", + "type-array" : "Array", + "type-html-section" : "Sezione HTML", + "group-title" : "Titolo del gruppo", + "no-properties" : "Nessuna proprietà configurata", + "add-property" : "Aggiungi proprietà", + "property-settings" : "Impostazioni proprietà", + "remove-property" : "Rimuovi proprietà", + "default-value" : "Valore predefinito", + "value-required" : "Valore richiesto", + "number-settings" : "Impostazioni numeriche", + "min" : "Min", + "max" : "Max", + "step" : "Passo", + "selected-options-limit" : "Limite opzioni selezionate", + "advanced-ui-settings" : "Impostazioni UI avanzate", + "disable-on-property" : "Disabilita in base alla proprietà", + "display-condition-function" : "Funzione di condizione di visualizzazione", + "sub-label" : "Sotto etichetta", + "vertical-divider-after" : "Divisore verticale dopo", + "input-field-suffix" : "Suffisso campo di input", + "property-row-classes" : "Classi riga proprietà", + "property-field-classes" : "Classi campo proprietà", + "not-unique-property-ids-error" : "Gli ID delle proprietà devono essere univoci!", + "enable-multiple-select" : "Abilita selezione multipla", + "allow-empty-select-option" : "Consenti opzione vuota", + "select-options" : "Opzioni di selezione", + "not-unique-select-option-value-error" : "I valori delle opzioni devono essere univoci!", + "value" : "Valore", + "label" : "Etichetta", + "add-option" : "Aggiungi opzione", + "no-options" : "Nessuna opzione configurata", + "remove-option" : "Rimuovi opzione", + "textarea-rows" : "Righe dell'area di testo", + "help-id" : "ID di aiuto", + "buttons-direction" : "Direzione dei pulsanti", + "direction-row" : "Riga", + "direction-column" : "Colonna", + "radio-button-options" : "Opzioni pulsanti radio", + "datetime-type" : "Tipo campo Data/Ora", + "datetime-type-date" : "Data", + "datetime-type-time" : "Ora", + "datetime-type-datetime" : "Data/Ora", + "enable-clear-button" : "Abilita pulsante di cancellazione", + "html-section-settings" : "Impostazioni sezione HTML", + "html-section-classes" : "Classi sezione HTML", + "html-section-content" : "Contenuto sezione HTML", + "array-item" : "Elemento dell'array", + "item-type" : "Tipo di elemento", + "item-name" : "Nome dell'elemento", + "no-items" : "Nessun elemento" + }, + "clear-form" : "Cancella modulo", + "clear-form-prompt" : "Sei sicuro di voler rimuovere tutte le proprietà del modulo?", + "import-form" : "Importa modulo da JSON", + "export-form" : "Esporta modulo in JSON", + "json-file" : "File JSON", + "json-content" : "Contenuto JSON", + "invalid-form-json-file-error" : "Impossibile importare il modulo da JSON: struttura dei dati JSON non valida." + }, + "asset-profile" : { + "asset-profile" : "Profilo asset", + "asset-profiles" : "Profili asset", + "all-asset-profiles" : "Tutti", + "add" : "Aggiungi profilo asset", + "edit" : "Modifica profilo asset", + "asset-profile-details" : "Dettagli del profilo asset", + "no-asset-profiles-text" : "Nessun profilo asset trovato", + "search" : "Cerca profili asset", + "selected-asset-profiles" : "{ count, plural, =1 {1 profilo asset} other {# profili asset} } selezionati", + "no-asset-profiles-matching" : "Nessun profilo asset corrispondente a '{{entity}}' trovato.", + "asset-profile-required" : "Il profilo asset è obbligatorio", + "idCopiedMessage" : "ID del profilo asset copiato negli appunti", + "set-default" : "Imposta come predefinito", + "delete" : "Elimina profilo asset", + "copyId" : "Copia ID profilo asset", + "name-max-length" : "Il nome deve essere inferiore a 256 caratteri", + "new-device-profile-name" : "Nome del profilo asset", + "new-device-profile-name-required" : "Il nome del profilo asset è obbligatorio.", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio.", + "image" : "Immagine profilo asset", + "description" : "Descrizione", + "default" : "Predefinito", + "default-rule-chain" : "Catena di regole predefinita", + "default-edge-rule-chain" : "Catena di regole Edge predefinita", + "default-edge-rule-chain-hint" : "Utilizzato su Edge come catena di regole per elaborare i dati in arrivo per gli asset di questo profilo", + "mobile-dashboard" : "Dashboard mobile", + "mobile-dashboard-hint" : "Utilizzato dall'app mobile come dashboard dei dettagli dell'asset", + "select-queue-hint" : "Seleziona dall'elenco a discesa.", + "delete-asset-profile-title" : "Sei sicuro di voler eliminare il profilo asset '{{assetProfileName}}'?", + "delete-asset-profile-text" : "Attenzione, dopo la conferma il profilo asset e tutti i dati correlati saranno irrecuperabili.", + "delete-asset-profiles-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 profilo asset} other {# profili asset} }?", + "delete-asset-profiles-text" : "Attenzione, dopo la conferma tutti i profili asset selezionati saranno eliminati e tutti i dati correlati saranno irrecuperabili.", + "set-default-asset-profile-title" : "Sei sicuro di voler rendere il profilo asset '{{assetProfileName}}' predefinito?", + "set-default-asset-profile-text" : "Dopo la conferma, il profilo asset sarà contrassegnato come predefinito e sarà utilizzato per i nuovi asset senza profilo specificato.", + "no-asset-profiles-found" : "Nessun profilo asset trovato.", + "create-new-asset-profile" : "Creane uno nuovo!", + "create-asset-profile" : "Crea nuovo profilo asset", + "import" : "Importa profilo asset", + "export" : "Esporta profilo asset", + "export-failed-error" : "Impossibile esportare il profilo asset: {{error}}", + "asset-profile-file" : "File profilo asset", + "invalid-asset-profile-file-error" : "Impossibile importare il profilo asset: struttura dati non valida." + }, + "device-profile" : { + "device-profile" : "Profilo dispositivo", + "device-profiles" : "Profili dispositivo", + "all-device-profiles" : "Tutti", + "add" : "Aggiungi profilo dispositivo", + "edit" : "Modifica profilo dispositivo", + "device-profile-details" : "Dettagli profilo dispositivo", + "no-device-profiles-text" : "Nessun profilo dispositivo trovato", + "search" : "Cerca profili dispositivo", + "selected-device-profiles" : "{ count, plural, =1 {1 profilo dispositivo} other {# profili dispositivo} } selezionati", + "no-device-profiles-matching" : "Nessun profilo dispositivo corrispondente a '{{entity}}' trovato.", + "device-profile-required" : "Il profilo dispositivo è obbligatorio", + "idCopiedMessage" : "ID profilo dispositivo copiato negli appunti", + "set-default" : "Imposta come predefinito", + "delete" : "Elimina profilo dispositivo", + "copyId" : "Copia ID profilo dispositivo", + "name-max-length" : "Il nome deve essere inferiore a 256 caratteri", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio.", + "type" : "Tipo di profilo", + "type-required" : "Il tipo di profilo è obbligatorio.", + "type-default" : "Predefinito", + "image" : "Immagine profilo dispositivo", + "transport-type" : "Tipo di trasporto", + "transport-type-required" : "Il tipo di trasporto è obbligatorio.", + "transport-type-default" : "Predefinito", + "transport-type-default-hint" : "Supporta trasporto base MQTT, HTTP e CoAP", + "transport-type-mqtt" : "MQTT", + "transport-type-mqtt-hint" : "Abilita impostazioni avanzate MQTT", + "transport-type-coap" : "CoAP", + "transport-type-coap-hint" : "Abilita impostazioni avanzate CoAP", + "transport-type-lwm2m" : "LWM2M", + "transport-type-lwm2m-hint" : "Tipo di trasporto LWM2M", + "transport-type-snmp" : "SNMP", + "transport-type-snmp-hint" : "Specifica configurazione trasporto SNMP", + "transport-type-http" : "HTTP", + "description" : "Descrizione", + "default" : "Predefinito", + "profile-configuration" : "Configurazione profilo", + "transport-configuration" : "Configurazione trasporto", + "default-rule-chain" : "Catena di regole predefinita", + "default-edge-rule-chain" : "Catena di regole Edge predefinita", + "default-edge-rule-chain-hint" : "Utilizzato su Edge come catena di regole per elaborare i dati in arrivo per dispositivi di questo profilo", + "mobile-dashboard" : "Dashboard mobile", + "mobile-dashboard-hint" : "Utilizzato dall'app mobile come dashboard dei dettagli del dispositivo", + "select-queue-hint" : "Seleziona dall'elenco a discesa.", + "delete-device-profile-title" : "Sei sicuro di voler eliminare il profilo dispositivo '{{deviceProfileName}}'?", + "delete-device-profile-text" : "Attenzione, dopo la conferma il profilo dispositivo e tutti i dati correlati, inclusi gli aggiornamenti OTA associati, saranno irrecuperabili.", + "delete-device-profiles-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 profilo dispositivo} other {# profili dispositivo} }?", + "delete-device-profiles-text" : "Attenzione, dopo la conferma tutti i profili dispositivo selezionati saranno rimossi e tutti i dati correlati, inclusi gli aggiornamenti OTA associati, saranno irrecuperabili.", + "set-default-device-profile-title" : "Sei sicuro di voler rendere il profilo dispositivo '{{deviceProfileName}}' predefinito?", + "set-default-device-profile-text" : "Dopo la conferma, il profilo dispositivo sarà impostato come predefinito e verrà utilizzato per i nuovi dispositivi senza profilo specificato.", + "no-device-profiles-found" : "Nessun profilo dispositivo trovato.", + "create-new-device-profile" : "Creane uno nuovo!", + "mqtt-device-topic-filters" : "Filtri topic dispositivo MQTT", + "mqtt-device-topic-filters-unique" : "I filtri topic del dispositivo MQTT devono essere unici.", + "mqtt-device-topic-filters-spark-plug" : "Nodo Edge of Network (EoN) MQTT Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-hint" : "Consente connessioni da nodi EoN con payload e formato topic Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names" : "Metriche SparkPlug da salvare come attributi.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint" : "Nomi delle metriche SparkPlug che verranno salvate come attributi del dispositivo. Tutte le altre metriche saranno archiviate come telemetria.", + "mqtt-device-payload-type" : "Payload dispositivo MQTT", + "mqtt-device-payload-type-json" : "JSON", + "mqtt-device-payload-type-proto" : "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format" : "Abilita compatibilità con altri formati payload.", + "mqtt-enable-compatibility-with-json-payload-format-hint" : "Se abilitato, la piattaforma utilizzerà di default il formato payload Protobuf. In caso di errore di parsing, tenterà di utilizzare il formato JSON. Utile per la retrocompatibilità durante aggiornamenti firmware. Modalità soggetta a un leggero degrado delle prestazioni.", + "mqtt-use-json-format-for-default-downlink-topics" : "Usa formato Json per topic di downlink predefiniti", + "mqtt-use-json-format-for-default-downlink-topics-hint" : "Usato per inviare attributi e RPC via Json su v1/devices/me/.... Non influisce sui topic v2.", + "mqtt-send-ack-on-validation-exception" : "Invia PUBACK in caso di errore di validazione PUBLISH", + "mqtt-send-ack-on-validation-exception-hint" : "Se abilitato, invia l'acknowledgment invece di chiudere la sessione MQTT in caso di errore di validazione.", + "snmp-add-mapping" : "Aggiungi mapping SNMP", + "snmp-mapping-not-configured" : "Nessun mapping OID configurato per time series/telemetria", + "snmp-timseries-or-attribute-name" : "Nome time series/attributo per mapping", + "snmp-timseries-or-attribute-type" : "Tipo time series/attributo per mapping", + "snmp-method-pdu-type-get-request" : "GetRequest", + "snmp-method-pdu-type-get-next-request" : "GetNextRequest", + "snmp-oid" : "OID", + "transport-device-payload-type-json" : "JSON", + "transport-device-payload-type-proto" : "Protobuf", + "mqtt-payload-type-required" : "Tipo payload obbligatorio.", + "coap-device-type" : "Tipo dispositivo CoAP", + "coap-device-payload-type" : "Payload dispositivo CoAP", + "coap-device-type-required" : "Tipo dispositivo CoAP obbligatorio.", + "coap-device-type-default" : "Predefinito", + "coap-device-type-efento" : "Efento NB-IoT", + "support-level-wildcards" : "Supportati i caratteri jolly singolo [+] e multilivello [#].", + "telemetry-topic-filter" : "Filtro topic telemetria", + "telemetry-topic-filter-required" : "Filtro topic telemetria obbligatorio.", + "attributes-topic-filter" : "Filtro topic pubblicazione attributi", + "attributes-subscribe-topic-filter" : "Filtro topic sottoscrizione attributi", + "attributes-topic-filter-required" : "Filtro topic pubblicazione attributi obbligatorio.", + "attributes-subscribe-topic-filter-required" : "Filtro topic sottoscrizione attributi obbligatorio.", + "telemetry-proto-schema" : "Schema proto telemetria", + "telemetry-proto-schema-required" : "Schema proto telemetria obbligatorio.", + "attributes-proto-schema" : "Schema proto attributi", + "attributes-proto-schema-required" : "Schema proto attributi obbligatorio.", + "rpc-response-proto-schema" : "Schema proto risposta RPC", + "rpc-response-proto-schema-required" : "Schema proto risposta RPC obbligatorio.", + "rpc-response-topic-filter" : "Filtro topic risposta RPC", + "rpc-response-topic-filter-required" : "Filtro topic risposta RPC obbligatorio.", + "rpc-request-proto-schema" : "Schema proto richiesta RPC", + "rpc-request-proto-schema-required" : "Schema proto richiesta RPC obbligatorio.", + "rpc-request-proto-schema-hint" : "Messaggio RPC deve contenere: string method = 1; int32 requestId = 2; params = 3 (qualsiasi tipo).", + "not-valid-pattern-topic-filter" : "Filtro topic con pattern non valido", + "not-valid-single-character" : "Utilizzo non valido del carattere jolly singolo", + "not-valid-multi-character" : "Utilizzo non valido del carattere jolly multilivello", + "single-level-wildcards-hint" : "[+] è adatto per qualsiasi livello del filtro del topic. Es.: v1/devices/+/telemetry oppure +/devices/+/attributes.", + "multi-level-wildcards-hint" : "[#] può sostituire l'intero filtro del topic e deve essere l'ultimo simbolo del topic. Es.: # oppure v1/devices/me/#.", + "alarm-rules" : "Regole allarme", + "alarm-rules-with-count" : "Regole allarme ({{count}})", + "no-alarm-rules" : "Nessuna regola allarme configurata", + "add-alarm-rule" : "Aggiungi regola allarme", + "edit-alarm-rule" : "Modifica regola allarme", + "alarm-type" : "Tipo allarme", + "alarm-type-required" : "Tipo allarme obbligatorio.", + "alarm-type-unique" : "Tipo allarme deve essere unico nel profilo dispositivo.", + "alarm-type-max-length" : "Tipo allarme deve essere inferiore a 256 caratteri", + "create-alarm-pattern" : "Crea allarme {{alarmType}}", + "create-alarm-rules" : "Crea regole allarme", + "no-create-alarm-rules" : "Nessuna condizione di creazione configurata", + "add-create-alarm-rule-prompt" : "Aggiungi una regola di creazione allarme", + "clear-alarm-rule" : "Regola di cancellazione allarme", + "no-clear-alarm-rule" : "Nessuna condizione di cancellazione configurata", + "add-create-alarm-rule" : "Aggiungi condizione di creazione", + "add-clear-alarm-rule" : "Aggiungi condizione di cancellazione", + "select-alarm-severity" : "Seleziona severità allarme", + "alarm-severity-required" : "Severità allarme obbligatoria.", + "condition-duration" : "Durata condizione", + "condition-duration-value" : "Valore durata", + "condition-duration-time-unit" : "Unità di tempo", + "condition-duration-value-range" : "Valore durata deve essere compreso tra 1 e 2147483647.", + "condition-duration-value-pattern" : "Valore durata deve essere un numero intero.", + "condition-duration-value-required" : "Valore durata obbligatorio.", + "condition-duration-time-unit-required" : "Unità di tempo obbligatoria.", + "advanced-settings" : "Impostazioni avanzate", + "alarm-rule-additional-info" : "Informazioni aggiuntive", + "edit-alarm-rule-additional-info" : "Modifica informazioni aggiuntive", + "alarm-rule-additional-info-placeholder" : "Fornisci qui i tuoi commenti e le modifiche per visualizzarli nei dettagli dell'allarme sotto 'Informazioni aggiuntive'", + "alarm-rule-additional-info-hint" : "Suggerimento: usa ${keyName} per sostituire i valori delle chiavi attributo o telemetria usate nella condizione dell'allarme.", + "alarm-rule-mobile-dashboard" : "Dashboard mobile", + "alarm-rule-mobile-dashboard-hint" : "Usata dall'app mobile come dashboard dei dettagli dell'allarme", + "alarm-rule-no-mobile-dashboard" : "Nessuna dashboard selezionata", + "propagate-alarm" : "Propaga allarme alle entità correlate", + "alarm-rule-relation-types-list" : "Tipi di relazione", + "alarm-rule-relation-types-list-hint" : "Definisce i tipi di relazione per filtrare le entità correlate. Se non impostato, l'allarme verrà propagato a tutte le entità correlate.", + "propagate-alarm-to-owner" : "Propaga allarme al proprietario dell'entità (Cliente o Tenant)", + "propagate-alarm-to-tenant" : "Propaga allarme al Tenant", + "alarm-rule-condition" : "Condizione regola allarme", + "enter-alarm-rule-condition-prompt" : "Aggiungi una condizione per la regola dell'allarme", + "edit-alarm-rule-condition" : "Modifica condizione regola allarme", + "device-provisioning" : "Provisioning dispositivo", + "provision-strategy" : "Strategia di provisioning", + "provision-strategy-required" : "Strategia di provisioning obbligatoria.", + "provision-strategy-disabled" : "Disabilitato", + "provision-strategy-created-new" : "Consenti la creazione di nuovi dispositivi", + "provision-strategy-check-pre-provisioned" : "Verifica dispositivi pre-provisioned", + "provision-device-key" : "Chiave dispositivo di provisioning", + "provision-device-key-required" : "Chiave dispositivo di provisioning obbligatoria.", + "copy-provision-key" : "Copia chiave di provisioning", + "provision-key-copied-message" : "Chiave di provisioning copiata negli appunti", + "provision-device-secret" : "Segreto dispositivo di provisioning", + "provision-device-secret-required" : "Segreto dispositivo di provisioning obbligatorio.", + "copy-provision-secret" : "Copia segreto di provisioning", + "provision-secret-copied-message" : "Segreto di provisioning copiato negli appunti", + "provision-strategy-x509" : { + "certificate-chain" : "Catena di certificati X509", + "certificate-chain-hint" : "La strategia X.509 è utilizzata per effettuare il provisioning dei dispositivi tramite certificati client in comunicazione TLS a doppia via.", + "allow-create-new-devices" : "Crea nuovi dispositivi", + "allow-create-new-devices-hint" : "Se selezionato, verranno creati nuovi dispositivi e il certificato client sarà utilizzato come credenziali del dispositivo.", + "certificate-value" : "Certificato in formato PEM", + "certificate-value-required" : "Certificato in formato PEM obbligatorio", + "cn-regex-variable" : "Variabile espressione regolare CN", + "cn-regex-variable-required" : "Variabile CN espressione regolare obbligatoria", + "cn-regex-variable-hint" : "Necessaria per estrarre il nome del dispositivo dal CN del certificato X509 del dispositivo." + }, + "condition" : "Condizione", + "condition-type" : "Tipo di condizione", + "condition-type-simple" : "Semplice", + "condition-type-duration" : "Durata", + "condition-during" : "Durante {{during}}", + "condition-during-dynamic" : "Durante \"{{ attribute }}\" ({{during}})", + "condition-type-repeating" : "Ripetuta", + "condition-type-required" : "Tipo di condizione obbligatorio.", + "condition-repeating-value" : "Numero di eventi", + "condition-repeating-value-range" : "Il numero di eventi deve essere compreso tra 1 e 2147483647.", + "condition-repeating-value-pattern" : "Il numero di eventi deve essere un numero intero.", + "condition-repeating-value-required" : "Numero di eventi obbligatorio.", + "condition-repeat-times" : "Ripetuto { count, plural, =1 {1 volta} other {# volte} }", + "condition-repeat-times-dynamic" : "Ripetuto \"{ attribute }\" ({ count, plural, =1 {1 volta} other {# volte} })", + "schedule-type" : "Tipo di pianificazione", + "schedule-type-required" : "Tipo di pianificazione obbligatorio.", + "schedule" : "Pianificazione", + "edit-schedule" : "Modifica pianificazione allarme", + "schedule-any-time" : "Attivo sempre", + "schedule-specific-time" : "Attivo in un momento specifico", + "schedule-custom" : "Personalizzato", + "schedule-day" : { + "monday" : "Lunedì", + "tuesday" : "Martedì", + "wednesday" : "Mercoledì", + "thursday" : "Giovedì", + "friday" : "Venerdì", + "saturday" : "Sabato", + "sunday" : "Domenica" + }, + "schedule-days" : "Giorni", + "schedule-time" : "Orario", + "schedule-time-from" : "Da", + "schedule-time-to" : "A", + "schedule-days-of-week-required" : "Deve essere selezionato almeno un giorno della settimana.", + "create-device-profile" : "Crea nuovo profilo dispositivo", + "import" : "Importa profilo dispositivo", + "export" : "Esporta profilo dispositivo", + "export-failed-error" : "Impossibile esportare il profilo dispositivo: {{error}}", + "device-profile-file" : "File del profilo dispositivo", + "invalid-device-profile-file-error" : "Impossibile importare il profilo dispositivo: struttura dati non valida.", + "power-saving-mode" : "Modalità risparmio energetico", + "power-saving-mode-type" : { + "default" : "Usa modalità risparmio energetico del profilo dispositivo", + "psm" : "Modalità risparmio energetico (PSM)", + "drx" : "Ricezione discontinua (DRX)", + "edrx" : "Ricezione discontinua estesa (eDRX)" + }, + "edrx-cycle" : "Ciclo eDRX", + "edrx-cycle-required" : "Il ciclo eDRX è obbligatorio.", + "edrx-cycle-pattern" : "Il ciclo eDRX deve essere un numero intero positivo.", + "edrx-cycle-min" : "Il numero minimo del ciclo eDRX è {{ min }} secondi.", + "paging-transmission-window" : "Finestra di trasmissione Paging", + "paging-transmission-window-required" : "La finestra di trasmissione Paging è obbligatoria.", + "paging-transmission-window-pattern" : "La finestra di trasmissione Paging deve essere un numero intero positivo.", + "paging-transmission-window-min" : "Il numero minimo della finestra di trasmissione Paging è {{ min }} secondi.", + "psm-activity-timer" : "Timer attività PSM", + "psm-activity-timer-required" : "Il timer attività PSM è obbligatorio.", + "psm-activity-timer-pattern" : "Il timer attività PSM deve essere un numero intero positivo.", + "psm-activity-timer-min" : "Il numero minimo del timer attività PSM è {{ min }} secondi.", + "lwm2m" : { + "object-list" : "Elenco oggetti", + "object-list-empty" : "Nessun oggetto selezionato.", + "no-objects-found" : "Nessun oggetto trovato.", + "no-objects-matching" : "Nessun oggetto corrispondente a '{{object}}' trovato.", + "model-tab" : "Modello LWM2M", + "add-new-instances" : "Aggiungi nuove istanze", + "instances-list" : "Elenco istanze", + "instances-list-required" : "L'elenco delle istanze è obbligatorio.", + "instance-id-pattern" : "L'ID istanza deve essere un numero intero positivo.", + "instance-id-max" : "Valore massimo dell'ID istanza {{max}}.", + "instance" : "Istanza", + "resource-label" : "#ID Nome risorsa", + "observe-label" : "Osserva", + "attribute-label" : "Attributo", + "telemetry-label" : "Telemetria", + "edit-observe-select" : "Per modificare l’osservazione, seleziona telemetria o attributo", + "edit-attributes-select" : "Per modificare gli attributi, seleziona telemetria o attributo", + "no-attributes-set" : "Nessun attributo impostato", + "key-name" : "Nome chiave", + "key-name-required" : "Nome chiave obbligatorio", + "attribute-name" : "Nome attributo", + "attribute-name-required" : "Nome attributo obbligatorio.", + "attribute-value" : "Valore attributo", + "attribute-value-required" : "Valore attributo obbligatorio.", + "attribute-value-pattern" : "Il valore dell’attributo deve essere un numero intero positivo.", + "edit-attributes" : "Modifica attributi: {{ name }}", + "view-attributes" : "Visualizza attributi: {{ name }}", + "add-attribute" : "Aggiungi attributo", + "edit-attribute" : "Modifica attributo", + "view-attribute" : "Visualizza attributo", + "remove-attribute" : "Rimuovi attributo", + "delete-server-text" : "Attenzione, dopo la conferma la configurazione del server non sarà più recuperabile.", + "delete-server-title" : "Sei sicuro di voler eliminare il server?", + "mode" : "Modalità di configurazione della sicurezza", + "bootstrap-tab" : "Bootstrap", + "bootstrap-server-legend" : "Server Bootstrap (ShortId...)", + "lwm2m-server-legend" : "Server LwM2M (ShortId...)", + "server" : "Server", + "short-id" : "ID server breve", + "short-id-tooltip" : "ID breve del server. Usato come collegamento per associare l'istanza dell’oggetto server.\nIdentifica in modo univoco ogni server LwM2M configurato per il client LwM2M.\nLa risorsa DEVE essere impostata quando la risorsa Bootstrap-Server è impostata su 'false'.\nGli ID 0 e 65535 NON DEVONO essere utilizzati per identificare un server LwM2M.", + "short-id-tooltip-bootstrap" : "ID breve del server. Usato come collegamento per associare l'istanza dell’oggetto server.\nIdentifica in modo univoco ogni server LwM2M configurato per il client LwM2M.\nLa risorsa DEVE essere impostata quando la risorsa Bootstrap-Server è impostata su 'false'.", + "short-id-required" : "ID server breve obbligatorio.", + "short-id-range" : "L'ID server breve deve essere compreso tra {{ min }} e {{ max }}.", + "short-id-pattern" : "L'ID server breve deve essere un numero intero positivo.", + "lifetime" : "Durata registrazione client", + "lifetime-required" : "Durata registrazione client obbligatoria.", + "lifetime-pattern" : "La durata della registrazione deve essere un numero intero positivo.", + "default-min-period" : "Periodo minimo tra due notifiche (s)", + "default-min-period-tooltip" : "Valore predefinito che il client LwM2M dovrebbe usare per il periodo minimo di un’osservazione, in assenza di parametro specifico.", + "default-min-period-required" : "Periodo minimo obbligatorio.", + "default-min-period-pattern" : "Il periodo minimo deve essere un numero intero positivo.", + "notification-storing" : "Memorizzazione notifiche quando disabilitato o offline", + "binding" : "Binding", + "binding-type" : { + "u" : "U: Il client è raggiungibile tramite binding UDP in qualsiasi momento.", + "m" : "M: Il client è raggiungibile tramite binding MQTT in qualsiasi momento.", + "h" : "H: Il client è raggiungibile tramite binding HTTP in qualsiasi momento.", + "t" : "T: Il client è raggiungibile tramite binding TCP in qualsiasi momento.", + "s" : "S: Il client è raggiungibile tramite binding SMS in qualsiasi momento.", + "n" : "N: Il client DEVE rispondere tramite binding Non-IP (supportato da LwM2M 1.1).", + "uq" : "UQ: Connessione UDP in modalità coda (non supportato da LwM2M 1.1)", + "uqs" : "UQS: connessioni UDP e SMS attive; UDP in modalità coda, SMS in modalità standard (non supportato da LwM2M 1.1)", + "tq" : "TQ: Connessione TCP in modalità coda (non supportato da LwM2M 1.1)", + "tqs" : "TQS: connessioni TCP e SMS attive; TCP in modalità coda, SMS in modalità standard (non supportato da LwM2M 1.1)", + "sq" : "SQ: Connessione SMS in modalità coda (non supportato da LwM2M 1.1)" + }, + "binding-tooltip" : "Questo è l'elenco nella risorsa \"binding\" dell'oggetto server LwM2M - /1/x/7.\nIndica le modalità di binding supportate nel client LwM2M.\nQuesto valore DOVREBBE essere uguale al valore nella risorsa “Supported Binding and Modes” dell’oggetto Device (/3/0/16).\nSebbene siano supportati più trasporti, solo uno può essere utilizzato durante l'intera sessione di trasporto.\nAd esempio, quando UDP e SMS sono entrambi supportati, il client e il server LwM2M possono scegliere di comunicare solo tramite UDP o SMS per l’intera sessione.", + "bootstrap-server" : "Server Bootstrap", + "lwm2m-server" : "Server LwM2M", + "include-bootstrap-server" : "Includi aggiornamenti del Server Bootstrap", + "bootstrap-update-title" : "Hai già configurato il Server Bootstrap. Sei sicuro di voler escludere gli aggiornamenti?", + "bootstrap-update-text" : "Attenzione, dopo la conferma la configurazione del Server Bootstrap non sarà più recuperabile.", + "server-host" : "Host", + "server-host-required" : "Host obbligatorio.", + "server-port" : "Porta", + "server-port-required" : "Porta obbligatoria.", + "server-port-pattern" : "La porta deve essere un numero intero positivo.", + "server-port-range" : "La porta deve essere compresa tra 1 e 65535.", + "server-public-key" : "Chiave pubblica del server", + "server-public-key-required" : "Chiave pubblica del server obbligatoria.", + "client-hold-off-time" : "Tempo di attesa client", + "client-hold-off-time-required" : "Tempo di attesa client obbligatorio.", + "client-hold-off-time-pattern" : "Il tempo di attesa deve essere un numero intero positivo.", + "client-hold-off-time-tooltip" : "Tempo di attesa client da usare solo con il Server Bootstrap", + "account-after-timeout" : "Contabilizza dopo il timeout", + "account-after-timeout-required" : "Contabilizzazione dopo il timeout obbligatoria.", + "account-after-timeout-pattern" : "Deve essere un numero intero positivo.", + "account-after-timeout-tooltip" : "Valore del timeout del Server Bootstrap dopo il quale avviene la contabilizzazione.", + "server-type" : "Tipo di server", + "add-new-server-title" : "Aggiungi nuova configurazione server", + "add-server-config" : "Aggiungi configurazione server", + "add-lwm2m-server-config" : "Aggiungi server LwM2M", + "no-config-servers" : "Nessun server configurato", + "others-tab" : "Altre impostazioni", + "client-strategy" : "Strategia client alla connessione", + "client-strategy-label" : "Strategia", + "client-strategy-only-observe" : "Solo richiesta di osservazione dopo la connessione iniziale", + "client-strategy-read-all" : "Leggi tutte le risorse e invia richiesta di osservazione dopo la registrazione", + "fw-update" : "Aggiornamento firmware", + "fw-update-strategy" : "Strategia di aggiornamento firmware", + "fw-update-strategy-data" : "Invia aggiornamento come file binario usando Oggetto 19, Risorsa 0 (Data)", + "fw-update-strategy-package" : "Invia aggiornamento come file binario usando Oggetto 5, Risorsa 0 (Package)", + "fw-update-strategy-package-uri" : "Genera URL CoAP univoco e invia aggiornamento come Oggetto 5, Risorsa 1 (Package URI)", + "sw-update" : "Aggiornamento software", + "sw-update-strategy" : "Strategia di aggiornamento software", + "sw-update-strategy-package" : "Invia file binario usando Oggetto 9, Risorsa 2 (Package)", + "sw-update-strategy-package-uri" : "Genera URL CoAP univoco per scaricare il pacchetto e invia aggiornamento usando Oggetto 9, Risorsa 3 (Package URI)", + "fw-update-resource" : "Risorsa CoAP aggiornamento firmware", + "fw-update-resource-required" : "Risorsa CoAP aggiornamento firmware obbligatoria.", + "sw-update-resource" : "Risorsa CoAP aggiornamento software", + "sw-update-resource-required" : "Risorsa CoAP aggiornamento software obbligatoria.", + "config-json-tab" : "Configurazione JSON profilo dispositivo", + "attributes-name" : { + "min-period" : "Periodo minimo", + "max-period" : "Periodo massimo", + "greater-than" : "Maggiore di", + "less-than" : "Minore di", + "step" : "Passo", + "min-evaluation-period" : "Periodo minimo di valutazione", + "max-evaluation-period" : "Periodo massimo di valutazione" + }, + "default-object-id" : "Versione oggetto predefinita (Attributo)", + "default-object-id-ver" : { + "v1-0" : "1.0", + "v1-1" : "1.1" + } + }, + "snmp" : { + "add-communication-config" : "Aggiungi configurazione di comunicazione", + "add-mapping" : "Aggiungi mappatura", + "authentication-passphrase" : "Passphrase di autenticazione", + "authentication-passphrase-required" : "La passphrase di autenticazione è obbligatoria.", + "authentication-protocol" : "Protocollo di autenticazione", + "authentication-protocol-required" : "Il protocollo di autenticazione è obbligatorio.", + "communication-configs" : "Configurazioni di comunicazione", + "community" : "Stringa di community", + "community-required" : "La stringa di community è obbligatoria.", + "context-name" : "Nome contesto", + "data-key" : "Chiave dati", + "data-key-required" : "La chiave dati è obbligatoria.", + "data-type" : "Tipo di dato", + "data-type-required" : "Il tipo di dato è obbligatorio.", + "engine-id" : "ID motore", + "host" : "Host", + "host-required" : "L'host è obbligatorio.", + "oid" : "OID", + "oid-pattern" : "Formato OID non valido.", + "oid-required" : "OID è obbligatorio.", + "please-add-communication-config" : "Aggiungi una configurazione di comunicazione", + "please-add-mapping-config" : "Aggiungi una configurazione di mappatura", + "port" : "Porta", + "port-format" : "Formato porta non valido.", + "port-required" : "La porta è obbligatoria.", + "privacy-passphrase" : "Passphrase di privacy", + "privacy-passphrase-required" : "La passphrase di privacy è obbligatoria.", + "privacy-protocol" : "Protocollo di privacy", + "privacy-protocol-required" : "Il protocollo di privacy è obbligatorio.", + "protocol-version" : "Versione del protocollo", + "protocol-version-required" : "La versione del protocollo è obbligatoria.", + "querying-frequency" : "Frequenza di interrogazione, ms", + "querying-frequency-invalid-format" : "La frequenza di interrogazione deve essere un intero positivo.", + "querying-frequency-required" : "La frequenza di interrogazione è obbligatoria.", + "retries" : "Tentativi", + "retries-invalid-format" : "I tentativi devono essere un intero positivo.", + "retries-required" : "I tentativi sono obbligatori.", + "scope" : "Ambito", + "scope-required" : "L'ambito è obbligatorio.", + "security-name" : "Nome di sicurezza", + "security-name-required" : "Il nome di sicurezza è obbligatorio.", + "timeout-ms" : "Timeout, ms", + "timeout-ms-invalid-format" : "Il timeout deve essere un intero positivo.", + "timeout-ms-required" : "Il timeout è obbligatorio.", + "user-name" : "Nome utente", + "user-name-required" : "Il nome utente è obbligatorio." + } + }, + "dialog" : { + "close" : "Chiudi finestra di dialogo", + "error-message-title" : "Messaggio di errore:", + "error-details-title" : "Dettagli dell'errore" + }, + "direction" : { + "column" : "Colonna", + "row" : "Riga" + }, + "edge" : { + "edge" : "Edge", + "edge-instances" : "Istanza edge", + "instances" : "Istanze", + "edge-file" : "File edge", + "name-max-length" : "Il nome deve essere inferiore a 256 caratteri", + "label-max-length" : "L'etichetta deve essere inferiore a 256 caratteri", + "type-max-length" : "Il tipo deve essere inferiore a 256 caratteri", + "management" : "Gestione edge", + "no-edges-matching" : "Nessun edge corrispondente a '{{entity}}' trovato.", + "add" : "Aggiungi edge", + "no-edges-text" : "Nessun edge trovato", + "edge-details" : "Dettagli edge", + "add-edge-text" : "Aggiungi nuovo edge", + "delete" : "Elimina edge", + "delete-edge-title" : "Sei sicuro di voler eliminare l'edge '{{edgeName}}'?", + "delete-edge-text" : "Attenzione, dopo la conferma l'edge e tutti i dati correlati saranno irrimediabilmente persi.", + "delete-edges-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 edge} other {# edges} }?", + "delete-edges-text" : "Attenzione, dopo la conferma tutti gli edge selezionati saranno eliminati e tutti i dati correlati saranno irrimediabilmente persi.", + "name" : "Nome", + "name-starts-with" : "Il nome dell'edge inizia con", + "name-required" : "Il nome è obbligatorio.", + "description" : "Descrizione", + "details" : "Dettagli", + "events" : "Eventi", + "copy-id" : "Copia ID edge", + "id-copied-message" : "ID edge copiato negli appunti", + "sync" : "Sincronizza edge", + "edge-required" : "Edge obbligatorio", + "edge-type" : "Tipo di edge", + "edge-type-required" : "Il tipo di edge è obbligatorio.", + "event-action" : "Azione evento", + "entity-id" : "ID entità", + "select-edge-type" : "Seleziona tipo di edge", + "assign-to-customer" : "Assegna al cliente", + "assign-to-customer-text" : "Seleziona il cliente a cui assegnare l'edge", + "assign-edge-to-customer" : "Assegna Edge al Cliente", + "assign-edge-to-customer-text" : "Seleziona gli edge da assegnare al cliente", + "assignedToCustomer" : "Assegnato al cliente", + "edge-public" : "Edge è pubblico", + "assigned-to-customer" : "Assegnato a: {{customerTitle}}", + "unassign-from-customer" : "Disassegna dal cliente", + "unassign-edge-title" : "Sei sicuro di voler disassegnare l'edge '{{edgeName}}'?", + "unassign-edge-text" : "Dopo la conferma, l'edge sarà disassegnato e non sarà più accessibile dal cliente.", + "unassign-edges-title" : "Sei sicuro di voler disassegnare { count, plural, =1 {1 edge} other {# edges} }?", + "unassign-edges-text" : "Dopo la conferma, tutti gli edge selezionati saranno disassegnati e non più accessibili dal cliente.", + "make-public" : "Rendi edge pubblico", + "make-public-edge-title" : "Sei sicuro di voler rendere pubblico l'edge '{{edgeName}}'?", + "make-public-edge-text" : "Dopo la conferma, l'edge e tutti i suoi dati saranno resi pubblici e accessibili da altri.", + "make-private" : "Rendi edge privato", + "public" : "Pubblico", + "make-private-edge-title" : "Sei sicuro di voler rendere privato l'edge '{{edgeName}}'?", + "make-private-edge-text" : "Dopo la conferma, l'edge e tutti i suoi dati saranno resi privati e non più accessibili da altri.", + "import" : "Importa edge", + "install-connect-instructions" : "Istruzioni di installazione e connessione", + "install-connect-instructions-edge-created" : "Edge creato! Controlla le istruzioni di installazione e connessione", + "loading-edge-instructions" : "Caricamento istruzioni edge...", + "label" : "Etichetta", + "load-entity-error" : "Errore nel caricamento dei dati. L'entità è stata eliminata.", + "assign-new-edge" : "Assegna nuovo edge", + "unassign-from-edge" : "Disassegna da edge", + "edge-key" : "Chiave edge", + "copy-edge-key" : "Copia chiave edge", + "edge-key-copied-message" : "Chiave edge copiata negli appunti", + "edge-secret" : "Segreto edge", + "copy-edge-secret" : "Copia segreto edge", + "edge-secret-copied-message" : "Segreto edge copiato negli appunti", + "manage-assets" : "Gestisci asset", + "manage-devices" : "Gestisci dispositivi", + "manage-entity-views" : "Gestisci viste entità", + "manage-dashboards" : "Gestisci dashboard", + "manage-rulechains" : "Gestisci catene di regole", + "assets" : "Asset edge", + "devices" : "Dispositivi Edge", + "entity-views" : "Viste entità Edge", + "dashboard" : "Dashboard Edge", + "dashboards" : "Dashboard Edge", + "rulechain-templates" : "Template di catene di regole", + "edge-rulechain-templates" : "Template catene di regole Edge", + "rulechains" : "Catene di regole Edge", + "search" : "Cerca edge", + "selected-edges" : "{ count, plural, =1 {1 edge} other {# edge} } selezionati", + "any-edge" : "Qualsiasi edge", + "no-edge-types-matching" : "Nessun tipo di edge corrispondente a '{{entitySubtype}}' trovato.", + "edge-type-list-empty" : "Nessun tipo di edge selezionato.", + "edge-types" : "Tipi di edge", + "enter-edge-type" : "Inserisci tipo di edge", + "deployed" : "Distribuito", + "pending" : "In attesa", + "downlinks" : "Downlink", + "no-downlinks-prompt" : "Nessun downlink trovato", + "sync-process-started-successfully" : "Processo di sincronizzazione avviato con successo!", + "missing-related-rule-chains-title" : "Mancano catene di regole correlate nell'edge", + "missing-related-rule-chains-text" : "Le catene di regole assegnate all'edge usano nodi che inoltrano messaggi a catene non assegnate.

Elenco delle catene mancanti:
{{missingRuleChains}}", + "widget-datasource-error" : "Questo widget supporta solo sorgenti dati di tipo EDGE", + "upgrade-instructions" : "Istruzioni di aggiornamento", + "connected" : "Connesso", + "disconnected" : "Disconnesso" + }, + "edge-event" : { + "type-dashboard" : "Dashboard", + "type-asset" : "Asset", + "type-device" : "Dispositivo", + "type-device-profile" : "Profilo dispositivo", + "type-asset-profile" : "Profilo asset", + "type-entity-view" : "Vista entità", + "type-alarm" : "Allarme", + "type-rule-chain" : "Catena di regole", + "type-rule-chain-metadata" : "Metadati catena di regole", + "type-edge" : "Edge", + "type-user" : "Utente", + "type-tenant" : "Tenant", + "type-tenant-profile" : "Profilo tenant", + "type-customer" : "Cliente", + "type-relation" : "Relazione", + "type-widgets-bundle" : "Pacchetto widget", + "type-widgets-type" : "Tipo di widget", + "type-admin-settings" : "Impostazioni amministratore", + "type-ota-package" : "Pacchetto OTA", + "type-queue" : "Coda", + "action-type-added" : "Aggiunto", + "action-type-deleted" : "Eliminato", + "action-type-updated" : "Aggiornato", + "action-type-post-attributes" : "Post attributi", + "action-type-attributes-updated" : "Attributi aggiornati", + "action-type-attributes-deleted" : "Attributi eliminati", + "action-type-timeseries-updated" : "Serie temporali aggiornate", + "action-type-credentials-updated" : "Credenziali aggiornate", + "action-type-assigned-to-customer" : "Assegnato al cliente", + "action-type-unassigned-from-customer" : "Disassegnato dal cliente", + "action-type-relation-add-or-update" : "Relazione aggiunta o aggiornata", + "action-type-relation-deleted" : "Relazione eliminata", + "action-type-rpc-call" : "Chiamata RPC", + "action-type-alarm-ack" : "Conferma allarme", + "action-type-alarm-clear" : "Reset allarme", + "action-type-alarm-assigned" : "Allarme assegnato", + "action-type-alarm-unassigned" : "Allarme disassegnato", + "action-type-assigned-to-edge" : "Assegnato a Edge", + "action-type-unassigned-from-edge" : "Disassegnato da Edge", + "action-type-credentials-request" : "Richiesta credenziali", + "action-type-entity-merge-request" : "Richiesta unione entità" + }, + "error" : { + "unable-to-connect" : "Impossibile connettersi al server! Controlla la connessione a Internet.", + "unhandled-error-code" : "Codice di errore non gestito: {{errorCode}}", + "unknown-error" : "Errore sconosciuto" + }, + "entity" : { + "entity" : "Entità", + "entities" : "Entità", + "entities-count" : "Conteggio entità", + "alarms-count" : "Conteggio allarmi", + "aliases" : "Alias entità", + "aliases-short" : "Alias", + "entity-alias" : "Alias entità", + "unable-delete-entity-alias-title" : "Impossibile eliminare l'alias dell'entità", + "unable-delete-entity-alias-text" : "L'alias dell'entità '{{entityAlias}}' non può essere eliminato poiché è utilizzato dai seguenti widget:
{{widgetsList}}", + "duplicate-alias-error" : "Alias duplicato trovato '{{alias}}'.
Gli alias devono essere univoci all'interno del dashboard.", + "missing-entity-filter-error" : "Filtro mancante per l'alias '{{alias}}'.", + "configure-alias" : "Configura alias '{{alias}}'", + "alias" : "Alias", + "alias-required" : "L'alias dell'entità è obbligatorio.", + "remove-alias" : "Rimuovi alias entità", + "add-alias" : "Aggiungi alias entità", + "edit-alias" : "Modifica alias entità", + "entity-list" : "Elenco entità", + "entity-type" : "Tipo entità", + "entity-types" : "Tipi entità", + "entity-type-list" : "Elenco tipi entità", + "any-entity" : "Qualsiasi entità", + "add-entity-type" : "Aggiungi tipo entità", + "enter-entity-type" : "Inserisci tipo entità", + "no-entities-matching" : "Nessuna entità corrispondente a '{{entity}}' trovata.", + "no-entities-text" : "Nessuna entità trovata", + "no-entity-types-matching" : "Nessun tipo entità corrispondente a '{{entityType}}' trovato.", + "name-starts-with" : "Espressione nome", + "help-text" : "Usa '%' secondo necessità: '%nome_entità_contiene%', '%nome_entità_finisce', 'entità_inizia_con'.", + "use-entity-name-filter" : "Usa filtro", + "entity-list-empty" : "Nessuna entità selezionata.", + "entity-type-list-required" : "È richiesto almeno un tipo di entità.", + "entity-name-filter-required" : "Il filtro sul nome dell'entità è obbligatorio.", + "entity-name-filter-no-entity-matched" : "Nessuna entità che inizia con '{{entity}}' trovata.", + "all-subtypes" : "Tutti", + "select-entities" : "Seleziona entità", + "no-aliases-found" : "Nessun alias trovato.", + "no-alias-matching" : "'{{alias}}' non trovato.", + "create-new-alias" : "Crea un nuovo alias!", + "create-new" : "Crea nuovo", + "key" : "Chiave", + "key-name" : "Nome chiave", + "no-keys-found" : "Nessuna chiave trovata.", + "no-key-matching" : "'{{key}}' non trovato.", + "create-new-key" : "Crea una nuova chiave!", + "type" : "Tipo", + "type-required" : "Il tipo di entità è obbligatorio.", + "type-device" : "Dispositivo", + "type-devices" : "Dispositivi", + "list-of-devices" : "{ count, plural, =1 {Un dispositivo} other {Elenco di # dispositivi} }", + "device-name-starts-with" : "Dispositivi con nomi che iniziano con '{{prefix}}'", + "type-device-profile" : "Profilo dispositivo", + "type-device-profiles" : "Profili dispositivo", + "clear-selected-profiles" : "Cancella profili selezionati", + "list-of-device-profiles" : "{ count, plural, =1 {Un profilo dispositivo} other {Elenco di # profili dispositivo} }", + "device-profile-name-starts-with" : "Profili dispositivo con nomi che iniziano con '{{prefix}}'", + "type-asset-profile" : "Profilo asset", + "type-asset-profiles" : "Profili asset", + "list-of-asset-profiles" : "{ count, plural, =1 {Un profilo asset} other {Elenco di # profili asset} }", + "asset-profile-name-starts-with" : "Profili asset con nomi che iniziano con '{{prefix}}'", + "type-asset" : "Asset", + "type-assets" : "Asset", + "list-of-assets" : "{ count, plural, =1 {Un asset} other {Elenco di # asset} }", + "asset-name-starts-with" : "Asset con nomi che iniziano con '{{prefix}}'", + "type-entity-view" : "Vista entità", + "type-entity-views" : "Viste entità", + "list-of-entity-views" : "{ count, plural, =1 {Una vista entità} other {Elenco di # viste entità} }", + "entity-view-name-starts-with" : "Viste entità con nomi che iniziano con '{{prefix}}'", + "type-rule" : "Regola", + "type-rules" : "Regole", + "list-of-rules" : "{ count, plural, =1 {Una regola} other {Elenco di # regole} }", + "rule-name-starts-with" : "Regole con nomi che iniziano con '{{prefix}}'", + "type-plugin" : "Plugin", + "type-plugins" : "Plugin", + "list-of-plugins" : "{ count, plural, =1 {Un plugin} other {Elenco di # plugin} }", + "plugin-name-starts-with" : "Plugin con nomi che iniziano con '{{prefix}}'", + "type-tenant" : "Tenant", + "type-tenants" : "Tenant", + "list-of-tenants" : "{ count, plural, =1 {Un tenant} other {Elenco di # tenant} }", + "tenant-name-starts-with" : "Tenant con nomi che iniziano con '{{prefix}}'", + "type-tenant-profile" : "Profilo tenant", + "type-tenant-profiles" : "Profili tenant", + "list-of-tenant-profiles" : "{ count, plural, =1 {Un profilo tenant} other {Elenco di # profili tenant} }", + "tenant-profile-name-starts-with" : "Profili tenant con nomi che iniziano con '{{prefix}}'", + "type-customer" : "Cliente", + "type-customers" : "Clienti", + "list-of-customers" : "{ count, plural, =1 {Un cliente} other {Elenco di # clienti} }", + "customer-name-starts-with" : "Clienti i cui nomi iniziano con '{{prefix}}'", + "type-user" : "Utente", + "type-users" : "Utenti", + "list-of-users" : "{ count, plural, =1 {Un utente} other {Elenco di # utenti} }", + "user-name-starts-with" : "Utenti i cui nomi iniziano con '{{prefix}}'", + "type-dashboard" : "Dashboard", + "type-dashboards" : "Dashboard", + "list-of-dashboards" : "{ count, plural, =1 {Una dashboard} other {Elenco di # dashboard} }", + "dashboard-name-starts-with" : "Dashboard i cui nomi iniziano con '{{prefix}}'", + "type-alarm" : "Allarme", + "type-alarms" : "Allarmi", + "list-of-alarms" : "{ count, plural, =1 {Un allarme} other {Elenco di # allarmi} }", + "alarm-name-starts-with" : "Allarmi i cui nomi iniziano con '{{prefix}}'", + "type-rulechain" : "Catena di regole", + "type-rulechains" : "Catene di regole", + "list-of-rulechains" : "{ count, plural, =1 {Una catena di regole} other {Elenco di # catene di regole} }", + "rulechain-name-starts-with" : "Catene di regole i cui nomi iniziano con '{{prefix}}'", + "type-rulenode" : "Nodo regola", + "type-rulenodes" : "Nodi regola", + "list-of-rulenodes" : "{ count, plural, =1 {Un nodo regola} other {Elenco di # nodi regola} }", + "rulenode-name-starts-with" : "Nodi regola i cui nomi iniziano con '{{prefix}}'", + "type-current-customer" : "Cliente corrente", + "type-current-tenant" : "Tenant corrente", + "type-current-user" : "Utente corrente", + "type-current-user-owner" : "Proprietario utente corrente", + "type-calculated-field" : "Campo calcolato", + "type-calculated-fields" : "Campi calcolati", + "type-widgets-bundle" : "Pacchetto widget", + "type-widgets-bundles" : "Pacchetti widget", + "list-of-widgets-bundles" : "{ count, plural, =1 {Un pacchetto widget} other {Elenco di # pacchetti widget} }", + "type-widget" : "Widget", + "type-widgets" : "Widget", + "list-of-widgets" : "{ count, plural, =1 {Un widget} other {Elenco di # widget} }", + "search" : "Cerca entità", + "selected-entities" : "{ count, plural, =1 {1 entità} other {# entità} } selezionate", + "entity-name" : "Nome entità", + "entity-label" : "Etichetta entità", + "details" : "Dettagli entità", + "no-entities-prompt" : "Nessuna entità trovata", + "no-data" : "Nessun dato da visualizzare", + "columns-to-display" : "Colonne da visualizzare", + "type-api-usage-state" : "Stato utilizzo API", + "type-edge" : "Edge", + "type-edges" : "Edge", + "list-of-edges" : "{ count, plural, =1 {Un edge} other {Elenco di # edge} }", + "edge-name-starts-with" : "Edge i cui nomi iniziano con '{{prefix}}'", + "version-conflict" : { + "message" : "Vuoi sovrascrivere la versione esistente o annullare le modifiche e caricare l'ultima versione?", + "link" : "Puoi scaricare la tua versione di {{entityType}} usando questo", + "overwrite" : "Sovrascrivi versione", + "discard" : "Annulla modifiche" + }, + "type-tb-resource" : "Risorsa", + "type-tb-resources" : "Risorse", + "list-of-tb-resources" : "{ count, plural, =1 {Una risorsa} other {Elenco di # risorse} }", + "type-ota-package" : "Pacchetto OTA", + "type-rpc" : "RPC", + "type-queue" : "Coda", + "type-queue-stats" : "Statistiche coda", + "type-queues-stats" : "Statistiche code", + "type-notification" : "Notifica", + "type-notification-rule" : "Regola notifica", + "type-notification-rules" : "Regole notifica", + "list-of-notification-rules" : "{ count, plural, =1 {Una regola di notifica} other {Elenco di # regole di notifica} }", + "type-notification-target" : "Destinatario della notifica", + "type-notification-targets" : "Destinatari della notifica", + "list-of-notification-targets" : "{ count, plural, =1 {Un destinatario della notifica} other {Elenco di # destinatari della notifica} }", + "type-notification-request" : "Richiesta di notifica", + "type-notification-template" : "Modello di notifica", + "type-notification-templates" : "Modelli di notifica", + "list-of-notification-templates" : "{ count, plural, =1 {Un modello di notifica} other {Elenco di # modelli di notifica} }", + "link" : "link", + "type-oauth2-client" : "Client OAuth 2.0", + "type-oauth2-clients" : "Client OAuth 2.0", + "list-of-oauth2-clients" : "{ count, plural, =1 {Un client OAuth 2.0} other {Elenco di # client OAuth 2.0} }", + "type-domain" : "Dominio", + "type-domains" : "Domini", + "list-of-domains" : "{ count, plural, =1 {Un dominio} other {Elenco di # domini} }", + "type-mobile-app" : "Applicazione mobile", + "type-mobile-apps" : "Applicazioni mobili", + "list-of-mobile-apps" : "{ count, plural, =1 {Un'applicazione mobile} other {Elenco di # applicazioni mobili} }", + "type-mobile-app-bundle" : "Pacchetto mobile", + "type-mobile-app-bundles" : "Pacchetti mobili", + "list-of-mobile-app-bundles" : "{ count, plural, =1 {Un pacchetto mobile} other {Elenco di # pacchetti mobili} }" + }, + "entity-field" : { + "created-time" : "Data di creazione", + "name" : "Nome", + "type" : "Tipo", + "first-name" : "Nome", + "last-name" : "Cognome", + "email" : "Email", + "title" : "Titolo", + "country" : "Paese", + "state" : "Stato", + "city" : "Città", + "address" : "Indirizzo", + "address2" : "Indirizzo 2", + "zip" : "CAP", + "phone" : "Telefono", + "label" : "Etichetta", + "queue-name" : "Nome della coda", + "service-id" : "ID del servizio", + "owner-name" : "Nome del proprietario", + "owner-type" : "Tipo di proprietario" + }, + "entity-view" : { + "entity-view" : "Vista entità", + "entity-view-required" : "Vista entità obbligatoria.", + "entity-views" : "Viste entità", + "management" : "Gestione viste entità", + "view-entity-views" : "Visualizza viste entità", + "entity-view-alias" : "Alias vista entità", + "aliases" : "Alias viste entità", + "no-alias-matching" : "'{{alias}}' non trovato.", + "no-aliases-found" : "Nessun alias trovato.", + "no-key-matching" : "'{{key}}' non trovato.", + "no-keys-found" : "Nessuna chiave trovata.", + "create-new-alias" : "Creane uno nuovo!", + "create-new-key" : "Creane una nuova!", + "duplicate-alias-error" : "Trovato alias duplicato '{{alias}}'.
Gli alias delle viste entità devono essere univoci all'interno della dashboard.", + "configure-alias" : "Configura alias '{{alias}}'", + "no-entity-views-matching" : "Nessuna vista entità corrispondente a '{{entity}}' trovata.", + "public" : "Pubblica", + "alias" : "Alias", + "alias-required" : "Alias vista entità obbligatorio.", + "remove-alias" : "Rimuovi alias vista entità", + "add-alias" : "Aggiungi alias vista entità", + "name-starts-with" : "Espressione nome vista entità", + "help-text" : "Usa '%' se necessario: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list" : "Elenco viste entità", + "use-entity-view-name-filter" : "Usa filtro", + "entity-view-list-empty" : "Nessuna vista entità selezionata.", + "entity-view-name-filter-required" : "Filtro nome vista entità obbligatorio.", + "entity-view-name-filter-no-entity-view-matched" : "Nessuna vista entità che inizia con '{{entityView}}' trovata.", + "add" : "Aggiungi vista entità", + "entity-view-public" : "La vista entità è pubblica", + "assign-to-customer" : "Assegna al cliente", + "assign-entity-view-to-customer" : "Assegna viste entità al cliente", + "assign-entity-view-to-customer-text" : "Seleziona le viste entità da assegnare al cliente", + "no-entity-views-text" : "Nessuna vista entità trovata", + "assign-to-customer-text" : "Seleziona il cliente a cui assegnare le viste entità", + "entity-view-details" : "Dettagli vista entità", + "add-entity-view-text" : "Aggiungi nuova vista entità", + "delete" : "Elimina vista entità", + "assign-entity-views" : "Assegna viste entità", + "assign-entity-views-text" : "Assegna { count, plural, =1 {1 vista entità} other {# viste entità} } al cliente", + "delete-entity-views" : "Elimina viste entità", + "make-public" : "Rendi vista entità pubblica", + "make-private" : "Rendi vista entità privata", + "unassign-from-customer" : "Disassegna dal cliente", + "unassign-entity-views" : "Disassegna viste entità", + "unassign-entity-views-action-title" : "Disassegna { count, plural, =1 {1 vista entità} other {# viste entità} } dal cliente", + "assign-new-entity-view" : "Assegna nuova vista entità", + "delete-entity-view-title" : "Sei sicuro di voler eliminare la vista entità '{{entityViewName}}'?", + "delete-entity-view-text" : "Attenzione, dopo la conferma la vista entità e tutti i dati correlati saranno irreversibili.", + "delete-entity-views-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 vista entità} other {# viste entità} }?", + "delete-entity-views-action-title" : "Elimina { count, plural, =1 {1 vista entità} other {# viste entità} }", + "delete-entity-views-text" : "Attenzione, dopo la conferma tutte le viste entità selezionate saranno eliminate e i relativi dati saranno irreversibili.", + "make-public-entity-view-title" : "Sei sicuro di voler rendere pubblica la vista entità '{{entityViewName}}'?", + "make-public-entity-view-text" : "Dopo la conferma, la vista entità e tutti i suoi dati saranno resi pubblici e accessibili da altri.", + "make-private-entity-view-title" : "Sei sicuro di voler rendere privata la vista entità '{{entityViewName}}'?", + "make-private-entity-view-text" : "Dopo la conferma, la vista entità e tutti i suoi dati saranno resi privati e non accessibili da altri.", + "unassign-entity-view-title" : "Sei sicuro di voler disassegnare la vista entità '{{entityViewName}}'?", + "unassign-entity-view-text" : "Dopo la conferma, la vista entità sarà disassegnata e non accessibile dal cliente.", + "unassign-entity-view" : "Disassegna vista entità", + "unassign-entity-views-title" : "Sei sicuro di voler disassegnare { count, plural, =1 {1 vista entità} other {# viste entità} }?", + "unassign-entity-views-text" : "Dopo la conferma, tutte le viste entità selezionate saranno disassegnate e non saranno accessibili dal cliente.", + "entity-view-type" : "Tipo di vista entità", + "entity-view-type-required" : "Tipo di vista entità obbligatorio.", + "select-entity-view-type" : "Seleziona tipo vista entità", + "enter-entity-view-type" : "Inserisci tipo vista entità", + "any-entity-view" : "Qualsiasi vista entità", + "no-entity-view-types-matching" : "Nessun tipo vista entità corrispondente a '{{entitySubtype}}' trovato.", + "entity-view-type-list-empty" : "Nessun tipo vista entità selezionato.", + "entity-view-types" : "Tipi di vista entità", + "created-time" : "Data di creazione", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio.", + "name-max-length" : "Il nome deve essere inferiore a 256 caratteri", + "type-max-length" : "Il tipo di vista entità deve essere inferiore a 256 caratteri", + "description" : "Descrizione", + "events" : "Eventi", + "details" : "Dettagli", + "copyId" : "Copia ID vista entità", + "idCopiedMessage" : "ID vista entità copiato negli appunti", + "assignedToCustomer" : "Assegnato al cliente", + "unable-entity-view-device-alias-title" : "Impossibile eliminare l'alias della vista entità", + "unable-entity-view-device-alias-text" : "L'alias del dispositivo '{{entityViewAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:
{{widgetsList}}", + "select-entity-view" : "Seleziona vista entità", + "start-ts" : "Ora di inizio", + "end-ts" : "Ora di fine", + "date-limits" : "Limiti di data", + "client-attributes" : "Attributi client", + "shared-attributes" : "Attributi condivisi", + "server-attributes" : "Attributi server", + "timeseries" : "Serie temporali", + "client-attributes-placeholder" : "Attributi client", + "shared-attributes-placeholder" : "Attributi condivisi", + "server-attributes-placeholder" : "Attributi server", + "timeseries-placeholder" : "Serie temporali", + "target-entity" : "Entità di destinazione", + "attributes-propagation" : "Propagazione degli attributi", + "attributes-propagation-hint" : "La vista entità copierà automaticamente gli attributi specificati dall'entità di destinazione ogni volta che viene salvata o aggiornata. Per motivi di prestazioni, gli attributi dell'entità di destinazione non vengono propagati ad ogni modifica. È possibile abilitare la propagazione automatica configurando un nodo 'copy to view' nella catena di regole e collegando i messaggi 'Post attributes' e 'Attributes Updated' a tale nodo.", + "timeseries-data" : "Dati serie temporali", + "timeseries-data-hint" : "Configura le chiavi di dati delle serie temporali dell'entità di destinazione accessibili alla vista entità. Questi dati sono di sola lettura.", + "search" : "Cerca viste entità", + "selected-entity-views" : "{ count, plural, =1 {1 vista entità} other {# viste entità} } selezionate", + "assign-entity-view-to-edge" : "Assegna vista(e) entità all'Edge", + "assign-entity-view-to-edge-text" : "Seleziona le viste entità da assegnare all'edge", + "unassign-entity-view-from-edge-title" : "Sei sicuro di voler disassegnare la vista entità '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text" : "Dopo la conferma, la vista entità verrà disassegnata e non sarà accessibile dall'edge.", + "unassign-entity-views-from-edge-action-title" : "Disassegna { count, plural, =1 {1 vista entità} other {# viste entità} } dall'edge", + "unassign-entity-view-from-edge" : "Disassegna vista entità", + "unassign-entity-views-from-edge-title" : "Sei sicuro di voler disassegnare { count, plural, =1 {1 vista entità} other {# viste entità} }?", + "unassign-entity-views-from-edge-text" : "Dopo la conferma, tutte le viste entità selezionate verranno disassegnate e non saranno più accessibili dall'edge." + }, + "event" : { + "event-type" : "Tipo di evento", + "events-filter" : "Filtro eventi", + "clean-events" : "Cancella eventi", + "type-error" : "Errore", + "type-lc-event" : "Evento di ciclo di vita", + "type-stats" : "Statistiche", + "type-debug-rule-node" : "Debug", + "type-debug-rule-chain" : "Debug", + "type-debug-calculated-field" : "Debug", + "arguments" : "Argomenti", + "result" : "Risultato", + "no-events-prompt" : "Nessun evento trovato", + "error" : "Errore", + "alarm" : "Allarme", + "event-time" : "Ora evento", + "server" : "Server", + "body" : "Corpo", + "method" : "Metodo", + "type" : "Tipo", + "metadata" : "Metadati", + "message" : "Messaggio", + "message-id" : "ID messaggio", + "copy-message-id" : "Copia ID messaggio", + "message-type" : "Tipo messaggio", + "data-type" : "Tipo dati", + "relation-type" : "Tipo relazione", + "data" : "Dati", + "event" : "Evento", + "status" : "Stato", + "success" : "Successo", + "failed" : "Fallito", + "messages-processed" : "Messaggi elaborati", + "max-messages-processed" : "Numero massimo di messaggi elaborati", + "min-messages-processed" : "Numero minimo di messaggi elaborati", + "errors-occurred" : "Errori rilevati", + "max-errors-occurred" : "Numero massimo di errori", + "min-errors-occurred" : "Numero minimo di errori", + "min-value" : "Il valore minimo è 0.", + "all-events" : "Tutti", + "has-error" : "Contiene errori", + "entity-id" : "ID entità", + "copy-entity-id" : "Copia ID entità", + "entity-type" : "Tipo entità", + "clear-filter" : "Cancella filtro", + "clear-request-title" : "Cancella tutti gli eventi", + "clear-request-text" : "Sei sicuro di voler cancellare tutti gli eventi?", + "started" : "Avviato", + "updated" : "Aggiornato", + "stopped" : "Interrotto" + }, + "extension" : { + "extensions" : "Estensioni", + "selected-extensions" : "{ count, plural, =1 {1 estensione} other {# estensioni} } selezionate", + "type" : "Tipo", + "key" : "Chiave", + "value" : "Valore", + "id" : "Id", + "extension-id" : "ID estensione", + "extension-type" : "Tipo estensione", + "transformer-json" : "JSON *", + "unique-id-required" : "L'ID estensione attuale esiste già.", + "delete" : "Elimina estensione", + "add" : "Aggiungi estensione", + "edit" : "Modifica estensione", + "delete-extension-title" : "Sei sicuro di voler eliminare l'estensione '{{extensionId}}'?", + "delete-extension-text" : "Attenzione, dopo la conferma l'estensione e tutti i dati collegati non saranno più recuperabili.", + "delete-extensions-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 estensione} other {# estensioni} }?", + "delete-extensions-text" : "Attenzione, dopo la conferma tutte le estensioni selezionate verranno eliminate.", + "converters" : "Convertitori", + "converter-id" : "ID convertitore", + "configuration" : "Configurazione", + "converter-configurations" : "Configurazioni convertitore", + "token" : "Token di sicurezza", + "add-converter" : "Aggiungi convertitore", + "add-config" : "Aggiungi configurazione convertitore", + "device-name-expression" : "Espressione nome dispositivo", + "device-type-expression" : "Espressione tipo dispositivo", + "custom" : "Personalizzato", + "to-double" : "Converti in double", + "transformer" : "Trasformatore", + "json-required" : "Il JSON del trasformatore è obbligatorio.", + "json-parse" : "Impossibile analizzare il JSON del trasformatore.", + "attributes" : "Attributi", + "add-attribute" : "Aggiungi attributo", + "add-map" : "Aggiungi elemento di mappatura", + "timeseries" : "Serie temporali", + "add-timeseries" : "Aggiungi serie temporale", + "field-required" : "Il campo è obbligatorio", + "brokers" : "Broker", + "add-broker" : "Aggiungi broker", + "host" : "Host", + "port" : "Porta", + "port-range" : "La porta deve essere compresa tra 1 e 65535.", + "ssl" : "Ssl", + "credentials" : "Credenziali", + "username" : "Nome utente", + "password" : "Password", + "retry-interval" : "Intervallo di retry in millisecondi", + "anonymous" : "Anonimo", + "basic" : "Base", + "pem" : "PEM", + "ca-cert" : "File certificato CA *", + "private-key" : "File chiave privata *", + "cert" : "File certificato *", + "no-file" : "Nessun file selezionato.", + "drop-file" : "Trascina un file o fai clic per selezionarlo e caricarlo.", + "mapping" : "Mappatura", + "topic-filter" : "Filtro topic", + "converter-type" : "Tipo convertitore", + "converter-json" : "Json", + "json-name-expression" : "Espressione nome dispositivo JSON", + "topic-name-expression" : "Espressione nome dispositivo topic", + "json-type-expression" : "Espressione tipo dispositivo JSON", + "topic-type-expression" : "Espressione tipo dispositivo topic", + "attribute-key-expression" : "Espressione chiave attributo", + "attr-json-key-expression" : "Espressione chiave attributo JSON", + "attr-topic-key-expression" : "Espressione chiave attributo topic", + "request-id-expression" : "Espressione ID richiesta", + "request-id-json-expression" : "Espressione ID richiesta JSON", + "request-id-topic-expression" : "Espressione ID richiesta topic", + "response-topic-expression" : "Espressione topic risposta", + "value-expression" : "Espressione valore", + "topic" : "Topic", + "timeout" : "Timeout in millisecondi", + "converter-json-required" : "Il convertitore JSON è obbligatorio.", + "converter-json-parse" : "Impossibile analizzare il JSON del convertitore.", + "filter-expression" : "Espressione filtro", + "connect-requests" : "Richieste di connessione", + "add-connect-request" : "Aggiungi richiesta di connessione", + "disconnect-requests" : "Richieste di disconnessione", + "add-disconnect-request" : "Aggiungi richiesta di disconnessione", + "attribute-requests" : "Richieste attributo", + "add-attribute-request" : "Aggiungi richiesta attributo", + "attribute-updates" : "Aggiornamenti attributo", + "add-attribute-update" : "Aggiungi aggiornamento attributo", + "server-side-rpc" : "RPC lato server", + "add-server-side-rpc-request" : "Aggiungi richiesta RPC lato server", + "device-name-filter" : "Filtro nome dispositivo", + "attribute-filter" : "Filtro attributo", + "method-filter" : "Filtro metodo", + "request-topic-expression" : "Espressione topic richiesta", + "response-timeout" : "Timeout di risposta in millisecondi", + "topic-expression" : "Espressione topic", + "client-scope" : "Ambito client", + "add-device" : "Aggiungi dispositivo", + "opc-server" : "Server", + "opc-add-server" : "Aggiungi server", + "opc-add-server-prompt" : "Aggiungi server", + "opc-application-name" : "Nome applicazione", + "opc-application-uri" : "URI applicazione", + "opc-scan-period-in-seconds" : "Periodo di scansione in secondi", + "opc-security" : "Sicurezza", + "opc-identity" : "Identità", + "opc-keystore" : "Keystore", + "opc-type" : "Tipo", + "opc-keystore-type" : "Tipo", + "opc-keystore-location" : "Percorso *", + "opc-keystore-password" : "Password", + "opc-keystore-alias" : "Alias", + "opc-keystore-key-password" : "Password chiave", + "opc-device-node-pattern" : "Pattern nodo dispositivo", + "opc-device-name-pattern" : "Pattern nome dispositivo", + "modbus-server" : "Server/slave", + "modbus-add-server" : "Aggiungi server/slave", + "modbus-add-server-prompt" : "Aggiungi server/slave", + "modbus-transport" : "Trasporto", + "modbus-tcp-reconnect" : "Riconnessione automatica", + "modbus-rtu-over-tcp" : "RTU su TCP", + "modbus-port-name" : "Nome porta seriale", + "modbus-encoding" : "Codifica", + "modbus-parity" : "Parità", + "modbus-baudrate" : "Velocità di trasmissione", + "modbus-databits" : "Bit di dati", + "modbus-stopbits" : "Bit di stop", + "modbus-databits-range" : "I bit di dati devono essere compresi tra 7 e 8.", + "modbus-stopbits-range" : "I bit di stop devono essere compresi tra 1 e 2.", + "modbus-unit-id" : "ID unità", + "modbus-unit-id-range" : "L'ID unità deve essere compreso tra 1 e 247.", + "modbus-device-name" : "Nome dispositivo", + "modbus-poll-period" : "Periodo polling (ms)", + "modbus-attributes-poll-period" : "Periodo polling attributi (ms)", + "modbus-timeseries-poll-period" : "Periodo polling serie temporali (ms)", + "modbus-poll-period-range" : "Il periodo di polling deve essere un valore positivo.", + "modbus-tag" : "Tag", + "modbus-function" : "Funzione", + "modbus-register-address" : "Indirizzo registro", + "modbus-register-address-range" : "L'indirizzo del registro deve essere compreso tra 0 e 65535.", + "modbus-register-bit-index" : "Indice bit", + "modbus-register-bit-index-range" : "L'indice bit deve essere compreso tra 0 e 15.", + "modbus-register-count" : "Conteggio registri", + "modbus-register-count-range" : "Il conteggio dei registri deve essere un valore positivo.", + "modbus-byte-order" : "Ordine byte", + "sync" : { + "status" : "Stato", + "sync" : "Sincronizza", + "not-sync" : "Non sincronizzato", + "last-sync-time" : "Ultimo tempo di sincronizzazione", + "not-available" : "Non disponibile" + }, + "export-extensions-configuration" : "Esporta configurazione estensioni", + "import-extensions-configuration" : "Importa configurazione estensioni", + "import-extensions" : "Importa estensioni", + "import-extension" : "Importa estensione", + "export-extension" : "Esporta estensione", + "file" : "File estensioni", + "invalid-file-error" : "File estensione non valido" + }, + "feature" : { + "advanced-features" : "Funzionalità avanzate" + }, + "filter" : { + "add" : "Aggiungi filtro", + "edit" : "Modifica filtro", + "name" : "Nome filtro", + "name-required" : "Il nome del filtro è obbligatorio.", + "duplicate-filter" : "Esiste già un filtro con lo stesso nome.", + "filters" : "Filtri", + "unable-delete-filter-title" : "Impossibile eliminare il filtro", + "unable-delete-filter-text" : "Il filtro '{{filter}}' non può essere eliminato poiché è utilizzato dai seguenti widget:
{{widgetsList}}", + "duplicate-filter-error" : "Filtro duplicato trovato '{{filter}}'.
I filtri devono essere univoci all'interno della dashboard.", + "missing-key-filters-error" : "I filtri chiave sono mancanti per il filtro '{{filter}}'.", + "filter" : "Filtro", + "editable" : "Modificabile", + "no-filters-found" : "Nessun filtro trovato.", + "no-filter-text" : "Nessun filtro specificato", + "add-filter-prompt" : "Aggiungi un filtro", + "no-filter-matching" : "'{{filter}}' non trovato.", + "create-new-filter" : "Creane uno nuovo!", + "create-new" : "Crea nuovo", + "filter-required" : "Il filtro è obbligatorio.", + "operation" : { + "operation" : "Operazione", + "equal" : "uguale", + "not-equal" : "diverso", + "starts-with" : "inizia con", + "ends-with" : "termina con", + "contains" : "contiene", + "not-contains" : "non contiene", + "greater" : "maggiore di", + "less" : "minore di", + "greater-or-equal" : "maggiore o uguale", + "less-or-equal" : "minore o uguale", + "and" : "e", + "or" : "o", + "in" : "in", + "not-in" : "non in" + }, + "ignore-case" : "ignora maiuscole/minuscole", + "value" : "Valore", + "remove-filter" : "Rimuovi filtro", + "duplicate-filter-action" : "Duplica filtro", + "preview" : "Anteprima filtro", + "no-filters" : "Nessun filtro configurato", + "add-filter" : "Aggiungi filtro", + "add-complex-filter" : "Aggiungi filtro complesso", + "add-complex" : "Aggiungi complesso", + "complex-filter" : "Filtro complesso", + "edit-complex-filter" : "Modifica filtro complesso", + "edit-filter-user-params" : "Modifica parametri utente predicato filtro", + "filter-user-params" : "Parametri utente predicato filtro", + "user-parameters" : "Parametri utente", + "display-label" : "Etichetta da visualizzare", + "order-priority" : "Priorità di ordinamento del campo", + "key-filter" : "Filtro chiave", + "key-filters" : "Filtri chiave", + "key-name" : "Nome chiave", + "key-name-required" : "Il nome chiave è obbligatorio.", + "key-type" : { + "key-type" : "Tipo di chiave", + "attribute" : "Attributo", + "timeseries" : "Serie temporale", + "entity-field" : "Campo entità", + "constant" : "Costante", + "client-attribute" : "Attributo client", + "server-attribute" : "Attributo server", + "shared-attribute" : "Attributo condiviso" + }, + "value-type" : { + "value-type" : "Tipo di valore", + "string" : "Stringa", + "numeric" : "Numerico", + "boolean" : "Booleano", + "date-time" : "Data/Ora" + }, + "value-type-required" : "Il tipo di valore della chiave è obbligatorio.", + "key-value-type-change-title" : "Sei sicuro di voler cambiare il tipo di valore della chiave?", + "key-value-type-change-message" : "Se confermi il nuovo tipo di valore, tutti i filtri chiave inseriti verranno rimossi.", + "no-key-filters" : "Nessun filtro chiave configurato", + "add-key-filter" : "Aggiungi filtro chiave", + "remove-key-filter" : "Rimuovi filtro chiave", + "edit-key-filter" : "Modifica filtro chiave", + "date" : "Data", + "time" : "Ora", + "current-tenant" : "Tenant corrente", + "current-customer" : "Cliente corrente", + "current-user" : "Utente corrente", + "current-device" : "Dispositivo corrente", + "default-value" : "Valore predefinito", + "default-comma-separated-values" : "Valori predefiniti separati da virgola", + "dynamic-source-type" : "Tipo di sorgente dinamica", + "dynamic-value" : "Valore dinamico", + "no-dynamic-value" : "Nessun valore dinamico", + "source-attribute" : "Attributo sorgente", + "switch-to-dynamic-value" : "Passa al valore dinamico", + "switch-to-default-value" : "Passa al valore predefinito", + "inherit-owner" : "Eredita dal proprietario", + "source-attribute-not-set" : "Se l'attributo sorgente non è impostato" + }, + "fullscreen" : { + "expand" : "Espandi a schermo intero", + "exit" : "Esci da schermo intero", + "toggle" : "Attiva/disattiva modalità schermo intero", + "fullscreen" : "Schermo intero" + }, + "function" : { + "function" : "Funzione" + }, + "gateway" : { + "gateway-name" : "Nome gateway", + "gateway-name-required" : "Il nome del gateway è obbligatorio.", + "gateways" : "Gateway", + "create-new-gateway" : "Crea un nuovo gateway", + "create-new-gateway-text" : "Sei sicuro di voler creare un nuovo gateway con il nome: '{{gatewayName}}'?", + "launch-command" : "Comando di avvio", + "no-gateway-found" : "Nessun gateway trovato.", + "no-gateway-matching" : "'{{item}}' non trovato." + }, + "grid" : { + "delete-item-title" : "Sei sicuro di voler eliminare questo elemento?", + "delete-item-text" : "Attenzione, dopo la conferma questo elemento e tutti i dati correlati non saranno recuperabili.", + "delete-items-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 elemento} other {# elementi} }?", + "delete-items-action-title" : "Elimina { count, plural, =1 {1 elemento} other {# elementi} }", + "delete-items-text" : "Attenzione, dopo la conferma tutti gli elementi selezionati saranno rimossi e i dati correlati non saranno recuperabili.", + "add-item-text" : "Aggiungi nuovo elemento", + "no-items-text" : "Nessun elemento trovato", + "item-details" : "Dettagli elemento", + "delete-item" : "Elimina elemento", + "delete-items" : "Elimina elementi", + "scroll-to-top" : "Scorri verso l'alto" + }, + "help" : { + "goto-help-page" : "Vai alla pagina di aiuto", + "show-help" : "Mostra aiuto" + }, + "home" : { + "home" : "Home", + "profile" : "Profilo", + "logout" : "Esci", + "menu" : "Menu", + "avatar" : "Avatar", + "open-user-menu" : "Apri menu utente" + }, + "file-input" : { + "browse-file" : "Sfoglia file", + "browse-files" : "Sfoglia file" + }, + "image" : { + "gallery" : "Galleria immagini", + "search" : "Cerca immagine", + "selected-images" : "{ count, plural, =1 {1 immagine} other {# immagini} } selezionate", + "created-time" : "Data di creazione", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio.", + "resolution" : "Risoluzione", + "size" : "Dimensione", + "system" : "Sistema", + "download-image" : "Scarica immagine", + "export-image" : "Esporta immagine in JSON", + "import-image" : "Importa immagine da JSON", + "upload-image" : "Carica immagine", + "edit-image" : "Modifica immagine", + "image-details" : "Dettagli immagine", + "no-images" : "Nessuna immagine trovata", + "delete-image" : "Elimina immagine", + "delete-image-title" : "Sei sicuro di voler eliminare l'immagine '{{imageTitle}}'?", + "delete-image-text" : "Attenzione, dopo la conferma l'immagine non sarà più recuperabile.", + "delete-images-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 immagine} other {# immagini} }?", + "delete-images-text" : "Attenzione, dopo la conferma tutte le immagini selezionate saranno rimosse e i dati correlati non saranno più recuperabili.", + "list-mode" : "Vista elenco", + "grid-mode" : "Vista griglia", + "image-preview" : "Anteprima immagine", + "update-image" : "Aggiorna immagine", + "export-failed-error" : "Impossibile esportare l'immagine: {{error}}", + "image-json-file" : "File JSON immagine", + "invalid-image-json-file-error" : "Impossibile importare immagine da JSON: struttura dati JSON immagine non valida.", + "image-is-in-use" : "L'immagine è utilizzata da altre entità", + "images-are-in-use" : "Le immagini sono utilizzate da altre entità", + "image-is-in-use-text" : "L'immagine '{{title}}' non è stata eliminata perché è utilizzata dalle seguenti entità:", + "images-are-in-use-text" : "Non tutte le immagini sono state eliminate perché sono utilizzate da altre entità.
Puoi visualizzare le entità di riferimento cliccando sul pulsante Riferimenti nella riga corrispondente.
Se desideri comunque eliminarle, selezionale nella tabella sottostante e clicca su Elimina selezionate.", + "delete-image-in-use-text" : "Se desideri comunque eliminare l'immagine, clicca su Elimina comunque.", + "system-entities" : "Entità di sistema:", + "entities" : "entità:", + "references" : "Riferimenti", + "include-system-images" : "Includi immagini di sistema", + "clear-image" : "Rimuovi immagine", + "no-image" : "Nessuna immagine", + "no-image-selected" : "Nessuna immagine selezionata", + "browse-from-gallery" : "Sfoglia dalla galleria", + "set-link" : "Imposta collegamento", + "image-link" : "Link immagine", + "link" : "Link", + "copy-image-link" : "Copia link immagine", + "embed-image" : "Incorpora immagine", + "embed-to-html" : "Incorpora in HTML", + "embed-to-html-hint" : "Questa funzione renderà il link disponibile a qualsiasi utente non autorizzato.", + "embed-to-html-text" : "Utilizzando il seguente frammento di codice, puoi incorporare un'immagine nei componenti basati su HTML semplice.
Tali componenti includono i widget di tipo card HTML, funzioni di contenuto cella, ecc.", + "embed-to-angular-template" : "Incorpora in modello HTML Angular", + "embed-to-angular-template-text" : "Utilizzando il seguente frammento di codice, puoi incorporare un'immagine nel modello HTML Angular che sarà usato per i componenti.
Tali componenti includono il widget Markdown, la sezione HTML nell'editor widget, azioni personalizzate, ecc." + }, + "image-input" : { + "drop-images-or" : "Trascina le immagini o", + "drag-and-drop" : "Trascina e rilascia", + "or" : "oppure", + "browse" : "Sfoglia", + "no-images" : "Nessuna immagine selezionata", + "images" : "immagini" + }, + "import" : { + "no-file" : "Nessun file selezionato", + "drop-file" : "Trascina un file JSON o clicca per selezionarlo e caricarlo.", + "drop-json-file-or" : "Trascina un file JSON o", + "drop-file-csv" : "Trascina un file CSV o clicca per selezionarlo e caricarlo.", + "drop-file-csv-or" : "Trascina un file CSV o", + "column-value" : "Valore", + "column-title" : "Titolo", + "column-example" : "Esempio di valore", + "column-key" : "Chiave attributo/telemetria", + "credentials" : "Credenziali", + "csv-delimiter" : "Delimitatore CSV", + "csv-first-line-header" : "La prima riga contiene i nomi delle colonne", + "csv-update-data" : "Aggiorna attributi/telemetria", + "details" : "Dettagli", + "import-csv-number-columns-error" : "Il file deve contenere almeno due colonne", + "import-csv-invalid-format-error" : "Formato file non valido. Riga: '{{line}}'", + "column-type" : { + "name" : "Nome", + "type" : "Tipo", + "label" : "Etichetta", + "column-type" : "Tipo colonna", + "client-attribute" : "Attributo client", + "shared-attribute" : "Attributo condiviso", + "server-attribute" : "Attributo server", + "timeseries" : "Serie temporali", + "entity-field" : "Campo entità", + "access-token" : "Token di accesso", + "x509" : "X.509", + "mqtt" : { + "client-id" : "ID client MQTT", + "user-name" : "Nome utente MQTT", + "password" : "Password MQTT" + }, + "lwm2m" : { + "client-endpoint" : "Nome endpoint client LwM2M", + "security-config-mode" : "Modalità configurazione sicurezza LwM2M", + "client-identity" : "Identità client LwM2M", + "client-key" : "Chiave client LwM2M", + "client-cert" : "Chiave pubblica client LwM2M", + "bootstrap-server-security-mode" : "Modalità di sicurezza server bootstrap LwM2M", + "bootstrap-server-secret-key" : "Chiave segreta server bootstrap LwM2M", + "bootstrap-server-public-key-id" : "Chiave pubblica o ID server bootstrap LwM2M", + "lwm2m-server-security-mode" : "Modalità di sicurezza server LwM2M", + "lwm2m-server-secret-key" : "Chiave segreta server LwM2M", + "lwm2m-server-public-key-id" : "Chiave pubblica o ID server LwM2M" + }, + "snmp" : { + "host" : "Host SNMP", + "port" : "Porta SNMP", + "version" : "Versione SNMP (v1, v2c o v3)", + "community-string" : "Stringa community SNMP" + }, + "isgateway" : "È un gateway", + "activity-time-from-gateway-device" : "Orario attività dal dispositivo gateway", + "description" : "Descrizione", + "routing-key" : "Chiave Edge", + "secret" : "Segreto Edge" + }, + "stepper-text" : { + "select-file" : "Seleziona un file", + "configuration" : "Configurazione importazione", + "column-type" : "Seleziona tipo colonne", + "creat-entities" : "Creazione nuove entità" + }, + "message" : { + "create-entities" : "{{count}} nuove entità create con successo.", + "update-entities" : "{{count}} entità aggiornate con successo.", + "error-entities" : "Errore durante la creazione di {{count}} entità." + } + }, + "scada" : { + "symbols" : "Simboli SCADA", + "search" : "Cerca simbolo", + "selected-symbols" : "{ count, plural, =1 {1 simbolo} other {# simboli} } selezionato/i", + "download-symbol" : "Scarica simbolo SCADA", + "export-symbol" : "Esporta simbolo SCADA in JSON", + "import-symbol" : "Importa simbolo SCADA da JSON", + "upload-symbol" : "Carica simbolo SCADA", + "update-symbol" : "Aggiorna simbolo SCADA", + "edit-symbol" : "Modifica simbolo SCADA", + "symbol-details" : "Dettagli del simbolo SCADA", + "mode-svg" : "SVG", + "mode-xml" : "XML", + "no-symbols" : "Nessun simbolo trovato", + "show-hidden-elements" : "Mostra elementi nascosti", + "hide-hidden-elements" : "Nascondi elementi nascosti", + "delete-symbol" : "Elimina simbolo SCADA", + "delete-symbol-title" : "Sei sicuro di voler eliminare il simbolo SCADA '{{imageTitle}}'?", + "delete-symbol-text" : "Attenzione, dopo la conferma il simbolo SCADA non sarà recuperabile.", + "delete-symbols-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 simbolo SCADA} other {# simboli SCADA} }?", + "delete-symbols-text" : "Attenzione, dopo la conferma tutti i simboli SCADA selezionati verranno rimossi e i dati correlati non saranno recuperabili.", + "include-system-symbols" : "Includi simboli di sistema", + "symbol-preview" : "Anteprima simbolo", + "general" : "Generale", + "tags" : "Tag", + "properties" : "Proprietà", + "title" : "Titolo", + "description" : "Descrizione", + "search-tags" : "Cerca tag", + "widget-size" : "Dimensione widget", + "cols" : "colonne", + "rows" : "righe", + "state-render-function" : "Funzione di rendering dello stato", + "preview" : "Anteprima", + "preview-widget-action-text" : "Azione widget '{{type}}' eseguita con successo!", + "no-symbol" : "Nessun simbolo SCADA", + "no-symbol-selected" : "Nessun simbolo SCADA selezionato", + "clear-symbol" : "Cancella simbolo SCADA", + "browse-symbol-from-gallery" : "Sfoglia simbolo SCADA dalla galleria", + "zoom-in" : "Ingrandisci", + "zoom-out" : "Riduci", + "create-widget" : "Crea widget", + "create-widget-from-symbol" : "Crea widget dal simbolo SCADA", + "hidden" : "nascosto", + "tag" : { + "tag" : "Tag", + "on-click-action" : "Azione al clic", + "no-tags" : "Nessun tag configurato", + "delete-tag-text" : "Sei sicuro di voler eliminare il tag
{{tag}} dall'elemento {{elementType}}?", + "update-tag" : "Aggiorna tag", + "enter-tag" : "Inserisci tag", + "tag-settings" : "Impostazioni tag", + "remove-tag" : "Rimuovi tag", + "add-tag" : "Aggiungi tag" + }, + "behavior" : { + "behavior" : "Comportamento", + "id" : "Id", + "name" : "Nome", + "type" : "Tipo", + "no-behaviors" : "Nessun comportamento configurato", + "add-behavior" : "Aggiungi comportamento", + "type-action" : "Azione", + "type-value" : "Valore", + "type-widget-action" : "Azione widget", + "behavior-settings" : "Impostazioni comportamento", + "remove-behavior" : "Rimuovi comportamento", + "hint" : "Suggerimento", + "group-title" : "Titolo del gruppo", + "value-type" : "Tipo di valore", + "default-value" : "Valore predefinito", + "true-label" : "Etichetta vero", + "false-label" : "Etichetta falso", + "state-label" : "Etichetta stato", + "default-payload" : "Payload predefinito", + "not-unique-behavior-ids-error" : "Gli ID dei comportamenti devono essere univoci!", + "default-settings" : "Impostazioni predefinite" + }, + "symbol" : { + "symbol" : "Simbolo SCADA", + "fluid-presence" : "Presenza di fluido", + "fluid-presence-hint" : "Indica se è presente fluido nel tubo.", + "fluid-present" : "Fluido presente", + "present" : "Presente", + "absent" : "Assente", + "flow-presence" : "Presenza di flusso", + "flow-presence-hint" : "Indica se il fluido scorre nel tubo.", + "flow-present" : "Flusso presente", + "flow-direction" : "Direzione del flusso", + "flow-direction-hint" : "Indica la direzione del flusso del fluido.", + "forward" : "Avanti", + "reverse" : "Indietro", + "flow-animation-speed" : "Velocità animazione del flusso", + "flow-animation-speed-hint" : "Valore decimale che indica la velocità dell'animazione del flusso. 1 - velocità normale, 0 - nessuna animazione, < 1 - animazione più lenta, > 1 - animazione più veloce.", + "leak" : "Perdita", + "leak-hint" : "Indica se è presente una perdita nel tubo.", + "leak-present" : "Perdita presente", + "fluid-color" : "Colore del fluido", + "pipe-color" : "Colore del tubo", + "horizontal-pipe" : "Tubo orizzontale", + "vertical-pipe" : "Tubo verticale", + "horizontal-fluid-color" : "Colore del fluido orizzontale", + "vertical-fluid-color" : "Colore del fluido verticale", + "left-pipe" : "Tubo sinistro", + "right-pipe" : "Tubo destro", + "top-pipe" : "Tubo superiore", + "bottom-pipe" : "Tubo inferiore", + "left-fluid-color" : "Colore fluido sinistro", + "right-fluid-color" : "Colore fluido destro", + "top-fluid-color" : "Colore fluido superiore", + "bottom-fluid-color" : "Colore fluido inferiore", + "display" : "Display", + "display-format" : "Formato del display", + "value" : "Valore", + "decimals" : "Decimali", + "units" : "Unità", + "flow-meter-value-hint" : "Valore decimale visualizzato sul flussometro", + "value-hint" : "Valore decimale che indica il valore attuale", + "running" : "In funzione", + "running-hint" : "Indica se il componente è in funzione.", + "warning-state" : "Stato di avviso", + "warning" : "Avviso", + "warning-click" : "Clic avviso", + "warning-state-hint" : "Indica se il componente è in stato di avviso.", + "critical-state" : "Stato critico", + "critical" : "Critico", + "critical-click" : "Clic critico", + "critical-state-hint" : "Indica se il componente è in stato critico.", + "critical-state-animation" : "Animazione stato critico", + "critical-state-animation-hint" : "Attiva animazione lampeggiante quando il componente è in stato critico.", + "warning-critical-state-animation" : "Animazione stato avviso/critico", + "warning-critical-state-animation-hint" : "Attiva animazione lampeggiante quando il componente è in stato di avviso o critico.", + "animation" : "Animazione", + "broken" : "Guasto", + "broken-hint" : "Indica se il componente è guasto.", + "on-display-click" : "Al clic sul display", + "on-display-click-hint" : "Azione eseguita quando l'utente clicca sul display.", + "pipe" : "Tubo", + "default-border-color" : "Colore bordo predefinito", + "active-border-color" : "Colore bordo attivo", + "warning-border-color" : "Colore bordo di avviso", + "critical-border-color" : "Colore bordo critico", + "background-color" : "Colore di sfondo", + "rotation-animation-speed" : "Velocità animazione rotazione", + "rotation-animation-speed-hint" : "Valore decimale che indica la velocità di rotazione. 1 - velocità normale, 0 - nessuna animazione, < 1 - animazione più lenta, > 1 - animazione più veloce.", + "on-click" : "Al clic", + "on-click-hint" : "Azione eseguita quando l'utente clicca sul componente.", + "connectors-positions" : "Posizioni connettori", + "right-connector" : "Connettore destro", + "right-top-connector" : "Connettore in alto a destra", + "right-bottom-connector" : "Connettore in basso a destra", + "left-connector" : "Connettore sinistro", + "left-top-connector" : "Connettore in alto a sinistra", + "left-bottom-connector" : "Connettore in basso a sinistra", + "top-left-connector" : "Connettore in alto a sinistra", + "top-right-connector" : "Connettore in alto a destra", + "top-connector" : "Connettore superiore", + "bottom-connector" : "Connettore inferiore", + "running-color" : "Colore funzionamento", + "stopped-color" : "Colore arresto", + "stopped" : "Fermo", + "warning-color" : "Colore avviso", + "critical-color" : "Colore critico", + "opened" : "Aperto", + "opened-hint" : "Indica se il componente è aperto.", + "open" : "Apri", + "open-hint" : "Azione eseguita quando l'utente clicca per aprire il componente.", + "close" : "Chiudi", + "close-hint" : "Azione eseguita quando l'utente clicca per chiudere il componente.", + "close-state-animation" : "Animazione stato chiuso", + "close-state-animation-hint" : "Attiva animazione lampeggiante quando il componente è in stato chiuso.", + "opened-color" : "Colore aperto", + "closed-color" : "Colore chiuso", + "opened-rotation-angle" : "Angolo di rotazione aperto", + "closed-rotation-angle" : "Angolo di rotazione chiuso", + "tank-capacity" : "Capacità del serbatoio", + "tank-capacity-hint" : "Valore decimale che indica la capacità totale del serbatoio.", + "current-volume" : "Volume attuale", + "current-volume-hint" : "Valore decimale che indica il volume attualmente occupato.", + "tank-color" : "Colore del serbatoio", + "value-box" : "Riquadro del valore", + "value-text" : "Testo del valore", + "scale" : "Scala", + "transparent-mode" : "Modalità trasparente", + "major-ticks" : "Graduazioni principali", + "intervals" : "Intervalli", + "major-ticks-color" : "Colore graduazioni principali", + "normal" : "Normale", + "minor-ticks" : "Graduazioni minori", + "minor-ticks-color" : "Colore graduazioni minori", + "temperature" : "Temperatura", + "temperature-hint" : "Valore decimale che indica la temperatura attuale.", + "update-temperature" : "Aggiorna temperatura", + "update-temperature-hint" : "Azione eseguita quando l'utente clicca per cambiare la temperatura attuale.", + "run" : "Avvia", + "run-hint" : "Azione eseguita quando l'utente clicca per avviare il componente.", + "stop" : "Ferma", + "stop-hint" : "Azione eseguita quando l'utente clicca per fermare il componente.", + "temperature-step" : "Incremento della temperatura", + "heat-pump-color" : "Colore della pompa di calore", + "power-button-background" : "Sfondo del pulsante di accensione", + "value-box-background" : "Sfondo del riquadro del valore", + "value-units" : "Unità di valore", + "filtration-mode" : "Modalità di filtrazione", + "filtration-mode-hint" : "Valore intero che indica la modalità di filtrazione attuale.", + "filtration-mode-update" : "Stato aggiornamento modalità di filtrazione", + "filtration-mode-update-hint" : "Azione eseguita quando l'utente clicca per cambiare la modalità di filtrazione attuale.", + "filter-mode" : "Filtro", + "waste-mode" : "Scarico", + "backwash-mode" : "Controlavaggio", + "recirculate-mode" : "Ricircolo", + "rinse-mode" : "Risciacquo", + "closed-mode" : "Chiuso", + "sand-filter-color" : "Colore filtro a sabbia", + "mode-box-background" : "Sfondo del box modalità", + "border-color" : "Colore del bordo", + "label-color" : "Colore dell'etichetta", + "water-leak-hint" : "Indica se è presente una perdita.", + "default-color" : "Colore predefinito", + "leak-color" : "Colore perdita", + "full-value" : "Valore massimo", + "full-value-hint" : "Valore decimale che indica il valore massimo.", + "label" : "Etichetta", + "icon" : "Icona", + "button-color" : "Colore del pulsante", + "on-label" : "Testo etichetta 'On'", + "off-label" : "Testo etichetta 'Off'", + "arrow-presence" : "Presenza freccia", + "arrow-presence-hint" : "Indica se è presente una freccia nel connettore.", + "arrow-present" : "Freccia presente", + "arrow-direction" : "Direzione freccia/animazione", + "arrow-direction-hint" : "Indica la direzione del flusso.", + "flow-animation" : "Animazione del flusso", + "flow-animation-hint" : "Indica se è presente un'animazione nel connettore.", + "flow" : "Flusso", + "flow-line" : "Linea", + "flow-line-style" : "Stile della linea", + "flow-style-hint" : "Imposta i valori Dash e Gap in modo che la loro somma sia divisibile per 100 senza resto per una perfetta sincronizzazione dell'animazione.", + "flow-dash-cap" : "Termine tratteggio flusso", + "dash-cap-butt" : "Piato", + "dash-cap-round" : "Arrotondato", + "dash-cap-square" : "Quadrato", + "dash" : "Tratteggio", + "gap" : "Spazio", + "main-line" : "Linea principale", + "line" : "Linea", + "line-color" : "Colore della linea", + "arrow-color" : "Colore della freccia", + "target-value" : "Valore obiettivo", + "target-value-hint" : "Indica il punto target sulla scala.", + "min-max-value" : "Valore minimo e massimo", + "min-value" : "Min", + "max-value" : "Max", + "progress-bar" : "Barra di progresso", + "progress-arrow" : "Freccia di progresso", + "warning-scale-color" : "Colore scala di avviso", + "critical-scale-color" : "Colore scala critica", + "scale-color" : "Colore della scala", + "target" : "Obiettivo", + "high-warning-state" : "Stato di avviso alto", + "show-high-warning-scale" : "Mostra scala di avviso alto", + "high-warning-scale" : "Scala di avviso alto", + "high-warning-state-hint" : "Valore decimale che indica l'intervallo di avviso alto fino a valore critico alto o massimo.", + "low-warning-state" : "Stato di avviso basso", + "show-low-warning-scale" : "Mostra scala di avviso basso", + "low-warning-scale" : "Scala di avviso basso", + "low-warning-state-hint" : "Valore decimale che indica l'intervallo di avviso basso fino a valore critico basso o minimo.", + "high-critical-state" : "Stato critico alto", + "show-high-critical-scale" : "Mostra scala critica alta", + "high-critical-scale" : "Scala critica alta", + "high-critical-state-hint" : "Valore decimale che indica l'intervallo critico alto fino al valore massimo della scala.", + "low-critical-state" : "Stato critico basso", + "show-low-critical-scale" : "Mostra scala critica bassa", + "low-critical-scale" : "Scala critica bassa", + "low-critical-state-hint" : "Valore decimale che indica un intervallo critico basso fino al valore minimo della scala.", + "filter-color" : "Colore del filtro", + "colors" : "Colori", + "indicator-colors" : "Colori indicatori", + "enabled" : "Abilitato", + "disabled" : "Disabilitato", + "on" : "ON", + "off" : "OFF", + "on-off-state" : "Stato On/Off", + "on-off-state-hint" : "Indica se il componente è nello stato On o Off.", + "on-update-state" : "Aggiorna stato su On", + "on-update-state-hint" : "Azione eseguita quando l'utente clicca per aggiornare lo stato a On.", + "off-update-state" : "Aggiorna stato su Off", + "off-update-state-hint" : "Azione eseguita quando l'utente clicca per aggiornare lo stato a Off.", + "voltage" : "Tensione", + "input-voltage" : "Tensione in ingresso", + "input-voltage-hint" : "Valore decimale che indica la tensione in ingresso.", + "output-voltage" : "Tensione in uscita", + "output-voltage-hint" : "Valore decimale che indica la tensione in uscita.", + "first-phase-voltage" : "Tensione prima fase", + "second-phase-voltage" : "Tensione seconda fase", + "third-phase-voltage" : "Tensione terza fase", + "phase-voltage-hint" : "Valore decimale che indica la tensione della fase corrente", + "voltage-hint" : "Valore decimale che indica la tensione corrente", + "current-voltage-color" : "Colore della tensione attuale", + "phase-indicator-color" : "Colore indicatore di fase", + "measured" : "Misurato", + "measured-hint" : "Valore decimale che indica l'energia consumata in kilowattora", + "day-rate" : "Tariffa diurna", + "night-rate" : "Tariffa notturna", + "off-peak-rate" : "Tariffa fuori punta", + "peak-rate" : "Tariffa di picco", + "export-rate" : "Tariffa di esportazione", + "operating-mode" : "Modalità operativa", + "bypass-mode" : "Bypass", + "operating-mode-hint" : "Valore intero che indica la modalità operativa attuale (0 - OFF, 1 - ON, 2 - BYPASS)", + "connected" : "Connesso", + "connected-hint" : "Indica se il componente è nello stato connesso.", + "disconnected" : "Disconnesso", + "indicator" : "Indicatore", + "operation-mode" : "Modalità di funzionamento", + "operation-mode-hint" : "Indica se l'inverter è in modalità Rete o Inverter.", + "operation-mode-indicators-color" : "Colore degli indicatori della modalità di funzionamento", + "mains-on-mode" : "Rete attiva", + "inverter-on-mode" : "Inverter attivo", + "charging-mode" : "Modalità di carica", + "charging-mode-hint" : "Valore intero che indica la modalità di carica attuale (1 - Bulk, 2 - Assorbimento, 3 - Mantenimento)", + "charging-mode-indicators-color" : "Colore degli indicatori della modalità di carica", + "inverter-faults" : "Guasti", + "inverter-fault-indicators-color" : "Colore degli indicatori di guasto", + "overload-fault" : "Sovraccarico", + "overload-fault-hint" : "Indica se l'inverter è in condizione di sovraccarico.", + "low-battery-fault" : "Batteria scarica", + "low-battery-fault-hint" : "Indica se la batteria è eccessivamente scarica.", + "temperature-fault" : "Temperatura", + "temperature-fault-hint" : "Indica se c'è alta temperatura nell'inverter.", + "triangle" : "Triangolo", + "socket" : "Presa", + "left-button" : "Pulsante sinistro", + "right-button" : "Pulsante destro", + "alarm-colors" : "Colori allarme", + "hook-color" : "Colore gancio" + } + }, + "item" : { + "selected" : "Selezionato" + }, + "js-func" : { + "no-return-error" : "La funzione deve restituire un valore!", + "return-type-mismatch" : "La funzione deve restituire un valore di tipo '{{type}}'!", + "tidy" : "Pulito", + "mini" : "Mini", + "modules" : "Moduli", + "remove-module" : "Rimuovi modulo", + "no-modules" : "Nessun modulo configurato", + "add-module" : "Aggiungi modulo", + "module-alias" : "Alias", + "invalid-module-alias-name" : "Nome alias non valido", + "module-resource" : "Risorsa modulo JS", + "not-unique-module-aliases-error" : "Gli alias dei moduli devono essere unici!", + "show-module-info" : "Mostra informazioni del modulo", + "show-module-source-code" : "Mostra codice sorgente del modulo", + "module-members" : "Membri del modulo", + "module-no-members" : "Il modulo non ha membri esportati", + "module-load-error" : "Errore durante il caricamento del modulo", + "source-code" : "Codice sorgente", + "source-code-load-error" : "Errore durante il caricamento del codice sorgente", + "no-js-module-text" : "Nessun modulo JS trovato", + "no-js-module-matching" : "Nessun modulo JS corrispondente a '{{module}}' trovato." + }, + "key-val" : { + "key" : "Chiave", + "value" : "Valore", + "remove-entry" : "Rimuovi voce", + "add-entry" : "Aggiungi voce", + "no-data" : "Nessuna voce" + }, + "layout" : { + "layout" : "Layout", + "layouts" : "Layout", + "manage" : "Gestisci layout", + "settings" : "Impostazioni layout", + "color" : "Colore", + "main" : "Principale", + "right" : "Destra", + "left" : "Sinistra", + "select" : "Seleziona layout di destinazione", + "percentage-width" : "Larghezza percentuale (%)", + "fixed-width" : "Larghezza fissa (px)", + "left-width" : "Colonna sinistra (%)", + "right-width" : "Colonna destra (%)", + "pick-fixed-side" : "Lato fisso: ", + "layout-fixed-width" : "Larghezza fissa (px)", + "value-min-error" : "Il valore deve essere maggiore di {{min}}{{unit}}", + "value-max-error" : "Il valore deve essere minore di {{max}}{{unit}}", + "layout-fixed-width-required" : "La larghezza fissa è obbligatoria", + "right-width-percentage-required" : "La percentuale di destra è obbligatoria", + "left-width-percentage-required" : "La percentuale di sinistra è obbligatoria", + "divider" : "Divisore", + "right-side" : "Layout lato destro", + "left-side" : "Layout lato sinistro", + "add-new-breakpoint" : "Aggiungi nuovo punto di interruzione", + "breakpoint" : "Punto di interruzione", + "breakpoints" : "Punti di interruzione", + "copy-from" : "Copia da", + "size" : "Dimensione", + "delete-breakpoint-title" : "Sei sicuro di voler eliminare il punto di interruzione '{{name}}'?", + "delete-breakpoint-text" : "Nota: dopo la conferma, il punto di interruzione non sarà più recuperabile e le impostazioni torneranno al punto di interruzione predefinito." + }, + "legend" : { + "direction" : "Direzione", + "position" : "Posizione", + "show-values" : "Mostra valori", + "min-option" : "Min", + "max-option" : "Max", + "average-option" : "Media", + "total-option" : "Totale", + "latest-option" : "Ultimo", + "sort-legend" : "Ordina le chiavi dati nella legenda", + "show-max" : "Mostra valore massimo", + "show-min" : "Mostra valore minimo", + "show-avg" : "Mostra valore medio", + "show-total" : "Mostra valore totale", + "show-latest" : "Mostra ultimo valore", + "settings" : "Impostazioni legenda", + "min" : "min", + "max" : "max", + "avg" : "media", + "total" : "totale", + "latest" : "ultimo", + "Min" : "Min", + "Max" : "Max", + "Avg" : "Media", + "Total" : "Totale", + "Latest" : "Ultimo", + "comparison-time-ago" : { + "previousInterval" : "(intervallo precedente)", + "customInterval" : "(intervallo personalizzato)", + "days" : "(giorno fa)", + "weeks" : "(settimana fa)", + "months" : "(mese fa)", + "years" : "(anno fa)" + }, + "column-title" : "Titolo colonna", + "label" : "Etichetta", + "value" : "Valore" + }, + "login" : { + "login" : "Accesso", + "request-password-reset" : "Richiedi reimpostazione password", + "reset-password" : "Reimposta password", + "create-password" : "Crea password", + "two-factor-authentication" : "Autenticazione a due fattori", + "passwords-mismatch-error" : "Le password inserite devono coincidere!", + "password-again" : "Ripeti password", + "sign-in" : "Accedi", + "username" : "Nome utente (email)", + "remember-me" : "Ricordami", + "forgot-password" : "Password dimenticata?", + "password-reset" : "Reimpostazione password", + "expired-password-reset-message" : "Le tue credenziali sono scadute! Crea una nuova password.", + "new-password" : "Nuova password", + "new-password-again" : "Conferma nuova password", + "password-link-sent-message" : "Link di reimpostazione inviato", + "email" : "Email", + "invalid-email-format" : "Formato email non valido.", + "login-with" : "Accedi con {{name}}", + "or" : "oppure", + "error" : "Errore di accesso", + "verify-your-identity" : "Verifica la tua identità", + "select-way-to-verify" : "Seleziona un metodo di verifica", + "resend-code" : "Reinvia codice", + "resend-code-wait" : "Reinvia codice in { time, plural, =1 {1 secondo} other {# secondi} }", + "try-another-way" : "Prova un altro metodo", + "totp-auth-description" : "Inserisci il codice di sicurezza dalla tua app di autenticazione.", + "totp-auth-placeholder" : "Codice", + "sms-auth-description" : "Un codice di sicurezza è stato inviato al tuo telefono al numero {{contact}}.", + "sms-auth-placeholder" : "Codice SMS", + "email-auth-description" : "Un codice di sicurezza è stato inviato al tuo indirizzo email {{contact}}.", + "email-auth-placeholder" : "Codice email", + "backup-code-auth-description" : "Inserisci uno dei tuoi codici di backup.", + "backup-code-auth-placeholder" : "Codice di backup", + "activation-link-expired" : "Il link di attivazione è scaduto", + "activation-link-expired-message" : "Il link per attivare il tuo profilo è scaduto. Puoi tornare alla pagina di accesso per ricevere una nuova email.", + "reset-password-link-expired" : "Il link per reimpostare la password è scaduto", + "reset-password-link-expired-message" : "Il link per reimpostare la password è scaduto. Puoi tornare alla pagina di accesso per ricevere una nuova email." + }, + "mobile" : { + "add-application" : "Aggiungi applicazione", + "app-id" : "ID applicazione", + "app-id-required" : "ID applicazione richiesto", + "app-id-pattern" : "Formato ID applicazione non valido", + "app-store-link" : "Link App Store", + "app-store-link-required" : "Link App Store richiesto", + "application-details" : "Dettagli applicazione", + "application-package" : "Pacchetto applicazione", + "application-secret" : "Segreto applicazione", + "application-secret-required" : "Segreto applicazione richiesto", + "application" : "Applicazione", + "applications" : "Applicazioni", + "copy-app-id" : "Copia ID applicazione", + "copy-app-store-link" : "Copia link App Store", + "copy-application-package" : "Copia pacchetto applicazione", + "copy-application-secret" : "Copia segreto applicazione", + "copy-google-play-link" : "Copia link Google Play", + "copy-sha256-certificate-fingerprints" : "Copia impronta SHA256 del certificato", + "delete-application" : "Elimina applicazione", + "delete-application-button-text" : "Comprendo le conseguenze, elimina applicazione", + "delete-application-text" : "Questa azione è irreversibile. L'applicazione sarà eliminata definitivamente.
Se non desideri eliminarla in modo permanente puoi sospendere temporaneamente l'applicazione.
Per eliminare comunque l'applicazione digita \"{{phrase}}\" per confermare.", + "delete-application-title-short" : "Sei sicuro di voler eliminare l'applicazione '{{name}}'?", + "delete-application-text-short" : "Attenzione, dopo la conferma l'applicazione e tutti i dati correlati saranno irreversibili.", + "delete-application-phrase" : "elimina applicazione", + "delete-applications-bundle-text" : "Attenzione, dopo la conferma il pacchetto mobile e tutti i dati correlati saranno irreversibili.", + "delete-applications-bundle-title" : "Sei sicuro di voler eliminare il pacchetto mobile '{{bundleName}}'?", + "generate-application-secret" : "Genera segreto applicazione", + "google-play-link" : "Link Google Play", + "google-play-link-required" : "Link Google Play richiesto", + "latest-version" : "Ultima versione", + "min-version" : "Versione minima", + "invalid-version-pattern" : "Formato versione non valido. Usa il formato: major.minor.patch (es. 1.0.0).", + "mobile-center" : "Centro mobile", + "mobile-package" : "Pacchetto applicazione", + "mobile-package-max-length" : "Il pacchetto applicazione deve contenere meno di 256 caratteri", + "mobile-package-required" : "Pacchetto applicazione richiesto.", + "mobile-package-pattern" : "Formato pacchetto applicazione non valido", + "no-application" : "Nessuna applicazione trovata", + "no-bundles" : "Nessun pacchetto trovato", + "platform-type" : "Tipo di piattaforma", + "search-application" : "Cerca applicazioni", + "search-bundles" : "Cerca pacchetti", + "set" : "Imposta", + "sha256-certificate-fingerprints" : "Impronta certificato SHA256", + "sha256-certificate-fingerprints-required" : "Impronta certificato SHA256 richiesta", + "sha256-certificate-fingerprints-pattern" : "Formato impronta SHA256 non valido", + "show-hidden-pages" : "Mostra pagine nascoste", + "status" : "Stato", + "status-type" : { + "deprecated" : "Deprecato", + "draft" : "Bozza", + "published" : "Pubblicato", + "suspended" : "Sospeso" + }, + "store-information" : "Informazioni dello store", + "version-information" : "Informazioni sulla versione", + "min-version-release-notes" : "Note di rilascio versione minima", + "latest-version-release-notes" : "Note di rilascio ultima versione", + "bundle" : "Pacchetto", + "bundles" : "Pacchetti", + "add-bundle" : "Aggiungi pacchetto", + "title" : "Titolo", + "title-required" : "Il titolo è obbligatorio", + "title-cannot-contain-only-spaces" : "Il titolo non può contenere solo spazi", + "title-max-length" : "Il titolo deve contenere meno di 256 caratteri", + "oauth-clients" : "Client OAuth 2.0", + "android-app" : "App Android", + "android-application" : "Applicazione Android", + "ios-app" : "App iOS", + "ios-application" : "Applicazione iOS", + "invalid-store-link" : "Link dello store non valido", + "enable-oauth" : "Abilita OAuth 2.0", + "enable-self-registration" : "Abilita autoregistrazione", + "edit-bundle" : "Modifica pacchetto", + "description" : "Descrizione", + "basic-settings" : "Impostazioni di base", + "no-application-matching" : "Nessuna applicazione corrispondente a '{{entity}}' trovata.", + "no-bundle-matching" : "Nessun pacchetto corrispondente a '{{entity}}' trovato.", + "application-required" : "Applicazione obbligatoria.", + "bundle-required" : "Pacchetto obbligatorio.", + "no-application-text" : "Nessuna applicazione trovata", + "no-bundle-text" : "Nessun pacchetto trovato", + "layout" : "Layout", + "pages" : "Pagine", + "hide-all-pages" : "Nascondi tutte le pagine", + "reset-to-default-pages" : "Reimposta alle pagine predefinite", + "add-specific-page" : "Aggiungi pagina specifica", + "visible" : "Visibile", + "hidden" : "Nascosto", + "reset-to-page-default" : "Ripristina pagina ai valori predefiniti", + "mobile-599" : "Mobile (max 599px)", + "tablet-959" : "Tablet (max 959px)", + "max-element-number" : "Numero massimo di elementi", + "page-name" : "Nome pagina", + "page-name-required" : "Il nome della pagina è obbligatorio.", + "page-name-cannot-contain-only-spaces" : "Il nome della pagina non può contenere solo spazi.", + "page-name-max-length" : "Il nome della pagina deve contenere meno di 256 caratteri", + "page-type" : "Tipo di pagina", + "pages-types" : { + "dashboard" : "Dashboard", + "web-view" : "Vista web", + "custom" : "Personalizzato" + }, + "url" : "URL", + "invalid-url-format" : "Formato URL non valido", + "path" : "Percorso", + "invalid-path-format" : "Formato del percorso non valido", + "custom-page" : "Pagina personalizzata", + "edit-page" : "Modifica pagina", + "edit-custom-page" : "Modifica pagina personalizzata", + "delete-page" : "Elimina pagina", + "qr-code-widget" : "Widget codice QR", + "type-here" : "Digita qui", + "configuration-dialog" : "Finestra di configurazione", + "configuration-app" : "App di configurazione", + "configuration-step" : { + "prepare-environment-title" : "Prepara l'ambiente di sviluppo", + "prepare-environment-text" : "L'app mobile Flutter ThingsBoard richiede Flutter SDK. Segui le istruzioni per configurare Flutter SDK.", + "get-source-code-title" : "Ottieni il codice sorgente dell'app", + "get-source-code-text" : "Puoi ottenere il codice sorgente dell'app mobile Flutter ThingsBoard clonandolo dal repository GitHub:", + "configure-api-title" : "Configura endpoint API di ThingsBoard", + "configure-api-text" : "Apri il progetto flutter_thingsboard_pe_app nel tuo editor/IDE. Modifica:", + "configure-api-hint" : "Imposta il valore della costante thingsBoardApiEndpoint per corrispondere all'endpoint API della tua istanza ThingsBoard. Non utilizzare gli hostname 'localhost' o '127.0.0.1'.", + "run-app-title" : "Esegui l'app", + "run-app-text" : "Esegui l'app come descritto nel tuo IDE.\nSe usi il terminale, esegui il comando seguente:", + "more-information" : "Informazioni dettagliate disponibili nella nostra documentazione introduttiva.", + "getting-started" : "Guida introduttiva", + "configure-package-title" : "Configura il pacchetto dell'applicazione", + "configure-package-text" : "Puoi modificare manualmente il pacchetto dell'applicazione o usare uno strumento CLI di terze parti.", + "configure-package-text-install" : "Per installare lo strumento CLI Rename, esegui il seguente comando:", + "configure-package-run-commands" : "Esegui questi comandi nella directory root del tuo progetto:" + } + }, + "notification" : { + "action-button" : "Pulsante di azione", + "action-type" : "Tipo di azione", + "active" : "Attivo", + "add-notification-recipients-group" : "Aggiungi gruppo destinatari notifica", + "add-notification-template" : "Aggiungi modello di notifica", + "add-recipient" : "Aggiungi destinatario", + "add-recipients" : "Aggiungi destinatari", + "add-rule" : "Aggiungi regola", + "add-stage" : "Aggiungi fase", + "add-template" : "Aggiungi modello", + "after" : "Dopo", + "alarm-assignment-trigger-settings" : "Impostazioni trigger assegnazione allarme", + "alarm-comment-trigger-settings" : "Impostazioni trigger commento allarme", + "alarm-trigger-settings" : "Impostazioni trigger allarme", + "all" : "Tutti", + "api-feature-hint" : "Se il campo è vuoto, il trigger sarà applicato a tutte le funzionalità API", + "api-usage-trigger-settings" : "Impostazioni trigger utilizzo API", + "new-platform-version-trigger-settings" : "Impostazioni trigger nuova versione piattaforma", + "rate-limits-trigger-settings" : "Impostazioni trigger superamento limiti", + "task-processing-failure-trigger-settings" : "Impostazioni trigger errore elaborazione attività", + "at-least-one-should-be-selected" : "Almeno uno deve essere selezionato", + "basic-settings" : "Impostazioni di base", + "button-text" : "Testo pulsante", + "button-text-required" : "Il testo del pulsante è obbligatorio", + "button-text-max-length" : "Il testo del pulsante deve essere lungo al massimo {{ length }} caratteri", + "compose" : "Componi", + "conversation" : "Conversazione", + "conversation-required" : "La conversazione è obbligatoria", + "copy-notification-template" : "Copia modello di notifica", + "copy-rule" : "Copia regola", + "copy-template" : "Copia modello", + "create-new" : "Crea nuovo", + "created" : "Creato", + "customize-messages" : "Personalizza messaggi", + "delete-notification-text" : "Attenzione, dopo la conferma la notifica non sarà più recuperabile.", + "delete-notification-title" : "Sei sicuro di voler eliminare la notifica?", + "delete-notifications-text" : "Attenzione, dopo la conferma le notifiche non saranno più recuperabili.", + "delete-notifications-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 notifica} other {# notifiche} }?", + "delete-recipient-text" : "Attenzione, dopo la conferma il destinatario non sarà più recuperabile.", + "delete-recipient-title" : "Sei sicuro di voler eliminare il destinatario '{{recipientName}}'?", + "delete-recipients-text" : "Attenzione, dopo la conferma i destinatari non saranno più recuperabili.", + "delete-recipients-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 destinatario} other {# destinatari} }?", + "delete-request-text" : "Attenzione, dopo la conferma la richiesta non sarà più recuperabile.", + "delete-request-title" : "Sei sicuro di voler eliminare la richiesta?", + "delete-requests-text" : "Attenzione, dopo la conferma le richieste non saranno più recuperabili.", + "delete-requests-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 richiesta} other {# richieste} }?", + "delete-rule-text" : "Attenzione, dopo la conferma la regola non sarà più recuperabile.", + "delete-rule-title" : "Sei sicuro di voler eliminare la regola '{{ruleName}}'?", + "delete-rules-text" : "Attenzione, dopo la conferma le regole non saranno più recuperabili.", + "delete-rules-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 regola} other {# regole} }?", + "delete-template-text" : "Attenzione, dopo la conferma il modello non sarà più recuperabile.", + "delete-template-title" : "Sei sicuro di voler eliminare il modello '{{templateName}}'?", + "delete-templates-text" : "Attenzione, dopo la conferma i modelli non saranno più recuperabili.", + "delete-templates-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 modello} other {# modelli} }?", + "deleted" : "Eliminato", + "delivery-method" : { + "delivery-method" : "Metodo di consegna", + "email" : "Email", + "email-preview" : "Anteprima notifica email", + "slack" : "Slack", + "slack-preview" : "Anteprima notifica Slack", + "microsoft-teams" : "Microsoft Teams", + "microsoft-teams-preview" : "Anteprima notifica Microsoft Teams", + "sms" : "SMS", + "sms-preview" : "Anteprima notifica SMS", + "web" : "Web", + "web-preview" : "Anteprima notifica Web", + "mobile-app" : "App mobile", + "mobile-app-preview" : "Anteprima notifica app mobile" + }, + "delivery-method-not-configure-click" : "Metodo di consegna non configurato. Clicca per configurarlo.", + "delivery-method-not-configure-contact" : "Metodo di consegna non configurato. Contatta l'amministratore di sistema.", + "delivery-methods" : "Metodi di consegna", + "description" : "Descrizione", + "device-activity-trigger-settings" : "Impostazioni trigger attività dispositivo", + "device-list-rule-hint" : "Se il campo è vuoto, il trigger sarà applicato a tutti i dispositivi", + "device-profiles-list-rule-hint" : "Se il campo è vuoto, il trigger sarà applicato a tutti i profili dispositivo", + "disabled" : "Disabilitato", + "edge-trigger-settings" : "Impostazioni trigger edge", + "edge-list-rule-hint" : "Se il campo è vuoto, il trigger sarà applicato a tutte le istanze edge", + "edit-notification-recipients-group" : "Modifica gruppo destinatari notifica", + "edit-notification-template" : "Modifica modello di notifica", + "edit-rule" : "Modifica regola", + "edit-template" : "Modifica modello", + "enabled" : "Abilitato", + "entities-limit-trigger-settings" : "Impostazioni trigger limite entità", + "entity-action-trigger-settings" : "Impostazioni trigger azione entità", + "entity-type" : "Tipo entità", + "escalation-chain" : "Catena di escalation", + "failed-send" : "Errori di invio", + "fails" : "{ count, plural, =1 {1 errore} other {# errori} }", + "filter" : "Filtro", + "first-recipient" : "Primo destinatario", + "inactive" : "Inattivo", + "inbox" : "Posta in arrivo", + "notification-inbox" : "Notifiche / Posta in arrivo", + "input-field-support-templatization" : "Il campo di input supporta la templatizzazione.", + "input-fields-support-templatization" : "I campi di input supportano la templatizzazione.", + "link" : "Link", + "link-required" : "Il link è obbligatorio", + "link-type" : { + "dashboard" : "Apri dashboard", + "link" : "Apri URL" + }, + "loading-notifications" : "Caricamento notifiche...", + "management" : "Gestione notifiche", + "mark-all-as-read" : "Segna tutte come lette", + "mark-as-read" : "Segna come letta", + "message" : "Messaggio", + "message-required" : "Il messaggio è obbligatorio", + "message-max-length" : "Il messaggio deve contenere al massimo {{ length }} caratteri", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio", + "new-notification" : "Nuova notifica", + "no-inbox-notification" : "Nessuna notifica trovata", + "no-notification-request" : "Nessuna richiesta di notifica", + "no-notification-templates" : "Nessun modello di notifica trovato", + "no-notifications-yet" : "Nessuna notifica al momento", + "no-recipients-notification" : "Nessuna notifica di destinatari", + "no-recipients-matching" : "Nessun destinatario corrispondente a '{{entity}}' trovato.", + "no-recipients-text" : "Nessun destinatario trovato", + "no-rule" : "Nessuna regola configurata", + "no-rules-notification" : "Nessuna regola di notifica", + "no-severity-found" : "Nessuna gravità trovata", + "no-severity-matching" : "'{{severity}}' non trovata.", + "no-template-matching" : "Nessuna risorsa corrispondente a '{{template}}' trovata.", + "not-found-slack-recipient" : "Destinatario Slack non trovato", + "notification" : "Notifica", + "notification-center" : "Centro notifiche", + "notification-tap-action" : "Azione al tocco della notifica", + "notification-tap-action-hint" : "Se non abilitata, verrà utilizzata la dashboard di default dell'allarme", + "notify" : "notifica", + "notify-again" : "Notifica di nuovo", + "notify-alarm-action" : { + "acknowledged" : "Allarme riconosciuto", + "assigned" : "Allarme assegnato", + "cleared" : "Allarme risolto", + "created" : "Allarme creato", + "severity-changed" : "Gravità allarme cambiata", + "unassigned" : "Allarme non assegnato" + }, + "notify-on" : "Notifica su", + "notify-on-comment-update" : "Notifica su aggiornamento commento", + "notify-on-required" : "Campo 'Notifica su' obbligatorio", + "notify-on-unassign" : "Notifica su disassegnazione", + "notify-only-user-comments" : "Notifica solo commenti utente", + "only-rule-chain-lifecycle-failures" : "Solo fallimenti del ciclo di vita della rule chain", + "only-rule-node-lifecycle-failures" : "Solo fallimenti del ciclo di vita del nodo", + "platform-users" : "Utenti piattaforma", + "rate-limits" : "Limiti di velocità", + "rate-limits-hint" : "Se il campo è vuoto, il trigger sarà applicato a tutti i limiti", + "recipient" : "Destinatario", + "recipient-group" : "Gruppo destinatari", + "recipient-type" : { + "affected-tenant-administrators" : "Amministratori tenant coinvolti", + "affected-user" : "Utente coinvolto", + "all-users" : "Tutti gli utenti", + "customer-users" : "Utenti cliente", + "system-administrators" : "Amministratori di sistema", + "tenant-administrators" : "Amministratori tenant", + "user-filters" : "Filtro utente", + "user-list" : "Lista utenti", + "users-entity-owner" : "Utenti del proprietario dell'entità" + }, + "recipients" : "Destinatari", + "notification-recipient" : "Destinatario notifica", + "notification-recipient-required" : "Il destinatario della notifica è obbligatorio.", + "notification-recipients" : "Notifiche / Destinatari", + "recipients-count" : "{ count, plural, =1 {1 destinatario} other {# destinatari} }", + "recipients-required" : "I destinatari sono obbligatori", + "refresh-allow-delivery-method" : "Aggiorna metodo di consegna consentito", + "request-search" : "Cerca richiesta", + "request-status" : { + "processing" : "Elaborazione in corso", + "scheduled" : "Pianificato", + "sent" : "Inviato" + }, + "review" : "Revisione", + "rule" : "Regola", + "rule-chain-list-rule-hint" : "Se il campo è vuoto, il trigger verrà applicato a tutte le rule chain", + "rule-engine-events-trigger-settings" : "Impostazioni trigger eventi rule engine", + "rule-engine-filter" : "Filtro rule engine", + "rule-name" : "Nome regola", + "rule-name-required" : "Il nome è obbligatorio", + "rule-disable" : "Disattiva regola di notifica", + "rule-enable" : "Attiva regola di notifica", + "rule-node-filter" : "Filtro nodo regola", + "rules" : "Regole", + "notification-rules" : "Notifiche / Regole", + "scheduler-later" : "Pianifica per dopo", + "search-notification" : "Cerca notifiche", + "search-recipients" : "Cerca destinatari", + "search-rules" : "Cerca regole", + "search-templates" : "Cerca modelli", + "see-documentation" : "Vedi documentazione", + "selected-notifications" : "{ count, plural, =1 {1 notifica} other {# notifiche} } selezionata", + "selected-recipients" : "{ count, plural, =1 {1 destinatario} other {# destinatari} } selezionato", + "selected-requests" : "{ count, plural, =1 {1 richiesta} other {# richieste} } selezionata", + "selected-rules" : "{ count, plural, =1 {1 regola} other {# regole} } selezionata", + "selected-template" : "{ count, plural, =1 {1 modello} other {# modelli} } selezionato", + "send-notification" : "Invia notifica", + "sent" : "Inviato", + "setup" : "Configurazione", + "notification-sent" : "Notifiche / Inviate", + "set-entity-from-notification" : "Imposta entità dalla notifica allo stato della dashboard", + "slack-chanel-type" : "Tipo di canale Slack", + "slack-chanel-types" : { + "direct" : "Messaggio diretto", + "private-channel" : "Canale privato", + "public-channel" : "Canale pubblico" + }, + "start-from-scratch" : "Inizia da zero", + "status" : "Stato", + "stop-escalation-alarm-status-become" : "Ferma l'escalation quando lo stato dell'allarme diventa:", + "subject" : "Oggetto", + "subject-required" : "L'oggetto è obbligatorio", + "subject-max-length" : "L'oggetto deve contenere al massimo {{ length }} caratteri", + "template" : "Modello", + "template-name" : "Nome modello", + "template-required" : "Il modello è obbligatorio", + "template-type" : { + "alarm" : "Allarme", + "alarm-assignment" : "Assegnazione allarme", + "alarm-comment" : "Commento allarme", + "api-usage-limit" : "Limite utilizzo API", + "device-activity" : "Attività dispositivo", + "entities-limit" : "Limite entità", + "entity-action" : "Azione entità", + "general" : "Generale", + "rule-engine-lifecycle-event" : "Evento ciclo di vita rule engine", + "rule-node" : "Nodo regola", + "new-platform-version" : "Nuova versione piattaforma", + "rate-limits" : "Limiti superati", + "edge-communication-failure" : "Errore comunicazione edge", + "edge-connection" : "Connessione edge", + "task-processing-failure" : "Errore elaborazione attività" + }, + "templates" : "Modelli", + "notification-templates" : "Notifiche / Modelli", + "tenant-profiles-list-rule-hint" : "Se il campo è vuoto, il trigger sarà applicato a tutti i profili tenant", + "tenants-list-rule-hint" : "Se il campo è vuoto, il trigger sarà applicato a tutti i tenant", + "threshold" : "Soglia", + "theme-color" : "Colore tema", + "time" : "Tempo", + "track-rule-node-events" : "Traccia eventi nodo regola", + "trigger" : { + "alarm" : "Allarme", + "alarm-assignment" : "Assegnazione allarme", + "alarm-comment" : "Commento allarme", + "api-usage-limit" : "Limite utilizzo API", + "device-activity" : "Attività dispositivo", + "entities-limit" : "Limite entità", + "entity-action" : "Azione entità", + "rule-engine-lifecycle-event" : "Evento ciclo di vita rule engine", + "new-platform-version" : "Nuova versione della piattaforma", + "rate-limits" : "Limiti superati", + "edge-connection" : "Connessione edge", + "edge-communication-failure" : "Errore comunicazione edge", + "task-processing-failure" : "Errore elaborazione attività", + "trigger" : "Trigger", + "trigger-required" : "Il trigger è obbligatorio" + }, + "type" : "Tipo", + "unread" : "Non letti", + "updated" : "Aggiornato", + "use-deprecated-webhook-connectors" : "Usa connettori webhook deprecati", + "use-old-api" : "Usa API vecchia", + "use-template" : "Usa modello", + "view-all" : "Visualizza tutto", + "warning" : "Avviso", + "webhook-url" : "URL webhook", + "webhook-url-required" : "L'URL webhook è obbligatorio", + "workflow-url" : "URL flusso di lavoro", + "workflow-url-required" : "L'URL del flusso di lavoro è obbligatorio", + "channel-name" : "Nome del canale", + "channel-name-required" : "Il nome del canale è obbligatorio", + "settings" : { + "notification-settings" : "Impostazioni notifica", + "reset-all" : "Reimposta tutte", + "reset-all-title" : "Sei sicuro di voler reimpostare il modulo?", + "reset-all-text" : "Dopo la conferma, il modulo delle impostazioni verrà reimpostato ai valori predefiniti e salvato.", + "type" : "Tipo", + "enable-all" : "Abilita tutte", + "disable-all" : "Disabilita tutte", + "delivery-not-configured" : "Metodo di consegna non configurato" + } + }, + "ota-update" : { + "add" : "Aggiungi pacchetto", + "assign-firmware" : "Firmware assegnato", + "assign-firmware-required" : "Il firmware assegnato è obbligatorio", + "assign-software" : "Software assegnato", + "assign-software-required" : "Il software assegnato è obbligatorio", + "auto-generate-checksum" : "Genera automaticamente checksum", + "checksum" : "Checksum", + "checksum-hint" : "Se il checksum è vuoto, verrà generato automaticamente", + "checksum-algorithm" : "Algoritmo di checksum", + "checksum-copied-message" : "Checksum del pacchetto copiato negli appunti", + "change-firmware" : "La modifica del firmware può causare l'aggiornamento di { count, plural, =1 {1 dispositivo} other {# dispositivi} }.", + "change-software" : "La modifica del software può causare l'aggiornamento di { count, plural, =1 {1 dispositivo} other {# dispositivi} }.", + "chose-compatible-device-profile" : "Il pacchetto caricato sarà disponibile solo per dispositivi con il profilo selezionato.", + "chose-firmware-distributed-device" : "Scegli il firmware da distribuire ai dispositivi", + "chose-software-distributed-device" : "Scegli il software da distribuire ai dispositivi", + "content-type" : "Tipo di contenuto", + "copy-checksum" : "Copia checksum", + "copy-direct-url" : "Copia URL diretto", + "copyId" : "Copia ID pacchetto", + "copied" : "Copiato!", + "delete" : "Elimina pacchetto", + "delete-ota-update-text" : "Attenzione, dopo la conferma l'aggiornamento OTA non sarà più recuperabile.", + "delete-ota-update-title" : "Sei sicuro di voler eliminare l'aggiornamento OTA '{{title}}'?", + "delete-ota-updates-text" : "Attenzione, dopo la conferma tutti gli aggiornamenti OTA selezionati saranno rimossi.", + "delete-ota-updates-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 aggiornamento OTA} other {# aggiornamenti OTA} }?", + "description" : "Descrizione", + "direct-url" : "URL diretto", + "direct-url-copied-message" : "URL diretto del pacchetto copiato negli appunti", + "direct-url-required" : "L'URL diretto è obbligatorio", + "download" : "Scarica pacchetto", + "drop-file" : "Trascina un file pacchetto o fai clic per selezionare un file da caricare.", + "drop-package-file-or" : "Trascina un file pacchetto o", + "file-name" : "Nome file", + "file-size" : "Dimensione file", + "file-size-bytes" : "Dimensione file in byte", + "idCopiedMessage" : "ID del pacchetto copiato negli appunti", + "no-firmware-matching" : "Nessun pacchetto OTA Firmware compatibile corrispondente a '{{entity}}' trovato.", + "no-firmware-text" : "Nessun pacchetto OTA Firmware compatibile disponibile.", + "no-packages-text" : "Nessun pacchetto trovato", + "no-software-matching" : "Nessun pacchetto OTA Software compatibile corrispondente a '{{entity}}' trovato.", + "no-software-text" : "Nessun pacchetto OTA Software compatibile disponibile.", + "ota-update" : "Aggiornamento OTA", + "ota-update-details" : "Dettagli aggiornamento OTA", + "ota-updates" : "Aggiornamenti OTA", + "package-file" : "File pacchetto", + "package-type" : "Tipo di pacchetto", + "packages-repository" : "Repository pacchetti", + "search" : "Cerca pacchetti", + "selected-package" : "{ count, plural, =1 {1 pacchetto} other {# pacchetti} } selezionati", + "title" : "Titolo", + "title-required" : "Il titolo è obbligatorio.", + "title-max-length" : "Il titolo deve essere inferiore a 256 caratteri", + "types" : { + "firmware" : "Firmware", + "software" : "Software" + }, + "upload-binary-file" : "Carica file binario", + "use-external-url" : "Usa URL esterno", + "version" : "Versione", + "version-required" : "La versione è obbligatoria.", + "version-tag" : "Tag versione", + "version-tag-hint" : "Il tag personalizzato deve corrispondere alla versione segnalata dal dispositivo.", + "version-max-length" : "La versione deve essere inferiore a 256 caratteri", + "warning-after-save-no-edit" : "Una volta caricato il pacchetto, non sarà possibile modificare titolo, versione, profilo dispositivo e tipo di pacchetto." + }, + "position" : { + "top" : "Alto", + "bottom" : "Basso", + "left" : "Sinistra", + "right" : "Destra" + }, + "profile" : { + "profile" : "Profilo", + "last-login-time" : "Ultimo accesso", + "change-password" : "Cambia password", + "current-password" : "Password attuale", + "copy-jwt-token" : "Copia token JWT", + "jwt-token" : "Token JWT", + "token-valid-till" : "Token valido fino a", + "tokenCopiedSuccessMessage" : "Token JWT copiato negli appunti", + "tokenCopiedWarnMessage" : "Il token JWT è scaduto! Per favore, ricarica la pagina." + }, + "profiles" : { + "profiles" : "Profili" + }, + "security" : { + "security" : "Sicurezza", + "general-settings" : "Impostazioni di sicurezza generali", + "access-token" : "Token di accesso", + "access-token-required" : "Il token di accesso è obbligatorio", + "clientId" : "ID client", + "clientId-required" : "L'ID client è obbligatorio", + "username" : "Nome utente", + "username-required" : "Il nome utente è obbligatorio", + "ca-cert" : "Certificato CA", + "2fa" : { + "2fa" : "Autenticazione a due fattori", + "2fa-description" : "L'autenticazione a due fattori protegge il tuo account da accessi non autorizzati. Tutto ciò che devi fare è inserire un codice di sicurezza durante l'accesso.", + "authenticate-with" : "Puoi autenticarti con:", + "disable-2fa-provider-text" : "La disattivazione di {{name}} renderà il tuo account meno sicuro", + "disable-2fa-provider-title" : "Sei sicuro di voler disattivare {{name}}?", + "get-new-code" : "Ottieni nuovo codice", + "main-2fa-method" : "Usa come metodo principale di autenticazione a due fattori", + "dialog" : { + "activation-step-description-email" : "La prossima volta che accederai, ti verrà chiesto di inserire il codice di sicurezza inviato al tuo indirizzo email.", + "activation-step-description-sms" : "La prossima volta che accederai, ti verrà chiesto di inserire il codice di sicurezza inviato al numero di telefono.", + "activation-step-description-totp" : "La prossima volta che accederai, dovrai fornire un codice di autenticazione a due fattori.", + "activation-step-label" : "Attivazione", + "backup-code-description" : "Stampa i codici per averli a portata di mano quando dovrai accedere al tuo account. Ogni codice può essere utilizzato una sola volta.", + "backup-code-warn" : "Una volta lasciata questa pagina, i codici non potranno essere visualizzati di nuovo. Conservali in modo sicuro usando le opzioni sotto.", + "download-txt" : "Scarica (txt)", + "email-step-description" : "Inserisci un'email da usare come autenticatore.", + "email-step-label" : "Email", + "enable-email-title" : "Abilita autenticatore email", + "enable-sms-title" : "Abilita autenticatore SMS", + "enable-totp-title" : "Abilita app autenticatrice", + "enter-verification-code" : "Inserisci qui il codice a 6 cifre", + "get-backup-code-title" : "Ottieni codice di backup", + "next" : "Avanti", + "scan-qr-code" : "Scansiona questo codice QR con la tua app di verifica", + "send-code" : "Invia codice", + "sms-step-description" : "Inserisci un numero di telefono da usare come autenticatore.", + "sms-step-label" : "Numero di telefono", + "success" : "Successo!", + "totp-step-description-install" : "Puoi installare app come Google Authenticator, Authy o Duo.", + "totp-step-description-open" : "Apri l'app di autenticazione sul tuo smartphone.", + "totp-step-label" : "Ottieni app", + "verification-code" : "Codice a 6 cifre", + "verification-code-invalid" : "Formato del codice di verifica non valido", + "verification-code-incorrect" : "Codice di verifica errato", + "verification-code-many-request" : "Troppe richieste, controlla il codice di verifica", + "verification-step-description" : "Inserisci il codice a 6 cifre che abbiamo appena inviato a {{address}}", + "verification-step-label" : "Verifica" + }, + "provider" : { + "email" : "Email", + "email-description" : "Usa un codice di sicurezza inviato al tuo indirizzo email per autenticarti.", + "email-hint" : "I codici di autenticazione vengono inviati via email a {{ info }}", + "sms" : "SMS", + "sms-description" : "Usa il tuo telefono per autenticarti. Ti invieremo un codice di sicurezza via SMS al momento dell'accesso.", + "sms-hint" : "I codici di autenticazione vengono inviati via SMS a {{ info }}", + "totp" : "App autenticatrice", + "totp-description" : "Usa app come Google Authenticator, Authy o Duo sul tuo telefono per autenticarti. Genereranno un codice di sicurezza per l'accesso.", + "totp-hint" : "L'app autenticatrice è configurata per il tuo account", + "backup_code" : "Codice di backup", + "backup-code-description" : "Questi codici di accesso monouso stampabili ti consentono di accedere quando sei lontano dal telefono, ad esempio durante un viaggio.", + "backup-code-hint" : "{{ info }} codici monouso sono attivi in questo momento" + } + }, + "password-requirement" : { + "at-least" : "Almeno:", + "character" : "{ count, plural, =1 {1 carattere} other {# caratteri} }", + "digit" : "{ count, plural, =1 {1 cifra} other {# cifre} }", + "incorrect-password-try-again" : "Password errata. Riprova", + "lowercase-letter" : "{ count, plural, =1 {1 lettera minuscola} other {# lettere minuscole} }", + "new-passwords-not-match" : "La nuova password non corrisponde", + "password-should-not-contain-spaces" : "La password non deve contenere spazi", + "password-not-meet-requirements" : "La password non soddisfa i requisiti", + "password-requirements" : "Requisiti della password", + "password-should-difference" : "La nuova password deve essere diversa da quella attuale", + "special-character" : "{ count, plural, =1 {1 carattere speciale} other {# caratteri speciali} }", + "uppercase-letter" : "{ count, plural, =1 {1 lettera maiuscola} other {# lettere maiuscole} }", + "at-most" : "Al massimo:" + } + }, + "relation" : { + "relations" : "Relazioni", + "direction" : "Direzione", + "clear-relation-type" : "Cancella tipo di relazione", + "search-direction" : { + "FROM" : "Da", + "TO" : "A" + }, + "direction-type" : { + "FROM" : "da", + "TO" : "a" + }, + "from-relations" : "Relazioni in uscita", + "to-relations" : "Relazioni in entrata", + "selected-relations" : "{ count, plural, =1 {1 relazione} other {# relazioni} } selezionata", + "type" : "Tipo", + "to-entity-type" : "Tipo di entità di destinazione", + "to-entity-name" : "Nome entità di destinazione", + "from-entity-type" : "Tipo di entità di origine", + "from-entity-name" : "Nome entità di origine", + "to-entity" : "A entità", + "from-entity" : "Da entità", + "delete" : "Elimina relazione", + "relation-type" : "Tipo di relazione", + "relation-type-required" : "Il tipo di relazione è obbligatorio.", + "relation-type-max-length" : "Il tipo di relazione deve essere inferiore a 256 caratteri", + "any-relation-type" : "Qualsiasi tipo", + "add" : "Aggiungi relazione", + "edit" : "Modifica relazione", + "delete-to-relation-title" : "Sei sicuro di voler eliminare la relazione con l'entità '{{entityName}}'?", + "delete-to-relation-text" : "Attenzione, dopo la conferma l'entità '{{entityName}}' non sarà più collegata all'entità corrente.", + "delete-to-relations-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 relazione} other {# relazioni} }?", + "delete-to-relations-text" : "Attenzione, dopo la conferma tutte le relazioni selezionate saranno rimosse e le entità corrispondenti non saranno più collegate all'entità corrente.", + "delete-from-relation-title" : "Sei sicuro di voler eliminare la relazione dall'entità '{{entityName}}'?", + "delete-from-relation-text" : "Attenzione, dopo la conferma l'entità corrente non sarà più collegata all'entità '{{entityName}}'.", + "delete-from-relations-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 relazione} other {# relazioni} }?", + "delete-from-relations-text" : "Attenzione, dopo la conferma tutte le relazioni selezionate saranno rimosse e l'entità corrente non sarà più collegata alle entità corrispondenti.", + "remove-relation-filter" : "Rimuovi filtro di relazione", + "remove-filter" : "Rimuovi filtro", + "add-relation-filter" : "Aggiungi filtro di relazione", + "any-relation" : "Qualsiasi relazione", + "relation-filters" : "Filtri di relazione", + "additional-info" : "Informazioni aggiuntive (JSON)", + "invalid-additional-info" : "Impossibile analizzare il JSON delle informazioni aggiuntive.", + "no-relations-text" : "Nessuna relazione trovata", + "not" : "Non" + }, + "resource" : { + "add" : "Aggiungi risorsa", + "all-types" : "Tutti", + "copyId" : "Copia ID risorsa", + "delete" : "Elimina risorsa", + "delete-resource-text" : "Attenzione, dopo la conferma la risorsa non sarà più recuperabile.", + "delete-resource-title" : "Sei sicuro di voler eliminare la risorsa '{{resourceTitle}}'?", + "delete-resources-action-title" : "Elimina { count, plural, =1 {1 risorsa} other {# risorse} }", + "delete-resources-text" : "Nota: le risorse selezionate, anche se utilizzate nei profili dispositivo, verranno eliminate.", + "delete-resources-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 risorsa} other {# risorse} }?", + "download" : "Scarica risorsa", + "drop-file" : "Trascina un file risorsa o clicca per selezionarlo da caricare.", + "drop-resource-file-or" : "Trascina un file risorsa o", + "empty" : "La risorsa è vuota", + "file-name" : "Nome file", + "idCopiedMessage" : "ID risorsa copiato negli appunti", + "no-resource-matching" : "Nessuna risorsa corrispondente a '{{widgetsBundle}}' trovata.", + "no-resource-text" : "Nessuna risorsa trovata", + "open-widgets-bundle" : "Apri bundle di widget", + "resource" : "Risorsa", + "resource-file" : "File risorsa", + "resource-files" : "File di risorsa", + "resource-library-details" : "Dettagli risorsa", + "resource-type" : "Tipo di risorsa", + "resources-library" : "Libreria risorse", + "search" : "Cerca risorse", + "selected-resources" : "{ count, plural, =1 {1 risorsa} other {# risorse} } selezionata", + "system" : "Sistema", + "title" : "Titolo", + "title-required" : "Il titolo è obbligatorio.", + "title-max-length" : "Il titolo deve essere inferiore a 256 caratteri", + "type" : { + "jks" : "JKS", + "js-module" : "Modulo JS", + "lwm2m-model" : "Modello LWM2M", + "pkcs-12" : "PKCS #12" + }, + "resource-sub-type" : "Sottotipo", + "sub-type" : { + "image" : "immagine", + "scada-symbol" : "Simbolo SCADA", + "extension" : "Estensione", + "module" : "Modulo" + } + }, + "javascript" : { + "add" : "Aggiungi risorsa JavaScript", + "delete" : "Elimina risorsa JavaScript", + "delete-javascript-resource-text" : "Attenzione, dopo la conferma la risorsa JavaScript non sarà più recuperabile.", + "delete-javascript-resource-title" : "Sei sicuro di voler eliminare la risorsa JavaScript '{{resourceTitle}}'?", + "delete-javascript-resources-action-title" : "Elimina { count, plural, =1 {1 risorsa} other {# risorse} } JavaScript", + "delete-javascript-resources-text" : "Nota: le risorse JavaScript selezionate, anche se usate nelle funzioni JavaScript, verranno eliminate.", + "delete-javascript-resources-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 risorsa} other {# risorse} } JavaScript?", + "delete-javascript-resource-in-use-text" : "Se desideri comunque eliminare la risorsa JavaScript, clicca sul pulsante Elimina comunque.", + "download" : "Scarica risorsa JavaScript", + "upload-from-file" : "Carica JavaScript da file", + "resource-file" : "File risorsa JavaScript", + "drop-file" : "Trascina un file JavaScript o clicca per selezionarlo da caricare.", + "drop-resource-file-or" : "Trascina un file JavaScript o", + "javascript-library" : "Libreria JavaScript", + "javascript-type" : "Tipo JavaScript", + "javascript-resource-details" : "Dettagli risorsa JavaScript", + "javascript-resource-is-in-use" : "La risorsa JavaScript è utilizzata da altre entità", + "javascript-resources-are-in-use" : "Le risorse JavaScript sono utilizzate da altre entità", + "javascript-resource-is-in-use-text" : "La risorsa JavaScript '{{title}}' non è stata eliminata perché è utilizzata dalle seguenti entità:", + "javascript-resources-are-in-use-text" : "Non tutte le risorse JavaScript sono state eliminate perché sono utilizzate da altre entità.
Puoi visualizzare le entità facendo clic sul pulsante Riferimenti nella riga corrispondente.
Se vuoi comunque eliminarle, selezionale nella tabella sottostante e clicca su Elimina selezionati.", + "search" : "Cerca risorse JavaScript", + "selected-javascript-resources" : "{ count, plural, =1 {1 risorsa JavaScript} other {# risorse JavaScript} } selezionata", + "no-javascript-resource-text" : "Nessuna risorsa JavaScript trovata", + "all-types" : "Tutti", + "module-script" : "Script modulo" + }, + "rpc" : { + "error" : { + "target-device-is-not-set" : "Dispositivo di destinazione non impostato!", + "invalid-target-entity" : "I comandi RPC non sono supportati per l'entità {{entityType}}.", + "failed-to-resolve-target-device" : "Impossibile risolvere il dispositivo di destinazione!", + "request-timeout" : "Timeout della richiesta", + "rpc-http-error" : "Errore: {{status}} - {{statusText}}" + } + }, + "rulechain" : { + "rulechain" : "Catena di regole", + "rulechain-events" : "Eventi della catena di regole", + "rulechains" : "Catene di regole", + "root" : "Radice", + "delete" : "Elimina catena di regole", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio.", + "name-max-length" : "Il nome deve essere inferiore a 256 caratteri", + "description" : "Descrizione", + "add" : "Aggiungi catena di regole", + "set-root" : "Rendi radice la catena di regole", + "set-root-rulechain-title" : "Sei sicuro di voler rendere radice la catena di regole '{{ruleChainName}}'?", + "set-root-rulechain-text" : "Dopo la conferma, la catena di regole diventerà radice e gestirà tutti i messaggi di trasporto in arrivo.", + "delete-rulechain-title" : "Sei sicuro di voler eliminare la catena di regole '{{ruleChainName}}'?", + "delete-rulechain-text" : "Attenzione, dopo la conferma la catena di regole e tutti i dati correlati non saranno più recuperabili.", + "delete-rulechains-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 catena di regole} other {# catene di regole} }?", + "delete-rulechains-action-title" : "Elimina { count, plural, =1 {1 catena di regole} other {# catene di regole} }", + "delete-rulechains-text" : "Attenzione, dopo la conferma tutte le catene di regole selezionate saranno rimosse e tutti i dati correlati non saranno più recuperabili.", + "add-rulechain-text" : "Aggiungi nuova catena di regole", + "no-rulechains-text" : "Nessuna catena di regole trovata", + "rulechain-details" : "Dettagli catena di regole", + "details" : "Dettagli", + "events" : "Eventi", + "system" : "Sistema", + "import" : "Importa catena di regole", + "export" : "Esporta catena di regole", + "export-failed-error" : "Impossibile esportare la catena di regole: {{error}}", + "create-new-rulechain" : "Crea nuova catena di regole", + "rulechain-file" : "File catena di regole", + "invalid-rulechain-file-error" : "Impossibile importare la catena di regole: struttura dati non valida.", + "copyId" : "Copia ID catena di regole", + "idCopiedMessage" : "ID catena di regole copiato negli appunti", + "select-rulechain" : "Seleziona catena di regole", + "no-rulechains-matching" : "Nessuna catena di regole corrispondente a '{{entity}}' trovata.", + "rulechain-required" : "Catena di regole obbligatoria", + "management" : "Gestione regole", + "debug-mode" : "Modalità debug", + "search" : "Cerca catene di regole", + "selected-rulechains" : "{ count, plural, =1 {1 catena di regole} other {# catene di regole} } selezionata", + "open-rulechain" : "Apri catena di regole", + "edge-template-root" : "Modello radice", + "assign-to-edge" : "Assegna all'Edge", + "edge-rulechain" : "Catena di regole Edge", + "unassign-rulechain-from-edge-text" : "Dopo la conferma la catena di regole sarà disassegnata e non sarà più accessibile dall'edge.", + "unassign-rulechains-from-edge-title" : "Sei sicuro di voler disassegnare { count, plural, =1 {1 catena} other {# catene} }?", + "unassign-rulechains-from-edge-text" : "Dopo la conferma tutte le catene di regole selezionate saranno disassegnate e non saranno più accessibili dall'edge.", + "assign-rulechain-to-edge-title" : "Assegna catena(e) di regole all'Edge", + "assign-rulechain-to-edge-text" : "Seleziona le catene di regole da assegnare all'edge", + "set-edge-template-root-rulechain" : "Rendi catena di regole come modello radice edge", + "set-edge-template-root-rulechain-title" : "Sei sicuro di voler rendere '{{ruleChainName}}' modello radice per l'edge?", + "set-edge-template-root-rulechain-text" : "Dopo la conferma, la catena sarà modello radice per gli edge creati successivamente.", + "invalid-rulechain-type-error" : "Impossibile importare la catena di regole: tipo non valido. Tipo atteso: {{expectedRuleChainType}}.", + "set-auto-assign-to-edge" : "Assegna catena di regole all'edge alla creazione", + "set-auto-assign-to-edge-title" : "Vuoi assegnare automaticamente la catena '{{ruleChainName}}' all'edge alla creazione?", + "set-auto-assign-to-edge-text" : "Dopo la conferma la catena sarà assegnata automaticamente all'edge alla creazione.", + "unset-auto-assign-to-edge" : "Non assegnare catena all'edge alla creazione", + "unset-auto-assign-to-edge-title" : "Vuoi disabilitare l'assegnazione automatica della catena '{{ruleChainName}}'?", + "unset-auto-assign-to-edge-text" : "Dopo la conferma, la catena non sarà più assegnata automaticamente agli edge in fase di creazione.", + "unassign-rulechain-title" : "Sei sicuro di voler disassegnare la catena '{{ruleChainName}}'?", + "unassign-rulechains" : "Disassegna catene di regole" + }, + "rulenode" : { + "rule-node-events" : "Eventi del nodo regola", + "details" : "Dettagli", + "events" : "Eventi", + "search" : "Cerca nodi", + "open-node-library" : "Apri libreria nodi", + "close-node-library" : "Chiudi libreria nodi", + "add" : "Aggiungi nodo regola", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio.", + "name-max-length" : "Il nome deve essere inferiore a 256 caratteri", + "type" : "Tipo", + "rule-node-description" : "Descrizione nodo regola", + "delete" : "Elimina nodo regola", + "select-all-objects" : "Seleziona tutti i nodi e le connessioni", + "deselect-all-objects" : "Deseleziona tutti i nodi e le connessioni", + "delete-selected-objects" : "Elimina nodi e connessioni selezionati", + "delete-selected" : "Elimina selezionati", + "create-nested-rulechain" : "Crea catena di regole nidificata", + "select-all" : "Seleziona tutto", + "copy-selected" : "Copia selezionati", + "deselect-all" : "Deseleziona tutto", + "rulenode-details" : "Dettagli nodo regola", + "debug-mode" : "Modalità debug", + "singleton" : "Singleton", + "configuration" : "Configurazione", + "link" : "Collegamento", + "link-details" : "Dettagli collegamento nodo regola", + "add-link" : "Aggiungi collegamento", + "link-label" : "Etichetta collegamento", + "link-label-required" : "Etichetta collegamento obbligatoria.", + "custom-link-label" : "Etichetta collegamento personalizzata", + "custom-link-label-required" : "Etichetta collegamento personalizzata obbligatoria.", + "link-labels" : "Etichette collegamento", + "link-labels-required" : "Etichette collegamento obbligatorie.", + "no-link-labels-found" : "Nessuna etichetta collegamento trovata", + "no-link-label-matching" : "'{{label}}' non trovata.", + "create-new-link-label" : "Creane una nuova!", + "type-filter" : "Filtro", + "type-filter-details" : "Filtra i messaggi in ingresso con condizioni configurate", + "type-enrichment" : "Arricchimento", + "type-enrichment-details" : "Aggiungi informazioni nel Metadata del Messaggio", + "type-transformation" : "Trasformazione", + "type-transformation-details" : "Modifica il payload e i metadati del Messaggio", + "type-action" : "Azione", + "type-action-details" : "Esegui un'azione specifica", + "type-external" : "Esterno", + "type-external-details" : "Interagisce con un sistema esterno", + "type-rule-chain" : "Catena di regole", + "type-rule-chain-details" : "Inoltra i messaggi in ingresso a una Catena di Regole specificata", + "type-flow" : "Flusso", + "type-flow-details" : "Organizza il flusso dei messaggi", + "type-input" : "Input", + "type-input-details" : "Input logico della Catena di Regole, inoltra i messaggi al nodo regola successivo", + "type-unknown" : "Sconosciuto", + "type-unknown-details" : "Nodo regola non risolto", + "directive-is-not-loaded" : "La direttiva '{{directiveName}}' definita non è disponibile.", + "ui-resources-load-error" : "Errore nel caricamento delle risorse UI di configurazione.", + "invalid-target-rulechain" : "Impossibile risolvere la catena di regole di destinazione!", + "test-script-function" : "Test funzione script", + "script-lang-java-script" : "JavaScript", + "script-lang-tbel" : "TBEL", + "message" : "Messaggio", + "message-type" : "Tipo di messaggio", + "select-message-type" : "Seleziona tipo di messaggio", + "message-type-required" : "Tipo di messaggio obbligatorio", + "metadata" : "Metadati", + "metadata-required" : "Le voci di metadati non possono essere vuote.", + "output" : "Output", + "test" : "Test", + "help" : "Aiuto", + "reset-debug-settings" : "Reimposta le impostazioni di debug in tutti i nodi", + "test-with-this-message" : "{{test}} con questo messaggio", + "queue-hint" : "Seleziona una coda per l'inoltro del messaggio a un'altra coda. La coda 'Main' è usata per impostazione predefinita.", + "queue-singleton-hint" : "Seleziona una coda per l'inoltro in ambienti multiistanza. La coda 'Main' è usata per impostazione predefinita." + }, + "rule-node-config" : { + "id" : "Id", + "additional-info" : "Informazioni aggiuntive", + "advanced-settings" : "Impostazioni avanzate", + "create-entity-if-not-exists" : "Crea una nuova entità se non esiste", + "create-entity-if-not-exists-hint" : "Se abilitato, verrà creata una nuova entità con i parametri specificati, a meno che non esista già. Le entità esistenti verranno utilizzate così come sono per la relazione.", + "select-device-connectivity-event" : "Seleziona evento di connettività del dispositivo", + "entity-name-pattern" : "Modello nome", + "device-name-pattern" : "Nome dispositivo", + "asset-name-pattern" : "Nome asset", + "entity-view-name-pattern" : "Nome vista entità", + "customer-title-pattern" : "Titolo cliente", + "dashboard-name-pattern" : "Titolo dashboard", + "user-name-pattern" : "Email utente", + "edge-name-pattern" : "Nome edge", + "entity-name-pattern-required" : "Il modello nome è obbligatorio", + "entity-name-pattern-hint" : "Il campo supporta la templatizzazione. Usa $[messageKey] per estrarre valore dal messaggio e ${metadataKey} per estrarre valore dai metadati.", + "copy-message-type" : "Copia tipo di messaggio", + "entity-type-pattern" : "Modello tipo", + "entity-type-pattern-required" : "Il modello tipo è obbligatorio", + "message-type-value" : "Valore tipo di messaggio", + "message-type-value-required" : "Valore tipo di messaggio obbligatorio", + "message-type-value-max-length" : "Valore tipo di messaggio deve essere inferiore a 256", + "output-message-type" : "Tipo di messaggio in uscita", + "entity-cache-expiration" : "Tempo di scadenza cache entità (sec)", + "entity-cache-expiration-hint" : "Specifica l’intervallo massimo di tempo consentito per memorizzare le entità trovate. Il valore 0 significa che non scadranno mai.", + "entity-cache-expiration-required" : "Tempo di scadenza cache entità obbligatorio.", + "entity-cache-expiration-range" : "Il tempo di scadenza deve essere maggiore o uguale a 0.", + "customer-name-pattern" : "Titolo cliente", + "customer-name-pattern-required" : "Titolo cliente obbligatorio", + "customer-name-pattern-hint" : "Usa $[messageKey] per estrarre valore dal messaggio e ${metadataKey} per estrarre valore dai metadati.", + "create-customer-if-not-exists" : "Crea un nuovo cliente se non esiste", + "unassign-from-customer" : "Rimuovi assegnazione da cliente specifico se l'origine è una dashboard", + "unassign-from-customer-tooltip" : "Solo le dashboard possono essere assegnate a più clienti contemporaneamente. \nSe il mittente è una dashboard, è necessario specificare esplicitamente il titolo del cliente da cui rimuovere l'assegnazione.", + "customer-cache-expiration" : "Tempo di scadenza cache clienti (sec)", + "customer-cache-expiration-hint" : "Specifica l’intervallo massimo di tempo consentito per memorizzare i clienti trovati. Il valore 0 significa che non scadranno mai.", + "customer-cache-expiration-required" : "Tempo di scadenza cache clienti obbligatorio.", + "customer-cache-expiration-range" : "Il tempo di scadenza deve essere maggiore o uguale a 0.", + "interval-start" : "Inizio intervallo", + "interval-end" : "Fine intervallo", + "time-unit" : "Unità di tempo", + "fetch-mode" : "Modalità di recupero", + "order-by-timestamp" : "Ordina per timestamp", + "limit" : "Limite", + "limit-hint" : "Il valore minimo del limite è 2, massimo 1000. Per ottenere una singola voce, seleziona 'First' o 'Last'.", + "limit-required" : "Limite obbligatorio.", + "limit-range" : "Il limite deve essere compreso tra 2 e 1000.", + "time-unit-milliseconds" : "Millisecondi", + "time-unit-seconds" : "Secondi", + "time-unit-minutes" : "Minuti", + "time-unit-hours" : "Ore", + "time-unit-days" : "Giorni", + "time-value-range" : "Intervallo consentito da 1 a 2147483647.", + "start-interval-value-required" : "Inizio intervallo obbligatorio.", + "end-interval-value-required" : "Fine intervallo obbligatorio.", + "filter" : "Filtro", + "switch" : "Interruttore", + "math-templatization-tooltip" : "Il campo supporta la templatizzazione. Usa $[messageKey] per estrarre valore dal messaggio e ${metadataKey} per estrarre valore dai metadati.", + "add-message-type" : "Aggiungi tipo di messaggio", + "select-message-types-required" : "Deve essere selezionato almeno un tipo di messaggio.", + "select-message-types" : "Seleziona tipi di messaggio", + "no-message-types-found" : "Nessun tipo di messaggio trovato", + "no-message-type-matching" : "'{{messageType}}' non trovato.", + "create-new-message-type" : "Creane uno nuovo.", + "message-types-required" : "Tipi di messaggio obbligatori.", + "client-attributes" : "Attributi client", + "shared-attributes" : "Attributi condivisi", + "server-attributes" : "Attributi server", + "attributes-keys" : "Chiavi attributi", + "attributes-keys-required" : "Chiavi attributi obbligatorie", + "attributes-scope" : "Ambito attributi", + "attributes-scope-value" : "Valore ambito attributi", + "attributes-scope-value-copy" : "Copia valore ambito attributi", + "attributes-scope-hint" : "Usa la chiave 'scope' nei metadati per impostare dinamicamente l'ambito per messaggio. Se presente, sovrascrive l’ambito impostato nella configurazione.", + "notify-device" : "Forza notifica al dispositivo", + "send-attributes-updated-notification" : "Invia notifica di attributi aggiornati", + "send-attributes-updated-notification-hint" : "Invia una notifica degli attributi aggiornati come messaggio separato alla coda del motore di regole.", + "send-attributes-deleted-notification" : "Invia notifica di attributi eliminati", + "send-attributes-deleted-notification-hint" : "Invia una notifica degli attributi eliminati come messaggio separato alla coda del motore di regole.", + "update-attributes-only-on-value-change" : "Salva attributi solo se il valore cambia", + "update-attributes-only-on-value-change-hint" : "Aggiorna gli attributi ad ogni messaggio, indipendentemente dal fatto che il valore sia cambiato. Aumenta l'uso delle API e riduce le prestazioni.", + "update-attributes-only-on-value-change-hint-enabled" : "Aggiorna gli attributi solo se il valore è cambiato. Se il valore non cambia, non verrà inviato alcun aggiornamento né notifica.", + "fetch-credentials-to-metadata" : "Recupera credenziali nei metadati", + "notify-device-on-update-hint" : "Se abilitato, forza la notifica al dispositivo dell’aggiornamento di attributi condivisi. Se disabilitato, il comportamento dipende dal parametro 'notifyDevice' nei metadati del messaggio.", + "notify-device-on-delete-hint" : "Se abilitato, forza la notifica al dispositivo della rimozione di attributi condivisi. Se disabilitato, il comportamento dipende dal parametro 'notifyDevice' nei metadati.", + "latest-timeseries" : "Chiavi serie temporali più recenti", + "timeseries-keys" : "Chiavi serie temporali", + "timeseries-keys-required" : "Deve essere selezionata almeno una chiave della serie temporale.", + "add-timeseries-key" : "Aggiungi chiave della serie temporale", + "add-message-field" : "Aggiungi campo del messaggio", + "relation-search-parameters" : "Parametri di ricerca relazione", + "relation-parameters" : "Parametri relazione", + "add-metadata-field" : "Aggiungi campo metadati", + "data-keys" : "Nomi dei campi del messaggio", + "copy-from" : "Copia da", + "data-to-metadata" : "Dati verso metadati", + "metadata-to-data" : "Metadati verso dati", + "use-regular-expression-hint" : "Utilizza espressioni regolari per copiare le chiavi in base al pattern.\n\nSuggerimenti:\nPremi 'Enter' per confermare il nome del campo.\nPremi 'Backspace' per eliminare il nome del campo. Sono supportati più nomi.", + "interval" : "Intervallo", + "interval-required" : "Intervallo richiesto", + "interval-hint" : "Intervallo di deduplicazione in secondi.", + "interval-min-error" : "Valore minimo consentito: 1", + "max-pending-msgs" : "Messaggi in attesa massimi", + "max-pending-msgs-hint" : "Numero massimo di messaggi memorizzati in memoria per ciascun ID di deduplicazione unico.", + "max-pending-msgs-required" : "Messaggi in attesa massimi richiesti", + "max-pending-msgs-max-error" : "Valore massimo consentito: 1000", + "max-pending-msgs-min-error" : "Valore minimo consentito: 1", + "max-retries" : "Massimo tentativi", + "max-retries-required" : "Massimo tentativi richiesto", + "max-retries-hint" : "Numero massimo di tentativi per inviare i messaggi deduplicati nella coda. Viene utilizzato un ritardo di 10 secondi tra i tentativi", + "max-retries-max-error" : "Valore massimo consentito: 100", + "max-retries-min-error" : "Valore minimo consentito: 0", + "strategy" : "Strategia", + "strategy-required" : "Strategia richiesta", + "strategy-all-hint" : "Restituisce tutti i messaggi arrivati durante il periodo di deduplicazione come singolo messaggio JSON array. Ogni elemento rappresenta un oggetto con proprietà msg e metadata.", + "strategy-first-hint" : "Restituisce il primo messaggio arrivato durante il periodo di deduplicazione.", + "strategy-last-hint" : "Restituisce l’ultimo messaggio arrivato durante il periodo di deduplicazione.", + "first" : "Primo", + "last" : "Ultimo", + "all" : "Tutti", + "output-msg-type-hint" : "Tipo di messaggio del risultato della deduplicazione.", + "queue-name-hint" : "Nome della coda in cui verrà pubblicato il risultato della deduplicazione.", + "keys" : "Chiavi", + "keys-required" : "Chiavi richieste", + "rename-keys-in" : "Rinomina chiavi in", + "data" : "Dati", + "message" : "Messaggio", + "metadata" : "Metadati", + "current-key-name" : "Nome chiave attuale", + "key-name-required" : "Nome chiave richiesto", + "new-key-name" : "Nuovo nome chiave", + "new-key-name-required" : "Nuovo nome chiave richiesto", + "metadata-keys" : "Nomi dei campi metadati", + "json-path-expression" : "Espressione JSON path", + "json-path-expression-required" : "Espressione JSON path richiesta", + "json-path-expression-hint" : "JSONPath specifica un percorso verso uno o più elementi in una struttura JSON. '$' rappresenta la radice.", + "relations-query" : "Query relazioni", + "device-relations-query" : "Query relazioni dispositivo", + "max-relation-level" : "Livello massimo relazione", + "max-relation-level-error" : "Il valore deve essere maggiore di 0 o non specificato.", + "max-relation-level-invalid" : "Il valore deve essere un intero.", + "relation-type" : "Tipo relazione", + "relation-type-pattern" : "Pattern tipo relazione", + "relation-type-pattern-required" : "Pattern tipo relazione richiesto", + "relation-types-list" : "Tipi relazione da propagare", + "relation-types-list-hint" : "Se i tipi di propagazione non sono selezionati, gli allarmi verranno propagati senza filtrare per tipo relazione.", + "unlimited-level" : "Livello illimitato", + "latest-telemetry" : "Telemetria più recente", + "add-telemetry-key" : "Aggiungi chiave telemetria", + "delete-from" : "Elimina da", + "use-regular-expression-delete-hint" : "Utilizza espressioni regolari per eliminare chiavi per pattern.\n\nSuggerimenti:\nPremi 'Enter' per confermare nome campo.\nPremi 'Backspace' per eliminare nome campo.\nSupportati più nomi.", + "fetch-into" : "Recupera in", + "attr-mapping" : "Mappatura attributi:", + "source-attribute" : "Chiave attributo sorgente", + "source-attribute-required" : "Chiave attributo sorgente richiesta.", + "source-telemetry" : "Chiave telemetria sorgente", + "source-telemetry-required" : "Chiave telemetria sorgente richiesta.", + "target-key" : "Chiave di destinazione", + "target-key-required" : "Chiave di destinazione richiesta.", + "attr-mapping-required" : "Deve essere specificata almeno una voce di mappatura.", + "fields-mapping" : "Mappatura campi", + "fields-mapping-hint" : "Se il campo messaggio è impostato su $entityId, l'id dell’origine sarà salvato nella colonna specificata.", + "relations-query-config-direction-suffix" : "originatore", + "profile-name" : "Nome profilo", + "fetch-circle-parameter-info-from-metadata-hint" : "Il campo metadati '{{perimeterKeyName}}' deve essere definito con il formato: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint" : "Il campo metadati '{{perimeterKeyName}}' deve essere definito con il formato: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip" : "Usa $[messageKey] per estrarre valore dal messaggio e ${metadataKey} dai metadati.", + "fields-mapping-required" : "Deve essere specificata almeno una mappatura di campo.", + "at-least-one-field-required" : "Deve essere fornito almeno un valore per un campo di input.", + "originator-fields-sv-map-hint" : "I campi chiave di destinazione supportano la templatizzazione. Usa $[messageKey] o ${metadataKey}.", + "sv-map-hint" : "Solo i campi chiave di destinazione supportano la templatizzazione. Usa $[messageKey] o ${metadataKey}.", + "source-field" : "Campo sorgente", + "source-field-required" : "Campo sorgente richiesto.", + "originator-source" : "Origine dell’origine", + "new-originator" : "Nuovo originatore", + "originator-customer" : "Cliente", + "originator-tenant" : "Tenant", + "originator-related" : "Entità correlata", + "originator-alarm-originator" : "Origine dell'allarme", + "originator-entity" : "Entità tramite nome", + "clone-message" : "Clona messaggio", + "transform" : "Trasforma", + "default-ttl" : "TTL predefinito", + "default-ttl-required" : "TTL predefinito richiesto.", + "default-ttl-hint" : "Il nodo regola recupererà il valore Time-to-Live (TTL) dai metadati del messaggio. Se non presente, verrà utilizzato quello specificato nella configurazione. Se impostato su 0, verrà applicato il TTL dalla configurazione del profilo tenant.", + "default-ttl-zero-hint" : "Il TTL non verrà applicato se il suo valore è impostato su 0.", + "min-default-ttl-message" : "È consentito solo il valore minimo TTL pari a 0.", + "generation-parameters" : "Parametri di generazione", + "message-count" : "Limite messaggi generati (0 - illimitato)", + "message-count-required" : "Limite messaggi generati richiesto.", + "min-message-count-message" : "È consentito solo il valore minimo pari a 0.", + "period-seconds" : "Periodo in secondi", + "period-seconds-required" : "Periodo richiesto.", + "generation-frequency-seconds" : "Frequenza generazione in secondi", + "generation-frequency-required" : "Frequenza generazione richiesta.", + "min-generation-frequency-message" : "È consentito solo un minimo di 60 secondi.", + "script-lang-tbel" : "TBEL", + "script-lang-js" : "JS", + "use-metadata-period-in-seconds-patterns" : "Utilizza il pattern periodo in secondi", + "use-metadata-period-in-seconds-patterns-hint" : "Se selezionato, il nodo regola utilizzerà il pattern periodo in secondi dai metadati del messaggio o dai dati, assumendo che gli intervalli siano espressi in secondi.", + "period-in-seconds-pattern" : "Pattern periodo in secondi", + "period-in-seconds-pattern-required" : "Pattern periodo in secondi richiesto", + "min-period-seconds-message" : "È consentito solo un periodo minimo di 60 secondi.", + "originator" : "Originatore", + "message-body" : "Corpo del messaggio", + "message-metadata" : "Metadati del messaggio", + "generate" : "Genera", + "current-rule-node" : "Nodo regola corrente", + "current-tenant" : "Tenant corrente", + "generator-function" : "Funzione generatrice", + "test-generator-function" : "Test funzione generatrice", + "generator" : "Generatore", + "test-filter-function" : "Test funzione filtro", + "test-switch-function" : "Test funzione switch", + "test-transformer-function" : "Test funzione trasformazione", + "transformer" : "Trasformatore", + "alarm-create-condition" : "Condizione di creazione allarme", + "test-condition-function" : "Test funzione condizione", + "alarm-clear-condition" : "Condizione di cancellazione allarme", + "alarm-details-builder" : "Generatore dettagli allarme", + "test-details-function" : "Test funzione dettagli", + "alarm-type" : "Tipo di allarme", + "select-entity-types" : "Seleziona tipi entità", + "alarm-type-required" : "Tipo di allarme richiesto.", + "alarm-severity" : "Gravità dell’allarme", + "alarm-severity-required" : "Gravità dell’allarme richiesta", + "alarm-severity-pattern" : "Pattern gravità dell’allarme", + "alarm-status-filter" : "Filtro stato allarme", + "alarm-status-list-empty" : "Elenco stato allarme vuoto", + "no-alarm-status-matching" : "Nessuna corrispondenza stato allarme trovata.", + "propagate" : "Propaga allarme alle entità correlate", + "propagate-to-owner" : "Propaga allarme al proprietario dell'entità (Cliente o Tenant)", + "propagate-to-tenant" : "Propaga allarme al Tenant", + "condition" : "Condizione", + "details" : "Dettagli", + "to-string" : "Converti in stringa", + "test-to-string-function" : "Test funzione di conversione in stringa", + "from-template" : "Da", + "from-template-required" : "Campo 'Da' richiesto", + "message-to-metadata" : "Messaggio a metadati", + "metadata-to-message" : "Metadati a messaggio", + "from-message" : "Dal messaggio", + "from-metadata" : "Dai metadati", + "to-template" : "A", + "to-template-required" : "Campo 'A' richiesto", + "mail-address-list-template-hint" : "Elenco di indirizzi separati da virgole. Usa ${metadataKey} per valori dai metadati, $[messageKey] per valori dal corpo del messaggio", + "cc-template" : "Cc", + "bcc-template" : "Bcc", + "subject-template" : "Oggetto", + "subject-template-required" : "Oggetto richiesto", + "body-template" : "Corpo", + "body-template-required" : "Corpo richiesto", + "dynamic-mail-body-type" : "Tipo dinamico corpo email", + "mail-body-type" : "Tipo corpo email", + "body-type-template" : "Template tipo corpo", + "reply-routing-configuration" : "Configurazione routing di risposta", + "rpc-reply-routing-configuration-hint" : "Queste configurazioni specificano i nomi delle chiavi nei metadati per identificare servizio, sessione e richiesta per la risposta.", + "reply-routing-configuration-hint" : "Queste configurazioni specificano i nomi delle chiavi nei metadati per identificare servizio e richiesta per la risposta.", + "request-id-metadata-attribute" : "Id richiesta", + "service-id-metadata-attribute" : "Id servizio", + "session-id-metadata-attribute" : "Id sessione", + "timeout-sec" : "Timeout in secondi", + "timeout-required" : "Timeout richiesto", + "min-timeout-message" : "È consentito solo un valore minimo di 0 per il timeout.", + "endpoint-url-pattern" : "Pattern URL endpoint", + "endpoint-url-pattern-required" : "Pattern URL endpoint richiesto", + "request-method" : "Metodo richiesta", + "use-simple-client-http-factory" : "Utilizza client HTTP semplice", + "ignore-request-body" : "Senza corpo richiesta", + "parse-to-plain-text" : "Analizza come testo semplice", + "parse-to-plain-text-hint" : "Se selezionato, il payload del corpo del messaggio della richiesta verrà trasformato da stringa JSON a testo semplice, ad esempio msg = \"Hello,\\t\"world\"\" verrà analizzato come Hello, \"world\"", + "read-timeout" : "Timeout di lettura in millisecondi", + "read-timeout-hint" : "Il valore 0 significa timeout infinito", + "max-parallel-requests-count" : "Numero massimo di richieste parallele", + "max-parallel-requests-count-hint" : "Il valore 0 specifica nessun limite nell'elaborazione parallela", + "max-response-size" : "Dimensione massima della risposta (in KB)", + "max-response-size-hint" : "Quantità massima di memoria allocata per il buffering dei dati durante la decodifica o codifica di messaggi HTTP, come payload JSON o XML", + "headers" : "Intestazioni", + "headers-hint" : "Usa ${metadataKey} per ottenere un valore dai metadati, $[messageKey] per ottenere un valore dal corpo del messaggio nei campi intestazione/valore", + "header" : "Intestazione", + "header-required" : "Intestazione richiesto", + "value" : "Valore", + "value-required" : "Valore richiesto", + "topic-pattern" : "Pattern del topic", + "key-pattern" : "Pattern della chiave", + "key-pattern-hint" : "Opzionale. Se viene specificato un numero di partizione valido, verrà utilizzato per l'invio del record. Se non è specificata alcuna partizione, verrà usata la chiave. Se nessuno dei due è specificato, una partizione verrà assegnata in modo round-robin.", + "topic-pattern-required" : "Pattern del topic richiesto", + "topic" : "Topic", + "topic-required" : "Topic richiesto", + "bootstrap-servers" : "Server bootstrap", + "bootstrap-servers-required" : "Valore dei server bootstrap richiesto", + "other-properties" : "Altre proprietà", + "key" : "Chiave", + "key-required" : "Chiave richiesta", + "retries" : "Numero di tentativi automatici in caso di errore", + "min-retries-message" : "È consentito solo un valore minimo di 0 per i tentativi.", + "batch-size-bytes" : "Dimensione del batch di produzione in byte", + "min-batch-size-bytes-message" : "È consentito solo un valore minimo di 0 per la dimensione del batch.", + "linger-ms" : "Tempo di buffer locale (ms)", + "min-linger-ms-message" : "È consentito solo un valore minimo di 0 ms.", + "buffer-memory-bytes" : "Dimensione massima del buffer client in byte", + "min-buffer-memory-message" : "È consentito solo un valore minimo di 0 per il buffer.", + "memory-buffer-size-range" : "La dimensione del buffer deve essere tra 0 e {{max}} KB", + "acks" : "Numero di conferme", + "topic-arn-pattern" : "Pattern ARN del topic", + "topic-arn-pattern-required" : "Pattern ARN del topic richiesto", + "aws-access-key-id" : "ID chiave di accesso AWS", + "aws-access-key-id-required" : "ID chiave di accesso AWS richiesto", + "aws-secret-access-key" : "Chiave di accesso segreta AWS", + "aws-secret-access-key-required" : "Chiave di accesso segreta AWS richiesta", + "aws-region" : "Regione AWS", + "aws-region-required" : "Regione AWS richiesta", + "exchange-name-pattern" : "Pattern del nome dell’exchange", + "routing-key-pattern" : "Pattern della chiave di routing", + "message-properties" : "Proprietà del messaggio", + "host" : "Host", + "host-required" : "Host richiesto", + "port" : "Porta", + "port-required" : "Porta richiesta", + "port-range" : "La porta deve essere compresa tra 1 e 65535.", + "virtual-host" : "Host virtuale", + "username" : "Nome utente", + "password" : "Password", + "automatic-recovery" : "Ripristino automatico", + "connection-timeout-ms" : "Timeout di connessione (ms)", + "min-connection-timeout-ms-message" : "È consentito solo un valore minimo di 0 ms.", + "handshake-timeout-ms" : "Timeout handshake (ms)", + "min-handshake-timeout-ms-message" : "È consentito solo un valore minimo di 0 ms.", + "client-properties" : "Proprietà client", + "queue-url-pattern" : "Pattern URL della coda", + "queue-url-pattern-required" : "Pattern URL della coda richiesto", + "delay-seconds" : "Ritardo (secondi)", + "min-delay-seconds-message" : "È consentito solo un valore minimo di 0 secondi.", + "max-delay-seconds-message" : "È consentito solo un valore massimo di 900 secondi.", + "name" : "Nome", + "name-required" : "Nome richiesto", + "queue-type" : "Tipo di coda", + "sqs-queue-standard" : "Standard", + "sqs-queue-fifo" : "FIFO", + "gcp-project-id" : "ID progetto GCP", + "gcp-project-id-required" : "ID progetto GCP richiesto", + "gcp-service-account-key" : "File chiave account di servizio GCP", + "gcp-service-account-key-required" : "File chiave account di servizio GCP richiesto", + "pubsub-topic-name" : "Nome topic", + "pubsub-topic-name-required" : "Nome topic richiesto", + "message-attributes" : "Attributi del messaggio", + "message-attributes-hint" : "Usa ${metadataKey} per il valore dai metadati, $[messageKey] per il valore dal corpo del messaggio nei campi nome/valore", + "connect-timeout" : "Timeout di connessione (sec)", + "connect-timeout-required" : "Timeout di connessione richiesto.", + "connect-timeout-range" : "Il timeout di connessione deve essere compreso tra 1 e 200.", + "client-id" : "ID client", + "client-id-hint" : "Opzionale. Lasciare vuoto per generare automaticamente l'ID client. Attenzione quando si specifica l'ID client. La maggior parte dei broker MQTT non consente più connessioni con lo stesso ID client. Per connettersi a tali broker, l'ID client MQTT deve essere univoco. Quando la piattaforma è in modalità micro-servizi, una copia del nodo regola viene avviata in ciascun micro-servizio, il che porterà automaticamente a più client MQTT con lo stesso ID e potrebbe causare errori del nodo regola. Per evitare tali errori, abilitare l'opzione \"Aggiungi ID Servizio come suffisso all'ID Client\" qui sotto.", + "append-client-id-suffix" : "Aggiungi ID Servizio come suffisso all'ID Client", + "client-id-suffix-hint" : "Opzionale. Applicato quando \"ID Client\" è specificato esplicitamente. Se selezionato, l'ID Servizio sarà aggiunto all'ID Client come suffisso. Aiuta a evitare errori quando la piattaforma è in modalità micro-servizi.", + "device-id" : "ID Dispositivo", + "device-id-required" : "ID Dispositivo richiesto.", + "clean-session" : "Sessione pulita", + "enable-ssl" : "Abilita SSL", + "credentials" : "Credenziali", + "credentials-type" : "Tipo di credenziali", + "credentials-type-required" : "Tipo di credenziali richiesto.", + "credentials-anonymous" : "Anonimo", + "credentials-basic" : "Base", + "credentials-pem" : "PEM", + "credentials-pem-hint" : "È richiesto almeno il file del certificato CA del server o una coppia di file certificato e chiave privata del client", + "credentials-sas" : "Firma di accesso condivisa", + "sas-key" : "Chiave SAS", + "sas-key-required" : "Chiave SAS richiesta.", + "hostname" : "Nome host", + "hostname-required" : "Nome host richiesto.", + "azure-ca-cert" : "File certificato CA", + "username-required" : "Nome utente richiesto.", + "password-required" : "Password richiesta.", + "ca-cert" : "File certificato CA del server", + "private-key" : "File chiave privata del client", + "cert" : "File certificato del client", + "no-file" : "Nessun file selezionato.", + "drop-file" : "Trascina un file o clicca per selezionarlo da caricare.", + "private-key-password" : "Password della chiave privata", + "use-system-smtp-settings" : "Usa impostazioni SMTP di sistema", + "use-metadata-dynamic-interval" : "Usa intervallo dinamico", + "metadata-dynamic-interval-hint" : "I campi di input dell'intervallo iniziale e finale supportano la templatizzazione. Il valore del template sostituito deve essere impostato in millisecondi. Usa $[messageKey] per estrarre il valore dal messaggio e ${metadataKey} per estrarre il valore dai metadati.", + "use-metadata-interval-patterns-hint" : "Se selezionato, il nodo regola utilizza i pattern dell'intervallo iniziale e finale dai metadati o dati del messaggio, assumendo che siano in millisecondi.", + "use-message-alarm-data" : "Usa i dati dell'allarme dal messaggio", + "overwrite-alarm-details" : "Sovrascrivi i dettagli dell'allarme", + "use-alarm-severity-pattern" : "Usa pattern per la severità dell'allarme", + "check-all-keys" : "Verifica che tutti i campi specificati siano presenti", + "check-all-keys-hint" : "Se selezionato, controlla che tutte le chiavi specificate siano presenti nei dati e metadati del messaggio.", + "check-relation-to-specific-entity" : "Verifica relazione con entità specifica", + "check-relation-to-specific-entity-tooltip" : "Se abilitato, verifica la presenza di una relazione con una specifica entità, altrimenti con qualsiasi entità. In entrambi i casi, la ricerca della relazione si basa su direzione e tipo configurati.", + "check-relation-hint" : "Verifica l'esistenza di una relazione con entità specifica o con qualsiasi entità in base a direzione e tipo.", + "delete-relation-with-specific-entity" : "Elimina relazione con entità specifica", + "delete-relation-with-specific-entity-hint" : "Se abilitato, eliminerà solo la relazione con una specifica entità. Altrimenti, verrà rimossa con tutte le entità corrispondenti.", + "delete-relation-hint" : "Elimina la relazione dall’origine del messaggio in arrivo all’entità specificata o alla lista di entità basata su direzione e tipo.", + "remove-current-relations" : "Rimuovi relazioni attuali", + "remove-current-relations-hint" : "Rimuove le relazioni attuali dall’origine del messaggio in arrivo in base a direzione e tipo.", + "change-originator-to-related-entity" : "Cambia origine con entità correlata", + "change-originator-to-related-entity-hint" : "Utilizzato per elaborare un messaggio come proveniente da un'altra entità.", + "start-interval" : "Inizio intervallo", + "end-interval" : "Fine intervallo", + "start-interval-required" : "Inizio intervallo richiesto.", + "end-interval-required" : "Fine intervallo richiesto.", + "smtp-protocol" : "Protocollo", + "smtp-host" : "Host SMTP", + "smtp-host-required" : "Host SMTP richiesto.", + "smtp-port" : "Porta SMTP", + "smtp-port-required" : "Devi specificare una porta SMTP.", + "smtp-port-range" : "La porta SMTP deve essere compresa tra 1 e 65535.", + "timeout-msec" : "Timeout (ms)", + "min-timeout-msec-message" : "È consentito solo un valore minimo di 0 ms.", + "enter-username" : "Inserisci nome utente", + "enter-password" : "Inserisci password", + "enable-tls" : "Abilita TLS", + "tls-version" : "Versione TLS", + "enable-proxy" : "Abilita proxy", + "use-system-proxy-properties" : "Usa proprietà proxy di sistema", + "proxy-host" : "Host proxy", + "proxy-host-required" : "Host proxy richiesto.", + "proxy-port" : "Porta proxy", + "proxy-port-required" : "Porta proxy richiesta.", + "proxy-port-range" : "La porta proxy deve essere compresa tra 1 e 65535.", + "proxy-user" : "Utente proxy", + "proxy-password" : "Password proxy", + "proxy-scheme" : "Schema proxy", + "numbers-to-template" : "Numeri di telefono al template", + "numbers-to-template-required" : "Numeri di telefono al template richiesti", + "numbers-to-template-hint" : "Numeri di telefono separati da virgola, usa ${metadataKey} per il valore dai metadati, $[messageKey] per il valore dal corpo del messaggio", + "sms-message-template" : "Template messaggio SMS", + "sms-message-template-required" : "Template messaggio SMS richiesto", + "use-system-sms-settings" : "Usa impostazioni provider SMS di sistema", + "min-period-0-seconds-message" : "È consentito solo un periodo minimo di 0 secondi.", + "max-pending-messages" : "Messaggi in attesa massimi", + "max-pending-messages-required" : "Messaggi in attesa massimi richiesti.", + "max-pending-messages-range" : "Messaggi in attesa massimi devono essere compresi tra 1 e 100000.", + "originator-types-filter" : "Filtro tipi originatore", + "interval-seconds" : "Intervallo in secondi", + "interval-seconds-required" : "Intervallo richiesto.", + "int-range" : "Il valore non deve superare il limite massimo di un intero (2147483648)", + "min-interval-seconds-message" : "È consentito solo un intervallo minimo di 1 secondo.", + "output-timeseries-key-prefix" : "Prefisso chiave serie temporale in uscita", + "output-timeseries-key-prefix-required" : "Prefisso chiave serie temporale in uscita richiesto.", + "separator-hint" : "Premere \"Invio\" per completare l'inserimento del campo.", + "select-details" : "Seleziona dettagli", + "entity-details-id" : "Id", + "entity-details-title" : "Titolo", + "entity-details-country" : "Paese", + "entity-details-state" : "Stato", + "entity-details-city" : "Città", + "entity-details-zip" : "CAP", + "entity-details-address" : "Indirizzo", + "entity-details-address2" : "Indirizzo 2", + "entity-details-additional_info" : "Informazioni aggiuntive", + "entity-details-phone" : "Telefono", + "entity-details-email" : "Email", + "email-sender" : "Mittente email", + "fields-to-check" : "Campi da verificare", + "add-detail" : "Aggiungi dettaglio", + "check-all-keys-tooltip" : "Se abilitato, verifica la presenza di tutti i campi elencati nei nomi dei campi del messaggio e dei metadati all'interno del messaggio in arrivo e dei suoi metadati.", + "fields-to-check-hint" : "Premi \"Invio\" per completare l'inserimento del nome campo. Supportati più nomi campo.", + "entity-details-list-empty" : "Almeno un dettaglio deve essere selezionato.", + "alarm-status" : "Stato allarme", + "alarm-required" : "Almeno uno stato di allarme deve essere selezionato.", + "no-entity-details-matching" : "Nessun dettaglio entità corrispondente trovato.", + "custom-table-name" : "Nome tabella personalizzato", + "custom-table-name-required" : "Il nome della tabella è richiesto", + "custom-table-hint" : "La tabella deve essere creata nel tuo cluster Cassandra e il suo nome deve iniziare con il prefisso 'cs_tb_' per evitare l'inserimento dei dati nelle tabelle TB comuni. Inserisci qui il nome della tabella senza il prefisso 'cs_tb_'.", + "message-field" : "Campo messaggio", + "message-field-required" : "Campo messaggio richiesto.", + "table-col" : "Colonna tabella", + "table-col-required" : "Colonna tabella richiesta.", + "latitude-field-name" : "Nome campo latitudine", + "longitude-field-name" : "Nome campo longitudine", + "latitude-field-name-required" : "Nome campo latitudine richiesto.", + "longitude-field-name-required" : "Nome campo longitudine richiesto.", + "fetch-perimeter-info-from-metadata" : "Recupera informazioni perimetro dai metadati", + "fetch-perimeter-info-from-metadata-tooltip" : "Se il tipo di perimetro è 'Poligono', il valore del campo metadati '{{perimeterKeyName}}' sarà usato come definizione del perimetro senza ulteriori analisi. Se è 'Cerchio', il valore verrà analizzato per estrarre i campi 'latitude', 'longitude', 'radius', 'radiusUnit' per la definizione del cerchio.", + "perimeter-key-name" : "Nome chiave perimetro", + "perimeter-key-name-hint" : "Nome campo metadati contenente le informazioni sul perimetro.", + "perimeter-key-name-required" : "Nome chiave perimetro richiesto.", + "perimeter-circle" : "Cerchio", + "perimeter-polygon" : "Poligono", + "perimeter-type" : "Tipo di perimetro", + "circle-center-latitude" : "Latitudine centro", + "circle-center-latitude-required" : "Latitudine centro richiesta.", + "circle-center-longitude" : "Longitudine centro", + "circle-center-longitude-required" : "Longitudine centro richiesta.", + "range-unit-meter" : "Metro", + "range-unit-kilometer" : "Chilometro", + "range-unit-foot" : "Piede", + "range-unit-mile" : "Miglio", + "range-unit-nautical-mile" : "Miglio nautico", + "range-units" : "Unità di misura distanza", + "range-units-required" : "Unità di misura distanza richiesta.", + "range" : "Raggio", + "range-required" : "Raggio richiesto.", + "polygon-definition" : "Definizione poligono", + "polygon-definition-required" : "Definizione poligono richiesta.", + "polygon-definition-hint" : "Usa il seguente formato per la definizione manuale del poligono: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration" : "Durata minima all'interno", + "min-inside-duration-value-required" : "Durata minima all'interno richiesta", + "min-inside-duration-time-unit" : "Unità di tempo durata interna", + "min-outside-duration" : "Durata minima all'esterno", + "min-outside-duration-value-required" : "Durata minima all'esterno richiesta", + "min-outside-duration-time-unit" : "Unità di tempo durata esterna", + "tell-failure-if-absent" : "Segnala fallimento", + "tell-failure-if-absent-hint" : "Se almeno una delle chiavi selezionate non esiste, il messaggio in uscita riporterà \"Fallimento\".", + "get-latest-value-with-ts" : "Recupera timestamp per gli ultimi valori di telemetria", + "get-latest-value-with-ts-hint" : "Se selezionato, i valori più recenti della telemetria includeranno anche il timestamp, es: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings" : "Ignora stringhe nulle", + "ignore-null-strings-hint" : "Se selezionato, il nodo regola ignorerà i campi entità con valore vuoto.", + "add-metadata-key-values-as-kafka-headers" : "Aggiungi metadati come header Kafka", + "add-metadata-key-values-as-kafka-headers-hint" : "Se selezionato, le coppie chiave-valore dei metadati verranno aggiunte agli header dei record Kafka in uscita come array di byte con codifica charset predefinita.", + "charset-encoding" : "Codifica charset", + "charset-encoding-required" : "Codifica charset richiesta.", + "charset-us-ascii" : "US-ASCII", + "charset-iso-8859-1" : "ISO-8859-1", + "charset-utf-8" : "UTF-8", + "charset-utf-16be" : "UTF-16BE", + "charset-utf-16le" : "UTF-16LE", + "charset-utf-16" : "UTF-16", + "select-queue-hint" : "Il nome della coda può essere selezionato da un elenco a discesa o inserito personalizzato.", + "device-profile-node-hint" : "Utile se hai condizioni di durata o ripetizione per garantire la continuità della valutazione dello stato dell'allarme.", + "persist-alarm-rules" : "Memorizza stato delle regole allarme", + "persist-alarm-rules-hint" : "Se abilitato, il nodo regola salverà lo stato dell'elaborazione nel database.", + "fetch-alarm-rules" : "Recupera stato delle regole allarme", + "fetch-alarm-rules-hint" : "Se abilitato, il nodo regola ripristinerà lo stato dell'elaborazione all'inizializzazione e garantirà che gli allarmi vengano generati anche dopo riavvii del server.", + "input-value-key" : "Chiave valore di input", + "input-value-key-required" : "Chiave del valore di input richiesta.", + "output-value-key" : "Chiave del valore di output", + "output-value-key-required" : "Chiave del valore di output richiesta.", + "number-of-digits-after-floating-point" : "Numero di cifre dopo il punto decimale", + "number-of-digits-after-floating-point-range" : "Il numero di cifre dopo il punto decimale deve essere compreso tra 0 e 15.", + "failure-if-delta-negative" : "Segnala errore se il delta è negativo", + "failure-if-delta-negative-tooltip" : "Il nodo regola forza l'errore del messaggio se il valore delta è negativo.", + "use-caching" : "Usa caching", + "use-caching-tooltip" : "Il nodo regola memorizzerà nella cache il valore di \"{{inputValueKey}}\" proveniente dal messaggio in arrivo per migliorare le prestazioni. La cache non verrà aggiornata se il valore di \"{{inputValueKey}}\" viene modificato altrove.", + "add-time-difference-between-readings" : "Aggiungi la differenza di tempo tra le letture di \"{{inputValueKey}}\"", + "add-time-difference-between-readings-tooltip" : "Se abilitato, il nodo regola aggiungerà \"{{periodValueKey}}\" al messaggio in uscita.", + "period-value-key" : "Chiave del valore del periodo", + "period-value-key-required" : "Chiave del valore del periodo richiesta.", + "general-pattern-hint" : "Usa ${metadataKey} per il valore dai metadati, $[messageKey] per il valore dal corpo del messaggio.", + "alarm-severity-pattern-hint" : "Usa ${metadataKey} per il valore dai metadati, $[messageKey] per il valore dal corpo del messaggio. La severità dell'allarme dovrebbe essere di sistema (CRITICAL, MAJOR ecc.)", + "output-node-name-hint" : "Il nome del nodo regola corrisponde al tipo di relazione del messaggio in uscita ed è utilizzato per inoltrare i messaggi ad altri nodi regola nella catena di regole chiamante.", + "use-server-ts" : "Usa timestamp del server", + "use-server-ts-hint" : "Utilizza il timestamp corrente del server per i dati della serie temporale che non hanno timestamp esplicito. Utile per mantenere l'ordine dei messaggi da fonti multiple o fuori sequenza.", + "kv-map-pattern-hint" : "Tutti i campi di input supportano la templatizzazione. Usa $[messageKey] per estrarre il valore dal messaggio e ${metadataKey} per estrarlo dai metadati.", + "kv-map-single-pattern-hint" : "Il campo di input supporta la templatizzazione. Usa $[messageKey] per estrarre il valore dal messaggio e ${metadataKey} per estrarlo dai metadati.", + "shared-scope" : "Ambito condiviso", + "server-scope" : "Ambito server", + "client-scope" : "Ambito client", + "attribute-type" : "Attributo", + "attribute-type-description" : "Recupera il valore dell'attributo dal database", + "attribute-type-result-description" : "Memorizza il risultato come attributo dell'entità nel database", + "constant-type" : "Costante", + "constant-type-description" : "Definisce un valore costante", + "time-series-type" : "Serie temporale", + "time-series-type-description" : "Recupera l'ultimo valore della serie temporale dal database", + "time-series-type-result-description" : "Memorizza il risultato come serie temporale dell'entità nel database", + "message-body-type" : "Messaggio", + "message-body-type-description" : "Recupera il valore dell'argomento dal messaggio in arrivo", + "message-body-type-result-description" : "Aggiunge il risultato al messaggio in uscita", + "message-metadata-type" : "Metadati", + "message-metadata-type-description" : "Recupera il valore dell'argomento dai metadati del messaggio", + "message-metadata-result-description" : "Aggiunge il risultato ai metadati del messaggio in uscita", + "argument-tile" : "Argomenti", + "no-arguments-prompt" : "Nessun argomento configurato", + "result-title" : "Risultato", + "functions-field-input" : "Funzioni", + "no-option-found" : "Nessuna opzione trovata", + "argument-source-field-input" : "Origine", + "argument-source-field-input-required" : "Origine dell'argomento richiesta.", + "argument-key-field-input" : "Chiave", + "argument-key-field-input-required" : "Chiave dell'argomento richiesta.", + "constant-value-field-input" : "Valore costante", + "constant-value-field-input-required" : "Valore costante richiesto.", + "attribute-scope-field-input" : "Ambito attributo", + "attribute-scope-field-input-required" : "Ambito attributo richiesto.", + "default-value-field-input" : "Valore predefinito", + "type-field-input" : "Tipo", + "type-field-input-required" : "Tipo richiesto.", + "key-field-input" : "Chiave", + "add-entity-type" : "Aggiungi tipo di entità", + "add-device-profile" : "Aggiungi profilo dispositivo", + "key-field-input-required" : "Chiave richiesta.", + "number-floating-point-field-input" : "Numero di cifre dopo la virgola", + "number-floating-point-field-input-hint" : "Usa 0 per convertire il risultato in intero", + "add-to-message-field-input" : "Aggiungi al messaggio", + "add-to-metadata-field-input" : "Aggiungi ai metadati", + "custom-expression-field-input" : "Espressione matematica", + "custom-expression-field-input-required" : "Espressione matematica richiesta", + "custom-expression-field-input-hint" : "Specifica un'espressione matematica da valutare. L'espressione predefinita mostra la conversione da Fahrenheit a Celsius", + "retained-message" : "Messaggio mantenuto", + "attributes-mapping" : "Mappatura attributi", + "latest-telemetry-mapping" : "Mappatura ultima telemetria", + "add-mapped-attribute-to" : "Aggiungi attributi mappati a", + "add-mapped-latest-telemetry-to" : "Aggiungi ultima telemetria mappata a", + "add-mapped-fields-to" : "Aggiungi campi mappati a", + "add-selected-details-to" : "Aggiungi dettagli selezionati a", + "clear-selected-types" : "Pulisci tipi selezionati", + "clear-selected-details" : "Pulisci dettagli selezionati", + "clear-selected-fields" : "Pulisci campi selezionati", + "clear-selected-keys" : "Pulisci chiavi selezionate", + "geofence-configuration" : "Configurazione geofence", + "coordinate-field-names" : "Nomi dei campi di coordinate", + "coordinate-field-hint" : "Il nodo regola cerca di recuperare i campi specificati dal messaggio. Se non presenti, li cerca nei metadati.", + "presence-monitoring-strategy" : "Strategia di monitoraggio della presenza", + "presence-monitoring-strategy-on-first-message" : "Al primo messaggio", + "presence-monitoring-strategy-on-each-message" : "A ogni messaggio", + "presence-monitoring-strategy-on-first-message-hint" : "Segnala stato 'Dentro' o 'Fuori' al primo messaggio dopo che è trascorso il tempo minimo dalla precedente modifica dello stato di presenza.", + "presence-monitoring-strategy-on-each-message-hint" : "Segnala stato 'Dentro' o 'Fuori' a ogni messaggio dopo una modifica dello stato di presenza.", + "fetch-credentials-to" : "Recupera credenziali in", + "add-originator-attributes-to" : "Aggiungi attributi dell'origine a", + "originator-attributes" : "Attributi dell'origine", + "fetch-latest-telemetry-with-timestamp" : "Recupera ultima telemetria con timestamp", + "fetch-latest-telemetry-with-timestamp-tooltip" : "Se selezionato, i valori più recenti della telemetria verranno aggiunti ai metadati in uscita con timestamp, ad es: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure" : "Segnala errore se mancano attributi", + "tell-failure-tooltip" : "Se almeno una chiave selezionata non esiste, il messaggio in uscita riporterà \"Errore\".", + "created-time" : "Ora di creazione", + "chip-help" : "Premi 'Invio' per completare l'inserimento di {{inputName}}. \nPremi 'Backspace' per cancellare {{inputName}}. \nSupportati valori multipli.", + "detail" : "dettaglio", + "field-name" : "Nome campo", + "device-profile" : "Profilo dispositivo", + "entity-type" : "Tipo di entità", + "message-type" : "Tipo di messaggio", + "timeseries-key" : "Chiave serie temporale", + "type" : "Tipo", + "first-name" : "Nome", + "last-name" : "Cognome", + "label" : "Etichetta", + "originator-fields-mapping" : "Mappatura campi dell'origine", + "add-mapped-originator-fields-to" : "Aggiungi campi dell'origine mappati a", + "fields" : "Campi", + "skip-empty-fields" : "Ignora campi vuoti", + "skip-empty-fields-tooltip" : "I campi con valori vuoti non verranno aggiunti al messaggio/metadati in uscita.", + "fetch-interval" : "Intervallo di recupero", + "fetch-strategy" : "Strategia di recupero", + "fetch-timeseries-from-to" : "Recupera serie temporale da {{startInterval}} {{startIntervalTimeUnit}} fa a {{endInterval}} {{endIntervalTimeUnit}} fa.", + "fetch-timeseries-from-to-invalid" : "Recupero serie temporale non valido (\"Intervallo iniziale\" deve essere inferiore a \"Intervallo finale\").", + "use-metadata-dynamic-interval-tooltip" : "Se selezionato, il nodo regola utilizzerà un intervallo dinamico basato su modelli di messaggio e metadati.", + "all-mode-hint" : "Se selezionata la modalità 'Tutto', il nodo regola recupererà la telemetria dall'intervallo di recupero con parametri configurabili.", + "first-mode-hint" : "Se selezionata la modalità 'Primo', il nodo regola recupererà la telemetria più vicina all'inizio dell'intervallo.", + "last-mode-hint" : "Se selezionata la modalità 'Ultimo', il nodo regola recupererà la telemetria più vicina alla fine dell'intervallo.", + "ascending" : "Ascendente", + "descending" : "Discendente", + "min" : "Minimo", + "max" : "Massimo", + "average" : "Media", + "sum" : "Somma", + "count" : "Conteggio", + "none" : "Nessuno", + "last-level-relation-tooltip" : "Se selezionato, verranno cercate entità correlate solo al livello massimo configurato.", + "last-level-device-relation-tooltip" : "Se selezionato, verranno cercati dispositivi correlati solo al livello massimo configurato.", + "data-to-fetch" : "Dati da recuperare", + "mapping-of-customers" : "Mappatura del cliente", + "map-fields-required" : "Tutti i campi di mappatura sono richiesti.", + "attributes" : "Attributi", + "related-device-attributes" : "Attributi del dispositivo correlato", + "add-selected-attributes-to" : "Aggiungi attributi selezionati a", + "device-profiles" : "Profili dispositivi", + "mapping-of-tenant" : "Mappatura del tenant", + "add-attribute-key" : "Aggiungi chiave attributo", + "message-template" : "Template del messaggio", + "message-template-required" : "Il template del messaggio è richiesto", + "use-system-slack-settings" : "Usa impostazioni di sistema per Slack", + "slack-api-token" : "Token API di Slack", + "slack-api-token-required" : "Token API di Slack richiesto", + "keys-mapping" : "Mappatura chiavi", + "add-key" : "Aggiungi chiave", + "recipients" : "Destinatari", + "message-subject-and-content" : "Oggetto e contenuto del messaggio", + "template-rules-hint" : "Entrambi i campi supportano la templatizzazione. Usa $[messageKey] per estrarre dal messaggio e ${metadataKey} dai metadati.", + "originator-customer-desc" : "Usa il cliente dell'origine come nuovo originatore.", + "originator-tenant-desc" : "Usa il tenant attuale come nuovo originatore.", + "originator-related-entity-desc" : "Usa un'entità correlata come nuovo originatore. Ricerca basata su tipo e direzione della relazione.", + "originator-alarm-originator-desc" : "Usa l'origine dell'allarme come nuovo originatore. Solo se l'origine del messaggio è un'entità di allarme.", + "originator-entity-by-name-pattern-desc" : "Usa l'entità recuperata dal DB come nuovo originatore. Ricerca basata su tipo e modello di nome.", + "email-from-template-hint" : "Usa $[messageKey] per estrarre dal messaggio e ${metadataKey} dai metadati.", + "recipients-block-main-hint" : "Lista di indirizzi separati da virgole. Tutti i campi supportano la templatizzazione.", + "forward-msg-default-rule-chain" : "Inoltra messaggio alla regola predefinita dell'origine", + "forward-msg-default-rule-chain-tooltip" : "Se abilitato, il messaggio sarà inoltrato alla regola predefinita dell'origine o a quella configurata.", + "exclude-zero-deltas" : "Escludi delta zero dal messaggio in uscita", + "exclude-zero-deltas-hint" : "Se abilitato, la chiave \"{{outputValueKey}}\" sarà aggiunta solo se il valore non è zero.", + "exclude-zero-deltas-time-difference-hint" : "Se abilitato, le chiavi \"{{outputValueKey}}\" e \"{{periodValueKey}}\" saranno aggiunte solo se \"{{outputValueKey}}\" non è zero.", + "search-direction-from" : "Dall'origine all'entità di destinazione", + "search-direction-to" : "Dall'entità di destinazione all'origine", + "del-relation-direction-from" : "Dall'origine", + "del-relation-direction-to" : "All'origine", + "target-entity" : "Entità di destinazione", + "function-configuration" : "Configurazione funzione", + "function-name" : "Nome funzione", + "function-name-required" : "Nome funzione richiesto.", + "qualifier" : "Qualificatore", + "qualifier-hint" : "Se non specificato, verrà usato \"$LATEST\".", + "aws-credentials" : "Credenziali AWS", + "connection-timeout" : "Timeout connessione", + "connection-timeout-required" : "Timeout connessione richiesto.", + "connection-timeout-min" : "Timeout minimo connessione è 0.", + "connection-timeout-hint" : "Tempo massimo in secondi per stabilire connessione. 0 significa infinito (sconsigliato).", + "request-timeout" : "Timeout richiesta", + "request-timeout-required" : "Timeout richiesta richiesto.", + "request-timeout-min" : "Timeout minimo richiesta è 0", + "request-timeout-hint" : "Tempo massimo in secondi per completare la richiesta. 0 significa infinito (sconsigliato).", + "units" : "Unità", + "tell-failure-aws-lambda" : "Segnala errore se AWS Lambda solleva eccezione", + "tell-failure-aws-lambda-hint" : "Il nodo forza un errore se l'esecuzione della funzione Lambda solleva un'eccezione.", + "basic-mode" : "Base", + "advanced-mode" : "Avanzato", + "save-time-series" : { + "processing-settings" : "Impostazioni di elaborazione", + "processing-settings-hint" : "Definisci come vengono elaborati i messaggi in arrivo. Le impostazioni di base consentono di selezionare strategie preconfigurate, mentre le impostazioni avanzate permettono di selezionare strategie individuali per ciascuna azione.", + "advanced-settings-hint" : "Fai attenzione quando configuri le strategie di elaborazione. Alcune combinazioni possono portare a comportamenti imprevisti.", + "strategy" : "Strategia", + "deduplication-interval" : "Intervallo di deduplicazione", + "deduplication-interval-required" : "L'intervallo di deduplicazione è richiesto", + "deduplication-interval-min-max-range" : "L'intervallo di deduplicazione deve essere almeno di 1 secondo e massimo di 1 giorno", + "strategy-type" : { + "every-message" : "Su ogni messaggio", + "skip" : "Salta", + "deduplicate" : "Deduplica", + "web-sockets-only" : "Solo WebSockets" + }, + "time-series" : "Serie temporale", + "latest" : "Ultimi valori", + "web-sockets" : "WebSockets", + "calculated-fields" : "Campi calcolati" + }, + "save-attribute" : { + "processing-settings" : "Impostazioni di elaborazione", + "processing-settings-hint" : "Definisci come vengono elaborati i messaggi in arrivo. Le impostazioni di base consentono di selezionare strategie preconfigurate, mentre le impostazioni avanzate permettono di selezionare strategie individuali per ciascuna azione.", + "advanced-settings-hint" : "Fai attenzione quando configuri le strategie di elaborazione. Alcune combinazioni possono portare a comportamenti imprevisti.", + "strategy" : "Strategia", + "deduplication-interval" : "Intervallo di deduplicazione", + "deduplication-interval-required" : "L'intervallo di deduplicazione è richiesto", + "deduplication-interval-min-max-range" : "L'intervallo di deduplicazione deve essere almeno di 1 secondo e massimo di 1 giorno", + "scope" : "Ambito", + "strategy-type" : { + "every-message" : "Su ogni messaggio", + "skip" : "Salta", + "deduplicate" : "Deduplica", + "web-sockets-only" : "Solo WebSockets" + }, + "attributes" : "Attributi" + }, + "key-val" : { + "key" : "Chiave", + "value" : "Valore", + "see-examples" : "Vedi esempi.", + "remove-entry" : "Rimuovi voce", + "remove-mapping-entry" : "Rimuovi mappatura", + "add-mapping-entry" : "Aggiungi mappatura", + "add-entry" : "Aggiungi voce", + "copy-key-values-from" : "Copia coppie chiave-valore da", + "delete-key-values" : "Elimina coppie chiave-valore", + "delete-key-values-from" : "Elimina coppie chiave-valore da", + "at-least-one-key-error" : "Almeno una chiave deve essere selezionata.", + "unique-key-value-pair-error" : "'{{keyText}}' deve essere diverso da '{{valText}}'!" + }, + "mail-body-types" : { + "plain-text" : "Testo semplice", + "html" : "HTML", + "dynamic" : "Dinamico", + "use-body-type-template" : "Usa template tipo di corpo", + "plain-text-description" : "Testo semplice, senza formattazione o stile speciale.", + "html-text-description" : "Permette di usare tag HTML per formattazione, link e immagini nel corpo dell'email.", + "dynamic-text-description" : "Permette di usare testo semplice o HTML dinamicamente in base alla templatizzazione.", + "after-template-evaluation-hint" : "Dopo la valutazione del template il valore deve essere true per HTML e false per testo semplice." + } + }, + "timezone" : { + "timezone" : "Fuso orario", + "select-timezone" : "Seleziona fuso orario", + "no-timezones-matching" : "Nessun fuso orario corrispondente a '{{timezone}}' trovato.", + "timezone-required" : "Il fuso orario è obbligatorio.", + "browser-time" : "Ora del browser" + }, + "queue" : { + "queue-name" : "Coda", + "no-queues-found" : "Nessuna coda trovata.", + "no-queues-matching" : "Nessuna coda corrispondente a '{{queue}}' trovata.", + "select-name" : "Seleziona nome coda", + "name" : "Nome", + "name-required" : "Il nome della coda è obbligatorio!", + "name-unique" : "Il nome della coda non è univoco!", + "name-pattern" : "Il nome della coda contiene caratteri non validi (solo alfanumerici ASCII, '.', '_' e '-')!", + "queue-required" : "La coda è obbligatoria!", + "topic-required" : "Il topic della coda è obbligatorio!", + "poll-interval-required" : "L'intervallo di polling è obbligatorio!", + "poll-interval-min-value" : "Il valore dell'intervallo di polling non può essere inferiore a 1", + "partitions-required" : "Le partizioni sono obbligatorie!", + "partitions-min-value" : "Il valore delle partizioni non può essere inferiore a 1", + "pack-processing-timeout-required" : "Il timeout di elaborazione è obbligatorio", + "pack-processing-timeout-min-value" : "Il valore del timeout non può essere inferiore a 1", + "batch-size-required" : "La dimensione del batch è obbligatoria!", + "batch-size-min-value" : "La dimensione del batch non può essere inferiore a 1", + "retries-required" : "Il numero di tentativi è obbligatorio!", + "retries-min-value" : "Il numero di tentativi non può essere negativo", + "failure-percentage-required" : "La percentuale di errore è obbligatoria!", + "failure-percentage-min-value" : "La percentuale di errore non può essere inferiore a 0", + "failure-percentage-max-value" : "La percentuale di errore non può superare 100", + "pause-between-retries-required" : "La pausa tra i tentativi è obbligatoria!", + "pause-between-retries-min-value" : "La pausa tra i tentativi non può essere inferiore a 1", + "max-pause-between-retries-required" : "Il valore massimo della pausa tra i tentativi è obbligatorio!", + "max-pause-between-retries-min-value" : "Il valore massimo della pausa tra i tentativi non può essere inferiore a 1", + "submit-strategy-type-required" : "Il tipo di strategia di invio è obbligatorio!", + "processing-strategy-type-required" : "Il tipo di strategia di elaborazione è obbligatorio!", + "queues" : "Code", + "selected-queues" : "{ count, plural, =1 {1 coda} other {# code} } selezionata", + "delete-queue-title" : "Sei sicuro di voler eliminare la coda '{{queueName}}'?", + "delete-queues-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 coda} other {# code} }?", + "delete-queue-text" : "Attenzione, dopo la conferma la coda e tutti i dati correlati saranno irrecuperabili.", + "delete-queues-text" : "Dopo la conferma tutte le code selezionate verranno eliminate e non saranno più accessibili.", + "search" : "Cerca coda", + "add" : "Aggiungi coda", + "details" : "Dettagli coda", + "topic" : "Topic", + "submit-settings" : "Impostazioni invio", + "submit-strategy" : "Tipo di strategia *", + "grouping-parameter" : "Parametro di raggruppamento", + "processing-settings" : "Impostazioni elaborazione tentativi", + "processing-strategy" : "Tipo di elaborazione *", + "retries-settings" : "Impostazioni dei tentativi", + "polling-settings" : "Impostazioni polling", + "batch-processing" : "Elaborazione batch", + "poll-interval" : "Intervallo di polling", + "partitions" : "Partizioni", + "immediate-processing" : "Elaborazione immediata", + "consumer-per-partition" : "Polling separato per ciascun consumer", + "consumer-per-partition-hint" : "Abilita consumer separati per ogni partizione", + "duplicate-msg-to-all-partitions" : "Duplica messaggio su tutte le partizioni", + "processing-timeout" : "Tempo di elaborazione, ms", + "batch-size" : "Dimensione batch", + "retries" : "Numero di tentativi (0 – illimitato)", + "failure-percentage" : "Messaggi di errore per saltare i tentativi, %", + "pause-between-retries" : "Tentativo successivo tra, sec", + "max-pause-between-retries" : "Tentativo successivo aggiuntivo tra, sec", + "delete" : "Elimina coda", + "copyId" : "Copia ID coda", + "idCopiedMessage" : "ID della coda copiato negli appunti", + "description" : "Descrizione", + "description-hint" : "Questo testo verrà visualizzato nella descrizione della coda anziché la strategia selezionata", + "alt-description" : "Strategia di invio: {{submitStrategy}}, Strategia di elaborazione: {{processingStrategy}}", + "custom-properties" : "Proprietà personalizzate", + "custom-properties-hint" : "Proprietà di creazione della coda personalizzata (topic), es. 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies" : { + "sequential-by-originator-label" : "Sequenziale per originatore", + "sequential-by-originator-hint" : "Un nuovo messaggio, ad esempio per il dispositivo A, non viene inviato finché il messaggio precedente per il dispositivo A non viene confermato", + "sequential-by-tenant-label" : "Sequenziale per tenant", + "sequential-by-tenant-hint" : "Un nuovo messaggio, ad esempio per il tenant A, non viene inviato finché il messaggio precedente per il tenant A non viene confermato", + "sequential-label" : "Sequenziale", + "sequential-hint" : "Un nuovo messaggio non viene inviato finché il messaggio precedente non viene confermato", + "burst-label" : "Raffica", + "burst-hint" : "Tutti i messaggi vengono inviati alle rule chain nell’ordine in cui arrivano", + "batch-label" : "Batch", + "batch-hint" : "Un nuovo batch non viene inviato finché il batch precedente non viene confermato", + "skip-all-failures-label" : "Ignora tutti gli errori", + "skip-all-failures-hint" : "Ignora tutti gli errori", + "skip-all-failures-and-timeouts-label" : "Ignora tutti gli errori e timeout", + "skip-all-failures-and-timeouts-hint" : "Ignora tutti gli errori e i timeout", + "retry-all-label" : "Ritenta tutto", + "retry-all-hint" : "Ritenta tutti i messaggi dal pacchetto di elaborazione", + "retry-failed-label" : "Ritenta falliti", + "retry-failed-hint" : "Ritenta tutti i messaggi falliti dal pacchetto di elaborazione", + "retry-timeout-label" : "Ritenta timeout", + "retry-timeout-hint" : "Ritenta tutti i messaggi andati in timeout dal pacchetto di elaborazione", + "retry-failed-and-timeout-label" : "Ritenta falliti e timeout", + "retry-failed-and-timeout-hint" : "Ritenta tutti i messaggi falliti e andati in timeout dal pacchetto di elaborazione" + } + }, + "queue-statistics" : { + "queue-statistics" : "Statistiche delle code", + "no-queue-statistics-matching" : "Nessuna statistica delle code trovata corrispondente a '{{entity}}'.", + "queue-statistics-required" : "Statistiche delle code richieste.", + "list-of-queue-statistics" : "{ count, plural, =1 {Una statistica di coda} other {Elenco di # statistiche di coda} }", + "selected-queue-statistics" : "{ count, plural, =1 {1 statistica di coda} other {# statistiche di coda} } selezionate", + "no-queue-statistics-text" : "Nessuna statistica di coda trovata", + "queue-statistics-starts-with" : "Statistiche delle code i cui nomi iniziano con '{{prefix}}'" + }, + "server-error" : { + "general" : "Errore generale del server", + "authentication" : "Errore di autenticazione", + "jwt-token-expired" : "Token JWT scaduto", + "tenant-trial-expired" : "Periodo di prova del tenant scaduto", + "credentials-expired" : "Credenziali scadute", + "permission-denied" : "Permesso negato", + "invalid-arguments" : "Argomenti non validi", + "bad-request-params" : "Parametri di richiesta errati", + "item-not-found" : "Elemento non trovato", + "too-many-requests" : "Troppe richieste", + "too-many-updates" : "Troppe modifiche" + }, + "tenant" : { + "tenant" : "Tenant", + "tenants" : "Tenant", + "management" : "Gestione tenant", + "add" : "Aggiungi tenant", + "admins" : "Amministratori", + "manage-tenant-admins" : "Gestisci amministratori tenant", + "delete" : "Elimina tenant", + "add-tenant-text" : "Aggiungi nuovo tenant", + "no-tenants-text" : "Nessun tenant trovato", + "tenant-details" : "Dettagli tenant", + "title-max-length" : "Il titolo deve contenere meno di 256 caratteri", + "delete-tenant-title" : "Sei sicuro di voler eliminare il tenant '{{tenantTitle}}'?", + "delete-tenant-text" : "Attenzione, dopo la conferma il tenant e tutti i dati correlati non saranno recuperabili.", + "delete-tenants-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 tenant} other {# tenant} }?", + "delete-tenants-action-title" : "Elimina { count, plural, =1 {1 tenant} other {# tenant} }", + "delete-tenants-text" : "Attenzione, dopo la conferma tutti i tenant selezionati verranno rimossi e tutti i dati correlati non saranno recuperabili.", + "title" : "Titolo", + "title-required" : "Il titolo è obbligatorio.", + "description" : "Descrizione", + "details" : "Dettagli", + "events" : "Eventi", + "copyId" : "Copia Id del tenant", + "idCopiedMessage" : "Id del tenant copiato negli appunti", + "select-tenant" : "Seleziona tenant", + "no-tenants-matching" : "Nessun tenant corrispondente a '{{entity}}' trovato.", + "tenant-required" : "Tenant richiesto", + "search" : "Cerca tenant", + "selected-tenants" : "{ count, plural, =1 {1 tenant} other {# tenant} } selezionato/i", + "isolated-tb-rule-engine" : "Usa code isolate per il Rule Engine di ThingsBoard", + "isolated-tb-rule-engine-details" : "Ogni tenant avrà code dedicate del Rule Engine" + }, + "tenant-profile" : { + "tenant-profile" : "Profilo tenant", + "tenant-profiles" : "Profili tenant", + "add" : "Aggiungi profilo tenant", + "add-profile" : "Aggiungi profilo", + "debug" : "Debug", + "edit" : "Modifica profilo tenant", + "tenant-profile-details" : "Dettagli del profilo tenant", + "no-tenant-profiles-text" : "Nessun profilo tenant trovato", + "name-max-length" : "Il nome deve contenere meno di 256 caratteri", + "search" : "Cerca profili tenant", + "selected-tenant-profiles" : "{ count, plural, =1 {1 profilo tenant} other {# profili tenant} } selezionato/i", + "no-tenant-profiles-matching" : "Nessun profilo tenant corrispondente a '{{entity}}' trovato.", + "tenant-profile-required" : "Profilo tenant richiesto", + "idCopiedMessage" : "Id del profilo tenant copiato negli appunti", + "set-default" : "Imposta come profilo tenant predefinito", + "delete" : "Elimina profilo tenant", + "copyId" : "Copia Id del profilo tenant", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio.", + "data" : "Dati del profilo", + "profile-configuration" : "Configurazione del profilo", + "description" : "Descrizione", + "default" : "Predefinito", + "delete-tenant-profile-title" : "Sei sicuro di voler eliminare il profilo tenant '{{tenantProfileName}}'?", + "delete-tenant-profile-text" : "Attenzione, dopo la conferma il profilo tenant e tutti i dati correlati non saranno recuperabili.", + "delete-tenant-profiles-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 profilo tenant} other {# profili tenant} }?", + "delete-tenant-profiles-text" : "Attenzione, dopo la conferma tutti i profili tenant selezionati saranno rimossi e tutti i dati correlati non saranno recuperabili.", + "set-default-tenant-profile-title" : "Sei sicuro di voler impostare '{{tenantProfileName}}' come profilo tenant predefinito?", + "set-default-tenant-profile-text" : "Dopo la conferma, il profilo tenant sarà contrassegnato come predefinito e sarà utilizzato per i nuovi tenant senza profilo specificato.", + "no-tenant-profiles-found" : "Nessun profilo tenant trovato.", + "create-new-tenant-profile" : "Creane uno nuovo!", + "create-tenant-profile" : "Crea nuovo profilo tenant", + "import" : "Importa profilo tenant", + "export" : "Esporta profilo tenant", + "export-failed-error" : "Impossibile esportare il profilo tenant: {{error}}", + "tenant-profile-file" : "File del profilo tenant", + "invalid-tenant-profile-file-error" : "Impossibile importare il profilo tenant: struttura dati non valida.", + "advanced-settings" : "Impostazioni avanzate", + "entities" : "Entità", + "rule-engine" : "Rule Engine", + "time-to-live" : "Time-to-live", + "calculated-fields" : "Campi calcolati", + "alarms-and-notifications" : "Allarmi e notifiche", + "ota-files-in-bytes" : "File", + "ws-title" : "WS", + "unlimited" : "(0 - illimitato)", + "maximum-devices" : "Numero massimo di dispositivi", + "maximum-devices-required" : "È richiesto il numero massimo di dispositivi.", + "maximum-devices-range" : "Il numero massimo di dispositivi non può essere negativo", + "maximum-assets" : "Numero massimo di asset", + "maximum-assets-required" : "È richiesto il numero massimo di asset.", + "maximum-assets-range" : "Il numero massimo di asset non può essere negativo", + "maximum-customers" : "Numero massimo di clienti", + "maximum-customers-required" : "È richiesto il numero massimo di clienti.", + "maximum-customers-range" : "Il numero massimo di clienti non può essere negativo", + "maximum-users" : "Numero massimo di utenti", + "maximum-users-required" : "È richiesto il numero massimo di utenti.", + "maximum-users-range" : "Il numero massimo di utenti non può essere negativo", + "maximum-dashboards" : "Numero massimo di dashboard", + "maximum-dashboards-required" : "È richiesto il numero massimo di dashboard.", + "maximum-dashboards-range" : "Il numero massimo di dashboard non può essere negativo", + "maximum-edges" : "Numero massimo di edge", + "maximum-edges-required" : "È richiesto il numero massimo di edge.", + "maximum-edges-range" : "Il numero massimo di edge non può essere negativo", + "maximum-rule-chains" : "Numero massimo di rule chain", + "maximum-rule-chains-required" : "È richiesto il numero massimo di rule chain.", + "maximum-rule-chains-range" : "Il numero massimo di rule chain non può essere negativo", + "maximum-resources-sum-data-size" : "Dimensione totale massima dei file risorsa (byte)", + "maximum-resources-sum-data-size-required" : "La dimensione totale massima dei file risorsa è richiesta.", + "maximum-resources-sum-data-size-range" : "La dimensione totale massima dei file risorsa non può essere negativa", + "maximum-resource-size" : "Dimensione massima del singolo file risorsa (byte)", + "maximum-resource-size-required" : "La dimensione massima del file risorsa è richiesta", + "maximum-resource-size-range" : "La dimensione massima del file risorsa non può essere negativa", + "maximum-ota-packages-sum-data-size" : "Dimensione totale massima dei file OTA (byte)", + "maximum-ota-package-sum-data-size-required" : "La dimensione totale massima dei file OTA è richiesta.", + "maximum-ota-package-sum-data-size-range" : "La dimensione totale massima dei file OTA non può essere negativa", + "maximum-debug-duration-min" : "Durata massima di debug (min)", + "maximum-debug-duration-min-range" : "La durata massima di debug non può essere negativa", + "rest-requests-for-tenant" : "Richieste REST per tenant", + "transport-tenant-telemetry-msg-rate-limit" : "Messaggi di telemetria del tenant (trasporto)", + "transport-tenant-telemetry-data-points-rate-limit" : "Punti dati di telemetria del tenant (trasporto)", + "transport-device-msg-rate-limit" : "Messaggi del dispositivo (trasporto)", + "transport-device-telemetry-msg-rate-limit" : "Messaggi di telemetria del dispositivo (trasporto)", + "transport-device-telemetry-data-points-rate-limit" : "Punti dati di telemetria del dispositivo (trasporto)", + "transport-gateway-msg-rate-limit" : "Messaggi del gateway (trasporto)", + "transport-gateway-telemetry-msg-rate-limit" : "Messaggi di telemetria del gateway (trasporto)", + "transport-gateway-telemetry-data-points-rate-limit" : "Punti dati di telemetria del gateway (trasporto)", + "transport-gateway-device-msg-rate-limit" : "Messaggi del dispositivo gateway (trasporto)", + "transport-gateway-device-telemetry-msg-rate-limit" : "Messaggi di telemetria del dispositivo gateway (trasporto)", + "transport-gateway-device-telemetry-data-points-rate-limit" : "Punti dati di telemetria del dispositivo gateway (trasporto)", + "tenant-entity-export-rate-limit" : "Creazione versione entità", + "tenant-entity-import-rate-limit" : "Caricamento versione entità", + "tenant-notification-request-rate-limit" : "Richieste di notifica", + "tenant-notification-requests-per-rule-rate-limit" : "Richieste di notifica per regola", + "max-calculated-fields" : "Numero massimo di campi calcolati per entità", + "max-calculated-fields-range" : "Il numero massimo di campi calcolati per entità non può essere negativo", + "max-calculated-fields-required" : "Il numero massimo di campi calcolati per entità è richiesto", + "max-data-points-per-rolling-arg" : "Numero massimo di punti dati negli argomenti rolling", + "max-data-points-per-rolling-arg-range" : "Il numero massimo di punti dati negli argomenti rolling non può essere negativo", + "max-data-points-per-rolling-arg-required" : "Il numero massimo di punti dati negli argomenti rolling è richiesto", + "max-arguments-per-cf" : "Numero massimo di argomenti per campo calcolato", + "max-arguments-per-cf-range" : "Il numero massimo di argomenti per campo calcolato non può essere negativo", + "max-arguments-per-cf-required" : "Il numero massimo di argomenti per campo calcolato è richiesto", + "max-state-size" : "Dimensione massima dello stato in KB", + "max-state-size-range" : "La dimensione massima dello stato in KB non può essere negativa", + "max-state-size-required" : "La dimensione massima dello stato in KB è richiesta", + "max-value-argument-size" : "Dimensione massima del singolo argomento di valore in KB", + "max-value-argument-size-range" : "La dimensione massima dell'argomento di valore in KB non può essere negativa", + "max-value-argument-size-required" : "La dimensione massima dell'argomento di valore in KB è richiesta", + "max-transport-messages" : "Numero massimo di messaggi (trasporto)", + "max-transport-messages-required" : "È richiesto il numero massimo di messaggi (trasporto).", + "max-transport-messages-range" : "Il numero massimo di messaggi (trasporto) non può essere negativo", + "max-transport-data-points" : "Numero massimo di punti dati (trasporto)", + "max-transport-data-points-required" : "È richiesto il numero massimo di punti dati (trasporto).", + "max-transport-data-points-range" : "Il numero massimo di punti dati (trasporto) non può essere negativo", + "max-r-e-executions" : "Numero massimo di esecuzioni Rule Engine", + "max-r-e-executions-required" : "È richiesto il numero massimo di esecuzioni Rule Engine.", + "max-r-e-executions-range" : "Il numero massimo di esecuzioni Rule Engine non può essere negativo", + "max-j-s-executions" : "Numero massimo di esecuzioni JavaScript", + "max-j-s-executions-required" : "È richiesto il numero massimo di esecuzioni JavaScript.", + "max-j-s-executions-range" : "Il numero massimo di esecuzioni JavaScript non può essere negativo", + "max-tbel-executions" : "Numero massimo di esecuzioni TBEL", + "max-tbel-executions-required" : "È richiesto il numero massimo di esecuzioni TBEL.", + "max-tbel-executions-range" : "Il numero massimo di esecuzioni TBEL non può essere negativo", + "max-d-p-storage-days" : "Numero massimo di giorni di archiviazione dei dati", + "max-d-p-storage-days-required" : "È richiesto il numero massimo di giorni di archiviazione dei dati.", + "max-d-p-storage-days-range" : "Il numero massimo di giorni di archiviazione dei dati non può essere negativo", + "default-storage-ttl-days" : "TTL archiviazione predefinito (giorni)", + "default-storage-ttl-days-required" : "È richiesto il TTL predefinito per l'archiviazione.", + "default-storage-ttl-days-range" : "Il TTL di archiviazione predefinito non può essere negativo", + "alarms-ttl-days" : "TTL degli allarmi (giorni)", + "alarms-ttl-days-required" : "TTL degli allarmi richiesto", + "alarms-ttl-days-days-range" : "Il TTL degli allarmi non può essere negativo", + "rpc-ttl-days" : "TTL delle RPC (giorni)", + "rpc-ttl-days-required" : "TTL delle RPC richiesto", + "rpc-ttl-days-days-range" : "Il TTL delle RPC non può essere negativo", + "queue-stats-ttl-days" : "TTL delle statistiche della coda (giorni)", + "queue-stats-ttl-days-required" : "TTL delle statistiche della coda richiesto", + "queue-stats-ttl-days-range" : "Il TTL delle statistiche della coda non può essere negativo", + "rule-engine-exceptions-ttl-days" : "TTL delle eccezioni del Rule Engine (giorni)", + "rule-engine-exceptions-ttl-days-required" : "TTL delle eccezioni del Rule Engine richiesto", + "rule-engine-exceptions-ttl-days-range" : "Il TTL delle eccezioni del Rule Engine non può essere negativo", + "max-rule-node-executions-per-message" : "Numero massimo di esecuzioni per nodo per messaggio", + "max-rule-node-executions-per-message-required" : "È richiesto il numero massimo di esecuzioni per nodo per messaggio.", + "max-rule-node-executions-per-message-range" : "Il numero massimo di esecuzioni per nodo per messaggio non può essere negativo", + "max-emails" : "Numero massimo di email inviate", + "max-emails-required" : "È richiesto il numero massimo di email inviate.", + "max-emails-range" : "Il numero massimo di email inviate non può essere negativo", + "sms-enabled" : "SMS abilitati", + "max-sms" : "Numero massimo di SMS inviati", + "max-sms-required" : "È richiesto il numero massimo di SMS inviati.", + "max-sms-range" : "Il numero massimo di SMS inviati non può essere negativo", + "max-created-alarms" : "Numero massimo di allarmi creati", + "max-created-alarms-required" : "È richiesto il numero massimo di allarmi creati.", + "max-created-alarms-range" : "Il numero massimo di allarmi creati non può essere negativo", + "no-queue" : "Nessuna coda configurata", + "add-queue" : "Aggiungi coda", + "queues-with-count" : "Code ({{count}})", + "tenant-rest-limits" : "Richieste REST per tenant", + "customer-rest-limits" : "Richieste REST per cliente", + "incorrect-pattern-for-rate-limits" : "Il formato è coppie separate da virgole di capacità e periodo (in secondi) con i due punti, ad es. 100:1,2000:60", + "too-small-value-zero" : "Il valore deve essere maggiore di 0", + "too-small-value-one" : "Il valore deve essere maggiore di 1", + "queue-size-is-limited-by-system-configuration" : "La dimensione della coda è anche limitata dalla configurazione di sistema.", + "cassandra-tenant-limits-configuration" : "Query Cassandra per tenant", + "ws-limit-max-sessions-per-tenant" : "Numero massimo di sessioni per tenant", + "ws-limit-max-sessions-per-customer" : "Numero massimo di sessioni per cliente", + "ws-limit-max-sessions-per-regular-user" : "Numero massimo di sessioni per utente regolare", + "ws-limit-max-sessions-per-public-user" : "Numero massimo di sessioni per utente pubblico", + "ws-limit-queue-per-session" : "Dimensione massima della coda per sessione", + "ws-limit-max-subscriptions-per-tenant" : "Numero massimo di sottoscrizioni per tenant", + "ws-limit-max-subscriptions-per-customer" : "Numero massimo di sottoscrizioni per cliente", + "ws-limit-max-subscriptions-per-regular-user" : "Numero massimo di sottoscrizioni per utente regolare", + "ws-limit-max-subscriptions-per-public-user" : "Numero massimo di sottoscrizioni per utente pubblico", + "ws-limit-updates-per-session" : "Aggiornamenti WS per sessione", + "rate-limits" : { + "add-limit" : "Aggiungi limite", + "advanced-settings" : "Impostazioni avanzate", + "edit-limit" : "Modifica limite", + "but-less-than" : "ma inferiore a", + "calculated-field-debug-event-rate-limit" : "Eventi di debug del campo calcolato", + "edit-calculated-field-debug-event-rate-limit" : "Modifica i limiti di eventi di debug del campo calcolato", + "edit-transport-tenant-msg-title" : "Modifica i limiti di messaggi di trasporto del tenant", + "edit-transport-tenant-telemetry-msg-title" : "Modifica i limiti di messaggi di telemetria del tenant", + "edit-transport-tenant-telemetry-data-points-title" : "Modifica i limiti dei punti dati di telemetria del tenant", + "edit-transport-device-msg-title" : "Modifica i limiti di messaggi del dispositivo", + "edit-transport-device-telemetry-msg-title" : "Modifica i limiti di messaggi di telemetria del dispositivo", + "edit-transport-device-telemetry-data-points-title" : "Modifica i limiti dei punti dati di telemetria del dispositivo", + "edit-transport-gateway-msg-title" : "Modifica i limiti di messaggi del gateway", + "edit-transport-gateway-telemetry-msg-title" : "Modifica i limiti di messaggi di telemetria del gateway", + "edit-transport-gateway-telemetry-data-points-title" : "Modifica i limiti dei punti dati di telemetria del gateway", + "edit-transport-gateway-device-msg-title" : "Modifica i limiti di messaggi dei dispositivi gateway", + "edit-transport-gateway-device-telemetry-msg-title" : "Modifica i limiti di messaggi di telemetria dei dispositivi gateway", + "edit-transport-gateway-device-telemetry-data-points-title" : "Modifica i limiti dei punti dati di telemetria dei dispositivi gateway", + "edit-tenant-rest-limits-title" : "Modifica i limiti delle richieste REST per il tenant", + "edit-customer-rest-limits-title" : "Modifica i limiti delle richieste REST per il cliente", + "edit-ws-limit-updates-per-session-title" : "Modifica i limiti degli aggiornamenti WS per sessione", + "edit-cassandra-tenant-limits-configuration-title" : "Modifica i limiti delle query Cassandra per tenant", + "edit-tenant-entity-export-rate-limit-title" : "Modifica i limiti di creazione delle versioni delle entità", + "edit-tenant-entity-import-rate-limit-title" : "Modifica i limiti di caricamento delle versioni delle entità", + "edit-tenant-notification-request-rate-limit-title" : "Modifica i limiti delle richieste di notifica", + "edit-tenant-notification-requests-per-rule-rate-limit-title" : "Modifica i limiti delle richieste di notifica per regola", + "edit-edge-events-rate-limit" : "Modifica i limiti degli eventi edge", + "edit-edge-events-per-edge-rate-limit" : "Modifica i limiti degli eventi edge per edge", + "edge-events-rate-limit" : "Eventi edge", + "edge-events-per-edge-rate-limit" : "Eventi edge per edge", + "edit-edge-uplink-messages-rate-limit" : "Modifica i limiti dei messaggi uplink edge", + "edit-edge-uplink-messages-per-edge-rate-limit" : "Modifica i limiti dei messaggi uplink edge per edge", + "edge-uplink-messages-rate-limit" : "Messaggi uplink edge", + "edge-uplink-messages-per-edge-rate-limit" : "Messaggi uplink edge per edge", + "messages-per" : "messaggi per", + "not-set" : "Non impostato", + "number-of-messages" : "Numero di messaggi", + "number-of-messages-required" : "È richiesto il numero di messaggi.", + "number-of-messages-min" : "Il valore minimo è 1.", + "preview" : "Anteprima", + "per-seconds" : "Ogni secondi", + "per-seconds-required" : "È richiesta la frequenza temporale.", + "per-seconds-min" : "Il valore minimo è 1.", + "rate-limits" : "Limiti di frequenza", + "remove-limit" : "Rimuovi limite", + "transport-tenant-msg" : "Messaggi di trasporto del tenant", + "transport-tenant-telemetry-msg" : "Messaggi di telemetria del tenant", + "transport-tenant-telemetry-data-points" : "Punti dati di telemetria del tenant", + "transport-device-msg" : "Messaggi di trasporto del dispositivo", + "transport-device-telemetry-msg" : "Messaggi di telemetria del dispositivo", + "transport-device-telemetry-data-points" : "Punti dati di telemetria del dispositivo", + "transport-gateway-msg" : "Messaggi di trasporto del gateway", + "transport-gateway-telemetry-msg" : "Messaggi di telemetria del gateway", + "transport-gateway-telemetry-data-points" : "Punti dati di telemetria del gateway", + "transport-gateway-device-msg" : "Messaggi dei dispositivi gateway", + "transport-gateway-device-telemetry-msg" : "Messaggi di telemetria dei dispositivi gateway", + "transport-gateway-device-telemetry-data-points" : "Punti dati di telemetria dei dispositivi gateway", + "sec" : "sec" + } + }, + "timeinterval" : { + "seconds-interval" : "{ seconds, plural, =1 {1 secondo} other {# secondi} }", + "minutes-interval" : "{ minutes, plural, =1 {1 minuto} other {# minuti} }", + "hours-interval" : "{ hours, plural, =1 {1 ora} other {# ore} }", + "days-interval" : "{ days, plural, =1 {1 giorno} other {# giorni} }", + "days" : "Giorni", + "hours" : "Ore", + "minutes" : "Minuti", + "seconds" : "Secondi", + "advanced" : "Avanzato", + "custom" : "Personalizzato", + "predefined" : { + "yesterday" : "Ieri", + "day-before-yesterday" : "L'altro ieri", + "this-day-last-week" : "Questo giorno la scorsa settimana", + "previous-week" : "Settimana precedente (dom - sab)", + "previous-week-iso" : "Settimana precedente (lun - dom)", + "previous-month" : "Mese precedente", + "previous-quarter" : "Trimestre precedente", + "previous-half-year" : "Semestre precedente", + "previous-year" : "Anno precedente", + "current-hour" : "Ora corrente", + "current-day" : "Giorno corrente", + "current-day-so-far" : "Giorno corrente fino ad ora", + "current-week" : "Settimana corrente (dom - sab)", + "current-week-iso" : "Settimana corrente (lun - dom)", + "current-week-so-far" : "Settimana corrente fino ad ora (dom - sab)", + "current-week-iso-so-far" : "Settimana corrente fino ad ora (lun - dom)", + "current-month" : "Mese corrente", + "current-month-so-far" : "Mese corrente fino ad ora", + "current-quarter" : "Trimestre corrente", + "current-quarter-so-far" : "Trimestre corrente fino ad ora", + "current-half-year" : "Semestre corrente", + "current-half-year-so-far" : "Semestre corrente fino ad ora", + "current-year" : "Anno corrente", + "current-year-so-far" : "Anno corrente fino ad ora" + }, + "type" : { + "week" : "Settimana (dom - sab)", + "week-iso" : "Settimana (lun - dom)", + "month" : "Mese", + "quarter" : "Trimestre" + } + }, + "timeunit" : { + "milliseconds" : "Millisecondi", + "seconds" : "Secondi", + "minutes" : "Minuti", + "hours" : "Ore", + "days" : "Giorni" + }, + "timewindow" : { + "timewindow" : "Finestra temporale", + "timewindow-settings" : "Impostazioni della finestra temporale", + "years" : "{ years, plural, =1 { anno } other {# anni } }", + "years-short" : "{{ years }}a", + "months" : "{ months, plural, =1 { mese } other {# mesi } }", + "months-short" : "{{ months }}M", + "weeks" : "{ weeks, plural, =1 { settimana } other {# settimane } }", + "weeks-short" : "{{ weeks }}s", + "days" : "{ days, plural, =1 { giorno } other {# giorni } }", + "days-short" : "{{ days }}g", + "hours" : "{ hours, plural, =0 { ora } =1 {1 ora } other {# ore } }", + "hr" : "{{ hr }} ore", + "hr-short" : "{{ hr }}h", + "minutes" : "{ minutes, plural, =0 { minuto } =1 {1 minuto } other {# minuti } }", + "min" : "{{ min }} min", + "min-short" : "{{ min }}m", + "seconds" : "{ seconds, plural, =0 { secondo } =1 {1 secondo } other {# secondi } }", + "sec" : "{{ sec }} sec", + "sec-short" : "{{ sec }}s", + "short" : { + "years" : "{ years, plural, =1 {1 anno } other {# anni } }", + "days" : "{ days, plural, =1 {1 giorno } other {# giorni } }", + "hours" : "{ hours, plural, =1 {1 ora } other {# ore } }", + "minutes" : "{{minutes}} min ", + "seconds" : "{{seconds}} sec " + }, + "realtime" : "Tempo reale", + "history" : "Cronologia", + "last-prefix" : "ultimo", + "period" : "da {{ startTime }} a {{ endTime }}", + "edit" : "Modifica finestra temporale", + "date-range" : "Intervallo di date", + "for-all-time" : "Per tutto il tempo", + "last" : "Ultimo", + "time-period" : "Periodo di tempo", + "hide" : "Nascondi", + "interval" : "Intervallo", + "just-now" : "Proprio ora", + "just-now-lower" : "proprio ora", + "ago" : "fa", + "style" : "Stile della finestra temporale", + "icon" : "Icona", + "icon-position" : "Posizione icona", + "icon-position-left" : "Sinistra", + "icon-position-right" : "Destra", + "font" : "Font", + "color" : "Colore", + "displayTypePrefix" : "Mostra prefisso Tempo reale/Cronologia", + "preview" : "Anteprima", + "relative" : "Relativo", + "range" : "Intervallo", + "hide-timewindow-section" : "Nascondi sezione finestra temporale agli utenti finali", + "hide-last-interval" : "Nascondi l'ultimo intervallo agli utenti finali", + "hide-relative-interval" : "Nascondi intervallo relativo agli utenti finali", + "hide-fixed-interval" : "Nascondi intervallo fisso agli utenti finali", + "hide-aggregation" : "Nascondi aggregazione agli utenti finali", + "hide-group-interval" : "Nascondi intervallo di raggruppamento agli utenti finali", + "hide-max-values" : "Nascondi valori massimi agli utenti finali", + "hide-timezone" : "Nascondi fuso orario agli utenti finali", + "disable-custom-interval" : "Disabilita selezione intervallo personalizzato", + "edit-aggregation-functions-list" : "Modifica elenco funzioni di aggregazione", + "edit-aggregation-functions-list-hint" : "È possibile specificare un elenco di opzioni disponibili.", + "allowed-aggregation-functions" : "Funzioni di aggregazione consentite", + "edit-intervals-list" : "Modifica elenco intervalli", + "allowed-agg-intervals" : "Intervalli di raggruppamento", + "default-agg-interval" : "Intervallo di raggruppamento predefinito", + "edit-intervals-list-hint" : "È possibile specificare un elenco di opzioni di intervallo disponibili.", + "edit-grouping-intervals-list-hint" : "È possibile configurare l'elenco degli intervalli di raggruppamento e l'intervallo di raggruppamento predefinito.", + "all" : "Tutti" + }, + "tooltip" : { + "trigger" : "Trigger", + "trigger-point" : "Punto", + "trigger-axis" : "Asse", + "label" : "Etichetta", + "value" : "Valore", + "date" : "Data", + "show-date-time-interval" : "Mostra intervallo data e ora", + "show-date-time-interval-hint" : "Mostra intervallo data e ora in base all'aggregazione dei dati.", + "background-color" : "Colore di sfondo", + "background-blur" : "Sfocatura sfondo" + }, + "unit" : { + "millimeter" : "Millimetro", + "centimeter" : "Centimetro", + "angstrom" : "Ångström", + "nanometer" : "Nanometro", + "micrometer" : "Micrometro", + "meter" : "Metro", + "kilometer" : "Chilometro", + "inch" : "Pollice", + "foot" : "Piede", + "yard" : "Iarda", + "mile" : "Miglio", + "nautical-mile" : "Miglio nautico", + "astronomical-unit" : "Unità astronomica", + "reciprocal-metre" : "Metro reciproco", + "meter-per-meter" : "Metro per metro", + "steradian" : "Steradiante", + "thou" : "Millesimo di pollice", + "barleycorn" : "Chicco d'orzo", + "hand" : "Mano", + "chain" : "Catena", + "furlong" : "Furlong", + "league" : "Lega", + "fathom" : "Braccio", + "cable" : "Cavo", + "link" : "Collegamento", + "rod" : "Asta", + "nanogram" : "Nanogrammo", + "microgram" : "Microgrammo", + "milligram" : "Milligrammo", + "gram" : "Grammo", + "kilogram" : "Chilogrammo", + "tonne" : "Tonnellata", + "ounce" : "Oncia", + "pound" : "Libbra", + "stone" : "Stone", + "hundredweight-count" : "Centoventi libbre", + "short-tons" : "Tonnellate corte", + "dalton" : "Dalton", + "grain" : "Grano", + "drachm" : "Dramma", + "quarter" : "Quarto", + "slug" : "Slug", + "carat" : "Carato", + "cubic-millimeter" : "Millimetro cubo", + "cubic-centimeter" : "Centimetro cubo", + "cubic-meter" : "Metro cubo", + "cubic-kilometer" : "Chilometro cubo", + "microliter" : "Microlitro", + "milliliter" : "Millilitro", + "liter" : "Litro", + "hectoliter" : "Ettolitro", + "cubic-inch" : "Pollice cubo", + "cubic-foot" : "Piede cubo", + "cubic-yard" : "Iarda cubica", + "fluid-ounce" : "Oncia liquida", + "pint" : "Pinta", + "quart" : "Quarto", + "gallon" : "Gallone", + "oil-barrels" : "Barile di petrolio", + "cubic-meter-per-kilogram" : "Metro cubo per chilogrammo", + "gill" : "Gill", + "hogshead" : "Hogshead", + "teaspoon" : "Cucchiaino", + "tablespoon" : "Cucchiaio", + "cup" : "Tazza", + "celsius" : "Celsius", + "kelvin" : "Kelvin", + "rankine" : "Rankine", + "fahrenheit" : "Fahrenheit", + "percent" : "Percentuale", + "meter-per-second" : "Metro al secondo", + "kilometer-per-hour" : "Chilometro all'ora", + "foot-per-second" : "Piede al secondo", + "mile-per-hour" : "Miglio all'ora", + "knot" : "Nodo", + "millimeters-per-minute" : "Millimetri al minuto", + "kilometer-per-hour-squared" : "Chilometro all'ora quadrato", + "foot-per-second-squared" : "Piede al secondo quadrato", + "pascal" : "Pascal", + "kilopascal" : "Kilopascal", + "megapascal" : "Megapascal", + "gigapascal" : "Gigapascal", + "millibar" : "Millibar", + "bar" : "Bar", + "kilobar" : "Kilobar", + "newton" : "Newton", + "newton-meter" : "Newton metro", + "foot-pounds" : "Piedi-libbra", + "inch-pounds" : "Pollici-libbra", + "newton-per-meter" : "Newton per metro", + "atmospheres" : "Atmosfere", + "pounds-per-square-inch" : "Libbre per pollice quadrato", + "torr" : "Torr", + "inches-of-mercury" : "Pollici di mercurio", + "pascal-per-square-meter" : "Pascal per metro quadrato", + "pound-per-square-inch" : "Libbra per pollice quadrato", + "newton-per-square-meter" : "Newton per metro quadrato", + "kilogram-force-per-square-meter" : "Forza di chilogrammo per metro quadrato", + "pascal-per-square-centimeter" : "Pascal per centimetro quadrato", + "ton-force-per-square-inch" : "Tonnellata-forza per pollice quadrato", + "kilonewton-per-square-meter" : "Kilonewton per metro quadrato", + "newton-per-square-millimeter" : "Newton per millimetro quadrato", + "microjoule" : "Microjoule", + "millijoule" : "Millijoule", + "joule" : "Joule", + "kilojoule" : "Kilojoule", + "megajoule" : "Megajoule", + "gigajoule" : "Gigajoule", + "watt-hour" : "Wattora", + "kilowatt-hour" : "Kilowattora", + "electron-volts" : "Elettronvolt", + "joules-per-coulomb" : "Joule per Coulomb", + "british-thermal-unit" : "Unità termica britannica", + "foot-pound" : "Piede-libbra", + "calorie" : "Caloria", + "small-calorie" : "Piccola caloria", + "kilocalorie" : "Chilocaloria", + "joule-per-kelvin" : "Joule per Kelvin", + "joule-per-kilogram-kelvin" : "Joule per chilogrammo-Kelvin", + "joule-per-kilogram" : "Joule per chilogrammo", + "watt-per-meter-kelvin" : "Watt per metro-Kelvin", + "joule-per-cubic-meter" : "Joule per metro cubo", + "therm" : "Therm", + "electric-dipole-moment" : "Momento di dipolo elettrico", + "magnetic-dipole-moment" : "Momento di dipolo magnetico", + "debye" : "Debye", + "coulomb-per-square-meter-per-volt" : "Coulomb per metro quadrato per Volt", + "milliwatt" : "Milliwatt", + "microwatt" : "Microwatt", + "watt" : "Watt", + "kilowatt" : "Kilowatt", + "megawatt" : "Megawatt", + "gigawatt" : "Gigawatt", + "metric-horsepower" : "Cavallo vapore metrico", + "milliwatt-per-square-centimeter" : "Milliwatt per centimetro quadrato", + "watt-per-square-centimeter" : "Watt per centimetro quadrato", + "kilowatt-per-square-centimeter" : "Kilowatt per centimetro quadrato", + "milliwatt-per-square-meter" : "Milliwatt per metro quadrato", + "watt-per-square-meter" : "Watt per metro quadrato", + "kilowatt-per-square-meter" : "Kilowatt per metro quadrato", + "watt-per-square-inch" : "Watt per pollice quadrato", + "kilowatt-per-square-inch" : "Kilowatt per pollice quadrato", + "horsepower" : "Cavallo vapore", + "btu-per-hour" : "Unità termiche britanniche/ora", + "coulomb" : "Coulomb", + "millicoulomb" : "Millicoulomb", + "microcoulomb" : "Microcoulomb", + "picocoulomb" : "Picocoulomb", + "coulomb-per-meter" : "Coulomb per metro", + "coulomb-per-cubic-meter" : "Coulomb per metro cubo", + "coulomb-per-square-meter" : "Coulomb per metro quadrato", + "square-millimeter" : "Millimetro quadrato", + "square-centimeter" : "Centimetro quadrato", + "square-meter" : "Metro quadrato", + "hectare" : "Ettaro", + "square-kilometer" : "Chilometro quadrato", + "square-inch" : "Pollice quadrato", + "square-foot" : "Piede quadrato", + "square-yard" : "Iarda quadrata", + "acre" : "Acri", + "square-mile" : "Miglio quadrato", + "are" : "Ara", + "barn" : "Barn", + "circular-inch" : "Pollice circolare", + "milliampere-hour" : "Milliampere-ora", + "ampere-hours" : "Ampere-ora", + "kiloampere-hours" : "Chiloampere-ora", + "nanoampere" : "Nanoampere", + "picoampere" : "Picoampere", + "microampere" : "Microampere", + "milliampere" : "Milliampere", + "ampere" : "Ampere", + "microampere-per-square-centimeter" : "Microampere per centimetro quadrato", + "ampere-per-square-meter" : "Ampere per metro quadrato", + "ampere-per-meter" : "Ampere per metro", + "oersted" : "Oersted", + "bohr-magneton" : "Magnetone di Bohr", + "ampere-meter-squared" : "Ampere metro quadrato", + "nanovolt" : "Nanovolt", + "picovolt" : "Picovolt", + "volt" : "Volt", + "dbmV" : "dBmV", + "dbm" : "dBm", + "volt-meter" : "Volt-metro", + "kilovolt-meter" : "Kilovolt-metro", + "megavolt-meter" : "Megavolt-metro", + "microvolt-meter" : "Microvolt-metro", + "millivolt-meter" : "Millivolt-metro", + "nanovolt-meter" : "Nanovolt-metro", + "ohm" : "Ohm", + "microohm" : "Microohm", + "milliohm" : "Milliohm", + "kilohm" : "Kilohm", + "megohm" : "Megohm", + "gigohm" : "Gigohm", + "hertz" : "Hertz", + "kilohertz" : "Kilohertz", + "megahertz" : "Megahertz", + "gigahertz" : "Gigahertz", + "rpm" : "Giri al minuto", + "candela-per-square-meter" : "Candela per metro quadrato", + "candela" : "Candela", + "lumen" : "Lumen", + "lux" : "Lux", + "foot-candle" : "Foot-candle", + "lumen-per-square-meter" : "Lumen per metro quadrato", + "lux-second" : "Secondo lux", + "lumen-second" : "Secondo lumen", + "lumens-per-watt" : "Lumen per watt", + "mole" : "Mole", + "nanomole" : "Nanomole", + "micromole" : "Micromole", + "millimole" : "Millimole", + "kilomole" : "Chilomole", + "mole-per-cubic-meter" : "Mole per metro cubo", + "rssi" : "RSSI", + "ppm" : "Parti per milione", + "ppb" : "Parti per miliardo", + "micrograms-per-cubic-meter" : "Microgrammi per metro cubo", + "aqi" : "Indice di qualità dell'aria (AQI)", + "gram-per-cubic-meter" : "Grammo per metro cubo", + "gram-per-kilogram" : "Umidità specifica", + "millimeters-per-second" : "Millimetri al secondo", + "neper" : "Neper", + "bel" : "Bel", + "decibel" : "Decibel", + "meters-per-second-squared" : "Metri al secondo quadrato", + "becquerel" : "Becquerel", + "curie" : "Curie", + "gray" : "Gray", + "sievert" : "Sievert", + "roentgen" : "Roentgen", + "cps" : "Conteggi al secondo", + "rad" : "Rad", + "rem" : "Rem", + "dps" : "Disintegrazioni al secondo", + "rutherford" : "Rutherford", + "coulombs-per-kilogram" : "Coulomb per chilogrammo", + "becquerels-per-cubic-meter" : "Becquerel per metro cubo", + "curies-per-liter" : "Curie per litro", + "becquerels-per-second" : "Becquerel al secondo", + "curies-per-second" : "Curie al secondo", + "gy-per-second" : "Gray al secondo", + "watt-per-steradian" : "Watt per steradiante", + "watt-per-square-metre-steradian" : "Watt per metro quadrato-steradiante", + "ph-level" : "Livello di pH", + "turbidity" : "Torbidità", + "mg-per-liter" : "Milligrammi per litro", + "microsiemens-per-centimeter" : "Microsiemens per centimetro", + "millisiemens-per-meter" : "Millisiemens per metro", + "siemens-per-meter" : "Siemens per metro", + "kilogram-per-cubic-meter" : "Chilogrammo per metro cubo", + "gram-per-cubic-centimeter" : "Grammo per centimetro cubo", + "kilogram-per-square-meter" : "Chilogrammo per metro quadrato", + "milligram-per-milliliter" : "Milligrammo per millilitro", + "milligram-per-cubic-meter" : "Milligrammo per metro cubo", + "pound-per-cubic-foot" : "Libbra per piede cubo", + "ounces-per-cubic-inch" : "Once per pollice cubo", + "tons-per-cubic-yard" : "Tonnellate per iarda cubica", + "particle-density" : "Densità di particelle", + "kilometers-per-liter" : "Chilometri per litro", + "miles-per-gallon" : "Miglia per gallone", + "liters-per-100-km" : "Litri per 100 km", + "gallons-per-mile" : "Galloni per miglio", + "liters-per-hour" : "Litri all'ora", + "gallons-per-hour" : "Galloni all'ora", + "beats-per-minute" : "Battiti per minuto", + "millimeters-of-mercury" : "Millimetri di mercurio", + "milligrams-per-deciliter" : "Milligrammi per decilitro", + "g-force" : "Forza G", + "kilonewton" : "Chilonewton", + "kilogram-force" : "Chilogrammo-forza", + "pound-force" : "Libbra-forza", + "kilopound-force" : "Chilolibbra-forza", + "dyne" : "Dina", + "poundal" : "Poundal", + "kip" : "Kip", + "gal" : "Gal", + "gravity" : "Gravità", + "hectopascal" : "Ettopascal", + "atmosphere" : "Atmosfera", + "millibars" : "Millibar", + "inch-of-mercury" : "Pollice di mercurio", + "richter-scale" : "Scala Richter", + "second" : "Secondo", + "minute" : "Minuto", + "hour" : "Ora", + "day" : "Giorno", + "week" : "Settimana", + "month" : "Mese", + "year" : "Anno", + "cubic-foot-per-minute" : "Piede cubo al minuto", + "cubic-meters-per-hour" : "Metri cubi all'ora", + "cubic-meters-per-second" : "Metri cubi al secondo", + "liter-per-second" : "Litri al secondo", + "liter-per-minute" : "Litri al minuto", + "gallons-per-minute" : "Galloni al minuto", + "cubic-foot-per-second" : "Piede cubo al secondo", + "milliliters-per-minute" : "Millilitri al minuto", + "bit" : "Bit", + "byte" : "Byte", + "kilobyte" : "Kilobyte", + "megabyte" : "Megabyte", + "gigabyte" : "Gigabyte", + "terabyte" : "Terabyte", + "petabyte" : "Petabyte", + "exabyte" : "Exabyte", + "zettabyte" : "Zettabyte", + "yottabyte" : "Yottabyte", + "bit-per-second" : "Bit al secondo", + "kilobit-per-second" : "Kilobit al secondo", + "megabit-per-second" : "Megabit al secondo", + "gigabit-per-second" : "Gigabit al secondo", + "terabit-per-second" : "Terabit al secondo", + "byte-per-second" : "Byte al secondo", + "kilobyte-per-second" : "Kilobyte al secondo", + "megabyte-per-second" : "Megabyte al secondo", + "gigabyte-per-second" : "Gigabyte al secondo", + "degree" : "Grado", + "radian" : "Radianti", + "gradian" : "Gradi centesimali", + "revolution" : "Rivoluzione", + "siemens" : "Siemens", + "millisiemens" : "Millisiemens", + "microsiemens" : "Microsiemens", + "kilosiemens" : "Kilosiemens", + "megasiemens" : "Megasiemens", + "gigasiemens" : "Gigasiemens", + "farad" : "Farad", + "millifarad" : "Millifarad", + "microfarad" : "Microfarad", + "nanofarad" : "Nanofarad", + "picofarad" : "Picofarad", + "kilofarad" : "Kilofarad", + "megafarad" : "Megafarad", + "gigafarad" : "Gigafarad", + "terfarad" : "Terfarad", + "farad-per-meter" : "Farad per metro", + "tesla" : "Tesla", + "gauss" : "Gauss", + "kilogauss" : "Kilogauss", + "millitesla" : "Millitesla", + "microtesla" : "Microtesla", + "nanotesla" : "Nanotesla", + "kilotesla" : "Kilotesla", + "megatesla" : "Megatesla", + "millitesla-square-meters" : "Millitesla per metro quadrato", + "gamma" : "Gamma", + "lambda" : "Lambda", + "square-meter-per-second" : "Metro quadrato al secondo", + "square-centimeter-per-second" : "Centimetro quadrato al secondo", + "stoke" : "Stoke", + "centistokes" : "Centistoke", + "square-foot-per-second" : "Piede quadrato al secondo", + "square-inch-per-second" : "Pollice quadrato al secondo", + "pascal-second" : "Pascal per secondo", + "centipoise" : "Centipoise", + "poise" : "Poise", + "reynolds" : "Reynolds", + "pound-per-foot-hour" : "Libbra per piede-ora", + "newton-second-per-square-meter" : "Newton secondo per metro quadrato", + "dyne-second-per-square-centimeter" : "Dina secondo per centimetro quadrato", + "kilogram-per-meter-second" : "Chilogrammo per metro-secondo", + "tesla-square-meters" : "Tesla per metro quadrato", + "maxwell" : "Maxwell", + "tesla-per-meter" : "Tesla per metro", + "gauss-per-centimeter" : "Gauss per centimetro", + "weber" : "Weber", + "microweber" : "Microweber", + "milliweber" : "Milliweber", + "gauss-square-centimeter" : "Gauss per centimetro quadrato", + "kilogauss-square-centimeter" : "Kilogauss per centimetro quadrato", + "henry" : "Henry", + "millihenry" : "Millihenry", + "microhenry" : "Microhenry", + "nanohenry" : "Nanohenry", + "henry-per-meter" : "Henry per metro", + "tesla-meter-per-ampere" : "Tesla metro per ampere", + "gauss-per-oersted" : "Gauss per Oersted", + "kilogram-per-mole" : "Chilogrammo per mole", + "gram-per-mole" : "Gramma per mole", + "milligram-per-mole" : "Milligrammo per mole", + "joule-per-mole" : "Joule per mole", + "joule-per-mole-kelvin" : "Joule per mole-Kelvin", + "millivolts-per-meter" : "Millivolt per metro", + "volts-per-meter" : "Volt per metro", + "kilovolts-per-meter" : "Kilovolt per metro", + "radian-per-second" : "Radianti al secondo", + "radian-per-second-squared" : "Radianti al secondo quadrato", + "revolutions-per-minute-per-second" : "Accelerazione angolare", + "deg-per-second" : "gradi al secondo (°/s)", + "degrees-brix" : "Gradi Brix", + "katal" : "Katal", + "katal-per-cubic-metre" : "Katal per metro cubo" + }, + "user" : { + "user" : "Utente", + "users" : "Utenti", + "customer-users" : "Utenti cliente", + "tenant-admins" : "Amministratori tenant", + "sys-admin" : "Amministratore di sistema", + "tenant-admin" : "Amministratore tenant", + "customer" : "Cliente", + "anonymous" : "Anonimo", + "add" : "Aggiungi utente", + "delete" : "Elimina utente", + "add-user-text" : "Aggiungi nuovo utente", + "no-users-text" : "Nessun utente trovato", + "user-details" : "Dettagli utente", + "delete-user-title" : "Sei sicuro di voler eliminare l'utente '{{userEmail}}'?", + "delete-user-text" : "Attenzione, dopo la conferma l'utente e tutti i dati correlati non saranno più recuperabili.", + "delete-users-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 utente} other {# utenti} }?", + "delete-users-action-title" : "Elimina { count, plural, =1 {1 utente} other {# utenti} }", + "delete-users-text" : "Attenzione, dopo la conferma tutti gli utenti selezionati saranno rimossi e tutti i dati correlati non saranno più recuperabili.", + "activation-email-sent-message" : "Email di attivazione inviata con successo!", + "resend-activation" : "Reinvia attivazione", + "email" : "Email", + "email-required" : "Email obbligatoria.", + "invalid-email-format" : "Formato email non valido.", + "first-name" : "Nome", + "last-name" : "Cognome", + "description" : "Descrizione", + "default-dashboard" : "Dashboard predefinita", + "always-fullscreen" : "Sempre a schermo intero", + "select-user" : "Seleziona utente", + "no-users-matching" : "Nessun utente corrispondente a '{{entity}}' trovato.", + "user-required" : "Utente obbligatorio", + "activation-method" : "Metodo di attivazione", + "display-activation-link" : "Visualizza link di attivazione", + "send-activation-mail" : "Invia email di attivazione", + "activation-link" : "Link di attivazione utente", + "activation-link-text" : "Per attivare l'utente utilizza il seguente link di attivazione (scade tra {{activationLinkTtl}}):", + "copy-activation-link" : "Copia link di attivazione", + "activation-link-copied-message" : "Il link di attivazione utente è stato copiato negli appunti", + "details" : "Dettagli", + "login-as-tenant-admin" : "Accedi come amministratore tenant", + "login-as-customer-user" : "Accedi come utente cliente", + "search" : "Cerca utenti", + "selected-users" : "{ count, plural, =1 {1 utente} other {# utenti} } selezionato", + "disable-account" : "Disabilita account utente", + "enable-account" : "Abilita account utente", + "enable-account-message" : "Account utente abilitato con successo!", + "disable-account-message" : "Account utente disabilitato con successo!", + "copyId" : "Copia ID utente", + "idCopiedMessage" : "L'ID utente è stato copiato negli appunti", + "user-list" : "Elenco utenti", + "user-list-required" : "L'elenco utenti è obbligatorio" + }, + "value" : { + "type" : "Tipo di valore", + "string" : "Stringa", + "string-value" : "Valore stringa", + "string-value-required" : "Il valore stringa è obbligatorio", + "integer" : "Intero", + "integer-value" : "Valore intero", + "integer-value-required" : "Il valore intero è obbligatorio", + "invalid-integer-value" : "Valore intero non valido", + "double" : "Double", + "double-value" : "Valore double", + "double-value-required" : "Il valore double è obbligatorio", + "boolean" : "Booleano", + "boolean-value" : "Valore booleano", + "false" : "Falso", + "true" : "Vero", + "long" : "Long", + "json" : "JSON", + "json-value" : "Valore JSON", + "json-value-invalid" : "Il valore JSON ha un formato non valido", + "json-value-required" : "Il valore JSON è obbligatorio." + }, + "version-control" : { + "version-control" : "Controllo versioni", + "management" : "Gestione controllo versioni", + "search" : "Cerca versioni", + "branch" : "Ramo", + "default" : "Predefinito", + "select-branch" : "Seleziona ramo", + "branch-required" : "Il ramo è obbligatorio", + "create-entity-version" : "Crea versione entità", + "version-name" : "Nome versione", + "version-name-required" : "Il nome versione è obbligatorio", + "author" : "Autore", + "export-relations" : "Esporta relazioni", + "export-attributes" : "Esporta attributi", + "export-credentials" : "Esporta credenziali", + "export-calculated-fields" : "Esporta campi calcolati", + "entity-versions" : "Versioni entità", + "versions" : "Versioni", + "created-time" : "Ora di creazione", + "version-id" : "ID versione", + "no-entity-versions-text" : "Nessuna versione entità trovata", + "no-versions-text" : "Nessuna versione trovata", + "copy-full-version-id" : "Copia ID versione completo", + "create-version" : "Crea versione", + "creating-version" : "Creazione versione... Attendere", + "nothing-to-commit" : "Nessuna modifica da inviare", + "restore-version" : "Ripristina versione", + "restore-entity-from-version" : "Ripristina entità dalla versione '{{versionName}}'", + "restoring-entity-version" : "Ripristino versione entità... Attendere", + "load-relations" : "Carica relazioni", + "load-attributes" : "Carica attributi", + "load-credentials" : "Carica credenziali", + "load-calculated-fields" : "Carica campi calcolati", + "compare-with-current" : "Confronta con l'attuale", + "diff-entity-with-version" : "Differenze con la versione '{{versionName}}'", + "previous-difference" : "Differenza precedente", + "next-difference" : "Prossima differenza", + "current" : "Attuale", + "differences" : "{ count, plural, =1 {1 differenza} other {# differenze} }", + "create-entities-version" : "Crea versione entità multiple", + "default-sync-strategy" : "Strategia di sincronizzazione predefinita", + "sync-strategy-merge" : "Unisci", + "sync-strategy-overwrite" : "Sovrascrivi", + "entities-to-export" : "Entità da esportare", + "entities-to-restore" : "Entità da ripristinare", + "sync-strategy" : "Strategia di sincronizzazione", + "all-entities" : "Tutte le entità", + "no-entities-to-export-prompt" : "Specificare le entità da esportare", + "no-entities-to-restore-prompt" : "Specificare le entità da ripristinare", + "add-entity-type" : "Aggiungi tipo entità", + "remove-all" : "Rimuovi tutto", + "version-create-result" : "{ added, plural, =0 {Nessuna entità} =1 {1 entità} other {# entità} } aggiunta.
{ modified, plural, =0 {Nessuna entità} =1 {1 entità} other {# entità} } modificata.
{ removed, plural, =0 {Nessuna entità} =1 {1 entità} other {# entità} } rimossa.", + "remove-other-entities" : "Rimuovi altre entità", + "find-existing-entity-by-name" : "Trova entità esistente per nome", + "restore-entities-from-version" : "Ripristina entità dalla versione '{{versionName}}'", + "restoring-entities-from-version" : "Ripristino entità... Attendere", + "no-entities-restored" : "Nessuna entità ripristinata", + "created" : "{{created}} create", + "updated" : "{{updated}} aggiornate", + "deleted" : "{{deleted}} eliminate", + "remove-other-entities-confirm-text" : "Attenzione! Questo eliminerà in modo permanente tutte le entità attuali
non presenti nella versione che si desidera ripristinare.

Digitare \"remove other entities\" per confermare.", + "auto-commit-to-branch" : "auto-commit al ramo {{ branch }}", + "default-create-entity-version-name" : "Aggiornamento {{entityName}}", + "sync-strategy-merge-hint" : "Crea o aggiorna le entità selezionate nel repository. Tutte le altre entità nel repository non vengono modificate.", + "sync-strategy-overwrite-hint" : "Crea o aggiorna le entità selezionate nel repository. Tutte le altre entità nel repository vengono eliminate.", + "device-credentials-conflict" : "Impossibile caricare il dispositivo con ID esterno {{entityId}}
poiché le stesse credenziali sono già presenti nel database per un altro dispositivo.
Valutare la possibilità di disabilitare l'opzione carica credenziali nel modulo di ripristino.", + "missing-referenced-entity" : "Impossibile caricare {{sourceEntityTypeName}} con ID esterno {{sourceEntityId}}
perché fa riferimento a {{targetEntityTypeName}} mancante con ID {{targetEntityId}}.", + "runtime-failed" : "Errore: {{message}}", + "auto-commit-settings-read-only-hint" : "La funzione di auto-commit non funziona quando l'opzione di sola lettura è abilitata nelle impostazioni del repository.", + "rollback-on-error" : "Rollback in caso di errore", + "rollback-on-error-hint" : "Se devi ripristinare un numero elevato di entità, valuta la possibilità di disabilitare questa opzione per migliorare le prestazioni.\nNota: in caso di errore durante il caricamento della versione, le entità già salvate (con relazioni, attributi, ecc.) rimarranno invariate." + }, + "widget" : { + "widget-library" : "Libreria widget", + "widget-bundle" : "Bundle di widget", + "all-bundles" : "Tutti i bundle", + "select-widgets-bundle" : "Seleziona bundle di widget", + "widgets" : "Widget", + "all-widgets" : "Tutti i widget", + "widget" : "Widget", + "select-widget" : "Seleziona widget", + "no-widgets-matching" : "Nessun widget corrispondente a '{{entity}}' trovato.", + "no-widgets" : "Nessun widget", + "no-widgets-text" : "Nessun widget trovato", + "management" : "Gestione widget", + "editor" : "Editor widget", + "confirm-to-exit-editor-html" : "Hai modifiche non salvate al widget.
Sei sicuro di voler lasciare questa pagina?", + "widget-type-not-found" : "Problema nel caricamento della configurazione del widget.
Probabilmente il tipo di widget associato è stato rimosso.", + "widget-type-load-error" : "Il widget non è stato caricato a causa dei seguenti errori:", + "remove" : "Rimuovi widget", + "delete" : "Elimina widget", + "edit" : "Modifica widget", + "remove-widget-title" : "Sei sicuro di voler rimuovere il widget '{{widgetTitle}}'?", + "remove-widget-text" : "Dopo la conferma, il widget e tutti i dati correlati non saranno più recuperabili.", + "replace-reference-with-widget-copy" : "Sostituisci riferimento con copia del widget", + "timeseries" : "Serie temporali", + "search-data" : "Cerca dati", + "no-data-found" : "Nessun dato trovato", + "latest" : "Valori recenti", + "rpc" : "Widget di controllo", + "alarm" : "Widget allarme", + "static" : "Widget statico", + "timeseries-short" : "serie", + "latest-short" : "recenti", + "rpc-short" : "controllo", + "alarm-short" : "allarme", + "static-short" : "statico", + "select-widget-type" : "Seleziona tipo di widget", + "missing-widget-title-error" : "Il titolo del widget deve essere specificato!", + "widget-saved" : "Widget salvato", + "unable-to-save-widget-error" : "Impossibile salvare il widget! Il widget contiene errori!", + "save" : "Salva widget", + "saveAs" : "Salva widget come", + "move" : "Sposta widget", + "save-widget-as" : "Salva widget come", + "save-widget-as-text" : "Inserisci il nuovo titolo del widget", + "toggle-fullscreen" : "Attiva/disattiva schermo intero", + "run" : "Esegui widget", + "widget-title" : "Titolo del widget", + "title" : "Titolo", + "title-required" : "Il titolo del widget è obbligatorio.", + "title-max-length" : "Il titolo deve contenere meno di 256 caratteri", + "system" : "Sistema", + "type" : "Tipo di widget", + "resources" : "Risorse", + "resource-url" : "URL JavaScript/CSS", + "resource-is-extension" : "È un'estensione", + "remove-resource" : "Rimuovi risorsa", + "add-resource" : "Aggiungi risorsa", + "html" : "HTML", + "tidy" : "Tidy", + "css" : "CSS", + "settings-form" : "Modulo impostazioni", + "data-key-settings-form" : "Modulo impostazioni chiavi dati", + "latest-data-key-settings-form" : "Modulo impostazioni dati recenti", + "widget-settings" : "Impostazioni widget", + "description" : "Descrizione", + "tags" : "Tag", + "image-preview" : "Anteprima immagine", + "settings-form-selector" : "Selettore modulo impostazioni", + "data-key-settings-form-selector" : "Selettore modulo chiavi dati", + "latest-data-key-settings-form-selector" : "Selettore modulo dati recenti", + "all" : "Tutti", + "actual" : "Attuale", + "scada" : "Simbolo SCADA", + "deprecated" : "Obsoleto", + "has-basic-mode" : "Ha modalità base", + "basic-mode-form-selector" : "Selettore modulo modalità base", + "basic-mode" : "Base", + "advanced-mode" : "Avanzata", + "javascript" : "Javascript", + "js" : "JS", + "delete-widget-title" : "Sei sicuro di voler eliminare il widget '{{widgetName}}'?", + "delete-widget-text" : "Dopo la conferma, il widget e tutti i dati correlati non saranno più recuperabili.", + "delete-widgets-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 widget} other {# widget} }?", + "delete-widgets-text" : "Attenzione, dopo la conferma tutti i widget selezionati verranno eliminati e i relativi dati non saranno più recuperabili.", + "delete-widget" : "Elimina widget", + "widget-template-load-failed-error" : "Impossibile caricare il template del widget!", + "details" : "Dettagli", + "widget-details" : "Dettagli del widget", + "add" : "Aggiungi widget", + "add-existing-widget" : "Aggiungi widget esistente", + "add-new-widget" : "Aggiungi nuovo widget", + "search-widgets" : "Cerca widget", + "selected-widgets" : "{ count, plural, =1 {1 widget} other {# widget} } selezionato", + "undo" : "Annulla modifiche al widget", + "export" : "Esporta widget", + "export-prompt" : "Incorpora immagini e risorse del widget", + "export-widgets" : "Esporta widget", + "export-widgets-prompt" : "Incorpora immagini e risorse dei widget", + "import" : "Importa widget", + "no-data" : "Nessun dato da visualizzare nel widget", + "data-overflow" : "Il widget visualizza {{count}} su {{total}} entità", + "alarm-data-overflow" : "Il widget visualizza allarmi per {{allowedEntities}} entità (massimo consentito) su un totale di {{totalEntities}} entità", + "search" : "Cerca widget", + "filter" : "Tipo di filtro del widget", + "loading-widgets" : "Caricamento widget...", + "widget-template-error" : "Template HTML del widget non valido.", + "reference" : "Riferimento" + }, + "widget-action" : { + "header-button" : "Pulsante dell'intestazione del widget", + "do-nothing" : "Non fare nulla", + "open-dashboard-state" : "Naviga verso un nuovo stato della dashboard", + "update-dashboard-state" : "Aggiorna lo stato corrente della dashboard", + "open-dashboard" : "Naviga verso un'altra dashboard", + "custom" : "Azione personalizzata", + "custom-pretty" : "Azione personalizzata (con template HTML)", + "custom-pretty-error-title" : "Errore del dialog personalizzato", + "custom-pretty-template-error" : "Template del dialog personalizzato non valido.", + "custom-pretty-controller-error" : "Si è verificato un errore durante la valutazione della funzione del dialog personalizzato.", + "mobile-action" : "Azione mobile", + "target-dashboard-state" : "Stato della dashboard di destinazione", + "target-dashboard-state-required" : "Lo stato della dashboard di destinazione è obbligatorio", + "set-entity-from-widget" : "Imposta entità dal widget", + "target-dashboard" : "Dashboard di destinazione", + "select-target-dashboard" : "Seleziona la dashboard di destinazione", + "target-dashboard-required" : "La dashboard di destinazione è obbligatoria.", + "open-right-layout" : "Apri layout destro della dashboard (vista mobile)", + "state-display-type" : "Opzione di visualizzazione dello stato della dashboard", + "open-normal" : "Normale", + "open-in-separate-dialog" : "Apri in una finestra di dialogo separata", + "open-in-popover" : "Apri in un popover", + "dialog-title" : "Titolo del dialog", + "dialog-hide-dashboard-toolbar" : "Nascondi la barra strumenti della dashboard nel dialog", + "dialog-width" : "Larghezza del dialog in percentuale rispetto alla larghezza della finestra", + "dialog-height" : "Altezza del dialog in percentuale rispetto all'altezza della finestra", + "dialog-size-range-error" : "Il valore percentuale della dimensione del dialog deve essere compreso tra 1 e 100.", + "popover-preferred-placement" : "Posizionamento preferito del popover", + "popover-placement-top" : "In alto", + "popover-placement-topLeft" : "In alto a sinistra", + "popover-placement-topRight" : "In alto a destra", + "popover-placement-right" : "A destra", + "popover-placement-rightTop" : "In alto a destra", + "popover-placement-rightBottom" : "In basso a destra", + "popover-placement-bottom" : "In basso", + "popover-placement-bottomLeft" : "In basso a sinistra", + "popover-placement-bottomRight" : "In basso a destra", + "popover-placement-left" : "A sinistra", + "popover-placement-leftTop" : "In alto a sinistra", + "popover-placement-leftBottom" : "In basso a sinistra", + "popover-hide-on-click-outside" : "Chiudi popover cliccando all'esterno", + "popover-hide-dashboard-toolbar" : "Nascondi la barra strumenti della dashboard nel popover", + "popover-width" : "Larghezza del popover", + "popover-height" : "Altezza del popover", + "popover-style" : "Stile del popover", + "open-new-browser-tab" : "Apri in una nuova scheda del browser", + "open-URL" : "Apri URL", + "URL" : "URL", + "url-required" : "L'URL è obbligatorio.", + "mobile" : { + "device-provision" : "Provisioning dispositivo", + "action-type" : "Tipo di azione mobile", + "select-action-type" : "Seleziona tipo di azione mobile", + "action-type-required" : "Il tipo di azione mobile è obbligatorio", + "take-picture-from-gallery" : "Scatta foto dalla galleria", + "take-photo" : "Scatta foto", + "map-direction" : "Apri indicazioni mappa", + "map-location" : "Apri posizione sulla mappa", + "scan-qr-code" : "Scansiona codice QR", + "make-phone-call" : "Effettua una chiamata", + "get-location" : "Ottieni la posizione del telefono", + "take-screenshot" : "Cattura schermata" + }, + "custom-action-function" : "Funzione azione personalizzata", + "custom-pretty-function" : "Funzione azione personalizzata (con template HTML)", + "map-item-type" : "Tipo di elemento mappa", + "map-item" : { + "marker" : "Indicatore", + "polygon" : "Poligono", + "rectangle" : "Rettangolo", + "circle" : "Cerchio" + }, + "place-map-item" : "Posiziona elemento sulla mappa", + "map-item-tooltip" : { + "customize-map-item-tooltips" : "Personalizza tooltip dell'elemento mappa", + "place-marker" : "Posiziona indicatore", + "start-draw-rectangle" : "Inizia a disegnare rettangolo", + "finish-draw-rectangle" : "Termina disegno rettangolo", + "start-draw-polygon" : "Inizia a disegnare poligono", + "continue-draw-polygon" : "Continua disegno poligono", + "finish-draw-polygon" : "Termina disegno poligono", + "start-draw-circle" : "Inizia a disegnare cerchio", + "finish-draw-circle" : "Termina disegno cerchio" + } + }, + "widgets-bundle" : { + "current" : "Bundle corrente", + "widgets-bundles" : "Bundle di widget", + "widgets-bundle-widgets" : "Widget del bundle", + "add" : "Aggiungi bundle di widget", + "delete" : "Elimina bundle di widget", + "title" : "Titolo", + "title-required" : "Il titolo è obbligatorio.", + "title-max-length" : "Il titolo deve contenere meno di 256 caratteri", + "description" : "Descrizione", + "image-preview" : "Anteprima immagine", + "scada" : "Bundle di widget SCADA", + "order" : "Ordine", + "add-widgets-bundle-text" : "Aggiungi nuovo bundle di widget", + "no-widgets-bundles-text" : "Nessun bundle di widget trovato", + "empty" : "Il bundle di widget è vuoto", + "details" : "Dettagli", + "widgets-bundle-details" : "Dettagli del bundle di widget", + "delete-widgets-bundle-title" : "Sei sicuro di voler eliminare il bundle di widget '{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text" : "Attenzione, dopo la conferma il bundle di widget e tutti i dati correlati saranno irrecuperabili.", + "delete-widgets-bundles-title" : "Sei sicuro di voler eliminare { count, plural, =1 {1 bundle di widget} other {# bundle di widget} }?", + "delete-widgets-bundles-action-title" : "Elimina { count, plural, =1 {1 bundle di widget} other {# bundle di widget} }", + "delete-widgets-bundles-text" : "Attenzione, dopo la conferma tutti i bundle di widget selezionati verranno rimossi e tutti i dati correlati saranno irrecuperabili.", + "no-widgets-bundles-matching" : "Nessun bundle di widget corrispondente a '{{widgetsBundle}}' trovato.", + "widgets-bundle-required" : "Il bundle di widget è obbligatorio.", + "system" : "Sistema", + "import" : "Importa bundle di widget", + "export" : "Esporta bundle di widget", + "export-widgets-bundle-widgets-prompt" : "Includi i widget del bundle nei dati esportati (in alternativa verranno esportati solo i FQN dei widget)", + "export-failed-error" : "Impossibile esportare il bundle di widget: {{error}}", + "create-new-widgets-bundle" : "Crea nuovo bundle di widget", + "widgets-bundle-file" : "File del bundle di widget", + "invalid-widgets-bundle-file-error" : "Impossibile importare il bundle di widget: struttura dati del file non valida.", + "search" : "Cerca bundle di widget", + "selected-widgets-bundles" : "{ count, plural, =1 {1 bundle di widget} other {# bundle di widget} } selezionato", + "open-widgets-bundle" : "Apri bundle di widget", + "loading-widgets-bundles" : "Caricamento dei bundle di widget...", + "create-new" : "Crea nuovo bundle di widget" + }, + "widget-config" : { + "data" : "Dati", + "settings" : "Impostazioni", + "advanced" : "Avanzate", + "appearance" : "Aspetto", + "widget-card" : "Scheda del widget", + "mobile" : "Mobile", + "title" : "Titolo", + "title-tooltip" : "Tooltip del titolo", + "general-settings" : "Impostazioni generali", + "display-title" : "Mostra titolo del widget", + "card-title" : "Titolo della scheda", + "drop-shadow" : "Ombra esterna", + "enable-fullscreen" : "Abilita modalità a schermo intero", + "background-color" : "Colore di sfondo", + "text-color" : "Colore del testo", + "border-radius" : "Raggio del bordo", + "padding" : "Padding", + "margin" : "Margine", + "widget-style" : "Stile del widget", + "widget-css" : "CSS del widget", + "title-style" : "Stile del titolo", + "mobile-mode-settings" : "Modalità mobile", + "order" : "Ordine", + "height" : "Altezza", + "mobile-hide" : "Nascondi il widget in modalità mobile", + "desktop-hide" : "Nascondi il widget in modalità desktop", + "units" : "Simbolo speciale da mostrare accanto al valore", + "units-by-default" : "Unità predefinite", + "decimals" : "Numero di cifre dopo la virgola", + "decimals-by-default" : "Decimali predefiniti", + "default-data-key-parameter-hint" : "Questo parametro si applica a tutti i valori del widget a meno che non sia sovrascritto dalla configurazione della chiave dati", + "units-short" : "Unità", + "decimals-short" : "Decimali", + "decimals-suffix" : "decimali", + "digits-suffix" : "cifre", + "timewindow" : "Finestra temporale", + "use-dashboard-timewindow" : "Usa la finestra temporale della dashboard", + "use-widget-timewindow" : "Usa la finestra temporale del widget", + "display-timewindow" : "Mostra finestra temporale", + "legend" : "Legenda", + "display-legend" : "Mostra legenda", + "datasources" : "Origini dati", + "datasource" : "Origine dati", + "maximum-datasources" : "Massimo { count, plural, =1 {è consentita 1 origine dati.} other {sono consentite # origini dati} }", + "timeseries-key-error" : "È necessario specificare almeno una chiave dati di serie temporali", + "datasource-type" : "Tipo", + "datasource-parameters" : "Parametri", + "remove-datasource" : "Rimuovi origine dati", + "add-datasource" : "Aggiungi origine dati", + "target-device" : "Dispositivo di destinazione", + "alarm-source" : "Fonte dell'allarme", + "actions" : "Azioni", + "action" : "Azione", + "add-action" : "Aggiungi azione", + "search-actions" : "Cerca azioni", + "no-actions-text" : "Nessuna azione trovata", + "action-source" : "Fonte dell'azione", + "select-action-source" : "Seleziona fonte dell'azione", + "action-source-required" : "La fonte dell'azione è obbligatoria.", + "column-index" : "Indice colonna", + "select-column-index" : "Seleziona indice colonna", + "column-index-required" : "L'indice colonna è obbligatorio.", + "not-set" : "Non impostato", + "action-name" : "Nome", + "action-name-required" : "Il nome dell'azione è obbligatorio.", + "action-name-not-unique" : "Esiste già un'altra azione con lo stesso nome.\nIl nome dell'azione deve essere univoco all'interno della stessa fonte dell'azione.", + "action-icon" : "Icona", + "header-button" : { + "button-settings" : "Impostazioni pulsante", + "button-type" : "Tipo di pulsante", + "button-type-basic" : "Base", + "button-type-raised" : "Sollecitato", + "button-type-stroked" : "Contornato", + "button-type-flat" : "Piatto", + "button-type-icon" : "Icona", + "button-type-mini-fab" : "FAB", + "colors" : "Colori", + "color" : "Colore", + "background" : "Sfondo", + "border" : "Bordo", + "advanced-button-style" : "Stile avanzato del pulsante", + "button-style" : "Stile del pulsante" + }, + "show-hide-action-using-function" : "Mostra/nascondi azione usando una funzione", + "show-action-function" : "Funzione per mostrare azione", + "action-type" : "Tipo", + "action-type-required" : "Il tipo di azione è obbligatorio.", + "edit-action" : "Modifica azione", + "delete-action" : "Elimina azione", + "delete-action-title" : "Elimina azione del widget", + "delete-action-text" : "Sei sicuro di voler eliminare l'azione del widget con nome '{{actionName}}'?", + "title-icon" : "Icona del titolo", + "display-icon" : "Mostra icona del titolo", + "card-icon" : "Icona della scheda", + "icon" : "Icona", + "icon-color" : "Colore icona", + "icon-size" : "Dimensione icona", + "advanced-settings" : "Impostazioni avanzate", + "data-settings" : "Impostazioni dati", + "limits" : "Limiti", + "no-data-display-message" : "Messaggio alternativo \"Nessun dato da visualizzare\"", + "data-page-size" : "Entità massime per origine dati", + "settings-component-not-found" : "Componente del modulo impostazioni non trovato per il selettore '{{selector}}'", + "preview" : "Anteprima", + "set" : "Imposta", + "set-message" : "Imposta messaggio", + "advanced-title-style" : "Stile avanzato del titolo", + "card-style" : "Stile della scheda", + "text" : "Testo", + "background" : "Sfondo", + "advanced-widget-style" : "Stile avanzato del widget", + "card-buttons" : "Pulsanti della scheda", + "show-card-buttons" : "Mostra pulsanti della scheda", + "card-border-radius" : "Raggio del bordo della scheda", + "card-padding" : "Spaziatura interna della scheda", + "card-appearance" : "Aspetto della scheda", + "color" : "Colore", + "tooltip" : "Tooltip", + "units-required" : "L'unità è obbligatoria.", + "list-layout" : "Layout a elenco", + "layout" : "Layout", + "resize-options" : "Opzioni di ridimensionamento", + "resizable" : "Ridimensionabile", + "preserve-aspect-ratio" : "Mantieni proporzioni" + }, + "widget-type" : { + "import" : "Importa tipo di widget", + "export" : "Esporta tipo di widget", + "export-failed-error" : "Impossibile esportare il widget: {{error}}", + "widget-file" : "File del widget", + "invalid-widget-file-error" : "Impossibile importare il widget: struttura dati del widget non valida." + }, + "markdown" : { + "edit" : "Modifica", + "preview" : "Anteprima", + "copy-code" : "Clicca per copiare", + "copied" : "Copiato!" + }, + "widgets" : { + "mobile-app-qr-code" : { + "configuration-hint" : "La configurazione dipende dal widget Codice QR dell'app mobile nelle impostazioni principali della piattaforma", + "get-it-on-google-play" : "Disponibile su Google Play", + "download-on-the-app-store" : "Scarica su App Store" + }, + "action-button" : { + "behavior" : "Comportamento", + "on-click" : "Al clic", + "on-click-hint" : "Azione attivata quando si fa clic sul pulsante", + "first-button-click" : "Clic primo pulsante", + "first-button-click-hint" : "Azione eseguita alla pressione del primo pulsante.", + "second-button-click" : "Clic secondo pulsante", + "second-button-click-hint" : "Azione eseguita alla pressione del secondo pulsante.", + "button-click-hint" : "Azione eseguita alla pressione del widget." + }, + "command-button" : { + "behavior" : "Comportamento", + "on-click" : "Al clic", + "on-click-hint" : "Azione eseguita quando si fa clic sul pulsante." + }, + "power-button" : { + "behavior" : "Comportamento", + "power-on" : "Accensione 'On'", + "power-on-hint" : "Azione eseguita per accendere il componente.", + "power-off" : "Spegnimento 'Off'", + "power-off-hint" : "Azione eseguita per spegnere il componente.", + "on-label" : "Acceso", + "off-label" : "Spento", + "layout" : "Layout", + "layout-default" : "Predefinito", + "layout-simplified" : "Semplificato", + "layout-outlined" : "Contornato", + "layout-default-volume" : "Predefinito.Volume", + "layout-simplified-volume" : "Semplificato.Volume", + "layout-outlined-volume" : "Contornato.Volume", + "layout-default-icon" : "Predefinito.Icona", + "layout-simplified-icon" : "Semplificato.Icona", + "layout-outlined-icon" : "Contornato.Icona", + "main" : "Principale", + "background" : "Sfondo", + "button-icon-on" : "Icona del pulsante 'On'", + "button-icon-off" : "Icona del pulsante 'Off'", + "power-on-colors" : "Colori 'On'", + "power-off-colors" : "Colori 'Off'", + "disabled-colors" : "Colori disabilitati", + "button" : "Pulsante" + }, + "toggle-button" : { + "behavior" : "Comportamento", + "checked" : "Selezionato", + "unchecked" : "Deselezionato", + "check" : "Seleziona", + "check-hint" : "Azione eseguita per selezionare il componente.", + "uncheck" : "Deseleziona", + "uncheck-hint" : "Azione eseguita per deselezionare il componente.", + "auto-scale" : "Ridimensionamento automatico", + "horizontal-fill" : "Riempimento orizzontale", + "vertical-fill" : "Riempimento verticale", + "button-appearance" : "Aspetto del pulsante" + }, + "segmented-button" : { + "layout" : "Layout", + "layout-squared" : "Quadrato", + "layout-rounded" : "Arrotondato", + "card-border" : "Bordo della scheda", + "button-appearance" : "Aspetto del pulsante", + "first" : "Primo", + "second" : "Secondo", + "color-styles" : "Stili di colore", + "selected" : "Selezionato", + "unselected" : "Non selezionato" + }, + "button" : { + "layout" : "Layout", + "outlined" : "Contornato", + "filled" : "Riempito", + "underlined" : "Sottolineato", + "basic" : "Base", + "auto-scale" : "Ridimensionamento automatico", + "label" : "Etichetta", + "icon" : "Icona", + "border-radius" : "Raggio del bordo", + "color-palette" : "Palette di colori", + "main" : "Principale", + "background" : "Sfondo", + "border" : "Bordo", + "custom-styles" : "Stili personalizzati", + "clear-style" : "Cancella stile", + "shadow" : "Ombra", + "enabled" : "Abilitato", + "disabled" : "Disabilitato", + "preview" : "Anteprima", + "copy-style-from" : "Copia stile da" + }, + "value-stepper" : { + "behavior" : "Comportamento", + "simplified" : "Semplificato", + "filled" : "Riempito", + "outlined" : "Contornato", + "volume" : "Volume", + "initial-state" : "Stato iniziale", + "initial-state-hint" : "Azione per ottenere il valore iniziale.", + "disabled-state" : "Stato disabilitato", + "disabled-state-hint" : "Configura la condizione in cui il componente è disabilitato.", + "right-button-click" : "Clic pulsante destro", + "right-button-click-hint" : "Azione eseguita alla pressione del pulsante destro.", + "left-button-click" : "Clic pulsante sinistro", + "left-button-click-hint" : "Azione eseguita alla pressione del pulsante sinistro.", + "auto-scale" : "Ridimensionamento automatico", + "value-range" : "Intervallo", + "min-range" : "Minimo", + "max-range" : "Massimo", + "value-increment-decrement-step" : "Passo di incremento/decremento del valore", + "value" : "Valore", + "value-box-background" : "Sfondo del riquadro valore", + "border" : "Bordo", + "button-appearance" : "Aspetto del pulsante", + "left" : "Sinistra", + "right" : "Destra", + "left-button" : "Pulsante sinistro", + "right-button" : "Pulsante destro", + "icon" : "Icona", + "color-palette" : "Palette di colori", + "main" : "Principale", + "background" : "Sfondo", + "button-icon-on" : "Icona pulsante 'On'", + "button-on-colors" : "Colori pulsante 'On'", + "disabled-colors" : "Colori disabilitati" + }, + "button-state" : { + "activated-state" : "Stato attivato", + "activated-state-hint" : "Configura la condizione in cui il pulsante è attivo.", + "disabled-state" : "Stato disabilitato", + "disabled-state-hint" : "Configura la condizione in cui il pulsante è disabilitato.", + "selected-state" : "Stato selezionato", + "selected-state-hint" : "Configura la condizione in cui il pulsante è selezionato.", + "enabled" : "Abilitato", + "hovered" : "Passaggio del mouse", + "pressed" : "Premuto", + "activated" : "Attivato", + "disabled" : "Disabilitato", + "initial" : "Primo pulsante", + "first" : "Primo", + "second" : "Secondo" + }, + "background" : { + "background" : "Sfondo", + "background-settings" : "Impostazioni sfondo", + "background-type-image" : "Immagine", + "background-type-color" : "Colore", + "image-url" : "URL immagine", + "overlay" : "Sovrapposizione", + "enable-overlay" : "Abilita sovrapposizione", + "blur" : "Sfocatura", + "preview" : "Anteprima" + }, + "bar-chart" : { + "bar-appearance" : "Aspetto della barra", + "label-on-bar" : "Etichetta sulla barra", + "value-on-bar" : "Valore sulla barra", + "bar-chart-style" : "Stile grafico a barre", + "bar-axis" : "Asse delle barre" + }, + "polar-area-chart" : { + "polar-axis" : "Asse polare", + "start-angle" : "Angolo di partenza", + "polar-area-chart-style" : "Stile del grafico ad area polare" + }, + "battery-level" : { + "layout" : "Layout", + "layout-vertical-solid" : "Verticale. Solido", + "layout-horizontal-solid" : "Orizzontale. Solido", + "layout-vertical-divided" : "Verticale. Diviso", + "layout-horizontal-divided" : "Orizzontale. Diviso", + "icon" : "Icona", + "value" : "Valore", + "auto-scale" : "Ridimensionamento automatico", + "battery-level-color" : "Colore del livello batteria", + "battery-shape-color" : "Colore della forma della batteria", + "battery-level-card-style" : "Stile scheda livello batteria", + "sections-count" : "Numero di sezioni" + }, + "signal-strength" : { + "value" : "Valore", + "last-update" : "Ultimo aggiornamento", + "no-signal" : "Nessun segnale", + "layout" : "Layout", + "layout-wifi" : "Wi-Fi", + "layout-cellular-bar" : "Barra segnale cellulare", + "icon" : "Icona", + "date" : "Data", + "active-bars-color" : "Colore barre attive", + "inactive-bars-color" : "Colore barre inattive", + "signal-strength-card-style" : "Stile della scheda segnale", + "no-signal-rssi-value" : "Valore RSSI per \"Nessun segnale\"" + }, + "status-widget" : { + "behavior" : "Comportamento", + "layout" : "Layout", + "layout-default" : "Predefinito", + "layout-center" : "Centrato", + "layout-icon" : "Icona", + "on" : "Acceso", + "off" : "Spento", + "label" : "Etichetta", + "status" : "Stato", + "icon" : "Icona", + "color-palette" : "Palette colori", + "disabled-color-palette" : "Palette colori disabilitata", + "primary" : "Primario", + "primary-color-hint" : "Colore di icona ed etichetta", + "secondary" : "Secondario", + "secondary-color-hint" : "Colore dello stato", + "background" : "Sfondo" + }, + "chart" : { + "common-settings" : "Impostazioni comuni", + "enable-stacking-mode" : "Abilita modalità accumulo", + "selection" : "Selezione intervallo temporale", + "enable-selection-mode" : "Abilita modalità selezione", + "line-shadow-size" : "Dimensione ombra linea", + "display-smooth-lines" : "Mostra linee curve", + "default-bar-width" : "Larghezza barra predefinita per dati non aggregati (millisecondi)", + "bar-alignment" : "Allineamento barra", + "bar-alignment-left" : "Sinistra", + "bar-alignment-right" : "Destra", + "bar-alignment-center" : "Centro", + "default-font" : "Font predefinito", + "default-font-size" : "Dimensione font predefinita", + "default-font-color" : "Colore font predefinito", + "thresholds-line-width" : "Spessore linea soglia predefinito", + "tooltip-settings" : "Impostazioni tooltip", + "tooltip" : "Tooltip", + "show-tooltip" : "Mostra tooltip", + "hover-individual-points" : "Evidenzia punti singoli", + "show-cumulative-values" : "Mostra valori cumulativi in modalità accumulo", + "hide-zero-false-values" : "Nascondi valori zero/falsi dal tooltip", + "tooltip-value-format-function" : "Funzione formato valore tooltip", + "grid-settings" : "Impostazioni griglia", + "show-vertical-lines" : "Mostra linee verticali", + "show-horizontal-lines" : "Mostra linee orizzontali", + "grid-outline-border-width" : "Spessore contorno griglia (px)", + "primary-color" : "Colore primario", + "background-color" : "Colore di sfondo", + "ticks-color" : "Colore tacche", + "xaxis-settings" : "Impostazioni asse X", + "axis-title" : "Titolo asse", + "xaxis-tick-labels-settings" : "Etichette tacche asse X", + "show-tick-labels" : "Mostra etichette tacche", + "yaxis-settings" : "Impostazioni asse Y", + "min-scale-value" : "Valore minimo scala", + "max-scale-value" : "Valore massimo scala", + "yaxis-tick-labels-settings" : "Etichette tacche asse Y", + "tick-step-size" : "Intervallo tacche", + "number-of-decimals" : "Numero di decimali da visualizzare", + "ticks-formatter-function" : "Funzione di formattazione tacche", + "comparison-settings" : "Impostazioni confronto", + "enable-comparison" : "Abilita confronto", + "time-for-comparison" : "Periodo di confronto", + "time-for-comparison-previous-interval" : "Intervallo precedente (predefinito)", + "time-for-comparison-days" : "Giorno fa", + "time-for-comparison-weeks" : "Settimana fa", + "time-for-comparison-months" : "Mese fa", + "time-for-comparison-years" : "Anno fa", + "time-for-comparison-custom-interval" : "Intervallo personalizzato", + "custom-interval-value" : "Valore intervallo personalizzato (ms)", + "comparison-x-axis-settings" : "Impostazioni asse X confronto", + "axis-position" : "Posizione asse", + "axis-position-top" : "Alto (predefinito)", + "axis-position-bottom" : "Basso", + "custom-legend-settings" : "Impostazioni legenda personalizzata", + "enable-custom-legend" : "Abilita legenda personalizzata (consente l'uso di attributi/serie temporali nelle etichette)", + "key-name" : "Nome chiave", + "key-name-required" : "Il nome della chiave è obbligatorio", + "key-type" : "Tipo di chiave", + "key-type-attribute" : "Attributo", + "key-type-timeseries" : "Serie temporale", + "label-keys-list" : "Elenco chiavi da usare nelle etichette", + "no-label-keys" : "Nessuna chiave configurata", + "add-label-key" : "Aggiungi nuova chiave", + "line-width" : "Spessore linea", + "color" : "Colore", + "data-is-hidden-by-default" : "I dati sono nascosti per impostazione predefinita", + "disable-data-hiding" : "Disabilita nascondimento dati", + "remove-from-legend" : "Rimuovi la chiave dati dalla legenda", + "exclude-from-stacking" : "Escludi dall'accumulo (disponibile in modalità \"Accumulamento\")", + "line-settings" : "Impostazioni linea", + "show-line" : "Mostra linea", + "fill-line" : "Riempimento linea", + "fill-line-opacity" : "Opacità riempimento", + "points-settings" : "Impostazioni punti", + "show-points" : "Mostra punti", + "points-line-width" : "Spessore linea dei punti", + "points-radius" : "Raggio dei punti", + "point-shape" : "Forma del punto", + "point-shape-circle" : "Cerchio", + "point-shape-cross" : "Croce", + "point-shape-diamond" : "Diamante", + "point-shape-square" : "Quadrato", + "point-shape-triangle" : "Triangolo", + "point-shape-custom" : "Funzione personalizzata", + "point-shape-draw-function" : "Funzione di disegno forma punto", + "show-separate-axis" : "Mostra asse separato", + "axis-position-left" : "Sinistra", + "axis-position-right" : "Destra", + "thresholds" : "Soglie", + "no-thresholds" : "Nessuna soglia configurata", + "add-threshold" : "Aggiungi soglia", + "show-values-for-comparison" : "Mostra valori storici per confronto", + "comparison-values-label" : "Etichetta valori storici", + "comparison-line-color" : "Colore linea di confronto", + "threshold-settings" : "Impostazioni soglia", + "use-as-threshold" : "Usa il valore chiave come soglia", + "threshold-line-width" : "Spessore linea soglia", + "threshold-color" : "Colore soglia", + "common-pie-settings" : "Impostazioni comuni torta", + "radius" : "Raggio", + "inner-radius" : "Raggio interno", + "tilt" : "Inclinazione", + "common-pie-settings-range-error" : "Il valore deve essere compreso tra 0 e 1", + "stroke-settings" : "Impostazioni contorno", + "width-pixels" : "Larghezza (pixel)", + "show-labels" : "Mostra etichette", + "animation-settings" : "Impostazioni animazione", + "animated-pie" : "Abilita animazione torta (sperimentale)", + "border-settings" : "Impostazioni bordo", + "border-width" : "Spessore bordo", + "border-color" : "Colore bordo", + "legend-settings" : "Impostazioni legenda", + "display-legend" : "Visualizza legenda", + "labels-font-color" : "Colore carattere etichette", + "series" : "Serie", + "add-series" : "Aggiungi serie", + "series-settings" : "Impostazioni serie", + "remove-series" : "Rimuovi serie", + "no-series" : "Nessuna serie configurata", + "no-series-error" : "Deve essere specificata almeno una serie", + "chart-appearance" : "Aspetto grafico", + "vertical-grid-lines" : "Linee griglia verticali", + "horizontal-grid-lines" : "Linee griglia orizzontali", + "chart-background" : "Sfondo grafico", + "grid-lines-color" : "Colore linee griglia", + "border" : "Bordo", + "axis" : "Asse", + "vertical-axis" : "Asse verticale", + "ticks" : "Tacche", + "horizontal-axis" : "Asse orizzontale", + "shape-empty-circle" : "Cerchio vuoto", + "shape-circle" : "Cerchio", + "shape-rect" : "Rettangolo", + "shape-round-rect" : "Rettangolo arrotondato", + "shape-triangle" : "Triangolo", + "shape-diamond" : "Diamante", + "shape-pin" : "Puntina", + "shape-arrow" : "Freccia", + "shape-none" : "Nessuna", + "line-type-solid" : "Solido", + "line-type-dashed" : "Tratteggiato", + "line-type-dotted" : "Punteggiato", + "label-position-top" : "Alto", + "label-position-bottom" : "Basso", + "label-position-outside" : "Esterno", + "label-position-inside" : "Interno", + "fill" : "Riempimento", + "fill-type-none" : "Nessuno", + "fill-type-solid" : "Solido", + "fill-type-opacity" : "Opacità", + "fill-type-gradient" : "Gradiente", + "background" : "Sfondo", + "opacity" : "Opacità", + "gradient-stops" : "Punti di gradiente", + "gradient-start" : "inizio", + "gradient-end" : "fine", + "animation" : { + "animation" : "Animazione", + "animation-threshold" : "Soglia di animazione", + "animation-duration" : "Durata animazione", + "animation-easing" : "Easing animazione", + "animation-delay" : "Ritardo animazione", + "update-animation-duration" : "Durata aggiornamento animazione", + "update-animation-easing" : "Easing aggiornamento animazione", + "update-animation-delay" : "Ritardo aggiornamento animazione" + }, + "chart-axis" : { + "scale" : "Scala", + "scale-min" : "min", + "scale-max" : "max", + "scale-auto" : "Auto" + }, + "bar" : { + "show-border" : "Mostra bordo", + "border-width" : "Spessore bordo", + "border-radius" : "Raggio bordo", + "bar-width" : "Larghezza barra", + "label" : "Etichetta", + "label-hint" : "Visualizza etichetta sopra la barra.", + "series-label-hint" : "Visualizza etichetta con valore sopra la barra.", + "label-background" : "Sfondo etichetta" + } + }, + "color" : { + "color-settings" : "Impostazioni colore", + "color-type-constant" : "Costante", + "color-type-gradient" : "Gradiente", + "color-type-range" : "Intervallo", + "color-type-function" : "Funzione", + "color" : "Colore", + "value-range" : "Intervallo di valori", + "from" : "Da", + "to" : "A", + "color-function" : "Funzione colore", + "copy-color-settings-from" : "Copia impostazioni colore da", + "copy-from" : "Copia da", + "settings-type" : "Tipo di impostazioni", + "basic-mode" : "Base", + "advanced-mode" : "Avanzato", + "entity-alias" : "Alias entità", + "entity-attribute" : "Attributo entità", + "gradient-color" : "Colore gradiente", + "gradient-color-min" : "Colore", + "gradient-start" : "Colore iniziale del gradiente", + "gradient-start-min" : "Inizio", + "gradient-end" : "Colore finale del gradiente", + "gradient-end-min" : "Fine", + "start-value" : "Valore iniziale", + "end-value" : "Valore finale", + "gradient-type" : "Tipo di gradiente" + }, + "dashboard-state" : { + "dashboard-state-settings" : "Impostazioni stato dashboard", + "dashboard-state" : "ID stato dashboard", + "autofill-state-layout" : "Compila automaticamente altezza layout stato per impostazione predefinita", + "default-margin" : "Margine predefinito dei widget", + "default-background-color" : "Colore di sfondo predefinito", + "sync-parent-state-params" : "Sincronizza parametri dello stato con il dashboard padre" + }, + "date-range-navigator" : { + "date-range-picker-settings" : "Impostazioni selezione intervallo date", + "hide-date-range-picker" : "Nascondi selezione intervallo date", + "picker-one-panel" : "Selettore intervallo date con un pannello", + "picker-auto-confirm" : "Conferma automatica selezione intervallo date", + "picker-show-template" : "Mostra template selezione intervallo date", + "first-day-of-week" : "Primo giorno della settimana", + "interval-settings" : "Impostazioni intervallo", + "hide-interval" : "Nascondi intervallo", + "initial-interval" : "Intervallo iniziale", + "interval-hour" : "Ora", + "interval-day" : "Giorno", + "interval-week" : "Settimana", + "interval-two-weeks" : "2 settimane", + "interval-month" : "Mese", + "interval-three-months" : "3 mesi", + "interval-six-months" : "6 mesi", + "step-settings" : "Impostazioni passo", + "hide-step-size" : "Nascondi dimensione passo", + "initial-step-size" : "Dimensione passo iniziale", + "hide-labels" : "Nascondi etichette", + "use-session-storage" : "Usa storage della sessione", + "localizationMap" : { + "Sun" : "Dom", + "Mon" : "Lun", + "Tue" : "Mar", + "Wed" : "Mer", + "Thu" : "Gio", + "Fri" : "Ven", + "Sat" : "Sab", + "Jan" : "Gen", + "Feb" : "Feb", + "Mar" : "Mar", + "Apr" : "Apr", + "May" : "Mag", + "Jun" : "Giu", + "Jul" : "Lug", + "Aug" : "Ago", + "Sep" : "Set", + "Oct" : "Ott", + "Nov" : "Nov", + "Dec" : "Dic", + "January" : "Gennaio", + "February" : "Febbraio", + "March" : "Marzo", + "April" : "Aprile", + "June" : "Giugno", + "July" : "Luglio", + "August" : "Agosto", + "September" : "Settembre", + "October" : "Ottobre", + "November" : "Novembre", + "December" : "Dicembre", + "Custom Date Range" : "Intervallo date personalizzato", + "Date Range Template" : "Template intervallo date", + "Today" : "Oggi", + "Yesterday" : "Ieri", + "This Week" : "Questa settimana", + "Last Week" : "Settimana scorsa", + "This Month" : "Questo mese", + "Last Month" : "Mese scorso", + "Year" : "Anno", + "This Year" : "Quest'anno", + "Last Year" : "Anno scorso", + "Date picker" : "Selettore data", + "Hour" : "Ora", + "Day" : "Giorno", + "Week" : "Settimana", + "2 weeks" : "2 settimane", + "Month" : "Mese", + "3 months" : "3 mesi", + "6 months" : "6 mesi", + "Custom interval" : "Intervallo personalizzato", + "Interval" : "Intervallo", + "Step size" : "Dimensione passo", + "Ok" : "Ok" + } + }, + "doughnut" : { + "doughnut-appearance" : "Aspetto del doughnut", + "layout" : "Layout", + "layout-default" : "Predefinito", + "layout-with-total" : "Con totale", + "central-total-value" : "Valore totale centrale", + "doughnut-card-style" : "Stile della scheda doughnut" + }, + "entities-hierarchy" : { + "hierarchy-data-settings" : "Impostazioni dei dati della gerarchia", + "relations-query-function" : "Funzione di query delle relazioni del nodo", + "has-children-function" : "Funzione che verifica se il nodo ha figli", + "node-state-settings" : "Impostazioni dello stato del nodo", + "node-opened-function" : "Funzione predefinita per nodo aperto", + "node-disabled-function" : "Funzione nodo disabilitato", + "display-settings" : "Impostazioni di visualizzazione", + "node-icon-function" : "Funzione icona del nodo", + "node-text-function" : "Funzione testo del nodo", + "sort-settings" : "Impostazioni di ordinamento", + "nodes-sort-function" : "Funzione di ordinamento dei nodi" + }, + "edge" : { + "display-default-title" : "Visualizza titolo predefinito" + }, + "gateway" : { + "general-settings" : "Impostazioni generali", + "widget-title" : "Titolo del widget", + "default-archive-file-name" : "Nome file di archivio predefinito", + "device-type-for-new-gateway" : "Tipo di dispositivo per nuovo gateway", + "messages-settings" : "Impostazioni dei messaggi", + "save-config-success-message" : "Messaggio di conferma salvataggio configurazione gateway", + "device-name-exists-message" : "Messaggio di errore: nome dispositivo già esistente", + "gateway-title" : "Modulo gateway", + "read-only" : "Sola lettura", + "events-title" : "Titolo modulo eventi gateway", + "events-filter" : "Filtro eventi", + "event-key-contains" : "Chiave evento contiene...", + "show-connector" : "Mostra per il connettore", + "connector-state-param-key" : "Chiave parametro stato connettore", + "message" : "Messaggio", + "level" : "Livello", + "created-time" : "Data di creazione" + }, + "gauge" : { + "default-color" : "Colore predefinito", + "radial-gauge-settings" : "Impostazioni del gauge radiale", + "ticks-settings" : "Impostazioni dei tick", + "min-value" : "Valore minimo", + "max-value" : "Valore massimo", + "min-value-short" : "min", + "max-value-short" : "max", + "start-ticks-angle" : "Angolo di inizio dei tick", + "ticks-angle" : "Angolo dei tick", + "major-ticks" : "Tick principali", + "major-ticks-count" : "Numero di tick principali", + "major-ticks-color" : "Colore dei tick principali", + "minor-ticks" : "Tick secondari", + "minor-ticks-count" : "Numero di tick secondari", + "minor-ticks-color" : "Colore dei tick secondari", + "tick-numbers-font" : "Font dei numeri dei tick", + "unit-title-settings" : "Impostazioni del titolo dell'unità", + "show-unit-title" : "Titolo unità", + "unit-title" : "Titolo unità", + "title-font" : "Font del testo del titolo", + "units-settings" : "Impostazioni delle unità", + "units-font" : "Font del testo delle unità", + "value-box-settings" : "Impostazioni della casella valore", + "show-value-box" : "Mostra casella valore", + "value-box" : "Casella valore", + "value-int" : "Numero di cifre per la parte intera del valore", + "value-text" : "Testo del valore", + "value-text-shadow" : "Ombra del testo del valore", + "value-font" : "Font del testo del valore", + "rect-stroke-color-start" : "Colore iniziale tratto rettangolo", + "rect-stroke-color-end" : "Colore finale tratto rettangolo", + "background-color" : "Colore di sfondo", + "shadow-color" : "Colore dell'ombra", + "value-box-rect-stroke-color" : "Colore tratto rettangolo casella valore", + "value-box-rect-stroke-color-end" : "Colore finale tratto rettangolo casella valore", + "value-box-background-color" : "Colore di sfondo della casella valore", + "value-box-shadow-color" : "Colore dell'ombra della casella valore", + "plate-settings" : "Impostazioni della piastra", + "show-plate-border" : "Bordo della piastra", + "plate-color" : "Colore della piastra", + "needle-settings" : "Impostazioni dell'indicatore", + "needle-circle-size" : "Dimensione cerchio indicatore", + "needle-color" : "Colore dell'indicatore", + "needle-color-start" : "Colore iniziale dell'indicatore", + "needle-color-end" : "Colore finale dell'indicatore", + "needle-color-shadow-up" : "Colore dell'ombra superiore dell'indicatore", + "needle-color-shadow-down" : "Ombra", + "highlights-settings" : "Impostazioni evidenziazioni", + "highlights-width" : "Larghezza evidenziazioni", + "highlights" : "Evidenziazioni", + "highlight-from" : "Da", + "highlight-to" : "A", + "highlight-color" : "Colore", + "no-highlights" : "Nessuna evidenziazione configurata", + "add-highlight" : "Aggiungi evidenziazione", + "animation-settings" : "Impostazioni dell'animazione", + "enable-animation" : "Animazione", + "animation-duration-rule" : "Durata e regola dell'animazione", + "animation-duration" : "Durata dell'animazione", + "animation-rule" : "Regola dell'animazione", + "animation-linear" : "Lineare", + "animation-quad" : "Quad", + "animation-quint" : "Quint", + "animation-cycle" : "Ciclo", + "animation-bounce" : "Rimbalzo", + "animation-elastic" : "Elastica", + "animation-dequad" : "Dequad", + "animation-dequint" : "Dequint", + "animation-decycle" : "Deciclo", + "animation-debounce" : "Debounce", + "animation-delastic" : "Delastica", + "linear-gauge-settings" : "Impostazioni gauge lineare", + "bar-stroke" : "Tratto della barra", + "bar-stroke-width" : "Larghezza tratto della barra", + "bar-stroke-color" : "Colore tratto della barra", + "bar-background-color" : "Colore di sfondo della barra - gradiente iniziale", + "bar-background-color-end" : "Colore di sfondo della barra - gradiente finale", + "progress-bar-color" : "Colore della barra di avanzamento", + "progress-bar" : "Barra di avanzamento", + "progress-bar-color-start" : "Colore barra di avanzamento - gradiente iniziale", + "progress-bar-color-end" : "Colore barra di avanzamento - gradiente finale", + "major-ticks-names" : "Nomi dei tick principali", + "show-stroke-ticks" : "Mostra tratto dei tick", + "major-ticks-font" : "Font dei tick principali", + "border-color" : "Colore del bordo", + "border-width" : "Larghezza del bordo", + "needle-circle" : "Cerchio dell'indicatore", + "needle-circle-color" : "Colore del cerchio dell'indicatore", + "animation-target" : "Obiettivo dell'animazione", + "animation-target-needle" : "Indicatore", + "animation-target-plate" : "Piastra", + "common-settings" : "Impostazioni comuni del gauge", + "gauge-type" : "Tipo di gauge", + "gauge-type-arc" : "Arco", + "gauge-type-donut" : "Ciambella", + "gauge-type-horizontal-bar" : "Barra orizzontale", + "gauge-type-vertical-bar" : "Barra verticale", + "donut-start-angle" : "Angolo di inizio (gradi)", + "bar-settings" : "Impostazioni della barra del gauge", + "relative-bar-width" : "Larghezza relativa della barra", + "neon-glow-brightness" : "Luminosità dell'effetto bagliore neon (0-100)", + "neon-glow-brightness-hint" : "0 - disabilita effetto", + "stripes-thickness" : "Spessore delle strisce", + "stripes-thickness-hint" : "0 - nessuna striscia", + "rounded-line-cap" : "Estremità linea arrotondata", + "bar-color-settings" : "Impostazioni colore della barra", + "use-precise-level-color-values" : "Usa valori di colore precisi", + "bar-colors" : "Colori della barra, da minore a maggiore", + "color" : "Colore", + "no-bar-colors" : "Nessun colore della barra configurato", + "add-bar-color" : "Aggiungi colore della barra", + "from" : "Da", + "to" : "A", + "fixed-level-colors" : "Colori della barra con valori limite", + "gauge-title-settings" : "Impostazioni del titolo del gauge", + "show-gauge-title" : "Mostra il titolo del gauge", + "gauge-title" : "Titolo del gauge", + "gauge-title-font" : "Font del titolo del gauge", + "unit-title-and-timestamp-settings" : "Impostazioni unità e timestamp", + "show-timestamp" : "Timestamp", + "timestamp-format" : "Formato timestamp", + "label-font" : "Font dell'etichetta sotto il valore", + "value-settings" : "Impostazioni del valore", + "show-value" : "Mostra testo del valore", + "min-max-settings" : "Impostazioni etichette minimo/massimo", + "show-min-max" : "Mostra valori minimo e massimo", + "min-max-font" : "Font delle etichette minimo e massimo", + "show-ticks" : "Mostra i tick", + "tick-width" : "Larghezza del tick", + "tick-color" : "Colore del tick", + "tick-values" : "Valori dei tick", + "no-tick-values" : "Nessun valore tick configurato", + "add-tick-value" : "Aggiungi valore tick", + "gauge-appearance" : "Aspetto del gauge", + "units-title" : "Titolo delle unità", + "value" : "Valore", + "ticks" : "Tick", + "arrow-and-scale-color" : "Colore predefinito di freccia e scala", + "scale-settings" : "Impostazioni della scala", + "scale" : "Scala", + "scale-color" : "Colori della scala", + "compass-appearance" : "Aspetto della bussola", + "label" : "Etichetta", + "labels" : "Etichette", + "label-style" : "Stile etichetta", + "simple-gauge-type" : "Tipo", + "gauge-bar-background" : "Sfondo della barra del gauge", + "bar-color" : "Colore della barra", + "min-and-max-value" : "Valore minimo e massimo", + "min-and-max-label" : "Etichetta minimo e massimo", + "font" : "Font", + "tick-width-and-color" : "Spessore e colore del tick", + "min-max-validation-text" : "Il valore massimo deve essere maggiore del valore minimo" + }, + "gpio" : { + "pin" : "Pin", + "label" : "Etichetta", + "row" : "Riga", + "column" : "Colonna", + "color" : "Colore", + "panel-settings" : "Impostazioni del pannello", + "background-color" : "Colore di sfondo", + "gpio-switches" : "Interruttori GPIO", + "no-gpio-switches" : "Nessun interruttore GPIO configurato", + "add-gpio-switch" : "Aggiungi interruttore GPIO", + "gpio-status-request" : "Richiesta stato GPIO", + "method-name" : "Nome del metodo", + "method-body" : "Corpo del metodo", + "gpio-status-change-request" : "Richiesta di modifica stato GPIO", + "parse-gpio-status-function" : "Funzione per l'analisi dello stato GPIO", + "gpio-leds" : "LED GPIO", + "no-gpio-leds" : "Nessun LED GPIO configurato", + "add-gpio-led" : "Aggiungi LED GPIO" + }, + "html-card" : { + "html" : "HTML", + "css" : "CSS" + }, + "input-widgets" : { + "attribute-not-allowed" : "Il parametro attributo non può essere usato in questo widget", + "blocked-location" : "La geolocalizzazione è bloccata nel tuo browser", + "claim-device" : "Rivendica dispositivo", + "claim-failed" : "Rivendicazione del dispositivo non riuscita!", + "claim-not-found" : "Dispositivo non trovato!", + "claim-successful" : "Dispositivo rivendicato con successo!", + "date" : "Data", + "device-name" : "Nome del dispositivo", + "device-name-required" : "Il nome del dispositivo è obbligatorio", + "discard-changes" : "Annulla modifiche", + "entity-attribute-required" : "Attributo dell'entità obbligatorio", + "entity-coordinate-required" : "Entrambi i campi, latitudine e longitudine, sono obbligatori", + "entity-timeseries-required" : "Serie temporale dell'entità obbligatoria", + "get-location" : "Ottieni posizione attuale", + "invalid-date" : "Data non valida", + "latitude" : "Latitudine", + "longitude" : "Longitudine", + "min-value-error" : "Valore minimo è {{value}}", + "max-value-error" : "Valore massimo è {{value}}", + "not-allowed-entity" : "L'entità selezionata non può avere attributi condivisi", + "no-attribute-selected" : "Nessun attributo selezionato", + "no-datakey-selected" : "Nessuna chiave dati selezionata", + "no-coordinate-specified" : "Chiave dati per latitudine/longitudine non specificata", + "no-entity-selected" : "Nessuna entità selezionata", + "no-image" : "Nessuna immagine", + "no-support-geolocation" : "Il tuo browser non supporta la geolocalizzazione", + "no-support-web-camera" : "Il tuo browser non supporta le telecamere", + "enable-https-use-widget" : "Abilita HTTPS per usare questo widget", + "no-found-your-camera" : "Telecamera non trovata", + "no-permission-camera" : "Permesso negato dall'utente / Questo sito non ha il permesso per usare la telecamera", + "no-timeseries-selected" : "Nessuna serie temporale selezionata", + "secret-key" : "Chiave segreta", + "secret-key-required" : "Chiave segreta obbligatoria", + "switch-attribute-value" : "Commuta valore attributo entità", + "switch-camera" : "Cambia fotocamera", + "switch-timeseries-value" : "Commuta valore serie temporale entità", + "take-photo" : "Scatta foto", + "time" : "Ora", + "timeseries-not-allowed" : "Il parametro serie temporale non può essere usato in questo widget", + "update-failed" : "Aggiornamento fallito", + "update-successful" : "Aggiornamento riuscito", + "update-attribute" : "Aggiorna attributo", + "update-timeseries" : "Aggiorna serie temporale", + "value" : "Valore", + "general-settings" : "Impostazioni generali", + "widget-title" : "Titolo del widget", + "claim-button-label" : "Etichetta del pulsante di rivendicazione", + "show-secret-key-field" : "Mostra campo di inserimento 'Chiave segreta'", + "labels-settings" : "Impostazioni etichette", + "show-labels" : "Mostra etichette", + "device-name-label" : "Etichetta per il campo nome del dispositivo", + "secret-key-label" : "Etichetta per il campo chiave segreta", + "messages-settings" : "Impostazioni messaggi", + "claim-device-success-message" : "Messaggio di successo per la rivendicazione del dispositivo", + "claim-device-not-found-message" : "Messaggio quando il dispositivo non viene trovato", + "claim-device-failed-message" : "Messaggio di errore nella rivendicazione del dispositivo", + "claim-device-name-required-message" : "Messaggio di errore 'Nome del dispositivo obbligatorio'", + "claim-device-secret-key-required-message" : "Messaggio di errore 'Chiave segreta obbligatoria'", + "show-label" : "Mostra etichetta", + "label" : "Etichetta", + "required" : "Obbligatorio", + "required-error-message" : "Messaggio di errore 'Obbligatorio'", + "show-result-message" : "Mostra messaggio di risultato", + "integer-field-settings" : "Impostazioni campo intero", + "min-value" : "Valore minimo", + "max-value" : "Valore massimo", + "double-field-settings" : "Impostazioni campo decimale", + "text-field-settings" : "Impostazioni campo di testo", + "min-length" : "Lunghezza minima", + "max-length" : "Lunghezza massima", + "checkbox-settings" : "Impostazioni casella di controllo", + "true-label" : "Etichetta selezionata", + "false-label" : "Etichetta deselezionata", + "image-input-settings" : "Impostazioni input immagine", + "display-preview" : "Mostra anteprima", + "display-clear-button" : "Mostra pulsante 'Cancella'", + "display-apply-button" : "Mostra pulsante 'Applica'", + "display-discard-button" : "Mostra pulsante 'Annulla'", + "datetime-field-settings" : "Impostazioni campo data/ora", + "display-time-input" : "Mostra input dell'ora", + "latitude-key-name" : "Nome chiave latitudine", + "longitude-key-name" : "Nome chiave longitudine", + "show-get-location-button" : "Mostra pulsante 'Ottieni posizione attuale'", + "use-high-accuracy" : "Usa alta precisione", + "location-fields-settings" : "Impostazioni campi posizione", + "latitude-label" : "Etichetta per latitudine", + "longitude-label" : "Etichetta per longitudine", + "input-fields-alignment" : "Allineamento campi di input", + "input-fields-alignment-column" : "Colonna (predefinito)", + "input-fields-alignment-row" : "Riga", + "layout" : "Layout", + "row-gap" : "Spazio tra le righe in pixel", + "column-gap" : "Spazio tra le colonne in pixel", + "latitude-field-required" : "Campo latitudine obbligatorio", + "longitude-field-required" : "Campo longitudine obbligatorio", + "attribute-settings" : "Impostazioni attributo", + "widget-mode" : "Modalità widget", + "widget-mode-update-attribute" : "Aggiorna attributo", + "widget-mode-update-timeseries" : "Aggiorna serie temporale", + "attribute-scope" : "Ambito attributo", + "attribute-scope-server" : "Attributo del server", + "attribute-scope-shared" : "Attributo condiviso", + "value-required" : "Valore obbligatorio", + "image-settings" : "Impostazioni immagine", + "image-format" : "Formato immagine", + "image-format-jpeg" : "JPEG", + "image-format-png" : "PNG", + "image-format-webp" : "WEBP", + "image-quality" : "Qualità immagine per formati compressi come jpeg e webp", + "max-image-width" : "Larghezza massima immagine", + "max-image-height" : "Altezza massima immagine", + "action-buttons" : "Pulsanti azione", + "show-action-buttons" : "Mostra pulsanti azione", + "update-all-values" : "Aggiorna tutti i valori, non solo quelli modificati", + "save-button-label" : "Etichetta pulsante 'SALVA'", + "reset-button-label" : "Etichetta pulsante 'ANNULLA'", + "group-settings" : "Impostazioni gruppo", + "show-group-title" : "Mostra titolo per gruppo di campi relativi a entità diverse", + "group-title" : "Titolo del gruppo", + "fields-alignment" : "Allineamento campi", + "fields-alignment-row" : "Riga (predefinito)", + "fields-alignment-column" : "Colonna", + "fields-in-row" : "Numero di campi nella riga", + "option-value" : "Valore (scrivi 'null' per creare un'opzione vuota)", + "option-label" : "Etichetta", + "hide-input-field" : "Nascondi campo di input", + "datakey-type" : "Tipo di chiave dati", + "datakey-type-server" : "Attributo del server (predefinito)", + "datakey-type-shared" : "Attributo condiviso", + "datakey-type-timeseries" : "Serie temporale", + "datakey-value-type" : "Tipo di valore della chiave dati", + "datakey-value-type-string" : "Stringa", + "datakey-value-type-double" : "Double", + "datakey-value-type-integer" : "Intero", + "datakey-value-type-json" : "JSON", + "datakey-value-type-boolean-checkbox" : "Booleano (Casella di controllo)", + "datakey-value-type-boolean-switch" : "Booleano (Interruttore)", + "datakey-value-type-date-time" : "Data e ora", + "datakey-value-type-date" : "Data", + "datakey-value-type-time" : "Ora", + "datakey-value-type-select" : "Selezione", + "datakey-value-type-radio" : "Radio", + "datakey-value-type-color" : "Colore", + "value-is-required" : "Valore obbligatorio", + "ability-to-edit-attribute" : "Possibilità di modificare l'attributo", + "ability-to-edit-attribute-editable" : "Modificabile (predefinito)", + "ability-to-edit-attribute-disabled" : "Disabilitato", + "ability-to-edit-attribute-readonly" : "Sola lettura", + "disable-on-datakey-name" : "Disabilita in base al valore falso di un'altra chiave dati (specificare il nome della chiave)", + "field-appearance" : "Aspetto del campo", + "appearance-fill" : "Riempimento", + "appearance-outline" : "Contorno", + "subscript-sizing" : "Dimensionamento del pedice", + "subscript-sizing-fixed" : "Fisso", + "subscript-sizing-dynamic" : "Dinamico", + "slide-toggle-settings" : "Impostazioni interruttore scorrevole", + "slide-toggle-label-position" : "Posizione etichetta interruttore", + "slide-toggle-label-position-after" : "Dopo", + "slide-toggle-label-position-before" : "Prima", + "select-options" : "Opzioni di selezione", + "no-select-options" : "Nessuna opzione di selezione configurata", + "add-select-option" : "Aggiungi opzione di selezione", + "numeric-field-settings" : "Impostazioni campo numerico", + "step-interval" : "Intervallo di passo tra i valori", + "error-messages" : "Messaggi di errore", + "min-value-error-message" : "Messaggio di errore 'Valore minimo'", + "max-value-error-message" : "Messaggio di errore 'Valore massimo'", + "invalid-date-error-message" : "Messaggio di errore 'Data non valida'", + "invalid-JSON-error-message" : "Messaggio di errore 'JSON non valido'", + "icon-settings" : "Impostazioni icona", + "dialog-editor-settings" : "Impostazioni editor dialogo", + "use-custom-icon" : "Usa icona personalizzata", + "input-cell-icon" : "Icona da mostrare prima della cella di input", + "value-conversion-settings" : "Impostazioni di conversione del valore", + "get-value-settings" : "Impostazioni per ottenere il valore", + "use-get-value-function" : "Usa la funzione getValue", + "get-value-function" : "Funzione getValue", + "set-value-settings" : "Impostazioni per impostare il valore", + "use-set-value-function" : "Usa la funzione setValue", + "set-value-function" : "Funzione setValue", + "json-invalid" : "Il valore JSON ha un formato non valido", + "title" : "Titolo", + "cancel-button-label" : "Etichetta del pulsante 'Annulla'", + "radio-button-settings" : "Impostazioni pulsante radio", + "color" : "Colore", + "columns" : "Colonne", + "radio-options" : "Opzioni radio", + "no-radio-options" : "Nessuna opzione radio configurata", + "add-radio-option" : "Aggiungi opzione radio", + "radio-label-position" : "Posizione dell'etichetta", + "radio-label-position-before" : "Prima", + "radio-label-position-after" : "Dopo" + }, + "invalid-qr-code-text" : "Testo non valido per codice QR. L'input deve essere di tipo stringa", + "qr-code" : { + "use-qr-code-text-function" : "Usa la funzione testo del codice QR", + "qr-code-text-pattern" : "Modello di testo del codice QR (ad es. '${entityName} | ${keyName} - some text.')", + "qr-code-text-pattern-hint" : "Il modello di testo del codice QR usa il valore della prima chiave trovata nelle entità dell'alias dell'entità.", + "qr-code-text-pattern-required" : "Il modello di testo del codice QR è obbligatorio.", + "qr-code-text-function" : "Funzione testo del codice QR" + }, + "label-widget" : { + "label-pattern" : "Modello", + "label-pattern-hint" : "Suggerimento: ad esempio, 'Testo ${keyName} unità.' oppure ${#<indice chiave>} unità'", + "label-pattern-required" : "Il pattern è obbligatorio", + "label-position" : "Posizione (percentuale rispetto allo sfondo)", + "x-pos" : "X", + "y-pos" : "Y", + "background-color" : "Colore di sfondo", + "font-settings" : "Impostazioni font", + "background-image" : "Immagine di sfondo", + "labels" : "Etichette", + "no-labels" : "Nessuna etichetta configurata", + "add-label" : "Aggiungi etichetta" + }, + "navigation" : { + "title" : "Titolo", + "navigation-path" : "Percorso di navigazione", + "filter-type" : "Tipo di filtro", + "filter-type-all" : "Tutti gli elementi", + "filter-type-include" : "Includi elementi", + "filter-type-exclude" : "Escludi elementi", + "items" : "Elementi", + "enter-urls-to-filter" : "Inserisci URL da filtrare..." + }, + "persistent-table" : { + "rpc-id" : "ID RPC", + "message-type" : "Tipo di messaggio", + "method" : "Metodo", + "params" : "Parametri", + "created-time" : "Data creazione", + "expiration-time" : "Data scadenza", + "retries" : "Tentativi", + "status" : "Stato", + "filter" : "Filtro", + "refresh" : "Aggiorna", + "add" : "Aggiungi richiesta RPC", + "details" : "Dettagli", + "delete" : "Elimina", + "delete-request-title" : "Elimina richiesta RPC persistente", + "delete-request-text" : "Sei sicuro di voler eliminare la richiesta?", + "details-title" : "Dettagli ID RPC: ", + "additional-info" : "Informazioni aggiuntive", + "response" : "Risposta", + "any-status" : "Qualsiasi stato", + "rpc-status-list" : "Elenco stati RPC", + "no-request-prompt" : "Nessuna richiesta da visualizzare", + "send-request" : "Invia richiesta", + "add-title" : "Crea richiesta RPC persistente", + "method-error" : "Il metodo è obbligatorio.", + "timeout-error" : "Il valore minimo del timeout è 5000 (5 secondi).", + "white-space-error" : "Gli spazi bianchi non sono consentiti.", + "rpc-status" : { + "QUEUED" : "IN CODA", + "SENT" : "INVIATO", + "DELIVERED" : "CONSEGNATO", + "SUCCESSFUL" : "RIUSCITO", + "TIMEOUT" : "TIMEOUT", + "EXPIRED" : "SCADUTO", + "FAILED" : "FALLITO" + }, + "rpc-search-status-all" : "TUTTI", + "message-types" : { + "false" : "Bidirezionale", + "true" : "Monodirezionale" + }, + "general-settings" : "Impostazioni generali", + "enable-filter" : "Abilita filtro", + "enable-sticky-header" : "Visualizza intestazione durante lo scorrimento", + "enable-sticky-action" : "Visualizza colonna azioni durante lo scorrimento", + "display-request-details" : "Visualizza dettagli richiesta", + "allow-send-request" : "Permetti invio richiesta RPC", + "allow-delete-request" : "Permetti eliminazione richiesta", + "columns-settings" : "Impostazioni colonne", + "display-columns" : "Colonne da visualizzare", + "column" : "Colonna", + "no-columns-found" : "Nessuna colonna trovata", + "no-columns-matching" : "'{{column}}' non trovata." + }, + "range-chart" : { + "chart" : "Grafico", + "data-zoom" : "Zoom dati", + "range-chart-appearance" : "Aspetto grafico intervallo", + "range-colors" : "Colori intervallo", + "out-of-range-color" : "Colore fuori intervallo", + "show-range-thresholds" : "Mostra soglie di intervallo", + "range-thresholds-settings" : "Impostazioni soglie intervallo", + "fill-area" : "Area riempita", + "fill-area-opacity" : "Opacità area riempita", + "range-chart-style" : "Stile grafico intervallo" + }, + "rpc" : { + "value-settings" : "Impostazioni valore", + "initial-value" : "Valore iniziale", + "retrieve-value-settings" : "Impostazioni per recupero valore on/off", + "retrieve-value-method" : "Recupera valore usando metodo", + "retrieve-value-method-none" : "Non recuperare", + "retrieve-value-method-rpc" : "Chiama metodo RPC per ottenere valore", + "retrieve-value-method-attribute" : "Sottoscrivi a un attributo", + "retrieve-value-method-timeseries" : "Sottoscrivi a una serie temporale", + "attribute-value-key" : "Chiave attributo", + "timeseries-value-key" : "Chiave serie temporale", + "get-value-method" : "Metodo RPC per ottenere valore", + "parse-value-function" : "Funzione per analizzare valore", + "update-value-settings" : "Impostazioni aggiornamento valore", + "set-value-method" : "Metodo RPC per impostare valore", + "convert-value-function" : "Funzione per convertire valore", + "rpc-settings" : "Impostazioni RPC", + "request-timeout" : "Timeout richiesta RPC (ms)", + "persistent-rpc-settings" : "Impostazioni RPC persistente", + "request-persistent" : "Richiesta RPC persistente", + "persistent-polling-interval" : "Intervallo polling (ms) per risposta comando RPC persistente", + "common-settings" : "Impostazioni comuni", + "switch-title" : "Titolo switch", + "show-on-off-labels" : "Mostra etichette on/off", + "slide-toggle-label" : "Etichetta slide toggle", + "label-position" : "Posizione etichetta", + "label-position-before" : "Prima", + "label-position-after" : "Dopo", + "slider-color" : "Colore slider", + "slider-color-primary" : "Primario", + "slider-color-accent" : "Accentato", + "slider-color-warn" : "Avviso", + "button-style" : "Stile pulsante", + "button-raised" : "Pulsante in rilievo", + "button-primary" : "Colore primario", + "button-background-color" : "Colore sfondo pulsante", + "button-text-color" : "Colore testo pulsante", + "widget-title" : "Titolo widget", + "button-label" : "Etichetta pulsante", + "device-attribute-scope" : "Ambito attributo dispositivo", + "server-attribute" : "Attributo server", + "shared-attribute" : "Attributo condiviso", + "device-attribute-parameters" : "Parametri attributo dispositivo", + "is-one-way-command" : "È comando monodirezionale", + "rpc-method" : "Metodo RPC", + "rpc-method-params" : "Parametri metodo RPC", + "show-rpc-error" : "Mostra errore esecuzione comando RPC", + "led-title" : "Titolo LED", + "led-color" : "Colore LED", + "check-status-settings" : "Impostazioni controllo stato", + "perform-rpc-status-check" : "Esegui controllo stato dispositivo tramite RPC", + "retrieve-led-status-value-method" : "Recupera valore stato LED usando metodo", + "led-status-value-attribute" : "Attributo dispositivo con valore stato LED", + "led-status-value-timeseries" : "Serie temporale dispositivo con valore stato LED", + "check-status-method" : "Metodo RPC per controllo stato dispositivo", + "parse-led-status-value-function" : "Funzione per analisi valore stato LED", + "knob-title" : "Titolo manopola", + "min-value" : "Valore minimo", + "max-value" : "Valore massimo" + }, + "maps" : { + "map-type" : { + "type" : "Tipo mappa", + "map" : "Mappa", + "image" : "Immagine" + }, + "image" : { + "image-source" : "Fonte immagine", + "image-source-image" : "Immagine", + "image-source-entity-key" : "Chiave entità", + "source-entity-alias" : "Alias entità sorgente", + "image-url-key" : "Chiave URL immagine", + "image-url-key-required" : "Chiave URL immagine obbligatoria" + }, + "control" : { + "map-controls" : "Controlli mappa", + "position" : "Posizione", + "position-topleft" : "In alto a sinistra", + "position-topright" : "In alto a destra", + "position-bottomleft" : "In basso a sinistra", + "position-bottomright" : "In basso a destra", + "zoom-actions" : "Azioni zoom", + "zoom-scroll" : "Scroll", + "zoom-double-click" : "Doppio clic", + "zoom-control-buttons" : "Pulsanti di controllo", + "scale" : "Scala", + "scale-metric" : "Metrico", + "scale-imperial" : "Imperiale", + "switch-to-drag-mode-using-button" : "Passa alla modalità trascinamento usando il pulsante" + }, + "timeline" : { + "control-panel" : "Pannello di controllo timeline", + "time-step" : "Passo temporale", + "speed-options" : "Opzioni velocità", + "timestamp" : "Timestamp", + "snap-to-real-location" : "Allinea alla posizione reale", + "location-snap-filter-function" : "Funzione filtro allineamento posizione", + "no-trips-data-available" : "Nessun dato di percorso disponibile" + }, + "map-action" : { + "map-action-buttons" : "Pulsanti azione mappa", + "label" : "Etichetta", + "icon" : "Icona", + "color" : "Colore", + "action" : "Azione", + "add-button" : "Aggiungi pulsante", + "no-action-buttons-configured" : "Nessun pulsante azione configurato", + "remove-action-button" : "Rimuovi pulsante azione", + "map-action-button" : "Pulsante azione mappa", + "button-requires" : "Il pulsante richiede un'etichetta o un'icona" + }, + "common" : { + "common-map-settings" : "Impostazioni comuni mappa", + "fit-map-bounds" : "Adatta i limiti della mappa per coprire tutti i marker", + "default-map-center-position" : "Posizione centrale predefinita della mappa", + "default-map-zoom-level" : "Livello di zoom predefinito della mappa", + "entities-limit" : "Limite di entità da caricare" + }, + "layer" : { + "label" : "Etichetta", + "layer" : "Livello", + "layers" : "Livelli", + "map-layers" : "Livelli della mappa", + "add-layer" : "Aggiungi livello", + "layer-settings" : "Impostazioni livello", + "remove-layer" : "Rimuovi livello", + "no-layers" : "Nessun livello configurato", + "roadmap" : "Stradale", + "satellite" : "Satellitare", + "hybrid" : "Ibrido", + "reference" : { + "reference-layer" : "Livello di riferimento", + "no-layer" : "Nessun livello", + "openstreetmap-hybrid" : "OpenStreetMap Ibrido", + "world-edition-hybrid" : "Edizione Mondiale Ibrido", + "enhanced-contrast-hybrid" : "Ibrido a Contrasto Aumentato" }, - "display-status": { - "ACTIVE_UNACK": "Attivo Non riconosciuto", - "ACTIVE_ACK": "Attivo Riconosciuto", - "CLEARED_UNACK": "Cancellato Non riconosciuto", - "CLEARED_ACK": "Cancellato Riconosciuto" + "provider" : { + "provider" : "Fornitore", + "openstreet" : { + "title" : "OpenStreet", + "mapnik" : "Mapnik", + "hot" : "HOT", + "esri-street" : "WorldStreetMap", + "esri-topo" : "WorldTopoMap", + "esri-imagery" : "WorldImagery", + "cartodb-positron" : "Positron", + "cartodb-dark-matter" : "DarkMatter" + }, + "google" : { + "title" : "Google", + "roadmap" : "Stradale", + "satellite" : "Satellitare", + "hybrid" : "Ibrido", + "terrain" : "Terreno" + }, + "here" : { + "title" : "HERE", + "normal-day" : "Giorno normale", + "normal-night" : "Notte normale", + "hybrid-day" : "Giorno ibrido", + "terrain-day" : "Terreno diurno" + }, + "tencent" : { + "title" : "Tencent", + "normal" : "Normale", + "satellite" : "Satellitare", + "terrain" : "Terreno" + }, + "custom" : { + "title" : "Personalizzato", + "tile-url" : "URL tile" + } }, - "no-alarms-prompt": "Nessun allarme trovato", - "created-time": "Orario di creazione", - "type": "Tipo", - "severity": "Livello di gravità", - "originator": "Origine", - "originator-type": "Tipo origine", - "details": "Dettagli", - "status": "Stato", - "alarm-details": "Dettagli allarme", - "start-time": "Ora inizio", - "end-time": "Ora fine", - "ack-time": "Ora conferma", - "clear-time": "Ora cancellazione", - "severity-critical": "Critico", - "severity-major": "Maggiore", - "severity-minor": "Minore", - "severity-warning": "Avviso", - "severity-indeterminate": "Indeterminato", - "acknowledge": "Conferma", - "clear": "Cancella", - "search": "Cerca allarmi", - "selected-alarms": "{ count, plural, =1 {1 allarme selezionato} other {# allarmi selezionati} }", - "no-data": "Nessun dato da visualizzare", - "polling-interval": "Intervallo di polling (sec) Allarmi", - "polling-interval-required": "Intervallo di polling Allarmi richiesto.", - "min-polling-interval-message": "L'intervallo di polling deve essere di almeno 1 sec.", - "aknowledge-alarms-title": "Conferma { count, plural, =1 {1 allarme} other {# allarmi} }", - "aknowledge-alarms-text": "Sei sicuro di voler confermare { count, plural, =1 {1 allarme} other {# allarmi} }?", - "aknowledge-alarm-title": "Conferma allarme", - "aknowledge-alarm-text": "Sei sicuro di voler confermare l'allarme?", - "clear-alarms-title": "Elimina { count, plural, =1 {1 allarme} other {# allarmi} }", - "clear-alarms-text": "Sei sicuro di voler eliminare { count, plural, =1 {1 allarme} other {# allarmi} }?", - "clear-alarm-title": "Elimina allarme", - "clear-alarm-text": "Sei sicuro di voler eliminare l'allarme?", - "alarm-status-filter": "Filtro stato allarme" - }, - "alias": { - "add": "Aggiungi alias", - "edit": "Modifica alias", - "name": "Nome Alias", - "name-required": "Nome Alias obbligatorio", - "duplicate-alias": "Un Alias con lo stesso nome è già presente.", - "filter-type-single-entity": "Singola entità", - "filter-type-entity-list": "Lista Entità", - "filter-type-entity-name": "Nome Entità", - "filter-type-state-entity": "Entità dallo stato della dashboard", - "filter-type-state-entity-description": "Entità prelevata dai parametri di stato della dashboard", - "filter-type-asset-type": "Tipo di Asset", - "filter-type-asset-type-description": "Asset di tipo '{{assetType}}'", - "filter-type-asset-type-and-name-description": "Asset di tipo '{{assetTypes}}' e con un nome che inizia per '{{prefix}}'", - "filter-type-device-type": "Tipo di dispositivo", - "filter-type-device-type-description": "Dispositivi di tipo '{{deviceTypes}}'", - "filter-type-device-type-and-name-description": "Dispositivi di tipo '{{deviceTypes}}' e con un nome che inizia per '{{prefix}}'", - "filter-type-entity-view-type": "Tipo vista entità", - "filter-type-entity-view-type-description": "Viste entità di tipo '{{entityViewTypes}}'", - "filter-type-entity-view-type-and-name-description": "Viste entità di tipo '{{entityViewTypes}}' e con un nome che inizia per '{{prefix}}'", - "filter-type-relations-query": "Query relazioni", - "filter-type-relations-query-description": "{{entities}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-asset-search-query": "Query ricerca asset", - "filter-type-asset-search-query-description": "Asset di tipo {{assetTypes}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-device-search-query": "Query ricerca dispositivo", - "filter-type-device-search-query-description": "Dispositivi di tipo {{deviceTypes}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-entity-view-search-query": "Query ricerca Vista entità", - "filter-type-entity-view-search-query-description": "Viste entità di tipo {{entityViewTypes}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}", - "entity-filter": "Filtro entità", - "resolve-multiple": "Risolvi come entità multiple", - "filter-type": "Tipo di filtro", - "filter-type-required": "Tipo di filtro richiesto.", - "entity-filter-no-entity-matched": "Nessuna entità corrispondente al filtro specificato è stata trovata.", - "no-entity-filter-specified": "Nessun filtro di entità specificato", - "root-state-entity": "Usa l'entità di stato della dashboard come radice", - "root-entity": "Entità radice", - "state-entity-parameter-name": "Nome parametro entità di stato", - "default-state-entity": "Entità di stato predefinita", - "default-entity-parameter-name": "Predefinito", - "max-relation-level": "Massimo livello relazione", - "unlimited-level": "Illimitato", - "state-entity": "Entità di stato della dashboard", - "all-entities": "Tutte le entità", - "any-relation": "qualsiasi" - }, - "asset": { - "asset": "Asset", - "assets": "Asset", - "management": "Gestione Asset", - "view-assets": "Visualizza Asset", - "add": "Aggiungi Asset", - "assign-to-customer": "Assegna a cliente", - "assign-asset-to-customer": "Assegna Asset al Cliente", - "assign-asset-to-customer-text": "Seleziona gli asset da assegnare al cliente", - "no-assets-text": "Nessun asset trovato", - "assign-to-customer-text": "Seleziona il cliente a cui assegnare l'asset / gli asset", - "public": "Pubblico", - "assignedToCustomer": "Assegnato al cliente", - "make-public": "Rendi pubblico l'asset", - "make-private": "Rendi privato l'asset", - "unassign-from-customer": "Assegnazione annullata dal cliente", - "delete": "Cancella asset", - "asset-public": "L'Asset è pubblico", - "asset-type": "Tipo di Asset", - "asset-type-required": "Tipo di Asset richiesto.", - "select-asset-type": "Seleziona tipo di asset", - "enter-asset-type": "Inserisci tipo di asset", - "any-asset": "Qualsiasi asset", - "no-asset-types-matching": "Nessun asset corrispondente al tipo '{{entitySubtype}}' è stato trovato.", - "asset-type-list-empty": "Nessun tipo di asset selezionato.", - "asset-types": "Tipi di Asset", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "description": "Descrizione", - "type": "Tipo", - "type-required": "Tipo obbligatorio.", - "details": "Dettagli", - "events": "Eventi", - "add-asset-text": "Aggiungi un nuovo asset", - "asset-details": "Dettagli Asset", - "assign-assets": "Assegna asset", - "assign-assets-text": "Assegna { count, plural, =1 {1 asset} other {# asset} } al cliente", - "delete-assets": "Cancella asset", - "unassign-assets": "Annulla assegnazione asset", - "unassign-assets-action-title": "Annulla assegnazione { count, plural, =1 {1 asset} other {# asset} } al cliente", - "assign-new-asset": "Assegna un nuovo asset", - "delete-asset-title": "Sei sicuro di voler cancellare l'asset '{{assetName}}'?", - "delete-asset-text": "Attenzione, dopo la conferma l'asset e tutti i relativi dati non saranno più recuperabili.", - "delete-assets-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 asset} other {# asset} }?", - "delete-assets-action-title": "Elimina { count, plural, =1 {1 asset} other {# asset} }", - "delete-assets-text": "Attenzione, dopo la modifica tutti gli asset selezionati saranno rimossi e tutti i relativi dati non saranno più recuperabili.", - "make-public-asset-title": "Sei sicuro di voler rendere pubblico l'asset '{{assetName}}'?", - "make-public-asset-text": "Dopo la conferma l'asset e tutti i suoi dati saranno resi pubblici e accessibili dagli altri.", - "make-private-asset-title": "Sei sicuro di voler rendere privato l'asset '{{assetName}}'?", - "make-private-asset-text": "Dopo la conferma l'asset e tutti i suoi dati saranno resi privati e non accessibili dagli altri.", - "unassign-asset-title": "Sei sicuro di voler annullare l'assegnazione dell'asset '{{assetName}}'?", - "unassign-asset-text": "Dopo la conferma l'assegnazione dell'asset sarà annullata e l'asset non sarà più accessibile dal cliente.", - "unassign-asset": "Annulla assegnazione asset", - "unassign-assets-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, =1 {1 asset} other {# asset} }?", - "unassign-assets-text": "Dopo la conferma sarà annullata l'assegnazione di tutti gli asset selezionati e questi non saranno più accessibili dal cliente.", - "copyId": "Copia Id asset", - "idCopiedMessage": "Id Asset copiato negli Appunti", - "select-asset": "Seleziona asset", - "no-assets-matching": "Nessun asset corrispondente a '{{entity}}' è stato trovato.", - "asset-required": "Asset obbligatorio", - "name-starts-with": "Asset con nome che inizia per", - "label": "Etichetta" - }, - "attribute": { - "attributes": "Attributi", - "latest-telemetry": "Ultima telemetria", - "attributes-scope": "Visibilità attributi entità", - "scope-telemetry": "Telemetria", - "scope-latest-telemetry": "Ultima telemetria", - "scope-client": "Attributi client", - "scope-server": "Attributi server", - "scope-shared": "Attributi condivisi", - "add": "Aggiungi attributo", - "key": "Chiave", - "last-update-time": "Ultimo aggiornamento", - "key-required": "Attributo chiave richiesto.", - "value": "Valore", - "value-required": "Attributo valore richiesto.", - "delete-attributes-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 attributo} other {# attributi} }?", - "delete-attributes-text": "Attenzione, dopo la conferma tutti gli attributi selezionati saranno rimossi.", - "delete-attributes": "Elimina attributi", - "enter-attribute-value": "Inserisci il valore dell'attributo", - "show-on-widget": "Mostra sul widget", - "widget-mode": "Modalità Widget", - "next-widget": "Widget successivo", - "prev-widget": "Widget precedente", - "add-to-dashboard": "Aggiungi alla dashboard", - "add-widget-to-dashboard": "Aggiungi widget alla dashboard", - "selected-attributes": "{ count, plural, =1 {1 attributo selezionato} other {# attributi selezionati} }", - "selected-telemetry": "{ count, plural, =1 {1 unità di telemetria selezionata} other {# unità di telemetria selezionate} }" - }, - "audit-log": { - "audit": "Audit", - "audit-logs": "Log Audit", - "timestamp": "Timestamp", - "entity-type": "Tipo Entità", - "entity-name": "Nome Entità", - "user": "Utente", - "type": "Tipo", - "status": "Stato", - "details": "Dettagli", - "type-added": "Aggiunto", - "type-deleted": "Eliminato", - "type-updated": "Aggiornato", - "type-attributes-updated": "Attributi aggiornati", - "type-attributes-deleted": "Attributi eliminati", - "type-rpc-call": "Chiamata RPC", - "type-credentials-updated": "Credenziali aggiornate", - "type-assigned-to-customer": "Assegnato al Cliente", - "type-unassigned-from-customer": "Assegnazione annullata dal Cliente", - "type-activated": "Attivato", - "type-suspended": "Sospeso", - "type-credentials-read": "Credenziali lette", - "type-attributes-read": "Attributi letti", - "type-relation-add-or-update": "Relazione aggiornata", - "type-relation-delete": "Relazione eliminata", - "type-relations-delete": "Eliminate tutte le relazioni", - "type-alarm-ack": "Confermato", - "type-alarm-clear": "Eliminato", - "type-login": "Accesso", - "type-logout": "Disconnettersi", - "type-lockout": "Bloccato", - "status-success": "Successo", - "status-failure": "Fallito", - "audit-log-details": "Dettaglio log audit", - "no-audit-logs-prompt": "Log non trovati", - "action-data": "Action data", - "failure-details": "Dettagli fallimento", - "search": "Cerca log audit", - "clear-search": "Cancella ricerca" - }, - "confirm-on-exit": { - "message": "Alcune modifiche non sono state salvate. Sei sicuro di voler abbandonare questa pagina?", - "html-message": "Alcune modifiche non sono state salvate.
Sei sicuro di voler abbandonare questa pagina?", - "title": "Modifiche non salvate" - }, - "contact": { - "country": "Nazione", - "city": "Città", - "state": "Stato / Provincia", - "postal-code": "CAP", - "postal-code-invalid": "Formato CAP non valido.", - "address": "Indirizzo", - "address2": "Indirizzo 2", - "phone": "Telefono", - "email": "Email", - "no-address": "Nessun indirizzo" - }, - "common": { - "username": "Nome utente", - "password": "Password", - "enter-username": "Inserisci nome utente", - "enter-password": "Inserisci password", - "enter-search": "Cerca ...", - "created-time": "Ora di creazione" - }, - "content-type": { - "json": "Json", - "text": "Testo", - "binary": "Binario (Base64)" - }, - "customer": { - "customer": "Cliente", - "customers": "Clienti", - "management": "Gestione cliente", - "dashboard": "Dashboard cliente", - "dashboards": "Dashboard cliente", - "devices": "Dispositivi cliente", - "entity-views": "Viste entità cliente", - "assets": "Asset cliente", - "public-dashboards": "Dashboard pubbliche", - "public-devices": "Dispositivi pubblici", - "public-assets": "Asset pubblici", - "public-entity-views": "Viste entità pubbliche", - "add": "Aggiungi cliente", - "delete": "Elimina cliente", - "manage-customer-users": "Gestisci utenti cliente", - "manage-customer-devices": "Gestisci dispositivi cliente", - "manage-customer-dashboards": "Gestisci dashboard cliente", - "manage-public-devices": "Gestisci dispositivi pubblici", - "manage-public-dashboards": "Gestisci dashboard pubbliche", - "manage-customer-assets": "Gestisci asset cliente", - "manage-public-assets": "Gestisci asset pubblici", - "add-customer-text": "Aggiungi nuovo cliente", - "no-customers-text": "Nessun cliente trovato", - "customer-details": "Dettagli cliente", - "delete-customer-title": "Sei sicuro di voler eliminare il cliente '{{customerTitle}}'?", - "delete-customer-text": "Attenzione, dopo la conferma il cliente e tutti i suoi dati non saranno più recuperabili.", - "delete-customers-title": "Sei sicuro di voler cancellare { count, plural, =1 {1 cliente} other {# clienti} }?", - "delete-customers-action-title": "Elimina { count, plural, =1 {1 cliente} other {# clienti} }", - "delete-customers-text": "Attenzione, dopo la conferma tutti i clienti selezionati saranno rimossi e i loro dati non saranno più recuperabili.", - "manage-users": "Gestisci utenti", - "manage-assets": "Gestisci asset", - "manage-devices": "Gestisci dispositivi", - "manage-dashboards": "Gestisci dashboard", - "title": "Titolo", - "title-required": "Titolo obbligatorio.", - "description": "Descrizione", - "details": "Dettagli", - "events": "Eventi", - "copyId": "Copia Id cliente", - "idCopiedMessage": "Id cliente copiato negli appunti", - "select-customer": "Seleziona cliente", - "no-customers-matching": "Nessun cliente corrispondente a '{{entity}}' è stato trovato.", - "customer-required": "Cliente obbligatorio", - "select-default-customer": "Seleziona cliente di default", - "default-customer": "Cliente di default", - "default-customer-required": "Il cliente di default è obbligatorio per il debug della dashboard a livello di Tenant" - }, - "datetime": { - "date-from": "Data da", - "time-from": "Ora da", - "date-to": "Data a", - "time-to": "Ora a" - }, - "dashboard": { - "dashboard": "Dashboard", - "dashboards": "Dashboard", - "management": "Gestione Dashboard", - "view-dashboards": "Mostra Dashboard", - "add": "Aggiungi Dashboard", - "assign-dashboard-to-customer": "Assegna Dashboard al cliente", - "assign-dashboard-to-customer-text": "Seleziona le dashboard da assegnare al client", - "assign-to-customer-text": "Seleziona il cliente a cui assegnare la/le dashboard", - "assign-to-customer": "Assegna al cliente", - "unassign-from-customer": "Annulla assegnazione al cliente", - "make-public": "Rendi pubblica la dashboard", - "make-private": "Rendi privata la dashboard", - "manage-assigned-customers": "Gestisci clienti assegnati", - "assigned-customers": "Clienti assegnati", - "assign-to-customers": "Assegna Dashboard ai Clienti", - "assign-to-customers-text": "Seleziona i clienti da assegnare alla/alle dashboard", - "unassign-from-customers": "Annulla assegnazione Dashboard ai Clienti", - "unassign-from-customers-text": "Seleziona i clienti di cui annullare l'assegnazione alla/alle dashboard", - "no-dashboards-text": "Nessuna dashboard trovata", - "no-widgets": "Nessun widget configurato", - "add-widget": "Aggiungi nuovo widget", - "title": "Titolo", - "select-widget-title": "Seleziona widget", - "select-widget-subtitle": "Elenco tipi di widget disponibili", - "delete": "Elimina dashboard", - "title-required": "Titolo obbligatorio.", - "description": "Descrizione", - "details": "Dettagli", - "dashboard-details": "Dettagli dashboard", - "add-dashboard-text": "Aggiungi nuova dashboard", - "assign-dashboards": "Assegna dashboard", - "assign-new-dashboard": "Assegna nuova dashboard", - "assign-dashboards-text": "Assegna { count, plural, =1 {1 dashboard} other {# dashboard} } ai clienti", - "unassign-dashboards-action-text": "Annulla assegnazione { count, plural, =1 {1 dashboard} other {# dashboard} } ai clienti", - "delete-dashboards": "Elimina dashboard", - "unassign-dashboards": "Annulla assegnazione dashboard", - "unassign-dashboards-action-title": "Annulla assegnazione { count, plural, =1 {1 dashboard} other {# dashboard} } al cliente", - "delete-dashboard-title": "Sei sicuro di voler cancellare la dashboard '{{dashboardTitle}}'?", - "delete-dashboard-text": "Attenzione, dopo la conferma la dashboard e tutti i suoi dati non saranno più recuperabili.", - "delete-dashboards-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 dashboard} other {# dashboard} }?", - "delete-dashboards-action-title": "Cancella { count, plural, =1 {1 dashboard} other {# dashboard} }", - "delete-dashboards-text": "Attenzione, dopo la conferma tutte le dashboard selezionate saranno eliminate e tutti i loro dati non saranno più recuperabili.", - "unassign-dashboard-title": "Sei sicuro di voler annullare l'assegnazione della dashboard '{{dashboardTitle}}'?", - "unassign-dashboard-text": "Dopo la conferma sarà annullata l'assegnazione della dashboard e questa non sarà più accessibile dal cliente.", - "unassign-dashboard": "Annulla assegnazione dashboard", - "unassign-dashboards-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, =1 {1 dashboard} other {# dashboard} }?", - "unassign-dashboards-text": "Dopo la conferma sarà annullata l'assegnazione di tutte le dashboard selezionate e queste non saranno più accessibili dal cliente.", - "public-dashboard-title": "La Dashboard è ora pubblica", - "public-dashboard-text": "La dashboard {{dashboardTitle}} è ora pubblica e accessibile al link:", - "public-dashboard-notice": "Nota: Ricorda di rendere pubblici i relativi dispositivi per accedere ai loro dati.", - "make-private-dashboard-title": "Sei sicuro di voler rendere privata la dashboard '{{dashboardTitle}}'?", - "make-private-dashboard-text": "Dopo la conferma la dashboard sarà resa privata e non più accessibile dagli altri.", - "make-private-dashboard": "Rendi privata la dashboard", - "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", - "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", - "select-dashboard": "Seleziona dashboard", - "no-dashboards-matching": "Nessuna dashboard corrispondente a '{{entity}}' è stata trovata.", - "dashboard-required": "Dashboard obbligatoria.", - "select-existing": "Seleziona una dashboard esistente", - "create-new": "Crea nuova dashboard", - "new-dashboard-title": "Titolo nuova dashboard", - "open-dashboard": "Apri dashboard", - "set-background": "Imposta sfondo", - "background-color": "Colore sfondo", - "background-image": "Immagine sfondo", - "background-size-mode": "Modalità dimensione sfondo", - "no-image": "Nessuna immagine selezionata", - "drop-image": "Trascina un'immagine o fai clic per selezionare un file da caricare.", - "settings": "Impostazioni", - "columns-count": "Numero colonne", - "columns-count-required": "Numero colonne obbligatorio.", - "min-columns-count-message": "Ammesso un numero minimo di colonne pari a 10.", - "max-columns-count-message": "Ammesso un numero massimo di colonne pari a 1000.", - "widgets-margins": "Margine tra i widget", - "horizontal-margin": "Margine orizzontale", - "horizontal-margin-required": "Margine orizzontale obbligatorio.", - "min-horizontal-margin-message": "Ammesso un margine orizzontale minimo pari a 0.", - "max-horizontal-margin-message": "Ammesso un margine orizzontale massimo pari a 50.", - "vertical-margin": "Margine verticale", - "vertical-margin-required": "Margine verticale obbligatorio.", - "min-vertical-margin-message": "Ammesso un margine verticale minimo pari a 0.", - "max-vertical-margin-message": "Ammesso un margine verticale massimo pari a 50.", - "autofill-height": "Riempi automaticamente altezza layout", - "mobile-layout": "Impostazioni layout mobile", - "mobile-row-height": "Altezza riga mobile (px)", - "mobile-row-height-required": "Altezza riga mobile è richiesta.", - "min-mobile-row-height-message": "5 pixel è il minimo concesso al valore altezza riga mobile.", - "max-mobile-row-height-message": "200 pixel è il massimo concesso al valore altezza riga mobile.", - "display-title": "Mostra titolo dashboard", - "toolbar-always-open": "Mantieni aperta la barra degli strumenti", - "title-color": "Colore titolo", - "display-dashboards-selection": "Mostra selezione dashboard", - "display-entities-selection": "Mostra selezione entità", - "display-dashboard-timewindow": "Mostra intervallo temporale", - "display-dashboard-export": "Mostra esportazione", - "import": "Importa dashboard", - "export": "Esporta dashboard", - "export-failed-error": "Impossibile esportare la dashboard: {{error}}", - "create-new-dashboard": "Crea nuova dashboard", - "dashboard-file": "File dashboard", - "invalid-dashboard-file-error": "Impossibile importare la dashboard: struttura dati della dashboard non valida.", - "dashboard-import-missing-aliases-title": "Configura alias utilizzati dalla dashboard importata", - "create-new-widget": "Crea nuovo widget", - "import-widget": "Importa widget", - "widget-file": "Widget file", - "invalid-widget-file-error": "Impossibile importare il widget: struttura dati del widget non valida.", - "widget-import-missing-aliases-title": "Configura gli alias utilizzati dai widget importati", - "open-toolbar": "Apri barra degli strumenti", - "close-toolbar": "Chiudi barra degli strumenti", - "configuration-error": "Errore di configurazione", - "alias-resolution-error-title": "Errore di configurazione degli alias della dashboard", - "invalid-aliases-config": "Impossibile trovare un dispositivo corrispondente ad un qualche filtro degli alias.
Contatta l'amministratore per risolvere il problema.", - "select-devices": "Seleziona dispositivi", - "assignedToCustomer": "Assegnato al cliente", - "assignedToCustomers": "Assegnato ai clienti", - "public": "Pubblico", - "public-link": "Link pubblico", - "copy-public-link": "Copia link pubblico", - "public-link-copied-message": "Link pubblico della dashboard copiato negli appunti", - "manage-states": "Gestisci stati dashboard", - "states": "Stati dashboard", - "search-states": "Ricerca stati dashboard", - "selected-states": "{ count, plural, =1 {1 stato dashboard selezionato} other {# stati dashboard selezionati} }", - "edit-state": "Modifica stato dashboard", - "delete-state": "Elimina stato dashboard", - "add-state": "Aggiungi stato dashboard", - "state": "Stato dashboard", - "state-name": "Nome", - "state-name-required": "Nome stato dashboard obbligatorio.", - "state-id": "Id stato", - "state-id-required": "Id stato dashboard obbligatorio.", - "state-id-exists": "Uno stato della dashboard con lo stesso id è già presente.", - "is-root-state": "Stato radice", - "delete-state-title": "Elimina stato dashboard", - "delete-state-text": "Sei sicuro di voler eliminare lo stato della dashboard di nome '{{stateName}}'?", - "show-details": "Mostra dettagli", - "hide-details": "Nascondi dettagli", - "select-state": "Seleziona stato target", - "state-controller": "Stato controller" - }, - "datakey": { - "settings": "Impostazioni", - "advanced": "Avanzate", - "label": "Etichetta", - "color": "Colore", - "units": "Simbolo speciale da mostrare accanto al valore", - "decimals": "Numero cifre decimali", - "data-generation-func": "Funzione generazione dati", - "use-data-post-processing-func": "Usa funzione dopo il processamento dei dati", - "configuration": "Configurazione data key", - "timeseries": "Serie temporali", - "attributes": "Attributi", - "alarm": "Campi allarme", - "timeseries-required": "Le serie temporali dell'entità sono richieste.", - "timeseries-or-attributes-required": "Le serie temporali o gli attributi dell'entità sono richiesti.", - "maximum-timeseries-or-attributes": "Massimo { count, plural, =1 {1 serie temporale/attributo consentito.} other {# serie temporali/attributi consentiti.} }", - "alarm-fields-required": "Campi allarme obbligatori.", - "function-types": "Tipi funzione", - "function-types-required": "Tipi funzione obbligatorio.", - "maximum-function-types": "Massimo { count, plural, =1 {1 tipo di funzione consentito.} other {# tipi di funzione consentiti} }", - "time-description": "timestamp del valore corrente;", - "value-description": "il valore corrente;", - "prev-value-description": "risultato della precedente chiamata alla funzione;", - "time-prev-description": "timestamp del valore precedente;", - "prev-orig-value-description": "valore precedente originale;" - }, - "datasource": { - "type": "Tipo sorgente dati", - "name": "Nome", - "add-datasource-prompt": "Aggiungi una sorgente dati" - }, - "details": { - "edit-mode": "Modalità modifica", - "toggle-edit-mode": "Attiva/disattiva modalità di modifica" - }, - "device": { - "device": "Dispositivo", - "device-required": "Dispositivo richiesto.", - "devices": "Dispositivi", - "management": "Gestione dispositivo", - "view-devices": "Visualizza Dispositivi", - "device-alias": "Alias dispositivo", - "aliases": "Alias dispositivo", - "no-alias-matching": "'{{alias}}' non trovato.", - "no-aliases-found": "Nessun alias trovato.", - "no-key-matching": "'{{key}}' non trovata.", - "no-keys-found": "Nessuna chiave trovata.", - "create-new-alias": "Creane uno nuovo!", - "create-new-key": "Creane una nuova!", - "duplicate-alias-error": "Sono stati trovati dei duplicati dell'alias '{{alias}}'.
Gli alias di un dispositivo devono essere univoci all'interno della dashboard.", - "configure-alias": "Configura alias '{{alias}}'", - "no-devices-matching": "Nessun dispositivo corrispondente a '{{entity}}' è stato trovato.", - "alias": "Alias", - "alias-required": "Alias dispositivo richiesto.", - "remove-alias": "Rimuovi alias dispositivo", - "add-alias": "Aggiungi alias dispositivo", - "name-starts-with": "Dispositivo il cui nome inizia per", - "device-list": "Lista dispositivi", - "use-device-name-filter": "Usa filtro", - "device-list-empty": "Nessun dispositivo selezionato.", - "device-name-filter-required": "Filtro nome dispositivo obbligatorio.", - "device-name-filter-no-device-matched": "Nessun dispositivo il cui nome inizia per '{{device}}' è stato trovato.", - "add": "Aggiungi Dispositivo", - "assign-to-customer": "Assegna al cliente", - "assign-device-to-customer": "Assegna dispositivo/dispositivi al Cliente", - "assign-device-to-customer-text": "Seleziona i dispositivi da assegnare al cliente", - "make-public": "Rendi pubblico il dispositivo", - "make-private": "Rendi privato il dispositivo", - "no-devices-text": "Nessun dispositivo trovato", - "assign-to-customer-text": "Seleziona il cliente a cui assegnare il dispositivo/i dispositivi", - "device-details": "Dettagli dispositivo", - "add-device-text": "Aggiungi nuovo dispositivo", - "credentials": "Credenziali", - "manage-credentials": "Gestisci credenziali", - "delete": "Elimina dispositivo", - "assign-devices": "Assegna dispositivi", - "assign-devices-text": "Assegna { count, plural, =1 {1 dispositivo} other {# dispositivi} } al cliente", - "delete-devices": "Elimina dispositivi", - "unassign-from-customer": "Annulla assegnazione al cliente", - "unassign-devices": "Annulla assegnazione dispositivi", - "unassign-devices-action-title": "Annulla assegnazione { count, plural, =1 {1 dispositivo} other {# dispositivi} } al cliente", - "assign-new-device": "Assegna nuovo dispositivo", - "make-public-device-title": "Sei sicuro di voler rendere pubblico il dispositivo '{{deviceName}}'?", - "make-public-device-text": "Dopo la conferma il dispositivo e tutti i suoi dati saranno resi pubblici e accessibili dagli altri.", - "make-private-device-title": "Sei sicuro di voler rendere privato il dispositivo '{{deviceName}}'?", - "make-private-device-text": "Dopo la conferma il dispositivo e tutti i suoi dati saranno resi privati e non più accessibili da altri utenti.", - "view-credentials": "Visualizza credenziali", - "delete-device-title": "Sei sicuro di voler eliminare il dispositivo '{{deviceName}}'?", - "delete-device-text": "Attenzione, dopo la conferma il dispositivo e tutti i suoi dati non saranno più recuperabili.", - "delete-devices-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", - "delete-devices-action-title": "Elimina { count, plural, =1 {1 dispositivo} other {# dispositivi} }", - "delete-devices-text": "Attenzione, dopo la conferma tutti i dispositivi selezionati saranno eliminati e i relativi dati non saranno più recuperabili.", - "unassign-device-title": "Sei sicuro di voler annullare l'assegnazione del dispositivo '{{deviceName}}'?", - "unassign-device-text": "Dopo la conferma sarà annullata l'assegnazione del dispositivo e questo non sarà più accessibile dal cliente.", - "unassign-device": "Annulla assegnazione dispositivo", - "unassign-devices-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", - "unassign-devices-text": "Dopo la conferma sarà annullata l'assegnazione di tutti i dispositivi selezionati e questi non saranno più accessibili dal cliente.", - "device-credentials": "Credenziali Dispositivo", - "credentials-type": "Tipo credenziali", - "access-token": "Token di accesso", - "access-token-required": "Token di accesso obbligatorio.", - "access-token-invalid": "Il token di accesso deve avere una lunghezza compresa tra 1 e 32 caratteri.", - "secret": "Secret", - "secret-required": "Secret obbligatorio.", - "device-type": "Tipo dispositivo", - "device-type-required": "Tipo dispositivo obbligatorio.", - "select-device-type": "Seleziona tipo dispositivo", - "enter-device-type": "Inserisci typo dispositivo", - "any-device": "Qualsiasi dispositivo", - "no-device-types-matching": "Nessun dispositivo corrispondente a '{{entitySubtype}}' è stato trovato.", - "device-type-list-empty": "Nessun tipo di dispositivo selezionato.", - "device-types": "Tipi dispositivo", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "description": "Descrizione", - "events": "Eventi", - "details": "Dettagli", - "copyId": "Copia Id dispositivo", - "copyAccessToken": "Copia token di accesso", - "idCopiedMessage": "Id dispositivo copiato negli Appunti", - "accessTokenCopiedMessage": "Token di accesso del dispositivo copiato negli Appunti", - "assignedToCustomer": "Assegnato al cliente", - "unable-delete-device-alias-title": "Impossibile rimuovere l'alias del dispositivo", - "unable-delete-device-alias-text": "L'alias del dispositivo '{{deviceAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:
{{widgetsList}}", - "is-gateway": "È un gateway", - "public": "Pubblico", - "device-public": "Il dispositivo è pubblico", - "select-device": "Seleziona dispositivo" - }, - "dialog": { - "close": "Close dialog" - }, - "direction": { - "column": "Colonna", - "row": "Riga" - }, - "error": { - "unable-to-connect": "Impossibile connettersi al server! Controlla la connessione ad Internet.", - "unhandled-error-code": "Codice errore non gestito: {{errorCode}}", - "unknown-error": "Errore sconosciuto" - }, - "entity": { - "entity": "Entità", - "entities": "Entità", - "aliases": "Alias entità", - "entity-alias": "Alias entità", - "unable-delete-entity-alias-title": "Impossibile eliminare alias entità", - "unable-delete-entity-alias-text": "L'alias dell'entità '{{entityAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:
{{widgetsList}}", - "duplicate-alias-error": "Trovato un duplicato dell'alias '{{alias}}'.
Gli alias dell'entità devono essere univoci all'interno della dashboard.", - "missing-entity-filter-error": "Manca il filtro per l'alias '{{alias}}'.", - "configure-alias": "Configura '{{alias}}' alias", - "alias": "Alias", - "alias-required": "Alias entità obbligatorio.", - "remove-alias": "Rimuovi alias entità", - "add-alias": "Aggiungi alias entità", - "entity-list": "Lista entità", - "entity-type": "Tipo entità", - "entity-types": "Tipi entità", - "entity-type-list": "Lista tipo entità", - "any-entity": "Qualsiasi entità", - "enter-entity-type": "Inserisci tipo entità", - "no-entities-matching": "Nessuna entità corrispondente a '{{entity}}' è stata trovata.", - "no-entity-types-matching": "Nessun tipo di entità corrispondente a '{{entityType}}' è stato trovato.", - "name-starts-with": "Nome inizia per", - "use-entity-name-filter": "Usa filtro", - "entity-list-empty": "Nessuna entità selezionata.", - "entity-name-filter-required": "Filtro nome entità obbligatorio.", - "entity-name-filter-no-entity-matched": "Nessuna entità che inizia per '{{entity}}' è stata trovata.", - "all-subtypes": "Tutte", - "select-entities": "Seleziona entità", - "no-aliases-found": "Nessun alias trovato.", - "no-alias-matching": "'{{alias}}' non trovato.", - "create-new-alias": "Creane uno nuovo!", - "key": "Chiave", - "key-name": "Nome chiave", - "no-keys-found": "Nessuna chiave trovata.", - "no-key-matching": "'{{key}}' non trovata.", - "create-new-key": "Creane una nuova!", - "type": "Tipo", - "type-required": "Tipo entità obbligatorio.", - "type-device": "Dispositivo", - "type-devices": "Dispositivi", - "list-of-devices": "{ count, plural, =1 {Un dispositivo} other {Lista di # dispositivi} }", - "device-name-starts-with": "Dispositivi i cui nomi iniziano per '{{prefix}}'", - "type-asset": "Asset", - "type-assets": "Asset", - "list-of-assets": "{ count, plural, =1 {Un asset} other {Lista di # asset} }", - "asset-name-starts-with": "Asset i cui nomi iniziano per '{{prefix}}'", - "type-entity-view": "Vista entità", - "type-entity-views": "Viste entità", - "list-of-entity-views": "{ count, plural, =1 {Una vista entità} other {Lista di # viste entità} }", - "entity-view-name-starts-with": "Viste entità i cui nomi iniziano per '{{prefix}}'", - "type-rule": "Regola", - "type-rules": "Regole", - "list-of-rules": "{ count, plural, =1 {Una regola} other {Lista di # regole} }", - "rule-name-starts-with": "Regole i cui nomi iniziano per '{{prefix}}'", - "type-plugin": "Plugin", - "type-plugins": "Plugin", - "list-of-plugins": "{ count, plural, =1 {Un plugin} other {Lista di # plugin} }", - "plugin-name-starts-with": "Plugin i cui nomi iniziano per '{{prefix}}'", - "type-tenant": "Tenant", - "type-tenants": "Tenants", - "list-of-tenants": "{ count, plural, =1 {One tenant} other {Lista di # tenants} }", - "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'", - "type-customer": "Cliente", - "type-customers": "Clienti", - "list-of-customers": "{ count, plural, =1 {Un cliente} other {Lista di # clienti} }", - "customer-name-starts-with": "Clienti i cui nomi iniziano per '{{prefix}}'", - "type-user": "Utente", - "type-users": "Utenti", - "list-of-users": "{ count, plural, =1 {Un utente} other {Lista di # utenti} }", - "user-name-starts-with": "Utenti i cui nomi iniziano per '{{prefix}}'", - "type-dashboard": "Dashboard", - "type-dashboards": "Dashboard", - "list-of-dashboards": "{ count, plural, =1 {Una dashboard} other {Lista di # dashboard} }", - "dashboard-name-starts-with": "Dashboard i cui nomi iniziano per '{{prefix}}'", - "type-alarm": "Allarme", - "type-alarms": "Allarmi", - "list-of-alarms": "{ count, plural, =1 {Un allarme} other {Lista di # allarmi} }", - "alarm-name-starts-with": "Allarmi i cui nomi iniziano per '{{prefix}}'", - "type-rulechain": "Rule chain", - "type-rulechains": "Rule chain", - "list-of-rulechains": "{ count, plural, =1 {Una rule chain} other {Lista di # catene di regole} }", - "rulechain-name-starts-with": "Catene di regole i cui nomi iniziano per '{{prefix}}'", - "type-rulenode": "Nodo regola", - "type-rulenodes": "Nodi regola", - "list-of-rulenodes": "{ count, plural, =1 {Un nodo regola} other {Lista di # nodi regola} }", - "rulenode-name-starts-with": "Nodi regola i cui nomi iniziano per '{{prefix}}'", - "type-current-customer": "Cliente attuale", - "search": "Ricerca entità", - "selected-entities": "{ count, plural, =1 {1 entità selezionata} other {# entità selezionate} }", - "entity-name": "Nome entità", - "details": "Dettagli entità", - "no-entities-prompt": "Nessuna entità trovata", - "no-data": "Nessun dato da mostrare", - "columns-to-display": "Colonne da mostrare" - }, - "entity-view": { - "entity-view": "Vista entità", - "entity-view-required": "Vista entità richiesta.", - "entity-views": "Viste entità", - "management": "Gestione viste entità", - "view-entity-views": "Visualizza Viste entità", - "entity-view-alias": "Alias vista entità", - "aliases": "Alias vista entità", - "no-alias-matching": "'{{alias}}' non trovato.", - "no-aliases-found": "Nessun alias trovato.", - "no-key-matching": "'{{key}}' non trovata.", - "no-keys-found": "Nessuna chiave trovata.", - "create-new-alias": "Creane uno nuovo!", - "create-new-key": "Creane una nuova!", - "duplicate-alias-error": "Sono stati trovati dei duplicati dell'alias '{{alias}}'.
Gli alias di una vista entità devono essere univoci all'interno della dashboard.", - "configure-alias": "Configura alias '{{alias}}'", - "no-entity-views-matching": "Nessuna vista entità corrispondente a '{{entity}}' è stata trovato.", - "alias": "Alias", - "alias-required": "Alias vista entità richiesto.", - "remove-alias": "Rimuovi alias vista entità", - "add-alias": "Aggiungi alias vista entità", - "name-starts-with": "Vista entità il cui nome inizia per", - "entity-view-list": "Lista viste entità", - "use-entity-view-name-filter": "Usa filtro", - "entity-view-list-empty": "Nessuna vista entità selezionata.", - "entity-view-name-filter-required": "Filtro nome vista entità obbligatorio.", - "entity-view-name-filter-no-entity-view-matched": "Nessuna vista entità il cui nome inizia per '{{entity-view}}' è stata trovata.", - "add": "Aggiungi Vista entità", - "assign-to-customer": "Assegna al cliente", - "assign-entity-view-to-customer": "Assegna vista entità/viste entità al Cliente", - "assign-entity-view-to-customer-text": "Seleziona la vista entità da assegnare al cliente", - "no-entity-views-text": "Nessuna vista entità trovata", - "assign-to-customer-text": "Seleziona il cliente a cui assegnare la vista entità/le vista entità", - "entity-view-details": "Dettagli vista entità", - "add-entity-view-text": "Aggiungi nuova vista entità", - "delete": "Elimina vista entità", - "assign-entity-views": "Assegna viste entità", - "assign-entity-views-text": "Assegna { count, plural, =1 {1 vista entità} other {# viste entità} } al cliente", - "delete-entity-views": "Elimina viste entità", - "unassign-from-customer": "Annulla assegnazione al cliente", - "unassign-entity-views": "Annulla assegnazione viste entità", - "unassign-entity-views-action-title": "Annulla assegnazione { count, plural, =1 {1 vista entità} other {# viste entità} } al cliente", - "assign-new-entity-view": "Assegna nuova vista entità", - "delete-entity-view-title": "Sei sicuro di voler eliminare la vista entità '{{entity-viewName}}'?", - "delete-entity-view-text": "Attenzione, dopo la conferma la vista entità e tutti i suoi dati non saranno più recuperabili.", - "delete-entity-views-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 vista entità} other {# viste entità} }?", - "delete-entity-views-action-title": "Elimina { count, plural, =1 {1 vista entità} other {# viste entità} }", - "delete-entity-views-text": "Attenzione, dopo la conferma tutte le vista entità selezionati saranno eliminate e i relativi dati non saranno più recuperabili.", - "unassign-entity-view-title": "Sei sicuro di voler annullare l'assegnazione della vista entità '{{entity-viewName}}'?", - "unassign-entity-view-text": "Dopo la conferma sarà annullata l'assegnazione della vista entità e questa non sarà più accessibile dal cliente.", - "unassign-entity-view": "Annulla assegnazione vista entità", - "unassign-entity-views-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, =1 {1 vista entità} other {# viste entità} }?", - "unassign-entity-views-text": "Dopo la conferma sarà annullata l'assegnazione di tutte le vista entità selezionate e queste non saranno più accessibili dal cliente.", - "entity-view-type": "Tipo vista entità", - "entity-view-type-required": "Tipo vista entità obbligatorio.", - "select-entity-view-type": "Seleziona tipo vista entità", - "enter-entity-view-type": "Inserisci tipo vista entità", - "any-entity-view": "Qualsiasi vista entità", - "no-entity-view-types-matching": "Nessuna vista entità corrispondente a '{{entitySubtype}}' è stata trovata.", - "entity-view-type-list-empty": "Nessun tipo di vista entità selezionato.", - "entity-view-types": "Tipi vista entità", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "description": "Descrizione", - "events": "Eventi", - "details": "Dettagli", - "copyId": "Copia Id vista entità", - "assignedToCustomer": "Assegnata al cliente", - "unable-entity-view-device-alias-title": "Impossibile rimuovere l'alias del vista entità", - "unable-entity-view-device-alias-text": "L'alias del vista entità '{{entity-viewAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:
{{widgetsList}}", - "select-entity-view": "Seleziona vista entità", - "make-public": "Rendi pubblica la vista entità", - "make-private": "Rendi privata la vista entità", - "start-date": "Data inizio", - "start-ts": "Ora inizio", - "end-date": "Data fine", - "end-ts": "Ora fine", - "date-limits": "Limiti temporali", - "client-attributes": "Attributi cliente", - "shared-attributes": "Attributi condivisi", - "server-attributes": "Attributi server", - "timeseries": "Serie temporali", - "client-attributes-placeholder": "Attributi cliente", - "shared-attributes-placeholder": "Attributi condivisi", - "server-attributes-placeholder": "Attributi server", - "timeseries-placeholder": "Serie temporali", - "target-entity": "Entità target", - "attributes-propagation": "Propagazione degli attributi", - "attributes-propagation-hint": "La vista entità copierà automaticamente gli attributi specificati dall'entità target ogni volta che questa vista entità sarà salvata e aggiornata. Per ragioni di performance, gli attributi dell'entità target non sono propagati alle viste entità ogni cambiamento di attributo. È possibile abilitare la propagazione automatica configurando il nodo regola \"Copia alla vista\" nella rule chain e collegando i messaggi \"Post attributes\" a \"Attributes Updated\" al nuovo nodo regola.", - "timeseries-data": "Dati delle serie temporali", - "timeseries-data-hint": "Imposta le chiavi delle serie temporali dell'entità target che saranno accessibili alla vista entità. Questi dati sono di sola lettura.", - "make-public-entity-view-title": "Sei sicuro di voler rendere pubblica la vista entità '{{entity-viewName}}'?", - "make-public-entity-view-text": "Dopo la conferma la vista entità e tutti i suoi dati saranno resi pubblici e accessibili dagli altri.", - "make-private-entity-view-title": "Sei sicuro di voler rendere privata la vista entità '{{entity-viewName}}'?", - "make-private-entity-view-text": "Dopo la conferma la vista entità e tutti i suoi dati saranno resi privati e non più accessibili da altri utenti." - }, - "event": { - "event-type": "Tipo evento", - "type-error": "Errore", - "type-lc-event": "Ciclo di vita evento", - "type-stats": "Statistiche", - "type-debug-rule-node": "Debug", - "type-debug-rule-chain": "Debug", - "no-events-prompt": "Nessun evento trovato", - "error": "Errore", - "alarm": "Allarme", - "event-time": "Orario evento", - "server": "Server", - "body": "Body", - "method": "Metodo", - "type": "Tipo", - "message-id": "Id Messaggio", - "message-type": "Tipo Messaggio", - "data-type": "Tipo di dato", - "relation-type": "Tipo di relazione", - "metadata": "Metadati", - "data": "Dati", - "event": "Evento", - "status": "Stato", - "success": "Success", - "failed": "Failed", - "messages-processed": "Messaggi elaborati", - "errors-occurred": "Si sono verificati degli errori", - "all-events": "Tutte", - "entity-type": "Tipo entità" - }, - "extension": { - "extensions": "Estensioni", - "selected-extensions": "{ count, plural, =1 {1 estensione selezionata} other {# estensioni selezionate} }", - "type": "Tipo", - "key": "Chiave", - "value": "Valore", - "id": "Id", - "extension-id": "Id Estensione", - "extension-type": "Tipo Estensione", - "transformer-json": "JSON *", - "unique-id-required": "Id estensione corrente già esistente.", - "delete": "Elimina estensione", - "add": "Aggiungi estensione", - "edit": "Modifica estensione", - "delete-extension-title": "Sei sicuro di voler eliminare l'estensione '{{extensionId}}'?", - "delete-extension-text": "Attenzione, dopo la conferma l'estensione e tutti i suoi data non saranno più recuperabili.", - "delete-extensions-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 estensione} other {# estensioni} }?", - "delete-extensions-text": "Attenzione, dopo la conferma tutte le estensioni selezionate saranno eliminate.", - "converters": "Convertitori", - "converter-id": "Id convertitore", - "configuration": "Configurazione", - "converter-configurations": "Configurazioni convertitore", - "token": "Token di sicurezza", - "add-converter": "Aggiungi convertitore", - "add-config": "Aggiungi configurazione convertitore", - "device-name-expression": "Espressione nome dispositivo", - "device-type-expression": "Espressione tipo dispositivo", - "custom": "Custom", - "to-double": "To Double", - "transformer": "Transformer", - "json-required": "Transformer json is required.", - "json-parse": "Unable to parse transformer json.", - "attributes": "Attributi", - "add-attribute": "Aggiungi attributo", - "add-map": "Add mapping element", - "timeseries": "Serie temporali", - "add-timeseries": "Add timeseries", - "field-required": "Campo obbligatorio", - "brokers": "Broker", - "add-broker": "Aggiungi broker", - "host": "Host", - "port": "Porta", - "port-range": "Il numero di porta deve essere compreso tra 1 e 65535.", - "ssl": "Ssl", - "credentials": "Credenziali", - "username": "Nome utente", - "password": "Password", - "retry-interval": "Intervallo di ripetizione in millisecondi", - "anonymous": "Anonimo", - "basic": "Basic", - "pem": "PEM", - "ca-cert": "File certificato CA *", - "private-key": "File chiave privata *", - "cert": "File certificato *", - "no-file": "Nessun file selezionato.", - "drop-file": "Trascina un file o fai clic per selezionare un file da caricare.", - "mapping": "Mapping", - "topic-filter": "Filtro topic", - "converter-type": "Tipo convertitore", - "converter-json": "Json", - "json-name-expression": "Device name json expression", - "topic-name-expression": "Device name topic expression", - "json-type-expression": "Device type json expression", - "topic-type-expression": "Device type topic expression", - "attribute-key-expression": "Attribute key expression", - "attr-json-key-expression": "Attribute key json expression", - "attr-topic-key-expression": "Attribute key topic expression", - "request-id-expression": "Request id expression", - "request-id-json-expression": "Request id json expression", - "request-id-topic-expression": "Request id topic expression", - "response-topic-expression": "Response topic expression", - "value-expression": "Value expression", - "topic": "Topic", - "timeout": "Timeout in millisecondi", - "converter-json-required": "Convertitore json obbligatorio.", - "converter-json-parse": "Unable to parse converter json.", - "filter-expression": "Filter expression", - "connect-requests": "Richieste di connessione", - "add-connect-request": "Aggiungi richiesta di connessione", - "disconnect-requests": "Richieste di disconnessione", - "add-disconnect-request": "Aggiungi richiesta di disconnessione", - "attribute-requests": "Attribute requests", - "add-attribute-request": "Add attribute request", - "attribute-updates": "Attribute updates", - "add-attribute-update": "Add attribute update", - "server-side-rpc": "RPC lato server", - "add-server-side-rpc-request": "Aggiungi richiesta RPC server-side", - "device-name-filter": "Filtro nome dispositivo", - "attribute-filter": "Filtro attributo", - "method-filter": "Filtro metodo", - "request-topic-expression": "Request topic expression", - "response-timeout": "Timeout risposta in millisecondi", - "topic-expression": "Topic expression", - "client-scope": "Visibilità client", - "add-device": "Aggiungi dispositivo", - "opc-server": "Server", - "opc-add-server": "Aggiungi server", - "opc-add-server-prompt": "Aggiungi server", - "opc-application-name": "Nome applicazione", - "opc-application-uri": "Uri applicazione", - "opc-scan-period-in-seconds": "Intervallo di scansione in secondi", - "opc-security": "Sicurezza", - "opc-identity": "Identità", - "opc-keystore": "Keystore", - "opc-type": "Tipo", - "opc-keystore-type": "Tipo", - "opc-keystore-location": "Location *", - "opc-keystore-password": "Password", - "opc-keystore-alias": "Alias", - "opc-keystore-key-password": "Chiave password", - "opc-device-node-pattern": "Device node pattern", - "opc-device-name-pattern": "Device name pattern", - "modbus-server": "Server/slave", - "modbus-add-server": "Aggiungi server/slave", - "modbus-add-server-prompt": "Aggiungi server/slave", - "modbus-transport": "Transport", - "modbus-tcp-reconnect": "Riconnessione automatica", - "modbus-rtu-over-tcp": "RTU over TCP", - "modbus-port-name": "Nome porta seriale", - "modbus-encoding": "Codifica", - "modbus-parity": "Parità", - "modbus-baudrate": "Baud rate", - "modbus-databits": "Data bits", - "modbus-stopbits": "Stop bits", - "modbus-databits-range": "Data bits deve essere compreso nell'intervallo 7-8.", - "modbus-stopbits-range": "Stop bits deve essere compreso nell'intervallo 1-2.", - "modbus-unit-id": "ID unità", - "modbus-unit-id-range": "ID unità deve essere compreso nell'intervallo 1-247.", - "modbus-device-name": "Nome dispositivo", - "modbus-poll-period": "Intervallo di polling (ms)", - "modbus-attributes-poll-period": "Intervallo di polling degli attributi (ms)", - "modbus-timeseries-poll-period": "Intervallo di polling delle serie temporali (ms)", - "modbus-poll-period-range": "L'intervallo di polling deve essere un valore positivo.", - "modbus-tag": "Tag", - "modbus-function": "Funzione", - "modbus-register-address": "Indirizzo registro", - "modbus-register-address-range": "Indirizzo registro deve essere compreso tra 0 e 65535.", - "modbus-register-bit-index": "Bit index", - "modbus-register-bit-index-range": "Bit index deve essere compreso tra 0 e 15.", - "modbus-register-count": "Register count", - "modbus-register-count-range": "Register count dovrebbe essereun valore positivo.", - "modbus-byte-order": "Byte order", - "sync": { - "status": "Stato", - "sync": "Sincronizzato", - "not-sync": "Non sincronizzato", - "last-sync-time": "Ultima sincronizzazione", - "not-available": "Non disponibile" + "credentials" : { + "credentials" : "Credenziali", + "api-key" : "Chiave API" + } + }, + "overlays" : { + "overlays" : "Overlay", + "overlays-hint" : "Configura sorgenti dati, aspetto, comportamento, opzioni di modifica e raggruppamento per le entità della mappa", + "trips" : "Percorsi", + "markers" : "Marker", + "polygons" : "Poligoni", + "circles" : "Cerchi" + }, + "data-layer" : { + "source" : "Sorgente", + "filter" : "Filtro", + "additional-data-keys" : "Chiavi dati aggiuntive", + "additional-datasources" : "Sorgenti dati aggiuntive", + "additional-datasources-hint" : "Sorgenti dati per accedere ad attributi o telemetria da entità non visualizzate sulla mappa, utilizzabili nelle funzioni degli overlay.", + "more-datasources" : "Altre sorgenti dati", + "data-keys" : "Chiavi dati", + "add-datasource" : "Aggiungi sorgente dati", + "no-datasources" : "Nessuna sorgente dati configurata", + "remove-datasource" : "Rimuovi sorgente dati", + "behavior" : "Comportamento", + "on-click" : "Al clic", + "on-click-hint" : "Azione invocata quando l'utente clicca sull'elemento della mappa.", + "groups" : "Gruppi", + "groups-hint" : "Elenco dei nomi dei gruppi assegnati all'overlay, utilizzati per alternarne la visibilità sulla mappa.", + "color" : "Colore", + "color-settings" : "Impostazioni colore", + "color-type-constant" : "Costante", + "color-type-range" : "Intervallo", + "color-type-function" : "Funzione", + "color-range-source-key" : "Chiave sorgente intervallo colori", + "color-range-source-key-required" : "Chiave sorgente intervallo colori richiesta", + "color-range" : "Intervallo colori", + "color-function" : "Funzione colore", + "label" : "Etichetta", + "tooltip" : "Suggerimento", + "pattern-type-pattern" : "Modello", + "pattern-type-function" : "Funzione", + "label-pattern" : "Etichetta (esempi di modello: '${entityName}', '${entityName}: (Testo ${keyName} unità.)')", + "label-function" : "Funzione etichetta", + "tooltip-pattern" : "Suggerimento (per es. 'Testo ${keyName} unità.' oppure Testo link)", + "tooltip-function" : "Funzione suggerimento", + "tooltip-trigger" : "Attivatore suggerimento", + "tooltip-trigger-click" : "Mostra suggerimento al clic", + "tooltip-trigger-hover" : "Mostra suggerimento al passaggio del mouse", + "auto-close-tooltips" : "Chiudi automaticamente i suggerimenti", + "tooltip-offset" : "Scostamento suggerimento", + "tooltip-offset-horizontal" : "Orizzontale", + "tooltip-offset-vertical" : "Verticale", + "tooltip-tag-actions" : "Azioni tag", + "add-tooltip-tag-action" : "Aggiungi azione tag", + "edit-tooltip-tag-action" : "Modifica azione tag", + "remove-tooltip-tag-action" : "Rimuovi azione tag", + "action-add" : "Aggiungi", + "action-edit" : "Modifica", + "action-move" : "Sposta", + "action-remove" : "Rimuovi", + "edit-instruments" : "Strumenti", + "persist-location-attribute-scope" : "Ambito dell'attributo per la persistenza della posizione", + "enable-snapping" : "Abilita aggancio ai vertici per disegno preciso", + "enable-snapping-hint" : "Allinea automaticamente i nuovi punti alle forme esistenti per facilitare e rendere più preciso il disegno.", + "drag-drop-mode" : "Modalità trascina e rilascia", + "trip" : { + "no-trips" : "Nessun percorso configurato", + "add-trip" : "Aggiungi percorso", + "trip-configuration" : "Configurazione percorso", + "remove-trip" : "Rimuovi percorso" }, - "export-extensions-configuration": "Esporta configurazione estensioni", - "import-extensions-configuration": "Importa configurazione estensioni", - "import-extensions": "Importa estensione", - "import-extension": "Importa estensione", - "export-extension": "Esporta estensione", - "file": "File estensione", - "invalid-file-error": "File estensione non valido" - }, - "fullscreen": { - "expand": "Espandi a tutto schermo", - "exit": "Esci da schermo intero", - "toggle": "Commuta modalità schermo intero", - "fullscreen": "Schermo intero" - }, - "function": { - "function": "Funzione" - }, - "grid": { - "delete-item-title": "Sei sicuro di voler eliminare questo elemento?", - "delete-item-text": "Attenzione, dopo la conferma questo elemento e tutti i suoi dati non saranno più recuperabili.", - "delete-items-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 elemento} other {# elementi} }?", - "delete-items-action-title": "Elimina { count, plural, =1 {1 elemento} other {# elementi} }", - "delete-items-text": "Attenzione, dopo la conferma tutti gli elementi selezionati saranno rimossi e i relativi dati non saranno più recuperabili.", - "add-item-text": "Aggiungi nuovo elemento", - "no-items-text": "Nessun elemento trovato", - "item-details": "Dettagli elemento", - "delete-item": "Elimina elemento", - "delete-items": "Elimina elementi", - "scroll-to-top": "Scorri verso l'alto" - }, - "help": { - "goto-help-page": "Vai all'help" - }, - "home": { - "home": "Home", - "profile": "Profilo", - "logout": "Logout", - "menu": "Menu", - "avatar": "Avatar", - "open-user-menu": "Apri menu utente" - }, - "import": { - "no-file": "Nessun file selezionato", - "drop-file": "Trascina un file JSON o fai clic per selezionare un file da caricare." - }, - "item": { - "selected": "Selezionata" - }, - "js-func": { - "no-return-error": "La funzione deve restituire un valore!", - "return-type-mismatch": "La funzione deve restituire un valore di tipo '{{type}}'!", - "tidy": "Tidy" - }, - "key-val": { - "key": "Chiave", - "value": "Valore", - "remove-entry": "Rimuovi voce", - "add-entry": "Aggiungi voce", - "no-data": "Nessuna voce" - }, - "layout": { - "layout": "Layout", - "manage": "Gestisci layout", - "settings": "Impostazioni layout", - "color": "Colore", - "main": "Main", - "right": "Destra", - "select": "Select target layout" - }, - "legend": { - "direction": "Direzione", - "position": "Posizione Legenda", - "show-max": "Mostra valore max", - "show-min": "Mostra valore min", - "show-avg": "Mostra valore medio", - "show-total": "Mostra valore totale", - "settings": "Impostazioni legenda", - "min": "min", - "max": "max", - "avg": "avg", - "total": "totale" - }, - "login": { - "login": "Accedi", - "request-password-reset": "Richiesta reset password", - "reset-password": "Reset Password", - "create-password": "Crea Password", - "passwords-mismatch-error": "Le password inserite devono corrispondere!", - "password-again": "Ripeti Password", - "sign-in": "Please sign in", - "username": "Nome utente (email)", - "remember-me": "Ricordami", - "forgot-password": "Password dimenticata?", - "password-reset": "Password reset", - "new-password": "Nuova password", - "new-password-again": "Ripeti nuova password", - "password-link-sent-message": "Link reset password inviato con successo!", - "email": "Email", - "login-with": "Accedi con {{name}}", - "or": "o" - }, - "position": { - "top": "Alto", - "bottom": "Basso", - "left": "Sinistra", - "right": "Destra" - }, - "profile": { - "profile": "Profilo", - "last-login-time": "Ultimo accesso", - "change-password": "Modifica Password", - "current-password": "Password attuale" - }, - "relation": { - "relations": "Relazioni", - "direction": "Direzione", - "search-direction": { - "FROM": "Da", - "TO": "A" + "marker" : { + "marker" : "Indicatore", + "latitude-key" : "Chiave latitudine", + "longitude-key" : "Chiave longitudine", + "x-pos-key" : "Chiave posizione X", + "y-pos-key" : "Chiave posizione Y", + "latitude-key-required" : "Chiave latitudine obbligatoria", + "longitude-key-required" : "Chiave longitudine obbligatoria", + "x-pos-key-required" : "Chiave posizione X obbligatoria", + "y-pos-key-required" : "Chiave posizione Y obbligatoria", + "no-markers" : "Nessun indicatore configurato", + "add-marker" : "Aggiungi indicatore", + "marker-configuration" : "Configurazione indicatore", + "remove-marker" : "Rimuovi indicatore", + "marker-type" : "Tipo di indicatore", + "marker-type-shape" : "Forma", + "marker-type-icon" : "Icona", + "marker-type-image" : "Immagine", + "shape" : "Forma", + "icon" : "Icona", + "image" : "Immagine", + "marker-shapes" : "Forme dell'indicatore", + "marker-icon" : "Icona indicatore", + "marker-appearance" : "Aspetto dell'indicatore", + "marker-image" : "Immagine indicatore", + "marker-image-type-image" : "Immagine", + "marker-image-type-function" : "Funzione", + "custom-marker-image-size" : "Dimensione immagine personalizzata", + "marker-image-function" : "Funzione immagine indicatore", + "marker-images" : "Immagini indicatore", + "marker-offset" : "Offset dell'indicatore", + "offset-horizontal" : "Orizzontale", + "offset-vertical" : "Verticale", + "rotate-marker" : "Ruota indicatore", + "offset-angle" : "Angolo di offset", + "position-conversion" : "Conversione posizione", + "position-conversion-function" : "Funzione di conversione posizione, deve restituire coordinate x,y come numeri tra 0 e 1", + "clustering" : { + "use-map-markers-clustering" : "Usa raggruppamento indicatori mappa", + "zoom-on-cluster-click" : "Zoom al clic su un cluster", + "max-zoom" : "Zoom massimo per includere un indicatore in un cluster (0 - 18)", + "max-radius" : "Raggio massimo coperto da un cluster", + "zoom-animation" : "Animazione durante lo zoom", + "bounds-on-cluster-mouse-over" : "Limiti del cluster al passaggio del mouse", + "spiderfy-max-zoom-level" : "Mostra tutti gli indicatori del cluster allo zoom massimo", + "load-optimization" : "Ottimizzazione del caricamento", + "chunked-load" : "Usa blocchi per aggiungere indicatori senza bloccare la pagina", + "lazy-load" : "Usa caricamento ritardato per gli indicatori", + "use-cluster-marker-color-function" : "Usa funzione colore per indicatori cluster", + "marker-color-function" : "Funzione colore indicatore" + }, + "edit" : "Modifica indicatore", + "remove-marker-for" : "Rimuovi indicatore per '{{entityName}}'", + "place-marker" : "Posiziona indicatore", + "place-marker-hint" : "Clicca per posizionare l'indicatore", + "place-marker-hint-with-entity" : "Clicca per posizionare l'entità '{{entityName}}'" }, - "direction-type": { - "FROM": "da", - "TO": "a" + "path" : { + "path" : "Percorso", + "path-decorator" : "Decoratore percorso", + "decorator-symbol" : "Simbolo decoratore", + "decorator-symbol-arrow-head" : "Freccia", + "decorator-symbol-dash" : "Tratto", + "decorator-arrangement" : "Disposizione decoratore", + "decorator-offset" : "Inizio", + "decorator-end-offset" : "Fine", + "decorator-repeat" : "Ripeti" }, - "from-relations": "Relazioni in uscita", - "to-relations": "Relazioni in ingresso", - "selected-relations": "{ count, plural, =1 {1 relazione selezionata} other {# relazioni selezionate} }", - "type": "Tipo", - "to-entity-type": "A tipo entità", - "to-entity-name": "A nome entità", - "from-entity-type": "Da tipo entità", - "from-entity-name": "Da nome entità", - "to-entity": "A entità", - "from-entity": "Da entità", - "delete": "Elimina relazione", - "relation-type": "Tipo di relazione", - "relation-type-required": "Tipo di relazione obbligatorio.", - "any-relation-type": "Ogni tipo", - "add": "Aggiungi relazione", - "edit": "Modifica relazione", - "delete-to-relation-title": "Sei sicuro di voler eliminare la relazione con l'entità '{{entityName}}'?", - "delete-to-relation-text": "Attenzione, dopo la conferma l'entità '{{entityName}}' sarà scollegata dall'entità corrente.", - "delete-to-relations-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 relazione} other {# relazioni} }?", - "delete-to-relations-text": "Attenzione, dopo la conferma tutte le relazioni selezionate saranno rimosse e le corrispondenti entità scollegate da quella corrente.", - "delete-from-relation-title": "Sei sicuro di voler eliminare la relazione dall'entità '{{entityName}}'?", - "delete-from-relation-text": "Attenzione, dopo la conferma l'entità corrente sarà scollegata dall'entità '{{entityName}}'.", - "delete-from-relations-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 relazione} other {# relazioni} }?", - "delete-from-relations-text": "Attenzione, dopo la conferma tutte le relazioni selezionate saranno rimosse e l'entità corrente scollegata dalle corrispondenti entità.", - "remove-relation-filter": "Rimuovi filtro relazioni", - "add-relation-filter": "Aggiungi filtro relazioni", - "any-relation": "Qualsiasi relazione", - "relation-filters": "Filtri relazioni", - "additional-info": "Informazioni aggiuntive (JSON)", - "invalid-additional-info": "Impossibile analizzare le informazioni aggiuntive in JSON." - }, - "rulechain": { - "rulechain": "Rule chain", - "rulechains": "Rule chain", - "root": "Root", - "delete": "Cancella rule chain", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "description": "Descrizione", - "add": "Aggiungi Rule Chain", - "set-root": "Imposta la rule chain come root", - "set-root-rulechain-title": "Sei sicuro di voler impostare la rule chain '{{ruleChainName}}' come root?", - "set-root-rulechain-text": "Dopo la conferma la rule chain diverrà root a gestirà tutti i messaggi in arrivo.", - "delete-rulechain-title": "Sei sicuro di voler eliminare la rule chain '{{ruleChainName}}'?", - "delete-rulechain-text": "Attenzione, dopo la conferma la rule chain e tutti i dati relativi non saranno più recuperabili.", - "delete-rulechains-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 rule chain} other {# rule chain} }?", - "delete-rulechains-action-title": "Elimina { count, plural, =1 {1 rule chain} other {# rule chain} }", - "delete-rulechains-text": "Attenzione, dopo la conferma tutte le rule chain selezionate saranno rimosse e tutti i relativi dati non saranno più recuperabili.", - "add-rulechain-text": "Aggiungi nuova rule chain", - "no-rulechains-text": "Nessuna rule chain trovata", - "rulechain-details": "Dettagli rule chain", - "details": "Dettagli", - "events": "Eventi", - "system": "Sistema", - "import": "Importa rule chain", - "export": "Esporta rule chain", - "export-failed-error": "Impossibile esportare rule chain: {{error}}", - "create-new-rulechain": "Crea nuova rule chain", - "rulechain-file": "File rule chain", - "invalid-rulechain-file-error": "Impossibile importare rule chain: struttura dati rule chain non valida.", - "copyId": "Copia Id rule chain", - "idCopiedMessage": "Id rule chain copiato negli appunti", - "select-rulechain": "Seleziona rule chain", - "no-rulechains-matching": "Nessuna rule chain corrispondente a '{{entity}}' è stata trovata.", - "rulechain-required": "Rule chain obbligatoria", - "management": "Gestione regole", - "debug-mode": "Modalità debug" - }, - "rulenode": { - "details": "Dettagli", - "events": "Eventi", - "search": "Ricerca nodi", - "open-node-library": "Apri libreria nodi", - "add": "Aggiungi nodo regola", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "type": "Tipo", - "delete": "Elimina nodo regola", - "select-all-objects": "Seleziona tutti i nodi e le connessioni", - "deselect-all-objects": "Deseleziona tutti i nodi e le connessioni", - "delete-selected-objects": "Cancella nodi e connessioni selezionate", - "delete-selected": "Elimina selezionati", - "select-all": "Seleziona tutto", - "copy-selected": "Copia selezionata", - "deselect-all": "Deseleziona tutto", - "rulenode-details": "Dettagli nodo regola", - "debug-mode": "Modalità debug", - "configuration": "Configurazione", - "link": "Link", - "link-details": "Dettagli link nodo regola", - "add-link": "Aggiungi link", - "link-label": "Etichetta link", - "link-label-required": "Etichetta link obbligatoria.", - "custom-link-label": "Etichetta link personalizzata", - "custom-link-label-required": "Etichetta link personalizzata obbligatoria.", - "link-labels": "Etichette link", - "link-labels-required": "Etichette link richieste.", - "no-link-labels-found": "Nessuna etichetta link trovata.", - "no-link-label-matching": "'{{label}}' non trovata.", - "create-new-link-label": "Creane una nuova!", - "type-filter": "Filtro", - "type-filter-details": "Filtra i messaggi in arrivo con le condizioni configurate", - "type-enrichment": "Enrichment", - "type-enrichment-details": "Aggiungi informazioni addizionali nei metadati del messaggio", - "type-transformation": "Trasformazione", - "type-transformation-details": "Modifica payload messaggio e metadati", - "type-action": "Azioni", - "type-action-details": "Perform special action", - "type-external": "External", - "type-external-details": "Interagisci con un sistema esterno", - "type-rule-chain": "Rule Chain", - "type-rule-chain-details": "Inoltra i messaggi in arrivo ad una specifica Rule Chain", - "type-input": "Input", - "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node", - "type-unknown": "Sconosciuto", - "type-unknown-details": "Nodo regola non trovato", - "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.", - "ui-resources-load-error": "Failed to load configuration ui resources.", - "invalid-target-rulechain": "Unable to resolve target rule chain!", - "test-script-function": "Test script function", - "message": "Messaggio", - "message-type": "Tipo messaggio", - "select-message-type": "Seleziona tipo messaggio", - "message-type-required": "Tipo messaggio obbligatorio", - "metadata": "Metadata", - "metadata-required": "Metadata entries can't be empty.", - "output": "Output", - "test": "Test", - "help": "Aiuto" - }, - "tenant": { - "tenant": "Tenant", - "tenants": "Tenant", - "management": "Gestione Tenant", - "add": "Aggiungi Tenant", - "admins": "Amministratori", - "manage-tenant-admins": "Gestisci amministratori tenant", - "delete": "Cancella tenant", - "add-tenant-text": "Aggiungi nuovo tenant", - "no-tenants-text": "Nessun tenant trovato", - "tenant-details": "Dettagli tenant", - "delete-tenant-title": "Sei sicuro di voler eliminare il tenant '{{tenantTitle}}'?", - "delete-tenant-text": "Attenzione, dopo la conferma il tenant e tutti i suoi dati non saranno più recuperabili.", - "delete-tenants-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 tenant} other {# tenant} }?", - "delete-tenants-action-title": "Elimina { count, plural, =1 {1 tenant} other {# tenant} }", - "delete-tenants-text": "Attenzione, dopo la conferma tutti i tenant selezionati saranno eliminati e tutti i loro dati non saranno più recuperabili.", - "title": "Titolo", - "title-required": "Titolo obbligatorio.", - "description": "Descrizione", - "details": "Dettagli", - "events": "Eventi", - "copyId": "Copia Id tenant", - "idCopiedMessage": "Id tenant copiato negli appunti", - "select-tenant": "Seleziona tenant", - "no-tenants-matching": "Nessun tenant corrispondente a '{{entity}}' è stato trovato.", - "tenant-required": "Tenant obbligatorio" - }, - "timeinterval": { - "seconds-interval": "{ seconds, plural, =1 {1 secondo} other {# secondi} }", - "minutes-interval": "{ minutes, plural, =1 {1 minuto} other {# minuti} }", - "hours-interval": "{ hours, plural, =1 {1 ora} other {# ore} }", - "days-interval": "{ days, plural, =1 {1 giorno} other {# giorni} }", - "days": "Giorni", - "hours": "Ore", - "minutes": "Minuti", - "seconds": "Secondi", - "advanced": "Avanzate" - }, - "timewindow": { - "days": "{ days, plural, =1 { giorno } other {# giorni } }", - "hours": "{ hours, plural, =0 { ora } =1 {1 ora } other {# ore } }", - "minutes": "{ minutes, plural, =0 { minuto } =1 {1 minuto } other {# minuti } }", - "seconds": "{ seconds, plural, =0 { secondo } =1 {1 secondo } other {# secondi } }", - "realtime": "Realtime", - "history": "Cronologia", - "last-prefix": "ultimo", - "period": "da {{ startTime }} a {{ endTime }}", - "edit": "Modifica intervallo temporale", - "date-range": "Intervallo date", - "last": "Ultimo", - "time-period": "Intervallo temporale", - "hide": "Nascondi" - }, - "user": { - "user": "Utente", - "users": "Utenti", - "customer-users": "Utente cliente", - "tenant-admins": "Amministratori Tenant", - "sys-admin": "Amministratore di sistema", - "tenant-admin": "Amministratore tenant", - "customer": "Cliente", - "anonymous": "Anonimo", - "add": "Aggiungi Utente", - "delete": "Elimina utente", - "add-user-text": "Aggiungi nuovo utente", - "no-users-text": "Nessun utente trovato", - "user-details": "Dettagli utente", - "delete-user-title": "Sei sicuro di voler eliminare l'utente '{{userEmail}}'?", - "delete-user-text": "Attenzione, dopo la conferma l'utente e tutti i suoi dati non saranno più recuperabili.", - "delete-users-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 utente} other {# utenti} }?", - "delete-users-action-title": "Elimina { count, plural, =1 {1 utente} other {# utenti} }", - "delete-users-text": "Attenzione, dopo la conferma tutti gli utenti selezionati saranno eliminati e tutti i relativi dati non saranno più recuperabili.", - "activation-email-sent-message": "Email di attivazione inviata con successo!", - "resend-activation": "Invia di nuovo attivazione", - "email": "Email", - "email-required": "Email obbligatoria.", - "invalid-email-format": "Formato email non valido.", - "first-name": "Nome", - "last-name": "Cognome", - "description": "Descrizione", - "default-dashboard": "Dashboard di default", - "always-fullscreen": "Sempre a schermo intero", - "select-user": "Seleziona utente", - "no-users-matching": "Nessun utente corrispondente a '{{entity}}' è stato trovato.", - "user-required": "Utente obbligatorio", - "activation-method": "Metodo di attivazione", - "display-activation-link": "Mostra link di attivazione", - "send-activation-mail": "Invia email di attivazione", - "activation-link": "Link attivazione utente", - "activation-link-text": "Per attivare l'utente utilizza il seguente link di attivazione :", - "copy-activation-link": "Copia link di attivazione", - "activation-link-copied-message": "Link di attivazione utente copiato negli appunti", - "details": "Dettagli", - "login-as-tenant-admin": "Accedi come Amministratore tenant", - "login-as-customer-user": "Accedi come Utente cliente", - "disable-account": "Disabilita account utente", - "enable-account": "Abilita account utente", - "enable-account-message": "L'account utente è stato abilitato correttamente!", - "disable-account-message": "L'account utente è stato disabilitato correttamente!" - }, - "value": { - "type": "Tipo valore", - "string": "String", - "string-value": "Valore string", - "integer": "Integer", - "integer-value": "Valore integer", - "invalid-integer-value": "Valore integer non valido", - "double": "Double", - "double-value": "Valore double", - "boolean": "Boolean", - "boolean-value": "Valore boolean", - "false": "Falso", - "true": "Vero", - "long": "Long" - }, - "widget": { - "widget-library": "Libreria Widget", - "widget-bundle": "Bundle widget", - "select-widgets-bundle": "Seleziona bundle widget", - "management": "Gestione widget", - "editor": "Editor Widget", - "widget-type-not-found": "Problem loading widget configuration.
Probably associated\n widget type was removed.", - "widget-type-load-error": "Widget non caricato a causa dei seguenti errori:", - "remove": "Elimina widget", - "edit": "Modifica widget", - "remove-widget-title": "sei sicuro di voler eliminare il widget '{{widgetTitle}}'?", - "remove-widget-text": "Dopo la conferma il widget e tutti i suoi dati non saranno più recuperabili.", - "timeseries": "Time series", - "search-data": "Cerca dati", - "no-data-found": "Nessun dato trovato", - "latest": "Ultimi valori", - "rpc": "Control widget", - "alarm": "Alarm widget", - "static": "Static widget", - "select-widget-type": "Seleziona tipo widget", - "missing-widget-title-error": "Il tiolo del widget deve essere specificato!", - "widget-saved": "Widget salvato", - "unable-to-save-widget-error": "Impossibile salvare il widget! Sono presenti degli errori!", - "save": "Salva widget", - "saveAs": "Salva widget come", - "save-widget-type-as": "Salva tipo widget come", - "save-widget-type-as-text": "Please enter new widget title and/or select target widgets bundle", - "toggle-fullscreen": "Commuta modalità schermo intero", - "run": "Esegui widget", - "title": "Titolo widget", - "title-required": "Titolo widget obbligatorio.", - "type": "Tipo widget", - "resources": "Risorse", - "resource-url": "JavaScript/CSS URL", - "remove-resource": "Rimuovi risorsa", - "add-resource": "Aggiungi risorsa", - "html": "HTML", - "tidy": "Tidy", - "css": "CSS", - "settings-schema": "Impostazioni schema", - "datakey-settings-schema": "Impostazioni Data key schema", - "javascript": "Javascript", - "add-widget-type": "Aggiungi nuovo tipo widget", - "widget-template-load-failed-error": "Caricamento template widget fallito!", - "add": "Aggiungi Widget", - "undo": "Annulla modifiche widget", - "export": "Esporta widget" - }, - "widget-action": { - "header-button": "Widget header button", - "open-dashboard-state": "Navigate to new dashboard state", - "update-dashboard-state": "Aggiorna lo stato della dashboard attuale", - "open-dashboard": "Navigate to other dashboard", - "custom": "Custom action", - "target-dashboard-state": "Target dashboard state", - "target-dashboard-state-required": "Target dashboard state is required", - "set-entity-from-widget": "Set entity from widget", - "target-dashboard": "Target dashboard", - "open-right-layout": "Open right dashboard layout (mobile view)" - }, - "widgets-bundle": { - "current": "Bundle corrente", - "widgets-bundles": "Bundle Widget", - "add": "Aggiungi Bundle Widget", - "delete": "Cancella bundle widget", - "title": "Titolo", - "title-required": "Titolo obbligatorio.", - "add-widgets-bundle-text": "Aggiungi nuovo bundle widget", - "no-widgets-bundles-text": "Nessun bundle widget trovato", - "empty": "Bundle widget vuoto", - "details": "Dettagli", - "widgets-bundle-details": "Dettagli bundle widget", - "delete-widgets-bundle-title": "Sei sicuro di voler eliminare il bundle widget '{{widgetsBundleTitle}}'?", - "delete-widgets-bundle-text": "Attenzione, dopo la conferma il bundle widget e tutti i suoi dati non saranno più recuperabili.", - "delete-widgets-bundles-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 bundle widget} other {# bundle widget} }?", - "delete-widgets-bundles-action-title": "Elimina { count, plural, =1 {1 bundle widget} other {# bundle widget} }", - "delete-widgets-bundles-text": "Attenzione, dopo la conferma tutti i bundle widget selezionati saranno rimossi e tutti i loro dati non saranno più recuperabili.", - "no-widgets-bundles-matching": "Nessun bundle widget corrispondente a '{{widgetsBundle}}' è stato trovato.", - "widgets-bundle-required": "Bundle widget obbligatorio.", - "system": "Sistema", - "import": "Importa bundle widget", - "export": "Esporta bundle widget", - "export-failed-error": "Impossibile esportare bundle widget: {{error}}", - "create-new-widgets-bundle": "Crea nuovo bundle widget", - "widgets-bundle-file": "File bundle widget", - "invalid-widgets-bundle-file-error": "Impossibile importare bundle widget: struttura dati non valida." - }, - "widget-config": { - "data": "Dati", - "settings": "Impostazioni", - "advanced": "Avanzate", - "title": "Titolo", - "title-tooltip": "Tooltip titolo", - "general-settings": "Impostazioni generali", - "display-title": "Mostra titolo", - "drop-shadow": "Drop shadow", - "enable-fullscreen": "Abilita schermo intero", - "background-color": "Colore sfondo", - "text-color": "Colore testo", - "padding": "Padding", - "margin": "Margin", - "widget-style": "Stile Widget", - "title-style": "Stile titolo", - "mobile-mode-settings": "Impostazioni modalità mobile", - "order": "Ordinamento", - "height": "Altezza", - "units": "Simbolo speciale da mostrare accanto al valore", - "decimals": "Numero di cifre decimali", - "timewindow": "Intervallo temporale", - "use-dashboard-timewindow": "Usa intervallo temporale dashboard", - "display-timewindow": "Mostra intervallo temporale", - "display-legend": "Mostra legenda", - "datasources": "Sorgenti dei dati", - "maximum-datasources": "Massimo { count, plural, =1 {1 sorgente dati consentita.} other {# sorgenti dati consentite} }", - "datasource-type": "Tipo", - "datasource-parameters": "Parametri", - "remove-datasource": "Rimuovi sorgente dati", - "add-datasource": "Aggiungi sorgente dati", - "target-device": "Dispositivo Target", - "alarm-source": "Sorgente Allarme", - "actions": "Azioni", - "action": "Azione", - "add-action": "Aggiungi azione", - "search-actions": "Ricerca azioni", - "action-source": "Sorgente azione", - "action-source-required": "Sorgente azione obbligatoria.", - "action-name": "Nome", - "action-name-required": "Nome azione obbligatorio.", - "action-name-not-unique": "Un'altra azione con lo stesso nome è già presente.\nIl nome di una azione dovrebbe essere univoco all'interno della stessa sorgente.", - "action-icon": "Icona", - "action-type": "Tipo", - "action-type-required": "Tipo azione obbligatorio.", - "edit-action": "Modifica azione", - "delete-action": "Cancella azione", - "delete-action-title": "Cancella azione del widget", - "delete-action-text": "Sei sicuro di voler cancellare l'azione del widget '{{actionName}}'?", - "display-icon": "Mostra icona titolo", - "icon-color": "Colore dell'icona", - "icon-size": "Dimensione dell'icona" - }, - "widget-type": { - "import": "Importa un tipo di widget", - "export": "Esporta un tipo di widget", - "export-failed-error": "Impossibile esportare il tipo di widget: {{error}}", - "create-new-widget-type": "Crea un nuovo tipo di widget", - "widget-type-file": "File tipo di widget", - "invalid-widget-type-file-error": "Impossibile importare un tipo di widget: struttura dati del widget non valida." - }, - "widgets": { - "date-range-navigator": { - "localizationMap": { - "Sun": "Dom", - "Mon": "Lun", - "Tue": "Mar", - "Wed": "Mer", - "Thu": "Gio", - "Fri": "Ven", - "Sat": "Sab", - "Jan": "Gen", - "Feb": "Feb", - "Mar": "Mar", - "Apr": "Apr", - "May": "Mag", - "Jun": "Giu", - "Jul": "Lug", - "Aug": "Ago", - "Sep": "Set", - "Oct": "Ott", - "Nov": "Nov", - "Dec": "Dic", - "January": "Gennaio", - "February": "Febbraio", - "March": "Marzo", - "April": "Aprile", - "June": "Giugno", - "July": "Luglio", - "August": "Agosto", - "September": "Settembre", - "October": "Ottobre", - "November": "Novembre", - "December": "Dicembre", - "Custom Date Range": "Intervallo date personalizzato", - "Date Range Template": "Modello intervallo date", - "Today": "Oggi", - "Yesterday": "Ieri", - "This Week": "Questa settimana", - "Last Week": "La settimana scorsa", - "This Month": "Questo mese", - "Last Month": "Lo scorso mese", - "Year": "Anno", - "This Year": "Quest'anno", - "Last Year": "L'anno scorso", - "Date picker": "Date picker", - "Hour": "Ora", - "Day": "Giorno", - "Week": "Settimana", - "2 weeks": "2 Settimane", - "Month": "Mese", - "3 months": "3 Mesi", - "6 months": "6 Mesi", - "Custom interval": "Intervallo personalizzato", - "Interval": "Intervallo", - "Step size": "Dimensione del passo", - "Ok": "Ok" - } + "points" : { + "points" : "Punti", + "point-tooltip" : "Suggerimento punto" }, - "input-widgets": { - "attribute-not-allowed": "Questo widget non può usare un parametro di tipo attributo", - "date": "Data", - "discard-changes": "Annulla modifiche", - "entity-attribute-required": "E' richiesta un'entità di tipo attributo", - "entity-timeseries-required": "E' richiesta un'entità di tipo serie temporale", - "not-allowed-entity": "L'entità selezionata non può avere attributi condivisi", - "no-attribute-selected": "Nessun attributo selezionato", - "no-datakey-selected": "Nessuna datakey selezionata", - "no-entity-selected": "Nessuna entità selezionata", - "no-image": "Nessuna immagine", - "no-support-web-camera": "Web camera non supportata", - "no-timeseries-selected": "Nessuna serie temporale selezionata", - "switch-attribute-value": "Cambia il valore dell'attributo", - "switch-camera": "Cambia camera", - "switch-timeseries-value": "Cambia il valore della serie temporale", - "take-photo": "Fai una foto", - "time": "Tempo", - "timeseries-not-allowed": "Questo widget non può usare un parametro di tipo serie temporale", - "update-failed": "Aggiornamento fallito", - "update-successful": "Aggiornamento eseguito con successo", - "update-attribute": "Aggiorna attributo", - "update-timeseries": "Aggiorna serie temporale", - "value": "Valore" - } + "shape" : { + "fill" : "Riempimento", + "fill-type-color" : "Colore", + "fill-type-stripe" : "Striscia", + "fill-type-image" : "Immagine", + "color" : "Colore", + "stripe" : "Striscia", + "image" : "Immagine", + "stroke" : "Contorno", + "fill-image" : "Immagine di riempimento", + "fill-image-type-image" : "Immagine", + "fill-image-type-function" : "Funzione", + "preserve-aspect-ratio" : "Preserva proporzioni", + "opacity" : "Opacità", + "angle" : "Angolo di rotazione", + "scale" : "Scala", + "fill-image-function" : "Funzione immagine di riempimento forma", + "fill-images" : "Immagini di riempimento forma", + "stripe-pattern" : "Motivo a strisce", + "first-stripe" : "Prima striscia", + "second-stripe" : "Seconda striscia" + }, + "polygon" : { + "polygon-key" : "Chiave poligono", + "polygon-key-required" : "Chiave poligono obbligatoria", + "no-polygons" : "Nessun poligono configurato", + "add-polygon" : "Aggiungi poligono", + "polygon-configuration" : "Configurazione poligono", + "remove-polygon" : "Rimuovi poligono", + "edit" : "Modifica poligono", + "remove-polygon-for" : "Rimuovi poligono per '{{entityName}}'", + "cut" : "Taglia area del poligono", + "rotate" : "Ruota poligono", + "draw-rectangle" : "Disegna rettangolo", + "draw-polygon" : "Disegna poligono", + "polygon-place-first-point-cut-hint" : "Clicca per posizionare il primo punto", + "continue-polygon-cut-hint" : "Clicca per continuare il disegno", + "finish-polygon-cut-hint" : "Clicca sul primo marcatore per terminare e salvare", + "polygon-place-first-point-hint" : "Poligono: clicca per posizionare il primo punto", + "polygon-place-first-point-hint-with-entity" : "Poligono per '{{entityName}}': clicca per posizionare il primo punto", + "continue-polygon-hint" : "Poligono: clicca per continuare il disegno", + "continue-polygon-hint-with-entity" : "Poligono per '{{entityName}}': clicca per continuare il disegno", + "finish-polygon-hint" : "Poligono: clicca sul primo marcatore per terminare il disegno", + "finish-polygon-hint-with-entity" : "Poligono per '{{entityName}}': clicca sul primo marcatore per terminare e salvare", + "rectangle-place-first-point-hint" : "Rettangolo: clicca per posizionare il primo punto", + "rectangle-place-first-point-hint-with-entity" : "Rettangolo per '{{entityName}}': clicca per posizionare il primo punto", + "finish-rectangle-hint" : "Rettangolo: clicca per terminare il disegno", + "finish-rectangle-hint-with-entity" : "Rettangolo per '{{entityName}}': clicca per terminare e salvare" + }, + "circle" : { + "circle-key" : "Chiave cerchio", + "circle-key-required" : "Chiave cerchio obbligatoria", + "no-circles" : "Nessun cerchio configurato", + "add-circle" : "Aggiungi cerchio", + "circle-configuration" : "Configurazione cerchio", + "remove-circle" : "Rimuovi cerchio", + "edit" : "Modifica cerchio", + "remove-circle-for" : "Rimuovi cerchio per '{{entityName}}'", + "draw-circle" : "Disegna cerchio", + "place-circle-center-hint-with-entity" : "Cerchio per '{{entityName}}': clicca per posizionare il centro", + "place-circle-center-hint" : "Cerchio: clicca per posizionare il centro", + "finish-circle-hint-with-entity" : "Cerchio per '{{entityName}}': clicca per terminare e salvare", + "finish-circle-hint" : "Cerchio: clicca per terminare il disegno" + }, + "select-entity" : "Seleziona entità", + "select-entity-hint" : "Suggerimento: dopo la selezione, clicca sulla mappa per impostare la posizione" + }, + "select-entity" : "Seleziona entità", + "select-entity-hint" : "Suggerimento: dopo la selezione clicca sulla mappa per impostare la posizione", + "tooltips" : { + "placeMarker" : "Clicca per posizionare l'entità '{{entityName}}'", + "firstVertex" : "Poligono per '{{entityName}}': clicca per posizionare il primo punto", + "firstVertex-cut" : "Clicca per posizionare il primo punto", + "continueLine" : "Poligono per '{{entityName}}': clicca per continuare il disegno", + "continueLine-cut" : "Clicca per continuare il disegno", + "finishLine" : "Clicca su un marcatore esistente per terminare", + "finishPoly" : "Poligono per '{{entityName}}': clicca sul primo marcatore per terminare e salvare", + "finishPoly-cut" : "Clicca sul primo marcatore per terminare e salvare", + "finishRect" : "Poligono per '{{entityName}}': clicca per terminare e salvare", + "startCircle" : "Cerchio per '{{entityName}}': clicca per posizionare il centro", + "finishCircle" : "Cerchio per '{{entityName}}': clicca per terminare il cerchio", + "placeCircleMarker" : "Clicca per posizionare il marcatore del cerchio" + }, + "actions" : { + "finish" : "Termina", + "cancel" : "Annulla", + "removeLastVertex" : "Rimuovi ultimo punto" + }, + "buttonTitles" : { + "drawMarkerButton" : "Posiziona entità", + "drawPolyButton" : "Crea poligono", + "drawLineButton" : "Crea polilinea", + "drawCircleButton" : "Crea cerchio", + "drawRectButton" : "Crea rettangolo", + "editButton" : "Modalità modifica", + "dragButton" : "Modalità trascina e rilascia", + "cutButton" : "Taglia area del poligono", + "deleteButton" : "Rimuovi", + "drawCircleMarkerButton" : "Crea marcatore del cerchio", + "rotateButton" : "Ruota poligono" + }, + "map-provider-settings" : "Impostazioni provider mappa", + "map-provider" : "Provider mappa", + "map-provider-google" : "Google Maps", + "map-provider-openstreet" : "OpenStreet Maps", + "map-provider-here" : "HERE Maps", + "map-provider-image" : "Mappa immagine", + "map-provider-tencent" : "Tencent Maps", + "openstreet-provider" : "Provider mappa OpenStreet", + "openstreet-provider-mapnik" : "OpenStreetMap.Mapnik (Predefinito)", + "openstreet-provider-hot" : "OpenStreetMap.HOT", + "openstreet-provider-esri-street" : "Esri.WorldStreetMap", + "openstreet-provider-esri-topo" : "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery" : "Esri.WorldImagery", + "openstreet-provider-cartodb-positron" : "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter" : "CartoDB.DarkMatter", + "use-custom-provider" : "Usa provider personalizzato", + "custom-provider-tile-url" : "URL tile del provider personalizzato", + "google-maps-api-key" : "Chiave API Google Maps", + "default-map-type" : "Tipo di mappa predefinito", + "google-map-type-roadmap" : "Stradale", + "google-map-type-satelite" : "Satellitare", + "google-map-type-hybrid" : "Ibrido", + "google-map-type-terrain" : "Terreno", + "map-layer" : "Layer mappa", + "here-map-normal-day" : "HERE.normalDay (Predefinito)", + "here-map-normal-night" : "HERE.normalNight", + "here-map-hybrid-day" : "HERE.hybridDay", + "here-map-terrain-day" : "HERE.terrainDay", + "credentials" : "Credenziali", + "here-app-id" : "ID app HERE", + "here-app-code" : "Codice app HERE", + "here-api-key" : "Chiave API HERE", + "here-use-new-version-api-3" : "Usa API versione 3", + "tencent-maps-api-key" : "Chiave API Tencent Maps", + "tencent-map-type-roadmap" : "Stradale", + "tencent-map-type-satelite" : "Satellitare", + "tencent-map-type-hybrid" : "Ibrido", + "image-map-background" : "Sfondo mappa immagine", + "image-map-background-from-entity-attribute" : "Prendi sfondo mappa immagine da attributo entità", + "image-url-source-entity-alias" : "Alias entità sorgente URL immagine", + "image-url-source-entity-attribute" : "Attributo entità sorgente URL immagine", + "common-map-settings" : "Impostazioni comuni mappa", + "x-pos-key-name" : "Nome chiave posizione X", + "y-pos-key-name" : "Nome chiave posizione Y", + "latitude-key-name" : "Nome chiave latitudine", + "longitude-key-name" : "Nome chiave longitudine", + "default-map-zoom-level" : "Livello di zoom mappa predefinito (0 - 20)", + "default-map-center-position" : "Posizione centrale mappa predefinita (0,0)", + "disable-scroll-zooming" : "Disabilita zoom con rotellina", + "disable-double-click-zooming" : "Disabilita zoom con doppio clic", + "disable-zoom-control-buttons" : "Disabilita pulsanti di controllo zoom", + "fit-map-bounds" : "Adatta i limiti della mappa per coprire tutti i marker", + "use-default-map-center-position" : "Usa posizione centrale mappa predefinita", + "entities-limit" : "Limite entità da caricare", + "markers-settings" : "Impostazioni marker", + "marker-offset-x" : "Offset X marker relativo alla posizione moltiplicato per la larghezza marker", + "marker-offset-y" : "Offset Y marker relativo alla posizione moltiplicato per l'altezza marker", + "position-function" : "Funzione di conversione posizione, deve restituire coordinate x,y come double da 0 a 1 ciascuna", + "draggable-marker" : "Marker trascinabile", + "label" : "Etichetta", + "show-label" : "Mostra etichetta", + "use-label-function" : "Usa funzione etichetta", + "label-pattern" : "Etichetta (esempi di pattern: '${entityName}', '${entityName}: (Testo ${keyName} unità.)' )", + "label-function" : "Funzione etichetta", + "tooltip" : "Tooltip", + "show-tooltip" : "Mostra tooltip", + "show-tooltip-action" : "Azione per mostrare il tooltip", + "show-tooltip-action-click" : "Mostra tooltip al clic (Predefinito)", + "show-tooltip-action-hover" : "Mostra tooltip al passaggio del mouse", + "auto-close-tooltips" : "Chiudi automaticamente i tooltip", + "use-tooltip-function" : "Usa funzione tooltip", + "tooltip-pattern" : "Tooltip (es.: 'Testo ${keyName} unità.' o Testo link)", + "tooltip-function" : "Funzione tooltip", + "tooltip-offset-x" : "Offset X tooltip relativo all'ancora del marker moltiplicato per la larghezza marker", + "tooltip-offset-y" : "Offset Y tooltip relativo all'ancora del marker moltiplicato per l'altezza marker", + "color" : "Colore", + "use-color-function" : "Usa funzione colore", + "color-function" : "Funzione colore", + "marker-image" : "Immagine marker", + "use-marker-image-function" : "Usa funzione immagine marker", + "custom-marker-image" : "Immagine marker personalizzata", + "custom-marker-image-size" : "Dimensione immagine marker personalizzata (px)", + "marker-image-function" : "Funzione immagine marker", + "marker-images" : "Immagini marker", + "polygon-settings" : "Impostazioni poligono", + "show-polygon" : "Mostra poligono", + "polygon-key-name" : "Nome chiave poligono", + "enable-polygon-edit" : "Abilita modifica poligono", + "polygon-label" : "Etichetta poligono", + "show-polygon-label" : "Mostra etichetta poligono", + "use-polygon-label-function" : "Usa funzione etichetta poligono", + "polygon-label-pattern" : "Etichetta poligono (esempi di pattern: '${entityName}', '${entityName}: (Testo ${keyName} unità.)')", + "polygon-label-function" : "Funzione etichetta poligono", + "polygon-tooltip" : "Tooltip poligono", + "show-polygon-tooltip" : "Mostra tooltip poligono", + "auto-close-polygon-tooltips" : "Chiudi automaticamente tooltip poligono", + "use-polygon-tooltip-function" : "Usa funzione tooltip poligono", + "polygon-tooltip-pattern" : "Tooltip (es.: 'Testo ${keyName} unità.' o Testo link)", + "polygon-tooltip-function" : "Funzione tooltip poligono", + "polygon-color" : "Colore poligono", + "polygon-opacity" : "Opacità poligono", + "use-polygon-color-function" : "Usa funzione colore poligono", + "polygon-color-function" : "Funzione colore poligono", + "polygon-stroke" : "Bordo poligono", + "stroke-color" : "Colore bordo", + "stroke-opacity" : "Opacità bordo", + "stroke-weight" : "Spessore bordo", + "use-polygon-stroke-color-function" : "Usa funzione colore bordo poligono", + "polygon-stroke-color-function" : "Funzione colore bordo poligono", + "circle-settings" : "Impostazioni cerchio", + "show-circle" : "Mostra cerchio", + "circle-key-name" : "Nome chiave cerchio", + "enable-circle-edit" : "Abilita modifica cerchio", + "circle-label" : "Etichetta cerchio", + "show-circle-label" : "Mostra etichetta cerchio", + "use-circle-label-function" : "Usa funzione etichetta cerchio", + "circle-label-pattern" : "Etichetta cerchio (esempi di pattern: '${entityName}', '${entityName}: (Testo ${keyName} unità.)')", + "circle-label-function" : "Funzione etichetta cerchio", + "circle-tooltip" : "Tooltip cerchio", + "show-circle-tooltip" : "Mostra tooltip cerchio", + "auto-close-circle-tooltips" : "Chiudi automaticamente tooltip cerchio", + "use-circle-tooltip-function" : "Usa funzione tooltip cerchio", + "circle-tooltip-pattern" : "Tooltip (es.: 'Testo ${keyName} unità.' o Testo link)", + "circle-tooltip-function" : "Funzione tooltip cerchio", + "circle-fill-color" : "Colore riempimento cerchio", + "circle-fill-color-opacity" : "Opacità riempimento cerchio", + "use-circle-fill-color-function" : "Usa funzione colore riempimento cerchio", + "circle-fill-color-function" : "Funzione colore riempimento cerchio", + "circle-stroke" : "Bordo cerchio", + "use-circle-stroke-color-function" : "Usa funzione colore bordo cerchio", + "circle-stroke-color-function" : "Funzione colore bordo cerchio", + "markers-clustering-settings" : "Impostazioni clustering marker", + "use-map-markers-clustering" : "Usa clustering marker mappa", + "zoom-on-cluster-click" : "Zoom al clic su un cluster", + "max-cluster-zoom" : "Livello di zoom massimo per includere marker in un cluster (0 - 18)", + "max-cluster-radius-pixels" : "Raggio massimo coperto da un cluster in pixel", + "cluster-zoom-animation" : "Mostra animazione su marker durante zoom", + "show-markers-bounds-on-cluster-mouse-over" : "Mostra limiti marker al passaggio su cluster", + "spiderfy-max-zoom-level" : "Espandi cluster al massimo livello di zoom", + "load-optimization" : "Ottimizzazione caricamento", + "cluster-chunked-loading" : "Usa caricamento a blocchi per evitare blocchi della pagina", + "cluster-markers-lazy-load" : "Usa caricamento ritardato per i marker", + "editor-settings" : "Impostazioni editor", + "enable-snapping" : "Abilita allineamento ai vertici per disegno preciso", + "init-draggable-mode" : "Inizializza mappa in modalità trascinamento", + "hide-all-edit-buttons" : "Nascondi tutti i pulsanti di modifica", + "hide-draw-buttons" : "Nascondi pulsanti disegno", + "hide-edit-buttons" : "Nascondi pulsanti modifica", + "hide-remove-button" : "Nascondi pulsante rimozione", + "route-map-settings" : "Impostazioni mappa percorso", + "trip-animation-settings" : "Impostazioni animazione percorso", + "normalization-step" : "Passo di normalizzazione dati (ms)", + "tooltip-background-color" : "Colore sfondo tooltip", + "tooltip-font-color" : "Colore font tooltip", + "tooltip-opacity" : "Opacità tooltip (0-1)", + "auto-close-tooltip" : "Chiudi automaticamente tooltip", + "rotation-angle" : "Imposta angolo di rotazione aggiuntivo marker (gradi)", + "path-settings" : "Impostazioni percorso", + "path-color" : "Colore percorso", + "use-path-color-function" : "Usa funzione colore percorso", + "path-color-function" : "Funzione colore percorso", + "path-decorator" : "Decoratore percorso", + "use-path-decorator" : "Usa decoratore percorso", + "decorator-symbol" : "Simbolo decoratore", + "decorator-symbol-arrow-head" : "Freccia", + "decorator-symbol-dash" : "Tratto", + "decorator-symbol-size" : "Dimensione simbolo decoratore (px)", + "use-path-decorator-custom-color" : "Usa colore personalizzato decoratore", + "decorator-custom-color" : "Colore personalizzato decoratore", + "decorator-offset" : "Offset decoratore", + "end-decorator-offset" : "Offset fine decoratore", + "decorator-repeat" : "Ripetizione decoratore", + "points-settings" : "Impostazioni punti", + "show-points" : "Mostra punti", + "point-color" : "Colore punto", + "point-size" : "Dimensione punto (px)", + "use-point-color-function" : "Usa funzione colore punto", + "point-color-function" : "Funzione colore punto", + "use-point-as-anchor" : "Usa punto come ancoraggio", + "point-as-anchor-function" : "Funzione ancoraggio punto", + "independent-point-tooltip" : "Tooltip punto indipendente", + "clustering-markers" : "Clustering marker", + "use-icon-create-function" : "Usa funzione di creazione icona", + "marker-color-function" : "Funzione colore marker" + }, + "markdown" : { + "use-markdown-text-function" : "Usa funzione per valore markdown/HTML", + "markdown-text-function" : "Funzione valore markdown/HTML", + "markdown-text-pattern" : "Pattern markdown/HTML (markdown o HTML con variabili, ad es. '${entityName} o ${keyName} - del testo.')", + "apply-default-markdown-style" : "Applica stile markdown predefinito", + "markdown-css" : "CSS per markdown/HTML" + }, + "simple-card" : { + "label" : "Etichetta", + "label-position" : "Posizione etichetta", + "label-position-left" : "Sinistra", + "label-position-top" : "In alto" + }, + "single-switch" : { + "behavior" : "Comportamento", + "layout" : "Layout", + "layout-right" : "Destra", + "layout-left" : "Sinistra", + "layout-centered" : "Centrato", + "auto-scale" : "Auto scala", + "label" : "Etichetta", + "icon" : "Icona", + "switch-color" : "Colore interruttore", + "on" : "Acceso", + "off" : "Spento", + "disabled" : "Disabilitato", + "tumbler-color" : "Colore del commutatore", + "on-label" : "Etichetta acceso", + "off-label" : "Etichetta spento", + "switch" : "Interruttore" + }, + "slider" : { + "behavior" : "Comportamento", + "initial-value" : "Valore iniziale", + "initial-value-hint" : "Azione per ottenere il valore iniziale del cursore.", + "on-value-change" : "Al cambio valore", + "on-value-change-hint" : "Azione eseguita quando il valore del cursore cambia.", + "layout" : "Layout", + "layout-default" : "Predefinito", + "layout-extended" : "Esteso", + "layout-simplified" : "Semplificato", + "auto-scale" : "Auto scala", + "icon" : "Icona", + "value" : "Valore", + "range" : "Intervallo", + "min" : "min", + "max" : "max", + "range-ticks" : "Indicatori intervallo", + "tick-marks" : "Tacche", + "colors" : "Colori", + "main" : "Principale", + "background" : "Sfondo", + "left-icon" : "Icona sinistra", + "right-icon" : "Icona destra", + "slider" : "Cursore" + }, + "value-card" : { + "layout" : "Layout", + "layout-square" : "Quadrato", + "layout-vertical" : "Verticale", + "layout-centered" : "Centrato", + "layout-simplified" : "Semplificato", + "layout-horizontal" : "Orizzontale", + "layout-horizontal-reversed" : "Orizzontale inverso", + "label" : "Etichetta", + "icon" : "Icona", + "value" : "Valore", + "date" : "Data", + "value-card-style" : "Stile scheda valore", + "auto-scale" : "Auto scala" + }, + "label-card" : { + "auto-scale" : "Auto scala", + "label" : "Etichetta", + "icon" : "Icona", + "label-card-style" : "Stile scheda etichetta" + }, + "label-value-card" : { + "value" : "Valore", + "label-value-card-style" : "Stile scheda etichetta & valore" + }, + "liquid-level-card" : { + "layout-simple" : "Semplice", + "layout-percentage" : "Percentuale", + "layout-absolute" : "Assoluto", + "layout" : "Layout", + "background-overlay" : "Sovrapposizione dello sfondo del valore", + "total-volume" : "Volume totale", + "total-volume-units" : "Unità volume totale", + "tank" : "Serbatoio", + "shape" : "Forma", + "datasource-units" : "Unità sorgente", + "widget-units" : "Unità widget", + "decimals" : "Decimali", + "liquid" : "Liquido", + "liquid-color" : "Colore del liquido", + "value" : "Valore", + "value-font" : "Font del valore", + "level" : "Livello", + "last-update" : "Ultimo aggiornamento", + "shape-by-attribute" : "Imposta forma del serbatoio per nome attributo", + "tooltip-background" : "Colore dello sfondo", + "background-blur" : "Sfocatura dello sfondo", + "tank-color" : "Colore del serbatoio", + "static" : "Statico", + "see-examples" : "Vedi esempi", + "attribute" : "Attributo", + "shape-type" : "Tipo", + "v-oval" : "Ovale verticale", + "v-cylinder" : "Cilindro verticale", + "v-capsule" : "Capsula verticale", + "rectangle" : "Rettangolo", + "h-oval" : "Ovale orizzontale", + "h-ellipse" : "Ellisse orizzontale", + "h-dish-ends" : "Estremità a piatto orizzontale", + "h-cylinder" : "Cilindro orizzontale", + "h-capsule" : "Capsula orizzontale", + "h-elliptical_2_1" : "Ellittica orizzontale 2:1", + "icon" : "Icona della scheda", + "title" : "Titolo della scheda", + "units" : "Unità", + "color-and-font" : "Colore e carattere", + "shape-attribute-name" : "Nome attributo", + "total-volume-required" : "Il volume totale è obbligatorio.", + "attribute-name-required" : "Il nome dell'attributo è obbligatorio.", + "attribute-key-not-set" : "Chiave dell'attributo '{{attributeName}}' non impostata", + "attribute-key-invalid" : "Chiave dell'attributo '{{attributeName}}' non valida" + }, + "aggregated-value-card" : { + "subtitle" : "Sottotitolo", + "chart" : "Grafico", + "values" : "Valori", + "value-appearance" : "Aspetto del valore", + "position" : "Posizione", + "position-center" : "Centro", + "position-right-top" : "Alto a destra", + "position-right-bottom" : "Basso a destra", + "position-left-top" : "Alto a sinistra", + "position-left-bottom" : "Basso a sinistra", + "font" : "Carattere", + "color" : "Colore", + "arrow" : "Freccia", + "display-up-down-arrow" : "Mostra freccia Su/Giù", + "add-value" : "Aggiungi valore", + "remove-value" : "Rimuovi valore", + "no-values" : "Nessun valore configurato", + "aggregation" : "Aggregazione", + "aggregated-value-card-style" : "Stile scheda valore aggregato", + "auto-scale" : "Auto scala" + }, + "value-chart-card" : { + "layout" : "Layout", + "layout-left" : "Sinistra", + "layout-right" : "Destra", + "auto-scale" : "Auto scala", + "icon" : "Icona", + "value" : "Valore", + "chart" : "Grafico", + "value-chart-card-style" : "Stile scheda valore con grafico" + }, + "progress-bar" : { + "layout" : "Layout", + "layout-default" : "Predefinito", + "layout-simplified" : "Semplificato", + "auto-scale" : "Auto scala", + "icon" : "Icona", + "value" : "Valore", + "range" : "Intervallo", + "min" : "min", + "max" : "max", + "range-ticks" : "Intervallo tick", + "bar" : "Barra", + "bar-color" : "Colore della barra", + "bar-background" : "Sfondo della barra", + "progress-bar-card-style" : "Stile scheda barra di avanzamento" + }, + "notification" : { + "max-notification-display" : "Numero massimo di notifiche da visualizzare", + "counter" : "Contatore", + "counter-hint" : "Il contatore verrà mostrato se è abilitato il \"Titolo del widget\"", + "icon" : "Icona", + "counter-value" : "Valore", + "counter-color" : "Colore", + "notification-button" : "Pulsanti di notifica", + "button-view-all" : "Visualizza tutto", + "button-filter" : "Filtro", + "type-filter" : "Filtro per tipo", + "button-mark-read" : "Segna tutto come letto", + "notification-types" : "Tipi di notifica", + "notification-type" : "Tipo di notifica", + "search-type" : "Tipo di ricerca", + "any-type" : "Qualsiasi tipo" + }, + "alarm-count" : { + "alarm-count-card-style" : "Stile scheda conteggio allarmi" }, - "icon": { - "icon": "Icona", - "select-icon": "Seleziona icona", - "material-icons": "Icone Material", - "show-all": "Mostra tutte le icone" - }, - "custom": { - "widget-action": { - "action-cell-button": "Pulsante azione cella", - "row-click": "Click sulla riga", - "polygon-click": "Click sul poligono", - "marker-click": "Click sul marker", - "tooltip-tag-action": "Azione tooltip", - "node-selected": "Click su nodo selezionato", - "element-click": "Click su elemento HTML", - "pie-slice-click": "Click sulla fetta", - "row-double-click": "Doppio click sulla riga" + "entity-count" : { + "entity-count-card-style" : "Stile scheda conteggio entità" + }, + "count" : { + "layout" : "Layout", + "layout-column" : "Colonna", + "layout-row" : "Riga", + "label" : "Etichetta", + "icon" : "Icona", + "icon-background" : "Sfondo icona", + "value" : "Valore", + "chevron" : "Chevron", + "auto-scale" : "Auto scala" + }, + "table" : { + "common-table-settings" : "Impostazioni comuni della tabella", + "enable-search" : "Abilita la ricerca", + "enable-sticky-header" : "Visualizza sempre l'intestazione", + "enable-sticky-action" : "Visualizza sempre la colonna delle azioni", + "hidden-cell-button-display-mode" : "Modalità di visualizzazione dei pulsanti nascosti nella cella", + "show-empty-space-hidden-action" : "Mostra spazio vuoto al posto del pulsante di azione nascosto", + "dont-reserve-space-hidden-action" : "Non riservare spazio per i pulsanti di azione nascosti", + "display-timestamp" : "Timestamp", + "display-pagination" : "Mostra paginazione", + "default-page-size" : "Dimensione pagina predefinita", + "page-step-settings" : "Impostazioni passo di pagina", + "page-step-count" : "Numero di passi", + "page-step-increment" : "Incremento del passo", + "page-step-count-format-message" : "Deve essere un valore intero, nell'intervallo da 1 a 100.", + "page-step-increment-format-message" : "Deve essere un valore intero, maggiore o uguale a 1.", + "use-entity-label-tab-name" : "Usa l'etichetta dell'entità nel nome della scheda", + "hide-empty-lines" : "Nascondi righe vuote", + "row-style" : "Stile riga", + "use-row-style-function" : "Usa funzione per stile riga", + "row-style-function" : "Funzione per stile riga", + "cell-style" : "Stile cella", + "use-cell-style-function" : "Usa funzione per stile cella", + "cell-style-function" : "Funzione per stile cella", + "cell-content" : "Contenuto cella", + "use-cell-content-function" : "Usa funzione per contenuto cella", + "cell-content-function" : "Funzione per contenuto cella", + "show-latest-data-column" : "Mostra colonna dati più recenti", + "latest-data-column-order" : "Ordine colonna dati più recenti", + "entities-table-title" : "Titolo tabella entità", + "enable-select-column-display" : "Abilita selezione colonne da visualizzare", + "display-entity-name" : "Mostra colonna nome entità", + "entity-name-column-title" : "Titolo colonna nome entità", + "display-entity-label" : "Mostra colonna etichetta entità", + "entity-label-column-title" : "Titolo colonna etichetta entità", + "display-entity-type" : "Mostra colonna tipo entità", + "default-sort-order" : "Ordine di ordinamento predefinito", + "custom-title" : "Titolo intestazione personalizzato", + "column-width" : "Larghezza colonna (px o %)", + "default-column-visibility" : "Visibilità predefinita colonna", + "column-visibility-visible" : "Visibile", + "column-visibility-hidden" : "Nascosta", + "column-visibility-hidden-mobile" : "Nascosta in modalità mobile", + "column-selection-to-display" : "Selezione colonna in 'Colonne da visualizzare'", + "column-selection-to-display-enabled" : "Abilitata", + "column-selection-to-display-disabled" : "Disabilitata", + "alarms-table-title" : "Titolo tabella allarmi", + "enable-alarms-selection" : "Abilita selezione allarmi", + "enable-alarms-search" : "Abilita ricerca allarmi", + "enable-alarm-filter" : "Abilita filtro allarmi", + "display-alarm-details" : "Mostra dettagli allarmi", + "allow-alarms-ack" : "Consenti conferma allarmi", + "allow-alarms-clear" : "Consenti eliminazione allarmi", + "display-alarm-activity" : "Mostra attività degli allarmi", + "allow-alarms-assign" : "Consenti assegnazione allarmi", + "columns" : "Colonne", + "column-settings" : "Impostazioni colonna", + "remove-column" : "Rimuovi colonna", + "add-column" : "Aggiungi colonna", + "no-columns" : "Nessuna colonna configurata", + "columns-to-display" : "Colonne da visualizzare", + "table-header" : "Intestazione tabella", + "header-buttons" : "Pulsanti intestazione", + "table-buttons" : "Pulsanti tabella", + "pagination" : "Paginazione", + "rows" : "Righe", + "timeseries-column-error" : "Deve essere specificata almeno una colonna time series", + "alarm-column-error" : "Deve essere specificata almeno una colonna allarmi", + "table-tabs" : "Schede della tabella", + "show-cell-actions-menu-mobile" : "Mostra menu azioni cella in modalità mobile", + "disable-sorting" : "Disabilita ordinamento" + }, + "latest-chart" : { + "total" : "Totale", + "auto-scale" : "Auto scala", + "clockwise-layout" : "Disposizione oraria", + "sort-series" : "Ordina serie per etichetta", + "tooltip-value-type-absolute" : "Assoluto", + "tooltip-value-type-percentage" : "Percentuale" + }, + "pie-chart" : { + "pie-chart-appearance" : "Aspetto grafico a torta", + "label" : "Etichetta", + "border" : "Bordo", + "radius" : "Raggio", + "pie-chart-card-style" : "Stile scheda grafico a torta" + }, + "radar-chart" : { + "radar-appearance" : "Aspetto radar", + "shape" : "Forma", + "shape-polygon" : "Poligono", + "shape-circle" : "Cerchio", + "color" : "Colore", + "line" : "Linea", + "points" : "Punti", + "points-label" : "Etichetta punti", + "radar-axis" : "Asse radar", + "axis-label" : "Etichetta asse", + "ticks-label" : "Etichetta tacche", + "radar-chart-style" : "Stile grafico radar" + }, + "time-series-chart" : { + "chart" : "Grafico", + "chart-style" : "Stile grafico", + "data-zoom" : "Zoom dati", + "stack-mode" : "Modalità impilamento", + "stack-mode-hint" : "Impila le serie nel grafico. Le serie con la stessa unità verranno sovrapposte.", + "axes" : "Assi", + "y-axes" : "Assi Y", + "line-type" : "Tipo linea", + "line-width" : "Spessore linea", + "type-line" : "Linea", + "type-bar" : "Barra", + "type-point" : "Punto", + "no-aggregation-bar-width-strategy" : "Strategia larghezza barra per dati non aggregati", + "no-aggregation-bar-width-strategy-group" : "Gruppo", + "no-aggregation-bar-width-strategy-separate" : "Separato", + "bar-group-width" : "Larghezza gruppo barre", + "bar-width" : "Larghezza barra", + "bar-width-relative" : "Percentuale della finestra temporale", + "bar-width-absolute" : "Assoluto (ms)", + "comparison" : { + "comparison" : "Confronto", + "comparison-hint" : "Il confronto funziona solo con dati storici!", + "show" : "Mostra", + "settings" : "Impostazioni confronto", + "show-values-for-comparison" : "Mostra dati storici per il confronto", + "comparison-values-label" : "Etichetta chiave di confronto", + "comparison-values-label-auto" : "Auto", + "comparison-data-color" : "Colore dati confronto" + }, + "threshold" : { + "thresholds" : "Soglie", + "source" : "Fonte", + "key-value" : "Chiave / Valore", + "no-thresholds" : "Nessuna soglia configurata", + "add-threshold" : "Aggiungi soglia", + "type-constant" : "Costante", + "type-latest-key" : "Chiave", + "type-entity" : "Entità", + "threshold-settings" : "Impostazioni soglia", + "remove-threshold" : "Rimuovi soglia", + "threshold-value-required" : "Valore soglia richiesto.", + "key-required" : "Chiave richiesta.", + "entity-key-required" : "Chiave entità richiesta.", + "line-appearance" : "Aspetto linea", + "line-color" : "Colore linea", + "start-symbol" : "Simbolo inizio", + "end-symbol" : "Simbolo fine", + "symbol-size" : "dimensione", + "label" : "Etichetta", + "label-position-start" : "Inizio", + "label-position-middle" : "Centro", + "label-position-end" : "Fine", + "label-position-inside-start" : "Dentro inizio", + "label-position-inside-start-top" : "Dentro inizio sopra", + "label-position-inside-start-bottom" : "Dentro inizio sotto", + "label-position-inside-middle" : "Dentro centro", + "label-position-inside-middle-top" : "Dentro centro sopra", + "label-position-inside-middle-bottom" : "Dentro centro sotto", + "label-position-inside-end" : "Dentro fine", + "label-position-inside-end-top" : "Dentro fine sopra", + "label-position-inside-end-bottom" : "Dentro fine sotto", + "label-background" : "Sfondo etichetta" + }, + "state" : { + "states" : "Stati", + "label" : "Etichetta", + "ticks-value" : "Valore delle tacche", + "source" : "Fonte", + "value-range" : "Valore / Intervallo", + "no-states" : "Nessuno stato configurato", + "add-state" : "Aggiungi stato", + "type-constant" : "Costante", + "type-range" : "Intervallo", + "from" : "Da", + "to" : "A", + "remove-state" : "Rimuovi stato" + }, + "grid" : { + "grid" : "Griglia", + "background-color" : "Colore di sfondo", + "border" : "Bordo" + }, + "axis" : { + "axes" : "Assi", + "x-axis" : "Asse X", + "y-axis" : "Asse Y", + "y-axis-settings" : "Impostazioni asse Y", + "comparison-x-axis-settings" : "Impostazioni asse X per confronto", + "remove-y-axis" : "Rimuovi asse Y", + "id" : "ID", + "label" : "Etichetta", + "position" : "Posizione", + "position-left" : "Sinistra", + "position-right" : "Destra", + "position-top" : "In alto", + "position-bottom" : "In basso", + "tick-labels" : "Etichette delle tacche", + "ticks-formatter-function" : "Funzione di formattazione delle tacche", + "ticks-generator-function" : "Funzione generatore di tacche", + "show-ticks" : "Mostra tacche", + "show-line" : "Mostra linea", + "show-split-lines" : "Mostra linee di divisione", + "show-split-lines-x-axis-hint" : "Se abilitato, verranno mostrate le linee verticali nel grafico.", + "show-split-lines-y-axis-hint" : "Se abilitato, verranno mostrate le linee orizzontali nel grafico.", + "ticks-interval" : "Intervallo delle tacche", + "ticks-interval-hint" : "Imposta forzatamente l'intervallo di segmentazione per l'asse.", + "split-number" : "Numero di divisioni", + "split-number-hint" : "Numero di segmenti in cui l'asse è diviso.", + "min" : "Min", + "max" : "Max", + "show" : "Mostra", + "add-y-axis" : "Aggiungi asse Y" + }, + "series" : { + "legend-settings" : "Impostazioni legenda", + "show-in-legend" : "Mostra nella legenda", + "show-in-legend-hint" : "Mostra nome e dati della serie nella legenda.", + "hidden-by-default" : "Nascosto di default", + "hidden-by-default-hint" : "Rende la serie nascosta nella legenda per impostazione predefinita.", + "series-type" : "Tipo di serie", + "type" : "Tipo", + "type-line" : "Linea", + "type-bar" : "Barra", + "line" : { + "line" : "Linea", + "show-line" : "Mostra linea", + "step-line" : "Linea a scalini", + "step-type-start" : "Inizio", + "step-type-middle" : "Centro", + "step-type-end" : "Fine", + "smooth-line" : "Linea smussata" + }, + "point" : { + "points" : "Punti", + "show-points" : "Mostra punti", + "point-label" : "Etichetta punto", + "point-label-hint" : "Visualizza etichetta con valore sopra il punto della serie.", + "point-label-background" : "Sfondo etichetta punto", + "point-shape" : "Forma del punto", + "point-size" : "Dimensione del punto" } + } + }, + "wind-speed-direction" : { + "layout" : "Layout", + "layout-default" : "Predefinito", + "layout-advanced" : "Avanzato", + "layout-simplified" : "Semplificato", + "values" : "Valori", + "wind-direction" : "Direzione del vento", + "center-value" : "Valore centrale", + "icon" : "Icona", + "arrow" : "Freccia", + "ticks" : "Tacche", + "labels-type" : "Tipo di etichette", + "directional-names" : "Nomi direzionali", + "degrees" : "Gradi", + "major-ticks" : "Tacche principali", + "minor-ticks" : "Tacche minori", + "wind-speed-direction-card-style" : "Stile scheda velocità e direzione del vento", + "ticks-color" : "Colore delle tacche", + "ticks-labels-type" : "Tipo di etichette delle tacche", + "arrow-color" : "Colore della freccia" + }, + "value-source" : { + "value-source" : "Fonte del valore", + "predefined-value" : "Costante", + "entity-attribute" : "Attributo dell'entità", + "value" : "Valore", + "value-required" : "Il valore è obbligatorio.", + "key-required" : "La chiave è obbligatoria.", + "entity-key-required" : "La chiave dell'entità è obbligatoria.", + "source-entity-alias" : "Alias dell'entità sorgente", + "source-entity-attribute" : "Attributo dell'entità sorgente", + "type-constant" : "Costante", + "type-latest-key" : "Chiave", + "type-entity" : "Entità" + }, + "rpc-state" : { + "initial-state" : "Stato iniziale", + "initial-state-hint" : "Azione per ottenere lo stato iniziale (On/Off) del componente.", + "disabled-state" : "Stato disabilitato", + "disabled-state-hint" : "Configura la condizione in cui il componente è disabilitato.", + "turn-on" : "Accendi", + "turn-on-hint" : "Azione attivata quando il cursore viene impostato su 'Acceso'", + "turn-off" : "Spegni", + "turn-off-hint" : "Azione attivata quando il cursore viene impostato su 'Spento'", + "on" : "Acceso", + "off" : "Spento", + "disabled" : "Disabilitato" + }, + "value-action" : { + "do-nothing" : "Non fare nulla", + "execute-rpc" : "Esegui RPC", + "get-attribute" : "Ottieni attributo", + "set-attribute" : "Imposta attributo", + "get-time-series" : "Ottieni serie temporale", + "get-alarm-status" : "Ottieni stato allarme", + "get-dashboard-state" : "Ottieni ID stato dashboard", + "get-dashboard-state-object" : "Ottieni oggetto stato dashboard", + "add-time-series" : "Aggiungi serie temporale", + "execute-rpc-text" : "Esegui metodo RPC '{{methodName}}'", + "get-time-series-text" : "Usa serie temporale '{{key}}'", + "get-attribute-text" : "Usa attributo '{{key}}'", + "get-alarm-status-text" : "Usa stato allarme", + "get-dashboard-state-text" : "Usa stato dashboard", + "get-dashboard-state-object-text" : "Usa oggetto stato dashboard", + "when-dashboard-state-is-text" : "Quando l'ID stato dashboard è '{{state}}'", + "when-dashboard-state-function-is-text" : "Quando f(ID stato dashboard) è '{{state}}'", + "when-dashboard-state-object-function-is-text" : "Quando f(oggetto stato dashboard) è '{{state}}'", + "set-attribute-to-value-text" : "Imposta attributo '{{key}}' su: {{value}}", + "add-time-series-value-text" : "Aggiungi valore alla serie temporale '{{key}}': {{value}}", + "set-attribute-text" : "Imposta attributo '{{key}}'", + "add-time-series-text" : "Aggiungi serie temporale '{{key}}'", + "action" : "Azione", + "value" : "Valore", + "init-value-hint" : "Valore che sarà impostato finché il dispositivo non invia dati.", + "method" : "Metodo", + "method-name-required" : "Il nome del metodo è obbligatorio.", + "request-timeout-ms" : "Timeout richiesta RPC (ms)", + "request-timeout-required" : "Il timeout della richiesta è obbligatorio.", + "min-request-timeout-error" : "Il timeout della richiesta deve essere maggiore o uguale a 5000 ms (5 secondi).", + "request-persistent" : "Richiesta RPC persistente", + "persistent-polling-interval" : "Intervallo di polling persistente (ms)", + "persistent-polling-interval-hint" : "Intervallo di polling (ms) per ottenere la risposta del comando RPC persistente", + "persistent-polling-interval-required" : "Intervallo di polling persistente obbligatorio.", + "min-persistent-polling-interval-error" : "L'intervallo di polling persistente deve essere maggiore o uguale a 1000 ms (1 secondo).", + "attribute-scope" : "Ambito attributo", + "attribute-key" : "Chiave attributo", + "attribute-key-required" : "La chiave dell'attributo è obbligatoria.", + "time-series-key" : "Chiave serie temporale", + "time-series-key-required" : "La chiave della serie temporale è obbligatoria.", + "action-result-converter" : "Convertitore risultato azione", + "converter-none" : "Nessuno", + "converter-function" : "Funzione", + "converter-constant" : "Costante", + "converter-value" : "Valore", + "parse-value-function" : "Funzione di parsing del valore", + "state-when-result-is" : "'{{state}}' quando il risultato è", + "parameters" : "Parametri", + "convert-value-function" : "Funzione di conversione del valore", + "error" : { + "target-entity-is-not-set" : "Entità di destinazione non impostata!", + "failed-to-perform-action" : "Impossibile eseguire l'azione {{ actionLabel }}.", + "invalid-attribute-scope" : "L'ambito dell'attributo {{scope}} non è supportato per l'entità {{entityType}}." + } + }, + "widget-font" : { + "font-settings" : "Impostazioni carattere", + "font-family" : "Famiglia carattere", + "size" : "Dimensione", + "relative-font-size" : "Dimensione relativa del carattere (percentuale)", + "font-style" : "Stile", + "font-style-normal" : "Normale", + "font-style-italic" : "Corsivo", + "font-style-oblique" : "Obliquo", + "font-weight" : "Peso", + "font-weight-normal" : "Normale", + "font-weight-bold" : "Grassetto", + "font-weight-bolder" : "Più grassetto", + "font-weight-lighter" : "Più leggero", + "color" : "Colore", + "shadow-color" : "Colore ombra", + "preview" : "Anteprima", + "line-height" : "Altezza linea", + "auto" : "Auto" + }, + "home" : { + "no-data-available" : "Nessun dato disponibile" + }, + "system-info" : { + "cpu" : "CPU", + "ram" : "RAM", + "disk" : "Disco", + "cpu-warning-text" : "Utilizzo elevato della CPU. Per evitare guasti, ottimizza le prestazioni del sistema.", + "cpu-critical-text" : "Utilizzo critico della CPU. Per evitare guasti, ottimizza le prestazioni del sistema.", + "ram-warning-text" : "Scarsa riserva di RAM. Per evitare guasti, ottimizza le prestazioni del sistema o aumenta la RAM.", + "ram-critical-text" : "Riserva di RAM criticamente bassa. Per evitare guasti, ottimizza le prestazioni del sistema o aumenta la RAM.", + "disk-warning-text" : "Spazio su disco insufficiente. Per evitare perdite di dati, libera o espandi lo spazio su disco.", + "disk-critical-text" : "Spazio su disco criticamente insufficiente. Per evitare perdite di dati, libera o espandi lo spazio su disco." + }, + "cluster-info" : { + "service-id" : "ID servizio", + "service-type" : "Tipo di servizio", + "no-data" : "Nessun dato" + }, + "transport-messages" : { + "title" : "Messaggi di trasporto", + "info" : "Tutti i messaggi provenienti dai dispositivi" + }, + "activity" : { + "title" : "Attività" + }, + "documentation" : { + "title" : "Documentazione", + "add-link" : "Aggiungi link", + "add-link-title" : "Aggiungi link alla documentazione", + "name" : "Nome", + "name-required" : "Il nome è obbligatorio.", + "link" : "Link", + "link-required" : "Il link è obbligatorio.", + "columns" : "Colonne" + }, + "quick-links" : { + "title" : "Link rapidi", + "add-link" : "Aggiungi link", + "add-link-title" : "Aggiungi link rapido", + "quick-link" : "Link rapido", + "quick-link-required" : "Il link rapido è obbligatorio.", + "no-links-matching" : "Nessun link corrispondente a '{{name}}' trovato.", + "columns" : "Colonne" + }, + "recent-dashboards" : { + "title" : "Dashboard", + "last" : "Ultima visualizzazione", + "starred" : "Preferiti", + "name" : "Nome", + "last-viewed" : "Ultima visualizzazione", + "no-last-viewed-dashboards" : "Nessuna dashboard visualizzata di recente" }, - "language": { - "language": "Lingua" + "configured-features" : { + "title" : "Funzionalità configurate", + "info" : "Stato delle funzionalità che richiedono configurazione", + "email-feature" : "Email", + "sms-feature" : "SMS", + "slack-feature" : "Slack", + "oauth2-feature" : "OAuth 2", + "2fa-feature" : "2FA", + "feature-configured" : "Funzionalità configurata.\nClicca per configurare", + "feature-not-configured" : "Funzionalità non configurata.\nClicca per configurare" + }, + "version-info" : { + "title" : "Versione", + "contact-us" : "Contattaci", + "current-version" : "Versione attuale", + "current" : "Attuale", + "available-version" : "Versione disponibile", + "available" : "Disponibile", + "upgrade" : "Aggiorna", + "version-is-up-to-date" : "La versione è aggiornata" + }, + "usage-info" : { + "title" : "Utilizzo", + "entities" : "Entità", + "api-calls" : "Chiamate API" + }, + "functions" : { + "title" : "Funzionalità", + "pe-feature-tooltip" : "Solo su ThingsBoard\nProfessional Edition", + "switch-to-pe" : "Passa alla PE", + "alarms" : "Allarmi", + "dashboards" : "Dashboard", + "entities-and-relations" : "Entità e relazioni", + "profiles" : "Profili", + "advanced-features" : "Funzionalità avanzate", + "notification-center" : "Centro notifiche", + "api-usage" : "Utilizzo API", + "customers" : "Clienti", + "customers-hierarchy" : "Gerarchia clienti", + "roles-and-permissions" : "Ruoli e permessi", + "groups" : "Gruppi", + "integrations" : "Integrazioni", + "solution-templates" : "Template di soluzioni", + "scheduler" : "Pianificatore", + "white-labeling" : "Personalizzazione del marchio" + }, + "devices" : { + "view-docs" : "Vedi documentazione", + "inactive" : "Inattivo", + "active" : "Attivo", + "total" : "Totale" + }, + "alarms" : { + "critical" : "Critico", + "assigned-to-me" : "Assegnato a me", + "total" : "Totale" + }, + "getting-started" : { + "get-started" : "Inizia", + "finish" : "Fine", + "done-welcome-title" : "Benvenuto a bordo", + "done-welcome-text" : "Hai fatto un ottimo lavoro!", + "sys-admin" : { + "step1" : { + "title" : "Crea Tenant e Amministratore Tenant", + "content" : "

Un tenant è un individuo o un'organizzazione che possiede o gestisce dispositivi e asset. Il tenant può avere più utenti amministratori, clienti, dispositivi e asset.

L'Amministratore Tenant può creare e gestire dispositivi, asset, clienti e dashboard all'interno dell'account tenant.

Segui la documentazione per sapere come fare:

", + "how-to-create-tenant" : "Come creare Tenant e Amministratore Tenant" + }, + "step2" : { + "title" : "Configura la funzionalità: Server di posta", + "content" : "

La configurazione del server di posta è essenziale per l'attivazione degli utenti, il recupero password e l'invio delle notifiche di allarme.

Segui la documentazione per sapere come fare:

", + "how-to-configure-mail-server" : "Come configurare il Server di posta" + }, + "step3" : { + "title" : "Configura la funzionalità: Fornitore SMS", + "content" : "

Configura i fornitori SMS per notificare i clienti sugli allarmi tramite SMS.

Segui la documentazione per sapere come fare:

", + "how-to-configure-sms-provider" : "Come configurare il Fornitore SMS" + }, + "step4" : { + "title" : "Configura la funzionalità: Personalizzazione marchio", + "content" : "

Personalizza facilmente il logo e lo schema colori della tua azienda o prodotto senza codifica e senza riavviare il servizio.

Segui la documentazione per sapere come fare:

" + }, + "step5" : { + "title" : "Configura la funzionalità: 2FA", + "content" : "

Migliora la sicurezza degli account sulla piattaforma con l'autenticazione a due fattori.

Segui la documentazione per sapere come fare:

" + }, + "step6" : { + "title" : "Configura la funzionalità: OAuth 2", + "content" : "

Semplifica l'accesso per utenti tenant e clienti con Single Sign-On tramite OAuth 2.0.

Segui la documentazione per sapere come fare:

" + } + }, + "tenant-admin" : { + "step1" : { + "title" : "Crea dispositivo", + "content" : "

Proviamo a fornire il tuo primo dispositivo alla piattaforma tramite UI. Segui la documentazione per sapere come fare:

", + "how-to-create-device" : "Come creare un Dispositivo" + }, + "step2" : { + "title" : "Connetti dispositivo", + "content-before" : "

Per connettere il dispositivo devi ottenere le credenziali. Per questa guida consigliamo di usare le credenziali auto-generate (token di accesso).

  • Vai alla tabella dispositivi
  • Clicca sulla riga del dispositivo per aprire i dettagli
  • Premi il pulsante \"Copia token di accesso\"

Usa semplici comandi per inviare dati via HTTP. Ricorda di sostituire $ACCESS_TOKEN con il token del tuo dispositivo:

", + "ubuntu" : { + "install-curl" : "Installa cURL per Ubuntu:" + }, + "macos" : { + "install-curl" : "Installa cURL per MacOS:" + }, + "windows" : { + "install-curl" : "Da Windows 10 b17063, cURL è disponibile di default." + }, + "replace-access-token" : "Sostituisci $ACCESS_TOKEN con il token del tuo dispositivo:", + "content-after" : "

Puoi anche usare altri protocolli come MQTT, CoAP, ecc.

Segui la documentazione per sapere come fare:

", + "how-to-connect-device" : "Come connettere un Dispositivo" + }, + "step3" : { + "title" : "Crea dashboard", + "content" : "

Crea una dashboard per visualizzare i dati delle entità come dispositivi, asset, ecc.

Segui la documentazione per sapere come fare:

", + "how-to-create-dashboard" : "Come creare una Dashboard" + }, + "step4" : { + "title" : "Configura regole di allarme", + "alarm-rules" : "Regole di allarme", + "content" : "

Generiamo un allarme quando la temperatura raggiunge i 25°C. Segui la documentazione per sapere come fare:

", + "how-to-configure-alarm-rules" : "Come configurare le Regole di Allarme" + }, + "step5" : { + "title" : "Crea allarme", + "content-before" : "

Per attivare l'allarme, invia un nuovo valore di telemetria di 26°C o superiore.

", + "replace-access-token" : "Sostituisci $ACCESS_TOKEN con il token del tuo dispositivo:", + "content-after" : "

Segui la documentazione per sapere come fare:

", + "how-to-create-alarm" : "Come creare un Allarme" + }, + "step6" : { + "title" : "Crea cliente e condividi dashboard", + "content" : "

Creando dashboard per gli utenti finali, ogni cliente può vedere solo i propri dispositivi; i dati degli altri clienti saranno nascosti.

Segui la documentazione per sapere come fare:

" + } + } + } + }, + "icon" : { + "icon" : "Icona", + "icons" : "Icone", + "select-icon" : "Seleziona icona", + "material-icons" : "Icone Material", + "show-all" : "Mostra tutte le icone", + "search-icon" : "Cerca icona", + "no-icons-found" : "Nessuna icona trovata per '{{iconSearch}}'" + }, + "phone-input" : { + "phone-input-label" : "Numero di telefono", + "phone-input-required" : "Il numero di telefono è obbligatorio", + "phone-input-validation" : "Il numero di telefono non è valido o non è possibile", + "phone-input-pattern" : "Numero di telefono non valido. Deve essere nel formato E.164, es. {{phoneNumber}}", + "phone-input-hint" : "Numero di telefono in formato E.164, es. {{phoneNumber}}" + }, + "custom" : { + "widget-action" : { + "action-cell-button" : "Pulsante nella cella azione", + "row-click" : "Al clic sulla riga", + "cell-click" : "Al clic sulla cella", + "polygon-click" : "Al clic sul poligono", + "marker-click" : "Al clic sul marcatore", + "circle-click" : "Al clic sul cerchio", + "tooltip-tag-action" : "Azione sull'etichetta del tooltip", + "node-selected" : "Al nodo selezionato", + "element-click" : "Al clic sull'elemento HTML", + "pie-slice-click" : "Al clic sulla fetta", + "row-double-click" : "Al doppio clic sulla riga", + "cell-double-click" : "Al doppio clic sulla cella", + "card-click" : "Al clic sulla scheda", + "click" : "Al clic" + } + }, + "paginator" : { + "items-per-page" : "Elementi per pagina:", + "first-page-label" : "Prima pagina", + "last-page-label" : "Ultima pagina", + "next-page-label" : "Pagina successiva", + "previous-page-label" : "Pagina precedente", + "items-per-page-separator" : "di" + }, + "language" : { + "language" : "Lingua", + "locales" : { + "ar_AE" : "العربية (الإمارات العربية المتحدة)", + "ca_ES" : "català (Espanya)", + "cs_CZ" : "čeština (Česko)", + "da_DK" : "dansk (Danmark)", + "de_DE" : "Deutsch (Deutschland)", + "el_GR" : "Ελληνικά (Ελλάδα)", + "en_US" : "English (United States)", + "es_ES" : "español (España)", + "fa_IR" : "فارسی (ایران)", + "fr_FR" : "français (France)", + "it_IT" : "italiano (Italia)", + "ja_JP" : "日本語 (日本)", + "ka_GE" : "ქართული (საქართველო)", + "ko_KR" : "한국어 (대한민국)", + "lt_LT" : "lietuvių (Lietuva)", + "lv_LV" : "latviešu (Latvija)", + "nl_BE" : "Nederlands (België)", + "pl_PL" : "polski (Polska)", + "pt_BR" : "português (Brasil)", + "ro_RO" : "română (România)", + "sl_SI" : "slovenščina (Slovenija)", + "tr_TR" : "Türkçe (Türkiye)", + "uk_UA" : "українська (Україна)", + "zh_CN" : "中文 (中国)", + "zh_TW" : "中文 (台灣)" } -} + } +} \ No newline at end of file From de62ab24fd6d8095b6956313d30e97b265313479 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 19 May 2025 17:12:29 +0300 Subject: [PATCH 191/335] Fix identation --- .../thingsboard/client/tools/i18n/TranslationPruner.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java index 73fe2ce94e..9791a67d1f 100644 --- a/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java +++ b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java @@ -1,5 +1,8 @@ package org.thingsboard.client.tools.i18n; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.core.util.Separators; +import com.fasterxml.jackson.core.util.Separators.Spacing; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -73,8 +76,9 @@ public class TranslationPruner { throw new IllegalArgumentException("Source JSON must be an object at root"); } ObjectNode pruned = pruneNode((ObjectNode) sourceRoot, validKeys, "", mapper); - - mapper.writerWithDefaultPrettyPrinter().writeValue(destFile, pruned); + Separators seps = Separators.createDefaultInstance() + .withObjectFieldValueSpacing(Spacing.AFTER); + mapper.writer(new DefaultPrettyPrinter().withSeparators(seps)).writeValue(destFile, pruned); System.out.println("Pruned translation written to " + destFile.getPath()); } } catch (IOException e) { From e842e9b8c5c92ac43f31f77b227a516610b79242 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 19 May 2025 17:14:54 +0300 Subject: [PATCH 192/335] i18n --- .../assets/locale/locale.constant-da_DK.json | 10701 ++++++++--- .../assets/locale/locale.constant-de_DE.json | 9405 ++++++++-- .../assets/locale/locale.constant-es_ES.json | 15186 ++++++++++------ .../assets/locale/locale.constant-fr_FR.json | 11489 +++++++++--- .../assets/locale/locale.constant-it_IT.json | 10889 +++++++++-- 5 files changed, 43769 insertions(+), 13901 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index e96ec45e3e..d9a5172664 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -4,99 +4,140 @@ "unauthorized-access": "Uautoriseret adgang", "unauthorized-access-text": "Du skal logge ind for at få adgang til denne ressource!", "access-forbidden": "Adgang forbudt", - "access-forbidden-text": "Du har ikke adgangsrettigheder til denne placering!
Prøv at logge ind med en anden bruger, hvis du stadig ønsker at få adgang til denne placering.", + "access-forbidden-text": "Du har ikke adgangsrettigheder til denne placering!
Prøv at logge ind med en anden bruger, hvis du stadig ønsker adgang til denne placering.", "refresh-token-expired": "Sessionen er udløbet", - "refresh-token-failed": "Kunne ikke opdatere session", - "permission-denied": "Tilladelse nægtet", + "refresh-token-failed": "Kan ikke forny sessionen", + "permission-denied": "Adgang nægtet", "permission-denied-text": "Du har ikke tilladelse til at udføre denne handling!" }, + "account": { + "account": "Konto", + "notification-settings": "Notifikationsindstillinger" + }, "action": { "activate": "Aktivér", - "suspend": "Udsæt", + "suspend": "Suspender", "save": "Gem", "saveAs": "Gem som", - "cancel": "Annuller", - "ok": "Okay", + "move": "Flyt", + "cancel": "Annullér", + "ok": "OK", "delete": "Slet", "add": "Tilføj", "yes": "Ja", "no": "Nej", - "update": "Opdatering", + "update": "Opdater", "remove": "Fjern", "search": "Søg", "clear-search": "Ryd søgning", "assign": "Tildel", - "unassign": "Fjern tildeling", + "unassign": "Fratildel", "share": "Del", "make-private": "Gør privat", - "make-public": "Gør offentlig", "apply": "Anvend", "apply-changes": "Anvend ændringer", "edit-mode": "Redigeringstilstand", "enter-edit-mode": "Gå til redigeringstilstand", "decline-changes": "Afvis ændringer", - "open": "Åbn", - "close": "Tæt", + "decline": "Afvis", + "close": "Luk", "back": "Tilbage", "run": "Kør", - "sign-in": "Log på!", - "edit": "Rediger", + "sign-in": "Log ind!", + "edit": "Redigér", "view": "Vis", "create": "Opret", "drag": "Træk", - "refresh": "Genopfrisk", + "refresh": "Opdatér", "undo": "Fortryd", "copy": "Kopiér", - "paste": "Sæt ind", + "paste": "Indsæt", "copy-reference": "Kopiér reference", "paste-reference": "Indsæt reference", "import": "Importér", "export": "Eksportér", - "share-via": "", - "move": "Flyt", + "share-via": "Del via {{provider}}", "select": "Vælg", "continue": "Fortsæt", "discard-changes": "Kassér ændringer", "download": "Download", - "next-with-label": "", + "next": "Næste", + "next-with-label": "Næste: {{label}}", "read-more": "Læs mere", - "hide": "Skjul" + "hide": "Skjul", + "test": "Test", + "done": "Færdig", + "print": "Udskriv", + "restore": "Gendan", + "confirm": "Bekræft", + "more": "Mere", + "less": "Mindre", + "skip": "Spring over", + "send": "Send", + "reset": "Nulstil", + "show-more": "Vis mere", + "dont-show-again": "Vis ikke igen", + "see-documentation": "Se dokumentation", + "clear": "Ryd", + "upload": "Upload", + "delete-anyway": "Slet alligevel", + "delete-selected": "Slet valgte", + "set": "Angiv" }, "aggregation": { - "aggregation": "Opsamling", - "function": "Dataopsamlingsfunktion", - "limit": "Maks.-værdier", + "aggregation": "Aggregering", + "function": "Dataaggregeringsfunktion", + "limit": "Maks. værdier", "group-interval": "Grupperingsinterval", "min": "Min", "max": "Maks", "avg": "Gennemsnit", - "sum": "I alt", - "count": "Sammentælling", + "sum": "Sum", + "count": "Antal", "none": "Ingen" }, "admin": { + "settings": "Indstillinger", "general": "Generelt", "general-settings": "Generelle indstillinger", "home-settings": "Hjem-indstillinger", + "home": "Hjem", "outgoing-mail": "Mailserver", - "outgoing-mail-settings": "Udgående mailserverindstillinger", + "outgoing-mail-settings": "Indstillinger for udgående mailserver", "system-settings": "Systemindstillinger", - "test-mail-sent": "Test-e-mail blev sendt!", + "test-mail-sent": "Testmail blev sendt med succes!", "base-url": "Basis-URL", "base-url-required": "Basis-URL er påkrævet.", - "prohibit-different-url": "Det er ikke tilladt at bruge værtsnavn fra overskrifterne for klientanmodninger", - "prohibit-different-url-hint": "Denne indstilling skal aktiveres for produktionsmiljøer. Kan forårsage sikkerhedsproblemer ved deaktivering", - "mail-from": "Afsender", - "mail-from-required": "Afsender er påkrævet.", + "prohibit-different-url": "Forbyd brug af værtsnavn fra klientanmodningens headers", + "prohibit-different-url-hint": "Denne indstilling bør være aktiveret i produktionsmiljøer. Kan medføre sikkerhedsproblemer, hvis deaktiveret", + "device-connectivity": { + "device-connectivity": "Enhedstilslutning", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPs", + "mqtt": "MQTT", + "mqtts": "MQTTs", + "coap": "COAP", + "coaps": "COAPs", + "hint": "Hvis felterne for vært eller port er tomme, anvendes standardprotokolværdien.", + "host": "Vært", + "port": "Port", + "port-pattern": "Porten skal være et positivt heltal.", + "port-range": "Porten skal være i intervallet fra 1 til 65535." + }, + "mail-from": "Mail fra", + "mail-from-required": "Mail fra er påkrævet.", "smtp-protocol": "SMTP-protokol", "smtp-host": "SMTP-vært", "smtp-host-required": "SMTP-vært er påkrævet.", "smtp-port": "SMTP-port", - "smtp-port-required": "Du skal angive en smtp-port.", - "smtp-port-invalid": "Det ligner ikke en gyldig smtp-port.", - "timeout-msec": "Timeout (msek.)", + "smtp-port-required": "Du skal angive en SMTP-port.", + "smtp-port-invalid": "Det ligner ikke en gyldig SMTP-port.", + "timeout-msec": "Timeout (ms)", "timeout-required": "Timeout er påkrævet.", - "timeout-invalid": "Det ser ikke ud til at være en gyldig timeout.", + "timeout-invalid": "Det ligner ikke en gyldig timeout.", "enable-tls": "Aktivér TLS", "tls-version": "TLS-version", "enable-proxy": "Aktivér proxy", @@ -104,41 +145,22 @@ "proxy-host-required": "Proxy-vært er påkrævet.", "proxy-port": "Proxy-port", "proxy-port-required": "Proxy-port er påkrævet.", - "proxy-port-range": "Proxy-porten skal ligge i området fra 1 til 65535.", + "proxy-port-range": "Proxy-porten skal være i intervallet fra 1 til 65535.", "proxy-user": "Proxy-bruger", "proxy-password": "Proxy-adgangskode", + "change-password": "Skift adgangskode", "send-test-mail": "Send testmail", - "use-system-mail-settings": "Brug systemets mailserverindstillinger", - "mail-templates": "Mailskabeloner", - "mail-template-settings": "Indstillinger for mailskabeloner", - "use-system-mail-template-settings": "Brug systemmailskabeloner", - "mail-template": { - "mail-template": "Mailskabelon", - "test": "Test-e-mailmeddelelse", - "activation": "Meddelelse om aktivering af konto", - "account-activated": "Meddelelse om aktiveret konto", - "account-lockout": "Meddelelse om kontospærring", - "reset-password": "Meddelelse om nulstilling af adgangskode", - "password-was-reset": "Meddelelse om nulstillet adgangskode", - "user-activated": "Brugeraktiveret-meddelelse", - "user-registered": "Brugerregistreret-meddelelse", - "api-usage-state-enabled": "Api-brugstilstand aktiveret", - "api-usage-state-warning": "Advarsel om Api-brugstilstand", - "api-usage-state-disabled": "Api-brugstilstand deaktiveret" - }, - "mail-subject": "Mailens emne", - "mail-body": "Mailens brødtekst", "sms-provider": "SMS-udbyder", "sms-provider-settings": "Indstillinger for SMS-udbyder", - "use-system-sms-settings": "Anvend systemets indstillinger for SMS-udbyder", "sms-provider-type": "SMS-udbydertype", "sms-provider-type-required": "SMS-udbydertype er påkrævet.", "sms-provider-type-aws-sns": "Amazon SNS", "sms-provider-type-twilio": "Twilio", - "aws-access-key-id": "AWS-adgangsnøgle-id", - "aws-access-key-id-required": "AWS-adgangsnøgle-id er påkrævet", - "aws-secret-access-key": "AWS hemmelig adgangsnøgle", - "aws-secret-access-key-required": "AWS hemmelig adgangsnøgle er påkrævet", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID er påkrævet", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key er påkrævet", "aws-region": "AWS-region", "aws-region-required": "AWS-region er påkrævet", "number-from": "Telefonnummer fra", @@ -146,177 +168,504 @@ "number-to": "Telefonnummer til", "number-to-required": "Telefonnummer til er påkrævet.", "phone-number-hint": "Telefonnummer i E.164-format, f.eks. +19995550123", - "phone-number-hint-twilio": "Telefonnummer i E.164-format/Telefonnummers SID/Meddelelsesservice SID, f.eks. +19995550123/PNXXX/MGXXX", + "phone-number-hint-twilio": "Telefonnummer i E.164-format/Telefonnummerets SID/Messaging Service SID, f.eks. +19995550123/PNXXX/MGXXX", "phone-number-pattern": "Ugyldigt telefonnummer. Skal være i E.164-format, f.eks. +19995550123.", - "phone-number-pattern-twilio": "Ugyldigt telefonnummer. Skal være i E.164-format/Telefonnummers SID/Meddelelsesservice SID, f.eks. +19995550123/PNXXX/MGXXX.", + "phone-number-pattern-twilio": "Ugyldigt telefonnummer. Skal være i E.164-format/Telefonnummerets SID/Messaging Service SID, f.eks. +19995550123/PNXXX/MGXXX.", "sms-message": "SMS-besked", "sms-message-required": "SMS-besked er påkrævet.", "sms-message-max-length": "SMS-beskeden må ikke være længere end 1600 tegn", "twilio-account-sid": "Twilio-konto SID", "twilio-account-sid-required": "Twilio-konto SID er påkrævet", - "twilio-account-token": "Twilio-konto-token", - "twilio-account-token-required": "Twilio-konto-token er påkrævet", + "twilio-account-token": "Twilio-konto Token", + "twilio-account-token-required": "Twilio-konto Token er påkrævet", "send-test-sms": "Send test-SMS", - "test-sms-sent": "Test-SMS'en blev sendt!", + "test-sms-sent": "Test-SMS blev sendt med succes!", "security-settings": "Sikkerhedsindstillinger", "password-policy": "Adgangskodepolitik", - "minimum-password-length": "Min. adgangskodelængde", - "minimum-password-length-required": "Min. adgangskodelængde er påkrævet", - "minimum-password-length-range": "Min. adgangskodelængde skal være mellem 5 og 50", - "minimum-uppercase-letters": "Min. antal store bogstaver", - "minimum-uppercase-letters-range": "Min. antal store bogstaver kan ikke være negativt", - "minimum-lowercase-letters": "Min. antal små bogstaver", - "minimum-lowercase-letters-range": "Min. antal små bogstaver kan ikke være negativt", - "minimum-digits": "Min. antal cifre", - "minimum-digits-range": "Minimum antal cifre kan ikke være negativt", + "minimum-password-length": "Minimum adgangskodelængde", + "minimum-password-length-required": "Minimum adgangskodelængde er påkrævet", + "minimum-password-length-range": "Minimum adgangskodelængde skal være mellem 6 og 50", + "maximum-password-length": "Maksimal adgangskodelængde", + "maximum-password-length-min": "Maksimal adgangskodelængde skal være mindst 6", + "maximum-password-length-less-min": "Maksimal adgangskodelængde skal være større end minimumslængde", + "minimum-uppercase-letters": "Minimum antal store bogstaver", + "minimum-uppercase-letters-range": "Minimum antal store bogstaver må ikke være negativt", + "minimum-lowercase-letters": "Minimum antal små bogstaver", + "minimum-lowercase-letters-range": "Minimum antal små bogstaver må ikke være negativt", + "minimum-digits": "Minimum antal cifre", + "minimum-digits-range": "Minimum antal cifre må ikke være negativt", "minimum-special-characters": "Minimum antal specialtegn", - "minimum-special-characters-range": "Minimum antal specialtegn kan ikke være negativt", - "password-expiration-period-days": "Adgangskodens udløbsperiode i dage", - "password-expiration-period-days-range": "Adgangskodens udløbsperiode i dage kan ikke være negativ", - "password-reuse-frequency-days": "Hyppighed af genbrug af adgangskode i dage", - "password-reuse-frequency-days-range": "Hyppigheden af genbrug af adgangskode i dage kan ikke være negativ", - "general-policy": "Generelle retningslinjer", - "max-failed-login-attempts": "Maks. antal mislykkede loginforsøg, før kontoen spærres", - "minimum-max-failed-login-attempts-range": "Maks. antal mislykkede loginforsøg kan ikke være negativt", - "user-lockout-notification-email": "Hvis brugerkontoen spærres, sendes en meddelelse til e-mail", + "minimum-special-characters-range": "Minimum antal specialtegn må ikke være negativt", + "password-expiration-period-days": "Adgangskodeudløbsperiode i dage", + "password-expiration-period-days-range": "Adgangskodeudløbsperiode i dage må ikke være negativ", + "password-reuse-frequency-days": "Adgangskodegenbrug hyppighed i dage", + "password-reuse-frequency-days-range": "Adgangskodegenbrug hyppighed i dage må ikke være negativ", + "allow-whitespace": "Tillad mellemrum", + "force-reset-password-if-no-valid": "Tving nulstilling af adgangskode, hvis ikke gyldig", + "force-reset-password-if-no-valid-hint": "Vær forsigtig, når denne funktion aktiveres: brugere med ugyldige adgangskoder skal nulstille deres adgangskode via e-mail.", + "general-policy": "Generel politik", + "max-failed-login-attempts": "Maksimalt antal mislykkede loginforsøg, før kontoen låses", + "minimum-max-failed-login-attempts-range": "Maksimalt antal mislykkede loginforsøg må ikke være negativt", + "user-lockout-notification-email": "Ved kontolåsning, send notifikation til e-mail", + "user-activation-token-ttl": "Brugeraktiveringslink TTL i timer", + "user-activation-token-ttl-range": "Brugeraktiveringslink TTL skal være mellem 1 og 24 timer", + "password-reset-token-ttl": "Link til nulstilling af adgangskode TTL i timer", + "password-reset-token-ttl-range": "Link til nulstilling af adgangskode TTL skal være mellem 1 og 24 timer", + "mobile-secret-key-length": "Længde på mobil hemmelig nøgle", + "mobile-secret-key-length-range": "Længden på mobil hemmelig nøgle skal være positiv", "domain-name": "Domænenavn", - "domain-name-unique": "Domænenavn og protokol skal være entydige.", + "domain-name-unique": "Domænenavn og protokol skal være unikke.", + "domain-name-max-length": "Domænenavnet skal være mindre end 256", "error-verification-url": "Et domænenavn må ikke indeholde symbolerne '/' og ':'. Eksempel: thingsboard.io", + "connection-settings": "Forbindelsesindstillinger", "oauth2": { - "access-token-uri": "Adgangstoken URI", - "access-token-uri-required": "Adgangstoken URI er påkrævet.", + "access-token-uri": "Adgangstoken-URI", + "access-token-uri-required": "Adgangstoken-URI er påkrævet.", "activate-user": "Aktivér bruger", "add-domain": "Tilføj domæne", "delete-domain": "Slet domæne", "add-provider": "Tilføj udbyder", "delete-provider": "Slet udbyder", - "allow-user-creation": "Tillad brugeroprettelse", - "always-fullscreen": "Altid fuld skærm", - "authorization-uri": "Godkendelses-URI", - "authorization-uri-required": "Godkendelses-URI er påkrævet.", - "client-authentication-method": "Klientgodkendelsesmetode", - "client-id": "Klient-id", - "client-id-required": "Klient-id er påkrævet.", - "client-secret": "Kunde hemmelig", - "client-secret-required": "Kunde hemmelig er påkrævet.", + "allow-user-creation": "Tillad oprettelse af bruger", + "always-fullscreen": "Altid fuldskærm", + "authorization-uri": "Autoriserings-URI", + "authorization-uri-required": "Autoriserings-URI er påkrævet.", + "add-client": "Tilføj OAuth 2.0-klient", + "client-details": "OAuth 2.0-klientdetaljer", + "client": "OAuth 2.0-klient", + "clients": "OAuth 2.0-klienter", + "no-oauth2-clients": "Ingen OAuth 2.0-klienter fundet", + "search-oauth2-clients": "Søg OAuth 2.0-klienter", + "delete-client-title": "Er du sikker på, at du vil slette OAuth 2.0-klienten '{{clientName}}'?", + "delete-client-text": "Vær forsigtig, efter bekræftelse vil klienten og alle relaterede data være uoprettelige.", + "delete-mobile-app-title": "Er du sikker på, at du vil slette mobilapplikationen '{{applicationName}}'?", + "delete-mobile-app-text": "Vær forsigtig, efter bekræftelse vil mobilapplikationen og alle relaterede data være uoprettelige.", + "title": "Titel", + "client-title-required": "Titel er påkrævet", + "client-title-max-length": "Titel skal være mindre end 100 tegn", + "advanced-settings": "Avancerede indstillinger", + "domain-details": "Domænedetaljer", + "no-domains": "Ingen domæner fundet", + "search-domains": "Søg domæner", + "mobile-app-details": "Detaljer for mobilapplikation", + "add-mobile-app": "Tilføj mobilapplikation", + "no-mobile-apps": "Ingen mobilapplikationer fundet", + "search-mobile-apps": "Søg mobilapplikationer", + "send-token": "Send token", + "create-new": "Opret ny", + "client-authentication-method": "Klientautentificeringsmetode", + "client-id": "Klient-ID", + "client-id-required": "Klient-ID er påkrævet.", + "client-id-max-length": "Klient-ID skal være mindre end 256 tegn", + "client-secret": "Klienthemmelighed", + "client-secret-required": "Klienthemmelighed er påkrævet.", + "client-secret-max-length": "Klienthemmelighed skal være mindre end 2049 tegn", "custom-setting": "Brugerdefinerede indstillinger", - "customer-name-pattern": "Kundenavnsmønster", - "parent-customer-name-pattern": "Overordnet kundenavnsmønster", - "user-groups-name-pattern": "Mønster for brugergruppenavn", - "default-dashboard-name": "Standard dashboardnavn", - "delete-domain-text": "Vær forsigtig. Efter bekræftelsen vil et domæne og alle udbyderdata være utilgængelige.", - "delete-domain-title": "", - "delete-registration-text": "Vær forsigtig. Efter bekræftelsen vil udbyderdata være utilgængelige.", - "delete-registration-title": "", - "email-attribute-key": "E-mailattributnøgle", - "email-attribute-key-required": "E-mailattributnøgle er påkrævet.", - "first-name-attribute-key": "Fornavn attributnøgle", + "customer-name-pattern": "Mønsternavn for kunde", + "customer-name-pattern-max-length": "Mønsternavn for kunde skal være mindre end 256 tegn", + "default-dashboard-name": "Standard dashboard-navn", + "default-dashboard-name-max-length": "Standard dashboard-navn skal være mindre end 256 tegn", + "delete-domain-text": "Vær forsigtig, efter bekræftelse vil domænet og alle udbyderdata ikke længere være tilgængelige.", + "delete-domain-title": "Er du sikker på, at du vil slette domænet '{{domainName}}'?", + "delete-registration-text": "Vær forsigtig, efter bekræftelse vil udbyderdata ikke længere være tilgængelige.", + "delete-registration-title": "Er du sikker på, at du vil slette udbyderen '{{name}}'?", + "email-attribute-key": "E-mail-attributnøgle", + "email-attribute-key-required": "E-mail-attributnøgle er påkrævet.", + "email-attribute-key-max-length": "E-mail-attributnøgle skal være mindre end 32 tegn", + "first-name-attribute-key": "Fornavn-attributnøgle", + "first-name-attribute-key-max-length": "Fornavn-attributnøgle skal være mindre end 32 tegn", "general": "Generelt", - "jwk-set-uri": "JSON webnøgle-URI", - "last-name-attribute-key": "Efternavn attributnøgle", - "login-button-icon": "Logon-knapikon", - "login-button-label": "Udbyder etiket", - "login-button-label-placeholder": "Log ind med $(Udbyder etiket)", + "jwk-set-uri": "JSON Web Key URI", + "last-name-attribute-key": "Efternavn-attributnøgle", + "last-name-attribute-key-max-length": "Efternavn-attributnøgle skal være mindre end 32 tegn", + "login-button-icon": "Login-knap ikon", + "login-button-label": "Udbydernavn", + "login-button-label-placeholder": "Log ind med $(Provider label)", "login-button-label-required": "Etiket er påkrævet.", - "login-provider": "Log ind udbyder", - "mapper": "Kortlægger", + "login-provider": "Loginudbyder", + "mapper": "Mapper", "new-domain": "Nyt domæne", - "oauth2": "OAuth2", - "redirect-uri-template": "Omdiriger URI-skabelon", - "copy-redirect-uri": "Kopiér omdirigering af URI", - "registration-id": "Registrerings-id", - "registration-id-required": "Registrerings-id er påkrævet.", - "registration-id-unique": "Registrerings-id skal være entydigt for systemet.", + "oauth2": "OAuth 2.0", + "password-max-length": "Adgangskode skal være mindre end 256 tegn", + "redirect-uri-template": "Skabelon for omdirigerings-URI", + "copy-redirect-uri": "Kopiér omdirigerings-URI", + "registration-id": "Registrerings-ID", + "registration-id-required": "Registrerings-ID er påkrævet.", + "registration-id-unique": "Registrerings-ID skal være unikt i systemet.", "scope": "Omfang", "scope-required": "Omfang er påkrævet.", - "tenant-name-pattern": "Mønster for lejernavn", - "tenant-name-pattern-required": "Mønster for lejernavn er påkrævet.", - "tenant-name-strategy": "Strategi for lejernavn", - "type": "Kortlæggertype", + "tenant-name-pattern": "Mønsternavn for lejer", + "tenant-name-pattern-required": "Mønsternavn for lejer er påkrævet.", + "tenant-name-pattern-max-length": "Mønsternavn for lejer skal være mindre end 256 tegn", + "tenant-name-strategy": "Strategi for lejerens navn", + "type": "Mapper-type", "uri-pattern-error": "Ugyldigt URI-format.", "url": "URL", "url-pattern": "Ugyldigt URL-format.", "url-required": "URL er påkrævet.", + "url-max-length": "URL skal være mindre end 256 tegn", "user-info-uri": "Brugerinfo URI", "user-info-uri-required": "Brugerinfo URI er påkrævet.", - "user-name-attribute-name": "Attributnøgle for brugernavn", - "user-name-attribute-name-required": "Attributnøgle for brugernavn er påkrævet", + "username-max-length": "Brugernavn skal være mindre end 256 tegn", + "user-name-attribute-name": "Brugernavn-attributnøgle", + "user-name-attribute-name-required": "Brugernavn-attributnøgle er påkrævet", "protocol": "Protokol", "domain-schema-http": "HTTP", "domain-schema-https": "HTTPS", "domain-schema-mixed": "HTTP+HTTPS", - "enable": "Aktivér OAuth2-indstillinger" - } + "enable": "Aktivér OAuth 2.0-indstillinger", + "disable": "Deaktivér OAuth 2.0-indstillinger", + "edge": "Distribuér til Edge", + "edge-enable": "Aktivér distribution til Edge", + "edge-disable": "Deaktivér distribution til Edge", + "domains": "Domæner", + "mobile-apps": "Mobilapplikationer", + "mobile-package": "Applikationspakke", + "mobile-package-placeholder": "F.eks.: my.example.app", + "mobile-package-hint": "For Android: dit eget unikke Application ID. For iOS: Produktets bundle-identifikator.", + "mobile-package-unique": "Applikationspakken skal være unik.", + "mobile-package-required": "Applikationspakken er påkrævet.", + "mobile-package-max-length": "Applikationspakken skal være mindre end 256 tegn", + "mobile-package-spaces": "Applikationspakken må ikke indeholde mellemrum", + "mobile-app-secret": "Applikationshemmelighed", + "mobile-app-secret-hint": "Base64-kodet streng, der repræsenterer mindst 512 bit data.", + "mobile-app-secret-required": "Applikationshemmelighed er påkrævet.", + "mobile-app-secret-min-length": "Applikationshemmelighed skal være mindst 512 bit data.", + "mobile-app-secret-base64": "Applikationshemmelighed skal være i base64-format.", + "invalid-mobile-app-secret": "Applikationshemmelighed må kun indeholde alfanumeriske tegn og være mellem 16 og 2048 tegn lang.", + "copy-mobile-app-secret": "Kopiér applikationshemmelighed", + "delete-mobile-app": "Slet applikationsinformation", + "providers": "Udbydere", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Alle platforme", + "smtp-provider": "SMTP-udbyder", + "allowed-platforms": "Tilladte platforme", + "authentication": "Autentificering", + "basic": "Basis", + "provider": "Udbyder", + "redirect-url": "Omdirigerings-URI", + "domain-name": "Domænenavn", + "domain-name-required": "Domænenavn er påkrævet", + "redirect-url-template": "Skabelon for omdirigerings-URI", + "microsoft-tenant-id": "Katalog (lejer) ID", + "microsoft-tenant-id-required": "Katalog (lejer) ID er påkrævet", + "token-uri": "Token-URI", + "token-uri-required": "Token-URI er påkrævet", + "redirect-uri": "Omdirigerings-URI", + "google-provider": "Google", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "Brugerdefineret", + "generate-access-token": "Generér adgangstoken", + "update-access-token": "Opdatér adgangstoken", + "access-token-status": "Status for adgangstoken:", + "token-status-generated": "genereret", + "token-status-not-generated": "ikke genereret" + }, + "smpp-provider": { + "smpp-version": "SMPP-version", + "smpp-host": "SMPP-vært", + "smpp-host-required": "SMPP-vært er påkrævet", + "smpp-port": "SMPP-port", + "smpp-port-required": "SMPP-port er påkrævet", + "system-id": "System-ID", + "system-id-required": "System-ID er påkrævet", + "password": "Adgangskode", + "password-required": "Adgangskode er påkrævet", + "type-settings": "Typeindstillinger", + "source-settings": "Kildeindstillinger", + "destination-settings": "Destinationsindstillinger", + "additional-settings": "Yderligere indstillinger", + "system-type": "Systemtype", + "bind-type": "Bind-type", + "service-type": "Tjenestetype", + "source-address": "Kildeadresse", + "source-ton": "Kilde TON", + "source-npi": "Kilde NPI", + "destination-ton": "Destination TON (Nummerets type)", + "destination-npi": "Destination NPI (Nummereringsplanidentifikation)", + "address-range": "Adresseområde", + "coding-scheme": "Kodningsskema", + "bind-type-tx": "Afsender", + "bind-type-rx": "Modtager", + "bind-type-trx": "Transceiver", + "ton-unknown": "Ukendt", + "ton-international": "International", + "ton-national": "National", + "ton-network-specific": "Netværksspecifik", + "ton-subscriber-number": "Abonnentnummer", + "ton-alphanumeric": "Alfanumerisk", + "ton-abbreviated": "Forkortet", + "npi-unknown": "0 - Ukendt", + "npi-isdn": "1 - ISDN/telefonnummereringsplan (E163/E164)", + "npi-data-numbering-plan": "3 - Data-nummereringsplan (X.121)", + "npi-telex-numbering-plan": "4 - Telex-nummereringsplan (F.69)", + "npi-land-mobile": "6 - Landmobil (E.212)", + "npi-national-numbering-plan": "8 - National nummereringsplan", + "npi-private-numbering-plan": "9 - Privat nummereringsplan", + "npi-ermes-numbering-plan": "10 - ERMES-nummereringsplan (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internet (IP)", + "npi-wap-client-id": "18 - WAP-klient-ID (defineres af WAP Forum)", + "scheme-smsc": "0 - SMSC Standard Alfabet (ASCII for korte og lange koder og GSM for gratisnumre)", + "scheme-ia5": "1 - IA5 (ASCII for korte og lange koder, Latin 9 for gratisnumre (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Oktet uspecificeret (8-bit binær)", + "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Oktet uspecificeret (8-bit binær)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Kyrillisk (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latin/Hebræisk (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Piktogramkodning", + "scheme-music-codes": "10 - Musik-koder (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Udvidet Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Koreansk grafisk tegnsæt (KS C 5601/KS X 1001)" + }, + "queue-select-name": "Vælg kønavn", + "queue-name": "Navn", + "queue-name-required": "Kønavn er påkrævet!", + "queues": "Køer", + "queue-partitions": "Partitioner", + "queue-submit-strategy": "Indsendelsesstrategi", + "queue-processing-strategy": "Behandlingsstrategi", + "queue-configuration": "Køkonfiguration", + "repository-settings": "Versionskontrolindstillinger", + "repository": "Versionskontrol", + "repository-url": "Versionskontrol-URL", + "repository-url-required": "Versionskontrol-URL er påkrævet.", + "default-branch": "Standard branch-navn", + "repository-read-only": "Skrivebeskyttet", + "show-merge-commits": "Vis flette-commits", + "authentication-settings": "Autentificeringsindstillinger", + "auth-method": "Autentificeringsmetode", + "auth-method-username-password": "Adgangskode / adgangstoken", + "auth-method-username-password-hint": "GitHub-brugere skal bruge tokens med skriverettigheder til depotet.", + "auth-method-private-key": "Privat nøgle", + "password-access-token": "Adgangskode / adgangstoken", + "change-password-access-token": "Skift adgangskode / adgangstoken", + "private-key": "Privat nøgle", + "drop-private-key-file-or": "Træk og slip en fil med privat nøgle eller", + "passphrase": "Adgangsfrase", + "enter-passphrase": "Indtast adgangsfrase", + "change-passphrase": "Skift adgangsfrase", + "check-access": "Tjek adgang", + "check-repository-access-success": "Adgang til versionskontrol blev bekræftet!", + "delete-repository-settings-title": "Er du sikker på, at du vil slette versionskontrolindstillingerne?", + "delete-repository-settings-text": "Vær forsigtig, efter bekræftelse fjernes indstillingerne og versionskontrolfunktionen bliver utilgængelig.", + "auto-commit-settings": "Indstillinger for automatisk commit", + "auto-commit": "Automatisk commit", + "auto-commit-entities": "Automatisk commit-enheder", + "no-auto-commit-entities-prompt": "Ingen enheder konfigureret til automatisk commit", + "delete-auto-commit-settings-title": "Er du sikker på, at du vil slette indstillingerne for automatisk commit?", + "delete-auto-commit-settings-text": "Vær forsigtig, efter bekræftelse fjernes indstillingerne og automatisk commit deaktiveres for alle enheder.", + "mobile-app": { + "mobile-app": "Mobilapp", + "mobile-app-qr-code-widget-settings": "Indstillinger for mobilapp QR-kode-widget", + "applications": "Applikationer", + "default": "Standard", + "custom": "Brugerdefineret", + "android": "Android", + "ios": "iOS", + "appearance": "Udseende", + "appearance-on-home-page": "Udseende på startsiden", + "enabled": "Aktiveret", + "disabled": "Deaktiveret", + "badges": "Mærker", + "label": "Etiket", + "label-required": "Etiket er påkrævet", + "label-max-length": "Etiketten må ikke overstige 50 tegn", + "right": "Højre", + "left": "Venstre", + "set": "Angiv", + "preview": "Forhåndsvisning", + "connect-mobile-app": "Tilslut mobilapp", + "use-system-settings": "Brug systemindstillinger" + }, + "2fa": { + "2fa": "To-faktor autentificering", + "available-providers": "Tilgængelige udbydere", + "issuer-name": "Udsteders navn", + "issuer-name-required": "Udsteders navn er påkrævet.", + "max-verification-failures-before-user-lockout": "Maks. antal verificeringsfejl før kontolåsning", + "max-verification-failures-before-user-lockout-pattern": "Maks. antal verificeringsfejl skal være et positivt heltal.", + "number-of-checking-attempts": "Antal verificeringsforsøg", + "number-of-checking-attempts-pattern": "Antal verificeringsforsøg skal være et positivt heltal.", + "number-of-checking-attempts-required": "Antal verificeringsforsøg er påkrævet.", + "number-of-codes": "Antal koder", + "number-of-codes-pattern": "Antal koder skal være et positivt heltal.", + "number-of-codes-required": "Antal koder er påkrævet.", + "provider": "Udbyder", + "retry-verification-code-period": "Ventetid før ny verificering (sek)", + "retry-verification-code-period-pattern": "Mindste ventetid er 5 sek.", + "retry-verification-code-period-required": "Ventetid før ny verificering er påkrævet.", + "total-allowed-time-for-verification": "Total tilladt tid til verificering (sek)", + "total-allowed-time-for-verification-pattern": "Mindste tilladte tid er 60 sek.", + "total-allowed-time-for-verification-required": "Total tilladt tid er påkrævet.", + "use-system-two-factor-auth-settings": "Brug systemets to-faktor autentificeringsindstillinger", + "verification-code-check-rate-limit": "Begrænsning af verificeringsforsøg", + "verification-code-lifetime": "Verificeringskode levetid (sek)", + "verification-code-lifetime-pattern": "Verificeringskode levetid skal være et positivt heltal.", + "verification-code-lifetime-required": "Verificeringskode levetid er påkrævet.", + "verification-message-template": "Skabelon for verificeringsbesked", + "verification-limitations": "Begrænsninger for verificering", + "verification-message-template-pattern": "Verificeringsbeskeden skal indeholde mønsteret: ${code}", + "verification-message-template-required": "Skabelon for verificeringsbesked er påkrævet.", + "within-time": "Inden for tid (sek)", + "within-time-pattern": "Tiden skal være et positivt heltal.", + "within-time-required": "Tid er påkrævet." + }, + "jwt": { + "security-settings": "JWT sikkerhedsindstillinger", + "issuer-name": "Udsteders navn", + "issuer-name-required": "Udsteders navn er påkrævet.", + "signings-key": "Signeringsnøgle", + "signings-key-hint": "Base64-kodet streng, der repræsenterer mindst 512 bit data.", + "signings-key-required": "Signeringsnøgle er påkrævet.", + "signings-key-min-length": "Signeringsnøgle skal være mindst 512 bit data.", + "signings-key-base64": "Signeringsnøgle skal være i base64-format.", + "expiration-time": "Udløbstid for token (sek)", + "expiration-time-required": "Udløbstid for token er påkrævet.", + "expiration-time-max": "Maksimalt tilladt tid er 2147483647 sekunder (68 år).", + "expiration-time-min": "Minimumstid er 60 sekunder (1 minut).", + "refresh-expiration-time": "Udløbstid for opfriskningstoken (sek)", + "refresh-expiration-time-required": "Udløbstid for opfriskningstoken er påkrævet.", + "refresh-expiration-time-max": "Maksimalt tilladt tid er 2147483647 sekunder (68 år).", + "refresh-expiration-time-min": "Minimumstid er 900 sekunder (15 minutter).", + "refresh-expiration-time-less-token": "Opfriskningstoken-tid skal være længere end token-tid.", + "generate-key": "Generér nøgle", + "info-header": "Alle brugere skal logge ind igen", + "info-message": "Ændring af JWT-signeringsnøgle vil ugyldiggøre alle udstedte tokens. Alle brugere skal logge ind igen. Dette påvirker også scripts, der bruger Rest API/Websockets." + }, + "resources": "Ressourcer", + "notifications": "Notifikationer", + "notifications-settings": "Notifikationsindstillinger", + "slack-api-token": "Slack API-token", + "slack": "Slack", + "slack-settings": "Slack-indstillinger", + "mobile-settings": "Mobilindstillinger", + "firebase-service-account-file": "Firebase servicekonto legitimationsoplysninger (JSON-fil)", + "select-firebase-service-account-file": "Træk og slip din Firebase servicekonto-fil eller " }, "alarm": { "alarm": "Alarm", "alarms": "Alarmer", + "all-alarms": "Alle alarmer", "select-alarm": "Vælg alarm", - "no-alarms-matching": "", + "no-alarms-matching": "Ingen alarmer matcher '{{entity}}'.", "alarm-required": "Alarm er påkrævet", + "alarm-filter": "Alarmfilter", + "filter": "Filter", "alarm-status": "Alarmstatus", - "alarm-status-list": "Alarmstatusliste", + "alarm-status-list": "Liste over alarmstatus", "any-status": "Enhver status", "search-status": { "ANY": "Enhver", "ACTIVE": "Aktiv", "CLEARED": "Ryddet", - "ACK": "Kvitteret", - "UNACK": "Ikke kvitteret" + "ACK": "Anerkendt", + "UNACK": "Ikke anerkendt" }, "display-status": { - "ACTIVE_UNACK": "Aktiv Ikke-kvitteret", - "ACTIVE_ACK": "Aktiv Kvitteret", - "CLEARED_UNACK": "Ryddet Ikke-kvitteret", - "CLEARED_ACK": "Ryddet Kvitteret" + "ACTIVE_UNACK": "Aktiv - ikke anerkendt", + "ACTIVE_ACK": "Aktiv - anerkendt", + "CLEARED_UNACK": "Ryddet - ikke anerkendt", + "CLEARED_ACK": "Ryddet - anerkendt" }, "no-alarms-prompt": "Ingen alarmer fundet", "created-time": "Oprettelsestidspunkt", "type": "Type", - "severity": "Alvorsgrad", + "severity": "Alvorlighed", "originator": "Ophavsmand", - "originator-type": "Ophavsmandtype", - "details": "Oplysninger", + "originator-type": "Ophavstype", + "details": "Detaljer", + "originator-label": "Etiket for ophavsmand", + "assign": "Tildel", + "assignments": "Tildelinger", + "assignee": "Tildelt person", + "assignee-id": "Tildelt ID", + "assignee-first-name": "Tildelt fornavn", + "assignee-last-name": "Tildelt efternavn", + "assignee-email": "Tildelt e-mail", + "unassigned": "Ikke tildelt", + "user-deleted": "Bruger slettet", + "assignee-not-set": "Alle", "status": "Status", - "alarm-details": "Alarmoplysninger", - "start-time": "Starttid", - "end-time": "Sluttid", - "ack-time": "Tidspunkt for Kvitteret", - "clear-time": "Tidspunkt for Ryddet", - "alarm-severity-list": "Liste over alvorsgrad for alamer", - "any-severity": "Enhver alvorsgrad", + "alarm-details": "Alarmdetaljer", + "start-time": "Starttidspunkt", + "assign-time": "Tildelingstidspunkt", + "end-time": "Sluttidspunkt", + "ack-time": "Tidspunkt for anerkendelse", + "clear-time": "Tidspunkt for rydning", + "duration": "Varighed", + "alarm-severity": "Alarmens alvorlighed", + "alarm-severity-list": "Liste over alvorlighedsniveauer", + "any-severity": "Enhver alvorlighed", "severity-critical": "Kritisk", - "severity-major": "Stor", + "severity-major": "Alvorlig", "severity-minor": "Mindre", "severity-warning": "Advarsel", - "severity-indeterminate": "Ubestemt", - "acknowledge": "Kvittér", - "clear": "Klar", - "search": "Søg efter alarmer", - "selected-alarms": "", + "severity-indeterminate": "Ubetinget", + "acknowledge": "Anerkend", + "clear": "Ryd", + "delete": "Slet", + "search": "Søg alarmer", + "selected-alarms": "{ count, plural, =1 {1 alarm} other {# alarmer} } valgt", "no-data": "Ingen data at vise", - "polling-interval": "Alarmer undersøgelsesinterval (sek.)", - "polling-interval-required": "Alarmer undersøgelsesinterval er påkrævet.", - "min-polling-interval-message": "Mindst 1 sek. undersøgelsesinterval er tilladt.", - "aknowledge-alarms-title": "", - "aknowledge-alarms-text": "", - "aknowledge-alarm-title": "Kvittér for alarm", - "aknowledge-alarm-text": "Er du sikker på, du ønsker at kvittere for alarm?", - "clear-alarms-title": "", - "clear-alarms-text": "", + "polling-interval": "Interval for alarmopdatering (sek)", + "polling-interval-required": "Interval for alarmopdatering er påkrævet.", + "min-polling-interval-message": "Mindste tilladte interval er 1 sek.", + "aknowledge-alarms-title": "Anerkend { count, plural, =1 {1 alarm} other {# alarmer} }", + "aknowledge-alarms-text": "Er du sikker på, at du vil anerkende { count, plural, =1 {1 alarm} other {# alarmer} }?", + "aknowledge-alarm-title": "Anerkend alarm", + "aknowledge-alarm-text": "Er du sikker på, at du vil anerkende alarmen?", + "selected-alarms-are-acknowledged": "Valgte alarmer er allerede anerkendt", + "clear-alarms-title": "Ryd { count, plural, =1 {1 alarm} other {# alarmer} }", + "clear-alarms-text": "Er du sikker på, at du vil rydde { count, plural, =1 {1 alarm} other {# alarmer} }?", "clear-alarm-title": "Ryd alarm", - "clear-alarm-text": "Er du sikker på, at du vil slette Alarm?", + "clear-alarm-text": "Er du sikker på, at du vil rydde alarmen?", + "delete-alarms-title": "Slet { count, plural, =1 {1 alarm} other {# alarmer} }", + "delete-alarms-text": "Er du sikker på, at du vil slette { count, plural, =1 {1 alarm} other {# alarmer} }?", + "selected-alarms-are-cleared": "Valgte alarmer er allerede ryddet", "alarm-status-filter": "Filter for alarmstatus", - "alarm-filter": "Alarmfilter", - "max-count-load": "Maks. antal alarmer, der skal indlæses (0 – ubegrænset)", - "max-count-load-required": "Maks. antal alarmer, der skal indlæses, er påkrævet.", - "max-count-load-error-min": "Minimumværdien er 0.", - "fetch-size": "Hent størrelse", - "fetch-size-required": "Hent størrelse er påkrævet.", - "fetch-size-error-min": "Minimumværdien er 10.", + "alarm-filter-title": "Alarmfilter", + "assigned": "Tildelt", + "filter-title": "Filter", + "max-count-load": "Maksimalt antal alarmer der skal indlæses (0 - ubegrænset)", + "max-count-load-required": "Maksimalt antal alarmer der skal indlæses er påkrævet.", + "max-count-load-error-min": "Minimumsværdien er 0.", + "fetch-size": "Hentningsstørrelse", + "fetch-size-required": "Hentningsstørrelse er påkrævet.", + "fetch-size-error-min": "Minimumsværdien er 10.", + "alarm-types": "Alarmtyper", "alarm-type-list": "Liste over alarmtyper", "any-type": "Enhver type", - "search-propagated-alarms": "Søg efter overførte alarmer" + "assigned-to-current-user": "Tildelt nuværende bruger", + "assigned-to-me": "Tildelt til mig", + "search-propagated-alarms": "Søg distribuerede alarmer", + "comments": "Alarmkommentarer", + "show-more": "Vis mere", + "additional-info": "Yderligere information", + "alarm-type": "Alarmtype", + "enter-alarm-type": "Indtast alarmtype", + "no-alarm-types-matching": "Ingen alarmtyper matcher '{{entitySubtype}}'.", + "alarm-type-list-empty": "Ingen alarmtyper valgt." + }, + "alarm-activity": { + "add": "Tilføj en kommentar...", + "alarm-comment": "Alarmkommentar", + "comments": "Kommentarer", + "delete-alarm-comment": "Vil du slette denne kommentar?", + "refresh": "Opdatér", + "oldest-first": "Ældste først", + "newest-first": "Nyeste først", + "activity": "Aktivitet", + "export": "Eksportér til CSV", + "author": "Forfatter", + "created-date": "Oprettelsesdato", + "edited-date": "Redigeringsdato", + "text": "Tekst", + "system": "System" }, "alias": { "add": "Tilføj alias", @@ -324,448 +673,558 @@ "name": "Aliasnavn", "name-required": "Aliasnavn er påkrævet", "duplicate-alias": "Alias med samme navn findes allerede.", - "filter-type-single-entity": "Enkelt entitet", - "filter-type-entity-group": "Gruppeentiteter", - "filter-type-entity-list": "Entitetsliste", - "filter-type-entity-name": "Entitetsnavn", - "filter-type-entity-type": "Entitetstype", - "filter-type-entity-group-list": "Entitetsgruppeliste", - "filter-type-entity-group-name": "Entitetsgruppenavn", - "filter-type-entities-by-group-name": "Entiteter efter gruppenavn", - "filter-type-state-entity": "Entitet fra dashboardtilstand", - "filter-type-state-entity-description": "Entitet taget fra dashboardtilstandsparametre", - "filter-type-state-entity-owner": "Ejer af entitet fra dashboardtilstand", - "filter-type-state-entity-owner-description": "Ejer af entitet taget fra dashboardtilstandsparametre", - "filter-type-asset-type": "Aktivtype", - "filter-type-asset-type-description": "", - "filter-type-asset-type-and-name-description": "", - "filter-type-device-type": "Enhedstype", - "filter-type-device-type-description": "", - "filter-type-device-type-and-name-description": "", - "filter-type-entity-view-type": "Entitetsvisningstype", - "filter-type-entity-view-type-description": "", - "filter-type-entity-view-type-and-name-description": "", + "filter-type-single-entity": "Enkelt enhed", + "filter-type-entity-list": "Enhedslisten", + "filter-type-entity-name": "Enhedsnavn", + "filter-type-entity-type": "Enhedstype", + "filter-type-state-entity": "Enhed fra dashboardtilstand", + "filter-type-state-entity-description": "Enhed hentet fra dashboardtilstandsparametre", + "filter-type-asset-type": "Assettype", + "filter-type-asset-type-description": "Assets af typen '{{assetTypes}}'", + "filter-type-asset-type-and-name-description": "Assets af typen '{{assetTypes}}' og med navn der begynder med '{{prefix}}'", + "filter-type-device-type": "Devicetype", + "filter-type-device-type-description": "Devices af typen '{{deviceTypes}}'", + "filter-type-device-type-and-name-description": "Devices af typen '{{deviceTypes}}' og med navn der begynder med '{{prefix}}'", + "filter-type-entity-view-type": "Entity View-type", + "filter-type-entity-view-type-description": "Entity Views af typen '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description": "Entity Views af typen '{{entityViewTypes}}' og med navn der begynder med '{{prefix}}'", + "filter-type-edge-type": "Edge-type", + "filter-type-edge-type-description": "Edges af typen '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description": "Edges af typen '{{edgeTypes}}' og med navn der begynder med '{{prefix}}'", "filter-type-relations-query": "Relationsforespørgsel", - "filter-type-relations-query-description": "", - "filter-type-asset-search-query": "Aktivsøgningsforespørgsel", - "filter-type-asset-search-query-description": "", - "filter-type-device-search-query": "Enhedssøgningsforespørgsel", - "filter-type-device-search-query-description": "", - "filter-type-entity-view-search-query": "Entitetsvisning for søgningsforespørgsel", - "filter-type-entity-view-search-query-description": "", - "filter-type-apiUsageState": "Api-brugstilstand", - "entity-filter": "Entitetsfilter", - "resolve-multiple": "Løs som flere entiteter", + "filter-type-relations-query-description": "{{entities}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Edge-søgeforespørgsel", + "filter-type-edge-search-query-description": "Edges med typerne {{edgeTypes}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query": "Asset-søgeforespørgsel", + "filter-type-asset-search-query-description": "Assets med typerne {{assetTypes}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query": "Device-søgeforespørgsel", + "filter-type-device-search-query-description": "Devices med typerne {{deviceTypes}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query": "Entity View-søgeforespørgsel", + "filter-type-entity-view-search-query-description": "Entity Views med typerne {{entityViewTypes}} der har relationen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState": "API-brugstilstand", + "entity-filter": "Enhedsfilter", + "resolve-multiple": "Løs som flere enheder", + "resolve-multiple-hint": "Aktivér for at vise data fra alle filtrerede enheder samtidig.\nHvis deaktiveret, viser widget kun data fra den valgte enhed.", "filter-type": "Filtertype", "filter-type-required": "Filtertype er påkrævet.", - "entity-filter-no-entity-matched": "Der blev ikke fundet nogen entiteter, der matcher det angivne filter.", - "no-entity-filter-specified": "Intet entitetsfilter angivet", - "root-state-entity": "Brug dashboardtilstandsentitet som rod", - "group-state-entity": "Brug dashboardtilstandsentitet som entitetsgruppe", - "group-state-entity-owner": "Brug dashboardtilstandsentitet som entitetsgruppeejer", - "last-level-relation": "Hent kun sidste niveaurelation", - "root-entity": "Rodentitet", - "state-entity-parameter-name": "Parameternavn for tilstandsentitet", - "default-state-entity": "Standard tilstandsentitet", - "default-state-entity-group": "Standard tilstandsentitetsgruppe", + "entity-filter-no-entity-matched": "Ingen enheder matchede det angivne filter.", + "no-entity-filter-specified": "Intet enhedsfilter angivet", + "root-state-entity": "Brug dashboardtilstandens enhed som rod", + "last-level-relation": "Hent kun sidste niveau relation", + "root-entity": "Rodenhed", + "state-entity-parameter-name": "Navn på tilstandsparameter", + "default-state-entity": "Standard tilstandsenhed", "default-entity-parameter-name": "Som standard", - "max-relation-level": "Maks. niveaurelation", + "max-relation-level": "Maks. relationsniveau", "unlimited-level": "Ubegrænset niveau", - "state-entity": "Dashboardtilstandsentitet", - "entities-of-group-state-entity": "Entiteter fra dashboardtilstandsentitetsgruppe", - "all-entities": "Alle entiteter", + "state-entity": "Dashboardtilstandsenhed", + "all-entities": "Alle enheder", "any-relation": "enhver" }, "asset": { - "asset": "Aktiv", - "assets": "Aktiver", - "management": "Styring af aktiver", - "view-assets": "Vis aktiver", - "add": "Tilføj aktiv", + "asset": "Asset", + "assets": "Assets", + "management": "Assetadministration", + "view-assets": "Vis assets", + "add": "Tilføj asset", + "asset-type-max-length": "Assettype skal være mindre end 256 tegn", "assign-to-customer": "Tildel til kunde", - "assign-asset-to-customer": "Tildel aktiv(er) til kunde", - "assign-asset-to-customer-text": "Vælg de aktiver, der skal tildeles til kunden", - "no-assets-text": "Ingen aktiver fundet", - "assign-to-customer-text": "Vælg den kunde, der skal tildeles aktivet/aktiverne", + "assign-asset-to-customer": "Tildel asset(s) til kunde", + "assign-asset-to-customer-text": "Vælg venligst de assets, der skal tildeles kunden", + "no-assets-text": "Ingen assets fundet", + "assign-to-customer-text": "Vælg venligst kunden, som asset(s) skal tildeles", "public": "Offentlig", "assignedToCustomer": "Tildelt til kunde", - "make-public": "Gør aktiv offentligt", - "make-private": "Gør aktiv privat", - "unassign-from-customer": "Fjern tildeling fra kunde", - "delete": "Slet aktiv", - "asset-public": "Aktiv er offentligt", - "asset-type": "Aktivtype", - "asset-type-required": "Aktivtype er påkrævet.", - "select-asset-type": "Vælg aktivtype", - "enter-asset-type": "Indtast aktivtype", - "any-asset": "Ethvert aktiv", - "no-asset-types-matching": "", - "asset-type-list-empty": "Ingen aktivtyper valgt.", - "asset-types": "Aktivtyper", + "make-public": "Gør asset offentlig", + "make-private": "Gør asset privat", + "unassign-from-customer": "Fratildel fra kunde", + "delete": "Slet asset", + "asset-public": "Asset er offentlig", + "asset-type": "Assettype", + "asset-type-required": "Assettype er påkrævet.", + "select-asset-type": "Vælg assettype", + "enter-asset-type": "Indtast assetprofil", + "any-asset": "Enhver asset", + "no-asset-types-matching": "Ingen assettyper matcher '{{entitySubtype}}'.", + "asset-type-list-empty": "Ingen assettyper valgt.", + "asset-types": "Assettyper", "name": "Navn", "name-required": "Navn er påkrævet.", + "name-max-length": "Navn skal være mindre end 256 tegn", + "label-max-length": "Etiket skal være mindre end 256 tegn", "description": "Beskrivelse", "type": "Type", "type-required": "Type er påkrævet.", - "details": "Oplysninger", + "details": "Detaljer", "events": "Begivenheder", - "add-asset-text": "Tilføj nyt aktiv", - "asset-details": "Oplysninger om aktiv", - "assign-assets": "Tildel aktiver", - "assign-assets-text": "", - "delete-assets": "Slet aktiver", - "unassign-assets": "Fjern tildeling af aktiver", - "unassign-assets-action-title": "", - "assign-new-asset": "Tildel nyt aktiv", - "delete-asset-title": "", - "delete-asset-text": "Vær forsigtig. Efter bekræftelsen vil aktivet og alle relaterede data være uoprettelige.", - "delete-assets-title": "", - "delete-assets-action-title": "", - "delete-assets-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte aktiver blive fjernet, og alle relaterede data vil være uoprettelige.", - "make-public-asset-title": "", - "make-public-asset-text": "Efter bekræftelsen vil aktivet og alle dets data blive gjort offentlige og tilgængelige for andre.", - "make-private-asset-title": "", - "make-private-asset-text": "Efter bekræftelsen vil aktivet og alle dets data blive gjort private og vil ikke være tilgængelige for andre.", - "unassign-asset-title": "", - "unassign-asset-text": "Efter bekræftelsen fjernes tildelingen af aktivet og vil ikke være tilgængeligt for kunden.", - "unassign-asset": "Fjern tildeling af aktiv", - "unassign-assets-title": "", - "unassign-assets-text": "Efter bekræftelsen vil alle valgte aktiver få fjernet tildelingen og ikke være tilgængelige for kunden.", - "copyId": "Kopiér aktiv-id", - "idCopiedMessage": "Aktiv-id er blevet kopieret til udklipsholder", - "select-asset": "Vælg aktiv", - "no-assets-matching": "", - "asset-required": "Aktiv er påkrævet", - "name-starts-with": "Aktivnavn starter med", - "selected-assets": "", - "search": "Søg efter aktiver", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte aktiver", - "select-group-to-move": "Vælg målgruppe for at flytte valgte aktiver", - "remove-assets-from-group": "", - "group": "Aktivgruppe", - "list-of-groups": "", - "group-name-starts-with": "", - "import": "Importér aktiver", - "asset-file": "Aktivfil", - "label": "Mærkning" + "add-asset-text": "Tilføj nyt asset", + "asset-details": "Assetdetaljer", + "assign-assets": "Tildel assets", + "assign-assets-text": "Tildel { count, plural, =1 {1 asset} other {# assets} } til kunde", + "assign-asset-to-edge-title": "Tildel asset(s) til edge", + "assign-asset-to-edge-text": "Vælg venligst de assets, der skal tildeles til edge", + "delete-assets": "Slet assets", + "unassign-assets": "Fratildel assets", + "unassign-assets-action-title": "Fratildel { count, plural, =1 {1 asset} other {# assets} } fra kunde", + "assign-new-asset": "Tildel nyt asset", + "delete-asset-title": "Er du sikker på, at du vil slette asset '{{assetName}}'?", + "delete-asset-text": "Vær forsigtig, efter bekræftelse vil asset og alle relaterede data ikke kunne gendannes.", + "delete-assets-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 asset} other {# assets} }?", + "delete-assets-action-title": "Slet { count, plural, =1 {1 asset} other {# assets} }", + "delete-assets-text": "Vær forsigtig, efter bekræftelse vil alle valgte assets blive fjernet og dataene kan ikke gendannes.", + "make-public-asset-title": "Er du sikker på, at du vil gøre asset '{{assetName}}' offentlig?", + "make-public-asset-text": "Efter bekræftelse vil asset og alle dens data blive gjort offentlige og tilgængelige for andre.", + "make-private-asset-title": "Er du sikker på, at du vil gøre asset '{{assetName}}' privat?", + "make-private-asset-text": "Efter bekræftelse vil asset og alle dens data blive gjort private og ikke være tilgængelige for andre.", + "unassign-asset-title": "Er du sikker på, at du vil fratilde asset '{{assetName}}'?", + "unassign-asset-text": "Efter bekræftelse vil asset blive fratildelt og ikke være tilgængelig for kunden.", + "unassign-asset": "Fratildel asset", + "unassign-assets-title": "Er du sikker på, at du vil fratilde { count, plural, =1 {1 asset} other {# assets} }?", + "unassign-assets-text": "Efter bekræftelse vil alle valgte assets blive fratildelt og ikke være tilgængelige for kunden.", + "copyId": "Kopiér asset-ID", + "idCopiedMessage": "Asset-ID er blevet kopieret til udklipsholderen", + "select-asset": "Vælg asset", + "no-assets-matching": "Ingen assets matcher '{{entity}}'.", + "asset-required": "Asset er påkrævet", + "name-starts-with": "Asset-navneudtryk", + "help-text": "Brug '%' efter behov: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search": "Søg assets", + "import": "Importér assets", + "asset-file": "Assetfil", + "label": "Etiket", + "assign-asset-to-edge": "Tildel asset(s) til edge", + "unassign-asset-from-edge": "Fratildel asset", + "unassign-asset-from-edge-title": "Er du sikker på, at du vil fratilde asset '{{assetName}}'?", + "unassign-asset-from-edge-text": "Efter bekræftelse vil asset blive fratildelt og ikke være tilgængelig for edge.", + "unassign-assets-from-edge-title": "Er du sikker på, at du vil fratilde { count, plural, =1 {1 asset} other {# assets} }?", + "unassign-assets-from-edge-text": "Efter bekræftelse vil alle valgte assets blive fratildelt og ikke være tilgængelige for edge.", + "selected-assets": "{ count, plural, =1 {1 asset} other {# assets} } valgt" }, "attribute": { "attributes": "Attributter", "latest-telemetry": "Seneste telemetri", - "attributes-scope": "Omfang af entitetsattributter", + "no-latest-telemetry": "Ingen seneste telemetri", + "attributes-scope": "Enhedsattributters område", "scope-telemetry": "Telemetri", "scope-latest-telemetry": "Seneste telemetri", "scope-client": "Klientattributter", "scope-server": "Serverattributter", "scope-shared": "Delte attributter", + "scope-client-short": "Klient", + "scope-server-short": "Server", + "scope-shared-short": "Delt", + "scope-latest-short": "Seneste", + "scope-any": "Enhver", "add": "Tilføj attribut", - "add-attribute-prompt": "Tilføj venligst attribut", "key": "Nøgle", - "last-update-time": "Seneste opdateringstid", + "key-max-length": "Nøgle skal være mindre end 256 tegn", + "last-update-time": "Seneste opdateringstidspunkt", "key-required": "Attributnøgle er påkrævet.", "value": "Værdi", "value-required": "Attributværdi er påkrævet.", - "delete-attributes-title": "", - "delete-attributes-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte attributter blive fjernet.", + "telemetry-key-required": "Telemetrinøgle er påkrævet", + "telemetry-value-required": "Telemetriværdi er påkrævet", + "delete-attributes-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 attribut} other {# attributter} }?", + "delete-attributes-text": "Vær forsigtig, efter bekræftelse vil alle valgte attributter blive fjernet.", "delete-attributes": "Slet attributter", "enter-attribute-value": "Indtast attributværdi", - "show-on-widget": "Vis på widget", + "show-on-widget": "Vis i widget", "widget-mode": "Widget-tilstand", "next-widget": "Næste widget", "prev-widget": "Forrige widget", - "add-to-dashboard": "Føj til dashboard", - "add-widget-to-dashboard": "Føj widget til dashboard", - "selected-attributes": "", - "selected-telemetry": "", + "add-to-dashboard": "Tilføj til dashboard", + "add-widget-to-dashboard": "Tilføj widget til dashboard", + "selected-attributes": "{ count, plural, =1 {1 attribut} other {# attributter} } valgt", + "selected-telemetry": "{ count, plural, =1 {1 telemetri-enhed} other {# telemetri-enheder} } valgt", "no-attributes-text": "Ingen attributter fundet", - "no-telemetry-text": "Ingen telemetri fundet" + "no-telemetry-text": "Ingen telemetri fundet", + "copy-key": "Kopiér nøgle", + "add-telemetry": "Tilføj telemetri", + "copy-value": "Kopiér værdi", + "delete-timeseries": { + "start-time": "Starttidspunkt", + "ends-on": "Slutter den", + "strategy": "Strategi", + "delete-strategy": "Sletstrategi", + "all-data": "Slet alle data", + "all-data-except-latest-value": "Slet alle data undtagen seneste værdi", + "latest-value": "Slet seneste værdi", + "all-data-for-time-period": "Slet alle data for tidsperiode", + "rewrite-latest-value": "Overskriv seneste værdi" + } }, "api-usage": { - "api-usage": "Api-brug", + "api-features": "API-funktioner", + "api-usage": "API-forbrug", + "alarm": "Alarm", + "alarms-created": "Oprettede alarmer", + "queue-stats": "Køstatistik", + "processing-failures-and-timeouts": "Behandlingsfejl og timeouts", + "exceptions": "Undtagelser", + "alarms-created-daily-activity": "Daglig aktivitet for oprettede alarmer", + "alarms-created-hourly-activity": "Timebaseret aktivitet for oprettede alarmer", + "alarms-created-monthly-activity": "Månedlig aktivitet for oprettede alarmer", "data-points": "Datapunkter", - "data-points-storage-days": "Lagringsdage for datapunkter", + "data-points-storage-days": "Opbevaringsdage for datapunkter", + "device-api": "Device API", "email": "E-mail", "email-messages": "E-mailbeskeder", "email-messages-daily-activity": "Daglig aktivitet for e-mailbeskeder", - "email-messages-hourly-activity": "Timeaktivitet for e-mailbeskeder", "email-messages-monthly-activity": "Månedlig aktivitet for e-mailbeskeder", - "exceptions": "Undtagelser", "executions": "Udførelser", + "scripts": "Scripts", + "scripts-hourly-activity": "Timebaseret aktivitet for scripts", + "scripts-daily-activity": "Daglig aktivitet for scripts", + "scripts-monthly-activity": "Månedlig aktivitet for scripts", "javascript": "JavaScript", "javascript-executions": "JavaScript-udførelser", + "tbel": "TBEL", + "tbel-executions": "TBEL-udførelser", "latest-error": "Seneste fejl", "messages": "Beskeder", - "permanent-failures": "${entityName} Permanente fejl", - "permanent-timeouts": "${entityName} Permanente timeouts", - "processing-failures": "${entityName} Behandling af fejl", - "processing-failures-and-timeouts": "Behandling af fejl og timeouts", - "processing-timeouts": "${entityName} Behandling af timeouts", - "queue-stats": "Køstatistikker", - "rule-chain": "Regelkæde", - "rule-engine": "Regelprogram", - "rule-engine-daily-activity": "Regelmotors daglige aktivitet", - "rule-engine-executions": "Regelmotorudførelser", - "rule-engine-hourly-activity": "Regelmotors timeaktivitet", - "rule-engine-monthly-activity": "Regelmotors månedlige aktivitet", - "rule-engine-statistics": "Statistik for regelmotor", - "rule-node": "Regelknude", + "notifications": "Notifikationer", + "notifications-email-sms": "Notifikationer (E-mail/SMS)", + "notifications-hourly-activity": "Timebaseret aktivitet for notifikationer", + "permanent-failures": "${entityName} permanente fejl", + "permanent-timeouts": "${entityName} permanente timeouts", + "processing-failures": "${entityName} behandlingsfejl", + "processing-timeouts": "${entityName} behandlingstimeouts", + "rule-chain": "Rule Chain", + "rule-engine": "Rule Engine", + "rule-engine-daily-activity": "Daglig aktivitet for Rule Engine", + "rule-engine-executions": "Rule Engine-udførelser", + "rule-engine-hourly-activity": "Timebaseret aktivitet for Rule Engine", + "rule-engine-monthly-activity": "Månedlig aktivitet for Rule Engine", + "rule-engine-statistics": "Statistik for Rule Engine", + "rule-node": "Rule Node", "sms": "SMS", "sms-messages": "SMS-beskeder", "sms-messages-daily-activity": "Daglig aktivitet for SMS-beskeder", - "sms-messages-hourly-activity": "Timeaktivitet for SMS-beskeder", "sms-messages-monthly-activity": "Månedlig aktivitet for SMS-beskeder", - "successful": "${entityName} Vellykket", + "successful": "${entityName} succesfuld", "telemetry": "Telemetri", - "telemetry-persistence": "Telemetri-vedholdenhed", - "telemetry-persistence-daily-activity": "Daglig aktivitet for telemetri-vedholdenhed", - "telemetry-persistence-hourly-activity": "Timeaktivitet for telemetri-vedholdenhed", - "telemetry-persistence-monthly-activity": "Månedlig aktivitet for telemetri-vedholdenhed", + "telemetry-persistence": "Telemetri-persistens", + "telemetry-persistence-daily-activity": "Daglig aktivitet for telemetri-persistens", + "telemetry-persistence-hourly-activity": "Timebaseret aktivitet for telemetri-persistens", + "telemetry-persistence-monthly-activity": "Månedlig aktivitet for telemetri-persistens", "transport": "Transport", - "transport-daily-activity": "Daglig aktivitet for transport", - "transport-data-points": "Transportdatapunkter", - "transport-hourly-activity": "Timeaktivitet for transport", - "transport-messages": "Transportmeddelelser", - "transport-monthly-activity": "Månedlig aktivitet for transport", - "view-details": "Vis oplysninger", + "transport-daily-activity": "Daglig transportaktivitet", + "transport-data-points": "Transport-datapunkter", + "transport-hourly-activity": "Timebaseret transportaktivitet", + "transport-messages": "Transportbeskeder", + "transport-monthly-activity": "Månedlig transportaktivitet", + "view-details": "Vis detaljer", "view-statistics": "Vis statistik" }, + "api-limit": { + "cassandra-queries": "Cassandra-forespørgsler", + "entity-version-creation": "Oprettelse af enhedsversion", + "entity-version-load": "Indlæsning af enhedsversion", + "notification-requests": "Notifikationsanmodninger", + "notification-requests-per-rule": "Notifikationsanmodninger pr. regel", + "rest-api-requests": "REST API-anmodninger", + "rest-api-requests-per-customer": "REST API-anmodninger pr. kunde", + "transport-messages": "Transportbeskeder", + "transport-messages-per-device": "Transportbeskeder pr. device", + "transport-messages-per-gateway": "Transportbeskeder pr. gateway", + "transport-messages-per-gateway-device": "Transportbeskeder pr. gateway-enhed", + "ws-updates-per-session": "WS-opdateringer pr. session", + "edge-events": "Edge-hændelser", + "edge-events-per-edge": "Edge-hændelser pr. edge", + "edge-uplink-messages": "Edge-uplinkbeskeder", + "edge-uplink-messages-per-edge": "Edge-uplinkbeskeder pr. edge" + }, "audit-log": { - "audit": "Audit", - "audit-logs": "Auditlogs", + "audit": "Revision", + "audit-logs": "Revisionslogge", "timestamp": "Tidsstempel", - "entity-type": "Entitetstype", - "entity-name": "Entitetsnavn", + "entity-type": "Enhedstype", + "entity-name": "Enhedsnavn", "user": "Bruger", "type": "Type", "status": "Status", - "details": "Oplysninger", + "details": "Detaljer", "type-added": "Tilføjet", "type-deleted": "Slettet", "type-updated": "Opdateret", "type-attributes-updated": "Attributter opdateret", "type-attributes-deleted": "Attributter slettet", - "type-rpc-call": "RPC-opkald", - "type-credentials-updated": "Brugeroplysninger opdateret", + "type-rpc-call": "RPC-kald", + "type-credentials-updated": "Oplysninger opdateret", "type-assigned-to-customer": "Tildelt til kunde", - "type-unassigned-from-customer": "Tildeling fjernet fra kunde", + "type-unassigned-from-customer": "Fratildelt fra kunde", + "type-assigned-to-edge": "Tildelt til edge", + "type-unassigned-from-edge": "Fratildelt fra edge", "type-activated": "Aktiveret", - "type-suspended": "Indstillet", - "type-credentials-read": "Brugeroplysninger læst", + "type-suspended": "Suspenderet", + "type-credentials-read": "Oplysninger læst", "type-attributes-read": "Attributter læst", - "type-added-to-entity-group": "Tilføjet til gruppe", - "type-removed-from-entity-group": "Fjernet fra gruppe", "type-relation-add-or-update": "Relation opdateret", "type-relation-delete": "Relation slettet", "type-relations-delete": "Alle relationer slettet", - "type-alarm-ack": "Kvitteret", - "type-alarm-clear": "Ryddet", - "type-rest-api-rule-engine-call": "Regelmotor REST API-opkald", - "type-made-public": "Gjort offentligt", - "type-made-private": "Gjort privat", - "type-login": "Log på", - "type-logout": "Log af", - "type-lockout": "Spærring", + "type-alarm-ack": "Alarm anerkendt", + "type-alarm-clear": "Alarm ryddet", + "type-alarm-delete": "Alarm slettet", + "type-alarm-assign": "Alarm tildelt", + "type-alarm-unassign": "Alarm fratildelt", + "type-added-comment": "Kommentar tilføjet", + "type-updated-comment": "Kommentar opdateret", + "type-deleted-comment": "Kommentar slettet", + "type-login": "Login", + "type-logout": "Logout", + "type-lockout": "Låst ude", "status-success": "Succes", "status-failure": "Fejl", - "audit-log-details": "Oplysninger om auditlog", - "no-audit-logs-prompt": "Ingen logs fundet", + "audit-log-details": "Detaljer for revisionslog", + "no-audit-logs-prompt": "Ingen logge fundet", "action-data": "Handlingsdata", - "failure-details": "Fejloplysninger", - "search": "Søg efter auditlogfiler", + "failure-details": "Fejldetaljer", + "search": "Søg i revisionslogge", "clear-search": "Ryd søgning", "type-assigned-from-tenant": "Tildelt fra lejer", "type-assigned-to-tenant": "Tildelt til lejer", - "type-provision-success": "Enhed klargjort", - "type-provision-failure": "Enhed klargjort mislykkedes", + "type-provision-success": "Device klargjort", + "type-provision-failure": "Klargøring af device mislykkedes", "type-timeseries-updated": "Telemetri opdateret", "type-timeseries-deleted": "Telemetri slettet", - "type-owner-changed": "Ejer ændret" + "type-sms-sent": "SMS sendt" + }, + "debug-settings": { + "label": "Fejlsøgningskonfiguration", + "on-failure": "Kun fejl (24/7)", + "all-messages": "Alle beskeder ({{time}})", + "failures": "Fejl", + "entity": "enhed", + "hint": { + "main-limited": "Alle {{entity}} fejlsøgningsbeskeder vil blive hastighedsbegrænset, med maksimalt {{msg}} beskeder tilladt pr. {{time}}.", + "on-failure": "Gem alle fejlsøgningshændelser uden tidsbegrænsning.", + "all-messages": "Gem alle fejlsøgningshændelser i en tidsbegrænset periode." + } + }, + "calculated-fields": { + "expression": "Udtryk", + "no-found": "Ingen beregnede felter fundet", + "list": "{ count, plural, =1 {Et beregnet felt} other {Liste over # beregnede felter} }", + "selected-fields": "{ count, plural, =1 {1 beregnet felt} other {# beregnede felter} } valgt", + "type": { + "simple": "Simpel", + "script": "Script" + }, + "arguments": "Argumenter", + "decimals-by-default": "Decimaler som standard", + "debugging": "Fejlfinding af beregnet felt", + "argument-name": "Argumentnavn", + "datasource": "Datakilde", + "add-argument": "Tilføj argument", + "test-script-function": "Test scriptfunktion", + "no-arguments": "Ingen argumenter konfigureret", + "argument-settings": "Argumentindstillinger", + "argument-current": "Nuværende enhed", + "argument-current-tenant": "Nuværende lejer", + "argument-device": "Enhed", + "argument-asset": "Aktiv", + "argument-customer": "Kunde", + "argument-tenant": "Nuværende lejer", + "argument-type": "Argumenttype", + "see-debug-events": "Se fejlsøgningshændelser", + "attribute": "Attribut", + "copy-argument-name": "Kopiér argumentnavn", + "timeseries-key": "Tidsserienøgle", + "device-name": "Enhedsnavn", + "latest-telemetry": "Seneste telemetri", + "rolling": "Tidsserieglidning", + "attribute-scope": "Attributområde", + "server-attributes": "Serverattributter", + "client-attributes": "Klientattributter", + "shared-attributes": "Delte attributter", + "attribute-key": "Attributnøgle", + "default-value": "Standardværdi", + "limit": "Maks. værdier", + "time-window": "Tidsvindue", + "customer-name": "Kundenavn", + "asset-name": "Aktivnavn", + "timeseries": "Tidsserie", + "output": "Output", + "create": "Opret nyt beregnet felt", + "file": "Beregnet felt-fil", + "invalid-file-error": "Ugyldigt filformat. Sørg for, at filen er en gyldig JSON-fil.", + "import": "Importér beregnet felt", + "export": "Eksportér beregnet felt", + "export-failed-error": "Kan ikke eksportere beregnet felt: {{error}}", + "output-type": "Outputtype", + "delete-title": "Er du sikker på, at du vil slette det beregnede felt '{{title}}'?", + "delete-text": "Vær forsigtig, efter bekræftelse vil det beregnede felt og alle relaterede data ikke kunne gendannes.", + "delete-multiple-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 beregnet felt} other {# beregnede felter} }?", + "delete-multiple-text": "Vær forsigtig, efter bekræftelse vil alle valgte beregnede felter blive fjernet og alle relaterede data ikke kunne gendannes.", + "test-with-this-message": "Test med denne meddelelse", + "hint": { + "arguments-simple-with-rolling": "Beregnet felt af typen simpel må ikke indeholde nøgler med tidsserieglidningstype.", + "arguments-empty": "Argumenter må ikke være tomme.", + "expression-required": "Udtryk er påkrævet.", + "expression-invalid": "Udtryk er ugyldigt", + "expression-max-length": "Udtrykslængde skal være under 255 tegn.", + "argument-name-required": "Argumentnavn er påkrævet.", + "argument-name-pattern": "Argumentnavn er ugyldigt.", + "argument-name-duplicate": "Argument med dette navn findes allerede.", + "argument-name-max-length": "Argumentnavn skal være under 256 tegn.", + "argument-name-forbidden": "Argumentnavn er reserveret og kan ikke anvendes.", + "argument-type-required": "Argumenttype er påkrævet.", + "max-args": "Maksimalt antal argumenter er nået.", + "decimals-range": "Standard decimaler skal være et tal mellem 0 og 15.", + "expression": "Standardudtryk demonstrerer, hvordan man omregner temperatur fra Fahrenheit til Celsius.", + "arguments-entity-not-found": "Målentitet for argument ikke fundet." + } }, "confirm-on-exit": { - "message": "Du har ikke-gemte ændringer. Er du sikker på, at du vil forlade denne side?", - "html-message": "Du har ikke-gemte ændringer.
Er du sikker på, at du vil forlade denne side?", - "title": "Ugemte ændringer" + "message": "Du har ikke gemte ændringer. Er du sikker på, at du vil forlade denne side?", + "html-message": "Du har ikke gemte ændringer.
Er du sikker på, at du vil forlade denne side?", + "title": "Ikke gemte ændringer" }, "contact": { "country": "Land", + "country-required": "Land er påkrævet.", "city": "By", - "state": "Region", + "state": "Stat / Provins", "postal-code": "Postnummer", - "postal-code-invalid": "Ugyldigt postnummerformat.", + "postal-code-invalid": "Ugyldigt format for postnummer.", "address": "Adresse", "address2": "Adresse 2", "phone": "Telefon", "email": "E-mail", - "no-address": "Ingen adresse" + "no-address": "Ingen adresse", + "no-country-found": "Ingen lande fundet.", + "no-country-matching": "Ingen lande matchede '{{country}}'.", + "state-max-length": "Statens længde skal være mindre end 256 tegn", + "phone-max-length": "Telefonnummeret skal være mindre end 256 tegn", + "city-max-length": "Byens navn skal være mindre end 256 tegn" }, "common": { + "name": "Navn", + "type": "Type", + "general": "Generelt", "username": "Brugernavn", "password": "Adgangskode", + "data": "Data", + "timestamp": "Tidsstempel", "enter-username": "Indtast brugernavn", "enter-password": "Indtast adgangskode", "enter-search": "Indtast søgning", - "created-time": "Oprettelsestidspunkt", - "loading": "Indlæser..." - }, - "converter": { - "converter": "Dataomformer", - "converters": "Dataomformere", - "select-converter": "Vælg dataomformer", - "no-converters-matching": "", - "converter-required": "Dataomformer er påkrævet", - "delete": "Slet omformer", - "management": "Styring af dataomformere", - "add-converter-text": "Tilføj ny dataomformer", - "no-converters-text": "Ingen dataomformere fundet", - "selected-converters": "", - "delete-converter-title": "", - "delete-converter-text": "Vær forsigtig. Efter bekræftelsen vil dataomformeren og alle relaterede data være uoprettelige.", - "delete-converters-title": "", - "delete-converters-action-title": "", - "delete-converters-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte dataomformere blive fjernet, og alle relaterede data vil være uoprettelige.", - "events": "Begivenheder", - "add": "Tilføj dataomformer", - "search": "Søg efter dataomformere", - "converter-details": "Oplysninger om dataomformer", - "details": "Oplysninger", - "copyId": "Kopiér omformer-id", - "idCopiedMessage": "Omformer-id er blevet kopieret til udklipsholder", - "debug-mode": "Debug-tilstand", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "decoder": "Dekoder", - "encoder": "Indkoder", - "test-decoder-fuction": "Test af dekoderfunktion", - "test-encoder-fuction": "Test af indkoderfunktion", - "decoder-input-params": "Parametre for dekoderindgang", - "encoder-input-params": "Parametre for indkoderindgang", - "payload": "Data", - "payload-content-type": "Dataindholdstype", - "payload-content": "Dataindhold", - "message": "Meddelelse", - "message-type": "Meddelelsestype", - "message-type-required": "Meddelelsestype er påkrævet", - "test": "Test", - "metadata": "Metadata", - "metadata-required": "Metadataposter må ikke være tomme.", - "integration-metadata": "Integrationsmetadata", - "integration-metadata-required": "Integrationsmetadataposter må ikke være tomme.", + "created-time": "Oprettelsestid", + "disabled": "Deaktiveret", + "loading": "Indlæser...", + "proceed": "Fortsæt", + "open-details-page": "Åbn detaljeside", + "not-found": "Ikke fundet", + "value": "Værdi", + "documentation": "Dokumentation", + "time-left": "{{time}} tilbage", "output": "Output", - "import": "Importér omformer", - "export": "Eksportér omformer", - "export-failed-error": "", - "create-new-converter": "Opret ny omformer", - "converter-file": "Omformerfil", - "invalid-converter-file-error": "Omformeren kunne ikke importeres: Ugyldig omformerdatastruktur.", - "type": "Type", - "type-required": "Type er påkrævet.", - "type-uplink": "Uplink", - "type-downlink": "Downlink" + "suffix": { + "s": "s", + "ms": "ms" + }, + "hint": { + "name-required": "Navn er påkrævet.", + "name-pattern": "Navn er ugyldigt.", + "name-max-length": "Navn skal være under 256 tegn.", + "title-required": "Titel er påkrævet.", + "title-pattern": "Titel er ugyldig.", + "title-max-length": "Titel skal være under 256 tegn.", + "key-required": "Nøgle er påkrævet.", + "key-pattern": "Nøgle er ugyldig.", + "key-max-length": "Nøgle skal være under 256 tegn." + }, + "required-fields": "Mangler påkrævede felter" }, "content-type": { "json": "Json", "text": "Tekst", "binary": "Binær (Base64)" }, + "color": { + "color": "Farve" + }, "customer": { "customer": "Kunde", "customers": "Kunder", "management": "Kundeadministration", - "dashboard": "Kundens dashboard", - "dashboards": "Kundens dashboards", + "dashboard": "Kundedashboard", + "dashboards": "Kundedashboards", "devices": "Kundeenheder", - "entity-views": "Kundeentitetsvisninger", + "entity-views": "Kundeenhedsvisninger", "assets": "Kundeaktiver", "public-dashboards": "Offentlige dashboards", "public-devices": "Offentlige enheder", "public-assets": "Offentlige aktiver", - "public-entity-views": "Visninger af offentlig entitet", + "public-entity-views": "Offentlige enhedsvisninger", "add": "Tilføj kunde", "delete": "Slet kunde", - "manage-customer-user-groups": "Administrer kundebrugergrupper", - "manage-customer-groups": "Administrer kundegrupper", - "manage-customer-device-groups": "Administrer kundeenhedsgrupper", - "manage-customer-asset-groups": "Administrer kundeaktivgrupper", - "manage-customer-entity-view-groups": "Administrer visningsgrupper for kundeentitet", - "manage-customer-dashboard-groups": "Administrer kundedashboardgrupper", - "manage-customer-users": "Administrer kundebrugere", - "manage-customers": "Administrer kunder", - "manage-customer-devices": "Administrer kundeenheder", - "manage-customer-entity-views": "Administrer visninger af kundeentitet", - "manage-customer-dashboards": "Administrer kundedashboards", - "manage-public-devices": "Administrer offentlige enheder", - "manage-public-dashboards": "Administrer offentlige dashboards", - "manage-customer-assets": "Administrer kundeaktiver", - "manage-public-assets": "Administrer offentlige aktiver", + "manage-customer-users": "Administrér kundens brugere", + "manage-customer-devices": "Administrér kundens enheder", + "manage-customer-dashboards": "Administrér kundens dashboards", + "manage-public-devices": "Administrér offentlige enheder", + "manage-public-dashboards": "Administrér offentlige dashboards", + "manage-customer-assets": "Administrér kundens aktiver", + "manage-customer-edges": "Administrér kundens edge-instanser", + "manage-public-assets": "Administrér offentlige aktiver", "add-customer-text": "Tilføj ny kunde", "no-customers-text": "Ingen kunder fundet", - "customer-details": "Kundeinformation", - "delete-customer-title": "", - "delete-customer-text": "Vær forsigtig. Efter bekræftelsen vil kunden og alle relaterede data være uoprettelige.", - "delete-customers-title": "", - "delete-customers-action-title": "", - "delete-customers-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte kunder blive fjernet, og alle relaterede data vil være uoprettelige.", - "manage-user-groups": "Administrer brugergrupper", - "manage-asset-groups": "Administrer aktivgrupper", - "manage-device-groups": "Administrer enhedsgrupper", - "manage-dashboard-groups": "Administrer dashboardgrupper", - "manage-entity-view-groups": "Administrer entitetsvisningsgrupper", - "manage-users": "Administrer brugere", - "manage-assets": "Administrer aktiver", - "manage-devices": "Administrer enheder", - "manage-dashboards": "Administrer dashboards", + "customer-details": "Kundedetaljer", + "delete-customer-title": "Er du sikker på, at du vil slette kunden '{{customerTitle}}'?", + "delete-customer-text": "Vær forsigtig, efter bekræftelse vil kunden og alle tilknyttede data blive uoprettelige.", + "delete-customers-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 kunde} other {# kunder} }?", + "delete-customers-action-title": "Slet { count, plural, =1 {1 kunde} other {# kunder} }", + "delete-customers-text": "Vær forsigtig, efter bekræftelse vil alle valgte kunder blive fjernet og tilhørende data gå tabt.", + "manage-users": "Administrér brugere", + "manage-assets": "Administrér aktiver", + "manage-devices": "Administrér enheder", + "manage-dashboards": "Administrér dashboards", "title": "Titel", "title-required": "Titel er påkrævet.", + "title-max-length": "Titel skal være mindre end 256 tegn", "description": "Beskrivelse", - "details": "Oplysninger", + "details": "Detaljer", "events": "Begivenheder", - "copyId": "Kopiér kunde-id", - "idCopiedMessage": "Kunde-id er blevet kopieret til udklipsholder", + "copyId": "Kopiér kunde-ID", + "idCopiedMessage": "Kunde-ID er kopieret til udklipsholderen", "select-customer": "Vælg kunde", - "no-customers-matching": "", + "no-customers-matching": "Ingen kunder matcher '{{entity}}'.", "customer-required": "Kunde er påkrævet", - "selected-customers": "", - "search": "Søg efter kunder", - "select-group-to-add": "Vælg målgruppe for at tilføje udvalgte kunder", - "select-group-to-move": "Vælg målgruppe for at flytte udvalgte kunder", - "remove-customers-from-group": "", - "group": "Kundegruppe", - "list-of-groups": "", - "group-name-starts-with": "", "select-default-customer": "Vælg standardkunde", "default-customer": "Standardkunde", - "default-customer-required": "Standardkunde er påkrævet for at debugge dashboard på lejerniveau", - "allow-white-labeling": "Tillad hvid mærkning" - }, - "customers-hierarchy": { - "customers-hierarchy": "Kundehierarki", - "open-nav-tree": "Åbn navigationstræ", - "return-to-top-level": "Tilbage til øverste niveau" - }, - "custom-menu": { - "custom-menu": "Brugerdefineret menu", - "custom-menu-hint": "Definer brugerdefineret menu JSON nedenfor. Denne JSON indeholder en liste over brugerdefinerede menupunkter." - }, - "custom-translation": { - "custom-translation": "Brugerdefineret oversættelse", - "translation-map": "Oversættelsestilknytning", - "key": "Oversættelsesnøgle", - "import": "Importér oversættelse", - "export": "Eksportér oversættelse", - "export-data": "Eksportér oversættelsesdata", - "import-data": "Importér oversættelsesdata", - "translation-file": "Oversættelsesfil", - "invalid-translation-file-error": "Kunne ikke importere oversættelsesfil: Ugyldig oversættelsesdatastruktur.", - "custom-translation-hint": "Definer brugerdefineret oversættelse JSON nedenfor. Denne JSON overskriver standardoversættelsen. Klik på 'Download lokalfil' for at hente eksisterende oversættelse. Du kan også bruge den downloadede fil som reference til tilgængelige nøgleværdipar for oversættelse.", - "download-locale-file": "Download lokalfil" + "default-customer-required": "Standardkunde er påkrævet for at fejlfinde dashboard på Tenant-niveau", + "search": "Søg kunder", + "selected-customers": "{ count, plural, =1 {1 kunde} other {# kunder} } valgt", + "edges": "Kundens edge-instanser", + "manage-edges": "Administrér edge-instanser" + }, + "css-size": { + "size-value-required": "Størrelsesværdi er påkrævet", + "invalid-size-value": "Ugyldig størrelsesværdi" + }, + "date": { + "last-update-n-ago": "Seneste opdatering for N siden", + "last-update-n-ago-text": "Seneste opdatering {{ agoText }}", + "custom-date": "Brugerdefineret dato", + "format": "Format", + "preview": "Forhåndsvisning", + "auto": "Auto", + "time-granularity-formats": "Tidsgranitetsformater", + "unit-year": "År", + "unit-month": "Måneder", + "unit-day": "Dage", + "unit-hour": "Timer", + "unit-minute": "Minutter", + "unit-second": "Sekunder", + "unit-millisecond": "Millisekunder" }, "datetime": { "date-from": "Dato fra", - "time-from": "Tidspunkt fra", + "time-from": "Tid fra", "date-to": "Dato til", - "time-to": "Tidspunkt til" + "time-to": "Tid til", + "from": "Fra", + "to": "Til" }, "dashboard": { "dashboard": "Dashboard", @@ -774,202 +1233,289 @@ "view-dashboards": "Vis dashboards", "add": "Tilføj dashboard", "assign-dashboard-to-customer": "Tildel dashboard(s) til kunde", - "assign-dashboard-to-customer-text": "Vælg de dashboards, der skal tildeles til kunden", - "assign-to-customer-text": "Vælg den kunde, der skal tildeles dashboard(s)", + "assign-dashboard-to-customer-text": "Vælg venligst dashboards, der skal tildeles kunden", + "assign-to-customer-text": "Vælg venligst kunde, som dashboard(s) skal tildeles", "assign-to-customer": "Tildel til kunde", - "unassign-from-customer": "Fjern tildeling fra kunde", + "unassign-from-customer": "Fratildel fra kunde", "make-public": "Gør dashboard offentligt", "make-private": "Gør dashboard privat", - "manage-assigned-customers": "Administrer tildelte kunder", + "manage-assigned-customers": "Administrér tildelte kunder", "assigned-customers": "Tildelte kunder", "assign-to-customers": "Tildel dashboard(s) til kunder", - "assign-to-customers-text": "Vælg de kunder, der skal tildeles dashboard(s)", - "unassign-from-customers": "Fjern tildeling af dashboard(s) fra kunder", - "unassign-from-customers-text": "Vælg de kunder, hvor tildeling skal fjernes fra dashboard(s)", + "assign-to-customers-text": "Vælg venligst kunder, der skal tildeles dashboard(s)", + "unassign-from-customers": "Fratildel dashboard(s) fra kunder", + "unassign-from-customers-text": "Vælg venligst kunder, der skal fratildeles dashboard(s)", "no-dashboards-text": "Ingen dashboards fundet", "no-widgets": "Ingen widgets konfigureret", "add-widget": "Tilføj ny widget", + "add-widget-button-text": "Tilføj widget", "title": "Titel", + "image": "Dashboardbillede", + "mobile-app-settings": "Indstillinger for mobilapplikation", + "mobile-order": "Dashboardrækkefølge i mobilapplikation", + "mobile-hide": "Skjul dashboard i mobilapplikation", + "update-image": "Opdatér dashboardbillede", + "take-screenshot": "Tag skærmbillede", "select-widget-title": "Vælg widget", - "select-widget-value": "", - "select-widget-subtitle": "Liste over tilgængelige widget-typer", + "select-widget-value": "{{title}}: vælg widget", + "select-widget-subtitle": "Liste over tilgængelige widgettyper", "delete": "Slet dashboard", "title-required": "Titel er påkrævet.", + "title-max-length": "Titel skal være mindre end 256 tegn", "description": "Beskrivelse", - "details": "Oplysninger", - "dashboard-details": "Oplysninger om dashboard", + "details": "Detaljer", + "dashboard-details": "Dashboarddetaljer", "add-dashboard-text": "Tilføj nyt dashboard", "assign-dashboards": "Tildel dashboards", "assign-new-dashboard": "Tildel nyt dashboard", - "assign-dashboards-text": "", - "unassign-dashboards-action-text": "", + "assign-dashboards-text": "Tildel { count, plural, =1 {1 dashboard} other {# dashboards} } til kunder", + "unassign-dashboards-action-text": "Fratildel { count, plural, =1 {1 dashboard} other {# dashboards} } fra kunder", "delete-dashboards": "Slet dashboards", - "unassign-dashboards": "Fjern tildeling af dashboards", - "unassign-dashboards-action-title": "", - "delete-dashboard-title": "", - "delete-dashboard-text": "Vær forsigtig. Efter bekræftelsen vil dashboardet og alle relaterede data være uoprettelige.", - "delete-dashboards-title": "", - "delete-dashboards-action-title": "", - "delete-dashboards-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte dashboards blive fjernet, og alle relaterede data vil være uoprettelige.", - "unassign-dashboard-title": "", - "unassign-dashboard-text": "Efter bekræftelsen vil dashboardets tildeling blive fjernet og vil ikke være tilgængelig for kunden.", - "unassign-dashboard": "Fjern tildeling af dashboard", - "unassign-dashboards-title": "", - "unassign-dashboards-text": "Efter bekræftelsen vil tildelingen af alle valgte dashboards blive fjernet og vil ikke være tilgængelige for kunden.", - "public-dashboard-title": "Dashboard er nu offentligt", - "public-dashboard-text": "", + "unassign-dashboards": "Fratildel dashboards", + "unassign-dashboards-action-title": "Fratildel { count, plural, =1 {1 dashboard} other {# dashboards} } fra kunde", + "delete-dashboard-title": "Er du sikker på, at du vil slette dashboardet '{{dashboardTitle}}'?", + "delete-dashboard-text": "Vær forsigtig, efter bekræftelse vil dashboardet og alle relaterede data ikke kunne gendannes.", + "delete-dashboards-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 dashboard} other {# dashboards} }?", + "delete-dashboards-action-title": "Slet { count, plural, =1 {1 dashboard} other {# dashboards} }", + "delete-dashboards-text": "Vær forsigtig, efter bekræftelse vil alle valgte dashboards blive slettet og dataene ikke kunne gendannes.", + "unassign-dashboard-title": "Er du sikker på, at du vil fratilde dashboardet '{{dashboardTitle}}'?", + "unassign-dashboard-text": "Efter bekræftelse vil dashboardet blive fratildelt og ikke længere være tilgængeligt for kunden.", + "unassign-dashboard": "Fratildel dashboard", + "unassign-dashboards-title": "Er du sikker på, at du vil fratilde { count, plural, =1 {1 dashboard} other {# dashboards} }?", + "unassign-dashboards-text": "Efter bekræftelse vil alle valgte dashboards blive fratildelt og ikke længere være tilgængelige for kunden.", + "public-dashboard-title": "Dashboardet er nu offentligt", + "public-dashboard-text": "Dit dashboard {{dashboardTitle}} er nu offentligt og tilgængeligt via følgende offentlige link:", "public-dashboard-notice": "Bemærk: Glem ikke at gøre relaterede enheder offentlige for at få adgang til deres data.", - "public-dashboard-link": "Link til offentligt dashboard", - "public-dashboard-link-text": "", - "public-dashboard-link-notice": "Bemærk: Glem ikke at gøre relaterede enheder, aktiver og entitetsvisninger offentlige for at få adgang til deres data.", - "make-private-dashboard-title": "", - "make-private-dashboard-text": "Efter bekræftelsen vil dashboardet blive gjort privat og vil ikke være tilgængeligt for andre.", + "make-private-dashboard-title": "Er du sikker på, at du vil gøre dashboardet '{{dashboardTitle}}' privat?", + "make-private-dashboard-text": "Efter bekræftelse vil dashboardet blive gjort privat og ikke længere være tilgængeligt for andre.", "make-private-dashboard": "Gør dashboard privat", - "socialshare-text": "", - "socialshare-title": "", + "socialshare-text": "'{{dashboardTitle}}' drevet af ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' drevet af ThingsBoard", "select-dashboard": "Vælg dashboard", - "no-dashboards-matching": "", + "no-dashboards-matching": "Ingen dashboards matcher '{{entity}}'.", "dashboard-required": "Dashboard er påkrævet.", "select-existing": "Vælg eksisterende dashboard", "create-new": "Opret nyt dashboard", - "new-dashboard-title": "Ny dashboardtitel", + "new-dashboard-title": "Titel på nyt dashboard", "open-dashboard": "Åbn dashboard", "set-background": "Angiv baggrund", "background-color": "Baggrundsfarve", "background-image": "Baggrundsbillede", "background-size-mode": "Baggrundsstørrelsestilstand", "no-image": "Intet billede valgt", - "drop-image": "Træk og slip et billede, eller klik for at vælge en fil, der skal uploades.", - "maximum-upload-file-size": "", - "cannot-upload-file": "Filen kan ikke uploades", + "empty-image": "Intet billede", + "drop-image": "Slip et billede eller klik for at vælge en fil til upload.", + "maximum-upload-file-size": "Maksimal uploadfilstørrelse: {{ size }}", + "cannot-upload-file": "Kan ikke uploade fil", "settings": "Indstillinger", - "columns-count": "Kolonneantal", - "columns-count-required": "Kolonneantal er påkrævet.", - "min-columns-count-message": "Kun 10 minimum kolonneantal er tilladt.", - "max-columns-count-message": "Kun 1000 maksimum kolonneantal er tilladt.", - "widgets-margins": "Margin mellem widgets", + "move-all-widgets": "Flyt alle widgets", + "move-by": "Flyt med", + "cols": "kolonner", + "rows": "rækker", + "layout": "Layout", + "layout-type-default": "Standard", + "layout-type-scada": "SCADA", + "layout-type-divider": "Opdeler", + "layout-settings-type": "Layoutindstillinger: {{ type }} breakpoint", + "columns-count": "Antal kolonner", + "columns-count-required": "Antal kolonner er påkrævet.", + "min-columns-count-message": "Mindst 10 kolonner kræves.", + "max-columns-count-message": "Højst 1000 kolonner tilladt.", + "min-layout-width": "Minimal layoutbredde", + "columns-suffix": "kolonner", + "widgets-margins": "Afstand mellem widgets", "margin-required": "Marginværdi er påkrævet.", - "min-margin-message": "Kun 0 er tilladt som minimummarginværdi.", - "max-margin-message": "Kun 50 er tilladt som maksimummarginværdi.", - "horizontal-margin": "Horisontal margin", - "horizontal-margin-required": "Horisontal marginværdi er påkrævet.", - "min-horizontal-margin-message": "Kun 0 er tilladt som horisontal minimummarginværdi.", - "max-horizontal-margin-message": "Kun 50 er tilladt som horisontal maksimummarginværdi.", - "vertical-margin": "Vertikal margin", - "vertical-margin-required": "Vertikal marginværdi er påkrævet.", - "min-vertical-margin-message": "Kun 0 er tilladt som vertikal minimummarginværdi.", - "max-vertical-margin-message": "Kun 50 er tilladt som vertikal maksimummarginværdi.", - "autofill-height": "Autoudfyld layouthøjde", - "mobile-layout": "Mobile layoutindstillinger", - "mobile-row-height": "Mobil rækkehøjde, px", - "mobile-row-height-required": "Mobil rækkehøjdeværdi er påkrævet.", - "min-mobile-row-height-message": "Kun 5 pixel er tilladt som minimumværdi for mobilrækkehøjde.", - "max-mobile-row-height-message": "Kun 200 pixel er tilladt som maksimumværdi for mobilrækkehøjde.", + "min-margin-message": "Mindst tilladte marginværdi er 0.", + "max-margin-message": "Maksimalt tilladte marginværdi er 50.", + "horizontal-margin": "Vandret margin", + "horizontal-margin-required": "Værdi for vandret margin er påkrævet.", + "min-horizontal-margin-message": "Mindst tilladte vandrette marginværdi er 0.", + "max-horizontal-margin-message": "Maksimalt tilladte vandrette marginværdi er 50.", + "vertical-margin": "Lodret margin", + "vertical-margin-required": "Værdi for lodret margin er påkrævet.", + "min-vertical-margin-message": "Mindst tilladte lodrette marginværdi er 0.", + "max-vertical-margin-message": "Maksimalt tilladte lodrette marginværdi er 50.", + "apply-outer-margin": "Anvend margin på layoutets sider", + "autofill-height": "Autofyld layout-højde", + "mobile-layout": "Indstillinger for mobillayout", + "mobile-row-height": "Mobilrækkehøjde", + "mobile-row-height-required": "Værdi for mobilrækkehøjde er påkrævet.", + "min-mobile-row-height-message": "Mindst tilladte mobilrækkehøjde er 5 pixels.", + "max-mobile-row-height-message": "Maksimalt tilladte mobilrækkehøjde er 200 pixels.", + "row-height": "Rækkehøjde", + "row-height-required": "Værdi for rækkehøjde er påkrævet.", + "min-row-height-message": "Mindst tilladte rækkehøjde er 5 pixels.", + "max-row-height-message": "Maksimalt tilladte rækkehøjde er 200 pixels.", + "display-first-in-mobile-view": "Vis først i mobilvisning", + "title-settings": "Titelindstillinger", "display-title": "Vis dashboardtitel", - "toolbar-always-open": "Hold værktøjslinjen åben", "title-color": "Titelfarve", - "display-dashboards-selection": "Vis valg af dashboards", - "display-entities-selection": "Vis valg af entiteter", + "toolbar-settings": "Værktøjslinjeindstillinger", + "hide-toolbar": "Skjul værktøjslinje", + "toolbar-always-open": "Hold værktøjslinjen åben", + "display-dashboards-selection": "Vis dashboardvalg", + "display-entities-selection": "Vis enhedsvalg", "display-filters": "Vis filtre", "display-dashboard-timewindow": "Vis tidsvindue", "display-dashboard-export": "Vis eksport", + "display-update-dashboard-image": "Vis opdater dashboardbillede", + "dashboard-logo-settings": "Dashboard logoindstillinger", + "display-dashboard-logo": "Vis logo i dashboardets fuldskærmstilstand", + "dashboard-logo-image": "Dashboard logobillede", + "advanced-settings": "Avancerede indstillinger", + "dashboard-css": "Dashboard CSS", "import": "Importér dashboard", "export": "Eksportér dashboard", - "export-failed-error": "", - "export-pdf": "Eksportér som PDF", - "export-png": "Eksportér som PNG", - "export-jpg": "Eksportér som JPEG", - "export-json-config": "Eksportér JSON-konfiguration", - "download-dashboard-progress": "", + "export-failed-error": "Kunne ikke eksportere dashboard: {{error}}", + "export-prompt": "Indlejr dashboardbilleder og ressourcer", "create-new-dashboard": "Opret nyt dashboard", "dashboard-file": "Dashboardfil", - "invalid-dashboard-file-error": "Kunne ikke importere dashboard: Ugyldig dashboarddatastruktur.", - "dashboard-import-missing-aliases-title": "Konfigurer aliasser anvendt af importeret dashboard", + "invalid-dashboard-file-error": "Kan ikke importere dashboard: Ugyldig datastruktur.", + "dashboard-import-missing-aliases-title": "Konfigurer aliaser brugt i det importerede dashboard", "create-new-widget": "Opret ny widget", "import-widget": "Importér widget", - "widget-file": "Widget-fil", - "invalid-widget-file-error": "Kan ikke importere widget: Ugyldig widget-datastruktur.", - "widget-import-missing-aliases-title": "Konfigurer aliasser, der bruges af importeret widget", - "open-toolbar": "Skjul dashboardets værktøjslinje", + "widget-file": "Widgetfil", + "invalid-widget-file-error": "Kan ikke importere widget: Ugyldig widgetdatastruktur.", + "widget-import-missing-aliases-title": "Konfigurer aliaser brugt i den importerede widget", + "open-toolbar": "Åbn dashboardværktøjslinje", "close-toolbar": "Luk værktøjslinje", "configuration-error": "Konfigurationsfejl", - "alias-resolution-error-title": "Konfigurationsfejl i dashboardaliasser", - "invalid-aliases-config": "Kunne ikke finde nogen enheder, der matcher nogle af aliasfiltrene.
Kontakt din administrator for at løse dette problem.", + "alias-resolution-error-title": "Fejl i dashboard-alias-konfiguration", + "invalid-aliases-config": "Kunne ikke finde nogen enheder, der matcher nogle af aliasfiltrene.
Kontakt venligst din administrator for at løse problemet.", "select-devices": "Vælg enheder", - "assignedToCustomer": "Tildelt til kunde", - "assignedToCustomers": "Tildelt til kunder", + "assignedToCustomer": "Tildelt kunde", + "assignedToCustomers": "Tildelt kunder", "public": "Offentlig", + "copyId": "Kopiér dashboard-id", + "idCopiedMessage": "Dashboard-id er blevet kopieret til udklipsholderen", "public-link": "Offentligt link", "copy-public-link": "Kopiér offentligt link", - "public-link-copied-message": "Dashboardets offentilige link er blevet kopieret til udklipsholderen", - "manage-states": "Administrer dashboardtilstande", + "public-link-copied-message": "Offentligt dashboardlink er blevet kopieret til udklipsholderen", + "manage-states": "Administrér dashboardtilstande", "states": "Dashboardtilstande", - "search-states": "Søg efter dashboardtilstande", - "selected-states": "", + "states-short": "Tilstande", + "search-states": "Søg i dashboardtilstande", + "selected-states": "{ count, plural, =1 {1 dashboardtilstand} other {# dashboardtilstande} } valgt", "edit-state": "Rediger dashboardtilstand", "delete-state": "Slet dashboardtilstand", "add-state": "Tilføj dashboardtilstand", "no-states-text": "Ingen tilstande fundet", "state": "Dashboardtilstand", "state-name": "Navn", - "state-name-required": "Dashboardtilstandsnavn er påkrævet.", + "state-name-required": "Dashboardtilstandens navn er påkrævet.", "state-id": "Tilstands-id", "state-id-required": "Dashboardtilstands-id er påkrævet.", - "state-id-exists": "Dashboardtilstand med samme id findes allerede.", + "state-id-exists": "Der findes allerede en dashboardtilstand med samme id.", "is-root-state": "Rodtilstand", "delete-state-title": "Slet dashboardtilstand", - "delete-state-text": "", - "show-details": "Vis oplysninger", - "hide-details": "Skjul oplysninger", - "select-state": "Vælg måltilstand", - "state-controller": "Tilstandscontroller", - "selected-dashboards": "", - "search": "Søg efter dashboards", - "home-dashboard": "Startside", - "home-dashboard-hide-toolbar": "Skjul startsidens værktøjslinje", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte dashboards", - "select-group-to-move": "Vælg målgruppe for at flytte valgte dashboards", - "remove-dashboards-from-group": "", - "group": "Gruppe af dashboards", - "list-of-groups": "", - "group-name-starts-with": "" + "delete-state-text": "Er du sikker på, at du vil slette dashboardtilstanden med navnet '{{stateName}}'?", + "show-details": "Vis detaljer", + "hide-details": "Skjul detaljer", + "select-state": "Vælg måldashboardtilstand", + "state-controller": "Tilstandskontrol", + "state-controller-default": "statisk (forældet)", + "search": "Søg dashboards", + "selected-dashboards": "{ count, plural, =1 {1 dashboard} other {# dashboards} } valgt", + "home-dashboard": "Startdashboard", + "home-dashboard-hide-toolbar": "Skjul værktøjslinje i startdashboard", + "unassign-dashboard-from-edge-text": "Efter bekræftelse vil dashboardet blive fratildelt og ikke være tilgængeligt for edge.", + "unassign-dashboards-from-edge-title": "Er du sikker på, at du vil fratilde { count, plural, =1 {1 dashboard} other {# dashboards} }?", + "unassign-dashboards-from-edge-text": "Efter bekræftelse vil alle valgte dashboards blive fratildelt og ikke være tilgængelige for edge.", + "assign-dashboard-to-edge": "Tildel dashboard(s) til edge", + "assign-dashboard-to-edge-text": "Vælg venligst dashboards, der skal tildeles til edge", + "non-existent-dashboard-state-error": "Dashboardtilstand med id \"{{ stateId }}\" blev ikke fundet", + "edit-mode": "Redigeringstilstand", + "duplicate-state-action": "Duplikér tilstand", + "breakpoint-value": "Breakpoint ({{ value }})", + "breakpoints-id": { + "default": "Standard", + "xs": "Mobil (xs)", + "sm": "Tablet (sm)", + "md": "Laptop (md)", + "lg": "Desktop (lg)", + "xl": "Desktop (xl)" + }, + "view-format-type-grid": "Gitter", + "view-format-type-list": "Liste", + "view-format": "Visningsformat" }, "datakey": { "settings": "Indstillinger", - "advanced": "Fremskreden", - "label": "Mærkning", + "general": "Generelt", + "advanced": "Avanceret", + "key": "Nøgle", + "keys": "Nøgler", + "label": "Etiket", "color": "Farve", - "units": "Specielt symbol, der vises ved siden af værdi", - "decimals": "Antal cifre efter flydende punkt", + "units": "Specielt symbol, der vises ved siden af værdien", + "decimals": "Antal decimaler", "data-generation-func": "Datagenereringsfunktion", "use-data-post-processing-func": "Brug dataefterbehandlingsfunktion", - "configuration": "Konfiguration af datanøgle", - "timeseries": "Tidsserier", + "configuration": "Datakey-konfiguration", + "timeseries": "Tidsserie", "attributes": "Attributter", - "entity-field": "Entitetsfelt", + "entity-field": "Enhedsfelt", "alarm": "Alarmfelter", - "timeseries-required": "Entitets tidsserier er påkrævet.", - "timeseries-or-attributes-required": "Entitets tidsserier/attributter er påkrævet.", - "alarm-fields-timeseries-or-attributes-required": "Alarmfelter eller entitets tidsserier/attributter er påkrævet.", - "maximum-timeseries-or-attributes": "", + "timeseries-required": "Enhedens tidsserier er påkrævet.", + "timeseries-or-attributes-required": "Enhedens tidsserier/attributter er påkrævet.", + "alarm-fields-timeseries-or-attributes-required": "Alarmfelter eller enhedens tidsserier/attributter er påkrævet.", + "maximum-timeseries-or-attributes": "Maksimalt { count, plural, =1 {1 tidsserie/attribut er tilladt.} other {# tidsserier/attributter er tilladt} }", "alarm-fields-required": "Alarmfelter er påkrævet.", "function-types": "Funktionstyper", + "function-type": "Funktionstype", "function-types-required": "Funktionstyper er påkrævet.", - "maximum-function-types": "", - "time-description": "tidsstempel for den aktuelle værdi", - "value-description": "den aktuelle værdi", - "prev-value-description": "resultat af forrige funktionskald", - "time-prev-description": "tidsstempel for den forrige værdi", - "prev-orig-value-description": "oprindelig tidligere værdi" + "data-keys": "Datataster", + "data-key": "Datatast", + "data-keys-required": "Datataster er påkrævet.", + "data-key-required": "Datatast er påkrævet.", + "alarm-keys": "Alarm-datataster", + "alarm-key": "Alarm-datatast", + "alarm-key-functions": "Alarm-funktioner", + "alarm-key-function": "Alarm-funktion", + "latest-keys": "Seneste datataster", + "latest-key": "Seneste datatast", + "latest-key-functions": "Seneste nøglefunktioner", + "latest-key-function": "Seneste nøglefunktion", + "timeseries-keys": "Tidsserie-datataster", + "timeseries-key": "Tidsserie-datatast", + "timeseries-key-functions": "Tidsserie-funktioner", + "timeseries-key-function": "Tidsserie-funktion", + "maximum-function-types": "Maksimalt { count, plural, =1 {1 funktionstype er tilladt.} other {# funktionstyper er tilladt} }", + "time-description": "tidsstempel for den aktuelle værdi;", + "value-description": "den aktuelle værdi;", + "prev-value-description": "resultat af forrige funktionskald;", + "time-prev-description": "tidsstempel for forrige værdi;", + "prev-orig-value-description": "oprindelig forrige værdi;", + "aggregation": "Aggregering", + "aggregation-type-hint-common": "Af ydeevnehensyn er aggregeringsberegning kun tilgængelig for faste tidsintervaller såsom \"nuværende dag\", \"nuværende måned\" osv., og ikke for glidende vinduer som 'sidste 30 minutter' eller 'sidste 24 timer'.", + "aggregation-type-none-hint": "Tag seneste værdi.", + "aggregation-type-min-hint": "Find minimumsværdien blandt datapunkter i det valgte tidsvindue.", + "aggregation-type-max-hint": "Find maksimumsværdien blandt datapunkter i det valgte tidsvindue.", + "aggregation-type-avg-hint": "Beregn gennemsnitsværdi blandt datapunkter i det valgte tidsvindue.", + "aggregation-type-sum-hint": "Summér alle værdier af datapunkterne i det valgte tidsvindue.", + "aggregation-type-count-hint": "Samlet antal datapunkter i det valgte tidsvindue.", + "delta-calculation": "Delta-beregning", + "enable-delta-calculation": "Aktivér delta-beregning", + "enable-delta-calculation-hint": "Når aktiveret, beregnes nøgleværdien ud fra aggregerede værdier for et valgt tidsvindue og en angivet sammenligningsperiode. Af ydeevnehensyn er delta-beregning kun tilgængelig for historiske tidsvinduer og ikke for realtidsværdier. F.eks. kan du beregne forskellen i energiforbrug i går sammenlignet med dagen før.", + "delta-calculation-result": "Resultat af delta-beregning", + "delta-calculation-result-previous-value": "Tidligere værdi", + "delta-calculation-result-delta-absolute": "Delta (absolut)", + "delta-calculation-result-delta-percent": "Delta (procent)", + "source": "Kilde", + "latest": "Seneste", + "latest-value": "Seneste værdi", + "delta": "delta", + "percent": "procent", + "absolute": "absolut" }, "datasource": { "type": "Datakildetype", "name": "Navn", - "label": "Mærkning", - "add-datasource-prompt": "Tilføj datakilde" + "label": "Etiket", + "add-datasource-prompt": "Tilføj venligst en datakilde" }, "details": { - "details": "Oplysninger", + "details": "Detaljer", "edit-mode": "Redigeringstilstand", "edit-json": "Rediger JSON", "toggle-edit-mode": "Skift redigeringstilstand" @@ -981,256 +1527,515 @@ "management": "Enhedsadministration", "view-devices": "Vis enheder", "device-alias": "Enhedsalias", + "device-type-max-length": "Enhedstype må være under 256 tegn", "aliases": "Enhedsaliasser", - "no-alias-matching": "", + "no-alias-matching": "'{{alias}}' blev ikke fundet.", "no-aliases-found": "Ingen aliasser fundet.", - "no-key-matching": "", + "no-key-matching": "'{{key}}' blev ikke fundet.", "no-keys-found": "Ingen nøgler fundet.", "create-new-alias": "Opret en ny!", "create-new-key": "Opret en ny!", - "duplicate-alias-error": "", - "configure-alias": "", - "no-devices-matching": "", + "duplicate-alias-error": "Dubletalias fundet '{{alias}}'.
Enhedsaliasser skal være unikke i dashboardet.", + "configure-alias": "Konfigurér alias '{{alias}}'", + "no-devices-matching": "Ingen enheder matcher '{{entity}}'.", "alias": "Alias", "alias-required": "Enhedsalias er påkrævet.", "remove-alias": "Fjern enhedsalias", "add-alias": "Tilføj enhedsalias", - "name-starts-with": "Enhedens navn starter med", + "name-starts-with": "Udtryk for enhedsnavn", + "help-text": "Brug '%' efter behov: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", "device-list": "Enhedsliste", - "use-device-name-filter": "Anvend filter", + "use-device-name-filter": "Brug filter", "device-list-empty": "Ingen enheder valgt.", - "device-name-filter-required": "Enhedsnavnfilter er påkrævet.", - "device-name-filter-no-device-matched": "", + "device-name-filter-required": "Filter for enhedsnavn er påkrævet.", + "device-name-filter-no-device-matched": "Ingen enheder der starter med '{{device}}' blev fundet.", "add": "Tilføj enhed", "assign-to-customer": "Tildel til kunde", "assign-device-to-customer": "Tildel enhed(er) til kunde", - "assign-device-to-customer-text": "Vælg de enheder, der skal tildeles kunden", - "make-public": "Gør enheden offentlig", - "make-private": "Gør enheden privat", + "assign-device-to-customer-text": "Vælg venligst enheder, der skal tildeles kunden", + "make-public": "Gør enhed offentlig", + "make-private": "Gør enhed privat", "no-devices-text": "Ingen enheder fundet", - "assign-to-customer-text": "Vælg den kunde, der skal tilknyttes enheden/enhederne", - "device-details": "Enhedsoplysninger", + "assign-to-customer-text": "Vælg venligst en kunde, som enheden skal tildeles", + "device-details": "Enhedsdetaljer", "add-device-text": "Tilføj ny enhed", - "credentials": "Brugeroplysninger", - "manage-credentials": "Administrer brugeroplysninger", + "credentials": "Oplysninger", + "manage-credentials": "Administrér oplysninger", "delete": "Slet enhed", "assign-devices": "Tildel enheder", - "assign-devices-text": "", + "assign-devices-text": "Tildel { count, plural, =1 {1 enhed} other {# enheder} } til kunde", "delete-devices": "Slet enheder", - "unassign-from-customer": "Fjern tildeling fra kunde", - "unassign-devices": "Fjern tildeling af enheder", - "unassign-devices-action-title": "", + "unassign-from-customer": "Fratildel fra kunde", + "unassign-devices": "Fratildel enheder", + "unassign-devices-action-title": "Fratildel { count, plural, =1 {1 enhed} other {# enheder} } fra kunde", + "unassign-device-from-edge-title": "Er du sikker på, at du vil fratilde enheden '{{deviceName}}'?", + "unassign-device-from-edge-text": "Efter bekræftelse vil enheden blive fratildelt og ikke længere være tilgængelig for edge.", + "unassign-devices-from-edge": "Fratildel enheder fra edge", "assign-new-device": "Tildel ny enhed", - "make-public-device-title": "", - "make-public-device-text": "Efter bekræftelsen vil enheden og alle dens data blive gjort offentlige og tilgængelige for andre.", - "make-private-device-title": "", - "make-private-device-text": "Efter bekræftelsen vil enheden og alle dens data blive gjort private og vil ikke være tilgængelige for andre.", - "view-credentials": "Vis brugeroplysninger", - "delete-device-title": "", - "delete-device-text": "Vær forsigtig. Efter bekræftelsen vil enheden og alle relaterede data være uoprettelige.", - "delete-devices-title": "", - "delete-devices-action-title": "", - "delete-devices-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte enheder blive fjernet, og alle relaterede data vil være uoprettelige.", - "unassign-device-title": "", - "unassign-device-text": "Efter bekræftelsen vil enhedens tildeling blive fjernet og vil ikke være tilgængelig for kunden.", - "unassign-device": "Fjern tildeling af enhed", - "unassign-devices-title": "", - "unassign-devices-text": "Efter bekræftelsen vil tildelingen af alle valgte enheder blive fjernet og vil ikke være tilgængelige for kunden.", - "device-credentials": "Enhedsbrugeroplysninger", - "credentials-type": "Type af brugeroplysninger", + "make-public-device-title": "Er du sikker på, at du vil gøre enheden '{{deviceName}}' offentlig?", + "make-public-device-text": "Efter bekræftelse vil enheden og alle dens data være offentlige og tilgængelige for andre.", + "make-private-device-title": "Er du sikker på, at du vil gøre enheden '{{deviceName}}' privat?", + "make-private-device-text": "Efter bekræftelse vil enheden og alle dens data være private og ikke længere tilgængelige for andre.", + "view-credentials": "Vis oplysninger", + "delete-device-title": "Er du sikker på, at du vil slette enheden '{{deviceName}}'?", + "delete-device-text": "Vær forsigtig, efter bekræftelse vil enheden og alle relaterede data ikke kunne gendannes.", + "delete-devices-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 enhed} other {# enheder} }?", + "delete-devices-action-title": "Slet { count, plural, =1 {1 enhed} other {# enheder} }", + "delete-devices-text": "Vær forsigtig, efter bekræftelse vil alle valgte enheder blive slettet og alle relaterede data vil gå tabt.", + "unassign-device-title": "Er du sikker på, at du vil fratilde enheden '{{deviceName}}'?", + "unassign-device-text": "Efter bekræftelse vil enheden blive fratildelt og ikke længere være tilgængelig for kunden.", + "unassign-device": "Fratildel enhed", + "unassign-devices-title": "Er du sikker på, at du vil fratilde { count, plural, =1 {1 enhed} other {# enheder} }?", + "unassign-devices-text": "Efter bekræftelse vil alle valgte enheder blive fratildelt og ikke længere være tilgængelige for kunden.", + "device-credentials": "Enhedsoplysninger", + "loading-device-credentials": "Indlæser enhedsoplysninger...", + "credentials-type": "Oplysningstype", "access-token": "Adgangstoken", "access-token-required": "Adgangstoken er påkrævet.", - "access-token-invalid": "Adgangstokenlængden skal være fra 1 til 20 tegn.", - "rsa-key": "RSA offentlig nøgle", - "rsa-key-required": "RSA offentlig nøgle er påkrævet.", - "client-id": "Klient-id", + "access-token-invalid": "Adgangstoken skal være mellem 1 og 32 tegn.", + "certificate-pem-format": "Certifikat i PEM-format", + "certificate-pem-format-required": "Certifikat er påkrævet.", + "copy-access-token": "Kopiér adgangstoken", + "copy-certificate": "Kopiér certifikat", + "copy-client-id": "Kopiér klient-id", + "copy-user-name": "Kopiér brugernavn", + "copy-password": "Kopiér adgangskode", + "generate-client-id": "Generér klient-id", + "generate-user-name": "Generér brugernavn", + "generate-password": "Generér adgangskode", + "generate-access-token": "Generér adgangstoken", + "lwm2m-security-config": { + "identity": "Klientidentitet", + "identity-required": "Klientidentitet er påkrævet.", + "identity-tooltip": "PSK-identifikatoren er en vilkårlig identifikator op til 128 bytes, som beskrevet i standarden [RFC7925].\nIdentifikatoren SKAL først konverteres til en streng og derefter kodes til oktetter med UTF-8.", + "client-key": "Klientnøgle", + "client-key-required": "Klientnøgle er påkrævet.", + "client-key-tooltip-prk": "RPK offentlig nøgle eller id skal følge standarden [RFC7250] og være kodet i Base64!", + "client-key-tooltip-psk": "PSK-nøgle skal følge standarden [RFC4279] og være i HexDec-format: 32, 64, 128 tegn!", + "endpoint": "Endpoint-klientnavn", + "endpoint-required": "Endpoint-klientnavn er påkrævet.", + "client-public-key": "Klientens offentlige nøgle", + "client-public-key-hint": "Hvis den offentlige nøgle er tom, vil det betroede certifikat blive brugt", + "client-public-key-tooltip": "X509 offentlig nøgle skal være i DER-kodet X509v3-format, udelukkende understøtte EC-algoritmen og derefter være Base64-kodet!", + "mode": "Sikkerhedskonfigurationstilstand", + "client-tab": "Klientens sikkerhedskonfiguration", + "client-certificate": "Klientcertifikat", + "bootstrap-tab": "Bootstrap-klient", + "bootstrap-server": "Bootstrap-server", + "lwm2m-server": "LwM2M-server", + "client-publicKey-or-id": "Klientens offentlige nøgle eller ID", + "client-publicKey-or-id-required": "Klientens offentlige nøgle eller ID er påkrævet.", + "client-publicKey-or-id-tooltip-psk": "PSK-identifikator er en vilkårlig identifikator op til 128 bytes som beskrevet i standarden [RFC7925].\nDen SKAL konverteres til en streng og kodes med UTF-8.", + "client-publicKey-or-id-tooltip-rpk": "RPK offentlig nøgle eller ID skal være i henhold til standard [RFC7250] og Base64-kodet!", + "client-publicKey-or-id-tooltip-x509": "X509 offentlig nøgle skal være i DER-kodet X509v3-format og udelukkende understøtte EC-algoritmen og derefter være Base64-kodet", + "client-secret-key": "Klientens hemmelige nøgle", + "client-secret-key-required": "Klientens hemmelige nøgle er påkrævet.", + "client-secret-key-tooltip-psk": "PSK-nøgle skal følge standarden [RFC4279] og være i HexDec-format: 32, 64, 128 tegn!", + "client-secret-key-tooltip-prk": "RPK hemmelige nøgle skal være i PKCS_8-format (DER-kodning, standard [RFC5958]) og derefter Base64-kodet!", + "client-secret-key-tooltip-x509": "X509 hemmelige nøgle skal være i PKCS_8-format (DER-kodning, standard [RFC5958]) og derefter Base64-kodet!" + }, + "client-id": "Klient-ID", "client-id-pattern": "Indeholder ugyldigt tegn.", "user-name": "Brugernavn", "user-name-required": "Brugernavn er påkrævet.", - "client-id-or-user-name-necessary": "Klient-id og/eller brugernavn er nødvendige", + "client-id-or-user-name-necessary": "Klient-ID og/eller brugernavn er nødvendige", "password": "Adgangskode", - "secret": "Hemmelig", - "secret-required": "Hemmelig er påkrævet.", - "device-type": "Enhedstype", + "secret": "Hemmelighed", + "secret-required": "Hemmelighed er påkrævet.", + "device-type": "Enhedsprofil", "device-type-required": "Enhedstype er påkrævet.", "select-device-type": "Vælg enhedstype", - "enter-device-type": "Indtast enhedstype", + "enter-device-type": "Indtast enhedsprofil", "any-device": "Enhver enhed", - "no-device-types-matching": "", - "device-type-list-empty": "Ingen enhedstyper valgt.", + "no-device-types-matching": "Ingen enhedsprofiler matcher '{{entitySubtype}}'.", + "device-type-list-empty": "Ingen enhedsprofiler valgt!", + "device-profile-type-list-empty": "Mindst én enhedsprofil skal vælges.", "device-types": "Enhedstyper", "name": "Navn", "name-required": "Navn er påkrævet.", + "name-max-length": "Navn må være under 256 tegn", + "label-max-length": "Etiket må være under 256 tegn", "description": "Beskrivelse", - "label": "Mærkning", - "events": "Begivenheder", - "details": "Oplysninger", - "copyId": "Kopiér enheds-id", + "label": "Etiket", + "events": "Hændelser", + "details": "Detaljer", + "copyId": "Kopiér enheds-ID", "copyAccessToken": "Kopiér adgangstoken", - "copy-mqtt-authentication": "Kopiér MQTT-brugeroplysninger", - "idCopiedMessage": "Enheds-id er blevet kopieret til udklipsholder", - "accessTokenCopiedMessage": "Enhedsadgangstoken er blevet kopieret til udklipsholder", - "mqtt-authentication-copied-message": "Enhedens MQTT-godkendelse er blevet kopieret til udklipsholder", - "assignedToCustomer": "Tildelt til kunde", + "copy-mqtt-authentication": "Kopiér MQTT-legitimationsoplysninger", + "idCopiedMessage": "Enheds-ID er kopieret til udklipsholderen", + "accessTokenCopiedMessage": "Enheds adgangstoken er kopieret til udklipsholderen", + "mqtt-authentication-copied-message": "Enhedens MQTT-legitimationsoplysninger er kopieret til udklipsholderen", + "assignedToCustomer": "Tildelt kunde", "unable-delete-device-alias-title": "Kan ikke slette enhedsalias", - "unable-delete-device-alias-text": "", + "unable-delete-device-alias-text": "Enhedsalias '{{deviceAlias}}' kan ikke slettes, da det bruges af følgende widget(s):
{{widgetsList}}", "is-gateway": "Er gateway", "overwrite-activity-time": "Overskriv aktivitetstid for tilsluttet enhed", + "device-filter": "Enhedsfilter", + "device-filter-title": "Enhedsfilter", + "filter-title": "Filter", + "device-state": "Enhedstilstand", + "state": "Tilstand", + "any": "Enhver", + "active": "Aktiv", + "inactive": "Inaktiv", "public": "Offentlig", "device-public": "Enheden er offentlig", "select-device": "Vælg enhed", - "selected-devices": "", - "search": "Søg efter enheder", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte enheder", - "select-group-to-move": "Vælg målgruppe for at flytte valgte enheder", - "remove-devices-from-group": "", - "group": "Gruppe af enheder", - "list-of-groups": "", - "group-name-starts-with": "", "import": "Importér enhed", "device-file": "Enhedsfil", + "search": "Søg enheder", + "selected-devices": "{ count, plural, =1 {1 enhed} other {# enheder} } valgt", "device-configuration": "Enhedskonfiguration", "transport-configuration": "Transportkonfiguration", "wizard": { - "device-details": "Enhedsoplysninger" + "device-details": "Enhedsdetaljer" + }, + "unassign-devices-from-edge-title": "Er du sikker på, at du vil fratilde { count, plural, =1 {1 enhed} other {# enheder} }?", + "unassign-devices-from-edge-text": "Efter bekræftelse vil alle valgte enheder blive fratildelt og ikke være tilgængelige for edge.", + "time": "Tid", + "connectivity": { + "check-connectivity": "Tjek forbindelsen", + "device-created-check-connectivity": "Enheden er oprettet. Lad os tjekke forbindelsen!", + "loading-check-connectivity-command": "Indlæser kommandoer til forbindelsestjek...", + "use-following-instructions": "Brug følgende instruktioner til at sende telemetri på vegne af enheden via shell", + "execute-following-command": "Udfør følgende kommando", + "install-curl-windows": "Fra og med Windows 10 b17063 er cURL tilgængelig som standard", + "install-curl-macos": "Fra og med Mac OS X 10.2 6C115 (Jaguar) er cURL tilgængelig som standard", + "install-mqtt-windows": "Brug instruktionerne til at downloade, installere, konfigurere og køre mosquitto_pub", + "install-coap-client": "Brug instruktionerne til at downloade, installere, konfigurere og køre coap-client", + "install-necessary-client-tools": "Installer nødvendige klientværktøjer", + "mqtts-x509-command": "Brug følgende dokumentation til at tilslutte enheden via MQTT med X509-godkendelse", + "coaps-x509-command": "Brug følgende dokumentation til at tilslutte enheden via CoAP over DTLS med X509-godkendelse", + "snmp-command": "Brug følgende dokumentation til at tilslutte enheden via SNMP.", + "sparkplug-command": "Brug følgende dokumentation til at tilslutte enheden via MQTT Sparkplug.", + "lwm2m-command": "Brug følgende dokumentation til at tilslutte enheden via LWM2M." } }, + "dynamic-form": { + "property": { + "properties": "Egenskaber", + "property": "Egenskab", + "id": "Id", + "name": "Navn", + "type": "Type", + "type-text": "Tekst", + "type-password": "Adgangskode", + "type-textarea": "Tekstområde", + "type-number": "Tal", + "type-switch": "Kontakt", + "type-select": "Vælg", + "type-radios": "Radioknapper", + "type-datetime": "Dato/tid", + "type-image": "Billede", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "Farve", + "type-color-settings": "Farveindstillinger", + "type-font": "Skrifttype", + "type-units": "Enheder", + "type-icon": "Ikon", + "type-fieldset": "Feltelement", + "type-array": "Array", + "type-html-section": "HTML-sektion", + "group-title": "Gruppetitel", + "no-properties": "Ingen egenskaber konfigureret", + "add-property": "Tilføj egenskab", + "property-settings": "Egenskabsindstillinger", + "remove-property": "Fjern egenskab", + "default-value": "Standardværdi", + "value-required": "Værdi kræves", + "number-settings": "Talindstillinger", + "min": "Min", + "max": "Maks", + "step": "Trin", + "selected-options-limit": "Valgte valgmuligheder grænse", + "advanced-ui-settings": "Avancerede UI-indstillinger", + "disable-on-property": "Deaktiver ved egenskab", + "display-condition-function": "Visningsbetingelsesfunktion", + "sub-label": "Undertekst", + "vertical-divider-after": "Lodret skillelinje efter", + "input-field-suffix": "Suffiks i inputfelt", + "property-row-classes": "Række-klasser", + "property-field-classes": "Felt-klasser", + "not-unique-property-ids-error": "Egenskabs-ID'er skal være unikke!", + "enable-multiple-select": "Tillad flere valg", + "allow-empty-select-option": "Tillad tom mulighed", + "select-options": "Vælg valgmuligheder", + "not-unique-select-option-value-error": "Valgmulighedsværdier skal være unikke!", + "value": "Værdi", + "label": "Etiket", + "add-option": "Tilføj mulighed", + "no-options": "Ingen valgmuligheder konfigureret", + "remove-option": "Fjern mulighed", + "textarea-rows": "Rækker i tekstområde", + "help-id": "Hjælp-id", + "buttons-direction": "Knappers retning", + "direction-row": "Række", + "direction-column": "Kolonne", + "radio-button-options": "Radioknapmuligheder", + "datetime-type": "Dato/tid felt-type", + "datetime-type-date": "Dato", + "datetime-type-time": "Tid", + "datetime-type-datetime": "Dato/tid", + "enable-clear-button": "Aktivér ryd-knap", + "html-section-settings": "HTML-sektionsindstillinger", + "html-section-classes": "HTML-sektionsklasser", + "html-section-content": "HTML-sektionsindhold", + "array-item": "Array-element", + "item-type": "Elementtype", + "item-name": "Elementnavn", + "no-items": "Ingen elementer" + }, + "clear-form": "Ryd formular", + "clear-form-prompt": "Er du sikker på, at du vil fjerne alle formularens egenskaber?", + "import-form": "Importér formular fra JSON", + "export-form": "Eksportér formular til JSON", + "json-file": "JSON-fil", + "json-content": "JSON-indhold", + "invalid-form-json-file-error": "Kan ikke importere formular fra JSON: Ugyldig JSON-datastruktur for formular." + }, + "asset-profile": { + "asset-profile": "Assetprofil", + "asset-profiles": "Assetprofiler", + "all-asset-profiles": "Alle", + "add": "Tilføj assetprofil", + "edit": "Rediger assetprofil", + "asset-profile-details": "Assetprofildetaljer", + "no-asset-profiles-text": "Ingen assetprofiler fundet", + "search": "Søg assetprofiler", + "selected-asset-profiles": "{ count, plural, =1 {1 assetprofil} other {# assetprofiler} } valgt", + "no-asset-profiles-matching": "Ingen assetprofil matcher '{{entity}}'.", + "asset-profile-required": "Assetprofil er påkrævet", + "idCopiedMessage": "Assetprofil-ID er blevet kopieret til udklipsholderen", + "set-default": "Gør assetprofil til standard", + "delete": "Slet assetprofil", + "copyId": "Kopiér assetprofil-ID", + "name-max-length": "Navnet må være under 256 tegn", + "new-device-profile-name": "Assetprofilnavn", + "new-device-profile-name-required": "Assetprofilnavn er påkrævet.", + "name": "Navn", + "name-required": "Navn er påkrævet.", + "image": "Assetprofilbillede", + "description": "Beskrivelse", + "default": "Standard", + "default-rule-chain": "Standardregelkæde", + "default-edge-rule-chain": "Standard edge-regelkæde", + "default-edge-rule-chain-hint": "Bruges på edge som regelkæde til behandling af indgående data for aktiver i denne assetprofil", + "mobile-dashboard": "Mobil-dashboard", + "mobile-dashboard-hint": "Bruges af mobilappen som dashboard for assetdetaljer", + "select-queue-hint": "Vælg fra rullelisten.", + "delete-asset-profile-title": "Er du sikker på, at du vil slette assetprofilen '{{assetProfileName}}'?", + "delete-asset-profile-text": "Vær forsigtig, efter bekræftelse vil assetprofilen og alle relaterede data ikke kunne gendannes.", + "delete-asset-profiles-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 assetprofil} other {# assetprofiler} }?", + "delete-asset-profiles-text": "Vær forsigtig, efter bekræftelse vil alle valgte assetprofiler blive fjernet og dataene ikke kunne gendannes.", + "set-default-asset-profile-title": "Er du sikker på, at du vil gøre assetprofilen '{{assetProfileName}}' til standard?", + "set-default-asset-profile-text": "Efter bekræftelse vil assetprofilen blive markeret som standard og anvendt til nye aktiver uden specifik profil.", + "no-asset-profiles-found": "Ingen assetprofiler fundet.", + "create-new-asset-profile": "Opret en ny!", + "create-asset-profile": "Opret ny assetprofil", + "import": "Importér assetprofil", + "export": "Eksportér assetprofil", + "export-failed-error": "Kan ikke eksportere assetprofil: {{error}}", + "asset-profile-file": "Assetprofilfil", + "invalid-asset-profile-file-error": "Kan ikke importere assetprofil: Ugyldig datastruktur." + }, "device-profile": { "device-profile": "Enhedsprofil", "device-profiles": "Enhedsprofiler", "all-device-profiles": "Alle", "add": "Tilføj enhedsprofil", "edit": "Rediger enhedsprofil", - "device-profile-details": "Oplysninger om enhedsprofil", + "device-profile-details": "Detaljer om enhedsprofil", "no-device-profiles-text": "Ingen enhedsprofiler fundet", - "search": "Søg efter enhedsprofiler", - "selected-device-profiles": "", - "no-device-profiles-matching": "", + "search": "Søg enhedsprofiler", + "selected-device-profiles": "{ count, plural, =1 {1 enhedsprofil} other {# enhedsprofiler} } valgt", + "no-device-profiles-matching": "Ingen enhedsprofil matcher '{{entity}}'.", "device-profile-required": "Enhedsprofil er påkrævet", - "idCopiedMessage": "Enhedsprofil-id er blevet kopieret til udklipsholder", - "set-default": "Gør enhedsprofil standard", + "idCopiedMessage": "Enhedsprofil-ID er blevet kopieret til udklipsholderen", + "set-default": "Gør enhedsprofil til standard", "delete": "Slet enhedsprofil", - "copyId": "Kopiér enhedsprofil-id", + "copyId": "Kopiér enhedsprofil-ID", + "name-max-length": "Navnet må være under 256 tegn", "name": "Navn", "name-required": "Navn er påkrævet.", "type": "Profiltype", "type-required": "Profiltype er påkrævet.", "type-default": "Standard", + "image": "Enhedsprofilbillede", "transport-type": "Transporttype", "transport-type-required": "Transporttype er påkrævet.", "transport-type-default": "Standard", - "transport-type-default-hint": "Understøtter grundlæggende MQTT-, HTTP- og CoAP-transport", + "transport-type-default-hint": "Understøtter grundlæggende MQTT, HTTP og CoAP transport", "transport-type-mqtt": "MQTT", - "transport-type-mqtt-hint": "Aktiverer avancerede MQTT-transportindstillinger", - "transport-type-lwm2m": "LWM2M", - "transport-type-lwm2m-hint": "LWM2M-transporttype", + "transport-type-mqtt-hint": "Aktiverer avancerede MQTT transportindstillinger", "transport-type-coap": "CoAP", - "transport-type-coap-hint": "Aktiverer avancerede CoAP-transportindstillinger", + "transport-type-coap-hint": "Aktiverer avancerede CoAP transportindstillinger", + "transport-type-lwm2m": "LWM2M", + "transport-type-lwm2m-hint": "LWM2M transporttype", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "Angiv SNMP transportkonfiguration", + "transport-type-http": "HTTP", "description": "Beskrivelse", "default": "Standard", "profile-configuration": "Profilkonfiguration", "transport-configuration": "Transportkonfiguration", "default-rule-chain": "Standardregelkæde", - "select-queue-hint": "Vælg fra en rulleliste, eller tilføj et brugerdefineret navn.", - "delete-device-profile-title": "", - "delete-device-profile-text": "Vær forsigtig. Efter bekræftelsen vil enhedsprofilen og alle relaterede data være uoprettelige.", - "delete-device-profiles-title": "", - "delete-device-profiles-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte enhedsprofiler blive fjernet, og alle relaterede data vil være uoprettelige.", - "set-default-device-profile-title": "", - "set-default-device-profile-text": "Efter bekræftelsen vil enhedsprofilen blive markeret som standard og vil blive brugt til nye enheder, hvor der ikke er angivet nogen profil.", + "default-edge-rule-chain": "Standard edge-regelkæde", + "default-edge-rule-chain-hint": "Bruges på edge som regelkæde til at behandle indgående data for enheder med denne enhedsprofil", + "mobile-dashboard": "Mobil-dashboard", + "mobile-dashboard-hint": "Bruges af mobilapplikationen som enhedens detaljer-dashboard", + "select-queue-hint": "Vælg fra rullemenuen.", + "delete-device-profile-title": "Er du sikker på, at du vil slette enhedsprofilen '{{deviceProfileName}}'?", + "delete-device-profile-text": "Vær forsigtig, efter bekræftelse vil enhedsprofilen og alle relaterede data, inklusive OTA-opdateringer, ikke kunne gendannes.", + "delete-device-profiles-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 enhedsprofil} other {# enhedsprofiler} }?", + "delete-device-profiles-text": "Vær forsigtig, efter bekræftelse vil alle valgte enhedsprofiler og relaterede data, inklusiv OTA-opdateringer, ikke kunne gendannes.", + "set-default-device-profile-title": "Er du sikker på, at du vil gøre enhedsprofilen '{{deviceProfileName}}' til standard?", + "set-default-device-profile-text": "Efter bekræftelse vil enhedsprofilen blive markeret som standard og brugt til nye enheder uden angivet profil.", "no-device-profiles-found": "Ingen enhedsprofiler fundet.", "create-new-device-profile": "Opret en ny!", - "mqtt-device-topic-filters": "MQTT-enhedsemnefiltre", - "mqtt-device-topic-filters-unique": "MQTT-enhedsemnefiltre skal være unikke.", - "mqtt-device-payload-type": "MQTT-enhedsdata", + "mqtt-device-topic-filters": "MQTT enheds-topic-filtre", + "mqtt-device-topic-filters-unique": "MQTT enheds-topic-filtre skal være unikke.", + "mqtt-device-topic-filters-spark-plug": "MQTT Sparkplug B Edge of Network (EoN) node.", + "mqtt-device-topic-filters-spark-plug-hint": "Tillad forbindelser fra EoN noder med Sparkplug B payload og topic-format.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "SparkPlug-metrikker der skal gemmes som attributter.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Navne på SparkPlug-metrikker, der vil blive gemt som enhedsattributter. Alle andre metrikker vil blive gemt som telemetri.", + "mqtt-device-payload-type": "MQTT enhedspayload", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Aktiver kompatibilitet med andre payloadformater.", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Når aktiveret, bruges Protobuf som standard payloadformat. Hvis parsing fejler, forsøges JSON. Nyttigt til bagudkompatibilitet under firmwareopdateringer. Kompatibilitetstilstand kan reducere ydeevne en smule.", + "mqtt-use-json-format-for-default-downlink-topics": "Brug JSON-format til standard downlink-topics", + "mqtt-use-json-format-for-default-downlink-topics-hint": "Ved aktivering bruges JSON-payload til push af attributter og RPC via standard topics. Har ingen effekt på nye (v2) topics.", + "mqtt-send-ack-on-validation-exception": "Send PUBACK ved valideringsfejl af PUBLISH-besked", + "mqtt-send-ack-on-validation-exception-hint": "Som standard afsluttes MQTT-session ved valideringsfejl. Ved aktivering sendes bekræftelse i stedet.", + "snmp-add-mapping": "Tilføj SNMP-kortlægning", + "snmp-mapping-not-configured": "Ingen kortlægning fra OID til tidsserier/telemetri konfigureret", + "snmp-timseries-or-attribute-name": "Tidsserie-/attributnavn til kortlægning", + "snmp-timseries-or-attribute-type": "Tidsserie-/attributtype til kortlægning", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", "transport-device-payload-type-json": "JSON", "transport-device-payload-type-proto": "Protobuf", - "mqtt-payload-type-required": "Datatype er påkrævet.", - "coap-device-type": "CoAP-enhedstype", - "coap-device-payload-type": "CoAP-enhedsdata", - "coap-device-type-required": "CoAP-enhedstype er påkrævet.", + "mqtt-payload-type-required": "Payloadtype er påkrævet.", + "coap-device-type": "CoAP enhedstype", + "coap-device-payload-type": "CoAP enhedspayload", + "coap-device-type-required": "CoAP enhedstype er påkrævet.", "coap-device-type-default": "Standard", "coap-device-type-efento": "Efento NB-IoT", - "support-level-wildcards": "Enkelt [+] jokertegn og [#] jokertegn på flere niveauer understøttes.", - "telemetry-topic-filter": "Telemetriemnefilter", - "telemetry-topic-filter-required": "Telemetriemnefilter er påkrævet.", - "attributes-topic-filter": "Attributemnefilter", - "attributes-topic-filter-required": "Attributemnefilter er påkrævet.", - "telemetry-proto-schema": "Telemetri-protoskema", - "telemetry-proto-schema-required": "Telemetri-protoskema er påkrævet.", - "attributes-proto-schema": "Protoskema for attributter", - "attributes-proto-schema-required": "Protoskema for attributter er påkrævet.", - "rpc-response-topic-filter": "RPC-svaremnefilter", - "rpc-response-topic-filter-required": "RPC-svaremnefilter er påkrævet.", - "not-valid-pattern-topic-filter": "Ikke gyldigt mønsteremnefilter", - "not-valid-single-character": "Ugyldig brug af jokertegn i et enkelt niveau", - "not-valid-multi-character": "Ugyldig brug af jokertegn i flere niveauer", - "single-level-wildcards-hint": "[+] er velegnet til ethvert emnefilterniveau. Eks.: v1/enheder/+/telemetri eller +/enheder/+/attributter.", - "multi-level-wildcards-hint": "[#] kan selv erstatte emnefilteret og skal være det sidste symbol i emnet. Eks.: # eller v1/enheder/me/#.", + "support-level-wildcards": "Enkelt [+] og multi-niveau [#] jokertegn understøttes.", + "telemetry-topic-filter": "Telemetri-topicfilter", + "telemetry-topic-filter-required": "Telemetri-topicfilter er påkrævet.", + "attributes-topic-filter": "Attributters publicerings-topicfilter", + "attributes-subscribe-topic-filter": "Attributters abonnements-topicfilter", + "attributes-topic-filter-required": "Attributters publicerings-topicfilter er påkrævet.", + "attributes-subscribe-topic-filter-required": "Attributters abonnements-topic er påkrævet", + "telemetry-proto-schema": "Telemetri proto-skema", + "telemetry-proto-schema-required": "Telemetri proto-skema er påkrævet.", + "attributes-proto-schema": "Attributter proto-skema", + "attributes-proto-schema-required": "Attributter proto-skema er påkrævet.", + "rpc-response-proto-schema": "RPC-svar proto-skema", + "rpc-response-proto-schema-required": "RPC-svar proto-skema er påkrævet.", + "rpc-response-topic-filter": "RPC-svar topicfilter", + "rpc-response-topic-filter-required": "RPC-svar topicfilter er påkrævet.", + "rpc-request-proto-schema": "RPC-anmodning proto-skema", + "rpc-request-proto-schema-required": "RPC-anmodning proto-skema er påkrævet.", + "rpc-request-proto-schema-hint": "RPC-anmodningsbeskeden skal altid indeholde felterne: string method = 1; int32 requestId = 2; og params = 3 af enhver datatype.", + "not-valid-pattern-topic-filter": "Ugyldigt mønster i topicfilter", + "not-valid-single-character": "Ugyldig brug af enkelt-niveau jokertegn", + "not-valid-multi-character": "Ugyldig brug af multi-niveau jokertegn", + "single-level-wildcards-hint": "[+] kan bruges på alle niveauer i topicfilter. Eksempel: v1/devices/+/telemetry eller +/devices/+/attributes.", + "multi-level-wildcards-hint": "[#] kan erstatte hele topicfilteret og skal være det sidste symbol. Eksempel: # eller v1/devices/me/#.", "alarm-rules": "Alarmregler", - "alarm-rules-with-count": "", + "alarm-rules-with-count": "Alarmregler ({{count}})", "no-alarm-rules": "Ingen alarmregler konfigureret", "add-alarm-rule": "Tilføj alarmregel", "edit-alarm-rule": "Rediger alarmregel", "alarm-type": "Alarmtype", "alarm-type-required": "Alarmtype er påkrævet.", - "alarm-type-unique": "Alarmtypen skal være unik inden for alarmreglerne for enhedsprofilen.", - "create-alarm-pattern": "", + "alarm-type-unique": "Alarmtype skal være unik inden for enhedsprofilens alarmregler.", + "alarm-type-max-length": "Alarmtype må være under 256 tegn", + "create-alarm-pattern": "Opret {{alarmType}} alarm", "create-alarm-rules": "Opret alarmregler", - "no-create-alarm-rules": "Ingen opret betingelser konfigureret", - "add-create-alarm-rule-prompt": "Tilføj opret alarmregel", - "clear-alarm-rule": "Ryd alarmregel", - "no-clear-alarm-rule": "Ingen klar betingelse konfigureret", - "add-create-alarm-rule": "Tilføj opret betingelse", - "add-clear-alarm-rule": "Tilføj ryd tilstand", - "select-alarm-severity": "Vælg alarmens alvorsgrad", - "alarm-severity-required": "Alarmens alvorsgrad er påkrævet.", - "condition-duration": "Varighed af betingelse", + "no-create-alarm-rules": "Ingen oprettelsesbetingelser konfigureret", + "add-create-alarm-rule-prompt": "Tilføj venligst oprettelsesregel for alarm", + "clear-alarm-rule": "Fjern alarmregel", + "no-clear-alarm-rule": "Ingen fjernbetingelse konfigureret", + "add-create-alarm-rule": "Tilføj oprettelsesbetingelse", + "add-clear-alarm-rule": "Tilføj fjernbetingelse", + "select-alarm-severity": "Vælg alarmens alvorlighed", + "alarm-severity-required": "Alvorlighed er påkrævet.", + "condition-duration": "Betingelsens varighed", "condition-duration-value": "Varighedsværdi", "condition-duration-time-unit": "Tidsenhed", - "condition-duration-value-range": "Varighedsværdien skal ligge i området fra 1 til 2147483647.", - "condition-duration-value-pattern": "Varighedsværdien skal være heltal.", + "condition-duration-value-range": "Varighedsværdi skal være mellem 1 og 2147483647.", + "condition-duration-value-pattern": "Varighedsværdien skal være et heltal.", "condition-duration-value-required": "Varighedsværdi er påkrævet.", "condition-duration-time-unit-required": "Tidsenhed er påkrævet.", "advanced-settings": "Avancerede indstillinger", - "propagate-alarm": "Overfør alarm", - "alarm-rule-relation-types-list": "Relationstyper, der skal overføres", - "alarm-rule-relation-types-list-hint": "Hvis der ikke vælges Overfør relationstyper vil alarmer blive overført uden filtrering på relationstype.", + "alarm-rule-additional-info": "Yderligere info", + "edit-alarm-rule-additional-info": "Rediger yderligere info", + "alarm-rule-additional-info-placeholder": "Tilføj kommentarer og justeringer, som vises under Yderligere info i alarmdetaljer", + "alarm-rule-additional-info-hint": "Tip: brug ${keyName} til at erstatte med attribut- eller telemetrinøgleværdier, der bruges i alarmbetingelsen.", + "alarm-rule-mobile-dashboard": "Mobil-dashboard", + "alarm-rule-mobile-dashboard-hint": "Bruges af mobilapplikationen som dashboard for alarmdetaljer", + "alarm-rule-no-mobile-dashboard": "Intet dashboard valgt", + "propagate-alarm": "Videregiv alarm til relaterede enheder", + "alarm-rule-relation-types-list": "Relationstyper", + "alarm-rule-relation-types-list-hint": "Angiver relationstyper til at filtrere relaterede enheder. Hvis tomt, videregives alarmen til alle relaterede enheder.", + "propagate-alarm-to-owner": "Videregiv alarm til enhedens ejer (Kunde eller Lejer)", + "propagate-alarm-to-tenant": "Videregiv alarm til Lejer", "alarm-rule-condition": "Alarmregelbetingelse", - "enter-alarm-rule-condition-prompt": "Tilføj alarmregelbetingelse", + "enter-alarm-rule-condition-prompt": "Tilføj venligst alarmregelbetingelse", "edit-alarm-rule-condition": "Rediger alarmregelbetingelse", - "device-provisioning": "Klargøring af enheden", - "provision-strategy": "Strategi for klargøring", - "provision-strategy-required": "Strategi for klargøring er påkrævet.", + "device-provisioning": "Enhedsregistrering", + "provision-strategy": "Registreringsstrategi", + "provision-strategy-required": "Registreringsstrategi er påkrævet.", "provision-strategy-disabled": "Deaktiveret", "provision-strategy-created-new": "Tillad oprettelse af nye enheder", - "provision-strategy-check-pre-provisioned": "Kontrollér, om der er forhåndsklargjorte enheder", - "provision-device-key": "Klargøringsenhedsnøgle", - "provision-device-key-required": "Klargøringsenhedsnøgle er påkrævet.", - "copy-provision-key": "Kopiér klargøringsnøgle", - "provision-key-copied-message": "Klargøringsnøgle er blevet kopieret til udklipsholder", - "provision-device-secret": "Klargøringsenhed hemmelig", - "provision-device-secret-required": "Klargøringsenhed hemmelig er påkrævet.", - "copy-provision-secret": "Kopiér klargøring hemmelig", - "provision-secret-copied-message": "Klargøring hemmelig er blevet kopieret til udklipsholder", + "provision-strategy-check-pre-provisioned": "Tjek for forhåndsregistrerede enheder", + "provision-device-key": "Registreringsnøgle", + "provision-device-key-required": "Registreringsnøgle er påkrævet.", + "copy-provision-key": "Kopiér registreringsnøgle", + "provision-key-copied-message": "Registreringsnøgle er kopieret til udklipsholderen", + "provision-device-secret": "Registreringshemmelighed", + "provision-device-secret-required": "Registreringshemmelighed er påkrævet.", + "copy-provision-secret": "Kopiér registreringshemmelighed", + "provision-secret-copied-message": "Registreringshemmelighed er kopieret til udklipsholderen", + "provision-strategy-x509": { + "certificate-chain": "X509-certifikatkæde", + "certificate-chain-hint": "X.509-certifikatstrategien bruges til at registrere enheder via klientcertifikater i tovej TLS-kommunikation.", + "allow-create-new-devices": "Opret nye enheder", + "allow-create-new-devices-hint": "Hvis valgt, vil nye enheder blive oprettet og klientcertifikatet vil blive brugt som enhedens legitimationsoplysninger.", + "certificate-value": "Certifikat i PEM-format", + "certificate-value-required": "Certifikat i PEM-format er påkrævet", + "cn-regex-variable": "CN regulært udtryk-variabel", + "cn-regex-variable-required": "CN regulært udtryk-variabel er påkrævet", + "cn-regex-variable-hint": "Påkrævet for at hente enhedens navn fra Common Name i X509-certifikatet." + }, "condition": "Betingelse", "condition-type": "Betingelsestype", - "condition-type-simple": "Enkel", + "condition-type-simple": "Simpel", "condition-type-duration": "Varighed", - "condition-during": "", - "condition-type-repeating": "Gentager", - "condition-type-required": "Betingelsesnavn er påkrævet.", - "condition-repeating-value": "Antal begivenheder", - "condition-repeating-value-range": "Antallet af begivenheder skal ligge i intervallet fra 1 til 2147483647.", - "condition-repeating-value-pattern": "Antal begivenheder skal være heltal.", - "condition-repeating-value-required": "Antal begivenheder er påkrævet.", - "condition-repeat-times": "", - "schedule-type": "Planlægningstype", - "schedule-type-required": "Planlægningstype er påkrævet.", - "schedule": "Tidsplan", - "edit-schedule": "Rediger alarmtidsplan", + "condition-during": "I løbet af {{during}}", + "condition-during-dynamic": "I løbet af \"{{ attribute }}\" ({{during}})", + "condition-type-repeating": "Gentagelse", + "condition-type-required": "Betingelsestype er påkrævet.", + "condition-repeating-value": "Antal hændelser", + "condition-repeating-value-range": "Antal hændelser skal være mellem 1 og 2147483647.", + "condition-repeating-value-pattern": "Antal hændelser skal være heltal.", + "condition-repeating-value-required": "Antal hændelser er påkrævet.", + "condition-repeat-times": "Gentager { count, plural, =1 {1 gang} other {# gange} }", + "condition-repeat-times-dynamic": "Gentager \"{ attribute }\" ({ count, plural, =1 {1 gang} other {# gange} })", + "schedule-type": "Skematype", + "schedule-type-required": "Skematype er påkrævet.", + "schedule": "Skema", + "edit-schedule": "Rediger alarmskema", "schedule-any-time": "Aktiv hele tiden", - "schedule-specific-time": "Aktiv på et bestemt tidspunkt", + "schedule-specific-time": "Aktiv på specifikt tidspunkt", "schedule-custom": "Brugerdefineret", "schedule-day": { "monday": "Mandag", @@ -1242,302 +2047,540 @@ "sunday": "Søndag" }, "schedule-days": "Dage", - "schedule-time": "Tid", + "schedule-time": "Tidspunkt", "schedule-time-from": "Fra", "schedule-time-to": "Til", - "schedule-days-of-week-required": "Der skal vælges mindst én ugedag.", + "schedule-days-of-week-required": "Mindst én ugedag skal vælges.", "create-device-profile": "Opret ny enhedsprofil", "import": "Importér enhedsprofil", "export": "Eksportér enhedsprofil", - "export-failed-error": "", - "device-profile-file": "Fil for enhedsprofil", - "invalid-device-profile-file-error": "Kan ikke importere enhedsprofil: Ugyldig datastruktur for enhedsprofil." + "export-failed-error": "Kan ikke eksportere enhedsprofil: {{error}}", + "device-profile-file": "Enhedsprofilfil", + "invalid-device-profile-file-error": "Kan ikke importere enhedsprofil: Ugyldig datastruktur.", + "power-saving-mode": "Strømbesparende tilstand", + "power-saving-mode-type": { + "default": "Brug enhedsprofilens strømbesparende tilstand", + "psm": "Strømbesparende tilstand (PSM)", + "drx": "Diskontinuerlig modtagelse (DRX)", + "edrx": "Udvidet diskontinuerlig modtagelse (eDRX)" + }, + "edrx-cycle": "eDRX-cyklus", + "edrx-cycle-required": "eDRX-cyklus er påkrævet.", + "edrx-cycle-pattern": "eDRX-cyklus skal være et positivt heltal.", + "edrx-cycle-min": "Minimum antal eDRX-cyklusser er {{ min }} sekunder.", + "paging-transmission-window": "Paging Transmission Window", + "paging-transmission-window-required": "Paging transmission window er påkrævet.", + "paging-transmission-window-pattern": "Paging transmission window skal være et positivt heltal.", + "paging-transmission-window-min": "Minimum paging transmission window er {{ min }} sekunder.", + "psm-activity-timer": "PSM-aktivitetstimer", + "psm-activity-timer-required": "PSM-aktivitetstimer er påkrævet.", + "psm-activity-timer-pattern": "PSM-aktivitetstimeren skal være et positivt heltal.", + "psm-activity-timer-min": "Minimum PSM-aktivitetstimer er {{ min }} sekunder.", + "lwm2m": { + "object-list": "Objektliste", + "object-list-empty": "Ingen objekter valgt.", + "no-objects-found": "Ingen objekter fundet.", + "no-objects-matching": "Ingen objekter matcher '{{object}}'.", + "model-tab": "LWM2M-model", + "add-new-instances": "Tilføj nye instanser", + "instances-list": "Instansliste", + "instances-list-required": "Instansliste er påkrævet.", + "instance-id-pattern": "Instans-ID skal være et positivt heltal.", + "instance-id-max": "Maksimal instans-ID værdi {{max}}.", + "instance": "Instans", + "resource-label": "#ID Ressourcenavn", + "observe-label": "Observer", + "attribute-label": "Attribut", + "telemetry-label": "Telemetri", + "edit-observe-select": "For at redigere observering, vælg telemetri eller attribut", + "edit-attributes-select": "For at redigere attributter, vælg telemetri eller attribut", + "no-attributes-set": "Ingen attributter angivet", + "key-name": "Nøglenavn", + "key-name-required": "Nøglenavn er påkrævet", + "attribute-name": "Attributnavn", + "attribute-name-required": "Attributnavn er påkrævet.", + "attribute-value": "Attributværdi", + "attribute-value-required": "Attributværdi er påkrævet.", + "attribute-value-pattern": "Attributværdi skal være et positivt heltal.", + "edit-attributes": "Rediger attributter: {{ name }}", + "view-attributes": "Vis attributter: {{ name }}", + "add-attribute": "Tilføj attribut", + "edit-attribute": "Rediger attribut", + "view-attribute": "Vis attribut", + "remove-attribute": "Fjern attribut", + "delete-server-text": "Vær forsigtig, efter bekræftelse vil serverkonfigurationen ikke kunne gendannes.", + "delete-server-title": "Er du sikker på, at du vil slette serveren?", + "mode": "Sikkerhedskonfigurationsmetode", + "bootstrap-tab": "Bootstrap", + "bootstrap-server-legend": "Bootstrap-server (ShortId...)", + "lwm2m-server-legend": "LwM2M-server (ShortId...)", + "server": "Server", + "short-id": "Kort server-ID", + "short-id-tooltip": "Serverens korte ID. Bruges til at linke serverobjektinstans.\nIdentificerer entydigt hver konfigureret LwM2M-server til LwM2M-klienten.\nSkal angives, når Bootstrap-Server ressourcen er 'false'.\nID:0 og ID:65535 må ikke anvendes.", + "short-id-tooltip-bootstrap": "Skal angives, når Bootstrap-Server ressourcen er 'false'.", + "short-id-required": "Kort server-ID er påkrævet.", + "short-id-range": "Kort server-ID skal være mellem {{ min }} og {{ max }}.", + "short-id-pattern": "Kort server-ID skal være et positivt heltal.", + "lifetime": "Klientens registreringslevetid", + "lifetime-required": "Registreringslevetid er påkrævet.", + "lifetime-pattern": "Registreringslevetid skal være et positivt heltal.", + "default-min-period": "Minimumsperiode mellem to notifikationer (s)", + "default-min-period-tooltip": "Standardværdi for Minimumsperiode for observering, hvis parameteren ikke er angivet.", + "default-min-period-required": "Minimumsperiode er påkrævet.", + "default-min-period-pattern": "Minimumsperiode skal være et positivt heltal.", + "notification-storing": "Gem notifikationer ved deaktivering eller offline", + "binding": "Binding", + "binding-type": { + "u": "U: Klient tilgængelig via UDP-binding hele tiden.", + "m": "M: Klient tilgængelig via MQTT-binding hele tiden.", + "h": "H: Klient tilgængelig via HTTP-binding hele tiden.", + "t": "T: Klient tilgængelig via TCP-binding hele tiden.", + "s": "S: Klient tilgængelig via SMS-binding hele tiden.", + "n": "N: Klient SKAL svare via Non-IP binding (understøttet fra LWM2M 1.1).", + "uq": "UQ: UDP i køtilstand (ikke understøttet fra LWM2M 1.1)", + "uqs": "UQS: Både UDP og SMS aktive; UDP i køtilstand, SMS i standardtilstand (ikke understøttet fra LWM2M 1.1)", + "tq": "TQ: TCP i køtilstand (ikke understøttet fra LWM2M 1.1)", + "tqs": "TQS: Både TCP og SMS aktive; TCP i køtilstand, SMS i standardtilstand (ikke understøttet fra LWM2M 1.1)", + "sq": "SQ: SMS i køtilstand (ikke understøttet fra LWM2M 1.1)" + }, + "binding-tooltip": "Angiver understøttede bindingstyper i klienten (/1/x/7). Bør matche værdien i “Supported Binding and Modes” i enhedsobjektet (/3/0/16). Kun én transportbinding kan bruges per session.", + "bootstrap-server": "Bootstrap-server", + "lwm2m-server": "LwM2M-server", + "include-bootstrap-server": "Inkluder Bootstrap Server-opdateringer", + "bootstrap-update-title": "Du har allerede konfigureret Bootstrap-server. Er du sikker på, at du vil udelukke opdateringerne?", + "bootstrap-update-text": "Vær forsigtig, efter bekræftelse vil Bootstrap Server-konfigurationen ikke kunne gendannes.", + "server-host": "Host", + "server-host-required": "Host er påkrævet.", + "server-port": "Port", + "server-port-required": "Port er påkrævet.", + "server-port-pattern": "Port skal være et positivt heltal.", + "server-port-range": "Port skal være mellem 1 og 65535.", + "server-public-key": "Serverens offentlige nøgle", + "server-public-key-required": "Serverens offentlige nøgle er påkrævet.", + "client-hold-off-time": "Hold off-tid", + "client-hold-off-time-required": "Hold off-tid er påkrævet.", + "client-hold-off-time-pattern": "Hold off-tid skal være et positivt heltal.", + "client-hold-off-time-tooltip": "Klientens hold off-tid til brug med kun Bootstrap-server", + "account-after-timeout": "Registrer efter timeout", + "account-after-timeout-required": "Registrer efter timeout er påkrævet.", + "account-after-timeout-pattern": "Registrer efter timeout skal være et positivt heltal.", + "account-after-timeout-tooltip": "Bootstrap-server registrerer efter den timeout-værdi, der er angivet af denne ressource.", + "server-type": "Servertype", + "add-new-server-title": "Tilføj ny serverkonfiguration", + "add-server-config": "Tilføj serverkonfiguration", + "add-lwm2m-server-config": "Tilføj LwM2M-server", + "no-config-servers": "Ingen servere konfigureret", + "others-tab": "Andre indstillinger", + "client-strategy": "Klientstrategi ved tilslutning", + "client-strategy-label": "Strategi", + "client-strategy-only-observe": "Send kun Observe-anmodning til klienten efter første forbindelse", + "client-strategy-read-all": "Læs alle ressourcer og send Observe-anmodning til klienten efter registrering", + "fw-update": "Firmwareopdatering", + "fw-update-strategy": "Firmwareopdateringsstrategi", + "fw-update-strategy-data": "Push firmware som binær fil via Object 19 og Resource 0 (Data)", + "fw-update-strategy-package": "Push firmware som binær fil via Object 5 og Resource 0 (Package)", + "fw-update-strategy-package-uri": "Generér automatisk unik CoAP-URL og push firmware via Object 5 og Resource 1 (Package URI)", + "sw-update": "Softwareopdatering", + "sw-update-strategy": "Softwareopdateringsstrategi", + "sw-update-strategy-package": "Push binær fil via Object 9 og Resource 2 (Package)", + "sw-update-strategy-package-uri": "Generér automatisk unik CoAP-URL og push software via Object 9 og Resource 3 (Package URI)", + "fw-update-resource": "Firmware CoAP-ressource", + "fw-update-resource-required": "Firmware CoAP-ressource er påkrævet.", + "sw-update-resource": "Software CoAP-ressource", + "sw-update-resource-required": "Software CoAP-ressource er påkrævet.", + "config-json-tab": "JSON-konfiguration af enhedsprofil", + "attributes-name": { + "min-period": "Minimumsperiode", + "max-period": "Maksimumsperiode", + "greater-than": "Større end", + "less-than": "Mindre end", + "step": "Trin", + "min-evaluation-period": "Minimum evalueringsperiode", + "max-evaluation-period": "Maksimum evalueringsperiode" + }, + "default-object-id": "Standardobjektversion (Attribut)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1" + } + }, + "snmp": { + "add-communication-config": "Tilføj kommunikationskonfiguration", + "add-mapping": "Tilføj mapping", + "authentication-passphrase": "Autentificeringsadgangskode", + "authentication-passphrase-required": "Autentificeringsadgangskode er påkrævet.", + "authentication-protocol": "Autentificeringsprotokol", + "authentication-protocol-required": "Autentificeringsprotokol er påkrævet.", + "communication-configs": "Kommunikationskonfigurationer", + "community": "Community-streng", + "community-required": "Community-streng er påkrævet.", + "context-name": "Kontekstnavn", + "data-key": "Datatast", + "data-key-required": "Datatast er påkrævet.", + "data-type": "Datatype", + "data-type-required": "Datatype er påkrævet.", + "engine-id": "Engine-ID", + "host": "Host", + "host-required": "Host er påkrævet.", + "oid": "OID", + "oid-pattern": "Ugyldigt OID-format.", + "oid-required": "OID er påkrævet.", + "please-add-communication-config": "Tilføj venligst kommunikationskonfiguration", + "please-add-mapping-config": "Tilføj venligst mapping-konfiguration", + "port": "Port", + "port-format": "Ugyldigt portformat.", + "port-required": "Port er påkrævet.", + "privacy-passphrase": "Privatlivs-adgangskode", + "privacy-passphrase-required": "Privatlivs-adgangskode er påkrævet.", + "privacy-protocol": "Privatlivsprotokol", + "privacy-protocol-required": "Privatlivsprotokol er påkrævet.", + "protocol-version": "Protokolversion", + "protocol-version-required": "Protokolversion er påkrævet.", + "querying-frequency": "Forespørgselsfrekvens, ms", + "querying-frequency-invalid-format": "Forespørgselsfrekvens skal være et positivt heltal.", + "querying-frequency-required": "Forespørgselsfrekvens er påkrævet.", + "retries": "Genforsøg", + "retries-invalid-format": "Genforsøg skal være et positivt heltal.", + "retries-required": "Genforsøg er påkrævet.", + "scope": "Scope", + "scope-required": "Scope er påkrævet.", + "security-name": "Sikkerhedsnavn", + "security-name-required": "Sikkerhedsnavn er påkrævet.", + "timeout-ms": "Timeout, ms", + "timeout-ms-invalid-format": "Timeout skal være et positivt heltal.", + "timeout-ms-required": "Timeout er påkrævet.", + "user-name": "Brugernavn", + "user-name-required": "Brugernavn er påkrævet." + } }, "dialog": { - "close": "Tæt" + "close": "Luk dialog", + "error-message-title": "Fejlmeddelelse:", + "error-details-title": "Fejldetaljer" }, "direction": { "column": "Kolonne", "row": "Række" }, + "edge": { + "edge": "Edge", + "edge-instances": "Edge-instanser", + "instances": "Instanser", + "edge-file": "Edge-fil", + "name-max-length": "Navnet skal være mindre end 256 tegn", + "label-max-length": "Etiketten skal være mindre end 256 tegn", + "type-max-length": "Typen skal være mindre end 256 tegn", + "management": "Edge-administration", + "no-edges-matching": "Ingen edges matcher '{{entity}}'.", + "add": "Tilføj edge", + "no-edges-text": "Ingen edges fundet", + "edge-details": "Edge-detaljer", + "add-edge-text": "Tilføj ny edge", + "delete": "Slet edge", + "delete-edge-title": "Er du sikker på, at du vil slette edge '{{edgeName}}'?", + "delete-edge-text": "Vær forsigtig, efter bekræftelse vil edge og alle relaterede data ikke kunne gendannes.", + "delete-edges-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 edge} other {# edges} }?", + "delete-edges-text": "Vær forsigtig, efter bekræftelse vil alle valgte edges blive fjernet og alle relaterede data vil gå tabt.", + "name": "Navn", + "name-starts-with": "Edge-navn begynder med", + "name-required": "Navn er påkrævet.", + "description": "Beskrivelse", + "details": "Detaljer", + "events": "Hændelser", + "copy-id": "Kopiér Edge ID", + "id-copied-message": "Edge ID er kopieret til udklipsholder", + "sync": "Synkroniser Edge", + "edge-required": "Edge er påkrævet", + "edge-type": "Edge-type", + "edge-type-required": "Edge-type er påkrævet.", + "event-action": "Hændelseshandling", + "entity-id": "Enheds-ID", + "select-edge-type": "Vælg edge-type", + "assign-to-customer": "Tildel til kunde", + "assign-to-customer-text": "Vælg kunden der skal tildeles edge(s)", + "assign-edge-to-customer": "Tildel edge(s) til kunde", + "assign-edge-to-customer-text": "Vælg edges der skal tildeles kunden", + "assignedToCustomer": "Tildelt kunde", + "edge-public": "Edge er offentlig", + "assigned-to-customer": "Tildelt til: {{customerTitle}}", + "unassign-from-customer": "Fjern tildeling fra kunde", + "unassign-edge-title": "Er du sikker på, at du vil fjerne tildelingen af edge '{{edgeName}}'?", + "unassign-edge-text": "Efter bekræftelse vil edge blive fjernet og ikke længere være tilgængelig for kunden.", + "unassign-edges-title": "Er du sikker på, at du vil fjerne tildeling af { count, plural, =1 {1 edge} other {# edges} }?", + "unassign-edges-text": "Efter bekræftelse vil alle valgte edges blive fjernet og ikke længere være tilgængelige for kunden.", + "make-public": "Gør edge offentlig", + "make-public-edge-title": "Er du sikker på, at du vil gøre edge '{{edgeName}}' offentlig?", + "make-public-edge-text": "Efter bekræftelse vil edge og alle dets data blive offentlige og tilgængelige for andre.", + "make-private": "Gør edge privat", + "public": "Offentlig", + "make-private-edge-title": "Er du sikker på, at du vil gøre edge '{{edgeName}}' privat?", + "make-private-edge-text": "Efter bekræftelse vil edge og alle dets data blive private og ikke tilgængelige for andre.", + "import": "Importér edge", + "install-connect-instructions": "Installér & tilslut instruktioner", + "install-connect-instructions-edge-created": "Edge oprettet! Tjek Installér & Tilslut instruktioner", + "loading-edge-instructions": "Indlæser edge-instruktioner...", + "label": "Etiket", + "load-entity-error": "Data kunne ikke indlæses. Enheden er blevet slettet.", + "assign-new-edge": "Tildel ny edge", + "unassign-from-edge": "Fjern tildeling fra edge", + "edge-key": "Edge-nøgle", + "copy-edge-key": "Kopiér edge-nøgle", + "edge-key-copied-message": "Edge-nøgle er kopieret til udklipsholder", + "edge-secret": "Edge-hemmelighed", + "copy-edge-secret": "Kopiér Edge-hemmelighed", + "edge-secret-copied-message": "Edge-hemmelighed er kopieret til udklipsholder", + "manage-assets": "Administrer aktiver", + "manage-devices": "Administrer enheder", + "manage-entity-views": "Administrer enhedsvisninger", + "manage-dashboards": "Administrer dashboards", + "manage-rulechains": "Administrer regelkæder", + "assets": "Edge-aktiver", + "devices": "Edge-enheder", + "entity-views": "Edge-enhedsvisninger", + "dashboard": "Edge-dashboard", + "dashboards": "Edge-dashboards", + "rulechain-templates": "Regelkædeskabeloner", + "edge-rulechain-templates": "Edge-regelkædeskabeloner", + "rulechains": "Edge-regelkæder", + "search": "Søg edges", + "selected-edges": "{ count, plural, =1 {1 edge} other {# edges} } valgt", + "any-edge": "Enhver edge", + "no-edge-types-matching": "Ingen edge-typer matcher '{{entitySubtype}}'.", + "edge-type-list-empty": "Ingen edge-typer valgt.", + "edge-types": "Edge-typer", + "enter-edge-type": "Indtast edge-type", + "deployed": "Deployet", + "pending": "Afventer", + "downlinks": "Downlinks", + "no-downlinks-prompt": "Ingen downlinks fundet", + "sync-process-started-successfully": "Synkroniseringsproces startet!", + "missing-related-rule-chains-title": "Edge mangler tilknyttede regelkæder", + "missing-related-rule-chains-text": "Tildelte regelkæder bruger regelnoder, som videresender til regelkæder, der ikke er tildelt denne edge.

Manglende regelkæder:
{{missingRuleChains}}", + "widget-datasource-error": "Denne widget understøtter kun EDGE-enhed som datakilde", + "upgrade-instructions": "Opgraderingsvejledning", + "connected": "Forbundet", + "disconnected": "Afbrudt" + }, + "edge-event": { + "type-dashboard": "Dashboard", + "type-asset": "Aktiv", + "type-device": "Enhed", + "type-device-profile": "Enhedsprofil", + "type-asset-profile": "Aktivprofil", + "type-entity-view": "Enhedsvisning", + "type-alarm": "Alarm", + "type-rule-chain": "Regelkæde", + "type-rule-chain-metadata": "Regelkæde-metadata", + "type-edge": "Edge", + "type-user": "Bruger", + "type-tenant": "Lejer", + "type-tenant-profile": "Lejerprofil", + "type-customer": "Kunde", + "type-relation": "Relation", + "type-widgets-bundle": "Widget-pakke", + "type-widgets-type": "Widget-type", + "type-admin-settings": "Adminindstillinger", + "type-ota-package": "OTA-pakke", + "type-queue": "Kø", + "action-type-added": "Tilføjet", + "action-type-deleted": "Slettet", + "action-type-updated": "Opdateret", + "action-type-post-attributes": "Send attributter", + "action-type-attributes-updated": "Attributter opdateret", + "action-type-attributes-deleted": "Attributter slettet", + "action-type-timeseries-updated": "Tidsserie opdateret", + "action-type-credentials-updated": "Legitimationsoplysninger opdateret", + "action-type-assigned-to-customer": "Tildelt kunde", + "action-type-unassigned-from-customer": "Fjernet fra kunde", + "action-type-relation-add-or-update": "Relation tilføjet eller opdateret", + "action-type-relation-deleted": "Relation slettet", + "action-type-rpc-call": "RPC-kald", + "action-type-alarm-ack": "Alarm kvitteret", + "action-type-alarm-clear": "Alarm ryddet", + "action-type-alarm-assigned": "Alarm tildelt", + "action-type-alarm-unassigned": "Alarm ikke tildelt", + "action-type-assigned-to-edge": "Tildelt edge", + "action-type-unassigned-from-edge": "Fjernet fra edge", + "action-type-credentials-request": "Legitimationsanmodning", + "action-type-entity-merge-request": "Fletningsanmodning for enhed" + }, "error": { "unable-to-connect": "Kan ikke oprette forbindelse til serveren! Kontrollér din internetforbindelse.", - "unhandled-error-code": "", + "unhandled-error-code": "Ubehandlet fejlkode: {{errorCode}}", "unknown-error": "Ukendt fejl" }, "entity": { - "entity": "Entitet", - "entities": "Entiteter", - "entities-count": "Antal entiteter", - "aliases": "Entitetsaliasser", - "entity-alias": "Entitetsalias", - "unable-delete-entity-alias-title": "Entitetsalias kunne ikke slettes", - "unable-delete-entity-alias-text": "", - "duplicate-alias-error": "", - "missing-entity-filter-error": "", - "configure-alias": "", + "entity": "Enhed", + "entities": "Enheder", + "entities-count": "Antal enheder", + "alarms-count": "Antal alarmer", + "aliases": "Enhedsaliasser", + "aliases-short": "Aliasser", + "entity-alias": "Enhedsalias", + "unable-delete-entity-alias-title": "Kan ikke slette enhedsalias", + "unable-delete-entity-alias-text": "Enhedsalias '{{entityAlias}}' kan ikke slettes, da det bruges af følgende widget(s):
{{widgetsList}}", + "duplicate-alias-error": "Duplikeret alias fundet '{{alias}}'.
Enhedsaliasser skal være unikke inden for dashboardet.", + "missing-entity-filter-error": "Filter mangler for alias '{{alias}}'.", + "configure-alias": "Konfigurer alias '{{alias}}'", "alias": "Alias", - "alias-required": "Entitetsalias er påkrævet.", - "remove-alias": "Fjern entitetsalias", - "add-alias": "Tilføj entitetsalias", - "entity-list": "Entitetsliste", - "entity-type": "Entitetstype", - "entity-types": "Entitetstyper", - "entity-type-list": "Liste over entitetstype", - "any-entity": "Enhver entitet", - "enter-entity-type": "Indtast entitetstype", - "no-entities-matching": "", - "no-entity-types-matching": "", - "name-starts-with": "Navnet begynder med", - "use-entity-name-filter": "Anvend filter", - "entity-list-empty": "Ingen entiteter valgt.", - "entity-name-filter-required": "Entitetsnavnfilter er påkrævet.", - "entity-name-filter-no-entity-matched": "", + "alias-required": "Enhedsalias er påkrævet.", + "remove-alias": "Fjern enhedsalias", + "add-alias": "Tilføj enhedsalias", + "edit-alias": "Rediger enhed-alias", + "entity-list": "Liste over enheder", + "entity-type": "Enhedstype", + "entity-types": "Enhedstyper", + "entity-type-list": "Liste over enhedstyper", + "any-entity": "Enhver enhed", + "add-entity-type": "Tilføj enhedstype", + "enter-entity-type": "Indtast enhedstype", + "no-entities-matching": "Ingen enheder matcher '{{entity}}'.", + "no-entities-text": "Ingen enheder fundet", + "no-entity-types-matching": "Ingen enhedstyper matcher '{{entityType}}'.", + "name-starts-with": "Navneudtryk", + "help-text": "Brug '%' efter behov: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter": "Brug filter", + "entity-list-empty": "Ingen enheder valgt.", + "entity-type-list-required": "Mindst én enhedstype skal vælges.", + "entity-name-filter-required": "Filter for enhedsnavn er påkrævet.", + "entity-name-filter-no-entity-matched": "Ingen enheder, der starter med '{{entity}}', blev fundet.", "all-subtypes": "Alle", - "select-entities": "Vælg entiteter", + "select-entities": "Vælg enheder", "no-aliases-found": "Ingen aliasser fundet.", - "no-alias-matching": "", + "no-alias-matching": "'{{alias}}' ikke fundet.", "create-new-alias": "Opret en ny!", + "create-new": "Opret ny", "key": "Nøgle", "key-name": "Nøglenavn", "no-keys-found": "Ingen nøgler fundet.", - "no-key-matching": "", + "no-key-matching": "'{{key}}' ikke fundet.", "create-new-key": "Opret en ny!", "type": "Type", - "type-required": "Entitetstype er påkrævet.", + "type-required": "Enhedstype er påkrævet.", "type-device": "Enhed", "type-devices": "Enheder", - "list-of-devices": "", - "device-name-starts-with": "", + "list-of-devices": "{ count, plural, =1 {Én enhed} other {Liste over # enheder} }", + "device-name-starts-with": "Enheder, hvis navne starter med '{{prefix}}'", "type-device-profile": "Enhedsprofil", "type-device-profiles": "Enhedsprofiler", - "list-of-device-profiles": "", - "device-profile-name-starts-with": "", + "clear-selected-profiles": "Ryd valgte profiler", + "list-of-device-profiles": "{ count, plural, =1 {Én enhedsprofil} other {Liste over # enhedsprofiler} }", + "device-profile-name-starts-with": "Enhedsprofiler, hvis navne starter med '{{prefix}}'", + "type-asset-profile": "Aktivprofil", + "type-asset-profiles": "Aktivprofiler", + "list-of-asset-profiles": "{ count, plural, =1 {Én aktivprofil} other {Liste over # aktivprofiler} }", + "asset-profile-name-starts-with": "Aktivprofiler, hvis navne starter med '{{prefix}}'", "type-asset": "Aktiv", "type-assets": "Aktiver", - "list-of-assets": "", - "asset-name-starts-with": "", - "type-entity-view": "Entitetsvisning", - "type-entity-views": "Entitetsvisninger", - "list-of-entity-views": "", - "entity-view-name-starts-with": "", + "list-of-assets": "{ count, plural, =1 {Én aktiv} other {Liste over # aktiver} }", + "asset-name-starts-with": "Aktiver, hvis navne starter med '{{prefix}}'", + "type-entity-view": "Enhedsvisning", + "type-entity-views": "Enhedsvisninger", + "list-of-entity-views": "{ count, plural, =1 {Én enhedsvisning} other {Liste over # enhedsvisninger} }", + "entity-view-name-starts-with": "Enhedsvisninger, hvis navne starter med '{{prefix}}'", "type-rule": "Regel", "type-rules": "Regler", - "list-of-rules": "", - "rule-name-starts-with": "", + "list-of-rules": "{ count, plural, =1 {Én regel} other {Liste over # regler} }", + "rule-name-starts-with": "Regler, hvis navne starter med '{{prefix}}'", "type-plugin": "Plugin", "type-plugins": "Plugins", - "list-of-plugins": "", - "plugin-name-starts-with": "", + "list-of-plugins": "{ count, plural, =1 {Én plugin} other {Liste over # plugins} }", + "plugin-name-starts-with": "Plugins, hvis navne starter med '{{prefix}}'", "type-tenant": "Lejer", "type-tenants": "Lejere", - "list-of-tenants": "", - "tenant-name-starts-with": "", + "list-of-tenants": "{ count, plural, =1 {Én lejer} other {Liste over # lejere} }", + "tenant-name-starts-with": "Lejere, hvis navne starter med '{{prefix}}'", "type-tenant-profile": "Lejerprofil", "type-tenant-profiles": "Lejerprofiler", - "list-of-tenant-profiles": "", - "tenant-profile-name-starts-with": "", + "list-of-tenant-profiles": "{ count, plural, =1 {Én lejerprofil} other {Liste over # lejerprofiler} }", + "tenant-profile-name-starts-with": "Lejerprofiler, hvis navne starter med '{{prefix}}'", "type-customer": "Kunde", "type-customers": "Kunder", - "list-of-customers": "", - "customer-name-starts-with": "", + "list-of-customers": "{ count, plural, =1 {Én kunde} other {Liste over # kunder} }", + "customer-name-starts-with": "Kunder, hvis navne starter med '{{prefix}}'", "type-user": "Bruger", "type-users": "Brugere", - "list-of-users": "", - "user-name-starts-with": "", + "list-of-users": "{ count, plural, =1 {Én bruger} other {Liste over # brugere} }", + "user-name-starts-with": "Brugere, hvis navne starter med '{{prefix}}'", "type-dashboard": "Dashboard", "type-dashboards": "Dashboards", - "list-of-dashboards": "", - "dashboard-name-starts-with": "", + "list-of-dashboards": "{ count, plural, =1 {Én dashboard} other {Liste over # dashboards} }", + "dashboard-name-starts-with": "Dashboards, hvis navne starter med '{{prefix}}'", "type-alarm": "Alarm", "type-alarms": "Alarmer", - "list-of-alarms": "", - "alarm-name-starts-with": "", + "list-of-alarms": "{ count, plural, =1 {Én alarm} other {Liste over # alarmer} }", + "alarm-name-starts-with": "Alarmer, hvis navne starter med '{{prefix}}'", "type-rulechain": "Regelkæde", "type-rulechains": "Regelkæder", - "list-of-rulechains": "", - "rulechain-name-starts-with": "", - "type-scheduler-event": "Planlægningsbegivenhed", - "type-scheduler-events": "Planlægningsbegivenheder", - "list-of-scheduler-events": "", - "scheduler-event-name-starts-with": "", - "type-blob-entity": "Blob-entitet", - "type-blob-entities": "Blob-entiteter", - "list-of-blob-entities": "", - "blob-entity-name-starts-with": "", - "type-rulenode": "Regelknude", - "type-rulenodes": "Regelknuder", - "list-of-rulenodes": "", - "rulenode-name-starts-with": "", - "type-current-customer": "Nuværende kunde", - "type-current-tenant": "Nuværende lejer", - "type-current-user": "Nuværende bruger", - "type-current-user-owner": "Nuværende brugerejer", - "search": "Søg efter entiteter", - "selected-entities": "", - "entity-name": "Entitetsnavn", - "entity-label": "Entitetsetiket", - "details": "Entitetsoplysninger", - "no-entities-prompt": "Ingen entiteter fundet", + "list-of-rulechains": "{ count, plural, =1 {Én regelkæde} other {Liste over # regelkæder} }", + "rulechain-name-starts-with": "Regelkæder, hvis navne starter med '{{prefix}}'", + "type-rulenode": "Regelnode", + "type-rulenodes": "Regelnoder", + "list-of-rulenodes": "{ count, plural, =1 {Én regelnode} other {Liste over # regelnoder} }", + "rulenode-name-starts-with": "Regelnoder, hvis navne starter med '{{prefix}}'", + "type-current-customer": "Aktuel kunde", + "type-current-tenant": "Aktuel lejer", + "type-current-user": "Aktuel bruger", + "type-current-user-owner": "Aktuel bruger-ejer", + "type-calculated-field": "Beregnet felt", + "type-calculated-fields": "Beregnet felter", + "type-widgets-bundle": "Widgetpakke", + "type-widgets-bundles": "Widgetpakker", + "list-of-widgets-bundles": "{ count, plural, =1 {Én widgetpakke} other {Liste over # widgetpakker} }", + "type-widget": "Widget", + "type-widgets": "Widgets", + "list-of-widgets": "{ count, plural, =1 {Én widget} other {Liste over # widgets} }", + "search": "Søg i enheder", + "selected-entities": "{ count, plural, =1 {1 enhed} other {# enheder} } valgt", + "entity-name": "Enhedsnavn", + "entity-label": "Enhedsetiket", + "details": "Enhedsdetaljer", + "no-entities-prompt": "Ingen enheder fundet", "no-data": "Ingen data at vise", - "columns-to-display": "Kolonner, der skal vises", - "type-api-usage-state": "Api-brugstilstand", - "type-entity-group": "Entitetsgruppe", - "type-converter": "Dataomformer", - "type-converters": "Dataomformere", - "list-of-converters": "", - "converter-name-starts-with": "", - "type-integration": "Integration", - "type-integrations": "Integrationer", - "list-of-integrations": "", - "integration-name-starts-with": "", - "type-role": "Rolle", - "type-roles": "Roller", - "list-of-roles": "", - "role-name-starts-with": "", - "type-group-permission": "Gruppetilladelse" - }, - "entity-group": { - "entity-group": "Entitetsgruppe", - "details": "Oplysninger", - "columns": "Kolonner", - "add-column": "Tilføj kolonne", - "column-value": "Værdi", - "column-value-required": "Kolonneværdi er påkrævet.", - "column-title": "Titel", - "default-sort-order": "Standardsorteringsrækkefølge", - "default-sort-order-required": "Standardsorteringsrækkefølge er påkrævet.", - "hide-in-mobile-view": "Mobil skjult", - "use-cell-style-function": "Brug celletypefunktion", - "use-cell-content-function": "Brug celleindholdsfunktion", - "edit-column": "Rediger kolonne", - "column-details": "Kolonneoplysninger", - "actions": "Handlinger", - "settings": "Indstillinger", - "search": "Søg entitetsgrupper", - "delete": "Slet entitetsgruppe", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "add": "Tilføj entitetsgruppe", - "open-entity-group": "Åbn entitetsgruppe", - "add-entity-group-text": "Tilføj ny entitetsgruppe", - "no-entity-groups-text": "Ingen entitetsgrupper fundet", - "entity-group-details": "Oplysninger om entitetsgruppe", - "selected-entity-groups": "", - "delete-entity-groups": "Slet entitetsgrupper", - "delete-entity-group-title": "", - "delete-entity-group-text": "Vær forsigtig. Efter bekræftelsen vil entitetsgruppen og alle relaterede data være uoprettelige.", - "delete-entity-groups-title": "", - "delete-entity-groups-action-title": "", - "delete-entity-groups-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte entitetsgrupper blive fjernet, og alle relaterede data vil være uoprettelige.", - "device-groups": "Enhedsgrupper", - "asset-groups": "Aktivgrupper", - "customer-groups": "Kundegrupper", - "device-group": "Enhedsgruppe", - "asset-group": "Aktivgruppe", - "user-group": "Brugergruppe", - "user-groups": "Brugergrupper", - "customer-group": "Kundegruppe", - "entity-view-groups": "Entitetsvisningsgrupper", - "entity-view-group": "Entitetsvisningsgruppe", - "dashboard-groups": "Dashboardgrupper", - "dashboard-group": "Dashboardgruppe", - "fetch-more": "Hent mere", - "column-type": { - "column-type": "Kolonnetype", - "client-attribute": "Klientattribut", - "shared-attribute": "Delt attribut", - "server-attribute": "Serverattribut", - "timeseries": "Tidsserier", - "entity-field": "Entitetsfelt" + "columns-to-display": "Kolonner at vise", + "type-api-usage-state": "API-brugsstatus", + "type-edge": "Edge", + "type-edges": "Edges", + "list-of-edges": "{ count, plural, =1 {Én edge} other {Liste over # edges} }", + "edge-name-starts-with": "Edges, hvis navne starter med '{{prefix}}'", + "version-conflict": { + "message": "Vil du overskrive eksisterende version eller kassere ændringer og indlæse den nyeste version?", + "link": "Du kan downloade din version af {{entityType}} ved hjælp af dette", + "overwrite": "Overskriv version", + "discard": "Kassér ændringer" }, - "column-type-required": "Kolonnetype er påkrævet.", - "entity-field": { - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "type": "Type", - "device_profile": "Enhedsprofil", - "assigned_customer": "Tildelt kunde", - "authority": "Autoritet", - "first_name": "Fornavn", - "last_name": "Efternavn", - "email": "E-mail", - "title": "Titel", - "country": "Land", - "state": "Region", - "city": "By", - "address": "Adresse", - "address2": "Adresse 2", - "zip": "Postnummer", - "phone": "Telefon", - "label": "Mærkning" - }, - "sort-order": { - "asc": "Stigende", - "desc": "Faldende", - "none": "Ingen" - }, - "details-mode": { - "on-row-click": "Ved klik på række", - "on-action-button-click": "Ved klik på knappen Oplysninger", - "disabled": "Deaktiveret" - }, - "change-owner": "Skift ejer", - "select-target-owner": "Vælg målejer", - "no-owners-matching": "", - "target-owner-required": "Målejer er påkrævet.", - "confirm-change-owner-title": "", - "confirm-change-owner-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte enheder blive fjernet fra den aktuelle ejer og blive placeret i gruppen 'Alle' for målejeren.", - "add-to-group": "Tilføj til gruppe", - "move-to-group": "Flyt til gruppe", - "select-entity-group": "Vælg entitetsgruppe", - "no-entity-groups-matching": "", - "target-entity-group-required": "Målentitetsgruppe er påkrævet.", - "select-user-group": "Vælg brugergruppe", - "no-user-groups-matching": "", - "target-user-group-required": "Målbrugergruppe er påkrævet.", - "remove-from-group": "Fjern fra gruppe", - "group-table-title": "Gruppetabeltitel", - "enable-search": "Aktivér entitetssøgning", - "enable-add": "Aktivér tilføjelse af entiteter", - "enable-delete": "Aktivér sletning af entiteter", - "enable-selection": "Aktivér entitetsvalg", - "enable-group-transfer": "Aktivér gruppeoverførselshandlinger", - "display-pagination": "Vis sidenummerering", - "default-page-size": "Standard sidestørrelse", - "enable-assignment-actions": "Aktivér tildelingshandlinger", - "enable-credentials-management": "Aktivér administration af brugeroplysninger", - "enable-login-as-user": "Aktivér login som brugerhandling", - "enable-users-management": "Aktivér administration af brugere", - "enable-customers-management": "Aktivér administration af kunder", - "enable-assets-management": "Aktivér administration af aktiver", - "enable-devices-management": "Aktivér administration af enheder", - "enable-entity-views-management": "Aktivér administration af entitetsvisninger", - "enable-dashboards-management": "Aktivér administration af dashboards", - "open-details-on": "Åbn entitetsoplysninger på", - "select-existing": "Vælg eksisterende entitetsgruppe", - "create-new": "Opret ny entitetsgruppe", - "new-entity-group-name": "Nyt entitetsgruppenavn", - "entity-group-list": "Entitetsgruppeliste", - "entity-group-list-empty": "Ingen entitetsgrupper valgt.", - "name-starts-with": "Entitetsgruppenavn starter med", - "entity-group-name-filter-required": "Entitetsgruppenavnfilter er påkrævet.", - "roles": "Roller", - "permissions": "Tilladelser", - "public": "Offentlig", - "entity-group-public": "Entitetsgruppe er offentlig", - "make-public": "Gør entitetsgruppen offentlig", - "make-private": "Gør entitetsgruppen privat", - "make-public-entity-group-title": "", - "make-public-entity-group-text": "Efter bekræftelsen vil entitetsgruppen og alle dens entiteter blive gjort offentlige og tilgængelige for andre.", - "make-private-entity-group-title": "", - "make-private-entity-group-text": "Efter bekræftelsen vil entitetsgruppen og alle dens entiteter blive gjort private og vil ikke være tilgængelige for andre.", - "share": "Del entitetsgruppe", - "copyId": "Kopiér entitetsgruppe-id", - "idCopiedMessage": "Entitetsgruppe-id er blevet kopieret til udklipsholder", - "entity-group-name": "Entitetsgruppenavn", - "all-users": "Alle brugere" + "type-tb-resource": "Ressource", + "type-tb-resources": "Ressourcer", + "list-of-tb-resources": "{ count, plural, =1 {Én ressource} other {Liste over # ressourcer} }", + "type-ota-package": "OTA-pakke", + "type-rpc": "RPC", + "type-queue": "Kø", + "type-queue-stats": "Køstatistik", + "type-queues-stats": "Statistik over køer", + "type-notification": "Notifikation", + "type-notification-rule": "Notifikationsregel", + "type-notification-rules": "Notifikationsregler", + "list-of-notification-rules": "{ count, plural, =1 {Én notifikationsregel} other {Liste over # notifikationsregler} }", + "type-notification-target": "Notifikationsmodtager", + "type-notification-targets": "Notifikationsmodtagere", + "list-of-notification-targets": "{ count, plural, =1 {Én notifikationsmodtager} other {Liste over # notifikationsmodtagere} }", + "type-notification-request": "Notifikationsanmodning", + "type-notification-template": "Notifikationsskabelon", + "type-notification-templates": "Notifikationsskabeloner", + "list-of-notification-templates": "{ count, plural, =1 {Én notifikationsskabelon} other {Liste over # notifikationsskabeloner} }", + "link": "link", + "type-oauth2-client": "OAuth 2.0 klient", + "type-oauth2-clients": "OAuth 2.0 klienter", + "list-of-oauth2-clients": "{ count, plural, =1 {Én OAuth 2.0 klient} other {Liste over # OAuth 2.0 klienter} }", + "type-domain": "Domæne", + "type-domains": "Domæner", + "list-of-domains": "{ count, plural, =1 {Ét domæne} other {Liste over # domæner} }", + "type-mobile-app": "Mobilapplikation", + "type-mobile-apps": "Mobilapplikationer", + "list-of-mobile-apps": "{ count, plural, =1 {Én mobilapplikation} other {Liste over # mobilapplikationer} }", + "type-mobile-app-bundle": "Mobilpakke", + "type-mobile-app-bundles": "Mobilpakker", + "list-of-mobile-app-bundles": "{ count, plural, =1 {Én mobilpakke} other {Liste over # mobilpakker} }" }, "entity-field": { "created-time": "Oprettelsestidspunkt", @@ -1545,103 +2588,108 @@ "type": "Type", "first-name": "Fornavn", "last-name": "Efternavn", - "email": "E-mail", + "email": "Email", "title": "Titel", "country": "Land", - "state": "Region", + "state": "Stat / Region", "city": "By", "address": "Adresse", "address2": "Adresse 2", "zip": "Postnummer", "phone": "Telefon", - "label": "Mærkning" + "label": "Etiket", + "queue-name": "Kønavn", + "service-id": "Service Id", + "owner-name": "Ejerens navn", + "owner-type": "Ejerens type" }, "entity-view": { "entity-view": "Entitetsvisning", "entity-view-required": "Entitetsvisning er påkrævet.", "entity-views": "Entitetsvisninger", - "management": "Administration af entitetsvisning", + "management": "Håndtering af entitetsvisning", "view-entity-views": "Vis entitetsvisninger", - "entity-view-alias": "Alias for entitetsvisning", - "aliases": "Aliasser for entitetsvisning", - "no-alias-matching": "", + "entity-view-alias": "Entitetsvisningsalias", + "aliases": "Entitetsvisningsaliasser", + "no-alias-matching": "'{{alias}}' blev ikke fundet.", "no-aliases-found": "Ingen aliasser fundet.", - "no-key-matching": "", + "no-key-matching": "'{{key}}' blev ikke fundet.", "no-keys-found": "Ingen nøgler fundet.", "create-new-alias": "Opret en ny!", "create-new-key": "Opret en ny!", - "duplicate-alias-error": "", - "configure-alias": "", - "no-entity-views-matching": "", + "duplicate-alias-error": "Duplikeret alias fundet '{{alias}}'.
Entitetsvisningsaliasser skal være unikke i dashboardet.", + "configure-alias": "Konfigurer aliaset '{{alias}}'", + "no-entity-views-matching": "Ingen entitetsvisninger matchede '{{entity}}'.", "public": "Offentlig", "alias": "Alias", "alias-required": "Alias for entitetsvisning er påkrævet.", - "remove-alias": "Fjern alias for entitetsvisning", - "add-alias": "Tilføj alias for entitetsvisning", - "name-starts-with": "Entitetsvisningsnavn starter med", - "entity-view-list": "Liste over entitetsvisning", - "use-entity-view-name-filter": "Anvend filter", + "remove-alias": "Fjern entitetsvisningsalias", + "add-alias": "Tilføj entitetsvisningsalias", + "name-starts-with": "Entitetsvisningsnavn udtryk", + "help-text": "Brug '%' efter behov: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list": "Entitetsvisningsliste", + "use-entity-view-name-filter": "Brug filter", "entity-view-list-empty": "Ingen entitetsvisninger valgt.", - "entity-view-name-filter-required": "Entitetsvisningsnavnfilter er påkrævet.", - "entity-view-name-filter-no-entity-view-matched": "", + "entity-view-name-filter-required": "Filter for entitetsvisningsnavn er påkrævet.", + "entity-view-name-filter-no-entity-view-matched": "Ingen entitetsvisninger starter med '{{entityView}}'.", "add": "Tilføj entitetsvisning", "entity-view-public": "Entitetsvisning er offentlig", "assign-to-customer": "Tildel til kunde", "assign-entity-view-to-customer": "Tildel entitetsvisning(er) til kunde", - "assign-entity-view-to-customer-text": "Vælg de entitetsvisninger, der skal tildeles til kunden", + "assign-entity-view-to-customer-text": "Vælg entitetsvisninger, der skal tildeles kunden", "no-entity-views-text": "Ingen entitetsvisninger fundet", - "assign-to-customer-text": "Vælg den kunde, der skal tildeles entitetsvisning(er)", - "entity-view-details": "Oplysninger for entitetsvisning", + "assign-to-customer-text": "Vælg den kunde, som entitetsvisning(en) skal tildeles", + "entity-view-details": "Detaljer for entitetsvisning", "add-entity-view-text": "Tilføj ny entitetsvisning", "delete": "Slet entitetsvisning", "assign-entity-views": "Tildel entitetsvisninger", - "assign-entity-views-text": "", + "assign-entity-views-text": "Tildel { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } til kunde", "delete-entity-views": "Slet entitetsvisninger", "make-public": "Gør entitetsvisning offentlig", "make-private": "Gør entitetsvisning privat", - "unassign-from-customer": "Fjern tildeling fra kunde", - "unassign-entity-views": "Fjern tildeling af entitetsvisninger", - "unassign-entity-views-action-title": "", + "unassign-from-customer": "Fjern fra kunde", + "unassign-entity-views": "Fjern entitetsvisninger", + "unassign-entity-views-action-title": "Fjern { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } fra kunde", "assign-new-entity-view": "Tildel ny entitetsvisning", - "delete-entity-view-title": "", - "delete-entity-view-text": "Vær forsigtig. Efter bekræftelsen vil entitetsvisningen og alle relaterede data være uoprettelige.", - "delete-entity-views-title": "", - "delete-entity-views-action-title": "", - "delete-entity-views-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte entitetsvisninger blive fjernet, og alle relaterede data vil være uoprettelige.", - "make-public-entity-view-title": "", - "make-public-entity-view-text": "Efter bekræftelsen vil entitetsvisningen og alle dens data blive gjort offentlige og tilgængelige for andre.", - "make-private-entity-view-title": "", - "make-private-entity-view-text": "Efter bekræftelsen vil entitetsvisningen og alle dens data blive gjort private og vil ikke være tilgængelige for andre.", - "unassign-entity-view-title": "", - "unassign-entity-view-text": "Efter bekræftelsen vil entitetsvisningens tildeling blive fjernet og vil ikke være tilgængelig for kunden.", - "unassign-entity-view": "Fjern tildeling af entitetsvisning", - "unassign-entity-views-title": "", - "unassign-entity-views-text": "Efter bekræftelsen vil tildelingen af alle valgte entitetsvisninger blive fjernet og vil ikke være tilgængelige for kunden.", + "delete-entity-view-title": "Er du sikker på, at du vil slette entitetsvisningen '{{entityViewName}}'?", + "delete-entity-view-text": "Vær forsigtig, efter bekræftelse vil entitetsvisningen og alle relaterede data ikke kunne gendannes.", + "delete-entity-views-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }?", + "delete-entity-views-action-title": "Slet { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }", + "delete-entity-views-text": "Vær forsigtig, efter bekræftelse vil alle valgte entitetsvisninger blive fjernet, og relaterede data vil ikke kunne gendannes.", + "make-public-entity-view-title": "Er du sikker på, at du vil gøre entitetsvisningen '{{entityViewName}}' offentlig?", + "make-public-entity-view-text": "Efter bekræftelse vil entitetsvisningen og alle dens data være offentlige og tilgængelige for andre.", + "make-private-entity-view-title": "Er du sikker på, at du vil gøre entitetsvisningen '{{entityViewName}}' privat?", + "make-private-entity-view-text": "Efter bekræftelse vil entitetsvisningen og alle dens data være private og ikke være tilgængelige for andre.", + "unassign-entity-view-title": "Er du sikker på, at du vil fjerne tildelingen af entitetsvisningen '{{entityViewName}}'?", + "unassign-entity-view-text": "Efter bekræftelse vil entitetsvisningen blive fjernet og ikke være tilgængelig for kunden.", + "unassign-entity-view": "Fjern entitetsvisning", + "unassign-entity-views-title": "Er du sikker på, at du vil fjerne { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }?", + "unassign-entity-views-text": "Efter bekræftelse vil alle valgte entitetsvisninger blive fjernet og ikke være tilgængelige for kunden.", "entity-view-type": "Entitetsvisningstype", "entity-view-type-required": "Entitetsvisningstype er påkrævet.", "select-entity-view-type": "Vælg entitetsvisningstype", "enter-entity-view-type": "Indtast entitetsvisningstype", "any-entity-view": "Enhver entitetsvisning", - "no-entity-view-types-matching": "", - "entity-view-type-list-empty": "Der er ikke valgt nogen entitetsvisningstyper.", + "no-entity-view-types-matching": "Ingen entitetsvisningstyper matcher '{{entitySubtype}}'.", + "entity-view-type-list-empty": "Ingen entitetsvisningstyper valgt.", "entity-view-types": "Entitetsvisningstyper", "created-time": "Oprettelsestidspunkt", "name": "Navn", "name-required": "Navn er påkrævet.", + "name-max-length": "Navn skal være mindre end 256 tegn", + "type-max-length": "Entitetsvisningstype skal være mindre end 256 tegn", "description": "Beskrivelse", - "events": "Begivenheder", - "details": "Oplysninger", + "events": "Hændelser", + "details": "Detaljer", "copyId": "Kopiér entitetsvisnings-id", - "idCopiedMessage": "Entitetsvisnings-id er blevet kopieret til udklipsholder", + "idCopiedMessage": "Entitetsvisnings-id er blevet kopieret til udklipsholderen", "assignedToCustomer": "Tildelt til kunde", "unable-entity-view-device-alias-title": "Kan ikke slette entitetsvisningsalias", - "unable-entity-view-device-alias-text": "", + "unable-entity-view-device-alias-text": "Enhedsaliaset '{{entityViewAlias}}' kan ikke slettes, da det bruges af følgende widget(s):
{{widgetsList}}", "select-entity-view": "Vælg entitetsvisning", - "start-date": "Startdato", "start-ts": "Starttid", - "end-date": "Slutdato", "end-ts": "Sluttid", - "date-limits": "Datofrister", + "date-limits": "Datogrænser", "client-attributes": "Klientattributter", "shared-attributes": "Delte attributter", "server-attributes": "Serverattributter", @@ -1650,60 +2698,76 @@ "shared-attributes-placeholder": "Delte attributter", "server-attributes-placeholder": "Serverattributter", "timeseries-placeholder": "Tidsserier", - "target-entity": "Målentitet", - "attributes-propagation": "Overførsel af attributter", - "attributes-propagation-hint": "Entitetsvisning vil automatisk kopiere specificerede attributter fra målentitet, hver gang du gemmer eller opdaterer denne entitetsvisning. Af ydelsesmæssige årsager overføres målenhedsattributter ikke til entitetsvisning ved hver attributændring. Du kan aktivere automatisk overførsel ved at konfigurere regelknuden \"Kopiér til visning\" i din regelkæde og knytte meddelelserne \"Postattributter\" og \"Attributter opdateret\" til den nye regelknude.", + "target-entity": "Målenhed", + "attributes-propagation": "Attributter propagation", + "attributes-propagation-hint": "Entitetsvisningen kopierer automatisk de specificerede attributter fra målenheden, hver gang du gemmer eller opdaterer denne entitetsvisning. Af hensyn til ydeevnen bliver attributterne ikke automatisk overført ved hver ændring. Du kan aktivere automatisk overførsel ved at konfigurere \"copy to view\" regelnode i din regelkæde og forbinde \"Post attributes\" og \"Attributes Updated\" beskeder til den nye regelnode.", "timeseries-data": "Tidsseriedata", - "timeseries-data-hint": "Konfigurer tidsseriedatanøgler for målentiteten, der vil være tilgængelige for entitetsvisningen. Disse tidsseriedata er skrivebeskyttede.", - "selected-entity-views": "", - "search": "Søg efter entitetsvisninger", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte entitetsvisninger", - "select-group-to-move": "Vælg målgruppe for at flytte valgte entitetsvisninger", - "remove-entity-views-from-group": "", - "group": "Gruppe af entitetsvisninger", - "list-of-groups": "", - "group-name-starts-with": "" + "timeseries-data-hint": "Konfigurer tidsserie-nøgler fra målenheden, som vil være tilgængelige i entitetsvisningen. Disse data er skrivebeskyttede.", + "search": "Søg i entitetsvisninger", + "selected-entity-views": "{ count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } valgt", + "assign-entity-view-to-edge": "Tildel entitetsvisning(er) til Edge", + "assign-entity-view-to-edge-text": "Vælg de entitetsvisninger, der skal tildeles Edge", + "unassign-entity-view-from-edge-title": "Er du sikker på, at du vil fjerne tildelingen af entitetsvisningen '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text": "Efter bekræftelse vil entitetsvisningen blive fjernet og ikke være tilgængelig af Edge.", + "unassign-entity-views-from-edge-action-title": "Fjern { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } fra Edge", + "unassign-entity-view-from-edge": "Fjern entitetsvisning", + "unassign-entity-views-from-edge-title": "Er du sikker på, at du vil fjerne { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }?", + "unassign-entity-views-from-edge-text": "Efter bekræftelse vil alle valgte entitetsvisninger blive fjernet og ikke være tilgængelige af Edge." }, "event": { - "events": "Begivenheder", - "event-type": "Begivenhedstype", + "event-type": "Hændelsestype", + "events-filter": "Filtrer hændelser", + "clean-events": "Ryd hændelser", "type-error": "Fejl", - "type-lc-event": "Livscyklusbegivenhed", - "type-rw-event": "Rådata", - "type-stats": "Statistikker", - "type-debug-converter": "Debug", - "type-debug-integration": "Debug", - "type-debug-rule-node": "Debug", - "type-debug-rule-chain": "Debug", - "no-events-prompt": "Ingen begivenheder fundet", + "type-lc-event": "Livscyklushændelse", + "type-stats": "Statistik", + "type-debug-rule-node": "Fejlfinding", + "type-debug-rule-chain": "Fejlfinding", + "type-debug-calculated-field": "Fejlfinding", + "arguments": "Argumenter", + "result": "Resultat", + "no-events-prompt": "Ingen hændelser fundet", "error": "Fejl", "alarm": "Alarm", - "event-time": "Begivenhedstidspunkt", + "event-time": "Hændelsestidspunkt", "server": "Server", - "body": "Tekst", + "body": "Indhold", "method": "Metode", "type": "Type", - "in": "Ind", - "out": "Ud", "metadata": "Metadata", - "message": "Meddelelse", - "entity": "Entitet", - "message-id": "Meddelelses-id", - "message-type": "Meddelelsestype", + "message": "Besked", + "message-id": "Besked-id", + "copy-message-id": "Kopiér besked-id", + "message-type": "Beskedtype", "data-type": "Datatype", "relation-type": "Relationstype", "data": "Data", - "event": "Begivenhed", + "event": "Hændelse", "status": "Status", "success": "Succes", "failed": "Mislykket", - "messages-processed": "Behandlede meddelelser", - "errors-occurred": "Der opstod fejl", - "uuid": "UUID" + "messages-processed": "Behandlede beskeder", + "max-messages-processed": "Maksimalt antal behandlede beskeder", + "min-messages-processed": "Minimalt antal behandlede beskeder", + "errors-occurred": "Opståede fejl", + "max-errors-occurred": "Maksimalt antal fejl", + "min-errors-occurred": "Minimalt antal fejl", + "min-value": "Mindste værdi er 0.", + "all-events": "Alle", + "has-error": "Har fejl", + "entity-id": "Enheds-id", + "copy-entity-id": "Kopiér enheds-id", + "entity-type": "Enhedstype", + "clear-filter": "Ryd filter", + "clear-request-title": "Ryd alle hændelser", + "clear-request-text": "Er du sikker på, at du vil rydde alle hændelser?", + "started": "Startet", + "updated": "Opdateret", + "stopped": "Stoppet" }, "extension": { "extensions": "Udvidelser", - "selected-extensions": "", + "selected-extensions": "{ count, plural, =1 {1 udvidelse} other {# udvidelser} } valgt", "type": "Type", "key": "Nøgle", "value": "Værdi", @@ -1711,46 +2775,44 @@ "extension-id": "Udvidelses-id", "extension-type": "Udvidelsestype", "transformer-json": "JSON *", - "unique-id-required": "Aktuelt udvidelses-id findes allerede.", + "unique-id-required": "Nuværende udvidelses-id findes allerede.", "delete": "Slet udvidelse", "add": "Tilføj udvidelse", "edit": "Rediger udvidelse", - "view": "Vis udvidelse", - "delete-extension-title": "", - "delete-extension-text": "Vær forsigtig. Efter bekræftelsen vil udvidelsen og alle relaterede data være uoprettelige.", - "delete-extensions-title": "", - "delete-extensions-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte udvidelser blive fjernet.", - "converters": "Omformere", - "converter-id": "Omformer-id", + "delete-extension-title": "Er du sikker på, at du vil slette udvidelsen '{{extensionId}}'?", + "delete-extension-text": "Vær forsigtig, efter bekræftelsen vil udvidelsen og alle relaterede data ikke kunne gendannes.", + "delete-extensions-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 udvidelse} other {# udvidelser} }?", + "delete-extensions-text": "Vær forsigtig, efter bekræftelsen vil alle valgte udvidelser blive fjernet.", + "converters": "Konvertere", + "converter-id": "Konverterings-id", "configuration": "Konfiguration", - "converter-configurations": "Omformerkonfigurationer", + "converter-configurations": "Konverteringskonfigurationer", "token": "Sikkerhedstoken", - "add-converter": "Tilføj omformer", - "add-config": "Tilføj omformerkonfiguration", - "device-name-expression": "Enhedsnavnudtryk", - "device-type-expression": "Enhedstypeudtryk", + "add-converter": "Tilføj konverter", + "add-config": "Tilføj konverterkonfiguration", + "device-name-expression": "Enhedsnavn udtryk", + "device-type-expression": "Enhedstype udtryk", "custom": "Brugerdefineret", "to-double": "Til dobbelt", "transformer": "Transformer", - "json-required": "Transformer-json er påkrævet.", - "json-parse": "Kan ikke parse transformer-json.", + "json-required": "Transformer JSON er påkrævet.", + "json-parse": "Kan ikke fortolke transformer JSON.", "attributes": "Attributter", "add-attribute": "Tilføj attribut", - "add-map": "Tilføj tilknytningselement", + "add-map": "Tilføj mappingelement", "timeseries": "Tidsserier", - "add-timeseries": "Tilføj tidsserier", + "add-timeseries": "Tilføj tidsserie", "field-required": "Felt er påkrævet", "brokers": "Mæglere", "add-broker": "Tilføj mægler", "host": "Vært", "port": "Port", - "port-range": "Porten skal ligge i området fra 1 til 65535.", - "ssl": "Ssl", - "credentials": "Brugeroplysninger", + "port-range": "Port skal være i området fra 1 til 65535.", + "ssl": "SSL", + "credentials": "Legitimationsoplysninger", "username": "Brugernavn", "password": "Adgangskode", - "retry-interval": "Forsøg interval igen i millisekunder", - "sas": "Signatur for delt adgang", + "retry-interval": "Genforsøgsinterval i millisekunder", "anonymous": "Anonym", "basic": "Basis", "pem": "PEM", @@ -1758,133 +2820,133 @@ "private-key": "Privat nøglefil *", "cert": "Certifikatfil *", "no-file": "Ingen fil valgt.", - "drop-file": "Træk og slip en fil, eller klik for at vælge en fil, der skal uploades.", - "mapping": "Tilknytning", + "drop-file": "Træk en fil her eller klik for at vælge en fil til upload.", + "mapping": "Kortlægning", "topic-filter": "Emnefilter", - "converter-type": "Omformertype", - "converter-json": "Json", - "json-name-expression": "Udtryk for enhedsnavn json", - "topic-name-expression": "Udtryk for enhedsnavnemne", - "json-type-expression": "Udtryk for enhedstype json", - "topic-type-expression": "Udtryk for enhedstypeemne", - "attribute-key-expression": "Udtryk for attributnøgle", - "attr-json-key-expression": "Udtryk for attributnøgle json", - "attr-topic-key-expression": "Udtryk for attributnøgleemne", - "request-id-expression": "Udtryk for anmodnings-id", - "request-id-json-expression": "Udtryk for anmodnings-id json", - "request-id-topic-expression": "Udtryk for anmodnings-id emne", - "response-topic-expression": "Udtryk for svaremne", - "value-expression": "Udtryk for værdi", + "converter-type": "Konverteringstype", + "converter-json": "JSON", + "json-name-expression": "Enhedsnavn JSON-udtryk", + "topic-name-expression": "Enhedsnavn emneudtryk", + "json-type-expression": "Enhedstype JSON-udtryk", + "topic-type-expression": "Enhedstype emneudtryk", + "attribute-key-expression": "Attributnøgle udtryk", + "attr-json-key-expression": "Attributnøgle JSON-udtryk", + "attr-topic-key-expression": "Attributnøgle emneudtryk", + "request-id-expression": "Anmodnings-id udtryk", + "request-id-json-expression": "Anmodnings-id JSON-udtryk", + "request-id-topic-expression": "Anmodnings-id emneudtryk", + "response-topic-expression": "Svar emneudtryk", + "value-expression": "Værdinudtryk", "topic": "Emne", "timeout": "Timeout i millisekunder", - "converter-json-required": "Omformer-json er påkrævet.", - "converter-json-parse": "Omformeren json kunne ikke parses.", - "filter-expression": "Udtryk for filter", - "connect-requests": "Tilslutningsanmodninger", - "add-connect-request": "Tilføj tilslutningsanmodning", - "disconnect-requests": "Afbryd anmodninger", - "add-disconnect-request": "Tilføj afbrydelsesanmodning", - "attribute-requests": "Attributanmodninger", - "add-attribute-request": "Tilføj attributanmodning", + "converter-json-required": "Konverter JSON er påkrævet.", + "converter-json-parse": "Kan ikke fortolke konverter JSON.", + "filter-expression": "Filterudtryk", + "connect-requests": "Forbindelsesforespørgsler", + "add-connect-request": "Tilføj forbindelsesforespørgsel", + "disconnect-requests": "Afbrydelsesforespørgsler", + "add-disconnect-request": "Tilføj afbrydelsesforespørgsel", + "attribute-requests": "Attributforespørgsler", + "add-attribute-request": "Tilføj attributforespørgsel", "attribute-updates": "Attributopdateringer", "add-attribute-update": "Tilføj attributopdatering", - "server-side-rpc": "Serverside RPC", - "add-server-side-rpc-request": "Tilføj serverside RPC-anmodning", - "device-name-filter": "Enhedsnavnfilter", + "server-side-rpc": "RPC på serversiden", + "add-server-side-rpc-request": "Tilføj RPC-forespørgsel på serversiden", + "device-name-filter": "Filter for enhedsnavn", "attribute-filter": "Attributfilter", "method-filter": "Metodefilter", - "request-topic-expression": "Anmod om udtryk for emne", - "response-timeout": "Svartimeout i millisekunder", - "topic-expression": "Udtryk for emne", - "client-scope": "Klientomfang", + "request-topic-expression": "Forespørgselsemneudtryk", + "response-timeout": "Svar-timeout i millisekunder", + "topic-expression": "Emneudtryk", + "client-scope": "Klientområde", "add-device": "Tilføj enhed", "opc-server": "Servere", "opc-add-server": "Tilføj server", "opc-add-server-prompt": "Tilføj venligst server", "opc-application-name": "Applikationsnavn", - "opc-application-uri": "Applikation uri", - "opc-scan-period-in-seconds": "Scanningsperiode i sekunder", + "opc-application-uri": "Applikations-URI", + "opc-scan-period-in-seconds": "Scanperiode i sekunder", "opc-security": "Sikkerhed", "opc-identity": "Identitet", - "opc-keystore": "Keystore", + "opc-keystore": "Nøglelager", "opc-type": "Type", "opc-keystore-type": "Type", "opc-keystore-location": "Placering *", "opc-keystore-password": "Adgangskode", "opc-keystore-alias": "Alias", - "opc-keystore-key-password": "Nøgleadgangskode", - "opc-device-node-pattern": "Mønster for enhedsknude", - "opc-device-name-pattern": "Mønster for enhedsnavn", + "opc-keystore-key-password": "Nøgle adgangskode", + "opc-device-node-pattern": "Enhedsnode-mønster", + "opc-device-name-pattern": "Enhedsnavnmønster", "modbus-server": "Servere/slaver", "modbus-add-server": "Tilføj server/slave", "modbus-add-server-prompt": "Tilføj venligst server/slave", "modbus-transport": "Transport", - "modbus-tcp-reconnect": "Automatisk gentilslutning", + "modbus-tcp-reconnect": "Genopret automatisk forbindelse", "modbus-rtu-over-tcp": "RTU over TCP", - "modbus-port-name": "Serielt portnavn", + "modbus-port-name": "Seriel portnavn", "modbus-encoding": "Kodning", "modbus-parity": "Paritet", - "modbus-baudrate": "Baudrate", + "modbus-baudrate": "Baudhastighed", "modbus-databits": "Databits", "modbus-stopbits": "Stopbits", - "modbus-databits-range": "Databits skal ligge i området fra 7 til 8.", - "modbus-stopbits-range": "Stopbits skal ligge i området fra 1 til 2.", - "modbus-unit-id": "Enheds-id", - "modbus-unit-id-range": "Enheds-id skal ligge i området fra 1 til 247.", + "modbus-databits-range": "Databits skal være i området fra 7 til 8.", + "modbus-stopbits-range": "Stopbits skal være i området fra 1 til 2.", + "modbus-unit-id": "Enheds-ID", + "modbus-unit-id-range": "Enheds-ID skal være i området fra 1 til 247.", "modbus-device-name": "Enhedsnavn", - "modbus-poll-period": "Undersøgelsesperiode (ms)", - "modbus-attributes-poll-period": "Attributundersøgelsesperiode (ms)", - "modbus-timeseries-poll-period": "Tidsserieundersøgelsesperiode (ms)", - "modbus-poll-period-range": "Undersøgelsesperioden skal have positiv værdi.", - "modbus-tag": "Tag", + "modbus-poll-period": "Afspørgningsperiode (ms)", + "modbus-attributes-poll-period": "Attributafspørgningsperiode (ms)", + "modbus-timeseries-poll-period": "Tidsserieafspørgningsperiode (ms)", + "modbus-poll-period-range": "Afspørgningsperioden skal være en positiv værdi.", + "modbus-tag": "Mærkat", "modbus-function": "Funktion", "modbus-register-address": "Registeradresse", - "modbus-register-address-range": "Registeradresse skal ligge i området fra 0 til 65535.", + "modbus-register-address-range": "Registeradresse skal være i området fra 0 til 65535.", "modbus-register-bit-index": "Bitindeks", - "modbus-register-bit-index-range": "Bitindeks skal ligge i området fra 0 til 15.", - "modbus-register-count": "Registeroptælling", - "modbus-register-count-range": "Registeroptælling skal være en positiv værdi.", - "modbus-byte-order": "Byterækkefølge", + "modbus-register-bit-index-range": "Bitindeks skal være i området fra 0 til 15.", + "modbus-register-count": "Antal registre", + "modbus-register-count-range": "Antal registre skal være en positiv værdi.", + "modbus-byte-order": "Byte-rækkefølge", "sync": { "status": "Status", - "sync": "Synkroniseret", + "sync": "Synkroniser", "not-sync": "Ikke synkroniseret", "last-sync-time": "Sidste synkroniseringstidspunkt", - "not-available": "Ikke til rådighed" + "not-available": "Ikke tilgængelig" }, - "export-extensions-configuration": "Konfiguration af eksportér udvidelser", - "import-extensions-configuration": "Konfiguration af importér udvidelser", + "export-extensions-configuration": "Eksportér udvidelseskonfiguration", + "import-extensions-configuration": "Importér udvidelseskonfiguration", "import-extensions": "Importér udvidelser", "import-extension": "Importér udvidelse", "export-extension": "Eksportér udvidelse", - "file": "Fil for udvidelser", - "invalid-file-error": "Ugyldig udvidelsesfil", - "text": "TEKST", - "json": "JSON", - "binary": "BINÆR", - "hex": "HEX" + "file": "Udvidelsesfil", + "invalid-file-error": "Ugyldig udvidelsesfil" + }, + "feature": { + "advanced-features": "Avancerede funktioner" }, "filter": { "add": "Tilføj filter", "edit": "Rediger filter", "name": "Filternavn", "name-required": "Filternavn er påkrævet.", - "duplicate-filter": "Filter med samme navn findes allerede.", + "duplicate-filter": "Et filter med samme navn findes allerede.", "filters": "Filtre", - "unable-delete-filter-title": "Filteret kunne ikke slettes", - "unable-delete-filter-text": "", - "duplicate-filter-error": "", - "missing-key-filters-error": "", + "unable-delete-filter-title": "Kan ikke slette filter", + "unable-delete-filter-text": "Filteret '{{filter}}' kan ikke slettes, da det bruges af følgende widget(s):
{{widgetsList}}", + "duplicate-filter-error": "Duplikatfilter fundet '{{filter}}'.
Filtre skal være unikke i dashboardet.", + "missing-key-filters-error": "Nøglefiltre mangler for filteret '{{filter}}'.", "filter": "Filter", "editable": "Redigerbar", "no-filters-found": "Ingen filtre fundet.", "no-filter-text": "Intet filter angivet", - "add-filter-prompt": "Tilføj venligst filter", - "no-filter-matching": "", - "create-new-filter": "Opret en ny!", + "add-filter-prompt": "Tilføj venligst et filter", + "no-filter-matching": "'{{filter}}' blev ikke fundet.", + "create-new-filter": "Opret et nyt!", + "create-new": "Opret ny", "filter-required": "Filter er påkrævet.", "operation": { - "operation": "Driftsopgave", + "operation": "Operation", "equal": "lig med", "not-equal": "ikke lig med", "starts-with": "starter med", @@ -1893,26 +2955,29 @@ "not-contains": "indeholder ikke", "greater": "større end", "less": "mindre end", - "greater-or-equal": "større end eller lig med", - "less-or-equal": "mindre end eller lig med", + "greater-or-equal": "større eller lig med", + "less-or-equal": "mindre eller lig med", "and": "og", - "or": "eller" + "or": "eller", + "in": "i", + "not-in": "ikke i" }, - "ignore-case": "ignorer versalfølsomhed", + "ignore-case": "ignorér store/små bogstaver", "value": "Værdi", "remove-filter": "Fjern filter", - "preview": "Visning af filter", + "duplicate-filter-action": "Duplikér filter", + "preview": "Filterforhåndsvisning", "no-filters": "Ingen filtre konfigureret", "add-filter": "Tilføj filter", "add-complex-filter": "Tilføj komplekst filter", "add-complex": "Tilføj kompleks", "complex-filter": "Komplekst filter", "edit-complex-filter": "Rediger komplekst filter", - "edit-filter-user-params": "Rediger filterprædikat for brugerparametre", - "filter-user-params": "Filtrerprædikat for brugerparametre", + "edit-filter-user-params": "Rediger brugerparametre for filterbetingelse", + "filter-user-params": "Filterbetingelsens brugerparametre", "user-parameters": "Brugerparametre", - "display-label": "Etiket, der skal vises", - "order-priority": "Prioritet af feltrækkefølge", + "display-label": "Etiket til visning", + "order-priority": "Prioritet for feltorden", "key-filter": "Nøglefilter", "key-filters": "Nøglefiltre", "key-name": "Nøglenavn", @@ -1920,73 +2985,79 @@ "key-type": { "key-type": "Nøgletype", "attribute": "Attribut", - "timeseries": "Tidsserier", + "timeseries": "Tidsserie", "entity-field": "Entitetsfelt", - "constant": "Konstant" + "constant": "Konstant", + "client-attribute": "Klientattribut", + "server-attribute": "Serverattribut", + "shared-attribute": "Delt attribut" }, "value-type": { "value-type": "Værditype", "string": "Streng", "numeric": "Numerisk", "boolean": "Boolesk", - "date-time": "Datotid" + "date-time": "Dato og tid" }, - "value-type-required": "Nøgleværditype er påkrævet.", - "key-value-type-change-title": "Er du sikker på, at du vil ændre nøgleværditypen?", - "key-value-type-change-message": "Hvis du bekræfter ny værditype, fjernes alle indtastede nøglefiltre.", + "value-type-required": "Værditype for nøgle er påkrævet.", + "key-value-type-change-title": "Er du sikker på, at du vil ændre værditypen?", + "key-value-type-change-message": "Hvis du bekræfter den nye værditype, vil alle indtastede nøglefiltre blive fjernet.", "no-key-filters": "Ingen nøglefiltre konfigureret", "add-key-filter": "Tilføj nøglefilter", "remove-key-filter": "Fjern nøglefilter", "edit-key-filter": "Rediger nøglefilter", "date": "Dato", "time": "Tid", - "current-tenant": "Nuværende lejer", - "current-customer": "Nuværende kunde", - "current-user": "Nuværende bruger", - "current-device": "Nuværende enhed", + "current-tenant": "Aktuel udlejer", + "current-customer": "Aktuel kunde", + "current-user": "Aktuel bruger", + "current-device": "Aktuel enhed", "default-value": "Standardværdi", + "default-comma-separated-values": "Standardværdi med komma-adskilte værdier", "dynamic-source-type": "Dynamisk kildetype", + "dynamic-value": "Dynamisk værdi", "no-dynamic-value": "Ingen dynamisk værdi", "source-attribute": "Kildeattribut", "switch-to-dynamic-value": "Skift til dynamisk værdi", "switch-to-default-value": "Skift til standardværdi", - "inherit-owner": "Nedarv fra ejer", - "source-attribute-not-set": "Hvis kildeattributten ikke er angivet" + "inherit-owner": "Arv fra ejer", + "source-attribute-not-set": "Hvis kildeattribut ikke er angivet" }, "fullscreen": { "expand": "Udvid til fuld skærm", - "exit": "Afslut fuldskærm", - "toggle": "Skift mellem fuldskærmstilstand", + "exit": "Afslut fuld skærm", + "toggle": "Skift fuldskærmstilstand", "fullscreen": "Fuld skærm" }, "function": { "function": "Funktion" }, "gateway": { + "gateway-name": "Gateway navn", + "gateway-name-required": "Gateway navn er påkrævet.", + "gateways": "Gateways", "create-new-gateway": "Opret en ny gateway", - "create-new-gateway-text": "", - "gateway-exists": "Enhed med samme navn findes allerede.", - "gateway-name": "Gateway-navn", - "gateway-name-required": "Gateway-navn er påkrævet.", - "gateway-saved": "Gateway-konfigurationen blev gemt.", + "create-new-gateway-text": "Er du sikker på, at du vil oprette en ny gateway med navnet: '{{gatewayName}}'?", + "launch-command": "Start kommando", "no-gateway-found": "Ingen gateway fundet.", - "no-gateway-matching": "" + "no-gateway-matching": " '{{item}}' blev ikke fundet." }, "grid": { - "delete-item-title": "Er du sikker på, du ønsker at slette dette element?", - "delete-item-text": "Vær forsigtig. Efter bekræftelsen vil dette element og alle relaterede data være uoprettelige.", - "delete-items-title": "", - "delete-items-action-title": "", - "delete-items-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte elementer blive fjernet, og alle relaterede data vil være uoprettelige.", + "delete-item-title": "Er du sikker på, at du vil slette dette element?", + "delete-item-text": "Vær forsigtig, efter bekræftelsen vil dette element og alle relaterede data være uoprettelige.", + "delete-items-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 element} other {# elementer} }?", + "delete-items-action-title": "Slet { count, plural, =1 {1 element} other {# elementer} }", + "delete-items-text": "Vær forsigtig, efter bekræftelsen vil alle valgte elementer blive fjernet, og alle relaterede data vil være uoprettelige.", "add-item-text": "Tilføj nyt element", "no-items-text": "Ingen elementer fundet", - "item-details": "Oplysninger om element", + "item-details": "Elementdetaljer", "delete-item": "Slet element", "delete-items": "Slet elementer", - "scroll-to-top": "Gå til toppen" + "scroll-to-top": "Rul til toppen" }, "help": { - "goto-help-page": "Gå til hjælpesiden" + "goto-help-page": "Gå til hjælpeside", + "show-help": "Vis hjælp" }, "home": { "home": "Hjem", @@ -1996,408 +3067,505 @@ "avatar": "Avatar", "open-user-menu": "Åbn brugermenu" }, + "file-input": { + "browse-file": "Gennemse fil", + "browse-files": "Gennemse filer" + }, + "image": { + "gallery": "Billedgalleri", + "search": "Søg billede", + "selected-images": "{ count, plural, =1 {1 billede} other {# billeder} } valgt", + "created-time": "Oprettelsestid", + "name": "Navn", + "name-required": "Navn er påkrævet.", + "resolution": "Opløsning", + "size": "Størrelse", + "system": "System", + "download-image": "Download billede", + "export-image": "Eksportér billede til JSON", + "import-image": "Importér billede fra JSON", + "upload-image": "Upload billede", + "edit-image": "Rediger billede", + "image-details": "Billeddetaljer", + "no-images": "Ingen billeder fundet", + "delete-image": "Slet billede", + "delete-image-title": "Er du sikker på, at du vil slette billedet '{{imageTitle}}'?", + "delete-image-text": "Vær forsigtig, efter bekræftelsen vil billedet være uopretteligt.", + "delete-images-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 billede} other {# billeder} }?", + "delete-images-text": "Vær forsigtig, efter bekræftelsen vil alle valgte billeder blive fjernet, og alle relaterede data vil være uoprettelige.", + "list-mode": "Listevisning", + "grid-mode": "Gittervisning", + "image-preview": "Billedforhåndsvisning", + "update-image": "Opdater billede", + "export-failed-error": "Kan ikke eksportere billede: {{error}}", + "image-json-file": "Billede JSON-fil", + "invalid-image-json-file-error": "Kan ikke importere billede fra JSON: Ugyldig billed-JSON-datastruktur.", + "image-is-in-use": "Billedet bruges af andre enheder", + "images-are-in-use": "Billeder bruges af andre enheder", + "image-is-in-use-text": "Billedet '{{title}}' blev ikke slettet, fordi det bruges af følgende enheder:", + "images-are-in-use-text": "Ikke alle billeder blev slettet, fordi de bruges af andre enheder.
Du kan se refererede enheder ved at klikke på Referencer-knappen i den tilsvarende billedrække.
Hvis du stadig vil slette disse billeder, skal du vælge dem i tabellen nedenfor og klikke på Slet valgte-knappen.", + "delete-image-in-use-text": "Hvis du stadig vil slette billedet, skal du klikke på Slet alligevel-knappen.", + "system-entities": "Systemenheder:", + "entities": "enheder:", + "references": "Referencer", + "include-system-images": "Medtag systembilleder", + "clear-image": "Ryd billede", + "no-image": "Intet billede", + "no-image-selected": "Intet billede valgt", + "browse-from-gallery": "Gennemse fra galleri", + "set-link": "Angiv link", + "image-link": "Billedlink", + "link": "Link", + "copy-image-link": "Kopiér billedlink", + "embed-image": "Indlejr billede", + "embed-to-html": "Indlejr i HTML", + "embed-to-html-hint": "Denne funktion vil gøre linket tilgængeligt for enhver uautoriseret bruger.", + "embed-to-html-text": "Ved at bruge følgende kodeeksempel kan du indlejre et billede i komponenter baseret på almindelig HTML.
Sådanne komponenter inkluderer HTML-kort-widgets, celleindholdsfunktioner osv.", + "embed-to-angular-template": "Indlejr i Angular HTML-skabelon", + "embed-to-angular-template-text": "Ved at bruge følgende kodeeksempel kan du indlejre et billede i Angular HTML-skabelonen, som vil blive brugt til komponenter.
Sådanne komponenter inkluderer Markdown-widget, HTML-sektion i widget-editor, brugerdefinerede handlinger osv." + }, + "image-input": { + "drop-images-or": "Træk og slip billeder eller", + "drag-and-drop": "Træk og slip", + "or": "eller", + "browse": "Gennemse", + "no-images": "Ingen billeder valgt", + "images": "billeder" + }, "import": { "no-file": "Ingen fil valgt", - "drop-file": "Træk og slip en JSON-fil, eller klik for at vælge en fil, der skal uploades.", - "drop-csv-file": "Træk og slip en CSV-fil, eller klik for at vælge en fil, der skal uploades.", - "drop-file-csv": "Træk og slip en CSV-fil, eller klik for at vælge en fil, der skal uploades.", + "drop-file": "Slip en JSON-fil eller klik for at vælge en fil, der skal uploades.", + "drop-json-file-or": "Træk og slip en JSON-fil eller", + "drop-file-csv": "Slip en CSV-fil eller klik for at vælge en fil, der skal uploades.", + "drop-file-csv-or": "Træk og slip en CSV-fil eller", "column-value": "Værdi", "column-title": "Titel", - "column-example": "Eksempel på værdidata", - "column-key": "Attribut-/telemetrinøgle", - "csv-delimiter": "CSV-skilletegn", + "column-example": "Eksempel på dataværdi", + "column-key": "Attribut/telemetrinøgle", + "credentials": "Oplysninger", + "csv-delimiter": "CSV-afgrænser", "csv-first-line-header": "Første linje indeholder kolonnenavne", "csv-update-data": "Opdater attributter/telemetri", + "details": "Detaljer", "import-csv-number-columns-error": "En fil skal indeholde mindst to kolonner", - "import-csv-invalid-format-error": "", + "import-csv-invalid-format-error": "Ugyldigt filformat. Linje: '{{line}}'", "column-type": { "name": "Navn", "type": "Type", - "label": "Mærkning", + "label": "Etiket", "column-type": "Kolonnetype", "client-attribute": "Klientattribut", "shared-attribute": "Delt attribut", "server-attribute": "Serverattribut", - "timeseries": "Tidsserier", - "entity-field": "Entitetsfelt", + "timeseries": "Tidsserie", + "entity-field": "Enhedsfelt", "access-token": "Adgangstoken", - "isgateway": "Er gateway", + "x509": "X.509", + "mqtt": { + "client-id": "MQTT-klient-ID", + "user-name": "MQTT-brugernavn", + "password": "MQTT-adgangskode" + }, + "lwm2m": { + "client-endpoint": "LwM2M endpoint klientnavn", + "security-config-mode": "LwM2M sikkerhedskonfigurationstilstand", + "client-identity": "LwM2M klientidentitet", + "client-key": "LwM2M klientnøgle", + "client-cert": "LwM2M klient offentlig nøgle", + "bootstrap-server-security-mode": "LwM2M bootstrap server sikkerhedstilstand", + "bootstrap-server-secret-key": "LwM2M bootstrap server hemmelig nøgle", + "bootstrap-server-public-key-id": "LwM2M bootstrap server offentlig nøgle eller ID", + "lwm2m-server-security-mode": "LwM2M server sikkerhedstilstand", + "lwm2m-server-secret-key": "LwM2M server hemmelig nøgle", + "lwm2m-server-public-key-id": "LwM2M server offentlig nøgle eller ID" + }, + "snmp": { + "host": "SNMP-vært", + "port": "SNMP-port", + "version": "SNMP-version (v1, v2c eller v3)", + "community-string": "SNMP community-streng" + }, + "isgateway": "Er Gateway", "activity-time-from-gateway-device": "Aktivitetstid fra gateway-enhed", - "description": "Beskrivelse" + "description": "Beskrivelse", + "routing-key": "Edge-nøgle", + "secret": "Edge-hemmelighed" }, "stepper-text": { "select-file": "Vælg en fil", - "configuration": "Importér konfiguration", - "column-type": "Vælg kolonnetype", - "creat-entities": "Oprettelse af nye entiteter", - "done": "Udført" + "configuration": "Importkonfiguration", + "column-type": "Vælg kolonnernes type", + "creat-entities": "Opretter nye enheder" }, "message": { - "create-entities": "", - "update-entities": "", - "error-entities": "" + "create-entities": "{{count}} nye enheder blev oprettet med succes.", + "update-entities": "{{count}} enheder blev opdateret med succes.", + "error-entities": "Der opstod en fejl under oprettelsen af {{count}} enheder." } }, - "integration": { - "integration": "Integration", - "integrations": "Integrationer", - "select-integration": "Vælg integration", - "no-integrations-matching": "", - "integration-required": "Integration er påkrævet", - "delete": "Slet integration", - "management": "Integrationsstyring", - "add-integration-text": "Tilføj ny integration", - "no-integrations-text": "Ingen integrationer fundet", - "selected-integrations": "", - "delete-integration-title": "", - "delete-integration-text": "Vær forsigtig. Efter bekræftelsen vil integrationen og alle relaterede data være uoprettelige.", - "delete-integrations-title": "", - "delete-integrations-action-title": "", - "delete-integrations-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte integrationer blive fjernet, og alle relaterede data vil være uoprettelige.", - "events": "Begivenheder", - "enabled": "Aktiveret", - "allow-create-devices-or-assets": "Tillad oprettelse af enheder eller aktiver", - "add": "Tilføj integration", - "search": "Søg efter integrationer", - "integration-details": "Integrationsoplysninger", - "details": "Oplysninger", - "copyId": "Kopiér integrations-id", - "idCopiedMessage": "Integrations-id er blevet kopieret til udklipsholder", - "debug-mode": "Debug-tilstand", - "enable-security": "Aktivér sikkerhed", - "enable-security-new": "Aktivér sikkerhed for automatiske tokenopdateringer", - "headers-filter": "Filter for overskrifter", - "header": "Overskrift", - "no-headers-filter": "Intet filter for overskrifter", - "downlink-url": "Downlink URL", - "downlink-url-required": "Downlink URL er påkrævet", - "create-loriot-output": "Opret Loriot-applikationsoutput", - "send-downlink": "Send downlink", - "server": "Server", - "server-required": "Server er påkrævet", - "app-id": "Applikations-id", - "app-id-required": "Applikations-id er påkrævet", - "app-token": "Applikationsadgangstoken", - "app-token-required": "Applikationsadgangstoken er påkrævet", - "email": "E-mail", - "email-required": "E-mail er påkrævet", - "application-uri": "Applikation URI", - "as-id": "AS-id", - "as-id-required": "AS-id er påkrævet.", - "as-key": "AS-nøgle", - "as-key-required": "AS-nøgle er påkrævet.", - "client-id-new": "Klient-id", - "client-id-new-required": "Klient-id (login) er påkrævet (login).", - "client-secret": "Klient hemmelig", - "client-secret-required": "Klient hemmelig (adgangskode) er påkrævet (adgangskode).", - "max-time-diff-in-seconds": "Maksimal tidsforskel (sekunder)", - "max-time-diff-in-seconds-required": "Maksimal tidsforskel er påkrævet.", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "name-required": "Navn er påkrævet.", + "scada": { + "symbols": "SCADA-symboler", + "search": "Søg symbol", + "selected-symbols": "{ count, plural, =1 {1 symbol} other {# symboler} } valgt", + "download-symbol": "Download SCADA-symbol", + "export-symbol": "Eksporter SCADA-symbol til JSON", + "import-symbol": "Importer SCADA-symbol fra JSON", + "upload-symbol": "Upload SCADA-symbol", + "update-symbol": "Opdater SCADA-symbol", + "edit-symbol": "Rediger SCADA-symbol", + "symbol-details": "SCADA-symboldetaljer", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "Ingen symboler fundet", + "show-hidden-elements": "Vis skjulte elementer", + "hide-hidden-elements": "Skjul skjulte elementer", + "delete-symbol": "Slet SCADA-symbol", + "delete-symbol-title": "Er du sikker på, at du vil slette SCADA-symbolet '{{imageTitle}}'?", + "delete-symbol-text": "Vær forsigtig, efter bekræftelsen vil SCADA-symbolet ikke kunne gendannes.", + "delete-symbols-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 SCADA-symbol} other {# SCADA-symboler} }?", + "delete-symbols-text": "Vær forsigtig, efter bekræftelsen vil alle valgte SCADA-symboler blive fjernet og al relateret data vil ikke kunne gendannes.", + "include-system-symbols": "Inkluder systemsymboler", + "symbol-preview": "Symbol forhåndsvisning", + "general": "Generelt", + "tags": "Tags", + "properties": "Egenskaber", + "title": "Titel", "description": "Beskrivelse", - "base-url": "Basis-URL", - "base-url-required": "Basis-URL er påkrævet", - "security-key": "Sikkerhedsnøgle", - "http-endpoint": "HTTP-slutpunkts-URL", - "replace-no-content-to-ok": "Erstat svarstatus fra 'Intet indhold' til 'OK'", - "copy-http-endpoint-url": "Kopiér HTTP-slutpunkts-URL", - "http-endpoint-url-copied-message": "HTTP-slutpunkts-URL er blevet kopieret til udklipsholder", - "host": "Vært", - "host-required": "Vært er påkrævet.", - "host-type": "Værtstype", - "host-type-required": "Værtstype er påkrævet.", - "custom-host": "Brugerdefineret vært", - "custom-host-required": "Brugerdefineret vært er påkrævet.", - "port": "Port", - "port-required": "Port er påkrævet.", - "port-range": "Porten skal ligge i området fra 1 til 65535.", - "connect-timeout": "Forbindelsestimeout (sek.)", - "connect-timeout-required": "Forbindelsestimeout er påkrævet.", - "connect-timeout-range": "Forbindelsestimeout skal ligge i området fra 1 til 200.", - "client-id": "Klient-id", - "client-id-required": "Klient-id er påkrævet.", - "client-id-range": "Klient-id skal ligge i et interval fra 1 til 23 tegn. [MQTT-3.1.3-5]", - "client-id-pattern": "Klient-id skal bestå af tal og store og små bogstaver. [MQTT-3.1.3-5]", - "max-bytes-in-message": "Maksimum bytes i meddelelse", - "max-bytes-in-message-range": "Maksimum bytes i meddelelsen skal ligge i området fra 1 til 256000000.", - "device-id": "Enheds-id", - "device-id-required": "Enheds-id er påkrævet.", - "device-id-range": "Enheds-id'et skal ligge i området fra 1 til 65535 tegn.", - "device-id-pattern": "Enheds-id'et skal bestå af tal og store og små bogstaver. [MQTT-3.1.3-5]", - "group-id": "Gruppe-id", - "group-id-required": "Gruppe-id er påkrævet.", - "topics": "Emner", - "topics-required": "Emne er påkrævet.", - "routing-keys": "Routing-nøgler", - "routing-keys-required": "Routing-nøgle er påkrævet.", - "queues": "Køer", - "queues-required": "Kønavn er påkrævet.", - "exchange-name": "Udskiftningsnavn", - "exchange-name-required": "Udskiftningsnavn er påkrævet.", - "downlink-topic": "Downlink-emne", - "connection-timeout": "Forbindelsestimeout, ms", - "connection-timeout-min": "Ugyldig forbindelsestimeoutværdi.", - "handshake-timeout": "Handshake-timeout, ms", - "handshake-timeout-min": "Ugyldig handshake-timeoutværdi.", - "virtual-host": "Virtuel vært", - "rabbit-mq-poll-interval": "Undersøgelsesinterval, ms", - "rabbit-mq-poll-interval-min": "Ugyldig undersøgelsesintervalværdi.", - "application-server-url": "Applikationsserver URL", - "application-server-api-token": "Applikationsserver API-token", - "application-server-api-token-required": "Applikationsserver API-token er påkrævet.", - "bootstrap-servers": "Bootstrap-servere", - "bootstrap-servers-required": "Bootstrap-servere er påkrævet.", - "poll-interval": "Undersøgelsesinterval", - "poll-interval-required": "Undersøgelsesinterval er påkrævet.", - "auto-create-topics": "Opret emner automatisk", - "clean-session": "Ryd session", - "enable-ssl": "Aktivér SSL", - "credentials": "Brugeroplysninger", - "credentials-type": "Type af brugeroplysninger", - "credentials-type-required": "Type af brugeroplysninger er påkrævet.", - "username": "Brugernavn", - "username-required": "Brugernavn er påkrævet.", - "password": "Adgangskode", - "password-required": "Brugernavn er påkrævet.", - "azure-ca-cert": "CA-certifikatfil", - "ca-cert": "CA-certifikatfil *", - "private-key": "Privat nøglefil *", - "private-key-password": "Adgangskode til privat nøgle", - "cert": "Certifikatfil *", - "no-file": "Ingen fil valgt.", - "drop-file": "Træk og slip en fil, eller klik for at vælge en fil, der skal uploades.", - "check-connection": "Kontrollér forbindelsen", - "check-success": "Forbindelse oprettet.", - "topic-filters": "Emnefiltre", - "remove-topic-filter": "Fjern emnefilter", - "add-topic-filter": "Tilføj emnefilter", - "add-topic-filter-prompt": "Tilføj venligst emnefilter", - "topic": "Emne", - "mqtt-qos": "QoS", - "mqtt-qos-at-most-once": "Højst én gang", - "mqtt-qos-at-least-once": "Mindst én gang", - "mqtt-qos-exactly-once": "Præcis én gang", - "downlink-topic-pattern": "Mønster for downlink-emne", - "downlink-topic-pattern-required": "Mønster for downlink-emne er påkrævet.", - "aws-access-key-id": "AWS-adgangsnøgle-id", - "aws-secret-access-key": "AWS hemmelig adgangsnøgle", - "aws-region": "Region", - "aws-iot-endpoint": "AWS IoT-slutpunkt", - "aws-iot-endpoint-required": "AWS IoT-slutpunkt er påkrævet.", - "aws-iot-credentials": "AWS IoT-brugeroplysninger", - "aws-sqs-polling-period-in-seconds": "Undersøgelsesperiode i sekunder", - "aws-sqs-queue-url": "SQS Kø URL", - "aws-sqs-queue-url-required": "SQS Kø URL er påkrævet", - "aws-sqs-access-key-id-required": "Adgangsnøgle-id er påkrævet", - "aws-sqs-secret-access-key-required": "Hemmelig adgangsnøgle er påkrævet", - "application-credentials": "Applikationsbrugeroplysninger", - "api-key": "API-nøgle", - "api-key-required": "API-nøgle er påkrævet.", - "api-key-format": "Ugyldigt API-nøgleformat.", - "auth-token": "Godkendelsestoken", - "auth-token-required": "Godkendelsestoken er påkrævet.", - "region": "Region", - "region-required": "Region er påkrævet.", - "application-id": "Applikations-id", - "application-id-required": "Applikations-id er påkrævet.", - "access-key": "Adgangsnøgle", - "access-key-required": "Adgangsnøgle er påkrævet.", - "connection-parameters": "Tilslutningsparametre", - "service-bus-namespace-name": "Namespace-navn for servicebus", - "service-bus-namespace-name-required": "Namespace-navn for servicebus er påkrævet.", - "connection-string": "Tilslutningsstreng", - "connection-string-required": "Tilslutningsstreng påkrævet!", - "event-hub-name": "Navn på begivenhedshub", - "event-hub-name-required": "Navn på begivenhedshub er påkrævet.", - "event-iot-hub-name-required": "Iot-hubnavn er påkrævet for downlink", - "sas-key-name": "SAS-nøglenavn", - "sas-key-name-required": "SAS-nøglenavn er påkrævet.", - "sas-key": "SAS-nøgle", - "sas-key-required": "SAS-nøgle er påkrævet.", - "iot-hub-name": "IoT-hubnavn (påkrævet for downlink)", - "hostname": "Værtsnavn", - "hostname-required": "Værtsnavn er påkrævet", - "integration-clazz": "Integrationsklasse", - "integration-clazz-required": "Integrationsklasse er påkrævet", - "integration-configuration": "Konfiguration af integration JSON", - "metadata": "Metadata", - "type": "Type", - "type-required": "Type er påkrævet.", - "uplink-converter": "Uplink-dataomformer", - "uplink-converter-required": "Uplink-dataomformer er påkrævet.", - "downlink-converter": "Downlink-dataomformer", - "type-http": "HTTP", - "type-ocean-connect": "OceanConnect", - "type-sigfox": "SigFox", - "type-thingpark": "ThingPark", - "type-loriot": "Loriot", - "type-thingpark-enterprise": "ThingParkEnterprise", - "type-tmobile-iot-cdp": "T-Mobile – IoT CDP", - "type-mqtt": "MQTT", - "type-aws-iot": "AWS IoT", - "type-aws-sqs": "AWS SQS", - "type-aws-kinesis": "AWS-kinese", - "type-ibm-watson-iot": "IBM Watson IoT", - "type-ttn": "TheThingsNetwork", - "type-tti": "The Things Stack", - "type-chirpstack": "ChirpStack", - "type-azure-event-hub": "Azure-begivenhedshub", - "type-azure-iot-hub": "Azure IoT-hub", - "type-opc-ua": "OPC-UA", - "type-custom": "Brugerdefineret", - "type-udp": "UDP", - "type-tcp": "TCP", - "type-kafka": "Kafka", - "type-rabbitmq": "RabbitMQ", - "type-pubsub": "Pub/Sub", - "opc-ua-application-name": "Applikationsnavn", - "opc-ua-application-uri": "Applikation uri", - "opc-ua-scan-period-in-seconds": "Scanningsperiode i sekunder", - "opc-ua-scan-period-in-seconds-required": "Scanningsperiode er påkrævet", - "opc-ua-timeout": "Timeout i millisekunder", - "opc-ua-timeout-required": "Timeout er påkrævet", - "opc-ua-security": "Sikkerhed", - "opc-ua-security-required": "Sikkerhed er påkrævet", - "opc-ua-identity": "Identitet", - "opc-ua-identity-required": "Identitet er påkrævet", - "opc-ua-keystore": "Keystore", - "add-opc-ua-keystore-prompt": "Tilføj venligst keystore-fil", - "opc-ua-keystore-required": "Keystore er påkrævet", - "opc-ua-type": "Type", - "opc-ua-keystore-type": "Type", - "opc-ua-keystore-type-required": "Type er påkrævet", - "opc-ua-keystore-location": "Placering *", - "opc-ua-keystore-password": "Adgangskode", - "opc-ua-keystore-password-required": "Adgangskode er påkrævet", - "opc-ua-keystore-alias": "Alias", - "opc-ua-keystore-alias-required": "Alias er påkrævet", - "opc-ua-keystore-key-password": "Nøgleadgangskode", - "opc-ua-keystore-key-password-required": "Adgangskode er påkrævet", - "opc-ua-mapping": "Tilknytning", - "add-opc-ua-mapping-prompt": "Tilføj tilknytning", - "opc-ua-mapping-type": "Tilknytningstype", - "opc-ua-mapping-type-required": "Tilknytningstype er påkrævet", - "opc-ua-device-node-pattern": "Enhedsknudemønster", - "opc-ua-device-node-pattern-required": "Enhedsknudemønster er påkrævet", - "opc-ua-namespace": "Namespace", - "opc-ua-add-map": "Tilføj tilknytningselement", - "kinesis-stream-name": "Streamnavn", - "kinesis-stream-name-required": "Streamnavn er påkrævet", - "kinesis-region": "Region", - "kinesis-region-required": "Region er påkrævet", - "kinesis-access-key-id": "Adgangsnøgle-id", - "kinesis-access-key-id-required": "Adgangsnøgle-id er påkrævet", - "kinesis-secret-access-key": "Hemmelig adgangsnøgle", - "kinesis-secret-access-key-required": "Hemmelig adgangsnøgle er påkrævet", - "kinesis-use-consumers-with-enhanced-fan-out": "Anvend forbrugere med forbedret Fan-Out", - "kinesis-use-credentials-from-instance-metadata": "Anvend brugeroplysninger fra Amazon EC2 Instance Metadata Service", - "kinesis-application-name": "Applikationsnavn (som standard lig med Streamnavn)", - "kinesis-initial-position-in-stream": "Startposition i stream", - "kinesis-initial-position-in-stream-required": "Startposition i stream påkrævet", - "kinesis-max-records": "Maks. poster", - "kinesis-max-records-required": "Maks. poster er påkrævet", - "kinesis-max-records-length-range": "Maks. længde for poster skal ligge i et område fra 1 til 10000", - "kinesis-request-timeout": "Anmodningstimeout i sekunder", - "kinesis-request-timeout-required": "Anmodningstimeout er påkrævet", - "other-properties": "Andre egenskaber", - "subscription-tags": "Abonnementstags", - "remove-subscription-tag": "Fjern abonnementstags", - "add-subscription-tag": "Tilføj abonnementstags", - "add-subscription-tag-prompt": "Tilføj venligst abonnementstags", - "key": "Nøgle", - "path": "Sti", - "required": "Påkrævet", - "integration-key": "Integrationsnøgle", - "copy-integration-key": "Kopiér integrationsnøgle", - "integration-key-copied-message": "Integrationsnøgle er blevet kopieret til udklipsholder", - "integration-secret": "Integration hemmelig", - "copy-integration-secret": "Kopiér integration hemmelig", - "integration-secret-copied-message": "Integration hemmelig er kopieret til udklipsholder", - "execute-remotely": "Fjernbetjent udførelse", - "handler-configuration": "Handler-konfiguration", - "handler-configuration-type": "Handler-type", - "so-broadcast": "Aktivér broadcast – integration accepterer adressepakker for broadcast", - "so-keepalive-option": "Muliggør afsendelse af keepalive-meddelelser på tilslutningsorienterede stik", - "so-reuse-addr": "Bind processen til en port", - "tcp-no-delay": "Tvinger et stik til at sende data uden buffer (deaktiver Nagle's bufferalgoritme)", - "fail-fast": "Undtagelse, når dekoderen bemærker, at rammelængden vil overstige maksimumstørrelse", - "strip-delimiter": "Fjern skilletegn", - "length-field-offset": "Feltforskydning for længde", - "length-field-offset-required": "Feltforskydning for længde er påkrævet.", - "length-field-offset-range": "Feltforskydning for længde skal ligge i et område fra 0 til 8.", - "length-field-length": "Længdefeltlængde", - "length-field-length-required": "Længdefeltlængde er påkrævet.", - "length-field-length-range": "Længdefeltlængde skal ligge i et område fra 0 til 8.", - "length-adjustment": "Længdejustering (kompenseringsværdien, der skal lægges til værdien af længdefeltet)", - "length-adjustment-required": "Længdejustering er påkrævet.", - "length-adjustment-range": "Længdejustering skal ligge i et område fra 0 til 8.", - "byte-order": "Byterækkefølge for længdefeltet", - "initial-bytes-to-strip": "Antal første bytes, der skal fjernes fra den afkodede ramme", - "initial-bytes-to-strip-required": "Antal første bytes, der skal fjernes fra den afkodede ramme, er påkrævet.", - "initial-bytes-to-strip-range": "Antal første bytes, der skal fjernes fra den afkodede ramme, skal ligge i området fra 0 til 8.", - "so-backlog-option": "Maks. antal ventende tilslutninger på stikket", - "so-backlog-option-required": "Maks. antal ventende tilslutninger på stikket er påkrævet.", - "so-backlog-option-range": "Maks. antal ventende tilslutninger på stikket skal ligge i området fra 1 til 65535.", - "so-rcv-buf": "Bufferens størrelse til indgående stik (i KB)", - "so-rcv-buf-required": "Bufferens størrelse til indgående stik (i KB) er påkrævet.", - "so-rcv-buf-range": "Bufferens størrelse til indgående stik (i KB) skal ligge i området fra 1 til 65535.", - "so-snd-buf": "Bufferens størrelse til udgående stik (i KB)", - "so-snd-buf-required": "Bufferens størrelse til udgående stik (i KB) er påkrævet.", - "so-snd-buf-range": "Bufferens størrelse til udgående stik (i KB) skal ligge i et område fra 1 til 65535.", - "charset-name": "Tegnsætnavn", - "charset-name-required": "Tegnsætnavn er påkrævet.", - "message-separator": "Meddelelsesseparator", - "message-separator-required": "Meddelelsesseparator er påkrævet.", - "character-sequence": "Tegnsekvens", - "character-sequence-required": "Tegnsekvens er påkrævet.", - "max-frame-length": "Maks. rammelængde (i bytes)", - "max-frame-length-required": "Maks. rammelængde (i bytes) er påkrævet.", - "max-frame-length-range": "Maks. rammelængde (i byte) skal ligge i området fra 1 til 65535.", - "handler-type": "Handler-type", - "message-size": "Meddelelsesstørrelse", - "message-size-required": "Meddelelsesstørrelse er påkrævet.", - "type-apache-pulsar": "Apache Pulsar", - "service-url": "Service-URL", - "service-url-required": "Service-URL er påkrævet.", - "subscription-name": "Abonnementsnavn", - "subscription-name-required": "Abonnementsnavn er påkrævet.", - "max-num-messages": "Maks. antal meddelelser", - "max-num-messages-required": "Maks. antal meddelelser er påkrævet.", - "max-num-bytes": "Maks. antal bytes", - "max-num-bytes-required": "Maks. antal bytes påkrævet.", - "timeout-in-ms": "Timeout i millisekunder", - "timeout-in-ms-required": "Timeout i millisekunder er påkrævet.", - "user-id": "Bruger-id", - "user-id-required": "Bruger-id er påkrævet.", - "token": "Token", - "token-required": "Token er påkrævet.", - "project-id": "Projekt-id", - "project-id-required": "Projekt-id er påkrævet.", - "subscription-id": "Abonnements-id", - "subscription-id-required": "Abonnements-id er påkrævet.", - "service-account-key": "Nøglefil til servicekonto", - "service-account-key-required": "Nøglefil til servicekonto er påkrævet.", - "tcp": { - "system-line-separator": "Systemlinjeseparator", - "nul-delimiter": "Nul-skilletegn", - "byte-order-little-endian": "Little Endian", - "byte-order-big-endian": "Big Endian" - }, - "cache-size": "Cachestørrelse", - "cache-time-to-live": "Cache time-to-live i minutter", - "min-cache-size": "Cachestørrelse kan ikke være lavere 0", - "min-cache-time-to-live": "Cache time-to-live kan ikke være lavere 0", - "max-cache-time-to-live": "Ugyldigt cache time-to-live, vælg mellem 0 og 525600" + "search-tags": "Søg tags", + "widget-size": "Widget-størrelse", + "cols": "kolonner", + "rows": "rækker", + "state-render-function": "Tilstandsrendreringsfunktion", + "preview": "Forhåndsvisning", + "preview-widget-action-text": "Widget-handling '{{type}}' udført med succes!", + "no-symbol": "Intet SCADA-symbol", + "no-symbol-selected": "Intet SCADA-symbol valgt", + "clear-symbol": "Ryd SCADA-symbol", + "browse-symbol-from-gallery": "Gennemse SCADA-symbol fra galleri", + "zoom-in": "Zoom ind", + "zoom-out": "Zoom ud", + "create-widget": "Opret widget", + "create-widget-from-symbol": "Opret widget fra SCADA-symbol", + "hidden": "skjult", + "tag": { + "tag": "Tag", + "on-click-action": "Ved klik-handling", + "no-tags": "Ingen tags konfigureret", + "delete-tag-text": "Er du sikker på, at du vil slette tag
{{tag}} fra {{elementType}}-element?", + "update-tag": "Opdater tag", + "enter-tag": "Indtast tag", + "tag-settings": "Tagindstillinger", + "remove-tag": "Fjern tag", + "add-tag": "Tilføj tag" + }, + "behavior": { + "behavior": "Adfærd", + "id": "Id", + "name": "Navn", + "type": "Type", + "no-behaviors": "Ingen adfærd konfigureret", + "add-behavior": "Tilføj adfærd", + "type-action": "Handling", + "type-value": "Værdi", + "type-widget-action": "Widget-handling", + "behavior-settings": "Adfærdsindstillinger", + "remove-behavior": "Fjern adfærd", + "hint": "Tip", + "group-title": "Gruppe titel", + "value-type": "Værditype", + "default-value": "Standardværdi", + "true-label": "Etiket for sand", + "false-label": "Etiket for falsk", + "state-label": "Tilstandsetiket", + "default-payload": "Standardindhold", + "not-unique-behavior-ids-error": "Adfærds-id'er skal være unikke!", + "default-settings": "Standardindstillinger" + }, + "symbol": { + "symbol": "SCADA-symbol", + "fluid-presence": "Væsketilstedeværelse", + "fluid-presence-hint": "Angiver, om der er væske i røret.", + "fluid-present": "Væske til stede", + "present": "Tilstede", + "absent": "Fraværende", + "flow-presence": "Strømningstilstedeværelse", + "flow-presence-hint": "Angiver, om væske strømmer i røret.", + "flow-present": "Strømning til stede", + "flow-direction": "Strømningsretning", + "flow-direction-hint": "Angiver væskens strømningsretning.", + "forward": "Fremad", + "reverse": "Baglæns", + "flow-animation-speed": "Strømningsanimationshastighed", + "flow-animation-speed-hint": "Double-værdi, der angiver animationshastigheden for strømning. 1 - normal hastighed, 0 - ingen animation, < 1 - langsommere, > 1 - hurtigere.", + "leak": "Lækage", + "leak-hint": "Angiver, om der er en lækage i røret.", + "leak-present": "Lækage til stede", + "fluid-color": "Væskefarve", + "pipe-color": "Rørfarve", + "horizontal-pipe": "Horisontalt rør", + "vertical-pipe": "Vertikalt rør", + "horizontal-fluid-color": "Horisontal væskefarve", + "vertical-fluid-color": "Vertikal væskefarve", + "left-pipe": "Venstre rør", + "right-pipe": "Højre rør", + "top-pipe": "Toprør", + "bottom-pipe": "Bundrør", + "left-fluid-color": "Venstre væskefarve", + "right-fluid-color": "Højre væskefarve", + "top-fluid-color": "Top væskefarve", + "bottom-fluid-color": "Bund væskefarve", + "display": "Display", + "display-format": "Visningsformat", + "value": "Værdi", + "decimals": "Decimaler", + "units": "Enheder", + "flow-meter-value-hint": "Double-værdi vist på strømningsmåleren", + "value-hint": "Double-værdi, der angiver den aktuelle værdi", + "running": "Kører", + "running-hint": "Angiver, om komponenten er i drift.", + "warning-state": "Advarselsstatus", + "warning": "Advarsel", + "warning-click": "Klik ved advarsel", + "warning-state-hint": "Angiver, om komponenten er i advarselsstatus.", + "critical-state": "Kritisk status", + "critical": "Kritisk", + "critical-click": "Klik ved kritisk status", + "critical-state-hint": "Angiver, om komponenten er i kritisk status.", + "critical-state-animation": "Kritisk status animation", + "critical-state-animation-hint": "Aktiverer blinkeanimation, når komponenten er i kritisk status.", + "warning-critical-state-animation": "Advarsel/Kritisk status animation", + "warning-critical-state-animation-hint": "Aktiverer blinkeanimation, når komponenten er i advarsels- eller kritisk status.", + "animation": "Animation", + "broken": "Ødelagt", + "broken-hint": "Angiver, om komponenten er ødelagt.", + "on-display-click": "Ved klik på display", + "on-display-click-hint": "Handling udføres, når brugeren klikker på displayet.", + "pipe": "Rør", + "default-border-color": "Standard kantfarve", + "active-border-color": "Aktiv kantfarve", + "warning-border-color": "Advarselskantfarve", + "critical-border-color": "Kritisk kantfarve", + "background-color": "Baggrundsfarve", + "rotation-animation-speed": "Rotationsanimationshastighed", + "rotation-animation-speed-hint": "Double-værdi, der angiver hastigheden for rotationsanimation. 1 - normal hastighed, 0 - ingen animation, < 1 - langsommere, > 1 - hurtigere.", + "on-click": "Ved klik", + "on-click-hint": "Handling udføres, når brugeren klikker på komponenten.", + "connectors-positions": "Forbinderpositioner", + "right-connector": "Højre forbindelse", + "right-top-connector": "Højre topforbinder", + "right-bottom-connector": "Højre bundforbinder", + "left-connector": "Venstre forbindelse", + "left-top-connector": "Venstre topforbinder", + "left-bottom-connector": "Venstre bundforbinder", + "top-left-connector": "Top venstre forbinder", + "top-right-connector": "Top højre forbinder", + "top-connector": "Øverste forbindelse", + "bottom-connector": "Nederste forbindelse", + "running-color": "Kører farve", + "stopped-color": "Stoppet farve", + "stopped": "Stoppet", + "warning-color": "Advarselsfarve", + "critical-color": "Kritisk farve", + "opened": "Åben", + "opened-hint": "Angiver, om komponenten er i åben tilstand.", + "open": "Åben", + "open-hint": "Handling udføres, når brugeren klikker for at åbne komponenten.", + "close": "Luk", + "close-hint": "Handling udføres, når brugeren klikker for at lukke komponenten.", + "close-state-animation": "Lukket tilstand animation", + "close-state-animation-hint": "Aktiverer blinkeanimation, når komponenten er i lukket tilstand.", + "opened-color": "Åben farve", + "closed-color": "Lukket farve", + "opened-rotation-angle": "Åben rotationsvinkel", + "closed-rotation-angle": "Lukket rotationsvinkel", + "tank-capacity": "Tankkapacitet", + "tank-capacity-hint": "Double-værdi, der angiver den samlede tankkapacitet.", + "current-volume": "Aktuelt volumen", + "current-volume-hint": "Double-værdi, der angiver det aktuelle optagede volumen.", + "tank-color": "Tankfarve", + "value-box": "Værdiboks", + "value-text": "Værdi tekst", + "scale": "Skala", + "transparent-mode": "Gennemsigtig tilstand", + "major-ticks": "Store markeringer", + "intervals": "Intervaller", + "major-ticks-color": "Farve for store markeringer", + "normal": "Normal", + "minor-ticks": "Små markeringer", + "minor-ticks-color": "Farve for små markeringer", + "temperature": "Temperatur", + "temperature-hint": "Double-værdi, der angiver den aktuelle temperatur.", + "update-temperature": "Opdater temperatur", + "update-temperature-hint": "Handling udføres, når brugeren klikker for at ændre temperaturen.", + "run": "Kør", + "run-hint": "Handling udføres, når brugeren klikker for at starte komponenten.", + "stop": "Stop", + "stop-hint": "Handling udføres, når brugeren klikker for at stoppe komponenten.", + "temperature-step": "Temperaturtrin", + "heat-pump-color": "Varme pumpe farve", + "power-button-background": "Baggrund for tænd/sluk-knap", + "value-box-background": "Baggrund for værdiboks", + "value-units": "Værdi enheder", + "filtration-mode": "Filtreringstilstand", + "filtration-mode-hint": "Heltalsværdi, der angiver den aktuelle filtreringstilstand.", + "filtration-mode-update": "Opdater filtreringstilstand", + "filtration-mode-update-hint": "Handling udføres, når brugeren klikker for at ændre filtreringstilstanden.", + "filter-mode": "Filter", + "waste-mode": "Affald", + "backwash-mode": "Tilbageskyl", + "recirculate-mode": "Recirkulation", + "rinse-mode": "Skyl", + "closed-mode": "Lukket", + "sand-filter-color": "Sandfilterfarve", + "mode-box-background": "Baggrund for tilstandsboks", + "border-color": "Kantfarve", + "label-color": "Etiketfarve", + "water-leak-hint": "Angiver, om der er en lækage.", + "default-color": "Standardfarve", + "leak-color": "Lækagefarve", + "full-value": "Fuldt niveau", + "full-value-hint": "Double-værdi, der angiver fuldt niveau.", + "label": "Etiket", + "icon": "Ikon", + "button-color": "Knapfarve", + "on-label": "'Tænd' etikettekst", + "off-label": "'Sluk' etikettekst", + "arrow-presence": "Pil til stede", + "arrow-presence-hint": "Angiver, om der er en pil i forbindelsen.", + "arrow-present": "Pil til stede", + "arrow-direction": "Pilretning", + "arrow-direction-hint": "Angiver strømningsretning.", + "flow-animation": "Flowtilstedeværelse", + "flow-animation-hint": "Angiver om der flyder væske i forbindelsen.", + "flow": "Flow", + "flow-line": "Linje", + "flow-line-style": "Linjestil", + "flow-style-hint": "Angiv værdier for Mellemrum og Mellemrum, så deres sum er delelig med 100 uden rest for perfekt animationssynkronisering.", + "flow-dash-cap": "Streg afslutning", + "dash-cap-butt": "Flad", + "dash-cap-round": "Rund", + "dash-cap-square": "Firkantet", + "dash": "Streg", + "gap": "Mellemrum", + "main-line": "Hovedledning", + "line": "Ledning", + "line-color": "Ledningsfarve", + "arrow-color": "Pilfarve", + "target-value": "Målværdi", + "target-value-hint": "Angiver målpunktet på skalaen.", + "min-max-value": "Min og maks værdi", + "min-value": "Min", + "max-value": "Maks", + "progress-bar": "Fremskridtsbjælke", + "progress-arrow": "Fremskridtspil", + "warning-scale-color": "Advarsels skala farve", + "critical-scale-color": "Kritisk skala farve", + "scale-color": "Skalafarve", + "target": "Mål", + "high-warning-state": "Høj advarselsstatus", + "show-high-warning-scale": "Vis høj advarselsskala", + "high-warning-scale": "Høj advarselsskala", + "high-warning-state-hint": "Double-værdi angiver et højt advarselsområde op til kritisk eller maks værdi.", + "low-warning-state": "Lav advarselsstatus", + "show-low-warning-scale": "Vis lav advarselsskala", + "low-warning-scale": "Lav advarselsskala", + "low-warning-state-hint": "Double-værdi angiver et lavt advarselsområde ned til kritisk eller min værdi.", + "high-critical-state": "Høj kritisk status", + "show-high-critical-scale": "Vis høj kritisk skala", + "high-critical-scale": "Høj kritisk skala", + "high-critical-state-hint": "Double-værdi angiver et højt kritisk område op til maksimum på skalaen.", + "low-critical-state": "Lav kritisk status", + "show-low-critical-scale": "Vis lav kritisk status", + "low-critical-scale": "Lav kritisk skala", + "low-critical-state-hint": "Double-værdi angiver et lavt kritisk område ned til minimum på skalaen.", + "filter-color": "Filterfarve", + "colors": "Farver", + "indicator-colors": "Indikatorfarver", + "enabled": "Aktiveret", + "disabled": "Deaktiveret", + "on": "TIL", + "off": "FRA", + "on-off-state": "Tænd/sluk-tilstand", + "on-off-state-hint": "Angiver om komponenten er tændt eller slukket.", + "on-update-state": "Ved opdatering til TIL", + "on-update-state-hint": "Handling der udføres, når brugeren klikker for at ændre tilstanden til TIL.", + "off-update-state": "Ved opdatering til FRA", + "off-update-state-hint": "Handling der udføres, når brugeren klikker for at ændre tilstanden til FRA.", + "voltage": "Spænding", + "input-voltage": "Indgangsspænding", + "input-voltage-hint": "Double-værdi der angiver indgangsspænding.", + "output-voltage": "Udgangsspænding", + "output-voltage-hint": "Double-værdi der angiver udgangsspænding.", + "first-phase-voltage": "Første fase spænding", + "second-phase-voltage": "Anden fase spænding", + "third-phase-voltage": "Tredje fase spænding", + "phase-voltage-hint": "Double-værdi der angiver spænding for den aktuelle fase", + "voltage-hint": "Double-værdi der angiver den aktuelle spænding", + "current-voltage-color": "Aktuel spændingsfarve", + "phase-indicator-color": "Faseindikatorfarve", + "measured": "Målt", + "measured-hint": "Double-værdi der angiver energiforbrug i kilowatt-timer", + "day-rate": "Dagpris", + "night-rate": "Natpris", + "off-peak-rate": "Lavsæson-pris", + "peak-rate": "Spidspris", + "export-rate": "Eksportpris", + "operating-mode": "Driftstilstand", + "bypass-mode": "Bypass", + "operating-mode-hint": "Integer-værdi der angiver aktuel driftstilstand (0 - FRA, 1 - TIL, 2 - BYPASS)", + "connected": "Forbundet", + "connected-hint": "Angiver om komponenten er i forbundet tilstand.", + "disconnected": "Afbrudt", + "indicator": "Indikator", + "operation-mode": "Driftstilstand", + "operation-mode-hint": "Angiver om inverteren er i nettilstand eller invertertilstand.", + "operation-mode-indicators-color": "Farve for driftstilstandsindikatorer", + "mains-on-mode": "Net til", + "inverter-on-mode": "Inverter til", + "charging-mode": "Opladningstilstand", + "charging-mode-hint": "Integer-værdi der angiver opladningstilstand (1 - Bulk, 2 - Absorption, 3 - Float)", + "charging-mode-indicators-color": "Farve for opladningstilstandsindikatorer", + "inverter-faults": "Fejl", + "inverter-fault-indicators-color": "Farve for fejlindikatorer", + "overload-fault": "Overbelastning", + "overload-fault-hint": "Angiver om inverteren er overbelastet.", + "low-battery-fault": "Lavt batteri", + "low-battery-fault-hint": "Angiver om batteriet er overdrevent afladet.", + "temperature-fault": "Temperatur", + "temperature-fault-hint": "Angiver om inverteren har høj temperatur.", + "triangle": "Trekant", + "socket": "Stik", + "left-button": "Venstre knap", + "right-button": "Højre knap", + "alarm-colors": "Alarmfarver", + "hook-color": "Krogfarve" + } }, "item": { "selected": "Valgt" }, "js-func": { - "no-return-error": "Funktionen skal returnere værdi!", - "return-type-mismatch": "", - "tidy": "Tidy", - "mini": "Mini" + "no-return-error": "Funktionen skal returnere en værdi!", + "return-type-mismatch": "Funktionen skal returnere en værdi af typen '{{type}}'!", + "tidy": "Ryd op", + "mini": "Mini", + "modules": "Moduler", + "remove-module": "Fjern modul", + "no-modules": "Ingen moduler konfigureret", + "add-module": "Tilføj modul", + "module-alias": "Alias", + "invalid-module-alias-name": "Ugyldigt aliasnavn", + "module-resource": "JS modulressource", + "not-unique-module-aliases-error": "Modulaliaser skal være unikke!", + "show-module-info": "Vis modulinfo", + "show-module-source-code": "Vis modulens kildekode", + "module-members": "Modulmedlemmer", + "module-no-members": "Modulet har ingen eksporterede medlemmer", + "module-load-error": "Fejl ved indlæsning af modul", + "source-code": "Kildekode", + "source-code-load-error": "Fejl ved indlæsning af kildekode", + "no-js-module-text": "Ingen JS-moduler fundet", + "no-js-module-matching": "Ingen JS-moduler matcher '{{module}}'" }, "key-val": { "key": "Nøgle", @@ -2408,81 +3576,601 @@ }, "layout": { "layout": "Layout", + "layouts": "Layouts", "manage": "Administrer layouts", "settings": "Layoutindstillinger", "color": "Farve", "main": "Hoved", "right": "Højre", - "select": "Vælg mållayout" + "left": "Venstre", + "select": "Vælg mål-layout", + "percentage-width": "Procentvis bredde (%)", + "fixed-width": "Fast bredde (px)", + "left-width": "Venstre kolonne (%)", + "right-width": "Højre kolonne (%)", + "pick-fixed-side": "Fast side: ", + "layout-fixed-width": "Fast bredde (px)", + "value-min-error": "Værdien skal være større end {{min}}{{unit}}", + "value-max-error": "Værdien skal være mindre end {{max}}{{unit}}", + "layout-fixed-width-required": "Fast bredde er påkrævet", + "right-width-percentage-required": "Procentdel for højre side er påkrævet", + "left-width-percentage-required": "Procentdel for venstre side er påkrævet", + "divider": "Skillelinje", + "right-side": "Layout for højre side", + "left-side": "Layout for venstre side", + "add-new-breakpoint": "Tilføj nyt breakpoint", + "breakpoint": "Breakpoint", + "breakpoints": "Breakpoints", + "copy-from": "Kopiér fra", + "size": "Størrelse", + "delete-breakpoint-title": "Er du sikker på, at du vil slette breakpointet '{{name}}'?", + "delete-breakpoint-text": "Bemærk venligst, efter bekræftelse vil breakpointet ikke kunne gendannes, og indstillingerne vil vende tilbage til standard-breakpointet." }, "legend": { - "direction": "Signaturforklaringens retning", - "position": "Signaturforklaringens placering", - "sort-legend": "Sortér datanøgler i signaturforklaring", - "show-max": "Vis maks. værdi", - "show-min": "Vis min. værdi", + "direction": "Retning", + "position": "Position", + "show-values": "Vis værdier", + "min-option": "Min", + "max-option": "Maks", + "average-option": "Gennemsnit", + "total-option": "Total", + "latest-option": "Seneste", + "sort-legend": "Sorter datanøgler i forklaring", + "show-max": "Vis maksimal værdi", + "show-min": "Vis minimumsværdi", "show-avg": "Vis gennemsnitsværdi", - "show-total": "Vis samlet værdi", - "settings": "Indstillinger for signaturforklaring", - "min": "min.", - "max": "maks.", - "avg": "gns.", - "total": "i alt", + "show-total": "Vis totalværdi", + "show-latest": "Vis seneste værdi", + "settings": "Forklaringsindstillinger", + "min": "min", + "max": "maks", + "avg": "gns", + "total": "total", + "latest": "seneste", + "Min": "Min", + "Max": "Maks", + "Avg": "Gns", + "Total": "Total", + "Latest": "Seneste", "comparison-time-ago": { - "previousInterval": "(tidligere interval)", + "previousInterval": "(forrige interval)", + "customInterval": "(brugerdefineret interval)", "days": "(dag siden)", "weeks": "(uge siden)", "months": "(måned siden)", "years": "(år siden)" - } + }, + "column-title": "Kolonne titel", + "label": "Etiket", + "value": "Værdi" }, "login": { - "login": "Log på", + "login": "Log ind", "request-password-reset": "Anmod om nulstilling af adgangskode", "reset-password": "Nulstil adgangskode", "create-password": "Opret adgangskode", + "two-factor-authentication": "To-faktor-godkendelse", "passwords-mismatch-error": "De indtastede adgangskoder skal være ens!", - "password-again": "Gentag adgangskode", - "sign-in": "Log på", - "username": "Brugernavn (e-mail)", + "password-again": "Adgangskode igen", + "sign-in": "Log venligst ind", + "username": "Brugernavn (email)", "remember-me": "Husk mig", "forgot-password": "Glemt adgangskode?", "password-reset": "Nulstilling af adgangskode", - "expired-password-reset-message": "Dine brugeroplysninger er udløbet! Opret venligst en ny adgangskode.", + "expired-password-reset-message": "Dine loginoplysninger er udløbet! Opret venligst en ny adgangskode.", "new-password": "Ny adgangskode", - "new-password-again": "Gentag ny adgangskode", - "password-link-sent-message": "Link til nulstilling af adgangskode blev sendt!", - "email": "E-mail", - "no-account": "Har du ikke en konto?", - "create-account": "Opret en konto", - "login-with": "Log ind med din e-mail", + "new-password-again": "Bekræft ny adgangskode", + "password-link-sent-message": "Nulstillingslink er sendt", + "email": "Email", + "invalid-email-format": "Ugyldigt emailformat.", + "login-with": "Log ind med {{name}}", "or": "eller", - "error": "Login-fejl" + "error": "Loginfejl", + "verify-your-identity": "Bekræft din identitet", + "select-way-to-verify": "Vælg en metode til bekræftelse", + "resend-code": "Send kode igen", + "resend-code-wait": "Send kode igen om { time, plural, =1 {1 sekund} other {# sekunder} }", + "try-another-way": "Prøv en anden metode", + "totp-auth-description": "Indtast sikkerhedskoden fra din autentifikator-app.", + "totp-auth-placeholder": "Kode", + "sms-auth-description": "En sikkerhedskode er sendt til din telefon på {{contact}}.", + "sms-auth-placeholder": "SMS-kode", + "email-auth-description": "En sikkerhedskode er sendt til din emailadresse på {{contact}}.", + "email-auth-placeholder": "Email-kode", + "backup-code-auth-description": "Indtast en af dine backupkoder.", + "backup-code-auth-placeholder": "Backupkode", + "activation-link-expired": "Aktiveringslinket er udløbet", + "activation-link-expired-message": "Linket til at aktivere din profil er udløbet. Du kan vende tilbage til login-siden for at modtage en ny email.", + "reset-password-link-expired": "Nulstillingslinket for adgangskode er udløbet", + "reset-password-link-expired-message": "Linket til at nulstille din adgangskode er udløbet. Du kan vende tilbage til login-siden for at modtage en ny email." }, - "signup": { - "firstname": "Fornavn", - "lastname": "Efternavn", - "email": "E-mail", - "signup": "Tilmeld", - "create-password": "Opret en adgangskode", - "repeat-password": "Gentag din adgangskode", - "have-account": "Har du allerede en konto?", - "signin": "Log på", - "no-captcha-message": "Du skal bekræfte, at du ikke er en robot", - "password-length-message": "Din adgangskode skal bestå af mindst 6 tegn", - "email-verification": "E-mailbekræftelse", - "email-verification-message": "Der er sendt en e-mail med bekræftelsesoplysninger til den angivne e-mailadresse.
Følg instruktionerne i e-mailen for at fuldføre din tilmeldingsprocedure.
Bemærk: Hvis du ikke har set e-mailen efter et stykke tid, skal du tjekke din 'spam'-mappe eller forsøge at sende e-mailen igen ved at klikke på knappen 'Send igen'.", - "account-activation-title": "Aktivering af konto", - "account-activated": "Kontoen er aktiveret!", - "account-activated-text": "Tillykke!
Din konto er blevet aktiveret.", - "resend": "Send igen", - "inactive-user-exists-title": "Inaktiv bruger findes allerede", - "inactive-user-exists-text": "Der er allerede registreret en bruger med en ikke-bekræftet e-mailadresse.
Klik på knappen \"Send igen\", hvis du ønsker at sende bekræftelses-e-mailen igen.", - "activating-account": "Aktiverer konto...", - "activating-account-text": "Din konto er i øjeblikket ved at blive aktiveret. Vent venligst...", - "accept-privacy-policy": "Accepter fortrolighedspolitik", - "accept": "Accepter", - "privacy-policy": "Fortrolighedspolitik" + "mobile": { + "add-application": "Tilføj applikation", + "app-id": "App-ID", + "app-id-required": "App-ID er påkrævet", + "app-id-pattern": "Ugyldigt format for App-ID", + "app-store-link": "App Store-link", + "app-store-link-required": "App Store-link er påkrævet", + "application-details": "Applikationsdetaljer", + "application-package": "Applikationspakke", + "application-secret": "Applikationshemmelighed", + "application-secret-required": "Applikationshemmelighed er påkrævet", + "application": "Applikation", + "applications": "Applikationer", + "copy-app-id": "Kopiér App-ID", + "copy-app-store-link": "Kopiér App Store-link", + "copy-application-package": "Kopiér applikationspakke", + "copy-application-secret": "Kopiér applikationshemmelighed", + "copy-google-play-link": "Kopiér Google Play-link", + "copy-sha256-certificate-fingerprints": "Kopiér SHA256-certifikatfingeraftryk", + "delete-application": "Slet applikation", + "delete-application-button-text": "Jeg forstår konsekvenserne, slet applikationen", + "delete-application-text": "Denne handling kan ikke fortrydes. Dette vil permanent slette din applikation.
Hvis du ikke vil slette den permanent, kan du suspendere applikationen midlertidigt.
For at slette applikationen alligevel, indtast venligst \"{{phrase}}\" for at bekræfte.", + "delete-application-title-short": "Er du sikker på, at du vil slette applikationen '{{name}}'?", + "delete-application-text-short": "Vær forsigtig, efter bekræftelsen vil applikationen og alle relaterede data ikke kunne gendannes.", + "delete-application-phrase": "slet applikation", + "delete-applications-bundle-text": "Vær forsigtig, efter bekræftelsen vil mobilpakken og alle relaterede data ikke kunne gendannes.", + "delete-applications-bundle-title": "Er du sikker på, at du vil slette mobilpakken '{{bundleName}}'?", + "generate-application-secret": "Generér applikationshemmelighed", + "google-play-link": "Google Play-link", + "google-play-link-required": "Google Play-link er påkrævet", + "latest-version": "Seneste version", + "min-version": "Minimumsversion", + "invalid-version-pattern": "Ugyldigt versionsformat. Brug formatet: major.minor.patch (f.eks. 1.0.0).", + "mobile-center": "Mobilcenter", + "mobile-package": "Applikationspakke", + "mobile-package-max-length": "Applikationspakken skal være mindre end 256 tegn", + "mobile-package-required": "Applikationspakken er påkrævet.", + "mobile-package-pattern": "Ugyldigt format for applikationspakke", + "no-application": "Ingen applikationer fundet", + "no-bundles": "Ingen pakker fundet", + "platform-type": "Platformstype", + "search-application": "Søg applikationer", + "search-bundles": "Søg pakker", + "set": "Angiv", + "sha256-certificate-fingerprints": "SHA256-certifikatfingeraftryk", + "sha256-certificate-fingerprints-required": "SHA256-certifikatfingeraftryk er påkrævet", + "sha256-certificate-fingerprints-pattern": "Ugyldigt format for SHA256-certifikatfingeraftryk", + "show-hidden-pages": "Vis skjulte sider", + "status": "Status", + "status-type": { + "deprecated": "Forældet", + "draft": "Udkast", + "published": "Udgivet", + "suspended": "Suspenderet" + }, + "store-information": "Butiksinformation", + "version-information": "Versionsinformation", + "min-version-release-notes": "Udgivelsesnoter for minimumsversion", + "latest-version-release-notes": "Udgivelsesnoter for seneste version", + "bundle": "Pakke", + "bundles": "Pakker", + "add-bundle": "Tilføj pakke", + "title": "Titel", + "title-required": "Titel er påkrævet", + "title-cannot-contain-only-spaces": "Titlen må ikke kun indeholde mellemrum", + "title-max-length": "Titlen skal være under 256 tegn", + "oauth-clients": "OAuth 2.0-klienter", + "android-app": "Android-app", + "android-application": "Android-applikation", + "ios-app": "iOS-app", + "ios-application": "iOS-applikation", + "invalid-store-link": "Ugyldigt butiklink", + "enable-oauth": "Aktiver OAuth 2.0", + "enable-self-registration": "Aktiver selvregistrering", + "edit-bundle": "Rediger pakke", + "description": "Beskrivelse", + "basic-settings": "Grundindstillinger", + "no-application-matching": "Ingen applikation fundet der matcher '{{entity}}'.", + "no-bundle-matching": "Ingen pakke fundet der matcher '{{entity}}'.", + "application-required": "Applikation er påkrævet.", + "bundle-required": "Pakke er påkrævet.", + "no-application-text": "Ingen applikationer fundet", + "no-bundle-text": "Ingen pakke fundet", + "layout": "Layout", + "pages": "Sider", + "hide-all-pages": "Skjul alle sider", + "reset-to-default-pages": "Nulstil til standardsider", + "add-specific-page": "Tilføj specifik side", + "visible": "Synlig", + "hidden": "Skjult", + "reset-to-page-default": "Nulstil siden til standard", + "mobile-599": "Mobil (maks 599px)", + "tablet-959": "Tablet (maks 959px)", + "max-element-number": "Maksimalt antal elementer", + "page-name": "Sidenavn", + "page-name-required": "Sidenavn er påkrævet.", + "page-name-cannot-contain-only-spaces": "Sidenavn må ikke kun indeholde mellemrum.", + "page-name-max-length": "Sidenavn skal være under 256 tegn", + "page-type": "Sidetype", + "pages-types": { + "dashboard": "Dashboard", + "web-view": "Webvisning", + "custom": "Brugerdefineret" + }, + "url": "URL", + "invalid-url-format": "Ugyldigt URL-format", + "path": "Sti", + "invalid-path-format": "Ugyldigt stiformat", + "custom-page": "Brugerdefineret side", + "edit-page": "Rediger side", + "edit-custom-page": "Rediger brugerdefineret side", + "delete-page": "Slet side", + "qr-code-widget": "QR-kode-widget", + "type-here": "Skriv her", + "configuration-dialog": "Konfigurationsdialog", + "configuration-app": "Konfigurationsapp", + "configuration-step": { + "prepare-environment-title": "Forbered udviklingsmiljø", + "prepare-environment-text": "Flutter ThingsBoard Mobile Application kræver Flutter SDK. Følg vejledningen for at konfigurere Flutter SDK.", + "get-source-code-title": "Hent appens kildekode", + "get-source-code-text": "Du kan hente Flutter ThingsBoard Mobile Application kildekode ved at klone den fra GitHub-repositoriet:", + "configure-api-title": "Konfigurer ThingsBoard API-endepunkt", + "configure-api-text": "Åbn projektet flutter_thingsboard_pe_app i din editor/IDE. Rediger:", + "configure-api-hint": "Angiv værdien af konstanten thingsBoardApiEndpoint, så den matcher API-endepunktet på din ThingsBoard-serverinstans. Brug ikke \"localhost\" eller \"127.0.0.1\" som værter.", + "run-app-title": "Kør appen", + "run-app-text": "Kør appen som beskrevet i din IDE.\nHvis du bruger terminalen, kan du køre appen med følgende kommando:", + "more-information": "Detaljeret information findes i vores dokumentation for Kom godt i gang.", + "getting-started": "Kom godt i gang", + "configure-package-title": "Konfigurer applikationspakke", + "configure-package-text": "Du kan manuelt ændre applikationspakken eller bruge et tredjeparts CLI-værktøj.", + "configure-package-text-install": "For at installere Rename CLI-værktøjet skal du køre følgende kommando:", + "configure-package-run-commands": "Kør disse kommandoer i projektets rodmappe:" + } + }, + "notification": { + "action-button": "Handlingsknap", + "action-type": "Handlingstype", + "active": "Aktiv", + "add-notification-recipients-group": "Tilføj gruppe af modtagere", + "add-notification-template": "Tilføj notifikationsskabelon", + "add-recipient": "Tilføj modtager", + "add-recipients": "Tilføj modtagere", + "add-rule": "Tilføj regel", + "add-stage": "Tilføj trin", + "add-template": "Tilføj skabelon", + "after": "Efter", + "alarm-assignment-trigger-settings": "Indstillinger for alarmtildelingsudløser", + "alarm-comment-trigger-settings": "Indstillinger for alarmkommentarudløser", + "alarm-trigger-settings": "Indstillinger for alarmudløser", + "all": "Alle", + "api-feature-hint": "Hvis feltet er tomt, anvendes udløseren på alle API-funktioner", + "api-usage-trigger-settings": "Indstillinger for API-brugsudløser", + "new-platform-version-trigger-settings": "Indstillinger for udløser af ny platformsversion", + "rate-limits-trigger-settings": "Indstillinger for udløser ved overskredne hastighedsgrænser", + "task-processing-failure-trigger-settings": "Indstillinger for udløser ved fejl i opgavebehandling", + "at-least-one-should-be-selected": "Mindst én skal vælges", + "basic-settings": "Grundindstillinger", + "button-text": "Knaptekst", + "button-text-required": "Knaptekst er påkrævet", + "button-text-max-length": "Knaptekst skal være mindre end eller lig med {{ length }} tegn", + "compose": "Sammensæt", + "conversation": "Samtale", + "conversation-required": "Samtale er påkrævet", + "copy-notification-template": "Kopiér notifikationsskabelon", + "copy-rule": "Kopiér regel", + "copy-template": "Kopiér skabelon", + "create-new": "Opret ny", + "created": "Oprettet", + "customize-messages": "Tilpas beskeder", + "delete-notification-text": "Vær forsigtig, efter bekræftelsen vil notifikationen ikke kunne gendannes.", + "delete-notification-title": "Er du sikker på, at du vil slette notifikationen?", + "delete-notifications-text": "Vær forsigtig, efter bekræftelsen vil notifikationerne ikke kunne gendannes.", + "delete-notifications-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 notifikation} other {# notifikationer} }?", + "delete-recipient-text": "Vær forsigtig, efter bekræftelsen vil modtageren ikke kunne gendannes.", + "delete-recipient-title": "Er du sikker på, at du vil slette modtageren '{{recipientName}}'?", + "delete-recipients-text": "Vær forsigtig, efter bekræftelsen vil modtagerne ikke kunne gendannes.", + "delete-recipients-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 modtager} other {# modtagere} }?", + "delete-request-text": "Vær forsigtig, efter bekræftelsen vil anmodningen ikke kunne gendannes.", + "delete-request-title": "Er du sikker på, at du vil slette anmodningen?", + "delete-requests-text": "Vær forsigtig, efter bekræftelsen vil anmodningerne ikke kunne gendannes.", + "delete-requests-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 anmodning} other {# anmodninger} }?", + "delete-rule-text": "Vær forsigtig, efter bekræftelsen vil reglen ikke kunne gendannes.", + "delete-rule-title": "Er du sikker på, at du vil slette reglen '{{ruleName}}'?", + "delete-rules-text": "Vær forsigtig, efter bekræftelsen vil reglerne ikke kunne gendannes.", + "delete-rules-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 regel} other {# regler} }?", + "delete-template-text": "Vær forsigtig, efter bekræftelsen vil skabelonen ikke kunne gendannes.", + "delete-template-title": "Er du sikker på, at du vil slette skabelonen '{{templateName}}'?", + "delete-templates-text": "Vær forsigtig, efter bekræftelsen vil skabelonerne ikke kunne gendannes.", + "delete-templates-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 skabelon} other {# skabeloner} }?", + "deleted": "Slettet", + "delivery-method": { + "delivery-method": "Leveringsmetode", + "email": "Email", + "email-preview": "Forhåndsvisning af emailnotifikation", + "slack": "Slack", + "slack-preview": "Forhåndsvisning af Slack-notifikation", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Forhåndsvisning af Microsoft Teams-notifikation", + "sms": "SMS", + "sms-preview": "Forhåndsvisning af SMS-notifikation", + "web": "Web", + "web-preview": "Forhåndsvisning af webnotifikation", + "mobile-app": "Mobilapp", + "mobile-app-preview": "Forhåndsvisning af mobilapp-notifikation" + }, + "delivery-method-not-configure-click": "Leveringsmetode er ikke konfigureret. Klik for at konfigurere.", + "delivery-method-not-configure-contact": "Leveringsmetode er ikke konfigureret. Kontakt din systemadministrator.", + "delivery-methods": "Leveringsmetoder", + "description": "Beskrivelse", + "device-activity-trigger-settings": "Indstillinger for udløser af enhedsaktivitet", + "device-list-rule-hint": "Hvis feltet er tomt, anvendes udløseren på alle enheder", + "device-profiles-list-rule-hint": "Hvis feltet er tomt, anvendes udløseren på alle enhedsprofiler", + "disabled": "Deaktiveret", + "edge-trigger-settings": "Indstillinger for Edge-udløser", + "edge-list-rule-hint": "Hvis feltet er tomt, anvendes udløseren på alle Edge-instanser", + "edit-notification-recipients-group": "Rediger gruppe af modtagere", + "edit-notification-template": "Rediger notifikationsskabelon", + "edit-rule": "Rediger regel", + "edit-template": "Rediger skabelon", + "enabled": "Aktiveret", + "entities-limit-trigger-settings": "Indstillinger for udløser ved entitetsgrænse", + "entity-action-trigger-settings": "Indstillinger for udløser af entitetshandling", + "entity-type": "Enhedstype", + "escalation-chain": "Eskaleringskæde", + "failed-send": "Fejl ved afsendelse", + "fails": "{ count, plural, =1 {1 fejl} other {# fejl} }", + "filter": "Filter", + "first-recipient": "Første modtager", + "inactive": "Inaktiv", + "inbox": "Indbakke", + "notification-inbox": "Notifikationer / Indbakke", + "input-field-support-templatization": "Inputfelt understøtter templatization.", + "input-fields-support-templatization": "Inputfelter understøtter templatization.", + "link": "Link", + "link-required": "Link er påkrævet", + "link-type": { + "dashboard": "Åbn dashboard", + "link": "Åbn URL-link" + }, + "loading-notifications": "Indlæser notifikationer...", + "management": "Notifikationsstyring", + "mark-all-as-read": "Markér alle som læst", + "mark-as-read": "Markér som læst", + "message": "Besked", + "message-required": "Besked er påkrævet", + "message-max-length": "Besked skal være mindre end eller lig med {{ length }} tegn", + "name": "Navn", + "name-required": "Navn er påkrævet", + "new-notification": "Ny notifikation", + "no-inbox-notification": "Ingen notifikation fundet", + "no-notification-request": "Ingen notifikationsanmodning", + "no-notification-templates": "Ingen notifikationsskabeloner fundet", + "no-notifications-yet": "Ingen notifikationer endnu", + "no-recipients-notification": "Ingen modtagere notifikation", + "no-recipients-matching": "Ingen modtagere matcher '{{entity}}'.", + "no-recipients-text": "Ingen modtager fundet", + "no-rule": "Ingen regel konfigureret", + "no-rules-notification": "Ingen regler notifikation", + "no-severity-found": "Ingen alvorlighed fundet", + "no-severity-matching": "'{{severity}}' ikke fundet.", + "no-template-matching": "Ingen ressource matcher '{{template}}'.", + "not-found-slack-recipient": "Slack-modtager ikke fundet", + "notification": "Notifikation", + "notification-center": "Notifikationscenter", + "notification-tap-action": "Handling ved tryk på notifikation", + "notification-tap-action-hint": "Hvis ikke aktiveret, anvendes standard dashboard for alarmer", + "notify": "notificer", + "notify-again": "Notificer igen", + "notify-alarm-action": { + "acknowledged": "Alarm bekræftet", + "assigned": "Alarm tildelt", + "cleared": "Alarm ryddet", + "created": "Alarm oprettet", + "severity-changed": "Alvorlighed ændret", + "unassigned": "Alarm ikke tildelt" + }, + "notify-on": "Notificer ved", + "notify-on-comment-update": "Notificer ved kommentaropdatering", + "notify-on-required": "Notificer ved er påkrævet", + "notify-on-unassign": "Notificer ved afkobling", + "notify-only-user-comments": "Notificer kun brugernes kommentarer", + "only-rule-chain-lifecycle-failures": "Kun fejl i livscyklus for regelkæder", + "only-rule-node-lifecycle-failures": "Kun fejl i livscyklus for regelnoder", + "platform-users": "Platformbrugere", + "rate-limits": "Grænser for frekvens", + "rate-limits-hint": "Hvis feltet er tomt, anvendes udløseren på alle grænser for frekvens", + "recipient": "Modtager", + "recipient-group": "Modtagergruppe", + "recipient-type": { + "affected-tenant-administrators": "Berørte tenantadministratorer", + "affected-user": "Berørt bruger", + "all-users": "Alle brugere", + "customer-users": "Kunde-brugere", + "system-administrators": "Systemadministratorer", + "tenant-administrators": "Tenantadministratorer", + "user-filters": "Brugerfilter", + "user-list": "Brugerliste", + "users-entity-owner": "Brugere af entitetens ejer" + }, + "recipients": "Modtagere", + "notification-recipient": "Notifikationsmodtager", + "notification-recipient-required": "Notifikationsmodtager er påkrævet.", + "notification-recipients": "Notifikationer / Modtagere", + "recipients-count": "{ count, plural, =1 {1 modtager} other {# modtagere} }", + "recipients-required": "Modtagere er påkrævet", + "refresh-allow-delivery-method": "Opdater tilladte leveringsmetoder", + "request-search": "Søg i anmodninger", + "request-status": { + "processing": "Behandler", + "scheduled": "Planlagt", + "sent": "Sendt" + }, + "review": "Gennemse", + "rule": "Regel", + "rule-chain-list-rule-hint": "Hvis feltet er tomt, anvendes udløseren på alle regelkæder", + "rule-engine-events-trigger-settings": "Indstillinger for udløser af regelmotorhændelser", + "rule-engine-filter": "Regelmotorfilter", + "rule-name": "Regelnavn", + "rule-name-required": "Navn er påkrævet", + "rule-disable": "Deaktiver notifikationsregel", + "rule-enable": "Aktivér notifikationsregel", + "rule-node-filter": "Filtrér regelnoder", + "rules": "Regler", + "notification-rules": "Notifikationer / Regler", + "scheduler-later": "Planlæg til senere", + "search-notification": "Søg notifikationer", + "search-recipients": "Søg modtagere", + "search-rules": "Søg regler", + "search-templates": "Søg skabeloner", + "see-documentation": "Se dokumentation", + "selected-notifications": "{ count, plural, =1 {1 notifikation} other {# notifikationer} } valgt", + "selected-recipients": "{ count, plural, =1 {1 modtager} other {# modtagere} } valgt", + "selected-requests": "{ count, plural, =1 {1 anmodning} other {# anmodninger} } valgt", + "selected-rules": "{ count, plural, =1 {1 regel} other {# regler} } valgt", + "selected-template": "{ count, plural, =1 {1 skabelon} other {# skabeloner} } valgt", + "send-notification": "Send notifikation", + "sent": "Sendt", + "setup": "Opsætning", + "notification-sent": "Notifikationer / Sendt", + "set-entity-from-notification": "Sæt entitet fra notifikation til dashboard-tilstand", + "slack-chanel-type": "Slack-kanaltype", + "slack-chanel-types": { + "direct": "Direkte besked", + "private-channel": "Privat kanal", + "public-channel": "Offentlig kanal" + }, + "start-from-scratch": "Start fra bunden", + "status": "Status", + "stop-escalation-alarm-status-become": "Stop eskaleringen når alarmstatus bliver:", + "subject": "Emne", + "subject-required": "Emne er påkrævet", + "subject-max-length": "Emnet må højst være {{ length }} tegn", + "template": "Skabelon", + "template-name": "Skabelonnavn", + "template-required": "Skabelon er påkrævet", + "template-type": { + "alarm": "Alarm", + "alarm-assignment": "Alarmtildeling", + "alarm-comment": "Alarmkommentar", + "api-usage-limit": "API-brugsgrænse", + "device-activity": "Enhedsaktivitet", + "entities-limit": "Enhedsgrænse", + "entity-action": "Entitetshandling", + "general": "Generel", + "rule-engine-lifecycle-event": "Livscyklus for regelmotor", + "rule-node": "Regelnode", + "new-platform-version": "Ny platformversion", + "rate-limits": "Overskredet grænse for frekvens", + "edge-communication-failure": "Edge-kommunikationsfejl", + "edge-connection": "Edge-forbindelse", + "task-processing-failure": "Fejl i opgavebehandling" + }, + "templates": "Skabeloner", + "notification-templates": "Notifikationer / Skabeloner", + "tenant-profiles-list-rule-hint": "Hvis feltet er tomt, anvendes udløseren på alle tenant-profiler", + "tenants-list-rule-hint": "Hvis feltet er tomt, anvendes udløseren på alle tenants", + "threshold": "Tærskel", + "theme-color": "Temafarve", + "time": "Tid", + "track-rule-node-events": "Spor regelnodehændelser", + "trigger": { + "alarm": "Alarm", + "alarm-assignment": "Alarmtildeling", + "alarm-comment": "Alarmkommentar", + "api-usage-limit": "API-brugsgrænse", + "device-activity": "Enhedsaktivitet", + "entities-limit": "Entitetsgrænse", + "entity-action": "Entitetshandling", + "rule-engine-lifecycle-event": "Regelmotor livscyklusbegivenhed", + "new-platform-version": "Ny platformversion", + "rate-limits": "Overskredet frekvensgrænse", + "edge-connection": "Edge-forbindelse", + "edge-communication-failure": "Edge-kommunikationsfejl", + "task-processing-failure": "Fejl i opgavebehandling", + "trigger": "Udløser", + "trigger-required": "Udløser er påkrævet" + }, + "type": "Type", + "unread": "Ulæst", + "updated": "Opdateret", + "use-deprecated-webhook-connectors": "Brug forældede Webhook-forbindelser", + "use-old-api": "Brug gammel API", + "use-template": "Brug skabelon", + "view-all": "Se alle", + "warning": "Advarsel", + "webhook-url": "Webhook URL", + "webhook-url-required": "Webhook URL er påkrævet", + "workflow-url": "Workflow URL", + "workflow-url-required": "Workflow URL er påkrævet", + "channel-name": "Kanalnavn", + "channel-name-required": "Kanalnavn er påkrævet", + "settings": { + "notification-settings": "Notifikationsindstillinger", + "reset-all": "Nulstil alle indstillinger", + "reset-all-title": "Er du sikker på, at du vil nulstille formularen?", + "reset-all-text": "Efter bekræftelse vil indstillingsformularen blive nulstillet til standardværdien og gemt.", + "type": "Type", + "enable-all": "Aktivér alle", + "disable-all": "Deaktiver alle", + "delivery-not-configured": "Leveringsmetode er ikke konfigureret" + } + }, + "ota-update": { + "add": "Tilføj pakke", + "assign-firmware": "Tildelt firmware", + "assign-firmware-required": "Tildelt firmware er påkrævet", + "assign-software": "Tildelt software", + "assign-software-required": "Tildelt software er påkrævet", + "auto-generate-checksum": "Generér checksum automatisk", + "checksum": "Checksum", + "checksum-hint": "Hvis checksum er tom, vil den blive genereret automatisk", + "checksum-algorithm": "Checksum-algoritme", + "checksum-copied-message": "Pakke-checksum er blevet kopieret til udklipsholderen", + "change-firmware": "Ændring af firmware kan medføre opdatering af { count, plural, =1 {1 enhed} other {# enheder} }.", + "change-software": "Ændring af software kan medføre opdatering af { count, plural, =1 {1 enhed} other {# enheder} }.", + "chose-compatible-device-profile": "Den uploadede pakke vil kun være tilgængelig for enheder med den valgte profil.", + "chose-firmware-distributed-device": "Vælg firmware, der skal distribueres til enhederne", + "chose-software-distributed-device": "Vælg software, der skal distribueres til enhederne", + "content-type": "Indholdstype", + "copy-checksum": "Kopiér checksum", + "copy-direct-url": "Kopiér direkte URL", + "copyId": "Kopiér pakke-ID", + "copied": "Kopieret!", + "delete": "Slet pakke", + "delete-ota-update-text": "Vær forsigtig, efter bekræftelse vil OTA-opdateringen ikke kunne gendannes.", + "delete-ota-update-title": "Er du sikker på, at du vil slette OTA-opdateringen '{{title}}'?", + "delete-ota-updates-text": "Vær forsigtig, efter bekræftelse vil alle valgte OTA-opdateringer blive fjernet.", + "delete-ota-updates-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 OTA-opdatering} other {# OTA-opdateringer} }?", + "description": "Beskrivelse", + "direct-url": "Direkte URL", + "direct-url-copied-message": "Pakkens direkte URL er blevet kopieret til udklipsholderen", + "direct-url-required": "Direkte URL er påkrævet", + "download": "Download pakke", + "drop-file": "Slip en pakkefil eller klik for at vælge en fil til upload.", + "drop-package-file-or": "Træk og slip en pakkefil eller", + "file-name": "Filnavn", + "file-size": "Filstørrelse", + "file-size-bytes": "Filstørrelse i bytes", + "idCopiedMessage": "Pakke-ID er blevet kopieret til udklipsholderen", + "no-firmware-matching": "Ingen kompatible firmware OTA-opdateringspakker fundet, der matcher '{{entity}}'.", + "no-firmware-text": "Ingen kompatible firmware OTA-opdateringspakker tilgængelige.", + "no-packages-text": "Ingen pakker fundet", + "no-software-matching": "Ingen kompatible software OTA-opdateringspakker fundet, der matcher '{{entity}}'.", + "no-software-text": "Ingen kompatible software OTA-opdateringspakker tilgængelige.", + "ota-update": "OTA-opdatering", + "ota-update-details": "OTA-opdateringsdetaljer", + "ota-updates": "OTA-opdateringer", + "package-file": "Pakkefil", + "package-type": "Pakketype", + "packages-repository": "Pakkelager", + "search": "Søg pakker", + "selected-package": "{ count, plural, =1 {1 pakke} other {# pakker} } valgt", + "title": "Titel", + "title-required": "Titel er påkrævet.", + "title-max-length": "Titel skal være mindre end 256 tegn", + "types": { + "firmware": "Firmware", + "software": "Software" + }, + "upload-binary-file": "Upload binær fil", + "use-external-url": "Brug ekstern URL", + "version": "Version", + "version-required": "Version er påkrævet.", + "version-tag": "Versionsmærke", + "version-tag-hint": "Brugertilpasset mærke skal matche den versionsinformation, som din enhed rapporterer.", + "version-max-length": "Version skal være mindre end 256 tegn", + "warning-after-save-no-edit": "Når pakken er uploadet, vil du ikke kunne redigere titel, version, enhedsprofil og pakketype." }, "position": { "top": "Top", @@ -2494,11 +4182,100 @@ "profile": "Profil", "last-login-time": "Sidste login", "change-password": "Skift adgangskode", - "current-password": "Nuværende adgangskode" + "current-password": "Nuværende adgangskode", + "copy-jwt-token": "Kopiér JWT-token", + "jwt-token": "JWT-token", + "token-valid-till": "Token er gyldig til", + "tokenCopiedSuccessMessage": "JWT-token er kopieret til udklipsholderen", + "tokenCopiedWarnMessage": "JWT-token er udløbet! Opdater venligst siden." + }, + "profiles": { + "profiles": "Profiler" + }, + "security": { + "security": "Sikkerhed", + "general-settings": "Generelle sikkerhedsindstillinger", + "access-token": "Adgangstoken", + "access-token-required": "Adgangstoken er påkrævet", + "clientId": "Klient-ID", + "clientId-required": "Klient-ID er påkrævet", + "username": "Brugernavn", + "username-required": "Brugernavn er påkrævet", + "ca-cert": "CA-certifikat", + "2fa": { + "2fa": "To-faktor autentificering", + "2fa-description": "To-faktor autentificering beskytter din konto mod uautoriseret adgang. Du skal blot indtaste en sikkerhedskode ved login.", + "authenticate-with": "Du kan autentificere med:", + "disable-2fa-provider-text": "Deaktivering af {{name}} vil gøre din konto mindre sikker", + "disable-2fa-provider-title": "Er du sikker på, at du vil deaktivere {{name}}?", + "get-new-code": "Få ny kode", + "main-2fa-method": "Brug som hovedmetode til to-faktor autentificering", + "dialog": { + "activation-step-description-email": "Næste gang du logger ind, vil du blive bedt om at indtaste den sikkerhedskode, der bliver sendt til din e-mailadresse.", + "activation-step-description-sms": "Næste gang du logger ind, vil du blive bedt om at indtaste den sikkerhedskode, der bliver sendt til telefonnummeret.", + "activation-step-description-totp": "Næste gang du logger ind, skal du indtaste en to-faktor autentificeringskode.", + "activation-step-label": "Aktivering", + "backup-code-description": "Udskriv koderne, så du har dem ved hånden, når du skal logge ind. Du kan kun bruge hver sikkerhedskode én gang.", + "backup-code-warn": "Når du forlader denne side, kan disse koder ikke vises igen. Gem dem sikkert.", + "download-txt": "Download (txt)", + "email-step-description": "Indtast en e-mail, der skal bruges som autentifikator.", + "email-step-label": "E-mail", + "enable-email-title": "Aktiver e-mail autentificering", + "enable-sms-title": "Aktiver SMS autentificering", + "enable-totp-title": "Aktiver autentificeringsapp", + "enter-verification-code": "Indtast 6-cifret kode her", + "get-backup-code-title": "Få sikkerhedskode", + "next": "Næste", + "scan-qr-code": "Scan denne QR-kode med din autentificeringsapp", + "send-code": "Send kode", + "sms-step-description": "Indtast et telefonnummer til autentificering.", + "sms-step-label": "Telefonnummer", + "success": "Succes!", + "totp-step-description-install": "Du kan installere apps som Google Authenticator, Authy eller Duo.", + "totp-step-description-open": "Åbn autentificeringsappen på din mobiltelefon.", + "totp-step-label": "Hent app", + "verification-code": "6-cifret kode", + "verification-code-invalid": "Ugyldigt kodeformat", + "verification-code-incorrect": "Forkert verificeringskode", + "verification-code-many-request": "For mange forsøg, tjek verificeringskode", + "verification-step-description": "Indtast en 6-cifret kode, vi netop har sendt til {{address}}", + "verification-step-label": "Verificering" + }, + "provider": { + "email": "E-mail", + "email-description": "Brug en kode sendt til din e-mail til autentificering.", + "email-hint": "Autentificeringskoder sendes via e-mail til {{ info }}", + "sms": "SMS", + "sms-description": "Brug din telefon til autentificering. Vi sender dig en sikkerhedskode via SMS, når du logger ind.", + "sms-hint": "Autentificeringskoder sendes som SMS til {{ info }}", + "totp": "Autentificeringsapp", + "totp-description": "Brug apps som Google Authenticator, Authy eller Duo til autentificering. De genererer en sikkerhedskode til login.", + "totp-hint": "Autentificeringsapp er opsat for din konto", + "backup_code": "Sikkerhedskode", + "backup-code-description": "Disse udskrivbare engangskoder giver dig mulighed for at logge ind, når du ikke har din telefon.", + "backup-code-hint": "{{ info }} engangskoder er aktive på nuværende tidspunkt" + } + }, + "password-requirement": { + "at-least": "Mindst:", + "character": "{ count, plural, =1 {1 tegn} other {# tegn} }", + "digit": "{ count, plural, =1 {1 ciffer} other {# cifre} }", + "incorrect-password-try-again": "Forkert adgangskode. Prøv igen", + "lowercase-letter": "{ count, plural, =1 {1 lille bogstav} other {# små bogstaver} }", + "new-passwords-not-match": "De nye adgangskoder matcher ikke", + "password-should-not-contain-spaces": "Adgangskoden må ikke indeholde mellemrum", + "password-not-meet-requirements": "Adgangskoden opfylder ikke kravene", + "password-requirements": "Adgangskodekrav", + "password-should-difference": "Ny adgangskode skal være forskellig fra den nuværende", + "special-character": "{ count, plural, =1 {1 specialtegn} other {# specialtegn} }", + "uppercase-letter": "{ count, plural, =1 {1 stort bogstav} other {# store bogstaver} }", + "at-most": "Højst:" + } }, "relation": { "relations": "Relationer", "direction": "Retning", + "clear-relation-type": "Ryd relationstype", "search-direction": { "FROM": "Fra", "TO": "Til" @@ -2509,580 +4286,1416 @@ }, "from-relations": "Udgående relationer", "to-relations": "Indgående relationer", - "selected-relations": "", + "selected-relations": "{ count, plural, =1 {1 relation} other {# relationer} } valgt", "type": "Type", - "to-entity-type": "Til entitetstype", - "to-entity-name": "Til entitetsnavn", - "from-entity-type": "Fra entitetstype", - "from-entity-name": "Fra entitetsnavn", - "to-entity": "Til entitet", - "from-entity": "Fra entitet", + "to-entity-type": "Til enhedstype", + "to-entity-name": "Til enhedsnavn", + "from-entity-type": "Fra enhedstype", + "from-entity-name": "Fra enhedsnavn", + "to-entity": "Til enhed", + "from-entity": "Fra enhed", "delete": "Slet relation", "relation-type": "Relationstype", "relation-type-required": "Relationstype er påkrævet.", + "relation-type-max-length": "Relationstype skal være mindre end 256 tegn", "any-relation-type": "Enhver type", "add": "Tilføj relation", "edit": "Rediger relation", - "delete-to-relation-title": "", - "delete-to-relation-text": "", - "delete-to-relations-title": "", - "delete-to-relations-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte relationer blive fjernet, og tilsvarende entiteter vil ikke være relaterede til den aktuelle entitet.", - "delete-from-relation-title": "", - "delete-from-relation-text": "", - "delete-from-relations-title": "", - "delete-from-relations-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte relationer blive fjernet, og den aktuelle entitet vil ikke være relateret til de tilsvarende entiteter.", + "delete-to-relation-title": "Er du sikker på, at du vil slette relationen til enheden '{{entityName}}'?", + "delete-to-relation-text": "Vær forsigtig, efter bekræftelsen vil enheden '{{entityName}}' ikke længere være relateret til den aktuelle enhed.", + "delete-to-relations-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 relation} other {# relationer} }?", + "delete-to-relations-text": "Vær forsigtig, efter bekræftelsen vil alle valgte relationer blive fjernet, og de tilsvarende enheder vil ikke længere være relateret.", + "delete-from-relation-title": "Er du sikker på, at du vil slette relationen fra enheden '{{entityName}}'?", + "delete-from-relation-text": "Vær forsigtig, efter bekræftelsen vil den aktuelle enhed ikke længere være relateret til enheden '{{entityName}}'.", + "delete-from-relations-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 relation} other {# relationer} }?", + "delete-from-relations-text": "Vær forsigtig, efter bekræftelsen vil alle valgte relationer blive fjernet, og den aktuelle enhed vil ikke længere være relateret til de tilsvarende enheder.", "remove-relation-filter": "Fjern relationsfilter", + "remove-filter": "Fjern filter", "add-relation-filter": "Tilføj relationsfilter", "any-relation": "Enhver relation", "relation-filters": "Relationsfiltre", "additional-info": "Yderligere info (JSON)", - "invalid-additional-info": "Kan ikke parse yderligere info-json.", - "no-relations-text": "Ingen relationer fundet" + "invalid-additional-info": "Kan ikke analysere yderligere info JSON.", + "no-relations-text": "Ingen relationer fundet", + "not": "Ikke" + }, + "resource": { + "add": "Tilføj ressource", + "all-types": "Alle", + "copyId": "Kopiér ressource-ID", + "delete": "Slet ressource", + "delete-resource-text": "Vær forsigtig, efter bekræftelsen vil ressourcen ikke kunne gendannes.", + "delete-resource-title": "Er du sikker på, at du vil slette ressourcen '{{resourceTitle}}'?", + "delete-resources-action-title": "Slet { count, plural, =1 {1 ressource} other {# ressourcer} }", + "delete-resources-text": "Bemærk, at de valgte ressourcer, selv hvis de bruges i enhedsprofiler, vil blive slettet.", + "delete-resources-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 ressource} other {# ressourcer} }?", + "download": "Download ressource", + "drop-file": "Slip en ressourcefil eller klik for at vælge en fil, der skal uploades.", + "drop-resource-file-or": "Træk og slip en ressourcefil eller", + "empty": "Ressourcen er tom", + "file-name": "Filnavn", + "idCopiedMessage": "Ressource-ID er kopieret til udklipsholderen", + "no-resource-matching": "Ingen ressource matcher '{{widgetsBundle}}'", + "no-resource-text": "Ingen ressourcer fundet", + "open-widgets-bundle": "Åbn widgets bundle", + "resource": "Ressource", + "resource-file": "Ressourcefil", + "resource-files": "Ressourcefiler", + "resource-library-details": "Ressourcedetaljer", + "resource-type": "Ressourcetype", + "resources-library": "Ressourcebibliotek", + "search": "Søg ressourcer", + "selected-resources": "{ count, plural, =1 {1 ressource} other {# ressourcer} } valgt", + "system": "System", + "title": "Titel", + "title-required": "Titel er påkrævet.", + "title-max-length": "Titel skal være mindre end 256 tegn", + "type": { + "jks": "JKS", + "js-module": "JS modul", + "lwm2m-model": "LWM2M model", + "pkcs-12": "PKCS #12" + }, + "resource-sub-type": "Undertype", + "sub-type": { + "image": "billede", + "scada-symbol": "Scada symbol", + "extension": "Udvidelse", + "module": "Modul" + } + }, + "javascript": { + "add": "Tilføj JavaScript-ressource", + "delete": "Slet JavaScript-ressource", + "delete-javascript-resource-text": "Vær forsigtig, efter bekræftelsen vil JavaScript-ressourcen ikke kunne gendannes.", + "delete-javascript-resource-title": "Er du sikker på, at du vil slette JavaScript-ressourcen '{{resourceTitle}}'?", + "delete-javascript-resources-action-title": "Slet JavaScript { count, plural, =1 {1 ressource} other {# ressourcer} }", + "delete-javascript-resources-text": "Bemærk, at de valgte JavaScript-ressourcer, selv hvis de bruges i JavaScript-funktioner, vil blive slettet.", + "delete-javascript-resources-title": "Er du sikker på, at du vil slette JavaScript { count, plural, =1 {1 ressource} other {# ressourcer} }?", + "delete-javascript-resource-in-use-text": "Hvis du stadig ønsker at slette JavaScript-ressourcen, skal du klikke på knappen Slet alligevel.", + "download": "Download JavaScript-ressource", + "upload-from-file": "Upload JavaScript fra fil", + "resource-file": "JavaScript-ressourcefil", + "drop-file": "Slip en JavaScript-fil eller klik for at vælge en fil, der skal uploades.", + "drop-resource-file-or": "Træk og slip en JavaScript-fil eller", + "javascript-library": "JavaScript-bibliotek", + "javascript-type": "JavaScript-type", + "javascript-resource-details": "JavaScript-resourcedetaljer", + "javascript-resource-is-in-use": "JavaScript-ressourcen bruges af andre enheder", + "javascript-resources-are-in-use": "JavaScript-ressourcer bruges af andre enheder", + "javascript-resource-is-in-use-text": "JavaScript-ressourcen '{{title}}' blev ikke slettet, fordi den bruges af følgende enheder:", + "javascript-resources-are-in-use-text": "Ikke alle JavaScript-ressourcer er blevet slettet, fordi de bruges af andre enheder.
Du kan se refererede enheder ved at klikke på knappen Referencer i den tilsvarende ressource-række.
Hvis du stadig ønsker at slette disse JavaScript-ressourcer, skal du vælge dem i tabellen nedenfor og klikke på knappen Slet valgte.", + "search": "Søg JavaScript-ressourcer", + "selected-javascript-resources": "{ count, plural, =1 {1 JavaScript-ressource} other {# JavaScript-ressourcer} } valgt", + "no-javascript-resource-text": "Ingen JavaScript-ressourcer fundet", + "all-types": "Alle", + "module-script": "Modulscript" + }, + "rpc": { + "error": { + "target-device-is-not-set": "Målenhed er ikke angivet!", + "invalid-target-entity": "RPC-kommandoer understøttes ikke af {{entityType}}-enheden.", + "failed-to-resolve-target-device": "Kunne ikke finde målenhed!", + "request-timeout": "Forespørgsels-timeout", + "rpc-http-error": "Fejl: {{status}} - {{statusText}}" + } }, "rulechain": { "rulechain": "Regelkæde", + "rulechain-events": "Regelkædebegivenheder", "rulechains": "Regelkæder", "root": "Rod", "delete": "Slet regelkæde", "name": "Navn", "name-required": "Navn er påkrævet.", + "name-max-length": "Navnet skal være mindre end 256 tegn", "description": "Beskrivelse", "add": "Tilføj regelkæde", - "set-root": "Lav regelkæde for rod", - "set-root-rulechain-title": "", - "set-root-rulechain-text": "Efter bekræftelsen vil regelkæden være rod og håndtere alle indgående transportmeddelelser.", - "delete-rulechain-title": "", - "delete-rulechain-text": "Vær forsigtig. Efter bekræftelsen vil regelkæden og alle relaterede data være uoprettelige.", - "delete-rulechains-title": "", - "delete-rulechains-action-title": "", - "delete-rulechains-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte regelkæder blive fjernet, og alle relaterede data vil være uoprettelige.", + "set-root": "Gør regelkæde til rod", + "set-root-rulechain-title": "Er du sikker på, at du vil gøre regelkæden '{{ruleChainName}}' til rod?", + "set-root-rulechain-text": "Efter bekræftelsen bliver regelkæden rod og håndterer alle indkommende transportbeskeder.", + "delete-rulechain-title": "Er du sikker på, at du vil slette regelkæden '{{ruleChainName}}'?", + "delete-rulechain-text": "Vær forsigtig, efter bekræftelsen bliver regelkæden og alle relaterede data uoprettelige.", + "delete-rulechains-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 regelkæde} other {# regelkæder} }?", + "delete-rulechains-action-title": "Slet { count, plural, =1 {1 regelkæde} other {# regelkæder} }", + "delete-rulechains-text": "Vær forsigtig, efter bekræftelsen bliver alle valgte regelkæder og alle relaterede data uoprettelige.", "add-rulechain-text": "Tilføj ny regelkæde", "no-rulechains-text": "Ingen regelkæder fundet", - "rulechain-details": "Oplysninger om regelkæde", - "details": "Oplysninger", + "rulechain-details": "Regelkæde detaljer", + "details": "Detaljer", "events": "Begivenheder", "system": "System", - "import": "Importér regelkæde", - "export": "Eksportér regelkæde", - "export-failed-error": "", + "import": "Importer regelkæde", + "export": "Eksporter regelkæde", + "export-failed-error": "Kan ikke eksportere regelkæde: {{error}}", "create-new-rulechain": "Opret ny regelkæde", "rulechain-file": "Regelkædefil", - "invalid-rulechain-file-error": "Regelkæden kunne ikke importeres: Ugyldig datastruktur for regelkæde.", - "copyId": "Kopiér regelkæde-id", - "idCopiedMessage": "Regelkæde-id er blevet kopieret til udklipsholder", + "invalid-rulechain-file-error": "Kan ikke importere regelkæde: Ugyldig datastruktur.", + "copyId": "Kopiér regelkæde-ID", + "idCopiedMessage": "Regelkæde-ID er kopieret til udklipsholderen", "select-rulechain": "Vælg regelkæde", - "no-rulechains-matching": "", + "no-rulechains-matching": "Ingen regelkæder, der matcher '{{entity}}', blev fundet.", "rulechain-required": "Regelkæde er påkrævet", "management": "Regelstyring", - "debug-mode": "Debug-tilstand", - "search": "Søg efter regelkæder", - "selected-rulechains": "", - "open-rulechain": "Åbn regelkæde" + "debug-mode": "Fejlsøgningstilstand", + "search": "Søg regelkæder", + "selected-rulechains": "{ count, plural, =1 {1 regelkæde} other {# regelkæder} } valgt", + "open-rulechain": "Åbn regelkæde", + "edge-template-root": "Skabelonrod", + "assign-to-edge": "Tildel til edge", + "edge-rulechain": "Edge regelkæde", + "unassign-rulechain-from-edge-text": "Efter bekræftelsen vil regelkæden blive fjernet og vil ikke være tilgængelig for edge.", + "unassign-rulechains-from-edge-title": "Er du sikker på, at du vil fjerne tildelingen af { count, plural, =1 {1 regelkæde} other {# regelkæder} }?", + "unassign-rulechains-from-edge-text": "Efter bekræftelsen vil alle valgte regelkæder blive fjernet og vil ikke være tilgængelige for edge.", + "assign-rulechain-to-edge-title": "Tildel regelkæde(r) til edge", + "assign-rulechain-to-edge-text": "Vælg regelkæder, der skal tildeles til edge", + "set-edge-template-root-rulechain": "Gør regelkæde til edge-skabelonrod", + "set-edge-template-root-rulechain-title": "Er du sikker på, at du vil gøre regelkæden '{{ruleChainName}}' til edge-skabelonrod?", + "set-edge-template-root-rulechain-text": "Efter bekræftelsen vil regelkæden blive skabelonrod og være root regelkæde for nyoprettede edges.", + "invalid-rulechain-type-error": "Kan ikke importere regelkæde: Ugyldig type. Forventet type er {{expectedRuleChainType}}.", + "set-auto-assign-to-edge": "Tildel regelkæde automatisk til edge ved oprettelse", + "set-auto-assign-to-edge-title": "Er du sikker på, at du vil tildele regelkæden '{{ruleChainName}}' automatisk til edge ved oprettelse?", + "set-auto-assign-to-edge-text": "Efter bekræftelsen vil regelkæden automatisk blive tildelt til edge ved oprettelse.", + "unset-auto-assign-to-edge": "Tildel ikke regelkæde til edge ved oprettelse", + "unset-auto-assign-to-edge-title": "Er du sikker på, at du ikke vil tildele regelkæden '{{ruleChainName}}' automatisk til edge ved oprettelse?", + "unset-auto-assign-to-edge-text": "Efter bekræftelsen vil regelkæden ikke længere blive tildelt automatisk til edge ved oprettelse.", + "unassign-rulechain-title": "Er du sikker på, at du vil fjerne tildelingen af regelkæden '{{ruleChainName}}'?", + "unassign-rulechains": "Fjern tildeling af regelkæder" }, "rulenode": { - "details": "Oplysninger", + "rule-node-events": "Regel node begivenheder", + "details": "Detaljer", "events": "Begivenheder", - "search": "Søg efter knuder", - "open-node-library": "Åbn knudebibliotek", - "add": "Tilføj regelknude", + "search": "Søg noder", + "open-node-library": "Åbn nodebibliotek", + "close-node-library": "Luk nodebibliotek", + "add": "Tilføj regel node", "name": "Navn", "name-required": "Navn er påkrævet.", + "name-max-length": "Navnet skal være mindre end 256 tegn", "type": "Type", - "delete": "Slet regelknude", - "select-all-objects": "Vælg alle knuder og tilslutninger", - "deselect-all-objects": "Fravælg alle knuder og tilslutninger", - "delete-selected-objects": "Slet valgte knuder og tilslutninger", + "rule-node-description": "Beskrivelse af regel node", + "delete": "Slet regel node", + "select-all-objects": "Vælg alle noder og forbindelser", + "deselect-all-objects": "Fravælg alle noder og forbindelser", + "delete-selected-objects": "Slet valgte noder og forbindelser", "delete-selected": "Slet valgte", + "create-nested-rulechain": "Opret indlejret regelkæde", "select-all": "Vælg alle", "copy-selected": "Kopiér valgte", "deselect-all": "Fravælg alle", - "rulenode-details": "Oplysninger om regelknude", - "debug-mode": "Debug-tilstand", + "rulenode-details": "Regel node detaljer", + "debug-mode": "Fejlsøgningstilstand", + "singleton": "Singleton", "configuration": "Konfiguration", - "link": "Link", - "link-details": "Oplysninger om regelknudelink", - "add-link": "Tilføj link", - "link-label": "Link-etiket", - "link-label-required": "Link-etiket er påkrævet.", - "custom-link-label": "Brugerdefineret link-etiket", - "custom-link-label-required": "Brugerdefineret link-etiket er påkrævet.", - "link-labels": "Link-etiketter", - "link-labels-required": "Link-etiketter er påkrævet.", - "no-link-labels-found": "Ingen link-etiketter fundet", - "no-link-label-matching": "", + "link": "Forbindelse", + "link-details": "Detaljer for regel node forbindelse", + "add-link": "Tilføj forbindelse", + "link-label": "Forbindelsesetiket", + "link-label-required": "Forbindelsesetiket er påkrævet.", + "custom-link-label": "Brugerdefineret etiket", + "custom-link-label-required": "Brugerdefineret etiket er påkrævet.", + "link-labels": "Forbindelsesetiketter", + "link-labels-required": "Forbindelsesetiketter er påkrævede.", + "no-link-labels-found": "Ingen forbindelsesetiketter fundet", + "no-link-label-matching": "'{{label}}' blev ikke fundet.", "create-new-link-label": "Opret en ny!", "type-filter": "Filter", - "type-filter-details": "Filtrer indgående meddelelser med konfigurerede betingelser", + "type-filter-details": "Filtrer indkommende beskeder med konfigurerede betingelser", "type-enrichment": "Berigelse", - "type-enrichment-details": "Tilføj yderligere oplysninger i meddelelsesmetadata", - "type-transformation": "Omdannelse", - "type-transformation-details": "Skift meddelelsesdata og metadata", + "type-enrichment-details": "Tilføj yderligere information til metadata", + "type-transformation": "Transformation", + "type-transformation-details": "Ændr beskedindhold og metadata", "type-action": "Handling", - "type-action-details": "Gennemfør ekstrahandling", - "type-analytics": "Analytik", - "type-analytics-details": "Udfør analyse af streamede eller vedvarende data", + "type-action-details": "Udfør specifik handling", "type-external": "Ekstern", "type-external-details": "Interagerer med eksternt system", "type-rule-chain": "Regelkæde", - "type-rule-chain-details": "Videresender indgående meddelelser til specificeret regelkæde", + "type-rule-chain-details": "Videresender beskeder til specificeret regelkæde", + "type-flow": "Flow", + "type-flow-details": "Organiserer beskedflow", "type-input": "Input", - "type-input-details": "Logisk input af regelkæde, videresender indgående meddelelser til næste relaterede regelknude", + "type-input-details": "Logisk input til regelkæde, videresender beskeder til næste node", "type-unknown": "Ukendt", - "type-unknown-details": "Uløst regelknude", - "directive-is-not-loaded": "", - "ui-resources-load-error": "Kunne ikke indlæse konfigurations-UI-ressourcer.", - "invalid-target-rulechain": "Kan ikke løse målregelkæde!", - "test-script-function": "Testscriptfunktion", - "message": "Meddelelse", - "message-type": "Meddelelsestype", - "select-message-type": "Vælg meddelelsestype", - "message-type-required": "Meddelelsestype er påkrævet", + "type-unknown-details": "Uafklaret regel node", + "directive-is-not-loaded": "Konfigurationsdirektiv '{{directiveName}}' er ikke tilgængeligt.", + "ui-resources-load-error": "Kunne ikke indlæse brugerflade konfigurationsressourcer.", + "invalid-target-rulechain": "Kunne ikke bestemme målregel kæde!", + "test-script-function": "Test script funktion", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", + "message": "Besked", + "message-type": "Beskedtype", + "select-message-type": "Vælg beskedtype", + "message-type-required": "Beskedtype er påkrævet", "metadata": "Metadata", - "metadata-required": "Metadataposter må ikke være tomme.", + "metadata-required": "Metadata poster må ikke være tomme.", "output": "Output", "test": "Test", - "help": "Hjælp" - }, - "role": { - "role": "Rolle", - "roles": "Roller", - "management": "Rollestyring", - "view-roles": "Vis roller", - "no-roles-matching": "", - "role-list": "Rolleliste", - "add": "Tilføj rolle", - "view": "Vis rolle", - "search": "Søg efter roller", - "selected-roles": "", - "no-roles-text": "Ingen roller fundet", - "role-details": "Rolleoplysninger", - "add-role-text": "Tilføj ny rolle", - "delete": "Slet rolle", - "delete-roles": "Slet roller", - "delete-role-title": "", - "delete-role-text": "Vær forsigtig. Efter bekræftelsen vil rollen og alle relaterede data være uoprettelige.", - "delete-roles-title": "", - "delete-roles-action-title": "", - "delete-roles-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte roller blive fjernet, og alle relaterede data vil være uoprettelige.", - "role-type": "Rolletype", - "role-type-required": "Rolletype er påkrævet.", - "select-role-type": "Vælg rolletype", - "enter-role-type": "Indtast rolletype", - "any-role": "Enhver rolle", - "no-role-types-matching": "", - "role-type-list-empty": "Ingen rolletyper valgt.", - "role-types": "Rolletyper", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "name-required": "Navn er påkrævet.", - "description": "Beskrivelse", - "events": "Begivenheder", - "details": "Oplysninger", - "copyId": "Kopiér rolle-id", - "idCopiedMessage": "Rolle-id er blevet kopieret til udklipsholder", - "permissions": "Tilladelser", - "role-required": "Rolle påkrævet", - "roles-required": "Roller påkrævet", - "display-type": { - "GENERIC": "Generisk", - "GROUP": "Gruppe" - } - }, - "group-permission": { - "user-group-roles": "Brugergrupperoller", - "entity-group-permissions": "Tilladelser for entitetsgruppe", - "role-type": "Rolletype", - "role-name": "Rollenavn", - "group-type": "Gruppetype", - "group-name": "Gruppenavn", - "group-owner": "Gruppeejer", - "user-group-name": "Brugergruppenavn", - "user-group-owner": "Brugergruppeejer", - "edit": "Rediger tilladelser", - "delete": "Slet tilladelser", - "selected-group-permissions": "", - "delete-group-permission-title": "", - "delete-group-permission-text": "Vær forsigtig. Efter bekræftelsen vil gruppetilladelsen og alle relaterede data være uoprettelige.", - "delete-group-permission": "Slet gruppetilladelse", - "delete-group-permissions-title": "", - "delete-group-permissions-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte gruppetilladelser blive fjernet, og tilsvarende brugere vil miste adgang til specificerede ressourcer.", - "delete-group-permissions": "Slet gruppetilladelser", - "add-group-permission": "Tilføj gruppetilladelse", - "edit-group-permission": "Rediger gruppetilladelse", - "entity-group": "Entitetsgruppe", - "user-group": "Brugergruppe", - "no-owners-matching": "", - "target-owner-required": "Entitetsgruppeejer er påkrævet.", - "target-user-group-owner-required": "Brugergruppeejer er påkrævet.", - "no-group-permissions-text": "Ingen gruppetilladelser fundet" - }, - "permission": { - "permissions-required": "Mindst én tilladelsespost skal angives.", - "remove-permission": "Fjern tilladelsespost", - "add-permission": "Tilføj tilladelsespost", - "other": "Andet", - "resource": { - "resource": "Ressource", - "select-resource": "Vælg ressource", - "resource-required": "Ressource er påkrævet", - "no-resources-matching": "", - "display-type": { - "ALL": "Alle", - "PROFILE": "Profil", - "ADMIN_SETTINGS": "Admin-indstillinger", - "ALARM": "Alarm", - "DEVICE": "Enhed", - "DEVICE_PROFILE": "Enhedsprofil", - "ASSET": "Aktiv", - "CUSTOMER": "Kunde", - "DASHBOARD": "Dashboard", - "ENTITY_VIEW": "Entitetsvisning", - "TENANT": "Lejer", - "TENANT_PROFILE": "Lejerprofil", - "RULE_CHAIN": "Regelkæde", - "USER": "Bruger", - "WIDGETS_BUNDLE": "Widgets-bundt", - "WIDGET_TYPE": "Widget-type", - "CONVERTER": "Omformer", - "INTEGRATION": "Integration", - "SCHEDULER_EVENT": "Planlægningsbegivenheder", - "BLOB_ENTITY": "Blob-entitet", - "CUSTOMER_GROUP": "Kundegruppe", - "DEVICE_GROUP": "Enhedsgruppe", - "ASSET_GROUP": "Aktivgruppe", - "USER_GROUP": "Brugergruppe", - "ENTITY_VIEW_GROUP": "Entitetsvisningsgruppe", - "DASHBOARD_GROUP": "Dashboardgruppe", - "ROLE": "Rolle", - "GROUP_PERMISSION": "Gruppetilladelse", - "WHITE_LABELING": "Hvid mærkning", - "AUDIT_LOG": "Auditlog", - "API_USAGE_STATE": "API-brugstilstand" - } - }, - "operation": { - "operation": "Driftsopgave", - "operations": "Driftsopgaver", - "operations-required": "Der skal angives mindst én driftsopgave.", - "enter-operation": "Indtast driftsopgave.", - "no-operations-matching": "", - "display-type": { - "ALL": "Alle", - "CREATE": "Opret", - "READ": "Læs", - "WRITE": "Skriv", - "DELETE": "Slet", - "ASSIGN_TO_CUSTOMER": "Tildel til kunde", - "UNASSIGN_FROM_CUSTOMER": "Fjern tildeling fra kunde", - "RPC_CALL": "RPC-opkald", - "READ_CREDENTIALS": "Læs brugeroplysninger", - "WRITE_CREDENTIALS": "Skriv brugeroplysninger", - "READ_ATTRIBUTES": "Læs attributter", - "WRITE_ATTRIBUTES": "Skriv attributter", - "READ_TELEMETRY": "Læs telemetri", - "WRITE_TELEMETRY": "Skriv telemetri", - "CLAIM_DEVICES": "Gør krav på enheder", - "IMPERSONATE": "Efterlign", - "CHANGE_OWNER": "Skift ejer", - "ADD_TO_GROUP": "Tilføj til gruppe", - "REMOVE_FROM_GROUP": "Fjern fra gruppe", - "SHARE_GROUP": "Del gruppe", - "ASSIGN_TO_TENANT": "Tildel til lejer" - } - } + "help": "Hjælp", + "reset-debug-settings": "Nulstil fejlsøgningsindstillinger i alle noder", + "test-with-this-message": "{{test}} med denne besked", + "queue-hint": "Vælg en kø til videresendelse. Standardkøen 'Main' anvendes som udgangspunkt.", + "queue-singleton-hint": "Vælg en kø til videresendelse i miljøer med flere instanser. Standardkøen 'Main' anvendes som udgangspunkt." }, - "scheduler": { - "scheduler": "Planlægger", - "scheduler-event": "Planlægningsbegivenhed", - "select-scheduler-event": "Vælg planlægningsbegivenhed", - "no-scheduler-events-matching": "", - "scheduler-event-required": "Planlægningsbegivenhed er påkrævet", - "management": "Tidsplansstyring", - "scheduler-events": "Planlægningsbegivenheder", - "add-scheduler-event": "Tilfjøj planlægningsbegivenhed", - "search-scheduler-events": "Søg efter planlægningsbegivenheder", - "created-time": "Oprettelsestidspunkt", - "name": "Navn", - "type": "Type", - "created_customer": "Oprettet af kunde", - "edit-scheduler-event": "Rediger planlægningsbegivenhed", - "view-scheduler-event": "Vis planlægningsbegivenhed", - "delete-scheduler-event": "Slet planlægningsbegivenhed", - "no-scheduler-events": "Ingen planlægningsbegivenheder fundet", - "selected-scheduler-events": "", - "delete-scheduler-event-title": "", - "delete-scheduler-event-text": "Vær forsigtig. Efter bekræftelsen vil planlægningsbegivenheden og alle relaterede data være uoprettelige.", - "delete-scheduler-events-title": "", - "delete-scheduler-events-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte planlægningsbegivenheder blive fjernet, og alle relaterede data vil være uoprettelige.", - "create": "Opret planlægningsbegivenhed", - "edit": "Rediger planlægningsbegivenhed", - "view": "Vis planlægningsbegivenhed", - "name-required": "Navn er påkrævet.", - "configuration": "Konfiguration", - "schedule": "Tidsplan", - "start-time": "Starttid", - "repeat": "Gentag", - "repeats": "Gentagelser", - "daily": "Daglig", - "weekly": "Ugentlig", - "monthly": "Månedlig", - "yearly": "Årlig", - "timer": "Timerbaseret", - "repeats-required": "Gentagelser er påkrævet.", - "repeat-on": "Gentag den", - "repeat-every": "Gentag hver", - "ends-on": "Slutter den", - "sunday-label": "L", - "monday-label": "M", - "tuesday-label": "T", - "wednesday-label": "O", - "thursday-label": "T", - "friday-label": "F", - "saturday-label": "L", - "repeat-on-sunday": "Gentag på søndag", - "repeat-on-monday": "Gentag på mandag", - "repeat-on-tuesday": "Gentag på tirsdag", - "repeat-on-wednesday": "Gentag på onsdag", - "repeat-on-thursday": "Gentag på torsdag", - "repeat-on-friday": "Gentag på fredag", - "repeat-on-saturday": "Gentag på lørdag", - "event-type": "Begivenhedstype", - "select-event-type": "Vælg begivenhedstype", - "event-type-required": "Begivenhedstype er påkrævet.", - "list-mode": "Listevisning", - "calendar-mode": "Kalendervisning", - "calendar-view-type": "Kalendervisningstype", - "month": "Måned", - "week": "Uge", - "day": "Dag", - "agenda-week": "Program for uge", - "agenda-day": "Program for dag", - "list-year": "Liste for år", - "list-month": "Liste for måned", - "list-week": "Liste for uge", - "list-day": "Liste for dag", - "today": "I dag", - "navigate-before": "Naviger før", - "navigate-next": "Naviger næste", - "starting-from": "Starter fra", - "until": "til og med", - "on": "den", - "sunday": "Søndag", - "monday": "Mandag", - "tuesday": "Tirsdag", - "wednesday": "Onsdag", - "thursday": "Torsdag", - "friday": "Fredag", - "saturday": "Lørdag", - "originator": "Ophavsmand", - "single-entity": "Enkelt entitet", - "group-of-entities": "Gruppe af entiteter", - "entities-group-owner": "Ejer af gruppe af entiteter", - "single-device": "Enkelt enhed", - "group-of-devices": "Gruppe af enheder", - "devices-group-owner": "Ejer af gruppe af enheder", - "message-body": "Meddelelsestekst", - "target": "Mål", - "rpc-method": "Metode", - "rpc-method-required": "Metode er påkrævet", - "rpc-params": "Parametre", - "select-dashboard-state": "Vælg dashboardtilstand", - "hours": "Timer", - "minutes": "Minutter", - "seconds": "Sekunder", - "time-interval-required": "Tidsinterval er påkrævet", - "time-unit-required": "Tidsenhed er påkrævet", - "every-hour": "", - "every-minute": "", - "every-second": "" - }, - "report": { - "report-config": "Rapportkonfiguration", - "email-config": "E-mailkonfiguration", - "dashboard-state-param": "Parameterværdi for dashboardtilstand", - "base-url": "Basis-URL", - "base-url-required": "Basis-URL er påkrævet.", - "use-dashboard-timewindow": "Brug dashboardtidsvindue", - "timewindow": "Tidsvindue", - "name-pattern": "Rapportnavnsmønster", - "name-pattern-required": "Rapportnavnsmønster er påkrævet", - "type": "Rapporttype", - "use-current-user-credentials": "Brug aktuelle brugeroplysninger", - "customer-user-credentials": "Kundens brugeroplysninger", - "customer-user-credentials-required": "Kundens brugeroplysninger er påkrævet", - "generate-test-report": "Generer testrapport", - "send-email": "Send e-mail", - "from": "Fra", - "from-required": "Fra er påkrævet.", - "to": "Til", - "to-required": "Til er påkrævet.", - "cc": "Cc", - "bcc": "Bcc", - "subject": "Emne", - "subject-required": "Emne er påkrævet.", - "body": "Tekst", - "body-required": "Tekst er påkrævet." - }, - "blob-entity": { - "blob-entity": "Blob-entitet", - "select-blob-entity": "Vælg blob-entitet", - "no-blob-entities-matching": "", - "blob-entity-required": "Blob-entitet er påkrævet", - "files": "Filer", - "search": "Søg efter filer", - "clear-search": "Ryd søgning", - "no-blob-entities-prompt": "Ingen filer fundet", - "report": "Rapport", - "created-time": "Oprettelsestidspunkt", + "rule-node-config": { + "id": "Id", + "additional-info": "Yderligere info", + "advanced-settings": "Avancerede indstillinger", + "create-entity-if-not-exists": "Opret ny enhed hvis den ikke findes", + "create-entity-if-not-exists-hint": "Hvis aktiveret, oprettes en ny enhed med de angivne parametre, medmindre den allerede eksisterer. Eksisterende enheder bruges som de er til relation.", + "select-device-connectivity-event": "Vælg enhedens forbindelseshændelse", + "entity-name-pattern": "Navnmønster", + "device-name-pattern": "Enhedsnavn", + "asset-name-pattern": "Aktivnavn", + "entity-view-name-pattern": "Enhedsvisningsnavn", + "customer-title-pattern": "Kundens titel", + "dashboard-name-pattern": "Dashboard titel", + "user-name-pattern": "Brugerens e-mail", + "edge-name-pattern": "Edge-navn", + "entity-name-pattern-required": "Navnmønster er påkrævet", + "entity-name-pattern-hint": "Navnmønsterfeltet understøtter templatisering. Brug $[messageKey] for at hente værdi fra besked og ${metadataKey} for at hente værdi fra metadata.", + "copy-message-type": "Kopiér beskedtype", + "entity-type-pattern": "Typemønster", + "entity-type-pattern-required": "Typemønster er påkrævet", + "message-type-value": "Beskedtypeværdi", + "message-type-value-required": "Beskedtypeværdi er påkrævet", + "message-type-value-max-length": "Beskedtypeværdi må være under 256 tegn", + "output-message-type": "Output beskedtype", + "entity-cache-expiration": "Cache udløbstid for enheder (sek)", + "entity-cache-expiration-hint": "Angiver maksimumsinterval for at gemme fundne enhedsoptegnelser. 0 betyder, at de aldrig udløber.", + "entity-cache-expiration-required": "Cache udløbstid for enheder er påkrævet.", + "entity-cache-expiration-range": "Udløbstiden skal være ≥ 0.", + "customer-name-pattern": "Kundens titel", + "customer-name-pattern-required": "Kundens titel er påkrævet", + "customer-name-pattern-hint": "Brug $[messageKey] eller ${metadataKey} for at hente værdier.", + "create-customer-if-not-exists": "Opret ny kunde hvis den ikke findes", + "unassign-from-customer": "Fjern tildeling fra kunde hvis afsender er dashboard", + "unassign-from-customer-tooltip": "Kun dashboards kan tildeles flere kunder. Angiv kundens titel for at fjerne tildelingen.", + "customer-cache-expiration": "Cache udløbstid for kunder (sek)", + "customer-cache-expiration-hint": "Angiver maksimumsinterval for gemte kundedata. 0 = aldrig udløber.", + "customer-cache-expiration-required": "Cache udløbstid for kunder er påkrævet.", + "customer-cache-expiration-range": "Udløbstiden skal være ≥ 0.", + "interval-start": "Interval start", + "interval-end": "Interval slut", + "time-unit": "Tidsenhed", + "fetch-mode": "Hentetilstand", + "order-by-timestamp": "Sorter efter tidsstempel", + "limit": "Grænse", + "limit-hint": "Min: 2, maks: 1000. Vælg 'First' eller 'Last' for én post.", + "limit-required": "Grænse er påkrævet.", + "limit-range": "Grænsen skal være mellem 2 og 1000.", + "time-unit-milliseconds": "Millisekunder", + "time-unit-seconds": "Sekunder", + "time-unit-minutes": "Minutter", + "time-unit-hours": "Timer", + "time-unit-days": "Dage", + "time-value-range": "Værdien skal være mellem 1 og 2147483647.", + "start-interval-value-required": "Interval start er påkrævet.", + "end-interval-value-required": "Interval slut er påkrævet.", + "filter": "Filter", + "switch": "Skift", + "math-templatization-tooltip": "Understøtter templatisering via $[messageKey] og ${metadataKey}.", + "add-message-type": "Tilføj beskedtype", + "select-message-types-required": "Mindst én beskedtype skal vælges.", + "select-message-types": "Vælg beskedtyper", + "no-message-types-found": "Ingen beskedtyper fundet", + "no-message-type-matching": "'{{messageType}}' ikke fundet.", + "create-new-message-type": "Opret en ny.", + "message-types-required": "Beskedtyper er påkrævede.", + "client-attributes": "Klientattributter", + "shared-attributes": "Delte attributter", + "server-attributes": "Serverattributter", + "attributes-keys": "Attributnøgler", + "attributes-keys-required": "Attributnøgler er påkrævede", + "attributes-scope": "Attributområde", + "attributes-scope-value": "Attributområdeværdi", + "attributes-scope-value-copy": "Kopiér attributområdeværdi", + "attributes-scope-hint": "Brug metadata 'scope' for at sætte attributområde dynamisk.", + "notify-device": "Tving besked til enheden", + "send-attributes-updated-notification": "Send opdateringsbesked for attributter", + "send-attributes-updated-notification-hint": "Sender besked om opdaterede attributter til Rule Engine køen.", + "send-attributes-deleted-notification": "Send sletningsbesked for attributter", + "send-attributes-deleted-notification-hint": "Sender besked om slettede attributter til Rule Engine køen.", + "update-attributes-only-on-value-change": "Opdatér kun attributter ved værdiændring", + "update-attributes-only-on-value-change-hint": "Opdaterer altid, hvilket øger API-brug og reducerer ydeevne.", + "update-attributes-only-on-value-change-hint-enabled": "Opdaterer kun ved ændret værdi. Ellers ingen tidsstempel- eller notifikationsopdatering.", + "fetch-credentials-to-metadata": "Hent legitimationsoplysninger til metadata", + "notify-device-on-update-hint": "Styrer tvungen notifikation eller baseret på metadata-parametre.", + "notify-device-on-delete-hint": "Styrer tvungen notifikation eller baseret på metadata-parametre.", + "latest-timeseries": "Seneste tidsseriedatanøgler", + "timeseries-keys": "Tidsserienøgler", + "timeseries-keys-required": "Mindst én tidsserienøgle skal vælges.", + "add-timeseries-key": "Tilføj tidsserienøgle", + "add-message-field": "Tilføj beskedfelt", + "relation-search-parameters": "Relationssøgningsparametre", + "relation-parameters": "Relationsparametre", + "add-metadata-field": "Tilføj metadatafelt", + "data-keys": "Beskedfeltnavne", + "copy-from": "Kopiér fra", + "data-to-metadata": "Data til metadata", + "metadata-to-data": "Metadata til data", + "use-regular-expression-hint": "Brug regulært udtryk for at kopiere nøgler efter mønster.\n\nTips:\nTryk 'Enter' for at bekræfte feltinput.\nTryk 'Backspace' for at slette felt. Flere felter understøttes.", + "interval": "Interval", + "interval-required": "Interval er påkrævet", + "interval-hint": "Dedupliceringsinterval i sekunder.", + "interval-min-error": "Mindste tilladte værdi er 1", + "max-pending-msgs": "Maks. ventende beskeder", + "max-pending-msgs-hint": "Maksimalt antal beskeder gemt i hukommelsen for hver unik deduplikerings-id.", + "max-pending-msgs-required": "Maks. ventende beskeder er påkrævet", + "max-pending-msgs-max-error": "Maks. værdi er 1000", + "max-pending-msgs-min-error": "Mindste værdi er 1", + "max-retries": "Maks. forsøg", + "max-retries-required": "Maks. forsøg er påkrævet", + "max-retries-hint": "Maksimalt antal forsøg på at skubbe deduplikerede beskeder til køen. Der bruges 10 sekunders forsinkelse mellem forsøgene", + "max-retries-max-error": "Maks. værdi er 100", + "max-retries-min-error": "Mindste værdi er 0", + "strategy": "Strategi", + "strategy-required": "Strategi er påkrævet", + "strategy-all-hint": "Returnér alle beskeder i perioden som én JSON-array. Hvert element indeholder 'msg' og 'metadata'.", + "strategy-first-hint": "Returnér første besked i perioden.", + "strategy-last-hint": "Returnér sidste besked i perioden.", + "first": "Første", + "last": "Sidste", + "all": "Alle", + "output-msg-type-hint": "Beskedtype for deduplikeringsresultatet.", + "queue-name-hint": "Navn på køen hvor resultatet publiceres.", + "keys": "Nøgler", + "keys-required": "Nøgler er påkrævede", + "rename-keys-in": "Omdøb nøgler i", + "data": "Data", + "message": "Besked", + "metadata": "Metadata", + "current-key-name": "Aktuel nøgle", + "key-name-required": "Nøglenavn er påkrævet", + "new-key-name": "Nyt nøglenavn", + "new-key-name-required": "Nyt nøglenavn er påkrævet", + "metadata-keys": "Metadatafeltnavne", + "json-path-expression": "JSONPath-udtryk", + "json-path-expression-required": "JSONPath-udtryk er påkrævet", + "json-path-expression-hint": "JSONPath specificerer sti til element(er) i en JSON-struktur. '$' er rodobjektet.", + "relations-query": "Relationsforespørgsel", + "device-relations-query": "Enhedsrelationsforespørgsel", + "max-relation-level": "Maks. relationsniveau", + "max-relation-level-error": "Værdien skal være > 0 eller tom.", + "max-relation-level-invalid": "Værdien skal være et heltal.", + "relation-type": "Relationstype", + "relation-type-pattern": "Relationstypemønster", + "relation-type-pattern-required": "Relationstypemønster er påkrævet", + "relation-types-list": "Relationstyper til udbredelse", + "relation-types-list-hint": "Hvis relationstyper ikke vælges, udbredes alarmer uden filtrering.", + "unlimited-level": "Ubegrænset niveau", + "latest-telemetry": "Seneste telemetri", + "add-telemetry-key": "Tilføj telemetrinøgle", + "delete-from": "Slet fra", + "use-regular-expression-delete-hint": "Brug regulært udtryk til sletning efter mønster.\n\nTips:\nTryk 'Enter' for at bekræfte.\nTryk 'Backspace' for at slette.", + "fetch-into": "Hent til", + "attr-mapping": "Attributmapping:", + "source-attribute": "Kildeattribut", + "source-attribute-required": "Kildeattribut er påkrævet.", + "source-telemetry": "Kildetelemetri", + "source-telemetry-required": "Kildetelemetri er påkrævet.", + "target-key": "Målnøgle", + "target-key-required": "Målnøgle er påkrævet.", + "attr-mapping-required": "Mindst én mapping skal angives.", + "fields-mapping": "Feltermapping", + "fields-mapping-hint": "Brug $entityId for at gemme afsenderens id.", + "relations-query-config-direction-suffix": "afsender", + "profile-name": "Profilnavn", + "fetch-circle-parameter-info-from-metadata-hint": "Metadatafelt '{{perimeterKeyName}}' skal være i formatet: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "Metadatafelt '{{perimeterKeyName}}' skal være i formatet: [[48.19736,24.65235],...]", + "short-templatization-tooltip": "Brug $[messageKey] og ${metadataKey} til at hente værdier.", + "fields-mapping-required": "Mindst én feltmapping er påkrævet.", + "at-least-one-field-required": "Mindst ét inputfelt skal have en værdi.", + "originator-fields-sv-map-hint": "Målnøgler understøtter templatisering via $[messageKey] og ${metadataKey}.", + "sv-map-hint": "Kun målnøgler understøtter templatisering via $[messageKey] og ${metadataKey}.", + "source-field": "Kildefelt", + "source-field-required": "Kildefelt er påkrævet.", + "originator-source": "Afsenderkilde", + "new-originator": "Ny afsender", + "originator-customer": "Kunde", + "originator-tenant": "Lejer", + "originator-related": "Relateret enhed", + "originator-alarm-originator": "Alarm-afsender", + "originator-entity": "Enhed efter navnemønster", + "clone-message": "Klon besked", + "transform": "Transformér", + "default-ttl": "Standard TTL", + "default-ttl-required": "Standard TTL er påkrævet.", + "default-ttl-hint": "Hvis metadata ikke indeholder TTL, anvendes standard TTL. Hvis 0, bruges TTL fra lejerprofil.", + "default-ttl-zero-hint": "TTL anvendes ikke hvis værdien er 0.", + "min-default-ttl-message": "Kun værdi 0 er tilladt som minimum TTL.", + "generation-parameters": "Genereringsparametre", + "message-count": "Maks. antal genererede beskeder (0 - ubegrænset)", + "message-count-required": "Begrænsning for genererede beskeder er påkrævet.", + "min-message-count-message": "Kun værdi 0 er tilladt som minimum.", + "period-seconds": "Periode i sekunder", + "period-seconds-required": "Periode er påkrævet.", + "generation-frequency-seconds": "Genereringsfrekvens i sekunder", + "generation-frequency-required": "Genereringsfrekvens er påkrævet.", + "min-generation-frequency-message": "Minimum tilladte værdi er 60 sekunder.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Brug periode-mønster i sekunder", + "use-metadata-period-in-seconds-patterns-hint": "Bruger mønster fra metadata til at angive periode i sekunder.", + "period-in-seconds-pattern": "Periode-mønster i sekunder", + "period-in-seconds-pattern-required": "Periode-mønster i sekunder er påkrævet", + "min-period-seconds-message": "Minimum tilladte periode er 60 sekunder.", + "originator": "Afsender", + "message-body": "Beskedindhold", + "message-metadata": "Beskedmetadata", + "generate": "Generér", + "current-rule-node": "Aktuel regelnode", + "current-tenant": "Aktuel lejer", + "generator-function": "Generatorfunktion", + "test-generator-function": "Test generatorfunktion", + "generator": "Generator", + "test-filter-function": "Test filterfunktion", + "test-switch-function": "Test skiftefunktion", + "test-transformer-function": "Test transformerfunktion", + "transformer": "Transformer", + "alarm-create-condition": "Alarmoprettelsesbetingelse", + "test-condition-function": "Test betingelsesfunktion", + "alarm-clear-condition": "Alarmryddebetingelse", + "alarm-details-builder": "Alarmdetalje-generator", + "test-details-function": "Test detaljefunktion", + "alarm-type": "Alarmtype", + "select-entity-types": "Vælg enhedstyper", + "alarm-type-required": "Alarmtype er påkrævet.", + "alarm-severity": "Alarm alvorlighed", + "alarm-severity-required": "Alvorlighed er påkrævet", + "alarm-severity-pattern": "Alvorlighedsmønster", + "alarm-status-filter": "Alarmstatusfilter", + "alarm-status-list-empty": "Listen over alarmstatusser er tom", + "no-alarm-status-matching": "Ingen matchende alarmstatus fundet.", + "propagate": "Udbred alarm til relaterede enheder", + "propagate-to-owner": "Udbred alarm til ejer (Kunde eller Lejer)", + "propagate-to-tenant": "Udbred alarm til lejer", + "condition": "Betingelse", + "details": "Detaljer", + "to-string": "Til streng", + "test-to-string-function": "Test til-streng funktion", + "from-template": "Fra", + "from-template-required": "'Fra' er påkrævet", + "message-to-metadata": "Besked til metadata", + "metadata-to-message": "Metadata til besked", + "from-message": "Fra besked", + "from-metadata": "Fra metadata", + "to-template": "Til", + "to-template-required": "'Til' er påkrævet", + "mail-address-list-template-hint": "Kommasepareret adresseliste, brug ${metadataKey} for værdi fra metadata, $[messageKey] for værdi fra meddelelsens indhold", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "Emne", + "subject-template-required": "Emne er påkrævet", + "body-template": "Indhold", + "body-template-required": "Indhold er påkrævet", + "dynamic-mail-body-type": "Dynamisk indholdstype", + "mail-body-type": "Indholdstype", + "body-type-template": "Indholdstypemønster", + "reply-routing-configuration": "Svarrute-konfiguration", + "rpc-reply-routing-configuration-hint": "Konfiguration for at identificere tjeneste, session og anmodning i metadata.", + "reply-routing-configuration-hint": "Konfiguration for at identificere tjeneste og anmodning i metadata.", + "request-id-metadata-attribute": "Anmodnings-id", + "service-id-metadata-attribute": "Tjeneste-id", + "session-id-metadata-attribute": "Session-id", + "timeout-sec": "Timeout i sekunder", + "timeout-required": "Timeout er påkrævet", + "min-timeout-message": "Kun timeout-værdien 0 er tilladt som minimum.", + "endpoint-url-pattern": "URL-mønster for slutpunkt", + "endpoint-url-pattern-required": "URL-mønster for slutpunkt er påkrævet", + "request-method": "Forespørgselsmetode", + "use-simple-client-http-factory": "Brug simpel HTTP-klientfabrik", + "ignore-request-body": "Uden forespørgselsindhold", + "parse-to-plain-text": "Fortolk som almindelig tekst", + "parse-to-plain-text-hint": "Hvis valgt, transformeres JSON-strengen i beskedens indhold til almindelig tekst.", + "read-timeout": "Læs timeout i millisekunder", + "read-timeout-hint": "Værdien 0 betyder uendelig timeout", + "max-parallel-requests-count": "Maksimalt antal parallelle forespørgsler", + "max-parallel-requests-count-hint": "Værdien 0 betyder ingen begrænsning", + "max-response-size": "Maks. svarstørrelse (i KB)", + "max-response-size-hint": "Maksimal hukommelse tildelt til buffering ved afkodning/enkodning af HTTP-meddelelser", + "headers": "Headers", + "headers-hint": "Brug ${metadataKey} for metadata eller $[messageKey] for beskeddata i header/værdi felter", + "header": "Header", + "header-required": "Header er påkrævet", + "value": "Værdi", + "value-required": "Værdi er påkrævet", + "topic-pattern": "Emnemønster", + "key-pattern": "Nøglemønster", + "key-pattern-hint": "Valgfri. Angiv partition eller nøgle til brug ved sending.", + "topic-pattern-required": "Emnemønster er påkrævet", + "topic": "Emne", + "topic-required": "Emne er påkrævet", + "bootstrap-servers": "Bootstrap-servere", + "bootstrap-servers-required": "Bootstrap-servere er påkrævede", + "other-properties": "Andre egenskaber", + "key": "Nøgle", + "key-required": "Nøgle er påkrævet", + "retries": "Antal genforsøg ved fejl", + "min-retries-message": "Kun 0 genforsøg tilladt som minimum", + "batch-size-bytes": "Batchstørrelse i bytes", + "min-batch-size-bytes-message": "Minimum batchstørrelse er 0", + "linger-ms": "Buffer-tid lokalt (ms)", + "min-linger-ms-message": "Minimum 0 ms tilladt", + "buffer-memory-bytes": "Maks. bufferstørrelse (bytes)", + "min-buffer-memory-message": "Minimum bufferstørrelse er 0", + "memory-buffer-size-range": "Bufferstørrelse skal være mellem 0 og {{max}} KB", + "acks": "Antal kvitteringer", + "topic-arn-pattern": "ARN-emnemønster", + "topic-arn-pattern-required": "ARN-emnemønster er påkrævet", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID er påkrævet", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key er påkrævet", + "aws-region": "AWS-region", + "aws-region-required": "AWS-region er påkrævet", + "exchange-name-pattern": "Exchange-navnemønster", + "routing-key-pattern": "Routing-nøglemønster", + "message-properties": "Beskedegenskaber", + "host": "Host", + "host-required": "Host er påkrævet", + "port": "Port", + "port-required": "Port er påkrævet", + "port-range": "Port skal være mellem 1 og 65535", + "virtual-host": "Virtuel host", + "username": "Brugernavn", + "password": "Adgangskode", + "automatic-recovery": "Automatisk gendannelse", + "connection-timeout-ms": "Forbindelsestimeout (ms)", + "min-connection-timeout-ms-message": "Minimum værdi er 0 ms", + "handshake-timeout-ms": "Handshake-timeout (ms)", + "min-handshake-timeout-ms-message": "Minimum værdi er 0 ms", + "client-properties": "Klientegenskaber", + "queue-url-pattern": "Kø-URL mønster", + "queue-url-pattern-required": "Kø-URL mønster er påkrævet", + "delay-seconds": "Forsinkelse (sekunder)", + "min-delay-seconds-message": "Minimum forsinkelse er 0 sekunder", + "max-delay-seconds-message": "Maksimal forsinkelse er 900 sekunder", "name": "Navn", + "name-required": "Navn er påkrævet", + "queue-type": "Køtype", + "sqs-queue-standard": "Standard", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "GCP-projekt-ID", + "gcp-project-id-required": "GCP-projekt-ID er påkrævet", + "gcp-service-account-key": "GCP servicekonto-nøglefil", + "gcp-service-account-key-required": "GCP servicekonto-nøglefil er påkrævet", + "pubsub-topic-name": "Emnenavn", + "pubsub-topic-name-required": "Emnenavn er påkrævet", + "message-attributes": "Beskedattributter", + "message-attributes-hint": "Brug ${metadataKey} eller $[messageKey] i navn/værdi felter", + "connect-timeout": "Forbindelsestimeout (sek)", + "connect-timeout-required": "Forbindelsestimeout er påkrævet.", + "connect-timeout-range": "Timeout skal være mellem 1 og 200.", + "client-id": "Klient-ID", + "client-id-hint": "Valgfri. Lad være tomt for automatisk generering. For at undgå konflikter i mikrotjenester, brug unikke ID’er.", + "append-client-id-suffix": "Tilføj service-ID som suffix til klient-ID", + "client-id-suffix-hint": "Anvendes kun hvis klient-ID er angivet. Hjælper med at undgå konflikter i mikrotjenestetilstand.", + "device-id": "Enheds-ID", + "device-id-required": "Enheds-ID er påkrævet.", + "clean-session": "Ren session", + "enable-ssl": "Aktiver SSL", + "credentials": "Legitimationsoplysninger", + "credentials-type": "Legitimationstype", + "credentials-type-required": "Legitimationstype er påkrævet.", + "credentials-anonymous": "Anonym", + "credentials-basic": "Grundlæggende", + "credentials-pem": "PEM", + "credentials-pem-hint": "Mindst serverens CA-certifikat eller et par af klientcertifikat og privat nøgle er påkrævet", + "credentials-sas": "Delt adgangsnøgle (SAS)", + "sas-key": "SAS-nøgle", + "sas-key-required": "SAS-nøgle er påkrævet.", + "hostname": "Værtsnavn", + "hostname-required": "Værtsnavn er påkrævet.", + "azure-ca-cert": "CA-certifikatfil", + "username-required": "Brugernavn er påkrævet.", + "password-required": "Adgangskode er påkrævet.", + "ca-cert": "Serverens CA-certifikatfil", + "private-key": "Klientens private nøglefil", + "cert": "Klientcertifikatfil", + "no-file": "Ingen fil valgt.", + "drop-file": "Slip en fil eller klik for at vælge en fil til upload.", + "private-key-password": "Adgangskode til privat nøgle", + "use-system-smtp-settings": "Brug systemets SMTP-indstillinger", + "use-metadata-dynamic-interval": "Brug dynamisk interval", + "metadata-dynamic-interval-hint": "Intervalstart og -slut felterne understøtter skabeloner. Brug $[messageKey] eller ${metadataKey}.", + "use-metadata-interval-patterns-hint": "Hvis valgt, bruges mønstre fra metadata eller data til interval.", + "use-message-alarm-data": "Brug alarmdata fra besked", + "overwrite-alarm-details": "Overskriv alarmdetaljer", + "use-alarm-severity-pattern": "Brug mønster for alarmens alvorlighed", + "check-all-keys": "Tjek at alle specificerede felter er til stede", + "check-all-keys-hint": "Tjekker at alle nøgler findes i data og metadata.", + "check-relation-to-specific-entity": "Tjek relation til specifik enhed", + "check-relation-to-specific-entity-tooltip": "Tjekker om relation eksisterer til en specifik eller hvilken som helst enhed baseret på type og retning.", + "check-relation-hint": "Tjek for relation til bestemt eller vilkårlig enhed.", + "delete-relation-with-specific-entity": "Slet relation med specifik enhed", + "delete-relation-with-specific-entity-hint": "Sletter relation med en bestemt enhed. Ellers slettes alle matchende relationer.", + "delete-relation-hint": "Sletter relation fra oprindelsen til den angivne enhed eller liste af enheder.", + "remove-current-relations": "Fjern nuværende relationer", + "remove-current-relations-hint": "Fjerner nuværende relationer baseret på type og retning.", + "change-originator-to-related-entity": "Skift oprindelse til relateret enhed", + "change-originator-to-related-entity-hint": "Behandler beskeden som om den kommer fra en anden enhed.", + "start-interval": "Interval start", + "end-interval": "Interval slut", + "start-interval-required": "Interval start er påkrævet.", + "end-interval-required": "Interval slut er påkrævet.", + "smtp-protocol": "Protokol", + "smtp-host": "SMTP-vært", + "smtp-host-required": "SMTP-vært er påkrævet.", + "smtp-port": "SMTP-port", + "smtp-port-required": "SMTP-port er påkrævet.", + "smtp-port-range": "SMTP-port skal være mellem 1 og 65535.", + "timeout-msec": "Timeout (ms)", + "min-timeout-msec-message": "Minimum tilladt værdi er 0 ms.", + "enter-username": "Indtast brugernavn", + "enter-password": "Indtast adgangskode", + "enable-tls": "Aktiver TLS", + "tls-version": "TLS-version", + "enable-proxy": "Aktiver proxy", + "use-system-proxy-properties": "Brug systemets proxy-egenskaber", + "proxy-host": "Proxy-vært", + "proxy-host-required": "Proxy-vært er påkrævet.", + "proxy-port": "Proxy-port", + "proxy-port-required": "Proxy-port er påkrævet.", + "proxy-port-range": "Proxy-port skal være mellem 1 og 65535.", + "proxy-user": "Proxy-bruger", + "proxy-password": "Proxy-adgangskode", + "proxy-scheme": "Proxy-skema", + "numbers-to-template": "Telefonnumre til skabelon", + "numbers-to-template-required": "Telefonnumre til skabelon er påkrævet", + "numbers-to-template-hint": "Kommaseparerede telefonnumre, brug ${metadataKey} for værdi fra metadata, $[messageKey] for værdi fra meddelelsens indhold", + "sms-message-template": "SMS beskedskabelon", + "sms-message-template-required": "SMS beskedskabelon er påkrævet", + "use-system-sms-settings": "Brug systemets SMS-udbyderindstillinger", + "min-period-0-seconds-message": "Kun 0 sekunders minimumsperiode er tilladt.", + "max-pending-messages": "Maksimalt antal ventende beskeder", + "max-pending-messages-required": "Maksimalt antal ventende beskeder er påkrævet.", + "max-pending-messages-range": "Skal være mellem 1 og 100000.", + "originator-types-filter": "Filter for afsendertyper", + "interval-seconds": "Interval i sekunder", + "interval-seconds-required": "Interval er påkrævet.", + "int-range": "Værdien må ikke overstige det maksimale heltalsgrænse (2147483648)", + "min-interval-seconds-message": "Kun 1 sekund som minimumsinterval er tilladt.", + "output-timeseries-key-prefix": "Præfiks for output time series nøgle", + "output-timeseries-key-prefix-required": "Præfiks for output time series nøgle er påkrævet.", + "separator-hint": "Tryk på \"Enter\" for at afslutte feltindtastning.", + "select-details": "Vælg detaljer", + "entity-details-id": "Id", + "entity-details-title": "Titel", + "entity-details-country": "Land", + "entity-details-state": "Stat", + "entity-details-city": "By", + "entity-details-zip": "Postnummer", + "entity-details-address": "Adresse", + "entity-details-address2": "Adresse 2", + "entity-details-additional_info": "Yderligere information", + "entity-details-phone": "Telefon", + "entity-details-email": "Email", + "email-sender": "Email afsender", + "fields-to-check": "Felter der skal kontrolleres", + "add-detail": "Tilføj detalje", + "check-all-keys-tooltip": "Kontrollerer, at alle angivne felter findes i besked og metadata.", + "fields-to-check-hint": "Tryk på \"Enter\" for at afslutte. Flere felter understøttes.", + "entity-details-list-empty": "Mindst én detalje skal vælges.", + "alarm-status": "Alarmstatus", + "alarm-required": "Mindst én alarmstatus skal vælges.", + "no-entity-details-matching": "Ingen tilsvarende entitetsdetaljer fundet.", + "custom-table-name": "Brugertabelnavn", + "custom-table-name-required": "Tabelnavn er påkrævet", + "custom-table-hint": "Skal starte med 'cs_tb_'. Angiv uden præfikset.", + "message-field": "Beskedfelt", + "message-field-required": "Beskedfelt er påkrævet.", + "table-col": "Tabelkolonne", + "table-col-required": "Tabelkolonne er påkrævet.", + "latitude-field-name": "Navn på breddegradsfelt", + "longitude-field-name": "Navn på længdegradsfelt", + "latitude-field-name-required": "Navn på breddegradsfelt er påkrævet.", + "longitude-field-name-required": "Navn på længdegradsfelt er påkrævet.", + "fetch-perimeter-info-from-metadata": "Hent perimeteroplysninger fra metadata", + "fetch-perimeter-info-from-metadata-tooltip": "Parser metadatafelt for cirkel eller polygon afhængigt af perimeter-typen.", + "perimeter-key-name": "Perimeter nøgle navn", + "perimeter-key-name-hint": "Metadata felt med perimeter info.", + "perimeter-key-name-required": "Perimeter nøgle navn er påkrævet.", + "perimeter-circle": "Cirkel", + "perimeter-polygon": "Polygon", + "perimeter-type": "Perimeter type", + "circle-center-latitude": "Cirkelens center breddegrad", + "circle-center-latitude-required": "Center breddegrad er påkrævet.", + "circle-center-longitude": "Cirkelens center længdegrad", + "circle-center-longitude-required": "Center længdegrad er påkrævet.", + "range-unit-meter": "Meter", + "range-unit-kilometer": "Kilometer", + "range-unit-foot": "Fod", + "range-unit-mile": "Mil", + "range-unit-nautical-mile": "Sømil", + "range-units": "Afstandsenheder", + "range-units-required": "Afstandsenheder er påkrævet.", + "range": "Rækkevidde", + "range-required": "Rækkevidde er påkrævet.", + "polygon-definition": "Polygondefinition", + "polygon-definition-required": "Polygondefinition er påkrævet.", + "polygon-definition-hint": "Format: [[lat1,lon1],[lat2,lon2],...,[latN,lonN]].", + "min-inside-duration": "Minimal varighed indenfor", + "min-inside-duration-value-required": "Værdi er påkrævet", + "min-inside-duration-time-unit": "Tidsenhed for minimal varighed indenfor", + "min-outside-duration": "Minimal varighed udenfor", + "min-outside-duration-value-required": "Værdi er påkrævet", + "min-outside-duration-time-unit": "Tidsenhed for minimal varighed udenfor", + "tell-failure-if-absent": "Rapporter fejl ved fravær", + "tell-failure-if-absent-hint": "Hvis en valgt nøgle mangler, returneres fejl.", + "get-latest-value-with-ts": "Hent seneste værdi med tidsstempel", + "get-latest-value-with-ts-hint": "Returnerer JSON med både værdi og tidsstempel.", + "ignore-null-strings": "Ignorér tomme strenge", + "ignore-null-strings-hint": "Ignorerer felter med tomme værdier.", + "add-metadata-key-values-as-kafka-headers": "Tilføj nøgle-værdi par fra metadata til Kafka headers", + "add-metadata-key-values-as-kafka-headers-hint": "Hvis valgt, tilføjes metadataens nøgle-værdi par som byte arrays med foruddefineret tegnkodning.", + "charset-encoding": "Tegnkodning", + "charset-encoding-required": "Tegnkodning er påkrævet.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "Vælg et kønavn fra rullemenu eller angiv et brugerdefineret navn.", + "device-profile-node-hint": "Nyttigt ved varighed eller gentagne betingelser for at sikre fortsat evaluering af alarmtilstand.", + "persist-alarm-rules": "Gem tilstand for alarmregler", + "persist-alarm-rules-hint": "Gemmer behandlingsstatus i databasen.", + "fetch-alarm-rules": "Hent tilstand for alarmregler", + "fetch-alarm-rules-hint": "Genskaber tilstand ved opstart for at sikre korrekt alarmstatus.", + "input-value-key": "Input nøgle", + "input-value-key-required": "Input nøgle er påkrævet.", + "output-value-key": "Output nøgle", + "output-value-key-required": "Output nøgle er påkrævet.", + "number-of-digits-after-floating-point": "Antal decimaler", + "number-of-digits-after-floating-point-range": "Antal decimaler skal være mellem 0 og 15.", + "failure-if-delta-negative": "Rapportér fejl ved negativ delta", + "failure-if-delta-negative-tooltip": "Fejl ved negativ forskel mellem værdier.", + "use-caching": "Brug caching", + "use-caching-tooltip": "Cacher input-værdi for performance.", + "add-time-difference-between-readings": "Tilføj tidsforskel mellem aflæsninger", + "add-time-difference-between-readings-tooltip": "Tilføjer \"{{periodValueKey}}\" til beskeden.", + "period-value-key": "Tidsforskel nøgle", + "period-value-key-required": "Tidsforskel nøgle er påkrævet.", + "general-pattern-hint": "Brug ${metadataKey} eller $[messageKey]", + "alarm-severity-pattern-hint": "Angiv alarmniveau (CRITICAL, MAJOR, etc.) via metadata eller besked.", + "output-node-name-hint": "Navn på regelnode bruges som relationstype til videresendelse.", + "use-server-ts": "Brug server-tidsstempel", + "use-server-ts-hint": "Sikrer korrekt rækkefølge ved manglende tidsstempel.", + "kv-map-pattern-hint": "Alle felter understøtter templatization.", + "kv-map-single-pattern-hint": "Felt understøtter templatization.", + "shared-scope": "Delt område", + "server-scope": "Serverområde", + "client-scope": "Klientområde", + "attribute-type": "Attribute", + "attribute-type-description": "Hent attributværdi fra database", + "attribute-type-result-description": "Gem som attribut i database", + "constant-type": "Konstant", + "constant-type-description": "Definér konstant værdi", + "time-series-type": "Tidsserie", + "time-series-type-description": "Hent seneste værdi fra database", + "time-series-type-result-description": "Gem som tidsserie i database", + "message-body-type": "Besked", + "message-body-type-description": "Hent argumentværdi fra besked", + "message-body-type-result-description": "Tilføj resultat til besked", + "message-metadata-type": "Metadata", + "message-metadata-type-description": "Hent argumentværdi fra metadata", + "message-metadata-result-description": "Tilføj resultat til metadata", + "argument-tile": "Argumenter", + "no-arguments-prompt": "Ingen argumenter konfigureret", + "result-title": "Resultat", + "functions-field-input": "Funktioner", + "no-option-found": "Ingen muligheder fundet", + "argument-source-field-input": "Kilde", + "argument-source-field-input-required": "Kilde er påkrævet.", + "argument-key-field-input": "Nøgle", + "argument-key-field-input-required": "Nøgle er påkrævet.", + "constant-value-field-input": "Konstant værdi", + "constant-value-field-input-required": "Konstant værdi er påkrævet.", + "attribute-scope-field-input": "Attribut område", + "attribute-scope-field-input-required": "Område er påkrævet.", + "default-value-field-input": "Standardværdi", + "type-field-input": "Type", + "type-field-input-required": "Type er påkrævet.", + "key-field-input": "Nøgle", + "add-entity-type": "Tilføj entitetstype", + "add-device-profile": "Tilføj enhedsprofil", + "key-field-input-required": "Nøgle er påkrævet.", + "number-floating-point-field-input": "Antal decimaler", + "number-floating-point-field-input-hint": "Brug 0 for heltal", + "add-to-message-field-input": "Tilføj til besked", + "add-to-metadata-field-input": "Tilføj til metadata", + "custom-expression-field-input": "Matematisk udtryk", + "custom-expression-field-input-required": "Matematisk udtryk er påkrævet", + "custom-expression-field-input-hint": "F.eks. konverter Fahrenheit til Celsius", + "retained-message": "Behold besked", + "attributes-mapping": "Attribut mapping", + "latest-telemetry-mapping": "Seneste telemetry mapping", + "add-mapped-attribute-to": "Tilføj mappede attributter til", + "add-mapped-latest-telemetry-to": "Tilføj mappet telemetry til", + "add-mapped-fields-to": "Tilføj mappede felter til", + "add-selected-details-to": "Tilføj valgte detaljer til", + "clear-selected-types": "Ryd valgte typer", + "clear-selected-details": "Ryd valgte detaljer", + "clear-selected-fields": "Ryd valgte felter", + "clear-selected-keys": "Ryd valgte nøgler", + "geofence-configuration": "Geofence-konfiguration", + "coordinate-field-names": "Koordinatfeltnavne", + "coordinate-field-hint": "Forsøger at hente felter fra besked. Hvis ikke til stede, bruges metadata.", + "presence-monitoring-strategy": "Strategi for tilstedeværelsesovervågning", + "presence-monitoring-strategy-on-first-message": "Ved første besked", + "presence-monitoring-strategy-on-each-message": "Ved hver besked", + "presence-monitoring-strategy-on-first-message-hint": "Rapporterer status 'Inde' eller 'Ude' ved første besked efter minimumsvarighed siden sidste status.", + "presence-monitoring-strategy-on-each-message-hint": "Rapporterer status ved hver besked efter statusændring.", + "fetch-credentials-to": "Hent legitimationsoplysninger til", + "add-originator-attributes-to": "Tilføj afsenders attributter til", + "originator-attributes": "Afsenders attributter", + "fetch-latest-telemetry-with-timestamp": "Hent seneste telemetry med tidsstempel", + "fetch-latest-telemetry-with-timestamp-tooltip": "Inkluderer tidsstempel i metadata, f.eks.: \"{{latestTsKeyName}}\": \"{ts:1574329385897, value:42}\"", + "tell-failure": "Rapportér fejl hvis attribut mangler", + "tell-failure-tooltip": "Rapporterer fejl hvis mindst én valgt nøgle mangler.", + "created-time": "Oprettelsestid", + "chip-help": "Tryk 'Enter' for at afslutte {{inputName}}. 'Backspace' sletter. Flere værdier understøttet.", + "detail": "detalje", + "field-name": "feltnavn", + "device-profile": "enhedsprofil", + "entity-type": "entitetstype", + "message-type": "beskedtype", + "timeseries-key": "tidsserienøgle", "type": "Type", - "created_customer": "Oprettet af kunde", - "selected-blob-entities": "", - "download-blob-entity": "Download fil", - "delete-blob-entity": "Slet fil", - "delete-blob-entity-title": "", - "delete-blob-entity-text": "Vær forsigtig. Efter bekræftelsen vil fildata være uoprettelige.", - "delete-blob-entities-title": "", - "delete-blob-entities-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte filer blive fjernet, og alle relaterede data vil være uoprettelige." + "first-name": "Fornavn", + "last-name": "Efternavn", + "label": "Etiket", + "originator-fields-mapping": "Afsender felttilknytning", + "add-mapped-originator-fields-to": "Tilføj mappede afsenderfelter til", + "fields": "Felter", + "skip-empty-fields": "Spring tomme felter over", + "skip-empty-fields-tooltip": "Tomme felter tilføjes ikke til uddata.", + "fetch-interval": "Hent interval", + "fetch-strategy": "Hentestrategi", + "fetch-timeseries-from-to": "Hent tidsserie fra {{startInterval}} {{startIntervalTimeUnit}} til {{endInterval}} {{endIntervalTimeUnit}} siden.", + "fetch-timeseries-from-to-invalid": "\"Start\" skal være mindre end \"Slut\".", + "use-metadata-dynamic-interval-tooltip": "Bruger dynamisk start og slut interval baseret på besked og metadata.", + "all-mode-hint": "Ved \"Alle\" hentes alle værdier i intervallet.", + "first-mode-hint": "Ved \"Første\" hentes nærmeste værdi ved start.", + "last-mode-hint": "Ved \"Sidste\" hentes nærmeste værdi ved slut.", + "ascending": "Stigende", + "descending": "Faldende", + "min": "Min", + "max": "Maks", + "average": "Gennemsnit", + "sum": "Sum", + "count": "Antal", + "none": "Ingen", + "last-level-relation-tooltip": "Søger kun relationer på det maksimale niveau.", + "last-level-device-relation-tooltip": "Søger kun enheder på det maksimale niveau.", + "data-to-fetch": "Data der skal hentes", + "mapping-of-customers": "Kundemapping", + "map-fields-required": "Alle felter skal udfyldes.", + "attributes": "Attributter", + "related-device-attributes": "Attributter for relateret enhed", + "add-selected-attributes-to": "Tilføj valgte attributter til", + "device-profiles": "Enhedsprofiler", + "mapping-of-tenant": "Tenant-mapping", + "add-attribute-key": "Tilføj attributnøgle", + "message-template": "Beskedskabelon", + "message-template-required": "Beskedskabelon er påkrævet", + "use-system-slack-settings": "Brug systemets Slack-indstillinger", + "slack-api-token": "Slack API-token", + "slack-api-token-required": "Slack API-token er påkrævet", + "keys-mapping": "Nøgletilknytning", + "add-key": "Tilføj nøgle", + "recipients": "Modtagere", + "message-subject-and-content": "Besked emne og indhold", + "template-rules-hint": "Brug $[messageKey] eller ${metadataKey} til værdier.", + "originator-customer-desc": "Brug kundens oprindelige afsender.", + "originator-tenant-desc": "Brug nuværende tenant.", + "originator-related-entity-desc": "Brug relateret entitet som afsender.", + "originator-alarm-originator-desc": "Brug alarmens afsender (kun hvis besked er alarm).", + "originator-entity-by-name-pattern-desc": "Find og brug entitet ud fra navn og type.", + "email-from-template-hint": "Brug $[messageKey] eller ${metadataKey} for at trække værdier.", + "recipients-block-main-hint": "Kommasepareret liste, understøtter templatization.", + "forward-msg-default-rule-chain": "Videresend besked til standard regelkæde", + "forward-msg-default-rule-chain-tooltip": "Bruger standard regelkæde for afsender eller konfigureret kæde.", + "exclude-zero-deltas": "Udelad nul-delta værdier", + "exclude-zero-deltas-hint": "Tilføj kun nøgle hvis værdi ikke er nul.", + "exclude-zero-deltas-time-difference-hint": "Tilføj kun hvis outputværdi ≠ 0.", + "search-direction-from": "Fra afsender til målentitet", + "search-direction-to": "Fra målentitet til afsender", + "del-relation-direction-from": "Fra afsender", + "del-relation-direction-to": "Til afsender", + "target-entity": "Målenhed", + "function-configuration": "Funktionskonfiguration", + "function-name": "Funktionsnavn", + "function-name-required": "Funktionsnavn er påkrævet.", + "qualifier": "Kvalifikator", + "qualifier-hint": "Hvis kvalifikator ikke er angivet, bruges standardværdien \"$LATEST\".", + "aws-credentials": "AWS-legitimationsoplysninger", + "connection-timeout": "Forbindelsestimeout", + "connection-timeout-required": "Forbindelsestimeout er påkrævet.", + "connection-timeout-min": "Minimum forbindelsestimeout er 0.", + "connection-timeout-hint": "Ventetid ved etablering af forbindelse. 0 betyder uendelig og frarådes.", + "request-timeout": "Anmodningstimeout", + "request-timeout-required": "Anmodningstimeout er påkrævet", + "request-timeout-min": "Minimum anmodningstimeout er 0", + "request-timeout-hint": "Ventetid for anmodning, før timeout. 0 betyder uendelig og frarådes.", + "units": "Enheder", + "tell-failure-aws-lambda": "Rapportér fejl hvis AWS Lambda-funktion kaster en undtagelse", + "tell-failure-aws-lambda-hint": "Tvinger fejl hvis Lambda-funktionen fejler.", + "basic-mode": "Grundlæggende", + "advanced-mode": "Avanceret", + "save-time-series": { + "processing-settings": "Behandlingsindstillinger", + "processing-settings-hint": "Definér hvordan beskeder behandles. Grundlæggende eller avancerede strategier.", + "advanced-settings-hint": "Vær forsigtig – visse kombinationer kan føre til uventet adfærd.", + "strategy": "Strategi", + "deduplication-interval": "Deduplikeringsinterval", + "deduplication-interval-required": "Dedupliceringsinterval er påkrævet", + "deduplication-interval-min-max-range": "Skal være mellem 1 sekund og 1 dag", + "strategy-type": { + "every-message": "Ved hver besked", + "skip": "Spring over", + "deduplicate": "Dedupliker", + "web-sockets-only": "Kun WebSockets" + }, + "time-series": "Tidsserier", + "latest": "Seneste værdier", + "web-sockets": "WebSockets", + "calculated-fields": "Beregnet felter" + }, + "save-attribute": { + "processing-settings": "Behandlingsindstillinger", + "processing-settings-hint": "Definér hvordan beskeder behandles. Grundlæggende eller avancerede strategier.", + "advanced-settings-hint": "Vær forsigtig – visse kombinationer kan føre til uventet adfærd.", + "strategy": "Strategi", + "deduplication-interval": "Deduplikeringsinterval", + "deduplication-interval-required": "Deduplikeringsinterval er påkrævet", + "deduplication-interval-min-max-range": "Skal være mellem 1 sekund og 1 dag", + "scope": "Omfang", + "strategy-type": { + "every-message": "Ved hver besked", + "skip": "Spring over", + "deduplicate": "Dedupliker", + "web-sockets-only": "Kun WebSockets" + }, + "attributes": "Attributter" + }, + "key-val": { + "key": "Nøgle", + "value": "Værdi", + "see-examples": "Se eksempler.", + "remove-entry": "Fjern post", + "remove-mapping-entry": "Fjern kortlægningspost", + "add-mapping-entry": "Tilføj kortlægning", + "add-entry": "Tilføj post", + "copy-key-values-from": "Kopiér nøgle-værdier fra", + "delete-key-values": "Slet nøgle-værdier", + "delete-key-values-from": "Slet nøgle-værdier fra", + "at-least-one-key-error": "Mindst én nøgle skal vælges.", + "unique-key-value-pair-error": "'{{keyText}}' skal være forskellig fra '{{valText}}'!" + }, + "mail-body-types": { + "plain-text": "Almindelig tekst", + "html": "HTML", + "dynamic": "Dynamisk", + "use-body-type-template": "Brug skabelon til brødtype", + "plain-text-description": "Simpel tekst uden formatering.", + "html-text-description": "Tillader HTML-tags til formatering, links og billeder.", + "dynamic-text-description": "Vælg dynamisk mellem almindelig tekst eller HTML.", + "after-template-evaluation-hint": "Efter evaluering: true = HTML, false = tekst." + } }, "timezone": { "timezone": "Tidszone", "select-timezone": "Vælg tidszone", - "no-timezones-matching": "", + "no-timezones-matching": "Ingen tidszoner matcher '{{timezone}}'.", "timezone-required": "Tidszone er påkrævet.", - "browser-time": "Browsertid" + "browser-time": "Browserens tid" }, "queue": { - "select_name": "Vælg kønavn", - "name": "Kønavn", - "name_required": "Kønavn er påkrævet." + "queue-name": "Kø", + "no-queues-found": "Ingen køer fundet.", + "no-queues-matching": "Ingen køer matcher '{{queue}}'.", + "select-name": "Vælg kønavn", + "name": "Navn", + "name-required": "Kønavn er påkrævet!", + "name-unique": "Kønavnet er ikke unikt!", + "name-pattern": "Kønavnet indeholder ugyldige tegn! Kun ASCII alfanumeriske tegn, '.', '_' og '-' er tilladt.", + "queue-required": "Kø er påkrævet!", + "topic-required": "Køemne er påkrævet!", + "poll-interval-required": "Poll-interval er påkrævet!", + "poll-interval-min-value": "Poll-intervalværdien må ikke være mindre end 1", + "partitions-required": "Partitioner er påkrævet!", + "partitions-min-value": "Antal partitioner må ikke være mindre end 1", + "pack-processing-timeout-required": "Behandlingstimeout er påkrævet", + "pack-processing-timeout-min-value": "Behandlingstimeout må ikke være mindre end 1", + "batch-size-required": "Batchstørrelse er påkrævet!", + "batch-size-min-value": "Batchstørrelse må ikke være mindre end 1", + "retries-required": "Antal forsøg er påkrævet!", + "retries-min-value": "Antal forsøg må ikke være negativt", + "failure-percentage-required": "Fejlprocent er påkrævet!", + "failure-percentage-min-value": "Fejlprocent må ikke være mindre end 0", + "failure-percentage-max-value": "Fejlprocent må ikke være mere end 100", + "pause-between-retries-required": "Pause mellem forsøg er påkrævet!", + "pause-between-retries-min-value": "Pause mellem forsøg må ikke være mindre end 1", + "max-pause-between-retries-required": "Maks. pause mellem forsøg er påkrævet!", + "max-pause-between-retries-min-value": "Maks. pause mellem forsøg må ikke være mindre end 1", + "submit-strategy-type-required": "Afsendelsesstrategi er påkrævet!", + "processing-strategy-type-required": "Behandlingsstrategi er påkrævet!", + "queues": "Køer", + "selected-queues": "{ count, plural, =1 {1 kø} other {# køer} } valgt", + "delete-queue-title": "Er du sikker på, at du vil slette køen '{{queueName}}'?", + "delete-queues-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 kø} other {# køer} }?", + "delete-queue-text": "Vær forsigtig, efter bekræftelse vil køen og alle relaterede data være uigenkaldelige.", + "delete-queues-text": "Efter bekræftelse vil alle valgte køer blive slettet og ikke være tilgængelige.", + "search": "Søg kø", + "add": "Tilføj kø", + "details": "Kødetaljer", + "topic": "Emne", + "submit-settings": "Indstillinger for afsendelse", + "submit-strategy": "Strategitype *", + "grouping-parameter": "Grupperingsparameter", + "processing-settings": "Behandlingsindstillinger for forsøg", + "processing-strategy": "Behandlingstype *", + "retries-settings": "Indstillinger for forsøg", + "polling-settings": "Polling-indstillinger", + "batch-processing": "Batchbehandling", + "poll-interval": "Polling-interval", + "partitions": "Partitioner", + "immediate-processing": "Øjeblikkelig behandling", + "consumer-per-partition": "Send beskedpolling for hver forbruger", + "consumer-per-partition-hint": "Aktivér separat forbruger pr. partition", + "duplicate-msg-to-all-partitions": "Duplikér besked til alle partitioner", + "processing-timeout": "Behandlingstid, ms", + "batch-size": "Batchstørrelse", + "retries": "Antal forsøg (0 – ubegrænset)", + "failure-percentage": "Fejlbeskeder til at springe forsøg over, %", + "pause-between-retries": "Forsøg igen efter, sek", + "max-pause-between-retries": "Yderligere forsøg efter, sek", + "delete": "Slet kø", + "copyId": "Kopiér kø-ID", + "idCopiedMessage": "Kø-ID er kopieret til udklipsholder", + "description": "Beskrivelse", + "description-hint": "Denne tekst vises i købeskrivelsen i stedet for den valgte strategi", + "alt-description": "Afsendelsesstrategi: {{submitStrategy}}, Behandlingsstrategi: {{processingStrategy}}", + "custom-properties": "Brugerdefinerede egenskaber", + "custom-properties-hint": "Brugerdefinerede egenskaber til køoprettelse, f.eks. 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies": { + "sequential-by-originator-label": "Sekventiel efter afsender", + "sequential-by-originator-hint": "Ny besked for f.eks. enhed A sendes først, når den tidligere besked for enhed A er bekræftet", + "sequential-by-tenant-label": "Sekventiel efter lejer", + "sequential-by-tenant-hint": "Ny besked for f.eks. lejer A sendes først, når den tidligere besked for lejer A er bekræftet", + "sequential-label": "Sekventiel", + "sequential-hint": "Ny besked sendes først, når den tidligere er bekræftet", + "burst-label": "Burst", + "burst-hint": "Alle beskeder sendes til regelkæder i den rækkefølge, de modtages", + "batch-label": "Batch", + "batch-hint": "Ny batch sendes først, når den forrige batch er bekræftet", + "skip-all-failures-label": "Spring alle fejl over", + "skip-all-failures-hint": "Ignorer alle fejl", + "skip-all-failures-and-timeouts-label": "Spring fejl og timeouts over", + "skip-all-failures-and-timeouts-hint": "Ignorer fejl og timeouts", + "retry-all-label": "Forsøg alle igen", + "retry-all-hint": "Forsøg alle beskeder fra pakken igen", + "retry-failed-label": "Forsøg fejlede igen", + "retry-failed-hint": "Forsøg alle fejlede beskeder fra pakken igen", + "retry-timeout-label": "Forsøg timeouts igen", + "retry-timeout-hint": "Forsøg alle timeout-beskeder fra pakken igen", + "retry-failed-and-timeout-label": "Forsøg fejl og timeout igen", + "retry-failed-and-timeout-hint": "Forsøg alle fejl og timeout-beskeder igen" + } + }, + "queue-statistics": { + "queue-statistics": "Køstatistik", + "no-queue-statistics-matching": "Ingen køstatistikker matcher '{{entity}}'.", + "queue-statistics-required": "Køstatistik er påkrævet.", + "list-of-queue-statistics": "{ count, plural, =1 {En køstatistik} other {Liste med # køstatistikker} }", + "selected-queue-statistics": "{ count, plural, =1 {1 køstatistik} other {# køstatistikker} } valgt", + "no-queue-statistics-text": "Ingen køstatistik fundet", + "queue-statistics-starts-with": "Køstatistikker hvis navne starter med '{{prefix}}'" + }, + "server-error": { + "general": "Generel serverfejl", + "authentication": "Godkendelsesfejl", + "jwt-token-expired": "JWT-token er udløbet", + "tenant-trial-expired": "Lejerens prøveperiode er udløbet", + "credentials-expired": "Adgangsoplysninger er udløbet", + "permission-denied": "Adgang nægtet", + "invalid-arguments": "Ugyldige argumenter", + "bad-request-params": "Ugyldige forespørgselsparametre", + "item-not-found": "Element ikke fundet", + "too-many-requests": "For mange forespørgsler", + "too-many-updates": "For mange opdateringer" }, "tenant": { "tenant": "Lejer", "tenants": "Lejere", - "management": "Administration af lejer", + "management": "Lejeradministration", "add": "Tilføj lejer", - "admins": "Admin.", - "manage-tenant-admins": "Administrer lejeradmin.", + "admins": "Administratorer", + "manage-tenant-admins": "Administrer lejeradministratorer", "delete": "Slet lejer", "add-tenant-text": "Tilføj ny lejer", "no-tenants-text": "Ingen lejere fundet", - "tenant-details": "Lejeroplysninger", - "delete-tenant-title": "", - "delete-tenant-text": "Vær forsigtig. Efter bekræftelsen vil lejeren og alle relaterede data være uoprettelige.", - "delete-tenants-title": "", - "delete-tenants-action-title": "", - "delete-tenants-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte lejere blive fjernet, og alle relaterede data vil være uoprettelige.", + "tenant-details": "Lejerdetaljer", + "title-max-length": "Titlen skal være under 256 tegn", + "delete-tenant-title": "Er du sikker på, at du vil slette lejeren '{{tenantTitle}}'?", + "delete-tenant-text": "Vær forsigtig, efter bekræftelsen vil lejeren og alle relaterede data blive uigenkaldelige.", + "delete-tenants-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 lejer} other {# lejere} }?", + "delete-tenants-action-title": "Slet { count, plural, =1 {1 lejer} other {# lejere} }", + "delete-tenants-text": "Vær forsigtig, efter bekræftelsen vil alle valgte lejere og relaterede data blive uigenkaldelige.", "title": "Titel", "title-required": "Titel er påkrævet.", "description": "Beskrivelse", - "details": "Oplysninger", - "events": "Begivenheder", - "copyId": "Kopiér lejer-id", - "idCopiedMessage": "Lejer-id er blevet kopieret til udklipsholder", + "details": "Detaljer", + "events": "Hændelser", + "copyId": "Kopiér lejer-ID", + "idCopiedMessage": "Lejer-ID er kopieret til udklipsholder", "select-tenant": "Vælg lejer", - "no-tenants-matching": "", + "no-tenants-matching": "Ingen lejere matcher '{{entity}}'.", "tenant-required": "Lejer er påkrævet", - "selected-tenants": "", - "search": "Søg efter lejere", - "allow-white-labeling": "Tillad hvid mærkning", - "allow-customer-white-labeling": "Tillad hvid mærkning af kunder", - "isolated-tb-core": "Behandling i isoleret ThingsBoard Core-beholder", - "isolated-tb-rule-engine": "Behandling i isoleret ThingsBoard regelmotor-beholder", - "isolated-tb-core-details": "Kræver separat(e) microserviceydelse(r) pr. isoleret lejer", - "isolated-tb-rule-engine-details": "Kræver separat(e) microserviceydelse(r) pr. isoleret lejer" + "search": "Søg lejere", + "selected-tenants": "{ count, plural, =1 {1 lejer} other {# lejere} } valgt", + "isolated-tb-rule-engine": "Brug isolerede ThingsBoard Rule Engine-køer", + "isolated-tb-rule-engine-details": "Hver lejer vil have dedikerede Rule Engine-køer" }, "tenant-profile": { "tenant-profile": "Lejerprofil", "tenant-profiles": "Lejerprofiler", "add": "Tilføj lejerprofil", + "add-profile": "Tilføj profil", + "debug": "Fejlfinding", "edit": "Rediger lejerprofil", - "tenant-profile-details": "Oplysninger om lejerprofil", + "tenant-profile-details": "Detaljer om lejerprofil", "no-tenant-profiles-text": "Ingen lejerprofiler fundet", - "search": "Søg efter lejerprofiler", - "selected-tenant-profiles": "", - "no-tenant-profiles-matching": "", + "name-max-length": "Navnet skal være under 256 tegn", + "search": "Søg lejerprofiler", + "selected-tenant-profiles": "{ count, plural, =1 {1 lejerprofil} other {# lejerprofiler} } valgt", + "no-tenant-profiles-matching": "Ingen lejerprofil matcher '{{entity}}'.", "tenant-profile-required": "Lejerprofil er påkrævet", - "idCopiedMessage": "Lejerprofil-id er blevet kopieret til udklipsholder", + "idCopiedMessage": "Lejerprofil-ID er kopieret til udklipsholder", "set-default": "Gør lejerprofil til standard", "delete": "Slet lejerprofil", - "copyId": "Kopiér lejerprofil-id", + "copyId": "Kopiér lejerprofil-ID", "name": "Navn", "name-required": "Navn er påkrævet.", "data": "Profildata", "profile-configuration": "Profilkonfiguration", "description": "Beskrivelse", "default": "Standard", - "delete-tenant-profile-title": "", - "delete-tenant-profile-text": "Vær forsigtig. Efter bekræftelsen vil lejerprofilen og alle relaterede data være uoprettelige.", - "delete-tenant-profiles-title": "", - "delete-tenant-profiles-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte lejerprofiler blive fjernet, og alle relaterede data vil være uoprettelige.", - "set-default-tenant-profile-title": "", - "set-default-tenant-profile-text": "Efter bekræftelsen vil lejerprofilen blive markeret som standard og vil blive brugt til nye lejere, hvor der ikke er angivet nogen profil.", + "delete-tenant-profile-title": "Er du sikker på, at du vil slette lejerprofilen '{{tenantProfileName}}'?", + "delete-tenant-profile-text": "Vær forsigtig, efter bekræftelsen vil lejerprofilen og alle relaterede data blive uigenkaldelige.", + "delete-tenant-profiles-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 lejerprofil} other {# lejerprofiler} }?", + "delete-tenant-profiles-text": "Vær forsigtig, efter bekræftelsen vil alle valgte lejerprofiler og relaterede data blive uigenkaldelige.", + "set-default-tenant-profile-title": "Er du sikker på, at du vil gøre lejerprofilen '{{tenantProfileName}}' til standard?", + "set-default-tenant-profile-text": "Efter bekræftelsen vil lejerprofilen blive markeret som standard og bruges til nye lejere uden specifik profil.", "no-tenant-profiles-found": "Ingen lejerprofiler fundet.", "create-new-tenant-profile": "Opret en ny!", - "maximum-devices": "Maks. antal enheder (0 – ubegrænset)", + "create-tenant-profile": "Opret ny lejerprofil", + "import": "Importer lejerprofil", + "export": "Eksporter lejerprofil", + "export-failed-error": "Kan ikke eksportere lejerprofil: {{error}}", + "tenant-profile-file": "Lejerprofilfil", + "invalid-tenant-profile-file-error": "Kan ikke importere lejerprofil: Ugyldig datastruktur.", + "advanced-settings": "Avancerede indstillinger", + "entities": "Entiteter", + "rule-engine": "Rule Engine", + "time-to-live": "Levetid", + "calculated-fields": "Beregnet felter", + "alarms-and-notifications": "Alarmer og notifikationer", + "ota-files-in-bytes": "Filer", + "ws-title": "WS", + "unlimited": "(0 - ubegrænset)", + "maximum-devices": "Maks. antal enheder", "maximum-devices-required": "Maks. antal enheder er påkrævet.", - "maximum-devices-range": "Min. antal enheder kan ikke være negativt", - "maximum-assets": "Maks. antal aktiver (0 – ubegrænset)", + "maximum-devices-range": "Maks. antal enheder må ikke være negativt", + "maximum-assets": "Maks. antal aktiver", "maximum-assets-required": "Maks. antal aktiver er påkrævet.", - "maximum-assets-range": "Maks. antal aktiver kan ikke være negativt", - "maximum-customers": "Maks. antal kunder (0 – ubegrænset)", + "maximum-assets-range": "Maks. antal aktiver må ikke være negativt", + "maximum-customers": "Maks. antal kunder", "maximum-customers-required": "Maks. antal kunder er påkrævet.", - "maximum-customers-range": "Maks. antal kunder kan ikke være negativt", - "maximum-users": "Maks. antal brugere (0 – ubegrænset)", + "maximum-customers-range": "Maks. antal kunder må ikke være negativt", + "maximum-users": "Maks. antal brugere", "maximum-users-required": "Maks. antal brugere er påkrævet.", - "maximum-users-range": "Maks. antal brugere kan ikke være negativt", - "maximum-dashboards": "Maks. antal dashboards (0 – ubegrænset)", + "maximum-users-range": "Maks. antal brugere må ikke være negativt", + "maximum-dashboards": "Maks. antal dashboards", "maximum-dashboards-required": "Maks. antal dashboards er påkrævet.", - "maximum-dashboards-range": "Maks. antal dashboards kan ikke være negativt", - "maximum-edges": "Maks. antal edges (0 – ubegrænset)", - "maximum-edges-required": "Maks. antal edges er påkrævet.", - "maximum-edges-range": "Maks. antal edges kan ikke være negativt", - "maximum-rule-chains": "Maks. antal regelkæder (0 – ubegrænset)", + "maximum-dashboards-range": "Maks. antal dashboards må ikke være negativt", + "maximum-edges": "Maks. antal edge-enheder", + "maximum-edges-required": "Maks. antal edge-enheder er påkrævet.", + "maximum-edges-range": "Maks. antal edge-enheder må ikke være negativt", + "maximum-rule-chains": "Maks. antal regelkæder", "maximum-rule-chains-required": "Maks. antal regelkæder er påkrævet.", - "maximum-rule-chains-range": "Maks. antal regelkæder kan ikke være negativt", - "maximum-integrations": "Maks. antal integrationer (0 – ubegrænset)", - "maximum-integrations-required": "Maks. antal integrationer er påkrævet.", - "maximum-integrations-range": "Maks. antal integrationer kan ikke være negativt", - "maximum-converters": "Maks. antal omformere (0 – ubegrænset)", - "maximum-converters-required": "Maks. antal omformere er påkrævet.", - "maximum-converters-range": "Maks. antal omformere kan ikke være negativt", - "maximum-scheduler-events": "Maks. antal planlægningsbegivenheder (0 – ubegrænset)", - "maximum-scheduler-events-required": "Maks. antal planlægningsbegivenheder er påkrævet.", - "maximum-scheduler-events-range": "Maks. antal planlægningsbegivenheder kan ikke være negativt", - "transport-tenant-telemetry-msg-rate-limit": "Hastighedsgrænse for transport af lejertelemetrimeddelelser.", - "transport-tenant-telemetry-data-points-rate-limit": "Hastighedsgrænse for transport af lejertelemetridatapunkter.", - "transport-device-msg-rate-limit": "Hastighedsgrænse for transport af enhedsmeddelelser.", - "transport-device-telemetry-msg-rate-limit": "Hastighedsgrænse for transport af enhedstelemetrimeddelelser.", - "transport-device-telemetry-data-points-rate-limit": "Hastighedsgrænse for transport af enhedstelemetridatapunkter.", - "max-transport-messages": "Maks. antal transportmeddelelser (0 – ubegrænset)", - "max-transport-messages-required": "Maks. antal transportmeddelelser er påkrævet.", - "max-transport-messages-range": "Maks. antal transportmeddelelser kan ikke være negativt", - "max-transport-data-points": "Maks. antal transportdatapunkter (0 – ubegrænset)", - "max-transport-data-points-required": "Maks. antal transportdatapunkter er påkrævet.", - "max-transport-data-points-range": "Maks. antal transportdatapunkter kan ikke være negativt", - "max-r-e-executions": "Maks. antal regelmotor-udførelser (0 – ubegrænset)", - "max-r-e-executions-required": "Maks. antal regelmotor-udførelser er påkrævet.", - "max-r-e-executions-range": "Maks. antal regelmotor-udførelser kan ikke være negativt", - "max-j-s-executions": "Maks. antal JavaScript-udførelser (0 – ubegrænset)", - "max-j-s-executions-required": "Maks. antal JavaScript-udførelser er påkrævet.", - "max-j-s-executions-range": "Maks. antal JavaScript-udførelser kan ikke være negativt", - "max-d-p-storage-days": "Maks. antal lagringsdage for datapunkter (0 – ubegrænset)", - "max-d-p-storage-days-required": "Maks. antal lagringsdage for datapunkter er påkrævet.", - "max-d-p-storage-days-range": "Maks. antal lagringsdage for datapunkter kan ikke være negativt", - "default-storage-ttl-days": "Standard lagringsdage for TTL (0 – ubegrænset)", - "default-storage-ttl-days-required": "Standard lagringsdage for TTL er påkrævet.", - "default-storage-ttl-days-range": "Standard lagringsdage for TTL kan ikke være negative", - "max-rule-node-executions-per-message": "Maks. antal regelknudeudførelser pr. meddelelse (0 – ubegrænset)", - "max-rule-node-executions-per-message-required": "Maks. antal regelknudeudførelser pr. meddelelse er påkrævet.", - "max-rule-node-executions-per-message-range": "Maks. antal regelknudeudførelser pr. meddelelse kan ikke være negativt", - "max-emails": "Maks. antal sendte e-mails (0 – ubegrænset)", - "max-emails-required": "Maks. antal sendte e-mails er påkrævet.", - "max-emails-range": "Maks. antal sendte e-mails kan ikke være negativt", - "max-sms": "Maks. antal sendte SMS'er (0 – ubegrænset)", - "max-sms-required": "Maks. antal sendte SMS'er er påkrævet.", - "max-sms-range": "Maks. antal sendte SMS'er kan ikke være negativt" + "maximum-rule-chains-range": "Maks. antal regelkæder må ikke være negativt", + "maximum-resources-sum-data-size": "Maks. samlet størrelse af ressourcefiler (bytes)", + "maximum-resources-sum-data-size-required": "Maks. samlet størrelse af ressourcefiler er påkrævet.", + "maximum-resources-sum-data-size-range": "Maks. samlet størrelse af ressourcefiler må ikke være negativt", + "maximum-resource-size": "Maks. størrelse på ressourcefil (bytes)", + "maximum-resource-size-required": "Maks. størrelse på ressourcefil er påkrævet", + "maximum-resource-size-range": "Maks. størrelse på ressourcefil må ikke være negativt", + "maximum-ota-packages-sum-data-size": "Maks. samlet størrelse af OTA-pakker (bytes)", + "maximum-ota-package-sum-data-size-required": "Maks. samlet størrelse af OTA-pakker er påkrævet.", + "maximum-ota-package-sum-data-size-range": "Maks. samlet størrelse af OTA-pakker må ikke være negativt", + "maximum-debug-duration-min": "Maks. fejlfindingstid (minutter)", + "maximum-debug-duration-min-range": "Maks. fejlfindingstid må ikke være negativ", + "rest-requests-for-tenant": "REST-forespørgsler for lejer", + "transport-tenant-telemetry-msg-rate-limit": "Transportlejers telemetribeskeder", + "transport-tenant-telemetry-data-points-rate-limit": "Transportlejers telemetridatapunkter", + "transport-device-msg-rate-limit": "Transportenhedsbeskeder", + "transport-device-telemetry-msg-rate-limit": "Transportenheds telemetribeskeder", + "transport-device-telemetry-data-points-rate-limit": "Transportenheds telemetridatapunkter", + "transport-gateway-msg-rate-limit": "Transport gateway-beskeder", + "transport-gateway-telemetry-msg-rate-limit": "Transport gateway telemetribeskeder", + "transport-gateway-telemetry-data-points-rate-limit": "Transport gateway telemetridatapunkter", + "transport-gateway-device-msg-rate-limit": "Transport gateway-enhedsbeskeder", + "transport-gateway-device-telemetry-msg-rate-limit": "Transport gateway-enheds telemetribeskeder", + "transport-gateway-device-telemetry-data-points-rate-limit": "Transport gateway-enheds telemetridatapunkter", + "tenant-entity-export-rate-limit": "Versionering af entiteter", + "tenant-entity-import-rate-limit": "Indlæsning af entitetsversion", + "tenant-notification-request-rate-limit": "Notifikationsanmodninger", + "tenant-notification-requests-per-rule-rate-limit": "Notifikationsanmodninger pr. regel", + "max-calculated-fields": "Maksimalt antal beregnede felter pr. enhed", + "max-calculated-fields-range": "Maksimalt antal beregnede felter pr. enhed kan ikke være negativt", + "max-calculated-fields-required": "Maksimalt antal beregnede felter pr. enhed er påkrævet", + "max-data-points-per-rolling-arg": "Maksimalt antal datapunkter i rullende argumenter", + "max-data-points-per-rolling-arg-range": "Maksimalt antal datapunkter i rullende argumenter kan ikke være negativt", + "max-data-points-per-rolling-arg-required": "Maksimalt antal datapunkter i rullende argumenter er påkrævet", + "max-arguments-per-cf": "Maksimalt antal argumenter pr. beregnet felt", + "max-arguments-per-cf-range": "Maksimalt antal argumenter pr. beregnet felt kan ikke være negativt", + "max-arguments-per-cf-required": "Maksimalt antal argumenter pr. beregnet felt er påkrævet", + "max-state-size": "Maksimal tilstandsstørrelse i KB", + "max-state-size-range": "Maksimal tilstandsstørrelse i KB kan ikke være negativ", + "max-state-size-required": "Maksimal tilstandsstørrelse i KB er påkrævet", + "max-value-argument-size": "Maksimal størrelse af enkeltværdien argument i KB", + "max-value-argument-size-range": "Maksimal størrelse af enkeltværdien argument i KB kan ikke være negativ", + "max-value-argument-size-required": "Maksimal størrelse af enkeltværdien argument i KB er påkrævet", + "max-transport-messages": "Maksimalt antal transportbeskeder", + "max-transport-messages-required": "Maksimalt antal transportbeskeder er påkrævet.", + "max-transport-messages-range": "Maksimalt antal transportbeskeder må ikke være negativt", + "max-transport-data-points": "Maksimalt antal transport datapunkter", + "max-transport-data-points-required": "Maksimalt antal transport datapunkter er påkrævet.", + "max-transport-data-points-range": "Maksimalt antal transport datapunkter må ikke være negativt", + "max-r-e-executions": "Maksimalt antal Rule Engine-udførelser", + "max-r-e-executions-required": "Maksimalt antal Rule Engine-udførelser er påkrævet.", + "max-r-e-executions-range": "Maksimalt antal Rule Engine-udførelser må ikke være negativt", + "max-j-s-executions": "Maksimalt antal JavaScript-udførelser", + "max-j-s-executions-required": "Maksimalt antal JavaScript-udførelser er påkrævet.", + "max-j-s-executions-range": "Maksimalt antal JavaScript-udførelser må ikke være negativt", + "max-tbel-executions": "Maksimalt antal TBEL-udførelser", + "max-tbel-executions-required": "Maksimalt antal TBEL-udførelser er påkrævet.", + "max-tbel-executions-range": "Maksimalt antal TBEL-udførelser må ikke være negativt", + "max-d-p-storage-days": "Maksimale lagringsdage for datapunkter", + "max-d-p-storage-days-required": "Maksimale lagringsdage for datapunkter er påkrævet.", + "max-d-p-storage-days-range": "Maksimale lagringsdage for datapunkter må ikke være negativt", + "default-storage-ttl-days": "Standard lagring TTL dage", + "default-storage-ttl-days-required": "Standard lagring TTL dage er påkrævet.", + "default-storage-ttl-days-range": "Standard lagring TTL dage må ikke være negativt", + "alarms-ttl-days": "Alarmers TTL dage", + "alarms-ttl-days-required": "Alarmers TTL dage er påkrævet", + "alarms-ttl-days-days-range": "Alarmers TTL dage må ikke være negativt", + "rpc-ttl-days": "RPC TTL dage", + "rpc-ttl-days-required": "RPC TTL dage er påkrævet", + "rpc-ttl-days-days-range": "RPC TTL dage må ikke være negativt", + "queue-stats-ttl-days": "Køstatistik TTL dage", + "queue-stats-ttl-days-required": "Køstatistik TTL dage er påkrævet", + "queue-stats-ttl-days-range": "Køstatistik TTL dage må ikke være negativt", + "rule-engine-exceptions-ttl-days": "Rule Engine-undtagelser TTL dage", + "rule-engine-exceptions-ttl-days-required": "Rule Engine-undtagelser TTL dage er påkrævet", + "rule-engine-exceptions-ttl-days-range": "Rule Engine-undtagelser TTL dage må ikke være negativt", + "max-rule-node-executions-per-message": "Maksimalt antal regelnode-udførelser pr. besked", + "max-rule-node-executions-per-message-required": "Maksimalt antal regelnode-udførelser pr. besked er påkrævet.", + "max-rule-node-executions-per-message-range": "Maksimalt antal regelnode-udførelser pr. besked må ikke være negativt", + "max-emails": "Maksimalt antal sendte e-mails", + "max-emails-required": "Maksimalt antal sendte e-mails er påkrævet.", + "max-emails-range": "Maksimalt antal sendte e-mails må ikke være negativt", + "sms-enabled": "SMS aktiveret", + "max-sms": "Maksimalt antal sendte SMS'er", + "max-sms-required": "Maksimalt antal sendte SMS'er er påkrævet.", + "max-sms-range": "Maksimalt antal sendte SMS'er må ikke være negativt", + "max-created-alarms": "Maksimalt antal oprettede alarmer", + "max-created-alarms-required": "Maksimalt antal oprettede alarmer er påkrævet.", + "max-created-alarms-range": "Maksimalt antal oprettede alarmer må ikke være negativt", + "no-queue": "Ingen kø konfigureret", + "add-queue": "Tilføj kø", + "queues-with-count": "Køer ({{count}})", + "tenant-rest-limits": "REST-anmodninger for lejer", + "customer-rest-limits": "REST-anmodninger for kunde", + "incorrect-pattern-for-rate-limits": "Formatet er kommaseparerede par af kapacitet og periode (i sekunder) adskilt af kolon, f.eks. 100:1,2000:60", + "too-small-value-zero": "Værdien skal være større end 0", + "too-small-value-one": "Værdien skal være større end 1", + "queue-size-is-limited-by-system-configuration": "Køens størrelse er også begrænset af systemkonfigurationen.", + "cassandra-tenant-limits-configuration": "Cassandra-forespørgsel for lejer", + "ws-limit-max-sessions-per-tenant": "Maksimalt antal sessioner pr. lejer", + "ws-limit-max-sessions-per-customer": "Maksimalt antal sessioner pr. kunde", + "ws-limit-max-sessions-per-regular-user": "Maksimalt antal sessioner pr. almindelig bruger", + "ws-limit-max-sessions-per-public-user": "Maksimalt antal sessioner pr. offentlig bruger", + "ws-limit-queue-per-session": "Maksimal køstørrelse pr. session", + "ws-limit-max-subscriptions-per-tenant": "Maksimalt antal abonnementer pr. lejer", + "ws-limit-max-subscriptions-per-customer": "Maksimalt antal abonnementer pr. kunde", + "ws-limit-max-subscriptions-per-regular-user": "Maksimalt antal abonnementer pr. almindelig bruger", + "ws-limit-max-subscriptions-per-public-user": "Maksimalt antal abonnementer pr. offentlig bruger", + "ws-limit-updates-per-session": "WS-opdateringer pr. session", + "rate-limits": { + "add-limit": "Tilføj begrænsning", + "advanced-settings": "Avancerede indstillinger", + "edit-limit": "Rediger begrænsning", + "but-less-than": "men mindre end", + "calculated-field-debug-event-rate-limit": "Fejlfindingshændelser for beregnede felter", + "edit-calculated-field-debug-event-rate-limit": "Rediger hastighedsgrænser for fejlfindingshændelser for beregnede felter", + "edit-transport-tenant-msg-title": "Rediger grænse for transportlejers beskeder", + "edit-transport-tenant-telemetry-msg-title": "Rediger grænse for transportlejers telemetribeskeder", + "edit-transport-tenant-telemetry-data-points-title": "Rediger grænse for transportlejers datapunkter", + "edit-transport-device-msg-title": "Rediger grænse for transportenheds beskeder", + "edit-transport-device-telemetry-msg-title": "Rediger grænse for transportenheds telemetribeskeder", + "edit-transport-device-telemetry-data-points-title": "Rediger grænse for transportenheds datapunkter", + "edit-transport-gateway-msg-title": "Rediger grænse for gateway-beskeder", + "edit-transport-gateway-telemetry-msg-title": "Rediger grænse for gateway telemetribeskeder", + "edit-transport-gateway-telemetry-data-points-title": "Rediger grænse for gateway datapunkter", + "edit-transport-gateway-device-msg-title": "Rediger grænse for gateway-enheds beskeder", + "edit-transport-gateway-device-telemetry-msg-title": "Rediger grænse for gateway-enheds telemetribeskeder", + "edit-transport-gateway-device-telemetry-data-points-title": "Rediger grænse for gateway-enheds datapunkter", + "edit-tenant-rest-limits-title": "Rediger REST-anmodninger for lejer", + "edit-customer-rest-limits-title": "Rediger REST-anmodninger for kunde", + "edit-ws-limit-updates-per-session-title": "Rediger grænse for WS-opdateringer pr. session", + "edit-cassandra-tenant-limits-configuration-title": "Rediger Cassandra-forespørgselsgrænse for lejer", + "edit-tenant-entity-export-rate-limit-title": "Rediger grænse for versionering af entitet", + "edit-tenant-entity-import-rate-limit-title": "Rediger grænse for indlæsning af entitet", + "edit-tenant-notification-request-rate-limit-title": "Rediger grænse for notifikationsanmodninger", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "Rediger grænse for notifikationsanmodninger pr. regel", + "edit-edge-events-rate-limit": "Rediger grænse for edge-hændelser", + "edit-edge-events-per-edge-rate-limit": "Rediger grænse for edge-hændelser pr. edge", + "edge-events-rate-limit": "Edge-hændelser", + "edge-events-per-edge-rate-limit": "Edge-hændelser pr. edge", + "edit-edge-uplink-messages-rate-limit": "Rediger grænse for edge uplink-beskeder", + "edit-edge-uplink-messages-per-edge-rate-limit": "Rediger grænse for edge uplink-beskeder pr. edge", + "edge-uplink-messages-rate-limit": "Edge uplink-beskeder", + "edge-uplink-messages-per-edge-rate-limit": "Edge uplink-beskeder pr. edge", + "messages-per": "beskeder pr.", + "not-set": "Ikke angivet", + "number-of-messages": "Antal beskeder", + "number-of-messages-required": "Antal beskeder er påkrævet.", + "number-of-messages-min": "Minimumsværdi er 1.", + "preview": "Forhåndsvis", + "per-seconds": "Pr. sekunder", + "per-seconds-required": "Tidsrate er påkrævet.", + "per-seconds-min": "Minimumsværdi er 1.", + "rate-limits": "Grænser", + "remove-limit": "Fjern begrænsning", + "transport-tenant-msg": "Transportlejers beskeder", + "transport-tenant-telemetry-msg": "Transportlejers telemetribeskeder", + "transport-tenant-telemetry-data-points": "Transportlejers telemetridatapunkter", + "transport-device-msg": "Transportenheds beskeder", + "transport-device-telemetry-msg": "Transportenheds telemetribeskeder", + "transport-device-telemetry-data-points": "Transportenheds telemetridatapunkter", + "transport-gateway-msg": "Gateway-beskeder", + "transport-gateway-telemetry-msg": "Gateway telemetribeskeder", + "transport-gateway-telemetry-data-points": "Gateway telemetridatapunkter", + "transport-gateway-device-msg": "Gateway-enheds beskeder", + "transport-gateway-device-telemetry-msg": "Gateway-enheds telemetribeskeder", + "transport-gateway-device-telemetry-data-points": "Gateway-enheds telemetridatapunkter", + "sec": "sek" + } }, "timeinterval": { "seconds-interval": "{ seconds, plural, =1 {1 sekund} other {# sekunder} }", @@ -3093,58 +5706,534 @@ "hours": "Timer", "minutes": "Minutter", "seconds": "Sekunder", - "advanced": "Fremskreden", + "advanced": "Avanceret", + "custom": "Brugerdefineret", "predefined": { "yesterday": "I går", - "day-before-yesterday": "Dagen før i går", + "day-before-yesterday": "I forgårs", "this-day-last-week": "Denne dag i sidste uge", - "previous-week": "Forrige uge (søn – lør)", - "previous-week-iso": "Forrige uge (man – søn)", + "previous-week": "Forrige uge (søn - lør)", + "previous-week-iso": "Forrige uge (man - søn)", "previous-month": "Forrige måned", + "previous-quarter": "Forrige kvartal", + "previous-half-year": "Forrige halvår", "previous-year": "Forrige år", - "current-hour": "Aktuel time", - "current-day": "Aktuel dag", - "current-day-so-far": "Aktuel dag indtil nu", - "current-week": "Aktuel uge (søn – lør)", - "current-week-iso": "Aktuel uge (man – søn)", - "current-week-so-far": "Aktuel uge indtil videre (søn – lør)", - "current-week-iso-so-far": "Aktuel uge indtil videre (man– søn)", - "current-month": "Aktuel måned", - "current-month-so-far": "Aktuel måned indtil nu", - "current-year": "Aktuelt år", - "current-year-so-far": "Aktuelt år indtil nu" + "current-hour": "Nuværende time", + "current-day": "Nuværende dag", + "current-day-so-far": "Nuværende dag indtil nu", + "current-week": "Nuværende uge (søn - lør)", + "current-week-iso": "Nuværende uge (man - søn)", + "current-week-so-far": "Nuværende uge indtil nu (søn - lør)", + "current-week-iso-so-far": "Nuværende uge indtil nu (man - søn)", + "current-month": "Nuværende måned", + "current-month-so-far": "Nuværende måned indtil nu", + "current-quarter": "Nuværende kvartal", + "current-quarter-so-far": "Nuværende kvartal indtil nu", + "current-half-year": "Nuværende halvår", + "current-half-year-so-far": "Nuværende halvår indtil nu", + "current-year": "Nuværende år", + "current-year-so-far": "Nuværende år indtil nu" + }, + "type": { + "week": "Uge (søn - lør)", + "week-iso": "Uge (man - søn)", + "month": "Måned", + "quarter": "Kvartal" } }, "timeunit": { + "milliseconds": "Millisekunder", "seconds": "Sekunder", "minutes": "Minutter", "hours": "Timer", "days": "Dage" }, "timewindow": { + "timewindow": "Tidsvindue", + "timewindow-settings": "Indstillinger for tidsvindue", + "years": "{ years, plural, =1 { år } other {# år } }", + "years-short": "{{ years }}å", + "months": "{ months, plural, =1 { måned } other {# måneder } }", + "months-short": "{{ months }}M", + "weeks": "{ weeks, plural, =1 { uge } other {# uger } }", + "weeks-short": "{{ weeks }}u", "days": "{ days, plural, =1 { dag } other {# dage } }", + "days-short": "{{ days }}d", "hours": "{ hours, plural, =0 { time } =1 {1 time } other {# timer } }", + "hr": "{{ hr }} t", + "hr-short": "{{ hr }}t", "minutes": "{ minutes, plural, =0 { minut } =1 {1 minut } other {# minutter } }", + "min": "{{ min }} min", + "min-short": "{{ min }}m", "seconds": "{ seconds, plural, =0 { sekund } =1 {1 sekund } other {# sekunder } }", - "realtime": "Realtid", - "history": "Historie", - "last-prefix": "sidst", + "sec": "{{ sec }} sek", + "sec-short": "{{ sec }}s", + "short": { + "years": "{ years, plural, =1 {1 år } other {# år } }", + "days": "{ days, plural, =1 {1 dag } other {# dage } }", + "hours": "{ hours, plural, =1 {1 time } other {# timer } }", + "minutes": "{{minutes}} min ", + "seconds": "{{seconds}} sek " + }, + "realtime": "Realtime", + "history": "Historik", + "last-prefix": "sidste", "period": "fra {{ startTime }} til {{ endTime }}", "edit": "Rediger tidsvindue", "date-range": "Datointerval", - "last": "Sidst", + "for-all-time": "For hele perioden", + "last": "Sidste", "time-period": "Tidsperiode", "hide": "Skjul", - "interval": "Interval" + "interval": "Interval", + "just-now": "Lige nu", + "just-now-lower": "lige nu", + "ago": "siden", + "style": "Tidsvinduets stil", + "icon": "Ikon", + "icon-position": "Ikonposition", + "icon-position-left": "Venstre", + "icon-position-right": "Højre", + "font": "Skrifttype", + "color": "Farve", + "displayTypePrefix": "Vis Realtime/Historik præfiks", + "preview": "Forhåndsvisning", + "relative": "Relativ", + "range": "Interval", + "hide-timewindow-section": "Skjul tidsvinduessektion for slutbrugere", + "hide-last-interval": "Skjul sidste interval for slutbrugere", + "hide-relative-interval": "Skjul relativt interval for slutbrugere", + "hide-fixed-interval": "Skjul fast interval for slutbrugere", + "hide-aggregation": "Skjul aggregering for slutbrugere", + "hide-group-interval": "Skjul grupperingsinterval for slutbrugere", + "hide-max-values": "Skjul maksimale værdier for slutbrugere", + "hide-timezone": "Skjul tidszone for slutbrugere", + "disable-custom-interval": "Deaktiver brugerdefineret intervalvalg", + "edit-aggregation-functions-list": "Rediger liste over aggregeringsfunktioner", + "edit-aggregation-functions-list-hint": "Liste over tilgængelige muligheder kan specificeres.", + "allowed-aggregation-functions": "Tilladte aggregeringsfunktioner", + "edit-intervals-list": "Rediger intervalliste", + "allowed-agg-intervals": "Grupperingsintervaller", + "default-agg-interval": "Standard grupperingsinterval", + "edit-intervals-list-hint": "Liste over tilgængelige intervalmuligheder kan specificeres.", + "edit-grouping-intervals-list-hint": "Det er muligt at konfigurere listen over grupperingsintervaller og standard grupperingsinterval.", + "all": "Alle" + }, + "tooltip": { + "trigger": "Udløser", + "trigger-point": "Punkt", + "trigger-axis": "Akse", + "label": "Etiket", + "value": "Værdi", + "date": "Dato", + "show-date-time-interval": "Vis dato og tidsinterval", + "show-date-time-interval-hint": "Vis dato og tidsinterval i henhold til dataaggregeringen.", + "background-color": "Baggrundsfarve", + "background-blur": "Baggrundssløring" + }, + "unit": { + "millimeter": "Millimeter", + "centimeter": "Centimeter", + "angstrom": "Ångström", + "nanometer": "Nanometer", + "micrometer": "Mikrometer", + "meter": "Meter", + "kilometer": "Kilometer", + "inch": "Tommer", + "foot": "Fod", + "yard": "Yard", + "mile": "Mil", + "nautical-mile": "Sømil", + "astronomical-unit": "Astronomisk enhed", + "reciprocal-metre": "Reciprok meter", + "meter-per-meter": "Meter pr. meter", + "steradian": "Steradian", + "thou": "Tusindedel tomme", + "barleycorn": "Bygkorn", + "hand": "Hånd", + "chain": "Kæde", + "furlong": "Furlong", + "league": "League", + "fathom": "Favn", + "cable": "Kabellængde", + "link": "Led", + "rod": "Stang", + "nanogram": "Nanogram", + "microgram": "Mikrogram", + "milligram": "Milligram", + "gram": "Gram", + "kilogram": "Kilogram", + "tonne": "Tonne", + "ounce": "Ounce", + "pound": "Pund", + "stone": "Stone", + "hundredweight-count": "Hundredweight", + "short-tons": "Amerikanske tons", + "dalton": "Dalton", + "grain": "Gran", + "drachm": "Drachm", + "quarter": "Quarter", + "slug": "Slug", + "carat": "Karat", + "cubic-millimeter": "Kubikmillimeter", + "cubic-centimeter": "Kubikcentimeter", + "cubic-meter": "Kubikmeter", + "cubic-kilometer": "Kubikkilometer", + "microliter": "Mikroliter", + "milliliter": "Milliliter", + "liter": "Liter", + "hectoliter": "Hektoliter", + "cubic-inch": "Kubiktomme", + "cubic-foot": "Kubikfod", + "cubic-yard": "Kubikyard", + "fluid-ounce": "Fluid ounce", + "pint": "Pint", + "quart": "Quart", + "gallon": "Gallon", + "oil-barrels": "Olie tønde", + "cubic-meter-per-kilogram": "Kubikmeter pr. kilogram", + "gill": "Gill", + "hogshead": "Tønde (hogshead)", + "teaspoon": "Teske", + "tablespoon": "Spiseske", + "cup": "Kop", + "celsius": "Celsius", + "kelvin": "Kelvin", + "rankine": "Rankine", + "fahrenheit": "Fahrenheit", + "percent": "Procent", + "meter-per-second": "Meter pr. sekund", + "kilometer-per-hour": "Kilometer i timen", + "foot-per-second": "Fod pr. sekund", + "mile-per-hour": "Mil i timen", + "knot": "Knob", + "millimeters-per-minute": "Millimeter pr. minut", + "kilometer-per-hour-squared": "Kilometer i timen i anden", + "foot-per-second-squared": "Fod pr. sekund i anden", + "pascal": "Pascal", + "kilopascal": "Kilopascal", + "megapascal": "Megapascal", + "gigapascal": "Gigapascal", + "millibar": "Millibar", + "bar": "Bar", + "kilobar": "Kilobar", + "newton": "Newton", + "newton-meter": "Newtonmeter", + "foot-pounds": "Fodpund", + "inch-pounds": "Tommer-pund", + "newton-per-meter": "Newton pr. meter", + "atmospheres": "Atmosfærer", + "pounds-per-square-inch": "Pund pr. kvadrattomme", + "torr": "Torr", + "inches-of-mercury": "Tommer kviksølv", + "pascal-per-square-meter": "Pascal pr. kvadratmeter", + "pound-per-square-inch": "Pund pr. kvadrattomme", + "newton-per-square-meter": "Newton pr. kvadratmeter", + "kilogram-force-per-square-meter": "Kilogramkraft pr. kvadratmeter", + "pascal-per-square-centimeter": "Pascal pr. kvadratcentimeter", + "ton-force-per-square-inch": "Ton-kraft pr. kvadrattomme", + "kilonewton-per-square-meter": "Kilonewton pr. kvadratmeter", + "newton-per-square-millimeter": "Newton pr. kvadratmillimeter", + "microjoule": "Mikrojoule", + "millijoule": "Millijoule", + "joule": "Joule", + "kilojoule": "Kilojoule", + "megajoule": "Megajoule", + "gigajoule": "Gigajoule", + "watt-hour": "Watt-time", + "kilowatt-hour": "Kilowatt-time", + "electron-volts": "Elektronvolt", + "joules-per-coulomb": "Joule pr. coulomb", + "british-thermal-unit": "Britiske termiske enheder", + "foot-pound": "Fodpund", + "calorie": "Kalorie", + "small-calorie": "Lille kalorie", + "kilocalorie": "Kilokalorie", + "joule-per-kelvin": "Joule pr. kelvin", + "joule-per-kilogram-kelvin": "Joule pr. kilogram-kelvin", + "joule-per-kilogram": "Joule pr. kilogram", + "watt-per-meter-kelvin": "Watt pr. meter-kelvin", + "joule-per-cubic-meter": "Joule pr. kubikmeter", + "therm": "Therm", + "electric-dipole-moment": "Elektrisk dipolmoment", + "magnetic-dipole-moment": "Magnetisk dipolmoment", + "debye": "Debye", + "coulomb-per-square-meter-per-volt": "Coulomb pr. kvadratmeter pr. volt", + "milliwatt": "Milliwatt", + "microwatt": "Mikrowatt", + "watt": "Watt", + "kilowatt": "Kilowatt", + "megawatt": "Megawatt", + "gigawatt": "Gigawatt", + "metric-horsepower": "Metrisk hestekraft", + "milliwatt-per-square-centimeter": "Milliwatt pr. kvadratcentimeter", + "watt-per-square-centimeter": "Watt pr. kvadratcentimeter", + "kilowatt-per-square-centimeter": "Kilowatt pr. kvadratcentimeter", + "milliwatt-per-square-meter": "Milliwatt pr. kvadratmeter", + "watt-per-square-meter": "Watt pr. kvadratmeter", + "kilowatt-per-square-meter": "Kilowatt pr. kvadratmeter", + "watt-per-square-inch": "Watt pr. kvadrattomme", + "kilowatt-per-square-inch": "Kilowatt pr. kvadrattomme", + "horsepower": "Hestekræfter", + "btu-per-hour": "Britiske termiske enheder/time", + "coulomb": "Coulomb", + "millicoulomb": "Millicoulomb", + "microcoulomb": "Mikrocoulomb", + "picocoulomb": "Picocoulomb", + "coulomb-per-meter": "Coulomb pr. meter", + "coulomb-per-cubic-meter": "Coulomb pr. kubikmeter", + "coulomb-per-square-meter": "Coulomb pr. kvadratmeter", + "square-millimeter": "Kvadratmillimeter", + "square-centimeter": "Kvadratcentimeter", + "square-meter": "Kvadratmeter", + "hectare": "Hektar", + "square-kilometer": "Kvadratkilometer", + "square-inch": "Kvadrattomme", + "square-foot": "Kvadratfod", + "square-yard": "Kvadratyard", + "acre": "Acre", + "square-mile": "Kvadratmil", + "are": "Ar", + "barn": "Barn", + "circular-inch": "Cirkulær tomme", + "milliampere-hour": "Milliampere-time", + "ampere-hours": "Amperetimer", + "kiloampere-hours": "Kiloamperetimer", + "nanoampere": "Nanoampere", + "picoampere": "Picoampere", + "microampere": "Mikroampere", + "milliampere": "Milliampere", + "ampere": "Ampere", + "microampere-per-square-centimeter": "Mikroampere pr. kvadratcentimeter", + "ampere-per-square-meter": "Ampere pr. kvadratmeter", + "ampere-per-meter": "Ampere pr. meter", + "oersted": "Oersted", + "bohr-magneton": "Bohr magneton", + "ampere-meter-squared": "Ampere-meter kvadreret", + "nanovolt": "Nanovolt", + "picovolt": "Picovolt", + "volt": "Volt", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "Volt-meter", + "kilovolt-meter": "Kilovolt-meter", + "megavolt-meter": "Megavolt-meter", + "microvolt-meter": "Mikrovolt-meter", + "millivolt-meter": "Millivolt-meter", + "nanovolt-meter": "Nanovolt-meter", + "ohm": "Ohm", + "microohm": "Mikroohm", + "milliohm": "Milliohm", + "kilohm": "Kiloohm", + "megohm": "Megaohm", + "gigohm": "Gigaohm", + "hertz": "Hertz", + "kilohertz": "Kilohertz", + "megahertz": "Megahertz", + "gigahertz": "Gigahertz", + "rpm": "Omdrejninger pr. minut", + "candela-per-square-meter": "Candela pr. kvadratmeter", + "candela": "Candela", + "lumen": "Lumen", + "lux": "Lux", + "foot-candle": "Foot-candle", + "lumen-per-square-meter": "Lumen pr. kvadratmeter", + "lux-second": "Lux sekund", + "lumen-second": "Lumen sekund", + "lumens-per-watt": "Lumen pr. watt", + "mole": "Mol", + "nanomole": "Nanomol", + "micromole": "Mikromol", + "millimole": "Millimol", + "kilomole": "Kilomol", + "mole-per-cubic-meter": "Mol pr. kubikmeter", + "rssi": "RSSI", + "ppm": "Dele pr. million", + "ppb": "Dele pr. milliard", + "micrograms-per-cubic-meter": "Mikrogram pr. kubikmeter", + "aqi": "Luftkvalitetsindeks (AQI)", + "gram-per-cubic-meter": "Gram pr. kubikmeter", + "gram-per-kilogram": "Specifik fugtighed", + "millimeters-per-second": "Millimeter pr. sekund", + "neper": "Neper", + "bel": "Bel", + "decibel": "Decibel", + "meters-per-second-squared": "Meter pr. sekund i anden", + "becquerel": "Becquerel", + "curie": "Curie", + "gray": "Gray", + "sievert": "Sievert", + "roentgen": "Röntgen", + "cps": "Tællinger pr. sekund", + "rad": "Rad", + "rem": "Rem", + "dps": "Henfald pr. sekund", + "rutherford": "Rutherford", + "coulombs-per-kilogram": "Coulomb pr. kilogram", + "becquerels-per-cubic-meter": "Becquerel pr. kubikmeter", + "curies-per-liter": "Curie pr. liter", + "becquerels-per-second": "Becquerel pr. sekund", + "curies-per-second": "Curie pr. sekund", + "gy-per-second": "Gray pr. sekund", + "watt-per-steradian": "Watt pr. steradian", + "watt-per-square-metre-steradian": "Watt pr. kvadratmeter-steradian", + "ph-level": "pH-niveau", + "turbidity": "Turbiditet", + "mg-per-liter": "Milligram pr. liter", + "microsiemens-per-centimeter": "Mikrosiemens pr. centimeter", + "millisiemens-per-meter": "Millisiemens pr. meter", + "siemens-per-meter": "Siemens pr. meter", + "kilogram-per-cubic-meter": "Kilogram pr. kubikmeter", + "gram-per-cubic-centimeter": "Gram pr. kubikcentimeter", + "kilogram-per-square-meter": "Kilogram pr. kvadratmeter", + "milligram-per-milliliter": "Milligram pr. milliliter", + "milligram-per-cubic-meter": "Milligram pr. kubikmeter", + "pound-per-cubic-foot": "Pund pr. kubikfod", + "ounces-per-cubic-inch": "Unse pr. kubiktomme", + "tons-per-cubic-yard": "Tons pr. kubikyard", + "particle-density": "Partikeltæthed", + "kilometers-per-liter": "Kilometer pr. liter", + "miles-per-gallon": "Miles pr. gallon", + "liters-per-100-km": "Liter pr. 100 km", + "gallons-per-mile": "Gallons pr. mile", + "liters-per-hour": "Liter pr. time", + "gallons-per-hour": "Gallons pr. time", + "beats-per-minute": "Slag pr. minut", + "millimeters-of-mercury": "Millimeter kviksølv", + "milligrams-per-deciliter": "Milligram pr. deciliter", + "g-force": "G-kraft", + "kilonewton": "Kilonewton", + "kilogram-force": "Kilogram-kraft", + "pound-force": "Pund-kraft", + "kilopound-force": "Kilopund-kraft", + "dyne": "Dyne", + "poundal": "Poundal", + "kip": "Kip", + "gal": "Gal", + "gravity": "Tyngdekraft", + "hectopascal": "Hektopascal", + "atmosphere": "Atmosfære", + "millibars": "Millibar", + "inch-of-mercury": "Tommer kviksølv", + "richter-scale": "Richterskala", + "second": "Sekund", + "minute": "Minut", + "hour": "Time", + "day": "Dag", + "week": "Uge", + "month": "Måned", + "year": "År", + "cubic-foot-per-minute": "Kubikfod pr. minut", + "cubic-meters-per-hour": "Kubikmeter pr. time", + "cubic-meters-per-second": "Kubikmeter pr. sekund", + "liter-per-second": "Liter pr. sekund", + "liter-per-minute": "Liter pr. minut", + "gallons-per-minute": "Galloner pr. minut", + "cubic-foot-per-second": "Kubikfod pr. sekund", + "milliliters-per-minute": "Milliliter pr. minut", + "bit": "Bit", + "byte": "Byte", + "kilobyte": "Kilobyte", + "megabyte": "Megabyte", + "gigabyte": "Gigabyte", + "terabyte": "Terabyte", + "petabyte": "Petabyte", + "exabyte": "Exabyte", + "zettabyte": "Zettabyte", + "yottabyte": "Yottabyte", + "bit-per-second": "Bit pr. sekund", + "kilobit-per-second": "Kilobit pr. sekund", + "megabit-per-second": "Megabit pr. sekund", + "gigabit-per-second": "Gigabit pr. sekund", + "terabit-per-second": "Terabit pr. sekund", + "byte-per-second": "Byte pr. sekund", + "kilobyte-per-second": "Kilobyte pr. sekund", + "megabyte-per-second": "Megabyte pr. sekund", + "gigabyte-per-second": "Gigabyte pr. sekund", + "degree": "Grad", + "radian": "Radian", + "gradian": "Gradian", + "revolution": "Omdrejning", + "siemens": "Siemens", + "millisiemens": "Millisiemens", + "microsiemens": "Mikrosiemens", + "kilosiemens": "Kilosiemens", + "megasiemens": "Megasiemens", + "gigasiemens": "Gigasiemens", + "farad": "Farad", + "millifarad": "Millifarad", + "microfarad": "Mikrofarad", + "nanofarad": "Nanofarad", + "picofarad": "Picofarad", + "kilofarad": "Kilofarad", + "megafarad": "Megafarad", + "gigafarad": "Gigafarad", + "terfarad": "Terfarad", + "farad-per-meter": "Farad pr. meter", + "tesla": "Tesla", + "gauss": "Gauss", + "kilogauss": "Kilogauss", + "millitesla": "Millitesla", + "microtesla": "Mikrotesla", + "nanotesla": "Nanotesla", + "kilotesla": "Kilotesla", + "megatesla": "Megatesla", + "millitesla-square-meters": "Millitesla kvadratmeter", + "gamma": "Gamma", + "lambda": "Lambda", + "square-meter-per-second": "Kvadratmeter pr. sekund", + "square-centimeter-per-second": "Kvadratcentimeter pr. sekund", + "stoke": "Stoke", + "centistokes": "Centistokes", + "square-foot-per-second": "Kvadratfod pr. sekund", + "square-inch-per-second": "Kvadrattomme pr. sekund", + "pascal-second": "Pascal-sekund", + "centipoise": "Centipoise", + "poise": "Poise", + "reynolds": "Reynolds", + "pound-per-foot-hour": "Pund pr. fod-time", + "newton-second-per-square-meter": "Newton-sekund pr. kvadratmeter", + "dyne-second-per-square-centimeter": "Dyne-sekund pr. kvadratcentimeter", + "kilogram-per-meter-second": "Kilogram pr. meter-sekund", + "tesla-square-meters": "Tesla kvadratmeter", + "maxwell": "Maxwell", + "tesla-per-meter": "Tesla pr. meter", + "gauss-per-centimeter": "Gauss pr. centimeter", + "weber": "Weber", + "microweber": "Mikroweber", + "milliweber": "Milliweber", + "gauss-square-centimeter": "Gauss-kvadratcentimeter", + "kilogauss-square-centimeter": "Kilogauss-kvadratcentimeter", + "henry": "Henry", + "millihenry": "Millihenry", + "microhenry": "Mikrohenry", + "nanohenry": "Nanohenry", + "henry-per-meter": "Henry pr. meter", + "tesla-meter-per-ampere": "Tesla meter pr. ampere", + "gauss-per-oersted": "Gauss pr. oersted", + "kilogram-per-mole": "Kilogram pr. mol", + "gram-per-mole": "Gram pr. mol", + "milligram-per-mole": "Milligram pr. mol", + "joule-per-mole": "Joule pr. mol", + "joule-per-mole-kelvin": "Joule pr. mol-kelvin", + "millivolts-per-meter": "Millivolt pr. meter", + "volts-per-meter": "Volt pr. meter", + "kilovolts-per-meter": "Kilovolt pr. meter", + "radian-per-second": "Radian pr. sekund", + "radian-per-second-squared": "Radian pr. sekund i anden", + "revolutions-per-minute-per-second": "Vinkelacceleration", + "deg-per-second": "grader/sekund", + "degrees-brix": "Grader Brix", + "katal": "Katal", + "katal-per-cubic-metre": "Katal pr. kubikmeter" }, "user": { "user": "Bruger", "users": "Brugere", - "management": "Brugeradministration", "customer-users": "Kundebrugere", - "tenant-admins": "Lejeradmin.", + "tenant-admins": "Lejeradministratorer", "sys-admin": "Systemadministrator", - "tenant-admin": "Bruger", + "tenant-admin": "Lejeradministrator", "customer": "Kunde", "anonymous": "Anonym", "add": "Tilføj bruger", @@ -3152,47 +6241,44 @@ "add-user-text": "Tilføj ny bruger", "no-users-text": "Ingen brugere fundet", "user-details": "Brugerdetaljer", - "delete-users": "Slet brugere", - "delete-user-title": "", - "delete-user-text": "Vær forsigtig. Efter bekræftelsen vil brugeren og alle relaterede data være uoprettelige.", - "delete-users-title": "", - "delete-users-action-title": "", - "delete-users-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte brugere blive fjernet, og alle relaterede data vil være uoprettelige.", - "activation-email-sent-message": "Aktiverings-e-mail blev sendt!", - "resend-activation": "Gensend aktivering", + "delete-user-title": "Er du sikker på, at du vil slette brugeren '{{userEmail}}'?", + "delete-user-text": "Vær forsigtig, efter bekræftelse bliver brugeren og alle relaterede data permanent slettet.", + "delete-users-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 bruger} other {# brugere} }?", + "delete-users-action-title": "Slet { count, plural, =1 {1 bruger} other {# brugere} }", + "delete-users-text": "Vær forsigtig, efter bekræftelse bliver alle valgte brugere slettet og data vil gå tabt.", + "activation-email-sent-message": "Aktiveringsmail blev sendt!", + "resend-activation": "Send aktiveringsmail igen", "email": "E-mail", "email-required": "E-mail er påkrævet.", - "invalid-email-format": "Ugyldigt e-mailformat.", + "invalid-email-format": "Ugyldigt e-mail-format.", "first-name": "Fornavn", "last-name": "Efternavn", "description": "Beskrivelse", "default-dashboard": "Standard dashboard", - "always-fullscreen": "Altid fuld skærm", + "always-fullscreen": "Altid fuldskærm", "select-user": "Vælg bruger", - "no-users-matching": "", + "no-users-matching": "Ingen brugere matcher '{{entity}}'.", "user-required": "Bruger er påkrævet", "activation-method": "Aktiveringsmetode", "display-activation-link": "Vis aktiveringslink", - "send-activation-mail": "Send aktiverings-e-mail", - "activation-link": "Link til brugeraktivering", - "activation-link-text": "", + "send-activation-mail": "Send aktiveringsmail", + "activation-link": "Brugeraktiveringslink", + "activation-link-text": "For at aktivere brugeren, brug følgende aktiveringslink (udløber om {{activationLinkTtl}}):", "copy-activation-link": "Kopiér aktiveringslink", - "activation-link-copied-message": "Brugeraktiveringslinket er blevet kopieret til udklipsholderen", - "selected-users": "", - "search": "Søg efter brugere", - "details": "Oplysninger", - "login-as-tenant-admin": "Log ind som lejeradministrator", - "login-as-customer-user": "Log ind som kundebruger", - "select-group-to-add": "Vælg målgruppe for at tilføje valgte brugere", - "select-group-to-move": "Vælg målgruppe for at flytte valgte brugere", - "remove-users-from-group": "", - "group": "Gruppe af brugere", - "list-of-groups": "", - "group-name-starts-with": "", + "activation-link-copied-message": "Brugeraktiveringslink blev kopieret til udklipsholder", + "details": "Detaljer", + "login-as-tenant-admin": "Log ind som Lejeradministrator", + "login-as-customer-user": "Log ind som Kundebruger", + "search": "Søg brugere", + "selected-users": "{ count, plural, =1 {1 bruger} other {# brugere} } valgt", "disable-account": "Deaktiver brugerkonto", - "enable-account": "Aktivér brugerkonto", - "enable-account-message": "Brugerkontoen er blevet aktiveret!", - "disable-account-message": "Brugerkontoen blev deaktiveret!" + "enable-account": "Aktiver brugerkonto", + "enable-account-message": "Brugerkonto blev aktiveret!", + "disable-account-message": "Brugerkonto blev deaktiveret!", + "copyId": "Kopiér bruger-id", + "idCopiedMessage": "Bruger-id blev kopieret til udklipsholder", + "user-list": "Brugerliste", + "user-list-required": "Brugerliste er påkrævet" }, "value": { "type": "Værditype", @@ -3203,163 +6289,370 @@ "integer-value": "Heltalsværdi", "integer-value-required": "Heltalsværdi er påkrævet", "invalid-integer-value": "Ugyldig heltalsværdi", - "double": "Dobbelt", - "double-value": "Dobbelt værdi", - "double-value-required": "Dobbelt værdi er påkrævet", + "double": "Decimal", + "double-value": "Decimalværdi", + "double-value-required": "Decimalværdi er påkrævet", "boolean": "Boolesk", "boolean-value": "Boolesk værdi", - "false": "FALSK", - "true": "SANDT", - "long": "Lang", + "false": "Falsk", + "true": "Sand", + "long": "Langt heltal", "json": "JSON", "json-value": "JSON-værdi", "json-value-invalid": "JSON-værdi har et ugyldigt format", "json-value-required": "JSON-værdi er påkrævet." }, + "version-control": { + "version-control": "Versionskontrol", + "management": "Versionskontroladministration", + "search": "Søg versioner", + "branch": "Gren", + "default": "Standard", + "select-branch": "Vælg gren", + "branch-required": "Gren er påkrævet", + "create-entity-version": "Opret entitetsversion", + "version-name": "Versionsnavn", + "version-name-required": "Versionsnavn er påkrævet", + "author": "Forfatter", + "export-relations": "Eksporter relationer", + "export-attributes": "Eksporter attributter", + "export-credentials": "Eksporter legitimationsoplysninger", + "export-calculated-fields": "Eksporter beregnede felter", + "entity-versions": "Entitetsversioner", + "versions": "Versioner", + "created-time": "Oprettelsestidspunkt", + "version-id": "Versions-ID", + "no-entity-versions-text": "Ingen entitetsversioner fundet", + "no-versions-text": "Ingen versioner fundet", + "copy-full-version-id": "Kopiér fuldt versions-id", + "create-version": "Opret version", + "creating-version": "Opretter version... Vent venligst", + "nothing-to-commit": "Ingen ændringer at committe", + "restore-version": "Gendan version", + "restore-entity-from-version": "Gendan entitet fra version '{{versionName}}'", + "restoring-entity-version": "Gendanner entitetsversion... Vent venligst", + "load-relations": "Indlæs relationer", + "load-attributes": "Indlæs attributter", + "load-credentials": "Indlæs legitimationsoplysninger", + "load-calculated-fields": "Indlæs beregnede felter", + "compare-with-current": "Sammenlign med nuværende", + "diff-entity-with-version": "Forskel fra entitetsversion '{{versionName}}'", + "previous-difference": "Forrige forskel", + "next-difference": "Næste forskel", + "current": "Nuværende", + "differences": "{ count, plural, =1 {1 forskel} other {# forskelle} }", + "create-entities-version": "Opret version af enheder", + "default-sync-strategy": "Standard synkroniseringsstrategi", + "sync-strategy-merge": "Flet", + "sync-strategy-overwrite": "Overskriv", + "entities-to-export": "Enheder der skal eksporteres", + "entities-to-restore": "Enheder der skal gendannes", + "sync-strategy": "Synkroniseringsstrategi", + "all-entities": "Alle enheder", + "no-entities-to-export-prompt": "Angiv venligst enheder, der skal eksporteres", + "no-entities-to-restore-prompt": "Angiv venligst enheder, der skal gendannes", + "add-entity-type": "Tilføj enhedstype", + "remove-all": "Fjern alle", + "version-create-result": "{ added, plural, =0 {Ingen enheder} =1 {1 enhed} other {# enheder} } tilføjet.
{ modified, plural, =0 {Ingen enheder} =1 {1 enhed} other {# enheder} } ændret.
{ removed, plural, =0 {Ingen enheder} =1 {1 enhed} other {# enheder} } fjernet.", + "remove-other-entities": "Fjern andre enheder", + "find-existing-entity-by-name": "Find eksisterende enhed efter navn", + "restore-entities-from-version": "Gendan enheder fra version '{{versionName}}'", + "restoring-entities-from-version": "Gendanner enheder... Vent venligst", + "no-entities-restored": "Ingen enheder gendannet", + "created": "{{created}} oprettet", + "updated": "{{updated}} opdateret", + "deleted": "{{deleted}} slettet", + "remove-other-entities-confirm-text": "Vær forsigtig! Dette vil permanent slette alle nuværende enheder
som ikke er til stede i den version, du ønsker at gendanne.

Indtast \"remove other entities\" for at bekræfte.", + "auto-commit-to-branch": "auto-commit til {{ branch }} gren", + "default-create-entity-version-name": "{{entityName}} opdatering", + "sync-strategy-merge-hint": "Opretter eller opdaterer valgte enheder i arkivet. Alle andre enheder i arkivet ændres ikke.", + "sync-strategy-overwrite-hint": "Opretter eller opdaterer valgte enheder i arkivet. Alle andre enheder i arkivet bliver slettet.", + "device-credentials-conflict": "Kunne ikke indlæse enhed med ekstern id {{entityId}}
fordi de samme legitimationsoplysninger allerede findes i databasen for en anden enhed.
Overvej at deaktivere indlæs legitimationsoplysninger i gendannelsesformularen.", + "missing-referenced-entity": "Kunne ikke indlæse {{sourceEntityTypeName}} med ekstern id {{sourceEntityId}}
fordi den refererer til en manglende {{targetEntityTypeName}} med id {{targetEntityId}}.", + "runtime-failed": "Mislykkedes: {{message}}", + "auto-commit-settings-read-only-hint": "Funktionen auto-commit fungerer ikke, når skrivebeskyttelse er aktiveret i arkivindstillingerne.", + "rollback-on-error": "Tilbagefør ved fejl", + "rollback-on-error-hint": "Hvis du har mange enheder at gendanne, bør du deaktivere denne mulighed for bedre ydeevne.\n Bemærk, hvis en fejl opstår under versionens indlæsning, forbliver allerede gemte enheder (med relationer, attributter osv.) som de er" + }, "widget": { - "widget-library": "Widgets-bibliotek", - "widget-bundle": "Widgets-bundt", - "all-bundles": "Alle bundter", - "select-widgets-bundle": "Vælg widgets-bundt", - "management": "Widget-administration", + "widget-library": "Widgetbibliotek", + "widget-bundle": "Widgetpakke", + "all-bundles": "Alle pakker", + "select-widgets-bundle": "Vælg widgetpakke", + "widgets": "Widgets", + "all-widgets": "Alle widgets", + "widget": "Widget", + "select-widget": "Vælg widget", + "no-widgets-matching": "Ingen widgets matcher '{{entity}}'.", + "no-widgets": "Ingen widgets endnu", + "no-widgets-text": "Ingen widgets fundet", + "management": "Widgetadministration", "editor": "Widget-editor", - "widget-type-not-found": "Problem under indlæsning af widget-konfiguration.
Sandsynligvis tilknyttet\r\n widget-type blev fjernet.", + "confirm-to-exit-editor-html": "Du har ikke gemte widgetindstillinger.
Er du sikker på, at du vil forlade denne side?", + "widget-type-not-found": "Problem med indlæsning af widgetkonfiguration.
Den tilknyttede widgettype er muligvis blevet fjernet.", "widget-type-load-error": "Widget blev ikke indlæst pga. følgende fejl:", "remove": "Fjern widget", + "delete": "Slet widget", "edit": "Rediger widget", - "remove-widget-title": "", - "remove-widget-text": "Efter bekræftelsen vil widgeten og alle relaterede data være uoprettelige.", - "timeseries": "Tidsserier", - "search-data": "Søg efter data", + "remove-widget-title": "Er du sikker på, at du vil fjerne widgetten '{{widgetTitle}}'?", + "remove-widget-text": "Efter bekræftelse vil widgetten og alle relaterede data være uoprettelige.", + "replace-reference-with-widget-copy": "Erstat reference med kopi af widget", + "timeseries": "Tidsserie", + "search-data": "Søg data", "no-data-found": "Ingen data fundet", "latest": "Seneste værdier", - "rpc": "Styring af widget", - "alarm": "Alarm-widget", + "rpc": "Kontrolwidget", + "alarm": "Alarmwidget", "static": "Statisk widget", - "select-widget-type": "Vælg widget-type", - "missing-widget-title-error": "Widget-titel skal angives!", + "timeseries-short": "serie", + "latest-short": "seneste", + "rpc-short": "kontrol", + "alarm-short": "alarm", + "static-short": "statisk", + "select-widget-type": "Vælg widgettype", + "missing-widget-title-error": "Widgettitel skal angives!", "widget-saved": "Widget gemt", - "unable-to-save-widget-error": "Widget kunne ikke gemmes! Widget har fejl!", + "unable-to-save-widget-error": "Kan ikke gemme widget! Widget har fejl!", "save": "Gem widget", "saveAs": "Gem widget som", - "save-widget-type-as": "Gem widget-type som", - "save-widget-type-as-text": "Indtast en ny widget-titel, og/eller vælg målwidget-bundt", - "toggle-fullscreen": "Skift til fuld skærm", + "move": "Flyt widget", + "save-widget-as": "Gem widget som", + "save-widget-as-text": "Indtast ny widgettitel", + "toggle-fullscreen": "Skift fuldskærm", "run": "Kør widget", - "title": "Widget-titel", - "title-required": "Widget-titel er påkrævet.", - "type": "Widget-type", + "widget-title": "Widgettitel", + "title": "Titel", + "title-required": "Widgettitel er påkrævet.", + "title-max-length": "Titel må ikke overstige 256 tegn", + "system": "System", + "type": "Widgettype", "resources": "Ressourcer", "resource-url": "JavaScript/CSS URL", + "resource-is-extension": "Er udvidelse", "remove-resource": "Fjern ressource", "add-resource": "Tilføj ressource", "html": "HTML", "tidy": "Tidy", "css": "CSS", - "settings-schema": "Indstillingsskema", - "datakey-settings-schema": "Skema over datanøgleindstillinger", - "widget-settings": "Widget-indstillinger", + "settings-form": "Indstillingsskema", + "data-key-settings-form": "Datakey-indstillingsskema", + "latest-data-key-settings-form": "Seneste datakey-indstillingsskema", + "widget-settings": "Widgetindstillinger", "description": "Beskrivelse", - "image-preview": "Forhåndsvisning af billede", + "tags": "Tags", + "image-preview": "Billedforhåndsvisning", + "settings-form-selector": "Indstillingsformularvælger", + "data-key-settings-form-selector": "Datakey-indstillingsformularvælger", + "latest-data-key-settings-form-selector": "Seneste datakey-indstillingsformularvælger", + "all": "Alle", + "actual": "Aktuel", + "scada": "SCADA-symbol", + "deprecated": "Forældet", + "has-basic-mode": "Har grundlæggende tilstand", + "basic-mode-form-selector": "Formularvælger for grundtilstand", + "basic-mode": "Grundlæggende", + "advanced-mode": "Avanceret", "javascript": "Javascript", "js": "JS", - "add-widget-type": "Tilføj ny widget-type", - "widget-template-load-failed-error": "Kunne ikke indlæse widget-skabelon!", + "delete-widget-title": "Er du sikker på, at du vil slette widgetten '{{widgetName}}'?", + "delete-widget-text": "Efter bekræftelse vil widgetten og alle relaterede data være uoprettelige.", + "delete-widgets-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 widget} other {# widgets} }?", + "delete-widgets-text": "Vær forsigtig, efter bekræftelse vil alle valgte widgets blive fjernet og data vil være uoprettelige.", + "delete-widget": "Slet widget", + "widget-template-load-failed-error": "Indlæsning af widgetskabelon mislykkedes!", + "details": "Detaljer", + "widget-details": "Widgetdetaljer", "add": "Tilføj widget", - "undo": "Fortryd widget-ændringer", + "add-existing-widget": "Tilføj eksisterende widget", + "add-new-widget": "Tilføj ny widget", + "search-widgets": "Søg widgets", + "selected-widgets": "{ count, plural, =1 {1 widget} other {# widgets} } valgt", + "undo": "Fortryd widgetændringer", "export": "Eksportér widget", - "export-data": "Eksportér widget-data", - "export-to-csv": "Eksportér data til CSV...", - "export-to-excel": "Eksportér data til XLS...", - "export-to-excel-xlsx": "Eksportér data til XLSX...", - "no-data": "Ingen data at vise på widget", - "data-overflow": "", - "alarm-data-overflow": "", - "search": "Søg efter widget", - "filter": "Widget-filtertype", - "loading-widgets": "Indlæser widgets..." + "export-prompt": "Indlejr widgetbilleder og ressourcer", + "export-widgets": "Eksportér widgets", + "export-widgets-prompt": "Indlejr widgets billeder og ressourcer", + "import": "Importér widget", + "no-data": "Ingen data at vise i widget", + "data-overflow": "Widget viser {{count}} ud af {{total}} enheder", + "alarm-data-overflow": "Widget viser alarmer for {{allowedEntities}} (maksimalt tilladt) enheder ud af {{totalEntities}} enheder", + "search": "Søg widget", + "filter": "Widget filtertype", + "loading-widgets": "Indlæser widgets...", + "widget-template-error": "Ugyldig HTML-skabelon for widget.", + "reference": "Reference" }, "widget-action": { - "header-button": "Widget-overskriftsknap", + "header-button": "Widget header-knap", + "do-nothing": "Gør ingenting", "open-dashboard-state": "Naviger til ny dashboardtilstand", "update-dashboard-state": "Opdater aktuel dashboardtilstand", - "open-dashboard": "Naviger til et andet dashboard", + "open-dashboard": "Naviger til andet dashboard", "custom": "Brugerdefineret handling", "custom-pretty": "Brugerdefineret handling (med HTML-skabelon)", + "custom-pretty-error-title": "Brugerdefineret dialogfejl", + "custom-pretty-template-error": "Ugyldig skabelon for brugerdefineret dialog.", + "custom-pretty-controller-error": "Der opstod en fejl ved evaluering af den brugerdefinerede dialogfunktion.", + "mobile-action": "Mobilhandling", "target-dashboard-state": "Mål-dashboardtilstand", "target-dashboard-state-required": "Mål-dashboardtilstand er påkrævet", - "set-entity-from-widget": "Angiv entitet fra widget", + "set-entity-from-widget": "Sæt enhed fra widget", "target-dashboard": "Mål-dashboard", - "open-right-layout": "Åbn højre dashboardlayout (mobilvisning)", - "open-in-separate-dialog": "Åbn i separat dialogboks", - "dialog-title": "Dialogbokstitel", - "dialog-hide-dashboard-toolbar": "Skjul dashboardets værktøjslinje i dialogboks", - "dialog-width": "Dialogboksbredde i procent i forhold til visningsportens bredde", - "dialog-height": "Dialogbokshøjde i procent i forhold til visningsportens højde", - "dialog-size-range-error": "Dialogboksstørrelsens procentværdi skal ligge i området fra 1 til 100.", - "open-new-browser-tab": "Åbn i en ny browserfane" + "select-target-dashboard": "Vælg mål-dashboard", + "target-dashboard-required": "Mål-dashboard er påkrævet.", + "open-right-layout": "Åbn højre dashboard-layout (mobilvisning)", + "state-display-type": "Visningsindstilling for dashboardtilstand", + "open-normal": "Normal", + "open-in-separate-dialog": "Åbn i separat dialog", + "open-in-popover": "Åbn i popover", + "dialog-title": "Dialogtitel", + "dialog-hide-dashboard-toolbar": "Skjul dashboardværktøjslinje i dialog", + "dialog-width": "Dialogbredde i procent i forhold til vinduets bredde", + "dialog-height": "Dialoghøjde i procent i forhold til vinduets højde", + "dialog-size-range-error": "Dialogens procentværdi skal være mellem 1 og 100.", + "popover-preferred-placement": "Foretrukken placering af popover", + "popover-placement-top": "Top", + "popover-placement-topLeft": "Øverst til venstre", + "popover-placement-topRight": "Øverst til højre", + "popover-placement-right": "Højre", + "popover-placement-rightTop": "Øverst til højre (højre)", + "popover-placement-rightBottom": "Nederst til højre (højre)", + "popover-placement-bottom": "Bund", + "popover-placement-bottomLeft": "Nederst til venstre", + "popover-placement-bottomRight": "Nederst til højre", + "popover-placement-left": "Venstre", + "popover-placement-leftTop": "Øverst til venstre (venstre)", + "popover-placement-leftBottom": "Nederst til venstre (venstre)", + "popover-hide-on-click-outside": "Skjul popover ved klik udenfor", + "popover-hide-dashboard-toolbar": "Skjul dashboardværktøjslinje i popover", + "popover-width": "Popover bredde", + "popover-height": "Popover højde", + "popover-style": "Popover stil", + "open-new-browser-tab": "Åbn i ny browserfane", + "open-URL": "Åbn URL", + "URL": "URL", + "url-required": "URL er påkrævet.", + "mobile": { + "device-provision": "Enhedsprovisionering", + "action-type": "Mobilhandlingstype", + "select-action-type": "Vælg mobilhandlingstype", + "action-type-required": "Mobilhandlingstype er påkrævet", + "take-picture-from-gallery": "Tag billede fra galleri", + "take-photo": "Tag foto", + "map-direction": "Åbn kortvejvisning", + "map-location": "Åbn kortplacering", + "scan-qr-code": "Scan QR-kode", + "make-phone-call": "Foretag telefonopkald", + "get-location": "Hent telefonplacering", + "take-screenshot": "Tag skærmbillede" + }, + "custom-action-function": "Brugerdefineret handlingsfunktion", + "custom-pretty-function": "Brugerdefineret handling (med HTML-skabelon) funktion", + "map-item-type": "Kortelementtype", + "map-item": { + "marker": "Markør", + "polygon": "Polygon", + "rectangle": "Rektangel", + "circle": "Cirkel" + }, + "place-map-item": "Placér kortelement", + "map-item-tooltip": { + "customize-map-item-tooltips": "Tilpas værktøjstips for kortelement", + "place-marker": "Placér markør", + "start-draw-rectangle": "Start med at tegne rektangel", + "finish-draw-rectangle": "Afslut tegning af rektangel", + "start-draw-polygon": "Start med at tegne polygon", + "continue-draw-polygon": "Fortsæt med at tegne polygon", + "finish-draw-polygon": "Afslut tegning af polygon", + "start-draw-circle": "Start med at tegne cirkel", + "finish-draw-circle": "Afslut tegning af cirkel" + } }, "widgets-bundle": { - "current": "Nuværende bundt", - "widgets-bundles": "Widgets-bundter", - "add": "Tilføj widgets-bundt", - "delete": "Slet widgets-bundt", + "current": "Aktuel pakke", + "widgets-bundles": "Widget-pakker", + "widgets-bundle-widgets": "Widgets i pakken", + "add": "Tilføj widget-pakke", + "delete": "Slet widget-pakke", "title": "Titel", "title-required": "Titel er påkrævet.", + "title-max-length": "Titel skal være mindre end 256 tegn", "description": "Beskrivelse", - "image-preview": "Forhåndsvisning af billede", - "add-widgets-bundle-text": "Tilføj nye widgets-bundter", - "no-widgets-bundles-text": "Ingen widgets-bundter fundet", - "empty": "Widgets-bundt er tomt", - "details": "Oplysninger", - "widgets-bundle-details": "Oplysninger om widgets-bundt", - "delete-widgets-bundle-title": "", - "delete-widgets-bundle-text": "Vær forsigtig. Efter bekræftelsen vil widgets-bundtet og alle relaterede data være uoprettelige.", - "delete-widgets-bundles-title": "", - "delete-widgets-bundles-action-title": "", - "delete-widgets-bundles-text": "Vær forsigtig. Efter bekræftelsen vil alle valgte widgets-bundter blive fjernet, og alle relaterede data vil være uoprettelige.", - "no-widgets-bundles-matching": "", - "widgets-bundle-required": "Widgets-bundt er påkrævet.", + "image-preview": "Billedforhåndsvisning", + "scada": "SCADA widget-pakke", + "order": "Rækkefølge", + "add-widgets-bundle-text": "Tilføj ny widget-pakke", + "no-widgets-bundles-text": "Ingen widget-pakker fundet", + "empty": "Widget-pakken er tom", + "details": "Detaljer", + "widgets-bundle-details": "Detaljer om widget-pakken", + "delete-widgets-bundle-title": "Er du sikker på, at du vil slette widget-pakken '{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text": "Vær forsigtig, efter bekræftelsen vil pakken og alle relaterede data ikke kunne gendannes.", + "delete-widgets-bundles-title": "Er du sikker på, at du vil slette { count, plural, =1 {1 widget-pakke} other {# widget-pakker} }?", + "delete-widgets-bundles-action-title": "Slet { count, plural, =1 {1 widget-pakke} other {# widget-pakker} }", + "delete-widgets-bundles-text": "Vær forsigtig, efter bekræftelsen vil alle valgte widget-pakker og relaterede data ikke kunne gendannes.", + "no-widgets-bundles-matching": "Ingen widget-pakker matcher '{{widgetsBundle}}'.", + "widgets-bundle-required": "Widget-pakke er påkrævet.", "system": "System", - "import": "Importér widgets-bundt", - "export": "Eksportér widgets-bundt", - "export-failed-error": "", - "create-new-widgets-bundle": "Opret nyt widgets-bundt", - "widgets-bundle-file": "Widgets-bundtfil", - "invalid-widgets-bundle-file-error": "Widgets-bundt kunne ikke importeres: Ugyldig datastruktur for widgets-bundt.", - "search": "Søg efter widget-bundter", - "selected-widgets-bundles": "", - "open-widgets-bundle": "Åbn widgets-bundt", - "loading-widgets-bundles": "Indlæser widgets-bundter..." + "import": "Importér widget-pakke", + "export": "Eksportér widget-pakke", + "export-widgets-bundle-widgets-prompt": "Medtag widgets i pakken i eksportdataene (ellers eksporteres kun refererede widget FQNs)", + "export-failed-error": "Kunne ikke eksportere widget-pakke: {{error}}", + "create-new-widgets-bundle": "Opret ny widget-pakke", + "widgets-bundle-file": "Widget-pakkefil", + "invalid-widgets-bundle-file-error": "Kunne ikke importere widget-pakke: Ugyldig datastruktur for widget-pakke.", + "search": "Søg i widget-pakker", + "selected-widgets-bundles": "{ count, plural, =1 {1 widget-pakke} other {# widget-pakker} } valgt", + "open-widgets-bundle": "Åbn widget-pakke", + "loading-widgets-bundles": "Indlæser widget-pakker...", + "create-new": "Opret ny widget-pakke" }, "widget-config": { "data": "Data", "settings": "Indstillinger", - "advanced": "Fremskreden", + "advanced": "Avanceret", + "appearance": "Udseende", + "widget-card": "Widgetkort", + "mobile": "Mobil", "title": "Titel", - "title-tooltip": "Værktøjstip for titel", + "title-tooltip": "Titelværktøjstip", "general-settings": "Generelle indstillinger", - "display-title": "Vis titel", - "drop-shadow": "Slip skygge", - "enable-fullscreen": "Aktivér fuldskærm", - "enable-data-export": "Aktivér dataeksport", + "display-title": "Vis widgettitel", + "card-title": "Korttitel", + "drop-shadow": "Skyggeeffekt", + "enable-fullscreen": "Aktivér fuld skærm", "background-color": "Baggrundsfarve", "text-color": "Tekstfarve", - "padding": "Padding", + "border-radius": "Kantafrunding", + "padding": "Polstring", "margin": "Margin", - "widget-style": "Widget-stil", - "title-style": "Titeltype", - "mobile-mode-settings": "Indstillinger for mobiltilstand", + "widget-style": "Widgetstil", + "widget-css": "Widget CSS", + "title-style": "Titelstil", + "mobile-mode-settings": "Mobiltilstand", "order": "Rækkefølge", "height": "Højde", - "units": "Specielt symbol, der vises ved siden af værdi", - "decimals": "Antal cifre efter flydende punkt", + "mobile-hide": "Skjul widget i mobiltilstand", + "desktop-hide": "Skjul widget i desktop-tilstand", + "units": "Specielt symbol vist ved siden af værdien", + "units-by-default": "Enheder som standard", + "decimals": "Antal cifre efter decimalpunkt", + "decimals-by-default": "Decimaler som standard", + "default-data-key-parameter-hint": "Denne parameter gælder for alle widget-værdier, medmindre de overskrives i datanøgle-konfigurationen", + "units-short": "Enheder", + "decimals-short": "Decimaler", + "decimals-suffix": "decimaler", + "digits-suffix": "cifre", "timewindow": "Tidsvindue", - "use-dashboard-timewindow": "Brug dashboardtidsvindue", + "use-dashboard-timewindow": "Brug dashboard tidsvindue", + "use-widget-timewindow": "Brug widget tidsvindue", "display-timewindow": "Vis tidsvindue", - "display-legend": "Vis siganturforklaring", + "legend": "Forklaring", + "display-legend": "Vis forklaring", "datasources": "Datakilder", - "maximum-datasources": "", + "datasource": "Datakilde", + "maximum-datasources": "Maksimum { count, plural, =1 {1 datakilde tilladt.} other {# datakilder tilladt} }", + "timeseries-key-error": "Mindst én tidsserie datanøgle skal være angivet", "datasource-type": "Type", "datasource-parameters": "Parametre", "remove-datasource": "Fjern datakilde", @@ -3372,89 +6665,546 @@ "search-actions": "Søg efter handlinger", "no-actions-text": "Ingen handlinger fundet", "action-source": "Handlingskilde", + "select-action-source": "Vælg handlingskilde", "action-source-required": "Handlingskilde er påkrævet.", + "column-index": "Kolonneindeks", + "select-column-index": "Vælg kolonneindeks", + "column-index-required": "Kolonneindeks er påkrævet.", + "not-set": "Ikke angivet", "action-name": "Navn", "action-name-required": "Handlingsnavn er påkrævet.", - "action-name-not-unique": "Der findes allerede en anden handling med samme navn.\nHandlingsnavnet skal være unikt inden for den samme handlingskilde.", + "action-name-not-unique": "Der findes allerede en anden handling med samme navn.\nHandlingsnavn skal være unikt inden for samme handlingskilde.", "action-icon": "Ikon", + "header-button": { + "button-settings": "Knapindstillinger", + "button-type": "Knaptype", + "button-type-basic": "Basis", + "button-type-raised": "Hævet", + "button-type-stroked": "Optrukket", + "button-type-flat": "Flad", + "button-type-icon": "Ikon", + "button-type-mini-fab": "FAB", + "colors": "Farver", + "color": "Farve", + "background": "Baggrund", + "border": "Kant", + "advanced-button-style": "Avanceret knapstil", + "button-style": "Knapstil" + }, + "show-hide-action-using-function": "Vis/skjul handling via funktion", + "show-action-function": "Vis handlingsfunktion", "action-type": "Type", "action-type-required": "Handlingstype er påkrævet.", "edit-action": "Rediger handling", "delete-action": "Slet handling", "delete-action-title": "Slet widget-handling", - "delete-action-text": "", + "delete-action-text": "Er du sikker på, at du vil slette widget-handlingen med navnet '{{actionName}}'?", + "title-icon": "Titelikon", "display-icon": "Vis titelikon", + "card-icon": "Kortikon", + "icon": "Ikon", "icon-color": "Ikonfarve", - "icon-size": "Ikonstørrelse" + "icon-size": "Ikonstørrelse", + "advanced-settings": "Avancerede indstillinger", + "data-settings": "Datainstillinger", + "limits": "Grænser", + "no-data-display-message": "Alternativ meddelelse for \"Ingen data at vise\"", + "data-page-size": "Maksimalt antal enheder pr. datakilde", + "settings-component-not-found": "Indstillingsformular komponent ikke fundet for selector '{{selector}}'", + "preview": "Forhåndsvisning", + "set": "Indstil", + "set-message": "Indstil besked", + "advanced-title-style": "Avanceret titelstil", + "card-style": "Kortstil", + "text": "Tekst", + "background": "Baggrund", + "advanced-widget-style": "Avanceret widgetstil", + "card-buttons": "Kortknapper", + "show-card-buttons": "Vis kortknapper", + "card-border-radius": "Kantafrunding på kort", + "card-padding": "Kortpolstring", + "card-appearance": "Kortudseende", + "color": "Farve", + "tooltip": "Værktøjstip", + "units-required": "Enhed er påkrævet.", + "list-layout": "Listeopsætning", + "layout": "Layout", + "resize-options": "Tilpasningsmuligheder", + "resizable": "Skalérbar", + "preserve-aspect-ratio": "Bevar billedformat" }, "widget-type": { - "import": "Importér widget-type", - "export": "Eksportér widget-type", - "export-failed-error": "", - "create-new-widget-type": "Opret ny widget-type", - "widget-type-file": "Widget-typefil", - "invalid-widget-type-file-error": "Kan ikke importere widget-type: Ugyldig datastruktur for widget-type." - }, - "self-registration": { - "self-registration": "Selvregistrering", - "self-registration-url": "Selvregistrerings-URL", - "captcha-site-key": "reCAPTCHA site-nøgle", - "captcha-secret-key": "reCAPTCHA hemmelig nøgle", - "notification-email": "Notifikations-e-mail", - "privacy-policy-text": "Fortrolighedspolitik tekst", - "text-message-page": "Tekstmeddelelse til tilmeldingsside" - }, - "white-labeling": { - "white-labeling": "Hvid mærkning", - "login-white-labeling": "Login for hvid mærkning", + "import": "Importer widgettype", + "export": "Eksporter widgettype", + "export-failed-error": "Kan ikke eksportere widget: {{error}}", + "widget-file": "Widgetfil", + "invalid-widget-file-error": "Kan ikke importere widget: Ugyldig widgetdatastruktur." + }, + "markdown": { + "edit": "Rediger", "preview": "Forhåndsvisning", - "app-title": "Applikationstitel", - "favicon": "Website-ikon", - "favicon-description": "", - "favicon-size-error": "", - "favicon-type-error": "Ugyldigt filformat for website-billede. Kun ICO-, GIF- eller PNG-billeder accepteres.", - "drop-favicon-image": "Træk og slip et website-ikonbillede, eller klik for at vælge en fil, der skal uploades.", - "no-favicon-image": "Intet ikon valgt", - "logo": "Logo", - "logo-description": "", - "logo-size-error": "", - "logo-type-error": "Ugyldigt logofilformat. Kun billeder accepteres.", - "drop-logo-image": "Træk og slip et logobillede, eller klik for at vælge en fil, der skal uploades.", - "no-logo-image": "Intet logo valgt", - "logo-height": "Logohøjde, px", - "primary-palette": "Primær palet", - "accent-palette": "Accent palet", - "customize-palette": "Tilpas", - "advanced-css": "Avanceret CSS", - "edit-palette": "Rediger palet", - "save-palette": "Gem palet", - "primary-background": "Primær baggrund", - "secondary-background": "Sekundær baggrund", - "hue1": "HUE 1", - "hue2": "HUE 2", - "hue3": "HUE 3", - "page-background-color": "Baggrundsfarve for side", - "dark-foreground": "Mørk forgrund", - "domain-name": "Domænenavn", - "base-url": "Basis-URL", - "base-url-required": "Basis-URL er påkrævet.", - "prohibit-different-url": "Det er ikke tilladt at bruge værtsnavn fra overskrifterne for klientanmodninger", - "prohibit-different-url-hint": "Denne indstilling skal aktiveres for produktionsmiljøer. Kan forårsage sikkerhedsproblemer ved deaktivering", - "help-link-base-url": "Basis-URL for hjælpelinks", - "enable-help-links": "Aktivér hjælpelinks", - "error-verification-url": "Et domænenavn må ikke indeholde symbolerne '/' og ':'. Eksempel: thingsboard.io", - "show-platform-name-version": "Vis platformnavn og version", - "platform-name": "Platformnavn", - "platform-version": "Platformversion", - "version-mask": "", - "position": { - "label": "Platformnavn og versionsposition", - "under-logo": "Under logoet", - "bottom": "Nederst i login-formularen" - } + "copy-code": "Klik for at kopiere", + "copied": "Kopieret!" }, "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "Konfigurationen afhænger af QR-kode widgeten for mobilappen i platformens hovedindstillinger", + "get-it-on-google-play": "Hent på Google Play", + "download-on-the-app-store": "Download i App Store" + }, + "action-button": { + "behavior": "Opførsel", + "on-click": "Ved klik", + "on-click-hint": "Handling udløses, når knappen klikkes", + "first-button-click": "Første knapklik", + "first-button-click-hint": "Handling ved tryk på første knap.", + "second-button-click": "Andet knapklik", + "second-button-click-hint": "Handling ved tryk på anden knap.", + "button-click-hint": "Handling ved tryk på widget." + }, + "command-button": { + "behavior": "Opførsel", + "on-click": "Ved klik", + "on-click-hint": "Handling udføres, når knappen klikkes." + }, + "power-button": { + "behavior": "Opførsel", + "power-on": "Tænd", + "power-on-hint": "Handling udføres for at tænde komponenten.", + "power-off": "Sluk", + "power-off-hint": "Handling udføres for at slukke komponenten.", + "on-label": "Tænd", + "off-label": "Sluk", + "layout": "Layout", + "layout-default": "Standard", + "layout-simplified": "Forenklet", + "layout-outlined": "Optrukket", + "layout-default-volume": "Standard.Lydstyrke", + "layout-simplified-volume": "Forenklet.Lydstyrke", + "layout-outlined-volume": "Optrukket.Lydstyrke", + "layout-default-icon": "Standard.Ikon", + "layout-simplified-icon": "Forenklet.Ikon", + "layout-outlined-icon": "Optrukket.Ikon", + "main": "Hoved", + "background": "Baggrund", + "button-icon-on": "Knapikon 'Tændt'", + "button-icon-off": "Knapikon 'Slukket'", + "power-on-colors": "Tænd-farver", + "power-off-colors": "Sluk-farver", + "disabled-colors": "Deaktiverede farver", + "button": "Knap" + }, + "toggle-button": { + "behavior": "Opførsel", + "checked": "Markeret", + "unchecked": "Ikke markeret", + "check": "Markér", + "check-hint": "Handling udføres for at markere komponenten.", + "uncheck": "Afmarkér", + "uncheck-hint": "Handling udføres for at afmarkere komponenten.", + "auto-scale": "Auto-skala", + "horizontal-fill": "Vandret udfyldning", + "vertical-fill": "Lodret udfyldning", + "button-appearance": "Knappens udseende" + }, + "segmented-button": { + "layout": "Layout", + "layout-squared": "Firkantet", + "layout-rounded": "Afrundet", + "card-border": "Kortkant", + "button-appearance": "Knappens udseende", + "first": "Første", + "second": "Anden", + "color-styles": "Farvestile", + "selected": "Valgt", + "unselected": "Ikke valgt" + }, + "button": { + "layout": "Layout", + "outlined": "Optrukket", + "filled": "Udfyldt", + "underlined": "Understreget", + "basic": "Grundlæggende", + "auto-scale": "Auto-skala", + "label": "Etiket", + "icon": "Ikon", + "border-radius": "Kanteradius", + "color-palette": "Farvepalet", + "main": "Hoved", + "background": "Baggrund", + "border": "Kant", + "custom-styles": "Brugerdefinerede stilarter", + "clear-style": "Ryd stil", + "shadow": "Skygge", + "enabled": "Aktiveret", + "disabled": "Deaktiveret", + "preview": "Forhåndsvisning", + "copy-style-from": "Kopiér stil fra" + }, + "value-stepper": { + "behavior": "Opførsel", + "simplified": "Forenklet", + "filled": "Udfyldt", + "outlined": "Omkredset", + "volume": "Volumen", + "initial-state": "Initial tilstand", + "initial-state-hint": "Handling for at hente den initiale værdi.", + "disabled-state": "Deaktiveret tilstand", + "disabled-state-hint": "Konfigurer betingelsen, hvorunder komponenten er deaktiveret.", + "right-button-click": "Højre knap klik", + "right-button-click-hint": "Handling ved tryk på højre knap.", + "left-button-click": "Venstre knap klik", + "left-button-click-hint": "Handling ved tryk på venstre knap.", + "auto-scale": "Automatisk skalering", + "value-range": "Interval", + "min-range": "Min", + "max-range": "Maks", + "value-increment-decrement-step": "Trin for værdi op/ned", + "value": "Værdi", + "value-box-background": "Baggrund for værdiboks", + "border": "Kant", + "button-appearance": "Knapudseende", + "left": "Venstre", + "right": "Højre", + "left-button": "Venstre knap", + "right-button": "Højre knap", + "icon": "Ikon", + "color-palette": "Farvepalet", + "main": "Primær", + "background": "Baggrund", + "button-icon-on": "Knapikon 'Tændt'", + "button-on-colors": "Farver for 'Tændt' tilstand", + "disabled-colors": "Deaktiverede farver" + }, + "button-state": { + "activated-state": "Aktiveret tilstand", + "activated-state-hint": "Konfigurer betingelse for hvornår knappen er aktiv.", + "disabled-state": "Deaktiveret tilstand", + "disabled-state-hint": "Konfigurer betingelse for hvornår knappen er deaktiveret.", + "selected-state": "Valgt tilstand", + "selected-state-hint": "Konfigurer betingelse for hvornår knappen er valgt.", + "enabled": "Aktiveret", + "hovered": "Holdt over", + "pressed": "Trykket", + "activated": "Aktiveret", + "disabled": "Deaktiveret", + "initial": "Første knap", + "first": "Første", + "second": "Anden" + }, + "background": { + "background": "Baggrund", + "background-settings": "Baggrundsindstillinger", + "background-type-image": "Billede", + "background-type-color": "Farve", + "image-url": "Billedets URL", + "overlay": "Overlay", + "enable-overlay": "Aktiver overlay", + "blur": "Sløring", + "preview": "Forhåndsvisning" + }, + "bar-chart": { + "bar-appearance": "Søjleudseende", + "label-on-bar": "Etiket på søjle", + "value-on-bar": "Værdi på søjle", + "bar-chart-style": "Søjlediagramstil", + "bar-axis": "Søjleafse" + }, + "polar-area-chart": { + "polar-axis": "Polarakse", + "start-angle": "Startvinkel", + "polar-area-chart-style": "Polar områdediagramstil" + }, + "battery-level": { + "layout": "Layout", + "layout-vertical-solid": "Lodret. Solid", + "layout-horizontal-solid": "Vandret. Solid", + "layout-vertical-divided": "Lodret. Opdelt", + "layout-horizontal-divided": "Vandret. Opdelt", + "icon": "Ikon", + "value": "Værdi", + "auto-scale": "Auto-skala", + "battery-level-color": "Batteriniveau farve", + "battery-shape-color": "Batteriform farve", + "battery-level-card-style": "Batteriniveau kortstil", + "sections-count": "Sektioner antal" + }, + "signal-strength": { + "value": "Værdi", + "last-update": "Seneste opdatering", + "no-signal": "Intet signal", + "layout": "Layout", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "Mobil søjle", + "icon": "Ikon", + "date": "Dato", + "active-bars-color": "Aktive signalbjælker farve", + "inactive-bars-color": "Inaktive signalbjælker farve", + "signal-strength-card-style": "Signalkortstil", + "no-signal-rssi-value": "\"Intet signal\" rssi-værdi" + }, + "status-widget": { + "behavior": "Adfærd", + "layout": "Layout", + "layout-default": "Standard", + "layout-center": "Centreret", + "layout-icon": "Ikon", + "on": "Tændt", + "off": "Slukket", + "label": "Etiket", + "status": "Status", + "icon": "Ikon", + "color-palette": "Farvepalet", + "disabled-color-palette": "Deaktiveret farvepalet", + "primary": "Primær", + "primary-color-hint": "Farve på ikon og etiket", + "secondary": "Sekundær", + "secondary-color-hint": "Farve på status", + "background": "Baggrund" + }, + "chart": { + "common-settings": "Fælles indstillinger", + "enable-stacking-mode": "Aktiver stabletilstand", + "selection": "Tidsområdevalg", + "enable-selection-mode": "Aktiver valgtilstand", + "line-shadow-size": "Skyggestørrelse på linje", + "display-smooth-lines": "Vis glatte (buede) linjer", + "default-bar-width": "Standard søjlebredde for ikke-aggregerede data (millisekunder)", + "bar-alignment": "Søjlens justering", + "bar-alignment-left": "Venstre", + "bar-alignment-right": "Højre", + "bar-alignment-center": "Centreret", + "default-font": "Standard skrifttype", + "default-font-size": "Standard skrifttypestørrelse", + "default-font-color": "Standard skrifttypefarve", + "thresholds-line-width": "Standard linjebredde for alle tærskler", + "tooltip-settings": "Værktøjstip-indstillinger", + "tooltip": "Værktøjstip", + "show-tooltip": "Vis værktøjstip", + "hover-individual-points": "Hold musen over individuelle punkter", + "show-cumulative-values": "Vis kumulative værdier i stabletilstand", + "hide-zero-false-values": "Skjul nul/falske værdier fra værktøjstip", + "tooltip-value-format-function": "Formateringsfunktion for værktøjstipværdi", + "grid-settings": "Gitterindstillinger", + "show-vertical-lines": "Vis lodrette linjer", + "show-horizontal-lines": "Vis vandrette linjer", + "grid-outline-border-width": "Gitterets kantbredde (px)", + "primary-color": "Primær farve", + "background-color": "Baggrundsfarve", + "ticks-color": "Farve på markeringer", + "xaxis-settings": "Indstillinger for X-akse", + "axis-title": "Aksens titel", + "xaxis-tick-labels-settings": "Indstillinger for X-aksens mærkater", + "show-tick-labels": "Vis mærkater på akse", + "yaxis-settings": "Indstillinger for Y-akse", + "min-scale-value": "Minimumværdi på skala", + "max-scale-value": "Maksimumværdi på skala", + "yaxis-tick-labels-settings": "Indstillinger for Y-aksens mærkater", + "tick-step-size": "Trin mellem markeringer", + "number-of-decimals": "Antal decimaler", + "ticks-formatter-function": "Formatteringsfunktion for markeringer", + "comparison-settings": "Sammenligningsindstillinger", + "enable-comparison": "Aktiver sammenligning", + "time-for-comparison": "Sammenligningsperiode", + "time-for-comparison-previous-interval": "Forrige interval (standard)", + "time-for-comparison-days": "En dag siden", + "time-for-comparison-weeks": "En uge siden", + "time-for-comparison-months": "En måned siden", + "time-for-comparison-years": "Et år siden", + "time-for-comparison-custom-interval": "Brugertilpasset interval", + "custom-interval-value": "Brugerdefineret intervalværdi (ms)", + "comparison-x-axis-settings": "X-akseindstillinger for sammenligning", + "axis-position": "Akses placering", + "axis-position-top": "Øverst (standard)", + "axis-position-bottom": "Nederst", + "custom-legend-settings": "Brugerdefinerede signaturindstillinger", + "enable-custom-legend": "Aktiver brugerdefineret signatur (brug attribut-/tidsserieværdier i nøgleetiketter)", + "key-name": "Nøglenavn", + "key-name-required": "Nøglenavn er påkrævet", + "key-type": "Nøgletype", + "key-type-attribute": "Attribut", + "key-type-timeseries": "Tidsserie", + "label-keys-list": "Liste over nøgler til brug i etiketter", + "no-label-keys": "Ingen nøgler konfigureret", + "add-label-key": "Tilføj ny nøgle", + "line-width": "Linjebredde", + "color": "Farve", + "data-is-hidden-by-default": "Data er som standard skjult", + "disable-data-hiding": "Deaktiver datas skjul", + "remove-from-legend": "Fjern datanøgle fra signatur", + "exclude-from-stacking": "Udeluk fra stabling (tilgængelig i \"Stabling\"-tilstand)", + "line-settings": "Linjens indstillinger", + "show-line": "Vis linje", + "fill-line": "Udfyld linje", + "fill-line-opacity": "Fyld opacitet", + "points-settings": "Punktindstillinger", + "show-points": "Vis punkter", + "points-line-width": "Linjebredde for punkter", + "points-radius": "Radius for punkter", + "point-shape": "Punktform", + "point-shape-circle": "Cirkel", + "point-shape-cross": "Kryds", + "point-shape-diamond": "Diamant", + "point-shape-square": "Firkant", + "point-shape-triangle": "Trekant", + "point-shape-custom": "Brugerdefineret funktion", + "point-shape-draw-function": "Tegnefunktion for punktform", + "show-separate-axis": "Vis separat akse", + "axis-position-left": "Venstre", + "axis-position-right": "Højre", + "thresholds": "Tærskler", + "no-thresholds": "Ingen tærskler konfigureret", + "add-threshold": "Tilføj tærskel", + "show-values-for-comparison": "Vis historiske værdier til sammenligning", + "comparison-values-label": "Etiket for historiske værdier", + "comparison-line-color": "Farve for sammenligningslinje", + "threshold-settings": "Tærskelindstillinger", + "use-as-threshold": "Brug nøgleværdi som tærskel", + "threshold-line-width": "Tærskellinjens bredde", + "threshold-color": "Tærskelfarve", + "common-pie-settings": "Fælles indstillinger for lagkage", + "radius": "Radius", + "inner-radius": "Indre radius", + "tilt": "Hældning", + "common-pie-settings-range-error": "Værdien skal være mellem 0 og 1", + "stroke-settings": "Stregindstillinger", + "width-pixels": "Bredde (pixels)", + "show-labels": "Vis etiketter", + "animation-settings": "Animationsindstillinger", + "animated-pie": "Aktiver lagkageanimation (eksperimentelt)", + "border-settings": "Kantindstillinger", + "border-width": "Kantbredde", + "border-color": "Kantfarve", + "legend-settings": "Signaturindstillinger", + "display-legend": "Vis signatur", + "labels-font-color": "Skrifttypefarve for etiketter", + "series": "Serier", + "add-series": "Tilføj serie", + "series-settings": "Serieindstillinger", + "remove-series": "Fjern serie", + "no-series": "Ingen serier konfigureret", + "no-series-error": "Mindst én serie skal angives", + "chart-appearance": "Diagrammets udseende", + "vertical-grid-lines": "Lodrette gitterlinjer", + "horizontal-grid-lines": "Vandrette gitterlinjer", + "chart-background": "Diagrambaggrund", + "grid-lines-color": "Gitterlinjefarve", + "border": "Kant", + "axis": "Akse", + "vertical-axis": "Lodret akse", + "ticks": "Mærker", + "horizontal-axis": "Vandret akse", + "shape-empty-circle": "Tom cirkel", + "shape-circle": "Cirkel", + "shape-rect": "Rektangel", + "shape-round-rect": "Afrundet rektangel", + "shape-triangle": "Trekant", + "shape-diamond": "Diamant", + "shape-pin": "Knappenål", + "shape-arrow": "Pil", + "shape-none": "Ingen", + "line-type-solid": "Solid", + "line-type-dashed": "Stiplet", + "line-type-dotted": "Prikket", + "label-position-top": "Top", + "label-position-bottom": "Bund", + "label-position-outside": "Udenfor", + "label-position-inside": "Indenfor", + "fill": "Fyld", + "fill-type-none": "Ingen", + "fill-type-solid": "Solid", + "fill-type-opacity": "Gennemsigtighed", + "fill-type-gradient": "Gradient", + "background": "Baggrund", + "opacity": "Gennemsigtighed", + "gradient-stops": "Gradientstop", + "gradient-start": "start", + "gradient-end": "slut", + "animation": { + "animation": "Animation", + "animation-threshold": "Animationsgrænse", + "animation-duration": "Animationsvarighed", + "animation-easing": "Animationsudjævning", + "animation-delay": "Animationsforsinkelse", + "update-animation-duration": "Opdater animationsvarighed", + "update-animation-easing": "Opdater animationsudjævning", + "update-animation-delay": "Opdater animationsforsinkelse" + }, + "chart-axis": { + "scale": "Skala", + "scale-min": "min", + "scale-max": "maks", + "scale-auto": "Auto" + }, + "bar": { + "show-border": "Vis kant", + "border-width": "Kantbredde", + "border-radius": "Kantens afrunding", + "bar-width": "Søjlebredde", + "label": "Etiket", + "label-hint": "Vis etiket over søjlen.", + "series-label-hint": "Vis etiket med værdi over søjlen.", + "label-background": "Etiketbaggrund" + } + }, + "color": { + "color-settings": "Farveindstillinger", + "color-type-constant": "Konstant", + "color-type-gradient": "Gradient", + "color-type-range": "Interval", + "color-type-function": "Funktion", + "color": "Farve", + "value-range": "Værdiparameter", + "from": "Fra", + "to": "Til", + "color-function": "Farvefunktion", + "copy-color-settings-from": "Kopiér farveindstillinger fra", + "copy-from": "Kopiér fra", + "settings-type": "Indstillingstype", + "basic-mode": "Basis", + "advanced-mode": "Avanceret", + "entity-alias": "Enhedsalias", + "entity-attribute": "Enhedsattribut", + "gradient-color": "Gradientfarve", + "gradient-color-min": "Farve", + "gradient-start": "Startfarve for gradient", + "gradient-start-min": "Start", + "gradient-end": "Slutfarve for gradient", + "gradient-end-min": "Slut", + "start-value": "Startværdi", + "end-value": "Slutværdi", + "gradient-type": "Gradienttype" + }, + "dashboard-state": { + "dashboard-state-settings": "Dashboardtilstandsindstillinger", + "dashboard-state": "Dashboardtilstand ID", + "autofill-state-layout": "Autoudfyld tilstandslayoutets højde som standard", + "default-margin": "Standard margen for widgets", + "default-background-color": "Standard baggrundsfarve", + "sync-parent-state-params": "Synkroniser tilstandsparametre med overordnet dashboard" + }, "date-range-navigator": { + "date-range-picker-settings": "Indstillinger for datoområdevælger", + "hide-date-range-picker": "Skjul datoområdevælger", + "picker-one-panel": "Datoområdevælger med ét panel", + "picker-auto-confirm": "Datoområdevælger automatisk bekræftelse", + "picker-show-template": "Vis skabelon for datoområdevælger", + "first-day-of-week": "Ugens første dag", + "interval-settings": "Intervalindstillinger", + "hide-interval": "Skjul interval", + "initial-interval": "Initialt interval", + "interval-hour": "Time", + "interval-day": "Dag", + "interval-week": "Uge", + "interval-two-weeks": "2 uger", + "interval-month": "Måned", + "interval-three-months": "3 måneder", + "interval-six-months": "6 måneder", + "step-settings": "Trinindstillinger", + "hide-step-size": "Skjul trinlængde", + "initial-step-size": "Initial trinlængde", + "hide-labels": "Skjul etiketter", + "use-session-storage": "Brug sessionslagring", "localizationMap": { "Sun": "Søn", "Mon": "Man", @@ -3486,8 +7236,8 @@ "October": "Oktober", "November": "November", "December": "December", - "Custom Date Range": "Brugerdefineret datointerval", - "Date Range Template": "Skabelon for datointerval", + "Custom Date Range": "Brugerdefineret datoområde", + "Date Range Template": "Skabelon for datoområde", "Today": "I dag", "Yesterday": "I går", "This Week": "Denne uge", @@ -3495,7 +7245,7 @@ "This Month": "Denne måned", "Last Month": "Sidste måned", "Year": "År", - "This Year": "I år", + "This Year": "Dette år", "Last Year": "Sidste år", "Date picker": "Datovælger", "Hour": "Time", @@ -3508,410 +7258,1967 @@ "Custom interval": "Brugerdefineret interval", "Interval": "Interval", "Step size": "Trinstørrelse", - "Ok": "Okay" + "Ok": "OK" } }, + "doughnut": { + "doughnut-appearance": "Udseende af doughnut", + "layout": "Layout", + "layout-default": "Standard", + "layout-with-total": "Med total", + "central-total-value": "Central totalværdi", + "doughnut-card-style": "Doughnut kortstil" + }, + "entities-hierarchy": { + "hierarchy-data-settings": "Indstillinger for hierarkidata", + "relations-query-function": "Funktion for forespørgsel på relationer", + "has-children-function": "Funktion for om noden har børn", + "node-state-settings": "Indstillinger for node-tilstand", + "node-opened-function": "Standardfunktion for åben node", + "node-disabled-function": "Funktion for deaktiveret node", + "display-settings": "Visningsindstillinger", + "node-icon-function": "Funktion for nodeikon", + "node-text-function": "Funktion for nodetekst", + "sort-settings": "Sorteringsindstillinger", + "nodes-sort-function": "Funktion for sortering af noder" + }, + "edge": { + "display-default-title": "Vis standardtitel" + }, + "gateway": { + "general-settings": "Generelle indstillinger", + "widget-title": "Widget-titel", + "default-archive-file-name": "Standardnavn for arkivfil", + "device-type-for-new-gateway": "Enhedstype for ny gateway", + "messages-settings": "Indstillinger for beskeder", + "save-config-success-message": "Besked om at gateway-konfigurationen er gemt", + "device-name-exists-message": "Besked når enhed med angivet navn allerede findes", + "gateway-title": "Gateway-formular", + "read-only": "Skrivebeskyttet", + "events-title": "Titel for gateway-hændelsesformular", + "events-filter": "Filter for hændelser", + "event-key-contains": "Hændelsesnøgle indeholder...", + "show-connector": "Vis for connector", + "connector-state-param-key": "Parameternøgle for connectorens tilstand", + "message": "Besked", + "level": "Niveau", + "created-time": "Oprettelsestid" + }, + "gauge": { + "default-color": "Standardfarve", + "radial-gauge-settings": "Indstillinger for radialmåler", + "ticks-settings": "Indstillinger for mærker", + "min-value": "Minimumværdi", + "max-value": "Maksimumværdi", + "min-value-short": "min", + "max-value-short": "maks", + "start-ticks-angle": "Startvinkel for mærker", + "ticks-angle": "Vinkel for mærker", + "major-ticks": "Primære mærker", + "major-ticks-count": "Antal primære mærker", + "major-ticks-color": "Farve på primære mærker", + "minor-ticks": "Sekundære mærker", + "minor-ticks-count": "Antal sekundære mærker", + "minor-ticks-color": "Farve på sekundære mærker", + "tick-numbers-font": "Skrifttype for mærketal", + "unit-title-settings": "Indstillinger for enhedstitel", + "show-unit-title": "Vis enhedstitel", + "unit-title": "Enhedstitel", + "title-font": "Skrifttype for titeltekst", + "units-settings": "Indstillinger for enheder", + "units-font": "Skrifttype for enhedstekst", + "value-box-settings": "Indstillinger for værdiboks", + "show-value-box": "Vis værdiboks", + "value-box": "Værdiboks", + "value-int": "Antal cifre i heltalsdelen af værdi", + "value-text": "Værdi tekst", + "value-text-shadow": "Skygge for værdi tekst", + "value-font": "Skrifttype for værdi tekst", + "rect-stroke-color-start": "Startfarve for rektangelkontur", + "rect-stroke-color-end": "Slutfarve for rektangelkontur", + "background-color": "Baggrundsfarve", + "shadow-color": "Skyggefarve", + "value-box-rect-stroke-color": "Konturfarve for værdiboks", + "value-box-rect-stroke-color-end": "Slutfarve for værdiboksens kontur", + "value-box-background-color": "Baggrundsfarve for værdiboks", + "value-box-shadow-color": "Skyggefarve for værdiboks", + "plate-settings": "Pladeindstillinger", + "show-plate-border": "Vis pladekant", + "plate-color": "Pladefarve", + "needle-settings": "Indstillinger for nål", + "needle-circle-size": "Størrelse på nålcirkel", + "needle-color": "Nålfarve", + "needle-color-start": "Startfarve for nål", + "needle-color-end": "Slutfarve for nål", + "needle-color-shadow-up": "Skyggefarve for øverste nål", + "needle-color-shadow-down": "Skyggefarve (nederst)", + "highlights-settings": "Indstillinger for fremhævning", + "highlights-width": "Bredde på fremhævning", + "highlights": "Fremhævninger", + "highlight-from": "Fra", + "highlight-to": "Til", + "highlight-color": "Farve", + "no-highlights": "Ingen fremhævninger konfigureret", + "add-highlight": "Tilføj fremhævning", + "animation-settings": "Indstillinger for animation", + "enable-animation": "Animation", + "animation-duration-rule": "Varighed og regel for animation", + "animation-duration": "Varighed af animation", + "animation-rule": "Animationsregel", + "animation-linear": "Lineær", + "animation-quad": "Kvadratisk", + "animation-quint": "Kvintisk", + "animation-cycle": "Cykel", + "animation-bounce": "Hop", + "animation-elastic": "Elastisk", + "animation-dequad": "De-kvadratisk", + "animation-dequint": "De-kvintisk", + "animation-decycle": "De-cykel", + "animation-debounce": "De-hop", + "animation-delastic": "De-elastisk", + "linear-gauge-settings": "Indstillinger for lineær måler", + "bar-stroke": "Streg på bjælke", + "bar-stroke-width": "Stregbredde på bjælke", + "bar-stroke-color": "Stregfarve på bjælke", + "bar-background-color": "Baggrundsfarve på bjælke - start", + "bar-background-color-end": "Baggrundsfarve på bjælke - slut", + "progress-bar-color": "Fremskridtsbjælkens farve", + "progress-bar": "Fremskridtsbjælke", + "progress-bar-color-start": "Startfarve på fremskridtsbjælke", + "progress-bar-color-end": "Slutfarve på fremskridtsbjælke", + "major-ticks-names": "Navne på primære mærker", + "show-stroke-ticks": "Vis mærkestreg", + "major-ticks-font": "Skrifttype for primære mærker", + "border-color": "Kantfarve", + "border-width": "Kantbredde", + "needle-circle": "Nål cirkel", + "needle-circle-color": "Nålcirklens farve", + "animation-target": "Animationsmål", + "animation-target-needle": "Nål", + "animation-target-plate": "Plade", + "common-settings": "Almindelige målerindstillinger", + "gauge-type": "Målertype", + "gauge-type-arc": "Bue", + "gauge-type-donut": "Donut", + "gauge-type-horizontal-bar": "Vandret bjælke", + "gauge-type-vertical-bar": "Lodret bjælke", + "donut-start-angle": "Startvinkel (grader)", + "bar-settings": "Bjælkeindstillinger for måler", + "relative-bar-width": "Relativ bjælkebredde", + "neon-glow-brightness": "Lysstyrke for neon-glødeeffekt (0-100)", + "neon-glow-brightness-hint": "0 - deaktiver effekt", + "stripes-thickness": "Stribebredde", + "stripes-thickness-hint": "0 - ingen striber", + "rounded-line-cap": "Afrundet linjekant", + "bar-color-settings": "Farveindstillinger for bjælke", + "use-precise-level-color-values": "Brug præcise farveniveauer", + "bar-colors": "Bjælkefarver, fra lav til høj", + "color": "Farve", + "no-bar-colors": "Ingen bjælkefarver konfigureret", + "add-bar-color": "Tilføj bjælkefarve", + "from": "Fra", + "to": "Til", + "fixed-level-colors": "Bjælkefarver baseret på grænseværdier", + "gauge-title-settings": "Indstillinger for målertitel", + "show-gauge-title": "Vis målertitel", + "gauge-title": "Målertitel", + "gauge-title-font": "Skrifttype for målertitel", + "unit-title-and-timestamp-settings": "Indstillinger for enhedstitel og tidsstempel", + "show-timestamp": "Tidsstempel", + "timestamp-format": "Tidsstempelformat", + "label-font": "Skrifttype for etikette under værdi", + "value-settings": "Værdiindstillinger", + "show-value": "Vis værditekst", + "min-max-settings": "Indstillinger for min./maks.-etiketter", + "show-min-max": "Vis min. og maks.-værdier", + "min-max-font": "Skrifttype for min. og maks.-etiketter", + "show-ticks": "Vis mærker", + "tick-width": "Bredde på mærker", + "tick-color": "Mærkefarve", + "tick-values": "Mærkeværdier", + "no-tick-values": "Ingen mærkeværdier konfigureret", + "add-tick-value": "Tilføj mærkeværdi", + "gauge-appearance": "Målerudseende", + "units-title": "Enhedstitel", + "value": "Værdi", + "ticks": "Mærker", + "arrow-and-scale-color": "Standardfarve for pil og skala", + "scale-settings": "Skalaindstillinger", + "scale": "Skala", + "scale-color": "Skalafarver", + "compass-appearance": "Kompasudseende", + "label": "Etiket", + "labels": "Etiketter", + "label-style": "Etiketstil", + "simple-gauge-type": "Type", + "gauge-bar-background": "Baggrund for målerbjælke", + "bar-color": "Bjælkefarve", + "min-and-max-value": "Min. og maks.-værdi", + "min-and-max-label": "Min. og maks.-etiket", + "font": "Skrifttype", + "tick-width-and-color": "Bredde og farve på mærker", + "min-max-validation-text": "Maks.-værdi skal være større end min.-værdi" + }, + "gpio": { + "pin": "Pin", + "label": "Etiket", + "row": "Række", + "column": "Kolonne", + "color": "Farve", + "panel-settings": "Panelindstillinger", + "background-color": "Baggrundsfarve", + "gpio-switches": "GPIO-kontakter", + "no-gpio-switches": "Ingen GPIO-kontakter konfigureret", + "add-gpio-switch": "Tilføj GPIO-kontakt", + "gpio-status-request": "GPIO-statusanmodning", + "method-name": "Metodenavn", + "method-body": "Metodeindhold", + "gpio-status-change-request": "Anmodning om ændring af GPIO-status", + "parse-gpio-status-function": "Funktion til at fortolke GPIO-status", + "gpio-leds": "GPIO-LED'er", + "no-gpio-leds": "Ingen GPIO-LED'er konfigureret", + "add-gpio-led": "Tilføj GPIO-LED" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, "input-widgets": { - "attribute-not-allowed": "Attributparameter kan ikke bruges i denne widget", - "blocked-location": "Geoplaceringen er blokeret i din browser", - "claim-device": "Gør krav på enhed", - "claim-failed": "Kunne ikke gøre krav på enheden!", + "attribute-not-allowed": "Attributparameter må ikke bruges i denne widget", + "blocked-location": "Geolokation er blokeret i din browser", + "claim-device": "Tag enhed i brug", + "claim-failed": "Det mislykkedes at tage enheden i brug!", "claim-not-found": "Enhed ikke fundet!", - "claim-successful": "Enheden er blevet krævet!", + "claim-successful": "Enheden blev taget i brug med succes!", "date": "Dato", "device-name": "Enhedsnavn", "device-name-required": "Enhedsnavn er påkrævet", "discard-changes": "Kassér ændringer", - "entity-attribute-required": "Entitetsattribut er påkrævet", - "entity-coordinate-required": "Både felter, breddegrad og længdegrad er påkrævet", - "entity-timeseries-required": "Tidsserie for entitet er påkrævet", - "get-location": "Få den aktuelle placering", + "entity-attribute-required": "Enhedsattribut er påkrævet", + "entity-coordinate-required": "Begge felter, breddegrad og længdegrad, er påkrævet", + "entity-timeseries-required": "Enhedens tidsserie er påkrævet", + "get-location": "Hent nuværende placering", "invalid-date": "Ugyldig dato", "latitude": "Breddegrad", - "longitude": "Længde", - "min-value-error": "", - "max-value-error": "", - "not-allowed-entity": "Den valgte entitet kan ikke have delte attributter", + "longitude": "Længdegrad", + "min-value-error": "Minimumsværdi er {{value}}", + "max-value-error": "Maksimumsværdi er {{value}}", + "not-allowed-entity": "Valgt enhed må ikke have delte attributter", "no-attribute-selected": "Ingen attribut er valgt", "no-datakey-selected": "Ingen datanøgle er valgt", - "no-coordinate-specified": "Datanøgle for breddegrad/længdegrad er ikke angivet", - "no-entity-selected": "Ingen entitet valgt", - "no-image": "Intet billede", - "no-support-geolocation": "Din browser understøtter ikke geoplacering", + "no-coordinate-specified": "Datanøgle for bredde-/længdegrad er ikke angivet", + "no-entity-selected": "Ingen enhed valgt", + "no-image": "Ingen billede", + "no-support-geolocation": "Din browser understøtter ikke geolokation", "no-support-web-camera": "Din browser understøtter ikke kameraer", "enable-https-use-widget": "Aktivér HTTPS for at bruge denne widget", "no-found-your-camera": "Kan ikke finde dit kamera", - "no-permission-camera": "Tilladelse blev nægtet af brugeren / Dette website har ikke tilladelse til at bruge kameraet", - "no-timeseries-selected": "Ingen tidsserier valgt", + "no-permission-camera": "Tilladelse blev nægtet af brugeren / Denne side har ikke tilladelse til at bruge kameraet", + "no-timeseries-selected": "Ingen tidsserie valgt", "secret-key": "Hemmelig nøgle", "secret-key-required": "Hemmelig nøgle er påkrævet", - "switch-attribute-value": "Skift entitetsattributværdi", + "switch-attribute-value": "Skift enhedsattributværdi", "switch-camera": "Skift kamera", - "switch-timeseries-value": "Skift entitetstidsserieværdi", - "take-photo": "Tag et billede", + "switch-timeseries-value": "Skift enhedens tidsserie-værdi", + "take-photo": "Tag billede", "time": "Tid", - "timeseries-not-allowed": "Tidsserieparameter kan ikke bruges i denne widget", + "timeseries-not-allowed": "Tidsserieparameter må ikke bruges i denne widget", "update-failed": "Opdatering mislykkedes", - "update-successful": "Opdatering lykkedes", - "update-attribute": "Opdatering", - "update-timeseries": "Opdater tidsserier", - "value": "Værdi" + "update-successful": "Opdatering gennemført", + "update-attribute": "Opdater attribut", + "update-timeseries": "Opdater tidsserie", + "value": "Værdi", + "general-settings": "Generelle indstillinger", + "widget-title": "Widgettitel", + "claim-button-label": "Etiket for brug-knap", + "show-secret-key-field": "Vis inputfelt for 'Hemmelig nøgle'", + "labels-settings": "Etiketindstillinger", + "show-labels": "Vis etiketter", + "device-name-label": "Etiket for feltet 'Enhedsnavn'", + "secret-key-label": "Etiket for feltet 'Hemmelig nøgle'", + "messages-settings": "Beskedindstillinger", + "claim-device-success-message": "Tekstbesked ved succesfuld enhedsbrug", + "claim-device-not-found-message": "Tekstbesked når enhed ikke findes", + "claim-device-failed-message": "Tekstbesked ved fejlslagen enhedsbrug", + "claim-device-name-required-message": "Fejlbesked: 'Enhedsnavn er påkrævet'", + "claim-device-secret-key-required-message": "Fejlbesked: 'Hemmelig nøgle er påkrævet'", + "show-label": "Vis etiket", + "label": "Etiket", + "required": "Påkrævet", + "required-error-message": "Fejlbesked: 'Påkrævet'", + "show-result-message": "Vis resultatbesked", + "integer-field-settings": "Indstillinger for heltalsfelt", + "min-value": "Minimumsværdi", + "max-value": "Maksimumsværdi", + "double-field-settings": "Indstillinger for decimaltalsfelt", + "text-field-settings": "Indstillinger for tekstfelt", + "min-length": "Minimumslængde", + "max-length": "Maksimumslængde", + "checkbox-settings": "Afkrydsningsfeltindstillinger", + "true-label": "Etiket når markeret", + "false-label": "Etiket når ikke markeret", + "image-input-settings": "Indstillinger for billedinput", + "display-preview": "Vis forhåndsvisning", + "display-clear-button": "Vis 'Ryd'-knap", + "display-apply-button": "Vis 'Anvend'-knap", + "display-discard-button": "Vis 'Kassér'-knap", + "datetime-field-settings": "Indstillinger for dato/tid-felt", + "display-time-input": "Vis tid-input", + "latitude-key-name": "Navn på breddegradsnøgle", + "longitude-key-name": "Navn på længdegradsnøgle", + "show-get-location-button": "Vis knappen 'Hent nuværende placering'", + "use-high-accuracy": "Brug høj præcision", + "location-fields-settings": "Indstillinger for lokationsfelter", + "latitude-label": "Etiket for breddegrad", + "longitude-label": "Etiket for længdegrad", + "input-fields-alignment": "Justering af inputfelter", + "input-fields-alignment-column": "Kolonne (standard)", + "input-fields-alignment-row": "Række", + "layout": "Layout", + "row-gap": "Afstand mellem rækker i pixels", + "column-gap": "Afstand mellem kolonner i pixels", + "latitude-field-required": "Breddegradsfelt påkrævet", + "longitude-field-required": "Længdegradsfelt påkrævet", + "attribute-settings": "Attributindstillinger", + "widget-mode": "Widgettilstand", + "widget-mode-update-attribute": "Opdater attribut", + "widget-mode-update-timeseries": "Opdater tidsserie", + "attribute-scope": "Attributområde", + "attribute-scope-server": "Serverattribut", + "attribute-scope-shared": "Delt attribut", + "value-required": "Værdi er påkrævet", + "image-settings": "Billedindstillinger", + "image-format": "Billedformat", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "Billedkvalitet der bruger tabsbaseret komprimering som JPEG og WEBP", + "max-image-width": "Maksimal billedbredde", + "max-image-height": "Maksimal billedhøjde", + "action-buttons": "Handlingsknapper", + "show-action-buttons": "Vis handlingsknapper", + "update-all-values": "Opdater alle værdier, ikke kun de ændrede", + "save-button-label": "Etiket for 'GEM'-knap", + "reset-button-label": "Etiket for 'FORTRYD'-knap", + "group-settings": "Gruppeindstillinger", + "show-group-title": "Vis titel for gruppe af felter relateret til forskellige enheder", + "group-title": "Gruppetitel", + "fields-alignment": "Justering af felter", + "fields-alignment-row": "Række (standard)", + "fields-alignment-column": "Kolonne", + "fields-in-row": "Antal felter i rækken", + "option-value": "Værdi (skriv 'null' for at oprette tom mulighed)", + "option-label": "Etiket", + "hide-input-field": "Skjul inputfelt", + "datakey-type": "Datanøgletype", + "datakey-type-server": "Serverattribut (standard)", + "datakey-type-shared": "Delt attribut", + "datakey-type-timeseries": "Tidsserie", + "datakey-value-type": "Datanøgleværdi-type", + "datakey-value-type-string": "Streng", + "datakey-value-type-double": "Decimal", + "datakey-value-type-integer": "Heltal", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "Boolesk (Afkrydsningsfelt)", + "datakey-value-type-boolean-switch": "Boolesk (Skift)", + "datakey-value-type-date-time": "Dato og tid", + "datakey-value-type-date": "Dato", + "datakey-value-type-time": "Tid", + "datakey-value-type-select": "Vælg", + "datakey-value-type-radio": "Radioknap", + "datakey-value-type-color": "Farve", + "value-is-required": "Værdi er påkrævet", + "ability-to-edit-attribute": "Mulighed for at redigere attribut", + "ability-to-edit-attribute-editable": "Redigerbar (standard)", + "ability-to-edit-attribute-disabled": "Deaktiveret", + "ability-to-edit-attribute-readonly": "Skrivebeskyttet", + "disable-on-datakey-name": "Deaktiver baseret på falsk værdi fra anden datanøgle (angiv navn)", + "field-appearance": "Feltets udseende", + "appearance-fill": "Fyld", + "appearance-outline": "Kantlinje", + "subscript-sizing": "Størrelse på undertekst", + "subscript-sizing-fixed": "Fast", + "subscript-sizing-dynamic": "Dynamisk", + "slide-toggle-settings": "Indstillinger for skiftekontakt", + "slide-toggle-label-position": "Etiketplacering for skiftekontakt", + "slide-toggle-label-position-after": "Efter", + "slide-toggle-label-position-before": "Før", + "select-options": "Valgmuligheder", + "no-select-options": "Ingen valgmuligheder konfigureret", + "add-select-option": "Tilføj valgmulighed", + "numeric-field-settings": "Indstillinger for numerisk felt", + "step-interval": "Trininterval mellem værdier", + "error-messages": "Fejlmeddelelser", + "min-value-error-message": "Fejlmeddelelse: 'Minimumsværdi'", + "max-value-error-message": "Fejlmeddelelse: 'Maksimumsværdi'", + "invalid-date-error-message": "Fejlmeddelelse: 'Ugyldig dato'", + "invalid-JSON-error-message": "Fejlmeddelelse: 'Ugyldig JSON'", + "icon-settings": "Ikonindstillinger", + "dialog-editor-settings": "Dialogeditor-indstillinger", + "use-custom-icon": "Brug brugerdefineret ikon", + "input-cell-icon": "Ikon vist før inputfelt", + "value-conversion-settings": "Indstillinger for værdikonvertering", + "get-value-settings": "Indstillinger for at hente værdi", + "use-get-value-function": "Brug getValue-funktion", + "get-value-function": "getValue-funktion", + "set-value-settings": "Indstillinger for at sætte værdi", + "use-set-value-function": "Brug setValue-funktion", + "set-value-function": "setValue-funktion", + "json-invalid": "JSON-værdi har et ugyldigt format", + "title": "Titel", + "cancel-button-label": "Etiket for 'Annuller'-knap", + "radio-button-settings": "Indstillinger for radioknap", + "color": "Farve", + "columns": "Kolonner", + "radio-options": "Radiomuligheder", + "no-radio-options": "Ingen radiomuligheder konfigureret", + "add-radio-option": "Tilføj radiomulighed", + "radio-label-position": "Etiketposition", + "radio-label-position-before": "Før", + "radio-label-position-after": "Efter" + }, + "invalid-qr-code-text": "Ugyldig inputtekst til QR-kode. Input skal være af typen streng", + "qr-code": { + "use-qr-code-text-function": "Brug QR-kodetekstfunktion", + "qr-code-text-pattern": "QR-kodetekstmønster (f.eks. '${entityName} | ${keyName} - noget tekst.')", + "qr-code-text-pattern-hint": "QR-kodetekstmønster bruger værdien af den første fundne nøgle i enhederne i enhedsaliasset.", + "qr-code-text-pattern-required": "QR-kodetekstmønster er påkrævet.", + "qr-code-text-function": "QR-kodetekstfunktion" + }, + "label-widget": { + "label-pattern": "Mønster", + "label-pattern-hint": "Tip: f.eks. 'Tekst ${keyName} enheder.' eller ${#<key index>} enheder'", + "label-pattern-required": "Mønster er påkrævet", + "label-position": "Position (procent relativt til baggrund)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Baggrundsfarve", + "font-settings": "Skrifttypeindstillinger", + "background-image": "Baggrundsbillede", + "labels": "Etiketter", + "no-labels": "Ingen etiketter konfigureret", + "add-label": "Tilføj etiket" + }, + "navigation": { + "title": "Titel", + "navigation-path": "Navigationssti", + "filter-type": "Filtertype", + "filter-type-all": "Alle elementer", + "filter-type-include": "Inkludér elementer", + "filter-type-exclude": "Ekskludér elementer", + "items": "Elementer", + "enter-urls-to-filter": "Indtast URL'er der skal filtreres..." + }, + "persistent-table": { + "rpc-id": "RPC ID", + "message-type": "Meddelelsestype", + "method": "Metode", + "params": "Parametre", + "created-time": "Oprettelsestidspunkt", + "expiration-time": "Udløbstidspunkt", + "retries": "Gentagelser", + "status": "Status", + "filter": "Filter", + "refresh": "Opdater", + "add": "Tilføj RPC-anmodning", + "details": "Detaljer", + "delete": "Slet", + "delete-request-title": "Slet Persistent RPC-anmodning", + "delete-request-text": "Er du sikker på, at du vil slette anmodningen?", + "details-title": "Detaljer RPC ID: ", + "additional-info": "Yderligere oplysninger", + "response": "Svar", + "any-status": "Enhver status", + "rpc-status-list": "RPC-statusliste", + "no-request-prompt": "Ingen anmodninger at vise", + "send-request": "Send anmodning", + "add-title": "Opret Persistent RPC-anmodning", + "method-error": "Metode er påkrævet.", + "timeout-error": "Minimum timeoutværdi er 5000 (5 sekunder).", + "white-space-error": "Mellemrum er ikke tilladt.", + "rpc-status": { + "QUEUED": "I KØ", + "SENT": "SENDT", + "DELIVERED": "LEVERET", + "SUCCESSFUL": "LYKKEDES", + "TIMEOUT": "TIDSUDLØB", + "EXPIRED": "UDLØBET", + "FAILED": "FEJLET" + }, + "rpc-search-status-all": "ALLE", + "message-types": { + "false": "Tovejs", + "true": "Envejs" + }, + "general-settings": "Generelle indstillinger", + "enable-filter": "Aktivér filter", + "enable-sticky-header": "Vis overskrift under rulning", + "enable-sticky-action": "Vis handlingskolonne under rulning", + "display-request-details": "Vis anmodningsdetaljer", + "allow-send-request": "Tillad RPC-anmodning", + "allow-delete-request": "Tillad sletning af anmodning", + "columns-settings": "Kolonneindstillinger", + "display-columns": "Kolonner der vises", + "column": "Kolonne", + "no-columns-found": "Ingen kolonner fundet", + "no-columns-matching": "'{{column}}' blev ikke fundet." + }, + "range-chart": { + "chart": "Diagram", + "data-zoom": "Datazoom", + "range-chart-appearance": "Udseende for område-diagram", + "range-colors": "Områdefarver", + "out-of-range-color": "Farve uden for området", + "show-range-thresholds": "Vis områdegrænser", + "range-thresholds-settings": "Indstillinger for områdegrænser", + "fill-area": "Udfyld område", + "fill-area-opacity": "Gennemsigtighed for områdeudfyldning", + "range-chart-style": "Stil for område-diagram" + }, + "rpc": { + "value-settings": "Indstillinger for værdi", + "initial-value": "Startværdi", + "retrieve-value-settings": "Indstillinger for hentning af tænd/sluk værdi", + "retrieve-value-method": "Hent værdi via metode", + "retrieve-value-method-none": "Hent ikke", + "retrieve-value-method-rpc": "Kald RPC get-værdimetode", + "retrieve-value-method-attribute": "Abonner på attribut", + "retrieve-value-method-timeseries": "Abonner på tidsserier", + "attribute-value-key": "Attributnøgle", + "timeseries-value-key": "Tidsserienøgle", + "get-value-method": "RPC get-værdimetode", + "parse-value-function": "Fortolk værdi-funktion", + "update-value-settings": "Opdater værdi-indstillinger", + "set-value-method": "RPC set-værdimetode", + "convert-value-function": "Konverter værdi-funktion", + "rpc-settings": "RPC-indstillinger", + "request-timeout": "RPC timeout for forespørgsel (ms)", + "persistent-rpc-settings": "Indstillinger for persistent RPC", + "request-persistent": "RPC-forespørgsel er persistent", + "persistent-polling-interval": "Polling-interval (ms) for at hente svar på persistent RPC-kommando", + "common-settings": "Fælles indstillinger", + "switch-title": "Titel for kontakt", + "show-on-off-labels": "Vis tænd/sluk-mærkater", + "slide-toggle-label": "Glidemærkat", + "label-position": "Etiketposition", + "label-position-before": "Før", + "label-position-after": "Efter", + "slider-color": "Skydefarve", + "slider-color-primary": "Primær", + "slider-color-accent": "Accent", + "slider-color-warn": "Advarsel", + "button-style": "Knapstil", + "button-raised": "Hævet knap", + "button-primary": "Primær farve", + "button-background-color": "Baggrundsfarve på knap", + "button-text-color": "Tekstfarve på knap", + "widget-title": "Widgettitel", + "button-label": "Knaptekst", + "device-attribute-scope": "Omfang for enhedsattribut", + "server-attribute": "Serverattribut", + "shared-attribute": "Delt attribut", + "device-attribute-parameters": "Parametre for enhedsattribut", + "is-one-way-command": "Er envejs-kommando", + "rpc-method": "RPC-metode", + "rpc-method-params": "Parametre for RPC-metode", + "show-rpc-error": "Vis RPC-kommandofejl", + "led-title": "LED-titel", + "led-color": "LED-farve", + "check-status-settings": "Indstillinger for statuskontrol", + "perform-rpc-status-check": "Udfør statuskontrol via RPC", + "retrieve-led-status-value-method": "Hent LED-statusværdi via metode", + "led-status-value-attribute": "Enhedsattribut der indeholder LED-statusværdi", + "led-status-value-timeseries": "Enheds tidsserie der indeholder LED-statusværdi", + "check-status-method": "RPC-metode til kontrol af enhedsstatus", + "parse-led-status-value-function": "Fortolk LED-statusværdi-funktion", + "knob-title": "Titel for knap", + "min-value": "Minimumværdi", + "max-value": "Maksimumværdi" + }, + "maps": { + "map-type": { + "type": "Korttype", + "map": "Kort", + "image": "Billede" + }, + "image": { + "image-source": "Billedkilde", + "image-source-image": "Billede", + "image-source-entity-key": "Enhedsnøgle", + "source-entity-alias": "Kildeenhedsalias", + "image-url-key": "Billede-URL-nøgle", + "image-url-key-required": "Billede-URL-nøgle er påkrævet" + }, + "control": { + "map-controls": "Kortkontroller", + "position": "Position", + "position-topleft": "Øverst til venstre", + "position-topright": "Øverst til højre", + "position-bottomleft": "Nederst til venstre", + "position-bottomright": "Nederst til højre", + "zoom-actions": "Zoomhandlinger", + "zoom-scroll": "Rul", + "zoom-double-click": "Dobbeltklik", + "zoom-control-buttons": "Kontrolknapper", + "scale": "Skala", + "scale-metric": "Metrisk", + "scale-imperial": "Imperial", + "switch-to-drag-mode-using-button": "Skift til træktilstand med knap" + }, + "timeline": { + "control-panel": "Tidslinjekontrolpanel", + "time-step": "Tidsinterval", + "speed-options": "Hastighedsmuligheder", + "timestamp": "Tidsstempel", + "snap-to-real-location": "Fastgør til reel position", + "location-snap-filter-function": "Filterfunktion for positionsfastgørelse", + "no-trips-data-available": "Ingen rejseoplysninger tilgængelige" + }, + "map-action": { + "map-action-buttons": "Handlingsknapper på kortet", + "label": "Etiket", + "icon": "Ikon", + "color": "Farve", + "action": "Handling", + "add-button": "Tilføj knap", + "no-action-buttons-configured": "Ingen handlingsknapper konfigureret", + "remove-action-button": "Fjern handlingsknap", + "map-action-button": "Handlingsknap på kortet", + "button-requires": "Knap kræver etiket eller ikon" + }, + "common": { + "common-map-settings": "Almindelige kortindstillinger", + "fit-map-bounds": "Tilpas kortgrænser til at inkludere alle markører", + "default-map-center-position": "Standardcenterposition for kort", + "default-map-zoom-level": "Standardzoomniveau for kort", + "entities-limit": "Grænse for antal enheder der skal indlæses" + }, + "layer": { + "label": "Etiket", + "layer": "Lag", + "layers": "Lag", + "map-layers": "Kortlag", + "add-layer": "Tilføj lag", + "layer-settings": "Lagindstillinger", + "remove-layer": "Fjern lag", + "no-layers": "Ingen lag konfigureret", + "roadmap": "Vejkort", + "satellite": "Satellit", + "hybrid": "Hybrid", + "reference": { + "reference-layer": "Referencelag", + "no-layer": "Intet lag", + "openstreetmap-hybrid": "OpenStreetMap Hybrid", + "world-edition-hybrid": "World Edition Hybrid", + "enhanced-contrast-hybrid": "Forbedret kontrast Hybrid" + }, + "provider": { + "provider": "Udbyder", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Vejkort", + "satellite": "Satellit", + "hybrid": "Hybrid", + "terrain": "Terræn" + }, + "here": { + "title": "HERE", + "normal-day": "Normal dag", + "normal-night": "Normal nat", + "hybrid-day": "Hybrid dag", + "terrain-day": "Terræn dag" + }, + "tencent": { + "title": "Tencent", + "normal": "Normal", + "satellite": "Satellit", + "terrain": "Terræn" + }, + "custom": { + "title": "Brugerdefineret", + "tile-url": "Flise-URL" + } + }, + "credentials": { + "credentials": "Legitimationsoplysninger", + "api-key": "API-nøgle" + } + }, + "overlays": { + "overlays": "Overlays", + "overlays-hint": "Konfigurer datakilder, udseende, adfærd, redigeringsmuligheder og gruppering for kortenheder", + "trips": "Rejser", + "markers": "Markører", + "polygons": "Polygoner", + "circles": "Cirkler" + }, + "data-layer": { + "source": "Kilde", + "filter": "Filter", + "additional-data-keys": "Yderligere datanøgler", + "additional-datasources": "Yderligere datakilder", + "additional-datasources-hint": "Datakilde til adgang til attributter eller telemetri fra enheder, der ikke vises på kortet, anvendelig i kortoverlay-funktioner.", + "more-datasources": "Flere datakilder", + "data-keys": "Datanøgler", + "add-datasource": "Tilføj datakilde", + "no-datasources": "Ingen datakilder konfigureret", + "remove-datasource": "Fjern datakilde", + "behavior": "Adfærd", + "on-click": "Ved klik", + "on-click-hint": "Handling udført når brugeren klikker på kortelementet.", + "groups": "Grupper", + "groups-hint": "Liste over gruppenavne tildelt overlayet, bruges til at skifte synlighed på kortet.", + "color": "Farve", + "color-settings": "Farveindstillinger", + "color-type-constant": "Konstant", + "color-type-range": "Interval", + "color-type-function": "Funktion", + "color-range-source-key": "Kilde nøgle for farveinterval", + "color-range-source-key-required": "Kilde nøgle for farveinterval er påkrævet", + "color-range": "Farveinterval", + "color-function": "Farvefunktion", + "label": "Etiket", + "tooltip": "Værktøjstip", + "pattern-type-pattern": "Mønster", + "pattern-type-function": "Funktion", + "label-pattern": "Etiket (eksempel: '${entityName}', '${entityName}: (Tekst ${keyName} enheder.)')", + "label-function": "Etiketfunktion", + "tooltip-pattern": "Værktøjstip (for eksempel 'Tekst ${keyName} enheder.' eller Linktekst)", + "tooltip-function": "Værktøjstipfunktion", + "tooltip-trigger": "Udløser for værktøjstip", + "tooltip-trigger-click": "Vis værktøjstip ved klik", + "tooltip-trigger-hover": "Vis værktøjstip ved hover", + "auto-close-tooltips": "Luk værktøjstip automatisk", + "tooltip-offset": "Forskydning af værktøjstip", + "tooltip-offset-horizontal": "Vandret", + "tooltip-offset-vertical": "Lodret", + "tooltip-tag-actions": "Mærkehandlinger", + "add-tooltip-tag-action": "Tilføj mærkehandling", + "edit-tooltip-tag-action": "Rediger mærkehandling", + "remove-tooltip-tag-action": "Fjern mærkehandling", + "action-add": "Tilføj", + "action-edit": "Rediger", + "action-move": "Flyt", + "action-remove": "Fjern", + "edit-instruments": "Redigeringsværktøjer", + "persist-location-attribute-scope": "Omfang for attribut til lagring af placering", + "enable-snapping": "Aktiver tilpasning til andre hjørner for præcis tegning", + "enable-snapping-hint": "Tilpasser automatisk nye punkter med eksisterende former for nemmere og mere præcis tegning.", + "drag-drop-mode": "Træk og slip tilstand", + "trip": { + "no-trips": "Ingen rejser konfigureret", + "add-trip": "Tilføj rejse", + "trip-configuration": "Rejsekonfiguration", + "remove-trip": "Fjern rejse" + }, + "marker": { + "marker": "Markør", + "latitude-key": "Breddegrad nøgle", + "longitude-key": "Længdegrad nøgle", + "x-pos-key": "X-position nøgle", + "y-pos-key": "Y-position nøgle", + "latitude-key-required": "Breddegrad nøgle er påkrævet", + "longitude-key-required": "Længdegrad nøgle er påkrævet", + "x-pos-key-required": "X-position nøgle er påkrævet", + "y-pos-key-required": "Y-position nøgle er påkrævet", + "no-markers": "Ingen markører konfigureret", + "add-marker": "Tilføj markør", + "marker-configuration": "Markørkonfiguration", + "remove-marker": "Fjern markør", + "marker-type": "Markørtype", + "marker-type-shape": "Form", + "marker-type-icon": "Ikon", + "marker-type-image": "Billede", + "shape": "Form", + "icon": "Ikon", + "image": "Billede", + "marker-shapes": "Markørformer", + "marker-icon": "Markørikon", + "marker-appearance": "Markørens udseende", + "marker-image": "Markørbillede", + "marker-image-type-image": "Billede", + "marker-image-type-function": "Funktion", + "custom-marker-image-size": "Tilpasset størrelse på markørbillede", + "marker-image-function": "Markørbilledefunktion", + "marker-images": "Markørbilleder", + "marker-offset": "Markørforskydning", + "offset-horizontal": "Vandret", + "offset-vertical": "Lodret", + "rotate-marker": "Rotér markør", + "offset-angle": "Forskydningsvinkel", + "position-conversion": "Positionskonvertering", + "position-conversion-function": "Positionskonverteringsfunktion, skal returnere x,y koordinater som double fra 0 til 1", + "clustering": { + "use-map-markers-clustering": "Brug kortmarkørklyngning", + "zoom-on-cluster-click": "Zoom ved klik på klynge", + "max-zoom": "Maksimalt zoomniveau hvor markør kan være en del af klynge (0 - 18)", + "max-radius": "Maksimal radius for en klynge", + "zoom-animation": "Animation på markører ved zoom", + "bounds-on-cluster-mouse-over": "Grænser for markører ved mus over klynge", + "spiderfy-max-zoom-level": "Udvid ved maksimal zoomniveau (for at se alle markører)", + "load-optimization": "Indlæsningsoptimering", + "chunked-load": "Brug opdeling til indlæsning af markører for at undgå frysning", + "lazy-load": "Brug lazy load til tilføjelse af markører", + "use-cluster-marker-color-function": "Brug farvefunktion for klyngemarkører", + "marker-color-function": "Markørfarvefunktion" + }, + "edit": "Rediger markør", + "remove-marker-for": "Fjern markør for '{{entityName}}'", + "place-marker": "Placer markør", + "place-marker-hint": "Klik for at placere markør", + "place-marker-hint-with-entity": "Klik for at placere enhed '{{entityName}}'" + }, + "path": { + "path": "Sti", + "path-decorator": "Sti dekoration", + "decorator-symbol": "Dekorationssymbol", + "decorator-symbol-arrow-head": "Pil", + "decorator-symbol-dash": "Streg", + "decorator-arrangement": "Dekorationsarrangement", + "decorator-offset": "Start", + "decorator-end-offset": "Slut", + "decorator-repeat": "Gentag" + }, + "points": { + "points": "Punkter", + "point-tooltip": "Punktværktøjstip" + }, + "shape": { + "fill": "Fyld", + "fill-type-color": "Farve", + "fill-type-stripe": "Stribe", + "fill-type-image": "Billede", + "color": "Farve", + "stripe": "Stribe", + "image": "Billede", + "stroke": "Streg", + "fill-image": "Fyldbillede", + "fill-image-type-image": "Billede", + "fill-image-type-function": "Funktion", + "preserve-aspect-ratio": "Bevar billedformat", + "opacity": "Gennemsigtighed", + "angle": "Rotationsvinkel", + "scale": "Skalering", + "fill-image-function": "Form fyldbilledfunktion", + "fill-images": "Form fyldbilleder", + "stripe-pattern": "Stribemønster", + "first-stripe": "Første stribe", + "second-stripe": "Anden stribe" + }, + "polygon": { + "polygon-key": "Polygonnøgle", + "polygon-key-required": "Polygonnøgle er påkrævet", + "no-polygons": "Ingen polygoner konfigureret", + "add-polygon": "Tilføj polygon", + "polygon-configuration": "Polygonkonfiguration", + "remove-polygon": "Fjern polygon", + "edit": "Rediger polygon", + "remove-polygon-for": "Fjern polygon for '{{entityName}}'", + "cut": "Klip polygonområde", + "rotate": "Rotér polygon", + "draw-rectangle": "Tegn rektangel", + "draw-polygon": "Tegn polygon", + "polygon-place-first-point-cut-hint": "Klik for at placere første punkt", + "continue-polygon-cut-hint": "Klik for at fortsætte tegning", + "finish-polygon-cut-hint": "Klik på første markør for at afslutte og gemme", + "polygon-place-first-point-hint": "Polygon: klik for at placere første punkt", + "polygon-place-first-point-hint-with-entity": "Polygon for '{{entityName}}': klik for at placere første punkt", + "continue-polygon-hint": "Polygon: klik for at fortsætte tegning", + "continue-polygon-hint-with-entity": "Polygon for '{{entityName}}': klik for at fortsætte tegning", + "finish-polygon-hint": "Polygon: klik på første markør for at afslutte tegning", + "finish-polygon-hint-with-entity": "Polygon for '{{entityName}}': klik på første markør for at afslutte og gemme", + "rectangle-place-first-point-hint": "Rektangel: klik for at placere første punkt", + "rectangle-place-first-point-hint-with-entity": "Rektangel for '{{entityName}}': klik for at placere første punkt", + "finish-rectangle-hint": "Rektangel: klik for at afslutte tegning", + "finish-rectangle-hint-with-entity": "Rektangel for '{{entityName}}': klik for at afslutte og gemme" + }, + "circle": { + "circle-key": "Cirkelnøgle", + "circle-key-required": "Cirkelnøgle er påkrævet", + "no-circles": "Ingen cirkler konfigureret", + "add-circle": "Tilføj cirkel", + "circle-configuration": "Cirkelkonfiguration", + "remove-circle": "Fjern cirkel", + "edit": "Rediger cirkel", + "remove-circle-for": "Fjern cirkel for '{{entityName}}'", + "draw-circle": "Tegn cirkel", + "place-circle-center-hint-with-entity": "Cirkel for '{{entityName}}': klik for at placere centrum", + "place-circle-center-hint": "Cirkel: klik for at placere centrum", + "finish-circle-hint-with-entity": "Cirkel for '{{entityName}}': klik for at afslutte og gemme cirkel", + "finish-circle-hint": "Cirkel: klik for at afslutte tegning" + }, + "select-entity": "Vælg enhed", + "select-entity-hint": "Tip: klik på kortet efter valg for at angive position" + }, + "select-entity": "Vælg enhed", + "select-entity-hint": "Tip: klik på kortet efter valg for at angive position", + "tooltips": { + "placeMarker": "Klik for at placere enheden '{{entityName}}'", + "firstVertex": "Polygon for '{{entityName}}': klik for at placere første punkt", + "firstVertex-cut": "Klik for at placere første punkt", + "continueLine": "Polygon for '{{entityName}}': klik for at fortsætte tegningen", + "continueLine-cut": "Klik for at fortsætte tegningen", + "finishLine": "Klik på en eksisterende markør for at afslutte", + "finishPoly": "Polygon for '{{entityName}}': klik på første markør for at afslutte og gemme", + "finishPoly-cut": "Klik på første markør for at afslutte og gemme", + "finishRect": "Polygon for '{{entityName}}': klik for at afslutte og gemme", + "startCircle": "Cirkel for '{{entityName}}': klik for at placere cirkelens centrum", + "finishCircle": "Cirkel for '{{entityName}}': klik for at afslutte cirklen", + "placeCircleMarker": "Klik for at placere cirkelmarkør" + }, + "actions": { + "finish": "Afslut", + "cancel": "Annuller", + "removeLastVertex": "Fjern sidste punkt" + }, + "buttonTitles": { + "drawMarkerButton": "Placer enhed", + "drawPolyButton": "Opret polygon", + "drawLineButton": "Opret polylinje", + "drawCircleButton": "Opret cirkel", + "drawRectButton": "Opret rektangel", + "editButton": "Redigeringstilstand", + "dragButton": "Træk-og-slip-tilstand", + "cutButton": "Skær polygonområde", + "deleteButton": "Fjern", + "drawCircleMarkerButton": "Opret cirkelmarkør", + "rotateButton": "Roter polygon" + }, + "map-provider-settings": "Kortudbyder-indstillinger", + "map-provider": "Kortudbyder", + "map-provider-google": "Google Maps", + "map-provider-openstreet": "OpenStreet Maps", + "map-provider-here": "HERE Maps", + "map-provider-image": "Billedkort", + "map-provider-tencent": "Tencent Maps", + "openstreet-provider": "OpenStreet kortudbyder", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Standard)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery": "Esri.WorldImagery", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Brug tilpasset udbyder", + "custom-provider-tile-url": "Tilpasset udbyder tile URL", + "google-maps-api-key": "Google Maps API-nøgle", + "default-map-type": "Standard korttype", + "google-map-type-roadmap": "Vejkort", + "google-map-type-satelite": "Satellit", + "google-map-type-hybrid": "Hybrid", + "google-map-type-terrain": "Terræn", + "map-layer": "Kortlag", + "here-map-normal-day": "HERE.normalDay (Standard)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "Legitimationsoplysninger", + "here-app-id": "HERE app-id", + "here-app-code": "HERE app-kode", + "here-api-key": "HERE API-nøgle", + "here-use-new-version-api-3": "Brug API version 3", + "tencent-maps-api-key": "Tencent Maps API-nøgle", + "tencent-map-type-roadmap": "Vejkort", + "tencent-map-type-satelite": "Satellit", + "tencent-map-type-hybrid": "Hybrid", + "image-map-background": "Baggrundsbillede for kort", + "image-map-background-from-entity-attribute": "Tag baggrundsbillede fra enhedsattribut", + "image-url-source-entity-alias": "Billed-URL kilde enhedsalias", + "image-url-source-entity-attribute": "Billed-URL kilde enhedsattribut", + "common-map-settings": "Fælles kortindstillinger", + "x-pos-key-name": "X-positionsnøgle", + "y-pos-key-name": "Y-positionsnøgle", + "latitude-key-name": "Breddegradsnøgle", + "longitude-key-name": "Længdegradsnøgle", + "default-map-zoom-level": "Standard zoom-niveau (0 - 20)", + "default-map-center-position": "Standard midtposition (0,0)", + "disable-scroll-zooming": "Deaktiver zoom med scroll", + "disable-double-click-zooming": "Deaktiver zoom med dobbeltklik", + "disable-zoom-control-buttons": "Deaktiver zoomkontrolknapper", + "fit-map-bounds": "Tilpas kortudsnit til at dække alle markører", + "use-default-map-center-position": "Brug standard midtposition", + "entities-limit": "Maks antal enheder at indlæse", + "markers-settings": "Markørindstillinger", + "marker-offset-x": "Markørens X-forskydning i forhold til position × bredde", + "marker-offset-y": "Markørens Y-forskydning i forhold til position × højde", + "position-function": "Positionskonverteringsfunktion (returnerer x,y mellem 0 og 1)", + "draggable-marker": "Trækkelig markør", + "label": "Etiket", + "show-label": "Vis etiket", + "use-label-function": "Brug etiketsfunktion", + "label-pattern": "Etiket (f.eks. '${entityName}', '${entityName}: (Tekst ${keyName} enheder.)')", + "label-function": "Etiketsfunktion", + "tooltip": "Værktøjstip", + "show-tooltip": "Vis værktøjstip", + "show-tooltip-action": "Handling for at vise værktøjstip", + "show-tooltip-action-click": "Klik for at vise værktøjstip (Standard)", + "show-tooltip-action-hover": "Hold musen over for at vise værktøjstip", + "auto-close-tooltips": "Luk værktøjstip automatisk", + "use-tooltip-function": "Brug værktøjstipsfunktion", + "tooltip-pattern": "Værktøjstip (f.eks. 'Tekst ${keyName} enheder.' eller Link tekst)", + "tooltip-function": "Værktøjstipsfunktion", + "tooltip-offset-x": "X-forskydning for værktøjstip i forhold til markørens anker × bredde", + "tooltip-offset-y": "Y-forskydning for værktøjstip i forhold til markørens anker × højde", + "color": "Farve", + "use-color-function": "Brug farvefunktion", + "color-function": "Farvefunktion", + "marker-image": "Markørbillede", + "use-marker-image-function": "Brug markørbilledfunktion", + "custom-marker-image": "Brugerdefineret markørbillede", + "custom-marker-image-size": "Størrelse på brugerdefineret markørbillede (px)", + "marker-image-function": "Markørbilledfunktion", + "marker-images": "Markørbilleder", + "polygon-settings": "Polygonindstillinger", + "show-polygon": "Vis polygon", + "polygon-key-name": "Polygonnøgle", + "enable-polygon-edit": "Tillad redigering af polygon", + "polygon-label": "Polygonetiket", + "show-polygon-label": "Vis polygonetiket", + "use-polygon-label-function": "Brug polygonetiketsfunktion", + "polygon-label-pattern": "Polygonetiket (f.eks. '${entityName}', '${entityName}: (Tekst ${keyName} enheder.)')", + "polygon-label-function": "Polygonetiketsfunktion", + "polygon-tooltip": "Polygon værktøjstip", + "show-polygon-tooltip": "Vis polygon værktøjstip", + "auto-close-polygon-tooltips": "Luk polygon-værktøjstip automatisk", + "use-polygon-tooltip-function": "Brug polygon værktøjstipsfunktion", + "polygon-tooltip-pattern": "Værktøjstip (f.eks. 'Tekst ${keyName} enheder.' eller Link tekst)", + "polygon-tooltip-function": "Polygon værktøjstipsfunktion", + "polygon-color": "Polygonfarve", + "polygon-opacity": "Polygon opacitet", + "use-polygon-color-function": "Brug polygonfarvefunktion", + "polygon-color-function": "Polygonfarvefunktion", + "polygon-stroke": "Polygonkant", + "stroke-color": "Stregfarve", + "stroke-opacity": "Streg opacitet", + "stroke-weight": "Stregtykkelse", + "use-polygon-stroke-color-function": "Brug polygonstregfarvefunktion", + "polygon-stroke-color-function": "Polygonstregfarvefunktion", + "circle-settings": "Cirkelindstillinger", + "show-circle": "Vis cirkel", + "circle-key-name": "Cirkel nøgle", + "enable-circle-edit": "Tillad redigering af cirkel", + "circle-label": "Cirkel etiket", + "show-circle-label": "Vis cirkeletiket", + "use-circle-label-function": "Brug cirkeletiketsfunktion", + "circle-label-pattern": "Cirkeletiket (f.eks. '${entityName}', '${entityName}: (Tekst ${keyName} enheder.)')", + "circle-label-function": "Cirkeletiketsfunktion", + "circle-tooltip": "Cirkel værktøjstip", + "show-circle-tooltip": "Vis cirkel værktøjstip", + "auto-close-circle-tooltips": "Luk cirkel-værktøjstip automatisk", + "use-circle-tooltip-function": "Brug cirkel værktøjstipsfunktion", + "circle-tooltip-pattern": "Værktøjstip (f.eks. 'Tekst ${keyName} enheder.' eller Link tekst)", + "circle-tooltip-function": "Cirkel værktøjstipsfunktion", + "circle-fill-color": "Fyldfarve for cirkel", + "circle-fill-color-opacity": "Fyldfarve opacitet", + "use-circle-fill-color-function": "Brug fyldfarvefunktion", + "circle-fill-color-function": "Fyldfarvefunktion for cirkel", + "circle-stroke": "Cirkelkant", + "use-circle-stroke-color-function": "Brug stregfarvefunktion for cirkel", + "circle-stroke-color-function": "Stregfarvefunktion for cirkel", + "markers-clustering-settings": "Indstillinger for markørgruppering", + "use-map-markers-clustering": "Brug markørgruppering", + "zoom-on-cluster-click": "Zoom ved klik på gruppe", + "max-cluster-zoom": "Maksimalt zoomniveau for at være del af en gruppe (0 - 18)", + "max-cluster-radius-pixels": "Maksimal radius for en gruppe (i pixels)", + "cluster-zoom-animation": "Vis animation ved zoom", + "show-markers-bounds-on-cluster-mouse-over": "Vis markørgrænser ved museovergang på gruppe", + "spiderfy-max-zoom-level": "Spiderfy ved maks zoomniveau (for at vise alle markører)", + "load-optimization": "Indlæsningsoptimering", + "cluster-chunked-loading": "Brug delt indlæsning for at undgå fastfrysning", + "cluster-markers-lazy-load": "Brug lazy-load til markører", + "editor-settings": "Editorindstillinger", + "enable-snapping": "Aktiver snapping til andre hjørner for præcis tegning", + "init-draggable-mode": "Initialiser kort i trækkemodus", + "hide-all-edit-buttons": "Skjul alle redigeringsknapper", + "hide-draw-buttons": "Skjul tegneknapper", + "hide-edit-buttons": "Skjul redigeringsknapper", + "hide-remove-button": "Skjul fjern-knap", + "route-map-settings": "Rutekortindstillinger", + "trip-animation-settings": "Ruteanimationsindstillinger", + "normalization-step": "Normaliserings-trin (ms)", + "tooltip-background-color": "Baggrundsfarve for værktøjstip", + "tooltip-font-color": "Skriftfarve for værktøjstip", + "tooltip-opacity": "Værktøjstip opacitet (0-1)", + "auto-close-tooltip": "Luk værktøjstip automatisk", + "rotation-angle": "Yderligere rotationsvinkel for markør (grader)", + "path-settings": "Stiindstillinger", + "path-color": "Stifarve", + "use-path-color-function": "Brug stifarvefunktion", + "path-color-function": "Stifarvefunktion", + "path-decorator": "Stidekoration", + "use-path-decorator": "Brug stidekoration", + "decorator-symbol": "Dekorationssymbol", + "decorator-symbol-arrow-head": "Pil", + "decorator-symbol-dash": "Streg", + "decorator-symbol-size": "Størrelse på dekorationssymbol (px)", + "use-path-decorator-custom-color": "Brug brugerdefineret farve til dekoration", + "decorator-custom-color": "Dekorationsfarve", + "decorator-offset": "Dekorationsforskydning", + "end-decorator-offset": "Afslut dekorationsforskydning", + "decorator-repeat": "Dekoration gentagelse", + "points-settings": "Punktindstillinger", + "show-points": "Vis punkter", + "point-color": "Punktfarve", + "point-size": "Punktstørrelse (px)", + "use-point-color-function": "Brug punktfarvefunktion", + "point-color-function": "Punktfarvefunktion", + "use-point-as-anchor": "Brug punkt som anker", + "point-as-anchor-function": "Ankerpunkt funktion", + "independent-point-tooltip": "Uafhængig punkt værktøjstip", + "clustering-markers": "Gruppering af markører", + "use-icon-create-function": "Brug markørfarvefunktion", + "marker-color-function": "Markørfarvefunktion" + }, + "markdown": { + "use-markdown-text-function": "Brug markdown/HTML værdifunktion", + "markdown-text-function": "Markdown/HTML værdifunktion", + "markdown-text-pattern": "Markdown/HTML mønster (markdown eller HTML med variabler, f.eks. '${entityName} eller ${keyName} - noget tekst.')", + "apply-default-markdown-style": "Anvend standard markdown-stil", + "markdown-css": "Markdown/HTML CSS" + }, + "simple-card": { + "label": "Etiket", + "label-position": "Etiketplacering", + "label-position-left": "Venstre", + "label-position-top": "Top" + }, + "single-switch": { + "behavior": "Adfærd", + "layout": "Layout", + "layout-right": "Højre", + "layout-left": "Venstre", + "layout-centered": "Centreret", + "auto-scale": "Auto skala", + "label": "Etiket", + "icon": "Ikon", + "switch-color": "Kontakts farve", + "on": "Tændt", + "off": "Slukket", + "disabled": "Deaktiveret", + "tumbler-color": "Tumbler-farve", + "on-label": "Tænd etiket", + "off-label": "Sluk etiket", + "switch": "Kontakt" + }, + "slider": { + "behavior": "Adfærd", + "initial-value": "Startværdi", + "initial-value-hint": "Handling for at hente skyderens startværdi.", + "on-value-change": "Ved værdiændring", + "on-value-change-hint": "Handling udløst når skyderens værdi ændres.", + "layout": "Layout", + "layout-default": "Standard", + "layout-extended": "Udvidet", + "layout-simplified": "Forenklet", + "auto-scale": "Auto skala", + "icon": "Ikon", + "value": "Værdi", + "range": "Interval", + "min": "min", + "max": "maks", + "range-ticks": "Intervalmærker", + "tick-marks": "Mærker", + "colors": "Farver", + "main": "Hoved", + "background": "Baggrund", + "left-icon": "Venstre ikon", + "right-icon": "Højre ikon", + "slider": "Skyder" + }, + "value-card": { + "layout": "Layout", + "layout-square": "Firkantet", + "layout-vertical": "Lodret", + "layout-centered": "Centreret", + "layout-simplified": "Forenklet", + "layout-horizontal": "Vandret", + "layout-horizontal-reversed": "Vandret (omvendt)", + "label": "Etiket", + "icon": "Ikon", + "value": "Værdi", + "date": "Dato", + "value-card-style": "Værdikort stil", + "auto-scale": "Auto skala" + }, + "label-card": { + "auto-scale": "Auto skala", + "label": "Etiket", + "icon": "Ikon", + "label-card-style": "Etikettens kortstil" + }, + "label-value-card": { + "value": "Værdi", + "label-value-card-style": "Etiket & værdi kortstil" + }, + "liquid-level-card": { + "layout-simple": "Simpel", + "layout-percentage": "Procent", + "layout-absolute": "Absolut", + "layout": "Layout", + "background-overlay": "Baggrundsoverlay for værdi", + "total-volume": "Samlet volumen", + "total-volume-units": "Volumen enheder", + "tank": "Tank", + "shape": "Form", + "datasource-units": "Kilde enheder", + "widget-units": "Widget enheder", + "decimals": "Decimaler", + "liquid": "Væske", + "liquid-color": "Væskens farve", + "value": "Værdi", + "value-font": "Værdiens skrifttype", + "level": "Niveau", + "last-update": "Seneste opdatering", + "shape-by-attribute": "Angiv tankform ud fra attributnavn", + "tooltip-background": "Baggrundsfarve", + "background-blur": "Baggrundssløring", + "tank-color": "Tankens farve", + "static": "Statisk", + "see-examples": "Se eksempler", + "attribute": "Attribut", + "shape-type": "Type", + "v-oval": "Lodret oval", + "v-cylinder": "Lodret cylinder", + "v-capsule": "Lodret kapsel", + "rectangle": "Rektangel", + "h-oval": "Vandret oval", + "h-ellipse": "Vandret ellipse", + "h-dish-ends": "Vandret med skålender", + "h-cylinder": "Vandret cylinder", + "h-capsule": "Vandret kapsel", + "h-elliptical_2_1": "Vandret 2:1 elliptisk", + "icon": "Kort ikon", + "title": "Korttitel", + "units": "Enheder", + "color-and-font": "Farve og skrifttype", + "shape-attribute-name": "Attributnavn", + "total-volume-required": "Samlet volumen er påkrævet.", + "attribute-name-required": "Attributnavn er påkrævet.", + "attribute-key-not-set": "Attributnøglen '{{attributeName}}' er ikke angivet", + "attribute-key-invalid": "Attributnøglen '{{attributeName}}' er ugyldig" + }, + "aggregated-value-card": { + "subtitle": "Undertitel", + "chart": "Diagram", + "values": "Værdier", + "value-appearance": "Værdiens udseende", + "position": "Position", + "position-center": "Center", + "position-right-top": "Øverst til højre", + "position-right-bottom": "Nederst til højre", + "position-left-top": "Øverst til venstre", + "position-left-bottom": "Nederst til venstre", + "font": "Skrifttype", + "color": "Farve", + "arrow": "Pil", + "display-up-down-arrow": "Vis op/ned pil", + "add-value": "Tilføj værdi", + "remove-value": "Fjern værdi", + "no-values": "Ingen værdier konfigureret", + "aggregation": "Aggregering", + "aggregated-value-card-style": "Aggregeret værdi kortstil", + "auto-scale": "Auto skala" + }, + "value-chart-card": { + "layout": "Layout", + "layout-left": "Venstre", + "layout-right": "Højre", + "auto-scale": "Auto skala", + "icon": "Ikon", + "value": "Værdi", + "chart": "Diagram", + "value-chart-card-style": "Værdi-diagram kortstil" + }, + "progress-bar": { + "layout": "Layout", + "layout-default": "Standard", + "layout-simplified": "Forenklet", + "auto-scale": "Auto skala", + "icon": "Ikon", + "value": "Værdi", + "range": "Interval", + "min": "min", + "max": "max", + "range-ticks": "Interval-mærker", + "bar": "Bar", + "bar-color": "Barens farve", + "bar-background": "Barens baggrund", + "progress-bar-card-style": "Fremskridtsbjælke kortstil" + }, + "notification": { + "max-notification-display": "Maksimalt antal notifikationer der vises", + "counter": "Tæller", + "counter-hint": "Tælleren vises hvis \"Widgettitel\" er aktiveret", + "icon": "Ikon", + "counter-value": "Værdi", + "counter-color": "Farve", + "notification-button": "Notifikationsknapper", + "button-view-all": "Se alle", + "button-filter": "Filter", + "type-filter": "Typefilter", + "button-mark-read": "Markér alle som læst", + "notification-types": "Notifikationstyper", + "notification-type": "Notifikationstype", + "search-type": "Søg efter type", + "any-type": "Alle typer" + }, + "alarm-count": { + "alarm-count-card-style": "Alarmtæller kortstil" + }, + "entity-count": { + "entity-count-card-style": "Enhedstæller kortstil" + }, + "count": { + "layout": "Layout", + "layout-column": "Kolonne", + "layout-row": "Række", + "label": "Etiket", + "icon": "Ikon", + "icon-background": "Ikon baggrund", + "value": "Værdi", + "chevron": "Pil", + "auto-scale": "Auto skala" + }, + "table": { + "common-table-settings": "Generelle tabelindstillinger", + "enable-search": "Aktiver søgning", + "enable-sticky-header": "Vis altid overskrift", + "enable-sticky-action": "Vis altid handlingskolonne", + "hidden-cell-button-display-mode": "Visningstilstand for skjulte cellehandlinger", + "show-empty-space-hidden-action": "Vis tomt mellemrum i stedet for skjult cellehandling", + "dont-reserve-space-hidden-action": "Reserver ikke plads til skjulte handlingsknapper", + "display-timestamp": "Tidsstempel", + "display-pagination": "Vis sidetal", + "default-page-size": "Standard sidestørrelse", + "page-step-settings": "Indstillinger for sidetrin", + "page-step-count": "Antal trin", + "page-step-increment": "Trinforøgelse", + "page-step-count-format-message": "Skal være en heltalsværdi i intervallet fra 1 til 100.", + "page-step-increment-format-message": "Skal være en heltalsværdi, større end eller lig med 1.", + "use-entity-label-tab-name": "Brug enhedsetiket som fanenavn", + "hide-empty-lines": "Skjul tomme linjer", + "row-style": "Rækkestil", + "use-row-style-function": "Brug rækkestilsfunktion", + "row-style-function": "Rækkestilsfunktion", + "cell-style": "Cellestil", + "use-cell-style-function": "Brug cellestilsfunktion", + "cell-style-function": "Cellestilsfunktion", + "cell-content": "Celleindhold", + "use-cell-content-function": "Brug celleindholdsfunktion", + "cell-content-function": "Celleindholdsfunktion", + "show-latest-data-column": "Vis kolonne med seneste data", + "latest-data-column-order": "Rækkefølge for kolonne med seneste data", + "entities-table-title": "Tabeltitel for enheder", + "enable-select-column-display": "Aktiver valg af viste kolonner", + "display-entity-name": "Vis kolonne for enhedsnavn", + "entity-name-column-title": "Titel på kolonne for enhedsnavn", + "display-entity-label": "Vis kolonne for enhedsetiket", + "entity-label-column-title": "Titel på kolonne for enhedsetiket", + "display-entity-type": "Vis kolonne for enhedstype", + "default-sort-order": "Standard sorteringsrækkefølge", + "custom-title": "Brugerdefineret overskriftstitel", + "column-width": "Kolonnebredde (px eller %)", + "default-column-visibility": "Standard kolonnesynlighed", + "column-visibility-visible": "Synlig", + "column-visibility-hidden": "Skjult", + "column-visibility-hidden-mobile": "Skjult i mobiltilstand", + "column-selection-to-display": "Kolonnevalg i 'Viste kolonner'", + "column-selection-to-display-enabled": "Aktiveret", + "column-selection-to-display-disabled": "Deaktiveret", + "alarms-table-title": "Tabeltitel for alarmer", + "enable-alarms-selection": "Aktiver valg af alarmer", + "enable-alarms-search": "Aktiver søgning i alarmer", + "enable-alarm-filter": "Aktiver alarmfilter", + "display-alarm-details": "Vis alarmdetaljer", + "allow-alarms-ack": "Tillad kvittering af alarmer", + "allow-alarms-clear": "Tillad rydning af alarmer", + "display-alarm-activity": "Vis alarmaktivitet", + "allow-alarms-assign": "Tillad tildeling af alarmer", + "columns": "Kolonner", + "column-settings": "Kolonneindstillinger", + "remove-column": "Fjern kolonne", + "add-column": "Tilføj kolonne", + "no-columns": "Ingen kolonner konfigureret", + "columns-to-display": "Kolonner at vise", + "table-header": "Tabeloverskrift", + "header-buttons": "Overskriftsknapper", + "table-buttons": "Tabelknapper", + "pagination": "Sidetal", + "rows": "Rækker", + "timeseries-column-error": "Mindst én tidsserie-kolonne skal angives", + "alarm-column-error": "Mindst én alarmkolonne skal angives", + "table-tabs": "Tabeller i faner", + "show-cell-actions-menu-mobile": "Vis cellehandlingsmenu i mobiltilstand", + "disable-sorting": "Deaktiver sortering" + }, + "latest-chart": { + "total": "Total", + "auto-scale": "Automatisk skalering", + "clockwise-layout": "Layout med urets retning", + "sort-series": "Sorter serier efter etiket", + "tooltip-value-type-absolute": "Absolut", + "tooltip-value-type-percentage": "Procent" + }, + "pie-chart": { + "pie-chart-appearance": "Udseende for cirkeldiagram", + "label": "Etiket", + "border": "Kant", + "radius": "Radius", + "pie-chart-card-style": "Kortstil for cirkeldiagram" + }, + "radar-chart": { + "radar-appearance": "Udseende for radardiagram", + "shape": "Form", + "shape-polygon": "Polygon", + "shape-circle": "Cirkel", + "color": "Farve", + "line": "Linje", + "points": "Punkter", + "points-label": "Etiket for punkter", + "radar-axis": "Radarakse", + "axis-label": "Aksesetiket", + "ticks-label": "Etiket for mærker", + "radar-chart-style": "Stil for radardiagram" + }, + "time-series-chart": { + "chart": "Diagram", + "chart-style": "Diagramstil", + "data-zoom": "Datavisning (zoom)", + "stack-mode": "Staktilstand", + "stack-mode-hint": "Lægger serier oven på hinanden i diagrammet. Serier med samme enhed placeres oven på hinanden.", + "axes": "Akser", + "y-axes": "Y-akser", + "line-type": "Linetype", + "line-width": "Linjebredde", + "type-line": "Linje", + "type-bar": "Søjle", + "type-point": "Punkt", + "no-aggregation-bar-width-strategy": "Søjlebredde-strategi for ikke-aggregerede data", + "no-aggregation-bar-width-strategy-group": "Grupperet", + "no-aggregation-bar-width-strategy-separate": "Separat", + "bar-group-width": "Søjlgruppebredde", + "bar-width": "Søjlens bredde", + "bar-width-relative": "Procentdel af tidsvindue", + "bar-width-absolute": "Absolut (ms)", + "comparison": { + "comparison": "Sammenligning", + "comparison-hint": "Sammenligning virker kun med historiske data!", + "show": "Vis", + "settings": "Indstillinger for sammenligning", + "show-values-for-comparison": "Vis historiske data til sammenligning", + "comparison-values-label": "Etiket for sammenligningsnøgle", + "comparison-values-label-auto": "Auto", + "comparison-data-color": "Farve for sammenligningsdata" + }, + "threshold": { + "thresholds": "Tærskler", + "source": "Kilde", + "key-value": "Nøgle / Værdi", + "no-thresholds": "Ingen tærskler konfigureret", + "add-threshold": "Tilføj tærskel", + "type-constant": "Konstant", + "type-latest-key": "Nøgle", + "type-entity": "Enhed", + "threshold-settings": "Tærskelindstillinger", + "remove-threshold": "Fjern tærskel", + "threshold-value-required": "Tærskelværdi er påkrævet.", + "key-required": "Nøgle er påkrævet.", + "entity-key-required": "Enhedsnøgle er påkrævet.", + "line-appearance": "Linjens udseende", + "line-color": "Linjefarve", + "start-symbol": "Startsymbol", + "end-symbol": "Slutsymbol", + "symbol-size": "Størrelse", + "label": "Etiket", + "label-position-start": "Start", + "label-position-middle": "Midt", + "label-position-end": "Slut", + "label-position-inside-start": "Inde i start", + "label-position-inside-start-top": "Inde i start top", + "label-position-inside-start-bottom": "Inde i start bund", + "label-position-inside-middle": "Inde i midten", + "label-position-inside-middle-top": "Inde i midten top", + "label-position-inside-middle-bottom": "Inde i midten bund", + "label-position-inside-end": "Inde i slutning", + "label-position-inside-end-top": "Inde i slutning top", + "label-position-inside-end-bottom": "Inde i slutning bund", + "label-background": "Etiketbaggrund" + }, + "state": { + "states": "Tilstande", + "label": "Etiket", + "ticks-value": "Mærkeværdi", + "source": "Kilde", + "value-range": "Værdi / Interval", + "no-states": "Ingen tilstande konfigureret", + "add-state": "Tilføj tilstand", + "type-constant": "Konstant", + "type-range": "Interval", + "from": "Fra", + "to": "Til", + "remove-state": "Fjern tilstand" + }, + "grid": { + "grid": "Gitter", + "background-color": "Baggrundsfarve", + "border": "Kant" + }, + "axis": { + "axes": "Akser", + "x-axis": "X-akse", + "y-axis": "Y-akse", + "y-axis-settings": "Indstillinger for Y-akse", + "comparison-x-axis-settings": "Indstillinger for X-akse (sammenligning)", + "remove-y-axis": "Fjern Y-akse", + "id": "Id", + "label": "Etiket", + "position": "Position", + "position-left": "Venstre", + "position-right": "Højre", + "position-top": "Top", + "position-bottom": "Bund", + "tick-labels": "Mærkeetiketter", + "ticks-formatter-function": "Formatteringsfunktion for mærker", + "ticks-generator-function": "Genereringsfunktion for mærker", + "show-ticks": "Vis mærker", + "show-line": "Vis linje", + "show-split-lines": "Vis opdelingslinjer", + "show-split-lines-x-axis-hint": "Hvis aktiveret, vises de lodrette linjer i diagrammet.", + "show-split-lines-y-axis-hint": "Hvis aktiveret, vises de vandrette linjer i diagrammet.", + "ticks-interval": "Mærkeinterval", + "ticks-interval-hint": "Tvangsindstil segmenteringsinterval for aksen.", + "split-number": "Opdelingsantal", + "split-number-hint": "Antal segmenter som aksen opdeles i.", + "min": "Min", + "max": "Maks", + "show": "Vis", + "add-y-axis": "Tilføj Y-akse" + }, + "series": { + "legend-settings": "Indstillinger for forklaring", + "show-in-legend": "Vis i forklaring", + "show-in-legend-hint": "Vis seriens navn og data i forklaringen.", + "hidden-by-default": "Skjult som standard", + "hidden-by-default-hint": "Gør serien skjult i forklaringen som standard.", + "series-type": "Serietype", + "type": "Type", + "type-line": "Linje", + "type-bar": "Søjle", + "line": { + "line": "Linje", + "show-line": "Vis linje", + "step-line": "Trinvis linje", + "step-type-start": "Start", + "step-type-middle": "Midte", + "step-type-end": "Slut", + "smooth-line": "Glat linje" + }, + "point": { + "points": "Punkter", + "show-points": "Vis punkter", + "point-label": "Punktetiket", + "point-label-hint": "Vis etiket med værdi over datapunktet i serien.", + "point-label-background": "Baggrund for punktetiket", + "point-shape": "Punktform", + "point-size": "Punktstørrelse" + } + } + }, + "wind-speed-direction": { + "layout": "Layout", + "layout-default": "Standard", + "layout-advanced": "Avanceret", + "layout-simplified": "Forenklet", + "values": "Værdier", + "wind-direction": "Vindretning", + "center-value": "Centrale værdi", + "icon": "Ikon", + "arrow": "Pil", + "ticks": "Mærker", + "labels-type": "Etikettype", + "directional-names": "Retningsnavne", + "degrees": "Grader", + "major-ticks": "Hovedmærker", + "minor-ticks": "Undermærker", + "wind-speed-direction-card-style": "Stil for vindhastighed og -retning", + "ticks-color": "Farve på mærker", + "ticks-labels-type": "Etiketter for mærketype", + "arrow-color": "Pilfarve" + }, + "value-source": { + "value-source": "Værdikilde", + "predefined-value": "Konstant", + "entity-attribute": "Entitetsattribut", + "value": "Værdi", + "value-required": "Værdi er påkrævet.", + "key-required": "Nøgle er påkrævet.", + "entity-key-required": "Entitetsnøgle er påkrævet.", + "source-entity-alias": "Kildeentitetsalias", + "source-entity-attribute": "Kildeentitetsattribut", + "type-constant": "Konstant", + "type-latest-key": "Nøgle", + "type-entity": "Entitet" + }, + "rpc-state": { + "initial-state": "Initial tilstand", + "initial-state-hint": "Handling for at hente komponentens starttilstand (Tænd/Sluk).", + "disabled-state": "Deaktiveret tilstand", + "disabled-state-hint": "Konfigurer betingelse hvorunder komponenten er deaktiveret.", + "turn-on": "Tænd", + "turn-on-hint": "Handling udført når skyderen skiftes til 'Tænd'", + "turn-off": "Sluk", + "turn-off-hint": "Handling udført når skyderen skiftes til 'Sluk'", + "on": "Tændt", + "off": "Slukket", + "disabled": "Deaktiveret" + }, + "value-action": { + "do-nothing": "Gør intet", + "execute-rpc": "Udfør RPC", + "get-attribute": "Hent attribut", + "set-attribute": "Angiv attribut", + "get-time-series": "Hent tidsserier", + "get-alarm-status": "Hent alarmstatus", + "get-dashboard-state": "Hent dashboardtilstands-id", + "get-dashboard-state-object": "Hent dashboardtilstandsobjekt", + "add-time-series": "Tilføj tidsserier", + "execute-rpc-text": "Udfør RPC-metode '{{methodName}}'", + "get-time-series-text": "Brug tidsserie '{{key}}'", + "get-attribute-text": "Brug attribut '{{key}}'", + "get-alarm-status-text": "Brug alarmstatus", + "get-dashboard-state-text": "Brug dashboardtilstand", + "get-dashboard-state-object-text": "Brug dashboardtilstandsobjekt", + "when-dashboard-state-is-text": "Når dashboard-tilstand er '{{state}}'", + "when-dashboard-state-function-is-text": "Når f(dashboard-tilstand) er '{{state}}'", + "when-dashboard-state-object-function-is-text": "Når f(dashboardtilstandsobjekt) er '{{state}}'", + "set-attribute-to-value-text": "Angiv '{{key}}' attribut til: {{value}}", + "add-time-series-value-text": "Tilføj '{{key}}' tidsserieværdi: {{value}}", + "set-attribute-text": "Angiv '{{key}}' attribut", + "add-time-series-text": "Tilføj '{{key}}' tidsserie", + "action": "Handling", + "value": "Værdi", + "init-value-hint": "Værdi der indstilles indtil enhed sender data.", + "method": "Metode", + "method-name-required": "Metodenavn er påkrævet.", + "request-timeout-ms": "RPC anmodning timeout (ms)", + "request-timeout-required": "Timeout for anmodning er påkrævet.", + "min-request-timeout-error": "Timeoutværdi skal være mindst 5000 ms (5 sekunder).", + "request-persistent": "Vedvarende RPC-anmodning", + "persistent-polling-interval": "Interval for vedvarende polling (ms)", + "persistent-polling-interval-hint": "Polling-interval (ms) for at hente svar på vedvarende RPC-kommando", + "persistent-polling-interval-required": "Polling-interval er påkrævet.", + "min-persistent-polling-interval-error": "Polling-interval skal være mindst 1000 ms (1 sekund).", + "attribute-scope": "Attribut-område", + "attribute-key": "Attributnøgle", + "attribute-key-required": "Attributnøgle er påkrævet.", + "time-series-key": "Tidsserienøgle", + "time-series-key-required": "Tidsserienøgle er påkrævet.", + "action-result-converter": "Resultatomformer for handling", + "converter-none": "Ingen", + "converter-function": "Funktion", + "converter-constant": "Konstant", + "converter-value": "Værdi", + "parse-value-function": "Fortolk værdifunktion", + "state-when-result-is": "'{{state}}' når resultat er", + "parameters": "Parametre", + "convert-value-function": "Konverter værdifunktion", + "error": { + "target-entity-is-not-set": "Målenhed er ikke angivet!", + "failed-to-perform-action": "Kunne ikke udføre {{ actionLabel }} handling.", + "invalid-attribute-scope": "{{scope}} attributområde understøttes ikke af {{entityType}}-entitet." + } + }, + "widget-font": { + "font-settings": "Skrifttypeindstillinger", + "font-family": "Skrifttypefamilie", + "size": "Størrelse", + "relative-font-size": "Relativ skriftstørrelse (procent)", + "font-style": "Stil", + "font-style-normal": "Normal", + "font-style-italic": "Kursiv", + "font-style-oblique": "Skrå", + "font-weight": "Vægt", + "font-weight-normal": "Normal", + "font-weight-bold": "Fed", + "font-weight-bolder": "Federe", + "font-weight-lighter": "Tyndere", + "color": "Farve", + "shadow-color": "Skyggefarve", + "preview": "Forhåndsvisning", + "line-height": "Linjeafstand", + "auto": "Auto" + }, + "home": { + "no-data-available": "Ingen data tilgængelige" + }, + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "Disk", + "cpu-warning-text": "Højt CPU-forbrug. Optimer ydeevnen for at undgå systemfejl.", + "cpu-critical-text": "Kritisk højt CPU-forbrug. Optimer ydeevnen for at undgå systemfejl.", + "ram-warning-text": "Lav RAM-reserve. Optimer ydeevnen eller øg RAM-størrelsen for at undgå systemfejl.", + "ram-critical-text": "Kritisk lav RAM-reserve. Optimer ydeevnen eller øg RAM-størrelsen for at undgå systemfejl.", + "disk-warning-text": "Lav diskplads. Frigør eller udvid diskpladsen for at undgå datatab.", + "disk-critical-text": "Kritisk lav diskplads. Frigør eller udvid diskpladsen for at undgå datatab." + }, + "cluster-info": { + "service-id": "Service-id", + "service-type": "Servicetype", + "no-data": "Ingen data" + }, + "transport-messages": { + "title": "Transportbeskeder", + "info": "Alle beskeder der kommer fra enheder" + }, + "activity": { + "title": "Aktivitet" + }, + "documentation": { + "title": "Dokumentation", + "add-link": "Tilføj link", + "add-link-title": "Tilføj dokumentationslink", + "name": "Navn", + "name-required": "Navn er påkrævet.", + "link": "Link", + "link-required": "Link er påkrævet.", + "columns": "Kolonner" + }, + "quick-links": { + "title": "Genveje", + "add-link": "Tilføj link", + "add-link-title": "Tilføj genvej", + "quick-link": "Genvej", + "quick-link-required": "Genvej er påkrævet.", + "no-links-matching": "Ingen links matcher '{{name}}'.", + "columns": "Kolonner" + }, + "recent-dashboards": { + "title": "Dashboards", + "last": "Sidst vist", + "starred": "Favoritter", + "name": "Navn", + "last-viewed": "Sidst vist", + "no-last-viewed-dashboards": "Ingen dashboards er blevet vist endnu" + }, + "configured-features": { + "title": "Konfigurerede funktioner", + "info": "Status for funktioner, der kræver konfiguration", + "email-feature": "E-mail", + "sms-feature": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "feature-configured": "Funktionen er konfigureret.\nKlik for at opsætte", + "feature-not-configured": "Funktionen er ikke konfigureret.\nKlik for at opsætte" + }, + "version-info": { + "title": "Version", + "contact-us": "Kontakt os", + "current-version": "Nuværende version", + "current": "Nuværende", + "available-version": "Tilgængelig version", + "available": "Tilgængelig", + "upgrade": "Opgrader", + "version-is-up-to-date": "Versionen er opdateret" + }, + "usage-info": { + "title": "Forbrug", + "entities": "Entiteter", + "api-calls": "API-kald" + }, + "functions": { + "title": "Funktioner", + "pe-feature-tooltip": "Kun i ThingsBoard\nProfessional Edition", + "switch-to-pe": "Skift til PE", + "alarms": "Alarmer", + "dashboards": "Dashboards", + "entities-and-relations": "Entiteter og relationer", + "profiles": "Profiler", + "advanced-features": "Avancerede funktioner", + "notification-center": "Notifikationscenter", + "api-usage": "API-forbrug", + "customers": "Kunder", + "customers-hierarchy": "Kundehierarki", + "roles-and-permissions": "Roller og tilladelser", + "groups": "Grupper", + "integrations": "Integrationer", + "solution-templates": "Løsningsskabeloner", + "scheduler": "Tidsplanlægger", + "white-labeling": "White-labeling" + }, + "devices": { + "view-docs": "Se dokumentation", + "inactive": "Inaktiv", + "active": "Aktiv", + "total": "I alt" + }, + "alarms": { + "critical": "Kritisk", + "assigned-to-me": "Tildelt mig", + "total": "I alt" + }, + "getting-started": { + "get-started": "Kom i gang", + "finish": "Afslut", + "done-welcome-title": "Velkommen ombord", + "done-welcome-text": "Du klarede det fantastisk!", + "sys-admin": { + "step1": { + "title": "Opret Lejer & Lejeradministrator", + "content": "

En lejer er en person eller organisation, der ejer eller producerer enheder og aktiver. Lejeren kan have flere lejeradministratorbrugere, kunder, enheder og aktiver.

Lejeradministratoren kan oprette og administrere enheder, aktiver, kunder og dashboards inden for lejerkontoen.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-create-tenant": "Sådan opretter du Lejer & Lejeradministrator" + }, + "step2": { + "title": "Konfigurer funktion: Mailserver", + "content": "

Konfiguration af mailserver er vigtig for aktivering af brugere, gendannelse af adgangskoder og levering af alarmnotifikationer.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-configure-mail-server": "Sådan konfigureres Mailserver" + }, + "step3": { + "title": "Konfigurer funktion: SMS-udbyder", + "content": "

Konfigurer SMS-udbydere for at kunne underrette kunder om alarmer via SMS.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-configure-sms-provider": "Sådan konfigureres SMS-udbyder" + }, + "step4": { + "title": "Konfigurer funktion: White-labeling", + "content": "

Tilpas nemt din virksomheds eller produkts logo og farveskema uden kodning og uden genstart af tjenesten.

Følg dokumentationen for at lære, hvordan du gør det:

" + }, + "step5": { + "title": "Konfigurer funktion: 2FA", + "content": "

Forbedr sikkerheden på platformens konti med tofaktorautentificering.

Følg dokumentationen for at lære, hvordan du gør det:

" + }, + "step6": { + "title": "Konfigurer funktion: OAuth 2", + "content": "

Gør login lettere for lejer- og kundebrugere med Single Sign-On-funktionalitet via OAuth 2.0.

Følg dokumentationen for at lære, hvordan du gør det:

" + } + }, + "tenant-admin": { + "step1": { + "title": "Opret enhed", + "content": "

Lad os klargøre din første enhed på platformen via brugerfladen. Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-create-device": "Sådan oprettes Enhed" + }, + "step2": { + "title": "Tilslut enhed", + "content-before": "

For at tilslutte enheden skal du hente enhedslegitimationsoplysningerne. Vi anbefaler at bruge standard, automatisk genererede legitimationsoplysninger – adgangstoken – til denne vejledning.

  • Gå til enhedstabellen
  • Klik på enhedsrækken for at åbne enhedsdetaljer
  • Tryk på knappen \"Kopiér adgangstoken\"

Brug simple kommandoer til at sende data via HTTP. Husk at erstatte $ACCESS_TOKEN med din enheds adgangstoken:

", + "ubuntu": { + "install-curl": "Installer cURL til Ubuntu:" + }, + "macos": { + "install-curl": "Installer cURL til MacOS:" + }, + "windows": { + "install-curl": "Fra og med Windows 10 b17063 er cURL tilgængelig som standard." + }, + "replace-access-token": "Erstat $ACCESS_TOKEN med din enheds token:", + "content-after": "

Du kan også bruge andre protokoller såsom MQTT, CoAP osv.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-connect-device": "Sådan tilsluttes Enhed" + }, + "step3": { + "title": "Opret dashboard", + "content": "

Opret et dashboard for at visualisere data fra entiteter såsom aktiver, enheder m.m.

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-create-dashboard": "Sådan oprettes Dashboard" + }, + "step4": { + "title": "Konfigurer alarmregler", + "alarm-rules": "Alarmregler", + "content": "

Lad os udløse en alarm, når temperaturen når 25°C. Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-configure-alarm-rules": "Sådan konfigureres Alarmregler" + }, + "step5": { + "title": "Opret alarm", + "content-before": "

For at udløse alarmen skal du sende en ny telemetriværdi på 26°C eller højere.

", + "replace-access-token": "Erstat $ACCESS_TOKEN med din enheds token:", + "content-after": "

Følg dokumentationen for at lære, hvordan du gør det:

", + "how-to-create-alarm": "Sådan oprettes Alarm" + }, + "step6": { + "title": "Opret kunde og del dashboard", + "content": "

Ved at oprette dashboards til slutbrugere kan en kundebruger kun se sine egne enheder, mens data fra andre kunder skjules.

Følg dokumentationen for at lære, hvordan du gør det:

" + } + } } }, "icon": { "icon": "Ikon", + "icons": "Ikoner", "select-icon": "Vælg ikon", "material-icons": "Materialeikoner", - "show-all": "Vis alle ikoner" - }, - "subscription": { - "entity-limit-text": "Du kan dog opgradere din abonnementsordning for at øge dine begrænsninger.", - "upgrade-your-plan": "Opgrader abonnementsordning", - "white-labeling-feature": "Funktion til hvid mærkning", - "white-labeling-text-full": "Rebrand ThingsBoard-platformens webinterface med dit virksomheds- eller produktlogo og farveskema på 2 minutter.

Fjern “Powered By” i dashboardets sidefod.
Ingen kodning eller genstart af tjeneste påkrævet. Giv også dine kunder mulighed for at hvidmærke deres interface.", - "enable-white-labeling": "Aktivér hvidmærkningsfunktionen nu ved at opgradere din abonnementsordning!", - "read-more": "Læs mere", - "white-labeling-video-text": "Se videovejledningen nedenfor for at se, hvordan denne funktion fungerer!" + "show-all": "Vis alle ikoner", + "search-icon": "Søg ikon", + "no-icons-found": "Ingen ikoner fundet for '{{iconSearch}}'" }, - "subscription-error": { - "title": "Misligholdelse af abonnement", - "warning-title": "Abonnementsadvarsel", - "limit-reached": { - "device-count": "", - "asset-count": "" - }, - "feature-disabled": { - "white-labeling": "Hvidmærkningsfunktionen er ikke tilladt af din abonnementsordning!" - } + "phone-input": { + "phone-input-label": "Telefonnummer", + "phone-input-required": "Telefonnummer er påkrævet", + "phone-input-validation": "Telefonnummeret er ugyldigt eller ikke muligt", + "phone-input-pattern": "Ugyldigt telefonnummer. Skal være i E.164-format, f.eks. {{phoneNumber}}", + "phone-input-hint": "Telefonnummer i E.164-format, f.eks. {{phoneNumber}}" }, "custom": { "widget-action": { - "action-cell-button": "Handlingscelleknap", + "action-cell-button": "Handlingsknap i celle", "row-click": "Ved klik på række", + "cell-click": "Ved klik på celle", "polygon-click": "Ved klik på polygon", "marker-click": "Ved klik på markør", - "tooltip-tag-action": "Værktøjstip for taghandling", - "node-selected": "På valgt knude", + "circle-click": "Ved klik på cirkel", + "tooltip-tag-action": "Handlingsmærke i værktøjstip", + "node-selected": "Ved valg af node", "element-click": "Ved klik på HTML-element", - "pie-slice-click": "Ved klik på udsnit", - "row-double-click": "Ved dobbeltklik på række" - }, - "add-new-facility": { - "add-entity": "Tilføj placering", - "facility-name": "placeringsnavn", - "facility-name-required": "placeringsnavn er påkrævet.", - "title": "Tilføj ny placering" - }, - "delete-facility": { - "are-you-sure-question": "Er du sikker på, at du vil slette placering '{{facilityName}}'?", - "title": "Slet placering" - }, - "facilities": { - "set-location-and-facility-map": "Indstil placering" - }, - "general": { - "account": "Konto", - "accounts": "Konti", - "activation-link-is-sent-to-the-email-address": "Aktiveringslink er sendt til e-mailadressen: '{{email}}'", - "add-ecl": "Tilføj ECL", - "add-ecl-device": "Tilføj ECL-enhed", - "add-edit-ally-email": "Tilføj Ally™ konto", - "add-edit-entity-ally-account": "Tilføj {{ entityName }}'s Ally™ konto", - "add-home": "Tilføj Hjem", - "add-home-area": "Tilføj Hjemmeområde", - "add-new-ally-account": "Tilføj ny konto", - "add-new-location": "Tilføj ny placering", - "add-new-read-only-user": "Tilføj ny skrivebeskyttet bruger", - "add-new-read-only-user-for-facility": "Tilføj ny skrivebeskyttet bruger for '{{facilityName}}'", - "add-vacation-for-zone": "Tilføj ferie for denne zone", - "open-calendar": "Åbn kalender", - "add-zone": "Tilføj zone", - "alarm-code": "Alarmkode", - "alarm-description": "Beskrivelse", - "alarm-message": "Meddelelse", - "alarms-disabled-msg": "Alarmer er deaktiveret.", - "alarms-disabled-state": "Deaktiveret", - "alarms-enabled-msg": "Alarmer er aktiveret.", - "alarms-enabled-state": "Aktiveret", - "all-devices-in-home": "Alle enheder i hjemmet", - "ally-email": "Ally™ e-mail", - "ally-email-access-check-email": "Tjek din e-mail {{ allyEmail }} for at se den 4-cifrede kode fra Ally™ Pro", - "ally-email-access-code-label": "Indtast din adgangskode (4 cifre)", - "ally-email-account-is-missing-or-is-not-valid": "Ally™ e-mailkonto mangler eller er ikke gyldig", - "an-error-occurred-entities-try-again": "Der opstod en fejl under sletning af enhederne. Prøv igen.", - "an-error-occurred-entity-try-again": "Der opstod en fejl under sletning af enheden. Prøv igen.", - "an-error-occurred-please-try-again": "Der opstod en fejl. Prøv igen.", - "app": "App", - "app-version": "Appversion", - "area-name-is-required": "Områdenavn er påkrævet.", - "assign-existing-user-to-facility": "Tildel eksisterende bruger til placering", - "assign-user-to-facility": "Tildel bruger til '{{facilityName}}'", - "available-devices": "Tilgængelige enheder", - "battery": "Batteri", - "calendar": "Kalender", - "cant-remove-yourself": "Du kan ikke fjerne dig selv fra placeringen.", - "change-temperature": "Ændr temperatur", - "change-zone-map": "Skift zonekort", - "child-lock": "Børnesikring", - "child-lock-disabled": "Deaktiveret", - "child-lock-enabled": "Aktiveret", - "circuit": "Kreds", - "comfort": "Komfort", - "comfort-dhw-setpoint": "Sætpunkt for Komfort varmt brugsvand", - "comfort-dhw-setpoint-is-required": "Sætpunkt for Komfort varmt brugsvand er påkrævet.", - "comfort-room": "Komfort rum", - "comfort-room-setpoint": "Sætpunkt for Komfort rum", - "comfort-room-setpoint-is-required": "Sætpunkt for Komfort rum er påkrævet.", - "confirm-btn": "Bekræft", - "confirm-delete-device": "Er du sikker på, du ønsker at slette enhed '{{deviceName}}'?", - "confirm-delete-devices": "Er du sikker på, du ønsker at slette {{numberOfDevices}} enheder?", - "confirm-delete-home": "Er du sikker på, du ønsker at slette hjem '{{homeName}}'; deres zoner og enheder?", - "confirm-delete-zone": "Er du sikker på, du ønsker at slette zonen '{{zoneName}}'?", - "confirm-remove-user": "Er du sikker på, du ønsker at fjerne {{ thisUser }} fra lokation {{ locationName }}", - "constant-comfort-temperature": "Konstant komforttemperatur", - "constant-setback-temperature": "Konstant sænkningstemperatur", - "coordinates": "Koordinater", - "create-ecl-home-without-ally-account": "Opret ECL hjem uden Ally™ konto", - "create-vacation-event": "Opret feriebegivenhed", - "current-humidity": "Luftfugtighed", - "current-temp": "Aktuel temp.", - "current-temperature": "Aktuel temperatur", - "default-flow-temperature-at-minus-30": "Standard fremløbstemperatur ved -30 °C", - "default-flow-temperature-at-minus-15": "Standard fremløbstemperatur ved -15 °C", - "default-flow-temperature-at-minus-5": "Standard fremløbstemperatur ved -5 °C", - "default-flow-temperature-at-zero": "Standard fremløbstemperatur ved 0 °C", - "default-flow-temperature-at-plus-5": "Standard fremløbstemperatur ved 5 °C", - "default-flow-temperature-at-plus-15": "Standard fremløbstemperatur ved 15 °C", - "delete-account": "Slet konto", - "delete-all-devices": "Slet alle enheder", - "delete-device-from-home": "Slet enhed fra hjem", - "delete-devices": "Slet enheder", - "delete-facility-account": "Slet konto", - "delete-home": "Slet hjem", - "delete-user": "Slet bruger", - "delete-zone": "Slet zone", - "device-id": "Enheds-id", - "device-is-now-removed-from-this-zone": "Enheden er nu fjernet fra denne zone.", - "device-manual_mode-updated": "Manuel tilstand er indstillet for denne enhed.", - "device-name": "Enhedsnavn", - "device-status-updated": "Enhedsstatus opdateret!", - "device-type": "Enhedstype", - "devices-found-in-home": "Enheder fundet i {{ homeName }}", - "devices-imported": "Enheder importeret!", - "devices-in-zone": "Enheder i zone", - "devices-placed-into-zone": "{{numberOfDevices}} enhed(er) placeret i zone.", - "dhw-circuit": "Varmt brugsvandskreds", - "domestic-hot-water-circuit-menu": "Varmt brugsvand (DHW), menu for kreds", - "duplicate-devices": "Duplikerede enheder", - "duplicate-devices-list": "Du har allerede importeret enheder: {{listOfDevices}}", - "duplicate-devices-were-not-imported": "Duplikerede enheder: {{listOfDevices}} blev ikke importeret.", - "ecl-access-code": "ECL-adgangskode", - "ecl-device-added": "Din ECL-enhed {{device}} er blevet tilføjet.", - "ecl-diagram-title": "Nyt ECL-diagram", - "ecl-diagram-title-a-230-1": "Nyt ECL-diagram A230.1", - "ecl-diagram-title-a-247-1": "Nyt ECL-diagram A247.1", - "ecl-diagram-title-a-260-1": "Nyt ECL-diagram A260.1", - "ecl-diagram-title-a-266-1": "Nyt ECL-diagram A266.1", - "ecl-diagram-title-a-347-1": "Nyt ECL-diagram A347.1", - "ecl-diagram-title-a-376-1": "Nyt ECL-diagram A376.1", - "ecl-diagram-title-a-390-1": "Nyt ECL-diagram A390.1", - "ecl-heating-circuit-has-been-successfully-set": "ECL-varmekredsen er blevet indstillet.", - "ecl-menu": "ECL-menu", - "ecl-mode": "Tilstand", - "ecl-mvp-menu": "ECL MVP-menu", - "ecl-status": "Status", - "edit-facilities-access": "Rediger adgang til faciliteter", - "edit-facility-permission": "Rediger placeringstilladelse", - "edit-user": "Rediger bruger", - "edit-zone": "Rediger zone", - "eg-read-only": "f.eks. skrivebeskyttet", - "energy": "Energi", - "error-occurred": "Der opstod en fejl", - "etrv-update-temperature": "Opdater temperatur", - "facilities": "Faciliteter", - "facilities-for-entity-as-read-only-user": "Faciliteter for '{{entityName}}' som skrivebeskyttet bruger", - "facility-area-name": "placeringsområdenavn", - "facility-managers": "Facility Managers", - "facility-name-is-required": "placeringsnavn er påkrævet.", - "floor-map": "Plantegning", - "floors-in-the-room": "Gulve i rummet", - "flow-heating-curve-parameters-history": "Historik over parametre for flowvarmekurve", - "flow-temperature": "Fremløbstemperatur", - "flow-temperature-at-minus-15": "Fremløbstemperatur ved -150 °C", - "flow-temperature-at-minus-30": "Fremløbstemperatur ved -30 °C", - "flow-temperature-at-minus-5": "Fremløbstemperatur ved -5 °C", - "flow-temperature-at-plus-15": "Fremløbstemperatur ved 15 °C", - "flow-temperature-at-plus-5": "Fremløbstemperatur ved 5 °C", - "flow-temperature-at-zero": "Fremløbstemperatur ved 0 °C", - "flow-temperature-curve": "Fremløbstemperaturkurve", - "flow-temperature-values": "Fremløbstemperaturværdier", - "flow-temperature-without-optimization": "Fremløbstemperatur uden optimering", - "gateway-subdevices": "Gateway-underenheder", - "get-subdevices": "Hent underenheder", - "global-temp-set-msg": "Global temperatur er indstillet.", - "go-to-manual": "Gå til manuel tilstand", - "hardware": "Hardware", - "heating-circuit": "Varmekreds", - "heating-circuit-menu": "Menu for varmekreds", - "holiday-7-23h-comfort-temperature": "Ferie 7-23 t komforttemperatur", - "holiday-constant-comfort-temperature": "Ferie, konstant komforttemperatur", - "holiday-constant-setback-temperature": "Ferie, konstant sænkningstemperatur", - "holiday-frost-protection-standby": "Ferie, frostsikring/standby", - "home-alarms": "Hjemmealarmer", - "home-map": "Hjemmekort", - "home-map-with-zones": "Zoner", - "home-name": "Hjemmenavn", - "home-name-is-required": "Hjemmenavn er påkrævet", - "homes": "Hjem", - "homes-and-zones": "Hjem og zoner", - "homes-imported": "Hjem importeret!", - "homes-successfully-imported": "Hjem blev importeret!", - "import-ally-homes-from-the-button": "Importér Ally™ hjem fra knappen med ikonet 'hus'.", - "import-devices": "Importér enheder", - "import-devices-from-home": "Importer enheder fra hjem", - "import-homes": "Importer hjem", - "information": "Information", - "information-will-be-sent-via-email-to-this-user": "Information sendes via e-mail til denne bruger", - "invalid-temp": "Ugyldig temperaturværdi", - "link-to-ecl": "Link til ECL", - "linked-devices": "Tilknyttede enheder", - "list-of-subdevices": "Liste over underenheder", - "location-city": "Placering, by", - "location-country": "Placering, landområder", - "location-on-maps": "Placering på kort", - "location-street": "Placering, vej", - "location-street-number": "Placering, vejnummer", - "location-successfully-created": "Placeringen blev oprettet!", - "location-zip": "Placering, postnummer", - "locations": "Placeringer", - "lower-temperature": "Lavere temperatur", - "manage-home": "Administrer hjem", - "manual-operation": "Manuel drift", - "manual-temp": "Manuel temp.", - "manual-temperature": "Manuel temperatur", - "map": "Kort", - "max-flow-temperature": "Maks. Fremløbstemperatur", - "min-flow-temperature": "Min. Fremløbstemperatur", - "mode": "Tilstand", - "mode-at-home": "Hjemme", - "mode-leaving-home": "Ikke til stede", - "mode-manual": "Manuel", - "mode-pause": "Pause", - "mode-vacation": "Ferie", - "new-entities-table": "Tabel over nye enheder", - "new-led-indicator": "Ny LED-indikator", - "new-scheduler-events": "Nye planlægningsbegivenheder", - "no-devices-found": "Ingen enheder fundet.", - "no-ecl-devices-found": "Ingen ECL-enheder fundet", - "no-vacations": "Ingen planlagte begivenheder", - "number-of-devices-imported": "{{numberOfDevices}} enhed(er) importeret.", - "offline-status": "Offline", - "online": "Online", - "online-status": "Online", - "outdoor-temperature": "Udendørstemperatur", - "override-temperature": "Overstyringstemperatur", - "overview": "Oversigt", - "owner-name": "Ejerens navn", - "parent-facility-name": "Overordnet placeringsnavn", - "pi-heating-demand": "Pi Heating Demand", - "pi-heating-demand-history": "Pi Heating Demand History", - "pi-heating-demand-values-history": "Pi Heating Demand Values History", - "place-devices-in-this-zone": "Placer enheder i denne zone", - "place-into-zone": "Placer i zone", - "please-choose-user-role": "Vælg brugerrolle", - "pre-comfort": "Forkomfort", - "pre-setback": "Forsænkning", - "production-week": "Produktionsuge", - "read-only-users": "Skrivebeskyttede brugere", - "real-etrv-current-temp": "Termostattemperatur", - "refresh-device-status": "Opdater enhedsstatus", - "remove-from-zone": "Fjern fra zone", - "remove-user-from-loc": "Fjern bruger fra denne placering", - "remove-user-title": "Fjern bruger", - "resend-code": "Send kode igen", - "resend-code-msg": "Din kode er sendt! Tjek din e-mail.", - "return-temp": "Returløbstemperatur", - "room-map": "Kort over rum", - "save-comfort-dhw-setpoint": "Gem sætpunkt for 'Komfort varmt brugsvand'", - "save-saving-dhw-setpoint": "Gem sætpunktet 'Gemmer varmt brugsvand'", - "saving-dhw-setpoint": "Gemmer sætpunktet varmt brugsvand", - "saving-room": "Gemmer rum", - "saving-room-setpoint": "Gemmer sætpunkt for rum", - "saving-room-setpoint-is-required": "Gemmer sætpunkt for rum er påkrævet.", - "scheduled-operation": "Planlagt drift", - "scheduler-events": "Planlægningsbegivenheder", - "see-advanced-settings-for-details": "Se avancerede indstillinger for at få yderligere oplysninger", - "see-areas": "se områder", - "see-devices-in-home": "Se enheder i hjemmet", - "see-information": "Se oplysninger", - "selected-devices-are-successfully-linked-with-ecl-device": "Selected device(s) are successfully linked with ECL device.", - "serial-number": "Serienummer", - "set-ecl-heating-circuit": "Set ECL heating circuit", - "select-ecl-device": "Vælg ECL-enhed...", - "device-are-linked-to-ecl": "Den eller de valgte enheder er med succes forbundet med ECL-enhed.", - "unlink-all-devices": "Fjern linket til alle enheder fra ECL", - "unlink-device": "Fjern forbindelsen mellem enhed fra ECL", - "see-pi-history": "Se PI-historik", - "device-unlinked-msg": "Enheden er fjernet fra ECL", - "set-global-temp": "Indstil den globale temperatur", - "are-you-sure-unlink-all": "Er du sikker på at fjerne linket til ALLE enheder fra ECL?", - "are-you-sure-unlink-device": "Er du sikker på at fjerne linket mellem enhed {{device}} og ECL-controlleren?", - "set-temp": "Indstil temp.", - "set-temperature": "Ønsket temperatur", - "set-vacation": "Indstil ferie", - "setback": "Sænkning", - "setback-dhw-setpoint-is-required": "Gemmer sætpunktet varmt brugsvand er påkrævet.", - "software": "Software", - "software-build-no": "Softwarebuildnr.", - "something-went-wrong": "Noget gik galt!", - "subdevices-information-saved": "Gateway-underenheder gemt.", - "successfully-inserted-devices": "{{numberOfDevices}} enhed(er) blev indsat.", - "temp-required": "Temperaturværdi er påkrævet", - "temperature": "Temperatur", - "this-user": "denne bruger", - "title-homes": "Hjem", - "title-location": "Placering", - "update-area-map": "Opdater områdekort", - "update-facilities": "Opdater faciliteter", - "update-home-map": "Opdater hjemmekort", - "update-zone-map": "Opdater zonekort", - "upper-temperature": "Øvre temperatur", - "user-activation-link-has-been-copied-to-clipboard": "Brugeraktiveringslinket er blevet kopieret til udklipsholderen", - "user-removed-fm": "Bruger fjernet som Facility Manager fra denne placering.", - "user-removed-ro": "Bruger fjernet som skrivebeskyttet bruger fra denne placering.", - "user-was-successfully-assigned-to-the-facility": "Brugeren blev tildelt {{facilityName}}", - "user-with-email-already-exists": "Bruger med e-mail '{{email}}' findes allerede!", - "user-with-this-email-is-already-assigned-to-the-facility": "Bruger med denne e-mail er allerede tildelt '{{facilityName}}'.", - "user-with-this-email-was-not-found": "Bruger med denne e-mail blev ikke fundet.", - "vacation-plan": "Ferieplan", - "view-details": "Vis detaljer", - "view-device-information": "Vis enhedsoplysninger", - "view-home": "Vis hjem", - "view-information": "Vis oplysninger", - "view-users": "Brugere", - "view-zone": "Vis zone", - "warning-uppercase": "ADVARSEL", - "were-not-imported": "blev ikke importeret", - "window-state": "Vinduestilstand", - "work-state": "Arbejdstilstand", - "you-dont-have-attribute-free": "Du har ikke en attribut, der er ledig til placeringsnavn!", - "you-have-valid-ally-account": "Du har en gyldig Ally™ konto.", - "your-ally-email-account": "Din Ally™ e-mailkonto", - "your-facilities": "Dine faciliteter", - "your-home-was-successfully-deleted": "Dit hjem blev slettet.", - "zone": "zone", - "zone-have-no-devices-msg": "Der er ingen enheder placeret i zonen. Placer venligst nogle enheder.", - "zone-management": "Zonestyring", - "zone-name": "Navn", - "zone-name-is-required": "Zonenavn er påkrævet.", - "zone-temp-not-enabled": " ikke aktiveret.", - "zone-temp-sett": "Indstilling af temperaturen", - "zone-temp-sett-tooltip": "Ændr temperaturen", - "zone-temperature": "Zonetemperatur", - "zone-view": "Zonevisning" + "pie-slice-click": "Ved klik på lagkageskive", + "row-double-click": "Ved dobbeltklik på række", + "cell-double-click": "Ved dobbeltklik på celle", + "card-click": "Ved klik på kort", + "click": "Ved klik" } }, + "paginator": { + "items-per-page": "Elementer pr. side:", + "first-page-label": "Første side", + "last-page-label": "Sidste side", + "next-page-label": "Næste side", + "previous-page-label": "Forrige side", + "items-per-page-separator": "af" + }, "language": { - "language": "Sprog" + "language": "Language", + "locales": { + "ar_AE": "العربية (الإمارات العربية المتحدة)", + "ca_ES": "català (Espanya)", + "cs_CZ": "čeština (Česko)", + "da_DK": "dansk (Danmark)", + "de_DE": "Deutsch (Deutschland)", + "el_GR": "Ελληνικά (Ελλάδα)", + "en_US": "English (United States)", + "es_ES": "español (España)", + "fa_IR": "فارسی (ایران)", + "fr_FR": "français (France)", + "it_IT": "italiano (Italia)", + "ja_JP": "日本語 (日本)", + "ka_GE": "ქართული (საქართველო)", + "ko_KR": "한국어 (대한민국)", + "lt_LT": "lietuvių (Lietuva)", + "lv_LV": "latviešu (Latvija)", + "nl_BE": "Nederlands (België)", + "pl_PL": "polski (Polska)", + "pt_BR": "português (Brasil)", + "ro_RO": "română (România)", + "sl_SI": "slovenščina (Slovenija)", + "tr_TR": "Türkçe (Türkiye)", + "uk_UA": "українська (Україна)", + "zh_CN": "中文 (中国)", + "zh_TW": "中文 (台灣)" + } } -} +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-de_DE.json b/ui-ngx/src/assets/locale/locale.constant-de_DE.json index acc2a5dee7..766cac37f2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-de_DE.json +++ b/ui-ngx/src/assets/locale/locale.constant-de_DE.json @@ -1,24 +1,24 @@ { "access": { "unauthorized": "Nicht autorisiert", - "unauthorized-access": "Unautorisierter Zugriff", - "unauthorized-access-text": "Sie sollten sich anmelden, um Zugriff auf diese Daten zu erhalten!", - "access-forbidden": "Keine Zugangsberechtigung", - "access-forbidden-text": "Sie haben keine Zugangsberechtigung für diesen Bereich!
Versuchen Sie sich mit einem anderen Benutzer anzumelden um Zugriff auf diesen Bereich zu bekommen.", + "unauthorized-access": "Unbefugter Zugriff", + "unauthorized-access-text": "Sie müssen sich anmelden, um Zugriff auf diese Ressource zu erhalten!", + "access-forbidden": "Zugriff verboten", + "access-forbidden-text": "Sie haben keine Zugriffsrechte auf diesen Ort!
Versuchen Sie, sich mit einem anderen Benutzer anzumelden, wenn Sie weiterhin Zugriff erhalten möchten.", "refresh-token-expired": "Sitzung ist abgelaufen", - "refresh-token-failed": "Sitzung kann nicht aktualisiert werden", + "refresh-token-failed": "Sitzung konnte nicht aktualisiert werden", "permission-denied": "Zugriff verweigert", - "permission-denied-text": "Sie haben keine Berechtigung, um diesen Vorgang auszuführen!" + "permission-denied-text": "Sie haben keine Berechtigung, diese Operation auszuführen!" }, "account": { - "account": "Benutzerkonto", + "account": "Konto", "notification-settings": "Benachrichtigungseinstellungen" }, "action": { "activate": "Aktivieren", - "suspend": "Unterbrechen", + "suspend": "Suspendieren", "save": "Speichern", - "saveAs": "Speichern unter", + "saveAs": "Speichern als", "move": "Verschieben", "cancel": "Abbrechen", "ok": "OK", @@ -27,47 +27,46 @@ "yes": "Ja", "no": "Nein", "update": "Aktualisieren", - "remove": "Löschen", - "search": "Suche", - "clear-search": "Suchanfrage löschen", - "assign": "Zuordnen", - "unassign": "Zuordnung aufheben", + "remove": "Entfernen", + "search": "Suchen", + "clear-search": "Suche löschen", + "assign": "Zuweisen", + "unassign": "Zuweisung aufheben", "share": "Teilen", "make-private": "Privat machen", - "make-public": "Öffentlich machen", "apply": "Anwenden", "apply-changes": "Änderungen übernehmen", "edit-mode": "Bearbeitungsmodus", - "enter-edit-mode": "Zum Bearbeitungsmodus wechseln", - "decline-changes": "Änderungen nicht übernehmen", - "open": "Öffnen", - "decline": "Verwerfen", + "enter-edit-mode": "In den Bearbeitungsmodus wechseln", + "decline-changes": "Änderungen ablehnen", + "decline": "Ablehnen", "close": "Schließen", "back": "Zurück", "run": "Ausführen", "sign-in": "Anmelden!", "edit": "Bearbeiten", - "view": "Ansicht", + "view": "Anzeigen", "create": "Erstellen", "drag": "Ziehen", "refresh": "Aktualisieren", - "undo": "Rückgängig machen", + "undo": "Rückgängig", "copy": "Kopieren", "paste": "Einfügen", - "copy-reference": "Zeichen kopieren", - "paste-reference": "Zeichen einfügen", + "copy-reference": "Referenz kopieren", + "paste-reference": "Referenz einfügen", "import": "Importieren", "export": "Exportieren", - "share-via": "Teilen mit {{provider}}", + "share-via": "Teilen via {{provider}}", "select": "Auswählen", "continue": "Fortsetzen", "discard-changes": "Änderungen verwerfen", - "download": "Download", - "next": "Nächste", - "next-with-label": "Nächste: {{label}}", - "read-more": "Mehr dazu", - "hide": "Verstecken", - "done": "Erledigt", + "download": "Herunterladen", + "next": "Weiter", + "next-with-label": "Weiter: {{label}}", + "read-more": "Mehr lesen", + "hide": "Verbergen", + "test": "Testen", + "done": "Fertig", "print": "Drucken", "restore": "Wiederherstellen", "confirm": "Bestätigen", @@ -76,39 +75,43 @@ "skip": "Überspringen", "send": "Senden", "reset": "Zurücksetzen", - "show-more": "Zeige mehr", - "dont-show-again": "Nicht wieder anzeigen", - "see-documentation": "Siehe Dokumentation", - "clear": "Leeren" + "show-more": "Mehr anzeigen", + "dont-show-again": "Nicht erneut anzeigen", + "see-documentation": "Dokumentation ansehen", + "clear": "Löschen", + "upload": "Hochladen", + "delete-anyway": "Trotzdem löschen", + "delete-selected": "Ausgewählte löschen", + "set": "Festlegen" }, "aggregation": { "aggregation": "Aggregation", "function": "Datenaggregationsfunktion", - "limit": "Höchstwerte", + "limit": "Maximale Werte", "group-interval": "Gruppierungsintervall", - "min": "Minimal", - "max": "Maximal", + "min": "Minimum", + "max": "Maximum", "avg": "Durchschnitt", "sum": "Summe", "count": "Anzahl", - "none": "kein Wert" + "none": "Keine" }, "admin": { "settings": "Einstellungen", "general": "Allgemein", "general-settings": "Allgemeine Einstellungen", - "home-settings": "Home Einstellungen", - "home": "Home", - "outgoing-mail": "E-Mail Versand", - "outgoing-mail-settings": "Konfiguration des Postausgangsservers", + "home-settings": "Startseiten-Einstellungen", + "home": "Startseite", + "outgoing-mail": "Mailserver", + "outgoing-mail-settings": "Einstellungen des ausgehenden Mailservers", "system-settings": "Systemeinstellungen", - "test-mail-sent": "Test E-Mail wurde erfolgreich versendet!", + "test-mail-sent": "Test-E-Mail wurde erfolgreich gesendet!", "base-url": "Basis-URL", "base-url-required": "Basis-URL ist erforderlich.", - "prohibit-different-url": "Prohibit to use hostname from the client request headers", - "prohibit-different-url-hint": "This setting should be enabled for production environments. May cause security issues when disabled", + "prohibit-different-url": "Verwendung des Hostnamens aus den Client-Anforderungsheadern untersagen", + "prohibit-different-url-hint": "Diese Einstellung sollte für Produktionsumgebungen aktiviert sein. Kann zu Sicherheitsproblemen führen, wenn deaktiviert.", "device-connectivity": { - "device-connectivity": "Geräte Konnektivität", + "device-connectivity": "Geräteverbindung", "http-s": "HTTP(s)", "mqtt-s": "MQTT(s)", "coap-s": "COAP(s)", @@ -118,1273 +121,2797 @@ "mqtts": "MQTTs", "coap": "COAP", "coaps": "COAPs", - "hint": "Falls Host und Port leer sind, werden die Standardwerte des Protokolls verwendet", + "hint": "Wenn Host- oder Portfelder leer sind, wird der Standardwert des Protokolls verwendet.", "host": "Host", "port": "Port", - "port-pattern": "Port muss einen positiven Wert haben", - "port-range": "Port sollte im Bereich 1 und 65535 liegen." - }, - "mail-from": "E-Mail von", - "mail-from-required": "E-Mail von ist erforderlich.", - "smtp-protocol": "SMTP Protokoll", - "smtp-host": "SMTP Host", - "smtp-host-required": "SMTP Host ist erforderlich.", - "smtp-port": "SMTP Port", - "smtp-port-required": "Sie müssen einen SMTP Port angeben.", - "smtp-port-invalid": "Das ist kein gültiger SMTP Port.", - "timeout-msec": "Wartezeit (msec)", - "timeout-required": "Wartezeit ist erforderlich.", - "timeout-invalid": "Das ist keine gültige Wartezeit.", + "port-pattern": "Port muss eine positive Ganzzahl sein.", + "port-range": "Port muss im Bereich von 1 bis 65535 liegen." + }, + "mail-from": "Absender-E-Mail", + "mail-from-required": "Absender-E-Mail ist erforderlich.", + "smtp-protocol": "SMTP-Protokoll", + "smtp-host": "SMTP-Host", + "smtp-host-required": "SMTP-Host ist erforderlich.", + "smtp-port": "SMTP-Port", + "smtp-port-required": "SMTP-Port ist erforderlich.", + "smtp-port-invalid": "Das scheint kein gültiger SMTP-Port zu sein.", + "timeout-msec": "Timeout (ms)", + "timeout-required": "Timeout ist erforderlich.", + "timeout-invalid": "Das scheint kein gültiger Timeout zu sein.", "enable-tls": "TLS aktivieren", "tls-version": "TLS-Version", "enable-proxy": "Proxy aktivieren", - "proxy-host": "Proxy Host", - "proxy-host-required": "Proxy Host ist erforderlich.", - "proxy-port": "Proxy Port", - "proxy-port-required": "Proxy Port ist erforderlich.", - "proxy-port-range": "Proxy Port sollte im Bereich 1 bis 65535 sein.", - "proxy-user": "Proxy Benutzername", - "proxy-password": "Proxy Passwort", + "proxy-host": "Proxy-Host", + "proxy-host-required": "Proxy-Host ist erforderlich.", + "proxy-port": "Proxy-Port", + "proxy-port-required": "Proxy-Port ist erforderlich.", + "proxy-port-range": "Proxy-Port muss im Bereich von 1 bis 65535 liegen.", + "proxy-user": "Proxy-Benutzer", + "proxy-password": "Proxy-Passwort", "change-password": "Passwort ändern", - "send-test-mail": "Test E-Mail senden", - "use-system-mail-settings": "System E-Mail-Server Einstellungen benutzen", - "mail-templates": "E-Mail Vorlagen", - "mail-template-settings": "Einstellungen E-Mail Vorlagen", - "use-system-mail-template-settings": "System E-Mail Vorlagen benutzen", - "mail-template": { - "mail-template": "E-Mail Vorlage", - "test": "E-Mail Nachricht testen", - "activation": "Nachricht Benutzerkontoaktivierung", - "account-activated": "Nachricht Benutzerkonto aktiviert", - "account-lockout": "Nachricht zur Kontosperrung", - "reset-password": "Nachricht zum Zurücksetzen des Passworts", - "password-was-reset": "Nachricht für Passwort wurde zurückgesetzt", - "user-activated": "Nachricht für Benutzerkonto wurde aktiviert", - "user-registered": "Nachricht für Benutzer hat sich registriert", - "api-usage-state-enabled": "API-Nutzungsstatus wurde aktiviert", - "api-usage-state-warning": "Warnung zum API-Nutzungsstatus", - "api-usage-state-disabled": "API-Nutzungsstatus wurde deaktiviert", - "two-fa-verification": "2FA-Bestätigungsnachricht" - }, - "mail-subject": "E-Mail Betreff", - "mail-body": "E-Mail Nachricht", - "sms-provider": "SMS Anbieter", - "sms-provider-settings": "SMS Anbieter Einstellungen", - "use-system-sms-settings": "System SMS Anbieter benutzen", - "sms-provider-type": "SMS Anbieter Typ", - "sms-provider-type-required": "SMS Anbieter Typ ist erforderlich.", + "send-test-mail": "Test-E-Mail senden", + "sms-provider": "SMS-Anbieter", + "sms-provider-settings": "SMS-Anbieter-Einstellungen", + "sms-provider-type": "Typ des SMS-Anbieters", + "sms-provider-type-required": "Typ des SMS-Anbieters ist erforderlich.", "sms-provider-type-aws-sns": "Amazon SNS", "sms-provider-type-twilio": "Twilio", "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID ist erforderlich", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key ist erforderlich", + "aws-region": "AWS-Region", + "aws-region-required": "AWS-Region ist erforderlich", + "number-from": "Absendernummer", + "number-from-required": "Absendernummer ist erforderlich.", + "number-to": "Empfängernummer", + "number-to-required": "Empfängernummer ist erforderlich.", + "phone-number-hint": "Telefonnummer im E.164-Format, z. B. +19995550123", + "phone-number-hint-twilio": "Telefonnummer im E.164-Format/SID der Telefonnummer/Messaging Service SID, z. B. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Ungültige Telefonnummer. Sollte im E.164-Format sein, z. B. +19995550123.", + "phone-number-pattern-twilio": "Ungültige Telefonnummer. Sollte im E.164-Format/SID der Telefonnummer/Messaging Service SID sein.", + "sms-message": "SMS-Nachricht", + "sms-message-required": "SMS-Nachricht ist erforderlich.", + "sms-message-max-length": "SMS-Nachricht darf nicht länger als 1600 Zeichen sein", + "twilio-account-sid": "Twilio Account SID", + "twilio-account-sid-required": "Twilio Account SID ist erforderlich", + "twilio-account-token": "Twilio Account Token", + "twilio-account-token-required": "Twilio Account Token ist erforderlich", + "send-test-sms": "Test-SMS senden", + "test-sms-sent": "Test-SMS wurde erfolgreich gesendet!", "security-settings": "Sicherheitseinstellungen", - "password-policy": "Kennwortrichtlinie", + "password-policy": "Passwortrichtlinie", "minimum-password-length": "Minimale Passwortlänge", "minimum-password-length-required": "Minimale Passwortlänge ist erforderlich", - "minimum-password-length-range": "Die Mindestlänge des Passworts sollte zwischen 5 und 50 liegen", - "minimum-uppercase-letters": "Mindestanzahl von Großbuchstaben", - "minimum-uppercase-letters-range": "Die Mindestanzahl von Großbuchstaben kann nicht negativ sein", - "minimum-lowercase-letters": "Mindestanzahl von Kleinbuchstaben", - "minimum-lowercase-letters-range": "Die Mindestanzahl von Kleinbuchstaben kann nicht negativ sein", - "minimum-digits": "Mindestanzahl von Ziffern", - "minimum-digits-range": "Die Mindestanzahl von Ziffern kann nicht negativ sein", - "minimum-special-characters": "Mindestanzahl von Sonderzeichen", - "minimum-special-characters-range": "Die Mindestanzahl von Sonderzeichen kann nicht negativ sein", - "password-expiration-period-days": "Gültigkeitsdauer des Passworts in Tagen", - "password-expiration-period-days-range": "Die Gültigkeitsdauer des Passworts in Tagen kann nicht negativ sein", - "password-reuse-frequency-days": "Häufigkeit der Kennwortwiederverwendung in Tagen", - "password-reuse-frequency-days-range": "Die Häufigkeit der Kennwortwiederverwendung in Tagen kann nicht negativ sein", + "minimum-password-length-range": "Minimale Passwortlänge sollte zwischen 6 und 50 Zeichen liegen", + "maximum-password-length": "Maximale Passwortlänge", + "maximum-password-length-min": "Maximale Passwortlänge sollte mindestens 6 betragen", + "maximum-password-length-less-min": "Maximale Passwortlänge muss größer als die minimale Länge sein", + "minimum-uppercase-letters": "Minimale Anzahl an Großbuchstaben", + "minimum-uppercase-letters-range": "Minimale Anzahl an Großbuchstaben darf nicht negativ sein", + "minimum-lowercase-letters": "Minimale Anzahl an Kleinbuchstaben", + "minimum-lowercase-letters-range": "Minimale Anzahl an Kleinbuchstaben darf nicht negativ sein", + "minimum-digits": "Minimale Anzahl an Ziffern", + "minimum-digits-range": "Minimale Anzahl an Ziffern darf nicht negativ sein", + "minimum-special-characters": "Minimale Anzahl an Sonderzeichen", + "minimum-special-characters-range": "Minimale Anzahl an Sonderzeichen darf nicht negativ sein", + "password-expiration-period-days": "Passwort-Ablaufzeitraum in Tagen", + "password-expiration-period-days-range": "Passwort-Ablaufzeitraum darf nicht negativ sein", + "password-reuse-frequency-days": "Häufigkeit der Passwortwiederverwendung in Tagen", + "password-reuse-frequency-days-range": "Häufigkeit der Passwortwiederverwendung darf nicht negativ sein", "allow-whitespace": "Leerzeichen erlauben", - "general-policy": "Allgemeine Politik", + "force-reset-password-if-no-valid": "Erzwinge Passwortzurücksetzung bei ungültigem Passwort", + "force-reset-password-if-no-valid-hint": "Seien Sie vorsichtig beim Aktivieren dieser Funktion: Benutzer mit ungültigem Passwort müssen ihr Passwort per E-Mail zurücksetzen.", + "general-policy": "Allgemeine Richtlinie", "max-failed-login-attempts": "Maximale Anzahl fehlgeschlagener Anmeldeversuche, bevor das Konto gesperrt wird", - "minimum-max-failed-login-attempts-range": "Die maximale Anzahl fehlgeschlagener Anmeldeversuche kann nicht negativ sein", - "user-lockout-notification-email": "Wenn das Benutzerkonto gesperrt ist, senden Sie eine Benachrichtigung per E-Mail" + "minimum-max-failed-login-attempts-range": "Maximale Anzahl fehlgeschlagener Anmeldeversuche darf nicht negativ sein", + "user-lockout-notification-email": "Im Falle einer Kontosperrung des Benutzers, Benachrichtigung per E-Mail senden", + "user-activation-token-ttl": "Gültigkeit des Aktivierungslinks in Stunden", + "user-activation-token-ttl-range": "Aktivierungslink muss zwischen 1 und 24 Stunden gültig sein", + "password-reset-token-ttl": "Gültigkeit des Passwort-Reset-Links in Stunden", + "password-reset-token-ttl-range": "Reset-Link muss zwischen 1 und 24 Stunden gültig sein", + "mobile-secret-key-length": "Länge des mobilen Geheimschlüssels", + "mobile-secret-key-length-range": "Länge des mobilen Geheimschlüssels muss positiv sein", + "domain-name": "Domainname", + "domain-name-unique": "Domainname und Protokoll müssen eindeutig sein.", + "domain-name-max-length": "Domainname sollte weniger als 256 Zeichen haben", + "error-verification-url": "Ein Domainname darf keine Zeichen wie '/' oder ':' enthalten. Beispiel: thingsboard.io", + "connection-settings": "Verbindungseinstellungen", + "oauth2": { + "access-token-uri": "Access Token URI", + "access-token-uri-required": "Access Token URI ist erforderlich.", + "activate-user": "Benutzer aktivieren", + "add-domain": "Domain hinzufügen", + "delete-domain": "Domain löschen", + "add-provider": "Anbieter hinzufügen", + "delete-provider": "Anbieter löschen", + "allow-user-creation": "Benutzererstellung erlauben", + "always-fullscreen": "Immer Vollbild", + "authorization-uri": "Autorisierungs-URI", + "authorization-uri-required": "Autorisierungs-URI ist erforderlich.", + "add-client": "OAuth 2.0-Client hinzufügen", + "client-details": "OAuth 2.0-Clientdetails", + "client": "OAuth 2.0-Client", + "clients": "OAuth 2.0-Clients", + "no-oauth2-clients": "Keine OAuth 2.0-Clients gefunden", + "search-oauth2-clients": "OAuth 2.0-Clients suchen", + "delete-client-title": "Sind Sie sicher, dass Sie den OAuth 2.0-Client '{{clientName}}' löschen möchten?", + "delete-client-text": "Seien Sie vorsichtig: Nach der Bestätigung werden der Client und alle zugehörigen Daten nicht mehr wiederherstellbar sein.", + "delete-mobile-app-title": "Sind Sie sicher, dass Sie die mobile Anwendung '{{applicationName}}' löschen möchten?", + "delete-mobile-app-text": "Seien Sie vorsichtig: Nach der Bestätigung werden die Anwendung und alle zugehörigen Daten nicht mehr wiederherstellbar sein.", + "title": "Titel", + "client-title-required": "Titel ist erforderlich", + "client-title-max-length": "Titel sollte weniger als 100 Zeichen enthalten", + "advanced-settings": "Erweiterte Einstellungen", + "domain-details": "Domain-Details", + "no-domains": "Keine Domains gefunden", + "search-domains": "Domains suchen", + "mobile-app-details": "Details der mobilen Anwendung", + "add-mobile-app": "Mobile Anwendung hinzufügen", + "no-mobile-apps": "Keine mobilen Anwendungen gefunden", + "search-mobile-apps": "Mobile Anwendungen suchen", + "send-token": "Token senden", + "create-new": "Neu erstellen", + "client-authentication-method": "Authentifizierungsmethode des Clients", + "client-id": "Client-ID", + "client-id-required": "Client-ID ist erforderlich.", + "client-id-max-length": "Client-ID sollte weniger als 256 Zeichen enthalten", + "client-secret": "Client-Geheimnis", + "client-secret-required": "Client-Geheimnis ist erforderlich.", + "client-secret-max-length": "Client-Geheimnis sollte weniger als 2049 Zeichen enthalten", + "custom-setting": "Benutzerdefinierte Einstellungen", + "customer-name-pattern": "Kundennamensmuster", + "customer-name-pattern-max-length": "Kundennamensmuster sollte weniger als 256 Zeichen enthalten", + "default-dashboard-name": "Standard-Dashboardname", + "default-dashboard-name-max-length": "Standard-Dashboardname sollte weniger als 256 Zeichen enthalten", + "delete-domain-text": "Seien Sie vorsichtig, nach der Bestätigung werden Domain und alle Anbieterdaten nicht mehr verfügbar sein.", + "delete-domain-title": "Sind Sie sicher, dass Sie die Domain '{{domainName}}' löschen möchten?", + "delete-registration-text": "Seien Sie vorsichtig, nach der Bestätigung werden die Anbieterdaten nicht mehr verfügbar sein.", + "delete-registration-title": "Sind Sie sicher, dass Sie den Anbieter '{{name}}' löschen möchten?", + "email-attribute-key": "E-Mail-Attributschlüssel", + "email-attribute-key-required": "E-Mail-Attributschlüssel ist erforderlich.", + "email-attribute-key-max-length": "E-Mail-Attributschlüssel sollte weniger als 32 Zeichen enthalten", + "first-name-attribute-key": "Vorname-Attributschlüssel", + "first-name-attribute-key-max-length": "Vorname-Attributschlüssel sollte weniger als 32 Zeichen enthalten", + "general": "Allgemein", + "jwk-set-uri": "JSON Web Key URI", + "last-name-attribute-key": "Nachname-Attributschlüssel", + "last-name-attribute-key-max-length": "Nachname-Attributschlüssel sollte weniger als 32 Zeichen enthalten", + "login-button-icon": "Login-Button-Symbol", + "login-button-label": "Anbieterbezeichnung", + "login-button-label-placeholder": "Anmelden mit $(Provider label)", + "login-button-label-required": "Bezeichnung ist erforderlich.", + "login-provider": "Login-Anbieter", + "mapper": "Mapper", + "new-domain": "Neue Domain", + "oauth2": "OAuth 2.0", + "password-max-length": "Passwort sollte weniger als 256 Zeichen enthalten", + "redirect-uri-template": "Redirect URI-Vorlage", + "copy-redirect-uri": "Redirect URI kopieren", + "registration-id": "Registrierungs-ID", + "registration-id-required": "Registrierungs-ID ist erforderlich.", + "registration-id-unique": "Registrierungs-ID muss im System eindeutig sein.", + "scope": "Scope", + "scope-required": "Scope ist erforderlich.", + "tenant-name-pattern": "Mandantennamensmuster", + "tenant-name-pattern-required": "Mandantennamensmuster ist erforderlich.", + "tenant-name-pattern-max-length": "Mandantennamensmuster sollte weniger als 256 Zeichen enthalten", + "tenant-name-strategy": "Strategie für Mandantennamen", + "type": "Mapper-Typ", + "uri-pattern-error": "Ungültiges URI-Format.", + "url": "URL", + "url-pattern": "Ungültiges URL-Format.", + "url-required": "URL ist erforderlich.", + "url-max-length": "URL sollte weniger als 256 Zeichen enthalten", + "user-info-uri": "Benutzerinfo-URI", + "user-info-uri-required": "Benutzerinfo-URI ist erforderlich.", + "username-max-length": "Benutzername sollte weniger als 256 Zeichen enthalten", + "user-name-attribute-name": "Benutzername-Attributschlüssel", + "user-name-attribute-name-required": "Benutzername-Attributschlüssel ist erforderlich", + "protocol": "Protokoll", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "OAuth 2.0-Einstellungen aktivieren", + "disable": "OAuth 2.0-Einstellungen deaktivieren", + "edge": "Auf Edge übertragen", + "edge-enable": "Übertragung auf Edge aktivieren", + "edge-disable": "Übertragung auf Edge deaktivieren", + "domains": "Domains", + "mobile-apps": "Mobile Anwendungen", + "mobile-package": "Anwendungspaket", + "mobile-package-placeholder": "z. B.: my.example.app", + "mobile-package-hint": "Für Android: Ihre eigene eindeutige Anwendungs-ID. Für iOS: Produkt-Bundle-Identifier.", + "mobile-package-unique": "Anwendungspaket muss eindeutig sein.", + "mobile-package-required": "Anwendungspaket ist erforderlich.", + "mobile-package-max-length": "Anwendungspaket sollte weniger als 256 Zeichen enthalten", + "mobile-package-spaces": "Anwendungspaket darf keine Leerzeichen enthalten", + "mobile-app-secret": "Anwendungsgeheimnis", + "mobile-app-secret-hint": "Base64-codierter String mit mindestens 512 Bits.", + "mobile-app-secret-required": "Anwendungsgeheimnis ist erforderlich.", + "mobile-app-secret-min-length": "Anwendungsgeheimnis muss mindestens 512 Bits lang sein.", + "mobile-app-secret-base64": "Anwendungsgeheimnis muss im Base64-Format sein.", + "invalid-mobile-app-secret": "Anwendungsgeheimnis darf nur alphanumerische Zeichen enthalten und muss zwischen 16 und 2048 Zeichen lang sein.", + "copy-mobile-app-secret": "Anwendungsgeheimnis kopieren", + "delete-mobile-app": "Anwendungsinfo löschen", + "providers": "Anbieter", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Alle Plattformen", + "smtp-provider": "SMTP-Anbieter", + "allowed-platforms": "Zugelassene Plattformen", + "authentication": "Authentifizierung", + "basic": "Basis", + "provider": "Anbieter", + "redirect-url": "Redirect-URI", + "domain-name": "Domainname", + "domain-name-required": "Domainname ist erforderlich", + "redirect-url-template": "Redirect-URI-Vorlage", + "microsoft-tenant-id": "Verzeichnis-ID (Mandanten-ID)", + "microsoft-tenant-id-required": "Verzeichnis-ID (Mandanten-ID) ist erforderlich", + "token-uri": "Token-URI", + "token-uri-required": "Token-URI ist erforderlich", + "redirect-uri": "Redirect-URI", + "google-provider": "Google", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "Benutzerdefiniert", + "generate-access-token": "Access Token generieren", + "update-access-token": "Access Token aktualisieren", + "access-token-status": "Status des Access Tokens:", + "token-status-generated": "generiert", + "token-status-not-generated": "nicht generiert" + }, + "smpp-provider": { + "smpp-version": "SMPP-Version", + "smpp-host": "SMPP-Host", + "smpp-host-required": "SMPP-Host ist erforderlich", + "smpp-port": "SMPP-Port", + "smpp-port-required": "SMPP-Port ist erforderlich", + "system-id": "System-ID", + "system-id-required": "System-ID ist erforderlich", + "password": "Passwort", + "password-required": "Passwort ist erforderlich", + "type-settings": "Typ-Einstellungen", + "source-settings": "Quell-Einstellungen", + "destination-settings": "Ziel-Einstellungen", + "additional-settings": "Zusätzliche Einstellungen", + "system-type": "Systemtyp", + "bind-type": "Bindetyp", + "service-type": "Diensttyp", + "source-address": "Quelladresse", + "source-ton": "Quell-TON", + "source-npi": "Quell-NPI", + "destination-ton": "Ziel-TON (Nummerntyp)", + "destination-npi": "Ziel-NPI (Nummerierungsplan-Identifikation)", + "address-range": "Adressbereich", + "coding-scheme": "Kodierungsschema", + "bind-type-tx": "Sender", + "bind-type-rx": "Empfänger", + "bind-type-trx": "Sender/Empfänger", + "ton-unknown": "Unbekannt", + "ton-international": "International", + "ton-national": "National", + "ton-network-specific": "Netzwerkspezifisch", + "ton-subscriber-number": "Teilnehmernummer", + "ton-alphanumeric": "Alphanumerisch", + "ton-abbreviated": "Abgekürzt", + "npi-unknown": "0 - Unbekannt", + "npi-isdn": "1 - ISDN/Telefonnummernplan (E163/E164)", + "npi-data-numbering-plan": "3 - Datennummerierungsplan (X.121)", + "npi-telex-numbering-plan": "4 - Telex-Nummerierungsplan (F.69)", + "npi-land-mobile": "6 - Land Mobile (E.212)", + "npi-national-numbering-plan": "8 - Nationaler Nummerierungsplan", + "npi-private-numbering-plan": "9 - Privater Nummerierungsplan", + "npi-ermes-numbering-plan": "10 - ERMES-Nummerierungsplan (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internet (IP)", + "npi-wap-client-id": "18 - WAP Client-ID (durch das WAP-Forum zu definieren)", + "scheme-smsc": "0 - SMSC Standardschrift (ASCII für kurze/lange Nummer, GSM für gebührenfrei)", + "scheme-ia5": "1 - IA5 (ASCII für kurze/lange Nummer, Latin 9 für gebührenfrei)", + "scheme-octet-unspecified-2": "2 - Oktett nicht angegeben (8-Bit binär)", + "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Oktett nicht angegeben (8-Bit binär)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Kyrillisch (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Lateinisch/Hebräisch (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Piktogramm-Kodierung", + "scheme-music-codes": "10 - Musikcodes (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Erweiterter Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Koreanischer Zeichensatz (KS C 5601/KS X 1001)" + }, + "queue-select-name": "Warteschlangenname auswählen", + "queue-name": "Name", + "queue-name-required": "Warteschlangenname ist erforderlich!", + "queues": "Warteschlangen", + "queue-partitions": "Partitionen", + "queue-submit-strategy": "Übermittlungsstrategie", + "queue-processing-strategy": "Verarbeitungsstrategie", + "queue-configuration": "Warteschlangenkonfiguration", + "repository-settings": "Repository-Einstellungen", + "repository": "Repository", + "repository-url": "Repository-URL", + "repository-url-required": "Repository-URL ist erforderlich.", + "default-branch": "Standard-Branch-Name", + "repository-read-only": "Nur Lesen", + "show-merge-commits": "Merge-Commits anzeigen", + "authentication-settings": "Authentifizierungseinstellungen", + "auth-method": "Authentifizierungsmethode", + "auth-method-username-password": "Passwort / Zugriffstoken", + "auth-method-username-password-hint": "GitHub-Nutzer müssen Tokens mit Schreibrechten zum Repository verwenden.", + "auth-method-private-key": "Privater Schlüssel", + "password-access-token": "Passwort / Zugriffstoken", + "change-password-access-token": "Passwort / Zugriffstoken ändern", + "private-key": "Privater Schlüssel", + "drop-private-key-file-or": "Ziehen Sie eine private Schlüsseldatei hierher oder", + "passphrase": "Passphrase", + "enter-passphrase": "Passphrase eingeben", + "change-passphrase": "Passphrase ändern", + "check-access": "Zugriff prüfen", + "check-repository-access-success": "Repository-Zugriff erfolgreich überprüft!", + "delete-repository-settings-title": "Möchten Sie die Repository-Einstellungen wirklich löschen?", + "delete-repository-settings-text": "Achtung, nach der Bestätigung werden die Repository-Einstellungen entfernt und die Versionskontrolle wird nicht mehr verfügbar sein.", + "auto-commit-settings": "Auto-Commit-Einstellungen", + "auto-commit": "Auto-Commit", + "auto-commit-entities": "Auto-Commit-Entitäten", + "no-auto-commit-entities-prompt": "Keine Entitäten für Auto-Commit konfiguriert", + "delete-auto-commit-settings-title": "Möchten Sie die Auto-Commit-Einstellungen wirklich löschen?", + "delete-auto-commit-settings-text": "Achtung, nach der Bestätigung werden die Auto-Commit-Einstellungen entfernt und Auto-Commit wird für alle Entitäten deaktiviert.", + "mobile-app": { + "mobile-app": "Mobile App", + "mobile-app-qr-code-widget-settings": "QR-Code-Widget-Einstellungen für Mobile App", + "applications": "Anwendungen", + "default": "Standard", + "custom": "Benutzerdefiniert", + "android": "Android", + "ios": "iOS", + "appearance": "Erscheinungsbild", + "appearance-on-home-page": "Erscheinungsbild auf der Startseite", + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "badges": "Abzeichen", + "label": "Bezeichnung", + "label-required": "Bezeichnung ist erforderlich", + "label-max-length": "Bezeichnung sollte maximal 50 Zeichen lang sein", + "right": "Rechts", + "left": "Links", + "set": "Festlegen", + "preview": "Vorschau", + "connect-mobile-app": "Mobile App verbinden", + "use-system-settings": "Systemeinstellungen verwenden" + }, + "2fa": { + "2fa": "Zwei-Faktor-Authentifizierung", + "available-providers": "Verfügbare Anbieter", + "issuer-name": "Ausstellername", + "issuer-name-required": "Ausstellername ist erforderlich.", + "max-verification-failures-before-user-lockout": "Maximale Verifizierungsfehler vor Kontosperrung", + "max-verification-failures-before-user-lockout-pattern": "Maximale Verifizierungsfehler müssen eine positive Ganzzahl sein.", + "number-of-checking-attempts": "Anzahl der Überprüfungsversuche", + "number-of-checking-attempts-pattern": "Überprüfungsversuche müssen eine positive Ganzzahl sein.", + "number-of-checking-attempts-required": "Überprüfungsversuche sind erforderlich.", + "number-of-codes": "Anzahl der Codes", + "number-of-codes-pattern": "Anzahl der Codes muss eine positive Ganzzahl sein.", + "number-of-codes-required": "Anzahl der Codes ist erforderlich.", + "provider": "Anbieter", + "retry-verification-code-period": "Wiederholungszeitraum für Verifizierungscode (Sek.)", + "retry-verification-code-period-pattern": "Mindestzeitraum beträgt 5 Sekunden", + "retry-verification-code-period-required": "Wiederholungszeitraum ist erforderlich.", + "total-allowed-time-for-verification": "Maximal erlaubte Verifizierungszeit (Sek.)", + "total-allowed-time-for-verification-pattern": "Mindestzeit beträgt 60 Sekunden", + "total-allowed-time-for-verification-required": "Maximale Verifizierungszeit ist erforderlich.", + "use-system-two-factor-auth-settings": "Systemeinstellungen für 2FA verwenden", + "verification-code-check-rate-limit": "Rate-Limit für Code-Überprüfung", + "verification-code-lifetime": "Lebensdauer des Verifizierungscodes (Sek.)", + "verification-code-lifetime-pattern": "Lebensdauer muss eine positive Ganzzahl sein.", + "verification-code-lifetime-required": "Lebensdauer ist erforderlich.", + "verification-message-template": "Nachrichtenvorlage für Verifizierung", + "verification-limitations": "Verifizierungseinschränkungen", + "verification-message-template-pattern": "Nachricht muss das Muster ${code} enthalten", + "verification-message-template-required": "Nachrichtenvorlage ist erforderlich.", + "within-time": "Innerhalb von (Sek.)", + "within-time-pattern": "Zeit muss eine positive Ganzzahl sein.", + "within-time-required": "Zeit ist erforderlich." + }, + "jwt": { + "security-settings": "JWT-Sicherheitseinstellungen", + "issuer-name": "Ausstellername", + "issuer-name-required": "Ausstellername ist erforderlich.", + "signings-key": "Signierschlüssel", + "signings-key-hint": "Base64-codierter String mit mindestens 512 Bit Daten.", + "signings-key-required": "Signierschlüssel ist erforderlich.", + "signings-key-min-length": "Signierschlüssel muss mindestens 512 Bit Daten enthalten.", + "signings-key-base64": "Signierschlüssel muss im Base64-Format vorliegen.", + "expiration-time": "Ablaufzeit des Tokens (Sek.)", + "expiration-time-required": "Ablaufzeit ist erforderlich.", + "expiration-time-max": "Maximal zulässige Zeit ist 2147483647 Sekunden (68 Jahre).", + "expiration-time-min": "Mindestzeit beträgt 60 Sekunden (1 Minute).", + "refresh-expiration-time": "Ablaufzeit des Refresh-Tokens (Sek.)", + "refresh-expiration-time-required": "Ablaufzeit des Refresh-Tokens ist erforderlich.", + "refresh-expiration-time-max": "Maximal zulässige Zeit ist 2147483647 Sekunden (68 Jahre).", + "refresh-expiration-time-min": "Mindestzeit beträgt 900 Sekunden (15 Minuten).", + "refresh-expiration-time-less-token": "Refresh-Token-Zeit muss größer als die Token-Zeit sein.", + "generate-key": "Schlüssel generieren", + "info-header": "Alle Benutzer müssen sich neu anmelden", + "info-message": "Die Änderung des JWT-Signierschlüssels macht alle ausgegebenen Tokens ungültig. Alle Benutzer müssen sich erneut anmelden. Dies betrifft auch Skripte, die die REST-API/Websockets verwenden." + }, + "resources": "Ressourcen", + "notifications": "Benachrichtigungen", + "notifications-settings": "Benachrichtigungseinstellungen", + "slack-api-token": "Slack-API-Token", + "slack": "Slack", + "slack-settings": "Slack-Einstellungen", + "mobile-settings": "Mobile Einstellungen", + "firebase-service-account-file": "Firebase-Service-Konto-Anmeldeinformationen (JSON-Datei)", + "select-firebase-service-account-file": "Ziehen Sie Ihre Firebase-Service-Konto-Datei hierher oder " }, "alarm": { "alarm": "Alarm", "alarms": "Alarme", "all-alarms": "Alle Alarme", "select-alarm": "Alarm auswählen", - "no-alarms-matching": "Keine passenden Alarme zu '{{entity}}' wurden gefunden.", + "no-alarms-matching": "Keine Alarme gefunden, die mit '{{entity}}' übereinstimmen.", "alarm-required": "Alarm ist erforderlich", "alarm-filter": "Alarmfilter", "filter": "Filter", - "alarm-status": "Alarm Status", - "alarm-status-list": "Alarm Statusliste", - "any-status": "Jeder Status", + "alarm-status": "Alarmstatus", + "alarm-status-list": "Liste der Alarmstatus", + "any-status": "Beliebiger Status", "search-status": { - "ANY": "Jeder", - "ACTIVE": "Aktiv", - "CLEARED": "Gelöscht", - "ACK": "Bestätigt", - "UNACK": "Nicht bestätigt" + "ANY": "Beliebig", + "ACTIVE": "Aktiv", + "CLEARED": "Zurückgesetzt", + "ACK": "Bestätigt", + "UNACK": "Nicht bestätigt" }, "display-status": { - "ACTIVE_UNACK": "Nicht bestätigt aktiv", - "ACTIVE_ACK": "Bestätigt aktiv", - "CLEARED_UNACK": "Nicht bestätigt", - "CLEARED_ACK": "Bestätigung gelöscht" + "ACTIVE_UNACK": "Aktiv nicht bestätigt", + "ACTIVE_ACK": "Aktiv bestätigt", + "CLEARED_UNACK": "Zurückgesetzt nicht bestätigt", + "CLEARED_ACK": "Zurückgesetzt bestätigt" }, "no-alarms-prompt": "Keine Alarme gefunden", "created-time": "Erstellungszeit", "type": "Typ", - "severity": "Schwere", - "originator": "Urheber", - "originator-type": "Urheber-Typ", + "severity": "Schweregrad", + "originator": "Auslöser", + "originator-type": "Typ des Auslösers", "details": "Details", + "originator-label": "Auslöser-Label", + "assign": "Zuweisen", + "assignments": "Zuweisungen", + "assignee": "Zuständiger", + "assignee-id": "ID des Zuständigen", + "assignee-first-name": "Vorname des Zuständigen", + "assignee-last-name": "Nachname des Zuständigen", + "assignee-email": "E-Mail des Zuständigen", + "unassigned": "Nicht zugewiesen", + "user-deleted": "Benutzer gelöscht", + "assignee-not-set": "Alle", "status": "Status", - "alarm-details": "Alarm-Details", + "alarm-details": "Alarmdetails", "start-time": "Startzeit", + "assign-time": "Zuweisungszeit", "end-time": "Endzeit", "ack-time": "Bestätigungszeit", - "clear-time": "Zeit gelöscht", + "clear-time": "Zurücksetzungszeit", "duration": "Dauer", - "alarm-severity-list": "Alarm Schwere Liste", - "any-severity": "Jede Schwere", + "alarm-severity": "Alarmschweregrad", + "alarm-severity-list": "Liste der Alarmschweregrade", + "any-severity": "Beliebiger Schweregrad", "severity-critical": "Kritisch", - "severity-major": "Groß", - "severity-minor": "Klein", + "severity-major": "Hoch", + "severity-minor": "Niedrig", "severity-warning": "Warnung", "severity-indeterminate": "Unbestimmt", "acknowledge": "Bestätigen", - "clear": "Löschen", + "clear": "Zurücksetzen", "delete": "Löschen", "search": "Alarme suchen", "selected-alarms": "{ count, plural, =1 {1 Alarm} other {# Alarme} } ausgewählt", "no-data": "Keine Daten zum Anzeigen", - "polling-interval": "Alarmabfrageintervall (sec)", - "polling-interval-required": "Alarmabfrageintervall ist erforderlich.", - "min-polling-interval-message": "Mindestens 1 sec Abrufintervall ist zulässig.", - "aknowledge-alarms-title": "{ count, plural, =1 {1 Alarm} other {# Alarme} } bestätigen", - "aknowledge-alarms-text": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Alarm} other {# Alarme} } bestätigen möchten?", + "polling-interval": "Alarm-Abfrageintervall (Sek.)", + "polling-interval-required": "Alarm-Abfrageintervall ist erforderlich.", + "min-polling-interval-message": "Mindestens 1 Sekunde Abfrageintervall ist erlaubt.", + "aknowledge-alarms-title": "{ count, plural, =1 {1 Alarm bestätigen} other {# Alarme bestätigen} }", + "aknowledge-alarms-text": "Möchten Sie { count, plural, =1 {1 Alarm} other {# Alarme} } wirklich bestätigen?", "aknowledge-alarm-title": "Alarm bestätigen", "aknowledge-alarm-text": "Möchten Sie den Alarm wirklich bestätigen?", - "selected-alarms-are-acknowledged": "Ausgewählte Alarme wurden bereits bestätigt", - "clear-alarms-title": "{ count, plural, =1 {1 Alarm} other {# Alarme} } löschen", - "clear-alarms-text": "Möchten Sie wirklich { count, plural, =1 {1 Alarm} other {# Alarme} } löschen?", - "clear-alarm-title": "Alarm löschen", - "clear-alarm-text": "Möchten Sie den Alarm wirklich löschen?", - "delete-alarms-title": "Lösche { count, plural, =1 {1 Alarm} other {# Alarme} }", - "delete-alarms-text": "Sind Sie sicher { count, plural, =1 {1 Alarm} other {# Alarme} } zu löschen?", - "selected-alarms-are-cleared": "Ausgewählte Alarme wurden bereits gelöscht", - "alarm-status-filter": "Alarm Status Filter", + "selected-alarms-are-acknowledged": "Ausgewählte Alarme sind bereits bestätigt", + "clear-alarms-title": "{ count, plural, =1 {1 Alarm zurücksetzen} other {# Alarme zurücksetzen} }", + "clear-alarms-text": "Möchten Sie { count, plural, =1 {1 Alarm} other {# Alarme} } wirklich zurücksetzen?", + "clear-alarm-title": "Alarm zurücksetzen", + "clear-alarm-text": "Möchten Sie den Alarm wirklich zurücksetzen?", + "delete-alarms-title": "{ count, plural, =1 {1 Alarm löschen} other {# Alarme löschen} }", + "delete-alarms-text": "Möchten Sie { count, plural, =1 {1 Alarm} other {# Alarme} } wirklich löschen?", + "selected-alarms-are-cleared": "Ausgewählte Alarme sind bereits zurückgesetzt", + "alarm-status-filter": "Alarmstatus-Filter", "alarm-filter-title": "Alarmfilter", "assigned": "Zugewiesen", - "filter-title": "Filter" + "filter-title": "Filter", + "max-count-load": "Maximale Anzahl zu ladender Alarme (0 - unbegrenzt)", + "max-count-load-required": "Maximale Anzahl zu ladender Alarme ist erforderlich.", + "max-count-load-error-min": "Minimalwert ist 0.", + "fetch-size": "Abrufgröße", + "fetch-size-required": "Abrufgröße ist erforderlich.", + "fetch-size-error-min": "Minimalwert ist 10.", + "alarm-types": "Alarmtypen", + "alarm-type-list": "Liste der Alarmtypen", + "any-type": "Beliebiger Typ", + "assigned-to-current-user": "Dem aktuellen Benutzer zugewiesen", + "assigned-to-me": "Mir zugewiesen", + "search-propagated-alarms": "Verbreitete Alarme durchsuchen", + "comments": "Alarmkommentare", + "show-more": "Mehr anzeigen", + "additional-info": "Zusätzliche Informationen", + "alarm-type": "Alarmtyp", + "enter-alarm-type": "Alarmtyp eingeben", + "no-alarm-types-matching": "Keine Alarmtypen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "alarm-type-list-empty": "Keine Alarmtypen ausgewählt." + }, + "alarm-activity": { + "add": "Kommentar hinzufügen...", + "alarm-comment": "Alarmkommentar", + "comments": "Kommentare", + "delete-alarm-comment": "Möchten Sie diesen Kommentar löschen?", + "refresh": "Aktualisieren", + "oldest-first": "Älteste zuerst", + "newest-first": "Neueste zuerst", + "activity": "Aktivität", + "export": "Als CSV exportieren", + "author": "Autor", + "created-date": "Erstellungsdatum", + "edited-date": "Bearbeitungsdatum", + "text": "Text", + "system": "System" }, "alias": { "add": "Alias hinzufügen", "edit": "Alias bearbeiten", "name": "Aliasname", "name-required": "Aliasname ist erforderlich", - "duplicate-alias": "Ein Alias mit demselben Namen ist bereits vorhanden.", + "duplicate-alias": "Ein Alias mit demselben Namen existiert bereits.", "filter-type-single-entity": "Einzelne Entität", "filter-type-entity-list": "Entitätsliste", "filter-type-entity-name": "Entitätsname", "filter-type-entity-type": "Entitätstyp", - "filter-type-state-entity": "Entität aus dem Dashboard Status", - "filter-type-state-entity-description": "Entität aus den Dashboard Status Parametern", - "filter-type-asset-type": "Objekttyp", - "filter-type-asset-type-description": "Objekte vom Typ '{{assetTypes}}'", - "filter-type-asset-type-and-name-description": "Objekte vom Typ '{{assetTypes}}' und Name beginnend mit '{{prefix}}'", + "filter-type-state-entity": "Entität aus Dashboard-Zustand", + "filter-type-state-entity-description": "Entität aus Dashboard-Zustandsparametern", + "filter-type-asset-type": "Asset-Typ", + "filter-type-asset-type-description": "Assets vom Typ '{{assetTypes}}'", + "filter-type-asset-type-and-name-description": "Assets vom Typ '{{assetTypes}}' mit Namen beginnend mit '{{prefix}}'", "filter-type-device-type": "Gerätetyp", "filter-type-device-type-description": "Geräte vom Typ '{{deviceTypes}}'", - "filter-type-device-type-and-name-description": "Geräte vom Typ '{{deviceTypes}}' und Name beginnend mit '{{prefix}}'", + "filter-type-device-type-and-name-description": "Geräte vom Typ '{{deviceTypes}}' mit Namen beginnend mit '{{prefix}}'", "filter-type-entity-view-type": "Entitätsansichtstyp", "filter-type-entity-view-type-description": "Entitätsansichten vom Typ '{{entityViewTypes}}'", - "filter-type-entity-view-type-and-name-description": "Entitätsansichten vom Typ '{{entityViewTypes}}' und Name beginnend mit '{{prefix}}'", - "filter-type-edge-type": "Randtyp", - "filter-type-edge-type-description": "Rand vom Typ '{{edgeTypes}}'", + "filter-type-entity-view-type-and-name-description": "Entitätsansichten vom Typ '{{entityViewTypes}}' mit Namen beginnend mit '{{prefix}}'", + "filter-type-edge-type": "Edge-Typ", + "filter-type-edge-type-description": "Edges vom Typ '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description": "Edges vom Typ '{{edgeTypes}}' mit Namen beginnend mit '{{prefix}}'", "filter-type-relations-query": "Beziehungsabfrage", - "filter-type-relations-query-description": "{{entities}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-edge-search-query": "Edge-Abfrage", - "filter-type-edge-search-query-description": "Edge vom Typ {{edgeTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-asset-search-query": "Objektabfrage", - "filter-type-asset-search-query-description": "Objekte vom Typ {{assetTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-device-search-query": "Geräteabfrage", - "filter-type-device-search-query-description": "Geräte vom Typ {{deviceTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-entity-view-search-query": "Entitätsansichtsabfrage", - "filter-type-entity-view-search-query-description": "Entitätsansichten vom Typ {{entityViewTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", + "filter-type-relations-query-description": "{{entities}}, die eine '{{relationType}}'-Beziehung '{{direction}}' zu '{{rootEntity}}' haben", + "filter-type-edge-search-query": "Edge-Suchabfrage", + "filter-type-edge-search-query-description": "Edges vom Typ {{edgeTypes}} mit '{{relationType}}'-Beziehung '{{direction}}' zu {{rootEntity}}", + "filter-type-asset-search-query": "Asset-Suchabfrage", + "filter-type-asset-search-query-description": "Assets vom Typ {{assetTypes}} mit '{{relationType}}'-Beziehung '{{direction}}' zu {{rootEntity}}", + "filter-type-device-search-query": "Geräte-Suchabfrage", + "filter-type-device-search-query-description": "Geräte vom Typ {{deviceTypes}} mit '{{relationType}}'-Beziehung '{{direction}}' zu {{rootEntity}}", + "filter-type-entity-view-search-query": "Entitätsansicht-Suchabfrage", + "filter-type-entity-view-search-query-description": "Entitätsansichten vom Typ {{entityViewTypes}} mit '{{relationType}}'-Beziehung '{{direction}}' zu {{rootEntity}}", + "filter-type-apiUsageState": "API-Nutzungsstatus", "entity-filter": "Entitätsfilter", "resolve-multiple": "Als mehrere Entitäten auflösen", + "resolve-multiple-hint": "Aktivieren, um Daten aller gefilterten Entitäten gleichzeitig anzuzeigen. \nWenn deaktiviert, zeigt das Widget nur die Daten der ausgewählten Entität an.", "filter-type": "Filtertyp", "filter-type-required": "Filtertyp ist erforderlich.", - "entity-filter-no-entity-matched": "Es wurden keine Entitäten gefunden, die dem angegebenen Filter entsprechen.", - "no-entity-filter-specified": "Es wurde kein Entitätsfilter angegeben", - "root-state-entity": "Dashboard Status Entität als Wurzel verwenden", - "root-entity": "Wurzelentität", - "state-entity-parameter-name": "Parameter-Name der Statusentität", - "default-state-entity": "Standard Statusentität", + "entity-filter-no-entity-matched": "Keine Entitäten entsprechen dem angegebenen Filter.", + "no-entity-filter-specified": "Kein Entitätsfilter angegeben", + "root-state-entity": "Dashboard-Zustandsentität als Root verwenden", + "last-level-relation": "Nur letzte Beziehungsebene abrufen", + "root-entity": "Root-Entität", + "state-entity-parameter-name": "Parametername der Zustandsentität", + "default-state-entity": "Standard-Zustandsentität", "default-entity-parameter-name": "Standardmäßig", - "max-relation-level": "Maximale Beziehungstiefe", - "unlimited-level": "Unbegrenzte Ebenen", - "state-entity": "Dashboard Status Entität", + "max-relation-level": "Maximale Beziehungsebene", + "unlimited-level": "Unbegrenzte Ebene", + "state-entity": "Dashboard-Zustandsentität", "all-entities": "Alle Entitäten", - "any-relation": "Jede Beziehung" + "any-relation": "beliebige" }, "asset": { - "all": "Alle", - "all-assets": "Alle Objekte", - "groups": "Gruppen", - "shared": "Geteilt", - "asset": "Objekt", - "assets": "Objekte", - "management": "Objektverwaltung", - "view-assets": "Objekte anzeigen", - "add": "Objekt hinzufügen", - "asset-type-max-length": "Objekttyp sollte kürzer als 256 Zeichen sein", - "assign-to-customer": "Einem Kunden zuordnen", - "assign-asset-to-customer": "Objekte dem Kunden zuordnen", - "assign-asset-to-customer-text": "Bitte wählen Sie die Objekte aus, die dem Kunden zugeordnet werden sollen", - "no-assets-text": "Keine Objekte gefunden", - "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Objekte zugeordnet werden sollen", + "asset": "Asset", + "assets": "Assets", + "management": "Asset-Verwaltung", + "view-assets": "Assets anzeigen", + "add": "Asset hinzufügen", + "asset-type-max-length": "Asset-Typ sollte weniger als 256 Zeichen haben", + "assign-to-customer": "Dem Kunden zuweisen", + "assign-asset-to-customer": "Asset(s) dem Kunden zuweisen", + "assign-asset-to-customer-text": "Bitte wählen Sie die Assets aus, die dem Kunden zugewiesen werden sollen", + "no-assets-text": "Keine Assets gefunden", + "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem das/die Asset(s) zugewiesen werden sollen", "public": "Öffentlich", - "assignedToCustomer": "Kunden zugeordnet", - "make-public": "Objekt öffentlich machen", - "make-private": "Objekt privat machen", - "unassign-from-customer": "Kundenzuordnung aufheben", - "delete": "Objekt löschen", - "asset-public": "Objekt ist öffentlich", - "asset-type": "Objekttyp", - "asset-type-required": "Objekttyp ist erforderlich.", - "select-asset-type": "Objekttyp auswählen", - "enter-asset-type": "Objekttyp bestätigen", - "any-asset": "Jedes Objekt", - "no-asset-types-matching": "Es wurden keine zu '{{entitySubtype}}' passenden Objekte gefunden.", - "asset-type-list-empty": "Keine Objekttypen ausgewählt.", - "asset-types": "Objekttypen", + "assignedToCustomer": "Dem Kunden zugewiesen", + "make-public": "Asset öffentlich machen", + "make-private": "Asset privat machen", + "unassign-from-customer": "Von Kunde entfernen", + "delete": "Asset löschen", + "asset-public": "Asset ist öffentlich", + "asset-type": "Asset-Typ", + "asset-type-required": "Asset-Typ ist erforderlich.", + "select-asset-type": "Asset-Typ auswählen", + "enter-asset-type": "Asset-Profil eingeben", + "any-asset": "Beliebiges Asset", + "no-asset-types-matching": "Keine Asset-Typen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "asset-type-list-empty": "Keine Asset-Typen ausgewählt.", + "asset-types": "Asset-Typen", "name": "Name", "name-required": "Name ist erforderlich.", - "name-max-length": "Name sollte weniger als 256 Zeichen sein", - "label-max-length": "Label sollte weniger als 256 Zeichen sein", + "name-max-length": "Name sollte weniger als 256 Zeichen haben", + "label-max-length": "Label sollte weniger als 256 Zeichen haben", "description": "Beschreibung", "type": "Typ", "type-required": "Typ ist erforderlich.", "details": "Details", "events": "Ereignisse", - "add-asset-text": "Neues Objekt hinzufügen", - "asset-details": "Objektdetails", - "assign-assets": "Objekte zuordnen", - "assign-assets-text": "Kunden { count, plural, =1 {1 Objekt} other {# Objekte} } zuordnen", - "assign-asset-to-edge-text":"Bitte wählen Sie die Objekte aus, die dem Edge zugeordnet werden sollen", - "delete-assets": "Objekte löschen", - "unassign-assets": "Objektzuordnungen aufheben", - "unassign-assets-action-title": "Kunden { count, plural, =1 {1 Objektzuordnung} other {# Objektzuordnungen} } aufheben", - "assign-new-asset": "Neues Objekt zuordnen", - "delete-asset-title": "Sind Sie sicher, dass Sie das Objekt '{{assetName}}' löschen möchten?", - "delete-asset-text": "Vorsicht, nach Bestätigung wird das Objekt und alle zugehörigen Daten nicht wiederherstellbar gelöscht.", - "delete-assets-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Objekt} other {# Objekte} } löschen möchten?", - "delete-assets-action-title": "{ count, plural, =1 {1 Objekt} other {# Objekte} } löschen", - "delete-assets-text": "Vorsicht, nach Bestätigung werden die ausgewählten Objekte und alle zugehörigen Daten nicht wiederherstellbar gelöscht", - "make-public-asset-title": "Sind Sie sicher, dass Sie das Objekt '{{assetName}}' öffentlich machen möchten?", - "make-public-asset-text": "Nach Bestätigung wird das Objekt und alle zugehörigen Daten anderen zugänglich gemacht.", - "make-private-asset-title": "Sind Sie sicher, dass Sie das Objekt '{{assetName}}' privat machen möchten?", - "make-private-asset-text": "Nach Bestätigung wird das Objekt und alle zugehörigen Daten privat und ist für andere nicht mehr zugänglich.", - "unassign-asset-title": "Sind Sie sicher, dass Sie die Zuordnung für das Objekt '{{assetName}}' aufheben möchten?", - "unassign-asset-text": "Nach Bestätigung wird die Zuordnung des Objekts aufgehoben und es ist für den Kunden nicht mehr zugänglich.", - "unassign-asset": "Zuordnung des Objekts aufheben", - "unassign-assets-title": "Möchten Sie die Zuordnung von { count, plural, =1 {1 Objekt} other {# Objekte} } aufheben?", - "unassign-assets-text": "Nach Bestätigung wird die Zuordnung der ausgewählten Objekte aufgehoben und sie sind für den Kunden nicht mehr zugänglich.", - "copyId": "Objekt-ID kopieren", - "idCopiedMessage": "Objekt-ID wurde in die Zwischenablage kopiert", - "select-asset": "Objekt auswählen", - "no-assets-matching": "Es wurden keine zu '{{entity}}' passenden Objekte gefunden.", - "asset-required": "Objekt ist erforderlich", - "name-starts-with": "Name des Objekts beginnt mit", - "label": "Bezeichnung", - "assign-asset-to-edge": "Objekte dem Rand zuordnen", - "unassign-asset-from-edge": "Objekte von Rand entfernen", - "unassign-asset-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für das Objekt '{{assetName}}' aufheben möchten?", - "unassign-asset-from-edge-text": "Nach Bestätigung wird die Zuordnung des Objekts aufgehoben und es ist für den Kunden nicht mehr zugänglich.", - "unassign-assets-from-edge-action-title": "Rand { count, plural, =1 {1 Objektzuordnung} other {# Objektzuordnungen} } aufheben", - "unassign-assets-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für das Objekt '{{assetName}}' wirklich aufheben möchten?", - "unassign-assets-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Objekte nicht zugewiesen und sind für den Rand nicht zugänglich." + "add-asset-text": "Neues Asset hinzufügen", + "asset-details": "Asset-Details", + "assign-assets": "Assets zuweisen", + "assign-assets-text": "{ count, plural, =1 {1 Asset} other {# Assets} } dem Kunden zuweisen", + "assign-asset-to-edge-title": "Asset(s) einem Edge zuweisen", + "assign-asset-to-edge-text": "Bitte wählen Sie die Assets aus, die dem Edge zugewiesen werden sollen", + "delete-assets": "Assets löschen", + "unassign-assets": "Assets entfernen", + "unassign-assets-action-title": "{ count, plural, =1 {1 Asset} other {# Assets} } vom Kunden entfernen", + "assign-new-asset": "Neues Asset zuweisen", + "delete-asset-title": "Möchten Sie das Asset '{{assetName}}' wirklich löschen?", + "delete-asset-text": "Vorsicht, nach der Bestätigung wird das Asset und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-assets-title": "Möchten Sie { count, plural, =1 {1 Asset} other {# Assets} } wirklich löschen?", + "delete-assets-action-title": "{ count, plural, =1 {1 Asset löschen} other {# Assets löschen} }", + "delete-assets-text": "Vorsicht, nach der Bestätigung werden alle ausgewählten Assets entfernt und alle zugehörigen Daten werden unwiederbringlich gelöscht.", + "make-public-asset-title": "Möchten Sie das Asset '{{assetName}}' wirklich öffentlich machen?", + "make-public-asset-text": "Nach der Bestätigung wird das Asset und alle zugehörigen Daten öffentlich zugänglich sein.", + "make-private-asset-title": "Möchten Sie das Asset '{{assetName}}' wirklich privat machen?", + "make-private-asset-text": "Nach der Bestätigung wird das Asset und alle zugehörigen Daten privat sein und nicht für andere zugänglich.", + "unassign-asset-title": "Möchten Sie das Asset '{{assetName}}' wirklich entfernen?", + "unassign-asset-text": "Nach der Bestätigung wird das Asset entfernt und ist für den Kunden nicht mehr zugänglich.", + "unassign-asset": "Asset entfernen", + "unassign-assets-title": "Möchten Sie { count, plural, =1 {1 Asset} other {# Assets} } wirklich entfernen?", + "unassign-assets-text": "Nach der Bestätigung werden alle ausgewählten Assets entfernt und sind für den Kunden nicht mehr zugänglich.", + "copyId": "Asset-ID kopieren", + "idCopiedMessage": "Asset-ID wurde in die Zwischenablage kopiert", + "select-asset": "Asset auswählen", + "no-assets-matching": "Keine Assets gefunden, die mit '{{entity}}' übereinstimmen.", + "asset-required": "Asset ist erforderlich", + "name-starts-with": "Ausdruck für Assetnamen", + "help-text": "Verwenden Sie '%' je nach Bedarf: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search": "Assets suchen", + "import": "Assets importieren", + "asset-file": "Asset-Datei", + "label": "Label", + "assign-asset-to-edge": "Asset(s) einem Edge zuweisen", + "unassign-asset-from-edge": "Asset vom Edge entfernen", + "unassign-asset-from-edge-title": "Möchten Sie das Asset '{{assetName}}' wirklich vom Edge entfernen?", + "unassign-asset-from-edge-text": "Nach der Bestätigung wird das Asset entfernt und ist für den Edge nicht mehr zugänglich.", + "unassign-assets-from-edge-title": "Möchten Sie { count, plural, =1 {1 Asset} other {# Assets} } wirklich vom Edge entfernen?", + "unassign-assets-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Assets entfernt und sind für den Edge nicht mehr zugänglich.", + "selected-assets": "{ count, plural, =1 {1 Asset} other {# Assets} } ausgewählt" }, "attribute": { - "attributes": "Eigenschaften", + "attributes": "Attribute", "latest-telemetry": "Neueste Telemetrie", - "no-latest-telemetry": "Keine Telemetriedaten", - "attributes-scope": "Entitätseigenschaftsbereich", + "no-latest-telemetry": "Keine neueste Telemetrie", + "attributes-scope": "Bereich der Entitätsattribute", "scope-telemetry": "Telemetrie", "scope-latest-telemetry": "Neueste Telemetrie", - "scope-client": "Client Eigenschaften", - "scope-server": "Server Eigenschaften", - "scope-shared": "Gemeinsame Eigenschaften", - "add": "Eigenschaft hinzufügen", - "add-attribute-prompt": "Bitte Attribut hinzufügen", + "scope-client": "Client-Attribute", + "scope-server": "Server-Attribute", + "scope-shared": "Geteilte Attribute", + "scope-client-short": "Client", + "scope-server-short": "Server", + "scope-shared-short": "Geteilt", + "scope-latest-short": "Neueste", + "scope-any": "Beliebig", + "add": "Attribut hinzufügen", "key": "Schlüssel", - "key-max-length": "Der Schlüssel sollte weniger als 256 Zeichen haben", - "last-update-time": "Datum der letzten Aktualisierung", - "key-required": "Eigenschaftsschlüssel ist erforderlich.", + "key-max-length": "Schlüssel sollte weniger als 256 Zeichen enthalten", + "last-update-time": "Letzte Aktualisierungszeit", + "key-required": "Attributschlüssel ist erforderlich.", "value": "Wert", - "value-required": "Eigenschaftswert ist erforderlich.", + "value-required": "Attributwert ist erforderlich.", "telemetry-key-required": "Telemetrieschlüssel ist erforderlich", "telemetry-value-required": "Telemetriewert ist erforderlich", - "delete-attributes-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Eigenschaft} other {# Eigenschaften} } löschen möchten?", - "delete-attributes-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Eigenschaften entfernt.", - "delete-attributes": "Eigenschaften löschen", - "enter-attribute-value": "Geben Sie den Eigenschaftswert ein", + "delete-attributes-title": "Möchten Sie { count, plural, =1 {1 Attribut} other {# Attribute} } wirklich löschen?", + "delete-attributes-text": "Vorsicht, nach der Bestätigung werden alle ausgewählten Attribute entfernt.", + "delete-attributes": "Attribute löschen", + "enter-attribute-value": "Attributwert eingeben", "show-on-widget": "Im Widget anzeigen", "widget-mode": "Widget-Modus", "next-widget": "Nächstes Widget", "prev-widget": "Vorheriges Widget", "add-to-dashboard": "Zum Dashboard hinzufügen", "add-widget-to-dashboard": "Widget zum Dashboard hinzufügen", - "selected-attributes": "{ count, plural, =1 {1 Eigenschaft} other {# Eigenschaften} } ausgewählt", - "selected-telemetry": "{ count, plural, =1 {1 Telemetrieeinheit } other {# Telemetrieeinheiten} } ausgewählt", + "selected-attributes": "{ count, plural, =1 {1 Attribut} other {# Attribute} } ausgewählt", + "selected-telemetry": "{ count, plural, =1 {1 Telemetrieeinheit} other {# Telemetrieeinheiten} } ausgewählt", "no-attributes-text": "Keine Attribute gefunden", - "no-telemetry-text": "Keine Telemetriedaten gefunden", + "no-telemetry-text": "Keine Telemetrie gefunden", "copy-key": "Schlüssel kopieren", - "add-telemetry": "Telemetriedaten hinzufügen", - "copy-value": "Wert kopieren" + "add-telemetry": "Telemetrie hinzufügen", + "copy-value": "Wert kopieren", + "delete-timeseries": { + "start-time": "Startzeit", + "ends-on": "Endet am", + "strategy": "Strategie", + "delete-strategy": "Löschstrategie", + "all-data": "Alle Daten löschen", + "all-data-except-latest-value": "Alle Daten außer dem neuesten Wert löschen", + "latest-value": "Neuester Wert löschen", + "all-data-for-time-period": "Alle Daten für einen Zeitraum löschen", + "rewrite-latest-value": "Neuester Wert neu schreiben" + } + }, + "api-usage": { + "api-features": "API-Funktionen", + "api-usage": "API-Nutzung", + "alarm": "Alarm", + "alarms-created": "Erstellte Alarme", + "queue-stats": "Warteschlangenstatistiken", + "processing-failures-and-timeouts": "Verarbeitungsfehler und Zeitüberschreitungen", + "exceptions": "Ausnahmen", + "alarms-created-daily-activity": "Tägliche Aktivität der Alarmerstellung", + "alarms-created-hourly-activity": "Stündliche Aktivität der Alarmerstellung", + "alarms-created-monthly-activity": "Monatliche Aktivität der Alarmerstellung", + "data-points": "Datenpunkte", + "data-points-storage-days": "Speichertage der Datenpunkte", + "device-api": "Geräte-API", + "email": "E-Mail", + "email-messages": "E-Mail-Nachrichten", + "email-messages-daily-activity": "Tägliche Aktivität der E-Mail-Nachrichten", + "email-messages-monthly-activity": "Monatliche Aktivität der E-Mail-Nachrichten", + "executions": "Ausführungen", + "scripts": "Skripte", + "scripts-hourly-activity": "Stündliche Skriptaktivität", + "scripts-daily-activity": "Tägliche Skriptaktivität", + "scripts-monthly-activity": "Monatliche Skriptaktivität", + "javascript": "JavaScript", + "javascript-executions": "JavaScript-Ausführungen", + "tbel": "TBEL", + "tbel-executions": "TBEL-Ausführungen", + "latest-error": "Letzter Fehler", + "messages": "Nachrichten", + "notifications": "Benachrichtigungen", + "notifications-email-sms": "Benachrichtigungen (E-Mail/SMS)", + "notifications-hourly-activity": "Stündliche Benachrichtigungsaktivität", + "permanent-failures": "${entityName} Dauerhafte Fehler", + "permanent-timeouts": "${entityName} Dauerhafte Zeitüberschreitungen", + "processing-failures": "${entityName} Verarbeitungsfehler", + "processing-timeouts": "${entityName} Verarbeitungszeitüberschreitungen", + "rule-chain": "Regelkette", + "rule-engine": "Regel-Engine", + "rule-engine-daily-activity": "Tägliche Aktivität der Regel-Engine", + "rule-engine-executions": "Ausführungen der Regel-Engine", + "rule-engine-hourly-activity": "Stündliche Aktivität der Regel-Engine", + "rule-engine-monthly-activity": "Monatliche Aktivität der Regel-Engine", + "rule-engine-statistics": "Regel-Engine-Statistiken", + "rule-node": "Regelknoten", + "sms": "SMS", + "sms-messages": "SMS-Nachrichten", + "sms-messages-daily-activity": "Tägliche Aktivität der SMS-Nachrichten", + "sms-messages-monthly-activity": "Monatliche Aktivität der SMS-Nachrichten", + "successful": "${entityName} Erfolgreich", + "telemetry": "Telemetrie", + "telemetry-persistence": "Telemetriepersistenz", + "telemetry-persistence-daily-activity": "Tägliche Aktivität der Telemetriepersistenz", + "telemetry-persistence-hourly-activity": "Stündliche Aktivität der Telemetriepersistenz", + "telemetry-persistence-monthly-activity": "Monatliche Aktivität der Telemetriepersistenz", + "transport": "Transport", + "transport-daily-activity": "Tägliche Transportaktivität", + "transport-data-points": "Transportierte Datenpunkte", + "transport-hourly-activity": "Stündliche Transportaktivität", + "transport-messages": "Transportnachrichten", + "transport-monthly-activity": "Monatliche Transportaktivität", + "view-details": "Details anzeigen", + "view-statistics": "Statistiken anzeigen" + }, + "api-limit": { + "cassandra-queries": "Cassandra-Abfragen", + "entity-version-creation": "Erstellung von Entitätsversionen", + "entity-version-load": "Laden von Entitätsversionen", + "notification-requests": "Benachrichtigungsanforderungen", + "notification-requests-per-rule": "Benachrichtigungsanforderungen pro Regel", + "rest-api-requests": "REST-API-Anfragen", + "rest-api-requests-per-customer": "REST-API-Anfragen pro Kunde", + "transport-messages": "Transportnachrichten", + "transport-messages-per-device": "Transportnachrichten pro Gerät", + "transport-messages-per-gateway": "Transportnachrichten pro Gateway", + "transport-messages-per-gateway-device": "Transportnachrichten pro Gateway-Gerät", + "ws-updates-per-session": "WebSocket-Aktualisierungen pro Sitzung", + "edge-events": "Edge-Ereignisse", + "edge-events-per-edge": "Edge-Ereignisse pro Edge", + "edge-uplink-messages": "Edge-Uplink-Nachrichten", + "edge-uplink-messages-per-edge": "Edge-Uplink-Nachrichten pro Edge" }, "audit-log": { "audit": "Audit", "audit-logs": "Audit-Protokolle", "timestamp": "Zeitstempel", - "entity-type": "Entitätstype", + "entity-type": "Entitätstyp", "entity-name": "Entitätsname", - "user": "User", + "user": "Benutzer", "type": "Typ", "status": "Status", "details": "Details", "type-added": "Hinzugefügt", "type-deleted": "Gelöscht", "type-updated": "Aktualisiert", - "type-attributes-updated": "Eigenschaften aktualisiert", - "type-attributes-deleted": "Eigenschaften gelöscht", - "type-rpc-call": "RPC Aufruf", - "type-credentials-updated": "Anmeldeinformationen wurden aktualisiert", - "type-assigned-to-customer": "Kunden Zuordnung", - "type-unassigned-from-customer": "Kunden Zuordnung aufgehoben", - "type-assigned-to-edge": "Zum Edge hinzugefügt", - "type-unassigned-from-edge": "Vom Edge entfernt", + "type-attributes-updated": "Attribute aktualisiert", + "type-attributes-deleted": "Attribute gelöscht", + "type-rpc-call": "RPC-Aufruf", + "type-credentials-updated": "Zugangsdaten aktualisiert", + "type-assigned-to-customer": "Kunde zugewiesen", + "type-unassigned-from-customer": "Vom Kunden entfernt", + "type-assigned-to-edge": "Edge zugewiesen", + "type-unassigned-from-edge": "Von Edge entfernt", "type-activated": "Aktiviert", - "type-suspended": "Ausgesetzt", - "type-credentials-read": "Anmeldeinformationen gelesen", - "type-attributes-read": "Eigenschaften gelesen", - "type-added-to-entity-group": "Zur Gruppe hinzugefügt", - "type-removed-from-entity-group": "Aus der Gruppe entfernt", + "type-suspended": "Suspendiert", + "type-credentials-read": "Zugangsdaten gelesen", + "type-attributes-read": "Attribute gelesen", "type-relation-add-or-update": "Beziehung aktualisiert", "type-relation-delete": "Beziehung gelöscht", "type-relations-delete": "Alle Beziehungen gelöscht", - "type-alarm-ack": "Bestätigt", - "type-alarm-clear": "Gelöscht", - "type-alarm-assign": "Zugeweisen", - "type-alarm-unassign": "nicht zugewiesen", + "type-alarm-ack": "Alarm bestätigt", + "type-alarm-clear": "Alarm zurückgesetzt", + "type-alarm-delete": "Alarm gelöscht", + "type-alarm-assign": "Alarm zugewiesen", + "type-alarm-unassign": "Alarm nicht zugewiesen", "type-added-comment": "Kommentar hinzugefügt", "type-updated-comment": "Kommentar aktualisiert", "type-deleted-comment": "Kommentar gelöscht", - "type-rest-api-rule-engine-call": "Regelkette REST API Aufruf", - "type-made-public": "Öffentlich machen", - "type-made-private": "Privat machen", - "type-login": "Anmeldung", - "type-logout": "Ausloggen", - "type-lockout": "Aussperrung", - "status-success": "Erfolg", - "status-failure": "Fehler", - "audit-log-details": "Audit-Protokoll Details", + "type-login": "Login", + "type-logout": "Logout", + "type-lockout": "Sperrung", + "status-success": "Erfolgreich", + "status-failure": "Fehlgeschlagen", + "audit-log-details": "Audit-Log-Details", "no-audit-logs-prompt": "Keine Protokolle gefunden", "action-data": "Aktionsdaten", "failure-details": "Fehlerdetails", - "search": "Audit-Protokolle durchsuchen", - "clear-search": "Suche leeren", - "type-assigned-from-tenant": "Vom Tenant zugewiesen", - "type-assigned-to-tenant": "Dem Tenant zugewiesen", - "type-provision-success": "Gerätebereitstellung erfolgreich", + "search": "Audit-Logs durchsuchen", + "clear-search": "Suche zurücksetzen", + "type-assigned-from-tenant": "Vom Mandanten zugewiesen", + "type-assigned-to-tenant": "Mandant zugewiesen", + "type-provision-success": "Gerät bereitgestellt", "type-provision-failure": "Gerätebereitstellung fehlgeschlagen", - "type-timeseries-updated": "Telemetriedaten aktualisiert", - "type-timeseries-deleted": "Telemetriedaten gelöscht", - "type-owner-changed": "Besitzer wurde gewechselt", + "type-timeseries-updated": "Telemetrie aktualisiert", + "type-timeseries-deleted": "Telemetrie gelöscht", "type-sms-sent": "SMS gesendet" }, + "debug-settings": { + "label": "Debug-Konfiguration", + "on-failure": "Nur Fehler (24/7)", + "all-messages": "Alle Nachrichten ({{time}})", + "failures": "Fehler", + "entity": "Entität", + "hint": { + "main-limited": "Nicht mehr als {{msg}} {{entity}} Debug-Nachrichten pro {{time}} werden aufgezeichnet.", + "on-failure": "Nur Fehlermeldungen protokollieren.", + "all-messages": "Alle Debug-Nachrichten protokollieren." + } + }, + "calculated-fields": { + "expression": "Ausdruck", + "no-found": "Keine berechneten Felder gefunden", + "list": "{ count, plural, =1 {Ein berechnetes Feld} other {Liste von # berechneten Feldern} }", + "selected-fields": "{ count, plural, =1 {1 berechnetes Feld} other {# berechnete Felder} } ausgewählt", + "type": { + "simple": "Einfach", + "script": "Skript" + }, + "arguments": "Argumente", + "decimals-by-default": "Standard-Dezimalstellen", + "debugging": "Berechnetes Feld Debugging", + "argument-name": "Argumentname", + "datasource": "Datenquelle", + "add-argument": "Argument hinzufügen", + "test-script-function": "Skriptfunktion testen", + "no-arguments": "Keine Argumente konfiguriert", + "argument-settings": "Argumenteinstellungen", + "argument-current": "Aktuelle Entität", + "argument-current-tenant": "Aktueller Mandant", + "argument-device": "Gerät", + "argument-asset": "Asset", + "argument-customer": "Kunde", + "argument-tenant": "Aktueller Mandant", + "argument-type": "Argumenttyp", + "see-debug-events": "Debug-Ereignisse anzeigen", + "attribute": "Attribut", + "copy-argument-name": "Argumentnamen kopieren", + "timeseries-key": "Zeitreihen-Schlüssel", + "device-name": "Gerätename", + "latest-telemetry": "Neueste Telemetrie", + "rolling": "Zeitreihen-Rollup", + "attribute-scope": "Attributbereich", + "server-attributes": "Server-Attribute", + "client-attributes": "Client-Attribute", + "shared-attributes": "Geteilte Attribute", + "attribute-key": "Attribut-Schlüssel", + "default-value": "Standardwert", + "limit": "Maximale Werte", + "time-window": "Zeitfenster", + "customer-name": "Kundenname", + "asset-name": "Asset-Name", + "timeseries": "Zeitreihe", + "output": "Ausgabe", + "create": "Neues berechnetes Feld erstellen", + "file": "Berechnetes Feld-Datei", + "invalid-file-error": "Ungültiges Dateiformat. Bitte stellen Sie sicher, dass die Datei eine gültige JSON-Datei ist.", + "import": "Berechnetes Feld importieren", + "export": "Berechnetes Feld exportieren", + "export-failed-error": "Berechnetes Feld konnte nicht exportiert werden: {{error}}", + "output-type": "Ausgabetyp", + "delete-title": "Möchten Sie das berechnete Feld '{{title}}' wirklich löschen?", + "delete-text": "Vorsicht, nach der Bestätigung wird das berechnete Feld und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-multiple-title": "Möchten Sie { count, plural, =1 {1 berechnetes Feld} other {# berechnete Felder} } wirklich löschen?", + "delete-multiple-text": "Vorsicht, nach der Bestätigung werden alle ausgewählten berechneten Felder entfernt und alle zugehörigen Daten unwiederbringlich gelöscht.", + "test-with-this-message": "Mit dieser Nachricht testen", + "hint": { + "arguments-simple-with-rolling": "Einfacher Feldtyp darf keine Schlüssel mit Zeitreihen-Rollup-Typ enthalten.", + "arguments-empty": "Argumente dürfen nicht leer sein.", + "expression-required": "Ausdruck ist erforderlich.", + "expression-invalid": "Ausdruck ist ungültig", + "expression-max-length": "Ausdruck sollte weniger als 255 Zeichen enthalten.", + "argument-name-required": "Argumentname ist erforderlich.", + "argument-name-pattern": "Argumentname ist ungültig.", + "argument-name-duplicate": "Ein Argument mit diesem Namen existiert bereits.", + "argument-name-max-length": "Argumentname sollte weniger als 256 Zeichen enthalten.", + "argument-name-forbidden": "Argumentname ist reserviert und darf nicht verwendet werden.", + "argument-type-required": "Argumenttyp ist erforderlich.", + "max-args": "Maximale Anzahl an Argumenten erreicht.", + "decimals-range": "Standard-Dezimalstellen sollten eine Zahl zwischen 0 und 15 sein.", + "expression": "Standardausdruck demonstriert, wie eine Temperatur von Fahrenheit in Celsius umgewandelt wird.", + "arguments-entity-not-found": "Zielentität des Arguments nicht gefunden." + } + }, "confirm-on-exit": { - "message": "Sie haben nicht gespeicherte Änderungen. Möchten Sie diese Seite wirklich verlassen?", - "html-message": "Sie haben nicht gespeicherte Änderungen.
Möchten Sie diese Seite wirklich verlassen?", - "title": "Nicht gespeicherte Änderungen" + "message": "Sie haben ungespeicherte Änderungen. Möchten Sie diese Seite wirklich verlassen?", + "html-message": "Sie haben ungespeicherte Änderungen.
Möchten Sie diese Seite wirklich verlassen?", + "title": "Ungespeicherte Änderungen" }, "contact": { "country": "Land", + "country-required": "Land ist erforderlich.", "city": "Stadt", - "state": "Bundesland", - "postal-code": "Postleitzahl", + "state": "Bundesland / Provinz", + "postal-code": "PLZ / Postleitzahl", "postal-code-invalid": "Ungültiges Format der Postleitzahl.", "address": "Adresse", "address2": "Adresse 2", "phone": "Telefon", "email": "E-Mail", "no-address": "Keine Adresse", - "state-max-length": "Staat sollte weniger als 256 Zeichen haben", - "phone-max-length": "Telefonnummer sollte weniger als 256 Zeichen haben", - "city-max-length": "Ort sollte weniger als 256 Zeichen haben" + "no-country-found": "Keine Länder gefunden.", + "no-country-matching": "Kein Land gefunden, das mit '{{country}}' übereinstimmt.", + "state-max-length": "Bundesland sollte weniger als 256 Zeichen enthalten", + "phone-max-length": "Telefonnummer sollte weniger als 256 Zeichen enthalten", + "city-max-length": "Stadtname sollte weniger als 256 Zeichen enthalten" }, "common": { + "name": "Name", + "type": "Typ", + "general": "Allgemein", "username": "Benutzername", "password": "Passwort", - "enter-username": "Benutzername eingeben", + "data": "Daten", + "timestamp": "Zeitstempel", + "enter-username": "Benutzernamen eingeben", "enter-password": "Passwort eingeben", "enter-search": "Suche eingeben", "created-time": "Erstellungszeit", - "loading": "wird geladen...", + "disabled": "Deaktiviert", + "loading": "Wird geladen...", "proceed": "Fortfahren", "open-details-page": "Detailseite öffnen", "not-found": "Nicht gefunden", - "documentation": "Dokumentation" + "value": "Wert", + "documentation": "Dokumentation", + "time-left": "{{time}} verbleibend", + "output": "Ausgabe", + "suffix": { + "s": "s", + "ms": "ms" + }, + "hint": { + "name-required": "Name ist erforderlich.", + "name-pattern": "Name ist ungültig.", + "name-max-length": "Name sollte weniger als 256 Zeichen enthalten.", + "title-required": "Titel ist erforderlich.", + "title-pattern": "Titel ist ungültig.", + "title-max-length": "Titel sollte weniger als 256 Zeichen enthalten.", + "key-required": "Schlüssel ist erforderlich.", + "key-pattern": "Schlüssel ist ungültig.", + "key-max-length": "Schlüssel sollte weniger als 256 Zeichen enthalten." + }, + "required-fields": "Erforderliche Felder fehlen" }, "content-type": { "json": "Json", "text": "Text", "binary": "Binär (Base64)" }, + "color": { + "color": "Farbe" + }, "customer": { - "all": "Alle", - "all-customers": "Alle Kunden", - "groups": "Gruppen", - "shared": "Geteilt", - "hierarchy": "Hierarchie", "customer": "Kunde", "customers": "Kunden", "management": "Kundenverwaltung", - "dashboard": "Kunden Dashboard", - "dashboards": "Kunden Dashboards", + "dashboard": "Kundendashboard", + "dashboards": "Kundendashboards", "devices": "Kundengeräte", - "entity-views": "Kunden Entitätsansichten", - "assets": "Kundenobjekte", + "entity-views": "Kundenansichten", + "assets": "Kundenassets", "public-dashboards": "Öffentliche Dashboards", "public-devices": "Öffentliche Geräte", - "public-assets": "Öffentliche Objekte", + "public-assets": "Öffentliche Assets", "public-entity-views": "Öffentliche Entitätsansichten", "add": "Kunde hinzufügen", "delete": "Kunde löschen", - "manage-customer-users": "Kundenbenutzer verwalten", + "manage-customer-users": "Kundennutzer verwalten", "manage-customer-devices": "Kundengeräte verwalten", - "manage-customer-dashboards": "Kunden-Dashboards verwalten", + "manage-customer-dashboards": "Kundendashboards verwalten", "manage-public-devices": "Öffentliche Geräte verwalten", "manage-public-dashboards": "Öffentliche Dashboards verwalten", - "manage-customer-assets": "Kundenobjekte verwalten", - "manage-public-assets": "Öffentliche Objekte verwalten", - "manage-public-edges": "Öffentliche Rand verwalten", + "manage-customer-assets": "Kundenassets verwalten", + "manage-customer-edges": "Kunden-Edges verwalten", + "manage-public-assets": "Öffentliche Assets verwalten", "add-customer-text": "Neuen Kunden hinzufügen", "no-customers-text": "Keine Kunden gefunden", "customer-details": "Kundendetails", - "delete-customer-title": "Sind Sie sicher, dass der Kunde '{{customerTitle}}' gelöscht werden soll?", - "delete-customer-text": "Vorsicht, nach Bestätigung wird der Kunde und alle zugehörigen Daten nicht wiederherstellbar gelöscht.", - "delete-customers-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Kunde} other {# Kunden} } löschen möchten?", - "delete-customers-action-title": "{ count, plural, =1 {1 Kunde} other {# Kunden} } löschen", - "delete-customers-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Kunden und alle zugehörigen Daten nicht wiederherstellbar gelöscht.", - "manage-users": "User verwalten", - "manage-assets": "Objekte verwalten", + "delete-customer-title": "Möchten Sie den Kunden '{{customerTitle}}' wirklich löschen?", + "delete-customer-text": "Achtung, nach der Bestätigung werden der Kunde und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-customers-title": "Möchten Sie { count, plural, =1 {1 Kunden} other {# Kunden} } wirklich löschen?", + "delete-customers-action-title": "{ count, plural, =1 {1 Kunden löschen} other {# Kunden löschen} }", + "delete-customers-text": "Achtung, nach der Bestätigung werden alle ausgewählten Kunden und alle zugehörigen Daten gelöscht.", + "manage-users": "Benutzer verwalten", + "manage-assets": "Assets verwalten", "manage-devices": "Geräte verwalten", "manage-dashboards": "Dashboards verwalten", "title": "Titel", "title-required": "Titel ist erforderlich.", - "title-max-length": "Titel sollte weniger asl 256 Zeichen haben", + "title-max-length": "Titel sollte weniger als 256 Zeichen enthalten", "description": "Beschreibung", "details": "Details", "events": "Ereignisse", "copyId": "Kunden-ID kopieren", "idCopiedMessage": "Kunden-ID wurde in die Zwischenablage kopiert", - "select-customer": "Kunden auswählen", - "no-customers-matching": "Keine Kunden für '{{entity}}' gefunden.", + "select-customer": "Kunde auswählen", + "no-customers-matching": "Keine Kunden gefunden, die mit '{{entity}}' übereinstimmen.", "customer-required": "Kunde ist erforderlich", - "select-default-customer": "Wählen Sie den Standardkunden aus.", + "select-default-customer": "Standardkunden auswählen", "default-customer": "Standardkunde", - "edge-instances": "Kunden Rand", - "default-customer-required": "Ein Standardkunde ist erforderlich, um das Dashboard auf Mandantenebene zu testen." + "default-customer-required": "Standardkunde ist erforderlich, um das Dashboard auf Mandantenebene zu debuggen", + "search": "Kunden suchen", + "selected-customers": "{ count, plural, =1 {1 Kunde} other {# Kunden} } ausgewählt", + "edges": "Edge-Instanzen des Kunden", + "manage-edges": "Edges verwalten" + }, + "css-size": { + "size-value-required": "Größenwert ist erforderlich", + "invalid-size-value": "Ungültiger Größenwert" + }, + "date": { + "last-update-n-ago": "Letzte Aktualisierung vor N", + "last-update-n-ago-text": "Letzte Aktualisierung {{ agoText }}", + "custom-date": "Benutzerdefiniertes Datum", + "format": "Format", + "preview": "Vorschau", + "auto": "Automatisch", + "time-granularity-formats": "Formate nach Zeitgranularität", + "unit-year": "Jahre", + "unit-month": "Monate", + "unit-day": "Tage", + "unit-hour": "Stunden", + "unit-minute": "Minuten", + "unit-second": "Sekunden", + "unit-millisecond": "Millisekunden" }, "datetime": { "date-from": "Datum von", "time-from": "Zeit von", "date-to": "Datum bis", - "time-to": "Zeit bis" + "time-to": "Zeit bis", + "from": "Von", + "to": "Bis" }, "dashboard": { "dashboard": "Dashboard", "dashboards": "Dashboards", - "management": "Dashboardverwaltung", + "management": "Dashboard-Verwaltung", "view-dashboards": "Dashboards anzeigen", "add": "Dashboard hinzufügen", - "assign-dashboard-to-customer": "Dashboard(s) dem Kunden zuordnen", - "assign-dashboard-to-customer-text": "Bitte wählen Sie die Dashboards aus, die Sie dem Kunden zuordnen möchten", - "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Dashboards zugeordnet werden sollen", - "assign-to-customer": "Kunden zuordnen", - "unassign-from-customer": "Zuordnung zum Kunden aufheben", + "assign-dashboard-to-customer": "Dashboard(s) dem Kunden zuweisen", + "assign-dashboard-to-customer-text": "Bitte wählen Sie die Dashboards aus, die dem Kunden zugewiesen werden sollen", + "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem das Dashboard zugewiesen werden soll", + "assign-to-customer": "Dem Kunden zuweisen", + "unassign-from-customer": "Von Kunde entfernen", "make-public": "Dashboard öffentlich machen", "make-private": "Dashboard privat machen", - "manage-assigned-customers": "Zugeordnete Kunden verwalten", - "assigned-customers": "Zugeordnete Kunden", - "assign-to-customers": "Dashboard(s) zu Kunden zuweisen", - "assign-to-customers-text": "Bitte wählen Sie den Kunden aus, dem Sie das Dashboard(s) zuweisen möchten", - "unassign-from-customers": "Zuordnung von Dashboard(s) zu Kunden aufheben", - "unassign-from-customers-text": "Bitte wählen Sie die Kunden aus, für die die Zuordnung von Dashboard(s) aufgehoben werden soll", - "no-dashboards-text": "Keine Dashboard(s) gefunden", + "manage-assigned-customers": "Zugewiesene Kunden verwalten", + "assigned-customers": "Zugewiesene Kunden", + "assign-to-customers": "Dashboard(s) Kunden zuweisen", + "assign-to-customers-text": "Bitte wählen Sie die Kunden aus, denen das Dashboard zugewiesen werden soll", + "unassign-from-customers": "Dashboard(s) von Kunden entfernen", + "unassign-from-customers-text": "Bitte wählen Sie die Kunden aus, von denen das Dashboard entfernt werden soll", + "no-dashboards-text": "Keine Dashboards gefunden", "no-widgets": "Keine Widgets konfiguriert", "add-widget": "Neues Widget hinzufügen", + "add-widget-button-text": "Widget hinzufügen", "title": "Titel", + "image": "Dashboard-Bild", + "mobile-app-settings": "Einstellungen für mobile App", + "mobile-order": "Reihenfolge des Dashboards in mobiler App", + "mobile-hide": "Dashboard in mobiler App ausblenden", + "update-image": "Dashboard-Bild aktualisieren", + "take-screenshot": "Screenshot erstellen", "select-widget-title": "Widget auswählen", - "select-widget-subtitle": "Liste der verfügbaren Widget-Typen", + "select-widget-value": "{{title}}: Widget auswählen", + "select-widget-subtitle": "Liste verfügbarer Widget-Typen", "delete": "Dashboard löschen", "title-required": "Titel ist erforderlich.", + "title-max-length": "Titel sollte weniger als 256 Zeichen enthalten", "description": "Beschreibung", "details": "Details", "dashboard-details": "Dashboard-Details", "add-dashboard-text": "Neues Dashboard hinzufügen", "assign-dashboards": "Dashboards zuweisen", "assign-new-dashboard": "Neues Dashboard zuweisen", - "assign-dashboards-text": "Zuordnen { count, plural, =1 {1 Dashboard} other {# Dashboards} } zu Kunden", - "unassign-dashboards-action-text": "Zuordnung { count, plural, =1 {1 Dashboard} other {# Dashboards} } vom Kunden aufheben", + "assign-dashboards-text": "{ count, plural, =1 {1 Dashboard} other {# Dashboards} } Kunden zuweisen", + "unassign-dashboards-action-text": "{ count, plural, =1 {1 Dashboard} other {# Dashboards} } von Kunden entfernen", "delete-dashboards": "Dashboards löschen", - "unassign-dashboards": "Zuordnung von Dashboards aufheben", - "unassign-dashboards-action-title": "Zuordnung { count, plural, =1 {1 Dashboard} other {# Dashboards} } vom Kunden aufheben", - "delete-dashboard-title": "Sind Sie sicher, dass Sie das Dashboard '{{dashboardTitle}}' löschen möchten?", - "delete-dashboard-text": "Vorsicht, nach Bestätigung werden das Dashboard und alle zugehörigen Daten nicht mehr wiederhergestellt.", - "delete-dashboards-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Dashboard} other {# Dashboards} } löschen möchten?", - "delete-dashboards-action-title": "Löschen { count, plural, =1 {1 Dashboard} other {# Dashboards} }", - "delete-dashboards-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Dashboards entfernt und alle zugehörigen Daten nicht mehr wiederhergestellt.", - "unassign-dashboard-title": "Sind Sie sicher, dass Sie die Zuordnung zum Dashboard '{{dashboardTitle}}' aufheben möchten?", - "unassign-dashboard-text": "Nach der Bestätigung wird die Zuordnung des Dashboards aufgehoben und es ist für den Kunden nicht mehr zugänglich.", - "unassign-dashboard": "Zuordnung zum Kunden aufheben", - "unassign-dashboards-title": "Sind Sie sicher, dass Sie die Zuordug aufheben möchten { count, plural, =1 {1 Dashboard} other {# Dashboards} }?", - "unassign-dashboards-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Dashboards aufgehoben und sie sind für den Kunden nicht mehr zugänglich.", - "public-dashboard-title": "Dashboard wurde veröffentlicht", - "public-dashboard-text": "Ihr Dashboard {{dashboardTitle}} ist jetzt öffentlich und über nächste Öffentlichkeit zugänglich link:", - "public-dashboard-notice": "Note: Vergessen Sie nicht, verwandte Geräte öffentlich zu machen, um auf Ihre Daten zugreifen zu können.", - "make-private-dashboard-title": "Sind Sie sicher, dass Sie das Dashboard '{{dashboardTitle}}' privatisieren möchten?", - "make-private-dashboard-text": "Nach der Bestätigung wird das Dashboard privatisiert und ist für andere nicht zugänglich.", - "make-private-dashboard": "Dashboard privatisieren", - "socialshare-text": "'{{dashboardTitle}}' Bereitgestellt vom ThingsBoard", - "socialshare-title": "'{{dashboardTitle}}' Bereitgestellt vom ThingsBoard", + "unassign-dashboards": "Dashboards entfernen", + "unassign-dashboards-action-title": "{ count, plural, =1 {1 Dashboard} other {# Dashboards} } von Kunde entfernen", + "delete-dashboard-title": "Möchten Sie das Dashboard '{{dashboardTitle}}' wirklich löschen?", + "delete-dashboard-text": "Achtung, nach der Bestätigung wird das Dashboard und alle zugehörigen Daten gelöscht.", + "delete-dashboards-title": "Möchten Sie { count, plural, =1 {1 Dashboard} other {# Dashboards} } wirklich löschen?", + "delete-dashboards-action-title": "{ count, plural, =1 {1 Dashboard löschen} other {# Dashboards löschen} }", + "delete-dashboards-text": "Achtung, nach der Bestätigung werden alle ausgewählten Dashboards und deren Daten gelöscht.", + "unassign-dashboard-title": "Möchten Sie das Dashboard '{{dashboardTitle}}' wirklich entfernen?", + "unassign-dashboard-text": "Nach der Bestätigung wird das Dashboard entfernt und ist für den Kunden nicht mehr zugänglich.", + "unassign-dashboard": "Dashboard entfernen", + "unassign-dashboards-title": "Möchten Sie { count, plural, =1 {1 Dashboard} other {# Dashboards} } wirklich entfernen?", + "unassign-dashboards-text": "Nach der Bestätigung werden alle ausgewählten Dashboards entfernt und sind für den Kunden nicht mehr zugänglich.", + "public-dashboard-title": "Dashboard ist jetzt öffentlich", + "public-dashboard-text": "Ihr Dashboard {{dashboardTitle}} ist jetzt öffentlich über folgenden Link zugänglich:", + "public-dashboard-notice": "Hinweis: Vergessen Sie nicht, zugehörige Geräte öffentlich zu machen, damit deren Daten zugänglich sind.", + "make-private-dashboard-title": "Möchten Sie das Dashboard '{{dashboardTitle}}' wirklich privat machen?", + "make-private-dashboard-text": "Nach der Bestätigung wird das Dashboard privat und ist für andere nicht mehr zugänglich.", + "make-private-dashboard": "Dashboard privat machen", + "socialshare-text": "'{{dashboardTitle}}' bereitgestellt von ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' bereitgestellt von ThingsBoard", "select-dashboard": "Dashboard auswählen", - "no-dashboards-matching": "Es wurden keine passenden Dashboards '{{entity}}' gefunden.", + "no-dashboards-matching": "Keine Dashboards gefunden, die mit '{{entity}}' übereinstimmen.", "dashboard-required": "Dashboard ist erforderlich.", - "select-existing": "Existierendes Dashboard auswählen", + "select-existing": "Vorhandenes Dashboard auswählen", "create-new": "Neues Dashboard erstellen", - "new-dashboard-title": "Neuer Dashboard Titel", + "new-dashboard-title": "Titel des neuen Dashboards", "open-dashboard": "Dashboard öffnen", - "set-background": "Hintergrund einstellen", + "set-background": "Hintergrund festlegen", "background-color": "Hintergrundfarbe", "background-image": "Hintergrundbild", - "background-size-mode": "Hintergrundgrößenmodus", + "background-size-mode": "Größenmodus des Hintergrunds", "no-image": "Kein Bild ausgewählt", - "drop-image": "Legen Sie ein Bild ab oder klicken Sie, um eine hochzuladende Datei auszuwählen.", + "empty-image": "Kein Bild", + "drop-image": "Bild ablegen oder klicken, um eine Datei auszuwählen.", + "maximum-upload-file-size": "Maximale Dateigröße für Upload: {{ size }}", + "cannot-upload-file": "Datei kann nicht hochgeladen werden", "settings": "Einstellungen", - "columns-count": "Spalten zählen", - "columns-count-required": "Die Anzahl der Spalten ist erforderlich.", - "min-columns-count-message": "Es müssen mindestens 10 Spalten vorhanden sein.", - "max-columns-count-message": "Es sind maximal 100 Spalten zulässig.", - "widgets-margins": "Abstand zwischen den Widgets", + "move-all-widgets": "Alle Widgets verschieben", + "move-by": "Verschieben um", + "cols": "Spalten", + "rows": "Zeilen", + "layout": "Layout", + "layout-type-default": "Standard", + "layout-type-scada": "SCADA", + "layout-type-divider": "Trennlinie", + "layout-settings-type": "Layout-Einstellungen: {{ type }} Breakpoint", + "columns-count": "Spaltenanzahl", + "columns-count-required": "Spaltenanzahl ist erforderlich.", + "min-columns-count-message": "Mindestens 10 Spalten sind erlaubt.", + "max-columns-count-message": "Höchstens 1000 Spalten sind erlaubt.", + "min-layout-width": "Minimale Layout-Breite", + "columns-suffix": "Spalten", + "widgets-margins": "Abstand zwischen Widgets", + "margin-required": "Abstandswert ist erforderlich.", + "min-margin-message": "Der minimale Abstandswert ist 0.", + "max-margin-message": "Der maximale Abstandswert ist 50.", "horizontal-margin": "Horizontaler Abstand", "horizontal-margin-required": "Horizontaler Abstandswert ist erforderlich.", - "min-horizontal-margin-message": "Der horizontale Abstandswert muss mindestens 0 betragen.", - "max-horizontal-margin-message": "Der horizontale Abstandswert beträgt maximal 50.", + "min-horizontal-margin-message": "Der minimale horizontale Abstandswert ist 0.", + "max-horizontal-margin-message": "Der maximale horizontale Abstandswert ist 50.", "vertical-margin": "Vertikaler Abstand", "vertical-margin-required": "Vertikaler Abstandswert ist erforderlich.", - "min-vertical-margin-message": "Der vertikale Abstandswert muss mindestens 0 betragen.", - "max-vertical-margin-message": "Der vertikale Abstandswert beträgt maximal 50.", - "autofill-height": "Layouthöhe automatisch füllen", - "mobile-layout": "Mobile Layouteinstellungen", - "mobile-row-height": "Mobile Zeilenhöhe, px", - "mobile-row-height-required": "Ein mobiler Zeilenhöhenwert ist erforderlich.", - "min-mobile-row-height-message": "Der Mindestwert für die mobile Zeilenhöhe beträgt 5 Pixel.", - "max-mobile-row-height-message": "Der Höchstwert für die mobile Zeilenhöhe beträgt 200 Pixel.", - "display-title": "Display Dashboard Titel", - "toolbar-always-open": "Werkzeugleiste geöffnet lassen", - "title-color": "Titelfarbe ", - "display-dashboards-selection": "Auswahl der Dashboards anzeigen", - "display-entities-selection": "Auswahl der Einheiten zulassen", + "min-vertical-margin-message": "Der minimale vertikale Abstandswert ist 0.", + "max-vertical-margin-message": "Der maximale vertikale Abstandswert ist 50.", + "apply-outer-margin": "Rand auf Layoutseiten anwenden", + "autofill-height": "Layout-Höhe automatisch ausfüllen", + "mobile-layout": "Mobile Layout-Einstellungen", + "mobile-row-height": "Zeilenhöhe für Mobilgeräte", + "mobile-row-height-required": "Zeilenhöhe für Mobilgeräte ist erforderlich.", + "min-mobile-row-height-message": "Mindestens 5 Pixel für mobile Zeilenhöhe erlaubt.", + "max-mobile-row-height-message": "Maximal 200 Pixel für mobile Zeilenhöhe erlaubt.", + "row-height": "Zeilenhöhe", + "row-height-required": "Zeilenhöhe ist erforderlich.", + "min-row-height-message": "Mindestens 5 Pixel als Zeilenhöhe erlaubt.", + "max-row-height-message": "Maximal 200 Pixel als Zeilenhöhe erlaubt.", + "display-first-in-mobile-view": "Zuerst in mobiler Ansicht anzeigen", + "title-settings": "Titel-Einstellungen", + "display-title": "Dashboard-Titel anzeigen", + "title-color": "Titelfarbe", + "toolbar-settings": "Werkzeugleisten-Einstellungen", + "hide-toolbar": "Werkzeugleiste ausblenden", + "toolbar-always-open": "Werkzeugleiste immer geöffnet lassen", + "display-dashboards-selection": "Dashboard-Auswahl anzeigen", + "display-entities-selection": "Entitätsauswahl anzeigen", + "display-filters": "Filter anzeigen", "display-dashboard-timewindow": "Zeitfenster anzeigen", "display-dashboard-export": "Export anzeigen", + "display-update-dashboard-image": "Aktualisierung des Dashboard-Bilds anzeigen", + "dashboard-logo-settings": "Dashboard-Logo-Einstellungen", + "display-dashboard-logo": "Logo im Vollbildmodus anzeigen", + "dashboard-logo-image": "Dashboard-Logo-Bild", + "advanced-settings": "Erweiterte Einstellungen", + "dashboard-css": "Dashboard CSS", "import": "Dashboard importieren", "export": "Dashboard exportieren", - "export-failed-error": "Dashboard kann nicht exportiert werden: {{error}}", + "export-failed-error": "Dashboard konnte nicht exportiert werden: {{error}}", + "export-prompt": "Dashboard-Bilder und -Ressourcen einbetten", "create-new-dashboard": "Neues Dashboard erstellen", "dashboard-file": "Dashboard-Datei", - "invalid-dashboard-file-error": "Dashboard kann nicht importiert werden: Ungültige Dashboard-Datenstruktur.", - "dashboard-import-missing-aliases-title": "Konfigurieren Sie die von importierten Dashboards verwendeten Aliasnamen", + "invalid-dashboard-file-error": "Dashboard konnte nicht importiert werden: Ungültige Datenstruktur.", + "dashboard-import-missing-aliases-title": "Aliase für importiertes Dashboard konfigurieren", "create-new-widget": "Neues Widget erstellen", "import-widget": "Widget importieren", "widget-file": "Widget-Datei", - "invalid-widget-file-error": "Widget kann nicht importiert werden: Ungültige Widget-Datenstruktur.", - "widget-import-missing-aliases-title": "Konfigurieren Sie die von importierten Widgets verwendeten Aliase", + "invalid-widget-file-error": "Widget konnte nicht importiert werden: Ungültige Widget-Datenstruktur.", + "widget-import-missing-aliases-title": "Aliase für importiertes Widget konfigurieren", "open-toolbar": "Dashboard-Werkzeugleiste öffnen", "close-toolbar": "Werkzeugleiste schließen", "configuration-error": "Konfigurationsfehler", - "alias-resolution-error-title": "Konfigurationsfehler für Dashboard-Aliasnamen", - "invalid-aliases-config": "Es konnten keine Geräte gefunden werden, die mit dem Aliase-Filter übereinstimmen.
Bitte wenden Sie sich an Ihren Administrator, um dieses problem zu beheben.", + "alias-resolution-error-title": "Fehler in der Alias-Konfiguration des Dashboards", + "invalid-aliases-config": "Für einige Aliase konnten keine passenden Geräte gefunden werden.
Bitte wenden Sie sich an Ihren Administrator.", "select-devices": "Geräte auswählen", "assignedToCustomer": "Dem Kunden zugewiesen", - "assignedToCustomers": "Kunden zugwiesen", + "assignedToCustomers": "Kunden zugewiesen", "public": "Öffentlich", + "copyId": "Dashboard-ID kopieren", + "idCopiedMessage": "Dashboard-ID wurde in die Zwischenablage kopiert", "public-link": "Öffentlicher Link", "copy-public-link": "Öffentlichen Link kopieren", - "public-link-copied-message": "Der öffentliche Link des Dashboards wurde in die Zwischenablage kopiert", - "manage-states": "Dashboard-Status verwalten", - "states": "Dashboard-Status", - "search-states": "Dashboard-Status suchen", - "selected-states": "{ count, plural, =1 {1 dashboard state} other {# dashboard states} } ausgewählt", - "edit-state": "Dashboard-Status bearbeiten", - "delete-state": "Dashboard-Status löschen", - "add-state": "Dashboard-Status hinzufügen", - "state": "Dashboard-Status", + "public-link-copied-message": "Öffentlicher Dashboard-Link wurde in die Zwischenablage kopiert", + "manage-states": "Dashboard-Zustände verwalten", + "states": "Dashboard-Zustände", + "states-short": "Zustände", + "search-states": "Dashboard-Zustände suchen", + "selected-states": "{ count, plural, =1 {1 Dashboard-Zustand} other {# Dashboard-Zustände} } ausgewählt", + "edit-state": "Dashboard-Zustand bearbeiten", + "delete-state": "Dashboard-Zustand löschen", + "add-state": "Dashboard-Zustand hinzufügen", + "no-states-text": "Keine Zustände gefunden", + "state": "Dashboard-Zustand", "state-name": "Name", - "state-name-required": "Name des Dashboard-Status ist erforderlich.", - "state-id": "Status-Id", - "state-id-required": "Dashboard-Status-ID ist erforderlich.", - "state-id-exists": "Dashboard-Status mit derselben ID ist bereits vorhanden .", - "is-root-state": "Grundzustand", - "delete-state-title": "Dashboard-Status löschen", - "delete-state-text": "Sind Sie sicher, dass Sie den Dashboard-Status '{{stateName}}' löschen möchten?", + "state-name-required": "Name des Dashboard-Zustands ist erforderlich.", + "state-id": "Zustands-ID", + "state-id-required": "Zustands-ID ist erforderlich.", + "state-id-exists": "Ein Dashboard-Zustand mit derselben ID existiert bereits.", + "is-root-state": "Hauptzustand", + "delete-state-title": "Dashboard-Zustand löschen", + "delete-state-text": "Möchten Sie den Zustand '{{stateName}}' wirklich löschen?", "show-details": "Details anzeigen", "hide-details": "Details ausblenden", - "select-state": "Soll-Zustand auswählen", + "select-state": "Zielzustand auswählen", "state-controller": "Zustandssteuerung", - "unassign-dashboard-from-edge-text": "Nach der Bestätigung wird die Zuordnung des Dashboards aufgehoben und es ist für der Rand nicht mehr zugänglich.", - "unassign-dashboards-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Dashboards aufgehoben und sie sind für den Rand nicht mehr zugänglich.", - "assign-dashboard-to-edge": "Dashboard(s) dem Rand zuordnen", - "assign-dashboard-to-edge-text": "Bitte wählen Sie die Dashboards aus, die Sie dem Rand zuordnen möchten" + "state-controller-default": "statisch (veraltet)", + "search": "Dashboards suchen", + "selected-dashboards": "{ count, plural, =1 {1 Dashboard} other {# Dashboards} } ausgewählt", + "home-dashboard": "Start-Dashboard", + "home-dashboard-hide-toolbar": "Werkzeugleiste im Start-Dashboard ausblenden", + "unassign-dashboard-from-edge-text": "Nach der Bestätigung wird das Dashboard entfernt und ist für den Edge nicht mehr zugänglich.", + "unassign-dashboards-from-edge-title": "Möchten Sie { count, plural, =1 {1 Dashboard} other {# Dashboards} } wirklich entfernen?", + "unassign-dashboards-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Dashboards entfernt und sind für den Edge nicht mehr zugänglich.", + "assign-dashboard-to-edge": "Dashboard(s) einem Edge zuweisen", + "assign-dashboard-to-edge-text": "Bitte wählen Sie die Dashboards aus, die dem Edge zugewiesen werden sollen", + "non-existent-dashboard-state-error": "Dashboard-Zustand mit ID \"{{ stateId }}\" wurde nicht gefunden", + "edit-mode": "Bearbeitungsmodus", + "duplicate-state-action": "Zustand duplizieren", + "breakpoint-value": "Breakpoint ({{ value }})", + "breakpoints-id": { + "default": "Standard", + "xs": "Mobilgerät (xs)", + "sm": "Tablet (sm)", + "md": "Laptop (md)", + "lg": "Desktop (lg)", + "xl": "Desktop (xl)" + }, + "view-format-type-grid": "Raster", + "view-format-type-list": "Liste", + "view-format": "Ansichtsformat" }, "datakey": { "settings": "Einstellungen", + "general": "Allgemein", "advanced": "Erweitert", + "key": "Schlüssel", + "keys": "Schlüssel", "label": "Bezeichnung", "color": "Farbe", - "units": "Maßeinheit die neben dem Wert angezeigt wird", - "decimals": "Anzahl der Stellen nach dem Komma", - "data-generation-func": "Daten generieren", - "use-data-post-processing-func": "Datenverarbeitungsfunktion verwenden", - "configuration": "Datenschlüsselkonfiguration", - "timeseries": "Zeitreihe", - "attributes": "Eigenschaften", + "units": "Spezialsymbol neben dem Wert", + "decimals": "Anzahl der Nachkommastellen", + "data-generation-func": "Daten-Generierungsfunktion", + "use-data-post-processing-func": "Daten-Nachbearbeitungsfunktion verwenden", + "configuration": "Datenkey-Konfiguration", + "timeseries": "Zeitreihen", + "attributes": "Attribute", + "entity-field": "Entitätsfeld", "alarm": "Alarmfelder", - "timeseries-required": "Entity-Zeitreihen sind erforderlich.", - "timeseries-or-attributes-required": "Entity-Zeitreihen/Eigenschaften sind erforderlich.", - "maximum-timeseries-or-attributes": "Maximum { count, plural, =1 {1 Zeitreihe/Eigenschaft ist erlaubt} other {# Zeitreihen/Eigenschaften sind erlaubt} }.", + "timeseries-required": "Zeitreihen der Entität sind erforderlich.", + "timeseries-or-attributes-required": "Zeitreihen/Attribute der Entität sind erforderlich.", + "alarm-fields-timeseries-or-attributes-required": "Alarmfelder oder Zeitreihen/Attribute der Entität sind erforderlich.", + "maximum-timeseries-or-attributes": "Maximal { count, plural, =1 {1 Zeitreihe/Attribut erlaubt.} other {# Zeitreihen/Attribute erlaubt} }", "alarm-fields-required": "Alarmfelder sind erforderlich.", - "function-types": "Funktionsarten", + "function-types": "Funktionstypen", + "function-type": "Funktionstyp", "function-types-required": "Funktionstypen sind erforderlich.", - "maximum-function-types": "Maximal { count, plural, =1 {1 Funktionstyp ist erlaubt} other {# Funktionstypen sind erlaubt} }.", - "time-description": "Zeitstempel des aktuellen Wertes;", - "value-description": "Der aktuelle Wert;", + "data-keys": "Datenkeys", + "data-key": "Datenkey", + "data-keys-required": "Datenkeys sind erforderlich.", + "data-key-required": "Datenkey ist erforderlich.", + "alarm-keys": "Alarm-Datenkeys", + "alarm-key": "Alarm-Datenkey", + "alarm-key-functions": "Funktionen für Alarmkey", + "alarm-key-function": "Alarmkey-Funktion", + "latest-keys": "Neueste Datenkeys", + "latest-key": "Neuester Datenkey", + "latest-key-functions": "Funktionen für neuesten Key", + "latest-key-function": "Neueste Key-Funktion", + "timeseries-keys": "Zeitreihen-Datenkeys", + "timeseries-key": "Zeitreihen-Datenkey", + "timeseries-key-functions": "Funktionen für Zeitreihenkey", + "timeseries-key-function": "Zeitreihenkey-Funktion", + "maximum-function-types": "Maximal { count, plural, =1 {1 Funktionstyp erlaubt.} other {# Funktionstypen erlaubt} }", + "time-description": "Zeitstempel des aktuellen Werts;", + "value-description": "der aktuelle Wert;", "prev-value-description": "Ergebnis des vorherigen Funktionsaufrufs;", - "time-prev-description": "Zeitmarke des vorherigen Wertes;", - "prev-orig-value-description": "Ursprünglicher vorheriger Wert;" + "time-prev-description": "Zeitstempel des vorherigen Werts;", + "prev-orig-value-description": "ursprünglicher vorheriger Wert;", + "aggregation": "Aggregation", + "aggregation-type-hint-common": "Aus Leistungsgründen ist die Berechnung aggregierter Werte nur für feste Zeitintervalle wie 'heutiger Tag', 'aktueller Monat' usw. verfügbar, nicht jedoch für gleitende Zeitfenster wie 'letzte 30 Minuten'.", + "aggregation-type-none-hint": "Letzten Wert übernehmen.", + "aggregation-type-min-hint": "Mindestwert innerhalb des ausgewählten Zeitfensters finden.", + "aggregation-type-max-hint": "Höchstwert innerhalb des ausgewählten Zeitfensters finden.", + "aggregation-type-avg-hint": "Durchschnittswert innerhalb des ausgewählten Zeitfensters berechnen.", + "aggregation-type-sum-hint": "Alle Werte der Datenpunkte im Zeitfenster summieren.", + "aggregation-type-count-hint": "Gesamtanzahl der Datenpunkte im Zeitfenster.", + "delta-calculation": "Delta-Berechnung", + "enable-delta-calculation": "Delta-Berechnung aktivieren", + "enable-delta-calculation-hint": "Wenn aktiviert, wird der Wert basierend auf aggregierten Werten für ein ausgewähltes Zeitfenster und einen Vergleichszeitraum berechnet. Delta-Berechnung ist nur für historische Zeitfenster verfügbar, nicht für Echtzeitwerte.", + "delta-calculation-result": "Ergebnis der Delta-Berechnung", + "delta-calculation-result-previous-value": "Vorheriger Wert", + "delta-calculation-result-delta-absolute": "Delta (absolut)", + "delta-calculation-result-delta-percent": "Delta (prozentual)", + "source": "Quelle", + "latest": "Neueste", + "latest-value": "Neuester Wert", + "delta": "delta", + "percent": "prozent", + "absolute": "absolut" }, "datasource": { "type": "Datenquellentyp", "name": "Name", + "label": "Bezeichnung", "add-datasource-prompt": "Bitte Datenquelle hinzufügen" }, "details": { + "details": "Details", "edit-mode": "Bearbeitungsmodus", + "edit-json": "JSON bearbeiten", "toggle-edit-mode": "Bearbeitungsmodus umschalten" }, "device": { "device": "Gerät", "device-required": "Gerät ist erforderlich.", "devices": "Geräte", - "management": "Geräte verwalten", + "management": "Geräteverwaltung", "view-devices": "Geräte anzeigen", - "device-alias": "Geräte-Alias", - "aliases": "Gerätealiasnamen", + "device-alias": "Gerätealias", + "device-type-max-length": "Gerätetyp sollte weniger als 256 Zeichen enthalten", + "aliases": "Gerätealiase", "no-alias-matching": "'{{alias}}' nicht gefunden.", "no-aliases-found": "Keine Aliase gefunden.", "no-key-matching": "'{{key}}' nicht gefunden.", "no-keys-found": "Keine Schlüssel gefunden.", - "create-new-alias": "Neues Alias erstellen!", - "create-new-key": "Neuen Schlüssel erstellen!", - "duplicate-alias-error": "Doppelter Alias gefunden '{{alias}}'.
Gerätealiasnamen müssen innerhalb des Dashboards eindeutig sein.", + "create-new-alias": "Neuen erstellen!", + "create-new-key": "Neuen erstellen!", + "duplicate-alias-error": "Doppelter Alias gefunden '{{alias}}'.
Gerätealiase müssen innerhalb des Dashboards eindeutig sein.", "configure-alias": "Alias '{{alias}}' konfigurieren", - "no-devices-matching": "Keine passenden Geräte '{{entity}}' gefunden.", + "no-devices-matching": "Keine Geräte gefunden, die mit '{{entity}}' übereinstimmen.", "alias": "Alias", - "alias-required": "Geräte-Alias ist erforderlich.", - "remove-alias": "Geräte-Alias entfernen", - "add-alias": "Geräte-Alias hinzufügen", - "name-starts-with": "Gerätename beginnt mit", + "alias-required": "Gerätealias ist erforderlich.", + "remove-alias": "Gerätealias entfernen", + "add-alias": "Gerätealias hinzufügen", + "name-starts-with": "Ausdruck für Gerätename", + "help-text": "Verwenden Sie '%' bei Bedarf: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", "device-list": "Geräteliste", "use-device-name-filter": "Filter verwenden", "device-list-empty": "Keine Geräte ausgewählt.", - "device-name-filter-required": "Der Gerätenamefilter ist erforderlich.", - "device-name-filter-no-device-matched": "Keine Geräte beginnend mit '{{device}}' gefunden.", + "device-name-filter-required": "Filter für Gerätenamen ist erforderlich.", + "device-name-filter-no-device-matched": "Keine Geräte gefunden, die mit '{{device}}' beginnen.", "add": "Gerät hinzufügen", - "assign-to-customer": "Kunden zuordnen", - "assign-device-to-customer": "Gerät(e) dem Kunden zuordnen", - "assign-device-to-customer-text": "Bitte wählen Sie die Geräte aus, die Sie dem Kunden zuordnen möchten", - "make-public": "Gerät veröffentlichen", - "make-private": "Gerät privatisieren", + "assign-to-customer": "Dem Kunden zuweisen", + "assign-device-to-customer": "Gerät(e) dem Kunden zuweisen", + "assign-device-to-customer-text": "Bitte wählen Sie die Geräte aus, die dem Kunden zugewiesen werden sollen", + "make-public": "Gerät öffentlich machen", + "make-private": "Gerät privat machen", "no-devices-text": "Keine Geräte gefunden", - "assign-to-customer-text": "Bitte wählen Sie einen Kunden aus, dem die Geräte zugeordnet werden sollen", + "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem das/die Gerät(e) zugewiesen werden soll(en)", "device-details": "Gerätedetails", "add-device-text": "Neues Gerät hinzufügen", "credentials": "Zugangsdaten", "manage-credentials": "Zugangsdaten verwalten", "delete": "Gerät löschen", - "assign-devices": "Gerät zuordnen", - "assign-devices-text": "{ count, plural, =1 {1 Gerät} other {# Geräte} } dem Kunden zuordnen", + "assign-devices": "Geräte zuweisen", + "assign-devices-text": "{ count, plural, =1 {1 Gerät} other {# Geräte} } dem Kunden zuweisen", "delete-devices": "Geräte löschen", - "unassign-from-customer": "Zuordnung zum Kunden aufheben", - "unassign-devices": "Nicht zugeordnete Geräte", - "unassign-devices-action-title": "Zuordnung von { count, plural, =1 {1 Gerät} other {# Geräte} } zum Kunden aufheben", - "assign-new-device": "Neues Gerät zuordnen", - "make-public-device-title": "Sind Sie sicher, dass Sie das Gerät '{{deviceName}}' öffentlich machen möchten?", - "make-public-device-text": "Nach der Bestätigung werden das Gerät und dessen Daten öffentlich und für andere zugänglich.", - "make-private-device-title": "Sind Sie sicher, dass Sie das Gerät '{{deviceName}}' privat machen möchten?", - "make-private-device-text": "Nach der Bestätigung werden das Gerät und dessen Daten privat und sind für andere nicht mehr zugänglich.", + "unassign-from-customer": "Vom Kunden entfernen", + "unassign-devices": "Geräte entfernen", + "unassign-devices-action-title": "{ count, plural, =1 {1 Gerät} other {# Geräte} } vom Kunden entfernen", + "unassign-device-from-edge-title": "Möchten Sie das Gerät '{{deviceName}}' wirklich vom Edge entfernen?", + "unassign-device-from-edge-text": "Nach der Bestätigung wird das Gerät entfernt und ist für den Edge nicht mehr zugänglich.", + "unassign-devices-from-edge": "Geräte vom Edge entfernen", + "assign-new-device": "Neues Gerät zuweisen", + "make-public-device-title": "Möchten Sie das Gerät '{{deviceName}}' wirklich öffentlich machen?", + "make-public-device-text": "Nach der Bestätigung wird das Gerät und alle zugehörigen Daten öffentlich und für andere zugänglich.", + "make-private-device-title": "Möchten Sie das Gerät '{{deviceName}}' wirklich privat machen?", + "make-private-device-text": "Nach der Bestätigung wird das Gerät und alle zugehörigen Daten privat und für andere nicht zugänglich.", "view-credentials": "Zugangsdaten anzeigen", "delete-device-title": "Möchten Sie das Gerät '{{deviceName}}' wirklich löschen?", - "delete-device-text": "Vorsicht, nach Bestätigung werden das Gerät und alle zugehörigen Daten nicht mehr wiederhergestellt.", - "delete-devices-title": "Sind Sie sicher, dass Sie löschen möchten { count, plural, =1 {1 Gerät} other {# Geräte} }?", - "delete-devices-action-title": "Löschen { count, plural, =1 {1 Gerät} other {# Geräte} }", - "delete-devices-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Geräte entfernt und alle zugehörigen Daten werden nicht mehr wiederhergestellt.", - "unassign-device-title": "Sind Sie sicher, dass Sie die Zuordnung zum Gerät '{{deviceName}}' wirklich aufheben möchten?", - "unassign-device-text": "Nach der Bestätigung ist das Gerät nicht zugeordnet und für den Kunden nicht zugänglich.", - "unassign-device": "Nicht zugeordnete Geräte", - "unassign-devices-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Gerät} other {# Geräte} } nicht mehr zuordnen möchten?", - "unassign-devices-text": "Nach der Bestätigung werden alle ausgewählten Geräte nicht zugewiesen und sind für den Kunden nicht zugänglich.", - "device-credentials": "Geräte Zugangsdaten", - "credentials-type": "Art der Zugangsdaten", - "access-token": "Zugangs-Token", - "access-token-required": "Zugangs-Token ist erforderlich.", - "access-token-invalid": "Die Länge des Zugangs-Tokens muss zwischen 1 und 32 Zeichen betragen.", + "delete-device-text": "Achtung, nach der Bestätigung wird das Gerät und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-devices-title": "Möchten Sie { count, plural, =1 {1 Gerät} other {# Geräte} } wirklich löschen?", + "delete-devices-action-title": "{ count, plural, =1 {1 Gerät löschen} other {# Geräte löschen} }", + "delete-devices-text": "Achtung, nach der Bestätigung werden alle ausgewählten Geräte und alle zugehörigen Daten unwiederbringlich gelöscht.", + "unassign-device-title": "Möchten Sie das Gerät '{{deviceName}}' wirklich entfernen?", + "unassign-device-text": "Nach der Bestätigung wird das Gerät entfernt und ist für den Kunden nicht mehr zugänglich.", + "unassign-device": "Gerät entfernen", + "unassign-devices-title": "Möchten Sie { count, plural, =1 {1 Gerät} other {# Geräte} } wirklich entfernen?", + "unassign-devices-text": "Nach der Bestätigung werden alle ausgewählten Geräte entfernt und sind für den Kunden nicht mehr zugänglich.", + "device-credentials": "Geräte-Zugangsdaten", + "loading-device-credentials": "Geräte-Zugangsdaten werden geladen...", + "credentials-type": "Zugangsdaten-Typ", + "access-token": "Zugangstoken", + "access-token-required": "Zugangstoken ist erforderlich.", + "access-token-invalid": "Die Länge des Zugangstokens muss zwischen 1 und 32 Zeichen liegen.", + "certificate-pem-format": "Zertifikat im PEM-Format", + "certificate-pem-format-required": "Zertifikat ist erforderlich.", + "copy-access-token": "Zugangstoken kopieren", + "copy-certificate": "Zertifikat kopieren", + "copy-client-id": "Client-ID kopieren", + "copy-user-name": "Benutzernamen kopieren", + "copy-password": "Passwort kopieren", + "generate-client-id": "Client-ID generieren", + "generate-user-name": "Benutzernamen generieren", + "generate-password": "Passwort generieren", + "generate-access-token": "Zugangstoken generieren", + "lwm2m-security-config": { + "identity": "Client-Identität", + "identity-required": "Client-Identität ist erforderlich.", + "identity-tooltip": "Die PSK-Kennung ist eine beliebige PSK-Kennung von bis zu 128 Byte, wie im Standard [RFC7925] beschrieben.\nDie PSK-Kennung MUSS zuerst in eine Zeichenkette umgewandelt und dann mittels UTF-8 in Oktetten codiert werden.", + "client-key": "Client-Schlüssel", + "client-key-required": "Client-Schlüssel ist erforderlich.", + "client-key-tooltip-prk": "RPK öffentlicher Schlüssel oder ID muss dem Standard [RFC7250] entsprechen und im Base64-Format codiert sein!", + "client-key-tooltip-psk": "PSK-Schlüssel muss dem Standard [RFC4279] entsprechen und im HexDec-Format sein: 32, 64, 128 Zeichen!", + "endpoint": "Endpoint-Clientname", + "endpoint-required": "Endpoint-Clientname ist erforderlich.", + "client-public-key": "Öffentlicher Client-Schlüssel", + "client-public-key-hint": "Wenn kein öffentlicher Client-Schlüssel angegeben ist, wird das vertrauenswürdige Zertifikat verwendet", + "client-public-key-tooltip": "X509-Schlüssel muss im DER-codierten X509v3-Format vorliegen, ausschließlich den EC-Algorithmus unterstützen und im Base64-Format codiert sein!", + "mode": "Sicherheitskonfigurationsmodus", + "client-tab": "Client-Sicherheitskonfiguration", + "client-certificate": "Client-Zertifikat", + "bootstrap-tab": "Bootstrap-Client", + "bootstrap-server": "Bootstrap-Server", + "lwm2m-server": "LwM2M-Server", + "client-publicKey-or-id": "Öffentlicher Client-Schlüssel oder ID", + "client-publicKey-or-id-required": "Öffentlicher Client-Schlüssel oder ID ist erforderlich.", + "client-publicKey-or-id-tooltip-psk": "Die PSK-Kennung ist eine beliebige PSK-Kennung bis zu 128 Byte, wie im Standard [RFC7925] beschrieben.\nDie PSK-Kennung MUSS zuerst in eine Zeichenkette umgewandelt und dann mittels UTF-8 in Oktetten codiert werden.", + "client-publicKey-or-id-tooltip-rpk": "RPK öffentlicher Schlüssel oder ID muss dem Standard [RFC7250] entsprechen und im Base64-Format codiert sein!", + "client-publicKey-or-id-tooltip-x509": "X509-Schlüssel muss im DER-codierten X509v3-Format vorliegen, ausschließlich den EC-Algorithmus unterstützen und im Base64-Format codiert sein", + "client-secret-key": "Geheimer Client-Schlüssel", + "client-secret-key-required": "Geheimer Client-Schlüssel ist erforderlich.", + "client-secret-key-tooltip-psk": "PSK-Schlüssel muss dem Standard [RFC4279] entsprechen und im HexDec-Format sein: 32, 64, 128 Zeichen!", + "client-secret-key-tooltip-prk": "RPK-Schlüssel muss im PKCS_8-Format (DER-Codierung, Standard [RFC5958]) vorliegen und im Base64-Format codiert sein!", + "client-secret-key-tooltip-x509": "X509-Schlüssel muss im PKCS_8-Format (DER-Codierung, Standard [RFC5958]) vorliegen und im Base64-Format codiert sein!" + }, + "client-id": "Client-ID", + "client-id-pattern": "Enthält ungültige Zeichen.", + "user-name": "Benutzername", + "user-name-required": "Benutzername ist erforderlich.", + "client-id-or-user-name-necessary": "Client-ID und/oder Benutzername sind erforderlich", + "password": "Passwort", "secret": "Geheimnis", "secret-required": "Geheimnis ist erforderlich.", - "device-type": "Gerätetyp", + "device-type": "Geräteprofil", "device-type-required": "Gerätetyp ist erforderlich.", "select-device-type": "Gerätetyp auswählen", - "enter-device-type": "Gerätetyp eingeben", - "any-device": "Jedes Gerät", - "no-device-types-matching": "Keine passenden Gerätetypen '{{entitySubtype}}' gefunden.", - "device-type-list-empty": "Kein Gerätetyp ausgewählt.", + "enter-device-type": "Geräteprofil eingeben", + "any-device": "Beliebiges Gerät", + "no-device-types-matching": "Keine Geräteprofile gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "device-type-list-empty": "Keine Geräteprofile ausgewählt!", + "device-profile-type-list-empty": "Mindestens ein Geräteprofil muss ausgewählt werden.", "device-types": "Gerätetypen", "name": "Name", "name-required": "Name ist erforderlich.", + "name-max-length": "Name sollte weniger als 256 Zeichen enthalten", + "label-max-length": "Bezeichnung sollte weniger als 256 Zeichen enthalten", "description": "Beschreibung", + "label": "Bezeichnung", "events": "Ereignisse", "details": "Details", "copyId": "Geräte-ID kopieren", - "copyAccessToken": "Zugangs-Token kopieren", + "copyAccessToken": "Zugangstoken kopieren", + "copy-mqtt-authentication": "MQTT-Zugangsdaten kopieren", "idCopiedMessage": "Geräte-ID wurde in die Zwischenablage kopiert", - "accessTokenCopiedMessage": "Geräte-Zugangs-Token wurde in die Zwischenablage kopiert", - "assignedToCustomer": "Dem Kunden zuordnen", - "unable-delete-device-alias-title": "Geräte-Alias kann nicht gelöscht werden", - "unable-delete-device-alias-text": "Geräte-Alias '{{deviceAlias}}' kann nicht gelöscht werden, da er von den folgenden Widgets verwendet wird:
{{widgetsList}}", - "is-gateway": "Ist ein Gateway", + "accessTokenCopiedMessage": "Geräte-Zugangstoken wurde in die Zwischenablage kopiert", + "mqtt-authentication-copied-message": "Geräte-MQTT-Zugang wurde in die Zwischenablage kopiert", + "assignedToCustomer": "Dem Kunden zugewiesen", + "unable-delete-device-alias-title": "Gerätealias kann nicht gelöscht werden", + "unable-delete-device-alias-text": "Gerätealias '{{deviceAlias}}' kann nicht gelöscht werden, da er in folgenden Widgets verwendet wird:
{{widgetsList}}", + "is-gateway": "Ist Gateway", + "overwrite-activity-time": "Aktivitätszeit für verbundenes Gerät überschreiben", + "device-filter": "Gerätefilter", + "device-filter-title": "Gerätefilter", + "filter-title": "Filter", + "device-state": "Gerätestatus", + "state": "Status", + "any": "Beliebig", + "active": "Aktiv", + "inactive": "Inaktiv", "public": "Öffentlich", "device-public": "Gerät ist öffentlich", "select-device": "Gerät auswählen", - "assign-device-to-edge-text":"Bitte wählen Sie die Geräte aus, die Sie dem Rand zuordnen möchten", - "unassign-device-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung zum Gerät '{{deviceName}}' wirklich aufheben möchten?", - "unassign-device-from-edge-text": "Nach der Bestätigung ist das Gerät nicht zugeordnet und für den Kunden nicht zugänglich.", - "unassign-devices-from-edge-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Gerät} other {# Geräte} } nicht mehr zuordnen möchten?", - "unassign-devices-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Geräte nicht zugewiesen und sind für den Rand nicht zugänglich." + "import": "Gerät importieren", + "device-file": "Gerätedatei", + "search": "Geräte suchen", + "selected-devices": "{ count, plural, =1 {1 Gerät} other {# Geräte} } ausgewählt", + "device-configuration": "Gerätekonfiguration", + "transport-configuration": "Transportkonfiguration", + "wizard": { + "device-details": "Gerätedetails" + }, + "unassign-devices-from-edge-title": "Möchten Sie { count, plural, =1 {1 Gerät} other {# Geräte} } wirklich vom Edge entfernen?", + "unassign-devices-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Geräte entfernt und sind für den Edge nicht mehr zugänglich.", + "time": "Zeit", + "connectivity": { + "check-connectivity": "Konnektivität prüfen", + "device-created-check-connectivity": "Gerät erstellt. Lassen Sie uns die Konnektivität prüfen!", + "loading-check-connectivity-command": "Befehle zur Konnektivitätsprüfung werden geladen...", + "use-following-instructions": "Verwenden Sie die folgenden Anweisungen, um Telemetrie im Namen des Geräts über die Shell zu senden", + "execute-following-command": "Führen Sie den folgenden Befehl aus", + "install-curl-windows": "Ab Windows 10 Build 17063 ist cURL standardmäßig verfügbar", + "install-curl-macos": "Ab Mac OS X 10.2 6C115 (Jaguar) ist cURL standardmäßig verfügbar", + "install-mqtt-windows": "Verwenden Sie die Anweisungen, um mosquitto_pub herunterzuladen, zu installieren, einzurichten und auszuführen", + "install-coap-client": "Verwenden Sie die Anweisungen, um coap-client herunterzuladen, zu installieren, einzurichten und auszuführen", + "install-necessary-client-tools": "Notwendige Client-Tools installieren", + "mqtts-x509-command": "Verwenden Sie die folgende Dokumentation, um das Gerät über MQTT mit X509-Authentifizierung zu verbinden", + "coaps-x509-command": "Verwenden Sie die folgende Dokumentation, um das Gerät über CoAP über DTLS mit X509-Authentifizierung zu verbinden", + "snmp-command": "Verwenden Sie die folgende Dokumentation, um das Gerät über SNMP zu verbinden", + "sparkplug-command": "Verwenden Sie die folgende Dokumentation, um das Gerät über MQTT Sparkplug zu verbinden", + "lwm2m-command": "Verwenden Sie die folgende Dokumentation, um das Gerät über LWM2M zu verbinden" + } + }, + "dynamic-form": { + "property": { + "properties": "Eigenschaften", + "property": "Eigenschaft", + "id": "ID", + "name": "Name", + "type": "Typ", + "type-text": "Text", + "type-password": "Passwort", + "type-textarea": "Textbereich", + "type-number": "Nummer", + "type-switch": "Schalter", + "type-select": "Auswahl", + "type-radios": "Optionsfelder", + "type-datetime": "Datum/Zeit", + "type-image": "Bild", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "Farbe", + "type-color-settings": "Farbeinstellungen", + "type-font": "Schriftart", + "type-units": "Einheiten", + "type-icon": "Symbol", + "type-fieldset": "Feldgruppe", + "type-array": "Array", + "type-html-section": "HTML-Abschnitt", + "group-title": "Gruppentitel", + "no-properties": "Keine Eigenschaften konfiguriert", + "add-property": "Eigenschaft hinzufügen", + "property-settings": "Eigenschaftseinstellungen", + "remove-property": "Eigenschaft entfernen", + "default-value": "Standardwert", + "value-required": "Wert erforderlich", + "number-settings": "Zahleneinstellungen", + "min": "Min", + "max": "Max", + "step": "Schritt", + "selected-options-limit": "Auswahloptionen Limit", + "advanced-ui-settings": "Erweiterte UI-Einstellungen", + "disable-on-property": "Bei Eigenschaft deaktivieren", + "display-condition-function": "Anzeigebedingungsfunktion", + "sub-label": "Unterbezeichnung", + "vertical-divider-after": "Vertikale Trennlinie danach", + "input-field-suffix": "Suffix des Eingabefeldes", + "property-row-classes": "Eigenschaftszeilenklassen", + "property-field-classes": "Eigenschaftsfeldklassen", + "not-unique-property-ids-error": "Eigenschafts-IDs müssen eindeutig sein!", + "enable-multiple-select": "Mehrfachauswahl aktivieren", + "allow-empty-select-option": "Leere Option erlauben", + "select-options": "Auswahloptionen", + "not-unique-select-option-value-error": "Werte der Auswahloptionen müssen eindeutig sein!", + "value": "Wert", + "label": "Bezeichnung", + "add-option": "Option hinzufügen", + "no-options": "Keine Optionen konfiguriert", + "remove-option": "Option entfernen", + "textarea-rows": "Textbereich Zeilen", + "help-id": "Hilfe-ID", + "buttons-direction": "Richtung der Schaltflächen", + "direction-row": "Reihe", + "direction-column": "Spalte", + "radio-button-options": "Optionsfelder-Optionen", + "datetime-type": "Datum/Zeit Feldtyp", + "datetime-type-date": "Datum", + "datetime-type-time": "Zeit", + "datetime-type-datetime": "Datum/Zeit", + "enable-clear-button": "Löschen-Schaltfläche aktivieren", + "html-section-settings": "HTML-Abschnittseinstellungen", + "html-section-classes": "HTML-Abschnitt Klassen", + "html-section-content": "Inhalt des HTML-Abschnitts", + "array-item": "Array-Element", + "item-type": "Elementtyp", + "item-name": "Elementname", + "no-items": "Keine Elemente" + }, + "clear-form": "Formular löschen", + "clear-form-prompt": "Möchten Sie wirklich alle Formulareigenschaften entfernen?", + "import-form": "Formular aus JSON importieren", + "export-form": "Formular in JSON exportieren", + "json-file": "JSON-Datei", + "json-content": "JSON-Inhalt", + "invalid-form-json-file-error": "Formularimport aus JSON fehlgeschlagen: Ungültige JSON-Datenstruktur." + }, + "asset-profile": { + "asset-profile": "Asset-Profil", + "asset-profiles": "Asset-Profile", + "all-asset-profiles": "Alle", + "add": "Asset-Profil hinzufügen", + "edit": "Asset-Profil bearbeiten", + "asset-profile-details": "Asset-Profil-Details", + "no-asset-profiles-text": "Keine Asset-Profile gefunden", + "search": "Asset-Profile suchen", + "selected-asset-profiles": "{ count, plural, =1 {1 Asset-Profil} other {# Asset-Profile} } ausgewählt", + "no-asset-profiles-matching": "Kein Asset-Profil passend zu '{{entity}}' gefunden.", + "asset-profile-required": "Asset-Profil ist erforderlich", + "idCopiedMessage": "Asset-Profil-ID wurde in die Zwischenablage kopiert", + "set-default": "Asset-Profil als Standard festlegen", + "delete": "Asset-Profil löschen", + "copyId": "Asset-Profil-ID kopieren", + "name-max-length": "Name sollte weniger als 256 Zeichen enthalten", + "new-device-profile-name": "Name des Asset-Profils", + "new-device-profile-name-required": "Name des Asset-Profils ist erforderlich.", + "name": "Name", + "name-required": "Name ist erforderlich.", + "image": "Asset-Profil-Bild", + "description": "Beschreibung", + "default": "Standard", + "default-rule-chain": "Standard-Regelkette", + "default-edge-rule-chain": "Standard Edge-Regelkette", + "default-edge-rule-chain-hint": "Wird auf Edge als Regelkette für eingehende Asset-Daten verwendet", + "mobile-dashboard": "Mobiles Dashboard", + "mobile-dashboard-hint": "Wird von der mobilen Anwendung als Asset-Details-Dashboard verwendet", + "select-queue-hint": "Aus der Dropdown-Liste auswählen.", + "delete-asset-profile-title": "Möchten Sie das Asset-Profil '{{assetProfileName}}' wirklich löschen?", + "delete-asset-profile-text": "Achtung, nach der Bestätigung wird das Asset-Profil und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-asset-profiles-title": "Möchten Sie { count, plural, =1 {1 Asset-Profil} other {# Asset-Profile} } wirklich löschen?", + "delete-asset-profiles-text": "Achtung, nach der Bestätigung werden alle ausgewählten Asset-Profile und alle zugehörigen Daten unwiederbringlich gelöscht.", + "set-default-asset-profile-title": "Möchten Sie das Asset-Profil '{{assetProfileName}}' als Standard festlegen?", + "set-default-asset-profile-text": "Nach der Bestätigung wird das Asset-Profil als Standard festgelegt und für neue Assets ohne spezifiziertes Profil verwendet.", + "no-asset-profiles-found": "Keine Asset-Profile gefunden.", + "create-new-asset-profile": "Neues erstellen!", + "create-asset-profile": "Neues Asset-Profil erstellen", + "import": "Asset-Profil importieren", + "export": "Asset-Profil exportieren", + "export-failed-error": "Asset-Profil konnte nicht exportiert werden: {{error}}", + "asset-profile-file": "Asset-Profil-Datei", + "invalid-asset-profile-file-error": "Asset-Profil konnte nicht importiert werden: Ungültige Asset-Profil-Datenstruktur." + }, + "device-profile": { + "device-profile": "Geräteprofil", + "device-profiles": "Geräteprofile", + "all-device-profiles": "Alle", + "add": "Geräteprofil hinzufügen", + "edit": "Geräteprofil bearbeiten", + "device-profile-details": "Geräteprofil-Details", + "no-device-profiles-text": "Keine Geräteprofile gefunden", + "search": "Geräteprofile suchen", + "selected-device-profiles": "{ count, plural, =1 {1 Geräteprofil} other {# Geräteprofile} } ausgewählt", + "no-device-profiles-matching": "Kein Geräteprofil passend zu '{{entity}}' gefunden.", + "device-profile-required": "Geräteprofil ist erforderlich", + "idCopiedMessage": "Geräteprofil-ID wurde in die Zwischenablage kopiert", + "set-default": "Geräteprofil als Standard festlegen", + "delete": "Geräteprofil löschen", + "copyId": "Geräteprofil-ID kopieren", + "name-max-length": "Name sollte weniger als 256 Zeichen enthalten", + "name": "Name", + "name-required": "Name ist erforderlich.", + "type": "Profiltyp", + "type-required": "Profiltyp ist erforderlich.", + "type-default": "Standard", + "image": "Geräteprofil-Bild", + "transport-type": "Transporttyp", + "transport-type-required": "Transporttyp ist erforderlich.", + "transport-type-default": "Standard", + "transport-type-default-hint": "Unterstützt grundlegendes MQTT, HTTP und CoAP Transport", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "Ermöglicht erweiterte MQTT-Transporteinstellungen", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "Ermöglicht erweiterte CoAP-Transporteinstellungen", + "transport-type-lwm2m": "LWM2M", + "transport-type-lwm2m-hint": "LWM2M-Transporttyp", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "SNMP-Transportkonfiguration angeben", + "transport-type-http": "HTTP", + "description": "Beschreibung", + "default": "Standard", + "profile-configuration": "Profilkonfiguration", + "transport-configuration": "Transportkonfiguration", + "default-rule-chain": "Standard-Regelkette", + "default-edge-rule-chain": "Standard Edge-Regelkette", + "default-edge-rule-chain-hint": "Wird auf Edge als Regelkette verwendet, um eingehende Gerätedaten dieses Geräteprofils zu verarbeiten", + "mobile-dashboard": "Mobiles Dashboard", + "mobile-dashboard-hint": "Wird von der mobilen Anwendung als Geräte-Details-Dashboard verwendet", + "select-queue-hint": "Aus der Dropdown-Liste auswählen.", + "delete-device-profile-title": "Möchten Sie das Geräteprofil '{{deviceProfileName}}' wirklich löschen?", + "delete-device-profile-text": "Achtung, nach der Bestätigung wird das Geräteprofil und alle zugehörigen Daten einschließlich der OTA-Updates unwiederbringlich gelöscht.", + "delete-device-profiles-title": "Möchten Sie { count, plural, =1 {1 Geräteprofil} other {# Geräteprofile} } wirklich löschen?", + "delete-device-profiles-text": "Achtung, nach der Bestätigung werden alle ausgewählten Geräteprofile und alle zugehörigen Daten einschließlich der OTA-Updates unwiederbringlich gelöscht.", + "set-default-device-profile-title": "Möchten Sie das Geräteprofil '{{deviceProfileName}}' als Standard festlegen?", + "set-default-device-profile-text": "Nach der Bestätigung wird das Geräteprofil als Standard markiert und für neue Geräte ohne spezifiziertes Profil verwendet.", + "no-device-profiles-found": "Keine Geräteprofile gefunden.", + "create-new-device-profile": "Neues erstellen!", + "mqtt-device-topic-filters": "MQTT-Gerät-Topic-Filter", + "mqtt-device-topic-filters-unique": "MQTT-Gerät-Topic-Filter müssen eindeutig sein.", + "mqtt-device-topic-filters-spark-plug": "MQTT Sparkplug B Edge-of-Network (EoN) Node.", + "mqtt-device-topic-filters-spark-plug-hint": "Erlaubt Verbindungen von EoN-Nodes mit Sparkplug B Nutzlast und Topic-Format.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "SparkPlug-Metriken zur Speicherung als Attribute.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Namen der SparkPlug-Metriken, die als Geräteattribute gespeichert werden. Alle anderen Metriken werden als Telemetrie gespeichert.", + "mqtt-device-payload-type": "MQTT-Geräte-Nutzlast", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Kompatibilität mit anderen Nutzlast-Formaten aktivieren.", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Wenn aktiviert, wird standardmäßig das Protobuf-Format verwendet. Bei Parsing-Fehlern wird JSON genutzt. Nützlich für die Abwärtskompatibilität bei Firmware-Updates. Dieser Modus kann die Leistung leicht beeinträchtigen und sollte nach Abschluss der Updates deaktiviert werden.", + "mqtt-use-json-format-for-default-downlink-topics": "JSON-Format für Standard-Downlink-Themen verwenden", + "mqtt-use-json-format-for-default-downlink-topics-hint": "Wenn aktiviert, wird das JSON-Format für die Standard-Downlink-Themen verwendet, z.B.: v1/devices/me/attributes/response/$request_id. Neue v2-Themen sind davon nicht betroffen.", + "mqtt-send-ack-on-validation-exception": "PUBACK bei Validierungsfehler senden", + "mqtt-send-ack-on-validation-exception-hint": "Standardmäßig wird die Sitzung bei einem Validierungsfehler geschlossen. Wenn aktiviert, wird stattdessen eine Bestätigung gesendet.", + "snmp-add-mapping": "SNMP-Zuordnung hinzufügen", + "snmp-mapping-not-configured": "Keine Zuordnung für OID zu Zeitreihen/Telemetrie konfiguriert", + "snmp-timseries-or-attribute-name": "Zeitreihe/Attributname für Zuordnung", + "snmp-timseries-or-attribute-type": "Zeitreihe/Attributtyp für Zuordnung", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "Nutzlast-Typ ist erforderlich.", + "coap-device-type": "CoAP-Gerätetyp", + "coap-device-payload-type": "CoAP-Geräte-Nutzlast", + "coap-device-type-required": "CoAP-Gerätetyp ist erforderlich.", + "coap-device-type-default": "Standard", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "Einfache [+] und mehrstufige [#] Wildcards werden unterstützt.", + "telemetry-topic-filter": "Telemetrie-Topic-Filter", + "telemetry-topic-filter-required": "Telemetrie-Topic-Filter ist erforderlich.", + "attributes-topic-filter": "Attribut-Veröffentlichungs-Topic-Filter", + "attributes-subscribe-topic-filter": "Attribut-Abonnement-Topic-Filter", + "attributes-topic-filter-required": "Attribut-Veröffentlichungs-Topic-Filter ist erforderlich.", + "attributes-subscribe-topic-filter-required": "Attribut-Abonnement-Topic ist erforderlich", + "telemetry-proto-schema": "Telemetrie-Protoschema", + "telemetry-proto-schema-required": "Telemetrie-Protoschema ist erforderlich.", + "attributes-proto-schema": "Attribut-Protoschema", + "attributes-proto-schema-required": "Attribut-Protoschema ist erforderlich.", + "rpc-response-proto-schema": "RPC-Antwort-Protoschema", + "rpc-response-proto-schema-required": "RPC-Antwort-Protoschema ist erforderlich.", + "rpc-response-topic-filter": "RPC-Antwort-Topic-Filter", + "rpc-response-topic-filter-required": "RPC-Antwort-Topic-Filter ist erforderlich.", + "rpc-request-proto-schema": "RPC-Anfrage-Protoschema", + "rpc-request-proto-schema-required": "RPC-Anfrage-Protoschema ist erforderlich.", + "rpc-request-proto-schema-hint": "RPC-Anforderungsnachrichten müssen die Felder 'string method = 1;', 'int32 requestId = 2;' und 'params = 3' enthalten.", + "not-valid-pattern-topic-filter": "Ungültiges Muster im Topic-Filter", + "not-valid-single-character": "Ungültige Verwendung eines Single-Level-Wildcards", + "not-valid-multi-character": "Ungültige Verwendung eines Multi-Level-Wildcards", + "single-level-wildcards-hint": "[+] kann jedes Topic-Level ersetzen. Beispiel: v1/devices/+/telemetry.", + "multi-level-wildcards-hint": "[#] ersetzt mehrere Ebenen und muss am Ende des Topics stehen. Beispiel: # oder v1/devices/me/#.", + "alarm-rules": "Alarmregeln", + "alarm-rules-with-count": "Alarmregeln ({{count}})", + "no-alarm-rules": "Keine Alarmregeln konfiguriert", + "add-alarm-rule": "Alarmregel hinzufügen", + "edit-alarm-rule": "Alarmregel bearbeiten", + "alarm-type": "Alarmtyp", + "alarm-type-required": "Alarmtyp ist erforderlich.", + "alarm-type-unique": "Alarmtyp muss innerhalb der Alarmregeln des Geräteprofils eindeutig sein.", + "alarm-type-max-length": "Alarmtyp sollte weniger als 256 Zeichen enthalten", + "create-alarm-pattern": "{{alarmType}} Alarm erstellen", + "create-alarm-rules": "Alarmregeln erstellen", + "no-create-alarm-rules": "Keine Erstellungsbedingungen konfiguriert", + "add-create-alarm-rule-prompt": "Bitte Erstellungsbedingung für Alarm hinzufügen", + "clear-alarm-rule": "Alarmregel löschen", + "no-clear-alarm-rule": "Keine Löschbedingung konfiguriert", + "add-create-alarm-rule": "Erstellungsbedingung hinzufügen", + "add-clear-alarm-rule": "Löschbedingung hinzufügen", + "select-alarm-severity": "Alarmstufe auswählen", + "alarm-severity-required": "Alarmstufe ist erforderlich.", + "condition-duration": "Bedingungsdauer", + "condition-duration-value": "Dauerwert", + "condition-duration-time-unit": "Zeiteinheit", + "condition-duration-value-range": "Dauerwert muss zwischen 1 und 2147483647 liegen.", + "condition-duration-value-pattern": "Dauerwert muss eine ganze Zahl sein.", + "condition-duration-value-required": "Dauerwert ist erforderlich.", + "condition-duration-time-unit-required": "Zeiteinheit ist erforderlich.", + "advanced-settings": "Erweiterte Einstellungen", + "alarm-rule-additional-info": "Zusätzliche Informationen", + "edit-alarm-rule-additional-info": "Zusätzliche Informationen bearbeiten", + "alarm-rule-additional-info-placeholder": "Bitte Kommentare und Anpassungen angeben, die unter 'Zusätzliche Informationen' bei Alarmdetails angezeigt werden.", + "alarm-rule-additional-info-hint": "Hinweis: Verwenden Sie ${keyName} zur Ersetzung von Attribut- oder Telemetriedaten.", + "alarm-rule-mobile-dashboard": "Mobiles Dashboard", + "alarm-rule-mobile-dashboard-hint": "Wird von der mobilen App als Alarminformationen-Dashboard genutzt", + "alarm-rule-no-mobile-dashboard": "Kein Dashboard ausgewählt", + "propagate-alarm": "Alarm auf verbundene Entitäten propagieren", + "alarm-rule-relation-types-list": "Beziehungstypen", + "alarm-rule-relation-types-list-hint": "Definiert Beziehungstypen zur Filterung der verbundenen Entitäten. Wenn nicht gesetzt, wird der Alarm an alle verbundenen Entitäten propagiert.", + "propagate-alarm-to-owner": "Alarm an Eigentümer der Entität (Kunde oder Mandant) propagieren", + "propagate-alarm-to-tenant": "Alarm an Mandant propagieren", + "alarm-rule-condition": "Alarmregelbedingung", + "enter-alarm-rule-condition-prompt": "Bitte fügen Sie eine Alarmregelbedingung hinzu", + "edit-alarm-rule-condition": "Alarmregelbedingung bearbeiten", + "device-provisioning": "Gerätebereitstellung", + "provision-strategy": "Bereitstellungsstrategie", + "provision-strategy-required": "Bereitstellungsstrategie ist erforderlich.", + "provision-strategy-disabled": "Deaktiviert", + "provision-strategy-created-new": "Erstellen neuer Geräte zulassen", + "provision-strategy-check-pre-provisioned": "Nach vorab bereitgestellten Geräten suchen", + "provision-device-key": "Gerätebereitstellungsschlüssel", + "provision-device-key-required": "Gerätebereitstellungsschlüssel ist erforderlich.", + "copy-provision-key": "Bereitstellungsschlüssel kopieren", + "provision-key-copied-message": "Bereitstellungsschlüssel wurde in die Zwischenablage kopiert", + "provision-device-secret": "Gerätebereitstellungsgeheimnis", + "provision-device-secret-required": "Gerätebereitstellungsgeheimnis ist erforderlich.", + "copy-provision-secret": "Bereitstellungsgeheimnis kopieren", + "provision-secret-copied-message": "Bereitstellungsgeheimnis wurde in die Zwischenablage kopiert", + "provision-strategy-x509": { + "certificate-chain": "X509-Zertifikatskette", + "certificate-chain-hint": "Die X.509-Zertifikatsstrategie wird verwendet, um Geräte anhand von Client-Zertifikaten in einer Zweiwege-TLS-Kommunikation bereitzustellen.", + "allow-create-new-devices": "Neue Geräte erstellen", + "allow-create-new-devices-hint": "Wenn ausgewählt, werden neue Geräte erstellt und das Client-Zertifikat als Geräteanmeldeinformationen verwendet.", + "certificate-value": "Zertifikat im PEM-Format", + "certificate-value-required": "Zertifikat im PEM-Format ist erforderlich", + "cn-regex-variable": "CN regulärer Ausdruck-Variable", + "cn-regex-variable-required": "CN regulärer Ausdruck-Variable ist erforderlich", + "cn-regex-variable-hint": "Erforderlich, um den Gerätenamen aus dem Common Name des X509-Zertifikats des Geräts abzurufen." + }, + "condition": "Bedingung", + "condition-type": "Bedingungstyp", + "condition-type-simple": "Einfach", + "condition-type-duration": "Dauer", + "condition-during": "Während {{during}}", + "condition-during-dynamic": "Während \"{{attribute}}\" ({{during}})", + "condition-type-repeating": "Wiederholend", + "condition-type-required": "Bedingungstyp ist erforderlich.", + "condition-repeating-value": "Anzahl der Ereignisse", + "condition-repeating-value-range": "Anzahl der Ereignisse muss zwischen 1 und 2147483647 liegen.", + "condition-repeating-value-pattern": "Anzahl der Ereignisse muss eine ganze Zahl sein.", + "condition-repeating-value-required": "Anzahl der Ereignisse ist erforderlich.", + "condition-repeat-times": "Wiederholt sich { count, plural, =1 {1 Mal} other {# Mal} }", + "condition-repeat-times-dynamic": "Wiederholt sich \"{attribute}\" ({ count, plural, =1 {1 Mal} other {# Mal} })", + "schedule-type": "Zeitplantyp", + "schedule-type-required": "Zeitplantyp ist erforderlich.", + "schedule": "Zeitplan", + "edit-schedule": "Alarmzeitplan bearbeiten", + "schedule-any-time": "Immer aktiv", + "schedule-specific-time": "Aktiv zu einer bestimmten Zeit", + "schedule-custom": "Benutzerdefiniert", + "schedule-day": { + "monday": "Montag", + "tuesday": "Dienstag", + "wednesday": "Mittwoch", + "thursday": "Donnerstag", + "friday": "Freitag", + "saturday": "Samstag", + "sunday": "Sonntag" + }, + "schedule-days": "Tage", + "schedule-time": "Zeit", + "schedule-time-from": "Von", + "schedule-time-to": "Bis", + "schedule-days-of-week-required": "Mindestens ein Wochentag muss ausgewählt sein.", + "create-device-profile": "Neues Geräteprofil erstellen", + "import": "Geräteprofil importieren", + "export": "Geräteprofil exportieren", + "export-failed-error": "Geräteprofil konnte nicht exportiert werden: {{error}}", + "device-profile-file": "Geräteprofil-Datei", + "invalid-device-profile-file-error": "Geräteprofil konnte nicht importiert werden: Ungültige Geräteprofil-Datenstruktur.", + "power-saving-mode": "Energiesparmodus", + "power-saving-mode-type": { + "default": "Energiesparmodus des Geräteprofils verwenden", + "psm": "Power Saving Mode (PSM)", + "drx": "Discontinuous Reception (DRX)", + "edrx": "Extended Discontinuous Reception (eDRX)" + }, + "edrx-cycle": "eDRX-Zyklus", + "edrx-cycle-required": "eDRX-Zyklus ist erforderlich.", + "edrx-cycle-pattern": "eDRX-Zyklus muss eine positive Ganzzahl sein.", + "edrx-cycle-min": "Die Mindestanzahl von eDRX-Zyklen beträgt {{ min }} Sekunden.", + "paging-transmission-window": "Übertragungsfenster für Paging", + "paging-transmission-window-required": "Übertragungsfenster für Paging ist erforderlich.", + "paging-transmission-window-pattern": "Übertragungsfenster für Paging muss eine positive Ganzzahl sein.", + "paging-transmission-window-min": "Die Mindestanzahl des Übertragungsfensters für Paging beträgt {{ min }} Sekunden.", + "psm-activity-timer": "PSM-Aktivitätstimer", + "psm-activity-timer-required": "PSM-Aktivitätstimer ist erforderlich.", + "psm-activity-timer-pattern": "PSM-Aktivitätstimer muss eine positive Ganzzahl sein.", + "psm-activity-timer-min": "Die Mindestanzahl des PSM-Aktivitätstimers beträgt {{ min }} Sekunden.", + "lwm2m": { + "object-list": "Objektliste", + "object-list-empty": "Keine Objekte ausgewählt.", + "no-objects-found": "Keine Objekte gefunden.", + "no-objects-matching": "Keine Objekte gefunden, die mit '{{object}}' übereinstimmen.", + "model-tab": "LWM2M-Modell", + "add-new-instances": "Neue Instanzen hinzufügen", + "instances-list": "Instanzenliste", + "instances-list-required": "Instanzenliste ist erforderlich.", + "instance-id-pattern": "Instanz-ID muss eine positive Ganzzahl sein.", + "instance-id-max": "Maximaler Instanz-ID-Wert {{max}}.", + "instance": "Instanz", + "resource-label": "#ID Ressourcenname", + "observe-label": "Beobachten", + "attribute-label": "Attribut", + "telemetry-label": "Telemetrie", + "edit-observe-select": "Zum Bearbeiten der Beobachtung Telemetrie oder Attribut auswählen", + "edit-attributes-select": "Zum Bearbeiten der Attribute Telemetrie oder Attribut auswählen", + "no-attributes-set": "Keine Attribute gesetzt", + "key-name": "Schlüsselname", + "key-name-required": "Schlüsselname ist erforderlich", + "attribute-name": "Attributname", + "attribute-name-required": "Attributname ist erforderlich.", + "attribute-value": "Attributwert", + "attribute-value-required": "Attributwert ist erforderlich.", + "attribute-value-pattern": "Attributwert muss eine positive Ganzzahl sein.", + "edit-attributes": "Attribute bearbeiten: {{ name }}", + "view-attributes": "Attribute anzeigen: {{ name }}", + "add-attribute": "Attribut hinzufügen", + "edit-attribute": "Attribut bearbeiten", + "view-attribute": "Attribut anzeigen", + "remove-attribute": "Attribut entfernen", + "delete-server-text": "Vorsicht: Nach der Bestätigung wird die Serverkonfiguration unwiderruflich gelöscht.", + "delete-server-title": "Sind Sie sicher, dass Sie den Server löschen möchten?", + "mode": "Sicherheitskonfigurationsmodus", + "bootstrap-tab": "Bootstrap", + "bootstrap-server-legend": "Bootstrap-Server (ShortId...)", + "lwm2m-server-legend": "LwM2M-Server (ShortId...)", + "server": "Server", + "short-id": "Kurze Server-ID", + "short-id-tooltip": "Kurze Server-ID. Wird verwendet, um die Serverobjektinstanz zu verknüpfen.\nDiese Kennung identifiziert jeden LwM2M-Server eindeutig, der für den LwM2M-Client konfiguriert ist.\nDie Ressource MUSS gesetzt werden, wenn die Bootstrap-Server-Ressource den Wert 'false' hat.\nDie Werte ID:0 und ID:65535 dürfen NICHT für die Identifizierung des LwM2M-Servers verwendet werden.", + "short-id-tooltip-bootstrap": "Kurze Server-ID. Wird verwendet, um die Serverobjektinstanz zu verknüpfen.\nDiese Kennung identifiziert jeden LwM2M-Server eindeutig, der für den LwM2M-Client konfiguriert ist.\nDie Ressource MUSS gesetzt werden, wenn die Bootstrap-Server-Ressource den Wert 'false' hat.", + "short-id-required": "Kurze Server-ID ist erforderlich.", + "short-id-range": "Kurze Server-ID sollte im Bereich von {{ min }} bis {{ max }} liegen.", + "short-id-pattern": "Kurze Server-ID muss eine positive Ganzzahl sein.", + "lifetime": "Registrierungslebensdauer des Clients", + "lifetime-required": "Registrierungslebensdauer des Clients ist erforderlich.", + "lifetime-pattern": "Registrierungslebensdauer muss eine positive Ganzzahl sein.", + "default-min-period": "Minimale Periode zwischen zwei Benachrichtigungen (s)", + "default-min-period-tooltip": "Standardwert für die minimale Beobachtungsperiode, wenn dieser Parameter nicht in der Beobachtung enthalten ist.", + "default-min-period-required": "Minimale Periode ist erforderlich.", + "default-min-period-pattern": "Minimale Periode muss eine positive Ganzzahl sein.", + "notification-storing": "Benachrichtigungsspeicherung bei Deaktivierung oder Offline-Modus", + "binding": "Bindung", + "binding-type": { + "u": "U: Client ist jederzeit über UDP-Bindung erreichbar.", + "m": "M: Client ist jederzeit über MQTT-Bindung erreichbar.", + "h": "H: Client ist jederzeit über HTTP-Bindung erreichbar.", + "t": "T: Client ist jederzeit über TCP-Bindung erreichbar.", + "s": "S: Client ist jederzeit über SMS-Bindung erreichbar.", + "n": "N: Client MUSS auf solche Anfragen über eine Non-IP-Verbindung antworten (unterstützt seit LWM2M 1.1).", + "uq": "UQ: UDP-Verbindung im Queue-Modus (nicht unterstützt seit LWM2M 1.1)", + "uqs": "UQS: Sowohl UDP- als auch SMS-Verbindungen aktiv; UDP im Queue-Modus, SMS im Standardmodus (nicht unterstützt seit LWM2M 1.1)", + "tq": "TQ: TCP-Verbindung im Queue-Modus (nicht unterstützt seit LWM2M 1.1)", + "tqs": "TQS: Sowohl TCP- als auch SMS-Verbindungen aktiv; TCP im Queue-Modus, SMS im Standardmodus (nicht unterstützt seit LWM2M 1.1)", + "sq": "SQ: SMS-Verbindung im Queue-Modus (nicht unterstützt seit LWM2M 1.1)" + }, + "binding-tooltip": "Liste der unterstützten Bindungsmodi im \"binding\"-Ressourcenelement des LwM2M-Serverobjekts - /1/x/7.\nMehrere Übertragungsmodi sind möglich, aber nur einer wird während einer Transport-Session genutzt.", + "bootstrap-server": "Bootstrap-Server", + "lwm2m-server": "LwM2M-Server", + "include-bootstrap-server": "Bootstrap-Server-Updates einbeziehen", + "bootstrap-update-title": "Bootstrap-Server ist bereits konfiguriert. Möchten Sie die Updates wirklich ausschließen?", + "bootstrap-update-text": "Vorsicht: Nach der Bestätigung gehen die Konfigurationsdaten des Bootstrap-Servers verloren.", + "server-host": "Host", + "server-host-required": "Host ist erforderlich.", + "server-port": "Port", + "server-port-required": "Port ist erforderlich.", + "server-port-pattern": "Port muss eine positive Ganzzahl sein.", + "server-port-range": "Port sollte im Bereich von 1 bis 65535 liegen.", + "server-public-key": "Öffentlicher Schlüssel des Servers", + "server-public-key-required": "Öffentlicher Schlüssel des Servers ist erforderlich.", + "client-hold-off-time": "Wartezeit des Clients", + "client-hold-off-time-required": "Wartezeit des Clients ist erforderlich.", + "client-hold-off-time-pattern": "Wartezeit des Clients muss eine positive Ganzzahl sein.", + "client-hold-off-time-tooltip": "Client-Wartezeit nur bei Verwendung eines Bootstrap-Servers", + "account-after-timeout": "Konto nach Zeitüberschreitung", + "account-after-timeout-required": "Konto nach Zeitüberschreitung ist erforderlich.", + "account-after-timeout-pattern": "Konto nach Zeitüberschreitung muss eine positive Ganzzahl sein.", + "account-after-timeout-tooltip": "Wert für Konto nach Zeitüberschreitung beim Bootstrap-Server.", + "server-type": "Servertyp", + "add-new-server-title": "Neue Serverkonfiguration hinzufügen", + "add-server-config": "Serverkonfiguration hinzufügen", + "add-lwm2m-server-config": "LwM2M-Server hinzufügen", + "no-config-servers": "Keine Server konfiguriert", + "others-tab": "Weitere Einstellungen", + "client-strategy": "Client-Strategie beim Verbinden", + "client-strategy-label": "Strategie", + "client-strategy-only-observe": "Nur Beobachtungsanfragen nach der ersten Verbindung", + "client-strategy-read-all": "Alle Ressourcen lesen und beobachten nach der Registrierung", + "fw-update": "Firmware-Update", + "fw-update-strategy": "Firmware-Update-Strategie", + "fw-update-strategy-data": "Firmware-Update als Binärdatei mit Objekt 19 und Ressource 0 (Daten) übertragen", + "fw-update-strategy-package": "Firmware-Update als Binärdatei mit Objekt 5 und Ressource 0 (Paket) übertragen", + "fw-update-strategy-package-uri": "Automatisch eindeutige CoAP-URL generieren, um das Paket herunterzuladen, und als Objekt 5 und Ressource 1 (Paket-URI) übertragen", + "sw-update": "Software-Update", + "sw-update-strategy": "Software-Update-Strategie", + "sw-update-strategy-package": "Binärdatei mit Objekt 9 und Ressource 2 (Paket) übertragen", + "sw-update-strategy-package-uri": "Automatisch eindeutige CoAP-URL generieren, um das Paket herunterzuladen, und Software-Update mit Objekt 9 und Ressource 3 (Paket-URI) übertragen", + "fw-update-resource": "Firmware-Update CoAP-Ressource", + "fw-update-resource-required": "Firmware-Update CoAP-Ressource ist erforderlich.", + "sw-update-resource": "Software-Update CoAP-Ressource", + "sw-update-resource-required": "Software-Update CoAP-Ressource ist erforderlich.", + "config-json-tab": "JSON-Konfigurationsprofil Gerät", + "attributes-name": { + "min-period": "Minimale Periode", + "max-period": "Maximale Periode", + "greater-than": "Größer als", + "less-than": "Kleiner als", + "step": "Schritt", + "min-evaluation-period": "Minimale Auswertungsperiode", + "max-evaluation-period": "Maximale Auswertungsperiode" + }, + "default-object-id": "Standardobjektversion (Attribut)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1" + } + }, + "snmp": { + "add-communication-config": "Kommunikationskonfiguration hinzufügen", + "add-mapping": "Zuordnung hinzufügen", + "authentication-passphrase": "Authentifizierungs-Passphrase", + "authentication-passphrase-required": "Authentifizierungs-Passphrase ist erforderlich.", + "authentication-protocol": "Authentifizierungsprotokoll", + "authentication-protocol-required": "Authentifizierungsprotokoll ist erforderlich.", + "communication-configs": "Kommunikationskonfigurationen", + "community": "Community-String", + "community-required": "Community-String ist erforderlich.", + "context-name": "Kontextname", + "data-key": "Datenschlüssel", + "data-key-required": "Datenschlüssel ist erforderlich.", + "data-type": "Datentyp", + "data-type-required": "Datentyp ist erforderlich.", + "engine-id": "Engine-ID", + "host": "Host", + "host-required": "Host ist erforderlich.", + "oid": "OID", + "oid-pattern": "Ungültiges OID-Format.", + "oid-required": "OID ist erforderlich.", + "please-add-communication-config": "Bitte Kommunikationskonfiguration hinzufügen", + "please-add-mapping-config": "Bitte Zuordnungskonfiguration hinzufügen", + "port": "Port", + "port-format": "Ungültiges Port-Format.", + "port-required": "Port ist erforderlich.", + "privacy-passphrase": "Privatsphäre-Passphrase", + "privacy-passphrase-required": "Privatsphäre-Passphrase ist erforderlich.", + "privacy-protocol": "Privatsphäreprotokoll", + "privacy-protocol-required": "Privatsphäreprotokoll ist erforderlich.", + "protocol-version": "Protokollversion", + "protocol-version-required": "Protokollversion ist erforderlich.", + "querying-frequency": "Abfragefrequenz, ms", + "querying-frequency-invalid-format": "Abfragefrequenz muss eine positive Ganzzahl sein.", + "querying-frequency-required": "Abfragefrequenz ist erforderlich.", + "retries": "Wiederholungen", + "retries-invalid-format": "Wiederholungen müssen eine positive Ganzzahl sein.", + "retries-required": "Wiederholungen sind erforderlich.", + "scope": "Bereich", + "scope-required": "Bereich ist erforderlich.", + "security-name": "Sicherheitsname", + "security-name-required": "Sicherheitsname ist erforderlich.", + "timeout-ms": "Zeitüberschreitung, ms", + "timeout-ms-invalid-format": "Zeitüberschreitung muss eine positive Ganzzahl sein.", + "timeout-ms-required": "Zeitüberschreitung ist erforderlich.", + "user-name": "Benutzername", + "user-name-required": "Benutzername ist erforderlich." + } }, "dialog": { - "close": "Dialog schließen" + "close": "Dialog schließen", + "error-message-title": "Fehlermeldung:", + "error-details-title": "Fehlerdetails" + }, + "direction": { + "column": "Spalte", + "row": "Zeile" }, "edge": { "edge": "Edge", - "edge-instances": "Kanteninstanzen", + "edge-instances": "Edge-Instanzen", + "instances": "Instanzen", "edge-file": "Edge-Datei", - "management": "Edge verwalten", - "no-edges-matching": "Keine passenden Edge '{{entity}}' gefunden.", + "name-max-length": "Name sollte weniger als 256 Zeichen haben", + "label-max-length": "Label sollte weniger als 256 Zeichen haben", + "type-max-length": "Typ sollte weniger als 256 Zeichen haben", + "management": "Edge-Verwaltung", + "no-edges-matching": "Keine Edges gefunden, die mit '{{entity}}' übereinstimmen.", "add": "Edge hinzufügen", - "no-edges-text": "Keine Edge gefunden.", - "edge-details": "Details der Edge", + "no-edges-text": "Keine Edges gefunden", + "edge-details": "Edge-Details", "add-edge-text": "Neue Edge hinzufügen", "delete": "Edge löschen", - "delete-edge-title": "Möchten Sie des Edges wirklich löschen '{{edgeName}}'?", - "delete-edge-text": "Seien Sie vorsichtig, nach der Bestätigung werden der Edge und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-edges-title": "Sind Sie sicher, dass Sie die Edge löschen möchten { count, plural, =1 {1 Edge} other {# Edges} }?", - "delete-edges-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Edge entfernt und alle zugehörigen Daten werden nicht wiederhergestellt.", + "delete-edge-title": "Sind Sie sicher, dass Sie die Edge '{{edgeName}}' löschen möchten?", + "delete-edge-text": "Achtung: Nach der Bestätigung wird die Edge und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-edges-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Edge} other {# Edges} } löschen möchten?", + "delete-edges-text": "Achtung: Nach der Bestätigung werden alle ausgewählten Edges und zugehörige Daten unwiederbringlich gelöscht.", "name": "Name", - "name-starts-with": "Der Kantenname beginnt mit", + "name-starts-with": "Edge-Name beginnt mit", "name-required": "Name ist erforderlich.", "description": "Beschreibung", "details": "Details", "events": "Ereignisse", - "copy-id": "Regelketten-ID kopieren", - "id-copied-message": "Regelketten-ID wurde in die Zwischenablage kopiert", - "sync": "Edge synchonisieren", - "edge-required": "Edge ist erforderlich.", - "edge-type": "Edgetyp", - "edge-type-required": "Edgetyp ist erforderlich.", + "copy-id": "Edge-ID kopieren", + "id-copied-message": "Edge-ID wurde in die Zwischenablage kopiert", + "sync": "Edge synchronisieren", + "edge-required": "Edge ist erforderlich", + "edge-type": "Edge-Typ", + "edge-type-required": "Edge-Typ ist erforderlich.", "event-action": "Ereignisaktion", - "entity-id": "Entität ID", - "select-edge-type": "Edgetyp auswählen", - "assign-to-customer": "Einem Kunden zuordnen", - "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Edge zugeordnet werden sollen", - "assign-edge-to-customer": "Edge dem Kunden zuordnen", - "assign-edge-to-customer-text": "Bitte wählen Sie die Edge aus, die dem Kunden zugeordnet werden sollen", - "assignedToCustomer": "Dem Kunden zugewiesen", + "entity-id": "Entitäts-ID", + "select-edge-type": "Edge-Typ auswählen", + "assign-to-customer": "Einem Kunden zuweisen", + "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Edge(s) zugewiesen werden sollen", + "assign-edge-to-customer": "Edge(s) einem Kunden zuweisen", + "assign-edge-to-customer-text": "Bitte wählen Sie die Edges aus, die dem Kunden zugewiesen werden sollen", + "assignedToCustomer": "Kunde zugewiesen", "edge-public": "Edge ist öffentlich", - "assigned-to-customer": "Kunden Zuordnung", - "unassign-from-customer": "Kunden Zuordnung aufgehoben", - "unassign-edge-title": "Sind Sie sicher, dass Sie die Zuordnung zum Edge '{{edgeName}}' wirklich aufheben möchten?", - "unassign-edge-text": "Nach der Bestätigung ist der Edge nicht zugeordnet und für den Kunden nicht zugänglich.", - "unassign-edges-title": "Sind Sie sicher, dass Sie die Zuordnung aufheben möchten { count, plural, =1 {1 Edge} other {# Edges} }?", - "unassign-edges-text": "Nach der Bestätigung werden alle ausgewählten Kanten nicht zugewiesen und sind für den Kunden nicht zugänglich.", + "assigned-to-customer": "Zugewiesen zu: {{customerTitle}}", + "unassign-from-customer": "Von Kunde lösen", + "unassign-edge-title": "Sind Sie sicher, dass Sie die Edge '{{edgeName}}' vom Kunden lösen möchten?", + "unassign-edge-text": "Nach der Bestätigung wird die Edge vom Kunden gelöst und ist für diesen nicht mehr zugänglich.", + "unassign-edges-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Edge} other {# Edges} } vom Kunden lösen möchten?", + "unassign-edges-text": "Nach der Bestätigung werden alle ausgewählten Edges vom Kunden gelöst und sind nicht mehr zugänglich.", "make-public": "Edge öffentlich machen", - "make-public-edge-title": "Sind Sie sicher, dass Sie der Edge '{{edgeName}}' öffentlich machen möchten?", - "make-public-edge-text": "Nach Bestätigung wird der Rabd und alle zugehörigen Daten anderen zugänglich gemacht.", + "make-public-edge-title": "Sind Sie sicher, dass Sie die Edge '{{edgeName}}' öffentlich machen möchten?", + "make-public-edge-text": "Nach der Bestätigung wird die Edge und alle zugehörigen Daten öffentlich zugänglich sein.", "make-private": "Edge privat machen", "public": "Öffentlich", - "make-private-edge-title": "Sind Sie sicher, dass Sie der Edge '{{edgeName}}' privat machen möchten?", - "make-private-edge-text": "Nach der Bestätigung werden der Edge und dessen Daten privat und sind für andere nicht mehr zugänglich.", + "make-private-edge-title": "Sind Sie sicher, dass Sie die Edge '{{edgeName}}' privat machen möchten?", + "make-private-edge-text": "Nach der Bestätigung wird die Edge und alle zugehörigen Daten privat sein und nicht mehr öffentlich zugänglich.", "import": "Edge importieren", - "label": "Bezeichnung", - "load-entity-error": "Entität nicht gefunden. Fehler beim Laden der Informationen", - "assign-new-edge": "Neue Edge zuordnen", - "unassign-from-edge": "Edge zuweisen", + "install-connect-instructions": "Installations- & Verbindungsanleitung", + "install-connect-instructions-edge-created": "Edge erstellt! Installations- & Verbindungsanleitung prüfen", + "loading-edge-instructions": "Lade Edge-Anweisungen...", + "label": "Label", + "load-entity-error": "Daten konnten nicht geladen werden. Entität wurde gelöscht.", + "assign-new-edge": "Neue Edge zuweisen", + "unassign-from-edge": "Von Edge lösen", "edge-key": "Edge-Schlüssel", "copy-edge-key": "Edge-Schlüssel kopieren", "edge-key-copied-message": "Edge-Schlüssel wurde in die Zwischenablage kopiert", "edge-secret": "Edge-Geheimnis", "copy-edge-secret": "Edge-Geheimnis kopieren", "edge-secret-copied-message": "Edge-Geheimnis wurde in die Zwischenablage kopiert", - "edge-assets": "Edge-Objekte verwalten", - "edge-devices": "Edge-Geräte verwalten", - "edge-entity-views": "Edge-Entitätsansichten verwalten", - "edge-dashboards": "Edge-Dashboards verwalten", - "edge-rulechains": "Edge-Regelketten", - "assets": "Edge Objekte", - "devices": "Objekte Geräte", - "entity-views": "Objekte Entitätsansichten", + "manage-assets": "Assets verwalten", + "manage-devices": "Geräte verwalten", + "manage-entity-views": "Entitätsansichten verwalten", + "manage-dashboards": "Dashboards verwalten", + "manage-rulechains": "Regelketten verwalten", + "assets": "Edge-Assets", + "devices": "Edge-Geräte", + "entity-views": "Edge-Entitätsansichten", "dashboard": "Edge-Dashboard", "dashboards": "Edge-Dashboards", - "rulechain-templates": "Regelkettenvorlagen", + "rulechain-templates": "Regelkettentemplates", + "edge-rulechain-templates": "Edge-Regelkettentemplates", "rulechains": "Edge-Regelketten", - "search": "Edges durchsuchen", - "selected-edges": "{count, plural, =1 {1 Edge} other {# Edges} } ausgewählt", - "any-edge": "Beliebige Kante", - "no-edge-types-matching": "Es wurden keine Edgetypen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", - "edge-type-list-empty": "Keine Edgetypen ausgewählt.", - "edge-types": "Edgetypen", - "enter-edge-type": "Geben Sie den Edgetyp ein", + "search": "Edges suchen", + "selected-edges": "{ count, plural, =1 {1 Edge} other {# Edges} } ausgewählt", + "any-edge": "Beliebige Edge", + "no-edge-types-matching": "Keine Edge-Typen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "edge-type-list-empty": "Keine Edge-Typen ausgewählt.", + "edge-types": "Edge-Typen", + "enter-edge-type": "Edge-Typ eingeben", "deployed": "Bereitgestellt", - "pending": "Steht aus", + "pending": "Ausstehend", "downlinks": "Downlinks", "no-downlinks-prompt": "Keine Downlinks gefunden", - "sync-process-started-successfully": "Synchronisierungsprozess erfolgreich gestartet!", - "missing-related-rule-chains-title": "In Edge fehlen verwandte Regelketten.", - "missing-related-rule-chains-text": "Randregelkette (n) zugewiesen Verwenden Sie Regelknoten, die Nachrichten an Regelkette (n) weiterleiten, die dieser Kante nicht zugeordnet sind.

Liste der fehlenden Regelketten:
{{missingRuleChains}}", - "widget-datasource-error": "Dieses Widget unterstützt nur EDGE-Entitätsdatenquellen" + "sync-process-started-successfully": "Synchronisationsprozess erfolgreich gestartet!", + "missing-related-rule-chains-title": "Edge hat fehlende zugehörige Regelketten", + "missing-related-rule-chains-text": "Zugewiesene Regelketten verwenden Knoten, die Nachrichten an nicht zugewiesene Regelketten weiterleiten.

Liste fehlender Regelketten:
{{missingRuleChains}}", + "widget-datasource-error": "Dieses Widget unterstützt nur Edge-Entitätsdatenquellen", + "upgrade-instructions": "Upgrade-Anleitung", + "connected": "Verbunden", + "disconnected": "Getrennt" }, "edge-event": { "type-dashboard": "Dashboard", "type-asset": "Asset", "type-device": "Gerät", - "type-device-profile": "Geräteprofile", + "type-device-profile": "Geräteprofil", + "type-asset-profile": "Asset-Profil", "type-entity-view": "Entitätsansicht", "type-alarm": "Alarm", "type-rule-chain": "Regelkette", - "type-rule-chain-metadata": "Regelkettenmetadaten", + "type-rule-chain-metadata": "Metadaten der Regelkette", "type-edge": "Edge", "type-user": "Benutzer", + "type-tenant": "Mieter", + "type-tenant-profile": "Mieterprofil", "type-customer": "Kunde", "type-relation": "Beziehung", - "type-widgets-bundle": "Widgetpaket", - "type-widgets-type": "Widgettyp", - "type-admin-settings": "Admineinstellungen", + "type-widgets-bundle": "Widget-Bündel", + "type-widgets-type": "Widget-Typ", + "type-admin-settings": "Admin-Einstellungen", + "type-ota-package": "OTA-Paket", + "type-queue": "Warteschlange", "action-type-added": "Hinzugefügt", "action-type-deleted": "Gelöscht", "action-type-updated": "Aktualisiert", - "action-type-post-attributes": "Attribute posten", - "action-type-attributes-updated": "Attribute Aktualisiert", - "action-type-attributes-deleted": "Attribute Gelöscht", - "action-type-timeseries-updated": "Timeseries Aktualisiert", - "action-type-credentials-updated": "Zugangsdaten Aktualisiert", - "action-type-assigned-to-customer": "Dem Kunden zugewiesen", - "action-type-unassigned-from-customer": "Kundenzuweisung aufgehoben", - "action-type-relation-add-or-update": "Beziehung hinzufügen oder Aktualisieren", + "action-type-post-attributes": "Attribute gesendet", + "action-type-attributes-updated": "Attribute aktualisiert", + "action-type-attributes-deleted": "Attribute gelöscht", + "action-type-timeseries-updated": "Zeitreihen aktualisiert", + "action-type-credentials-updated": "Anmeldeinformationen aktualisiert", + "action-type-assigned-to-customer": "Kunde zugewiesen", + "action-type-unassigned-from-customer": "Vom Kunden gelöst", + "action-type-relation-add-or-update": "Beziehung hinzugefügt oder aktualisiert", "action-type-relation-deleted": "Beziehung gelöscht", "action-type-rpc-call": "RPC-Aufruf", - "action-type-alarm-ack": "Alarm zurkenntnisnehmen", - "action-type-alarm-clear": "Alarm löschen", - "action-type-assigned-to-edge": "Der Edge zuweisen", - "action-type-unassigned-from-edge": "Edge-Zuweisung aufheben", - "action-type-credentials-request": "Zugangsdatenabfrage", - "action-type-entity-merge-request": "Entitäts-Merge-Request" + "action-type-alarm-ack": "Alarm bestätigt", + "action-type-alarm-clear": "Alarm gelöscht", + "action-type-alarm-assigned": "Alarm zugewiesen", + "action-type-alarm-unassigned": "Alarm gelöst", + "action-type-assigned-to-edge": "Edge zugewiesen", + "action-type-unassigned-from-edge": "Edge gelöst", + "action-type-credentials-request": "Anmeldeanforderung", + "action-type-entity-merge-request": "Entitätszusammenführungsanfrage" }, "error": { - "unable-to-connect": "Es konnte keine Verbindung zum Server hergestellt werden! Bitte überprüfen Sie Ihre Internetverbindung.", + "unable-to-connect": "Verbindung zum Server nicht möglich! Bitte überprüfen Sie Ihre Internetverbindung.", "unhandled-error-code": "Unbehandelter Fehlercode: {{errorCode}}", "unknown-error": "Unbekannter Fehler" }, "entity": { "entity": "Entität", "entities": "Entitäten", - "aliases": "Entitäts-Aliasnamen", + "entities-count": "Anzahl der Entitäten", + "alarms-count": "Anzahl der Alarme", + "aliases": "Entitätsalias", + "aliases-short": "Aliase", "entity-alias": "Entitätsalias", - "unable-delete-entity-alias-title": "Alias der Entität kann nicht gelöscht werden", - "unable-delete-entity-alias-text": "Alias der Entität '{{entityAlias}}' kann nicht gelöscht werden, da es von den folgenden Widget(s) verwendet wird:
{{widgetsList}}", - "duplicate-alias-error": "Doppelte Alias gefunden '{{alias}}'.
Die Aliase der Entität müssen innerhalb des Dashboards eindeutig sein.", - "missing-entity-filter-error": "Fehlender Filter für Alias '{{alias}}'.", - "configure-alias": "Alias '{{alias}}' konfigurieren", + "unable-delete-entity-alias-title": "Entitätsalias konnte nicht gelöscht werden", + "unable-delete-entity-alias-text": "Der Entitätsalias '{{entityAlias}}' kann nicht gelöscht werden, da er von folgenden Widget(s) verwendet wird:
{{widgetsList}}", + "duplicate-alias-error": "Doppelter Alias gefunden '{{alias}}'.
Entitätsaliase müssen innerhalb des Dashboards eindeutig sein.", + "missing-entity-filter-error": "Filter für Alias '{{alias}}' fehlt.", + "configure-alias": "Konfiguriere Alias '{{alias}}'", "alias": "Alias", - "alias-required": "Alias der Entität ist erforderlich.", - "remove-alias": "Alias der Entität entfernen", - "add-alias": "Alias der Entität erforderlich", - "entity-list": "Entitätsliste", + "alias-required": "Entitätsalias ist erforderlich.", + "remove-alias": "Entitätsalias entfernen", + "add-alias": "Entitätsalias hinzufügen", + "edit-alias": "Entitätsalias bearbeiten", + "entity-list": "Entitätenliste", "entity-type": "Entitätstyp", "entity-types": "Entitätstypen", - "entity-type-list": "Liste der Entitätstyp", - "any-entity": "Jede Entität", + "entity-type-list": "Liste der Entitätstypen", + "any-entity": "Beliebige Entität", + "add-entity-type": "Entitätstyp hinzufügen", "enter-entity-type": "Entitätstyp eingeben", - "no-entities-matching": "Keine passenden Entitäten für '{{entity}}' gefunden.", - "no-entity-types-matching": "Keine passende Entitätstypen für '{{entityType}}' gefunden.", - "name-starts-with": "Name beginnt mit", + "no-entities-matching": "Keine Entitäten gefunden, die mit '{{entity}}' übereinstimmen.", + "no-entities-text": "Keine Entitäten gefunden", + "no-entity-types-matching": "Keine Entitätstypen gefunden, die mit '{{entityType}}' übereinstimmen.", + "name-starts-with": "Namensausdruck", + "help-text": "Verwenden Sie '%' nach Bedarf: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", "use-entity-name-filter": "Filter verwenden", "entity-list-empty": "Keine Entitäten ausgewählt.", - "entity-name-filter-required": "Entitätsnamenfilter ist erforderlich.", - "entity-name-filter-no-entity-matched": "Keine Entitäten beginnend mit '{{entity}}' gefunden.", + "entity-type-list-required": "Mindestens ein Entitätstyp sollte ausgewählt werden.", + "entity-name-filter-required": "Filter für Entitätsname ist erforderlich.", + "entity-name-filter-no-entity-matched": "Keine Entitäten gefunden, die mit '{{entity}}' beginnen.", "all-subtypes": "Alle", "select-entities": "Entitäten auswählen", "no-aliases-found": "Keine Aliase gefunden.", "no-alias-matching": "'{{alias}}' nicht gefunden.", - "create-new-alias": "Erstellen Sie einen neuen Alias!", + "create-new-alias": "Einen neuen erstellen!", + "create-new": "Neu erstellen", "key": "Schlüssel", - "key-name": "Name des Schlüssels", - "no-keys-found": "Kein Schlüssel gefunden.", + "key-name": "Schlüsselname", + "no-keys-found": "Keine Schlüssel gefunden.", "no-key-matching": "'{{key}}' nicht gefunden.", - "create-new-key": "Erstellen Sie einen neuen Schlüssel!", + "create-new-key": "Einen neuen erstellen!", "type": "Typ", - "type-required": "Typ der Entität ist erforderlich.", + "type-required": "Entitätstyp ist erforderlich.", "type-device": "Gerät", "type-devices": "Geräte", "list-of-devices": "{ count, plural, =1 {Ein Gerät} other {Liste von # Geräten} }", - "device-name-starts-with": "Geräte beginnend mit '{{prefix}}'", - "type-asset": "Objekt", - "type-assets": "Objekte", - "list-of-assets": "{ count, plural, =1 {Ein Objekt} other {Liste von # Objekten} }", - "asset-name-starts-with": "Objekte beginnend mit '{{prefix}}'", + "device-name-starts-with": "Geräte, deren Name mit '{{prefix}}' beginnt", + "type-device-profile": "Geräteprofil", + "type-device-profiles": "Geräteprofile", + "clear-selected-profiles": "Ausgewählte Profile löschen", + "list-of-device-profiles": "{ count, plural, =1 {Ein Geräteprofil} other {Liste von # Geräteprofilen} }", + "device-profile-name-starts-with": "Geräteprofile, deren Name mit '{{prefix}}' beginnt", + "type-asset-profile": "Asset-Profil", + "type-asset-profiles": "Asset-Profile", + "list-of-asset-profiles": "{ count, plural, =1 {Ein Asset-Profil} other {Liste von # Asset-Profilen} }", + "asset-profile-name-starts-with": "Asset-Profile, deren Name mit '{{prefix}}' beginnt", + "type-asset": "Asset", + "type-assets": "Assets", + "list-of-assets": "{ count, plural, =1 {Ein Asset} other {Liste von # Assets} }", + "asset-name-starts-with": "Assets, deren Name mit '{{prefix}}' beginnt", "type-entity-view": "Entitätsansicht", "type-entity-views": "Entitätsansichten", "list-of-entity-views": "{ count, plural, =1 {Eine Entitätsansicht} other {Liste von # Entitätsansichten} }", - "entity-view-name-starts-with": "Entitätsansichten beginnend mit'{{prefix}}'", + "entity-view-name-starts-with": "Entitätsansichten, deren Name mit '{{prefix}}' beginnt", "type-rule": "Regel", "type-rules": "Regeln", "list-of-rules": "{ count, plural, =1 {Eine Regel} other {Liste von # Regeln} }", - "rule-name-starts-with": "Regeln beginnend mit '{{prefix}}'", + "rule-name-starts-with": "Regeln, deren Name mit '{{prefix}}' beginnt", "type-plugin": "Plugin", "type-plugins": "Plugins", "list-of-plugins": "{ count, plural, =1 {Ein Plugin} other {Liste von # Plugins} }", - "plugin-name-starts-with": "Plugins beginnend mit '{{prefix}}'", - "type-tenant": "Mandant", - "type-tenants": "Mandanten", - "list-of-tenants": "{ count, plural, =1 {Ein Mandant} other {Liste von # Mandanten} }", - "tenant-name-starts-with": "Mandanten beginnend mit '{{prefix}}'", + "plugin-name-starts-with": "Plugins, deren Name mit '{{prefix}}' beginnt", + "type-tenant": "Tenant", + "type-tenants": "Tenants", + "list-of-tenants": "{ count, plural, =1 {Ein Tenant} other {Liste von # Tenants} }", + "tenant-name-starts-with": "Tenants, deren Name mit '{{prefix}}' beginnt", + "type-tenant-profile": "Tenant-Profil", + "type-tenant-profiles": "Tenant-Profile", + "list-of-tenant-profiles": "{ count, plural, =1 {Ein Tenant-Profil} other {Liste von # Tenant-Profilen} }", + "tenant-profile-name-starts-with": "Tenant-Profile, deren Name mit '{{prefix}}' beginnt", "type-customer": "Kunde", "type-customers": "Kunden", "list-of-customers": "{ count, plural, =1 {Ein Kunde} other {Liste von # Kunden} }", - "customer-name-starts-with": "Kunden beginnend mit '{{prefix}}'", + "customer-name-starts-with": "Kunden, deren Name mit '{{prefix}}' beginnt", "type-user": "Benutzer", "type-users": "Benutzer", "list-of-users": "{ count, plural, =1 {Ein Benutzer} other {Liste von # Benutzern} }", - "user-name-starts-with": "Benutzer beginnend mit '{{prefix}}'", + "user-name-starts-with": "Benutzer, deren Name mit '{{prefix}}' beginnt", "type-dashboard": "Dashboard", "type-dashboards": "Dashboards", "list-of-dashboards": "{ count, plural, =1 {Ein Dashboard} other {Liste von # Dashboards} }", - "dashboard-name-starts-with": "Dashboards beginnend mit '{{prefix}}'", + "dashboard-name-starts-with": "Dashboards, deren Name mit '{{prefix}}' beginnt", "type-alarm": "Alarm", "type-alarms": "Alarme", "list-of-alarms": "{ count, plural, =1 {Ein Alarm} other {Liste von # Alarmen} }", - "alarm-name-starts-with": "Alarme, beginnend mit '{{prefix}}'", + "alarm-name-starts-with": "Alarme, deren Name mit '{{prefix}}' beginnt", "type-rulechain": "Regelkette", "type-rulechains": "Regelketten", "list-of-rulechains": "{ count, plural, =1 {Eine Regelkette} other {Liste von # Regelketten} }", - "rulechain-name-starts-with": "Regelketten beginnend mit '{{prefix}}'", + "rulechain-name-starts-with": "Regelketten, deren Name mit '{{prefix}}' beginnt", "type-rulenode": "Regelknoten", "type-rulenodes": "Regelknoten", "list-of-rulenodes": "{ count, plural, =1 {Ein Regelknoten} other {Liste von # Regelknoten} }", - "rulenode-name-starts-with": "Regelknoten beginnend mit '{{prefix}}'", - "type-edge": "Edgetyp", - "type-edges": "Eandtyp", - "list-of-edges": "{ count, plural, =1 {1 Edge} other {# Edges} }", - "edge-name-starts-with": "Edge beginnend mit '{{prefix}}'", + "rulenode-name-starts-with": "Regelknoten, deren Name mit '{{prefix}}' beginnt", "type-current-customer": "Aktueller Kunde", + "type-current-tenant": "Aktueller Tenant", + "type-current-user": "Aktueller Benutzer", + "type-current-user-owner": "Aktueller Benutzerinhaber", + "type-calculated-field": "Berechnetes Feld", + "type-calculated-fields": "Berechnete Felder", + "type-widgets-bundle": "Widget-Bündel", + "type-widgets-bundles": "Widget-Bündel", + "list-of-widgets-bundles": "{ count, plural, =1 {Ein Widget-Bündel} other {Liste von # Widget-Bündeln} }", + "type-widget": "Widget", + "type-widgets": "Widgets", + "list-of-widgets": "{ count, plural, =1 {Ein Widget} other {Liste von # Widgets} }", "search": "Entitäten suchen", - "selected-entities": "{ count, plural, =1 {Entität} other {# Entitäten} } ausgewählt", + "selected-entities": "{ count, plural, =1 {1 Entität} other {# Entitäten} } ausgewählt", "entity-name": "Entitätsname", + "entity-label": "Entitätslabel", "details": "Entitätsdetails", "no-entities-prompt": "Keine Entitäten gefunden", - "no-data": "Keine Daten zum Anzeigen", - "columns-to-display": "Anzuzeigende Spalten" + "no-data": "Keine Daten zur Anzeige vorhanden", + "columns-to-display": "Anzuzeigende Spalten", + "type-api-usage-state": "API-Nutzungsstatus", + "type-edge": "Edge", + "type-edges": "Edges", + "list-of-edges": "{ count, plural, =1 {Ein Edge} other {Liste von # Edges} }", + "edge-name-starts-with": "Edges, deren Name mit '{{prefix}}' beginnt", + "version-conflict": { + "message": "Möchten Sie die bestehende Version überschreiben oder Änderungen verwerfen und die neueste Version laden?", + "link": "Sie können Ihre Version der {{entityType}} mit diesem Link herunterladen", + "overwrite": "Version überschreiben", + "discard": "Änderungen verwerfen" + }, + "type-tb-resource": "Ressource", + "type-tb-resources": "Ressourcen", + "list-of-tb-resources": "{ count, plural, =1 {Eine Ressource} other {Liste von # Ressourcen} }", + "type-ota-package": "OTA-Paket", + "type-rpc": "RPC", + "type-queue": "Warteschlange", + "type-queue-stats": "Warteschlangenstatistiken", + "type-queues-stats": "Warteschlangenstatistiken", + "type-notification": "Benachrichtigung", + "type-notification-rule": "Benachrichtigungsregel", + "type-notification-rules": "Benachrichtigungsregeln", + "list-of-notification-rules": "{ count, plural, =1 {Eine Benachrichtigungsregel} other {Liste von # Benachrichtigungsregeln} }", + "type-notification-target": "Benachrichtigungsempfänger", + "type-notification-targets": "Benachrichtigungsempfänger", + "list-of-notification-targets": "{ count, plural, =1 {Ein Benachrichtigungsempfänger} other {Liste von # Benachrichtigungsempfängern} }", + "type-notification-request": "Benachrichtigungsanfrage", + "type-notification-template": "Benachrichtigungsvorlage", + "type-notification-templates": "Benachrichtigungsvorlagen", + "list-of-notification-templates": "{ count, plural, =1 {Eine Benachrichtigungsvorlage} other {Liste von # Benachrichtigungsvorlagen} }", + "link": "Link", + "type-oauth2-client": "OAuth 2.0-Client", + "type-oauth2-clients": "OAuth 2.0-Clients", + "list-of-oauth2-clients": "{ count, plural, =1 {Ein OAuth 2.0-Client} other {Liste von # OAuth 2.0-Clients} }", + "type-domain": "Domain", + "type-domains": "Domains", + "list-of-domains": "{ count, plural, =1 {Eine Domain} other {Liste von # Domains} }", + "type-mobile-app": "Mobile Anwendung", + "type-mobile-apps": "Mobile Anwendungen", + "list-of-mobile-apps": "{ count, plural, =1 {Eine mobile Anwendung} other {Liste von # mobilen Anwendungen} }", + "type-mobile-app-bundle": "Mobile-Bundle", + "type-mobile-app-bundles": "Mobile-Bundles", + "list-of-mobile-app-bundles": "{ count, plural, =1 {Ein Mobile-Bundle} other {Liste von # Mobile-Bundles} }" + }, + "entity-field": { + "created-time": "Erstellungszeit", + "name": "Name", + "type": "Typ", + "first-name": "Vorname", + "last-name": "Nachname", + "email": "E-Mail", + "title": "Titel", + "country": "Land", + "state": "Bundesland", + "city": "Stadt", + "address": "Adresse", + "address2": "Adresse 2", + "zip": "Postleitzahl", + "phone": "Telefon", + "label": "Bezeichnung", + "queue-name": "Name der Warteschlange", + "service-id": "Service-ID", + "owner-name": "Name des Besitzers", + "owner-type": "Typ des Besitzers" }, "entity-view": { "entity-view": "Entitätsansicht", "entity-view-required": "Entitätsansicht ist erforderlich.", "entity-views": "Entitätsansichten", - "management": "Entitätsansichten verwalten", + "management": "Entitätsansichtenverwaltung", "view-entity-views": "Entitätsansichten anzeigen", - "entity-view-alias": "Entitätsansichtsalias", - "aliases": "Entitätsansichten-Aliase", + "entity-view-alias": "Entitätsansicht-Alias", + "aliases": "Entitätsansicht-Aliase", "no-alias-matching": "'{{alias}}' nicht gefunden.", "no-aliases-found": "Keine Aliase gefunden.", "no-key-matching": "'{{key}}' nicht gefunden.", "no-keys-found": "Keine Schlüssel gefunden.", "create-new-alias": "Neuen Alias erstellen!", "create-new-key": "Neuen Schlüssel erstellen!", - "duplicate-alias-error": "Doppelter Alias gefunden '{{alias}}'.
Aliase der Entitätsansicht müssen innerhalb des Dashboards eindeutig sein.", - "configure-alias": "Alias '{{alias}}' konfigurieren", - "no-entity-views-matching": "Keine passenden Entitätsansichten für '{{entity}}' gefunden.", + "duplicate-alias-error": "Doppelter Alias gefunden '{{alias}}'.
Entitätsansicht-Aliase müssen innerhalb des Dashboards eindeutig sein.", + "configure-alias": "Konfiguriere Alias '{{alias}}'", + "no-entity-views-matching": "Keine Entitätsansichten gefunden, die '{{entity}}' entsprechen.", + "public": "Öffentlich", "alias": "Alias", - "alias-required": "Alias der Entitätsansicht erforderlich.", - "remove-alias": "Alias der Entitätsansicht entfernen", - "add-alias": "Alias für die Entitätsansicht hinzufügen", - "name-starts-with": "Entitätsansichtsname beginnend mit", + "alias-required": "Entitätsansicht-Alias ist erforderlich.", + "remove-alias": "Entitätsansicht-Alias entfernen", + "add-alias": "Entitätsansicht-Alias hinzufügen", + "name-starts-with": "Namensausdruck für Entitätsansicht", + "help-text": "Verwenden Sie '%' nach Bedarf: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", "entity-view-list": "Liste der Entitätsansichten", - "use-entity-view-name-filter": "Filter anwenden", - "entity-view-list-empty": "Keine der Entitätsansichten ausgewählt.", - "entity-view-name-filter-required": "Filterung nach Entitätsansichtenname erforderlich.", - "entity-view-name-filter-no-entity-view-matched": "Keine Entitätsansichten beginnend mit '{{entityView}}' wurden gefunden.", + "use-entity-view-name-filter": "Filter verwenden", + "entity-view-list-empty": "Keine Entitätsansichten ausgewählt.", + "entity-view-name-filter-required": "Filter für Entitätsansicht-Name ist erforderlich.", + "entity-view-name-filter-no-entity-view-matched": "Keine Entitätsansichten gefunden, die mit '{{entityView}}' beginnen.", "add": "Entitätsansicht hinzufügen", - "assign-to-customer": "Einem Kunden zuordnen", - "assign-entity-view-to-customer": "Entitätsansichten dem Kunden zuordnen", - "assign-entity-view-to-customer-text": "Bitte wählen Sie die Entitätsansichten aus, die dem Kunden zugeordnet werden sollen", + "entity-view-public": "Entitätsansicht ist öffentlich", + "assign-to-customer": "Kunde zuweisen", + "assign-entity-view-to-customer": "Entitätsansicht(en) einem Kunden zuweisen", + "assign-entity-view-to-customer-text": "Bitte wählen Sie die Entitätsansichten aus, die dem Kunden zugewiesen werden sollen", "no-entity-views-text": "Keine Entitätsansichten gefunden", - "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Entitätsansichten zugeordnet werden sollen", + "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Entitätsansicht(en) zugewiesen werden sollen", "entity-view-details": "Details der Entitätsansicht", "add-entity-view-text": "Neue Entitätsansicht hinzufügen", "delete": "Entitätsansicht löschen", - "assign-entity-views": "Entitätsansicht zuordnen", - "assign-entity-views-text": "Dem Kunden { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } zuordnen", + "assign-entity-views": "Entitätsansichten zuweisen", + "assign-entity-views-text": "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } einem Kunden zuweisen", "delete-entity-views": "Entitätsansichten löschen", - "unassign-from-customer": "Zuordnung zum Kunden aufheben", - "unassign-entity-views": "Zuordnung der Entitätsansichten aufheben", - "unassign-entity-views-action-title": "Die Zuordnung { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } zum Kunden aufheben", - "assign-new-entity-view": "Neue Entitätsansicht zuordnen", - "delete-entity-view-title": "Möchten Sie die Entitätsansicht wirklich löschen '{{entityViewName}}'?", - "delete-entity-view-text": "Seien Sie vorsichtig, nach der Bestätigung werden die Entitätsansicht und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-entity-views-title": "Sind Sie sicher, dass Sie die Entitätsansichten löschen möchten { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} }?", - "delete-entity-views-action-title": "Löschen { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} }", - "delete-entity-views-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Entitätsansichten entfernt und alle zugehörigen Daten werden nicht wiederhergestellt.", - "unassign-entity-view-title": "Möchten Sie die Zuordnung der Entitätsansicht '{{entityViewName}}' wirklich aufheben?", - "unassign-entity-view-text": "Nach der Bestätigung wird die Zuordnung der Entitätsansicht aufgehoben und ist für den Kunden nicht mehr zugänglich.", - "unassign-entity-view": "Zuordnung der Entitätsansicht aufheben", - "unassign-entity-views-title": "Sind Sie sicher, dass Sie die Zuordnung aufheben möchten { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} }?", - "unassign-entity-views-text": "Nach der Bestätigung werden die Zuordnungen der ausgewählten Entitätsansichten aufgehoben und sind für den Kunden nicht mehr zugänglich.", - "entity-view-type": "Entitätsansichtstyp", - "entity-view-type-required": "Entitätsansichtstyp ist erforderlich.", - "select-entity-view-type": "Entitätsansichtstyp auswählen", - "enter-entity-view-type": "Entitätsansichtstyp eingeben", - "any-entity-view": "Jede Entitätsansicht", - "no-entity-view-types-matching": "Es wurden keine passenden Entitätsansichtstypen für '{{entitySubtype}}' gefunden.", + "make-public": "Entitätsansicht öffentlich machen", + "make-private": "Entitätsansicht privat machen", + "unassign-from-customer": "Zuweisung vom Kunden aufheben", + "unassign-entity-views": "Entitätsansichtenzuweisung aufheben", + "unassign-entity-views-action-title": "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } vom Kundenzuweisung aufheben", + "assign-new-entity-view": "Neue Entitätsansicht zuweisen", + "delete-entity-view-title": "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' löschen möchten?", + "delete-entity-view-text": "Seien Sie vorsichtig, nach der Bestätigung wird die Entitätsansicht und alle zugehörigen Daten unwiderruflich gelöscht.", + "delete-entity-views-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } löschen möchten?", + "delete-entity-views-action-title": "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } löschen", + "delete-entity-views-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Entitätsansichten und deren Daten unwiderruflich gelöscht.", + "make-public-entity-view-title": "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' öffentlich machen möchten?", + "make-public-entity-view-text": "Nach der Bestätigung wird die Entitätsansicht und alle ihre Daten öffentlich und für andere zugänglich gemacht.", + "make-private-entity-view-title": "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' privat machen möchten?", + "make-private-entity-view-text": "Nach der Bestätigung wird die Entitätsansicht und alle ihre Daten privat und nicht mehr zugänglich sein.", + "unassign-entity-view-title": "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' von der Zuweisung lösen möchten?", + "unassign-entity-view-text": "Nach der Bestätigung wird die Entitätsansicht von der Zuweisung entfernt und ist für den Kunden nicht mehr zugänglich.", + "unassign-entity-view": "Entitätsansichtzuweisung aufheben", + "unassign-entity-views-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } von der Zuweisung lösen möchten?", + "unassign-entity-views-text": "Nach der Bestätigung werden alle ausgewählten Entitätsansichten von der Zuweisung gelöst und sind für den Kunden nicht mehr zugänglich.", + "entity-view-type": "Typ der Entitätsansicht", + "entity-view-type-required": "Typ der Entitätsansicht ist erforderlich.", + "select-entity-view-type": "Typ der Entitätsansicht auswählen", + "enter-entity-view-type": "Typ der Entitätsansicht eingeben", + "any-entity-view": "Beliebige Entitätsansicht", + "no-entity-view-types-matching": "Keine Entitätsansichtstypen gefunden, die '{{entitySubtype}}' entsprechen.", "entity-view-type-list-empty": "Keine Entitätsansichtstypen ausgewählt.", "entity-view-types": "Entitätsansichtstypen", + "created-time": "Erstellungszeit", "name": "Name", "name-required": "Name ist erforderlich.", - "assign-entity-view-to-edge": "Entitätsansicht dem Rand zuordnen", - "assign-entity-view-to-edge-text":"Bitte wählen Sie die Entitätsansicht aus, die dem Rand zugeordnet werden sollen", - "unassign-entity-view-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für Entitätsansicht '{{entityViewName}}' aufheben möchten?", - "unassign-entity-view-from-edge-text": "Nach Bestätigung wird die Zuordnung des Entitätsansichts aufgehoben und es ist für den Kunden nicht mehr zugänglich.", - "unassign-entity-views-from-edge-action-title": "Rand { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichte} } aufheben", - "unassign-entity-view-from-edge": "Entitätsansichtzuordnung aufheben", - "unassign-entity-views-from-edge-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichte} } nicht mehr zuordnen möchten?", - "unassign-entity-views-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Entitätsansicht nicht zugewiesen und sind für den Rand nicht zugänglich.", + "name-max-length": "Name sollte weniger als 256 Zeichen haben.", + "type-max-length": "Entitätsansichtstyp sollte weniger als 256 Zeichen haben.", "description": "Beschreibung", "events": "Ereignisse", "details": "Details", - "copyId": "Entitätsansichts-ID kopieren", - "assignedToCustomer": "Dem Kunden zuordnen", - "unable-entity-view-device-alias-title": "Alias der Entitätsansicht kann nicht gelöscht werden", - "unable-entity-view-device-alias-text": "Geräte-Alias '{{entityViewAlias}}' kann nicht gelöscht werden, da es von den folgenden widget(s):
{{widgetsList}} verwendet wird", + "copyId": "Entitätsansicht-ID kopieren", + "idCopiedMessage": "Entitätsansicht-ID wurde in die Zwischenablage kopiert", + "assignedToCustomer": "Dem Kunden zugewiesen", + "unable-entity-view-device-alias-title": "Entitätsansicht-Alias konnte nicht gelöscht werden", + "unable-entity-view-device-alias-text": "Gerätealias '{{entityViewAlias}}' kann nicht gelöscht werden, da er von folgenden Widget(s) verwendet wird:
{{widgetsList}}", "select-entity-view": "Entitätsansicht auswählen", - "make-public": "Entitätsansicht öffentlich machen", - "start-date": "Start-Datum", - "start-ts": "Start-Zeit", - "end-date": "Ende-Datum", - "end-ts": "Ende-Zeit", - "date-limits": "Datumslimits", - "client-attributes": "Client Eigenschaften", - "shared-attributes": "Gemeinsame Eigenschaften", - "server-attributes": "Server Eigenschaften", - "timeseries": "Zeitreihe", - "client-attributes-placeholder": "Client Eigenschaften", - "shared-attributes-placeholder": "Gemeinsame Eigenschaften", - "server-attributes-placeholder": "Server Eigenschaften", - "timeseries-placeholder": "Zeitreihe", + "start-ts": "Startzeit", + "end-ts": "Endzeit", + "date-limits": "Datumsgrenzen", + "client-attributes": "Client-Attribute", + "shared-attributes": "Geteilte Attribute", + "server-attributes": "Server-Attribute", + "timeseries": "Zeitreihen", + "client-attributes-placeholder": "Client-Attribute", + "shared-attributes-placeholder": "Geteilte Attribute", + "server-attributes-placeholder": "Server-Attribute", + "timeseries-placeholder": "Zeitreihen", "target-entity": "Zielentität", - "attributes-propagation": "Eigenschaftsübertragung", - "attributes-propagation-hint": "Die Entitätsansicht kopiert automatisch die angegebenen Eigenschaften der Ziel-Entität, wenn Sie diese Entitätsansicht speichern oder aktualisieren. Aus Performance-Gründen werden die Attribute der Ziel-Entität nicht bei jeder Eigenschaftsänderung in die Entitätsansicht übertragen. Sie können die automatische Weitergabe aktivieren, indem Sie den Regelknoten \"copy to view\" in Ihrer Regelkette konfigurieren und die Nachrichten \"Post attributes\" und \"Attributes updated\" mit dem neuen Regelknoten verknüpfen.", + "attributes-propagation": "Attributübertragung", + "attributes-propagation-hint": "Die Entitätsansicht kopiert automatisch die angegebenen Attribute von der Zielentität jedes Mal, wenn Sie diese Entitätsansicht speichern oder aktualisieren. Aus Leistungsgründen werden Zielentitätsattribute nicht bei jeder Attributänderung auf die Entitätsansicht übertragen. Sie können die automatische Übertragung aktivieren, indem Sie einen 'copy to view'-Regelknoten in Ihrer Regelkette konfigurieren und 'Post attributes' und 'Attributes Updated'-Nachrichten an diesen neuen Regelknoten verlinken.", "timeseries-data": "Zeitreihendaten", - "timeseries-data-hint": "Konfigurieren Sie die Datensatzschlüssel der Zeitreihe der Zielentität, auf die die Entitätsansicht zugreifen kann. Die Daten dieser Zeitreihe sind schreibgeschützt." + "timeseries-data-hint": "Konfigurieren Sie die Zeitreihendatenschlüssel der Zielentität, die für die Entitätsansicht zugänglich sein werden. Diese Zeitreihendaten sind schreibgeschützt.", + "search": "Entitätsansichten suchen", + "selected-entity-views": "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } ausgewählt", + "assign-entity-view-to-edge": "Entitätsansicht(en) Edge zuweisen", + "assign-entity-view-to-edge-text": "Bitte wählen Sie die Entitätsansichten aus, die dem Edge zugewiesen werden sollen", + "unassign-entity-view-from-edge-title": "Sind Sie sicher, dass Sie die Entitätsansicht '{{entityViewName}}' von Edge entfernen möchten?", + "unassign-entity-view-from-edge-text": "Nach der Bestätigung wird die Entitätsansicht von Edge entfernt und ist nicht mehr zugänglich.", + "unassign-entity-views-from-edge-action-title": "{ count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } von Edge entfernen", + "unassign-entity-view-from-edge": "Entitätsansicht von Edge entfernen", + "unassign-entity-views-from-edge-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Entitätsansicht} other {# Entitätsansichten} } von Edge entfernen möchten?", + "unassign-entity-views-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Entitätsansichten von Edge entfernt und sind nicht mehr zugänglich." }, "event": { "event-type": "Ereignistyp", + "events-filter": "Ereignisfilter", + "clean-events": "Ereignisse löschen", "type-error": "Fehler", "type-lc-event": "Lebenszyklusereignis", "type-stats": "Statistiken", - "type-debug-rule-node": "Fehlersuche", - "type-debug-rule-chain": "Fehlersuche", + "type-debug-rule-node": "Debug", + "type-debug-rule-chain": "Debug", + "type-debug-calculated-field": "Debug", + "arguments": "Argumente", + "result": "Ergebnis", "no-events-prompt": "Keine Ereignisse gefunden", "error": "Fehler", - "type-edge-event": "Downlink", "alarm": "Alarm", "event-time": "Ereigniszeit", "server": "Server", "body": "Inhalt", "method": "Methode", "type": "Typ", - "message-id": "Nachrichten-Id", - "message-type": "Nachrichten-Typ", + "metadata": "Metadaten", + "message": "Nachricht", + "message-id": "Nachrichten-ID", + "copy-message-id": "Nachrichten-ID kopieren", + "message-type": "Nachrichtentyp", "data-type": "Datentyp", - "relation-type": "Beziehungstyp", - "metadata": "Meta-Daten", + "relation-type": "Relationstyp", "data": "Daten", "event": "Ereignis", "status": "Status", "success": "Erfolg", "failed": "Fehlgeschlagen", - "messages-processed": "Nachrichten verarbeitet", + "messages-processed": "Verarbeitete Nachrichten", + "max-messages-processed": "Maximal verarbeitete Nachrichten", + "min-messages-processed": "Minimal verarbeitete Nachrichten", "errors-occurred": "Fehler aufgetreten", + "max-errors-occurred": "Maximal aufgetretene Fehler", + "min-errors-occurred": "Minimal aufgetretene Fehler", + "min-value": "Mindestwert ist 0.", "all-events": "Alle", - "entity-type": "Entitätstyp" + "has-error": "Hat Fehler", + "entity-id": "Entitäts-ID", + "copy-entity-id": "Entitäts-ID kopieren", + "entity-type": "Entitätstyp", + "clear-filter": "Filter löschen", + "clear-request-title": "Alle Ereignisse löschen", + "clear-request-text": "Sind Sie sicher, dass Sie alle Ereignisse löschen möchten?", + "started": "Gestartet", + "updated": "Aktualisiert", + "stopped": "Gestoppt" }, "extension": { "extensions": "Erweiterungen", - "selected-extensions": "{ count, plural, =1 {Erweiterung} other {# Erweiterungen} } ausgewählt", + "selected-extensions": "{ count, plural, =1 {1 Erweiterung} other {# Erweiterungen} } ausgewählt", "type": "Typ", "key": "Schlüssel", "value": "Wert", - "id": "ID", + "id": "Id", "extension-id": "Erweiterungs-ID", "extension-type": "Erweiterungstyp", "transformer-json": "JSON *", - "unique-id-required": "Die aktuelle Erweiterungs-ID ist bereits vorhanden.", + "unique-id-required": "Die aktuelle Erweiterungs-ID existiert bereits.", "delete": "Erweiterung löschen", "add": "Erweiterung hinzufügen", "edit": "Erweiterung bearbeiten", - "delete-extension-title": "Möchten Sie die Erweiterung '{{extensionId}}' wirklich löschen?", - "delete-extension-text": "Vorsicht, nach Bestätigung werden die Erweiterung und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-extensions-title": "Möchten Sie wirklich { count, plural, =1 {1 Erweiterung} other {# Erweiterungen} } löschen?", - "delete-extensions-text": "Vorsicht, nach der Bestätigung werden alle ausgewählten Erweiterungen entfernt.", + "delete-extension-title": "Sind Sie sicher, dass Sie die Erweiterung '{{extensionId}}' löschen möchten?", + "delete-extension-text": "Seien Sie vorsichtig, nach der Bestätigung wird die Erweiterung und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-extensions-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Erweiterung} other {# Erweiterungen} } löschen möchten?", + "delete-extensions-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Erweiterungen entfernt.", "converters": "Konverter", "converter-id": "Konverter-ID", "configuration": "Konfiguration", - "converter-configurations": "Konvertierte Konfigurationen", - "token": "Sicherheitszeichen", + "converter-configurations": "Konverterkonfigurationen", + "token": "Sicherheitstoken", "add-converter": "Konverter hinzufügen", - "add-config": "Konvertierte Konfigurationen hinzufügen", - "device-name-expression": "Angabe des Gerätenamens", - "device-type-expression": "Angabe des Gerätetyps", - "custom": "Regel", - "to-double": "Duplizieren", - "transformer": "Transformator", - "json-required": "Transformer json ist erforderlich.", - "json-parse": "Transformer json kann nicht analysiert werden.", - "attributes": "Eigenschaften", - "add-attribute": "Eigenschaften hinzufügen", - "add-map": "Mapping-Element hinzufügen", - "timeseries": "Zeitreihe", + "add-config": "Konverterkonfiguration hinzufügen", + "device-name-expression": "Gerätename Ausdruck", + "device-type-expression": "Gerätetyp Ausdruck", + "custom": "Benutzerdefiniert", + "to-double": "In Double umwandeln", + "transformer": "Transformer", + "json-required": "Transformer-JSON ist erforderlich.", + "json-parse": "Transformer-JSON kann nicht geparst werden.", + "attributes": "Attribute", + "add-attribute": "Attribut hinzufügen", + "add-map": "Zuordnungselement hinzufügen", + "timeseries": "Zeitreihen", "add-timeseries": "Zeitreihe hinzufügen", "field-required": "Feld ist erforderlich", - "brokers": "Vermittler", - "add-broker": "Vermittler hinzufügen", + "brokers": "Brokers", + "add-broker": "Broker hinzufügen", "host": "Host", "port": "Port", - "port-range": "Der Port sollte im Bereich von 1 bis 65535 liegen.", + "port-range": "Der Port muss zwischen 1 und 65535 liegen.", "ssl": "SSL", "credentials": "Zugangsdaten", "username": "Benutzername", "password": "Passwort", "retry-interval": "Wiederholungsintervall in Millisekunden", "anonymous": "Anonym", - "basic": "Basic", + "basic": "Basis", "pem": "PEM", - "ca-cert": "CA-Zertifikatsdatei *", - "private-key": "Privatschlüsseldatei *", - "cert": "Zertifikatsdatei *", + "ca-cert": "CA-Zertifikatdatei *", + "private-key": "Privater Schlüsseldatei *", + "cert": "Zertifikatdatei *", "no-file": "Keine Datei ausgewählt.", - "drop-file": "Legen Sie eine Datei ab oder wählen Sie eine Datei aus um diese hochzuladen.", - "mapping": "Mapping", + "drop-file": "Datei ablegen oder klicken, um eine Datei hochzuladen.", + "mapping": "Zuordnung", "topic-filter": "Themenfilter", - "converter-type": "Konvertierungstyp", + "converter-type": "Konvertertyp", "converter-json": "Json", - "json-name-expression": "Angabe des Gerätenamens json", - "topic-name-expression": "Themenangabe des Gerätenamens", - "json-type-expression": "Angabe des Gerätenamens json", - "topic-type-expression": "Themenangabe des Gerätetyps", - "attribute-key-expression": "Angabe des Eigenschaftenschlüssels", - "attr-json-key-expression": "Angabe des Eigenschaftenschlüssels", - "attr-topic-key-expression": "Themenangabe des Eigenschaftenschlüssels", - "request-id-expression": "ID-Angabe anfordern", - "request-id-json-expression": "ID-Angabe anforern json", - "request-id-topic-expression": "Themenangabe der ID anfordern", - "response-topic-expression": "Antwort Themenangabe", - "value-expression": "Wertangabe", + "json-name-expression": "Gerätename JSON-Ausdruck", + "topic-name-expression": "Gerätename Themenausdruck", + "json-type-expression": "Gerätetyp JSON-Ausdruck", + "topic-type-expression": "Gerätetyp Themenausdruck", + "attribute-key-expression": "Attributschlüssel Ausdruck", + "attr-json-key-expression": "Attributschlüssel JSON-Ausdruck", + "attr-topic-key-expression": "Attributschlüssel Themenausdruck", + "request-id-expression": "Anforderungs-ID Ausdruck", + "request-id-json-expression": "Anforderungs-ID JSON-Ausdruck", + "request-id-topic-expression": "Anforderungs-ID Themenausdruck", + "response-topic-expression": "Antwortthemenausdruck", + "value-expression": "Wertausdruck", "topic": "Thema", - "timeout": "Unterbrechung in Millisekunden", - "converter-json-required": "Konvertierte json ist erforderlich.", - "converter-json-parse": "Konvertierte json konnte nicht analysiert werden.", - "filter-expression": "Filterangabe", - "connect-requests": "Abfragen verbinden", - "add-connect-request": "Verbindungsabfrage hinzufügen", - "disconnect-requests": "Abfrage trennen", - "add-disconnect-request": "Trennung der Abfrage hinzufügen", - "attribute-requests": "Abfrage der Eigenschaften", - "add-attribute-request": "Abfrage der Eigenschaften hinzufügen", - "attribute-updates": "Aktualisierungen der Eigenschaften", - "add-attribute-update": "Aktualisierung der Eigenschaften hinzufügen", - "server-side-rpc": "Serverseite RPC", - "add-server-side-rpc-request": "Abfrage der Serverseite RPC hinzufügen", - "device-name-filter": "Gerätenamefilter", - "attribute-filter": "Eigenschaftenfilter", + "timeout": "Timeout in Millisekunden", + "converter-json-required": "Konverter-JSON ist erforderlich.", + "converter-json-parse": "Konverter-JSON kann nicht geparst werden.", + "filter-expression": "Filterausdruck", + "connect-requests": "Verbindungsanfragen", + "add-connect-request": "Verbindungsanfrage hinzufügen", + "disconnect-requests": "Trennungsanfragen", + "add-disconnect-request": "Trennungsanfrage hinzufügen", + "attribute-requests": "Attributanfragen", + "add-attribute-request": "Attributanfrage hinzufügen", + "attribute-updates": "Attributaktualisierungen", + "add-attribute-update": "Attributaktualisierung hinzufügen", + "server-side-rpc": "Serverseitige RPC", + "add-server-side-rpc-request": "Serverseitige RPC-Anfrage hinzufügen", + "device-name-filter": "Gerätenamenfilter", + "attribute-filter": "Attributfilter", "method-filter": "Methodenfilter", - "request-topic-expression": "Themenabgabe anfordern", - "response-timeout": "Antwortzeit in Millisekunden", - "topic-expression": "Themenangabe", - "client-scope": "Kundenumfrage", + "request-topic-expression": "Anforderungsthemenausdruck", + "response-timeout": "Antwortzeitüberschreitung in Millisekunden", + "topic-expression": "Themenausdruck", + "client-scope": "Clientbereich", "add-device": "Gerät hinzufügen", - "opc-server": "Servers", + "opc-server": "Server", "opc-add-server": "Server hinzufügen", - "opc-add-server-prompt": "Bitte einen Server hinzufügen", + "opc-add-server-prompt": "Bitte Server hinzufügen", "opc-application-name": "Anwendungsname", - "opc-application-uri": "Anwendung uri", - "opc-scan-period-in-seconds": "Scanzeitraum in Sekunden", + "opc-application-uri": "Anwendungs-URI", + "opc-scan-period-in-seconds": "Scan-Periode in Sekunden", "opc-security": "Sicherheit", - "opc-identity": "Identifizierung", - "opc-keystore": "Schlüsselspeicher", + "opc-identity": "Identität", + "opc-keystore": "Schlüsselablage", "opc-type": "Typ", "opc-keystore-type": "Typ", "opc-keystore-location": "Speicherort *", "opc-keystore-password": "Passwort", "opc-keystore-alias": "Alias", "opc-keystore-key-password": "Schlüsselpasswort", - "opc-device-node-pattern": "Geräteknotenmuster", - "opc-device-name-pattern": "Gerätenamensmuster", - "modbus-server": "Servers/Folgegerät", - "modbus-add-server": "Server/Folgegerät hinzufügen", - "modbus-add-server-prompt": "Bitte Server/Folgegerät hinzufügen", + "opc-device-node-pattern": "Geräteknoten-Muster", + "opc-device-name-pattern": "Gerätename-Muster", + "modbus-server": "Server/Slaves", + "modbus-add-server": "Server/Slave hinzufügen", + "modbus-add-server-prompt": "Bitte Server/Slave hinzufügen", "modbus-transport": "Transport", - "modbus-tcp-reconnect": "Verbindung automatisch wiederherstellen", + "modbus-tcp-reconnect": "Automatisch neu verbinden", "modbus-rtu-over-tcp": "RTU über TCP", - "modbus-port-name": "Name des Seriellen Anschlusses", - "modbus-encoding": "Verschlüsselung", - "modbus-parity": "Übereinstimmung", - "modbus-baudrate": "Datenübertragungsgeschwindigkeit", - "modbus-databits": "Daten Bits", - "modbus-stopbits": "Stopp-Bits", + "modbus-port-name": "Serieller Portname", + "modbus-encoding": "Kodierung", + "modbus-parity": "Parität", + "modbus-baudrate": "Baudrate", + "modbus-databits": "Datenbits", + "modbus-stopbits": "Stoppbits", "modbus-databits-range": "Datenbits sollten im Bereich von 7 bis 8 liegen.", "modbus-stopbits-range": "Stoppbits sollten im Bereich von 1 bis 2 liegen.", - "modbus-unit-id": "ID der Einheit", - "modbus-unit-id-range": "Die Einheiten-ID sollte im Bereich von 1 bis 247 liegen.", + "modbus-unit-id": "Einheiten-ID", + "modbus-unit-id-range": "Einheiten-ID sollte im Bereich von 1 bis 247 liegen.", "modbus-device-name": "Gerätename", - "modbus-poll-period": "Abfragezeitraum in Millisekunden", - "modbus-attributes-poll-period": "Abfrageintervall der Eigenschaften in Millisekunden", - "modbus-timeseries-poll-period": "Abfrageintervall der Zeitreihen in Millisekunden", - "modbus-poll-period-range": "Das Abfrageintervall sollte einen positiven Wert haben.", - "modbus-tag": "Kennzeichnung", + "modbus-poll-period": "Abfrageintervall (ms)", + "modbus-attributes-poll-period": "Abfrageintervall für Attribute (ms)", + "modbus-timeseries-poll-period": "Abfrageintervall für Zeitreihen (ms)", + "modbus-poll-period-range": "Das Abfrageintervall sollte ein positiver Wert sein.", + "modbus-tag": "Tag", "modbus-function": "Funktion", "modbus-register-address": "Registeradresse", - "modbus-register-address-range": "Die Registeradresse sollte im Bereich zwischen 0 und 65535 liegen.", - "modbus-register-bit-index": "Bitindex", - "modbus-register-bit-index-range": "Der Bitindex sollte im Bereich von 0 bis 15 liegen.", + "modbus-register-address-range": "Registeradresse sollte im Bereich von 0 bis 65535 liegen.", + "modbus-register-bit-index": "Bit-Index", + "modbus-register-bit-index-range": "Bit-Index sollte im Bereich von 0 bis 15 liegen.", "modbus-register-count": "Registeranzahl", - "modbus-register-count-range": "Die Registeranzahl sollten einen positiven Wert haben.", + "modbus-register-count-range": "Registeranzahl sollte ein positiver Wert sein.", "modbus-byte-order": "Byte-Reihenfolge", "sync": { "status": "Status", - "sync": "Synchronisiert", + "sync": "Synchronisieren", "not-sync": "Nicht synchronisiert", - "last-sync-time": "Zeit der letzten Synchronisierung", + "last-sync-time": "Letzte Synchronisationszeit", "not-available": "Nicht verfügbar" }, "export-extensions-configuration": "Erweiterungskonfiguration exportieren", @@ -1395,6 +2922,107 @@ "file": "Erweiterungsdatei", "invalid-file-error": "Ungültige Erweiterungsdatei" }, + "feature": { + "advanced-features": "Erweiterte Funktionen" + }, + "filter": { + "add": "Filter hinzufügen", + "edit": "Filter bearbeiten", + "name": "Filtername", + "name-required": "Filtername ist erforderlich.", + "duplicate-filter": "Ein Filter mit demselben Namen existiert bereits.", + "filters": "Filter", + "unable-delete-filter-title": "Filter kann nicht gelöscht werden", + "unable-delete-filter-text": "Filter '{{filter}}' kann nicht gelöscht werden, da er von folgendem/n Widget(s) verwendet wird:
{{widgetsList}}", + "duplicate-filter-error": "Doppelter Filter gefunden '{{filter}}'.
Filter müssen innerhalb des Dashboards eindeutig sein.", + "missing-key-filters-error": "Schlüsselfilter fehlen für Filter '{{filter}}'.", + "filter": "Filter", + "editable": "Bearbeitbar", + "no-filters-found": "Keine Filter gefunden.", + "no-filter-text": "Kein Filter angegeben", + "add-filter-prompt": "Bitte Filter hinzufügen", + "no-filter-matching": "'{{filter}}' nicht gefunden.", + "create-new-filter": "Neuen Filter erstellen!", + "create-new": "Neu erstellen", + "filter-required": "Filter ist erforderlich.", + "operation": { + "operation": "Operation", + "equal": "gleich", + "not-equal": "ungleich", + "starts-with": "beginnt mit", + "ends-with": "endet mit", + "contains": "enthält", + "not-contains": "enthält nicht", + "greater": "größer als", + "less": "kleiner als", + "greater-or-equal": "größer oder gleich", + "less-or-equal": "kleiner oder gleich", + "and": "und", + "or": "oder", + "in": "in", + "not-in": "nicht in" + }, + "ignore-case": "Groß-/Kleinschreibung ignorieren", + "value": "Wert", + "remove-filter": "Filter entfernen", + "duplicate-filter-action": "Filter duplizieren", + "preview": "Filtervorschau", + "no-filters": "Keine Filter konfiguriert", + "add-filter": "Filter hinzufügen", + "add-complex-filter": "Komplexen Filter hinzufügen", + "add-complex": "Komplex hinzufügen", + "complex-filter": "Komplexer Filter", + "edit-complex-filter": "Komplexen Filter bearbeiten", + "edit-filter-user-params": "Filter-Benutzerparameter bearbeiten", + "filter-user-params": "Filterprädikat-Benutzerparameter", + "user-parameters": "Benutzerparameter", + "display-label": "Anzeigebezeichnung", + "order-priority": "Priorität der Feldreihenfolge", + "key-filter": "Schlüsselfilter", + "key-filters": "Schlüsselfilter", + "key-name": "Schlüsselname", + "key-name-required": "Schlüsselname ist erforderlich.", + "key-type": { + "key-type": "Schlüsseltyp", + "attribute": "Attribut", + "timeseries": "Zeitreihe", + "entity-field": "Entitätsfeld", + "constant": "Konstante", + "client-attribute": "Client-Attribut", + "server-attribute": "Server-Attribut", + "shared-attribute": "Geteiltes Attribut" + }, + "value-type": { + "value-type": "Werttyp", + "string": "String", + "numeric": "Numerisch", + "boolean": "Boolesch", + "date-time": "Datum und Zeit" + }, + "value-type-required": "Werttyp des Schlüssels ist erforderlich.", + "key-value-type-change-title": "Sind Sie sicher, dass Sie den Werttyp des Schlüssels ändern möchten?", + "key-value-type-change-message": "Wenn Sie den neuen Werttyp bestätigen, werden alle eingegebenen Schlüsselfilter entfernt.", + "no-key-filters": "Keine Schlüsselfilter konfiguriert", + "add-key-filter": "Schlüsselfilter hinzufügen", + "remove-key-filter": "Schlüsselfilter entfernen", + "edit-key-filter": "Schlüsselfilter bearbeiten", + "date": "Datum", + "time": "Zeit", + "current-tenant": "Aktueller Mieter", + "current-customer": "Aktueller Kunde", + "current-user": "Aktueller Benutzer", + "current-device": "Aktuelles Gerät", + "default-value": "Standardwert", + "default-comma-separated-values": "Standardwerte, komma-getrennt", + "dynamic-source-type": "Dynamischer Quelltyp", + "dynamic-value": "Dynamischer Wert", + "no-dynamic-value": "Kein dynamischer Wert", + "source-attribute": "Quellattribut", + "switch-to-dynamic-value": "Auf dynamischen Wert umschalten", + "switch-to-default-value": "Auf Standardwert umschalten", + "inherit-owner": "Vom Eigentümer übernehmen", + "source-attribute-not-set": "Falls Quellattribut nicht gesetzt ist" + }, "fullscreen": { "expand": "Auf Vollbildmodus erweitern", "exit": "Vollbildmodus verlassen", @@ -1404,33 +3032,514 @@ "function": { "function": "Funktion" }, + "gateway": { + "gateway-name": "Gateway-Name", + "gateway-name-required": "Gateway-Name ist erforderlich.", + "gateways": "Gateways", + "create-new-gateway": "Neues Gateway erstellen", + "create-new-gateway-text": "Sind Sie sicher, dass Sie ein neues Gateway mit dem Namen '{{gatewayName}}' erstellen möchten?", + "launch-command": "Startbefehl", + "no-gateway-found": "Kein Gateway gefunden.", + "no-gateway-matching": "'{{item}}' nicht gefunden." + }, "grid": { - "delete-item-title": "Möchten Sie dieses Element wirklich löschen?", - "delete-item-text": "Vorsicht, nach Bestätigung wird das Element und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-items-title": "Sind Sie sicher, dass Sie löschen möchten { count, plural, =1 {Symbol} other {Symbole} }?", - "delete-items-action-title": "Löschen { count, plural, =1 {Symbol} other {# Symbole} }", - "delete-items-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Elemente entfernt und alle zugehörigen Daten nicht wiederhergestellt.", + "delete-item-title": "Sind Sie sicher, dass Sie dieses Element löschen möchten?", + "delete-item-text": "Seien Sie vorsichtig, nach der Bestätigung werden dieses Element und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-items-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Element} other {# Elemente} } löschen möchten?", + "delete-items-action-title": "{ count, plural, =1 {1 Element} other {# Elemente} } löschen", + "delete-items-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Elemente und alle zugehörigen Daten entfernt.", "add-item-text": "Neues Element hinzufügen", "no-items-text": "Keine Elemente gefunden", "item-details": "Elementdetails", "delete-item": "Element löschen", "delete-items": "Elemente löschen", - "scroll-to-top": "zum Seitenanfang" + "scroll-to-top": "Nach oben scrollen" }, "help": { - "goto-help-page": "Gehen Sie zur Hilfeseite" + "goto-help-page": "Zur Hilfeseite gehen", + "show-help": "Hilfe anzeigen" }, "home": { "home": "Startseite", "profile": "Profil", "logout": "Abmelden", "menu": "Menü", - "avatar": "Benutzerbild", + "avatar": "Avatar", "open-user-menu": "Benutzermenü öffnen" }, + "file-input": { + "browse-file": "Datei durchsuchen", + "browse-files": "Dateien durchsuchen" + }, + "image": { + "gallery": "Bildergalerie", + "search": "Bild suchen", + "selected-images": "{ count, plural, =1 {1 Bild} other {# Bilder} } ausgewählt", + "created-time": "Erstellungszeit", + "name": "Name", + "name-required": "Name ist erforderlich.", + "resolution": "Auflösung", + "size": "Größe", + "system": "System", + "download-image": "Bild herunterladen", + "export-image": "Bild als JSON exportieren", + "import-image": "Bild aus JSON importieren", + "upload-image": "Bild hochladen", + "edit-image": "Bild bearbeiten", + "image-details": "Bilddetails", + "no-images": "Keine Bilder gefunden", + "delete-image": "Bild löschen", + "delete-image-title": "Sind Sie sicher, dass Sie das Bild '{{imageTitle}}' löschen möchten?", + "delete-image-text": "Seien Sie vorsichtig, nach der Bestätigung kann das Bild nicht wiederhergestellt werden.", + "delete-images-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Bild} other {# Bilder} } löschen möchten?", + "delete-images-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Bilder und deren zugehörige Daten gelöscht.", + "list-mode": "Listenansicht", + "grid-mode": "Rasteransicht", + "image-preview": "Bildvorschau", + "update-image": "Bild aktualisieren", + "export-failed-error": "Bild konnte nicht exportiert werden: {{error}}", + "image-json-file": "Bild-JSON-Datei", + "invalid-image-json-file-error": "Bildimport aus JSON fehlgeschlagen: Ungültige Bild-JSON-Struktur.", + "image-is-in-use": "Bild wird von anderen Entitäten verwendet", + "images-are-in-use": "Bilder werden von anderen Entitäten verwendet", + "image-is-in-use-text": "Das Bild '{{title}}' wurde nicht gelöscht, da es von den folgenden Entitäten verwendet wird:", + "images-are-in-use-text": "Nicht alle Bilder wurden gelöscht, da sie von anderen Entitäten verwendet werden.
Sie können die referenzierten Entitäten anzeigen, indem Sie auf die Schaltfläche Referenzen in der entsprechenden Zeile klicken.
Wenn Sie diese Bilder dennoch löschen möchten, wählen Sie sie unten aus und klicken Sie auf die Schaltfläche Ausgewählte löschen.", + "delete-image-in-use-text": "Wenn Sie das Bild trotzdem löschen möchten, klicken Sie auf die Schaltfläche Trotzdem löschen.", + "system-entities": "Systementitäten:", + "entities": "Entitäten:", + "references": "Referenzen", + "include-system-images": "Systembilder einbeziehen", + "clear-image": "Bild entfernen", + "no-image": "Kein Bild", + "no-image-selected": "Kein Bild ausgewählt", + "browse-from-gallery": "Aus Galerie auswählen", + "set-link": "Link festlegen", + "image-link": "Bildlink", + "link": "Link", + "copy-image-link": "Bildlink kopieren", + "embed-image": "Bild einbetten", + "embed-to-html": "In HTML einbetten", + "embed-to-html-hint": "Diese Funktion macht den Link für jeden unautorisierten Benutzer verfügbar.", + "embed-to-html-text": "Verwenden Sie den folgenden Code-Schnipsel, um ein Bild in komponentenbasierte HTMLs einzubetten.
Solche Komponenten umfassen HTML-Card-Widgets, Zellinhalt-Funktionen usw.", + "embed-to-angular-template": "In Angular HTML-Template einbetten", + "embed-to-angular-template-text": "Verwenden Sie den folgenden Code-Schnipsel, um ein Bild in das Angular HTML-Template einzubetten.
Solche Komponenten umfassen das Markdown-Widget, den HTML-Bereich im Widget-Editor, benutzerdefinierte Aktionen usw." + }, + "image-input": { + "drop-images-or": "Ziehen Sie Bilder hierher oder", + "drag-and-drop": "Drag & Drop", + "or": "oder", + "browse": "Durchsuchen", + "no-images": "Keine Bilder ausgewählt", + "images": "Bilder" + }, "import": { "no-file": "Keine Datei ausgewählt", - "drop-file": "Legen Sie eine JSON-Datei ab oder wählen Sie eine Datei zum hochladen aus." + "drop-file": "JSON-Datei ablegen oder klicken, um eine Datei auszuwählen.", + "drop-json-file-or": "Ziehen Sie eine JSON-Datei hierher oder", + "drop-file-csv": "CSV-Datei ablegen oder klicken, um eine Datei auszuwählen.", + "drop-file-csv-or": "Ziehen Sie eine CSV-Datei hierher oder", + "column-value": "Wert", + "column-title": "Titel", + "column-example": "Beispielwert-Daten", + "column-key": "Attribut-/Telemetrieschlüssel", + "credentials": "Zugangsdaten", + "csv-delimiter": "CSV-Trennzeichen", + "csv-first-line-header": "Erste Zeile enthält Spaltennamen", + "csv-update-data": "Attribute/Telemetrie aktualisieren", + "details": "Details", + "import-csv-number-columns-error": "Eine Datei sollte mindestens zwei Spalten enthalten", + "import-csv-invalid-format-error": "Ungültiges Dateiformat. Zeile: '{{line}}'", + "column-type": { + "name": "Name", + "type": "Typ", + "label": "Bezeichnung", + "column-type": "Spaltentyp", + "client-attribute": "Client-Attribut", + "shared-attribute": "Geteiltes Attribut", + "server-attribute": "Server-Attribut", + "timeseries": "Zeitreihe", + "entity-field": "Entitätsfeld", + "access-token": "Zugangstoken", + "x509": "X.509", + "mqtt": { + "client-id": "MQTT-Client-ID", + "user-name": "MQTT-Benutzername", + "password": "MQTT-Passwort" + }, + "lwm2m": { + "client-endpoint": "LwM2M-Endpunkt-Clientname", + "security-config-mode": "LwM2M-Sicherheitskonfigurationsmodus", + "client-identity": "LwM2M-Client-Identität", + "client-key": "LwM2M-Client-Schlüssel", + "client-cert": "LwM2M-Client-öffentlicher Schlüssel", + "bootstrap-server-security-mode": "LwM2M-Bootstrap-Server-Sicherheitsmodus", + "bootstrap-server-secret-key": "LwM2M-Bootstrap-Server-Geheimschlüssel", + "bootstrap-server-public-key-id": "LwM2M-Bootstrap-Server-öffentlicher Schlüssel oder ID", + "lwm2m-server-security-mode": "LwM2M-Server-Sicherheitsmodus", + "lwm2m-server-secret-key": "LwM2M-Server-Geheimschlüssel", + "lwm2m-server-public-key-id": "LwM2M-Server-öffentlicher Schlüssel oder ID" + }, + "snmp": { + "host": "SNMP-Host", + "port": "SNMP-Port", + "version": "SNMP-Version (v1, v2c oder v3)", + "community-string": "SNMP-Community-String" + }, + "isgateway": "Ist Gateway", + "activity-time-from-gateway-device": "Aktivitätszeit vom Gateway-Gerät", + "description": "Beschreibung", + "routing-key": "Edge-Schlüssel", + "secret": "Edge-Geheimnis" + }, + "stepper-text": { + "select-file": "Datei auswählen", + "configuration": "Konfiguration importieren", + "column-type": "Spaltentyp auswählen", + "creat-entities": "Neue Entitäten erstellen" + }, + "message": { + "create-entities": "{{count}} neue Entitäten wurden erfolgreich erstellt.", + "update-entities": "{{count}} Entitäten wurden erfolgreich aktualisiert.", + "error-entities": "Beim Erstellen von {{count}} Entitäten ist ein Fehler aufgetreten." + } + }, + "scada": { + "symbols": "SCADA-Symbole", + "search": "Symbol suchen", + "selected-symbols": "{ count, plural, =1 {1 Symbol} other {# Symbole} } ausgewählt", + "download-symbol": "SCADA-Symbol herunterladen", + "export-symbol": "SCADA-Symbol als JSON exportieren", + "import-symbol": "SCADA-Symbol aus JSON importieren", + "upload-symbol": "SCADA-Symbol hochladen", + "update-symbol": "SCADA-Symbol aktualisieren", + "edit-symbol": "SCADA-Symbol bearbeiten", + "symbol-details": "SCADA-Symboldetails", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "Keine Symbole gefunden", + "show-hidden-elements": "Versteckte Elemente anzeigen", + "hide-hidden-elements": "Versteckte Elemente ausblenden", + "delete-symbol": "SCADA-Symbol löschen", + "delete-symbol-title": "Möchten Sie das SCADA-Symbol '{{imageTitle}}' wirklich löschen?", + "delete-symbol-text": "Vorsicht, nach der Bestätigung ist das SCADA-Symbol nicht wiederherstellbar.", + "delete-symbols-title": "Möchten Sie wirklich { count, plural, =1 {1 SCADA-Symbol} other {# SCADA-Symbole} } löschen?", + "delete-symbols-text": "Vorsicht, nach der Bestätigung werden alle ausgewählten SCADA-Symbole gelöscht und alle zugehörigen Daten sind nicht wiederherstellbar.", + "include-system-symbols": "Systemsymbole einbeziehen", + "symbol-preview": "Symbolvorschau", + "general": "Allgemein", + "tags": "Tags", + "properties": "Eigenschaften", + "title": "Titel", + "description": "Beschreibung", + "search-tags": "Tags durchsuchen", + "widget-size": "Widget-Größe", + "cols": "Spalten", + "rows": "Zeilen", + "state-render-function": "Status-Render-Funktion", + "preview": "Vorschau", + "preview-widget-action-text": "Widget-Aktion '{{type}}' erfolgreich ausgeführt!", + "no-symbol": "Kein SCADA-Symbol", + "no-symbol-selected": "Kein SCADA-Symbol ausgewählt", + "clear-symbol": "SCADA-Symbol löschen", + "browse-symbol-from-gallery": "SCADA-Symbol aus Galerie auswählen", + "zoom-in": "Vergrößern", + "zoom-out": "Verkleinern", + "create-widget": "Widget erstellen", + "create-widget-from-symbol": "Widget aus SCADA-Symbol erstellen", + "hidden": "versteckt", + "tag": { + "tag": "Tag", + "on-click-action": "Aktion bei Klick", + "no-tags": "Keine Tags konfiguriert", + "delete-tag-text": "Möchten Sie wirklich den Tag
{{tag}} vom {{elementType}}-Element löschen?", + "update-tag": "Tag aktualisieren", + "enter-tag": "Tag eingeben", + "tag-settings": "Tag-Einstellungen", + "remove-tag": "Tag entfernen", + "add-tag": "Tag hinzufügen" + }, + "behavior": { + "behavior": "Verhalten", + "id": "ID", + "name": "Name", + "type": "Typ", + "no-behaviors": "Keine Verhaltensweisen konfiguriert", + "add-behavior": "Verhalten hinzufügen", + "type-action": "Aktion", + "type-value": "Wert", + "type-widget-action": "Widget-Aktion", + "behavior-settings": "Verhaltenseinstellungen", + "remove-behavior": "Verhalten entfernen", + "hint": "Hinweis", + "group-title": "Gruppentitel", + "value-type": "Wertetyp", + "default-value": "Standardwert", + "true-label": "Bezeichnung für Wahr", + "false-label": "Bezeichnung für Falsch", + "state-label": "Statusbezeichnung", + "default-payload": "Standard-Nutzlast", + "not-unique-behavior-ids-error": "Verhaltens-IDs müssen eindeutig sein!", + "default-settings": "Standardeinstellungen" + }, + "symbol": { + "symbol": "SCADA-Symbol", + "fluid-presence": "Flüssigkeitspräsenz", + "fluid-presence-hint": "Gibt an, ob Flüssigkeit im Rohr vorhanden ist.", + "fluid-present": "Flüssigkeit vorhanden", + "present": "Vorhanden", + "absent": "Nicht vorhanden", + "flow-presence": "Flusspräsenz", + "flow-presence-hint": "Gibt an, ob Flüssigkeit im Rohr fließt.", + "flow-present": "Fluss vorhanden", + "flow-direction": "Flussrichtung", + "flow-direction-hint": "Gibt die Flussrichtung der Flüssigkeit an.", + "forward": "Vorwärts", + "reverse": "Rückwärts", + "flow-animation-speed": "Flussanimationsgeschwindigkeit", + "flow-animation-speed-hint": "Gleitkommazahl, die die Animationsgeschwindigkeit des Flusses angibt. 1 - normale Geschwindigkeit, 0 - keine Animation, < 1 - langsamere Animation, > 1 - schnellere Animation.", + "leak": "Leck", + "leak-hint": "Gibt an, ob ein Leck im Rohr vorhanden ist.", + "leak-present": "Leck vorhanden", + "fluid-color": "Flüssigkeitsfarbe", + "pipe-color": "Rohrfarbe", + "horizontal-pipe": "Horizontales Rohr", + "vertical-pipe": "Vertikales Rohr", + "horizontal-fluid-color": "Horizontale Flüssigkeitsfarbe", + "vertical-fluid-color": "Vertikale Flüssigkeitsfarbe", + "left-pipe": "Linkes Rohr", + "right-pipe": "Rechtes Rohr", + "top-pipe": "Oberes Rohr", + "bottom-pipe": "Unteres Rohr", + "left-fluid-color": "Linke Flüssigkeitsfarbe", + "right-fluid-color": "Rechte Flüssigkeitsfarbe", + "top-fluid-color": "Obere Flüssigkeitsfarbe", + "bottom-fluid-color": "Untere Flüssigkeitsfarbe", + "display": "Anzeige", + "display-format": "Anzeigeformat", + "value": "Wert", + "decimals": "Dezimalstellen", + "units": "Einheiten", + "flow-meter-value-hint": "Gleitkommazahl auf der Durchflussanzeige", + "value-hint": "Gleitkommazahl, die den aktuellen Wert anzeigt", + "running": "In Betrieb", + "running-hint": "Gibt an, ob die Komponente im Betriebszustand ist.", + "warning-state": "Warnzustand", + "warning": "Warnung", + "warning-click": "Warnung Klick", + "warning-state-hint": "Gibt an, ob die Komponente im Warnzustand ist.", + "critical-state": "Kritischer Zustand", + "critical": "Kritisch", + "critical-click": "Kritisch Klick", + "critical-state-hint": "Gibt an, ob die Komponente im kritischen Zustand ist.", + "critical-state-animation": "Animation im kritischen Zustand", + "critical-state-animation-hint": "Ob die blinkende Animation im kritischen Zustand aktiviert werden soll.", + "warning-critical-state-animation": "Animation im Warn-/Kritischen Zustand", + "warning-critical-state-animation-hint": "Ob die blinkende Animation im Warn- oder kritischen Zustand aktiviert werden soll.", + "animation": "Animation", + "broken": "Defekt", + "broken-hint": "Gibt an, ob die Komponente defekt ist.", + "on-display-click": "Bei Klick auf Anzeige", + "on-display-click-hint": "Aktion, die beim Klicken auf die Anzeige ausgelöst wird.", + "pipe": "Rohr", + "default-border-color": "Standard-Rahmenfarbe", + "active-border-color": "Aktive Rahmenfarbe", + "warning-border-color": "Warnrahmenfarbe", + "critical-border-color": "Kritische Rahmenfarbe", + "background-color": "Hintergrundfarbe", + "rotation-animation-speed": "Rotationsanimationsgeschwindigkeit", + "rotation-animation-speed-hint": "Gleitkommazahl zur Angabe der Rotationsanimationsgeschwindigkeit. 1 - normale Geschwindigkeit, 0 - keine Animation, < 1 - langsamere Animation, > 1 - schnellere Animation.", + "on-click": "Bei Klick", + "on-click-hint": "Aktion, die beim Klicken auf die Komponente ausgeführt wird.", + "connectors-positions": "Positionen der Anschlüsse", + "right-connector": "Rechter Anschluss", + "right-top-connector": "Rechter oberer Anschluss", + "right-bottom-connector": "Rechter unterer Anschluss", + "left-connector": "Linker Anschluss", + "left-top-connector": "Linker oberer Anschluss", + "left-bottom-connector": "Linker unterer Anschluss", + "top-left-connector": "Oberer linker Anschluss", + "top-right-connector": "Oberer rechter Anschluss", + "top-connector": "Oberer Anschluss", + "bottom-connector": "Unterer Anschluss", + "running-color": "Farbe im Betriebszustand", + "stopped-color": "Farbe im Stoppzustand", + "stopped": "Gestoppt", + "warning-color": "Warnfarbe", + "critical-color": "Kritische Farbe", + "opened": "Geöffnet", + "opened-hint": "Gibt an, ob sich die Komponente im geöffneten Zustand befindet.", + "open": "Öffnen", + "open-hint": "Aktion, die beim Öffnen der Komponente ausgeführt wird.", + "close": "Schließen", + "close-hint": "Aktion, die beim Schließen der Komponente ausgeführt wird.", + "close-state-animation": "Animation im geschlossenen Zustand", + "close-state-animation-hint": "Ob eine Blinkanimation im geschlossenen Zustand aktiviert werden soll.", + "opened-color": "Geöffnete Farbe", + "closed-color": "Geschlossene Farbe", + "opened-rotation-angle": "Drehwinkel geöffnet", + "closed-rotation-angle": "Drehwinkel geschlossen", + "tank-capacity": "Tankkapazität", + "tank-capacity-hint": "Gleitkommazahl zur Angabe der Gesamtkapazität des Tanks.", + "current-volume": "Aktuelles Volumen", + "current-volume-hint": "Gleitkommazahl zur Angabe des aktuell belegten Volumens.", + "tank-color": "Tankfarbe", + "value-box": "Wertbox", + "value-text": "Wertetext", + "scale": "Skala", + "transparent-mode": "Transparenter Modus", + "major-ticks": "Hauptmarkierungen", + "intervals": "Intervalle", + "major-ticks-color": "Farbe der Hauptmarkierungen", + "normal": "Normal", + "minor-ticks": "Nebenmarkierungen", + "minor-ticks-color": "Farbe der Nebenmarkierungen", + "temperature": "Temperatur", + "temperature-hint": "Gleitkommazahl zur Anzeige der aktuellen Temperatur.", + "update-temperature": "Temperatur aktualisieren", + "update-temperature-hint": "Aktion, die beim Klicken zur Änderung der Temperatur ausgeführt wird.", + "run": "Starten", + "run-hint": "Aktion, die beim Starten der Komponente ausgeführt wird.", + "stop": "Stoppen", + "stop-hint": "Aktion, die beim Stoppen der Komponente ausgeführt wird.", + "temperature-step": "Temperaturschritt", + "heat-pump-color": "Farbe der Wärmepumpe", + "power-button-background": "Hintergrund des Netzschalters", + "value-box-background": "Hintergrund der Wertbox", + "value-units": "Werteinheiten", + "filtration-mode": "Filtrationsmodus", + "filtration-mode-hint": "Ganzzahlige Angabe des aktuellen Filtrationsmodus.", + "filtration-mode-update": "Filtrationsmodus-Aktualisierung", + "filtration-mode-update-hint": "Aktion, die beim Ändern des aktuellen Filtrationsmodus ausgeführt wird.", + "filter-mode": "Filtern", + "waste-mode": "Abfall", + "backwash-mode": "Rückspülen", + "recirculate-mode": "Rezirkulieren", + "rinse-mode": "Spülen", + "closed-mode": "Geschlossen", + "sand-filter-color": "Sandfilter-Farbe", + "mode-box-background": "Modus-Box-Hintergrund", + "border-color": "Rahmenfarbe", + "label-color": "Beschriftungsfarbe", + "water-leak-hint": "Gibt an, ob ein Leck vorliegt.", + "default-color": "Standardfarbe", + "leak-color": "Leck-Farbe", + "full-value": "Vollwert", + "full-value-hint": "Gleitkommazahl, die den Vollwert angibt.", + "label": "Beschriftung", + "icon": "Symbol", + "button-color": "Schaltflächenfarbe", + "on-label": "Text für 'Ein'", + "off-label": "Text für 'Aus'", + "arrow-presence": "Pfeil vorhanden", + "arrow-presence-hint": "Gibt an, ob ein Pfeil im Anschluss vorhanden ist.", + "arrow-present": "Pfeil vorhanden", + "arrow-direction": "Pfeil-/Animationsrichtung", + "arrow-direction-hint": "Gibt die Flussrichtung an.", + "flow-animation": "Flussanimation", + "flow-animation-hint": "Gibt an, ob eine Animation im Anschluss vorhanden ist.", + "flow": "Fluss", + "flow-line": "Linie", + "flow-line-style": "Linienstil", + "flow-style-hint": "Setzen Sie die Werte für Strich und Lücke so, dass ihre Summe ohne Rest durch 100 teilbar ist, um eine perfekte Animationssynchronisation zu erreichen.", + "flow-dash-cap": "Fluss-Strich-Endung", + "dash-cap-butt": "Abgeschnitten", + "dash-cap-round": "Rund", + "dash-cap-square": "Quadratisch", + "dash": "Strich", + "gap": "Abstand", + "main-line": "Hauptlinie", + "line": "Linie", + "line-color": "Linienfarbe", + "arrow-color": "Pfeilfarbe", + "target-value": "Zielwert", + "target-value-hint": "Gibt den Zielpunkt auf der Skala an.", + "min-max-value": "Min- und Max-Wert", + "min-value": "Min", + "max-value": "Max", + "progress-bar": "Fortschrittsbalken", + "progress-arrow": "Fortschrittspfeil", + "warning-scale-color": "Warnskalenfarbe", + "critical-scale-color": "Kritische Skalenfarbe", + "scale-color": "Skalenfarbe", + "target": "Ziel", + "high-warning-state": "Zustand hohe Warnung", + "show-high-warning-scale": "Hohe Warnskala anzeigen", + "high-warning-scale": "Hohe Warnskala", + "high-warning-state-hint": "Gleitkommazahl gibt den Bereich der hohen Warnung bis zu kritischem oder Max-Wert an.", + "low-warning-state": "Zustand niedrige Warnung", + "show-low-warning-scale": "Niedrige Warnskala anzeigen", + "low-warning-scale": "Niedrige Warnskala", + "low-warning-state-hint": "Gleitkommazahl gibt den Bereich der niedrigen Warnung bis zu kritischem oder Min-Wert an.", + "high-critical-state": "Zustand hohe Kritikalität", + "show-high-critical-scale": "Hohe kritische Skala anzeigen", + "high-critical-scale": "Hohe kritische Skala", + "high-critical-state-hint": "Gleitkommazahl gibt den kritischen Bereich bis zum Max-Wert an.", + "low-critical-state": "Zustand niedrige Kritikalität", + "show-low-critical-scale": "Niedrige kritische Skala anzeigen", + "low-critical-scale": "Niedrige kritische Skala", + "low-critical-state-hint": "Gleitkommazahl gibt den kritischen Bereich bis zum Min-Wert an.", + "filter-color": "Filterfarbe", + "colors": "Farben", + "indicator-colors": "Indikatorfarben", + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "on": "EIN", + "off": "AUS", + "on-off-state": "Ein-/Aus-Zustand", + "on-off-state-hint": "Gibt an, ob die Komponente im Ein- oder Aus-Zustand ist.", + "on-update-state": "Zustand zu Ein aktualisieren", + "on-update-state-hint": "Aktion, die ausgeführt wird, wenn der Benutzer den Zustand auf Ein aktualisiert.", + "off-update-state": "Zustand zu Aus aktualisieren", + "off-update-state-hint": "Aktion, die ausgeführt wird, wenn der Benutzer den Zustand auf Aus aktualisiert.", + "voltage": "Spannung", + "input-voltage": "Eingangsspannung", + "input-voltage-hint": "Gleitkommazahl gibt den Wert der Eingangsspannung an.", + "output-voltage": "Ausgangsspannung", + "output-voltage-hint": "Gleitkommazahl gibt den Wert der Ausgangsspannung an.", + "first-phase-voltage": "Spannung der ersten Phase", + "second-phase-voltage": "Spannung der zweiten Phase", + "third-phase-voltage": "Spannung der dritten Phase", + "phase-voltage-hint": "Gleitkommazahl gibt den Spannungswert der aktuellen Phase an", + "voltage-hint": "Gleitkommazahl gibt die aktuelle Spannung an", + "current-voltage-color": "Farbe der aktuellen Spannung", + "phase-indicator-color": "Phasenanzeigerfarbe", + "measured": "Gemessen", + "measured-hint": "Gleitkommazahl zeigt den Energieverbrauch in Kilowattstunden an", + "day-rate": "Tagestarif", + "night-rate": "Nachttarif", + "off-peak-rate": "Niedrigtarif", + "peak-rate": "Spitzentarif", + "export-rate": "Exporttarif", + "operating-mode": "Betriebsmodus", + "bypass-mode": "Bypass", + "operating-mode-hint": "Ganzzahliger Wert gibt den aktuellen Betriebsmodus an (0 - AUS, 1 - EIN, 2 - BYPASS)", + "connected": "Verbunden", + "connected-hint": "Gibt an, ob sich die Komponente im verbundenen Zustand befindet.", + "disconnected": "Getrennt", + "indicator": "Indikator", + "operation-mode": "Betriebsmodus", + "operation-mode-hint": "Gibt an, ob sich der Wechselrichter im Netz- oder Wechselrichtermodus befindet.", + "operation-mode-indicators-color": "Farbe der Betriebsmodus-Indikatoren", + "mains-on-mode": "Netz eingeschaltet", + "inverter-on-mode": "Wechselrichter eingeschaltet", + "charging-mode": "Lademodus", + "charging-mode-hint": "Ganzzahliger Wert gibt den aktuellen Lademodus an (1 - Bulk, 2 - Absorption, 3 - Float)", + "charging-mode-indicators-color": "Farbe der Lademodus-Indikatoren", + "inverter-faults": "Fehler", + "inverter-fault-indicators-color": "Farbe der Fehlerindikatoren", + "overload-fault": "Überlastung", + "overload-fault-hint": "Gibt an, ob sich der Wechselrichter in einem Überlastzustand befindet.", + "low-battery-fault": "Niedriger Batteriestand", + "low-battery-fault-hint": "Gibt an, ob die Batterie stark entladen ist.", + "temperature-fault": "Temperatur", + "temperature-fault-hint": "Gibt an, ob eine hohe Temperatur im Wechselrichter vorliegt.", + "triangle": "Dreieck", + "socket": "Steckdose", + "left-button": "Linke Taste", + "right-button": "Rechte Taste", + "alarm-colors": "Alarmfarben", + "hook-color": "Hakenfarbe" + } }, "item": { "selected": "Ausgewählt" @@ -1438,54 +3547,630 @@ "js-func": { "no-return-error": "Funktion muss einen Wert zurückgeben!", "return-type-mismatch": "Funktion muss einen Wert vom Typ '{{type}}' zurückgeben!", - "tidy": "Aufräumen" + "tidy": "Ordentlich", + "mini": "Mini", + "modules": "Module", + "remove-module": "Modul entfernen", + "no-modules": "Keine Module konfiguriert", + "add-module": "Modul hinzufügen", + "module-alias": "Alias", + "invalid-module-alias-name": "Ungültiger Alias-Name", + "module-resource": "JS-Modulressource", + "not-unique-module-aliases-error": "Modul-Aliase müssen eindeutig sein!", + "show-module-info": "Modulinformationen anzeigen", + "show-module-source-code": "Modul-Quellcode anzeigen", + "module-members": "Modulmitglieder", + "module-no-members": "Modul hat keine exportierten Mitglieder", + "module-load-error": "Fehler beim Laden des Moduls", + "source-code": "Quellcode", + "source-code-load-error": "Fehler beim Laden des Quellcodes", + "no-js-module-text": "Keine JS-Module gefunden", + "no-js-module-matching": "Keine JS-Module gefunden, die mit '{{module}}' übereinstimmen." }, "key-val": { "key": "Schlüssel", "value": "Wert", "remove-entry": "Eintrag entfernen", - "add-entry": "Eintag hinzufügen", + "add-entry": "Eintrag hinzufügen", "no-data": "Keine Einträge" }, "layout": { "layout": "Layout", + "layouts": "Layouts", "manage": "Layouts verwalten", - "settings": "Layout-Einstellungen", + "settings": "Layouteinstellungen", "color": "Farbe", "main": "Hauptbereich", - "right": "Recht", - "select": "Wählen Sie das Ziellayout aus" + "right": "Rechts", + "left": "Links", + "select": "Ziel-Layout auswählen", + "percentage-width": "Prozentuale Breite (%)", + "fixed-width": "Feste Breite (px)", + "left-width": "Linke Spalte (%)", + "right-width": "Rechte Spalte (%)", + "pick-fixed-side": "Feste Seite:", + "layout-fixed-width": "Feste Breite (px)", + "value-min-error": "Wert muss größer als {{min}}{{unit}} sein", + "value-max-error": "Wert muss kleiner als {{max}}{{unit}} sein", + "layout-fixed-width-required": "Feste Breite ist erforderlich", + "right-width-percentage-required": "Prozentangabe für rechte Seite ist erforderlich", + "left-width-percentage-required": "Prozentangabe für linke Seite ist erforderlich", + "divider": "Trennlinie", + "right-side": "Layout rechte Seite", + "left-side": "Layout linke Seite", + "add-new-breakpoint": "Neuen Breakpoint hinzufügen", + "breakpoint": "Breakpoint", + "breakpoints": "Breakpoints", + "copy-from": "Kopieren von", + "size": "Größe", + "delete-breakpoint-title": "Möchten Sie den Breakpoint '{{name}}' wirklich löschen?", + "delete-breakpoint-text": "Bitte beachten Sie: Nach der Bestätigung ist der Breakpoint nicht wiederherstellbar, und die Einstellungen werden auf den Standard-Breakpoint zurückgesetzt." }, "legend": { - "position": "Legendenposition", + "direction": "Richtung", + "position": "Position", + "show-values": "Werte anzeigen", + "min-option": "Min", + "max-option": "Max", + "average-option": "Durchschnitt", + "total-option": "Gesamt", + "latest-option": "Letzter", + "sort-legend": "Daten in Legende sortieren", "show-max": "Maximalwert anzeigen", "show-min": "Minimalwert anzeigen", - "show-avg": "Durchschnittswert anzeigen", - "show-total": "Gesamtwert anzeigen", + "show-avg": "Durchschnitt anzeigen", + "show-total": "Gesamt anzeigen", + "show-latest": "Letzten Wert anzeigen", "settings": "Legendeneinstellungen", - "min": "min.", - "max": "max.", - "avg": "mittelw.", - "total": "Gesamt" + "min": "min", + "max": "max", + "avg": "avg", + "total": "gesamt", + "latest": "letzter", + "Min": "Min", + "Max": "Max", + "Avg": "Durchschn.", + "Total": "Gesamt", + "Latest": "Letzter", + "comparison-time-ago": { + "previousInterval": "(vorheriges Intervall)", + "customInterval": "(benutzerdefiniertes Intervall)", + "days": "(vor einem Tag)", + "weeks": "(vor einer Woche)", + "months": "(vor einem Monat)", + "years": "(vor einem Jahr)" + }, + "column-title": "Spaltentitel", + "label": "Bezeichnung", + "value": "Wert" }, "login": { "login": "Anmelden", - "request-password-reset": "Passwortzurücksetzung anfordern", + "request-password-reset": "Passwort-Zurücksetzung anfordern", "reset-password": "Passwort zurücksetzen", "create-password": "Passwort erstellen", - "passwords-mismatch-error": "Eingegebene Passwörter müssen identisch sein!", + "two-factor-authentication": "Zwei-Faktor-Authentifizierung", + "passwords-mismatch-error": "Die eingegebenen Passwörter müssen übereinstimmen!", "password-again": "Passwort wiederholen", "sign-in": "Bitte anmelden", "username": "Benutzername (E-Mail)", - "remember-me": "Login speichern", + "remember-me": "Angemeldet bleiben", "forgot-password": "Passwort vergessen?", "password-reset": "Passwort zurücksetzen", + "expired-password-reset-message": "Ihre Anmeldedaten sind abgelaufen! Bitte erstellen Sie ein neues Passwort.", "new-password": "Neues Passwort", - "new-password-again": "Neues Passwort wiederholen", - "password-link-sent-message": "Der Link zum Zurücksetzen des Passworts wurde erfolgreich versendet!", + "new-password-again": "Neues Passwort bestätigen", + "password-link-sent-message": "Link zum Zurücksetzen wurde gesendet", "email": "E-Mail", - "login-with": "Mit {{name}} anmelden", - "or": "oder" + "invalid-email-format": "Ungültiges E-Mail-Format.", + "login-with": "Anmelden mit {{name}}", + "or": "oder", + "error": "Anmeldefehler", + "verify-your-identity": "Identität bestätigen", + "select-way-to-verify": "Wählen Sie eine Methode zur Verifizierung", + "resend-code": "Code erneut senden", + "resend-code-wait": "Code erneut senden in { time, plural, =1 {1 Sekunde} other {# Sekunden} }", + "try-another-way": "Andere Methode versuchen", + "totp-auth-description": "Bitte geben Sie den Sicherheitscode aus Ihrer Authentifizierungs-App ein.", + "totp-auth-placeholder": "Code", + "sms-auth-description": "Ein Sicherheitscode wurde an Ihr Telefon unter {{contact}} gesendet.", + "sms-auth-placeholder": "SMS-Code", + "email-auth-description": "Ein Sicherheitscode wurde an Ihre E-Mail-Adresse unter {{contact}} gesendet.", + "email-auth-placeholder": "E-Mail-Code", + "backup-code-auth-description": "Bitte geben Sie einen Ihrer Backup-Codes ein.", + "backup-code-auth-placeholder": "Backup-Code", + "activation-link-expired": "Aktivierungslink ist abgelaufen", + "activation-link-expired-message": "Der Link zur Aktivierung Ihres Profils ist abgelaufen. Sie können zur Anmeldeseite zurückkehren, um eine neue E-Mail zu erhalten.", + "reset-password-link-expired": "Link zum Zurücksetzen des Passworts ist abgelaufen", + "reset-password-link-expired-message": "Der Link zum Zurücksetzen Ihres Passworts ist abgelaufen. Sie können zur Anmeldeseite zurückkehren, um eine neue E-Mail zu erhalten." + }, + "mobile": { + "add-application": "Anwendung hinzufügen", + "app-id": "App-ID", + "app-id-required": "App-ID ist erforderlich", + "app-id-pattern": "Ungültiges App-ID-Format", + "app-store-link": "App Store-Link", + "app-store-link-required": "App Store-Link ist erforderlich", + "application-details": "Anwendungsdetails", + "application-package": "Anwendungspaket", + "application-secret": "Anwendungsgeheimnis", + "application-secret-required": "Anwendungsgeheimnis ist erforderlich", + "application": "Anwendung", + "applications": "Anwendungen", + "copy-app-id": "App-ID kopieren", + "copy-app-store-link": "App Store-Link kopieren", + "copy-application-package": "Anwendungspaket kopieren", + "copy-application-secret": "Anwendungsgeheimnis kopieren", + "copy-google-play-link": "Google Play-Link kopieren", + "copy-sha256-certificate-fingerprints": "SHA256-Zertifikat-Fingerabdrücke kopieren", + "delete-application": "Anwendung löschen", + "delete-application-button-text": "Ich verstehe die Konsequenzen, Anwendung löschen", + "delete-application-text": "Diese Aktion kann nicht rückgängig gemacht werden. Die Anwendung wird dauerhaft gelöscht.
Wenn Sie sie nicht dauerhaft löschen möchten, können Sie die Anwendung vorübergehend aussetzen.
Zum Löschen geben Sie bitte \"{{phrase}}\" zur Bestätigung ein.", + "delete-application-title-short": "Sind Sie sicher, dass Sie die Anwendung '{{name}}' löschen möchten?", + "delete-application-text-short": "Seien Sie vorsichtig, nach der Bestätigung werden die Anwendung und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-application-phrase": "Anwendung löschen", + "delete-applications-bundle-text": "Seien Sie vorsichtig, nach der Bestätigung werden das Mobile-Bundle und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-applications-bundle-title": "Sind Sie sicher, dass Sie das Mobile-Bundle '{{bundleName}}' löschen möchten?", + "generate-application-secret": "Anwendungsgeheimnis generieren", + "google-play-link": "Google Play-Link", + "google-play-link-required": "Google Play-Link ist erforderlich", + "latest-version": "Neueste Version", + "min-version": "Minimale Version", + "invalid-version-pattern": "Ungültiges Versionsformat. Bitte verwenden Sie das Format: major.minor.patch (z. B. 1.0.0).", + "mobile-center": "Mobiles Zentrum", + "mobile-package": "Anwendungspaket", + "mobile-package-max-length": "Anwendungspaket sollte weniger als 256 Zeichen enthalten", + "mobile-package-required": "Anwendungspaket ist erforderlich.", + "mobile-package-pattern": "Ungültiges Format des Anwendungspakets", + "no-application": "Keine Anwendungen gefunden", + "no-bundles": "Keine Bundles gefunden", + "platform-type": "Plattformtyp", + "search-application": "Anwendungen suchen", + "search-bundles": "Bundles suchen", + "set": "Festlegen", + "sha256-certificate-fingerprints": "SHA256-Zertifikat-Fingerabdrücke", + "sha256-certificate-fingerprints-required": "SHA256-Zertifikat-Fingerabdrücke sind erforderlich", + "sha256-certificate-fingerprints-pattern": "Ungültiges SHA256-Zertifikat-Fingerabdruckformat", + "show-hidden-pages": "Versteckte Seiten anzeigen", + "status": "Status", + "status-type": { + "deprecated": "Veraltet", + "draft": "Entwurf", + "published": "Veröffentlicht", + "suspended": "Ausgesetzt" + }, + "store-information": "Shop-Informationen", + "version-information": "Versionsinformationen", + "min-version-release-notes": "Versionshinweise zur Mindestversion", + "latest-version-release-notes": "Versionshinweise zur neuesten Version", + "bundle": "Paket", + "bundles": "Pakete", + "add-bundle": "Paket hinzufügen", + "title": "Titel", + "title-required": "Titel ist erforderlich", + "title-cannot-contain-only-spaces": "Titel darf nicht nur Leerzeichen enthalten", + "title-max-length": "Titel sollte weniger als 256 Zeichen enthalten", + "oauth-clients": "OAuth 2.0 Clients", + "android-app": "Android-App", + "android-application": "Android-Anwendung", + "ios-app": "iOS-App", + "ios-application": "iOS-Anwendung", + "invalid-store-link": "Ungültiger Store-Link", + "enable-oauth": "OAuth 2.0 aktivieren", + "enable-self-registration": "Selbstregistrierung aktivieren", + "edit-bundle": "Bundle bearbeiten", + "description": "Beschreibung", + "basic-settings": "Grundeinstellungen", + "no-application-matching": "Keine Anwendung gefunden, die '{{entity}}' entspricht.", + "no-bundle-matching": "Kein Bundle gefunden, das '{{entity}}' entspricht.", + "application-required": "Anwendung ist erforderlich.", + "bundle-required": "Bundle ist erforderlich.", + "no-application-text": "Keine Anwendungen gefunden", + "no-bundle-text": "Kein Bundle gefunden", + "layout": "Layout", + "pages": "Seiten", + "hide-all-pages": "Alle Seiten ausblenden", + "reset-to-default-pages": "Auf Standardseiten zurücksetzen", + "add-specific-page": "Spezifische Seite hinzufügen", + "visible": "Sichtbar", + "hidden": "Ausgeblendet", + "reset-to-page-default": "Seite auf Standard zurücksetzen", + "mobile-599": "Mobilgerät (max. 599px)", + "tablet-959": "Tablet (max. 959px)", + "max-element-number": "Maximale Anzahl von Elementen", + "page-name": "Seitenname", + "page-name-required": "Seitenname ist erforderlich.", + "page-name-cannot-contain-only-spaces": "Seitenname darf nicht nur Leerzeichen enthalten.", + "page-name-max-length": "Seitenname sollte weniger als 256 Zeichen enthalten", + "page-type": "Seitentyp", + "pages-types": { + "dashboard": "Dashboard", + "web-view": "Webansicht", + "custom": "Benutzerdefiniert" + }, + "url": "URL", + "invalid-url-format": "Ungültiges URL-Format", + "path": "Pfad", + "invalid-path-format": "Ungültiges Pfadformat", + "custom-page": "Benutzerdefinierte Seite", + "edit-page": "Seite bearbeiten", + "edit-custom-page": "Benutzerdefinierte Seite bearbeiten", + "delete-page": "Seite löschen", + "qr-code-widget": "QR-Code-Widget", + "type-here": "Hier eingeben", + "configuration-dialog": "Konfigurationsdialog", + "configuration-app": "Konfigurations-App", + "configuration-step": { + "prepare-environment-title": "Entwicklungsumgebung vorbereiten", + "prepare-environment-text": "Für die ThingsBoard Flutter Mobile App wird das Flutter SDK benötigt. Folgen Sie den Anweisungen zur Einrichtung des Flutter SDK.", + "get-source-code-title": "Quellcode der App erhalten", + "get-source-code-text": "Sie können den Quellcode der ThingsBoard Flutter Mobile App durch Klonen des GitHub-Repositories erhalten:", + "configure-api-title": "ThingsBoard API-Endpunkt konfigurieren", + "configure-api-text": "Öffnen Sie das Projekt 'flutter_thingsboard_pe_app' in Ihrem Editor/IDE. Bearbeiten Sie:", + "configure-api-hint": "Setzen Sie den Wert der Konstante 'thingsBoardApiEndpoint' entsprechend dem API-Endpunkt Ihrer ThingsBoard-Instanz. Verwenden Sie keine Hostnamen wie „localhost“ oder „127.0.0.1“.", + "run-app-title": "App ausführen", + "run-app-text": "Führen Sie die App wie in Ihrer IDE beschrieben aus.\nWenn Sie das Terminal verwenden, führen Sie folgenden Befehl aus:", + "more-information": "Detaillierte Informationen finden Sie in unserer Einstiegsdokumentation.", + "getting-started": "Erste Schritte", + "configure-package-title": "Anwendungspaket konfigurieren", + "configure-package-text": "Sie können das Anwendungspaket manuell ändern oder ein CLI-Tool eines Drittanbieters verwenden.", + "configure-package-text-install": "Um das Rename CLI Tool zu installieren, führen Sie folgenden Befehl aus:", + "configure-package-run-commands": "Führen Sie diese Befehle im Stammverzeichnis Ihres Projekts aus:" + } + }, + "notification": { + "action-button": "Aktionsschaltfläche", + "action-type": "Aktionstyp", + "active": "Aktiv", + "add-notification-recipients-group": "Benachrichtigungs-Empfängergruppe hinzufügen", + "add-notification-template": "Benachrichtigungsvorlage hinzufügen", + "add-recipient": "Empfänger hinzufügen", + "add-recipients": "Empfänger hinzufügen", + "add-rule": "Regel hinzufügen", + "add-stage": "Stufe hinzufügen", + "add-template": "Vorlage hinzufügen", + "after": "Nach", + "alarm-assignment-trigger-settings": "Alarmazuweisung Auslöser-Einstellungen", + "alarm-comment-trigger-settings": "Alarmkommentar Auslöser-Einstellungen", + "alarm-trigger-settings": "Alarm Auslöser-Einstellungen", + "all": "Alle", + "api-feature-hint": "Wenn das Feld leer ist, wird der Auslöser auf alle API-Funktionen angewendet", + "api-usage-trigger-settings": "API-Nutzungs-Auslöser-Einstellungen", + "new-platform-version-trigger-settings": "Neue Plattformversion Auslöser-Einstellungen", + "rate-limits-trigger-settings": "Auslöser für überschrittene Ratenlimits", + "task-processing-failure-trigger-settings": "Aufgabenverarbeitungsfehler Auslöser-Einstellungen", + "at-least-one-should-be-selected": "Mindestens eine Auswahl muss getroffen werden", + "basic-settings": "Grundeinstellungen", + "button-text": "Schaltflächentext", + "button-text-required": "Schaltflächentext ist erforderlich", + "button-text-max-length": "Schaltflächentext darf höchstens {{ length }} Zeichen lang sein", + "compose": "Verfassen", + "conversation": "Konversation", + "conversation-required": "Konversation ist erforderlich", + "copy-notification-template": "Benachrichtigungsvorlage kopieren", + "copy-rule": "Regel kopieren", + "copy-template": "Vorlage kopieren", + "create-new": "Neu erstellen", + "created": "Erstellt", + "customize-messages": "Nachrichten anpassen", + "delete-notification-text": "Seien Sie vorsichtig, nach der Bestätigung ist die Benachrichtigung nicht wiederherstellbar.", + "delete-notification-title": "Sind Sie sicher, dass Sie die Benachrichtigung löschen möchten?", + "delete-notifications-text": "Seien Sie vorsichtig, nach der Bestätigung sind die Benachrichtigungen nicht wiederherstellbar.", + "delete-notifications-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Benachrichtigung} other {# Benachrichtigungen} } löschen möchten?", + "delete-recipient-text": "Seien Sie vorsichtig, nach der Bestätigung ist der Empfänger nicht wiederherstellbar.", + "delete-recipient-title": "Sind Sie sicher, dass Sie den Empfänger '{{recipientName}}' löschen möchten?", + "delete-recipients-text": "Seien Sie vorsichtig, nach der Bestätigung sind die Empfänger nicht wiederherstellbar.", + "delete-recipients-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Empfänger} other {# Empfänger} } löschen möchten?", + "delete-request-text": "Seien Sie vorsichtig, nach der Bestätigung ist die Anfrage nicht wiederherstellbar.", + "delete-request-title": "Sind Sie sicher, dass Sie die Anfrage löschen möchten?", + "delete-requests-text": "Seien Sie vorsichtig, nach der Bestätigung sind die Anfragen nicht wiederherstellbar.", + "delete-requests-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Anfrage} other {# Anfragen} } löschen möchten?", + "delete-rule-text": "Seien Sie vorsichtig, nach der Bestätigung ist die Regel nicht wiederherstellbar.", + "delete-rule-title": "Sind Sie sicher, dass Sie die Regel '{{ruleName}}' löschen möchten?", + "delete-rules-text": "Seien Sie vorsichtig, nach der Bestätigung sind die Regeln nicht wiederherstellbar.", + "delete-rules-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Regel} other {# Regeln} } löschen möchten?", + "delete-template-text": "Seien Sie vorsichtig, nach der Bestätigung ist die Vorlage nicht wiederherstellbar.", + "delete-template-title": "Sind Sie sicher, dass Sie die Vorlage '{{templateName}}' löschen möchten?", + "delete-templates-text": "Seien Sie vorsichtig, nach der Bestätigung sind die Vorlagen nicht wiederherstellbar.", + "delete-templates-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Vorlage} other {# Vorlagen} } löschen möchten?", + "deleted": "Gelöscht", + "delivery-method": { + "delivery-method": "Zustellmethode", + "email": "E-Mail", + "email-preview": "E-Mail-Benachrichtigungsvorschau", + "slack": "Slack", + "slack-preview": "Slack-Benachrichtigungsvorschau", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Microsoft Teams-Benachrichtigungsvorschau", + "sms": "SMS", + "sms-preview": "SMS-Benachrichtigungsvorschau", + "web": "Web", + "web-preview": "Web-Benachrichtigungsvorschau", + "mobile-app": "Mobile App", + "mobile-app-preview": "Mobile App-Benachrichtigungsvorschau" + }, + "delivery-method-not-configure-click": "Liefermethode ist nicht konfiguriert. Klicken Sie, um sie einzurichten.", + "delivery-method-not-configure-contact": "Liefermethode ist nicht konfiguriert. Wenden Sie sich an Ihren Systemadministrator.", + "delivery-methods": "Liefermöglichkeiten", + "description": "Beschreibung", + "device-activity-trigger-settings": "Einstellungen für Geräteaktivitätsauslöser", + "device-list-rule-hint": "Wenn das Feld leer ist, wird der Auslöser auf alle Geräte angewendet", + "device-profiles-list-rule-hint": "Wenn das Feld leer ist, wird der Auslöser auf alle Geräteprofile angewendet", + "disabled": "Deaktiviert", + "edge-trigger-settings": "Einstellungen für Edge-Auslöser", + "edge-list-rule-hint": "Wenn das Feld leer ist, wird der Auslöser auf alle Edge-Instanzen angewendet", + "edit-notification-recipients-group": "Empfängergruppe der Benachrichtigungen bearbeiten", + "edit-notification-template": "Benachrichtigungsvorlage bearbeiten", + "edit-rule": "Regel bearbeiten", + "edit-template": "Vorlage bearbeiten", + "enabled": "Aktiviert", + "entities-limit-trigger-settings": "Einstellungen für Entitäten-Limit-Auslöser", + "entity-action-trigger-settings": "Einstellungen für Entitätsaktionsauslöser", + "entity-type": "Entitätstyp", + "escalation-chain": "Eskalationskette", + "failed-send": "Sendeausfälle", + "fails": "{ count, plural, =1 {1 Fehler} other {# Fehler} }", + "filter": "Filter", + "first-recipient": "Erster Empfänger", + "inactive": "Inaktiv", + "inbox": "Posteingang", + "notification-inbox": "Benachrichtigungen / Posteingang", + "input-field-support-templatization": "Eingabefeld unterstützt Templatisierung.", + "input-fields-support-templatization": "Eingabefelder unterstützen Templatisierung.", + "link": "Link", + "link-required": "Link ist erforderlich", + "link-type": { + "dashboard": "Dashboard öffnen", + "link": "URL-Link öffnen" + }, + "loading-notifications": "Benachrichtigungen werden geladen...", + "management": "Benachrichtigungsverwaltung", + "mark-all-as-read": "Alle als gelesen markieren", + "mark-as-read": "Als gelesen markieren", + "message": "Nachricht", + "message-required": "Nachricht ist erforderlich", + "message-max-length": "Nachricht sollte höchstens {{ length }} Zeichen lang sein", + "name": "Name", + "name-required": "Name ist erforderlich", + "new-notification": "Neue Benachrichtigung", + "no-inbox-notification": "Keine Benachrichtigung gefunden", + "no-notification-request": "Keine Benachrichtigungsanfrage", + "no-notification-templates": "Keine Benachrichtigungsvorlagen gefunden", + "no-notifications-yet": "Noch keine Benachrichtigungen", + "no-recipients-notification": "Keine Empfängerbenachrichtigung", + "no-recipients-matching": "Keine passenden Empfänger für '{{entity}}' gefunden.", + "no-recipients-text": "Kein Empfänger gefunden", + "no-rule": "Keine Regel konfiguriert", + "no-rules-notification": "Keine Regeln zur Benachrichtigung", + "no-severity-found": "Keine Schwere gefunden", + "no-severity-matching": "'{{severity}}' nicht gefunden.", + "no-template-matching": "Keine Ressource passend zu '{{template}}' gefunden.", + "not-found-slack-recipient": "Slack-Empfänger nicht gefunden", + "notification": "Benachrichtigung", + "notification-center": "Benachrichtigungszentrale", + "notification-tap-action": "Benachrichtigungstipaktion", + "notification-tap-action-hint": "Wenn nicht aktiviert, wird das Standard-Alarm-Dashboard verwendet", + "notify": "benachrichtigen", + "notify-again": "Erneut benachrichtigen", + "notify-alarm-action": { + "acknowledged": "Alarm bestätigt", + "assigned": "Alarm zugewiesen", + "cleared": "Alarm gelöscht", + "created": "Alarm erstellt", + "severity-changed": "Alarm-Schweregrad geändert", + "unassigned": "Alarm nicht mehr zugewiesen" + }, + "notify-on": "Benachrichtigen bei", + "notify-on-comment-update": "Bei Kommentaraktualisierung benachrichtigen", + "notify-on-required": "Benachrichtigungsauslöser ist erforderlich", + "notify-on-unassign": "Bei Entzug der Zuweisung benachrichtigen", + "notify-only-user-comments": "Nur Benutzerkommentare benachrichtigen", + "only-rule-chain-lifecycle-failures": "Nur Lebenszyklusfehler von Regelketten", + "only-rule-node-lifecycle-failures": "Nur Lebenszyklusfehler von Regelknoten", + "platform-users": "Plattformbenutzer", + "rate-limits": "Ratenbegrenzungen", + "rate-limits-hint": "Wenn das Feld leer ist, wird der Auslöser auf alle Ratenbegrenzungen angewendet", + "recipient": "Empfänger", + "recipient-group": "Empfängergruppe", + "recipient-type": { + "affected-tenant-administrators": "Betroffene Mandantenadministratoren", + "affected-user": "Betroffener Benutzer", + "all-users": "Alle Benutzer", + "customer-users": "Kundenbenutzer", + "system-administrators": "Systemadministratoren", + "tenant-administrators": "Mandantenadministratoren", + "user-filters": "Benutzerfilter", + "user-list": "Benutzerliste", + "users-entity-owner": "Benutzer des Entitätseigentümers" + }, + "recipients": "Empfänger", + "notification-recipient": "Benachrichtigungsempfänger", + "notification-recipient-required": "Benachrichtigungsempfänger ist erforderlich.", + "notification-recipients": "Benachrichtigungen / Empfänger", + "recipients-count": "{ count, plural, =1 {1 Empfänger} other {# Empfänger} }", + "recipients-required": "Empfänger sind erforderlich", + "refresh-allow-delivery-method": "Liefermethode aktualisieren", + "request-search": "Suchanfrage", + "request-status": { + "processing": "In Bearbeitung", + "scheduled": "Geplant", + "sent": "Gesendet" + }, + "review": "Überprüfen", + "rule": "Regel", + "rule-chain-list-rule-hint": "Wenn das Feld leer ist, wird der Auslöser auf alle Regelketten angewendet", + "rule-engine-events-trigger-settings": "Einstellungen für Regelmaschinen-Ereignisauslöser", + "rule-engine-filter": "Regelmaschinenfilter", + "rule-name": "Regelname", + "rule-name-required": "Name ist erforderlich", + "rule-disable": "Benachrichtigungsregel deaktivieren", + "rule-enable": "Benachrichtigungsregel aktivieren", + "rule-node-filter": "Regelknotenfilter", + "rules": "Regeln", + "notification-rules": "Benachrichtigungen / Regeln", + "scheduler-later": "Für später planen", + "search-notification": "Benachrichtigungen suchen", + "search-recipients": "Empfänger suchen", + "search-rules": "Regeln suchen", + "search-templates": "Vorlagen suchen", + "see-documentation": "Dokumentation ansehen", + "selected-notifications": "{ count, plural, =1 {1 Benachrichtigung} other {# Benachrichtigungen} } ausgewählt", + "selected-recipients": "{ count, plural, =1 {1 Empfänger} other {# Empfänger} } ausgewählt", + "selected-requests": "{ count, plural, =1 {1 Anfrage} other {# Anfragen} } ausgewählt", + "selected-rules": "{ count, plural, =1 {1 Regel} other {# Regeln} } ausgewählt", + "selected-template": "{ count, plural, =1 {1 Vorlage} other {# Vorlagen} } ausgewählt", + "send-notification": "Benachrichtigung senden", + "sent": "Gesendet", + "setup": "Einrichtung", + "notification-sent": "Benachrichtigungen / Gesendet", + "set-entity-from-notification": "Entität aus Benachrichtigung in Dashboard-Status übernehmen", + "slack-chanel-type": "Slack-Kanaltyp", + "slack-chanel-types": { + "direct": "Direktnachricht", + "private-channel": "Privater Kanal", + "public-channel": "Öffentlicher Kanal" + }, + "start-from-scratch": "Von vorne beginnen", + "status": "Status", + "stop-escalation-alarm-status-become": "Eskalation beenden, wenn Alarmstatus wird zu:", + "subject": "Betreff", + "subject-required": "Betreff ist erforderlich", + "subject-max-length": "Betreff sollte höchstens {{ length }} Zeichen lang sein", + "template": "Vorlage", + "template-name": "Vorlagenname", + "template-required": "Vorlage ist erforderlich", + "template-type": { + "alarm": "Alarm", + "alarm-assignment": "Alarmzuweisung", + "alarm-comment": "Alarmkommentar", + "api-usage-limit": "API-Nutzungslimit", + "device-activity": "Geräteaktivität", + "entities-limit": "Entitätenlimit", + "entity-action": "Entitätsaktion", + "general": "Allgemein", + "rule-engine-lifecycle-event": "Regelmaschinenlebenszyklusereignis", + "rule-node": "Regelknoten", + "new-platform-version": "Neue Plattformversion", + "rate-limits": "Überschrittene Ratenlimits", + "edge-communication-failure": "Edge-Kommunikationsfehler", + "edge-connection": "Edge-Verbindung", + "task-processing-failure": "Fehler bei der Aufgabenverarbeitung" + }, + "templates": "Vorlagen", + "notification-templates": "Benachrichtigungen / Vorlagen", + "tenant-profiles-list-rule-hint": "Wenn das Feld leer ist, wird der Auslöser auf alle Mandantenprofile angewendet", + "tenants-list-rule-hint": "Wenn das Feld leer ist, wird der Auslöser auf alle Mandanten angewendet", + "threshold": "Schwellenwert", + "theme-color": "Designfarbe", + "time": "Zeit", + "track-rule-node-events": "Regelknotenereignisse verfolgen", + "trigger": { + "alarm": "Alarm", + "alarm-assignment": "Alarmzuweisung", + "alarm-comment": "Alarmkommentar", + "api-usage-limit": "API-Nutzungslimit", + "device-activity": "Geräteaktivität", + "entities-limit": "Entitätenlimit", + "entity-action": "Entitätsaktion", + "rule-engine-lifecycle-event": "Regelmaschinenlebenszyklusereignis", + "new-platform-version": "Neue Plattformversion", + "rate-limits": "Überschrittene Ratenlimits", + "edge-connection": "Edge-Verbindung", + "edge-communication-failure": "Edge-Kommunikationsfehler", + "task-processing-failure": "Fehler bei der Aufgabenverarbeitung", + "trigger": "Auslöser", + "trigger-required": "Auslöser ist erforderlich" + }, + "type": "Typ", + "unread": "Ungelesen", + "updated": "Aktualisiert", + "use-deprecated-webhook-connectors": "Veraltete Webhook-Connectoren verwenden", + "use-old-api": "Alte API verwenden", + "use-template": "Vorlage verwenden", + "view-all": "Alle anzeigen", + "warning": "Warnung", + "webhook-url": "Webhook-URL", + "webhook-url-required": "Webhook-URL ist erforderlich", + "workflow-url": "Workflow-URL", + "workflow-url-required": "Workflow-URL ist erforderlich", + "channel-name": "Kanalname", + "channel-name-required": "Kanalname ist erforderlich", + "settings": { + "notification-settings": "Benachrichtigungseinstellungen", + "reset-all": "Alle Einstellungen zurücksetzen", + "reset-all-title": "Sind Sie sicher, dass Sie das Formular zurücksetzen möchten?", + "reset-all-text": "Nach der Bestätigung wird das Einstellungsformular auf den Standardwert zurückgesetzt und gespeichert.", + "type": "Typ", + "enable-all": "Alle aktivieren", + "disable-all": "Alle deaktivieren", + "delivery-not-configured": "Liefermethode ist nicht konfiguriert" + } + }, + "ota-update": { + "add": "Paket hinzufügen", + "assign-firmware": "Firmware zugewiesen", + "assign-firmware-required": "Zugewiesene Firmware ist erforderlich", + "assign-software": "Software zugewiesen", + "assign-software-required": "Zugewiesene Software ist erforderlich", + "auto-generate-checksum": "Prüfsumme automatisch generieren", + "checksum": "Prüfsumme", + "checksum-hint": "Wenn die Prüfsumme leer ist, wird sie automatisch generiert", + "checksum-algorithm": "Prüfsummenalgorithmus", + "checksum-copied-message": "Paket-Prüfsumme wurde in die Zwischenablage kopiert", + "change-firmware": "Die Änderung der Firmware kann ein Update von { count, plural, =1 {1 Gerät} other {# Geräten} } verursachen.", + "change-software": "Die Änderung der Software kann ein Update von { count, plural, =1 {1 Gerät} other {# Geräten} } verursachen.", + "chose-compatible-device-profile": "Das hochgeladene Paket ist nur für Geräte mit dem ausgewählten Profil verfügbar.", + "chose-firmware-distributed-device": "Firmware auswählen, die auf die Geräte verteilt wird", + "chose-software-distributed-device": "Software auswählen, die auf die Geräte verteilt wird", + "content-type": "Inhaltstyp", + "copy-checksum": "Prüfsumme kopieren", + "copy-direct-url": "Direkte URL kopieren", + "copyId": "Paket-ID kopieren", + "copied": "Kopiert!", + "delete": "Paket löschen", + "delete-ota-update-text": "Achtung, nach der Bestätigung kann das OTA-Update nicht wiederhergestellt werden.", + "delete-ota-update-title": "Sind Sie sicher, dass Sie das OTA-Update '{{title}}' löschen möchten?", + "delete-ota-updates-text": "Achtung, nach der Bestätigung werden alle ausgewählten OTA-Updates entfernt.", + "delete-ota-updates-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 OTA-Update} other {# OTA-Updates} } löschen möchten?", + "description": "Beschreibung", + "direct-url": "Direkte URL", + "direct-url-copied-message": "Direkte URL des Pakets wurde in die Zwischenablage kopiert", + "direct-url-required": "Direkte URL ist erforderlich", + "download": "Paket herunterladen", + "drop-file": "Paketdatei ablegen oder klicken, um eine Datei auszuwählen.", + "drop-package-file-or": "Paketdatei hierher ziehen oder", + "file-name": "Dateiname", + "file-size": "Dateigröße", + "file-size-bytes": "Dateigröße in Bytes", + "idCopiedMessage": "Paket-ID wurde in die Zwischenablage kopiert", + "no-firmware-matching": "Keine kompatiblen Firmware-OTA-Update-Pakete für '{{entity}}' gefunden.", + "no-firmware-text": "Keine kompatiblen Firmware-OTA-Update-Pakete bereitgestellt.", + "no-packages-text": "Keine Pakete gefunden", + "no-software-matching": "Keine kompatiblen Software-OTA-Update-Pakete für '{{entity}}' gefunden.", + "no-software-text": "Keine kompatiblen Software-OTA-Update-Pakete bereitgestellt.", + "ota-update": "OTA-Update", + "ota-update-details": "OTA-Update-Details", + "ota-updates": "OTA-Updates", + "package-file": "Paketdatei", + "package-type": "Pakettyp", + "packages-repository": "Paket-Repository", + "search": "Pakete suchen", + "selected-package": "{ count, plural, =1 {1 Paket} other {# Pakete} } ausgewählt", + "title": "Titel", + "title-required": "Titel ist erforderlich.", + "title-max-length": "Titel darf maximal 256 Zeichen lang sein", + "types": { + "firmware": "Firmware", + "software": "Software" + }, + "upload-binary-file": "Binärdatei hochladen", + "use-external-url": "Externe URL verwenden", + "version": "Version", + "version-required": "Version ist erforderlich.", + "version-tag": "Versionstag", + "version-tag-hint": "Benutzerdefinierter Tag muss mit der vom Gerät gemeldeten Paketversion übereinstimmen.", + "version-max-length": "Version darf maximal 256 Zeichen lang sein", + "warning-after-save-no-edit": "Nach dem Hochladen des Pakets können Titel, Version, Geräteprofil und Pakettyp nicht mehr geändert werden." }, "position": { "top": "Oben", @@ -1497,11 +4182,100 @@ "profile": "Profil", "last-login-time": "Letzte Anmeldung", "change-password": "Passwort ändern", - "current-password": "Aktuelles Passwort" + "current-password": "Aktuelles Passwort", + "copy-jwt-token": "JWT-Token kopieren", + "jwt-token": "JWT-Token", + "token-valid-till": "Token ist gültig bis", + "tokenCopiedSuccessMessage": "JWT-Token wurde in die Zwischenablage kopiert", + "tokenCopiedWarnMessage": "JWT-Token ist abgelaufen! Bitte aktualisieren Sie die Seite." + }, + "profiles": { + "profiles": "Profile" + }, + "security": { + "security": "Sicherheit", + "general-settings": "Allgemeine Sicherheitseinstellungen", + "access-token": "Zugriffstoken", + "access-token-required": "Zugriffstoken ist erforderlich", + "clientId": "Client-ID", + "clientId-required": "Client-ID ist erforderlich", + "username": "Benutzername", + "username-required": "Benutzername ist erforderlich", + "ca-cert": "CA-Zertifikat", + "2fa": { + "2fa": "Zwei-Faktor-Authentifizierung", + "2fa-description": "Die Zwei-Faktor-Authentifizierung schützt Ihr Konto vor unbefugtem Zugriff. Sie müssen lediglich beim Login einen Sicherheitscode eingeben.", + "authenticate-with": "Sie können sich authentifizieren mit:", + "disable-2fa-provider-text": "Das Deaktivieren von {{name}} macht Ihr Konto weniger sicher", + "disable-2fa-provider-title": "Sind Sie sicher, dass Sie {{name}} deaktivieren möchten?", + "get-new-code": "Neuen Code erhalten", + "main-2fa-method": "Als primäre Zwei-Faktor-Methode verwenden", + "dialog": { + "activation-step-description-email": "Beim nächsten Login müssen Sie den Sicherheitscode eingeben, der an Ihre E-Mail-Adresse gesendet wird.", + "activation-step-description-sms": "Beim nächsten Login müssen Sie den Sicherheitscode eingeben, der an die Telefonnummer gesendet wird.", + "activation-step-description-totp": "Beim nächsten Login müssen Sie einen Zwei-Faktor-Authentifizierungscode angeben.", + "activation-step-label": "Aktivierung", + "backup-code-description": "Drucken Sie die Codes aus, um sie griffbereit zu haben, wenn Sie sich bei Ihrem Konto anmelden. Jeder Backup-Code kann einmal verwendet werden.", + "backup-code-warn": "Sobald Sie diese Seite verlassen, können diese Codes nicht erneut angezeigt werden. Bewahren Sie sie sicher auf.", + "download-txt": "Herunterladen (txt)", + "email-step-description": "Geben Sie eine E-Mail-Adresse ein, die als Authentifizierer verwendet werden soll.", + "email-step-label": "E-Mail", + "enable-email-title": "E-Mail-Authentifizierer aktivieren", + "enable-sms-title": "SMS-Authentifizierer aktivieren", + "enable-totp-title": "Authentifizierungs-App aktivieren", + "enter-verification-code": "6-stelligen Code hier eingeben", + "get-backup-code-title": "Backup-Code erhalten", + "next": "Weiter", + "scan-qr-code": "Scannen Sie diesen QR-Code mit Ihrer Verifizierungs-App", + "send-code": "Code senden", + "sms-step-description": "Geben Sie eine Telefonnummer ein, die als Authentifizierer verwendet werden soll.", + "sms-step-label": "Telefonnummer", + "success": "Erfolg!", + "totp-step-description-install": "Sie können Apps wie Google Authenticator, Authy oder Duo installieren.", + "totp-step-description-open": "Öffnen Sie die Authentifizierungs-App auf Ihrem Mobiltelefon.", + "totp-step-label": "App erhalten", + "verification-code": "6-stelliger Code", + "verification-code-invalid": "Ungültiges Code-Format", + "verification-code-incorrect": "Verifizierungscode ist falsch", + "verification-code-many-request": "Zu viele Anfragen zur Überprüfung des Codes", + "verification-step-description": "Geben Sie den 6-stelligen Code ein, den wir gerade an {{address}} gesendet haben", + "verification-step-label": "Verifizierung" + }, + "provider": { + "email": "E-Mail", + "email-description": "Verwenden Sie einen Sicherheitscode, der an Ihre E-Mail-Adresse gesendet wird, um sich zu authentifizieren.", + "email-hint": "Authentifizierungscodes werden per E-Mail an {{ info }} gesendet", + "sms": "SMS", + "sms-description": "Verwenden Sie Ihr Telefon zur Authentifizierung. Wir senden Ihnen beim Login einen Sicherheitscode per SMS.", + "sms-hint": "Authentifizierungscodes werden per SMS an {{ info }} gesendet", + "totp": "Authentifizierungs-App", + "totp-description": "Verwenden Sie Apps wie Google Authenticator, Authy oder Duo auf Ihrem Telefon zur Authentifizierung. Diese generieren einen Sicherheitscode für die Anmeldung.", + "totp-hint": "Eine Authentifizierungs-App ist für Ihr Konto eingerichtet", + "backup_code": "Backup-Code", + "backup-code-description": "Diese druckbaren Einmalcodes ermöglichen die Anmeldung, wenn Sie nicht auf Ihr Telefon zugreifen können, z. B. auf Reisen.", + "backup-code-hint": "{{ info }} Einmalcodes sind derzeit aktiv" + } + }, + "password-requirement": { + "at-least": "Mindestens:", + "character": "{ count, plural, =1 {1 Zeichen} other {# Zeichen} }", + "digit": "{ count, plural, =1 {1 Ziffer} other {# Ziffern} }", + "incorrect-password-try-again": "Falsches Passwort. Bitte versuchen Sie es erneut", + "lowercase-letter": "{ count, plural, =1 {1 Kleinbuchstabe} other {# Kleinbuchstaben} }", + "new-passwords-not-match": "Die neuen Passwörter stimmen nicht überein", + "password-should-not-contain-spaces": "Ihr Passwort darf keine Leerzeichen enthalten", + "password-not-meet-requirements": "Passwort erfüllt die Anforderungen nicht", + "password-requirements": "Passwortanforderungen", + "password-should-difference": "Das neue Passwort sollte sich vom aktuellen unterscheiden", + "special-character": "{ count, plural, =1 {1 Sonderzeichen} other {# Sonderzeichen} }", + "uppercase-letter": "{ count, plural, =1 {1 Großbuchstabe} other {# Großbuchstaben} }", + "at-most": "Höchstens:" + } }, "relation": { "relations": "Beziehungen", "direction": "Richtung", + "clear-relation-type": "Beziehungstyp löschen", "search-direction": { "FROM": "Von", "TO": "Zu" @@ -1510,57 +4284,144 @@ "FROM": "von", "TO": "zu" }, - "from-relations": "Ausgehende Verbindungen", - "to-relations": "Eingehende Verbindungen", + "from-relations": "Ausgehende Beziehungen", + "to-relations": "Eingehende Beziehungen", "selected-relations": "{ count, plural, =1 {1 Beziehung} other {# Beziehungen} } ausgewählt", "type": "Typ", - "to-entity-type": "Zum Entitätstyp", - "to-entity-name": "Zum Entitätsnamen", - "from-entity-type": "Vom Entitätstyp", - "from-entity-name": "Vom Entitätsnamen", - "to-entity": "Zur Entität", - "from-entity": "Von Entität", + "to-entity-type": "Zielentitätstyp", + "to-entity-name": "Name der Zielentität", + "from-entity-type": "Quellentitätstyp", + "from-entity-name": "Name der Quellentität", + "to-entity": "Zielentität", + "from-entity": "Quellentität", "delete": "Beziehung löschen", - "relation-type": "Art der Beziehung", - "relation-type-required": "Art der Beziehung erforderlich.", - "any-relation-type": "Jede Art", + "relation-type": "Beziehungstyp", + "relation-type-required": "Beziehungstyp ist erforderlich.", + "relation-type-max-length": "Beziehungstyp sollte weniger als 256 Zeichen enthalten", + "any-relation-type": "Beliebiger Typ", "add": "Beziehung hinzufügen", "edit": "Beziehung bearbeiten", - "delete-to-relation-title": "Möchten Sie die Beziehung zur Einheit'{{entityName}}' wirklich löschen?", - "delete-to-relation-text": "Vorsicht, nach Bestätigung ist die Entität '{{entityName}}' nicht mehr mit der aktuellen Entität verbunden.", - "delete-to-relations-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Beziehung} other {# Beziehungen} } wirklich löschen?", - "delete-to-relations-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Beziehungen entfernt und die entsprechenden Entitäten sind nicht mehr mit der aktuellen Entität verbunden.", - "delete-from-relation-title": "Sind Sie sicher, dass Sie die Verbindung aus der Entität '{{entityName}}' löschen möchten?", - "delete-from-relation-text": "Vorsicht, nach Bestätigung wird die aktuelle Entität '{{entityName}}' von der Entität unabhängig sein.", + "delete-to-relation-title": "Sind Sie sicher, dass Sie die Beziehung zur Entität '{{entityName}}' löschen möchten?", + "delete-to-relation-text": "Seien Sie vorsichtig, nach der Bestätigung wird die Entität '{{entityName}}' von der aktuellen Entität getrennt.", + "delete-to-relations-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Beziehung} other {# Beziehungen} } löschen möchten?", + "delete-to-relations-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Beziehungen entfernt und die entsprechenden Entitäten von der aktuellen Entität getrennt.", + "delete-from-relation-title": "Sind Sie sicher, dass Sie die Beziehung von der Entität '{{entityName}}' löschen möchten?", + "delete-from-relation-text": "Seien Sie vorsichtig, nach der Bestätigung wird die aktuelle Entität von der Entität '{{entityName}}' getrennt.", "delete-from-relations-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Beziehung} other {# Beziehungen} } löschen möchten?", - "delete-from-relations-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Beziehungen entfernt und die aktuellen Entität wird nicht mehr mit den entsprechenden Entitäten verknüpft sein.", + "delete-from-relations-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Beziehungen entfernt und die aktuelle Entität wird von den entsprechenden Entitäten getrennt.", "remove-relation-filter": "Beziehungsfilter entfernen", + "remove-filter": "Filter entfernen", "add-relation-filter": "Beziehungsfilter hinzufügen", - "any-relation": "Jede Beziehung", + "any-relation": "Beliebige Beziehung", "relation-filters": "Beziehungsfilter", - "additional-info": "Zusätzliche Information (JSON)", - "invalid-additional-info": "Json der Zusätzlichen Informationen konnte nicht gelesen werden." + "additional-info": "Zusätzliche Informationen (JSON)", + "invalid-additional-info": "Zusätzliche Info-JSON kann nicht geparst werden.", + "no-relations-text": "Keine Beziehungen gefunden", + "not": "Nicht" + }, + "resource": { + "add": "Ressource hinzufügen", + "all-types": "Alle", + "copyId": "Ressourcen-ID kopieren", + "delete": "Ressource löschen", + "delete-resource-text": "Seien Sie vorsichtig, nach der Bestätigung kann die Ressource nicht wiederhergestellt werden.", + "delete-resource-title": "Sind Sie sicher, dass Sie die Ressource '{{resourceTitle}}' löschen möchten?", + "delete-resources-action-title": "{ count, plural, =1 {1 Ressource löschen} other {# Ressourcen löschen} }", + "delete-resources-text": "Bitte beachten Sie, dass die ausgewählten Ressourcen gelöscht werden, auch wenn sie in Geräteprofilen verwendet werden.", + "delete-resources-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Ressource} other {# Ressourcen} } löschen möchten?", + "download": "Ressource herunterladen", + "drop-file": "Datei ablegen oder klicken, um eine Datei zum Hochladen auszuwählen.", + "drop-resource-file-or": "Datei hierher ziehen oder", + "empty": "Ressource ist leer", + "file-name": "Dateiname", + "idCopiedMessage": "Ressourcen-ID wurde in die Zwischenablage kopiert", + "no-resource-matching": "Keine passende Ressource für '{{widgetsBundle}}' gefunden.", + "no-resource-text": "Keine Ressourcen gefunden", + "open-widgets-bundle": "Widget-Bundle öffnen", + "resource": "Ressource", + "resource-file": "Ressourcendatei", + "resource-files": "Ressourcendateien", + "resource-library-details": "Ressourcendetails", + "resource-type": "Ressourcentyp", + "resources-library": "Ressourcenbibliothek", + "search": "Ressourcen suchen", + "selected-resources": "{ count, plural, =1 {1 Ressource} other {# Ressourcen} } ausgewählt", + "system": "System", + "title": "Titel", + "title-required": "Titel ist erforderlich.", + "title-max-length": "Titel sollte weniger als 256 Zeichen enthalten", + "type": { + "jks": "JKS", + "js-module": "JS-Modul", + "lwm2m-model": "LWM2M-Modell", + "pkcs-12": "PKCS #12" + }, + "resource-sub-type": "Subtyp", + "sub-type": { + "image": "Bild", + "scada-symbol": "SCADA-Symbol", + "extension": "Erweiterung", + "module": "Modul" + } + }, + "javascript": { + "add": "JavaScript-Ressource hinzufügen", + "delete": "JavaScript-Ressource löschen", + "delete-javascript-resource-text": "Seien Sie vorsichtig, nach der Bestätigung kann die JavaScript-Ressource nicht wiederhergestellt werden.", + "delete-javascript-resource-title": "Sind Sie sicher, dass Sie die JavaScript-Ressource '{{resourceTitle}}' löschen möchten?", + "delete-javascript-resources-action-title": "JavaScript { count, plural, =1 {1 Ressource} other {# Ressourcen} } löschen", + "delete-javascript-resources-text": "Bitte beachten Sie, dass die ausgewählten JavaScript-Ressourcen gelöscht werden, auch wenn sie in JavaScript-Funktionen verwendet werden.", + "delete-javascript-resources-title": "Sind Sie sicher, dass Sie JavaScript { count, plural, =1 {1 Ressource} other {# Ressourcen} } löschen möchten?", + "delete-javascript-resource-in-use-text": "Wenn Sie die JavaScript-Ressource trotzdem löschen möchten, klicken Sie auf die Schaltfläche Trotzdem löschen.", + "download": "JavaScript-Ressource herunterladen", + "upload-from-file": "JavaScript aus Datei hochladen", + "resource-file": "JavaScript-Ressourcendatei", + "drop-file": "Legen Sie eine JavaScript-Datei ab oder klicken Sie, um eine Datei auszuwählen.", + "drop-resource-file-or": "JavaScript-Datei hierher ziehen oder", + "javascript-library": "JavaScript-Bibliothek", + "javascript-type": "JavaScript-Typ", + "javascript-resource-details": "Details der JavaScript-Ressource", + "javascript-resource-is-in-use": "JavaScript-Ressource wird von anderen Entitäten verwendet", + "javascript-resources-are-in-use": "JavaScript-Ressourcen werden von anderen Entitäten verwendet", + "javascript-resource-is-in-use-text": "Die JavaScript-Ressource '{{title}}' wurde nicht gelöscht, da sie von folgenden Entitäten verwendet wird:", + "javascript-resources-are-in-use-text": "Nicht alle JavaScript-Ressourcen wurden gelöscht, da sie von anderen Entitäten verwendet werden.
Sie können die referenzierten Entitäten über die Schaltfläche Referenzen in der jeweiligen Ressourcenzeile anzeigen.
Wenn Sie diese JavaScript-Ressourcen dennoch löschen möchten, wählen Sie sie in der Tabelle unten aus und klicken Sie auf Ausgewählte löschen.", + "search": "JavaScript-Ressourcen suchen", + "selected-javascript-resources": "{ count, plural, =1 {1 JavaScript-Ressource} other {# JavaScript-Ressourcen} } ausgewählt", + "no-javascript-resource-text": "Keine JavaScript-Ressourcen gefunden", + "all-types": "Alle", + "module-script": "Modulskript" + }, + "rpc": { + "error": { + "target-device-is-not-set": "Zielgerät ist nicht festgelegt!", + "invalid-target-entity": "RPC-Befehle werden von der Entität {{entityType}} nicht unterstützt.", + "failed-to-resolve-target-device": "Zielgerät konnte nicht aufgelöst werden!", + "request-timeout": "Anfragezeitüberschreitung", + "rpc-http-error": "Fehler: {{status}} - {{statusText}}" + } }, "rulechain": { "rulechain": "Regelkette", + "rulechain-events": "Regelkettenereignisse", "rulechains": "Regelketten", "root": "Wurzel", "delete": "Regelkette löschen", "name": "Name", "name-required": "Name ist erforderlich.", + "name-max-length": "Name sollte weniger als 256 Zeichen enthalten", "description": "Beschreibung", "add": "Regelkette hinzufügen", "set-root": "Regelkette zur Wurzel machen", "set-root-rulechain-title": "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' zur Wurzel machen möchten?", - "set-root-rulechain-text": "Nach der Bestätigung wird die Regelkette zur Wurzel und bearbeitet alle eingehenden Transportnachrichten.", + "set-root-rulechain-text": "Nach der Bestätigung wird diese Regelkette zur Wurzel und verarbeitet alle eingehenden Transportnachrichten.", "delete-rulechain-title": "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' löschen möchten?", - "delete-rulechain-text": "Vorsichtig, nach Bestätigung werden die Regelkette und alle zugehörigen Daten gelöscht.", + "delete-rulechain-text": "Seien Sie vorsichtig, nach der Bestätigung wird die Regelkette und alle zugehörigen Daten unwiederbringlich gelöscht.", "delete-rulechains-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Regelkette} other {# Regelketten} } löschen möchten?", - "delete-rulechains-action-title": "{ count, plural, =1 {1 Regelkette} other {# Regelketten} } löschen", - "delete-rulechains-text": "Vorsichtig, nach Bestätigung werden alle ausgewählten Regelketten entfernt und alle zugehörigen Daten werden gelöscht.", + "delete-rulechains-action-title": "{ count, plural, =1 {1 Regelkette löschen} other {# Regelketten löschen} }", + "delete-rulechains-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Regelketten entfernt und alle zugehörigen Daten gelöscht.", "add-rulechain-text": "Neue Regelkette hinzufügen", - "no-rulechains-text": "Keine Regelkette gefunden", - "rulechain-details": "Regelketten-Details", + "no-rulechains-text": "Keine Regelketten gefunden", + "rulechain-details": "Details zur Regelkette", "details": "Details", "events": "Ereignisse", "system": "System", @@ -1568,94 +4429,101 @@ "export": "Regelkette exportieren", "export-failed-error": "Regelkette konnte nicht exportiert werden: {{error}}", "create-new-rulechain": "Neue Regelkette erstellen", - "rulechain-file": "Regelkettendatei", - "invalid-rulechain-file-error": "Regelkette konnte nicht importiert werden: Ungültige Regelkettendatenstruktur.", - "copyId": "Regelketten-ID kopieren", - "idCopiedMessage": "Regelketten-ID wurde in die Zwischenablage kopiert", + "rulechain-file": "Regelketten-Datei", + "invalid-rulechain-file-error": "Regelkette konnte nicht importiert werden: Ungültige Datenstruktur.", + "copyId": "Regelkette-ID kopieren", + "idCopiedMessage": "Regelkette-ID wurde in die Zwischenablage kopiert", "select-rulechain": "Regelkette auswählen", - "no-rulechains-matching": "Es wurden keine passenden Regelketten für '{{entity}}' gefunden.", + "no-rulechains-matching": "Keine Regelketten für '{{entity}}' gefunden.", "rulechain-required": "Regelkette ist erforderlich", "management": "Regelverwaltung", - "debug-mode": "Modus zur Fehlersuche", - "assign-rulechains": "Regelketten zuweisen", - "delete-rulechains": "Regelketten löschen", - "unassign-rulechain": "Nicht zugeordnete Regelkette", - "unassign-rulechains": "Nicht zugeordnete Regelketten", - "unassign-rulechain-title": "Möchten Sie die Zuordnung die Regelkette '{{ruleChainTitle}}' wirklich aufheben?", - "unassign-rulechain-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Regelkette aufgehoben und sie sind für den Rand nicht mehr zugänglich.", - "unassign-rulechains-from-edge-action-title": "Zuordnung { count, plural, =1 {1 Regelkette} other {# Regelketten} } vom Rand aufheben", - "unassign-rulechains-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Regelketten aufgehoben und sie sind für den Rand nicht mehr zugänglich.", - "assign-rulechain-to-edge-title": "Regelkette(n) dem Rand zuordnen", - "assign-rulechain-to-edge-text": "Bitte wählen Sie die Regelketten aus, die Sie dem Rand zuordnen möchten", - "set-edge-template-root-rulechain": "Erstellen Sie den Stamm der Regelkettenkantenvorlage", - "set-edge-template-root-rulechain-title": "Möchten Sie die Kantenvorlage der Regelkette '{{ruleChainName}}' wirklich als Root festlegen?", - "set-edge-template-root-rulechain-text": "Nach der Bestätigung wird die Regelkette zum Stamm der Kantenvorlage und zur Stammregelkette für neu erstellte Kanten.", - "invalid-rulechain-type-error": "Regelkette konnte nicht importiert werden: Ungültige Regelkettentyp. Erwarteter Typ ist {{expectedRuleChainType}}.", - "set-auto-assign-to-edge": "Weisen Sie bei der Erstellung den Kanten die Regelkette zu", - "set-auto-assign-to-edge-title": "Möchten Sie die Kantenregelkette '{{ruleChainName}}' bei der Erstellung automatisch den Kanten zuweisen?", - "set-auto-assign-to-edge-text": "Nach der Bestätigung wird die Kantenregelkette bei der Erstellung automatisch den Kanten zugewiesen.", - "unset-auto-assign-to-edge": "Deaktiviert die Zuordnung der Regelkette zu Kanten bei der Erstellung", - "unset-auto-assign-to-edge-title": "Möchten Sie die Kantenregelkette '{{ruleChainName}}' bei der Erstellung unbedingt den Kanten zuweisen?", - "unset-auto-assign-to-edge-text": "Nach der Bestätigung wird die Kantenregelkette bei der Erstellung nicht mehr automatisch den Kanten zugewiesen.", - "edge-template-root": "Vorlagenstamm", - "search": "Suchen Sie nach Regelketten", - "selected-rulechains": "{count, plural, =1 {1 Regelkette} other {# Regelketten} } ausgewählt", + "debug-mode": "Debug-Modus", + "search": "Regelketten suchen", + "selected-rulechains": "{ count, plural, =1 {1 Regelkette} other {# Regelketten} } ausgewählt", "open-rulechain": "Regelkette öffnen", - "assign-to-edge": "Rand zuweisen", - "edge-rulechain": "Kantenregelkette" + "edge-template-root": "Vorlagen-Wurzel", + "assign-to-edge": "Edge zuweisen", + "edge-rulechain": "Edge-Regelkette", + "unassign-rulechain-from-edge-text": "Nach der Bestätigung wird die Regelkette entfernt und ist für das Edge nicht mehr verfügbar.", + "unassign-rulechains-from-edge-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Regelkette} other {# Regelketten} } entfernen möchten?", + "unassign-rulechains-from-edge-text": "Nach der Bestätigung werden alle ausgewählten Regelketten entfernt und sind für das Edge nicht mehr verfügbar.", + "assign-rulechain-to-edge-title": "Regelketten dem Edge zuweisen", + "assign-rulechain-to-edge-text": "Bitte wählen Sie die Regelketten aus, die dem Edge zugewiesen werden sollen", + "set-edge-template-root-rulechain": "Regelkette als Edge-Vorlagen-Wurzel festlegen", + "set-edge-template-root-rulechain-title": "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' zur Edge-Vorlagen-Wurzel machen möchten?", + "set-edge-template-root-rulechain-text": "Nach der Bestätigung wird diese Regelkette als Wurzel-Regelkette für neu erstellte Edges festgelegt.", + "invalid-rulechain-type-error": "Regelkette konnte nicht importiert werden: Ungültiger Regelkettentyp. Erwarteter Typ ist {{expectedRuleChainType}}.", + "set-auto-assign-to-edge": "Regelkette bei Erstellung automatisch Edge(s) zuweisen", + "set-auto-assign-to-edge-title": "Sind Sie sicher, dass Sie die Edge-Regelkette '{{ruleChainName}}' bei der Erstellung automatisch Edge(s) zuweisen möchten?", + "set-auto-assign-to-edge-text": "Nach der Bestätigung wird die Edge-Regelkette bei der Erstellung automatisch Edge(s) zugewiesen.", + "unset-auto-assign-to-edge": "Regelkette bei Erstellung nicht automatisch Edge(s) zuweisen", + "unset-auto-assign-to-edge-title": "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' bei der Erstellung nicht automatisch Edge(s) zuweisen möchten?", + "unset-auto-assign-to-edge-text": "Nach der Bestätigung wird die Edge-Regelkette nicht mehr automatisch bei Erstellung zugewiesen.", + "unassign-rulechain-title": "Sind Sie sicher, dass Sie die Regelkette '{{ruleChainName}}' entfernen möchten?", + "unassign-rulechains": "Regelketten entfernen" }, "rulenode": { + "rule-node-events": "Regelknotenereignisse", "details": "Details", "events": "Ereignisse", "search": "Knoten suchen", "open-node-library": "Knotenbibliothek öffnen", - "add": "Neuen Regelknoten hinzufügen", + "close-node-library": "Knotenbibliothek schließen", + "add": "Regelknoten hinzufügen", "name": "Name", "name-required": "Name ist erforderlich.", + "name-max-length": "Name sollte weniger als 256 Zeichen enthalten", "type": "Typ", + "rule-node-description": "Beschreibung des Regelknotens", "delete": "Regelknoten löschen", "select-all-objects": "Alle Knoten und Verbindungen auswählen", - "deselect-all-objects": "Auswahl aller Knoten und Verbindungen aufheben", + "deselect-all-objects": "Alle Knoten und Verbindungen abwählen", "delete-selected-objects": "Ausgewählte Knoten und Verbindungen löschen", - "delete-selected": "Auswahl löschen", + "delete-selected": "Ausgewählte löschen", + "create-nested-rulechain": "Verschachtelte Regelkette erstellen", "select-all": "Alle auswählen", - "copy-selected": "Auswahl kopieren", - "deselect-all": "Nichts auswählen", - "rulenode-details": "Details der Regelknoten", - "debug-mode": "Modus zur Fehlersuche", + "copy-selected": "Ausgewählte kopieren", + "deselect-all": "Alle abwählen", + "rulenode-details": "Regelknotendetails", + "debug-mode": "Debug-Modus", + "singleton": "Einzelexemplar", "configuration": "Konfiguration", "link": "Verbindung", - "link-details": "Verbindungsdetails der Regelknoten", + "link-details": "Details der Regelknotenverbindung", "add-link": "Verbindung hinzufügen", - "link-label": "Verbindungsbeschriftung", - "link-label-required": "Verbindungsbeschriftung ist erforderlich.", - "custom-link-label": "Benutzerdefinierte Verbindungsbeschriftung", - "custom-link-label-required": "Benutzerdefinierte Verbindungsbeschriftung ist erforderlich.", - "link-labels": "Verbindungsbeschriftungen", - "link-labels-required": "Verbindungsbeschriftungen sind erforderlich.", - "no-link-labels-found": "Keine Verbindungsbeschriftungen gefunden", + "link-label": "Verbindungsbezeichnung", + "link-label-required": "Verbindungsbezeichnung ist erforderlich.", + "custom-link-label": "Benutzerdefinierte Verbindungsbezeichnung", + "custom-link-label-required": "Benutzerdefinierte Verbindungsbezeichnung ist erforderlich.", + "link-labels": "Verbindungsbezeichnungen", + "link-labels-required": "Verbindungsbezeichnungen sind erforderlich.", + "no-link-labels-found": "Keine Verbindungsbezeichnungen gefunden", "no-link-label-matching": "'{{label}}' nicht gefunden.", - "create-new-link-label": "Bitte erstellen Sie eine neue Verbindungsbeschriftung!", + "create-new-link-label": "Neue erstellen!", "type-filter": "Filter", "type-filter-details": "Eingehende Nachrichten mit konfigurierten Bedingungen filtern", "type-enrichment": "Anreicherung", - "type-enrichment-details": "Fügen Sie zusätzliche Informationen zu den Nachrichtenmetadaten hinzu", + "type-enrichment-details": "Zusätzliche Informationen zu den Nachrichtendaten hinzufügen", "type-transformation": "Transformation", - "type-transformation-details": "Ändern Sie die Nutzerdaten und Metadaten der Nachricht", + "type-transformation-details": "Nachrichtennutzlast und Metadaten ändern", "type-action": "Aktion", - "type-action-details": "Besondere Aktion ausführen", + "type-action-details": "Spezielle Aktion ausführen", "type-external": "Extern", - "type-external-details": "Interagiert mit externem System", + "type-external-details": "Interaktion mit externem System", "type-rule-chain": "Regelkette", - "type-rule-chain-details": "Leitet eingehende Nachrichten an die angegebene Regelkette weiter", - "type-input": "Input", - "type-input-details": "Logische Eingabe der Regelkette, leitet eingehende Nachrichten an die nächste zugehörige Regelkette weiter", + "type-rule-chain-details": "Eingehende Nachrichten an definierte Regelkette weiterleiten", + "type-flow": "Fluss", + "type-flow-details": "Nachrichtenfluss organisieren", + "type-input": "Eingang", + "type-input-details": "Logischer Eingang der Regelkette, leitet eingehende Nachrichten an den nächsten Regelknoten weiter", "type-unknown": "Unbekannt", "type-unknown-details": "Nicht aufgelöster Regelknoten", - "directive-is-not-loaded": "Definierte Konfigurationsanweisung '{{directiveName}}' ist nicht verfügbar.", - "ui-resources-load-error": "Fehler beim Laden der Konfigurations-UI-Ressourcen.", - "invalid-target-rulechain": "Zielregelkette kann nicht aufgelöst werden!", + "directive-is-not-loaded": "Die definierte Konfigurationsdirektive '{{directiveName}}' ist nicht verfügbar.", + "ui-resources-load-error": "Konfigurations-UI-Ressourcen konnten nicht geladen werden.", + "invalid-target-rulechain": "Ziel-Regelkette konnte nicht aufgelöst werden!", "test-script-function": "Skriptfunktion testen", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", "message": "Nachricht", "message-type": "Nachrichtentyp", "select-message-type": "Nachrichtentyp auswählen", @@ -1664,34 +4532,1170 @@ "metadata-required": "Metadateneinträge dürfen nicht leer sein.", "output": "Ausgabe", "test": "Test", - "help": "Hilfe" + "help": "Hilfe", + "reset-debug-settings": "Debug-Einstellungen in allen Knoten zurücksetzen", + "test-with-this-message": "{{test}} mit dieser Nachricht", + "queue-hint": "Wählen Sie eine Warteschlange für die Nachrichtenweiterleitung zu einer anderen Warteschlange. Standardmäßig wird die 'Main'-Warteschlange verwendet.", + "queue-singleton-hint": "Wählen Sie eine Warteschlange für die Nachrichtenweiterleitung in Mehrinstanzumgebungen. Standardmäßig wird die 'Main'-Warteschlange verwendet." + }, + "rule-node-config": { + "id": "ID", + "additional-info": "Zusätzliche Informationen", + "advanced-settings": "Erweiterte Einstellungen", + "create-entity-if-not-exists": "Neue Entität erstellen, wenn nicht vorhanden", + "create-entity-if-not-exists-hint": "Wenn aktiviert, wird eine neue Entität mit angegebenen Parametern erstellt, sofern sie noch nicht existiert. Existierende Entitäten werden wie vorhanden für die Beziehung verwendet.", + "select-device-connectivity-event": "Geräteverbindungsereignis auswählen", + "entity-name-pattern": "Namensmuster", + "device-name-pattern": "Gerätename", + "asset-name-pattern": "Asset-Name", + "entity-view-name-pattern": "Entitätsansichtsname", + "customer-title-pattern": "Kundentitel", + "dashboard-name-pattern": "Dashboard-Titel", + "user-name-pattern": "Benutzer-E-Mail", + "edge-name-pattern": "Edge-Name", + "entity-name-pattern-required": "Namensmuster ist erforderlich", + "entity-name-pattern-hint": "Das Feld 'Namensmuster' unterstützt Templatisierung. Verwenden Sie $[messageKey] für Nachrichtenwerte und ${metadataKey} für Metadatenwerte.", + "copy-message-type": "Nachrichtentyp kopieren", + "entity-type-pattern": "Typmuster", + "entity-type-pattern-required": "Typmuster ist erforderlich", + "message-type-value": "Nachrichtentyp-Wert", + "message-type-value-required": "Nachrichtentyp-Wert ist erforderlich", + "message-type-value-max-length": "Nachrichtentyp-Wert sollte weniger als 256 Zeichen enthalten", + "output-message-type": "Ausgabe-Nachrichtentyp", + "entity-cache-expiration": "Ablaufzeit des Entitäten-Caches (Sekunden)", + "entity-cache-expiration-hint": "Gibt das maximale Intervall an, in dem gefundene Entitätsdatensätze gespeichert werden dürfen. Wert 0 bedeutet, dass Datensätze nie ablaufen.", + "entity-cache-expiration-required": "Ablaufzeit des Entitäten-Caches ist erforderlich.", + "entity-cache-expiration-range": "Ablaufzeit des Entitäten-Caches muss größer oder gleich 0 sein.", + "customer-name-pattern": "Kundentitel", + "customer-name-pattern-required": "Kundentitel ist erforderlich", + "customer-name-pattern-hint": "Verwenden Sie $[messageKey] für Nachrichtenwerte und ${metadataKey} für Metadatenwerte.", + "create-customer-if-not-exists": "Neuen Kunden erstellen, wenn nicht vorhanden", + "unassign-from-customer": "Von bestimmtem Kunden trennen, wenn Absender ein Dashboard ist", + "unassign-from-customer-tooltip": "Nur Dashboards können mehreren Kunden gleichzeitig zugewiesen werden.\nWenn der Nachrichtenabsender ein Dashboard ist, müssen Sie den Kundentitel explizit angeben, um die Zuordnung zu entfernen.", + "customer-cache-expiration": "Ablaufzeit des Kunden-Caches (Sekunden)", + "customer-cache-expiration-hint": "Gibt das maximale Intervall an, in dem gefundene Kundendatensätze gespeichert werden dürfen. Wert 0 bedeutet, dass Datensätze nie ablaufen.", + "customer-cache-expiration-required": "Ablaufzeit des Kunden-Caches ist erforderlich.", + "customer-cache-expiration-range": "Ablaufzeit des Kunden-Caches muss größer oder gleich 0 sein.", + "interval-start": "Intervallbeginn", + "interval-end": "Intervallende", + "time-unit": "Zeiteinheit", + "fetch-mode": "Abrufmodus", + "order-by-timestamp": "Nach Zeitstempel sortieren", + "limit": "Grenzwert", + "limit-hint": "Min. Wert: 2, Max: 1000. Um einen Eintrag abzurufen, wählen Sie den Abrufmodus 'Erster' oder 'Letzter'.", + "limit-required": "Grenzwert ist erforderlich.", + "limit-range": "Grenzwert muss zwischen 2 und 1000 liegen.", + "time-unit-milliseconds": "Millisekunden", + "time-unit-seconds": "Sekunden", + "time-unit-minutes": "Minuten", + "time-unit-hours": "Stunden", + "time-unit-days": "Tage", + "time-value-range": "Zulässiger Bereich: 1 bis 2147483647.", + "start-interval-value-required": "Intervallbeginn ist erforderlich.", + "end-interval-value-required": "Intervallende ist erforderlich.", + "filter": "Filter", + "switch": "Umschalter", + "math-templatization-tooltip": "Dieses Feld unterstützt Templatisierung. Verwenden Sie $[messageKey] für Nachrichtenwerte und ${metadataKey} für Metadatenwerte.", + "add-message-type": "Nachrichtentyp hinzufügen", + "select-message-types-required": "Mindestens ein Nachrichtentyp muss ausgewählt werden.", + "select-message-types": "Nachrichtentypen auswählen", + "no-message-types-found": "Keine Nachrichtentypen gefunden", + "no-message-type-matching": "'{{messageType}}' nicht gefunden.", + "create-new-message-type": "Neuen erstellen.", + "message-types-required": "Nachrichtentypen sind erforderlich.", + "client-attributes": "Client-Attribute", + "shared-attributes": "Geteilte Attribute", + "server-attributes": "Server-Attribute", + "attributes-keys": "Attributschlüssel", + "attributes-keys-required": "Attributschlüssel sind erforderlich", + "attributes-scope": "Attributbereich", + "attributes-scope-value": "Attributbereichswert", + "attributes-scope-value-copy": "Attributbereichswert kopieren", + "attributes-scope-hint": "Verwenden Sie den Metadatenschlüssel 'scope', um den Attributbereich pro Nachricht dynamisch zu setzen. Überschreibt die Konfiguration.", + "notify-device": "Benachrichtigung an Gerät erzwingen", + "send-attributes-updated-notification": "Benachrichtigung bei aktualisierten Attributen senden", + "send-attributes-updated-notification-hint": "Sendet Benachrichtigung über aktualisierte Attribute als separate Nachricht an die Regelmaschinen-Warteschlange.", + "send-attributes-deleted-notification": "Benachrichtigung bei gelöschten Attributen senden", + "send-attributes-deleted-notification-hint": "Sendet Benachrichtigung über gelöschte Attribute als separate Nachricht an die Regelmaschinen-Warteschlange.", + "update-attributes-only-on-value-change": "Attribute nur bei Wertänderung speichern", + "update-attributes-only-on-value-change-hint": "Aktualisiert Attribute bei jeder eingehenden Nachricht, unabhängig von Wertänderung. Erhöht API-Nutzung und verringert Leistung.", + "update-attributes-only-on-value-change-hint-enabled": "Aktualisiert Attribute nur bei Wertänderung. Keine Aktualisierung von Zeitstempel oder Änderungsbenachrichtigung bei gleichem Wert.", + "fetch-credentials-to-metadata": "Zugangsdaten in Metadaten übernehmen", + "notify-device-on-update-hint": "Wenn aktiviert, erzwingt Benachrichtigung an das Gerät bei Attribut-Update. Bei Deaktivierung erfolgt Steuerung über Metadatenparameter 'notifyDevice'.", + "notify-device-on-delete-hint": "Wenn aktiviert, erzwingt Benachrichtigung an das Gerät bei Attribut-Entfernung. Bei Deaktivierung erfolgt Steuerung über Metadatenparameter 'notifyDevice'.", + "latest-timeseries": "Neueste Zeitreihenschlüssel", + "timeseries-keys": "Zeitreihenschlüssel", + "timeseries-keys-required": "Mindestens ein Zeitreihenschlüssel muss ausgewählt werden.", + "add-timeseries-key": "Zeitreihenschlüssel hinzufügen", + "add-message-field": "Nachrichtenfeld hinzufügen", + "relation-search-parameters": "Beziehungs-Suchparameter", + "relation-parameters": "Beziehungsparameter", + "add-metadata-field": "Metadatenfeld hinzufügen", + "data-keys": "Feldnamen der Nachricht", + "copy-from": "Kopieren von", + "data-to-metadata": "Daten zu Metadaten", + "metadata-to-data": "Metadaten zu Daten", + "use-regular-expression-hint": "Verwenden Sie reguläre Ausdrücke zum Kopieren nach Muster.\n\nTipps:\nEnter = Eingabe abschließen\nBackspace = löschen\nMehrere Felder erlaubt.", + "interval": "Intervall", + "interval-required": "Intervall ist erforderlich", + "interval-hint": "Deduplizierungsintervall in Sekunden.", + "interval-min-error": "Minimal zulässiger Wert ist 1", + "max-pending-msgs": "Max. wartende Nachrichten", + "max-pending-msgs-hint": "Maximale Anzahl an Nachrichten, die im Speicher für jede eindeutige Deduplizierungs-ID gespeichert werden.", + "max-pending-msgs-required": "Max. wartende Nachrichten ist erforderlich", + "max-pending-msgs-max-error": "Maximal zulässiger Wert ist 1000", + "max-pending-msgs-min-error": "Minimal zulässiger Wert ist 1", + "max-retries": "Maximale Wiederholungen", + "max-retries-required": "Maximale Wiederholungen sind erforderlich", + "max-retries-hint": "Maximale Anzahl an Wiederholungen zum Einreihen deduplizierter Nachrichten in die Warteschlange. Verzögerung von 10 Sekunden zwischen den Wiederholungen.", + "max-retries-max-error": "Maximal zulässiger Wert ist 100", + "max-retries-min-error": "Minimal zulässiger Wert ist 0", + "strategy": "Strategie", + "strategy-required": "Strategie ist erforderlich", + "strategy-all-hint": "Gibt alle Nachrichten, die während des Deduplizierungszeitraums eingetroffen sind, als einzelnes JSON-Array zurück. Jedes Element enthält die Eigenschaften msg und metadata.", + "strategy-first-hint": "Gibt die erste Nachricht zurück, die während des Deduplizierungszeitraums eingetroffen ist.", + "strategy-last-hint": "Gibt die letzte Nachricht zurück, die während des Deduplizierungszeitraums eingetroffen ist.", + "first": "Erste", + "last": "Letzte", + "all": "Alle", + "output-msg-type-hint": "Der Nachrichtentyp des Deduplizierungsergebnisses.", + "queue-name-hint": "Name der Warteschlange, in die das Deduplizierungsergebnis veröffentlicht wird.", + "keys": "Schlüssel", + "keys-required": "Schlüssel sind erforderlich", + "rename-keys-in": "Schlüssel umbenennen in", + "data": "Daten", + "message": "Nachricht", + "metadata": "Metadaten", + "current-key-name": "Aktueller Schlüsselname", + "key-name-required": "Schlüsselname ist erforderlich", + "new-key-name": "Neuer Schlüsselname", + "new-key-name-required": "Neuer Schlüsselname ist erforderlich", + "metadata-keys": "Metadatenfeldnamen", + "json-path-expression": "JSONPath-Ausdruck", + "json-path-expression-required": "JSONPath-Ausdruck ist erforderlich", + "json-path-expression-hint": "JSONPath gibt einen Pfad zu einem Element oder einer Menge von Elementen in einer JSON-Struktur an. '$' steht für das Wurzelelement.", + "relations-query": "Beziehungsabfrage", + "device-relations-query": "Gerätebeziehungsabfrage", + "max-relation-level": "Max. Beziehungsebene", + "max-relation-level-error": "Wert muss größer als 0 oder leer sein.", + "max-relation-level-invalid": "Wert muss eine ganze Zahl sein.", + "relation-type": "Beziehungstyp", + "relation-type-pattern": "Beziehungstyp-Muster", + "relation-type-pattern-required": "Beziehungstyp-Muster ist erforderlich", + "relation-types-list": "Zu propagierende Beziehungstypen", + "relation-types-list-hint": "Wenn keine Beziehungstypen ausgewählt sind, werden Alarme ohne Filterung nach Typ propagiert.", + "unlimited-level": "Unbegrenzte Ebene", + "latest-telemetry": "Neueste Telemetriedaten", + "add-telemetry-key": "Telemetrieschlüssel hinzufügen", + "delete-from": "Löschen von", + "use-regular-expression-delete-hint": "Regulären Ausdruck verwenden, um Schlüssel nach Muster zu löschen.\n\nTipps:\nEnter = Feldname abschließen\nBackspace = löschen\nMehrere Felder erlaubt.", + "fetch-into": "Abrufen in", + "attr-mapping": "Attribut-Zuordnung:", + "source-attribute": "Quell-Attributschlüssel", + "source-attribute-required": "Quell-Attributschlüssel ist erforderlich.", + "source-telemetry": "Quell-Telemetrieschlüssel", + "source-telemetry-required": "Quell-Telemetrieschlüssel ist erforderlich.", + "target-key": "Zielschlüssel", + "target-key-required": "Zielschlüssel ist erforderlich.", + "attr-mapping-required": "Mindestens ein Zuordnungseintrag muss angegeben werden.", + "fields-mapping": "Feld-Zuordnung", + "fields-mapping-hint": "Wenn das Nachrichtenfeld auf $entityId gesetzt ist, wird die ID des Absenders in die angegebene Spalte gespeichert.", + "relations-query-config-direction-suffix": "Absender", + "profile-name": "Profilname", + "fetch-circle-parameter-info-from-metadata-hint": "Das Metadatenfeld '{{perimeterKeyName}}' sollte folgendes Format haben: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "Das Metadatenfeld '{{perimeterKeyName}}' sollte folgendes Format haben: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "Verwenden Sie $[messageKey] für Nachrichtenwerte und ${metadataKey} für Metadatenwerte.", + "fields-mapping-required": "Mindestens eine Feldzuordnung muss angegeben werden.", + "at-least-one-field-required": "Mindestens ein Eingabefeld muss einen Wert enthalten.", + "originator-fields-sv-map-hint": "Zielschlüsselfelder unterstützen Templatisierung. Verwenden Sie $[messageKey] oder ${metadataKey}.", + "sv-map-hint": "Nur Zielschlüsselfelder unterstützen Templatisierung. Verwenden Sie $[messageKey] oder ${metadataKey}.", + "source-field": "Quellfeld", + "source-field-required": "Quellfeld ist erforderlich.", + "originator-source": "Absenderquelle", + "new-originator": "Neuer Absender", + "originator-customer": "Kunde", + "originator-tenant": "Mandant", + "originator-related": "Zugehörige Entität", + "originator-alarm-originator": "Alarm-Absender", + "originator-entity": "Entität nach Namensmuster", + "clone-message": "Nachricht duplizieren", + "transform": "Transformieren", + "default-ttl": "Standard-TTL", + "default-ttl-required": "Standard-TTL ist erforderlich.", + "default-ttl-hint": "Der Regelknoten holt die TTL aus den Metadaten der Nachricht. Wenn nicht vorhanden, wird die Konfiguration verwendet. Wert 0 verwendet TTL aus Mandantenprofil.", + "default-ttl-zero-hint": "TTL wird nicht angewendet, wenn der Wert 0 ist.", + "min-default-ttl-message": "Nur minimaler TTL-Wert von 0 ist erlaubt.", + "generation-parameters": "Generierungsparameter", + "message-count": "Limit für generierte Nachrichten (0 = unbegrenzt)", + "message-count-required": "Limit für generierte Nachrichten ist erforderlich.", + "min-message-count-message": "Nur 0 als minimaler Nachrichtenwert erlaubt.", + "period-seconds": "Zeitraum in Sekunden", + "period-seconds-required": "Zeitraum ist erforderlich.", + "generation-frequency-seconds": "Generierungshäufigkeit in Sekunden", + "generation-frequency-required": "Generierungshäufigkeit ist erforderlich.", + "min-generation-frequency-message": "Mindestens 60 Sekunden sind erforderlich.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Muster für Zeitraum in Sekunden verwenden", + "use-metadata-period-in-seconds-patterns-hint": "Wenn ausgewählt, verwendet der Regelknoten ein Zeitraum-Muster in Sekunden aus den Metadaten oder Daten der Nachricht.", + "period-in-seconds-pattern": "Zeitraum-Muster in Sekunden", + "period-in-seconds-pattern-required": "Zeitraum-Muster in Sekunden ist erforderlich", + "min-period-seconds-message": "Mindestens 60 Sekunden als Zeitraum erforderlich.", + "originator": "Absender", + "message-body": "Nachrichteninhalt", + "message-metadata": "Nachrichtenmetadaten", + "generate": "Generieren", + "current-rule-node": "Aktueller Regelknoten", + "current-tenant": "Aktueller Mandant", + "generator-function": "Generatorfunktion", + "test-generator-function": "Generatorfunktion testen", + "generator": "Generator", + "test-filter-function": "Filterfunktion testen", + "test-switch-function": "Switch-Funktion testen", + "test-transformer-function": "Transformationsfunktion testen", + "transformer": "Transformer", + "alarm-create-condition": "Bedingung zur Alarmerstellung", + "test-condition-function": "Bedingungsfunktion testen", + "alarm-clear-condition": "Bedingung zur Alarmauflösung", + "alarm-details-builder": "Alarmdetails-Generator", + "test-details-function": "Detailfunktion testen", + "alarm-type": "Alarmtyp", + "select-entity-types": "Entitätstypen auswählen", + "alarm-type-required": "Alarmtyp ist erforderlich.", + "alarm-severity": "Alarm-Schweregrad", + "alarm-severity-required": "Alarm-Schweregrad ist erforderlich", + "alarm-severity-pattern": "Muster für Alarm-Schweregrad", + "alarm-status-filter": "Alarmstatus-Filter", + "alarm-status-list-empty": "Alarmstatusliste ist leer", + "no-alarm-status-matching": "Kein übereinstimmender Alarmstatus gefunden.", + "propagate": "Alarm an verknüpfte Entitäten weiterleiten", + "propagate-to-owner": "Alarm an Eigentümer (Kunde oder Mandant) weiterleiten", + "propagate-to-tenant": "Alarm an Mandant weiterleiten", + "condition": "Bedingung", + "details": "Details", + "to-string": "In String umwandeln", + "test-to-string-function": "To-String-Funktion testen", + "from-template": "Von", + "from-template-required": "Absender ist erforderlich", + "message-to-metadata": "Nachricht zu Metadaten", + "metadata-to-message": "Metadaten zur Nachricht", + "from-message": "Aus Nachricht", + "from-metadata": "Aus Metadaten", + "to-template": "An", + "to-template-required": "Empfänger-Vorlage ist erforderlich", + "mail-address-list-template-hint": "Kommagetrennte Adressliste. Verwenden Sie ${metadataKey} für Werte aus den Metadaten und $[messageKey] für Werte aus dem Nachrichtentext.", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "Betreff", + "subject-template-required": "Betreffvorlage ist erforderlich", + "body-template": "Inhalt", + "body-template-required": "Inhaltsvorlage ist erforderlich", + "dynamic-mail-body-type": "Dynamischer Nachrichtentyp", + "mail-body-type": "Nachrichtentyp", + "body-type-template": "Vorlage Nachrichtentyp", + "reply-routing-configuration": "Antwort-Routing-Konfiguration", + "rpc-reply-routing-configuration-hint": "Diese Parameter geben die Metadaten-Schlüssel an, die zur Identifikation von Dienst, Sitzung und Anfrage verwendet werden.", + "reply-routing-configuration-hint": "Diese Parameter geben die Metadaten-Schlüssel an, die zur Identifikation von Dienst und Anfrage verwendet werden.", + "request-id-metadata-attribute": "Anfrage-ID", + "service-id-metadata-attribute": "Dienst-ID", + "session-id-metadata-attribute": "Sitzungs-ID", + "timeout-sec": "Timeout in Sekunden", + "timeout-required": "Timeout ist erforderlich", + "min-timeout-message": "Nur Timeout-Wert 0 ist erlaubt.", + "endpoint-url-pattern": "Endpoint-URL-Muster", + "endpoint-url-pattern-required": "Endpoint-URL-Muster ist erforderlich", + "request-method": "Anfragemethode", + "use-simple-client-http-factory": "Einfache HTTP-Client-Fabrik verwenden", + "ignore-request-body": "Ohne Nachrichteninhalt", + "parse-to-plain-text": "In Klartext umwandeln", + "parse-to-plain-text-hint": "Wenn ausgewählt, wird die JSON-Nutzlast in Klartext umgewandelt, z. B. msg = \"Hello,\\t\"world\"\" → Hello, \"world\"", + "read-timeout": "Lese-Timeout in Millisekunden", + "read-timeout-hint": "Wert 0 bedeutet unbegrenztes Timeout", + "max-parallel-requests-count": "Max. Anzahl paralleler Anfragen", + "max-parallel-requests-count-hint": "Wert 0 bedeutet unbegrenzte Parallelität", + "max-response-size": "Maximale Antwortgröße (in KB)", + "max-response-size-hint": "Maximaler Speicher für das Puffern von Daten beim Kodieren/Decodieren von HTTP-Nachrichten (z. B. JSON, XML)", + "headers": "Header", + "headers-hint": "Verwenden Sie ${metadataKey} für Werte aus den Metadaten und $[messageKey] für Werte aus dem Nachrichtentext in Header-/Wert-Feldern.", + "header": "Header", + "header-required": "Header ist erforderlich", + "value": "Wert", + "value-required": "Wert ist erforderlich", + "topic-pattern": "Themenmuster", + "key-pattern": "Schlüsselmuster", + "key-pattern-hint": "Optional. Wenn eine gültige Partitionsnummer angegeben ist, wird sie beim Senden des Datensatzes verwendet. Wenn keine Partition angegeben ist, wird stattdessen der Schlüssel verwendet. Wenn beides nicht angegeben ist, wird die Partition im Round-Robin-Verfahren zugewiesen.", + "topic-pattern-required": "Themenmuster ist erforderlich", + "topic": "Thema", + "topic-required": "Thema ist erforderlich", + "bootstrap-servers": "Bootstrap-Server", + "bootstrap-servers-required": "Bootstrap-Server-Wert ist erforderlich", + "other-properties": "Weitere Eigenschaften", + "key": "Schlüssel", + "key-required": "Schlüssel ist erforderlich", + "retries": "Automatische Wiederholungen bei Fehlern", + "min-retries-message": "Nur 0 Mindestanzahl an Wiederholungen ist erlaubt.", + "batch-size-bytes": "Batchgröße in Bytes", + "min-batch-size-bytes-message": "Nur 0 minimale Batchgröße ist erlaubt.", + "linger-ms": "Lokale Pufferdauer (ms)", + "min-linger-ms-message": "Nur 0 ms als Mindestwert ist erlaubt.", + "buffer-memory-bytes": "Maximale Client-Puffergröße in Bytes", + "min-buffer-memory-message": "Nur 0 minimale Puffergröße ist erlaubt.", + "memory-buffer-size-range": "Die Speicherpuffergröße muss zwischen 0 und {{max}} KB liegen", + "acks": "Anzahl der Bestätigungen", + "topic-arn-pattern": "Thema-ARN-Muster", + "topic-arn-pattern-required": "Thema-ARN-Muster ist erforderlich", + "aws-access-key-id": "AWS-Zugangsschlüssel-ID", + "aws-access-key-id-required": "AWS-Zugangsschlüssel-ID ist erforderlich", + "aws-secret-access-key": "AWS-Geheimer Zugangsschlüssel", + "aws-secret-access-key-required": "AWS-Geheimer Zugangsschlüssel ist erforderlich", + "aws-region": "AWS-Region", + "aws-region-required": "AWS-Region ist erforderlich", + "exchange-name-pattern": "Exchange-Namensmuster", + "routing-key-pattern": "Routing-Schlüssel-Muster", + "message-properties": "Nachrichteneigenschaften", + "host": "Host", + "host-required": "Host ist erforderlich", + "port": "Port", + "port-required": "Port ist erforderlich", + "port-range": "Port muss im Bereich von 1 bis 65535 liegen.", + "virtual-host": "Virtueller Host", + "username": "Benutzername", + "password": "Passwort", + "automatic-recovery": "Automatische Wiederherstellung", + "connection-timeout-ms": "Verbindungs-Timeout (ms)", + "min-connection-timeout-ms-message": "Nur 0 ms als Mindestwert ist erlaubt.", + "handshake-timeout-ms": "Handshake-Timeout (ms)", + "min-handshake-timeout-ms-message": "Nur 0 ms als Mindestwert ist erlaubt.", + "client-properties": "Client-Eigenschaften", + "queue-url-pattern": "Warteschlangen-URL-Muster", + "queue-url-pattern-required": "Warteschlangen-URL-Muster ist erforderlich", + "delay-seconds": "Verzögerung (Sekunden)", + "min-delay-seconds-message": "Nur 0 Sekunden als Mindestwert ist erlaubt.", + "max-delay-seconds-message": "Nur maximal 900 Sekunden sind erlaubt.", + "name": "Name", + "name-required": "Name ist erforderlich", + "queue-type": "Warteschlangentyp", + "sqs-queue-standard": "Standard", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "GCP-Projekt-ID", + "gcp-project-id-required": "GCP-Projekt-ID ist erforderlich", + "gcp-service-account-key": "GCP-Dienstkonto-Schlüsseldatei", + "gcp-service-account-key-required": "GCP-Dienstkonto-Schlüsseldatei ist erforderlich", + "pubsub-topic-name": "Themenname", + "pubsub-topic-name-required": "Themenname ist erforderlich", + "message-attributes": "Nachrichtenattribute", + "message-attributes-hint": "Verwende ${metadataKey} für Werte aus den Metadaten, $[messageKey] für Werte aus dem Nachrichteninhalt in Name/Wert-Feldern", + "connect-timeout": "Verbindungs-Timeout (Sek)", + "connect-timeout-required": "Verbindungs-Timeout ist erforderlich.", + "connect-timeout-range": "Verbindungs-Timeout muss im Bereich von 1 bis 200 liegen.", + "client-id": "Client-ID", + "client-id-hint": "Optional. Leer lassen für automatisch generierte Client-ID. Vorsicht beim Festlegen der Client-ID: Die meisten MQTT-Broker erlauben keine mehrfachen Verbindungen mit derselben Client-ID. Um mit solchen Brokern zu verbinden, muss deine MQTT-Client-ID eindeutig sein. Wenn die Plattform im Microservice-Modus betrieben wird, wird eine Kopie des Regelknotens in jedem Microservice gestartet. Dies führt automatisch zu mehreren MQTT-Clients mit derselben ID und kann zu Fehlern führen. Um dies zu vermeiden, aktiviere die Option „Dienst-ID als Suffix zur Client-ID hinzufügen“ unten.", + "append-client-id-suffix": "Dienst-ID als Suffix zur Client-ID hinzufügen", + "client-id-suffix-hint": "Optional. Wird angewendet, wenn die „Client-ID“ explizit angegeben wurde. Falls ausgewählt, wird die Dienst-ID als Suffix zur Client-ID hinzugefügt. Hilft Fehler zu vermeiden, wenn die Plattform im Microservice-Modus läuft.", + "device-id": "Geräte-ID", + "device-id-required": "Geräte-ID ist erforderlich.", + "clean-session": "Saubere Sitzung", + "enable-ssl": "SSL aktivieren", + "credentials": "Anmeldedaten", + "credentials-type": "Anmeldedatentyp", + "credentials-type-required": "Anmeldedatentyp ist erforderlich.", + "credentials-anonymous": "Anonym", + "credentials-basic": "Einfach", + "credentials-pem": "PEM", + "credentials-pem-hint": "Mindestens Server-CA-Zertifikat oder ein Paar aus Client-Zertifikat und privatem Client-Schlüssel erforderlich", + "credentials-sas": "Gemeinsam genutzte Zugriffssignatur (SAS)", + "sas-key": "SAS-Schlüssel", + "sas-key-required": "SAS-Schlüssel ist erforderlich.", + "hostname": "Hostname", + "hostname-required": "Hostname ist erforderlich.", + "azure-ca-cert": "CA-Zertifikatsdatei", + "username-required": "Benutzername ist erforderlich.", + "password-required": "Passwort ist erforderlich.", + "ca-cert": "Server-CA-Zertifikatsdatei", + "private-key": "Privater Client-Schlüssel", + "cert": "Client-Zertifikat", + "no-file": "Keine Datei ausgewählt.", + "drop-file": "Datei ablegen oder klicken, um eine Datei hochzuladen.", + "private-key-password": "Passwort für privaten Schlüssel", + "use-system-smtp-settings": "Systemweite SMTP-Einstellungen verwenden", + "use-metadata-dynamic-interval": "Dynamisches Intervall verwenden", + "metadata-dynamic-interval-hint": "Eingabefelder für Start- und Endzeitpunkt unterstützen Templating. Beachten Sie, dass der ersetzte Template-Wert in Millisekunden angegeben werden muss. Verwenden Sie $[messageKey] für Werte aus der Nachricht und ${metadataKey} für Werte aus den Metadaten.", + "use-metadata-interval-patterns-hint": "Wenn ausgewählt, verwendet der Regelknoten Start- und Endintervallmuster aus den Nachrichtendaten oder Metadaten, wobei angenommen wird, dass die Intervalle in Millisekunden angegeben sind.", + "use-message-alarm-data": "Alarminformationen aus Nachricht verwenden", + "overwrite-alarm-details": "Alarmdetails überschreiben", + "use-alarm-severity-pattern": "Alarm-Schweregrad-Muster verwenden", + "check-all-keys": "Überprüfen, ob alle angegebenen Felder vorhanden sind", + "check-all-keys-hint": "Wenn ausgewählt, wird geprüft, ob alle angegebenen Schlüssel in den Nachrichtendaten und Metadaten vorhanden sind.", + "check-relation-to-specific-entity": "Beziehung zu einer bestimmten Entität überprüfen", + "check-relation-to-specific-entity-tooltip": "Wenn aktiviert, wird die Existenz einer Beziehung zu einer bestimmten Entität überprüft. Andernfalls wird das Vorhandensein einer Beziehung zu einer beliebigen Entität überprüft. In beiden Fällen erfolgt die Beziehungssuche anhand von Richtung und Typ.", + "check-relation-hint": "Überprüft das Vorhandensein einer Beziehung zu einer bestimmten oder beliebigen Entität basierend auf Richtung und Beziehungstyp.", + "delete-relation-with-specific-entity": "Beziehung mit spezifischer Entität löschen", + "delete-relation-with-specific-entity-hint": "Wenn aktiviert, wird nur die Beziehung zu einer spezifischen Entität gelöscht. Andernfalls werden alle passenden Beziehungen entfernt.", + "delete-relation-hint": "Löscht die Beziehung vom Ursprung der eingehenden Nachricht zur angegebenen Entität oder Entitätenliste basierend auf Richtung und Typ.", + "remove-current-relations": "Aktuelle Beziehungen entfernen", + "remove-current-relations-hint": "Entfernt aktuelle Beziehungen vom Ursprung der eingehenden Nachricht basierend auf Richtung und Typ.", + "change-originator-to-related-entity": "Ursprung zur verbundenen Entität ändern", + "change-originator-to-related-entity-hint": "Verwendet zur Verarbeitung der eingereichten Nachricht als käme sie von einer anderen Entität.", + "start-interval": "Intervallstart", + "end-interval": "Intervallende", + "start-interval-required": "Intervallstart ist erforderlich.", + "end-interval-required": "Intervallende ist erforderlich.", + "smtp-protocol": "Protokoll", + "smtp-host": "SMTP-Host", + "smtp-host-required": "SMTP-Host ist erforderlich.", + "smtp-port": "SMTP-Port", + "smtp-port-required": "SMTP-Port muss angegeben werden.", + "smtp-port-range": "SMTP-Port muss im Bereich von 1 bis 65535 liegen.", + "timeout-msec": "Timeout in ms", + "min-timeout-msec-message": "Nur 0 ms als Mindestwert ist erlaubt.", + "enter-username": "Benutzernamen eingeben", + "enter-password": "Passwort eingeben", + "enable-tls": "TLS aktivieren", + "tls-version": "TLS-Version", + "enable-proxy": "Proxy aktivieren", + "use-system-proxy-properties": "System-Proxy-Eigenschaften verwenden", + "proxy-host": "Proxy-Host", + "proxy-host-required": "Proxy-Host ist erforderlich.", + "proxy-port": "Proxy-Port", + "proxy-port-required": "Proxy-Port ist erforderlich.", + "proxy-port-range": "Proxy-Port muss im Bereich von 1 bis 65535 liegen.", + "proxy-user": "Proxy-Benutzer", + "proxy-password": "Proxy-Passwort", + "proxy-scheme": "Proxy-Schema", + "numbers-to-template": "Telefonnummern zum Template", + "numbers-to-template-required": "Telefonnummern zum Template sind erforderlich", + "numbers-to-template-hint": "Kommagetrennte Telefonnummern. Verwenden Sie ${metadataKey} für Werte aus Metadaten und $[messageKey] für Werte aus dem Nachrichtentext", + "sms-message-template": "SMS-Nachrichtenvorlage", + "sms-message-template-required": "SMS-Nachrichtenvorlage ist erforderlich", + "use-system-sms-settings": "Systemweite SMS-Anbietereinstellungen verwenden", + "min-period-0-seconds-message": "Nur 0 Sekunden Mindestzeitraum ist erlaubt.", + "max-pending-messages": "Maximale ausstehende Nachrichten", + "max-pending-messages-required": "Maximale ausstehende Nachrichten sind erforderlich.", + "max-pending-messages-range": "Maximale ausstehende Nachrichten müssen im Bereich von 1 bis 100000 liegen.", + "originator-types-filter": "Filter für Ursprungstypen", + "interval-seconds": "Intervall in Sekunden", + "interval-seconds-required": "Intervall ist erforderlich.", + "int-range": "Wert darf den maximalen Integer-Grenzwert (2147483648) nicht überschreiten", + "min-interval-seconds-message": "Nur 1 Sekunde Mindestintervall ist erlaubt.", + "output-timeseries-key-prefix": "Präfix des Zeitreihen-Schlüssels für Ausgabe", + "output-timeseries-key-prefix-required": "Präfix des Zeitreihen-Schlüssels für Ausgabe ist erforderlich.", + "separator-hint": "Drücken Sie \"Enter\", um die Eingabe abzuschließen.", + "select-details": "Details auswählen", + "entity-details-id": "ID", + "entity-details-title": "Titel", + "entity-details-country": "Land", + "entity-details-state": "Bundesland", + "entity-details-city": "Stadt", + "entity-details-zip": "PLZ", + "entity-details-address": "Adresse", + "entity-details-address2": "Adresse 2", + "entity-details-additional_info": "Zusätzliche Info", + "entity-details-phone": "Telefon", + "entity-details-email": "E-Mail", + "email-sender": "E-Mail-Absender", + "fields-to-check": "Zu überprüfende Felder", + "add-detail": "Detail hinzufügen", + "check-all-keys-tooltip": "Wenn aktiviert, überprüft das Vorhandensein aller in den Nachrichtendaten und Metadaten aufgeführten Felder innerhalb der eingehenden Nachricht.", + "fields-to-check-hint": "Drücken Sie \"Enter\", um die Eingabe eines Feldnamens abzuschließen. Mehrere Feldnamen werden unterstützt.", + "entity-details-list-empty": "Mindestens ein Detail muss ausgewählt werden.", + "alarm-status": "Alarmstatus", + "alarm-required": "Mindestens ein Alarmstatus muss ausgewählt werden.", + "no-entity-details-matching": "Keine passenden Entitätsdetails gefunden.", + "custom-table-name": "Benutzerdefinierter Tabellenname", + "custom-table-name-required": "Tabellenname ist erforderlich", + "custom-table-hint": "Die Tabelle muss in Ihrem Cassandra-Cluster erstellt sein und mit dem Präfix 'cs_tb_' beginnen, um das Einfügen in allgemeine TB-Tabellen zu vermeiden. Geben Sie hier den Tabellennamen ohne das Präfix 'cs_tb_' ein.", + "message-field": "Nachrichtenfeld", + "message-field-required": "Nachrichtenfeld ist erforderlich.", + "table-col": "Tabellenspalte", + "table-col-required": "Tabellenspalte ist erforderlich.", + "latitude-field-name": "Breitengrad-Feldname", + "longitude-field-name": "Längengrad-Feldname", + "latitude-field-name-required": "Breitengrad-Feldname ist erforderlich.", + "longitude-field-name-required": "Längengrad-Feldname ist erforderlich.", + "fetch-perimeter-info-from-metadata": "Perimeterinformationen aus Metadaten abrufen", + "fetch-perimeter-info-from-metadata-tooltip": "Wenn der Perimetertyp auf 'Polygon' gesetzt ist, wird der Wert des Metadatenfelds '{{perimeterKeyName}}' direkt als Definition verwendet, ohne zusätzliche Analyse. Wenn der Typ auf 'Kreis' gesetzt ist, wird der Wert von '{{perimeterKeyName}}' analysiert, um 'latitude', 'longitude', 'radius' und 'radiusUnit' für die Kreisdefinition zu extrahieren.", + "perimeter-key-name": "Perimeter-Schlüsselname", + "perimeter-key-name-hint": "Metadatenfeldname, der die Perimeterinformationen enthält.", + "perimeter-key-name-required": "Perimeter-Schlüsselname ist erforderlich.", + "perimeter-circle": "Kreis", + "perimeter-polygon": "Polygon", + "perimeter-type": "Perimetertyp", + "circle-center-latitude": "Breite des Kreismittelpunkts", + "circle-center-latitude-required": "Breite des Kreismittelpunkts ist erforderlich.", + "circle-center-longitude": "Länge des Kreismittelpunkts", + "circle-center-longitude-required": "Länge des Kreismittelpunkts ist erforderlich.", + "range-unit-meter": "Meter", + "range-unit-kilometer": "Kilometer", + "range-unit-foot": "Fuß", + "range-unit-mile": "Meile", + "range-unit-nautical-mile": "Seemeile", + "range-units": "Entfernungseinheiten", + "range-units-required": "Entfernungseinheiten sind erforderlich.", + "range": "Entfernung", + "range-required": "Entfernung ist erforderlich.", + "polygon-definition": "Polygon-Definition", + "polygon-definition-required": "Polygon-Definition ist erforderlich.", + "polygon-definition-hint": "Verwenden Sie folgendes Format zur manuellen Definition eines Polygons: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration": "Minimale Verweildauer innen", + "min-inside-duration-value-required": "Minimale Verweildauer innen ist erforderlich", + "min-inside-duration-time-unit": "Zeiteinheit der minimalen Verweildauer innen", + "min-outside-duration": "Minimale Verweildauer außen", + "min-outside-duration-value-required": "Minimale Verweildauer außen ist erforderlich", + "min-outside-duration-time-unit": "Zeiteinheit der minimalen Verweildauer außen", + "tell-failure-if-absent": "Fehler melden", + "tell-failure-if-absent-hint": "Wenn mindestens ein ausgewählter Schlüssel nicht vorhanden ist, wird die ausgehende Nachricht mit \"Fehler\" gekennzeichnet.", + "get-latest-value-with-ts": "Zeitstempel für letzte Telemetriewerte abrufen", + "get-latest-value-with-ts-hint": "Wenn ausgewählt, enthalten die letzten Telemetriewerte auch einen Zeitstempel, z. B.: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings": "Leere Zeichenfolgen ignorieren", + "ignore-null-strings-hint": "Wenn ausgewählt, ignoriert der Regelknoten Entitätsfelder mit leerem Wert.", + "add-metadata-key-values-as-kafka-headers": "Metadaten-Schlüssel-Werte-Paare als Kafka-Header hinzufügen", + "add-metadata-key-values-as-kafka-headers-hint": "Wenn ausgewählt, werden Schlüssel-Wert-Paare aus den Nachrichtemetadaten als Byte-Arrays mit vordefinierter Zeichenkodierung zu den ausgehenden Kafka-Headern hinzugefügt.", + "charset-encoding": "Zeichenkodierung", + "charset-encoding-required": "Zeichenkodierung ist erforderlich.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "Der Warteschlangenname kann aus einer Dropdown-Liste ausgewählt oder benutzerdefiniert eingegeben werden.", + "device-profile-node-hint": "Nützlich bei Dauer- oder Wiederholungsbedingungen, um die Alarmzustandsbewertung fortlaufend sicherzustellen.", + "persist-alarm-rules": "Alarmregelzustand speichern", + "persist-alarm-rules-hint": "Wenn aktiviert, speichert der Regelknoten den Verarbeitungszustand in der Datenbank.", + "fetch-alarm-rules": "Alarmregelzustand abrufen", + "fetch-alarm-rules-hint": "Wenn aktiviert, stellt der Regelknoten beim Start den Verarbeitungszustand wieder her, um sicherzustellen, dass Alarme auch nach Neustarts ausgelöst werden. Andernfalls erfolgt die Wiederherstellung beim Eintreffen der ersten Nachricht.", + "input-value-key": "Eingabewert-Schlüssel", + "input-value-key-required": "Eingabewert-Schlüssel ist erforderlich.", + "output-value-key": "Ausgabewert-Schlüssel", + "output-value-key-required": "Ausgabewert-Schlüssel ist erforderlich.", + "number-of-digits-after-floating-point": "Anzahl der Stellen nach dem Dezimalpunkt", + "number-of-digits-after-floating-point-range": "Anzahl der Stellen nach dem Dezimalpunkt muss zwischen 0 und 15 liegen.", + "failure-if-delta-negative": "Fehler melden, wenn Differenz negativ ist", + "failure-if-delta-negative-tooltip": "Regelknoten erzwingt Fehler bei der Nachrichtenverarbeitung, wenn der Delta-Wert negativ ist.", + "use-caching": "Caching verwenden", + "use-caching-tooltip": "Regelknoten cached den Wert von \"{{inputValueKey}}\" aus der eingehenden Nachricht zur Leistungsverbesserung. Änderungen an \"{{inputValueKey}}\" außerhalb dieses Knotens aktualisieren den Cache nicht.", + "add-time-difference-between-readings": "Zeitdifferenz zwischen \"{{inputValueKey}}\"-Werten hinzufügen", + "add-time-difference-between-readings-tooltip": "Wenn aktiviert, fügt der Regelknoten den Schlüssel \"{{periodValueKey}}\" zur ausgehenden Nachricht hinzu.", + "period-value-key": "Zeitraum-Wert-Schlüssel", + "period-value-key-required": "Zeitraum-Wert-Schlüssel ist erforderlich.", + "general-pattern-hint": "Verwenden Sie ${metadataKey} für Metadatenwert, $[messageKey] für Nachrichtenwert.", + "alarm-severity-pattern-hint": "Verwenden Sie ${metadataKey} für Metadatenwert, $[messageKey] für Nachrichtenwert. Alarm-Schweregrade sollten systemdefiniert sein (CRITICAL, MAJOR usw.)", + "output-node-name-hint": "Der Name des Regelknotens entspricht dem Beziehungstyp der Ausgabemeldung und wird verwendet, um Nachrichten an andere Knoten in der Regelkette weiterzuleiten.", + "use-server-ts": "Server-Zeitstempel verwenden", + "use-server-ts-hint": "Verwenden Sie den aktuellen Zeitstempel des Servers für Zeitreihen ohne expliziten Zeitstempel. Dies hilft, die Reihenfolge bei mehreren Quellen oder verspäteten Nachrichten zu wahren.", + "kv-map-pattern-hint": "Alle Eingabefelder unterstützen Templating. Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "kv-map-single-pattern-hint": "Eingabefeld unterstützt Templating. Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "shared-scope": "Geteilter Bereich", + "server-scope": "Server-Bereich", + "client-scope": "Client-Bereich", + "attribute-type": "Attribut", + "attribute-type-description": "Attributwert aus der Datenbank abrufen", + "attribute-type-result-description": "Ergebnis als Entitätsattribut in der Datenbank speichern", + "constant-type": "Konstante", + "constant-type-description": "Konstantenwert definieren", + "time-series-type": "Zeitreihe", + "time-series-type-description": "Letzten Zeitreihenwert aus Datenbank abrufen", + "time-series-type-result-description": "Ergebnis als Zeitreihe in der Datenbank speichern", + "message-body-type": "Nachricht", + "message-body-type-description": "Argumentwert aus Nachrichtentext abrufen", + "message-body-type-result-description": "Ergebnis zur Nachricht hinzufügen", + "message-metadata-type": "Metadaten", + "message-metadata-type-description": "Argumentwert aus Nachrichtenmetadaten abrufen", + "message-metadata-result-description": "Ergebnis zu Nachrichtenmetadaten hinzufügen", + "argument-tile": "Argumente", + "no-arguments-prompt": "Keine Argumente konfiguriert", + "result-title": "Ergebnis", + "functions-field-input": "Funktionen", + "no-option-found": "Keine Option gefunden", + "argument-source-field-input": "Quelle", + "argument-source-field-input-required": "Quellenangabe für Argument ist erforderlich.", + "argument-key-field-input": "Schlüssel", + "argument-key-field-input-required": "Argumentschlüssel ist erforderlich.", + "constant-value-field-input": "Konstantenwert", + "constant-value-field-input-required": "Konstantenwert ist erforderlich.", + "attribute-scope-field-input": "Attributbereich", + "attribute-scope-field-input-required": "Attributbereich ist erforderlich.", + "default-value-field-input": "Standardwert", + "type-field-input": "Typ", + "type-field-input-required": "Typ ist erforderlich.", + "key-field-input": "Schlüssel", + "add-entity-type": "Entitätstyp hinzufügen", + "add-device-profile": "Geräteprofil hinzufügen", + "key-field-input-required": "Schlüssel ist erforderlich.", + "number-floating-point-field-input": "Anzahl Dezimalstellen", + "number-floating-point-field-input-hint": "Verwenden Sie 0, um das Ergebnis in eine Ganzzahl umzuwandeln", + "add-to-message-field-input": "Zur Nachricht hinzufügen", + "add-to-metadata-field-input": "Zu Metadaten hinzufügen", + "custom-expression-field-input": "Mathematischer Ausdruck", + "custom-expression-field-input-required": "Mathematischer Ausdruck ist erforderlich", + "custom-expression-field-input-hint": "Geben Sie einen mathematischen Ausdruck zur Auswertung an. Der Standardausdruck zeigt die Umrechnung von Fahrenheit in Celsius.", + "retained-message": "Beibehalten", + "attributes-mapping": "Attribut-Zuordnung", + "latest-telemetry-mapping": "Neueste Telemetrie-Zuordnung", + "add-mapped-attribute-to": "Zuordnung zu Attributen hinzufügen", + "add-mapped-latest-telemetry-to": "Zuordnung zu neuester Telemetrie hinzufügen", + "add-mapped-fields-to": "Zuordnung zu Feldern hinzufügen", + "add-selected-details-to": "Ausgewählte Details hinzufügen zu", + "clear-selected-types": "Ausgewählte Typen löschen", + "clear-selected-details": "Ausgewählte Details löschen", + "clear-selected-fields": "Ausgewählte Felder löschen", + "clear-selected-keys": "Ausgewählte Schlüssel löschen", + "geofence-configuration": "Geofence-Konfiguration", + "coordinate-field-names": "Koordinaten-Feldnamen", + "coordinate-field-hint": "Der Regelknoten versucht, die angegebenen Felder aus der Nachricht abzurufen. Wenn diese nicht vorhanden sind, werden sie in den Metadaten gesucht.", + "presence-monitoring-strategy": "Anwesenheitsüberwachungsstrategie", + "presence-monitoring-strategy-on-first-message": "Bei erster Nachricht", + "presence-monitoring-strategy-on-each-message": "Bei jeder Nachricht", + "presence-monitoring-strategy-on-first-message-hint": "Meldet den Anwesenheitsstatus 'Innen' oder 'Außen' bei der ersten Nachricht, nachdem die konfigurierte Mindestdauer seit dem letzten Status 'Betreten' oder 'Verlassen' vergangen ist.", + "presence-monitoring-strategy-on-each-message-hint": "Meldet den Anwesenheitsstatus 'Innen' oder 'Außen' bei jeder Nachricht nach dem Statuswechsel 'Betreten' oder 'Verlassen'.", + "fetch-credentials-to": "Anmeldedaten abrufen zu", + "add-originator-attributes-to": "Ursprungsattribute hinzufügen zu", + "originator-attributes": "Ursprungsattribute", + "fetch-latest-telemetry-with-timestamp": "Neueste Telemetrie mit Zeitstempel abrufen", + "fetch-latest-telemetry-with-timestamp-tooltip": "Wenn ausgewählt, werden die neuesten Telemetriewerte mit Zeitstempel zu den ausgehenden Metadaten hinzugefügt, z. B.: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure": "Fehler melden, wenn Attribute fehlen", + "tell-failure-tooltip": "Wenn mindestens ein ausgewählter Schlüssel fehlt, wird die ausgehende Nachricht mit 'Fehler' markiert.", + "created-time": "Erstellungszeit", + "chip-help": "Drücken Sie 'Enter', um die Eingabe von {{inputName}} abzuschließen.\nDrücken Sie 'Backspace', um {{inputName}} zu löschen.\nMehrere Werte werden unterstützt.", + "detail": "Detail", + "field-name": "Feldname", + "device-profile": "Geräteprofil", + "entity-type": "Entitätstyp", + "message-type": "Nachrichtentyp", + "timeseries-key": "Zeitreihen-Schlüssel", + "type": "Typ", + "first-name": "Vorname", + "last-name": "Nachname", + "label": "Bezeichnung", + "originator-fields-mapping": "Ursprungsfeld-Zuordnung", + "add-mapped-originator-fields-to": "Zugeordnete Ursprungsfelder hinzufügen zu", + "fields": "Felder", + "skip-empty-fields": "Leere Felder überspringen", + "skip-empty-fields-tooltip": "Felder mit leerem Wert werden nicht zur Ausgabemeldung/Metadaten hinzugefügt.", + "fetch-interval": "Abrufintervall", + "fetch-strategy": "Abrufstrategie", + "fetch-timeseries-from-to": "Zeitreihe abrufen von {{startInterval}} {{startIntervalTimeUnit}} bis {{endInterval}} {{endIntervalTimeUnit}} zuvor.", + "fetch-timeseries-from-to-invalid": "Ungültiger Zeitraum für Zeitreihenabruf (\"Intervallstart\" muss kleiner als \"Intervallende\" sein).", + "use-metadata-dynamic-interval-tooltip": "Wenn aktiviert, verwendet der Regelknoten ein dynamisches Start- und Endintervall basierend auf Nachrichten- und Metadatenmustern.", + "all-mode-hint": "Bei Auswahl des Modus \"Alle\" ruft der Regelknoten Telemetriedaten im Intervall mit konfigurierbaren Abfrageparametern ab.", + "first-mode-hint": "Bei Auswahl des Modus \"Erste\" ruft der Regelknoten die Telemetrie am nächsten zum Intervallbeginn ab.", + "last-mode-hint": "Bei Auswahl des Modus \"Letzte\" ruft der Regelknoten die Telemetrie am nächsten zum Intervallende ab.", + "ascending": "Aufsteigend", + "descending": "Absteigend", + "min": "Minimum", + "max": "Maximum", + "average": "Durchschnitt", + "sum": "Summe", + "count": "Anzahl", + "none": "Keine", + "last-level-relation-tooltip": "Wenn ausgewählt, sucht der Regelknoten nur auf der im maximalen Beziehungsebenenwert angegebenen Ebene nach zugehörigen Entitäten.", + "last-level-device-relation-tooltip": "Wenn ausgewählt, sucht der Regelknoten nur auf der im maximalen Beziehungsebenenwert angegebenen Ebene nach zugehörigen Geräten.", + "data-to-fetch": "Daten zum Abrufen", + "mapping-of-customers": "Zuordnung des Kunden", + "map-fields-required": "Alle Zuordnungsfelder sind erforderlich.", + "attributes": "Attribute", + "related-device-attributes": "Attribute zugehöriger Geräte", + "add-selected-attributes-to": "Ausgewählte Attribute hinzufügen zu", + "device-profiles": "Geräteprofile", + "mapping-of-tenant": "Zuordnung des Mieters", + "add-attribute-key": "Attributschlüssel hinzufügen", + "message-template": "Nachrichtenvorlage", + "message-template-required": "Nachrichtenvorlage ist erforderlich", + "use-system-slack-settings": "Systemweite Slack-Einstellungen verwenden", + "slack-api-token": "Slack-API-Token", + "slack-api-token-required": "Slack-API-Token ist erforderlich", + "keys-mapping": "Schlüsselzuordnung", + "add-key": "Schlüssel hinzufügen", + "recipients": "Empfänger", + "message-subject-and-content": "Nachrichtenbetreff und -inhalt", + "template-rules-hint": "Beide Felder unterstützen Templating. Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "originator-customer-desc": "Verwende Kunde des Nachrichtenursprungs als neuen Ursprung.", + "originator-tenant-desc": "Verwende aktuellen Mieter als neuen Ursprung.", + "originator-related-entity-desc": "Verwende zugehörige Entität als neuen Ursprung. Suche basiert auf Relationstyp und Richtung.", + "originator-alarm-originator-desc": "Verwende Alarmursprung als neuen Ursprung. Gilt nur, wenn Nachrichtenursprung eine Alarmentität ist.", + "originator-entity-by-name-pattern-desc": "Verwende Entität aus der DB als neuen Ursprung. Suche basiert auf Entitätstyp und Namensmuster.", + "email-from-template-hint": "Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "recipients-block-main-hint": "Kommagetrennte Adressliste. Alle Felder unterstützen Templating. Verwenden Sie $[messageKey] für Nachrichtendaten und ${metadataKey} für Metadaten.", + "forward-msg-default-rule-chain": "Nachricht an Standard-Regelkette des Ursprungs weiterleiten", + "forward-msg-default-rule-chain-tooltip": "Wenn aktiviert, wird die Nachricht an die Standard-Regelkette des Ursprungs weitergeleitet, oder eine konfigurierte Kette, wenn keine Standardkette definiert ist.", + "exclude-zero-deltas": "Null-Deltas von Nachricht ausschließen", + "exclude-zero-deltas-hint": "Wenn aktiviert, wird der Ausgabeschlüssel \"{{outputValueKey}}\" nur hinzugefügt, wenn der Wert ungleich null ist.", + "exclude-zero-deltas-time-difference-hint": "Wenn aktiviert, werden \"{{outputValueKey}}\" und \"{{periodValueKey}}\" nur hinzugefügt, wenn der Wert von \"{{outputValueKey}}\" ungleich null ist.", + "search-direction-from": "Vom Ursprung zur Zielentität", + "search-direction-to": "Von Zielentität zum Ursprung", + "del-relation-direction-from": "Vom Ursprung", + "del-relation-direction-to": "Zum Ursprung", + "target-entity": "Zielentität", + "function-configuration": "Funktionskonfiguration", + "function-name": "Funktionsname", + "function-name-required": "Funktionsname ist erforderlich.", + "qualifier": "Qualifier", + "qualifier-hint": "Wenn kein Qualifier angegeben ist, wird der Standard-Qualifier \"$LATEST\" verwendet.", + "aws-credentials": "AWS-Zugangsdaten", + "connection-timeout": "Verbindungs-Timeout", + "connection-timeout-required": "Verbindungs-Timeout ist erforderlich.", + "connection-timeout-min": "Minimales Verbindungs-Timeout ist 0.", + "connection-timeout-hint": "Die Wartezeit in Sekunden beim Verbindungsaufbau, bevor abgebrochen wird. Ein Wert von 0 bedeutet unendlich und wird nicht empfohlen.", + "request-timeout": "Anfrage-Timeout", + "request-timeout-required": "Anfrage-Timeout ist erforderlich.", + "request-timeout-min": "Minimales Anfrage-Timeout ist 0.", + "request-timeout-hint": "Die Wartezeit in Sekunden, bis die Anfrage abgeschlossen ist, bevor abgebrochen wird. Ein Wert von 0 bedeutet unendlich und wird nicht empfohlen.", + "units": "Einheiten", + "tell-failure-aws-lambda": "Fehler melden, wenn AWS Lambda Ausnahme auslöst", + "tell-failure-aws-lambda-hint": "Der Regelknoten erzwingt einen Fehler, wenn die AWS Lambda-Ausführung eine Ausnahme auslöst.", + "basic-mode": "Einfach", + "advanced-mode": "Erweitert", + "save-time-series": { + "processing-settings": "Verarbeitungseinstellungen", + "processing-settings-hint": "Definiert, wie eingehende Nachrichten verarbeitet werden. Einfache Einstellungen bieten vorkonfigurierte Strategien, während erweiterte Einstellungen individuelle Strategien je Aktion erlauben.", + "advanced-settings-hint": "Seien Sie vorsichtig bei der Konfiguration der Strategien. Bestimmte Kombinationen können zu unerwartetem Verhalten führen.", + "strategy": "Strategie", + "deduplication-interval": "Deduplizierungsintervall", + "deduplication-interval-required": "Deduplizierungsintervall ist erforderlich.", + "deduplication-interval-min-max-range": "Deduplizierungsintervall muss zwischen 1 Sekunde und 1 Tag liegen.", + "strategy-type": { + "every-message": "Bei jeder Nachricht", + "skip": "Überspringen", + "deduplicate": "Deduplizieren", + "web-sockets-only": "Nur WebSockets" + }, + "time-series": "Zeitreihe", + "latest": "Neueste Werte", + "web-sockets": "WebSockets", + "calculated-fields": "Berechnete Felder" + }, + "save-attribute": { + "processing-settings": "Verarbeitungseinstellungen", + "processing-settings-hint": "Definiert, wie eingehende Nachrichten verarbeitet werden. Einfache Einstellungen bieten vorkonfigurierte Strategien, während erweiterte Einstellungen individuelle Strategien je Aktion erlauben.", + "advanced-settings-hint": "Seien Sie vorsichtig bei der Konfiguration der Strategien. Bestimmte Kombinationen können zu unerwartetem Verhalten führen.", + "strategy": "Strategie", + "deduplication-interval": "Deduplizierungsintervall", + "deduplication-interval-required": "Deduplizierungsintervall ist erforderlich.", + "deduplication-interval-min-max-range": "Deduplizierungsintervall muss zwischen 1 Sekunde und 1 Tag liegen.", + "scope": "Bereich", + "strategy-type": { + "every-message": "Bei jeder Nachricht", + "skip": "Überspringen", + "deduplicate": "Deduplizieren", + "web-sockets-only": "Nur WebSockets" + }, + "attributes": "Attribute" + }, + "key-val": { + "key": "Schlüssel", + "value": "Wert", + "see-examples": "Beispiele anzeigen.", + "remove-entry": "Eintrag entfernen", + "remove-mapping-entry": "Zuordnungseintrag entfernen", + "add-mapping-entry": "Zuordnung hinzufügen", + "add-entry": "Eintrag hinzufügen", + "copy-key-values-from": "Schlüssel-Werte von kopieren", + "delete-key-values": "Schlüssel-Werte löschen", + "delete-key-values-from": "Schlüssel-Werte von löschen", + "at-least-one-key-error": "Mindestens ein Schlüssel muss ausgewählt werden.", + "unique-key-value-pair-error": "'{{keyText}}' muss sich von '{{valText}}' unterscheiden!" + }, + "mail-body-types": { + "plain-text": "Reiner Text", + "html": "HTML", + "dynamic": "Dynamisch", + "use-body-type-template": "Body-Typ-Template verwenden", + "plain-text-description": "Einfacher, unformatierter Text ohne besondere Gestaltung.", + "html-text-description": "Ermöglicht HTML-Tags zur Formatierung, für Links und Bilder im Nachrichtentext.", + "dynamic-text-description": "Erlaubt die dynamische Verwendung von reinem Text oder HTML basierend auf der Templating-Funktion.", + "after-template-evaluation-hint": "Nach der Template-Auswertung muss der Wert true für HTML und false für reinen Text sein." + } + }, + "timezone": { + "timezone": "Zeitzone", + "select-timezone": "Zeitzone auswählen", + "no-timezones-matching": "Keine passenden Zeitzonen für '{{timezone}}' gefunden.", + "timezone-required": "Zeitzone ist erforderlich.", + "browser-time": "Browserzeit" + }, + "queue": { + "queue-name": "Warteschlange", + "no-queues-found": "Keine Warteschlangen gefunden.", + "no-queues-matching": "Keine passenden Warteschlangen für '{{queue}}' gefunden.", + "select-name": "Warteschlangennamen auswählen", + "name": "Name", + "name-required": "Warteschlangenname ist erforderlich!", + "name-unique": "Warteschlangenname ist nicht eindeutig!", + "name-pattern": "Warteschlangenname enthält ungültige Zeichen (nur ASCII-Buchstaben, Ziffern, '.', '_' und '-' erlaubt)!", + "queue-required": "Warteschlange ist erforderlich!", + "topic-required": "Thema der Warteschlange ist erforderlich!", + "poll-interval-required": "Abfrageintervall ist erforderlich!", + "poll-interval-min-value": "Abfrageintervall darf nicht kleiner als 1 sein", + "partitions-required": "Partitionen sind erforderlich!", + "partitions-min-value": "Anzahl der Partitionen darf nicht kleiner als 1 sein", + "pack-processing-timeout-required": "Verarbeitungstimeout ist erforderlich", + "pack-processing-timeout-min-value": "Verarbeitungstimeout darf nicht kleiner als 1 sein", + "batch-size-required": "Batchgröße ist erforderlich!", + "batch-size-min-value": "Batchgröße darf nicht kleiner als 1 sein", + "retries-required": "Wiederholungen sind erforderlich!", + "retries-min-value": "Anzahl der Wiederholungen darf nicht negativ sein", + "failure-percentage-required": "Fehlerschwellenwert ist erforderlich!", + "failure-percentage-min-value": "Fehlerschwellenwert darf nicht kleiner als 0 sein", + "failure-percentage-max-value": "Fehlerschwellenwert darf nicht größer als 100 sein", + "pause-between-retries-required": "Pause zwischen Wiederholungen ist erforderlich!", + "pause-between-retries-min-value": "Pause zwischen Wiederholungen darf nicht kleiner als 1 sein", + "max-pause-between-retries-required": "Maximale Pause zwischen Wiederholungen ist erforderlich!", + "max-pause-between-retries-min-value": "Maximale Pause darf nicht kleiner als 1 sein", + "submit-strategy-type-required": "Übermittlungsstrategie ist erforderlich!", + "processing-strategy-type-required": "Verarbeitungsstrategie ist erforderlich!", + "queues": "Warteschlangen", + "selected-queues": "{ count, plural, =1 {1 Warteschlange} other {# Warteschlangen} } ausgewählt", + "delete-queue-title": "Sind Sie sicher, dass Sie die Warteschlange '{{queueName}}' löschen möchten?", + "delete-queues-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Warteschlange} other {# Warteschlangen} } löschen möchten?", + "delete-queue-text": "Achtung! Nach der Bestätigung wird die Warteschlange und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-queues-text": "Nach der Bestätigung werden alle ausgewählten Warteschlangen gelöscht und sind nicht mehr zugänglich.", + "search": "Warteschlange suchen", + "add": "Warteschlange hinzufügen", + "details": "Details der Warteschlange", + "topic": "Thema", + "submit-settings": "Übermittlungseinstellungen", + "submit-strategy": "Strategietyp *", + "grouping-parameter": "Gruppierungsparameter", + "processing-settings": "Einstellungen für Wiederholungsverarbeitung", + "processing-strategy": "Verarbeitungstyp *", + "retries-settings": "Wiederholungseinstellungen", + "polling-settings": "Abfrageeinstellungen", + "batch-processing": "Batch-Verarbeitung", + "poll-interval": "Abfrageintervall", + "partitions": "Partitionen", + "immediate-processing": "Sofortige Verarbeitung", + "consumer-per-partition": "Nachricht pro Partition abrufen", + "consumer-per-partition-hint": "Separaten Verbraucher für jede Partition aktivieren", + "duplicate-msg-to-all-partitions": "Nachricht an alle Partitionen duplizieren", + "processing-timeout": "Verarbeitung innerhalb, ms", + "batch-size": "Batchgröße", + "retries": "Anzahl der Wiederholungen (0 = unbegrenzt)", + "failure-percentage": "Fehlermeldungen zum Überspringen von Wiederholungen, %", + "pause-between-retries": "Wiederholung nach, Sek.", + "max-pause-between-retries": "Zusätzliche Wiederholung nach, Sek.", + "delete": "Warteschlange löschen", + "copyId": "Warteschlangen-ID kopieren", + "idCopiedMessage": "Warteschlangen-ID wurde in die Zwischenablage kopiert", + "description": "Beschreibung", + "description-hint": "Dieser Text wird in der Beschreibung der Warteschlange angezeigt, anstelle der gewählten Strategie", + "alt-description": "Übermittlungsstrategie: {{submitStrategy}}, Verarbeitungsstrategie: {{processingStrategy}}", + "custom-properties": "Benutzerdefinierte Eigenschaften", + "custom-properties-hint": "Eigenschaften für benutzerdefinierte Warteschlangenerstellung (Thema), z. B.: 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies": { + "sequential-by-originator-label": "Sequenziell nach Ursprung", + "sequential-by-originator-hint": "Neue Nachricht z. B. für Gerät A wird erst übermittelt, wenn die vorherige Nachricht für Gerät A bestätigt wurde", + "sequential-by-tenant-label": "Sequenziell nach Mieter", + "sequential-by-tenant-hint": "Neue Nachricht z. B. für Mieter A wird erst übermittelt, wenn die vorherige Nachricht für Mieter A bestätigt wurde", + "sequential-label": "Sequenziell", + "sequential-hint": "Neue Nachricht wird erst übermittelt, wenn die vorherige bestätigt wurde", + "burst-label": "Schubweise", + "burst-hint": "Alle Nachrichten werden in der Reihenfolge ihres Eingangs an die Regelketten übermittelt", + "batch-label": "Stapel", + "batch-hint": "Neuer Stapel wird erst übermittelt, wenn der vorherige bestätigt wurde", + "skip-all-failures-label": "Alle Fehler überspringen", + "skip-all-failures-hint": "Alle Fehler ignorieren", + "skip-all-failures-and-timeouts-label": "Alle Fehler und Timeouts überspringen", + "skip-all-failures-and-timeouts-hint": "Alle Fehler und Timeouts ignorieren", + "retry-all-label": "Alle wiederholen", + "retry-all-hint": "Alle Nachrichten aus dem Verarbeitungspaket erneut versuchen", + "retry-failed-label": "Fehlgeschlagene wiederholen", + "retry-failed-hint": "Alle fehlgeschlagenen Nachrichten aus dem Verarbeitungspaket erneut versuchen", + "retry-timeout-label": "Timeouts wiederholen", + "retry-timeout-hint": "Alle Nachrichten mit Timeout aus dem Verarbeitungspaket erneut versuchen", + "retry-failed-and-timeout-label": "Fehlgeschlagene und Timeouts wiederholen", + "retry-failed-and-timeout-hint": "Alle fehlgeschlagenen und mit Timeout versehenen Nachrichten aus dem Verarbeitungspaket erneut versuchen" + } + }, + "queue-statistics": { + "queue-statistics": "Warteschlangenstatistiken", + "no-queue-statistics-matching": "Keine passenden Warteschlangenstatistiken für '{{entity}}' gefunden.", + "queue-statistics-required": "Warteschlangenstatistiken sind erforderlich.", + "list-of-queue-statistics": "{ count, plural, =1 {Eine Warteschlangenstatistik} other {Liste von # Warteschlangenstatistiken} }", + "selected-queue-statistics": "{ count, plural, =1 {1 Warteschlangenstatistik} other {# Warteschlangenstatistiken} } ausgewählt", + "no-queue-statistics-text": "Keine Warteschlangenstatistiken gefunden", + "queue-statistics-starts-with": "Warteschlangenstatistiken, deren Name mit '{{prefix}}' beginnt" + }, + "server-error": { + "general": "Allgemeiner Serverfehler", + "authentication": "Authentifizierungsfehler", + "jwt-token-expired": "JWT-Token abgelaufen", + "tenant-trial-expired": "Testzeitraum des Mieters abgelaufen", + "credentials-expired": "Anmeldedaten abgelaufen", + "permission-denied": "Zugriff verweigert", + "invalid-arguments": "Ungültige Argumente", + "bad-request-params": "Ungültige Anfrageparameter", + "item-not-found": "Element nicht gefunden", + "too-many-requests": "Zu viele Anfragen", + "too-many-updates": "Zu viele Aktualisierungen" }, "tenant": { - "tenant": "Mandant", - "tenants": "Mandanten", - "management": "Mandantenverwaltung", - "add": "Mandant hinzufügen", + "tenant": "Mieter", + "tenants": "Mieter", + "management": "Mieterverwaltung", + "add": "Mieter hinzufügen", "admins": "Administratoren", - "manage-tenant-admins": "Mandantenadministratoren verwalten", - "delete": "Mandant löschen", - "add-tenant-text": "Neuen Mandanten hinzufügen", - "no-tenants-text": "Keine Mandanten gefunden", - "tenant-details": "Mandantendetails", - "delete-tenant-title": "Möchten Sie den Mandanten '{{tenantTitle}}' wirklich löschen?", - "delete-tenant-text": "Vorsicht, nach Bestätigung werden der Mandant und alle zugehörigen Daten gelöscht.", - "delete-tenants-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Mandant} other {# Mandanten} } löschen möchten?", - "delete-tenants-action-title": "{ count, plural, =1 {1 Mandant} other {# Mandanten} } löschen", - "delete-tenants-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Mandanten entfernt und alle zugehörigen Daten werden gelöscht.", + "manage-tenant-admins": "Mieteradministratoren verwalten", + "delete": "Mieter löschen", + "add-tenant-text": "Neuen Mieter hinzufügen", + "no-tenants-text": "Keine Mieter gefunden", + "tenant-details": "Mieterdetails", + "title-max-length": "Titel darf weniger als 256 Zeichen enthalten", + "delete-tenant-title": "Sind Sie sicher, dass Sie den Mieter '{{tenantTitle}}' löschen möchten?", + "delete-tenant-text": "Achtung! Nach der Bestätigung werden der Mieter und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-tenants-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Mieter} other {# Mieter} } löschen möchten?", + "delete-tenants-action-title": "{ count, plural, =1 {1 Mieter löschen} other {# Mieter löschen} }", + "delete-tenants-text": "Achtung! Nach der Bestätigung werden alle ausgewählten Mieter und ihre zugehörigen Daten gelöscht und können nicht wiederhergestellt werden.", "title": "Titel", "title-required": "Titel ist erforderlich.", "description": "Beschreibung", "details": "Details", "events": "Ereignisse", - "copyId": "Mandanten-ID kopieren", - "idCopiedMessage": "Mandanten-ID wurde in die Zwischenablage kopiert", - "select-tenant": "Mandant auswählen", - "no-tenants-matching": "Es wurden keine passenden Mandanten für '{{entity}}' gefunden.", - "tenant-required": "Mandant ist erforderlich" + "copyId": "Mieter-ID kopieren", + "idCopiedMessage": "Mieter-ID wurde in die Zwischenablage kopiert", + "select-tenant": "Mieter auswählen", + "no-tenants-matching": "Keine passenden Mieter für '{{entity}}' gefunden.", + "tenant-required": "Mieter ist erforderlich", + "search": "Mieter suchen", + "selected-tenants": "{ count, plural, =1 {1 Mieter} other {# Mieter} } ausgewählt", + "isolated-tb-rule-engine": "Isolierte ThingsBoard Rule Engine-Warteschlangen verwenden", + "isolated-tb-rule-engine-details": "Jeder Mieter erhält dedizierte Rule Engine-Warteschlangen" + }, + "tenant-profile": { + "tenant-profile": "Mieterprofil", + "tenant-profiles": "Mieterprofile", + "add": "Mieterprofil hinzufügen", + "add-profile": "Profil hinzufügen", + "debug": "Debug", + "edit": "Mieterprofil bearbeiten", + "tenant-profile-details": "Details zum Mieterprofil", + "no-tenant-profiles-text": "Keine Mieterprofile gefunden", + "name-max-length": "Name darf weniger als 256 Zeichen enthalten", + "search": "Mieterprofile suchen", + "selected-tenant-profiles": "{ count, plural, =1 {1 Mieterprofil} other {# Mieterprofile} } ausgewählt", + "no-tenant-profiles-matching": "Keine passenden Mieterprofile für '{{entity}}' gefunden.", + "tenant-profile-required": "Mieterprofil ist erforderlich", + "idCopiedMessage": "Mieterprofil-ID wurde in die Zwischenablage kopiert", + "set-default": "Als Standard-Mieterprofil festlegen", + "delete": "Mieterprofil löschen", + "copyId": "Mieterprofil-ID kopieren", + "name": "Name", + "name-required": "Name ist erforderlich.", + "data": "Profildaten", + "profile-configuration": "Profilkonfiguration", + "description": "Beschreibung", + "default": "Standard", + "delete-tenant-profile-title": "Sind Sie sicher, dass Sie das Mieterprofil '{{tenantProfileName}}' löschen möchten?", + "delete-tenant-profile-text": "Achtung! Nach der Bestätigung wird das Mieterprofil und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-tenant-profiles-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Mieterprofil} other {# Mieterprofile} } löschen möchten?", + "delete-tenant-profiles-text": "Achtung! Nach der Bestätigung werden alle ausgewählten Mieterprofile gelöscht und zugehörige Daten können nicht wiederhergestellt werden.", + "set-default-tenant-profile-title": "Sind Sie sicher, dass Sie das Mieterprofil '{{tenantProfileName}}' als Standard festlegen möchten?", + "set-default-tenant-profile-text": "Nach der Bestätigung wird das Mieterprofil als Standard markiert und für neue Mieter ohne spezifisches Profil verwendet.", + "no-tenant-profiles-found": "Keine Mieterprofile gefunden.", + "create-new-tenant-profile": "Neues erstellen!", + "create-tenant-profile": "Neues Mieterprofil erstellen", + "import": "Mieterprofil importieren", + "export": "Mieterprofil exportieren", + "export-failed-error": "Mieterprofil konnte nicht exportiert werden: {{error}}", + "tenant-profile-file": "Mieterprofil-Datei", + "invalid-tenant-profile-file-error": "Import fehlgeschlagen: Ungültige Datenstruktur des Mieterprofils.", + "advanced-settings": "Erweiterte Einstellungen", + "entities": "Entitäten", + "rule-engine": "Rule Engine", + "time-to-live": "Lebensdauer", + "calculated-fields": "Berechnete Felder", + "alarms-and-notifications": "Alarme und Benachrichtigungen", + "ota-files-in-bytes": "Dateien", + "ws-title": "WS", + "unlimited": "(0 – unbegrenzt)", + "maximum-devices": "Maximale Anzahl Geräte", + "maximum-devices-required": "Maximale Anzahl Geräte ist erforderlich.", + "maximum-devices-range": "Maximale Anzahl Geräte darf nicht negativ sein", + "maximum-assets": "Maximale Anzahl Assets", + "maximum-assets-required": "Maximale Anzahl Assets ist erforderlich.", + "maximum-assets-range": "Maximale Anzahl Assets darf nicht negativ sein", + "maximum-customers": "Maximale Anzahl Kunden", + "maximum-customers-required": "Maximale Anzahl Kunden ist erforderlich.", + "maximum-customers-range": "Maximale Anzahl Kunden darf nicht negativ sein", + "maximum-users": "Maximale Anzahl Benutzer", + "maximum-users-required": "Maximale Anzahl Benutzer ist erforderlich.", + "maximum-users-range": "Maximale Anzahl Benutzer darf nicht negativ sein", + "maximum-dashboards": "Maximale Anzahl Dashboards", + "maximum-dashboards-required": "Maximale Anzahl Dashboards ist erforderlich.", + "maximum-dashboards-range": "Maximale Anzahl Dashboards darf nicht negativ sein", + "maximum-edges": "Maximale Anzahl Edges", + "maximum-edges-required": "Maximale Anzahl Edges ist erforderlich.", + "maximum-edges-range": "Maximale Anzahl Edges darf nicht negativ sein", + "maximum-rule-chains": "Maximale Anzahl Regelketten", + "maximum-rule-chains-required": "Maximale Anzahl Regelketten ist erforderlich.", + "maximum-rule-chains-range": "Maximale Anzahl Regelketten darf nicht negativ sein", + "maximum-resources-sum-data-size": "Maximale Gesamtgröße der Ressourcendateien (Byte)", + "maximum-resources-sum-data-size-required": "Maximale Gesamtgröße der Ressourcendateien ist erforderlich.", + "maximum-resources-sum-data-size-range": "Maximale Gesamtgröße der Ressourcendateien darf nicht negativ sein", + "maximum-resource-size": "Maximale Ressourcendateigröße (Byte)", + "maximum-resource-size-required": "Maximale Ressourcendateigröße ist erforderlich.", + "maximum-resource-size-range": "Maximale Ressourcendateigröße darf nicht negativ sein", + "maximum-ota-packages-sum-data-size": "Maximale Gesamtgröße der OTA-Paketdateien (Byte)", + "maximum-ota-package-sum-data-size-required": "Maximale Gesamtgröße der OTA-Paketdateien ist erforderlich.", + "maximum-ota-package-sum-data-size-range": "Maximale Gesamtgröße der OTA-Paketdateien darf nicht negativ sein", + "maximum-debug-duration-min": "Maximale Debug-Dauer (Minuten)", + "maximum-debug-duration-min-range": "Maximale Debug-Dauer darf nicht negativ sein", + "rest-requests-for-tenant": "REST-Anfragen für Mieter", + "transport-tenant-telemetry-msg-rate-limit": "Transport-Telemetrie-Nachrichten des Mieters", + "transport-tenant-telemetry-data-points-rate-limit": "Transport-Telemetrie-Datenpunkte des Mieters", + "transport-device-msg-rate-limit": "Transport-Gerätenachrichten", + "transport-device-telemetry-msg-rate-limit": "Transport-Gerätetelemetrie-Nachrichten", + "transport-device-telemetry-data-points-rate-limit": "Transport-Gerätetelemetrie-Datenpunkte", + "transport-gateway-msg-rate-limit": "Transport-Gateway-Nachrichten", + "transport-gateway-telemetry-msg-rate-limit": "Transport-Gateway-Telemetrie-Nachrichten", + "transport-gateway-telemetry-data-points-rate-limit": "Transport-Gateway-Telemetrie-Datenpunkte", + "transport-gateway-device-msg-rate-limit": "Transport-Gateway-Gerätenachrichten", + "transport-gateway-device-telemetry-msg-rate-limit": "Transport-Gateway-Gerätetelemetrie-Nachrichten", + "transport-gateway-device-telemetry-data-points-rate-limit": "Transport-Gateway-Gerätetelemetrie-Datenpunkte", + "tenant-entity-export-rate-limit": "Export von Entitätsversionen", + "tenant-entity-import-rate-limit": "Import von Entitätsversionen", + "tenant-notification-request-rate-limit": "Benachrichtigungsanfragen", + "tenant-notification-requests-per-rule-rate-limit": "Benachrichtigungsanfragen pro Regel", + "max-calculated-fields": "Maximale berechnete Felder pro Entität", + "max-calculated-fields-range": "Maximale Anzahl berechneter Felder darf nicht negativ sein", + "max-calculated-fields-required": "Maximale Anzahl berechneter Felder ist erforderlich", + "max-data-points-per-rolling-arg": "Maximale Anzahl Datenpunkte in rollierenden Argumenten", + "max-data-points-per-rolling-arg-range": "Maximale Anzahl Datenpunkte in rollierenden Argumenten darf nicht negativ sein", + "max-data-points-per-rolling-arg-required": "Maximale Anzahl Datenpunkte in rollierenden Argumenten ist erforderlich", + "max-arguments-per-cf": "Maximale Argumente pro berechnetem Feld", + "max-arguments-per-cf-range": "Maximale Argumente pro berechnetem Feld dürfen nicht negativ sein", + "max-arguments-per-cf-required": "Maximale Argumente pro berechnetem Feld sind erforderlich", + "max-state-size": "Maximale Zustandsgröße in KB", + "max-state-size-range": "Maximale Zustandsgröße darf nicht negativ sein", + "max-state-size-required": "Maximale Zustandsgröße ist erforderlich", + "max-value-argument-size": "Maximale Größe eines Einzelwert-Arguments in KB", + "max-value-argument-size-range": "Größe eines Einzelwert-Arguments darf nicht negativ sein", + "max-value-argument-size-required": "Größe eines Einzelwert-Arguments ist erforderlich", + "max-transport-messages": "Maximale Transportnachrichten", + "max-transport-messages-required": "Maximale Transportnachrichten sind erforderlich.", + "max-transport-messages-range": "Maximale Transportnachrichten dürfen nicht negativ sein", + "max-transport-data-points": "Maximale Transportdatenpunkte", + "max-transport-data-points-required": "Maximale Transportdatenpunkte sind erforderlich.", + "max-transport-data-points-range": "Maximale Transportdatenpunkte dürfen nicht negativ sein", + "max-r-e-executions": "Maximale Rule Engine Ausführungen", + "max-r-e-executions-required": "Maximale Rule Engine Ausführungen sind erforderlich.", + "max-r-e-executions-range": "Maximale Rule Engine Ausführungen dürfen nicht negativ sein", + "max-j-s-executions": "Maximale JavaScript-Ausführungen", + "max-j-s-executions-required": "Maximale JavaScript-Ausführungen sind erforderlich.", + "max-j-s-executions-range": "Maximale JavaScript-Ausführungen dürfen nicht negativ sein", + "max-tbel-executions": "Maximale TBEL-Ausführungen", + "max-tbel-executions-required": "Maximale TBEL-Ausführungen sind erforderlich.", + "max-tbel-executions-range": "Maximale TBEL-Ausführungen dürfen nicht negativ sein", + "max-d-p-storage-days": "Maximale Speicherzeit für Datenpunkte (Tage)", + "max-d-p-storage-days-required": "Maximale Speicherzeit für Datenpunkte ist erforderlich.", + "max-d-p-storage-days-range": "Maximale Speicherzeit für Datenpunkte darf nicht negativ sein", + "default-storage-ttl-days": "Standardmäßige Speicherzeit (Tage)", + "default-storage-ttl-days-required": "Standardmäßige Speicherzeit ist erforderlich.", + "default-storage-ttl-days-range": "Standardmäßige Speicherzeit darf nicht negativ sein", + "alarms-ttl-days": "Alarme Speicherzeit (Tage)", + "alarms-ttl-days-required": "Alarme Speicherzeit ist erforderlich", + "alarms-ttl-days-days-range": "Alarme Speicherzeit darf nicht negativ sein", + "rpc-ttl-days": "RPC Speicherzeit (Tage)", + "rpc-ttl-days-required": "RPC Speicherzeit ist erforderlich", + "rpc-ttl-days-days-range": "RPC Speicherzeit darf nicht negativ sein", + "queue-stats-ttl-days": "Warteschlangenstatistik Speicherzeit (Tage)", + "queue-stats-ttl-days-required": "Warteschlangenstatistik Speicherzeit ist erforderlich", + "queue-stats-ttl-days-range": "Warteschlangenstatistik Speicherzeit darf nicht negativ sein", + "rule-engine-exceptions-ttl-days": "Rule Engine Ausnahme Speicherzeit (Tage)", + "rule-engine-exceptions-ttl-days-required": "Rule Engine Ausnahme Speicherzeit ist erforderlich", + "rule-engine-exceptions-ttl-days-range": "Rule Engine Ausnahme Speicherzeit darf nicht negativ sein", + "max-rule-node-executions-per-message": "Maximale Regelknotenausführungen pro Nachricht", + "max-rule-node-executions-per-message-required": "Maximale Regelknotenausführungen pro Nachricht sind erforderlich.", + "max-rule-node-executions-per-message-range": "Maximale Regelknotenausführungen pro Nachricht dürfen nicht negativ sein", + "max-emails": "Maximale gesendete E-Mails", + "max-emails-required": "Maximale gesendete E-Mails sind erforderlich.", + "max-emails-range": "Maximale gesendete E-Mails dürfen nicht negativ sein", + "sms-enabled": "SMS aktiviert", + "max-sms": "Maximale gesendete SMS", + "max-sms-required": "Maximale gesendete SMS sind erforderlich.", + "max-sms-range": "Maximale gesendete SMS dürfen nicht negativ sein", + "max-created-alarms": "Maximale Anzahl erstellter Alarme", + "max-created-alarms-required": "Maximale Anzahl erstellter Alarme ist erforderlich.", + "max-created-alarms-range": "Maximale Anzahl erstellter Alarme darf nicht negativ sein", + "no-queue": "Keine Warteschlange konfiguriert", + "add-queue": "Warteschlange hinzufügen", + "queues-with-count": "Warteschlangen ({{count}})", + "tenant-rest-limits": "REST-Anfragen für Mieter", + "customer-rest-limits": "REST-Anfragen für Kunden", + "incorrect-pattern-for-rate-limits": "Das Format ist durch Kommas getrennte Paare aus Kapazität und Zeitraum (in Sekunden), z. B. 100:1,2000:60", + "too-small-value-zero": "Der Wert muss größer als 0 sein", + "too-small-value-one": "Der Wert muss größer als 1 sein", + "queue-size-is-limited-by-system-configuration": "Die Größe der Warteschlange ist auch durch die Systemkonfiguration begrenzt.", + "cassandra-tenant-limits-configuration": "Cassandra-Abfrage für Mieter", + "ws-limit-max-sessions-per-tenant": "Maximale Sitzungen pro Mieter", + "ws-limit-max-sessions-per-customer": "Maximale Sitzungen pro Kunde", + "ws-limit-max-sessions-per-regular-user": "Maximale Sitzungen pro normalem Benutzer", + "ws-limit-max-sessions-per-public-user": "Maximale Sitzungen pro öffentlichem Benutzer", + "ws-limit-queue-per-session": "Maximale Nachrichtenwarteschlange pro Sitzung", + "ws-limit-max-subscriptions-per-tenant": "Maximale Abonnements pro Mieter", + "ws-limit-max-subscriptions-per-customer": "Maximale Abonnements pro Kunde", + "ws-limit-max-subscriptions-per-regular-user": "Maximale Abonnements pro normalem Benutzer", + "ws-limit-max-subscriptions-per-public-user": "Maximale Abonnements pro öffentlichem Benutzer", + "ws-limit-updates-per-session": "WebSocket-Aktualisierungen pro Sitzung", + "rate-limits": { + "add-limit": "Limit hinzufügen", + "advanced-settings": "Erweiterte Einstellungen", + "edit-limit": "Limit bearbeiten", + "but-less-than": "aber weniger als", + "calculated-field-debug-event-rate-limit": "Berechnete Feld-Debug-Ereignisse", + "edit-calculated-field-debug-event-rate-limit": "Limit für berechnete Feld-Debug-Ereignisse bearbeiten", + "edit-transport-tenant-msg-title": "Limit für Transport-Mieter-Nachrichten bearbeiten", + "edit-transport-tenant-telemetry-msg-title": "Limit für Transport-Mieter-Telemetrienachrichten bearbeiten", + "edit-transport-tenant-telemetry-data-points-title": "Limit für Transport-Mieter-Telemetriedatenpunkte bearbeiten", + "edit-transport-device-msg-title": "Limit für Transport-Gerätenachrichten bearbeiten", + "edit-transport-device-telemetry-msg-title": "Limit für Transport-Gerätetelemetrienachrichten bearbeiten", + "edit-transport-device-telemetry-data-points-title": "Limit für Transport-Gerätetelemetriedatenpunkte bearbeiten", + "edit-transport-gateway-msg-title": "Limit für Transport-Gateway-Nachrichten bearbeiten", + "edit-transport-gateway-telemetry-msg-title": "Limit für Transport-Gateway-Telemetrienachrichten bearbeiten", + "edit-transport-gateway-telemetry-data-points-title": "Limit für Transport-Gateway-Telemetriedatenpunkte bearbeiten", + "edit-transport-gateway-device-msg-title": "Limit für Transport-Gateway-Gerätenachrichten bearbeiten", + "edit-transport-gateway-device-telemetry-msg-title": "Limit für Transport-Gateway-Gerätetelemetrienachrichten bearbeiten", + "edit-transport-gateway-device-telemetry-data-points-title": "Limit für Transport-Gateway-Gerätetelemetriedatenpunkte bearbeiten", + "edit-tenant-rest-limits-title": "REST-Anfragelimits für Mieter bearbeiten", + "edit-customer-rest-limits-title": "REST-Anfragelimits für Kunden bearbeiten", + "edit-ws-limit-updates-per-session-title": "Limit für WebSocket-Aktualisierungen pro Sitzung bearbeiten", + "edit-cassandra-tenant-limits-configuration-title": "Cassandra-Abfrage-Limits für Mieter bearbeiten", + "edit-tenant-entity-export-rate-limit-title": "Limit für Entitätsversionserstellung bearbeiten", + "edit-tenant-entity-import-rate-limit-title": "Limit für Entitätsversionsladevorgang bearbeiten", + "edit-tenant-notification-request-rate-limit-title": "Limit für Benachrichtigungsanfragen bearbeiten", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "Limit für Benachrichtigungsanfragen pro Regel bearbeiten", + "edit-edge-events-rate-limit": "Limit für Edge-Ereignisse bearbeiten", + "edit-edge-events-per-edge-rate-limit": "Limit für Edge-Ereignisse pro Edge bearbeiten", + "edge-events-rate-limit": "Edge-Ereignisse", + "edge-events-per-edge-rate-limit": "Edge-Ereignisse pro Edge", + "edit-edge-uplink-messages-rate-limit": "Limit für Edge-Uplink-Nachrichten bearbeiten", + "edit-edge-uplink-messages-per-edge-rate-limit": "Limit für Edge-Uplink-Nachrichten pro Edge bearbeiten", + "edge-uplink-messages-rate-limit": "Edge-Uplink-Nachrichten", + "edge-uplink-messages-per-edge-rate-limit": "Edge-Uplink-Nachrichten pro Edge", + "messages-per": "Nachrichten pro", + "not-set": "Nicht festgelegt", + "number-of-messages": "Anzahl der Nachrichten", + "number-of-messages-required": "Anzahl der Nachrichten ist erforderlich.", + "number-of-messages-min": "Minimalwert ist 1.", + "preview": "Vorschau", + "per-seconds": "Pro Sekunde", + "per-seconds-required": "Zeitintervall ist erforderlich.", + "per-seconds-min": "Minimalwert ist 1.", + "rate-limits": "Ratenlimits", + "remove-limit": "Limit entfernen", + "transport-tenant-msg": "Transport-Mieter-Nachrichten", + "transport-tenant-telemetry-msg": "Transport-Mieter-Telemetrienachrichten", + "transport-tenant-telemetry-data-points": "Transport-Mieter-Telemetriedatenpunkte", + "transport-device-msg": "Transport-Gerätenachrichten", + "transport-device-telemetry-msg": "Transport-Gerätetelemetrienachrichten", + "transport-device-telemetry-data-points": "Transport-Gerätetelemetriedatenpunkte", + "transport-gateway-msg": "Transport-Gateway-Nachrichten", + "transport-gateway-telemetry-msg": "Transport-Gateway-Telemetrienachrichten", + "transport-gateway-telemetry-data-points": "Transport-Gateway-Telemetriedatenpunkte", + "transport-gateway-device-msg": "Transport-Gateway-Gerätenachrichten", + "transport-gateway-device-telemetry-msg": "Transport-Gateway-Gerätetelemetrienachrichten", + "transport-gateway-device-telemetry-data-points": "Transport-Gateway-Gerätetelemetriedatenpunkte", + "sec": "Sek." + } }, "timeinterval": { "seconds-interval": "{ seconds, plural, =1 {1 Sekunde} other {# Sekunden} }", @@ -1703,31 +5707,38 @@ "minutes": "Minuten", "seconds": "Sekunden", "advanced": "Erweitert", + "custom": "Benutzerdefiniert", "predefined": { "yesterday": "Gestern", "day-before-yesterday": "Vorgestern", "this-day-last-week": "Dieser Tag letzte Woche", - "previous-week": "Letzte Woche (So - Sa)", - "previous-week-iso": "Letzte Woche (Mo - So)", - "previous-month": "Letzter Monat", - "previous-quarter": "Letztes Quartal", - "previous-half-year": "Letztes Halbjahr", - "previous-year": "Letztes Jahr", + "previous-week": "Vorherige Woche (So - Sa)", + "previous-week-iso": "Vorherige Woche (Mo - So)", + "previous-month": "Vorheriger Monat", + "previous-quarter": "Vorheriges Quartal", + "previous-half-year": "Vorheriges Halbjahr", + "previous-year": "Vorheriges Jahr", "current-hour": "Aktuelle Stunde", "current-day": "Aktueller Tag", - "current-day-so-far": "Aktueller Tag bisher", + "current-day-so-far": "Bisheriger aktueller Tag", "current-week": "Aktuelle Woche (So - Sa)", "current-week-iso": "Aktuelle Woche (Mo - So)", - "current-week-so-far": "Aktuelle Woche bisher (So - Sa)", - "current-week-iso-so-far": "Aktuelle Woche bisher (Mo - So)", + "current-week-so-far": "Bisherige aktuelle Woche (So - Sa)", + "current-week-iso-so-far": "Bisherige aktuelle Woche (Mo - So)", "current-month": "Aktueller Monat", - "current-month-so-far": "Aktueller Monat bisher", + "current-month-so-far": "Bisheriger aktueller Monat", "current-quarter": "Aktuelles Quartal", - "current-quarter-so-far": "Aktuelles Quartal bisher", + "current-quarter-so-far": "Bisheriges aktuelles Quartal", "current-half-year": "Aktuelles Halbjahr", - "current-half-year-so-far": "Aktuelles Halbjahr bisher", + "current-half-year-so-far": "Bisheriges aktuelles Halbjahr", "current-year": "Aktuelles Jahr", - "current-year-so-far": "Aktuelles Jahr bisher" + "current-year-so-far": "Bisheriges aktuelles Jahr" + }, + "type": { + "week": "Woche (So - Sa)", + "week-iso": "Woche (Mo - So)", + "month": "Monat", + "quarter": "Quartal" } }, "timeunit": { @@ -1739,6 +5750,7 @@ }, "timewindow": { "timewindow": "Zeitfenster", + "timewindow-settings": "Zeitfenster-Einstellungen", "years": "{ years, plural, =1 { Jahr } other {# Jahre } }", "years-short": "{{ years }}J", "months": "{ months, plural, =1 { Monat } other {# Monate } }", @@ -1748,271 +5760,1439 @@ "days": "{ days, plural, =1 { Tag } other {# Tage } }", "days-short": "{{ days }}T", "hours": "{ hours, plural, =0 { Stunde } =1 {1 Stunde } other {# Stunden } }", - "hr": "{{ hr }} Std", - "hr-short": "{{ hr }}S", + "hr": "{{ hr }} Std.", + "hr-short": "{{ hr }}h", "minutes": "{ minutes, plural, =0 { Minute } =1 {1 Minute } other {# Minuten } }", - "min": "{{ min }} Min", + "min": "{{ min }} Min.", "min-short": "{{ min }}m", "seconds": "{ seconds, plural, =0 { Sekunde } =1 {1 Sekunde } other {# Sekunden } }", - "sec": "{{ sec }} Sek", + "sec": "{{ sec }} Sek.", "sec-short": "{{ sec }}s", "short": { + "years": "{ years, plural, =1 {1 Jahr } other {# Jahre } }", "days": "{ days, plural, =1 {1 Tag } other {# Tage } }", "hours": "{ hours, plural, =1 {1 Stunde } other {# Stunden } }", - "minutes": "{{minutes}} Min ", - "seconds": "{{seconds}} Sek " + "minutes": "{{minutes}} Min.", + "seconds": "{{seconds}} Sek." }, "realtime": "Echtzeit", - "history": "Historie", + "history": "Verlauf", "last-prefix": "letzte", "period": "von {{ startTime }} bis {{ endTime }}", "edit": "Zeitfenster bearbeiten", "date-range": "Datumsbereich", - "for-all-time": "Für immer", + "for-all-time": "Für gesamten Zeitraum", "last": "Letzte", - "time-period": "Zeitfenster", - "hide": "Verstecken", + "time-period": "Zeitraum", + "hide": "Ausblenden", "interval": "Intervall", - "just-now": "Soeben", - "just-now-lower": "soeben", - "ago": "vor", - "style": "Zeitfenster Style", + "just-now": "Gerade eben", + "just-now-lower": "gerade eben", + "ago": "her", + "style": "Zeitfenster-Stil", "icon": "Symbol", - "icon-position": "Position Symbol", + "icon-position": "Symbolposition", "icon-position-left": "Links", "icon-position-right": "Rechts", "font": "Schriftart", "color": "Farbe", - "displayTypePrefix": "Echtzeit-/Verlaufspräfix anzeigen", - "preview": "Vorschau" + "displayTypePrefix": "Echtzeit/Verlauf-Präfix anzeigen", + "preview": "Vorschau", + "relative": "Relativ", + "range": "Bereich", + "hide-timewindow-section": "Zeitfensterbereich für Endbenutzer ausblenden", + "hide-last-interval": "Letztes Intervall für Endbenutzer ausblenden", + "hide-relative-interval": "Relatives Intervall für Endbenutzer ausblenden", + "hide-fixed-interval": "Festes Intervall für Endbenutzer ausblenden", + "hide-aggregation": "Aggregation für Endbenutzer ausblenden", + "hide-group-interval": "Gruppierungsintervall für Endbenutzer ausblenden", + "hide-max-values": "Maximale Werte für Endbenutzer ausblenden", + "hide-timezone": "Zeitzone für Endbenutzer ausblenden", + "disable-custom-interval": "Benutzerdefiniertes Intervall deaktivieren", + "edit-aggregation-functions-list": "Liste der Aggregationsfunktionen bearbeiten", + "edit-aggregation-functions-list-hint": "Liste verfügbarer Optionen kann angegeben werden.", + "allowed-aggregation-functions": "Erlaubte Aggregationsfunktionen", + "edit-intervals-list": "Intervallliste bearbeiten", + "allowed-agg-intervals": "Gruppierungsintervalle", + "default-agg-interval": "Standard-Gruppierungsintervall", + "edit-intervals-list-hint": "Liste verfügbarer Intervalloptionen kann angegeben werden.", + "edit-grouping-intervals-list-hint": "Gruppierungsintervallliste und Standardintervall konfigurierbar.", + "all": "Alle" + }, + "tooltip": { + "trigger": "Auslöser", + "trigger-point": "Punkt", + "trigger-axis": "Achse", + "label": "Beschriftung", + "value": "Wert", + "date": "Datum", + "show-date-time-interval": "Datum/Zeit-Intervall anzeigen", + "show-date-time-interval-hint": "Datum/Zeit-Intervall gemäß Datenaggregation anzeigen.", + "background-color": "Hintergrundfarbe", + "background-blur": "Hintergrundunschärfe" + }, + "unit": { + "millimeter": "Millimeter", + "centimeter": "Zentimeter", + "angstrom": "Angström", + "nanometer": "Nanometer", + "micrometer": "Mikrometer", + "meter": "Meter", + "kilometer": "Kilometer", + "inch": "Zoll", + "foot": "Fuß", + "yard": "Yard", + "mile": "Meile", + "nautical-mile": "Seemeile", + "astronomical-unit": "Astronomische Einheit", + "reciprocal-metre": "Reziproker Meter", + "meter-per-meter": "Meter pro Meter", + "steradian": "Steradiant", + "thou": "Thou", + "barleycorn": "Gerstenkorn", + "hand": "Handbreit", + "chain": "Kette", + "furlong": "Furlong", + "league": "Leuge", + "fathom": "Faden", + "cable": "Kabellänge", + "link": "Glied", + "rod": "Rute", + "nanogram": "Nanogramm", + "microgram": "Mikrogramm", + "milligram": "Milligramm", + "gram": "Gramm", + "kilogram": "Kilogramm", + "tonne": "Tonne", + "ounce": "Unze", + "pound": "Pfund", + "stone": "Stone", + "hundredweight-count": "Zentner", + "short-tons": "US-Tonnen", + "dalton": "Dalton", + "grain": "Grain", + "drachm": "Drachme", + "quarter": "Quarter", + "slug": "Slug", + "carat": "Karat", + "cubic-millimeter": "Kubikmillimeter", + "cubic-centimeter": "Kubikzentimeter", + "cubic-meter": "Kubikmeter", + "cubic-kilometer": "Kubikkilometer", + "microliter": "Mikroliter", + "milliliter": "Milliliter", + "liter": "Liter", + "hectoliter": "Hektoliter", + "cubic-inch": "Kubikzoll", + "cubic-foot": "Kubikfuß", + "cubic-yard": "Kubikyard", + "fluid-ounce": "Flüssigunze", + "pint": "Pint", + "quart": "Quart", + "gallon": "Gallone", + "oil-barrels": "Ölfass", + "cubic-meter-per-kilogram": "Kubikmeter pro Kilogramm", + "gill": "Gill", + "hogshead": "Hogshead", + "teaspoon": "Teelöffel", + "tablespoon": "Esslöffel", + "cup": "Tasse", + "celsius": "Celsius", + "kelvin": "Kelvin", + "rankine": "Rankine", + "fahrenheit": "Fahrenheit", + "percent": "Prozent", + "meter-per-second": "Meter pro Sekunde", + "kilometer-per-hour": "Kilometer pro Stunde", + "foot-per-second": "Fuß pro Sekunde", + "mile-per-hour": "Meile pro Stunde", + "knot": "Knoten", + "millimeters-per-minute": "Millimeter pro Minute", + "kilometer-per-hour-squared": "Kilometer pro Stunde zum Quadrat", + "foot-per-second-squared": "Fuß pro Sekunde zum Quadrat", + "pascal": "Pascal", + "kilopascal": "Kilopascal", + "megapascal": "Megapascal", + "gigapascal": "Gigapascal", + "millibar": "Millibar", + "bar": "Bar", + "kilobar": "Kilobar", + "newton": "Newton", + "newton-meter": "Newtonmeter", + "foot-pounds": "Fuß-Pfund", + "inch-pounds": "Zoll-Pfund", + "newton-per-meter": "Newton pro Meter", + "atmospheres": "Atmosphären", + "pounds-per-square-inch": "Pfund pro Quadratzoll", + "torr": "Torr", + "inches-of-mercury": "Zoll Quecksilbersäule", + "pascal-per-square-meter": "Pascal pro Quadratmeter", + "pound-per-square-inch": "Pfund pro Quadratzoll", + "newton-per-square-meter": "Newton pro Quadratmeter", + "kilogram-force-per-square-meter": "Kilopond pro Quadratmeter", + "pascal-per-square-centimeter": "Pascal pro Quadratzentimeter", + "ton-force-per-square-inch": "Tonnenkraft pro Quadratzoll", + "kilonewton-per-square-meter": "Kilonewton pro Quadratmeter", + "newton-per-square-millimeter": "Newton pro Quadratmillimeter", + "microjoule": "Mikrojoule", + "millijoule": "Millijoule", + "joule": "Joule", + "kilojoule": "Kilojoule", + "megajoule": "Megajoule", + "gigajoule": "Gigajoule", + "watt-hour": "Wattstunde", + "kilowatt-hour": "Kilowattstunde", + "electron-volts": "Elektronenvolt", + "joules-per-coulomb": "Joule pro Coulomb", + "british-thermal-unit": "Britische Wärmeeinheit (BTU)", + "foot-pound": "Fuß-Pfund", + "calorie": "Kalorie", + "small-calorie": "Kleine Kalorie", + "kilocalorie": "Kilokalorie", + "joule-per-kelvin": "Joule pro Kelvin", + "joule-per-kilogram-kelvin": "Joule pro Kilogramm-Kelvin", + "joule-per-kilogram": "Joule pro Kilogramm", + "watt-per-meter-kelvin": "Watt pro Meter-Kelvin", + "joule-per-cubic-meter": "Joule pro Kubikmeter", + "therm": "Therm", + "electric-dipole-moment": "Elektrisches Dipolmoment", + "magnetic-dipole-moment": "Magnetisches Dipolmoment", + "debye": "Debye", + "coulomb-per-square-meter-per-volt": "Coulomb pro Quadratmeter pro Volt", + "milliwatt": "Milliwatt", + "microwatt": "Mikrowatt", + "watt": "Watt", + "kilowatt": "Kilowatt", + "megawatt": "Megawatt", + "gigawatt": "Gigawatt", + "metric-horsepower": "Metrische Pferdestärke", + "milliwatt-per-square-centimeter": "Milliwatt pro Quadratzentimeter", + "watt-per-square-centimeter": "Watt pro Quadratzentimeter", + "kilowatt-per-square-centimeter": "Kilowatt pro Quadratzentimeter", + "milliwatt-per-square-meter": "Milliwatt pro Quadratmeter", + "watt-per-square-meter": "Watt pro Quadratmeter", + "kilowatt-per-square-meter": "Kilowatt pro Quadratmeter", + "watt-per-square-inch": "Watt pro Quadratzoll", + "kilowatt-per-square-inch": "Kilowatt pro Quadratzoll", + "horsepower": "Pferdestärke", + "btu-per-hour": "BTU pro Stunde", + "coulomb": "Coulomb", + "millicoulomb": "Millicoulomb", + "microcoulomb": "Mikrocoulomb", + "picocoulomb": "Picocoulomb", + "coulomb-per-meter": "Coulomb pro Meter", + "coulomb-per-cubic-meter": "Coulomb pro Kubikmeter", + "coulomb-per-square-meter": "Coulomb pro Quadratmeter", + "square-millimeter": "Quadratmillimeter", + "square-centimeter": "Quadratzentimeter", + "square-meter": "Quadratmeter", + "hectare": "Hektar", + "square-kilometer": "Quadratkilometer", + "square-inch": "Quadratzoll", + "square-foot": "Quadratfuß", + "square-yard": "Quadratyard", + "acre": "Acre", + "square-mile": "Quadratmeile", + "are": "Ar", + "barn": "Barn", + "circular-inch": "Kreiszoll", + "milliampere-hour": "Milliampere-Stunde", + "ampere-hours": "Amperestunden", + "kiloampere-hours": "Kiloamperestunden", + "nanoampere": "Nanoampere", + "picoampere": "Picoampere", + "microampere": "Mikroampere", + "milliampere": "Milliampere", + "ampere": "Ampere", + "microampere-per-square-centimeter": "Mikroampere pro Quadratzentimeter", + "ampere-per-square-meter": "Ampere pro Quadratmeter", + "ampere-per-meter": "Ampere pro Meter", + "oersted": "Oersted", + "bohr-magneton": "Bohrsche Magneton", + "ampere-meter-squared": "Ampere-Quadratmeter", + "nanovolt": "Nanovolt", + "picovolt": "Picovolt", + "volt": "Volt", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "Volt-Meter", + "kilovolt-meter": "Kilovolt-Meter", + "megavolt-meter": "Megavolt-Meter", + "microvolt-meter": "Mikrovolt-Meter", + "millivolt-meter": "Millivolt-Meter", + "nanovolt-meter": "Nanovolt-Meter", + "ohm": "Ohm", + "microohm": "Mikroohm", + "milliohm": "Milliohm", + "kilohm": "Kiloohm", + "megohm": "Megaohm", + "gigohm": "Gigaohm", + "hertz": "Hertz", + "kilohertz": "Kilohertz", + "megahertz": "Megahertz", + "gigahertz": "Gigahertz", + "rpm": "Umdrehungen pro Minute", + "candela-per-square-meter": "Candela pro Quadratmeter", + "candela": "Candela", + "lumen": "Lumen", + "lux": "Lux", + "foot-candle": "Fußkerze", + "lumen-per-square-meter": "Lumen pro Quadratmeter", + "lux-second": "Lux-Sekunde", + "lumen-second": "Lumen-Sekunde", + "lumens-per-watt": "Lumen pro Watt", + "mole": "Mol", + "nanomole": "Nanomol", + "micromole": "Mikromol", + "millimole": "Millimol", + "kilomole": "Kilomol", + "mole-per-cubic-meter": "Mol pro Kubikmeter", + "rssi": "RSSI", + "ppm": "Teile pro Million", + "ppb": "Teile pro Milliarde", + "micrograms-per-cubic-meter": "Mikrogramm pro Kubikmeter", + "aqi": "Luftqualitätsindex (AQI)", + "gram-per-cubic-meter": "Gramm pro Kubikmeter", + "gram-per-kilogram": "Spezifische Feuchtigkeit", + "millimeters-per-second": "Millimeter pro Sekunde", + "neper": "Neper", + "bel": "Bel", + "decibel": "Dezibel", + "meters-per-second-squared": "Meter pro Sekunde zum Quadrat", + "becquerel": "Becquerel", + "curie": "Curie", + "gray": "Gray", + "sievert": "Sievert", + "roentgen": "Röntgen", + "cps": "Zählungen pro Sekunde", + "rad": "Rad", + "rem": "Rem", + "dps": "Zerfälle pro Sekunde", + "rutherford": "Rutherford", + "coulombs-per-kilogram": "Coulomb pro Kilogramm", + "becquerels-per-cubic-meter": "Becquerel pro Kubikmeter", + "curies-per-liter": "Curie pro Liter", + "becquerels-per-second": "Becquerel pro Sekunde", + "curies-per-second": "Curie pro Sekunde", + "gy-per-second": "Gray pro Sekunde", + "watt-per-steradian": "Watt pro Steradiant", + "watt-per-square-metre-steradian": "Watt pro Quadratmeter-Steradiant", + "ph-level": "pH-Wert", + "turbidity": "Trübung", + "mg-per-liter": "Milligramm pro Liter", + "microsiemens-per-centimeter": "Mikrosiemens pro Zentimeter", + "millisiemens-per-meter": "Millisimens pro Meter", + "siemens-per-meter": "Siemens pro Meter", + "kilogram-per-cubic-meter": "Kilogramm pro Kubikmeter", + "gram-per-cubic-centimeter": "Gramm pro Kubikzentimeter", + "kilogram-per-square-meter": "Kilogramm pro Quadratmeter", + "milligram-per-milliliter": "Milligramm pro Milliliter", + "milligram-per-cubic-meter": "Milligramm pro Kubikmeter", + "pound-per-cubic-foot": "Pfund pro Kubikfuß", + "ounces-per-cubic-inch": "Unzen pro Kubikzoll", + "tons-per-cubic-yard": "Tonnen pro Kubikyard", + "particle-density": "Partikeldichte", + "kilometers-per-liter": "Kilometer pro Liter", + "miles-per-gallon": "Meilen pro Gallone", + "liters-per-100-km": "Liter pro 100 km", + "gallons-per-mile": "Gallonen pro Meile", + "liters-per-hour": "Liter pro Stunde", + "gallons-per-hour": "Gallonen pro Stunde", + "beats-per-minute": "Schläge pro Minute", + "millimeters-of-mercury": "Millimeter Quecksilbersäule", + "milligrams-per-deciliter": "Milligramm pro Deziliter", + "g-force": "g-Kraft", + "kilonewton": "Kilonewton", + "kilogram-force": "Kilopond", + "pound-force": "Pfundkraft", + "kilopound-force": "Kilopfundkraft", + "dyne": "Dyne", + "poundal": "Poundal", + "kip": "Kip", + "gal": "Gal", + "gravity": "Schwerkraft", + "hectopascal": "Hektopascal", + "atmosphere": "Atmosphäre", + "millibars": "Millibar", + "inch-of-mercury": "Zoll Quecksilbersäule", + "richter-scale": "Richterskala", + "second": "Sekunde", + "minute": "Minute", + "hour": "Stunde", + "day": "Tag", + "week": "Woche", + "month": "Monat", + "year": "Jahr", + "cubic-foot-per-minute": "Kubikfuß pro Minute", + "cubic-meters-per-hour": "Kubikmeter pro Stunde", + "cubic-meters-per-second": "Kubikmeter pro Sekunde", + "liter-per-second": "Liter pro Sekunde", + "liter-per-minute": "Liter pro Minute", + "gallons-per-minute": "Gallonen pro Minute", + "cubic-foot-per-second": "Kubikfuß pro Sekunde", + "milliliters-per-minute": "Milliliter pro Minute", + "bit": "Bit", + "byte": "Byte", + "kilobyte": "Kilobyte", + "megabyte": "Megabyte", + "gigabyte": "Gigabyte", + "terabyte": "Terabyte", + "petabyte": "Petabyte", + "exabyte": "Exabyte", + "zettabyte": "Zettabyte", + "yottabyte": "Yottabyte", + "bit-per-second": "Bit pro Sekunde", + "kilobit-per-second": "Kilobit pro Sekunde", + "megabit-per-second": "Megabit pro Sekunde", + "gigabit-per-second": "Gigabit pro Sekunde", + "terabit-per-second": "Terabit pro Sekunde", + "byte-per-second": "Byte pro Sekunde", + "kilobyte-per-second": "Kilobyte pro Sekunde", + "megabyte-per-second": "Megabyte pro Sekunde", + "gigabyte-per-second": "Gigabyte pro Sekunde", + "degree": "Grad", + "radian": "Radiant", + "gradian": "Gon", + "revolution": "Umdrehung", + "siemens": "Siemens", + "millisiemens": "Millisimens", + "microsiemens": "Mikrosiemens", + "kilosiemens": "Kilosiemens", + "megasiemens": "Megasiemens", + "gigasiemens": "Gigasiemens", + "farad": "Farad", + "millifarad": "Millifarad", + "microfarad": "Mikrofarad", + "nanofarad": "Nanofarad", + "picofarad": "Pikofarad", + "kilofarad": "Kilofarad", + "megafarad": "Megafarad", + "gigafarad": "Gigafarad", + "terfarad": "Terafarad", + "farad-per-meter": "Farad pro Meter", + "tesla": "Tesla", + "gauss": "Gauss", + "kilogauss": "Kilogauss", + "millitesla": "Millitesla", + "microtesla": "Mikrotesla", + "nanotesla": "Nanotesla", + "kilotesla": "Kilotesla", + "megatesla": "Megatesla", + "millitesla-square-meters": "Millitesla-Quadratmeter", + "gamma": "Gamma", + "lambda": "Lambda", + "square-meter-per-second": "Quadratmeter pro Sekunde", + "square-centimeter-per-second": "Quadratzentimeter pro Sekunde", + "stoke": "Stoke", + "centistokes": "Zentistokes", + "square-foot-per-second": "Quadratfuß pro Sekunde", + "square-inch-per-second": "Quadratzoll pro Sekunde", + "pascal-second": "Pascal-Sekunde", + "centipoise": "Zentipoise", + "poise": "Poise", + "reynolds": "Reynolds", + "pound-per-foot-hour": "Pfund pro Fuß-Stunde", + "newton-second-per-square-meter": "Newtonsekunde pro Quadratmeter", + "dyne-second-per-square-centimeter": "Dynesekunde pro Quadratzentimeter", + "kilogram-per-meter-second": "Kilogramm pro Meter-Sekunde", + "tesla-square-meters": "Tesla-Quadratmeter", + "maxwell": "Maxwell", + "tesla-per-meter": "Tesla pro Meter", + "gauss-per-centimeter": "Gauss pro Zentimeter", + "weber": "Weber", + "microweber": "Mikroweber", + "milliweber": "Milliweber", + "gauss-square-centimeter": "Gauss-Quadratzentimeter", + "kilogauss-square-centimeter": "Kilogauss-Quadratzentimeter", + "henry": "Henry", + "millihenry": "Millihenry", + "microhenry": "Mikrohenry", + "nanohenry": "Nanohenry", + "henry-per-meter": "Henry pro Meter", + "tesla-meter-per-ampere": "Tesla-Meter pro Ampere", + "gauss-per-oersted": "Gauss pro Oersted", + "kilogram-per-mole": "Kilogramm pro Mol", + "gram-per-mole": "Gramm pro Mol", + "milligram-per-mole": "Milligramm pro Mol", + "joule-per-mole": "Joule pro Mol", + "joule-per-mole-kelvin": "Joule pro Mol-Kelvin", + "millivolts-per-meter": "Millivolt pro Meter", + "volts-per-meter": "Volt pro Meter", + "kilovolts-per-meter": "Kilovolt pro Meter", + "radian-per-second": "Radiant pro Sekunde", + "radian-per-second-squared": "Radiant pro Sekunde zum Quadrat", + "revolutions-per-minute-per-second": "Drehbeschleunigung", + "deg-per-second": "Grad pro Sekunde", + "degrees-brix": "Grad Brix", + "katal": "Katal", + "katal-per-cubic-metre": "Katal pro Kubikmeter" }, "user": { - "all": "Alle", - "all-users": "Alle Benutzer", - "groups": "Gruppen", "user": "Benutzer", "users": "Benutzer", - "management": "Benutzerverwaltung", - "customer-users": "Kunden-Benutzer", - "tenant-admins": "Mandanten-Administratoren", - "sys-admin": "System-Administrator", - "tenant-admin": "Mandanten-Administrator", + "customer-users": "Kundenbenutzer", + "tenant-admins": "Tenant-Administratoren", + "sys-admin": "Systemadministrator", + "tenant-admin": "Tenant-Administrator", "customer": "Kunde", "anonymous": "Anonym", "add": "Benutzer hinzufügen", "delete": "Benutzer löschen", "add-user-text": "Neuen Benutzer hinzufügen", "no-users-text": "Keine Benutzer gefunden", - "user-details": "Benutzer-Details", - "delete-users": "Benutzer löschen", + "user-details": "Benutzerdetails", "delete-user-title": "Möchten Sie den Benutzer '{{userEmail}}' wirklich löschen?", - "delete-user-text": "Vorsicht, nach Bestätigung werden der Benutzer und alle zugehörigen Daten gelöscht.", - "delete-users-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Benutzer} other {# Benutzer} } löschen möchten??", - "delete-users-action-title": "{ count, plural, =1 {1 Benutzer} other {# Benutzer} } löschen", - "delete-users-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Benutzer entfernt und alle zugehörigen Daten werden gelöscht.", - "activation-email-sent-message": "Aktivierungs E-Mail wurde erfolgreich gesendet!", + "delete-user-text": "Vorsicht! Nach der Bestätigung werden der Benutzer und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-users-title": "Möchten Sie wirklich { count, plural, =1 {1 Benutzer} other {# Benutzer} } löschen?", + "delete-users-action-title": "{ count, plural, =1 {1 Benutzer löschen} other {# Benutzer löschen} }", + "delete-users-text": "Vorsicht! Nach der Bestätigung werden alle ausgewählten Benutzer und zugehörige Daten gelöscht.", + "activation-email-sent-message": "Aktivierungs-E-Mail wurde erfolgreich gesendet!", "resend-activation": "Aktivierung erneut senden", "email": "E-Mail", "email-required": "E-Mail ist erforderlich.", - "invalid-email-format": "Ungültiges E-Mail Format.", + "invalid-email-format": "Ungültiges E-Mail-Format.", "first-name": "Vorname", "last-name": "Nachname", "description": "Beschreibung", "default-dashboard": "Standard-Dashboard", - "always-fullscreen": "Immer Vollbild", + "always-fullscreen": "Immer Vollbildmodus", "select-user": "Benutzer auswählen", - "no-users-matching": "Keine passenden Benutzer für '{{entity}}' gefunden.", + "no-users-matching": "Keine Benutzer, die mit '{{entity}}' übereinstimmen, gefunden.", "user-required": "Benutzer ist erforderlich", "activation-method": "Aktivierungsmethode", "display-activation-link": "Aktivierungslink anzeigen", - "send-activation-mail": "Aktivierungs E-Mail senden", - "activation-link": "Link zur Benutzer-Aktivierung", - "activation-link-text": "Um den Benutzer zu aktivieren, verwenden Sie bitte folgenden Aktivierungslink:", + "send-activation-mail": "Aktivierungsmail senden", + "activation-link": "Benutzeraktivierungslink", + "activation-link-text": "Um den Benutzer zu aktivieren, verwenden Sie folgenden Aktivierungslink (gültig für {{activationLinkTtl}}):", "copy-activation-link": "Aktivierungslink kopieren", - "activation-link-copied-message": "Der Link zur Benutzer-Aktivierung wurde in die Zwischenablage kopiert ", + "activation-link-copied-message": "Benutzeraktivierungslink wurde in die Zwischenablage kopiert", "details": "Details", - "login-as-tenant-admin": "Als Mandanten-Administrator anmelden", - "login-as-customer-user": "Als Kunden-Benutzer anmelden", + "login-as-tenant-admin": "Als Tenant-Admin anmelden", + "login-as-customer-user": "Als Kundenbenutzer anmelden", + "search": "Benutzer suchen", + "selected-users": "{ count, plural, =1 {1 Benutzer} other {# Benutzer} } ausgewählt", "disable-account": "Benutzerkonto deaktivieren", "enable-account": "Benutzerkonto aktivieren", "enable-account-message": "Benutzerkonto wurde erfolgreich aktiviert!", "disable-account-message": "Benutzerkonto wurde erfolgreich deaktiviert!", - "copyId": "Benutzer-Id kopieren", - "idCopiedMessage": "Benutzer-Id in die Zwischenablage kopiert", + "copyId": "Benutzer-ID kopieren", + "idCopiedMessage": "Benutzer-ID wurde in die Zwischenablage kopiert", "user-list": "Benutzerliste", "user-list-required": "Benutzerliste ist erforderlich" }, "value": { - "type": "Wertetyp", - "string": "Text", - "string-value": "Textwert", - "integer": "Ganzzahlig", - "integer-value": "Ganzzahliger Wert", - "invalid-integer-value": "Ungültiger ganzzahliger Wert", - "double": "Gleitkommazahl", - "double-value": "Gleitkomma Wert", - "boolean": "Binär", - "boolean-value": "Binärwert", + "type": "Werttyp", + "string": "Zeichenkette", + "string-value": "Zeichenkettenwert", + "string-value-required": "Zeichenkettenwert ist erforderlich", + "integer": "Ganzzahl", + "integer-value": "Ganzzahlwert", + "integer-value-required": "Ganzzahlwert ist erforderlich", + "invalid-integer-value": "Ungültiger Ganzzahlwert", + "double": "Dezimalzahl", + "double-value": "Dezimalwert", + "double-value-required": "Dezimalwert ist erforderlich", + "boolean": "Boolesch", + "boolean-value": "Boolescher Wert", "false": "Falsch", "true": "Wahr", - "long": "Lang" + "long": "Lange Ganzzahl", + "json": "JSON", + "json-value": "JSON-Wert", + "json-value-invalid": "JSON-Wert hat ein ungültiges Format", + "json-value-required": "JSON-Wert ist erforderlich." + }, + "version-control": { + "version-control": "Versionskontrolle", + "management": "Versionskontrollverwaltung", + "search": "Versionen durchsuchen", + "branch": "Zweig", + "default": "Standard", + "select-branch": "Zweig auswählen", + "branch-required": "Zweig ist erforderlich", + "create-entity-version": "Entitätsversion erstellen", + "version-name": "Versionsname", + "version-name-required": "Versionsname ist erforderlich", + "author": "Autor", + "export-relations": "Beziehungen exportieren", + "export-attributes": "Attribute exportieren", + "export-credentials": "Zugangsdaten exportieren", + "export-calculated-fields": "Berechnete Felder exportieren", + "entity-versions": "Entitätsversionen", + "versions": "Versionen", + "created-time": "Erstellungszeit", + "version-id": "Versions-ID", + "no-entity-versions-text": "Keine Entitätsversionen gefunden", + "no-versions-text": "Keine Versionen gefunden", + "copy-full-version-id": "Vollständige Versions-ID kopieren", + "create-version": "Version erstellen", + "creating-version": "Version wird erstellt... Bitte warten", + "nothing-to-commit": "Keine Änderungen zum Übernehmen", + "restore-version": "Version wiederherstellen", + "restore-entity-from-version": "Entität aus Version '{{versionName}}' wiederherstellen", + "restoring-entity-version": "Entitätsversion wird wiederhergestellt... Bitte warten", + "load-relations": "Beziehungen laden", + "load-attributes": "Attribute laden", + "load-credentials": "Zugangsdaten laden", + "load-calculated-fields": "Berechnete Felder laden", + "compare-with-current": "Mit aktueller vergleichen", + "diff-entity-with-version": "Unterschiede mit Entitätsversion '{{versionName}}'", + "previous-difference": "Vorheriger Unterschied", + "next-difference": "Nächster Unterschied", + "current": "Aktuell", + "differences": "{ count, plural, =1 {1 Unterschied} other {# Unterschiede} }", + "create-entities-version": "Entitätsversion erstellen", + "default-sync-strategy": "Standard-Synchronisierungsstrategie", + "sync-strategy-merge": "Zusammenführen", + "sync-strategy-overwrite": "Überschreiben", + "entities-to-export": "Zu exportierende Entitäten", + "entities-to-restore": "Zu wiederherstellende Entitäten", + "sync-strategy": "Synchronisierungsstrategie", + "all-entities": "Alle Entitäten", + "no-entities-to-export-prompt": "Bitte Entitäten zum Export angeben", + "no-entities-to-restore-prompt": "Bitte Entitäten zur Wiederherstellung angeben", + "add-entity-type": "Entitätstyp hinzufügen", + "remove-all": "Alle entfernen", + "version-create-result": "{ added, plural, =0 {Keine Entitäten} =1 {1 Entität} other {# Entitäten} } hinzugefügt.
{ modified, plural, =0 {Keine Entitäten} =1 {1 Entität} other {# Entitäten} } geändert.
{ removed, plural, =0 {Keine Entitäten} =1 {1 Entität} other {# Entitäten} } entfernt.", + "remove-other-entities": "Andere Entitäten entfernen", + "find-existing-entity-by-name": "Vorhandene Entität nach Namen finden", + "restore-entities-from-version": "Entitäten aus Version '{{versionName}}' wiederherstellen", + "restoring-entities-from-version": "Entitäten werden wiederhergestellt... Bitte warten", + "no-entities-restored": "Keine Entitäten wiederhergestellt", + "created": "{{created}} erstellt", + "updated": "{{updated}} aktualisiert", + "deleted": "{{deleted}} gelöscht", + "remove-other-entities-confirm-text": "Achtung! Dies wird alle aktuellen Entitäten dauerhaft löschen
die nicht in der wiederherzustellenden Version vorhanden sind.

Bitte geben Sie \"andere Entitäten entfernen\" ein, um zu bestätigen.", + "auto-commit-to-branch": "automatisch in {{ branch }}-Zweig übernehmen", + "default-create-entity-version-name": "{{entityName}} Aktualisierung", + "sync-strategy-merge-hint": "Erstellt oder aktualisiert ausgewählte Entitäten im Repository. Alle anderen Repository-Entitäten werden nicht verändert.", + "sync-strategy-overwrite-hint": "Erstellt oder aktualisiert ausgewählte Entitäten im Repository. Alle anderen Repository-Entitäten werden gelöscht.", + "device-credentials-conflict": "Gerät mit externer ID {{entityId}} konnte nicht geladen werden,
da dieselben Zugangsdaten bereits für ein anderes Gerät vorhanden sind.
Bitte erwägen Sie, die Einstellung Zugangsdaten laden im Wiederherstellungsformular zu deaktivieren.", + "missing-referenced-entity": "{{sourceEntityTypeName}} mit externer ID {{sourceEntityId}} konnte nicht geladen werden,
da sie auf eine fehlende {{targetEntityTypeName}} mit ID {{targetEntityId}} verweist.", + "runtime-failed": "Fehlgeschlagen: {{message}}", + "auto-commit-settings-read-only-hint": "Auto-Commit funktioniert nicht, wenn die Option 'Nur-Lesen' in den Repository-Einstellungen aktiviert ist.", + "rollback-on-error": "Bei Fehler zurücksetzen", + "rollback-on-error-hint": "Wenn Sie eine große Menge an Entitäten wiederherstellen, deaktivieren Sie diese Option zur Leistungssteigerung.\nBeachten Sie, dass bei einem Fehler bereits gespeicherte Entitäten (mit Beziehungen, Attributen etc.) erhalten bleiben." }, "widget": { "widget-library": "Widget-Bibliothek", - "widget-bundle": "Widget-Paket", - "select-widgets-bundle": "Widget-Paket auswählen", - "management": "Widget Verwaltung", - "editor": "Widget Editor", - "widget-type-not-found": "Problem beim Laden der Widget-Konfiguration.
Zugeordneter Widget-Typ wurde entfernt.", - "widget-type-load-error": "Widget wurde aufgrund der folgenden Fehler nicht geladen:", + "widget-bundle": "Widget-Bundle", + "all-bundles": "Alle Bundles", + "select-widgets-bundle": "Widget-Bundle auswählen", + "widgets": "Widgets", + "all-widgets": "Alle Widgets", + "widget": "Widget", + "select-widget": "Widget auswählen", + "no-widgets-matching": "Keine Widgets gefunden, die mit '{{entity}}' übereinstimmen.", + "no-widgets": "Noch keine Widgets", + "no-widgets-text": "Keine Widgets gefunden", + "management": "Widget-Verwaltung", + "editor": "Widget-Editor", + "confirm-to-exit-editor-html": "Sie haben nicht gespeicherte Widget-Einstellungen.
Möchten Sie diese Seite wirklich verlassen?", + "widget-type-not-found": "Fehler beim Laden der Widget-Konfiguration.
Vermutlich wurde der zugehörige Widget-Typ entfernt.", + "widget-type-load-error": "Widget konnte aufgrund folgender Fehler nicht geladen werden:", "remove": "Widget entfernen", + "delete": "Widget löschen", "edit": "Widget bearbeiten", "remove-widget-title": "Möchten Sie das Widget '{{widgetTitle}}' wirklich entfernen?", - "remove-widget-text": "Nach der Bestätigung werden das Widget und alle zugehörigen Daten nicht wiederhergestellt.", + "remove-widget-text": "Nach der Bestätigung wird das Widget und alle zugehörigen Daten unwiderruflich gelöscht.", + "replace-reference-with-widget-copy": "Referenz durch Widget-Kopie ersetzen", "timeseries": "Zeitreihe", - "search-data": "Daten suchen", + "search-data": "Daten durchsuchen", "no-data-found": "Keine Daten gefunden", - "latest": "Neueste Werte", - "rpc": "Steuerungswidget", + "latest": "Aktuelle Werte", + "rpc": "Steuerungs-Widget", "alarm": "Alarm-Widget", "static": "Statisches Widget", + "timeseries-short": "reihe", + "latest-short": "aktuell", + "rpc-short": "steuerung", + "alarm-short": "alarm", + "static-short": "statisch", "select-widget-type": "Widget-Typ auswählen", "missing-widget-title-error": "Widget-Titel muss angegeben werden!", "widget-saved": "Widget gespeichert", - "unable-to-save-widget-error": "Das Widget kann nicht gespeichert werden! Fehlermeldung!", + "unable-to-save-widget-error": "Widget konnte nicht gespeichert werden! Es liegen Fehler vor!", "save": "Widget speichern", "saveAs": "Widget speichern unter", - "save-widget-type-as": "Widget-Typ speichern unter", - "save-widget-type-as-text": "Bitte geben Sie den neuen Widget-Titel ein und/oder wählen Sie das Ziel-Widget-Paket aus", - "toggle-fullscreen": "Vollbild umschalten", + "move": "Widget verschieben", + "save-widget-as": "Widget speichern unter", + "save-widget-as-text": "Bitte geben Sie einen neuen Widget-Titel ein", + "toggle-fullscreen": "Vollbildmodus umschalten", "run": "Widget ausführen", - "title": "Widget-Titel", + "widget-title": "Widget-Titel", + "title": "Titel", "title-required": "Widget-Titel ist erforderlich.", + "title-max-length": "Titel darf nicht länger als 256 Zeichen sein", + "system": "System", "type": "Widget-Typ", "resources": "Ressourcen", - "resource-url": "JavaScript/CSS URL", + "resource-url": "JavaScript/CSS-URL", + "resource-is-extension": "Ist Erweiterung", "remove-resource": "Ressource entfernen", "add-resource": "Ressource hinzufügen", "html": "HTML", - "tidy": "Aufgeräumt", + "tidy": "Ordnen", "css": "CSS", - "settings-schema": "Einstellungsschema", - "datakey-settings-schema": "Datenschlüssel-Einstellungsschema", - "javascript": "Javascript", - "add-widget-type": "Neuen Widget-Typ hinzufügen", + "settings-form": "Einstellungsformular", + "data-key-settings-form": "Daten-Schlüssel-Einstellungsformular", + "latest-data-key-settings-form": "Aktuelle Daten-Schlüssel-Einstellungsformular", + "widget-settings": "Widget-Einstellungen", + "description": "Beschreibung", + "tags": "Tags", + "image-preview": "Bildvorschau", + "settings-form-selector": "Einstellungsformularauswahl", + "data-key-settings-form-selector": "Daten-Schlüssel-Formularauswahl", + "latest-data-key-settings-form-selector": "Formularauswahl für aktuelle Daten-Schlüssel", + "all": "Alle", + "actual": "Aktuell", + "scada": "SCADA-Symbol", + "deprecated": "Veraltet", + "has-basic-mode": "Hat einfachen Modus", + "basic-mode-form-selector": "Formularauswahl für einfachen Modus", + "basic-mode": "Einfach", + "advanced-mode": "Erweitert", + "javascript": "JavaScript", + "js": "JS", + "delete-widget-title": "Möchten Sie das Widget '{{widgetName}}' wirklich löschen?", + "delete-widget-text": "Nach der Bestätigung wird das Widget und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-widgets-title": "Möchten Sie wirklich { count, plural, =1 {1 Widget} other {# Widgets} } löschen?", + "delete-widgets-text": "Nach der Bestätigung werden alle ausgewählten Widgets und alle zugehörigen Daten gelöscht.", + "delete-widget": "Widget löschen", "widget-template-load-failed-error": "Widget-Vorlage konnte nicht geladen werden!", + "details": "Details", + "widget-details": "Widget-Details", "add": "Widget hinzufügen", - "undo": "Widget-Änderungen widerrufen ", - "export": "Widget exportieren" + "add-existing-widget": "Vorhandenes Widget hinzufügen", + "add-new-widget": "Neues Widget hinzufügen", + "search-widgets": "Widgets durchsuchen", + "selected-widgets": "{ count, plural, =1 {1 Widget} other {# Widgets} } ausgewählt", + "undo": "Widget-Änderungen rückgängig machen", + "export": "Widget exportieren", + "export-prompt": "Widget-Bilder und Ressourcen einbetten", + "export-widgets": "Widgets exportieren", + "export-widgets-prompt": "Widgets-Bilder und Ressourcen einbetten", + "import": "Widget importieren", + "no-data": "Keine Daten zur Anzeige im Widget", + "data-overflow": "Widget zeigt {{count}} von {{total}} Entitäten an", + "alarm-data-overflow": "Widget zeigt Alarme für {{allowedEntities}} (maximal erlaubt) von {{totalEntities}} Entitäten an", + "search": "Widget suchen", + "filter": "Widget-Filtertyp", + "loading-widgets": "Widgets werden geladen...", + "widget-template-error": "Ungültige Widget-HTML-Vorlage.", + "reference": "Referenz" }, "widget-action": { - "header-button": "Widget-Header-Schaltfläche", - "open-dashboard-state": "Zum neuen Dashboard-Status navigieren", - "update-dashboard-state": "Aktuellen Dashboard-Status aktualisieren", - "open-dashboard": "Zu einem anderen Dashboard navigieren", + "header-button": "Widget-Kopfzeilen-Schaltfläche", + "do-nothing": "Nichts tun", + "open-dashboard-state": "Zu neuem Dashboard-Zustand navigieren", + "update-dashboard-state": "Aktuellen Dashboard-Zustand aktualisieren", + "open-dashboard": "Zu anderem Dashboard navigieren", "custom": "Benutzerdefinierte Aktion", - "target-dashboard-state": "Zielstatus des Dashboards", - "target-dashboard-state-required": "Der Zielstatus ist erforderlich", - "set-entity-from-widget": "Widget-Entität festlegen", + "custom-pretty": "Benutzerdefinierte Aktion (mit HTML-Vorlage)", + "custom-pretty-error-title": "Benutzerdefinierter Dialogfehler", + "custom-pretty-template-error": "Ungültige benutzerdefinierte Dialogvorlage.", + "custom-pretty-controller-error": "Fehler bei der Auswertung der benutzerdefinierten Dialogfunktion.", + "mobile-action": "Mobile Aktion", + "target-dashboard-state": "Ziel-Dashboard-Zustand", + "target-dashboard-state-required": "Ziel-Dashboard-Zustand ist erforderlich", + "set-entity-from-widget": "Entität vom Widget setzen", "target-dashboard": "Ziel-Dashboard", - "open-right-layout": "Das rechte Dashboard-Layout öffnen (mobile Ansicht)" + "select-target-dashboard": "Ziel-Dashboard auswählen", + "target-dashboard-required": "Ziel-Dashboard ist erforderlich.", + "open-right-layout": "Rechtes Dashboard-Layout öffnen (mobile Ansicht)", + "state-display-type": "Anzeigeoption für Dashboard-Zustand", + "open-normal": "Normal", + "open-in-separate-dialog": "In separatem Dialog öffnen", + "open-in-popover": "In Popover öffnen", + "dialog-title": "Dialogtitel", + "dialog-hide-dashboard-toolbar": "Dashboard-Werkzeugleiste im Dialog ausblenden", + "dialog-width": "Dialogbreite in Prozent relativ zur Fensterbreite", + "dialog-height": "Dialoghöhe in Prozent relativ zur Fensterhöhe", + "dialog-size-range-error": "Dialoggrößenwert muss im Bereich von 1 bis 100 liegen.", + "popover-preferred-placement": "Bevorzugte Popover-Platzierung", + "popover-placement-top": "Oben", + "popover-placement-topLeft": "Oben links", + "popover-placement-topRight": "Oben rechts", + "popover-placement-right": "Rechts", + "popover-placement-rightTop": "Rechts oben", + "popover-placement-rightBottom": "Rechts unten", + "popover-placement-bottom": "Unten", + "popover-placement-bottomLeft": "Unten links", + "popover-placement-bottomRight": "Unten rechts", + "popover-placement-left": "Links", + "popover-placement-leftTop": "Links oben", + "popover-placement-leftBottom": "Links unten", + "popover-hide-on-click-outside": "Popover beim Klicken außerhalb ausblenden", + "popover-hide-dashboard-toolbar": "Dashboard-Werkzeugleiste im Popover ausblenden", + "popover-width": "Popover-Breite", + "popover-height": "Popover-Höhe", + "popover-style": "Popover-Stil", + "open-new-browser-tab": "In neuem Browser-Tab öffnen", + "open-URL": "URL öffnen", + "URL": "URL", + "url-required": "URL ist erforderlich.", + "mobile": { + "device-provision": "Gerätekonfiguration", + "action-type": "Mobiler Aktionstyp", + "select-action-type": "Mobilen Aktionstyp auswählen", + "action-type-required": "Mobiler Aktionstyp ist erforderlich", + "take-picture-from-gallery": "Bild aus Galerie auswählen", + "take-photo": "Foto aufnehmen", + "map-direction": "Kartennavigation öffnen", + "map-location": "Kartenstandort öffnen", + "scan-qr-code": "QR-Code scannen", + "make-phone-call": "Anruf tätigen", + "get-location": "Standort des Telefons abrufen", + "take-screenshot": "Screenshot erstellen" + }, + "custom-action-function": "Benutzerdefinierte Aktionsfunktion", + "custom-pretty-function": "Benutzerdefinierte Aktion (mit HTML-Vorlage) Funktion", + "map-item-type": "Kartenelementtyp", + "map-item": { + "marker": "Markierung", + "polygon": "Polygon", + "rectangle": "Rechteck", + "circle": "Kreis" + }, + "place-map-item": "Kartenelement platzieren", + "map-item-tooltip": { + "customize-map-item-tooltips": "Tooltips für Kartenelemente anpassen", + "place-marker": "Markierung setzen", + "start-draw-rectangle": "Rechteck zeichnen starten", + "finish-draw-rectangle": "Rechteckzeichnen beenden", + "start-draw-polygon": "Polygon zeichnen starten", + "continue-draw-polygon": "Polygon weiterzeichnen", + "finish-draw-polygon": "Polygonzeichnen beenden", + "start-draw-circle": "Kreis zeichnen starten", + "finish-draw-circle": "Kreiszeichnen beenden" + } }, "widgets-bundle": { - "current": "Aktuelles Paket", - "widgets-bundles": "Widget-Pakete", - "add": "Widget-Pakete hinzufügen", - "delete": "Widget-Pakete löschen", + "current": "Aktuelles Bundle", + "widgets-bundles": "Widget-Bundles", + "widgets-bundle-widgets": "Widgets im Bundle", + "add": "Widget-Bundle hinzufügen", + "delete": "Widget-Bundle löschen", "title": "Titel", "title-required": "Titel ist erforderlich.", - "add-widgets-bundle-text": "Neues Widget-Paket hinzufügen", - "no-widgets-bundles-text": "Keine Widget-Pakete gefunden", - "empty": "Widget-Paket ist leer ", + "title-max-length": "Titel darf nicht länger als 256 Zeichen sein", + "description": "Beschreibung", + "image-preview": "Bildvorschau", + "scada": "SCADA-Widget-Bundle", + "order": "Reihenfolge", + "add-widgets-bundle-text": "Neues Widget-Bundle hinzufügen", + "no-widgets-bundles-text": "Keine Widget-Bundles gefunden", + "empty": "Widget-Bundle ist leer", "details": "Details", - "widgets-bundle-details": "Widget-Paket-Details", - "delete-widgets-bundle-title": "Möchten Sie das Widget-Paket '{{widgetsBundleTitle}}' wirklich löschen? ", - "delete-widgets-bundle-text": "Seien Sie vorsichtig, nach der Bestätigung werden das Widget-Paket und alle zugehörigen Daten gelöscht.", - "delete-widgets-bundles-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Widget-Paket} other {# Widget-Pakete} } löschen möchten?", - "delete-widgets-bundles-action-title": "{ count, plural, =1 {1 Widgets-Paket} other {# Widget-Pakete} } löschen", - "delete-widgets-bundles-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Widget-Pakete entfernt und alle zugehörigen Daten werden gelöscht.", - "no-widgets-bundles-matching": "Keine passenden Widget-Pakete '{{widgetsBundle}}' gefunden.", - "widgets-bundle-required": "Widget-Paket ist erforderlich.", + "widgets-bundle-details": "Details des Widget-Bundles", + "delete-widgets-bundle-title": "Möchten Sie das Widget-Bundle '{{widgetsBundleTitle}}' wirklich löschen?", + "delete-widgets-bundle-text": "Seien Sie vorsichtig, nach der Bestätigung werden das Widget-Bundle und alle zugehörigen Daten unwiederbringlich gelöscht.", + "delete-widgets-bundles-title": "Möchten Sie wirklich { count, plural, =1 {1 Widget-Bundle} other {# Widget-Bundles} } löschen?", + "delete-widgets-bundles-action-title": "{ count, plural, =1 {1 Widget-Bundle löschen} other {# Widget-Bundles löschen} }", + "delete-widgets-bundles-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Widget-Bundles und alle zugehörigen Daten gelöscht.", + "no-widgets-bundles-matching": "Keine Widget-Bundles gefunden, die mit '{{widgetsBundle}}' übereinstimmen.", + "widgets-bundle-required": "Widget-Bundle ist erforderlich.", "system": "System", - "import": "Widget-Paket importieren", - "export": "Widget-Paket exportieren", - "export-failed-error": "Widget-Paket kann nicht exportiert werden: {{error}}", - "create-new-widgets-bundle": "Neues Widget-Paket erstellen", - "widgets-bundle-file": "Widget-Paket-Datei", - "invalid-widgets-bundle-file-error": "Widget-Paket kann nicht importiert werden: Ungültige Widget-Paket-Datenstruktur." + "import": "Widget-Bundle importieren", + "export": "Widget-Bundle exportieren", + "export-widgets-bundle-widgets-prompt": "Bundle-Widgets in exportierte Daten einbeziehen (ansonsten werden nur referenzierte Widget-FQNs exportiert)", + "export-failed-error": "Widget-Bundle konnte nicht exportiert werden: {{error}}", + "create-new-widgets-bundle": "Neues Widget-Bundle erstellen", + "widgets-bundle-file": "Widget-Bundle-Datei", + "invalid-widgets-bundle-file-error": "Widget-Bundle konnte nicht importiert werden: Ungültige Datenstruktur.", + "search": "Widget-Bundles durchsuchen", + "selected-widgets-bundles": "{ count, plural, =1 {1 Widget-Bundle} other {# Widget-Bundles} } ausgewählt", + "open-widgets-bundle": "Widget-Bundle öffnen", + "loading-widgets-bundles": "Widget-Bundles werden geladen...", + "create-new": "Neues Widget-Bundle erstellen" }, "widget-config": { "data": "Daten", "settings": "Einstellungen", - "advanced": "Erweitert ", + "advanced": "Erweitert", + "appearance": "Erscheinungsbild", + "widget-card": "Widget-Karte", + "mobile": "Mobil", "title": "Titel", - "title-tooltip": "Titel Tooltip", + "title-tooltip": "Titel-Tooltip", "general-settings": "Allgemeine Einstellungen", - "display-title": "Titel anzeigen", - "drop-shadow": "Schlagschatten", + "display-title": "Widget-Titel anzeigen", + "card-title": "Kartentitel", + "drop-shadow": "Schatteneffekt", "enable-fullscreen": "Vollbild aktivieren", "background-color": "Hintergrundfarbe", "text-color": "Textfarbe", - "padding": "Pufferung", - "margin": "Rand", + "border-radius": "Rahmenradius", + "padding": "Innenabstand (Padding)", + "margin": "Außenabstand (Margin)", "widget-style": "Widget-Stil", - "title-style": "Titel-Stil", - "mobile-mode-settings": "Einstellungen für den mobilen Modus", + "widget-css": "Widget-CSS", + "title-style": "Titelstil", + "mobile-mode-settings": "Mobile Ansicht", "order": "Reihenfolge", - "height": "Größe", - "units": "Spezielles Symbol, das neben dem Wert angezeigt wird", - "decimals": "Anzahl der Stellen nach dem Fließkomma", + "height": "Höhe", + "mobile-hide": "Widget in mobiler Ansicht ausblenden", + "desktop-hide": "Widget in Desktop-Ansicht ausblenden", + "units": "Sonderzeichen neben dem Wert anzeigen", + "units-by-default": "Standard-Einheiten", + "decimals": "Anzahl der Nachkommastellen", + "decimals-by-default": "Standard-Nachkommastellen", + "default-data-key-parameter-hint": "Dieser Parameter gilt für alle Widget-Werte, sofern nicht durch die Daten-Schlüsselkonfiguration überschrieben.", + "units-short": "Einheiten", + "decimals-short": "Nachkommastellen", + "decimals-suffix": "Nachkommastellen", + "digits-suffix": "Ziffern", "timewindow": "Zeitfenster", "use-dashboard-timewindow": "Dashboard-Zeitfenster verwenden", + "use-widget-timewindow": "Widget-Zeitfenster verwenden", "display-timewindow": "Zeitfenster anzeigen", + "legend": "Legende", "display-legend": "Legende anzeigen", "datasources": "Datenquellen", - "maximum-datasources": "Maximal { count, plural, =1 {1 Datenquelle ist erlaubt} other {# Datenquellen sind erlaubt} }.", + "datasource": "Datenquelle", + "maximum-datasources": "Maximal { count, plural, =1 {1 Datenquelle erlaubt.} other {# Datenquellen erlaubt} }", + "timeseries-key-error": "Mindestens ein Zeitreihen-Datenschlüssel muss angegeben werden", "datasource-type": "Typ", "datasource-parameters": "Parameter", "remove-datasource": "Datenquelle entfernen", - "add-datasource": "Datenquelle hinzufügen ", + "add-datasource": "Datenquelle hinzufügen", "target-device": "Zielgerät", "alarm-source": "Alarmquelle", "actions": "Aktionen", "action": "Aktion", "add-action": "Aktion hinzufügen", - "search-actions": "Aktion suchen", + "search-actions": "Aktionen suchen", + "no-actions-text": "Keine Aktionen gefunden", "action-source": "Aktionsquelle", + "select-action-source": "Aktionsquelle auswählen", "action-source-required": "Aktionsquelle ist erforderlich.", + "column-index": "Spaltenindex", + "select-column-index": "Spaltenindex auswählen", + "column-index-required": "Spaltenindex ist erforderlich.", + "not-set": "Nicht gesetzt", "action-name": "Name", "action-name-required": "Aktionsname ist erforderlich.", - "action-name-not-unique": "Eine andere Aktion mit demselben Namen ist bereits vorhanden.\n Der Aktionsname sollte innerhalb derselben Aktionsquelle eindeutig sein.", - "action-icon": "Symbol ", - "action-type": "Art", - "action-type-required": "Aktionsart ist erforderlich.", + "action-name-not-unique": "Eine andere Aktion mit demselben Namen existiert bereits.\nDer Aktionsname muss innerhalb derselben Quelle eindeutig sein.", + "action-icon": "Symbol", + "header-button": { + "button-settings": "Schaltflächeneinstellungen", + "button-type": "Schaltflächentyp", + "button-type-basic": "Einfach", + "button-type-raised": "Erhöht", + "button-type-stroked": "Umrandet", + "button-type-flat": "Flach", + "button-type-icon": "Symbol", + "button-type-mini-fab": "FAB", + "colors": "Farben", + "color": "Farbe", + "background": "Hintergrund", + "border": "Rahmen", + "advanced-button-style": "Erweiterter Schaltflächenstil", + "button-style": "Schaltflächenstil" + }, + "show-hide-action-using-function": "Aktion über Funktion ein-/ausblenden", + "show-action-function": "Funktion zum Anzeigen der Aktion", + "action-type": "Typ", + "action-type-required": "Aktionstyp ist erforderlich.", "edit-action": "Aktion bearbeiten", "delete-action": "Aktion löschen", "delete-action-title": "Widget-Aktion löschen", - "delete-action-text": "Möchten Sie die Widget-Aktion mit Namen '{{actionName}}' wirklich löschen?" + "delete-action-text": "Möchten Sie die Widget-Aktion mit dem Namen '{{actionName}}' wirklich löschen?", + "title-icon": "Titelsymbol", + "display-icon": "Titelsymbol anzeigen", + "card-icon": "Kartensymbol", + "icon": "Symbol", + "icon-color": "Symbolfarbe", + "icon-size": "Symbolgröße", + "advanced-settings": "Erweiterte Einstellungen", + "data-settings": "Daten-Einstellungen", + "limits": "Grenzwerte", + "no-data-display-message": "Alternative Nachricht bei fehlenden Daten", + "data-page-size": "Maximale Anzahl von Entitäten pro Datenquelle", + "settings-component-not-found": "Einstellungsformular-Komponente für Selector '{{selector}}' nicht gefunden", + "preview": "Vorschau", + "set": "Festlegen", + "set-message": "Nachricht festlegen", + "advanced-title-style": "Erweiterter Titelstil", + "card-style": "Kartenstil", + "text": "Text", + "background": "Hintergrund", + "advanced-widget-style": "Erweiterter Widget-Stil", + "card-buttons": "Karten-Schaltflächen", + "show-card-buttons": "Karten-Schaltflächen anzeigen", + "card-border-radius": "Karten-Rahmenradius", + "card-padding": "Karten-Abstand innen", + "card-appearance": "Karten-Erscheinungsbild", + "color": "Farbe", + "tooltip": "Tooltip", + "units-required": "Einheit ist erforderlich.", + "list-layout": "Listenlayout", + "layout": "Layout", + "resize-options": "Größenänderungsoptionen", + "resizable": "Größenänderbar", + "preserve-aspect-ratio": "Seitenverhältnis beibehalten" }, "widget-type": { "import": "Widget-Typ importieren", "export": "Widget-Typ exportieren", - "export-failed-error": "Widget-Typ kann nicht exportiert werden: {{error}}", - "create-new-widget-type": "Neuen Widget-Typ erstellen", - "widget-type-file": "Widget-Typdatei", - "invalid-widget-type-file-error": "Widget-Typ kann nicht importiert werden: Ungültige Datenstruktur des Widget-Typs." + "export-failed-error": "Widget konnte nicht exportiert werden: {{error}}", + "widget-file": "Widget-Datei", + "invalid-widget-file-error": "Widget konnte nicht importiert werden: Ungültige Widget-Datenstruktur." + }, + "markdown": { + "edit": "Bearbeiten", + "preview": "Vorschau", + "copy-code": "Zum Kopieren klicken", + "copied": "Kopiert!" }, "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "Die Konfiguration hängt vom QR-Code-Widget der mobilen App in den Haupteinstellungen der Plattform ab", + "get-it-on-google-play": "Im Google Play Store herunterladen", + "download-on-the-app-store": "Im App Store herunterladen" + }, + "action-button": { + "behavior": "Verhalten", + "on-click": "Beim Klicken", + "on-click-hint": "Aktion, die beim Klicken auf die Schaltfläche ausgelöst wird", + "first-button-click": "Erster Klick", + "first-button-click-hint": "Aktion beim Drücken der ersten Schaltfläche", + "second-button-click": "Zweiter Klick", + "second-button-click-hint": "Aktion beim Drücken der zweiten Schaltfläche", + "button-click-hint": "Aktion beim Drücken auf das Widget" + }, + "command-button": { + "behavior": "Verhalten", + "on-click": "Beim Klicken", + "on-click-hint": "Aktion, die beim Klicken auf die Schaltfläche ausgeführt wird." + }, + "power-button": { + "behavior": "Verhalten", + "power-on": "Einschalten", + "power-on-hint": "Aktion zum Einschalten der Komponente", + "power-off": "Ausschalten", + "power-off-hint": "Aktion zum Ausschalten der Komponente", + "on-label": "Ein", + "off-label": "Aus", + "layout": "Layout", + "layout-default": "Standard", + "layout-simplified": "Vereinfacht", + "layout-outlined": "Umrandet", + "layout-default-volume": "Standard.Lautstärke", + "layout-simplified-volume": "Vereinfacht.Lautstärke", + "layout-outlined-volume": "Umrandet.Lautstärke", + "layout-default-icon": "Standard.Symbol", + "layout-simplified-icon": "Vereinfacht.Symbol", + "layout-outlined-icon": "Umrandet.Symbol", + "main": "Hauptbereich", + "background": "Hintergrund", + "button-icon-on": "Symbol bei 'Ein'", + "button-icon-off": "Symbol bei 'Aus'", + "power-on-colors": "Farben für 'Ein'", + "power-off-colors": "Farben für 'Aus'", + "disabled-colors": "Farben für deaktiviert", + "button": "Schaltfläche" + }, + "toggle-button": { + "behavior": "Verhalten", + "checked": "Ausgewählt", + "unchecked": "Nicht ausgewählt", + "check": "Auswählen", + "check-hint": "Aktion zum Auswählen der Komponente", + "uncheck": "Abwählen", + "uncheck-hint": "Aktion zum Abwählen der Komponente", + "auto-scale": "Automatische Skalierung", + "horizontal-fill": "Horizontal füllen", + "vertical-fill": "Vertikal füllen", + "button-appearance": "Schaltflächenstil" + }, + "segmented-button": { + "layout": "Layout", + "layout-squared": "Eckig", + "layout-rounded": "Abgerundet", + "card-border": "Kartenrahmen", + "button-appearance": "Schaltflächenstil", + "first": "Erste", + "second": "Zweite", + "color-styles": "Farbstile", + "selected": "Ausgewählt", + "unselected": "Nicht ausgewählt" + }, + "button": { + "layout": "Layout", + "outlined": "Umrandet", + "filled": "Ausgefüllt", + "underlined": "Unterstrichen", + "basic": "Einfach", + "auto-scale": "Automatisch skalieren", + "label": "Beschriftung", + "icon": "Symbol", + "border-radius": "Randradius", + "color-palette": "Farbpalette", + "main": "Hauptbereich", + "background": "Hintergrund", + "border": "Rahmen", + "custom-styles": "Benutzerdefinierte Stile", + "clear-style": "Stil zurücksetzen", + "shadow": "Schatten", + "enabled": "Aktiviert", + "disabled": "Deaktiviert", + "preview": "Vorschau", + "copy-style-from": "Stil kopieren von" + }, + "value-stepper": { + "behavior": "Verhalten", + "simplified": "Vereinfacht", + "filled": "Ausgefüllt", + "outlined": "Umrandet", + "volume": "Lautstärke", + "initial-state": "Anfangszustand", + "initial-state-hint": "Aktion zum Abrufen des Anfangswerts.", + "disabled-state": "Deaktivierter Zustand", + "disabled-state-hint": "Bedingung, unter der die Komponente deaktiviert ist.", + "right-button-click": "Klick auf rechte Schaltfläche", + "right-button-click-hint": "Aktion beim Klicken auf die rechte Schaltfläche.", + "left-button-click": "Klick auf linke Schaltfläche", + "left-button-click-hint": "Aktion beim Klicken auf die linke Schaltfläche.", + "auto-scale": "Automatisch skalieren", + "value-range": "Wertebereich", + "min-range": "Min", + "max-range": "Max", + "value-increment-decrement-step": "Wertänderungsschritt", + "value": "Wert", + "value-box-background": "Hintergrund des Wertfelds", + "border": "Rahmen", + "button-appearance": "Schaltflächenstil", + "left": "Links", + "right": "Rechts", + "left-button": "Linke Schaltfläche", + "right-button": "Rechte Schaltfläche", + "icon": "Symbol", + "color-palette": "Farbpalette", + "main": "Hauptbereich", + "background": "Hintergrund", + "button-icon-on": "Symbol bei 'Ein'", + "button-on-colors": "Farben bei 'Ein'", + "disabled-colors": "Farben bei 'Deaktiviert'" + }, + "button-state": { + "activated-state": "Aktivierter Zustand", + "activated-state-hint": "Bedingung, unter der die Schaltfläche aktiv ist.", + "disabled-state": "Deaktivierter Zustand", + "disabled-state-hint": "Bedingung, unter der die Schaltfläche deaktiviert ist.", + "selected-state": "Ausgewählter Zustand", + "selected-state-hint": "Bedingung, unter der die Schaltfläche ausgewählt ist.", + "enabled": "Aktiviert", + "hovered": "Hover", + "pressed": "Gedrückt", + "activated": "Aktiviert", + "disabled": "Deaktiviert", + "initial": "Erste Schaltfläche", + "first": "Erste", + "second": "Zweite" + }, + "background": { + "background": "Hintergrund", + "background-settings": "Hintergrundeinstellungen", + "background-type-image": "Bild", + "background-type-color": "Farbe", + "image-url": "Bild-URL", + "overlay": "Überlagerung", + "enable-overlay": "Überlagerung aktivieren", + "blur": "Weichzeichnen", + "preview": "Vorschau" + }, + "bar-chart": { + "bar-appearance": "Balkenanzeige", + "label-on-bar": "Beschriftung auf dem Balken", + "value-on-bar": "Wert auf dem Balken", + "bar-chart-style": "Balkendiagramm-Stil", + "bar-axis": "Balkenachse" + }, + "polar-area-chart": { + "polar-axis": "Polare Achse", + "start-angle": "Startwinkel", + "polar-area-chart-style": "Polardiagramm-Stil" + }, + "battery-level": { + "layout": "Layout", + "layout-vertical-solid": "Vertikal. Durchgehend", + "layout-horizontal-solid": "Horizontal. Durchgehend", + "layout-vertical-divided": "Vertikal. Geteilt", + "layout-horizontal-divided": "Horizontal. Geteilt", + "icon": "Symbol", + "value": "Wert", + "auto-scale": "Automatisch skalieren", + "battery-level-color": "Akkustandfarbe", + "battery-shape-color": "Akkuformfarbe", + "battery-level-card-style": "Akkustands-Kartenstil", + "sections-count": "Anzahl der Abschnitte" + }, + "signal-strength": { + "value": "Wert", + "last-update": "Letzte Aktualisierung", + "no-signal": "Kein Signal", + "layout": "Layout", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "Mobilfunkbalken", + "icon": "Symbol", + "date": "Datum", + "active-bars-color": "Farbe der aktiven Balken", + "inactive-bars-color": "Farbe der inaktiven Balken", + "signal-strength-card-style": "Signalstärke-Kartenstil", + "no-signal-rssi-value": "\"Kein Signal\" RSSI-Wert" + }, + "status-widget": { + "behavior": "Verhalten", + "layout": "Layout", + "layout-default": "Standard", + "layout-center": "Zentriert", + "layout-icon": "Symbol", + "on": "Ein", + "off": "Aus", + "label": "Beschriftung", + "status": "Status", + "icon": "Symbol", + "color-palette": "Farbpalette", + "disabled-color-palette": "Farbpalette bei Deaktivierung", + "primary": "Primär", + "primary-color-hint": "Farbe von Symbol und Beschriftung", + "secondary": "Sekundär", + "secondary-color-hint": "Farbe des Status", + "background": "Hintergrund" + }, + "chart": { + "common-settings": "Allgemeine Einstellungen", + "enable-stacking-mode": "Stacking-Modus aktivieren", + "selection": "Zeitraum-Auswahl", + "enable-selection-mode": "Auswahlmodus aktivieren", + "line-shadow-size": "Linienschatten-Größe", + "display-smooth-lines": "Glatte (gebogene) Linien anzeigen", + "default-bar-width": "Standardbalkenbreite für nicht aggregierte Daten (Millisekunden)", + "bar-alignment": "Balkenausrichtung", + "bar-alignment-left": "Links", + "bar-alignment-right": "Rechts", + "bar-alignment-center": "Zentriert", + "default-font": "Standardschriftart", + "default-font-size": "Standardschriftgröße", + "default-font-color": "Standardschriftfarbe", + "thresholds-line-width": "Standardlinienbreite für alle Schwellenwerte", + "tooltip-settings": "Tooltip-Einstellungen", + "tooltip": "Tooltip", + "show-tooltip": "Tooltip anzeigen", + "hover-individual-points": "Einzelne Punkte beim Überfahren anzeigen", + "show-cumulative-values": "Kumulative Werte im Stacking-Modus anzeigen", + "hide-zero-false-values": "Null-/Falsch-Werte im Tooltip ausblenden", + "tooltip-value-format-function": "Tooltip-Wertformatierungsfunktion", + "grid-settings": "Gittereinstellungen", + "show-vertical-lines": "Vertikale Linien anzeigen", + "show-horizontal-lines": "Horizontale Linien anzeigen", + "grid-outline-border-width": "Gitter-Umriss-/Rahmenbreite (px)", + "primary-color": "Primärfarbe", + "background-color": "Hintergrundfarbe", + "ticks-color": "Tick-Farbe", + "xaxis-settings": "X-Achse Einstellungen", + "axis-title": "Achsentitel", + "xaxis-tick-labels-settings": "Einstellungen für X-Achsenbeschriftung", + "show-tick-labels": "Tick-Beschriftungen anzeigen", + "yaxis-settings": "Y-Achse Einstellungen", + "min-scale-value": "Minimalwert der Skala", + "max-scale-value": "Maximalwert der Skala", + "yaxis-tick-labels-settings": "Einstellungen für Y-Achsenbeschriftung", + "tick-step-size": "Tick-Schrittweite", + "number-of-decimals": "Anzahl an Dezimalstellen", + "ticks-formatter-function": "Tick-Formatierungsfunktion", + "comparison-settings": "Vergleichseinstellungen", + "enable-comparison": "Vergleich aktivieren", + "time-for-comparison": "Vergleichszeitraum", + "time-for-comparison-previous-interval": "Vorheriges Intervall (Standard)", + "time-for-comparison-days": "Vor einem Tag", + "time-for-comparison-weeks": "Vor einer Woche", + "time-for-comparison-months": "Vor einem Monat", + "time-for-comparison-years": "Vor einem Jahr", + "time-for-comparison-custom-interval": "Benutzerdefiniertes Intervall", + "custom-interval-value": "Benutzerdefinierter Intervallwert (ms)", + "comparison-x-axis-settings": "Vergleichs-X-Achsen-Einstellungen", + "axis-position": "Achsenposition", + "axis-position-top": "Oben (Standard)", + "axis-position-bottom": "Unten", + "custom-legend-settings": "Benutzerdefinierte Legenden-Einstellungen", + "enable-custom-legend": "Benutzerdefinierte Legende aktivieren (ermöglicht die Verwendung von Attribut-/Zeitreihenwerten in Schlüsselbeschriftungen)", + "key-name": "Schlüsselname", + "key-name-required": "Schlüsselname ist erforderlich", + "key-type": "Schlüsseltyp", + "key-type-attribute": "Attribut", + "key-type-timeseries": "Zeitreihe", + "label-keys-list": "Liste der Schlüssel zur Verwendung in Beschriftungen", + "no-label-keys": "Keine Schlüssel konfiguriert", + "add-label-key": "Neuen Schlüssel hinzufügen", + "line-width": "Linienbreite", + "color": "Farbe", + "data-is-hidden-by-default": "Daten standardmäßig ausgeblendet", + "disable-data-hiding": "Ausblenden von Daten deaktivieren", + "remove-from-legend": "Schlüssel aus Legende entfernen", + "exclude-from-stacking": "Vom Stacking ausschließen (verfügbar im \"Stacking\"-Modus)", + "line-settings": "Linieneinstellungen", + "show-line": "Linie anzeigen", + "fill-line": "Linie ausfüllen", + "fill-line-opacity": "Füllopazität", + "points-settings": "Punkteinstellungen", + "show-points": "Punkte anzeigen", + "points-line-width": "Linienbreite der Punkte", + "points-radius": "Punkteradius", + "point-shape": "Punktform", + "point-shape-circle": "Kreis", + "point-shape-cross": "Kreuz", + "point-shape-diamond": "Diamant", + "point-shape-square": "Quadrat", + "point-shape-triangle": "Dreieck", + "point-shape-custom": "Benutzerdefinierte Funktion", + "point-shape-draw-function": "Punktzeichnungsfunktion", + "show-separate-axis": "Separate Achse anzeigen", + "axis-position-left": "Links", + "axis-position-right": "Rechts", + "thresholds": "Schwellenwerte", + "no-thresholds": "Keine Schwellenwerte konfiguriert", + "add-threshold": "Schwellenwert hinzufügen", + "show-values-for-comparison": "Historische Vergleichswerte anzeigen", + "comparison-values-label": "Beschriftung historischer Werte", + "comparison-line-color": "Vergleichslinienfarbe", + "threshold-settings": "Schwellenwert-Einstellungen", + "use-as-threshold": "Schlüsselwert als Schwellenwert verwenden", + "threshold-line-width": "Linienbreite des Schwellenwerts", + "threshold-color": "Schwellenwertfarbe", + "common-pie-settings": "Allgemeine Einstellungen für Tortendiagramme", + "radius": "Radius", + "inner-radius": "Innenradius", + "tilt": "Neigung", + "common-pie-settings-range-error": "Wert muss im Bereich von 0 bis 1 liegen", + "stroke-settings": "Strich-Einstellungen", + "width-pixels": "Breite (Pixel)", + "show-labels": "Beschriftungen anzeigen", + "animation-settings": "Animationseinstellungen", + "animated-pie": "Tortenanimation aktivieren (experimentell)", + "border-settings": "Rahmen-Einstellungen", + "border-width": "Rahmenbreite", + "border-color": "Rahmenfarbe", + "legend-settings": "Legenden-Einstellungen", + "display-legend": "Legende anzeigen", + "labels-font-color": "Schriftfarbe der Beschriftungen", + "series": "Reihen", + "add-series": "Reihe hinzufügen", + "series-settings": "Reiheneinstellungen", + "remove-series": "Reihe entfernen", + "no-series": "Keine Reihen konfiguriert", + "no-series-error": "Mindestens eine Reihe muss angegeben werden", + "chart-appearance": "Diagramm-Darstellung", + "vertical-grid-lines": "Vertikale Gitterlinien", + "horizontal-grid-lines": "Horizontale Gitterlinien", + "chart-background": "Diagrammhintergrund", + "grid-lines-color": "Gitterlinienfarbe", + "border": "Rahmen", + "axis": "Achse", + "vertical-axis": "Vertikale Achse", + "ticks": "Ticks", + "horizontal-axis": "Horizontale Achse", + "shape-empty-circle": "Leerer Kreis", + "shape-circle": "Kreis", + "shape-rect": "Rechteck", + "shape-round-rect": "Abgerundetes Rechteck", + "shape-triangle": "Dreieck", + "shape-diamond": "Diamant", + "shape-pin": "Pin", + "shape-arrow": "Pfeil", + "shape-none": "Keine", + "line-type-solid": "Durchgezogen", + "line-type-dashed": "Gestrichelt", + "line-type-dotted": "Gepunktet", + "label-position-top": "Oben", + "label-position-bottom": "Unten", + "label-position-outside": "Außerhalb", + "label-position-inside": "Innerhalb", + "fill": "Füllung", + "fill-type-none": "Keine", + "fill-type-solid": "Voll", + "fill-type-opacity": "Deckkraft", + "fill-type-gradient": "Farbverlauf", + "background": "Hintergrund", + "opacity": "Deckkraft", + "gradient-stops": "Farbverlauf-Stops", + "gradient-start": "Start", + "gradient-end": "Ende", + "animation": { + "animation": "Animation", + "animation-threshold": "Animationsschwelle", + "animation-duration": "Animationsdauer", + "animation-easing": "Animationseffekt", + "animation-delay": "Animationsverzögerung", + "update-animation-duration": "Dauer der Aktualisierungsanimation", + "update-animation-easing": "Effekt der Aktualisierungsanimation", + "update-animation-delay": "Verzögerung der Aktualisierungsanimation" + }, + "chart-axis": { + "scale": "Skalierung", + "scale-min": "min", + "scale-max": "max", + "scale-auto": "Auto" + }, + "bar": { + "show-border": "Rahmen anzeigen", + "border-width": "Rahmenbreite", + "border-radius": "Rahmenradius", + "bar-width": "Balkenbreite", + "label": "Beschriftung", + "label-hint": "Beschriftung über dem Balken anzeigen.", + "series-label-hint": "Beschriftung mit Wert über dem Balken anzeigen.", + "label-background": "Beschriftungshintergrund" + } + }, + "color": { + "color-settings": "Farbeinstellungen", + "color-type-constant": "Konstant", + "color-type-gradient": "Farbverlauf", + "color-type-range": "Bereich", + "color-type-function": "Funktion", + "color": "Farbe", + "value-range": "Wertebereich", + "from": "Von", + "to": "Bis", + "color-function": "Farbfunktion", + "copy-color-settings-from": "Farbeinstellungen kopieren von", + "copy-from": "Kopieren von", + "settings-type": "Einstellungstyp", + "basic-mode": "Einfach", + "advanced-mode": "Erweitert", + "entity-alias": "Entitätsalias", + "entity-attribute": "Entitätsattribut", + "gradient-color": "Farbverlauf", + "gradient-color-min": "Farbe", + "gradient-start": "Verlaufsstartfarbe", + "gradient-start-min": "Start", + "gradient-end": "Verlaufsendfarbe", + "gradient-end-min": "Ende", + "start-value": "Startwert", + "end-value": "Endwert", + "gradient-type": "Verlaufsart" + }, + "dashboard-state": { + "dashboard-state-settings": "Dashboard-Zustandseinstellungen", + "dashboard-state": "Dashboard-Zustands-ID", + "autofill-state-layout": "Layout-Höhe des Zustands automatisch ausfüllen", + "default-margin": "Standard-Widget-Abstand", + "default-background-color": "Standardhintergrundfarbe", + "sync-parent-state-params": "Zustandsparameter mit übergeordnetem Dashboard synchronisieren" + }, "date-range-navigator": { - "date-range-picker-settings": "Einstellungen Datumsbereichsauswahl", - "hide-date-range-picker": "Datumsbereichsauswahl verstecken", - "picker-one-panel": "Datumsbereichsauswahl in einem Bereich", - "picker-auto-confirm": "Automatische Bestätigung der Datumsbereichsauswahl", - "picker-show-template": "Vorlage für die Anzeige der Datumsbereichsauswahl", - "first-day-of-week": "Erster Tag der Woche", + "date-range-picker-settings": "Einstellungen für Datumsbereich-Auswahl", + "hide-date-range-picker": "Datumsbereich-Auswahl ausblenden", + "picker-one-panel": "Datumsbereich-Auswahl in einem Panel", + "picker-auto-confirm": "Datumsbereich-Auswahl automatisch bestätigen", + "picker-show-template": "Datumsbereich-Auswahl Vorlage anzeigen", + "first-day-of-week": "Erster Wochentag", "interval-settings": "Intervalleinstellungen", - "hide-interval": "Intervall verstecken", - "initial-interval": "Anfangsintervall", + "hide-interval": "Intervall ausblenden", + "initial-interval": "Anfängliches Intervall", "interval-hour": "Stunde", "interval-day": "Tag", "interval-week": "Woche", @@ -2021,30 +7201,30 @@ "interval-three-months": "3 Monate", "interval-six-months": "6 Monate", "step-settings": "Schritteinstellungen", - "hide-step-size": "Schrittweite verstecken", - "initial-step-size": "Anfangsschrittweite", - "hide-labels": "Beschriftung verstecken", - "use-session-storage": "Sessionspeicher verwenden", + "hide-step-size": "Schrittgröße ausblenden", + "initial-step-size": "Anfängliche Schrittgröße", + "hide-labels": "Beschriftungen ausblenden", + "use-session-storage": "Sitzungsspeicher verwenden", "localizationMap": { - "Sun": "So.", - "Mon": "Mo.", - "Tue": "Di.", - "Wed": "Mi.", - "Thu": "Do.", - "Fri": "Fr.", - "Sat": "Sa.", - "Jan": "Jan.", - "Feb": "Feb.", - "Mar": "März", - "Apr": "Apr.", + "Sun": "So", + "Mon": "Mo", + "Tue": "Di", + "Wed": "Mi", + "Thu": "Do", + "Fri": "Fr", + "Sat": "Sa", + "Jan": "Jan", + "Feb": "Feb", + "Mar": "Mär", + "Apr": "Apr", "May": "Mai", - "Jun": "Juni", - "Jul": "Juli", - "Aug": "Aug.", - "Sep": "Sep.", - "Oct": "Okt.", - "Nov": "Nov.", - "Dec": "Dez.", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Aug", + "Sep": "Sep", + "Oct": "Okt", + "Nov": "Nov", + "Dec": "Dez", "January": "Januar", "February": "Februar", "March": "März", @@ -2062,12 +7242,12 @@ "Yesterday": "Gestern", "This Week": "Diese Woche", "Last Week": "Letzte Woche", - "This Month": "Diesen Monat", - "Last Month": "Im vergangenen Monat", + "This Month": "Dieser Monat", + "Last Month": "Letzter Monat", "Year": "Jahr", "This Year": "Dieses Jahr", - "Last Year": "Vergangenes Jahr", - "Date picker": "Datumsauswahl", + "Last Year": "Letztes Jahr", + "Date picker": "Datumswähler", "Hour": "Stunde", "Day": "Tag", "Week": "Woche", @@ -2077,65 +7257,1968 @@ "6 months": "6 Monate", "Custom interval": "Benutzerdefiniertes Intervall", "Interval": "Intervall", - "Step size": "Schrittlänge", + "Step size": "Schrittgröße", "Ok": "Ok" } }, + "doughnut": { + "doughnut-appearance": "Donut-Diagramm Darstellung", + "layout": "Layout", + "layout-default": "Standard", + "layout-with-total": "Mit Gesamtwert", + "central-total-value": "Zentraler Gesamtwert", + "doughnut-card-style": "Donut-Kartenstil" + }, + "entities-hierarchy": { + "hierarchy-data-settings": "Hierarchiedaten-Einstellungen", + "relations-query-function": "Beziehungsabfragefunktion der Knoten", + "has-children-function": "Funktion zur Erkennung von Kindknoten", + "node-state-settings": "Knotenstatus-Einstellungen", + "node-opened-function": "Standardfunktion für geöffnete Knoten", + "node-disabled-function": "Funktion für deaktivierte Knoten", + "display-settings": "Anzeigeeinstellungen", + "node-icon-function": "Knotensymbolfunktion", + "node-text-function": "Knotentextfunktion", + "sort-settings": "Sortiereinstellungen", + "nodes-sort-function": "Knotensortierfunktion" + }, + "edge": { + "display-default-title": "Standardtitel anzeigen" + }, + "gateway": { + "general-settings": "Allgemeine Einstellungen", + "widget-title": "Widget-Titel", + "default-archive-file-name": "Standardname für Archivdatei", + "device-type-for-new-gateway": "Gerätetyp für neues Gateway", + "messages-settings": "Nachrichteneinstellungen", + "save-config-success-message": "Nachricht bei erfolgreichem Speichern der Gateway-Konfiguration", + "device-name-exists-message": "Nachricht, wenn ein Gerät mit dem eingegebenen Namen bereits existiert", + "gateway-title": "Gateway-Formular", + "read-only": "Nur lesen", + "events-title": "Titel des Gateway-Ereignisformulars", + "events-filter": "Ereignisfilter", + "event-key-contains": "Ereignisschlüssel enthält...", + "show-connector": "Für den Konnektor anzeigen", + "connector-state-param-key": "Statusparameter-Schlüssel des Konnektors", + "message": "Nachricht", + "level": "Ebene", + "created-time": "Erstellungszeit" + }, + "gauge": { + "default-color": "Standardfarbe", + "radial-gauge-settings": "Einstellungen für Radialanzeige", + "ticks-settings": "Skaleneinstellungen", + "min-value": "Minimalwert", + "max-value": "Maximalwert", + "min-value-short": "min", + "max-value-short": "max", + "start-ticks-angle": "Startwinkel der Skalen", + "ticks-angle": "Skalenwinkel", + "major-ticks": "Große Skalen", + "major-ticks-count": "Anzahl der großen Skalen", + "major-ticks-color": "Farbe der großen Skalen", + "minor-ticks": "Kleine Skalen", + "minor-ticks-count": "Anzahl der kleinen Skalen", + "minor-ticks-color": "Farbe der kleinen Skalen", + "tick-numbers-font": "Schriftart der Skalenzahlen", + "unit-title-settings": "Einheitstitel-Einstellungen", + "show-unit-title": "Einheitstitel anzeigen", + "unit-title": "Einheitstitel", + "title-font": "Schriftart des Titels", + "units-settings": "Einheitseinstellungen", + "units-font": "Schriftart der Einheit", + "value-box-settings": "Wertanzeige-Einstellungen", + "show-value-box": "Wertanzeige anzeigen", + "value-box": "Wertanzeige", + "value-int": "Ziffernanzahl für den ganzzahligen Wertanteil", + "value-text": "Werttext", + "value-text-shadow": "Werttext-Schatten", + "value-font": "Werttext-Schriftart", + "rect-stroke-color-start": "Rahmenfarbe Rechteck - Anfangsverlauf", + "rect-stroke-color-end": "Rahmenfarbe Rechteck - Endverlauf", + "background-color": "Hintergrundfarbe", + "shadow-color": "Schattenfarbe", + "value-box-rect-stroke-color": "Rahmenfarbe Wertanzeige-Rechteck", + "value-box-rect-stroke-color-end": "Rahmenfarbe Wertanzeige-Rechteck - Endverlauf", + "value-box-background-color": "Hintergrundfarbe der Wertanzeige", + "value-box-shadow-color": "Schattenfarbe der Wertanzeige", + "plate-settings": "Platteneinstellungen", + "show-plate-border": "Plattenrand anzeigen", + "plate-color": "Plattenfarbe", + "needle-settings": "Zeigereinstellungen", + "needle-circle-size": "Zeigerkreisgröße", + "needle-color": "Zeigerfarbe", + "needle-color-start": "Zeigerfarbe - Anfangsverlauf", + "needle-color-end": "Zeigerfarbe - Endverlauf", + "needle-color-shadow-up": "Obere Schattenfarbe des Zeigers", + "needle-color-shadow-down": "Schlagschatten", + "highlights-settings": "Hervorhebungseinstellungen", + "highlights-width": "Hervorhebungsbreite", + "highlights": "Hervorhebungen", + "highlight-from": "Von", + "highlight-to": "Bis", + "highlight-color": "Farbe", + "no-highlights": "Keine Hervorhebungen konfiguriert", + "add-highlight": "Hervorhebung hinzufügen", + "animation-settings": "Animationseinstellungen", + "enable-animation": "Animation aktivieren", + "animation-duration-rule": "Animationsdauer und -regel", + "animation-duration": "Animationsdauer", + "animation-rule": "Animationsregel", + "animation-linear": "Linear", + "animation-quad": "Quadratisch", + "animation-quint": "Fünfpolig", + "animation-cycle": "Zyklus", + "animation-bounce": "Prallen", + "animation-elastic": "Elastisch", + "animation-dequad": "Ent-Quadratisch", + "animation-dequint": "Ent-Fünfpolig", + "animation-decycle": "Ent-Zyklus", + "animation-debounce": "Ent-Prallen", + "animation-delastic": "Ent-Elastisch", + "linear-gauge-settings": "Einstellungen für Linearanzeige", + "bar-stroke": "Balkenrand", + "bar-stroke-width": "Breite des Balkenrands", + "bar-stroke-color": "Farbe des Balkenrands", + "bar-background-color": "Balkenhintergrundfarbe - Anfangsverlauf", + "bar-background-color-end": "Balkenhintergrundfarbe - Endverlauf", + "progress-bar-color": "Farbe der Fortschrittsanzeige", + "progress-bar": "Fortschrittsanzeige", + "progress-bar-color-start": "Fortschrittsanzeige-Farbe - Anfangsverlauf", + "progress-bar-color-end": "Fortschrittsanzeige-Farbe - Endverlauf", + "major-ticks-names": "Bezeichnungen der großen Skalen", + "show-stroke-ticks": "Skalenstrich anzeigen", + "major-ticks-font": "Schriftart der großen Skalen", + "border-color": "Rahmenfarbe", + "border-width": "Rahmenbreite", + "needle-circle": "Zeigerkreis", + "needle-circle-color": "Zeigerkreisfarbe", + "animation-target": "Animationselement", + "animation-target-needle": "Zeiger", + "animation-target-plate": "Platte", + "common-settings": "Allgemeine Anzeigeeinstellungen", + "gauge-type": "Anzeigetyp", + "gauge-type-arc": "Bogen", + "gauge-type-donut": "Donut", + "gauge-type-horizontal-bar": "Horizontale Leiste", + "gauge-type-vertical-bar": "Vertikale Leiste", + "donut-start-angle": "Startwinkel (Grad)", + "bar-settings": "Leistenanzeige-Einstellungen", + "relative-bar-width": "Relative Leistenbreite", + "neon-glow-brightness": "Helligkeit des Neonlichteffekts (0-100)", + "neon-glow-brightness-hint": "0 - Effekt deaktivieren", + "stripes-thickness": "Streifendicke", + "stripes-thickness-hint": "0 - keine Streifen", + "rounded-line-cap": "Abgerundete Linienenden", + "bar-color-settings": "Leistenfarbeneinstellungen", + "use-precise-level-color-values": "Genaue Farbstufen verwenden", + "bar-colors": "Leistenfarben, von unten nach oben", + "color": "Farbe", + "no-bar-colors": "Keine Leistenfarben konfiguriert", + "add-bar-color": "Leistenfarbe hinzufügen", + "from": "Von", + "to": "Bis", + "fixed-level-colors": "Leistenfarben mit Grenzwerten", + "gauge-title-settings": "Titelanzeige-Einstellungen", + "show-gauge-title": "Titel der Anzeige anzeigen", + "gauge-title": "Anzeigetitel", + "gauge-title-font": "Schriftart des Anzeigetitels", + "unit-title-and-timestamp-settings": "Einheitentitel- und Zeitstempel-Einstellungen", + "show-timestamp": "Zeitstempel", + "timestamp-format": "Zeitstempelformat", + "label-font": "Schriftart der unter dem Wert angezeigten Beschriftung", + "value-settings": "Wertanzeige-Einstellungen", + "show-value": "Werttext anzeigen", + "min-max-settings": "Min./Max.-Beschriftungseinstellungen", + "show-min-max": "Min. und Max. Werte anzeigen", + "min-max-font": "Schriftart für Min./Max.-Beschriftungen", + "show-ticks": "Skalen anzeigen", + "tick-width": "Skalenbreite", + "tick-color": "Skalenfarbe", + "tick-values": "Skalenwerte", + "no-tick-values": "Keine Skalenwerte konfiguriert", + "add-tick-value": "Skalenwert hinzufügen", + "gauge-appearance": "Anzeigestil", + "units-title": "Einheitentitel", + "value": "Wert", + "ticks": "Skalen", + "arrow-and-scale-color": "Standardfarbe für Zeiger und Skala", + "scale-settings": "Skaleneinstellungen", + "scale": "Skala", + "scale-color": "Skalenfarben", + "compass-appearance": "Kompass-Darstellung", + "label": "Beschriftung", + "labels": "Beschriftungen", + "label-style": "Beschriftungsstil", + "simple-gauge-type": "Typ", + "gauge-bar-background": "Hintergrund der Anzeigeleiste", + "bar-color": "Leistenfarbe", + "min-and-max-value": "Minimal- und Maximalwert", + "min-and-max-label": "Min.- und Max.-Beschriftung", + "font": "Schriftart", + "tick-width-and-color": "Skalenbreite und -farbe", + "min-max-validation-text": "Maximalwert muss größer als Minimalwert sein" + }, + "gpio": { + "pin": "Pin", + "label": "Bezeichnung", + "row": "Zeile", + "column": "Spalte", + "color": "Farbe", + "panel-settings": "Panel-Einstellungen", + "background-color": "Hintergrundfarbe", + "gpio-switches": "GPIO-Schalter", + "no-gpio-switches": "Keine GPIO-Schalter konfiguriert", + "add-gpio-switch": "GPIO-Schalter hinzufügen", + "gpio-status-request": "GPIO-Statusabfrage", + "method-name": "Methodenname", + "method-body": "Methodeninhalt", + "gpio-status-change-request": "GPIO-Statusänderungsanfrage", + "parse-gpio-status-function": "GPIO-Status-Funktion analysieren", + "gpio-leds": "GPIO-LEDs", + "no-gpio-leds": "Keine GPIO-LEDs konfiguriert", + "add-gpio-led": "GPIO-LED hinzufügen" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, "input-widgets": { - "attribute-not-allowed": "Attributparameter können in diesem Widget nicht verwendet werden", - "date": "Datum", - "discard-changes": "Änderungen verwerfen", - "entity-attribute-required": "Entitätsattribut ist erforderlich", - "entity-timeseries-required": "Zeitreihen für Entitäten sind erforderlich", - "not-allowed-entity": "Die ausgewählte Entität kann keine gemeinsamen Attribute haben", - "no-attribute-selected": "Es ist kein Attribut ausgewählt", - "no-datakey-selected": "Es ist kein Datenschlüssel ausgewählt", - "no-entity-selected": "Keine Entität ausgewählt", - "no-image": "Kein Bild", - "no-support-web-camera": "Keine unterstützte Webcam", - "no-timeseries-selected": "Keine Zeitreihen ausgewählt", - "switch-attribute-value": "Entitätsattributwert wechseln", - "switch-camera": "Kamera wechseln", - "switch-timeseries-value": "Wert für Zeitreihen von Entitäten wechseln", - "take-photo": "Foto machen", - "time": "Zeit", - "timeseries-not-allowed": "Der Timeseries-Parameter kann in diesem Widget nicht verwendet werden", - "update-failed": "Aktualisierung fehlgeschlagen", - "update-successful": "Aktualisierung erfolgreich", - "update-attribute": "Attribut aktualisieren", - "update-timeseries": "Zeitreihen aktualisieren", - "value": "Wert" + "attribute-not-allowed": "Attributparameter kann in diesem Widget nicht verwendet werden", + "blocked-location": "Geolokalisierung ist in Ihrem Browser blockiert", + "claim-device": "Gerät beanspruchen", + "claim-failed": "Gerätebeanspruchung fehlgeschlagen!", + "claim-not-found": "Gerät nicht gefunden!", + "claim-successful": "Gerät wurde erfolgreich beansprucht!", + "date": "Datum", + "device-name": "Gerätename", + "device-name-required": "Gerätename ist erforderlich", + "discard-changes": "Änderungen verwerfen", + "entity-attribute-required": "Entitätsattribut ist erforderlich", + "entity-coordinate-required": "Beide Felder, Breitengrad und Längengrad, sind erforderlich", + "entity-timeseries-required": "Zeitreihe der Entität ist erforderlich", + "get-location": "Aktuelle Position abrufen", + "invalid-date": "Ungültiges Datum", + "latitude": "Breitengrad", + "longitude": "Längengrad", + "min-value-error": "Minimalwert ist {{value}}", + "max-value-error": "Maximalwert ist {{value}}", + "not-allowed-entity": "Ausgewählte Entität kann keine gemeinsamen Attribute haben", + "no-attribute-selected": "Kein Attribut ausgewählt", + "no-datakey-selected": "Kein Daten-Schlüssel ausgewählt", + "no-coordinate-specified": "Daten-Schlüssel für Breitengrad/Längengrad nicht angegeben", + "no-entity-selected": "Keine Entität ausgewählt", + "no-image": "Kein Bild", + "no-support-geolocation": "Ihr Browser unterstützt keine Geolokalisierung", + "no-support-web-camera": "Ihr Browser unterstützt keine Kameras", + "enable-https-use-widget": "Bitte HTTPS aktivieren, um dieses Widget zu verwenden", + "no-found-your-camera": "Kamera nicht gefunden", + "no-permission-camera": "Zugriff durch Benutzer verweigert / Diese Website hat keine Berechtigung für die Kamera", + "no-timeseries-selected": "Keine Zeitreihe ausgewählt", + "secret-key": "Geheimer Schlüssel", + "secret-key-required": "Geheimer Schlüssel ist erforderlich", + "switch-attribute-value": "Attributwert der Entität umschalten", + "switch-camera": "Kamera wechseln", + "switch-timeseries-value": "Zeitreihenwert der Entität umschalten", + "take-photo": "Foto aufnehmen", + "time": "Uhrzeit", + "timeseries-not-allowed": "Zeitreihenparameter kann in diesem Widget nicht verwendet werden", + "update-failed": "Aktualisierung fehlgeschlagen", + "update-successful": "Erfolgreich aktualisiert", + "update-attribute": "Attribut aktualisieren", + "update-timeseries": "Zeitreihe aktualisieren", + "value": "Wert", + "general-settings": "Allgemeine Einstellungen", + "widget-title": "Widget-Titel", + "claim-button-label": "Bezeichnung der Beanspruchungsschaltfläche", + "show-secret-key-field": "Feld 'Geheimer Schlüssel' anzeigen", + "labels-settings": "Beschriftungseinstellungen", + "show-labels": "Beschriftungen anzeigen", + "device-name-label": "Beschriftung für das Geräteeingabefeld", + "secret-key-label": "Beschriftung für das Eingabefeld 'Geheimer Schlüssel'", + "messages-settings": "Nachrichteneinstellungen", + "claim-device-success-message": "Textnachricht bei erfolgreicher Gerätebeanspruchung", + "claim-device-not-found-message": "Textnachricht, wenn das Gerät nicht gefunden wurde", + "claim-device-failed-message": "Textnachricht bei fehlgeschlagener Gerätebeanspruchung", + "claim-device-name-required-message": "Fehlermeldung 'Gerätename erforderlich'", + "claim-device-secret-key-required-message": "Fehlermeldung 'Geheimer Schlüssel erforderlich'", + "show-label": "Beschriftung anzeigen", + "label": "Beschriftung", + "required": "Erforderlich", + "required-error-message": "Fehlermeldung 'Erforderlich'", + "show-result-message": "Ergebnismeldung anzeigen", + "integer-field-settings": "Einstellungen für Ganzzahl-Feld", + "min-value": "Mindestwert", + "max-value": "Höchstwert", + "double-field-settings": "Einstellungen für Dezimalzahl-Feld", + "text-field-settings": "Einstellungen für Textfeld", + "min-length": "Minimale Länge", + "max-length": "Maximale Länge", + "checkbox-settings": "Einstellungen für Kontrollkästchen", + "true-label": "Bezeichnung für aktiviert", + "false-label": "Bezeichnung für deaktiviert", + "image-input-settings": "Einstellungen für Bildeingabe", + "display-preview": "Vorschau anzeigen", + "display-clear-button": "Schaltfläche 'Löschen' anzeigen", + "display-apply-button": "Schaltfläche 'Übernehmen' anzeigen", + "display-discard-button": "Schaltfläche 'Verwerfen' anzeigen", + "datetime-field-settings": "Einstellungen für Datum/Zeit-Feld", + "display-time-input": "Zeiteingabe anzeigen", + "latitude-key-name": "Name des Schlüssels für Breitengrad", + "longitude-key-name": "Name des Schlüssels für Längengrad", + "show-get-location-button": "Schaltfläche 'Aktuelle Position abrufen' anzeigen", + "use-high-accuracy": "Hohe Genauigkeit verwenden", + "location-fields-settings": "Einstellungen für Standortfelder", + "latitude-label": "Bezeichnung für Breitengrad", + "longitude-label": "Bezeichnung für Längengrad", + "input-fields-alignment": "Ausrichtung der Eingabefelder", + "input-fields-alignment-column": "Spalte (Standard)", + "input-fields-alignment-row": "Zeile", + "layout": "Layout", + "row-gap": "Abstand zwischen Zeilen (in Pixel)", + "column-gap": "Abstand zwischen Spalten (in Pixel)", + "latitude-field-required": "Breitengrad-Feld erforderlich", + "longitude-field-required": "Längengrad-Feld erforderlich", + "attribute-settings": "Attribut-Einstellungen", + "widget-mode": "Widget-Modus", + "widget-mode-update-attribute": "Attribut aktualisieren", + "widget-mode-update-timeseries": "Zeitreihe aktualisieren", + "attribute-scope": "Attributbereich", + "attribute-scope-server": "Serverattribut", + "attribute-scope-shared": "Geteiltes Attribut", + "value-required": "Wert erforderlich", + "image-settings": "Bildeinstellungen", + "image-format": "Bildformat", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "Bildqualität bei verlustbehafteter Komprimierung (z. B. JPEG, WEBP)", + "max-image-width": "Maximale Bildbreite", + "max-image-height": "Maximale Bildhöhe", + "action-buttons": "Aktionsschaltflächen", + "show-action-buttons": "Aktionsschaltflächen anzeigen", + "update-all-values": "Alle Werte aktualisieren, nicht nur geänderte", + "save-button-label": "Bezeichnung der Schaltfläche 'SPEICHERN'", + "reset-button-label": "Bezeichnung der Schaltfläche 'RÜCKGÄNGIG'", + "group-settings": "Gruppeneinstellungen", + "show-group-title": "Titel für Gruppe von Feldern anzeigen", + "group-title": "Gruppentitel", + "fields-alignment": "Feld-Ausrichtung", + "fields-alignment-row": "Zeile (Standard)", + "fields-alignment-column": "Spalte", + "fields-in-row": "Anzahl Felder pro Zeile", + "option-value": "Wert (für leere Option 'null' schreiben)", + "option-label": "Bezeichnung", + "hide-input-field": "Eingabefeld ausblenden", + "datakey-type": "Typ des Daten-Schlüssels", + "datakey-type-server": "Serverattribut (Standard)", + "datakey-type-shared": "Geteiltes Attribut", + "datakey-type-timeseries": "Zeitreihe", + "datakey-value-type": "Datenschlüssel-Werttyp", + "datakey-value-type-string": "Zeichenkette", + "datakey-value-type-double": "Dezimalzahl", + "datakey-value-type-integer": "Ganzzahl", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "Boolesch (Kontrollkästchen)", + "datakey-value-type-boolean-switch": "Boolesch (Schalter)", + "datakey-value-type-date-time": "Datum & Zeit", + "datakey-value-type-date": "Datum", + "datakey-value-type-time": "Zeit", + "datakey-value-type-select": "Auswahl", + "datakey-value-type-radio": "Radio", + "datakey-value-type-color": "Farbe", + "value-is-required": "Wert ist erforderlich", + "ability-to-edit-attribute": "Fähigkeit, Attribut zu bearbeiten", + "ability-to-edit-attribute-editable": "Bearbeitbar (Standard)", + "ability-to-edit-attribute-disabled": "Deaktiviert", + "ability-to-edit-attribute-readonly": "Nur-Lesen", + "disable-on-datakey-name": "Deaktivieren bei falschem Wert eines anderen Daten-Schlüssels (Schlüsselname angeben)", + "field-appearance": "Erscheinungsbild des Feldes", + "appearance-fill": "Ausfüllen", + "appearance-outline": "Umriss", + "subscript-sizing": "Größenanpassung für Tiefstellung", + "subscript-sizing-fixed": "Fest", + "subscript-sizing-dynamic": "Dynamisch", + "slide-toggle-settings": "Einstellungen für Schiebeschalter", + "slide-toggle-label-position": "Position des Schiebeschalter-Labels", + "slide-toggle-label-position-after": "Nachher", + "slide-toggle-label-position-before": "Vorher", + "select-options": "Auswahloptionen", + "no-select-options": "Keine Auswahloptionen konfiguriert", + "add-select-option": "Auswahloption hinzufügen", + "numeric-field-settings": "Einstellungen für numerisches Feld", + "step-interval": "Schrittintervall zwischen Werten", + "error-messages": "Fehlermeldungen", + "min-value-error-message": "Fehlermeldung 'Mindestwert'", + "max-value-error-message": "Fehlermeldung 'Höchstwert'", + "invalid-date-error-message": "Fehlermeldung 'Ungültiges Datum'", + "invalid-JSON-error-message": "Fehlermeldung 'Ungültiges JSON'", + "icon-settings": "Symboleinstellungen", + "dialog-editor-settings": "Einstellungen für Dialogeditor", + "use-custom-icon": "Benutzerdefiniertes Symbol verwenden", + "input-cell-icon": "Symbol vor Eingabezelle anzeigen", + "value-conversion-settings": "Wertkonvertierungseinstellungen", + "get-value-settings": "Einstellungen für Werteabruf", + "use-get-value-function": "getValue-Funktion verwenden", + "get-value-function": "getValue-Funktion", + "set-value-settings": "Einstellungen zum Setzen des Wertes", + "use-set-value-function": "setValue-Funktion verwenden", + "set-value-function": "setValue-Funktion", + "json-invalid": "JSON-Wert hat ein ungültiges Format", + "title": "Titel", + "cancel-button-label": "Bezeichnung der Schaltfläche 'Abbrechen'", + "radio-button-settings": "Einstellungen für Optionsfelder", + "color": "Farbe", + "columns": "Spalten", + "radio-options": "Optionsfeldoptionen", + "no-radio-options": "Keine Optionsfeldoptionen konfiguriert", + "add-radio-option": "Optionsfeldoption hinzufügen", + "radio-label-position": "Labelposition", + "radio-label-position-before": "Vorher", + "radio-label-position-after": "Nachher" + }, + "invalid-qr-code-text": "Ungültiger Eingabetext für QR-Code. Eingabe sollte vom Typ String sein", + "qr-code": { + "use-qr-code-text-function": "QR-Code-Textfunktion verwenden", + "qr-code-text-pattern": "QR-Code-Textmuster (z. B. '${entityName} | ${keyName} - ein Text.')", + "qr-code-text-pattern-hint": "QR-Code-Textmuster verwendet den Wert des zuerst gefundenen Schlüssels in den Entitäten im Entitätsalias.", + "qr-code-text-pattern-required": "QR-Code-Textmuster ist erforderlich.", + "qr-code-text-function": "QR-Code-Textfunktion" + }, + "label-widget": { + "label-pattern": "Muster", + "label-pattern-hint": "Hinweis: z. B. 'Text ${keyName} Einheiten.' oder ${#<key index>} Einheiten", + "label-pattern-required": "Muster ist erforderlich", + "label-position": "Position (Prozent relativ zum Hintergrund)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Hintergrundfarbe", + "font-settings": "Schriftarteinstellungen", + "background-image": "Hintergrundbild", + "labels": "Beschriftungen", + "no-labels": "Keine Beschriftungen konfiguriert", + "add-label": "Beschriftung hinzufügen" + }, + "navigation": { + "title": "Titel", + "navigation-path": "Navigationspfad", + "filter-type": "Filtertyp", + "filter-type-all": "Alle Elemente", + "filter-type-include": "Elemente einbeziehen", + "filter-type-exclude": "Elemente ausschließen", + "items": "Elemente", + "enter-urls-to-filter": "URLs zum Filtern eingeben..." + }, + "persistent-table": { + "rpc-id": "RPC-ID", + "message-type": "Nachrichtentyp", + "method": "Methode", + "params": "Parameter", + "created-time": "Erstellungszeit", + "expiration-time": "Ablaufzeit", + "retries": "Wiederholungen", + "status": "Status", + "filter": "Filter", + "refresh": "Aktualisieren", + "add": "RPC-Anfrage hinzufügen", + "details": "Details", + "delete": "Löschen", + "delete-request-title": "Persistente RPC-Anfrage löschen", + "delete-request-text": "Sind Sie sicher, dass Sie die Anfrage löschen möchten?", + "details-title": "Details RPC-ID: ", + "additional-info": "Zusätzliche Informationen", + "response": "Antwort", + "any-status": "Beliebiger Status", + "rpc-status-list": "RPC-Statusliste", + "no-request-prompt": "Keine Anfrage zum Anzeigen vorhanden", + "send-request": "Anfrage senden", + "add-title": "Persistente RPC-Anfrage erstellen", + "method-error": "Methode ist erforderlich.", + "timeout-error": "Mindest-Timeout-Wert ist 5000 (5 Sekunden).", + "white-space-error": "Leerzeichen sind nicht erlaubt.", + "rpc-status": { + "QUEUED": "WARTESCHLANGE", + "SENT": "GESENDET", + "DELIVERED": "ZUGESTELLT", + "SUCCESSFUL": "ERFOLGREICH", + "TIMEOUT": "ZEITÜBERSCHREITUNG", + "EXPIRED": "ABGELAUFEN", + "FAILED": "FEHLGESCHLAGEN" + }, + "rpc-search-status-all": "ALLE", + "message-types": { + "false": "Zweiwege", + "true": "Einweg" + }, + "general-settings": "Allgemeine Einstellungen", + "enable-filter": "Filter aktivieren", + "enable-sticky-header": "Kopfzeile beim Scrollen anzeigen", + "enable-sticky-action": "Aktionsspalte beim Scrollen anzeigen", + "display-request-details": "Anfragedetails anzeigen", + "allow-send-request": "Senden von RPC-Anfragen erlauben", + "allow-delete-request": "Löschen von Anfragen erlauben", + "columns-settings": "Spalteneinstellungen", + "display-columns": "Anzuzeigende Spalten", + "column": "Spalte", + "no-columns-found": "Keine Spalten gefunden", + "no-columns-matching": "'{{column}}' nicht gefunden." + }, + "range-chart": { + "chart": "Diagramm", + "data-zoom": "Datenzoom", + "range-chart-appearance": "Darstellung des Bereichsdiagramms", + "range-colors": "Bereichsfarben", + "out-of-range-color": "Farbe außerhalb des Bereichs", + "show-range-thresholds": "Bereichsschwellen anzeigen", + "range-thresholds-settings": "Bereichsschwellen-Einstellungen", + "fill-area": "Bereich füllen", + "fill-area-opacity": "Füllbereichsdeckkraft", + "range-chart-style": "Stil des Bereichsdiagramms" + }, + "rpc": { + "value-settings": "Werteinstellungen", + "initial-value": "Anfangswert", + "retrieve-value-settings": "Ein-/Ausschaltwert abrufen", + "retrieve-value-method": "Wert mit Methode abrufen", + "retrieve-value-method-none": "Nicht abrufen", + "retrieve-value-method-rpc": "RPC-Methode zum Abrufen verwenden", + "retrieve-value-method-attribute": "Für Attribut abonnieren", + "retrieve-value-method-timeseries": "Für Zeitreihe abonnieren", + "attribute-value-key": "Attributschlüssel", + "timeseries-value-key": "Zeitreihenschlüssel", + "get-value-method": "RPC-Methode zum Abrufen des Werts", + "parse-value-function": "Funktion zum Parsen des Werts", + "update-value-settings": "Wertaktualisierungseinstellungen", + "set-value-method": "RPC-Methode zum Setzen des Werts", + "convert-value-function": "Wertumwandlungsfunktion", + "rpc-settings": "RPC-Einstellungen", + "request-timeout": "RPC-Anforderungszeitlimit (ms)", + "persistent-rpc-settings": "Persistente RPC-Einstellungen", + "request-persistent": "Persistente RPC-Anfrage", + "persistent-polling-interval": "Abfrageintervall (ms) für persistente RPC-Antwort", + "common-settings": "Allgemeine Einstellungen", + "switch-title": "Schaltertitel", + "show-on-off-labels": "Ein-/Aus-Beschriftungen anzeigen", + "slide-toggle-label": "Schaltflächenbeschriftung", + "label-position": "Beschriftungsposition", + "label-position-before": "Vorher", + "label-position-after": "Nachher", + "slider-color": "Schiebereglerfarbe", + "slider-color-primary": "Primär", + "slider-color-accent": "Akzent", + "slider-color-warn": "Warnung", + "button-style": "Schaltflächenstil", + "button-raised": "Erhöhte Schaltfläche", + "button-primary": "Primärfarbe", + "button-background-color": "Hintergrundfarbe der Schaltfläche", + "button-text-color": "Textfarbe der Schaltfläche", + "widget-title": "Widget-Titel", + "button-label": "Schaltflächenbeschriftung", + "device-attribute-scope": "Attributbereich des Geräts", + "server-attribute": "Serverattribut", + "shared-attribute": "Geteiltes Attribut", + "device-attribute-parameters": "Geräteattributparameter", + "is-one-way-command": "Ist Einweg-Befehl", + "rpc-method": "RPC-Methode", + "rpc-method-params": "RPC-Methodenparameter", + "show-rpc-error": "RPC-Ausführungsfehler anzeigen", + "led-title": "LED-Titel", + "led-color": "LED-Farbe", + "check-status-settings": "Statusprüfungseinstellungen", + "perform-rpc-status-check": "RPC-Statusprüfung des Geräts durchführen", + "retrieve-led-status-value-method": "LED-Statuswert mit Methode abrufen", + "led-status-value-attribute": "Geräteattribut mit LED-Statuswert", + "led-status-value-timeseries": "Zeitreihe des Geräts mit LED-Statuswert", + "check-status-method": "RPC-Methode zur Geräteprüfung", + "parse-led-status-value-function": "Funktion zum Parsen des LED-Statuswerts", + "knob-title": "Drehregler-Titel", + "min-value": "Minimalwert", + "max-value": "Maximalwert" + }, + "maps": { + "map-type": { + "type": "Kartentyp", + "map": "Karte", + "image": "Bild" + }, + "image": { + "image-source": "Bildquelle", + "image-source-image": "Bild", + "image-source-entity-key": "Entitätsschlüssel", + "source-entity-alias": "Alias der Quell-Entität", + "image-url-key": "Bild-URL-Schlüssel", + "image-url-key-required": "Bild-URL-Schlüssel ist erforderlich" + }, + "control": { + "map-controls": "Kartensteuerungen", + "position": "Position", + "position-topleft": "Oben links", + "position-topright": "Oben rechts", + "position-bottomleft": "Unten links", + "position-bottomright": "Unten rechts", + "zoom-actions": "Zoom-Aktionen", + "zoom-scroll": "Scrollen", + "zoom-double-click": "Doppelklick", + "zoom-control-buttons": "Steuerungsknöpfe", + "scale": "Maßstab", + "scale-metric": "Metrisch", + "scale-imperial": "Imperial", + "switch-to-drag-mode-using-button": "Zum Drag-Modus per Schaltfläche wechseln" + }, + "timeline": { + "control-panel": "Zeitleisten-Bedienfeld", + "time-step": "Zeitschritt", + "speed-options": "Geschwindigkeitsoptionen", + "timestamp": "Zeitstempel", + "snap-to-real-location": "An reale Position ausrichten", + "location-snap-filter-function": "Filterfunktion zur Positionsausrichtung", + "no-trips-data-available": "Keine Reisedaten verfügbar" + }, + "map-action": { + "map-action-buttons": "Kartenaktionsschaltflächen", + "label": "Beschriftung", + "icon": "Symbol", + "color": "Farbe", + "action": "Aktion", + "add-button": "Schaltfläche hinzufügen", + "no-action-buttons-configured": "Keine Aktionsschaltflächen konfiguriert", + "remove-action-button": "Aktionsschaltfläche entfernen", + "map-action-button": "Kartenaktionsschaltfläche", + "button-requires": "Schaltfläche erfordert Beschriftung oder Symbol" + }, + "common": { + "common-map-settings": "Allgemeine Karteneinstellungen", + "fit-map-bounds": "Kartenbegrenzung an alle Marker anpassen", + "default-map-center-position": "Standardposition des Kartenmittelpunkts", + "default-map-zoom-level": "Standard-Zoomstufe der Karte", + "entities-limit": "Anzahl der zu ladenden Entitäten" + }, + "layer": { + "label": "Beschriftung", + "layer": "Ebene", + "layers": "Ebenen", + "map-layers": "Kartenebenen", + "add-layer": "Ebene hinzufügen", + "layer-settings": "Ebeneneinstellungen", + "remove-layer": "Ebene entfernen", + "no-layers": "Keine Ebenen konfiguriert", + "roadmap": "Straßenkarte", + "satellite": "Satellit", + "hybrid": "Hybrid", + "reference": { + "reference-layer": "Referenzebene", + "no-layer": "Keine Ebene", + "openstreetmap-hybrid": "OpenStreetMap Hybrid", + "world-edition-hybrid": "Weltedition Hybrid", + "enhanced-contrast-hybrid": "Hybrid mit erhöhtem Kontrast" + }, + "provider": { + "provider": "Anbieter", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Straßenkarte", + "satellite": "Satellit", + "hybrid": "Hybrid", + "terrain": "Gelände" + }, + "here": { + "title": "HERE", + "normal-day": "Normal (Tag)", + "normal-night": "Normal (Nacht)", + "hybrid-day": "Hybrid (Tag)", + "terrain-day": "Gelände (Tag)" + }, + "tencent": { + "title": "Tencent", + "normal": "Normal", + "satellite": "Satellit", + "terrain": "Gelände" + }, + "custom": { + "title": "Benutzerdefiniert", + "tile-url": "Kachel-URL" + } + }, + "credentials": { + "credentials": "Zugangsdaten", + "api-key": "API-Schlüssel" + } + }, + "overlays": { + "overlays": "Overlays", + "overlays-hint": "Konfigurieren Sie Datenquellen, Darstellung, Verhalten, Bearbeitungsoptionen und Gruppierung für Kartenentitäten", + "trips": "Reisen", + "markers": "Marker", + "polygons": "Polygone", + "circles": "Kreise" + }, + "data-layer": { + "source": "Quelle", + "filter": "Filter", + "additional-data-keys": "Zusätzliche Daten-Schlüssel", + "additional-datasources": "Zusätzliche Datenquellen", + "additional-datasources-hint": "Datenquelle für den Zugriff auf Attribute oder Telemetriedaten von Entitäten, die nicht auf der Karte angezeigt werden, nutzbar in Karten-Overlay-Funktionen.", + "more-datasources": "Weitere Datenquellen", + "data-keys": "Daten-Schlüssel", + "add-datasource": "Datenquelle hinzufügen", + "no-datasources": "Keine Datenquellen konfiguriert", + "remove-datasource": "Datenquelle entfernen", + "behavior": "Verhalten", + "on-click": "Beim Klicken", + "on-click-hint": "Aktion, die beim Klicken auf das Kartenelement ausgelöst wird.", + "groups": "Gruppen", + "groups-hint": "Liste der Gruppennamen, die dem Overlay zugewiesen sind und zur Steuerung der Sichtbarkeit auf der Karte verwendet werden.", + "color": "Farbe", + "color-settings": "Farbeinstellungen", + "color-type-constant": "Konstant", + "color-type-range": "Bereich", + "color-type-function": "Funktion", + "color-range-source-key": "Schlüssel für Farbwertbereich", + "color-range-source-key-required": "Schlüssel für Farbwertbereich ist erforderlich", + "color-range": "Farbwertbereich", + "color-function": "Farbfunktion", + "label": "Beschriftung", + "tooltip": "Tooltip", + "pattern-type-pattern": "Muster", + "pattern-type-function": "Funktion", + "label-pattern": "Beschriftung (Musterbeispiele: '${entityName}', '${entityName}: (Text ${keyName} Einheiten.)')", + "label-function": "Beschriftungsfunktion", + "tooltip-pattern": "Tooltip (z.B. 'Text ${keyName} Einheiten.' oder Linktext)", + "tooltip-function": "Tooltip-Funktion", + "tooltip-trigger": "Tooltip-Auslöser", + "tooltip-trigger-click": "Tooltip bei Klick anzeigen", + "tooltip-trigger-hover": "Tooltip bei Hover anzeigen", + "auto-close-tooltips": "Tooltips automatisch schließen", + "tooltip-offset": "Tooltip-Abstand", + "tooltip-offset-horizontal": "Horizontal", + "tooltip-offset-vertical": "Vertikal", + "tooltip-tag-actions": "Tag-Aktionen", + "add-tooltip-tag-action": "Tag-Aktion hinzufügen", + "edit-tooltip-tag-action": "Tag-Aktion bearbeiten", + "remove-tooltip-tag-action": "Tag-Aktion entfernen", + "action-add": "Hinzufügen", + "action-edit": "Bearbeiten", + "action-move": "Verschieben", + "action-remove": "Entfernen", + "edit-instruments": "Werkzeuge", + "persist-location-attribute-scope": "Attributbereich zur Speicherung der Position", + "enable-snapping": "Snapping für genaues Zeichnen aktivieren", + "enable-snapping-hint": "Richtet neue Punkte automatisch an bestehenden Formen aus, um das Zeichnen zu erleichtern und präziser zu gestalten.", + "drag-drop-mode": "Drag-and-Drop-Modus", + "trip": { + "no-trips": "Keine Fahrten konfiguriert", + "add-trip": "Fahrt hinzufügen", + "trip-configuration": "Fahrteinstellungen", + "remove-trip": "Fahrt entfernen" + }, + "marker": { + "marker": "Marker", + "latitude-key": "Latitude-Schlüssel", + "longitude-key": "Longitude-Schlüssel", + "x-pos-key": "X-Positions-Schlüssel", + "y-pos-key": "Y-Positions-Schlüssel", + "latitude-key-required": "Latitude-Schlüssel ist erforderlich", + "longitude-key-required": "Longitude-Schlüssel ist erforderlich", + "x-pos-key-required": "X-Positions-Schlüssel ist erforderlich", + "y-pos-key-required": "Y-Positions-Schlüssel ist erforderlich", + "no-markers": "Keine Marker konfiguriert", + "add-marker": "Marker hinzufügen", + "marker-configuration": "Marker-Konfiguration", + "remove-marker": "Marker entfernen", + "marker-type": "Markertyp", + "marker-type-shape": "Form", + "marker-type-icon": "Symbol", + "marker-type-image": "Bild", + "shape": "Form", + "icon": "Symbol", + "image": "Bild", + "marker-shapes": "Markerformen", + "marker-icon": "Markersymbol", + "marker-appearance": "Markeraussehen", + "marker-image": "Markerbild", + "marker-image-type-image": "Bild", + "marker-image-type-function": "Funktion", + "custom-marker-image-size": "Benutzerdefinierte Markerbildgröße", + "marker-image-function": "Markerbild-Funktion", + "marker-images": "Markerbilder", + "marker-offset": "Markerversatz", + "offset-horizontal": "Horizontal", + "offset-vertical": "Vertikal", + "rotate-marker": "Marker drehen", + "offset-angle": "Versatzwinkel", + "position-conversion": "Positionsumrechnung", + "position-conversion-function": "Positionsumrechnungsfunktion, sollte x,y-Koordinaten als Double von 0 bis 1 zurückgeben", + "clustering": { + "use-map-markers-clustering": "Marker-Clusterung auf der Karte verwenden", + "zoom-on-cluster-click": "Beim Klicken auf Cluster zoomen", + "max-zoom": "Maximaler Zoomlevel, bei dem ein Marker Teil eines Clusters sein kann (0 - 18)", + "max-radius": "Maximaler Radius, den ein Cluster abdeckt", + "zoom-animation": "Animation der Marker beim Zoomen", + "bounds-on-cluster-mouse-over": "Marker-Grenzen bei Mouseover auf Cluster anzeigen", + "spiderfy-max-zoom-level": "Spiderfy auf maximaler Zoomstufe (um alle Cluster-Marker anzuzeigen)", + "load-optimization": "Ladeoptimierung", + "chunked-load": "Chunk-Laden verwenden, um Seitenhänger zu vermeiden", + "lazy-load": "Lazy Load zum Hinzufügen von Markern verwenden", + "use-cluster-marker-color-function": "Farb-Funktion für Cluster-Marker verwenden", + "marker-color-function": "Marker-Farb-Funktion" + }, + "edit": "Marker bearbeiten", + "remove-marker-for": "Marker für '{{entityName}}' entfernen", + "place-marker": "Marker platzieren", + "place-marker-hint": "Klicken Sie, um den Marker zu platzieren", + "place-marker-hint-with-entity": "Klicken Sie, um die Entität '{{entityName}}' zu platzieren" + }, + "path": { + "path": "Pfad", + "path-decorator": "Pfad-Dekoration", + "decorator-symbol": "Dekorationssymbol", + "decorator-symbol-arrow-head": "Pfeil", + "decorator-symbol-dash": "Strich", + "decorator-arrangement": "Dekorationsanordnung", + "decorator-offset": "Start", + "decorator-end-offset": "Ende", + "decorator-repeat": "Wiederholen" + }, + "points": { + "points": "Punkte", + "point-tooltip": "Punkt-Tooltip" + }, + "shape": { + "fill": "Füllung", + "fill-type-color": "Farbe", + "fill-type-stripe": "Streifen", + "fill-type-image": "Bild", + "color": "Farbe", + "stripe": "Streifen", + "image": "Bild", + "stroke": "Rand", + "fill-image": "Füllbild", + "fill-image-type-image": "Bild", + "fill-image-type-function": "Funktion", + "preserve-aspect-ratio": "Seitenverhältnis beibehalten", + "opacity": "Transparenz", + "angle": "Rotationswinkel", + "scale": "Skalierung", + "fill-image-function": "Funktion zur Bildfüllung", + "fill-images": "Bilder zur Füllung", + "stripe-pattern": "Streifenmuster", + "first-stripe": "Erster Streifen", + "second-stripe": "Zweiter Streifen" + }, + "polygon": { + "polygon-key": "Polygon-Schlüssel", + "polygon-key-required": "Polygon-Schlüssel ist erforderlich", + "no-polygons": "Keine Polygone konfiguriert", + "add-polygon": "Polygon hinzufügen", + "polygon-configuration": "Polygon-Konfiguration", + "remove-polygon": "Polygon entfernen", + "edit": "Polygon bearbeiten", + "remove-polygon-for": "Polygon für '{{entityName}}' entfernen", + "cut": "Polygonbereich ausschneiden", + "rotate": "Polygon drehen", + "draw-rectangle": "Rechteck zeichnen", + "draw-polygon": "Polygon zeichnen", + "polygon-place-first-point-cut-hint": "Klicken, um ersten Punkt zu platzieren", + "continue-polygon-cut-hint": "Klicken, um weiter zu zeichnen", + "finish-polygon-cut-hint": "Ersten Marker anklicken zum Beenden und Speichern", + "polygon-place-first-point-hint": "Polygon: Klicken, um ersten Punkt zu platzieren", + "polygon-place-first-point-hint-with-entity": "Polygon für '{{entityName}}': Klicken, um ersten Punkt zu platzieren", + "continue-polygon-hint": "Polygon: Klicken, um weiter zu zeichnen", + "continue-polygon-hint-with-entity": "Polygon für '{{entityName}}': Klicken, um weiter zu zeichnen", + "finish-polygon-hint": "Polygon: Ersten Marker anklicken zum Beenden", + "finish-polygon-hint-with-entity": "Polygon für '{{entityName}}': Ersten Marker anklicken zum Beenden und Speichern", + "rectangle-place-first-point-hint": "Rechteck: Klicken, um ersten Punkt zu platzieren", + "rectangle-place-first-point-hint-with-entity": "Rechteck für '{{entityName}}': Klicken, um ersten Punkt zu platzieren", + "finish-rectangle-hint": "Rechteck: Klicken, um Zeichnung zu beenden", + "finish-rectangle-hint-with-entity": "Rechteck für '{{entityName}}': Klicken, um Zeichnung zu beenden und zu speichern" + }, + "circle": { + "circle-key": "Kreis-Schlüssel", + "circle-key-required": "Kreis-Schlüssel ist erforderlich", + "no-circles": "Keine Kreise konfiguriert", + "add-circle": "Kreis hinzufügen", + "circle-configuration": "Kreis-Konfiguration", + "remove-circle": "Kreis entfernen", + "edit": "Kreis bearbeiten", + "remove-circle-for": "Kreis für '{{entityName}}' entfernen", + "draw-circle": "Kreis zeichnen", + "place-circle-center-hint-with-entity": "Kreis für '{{entityName}}': Klicken, um Mittelpunkt zu setzen", + "place-circle-center-hint": "Kreis: Klicken, um Mittelpunkt zu setzen", + "finish-circle-hint-with-entity": "Kreis für '{{entityName}}': Klicken, um Kreis zu beenden und zu speichern", + "finish-circle-hint": "Kreis: Klicken, um Zeichnung zu beenden" + }, + "select-entity": "Entität auswählen", + "select-entity-hint": "Hinweis: Nach Auswahl auf die Karte klicken, um Position festzulegen" + }, + "select-entity": "Entität auswählen", + "select-entity-hint": "Hinweis: Nach der Auswahl auf die Karte klicken, um die Position festzulegen", + "tooltips": { + "placeMarker": "Klicken, um Entität '{{entityName}}' zu platzieren", + "firstVertex": "Polygon für '{{entityName}}': Klicken, um ersten Punkt zu setzen", + "firstVertex-cut": "Klicken, um ersten Punkt zu setzen", + "continueLine": "Polygon für '{{entityName}}': Klicken, um weiter zu zeichnen", + "continueLine-cut": "Klicken, um weiter zu zeichnen", + "finishLine": "Beliebigen existierenden Marker klicken, um zu beenden", + "finishPoly": "Polygon für '{{entityName}}': Ersten Marker klicken, um zu beenden und zu speichern", + "finishPoly-cut": "Ersten Marker klicken, um zu beenden und zu speichern", + "finishRect": "Polygon für '{{entityName}}': Klicken, um zu beenden und zu speichern", + "startCircle": "Kreis für '{{entityName}}': Klicken, um Mittelpunkt zu setzen", + "finishCircle": "Kreis für '{{entityName}}': Klicken, um Kreis zu beenden", + "placeCircleMarker": "Klicken, um Kreis-Marker zu setzen" + }, + "actions": { + "finish": "Fertigstellen", + "cancel": "Abbrechen", + "removeLastVertex": "Letzten Punkt entfernen" + }, + "buttonTitles": { + "drawMarkerButton": "Entität platzieren", + "drawPolyButton": "Polygon erstellen", + "drawLineButton": "Linie erstellen", + "drawCircleButton": "Kreis erstellen", + "drawRectButton": "Rechteck erstellen", + "editButton": "Bearbeitungsmodus", + "dragButton": "Drag-and-Drop-Modus", + "cutButton": "Polygonbereich ausschneiden", + "deleteButton": "Entfernen", + "drawCircleMarkerButton": "Kreis-Marker erstellen", + "rotateButton": "Polygon drehen" + }, + "map-provider-settings": "Kartendienst-Einstellungen", + "map-provider": "Kartendienst", + "map-provider-google": "Google Maps", + "map-provider-openstreet": "OpenStreet Maps", + "map-provider-here": "HERE Maps", + "map-provider-image": "Bildkarte", + "map-provider-tencent": "Tencent Maps", + "openstreet-provider": "OpenStreet Kartendienst", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Standard)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery": "Esri.WorldImagery", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Benutzerdefinierten Dienst verwenden", + "custom-provider-tile-url": "Kachel-URL des benutzerdefinierten Dienstes", + "google-maps-api-key": "Google Maps API-Schlüssel", + "default-map-type": "Standardkartentyp", + "google-map-type-roadmap": "Straßenkarte", + "google-map-type-satelite": "Satellit", + "google-map-type-hybrid": "Hybrid", + "google-map-type-terrain": "Gelände", + "map-layer": "Kartenebene", + "here-map-normal-day": "HERE.normalDay (Standard)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "Zugangsdaten", + "here-app-id": "HERE App-ID", + "here-app-code": "HERE App-Code", + "here-api-key": "HERE API-Schlüssel", + "here-use-new-version-api-3": "API-Version 3 verwenden", + "tencent-maps-api-key": "Tencent Maps API-Schlüssel", + "tencent-map-type-roadmap": "Straßenkarte", + "tencent-map-type-satelite": "Satellit", + "tencent-map-type-hybrid": "Hybrid", + "image-map-background": "Bildkarten-Hintergrund", + "image-map-background-from-entity-attribute": "Bildkarten-Hintergrund aus Entitätsattribut übernehmen", + "image-url-source-entity-alias": "Alias der Quell-Entität für Bild-URL", + "image-url-source-entity-attribute": "Entitätsattribut für Bild-URL", + "common-map-settings": "Allgemeine Karteneinstellungen", + "x-pos-key-name": "Schlüsselname für X-Position", + "y-pos-key-name": "Schlüsselname für Y-Position", + "latitude-key-name": "Schlüsselname für Breitengrad", + "longitude-key-name": "Schlüsselname für Längengrad", + "default-map-zoom-level": "Standard-Zoomstufe der Karte (0 - 20)", + "default-map-center-position": "Standard-Zentrum der Karte (0,0)", + "disable-scroll-zooming": "Zoom mit Scrollen deaktivieren", + "disable-double-click-zooming": "Zoom bei Doppelklick deaktivieren", + "disable-zoom-control-buttons": "Zoom-Schaltflächen deaktivieren", + "fit-map-bounds": "Kartenausschnitt anpassen, um alle Marker abzudecken", + "use-default-map-center-position": "Standard-Zentrum der Karte verwenden", + "entities-limit": "Grenze für zu ladende Entitäten", + "markers-settings": "Marker-Einstellungen", + "marker-offset-x": "X-Versatz des Markers multipliziert mit Breite", + "marker-offset-y": "Y-Versatz des Markers multipliziert mit Höhe", + "position-function": "Positionsumrechnungsfunktion, sollte x,y-Koordinaten als Double (0-1) zurückgeben", + "draggable-marker": "Marker verschiebbar", + "label": "Beschriftung", + "show-label": "Beschriftung anzeigen", + "use-label-function": "Beschriftungsfunktion verwenden", + "label-pattern": "Beschriftung (z.B.: '${entityName}', '${entityName}: (Text ${keyName} Einheiten.)')", + "label-function": "Beschriftungsfunktion", + "tooltip": "Tooltip", + "show-tooltip": "Tooltip anzeigen", + "show-tooltip-action": "Aktion zur Anzeige des Tooltips", + "show-tooltip-action-click": "Tooltip bei Klick anzeigen (Standard)", + "show-tooltip-action-hover": "Tooltip beim Hover anzeigen", + "auto-close-tooltips": "Tooltip automatisch schließen", + "use-tooltip-function": "Tooltip-Funktion verwenden", + "tooltip-pattern": "Tooltip (z.B.: 'Text ${keyName} Einheiten.' oder Linktext)", + "tooltip-function": "Tooltip-Funktion", + "tooltip-offset-x": "Tooltip-X-Versatz multipliziert mit Marker-Breite", + "tooltip-offset-y": "Tooltip-Y-Versatz multipliziert mit Marker-Höhe", + "color": "Farbe", + "use-color-function": "Farbfunktion verwenden", + "color-function": "Farbfunktion", + "marker-image": "Markerbild", + "use-marker-image-function": "Markerbildfunktion verwenden", + "custom-marker-image": "Benutzerdefiniertes Markerbild", + "custom-marker-image-size": "Benutzerdefinierte Markerbildgröße (px)", + "marker-image-function": "Markerbildfunktion", + "marker-images": "Markerbilder", + "polygon-settings": "Polygoneinstellungen", + "show-polygon": "Polygon anzeigen", + "polygon-key-name": "Polygon-Schlüsselname", + "enable-polygon-edit": "Bearbeitung des Polygons aktivieren", + "polygon-label": "Polygonbeschriftung", + "show-polygon-label": "Polygonbeschriftung anzeigen", + "use-polygon-label-function": "Polygonbeschriftungsfunktion verwenden", + "polygon-label-pattern": "Polygonbeschriftung (z.B.: '${entityName}', '${entityName}: (Text ${keyName} Einheiten.)')", + "polygon-label-function": "Polygonbeschriftungsfunktion", + "polygon-tooltip": "Polygontooltip", + "show-polygon-tooltip": "Polygontooltip anzeigen", + "auto-close-polygon-tooltips": "Polygontooltips automatisch schließen", + "use-polygon-tooltip-function": "Polygontooltip-Funktion verwenden", + "polygon-tooltip-pattern": "Tooltip (z.B.: 'Text ${keyName} Einheiten.' oder Linktext)", + "polygon-tooltip-function": "Polygontooltip-Funktion", + "polygon-color": "Polygonfarbe", + "polygon-opacity": "Polygondeckkraft", + "use-polygon-color-function": "Polygonfarbfunktion verwenden", + "polygon-color-function": "Polygonfarbfunktion", + "polygon-stroke": "Polygonkontur", + "stroke-color": "Konturfarbe", + "stroke-opacity": "Konturdeckkraft", + "stroke-weight": "Konturbreite", + "use-polygon-stroke-color-function": "Polygonkonturfarbfunktion verwenden", + "polygon-stroke-color-function": "Polygonkonturfarbfunktion", + "circle-settings": "Kreiseinstellungen", + "show-circle": "Kreis anzeigen", + "circle-key-name": "Kreisschlüsselname", + "enable-circle-edit": "Kreisbearbeitung aktivieren", + "circle-label": "Kreisbeschriftung", + "show-circle-label": "Kreisbeschriftung anzeigen", + "use-circle-label-function": "Kreisbeschriftungsfunktion verwenden", + "circle-label-pattern": "Kreisbeschriftung (z.B.: '${entityName}', '${entityName}: (Text ${keyName} Einheiten.)')", + "circle-label-function": "Kreisbeschriftungsfunktion", + "circle-tooltip": "Kreistooltip", + "show-circle-tooltip": "Kreistooltip anzeigen", + "auto-close-circle-tooltips": "Kreistooltips automatisch schließen", + "use-circle-tooltip-function": "Kreistooltip-Funktion verwenden", + "circle-tooltip-pattern": "Tooltip (z.B.: 'Text ${keyName} Einheiten.' oder Linktext)", + "circle-tooltip-function": "Kreistooltip-Funktion", + "circle-fill-color": "Kreisfüllfarbe", + "circle-fill-color-opacity": "Kreisfüllfarbdeckkraft", + "use-circle-fill-color-function": "Kreisfüllfarbfunktion verwenden", + "circle-fill-color-function": "Kreisfüllfarbfunktion", + "circle-stroke": "Kreiskontur", + "use-circle-stroke-color-function": "Kreiskonturfarbfunktion verwenden", + "circle-stroke-color-function": "Kreiskonturfarbfunktion", + "markers-clustering-settings": "Markerkonfigurations-Einstellungen", + "use-map-markers-clustering": "Marker-Clusterung verwenden", + "zoom-on-cluster-click": "Zoom bei Klick auf Cluster", + "max-cluster-zoom": "Maximale Zoomstufe für Clusterzugehörigkeit (0 - 18)", + "max-cluster-radius-pixels": "Maximaler Radius, den ein Cluster abdeckt (in Pixel)", + "cluster-zoom-animation": "Animation beim Zoomen auf Marker anzeigen", + "show-markers-bounds-on-cluster-mouse-over": "Grenzen von Markern bei Mauszeiger über Cluster anzeigen", + "spiderfy-max-zoom-level": "Spiderfy auf maximaler Zoomstufe (alle Marker im Cluster anzeigen)", + "load-optimization": "Ladeoptimierung", + "cluster-chunked-loading": "Chunks beim Laden von Markern verwenden, um Einfrieren der Seite zu vermeiden", + "cluster-markers-lazy-load": "Lazy Load für Marker aktivieren", + "editor-settings": "Editor-Einstellungen", + "enable-snapping": "Snapping an andere Punkte aktivieren", + "init-draggable-mode": "Karte im Drag-Modus initialisieren", + "hide-all-edit-buttons": "Alle Bearbeitungs-Schaltflächen ausblenden", + "hide-draw-buttons": "Zeichenschaltflächen ausblenden", + "hide-edit-buttons": "Bearbeitungsschaltflächen ausblenden", + "hide-remove-button": "Entfernen-Schaltfläche ausblenden", + "route-map-settings": "Routenkarte-Einstellungen", + "trip-animation-settings": "Fahrt-Animations-Einstellungen", + "normalization-step": "Normalisierungsschritt (ms)", + "tooltip-background-color": "Tooltip-Hintergrundfarbe", + "tooltip-font-color": "Tooltip-Schriftfarbe", + "tooltip-opacity": "Tooltip-Deckkraft (0-1)", + "auto-close-tooltip": "Tooltip automatisch schließen", + "rotation-angle": "Zusätzlicher Rotationswinkel für Marker (°)", + "path-settings": "Pfad-Einstellungen", + "path-color": "Pfadfarbe", + "use-path-color-function": "Pfadfarbfunktion verwenden", + "path-color-function": "Pfadfarbfunktion", + "path-decorator": "Pfad-Dekorator", + "use-path-decorator": "Pfad-Dekorator verwenden", + "decorator-symbol": "Dekorator-Symbol", + "decorator-symbol-arrow-head": "Pfeil", + "decorator-symbol-dash": "Strich", + "decorator-symbol-size": "Größe des Dekorator-Symbols (px)", + "use-path-decorator-custom-color": "Benutzerdefinierte Dekoratorfarbe verwenden", + "decorator-custom-color": "Benutzerdefinierte Dekoratorfarbe", + "decorator-offset": "Dekorator-Startversatz", + "end-decorator-offset": "Dekorator-Endversatz", + "decorator-repeat": "Dekorator-Wiederholung", + "points-settings": "Punkte-Einstellungen", + "show-points": "Punkte anzeigen", + "point-color": "Punktfarbe", + "point-size": "Punktgröße (px)", + "use-point-color-function": "Punktfarbfunktion verwenden", + "point-color-function": "Punktfarbfunktion", + "use-point-as-anchor": "Punkt als Anker verwenden", + "point-as-anchor-function": "Punkt-als-Anker-Funktion", + "independent-point-tooltip": "Unabhängiger Punkt-Tooltip", + "clustering-markers": "Marker-Clustering", + "use-icon-create-function": "Markierungsfarbfunktion verwenden", + "marker-color-function": "Markerfarbfunktion" + }, + "markdown": { + "use-markdown-text-function": "Markdown/HTML-Wertfunktion verwenden", + "markdown-text-function": "Markdown/HTML-Wertfunktion", + "markdown-text-pattern": "Markdown/HTML-Muster (Markdown oder HTML mit Variablen, z. B. '${entityName} oder ${keyName} - etwas Text.')", + "apply-default-markdown-style": "Standard-Markdown-Stil anwenden", + "markdown-css": "Markdown/HTML CSS" + }, + "simple-card": { + "label": "Bezeichnung", + "label-position": "Bezeichnungsposition", + "label-position-left": "Links", + "label-position-top": "Oben" + }, + "single-switch": { + "behavior": "Verhalten", + "layout": "Layout", + "layout-right": "Rechts", + "layout-left": "Links", + "layout-centered": "Zentriert", + "auto-scale": "Automatische Skalierung", + "label": "Bezeichnung", + "icon": "Symbol", + "switch-color": "Schalterfarbe", + "on": "Ein", + "off": "Aus", + "disabled": "Deaktiviert", + "tumbler-color": "Schalterfarbe", + "on-label": "Ein-Beschriftung", + "off-label": "Aus-Beschriftung", + "switch": "Schalter" + }, + "slider": { + "behavior": "Verhalten", + "initial-value": "Anfangswert", + "initial-value-hint": "Aktion zum Abrufen des Anfangswerts des Sliders.", + "on-value-change": "Bei Wertänderung", + "on-value-change-hint": "Aktion, die ausgelöst wird, wenn sich der Wert des Sliders ändert.", + "layout": "Layout", + "layout-default": "Standard", + "layout-extended": "Erweitert", + "layout-simplified": "Vereinfacht", + "auto-scale": "Automatische Skalierung", + "icon": "Symbol", + "value": "Wert", + "range": "Bereich", + "min": "Min", + "max": "Max", + "range-ticks": "Bereichsmarkierungen", + "tick-marks": "Markierungen", + "colors": "Farben", + "main": "Hauptfarbe", + "background": "Hintergrund", + "left-icon": "Linkes Symbol", + "right-icon": "Rechtes Symbol", + "slider": "Slider" + }, + "value-card": { + "layout": "Layout", + "layout-square": "Quadratisch", + "layout-vertical": "Vertikal", + "layout-centered": "Zentriert", + "layout-simplified": "Vereinfacht", + "layout-horizontal": "Horizontal", + "layout-horizontal-reversed": "Horizontal umgekehrt", + "label": "Bezeichnung", + "icon": "Symbol", + "value": "Wert", + "date": "Datum", + "value-card-style": "Wertkartenstil", + "auto-scale": "Automatische Skalierung" + }, + "label-card": { + "auto-scale": "Automatische Skalierung", + "label": "Bezeichnung", + "icon": "Symbol", + "label-card-style": "Bezeichnungskartenstil" + }, + "label-value-card": { + "value": "Wert", + "label-value-card-style": "Bezeichnungs- & Wertkartenstil" + }, + "liquid-level-card": { + "layout-simple": "Einfach", + "layout-percentage": "Prozentual", + "layout-absolute": "Absolut", + "layout": "Layout", + "background-overlay": "Hintergrundüberlagerung für Wert", + "total-volume": "Gesamtvolumen", + "total-volume-units": "Volumeneinheiten", + "tank": "Tank", + "shape": "Form", + "datasource-units": "Einheiten der Datenquelle", + "widget-units": "Einheiten des Widgets", + "decimals": "Dezimalstellen", + "liquid": "Flüssigkeit", + "liquid-color": "Farbe der Flüssigkeit", + "value": "Wert", + "value-font": "Schriftart für Wert", + "level": "Füllstand", + "last-update": "Letzte Aktualisierung", + "shape-by-attribute": "Tankform durch Attributnamen festlegen", + "tooltip-background": "Hintergrundfarbe", + "background-blur": "Hintergrundunschärfe", + "tank-color": "Tankfarbe", + "static": "Statisch", + "see-examples": "Beispiele ansehen", + "attribute": "Attribut", + "shape-type": "Typ", + "v-oval": "Vertikal Oval", + "v-cylinder": "Vertikal Zylinder", + "v-capsule": "Vertikal Kapsel", + "rectangle": "Rechteck", + "h-oval": "Horizontal Oval", + "h-ellipse": "Horizontal Ellipse", + "h-dish-ends": "Horizontal Teller-Enden", + "h-cylinder": "Horizontal Zylinder", + "h-capsule": "Horizontal Kapsel", + "h-elliptical_2_1": "Horizontal 2:1 Elliptisch", + "icon": "Karten-Symbol", + "title": "Karten-Titel", + "units": "Einheiten", + "color-and-font": "Farbe und Schriftart", + "shape-attribute-name": "Attributname", + "total-volume-required": "Gesamtvolumen ist erforderlich.", + "attribute-name-required": "Attributname ist erforderlich.", + "attribute-key-not-set": "Attributschlüssel '{{attributeName}}' nicht gesetzt", + "attribute-key-invalid": "Attributschlüssel '{{attributeName}}' ist ungültig" + }, + "aggregated-value-card": { + "subtitle": "Untertitel", + "chart": "Diagramm", + "values": "Werte", + "value-appearance": "Darstellung der Werte", + "position": "Position", + "position-center": "Zentriert", + "position-right-top": "Rechts oben", + "position-right-bottom": "Rechts unten", + "position-left-top": "Links oben", + "position-left-bottom": "Links unten", + "font": "Schriftart", + "color": "Farbe", + "arrow": "Pfeil", + "display-up-down-arrow": "Auf/Ab-Pfeil anzeigen", + "add-value": "Wert hinzufügen", + "remove-value": "Wert entfernen", + "no-values": "Keine Werte konfiguriert", + "aggregation": "Aggregation", + "aggregated-value-card-style": "Stil der aggregierten Wertkarte", + "auto-scale": "Automatische Skalierung" + }, + "value-chart-card": { + "layout": "Layout", + "layout-left": "Links", + "layout-right": "Rechts", + "auto-scale": "Automatische Skalierung", + "icon": "Symbol", + "value": "Wert", + "chart": "Diagramm", + "value-chart-card-style": "Stil der Wertdiagrammkarte" + }, + "progress-bar": { + "layout": "Layout", + "layout-default": "Standard", + "layout-simplified": "Vereinfacht", + "auto-scale": "Automatische Skalierung", + "icon": "Symbol", + "value": "Wert", + "range": "Bereich", + "min": "Min", + "max": "Max", + "range-ticks": "Bereichsmarkierungen", + "bar": "Balken", + "bar-color": "Balkenfarbe", + "bar-background": "Balkenhintergrund", + "progress-bar-card-style": "Stil der Fortschrittsbalkenkarte" + }, + "notification": { + "max-notification-display": "Maximale Anzahl anzuzeigender Benachrichtigungen", + "counter": "Zähler", + "counter-hint": "Der Zähler wird angezeigt, wenn der \"Widget-Titel\" aktiviert ist", + "icon": "Symbol", + "counter-value": "Wert", + "counter-color": "Farbe", + "notification-button": "Benachrichtigungsschaltflächen", + "button-view-all": "Alle anzeigen", + "button-filter": "Filter", + "type-filter": "Typfilter", + "button-mark-read": "Alle als gelesen markieren", + "notification-types": "Benachrichtigungstypen", + "notification-type": "Benachrichtigungstyp", + "search-type": "Typ suchen", + "any-type": "Beliebiger Typ" + }, + "alarm-count": { + "alarm-count-card-style": "Stil der Alarmzählkarte" + }, + "entity-count": { + "entity-count-card-style": "Stil der Entitätszählkarte" + }, + "count": { + "layout": "Layout", + "layout-column": "Spalte", + "layout-row": "Zeile", + "label": "Beschriftung", + "icon": "Symbol", + "icon-background": "Symbolhintergrund", + "value": "Wert", + "chevron": "Chevron", + "auto-scale": "Automatische Skalierung" + }, + "table": { + "common-table-settings": "Allgemeine Tabelleneinstellungen", + "enable-search": "Suche aktivieren", + "enable-sticky-header": "Kopfzeile immer anzeigen", + "enable-sticky-action": "Aktionsspalte immer anzeigen", + "hidden-cell-button-display-mode": "Anzeige-Modus für versteckte Zellaktionen", + "show-empty-space-hidden-action": "Leeren Platz anstelle der versteckten Zellaktion anzeigen", + "dont-reserve-space-hidden-action": "Keinen Platz für versteckte Aktionsschaltflächen reservieren", + "display-timestamp": "Zeitstempel anzeigen", + "display-pagination": "Seitennummerierung anzeigen", + "default-page-size": "Standard-Seitengröße", + "page-step-settings": "Seitenschritteinstellungen", + "page-step-count": "Anzahl der Schritte", + "page-step-increment": "Schrittgröße", + "page-step-count-format-message": "Muss ein ganzzahliger Wert im Bereich von 1 bis 100 sein.", + "page-step-increment-format-message": "Muss ein ganzzahliger Wert sein, größer oder gleich 1.", + "use-entity-label-tab-name": "Entitätsbezeichnung im Tabnamen verwenden", + "hide-empty-lines": "Leere Zeilen ausblenden", + "row-style": "Zeilenstil", + "use-row-style-function": "Zeilenstilfunktion verwenden", + "row-style-function": "Zeilenstilfunktion", + "cell-style": "Zellenstil", + "use-cell-style-function": "Zellenstilfunktion verwenden", + "cell-style-function": "Zellenstilfunktion", + "cell-content": "Zelleninhalt", + "use-cell-content-function": "Zelleninhaltsfunktion verwenden", + "cell-content-function": "Zelleninhaltsfunktion", + "show-latest-data-column": "Spalte mit neuesten Daten anzeigen", + "latest-data-column-order": "Reihenfolge der Spalte mit neuesten Daten", + "entities-table-title": "Titel der Entitätstabelle", + "enable-select-column-display": "Auswahlspaltenanzeige aktivieren", + "display-entity-name": "Spalte mit Entitätsnamen anzeigen", + "entity-name-column-title": "Titel der Entitätsnamenspalte", + "display-entity-label": "Spalte mit Entitätsbezeichnung anzeigen", + "entity-label-column-title": "Titel der Entitätsbezeichnungsspalte", + "display-entity-type": "Spalte mit Entitätstyp anzeigen", + "default-sort-order": "Standard-Sortierreihenfolge", + "custom-title": "Benutzerdefinierter Kopfzeilentitel", + "column-width": "Spaltenbreite (px oder %)", + "default-column-visibility": "Standardmäßige Spaltensichtbarkeit", + "column-visibility-visible": "Sichtbar", + "column-visibility-hidden": "Ausgeblendet", + "column-visibility-hidden-mobile": "Im mobilen Modus ausgeblendet", + "column-selection-to-display": "Spaltenauswahl in 'Anzuzeigende Spalten'", + "column-selection-to-display-enabled": "Aktiviert", + "column-selection-to-display-disabled": "Deaktiviert", + "alarms-table-title": "Titel der Alarmtabelle", + "enable-alarms-selection": "Alarmauswahl aktivieren", + "enable-alarms-search": "Alarmsuche aktivieren", + "enable-alarm-filter": "Alarmfilter aktivieren", + "display-alarm-details": "Alarmdetails anzeigen", + "allow-alarms-ack": "Alarmerkennung zulassen", + "allow-alarms-clear": "Alarmquittierung zulassen", + "display-alarm-activity": "Alarmaktivität anzeigen", + "allow-alarms-assign": "Alarmzuweisung zulassen", + "columns": "Spalten", + "column-settings": "Spalteneinstellungen", + "remove-column": "Spalte entfernen", + "add-column": "Spalte hinzufügen", + "no-columns": "Keine Spalten konfiguriert", + "columns-to-display": "Anzuzeigende Spalten", + "table-header": "Tabellenkopf", + "header-buttons": "Kopfzeilenschaltflächen", + "table-buttons": "Tabellenschaltflächen", + "pagination": "Seitennummerierung", + "rows": "Zeilen", + "timeseries-column-error": "Mindestens eine Zeitreihenspalte muss angegeben werden", + "alarm-column-error": "Mindestens eine Alarmspalte muss angegeben werden", + "table-tabs": "Tabellen-Tabs", + "show-cell-actions-menu-mobile": "Zellenaktions-Dropdownmenü im mobilen Modus anzeigen", + "disable-sorting": "Sortierung deaktivieren" + }, + "latest-chart": { + "total": "Gesamt", + "auto-scale": "Automatische Skalierung", + "clockwise-layout": "Uhrzeigerlayout", + "sort-series": "Serien nach Bezeichnung sortieren", + "tooltip-value-type-absolute": "Absolut", + "tooltip-value-type-percentage": "Prozentual" + }, + "pie-chart": { + "pie-chart-appearance": "Erscheinungsbild Kreisdiagramm", + "label": "Bezeichnung", + "border": "Rahmen", + "radius": "Radius", + "pie-chart-card-style": "Kreisdiagramm-Kartenstil" + }, + "radar-chart": { + "radar-appearance": "Radar-Diagramm", + "shape": "Form", + "shape-polygon": "Polygon", + "shape-circle": "Kreis", + "color": "Farbe", + "line": "Linie", + "points": "Punkte", + "points-label": "Punktebezeichnung", + "radar-axis": "Radar-Achse", + "axis-label": "Achsenbeschriftung", + "ticks-label": "Skalenbeschriftung", + "radar-chart-style": "Radar-Diagrammstil" + }, + "time-series-chart": { + "chart": "Diagramm", + "chart-style": "Diagrammstil", + "data-zoom": "Daten-Zoom", + "stack-mode": "Stapelmodus", + "stack-mode-hint": "Stapel Serien im Diagramm. Serien mit derselben Einheit werden übereinandergelegt.", + "axes": "Achsen", + "y-axes": "Y-Achsen", + "line-type": "Linientyp", + "line-width": "Linienstärke", + "type-line": "Linie", + "type-bar": "Balken", + "type-point": "Punkt", + "no-aggregation-bar-width-strategy": "Balkenbreitenstrategie für nicht aggregierte Daten", + "no-aggregation-bar-width-strategy-group": "Gruppieren", + "no-aggregation-bar-width-strategy-separate": "Separat", + "bar-group-width": "Balkengruppenbreite", + "bar-width": "Balkenbreite", + "bar-width-relative": "Prozentsatz des Zeitfensters", + "bar-width-absolute": "Absolut (ms)", + "comparison": { + "comparison": "Vergleich", + "comparison-hint": "Vergleich funktioniert nur mit historischen Daten!", + "show": "Anzeigen", + "settings": "Vergleichseinstellungen", + "show-values-for-comparison": "Historische Daten für Vergleich anzeigen", + "comparison-values-label": "Vergleichsschlüssel-Bezeichnung", + "comparison-values-label-auto": "Automatisch", + "comparison-data-color": "Farbe der Vergleichsdaten" + }, + "threshold": { + "thresholds": "Schwellenwerte", + "source": "Quelle", + "key-value": "Schlüssel / Wert", + "no-thresholds": "Keine Schwellenwerte konfiguriert", + "add-threshold": "Schwellenwert hinzufügen", + "type-constant": "Konstant", + "type-latest-key": "Schlüssel", + "type-entity": "Entität", + "threshold-settings": "Schwellenwerteinstellungen", + "remove-threshold": "Schwellenwert entfernen", + "threshold-value-required": "Schwellenwert ist erforderlich.", + "key-required": "Schlüssel ist erforderlich.", + "entity-key-required": "Entitätsschlüssel ist erforderlich.", + "line-appearance": "Linienerscheinung", + "line-color": "Linienfarbe", + "start-symbol": "Starts Symbol", + "end-symbol": "Ends Symbol", + "symbol-size": "Größe", + "label": "Bezeichnung", + "label-position-start": "Anfang", + "label-position-middle": "Mitte", + "label-position-end": "Ende", + "label-position-inside-start": "Innen Anfang", + "label-position-inside-start-top": "Innen Anfang oben", + "label-position-inside-start-bottom": "Innen Anfang unten", + "label-position-inside-middle": "Innen Mitte", + "label-position-inside-middle-top": "Innen Mitte oben", + "label-position-inside-middle-bottom": "Innen Mitte unten", + "label-position-inside-end": "Innen Ende", + "label-position-inside-end-top": "Innen Ende oben", + "label-position-inside-end-bottom": "Innen Ende unten", + "label-background": "Bezeichnungshintergrund" + }, + "state": { + "states": "Zustände", + "label": "Bezeichnung", + "ticks-value": "Tick-Wert", + "source": "Quelle", + "value-range": "Wert / Bereich", + "no-states": "Keine Zustände konfiguriert", + "add-state": "Zustand hinzufügen", + "type-constant": "Konstant", + "type-range": "Bereich", + "from": "Von", + "to": "Bis", + "remove-state": "Zustand entfernen" + }, + "grid": { + "grid": "Gitter", + "background-color": "Hintergrundfarbe", + "border": "Rahmen" + }, + "axis": { + "axes": "Achsen", + "x-axis": "X-Achse", + "y-axis": "Y-Achse", + "y-axis-settings": "Y-Achse Einstellungen", + "comparison-x-axis-settings": "Vergleich X-Achse Einstellungen", + "remove-y-axis": "Y-Achse entfernen", + "id": "ID", + "label": "Bezeichnung", + "position": "Position", + "position-left": "Links", + "position-right": "Rechts", + "position-top": "Oben", + "position-bottom": "Unten", + "tick-labels": "Tick-Beschriftungen", + "ticks-formatter-function": "Tick-Formatierungsfunktion", + "ticks-generator-function": "Tick-Generator-Funktion", + "show-ticks": "Ticks anzeigen", + "show-line": "Linie anzeigen", + "show-split-lines": "Hilfslinien anzeigen", + "show-split-lines-x-axis-hint": "Wenn aktiviert, werden vertikale Linien im Diagramm angezeigt.", + "show-split-lines-y-axis-hint": "Wenn aktiviert, werden horizontale Linien im Diagramm angezeigt.", + "ticks-interval": "Tick-Intervall", + "ticks-interval-hint": "Segmentierungsintervall der Achse festlegen.", + "split-number": "Teilungsanzahl", + "split-number-hint": "Anzahl der Segmente, in die die Achse unterteilt wird.", + "min": "Min", + "max": "Max", + "show": "Anzeigen", + "add-y-axis": "Y-Achse hinzufügen" + }, + "series": { + "legend-settings": "Legenden-Einstellungen", + "show-in-legend": "In Legende anzeigen", + "show-in-legend-hint": "Serienname und Daten in der Legende anzeigen.", + "hidden-by-default": "Standardmäßig ausgeblendet", + "hidden-by-default-hint": "Serie standardmäßig in der Legende ausblenden.", + "series-type": "Serientyp", + "type": "Typ", + "type-line": "Linie", + "type-bar": "Balken", + "line": { + "line": "Linie", + "show-line": "Linie anzeigen", + "step-line": "Treppenlinie", + "step-type-start": "Start", + "step-type-middle": "Mitte", + "step-type-end": "Ende", + "smooth-line": "Geglättete Linie" + }, + "point": { + "points": "Punkte", + "show-points": "Punkte anzeigen", + "point-label": "Punktbezeichnung", + "point-label-hint": "Bezeichnung mit Wert über dem Serienpunkt anzeigen.", + "point-label-background": "Hintergrund der Punktbezeichnung", + "point-shape": "Punktform", + "point-size": "Punktgröße" + } + } + }, + "wind-speed-direction": { + "layout": "Layout", + "layout-default": "Standard", + "layout-advanced": "Erweitert", + "layout-simplified": "Vereinfacht", + "values": "Werte", + "wind-direction": "Windrichtung", + "center-value": "Zentralwert", + "icon": "Symbol", + "arrow": "Pfeil", + "ticks": "Ticks", + "labels-type": "Beschriftungstyp", + "directional-names": "Richtung Namen", + "degrees": "Grad", + "major-ticks": "Große Ticks", + "minor-ticks": "Kleine Ticks", + "wind-speed-direction-card-style": "Stil der Windgeschwindigkeits- und Richtungsanzeige", + "ticks-color": "Tick-Farbe", + "ticks-labels-type": "Tick-Beschriftungstyp", + "arrow-color": "Pfeilfarbe" + }, + "value-source": { + "value-source": "Wertquelle", + "predefined-value": "Konstant", + "entity-attribute": "Entitätsattribut", + "value": "Wert", + "value-required": "Wert ist erforderlich.", + "key-required": "Schlüssel ist erforderlich.", + "entity-key-required": "Entitätsschlüssel ist erforderlich.", + "source-entity-alias": "Alias der Quell-Entität", + "source-entity-attribute": "Attribut der Quell-Entität", + "type-constant": "Konstant", + "type-latest-key": "Schlüssel", + "type-entity": "Entität" + }, + "rpc-state": { + "initial-state": "Anfangszustand", + "initial-state-hint": "Aktion, um den Anfangszustand (Ein/Aus) der Komponente abzurufen.", + "disabled-state": "Deaktivierter Zustand", + "disabled-state-hint": "Bedingung konfigurieren, unter der die Komponente deaktiviert ist.", + "turn-on": "Einschalten", + "turn-on-hint": "Aktion, wenn der Schalter auf 'Ein' gestellt wird", + "turn-off": "Ausschalten", + "turn-off-hint": "Aktion, wenn der Schalter auf 'Aus' gestellt wird", + "on": "Ein", + "off": "Aus", + "disabled": "Deaktiviert" + }, + "value-action": { + "do-nothing": "Nichts tun", + "execute-rpc": "RPC ausführen", + "get-attribute": "Attribut abrufen", + "set-attribute": "Attribut setzen", + "get-time-series": "Zeitreihe abrufen", + "get-alarm-status": "Alarmstatus abrufen", + "get-dashboard-state": "Dashboard-Zustands-ID abrufen", + "get-dashboard-state-object": "Dashboard-Zustandsobjekt abrufen", + "add-time-series": "Zeitreihe hinzufügen", + "execute-rpc-text": "Führe RPC-Methode '{{methodName}}' aus", + "get-time-series-text": "Verwende Zeitreihe '{{key}}'", + "get-attribute-text": "Verwende Attribut '{{key}}'", + "get-alarm-status-text": "Verwende Alarmstatus", + "get-dashboard-state-text": "Verwende Dashboard-Zustand", + "get-dashboard-state-object-text": "Verwende Dashboard-Zustandsobjekt", + "when-dashboard-state-is-text": "Wenn Dashboard-Zustand-ID '{{state}}' ist", + "when-dashboard-state-function-is-text": "Wenn f(Dashboard-Zustand-ID) '{{state}}' ist", + "when-dashboard-state-object-function-is-text": "Wenn f(Dashboard-Zustandsobjekt) '{{state}}' ist", + "set-attribute-to-value-text": "Setze Attribut '{{key}}' auf: {{value}}", + "add-time-series-value-text": "Füge Zeitreihenwert '{{key}}': {{value}} hinzu", + "set-attribute-text": "Setze Attribut '{{key}}'", + "add-time-series-text": "Füge Zeitreihe '{{key}}' hinzu", + "action": "Aktion", + "value": "Wert", + "init-value-hint": "Wert, der gesetzt wird, bis das Gerät Daten sendet.", + "method": "Methode", + "method-name-required": "Methodenname ist erforderlich.", + "request-timeout-ms": "RPC-Anfrage-Timeout (ms)", + "request-timeout-required": "Timeout ist erforderlich.", + "min-request-timeout-error": "Der Timeout-Wert muss mindestens 5000 ms (5 Sekunden) betragen.", + "request-persistent": "Persistente RPC-Anfrage", + "persistent-polling-interval": "Polling-Intervall (ms)", + "persistent-polling-interval-hint": "Polling-Intervall (ms) zur Abfrage der Antwort auf die persistente RPC-Anfrage", + "persistent-polling-interval-required": "Polling-Intervall ist erforderlich.", + "min-persistent-polling-interval-error": "Der Wert für das Polling-Intervall muss mindestens 1000 ms (1 Sekunde) betragen.", + "attribute-scope": "Attributbereich", + "attribute-key": "Attributschlüssel", + "attribute-key-required": "Attributschlüssel ist erforderlich.", + "time-series-key": "Zeitreihenschlüssel", + "time-series-key-required": "Zeitreihenschlüssel ist erforderlich.", + "action-result-converter": "Konverter für Aktionsergebnis", + "converter-none": "Keiner", + "converter-function": "Funktion", + "converter-constant": "Konstante", + "converter-value": "Wert", + "parse-value-function": "Funktion zur Wertanalyse", + "state-when-result-is": "'{{state}}' wenn Ergebnis ist", + "parameters": "Parameter", + "convert-value-function": "Funktion zur Wertumwandlung", + "error": { + "target-entity-is-not-set": "Zielentität ist nicht gesetzt!", + "failed-to-perform-action": "Aktion {{ actionLabel }} konnte nicht ausgeführt werden.", + "invalid-attribute-scope": "{{scope}}-Attributbereich wird von der Entität {{entityType}} nicht unterstützt." + } + }, + "widget-font": { + "font-settings": "Schriftarteinstellungen", + "font-family": "Schriftfamilie", + "size": "Größe", + "relative-font-size": "Relative Schriftgröße (Prozent)", + "font-style": "Stil", + "font-style-normal": "Normal", + "font-style-italic": "Kursiv", + "font-style-oblique": "Schräg", + "font-weight": "Stärke", + "font-weight-normal": "Normal", + "font-weight-bold": "Fett", + "font-weight-bolder": "Stärker", + "font-weight-lighter": "Leichter", + "color": "Farbe", + "shadow-color": "Schattenfarbe", + "preview": "Vorschau", + "line-height": "Zeilenhöhe", + "auto": "Auto" + }, + "home": { + "no-data-available": "Keine Daten verfügbar" + }, + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "Festplatte", + "cpu-warning-text": "Hohe CPU-Auslastung. Optimieren Sie die Systemleistung, um einen Systemausfall zu vermeiden.", + "cpu-critical-text": "Kritisch hohe CPU-Auslastung. Optimieren Sie die Systemleistung, um einen Systemausfall zu vermeiden.", + "ram-warning-text": "Wenig verfügbarer RAM. Optimieren Sie die Systemleistung oder erhöhen Sie die RAM-Kapazität, um Systemausfälle zu vermeiden.", + "ram-critical-text": "Kritisch wenig verfügbarer RAM. Optimieren Sie die Systemleistung oder erhöhen Sie die RAM-Kapazität, um Systemausfälle zu vermeiden.", + "disk-warning-text": "Wenig Festplattenspeicher. Bereinigen oder erweitern Sie den Speicherplatz, um Datenverlust zu vermeiden.", + "disk-critical-text": "Kritisch wenig Festplattenspeicher. Bereinigen oder erweitern Sie den Speicherplatz, um Datenverlust zu vermeiden." + }, + "cluster-info": { + "service-id": "Dienst-ID", + "service-type": "Diensttyp", + "no-data": "Keine Daten" + }, + "transport-messages": { + "title": "Transportnachrichten", + "info": "Alle Nachrichten, die von Geräten empfangen wurden" + }, + "activity": { + "title": "Aktivität" + }, + "documentation": { + "title": "Dokumentation", + "add-link": "Link hinzufügen", + "add-link-title": "Dokumentationslink hinzufügen", + "name": "Name", + "name-required": "Name ist erforderlich.", + "link": "Link", + "link-required": "Link ist erforderlich.", + "columns": "Spalten" + }, + "quick-links": { + "title": "Schnellzugriffe", + "add-link": "Link hinzufügen", + "add-link-title": "Schnelllink hinzufügen", + "quick-link": "Schnelllink", + "quick-link-required": "Schnelllink ist erforderlich.", + "no-links-matching": "Keine Links passend zu '{{name}}' gefunden.", + "columns": "Spalten" + }, + "recent-dashboards": { + "title": "Dashboards", + "last": "Zuletzt angesehen", + "starred": "Favorisiert", + "name": "Name", + "last-viewed": "Zuletzt angesehen", + "no-last-viewed-dashboards": "Noch keine zuletzt angesehenen Dashboards" + }, + "configured-features": { + "title": "Konfigurierte Funktionen", + "info": "Status der Funktionen, die eine Konfiguration erfordern", + "email-feature": "E-Mail", + "sms-feature": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "feature-configured": "Funktion ist konfiguriert.\nZum Einrichten klicken", + "feature-not-configured": "Funktion ist nicht konfiguriert.\nZum Einrichten klicken" + }, + "version-info": { + "title": "Version", + "contact-us": "Kontaktieren Sie uns", + "current-version": "Aktuelle Version", + "current": "Aktuell", + "available-version": "Verfügbare Version", + "available": "Verfügbar", + "upgrade": "Aktualisieren", + "version-is-up-to-date": "Version ist aktuell" + }, + "usage-info": { + "title": "Nutzung", + "entities": "Entitäten", + "api-calls": "API-Aufrufe" + }, + "functions": { + "title": "Funktionen", + "pe-feature-tooltip": "Nur in der ThingsBoard\nProfessional Edition verfügbar", + "switch-to-pe": "Zur Professional Edition wechseln", + "alarms": "Alarme", + "dashboards": "Dashboards", + "entities-and-relations": "Entitäten & Relationen", + "profiles": "Profile", + "advanced-features": "Erweiterte Funktionen", + "notification-center": "Benachrichtigungszentrale", + "api-usage": "API-Nutzung", + "customers": "Kunden", + "customers-hierarchy": "Kundenhierarchie", + "roles-and-permissions": "Rollen & Berechtigungen", + "groups": "Gruppen", + "integrations": "Integrationen", + "solution-templates": "Lösungsvorlagen", + "scheduler": "Zeitplaner", + "white-labeling": "White Labeling" + }, + "devices": { + "view-docs": "Dokumentation anzeigen", + "inactive": "Inaktiv", + "active": "Aktiv", + "total": "Gesamt" + }, + "alarms": { + "critical": "Kritisch", + "assigned-to-me": "Mir zugewiesen", + "total": "Gesamt" + }, + "getting-started": { + "get-started": "Loslegen", + "finish": "Fertigstellen", + "done-welcome-title": "Willkommen an Bord", + "done-welcome-text": "Das hast du großartig gemacht!", + "sys-admin": { + "step1": { + "title": "Erstellen Sie einen Mandanten und einen Mandantenadministrator", + "content": "

Ein Mandant ist eine Einzelperson oder Organisation, die Geräte und Assets besitzt oder produziert. Ein Mandant kann mehrere Mandantenadministrator-Benutzer, Kunden, Geräte und Assets haben.

Der Mandantenadministrator kann Geräte, Assets, Kunden und Dashboards innerhalb des Mandantenkontos erstellen und verwalten.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-create-tenant": "Wie man einen Mandanten und Mandantenadministrator erstellt" + }, + "step2": { + "title": "Funktion konfigurieren: Mailserver", + "content": "

Die Konfiguration des Mailservers ist unerlässlich für die Benutzeraktivierung, Passwortwiederherstellung und die Zustellung von Alarmbenachrichtigungen.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-configure-mail-server": "Wie man den Mailserver konfiguriert" + }, + "step3": { + "title": "Funktion konfigurieren: SMS-Anbieter", + "content": "

Konfigurieren Sie SMS-Anbieter, um Kunden über Alarme per SMS zu benachrichtigen.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-configure-sms-provider": "Wie man den SMS-Anbieter konfiguriert" + }, + "step4": { + "title": "Funktion konfigurieren: White-Labeling", + "content": "

Passen Sie ganz einfach das Logo und Farbschema Ihres Unternehmens oder Produkts an – ohne Programmierung und ohne Neustart des Dienstes.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

" + }, + "step5": { + "title": "Funktion konfigurieren: 2FA", + "content": "

Verbessern Sie die Sicherheit der Plattformkonten mit Zwei-Faktor-Authentifizierung.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

" + }, + "step6": { + "title": "Funktion konfigurieren: OAuth 2", + "content": "

Vereinfachen Sie die Anmeldung für Mandanten- und Kundenbenutzer mit Single Sign-On über OAuth 2.0.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

" + } + }, + "tenant-admin": { + "step1": { + "title": "Gerät erstellen", + "content": "

Stellen wir Ihr erstes Gerät über die Benutzeroberfläche auf der Plattform bereit. Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-create-device": "Wie man ein Gerät erstellt" + }, + "step2": { + "title": "Gerät verbinden", + "content-before": "

Um das Gerät zu verbinden, benötigen Sie die Gerätezugangsdaten. Für diese Anleitung empfehlen wir die Verwendung der standardmäßig generierten Zugangsdaten – eines Zugriffstokens.

  • Gehen Sie zur Gerätetabelle
  • Klicken Sie auf die Gerätezeile, um die Gerätedetails zu öffnen
  • Klicken Sie auf die Schaltfläche „Zugriffstoken kopieren“

Verwenden Sie einfache Befehle, um Daten über HTTP zu senden. Vergessen Sie nicht, $ACCESS_TOKEN mit dem Zugriffstoken Ihres Geräts zu ersetzen:

", + "ubuntu": { + "install-curl": "cURL für Ubuntu installieren:" + }, + "macos": { + "install-curl": "cURL für MacOS installieren:" + }, + "windows": { + "install-curl": "Ab Windows 10 b17063 ist cURL standardmäßig verfügbar." + }, + "replace-access-token": "Ersetzen Sie $ACCESS_TOKEN durch das Token Ihres Geräts:", + "content-after": "

Sie können auch andere Protokolle wie MQTT, CoAP usw. verwenden.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-connect-device": "Wie man ein Gerät verbindet" + }, + "step3": { + "title": "Dashboard erstellen", + "content": "

Erstellen Sie ein Dashboard zur Visualisierung von Daten von Entitäten wie Assets, Geräten usw.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-create-dashboard": "Wie man ein Dashboard erstellt" + }, + "step4": { + "title": "Alarmregeln konfigurieren", + "alarm-rules": "Alarmregeln", + "content": "

Lösen wir einen Alarm aus, wenn die Temperatur 25°C erreicht. Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-configure-alarm-rules": "Wie man Alarmregeln konfiguriert" + }, + "step5": { + "title": "Alarm erstellen", + "content-before": "

Um den Alarm auszulösen, senden Sie einen neuen Telemetriedatenwert von 26°C oder höher.

", + "replace-access-token": "Ersetzen Sie $ACCESS_TOKEN durch das Token Ihres Geräts:", + "content-after": "

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

", + "how-to-create-alarm": "Wie man einen Alarm erstellt" + }, + "step6": { + "title": "Kunde erstellen und Dashboard teilen", + "content": "

Durch die Erstellung von Dashboards für Endbenutzer kann ein Kundenbenutzer nur seine eigenen Geräte sehen, während Daten anderer Kunden verborgen bleiben.

Folgen Sie der Dokumentation, um zu erfahren, wie es geht:

" + } + } } }, "icon": { "icon": "Symbol", "icons": "Symbole", "select-icon": "Symbol auswählen", - "material-icons": "Material-Symbole", - "show-all": "Alle Symbole anzeigen" + "material-icons": "Materialsymbole", + "show-all": "Alle Symbole anzeigen", + "search-icon": "Symbol suchen", + "no-icons-found": "Keine Symbole für '{{iconSearch}}' gefunden" + }, + "phone-input": { + "phone-input-label": "Telefonnummer", + "phone-input-required": "Telefonnummer ist erforderlich", + "phone-input-validation": "Telefonnummer ist ungültig oder nicht möglich", + "phone-input-pattern": "Ungültige Telefonnummer. Muss im E.164-Format sein, z. B. {{phoneNumber}}", + "phone-input-hint": "Telefonnummer im E.164-Format, z. B. {{phoneNumber}}" }, "custom": { "widget-action": { - "action-cell-button": "Aktionszellenschaltfläche", - "row-click": "Klick auf Zeile", - "polygon-click": "Klick auf Polygon", - "marker-click": "Klick auf Marker", + "action-cell-button": "Zellenaktionstaste", + "row-click": "Bei Zeilenklick", + "cell-click": "Bei Zellenklick", + "polygon-click": "Bei Polygonklick", + "marker-click": "Bei Markerklick", + "circle-click": "Bei Kreis-Klick", "tooltip-tag-action": "Tooltip-Tag-Aktion", - "node-selected": "Klick auf Node", - "element-click": "Klick auf HTML element", - "pie-slice-click": "Klicken auf Slice", - "row-double-click": "Doppelklicken auf Zeile" + "node-selected": "Bei Knotenauswahl", + "element-click": "Bei HTML-Element-Klick", + "pie-slice-click": "Bei Tortenstück-Klick", + "row-double-click": "Bei Zeilendoppelklick", + "cell-double-click": "Bei Zellendoppelklick", + "card-click": "Bei Kartenklick", + "click": "Beim Klick" } }, - "paginator" : { - "items-per-page": "Einträge pro Seite:", - "first-page-label": "Erste Seite", - "last-page-label": "Letzte Seite", - "next-page-label": "Nächste Seite", - "previous-page-label": "Vorherige Seite", - "items-per-page-separator": "von" - }, + "paginator": { + "items-per-page": "Elemente pro Seite:", + "first-page-label": "Erste Seite", + "last-page-label": "Letzte Seite", + "next-page-label": "Nächste Seite", + "previous-page-label": "Vorherige Seite", + "items-per-page-separator": "von" + }, "language": { - "language": "Sprache" + "language": "Sprache", + "locales": { + "ar_AE": "العربية (الإمارات العربية المتحدة)", + "ca_ES": "katalanisch (Spanien)", + "cs_CZ": "tschechisch (Tschechien)", + "da_DK": "dänisch (Dänemark)", + "de_DE": "Deutsch (Deutschland)", + "el_GR": "griechisch (Griechenland)", + "en_US": "Englisch (USA)", + "es_ES": "spanisch (Spanien)", + "fa_IR": "persisch (Iran)", + "fr_FR": "französisch (Frankreich)", + "it_IT": "italienisch (Italien)", + "ja_JP": "japanisch (Japan)", + "ka_GE": "georgisch (Georgien)", + "ko_KR": "koreanisch (Südkorea)", + "lt_LT": "litauisch (Litauen)", + "lv_LV": "lettisch (Lettland)", + "nl_BE": "niederländisch (Belgien)", + "pl_PL": "polnisch (Polen)", + "pt_BR": "portugiesisch (Brasilien)", + "ro_RO": "rumänisch (Rumänien)", + "sl_SI": "slowenisch (Slowenien)", + "tr_TR": "türkisch (Türkei)", + "uk_UA": "ukrainisch (Ukraine)", + "zh_CN": "chinesisch (China)", + "zh_TW": "chinesisch (Taiwan)" + } } -} +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index e1329e657d..bb6f70c2aa 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -1,6176 +1,9224 @@ { - "access": { - "unauthorized": "No autorizado", - "unauthorized-access": "Acceso no autorizado", - "unauthorized-access-text": "Debes iniciar sesión para tener acceso a este recurso!", - "access-forbidden": "Acceso Prohibido", - "access-forbidden-text": "No tienes suficientes derechos para acceder a esta ubicación!
Intenta iniciar sesión con otro usuario si todavía quieres acceder a esta ubicación.", - "refresh-token-expired": "La sesión ha expirado", - "refresh-token-failed": "No se puede actualizar la sesión", - "permission-denied": "Permiso Denegado", - "permission-denied-text": "No tienes suficientes derechos para realizar esta operación!" + "access": { + "unauthorized": "No autorizado", + "unauthorized-access": "Acceso no autorizado", + "unauthorized-access-text": "¡Debes iniciar sesión para tener acceso a este recurso!", + "access-forbidden": "Acceso prohibido", + "access-forbidden-text": "¡No tienes derechos de acceso a esta ubicación!
Intenta iniciar sesión con un usuario diferente si aún deseas acceder a esta ubicación.", + "refresh-token-expired": "La sesión ha expirado", + "refresh-token-failed": "No se puede actualizar la sesión", + "permission-denied": "Permiso denegado", + "permission-denied-text": "¡No tienes permiso para realizar esta operación!" + }, + "account": { + "account": "Cuenta", + "notification-settings": "Configuración de notificaciones" + }, + "action": { + "activate": "Activar", + "suspend": "Suspender", + "save": "Guardar", + "saveAs": "Guardar como", + "move": "Mover", + "cancel": "Cancelar", + "ok": "OK", + "delete": "Eliminar", + "add": "Añadir", + "yes": "Sí", + "no": "No", + "update": "Actualizar", + "remove": "Eliminar", + "search": "Buscar", + "clear-search": "Borrar búsqueda", + "assign": "Asignar", + "unassign": "Desasignar", + "share": "Compartir", + "make-private": "Hacer privado", + "apply": "Aplicar", + "apply-changes": "Aplicar cambios", + "edit-mode": "Modo de edición", + "enter-edit-mode": "Entrar en modo de edición", + "decline-changes": "Rechazar cambios", + "decline": "Rechazar", + "close": "Cerrar", + "back": "Atrás", + "run": "Ejecutar", + "sign-in": "¡Iniciar sesión!", + "edit": "Editar", + "view": "Ver", + "create": "Crear", + "drag": "Arrastrar", + "refresh": "Actualizar", + "undo": "Deshacer", + "copy": "Copiar", + "paste": "Pegar", + "copy-reference": "Copiar referencia", + "paste-reference": "Pegar referencia", + "import": "Importar", + "export": "Exportar", + "share-via": "Compartir vía {{provider}}", + "select": "Seleccionar", + "continue": "Continuar", + "discard-changes": "Descartar cambios", + "download": "Descargar", + "next": "Siguiente", + "next-with-label": "Siguiente: {{label}}", + "read-more": "Leer más", + "hide": "Ocultar", + "test": "Probar", + "done": "Hecho", + "print": "Imprimir", + "restore": "Restaurar", + "confirm": "Confirmar", + "more": "Más", + "less": "Menos", + "skip": "Omitir", + "send": "Enviar", + "reset": "Restablecer", + "show-more": "Mostrar más", + "dont-show-again": "No mostrar de nuevo", + "see-documentation": "Ver documentación", + "clear": "Limpiar", + "upload": "Subir", + "delete-anyway": "Eliminar de todos modos", + "delete-selected": "Eliminar seleccionados", + "set": "Establecer" + }, + "aggregation": { + "aggregation": "Agregación", + "function": "Función de agregación de datos", + "limit": "Valores máximos", + "group-interval": "Intervalo de agrupación", + "min": "Mín", + "max": "Máx", + "avg": "Promedio", + "sum": "Suma", + "count": "Conteo", + "none": "Ninguno" + }, + "admin": { + "settings": "Configuraciones", + "general": "General", + "general-settings": "Configuración general", + "home-settings": "Configuración de inicio", + "home": "Inicio", + "outgoing-mail": "Servidor de correo", + "outgoing-mail-settings": "Configuración del servidor de correo saliente", + "system-settings": "Configuración del sistema", + "test-mail-sent": "¡El correo de prueba se envió correctamente!", + "base-url": "URL base", + "base-url-required": "Se requiere la URL base.", + "prohibit-different-url": "Prohibir el uso del nombre de host desde las cabeceras de solicitud del cliente", + "prohibit-different-url-hint": "Esta configuración debe habilitarse en entornos de producción. Puede causar problemas de seguridad si está deshabilitada", + "device-connectivity": { + "device-connectivity": "Conectividad del dispositivo", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPs", + "mqtt": "MQTT", + "mqtts": "MQTTs", + "coap": "COAP", + "coaps": "COAPs", + "hint": "Si los campos de host o puerto están vacíos, se utilizará el valor predeterminado del protocolo.", + "host": "Host", + "port": "Puerto", + "port-pattern": "El puerto debe ser un número entero positivo.", + "port-range": "El puerto debe estar en el rango de 1 a 65535." }, - "account": { - "account": "Cuenta", - "notification-settings": "Ajustes de notificaciones" + "mail-from": "Correo del remitente", + "mail-from-required": "Se requiere el correo del remitente.", + "smtp-protocol": "Protocolo SMTP", + "smtp-host": "Host SMTP", + "smtp-host-required": "Se requiere el host SMTP.", + "smtp-port": "Puerto SMTP", + "smtp-port-required": "Debes proporcionar un puerto SMTP.", + "smtp-port-invalid": "Ese puerto SMTP no parece válido.", + "timeout-msec": "Tiempo de espera (mseg)", + "timeout-required": "Se requiere un tiempo de espera.", + "timeout-invalid": "Ese tiempo de espera no parece válido.", + "enable-tls": "Habilitar TLS", + "tls-version": "Versión de TLS", + "enable-proxy": "Habilitar proxy", + "proxy-host": "Host proxy", + "proxy-host-required": "Se requiere el host proxy.", + "proxy-port": "Puerto proxy", + "proxy-port-required": "Se requiere el puerto proxy.", + "proxy-port-range": "El puerto proxy debe estar en el rango de 1 a 65535.", + "proxy-user": "Usuario del proxy", + "proxy-password": "Contraseña del proxy", + "change-password": "Cambiar contraseña", + "send-test-mail": "Enviar correo de prueba", + "sms-provider": "Proveedor de SMS", + "sms-provider-settings": "Configuración del proveedor de SMS", + "sms-provider-type": "Tipo de proveedor de SMS", + "sms-provider-type-required": "Se requiere el tipo de proveedor de SMS.", + "sms-provider-type-aws-sns": "Amazon SNS", + "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "ID de clave de acceso de AWS", + "aws-access-key-id-required": "Se requiere el ID de clave de acceso de AWS", + "aws-secret-access-key": "Clave de acceso secreta de AWS", + "aws-secret-access-key-required": "Se requiere la clave de acceso secreta de AWS", + "aws-region": "Región de AWS", + "aws-region-required": "Se requiere la región de AWS", + "number-from": "Número de teléfono de origen", + "number-from-required": "Se requiere el número de teléfono de origen.", + "number-to": "Número de teléfono de destino", + "number-to-required": "Se requiere el número de teléfono de destino.", + "phone-number-hint": "Número de teléfono en formato E.164, ej. +19995550123", + "phone-number-hint-twilio": "Número de teléfono en formato E.164/SID del número/SID del servicio de mensajería, ej. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Número de teléfono inválido. Debe estar en formato E.164, ej. +19995550123.", + "phone-number-pattern-twilio": "Número de teléfono inválido. Debe estar en formato E.164/SID del número/SID del servicio de mensajería, ej. +19995550123/PNXXX/MGXXX.", + "sms-message": "Mensaje SMS", + "sms-message-required": "Se requiere el mensaje SMS.", + "sms-message-max-length": "El mensaje SMS no puede superar los 1600 caracteres", + "twilio-account-sid": "SID de cuenta de Twilio", + "twilio-account-sid-required": "Se requiere el SID de cuenta de Twilio", + "twilio-account-token": "Token de cuenta de Twilio", + "twilio-account-token-required": "Se requiere el token de cuenta de Twilio", + "send-test-sms": "Enviar SMS de prueba", + "test-sms-sent": "¡El SMS de prueba se envió correctamente!", + "security-settings": "Configuración de seguridad", + "password-policy": "Política de contraseñas", + "minimum-password-length": "Longitud mínima de la contraseña", + "minimum-password-length-required": "Se requiere la longitud mínima de la contraseña", + "minimum-password-length-range": "La longitud mínima de la contraseña debe estar entre 6 y 50", + "maximum-password-length": "Longitud máxima de la contraseña", + "maximum-password-length-min": "La longitud máxima de la contraseña debe ser al menos 6", + "maximum-password-length-less-min": "La longitud máxima de la contraseña debe ser mayor que la mínima", + "minimum-uppercase-letters": "Número mínimo de letras mayúsculas", + "minimum-uppercase-letters-range": "El número mínimo de letras mayúsculas no puede ser negativo", + "minimum-lowercase-letters": "Número mínimo de letras minúsculas", + "minimum-lowercase-letters-range": "El número mínimo de letras minúsculas no puede ser negativo", + "minimum-digits": "Número mínimo de dígitos", + "minimum-digits-range": "El número mínimo de dígitos no puede ser negativo", + "minimum-special-characters": "Número mínimo de caracteres especiales", + "minimum-special-characters-range": "El número mínimo de caracteres especiales no puede ser negativo", + "password-expiration-period-days": "Periodo de expiración de contraseña (en días)", + "password-expiration-period-days-range": "El periodo de expiración de la contraseña no puede ser negativo", + "password-reuse-frequency-days": "Frecuencia de reutilización de contraseña (en días)", + "password-reuse-frequency-days-range": "La frecuencia de reutilización de la contraseña no puede ser negativa", + "allow-whitespace": "Permitir espacios en blanco", + "force-reset-password-if-no-valid": "Forzar restablecimiento de contraseña si no es válida", + "force-reset-password-if-no-valid-hint": "Ten cuidado al habilitar esta función: requerirá a los usuarios con contraseñas no válidas que las restablezcan por correo electrónico.", + "general-policy": "Política general", + "max-failed-login-attempts": "Número máximo de intentos fallidos de inicio de sesión antes de bloquear la cuenta", + "minimum-max-failed-login-attempts-range": "El número máximo de intentos fallidos no puede ser negativo", + "user-lockout-notification-email": "En caso de bloqueo de cuenta de usuario, enviar notificación por correo", + "user-activation-token-ttl": "Duración del enlace de activación de usuario (en horas)", + "user-activation-token-ttl-range": "La duración del enlace de activación debe estar entre 1 y 24 horas", + "password-reset-token-ttl": "Duración del enlace de restablecimiento de contraseña (en horas)", + "password-reset-token-ttl-range": "La duración del enlace de restablecimiento debe estar entre 1 y 24 horas", + "mobile-secret-key-length": "Longitud de la clave secreta móvil", + "mobile-secret-key-length-range": "La longitud de la clave secreta móvil debe ser positiva", + "domain-name": "Nombre de dominio", + "domain-name-unique": "El nombre de dominio y el protocolo deben ser únicos.", + "domain-name-max-length": "El nombre de dominio debe tener menos de 256 caracteres", + "error-verification-url": "Un nombre de dominio no debe contener símbolos '/' ni ':'. Ejemplo: thingsboard.io", + "connection-settings": "Configuración de conexión", + "oauth2": { + "access-token-uri": "URI del token de acceso", + "access-token-uri-required": "Se requiere la URI del token de acceso.", + "activate-user": "Activar usuario", + "add-domain": "Agregar dominio", + "delete-domain": "Eliminar dominio", + "add-provider": "Agregar proveedor", + "delete-provider": "Eliminar proveedor", + "allow-user-creation": "Permitir creación de usuarios", + "always-fullscreen": "Siempre en pantalla completa", + "authorization-uri": "URI de autorización", + "authorization-uri-required": "Se requiere la URI de autorización.", + "add-client": "Agregar cliente OAuth 2.0", + "client-details": "Detalles del cliente OAuth 2.0", + "client": "Cliente OAuth 2.0", + "clients": "Clientes OAuth 2.0", + "no-oauth2-clients": "No se encontraron clientes OAuth 2.0", + "search-oauth2-clients": "Buscar clientes OAuth 2.0", + "delete-client-title": "¿Estás seguro de que deseas eliminar el cliente OAuth 2.0 '{{clientName}}'?", + "delete-client-text": "Ten cuidado, después de la confirmación el cliente y todos los datos relacionados serán irrecuperables.", + "delete-mobile-app-title": "¿Estás seguro de que deseas eliminar la aplicación móvil '{{applicationName}}'?", + "delete-mobile-app-text": "Ten cuidado, después de la confirmación la aplicación móvil y todos los datos relacionados serán irrecuperables.", + "title": "Título", + "client-title-required": "Se requiere el título", + "client-title-max-length": "El título debe tener menos de 100 caracteres", + "advanced-settings": "Configuraciones avanzadas", + "domain-details": "Detalles del dominio", + "no-domains": "No se encontraron dominios", + "search-domains": "Buscar dominios", + "mobile-app-details": "Detalles de la aplicación móvil", + "add-mobile-app": "Agregar aplicación móvil", + "no-mobile-apps": "No se encontraron aplicaciones móviles", + "search-mobile-apps": "Buscar aplicaciones móviles", + "send-token": "Enviar token", + "create-new": "Crear nuevo", + "client-authentication-method": "Método de autenticación del cliente", + "client-id": "ID de cliente", + "client-id-required": "Se requiere el ID de cliente.", + "client-id-max-length": "El ID de cliente debe tener menos de 256 caracteres", + "client-secret": "Secreto del cliente", + "client-secret-required": "Se requiere el secreto del cliente.", + "client-secret-max-length": "El secreto del cliente debe tener menos de 2049 caracteres", + "custom-setting": "Configuraciones personalizadas", + "customer-name-pattern": "Patrón de nombre de cliente", + "customer-name-pattern-max-length": "El patrón de nombre de cliente debe tener menos de 256 caracteres", + "default-dashboard-name": "Nombre del tablero predeterminado", + "default-dashboard-name-max-length": "El nombre del tablero predeterminado debe tener menos de 256 caracteres", + "delete-domain-text": "Ten cuidado, después de la confirmación el dominio y todos los datos del proveedor dejarán de estar disponibles.", + "delete-domain-title": "¿Estás seguro de que deseas eliminar el dominio '{{domainName}}'?", + "delete-registration-text": "Ten cuidado, después de la confirmación los datos del proveedor dejarán de estar disponibles.", + "delete-registration-title": "¿Estás seguro de que deseas eliminar el proveedor '{{name}}'?", + "email-attribute-key": "Clave del atributo de correo electrónico", + "email-attribute-key-required": "Se requiere la clave del atributo de correo electrónico.", + "email-attribute-key-max-length": "La clave del atributo de correo electrónico debe tener menos de 32 caracteres", + "first-name-attribute-key": "Clave del atributo del nombre", + "first-name-attribute-key-max-length": "La clave del atributo del nombre debe tener menos de 32 caracteres", + "general": "General", + "jwk-set-uri": "URI del conjunto de claves JSON Web", + "last-name-attribute-key": "Clave del atributo del apellido", + "last-name-attribute-key-max-length": "La clave del atributo del apellido debe tener menos de 32 caracteres", + "login-button-icon": "Icono del botón de inicio de sesión", + "login-button-label": "Etiqueta del proveedor", + "login-button-label-placeholder": "Iniciar sesión con $(Provider label)", + "login-button-label-required": "Se requiere la etiqueta.", + "login-provider": "Proveedor de inicio de sesión", + "mapper": "Mapeador", + "new-domain": "Nuevo dominio", + "oauth2": "OAuth 2.0", + "password-max-length": "La contraseña debe tener menos de 256 caracteres", + "redirect-uri-template": "Plantilla de URI de redirección", + "copy-redirect-uri": "Copiar URI de redirección", + "registration-id": "ID de registro", + "registration-id-required": "Se requiere el ID de registro.", + "registration-id-unique": "El ID de registro debe ser único en el sistema.", + "scope": "Alcance", + "scope-required": "Se requiere el alcance.", + "tenant-name-pattern": "Patrón de nombre del inquilino", + "tenant-name-pattern-required": "Se requiere el patrón de nombre del inquilino.", + "tenant-name-pattern-max-length": "El patrón de nombre del inquilino debe tener menos de 256 caracteres", + "tenant-name-strategy": "Estrategia de nombre del inquilino", + "type": "Tipo de mapeador", + "uri-pattern-error": "Formato de URI inválido.", + "url": "URL", + "url-pattern": "Formato de URL inválido.", + "url-required": "Se requiere la URL.", + "url-max-length": "La URL debe tener menos de 256 caracteres", + "user-info-uri": "URI de información del usuario", + "user-info-uri-required": "Se requiere la URI de información del usuario.", + "username-max-length": "El nombre de usuario debe tener menos de 256 caracteres", + "user-name-attribute-name": "Clave del atributo del nombre de usuario", + "user-name-attribute-name-required": "Se requiere la clave del atributo del nombre de usuario", + "protocol": "Protocolo", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "Habilitar configuración OAuth 2.0", + "disable": "Deshabilitar configuración OAuth 2.0", + "edge": "Propagar a Edge", + "edge-enable": "Habilitar propagación a Edge", + "edge-disable": "Deshabilitar propagación a Edge", + "domains": "Dominios", + "mobile-apps": "Aplicaciones móviles", + "mobile-package": "Paquete de la aplicación", + "mobile-package-placeholder": "Ej.: my.example.app", + "mobile-package-hint": "Para Android: tu propio ID único de aplicación. Para iOS: identificador del paquete del producto.", + "mobile-package-unique": "El paquete de la aplicación debe ser único.", + "mobile-package-required": "Se requiere el paquete de la aplicación.", + "mobile-package-max-length": "El paquete de la aplicación debe tener menos de 256 caracteres", + "mobile-package-spaces": "El paquete de la aplicación no debe contener espacios", + "mobile-app-secret": "Secreto de la aplicación", + "mobile-app-secret-hint": "Cadena codificada en Base64 que representa al menos 512 bits de datos.", + "mobile-app-secret-required": "Se requiere el secreto de la aplicación.", + "mobile-app-secret-min-length": "El secreto de la aplicación debe tener al menos 512 bits de datos.", + "mobile-app-secret-base64": "El secreto de la aplicación debe estar en formato base64.", + "invalid-mobile-app-secret": "El secreto de la aplicación solo debe contener caracteres alfanuméricos y tener entre 16 y 2048 caracteres.", + "copy-mobile-app-secret": "Copiar secreto de la aplicación", + "delete-mobile-app": "Eliminar información de la aplicación", + "providers": "Proveedores", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Todas las plataformas", + "smtp-provider": "Proveedor SMTP", + "allowed-platforms": "Plataformas permitidas", + "authentication": "Autenticación", + "basic": "Básico", + "provider": "Proveedor", + "redirect-url": "URI de redirección", + "domain-name": "Nombre de dominio", + "domain-name-required": "Se requiere el nombre de dominio", + "redirect-url-template": "Plantilla de URI de redirección", + "microsoft-tenant-id": "ID de directorio (inquilino)", + "microsoft-tenant-id-required": "Se requiere el ID de directorio (inquilino)", + "token-uri": "URI del token", + "token-uri-required": "Se requiere la URI del token", + "redirect-uri": "URI de redirección", + "google-provider": "Google", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "Personalizado", + "generate-access-token": "Generar token de acceso", + "update-access-token": "Actualizar token de acceso", + "access-token-status": "Estado del token de acceso:", + "token-status-generated": "generado", + "token-status-not-generated": "no generado" }, - "action": { - "activate": "Activar", - "suspend": "Suspender", - "save": "Guardar", - "saveAs": "Guardar como", - "move": "Mover", - "cancel": "Cancelar", - "ok": "OK", - "delete": "Borrar", - "add": "Agregar", - "yes": "Si", - "no": "No", - "update": "Actualizar", - "remove": "Eliminar", - "select": "Seleccionar", - "search": "Buscar", - "clear-search": "Borrar búsqueda", - "assign": "Asignar", - "unassign": "Anular asignación", - "share": "Compartir", - "make-private": "Hacer privado", - "apply": "Aplicar", - "apply-changes": "Aplicar cambios", - "edit-mode": "Modo Edición", - "enter-edit-mode": "Modo Edición", - "decline-changes": "Descartar cambios", - "decline": "Descartar", - "close": "Cerrar", - "back": "Atrás", - "run": "Ejecutar", - "sign-in": "Entrar!", - "edit": "Editar", - "view": "Ver", - "create": "Crear", - "drag": "Arrastrar", - "refresh": "Actualizar", - "undo": "Deshacer", - "copy": "Copiar", - "paste": "Pegar", - "copy-reference": "Copiar referencia", - "paste-reference": "Pegar referencia", - "import": "Importar", - "export": "Exportar", - "share-via": "Compartir vía {{provider}}", - "continue": "Continuar", - "discard-changes": "Cancelar cambios", - "download": "Descargar", - "next": "Siguiente", - "next-with-label": "Siguiente: {{label}}", - "read-more": "Leer más", - "hide": "Ocultar", - "done": "Terminado", - "print": "Imprimir", - "restore": "Restaurar", - "confirm": "Confirmar", - "more": "Más", - "less": "Menos", - "skip": "Saltar", - "send": "Enviar", - "reset": "Reset", - "show-more": "Mostrar más", - "dont-show-again": "No volver a mostrar", - "see-documentation": "Ver documentación", - "clear": "Borrar" + "smpp-provider": { + "smpp-version": "Versión SMPP", + "smpp-host": "Host SMPP", + "smpp-host-required": "Se requiere el host SMPP", + "smpp-port": "Puerto SMPP", + "smpp-port-required": "Se requiere el puerto SMPP", + "system-id": "ID del sistema", + "system-id-required": "Se requiere el ID del sistema", + "password": "Contraseña", + "password-required": "Se requiere la contraseña", + "type-settings": "Configuración del tipo", + "source-settings": "Configuración de origen", + "destination-settings": "Configuración de destino", + "additional-settings": "Configuración adicional", + "system-type": "Tipo de sistema", + "bind-type": "Tipo de enlace", + "service-type": "Tipo de servicio", + "source-address": "Dirección de origen", + "source-ton": "TON de origen", + "source-npi": "NPI de origen", + "destination-ton": "TON de destino (Tipo de número)", + "destination-npi": "NPI de destino (Identificación del plan de numeración)", + "address-range": "Rango de direcciones", + "coding-scheme": "Esquema de codificación", + "bind-type-tx": "Transmisor", + "bind-type-rx": "Receptor", + "bind-type-trx": "Transceptor", + "ton-unknown": "Desconocido", + "ton-international": "Internacional", + "ton-national": "Nacional", + "ton-network-specific": "Específico de red", + "ton-subscriber-number": "Número de suscriptor", + "ton-alphanumeric": "Alfanumérico", + "ton-abbreviated": "Abreviado", + "npi-unknown": "0 - Desconocido", + "npi-isdn": "1 - ISDN/plan de numeración telefónica (E163/E164)", + "npi-data-numbering-plan": "3 - Plan de numeración de datos (X.121)", + "npi-telex-numbering-plan": "4 - Plan de numeración de télex (F.69)", + "npi-land-mobile": "6 - Móvil terrestre (E.212)", + "npi-national-numbering-plan": "8 - Plan de numeración nacional", + "npi-private-numbering-plan": "9 - Plan de numeración privado", + "npi-ermes-numbering-plan": "10 - Plan de numeración ERMES (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internet (IP)", + "npi-wap-client-id": "18 - ID de cliente WAP (definido por el Foro WAP)", + "scheme-smsc": "0 - Alfabeto predeterminado SMSC (ASCII para códigos cortos y largos y GSM para números gratuitos)", + "scheme-ia5": "1 - IA5 (ASCII para códigos cortos y largos, Latin 9 para números gratuitos (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Octeto no especificado (binario de 8 bits)", + "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Octeto no especificado (binario de 8 bits)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Cirílico (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latín/Hebreo (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Codificación de pictogramas", + "scheme-music-codes": "10 - Códigos de música (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Kanji extendido JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Conjunto gráfico de caracteres coreanos (KS C 5601/KS X 1001)" }, - "aggregation": { - "aggregation": "Agrupación", - "function": "Función de Agrupación", - "limit": "Valores Max", - "group-interval": "Intervalo de agrupación", - "min": "Min", - "max": "Max", - "avg": "Promedio", - "sum": "Suma", - "count": "Contar", - "none": "Ninguno" + "queue-select-name": "Seleccionar nombre de la cola", + "queue-name": "Nombre", + "queue-name-required": "¡Se requiere el nombre de la cola!", + "queues": "Colas", + "queue-partitions": "Particiones", + "queue-submit-strategy": "Estrategia de envío", + "queue-processing-strategy": "Estrategia de procesamiento", + "queue-configuration": "Configuración de la cola", + "repository-settings": "Configuración del repositorio", + "repository": "Repositorio", + "repository-url": "URL del repositorio", + "repository-url-required": "Se requiere la URL del repositorio.", + "default-branch": "Nombre de la rama predeterminada", + "repository-read-only": "Solo lectura", + "show-merge-commits": "Mostrar commits de fusión", + "authentication-settings": "Configuración de autenticación", + "auth-method": "Método de autenticación", + "auth-method-username-password": "Contraseña / token de acceso", + "auth-method-username-password-hint": "Los usuarios de GitHub deben usar tokens con permisos de escritura para el repositorio.", + "auth-method-private-key": "Clave privada", + "password-access-token": "Contraseña / token de acceso", + "change-password-access-token": "Cambiar contraseña / token de acceso", + "private-key": "Clave privada", + "drop-private-key-file-or": "Arrastra y suelta un archivo de clave privada o", + "passphrase": "Frase de contraseña", + "enter-passphrase": "Introduce la frase de contraseña", + "change-passphrase": "Cambiar frase de contraseña", + "check-access": "Verificar acceso", + "check-repository-access-success": "¡Acceso al repositorio verificado con éxito!", + "delete-repository-settings-title": "¿Estás seguro de que deseas eliminar la configuración del repositorio?", + "delete-repository-settings-text": "Ten cuidado, después de la confirmación la configuración del repositorio se eliminará y la función de control de versiones no estará disponible.", + "auto-commit-settings": "Configuración de auto-commit", + "auto-commit": "Auto-commit", + "auto-commit-entities": "Entidades de auto-commit", + "no-auto-commit-entities-prompt": "No hay entidades configuradas para auto-commit", + "delete-auto-commit-settings-title": "¿Estás seguro de que deseas eliminar la configuración de auto-commit?", + "delete-auto-commit-settings-text": "Ten cuidado, después de la confirmación la configuración de auto-commit se eliminará y se deshabilitará para todas las entidades.", + "mobile-app": { + "mobile-app": "Aplicación móvil", + "mobile-app-qr-code-widget-settings": "Configuración del widget de código QR de la aplicación móvil", + "applications": "Aplicaciones", + "default": "Predeterminado", + "custom": "Personalizado", + "android": "Android", + "ios": "iOS", + "appearance": "Apariencia", + "appearance-on-home-page": "Apariencia en la página de inicio", + "enabled": "Habilitado", + "disabled": "Deshabilitado", + "badges": "Insignias", + "label": "Etiqueta", + "label-required": "Se requiere la etiqueta", + "label-max-length": "La etiqueta debe tener 50 caracteres o menos", + "right": "Derecha", + "left": "Izquierda", + "set": "Establecer", + "preview": "Vista previa", + "connect-mobile-app": "Conectar aplicación móvil", + "use-system-settings": "Usar configuración del sistema" }, - "admin": { - "settings": "Ajustes", - "general": "General", - "general-settings": "Configuración general", - "home-settings": "Ajustes Home", - "home": "Home", - "outgoing-mail": "Servidor de correo", - "outgoing-mail-settings": "Configuración del servidor de correo de salida", - "system-settings": "Sistema", - "test-mail-sent": "Mail de prueba enviado correctamente!", - "base-url": "URL Base", - "base-url-required": "URL Base requerida.", - "prohibit-different-url": "Prohibir el uso de hostname en cabeceras de request del cliente", - "prohibit-different-url-hint": "Este ajuste debe ser activado en entornos de producción. Puede causar fallos de seguridad si está desactivado", - "mail-from": "Mail Desde", - "mail-from-required": "Mail Desde requerido.", - "smtp-protocol": "Protocolo SMTP", - "smtp-host": "Host SMTP", - "smtp-host-required": "Host SMTP requerido.", - "smtp-port": "Puerto SMTP", - "smtp-port-required": "Debe ingresar un Puerto SMTP.", - "smtp-port-invalid": "No parece un Puerto SMTP valido.", - "timeout-msec": "Timeout (ms)", - "timeout-required": "Timeout requerido.", - "timeout-invalid": "No parece un Timeout valido.", - "enable-tls": "Habilitar TLS", - "tls-version": "Versión TLS", - "enable-proxy": "Habilitar proxy", - "proxy-host": "Host proxy", - "proxy-host-required": "Se requiere host Proxy.", - "proxy-port": "Puerto proxy", - "proxy-port-required": "Se requiere puerto proxy.", - "proxy-port-range": "El puerto proxy debe estar en un rango de 1 a 65535.", - "proxy-user": "Usuario proxy", - "proxy-password": "Contraseña proxy", - "change-password": "Cambiar contraseña", - "send-test-mail": "Enviar correo de prueba", - "sms-provider": "Proveedor SMS", - "sms-provider-settings": "Ajustes proveedor SMS", - "sms-provider-type": "Tipo de proveedor SMS", - "sms-provider-type-required": "Se requiere proveedor SMS.", - "sms-provider-type-aws-sns": "Amazon SNS", - "sms-provider-type-twilio": "Twilio", - "sms-provider-type-smpp": "SMPP", - "aws-access-key-id": "AWS Access Key ID", - "aws-access-key-id-required": "AWS Access Key ID requerido", - "aws-secret-access-key": "AWS Secret Access Key", - "aws-secret-access-key-required": "AWS Secret Access Key requerido", - "aws-region": "Región AWS", - "aws-region-required": "Se requere región AWS", - "number-from": "Nº de teléfono Origen", - "number-from-required": "Se requere Nº de teléfono origen.", - "number-to": "Nº de teléfono de destino", - "number-to-required": "Se requere Nº de teléfono de destino.", - "phone-number-hint": "Nº de teléfono en formato E.164, ej. +19995550123", - "phone-number-hint-twilio": "Nº de teléfono en formato E.164 SID/SID de servicio de mensajería, ej. +19995550123/PNXXX/MGXXX", - "phone-number-pattern": "Nº Inválido. Debe estar en formato E.164, ej. +19995550123.", - "phone-number-pattern-twilio": "Nº Inválido. Debe estar en formato E.164 , ej. +19995550123/PNXXX/MGXXX.", - "sms-message": "Mensaje SMS", - "sms-message-required": "Se requeriere mensaje SMS.", - "sms-message-max-length": "Los SMS no pueden ser más largos de 1600 caracteres", - "twilio-account-sid": "SID de cuenta Twilio", - "twilio-account-sid-required": "Se requere SID de cuenta Twilio", - "twilio-account-token": "Token de cuenta Twilio", - "twilio-account-token-required": "Se requiere Token Twilio", - "send-test-sms": "Enviar SMS de prueba", - "test-sms-sent": "SMS enviado con éxito!", - "security-settings": "Configuraciones de seguridad", - "password-policy": "Política de contraseñas", - "minimum-password-length": "Longitud mínima de contraseña", - "minimum-password-length-required": "Se requiere una longitud mínima de contraseña", - "minimum-password-length-range": "La longitud mínima de la contraseña debe estar en un rango de 5 a 50", - "minimum-uppercase-letters": "Número mínimo de letras mayúsculas", - "minimum-uppercase-letters-range": "El número mínimo de letras mayúsculas no puede ser negativo", - "minimum-lowercase-letters": "Número mínimo de letras minúsculas", - "minimum-lowercase-letters-range": "El número mínimo de letras minúsculas no puede ser negativo", - "minimum-digits": "Número mínimo de dígitos", - "minimum-digits-range": "El número mínimo de dígitos no puede ser negativo", - "minimum-special-characters": "Número mínimo de caracteres especiales.", - "minimum-special-characters-range": "El número mínimo de caracteres especiales no puede ser negativo.", - "password-expiration-period-days": "Periodo de caducidad de contraseña en días", - "password-expiration-period-days-range": "El período de caducidad de la contraseña en días no puede ser negativo", - "password-reuse-frequency-days": "Frecuencia de reutilización de contraseña en días", - "password-reuse-frequency-days-range": "La frecuencia de reutilización de contraseña en días no puede ser negativa", - "allow-whitespace": "Permitir espacios en blanco", - "general-policy": "Política general", - "max-failed-login-attempts": "Número máximo de intentos fallidos de inicio de sesión, antes de que la cuenta esté bloqueada", - "minimum-max-failed-login-attempts-range": "El número máximo de intentos fallidos de inicio de sesión no puede ser negativo", - "user-lockout-notification-email": "En caso de bloqueo de la cuenta del usuario, envíe una notificación por correo electrónico", - "domain-name": "Nombre de dominio", - "domain-name-unique": "El nombre de dominio y protocolo debe ser único.", - "domain-name-max-length": "El nombre de dominio debe ser menor que 256", - "error-verification-url": "Un nombre de dominio no debe contener símbolos '/' y ':'. Ejemplo: thingsboard.io", - "connection-settings": "Ajustes de conexión", - "oauth2": { - "access-token-uri": "URI Access token", - "access-token-uri-required": "Se requere URI Access token.", - "activate-user": "Activar usuario", - "add-domain": "Añadir dominio", - "delete-domain": "Borrar dominio", - "add-provider": "Añadir proveedor", - "delete-provider": "Borrar proveedor", - "allow-user-creation": "Permitir creación de usuario", - "always-fullscreen": "Siempre pantalla completa", - "authorization-uri": "URI Autorización", - "authorization-uri-required": "Se requiere URI de Autorización.", - "client-authentication-method": "Método de autenticación", - "client-id": "ID Cliente", - "client-id-required": "Se requere ID Cliente.", - "client-id-max-length": "El ID Cliente debe ser menor de 256", - "client-secret": "Secreto de Cliente", - "client-secret-required": "Se requiere Secreto de Cliente.", - "client-secret-max-length": "El secreto de cliente debe ser menor de 2049", - "custom-setting": "Ajustes personalizados", - "customer-name-pattern": "Patrón nombre de cliente", - "customer-name-pattern-max-length": "El patrón del nombre de cliente debe ser menor de 256", - "default-dashboard-name": "Nombre de panel por defecto", - "default-dashboard-name-max-length": "El nombre del panel debe ser menor de 256", - "delete-domain-text": "Atención, tras la confirmación el dominio y todos los datos del proveedor no estarán disponibles.", - "delete-domain-title": "Eliminar los ajustes del dominio '{{domainName}}'?", - "delete-registration-text": "Atención, tras la confirmación los datos del proveedor no estarán disponibles.", - "delete-registration-title": "Eliminar el proveedor '{{name}}'?", - "email-attribute-key": "Clave de atributos email", - "email-attribute-key-required": "Se requiere clave de atributos de email.", - "email-attribute-key-max-length": "La clave de atributos de email debe ser menor de 32", - "first-name-attribute-key": "Clave de atributos de nombre", - "first-name-attribute-key-max-length": "La clave de atributos de nombre debe ser menor de 32", - "general": "General", - "jwk-set-uri": "URI web key JSON", - "last-name-attribute-key": "Clave de atributos de apellido", - "last-name-attribute-key-max-length": "La clave de atributos de apellido debe ser menor de 32", - "login-button-icon": "Icono de botón login", - "login-button-label": "Etiqueta de proveedor", - "login-button-label-placeholder": "Login con $(Provider label)", - "login-button-label-required": "Clave de etiqueta requerida.", - "login-provider": "Proveedor de login", - "mapper": "Mapeador", - "new-domain": "Nuevo dominio", - "oauth2": "OAuth2", - "password-max-length": "La contraseña debe ser menor de 256", - "redirect-uri-template": "Plantilla de redirección URI", - "copy-redirect-uri": "Copiar URI de redirección", - "registration-id": "ID de registro", - "registration-id-required": "Se requiere ID de registro.", - "registration-id-unique": "El ID de registro debe ser único en el sistema.", - "scope": "Alcance", - "scope-required": "Se requiere alcance.", - "tenant-name-pattern": "Patrón de nombre de propietario", - "tenant-name-pattern-required": "Se requiere patrón de nombre de propietario.", - "tenant-name-pattern-max-length": "EL patrón de nombre de propietario debe ser menor de 256", - "tenant-name-strategy": "Estrategia de Nombre de Propietario", - "type": "Tipo de mapeador", - "uri-pattern-error": "Formato de URI inválido.", - "url": "URL", - "url-pattern": "Formato URL inválido.", - "url-required": "Se requiere URL.", - "url-max-length": "URL debe ser menor de 256", - "user-info-uri": "URI Información de usuario", - "user-info-uri-required": "Se requiere URI de información usuario.", - "username-max-length": "El usuario debe ser menor de 256", - "user-name-attribute-name": "Clave de atributos de nombre de usuario", - "user-name-attribute-name-required": "Se requiere clave de atributos de nombre de usuario", - "protocol": "Protocolo", - "domain-schema-http": "HTTP", - "domain-schema-https": "HTTPS", - "domain-schema-mixed": "HTTP+HTTPS", - "enable": "Activar ajustes OAuth2", - "domains": "Dominios", - "mobile-apps": "Aplicaciones móviles", - "no-mobile-apps": "No hay aplicaciones configuradas", - "mobile-package": "Paquete de aplicación", - "mobile-package-placeholder": "Ej.: mi.ejemplo.app", - "mobile-package-hint": "Para Android: El ID único de aplicación. Para iOS: Identificador Product bundle.", - "mobile-package-unique": "El paquete de aplicación debe ser único.", - "mobile-app-secret": "Secreto de aplicación", - "invalid-mobile-app-secret": "El secreto de aplicación sólo puede contener carácteres alfanuméricos y debe tener entre 16 y 2048 carácteres de longitud.", - "copy-mobile-app-secret": "Copiar secreto de aplicación", - "add-mobile-app": "Añadir aplicación", - "delete-mobile-app": "Borrar información de aplicación", - "providers": "Proveedores", - "platform-web": "Web", - "platform-android": "Android", - "platform-ios": "iOS", - "all-platforms": "Todas las plataformas", - "smtp-provider": "Proveedor SMTP", - "allowed-platforms": "Plataformas permitidas", - "authentication": "Autenticación", - "basic": "Básica", - "provider": "Proveedor", - "redirect-url": "URL Redirección", - "domain-name": "Nombre de dominio", - "redirect-url-template": "Plantilla de redirección (URI)", - "microsoft-tenant-id": "Microsoft tenant ID", - "microsoft-tenant-id-required": "Se requiere tenant ID", - "token-uri": "URI Token", - "token-uri-required": "Se requiere URI token", - "redirect-uri": "URI Redirección", - "google-provider": "Google", - "microsoft-provider": "Office 365", - "sendgrid-provider": "Sendgrid", - "custom-provider": "Personalizado", - "generate-access-token": "Generar token de acceso", - "update-access-token": "Actualizar token de acceso", - "access-token-status": "Estado token:", - "token-status-generated": "Generado", - "token-status-not-generated": "No generado" - }, - "smpp-provider": { - "smpp-version": "Versión SMPP", - "smpp-host": "Host SMPP", - "smpp-host-required": "Host SMPP requerido", - "smpp-port": "Puerto SMPP", - "smpp-port-required": "Puerto SMPP requerido", - "system-id": "System ID", - "system-id-required": "System ID requerido", - "password": "Contraseña", - "password-required": "Contraseña requerida", - "type-settings": "Ajustes de tipo", - "source-settings": "Ajustes de origen", - "destination-settings": "Ajustes de destino", - "additional-settings": "Ajustes adicionales", - "system-type": "Tipo de sistema", - "bind-type": "Tipo de enlace", - "service-type": "Tipo de servicio", - "source-address": "Dirección de origen", - "source-ton": "Origen TON", - "source-npi": "Origen NPI", - "destination-ton": "Destino TON (Tipo de número)", - "destination-npi": "Destino NPI (Numbering Plan Identification)", - "address-range": "Rango de dirección", - "coding-scheme": "Esquema de codificación", - "bind-type-tx": "Transmisor", - "bind-type-rx": "Receptor", - "bind-type-trx": "Transceptor", - "ton-unknown": "Desconocido", - "ton-international": "Internacional", - "ton-national": "Nacional", - "ton-network-specific": "Específico de red", - "ton-subscriber-number": "Número de suscriptor", - "ton-alphanumeric": "Alfanumérico", - "ton-abbreviated": "Abreviado", - "npi-unknown": "0 - Desconocido", - "npi-isdn": "1 - RDSI/Teléfono plan numérico (E163/E164)", - "npi-data-numbering-plan": "3 - Datos plan numérico (X.121)", - "npi-telex-numbering-plan": "4 - Telex plan numérico (F.69)", - "npi-land-mobile": "6 - Línea Móvil (E.212)", - "npi-national-numbering-plan": "8 - Nacional", - "npi-private-numbering-plan": "9 - Privado", - "npi-ermes-numbering-plan": "10 - ERMES (ETSI DE/PS 3 01-3)", - "npi-internet": "13 - Internet (IP)", - "npi-wap-client-id": "18 - WAP Client Id (a definir por WAP Forum)", - "scheme-smsc": "0 - Alfabeto por defecto SMSC (ASCII para códigos cortos y largos y GSM para gratuitos)", - "scheme-ia5": "1 - IA5 (ASCII para códigos cortos y largos, Latin 9 para gratuitos (ISO-8859-9))", - "scheme-octet-unspecified-2": "2 - Octetos sin especificar (binario 8-bit)", - "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", - "scheme-octet-unspecified-4": "4 - Octetos sin especificar (binario 8-bit)", - "scheme-jis": "5 - JIS (X 0208-1990)", - "scheme-cyrillic": "6 - Ciríllico (ISO-8859-5)", - "scheme-latin-hebrew": "7 - Latin/Hebreo (ISO-8859-8)", - "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", - "scheme-pictogram-encoding": "9 - Codificación por Pictograma", - "scheme-music-codes": "10 - Códigos musicales (ISO-2022-JP)", - "scheme-extended-kanji-jis": "13 - Kanji extendido JIS (X 0212-1990)", - "scheme-korean-graphic-character-set": "14 - Set de carácteres Koreanos (KS C 5601/KS X 1001)" - }, - "queue-select-name": "Seleccionar nombre de cola", - "queue-name": "Nombre", - "queue-name-required": "Nombre de cola requerido!", - "queues": "Colas", - "queue-partitions": "Particiones", - "queue-submit-strategy": "Estrategia de envíos", - "queue-processing-strategy": "Estrategia de procesamiento", - "queue-configuration": "Configuración Cola", - "repository-settings": "Ajustes del repositorio", - "repository": "Repositorio", - "repository-url": "URL del repositorio", - "repository-url-required": "Se requiere la URL del repositorio.", - "default-branch": "Nombre de rama por defecto", - "repository-read-only": "Sólo lectura", - "show-merge-commits": "Mostrar commits merge", - "authentication-settings": "Ajustes de autenticación", - "auth-method": "Método de autenticación", - "auth-method-username-password": "Usuario / Contraseña", - "auth-method-username-password-hint": "Los usuarios de GitHub deben usar access tokens con permisos de escritura en el repositorio.", - "auth-method-private-key": "Clave privada", - "password-access-token": "Contraseña / Tóken de acceso", - "change-password-access-token": "Cambiar contraseña / Tóken de acceso", - "private-key": "Clave privada", - "drop-private-key-file-or": "Arrastrar y soltar un fichero de llave privada o", - "passphrase": "Frase de contraseña", - "enter-passphrase": "Entrar frase de contraseña", - "change-passphrase": "Cambiar frase de contraseña", - "check-access": "Verificar acceso", - "check-repository-access-success": "Acceso al repositorio verificado satisfactoriamente!", - "delete-repository-settings-title": "Estás seguro de borrar los ajustes del repositorio?", - "delete-repository-settings-text": "Atención, tras la confirmación los ajustes del repositorio serán eliminados y la característica de control de versiones no estará disponible.", - "auto-commit-settings": "Ajustes Auto-commit", - "auto-commit": "Auto-commit", - "auto-commit-entities": "Entidades Auto-commit", - "no-auto-commit-entities-prompt": "No hay entidades configuradas para auto-publicar", - "delete-auto-commit-settings-title": "Estás seguro de borrar los ajustes de auto-publicar?", - "delete-auto-commit-settings-text": "Atención, tras la confirmación los ajustes de auto-publicar serán borrados y la característica de auto-publicar se desactivará para todas las entidades.", - "2fa": { - "2fa": "Autenticación de dos factores (2FA)", - "available-providers": "Proveedores disponibles", - "issuer-name": "Nombre de emisor", - "issuer-name-required": "Nombre de emisor requerido.", - "max-verification-failures-before-user-lockout": "Máximo de fallos de verificación antes de bloquear cuenta", - "max-verification-failures-before-user-lockout-pattern": "Máximo de fallos debe ser un número entero positivo.", - "number-of-checking-attempts": "Número de intentos de verificación", - "number-of-checking-attempts-pattern": "Número de intentos debe ser un número entero positivo.", - "number-of-checking-attempts-required": "Número de intentos requerido.", - "number-of-codes": "Número de códigos", - "number-of-codes-pattern": "Número de códigos debe ser un número entero positivo.", - "number-of-codes-required": "Número de códigos requerido.", - "provider": "Proveedor", - "retry-verification-code-period": "Reintentos de código de verificación (sec)", - "retry-verification-code-period-pattern": "El período mínimo es de 5 sec", - "retry-verification-code-period-required": "Reintentos requerido.", - "total-allowed-time-for-verification": "Total de tiempo permitido para verificación (sec)", - "total-allowed-time-for-verification-pattern": "El mínimo del total de tiempo perminito es de 60 sec", - "total-allowed-time-for-verification-required": "Total de tiempo requerido.", - "use-system-two-factor-auth-settings": "Usar ajustes 2FA del sistema", - "verification-code-check-rate-limit": "Límite de chequeo del código de verificación", - "verification-code-lifetime": "Tiempo de vida del código de verificación (sec)", - "verification-code-lifetime-pattern": "Tiempo de vida del código de verificación debe ser un número entero positivo.", - "verification-code-lifetime-required": "Tiempo de vida requerido.", - "verification-message-template": "Plantilla del mensaje de verificación", - "verification-limitations": "Límites de verificación", - "verification-message-template-pattern": "El mensaje de verificación debe contener el patrón: ${code}", - "verification-message-template-required": "Plantilla de mensaje de verificación requerida.", - "within-time": "Rango de tiempo (sec)", - "within-time-pattern": "Tiempo debe ser un número entero positivo.", - "within-time-required": "Tiempo requerido." - }, - "jwt": { - "security-settings": "Ajustes de seguridad JWT", - "issuer-name": "Nombre del emisor", - "issuer-name-required": "Se requiere nombre del emisor.", - "signings-key": "Clave de firma", - "signings-key-hint": "Una string codificada en Base64 representando por lo menos 512 bits de datos.", - "signings-key-required": "Se requiere clave de firma.", - "signings-key-min-length": "La clave de firma debe tener al menos 512 bits de datos.", - "signings-key-base64": "La clave de firma debe estar en formato base64.", - "expiration-time": "Caducidad del token (en segundos)", - "expiration-time-required": "Se requiere caducidad del token.", - "expiration-time-min": "El tiempo mínimo debe ser al menos de 60 segundos (1 minuto).", - "refresh-expiration-time": "Caducidad del token de actualización (en segundos)", - "refresh-expiration-time-required": "Se requiere especificar caducidad del token de actualización.", - "refresh-expiration-time-min": "El tiempo mínimo es de 900 segundos (15 minutos).", - "refresh-expiration-time-less-token": "El tiempo de actualización debe ser mayor al de caducidad.", - "generate-key": "Generar clave", - "info-header": "Todos los usuarios necesitarán volver a logearse en la plataforma", - "info-message": "El cambio en la clave de firma del JWT causará que todos los tokens emitidos sean inválidos. Todos los usuarios deberán hacer login de nuevo. Esto también afectará a los scripts que usen la API REST o Websockets." - }, - "resources": "Recursos", - "notifications": "Notificaciones", - "notifications-settings": "Ajustes de notificaciones", - "slack-api-token": "Token de API Slack", - "slack": "Slack", - "slack-settings": "Ajustes de Slack" - }, - "alarm": { - "alarm": "Alarma", - "alarms": "Alarmas", - "all-alarms": "Todas las alarmas", - "select-alarm": "Seleccionar alarma", - "no-alarms-matching": "No se han encontrado alarmas coincidentes con '{{entity}}' .", - "alarm-required": "Alarma requerida", - "alarm-filter": "Filtro de alarma", - "filter": "Filtro", - "alarm-status": "Estado de Alarma", - "alarm-status-list": "Lista de estados de Alarmas", - "any-status": "Cualquier estado", - "search-status": { - "ANY": "Todas", - "ACTIVE": "Activas", - "CLEARED": "Borradas", - "ACK": "Reconocidas", - "UNACK": "Ignoradas" - }, - "display-status": { - "ACTIVE_UNACK": "Activa No reconocida", - "ACTIVE_ACK": "Activa Reconocida", - "CLEARED_UNACK": "Normalizada no reconocida", - "CLEARED_ACK": "Normalizada reconocida" - }, - "no-alarms-prompt": "No se encontraron alarmas", - "created-time": "Hora de creación", - "type": "Tipo", - "severity": "Gravedad", - "originator": "Origen", - "originator-type": "Tipo de origen", - "details": "Detalles", - "originator-label": "Etiqueta de autor", - "assign": "Asignar", - "assignments": "Asignaciones", - "assignee": "Asignado", - "assignee-id": "ID de asignado", - "assignee-first-name": "Nombre de asignado", - "assignee-last-name": "Apellido de asignado", - "assignee-email": "Email del Assignee email", - "unassigned": "Sin asignar", - "assignee-not-set": "Todas", - "status": "Estado", - "alarm-details": "Detalles Alarma", - "start-time": "Hora de inicio", - "assign-time": "Hora de asignación", - "end-time": "Hora fin", - "ack-time": "Hora de reconocimiento", - "clear-time": "Hora de normalización", - "duration": "Duración", - "alarm-severity-list": "Lista de gravedad de alarmas", - "any-severity": "Cualquier gravedad", - "severity-critical": "Crítica", - "severity-major": "Mayor", - "severity-minor": "Menor", - "severity-warning": "Peligro", - "severity-indeterminate": "Indeterminada", - "acknowledge": "Reconocer", - "clear": "Normalizar", - "delete": "Borrar", - "search": "Buscar alarmas", - "selected-alarms": "{ count, plural, =1 {1 alarma} other {# alarmas} } seleccionadas", - "no-data": "Sin datos que mostrar", - "polling-interval": "Ciclo de refresco de alarmas (seg)", - "polling-interval-required": "Se requiere un ciclo de refresco válido.", - "min-polling-interval-message": "El ciclo debe ser por lo menos de 1 segundo.", - "aknowledge-alarms-title": "Reconocer { count, plural, =1 {1 alarma} other {# alarmas} }", - "aknowledge-alarms-text": "Estas seguro de reconocer { count, plural, =1 {1 alarma} other {# alarmas} }?", - "aknowledge-alarm-title": "Recononcer Alarma", - "aknowledge-alarm-text": "Estas seguro de reconocer Alarma?", - "selected-alarms-are-acknowledged": "Las alarmas seleccionadas ya están reconocidas", - "clear-alarms-title": "Normalizar { count, plural, =1 {1 alarma} other {# alarmas} }", - "clear-alarms-text": "Limpiar { count, plural, =1 {1 alarma} other {# alarmas} }?", - "clear-alarm-title": "Limpiar Alarma", - "clear-alarm-text": "Limpiar Alarma?", - "delete-alarms-title": "Delete { count, plural, =1 {1 alarm} other {# alarms} }", - "delete-alarms-text": "Are you sure you want to delete { count, plural, =1 {1 alarm} other {# alarms} }?", - "selected-alarms-are-cleared": "Selected alarms are already cleared", - "alarm-status-filter": "Filtro de estados de Alarmas", - "alarm-filter-title": "Filtros de alarmas", - "assigned": "Asignadas", - "filter-title": "Filtrar", - "max-count-load": "Número máximo de alarmas a cargar (0 - ilimitado)", - "max-count-load-required": "Se requiere número máximo de alarmas.", - "max-count-load-error-min": "El valor mínimo es 0.", - "fetch-size": "Tamaño de búsqueda (Fetch)", - "fetch-size-required": "Se requiere tamaño de búsqueda.", - "fetch-size-error-min": "El valor mínimo es 10.", - "alarm-type-list": "Lista de tipos de alarma", - "any-type": "Cualquier tipo", - "assigned-to-current-user": "Asignadas al ususario actual", - "assigned-to-me": "Asignadas a mí", - "search-propagated-alarms": "Buscar alarmas propagadas", - "comments": "Comentarios de alarma", - "show-more": "Mostrar más", - "additional-info": "Información adicional", - "alarm-type": "Tipo de alarma", - "enter-alarm-type": "Introduce tipo de alarma", - "no-alarm-types-matching": "No se han encontrado tipos de alarma que coincidan con '{{entitySubtype}}'.", - "alarm-type-list-empty": "No se han seleccionado tipos de alarma." + "2fa": { + "2fa": "Autenticación de dos factores", + "available-providers": "Proveedores disponibles", + "issuer-name": "Nombre del emisor", + "issuer-name-required": "Se requiere el nombre del emisor.", + "max-verification-failures-before-user-lockout": "Máximo de fallos de verificación antes de bloquear al usuario", + "max-verification-failures-before-user-lockout-pattern": "El número máximo de fallos de verificación debe ser un número entero positivo.", + "number-of-checking-attempts": "Número de intentos de verificación", + "number-of-checking-attempts-pattern": "El número de intentos debe ser un número entero positivo.", + "number-of-checking-attempts-required": "Se requiere el número de intentos de verificación.", + "number-of-codes": "Número de códigos", + "number-of-codes-pattern": "El número de códigos debe ser un número entero positivo.", + "number-of-codes-required": "Se requiere el número de códigos.", + "provider": "Proveedor", + "retry-verification-code-period": "Periodo de reintento del código de verificación (seg)", + "retry-verification-code-period-pattern": "El tiempo mínimo del periodo es de 5 segundos", + "retry-verification-code-period-required": "Se requiere el periodo de reintento del código de verificación.", + "total-allowed-time-for-verification": "Tiempo total permitido para la verificación (seg)", + "total-allowed-time-for-verification-pattern": "El tiempo total permitido debe ser al menos 60 segundos", + "total-allowed-time-for-verification-required": "Se requiere el tiempo total permitido.", + "use-system-two-factor-auth-settings": "Usar configuración del sistema de autenticación en dos pasos", + "verification-code-check-rate-limit": "Límite de velocidad de comprobación del código de verificación", + "verification-code-lifetime": "Duración del código de verificación (seg)", + "verification-code-lifetime-pattern": "La duración debe ser un número entero positivo.", + "verification-code-lifetime-required": "Se requiere la duración del código de verificación.", + "verification-message-template": "Plantilla del mensaje de verificación", + "verification-limitations": "Limitaciones de verificación", + "verification-message-template-pattern": "El mensaje de verificación debe contener el patrón: ${code}", + "verification-message-template-required": "Se requiere la plantilla del mensaje de verificación.", + "within-time": "Dentro del tiempo (seg)", + "within-time-pattern": "El tiempo debe ser un número entero positivo.", + "within-time-required": "Se requiere el tiempo." }, - "alarm-activity": { - "add": "Añadir comentario...", - "alarm-comment": "Comentario", - "comments": "Commentarios", - "delete-alarm-comment": "Quieres borrar este comentario?", - "refresh": "Actualizar", - "oldest-first": "Antíguos primero", - "newest-first": "Nuevos primero", - "activity": "Actividad", - "export": "Exportar a CSV", - "author": "Autor", - "created-date": "Fecha de creación", - "edited-date": "Fecha de edición", - "text": "Texto", - "system": "Sistema" + "jwt": { + "security-settings": "Configuración de seguridad JWT", + "issuer-name": "Nombre del emisor", + "issuer-name-required": "Se requiere el nombre del emisor.", + "signings-key": "Clave de firma", + "signings-key-hint": "Cadena codificada en Base64 que representa al menos 512 bits de datos.", + "signings-key-required": "Se requiere la clave de firma.", + "signings-key-min-length": "La clave de firma debe tener al menos 512 bits de datos.", + "signings-key-base64": "La clave de firma debe estar en formato base64.", + "expiration-time": "Tiempo de expiración del token (seg)", + "expiration-time-required": "Se requiere el tiempo de expiración del token.", + "expiration-time-max": "El tiempo máximo permitido es de 2147483647 segundos (68 años).", + "expiration-time-min": "El tiempo mínimo es de 60 segundos (1 minuto).", + "refresh-expiration-time": "Tiempo de expiración del token de actualización (seg)", + "refresh-expiration-time-required": "Se requiere el tiempo de expiración del token de actualización.", + "refresh-expiration-time-max": "El tiempo máximo permitido es de 2147483647 segundos (68 años).", + "refresh-expiration-time-min": "El tiempo mínimo es de 900 segundos (15 minutos).", + "refresh-expiration-time-less-token": "El tiempo del token de actualización debe ser mayor que el del token.", + "generate-key": "Generar clave", + "info-header": "Todos los usuarios tendrán que volver a iniciar sesión", + "info-message": "El cambio de la clave de firma JWT invalidará todos los tokens emitidos. Todos los usuarios tendrán que volver a iniciar sesión. Esto también afectará a los scripts que utilizan la API Rest/Websockets." }, - "alias": { - "add": "Añadir alias", - "edit": "Editar alias", - "name": "Nombre de Alias", - "name-required": "Se requiere nombre de alias", - "duplicate-alias": "Ya existe ese nombre de alias.", - "filter-type-single-entity": "Única entidad", - "filter-type-entity-list": "Lista de entidades", - "filter-type-entity-name": "Nombre de entidad", - "filter-type-entity-type": "Tipo de entidad", - "filter-type-state-entity": "Entidad desde estado de panel", - "filter-type-state-entity-description": "Entidad tomada de los parámetros de panel", - "filter-type-asset-type": "Tipo de activo", - "filter-type-asset-type-description": "Activos del tipo '{{assetTypes}}'", - "filter-type-asset-type-and-name-description": "Activos del tipo '{{assetTypes}}' y cuyo nombre comience por '{{prefix}}'", - "filter-type-device-type": "Tipo de dispositivo", - "filter-type-device-type-description": "Dispositivos de tipo '{{deviceTypes}}'", - "filter-type-device-type-and-name-description": "Dispositivos de tipo '{{deviceTypes}}' y cuyo nombre comience por '{{prefix}}'", - "filter-type-entity-view-type": "Tipo de vista de entidad", - "filter-type-entity-view-type-description": "Vistas de entidad del tipo '{{entityViewTypes}}'", - "filter-type-entity-view-type-and-name-description": "Vistas de entidad del tipo '{{entityViewTypes}}' y cuyo nombre comience por '{{prefix}}'", - "filter-type-edge-type": "Tipo de Edge", - "filter-type-edge-type-description": "Edges del tipo '{{edgeTypes}}'", - "filter-type-edge-type-and-name-description": "Vistas de entidad del tipo '{{edgeTypes}}' y cuyo nombre comience por '{{prefix}}'", - "filter-type-relations-query": "Consulta de relaciones", - "filter-type-relations-query-description": "{{entities}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", - "filter-type-asset-search-query": "Búsqueda de activos", - "filter-type-asset-search-query-description": "Activos con tipos {{assetTypes}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", - "filter-type-device-search-query": "Búsqueda de dispositivos", - "filter-type-device-search-query-description": "Dispositivos con tipos {{deviceTypes}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", - "filter-type-entity-view-search-query": "Consulta de búsqueda de vista de entidad", - "filter-type-entity-view-search-query-description": "Vistas de entidad con tipos {{entityViewTypes}} que tienen tipo de relación {{relationType}} con dirección {{direction}} {{rootEntity}}", - "filter-type-apiUsageState": "Uso de API", - "filter-type-edge-search-query": "Consultar búsqueda de Edge", - "filter-type-edge-search-query-description": "Edges con tipos {{edgeTypes}} que tienen {{relationType}} relación {{direction}} {{rootEntity}}", - "entity-filter": "Filtro de entidad", - "resolve-multiple": "Resolver como múltiples entidades", - "filter-type": "Tipo de filtro", - "filter-type-required": "Se requiere tipo de filtro.", - "entity-filter-no-entity-matched": "No se encontraron entidades que coincidan con el filtro especificado.", - "no-entity-filter-specified": "No se ha especificado filtro de entidad", - "root-state-entity": "Usar entidad de estado del panel como raíz", - "last-level-relation": "Obtener sólo el último nivel de relación", - "root-entity": "Entidad raíz", - "state-entity-parameter-name": "Parámetro de estado de entidad", - "default-state-entity": "Entidad de estado por defecto", - "default-entity-parameter-name": "Por defecto", - "max-relation-level": "Máximo nivel de relación", - "unlimited-level": "Nivel ilimitado", - "state-entity": "Entidad de estado del panel", - "all-entities": "Todas las entidades", - "any-relation": "cualquiera" + "resources": "Recursos", + "notifications": "Notificaciones", + "notifications-settings": "Configuración de notificaciones", + "slack-api-token": "Token de API de Slack", + "slack": "Slack", + "slack-settings": "Configuración de Slack", + "mobile-settings": "Configuración móvil", + "firebase-service-account-file": "Archivo JSON de credenciales de cuenta de servicio de Firebase", + "select-firebase-service-account-file": "Arrastra y suelta tu archivo de credenciales de cuenta de servicio de Firebase o " + }, + "alarm": { + "alarm": "Alarma", + "alarms": "Alarmas", + "all-alarms": "Todas las alarmas", + "select-alarm": "Seleccionar alarma", + "no-alarms-matching": "No se encontraron alarmas que coincidan con '{{entity}}'.", + "alarm-required": "Se requiere una alarma", + "alarm-filter": "Filtro de alarma", + "filter": "Filtro", + "alarm-status": "Estado de la alarma", + "alarm-status-list": "Lista de estados de alarma", + "any-status": "Cualquier estado", + "search-status": { + "ANY": "Cualquiera", + "ACTIVE": "Activa", + "CLEARED": "Despejada", + "ACK": "Reconocida", + "UNACK": "No reconocida" }, - "asset": { - "asset": "Activo", - "assets": "Activos", - "management": "Gestión de Activos", - "view-assets": "Ver Activos", - "add": "Añadir Activo", - "asset-type-max-length": "El tipo de activo debe ser menor de 256", - "assign-to-customer": "Asignar a cliente", - "assign-asset-to-customer": "Asignar Activo(s) A Cliente", - "assign-asset-to-customer-text": "Selecciona los activos a asignar al cliente", - "no-assets-text": "No se encontraron activos", - "assign-to-customer-text": "Selecciona el cliente al que asignar los activos", - "public": "Público", - "assignedToCustomer": "Asignado a cliente", - "make-public": "Hacer activo público", - "make-private": "Hacer activo privado", - "unassign-from-customer": "Cancelar la asignación de activo del cliente", - "delete": "Borrar activo", - "asset-public": "El activo es público", - "asset-type": "Tipo de activo", - "asset-type-required": "Se requiere tipo de activo.", - "select-asset-type": "Selecciona tipo de activo", - "enter-asset-type": "Entrar tipo de activo", - "any-asset": "Cualquier activo", - "no-asset-types-matching": "No se han encontrado activos coincidiendo con '{{entitySubtype}}' .", - "asset-type-list-empty": "No hay ningun tipo de activo seleccionado.", - "asset-types": "Tipos de activo", - "name": "Nombre", - "name-required": "Nombre requerido.", - "name-max-length": "El nombre debe tener menos de 256", - "label-max-length": "La etiqueta debe tener menos de 256", - "description": "Descripción", - "type": "Tipo", - "type-required": "Tipo requerido.", - "details": "Detalles", - "events": "Eventos", - "add-asset-text": "Añadir nuevo activo", - "asset-details": "Detalles de activo", - "assign-assets": "Asignar activos", - "assign-assets-text": "Asignar { count, plural, =1 {1 activo} other {# activos} } a cliente", - "assign-asset-to-edge-title": "Asignar activo(s) al Edge", - "assign-asset-to-edge-text": "Por favor, selecciona lo activos a asignar al Edge", - "delete-assets": "Borrar activos", - "unassign-assets": "Cancelar asignación de activo", - "unassign-assets-action-title": "Cancelar asignación de { count, plural, =1 {1 activo} other {# activos} } del cliente", - "assign-new-asset": "Asignar nuevo activo", - "delete-asset-title": "Eliminar el activo '{{assetName}}'?", - "delete-asset-text": "Atención, tras la confirmación el activo y sus datos serán borrados e irrecuperables.", - "delete-assets-title": "Eliminar los activos { count, plural, =1 {1 activo} other {# activos} }?", - "delete-assets-action-title": "Borrar { count, plural, =1 {1 activo} other {# activos} }", - "delete-assets-text": "Atención, tras la confirmación todos los activos seleccionados y sus datos serán borrados e irrecuperables.", - "make-public-asset-title": "Hacer el activo '{{assetName}}' público?", - "make-public-asset-text": "Tras la confirmación, el activo y sus datos se harán públicos y accesibles por otros.", - "make-private-asset-title": "Hacer el activo '{{assetName}}' privado?", - "make-private-asset-text": "Tras la confirmación, el activo y sus datos se harán privados y no serán accesibles por otros.", - "unassign-asset-title": "Cancelar la asignación del activo '{{assetName}}'?", - "unassign-asset-text": "Tras la confirmación, el activo será desasignado y no será accesible por el cliente.", - "unassign-asset": "Cancelar asignación de activo", - "unassign-assets-title": "Cancelar las asignaciones { count, plural, =1 {1 activo} other {# activos} }?", - "unassign-assets-text": "Tras la confirmación todos los activos seleccionados serán desasignados y no serán accesibles por el cliente.", - "unassign-assets-from-edge": "Anular activos de Edge", - "copyId": "Copiar ID de activo", - "idCopiedMessage": "El ID ha sido copiado al portapapeles", - "select-asset": "Seleccionar activo", - "no-assets-matching": "No se han encontrado activos que coincidan con '{{entity}}' .", - "asset-required": "Nombre de activo requerido", - "name-starts-with": "El nombre de activo comienza con", - "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%nombre_activo_contiene%', '%nombre_activo_acaba_en', 'nombre_activo_comienza_con'.", - "import": "Importar activos", - "asset-file": "Archivo del activo", - "label": "Etiqueta", - "search": "Buscar activos", - "assign-asset-to-edge": "Asignar activo(s) al Edge", - "unassign-asset-from-edge": "Anular activo de Edge", - "unassign-asset-from-edge-title": "¿Está seguro de que desea desasignar el activo '{{assetName}}'?", - "unassign-asset-from-edge-text": "Después de la confirmación, el activo no será asignado y el Edge no podrá acceder a él", - "unassign-assets-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, =1 {1 activo} other {# activos} }?", - "unassign-assets-from-edge-text": "Después de la confirmación, todos los activos seleccionados quedarán sin asignar y el Edge no podrá acceder a ellos.", - "selected-assets": "{ count, plural, =1 {1 activo} other {# activos} } seleccionados" + "display-status": { + "ACTIVE_UNACK": "Activa no reconocida", + "ACTIVE_ACK": "Activa reconocida", + "CLEARED_UNACK": "Despejada no reconocida", + "CLEARED_ACK": "Despejada reconocida" }, - "attribute": { - "attributes": "Atributos", - "latest-telemetry": "Última telemetría", - "no-latest-telemetry": "No hay última telemetría", - "attributes-scope": "Alcance de los atributos del dispositivo", - "scope-telemetry": "Telemetría", - "scope-latest-telemetry": "Última telemetría", - "scope-client": "Atributos de Cliente", - "scope-server": "Atributos de Servidor", - "scope-shared": "Atributos Compartidos", - "add": "Agregar atributo", - "key": "Clave", - "key-max-length": "La clave debe ser menor de 256", - "last-update-time": "Hora de última actualización", - "key-required": "Clave del atributo requerida.", - "value": "Valor", - "value-required": "Valor del atributo requerido.", - "telemetry-key-required": "Se requiere clave de telemetría", - "telemetry-value-required": "Se requiere valor Telemetry value is required", - "delete-attributes-title": "¿Eliminar { count, plural, =1 {1 atributo} other {# atributos} }?", - "delete-attributes-text": "Atención, tras la confirmación el atributo será eliminado, y la información relacionada será irrecuperable.", - "delete-attributes": "Borrar atributo", - "enter-attribute-value": "Ingresar valor del atributo", - "show-on-widget": "Mostrar en Widget", - "widget-mode": "Widget", - "next-widget": "Widget siguiente", - "prev-widget": "Widget anterior", - "add-to-dashboard": "Agregar al Panel", - "add-widget-to-dashboard": "Agregar widget al Panel", - "selected-attributes": "{ count, plural, =1 {1 atributo} other {# atributos} } seleccionados", - "selected-telemetry": "{ count, plural, =1 {1 telemetría} other {# telemetrías} } seleccionadas", - "no-attributes-text": "No se encontró ningún atributo", - "no-telemetry-text": "No se encontró ninguna telemetría", - "copy-key": "Copiar clave", - "add-telemetry": "Añadir telemetría", - "copy-value": "Copiar valor", - "delete-timeseries": { - "start-time": "Hora de inicio", - "ends-on": "Fin", - "strategy": "Estrategia", - "delete-strategy": "Estrategia de borrado", - "all-data": "Borrar todos los datos", - "all-data-except-latest-value": "Borrar todos los datos salvo los últimos valores", - "latest-value": "Borrar último valor", - "all-data-for-time-period": "Borrar todos los datos del periodo seleccionado", - "rewrite-latest-value": "Reescribir último valor" - } + "no-alarms-prompt": "No se encontraron alarmas", + "created-time": "Hora de creación", + "type": "Tipo", + "severity": "Severidad", + "originator": "Originador", + "originator-type": "Tipo de originador", + "details": "Detalles", + "originator-label": "Etiqueta del originador", + "assign": "Asignar", + "assignments": "Asignaciones", + "assignee": "Asignado", + "assignee-id": "ID del asignado", + "assignee-first-name": "Nombre del asignado", + "assignee-last-name": "Apellido del asignado", + "assignee-email": "Correo del asignado", + "unassigned": "No asignado", + "user-deleted": "Usuario eliminado", + "assignee-not-set": "Todos", + "status": "Estado", + "alarm-details": "Detalles de la alarma", + "start-time": "Hora de inicio", + "assign-time": "Hora de asignación", + "end-time": "Hora de finalización", + "ack-time": "Hora de reconocimiento", + "clear-time": "Hora de despeje", + "duration": "Duración", + "alarm-severity": "Severidad de la alarma", + "alarm-severity-list": "Lista de severidades de alarma", + "any-severity": "Cualquier severidad", + "severity-critical": "Crítica", + "severity-major": "Mayor", + "severity-minor": "Menor", + "severity-warning": "Advertencia", + "severity-indeterminate": "Indeterminada", + "acknowledge": "Reconocer", + "clear": "Despejar", + "delete": "Eliminar", + "search": "Buscar alarmas", + "selected-alarms": "{ count, plural, =1 {1 alarma} other {# alarmas} } seleccionadas", + "no-data": "No hay datos para mostrar", + "polling-interval": "Intervalo de sondeo de alarmas (seg)", + "polling-interval-required": "Se requiere el intervalo de sondeo de alarmas.", + "min-polling-interval-message": "Se permite un intervalo mínimo de sondeo de 1 segundo.", + "aknowledge-alarms-title": "Reconocer { count, plural, =1 {1 alarma} other {# alarmas} }", + "aknowledge-alarms-text": "¿Estás seguro de que deseas reconocer { count, plural, =1 {1 alarma} other {# alarmas} }?", + "aknowledge-alarm-title": "Reconocer alarma", + "aknowledge-alarm-text": "¿Estás seguro de que deseas reconocer la alarma?", + "selected-alarms-are-acknowledged": "Las alarmas seleccionadas ya están reconocidas", + "clear-alarms-title": "Despejar { count, plural, =1 {1 alarma} other {# alarmas} }", + "clear-alarms-text": "¿Estás seguro de que deseas despejar { count, plural, =1 {1 alarma} other {# alarmas} }?", + "clear-alarm-title": "Despejar alarma", + "clear-alarm-text": "¿Estás seguro de que deseas despejar la alarma?", + "delete-alarms-title": "Eliminar { count, plural, =1 {1 alarma} other {# alarmas} }", + "delete-alarms-text": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 alarma} other {# alarmas} }?", + "selected-alarms-are-cleared": "Las alarmas seleccionadas ya están despejadas", + "alarm-status-filter": "Filtro de estado de alarma", + "alarm-filter-title": "Filtro de alarma", + "assigned": "Asignado", + "filter-title": "Filtro", + "max-count-load": "Número máximo de alarmas a cargar (0 - ilimitado)", + "max-count-load-required": "Se requiere el número máximo de alarmas a cargar.", + "max-count-load-error-min": "El valor mínimo es 0.", + "fetch-size": "Tamaño de carga", + "fetch-size-required": "Se requiere el tamaño de carga.", + "fetch-size-error-min": "El valor mínimo es 10.", + "alarm-types": "Tipos de alarma", + "alarm-type-list": "Lista de tipos de alarma", + "any-type": "Cualquier tipo", + "assigned-to-current-user": "Asignadas al usuario actual", + "assigned-to-me": "Asignadas a mí", + "search-propagated-alarms": "Buscar alarmas propagadas", + "comments": "Comentarios de la alarma", + "show-more": "Mostrar más", + "additional-info": "Información adicional", + "alarm-type": "Tipo de alarma", + "enter-alarm-type": "Introducir tipo de alarma", + "no-alarm-types-matching": "No se encontraron tipos de alarma que coincidan con '{{entitySubtype}}'.", + "alarm-type-list-empty": "No se han seleccionado tipos de alarma." + }, + "alarm-activity": { + "add": "Agregar un comentario...", + "alarm-comment": "Comentario de la alarma", + "comments": "Comentarios", + "delete-alarm-comment": "¿Deseas eliminar este comentario?", + "refresh": "Actualizar", + "oldest-first": "Más antiguos primero", + "newest-first": "Más recientes primero", + "activity": "Actividad", + "export": "Exportar a CSV", + "author": "Autor", + "created-date": "Fecha de creación", + "edited-date": "Fecha de edición", + "text": "Texto", + "system": "Sistema" + }, + "alias": { + "add": "Agregar alias", + "edit": "Editar alias", + "name": "Nombre del alias", + "name-required": "Se requiere el nombre del alias", + "duplicate-alias": "Ya existe un alias con el mismo nombre.", + "filter-type-single-entity": "Entidad única", + "filter-type-entity-list": "Lista de entidades", + "filter-type-entity-name": "Nombre de la entidad", + "filter-type-entity-type": "Tipo de entidad", + "filter-type-state-entity": "Entidad del estado del tablero", + "filter-type-state-entity-description": "Entidad tomada de los parámetros del estado del tablero", + "filter-type-asset-type": "Tipo de activo", + "filter-type-asset-type-description": "Activos del tipo '{{assetTypes}}'", + "filter-type-asset-type-and-name-description": "Activos del tipo '{{assetTypes}}' con nombre que comienza con '{{prefix}}'", + "filter-type-device-type": "Tipo de dispositivo", + "filter-type-device-type-description": "Dispositivos del tipo '{{deviceTypes}}'", + "filter-type-device-type-and-name-description": "Dispositivos del tipo '{{deviceTypes}}' con nombre que comienza con '{{prefix}}'", + "filter-type-entity-view-type": "Tipo de vista de entidad", + "filter-type-entity-view-type-description": "Vistas de entidad del tipo '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description": "Vistas de entidad del tipo '{{entityViewTypes}}' con nombre que comienza con '{{prefix}}'", + "filter-type-edge-type": "Tipo de Edge", + "filter-type-edge-type-description": "Edges del tipo '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description": "Edges del tipo '{{edgeTypes}}' con nombre que comienza con '{{prefix}}'", + "filter-type-relations-query": "Consulta de relaciones", + "filter-type-relations-query-description": "{{entities}} que tienen una relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Consulta de búsqueda de Edge", + "filter-type-edge-search-query-description": "Edges de tipo {{edgeTypes}} que tienen una relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query": "Consulta de búsqueda de activos", + "filter-type-asset-search-query-description": "Activos de tipo {{assetTypes}} con relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query": "Consulta de búsqueda de dispositivos", + "filter-type-device-search-query-description": "Dispositivos de tipo {{deviceTypes}} con relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query": "Consulta de búsqueda de vistas de entidad", + "filter-type-entity-view-search-query-description": "Vistas de entidad de tipo {{entityViewTypes}} con relación de tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState": "Estado de uso de la API", + "entity-filter": "Filtro de entidad", + "resolve-multiple": "Resolver como múltiples entidades", + "resolve-multiple-hint": "Activa esta opción para mostrar datos de todas las entidades filtradas simultáneamente.\nSi se desactiva, el widget solo mostrará datos de la entidad seleccionada.", + "filter-type": "Tipo de filtro", + "filter-type-required": "Se requiere el tipo de filtro.", + "entity-filter-no-entity-matched": "No se encontraron entidades que coincidan con el filtro especificado.", + "no-entity-filter-specified": "No se especificó ningún filtro de entidad", + "root-state-entity": "Usar entidad del estado del tablero como raíz", + "last-level-relation": "Obtener solo relaciones del último nivel", + "root-entity": "Entidad raíz", + "state-entity-parameter-name": "Nombre del parámetro de entidad del estado", + "default-state-entity": "Entidad de estado predeterminada", + "default-entity-parameter-name": "Por defecto", + "max-relation-level": "Nivel máximo de relación", + "unlimited-level": "Nivel ilimitado", + "state-entity": "Entidad del estado del tablero", + "all-entities": "Todas las entidades", + "any-relation": "cualquiera" + }, + "asset": { + "asset": "Activo", + "assets": "Activos", + "management": "Gestión de activos", + "view-assets": "Ver activos", + "add": "Agregar activo", + "asset-type-max-length": "El tipo de activo debe tener menos de 256 caracteres", + "assign-to-customer": "Asignar a cliente", + "assign-asset-to-customer": "Asignar activo(s) al cliente", + "assign-asset-to-customer-text": "Selecciona los activos que deseas asignar al cliente", + "no-assets-text": "No se encontraron activos", + "assign-to-customer-text": "Selecciona el cliente al que deseas asignar el/los activo(s)", + "public": "Público", + "assignedToCustomer": "Asignado a cliente", + "make-public": "Hacer público el activo", + "make-private": "Hacer privado el activo", + "unassign-from-customer": "Desasignar del cliente", + "delete": "Eliminar activo", + "asset-public": "El activo es público", + "asset-type": "Tipo de activo", + "asset-type-required": "Se requiere el tipo de activo.", + "select-asset-type": "Seleccionar tipo de activo", + "enter-asset-type": "Introducir perfil de activo", + "any-asset": "Cualquier activo", + "no-asset-types-matching": "No se encontraron tipos de activo que coincidan con '{{entitySubtype}}'.", + "asset-type-list-empty": "No se seleccionaron tipos de activo.", + "asset-types": "Tipos de activo", + "name": "Nombre", + "name-required": "Se requiere el nombre.", + "name-max-length": "El nombre debe tener menos de 256 caracteres", + "label-max-length": "La etiqueta debe tener menos de 256 caracteres", + "description": "Descripción", + "type": "Tipo", + "type-required": "Se requiere el tipo.", + "details": "Detalles", + "events": "Eventos", + "add-asset-text": "Agregar nuevo activo", + "asset-details": "Detalles del activo", + "assign-assets": "Asignar activos", + "assign-assets-text": "Asignar { count, plural, =1 {1 activo} other {# activos} } al cliente", + "assign-asset-to-edge-title": "Asignar activo(s) a Edge", + "assign-asset-to-edge-text": "Selecciona los activos que deseas asignar a Edge", + "delete-assets": "Eliminar activos", + "unassign-assets": "Desasignar activos", + "unassign-assets-action-title": "Desasignar { count, plural, =1 {1 activo} other {# activos} } del cliente", + "assign-new-asset": "Asignar nuevo activo", + "delete-asset-title": "¿Estás seguro de que deseas eliminar el activo '{{assetName}}'?", + "delete-asset-text": "Ten cuidado, después de la confirmación el activo y todos los datos relacionados serán irrecuperables.", + "delete-assets-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 activo} other {# activos} }?", + "delete-assets-action-title": "Eliminar { count, plural, =1 {1 activo} other {# activos} }", + "delete-assets-text": "Ten cuidado, después de la confirmación todos los activos seleccionados se eliminarán y todos los datos relacionados serán irrecuperables.", + "make-public-asset-title": "¿Estás seguro de que deseas hacer público el activo '{{assetName}}'?", + "make-public-asset-text": "Después de la confirmación, el activo y todos sus datos serán públicos y accesibles por otros.", + "make-private-asset-title": "¿Estás seguro de que deseas hacer privado el activo '{{assetName}}'?", + "make-private-asset-text": "Después de la confirmación, el activo y todos sus datos serán privados y no serán accesibles por otros.", + "unassign-asset-title": "¿Estás seguro de que deseas desasignar el activo '{{assetName}}'?", + "unassign-asset-text": "Después de la confirmación, el activo se desasignará y no será accesible por el cliente.", + "unassign-asset": "Desasignar activo", + "unassign-assets-title": "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 activo} other {# activos} }?", + "unassign-assets-text": "Después de la confirmación, todos los activos seleccionados se desasignarán y no serán accesibles por el cliente.", + "copyId": "Copiar ID del activo", + "idCopiedMessage": "El ID del activo ha sido copiado al portapapeles", + "select-asset": "Seleccionar activo", + "no-assets-matching": "No se encontraron activos que coincidan con '{{entity}}'.", + "asset-required": "Se requiere un activo", + "name-starts-with": "Expresión del nombre del activo", + "help-text": "Usa '%' según sea necesario: '%nombre_activo_contiene%', '%nombre_activo_termina', 'activo_empieza_con'.", + "search": "Buscar activos", + "import": "Importar activos", + "asset-file": "Archivo de activos", + "label": "Etiqueta", + "assign-asset-to-edge": "Asignar activo(s) a Edge", + "unassign-asset-from-edge": "Desasignar activo", + "unassign-asset-from-edge-title": "¿Estás seguro de que deseas desasignar el activo '{{assetName}}'?", + "unassign-asset-from-edge-text": "Después de la confirmación, el activo se desasignará y no será accesible por Edge.", + "unassign-assets-from-edge-title": "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 activo} other {# activos} }?", + "unassign-assets-from-edge-text": "Después de la confirmación, todos los activos seleccionados se desasignarán y no serán accesibles por Edge.", + "selected-assets": "{ count, plural, =1 {1 activo} other {# activos} } seleccionado" + }, + "attribute": { + "attributes": "Atributos", + "latest-telemetry": "Última telemetría", + "no-latest-telemetry": "No se encontró última telemetría", + "attributes-scope": "Alcance de atributos de entidad", + "scope-telemetry": "Telemetría", + "scope-latest-telemetry": "Última telemetría", + "scope-client": "Atributos del cliente", + "scope-server": "Atributos del servidor", + "scope-shared": "Atributos compartidos", + "scope-client-short": "Cliente", + "scope-server-short": "Servidor", + "scope-shared-short": "Compartido", + "scope-latest-short": "Última", + "scope-any": "Cualquiera", + "add": "Agregar atributo", + "key": "Clave", + "key-max-length": "La clave debe tener menos de 256 caracteres", + "last-update-time": "Última hora de actualización", + "key-required": "Se requiere la clave del atributo.", + "value": "Valor", + "value-required": "Se requiere el valor del atributo.", + "telemetry-key-required": "Se requiere la clave de telemetría", + "telemetry-value-required": "Se requiere el valor de telemetría", + "delete-attributes-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 atributo} other {# atributos} }?", + "delete-attributes-text": "Ten cuidado, después de la confirmación todos los atributos seleccionados se eliminarán.", + "delete-attributes": "Eliminar atributos", + "enter-attribute-value": "Ingresar valor del atributo", + "show-on-widget": "Mostrar en el widget", + "widget-mode": "Modo de widget", + "next-widget": "Siguiente widget", + "prev-widget": "Widget anterior", + "add-to-dashboard": "Agregar al tablero", + "add-widget-to-dashboard": "Agregar widget al tablero", + "selected-attributes": "{ count, plural, =1 {1 atributo} other {# atributos} } seleccionado", + "selected-telemetry": "{ count, plural, =1 {1 unidad de telemetría} other {# unidades de telemetría} } seleccionada", + "no-attributes-text": "No se encontraron atributos", + "no-telemetry-text": "No se encontró telemetría", + "copy-key": "Copiar clave", + "add-telemetry": "Agregar telemetría", + "copy-value": "Copiar valor", + "delete-timeseries": { + "start-time": "Hora de inicio", + "ends-on": "Finaliza en", + "strategy": "Estrategia", + "delete-strategy": "Estrategia de eliminación", + "all-data": "Eliminar todos los datos", + "all-data-except-latest-value": "Eliminar todos los datos excepto el último valor", + "latest-value": "Eliminar el último valor", + "all-data-for-time-period": "Eliminar todos los datos del período de tiempo", + "rewrite-latest-value": "Reescribir el último valor" + } + }, + "api-usage": { + "api-features": "Características de la API", + "api-usage": "Uso de la API", + "alarm": "Alarma", + "alarms-created": "Alarmas creadas", + "queue-stats": "Estadísticas de la cola", + "processing-failures-and-timeouts": "Fallos y tiempos de espera en el procesamiento", + "exceptions": "Excepciones", + "alarms-created-daily-activity": "Actividad diaria de alarmas creadas", + "alarms-created-hourly-activity": "Actividad por hora de alarmas creadas", + "alarms-created-monthly-activity": "Actividad mensual de alarmas creadas", + "data-points": "Puntos de datos", + "data-points-storage-days": "Días de almacenamiento de puntos de datos", + "device-api": "API de dispositivo", + "email": "Correo electrónico", + "email-messages": "Mensajes de correo", + "email-messages-daily-activity": "Actividad diaria de mensajes de correo", + "email-messages-monthly-activity": "Actividad mensual de mensajes de correo", + "executions": "Ejecuciones", + "scripts": "Scripts", + "scripts-hourly-activity": "Actividad por hora de scripts", + "scripts-daily-activity": "Actividad diaria de scripts", + "scripts-monthly-activity": "Actividad mensual de scripts", + "javascript": "JavaScript", + "javascript-executions": "Ejecuciones de JavaScript", + "tbel": "TBEL", + "tbel-executions": "Ejecuciones de TBEL", + "latest-error": "Último error", + "messages": "Mensajes", + "notifications": "Notificaciones", + "notifications-email-sms": "Notificaciones (Correo/SMS)", + "notifications-hourly-activity": "Actividad por hora de notificaciones", + "permanent-failures": "Fallos permanentes de ${entityName}", + "permanent-timeouts": "Tiempos de espera permanentes de ${entityName}", + "processing-failures": "Fallos de procesamiento de ${entityName}", + "processing-timeouts": "Tiempos de espera de procesamiento de ${entityName}", + "rule-chain": "Cadena de reglas", + "rule-engine": "Motor de reglas", + "rule-engine-daily-activity": "Actividad diaria del motor de reglas", + "rule-engine-executions": "Ejecuciones del motor de reglas", + "rule-engine-hourly-activity": "Actividad por hora del motor de reglas", + "rule-engine-monthly-activity": "Actividad mensual del motor de reglas", + "rule-engine-statistics": "Estadísticas del motor de reglas", + "rule-node": "Nodo de regla", + "sms": "SMS", + "sms-messages": "Mensajes SMS", + "sms-messages-daily-activity": "Actividad diaria de mensajes SMS", + "sms-messages-monthly-activity": "Actividad mensual de mensajes SMS", + "successful": "${entityName} exitoso", + "telemetry": "Telemetría", + "telemetry-persistence": "Persistencia de telemetría", + "telemetry-persistence-daily-activity": "Actividad diaria de persistencia de telemetría", + "telemetry-persistence-hourly-activity": "Actividad por hora de persistencia de telemetría", + "telemetry-persistence-monthly-activity": "Actividad mensual de persistencia de telemetría", + "transport": "Transporte", + "transport-daily-activity": "Actividad diaria de transporte", + "transport-data-points": "Puntos de datos de transporte", + "transport-hourly-activity": "Actividad por hora de transporte", + "transport-messages": "Mensajes de transporte", + "transport-monthly-activity": "Actividad mensual de transporte", + "view-details": "Ver detalles", + "view-statistics": "Ver estadísticas" + }, + "api-limit": { + "cassandra-queries": "Consultas Cassandra", + "entity-version-creation": "Creación de versión de entidad", + "entity-version-load": "Carga de versión de entidad", + "notification-requests": "Solicitudes de notificación", + "notification-requests-per-rule": "Solicitudes de notificación por regla", + "rest-api-requests": "Solicitudes de API REST", + "rest-api-requests-per-customer": "Solicitudes de API REST por cliente", + "transport-messages": "Mensajes de transporte", + "transport-messages-per-device": "Mensajes de transporte por dispositivo", + "transport-messages-per-gateway": "Mensajes de transporte por gateway", + "transport-messages-per-gateway-device": "Mensajes de transporte por dispositivo de gateway", + "ws-updates-per-session": "Actualizaciones WS por sesión", + "edge-events": "Eventos de Edge", + "edge-events-per-edge": "Eventos de Edge por Edge", + "edge-uplink-messages": "Mensajes uplink de Edge", + "edge-uplink-messages-per-edge": "Mensajes uplink de Edge por Edge" + }, + "audit-log": { + "audit": "Auditoría", + "audit-logs": "Registros de auditoría", + "timestamp": "Marca de tiempo", + "entity-type": "Tipo de entidad", + "entity-name": "Nombre de la entidad", + "user": "Usuario", + "type": "Tipo", + "status": "Estado", + "details": "Detalles", + "type-added": "Agregado", + "type-deleted": "Eliminado", + "type-updated": "Actualizado", + "type-attributes-updated": "Atributos actualizados", + "type-attributes-deleted": "Atributos eliminados", + "type-rpc-call": "Llamada RPC", + "type-credentials-updated": "Credenciales actualizadas", + "type-assigned-to-customer": "Asignado al cliente", + "type-unassigned-from-customer": "Desasignado del cliente", + "type-assigned-to-edge": "Asignado a Edge", + "type-unassigned-from-edge": "Desasignado de Edge", + "type-activated": "Activado", + "type-suspended": "Suspendido", + "type-credentials-read": "Credenciales leídas", + "type-attributes-read": "Atributos leídos", + "type-relation-add-or-update": "Relación actualizada", + "type-relation-delete": "Relación eliminada", + "type-relations-delete": "Todas las relaciones eliminadas", + "type-alarm-ack": "Alarma reconocida", + "type-alarm-clear": "Alarma despejada", + "type-alarm-delete": "Alarma eliminada", + "type-alarm-assign": "Alarma asignada", + "type-alarm-unassign": "Alarma desasignada", + "type-added-comment": "Comentario agregado", + "type-updated-comment": "Comentario actualizado", + "type-deleted-comment": "Comentario eliminado", + "type-login": "Inicio de sesión", + "type-logout": "Cierre de sesión", + "type-lockout": "Bloqueo", + "status-success": "Éxito", + "status-failure": "Fallo", + "audit-log-details": "Detalles del registro de auditoría", + "no-audit-logs-prompt": "No se encontraron registros", + "action-data": "Datos de la acción", + "failure-details": "Detalles del fallo", + "search": "Buscar registros de auditoría", + "clear-search": "Borrar búsqueda", + "type-assigned-from-tenant": "Asignado desde inquilino", + "type-assigned-to-tenant": "Asignado a inquilino", + "type-provision-success": "Dispositivo aprovisionado", + "type-provision-failure": "El aprovisionamiento del dispositivo falló", + "type-timeseries-updated": "Telemetría actualizada", + "type-timeseries-deleted": "Telemetría eliminada", + "type-sms-sent": "SMS enviado" + }, + "debug-settings": { + "label": "Configuración de depuración", + "on-failure": "Solo fallos (24/7)", + "all-messages": "Todos los mensajes ({{time}})", + "failures": "Fallos", + "entity": "entidad", + "hint": { + "main-limited": "No se registrarán más de {{msg}} mensajes de depuración de {{entity}} por {{time}}.", + "on-failure": "Registrar solo mensajes de error.", + "all-messages": "Registrar todos los mensajes de depuración." + } + }, + "calculated-fields": { + "expression": "Expresión", + "no-found": "No se encontraron campos calculados", + "list": "{ count, plural, =1 {Un campo calculado} other {Lista de # campos calculados} }", + "selected-fields": "{ count, plural, =1 {1 campo calculado} other {# campos calculados} } seleccionado(s)", + "type": { + "simple": "Simple", + "script": "Script" }, - "api-usage": { - "api-features": "Características API", - "api-usage": "Uso de API", - "alarm": "Alarma", - "alarms-created": "Alarmas creadas", - "alarms-created-daily-activity": "Actividad diaria de Alarmas creadas", - "alarms-created-hourly-activity": "Actividad horaria de Alarmas creadas", - "alarms-created-monthly-activity": "Actividad mensual de Alarmas creadas", - "data-points": "Puntos de datos", - "data-points-storage-days": "Días de guardado de puntos de datos", - "device-api": "API Dispositivos", - "email": "Email", - "email-messages": "Mensajes de Email", - "email-messages-daily-activity": "Actividad diaria de Emails", - "email-messages-monthly-activity": "Actividad mensual de Emails", - "exceptions": "Excepciones", - "executions": "Ejecuciones", - "javascript": "JavaScript", - "javascript-executions": "Ejecuciones JavaScript", - "latest-error": "Último error", - "messages": "Mensajes", - "notifications": "Notificaciones", - "notifications-email-sms": "Notificaciones (Email/SMS)", - "notifications-hourly-activity": "Notificaciones actividad horaria", - "permanent-failures": "${entityName} Fallos permanentes", - "permanent-timeouts": "${entityName} Timeouts permanentes", - "processing-failures": "${entityName} Fallos de procesamiento", - "processing-failures-and-timeouts": "Fallos de procesamiento y timeouts", - "processing-timeouts": "${entityName} Timeouts de procesamiento", - "queue-stats": "Estadísticas de colas", - "rule-chain": "Cadena de reglas", - "rule-engine": "Motor de reglas", - "rule-engine-daily-activity": "Actividad diaria de motor de reglas", - "rule-engine-executions": "Ejecuciones de motor de reglas", - "rule-engine-hourly-activity": "Actividad horaria de motor de reglas", - "rule-engine-monthly-activity": "Actividad mensual de motor de reglas", - "rule-engine-statistics": "Estadisticas del motor de reglas", - "rule-node": "Nodo de reglas", - "sms": "SMS", - "sms-messages": "Mensajes SMS", - "sms-messages-daily-activity": "Actividad diaria de mensajes SMS", - "sms-messages-monthly-activity": "Actividad mensual de mensajes SMS", - "successful": "${entityName} Exitoso", - "telemetry": "Telemetría", - "telemetry-persistence": "Persistencia de telemetría", - "telemetry-persistence-daily-activity": "Actividad diaria de persistencia de telemetría", - "telemetry-persistence-hourly-activity": "Actividad horaria de persistencia de telemetría", - "telemetry-persistence-monthly-activity": "Actividad mensual de persistencia de telemetría", - "transport": "Transporte", - "transport-daily-activity": "Actividad diaria de transporte", - "transport-data-points": "Puntos de datos de transporte", - "transport-hourly-activity": "Actividad horaria de transporte", - "transport-messages": "Mensajes de transporte", - "transport-monthly-activity": "Actividad mensual de transporte", - "view-details": "Ver detalles", - "view-statistics": "Ver estadísticas" + "arguments": "Argumentos", + "decimals-by-default": "Decimales por defecto", + "debugging": "Depuración de campos calculados", + "argument-name": "Nombre del argumento", + "datasource": "Origen de datos", + "add-argument": "Agregar argumento", + "test-script-function": "Probar función de script", + "no-arguments": "No hay argumentos configurados", + "argument-settings": "Configuración del argumento", + "argument-current": "Entidad actual", + "argument-current-tenant": "Inquilino actual", + "argument-device": "Dispositivo", + "argument-asset": "Activo", + "argument-customer": "Cliente", + "argument-tenant": "Inquilino actual", + "argument-type": "Tipo de argumento", + "see-debug-events": "Ver eventos de depuración", + "attribute": "Atributo", + "copy-argument-name": "Copiar nombre del argumento", + "timeseries-key": "Clave de serie temporal", + "device-name": "Nombre del dispositivo", + "latest-telemetry": "Última telemetría", + "rolling": "Acumulación de serie temporal", + "attribute-scope": "Ámbito del atributo", + "server-attributes": "Atributos del servidor", + "client-attributes": "Atributos del cliente", + "shared-attributes": "Atributos compartidos", + "attribute-key": "Clave del atributo", + "default-value": "Valor por defecto", + "limit": "Máx. valores", + "time-window": "Ventana de tiempo", + "customer-name": "Nombre del cliente", + "asset-name": "Nombre del activo", + "timeseries": "Serie temporal", + "output": "Salida", + "create": "Crear nuevo campo calculado", + "file": "Archivo de campo calculado", + "invalid-file-error": "Formato de archivo inválido. Asegúrate de que el archivo sea un JSON válido.", + "import": "Importar campo calculado", + "export": "Exportar campo calculado", + "export-failed-error": "No se pudo exportar el campo calculado: {{error}}", + "output-type": "Tipo de salida", + "delete-title": "¿Estás seguro de que deseas eliminar el campo calculado '{{title}}'?", + "delete-text": "Ten cuidado, después de la confirmación el campo calculado y todos los datos relacionados serán irrecuperables.", + "delete-multiple-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 campo calculado} other {# campos calculados} }?", + "delete-multiple-text": "Ten cuidado, después de la confirmación todos los campos calculados seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "test-with-this-message": "Probar con este mensaje", + "hint": { + "arguments-simple-with-rolling": "Un campo calculado de tipo simple no debe contener claves con tipo de serie temporal acumulativa.", + "arguments-empty": "Los argumentos no deben estar vacíos.", + "expression-required": "Se requiere una expresión.", + "expression-invalid": "La expresión es inválida", + "expression-max-length": "La longitud de la expresión debe ser menor a 255 caracteres.", + "argument-name-required": "Se requiere el nombre del argumento.", + "argument-name-pattern": "El nombre del argumento es inválido.", + "argument-name-duplicate": "Ya existe un argumento con ese nombre.", + "argument-name-max-length": "El nombre del argumento debe tener menos de 256 caracteres.", + "argument-name-forbidden": "El nombre del argumento está reservado y no puede ser utilizado.", + "argument-type-required": "Se requiere el tipo de argumento.", + "max-args": "Se alcanzó el número máximo de argumentos.", + "decimals-range": "Los decimales por defecto deben ser un número entre 0 y 15.", + "expression": "La expresión por defecto muestra cómo transformar una temperatura de Fahrenheit a Celsius.", + "arguments-entity-not-found": "No se encontró la entidad objetivo del argumento." + } + }, + "confirm-on-exit": { + "message": "Tienes cambios sin guardar. ¿Estás seguro de que deseas salir de esta página?", + "html-message": "Tienes cambios sin guardar.
¿Estás seguro de que deseas salir de esta página?", + "title": "Cambios sin guardar" + }, + "contact": { + "country": "País", + "country-required": "Se requiere el país.", + "city": "Ciudad", + "state": "Estado / Provincia", + "postal-code": "Código postal", + "postal-code-invalid": "Formato de código postal inválido.", + "address": "Dirección", + "address2": "Dirección 2", + "phone": "Teléfono", + "email": "Correo electrónico", + "no-address": "Sin dirección", + "no-country-found": "No se encontraron países.", + "no-country-matching": "No se encontraron países que coincidan con '{{country}}'.", + "state-max-length": "La longitud del estado debe ser menor a 256", + "phone-max-length": "El número de teléfono debe tener menos de 256 caracteres", + "city-max-length": "La ciudad especificada debe tener menos de 256 caracteres" + }, + "common": { + "name": "Nombre", + "type": "Tipo", + "general": "General", + "username": "Nombre de usuario", + "password": "Contraseña", + "data": "Datos", + "timestamp": "Marca de tiempo", + "enter-username": "Introducir nombre de usuario", + "enter-password": "Introducir contraseña", + "enter-search": "Introducir búsqueda", + "created-time": "Hora de creación", + "disabled": "Deshabilitado", + "loading": "Cargando...", + "proceed": "Continuar", + "open-details-page": "Abrir página de detalles", + "not-found": "No encontrado", + "value": "Valor", + "documentation": "Documentación", + "time-left": "{{time}} restante", + "output": "Salida", + "suffix": { + "s": "s", + "ms": "ms" }, - "api-limit": { - "cassandra-queries": "Consultas Cassandra", - "entity-version-creation": "Creación de versiones de entidad", - "entity-version-load": "Carga de versiones de entidad", - "notification-requests": "Solicitudes de notificación", - "notification-requests-per-rule": "Solicitudes de notificación por regla", - "rest-api-requests": "Solicitudes REST API", - "rest-api-requests-per-customer": "Solicitudes REST API por cliente", - "transport-messages": "Mensajes de transporte", - "transport-messages-per-device": "Mensajes de transporte por dispositivo", - "ws-updates-per-session": "Actualizaciones WS por sesión" + "hint": { + "name-required": "Se requiere el nombre.", + "name-pattern": "El nombre no es válido.", + "name-max-length": "El nombre debe tener menos de 256 caracteres.", + "title-required": "Se requiere el título.", + "title-pattern": "El título no es válido.", + "title-max-length": "El título debe tener menos de 256 caracteres.", + "key-required": "Se requiere la clave.", + "key-pattern": "La clave no es válida.", + "key-max-length": "La clave debe tener menos de 256 caracteres." }, - "audit-log": { - "audit": "Auditoría", - "audit-logs": "Registro Auditoría", - "timestamp": "Timestamp", - "entity-type": "Tipo Entidad", - "entity-name": "Nombre Entidad", - "user": "Usuario", - "type": "Tipo", - "status": "Estado", - "details": "Detalles", - "type-added": "Añadido", - "type-deleted": "Borrado", - "type-updated": "Actualizado", - "type-attributes-updated": "Atributos actualizados", - "type-attributes-deleted": "Atributos borrados", - "type-rpc-call": "Llamada RPC", - "type-credentials-updated": "Credenciales actualizados", - "type-assigned-to-customer": "Asignado a Cliente", - "type-unassigned-from-customer": "Deasignado a Cliente", - "type-assigned-to-edge": "Asignado a Edge", - "type-unassigned-from-edge": "Deasignado de Edge", - "type-activated": "Activado", - "type-suspended": "Suspendido", - "type-credentials-read": "Lectura de credenciales", - "type-attributes-read": "Lectura de atributos", - "type-relation-add-or-update": "Relación actualizada", - "type-relation-delete": "Relación borrada", - "type-relations-delete": "Borradas todas las relaciones", - "type-alarm-ack": "Alarma Acusada", - "type-alarm-clear": "Alarma Limpiada", - "type-alarm-assign": "Asignada", - "type-alarm-unassign": "Deasignado", - "type-added-comment": "Comentario añadido", - "type-updated-comment": "Comentario actualizado", - "type-deleted-comment": "Comentario borrado", - "type-login": "Inicio de sesión", - "type-logout": "Cierre de sesión", - "type-lockout": "Cierre por bloqueo", - "status-success": "Éxito", - "status-failure": "Fallo", - "audit-log-details": "Detalle del registro de auditoría", - "no-audit-logs-prompt": "No se encontraron registros", - "action-data": "Datos de acción", - "failure-details": "Detalles del error", - "search": "Buscar registros de auditoría", - "clear-search": "Borrar búsqueda", - "type-assigned-from-tenant": "Asignado desde el administrador", - "type-assigned-to-tenant": "Asignado al administrador", - "type-provision-success": "Dispositivo aprovisionado", - "type-provision-failure": "Aprovisionamiento fallido", - "type-timeseries-updated": "Telemetría actualizada", - "type-timeseries-deleted": "Telemetría borrada", - "type-sms-sent": "SMS enviado" + "required-fields": "Faltan campos obligatorios" + }, + "content-type": { + "json": "Json", + "text": "Texto", + "binary": "Binario (Base64)" + }, + "color": { + "color": "Color" + }, + "customer": { + "customer": "Cliente", + "customers": "Clientes", + "management": "Gestión de clientes", + "dashboard": "Tablero del cliente", + "dashboards": "Tableros del cliente", + "devices": "Dispositivos del cliente", + "entity-views": "Vistas de entidad del cliente", + "assets": "Activos del cliente", + "public-dashboards": "Tableros públicos", + "public-devices": "Dispositivos públicos", + "public-assets": "Activos públicos", + "public-entity-views": "Vistas de entidad públicas", + "add": "Agregar cliente", + "delete": "Eliminar cliente", + "manage-customer-users": "Gestionar usuarios del cliente", + "manage-customer-devices": "Gestionar dispositivos del cliente", + "manage-customer-dashboards": "Gestionar tableros del cliente", + "manage-public-devices": "Gestionar dispositivos públicos", + "manage-public-dashboards": "Gestionar tableros públicos", + "manage-customer-assets": "Gestionar activos del cliente", + "manage-customer-edges": "Gestionar edge del cliente", + "manage-public-assets": "Gestionar activos públicos", + "add-customer-text": "Agregar nuevo cliente", + "no-customers-text": "No se encontraron clientes", + "customer-details": "Detalles del cliente", + "delete-customer-title": "¿Estás seguro de que deseas eliminar el cliente '{{customerTitle}}'?", + "delete-customer-text": "Ten cuidado, después de la confirmación el cliente y todos los datos relacionados serán irrecuperables.", + "delete-customers-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 cliente} other {# clientes} }?", + "delete-customers-action-title": "Eliminar { count, plural, =1 {1 cliente} other {# clientes} }", + "delete-customers-text": "Ten cuidado, después de la confirmación todos los clientes seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "manage-users": "Gestionar usuarios", + "manage-assets": "Gestionar activos", + "manage-devices": "Gestionar dispositivos", + "manage-dashboards": "Gestionar tableros", + "title": "Título", + "title-required": "Se requiere el título.", + "title-max-length": "El título debe tener menos de 256 caracteres", + "description": "Descripción", + "details": "Detalles", + "events": "Eventos", + "copyId": "Copiar ID del cliente", + "idCopiedMessage": "ID del cliente copiado al portapapeles", + "select-customer": "Seleccionar cliente", + "no-customers-matching": "No se encontraron clientes que coincidan con '{{entity}}'.", + "customer-required": "Se requiere un cliente", + "select-default-customer": "Seleccionar cliente por defecto", + "default-customer": "Cliente por defecto", + "default-customer-required": "Se requiere el cliente por defecto para depurar el tablero a nivel de inquilino", + "search": "Buscar clientes", + "selected-customers": "{ count, plural, =1 {1 cliente} other {# clientes} } seleccionado(s)", + "edges": "Instancias de edge del cliente", + "manage-edges": "Gestionar edge" + }, + "css-size": { + "size-value-required": "Se requiere un valor de tamaño", + "invalid-size-value": "Valor de tamaño inválido" + }, + "date": { + "last-update-n-ago": "Última actualización hace N", + "last-update-n-ago-text": "Última actualización hace {{ agoText }}", + "custom-date": "Fecha personalizada", + "format": "Formato", + "preview": "Vista previa", + "auto": "Auto", + "time-granularity-formats": "Formatos de granularidad de tiempo", + "unit-year": "Años", + "unit-month": "Meses", + "unit-day": "Días", + "unit-hour": "Horas", + "unit-minute": "Minutos", + "unit-second": "Segundos", + "unit-millisecond": "Milisegundos" + }, + "datetime": { + "date-from": "Fecha desde", + "time-from": "Hora desde", + "date-to": "Fecha hasta", + "time-to": "Hora hasta", + "from": "Desde", + "to": "Hasta" + }, + "dashboard": { + "dashboard": "Tablero", + "dashboards": "Tableros", + "management": "Gestión de tableros", + "view-dashboards": "Ver tableros", + "add": "Añadir tablero", + "assign-dashboard-to-customer": "Asignar tablero(s) al cliente", + "assign-dashboard-to-customer-text": "Por favor, seleccione los tableros para asignar al cliente", + "assign-to-customer-text": "Por favor, seleccione el cliente para asignar el/los tablero(s)", + "assign-to-customer": "Asignar al cliente", + "unassign-from-customer": "Desasignar del cliente", + "make-public": "Hacer público el tablero", + "make-private": "Hacer privado el tablero", + "manage-assigned-customers": "Gestionar clientes asignados", + "assigned-customers": "Clientes asignados", + "assign-to-customers": "Asignar tablero(s) a clientes", + "assign-to-customers-text": "Selecciona los clientes a los que deseas asignar el/los tablero(s)", + "unassign-from-customers": "Desasignar tablero(s) de clientes", + "unassign-from-customers-text": "Selecciona los clientes de los que deseas desasignar el/los tablero(s)", + "no-dashboards-text": "No se encontraron tableros", + "no-widgets": "No hay widgets configurados", + "add-widget": "Agregar nuevo widget", + "add-widget-button-text": "Agregar widget", + "title": "Título", + "image": "Imagen del tablero", + "mobile-app-settings": "Configuración de la aplicación móvil", + "mobile-order": "Orden del tablero en la aplicación móvil", + "mobile-hide": "Ocultar tablero en la aplicación móvil", + "update-image": "Actualizar imagen del tablero", + "take-screenshot": "Tomar captura de pantalla", + "select-widget-title": "Seleccionar widget", + "select-widget-value": "{{title}}: seleccionar widget", + "select-widget-subtitle": "Lista de tipos de widget disponibles", + "delete": "Eliminar tablero", + "title-required": "Se requiere el título.", + "title-max-length": "El título debe tener menos de 256 caracteres", + "description": "Descripción", + "details": "Detalles", + "dashboard-details": "Detalles del tablero", + "add-dashboard-text": "Agregar nuevo tablero", + "assign-dashboards": "Asignar tableros", + "assign-new-dashboard": "Asignar nuevo tablero", + "assign-dashboards-text": "Asignar { count, plural, =1 {1 tablero} other {# tableros} } a clientes", + "unassign-dashboards-action-text": "Desasignar { count, plural, =1 {1 tablero} other {# tableros} } de clientes", + "delete-dashboards": "Eliminar tableros", + "unassign-dashboards": "Desasignar tableros", + "unassign-dashboards-action-title": "Desasignar { count, plural, =1 {1 tablero} other {# tableros} } del cliente", + "delete-dashboard-title": "¿Estás seguro de que deseas eliminar el tablero '{{dashboardTitle}}'?", + "delete-dashboard-text": "Ten cuidado, después de la confirmación el tablero y todos los datos relacionados serán irrecuperables.", + "delete-dashboards-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 tablero} other {# tableros} }?", + "delete-dashboards-action-title": "Eliminar { count, plural, =1 {1 tablero} other {# tableros} }", + "delete-dashboards-text": "Ten cuidado, después de la confirmación todos los tableros seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "unassign-dashboard-title": "¿Estás seguro de que deseas desasignar el tablero '{{dashboardTitle}}'?", + "unassign-dashboard-text": "Después de la confirmación, el tablero será desasignado y no será accesible por el cliente.", + "unassign-dashboard": "Desasignar tablero", + "unassign-dashboards-title": "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 tablero} other {# tableros} }?", + "unassign-dashboards-text": "Después de la confirmación, todos los tableros seleccionados serán desasignados y no serán accesibles por el cliente.", + "public-dashboard-title": "El tablero ahora es público", + "public-dashboard-text": "Tu tablero {{dashboardTitle}} ahora es público y accesible mediante el siguiente enlace:", + "public-dashboard-notice": "Nota: No olvides hacer públicos los dispositivos relacionados para acceder a sus datos.", + "make-private-dashboard-title": "¿Estás seguro de que deseas hacer privado el tablero '{{dashboardTitle}}'?", + "make-private-dashboard-text": "Después de la confirmación, el tablero será privado y no será accesible por otros.", + "make-private-dashboard": "Hacer privado el tablero", + "socialshare-text": "'{{dashboardTitle}}' impulsado por ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' impulsado por ThingsBoard", + "select-dashboard": "Seleccionar tablero", + "no-dashboards-matching": "No se encontraron tableros que coincidan con '{{entity}}'.", + "dashboard-required": "Se requiere un tablero.", + "select-existing": "Seleccionar tablero existente", + "create-new": "Crear nuevo tablero", + "new-dashboard-title": "Título del nuevo tablero", + "open-dashboard": "Abrir tablero", + "set-background": "Establecer fondo", + "background-color": "Color de fondo", + "background-image": "Imagen de fondo", + "background-size-mode": "Modo de tamaño del fondo", + "no-image": "Ninguna imagen seleccionada", + "empty-image": "Sin imagen", + "drop-image": "Suelta una imagen o haz clic para seleccionar un archivo para subir.", + "maximum-upload-file-size": "Tamaño máximo del archivo de carga: {{ size }}", + "cannot-upload-file": "No se puede cargar el archivo", + "settings": "Configuraciones", + "move-all-widgets": "Mover todos los widgets", + "move-by": "Mover por", + "cols": "columnas", + "rows": "filas", + "layout": "Disposición", + "layout-type-default": "Predeterminado", + "layout-type-scada": "SCADA", + "layout-type-divider": "Divisor", + "layout-settings-type": "Configuración de disposición: punto de interrupción {{ type }}", + "columns-count": "Cantidad de columnas", + "columns-count-required": "Se requiere la cantidad de columnas.", + "min-columns-count-message": "Solo se permite un mínimo de 10 columnas.", + "max-columns-count-message": "Solo se permite un máximo de 1000 columnas.", + "min-layout-width": "Ancho mínimo de disposición", + "columns-suffix": "columnas", + "widgets-margins": "Margen entre widgets", + "margin-required": "Se requiere un valor de margen.", + "min-margin-message": "Solo se permite 0 como valor mínimo de margen.", + "max-margin-message": "Solo se permite 50 como valor máximo de margen.", + "horizontal-margin": "Margen horizontal", + "horizontal-margin-required": "Se requiere el valor del margen horizontal.", + "min-horizontal-margin-message": "Solo se permite 0 como valor mínimo de margen horizontal.", + "max-horizontal-margin-message": "Solo se permite 50 como valor máximo de margen horizontal.", + "vertical-margin": "Margen vertical", + "vertical-margin-required": "Se requiere el valor del margen vertical.", + "min-vertical-margin-message": "Solo se permite 0 como valor mínimo de margen vertical.", + "max-vertical-margin-message": "Solo se permite 50 como valor máximo de margen vertical.", + "apply-outer-margin": "Aplicar margen a los lados de la disposición", + "autofill-height": "Altura de disposición automática", + "mobile-layout": "Configuración de disposición móvil", + "mobile-row-height": "Altura de fila móvil", + "mobile-row-height-required": "Se requiere la altura de fila móvil.", + "min-mobile-row-height-message": "Solo se permite 5 píxeles como valor mínimo de altura de fila móvil.", + "max-mobile-row-height-message": "Solo se permite 200 píxeles como valor máximo de altura de fila móvil.", + "row-height": "Altura de fila", + "row-height-required": "Se requiere el valor de altura de fila.", + "min-row-height-message": "Solo se permite 5 píxeles como valor mínimo de altura de fila.", + "max-row-height-message": "Solo se permite 200 píxeles como valor máximo de altura de fila.", + "display-first-in-mobile-view": "Mostrar primero en vista móvil", + "title-settings": "Configuración del título", + "display-title": "Mostrar título del tablero", + "title-color": "Color del título", + "toolbar-settings": "Configuración de la barra de herramientas", + "hide-toolbar": "Ocultar barra de herramientas", + "toolbar-always-open": "Mantener la barra de herramientas abierta", + "display-dashboards-selection": "Mostrar selección de tableros", + "display-entities-selection": "Mostrar selección de entidades", + "display-filters": "Mostrar filtros", + "display-dashboard-timewindow": "Mostrar ventana de tiempo del tablero", + "display-dashboard-export": "Mostrar exportación", + "display-update-dashboard-image": "Mostrar actualización de imagen del tablero", + "dashboard-logo-settings": "Configuración del logo del tablero", + "display-dashboard-logo": "Mostrar logo en modo de pantalla completa del tablero", + "dashboard-logo-image": "Imagen del logo del tablero", + "advanced-settings": "Configuraciones avanzadas", + "dashboard-css": "CSS del tablero", + "import": "Importar tablero", + "export": "Exportar tablero", + "export-failed-error": "No se pudo exportar el tablero: {{error}}", + "export-prompt": "Incluir imágenes y recursos del tablero", + "create-new-dashboard": "Crear nuevo tablero", + "dashboard-file": "Archivo del tablero", + "invalid-dashboard-file-error": "No se pudo importar el tablero: estructura de datos inválida.", + "dashboard-import-missing-aliases-title": "Configurar alias usados por el tablero importado", + "create-new-widget": "Crear nuevo widget", + "import-widget": "Importar widget", + "widget-file": "Archivo del widget", + "invalid-widget-file-error": "No se pudo importar el widget: estructura de datos inválida.", + "widget-import-missing-aliases-title": "Configurar alias usados por el widget importado", + "open-toolbar": "Abrir barra de herramientas del tablero", + "close-toolbar": "Cerrar barra de herramientas", + "configuration-error": "Error de configuración", + "alias-resolution-error-title": "Error de configuración de alias del tablero", + "invalid-aliases-config": "No se pudieron encontrar dispositivos que coincidan con algunos de los filtros de alias.
Por favor, contacta con tu administrador para resolver este problema.", + "select-devices": "Seleccionar dispositivos", + "assignedToCustomer": "Asignado al cliente", + "assignedToCustomers": "Asignado a clientes", + "public": "Público", + "copyId": "Copiar ID del tablero", + "idCopiedMessage": "El ID del tablero ha sido copiado al portapapeles", + "public-link": "Enlace público", + "copy-public-link": "Copiar enlace público", + "public-link-copied-message": "El enlace público del tablero ha sido copiado al portapapeles", + "manage-states": "Gestionar estados del tablero", + "states": "Estados del tablero", + "states-short": "Estados", + "search-states": "Buscar estados del tablero", + "selected-states": "{ count, plural, =1 {1 estado del tablero} other {# estados del tablero} } seleccionado(s)", + "edit-state": "Editar estado del tablero", + "delete-state": "Eliminar estado del tablero", + "add-state": "Agregar estado del tablero", + "no-states-text": "No se encontraron estados", + "state": "Estado del tablero", + "state-name": "Nombre", + "state-name-required": "Se requiere el nombre del estado del tablero.", + "state-id": "ID del estado", + "state-id-required": "Se requiere el ID del estado del tablero.", + "state-id-exists": "Ya existe un estado del tablero con el mismo ID.", + "is-root-state": "Estado raíz", + "delete-state-title": "Eliminar estado del tablero", + "delete-state-text": "¿Estás seguro de que deseas eliminar el estado del tablero con nombre '{{stateName}}'?", + "show-details": "Mostrar detalles", + "hide-details": "Ocultar detalles", + "select-state": "Seleccionar estado objetivo", + "state-controller": "Controlador de estado", + "state-controller-default": "estático (obsoleto)", + "search": "Buscar tableros", + "selected-dashboards": "{ count, plural, =1 {1 tablero} other {# tableros} } seleccionado(s)", + "home-dashboard": "Tablero de inicio", + "home-dashboard-hide-toolbar": "Ocultar barra de herramientas del tablero de inicio", + "unassign-dashboard-from-edge-text": "Después de la confirmación, el tablero será desasignado y no será accesible por el edge.", + "unassign-dashboards-from-edge-title": "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 tablero} other {# tableros} }?", + "unassign-dashboards-from-edge-text": "Después de la confirmación, todos los tableros seleccionados serán desasignados y no serán accesibles por el edge.", + "assign-dashboard-to-edge": "Asignar tablero(s) al edge", + "assign-dashboard-to-edge-text": "Selecciona los tableros que deseas asignar al edge", + "non-existent-dashboard-state-error": "No se encontró el estado del tablero con ID \"{{ stateId }}\"", + "edit-mode": "Modo de edición", + "duplicate-state-action": "Duplicar estado", + "breakpoint-value": "Punto de interrupción ({{ value }})", + "breakpoints-id": { + "default": "Predeterminado", + "xs": "Móvil (xs)", + "sm": "Tablet (sm)", + "md": "Portátil (md)", + "lg": "Escritorio (lg)", + "xl": "Escritorio (xl)" }, - "confirm-on-exit": { - "message": "Tienes cambios sin guardar. ¿Abandonar la página?", - "html-message": "Tienes cambios sin guardar.
¿Abandonar la página?", - "title": "Cambios sin guardar" + "view-format-type-grid": "Cuadrícula", + "view-format-type-list": "Lista", + "view-format": "Formato de vista" + }, + "datakey": { + "settings": "Configuraciones", + "general": "General", + "advanced": "Avanzado", + "key": "Clave", + "keys": "Claves", + "label": "Etiqueta", + "color": "Color", + "units": "Símbolo especial a mostrar junto al valor", + "decimals": "Número de dígitos después del punto decimal", + "data-generation-func": "Función de generación de datos", + "use-data-post-processing-func": "Usar función de post-procesamiento de datos", + "configuration": "Configuración de claves de datos", + "timeseries": "Serie temporal", + "attributes": "Atributos", + "entity-field": "Campo de entidad", + "alarm": "Campos de alarma", + "timeseries-required": "Se requiere la serie temporal de la entidad.", + "timeseries-or-attributes-required": "Se requiere la serie temporal o atributos de la entidad.", + "alarm-fields-timeseries-or-attributes-required": "Se requieren campos de alarma o serie temporal/atributos de la entidad.", + "maximum-timeseries-or-attributes": "Máximo { count, plural, =1 {1 serie temporal/atributo permitido.} other {# series temporales/atributos permitidos} }", + "alarm-fields-required": "Se requieren campos de alarma.", + "function-types": "Tipos de función", + "function-type": "Tipo de función", + "function-types-required": "Se requieren tipos de función.", + "data-keys": "Claves de datos", + "data-key": "Clave de datos", + "data-keys-required": "Se requieren claves de datos.", + "data-key-required": "Se requiere una clave de datos.", + "alarm-keys": "Claves de datos de alarma", + "alarm-key": "Clave de datos de alarma", + "alarm-key-functions": "Funciones de clave de alarma", + "alarm-key-function": "Función de clave de alarma", + "latest-keys": "Últimas claves de datos", + "latest-key": "Última clave de datos", + "latest-key-functions": "Funciones de última clave", + "latest-key-function": "Función de última clave", + "timeseries-keys": "Claves de datos de serie temporal", + "timeseries-key": "Clave de serie temporal", + "timeseries-key-functions": "Funciones de clave de serie temporal", + "timeseries-key-function": "Función de clave de serie temporal", + "maximum-function-types": "Máximo { count, plural, =1 {1 tipo de función permitido.} other {# tipos de función permitidos} }", + "time-description": "marca de tiempo del valor actual;", + "value-description": "el valor actual;", + "prev-value-description": "resultado de la llamada anterior a la función;", + "time-prev-description": "marca de tiempo del valor anterior;", + "prev-orig-value-description": "valor original anterior;", + "aggregation": "Agregación", + "aggregation-type-hint-common": "Por razones de rendimiento, el cálculo de valores agregados está disponible solo para intervalos de tiempo fijos como \"día actual\", \"mes actual\", etc., y no está disponible para ventanas de tiempo deslizantes como 'últimos 30 minutos' o 'últimas 24 horas'.", + "aggregation-type-none-hint": "Tomar el valor más reciente.", + "aggregation-type-min-hint": "Encontrar el valor mínimo entre los puntos de datos dentro de una ventana de tiempo seleccionada.", + "aggregation-type-max-hint": "Encontrar el valor máximo entre los puntos de datos dentro de una ventana de tiempo seleccionada.", + "aggregation-type-avg-hint": "Calcular un valor promedio entre los puntos de datos dentro de una ventana de tiempo seleccionada.", + "aggregation-type-sum-hint": "Suma de todos los valores de los puntos de datos dentro de una ventana de tiempo seleccionada.", + "aggregation-type-count-hint": "Número total de puntos de datos dentro de una ventana de tiempo seleccionada.", + "delta-calculation": "Cálculo delta", + "enable-delta-calculation": "Habilitar cálculo delta", + "enable-delta-calculation-hint": "Cuando está habilitado, el valor de la clave de datos se calcula en función de los valores agregados para una ventana de tiempo seleccionada y un período de comparación especificado. Por razones de rendimiento, el cálculo delta está disponible solo para ventanas de tiempo históricas y no para valores en tiempo real. Por ejemplo, puedes calcular el delta entre el consumo de energía de ayer comparado con el del día anterior.", + "delta-calculation-result": "Resultado del cálculo delta", + "delta-calculation-result-previous-value": "Valor anterior", + "delta-calculation-result-delta-absolute": "Delta (absoluto)", + "delta-calculation-result-delta-percent": "Delta (porcentaje)", + "source": "Fuente", + "latest": "Último", + "latest-value": "Último valor", + "delta": "delta", + "percent": "porcentaje", + "absolute": "absoluto" + }, + "datasource": { + "type": "Tipo de fuente de datos", + "name": "Nombre", + "label": "Etiqueta", + "add-datasource-prompt": "Por favor, agrega una fuente de datos" + }, + "details": { + "details": "Detalles", + "edit-mode": "Modo de edición", + "edit-json": "Editar JSON", + "toggle-edit-mode": "Alternar modo de edición" + }, + "device": { + "device": "Dispositivo", + "device-required": "Se requiere un dispositivo.", + "devices": "Dispositivos", + "management": "Gestión de dispositivos", + "view-devices": "Ver dispositivos", + "device-alias": "Alias del dispositivo", + "device-type-max-length": "El tipo de dispositivo debe tener menos de 256 caracteres", + "aliases": "Alias de dispositivos", + "no-alias-matching": "'{{alias}}' no encontrado.", + "no-aliases-found": "No se encontraron alias.", + "no-key-matching": "'{{key}}' no encontrado.", + "no-keys-found": "No se encontraron claves.", + "create-new-alias": "¡Crear uno nuevo!", + "create-new-key": "¡Crear uno nuevo!", + "duplicate-alias-error": "Se encontró un alias duplicado '{{alias}}'.
Los alias de dispositivos deben ser únicos dentro del tablero.", + "configure-alias": "Configurar alias '{{alias}}'", + "no-devices-matching": "No se encontraron dispositivos que coincidan con '{{entity}}'.", + "alias": "Alias", + "alias-required": "Se requiere un alias de dispositivo.", + "remove-alias": "Eliminar alias de dispositivo", + "add-alias": "Agregar alias de dispositivo", + "name-starts-with": "Expresión del nombre del dispositivo", + "help-text": "Usa '%' según sea necesario: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list": "Lista de dispositivos", + "use-device-name-filter": "Usar filtro", + "device-list-empty": "No se seleccionaron dispositivos.", + "device-name-filter-required": "Se requiere el filtro de nombre del dispositivo.", + "device-name-filter-no-device-matched": "No se encontraron dispositivos que comiencen con '{{device}}'.", + "add": "Agregar dispositivo", + "assign-to-customer": "Asignar al cliente", + "assign-device-to-customer": "Asignar dispositivo(s) al cliente", + "assign-device-to-customer-text": "Selecciona los dispositivos que deseas asignar al cliente", + "make-public": "Hacer público el dispositivo", + "make-private": "Hacer privado el dispositivo", + "no-devices-text": "No se encontraron dispositivos", + "assign-to-customer-text": "Selecciona el cliente al que deseas asignar el/los dispositivo(s)", + "device-details": "Detalles del dispositivo", + "add-device-text": "Agregar nuevo dispositivo", + "credentials": "Credenciales", + "manage-credentials": "Gestionar credenciales", + "delete": "Eliminar dispositivo", + "assign-devices": "Asignar dispositivos", + "assign-devices-text": "Asignar { count, plural, =1 {1 dispositivo} other {# dispositivos} } al cliente", + "delete-devices": "Eliminar dispositivos", + "unassign-from-customer": "Desasignar del cliente", + "unassign-devices": "Desasignar dispositivos", + "unassign-devices-action-title": "Desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} } del cliente", + "unassign-device-from-edge-title": "¿Estás seguro de que deseas desasignar el dispositivo '{{deviceName}}'?", + "unassign-device-from-edge-text": "Después de la confirmación, el dispositivo será desasignado y no será accesible por el edge.", + "unassign-devices-from-edge": "Desasignar dispositivos del edge", + "assign-new-device": "Asignar nuevo dispositivo", + "make-public-device-title": "¿Estás seguro de que deseas hacer público el dispositivo '{{deviceName}}'?", + "make-public-device-text": "Después de la confirmación, el dispositivo y todos sus datos serán públicos y accesibles por otros.", + "make-private-device-title": "¿Estás seguro de que deseas hacer privado el dispositivo '{{deviceName}}'?", + "make-private-device-text": "Después de la confirmación, el dispositivo y todos sus datos serán privados y no serán accesibles por otros.", + "view-credentials": "Ver credenciales", + "delete-device-title": "¿Estás seguro de que deseas eliminar el dispositivo '{{deviceName}}'?", + "delete-device-text": "Ten cuidado, después de la confirmación el dispositivo y todos los datos relacionados serán irrecuperables.", + "delete-devices-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", + "delete-devices-action-title": "Eliminar { count, plural, =1 {1 dispositivo} other {# dispositivos} }", + "delete-devices-text": "Ten cuidado, después de la confirmación todos los dispositivos seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "unassign-device-title": "¿Estás seguro de que deseas desasignar el dispositivo '{{deviceName}}'?", + "unassign-device-text": "Después de la confirmación, el dispositivo será desasignado y no será accesible por el cliente.", + "unassign-device": "Desasignar dispositivo", + "unassign-devices-title": "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", + "unassign-devices-text": "Después de la confirmación, todos los dispositivos seleccionados serán desasignados y no serán accesibles por el cliente.", + "device-credentials": "Credenciales del dispositivo", + "loading-device-credentials": "Cargando credenciales del dispositivo...", + "credentials-type": "Tipo de credenciales", + "access-token": "Token de acceso", + "access-token-required": "Se requiere el token de acceso.", + "access-token-invalid": "La longitud del token de acceso debe ser de 1 a 32 caracteres.", + "certificate-pem-format": "Certificado en formato PEM", + "certificate-pem-format-required": "Se requiere un certificado.", + "copy-access-token": "Copiar token de acceso", + "copy-certificate": "Copiar certificado", + "copy-client-id": "Copiar ID del cliente", + "copy-user-name": "Copiar nombre de usuario", + "copy-password": "Copiar contraseña", + "generate-client-id": "Generar ID del cliente", + "generate-user-name": "Generar nombre de usuario", + "generate-password": "Generar contraseña", + "generate-access-token": "Generar token de acceso", + "lwm2m-security-config": { + "identity": "Identidad del cliente", + "identity-required": "Se requiere la identidad del cliente.", + "identity-tooltip": "El identificador PSK es un identificador arbitrario de hasta 128 bytes, como se describe en la norma [RFC7925].\nEl identificador PSK DEBE convertirse primero en una cadena de caracteres y luego codificarse en octetos usando UTF-8.", + "client-key": "Clave del cliente", + "client-key-required": "Se requiere la clave del cliente.", + "client-key-tooltip-prk": "¡La clave pública o ID RPK debe cumplir con la norma [RFC7250] y estar codificada en formato Base64!", + "client-key-tooltip-psk": "¡La clave PSK debe cumplir con la norma [RFC4279] y estar en formato HexDec: 32, 64 o 128 caracteres!", + "endpoint": "Nombre del cliente endpoint", + "endpoint-required": "Se requiere el nombre del cliente endpoint.", + "client-public-key": "Clave pública del cliente", + "client-public-key-hint": "Si la clave pública del cliente está vacía, se usará el certificado confiable", + "client-public-key-tooltip": "La clave pública X509 debe estar en formato DER-encoded X509v3, soportar exclusivamente el algoritmo EC y luego codificarse en Base64.", + "mode": "Modo de configuración de seguridad", + "client-tab": "Configuración de seguridad del cliente", + "client-certificate": "Certificado del cliente", + "bootstrap-tab": "Cliente Bootstrap", + "bootstrap-server": "Servidor Bootstrap", + "lwm2m-server": "Servidor LwM2M", + "client-publicKey-or-id": "Clave pública o ID del cliente", + "client-publicKey-or-id-required": "Se requiere la clave pública o el ID del cliente.", + "client-publicKey-or-id-tooltip-psk": "El identificador PSK es arbitrario hasta 128 bytes, conforme a la norma [RFC7925].\nDebe convertirse a cadena y luego a octetos en UTF-8.", + "client-publicKey-or-id-tooltip-rpk": "La clave pública o ID RPK debe cumplir con [RFC7250] y estar en Base64.", + "client-publicKey-or-id-tooltip-x509": "La clave pública X509 debe cumplir con X509v3 DER-encoded, usar algoritmo EC y estar en Base64.", + "client-secret-key": "Clave secreta del cliente", + "client-secret-key-required": "Se requiere la clave secreta del cliente.", + "client-secret-key-tooltip-psk": "La clave PSK debe cumplir con [RFC4279] y estar en HexDec (32, 64, 128 caracteres).", + "client-secret-key-tooltip-prk": "La clave secreta RPK debe estar en formato PKCS_8 (codificación DER, estándar [RFC5958]) y en Base64.", + "client-secret-key-tooltip-x509": "La clave secreta X509 debe estar en formato PKCS_8 (codificación DER, estándar [RFC5958]) y en Base64." }, - "contact": { - "country": "País", - "city": "Ciudad", - "state": "Estado / Provincia", - "postal-code": "Código Postal", - "postal-code-invalid": "Formato de código postal inválido.", - "address": "Dirección", - "address2": "Dirección 2", - "phone": "Teléfono", - "email": "Email", - "no-address": "Sin Dirección", - "state-max-length": "Longitud de provincia debe ser menor que 256", - "phone-max-length": "Teléfono debe ser menor que 256", - "city-max-length": "Ciudad debe ser menor que 256" + "client-id": "ID del cliente", + "client-id-pattern": "Contiene carácter inválido.", + "user-name": "Nombre de usuario", + "user-name-required": "Se requiere el nombre de usuario.", + "client-id-or-user-name-necessary": "Se requiere el ID del cliente y/o el nombre de usuario", + "password": "Contraseña", + "secret": "Secreto", + "secret-required": "Se requiere un secreto.", + "device-type": "Perfil del dispositivo", + "device-type-required": "Se requiere el tipo de dispositivo.", + "select-device-type": "Seleccionar tipo de dispositivo", + "enter-device-type": "Ingresar perfil del dispositivo", + "any-device": "Cualquier dispositivo", + "no-device-types-matching": "No se encontraron perfiles de dispositivo que coincidan con '{{entitySubtype}}'.", + "device-type-list-empty": "¡No se seleccionaron perfiles de dispositivo!", + "device-profile-type-list-empty": "Debe seleccionarse al menos un perfil de dispositivo.", + "device-types": "Tipos de dispositivos", + "name": "Nombre", + "name-required": "Se requiere el nombre.", + "name-max-length": "El nombre debe tener menos de 256 caracteres", + "label-max-length": "La etiqueta debe tener menos de 256 caracteres", + "description": "Descripción", + "label": "Etiqueta", + "events": "Eventos", + "details": "Detalles", + "copyId": "Copiar ID del dispositivo", + "copyAccessToken": "Copiar token de acceso", + "copy-mqtt-authentication": "Copiar credenciales MQTT", + "idCopiedMessage": "ID del dispositivo copiado al portapapeles", + "accessTokenCopiedMessage": "Token de acceso del dispositivo copiado al portapapeles", + "mqtt-authentication-copied-message": "Autenticación MQTT del dispositivo copiada al portapapeles", + "assignedToCustomer": "Asignado al cliente", + "unable-delete-device-alias-title": "No se puede eliminar el alias del dispositivo", + "unable-delete-device-alias-text": "No se puede eliminar el alias del dispositivo '{{deviceAlias}}' porque lo utilizan los siguientes widget(s):
{{widgetsList}}", + "is-gateway": "Es un gateway", + "overwrite-activity-time": "Sobrescribir tiempo de actividad del dispositivo conectado", + "device-filter": "Filtro de dispositivo", + "device-filter-title": "Filtro de dispositivo", + "filter-title": "Filtro", + "device-state": "Estado del dispositivo", + "state": "Estado", + "any": "Cualquiera", + "active": "Activo", + "inactive": "Inactivo", + "public": "Público", + "device-public": "El dispositivo es público", + "select-device": "Seleccionar dispositivo", + "import": "Importar dispositivo", + "device-file": "Archivo del dispositivo", + "search": "Buscar dispositivos", + "selected-devices": "{ count, plural, =1 {1 dispositivo} other {# dispositivos} } seleccionado(s)", + "device-configuration": "Configuración del dispositivo", + "transport-configuration": "Configuración de transporte", + "wizard": { + "device-details": "Detalles del dispositivo" }, - "common": { - "username": "Usuario", - "password": "Contraseña", - "enter-username": "Introduce el nombre de usuario.", - "enter-password": "Introduce la contraseña", - "enter-search": "Introduce búsqueda", - "created-time": "Fecha de creación", - "loading": "Cargando...", - "proceed": "Proceder", - "open-details-page": "Abrir detalles", - "not-found": "No encontrado", - "documentation": "Documentación" + "unassign-devices-from-edge-title": "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", + "unassign-devices-from-edge-text": "Después de la confirmación, todos los dispositivos seleccionados serán desasignados y no serán accesibles por el edge.", + "time": "Tiempo", + "connectivity": { + "check-connectivity": "Verificar conectividad", + "device-created-check-connectivity": "Dispositivo creado. ¡Vamos a verificar la conectividad!", + "loading-check-connectivity-command": "Cargando comandos para verificar conectividad...", + "use-following-instructions": "Usa las siguientes instrucciones para enviar telemetría en nombre del dispositivo usando shell", + "execute-following-command": "Ejecuta el siguiente comando", + "install-curl-windows": "A partir de Windows 10 b17063, cURL está disponible por defecto", + "install-curl-macos": "Desde Mac OS X 10.2 6C115 (Jaguar), cURL está disponible por defecto", + "install-mqtt-windows": "Usa las instrucciones para descargar, instalar, configurar y ejecutar mosquitto_pub", + "install-coap-client": "Usa las instrucciones para descargar, instalar, configurar y ejecutar coap-client", + "install-necessary-client-tools": "Instalar las herramientas de cliente necesarias", + "mqtts-x509-command": "Usa la siguiente documentación para conectar el dispositivo mediante MQTT con autorización X509", + "coaps-x509-command": "Usa la siguiente documentación para conectar el dispositivo mediante CoAP sobre DTLS con autorización X509", + "snmp-command": "Usa la siguiente documentación para conectar el dispositivo a través de SNMP.", + "sparkplug-command": "Usa la siguiente documentación para conectar el dispositivo a través de MQTT Sparkplug.", + "lwm2m-command": "Usa la siguiente documentación para conectar el dispositivo mediante LWM2M." + } + }, + "dynamic-form": { + "property": { + "properties": "Propiedades", + "property": "Propiedad", + "id": "Id", + "name": "Nombre", + "type": "Tipo", + "type-text": "Texto", + "type-password": "Contraseña", + "type-textarea": "Área de texto", + "type-number": "Número", + "type-switch": "Interruptor", + "type-select": "Seleccionar", + "type-radios": "Botones de opción", + "type-datetime": "Fecha/Hora", + "type-image": "Imagen", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "Color", + "type-color-settings": "Configuración de color", + "type-font": "Fuente", + "type-units": "Unidades", + "type-icon": "Ícono", + "type-fieldset": "Conjunto de campos", + "type-array": "Arreglo", + "type-html-section": "Sección HTML", + "group-title": "Título del grupo", + "no-properties": "No hay propiedades configuradas", + "add-property": "Agregar propiedad", + "property-settings": "Configuración de propiedad", + "remove-property": "Eliminar propiedad", + "default-value": "Valor por defecto", + "value-required": "Se requiere valor", + "number-settings": "Configuración de número", + "min": "Mín", + "max": "Máx", + "step": "Paso", + "selected-options-limit": "Límite de opciones seleccionadas", + "advanced-ui-settings": "Configuración avanzada de la interfaz", + "disable-on-property": "Deshabilitar en propiedad", + "display-condition-function": "Función de condición de visualización", + "sub-label": "Subetiqueta", + "vertical-divider-after": "Divisor vertical después", + "input-field-suffix": "Sufijo del campo de entrada", + "property-row-classes": "Clases de fila de propiedad", + "property-field-classes": "Clases de campo de propiedad", + "not-unique-property-ids-error": "¡Los Ids de propiedad deben ser únicos!", + "enable-multiple-select": "Habilitar selección múltiple", + "allow-empty-select-option": "Permitir opción vacía", + "select-options": "Opciones de selección", + "not-unique-select-option-value-error": "¡Los valores de las opciones de selección deben ser únicos!", + "value": "Valor", + "label": "Etiqueta", + "add-option": "Agregar opción", + "no-options": "No hay opciones configuradas", + "remove-option": "Eliminar opción", + "textarea-rows": "Filas del área de texto", + "help-id": "Id de ayuda", + "buttons-direction": "Dirección de los botones", + "direction-row": "Fila", + "direction-column": "Columna", + "radio-button-options": "Opciones de botones de radio", + "datetime-type": "Tipo de campo Fecha/Hora", + "datetime-type-date": "Fecha", + "datetime-type-time": "Hora", + "datetime-type-datetime": "Fecha/Hora", + "enable-clear-button": "Habilitar botón de limpiar", + "html-section-settings": "Configuración de la sección HTML", + "html-section-classes": "Clases de la sección HTML", + "html-section-content": "Contenido de la sección HTML", + "array-item": "Elemento del arreglo", + "item-type": "Tipo de elemento", + "item-name": "Nombre del elemento", + "no-items": "No hay elementos" }, - "content-type": { - "json": "Json", - "text": "Texto", - "binary": "Binario (Base64)" + "clear-form": "Limpiar formulario", + "clear-form-prompt": "¿Estás seguro de que deseas eliminar todas las propiedades del formulario?", + "import-form": "Importar formulario desde JSON", + "export-form": "Exportar formulario a JSON", + "json-file": "Archivo JSON", + "json-content": "Contenido JSON", + "invalid-form-json-file-error": "No se pudo importar el formulario desde JSON: estructura de datos JSON del formulario inválida." + }, + "asset-profile": { + "asset-profile": "Perfil de activo", + "asset-profiles": "Perfiles de activos", + "all-asset-profiles": "Todos", + "add": "Agregar perfil de activo", + "edit": "Editar perfil de activo", + "asset-profile-details": "Detalles del perfil de activo", + "no-asset-profiles-text": "No se encontraron perfiles de activos", + "search": "Buscar perfiles de activos", + "selected-asset-profiles": "{ count, plural, =1 {1 perfil de activo} other {# perfiles de activos} } seleccionado(s)", + "no-asset-profiles-matching": "No se encontraron perfiles de activos que coincidan con '{{entity}}'.", + "asset-profile-required": "Se requiere un perfil de activo", + "idCopiedMessage": "El ID del perfil de activo ha sido copiado al portapapeles", + "set-default": "Establecer como perfil de activo predeterminado", + "delete": "Eliminar perfil de activo", + "copyId": "Copiar ID del perfil de activo", + "name-max-length": "El nombre debe tener menos de 256 caracteres", + "new-device-profile-name": "Nombre del perfil de activo", + "new-device-profile-name-required": "Se requiere el nombre del perfil de activo.", + "name": "Nombre", + "name-required": "Se requiere el nombre.", + "image": "Imagen del perfil de activo", + "description": "Descripción", + "default": "Predeterminado", + "default-rule-chain": "Cadena de reglas predeterminada", + "default-edge-rule-chain": "Cadena de reglas predeterminada para el edge", + "default-edge-rule-chain-hint": "Utilizada en edge como cadena de reglas para procesar datos entrantes de activos con este perfil", + "mobile-dashboard": "Tablero móvil", + "mobile-dashboard-hint": "Utilizado por la aplicación móvil como tablero de detalles del activo", + "select-queue-hint": "Seleccionar de la lista desplegable.", + "delete-asset-profile-title": "¿Estás seguro de que deseas eliminar el perfil de activo '{{assetProfileName}}'?", + "delete-asset-profile-text": "Ten cuidado, después de la confirmación el perfil de activo y todos los datos relacionados serán irrecuperables.", + "delete-asset-profiles-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 perfil de activo} other {# perfiles de activos} }?", + "delete-asset-profiles-text": "Ten cuidado, después de la confirmación todos los perfiles de activos seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "set-default-asset-profile-title": "¿Estás seguro de que deseas establecer el perfil de activo '{{assetProfileName}}' como predeterminado?", + "set-default-asset-profile-text": "Después de la confirmación, el perfil de activo se marcará como predeterminado y se utilizará para nuevos activos sin perfil especificado.", + "no-asset-profiles-found": "No se encontraron perfiles de activos.", + "create-new-asset-profile": "¡Crear uno nuevo!", + "create-asset-profile": "Crear nuevo perfil de activo", + "import": "Importar perfil de activo", + "export": "Exportar perfil de activo", + "export-failed-error": "No se pudo exportar el perfil de activo: {{error}}", + "asset-profile-file": "Archivo del perfil de activo", + "invalid-asset-profile-file-error": "No se pudo importar el perfil de activo: estructura de datos inválida." + }, + "device-profile": { + "device-profile": "Perfil de dispositivo", + "device-profiles": "Perfiles de dispositivos", + "all-device-profiles": "Todos", + "add": "Agregar perfil de dispositivo", + "edit": "Editar perfil de dispositivo", + "device-profile-details": "Detalles del perfil de dispositivo", + "no-device-profiles-text": "No se encontraron perfiles de dispositivos", + "search": "Buscar perfiles de dispositivos", + "selected-device-profiles": "{ count, plural, =1 {1 perfil de dispositivo} other {# perfiles de dispositivos} } seleccionado(s)", + "no-device-profiles-matching": "No se encontraron perfiles de dispositivos que coincidan con '{{entity}}'.", + "device-profile-required": "Se requiere un perfil de dispositivo", + "idCopiedMessage": "El ID del perfil de dispositivo ha sido copiado al portapapeles", + "set-default": "Establecer como perfil predeterminado", + "delete": "Eliminar perfil de dispositivo", + "copyId": "Copiar ID del perfil de dispositivo", + "name-max-length": "El nombre debe tener menos de 256 caracteres", + "name": "Nombre", + "name-required": "Se requiere el nombre.", + "type": "Tipo de perfil", + "type-required": "Se requiere el tipo de perfil.", + "type-default": "Predeterminado", + "image": "Imagen del perfil de dispositivo", + "transport-type": "Tipo de transporte", + "transport-type-required": "Se requiere el tipo de transporte.", + "transport-type-default": "Predeterminado", + "transport-type-default-hint": "Soporta transporte básico MQTT, HTTP y CoAP", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "Habilita configuraciones avanzadas para transporte MQTT", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "Habilita configuraciones avanzadas para transporte CoAP", + "transport-type-lwm2m": "LWM2M", + "transport-type-lwm2m-hint": "Tipo de transporte LWM2M", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "Especificar configuración de transporte SNMP", + "transport-type-http": "HTTP", + "description": "Descripción", + "default": "Predeterminado", + "profile-configuration": "Configuración del perfil", + "transport-configuration": "Configuración de transporte", + "default-rule-chain": "Cadena de reglas predeterminada", + "default-edge-rule-chain": "Cadena de reglas predeterminada para el edge", + "default-edge-rule-chain-hint": "Utilizado en el edge para procesar datos entrantes de dispositivos con este perfil", + "mobile-dashboard": "Tablero móvil", + "mobile-dashboard-hint": "Utilizado por la aplicación móvil como tablero de detalles del dispositivo", + "select-queue-hint": "Seleccionar desde la lista desplegable.", + "delete-device-profile-title": "¿Estás seguro de que deseas eliminar el perfil de dispositivo '{{deviceProfileName}}'?", + "delete-device-profile-text": "Ten cuidado, después de la confirmación el perfil de dispositivo y todos los datos relacionados, incluyendo actualizaciones OTA asociadas, serán irrecuperables.", + "delete-device-profiles-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 perfil de dispositivo} other {# perfiles de dispositivos} }?", + "delete-device-profiles-text": "Ten cuidado, después de la confirmación todos los perfiles de dispositivos seleccionados serán eliminados y todos los datos relacionados, incluyendo actualizaciones OTA asociadas, serán irrecuperables.", + "set-default-device-profile-title": "¿Estás seguro de que deseas establecer el perfil de dispositivo '{{deviceProfileName}}' como predeterminado?", + "set-default-device-profile-text": "Después de la confirmación, el perfil de dispositivo se marcará como predeterminado y se usará para nuevos dispositivos sin perfil especificado.", + "no-device-profiles-found": "No se encontraron perfiles de dispositivos.", + "create-new-device-profile": "¡Crear uno nuevo!", + "mqtt-device-topic-filters": "Filtros de temas MQTT del dispositivo", + "mqtt-device-topic-filters-unique": "Los filtros de temas MQTT deben ser únicos.", + "mqtt-device-topic-filters-spark-plug": "Nodo EoN de Sparkplug B para MQTT.", + "mqtt-device-topic-filters-spark-plug-hint": "Permite conexiones de nodos EoN con formato de carga útil y tema de Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "Métricas SparkPlug para almacenar como atributos.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Nombres de métricas SparkPlug que se almacenarán como atributos del dispositivo. Todas las demás métricas se almacenarán como telemetría.", + "mqtt-device-payload-type": "Carga útil del dispositivo MQTT", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Habilitar compatibilidad con otros formatos de carga útil.", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Cuando está habilitado, la plataforma usará por defecto Protobuf. Si falla el análisis, intentará usar JSON. Útil para compatibilidad durante actualizaciones de firmware. Puede degradar el rendimiento, se recomienda deshabilitarlo cuando todos los dispositivos estén actualizados.", + "mqtt-use-json-format-for-default-downlink-topics": "Usar formato Json para temas de bajada predeterminados", + "mqtt-use-json-format-for-default-downlink-topics-hint": "Cuando está habilitado, se usa formato Json para enviar atributos y RPC en temas como: v1/devices/me/attributes/response/$request_id, etc. No afecta suscripciones en temas v2: v2/a/res/$request_id, etc.", + "mqtt-send-ack-on-validation-exception": "Enviar PUBACK en error de validación de mensaje PUBLISH", + "mqtt-send-ack-on-validation-exception-hint": "Por defecto se cierra la sesión MQTT ante errores. Con esta opción, se envía un ACK en su lugar.", + "snmp-add-mapping": "Agregar mapeo SNMP", + "snmp-mapping-not-configured": "No hay mapeo de OID a serie temporal/telemetría configurado", + "snmp-timseries-or-attribute-name": "Nombre de serie temporal/atributo para mapeo", + "snmp-timseries-or-attribute-type": "Tipo de serie temporal/atributo para mapeo", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "Se requiere tipo de carga útil.", + "coap-device-type": "Tipo de dispositivo CoAP", + "coap-device-payload-type": "Carga útil del dispositivo CoAP", + "coap-device-type-required": "Se requiere el tipo de dispositivo CoAP.", + "coap-device-type-default": "Predeterminado", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "Se admiten comodines de un solo nivel [+] y de múltiples niveles [#].", + "telemetry-topic-filter": "Filtro de tema de telemetría", + "telemetry-topic-filter-required": "Se requiere filtro de tema de telemetría.", + "attributes-topic-filter": "Filtro de tema para publicación de atributos", + "attributes-subscribe-topic-filter": "Filtro de tema para suscripción de atributos", + "attributes-topic-filter-required": "Se requiere filtro de tema para publicación de atributos.", + "attributes-subscribe-topic-filter-required": "Se requiere tema de suscripción de atributos", + "telemetry-proto-schema": "Esquema proto de telemetría", + "telemetry-proto-schema-required": "Se requiere esquema proto de telemetría.", + "attributes-proto-schema": "Esquema proto de atributos", + "attributes-proto-schema-required": "Se requiere esquema proto de atributos.", + "rpc-response-proto-schema": "Esquema proto de respuesta RPC", + "rpc-response-proto-schema-required": "Se requiere esquema proto de respuesta RPC.", + "rpc-response-topic-filter": "Filtro de tema de respuesta RPC", + "rpc-response-topic-filter-required": "Se requiere filtro de tema de respuesta RPC.", + "rpc-request-proto-schema": "Esquema proto de solicitud RPC", + "rpc-request-proto-schema-required": "Se requiere esquema proto de solicitud RPC.", + "rpc-request-proto-schema-hint": "El mensaje de solicitud RPC siempre debe tener los campos: string method = 1; int32 requestId = 2; y params = 3 de cualquier tipo de dato.", + "not-valid-pattern-topic-filter": "Filtro de tema con patrón no válido", + "not-valid-single-character": "Uso inválido del comodín de un solo nivel", + "not-valid-multi-character": "Uso inválido del comodín de múltiples niveles", + "single-level-wildcards-hint": "[+] es adecuado para cualquier nivel del filtro de tema. Ej.: v1/devices/+/telemetry o +/devices/+/attributes.", + "multi-level-wildcards-hint": "[#] puede reemplazar todo el filtro de tema y debe ser el último símbolo. Ej.: # o v1/devices/me/#.", + "alarm-rules": "Reglas de alarma", + "alarm-rules-with-count": "Reglas de alarma ({{count}})", + "no-alarm-rules": "No hay reglas de alarma configuradas", + "add-alarm-rule": "Agregar regla de alarma", + "edit-alarm-rule": "Editar regla de alarma", + "alarm-type": "Tipo de alarma", + "alarm-type-required": "Se requiere tipo de alarma.", + "alarm-type-unique": "El tipo de alarma debe ser único dentro de las reglas de alarma del perfil.", + "alarm-type-max-length": "El tipo de alarma debe tener menos de 256 caracteres", + "create-alarm-pattern": "Crear alarma {{alarmType}}", + "create-alarm-rules": "Crear reglas de alarma", + "no-create-alarm-rules": "No hay condiciones de creación configuradas", + "add-create-alarm-rule-prompt": "Por favor, añade una regla de creación de alarma", + "clear-alarm-rule": "Condición de limpieza de alarma", + "no-clear-alarm-rule": "No hay condición de limpieza configurada", + "add-create-alarm-rule": "Agregar condición de creación", + "add-clear-alarm-rule": "Agregar condición de limpieza", + "select-alarm-severity": "Seleccionar severidad de la alarma", + "alarm-severity-required": "Se requiere severidad de la alarma.", + "condition-duration": "Duración de la condición", + "condition-duration-value": "Valor de duración", + "condition-duration-time-unit": "Unidad de tiempo", + "condition-duration-value-range": "El valor debe estar entre 1 y 2147483647.", + "condition-duration-value-pattern": "El valor debe ser un número entero.", + "condition-duration-value-required": "Se requiere valor de duración.", + "condition-duration-time-unit-required": "Se requiere unidad de tiempo.", + "advanced-settings": "Configuración avanzada", + "alarm-rule-additional-info": "Información adicional", + "edit-alarm-rule-additional-info": "Editar información adicional", + "alarm-rule-additional-info-placeholder": "Proporcione sus comentarios y ajustes para que aparezcan en los detalles de la alarma bajo Información adicional", + "alarm-rule-additional-info-hint": "Sugerencia: use ${keyName} para sustituir valores de atributos o claves de telemetría en la condición.", + "alarm-rule-mobile-dashboard": "Tablero móvil", + "alarm-rule-mobile-dashboard-hint": "Usado por la aplicación móvil como tablero de detalles de la alarma", + "alarm-rule-no-mobile-dashboard": "No hay tablero seleccionado", + "propagate-alarm": "Propagar alarma a entidades relacionadas", + "alarm-rule-relation-types-list": "Tipos de relación", + "alarm-rule-relation-types-list-hint": "Define tipos de relación para filtrar las entidades relacionadas. Si no se define, la alarma se propagará a todas las entidades relacionadas.", + "propagate-alarm-to-owner": "Propagar alarma al propietario de la entidad (Cliente o Inquilino)", + "propagate-alarm-to-tenant": "Propagar alarma al inquilino", + "alarm-rule-condition": "Condición de la regla de alarma", + "enter-alarm-rule-condition-prompt": "Por favor, agregue condición para la regla de alarma", + "edit-alarm-rule-condition": "Editar condición de la regla de alarma", + "device-provisioning": "Aprovisionamiento del dispositivo", + "provision-strategy": "Estrategia de aprovisionamiento", + "provision-strategy-required": "Se requiere estrategia de aprovisionamiento.", + "provision-strategy-disabled": "Deshabilitada", + "provision-strategy-created-new": "Permitir creación de nuevos dispositivos", + "provision-strategy-check-pre-provisioned": "Verificar dispositivos preaprobados", + "provision-device-key": "Clave de aprovisionamiento del dispositivo", + "provision-device-key-required": "Se requiere clave de aprovisionamiento del dispositivo.", + "copy-provision-key": "Copiar clave de aprovisionamiento", + "provision-key-copied-message": "La clave de aprovisionamiento ha sido copiada al portapapeles", + "provision-device-secret": "Secreto de aprovisionamiento del dispositivo", + "provision-device-secret-required": "Se requiere secreto de aprovisionamiento del dispositivo.", + "copy-provision-secret": "Copiar secreto de aprovisionamiento", + "provision-secret-copied-message": "El secreto de aprovisionamiento ha sido copiado al portapapeles", + "provision-strategy-x509": { + "certificate-chain": "Cadena de certificados X509", + "certificate-chain-hint": "La estrategia de certificados X.509 se utiliza para aprovisionar dispositivos mediante certificados de cliente en comunicación TLS bidireccional.", + "allow-create-new-devices": "Crear nuevos dispositivos", + "allow-create-new-devices-hint": "Si se selecciona, se crearán nuevos dispositivos y el certificado del cliente se usará como credencial del dispositivo.", + "certificate-value": "Certificado en formato PEM", + "certificate-value-required": "Se requiere certificado en formato PEM", + "cn-regex-variable": "Variable de expresión regular CN", + "cn-regex-variable-required": "Se requiere variable de expresión regular CN", + "cn-regex-variable-hint": "Necesario para obtener el nombre del dispositivo desde el nombre común del certificado X509 del dispositivo." }, - "customer": { - "customer": "Cliente", - "customers": "Clientes", - "management": "Gestión de Clientes", - "dashboard": "Panel del Cliente", - "dashboards": "Paneles del Cliente", - "devices": "Dispositivos del cliente", - "entity-views": "Vistas de Entidad del cliente", - "assets": "Activos de Cliente", - "public-dashboards": "Paneles Públicos", - "public-devices": "Dispositivos Públicos", - "public-assets": "Activos Públicos", - "public-entity-views": "Vistas de Entidad Públicas", - "public-edges": "Edges públicos", - "add": "Agregar cliente", - "delete": "Borrar cliente", - "manage-customer-users": "Gestionar usuarios del cliente", - "manage-customer-devices": "Gestionar dispositivos del cliente", - "manage-customer-dashboards": "Gestionar paneles del cliente", - "manage-public-devices": "Gestionar dispositivos públicos", - "manage-public-dashboards": "Gestionar paneles públicos", - "manage-customer-assets": "Gestionar activos del cliente", - "manage-public-assets": "Gestionar activos públicos", - "manage-customer-edges": "Administrar Edges de clientes", - "manage-public-edges": "Administrar Edges públicos", - "add-customer-text": "Agregar nuevo cliente", - "no-customers-text": "No se encontraron clientes", - "customer-details": "Detalles del cliente", - "delete-customer-title": "¿Eliminar el cliente '{{customerTitle}}'?", - "delete-customer-text": "Atención, tras la confirmación el cliente será eliminado y toda la información relacionada será irrecuperable.", - "delete-customers-title": "¿Eliminar { count, plural, =1 {1 cliente} other {# clientes} }?", - "delete-customers-action-title": "Borrar { count, plural, =1 {1 cliente} other {# clientes} }", - "delete-customers-text": "Atención, tras la confirmación todos los clientes seleccionados serán eliminados y su información relacionada será irrecuperable.", - "manage-users": "Gestionar usuarios", - "manage-assets": "Gestionar activos", - "manage-devices": "Gestionar dispositivos", - "manage-dashboards": "Gestionar paneles", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "Título debe ser menor de 256", - "description": "Descripción", - "details": "Detalles", - "events": "Eventos", - "copyId": "Copiar ID de cliente", - "idCopiedMessage": "El ID de cliente se ha copiado al portapapeles", - "select-customer": "Seleccionar Cliente", - "no-customers-matching": "No se han encontrado clientes que coincidan con '{{entity}}' .", - "customer-required": "Cliente requerido", - "select-default-customer": "Seleccionar cliente por defecto", - "default-customer": "Cliente por defecto", - "default-customer-required": "Se requiere cliente por defecto para realizar debu a nivel de propietario", - "search": "Buscar clientes", - "selected-customers": "{ count, plural, =1 {1 cliente} other {# clientes} } seleccionados", - "edges": "Instancias de Edge del cliente", - "manage-edges": "Administrar Edges" + "condition": "Condición", + "condition-type": "Tipo de condición", + "condition-type-simple": "Simple", + "condition-type-duration": "Duración", + "condition-during": "Durante {{during}}", + "condition-during-dynamic": "Durante \"{{ attribute }}\" ({{during}})", + "condition-type-repeating": "Repetitiva", + "condition-type-required": "Se requiere tipo de condición.", + "condition-repeating-value": "Cantidad de eventos", + "condition-repeating-value-range": "La cantidad de eventos debe estar entre 1 y 2147483647.", + "condition-repeating-value-pattern": "La cantidad de eventos debe ser un número entero.", + "condition-repeating-value-required": "Se requiere la cantidad de eventos.", + "condition-repeat-times": "Se repite { count, plural, =1 {1 vez} other {# veces} }", + "condition-repeat-times-dynamic": "Se repite \"{ attribute }\" ({ count, plural, =1 {1 vez} other {# veces} })", + "schedule-type": "Tipo de programación", + "schedule-type-required": "Se requiere tipo de programación.", + "schedule": "Horario", + "edit-schedule": "Editar programación de la alarma", + "schedule-any-time": "Activo todo el tiempo", + "schedule-specific-time": "Activo en un horario específico", + "schedule-custom": "Personalizado", + "schedule-day": { + "monday": "Lunes", + "tuesday": "Martes", + "wednesday": "Miércoles", + "thursday": "Jueves", + "friday": "Viernes", + "saturday": "Sábado", + "sunday": "Domingo" }, - "date": { - "last-update-n-ago": "Última actualización hace N", - "last-update-n-ago-text": "Última actualización {{ agoText }}", - "custom-date": "Fecha personalizada", - "format": "Formato", - "preview": "Previsualizar" + "schedule-days": "Días", + "schedule-time": "Hora", + "schedule-time-from": "Desde", + "schedule-time-to": "Hasta", + "schedule-days-of-week-required": "Se debe seleccionar al menos un día de la semana.", + "create-device-profile": "Crear nuevo perfil de dispositivo", + "import": "Importar perfil de dispositivo", + "export": "Exportar perfil de dispositivo", + "export-failed-error": "No se pudo exportar el perfil de dispositivo: {{error}}", + "device-profile-file": "Archivo del perfil de dispositivo", + "invalid-device-profile-file-error": "No se pudo importar el perfil de dispositivo: estructura de datos inválida.", + "power-saving-mode": "Modo de ahorro de energía", + "power-saving-mode-type": { + "default": "Usar modo de ahorro de energía del perfil del dispositivo", + "psm": "Modo de ahorro de energía (PSM)", + "drx": "Recepción discontinua (DRX)", + "edrx": "Recepción discontinua extendida (eDRX)" }, - "datetime": { - "date-from": "Fecha desde", - "time-from": "Hora desde", - "date-to": "Fecha hasta", - "time-to": "Hora hasta" + "edrx-cycle": "Ciclo eDRX", + "edrx-cycle-required": "Se requiere el ciclo eDRX.", + "edrx-cycle-pattern": "El ciclo eDRX debe ser un número entero positivo.", + "edrx-cycle-min": "El número mínimo del ciclo eDRX es {{ min }} segundos.", + "paging-transmission-window": "Ventana de transmisión de paginación", + "paging-transmission-window-required": "Se requiere la ventana de transmisión de paginación.", + "paging-transmission-window-pattern": "La ventana de transmisión debe ser un número entero positivo.", + "paging-transmission-window-min": "El mínimo de la ventana de transmisión de paginación es {{ min }} segundos.", + "psm-activity-timer": "Temporizador de actividad PSM", + "psm-activity-timer-required": "Se requiere el temporizador de actividad PSM.", + "psm-activity-timer-pattern": "El temporizador de actividad PSM debe ser un número entero positivo.", + "psm-activity-timer-min": "El número mínimo del temporizador de actividad PSM es {{ min }} segundos.", + "lwm2m": { + "object-list": "Lista de objetos", + "object-list-empty": "No se han seleccionado objetos.", + "no-objects-found": "No se encontraron objetos.", + "no-objects-matching": "No se encontraron objetos que coincidan con '{{object}}'.", + "model-tab": "Modelo LWM2M", + "add-new-instances": "Agregar nuevas instancias", + "instances-list": "Lista de instancias", + "instances-list-required": "Se requiere la lista de instancias.", + "instance-id-pattern": "El ID de instancia debe ser un número entero positivo.", + "instance-id-max": "Valor máximo de ID de instancia {{max}}.", + "instance": "Instancia", + "resource-label": "#ID Nombre del recurso", + "observe-label": "Observar", + "attribute-label": "Atributo", + "telemetry-label": "Telemetría", + "edit-observe-select": "Para editar observación seleccione telemetría o atributo", + "edit-attributes-select": "Para editar atributos seleccione telemetría o atributo", + "no-attributes-set": "No se han configurado atributos", + "key-name": "Nombre de la clave", + "key-name-required": "Se requiere el nombre de la clave", + "attribute-name": "Nombre del atributo", + "attribute-name-required": "Se requiere el nombre del atributo.", + "attribute-value": "Valor del atributo", + "attribute-value-required": "Se requiere el valor del atributo.", + "attribute-value-pattern": "El valor del atributo debe ser un número entero positivo.", + "edit-attributes": "Editar atributos: {{ name }}", + "view-attributes": "Ver atributos: {{ name }}", + "add-attribute": "Agregar atributo", + "edit-attribute": "Editar atributo", + "view-attribute": "Ver atributo", + "remove-attribute": "Eliminar atributo", + "delete-server-text": "Ten cuidado, después de la confirmación la configuración del servidor será irrecuperable.", + "delete-server-title": "¿Estás seguro de que deseas eliminar el servidor?", + "mode": "Modo de configuración de seguridad", + "bootstrap-tab": "Bootstrap", + "bootstrap-server-legend": "Servidor Bootstrap (ShortId...)", + "lwm2m-server-legend": "Servidor LwM2M (ShortId...)", + "server": "Servidor", + "short-id": "ID corto del servidor", + "short-id-tooltip": "ID corto del servidor. Se utiliza como enlace para asociar una instancia del objeto servidor.\nEste identificador identifica de forma única cada servidor LwM2M configurado para el cliente LwM2M.\nEl recurso DEBE establecerse cuando el recurso Bootstrap-Server tenga el valor 'false'.\nLos valores ID:0 y ID:65535 NO DEBEN usarse para identificar al servidor LwM2M.", + "short-id-tooltip-bootstrap": "ID corto del servidor. Se utiliza como enlace para asociar una instancia del objeto servidor.\nEste identificador identifica de forma única cada servidor LwM2M configurado para el cliente LwM2M.\nEl recurso DEBE establecerse cuando el recurso Bootstrap-Server tenga el valor 'false'.", + "short-id-required": "Se requiere el ID corto del servidor.", + "short-id-range": "El ID corto del servidor debe estar en un rango de {{ min }} a {{ max }}.", + "short-id-pattern": "El ID corto del servidor debe ser un número entero positivo.", + "lifetime": "Tiempo de vida del registro del cliente", + "lifetime-required": "Se requiere el tiempo de vida del registro del cliente.", + "lifetime-pattern": "El tiempo de vida del registro debe ser un número entero positivo.", + "default-min-period": "Período mínimo entre dos notificaciones (s)", + "default-min-period-tooltip": "El valor predeterminado que debe usar el cliente LwM2M para el Período Mínimo de una Observación en ausencia de este parámetro.", + "default-min-period-required": "Se requiere el período mínimo.", + "default-min-period-pattern": "El período mínimo debe ser un número entero positivo.", + "notification-storing": "Almacenamiento de notificaciones cuando está deshabilitado o fuera de línea", + "binding": "Asociación", + "binding-type": { + "u": "U: El cliente es accesible a través de UDP en cualquier momento.", + "m": "M: El cliente es accesible a través de MQTT en cualquier momento.", + "h": "H: El cliente es accesible a través de HTTP en cualquier momento.", + "t": "T: El cliente es accesible a través de TCP en cualquier momento.", + "s": "S: El cliente es accesible a través de SMS en cualquier momento.", + "n": "N: El cliente DEBE enviar la respuesta a dicha solicitud a través de una vinculación Non-IP (admitido desde LWM2M 1.1).", + "uq": "UQ: Conexión UDP en modo en cola (no admitido desde LWM2M 1.1)", + "uqs": "UQS: conexiones UDP y SMS activas; UDP en modo en cola, SMS en modo estándar (no admitido desde LWM2M 1.1)", + "tq": "TQ: Conexión TCP en modo en cola (no admitido desde LWM2M 1.1)", + "tqs": "TQS: conexiones TCP y SMS activas; TCP en modo en cola, SMS en modo estándar (no admitido desde LWM2M 1.1)", + "sq": "SQ: Conexión SMS en modo en cola (no admitido desde LWM2M 1.1)" + }, + "binding-tooltip": "Esta es la lista en el recurso \"binding\" del objeto del servidor LwM2M - /1/x/7.\nIndica los modos de asociación admitidos en el Cliente LwM2M.\nEste valor DEBERÍA ser igual al valor en el recurso “Supported Binding and Modes” en el Objeto de Dispositivo (/3/0/16).\nAunque se admiten múltiples transportes, solo uno puede usarse durante toda la sesión de transporte.\nPor ejemplo, si se admiten UDP y SMS, el Cliente y el Servidor LwM2M pueden elegir comunicarse a través de UDP o SMS durante toda la sesión de transporte.", + "bootstrap-server": "Servidor Bootstrap", + "lwm2m-server": "Servidor LwM2M", + "include-bootstrap-server": "Incluir actualizaciones del servidor Bootstrap", + "bootstrap-update-title": "Ya tienes configurado un Servidor Bootstrap. ¿Estás seguro de que deseas excluir las actualizaciones?", + "bootstrap-update-text": "Ten cuidado, después de la confirmación los datos de configuración del servidor Bootstrap serán irrecuperables.", + "server-host": "Host", + "server-host-required": "Se requiere el host.", + "server-port": "Puerto", + "server-port-required": "Se requiere el puerto.", + "server-port-pattern": "El puerto debe ser un número entero positivo.", + "server-port-range": "El puerto debe estar en un rango de 1 a 65535.", + "server-public-key": "Clave pública del servidor", + "server-public-key-required": "Se requiere la clave pública del servidor.", + "client-hold-off-time": "Tiempo de espera del cliente", + "client-hold-off-time-required": "Se requiere el tiempo de espera del cliente.", + "client-hold-off-time-pattern": "El tiempo de espera debe ser un número entero positivo.", + "client-hold-off-time-tooltip": "Tiempo de espera del cliente para usar solo con el Servidor Bootstrap", + "account-after-timeout": "Cuenta después del tiempo de espera", + "account-after-timeout-required": "Se requiere la cuenta después del tiempo de espera.", + "account-after-timeout-pattern": "La cuenta después del tiempo de espera debe ser un número entero positivo.", + "account-after-timeout-tooltip": "Cuenta del Servidor Bootstrap después del valor de tiempo de espera proporcionado por este recurso.", + "server-type": "Tipo de servidor", + "add-new-server-title": "Agregar nueva configuración de servidor", + "add-server-config": "Agregar configuración de servidor", + "add-lwm2m-server-config": "Agregar servidor LwM2M", + "no-config-servers": "No hay servidores configurados", + "others-tab": "Otras configuraciones", + "client-strategy": "Estrategia del cliente al conectarse", + "client-strategy-label": "Estrategia", + "client-strategy-only-observe": "Solo enviar solicitud Observe al cliente después de la conexión inicial", + "client-strategy-read-all": "Leer todos los recursos y enviar solicitud Observe al cliente después del registro", + "fw-update": "Actualización de firmware", + "fw-update-strategy": "Estrategia de actualización de firmware", + "fw-update-strategy-data": "Enviar actualización de firmware como archivo binario usando Objeto 19 y Recurso 0 (Data)", + "fw-update-strategy-package": "Enviar actualización de firmware como archivo binario usando Objeto 5 y Recurso 0 (Package)", + "fw-update-strategy-package-uri": "Generar automáticamente URL única de CoAP para descargar el paquete y enviar actualización usando Objeto 5 y Recurso 1 (Package URI)", + "sw-update": "Actualización de software", + "sw-update-strategy": "Estrategia de actualización de software", + "sw-update-strategy-package": "Enviar archivo binario usando Objeto 9 y Recurso 2 (Package)", + "sw-update-strategy-package-uri": "Generar automáticamente URL única de CoAP para descargar el paquete y enviar actualización usando Objeto 9 y Recurso 3 (Package URI)", + "fw-update-resource": "Recurso CoAP para actualización de firmware", + "fw-update-resource-required": "Se requiere el recurso CoAP para actualización de firmware.", + "sw-update-resource": "Recurso CoAP para actualización de software", + "sw-update-resource-required": "Se requiere el recurso CoAP para actualización de software.", + "config-json-tab": "Configuración Json del perfil del dispositivo", + "attributes-name": { + "min-period": "Período mínimo", + "max-period": "Período máximo", + "greater-than": "Mayor que", + "less-than": "Menor que", + "step": "Paso", + "min-evaluation-period": "Período mínimo de evaluación", + "max-evaluation-period": "Período máximo de evaluación" + }, + "default-object-id": "Versión de objeto predeterminado (Atributo)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1" + } }, - "dashboard": { - "dashboard": "Panel", - "dashboards": "Paneles", - "management": "Gestión de Paneles", - "view-dashboards": "Ver Paneles", - "add": "Agregar Panel", - "assign-dashboard-to-customer": "Asignar panel(es) a cliente", - "assign-dashboard-to-customer-text": "Por favor, seleccione algún panel para asignar al Cliente.", - "assign-dashboard-to-edge-title": "Asignar panel(es) a Edge", - "assign-to-customer-text": "Por favor, seleccione algún cliente para asignar al(los) panel(es).", - "assign-to-customer": "Asignar a cliente", - "unassign-from-customer": "Desasignar del cliente", - "make-public": "Hacer panel público", - "make-private": "Hacer panel privado", - "manage-assigned-customers": "Administrar clientes asignados", - "assigned-customers": "Clientes asignados", - "assign-to-customers": "Asignar Panel / Paneles a Clientes", - "assign-to-customers-text": "Selecciona los clientes para asignar los paneles", - "unassign-from-customers": "Desasignar Panel / Paneles de clientes", - "unassign-from-customers-text": "Selecciona los clientes para desasignar los paneles", - "no-dashboards-text": "Ningún panel encontrado", - "no-widgets": "Ningún widget configurado", - "add-widget": "Agregar nuevo widget", - "add-widget-button-text": "Agregar widget", - "title": "Título", - "image": "Imagen de panel", - "mobile-app-settings": "Ajustes de aplicación móvil", - "mobile-order": "Órden de paneles en aplicación móvil", - "mobile-hide": "Ocultar panel en aplicación móvil", - "update-image": "Actualizar imagen de panel", - "take-screenshot": "Captura de pantalla", - "select-widget-title": "Seleccionar widget", - "select-widget-value": "{{title}}: seleccionar widget", - "select-widget-subtitle": "Lista de tipos de widgets disponibles", - "delete": "Eliminar panel", - "title-required": "Título requerido.", - "title-max-length": "Título debe ser menor de 256", - "description": "Descripción", - "details": "Detalles", - "dashboard-details": "Detalles del panel", - "add-dashboard-text": "Agregar nuevo panel", - "assign-dashboards": "Asignar paneles", - "assign-new-dashboard": "Asignar nuevo panel", - "assign-dashboards-text": "Asignar { count, plural, =1 {1 panel} other {# paneles} } al cliente", - "unassign-dashboards-action-text": "Desasignar { count, plural, =1 {1 panel} other {# paneles} } a los clientes", - "delete-dashboards": "Eliminar paneles", - "unassign-dashboards": "Desasignar paneles", - "unassign-dashboards-action-title": "Desasignar { count, plural, =1 {1 paneles} other {# paneles} } del cliente", - "delete-dashboard-title": "¿Eliminar el panel '{{dashboardTitle}}'?", - "delete-dashboard-text": "Atención, el panel seleccionado será eliminado y la información relacionada sera irrecuperable.", - "delete-dashboards-title": "¿Eliminar { count, plural, =1 {1 panel} other {# paneles} }?", - "delete-dashboards-action-title": "Eliminar { count, plural, =1 {1 panel} other {# paneles} }", - "delete-dashboards-text": "Atención, los paneles seleccionados serán eliminados y la información relacionada será irrecuperable.", - "unassign-dashboard-title": "¿Desasignar el panel '{{dashboardTitle}}'?", - "unassign-dashboard-text": "Tras la confirmación, el panel será desasignado y no podrá ser accesible por el cliente.", - "unassign-dashboard": "Desasignar panel", - "unassign-dashboards-title": "¿Desasignar { count, plural, =1 {1 panel} other {# paneles} }?", - "unassign-dashboards-text": "Atención, tras la confirmación los paneles seleccionados serán desasignados y no podrán ser accesibles por el cliente.", - "public-dashboard-title": "El panel ahora es público", - "public-dashboard-text": "Tu panel {{dashboardTitle}} es ahora público y podrá ser accedido desde: aquí:", - "public-dashboard-notice": "Nota: No olvides hacer públicos los dispositivos relacionados para acceder a sus datos.", - "make-private-dashboard-title": "¿Hacer el panel '{{dashboardTitle}}' privado?", - "make-private-dashboard-text": "Tras la confirmación, el panel será privado y no podrá ser accesible por otros.", - "make-private-dashboard": "Hacer panel privado", - "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", - "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", - "select-dashboard": "Seleccionar panel", - "no-dashboards-matching": "Panel '{{entity}}' no encontrado.", - "dashboard-required": "Panel requerido.", - "select-existing": "Seleccionar paneles existentes", - "create-new": "Crear nuevo panel", - "new-dashboard-title": "Nuevo título", - "open-dashboard": "Abrir panel", - "set-background": "Definir fondo", - "background-color": "Color de fondo", - "background-image": "Imagen de fondo", - "background-size-mode": "Modo tamaño de fondo", - "no-image": "No se ha seleccionado ningúna imagen", - "empty-image": "Sin imagen", - "drop-image": "Suelte una imagen o haga clic para seleccionar un archivo para cargar.", - "maximum-upload-file-size": "Tamaño máximo de fichero: {{ size }}", - "cannot-upload-file": "No es posible subir el fichero", - "settings": "Ajustes", - "columns-count": "Número de columnas", - "columns-count-required": "Número de columnas requerido.", - "min-columns-count-message": "Solo se permite un número mínimo de 10 columnas.", - "max-columns-count-message": "Solo se permite un número máximo de 1000 columnas.", - "widgets-margins": "Margen entre widgets", - "margin-required": "Valor de margen requerido.", - "min-margin-message": "0 es el valor de margen mínimo permitido.", - "max-margin-message": "50 es el valor de margen máximo permitido.", - "horizontal-margin": "Margen horizontal", - "horizontal-margin-required": "Margen horizontal requerido.", - "min-horizontal-margin-message": "Solo se permite margen horizontal mínimo de 0.", - "max-horizontal-margin-message": "Solo se permite margen horizontal máximo de 50.", - "vertical-margin": "Margen vertical", - "vertical-margin-required": "Margen vertical requerido.", - "min-vertical-margin-message": "Solo se permite margen vertical mínimo de 0.", - "max-vertical-margin-message": "Solo se permite margen vertical máximo de 50.", - "apply-outer-margin": "Aplicar márgen en los lados del diseño", - "autofill-height": "Altura diseño auto relleno", - "mobile-layout": "Ajustes del diseño móvil", - "mobile-row-height": "Altura de fila para móvil, px", - "mobile-row-height-required": "Altura de fila requerida.", - "min-mobile-row-height-message": "Sólo se permiten 5 píxeles como altura mínima de fila (móvil).", - "max-mobile-row-height-message": "Sólo se permiten 200 píxeles como altura máxima de fila (móvil).", - "title-settings": "Ajustes de título", - "display-title": "Mostrar título del panel", - "title-color": "Color del título", - "toolbar-settings": "Ajustes de la barra de herramientas", - "hide-toolbar": "Ocultar barra de herramientas", - "toolbar-always-open": "Mantener la barra de herramientas abierta", - "display-dashboards-selection": "Mostrar selección de paneles", - "display-entities-selection": "Mostrar selección de entidades", - "display-filters": "Mostrar filtros", - "display-dashboard-timewindow": "Mostrar ventana de tiempo", - "display-dashboard-export": "Mostrar exportar", - "display-update-dashboard-image": "Mostrar actualización de imagen", - "dashboard-logo-settings": "Ajustes del logotipo del panel", - "display-dashboard-logo": "Mostrar logotipo en pantalla completa", - "dashboard-logo-image": "Imagen logotipo", - "advanced-settings": "Ajustes avanzados", - "dashboard-css": "CSS del panel", - "import": "Importar panel", - "export": "Exportar panel", - "export-failed-error": "Imposible exportar panel: {{error}}", - "create-new-dashboard": "Crear nuevo panel", - "dashboard-file": "Archivar panel", - "invalid-dashboard-file-error": "Imposible importar panel: Estructura de datos inválida.", - "dashboard-import-missing-aliases-title": "Configurar alias utilizados por el panel importado", - "create-new-widget": "Crear nuevo widget", - "import-widget": "Importar widget", - "widget-file": "Archivo de widget", - "invalid-widget-file-error": "Imposible importar widget: Estructura de datos inválida.", - "widget-import-missing-aliases-title": "Configurar alias utilizados por el widget", - "open-toolbar": "Abrir toolbar del panel", - "close-toolbar": "Cerrar toolbar", - "configuration-error": "Error de configuración", - "alias-resolution-error-title": "Error de configuración de alias del panel", - "invalid-aliases-config": "No se puede encontrar ningún dispositivo que coincida con algunos de los alias de filtro.
Póngase en contacto con su administrador para resolver este problema.", - "select-devices": "Seleccionar dispositivos", - "assignedToCustomer": "Asignado al cliente", - "assignedToCustomers": "Asignado a los clientes", - "public": "Público", - "copyId": "Copiar ID del panel", - "idCopiedMessage": "El ID del panel ha sido copiado al portapapeles", - "public-link": "Link público", - "copy-public-link": "Copiar link público", - "public-link-copied-message": "El link público del panel se ha copiado al portapapeles", - "manage-states": "Administrar estados de paneles", - "states": "Estados de paneles", - "states-short": "Estados", - "search-states": "Buscar estados de paneles", - "selected-states": "{ count, plural, =1 {1 estado panel} other {# estado paneles} } seleccionados", - "edit-state": "Editar estado panel", - "delete-state": "Borrar estado panel", - "add-state": "Añadir estado panel", - "no-states-text": "No se han encontrado estados", - "state": "Estado de panel", - "state-name": "Nombre", - "state-name-required": "Se requiere nombre del estado.", - "state-id": "ID Estado", - "state-id-required": "Se requiere el ID de estado.", - "state-id-exists": "Ya existe un ID de estado.", - "is-root-state": "Estado raiz(Root)", - "delete-state-title": "Borrar estado de panel", - "delete-state-text": "Eliminar el estado de panel con nombre: '{{stateName}}'?", - "show-details": "Mostrar detalles", - "hide-details": "Ocultar detalles", - "select-state": "Seleccionar estado destino (target state)", - "state-controller": "Controlador de estados", - "search": "Buscar paneles", - "selected-dashboards": "{count, plural, =1 {1 panel} other {# paneles} } seleccionados", - "home-dashboard": "Panel principal", - "home-dashboard-hide-toolbar": "Ocultar barra de herramientas en panel principal", - "unassign-dashboard-from-edge-text": "Después de la confirmación, el tablero no será asignado y el Edge no podrá acceder a él", - "unassign-dashboards-from-edge-title": "Estas seguro de desasignar { count, plural, =1 {1 panel} other {# paneles} }?", - "unassign-dashboards-from-edge-text": "Después de la confirmación, se anulará la asignación de todos los paneles seleccionados y no serán accesibles por de Edge", - "assign-dashboard-to-edge": "Asignar panel(es) al Edge", - "assign-dashboard-to-edge-text": "Por favor selecciona los paneles para asignar al Edge", - "non-existent-dashboard-state-error": "El panel con id de estado \"{{ stateId }}\" no se ha encontrado", - "edit-mode": "Modo Edición" + "snmp": { + "add-communication-config": "Agregar configuración de comunicación", + "add-mapping": "Agregar mapeo", + "authentication-passphrase": "Frase de autenticación", + "authentication-passphrase-required": "Se requiere la frase de autenticación.", + "authentication-protocol": "Protocolo de autenticación", + "authentication-protocol-required": "Se requiere el protocolo de autenticación.", + "communication-configs": "Configuraciones de comunicación", + "community": "Cadena de comunidad", + "community-required": "Se requiere la cadena de comunidad.", + "context-name": "Nombre de contexto", + "data-key": "Clave de datos", + "data-key-required": "Se requiere la clave de datos.", + "data-type": "Tipo de datos", + "data-type-required": "Se requiere el tipo de datos.", + "engine-id": "ID del motor", + "host": "Host", + "host-required": "Se requiere el host.", + "oid": "OID", + "oid-pattern": "Formato de OID inválido.", + "oid-required": "Se requiere el OID.", + "please-add-communication-config": "Por favor, agregue una configuración de comunicación", + "please-add-mapping-config": "Por favor, agregue una configuración de mapeo", + "port": "Puerto", + "port-format": "Formato de puerto inválido.", + "port-required": "Se requiere el puerto.", + "privacy-passphrase": "Frase de privacidad", + "privacy-passphrase-required": "Se requiere la frase de privacidad.", + "privacy-protocol": "Protocolo de privacidad", + "privacy-protocol-required": "Se requiere el protocolo de privacidad.", + "protocol-version": "Versión del protocolo", + "protocol-version-required": "Se requiere la versión del protocolo.", + "querying-frequency": "Frecuencia de consulta, ms", + "querying-frequency-invalid-format": "La frecuencia de consulta debe ser un número entero positivo.", + "querying-frequency-required": "Se requiere la frecuencia de consulta.", + "retries": "Reintentos", + "retries-invalid-format": "Los reintentos deben ser un número entero positivo.", + "retries-required": "Se requieren los reintentos.", + "scope": "Alcance", + "scope-required": "Se requiere el alcance.", + "security-name": "Nombre de seguridad", + "security-name-required": "Se requiere el nombre de seguridad.", + "timeout-ms": "Tiempo de espera, ms", + "timeout-ms-invalid-format": "El tiempo de espera debe ser un número entero positivo.", + "timeout-ms-required": "Se requiere el tiempo de espera.", + "user-name": "Nombre de usuario", + "user-name-required": "Se requiere el nombre de usuario." + } + }, + "dialog": { + "close": "Cerrar diálogo", + "error-message-title": "Mensaje de error:", + "error-details-title": "Detalles del error" + }, + "direction": { + "column": "Columna", + "row": "Fila" + }, + "edge": { + "edge": "Edge", + "edge-instances": "Instancias de edge", + "instances": "Instancias", + "edge-file": "Archivo de edge", + "name-max-length": "El nombre debe tener menos de 256 caracteres", + "label-max-length": "La etiqueta debe tener menos de 256 caracteres", + "type-max-length": "El tipo debe tener menos de 256 caracteres", + "management": "Gestión de Edge", + "no-edges-matching": "No se encontraron edges que coincidan con '{{entity}}'.", + "add": "Agregar edge", + "no-edges-text": "No se encontraron edges", + "edge-details": "Detalles del edge", + "add-edge-text": "Agregar nuevo edge", + "delete": "Eliminar edge", + "delete-edge-title": "¿Está seguro de que desea eliminar el edge '{{edgeName}}'?", + "delete-edge-text": "Tenga cuidado, después de la confirmación el edge y todos los datos relacionados serán irrecuperables.", + "delete-edges-title": "¿Está seguro de que desea eliminar { count, plural, =1 {1 edge} other {# edges} }?", + "delete-edges-text": "Tenga cuidado, después de la confirmación todos los edges seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "name": "Nombre", + "name-starts-with": "Nombre del edge empieza con", + "name-required": "El nombre es obligatorio.", + "description": "Descripción", + "details": "Detalles", + "events": "Eventos", + "copy-id": "Copiar ID del edge", + "id-copied-message": "El ID del edge se ha copiado al portapapeles", + "sync": "Sincronizar edge", + "edge-required": "El edge es obligatorio", + "edge-type": "Tipo de edge", + "edge-type-required": "Se requiere el tipo de edge.", + "event-action": "Acción del evento", + "entity-id": "ID de entidad", + "select-edge-type": "Seleccionar tipo de edge", + "assign-to-customer": "Asignar al cliente", + "assign-to-customer-text": "Por favor, seleccione el cliente para asignar el(los) edge(s)", + "assign-edge-to-customer": "Asignar edge(s) al cliente", + "assign-edge-to-customer-text": "Por favor, seleccione los edges para asignar al cliente", + "assignedToCustomer": "Asignado al cliente", + "edge-public": "El edge es público", + "assigned-to-customer": "Asignado a: {{customerTitle}}", + "unassign-from-customer": "Desasignar del cliente", + "unassign-edge-title": "¿Está seguro de que desea desasignar el edge '{{edgeName}}'?", + "unassign-edge-text": "Después de la confirmación, el edge se desasignará y no será accesible por el cliente.", + "unassign-edges-title": "¿Está seguro de que desea desasignar { count, plural, =1 {1 edge} other {# edges} }?", + "unassign-edges-text": "Después de la confirmación, todos los edges seleccionados se desasignarán y no serán accesibles por el cliente.", + "make-public": "Hacer público el edge", + "make-public-edge-title": "¿Está seguro de que desea hacer público el edge '{{edgeName}}'?", + "make-public-edge-text": "Después de la confirmación, el edge y todos sus datos se harán públicos y serán accesibles por otros.", + "make-private": "Hacer privado el edge", + "public": "Público", + "make-private-edge-title": "¿Está seguro de que desea hacer privado el edge '{{edgeName}}'?", + "make-private-edge-text": "Después de la confirmación, el edge y todos sus datos se harán privados y no serán accesibles por otros.", + "import": "Importar edge", + "install-connect-instructions": "Instrucciones de instalación y conexión", + "install-connect-instructions-edge-created": "¡Edge creado! Revise las instrucciones de instalación y conexión", + "loading-edge-instructions": "Cargando instrucciones del edge...", + "label": "Etiqueta", + "load-entity-error": "Error al cargar los datos. La entidad ha sido eliminada.", + "assign-new-edge": "Asignar nuevo edge", + "unassign-from-edge": "Desasignar del edge", + "edge-key": "Clave del edge", + "copy-edge-key": "Copiar clave del edge", + "edge-key-copied-message": "La clave del edge se ha copiado al portapapeles", + "edge-secret": "Secreto del edge", + "copy-edge-secret": "Copiar secreto del edge", + "edge-secret-copied-message": "El secreto del edge se ha copiado al portapapeles", + "manage-assets": "Gestionar activos", + "manage-devices": "Gestionar dispositivos", + "manage-entity-views": "Gestionar vistas de entidad", + "manage-dashboards": "Gestionar tableros", + "manage-rulechains": "Gestionar cadenas de reglas", + "assets": "Activos del edge", + "devices": "Dispositivos del edge", + "entity-views": "Vistas de entidad del edge", + "dashboard": "Tablero del edge", + "dashboards": "Tableros del edge", + "rulechain-templates": "Plantillas de cadenas de reglas", + "edge-rulechain-templates": "Plantillas de cadenas de reglas del edge", + "rulechains": "Cadenas de reglas del edge", + "search": "Buscar edges", + "selected-edges": "{ count, plural, =1 {1 edge} other {# edges} } seleccionados", + "any-edge": "Cualquier edge", + "no-edge-types-matching": "No se encontraron tipos de edge que coincidan con '{{entitySubtype}}'.", + "edge-type-list-empty": "No se seleccionaron tipos de edge.", + "edge-types": "Tipos de edge", + "enter-edge-type": "Introducir tipo de edge", + "deployed": "Desplegado", + "pending": "Pendiente", + "downlinks": "Downlinks", + "no-downlinks-prompt": "No se encontraron downlinks", + "sync-process-started-successfully": "¡Proceso de sincronización iniciado con éxito!", + "missing-related-rule-chains-title": "El edge tiene cadenas de reglas relacionadas faltantes", + "missing-related-rule-chains-text": "Las cadenas de reglas asignadas al edge usan nodos de reglas que envían mensajes a cadenas de reglas que no están asignadas a este edge.

Lista de cadenas de reglas faltantes:
{{missingRuleChains}}", + "widget-datasource-error": "Este widget solo admite fuente de datos de entidad EDGE", + "upgrade-instructions": "Instrucciones de actualización", + "connected": "Conectado", + "disconnected": "Desconectado" + }, + "edge-event": { + "type-dashboard": "Tablero", + "type-asset": "Activo", + "type-device": "Dispositivo", + "type-device-profile": "Perfil de dispositivo", + "type-asset-profile": "Perfil de activo", + "type-entity-view": "Vista de entidad", + "type-alarm": "Alarma", + "type-rule-chain": "Cadena de reglas", + "type-rule-chain-metadata": "Metadatos de cadena de reglas", + "type-edge": "Edge", + "type-user": "Usuario", + "type-tenant": "Inquilino", + "type-tenant-profile": "Perfil de inquilino", + "type-customer": "Cliente", + "type-relation": "Relación", + "type-widgets-bundle": "Paquete de widgets", + "type-widgets-type": "Tipo de widget", + "type-admin-settings": "Configuraciones de administrador", + "type-ota-package": "Paquete OTA", + "type-queue": "Cola", + "action-type-added": "Agregado", + "action-type-deleted": "Eliminado", + "action-type-updated": "Actualizado", + "action-type-post-attributes": "Publicar atributos", + "action-type-attributes-updated": "Atributos actualizados", + "action-type-attributes-deleted": "Atributos eliminados", + "action-type-timeseries-updated": "Serie temporal actualizada", + "action-type-credentials-updated": "Credenciales actualizadas", + "action-type-assigned-to-customer": "Asignado al cliente", + "action-type-unassigned-from-customer": "Desasignado del cliente", + "action-type-relation-add-or-update": "Relación agregada o actualizada", + "action-type-relation-deleted": "Relación eliminada", + "action-type-rpc-call": "Llamada RPC", + "action-type-alarm-ack": "Reconocimiento de alarma", + "action-type-alarm-clear": "Alarma borrada", + "action-type-alarm-assigned": "Alarma asignada", + "action-type-alarm-unassigned": "Alarma desasignada", + "action-type-assigned-to-edge": "Asignado al edge", + "action-type-unassigned-from-edge": "Desasignado del edge", + "action-type-credentials-request": "Solicitud de credenciales", + "action-type-entity-merge-request": "Solicitud de fusión de entidad" + }, + "error": { + "unable-to-connect": "¡No se puede conectar al servidor! Por favor, verifique su conexión a Internet.", + "unhandled-error-code": "Código de error no gestionado: {{errorCode}}", + "unknown-error": "Error desconocido" + }, + "entity": { + "entity": "Entidad", + "entities": "Entidades", + "entities-count": "Cantidad de entidades", + "alarms-count": "Cantidad de alarmas", + "aliases": "Alias de entidad", + "aliases-short": "Alias", + "entity-alias": "Alias de entidad", + "unable-delete-entity-alias-title": "No se puede eliminar el alias de entidad", + "unable-delete-entity-alias-text": "El alias de entidad '{{entityAlias}}' no se puede eliminar ya que lo utilizan los siguientes widgets:
{{widgetsList}}", + "duplicate-alias-error": "Alias duplicado encontrado '{{alias}}'.
Los alias de entidad deben ser únicos dentro del tablero.", + "missing-entity-filter-error": "Falta el filtro para el alias '{{alias}}'.", + "configure-alias": "Configurar alias '{{alias}}'", + "alias": "Alias", + "alias-required": "El alias de entidad es obligatorio.", + "remove-alias": "Eliminar alias de entidad", + "add-alias": "Agregar alias de entidad", + "edit-alias": "Editar alias de entidad", + "entity-list": "Lista de entidades", + "entity-type": "Tipo de entidad", + "entity-types": "Tipos de entidad", + "entity-type-list": "Lista de tipos de entidad", + "any-entity": "Cualquier entidad", + "add-entity-type": "Agregar tipo de entidad", + "enter-entity-type": "Ingrese el tipo de entidad", + "no-entities-matching": "No se encontraron entidades que coincidan con '{{entity}}'.", + "no-entities-text": "No se encontraron entidades", + "no-entity-types-matching": "No se encontraron tipos de entidad que coincidan con '{{entityType}}'.", + "name-starts-with": "Expresión de nombre", + "help-text": "Use '%' según sea necesario: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter": "Usar filtro", + "entity-list-empty": "No hay entidades seleccionadas.", + "entity-type-list-required": "Se debe seleccionar al menos un tipo de entidad.", + "entity-name-filter-required": "Se requiere el filtro de nombre de entidad.", + "entity-name-filter-no-entity-matched": "No se encontraron entidades que comiencen con '{{entity}}'.", + "all-subtypes": "Todos", + "select-entities": "Seleccionar entidades", + "no-aliases-found": "No se encontraron alias.", + "no-alias-matching": "'{{alias}}' no encontrado.", + "create-new-alias": "¡Cree uno nuevo!", + "create-new": "Crear nuevo", + "key": "Clave", + "key-name": "Nombre de la clave", + "no-keys-found": "No se encontraron claves.", + "no-key-matching": "'{{key}}' no encontrado.", + "create-new-key": "¡Cree uno nuevo!", + "type": "Tipo", + "type-required": "El tipo de entidad es obligatorio.", + "type-device": "Dispositivo", + "type-devices": "Dispositivos", + "list-of-devices": "{ count, plural, =1 {Un dispositivo} other {Lista de # dispositivos} }", + "device-name-starts-with": "Dispositivos cuyos nombres comienzan con '{{prefix}}'", + "type-device-profile": "Perfil de dispositivo", + "type-device-profiles": "Perfiles de dispositivo", + "clear-selected-profiles": "Borrar perfiles seleccionados", + "list-of-device-profiles": "{ count, plural, =1 {Un perfil de dispositivo} other {Lista de # perfiles de dispositivo} }", + "device-profile-name-starts-with": "Perfiles de dispositivo cuyos nombres comienzan con '{{prefix}}'", + "type-asset-profile": "Perfil de activo", + "type-asset-profiles": "Perfiles de activo", + "list-of-asset-profiles": "{ count, plural, =1 {Un perfil de activo} other {Lista de # perfiles de activo} }", + "asset-profile-name-starts-with": "Perfiles de activo cuyos nombres comienzan con '{{prefix}}'", + "type-asset": "Activo", + "type-assets": "Activos", + "list-of-assets": "{ count, plural, =1 {Un activo} other {Lista de # activos} }", + "asset-name-starts-with": "Activos cuyos nombres comienzan con '{{prefix}}'", + "type-entity-view": "Vista de entidad", + "type-entity-views": "Vistas de entidad", + "list-of-entity-views": "{ count, plural, =1 {Una vista de entidad} other {Lista de # vistas de entidad} }", + "entity-view-name-starts-with": "Vistas de entidad cuyos nombres comienzan con '{{prefix}}'", + "type-rule": "Regla", + "type-rules": "Reglas", + "list-of-rules": "{ count, plural, =1 {Una regla} other {Lista de # reglas} }", + "rule-name-starts-with": "Reglas cuyos nombres comienzan con '{{prefix}}'", + "type-plugin": "Plugin", + "type-plugins": "Plugins", + "list-of-plugins": "{ count, plural, =1 {Un plugin} other {Lista de # plugins} }", + "plugin-name-starts-with": "Plugins cuyos nombres comienzan con '{{prefix}}'", + "type-tenant": "Inquilino", + "type-tenants": "Inquilinos", + "list-of-tenants": "{ count, plural, =1 {Un inquilino} other {Lista de # inquilinos} }", + "tenant-name-starts-with": "Inquilinos cuyos nombres comienzan con '{{prefix}}'", + "type-tenant-profile": "Perfil de inquilino", + "type-tenant-profiles": "Perfiles de inquilino", + "list-of-tenant-profiles": "{ count, plural, =1 {Un perfil de inquilino} other {Lista de # perfiles de inquilino} }", + "tenant-profile-name-starts-with": "Perfiles de inquilino cuyos nombres comienzan con '{{prefix}}'", + "type-customer": "Cliente", + "type-customers": "Clientes", + "list-of-customers": "{ count, plural, =1 {Un cliente} other {Lista de # clientes} }", + "customer-name-starts-with": "Clientes cuyos nombres comienzan con '{{prefix}}'", + "type-user": "Usuario", + "type-users": "Usuarios", + "list-of-users": "{ count, plural, =1 {Un usuario} other {Lista de # usuarios} }", + "user-name-starts-with": "Usuarios cuyos nombres comienzan con '{{prefix}}'", + "type-dashboard": "Tablero", + "type-dashboards": "tableros", + "list-of-dashboards": "{ count, plural, =1 {Un tablero} other {Lista de # tableros} }", + "dashboard-name-starts-with": "Tableros cuyos nombres comienzan con '{{prefix}}'", + "type-alarm": "Alarma", + "type-alarms": "Alarmas", + "list-of-alarms": "{ count, plural, =1 {Una alarma} other {Lista de # alarmas} }", + "alarm-name-starts-with": "Alarmas cuyos nombres comienzan con '{{prefix}}'", + "type-rulechain": "Cadena de reglas", + "type-rulechains": "Cadenas de reglas", + "list-of-rulechains": "{ count, plural, =1 {Una cadena de reglas} other {Lista de # cadenas de reglas} }", + "rulechain-name-starts-with": "Cadenas de reglas cuyos nombres comienzan con '{{prefix}}'", + "type-rulenode": "Nodo de regla", + "type-rulenodes": "Nodos de regla", + "list-of-rulenodes": "{ count, plural, =1 {Un nodo de regla} other {Lista de # nodos de regla} }", + "rulenode-name-starts-with": "Nodos de regla cuyos nombres comienzan con '{{prefix}}'", + "type-current-customer": "Cliente actual", + "type-current-tenant": "Inquilino actual", + "type-current-user": "Usuario actual", + "type-current-user-owner": "Propietario del usuario actual", + "type-calculated-field": "Campo calculado", + "type-calculated-fields": "Campos calculados", + "type-widgets-bundle": "Paquete de widgets", + "type-widgets-bundles": "Paquetes de widgets", + "list-of-widgets-bundles": "{ count, plural, =1 {Un paquete de widgets} other {Lista de # paquetes de widgets} }", + "type-widget": "Widget", + "type-widgets": "Widgets", + "list-of-widgets": "{ count, plural, =1 {Un widget} other {Lista de # widgets} }", + "search": "Buscar entidades", + "selected-entities": "{ count, plural, =1 {1 entidad} other {# entidades} } seleccionadas", + "entity-name": "Nombre de la entidad", + "entity-label": "Etiqueta de la entidad", + "details": "Detalles de la entidad", + "no-entities-prompt": "No se encontraron entidades", + "no-data": "No hay datos para mostrar", + "columns-to-display": "Columnas para mostrar", + "type-api-usage-state": "Estado de uso de API", + "type-edge": "Edge", + "type-edges": "Edges", + "list-of-edges": "{ count, plural, =1 {Un edge} other {Lista de # edges} }", + "edge-name-starts-with": "Edges cuyos nombres comienzan con '{{prefix}}'", + "version-conflict": { + "message": "¿Desea sobrescribir la versión existente o descartar los cambios y cargar la última versión?", + "link": "Puede descargar su versión de {{entityType}} usando este", + "overwrite": "Sobrescribir versión", + "discard": "Descartar cambios" }, - "datakey": { - "settings": "Ajustes", - "general": "General", - "advanced": "Avanzado", - "key": "Clave", - "label": "Etiqueta", - "color": "Color", - "units": "Símbolo especial para mostrar junto con el valor", - "decimals": "Número de dígitos después de la coma", - "data-generation-func": "Función de generación de datos", - "use-data-post-processing-func": "Usar funcíon de post-procesamiendo de datos", - "configuration": "Ajustes de clave de datos", - "timeseries": "Serie de tiempos", - "attributes": "Atributos", - "entity-field" : "Campo de entidad", - "alarm": "Campos de alarma", - "timeseries-required": "Series de tiempo del dispositivo requerido.", - "timeseries-or-attributes-required": "Series de tiempo/Atributos requeridos.", - "alarm-fields-timeseries-or-attributes-required": "Se requieren campos de alarma o series de tiempo/atributos.", - "maximum-timeseries-or-attributes": "Máximo { count, plural, =1 {1 timeseries/atributo es permitido.} other {# timeseries/atributos son permitidos} }", - "alarm-fields-required": "Campos de alarma requeridos.", - "function-types": "Tipos de funciones", - "function-type": "Tipos de función", - "function-types-required": "Tipos de funciones requerido.", - "data-keys": "Claves de datos", - "data-key": "Clave de datos", - "data-keys-required": "Se requieren claves de datos.", - "data-key-required": "Se requiere clave de datos.", - "alarm-keys": "Claves de Alarmas", - "alarm-key": "Clave de Slarma", - "alarm-key-functions": "Funciones de claves de Alarmas", - "alarm-key-function": "Función de clave de Alarma", - "latest-keys": "Últimas claves", - "latest-key": "Última clave", - "latest-key-functions": "Funciones de últimas claves", - "latest-key-function": "Función de última clave", - "timeseries-keys": "Claves de series de tiempo", - "timeseries-key": "Clave de series de tiempo", - "timeseries-key-functions": "Funciones de series de tiempo", - "timeseries-key-function": "Función de serie de tiempo", - "maximum-function-types": "Máximo { count, plural, =1 {1 tipo de función está permitida.} other {# tipos de funciones están permitidos} }", - "time-description": "hora del valor actual", - "value-description": "el valor actual", - "prev-value-description": "resultado de la llamada anterior de la función", - "time-prev-description": "hora del valor previo", - "prev-orig-value-description": "valor original previo", - "aggregation": "Agregación", - "aggregation-type-hint-common": "Por motivos de rendimiento, el cálculo de valores agregados solamente está disponible para intervalos de tiempo fijos como \"día actual\", \"mes actual\", etc, y no está disponible para ventanas de tiempo móviles, como 'últimos 30 minutos' o 'últimas 24 horas'.", - "aggregation-type-none-hint": "Tomar último valor.", - "aggregation-type-min-hint": "Encuentra el valor mínimo de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "aggregation-type-max-hint": "Encuentra el valor máximo de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "aggregation-type-avg-hint": "Calcula el valor medio de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "aggregation-type-sum-hint": "Suma todos los valores de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "aggregation-type-count-hint": "Cuenta total de los puntos de datos contenidos en la ventana de tiempo seleccionada.", - "delta-calculation": "Cálculo delta", - "enable-delta-calculation": "Activar cálculo delta", - "enable-delta-calculation-hint": "Cuando se activa, el valor se calcula basado en los valores agregados de la ventana de tiempo seleccionada y el período de comparación seleccionado. Por razones de rendimiento, el cálculo delta solamente está disponible en las series históricas y no para los valores en tiempo real. Por ejemplo, puedes calcular el delta entre la energía consumida ayer y la energía consumida anteayer.", - "delta-calculation-result": "Resultado de cálculo delta", - "delta-calculation-result-previous-value": "Valor anterior", - "delta-calculation-result-delta-absolute": "Delta (absoluto)", - "delta-calculation-result-delta-percent": "Delta (percentil)", - "source": "Origen", - "latest": "Último", - "latest-value": "Último valor", - "delta": "delta", - "percent": "porcentaje", - "absolute": "absoluto" + "type-tb-resource": "Recurso", + "type-tb-resources": "Recursos", + "list-of-tb-resources": "{ count, plural, =1 {Un recurso} other {Lista de # recursos} }", + "type-ota-package": "Paquete OTA", + "type-rpc": "RPC", + "type-queue": "Cola", + "type-queue-stats": "Estadísticas de cola", + "type-queues-stats": "Estadísticas de colas", + "type-notification": "Notificación", + "type-notification-rule": "Regla de notificación", + "type-notification-rules": "Reglas de notificación", + "list-of-notification-rules": "{ count, plural, =1 {Una regla de notificación} other {Lista de # reglas de notificación} }", + "type-notification-target": "Destinatario de notificación", + "type-notification-targets": "Destinatarios de notificación", + "list-of-notification-targets": "{ count, plural, =1 {Un destinatario de notificación} other {Lista de # destinatarios de notificación} }", + "type-notification-request": "Solicitud de notificación", + "type-notification-template": "Plantilla de notificación", + "type-notification-templates": "Plantillas de notificación", + "list-of-notification-templates": "{ count, plural, =1 {Una plantilla de notificación} other {Lista de # plantillas de notificación} }", + "link": "enlace", + "type-oauth2-client": "Cliente OAuth 2.0", + "type-oauth2-clients": "Clientes OAuth 2.0", + "list-of-oauth2-clients": "{ count, plural, =1 {Un cliente OAuth 2.0} other {Lista de # clientes OAuth 2.0} }", + "type-domain": "Dominio", + "type-domains": "Dominios", + "list-of-domains": "{ count, plural, =1 {Un dominio} other {Lista de # dominios} }", + "type-mobile-app": "Aplicación móvil", + "type-mobile-apps": "Aplicaciones móviles", + "list-of-mobile-apps": "{ count, plural, =1 {Una aplicación móvil} other {Lista de # aplicaciones móviles} }", + "type-mobile-app-bundle": "Paquete móvil", + "type-mobile-app-bundles": "Paquetes móviles", + "list-of-mobile-app-bundles": "{ count, plural, =1 {Un paquete móvil} other {Lista de # paquetes móviles} }" + }, + "entity-field": { + "created-time": "Hora de creación", + "name": "Nombre", + "type": "Tipo", + "first-name": "Nombre", + "last-name": "Apellido", + "email": "Correo electrónico", + "title": "Título", + "country": "País", + "state": "Estado", + "city": "Ciudad", + "address": "Dirección", + "address2": "Dirección 2", + "zip": "Código postal", + "phone": "Teléfono", + "label": "Etiqueta", + "queue-name": "Nombre de la cola", + "service-id": "ID del servicio", + "owner-name": "Nombre del propietario", + "owner-type": "Tipo de propietario" + }, + "entity-view": { + "entity-view": "Vista de entidad", + "entity-view-required": "La vista de entidad es obligatoria.", + "entity-views": "Vistas de entidad", + "management": "Gestión de vistas de entidad", + "view-entity-views": "Ver vistas de entidad", + "entity-view-alias": "Alias de vista de entidad", + "aliases": "Alias de vistas de entidad", + "no-alias-matching": "'{{alias}}' no encontrado.", + "no-aliases-found": "No se encontraron alias.", + "no-key-matching": "'{{key}}' no encontrado.", + "no-keys-found": "No se encontraron claves.", + "create-new-alias": "¡Crear uno nuevo!", + "create-new-key": "¡Crear uno nuevo!", + "duplicate-alias-error": "Se encontró un alias duplicado '{{alias}}'.
Los alias de vistas de entidad deben ser únicos dentro del tablero.", + "configure-alias": "Configurar alias '{{alias}}'", + "no-entity-views-matching": "No se encontraron vistas de entidad que coincidan con '{{entity}}'.", + "public": "Público", + "alias": "Alias", + "alias-required": "El alias de vista de entidad es obligatorio.", + "remove-alias": "Eliminar alias de vista de entidad", + "add-alias": "Agregar alias de vista de entidad", + "name-starts-with": "Expresión del nombre de la vista de entidad", + "help-text": "Use '%' según sea necesario: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list": "Lista de vistas de entidad", + "use-entity-view-name-filter": "Usar filtro", + "entity-view-list-empty": "No se seleccionaron vistas de entidad.", + "entity-view-name-filter-required": "Se requiere un filtro de nombre de vista de entidad.", + "entity-view-name-filter-no-entity-view-matched": "No se encontraron vistas de entidad que comiencen con '{{entityView}}'.", + "add": "Agregar vista de entidad", + "entity-view-public": "La vista de entidad es pública", + "assign-to-customer": "Asignar al cliente", + "assign-entity-view-to-customer": "Asignar vista(s) de entidad al cliente", + "assign-entity-view-to-customer-text": "Seleccione las vistas de entidad que desea asignar al cliente", + "no-entity-views-text": "No se encontraron vistas de entidad", + "assign-to-customer-text": "Seleccione el cliente al que asignar la(s) vista(s) de entidad", + "entity-view-details": "Detalles de la vista de entidad", + "add-entity-view-text": "Agregar nueva vista de entidad", + "delete": "Eliminar vista de entidad", + "assign-entity-views": "Asignar vistas de entidad", + "assign-entity-views-text": "Asignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } al cliente", + "delete-entity-views": "Eliminar vistas de entidad", + "make-public": "Hacer pública la vista de entidad", + "make-private": "Hacer privada la vista de entidad", + "unassign-from-customer": "Desasignar del cliente", + "unassign-entity-views": "Desasignar vistas de entidad", + "unassign-entity-views-action-title": "Desasignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } del cliente", + "assign-new-entity-view": "Asignar nueva vista de entidad", + "delete-entity-view-title": "¿Está seguro de que desea eliminar la vista de entidad '{{entityViewName}}'?", + "delete-entity-view-text": "Tenga cuidado, después de la confirmación, la vista de entidad y todos los datos relacionados serán irrecuperables.", + "delete-entity-views-title": "¿Está seguro de que desea eliminar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", + "delete-entity-views-action-title": "Eliminar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }", + "delete-entity-views-text": "Tenga cuidado, después de la confirmación, todas las vistas de entidad seleccionadas serán eliminadas y todos los datos relacionados serán irrecuperables.", + "make-public-entity-view-title": "¿Está seguro de que desea hacer pública la vista de entidad '{{entityViewName}}'?", + "make-public-entity-view-text": "Después de la confirmación, la vista de entidad y todos sus datos serán públicos y accesibles por otros.", + "make-private-entity-view-title": "¿Está seguro de que desea hacer privada la vista de entidad '{{entityViewName}}'?", + "make-private-entity-view-text": "Después de la confirmación, la vista de entidad y todos sus datos serán privados y no estarán accesibles por otros.", + "unassign-entity-view-title": "¿Está seguro de que desea desasignar la vista de entidad '{{entityViewName}}'?", + "unassign-entity-view-text": "Después de la confirmación, la vista de entidad será desasignada y no estará accesible por el cliente.", + "unassign-entity-view": "Desasignar vista de entidad", + "unassign-entity-views-title": "¿Está seguro de que desea desasignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", + "unassign-entity-views-text": "Después de la confirmación, todas las vistas de entidad seleccionadas serán desasignadas y no estarán accesibles por el cliente.", + "entity-view-type": "Tipo de vista de entidad", + "entity-view-type-required": "El tipo de vista de entidad es obligatorio.", + "select-entity-view-type": "Seleccionar tipo de vista de entidad", + "enter-entity-view-type": "Ingresar tipo de vista de entidad", + "any-entity-view": "Cualquier vista de entidad", + "no-entity-view-types-matching": "No se encontraron tipos de vista de entidad que coincidan con '{{entitySubtype}}'.", + "entity-view-type-list-empty": "No se seleccionaron tipos de vista de entidad.", + "entity-view-types": "Tipos de vista de entidad", + "created-time": "Hora de creación", + "name": "Nombre", + "name-required": "El nombre es obligatorio.", + "name-max-length": "El nombre debe tener menos de 256 caracteres.", + "type-max-length": "El tipo de vista de entidad debe tener menos de 256 caracteres.", + "description": "Descripción", + "events": "Eventos", + "details": "Detalles", + "copyId": "Copiar ID de la vista de entidad", + "idCopiedMessage": "El ID de la vista de entidad se ha copiado al portapapeles", + "assignedToCustomer": "Asignado al cliente", + "unable-entity-view-device-alias-title": "No se puede eliminar el alias de la vista de entidad", + "unable-entity-view-device-alias-text": "El alias del dispositivo '{{entityViewAlias}}' no puede eliminarse porque lo usan los siguientes widgets:
{{widgetsList}}", + "select-entity-view": "Seleccionar vista de entidad", + "start-ts": "Hora de inicio", + "end-ts": "Hora de fin", + "date-limits": "Límites de fecha", + "client-attributes": "Atributos del cliente", + "shared-attributes": "Atributos compartidos", + "server-attributes": "Atributos del servidor", + "timeseries": "Series temporales", + "client-attributes-placeholder": "Atributos del cliente", + "shared-attributes-placeholder": "Atributos compartidos", + "server-attributes-placeholder": "Atributos del servidor", + "timeseries-placeholder": "Series temporales", + "target-entity": "Entidad objetivo", + "attributes-propagation": "Propagación de atributos", + "attributes-propagation-hint": "La vista de entidad copiará automáticamente los atributos especificados desde la entidad objetivo cada vez que guarde o actualice esta vista de entidad. Por razones de rendimiento, los atributos de la entidad objetivo no se propagan a la vista de entidad en cada cambio. Puede habilitar la propagación automática configurando un nodo de reglas \"copy to view\" y vinculando los mensajes \"Post attributes\" y \"Attributes Updated\" al nuevo nodo de reglas.", + "timeseries-data": "Datos de series temporales", + "timeseries-data-hint": "Configure las claves de datos de series temporales de la entidad objetivo que estarán accesibles para la vista de entidad. Estos datos de series temporales son de solo lectura.", + "search": "Buscar vistas de entidad", + "selected-entity-views": "{ count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } seleccionadas", + "assign-entity-view-to-edge": "Asignar vista(s) de entidad al Edge", + "assign-entity-view-to-edge-text": "Seleccione las vistas de entidad para asignar al Edge", + "unassign-entity-view-from-edge-title": "¿Está seguro de que desea desasignar la vista de entidad '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text": "Después de la confirmación, la vista de entidad será desasignada y no estará accesible por el Edge.", + "unassign-entity-views-from-edge-action-title": "Desasignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } del Edge", + "unassign-entity-view-from-edge": "Desasignar vista de entidad", + "unassign-entity-views-from-edge-title": "¿Está seguro de que desea desasignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", + "unassign-entity-views-from-edge-text": "Después de la confirmación, todas las vistas de entidad seleccionadas serán desasignadas y no estarán accesibles por el Edge." + }, + "event": { + "event-type": "Tipo de evento", + "events-filter": "Filtro de eventos", + "clean-events": "Borrar eventos", + "type-error": "Error", + "type-lc-event": "Evento de ciclo de vida", + "type-stats": "Estadísticas", + "type-debug-rule-node": "Depuración", + "type-debug-rule-chain": "Depuración", + "type-debug-calculated-field": "Depuración", + "arguments": "Argumentos", + "result": "Resultado", + "no-events-prompt": "No se encontraron eventos", + "error": "Error", + "alarm": "Alarma", + "event-time": "Hora del evento", + "server": "Servidor", + "body": "Cuerpo", + "method": "Método", + "type": "Tipo", + "metadata": "Metadatos", + "message": "Mensaje", + "message-id": "ID del mensaje", + "copy-message-id": "Copiar ID del mensaje", + "message-type": "Tipo de mensaje", + "data-type": "Tipo de datos", + "relation-type": "Tipo de relación", + "data": "Datos", + "event": "Evento", + "status": "Estado", + "success": "Éxito", + "failed": "Fallido", + "messages-processed": "Mensajes procesados", + "max-messages-processed": "Máximo de mensajes procesados", + "min-messages-processed": "Mínimo de mensajes procesados", + "errors-occurred": "Errores ocurridos", + "max-errors-occurred": "Máximo de errores ocurridos", + "min-errors-occurred": "Mínimo de errores ocurridos", + "min-value": "El valor mínimo es 0.", + "all-events": "Todos", + "has-error": "Con error", + "entity-id": "ID de la entidad", + "copy-entity-id": "Copiar ID de la entidad", + "entity-type": "Tipo de entidad", + "clear-filter": "Limpiar filtro", + "clear-request-title": "Borrar todos los eventos", + "clear-request-text": "¿Está seguro de que desea borrar todos los eventos?", + "started": "Iniciado", + "updated": "Actualizado", + "stopped": "Detenido" + }, + "extension": { + "extensions": "Extensiones", + "selected-extensions": "{ count, plural, =1 {1 extensión} other {# extensiones} } seleccionadas", + "type": "Tipo", + "key": "Clave", + "value": "Valor", + "id": "Id", + "extension-id": "Id de la extensión", + "extension-type": "Tipo de extensión", + "transformer-json": "JSON *", + "unique-id-required": "El id de extensión actual ya existe.", + "delete": "Eliminar extensión", + "add": "Agregar extensión", + "edit": "Editar extensión", + "delete-extension-title": "¿Está seguro de que desea eliminar la extensión '{{extensionId}}'?", + "delete-extension-text": "Tenga cuidado, después de la confirmación la extensión y todos los datos relacionados serán irrecuperables.", + "delete-extensions-title": "¿Está seguro de que desea eliminar { count, plural, =1 {1 extensión} other {# extensiones} }?", + "delete-extensions-text": "Tenga cuidado, después de la confirmación todas las extensiones seleccionadas serán eliminadas.", + "converters": "Convertidores", + "converter-id": "Id del convertidor", + "configuration": "Configuración", + "converter-configurations": "Configuraciones de convertidor", + "token": "Token de seguridad", + "add-converter": "Agregar convertidor", + "add-config": "Agregar configuración de convertidor", + "device-name-expression": "Expresión de nombre de dispositivo", + "device-type-expression": "Expresión de tipo de dispositivo", + "custom": "Personalizado", + "to-double": "A doble", + "transformer": "Transformador", + "json-required": "Se requiere JSON del transformador.", + "json-parse": "No se puede analizar el JSON del transformador.", + "attributes": "Atributos", + "add-attribute": "Agregar atributo", + "add-map": "Agregar elemento de mapeo", + "timeseries": "Series temporales", + "add-timeseries": "Agregar serie temporal", + "field-required": "El campo es obligatorio", + "brokers": "Brokers", + "add-broker": "Agregar broker", + "host": "Host", + "port": "Puerto", + "port-range": "El puerto debe estar en el rango de 1 a 65535.", + "ssl": "Ssl", + "credentials": "Credenciales", + "username": "Nombre de usuario", + "password": "Contraseña", + "retry-interval": "Intervalo de reintento en milisegundos", + "anonymous": "Anónimo", + "basic": "Básico", + "pem": "PEM", + "ca-cert": "Archivo del certificado CA *", + "private-key": "Archivo de clave privada *", + "cert": "Archivo del certificado *", + "no-file": "Ningún archivo seleccionado.", + "drop-file": "Suelte un archivo o haga clic para seleccionar un archivo para subir.", + "mapping": "Mapeo", + "topic-filter": "Filtro de tema", + "converter-type": "Tipo de convertidor", + "converter-json": "Json", + "json-name-expression": "Expresión JSON del nombre del dispositivo", + "topic-name-expression": "Expresión del tema del nombre del dispositivo", + "json-type-expression": "Expresión JSON del tipo de dispositivo", + "topic-type-expression": "Expresión del tema del tipo de dispositivo", + "attribute-key-expression": "Expresión de la clave del atributo", + "attr-json-key-expression": "Expresión JSON de la clave del atributo", + "attr-topic-key-expression": "Expresión del tema de la clave del atributo", + "request-id-expression": "Expresión del ID de solicitud", + "request-id-json-expression": "Expresión JSON del ID de solicitud", + "request-id-topic-expression": "Expresión del tema del ID de solicitud", + "response-topic-expression": "Expresión del tema de respuesta", + "value-expression": "Expresión de valor", + "topic": "Tema", + "timeout": "Tiempo de espera en milisegundos", + "converter-json-required": "Se requiere JSON del convertidor.", + "converter-json-parse": "No se puede analizar el JSON del convertidor.", + "filter-expression": "Expresión de filtro", + "connect-requests": "Solicitudes de conexión", + "add-connect-request": "Agregar solicitud de conexión", + "disconnect-requests": "Solicitudes de desconexión", + "add-disconnect-request": "Agregar solicitud de desconexión", + "attribute-requests": "Solicitudes de atributos", + "add-attribute-request": "Agregar solicitud de atributo", + "attribute-updates": "Actualizaciones de atributos", + "add-attribute-update": "Agregar actualización de atributo", + "server-side-rpc": "RPC del lado del servidor", + "add-server-side-rpc-request": "Agregar solicitud de RPC del servidor", + "device-name-filter": "Filtro de nombre de dispositivo", + "attribute-filter": "Filtro de atributo", + "method-filter": "Filtro de método", + "request-topic-expression": "Expresión del tema de solicitud", + "response-timeout": "Tiempo de espera de respuesta en milisegundos", + "topic-expression": "Expresión del tema", + "client-scope": "Alcance del cliente", + "add-device": "Agregar dispositivo", + "opc-server": "Servidores", + "opc-add-server": "Agregar servidor", + "opc-add-server-prompt": "Por favor agregue un servidor", + "opc-application-name": "Nombre de la aplicación", + "opc-application-uri": "URI de la aplicación", + "opc-scan-period-in-seconds": "Periodo de escaneo en segundos", + "opc-security": "Seguridad", + "opc-identity": "Identidad", + "opc-keystore": "Almacén de claves", + "opc-type": "Tipo", + "opc-keystore-type": "Tipo", + "opc-keystore-location": "Ubicación *", + "opc-keystore-password": "Contraseña", + "opc-keystore-alias": "Alias", + "opc-keystore-key-password": "Contraseña de la clave", + "opc-device-node-pattern": "Patrón de nodo del dispositivo", + "opc-device-name-pattern": "Patrón de nombre del dispositivo", + "modbus-server": "Servidores/esclavos", + "modbus-add-server": "Agregar servidor/esclavo", + "modbus-add-server-prompt": "Por favor agregue servidor/esclavo", + "modbus-transport": "Transporte", + "modbus-tcp-reconnect": "Reconectar automáticamente", + "modbus-rtu-over-tcp": "RTU sobre TCP", + "modbus-port-name": "Nombre del puerto serie", + "modbus-encoding": "Codificación", + "modbus-parity": "Paridad", + "modbus-baudrate": "Velocidad en baudios", + "modbus-databits": "Bits de datos", + "modbus-stopbits": "Bits de parada", + "modbus-databits-range": "Los bits de datos deben estar en el rango de 7 a 8.", + "modbus-stopbits-range": "Los bits de parada deben estar en el rango de 1 a 2.", + "modbus-unit-id": "ID de unidad", + "modbus-unit-id-range": "El ID de unidad debe estar en el rango de 1 a 247.", + "modbus-device-name": "Nombre del dispositivo", + "modbus-poll-period": "Periodo de sondeo (ms)", + "modbus-attributes-poll-period": "Periodo de sondeo de atributos (ms)", + "modbus-timeseries-poll-period": "Periodo de sondeo de series temporales (ms)", + "modbus-poll-period-range": "El periodo de sondeo debe ser un valor positivo.", + "modbus-tag": "Etiqueta", + "modbus-function": "Función", + "modbus-register-address": "Dirección de registro", + "modbus-register-address-range": "La dirección del registro debe estar en el rango de 0 a 65535.", + "modbus-register-bit-index": "Índice de bit del registro", + "modbus-register-bit-index-range": "El índice de bit debe estar en el rango de 0 a 15.", + "modbus-register-count": "Cantidad de registros", + "modbus-register-count-range": "La cantidad de registros debe ser un valor positivo.", + "modbus-byte-order": "Orden de bytes", + "sync": { + "status": "Estado", + "sync": "Sincronizar", + "not-sync": "No sincronizado", + "last-sync-time": "Última sincronización", + "not-available": "No disponible" }, - "datasource": { - "type": "Típo de fuente de datos", - "name": "Nombre", - "label": "Etiqueta", - "add-datasource-prompt": "Por favor, agrega una fuente de datos" + "export-extensions-configuration": "Exportar configuración de extensiones", + "import-extensions-configuration": "Importar configuración de extensiones", + "import-extensions": "Importar extensiones", + "import-extension": "Importar extensión", + "export-extension": "Exportar extensión", + "file": "Archivo de extensiones", + "invalid-file-error": "Archivo de extensión no válido" + }, + "feature": { + "advanced-features": "Funciones avanzadas" + }, + "filter": { + "add": "Agregar filtro", + "edit": "Editar filtro", + "name": "Nombre del filtro", + "name-required": "El nombre del filtro es obligatorio.", + "duplicate-filter": "Ya existe un filtro con el mismo nombre.", + "filters": "Filtros", + "unable-delete-filter-title": "No se puede eliminar el filtro", + "unable-delete-filter-text": "El filtro '{{filter}}' no puede ser eliminado ya que está en uso por los siguientes widgets:
{{widgetsList}}", + "duplicate-filter-error": "Filtro duplicado '{{filter}}'.
Los filtros deben ser únicos dentro del tablero.", + "missing-key-filters-error": "Faltan filtros clave para el filtro '{{filter}}'.", + "filter": "Filtro", + "editable": "Editable", + "no-filters-found": "No se encontraron filtros.", + "no-filter-text": "Ningún filtro especificado", + "add-filter-prompt": "Por favor, agregue un filtro", + "no-filter-matching": "'{{filter}}' no encontrado.", + "create-new-filter": "¡Crear uno nuevo!", + "create-new": "Crear nuevo", + "filter-required": "El filtro es obligatorio.", + "operation": { + "operation": "Operación", + "equal": "igual", + "not-equal": "no igual", + "starts-with": "empieza con", + "ends-with": "termina con", + "contains": "contiene", + "not-contains": "no contiene", + "greater": "mayor que", + "less": "menor que", + "greater-or-equal": "mayor o igual", + "less-or-equal": "menor o igual", + "and": "y", + "or": "o", + "in": "en", + "not-in": "no en" }, - "details": { - "details": "Detalles", - "edit-mode": "Modo Edición", - "edit-json": "Editar JSON", - "toggle-edit-mode": "Ir a Modo Edición" + "ignore-case": "ignorar mayúsculas/minúsculas", + "value": "Valor", + "remove-filter": "Eliminar filtro", + "duplicate-filter-action": "Duplicar filtro", + "preview": "Vista previa del filtro", + "no-filters": "No hay filtros configurados", + "add-filter": "Agregar filtro", + "add-complex-filter": "Agregar filtro complejo", + "add-complex": "Agregar complejo", + "complex-filter": "Filtro complejo", + "edit-complex-filter": "Editar filtro complejo", + "edit-filter-user-params": "Editar parámetros del filtro del usuario", + "filter-user-params": "Parámetros del predicado del filtro", + "user-parameters": "Parámetros del usuario", + "display-label": "Etiqueta para mostrar", + "order-priority": "Prioridad de orden del campo", + "key-filter": "Filtro de clave", + "key-filters": "Filtros de clave", + "key-name": "Nombre de la clave", + "key-name-required": "El nombre de la clave es obligatorio.", + "key-type": { + "key-type": "Tipo de clave", + "attribute": "Atributo", + "timeseries": "Serie temporal", + "entity-field": "Campo de entidad", + "constant": "Constante", + "client-attribute": "Atributo del cliente", + "server-attribute": "Atributo del servidor", + "shared-attribute": "Atributo compartido" }, - "device": { - "device": "Dispositivo", - "device-required": "Dispositivo requerido.", - "devices": "Dispositivos", - "management": "Gestión de Dispositivos", - "view-devices": "Ver Dispositivos", - "device-alias": "Alias de dispositivo", - "device-type-max-length": "El tipo de dispositivo debe ser menor de 256", - "aliases": "Alias de dispositivos", - "no-alias-matching": "'{{alias}}' no encontrado.", - "no-aliases-found": "Ningún alias encontrado.", - "no-key-matching": "'{{key}}' no encontrado.", - "no-keys-found": "Ninguna clave encontrada.", - "create-new-alias": "Crear nuevo alias!", - "create-new-key": "Crear nueva clave!", - "duplicate-alias-error": "Alias duplicado '{{alias}}'.
El alias de los dispositivos deben ser únicos dentro del panel.", - "configure-alias": "Configurar alias '{{alias}}'", - "no-devices-matching": "No se encontró dispositivo '{{entity}}'", - "alias": "Alias", - "alias-required": "Alias de dispositivo requerido.", - "remove-alias": "Eliminar alias", - "add-alias": "Agregar alias", - "name-starts-with": "Nombre empieza con", - "help-text": "Usar '%' de acuerdo a las necesidades: '%nombre_dispositivo_contiene%', '%nombre_dispositivo_termina_en', 'nombre_dispositivo_empieza_con'.", - "device-list": "Lista de dispositivos", - "use-device-name-filter": "Usar filtro", - "device-list-empty": "Ningún dispositivo seleccionado.", - "device-name-filter-required": "Nombre de filtro requerido.", - "device-name-filter-no-device-matched": "Ningún dispositivo encontrado que comience con '{{device}}'.", - "add": "Agregar dispositivo", - "assign-to-customer": "Asignar a cliente", - "assign-device-to-customer": "Asignar dispositivo(s) a Cliente", - "assign-device-to-customer-text": "Por favor, seleccione los dispositivos que serán asignados al cliente", - "assign-device-to-edge-title": "Asignar Dispositivo(s) a Edge", - "assign-device-to-edge-text": "Selecciona los dispositivos a asignar al Edge", - "make-public": "Hacer dispositivo público", - "make-private": "Hacer dispositivo privado", - "no-devices-text": "Ningún dispositivo encontrado", - "assign-to-customer-text": "Por favor, seleccione el cliente para asignar el(los) dispositivo(s)", - "device-details": "Detalles del dispositivo", - "add-device-text": "Agregar nuevo dispositivo", - "credentials": "Credenciales", - "manage-credentials": "Gestionar credenciales", - "delete": "Eliminar dispositivo", - "assign-devices": "Asignar dispositivo", - "assign-devices-text": "Asignar { count, plural, =1 {1 dispositivo} other {# dispositivos} } al cliente", - "delete-devices": "Eliminar dispositivo", - "unassign-from-customer": "Desasignar del cliente", - "unassign-devices": "Desasignar dispositivos", - "unassign-devices-action-title": "Desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} } del cliente", - "unassign-device-from-edge-title": "¿Está seguro de que desea desasignar el dispositivo '{{deviceName}}'?", - "unassign-device-from-edge-text": "Después de la confirmación, el dispositivo no será asignado y el Edge no podrá acceder a él", - "unassign-devices-from-edge": "Desasignar dispositivos del Edge", - "assign-new-device": "Asignar nuevo dispositivo", - "make-public-device-title": "¿Hacer el dispositivo '{{deviceName}}' público?", - "make-public-device-text": "Tras la confirmación, el dispositivo y la información relacionada serán públicos y podrá ser accesible por otros.", - "make-private-device-title": "¿Hacer el dispositivo '{{deviceName}}' privado?", - "make-private-device-text": "Tras la confirmación, el dispositivo y la información relacionada serán privados y no podrá ser accesible por otros.", - "view-credentials": "Ver credenciales", - "delete-device-title": "¿Eliminar el dispositivo '{{deviceName}}'?", - "delete-device-text": "Atención, tras la confirmación los dispositivos serán eliminados y la información relacionada será irrecuperable.", - "delete-devices-title": "¿Eliminar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", - "delete-devices-action-title": "Eliminar { count, plural, =1 {1 dispositivo} other {# dispositivos} }", - "delete-devices-text": "Atención, tras la confirmación los dispositivos seleccionados serán eliminados y la información relacionada será irrecuperable.", - "unassign-device-title": "¿Desasignar el dispositivo '{{deviceName}}'?", - "unassign-device-text": "Tras la confirmación, el dispositivo será desasignado y no podrá ser accesible por el cliente.", - "unassign-device": "Desasignar dispositivo", - "unassign-devices-title": "¿Desasignar { count, plural, =1 {1 dispositivo} other {# dispositivos} }?", - "unassign-devices-text": "Tras la confirmación, los dispositivos seleccionados serán desasignados y no podrán ser accedidos por el cliente.", - "device-credentials": "Credenciales del dispositivo", - "loading-device-credentials": "Cargando credenciales del dispositivo...", - "credentials-type": "Tipo de credenciales", - "access-token": "Tóken de acceso", - "access-token-required": "Tóken de acceso requerido.", - "access-token-invalid": "Tóken de acceso debe tener entre 1 a 32 caracteres.", - "certificate-pem-format": "Certificado en formato PEM", - "certificate-pem-format-required": "Certificado requerido.", - "copy-access-token": "Copiar Access token", - "copy-certificate": "Copiar Certificado", - "copy-client-id": "Copiar ID Cliente", - "copy-user-name": "Copiar Nombre de usuario", - "copy-password": "Copiar Contraseña", - "generate-client-id": "Generar ID Cliente", - "generate-user-name": "Generar Nombre de usuario", - "generate-password": "Generar contraseña", - "generate-access-token": "Generar Access Token", - "lwm2m-security-config": { - "identity": "Identidad Cliente", - "identity-required": "Identidad Cliente requerida.", - "identity-tooltip": "La identidad PSK es un identificador PSK arbitrario hasta 128 bytes, como se describe en el estándar [RFC7925].\nEl identificador PSK DEBE ser convertido a un string y después codificado en octetos usando UTF-8.", - "client-key": "Clave Cliente", - "client-key-required": "Clave Cliente requerida.", - "client-key-tooltip-prk": "La clave RPK o id DEBE estar conforme al estándar [RFC7250] y codificada a un formato Base64!", - "client-key-tooltip-psk": "La clave PSK DEBE estar conforme al estándar [RFC4279] y en formato HexDec de: 32, 64 o 128 caracteres!", - "endpoint": "Nombre Endpoint Cliente", - "endpoint-required": "Nombre Endpoint requerido.", - "client-public-key": "Clave pública Cliente", - "client-public-key-hint": "Si la clave pública está vacía, se usará el certificado de confianza", - "client-public-key-tooltip": "La clave pública X509 debe estar codificada en formato DER X509v3 y soportar exclusivamente el algoritmo EC y codificada en formato Base64!", - "mode": "Modo de Seguridad", - "client-tab": "Configuración de Seguridad del cliente", - "client-certificate": "Certificado de Cliente", - "bootstrap-tab": "Cliente Bootstrap", - "bootstrap-server": "Servidor Bootstrap", - "lwm2m-server": "Servidor LwM2M", - "client-publicKey-or-id": "Id o clave pública de cliente", - "client-publicKey-or-id-required": "Id o clave pública requerida.", - "client-publicKey-or-id-tooltip-psk": "La clave pública PSK es un identificador PSK arbitrario hasta 128 bytes, como se describe en el estándar [RFC7925].\nEl identificador PSK DEBE ser convertido a un string y después codificado en octetos usando UTF-8.", - "client-publicKey-or-id-tooltip-rpk": "La clave pública RPK o id DEBE estar conforme al estándar [RFC7250] y codificada a un formato Base64!", - "client-publicKey-or-id-tooltip-x509": "La clave pública X509 debe estar codificada en formato DER X509v3 y soportar exclusivamente el algoritmo EC y codificada en formato Base64", - "client-secret-key": "Clave secreta de Cliente", - "client-secret-key-required": "Clave secreta requerida.", - "client-secret-key-tooltip-psk": "La clave PSK debe ser en el estándar [RFC4279] y en formato HexDec de: 32, 64 o 128 caracteres!", - "client-secret-key-tooltip-prk": "La clave RPK debe estar en formato PKCS_8 (Codificación DER, estándar [RFC5958]) y luego codificada en formato Base64!", - "client-secret-key-tooltip-x509": "La clave X509 debe estar en formato PKCS_8 (Codificación DER, estándar [RFC5958]) y luego codificada en formato Base64!" - }, - "client-id": "ID Cliente", - "client-id-pattern": "Contiene carácter inválido.", - "user-name": "Nombre Usuario", - "user-name-required": "Se requiere nombre de usuario.", - "client-id-or-user-name-necessary": "El ID Cliente y/o el Nombre de usuario son necesarios", - "password": "Contraseña", - "secret": "Secreto", - "secret-required": "Secreto requerido.", - "device-type": "Tipo de dispositivo", - "device-type-required": "Tipo de dispositivo requerido.", - "select-device-type": "Seleccionar tipo de dispositivo", - "enter-device-type": "Entrar tipo de dispositivo", - "any-device": "Cualquier dispositivo", - "no-device-types-matching": "No hay tipos de dispositivo que coincidan con '{{entitySubtype}}' .", - "device-type-list-empty": "No hay tipos de dispositivo seleccionados.", - "device-types": "Tipos de dispositivo", - "name": "Nombre", - "name-required": "El nombre es requerido.", - "name-max-length": "El nombre debe ser menor de 256", - "label-max-length": "La etiqueta debe ser menor de 256", - "description": "Descripción", - "label": "Etiqueta", - "events": "Eventos", - "details": "Detalles", - "copyId": "Copiar ID de dispositivo", - "copyAccessToken": "Copiar access token", - "copy-mqtt-authentication": "Copiar credenciales MQTT", - "idCopiedMessage": "Id del dispositivo copiado al portapapeles", - "accessTokenCopiedMessage": "Access token del dispositivo copiado al portapapeles", - "mqtt-authentication-copied-message": "Los datos de autenticación MQTT se han copiado al portapapeles", - "assignedToCustomer": "Asignado al cliente", - "unable-delete-device-alias-title": "Imposible eliminar alias del dispositivo", - "unable-delete-device-alias-text": "Alias '{{deviceAlias}}' no puede ser eliminado. Esta siendo usado por el(los) widget(s):
{{widgetsList}}", - "is-gateway": "Es gateway", - "overwrite-activity-time": "Sobreescribir hora de actividad para el dispositivo conectado", - "device-filter": "Filtro de dispositivo", - "device-filter-title": "Filtro de dispositivo", - "filter-title": "Filtro", - "device-state": "Estado de dispositivo", - "state": "Estado", - "any": "Cualquier", - "active": "Activo", - "inactive": "Inactivo", - "public": "Público", - "device-public": "El dispositivo es público", - "select-device": "Seleccionar dispositivo", - "import": "Importar dispositivo", - "device-file": "Archivo de dispositivo", - "search": "Buscar dispositivos", - "selected-devices": "{ count, plural, =1 {1 dispositivo} other {# dispositivos} } seleccionados", - "device-configuration": "Configuración del dispositivo", - "transport-configuration": "Configuración del transporte", - "wizard": { - "device-details": "Detalles del dispositivo" - }, - "unassign-devices-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, =1 {1 dispositivo} other {# dispositivos} }?", - "unassign-devices-from-edge-text": "Después de la confirmación, todos los dispositivos seleccionados quedarán sin asignar y el Edge no podrá acceder a ellos.", - "time": "Hora", - "connectivity": { - "check-connectivity": "Probar conectividad", - "device-created-check-connectivity": "Dispositivo creado. Prueba de conectividad.", - "loading-check-connectivity-command": "Cargando comandos...", - "use-following-instructions": "Usa las siguientes instrucciones para enviar telemetrías usando comandos shell", - "execute-following-command": "Ejecuta el comando siguietne", - "install-curl-windows": "Desde Windows 10 b17063, el comando cURL está disponible por defecto", - "install-curl-macos": "Desde Mac OS X 10.2 6C115 (Jaguar), el comando cURL está disponible por defecto", - "install-mqtt-windows": "Usa las instrucciones para descargar, instalar y configurar mosquitto_pub", - "install-coap-client": "Usa las instrucciones para descargar, instalar y configurar coap-client", - "install-necessary-client-tools": "Instala las herramientas de cliente", - "mqtts-x509-command": "Usa la documentación para conectar el dispositivo vía MQTT con autorización X509", - "coaps-x509-command": "Usa la documentación para conectar el dispositivo vía CoAP sobre DTLS con autorización X509", - "snmp-command": "Usa la siguiente documentación para conectar el dispositivo vía SNMP.", - "sparkplug-command": "Usa la siguiente documentación para conectar el dispositivo vía MQTT Sparkplug.", - "lwm2m-command": "Usa la siguiente documentación para conectar el dispositivo vía LWM2M." - } + "value-type": { + "value-type": "Tipo de valor", + "string": "Cadena", + "numeric": "Numérico", + "boolean": "Booleano", + "date-time": "Fecha y hora" }, - "asset-profile": { - "asset-profile": "Perfil de activo", - "asset-profiles": "Perfiles de activos", - "all-asset-profiles": "Todos", - "add": "Añadir perfil de activo", - "edit": "Editar perfil de activo", - "asset-profile-details": "Detalles de perfil de activo", - "no-asset-profiles-text": "No se encontraron perfiles de activo", - "search": "Buscar perfiles de activo", - "selected-asset-profiles": "{ count, plural, =1 {1 perfil} other {# perfiles} } seleccionados", - "no-asset-profiles-matching": "No se encontraron perfiles que coincidan con '{{entity}}'.", - "asset-profile-required": "Se requiere perfil de activo", - "idCopiedMessage": "El ID de perfil, ha sido copiado al portapapeles", - "set-default": "Hace perfil de activo por defecto", - "delete": "Borrar perfil de activo", - "copyId": "Copiar ID de perfil de activo", - "name-max-length": "El nombre debe ser menor de 256", - "new-device-profile-name": "Nombre de perfil de activo", - "new-device-profile-name-required": "Se requiere nombre de perfil", - "name": "Nombre", - "name-required": "Se requiere nombre", - "image": "Imagen de perfil de activo", - "description": "Descripción", - "default": "Por defecto", - "default-rule-chain": "Cadena de reglas por defecto", - "default-edge-rule-chain": "Cadena de reglas por defecto en Edge", - "default-edge-rule-chain-hint": "Usado en Edge, como cadena de reglas para procesas los datos recibidos para el perfil de activo.", - "mobile-dashboard": "Panel móvil", - "mobile-dashboard-hint": "Usado en la aplicación móvil, como un panel para los detalles del activo", - "select-queue-hint": "Selecciona desde el desplegable.", - "delete-asset-profile-title": "Quieres borrar el perfil de activo '{{assetProfileName}}'?", - "delete-asset-profile-text": "Atención, tras la confirmación, el perfil de activo será borrado y todas su información relacionada será irrecuperable.", - "delete-asset-profiles-title": "Quieres borrar { count, plural, =1 {1 perfil} other {# perfiles} }?", - "delete-asset-profiles-text": "Atención, tras la confirmación, todos los perfiles seleccionados serán borrados y su información relacionada será irrecuperable.", - "set-default-asset-profile-title": "Quieres establecer el perfil '{{assetProfileName}}' como perfil por defecto?", - "set-default-asset-profile-text": "Tras la confirmación, el perfil se marcara por defecto y será usada por los nuevos activos en los que no se especifique perfil.", - "no-asset-profiles-found": "No se han encontrado perfiles de activos.", - "create-new-asset-profile": "Crear uno nuevo!", - "create-asset-profile": "Crear un nuevo perfil de activo", - "import": "Importar perfil de activo", - "export": "Exportar perfil de activo", - "export-failed-error": "No ha sido posible exportar el perfil de activo: {{error}}", - "asset-profile-file": "Fichero de perfil de activo", - "invalid-asset-profile-file-error": "No ha sido posible importar el perfil de activo: Estructura de datos inválida." + "value-type-required": "Se requiere el tipo de valor de la clave.", + "key-value-type-change-title": "¿Está seguro de que desea cambiar el tipo de valor de la clave?", + "key-value-type-change-message": "Si confirma, todos los filtros de clave ingresados se eliminarán.", + "no-key-filters": "No hay filtros de clave configurados", + "add-key-filter": "Agregar filtro de clave", + "remove-key-filter": "Eliminar filtro de clave", + "edit-key-filter": "Editar filtro de clave", + "date": "Fecha", + "time": "Hora", + "current-tenant": "Tenant actual", + "current-customer": "Cliente actual", + "current-user": "Usuario actual", + "current-device": "Dispositivo actual", + "default-value": "Valor predeterminado", + "default-comma-separated-values": "Valores predeterminados separados por comas", + "dynamic-source-type": "Tipo de fuente dinámica", + "dynamic-value": "Valor dinámico", + "no-dynamic-value": "Sin valor dinámico", + "source-attribute": "Atributo de origen", + "switch-to-dynamic-value": "Cambiar a valor dinámico", + "switch-to-default-value": "Cambiar a valor predeterminado", + "inherit-owner": "Heredar del propietario", + "source-attribute-not-set": "Si el atributo de origen no está establecido" + }, + "fullscreen": { + "expand": "Expandir a pantalla completa", + "exit": "Salir de pantalla completa", + "toggle": "Alternar modo de pantalla completa", + "fullscreen": "Pantalla completa" + }, + "function": { + "function": "Función" + }, + "gateway": { + "gateway-name": "Nombre del gateway", + "gateway-name-required": "Se requiere el nombre del gateway.", + "gateways": "Gateways", + "create-new-gateway": "Crear nuevo gateway", + "create-new-gateway-text": "¿Está seguro de que desea crear un nuevo gateway con el nombre: '{{gatewayName}}'?", + "launch-command": "Comando de lanzamiento", + "no-gateway-found": "No se encontró ningún gateway.", + "no-gateway-matching": "'{{item}}' no encontrado." + }, + "grid": { + "delete-item-title": "¿Está seguro de que desea eliminar este elemento?", + "delete-item-text": "Tenga cuidado, después de la confirmación este elemento y todos los datos relacionados serán irrecuperables.", + "delete-items-title": "¿Está seguro de que desea eliminar { count, plural, =1 {1 elemento} other {# elementos} }?", + "delete-items-action-title": "Eliminar { count, plural, =1 {1 elemento} other {# elementos} }", + "delete-items-text": "Tenga cuidado, después de la confirmación todos los elementos seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "add-item-text": "Agregar nuevo elemento", + "no-items-text": "No se encontraron elementos", + "item-details": "Detalles del elemento", + "delete-item": "Eliminar elemento", + "delete-items": "Eliminar elementos", + "scroll-to-top": "Desplazar hacia arriba" + }, + "help": { + "goto-help-page": "Ir a la página de ayuda", + "show-help": "Mostrar ayuda" + }, + "home": { + "home": "Inicio", + "profile": "Perfil", + "logout": "Cerrar sesión", + "menu": "Menú", + "avatar": "Avatar", + "open-user-menu": "Abrir menú de usuario" + }, + "file-input": { + "browse-file": "Buscar archivo", + "browse-files": "Buscar archivos" + }, + "image": { + "gallery": "Galería de imágenes", + "search": "Buscar imagen", + "selected-images": "{ count, plural, =1 {1 imagen} other {# imágenes} } seleccionadas", + "created-time": "Hora de creación", + "name": "Nombre", + "name-required": "El nombre es obligatorio.", + "resolution": "Resolución", + "size": "Tamaño", + "system": "Sistema", + "download-image": "Descargar imagen", + "export-image": "Exportar imagen a JSON", + "import-image": "Importar imagen desde JSON", + "upload-image": "Subir imagen", + "edit-image": "Editar imagen", + "image-details": "Detalles de la imagen", + "no-images": "No se encontraron imágenes", + "delete-image": "Eliminar imagen", + "delete-image-title": "¿Está seguro de que desea eliminar la imagen '{{imageTitle}}'?", + "delete-image-text": "Tenga cuidado, después de la confirmación la imagen será irrecuperable.", + "delete-images-title": "¿Está seguro de que desea eliminar { count, plural, =1 {1 imagen} other {# imágenes} }?", + "delete-images-text": "Tenga cuidado, después de la confirmación todas las imágenes seleccionadas serán eliminadas y todos los datos relacionados serán irrecuperables.", + "list-mode": "Vista de lista", + "grid-mode": "Vista de cuadrícula", + "image-preview": "Vista previa de imagen", + "update-image": "Actualizar imagen", + "export-failed-error": "No se pudo exportar la imagen: {{error}}", + "image-json-file": "Archivo JSON de imagen", + "invalid-image-json-file-error": "No se pudo importar la imagen desde JSON: estructura de datos inválida.", + "image-is-in-use": "La imagen está siendo utilizada por otras entidades", + "images-are-in-use": "Las imágenes están siendo utilizadas por otras entidades", + "image-is-in-use-text": "La imagen '{{title}}' no fue eliminada porque está siendo utilizada por las siguientes entidades:", + "images-are-in-use-text": "No se eliminaron todas las imágenes porque están siendo utilizadas por otras entidades.
Puede ver las entidades referenciadas haciendo clic en el botón Referencias en la fila correspondiente.
Si aún desea eliminar estas imágenes, selecciónelas en la tabla inferior y haga clic en el botón Eliminar seleccionadas.", + "delete-image-in-use-text": "Si aún desea eliminar la imagen, haga clic en el botón Eliminar de todos modos.", + "system-entities": "Entidades del sistema:", + "entities": "entidades:", + "references": "Referencias", + "include-system-images": "Incluir imágenes del sistema", + "clear-image": "Borrar imagen", + "no-image": "Sin imagen", + "no-image-selected": "Ninguna imagen seleccionada", + "browse-from-gallery": "Buscar en la galería", + "set-link": "Establecer enlace", + "image-link": "Enlace de imagen", + "link": "Enlace", + "copy-image-link": "Copiar enlace de imagen", + "embed-image": "Incrustar imagen", + "embed-to-html": "Incrustar en HTML", + "embed-to-html-hint": "Esta función hará que el enlace esté disponible para cualquier usuario no autorizado.", + "embed-to-html-text": "Utilizando el siguiente fragmento de código, puede incrustar una imagen en componentes basados en HTML plano.
Estos componentes incluyen widgets de tarjeta HTML, funciones de contenido de celda, etc.", + "embed-to-angular-template": "Incrustar en plantilla HTML de Angular", + "embed-to-angular-template-text": "Utilizando el siguiente fragmento de código, puede incrustar una imagen en la plantilla HTML de Angular que será utilizada por los componentes.
Estos componentes incluyen el widget de Markdown, sección HTML en el editor de widgets, acciones personalizadas, etc." + }, + "image-input": { + "drop-images-or": "Arrastra y suelta imágenes o", + "drag-and-drop": "Arrastrar y soltar", + "or": "o", + "browse": "Buscar", + "no-images": "No hay imágenes seleccionadas", + "images": "imágenes" + }, + "import": { + "no-file": "No se ha seleccionado ningún archivo", + "drop-file": "Suelta un archivo JSON o haz clic para seleccionar un archivo para subir.", + "drop-json-file-or": "Arrastra y suelta un archivo JSON o", + "drop-file-csv": "Suelta un archivo CSV o haz clic para seleccionar un archivo para subir.", + "drop-file-csv-or": "Arrastra y suelta un archivo CSV o", + "column-value": "Valor", + "column-title": "Título", + "column-example": "Valor de ejemplo", + "column-key": "Clave de atributo/telemetría", + "credentials": "Credenciales", + "csv-delimiter": "Delimitador CSV", + "csv-first-line-header": "La primera línea contiene nombres de columnas", + "csv-update-data": "Actualizar atributos/telemetría", + "details": "Detalles", + "import-csv-number-columns-error": "Un archivo debe contener al menos dos columnas", + "import-csv-invalid-format-error": "Formato de archivo inválido. Línea: '{{line}}'", + "column-type": { + "name": "Nombre", + "type": "Tipo", + "label": "Etiqueta", + "column-type": "Tipo de columna", + "client-attribute": "Atributo del cliente", + "shared-attribute": "Atributo compartido", + "server-attribute": "Atributo del servidor", + "timeseries": "Serie temporal", + "entity-field": "Campo de entidad", + "access-token": "Token de acceso", + "x509": "X.509", + "mqtt": { + "client-id": "ID del cliente MQTT", + "user-name": "Nombre de usuario MQTT", + "password": "Contraseña MQTT" + }, + "lwm2m": { + "client-endpoint": "Nombre del cliente endpoint LwM2M", + "security-config-mode": "Modo de configuración de seguridad LwM2M", + "client-identity": "Identidad del cliente LwM2M", + "client-key": "Clave del cliente LwM2M", + "client-cert": "Clave pública del cliente LwM2M", + "bootstrap-server-security-mode": "Modo de seguridad del servidor bootstrap LwM2M", + "bootstrap-server-secret-key": "Clave secreta del servidor bootstrap LwM2M", + "bootstrap-server-public-key-id": "Clave pública o ID del servidor bootstrap LwM2M", + "lwm2m-server-security-mode": "Modo de seguridad del servidor LwM2M", + "lwm2m-server-secret-key": "Clave secreta del servidor LwM2M", + "lwm2m-server-public-key-id": "Clave pública o ID del servidor LwM2M" + }, + "snmp": { + "host": "Host SNMP", + "port": "Puerto SNMP", + "version": "Versión SNMP (v1, v2c o v3)", + "community-string": "Cadena de comunidad SNMP" + }, + "isgateway": "Es Gateway", + "activity-time-from-gateway-device": "Hora de actividad desde dispositivo gateway", + "description": "Descripción", + "routing-key": "Clave del Edge", + "secret": "Secreto del Edge" }, - "device-profile": { - "device-profile": "Perfil de dispositivo", - "device-profiles": "Perfiles de dispositivo", - "all-device-profiles": "Todos", - "add": "Añadir perfil de dispositivo", - "edit": "Editar perfil de dispositivo", - "device-profile-details": "Detalles de perfil de dispositivo", - "no-device-profiles-text": "No se encontraron perfiles", - "search": "Buscar perfiles", - "selected-device-profiles": "{ count, plural, =1 {1 perfil} other {# perfiles} } seleccionados", - "no-device-profiles-matching": "No existe perfil que conincida con '{{entity}}'.", - "device-profile-required": "Se requiere perfil de dispositivo", - "idCopiedMessage": "Se ha copiado el ID de perfil al portapapeles", - "set-default": "Hacer perfil por defecto", - "delete": "Borrar perfil de dispositivo", - "copyId": "Copiar ID de perfil", - "name-max-length": "El nombre debe ser menor de 256", - "name": "Nombre", - "name-required": "Se requiere nombre.", - "type": "Tipo de perfil", - "type-required": "Se requiere tipo de perfil.", - "type-default": "Por defecto", - "image": "Imagen del perfil de dispositivo", - "transport-type": "Tipo de transporte", - "transport-type-required": "Se requiere tipo de transporte.", - "transport-type-default": "Por defecto", - "transport-type-default-hint": "Soporta transportes por MQTT básico, HTTP y CoAP", - "transport-type-mqtt": "MQTT", - "transport-type-mqtt-hint": "Activa ajustes avanzados de transporte MQTT", - "transport-type-coap": "CoAP", - "transport-type-coap-hint": "Activa ajustes avanzados del transporte CoAP", - "transport-type-lwm2m": "LWM2M", - "transport-type-lwm2m-hint": "Transporte LWM2M", - "transport-type-snmp": "SNMP", - "transport-type-snmp-hint": "Configuración transporte SNMP", - "transport-type-http": "HTTP", - "description": "Descripción", - "default": "Defecto", - "profile-configuration": "Configuración de perfil", - "transport-configuration": "Configuración de transporte", - "default-rule-chain": "Cadena de reglas por defecto", - "default-edge-rule-chain": "Cadena de reglas por defecto en Edge", - "default-edge-rule-chain-hint": "Usado en Edge, como cadena de reglas para procesas los datos recibidos para el perfil de dispositivo.", - "mobile-dashboard": "Panel móvil", - "mobile-dashboard-hint": "Usado por la aplicación móvil como panel de detalle", - "select-queue-hint": "Selecciona desde el desplegable o añade un nombre personalizado.", - "delete-device-profile-title": "Eliminar el perfil '{{deviceProfileName}}'?", - "delete-device-profile-text": "Atención, tras la confirmación el perfil y todos sus datos serán borrados e irrecuperables.", - "delete-device-profiles-title": "Eliminar { count, plural, =1 {1 perfil} other {# perfiles} }?", - "delete-device-profiles-text": "Atención, tras la confirmación los perfiles seleccionados y todos sus datos serán borrados e irrecuperables.", - "set-default-device-profile-title": "Establecer el perfil '{{deviceProfileName}}' como perfil por defecto?", - "set-default-device-profile-text": "Tras la confirmación, el perfil será marcado como por defecto y será usado por todos los nuevos dispositivos que no tengan perfil especificado.", - "no-device-profiles-found": "No se encontraron perfiles.", - "create-new-device-profile": "Crear un nuevo perfil!", - "mqtt-device-topic-filters": "Filtros de topic MQTT", - "mqtt-device-topic-filters-unique": "Los filtros de topic de dispositivo MQTT deben ser únicos.", - "mqtt-device-topic-filters-spark-plug": "Nodo MQTT Sparkplug B Edge of Network (EoN).", - "mqtt-device-topic-filters-spark-plug-hint": "Permitir conexiones desde los nodos EoN con payload B y su formato de temas (topics).", - "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "Métricas SparkPlug para grabar como atributos.", - "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Nombre de las métricas SparkPlug que se grabarán como atributos de dispositivo. Las métricas restantes, se grabarán como telemetría.", - "mqtt-device-payload-type": "Payload de dispositivo MQTT", - "mqtt-device-payload-type-json": "JSON", - "mqtt-device-payload-type-proto": "Protobuf", - "mqtt-enable-compatibility-with-json-payload-format": "Activar compatibilidad con otros formatos de payload.", - "mqtt-enable-compatibility-with-json-payload-format-hint": "Si se activa, la plataforma usará un formato de payload Protobuf por defecto. Si el parseo falla, la plataforma intentará usar el formato JSON. Útil para retrocompatibilidad durante actualizaciones de firmware. Por ejemplo, si la versión inicial de firmware usa JSON y una versión posterior usa Protobuf. Durante el proceso de actualización de firmware a los dispositivos, se requiere soportar Protobuf y JSON al mismo tiempo. El modo de retrocompatibilidad introduce una pequeña degradación en el rendimiento, es recomendable desactivarlo una vez que todos los dispositivos estén actualizados.", - "mqtt-use-json-format-for-default-downlink-topics": "Usar formato JSON para los downlink", - "mqtt-use-json-format-for-default-downlink-topics-hint": "Cuando esté activado, la plataforma usará el formato JSON para enviar atributos y llamadas RPC a través de los siguientes temas (topics): v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, v1/devices/me/rpc/request/$request_id, v1/devices/me/rpc/response/$request_id. Este ajuste, no afectará a las subscripciones de atributos y a los mensajes RPC que se envíen usando los nuevos temas v2: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id. Donde $request_id es un número entero para identificar la transacción.", - "mqtt-send-ack-on-validation-exception": "Enviar PUBACK en error de validación al publicar (PUBLISH)", - "mqtt-send-ack-on-validation-exception-hint": "Por defecto, la plataforma cerrará la sesión MQTT cuando haya un fallo de validación. Si está activado, la plataforma enviará un mensaje tipo PUBACK, en lugar de cerrar la sesión.", - "snmp-add-mapping": "Añadir mapeado SNMP", - "snmp-mapping-not-configured": "No hay mapeado OID a series de tiempo/telemetría configurado", - "snmp-timseries-or-attribute-name": "Nombre Series de tiempo/nombre atributo para mapeado", - "snmp-timseries-or-attribute-type": "Tipo Series de tiempo/nombre atributo para mapeado", - "snmp-method-pdu-type-get-request": "GetRequest", - "snmp-method-pdu-type-get-next-request": "GetNextRequest", - "snmp-oid": "OID", - "transport-device-payload-type-json": "JSON", - "transport-device-payload-type-proto": "Protobuf", - "mqtt-payload-type-required": "Se requiere tipo de Payload.", - "coap-device-type": "Tipo de dispositivo CoAP", - "coap-device-payload-type": "Payload dispositivo CoAP", - "coap-device-type-required": "Se requiere tipo de dispositivo CoAP.", - "coap-device-type-default": "Por defecto", - "coap-device-type-efento": "Efento NB-IoT", - "support-level-wildcards": "Se soportan los wilcards únicos [+] y multi-nivel [#].", - "telemetry-topic-filter": "Filtro de topic en telemetría", - "telemetry-topic-filter-required": "Se requiere filtro de topic (telemetría).", - "attributes-topic-filter": "Filtro de tema (topic) en publicación de atributos", - "attributes-subscribe-topic-filter": "Filtro de tema (topic) en suscripción de atributos", - "attributes-topic-filter-required": "Se requiere filtro de publicación.", - "attributes-subscribe-topic-filter-required": "Se requiere filtro de suscripción", - "telemetry-proto-schema": "Proto esquema de telemetría", - "telemetry-proto-schema-required": "Se requiere proto esquema de telemetría.", - "attributes-proto-schema": "Proto esquema de atributos", - "attributes-proto-schema-required": "Se requiere proto esquema de atributos.", - "rpc-response-proto-schema": "Proto esquema de respuesta RPC", - "rpc-response-proto-schema-required": "Se requiere proto esquema de respuesta RPC.", - "rpc-response-topic-filter": "Filtro de topic de respuesta RPC", - "rpc-response-topic-filter-required": "Se requiere filtro de topic respuesta RPC.", - "rpc-request-proto-schema": "Proto esquema de petición RPC", - "rpc-request-proto-schema-required": "Se requiere proto esquema de petición RPC.", - "rpc-request-proto-schema-hint": "Las peticiones RPC deben contener siempre los siguientes campos: string method = 1; int32 requestId = 2; y params = 3 para cualquier tipo de datoas.", - "not-valid-pattern-topic-filter": "No es un patrón de filtro válido", - "not-valid-single-character": "Uso inválido de wildcard único", - "not-valid-multi-character": "Uso inválido de wildcard multi-nivel", - "single-level-wildcards-hint": "[+] es adecuado para cualquier nivel. Ej.: v1/devices/+/telemetry o +/devices/+/attributes.", - "multi-level-wildcards-hint": "[#] puede reemplazar el mismo filtro y debe ser el último símbolo del topic. Ej.: # o v1/devices/me/#.", - "alarm-rules": "Reglas de alarma", - "alarm-rules-with-count": "Reglas de alarma ({{count}})", - "no-alarm-rules": "No hay reglas de alarma configuradas", - "add-alarm-rule": "Añadir regla de alarma", - "edit-alarm-rule": "Editar regla de alarma", - "alarm-type": "Tipo de alarma", - "alarm-type-required": "Se requiere tipo de alarma.", - "alarm-type-unique": "El tipo de alarma, debe ser único dentro de las reglas de alarma del perfil de dispositivo.", - "alarm-type-max-length": "El tipo de alarma debe ser menor de 256", - "create-alarm-pattern": "Crear alarma {{alarmType}}", - "create-alarm-rules": "Crear reglas de alarma", - "no-create-alarm-rules": "No hay condiciones de creación de alarma configuradas", - "add-create-alarm-rule-prompt": "Por favor, añade una regla de alarma", - "clear-alarm-rule": "Borrar regla de alarma", - "no-clear-alarm-rule": "No hay condiciones de borrado de alarma configuradas", - "add-create-alarm-rule": "Añadir crear condición (activar alarma)", - "add-clear-alarm-rule": "Añair borrar condición (limpiar alarma)", - "select-alarm-severity": "Selecciona severidad de alarma", - "alarm-severity-required": "Se requiere especificar severidad de alarma.", - "condition-duration": "Duración de condición", - "condition-duration-value": "Valor de duración", - "condition-duration-time-unit": "Unidad de tiempo", - "condition-duration-value-range": "El valor debe estar en un rango desde 1 a 2147483647.", - "condition-duration-value-pattern": "El valor de duración debe ser un número entero.", - "condition-duration-value-required": "Se requiere valor de duración.", - "condition-duration-time-unit-required": "Se requiere una unidad de tiempo.", - "advanced-settings": "Ajustes avanzados", - "alarm-rule-additional-info-hint": "Ayuda: usa ${nombredeClave} para sustituir los valores de atributos o telemetrías usadas en la condición de la regla de alarma.", - "alarm-rule-mobile-dashboard": "Panel Móvil", - "alarm-rule-mobile-dashboard-hint": "Usado por la aplicación móvil como panel de detalle de alarmas", - "alarm-rule-no-mobile-dashboard": "No hay panel seleccionado", - "propagate-alarm": "Propagar alarma", - "alarm-rule-relation-types-list": "Tipos de relación para propagar", - "alarm-rule-relation-types-list-hint": "Si no está seleccionado 'propagar relaciones', las alarmas serán propagadas sin filtrar por relación.", - "propagate-alarm-to-owner": "Propagar alarma al propietario de la entidad (Cliente o Administrador)", - "propagate-alarm-to-tenant": "Propagar alarma a Administrador", - "alarm-rule-condition": "Condiciones de regla de alarma", - "enter-alarm-rule-condition-prompt": "Por favor, añade una condición de alarma", - "edit-alarm-rule-condition": "Editar condición de alarma", - "device-provisioning": "Aprovisionamiento de dispositivos", - "provision-strategy": "Estrategia de aprovisionamiento", - "provision-strategy-required": "Se requiere estrategia de aprovisionamiento.", - "provision-strategy-disabled": "Desactivado", - "provision-strategy-created-new": "Permitir crear nuevos dispositivos", - "provision-strategy-check-pre-provisioned": "Revisar dispositivos pre-aprovisionados", - "provision-device-key": "Clave de aprovisionamiento", - "provision-device-key-required": "Se requiere clave de aprovisionamiento.", - "copy-provision-key": "Copiar clave de aprovisionamiento", - "provision-key-copied-message": "La clave de aprovisionamiento se ha copiado al portapapeles", - "provision-device-secret": "Secreto de aprovisionamiento", - "provision-device-secret-required": "Se requiere secreto de aprovisionamiento.", - "copy-provision-secret": "Copiar secreto de aprovisionamiento", - "provision-secret-copied-message": "Se ha copiado el secreto de aprovisionamiento al portapapeles", - "provision-strategy-x509": { - "certificate-chain": "Cadena de certificados X509", - "certificate-chain-hint": "La estrategia de certificados X.509 se utiliza para aprovisionar dispositivos mediante certificados de cliente en comunicación TLS bidireccional.", - "allow-create-new-devices": "Crear nuevos dispositivos", - "allow-create-new-devices-hint": "Si se selecciona, los nuevos dispositivos se crearán y el certificado del cliente se usara como credenciales del dispositivo.", - "certificate-value": "Certificado en formato PEM", - "certificate-value-required": "Se requiere certificado en formato PEM", - "cn-regex-variable": "Variable de expresión regular CN", - "cn-regex-variable-required": "Se requiere una variable de expresión regular", - "cn-regex-variable-hint": "Requerido para obtener el nombre del dispositivo desde el CN del certificado." - }, - "condition": "Condición", - "condition-type": "Tipo de condición", - "condition-type-simple": "Simple", - "condition-type-duration": "Duración", - "condition-during": "Durante {{during}}", - "condition-during-dynamic": "Durante \"{{ attribute }}\" ({{during}})", - "condition-type-repeating": "Repetitiva", - "condition-type-required": "Se requiere tipo de condición.", - "condition-repeating-value": "Nº de eventos", - "condition-repeating-value-range": "El Nº de eventos debe estar en un rango de 1 a 2147483647.", - "condition-repeating-value-pattern": "Nº de eventos debe ser un número entero.", - "condition-repeating-value-required": "Se requiere Nº de eventos.", - "condition-repeat-times": "Repetición { count, plural, =1 {1 vez} other {# veces} }", - "condition-repeat-times-dynamic": "Repetición \"{ atributo }\" ({ count, plural, =1 {1 vez} other {# veces} })", - "schedule-type": "Tipo de horario", - "schedule-type-required": "Tipo de horario requerido.", - "schedule": "Horario", - "edit-schedule": "Editar horario de alarma", - "schedule-any-time": "Siempre activo", - "schedule-specific-time": "Activo en una hora específica", - "schedule-custom": "Personalizado", - "schedule-day": { - "monday": "Lunes", - "tuesday": "Martes", - "wednesday": "Miércoles", - "thursday": "Jueves", - "friday": "Viernes", - "saturday": "Sábado", - "sunday": "Domingo" - }, - "schedule-days": "Días", - "schedule-time": "Hora", - "schedule-time-from": "Desde", - "schedule-time-to": "Hasta", - "schedule-days-of-week-required": "Seleccionar por lo menos un día de la semana.", - "create-device-profile": "Crear un nuevo perfil de dispositivo", - "import": "Importar perfil de dispositivo", - "export": "Exportar perfil de dispositivo", - "export-failed-error": "No ha sido posible exportar el perfil de dispositivo: {{error}}", - "device-profile-file": "Archivo de perfil de dispositivo", - "invalid-device-profile-file-error": "No ha sido posible importar perfil de dispositivo: Estructura de datos inválida.", - "power-saving-mode": "Modo de ahorro de energía (PSM)", - "power-saving-mode-type": { - "default": "Usar el del perfil del dispositivo", - "psm": "Power Saving Mode (PSM)", - "drx": "Discontinuous Reception (DRX)", - "edrx": "Extended Discontinuous Reception (eDRX)" - }, - "edrx-cycle": "Ciclo eDRX", - "edrx-cycle-required": "Se requiere ciclo eDRX.", - "edrx-cycle-pattern": "El ciclo eDRX debe ser un número entero positivo.", - "edrx-cycle-min": "El mínimo de ciclo eDRX es de {{ min }} segundos.", - "paging-transmission-window": "Ventana de transmisión (Paging Transmission Window)", - "paging-transmission-window-required": "Se requiere ventana de transmisión (Paging transmission window).", - "paging-transmission-window-pattern": "Ventana de transmision debe ser un número entero positivo.", - "paging-transmission-window-min": "El mínimo de ventana de transmisión es de {{ min }} segundos.", - "psm-activity-timer": "Tiempo Actividad PSM (PSM Activity Timer)", - "psm-activity-timer-required": "Se requiere el tiempo de actividad PSM.", - "psm-activity-timer-pattern": "Tiempo de actividad PSM debe ser un número entero positivo.", - "psm-activity-timer-min": "El tiempo de actividad PSM mínimo es de {{ min }} segundos.", - "lwm2m": { - "object-list": "Lista de objetos", - "object-list-empty": "No hay objetos seleccionados.", - "no-objects-found": "No se encontraron objetos.", - "no-objects-matching": "No hay objetos que coincidan con '{{object}}'.", - "model-tab": "Modelo LWM2M", - "add-new-instances": "Añadir nueva instancia", - "instances-list": "Lista de Instancias", - "instances-list-required": "Se requiere lista de instancias.", - "instance-id-pattern": "El id de instancia debe ser un número entero positivo.", - "instance-id-max": "El id máximo de instancia es {{max}}.", - "instance": "Instancia", - "resource-label": "#ID Nombre Recurso", - "observe-label": "Observar", - "attribute-label": "Atributo", - "telemetry-label": "Telemetría", - "edit-observe-select": "Para editar observar, selecciona telemetría o atributo", - "edit-attributes-select": "Para editar atributo, selecciona telemetría o atributo", - "no-attributes-set": "No hay atributos ajustados", - "key-name": "Nombre de clave", - "key-name-required": "Se requiere nombre de clave", - "attribute-name": "Nombre Atributo", - "attribute-name-required": "Se requiere nombre de atributo.", - "attribute-value": "Valor de atributo", - "attribute-value-required": "Se requiere valor de atributo.", - "attribute-value-pattern": "El valor del atributo debe ser un número entero positivo.", - "edit-attributes": "Edita atributos: {{ name }}", - "view-attributes": "Ver atributos: {{ name }}", - "add-attribute": "Añadir atributo", - "edit-attribute": "Editar atributo", - "view-attribute": "Ver atributos", - "remove-attribute": "Borrar atributos", - "delete-server-text": "Atención, tras la confirmación la configuración del servidor se borrará y será irrecuperable.", - "delete-server-title": "Estas seguro de borrar el servidor?", - "mode": "Configuración de seguridad", - "bootstrap-tab": "Bootstrap", - "bootstrap-server-legend": "Servidor Bootstrap (ShortId...)", - "lwm2m-server-legend": "Servidor LwM2M (ShortId...)", - "server": "Servidor", - "short-id": "Short server ID", - "short-id-tooltip": "Id corto del servidor. Usado como enlace para asociar las instancias de objetos del servidor.\nEste identificador sirve para identificar únicamente cada servidor LwM2M configurado para el cliente LwM2M.\nLos recursos DEBEN ser ajustados cuando el servidor Bootstrap tenga un valor de 'false'.\nLos valores ID:0 and ID:65535 NO DEBEN ser usados para identificar al servidor LwM2M.", - "short-id-required": "Se requiere Short server ID.", - "short-id-range": "Short server ID debe estar en un rango de {{ min }} a {{ max }}.", - "short-id-pattern": "Short server ID debe ser un número entero positivo.", - "lifetime": "Ciclo de vida registro de cliente (Registration Lifetime)", - "lifetime-required": "Se requiere ciclo de vida.", - "lifetime-pattern": "Ciclo de vida debe ser un número entero positivo.", - "default-min-period": "Periodo mínimo entre notificaciones (s)", - "default-min-period-tooltip": "Valor predeterminado que el cliente LwM2M debe usar para el período mínimo de una Observación en ausencia de éste parámetro incluido en una observación.", - "default-min-period-required": "Período mímino requerido.", - "default-min-period-pattern": "El período mínimo debe ser un número entero positivo.", - "notification-storing": "Grabado de notificaciones cuando esté desactivado u offline", - "binding": "Binding", - "binding-type": { - "u": "U: El cliente es alcanzable por UDP en cualquier momento.", - "m": "M: El cliente es alcanzable por MQTT en cualquier momento.", - "h": "H: El cliente es alcanzable por HTTP en cualquier momento.", - "t": "T: El cliente es alcanzable por TCP en cualquier momento.", - "s": "S: El cliente es alcanzable por SMS en cualquier momento.", - "n": "N: El cliente DEBE enviar las respuestas a las peticiones sobre el modo Non-IP (soportado desde la versión LWM2M 1.1).", - "uq": "UQ: Conexión UDP en modo de cola (no soportado desde la versión LWM2M 1.1)", - "uqs": "UQS: Conexiones UDP y SMS activas, UDP en modo cola, SMS en modo estándar (no soportado desde la versión LWM2M 1.1)", - "tq": "TQ: Conexión TCP en modo de cola (no soportado desde la versión LWM2M 1.1)", - "tqs": "TQS: Conexiones TCP y SMS activas, TCP en modo cola, SMS en modo estándar (no soportado desde la versión LWM2M 1.1)", - "sq": "SQ: Conexión SMS en modo de cola (no soportado desde la versión LWM2M 1.1)" - }, - "binding-tooltip": "Esta es la lista del recurso \"binding\" del objeto de servidor LwM2M - /1/x/7.\nIndica los modos de enlace admitidos por el cliente LwM2M.\nEste valor DEBE ser el mismo que el valor reflejado en el recurso “Supported Binding and Modes” en el objeto (/3/0/16) del dispositivo.\nAunque se admiten varios transportes, solo se puede utilizar un tipo de binding durante toda la sesión.\nPor ejemplo, si UDP y SMS son admitidos, el servidor o el cliente LwM2M, pueden optar por comunicarse a través de UDP o SMS durante toda la sesión de transporte.", - "bootstrap-server": "Servidor Bootstrap", - "lwm2m-server": "Servidor LwM2M", - "include-bootstrap-server": "Incluir actualizaciónes del servidor Bootstrap", - "bootstrap-update-title": "Ya has configurado un servidor Bootstrap. Estás seguro de que quieres excluir las actualizaciones?", - "bootstrap-update-text": "Atención, tras la confirmación la configuración del servidor Bootstrap será irrecuperable.", - "server-host": "Host", - "server-host-required": "Se requiere Host.", - "server-port": "Puerto", - "server-port-required": "Se requiere Puerto.", - "server-port-pattern": "El puerto debe ser un número entero positivo.", - "server-port-range": "El puerto debe comprender en el rango 1 a 65535.", - "server-public-key": "Clave Pública del Servidor", - "server-public-key-required": "Se requiere la Clave Pública del servidor.", - "client-hold-off-time": "Tiempo de espera (Hold Off Time)", - "client-hold-off-time-required": "Se requiere tiempo de espera.", - "client-hold-off-time-pattern": "El tiempo de espera debe ser un número entero positivo.", - "client-hold-off-time-tooltip": "El tiempo de espera se usa sólamente con un servidor Bootstrap", - "account-after-timeout": "Cuenta tras el tiempo de espera", - "account-after-timeout-required": "Se requiere Cuenta tras el tiempo de espera.", - "account-after-timeout-pattern": "Cuenta tras el tiempo de espera debe ser un número entero positivo.", - "account-after-timeout-tooltip": "Cuenta tras el tiempo de espera otorgado por este recurso.", - "server-type": "Tipo de servidor", - "add-new-server-title": "Añadir nueva configuración", - "add-server-config": "Añadir configuración de servidor", - "add-lwm2m-server-config": "Añadir Servidor LwM2M", - "no-config-servers": "No hay servidores configurados", - "others-tab": "Otros ajustes", - "client-strategy": "Estrategia del cliente en la conexión", - "client-strategy-label": "Estrategia", - "client-strategy-only-observe": "Realizar un request para observar al cliente tras la conexión inicial", - "client-strategy-read-all": "Leer todos los recursos y realizar un request para observar al cliente tras el registro", - "fw-update": "Actualización de Firmware", - "fw-update-strategy": "Estrategia de actualización de Firmware", - "fw-update-strategy-data": "Enviar actualización de firmware como un fichero binario usando el Objeto 19 y el Recurso 0 (Data)", - "fw-update-strategy-package": "Enviar actualización de firmware como un fichero binario usando el Objeto 5 y el Recurso 0 (Package)", - "fw-update-strategy-package-uri": "Auto-generar una URL CoAP única para la descarga del paquete y enviarla usando el Objeto 5 y el Recurso 1 (Package URI)", - "sw-update": "Actualización de Software", - "sw-update-strategy": "Estrategia de Actualización de Software", - "sw-update-strategy-package": "Enviar como un fichero binario usando el Objeto 9 y el Recurso 2 (Package)", - "sw-update-strategy-package-uri": "Auto-generar una URL CoAP única para la descarga del paquete y enviarla usando el Objeto 9 y el Recurso 3 (Package URI)", - "fw-update-resource": "Recurso CoAP Actualización de Firmware", - "fw-update-resource-required": "Se requiere el Recurso CoAP Actualización de Firmware.", - "sw-update-resource": "Recurso CoAP Actualización de Software", - "sw-update-resource-required": "Se requiere el Recurso CoAP Actualización de Software.", - "config-json-tab": "Configuracion Json Perfil de dispositivo", - "attributes-name": { - "min-period": "Período mínimo", - "max-period": "Período máximo", - "greater-than": "Mayor que", - "less-than": "Menor que", - "step": "Paso", - "min-evaluation-period": "Período mínimo de evaluación", - "max-evaluation-period": "Período máximo de evaluación" - } - }, - "snmp": { - "add-communication-config": "Añadir configuración de comunicaciones", - "add-mapping": "Añadir mapeado", - "authentication-passphrase": "Frase de contraseña", - "authentication-passphrase-required": "Se requiere frase de contraseña.", - "authentication-protocol": "Protocolo de autenticación", - "authentication-protocol-required": "Se requiere Protocolo de autenticación.", - "communication-configs": "Configuracion de comunicaciones", - "community": "Community", - "community-required": "Se requiere Community.", - "context-name": "Nombre de contexto", - "data-key": "Clave de datos", - "data-key-required": "Se requiere clave de datos.", - "data-type": "Tipo de datos", - "data-type-required": "Se requiere tipo de datos.", - "engine-id": "Engine ID", - "host": "Host", - "host-required": "Se requiere Host.", - "oid": "OID", - "oid-pattern": "Formato OID inválido.", - "oid-required": "Se requiere OID.", - "please-add-communication-config": "Por favor, añadir configuración de comunicación", - "please-add-mapping-config": "Por favor, añadir configuración de mapeado", - "port": "Puerto", - "port-format": "Formato de puerto inválido.", - "port-required": "Se requiere puerto.", - "privacy-passphrase": "Frase de clave de privacidad", - "privacy-passphrase-required": "Se requiere Frase de clave de privacidad.", - "privacy-protocol": "Protocolo privacidad", - "privacy-protocol-required": "Se requiere Protocolo privacidad.", - "protocol-version": "Versión protocolo", - "protocol-version-required": "Se requiere versión protocolo.", - "querying-frequency": "Frecuencia de peticiones, ms", - "querying-frequency-invalid-format": "Frecuencia de peticiones debe ser un número entero positivo.", - "querying-frequency-required": "Se requiere frecuencia de peticiones.", - "retries": "Reintentos", - "retries-invalid-format": "Reintentos debe ser un número entero positivo.", - "retries-required": "Se requiere reintentos.", - "scope": "Alcance", - "scope-required": "Se requiere alcance.", - "security-name": "Nombre seguridad", - "security-name-required": "Se requiere Nombre seguridad.", - "timeout-ms": "Timeout, ms", - "timeout-ms-invalid-format": "Timeout debe ser un número entero positivo.", - "timeout-ms-required": "Se requiere timeout.", - "user-name": "Usuario", - "user-name-required": "Se requiere usuario." - } + "stepper-text": { + "select-file": "Seleccionar un archivo", + "configuration": "Configuración de importación", + "column-type": "Seleccionar tipo de columnas", + "creat-entities": "Creando nuevas entidades" }, - "dialog": { - "close": "Cerrar diálogo", - "error-message-title": "Mensaje de error:", - "error-details-title": "Detalles de error" + "message": { + "create-entities": "{{count}} nuevas entidades fueron creadas exitosamente.", + "update-entities": "{{count}} entidades fueron actualizadas exitosamente.", + "error-entities": "Hubo un error al crear {{count}} entidades." + } + }, + "scada": { + "symbols": "Símbolos SCADA", + "search": "Buscar símbolo", + "selected-symbols": "{ count, plural, =1 {1 símbolo} other {# símbolos} } seleccionados", + "download-symbol": "Descargar símbolo SCADA", + "export-symbol": "Exportar símbolo SCADA a JSON", + "import-symbol": "Importar símbolo SCADA desde JSON", + "upload-symbol": "Subir símbolo SCADA", + "update-symbol": "Actualizar símbolo SCADA", + "edit-symbol": "Editar símbolo SCADA", + "symbol-details": "Detalles del símbolo SCADA", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "No se encontraron símbolos", + "show-hidden-elements": "Mostrar elementos ocultos", + "hide-hidden-elements": "Ocultar elementos ocultos", + "delete-symbol": "Eliminar símbolo SCADA", + "delete-symbol-title": "¿Estás seguro de que deseas eliminar el símbolo SCADA '{{imageTitle}}'?", + "delete-symbol-text": "Ten cuidado, después de la confirmación el símbolo SCADA será irrecuperable.", + "delete-symbols-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 símbolo SCADA} other {# símbolos SCADA} }?", + "delete-symbols-text": "Ten cuidado, después de la confirmación todos los símbolos SCADA seleccionados serán eliminados y todos los datos relacionados se volverán irrecuperables.", + "include-system-symbols": "Incluir símbolos del sistema", + "symbol-preview": "Vista previa del símbolo", + "general": "General", + "tags": "Etiquetas", + "properties": "Propiedades", + "title": "Título", + "description": "Descripción", + "search-tags": "Buscar etiquetas", + "widget-size": "Tamaño del widget", + "cols": "columnas", + "rows": "filas", + "state-render-function": "Función de renderizado de estado", + "preview": "Vista previa", + "preview-widget-action-text": "¡Acción del widget '{{type}}' invocada con éxito!", + "no-symbol": "Sin símbolo SCADA", + "no-symbol-selected": "Ningún símbolo SCADA seleccionado", + "clear-symbol": "Limpiar símbolo SCADA", + "browse-symbol-from-gallery": "Buscar símbolo SCADA en la galería", + "zoom-in": "Acercar", + "zoom-out": "Alejar", + "create-widget": "Crear widget", + "create-widget-from-symbol": "Crear widget desde símbolo SCADA", + "hidden": "oculto", + "tag": { + "tag": "Etiqueta", + "on-click-action": "Acción al hacer clic", + "no-tags": "No hay etiquetas configuradas", + "delete-tag-text": "¿Estás seguro de que deseas eliminar la etiqueta
{{tag}} del elemento {{elementType}}?", + "update-tag": "Actualizar etiqueta", + "enter-tag": "Introducir etiqueta", + "tag-settings": "Configuración de etiqueta", + "remove-tag": "Eliminar etiqueta", + "add-tag": "Agregar etiqueta" }, - "direction": { - "column": "Columna", - "row": "Fila" + "behavior": { + "behavior": "Comportamiento", + "id": "Id", + "name": "Nombre", + "type": "Tipo", + "no-behaviors": "No hay comportamientos configurados", + "add-behavior": "Agregar comportamiento", + "type-action": "Acción", + "type-value": "Valor", + "type-widget-action": "Acción de widget", + "behavior-settings": "Configuración del comportamiento", + "remove-behavior": "Eliminar comportamiento", + "hint": "Pista", + "group-title": "Título del grupo", + "value-type": "Tipo de valor", + "default-value": "Valor por defecto", + "true-label": "Etiqueta verdadero", + "false-label": "Etiqueta falso", + "state-label": "Etiqueta de estado", + "default-payload": "Carga útil por defecto", + "not-unique-behavior-ids-error": "¡Los Ids de comportamiento deben ser únicos!", + "default-settings": "Configuración predeterminada" }, - "edge": { - "edge": "Edge", - "edge-instances": "Instancias de Edge", - "instances": "Instancias", - "edge-file": "Archivo de Edge", - "name-max-length": "El nombre debe ser menor de 256", - "label-max-length": "La etiqueta debe ser menor de 256", - "type-max-length": "El tipo debe ser menor de 256", - "management": "Gestión de Edges", - "no-edges-matching": "No se encontraron Edges que coincidan con '{{entity}}'", - "add": "Agregar Edge", - "no-edges-text": "No se encontraron Edges", - "edge-details": "Detalles del Edge", - "add-edge-text": "Agregar nuevo Edge", - "delete": "Eliminar Edge", - "delete-edge-title": "¿Está seguro de que desea eliminar el Edge '{{edgeName}}'?", - "delete-edge-text": "Tenga cuidado, después de la confirmación, el Edge y todos los datos relacionados serán irrecuperables", - "delete-edges-title": "¿Está seguro de que desea edge {count, plural, =1 {1 Edge} other {# Edges} }?", - "delete-edges-text": "Tenga cuidado, después de la confirmación se eliminarán todos los Edges seleccionados y todos los datos relacionados se volverán irrecuperables", - "name": "Nombre", - "name-starts-with": "Nombre de Edge comienza con", - "name-required": "Se requiere nombre", - "description": "Descripción", - "details": "Detalles", - "events": "Eventos", - "copy-id": "Copiar ID de Edge", - "id-copied-message": "El ID de Edge se ha copiado al portapapeles", - "sync": "Sinc Edge", - "edge-required": "Edge Requerido", - "edge-type": "Tipo de Edge", - "edge-type-required": "El tipo de Edge es requerido.", - "event-action": "Información de la entidad", - "entity-id": "ID de entidad", - "select-edge-type": "Seleccionar tipo de Edge", - "assign-to-customer": "Asignar al cliente", - "assign-to-customer-text": "Seleccione el cliente para asignar los Edges", - "assign-edge-to-customer": "Asignar Edge(s) al cliente", - "assign-edge-to-customer-text": "Seleccione los Edges para asignar al cliente", - "assignedToCustomer": "Asignada a la cliente", - "edge-public": "Edge es pública", - "assigned-to-customer": "Asignado al cliente", - "unassign-from-customer": "Anular asignación del cliente", - "unassign-edge-title": "¿Está seguro de que desea desasignar el Edge '{{edgeName}}'?", - "unassign-edge-text": "Después de la confirmación, el Edge quedará sin asignar y el cliente no podrá acceder a él", - "unassign-edges-title": "¿Está seguro de que desea anular la asignación de {count, plural, =1 {1 Edge} other {# Edges} }?", - "unassign-edges-text": "Después de la confirmación de todos los Edges seleccionados, se anulará la asignación y el cliente no podrá acceder a ellos.", - "make-public": "Hacer público el Edge", - "make-public-edge-title": "¿Estás seguro de que quieres hacer público el edge '{{edgeName}}'?", - "make-public-edge-text": "Después de la confirmación, el Edge y todos sus datos serán públicos y accesibles para otros", - "make-private": "Hacer que edge sea privado", - "public": "Público", - "make-private-edge-title": "¿Está seguro de que desea que el Edge '{{edgeName}}' sea privado?", - "make-private-edge-text": "Después de la confirmación, el Edge y todos sus datos se harán privados y otros no podrán acceder a ellos", - "import": "Importar Edge", - "install-connect-instructions": "Instrucciones de instalación y conexión", - "install-connect-instructions-edge-created": "Edge creado! Revisa las instrucciones de instalación y conexión", - "loading-edge-instructions": "Cargando instrucciones edge...", - "label": "Etiqueta", - "load-entity-error": "Entidad no encontrada. No se pudo cargar la información", - "assign-new-edge": "Asignar nuevo Edge", - "unassign-from-edge": "Anular asignación de Edge", - "edge-key": "Clave de Edge", - "copy-edge-key": "Copiar clave de Edge", - "edge-key-copied-message": "La clave de Edge se ha copiado al portapapeles", - "edge-secret": "Secreto del Edge", - "copy-edge-secret": "Copiar secreto del Edge", - "edge-secret-copied-message": "El secreto de Edge se ha copiado al portapapeles", - "manage-assets": "Gestionar activos", - "manage-devices": "Gestionar dispositivos", - "manage-entity-views": "Gestionar vistas de entidad", - "manage-dashboards": "Administrar paneles", - "manage-rulechains": "Administrar cadenas de reglas", - "assets": "Activos de Edge", - "devices": "Dispositivos de Edge", - "entity-views": "Vistas de entidad de Edge", - "dashboard": "Panel de control Edge", - "dashboards": "Paneles de Edge", - "rulechain-templates": "Plantillas, de cadena de reglas", - "edge-rulechain-templates": "Plantillas de cadena de reglas (Edge)", - "rulechains": "Cadenas de regla (Edge)", - "search": "Edges de búsqueda", - "selected-edges": "{count, plural, =1 {1 Edge} other {# Edges} } seleccionadas", - "any-edge": "Cualquier Edge", - "no-edge-types-matching": "No se encontraron tipos de aristas que coincidan con '{{entitySubtype}}'.", - "edge-type-list-empty": "No se seleccionó ningún tipo de Edge.", - "edge-types": "Tipos de Edges", - "enter-edge-type": "Ingrese el tipo de Edge", - "deployed": "Desplegada", - "pending": "Pendiente", - "downlinks": "Enlaces descendentes", - "no-downlinks-prompt": "No se encontraron enlaces descendentes", - "sync-process-started-successfully": "¡El proceso de sincronización se inició correctamente!", - "missing-related-rule-chains-title": "Al Edge le faltan cadenas de reglas relacionadas", - "missing-related-rule-chains-text": "Asignado a la (s) cadena (s) de reglas de Edge usa nodos de reglas que reenvían mensajes a cadenas de reglas que no están asignadas a este Edge.

Lista de cadenas de reglas faltantes:
{{missingRuleChains}}", - "widget-datasource-error": "Este widget solo admite la fuente de datos de la entidad EDGE" + "symbol": { + "symbol": "Símbolo SCADA", + "fluid-presence": "Presencia de fluido", + "fluid-presence-hint": "Indica si hay fluido presente en la tubería.", + "fluid-present": "Fluido presente", + "present": "Presente", + "absent": "Ausente", + "flow-presence": "Presencia de flujo", + "flow-presence-hint": "Indica si el fluido está fluyendo en la tubería.", + "flow-present": "Flujo presente", + "flow-direction": "Dirección del flujo", + "flow-direction-hint": "Indica la dirección del flujo del fluido.", + "forward": "Hacia adelante", + "reverse": "Reversa", + "flow-animation-speed": "Velocidad de animación del flujo", + "flow-animation-speed-hint": "Valor decimal que indica la velocidad de animación del flujo. 1 - velocidad normal, 0 - sin animación, < 1 - animación más lenta, > 1 - animación más rápida.", + "leak": "Fuga", + "leak-hint": "Indica si hay una fuga en la tubería.", + "leak-present": "Fuga presente", + "fluid-color": "Color del fluido", + "pipe-color": "Color de la tubería", + "horizontal-pipe": "Tubería horizontal", + "vertical-pipe": "Tubería vertical", + "horizontal-fluid-color": "Color del fluido horizontal", + "vertical-fluid-color": "Color del fluido vertical", + "left-pipe": "Tubería izquierda", + "right-pipe": "Tubería derecha", + "top-pipe": "Tubería superior", + "bottom-pipe": "Tubería inferior", + "left-fluid-color": "Color del fluido izquierdo", + "right-fluid-color": "Color del fluido derecho", + "top-fluid-color": "Color del fluido superior", + "bottom-fluid-color": "Color del fluido inferior", + "display": "Pantalla", + "display-format": "Formato de pantalla", + "value": "Valor", + "decimals": "Decimales", + "units": "Unidades", + "flow-meter-value-hint": "Valor decimal mostrado en el medidor de flujo", + "value-hint": "Valor decimal que indica el valor actual", + "running": "En ejecución", + "running-hint": "Indica si el componente está en estado de ejecución.", + "warning-state": "Estado de advertencia", + "warning": "Advertencia", + "warning-click": "Clic de advertencia", + "warning-state-hint": "Indica si el componente está en estado de advertencia.", + "critical-state": "Estado crítico", + "critical": "Crítico", + "critical-click": "Clic crítico", + "critical-state-hint": "Indica si el componente está en estado crítico.", + "critical-state-animation": "Animación de estado crítico", + "critical-state-animation-hint": "Indica si se habilita la animación parpadeante cuando el componente está en estado crítico.", + "warning-critical-state-animation": "Animación de estado de advertencia/crítico", + "warning-critical-state-animation-hint": "Indica si se habilita la animación parpadeante cuando el componente está en estado de advertencia o crítico.", + "animation": "Animación", + "broken": "Roto", + "broken-hint": "Indica si el componente está roto.", + "on-display-click": "Al hacer clic en la pantalla", + "on-display-click-hint": "Acción invocada cuando el usuario hace clic en la pantalla.", + "pipe": "Tubería", + "default-border-color": "Color de borde por defecto", + "active-border-color": "Color de borde activo", + "warning-border-color": "Color de borde de advertencia", + "critical-border-color": "Color de borde crítico", + "background-color": "Color de fondo", + "rotation-animation-speed": "Velocidad de animación de rotación", + "rotation-animation-speed-hint": "Valor decimal que indica la velocidad de la animación de rotación. 1 - velocidad normal, 0 - sin animación, < 1 - más lenta, > 1 - más rápida.", + "on-click": "Al hacer clic", + "on-click-hint": "Acción invocada cuando el usuario hace clic en el componente.", + "connectors-positions": "Posiciones de conectores", + "right-connector": "Conector derecho", + "right-top-connector": "Conector superior derecho", + "right-bottom-connector": "Conector inferior derecho", + "left-connector": "Conector izquierdo", + "left-top-connector": "Conector superior izquierdo", + "left-bottom-connector": "Conector inferior izquierdo", + "top-left-connector": "Conector izquierdo superior", + "top-right-connector": "Conector derecho superior", + "top-connector": "Conector superior", + "bottom-connector": "Conector inferior", + "running-color": "Color en funcionamiento", + "stopped-color": "Color detenido", + "stopped": "Detenido", + "warning-color": "Color de advertencia", + "critical-color": "Color crítico", + "opened": "Abierto", + "opened-hint": "Indica si el componente está en estado abierto.", + "open": "Abrir", + "open-hint": "Acción invocada cuando el usuario hace clic para abrir el componente.", + "close": "Cerrar", + "close-hint": "Acción invocada cuando el usuario hace clic para cerrar el componente.", + "close-state-animation": "Animación de estado cerrado", + "close-state-animation-hint": "Si se debe habilitar la animación parpadeante cuando el componente está en estado cerrado.", + "opened-color": "Color de abierto", + "closed-color": "Color de cerrado", + "opened-rotation-angle": "Ángulo de rotación abierto", + "closed-rotation-angle": "Ángulo de rotación cerrado", + "tank-capacity": "Capacidad del tanque", + "tank-capacity-hint": "Valor decimal que indica la capacidad total del tanque.", + "current-volume": "Volumen actual", + "current-volume-hint": "Valor decimal que indica el volumen ocupado actual.", + "tank-color": "Color del tanque", + "value-box": "Caja de valor", + "value-text": "Texto del valor", + "scale": "Escala", + "transparent-mode": "Modo transparente", + "major-ticks": "Marcas mayores", + "intervals": "Intervalos", + "major-ticks-color": "Color de marcas mayores", + "normal": "Normal", + "minor-ticks": "Marcas menores", + "minor-ticks-color": "Color de marcas menores", + "temperature": "Temperatura", + "temperature-hint": "Valor decimal que indica la temperatura actual.", + "update-temperature": "Actualizar temperatura", + "update-temperature-hint": "Acción invocada cuando el usuario hace clic para cambiar la temperatura actual.", + "run": "Ejecutar", + "run-hint": "Acción invocada cuando el usuario hace clic para ejecutar el componente.", + "stop": "Detener", + "stop-hint": "Acción invocada cuando el usuario hace clic para detener el componente.", + "temperature-step": "Incremento de paso de temperatura", + "heat-pump-color": "Color de la bomba de calor", + "power-button-background": "Fondo del botón de encendido", + "value-box-background": "Fondo de la caja de valor", + "value-units": "Unidades del valor", + "filtration-mode": "Modo de filtración", + "filtration-mode-hint": "Valor entero que indica el modo de filtración actual.", + "filtration-mode-update": "Estado de actualización del modo de filtración", + "filtration-mode-update-hint": "Acción invocada cuando el usuario hace clic para cambiar el modo de filtración actual.", + "filter-mode": "Filtrar", + "waste-mode": "Desecho", + "backwash-mode": "Contralavado", + "recirculate-mode": "Recircular", + "rinse-mode": "Enjuague", + "closed-mode": "Cerrado", + "sand-filter-color": "Color del filtro de arena", + "mode-box-background": "Fondo de la caja de modo", + "border-color": "Color del borde", + "label-color": "Color de la etiqueta", + "water-leak-hint": "Indica si hay una fuga.", + "default-color": "Color predeterminado", + "leak-color": "Color de fuga", + "full-value": "Valor máximo", + "full-value-hint": "Valor decimal que indica el valor completo.", + "label": "Etiqueta", + "icon": "Icono", + "button-color": "Color del botón", + "on-label": "Texto de la etiqueta 'Encendido'", + "off-label": "Texto de la etiqueta 'Apagado'", + "arrow-presence": "Presencia de flecha", + "arrow-presence-hint": "Indica si hay una flecha presente en el conector.", + "arrow-present": "Flecha presente", + "arrow-direction": "Dirección de flecha/animación", + "arrow-direction-hint": "Indica la dirección del flujo.", + "flow-animation": "Animación de flujo", + "flow-animation-hint": "Indica si hay animación presente en el conector.", + "flow": "Flujo", + "flow-line": "Línea", + "flow-line-style": "Estilo de línea", + "flow-style-hint": "Establece los valores de Trazo y Espacio de manera que su suma sea divisible por 100 sin residuo para una sincronización perfecta de la animación.", + "flow-dash-cap": "Terminación de línea discontinua del flujo", + "dash-cap-butt": "Final recto", + "dash-cap-round": "Final redondeado", + "dash-cap-square": "Final cuadrado", + "dash": "Guion", + "gap": "Espacio", + "main-line": "Línea principal", + "line": "Línea", + "line-color": "Color de la línea", + "arrow-color": "Color de la flecha", + "target-value": "Valor objetivo", + "target-value-hint": "Indica el punto objetivo en la escala.", + "min-max-value": "Valor mínimo y máximo", + "min-value": "Mínimo", + "max-value": "Máximo", + "progress-bar": "Barra de progreso", + "progress-arrow": "Flecha de progreso", + "warning-scale-color": "Color de escala de advertencia", + "critical-scale-color": "Color de escala crítica", + "scale-color": "Color de la escala", + "target": "Objetivo", + "high-warning-state": "Estado de advertencia alta", + "show-high-warning-scale": "Mostrar escala de advertencia alta", + "high-warning-scale": "Escala de advertencia alta", + "high-warning-state-hint": "Valor decimal que indica un rango de advertencia alta hasta un valor crítico alto o máximo.", + "low-warning-state": "Estado de advertencia baja", + "show-low-warning-scale": "Mostrar escala de advertencia baja", + "low-warning-scale": "Escala de advertencia baja", + "low-warning-state-hint": "Valor decimal que indica un rango de advertencia baja hasta un valor crítico bajo o mínimo.", + "high-critical-state": "Estado crítico alto", + "show-high-critical-scale": "Mostrar escala crítica alta", + "high-critical-scale": "Escala crítica alta", + "high-critical-state-hint": "Valor decimal que indica un rango crítico alto hasta el valor máximo de la escala.", + "low-critical-state": "Estado crítico bajo", + "show-low-critical-scale": "Mostrar escala crítica baja", + "low-critical-scale": "Escala crítica baja", + "low-critical-state-hint": "Valor decimal que indica un rango crítico bajo hasta el valor mínimo de la escala.", + "filter-color": "Color del filtro", + "colors": "Colores", + "indicator-colors": "Colores del indicador", + "enabled": "Habilitado", + "disabled": "Deshabilitado", + "on": "ENCENDIDO", + "off": "APAGADO", + "on-off-state": "Estado encendido/apagado", + "on-off-state-hint": "Indica si el componente está en estado Encendido o Apagado.", + "on-update-state": "Actualizar estado a encendido", + "on-update-state-hint": "Acción invocada cuando el usuario hace clic para actualizar el estado a Encendido.", + "off-update-state": "Actualizar estado a apagado", + "off-update-state-hint": "Acción invocada cuando el usuario hace clic para actualizar el estado a Apagado.", + "voltage": "Voltaje", + "input-voltage": "Voltaje de entrada", + "input-voltage-hint": "Valor decimal que indica el voltaje de entrada.", + "output-voltage": "Voltaje de salida", + "output-voltage-hint": "Valor decimal que indica el voltaje de salida.", + "first-phase-voltage": "Voltaje de la primera fase", + "second-phase-voltage": "Voltaje de la segunda fase", + "third-phase-voltage": "Voltaje de la tercera fase", + "phase-voltage-hint": "Valor decimal que indica el voltaje para la fase actual.", + "voltage-hint": "Valor decimal que indica el voltaje actual.", + "current-voltage-color": "Color del voltaje actual", + "phase-indicator-color": "Color del indicador de fase", + "measured": "Medido", + "measured-hint": "Valor decimal que indica el uso de energía en kilovatios-hora.", + "day-rate": "Tarifa diurna", + "night-rate": "Tarifa nocturna", + "off-peak-rate": "Tarifa valle", + "peak-rate": "Tarifa punta", + "export-rate": "Tarifa de exportación", + "operating-mode": "Modo de funcionamiento", + "bypass-mode": "Bypass", + "operating-mode-hint": "Valor entero que indica el modo de funcionamiento actual (0 - APAGADO, 1 - ENCENDIDO, 2 - BYPASS).", + "connected": "Conectado", + "connected-hint": "Indica si el componente está en estado conectado.", + "disconnected": "Desconectado", + "indicator": "Indicador", + "operation-mode": "Modo de operación", + "operation-mode-hint": "Indica si el inversor está en modo Red eléctrica o Inversor.", + "operation-mode-indicators-color": "Color de los indicadores del modo de operación", + "mains-on-mode": "Red eléctrica activa", + "inverter-on-mode": "Inversor activo", + "charging-mode": "Modo de carga", + "charging-mode-hint": "Valor entero que indica el modo de carga actual (1 - Carga rápida, 2 - Absorción, 3 - Flotación).", + "charging-mode-indicators-color": "Color de los indicadores del modo de carga", + "inverter-faults": "Fallas", + "inverter-fault-indicators-color": "Color de los indicadores de fallas", + "overload-fault": "Sobrecarga", + "overload-fault-hint": "Indica si el inversor está en condición de sobrecarga.", + "low-battery-fault": "Batería baja", + "low-battery-fault-hint": "Indica si la batería está excesivamente descargada.", + "temperature-fault": "Temperatura", + "temperature-fault-hint": "Indica si hay alta temperatura en un inversor.", + "triangle": "Triángulo", + "socket": "Enchufe", + "left-button": "Botón izquierdo", + "right-button": "Botón derecho", + "alarm-colors": "Colores de alarma", + "hook-color": "Color del gancho" + } + }, + "item": { + "selected": "Seleccionado" + }, + "js-func": { + "no-return-error": "¡La función debe devolver un valor!", + "return-type-mismatch": "¡La función debe devolver un valor del tipo '{{type}}'!", + "tidy": "Ordenar", + "mini": "Mini", + "modules": "Módulos", + "remove-module": "Eliminar módulo", + "no-modules": "No hay módulos configurados", + "add-module": "Agregar módulo", + "module-alias": "Alias", + "invalid-module-alias-name": "Nombre de alias inválido", + "module-resource": "Recurso del módulo JS", + "not-unique-module-aliases-error": "¡Los alias de los módulos deben ser únicos!", + "show-module-info": "Mostrar información del módulo", + "show-module-source-code": "Mostrar código fuente del módulo", + "module-members": "Miembros del módulo", + "module-no-members": "El módulo no tiene miembros exportados", + "module-load-error": "Error al cargar el módulo", + "source-code": "Código fuente", + "source-code-load-error": "Error al cargar el código fuente", + "no-js-module-text": "No se encontraron módulos JS", + "no-js-module-matching": "No se encontraron módulos JS que coincidan con '{{module}}'." + }, + "key-val": { + "key": "Clave", + "value": "Valor", + "remove-entry": "Eliminar entrada", + "add-entry": "Agregar entrada", + "no-data": "No hay entradas" + }, + "layout": { + "layout": "Diseño", + "layouts": "Diseños", + "manage": "Gestionar diseños", + "settings": "Configuración del diseño", + "color": "Color", + "main": "Principal", + "right": "Derecha", + "left": "Izquierda", + "select": "Seleccionar diseño objetivo", + "percentage-width": "Ancho porcentual (%)", + "fixed-width": "Ancho fijo (px)", + "left-width": "Columna izquierda (%)", + "right-width": "Columna derecha (%)", + "pick-fixed-side": "Lado fijo:", + "layout-fixed-width": "Ancho fijo (px)", + "value-min-error": "El valor debe ser mayor que {{min}}{{unit}}", + "value-max-error": "El valor debe ser menor que {{max}}{{unit}}", + "layout-fixed-width-required": "Se requiere ancho fijo", + "right-width-percentage-required": "Se requiere porcentaje derecho", + "left-width-percentage-required": "Se requiere porcentaje izquierdo", + "divider": "Divisor", + "right-side": "Diseño del lado derecho", + "left-side": "Diseño del lado izquierdo", + "add-new-breakpoint": "Agregar nuevo punto de interrupción", + "breakpoint": "Punto de interrupción", + "breakpoints": "Puntos de interrupción", + "copy-from": "Copiar desde", + "size": "Tamaño", + "delete-breakpoint-title": "¿Estás seguro de que deseas eliminar el punto de interrupción '{{name}}'?", + "delete-breakpoint-text": "Ten en cuenta que, después de la confirmación, el punto de interrupción será irrecuperable y la configuración volverá al punto de interrupción predeterminado." + }, + "legend": { + "direction": "Dirección", + "position": "Posición", + "show-values": "Mostrar valores", + "min-option": "Mínimo", + "max-option": "Máximo", + "average-option": "Promedio", + "total-option": "Total", + "latest-option": "Último", + "sort-legend": "Ordenar claves de datos en la leyenda", + "show-max": "Mostrar valor máximo", + "show-min": "Mostrar valor mínimo", + "show-avg": "Mostrar valor promedio", + "show-total": "Mostrar valor total", + "show-latest": "Mostrar último valor", + "settings": "Configuración de leyenda", + "min": "mín", + "max": "máx", + "avg": "prom", + "total": "total", + "latest": "último", + "Min": "Mínimo", + "Max": "Máximo", + "Avg": "Promedio", + "Total": "Total", + "Latest": "Último", + "comparison-time-ago": { + "previousInterval": "(intervalo anterior)", + "customInterval": "(intervalo personalizado)", + "days": "(hace un día)", + "weeks": "(hace una semana)", + "months": "(hace un mes)", + "years": "(hace un año)" }, - "edge-event": { - "type-dashboard": "Panel", - "type-asset": "Activo", - "type-device": "Dispositivo", - "type-device-profile": "Perfil de dispositivo", - "type-asset-profile": "Perfil de activo", - "type-entity-view": "Vista de entidad", - "type-alarm": "Alarma", - "type-rule-chain": "Cadena de reglas", - "type-rule-chain-metadata": "Metadatos de Cadena de Reglas", - "type-edge": "Edge", - "type-user": "Usuario", - "type-tenant": "Propietario", - "type-tenant-profile": "Perfil de Propietario", - "type-customer": "Cliente", - "type-relation": "Relación", - "type-widgets-bundle": "Paquete de Widgets", - "type-widgets-type": "Tipos de Widgets", - "type-admin-settings": "Ajustes de Administración", - "type-ota-package": "Paquete OTA", - "type-queue": "Cola", - "action-type-added": "Añadido", - "action-type-deleted": "Borrado", - "action-type-updated": "Actualizado", - "action-type-post-attributes": "Envío de Atributos", - "action-type-attributes-updated": "Atributos Actualizados", - "action-type-attributes-deleted": "Atributos Borrados", - "action-type-timeseries-updated": "Series de tiempo Actualizadas", - "action-type-credentials-updated": "Credenciales Actualizadas", - "action-type-assigned-to-customer": "Asignado a Cliente", - "action-type-unassigned-from-customer": "Desasignado de Cliente", - "action-type-relation-add-or-update": "Añadir o Actualizar relación", - "action-type-relation-deleted": "Relación borrada", - "action-type-rpc-call": "Llamada RPC", - "action-type-alarm-ack": "ACK Alarma", - "action-type-alarm-clear": "Borrado de Alarma", - "action-type-alarm-assigned": "Alarma Asignada", - "action-type-alarm-unassigned": "Alarma Desasignada", - "action-type-assigned-to-edge": "Asignado a Edge", - "action-type-unassigned-from-edge": "Desasignado de Edge", - "action-type-credentials-request": "Obtención de Credenciales", - "action-type-entity-merge-request": "Unión de entidades" + "column-title": "Título de columna", + "label": "Etiqueta", + "value": "Valor" + }, + "login": { + "login": "Iniciar sesión", + "request-password-reset": "Solicitar restablecimiento de contraseña", + "reset-password": "Restablecer contraseña", + "create-password": "Crear contraseña", + "two-factor-authentication": "Autenticación de dos factores", + "passwords-mismatch-error": "¡Las contraseñas ingresadas deben coincidir!", + "password-again": "Repetir contraseña", + "sign-in": "Por favor inicia sesión", + "username": "Nombre de usuario (correo electrónico)", + "remember-me": "Recordarme", + "forgot-password": "¿Olvidaste tu contraseña?", + "password-reset": "Restablecimiento de contraseña", + "expired-password-reset-message": "¡Tus credenciales han expirado! Por favor crea una nueva contraseña.", + "new-password": "Nueva contraseña", + "new-password-again": "Confirmar nueva contraseña", + "password-link-sent-message": "Enlace de restablecimiento enviado", + "email": "Correo electrónico", + "invalid-email-format": "Formato de correo electrónico inválido.", + "login-with": "Iniciar sesión con {{name}}", + "or": "o", + "error": "Error de inicio de sesión", + "verify-your-identity": "Verifica tu identidad", + "select-way-to-verify": "Selecciona una forma de verificación", + "resend-code": "Reenviar código", + "resend-code-wait": "Reenviar código en { time, plural, =1 {1 segundo} other {# segundos} }", + "try-another-way": "Intentar otra forma", + "totp-auth-description": "Por favor ingresa el código de seguridad desde tu aplicación de autenticación.", + "totp-auth-placeholder": "Código", + "sms-auth-description": "Se ha enviado un código de seguridad a tu teléfono en {{contact}}.", + "sms-auth-placeholder": "Código SMS", + "email-auth-description": "Se ha enviado un código de seguridad a tu correo electrónico en {{contact}}.", + "email-auth-placeholder": "Código de correo", + "backup-code-auth-description": "Por favor ingresa uno de tus códigos de respaldo.", + "backup-code-auth-placeholder": "Código de respaldo", + "activation-link-expired": "El enlace de activación ha expirado", + "activation-link-expired-message": "El enlace para activar tu perfil ha expirado. Puedes regresar a la página de inicio de sesión para recibir un nuevo correo.", + "reset-password-link-expired": "El enlace para restablecer la contraseña ha expirado", + "reset-password-link-expired-message": "El enlace para restablecer tu contraseña ha expirado. Puedes regresar a la página de inicio de sesión para recibir un nuevo correo." + }, + "mobile": { + "add-application": "Agregar aplicación", + "app-id": "ID de la aplicación", + "app-id-required": "Se requiere el ID de la aplicación", + "app-id-pattern": "Formato de ID de aplicación inválido", + "app-store-link": "Enlace de App Store", + "app-store-link-required": "Se requiere el enlace de App Store", + "application-details": "Detalles de la aplicación", + "application-package": "Paquete de la aplicación", + "application-secret": "Secreto de la aplicación", + "application-secret-required": "Se requiere el secreto de la aplicación", + "application": "Aplicación", + "applications": "Aplicaciones", + "copy-app-id": "Copiar ID de la aplicación", + "copy-app-store-link": "Copiar enlace de App Store", + "copy-application-package": "Copiar paquete de la aplicación", + "copy-application-secret": "Copiar secreto de la aplicación", + "copy-google-play-link": "Copiar enlace de Google Play", + "copy-sha256-certificate-fingerprints": "Copiar huellas digitales SHA256 del certificado", + "delete-application": "Eliminar aplicación", + "delete-application-button-text": "Entiendo las consecuencias, eliminar aplicación", + "delete-application-text": "Esta acción no se puede deshacer. Esto eliminará permanentemente tu aplicación.
Si no deseas eliminarla permanentemente puedes suspender temporalmente la aplicación.
Para eliminar la aplicación de todos modos, escribe \"{{phrase}}\" para confirmar.", + "delete-application-title-short": "¿Estás seguro de que deseas eliminar la aplicación '{{name}}'?", + "delete-application-text-short": "Ten cuidado, después de la confirmación la aplicación y todos los datos relacionados serán irrecuperables.", + "delete-application-phrase": "eliminar aplicación", + "delete-applications-bundle-text": "Ten cuidado, después de la confirmación el paquete móvil y todos los datos relacionados serán irrecuperables.", + "delete-applications-bundle-title": "¿Estás seguro de que deseas eliminar el paquete móvil '{{bundleName}}'?", + "generate-application-secret": "Generar secreto de la aplicación", + "google-play-link": "Enlace de Google Play", + "google-play-link-required": "Se requiere el enlace de Google Play", + "latest-version": "Última versión", + "min-version": "Versión mínima", + "invalid-version-pattern": "Formato de versión inválido. Usa el formato: mayor.menor.revisión (ej., 1.0.0).", + "mobile-center": "Centro móvil", + "mobile-package": "Paquete de la aplicación", + "mobile-package-max-length": "El paquete de la aplicación debe tener menos de 256 caracteres", + "mobile-package-required": "Se requiere el paquete de la aplicación", + "mobile-package-pattern": "Formato inválido del paquete de la aplicación", + "no-application": "No se encontraron aplicaciones", + "no-bundles": "No se encontraron paquetes", + "platform-type": "Tipo de plataforma", + "search-application": "Buscar aplicaciones", + "search-bundles": "Buscar paquetes", + "set": "Establecer", + "sha256-certificate-fingerprints": "Huellas digitales SHA256 del certificado", + "sha256-certificate-fingerprints-required": "Se requieren las huellas digitales SHA256 del certificado", + "sha256-certificate-fingerprints-pattern": "Formato inválido de huella digital SHA256 del certificado", + "show-hidden-pages": "Mostrar páginas ocultas", + "status": "Estado", + "status-type": { + "deprecated": "Obsoleto", + "draft": "Borrador", + "published": "Publicado", + "suspended": "Suspendido" }, - "error": { - "unable-to-connect": "Imposible conectar con el servidor! Por favor, revise su conexión a internet.", - "unhandled-error-code": "Código de error no controlado: {{errorCode}}", - "unknown-error": "Error desconocido" + "store-information": "Información de la tienda", + "version-information": "Información de la versión", + "min-version-release-notes": "Notas de la versión mínima", + "latest-version-release-notes": "Notas de la última versión", + "bundle": "Paquete", + "bundles": "Paquetes", + "add-bundle": "Agregar paquete", + "title": "Título", + "title-required": "Se requiere título", + "title-cannot-contain-only-spaces": "El título no puede contener solo espacios", + "title-max-length": "El título debe tener menos de 256 caracteres", + "oauth-clients": "Clientes OAuth 2.0", + "android-app": "Aplicación Android", + "android-application": "Aplicación Android", + "ios-app": "Aplicación iOS", + "ios-application": "Aplicación iOS", + "invalid-store-link": "Enlace de tienda inválido", + "enable-oauth": "Habilitar OAuth 2.0", + "enable-self-registration": "Habilitar auto registro", + "edit-bundle": "Editar paquete", + "description": "Descripción", + "basic-settings": "Configuraciones básicas", + "no-application-matching": "No se encontró ninguna aplicación que coincida con '{{entity}}'.", + "no-bundle-matching": "No se encontró ningún paquete que coincida con '{{entity}}'.", + "application-required": "Se requiere la aplicación.", + "bundle-required": "Se requiere el paquete.", + "no-application-text": "No se encontraron aplicaciones", + "no-bundle-text": "No se encontró paquete", + "layout": "Diseño", + "pages": "Páginas", + "hide-all-pages": "Ocultar todas las páginas", + "reset-to-default-pages": "Restablecer a las páginas predeterminadas", + "add-specific-page": "Agregar página específica", + "visible": "Visible", + "hidden": "Oculto", + "reset-to-page-default": "Restablecer página a predeterminado", + "mobile-599": "Móvil (máx. 599px)", + "tablet-959": "Tableta (máx. 959px)", + "max-element-number": "Número máximo de elementos", + "page-name": "Nombre de la página", + "page-name-required": "Se requiere el nombre de la página.", + "page-name-cannot-contain-only-spaces": "El nombre de la página no puede contener solo espacios.", + "page-name-max-length": "El nombre de la página debe tener menos de 256 caracteres", + "page-type": "Tipo de página", + "pages-types": { + "dashboard": "Tablero", + "web-view": "Vista web", + "custom": "Personalizado" }, - "entity": { - "entity": "Entidad", - "entities": "Entidades", - "entities-count": "Nº de entidades", - "alarms-count": "Nº de alarmas", - "aliases": "Alias de entidad", - "aliases-short": "Alias", - "entity-alias": "Alias de entidad", - "unable-delete-entity-alias-title": "No ha sido posible eliminar el alias de entidad", - "unable-delete-entity-alias-text": "El alias de entidad '{{entityAlias}}' no puede ser eliminado ya que se esta usando por los siguientes widgets:
{{widgetsList}}", - "duplicate-alias-error": "Encontrado un alias duplicado '{{alias}}'.
Loas alias de entidad tienen que ser únicos para cada panel.", - "missing-entity-filter-error": "Falta el filtro para el alias '{{alias}}'.", - "configure-alias": "Configurar alias '{{alias}}' ", - "alias": "Alias", - "alias-required": "Alias de entidad requerido.", - "remove-alias": "Eliminar alias de entidad", - "add-alias": "Añadir alias de entidad", - "entity-list": "Lista de entidades", - "entity-type": "Tipo de entidad", - "entity-types": "Tipos de entidades", - "entity-type-list": "Lista de tipos de entidad", - "any-entity": "Cualquier entdad", - "add-entity-type": "Añadir tipo de entidad", - "enter-entity-type": "Introducir tipo de entidad", - "no-entities-matching": "No se han encontrado entidades que coincidan con '{{entity}}' .", - "no-entity-types-matching": "No se han encontrado tipos de entidad que coincidan con '{{entityType}}' .", - "name-starts-with": "Nombre empieza con", - "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", - "use-entity-name-filter": "Usar filtro", - "entity-list-empty": "No hay entidades seleccionadas.", - "entity-type-list-required": "Al menos debe seleccionar un tipo de entidad", - "entity-name-filter-required": "Filtro de nombre de entidad requerido.", - "entity-name-filter-no-entity-matched": "No hay entidades que comiencen con '{{entity}}' .", - "all-subtypes": "Todos", - "select-entities": "Seleccionar entidades", - "no-aliases-found": "No se han encontrado alias.", - "no-alias-matching": "'{{alias}}' no encontrado.", - "create-new-alias": "Crear nuevo alias!", - "create-new": "Crear nuevo", - "key": "Clave", - "key-name": "Nombre de clave", - "no-keys-found": "No se han encontrado claves.", - "no-key-matching": "'{{key}}' no encontrada.", - "create-new-key": "Crear nueva clave!", - "type": "Tipo", - "type-required": "Tipo de entidad requerido.", - "type-device": "Dispositivo", - "type-devices": "Dispositivos", - "list-of-devices": "{ count, plural, =1 {Un dispositivo} other {Lista de # Dispositivos} }", - "device-name-starts-with": "Dispositivos cuyos nombres comiencen por '{{prefix}}'", - "type-device-profile": "Perfil de dispositivo", - "type-device-profiles": "Perfiles de dispositivo", - "clear-selected-profiles": "Borrar perfiles seleccionados", - "list-of-device-profiles": "{ count, plural, =1 {un perfil} other {Lista de # perfiles} }", - "device-profile-name-starts-with": "Perfiles cuyo nombre empiece por '{{prefix}}'", - "type-asset-profile": "Perfil de activo", - "type-asset-profiles": "Perfiles de activo", - "list-of-asset-profiles": "{ count, plural, =1 {Un perfil de activo} other {Lista de # perfiles de activos} }", - "asset-profile-name-starts-with": "Perfiles de activo cuyo nombre comience por '{{prefix}}'", - "type-asset": "Activo", - "type-assets": "Activos", - "list-of-assets": "{ count, plural, =1 {Un activo} other {Lista de # activos} }", - "asset-name-starts-with": "Activos cuyos nombres comiencen por '{{prefix}}'", - "type-entity-view": "Vista Entidad", - "type-entity-views": "Vistas Entidades", - "list-of-entity-views": "{ count, plural, =1 {Una vista de entidad} other {Lista de # Vistas de Entidades} }", - "entity-view-name-starts-with": "Vistas de Entidades cuyo nombre comiencen por '{{prefix}}'", - "type-rule": "Regla", - "type-rules": "Reglas", - "list-of-rules": "{ count, plural, =1 {Una regla} other {Lista de # reglas} }", - "rule-name-starts-with": "Reglas cuyos nombres comiencen por '{{prefix}}'", - "type-plugin": "Plugin", - "type-plugins": "Plugins", - "list-of-plugins": "{ count, plural, =1 {Un plugin} other {Lista de # plugins} }", - "plugin-name-starts-with": "Plugins cuyos nombres comiencen por '{{prefix}}'", - "type-tenant": "Propietario", - "type-tenants": "Propietarios", - "list-of-tenants": "{ count, plural, =1 {Un propietario} other {Lista de # propietarios} }", - "tenant-name-starts-with": "Propietarios cuyo nombre comience por '{{prefix}}'", - "type-tenant-profile": "Perfil de Propietario", - "type-tenant-profiles": "Perfiles de propietario", - "list-of-tenant-profiles": "{ count, plural, =1 {Un perfil de propietario} other {Lista de # perfiles de propietario} }", - "tenant-profile-name-starts-with": "Pefiles de propietario cuyo nombre empiece por '{{prefix}}'", - "type-customer": "Cliente", - "type-customers": "Clientes", - "list-of-customers": "{ count, plural, =1 {Un cliente} other {Lista de # clientes} }", - "customer-name-starts-with": "Clientes cuyos nombres comiencen por '{{prefix}}'", - "type-user": "Usuario", - "type-users": "Usuarios", - "list-of-users": "{ count, plural, =1 {Un usuario} other {Lista de # usuarios} }", - "user-name-starts-with": "Usuarios cuyos nombres comiencen por '{{prefix}}'", - "type-dashboard": "Panel", - "type-dashboards": "Paneles", - "list-of-dashboards": "{ count, plural, =1 {Un panel} other {Lista de # paneles} }", - "dashboard-name-starts-with": "Paneles cuyos nombres comiencen por '{{prefix}}'", - "type-alarm": "Alarma", - "type-alarms": "Alarmas", - "list-of-alarms": "{ count, plural, =1 {Una alarma} other {Lista de # alarmas} }", - "alarm-name-starts-with": "Alarmas cuyos nombres comienzan con '{{prefix}}'", - "type-rulechain": "Cadena de reglas", - "type-rulechains": "Cadenas de reglas", - "list-of-rulechains": "{ count, plural, =1 {Una cadena de reglas} other {Lista de # cadenas de reglas} }", - "rulechain-name-starts-with": "Cadenas de reglas cuyos nombres comienzan con '{{prefix}}'", - "type-rulenode": "Nodo de reglas", - "type-rulenodes": "Nodos de reglas", - "list-of-rulenodes": "{ count, plural, =1 {Un nodo de reglas} other {Lista de # nodos de reglas} }", - "rulenode-name-starts-with": "Nodos de reglas cuyos nombres comienzan con '{{prefix}}'", - "type-current-customer": "Cliente Actual", - "type-current-tenant": "Propietario Actual", - "type-current-user": "Usuario Actual", - "type-current-user-owner": "Usuario Propietario Actual", - "type-widgets-bundle": "Paquete de widgets", - "type-widgets-bundles": "Paquetes de widgets", - "list-of-widgets-bundles": "{ count, plural, =1 {Un paquete de widget} other {Lista de # paquetes de widgets} }", - "type-widget-type": "Tipo de widget", - "type-widget-types": "Tipos de widget", - "list-of-widget-types": "{ count, plural, =1 {Un tipo de widget} other {Lista de # tipos de widgets} }", - "search": "Buscar entidades", - "selected-entities": "{ count, plural, =1 {1 entidad} other {# entidades} } seleccionadas", - "entity-label": "Etiqueta de entidad", - "entity-name": "Nombre de entidad", - "details": "Detalles de entidad", - "no-entities-prompt": "No se han encontrado entidades", - "no-data": "No hay datos que mostrar", - "columns-to-display": "Columnas a Mostrar", - "type-api-usage-state": "Estado de uso de la API", - "type-edge": "Edge", - "type-edges": "Edges", - "list-of-edges": "{count, plural, =1 {Un Edge} other {Lista de # Edges} }", - "edge-name-starts-with": "Edges cuyos nombres comienzan con '{{prefijo}}'", - "type-tb-resource": "Recurso", - "type-ota-package": "Paquete OTA", - "type-rpc": "RPC", - "type-queue": "Cola", - "type-notification": "Notificación", - "type-notification-rule": "Regla de notificación", - "type-notification-rules": "Reglas de notificación", - "list-of-notification-rules": "{ count, plural, =1 {Una regla de notificación} other {Lista de # reglas de notificación} }", - "type-notification-target": "Destinatario de notificación", - "type-notification-targets": "Destinatarios de notificación", - "list-of-notification-targets": "{ count, plural, =1 {Un destinatario} other {Lista de # destinatarios} }", - "type-notification-request": "Solicitud de notificaciones", - "type-notification-template": "Plantilla de notificaciones", - "type-notification-templates": "Plantillas de notificación", - "list-of-notification-templates": "{ count, plural, =1 {Una plantilla de notificación} other {Lista de # plantillas de notificación} }" + "url": "URL", + "invalid-url-format": "Formato de URL inválido", + "path": "Ruta", + "invalid-path-format": "Formato de ruta inválido", + "custom-page": "Página personalizada", + "edit-page": "Editar página", + "edit-custom-page": "Editar página personalizada", + "delete-page": "Eliminar página", + "qr-code-widget": "Widget de código QR", + "type-here": "Escribe aquí", + "configuration-dialog": "Diálogo de configuración", + "configuration-app": "Aplicación de configuración", + "configuration-step": { + "prepare-environment-title": "Preparar entorno de desarrollo", + "prepare-environment-text": "La aplicación móvil ThingsBoard Flutter requiere el SDK de Flutter. Sigue las instrucciones para configurar el SDK.", + "get-source-code-title": "Obtener código fuente de la aplicación", + "get-source-code-text": "Puedes obtener el código fuente de la aplicación móvil ThingsBoard Flutter clonándolo desde el repositorio de GitHub:", + "configure-api-title": "Configurar el endpoint de la API de ThingsBoard", + "configure-api-text": "Abre el proyecto flutter_thingsboard_pe_app en tu editor/IDE. Edita:", + "configure-api-hint": "Establece el valor de la constante thingsBoardApiEndpoint para que coincida con el endpoint API de tu instancia de servidor ThingsBoard. No uses los nombres de host “localhost” o “127.0.0.1”.", + "run-app-title": "Ejecutar la aplicación", + "run-app-text": "Ejecuta la aplicación según lo descrito en tu IDE.\nSi usas la terminal, ejecuta la aplicación con el siguiente comando:", + "more-information": "Información detallada se encuentra en nuestra documentación de introducción.", + "getting-started": "Guía de inicio", + "configure-package-title": "Configurar paquete de la aplicación", + "configure-package-text": "Puedes cambiar manualmente el paquete de la aplicación o usar una herramienta CLI de terceros.", + "configure-package-text-install": "Para instalar la herramienta Rename CLI, ejecuta el siguiente comando:", + "configure-package-run-commands": "Ejecuta estos comandos en el directorio raíz de tu proyecto:" + } + }, + "notification": { + "action-button": "Botón de acción", + "action-type": "Tipo de acción", + "active": "Activo", + "add-notification-recipients-group": "Agregar grupo de destinatarios de notificaciones", + "add-notification-template": "Agregar plantilla de notificación", + "add-recipient": "Agregar destinatario", + "add-recipients": "Agregar destinatarios", + "add-rule": "Agregar regla", + "add-stage": "Agregar etapa", + "add-template": "Agregar plantilla", + "after": "Después", + "alarm-assignment-trigger-settings": "Configuración del disparador de asignación de alarma", + "alarm-comment-trigger-settings": "Configuración del disparador de comentario de alarma", + "alarm-trigger-settings": "Configuración del disparador de alarma", + "all": "Todos", + "api-feature-hint": "Si el campo está vacío, el disparador se aplicará a todas las funciones de la API", + "api-usage-trigger-settings": "Configuración del disparador de uso de API", + "new-platform-version-trigger-settings": "Configuración del disparador de nueva versión de plataforma", + "rate-limits-trigger-settings": "Configuración del disparador por límites de tasa excedidos", + "task-processing-failure-trigger-settings": "Configuración del disparador por error en procesamiento de tareas", + "at-least-one-should-be-selected": "Debe seleccionarse al menos uno", + "basic-settings": "Configuraciones básicas", + "button-text": "Texto del botón", + "button-text-required": "Se requiere el texto del botón", + "button-text-max-length": "El texto del botón debe tener menos o igual a {{ length }} caracteres", + "compose": "Redactar", + "conversation": "Conversación", + "conversation-required": "Se requiere una conversación", + "copy-notification-template": "Copiar plantilla de notificación", + "copy-rule": "Copiar regla", + "copy-template": "Copiar plantilla", + "create-new": "Crear nuevo", + "created": "Creado", + "customize-messages": "Personalizar mensajes", + "delete-notification-text": "Ten cuidado, después de la confirmación, la notificación será irrecuperable.", + "delete-notification-title": "¿Estás seguro de que deseas eliminar la notificación?", + "delete-notifications-text": "Ten cuidado, después de la confirmación, las notificaciones serán irrecuperables.", + "delete-notifications-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 notificación} other {# notificaciones} }?", + "delete-recipient-text": "Ten cuidado, después de la confirmación, el destinatario será irrecuperable.", + "delete-recipient-title": "¿Estás seguro de que deseas eliminar al destinatario '{{recipientName}}'?", + "delete-recipients-text": "Ten cuidado, después de la confirmación, los destinatarios serán irrecuperables.", + "delete-recipients-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 destinatario} other {# destinatarios} }?", + "delete-request-text": "Ten cuidado, después de la confirmación, la solicitud será irrecuperable.", + "delete-request-title": "¿Estás seguro de que deseas eliminar la solicitud?", + "delete-requests-text": "Ten cuidado, después de la confirmación, las solicitudes serán irrecuperables.", + "delete-requests-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 solicitud} other {# solicitudes} }?", + "delete-rule-text": "Ten cuidado, después de la confirmación, la regla será irrecuperable.", + "delete-rule-title": "¿Estás seguro de que deseas eliminar la regla '{{ruleName}}'?", + "delete-rules-text": "Ten cuidado, después de la confirmación, las reglas serán irrecuperables.", + "delete-rules-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 regla} other {# reglas} }?", + "delete-template-text": "Ten cuidado, después de la confirmación, la plantilla será irrecuperable.", + "delete-template-title": "¿Estás seguro de que deseas eliminar la plantilla '{{templateName}}'?", + "delete-templates-text": "Ten cuidado, después de la confirmación, las plantillas serán irrecuperables.", + "delete-templates-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 plantilla} other {# plantillas} }?", + "deleted": "Eliminado", + "delivery-method": { + "delivery-method": "Método de entrega", + "email": "Correo electrónico", + "email-preview": "Vista previa de notificación por correo", + "slack": "Slack", + "slack-preview": "Vista previa de notificación por Slack", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Vista previa de notificación por Microsoft Teams", + "sms": "SMS", + "sms-preview": "Vista previa de notificación por SMS", + "web": "Web", + "web-preview": "Vista previa de notificación web", + "mobile-app": "Aplicación móvil", + "mobile-app-preview": "Vista previa de notificación por aplicación móvil" }, - "entity-field": { - "created-time": "Hora de creación", - "name": "Nombre", - "type": "Tipo", - "first-name": "Nombre", - "last-name": "Apellido", - "email": "Correo electrónico", - "title": "Título", - "country": "País", - "state": "Estado", - "city": "Ciudad", - "address": "Dirección", - "address2": "Dirección 2", - "zip": "Código postal", - "phone": "Teléfono", - "label": "Etiqueta" + "delivery-method-not-configure-click": "El método de entrega no está configurado. Haz clic para configurar.", + "delivery-method-not-configure-contact": "El método de entrega no está configurado. Contacta con tu administrador del sistema.", + "delivery-methods": "Métodos de entrega", + "description": "Descripción", + "device-activity-trigger-settings": "Configuración del disparador de actividad del dispositivo", + "device-list-rule-hint": "Si el campo está vacío, el disparador se aplicará a todos los dispositivos", + "device-profiles-list-rule-hint": "Si el campo está vacío, el disparador se aplicará a todos los perfiles de dispositivo", + "disabled": "Deshabilitado", + "edge-trigger-settings": "Configuración del disparador de Edge", + "edge-list-rule-hint": "Si el campo está vacío, el disparador se aplicará a todas las instancias de Edge", + "edit-notification-recipients-group": "Editar grupo de destinatarios de notificaciones", + "edit-notification-template": "Editar plantilla de notificación", + "edit-rule": "Editar regla", + "edit-template": "Editar plantilla", + "enabled": "Habilitado", + "entities-limit-trigger-settings": "Configuración del disparador por límite de entidades", + "entity-action-trigger-settings": "Configuración del disparador de acción de entidad", + "entity-type": "Tipo de entidad", + "escalation-chain": "Cadena de escalado", + "failed-send": "Fallos de envío", + "fails": "{ count, plural, =1 {1 fallo} other {# fallos} }", + "filter": "Filtro", + "first-recipient": "Primer destinatario", + "inactive": "Inactivo", + "inbox": "Bandeja de entrada", + "notification-inbox": "Notificaciones / Bandeja de entrada", + "input-field-support-templatization": "El campo de entrada admite plantillas.", + "input-fields-support-templatization": "Los campos de entrada admiten plantillas.", + "link": "Enlace", + "link-required": "El enlace es obligatorio", + "link-type": { + "dashboard": "Abrir tablero", + "link": "Abrir enlace URL" }, - "entity-view": { - "entity-view": "Vista de entidad", - "entity-view-required": "Vista de entidad es requerido.", - "entity-views": "Vistas de entidad", - "management": "Gestión de vistas de entidad", - "view-entity-views": "Ver vista de entidad", - "entity-view-alias": "Alias de vista de entidad", - "aliases": "Alias de vista de entidad", - "no-alias-matching": "'{{alias}}' no encontrado.", - "no-aliases-found": "No se encontraron alias.", - "no-key-matching": "'{{key}}' no encontrada.", - "no-keys-found": "No se encontraron claves.", - "create-new-alias": "¡Crear un nuevo!", - "create-new-key": "¡Crear una nueva!", - "duplicate-alias-error": "Alias duplicado'{{alias}}'.
Los alias de Entity View deben ser únicos en el panel.", - "configure-alias": "Configurar alias '{{alias}}'", - "no-entity-views-matching": "No se encontraron vistas que coincidan con '{{entity}}'.", - "public": "Público", - "alias": "Alias", - "alias-required": "Alias de vista de entidad es requerido.", - "remove-alias": "Borrar alias de la vista de entidad", - "add-alias": "Añadir alias a la vista de entidad", - "name-starts-with": "Nombre de vista de entidad comienza con", - "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", - "entity-view-list": "Lista de vistas de entidad", - "use-entity-view-name-filter": "Usar el filtro", - "entity-view-list-empty": "No hay vistas de entidad seleccionadas.", - "entity-view-name-filter-required": "Nombre del filtro de vista de entidad es requerido.", - "entity-view-name-filter-no-entity-view-matched": "No se encontraron vistas de entidad que comiencen con '{{entityView}}'.", - "add": "Añadir vista de entidad", - "entity-view-public": "Vista de entidad es pública", - "assign-to-customer": "Asignar a cliente", - "assign-entity-view-to-customer": "Asignar vista de entidad a cliente", - "assign-entity-view-to-customer-text": "Por favor, seleccione las vistas de entidad para asignar al cliente", - "assign-entity-view-to-edge-title": "Asignar vista(s) de entidad a Edge", - "no-entity-views-text": "No se encontraron vistas de entidad", - "assign-to-customer-text": "Por favor, seleccione el cliente para asignar la vista de entidad", - "entity-view-details": "Detalles de la vista de entidad", - "add-entity-view-text": "Añadir nueva vista de entidad", - "delete": "Borrar vista de entidad", - "assign-entity-views": "Asignar vistas de entidad", - "assign-entity-views-text": "Asignar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } a cliente", - "delete-entity-views": "Borrar vistas de entidad", - "unassign-from-customer": "Anular asignación a cliente", - "unassign-entity-views": "Anular asignación de vistas de entidad", - "unassign-entity-views-action-title": "Anular asignación { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } al cliente", - "assign-new-entity-view": "Asignar nueva vista de entidad", - "delete-entity-view-title": "¿Está seguro que quiere borrar la vista de entidad '{{entityViewName}}'?", - "delete-entity-view-text": "¡Cuidado! Tras la confirmación, la vista de la entidad y todos los datos relacionados serán irrecuperables.", - "delete-entity-views-title": "¿Está seguro que quiere borrar las vistas de entidad { count, plural, =1 {1 entityView} other {# entityViews} }?", - "delete-entity-views-action-title": "Borrar { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }", - "delete-entity-views-text": "¡Cuidado! Tras la confirmación, todas las vistas de entidades seleccionadas se eliminarán y todos los datos relacionados serán irrecuperables.", - "unassign-entity-view-title": "¿Está seguro que quiere anular la asignación de la vista de entidad '{{entityViewName}}'?", - "unassign-entity-view-text": "Tras la confirmación, la vista de la entidad quedará sin asignar y el cliente no podrá acceder a ella.", - "unassign-entity-view": "Anular asignación de la vista de entidad", - "unassign-entity-views-title": "¿Está seguro que quiere anular la asignación de { count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", - "unassign-entity-views-text": "Tras la confirmación, todas las vistas de entidades seleccionadas quedarán sin asignar y el cliente no podrá acceder a ellas.", - "entity-view-type": "Tipo de vista de entidad", - "entity-view-type-required": "Tipo de vista de entidad es requerido.", - "select-entity-view-type": "Seleccione el tipo de vista de entidad", - "enter-entity-view-type": "Teclee el tipo de vista de entidad", - "any-entity-view": "Cualquier vista de entidad", - "no-entity-view-types-matching": "No se encontraron tipos de vista de entidad que coincidan con '{{entitySubtype}}'.", - "entity-view-type-list-empty": "No hay tipos de vista de entidad seleccionados.", - "entity-view-types": "Tipos de vista de entidad", - "created-time": "Fecha de creación", - "name": "Nombre", - "name-required": "Nombre Requerido.", - "name-max-length": "Nombre debe ser menor de 256", - "type-max-length": "Tipo de vista de entidad debe ser menor de 256", - "description": "Descripción", - "events": "Eventos", - "details": "Detalles", - "copyId": "Copiar el Id de la vista de entidad", - "idCopiedMessage": "El Id de la vista de entidad se ha copiado al portapapeles", - "assignedToCustomer": "Asignado a cliente", - "unable-entity-view-device-alias-title": "No se puede eliminar el alias de vista de entidad", - "unable-entity-view-device-alias-text": "El alias del dispositivo '{{entityViewAlias}}' no se puede borrar porque está siendo usado por el widget(s):
{{widgetsList}}", - "select-entity-view": "Seleccionar vista de entidad", - "make-public": "Hacer pública la vista de entidad", - "make-private": "Hacer que la vista de entidad sea privada", - "start-date": "Fecha de inicio", - "start-ts": "Tiempo de inicio", - "end-date": "Fecha de finalización", - "end-ts": "Tiempo de finalización", - "date-limits": "Limites de fecha", - "client-attributes": "Atributos de cliente", - "shared-attributes": "Atributos compartidos", - "server-attributes": "Atributos de servidor", - "timeseries": "Series temporales", - "client-attributes-placeholder": "Atributos de cliente", - "shared-attributes-placeholder": "Atributos compartidos", - "server-attributes-placeholder": "Atributos de servidor", - "timeseries-placeholder": "Series temporales", - "target-entity": "Entidad objetivo", - "attributes-propagation": "Propagación de atributos", - "attributes-propagation-hint": "La vista de entidad copiará automáticamente los atributos especificados de la entidad de destino cada vez que guarde o actualice esta vista de entidad. Por razones de rendimiento, los atributos de entidad objetivo no se propagan a la vista de entidad en cada cambio de atributo. Puede habilitar la propagación automática configurando el nodo de la regla \"copiar a la vista\" en su cadena de reglas y vincular los mensajes \"Atributos de la publicación\" y \"Atributos actualizados\" al nuevo nodo de la regla.", - "timeseries-data": "Datos de series temporales", - "timeseries-data-hint": "Configure las claves de los datos de las series temporales de la entidad de destino que serán accesibles para la vista de la entidad. Los datos de esta serie temporal son de solo lectura.", - "search": "Buscar vistas de entidad", - "selected-entity-views": "{ count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } seleccionadas", - "make-public-entity-view-title": "¿Está seguro de que desea que la vista de entidad '{{entityViewName}}' sea pública?", - "make-public-entity-view-text": "Tras la confirmación, la vista de la entidad y todos sus datos se harán públicos y accesibles para otros.", - "make-private-entity-view-title": "¿Está seguro de que desea que la vista de entidad '{{entityViewName}}' sea privada?", - "make-private-entity-view-text": "Tras la confirmación, la vista de la entidad y todos sus datos se harán privados y no serán accesibles para otros.", - "assign-entity-view-to-edge": "Asignar vista (s) de entidad a Edge", - "assign-entity-view-to-edge-text": "Seleccione las vistas de entidad para asignar al Edge", - "unassign-entity-view-from-edge-title": "¿Está seguro de que desea anular la asignación de la vista de entidad '{{entityViewName}}'?", - "unassign-entity-view-from-edge-text": "Después de la confirmación, la vista de entidad quedará sin asignar y el Edge no podrá acceder a ella", - "unassign-entity-views-from-edge-action-title": "Anular asignación {count, plural, =1 {1 vista de entidad} other {# vistas de entidad} } del Edge", - "unassign-entity-view-from-edge": "Anular asignación de vista de entidad", - "unassign-entity-views-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, =1 {1 vista de entidad} other {# vistas de entidad} }?", - "unassign-entity-views-from-edge-text": "Después de la confirmación, todas las vistas de entidad seleccionadas no serán asignadas y el Edge no podrá acceder a ellas" + "loading-notifications": "Cargando notificaciones...", + "management": "Gestión de notificaciones", + "mark-all-as-read": "Marcar todo como leído", + "mark-as-read": "Marcar como leído", + "message": "Mensaje", + "message-required": "El mensaje es obligatorio", + "message-max-length": "El mensaje debe tener menos o igual a {{ length }} caracteres", + "name": "Nombre", + "name-required": "El nombre es obligatorio", + "new-notification": "Nueva notificación", + "no-inbox-notification": "No se encontraron notificaciones", + "no-notification-request": "No hay solicitudes de notificación", + "no-notification-templates": "No se encontraron plantillas de notificación", + "no-notifications-yet": "Aún no hay notificaciones", + "no-recipients-notification": "No hay destinatarios para la notificación", + "no-recipients-matching": "No se encontraron destinatarios que coincidan con '{{entity}}'.", + "no-recipients-text": "No se encontraron destinatarios", + "no-rule": "No hay reglas configuradas", + "no-rules-notification": "No hay reglas para la notificación", + "no-severity-found": "No se encontró severidad", + "no-severity-matching": "'{{severity}}' no encontrado.", + "no-template-matching": "No se encontraron recursos que coincidan con '{{template}}'.", + "not-found-slack-recipient": "Destinatario de Slack no encontrado", + "notification": "Notificación", + "notification-center": "Centro de notificaciones", + "notification-tap-action": "Acción al tocar notificación", + "notification-tap-action-hint": "Si no está habilitado, se utilizará el tablero de alarmas predeterminado", + "notify": "notificar", + "notify-again": "Notificar de nuevo", + "notify-alarm-action": { + "acknowledged": "Alarma reconocida", + "assigned": "Alarma asignada", + "cleared": "Alarma aclarada", + "created": "Alarma creada", + "severity-changed": "Severidad de la alarma cambiada", + "unassigned": "Alarma desasignada" }, - "event": { - "event-type": "Tipo de evento", - "events-filter": "Filtro de Eventos", - "clean-events": "Borrar Eventos", - "type-error": "Error", - "type-lc-event": "Ciclo de vida del evento", - "type-stats": "Estadísticas", - "type-debug-rule-node": "Debug", - "type-debug-rule-chain": "Debug", - "no-events-prompt": "Ningún evento encontrado.", - "error": "Error", - "alarm": "Alarma", - "event-time": "Hora del evento", - "server": "Servidor", - "body": "Cuerpo", - "method": "Método", - "type": "Tipo", - "message": "Mensaje", - "message-id": "Id Mensaje", - "copy-message-id": "Copiar Id de mensaje", - "message-type": "Tipo Mensaje", - "data-type": "Tipo de Datos", - "relation-type": "Tipo de relación", - "metadata": "Metadatos", - "data": "Datos", - "event": "Evento", - "status": "Estado", - "success": "Éxito", - "failed": "Fallo", - "messages-processed": "Mensajes procesados", - "max-messages-processed": "Máximo de mensajes procesados", - "min-messages-processed": "Mínimo de mensajes procesados", - "errors-occurred": "Ocurrieron errores", - "max-errors-occurred": "Errores máximos ocurridos", - "min-errors-occurred": "Errores mínimos ocurridos", - "min-value": "El valor mínimo es 0.", - "all-events": "Todos", - "has-error": "Tiene error", - "entity-id": "Id de Entidad", - "copy-entity-id": "Copiar Id de entidad", - "entity-type": "Tipo de entidad", - "clear-filter": "Limpiar Filtro", - "clear-request-title": "Borrar todos los eventos", - "clear-request-text": "Estás seguro de borrar todos los eventos?", - "started": "Arrancado", - "updated": "Actualizado", - "stopped": "Parado" + "notify-on": "Notificar en", + "notify-on-comment-update": "Notificar en actualización de comentario", + "notify-on-required": "Se requiere 'Notificar en'", + "notify-on-unassign": "Notificar en desasignación", + "notify-only-user-comments": "Notificar solo comentarios de usuario", + "only-rule-chain-lifecycle-failures": "Solo fallos en el ciclo de vida de la cadena de reglas", + "only-rule-node-lifecycle-failures": "Solo fallos en el ciclo de vida del nodo de regla", + "platform-users": "Usuarios de la plataforma", + "rate-limits": "Límites de tasa", + "rate-limits-hint": "Si el campo está vacío, el disparador se aplicará a todos los límites de tasa", + "recipient": "Destinatario", + "recipient-group": "Grupo de destinatarios", + "recipient-type": { + "affected-tenant-administrators": "Administradores del inquilino afectados", + "affected-user": "Usuario afectado", + "all-users": "Todos los usuarios", + "customer-users": "Usuarios del cliente", + "system-administrators": "Administradores del sistema", + "tenant-administrators": "Administradores del inquilino", + "user-filters": "Filtro de usuarios", + "user-list": "Lista de usuarios", + "users-entity-owner": "Usuarios del propietario de la entidad" }, - "extension": { - "extensions": "Extensiones", - "selected-extensions": "{ count, plural, =1 {1 extensión} other {# extensiones} } seleccionadas", - "type": "Tipo", - "key": "Clave", - "value": "Valor", - "id": "ID", - "extension-id": "ID de extensión", - "extension-type": "Tipo de extensión", - "transformer-json": "JSON *", - "unique-id-required": "El id de extensión ya existe.", - "delete": "Borrar Extensión", - "add": "Añadir Extensión", - "edit": "Editar Extensión", - "delete-extension-title": "Eliminar la extensión '{{extensionId}}'?", - "delete-extension-text": "Atención, tras la confirmación la extensión y sus datos serán borrados e irrecuperables.", - "delete-extensions-title": "Eliminar las extensiones { count, plural, =1 {1 extensión} other {# extensiones} }?", - "delete-extensions-text": "Atención, tras la confirmación todas las extensiones seleccionadas y sus datos serán borrados e irrecuperables.", - "converters": "Convertidores", - "converter-id": "Id de convertidor", - "configuration": "Configuración", - "converter-configurations": "Ajustes de convertidor", - "token": "Tóken de seguridad", - "add-converter": "Añadir convertidor", - "add-config": "Añadir configuración de convertidor", - "device-name-expression": "Expresión del nombre de dispositivo", - "device-type-expression": "Expresión del tipo de dispositivo", - "custom": "Personalizado", - "to-double": "Para duplicar", - "transformer": "Transformador", - "json-required": "Se requiere el JSON del transformador.", - "json-parse": "No ha sido posible analizar el JSON del transformador.", - "attributes": "Atributos", - "add-attribute": "Añadir Atributo", - "add-map": "Agregar elemento de mapeado", - "timeseries": "Series de tiempo", - "add-timeseries": "Añadir series de tiempo", - "field-required": "Campo requerido", - "brokers": "Brokers", - "add-broker": "Añadir broker", - "host": "Host", - "port": "Puerto", - "port-range": "El puerto debe estar en un rango de 1 a 65535.", - "ssl": "SSL", - "credentials": "Credenciales", - "username": "Usuario", - "password": "Contraseña", - "retry-interval": "Intervalo de reintento en milisegundos", - "anonymous": "Anónimo", - "basic": "Básico", - "pem": "PEM", - "ca-cert": "Archivo de certificado CA *", - "private-key": "Archivo de clave privado *", - "cert": "Archivo de certificado *", - "no-file": "Ningún archivo seleccionado.", - "drop-file": "Colocar un archivo o hacer clic para seleccionar un archivo para cargar.", - "mapping": "Mapeo", - "topic-filter": "Filtro de tema", - "converter-type": "Tipo de conversor", - "converter-json": "Json", - "json-name-expression": "Expresión json para nombre del dispositivo", - "topic-name-expression": "Expresión temática para nombre del dispositivo", - "json-type-expression": "Expresión json para tipo de dispositivo", - "topic-type-expression": "Expresión temática para tipo de dispositivo", - "attribute-key-expression": "Expresión para clave de atributo", - "attr-json-key-expression": "Expresión json para clave de atributo", - "attr-topic-key-expression": "Expresión temática para clave de atributo", - "request-id-expression": "Expresión para solicitud de ID", - "request-id-json-expression": "Expresión json para solicitud de ID", - "request-id-topic-expression": "Expresión temática para solicitud de ID", - "response-topic-expression": "Expresión temática para respuesta", - "value-expression": "Expresión para valor", - "topic": "Tema", - "timeout": "Tiempo de espera en milisegundos", - "converter-json-required": "Conversor json es requerido.", - "converter-json-parse": "No se puede analizar el conversor json.", - "filter-expression": "Expresión para filtro", - "connect-requests": "Solicitudes de conexión", - "add-connect-request": "Agregar solicitudes de conexión", - "disconnect-requests": "Solicitudes de desconexión", - "add-disconnect-request": "Agregar solicitud de desconexión", - "attribute-requests": "Solicitudes de atributo", - "add-attribute-request": "Agregar solicitudes de atributo", - "attribute-updates": "Actualizaciones de atributo", - "add-attribute-update": "Agregar actualizaciones de atributo", - "server-side-rpc": "RPC lado servidor", - "add-server-side-rpc-request": "Agregar solicitud RPC lado servidor", - "device-name-filter": "Filtro de nombre de dispositivo", - "attribute-filter": "Filtro de atributo", - "method-filter": "Filtro de método", - "request-topic-expression": "Expresión temática para solicitud", - "response-timeout": "Tiempo de espera de respuesta en milisegundos", - "topic-expression": "Expresión temática", - "client-scope": "Alcance del cliente", - "add-device": "Agregar dispositivo", - "opc-server": "Servidores", - "opc-add-server": "Agregar servidor", - "opc-add-server-prompt": "Por favor agregar servidor", - "opc-application-name": "Nombre de aplicación", - "opc-application-uri": "Aplicación URI", - "opc-scan-period-in-seconds": "Período de exploración en segundos", - "opc-security": "Seguridad", - "opc-identity": "Identidad", - "opc-keystore": "Almacén de claves", - "opc-type": "Tipo", - "opc-keystore-type": "Tipo", - "opc-keystore-location": "Ubicación *", - "opc-keystore-password": "Contraseña", - "opc-keystore-alias": "Alias", - "opc-keystore-key-password": "Clave de contraseña", - "opc-device-node-pattern": "Patrón de nodo de dispositivo", - "opc-device-name-pattern": "Patrón de nombre de dispositivo", - "modbus-server": "Servidores/esclavos", - "modbus-add-server": "Agregar servidor/esclavo", - "modbus-add-server-prompt": "Por favor agregar servidor/esclavo", - "modbus-transport": "Transporte", - "modbus-tcp-reconnect": "Reconexión automática", - "modbus-rtu-over-tcp": "RTU sobre TCP", - "modbus-port-name": "Nombre del puerto serial", - "modbus-encoding": "Codificación", - "modbus-parity": "Paridad", - "modbus-baudrate": "Tasa de baudios", - "modbus-databits": "Bits de datos", - "modbus-stopbits": "Bits de parada", - "modbus-databits-range": "Bits de datos deben estar en un rango entre 7 y 8.", - "modbus-stopbits-range": "Bits de parada deben estar en un rango entre 1 a 2.", - "modbus-unit-id": "ID de unidad", - "modbus-unit-id-range": "ID de unidad debe estar en un rango entre 1 a 247.", - "modbus-device-name": "Nombre del dispositivo", - "modbus-poll-period": "Período de sondeo (ms)", - "modbus-attributes-poll-period": "Atributos del período de sondeo (ms)", - "modbus-timeseries-poll-period": "Período de sondeo de las series temporales (ms)", - "modbus-poll-period-range": "El período de sondeo debe ser una valor positivo.", - "modbus-tag": "Etiqueta", - "modbus-function": "Función", - "modbus-register-address": "Dirección del registro", - "modbus-register-address-range": "Dirección del registro debe estar en un rango entre 0 y 65535.", - "modbus-register-bit-index": "Índice de bit", - "modbus-register-bit-index-range": "Índice de bit debe estar en un rango entre 0 y 15.", - "modbus-register-count": "Contador del registro", - "modbus-register-count-range": "Contador del registro debe ser un valor positivo.", - "modbus-byte-order": "Orden del byte", - "sync": { - "status": "Estado", - "sync": "Sincronizado", - "not-sync": "No Sincronizado", - "last-sync-time": "Hora de última sincronización", - "not-available": "No disponible" - }, - "export-extensions-configuration": "Exportar configuración de extensiones", - "import-extensions-configuration": "Importar configuración de extensiones", - "import-extensions": "Importar extensiones", - "import-extension": "Importar extensión", - "export-extension": "Exportar extensión", - "file": "Fichero de extensiones", - "invalid-file-error": "Fichero de extensiones inválido" + "recipients": "Destinatarios", + "notification-recipient": "Destinatario de la notificación", + "notification-recipient-required": "El destinatario de la notificación es obligatorio.", + "notification-recipients": "Notificaciones / Destinatarios", + "recipients-count": "{ count, plural, =1 {1 destinatario} other {# destinatarios} }", + "recipients-required": "Se requieren destinatarios", + "refresh-allow-delivery-method": "Actualizar método de entrega permitido", + "request-search": "Buscar solicitud", + "request-status": { + "processing": "Procesando", + "scheduled": "Programado", + "sent": "Enviado" }, - "feature": { - "advanced-features": "Características avanzadas" + "review": "Revisar", + "rule": "Regla", + "rule-chain-list-rule-hint": "Si el campo está vacío, el disparador se aplicará a todas las cadenas de reglas", + "rule-engine-events-trigger-settings": "Configuración de disparadores de eventos del motor de reglas", + "rule-engine-filter": "Filtro del motor de reglas", + "rule-name": "Nombre de la regla", + "rule-name-required": "El nombre es obligatorio", + "rule-disable": "Desactivar regla de notificación", + "rule-enable": "Activar regla de notificación", + "rule-node-filter": "Filtro de nodo de regla", + "rules": "Reglas", + "notification-rules": "Notificaciones / Reglas", + "scheduler-later": "Programar para más tarde", + "search-notification": "Buscar notificaciones", + "search-recipients": "Buscar destinatarios", + "search-rules": "Buscar reglas", + "search-templates": "Buscar plantillas", + "see-documentation": "Ver documentación", + "selected-notifications": "{ count, plural, =1 {1 notificación} other {# notificaciones} } seleccionada(s)", + "selected-recipients": "{ count, plural, =1 {1 destinatario} other {# destinatarios} } seleccionado(s)", + "selected-requests": "{ count, plural, =1 {1 solicitud} other {# solicitudes} } seleccionada(s)", + "selected-rules": "{ count, plural, =1 {1 regla} other {# reglas} } seleccionada(s)", + "selected-template": "{ count, plural, =1 {1 plantilla} other {# plantillas} } seleccionada(s)", + "send-notification": "Enviar notificación", + "sent": "Enviado", + "setup": "Configuración", + "notification-sent": "Notificaciones / Enviadas", + "set-entity-from-notification": "Establecer entidad de la notificación en el estado del tablero", + "slack-chanel-type": "Tipo de canal de Slack", + "slack-chanel-types": { + "direct": "Mensaje directo", + "private-channel": "Canal privado", + "public-channel": "Canal público" }, - "filter": { - "add": "Añadir filtro", - "edit": "Editar filtro", - "name": "Nombre de filtro", - "name-required": "Se requiere nombre de filtro.", - "duplicate-filter": "Ya existe un filtro con el mismo nombre.", - "filters": "Filtros", - "unable-delete-filter-title": "Error borrando filtro", - "unable-delete-filter-text": "El filtro '{{filter}}' no puede ser borrado debido a que está siendo usado actualmente por los siguientes widgets:
{{widgetsList}}", - "duplicate-filter-error": "Se ha encontrado un filtro duplicado '{{filter}}'.
Los filtros deben ser únicos en el panel.", - "missing-key-filters-error": "No se encontró la clave de filtros para el filtro '{{filter}}'.", - "filter": "Filtro", - "editable": "Editable", - "no-filters-found": "No se encontraron filtros.", - "no-filter-text": "No se ha especificado filtro", - "add-filter-prompt": "Por favos, añadir filtro", - "no-filter-matching": "'{{filter}}' no encontrado.", - "create-new-filter": "Crear un filtro nuevo!", - "create-new": "Crear nuevo", - "filter-required": "Se requiere filtro.", - "operation": { - "operation": "Operación", - "equal": "igual", - "not-equal": "no igual", - "starts-with": "comienza con", - "ends-with": "acaba con", - "contains": "contiene", - "not-contains": "no contiene", - "greater": "mayor que", - "less": "menor que", - "greater-or-equal": "mayor o igual", - "less-or-equal": "menor o igual", - "and": "y", - "or": "o", - "in": "en", - "not-in": "no en" - }, - "ignore-case": "Ignorar mayús/minus", - "value": "Valor", - "remove-filter": "Borrar filtro", - "duplicate-filter-action": "Duplicar filtro", - "preview": "Vista previa de filtro", - "no-filters": "No hay filtros configurados", - "add-filter": "Añadir filtro", - "add-complex-filter": "Añadir filtro complejo", - "add-complex": "Agregar filtro complejo", - "complex-filter": "Filtro complejo", - "edit-complex-filter": "Editar filtro complejo", - "edit-filter-user-params": "Editar parámetros de usuario del filtro", - "filter-user-params": "Filtro de parámetros de usuario (predicado)", - "user-parameters": "Parámetros de usuario", - "display-label": "Etiqueta a mostrar", - "order-priority": "Prioridad orden de campos", - "key-filter": "Filtros (clave)", - "key-filters": "Filtros (claves)", - "key-name": "Nombre de clave", - "key-name-required": "Se requiere nombre de clave.", - "key-type": { - "key-type": "Tipo de clave", - "attribute": "Atributo", - "timeseries": "Timeseries", - "entity-field": "Campo de entidad", - "constant": "Constante" - }, - "value-type": { - "value-type": "Tipo de valor", - "string": "Cadena", - "numeric": "Numerico", - "boolean": "Booleano", - "date-time": "Fecha/Hora" - }, - "value-type-required": "Se requiere tipo de valor.", - "key-value-type-change-title": "Cambiar el tipo de valor de la clave?", - "key-value-type-change-message": "Si confirmas el nuevo tipo, todos los filtros se borrarán.", - "no-key-filters": "No hay filtros claves configurados", - "add-key-filter": "Añadir filtro clave", - "remove-key-filter": "Borrar filtro clave", - "edit-key-filter": "Editar filtro clave", - "date": "Fecha", - "time": "Hora", - "current-tenant": "Admin actual", - "current-customer": "Cliente actual", - "current-user": "Usuario actual", - "current-device": "Dispositivo actual", - "default-value": "Valor por defecto", - "dynamic-source-type": "Tipo de origen dinámico", - "dynamic-value": "Valor dinámico", - "no-dynamic-value": "Sin valor dinámico", - "source-attribute": "Atributo de origen", - "switch-to-dynamic-value": "Cambiar a valor dinámico", - "switch-to-default-value": "Cambiar a valor por defecto", - "inherit-owner": "Heredar de propietario", - "source-attribute-not-set": "Si los atributos de origen no están seleccionados" + "start-from-scratch": "Empezar desde cero", + "status": "Estado", + "stop-escalation-alarm-status-become": "Detener la escalada al cambiar el estado de la alarma a:", + "subject": "Asunto", + "subject-required": "El asunto es obligatorio", + "subject-max-length": "El asunto debe tener menos o igual a {{ length }} caracteres", + "template": "Plantilla", + "template-name": "Nombre de la plantilla", + "template-required": "La plantilla es obligatoria", + "template-type": { + "alarm": "Alarma", + "alarm-assignment": "Asignación de alarma", + "alarm-comment": "Comentario de alarma", + "api-usage-limit": "Límite de uso de API", + "device-activity": "Actividad del dispositivo", + "entities-limit": "Límite de entidades", + "entity-action": "Acción sobre entidad", + "general": "General", + "rule-engine-lifecycle-event": "Evento del ciclo de vida del motor de reglas", + "rule-node": "Nodo de regla", + "new-platform-version": "Nueva versión de la plataforma", + "rate-limits": "Límites de tasa excedidos", + "edge-communication-failure": "Fallo de comunicación con el edge", + "edge-connection": "Conexión con el edge", + "task-processing-failure": "Fallo de procesamiento de tarea" }, - "fullscreen": { - "expand": "Expandir a Pantalla Completa", - "exit": "Salir de Pantalla Completa", - "toggle": "Cambiar el modo de Pantalla Completa", - "fullscreen": "Pantalla Completa" + "templates": "Plantillas", + "notification-templates": "Notificaciones / Plantillas", + "tenant-profiles-list-rule-hint": "Si el campo está vacío, el disparador se aplicará a todos los perfiles de inquilino", + "tenants-list-rule-hint": "Si el campo está vacío, el disparador se aplicará a todos los inquilinos", + "threshold": "Umbral", + "theme-color": "Color del tema", + "time": "Hora", + "track-rule-node-events": "Rastrear eventos del nodo de regla", + "trigger": { + "alarm": "Alarma", + "alarm-assignment": "Asignación de alarma", + "alarm-comment": "Comentario de alarma", + "api-usage-limit": "Límite de uso de API", + "device-activity": "Actividad del dispositivo", + "entities-limit": "Límite de entidades", + "entity-action": "Acción sobre entidad", + "rule-engine-lifecycle-event": "Evento del ciclo de vida del motor de reglas", + "new-platform-version": "Nueva versión de la plataforma", + "rate-limits": "Límites de tasa excedidos", + "edge-connection": "Conexión con el edge", + "edge-communication-failure": "Fallo de comunicación con el edge", + "task-processing-failure": "Fallo de procesamiento de tarea", + "trigger": "Disparador", + "trigger-required": "El disparador es obligatorio" }, - "function": { - "function": "Función" + "type": "Tipo", + "unread": "No leído", + "updated": "Actualizado", + "use-deprecated-webhook-connectors": "Usar conectores Webhook obsoletos", + "use-old-api": "Usar API antigua", + "use-template": "Usar plantilla", + "view-all": "Ver todo", + "warning": "Advertencia", + "webhook-url": "URL del Webhook", + "webhook-url-required": "La URL del Webhook es obligatoria", + "workflow-url": "URL del flujo de trabajo", + "workflow-url-required": "La URL del flujo de trabajo es obligatoria", + "channel-name": "Nombre del canal", + "channel-name-required": "El nombre del canal es obligatorio", + "settings": { + "notification-settings": "Configuración de notificaciones", + "reset-all": "Restablecer todas las configuraciones", + "reset-all-title": "¿Estás seguro de que deseas restablecer el formulario?", + "reset-all-text": "Después de la confirmación, el formulario de configuración se restablecerá al valor predeterminado y se guardará.", + "type": "Tipo", + "enable-all": "Habilitar todo", + "disable-all": "Deshabilitar todo", + "delivery-not-configured": "El método de entrega no está configurado" + } + }, + "ota-update": { + "add": "Agregar paquete", + "assign-firmware": "Firmware asignado", + "assign-firmware-required": "El firmware asignado es obligatorio", + "assign-software": "Software asignado", + "assign-software-required": "El software asignado es obligatorio", + "auto-generate-checksum": "Generar checksum automáticamente", + "checksum": "Checksum", + "checksum-hint": "Si el checksum está vacío, se generará automáticamente", + "checksum-algorithm": "Algoritmo de checksum", + "checksum-copied-message": "El checksum del paquete se ha copiado al portapapeles", + "change-firmware": "El cambio de firmware puede provocar la actualización de { count, plural, =1 {1 dispositivo} other {# dispositivos} }.", + "change-software": "El cambio de software puede provocar la actualización de { count, plural, =1 {1 dispositivo} other {# dispositivos} }.", + "chose-compatible-device-profile": "El paquete cargado solo estará disponible para dispositivos con el perfil seleccionado.", + "chose-firmware-distributed-device": "Seleccione el firmware que se distribuirá a los dispositivos", + "chose-software-distributed-device": "Seleccione el software que se distribuirá a los dispositivos", + "content-type": "Tipo de contenido", + "copy-checksum": "Copiar checksum", + "copy-direct-url": "Copiar URL directa", + "copyId": "Copiar ID del paquete", + "copied": "¡Copiado!", + "delete": "Eliminar paquete", + "delete-ota-update-text": "Ten cuidado, después de la confirmación la actualización OTA será irrecuperable.", + "delete-ota-update-title": "¿Estás seguro de que deseas eliminar la actualización OTA '{{title}}'?", + "delete-ota-updates-text": "Ten cuidado, después de la confirmación todas las actualizaciones OTA seleccionadas serán eliminadas.", + "delete-ota-updates-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 actualización OTA} other {# actualizaciones OTA} }?", + "description": "Descripción", + "direct-url": "URL directa", + "direct-url-copied-message": "La URL directa del paquete se ha copiado al portapapeles", + "direct-url-required": "La URL directa es obligatoria", + "download": "Descargar paquete", + "drop-file": "Suelta un archivo de paquete o haz clic para seleccionar un archivo para subir.", + "drop-package-file-or": "Arrastra y suelta un archivo de paquete o", + "file-name": "Nombre del archivo", + "file-size": "Tamaño del archivo", + "file-size-bytes": "Tamaño del archivo en bytes", + "idCopiedMessage": "El ID del paquete se ha copiado al portapapeles", + "no-firmware-matching": "No se encontraron paquetes OTA de firmware compatibles que coincidan con '{{entity}}'.", + "no-firmware-text": "No se han aprovisionado paquetes OTA de firmware compatibles.", + "no-packages-text": "No se encontraron paquetes", + "no-software-matching": "No se encontraron paquetes OTA de software compatibles que coincidan con '{{entity}}'.", + "no-software-text": "No se han aprovisionado paquetes OTA de software compatibles.", + "ota-update": "Actualización OTA", + "ota-update-details": "Detalles de la actualización OTA", + "ota-updates": "Actualizaciones OTA", + "package-file": "Archivo del paquete", + "package-type": "Tipo de paquete", + "packages-repository": "Repositorio de paquetes", + "search": "Buscar paquetes", + "selected-package": "{ count, plural, =1 {1 paquete} other {# paquetes} } seleccionado", + "title": "Título", + "title-required": "El título es obligatorio.", + "title-max-length": "El título debe tener menos de 256 caracteres", + "types": { + "firmware": "Firmware", + "software": "Software" }, - "gateway": { - "gateway-exists": "Ya existe un dispositivo con el mismo nombre.", - "gateway-name": "Nombre de Gateway", - "gateway-name-required": "Se requiere un nombre de gateway.", - "gateway-saved": "Configuración de gateway grabada satisfactoriamente.", - "gateway": "Gateway", - "launch-gateway": "Iniciar gateway", - "launch-command": "Comando de lanzamiento", - "create-new-gateway": "Crear un gateway nuevo", - "create-new-gateway-text": "¿Está seguro de que desea crear un nuevo gateway con el nombre: '{{gatewayName}}'?", - "no-gateway-found": "No se ha encontrado ningún gateway.", - "no-gateway-matching": "No se encontró '{{item}}'." + "upload-binary-file": "Subir archivo binario", + "use-external-url": "Usar URL externa", + "version": "Versión", + "version-required": "La versión es obligatoria.", + "version-tag": "Etiqueta de versión", + "version-tag-hint": "La etiqueta personalizada debe coincidir con la versión del paquete informada por su dispositivo.", + "version-max-length": "La versión debe tener menos de 256 caracteres", + "warning-after-save-no-edit": "Una vez que el paquete esté subido, no podrás modificar el título, versión, perfil de dispositivo y tipo de paquete." + }, + "position": { + "top": "Superior", + "bottom": "Inferior", + "left": "Izquierda", + "right": "Derecha" + }, + "profile": { + "profile": "Perfil", + "last-login-time": "Último acceso", + "change-password": "Cambiar contraseña", + "current-password": "Contraseña actual", + "copy-jwt-token": "Copiar token JWT", + "jwt-token": "Token JWT", + "token-valid-till": "El token es válido hasta", + "tokenCopiedSuccessMessage": "El token JWT se ha copiado al portapapeles", + "tokenCopiedWarnMessage": "¡El token JWT ha expirado! Por favor, actualiza la página." + }, + "profiles": { + "profiles": "Perfiles" + }, + "security": { + "security": "Seguridad", + "general-settings": "Configuraciones generales de seguridad", + "access-token": "Token de acceso", + "access-token-required": "El token de acceso es obligatorio", + "clientId": "ID de Cliente", + "clientId-required": "El ID de Cliente es obligatorio", + "username": "Nombre de usuario", + "username-required": "El nombre de usuario es obligatorio", + "ca-cert": "Certificado CA", + "2fa": { + "2fa": "Autenticación de dos factores", + "2fa-description": "La autenticación de dos factores protege tu cuenta contra accesos no autorizados. Solo tienes que ingresar un código de seguridad al iniciar sesión.", + "authenticate-with": "Puedes autenticarte con:", + "disable-2fa-provider-text": "Desactivar {{name}} hará que tu cuenta sea menos segura", + "disable-2fa-provider-title": "¿Estás seguro de que deseas desactivar {{name}}?", + "get-new-code": "Obtener nuevo código", + "main-2fa-method": "Usar como método principal de autenticación de dos factores", + "dialog": { + "activation-step-description-email": "La próxima vez que inicies sesión, se te pedirá ingresar el código de seguridad que será enviado a tu dirección de correo electrónico.", + "activation-step-description-sms": "La próxima vez que inicies sesión, se te pedirá ingresar el código de seguridad que será enviado a tu número de teléfono.", + "activation-step-description-totp": "La próxima vez que inicies sesión, deberás proporcionar un código de autenticación de dos factores.", + "activation-step-label": "Activación", + "backup-code-description": "Imprime los códigos para tenerlos a mano cuando necesites usarlos para iniciar sesión en tu cuenta. Puedes usar cada código de respaldo una vez.", + "backup-code-warn": "Una vez que salgas de esta página, estos códigos no podrán mostrarse de nuevo. Guárdalos de forma segura usando las opciones siguientes.", + "download-txt": "Descargar (txt)", + "email-step-description": "Ingresa un correo electrónico para usar como autenticador.", + "email-step-label": "Correo electrónico", + "enable-email-title": "Habilitar autenticador por correo", + "enable-sms-title": "Habilitar autenticador por SMS", + "enable-totp-title": "Habilitar aplicación de autenticación", + "enter-verification-code": "Introduce aquí el código de 6 dígitos", + "get-backup-code-title": "Obtener código de respaldo", + "next": "Siguiente", + "scan-qr-code": "Escanea este código QR con tu aplicación de verificación", + "send-code": "Enviar código", + "sms-step-description": "Ingresa un número de teléfono para usar como autenticador.", + "sms-step-label": "Número de teléfono", + "success": "¡Éxito!", + "totp-step-description-install": "Puedes instalar aplicaciones como Google Authenticator, Authy o Duo.", + "totp-step-description-open": "Abre la aplicación de autenticación en tu teléfono móvil.", + "totp-step-label": "Obtener aplicación", + "verification-code": "Código de 6 dígitos", + "verification-code-invalid": "Formato de código de verificación inválido", + "verification-code-incorrect": "Código de verificación incorrecto", + "verification-code-many-request": "Demasiadas solicitudes, revisa el código de verificación", + "verification-step-description": "Introduce un código de 6 dígitos que acabamos de enviar a {{address}}", + "verification-step-label": "Verificación" + }, + "provider": { + "email": "Correo electrónico", + "email-description": "Usa un código de seguridad enviado a tu dirección de correo electrónico para autenticarte.", + "email-hint": "Los códigos de autenticación se envían por correo a {{ info }}", + "sms": "SMS", + "sms-description": "Usa tu teléfono para autenticarte. Te enviaremos un código de seguridad por mensaje SMS al iniciar sesión.", + "sms-hint": "Los códigos de autenticación se envían por mensaje de texto a {{ info }}", + "totp": "Aplicación de autenticación", + "totp-description": "Usa aplicaciones como Google Authenticator, Authy o Duo en tu teléfono para autenticarte. Generará un código de seguridad para iniciar sesión.", + "totp-hint": "La aplicación de autenticación está configurada para tu cuenta", + "backup_code": "Código de respaldo", + "backup-code-description": "Estos códigos de un solo uso e imprimibles te permiten iniciar sesión cuando no tienes acceso a tu teléfono, como cuando estás viajando.", + "backup-code-hint": "{{ info }} códigos de un solo uso están activos actualmente" + } }, - "grid": { - "delete-item-title": "¿Quieres eliminar este item?", - "delete-item-text": "Atención, tras la confirmación el item será eliminado y la información relacionada será irrecuperable.", - "delete-items-title": "¿Quieres eliminar { count, plural, =1 {1 item} other {# items} }?", - "delete-items-action-title": "Eliminar { count, plural, =1 {1 item} other {# items} }", - "delete-items-text": "Atención, tras la confirmación los items seleccionados serán eliminados y la información relacionada será irrecuperable.", - "add-item-text": "Agregar nuevo item", - "no-items-text": "Ningún item encontrado", - "item-details": "Detalles del item", - "delete-item": "Borrar Item", - "delete-items": "Borrar Items", - "scroll-to-top": "Ir hacia arriba" + "password-requirement": { + "at-least": "Al menos:", + "character": "{ count, plural, =1 {1 carácter} other {# caracteres} }", + "digit": "{ count, plural, =1 {1 dígito} other {# dígitos} }", + "incorrect-password-try-again": "Contraseña incorrecta. Inténtalo de nuevo", + "lowercase-letter": "{ count, plural, =1 {1 letra minúscula} other {# letras minúsculas} }", + "new-passwords-not-match": "La nueva contraseña no coincide", + "password-should-not-contain-spaces": "Tu contraseña no debe contener espacios", + "password-not-meet-requirements": "La contraseña no cumple con los requisitos", + "password-requirements": "Requisitos de la contraseña", + "password-should-difference": "La nueva contraseña debe ser diferente de la actual", + "special-character": "{ count, plural, =1 {1 carácter especial} other {# caracteres especiales} }", + "uppercase-letter": "{ count, plural, =1 {1 letra mayúscula} other {# letras mayúsculas} }", + "at-most": "Como máximo:" + } + }, + "relation": { + "relations": "Relaciones", + "direction": "Dirección", + "clear-relation-type": "Borrar tipo de relación", + "search-direction": { + "FROM": "Desde", + "TO": "Hacia" }, - "help": { - "goto-help-page": "Ir a la página de ayuda", - "show-help": "Mostrar ayuda" + "direction-type": { + "FROM": "desde", + "TO": "hacia" }, - "home": { - "home": "Principal", - "profile": "Perfil", - "logout": "Salir", - "menu": "Menu", - "avatar": "Avatar", - "open-user-menu": "Abrir menú de usuario" + "from-relations": "Relaciones salientes", + "to-relations": "Relaciones entrantes", + "selected-relations": "{ count, plural, =1 {1 relación} other {# relaciones} } seleccionadas", + "type": "Tipo", + "to-entity-type": "Tipo de entidad de destino", + "to-entity-name": "Nombre de entidad de destino", + "from-entity-type": "Tipo de entidad de origen", + "from-entity-name": "Nombre de entidad de origen", + "to-entity": "Entidad de destino", + "from-entity": "Entidad de origen", + "delete": "Eliminar relación", + "relation-type": "Tipo de relación", + "relation-type-required": "Se requiere tipo de relación.", + "relation-type-max-length": "El tipo de relación debe tener menos de 256 caracteres", + "any-relation-type": "Cualquier tipo", + "add": "Agregar relación", + "edit": "Editar relación", + "delete-to-relation-title": "¿Estás seguro de que deseas eliminar la relación con la entidad '{{entityName}}'?", + "delete-to-relation-text": "Ten cuidado, tras la confirmación la entidad '{{entityName}}' quedará desvinculada de la entidad actual.", + "delete-to-relations-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 relación} other {# relaciones} }?", + "delete-to-relations-text": "Ten cuidado, tras la confirmación todas las relaciones seleccionadas serán eliminadas y las entidades correspondientes quedarán desvinculadas de la entidad actual.", + "delete-from-relation-title": "¿Estás seguro de que deseas eliminar la relación desde la entidad '{{entityName}}'?", + "delete-from-relation-text": "Ten cuidado, tras la confirmación la entidad actual quedará desvinculada de la entidad '{{entityName}}'.", + "delete-from-relations-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 relación} other {# relaciones} }?", + "delete-from-relations-text": "Ten cuidado, tras la confirmación todas las relaciones seleccionadas serán eliminadas y la entidad actual quedará desvinculada de las entidades correspondientes.", + "remove-relation-filter": "Eliminar filtro de relación", + "remove-filter": "Eliminar filtro", + "add-relation-filter": "Agregar filtro de relación", + "any-relation": "Cualquier relación", + "relation-filters": "Filtros de relación", + "additional-info": "Información adicional (JSON)", + "invalid-additional-info": "No se pudo analizar el JSON de información adicional.", + "no-relations-text": "No se encontraron relaciones", + "not": "No" + }, + "resource": { + "add": "Agregar recurso", + "all-types": "Todos", + "copyId": "Copiar ID del recurso", + "delete": "Eliminar recurso", + "delete-resource-text": "Ten cuidado, tras la confirmación el recurso será irrecuperable.", + "delete-resource-title": "¿Estás seguro de que deseas eliminar el recurso '{{resourceTitle}}'?", + "delete-resources-action-title": "Eliminar { count, plural, =1 {1 recurso} other {# recursos} }", + "delete-resources-text": "Ten en cuenta que los recursos seleccionados, aunque estén siendo utilizados en perfiles de dispositivos, serán eliminados.", + "delete-resources-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 recurso} other {# recursos} }?", + "download": "Descargar recurso", + "drop-file": "Suelta un archivo de recurso o haz clic para seleccionar un archivo para subir.", + "drop-resource-file-or": "Arrastra y suelta un archivo de recurso o", + "empty": "El recurso está vacío", + "file-name": "Nombre del archivo", + "idCopiedMessage": "ID del recurso copiado al portapapeles", + "no-resource-matching": "No se encontraron recursos que coincidan con '{{widgetsBundle}}'.", + "no-resource-text": "No se encontraron recursos", + "open-widgets-bundle": "Abrir paquete de widgets", + "resource": "Recurso", + "resource-file": "Archivo de recurso", + "resource-files": "Archivos de recurso", + "resource-library-details": "Detalles del recurso", + "resource-type": "Tipo de recurso", + "resources-library": "Biblioteca de recursos", + "search": "Buscar recursos", + "selected-resources": "{ count, plural, =1 {1 recurso} other {# recursos} } seleccionados", + "system": "Sistema", + "title": "Título", + "title-required": "El título es obligatorio.", + "title-max-length": "El título debe tener menos de 256 caracteres", + "type": { + "jks": "JKS", + "js-module": "Módulo JS", + "lwm2m-model": "Modelo LWM2M", + "pkcs-12": "PKCS #12" }, - "file-input": { - "browse-file": "Navegar fichero", - "browse-files": "Navegar ficheros" + "resource-sub-type": "Subtipo", + "sub-type": { + "image": "imagen", + "scada-symbol": "Símbolo Scada", + "extension": "Extensión", + "module": "Módulo" + } + }, + "javascript": { + "add": "Agregar recurso JavaScript", + "delete": "Eliminar recurso JavaScript", + "delete-javascript-resource-text": "Ten cuidado, después de la confirmación el recurso JavaScript será irrecuperable.", + "delete-javascript-resource-title": "¿Estás seguro de que deseas eliminar el recurso JavaScript '{{resourceTitle}}'?", + "delete-javascript-resources-action-title": "Eliminar JavaScript { count, plural, =1 {1 recurso} other {# recursos} }", + "delete-javascript-resources-text": "Ten en cuenta que los recursos JavaScript seleccionados, incluso si se usan en funciones JavaScript, serán eliminados.", + "delete-javascript-resources-title": "¿Estás seguro de que deseas eliminar JavaScript { count, plural, =1 {1 recurso} other {# recursos} }?", + "delete-javascript-resource-in-use-text": "Si aún deseas eliminar el recurso JavaScript, haz clic en el botón Eliminar de todos modos.", + "download": "Descargar recurso JavaScript", + "upload-from-file": "Subir JavaScript desde archivo", + "resource-file": "Archivo de recurso JavaScript", + "drop-file": "Suelta un archivo JavaScript o haz clic para seleccionar uno para subir.", + "drop-resource-file-or": "Arrastra y suelta un archivo JavaScript o", + "javascript-library": "Biblioteca JavaScript", + "javascript-type": "Tipo de JavaScript", + "javascript-resource-details": "Detalles del recurso JavaScript", + "javascript-resource-is-in-use": "El recurso JavaScript está siendo usado por otras entidades", + "javascript-resources-are-in-use": "Los recursos JavaScript están siendo usados por otras entidades", + "javascript-resource-is-in-use-text": "El recurso JavaScript '{{title}}' no fue eliminado porque está siendo utilizado por las siguientes entidades:", + "javascript-resources-are-in-use-text": "No todos los recursos JavaScript han sido eliminados porque están siendo utilizados por otras entidades.
Puedes ver las entidades referenciadas haciendo clic en el botón Referencias en la fila correspondiente del recurso.
Si aún deseas eliminar estos recursos JavaScript, selecciónalos en la tabla a continuación y haz clic en el botón Eliminar seleccionados.", + "search": "Buscar recursos JavaScript", + "selected-javascript-resources": "{ count, plural, =1 {1 recurso JavaScript} other {# recursos JavaScript} } seleccionados", + "no-javascript-resource-text": "No se encontraron recursos JavaScript", + "all-types": "Todos", + "module-script": "Script de módulo" + }, + "rpc": { + "error": { + "target-device-is-not-set": "¡El dispositivo de destino no está definido!", + "invalid-target-entity": "Los comandos RPC no son compatibles con la entidad {{entityType}}.", + "failed-to-resolve-target-device": "¡No se pudo resolver el dispositivo de destino!", + "request-timeout": "Tiempo de espera de la solicitud agotado", + "rpc-http-error": "Error: {{status}} - {{statusText}}" + } + }, + "rulechain": { + "rulechain": "Cadena de reglas", + "rulechain-events": "Eventos de cadena de reglas", + "rulechains": "Cadenas de reglas", + "root": "Raíz", + "delete": "Eliminar cadena de reglas", + "name": "Nombre", + "name-required": "El nombre es obligatorio.", + "name-max-length": "El nombre debe tener menos de 256 caracteres", + "description": "Descripción", + "add": "Agregar cadena de reglas", + "set-root": "Hacer raíz esta cadena de reglas", + "set-root-rulechain-title": "¿Estás seguro de que deseas hacer la cadena de reglas '{{ruleChainName}}' raíz?", + "set-root-rulechain-text": "Después de la confirmación, la cadena de reglas se convertirá en la raíz y gestionará todos los mensajes de transporte entrantes.", + "delete-rulechain-title": "¿Estás seguro de que deseas eliminar la cadena de reglas '{{ruleChainName}}'?", + "delete-rulechain-text": "Ten cuidado, después de la confirmación la cadena de reglas y todos los datos relacionados serán irrecuperables.", + "delete-rulechains-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }?", + "delete-rulechains-action-title": "Eliminar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }", + "delete-rulechains-text": "Ten cuidado, después de la confirmación todas las cadenas de reglas seleccionadas serán eliminadas y todos los datos relacionados serán irrecuperables.", + "add-rulechain-text": "Agregar nueva cadena de reglas", + "no-rulechains-text": "No se encontraron cadenas de reglas", + "rulechain-details": "Detalles de la cadena de reglas", + "details": "Detalles", + "events": "Eventos", + "system": "Sistema", + "import": "Importar cadena de reglas", + "export": "Exportar cadena de reglas", + "export-failed-error": "No se pudo exportar la cadena de reglas: {{error}}", + "create-new-rulechain": "Crear nueva cadena de reglas", + "rulechain-file": "Archivo de cadena de reglas", + "invalid-rulechain-file-error": "No se pudo importar la cadena de reglas: estructura de datos inválida.", + "copyId": "Copiar ID de cadena de reglas", + "idCopiedMessage": "El ID de la cadena de reglas ha sido copiado al portapapeles", + "select-rulechain": "Seleccionar cadena de reglas", + "no-rulechains-matching": "No se encontraron cadenas de reglas que coincidan con '{{entity}}'.", + "rulechain-required": "La cadena de reglas es obligatoria", + "management": "Gestión de reglas", + "debug-mode": "Modo depuración", + "search": "Buscar cadenas de reglas", + "selected-rulechains": "{ count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} } seleccionadas", + "open-rulechain": "Abrir cadena de reglas", + "edge-template-root": "Raíz de plantilla", + "assign-to-edge": "Asignar a borde", + "edge-rulechain": "Cadena de reglas de borde", + "unassign-rulechain-from-edge-text": "Después de la confirmación, la cadena de reglas será desasignada y no estará accesible desde el borde.", + "unassign-rulechains-from-edge-title": "¿Estás seguro de que deseas desasignar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }?", + "unassign-rulechains-from-edge-text": "Después de la confirmación, todas las cadenas de reglas seleccionadas serán desasignadas y no estarán accesibles desde el borde.", + "assign-rulechain-to-edge-title": "Asignar cadena(s) de reglas al borde", + "assign-rulechain-to-edge-text": "Por favor, selecciona las cadenas de reglas para asignar al borde", + "set-edge-template-root-rulechain": "Establecer como raíz de plantilla de borde", + "set-edge-template-root-rulechain-title": "¿Estás seguro de que deseas establecer la cadena de reglas '{{ruleChainName}}' como raíz de plantilla de borde?", + "set-edge-template-root-rulechain-text": "Después de la confirmación, la cadena de reglas se convertirá en raíz de plantilla de borde y será la cadena raíz para los bordes recién creados.", + "invalid-rulechain-type-error": "No se pudo importar la cadena de reglas: tipo de cadena inválido. El tipo esperado es {{expectedRuleChainType}}.", + "set-auto-assign-to-edge": "Asignar cadena de reglas automáticamente al crear borde(s)", + "set-auto-assign-to-edge-title": "¿Estás seguro de que deseas asignar la cadena de reglas de borde '{{ruleChainName}}' automáticamente al crear borde(s)?", + "set-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de borde se asignará automáticamente al crear borde(s).", + "unset-auto-assign-to-edge": "No asignar cadena de reglas a borde(s) al crearlos", + "unset-auto-assign-to-edge-title": "¿Estás seguro de que no deseas asignar la cadena de reglas de borde '{{ruleChainName}}' al crear borde(s)?", + "unset-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de borde ya no se asignará automáticamente al crear borde(s).", + "unassign-rulechain-title": "¿Estás seguro de que deseas desasignar la cadena de reglas '{{ruleChainName}}'?", + "unassign-rulechains": "Desasignar cadenas de reglas" + }, + "rulenode": { + "rule-node-events": "Eventos del nodo de reglas", + "details": "Detalles", + "events": "Eventos", + "search": "Buscar nodos", + "open-node-library": "Abrir biblioteca de nodos", + "close-node-library": "Cerrar biblioteca de nodos", + "add": "Agregar nodo de reglas", + "name": "Nombre", + "name-required": "El nombre es obligatorio.", + "name-max-length": "El nombre debe tener menos de 256 caracteres", + "type": "Tipo", + "rule-node-description": "Descripción del nodo de reglas", + "delete": "Eliminar nodo de reglas", + "select-all-objects": "Seleccionar todos los nodos y conexiones", + "deselect-all-objects": "Deseleccionar todos los nodos y conexiones", + "delete-selected-objects": "Eliminar nodos y conexiones seleccionados", + "delete-selected": "Eliminar seleccionados", + "create-nested-rulechain": "Crear cadena de reglas anidada", + "select-all": "Seleccionar todo", + "copy-selected": "Copiar seleccionados", + "deselect-all": "Deseleccionar todo", + "rulenode-details": "Detalles del nodo de reglas", + "debug-mode": "Modo depuración", + "singleton": "Instancia única", + "configuration": "Configuración", + "link": "Enlace", + "link-details": "Detalles del enlace del nodo de reglas", + "add-link": "Agregar enlace", + "link-label": "Etiqueta del enlace", + "link-label-required": "La etiqueta del enlace es obligatoria.", + "custom-link-label": "Etiqueta personalizada del enlace", + "custom-link-label-required": "La etiqueta personalizada del enlace es obligatoria.", + "link-labels": "Etiquetas del enlace", + "link-labels-required": "Las etiquetas del enlace son obligatorias.", + "no-link-labels-found": "No se encontraron etiquetas del enlace", + "no-link-label-matching": "'{{label}}' no encontrado.", + "create-new-link-label": "¡Crear una nueva!", + "type-filter": "Filtro", + "type-filter-details": "Filtra mensajes entrantes con condiciones configuradas", + "type-enrichment": "Enriquecimiento", + "type-enrichment-details": "Agrega información adicional en los metadatos del mensaje", + "type-transformation": "Transformación", + "type-transformation-details": "Cambia el contenido del mensaje y metadatos", + "type-action": "Acción", + "type-action-details": "Realiza una acción especial", + "type-external": "Externo", + "type-external-details": "Interactúa con un sistema externo", + "type-rule-chain": "Cadena de reglas", + "type-rule-chain-details": "Reenvía mensajes entrantes a la cadena de reglas especificada", + "type-flow": "Flujo", + "type-flow-details": "Organiza el flujo de mensajes", + "type-input": "Entrada", + "type-input-details": "Entrada lógica de la cadena de reglas, reenvía mensajes entrantes al siguiente nodo relacionado", + "type-unknown": "Desconocido", + "type-unknown-details": "Nodo de reglas no resuelto", + "directive-is-not-loaded": "La directiva de configuración definida '{{directiveName}}' no está disponible.", + "ui-resources-load-error": "Error al cargar los recursos de interfaz de configuración.", + "invalid-target-rulechain": "¡No se pudo resolver la cadena de reglas de destino!", + "test-script-function": "Probar función de script", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", + "message": "Mensaje", + "message-type": "Tipo de mensaje", + "select-message-type": "Seleccionar tipo de mensaje", + "message-type-required": "El tipo de mensaje es obligatorio", + "metadata": "Metadatos", + "metadata-required": "Las entradas de metadatos no pueden estar vacías.", + "output": "Salida", + "test": "Probar", + "help": "Ayuda", + "reset-debug-settings": "Restablecer configuración de depuración en todos los nodos", + "test-with-this-message": "{{test}} con este mensaje", + "queue-hint": "Selecciona una cola para reenviar el mensaje a otra cola. Por defecto se usa la cola 'Main'.", + "queue-singleton-hint": "Selecciona una cola para reenvío de mensajes en entornos multi-instancia. Por defecto se usa la cola 'Main'." + }, + "rule-node-config": { + "id": "Id", + "additional-info": "Información adicional", + "advanced-settings": "Configuraciones avanzadas", + "create-entity-if-not-exists": "Crear nueva entidad si no existe", + "create-entity-if-not-exists-hint": "Si está habilitado, se creará una nueva entidad con los parámetros especificados a menos que ya exista. Las entidades existentes se usarán tal cual para la relación.", + "select-device-connectivity-event": "Seleccionar evento de conectividad del dispositivo", + "entity-name-pattern": "Patrón de nombre", + "device-name-pattern": "Nombre del dispositivo", + "asset-name-pattern": "Nombre del activo", + "entity-view-name-pattern": "Nombre de vista de entidad", + "customer-title-pattern": "Título del cliente", + "dashboard-name-pattern": "Título del tablero", + "user-name-pattern": "Correo del usuario", + "edge-name-pattern": "Nombre del Edge", + "entity-name-pattern-required": "El patrón de nombre es obligatorio", + "entity-name-pattern-hint": "El campo patrón de nombre admite plantillas. Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraerlo de los metadatos.", + "copy-message-type": "Copiar tipo de mensaje", + "entity-type-pattern": "Patrón de tipo", + "entity-type-pattern-required": "El patrón de tipo es obligatorio", + "message-type-value": "Valor del tipo de mensaje", + "message-type-value-required": "El valor del tipo de mensaje es obligatorio", + "message-type-value-max-length": "El valor del tipo de mensaje debe tener menos de 256 caracteres", + "output-message-type": "Tipo de mensaje de salida", + "entity-cache-expiration": "Tiempo de expiración de la caché de entidades (seg)", + "entity-cache-expiration-hint": "Especifica el intervalo de tiempo máximo permitido para almacenar registros de entidades encontrados. El valor 0 significa que los registros nunca expirarán.", + "entity-cache-expiration-required": "El tiempo de expiración de la caché de entidades es obligatorio.", + "entity-cache-expiration-range": "El tiempo de expiración debe ser mayor o igual a 0.", + "customer-name-pattern": "Título del cliente", + "customer-name-pattern-required": "El título del cliente es obligatorio", + "customer-name-pattern-hint": "Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraerlo de los metadatos.", + "create-customer-if-not-exists": "Crear nuevo cliente si no existe", + "unassign-from-customer": "Desasignar de cliente específico si el originador es un tablero", + "unassign-from-customer-tooltip": "Solo los tableros pueden asignarse a múltiples clientes al mismo tiempo.\nSi el originador del mensaje es un tablero, debes especificar explícitamente el título del cliente del que se desea desasignar.", + "customer-cache-expiration": "Tiempo de expiración de la caché de clientes (seg)", + "customer-cache-expiration-hint": "Especifica el intervalo de tiempo máximo permitido para almacenar registros de clientes encontrados. El valor 0 significa que los registros nunca expirarán.", + "customer-cache-expiration-required": "El tiempo de expiración de la caché de clientes es obligatorio.", + "customer-cache-expiration-range": "El tiempo de expiración debe ser mayor o igual a 0.", + "interval-start": "Inicio del intervalo", + "interval-end": "Fin del intervalo", + "time-unit": "Unidad de tiempo", + "fetch-mode": "Modo de obtención", + "order-by-timestamp": "Ordenar por marca de tiempo", + "limit": "Límite", + "limit-hint": "El valor mínimo es 2, máximo - 1000. Si deseas obtener una sola entrada, selecciona el modo 'Primero' o 'Último'.", + "limit-required": "El límite es obligatorio.", + "limit-range": "El límite debe estar en el rango de 2 a 1000.", + "time-unit-milliseconds": "Milisegundos", + "time-unit-seconds": "Segundos", + "time-unit-minutes": "Minutos", + "time-unit-hours": "Horas", + "time-unit-days": "Días", + "time-value-range": "Rango permitido de 1 a 2147483647.", + "start-interval-value-required": "El inicio del intervalo es obligatorio.", + "end-interval-value-required": "El fin del intervalo es obligatorio.", + "filter": "Filtro", + "switch": "Conmutador", + "math-templatization-tooltip": "Este campo admite plantillas. Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraerlo de los metadatos.", + "add-message-type": "Agregar tipo de mensaje", + "select-message-types-required": "Debe seleccionarse al menos un tipo de mensaje.", + "select-message-types": "Seleccionar tipos de mensaje", + "no-message-types-found": "No se encontraron tipos de mensaje", + "no-message-type-matching": "'{{messageType}}' no encontrado.", + "create-new-message-type": "Crear uno nuevo.", + "message-types-required": "Los tipos de mensaje son obligatorios.", + "client-attributes": "Atributos del cliente", + "shared-attributes": "Atributos compartidos", + "server-attributes": "Atributos del servidor", + "attributes-keys": "Claves de atributos", + "attributes-keys-required": "Las claves de atributos son obligatorias", + "attributes-scope": "Ámbito de los atributos", + "attributes-scope-value": "Valor del ámbito de los atributos", + "attributes-scope-value-copy": "Copiar valor del ámbito de los atributos", + "attributes-scope-hint": "Usa la clave de metadatos 'scope' para definir dinámicamente el ámbito por mensaje. Si se proporciona, sobrescribe el ámbito definido en la configuración.", + "notify-device": "Forzar notificación al dispositivo", + "send-attributes-updated-notification": "Enviar notificación de atributos actualizados", + "send-attributes-updated-notification-hint": "Enviar notificación sobre los atributos actualizados como mensaje separado a la cola del motor de reglas.", + "send-attributes-deleted-notification": "Enviar notificación de atributos eliminados", + "send-attributes-deleted-notification-hint": "Enviar notificación sobre los atributos eliminados como mensaje separado a la cola del motor de reglas.", + "update-attributes-only-on-value-change": "Guardar atributos solo si el valor cambia", + "update-attributes-only-on-value-change-hint": "Actualiza los atributos en cada mensaje entrante sin importar si su valor ha cambiado. Aumenta el uso de la API y reduce el rendimiento.", + "update-attributes-only-on-value-change-hint-enabled": "Actualiza los atributos solo si su valor ha cambiado. Si el valor no cambia, no se actualiza la marca de tiempo ni se envía notificación de cambio.", + "fetch-credentials-to-metadata": "Obtener credenciales en metadatos", + "notify-device-on-update-hint": "Si está habilitado, forzar notificación al dispositivo sobre la actualización de atributos compartidos. Si está deshabilitado, el comportamiento se controla con el parámetro 'notifyDevice' desde los metadatos del mensaje. Para desactivar la notificación, los metadatos deben contener 'notifyDevice' con valor 'false'. En cualquier otro caso, se enviará la notificación al dispositivo.", + "notify-device-on-delete-hint": "Si está habilitado, forzar notificación al dispositivo sobre la eliminación de atributos compartidos. Si está deshabilitado, el comportamiento se controla con el parámetro 'notifyDevice' desde los metadatos del mensaje. Para activar la notificación, los metadatos deben contener 'notifyDevice' con valor 'true'. En cualquier otro caso, no se enviará notificación al dispositivo.", + "latest-timeseries": "Claves de datos de series temporales más recientes", + "timeseries-keys": "Claves de series temporales", + "timeseries-keys-required": "Debe seleccionarse al menos una clave de series temporales.", + "add-timeseries-key": "Agregar clave de series temporales", + "add-message-field": "Agregar campo de mensaje", + "relation-search-parameters": "Parámetros de búsqueda de relaciones", + "relation-parameters": "Parámetros de relación", + "add-metadata-field": "Agregar campo de metadatos", + "data-keys": "Nombres de campos del mensaje", + "copy-from": "Copiar desde", + "data-to-metadata": "Datos a metadatos", + "metadata-to-data": "Metadatos a datos", + "use-regular-expression-hint": "Usa expresión regular para copiar claves por patrón.\n\nConsejos y trucos:\nPresiona 'Enter' para completar el nombre del campo.\nPresiona 'Retroceso' para eliminar el nombre del campo. Se admiten múltiples nombres de campos.", + "interval": "Intervalo", + "interval-required": "El intervalo es obligatorio", + "interval-hint": "Intervalo de desduplicación en segundos.", + "interval-min-error": "El valor mínimo permitido es 1", + "max-pending-msgs": "Máximo de mensajes pendientes", + "max-pending-msgs-hint": "Número máximo de mensajes que se almacenan en memoria para cada id único de desduplicación.", + "max-pending-msgs-required": "El máximo de mensajes pendientes es obligatorio", + "max-pending-msgs-max-error": "El valor máximo permitido es 1000", + "max-pending-msgs-min-error": "El valor mínimo permitido es 1", + "max-retries": "Máximo de reintentos", + "max-retries-required": "El número máximo de reintentos es obligatorio", + "max-retries-hint": "Número máximo de reintentos para insertar los mensajes desduplicados en la cola. Se usa un retraso de 10 segundos entre reintentos.", + "max-retries-max-error": "El valor máximo permitido es 100", + "max-retries-min-error": "El valor mínimo permitido es 0", + "strategy": "Estrategia", + "strategy-required": "La estrategia es obligatoria", + "strategy-all-hint": "Devuelve todos los mensajes que llegaron durante el periodo de desduplicación como un único mensaje JSON en forma de array. Cada elemento representa un objeto con propiedades internas msg y metadata.", + "strategy-first-hint": "Devuelve el primer mensaje que llegó durante el periodo de desduplicación.", + "strategy-last-hint": "Devuelve el último mensaje que llegó durante el periodo de desduplicación.", + "first": "Primero", + "last": "Último", + "all": "Todos", + "output-msg-type-hint": "El tipo de mensaje del resultado de la desduplicación.", + "queue-name-hint": "El nombre de la cola donde se publicará el resultado de la desduplicación.", + "keys": "Claves", + "keys-required": "Las claves son obligatorias", + "rename-keys-in": "Renombrar claves en", + "data": "Datos", + "message": "Mensaje", + "metadata": "Metadatos", + "current-key-name": "Nombre actual de la clave", + "key-name-required": "El nombre de la clave es obligatorio", + "new-key-name": "Nuevo nombre de la clave", + "new-key-name-required": "El nuevo nombre de la clave es obligatorio", + "metadata-keys": "Nombres de campos de metadatos", + "json-path-expression": "Expresión de ruta JSON", + "json-path-expression-required": "La expresión de ruta JSON es obligatoria", + "json-path-expression-hint": "JSONPath especifica una ruta a un elemento o conjunto de elementos en una estructura JSON. '$' representa el objeto raíz o el array.", + "relations-query": "Consulta de relaciones", + "device-relations-query": "Consulta de relaciones del dispositivo", + "max-relation-level": "Nivel máximo de relación", + "max-relation-level-error": "El valor debe ser mayor que 0 o no estar especificado.", + "max-relation-level-invalid": "El valor debe ser un número entero.", + "relation-type": "Tipo de relación", + "relation-type-pattern": "Patrón de tipo de relación", + "relation-type-pattern-required": "El patrón de tipo de relación es obligatorio", + "relation-types-list": "Tipos de relación para propagar", + "relation-types-list-hint": "Si no se seleccionan tipos de relación para propagar, las alarmas se propagarán sin filtrar por tipo de relación.", + "unlimited-level": "Nivel ilimitado", + "latest-telemetry": "Última telemetría", + "add-telemetry-key": "Agregar clave de telemetría", + "delete-from": "Eliminar de", + "use-regular-expression-delete-hint": "Usa expresión regular para eliminar claves por patrón.\n\nConsejos y trucos:\nPresiona 'Enter' para completar el nombre del campo.\nPresiona 'Retroceso' para eliminar el nombre del campo.\nSe admiten múltiples nombres de campos.", + "fetch-into": "Obtener en", + "attr-mapping": "Mapeo de atributos:", + "source-attribute": "Clave del atributo de origen", + "source-attribute-required": "La clave del atributo de origen es obligatoria.", + "source-telemetry": "Clave de telemetría de origen", + "source-telemetry-required": "La clave de telemetría de origen es obligatoria.", + "target-key": "Clave de destino", + "target-key-required": "La clave de destino es obligatoria.", + "attr-mapping-required": "Debe especificarse al menos una entrada de mapeo.", + "fields-mapping": "Mapeo de campos", + "fields-mapping-hint": "Si el campo del mensaje se establece en $entityId, se guardará el ID del originador del mensaje en la columna correspondiente de la tabla.", + "relations-query-config-direction-suffix": "originador", + "profile-name": "Nombre del perfil", + "fetch-circle-parameter-info-from-metadata-hint": "El campo de metadatos '{{perimeterKeyName}}' debe definirse en el siguiente formato: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "El campo de metadatos '{{perimeterKeyName}}' debe definirse en el siguiente formato: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "Usa $[messageKey] para extraer valores del mensaje y ${metadataKey} para extraer valores de los metadatos.", + "fields-mapping-required": "Se debe especificar al menos un mapeo de campo.", + "at-least-one-field-required": "Al menos un campo de entrada debe tener un valor proporcionado.", + "originator-fields-sv-map-hint": "Los campos de clave de destino admiten plantillas. Usa $[messageKey] para extraer valores del mensaje y ${metadataKey} para extraer valores de los metadatos.", + "sv-map-hint": "Solo los campos de clave de destino admiten plantillas. Usa $[messageKey] para extraer valores del mensaje y ${metadataKey} para extraer valores de los metadatos.", + "source-field": "Campo fuente", + "source-field-required": "El campo fuente es obligatorio.", + "originator-source": "Fuente del originador", + "new-originator": "Nuevo originador", + "originator-customer": "Cliente", + "originator-tenant": "Tenant", + "originator-related": "Entidad relacionada", + "originator-alarm-originator": "Originador de la alarma", + "originator-entity": "Entidad por patrón de nombre", + "clone-message": "Clonar mensaje", + "transform": "Transformar", + "default-ttl": "TTL por defecto", + "default-ttl-required": "El TTL por defecto es obligatorio.", + "default-ttl-hint": "El nodo de regla obtendrá el valor de TTL del mensaje metadata. Si no hay ningún valor, usará el TTL especificado en la configuración. Si se establece en 0, se usará el TTL del perfil del tenant.", + "default-ttl-zero-hint": "No se aplicará TTL si el valor es 0.", + "min-default-ttl-message": "Solo se permite un TTL mínimo de 0.", + "generation-parameters": "Parámetros de generación", + "message-count": "Límite de mensajes generados (0 - ilimitado)", + "message-count-required": "El límite de mensajes generados es obligatorio.", + "min-message-count-message": "Solo se permite un mínimo de 0 mensajes.", + "period-seconds": "Periodo en segundos", + "period-seconds-required": "El periodo es obligatorio.", + "generation-frequency-seconds": "Frecuencia de generación en segundos", + "generation-frequency-required": "La frecuencia de generación es obligatoria.", + "min-generation-frequency-message": "Se permite un mínimo de 60 segundos.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Usar patrón de periodo en segundos", + "use-metadata-period-in-seconds-patterns-hint": "Si se selecciona, el nodo de regla usará el patrón de intervalo de segundos del mensaje metadata o data suponiendo que los intervalos están en segundos.", + "period-in-seconds-pattern": "Patrón de periodo en segundos", + "period-in-seconds-pattern-required": "El patrón de periodo en segundos es obligatorio", + "min-period-seconds-message": "Solo se permite un mínimo de 60 segundos.", + "originator": "Originador", + "message-body": "Cuerpo del mensaje", + "message-metadata": "Metadatos del mensaje", + "generate": "Generar", + "current-rule-node": "Nodo de regla actual", + "current-tenant": "Tenant actual", + "generator-function": "Función generadora", + "test-generator-function": "Probar función generadora", + "generator": "Generador", + "test-filter-function": "Probar función de filtro", + "test-switch-function": "Probar función de switch", + "test-transformer-function": "Probar función transformadora", + "transformer": "Transformador", + "alarm-create-condition": "Condición de creación de alarma", + "test-condition-function": "Probar función de condición", + "alarm-clear-condition": "Condición de limpieza de alarma", + "alarm-details-builder": "Constructor de detalles de alarma", + "test-details-function": "Probar función de detalles", + "alarm-type": "Tipo de alarma", + "select-entity-types": "Seleccionar tipos de entidad", + "alarm-type-required": "El tipo de alarma es obligatorio.", + "alarm-severity": "Severidad de la alarma", + "alarm-severity-required": "La severidad de la alarma es obligatoria", + "alarm-severity-pattern": "Patrón de severidad de la alarma", + "alarm-status-filter": "Filtro de estado de la alarma", + "alarm-status-list-empty": "La lista de estados de alarma está vacía", + "no-alarm-status-matching": "No se encontró ningún estado de alarma coincidente.", + "propagate": "Propagar alarma a entidades relacionadas", + "propagate-to-owner": "Propagar alarma al propietario de la entidad (Cliente o Tenant)", + "propagate-to-tenant": "Propagar alarma al Tenant", + "condition": "Condición", + "details": "Detalles", + "to-string": "A cadena", + "test-to-string-function": "Probar función a cadena", + "from-template": "Desde", + "from-template-required": "El campo 'Desde' es obligatorio", + "message-to-metadata": "Mensaje a metadatos", + "metadata-to-message": "Metadatos a mensaje", + "from-message": "Desde el mensaje", + "from-metadata": "Desde metadatos", + "to-template": "Hacia", + "to-template-required": "El campo 'Hacia' es obligatorio", + "mail-address-list-template-hint": "Lista de direcciones separadas por comas, usa ${metadataKey} para valores desde metadatos, $[messageKey] para valores desde el cuerpo del mensaje", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "Asunto", + "subject-template-required": "El campo de Asunto es obligatorio", + "body-template": "Cuerpo", + "body-template-required": "El campo de Cuerpo es obligatorio", + "dynamic-mail-body-type": "Tipo de cuerpo dinámico", + "mail-body-type": "Tipo de cuerpo de correo", + "body-type-template": "Plantilla de tipo de cuerpo", + "reply-routing-configuration": "Configuración de enrutamiento de respuesta", + "rpc-reply-routing-configuration-hint": "Estos parámetros de configuración especifican las claves de metadatos utilizadas para identificar el servicio, la sesión y la solicitud para enviar una respuesta.", + "reply-routing-configuration-hint": "Estos parámetros de configuración especifican las claves de metadatos utilizadas para identificar el servicio y la solicitud para enviar una respuesta.", + "request-id-metadata-attribute": "Id de solicitud", + "service-id-metadata-attribute": "Id de servicio", + "session-id-metadata-attribute": "Id de sesión", + "timeout-sec": "Tiempo de espera en segundos", + "timeout-required": "El tiempo de espera es obligatorio", + "min-timeout-message": "Solo se permite un valor mínimo de tiempo de espera de 0.", + "endpoint-url-pattern": "Patrón de URL del endpoint", + "endpoint-url-pattern-required": "El patrón de URL del endpoint es obligatorio", + "request-method": "Método de solicitud", + "use-simple-client-http-factory": "Usar cliente HTTP simple", + "ignore-request-body": "Sin cuerpo de solicitud", + "parse-to-plain-text": "Parsear a texto plano", + "parse-to-plain-text-hint": "Si está seleccionado, el cuerpo del mensaje de solicitud será transformado de cadena JSON a texto plano, por ejemplo: msg = \"Hello,\\t\\\"world\\\"\" será convertido a Hello, \"world\"", + "read-timeout": "Tiempo de espera de lectura en milisegundos", + "read-timeout-hint": "El valor de 0 significa espera infinita", + "max-parallel-requests-count": "Número máximo de solicitudes paralelas", + "max-parallel-requests-count-hint": "El valor 0 especifica que no hay límite en el procesamiento paralelo", + "max-response-size": "Tamaño máximo de respuesta (en KB)", + "max-response-size-hint": "Cantidad máxima de memoria asignada para almacenar datos al decodificar o codificar mensajes HTTP como cargas JSON o XML", + "headers": "Encabezados", + "headers-hint": "Usa ${metadataKey} para valores desde metadatos, $[messageKey] para valores desde el cuerpo del mensaje en campos de encabezado/valor", + "header": "Encabezado", + "header-required": "El encabezado es obligatorio", + "value": "Valor", + "value-required": "El valor es obligatorio", + "topic-pattern": "Patrón de tópico", + "key-pattern": "Patrón de clave", + "key-pattern-hint": "Opcional. Si se especifica un número de partición válido, se usará al enviar el registro. Si no se especifica partición, se usará la clave. Si no se especifica ninguno, se asignará una partición en orden circular.", + "topic-pattern-required": "El patrón de tópico es obligatorio", + "topic": "Tópico", + "topic-required": "El tópico es obligatorio", + "bootstrap-servers": "Servidores bootstrap", + "bootstrap-servers-required": "El valor de servidores bootstrap es obligatorio", + "other-properties": "Otras propiedades", + "key": "Clave", + "key-required": "La clave es obligatoria", + "retries": "Reintentos automáticos en caso de fallo", + "min-retries-message": "Solo se permite un mínimo de 0 reintentos.", + "batch-size-bytes": "Tamaño del lote en bytes", + "min-batch-size-bytes-message": "Solo se permite un tamaño mínimo de lote de 0.", + "linger-ms": "Tiempo de retención local (ms)", + "min-linger-ms-message": "Solo se permite un valor mínimo de 0 ms.", + "buffer-memory-bytes": "Tamaño máximo del buffer del cliente en bytes", + "min-buffer-memory-message": "Solo se permite un tamaño mínimo de buffer de 0.", + "memory-buffer-size-range": "El tamaño del buffer de memoria debe estar entre 0 y {{max}} KB", + "acks": "Número de confirmaciones", + "topic-arn-pattern": "Patrón del ARN del tópico", + "topic-arn-pattern-required": "El patrón del ARN del tópico es obligatorio", + "aws-access-key-id": "ID de clave de acceso AWS", + "aws-access-key-id-required": "El ID de clave de acceso AWS es obligatorio", + "aws-secret-access-key": "Clave secreta de acceso AWS", + "aws-secret-access-key-required": "La clave secreta de acceso AWS es obligatoria", + "aws-region": "Región AWS", + "aws-region-required": "La región AWS es obligatoria", + "exchange-name-pattern": "Patrón de nombre de exchange", + "routing-key-pattern": "Patrón de clave de enrutamiento", + "message-properties": "Propiedades del mensaje", + "host": "Host", + "host-required": "El host es obligatorio", + "port": "Puerto", + "port-required": "El puerto es obligatorio", + "port-range": "El puerto debe estar en el rango de 1 a 65535.", + "virtual-host": "Host virtual", + "username": "Nombre de usuario", + "password": "Contraseña", + "automatic-recovery": "Recuperación automática", + "connection-timeout-ms": "Tiempo de espera de conexión (ms)", + "min-connection-timeout-ms-message": "Solo se permite un valor mínimo de 0 ms.", + "handshake-timeout-ms": "Tiempo de espera de apretón de manos (ms)", + "min-handshake-timeout-ms-message": "Solo se permite un valor mínimo de 0 ms.", + "client-properties": "Propiedades del cliente", + "queue-url-pattern": "Patrón de URL de la cola", + "queue-url-pattern-required": "El patrón de URL de la cola es obligatorio", + "delay-seconds": "Retardo (segundos)", + "min-delay-seconds-message": "Solo se permite un valor mínimo de 0 segundos.", + "max-delay-seconds-message": "Solo se permite un valor máximo de 900 segundos.", + "name": "Nombre", + "name-required": "El nombre es obligatorio", + "queue-type": "Tipo de cola", + "sqs-queue-standard": "Estándar", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "ID del proyecto de GCP", + "gcp-project-id-required": "El ID del proyecto de GCP es obligatorio", + "gcp-service-account-key": "Archivo de clave de cuenta de servicio de GCP", + "gcp-service-account-key-required": "El archivo de clave de cuenta de servicio de GCP es obligatorio", + "pubsub-topic-name": "Nombre del tópico", + "pubsub-topic-name-required": "El nombre del tópico es obligatorio", + "message-attributes": "Atributos del mensaje", + "message-attributes-hint": "Usa ${metadataKey} para valores desde metadatos, $[messageKey] para valores desde el cuerpo del mensaje en campos nombre/valor", + "connect-timeout": "Tiempo de espera de conexión (seg)", + "connect-timeout-required": "El tiempo de espera de conexión es obligatorio.", + "connect-timeout-range": "El tiempo de espera de conexión debe estar en el rango de 1 a 200.", + "client-id": "ID del cliente", + "client-id-hint": "Opcional. Déjelo vacío para un ID de cliente autogenerado. Cuidado al especificar el ID del cliente. La mayoría de los brokers MQTT no permiten múltiples conexiones con el mismo ID. Para evitar errores en entornos de microservicios, habilite la opción \"Agregar ID de servicio como sufijo al ID de cliente\".", + "append-client-id-suffix": "Agregar ID de servicio como sufijo al ID del cliente", + "client-id-suffix-hint": "Opcional. Se aplica cuando se especifica un \"ID de cliente\" explícitamente. Si está seleccionado, el ID del servicio se agregará como sufijo al ID del cliente.", + "device-id": "ID del dispositivo", + "device-id-required": "El ID del dispositivo es obligatorio.", + "clean-session": "Sesión limpia", + "enable-ssl": "Habilitar SSL", + "credentials": "Credenciales", + "credentials-type": "Tipo de credenciales", + "credentials-type-required": "El tipo de credenciales es obligatorio.", + "credentials-anonymous": "Anónimo", + "credentials-basic": "Básico", + "credentials-pem": "PEM", + "credentials-pem-hint": "Se requiere al menos el archivo del certificado CA del servidor o un par de archivos de certificado de cliente y clave privada del cliente", + "credentials-sas": "Firma de acceso compartido (SAS)", + "sas-key": "Clave SAS", + "sas-key-required": "La clave SAS es obligatoria.", + "hostname": "Nombre del host", + "hostname-required": "El nombre del host es obligatorio.", + "azure-ca-cert": "Archivo de certificado CA", + "username-required": "El nombre de usuario es obligatorio.", + "password-required": "La contraseña es obligatoria.", + "ca-cert": "Archivo del certificado CA del servidor", + "private-key": "Archivo de clave privada del cliente", + "cert": "Archivo de certificado del cliente", + "no-file": "No se seleccionó ningún archivo.", + "drop-file": "Suelte un archivo o haga clic para seleccionar uno para cargar.", + "private-key-password": "Contraseña de la clave privada", + "use-system-smtp-settings": "Usar configuración SMTP del sistema", + "use-metadata-dynamic-interval": "Usar intervalo dinámico", + "metadata-dynamic-interval-hint": "Los campos de entrada de intervalo de inicio y fin soportan plantillas. El valor sustituido debe estar en milisegundos. Usa $[messageKey] o ${metadataKey}.", + "use-metadata-interval-patterns-hint": "Si está seleccionado, el nodo de reglas usará los patrones de intervalo desde metadatos o datos, asumiendo que están en milisegundos.", + "use-message-alarm-data": "Usar datos de alarma del mensaje", + "overwrite-alarm-details": "Sobrescribir detalles de alarma", + "use-alarm-severity-pattern": "Usar patrón de severidad de alarma", + "check-all-keys": "Verificar que todos los campos especificados estén presentes", + "check-all-keys-hint": "Si está seleccionado, se verifica que todas las claves especificadas estén presentes en los datos del mensaje y los metadatos.", + "check-relation-to-specific-entity": "Verificar relación con una entidad específica", + "check-relation-to-specific-entity-tooltip": "Si está habilitado, verifica la relación con una entidad específica, de lo contrario, verifica con cualquier entidad.", + "check-relation-hint": "Verifica la existencia de relación con una entidad específica o cualquier entidad según la dirección y tipo de relación.", + "delete-relation-with-specific-entity": "Eliminar relación con una entidad específica", + "delete-relation-with-specific-entity-hint": "Si está habilitado, eliminará la relación con solo una entidad específica. Si no, se eliminará con todas las entidades coincidentes.", + "delete-relation-hint": "Elimina relaciones del originador del mensaje entrante con una entidad o lista de entidades según dirección y tipo.", + "remove-current-relations": "Eliminar relaciones actuales", + "remove-current-relations-hint": "Elimina relaciones actuales del originador del mensaje entrante basadas en dirección y tipo.", + "change-originator-to-related-entity": "Cambiar originador a entidad relacionada", + "change-originator-to-related-entity-hint": "Se utiliza para procesar el mensaje como si viniera de otra entidad.", + "start-interval": "Inicio del intervalo", + "end-interval": "Fin del intervalo", + "start-interval-required": "El inicio del intervalo es obligatorio.", + "end-interval-required": "El fin del intervalo es obligatorio.", + "smtp-protocol": "Protocolo", + "smtp-host": "Host SMTP", + "smtp-host-required": "El host SMTP es obligatorio.", + "smtp-port": "Puerto SMTP", + "smtp-port-required": "Debe proporcionar un puerto SMTP.", + "smtp-port-range": "El puerto SMTP debe estar en un rango de 1 a 65535.", + "timeout-msec": "Tiempo de espera ms", + "min-timeout-msec-message": "Solo se permite un mínimo de 0 ms.", + "enter-username": "Ingrese nombre de usuario", + "enter-password": "Ingrese contraseña", + "enable-tls": "Habilitar TLS", + "tls-version": "Versión de TLS", + "enable-proxy": "Habilitar proxy", + "use-system-proxy-properties": "Usar propiedades de proxy del sistema", + "proxy-host": "Host del proxy", + "proxy-host-required": "El host del proxy es obligatorio.", + "proxy-port": "Puerto del proxy", + "proxy-port-required": "El puerto del proxy es obligatorio.", + "proxy-port-range": "El puerto del proxy debe estar en el rango de 1 a 65535.", + "proxy-user": "Usuario del proxy", + "proxy-password": "Contraseña del proxy", + "proxy-scheme": "Esquema del proxy", + "numbers-to-template": "Plantilla de números de teléfono de destino", + "numbers-to-template-required": "La plantilla de números de teléfono de destino es obligatoria", + "numbers-to-template-hint": "Números de teléfono separados por comas, usa ${metadataKey} para valores desde metadatos, $[messageKey] para valores desde el cuerpo del mensaje", + "sms-message-template": "Plantilla de mensaje SMS", + "sms-message-template-required": "La plantilla de mensaje SMS es obligatoria", + "use-system-sms-settings": "Usar configuración del proveedor SMS del sistema", + "min-period-0-seconds-message": "Solo se permite un período mínimo de 0 segundos.", + "max-pending-messages": "Máximo de mensajes pendientes", + "max-pending-messages-required": "El número máximo de mensajes pendientes es obligatorio.", + "max-pending-messages-range": "El número máximo de mensajes pendientes debe estar en el rango de 1 a 100000.", + "originator-types-filter": "Filtro por tipos de originador", + "interval-seconds": "Intervalo en segundos", + "interval-seconds-required": "El intervalo es obligatorio.", + "int-range": "El valor no debe exceder el límite máximo de enteros (2147483648)", + "min-interval-seconds-message": "Solo se permite un intervalo mínimo de 1 segundo.", + "output-timeseries-key-prefix": "Prefijo de clave de serie temporal de salida", + "output-timeseries-key-prefix-required": "Se requiere el prefijo de clave de serie temporal de salida.", + "separator-hint": "Debes presionar \"Enter\" para completar el ingreso del campo.", + "select-details": "Seleccionar detalles", + "entity-details-id": "Id", + "entity-details-title": "Título", + "entity-details-country": "País", + "entity-details-state": "Estado", + "entity-details-city": "Ciudad", + "entity-details-zip": "Código postal", + "entity-details-address": "Dirección", + "entity-details-address2": "Dirección 2", + "entity-details-additional_info": "Información adicional", + "entity-details-phone": "Teléfono", + "entity-details-email": "Correo electrónico", + "email-sender": "Remitente del correo", + "fields-to-check": "Campos a verificar", + "add-detail": "Agregar detalle", + "check-all-keys-tooltip": "Si está habilitado, verifica la presencia de todos los campos listados en los nombres de campo del mensaje y de metadatos dentro del mensaje entrante y sus metadatos.", + "fields-to-check-hint": "Presiona \"Enter\" para completar la entrada del nombre del campo. Se admiten múltiples nombres de campo.", + "entity-details-list-empty": "Debe seleccionarse al menos un detalle.", + "alarm-status": "Estado de alarma", + "alarm-required": "Debe seleccionarse al menos un estado de alarma.", + "no-entity-details-matching": "No se encontraron detalles de entidad coincidentes.", + "custom-table-name": "Nombre de tabla personalizado", + "custom-table-name-required": "El nombre de la tabla es obligatorio", + "custom-table-hint": "La tabla debe estar creada en su clúster de Cassandra y su nombre debe comenzar con el prefijo 'cs_tb_' para evitar la inserción de datos en las tablas comunes de TB. Introduzca aquí el nombre de la tabla sin el prefijo 'cs_tb_'.", + "message-field": "Campo del mensaje", + "message-field-required": "El campo del mensaje es obligatorio.", + "table-col": "Columna de tabla", + "table-col-required": "La columna de tabla es obligatoria.", + "latitude-field-name": "Nombre del campo de latitud", + "longitude-field-name": "Nombre del campo de longitud", + "latitude-field-name-required": "El nombre del campo de latitud es obligatorio.", + "longitude-field-name-required": "El nombre del campo de longitud es obligatorio.", + "fetch-perimeter-info-from-metadata": "Obtener información del perímetro desde metadatos", + "fetch-perimeter-info-from-metadata-tooltip": "Si el tipo de perímetro está configurado como 'Polígono', el valor del campo de metadatos '{{perimeterKeyName}}' se establecerá como definición de perímetro sin análisis adicional. Si el tipo de perímetro está configurado como 'Círculo', el valor del campo de metadatos '{{perimeterKeyName}}' se analizará para extraer los campos 'latitude', 'longitude', 'radius', 'radiusUnit' usados en la definición de perímetro circular.", + "perimeter-key-name": "Nombre de clave de perímetro", + "perimeter-key-name-hint": "Nombre del campo de metadatos que contiene la información del perímetro.", + "perimeter-key-name-required": "El nombre de la clave de perímetro es obligatorio.", + "perimeter-circle": "Círculo", + "perimeter-polygon": "Polígono", + "perimeter-type": "Tipo de perímetro", + "circle-center-latitude": "Latitud del centro", + "circle-center-latitude-required": "La latitud del centro es obligatoria.", + "circle-center-longitude": "Longitud del centro", + "circle-center-longitude-required": "La longitud del centro es obligatoria.", + "range-unit-meter": "Metro", + "range-unit-kilometer": "Kilómetro", + "range-unit-foot": "Pie", + "range-unit-mile": "Milla", + "range-unit-nautical-mile": "Milla náutica", + "range-units": "Unidades de distancia", + "range-units-required": "Las unidades de distancia son obligatorias.", + "range": "Distancia", + "range-required": "La distancia es obligatoria.", + "polygon-definition": "Definición de polígono", + "polygon-definition-required": "La definición de polígono es obligatoria.", + "polygon-definition-hint": "Use el siguiente formato para la definición manual de un polígono: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration": "Duración mínima dentro", + "min-inside-duration-value-required": "La duración mínima dentro es obligatoria", + "min-inside-duration-time-unit": "Unidad de tiempo de duración mínima dentro", + "min-outside-duration": "Duración mínima fuera", + "min-outside-duration-value-required": "La duración mínima fuera es obligatoria", + "min-outside-duration-time-unit": "Unidad de tiempo de duración mínima fuera", + "tell-failure-if-absent": "Informar fallo", + "tell-failure-if-absent-hint": "Si al menos una clave seleccionada no existe, el mensaje saliente indicará \"Fallo\".", + "get-latest-value-with-ts": "Obtener marca de tiempo para los valores más recientes de telemetría", + "get-latest-value-with-ts-hint": "Si está seleccionado, los valores más recientes de telemetría también incluirán una marca de tiempo, p. ej.: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings": "Ignorar cadenas vacías", + "ignore-null-strings-hint": "Si está seleccionado, el nodo de reglas ignorará los campos de entidad con valores vacíos.", + "add-metadata-key-values-as-kafka-headers": "Agregar pares clave-valor de metadatos como encabezados de registros Kafka", + "add-metadata-key-values-as-kafka-headers-hint": "Si está seleccionado, los pares clave-valor de los metadatos del mensaje se agregarán como encabezados en los registros salientes como matrices de bytes con codificación de caracteres predefinida.", + "charset-encoding": "Codificación de caracteres", + "charset-encoding-required": "La codificación de caracteres es obligatoria.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "El nombre de la cola puede seleccionarse de una lista desplegable o introducirse de forma personalizada.", + "device-profile-node-hint": "Útil si tiene condiciones de duración o repetición para garantizar la continuidad en la evaluación del estado de la alarma.", + "persist-alarm-rules": "Persistir estado de reglas de alarma", + "persist-alarm-rules-hint": "Si está habilitado, el nodo de reglas almacenará el estado del procesamiento en la base de datos.", + "fetch-alarm-rules": "Recuperar estado de reglas de alarma", + "fetch-alarm-rules-hint": "Si está habilitado, el nodo de reglas restaurará el estado del procesamiento durante la inicialización y garantizará que las alarmas se activen incluso después de reinicios del servidor. De lo contrario, el estado se restaurará cuando llegue el primer mensaje del dispositivo.", + "input-value-key": "Clave de valor de entrada", + "input-value-key-required": "La clave de valor de entrada es obligatoria.", + "output-value-key": "Clave de valor de salida", + "output-value-key-required": "La clave de valor de salida es obligatoria.", + "number-of-digits-after-floating-point": "Número de dígitos después del punto decimal", + "number-of-digits-after-floating-point-range": "El número de dígitos después del punto decimal debe estar entre 0 y 15.", + "failure-if-delta-negative": "Informar fallo si la diferencia es negativa", + "failure-if-delta-negative-tooltip": "El nodo de reglas fuerza el fallo del procesamiento del mensaje si el valor de la diferencia es negativo.", + "use-caching": "Usar caché", + "use-caching-tooltip": "El nodo de reglas almacenará en caché el valor de \"{{inputValueKey}}\" que llega desde el mensaje entrante para mejorar el rendimiento. Nota: la caché no se actualizará si se modifica el valor de \"{{inputValueKey}}\" en otro lugar.", + "add-time-difference-between-readings": "Agregar la diferencia de tiempo entre lecturas de \"{{inputValueKey}}\"", + "add-time-difference-between-readings-tooltip": "Si está habilitado, el nodo de reglas añadirá \"{{periodValueKey}}\" al mensaje de salida.", + "period-value-key": "Clave del valor del período", + "period-value-key-required": "La clave del valor del período es obligatoria.", + "general-pattern-hint": "Usa ${metadataKey} para valores de metadatos, $[messageKey] para valores del cuerpo del mensaje.", + "alarm-severity-pattern-hint": "Usa ${metadataKey} para metadatos y $[messageKey] para cuerpo del mensaje. La severidad debe ser del sistema (CRITICAL, MAJOR, etc.)", + "output-node-name-hint": "El nombre del nodo de reglas corresponde al tipo de relación del mensaje de salida, y se usa para reenviar mensajes a otros nodos de reglas en la misma cadena.", + "use-server-ts": "Usar marca de tiempo del servidor", + "use-server-ts-hint": "Usar la marca de tiempo actual del servidor para datos de series temporales que no la incluyen. Ayuda a mantener el orden cuando los mensajes llegan fuera de secuencia o desde múltiples fuentes.", + "kv-map-pattern-hint": "Todos los campos de entrada admiten plantillas. Usa $[messageKey] para extraer valores del mensaje y ${metadataKey} de los metadatos.", + "kv-map-single-pattern-hint": "El campo de entrada admite plantillas. Usa $[messageKey] para valores del mensaje y ${metadataKey} para valores de metadatos.", + "shared-scope": "Ámbito compartido", + "server-scope": "Ámbito del servidor", + "client-scope": "Ámbito del cliente", + "attribute-type": "Atributo", + "attribute-type-description": "Obtener valor del atributo desde la base de datos", + "attribute-type-result-description": "Guardar resultado como atributo de la entidad en la base de datos", + "constant-type": "Constante", + "constant-type-description": "Definir valor constante", + "time-series-type": "Serie temporal", + "time-series-type-description": "Obtener último valor de serie temporal desde la base de datos", + "time-series-type-result-description": "Guardar resultado como serie temporal de la entidad en la base de datos", + "message-body-type": "Mensaje", + "message-body-type-description": "Obtener valor del argumento desde el mensaje entrante", + "message-body-type-result-description": "Agregar resultado al mensaje de salida", + "message-metadata-type": "Metadatos", + "message-metadata-type-description": "Obtener valor del argumento desde los metadatos del mensaje", + "message-metadata-result-description": "Agregar resultado a los metadatos del mensaje de salida", + "argument-tile": "Argumentos", + "no-arguments-prompt": "No hay argumentos configurados", + "result-title": "Resultado", + "functions-field-input": "Funciones", + "no-option-found": "No se encontraron opciones", + "argument-source-field-input": "Fuente", + "argument-source-field-input-required": "La fuente del argumento es obligatoria.", + "argument-key-field-input": "Clave", + "argument-key-field-input-required": "La clave del argumento es obligatoria.", + "constant-value-field-input": "Valor constante", + "constant-value-field-input-required": "El valor constante es obligatorio.", + "attribute-scope-field-input": "Ámbito del atributo", + "attribute-scope-field-input-required": "El ámbito del atributo es obligatorio.", + "default-value-field-input": "Valor por defecto", + "type-field-input": "Tipo", + "type-field-input-required": "El tipo es obligatorio.", + "key-field-input": "Clave", + "key-field-input-required": "La clave es obligatoria.", + "add-entity-type": "Agregar tipo de entidad", + "add-device-profile": "Agregar perfil de dispositivo", + "number-floating-point-field-input": "Dígitos después del punto decimal", + "number-floating-point-field-input-hint": "Usa 0 para convertir el resultado en entero", + "add-to-message-field-input": "Agregar al mensaje", + "add-to-metadata-field-input": "Agregar a metadatos", + "custom-expression-field-input": "Expresión matemática", + "custom-expression-field-input-required": "La expresión matemática es obligatoria", + "custom-expression-field-input-hint": "Especifica una expresión matemática para evaluar. La expresión por defecto demuestra cómo convertir Fahrenheit a Celsius", + "retained-message": "Retenido", + "attributes-mapping": "Mapeo de atributos", + "latest-telemetry-mapping": "Mapeo de última telemetría", + "add-mapped-attribute-to": "Agregar atributos mapeados a", + "add-mapped-latest-telemetry-to": "Agregar última telemetría mapeada a", + "add-mapped-fields-to": "Agregar campos mapeados a", + "add-selected-details-to": "Agregar detalles seleccionados a", + "clear-selected-types": "Borrar tipos seleccionados", + "clear-selected-details": "Borrar detalles seleccionados", + "clear-selected-fields": "Borrar campos seleccionados", + "clear-selected-keys": "Borrar claves seleccionadas", + "geofence-configuration": "Configuración de geocerca", + "coordinate-field-names": "Nombres de campos de coordenadas", + "coordinate-field-hint": "El nodo de reglas intenta obtener los campos especificados desde el mensaje. Si no están presentes, los buscará en los metadatos.", + "presence-monitoring-strategy": "Estrategia de monitoreo de presencia", + "presence-monitoring-strategy-on-first-message": "En el primer mensaje", + "presence-monitoring-strategy-on-each-message": "En cada mensaje", + "presence-monitoring-strategy-on-first-message-hint": "Informa el estado de presencia 'Dentro' o 'Fuera' en el primer mensaje después de que haya pasado la duración mínima configurada desde la última actualización de estado de presencia 'Entró' o 'Salió'.", + "presence-monitoring-strategy-on-each-message-hint": "Informa el estado de presencia 'Dentro' o 'Fuera' en cada mensaje después de una actualización de estado de presencia 'Entró' o 'Salió'.", + "fetch-credentials-to": "Obtener credenciales en", + "add-originator-attributes-to": "Agregar atributos del originador a", + "originator-attributes": "Atributos del originador", + "fetch-latest-telemetry-with-timestamp": "Obtener la última telemetría con marca de tiempo", + "fetch-latest-telemetry-with-timestamp-tooltip": "Si está seleccionado, los últimos valores de telemetría se agregarán a los metadatos de salida con marca de tiempo, por ejemplo: \"{{latestTsKeyName}}\": \"{\\\"ts\\\":1574329385897, \\\"value\\\":42}\"", + "tell-failure": "Informar fallo si falta algún atributo", + "tell-failure-tooltip": "Si falta al menos una clave seleccionada, el mensaje de salida informará 'Fallo'.", + "created-time": "Hora de creación", + "chip-help": "Presiona 'Enter' para completar la entrada de {{inputName}}. \nPresiona 'Backspace' para borrar {{inputName}}. \nSe admiten múltiples valores.", + "detail": "detalle", + "field-name": "nombre del campo", + "device-profile": "perfil de dispositivo", + "entity-type": "tipo de entidad", + "message-type": "tipo de mensaje", + "timeseries-key": "clave de serie temporal", + "type": "Tipo", + "first-name": "Nombre", + "last-name": "Apellido", + "label": "Etiqueta", + "originator-fields-mapping": "Mapeo de campos del originador", + "add-mapped-originator-fields-to": "Agregar campos mapeados del originador a", + "fields": "Campos", + "skip-empty-fields": "Omitir campos vacíos", + "skip-empty-fields-tooltip": "Los campos con valores vacíos no se agregarán al mensaje/metadatos de salida.", + "fetch-interval": "Intervalo de obtención", + "fetch-strategy": "Estrategia de obtención", + "fetch-timeseries-from-to": "Obtener serie temporal desde hace {{startInterval}} {{startIntervalTimeUnit}} hasta hace {{endInterval}} {{endIntervalTimeUnit}}.", + "fetch-timeseries-from-to-invalid": "Obtención de serie temporal no válida (\"Inicio del intervalo\" debe ser menor que \"Fin del intervalo\").", + "use-metadata-dynamic-interval-tooltip": "Si está seleccionado, el nodo de reglas usará intervalos dinámicos de inicio y fin basados en patrones de mensaje y metadatos.", + "all-mode-hint": "Si se selecciona el modo de obtención 'Todo', el nodo de reglas recuperará telemetría desde el intervalo de obtención con parámetros configurables.", + "first-mode-hint": "Si se selecciona el modo de obtención 'Primero', el nodo recuperará la telemetría más cercana al inicio del intervalo.", + "last-mode-hint": "Si se selecciona el modo de obtención 'Último', el nodo recuperará la telemetría más cercana al final del intervalo.", + "ascending": "Ascendente", + "descending": "Descendente", + "min": "Mínimo", + "max": "Máximo", + "average": "Promedio", + "sum": "Suma", + "count": "Conteo", + "none": "Ninguno", + "last-level-relation-tooltip": "Si se selecciona, el nodo buscará entidades relacionadas solo en el nivel definido en el máximo nivel de relación.", + "last-level-device-relation-tooltip": "Si se selecciona, el nodo buscará dispositivos relacionados solo en el nivel definido en el máximo nivel de relación.", + "data-to-fetch": "Datos a obtener", + "mapping-of-customers": "Mapeo de clientes", + "map-fields-required": "Todos los campos de mapeo son obligatorios.", + "attributes": "Atributos", + "related-device-attributes": "Atributos de dispositivos relacionados", + "add-selected-attributes-to": "Agregar atributos seleccionados a", + "device-profiles": "Perfiles de dispositivo", + "mapping-of-tenant": "Mapeo de inquilino", + "add-attribute-key": "Agregar clave de atributo", + "message-template": "Plantilla de mensaje", + "message-template-required": "La plantilla de mensaje es obligatoria", + "use-system-slack-settings": "Usar configuración de Slack del sistema", + "slack-api-token": "Token de API de Slack", + "slack-api-token-required": "El token de API de Slack es obligatorio", + "keys-mapping": "Mapeo de claves", + "add-key": "Agregar clave", + "recipients": "Destinatarios", + "message-subject-and-content": "Asunto y contenido del mensaje", + "template-rules-hint": "Ambos campos de entrada admiten tematización. Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraer el valor de los metadatos.", + "originator-customer-desc": "Usar el cliente del originador del mensaje entrante como nuevo originador.", + "originator-tenant-desc": "Usar el inquilino actual como nuevo originador.", + "originator-related-entity-desc": "Usar entidad relacionada como nuevo originador. La búsqueda se basa en el tipo y dirección de relación configurados.", + "originator-alarm-originator-desc": "Usar el originador de la alarma como nuevo originador. Solo si el originador del mensaje entrante es una entidad de alarma.", + "originator-entity-by-name-pattern-desc": "Usar entidad obtenida desde la base de datos como nuevo originador. La búsqueda se basa en el tipo de entidad y el patrón de nombre especificado.", + "email-from-template-hint": "Usa $[messageKey] para extraer el valor del mensaje y ${metadataKey} para extraer el valor de los metadatos.", + "recipients-block-main-hint": "Lista de direcciones separadas por comas. Todos los campos de entrada admiten tematización. Usa $[messageKey] para extraer valor del mensaje y ${metadataKey} para extraer valor de metadatos.", + "forward-msg-default-rule-chain": "Redirigir mensaje a la cadena de reglas predeterminada del originador", + "forward-msg-default-rule-chain-tooltip": "Si está habilitado, el mensaje se redirigirá a la cadena de reglas predeterminada del originador, o a la definida en la configuración si no tiene una definida en el perfil de la entidad.", + "exclude-zero-deltas": "Excluir deltas cero del mensaje saliente", + "exclude-zero-deltas-hint": "Si está habilitado, la clave de salida \"{{outputValueKey}}\" se añadirá al mensaje de salida solo si su valor no es cero.", + "exclude-zero-deltas-time-difference-hint": "Si está habilitado, las claves de salida \"{{outputValueKey}}\" y \"{{periodValueKey}}\" se añadirán al mensaje de salida solo si el valor de \"{{outputValueKey}}\" no es cero.", + "search-direction-from": "Del originador a la entidad destino", + "search-direction-to": "De la entidad destino al originador", + "del-relation-direction-from": "Desde el originador", + "del-relation-direction-to": "Hacia el originador", + "target-entity": "Entidad destino", + "function-configuration": "Configuración de la función", + "function-name": "Nombre de la función", + "function-name-required": "El nombre de la función es obligatorio.", + "qualifier": "Calificador", + "qualifier-hint": "Si no se especifica calificador, se usará el calificador predeterminado \"$LATEST\".", + "aws-credentials": "Credenciales de AWS", + "connection-timeout": "Tiempo de espera de conexión", + "connection-timeout-required": "El tiempo de espera de conexión es obligatorio.", + "connection-timeout-min": "El tiempo mínimo de conexión es 0.", + "connection-timeout-hint": "Tiempo en segundos que se espera al establecer la conexión antes de agotar el tiempo. 0 significa infinito, pero no se recomienda.", + "request-timeout": "Tiempo de espera de la solicitud", + "request-timeout-required": "El tiempo de espera de la solicitud es obligatorio", + "request-timeout-min": "El tiempo mínimo de solicitud es 0", + "request-timeout-hint": "Tiempo en segundos que se espera para completar la solicitud antes de agotar el tiempo. 0 significa infinito, pero no se recomienda.", + "units": "Unidades", + "tell-failure-aws-lambda": "Informar fallo si la ejecución de AWS Lambda lanza una excepción", + "tell-failure-aws-lambda-hint": "El nodo de reglas forzará el fallo del procesamiento si la función AWS Lambda lanza una excepción.", + "basic-mode": "Básico", + "advanced-mode": "Avanzado", + "save-time-series": { + "processing-settings": "Configuraciones de procesamiento", + "processing-settings-hint": "Define cómo se procesan los mensajes entrantes. Las configuraciones básicas permiten seleccionar estrategias preconfiguradas, mientras que las avanzadas permiten seleccionar estrategias individuales por acción.", + "advanced-settings-hint": "Ten cuidado al configurar estrategias de procesamiento. Algunas combinaciones pueden llevar a comportamientos inesperados.", + "strategy": "Estrategia", + "deduplication-interval": "Intervalo de desduplicación", + "deduplication-interval-required": "El intervalo de desduplicación es obligatorio", + "deduplication-interval-min-max-range": "El intervalo de desduplicación debe ser al menos 1 segundo y como máximo 1 día", + "strategy-type": { + "every-message": "En cada mensaje", + "skip": "Omitir", + "deduplicate": "Desduplicar", + "web-sockets-only": "Solo WebSockets" + }, + "time-series": "Serie temporal", + "latest": "Últimos valores", + "web-sockets": "WebSockets", + "calculated-fields": "Campos calculados" }, - "image-input": { - "drop-images-or": "Arrastrar y soltar imagenes o", - "drag-and-drop": "Arrastrar y soltar", - "or": "O", - "browse": "Navegar", - "no-images": "No hay imágenes seleccionadas", - "images": "imágenes" + "save-attribute": { + "processing-settings": "Configuraciones de procesamiento", + "processing-settings-hint": "Define cómo se procesan los mensajes entrantes. Las configuraciones básicas permiten seleccionar estrategias preconfiguradas, mientras que las avanzadas permiten seleccionar estrategias individuales por acción.", + "advanced-settings-hint": "Ten cuidado al configurar estrategias de procesamiento. Algunas combinaciones pueden llevar a comportamientos inesperados.", + "strategy": "Estrategia", + "deduplication-interval": "Intervalo de desduplicación", + "deduplication-interval-required": "El intervalo de desduplicación es obligatorio", + "deduplication-interval-min-max-range": "El intervalo de desduplicación debe ser al menos 1 segundo y como máximo 1 día", + "scope": "Ámbito", + "strategy-type": { + "every-message": "En cada mensaje", + "skip": "Omitir", + "deduplicate": "Desduplicar", + "web-sockets-only": "Solo WebSockets" + }, + "attributes": "Atributos" }, - "import": { - "no-file": "Ningún archivo seleccionado", - "drop-file": "Suelte un archivo JSON o haga clic para seleccionar un archivo para cargar.", - "drop-json-file-or": "Suele un archivo JSON o", - "drop-file-csv": "Suelte un archivo CSV o haga clic para seleccionar un archivo para cargar.", - "drop-file-csv-or": "Suelte un archivo CSV o", - "column-value": "Valor", - "column-title": "Título", - "column-example": "Datos de ejemplo", - "column-key": "Clave de atributo/telemetría", - "credentials": "Credenciales", - "csv-delimiter": "Delimitador CSV", - "csv-first-line-header": "La primera línea contiene nombres de columna.", - "csv-update-data": "Actualizar atributos/telemetría", - "details": "Detalles", - "import-csv-number-columns-error": "Un archivo debe contener al menos dos columnas", - "import-csv-invalid-format-error": "Formato de archivo inválido. Línea: '{{line}}'", - "column-type": { - "name": "Nombre", - "type": "Tipo", - "label": "Etiqueta", - "column-type": "Tipo de columna", - "client-attribute": "Atributo de cliente", - "shared-attribute": "Atributo compartido", - "server-attribute": "Atributo de servidor", - "timeseries": "Series de tiempo", - "entity-field": "Campo de entidad", - "access-token": "Token de acceso", - "x509": "X.509", - "mqtt": { - "client-id": "Client ID MQTT", - "user-name": "Usuario MQTT", - "password": "Contraseña MQTT" - }, - "lwm2m": { - "client-endpoint": "Nombre endpoint cliente LwM2M", - "security-config-mode": "Configuración de seguridad LwM2M", - "client-identity": "Identidad cliente LwM2M", - "client-key": "Clave cliente LwM2M", - "client-cert": "Clave pública cliente LwM2M", - "bootstrap-server-security-mode": "Modo seguridad servidor Bootstrap LwM2M", - "bootstrap-server-secret-key": "Clave secreta servidor Bootstrap LwM2M", - "bootstrap-server-public-key-id": "Clave pública o id servidor Bootstrap LwM2M", - "lwm2m-server-security-mode": "Modo seguridad servidor LwM2M", - "lwm2m-server-secret-key": "Clave secreta servidor LwM2M", - "lwm2m-server-public-key-id": "Clave pública servidor LwM2M" - }, - "snmp": { - "host": "Host SNMP", - "port": "Puerto SNMP", - "version": "Versión SNMP (v1, v2c or v3)", - "community-string": "String de SNMP community" - }, - "isgateway": "Es Gateway", - "activity-time-from-gateway-device": "Fecha de actividad desde el dispositivo gateway", - "description": "Descripción", - "routing-key": "Clave Edge", - "secret": "Secreto Edge" - }, - "stepper-text": { - "select-file": "Seleccione un archivo", - "configuration": "Importar configuración", - "column-type": "Seleccionar tipo de columnas", - "creat-entities": "Creando nuevas entidades" - }, - "message": { - "create-entities": "Se crearon {{count}} nuevas entidades correctamente.", - "update-entities": "{{count}} entidades se actualizaron correctamente.", - "error-entities": "Se produjo un error al crear {{count}} entidades." - } + "key-val": { + "key": "Clave", + "value": "Valor", + "see-examples": "Ver ejemplos.", + "remove-entry": "Eliminar entrada", + "remove-mapping-entry": "Eliminar asignación", + "add-mapping-entry": "Agregar asignación", + "add-entry": "Agregar entrada", + "copy-key-values-from": "Copiar clave-valor desde", + "delete-key-values": "Eliminar clave-valor", + "delete-key-values-from": "Eliminar clave-valor desde", + "at-least-one-key-error": "Se debe seleccionar al menos una clave.", + "unique-key-value-pair-error": "¡'{{keyText}}' debe ser diferente de '{{valText}}'!" }, - "item": { - "selected": "Seleccionado" + "mail-body-types": { + "plain-text": "Texto plano", + "html": "HTML", + "dynamic": "Dinámico", + "use-body-type-template": "Usar plantilla de tipo de cuerpo", + "plain-text-description": "Texto simple sin formato con estilo o formato especial.", + "html-text-description": "Permite el uso de etiquetas HTML para formato, enlaces e imágenes en el cuerpo del correo.", + "dynamic-text-description": "Permite usar texto plano o HTML dinámicamente según la función de tematización.", + "after-template-evaluation-hint": "Después de la evaluación de la plantilla, el valor debe ser true para HTML y false para texto plano." + } + }, + "timezone": { + "timezone": "Zona horaria", + "select-timezone": "Seleccionar zona horaria", + "no-timezones-matching": "No se encontraron zonas horarias que coincidan con '{{timezone}}'.", + "timezone-required": "La zona horaria es obligatoria.", + "browser-time": "Hora del navegador" + }, + "queue": { + "queue-name": "Cola", + "no-queues-found": "No se encontraron colas.", + "no-queues-matching": "No se encontraron colas que coincidan con '{{queue}}'.", + "select-name": "Seleccionar nombre de la cola", + "name": "Nombre", + "name-required": "¡El nombre de la cola es obligatorio!", + "name-unique": "¡El nombre de la cola no es único!", + "name-pattern": "¡El nombre de la cola contiene caracteres no permitidos! Solo se permiten caracteres alfanuméricos ASCII, '.', '_' y '-'.", + "queue-required": "¡La cola es obligatoria!", + "topic-required": "¡El tópico de la cola es obligatorio!", + "poll-interval-required": "¡El intervalo de sondeo es obligatorio!", + "poll-interval-min-value": "El valor del intervalo de sondeo no puede ser menor que 1", + "partitions-required": "¡Las particiones son obligatorias!", + "partitions-min-value": "El valor de las particiones no puede ser menor que 1", + "pack-processing-timeout-required": "El tiempo de procesamiento es obligatorio", + "pack-processing-timeout-min-value": "El tiempo de procesamiento no puede ser menor que 1", + "batch-size-required": "¡El tamaño del lote es obligatorio!", + "batch-size-min-value": "El tamaño del lote no puede ser menor que 1", + "retries-required": "¡Los reintentos son obligatorios!", + "retries-min-value": "El número de reintentos no puede ser negativo", + "failure-percentage-required": "¡El porcentaje de fallos es obligatorio!", + "failure-percentage-min-value": "El porcentaje de fallos no puede ser menor que 0", + "failure-percentage-max-value": "El porcentaje de fallos no puede ser mayor que 100", + "pause-between-retries-required": "¡La pausa entre reintentos es obligatoria!", + "pause-between-retries-min-value": "La pausa entre reintentos no puede ser menor que 1", + "max-pause-between-retries-required": "¡La pausa máxima entre reintentos es obligatoria!", + "max-pause-between-retries-min-value": "La pausa máxima entre reintentos no puede ser menor que 1", + "submit-strategy-type-required": "¡El tipo de estrategia de envío es obligatorio!", + "processing-strategy-type-required": "¡El tipo de estrategia de procesamiento es obligatorio!", + "queues": "Colas", + "selected-queues": "{ count, plural, =1 {1 cola} other {# colas} } seleccionada(s)", + "delete-queue-title": "¿Estás seguro de que deseas eliminar la cola '{{queueName}}'?", + "delete-queues-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 cola} other {# colas} }?", + "delete-queue-text": "Ten cuidado, tras la confirmación la cola y todos los datos relacionados serán irrecuperables.", + "delete-queues-text": "Tras la confirmación, todas las colas seleccionadas serán eliminadas y dejarán de estar disponibles.", + "search": "Buscar cola", + "add": "Agregar cola", + "details": "Detalles de la cola", + "topic": "Tópico", + "submit-settings": "Configuración de envío", + "submit-strategy": "Tipo de estrategia *", + "grouping-parameter": "Parámetro de agrupación", + "processing-settings": "Configuración de reintentos", + "processing-strategy": "Tipo de procesamiento *", + "retries-settings": "Configuración de reintentos", + "polling-settings": "Configuración de sondeo", + "batch-processing": "Procesamiento por lotes", + "poll-interval": "Intervalo de sondeo", + "partitions": "Particiones", + "immediate-processing": "Procesamiento inmediato", + "consumer-per-partition": "Enviar mensaje por consumidor", + "consumer-per-partition-hint": "Habilitar consumidores separados por cada partición", + "duplicate-msg-to-all-partitions": "Duplicar mensaje en todas las particiones", + "processing-timeout": "Procesar en, ms", + "batch-size": "Tamaño del lote", + "retries": "Número de reintentos (0 – ilimitado)", + "failure-percentage": "Mensajes fallidos para omitir reintentos, %", + "pause-between-retries": "Reintentar en, seg", + "max-pause-between-retries": "Reintento adicional en, seg", + "delete": "Eliminar cola", + "copyId": "Copiar ID de la cola", + "idCopiedMessage": "ID de la cola copiado al portapapeles", + "description": "Descripción", + "description-hint": "Este texto se mostrará en la descripción de la cola en lugar de la estrategia seleccionada", + "alt-description": "Estrategia de envío: {{submitStrategy}}, Estrategia de procesamiento: {{processingStrategy}}", + "custom-properties": "Propiedades personalizadas", + "custom-properties-hint": "Propiedades personalizadas para la creación de la cola (tópico), por ejemplo: 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies": { + "sequential-by-originator-label": "Secuencial por originador", + "sequential-by-originator-hint": "No se envía un nuevo mensaje para p.ej. dispositivo A hasta que se confirme el mensaje anterior para ese dispositivo", + "sequential-by-tenant-label": "Secuencial por inquilino", + "sequential-by-tenant-hint": "No se envía un nuevo mensaje para p.ej. inquilino A hasta que se confirme el mensaje anterior para ese inquilino", + "sequential-label": "Secuencial", + "sequential-hint": "No se envía un nuevo mensaje hasta que el anterior sea confirmado", + "burst-label": "Explosión (burst)", + "burst-hint": "Todos los mensajes se envían a las cadenas de reglas en el orden en que llegan", + "batch-label": "Por lotes", + "batch-hint": "No se envía un nuevo lote hasta que se confirme el anterior", + "skip-all-failures-label": "Omitir todos los fallos", + "skip-all-failures-hint": "Ignorar todos los fallos", + "skip-all-failures-and-timeouts-label": "Omitir todos los fallos y tiempos de espera", + "skip-all-failures-and-timeouts-hint": "Ignorar todos los fallos y tiempos de espera", + "retry-all-label": "Reintentar todos", + "retry-all-hint": "Reintentar todos los mensajes del paquete de procesamiento", + "retry-failed-label": "Reintentar fallidos", + "retry-failed-hint": "Reintentar todos los mensajes fallidos del paquete", + "retry-timeout-label": "Reintentar tiempos de espera", + "retry-timeout-hint": "Reintentar todos los mensajes con tiempo de espera del paquete", + "retry-failed-and-timeout-label": "Reintentar fallidos y con tiempo de espera", + "retry-failed-and-timeout-hint": "Reintentar todos los mensajes fallidos y con tiempo de espera del paquete" + } + }, + "queue-statistics": { + "queue-statistics": "Estadísticas de la cola", + "no-queue-statistics-matching": "No se encontraron estadísticas de cola que coincidan con '{{entity}}'.", + "queue-statistics-required": "Las estadísticas de la cola son obligatorias.", + "list-of-queue-statistics": "{ count, plural, =1 {Una estadística de cola} other {Lista de # estadísticas de cola} }", + "selected-queue-statistics": "{ count, plural, =1 {1 estadística de cola} other {# estadísticas de cola} } seleccionada(s)", + "no-queue-statistics-text": "No se encontraron estadísticas de cola", + "queue-statistics-starts-with": "Estadísticas de cola cuyos nombres comienzan con '{{prefix}}'" + }, + "server-error": { + "general": "Error general del servidor", + "authentication": "Error de autenticación", + "jwt-token-expired": "Token JWT expirado", + "tenant-trial-expired": "Prueba del inquilino expirada", + "credentials-expired": "Credenciales expiradas", + "permission-denied": "Permiso denegado", + "invalid-arguments": "Argumentos inválidos", + "bad-request-params": "Parámetros de solicitud incorrectos", + "item-not-found": "Elemento no encontrado", + "too-many-requests": "Demasiadas solicitudes", + "too-many-updates": "Demasiadas actualizaciones" + }, + "tenant": { + "tenant": "Inquilino", + "tenants": "Inquilinos", + "management": "Gestión de inquilinos", + "add": "Agregar inquilino", + "admins": "Administradores", + "manage-tenant-admins": "Gestionar administradores del inquilino", + "delete": "Eliminar inquilino", + "add-tenant-text": "Agregar nuevo inquilino", + "no-tenants-text": "No se encontraron inquilinos", + "tenant-details": "Detalles del inquilino", + "title-max-length": "El título debe tener menos de 256 caracteres", + "delete-tenant-title": "¿Estás seguro de que deseas eliminar el inquilino '{{tenantTitle}}'?", + "delete-tenant-text": "Ten cuidado, después de la confirmación el inquilino y todos los datos relacionados serán irrecuperables.", + "delete-tenants-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 inquilino} other {# inquilinos} }?", + "delete-tenants-action-title": "Eliminar { count, plural, =1 {1 inquilino} other {# inquilinos} }", + "delete-tenants-text": "Ten cuidado, después de la confirmación todos los inquilinos seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "title": "Título", + "title-required": "El título es obligatorio.", + "description": "Descripción", + "details": "Detalles", + "events": "Eventos", + "copyId": "Copiar ID del inquilino", + "idCopiedMessage": "ID del inquilino copiado al portapapeles", + "select-tenant": "Seleccionar inquilino", + "no-tenants-matching": "No se encontraron inquilinos que coincidan con '{{entity}}'.", + "tenant-required": "El inquilino es obligatorio", + "search": "Buscar inquilinos", + "selected-tenants": "{ count, plural, =1 {1 inquilino} other {# inquilinos} } seleccionado(s)", + "isolated-tb-rule-engine": "Usar colas aisladas del motor de reglas de ThingsBoard", + "isolated-tb-rule-engine-details": "Cada inquilino tendrá colas del motor de reglas dedicadas" + }, + "tenant-profile": { + "tenant-profile": "Perfil del inquilino", + "tenant-profiles": "Perfiles del inquilino", + "add": "Agregar perfil de inquilino", + "add-profile": "Agregar perfil", + "debug": "Depurar", + "edit": "Editar perfil de inquilino", + "tenant-profile-details": "Detalles del perfil del inquilino", + "no-tenant-profiles-text": "No se encontraron perfiles de inquilino", + "name-max-length": "El nombre debe tener menos de 256 caracteres", + "search": "Buscar perfiles de inquilino", + "selected-tenant-profiles": "{ count, plural, =1 {1 perfil de inquilino} other {# perfiles de inquilino} } seleccionado(s)", + "no-tenant-profiles-matching": "No se encontró ningún perfil de inquilino que coincida con '{{entity}}'.", + "tenant-profile-required": "El perfil de inquilino es obligatorio", + "idCopiedMessage": "El ID del perfil de inquilino ha sido copiado al portapapeles", + "set-default": "Hacer perfil de inquilino predeterminado", + "delete": "Eliminar perfil de inquilino", + "copyId": "Copiar ID del perfil de inquilino", + "name": "Nombre", + "name-required": "El nombre es obligatorio.", + "data": "Datos del perfil", + "profile-configuration": "Configuración del perfil", + "description": "Descripción", + "default": "Predeterminado", + "delete-tenant-profile-title": "¿Estás seguro de que deseas eliminar el perfil de inquilino '{{tenantProfileName}}'?", + "delete-tenant-profile-text": "Ten cuidado, después de la confirmación el perfil de inquilino y todos los datos relacionados serán irrecuperables.", + "delete-tenant-profiles-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 perfil de inquilino} other {# perfiles de inquilino} }?", + "delete-tenant-profiles-text": "Ten cuidado, después de la confirmación todos los perfiles de inquilino seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "set-default-tenant-profile-title": "¿Estás seguro de que deseas hacer predeterminado el perfil de inquilino '{{tenantProfileName}}'?", + "set-default-tenant-profile-text": "Después de la confirmación, el perfil de inquilino se marcará como predeterminado y se usará para nuevos inquilinos sin un perfil especificado.", + "no-tenant-profiles-found": "No se encontraron perfiles de inquilino.", + "create-new-tenant-profile": "¡Crear uno nuevo!", + "create-tenant-profile": "Crear nuevo perfil de inquilino", + "import": "Importar perfil de inquilino", + "export": "Exportar perfil de inquilino", + "export-failed-error": "No se pudo exportar el perfil de inquilino: {{error}}", + "tenant-profile-file": "Archivo del perfil de inquilino", + "invalid-tenant-profile-file-error": "No se pudo importar el perfil de inquilino: estructura de datos inválida.", + "advanced-settings": "Configuraciones avanzadas", + "entities": "Entidades", + "rule-engine": "Motor de reglas", + "time-to-live": "Tiempo de vida", + "calculated-fields": "Campos calculados", + "alarms-and-notifications": "Alarmas y notificaciones", + "ota-files-in-bytes": "Archivos", + "ws-title": "WS", + "unlimited": "(0 - ilimitado)", + "maximum-devices": "Máximo número de dispositivos", + "maximum-devices-required": "Se requiere el número máximo de dispositivos.", + "maximum-devices-range": "El número máximo de dispositivos no puede ser negativo", + "maximum-assets": "Máximo número de activos", + "maximum-assets-required": "Se requiere el número máximo de activos.", + "maximum-assets-range": "El número máximo de activos no puede ser negativo", + "maximum-customers": "Máximo número de clientes", + "maximum-customers-required": "Se requiere el número máximo de clientes.", + "maximum-customers-range": "El número máximo de clientes no puede ser negativo", + "maximum-users": "Máximo número de usuarios", + "maximum-users-required": "Se requiere el número máximo de usuarios.", + "maximum-users-range": "El número máximo de usuarios no puede ser negativo", + "maximum-dashboards": "Máximo número de tableros", + "maximum-dashboards-required": "Se requiere el número máximo de tableros.", + "maximum-dashboards-range": "El número máximo de tableros no puede ser negativo", + "maximum-edges": "Máximo número de edges", + "maximum-edges-required": "Se requiere el número máximo de edges.", + "maximum-edges-range": "El número máximo de edges no puede ser negativo", + "maximum-rule-chains": "Máximo número de cadenas de reglas", + "maximum-rule-chains-required": "Se requiere el número máximo de cadenas de reglas.", + "maximum-rule-chains-range": "El número máximo de cadenas de reglas no puede ser negativo", + "maximum-resources-sum-data-size": "Tamaño total máximo de archivos de recursos (bytes)", + "maximum-resources-sum-data-size-required": "Se requiere el tamaño total máximo de archivos de recursos.", + "maximum-resources-sum-data-size-range": "El tamaño total máximo de archivos de recursos no puede ser negativo", + "maximum-resource-size": "Tamaño máximo de archivo de recurso (bytes)", + "maximum-resource-size-required": "Se requiere el tamaño máximo de archivo de recurso", + "maximum-resource-size-range": "El tamaño máximo de archivo de recurso no puede ser negativo", + "maximum-ota-packages-sum-data-size": "Tamaño total máximo de archivos OTA (bytes)", + "maximum-ota-package-sum-data-size-required": "Se requiere el tamaño total máximo de archivos OTA.", + "maximum-ota-package-sum-data-size-range": "El tamaño total máximo de archivos OTA no puede ser negativo", + "maximum-debug-duration-min": "Duración máxima de depuración (min)", + "maximum-debug-duration-min-range": "La duración máxima de depuración no puede ser negativa", + "rest-requests-for-tenant": "Solicitudes REST para el inquilino", + "transport-tenant-telemetry-msg-rate-limit": "Mensajes de telemetría del inquilino por transporte", + "transport-tenant-telemetry-data-points-rate-limit": "Puntos de datos de telemetría del inquilino por transporte", + "transport-device-msg-rate-limit": "Mensajes del dispositivo por transporte", + "transport-device-telemetry-msg-rate-limit": "Límite de mensajes de telemetría del dispositivo por transporte", + "transport-device-telemetry-data-points-rate-limit": "Límite de puntos de datos de telemetría del dispositivo por transporte", + "transport-gateway-msg-rate-limit": "Límite de mensajes del gateway por transporte", + "transport-gateway-telemetry-msg-rate-limit": "Límite de mensajes de telemetría del gateway por transporte", + "transport-gateway-telemetry-data-points-rate-limit": "Límite de puntos de datos de telemetría del gateway por transporte", + "transport-gateway-device-msg-rate-limit": "Límite de mensajes del dispositivo del gateway por transporte", + "transport-gateway-device-telemetry-msg-rate-limit": "Límite de mensajes de telemetría del dispositivo del gateway por transporte", + "transport-gateway-device-telemetry-data-points-rate-limit": "Límite de puntos de datos de telemetría del dispositivo del gateway por transporte", + "tenant-entity-export-rate-limit": "Límite de creación de versiones de entidad", + "tenant-entity-import-rate-limit": "Límite de carga de versiones de entidad", + "tenant-notification-request-rate-limit": "Límite de solicitudes de notificación", + "tenant-notification-requests-per-rule-rate-limit": "Límite de solicitudes de notificación por regla", + "max-calculated-fields": "Número máximo de campos calculados por entidad", + "max-calculated-fields-range": "El número máximo de campos calculados por entidad no puede ser negativo", + "max-calculated-fields-required": "Se requiere el número máximo de campos calculados por entidad", + "max-data-points-per-rolling-arg": "Número máximo de puntos de datos en argumentos deslizantes", + "max-data-points-per-rolling-arg-range": "El número máximo de puntos de datos en argumentos deslizantes no puede ser negativo", + "max-data-points-per-rolling-arg-required": "Se requiere el número máximo de puntos de datos en argumentos deslizantes", + "max-arguments-per-cf": "Número máximo de argumentos por campo calculado", + "max-arguments-per-cf-range": "El número máximo de argumentos por campo calculado no puede ser negativo", + "max-arguments-per-cf-required": "Se requiere el número máximo de argumentos por campo calculado", + "max-state-size": "Tamaño máximo del estado en KB", + "max-state-size-range": "El tamaño máximo del estado en KB no puede ser negativo", + "max-state-size-required": "Se requiere el tamaño máximo del estado en KB", + "max-value-argument-size": "Tamaño máximo de argumento de valor único en KB", + "max-value-argument-size-range": "El tamaño máximo de argumento de valor único en KB no puede ser negativo", + "max-value-argument-size-required": "Se requiere el tamaño máximo de argumento de valor único en KB", + "max-transport-messages": "Número máximo de mensajes por transporte", + "max-transport-messages-required": "Se requiere el número máximo de mensajes por transporte", + "max-transport-messages-range": "El número máximo de mensajes por transporte no puede ser negativo", + "max-transport-data-points": "Número máximo de puntos de datos por transporte", + "max-transport-data-points-required": "Se requiere el número máximo de puntos de datos por transporte", + "max-transport-data-points-range": "El número máximo de puntos de datos por transporte no puede ser negativo", + "max-r-e-executions": "Número máximo de ejecuciones del motor de reglas", + "max-r-e-executions-required": "Se requiere el número máximo de ejecuciones del motor de reglas", + "max-r-e-executions-range": "El número máximo de ejecuciones del motor de reglas no puede ser negativo", + "max-j-s-executions": "Número máximo de ejecuciones de JavaScript", + "max-j-s-executions-required": "Se requiere el número máximo de ejecuciones de JavaScript", + "max-j-s-executions-range": "El número máximo de ejecuciones de JavaScript no puede ser negativo", + "max-tbel-executions": "Número máximo de ejecuciones de TBEL", + "max-tbel-executions-required": "Se requiere el número máximo de ejecuciones de TBEL", + "max-tbel-executions-range": "El número máximo de ejecuciones de TBEL no puede ser negativo", + "max-d-p-storage-days": "Número máximo de días de almacenamiento de puntos de datos", + "max-d-p-storage-days-required": "Se requiere el número máximo de días de almacenamiento de puntos de datos", + "max-d-p-storage-days-range": "El número máximo de días de almacenamiento de puntos de datos no puede ser negativo", + "default-storage-ttl-days": "Días TTL de almacenamiento por defecto", + "default-storage-ttl-days-required": "Se requiere el valor de días TTL de almacenamiento por defecto", + "default-storage-ttl-days-range": "El valor de días TTL de almacenamiento por defecto no puede ser negativo", + "alarms-ttl-days": "Días TTL de las alarmas", + "alarms-ttl-days-required": "Se requiere los días TTL de las alarmas", + "alarms-ttl-days-days-range": "Los días TTL de las alarmas no pueden ser negativos", + "rpc-ttl-days": "Días TTL de RPC", + "rpc-ttl-days-required": "Se requiere los días TTL de RPC", + "rpc-ttl-days-days-range": "Los días TTL de RPC no pueden ser negativos", + "queue-stats-ttl-days": "Días TTL de estadísticas de colas", + "queue-stats-ttl-days-required": "Se requiere los días TTL de estadísticas de colas", + "queue-stats-ttl-days-range": "Los días TTL de estadísticas de colas no pueden ser negativos", + "rule-engine-exceptions-ttl-days": "Días TTL de excepciones del motor de reglas", + "rule-engine-exceptions-ttl-days-required": "Se requiere los días TTL de excepciones del motor de reglas", + "rule-engine-exceptions-ttl-days-range": "Los días TTL de excepciones del motor de reglas no pueden ser negativos", + "max-rule-node-executions-per-message": "Número máximo de ejecuciones de nodo de reglas por mensaje", + "max-rule-node-executions-per-message-required": "Se requiere el número máximo de ejecuciones de nodo de reglas por mensaje", + "max-rule-node-executions-per-message-range": "El número máximo de ejecuciones de nodo de reglas por mensaje no puede ser negativo", + "max-emails": "Número máximo de correos electrónicos enviados", + "max-emails-required": "Se requiere el número máximo de correos electrónicos enviados", + "max-emails-range": "El número máximo de correos electrónicos enviados no puede ser negativo", + "sms-enabled": "SMS habilitado", + "max-sms": "Número máximo de SMS enviados", + "max-sms-required": "Se requiere el número máximo de SMS enviados", + "max-sms-range": "El número máximo de SMS enviados no puede ser negativo", + "max-created-alarms": "Número máximo de alarmas creadas", + "max-created-alarms-required": "Se requiere el número máximo de alarmas creadas", + "max-created-alarms-range": "El número máximo de alarmas creadas no puede ser negativo", + "no-queue": "Ninguna cola configurada", + "add-queue": "Agregar cola", + "queues-with-count": "Colas ({{count}})", + "tenant-rest-limits": "Solicitudes REST para el inquilino", + "customer-rest-limits": "Solicitudes REST para el cliente", + "incorrect-pattern-for-rate-limits": "El formato es una lista separada por comas de pares de capacidad y período (en segundos) con dos puntos entre ellos, por ejemplo: 100:1,2000:60", + "too-small-value-zero": "El valor debe ser mayor que 0", + "too-small-value-one": "El valor debe ser mayor que 1", + "queue-size-is-limited-by-system-configuration": "El tamaño de la cola también está limitado por la configuración del sistema.", + "cassandra-tenant-limits-configuration": "Consulta Cassandra para el inquilino", + "ws-limit-max-sessions-per-tenant": "Número máximo de sesiones por inquilino", + "ws-limit-max-sessions-per-customer": "Número máximo de sesiones por cliente", + "ws-limit-max-sessions-per-regular-user": "Número máximo de sesiones por usuario regular", + "ws-limit-max-sessions-per-public-user": "Número máximo de sesiones por usuario público", + "ws-limit-queue-per-session": "Tamaño máximo de la cola de mensajes por sesión", + "ws-limit-max-subscriptions-per-tenant": "Número máximo de suscripciones por inquilino", + "ws-limit-max-subscriptions-per-customer": "Número máximo de suscripciones por cliente", + "ws-limit-max-subscriptions-per-regular-user": "Número máximo de suscripciones por usuario regular", + "ws-limit-max-subscriptions-per-public-user": "Número máximo de suscripciones por usuario público", + "ws-limit-updates-per-session": "Actualizaciones WS por sesión", + "rate-limits": { + "add-limit": "Agregar límite", + "advanced-settings": "Configuraciones avanzadas", + "edit-limit": "Editar límite", + "but-less-than": "pero menor que", + "calculated-field-debug-event-rate-limit": "Eventos de depuración de campo calculado", + "edit-calculated-field-debug-event-rate-limit": "Editar límites de eventos de depuración de campo calculado", + "edit-transport-tenant-msg-title": "Editar límites de velocidad de mensajes de transporte del inquilino", + "edit-transport-tenant-telemetry-msg-title": "Editar límites de velocidad de mensajes de telemetría del inquilino", + "edit-transport-tenant-telemetry-data-points-title": "Editar límites de velocidad de puntos de datos de telemetría del inquilino", + "edit-transport-device-msg-title": "Editar límites de velocidad de mensajes de transporte del dispositivo", + "edit-transport-device-telemetry-msg-title": "Editar límites de velocidad de mensajes de telemetría del dispositivo", + "edit-transport-device-telemetry-data-points-title": "Editar límites de velocidad de puntos de datos de telemetría del dispositivo", + "edit-transport-gateway-msg-title": "Editar límites de velocidad de mensajes del gateway de transporte", + "edit-transport-gateway-telemetry-msg-title": "Editar límites de velocidad de mensajes de telemetría del gateway", + "edit-transport-gateway-telemetry-data-points-title": "Editar límites de velocidad de puntos de datos de telemetría del gateway", + "edit-transport-gateway-device-msg-title": "Editar límites de velocidad de mensajes del dispositivo del gateway", + "edit-transport-gateway-device-telemetry-msg-title": "Editar límites de velocidad de mensajes de telemetría del dispositivo del gateway", + "edit-transport-gateway-device-telemetry-data-points-title": "Editar límites de velocidad de puntos de datos de telemetría del dispositivo del gateway", + "edit-tenant-rest-limits-title": "Editar límites de solicitudes REST para el inquilino", + "edit-customer-rest-limits-title": "Editar límites de solicitudes REST para el cliente", + "edit-ws-limit-updates-per-session-title": "Editar límites de actualizaciones WS por sesión", + "edit-cassandra-tenant-limits-configuration-title": "Editar límites de consulta de Cassandra para el inquilino", + "edit-tenant-entity-export-rate-limit-title": "Editar límites de velocidad de creación de versión de entidad", + "edit-tenant-entity-import-rate-limit-title": "Editar límites de velocidad de carga de versión de entidad", + "edit-tenant-notification-request-rate-limit-title": "Editar límites de velocidad de solicitudes de notificación", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "Editar límites de velocidad de solicitudes por regla de notificación", + "edit-edge-events-rate-limit": "Editar límites de velocidad de eventos del edge", + "edit-edge-events-per-edge-rate-limit": "Editar límites de eventos por edge", + "edge-events-rate-limit": "Eventos del edge", + "edge-events-per-edge-rate-limit": "Eventos por edge", + "edit-edge-uplink-messages-rate-limit": "Editar límites de velocidad de mensajes de subida del edge", + "edit-edge-uplink-messages-per-edge-rate-limit": "Editar límites de mensajes de subida por edge", + "edge-uplink-messages-rate-limit": "Mensajes de subida del edge", + "edge-uplink-messages-per-edge-rate-limit": "Mensajes de subida por edge", + "messages-per": "mensajes por", + "not-set": "No establecido", + "number-of-messages": "Número de mensajes", + "number-of-messages-required": "El número de mensajes es obligatorio.", + "number-of-messages-min": "El valor mínimo es 1.", + "preview": "Vista previa", + "per-seconds": "Por segundos", + "per-seconds-required": "La tasa de tiempo es obligatoria.", + "per-seconds-min": "El valor mínimo es 1.", + "rate-limits": "Límites de velocidad", + "remove-limit": "Eliminar límite", + "transport-tenant-msg": "Mensajes de transporte del inquilino", + "transport-tenant-telemetry-msg": "Mensajes de telemetría del inquilino", + "transport-tenant-telemetry-data-points": "Puntos de datos de telemetría del inquilino", + "transport-device-msg": "Mensajes de transporte del dispositivo", + "transport-device-telemetry-msg": "Mensajes de telemetría del dispositivo", + "transport-device-telemetry-data-points": "Puntos de datos de telemetría del dispositivo", + "transport-gateway-msg": "Mensajes de gateway de transporte", + "transport-gateway-telemetry-msg": "Mensajes de telemetría del gateway", + "transport-gateway-telemetry-data-points": "Puntos de datos de telemetría del gateway", + "transport-gateway-device-msg": "Mensajes del dispositivo del gateway", + "transport-gateway-device-telemetry-msg": "Mensajes de telemetría del dispositivo del gateway", + "transport-gateway-device-telemetry-data-points": "Puntos de datos de telemetría del dispositivo del gateway", + "sec": "seg" + } + }, + "timeinterval": { + "seconds-interval": "{ seconds, plural, =1 {1 segundo} other {# segundos} }", + "minutes-interval": "{ minutes, plural, =1 {1 minuto} other {# minutos} }", + "hours-interval": "{ hours, plural, =1 {1 hora} other {# horas} }", + "days-interval": "{ days, plural, =1 {1 día} other {# días} }", + "days": "Días", + "hours": "Horas", + "minutes": "Minutos", + "seconds": "Segundos", + "advanced": "Avanzado", + "custom": "Personalizado", + "predefined": { + "yesterday": "Ayer", + "day-before-yesterday": "Anteayer", + "this-day-last-week": "Este día la semana pasada", + "previous-week": "Semana anterior (Dom - Sáb)", + "previous-week-iso": "Semana anterior (Lun - Dom)", + "previous-month": "Mes anterior", + "previous-quarter": "Trimestre anterior", + "previous-half-year": "Semestre anterior", + "previous-year": "Año anterior", + "current-hour": "Hora actual", + "current-day": "Día actual", + "current-day-so-far": "Día actual hasta ahora", + "current-week": "Semana actual (Dom - Sáb)", + "current-week-iso": "Semana actual (Lun - Dom)", + "current-week-so-far": "Semana actual hasta ahora (Dom - Sáb)", + "current-week-iso-so-far": "Semana actual hasta ahora (Lun - Dom)", + "current-month": "Mes actual", + "current-month-so-far": "Mes actual hasta ahora", + "current-quarter": "Trimestre actual", + "current-quarter-so-far": "Trimestre actual hasta ahora", + "current-half-year": "Semestre actual", + "current-half-year-so-far": "Semestre actual hasta ahora", + "current-year": "Año actual", + "current-year-so-far": "Año actual hasta ahora" }, - "js-func": { - "no-return-error": "La función debe retornar un valor!", - "return-type-mismatch": "La función debe retornar un valor de tipo: '{{type}}'!", - "tidy": "Tidy", - "mini": "Mini" + "type": { + "week": "Semana (Dom - Sáb)", + "week-iso": "Semana (Lun - Dom)", + "month": "Mes", + "quarter": "Trimestre" + } + }, + "timeunit": { + "milliseconds": "Milisegundos", + "seconds": "Segundos", + "minutes": "Minutos", + "hours": "Horas", + "days": "Días" + }, + "timewindow": { + "timewindow": "Ventana de tiempo", + "timewindow-settings": "Configuraciones de ventana de tiempo", + "years": "{ years, plural, =1 { año } other {# años } }", + "years-short": "{{ years }}a", + "months": "{ months, plural, =1 { mes } other {# meses } }", + "months-short": "{{ months }}M", + "weeks": "{ weeks, plural, =1 { semana } other {# semanas } }", + "weeks-short": "{{ weeks }}s", + "days": "{ days, plural, =1 { día } other {# días } }", + "days-short": "{{ days }}d", + "hours": "{ hours, plural, =0 { hora } =1 {1 hora } other {# horas } }", + "hr": "{{ hr }} h", + "hr-short": "{{ hr }}h", + "minutes": "{ minutes, plural, =0 { minuto } =1 {1 minuto } other {# minutos } }", + "min": "{{ min }} min", + "min-short": "{{ min }}m", + "seconds": "{ seconds, plural, =0 { segundo } =1 {1 segundo } other {# segundos } }", + "sec": "{{ sec }} seg", + "sec-short": "{{ sec }}s", + "short": { + "years": "{ years, plural, =1 {1 año } other {# años } }", + "days": "{ days, plural, =1 {1 día } other {# días } }", + "hours": "{ hours, plural, =1 {1 hora } other {# horas } }", + "minutes": "{{minutes}} min", + "seconds": "{{seconds}} seg" }, - "key-val": { - "key": "Clave", - "value": "Valor", - "remove-entry": "Borrar entrada", - "add-entry": "Añadir entrada", - "no-data": "Sin datos" + "realtime": "Tiempo real", + "history": "Historial", + "last-prefix": "últimos", + "period": "desde {{ startTime }} hasta {{ endTime }}", + "edit": "Editar ventana de tiempo", + "date-range": "Rango de fechas", + "for-all-time": "Para todo el tiempo", + "last": "Últimos", + "time-period": "Período de tiempo", + "hide": "Ocultar", + "interval": "Intervalo", + "just-now": "Justo ahora", + "just-now-lower": "justo ahora", + "ago": "atrás", + "style": "Estilo de la ventana de tiempo", + "icon": "Ícono", + "icon-position": "Posición del ícono", + "icon-position-left": "Izquierda", + "icon-position-right": "Derecha", + "font": "Fuente", + "color": "Color", + "displayTypePrefix": "Mostrar prefijo Tiempo real/Historial", + "preview": "Vista previa", + "relative": "Relativo", + "range": "Rango", + "hide-timewindow-section": "Ocultar la sección de ventana de tiempo para los usuarios finales", + "hide-last-interval": "Ocultar el último intervalo para los usuarios finales", + "hide-relative-interval": "Ocultar el intervalo relativo para los usuarios finales", + "hide-fixed-interval": "Ocultar el intervalo fijo para los usuarios finales", + "hide-aggregation": "Ocultar agregación para los usuarios finales", + "hide-group-interval": "Ocultar intervalo de agrupación para los usuarios finales", + "hide-max-values": "Ocultar valores máximos para los usuarios finales", + "hide-timezone": "Ocultar zona horaria para los usuarios finales", + "disable-custom-interval": "Desactivar selección de intervalo personalizado", + "edit-aggregation-functions-list": "Editar lista de funciones de agregación", + "edit-aggregation-functions-list-hint": "Se puede especificar una lista de opciones disponibles.", + "allowed-aggregation-functions": "Funciones de agregación permitidas", + "edit-intervals-list": "Editar lista de intervalos", + "allowed-agg-intervals": "Intervalos de agrupación", + "default-agg-interval": "Intervalo de agrupación predeterminado", + "edit-intervals-list-hint": "Se puede especificar una lista de opciones de intervalo disponibles.", + "edit-grouping-intervals-list-hint": "Es posible configurar la lista de intervalos de agrupación y el intervalo de agrupación predeterminado.", + "all": "Todos" + }, + "tooltip": { + "trigger": "Disparador", + "trigger-point": "Punto", + "trigger-axis": "Eje", + "label": "Etiqueta", + "value": "Valor", + "date": "Fecha", + "show-date-time-interval": "Mostrar intervalo de fecha y hora", + "show-date-time-interval-hint": "Mostrar intervalo de fecha y hora de acuerdo con la agregación de datos.", + "background-color": "Color de fondo", + "background-blur": "Desenfoque de fondo" + }, + "unit": { + "millimeter": "Milímetro", + "centimeter": "Centímetro", + "angstrom": "Ångström", + "nanometer": "Nanómetro", + "micrometer": "Micrómetro", + "meter": "Metro", + "kilometer": "Kilómetro", + "inch": "Pulgada", + "foot": "Pie", + "yard": "Yarda", + "mile": "Milla", + "nautical-mile": "Milla náutica", + "astronomical-unit": "Unidad astronómica", + "reciprocal-metre": "Metro recíproco", + "meter-per-meter": "Metro por metro", + "steradian": "Estereorradián", + "thou": "Milésima de pulgada", + "barleycorn": "Barleycorn", + "hand": "Mano", + "chain": "Cadena", + "furlong": "Furlong", + "league": "Legua", + "fathom": "Braza", + "cable": "Cable", + "link": "Eslabón", + "rod": "Vara", + "nanogram": "Nanogramo", + "microgram": "Microgramo", + "milligram": "Miligramo", + "gram": "Gramo", + "kilogram": "Kilogramo", + "tonne": "Tonelada", + "ounce": "Onza", + "pound": "Libra", + "stone": "Stone", + "hundredweight-count": "Centena de peso (hundredweight)", + "short-tons": "Toneladas cortas", + "dalton": "Dalton", + "grain": "Grano", + "drachm": "Dracma", + "quarter": "Cuarto", + "slug": "Slug", + "carat": "Quilate", + "cubic-millimeter": "Milímetro cúbico", + "cubic-centimeter": "Centímetro cúbico", + "cubic-meter": "Metro cúbico", + "cubic-kilometer": "Kilómetro cúbico", + "microliter": "Microlitro", + "milliliter": "Mililitro", + "liter": "Litro", + "hectoliter": "Hectolitro", + "cubic-inch": "Pulgada cúbica", + "cubic-foot": "Pie cúbico", + "cubic-yard": "Yarda cúbica", + "fluid-ounce": "Onza líquida", + "pint": "Pinta", + "quart": "Cuarto", + "gallon": "Galón", + "oil-barrels": "Barril de petróleo", + "cubic-meter-per-kilogram": "Metro cúbico por kilogramo", + "gill": "Gill", + "hogshead": "Hogshead", + "teaspoon": "Cucharadita", + "tablespoon": "Cucharada", + "cup": "Taza", + "celsius": "Celsius", + "kelvin": "Kelvin", + "rankine": "Rankine", + "fahrenheit": "Fahrenheit", + "percent": "Porcentaje", + "meter-per-second": "Metro por segundo", + "kilometer-per-hour": "Kilómetro por hora", + "foot-per-second": "Pie por segundo", + "mile-per-hour": "Milla por hora", + "knot": "Nudo", + "millimeters-per-minute": "Milímetros por minuto", + "kilometer-per-hour-squared": "Kilómetro por hora al cuadrado", + "foot-per-second-squared": "Pie por segundo al cuadrado", + "pascal": "Pascal", + "kilopascal": "Kilopascal", + "megapascal": "Megapascal", + "gigapascal": "Gigapascal", + "millibar": "Milibar", + "bar": "Bar", + "kilobar": "Kilobar", + "newton": "Newton", + "newton-meter": "Newton metro", + "foot-pounds": "Pies-libra", + "inch-pounds": "Pulgadas-libra", + "newton-per-meter": "Newton por metro", + "atmospheres": "Atmósferas", + "pounds-per-square-inch": "Libras por pulgada cuadrada", + "torr": "Torr", + "inches-of-mercury": "Pulgadas de mercurio", + "pascal-per-square-meter": "Pascal por metro cuadrado", + "pound-per-square-inch": "Libra por pulgada cuadrada", + "newton-per-square-meter": "Newton por metro cuadrado", + "kilogram-force-per-square-meter": "Kilogramo-fuerza por metro cuadrado", + "pascal-per-square-centimeter": "Pascal por centímetro cuadrado", + "ton-force-per-square-inch": "Tonelada-fuerza por pulgada cuadrada", + "kilonewton-per-square-meter": "Kilonewton por metro cuadrado", + "newton-per-square-millimeter": "Newton por milímetro cuadrado", + "microjoule": "Microjulio", + "millijoule": "Milijulio", + "joule": "Julio", + "kilojoule": "Kilojulio", + "megajoule": "Megajulio", + "gigajoule": "Gigajulio", + "watt-hour": "Vatio-hora", + "kilowatt-hour": "Kilovatio-hora", + "electron-volts": "Electrón-voltios", + "joules-per-coulomb": "Julios por culombio", + "british-thermal-unit": "Unidad térmica británica", + "foot-pound": "Pie-libra", + "calorie": "Caloría", + "small-calorie": "Caloría pequeña", + "kilocalorie": "Kilocaloría", + "joule-per-kelvin": "Julio por kelvin", + "joule-per-kilogram-kelvin": "Julio por kilogramo-kelvin", + "joule-per-kilogram": "Julio por kilogramo", + "watt-per-meter-kelvin": "Vatio por metro-kelvin", + "joule-per-cubic-meter": "Julio por metro cúbico", + "therm": "Termia", + "electric-dipole-moment": "Momento dipolar eléctrico", + "magnetic-dipole-moment": "Momento dipolar magnético", + "debye": "Debye", + "coulomb-per-square-meter-per-volt": "Culombio por metro cuadrado por voltio", + "milliwatt": "Miliwatio", + "microwatt": "Microwatio", + "watt": "Vatio", + "kilowatt": "Kilovatio", + "megawatt": "Megavatio", + "gigawatt": "Gigavatio", + "metric-horsepower": "Caballo de fuerza métrico", + "milliwatt-per-square-centimeter": "Miliwatios por centímetro cuadrado", + "watt-per-square-centimeter": "Vatios por centímetro cuadrado", + "kilowatt-per-square-centimeter": "Kilovatios por centímetro cuadrado", + "milliwatt-per-square-meter": "Miliwatios por metro cuadrado", + "watt-per-square-meter": "Vatios por metro cuadrado", + "kilowatt-per-square-meter": "Kilovatios por metro cuadrado", + "watt-per-square-inch": "Vatios por pulgada cuadrada", + "kilowatt-per-square-inch": "Kilovatios por pulgada cuadrada", + "horsepower": "Caballo de fuerza", + "btu-per-hour": "Unidades térmicas británicas por hora", + "coulomb": "Culombio", + "millicoulomb": "Miliculombios", + "microcoulomb": "Microculombio", + "picocoulomb": "Picoculombio", + "coulomb-per-meter": "Culombio por metro", + "coulomb-per-cubic-meter": "Culombio por metro cúbico", + "coulomb-per-square-meter": "Culombio por metro cuadrado", + "square-millimeter": "Milímetro cuadrado", + "square-centimeter": "Centímetro cuadrado", + "square-meter": "Metro cuadrado", + "hectare": "Hectárea", + "square-kilometer": "Kilómetro cuadrado", + "square-inch": "Pulgada cuadrada", + "square-foot": "Pie cuadrado", + "square-yard": "Yarda cuadrada", + "acre": "Acre", + "square-mile": "Milla cuadrada", + "are": "Área", + "barn": "Barn", + "circular-inch": "Pulgada circular", + "milliampere-hour": "Miliamperio-hora", + "ampere-hours": "Amperios-hora", + "kiloampere-hours": "Kiloamperios-hora", + "nanoampere": "Nanoamperio", + "picoampere": "Picoamperio", + "microampere": "Microamperio", + "milliampere": "Miliamperio", + "ampere": "Amperio", + "microampere-per-square-centimeter": "Microamperio por centímetro cuadrado", + "ampere-per-square-meter": "Amperio por metro cuadrado", + "ampere-per-meter": "Amperio por metro", + "oersted": "Oersted", + "bohr-magneton": "Magnetón de Bohr", + "ampere-meter-squared": "Amperio-metro cuadrado", + "nanovolt": "Nanovoltio", + "picovolt": "Picovoltio", + "volt": "Voltio", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "Voltímetro", + "kilovolt-meter": "Kilovoltímetro", + "megavolt-meter": "Megavoltímetro", + "microvolt-meter": "Microvoltímetro", + "millivolt-meter": "Milivoltímetro", + "nanovolt-meter": "Nanovoltímetro", + "ohm": "Ohmio", + "microohm": "Microohmio", + "milliohm": "Miliohmio", + "kilohm": "Kiloohmio", + "megohm": "Megaohmio", + "gigohm": "Gigaohmio", + "hertz": "Hercio", + "kilohertz": "Kilohertz", + "megahertz": "Megahertz", + "gigahertz": "Gigahertz", + "rpm": "Revoluciones por minuto", + "candela-per-square-meter": "Candela por metro cuadrado", + "candela": "Candela", + "lumen": "Lumen", + "lux": "Lux", + "foot-candle": "Pie-candela", + "lumen-per-square-meter": "Lumen por metro cuadrado", + "lux-second": "Lux segundo", + "lumen-second": "Lumen segundo", + "lumens-per-watt": "Lúmenes por vatio", + "mole": "Mol", + "nanomole": "Nanomol", + "micromole": "Micromol", + "millimole": "Milimol", + "kilomole": "Kilomol", + "mole-per-cubic-meter": "Mol por metro cúbico", + "rssi": "RSSI", + "ppm": "Partes por millón", + "ppb": "Partes por mil millones", + "micrograms-per-cubic-meter": "Microgramos por metro cúbico", + "aqi": "Índice de calidad del aire (AQI)", + "gram-per-cubic-meter": "Gramo por metro cúbico", + "gram-per-kilogram": "Humedad específica", + "millimeters-per-second": "Milímetros por segundo", + "neper": "Neper", + "bel": "Bel", + "decibel": "Decibelio", + "meters-per-second-squared": "Metros por segundo al cuadrado", + "becquerel": "Becquerel", + "curie": "Curie", + "gray": "Gray", + "sievert": "Sievert", + "roentgen": "Roentgen", + "cps": "Cuentas por segundo", + "rad": "Rad", + "rem": "Rem", + "dps": "Desintegraciones por segundo", + "rutherford": "Rutherford", + "coulombs-per-kilogram": "Culombios por kilogramo", + "becquerels-per-cubic-meter": "Becquereles por metro cúbico", + "curies-per-liter": "Curies por litro", + "becquerels-per-second": "Becquereles por segundo", + "curies-per-second": "Curies por segundo", + "gy-per-second": "Gray por segundo", + "watt-per-steradian": "Vatio por estereorradián", + "watt-per-square-metre-steradian": "Vatio por metro cuadrado-estereorradián", + "ph-level": "Nivel de pH", + "turbidity": "Turbidez", + "mg-per-liter": "Miligramos por litro", + "microsiemens-per-centimeter": "Microsiemens por centímetro", + "millisiemens-per-meter": "Milisiemens por metro", + "siemens-per-meter": "Siemens por metro", + "kilogram-per-cubic-meter": "Kilogramo por metro cúbico", + "gram-per-cubic-centimeter": "Gramo por centímetro cúbico", + "kilogram-per-square-meter": "Kilogramo por metro cuadrado", + "milligram-per-milliliter": "Miligramo por mililitro", + "milligram-per-cubic-meter": "Miligramo por metro cúbico", + "pound-per-cubic-foot": "Libra por pie cúbico", + "ounces-per-cubic-inch": "Onzas por pulgada cúbica", + "tons-per-cubic-yard": "Toneladas por yarda cúbica", + "particle-density": "Densidad de partículas", + "kilometers-per-liter": "Kilómetros por litro", + "miles-per-gallon": "Millas por galón", + "liters-per-100-km": "Litros por 100 km", + "gallons-per-mile": "Galones por milla", + "liters-per-hour": "Litros por hora", + "gallons-per-hour": "Galones por hora", + "beats-per-minute": "Pulsaciones por minuto", + "millimeters-of-mercury": "Milímetros de mercurio", + "milligrams-per-deciliter": "Miligramos por decilitro", + "g-force": "Fuerza G", + "kilonewton": "Kilonewton", + "kilogram-force": "Kilogramo-fuerza", + "pound-force": "Libra-fuerza", + "kilopound-force": "Kilolibra-fuerza", + "dyne": "Dina", + "poundal": "Poundal", + "kip": "Kip", + "gal": "Gal", + "gravity": "Gravedad", + "hectopascal": "Hectopascal", + "atmosphere": "Atmósfera", + "millibars": "Milibares", + "inch-of-mercury": "Pulgada de mercurio", + "richter-scale": "Escala de Richter", + "second": "Segundo", + "minute": "Minuto", + "hour": "Hora", + "day": "Día", + "week": "Semana", + "month": "Mes", + "year": "Año", + "cubic-foot-per-minute": "Pie cúbico por minuto", + "cubic-meters-per-hour": "Metros cúbicos por hora", + "cubic-meters-per-second": "Metros cúbicos por segundo", + "liter-per-second": "Litro por segundo", + "liter-per-minute": "Litro por minuto", + "gallons-per-minute": "Galones por minuto", + "cubic-foot-per-second": "Pie cúbico por segundo", + "milliliters-per-minute": "Mililitros por minuto", + "bit": "Bit", + "byte": "Byte", + "kilobyte": "Kilobyte", + "megabyte": "Megabyte", + "gigabyte": "Gigabyte", + "terabyte": "Terabyte", + "petabyte": "Petabyte", + "exabyte": "Exabyte", + "zettabyte": "Zettabyte", + "yottabyte": "Yottabyte", + "bit-per-second": "Bit por segundo", + "kilobit-per-second": "Kilobit por segundo", + "megabit-per-second": "Megabit por segundo", + "gigabit-per-second": "Gigabit por segundo", + "terabit-per-second": "Terabit por segundo", + "byte-per-second": "Byte por segundo", + "kilobyte-per-second": "Kilobyte por segundo", + "megabyte-per-second": "Megabyte por segundo", + "gigabyte-per-second": "Gigabyte por segundo", + "degree": "Grado", + "radian": "Radián", + "gradian": "Gradián", + "revolution": "Revolución", + "siemens": "Siemens", + "millisiemens": "Milisiemens", + "microsiemens": "Microsiemens", + "kilosiemens": "Kilosiemens", + "megasiemens": "Megasiemens", + "gigasiemens": "Gigasiemens", + "farad": "Faradio", + "millifarad": "Milifaradio", + "microfarad": "Microfaradio", + "nanofarad": "Nanofaradio", + "picofarad": "Picofaradio", + "kilofarad": "Kilofaradio", + "megafarad": "Megafaradio", + "gigafarad": "Gigafaradio", + "terfarad": "Terafaradio", + "farad-per-meter": "Faradio por metro", + "tesla": "Tesla", + "gauss": "Gauss", + "kilogauss": "Kilogauss", + "millitesla": "Militesla", + "microtesla": "Microtesla", + "nanotesla": "Nanotesla", + "kilotesla": "Kilotesla", + "megatesla": "Megatesla", + "millitesla-square-meters": "Militesla por metro cuadrado", + "gamma": "Gamma", + "lambda": "Lambda", + "square-meter-per-second": "Metro cuadrado por segundo", + "square-centimeter-per-second": "Centímetro cuadrado por segundo", + "stoke": "Stoke", + "centistokes": "Centistokes", + "square-foot-per-second": "Pie cuadrado por segundo", + "square-inch-per-second": "Pulgada cuadrada por segundo", + "pascal-second": "Pascal-segundo", + "centipoise": "Centipoise", + "poise": "Poise", + "reynolds": "Reynolds", + "pound-per-foot-hour": "Libra por pie-hora", + "newton-second-per-square-meter": "Newton segundo por metro cuadrado", + "dyne-second-per-square-centimeter": "Dina segundo por centímetro cuadrado", + "kilogram-per-meter-second": "Kilogramo por metro-segundo", + "tesla-square-meters": "Tesla por metro cuadrado", + "maxwell": "Maxwell", + "tesla-per-meter": "Tesla por metro", + "gauss-per-centimeter": "Gauss por centímetro", + "weber": "Weber", + "microweber": "Microweber", + "milliweber": "Milliweber", + "gauss-square-centimeter": "Gauss por centímetro cuadrado", + "kilogauss-square-centimeter": "Kilogauss por centímetro cuadrado", + "henry": "Henry", + "millihenry": "Milihenry", + "microhenry": "Microhenry", + "nanohenry": "Nanohenry", + "henry-per-meter": "Henry por metro", + "tesla-meter-per-ampere": "Tesla metro por amperio", + "gauss-per-oersted": "Gauss por Oersted", + "kilogram-per-mole": "Kilogramo por mol", + "gram-per-mole": "Gramo por mol", + "milligram-per-mole": "Miligramo por mol", + "joule-per-mole": "Julio por mol", + "joule-per-mole-kelvin": "Julio por mol-kelvin", + "millivolts-per-meter": "Milivoltios por metro", + "volts-per-meter": "Voltios por metro", + "kilovolts-per-meter": "Kilovoltios por metro", + "radian-per-second": "Radián por segundo", + "radian-per-second-squared": "Radián por segundo cuadrado", + "revolutions-per-minute-per-second": "Aceleración angular", + "deg-per-second": "grados/segundo", + "degrees-brix": "Grados Brix", + "katal": "Katal", + "katal-per-cubic-metre": "Katal por metro cúbico" + }, + "user": { + "user": "Usuario", + "users": "Usuarios", + "customer-users": "Usuarios del cliente", + "tenant-admins": "Administradores del inquilino", + "sys-admin": "Administrador del sistema", + "tenant-admin": "Administrador del inquilino", + "customer": "Cliente", + "anonymous": "Anónimo", + "add": "Agregar usuario", + "delete": "Eliminar usuario", + "add-user-text": "Agregar nuevo usuario", + "no-users-text": "No se encontraron usuarios", + "user-details": "Detalles del usuario", + "delete-user-title": "¿Está seguro de que desea eliminar al usuario '{{userEmail}}'?", + "delete-user-text": "Tenga cuidado, después de la confirmación el usuario y todos los datos relacionados serán irrecuperables.", + "delete-users-title": "¿Está seguro de que desea eliminar { count, plural, =1 {1 usuario} other {# usuarios} }?", + "delete-users-action-title": "Eliminar { count, plural, =1 {1 usuario} other {# usuarios} }", + "delete-users-text": "Tenga cuidado, después de la confirmación todos los usuarios seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "activation-email-sent-message": "¡Correo de activación enviado con éxito!", + "resend-activation": "Reenviar activación", + "email": "Correo electrónico", + "email-required": "El correo electrónico es obligatorio.", + "invalid-email-format": "Formato de correo electrónico no válido.", + "first-name": "Nombre", + "last-name": "Apellido", + "description": "Descripción", + "default-dashboard": "Tablero predeterminado", + "always-fullscreen": "Siempre pantalla completa", + "select-user": "Seleccionar usuario", + "no-users-matching": "No se encontraron usuarios que coincidan con '{{entity}}'.", + "user-required": "Se requiere un usuario", + "activation-method": "Método de activación", + "display-activation-link": "Mostrar enlace de activación", + "send-activation-mail": "Enviar correo de activación", + "activation-link": "Enlace de activación del usuario", + "activation-link-text": "Para activar el usuario use el siguiente enlace de activación (expira en {{activationLinkTtl}}) :", + "copy-activation-link": "Copiar enlace de activación", + "activation-link-copied-message": "El enlace de activación del usuario ha sido copiado al portapapeles", + "details": "Detalles", + "login-as-tenant-admin": "Iniciar sesión como administrador del inquilino", + "login-as-customer-user": "Iniciar sesión como usuario del cliente", + "search": "Buscar usuarios", + "selected-users": "{ count, plural, =1 {1 usuario} other {# usuarios} } seleccionados", + "disable-account": "Desactivar cuenta de usuario", + "enable-account": "Activar cuenta de usuario", + "enable-account-message": "¡La cuenta de usuario ha sido activada con éxito!", + "disable-account-message": "¡La cuenta de usuario ha sido desactivada con éxito!", + "copyId": "Copiar ID del usuario", + "idCopiedMessage": "El ID del usuario ha sido copiado al portapapeles", + "user-list": "Lista de usuarios", + "user-list-required": "Se requiere la lista de usuarios" + }, + "value": { + "type": "Tipo de valor", + "string": "Cadena", + "string-value": "Valor de cadena", + "string-value-required": "Se requiere un valor de cadena", + "integer": "Entero", + "integer-value": "Valor entero", + "integer-value-required": "Se requiere un valor entero", + "invalid-integer-value": "Valor entero no válido", + "double": "Doble", + "double-value": "Valor doble", + "double-value-required": "Se requiere un valor doble", + "boolean": "Booleano", + "boolean-value": "Valor booleano", + "false": "Falso", + "true": "Verdadero", + "long": "Long", + "json": "JSON", + "json-value": "Valor JSON", + "json-value-invalid": "El valor JSON tiene un formato no válido", + "json-value-required": "Se requiere un valor JSON." + }, + "version-control": { + "version-control": "Control de versiones", + "management": "Gestión de control de versiones", + "search": "Buscar versiones", + "branch": "Rama", + "default": "Predeterminado", + "select-branch": "Seleccionar rama", + "branch-required": "Se requiere una rama", + "create-entity-version": "Crear versión de entidad", + "version-name": "Nombre de la versión", + "version-name-required": "Se requiere el nombre de la versión", + "author": "Autor", + "export-relations": "Exportar relaciones", + "export-attributes": "Exportar atributos", + "export-credentials": "Exportar credenciales", + "export-calculated-fields": "Exportar campos calculados", + "entity-versions": "Versiones de entidad", + "versions": "Versiones", + "created-time": "Fecha de creación", + "version-id": "ID de versión", + "no-entity-versions-text": "No se encontraron versiones de entidad", + "no-versions-text": "No se encontraron versiones", + "copy-full-version-id": "Copiar ID de versión completo", + "create-version": "Crear versión", + "creating-version": "Creando versión... Por favor espera", + "nothing-to-commit": "No hay cambios para confirmar", + "restore-version": "Restaurar versión", + "restore-entity-from-version": "Restaurar entidad desde versión '{{versionName}}'", + "restoring-entity-version": "Restaurando versión de entidad... Por favor espera", + "load-relations": "Cargar relaciones", + "load-attributes": "Cargar atributos", + "load-credentials": "Cargar credenciales", + "load-calculated-fields": "Cargar campos calculados", + "compare-with-current": "Comparar con actual", + "diff-entity-with-version": "Diferencias con versión de entidad '{{versionName}}'", + "previous-difference": "Diferencia anterior", + "next-difference": "Siguiente diferencia", + "current": "Actual", + "differences": "{ count, plural, =1 {1 diferencia} other {# diferencias} }", + "create-entities-version": "Crear versión de entidades", + "default-sync-strategy": "Estrategia de sincronización predeterminada", + "sync-strategy-merge": "Combinar", + "sync-strategy-overwrite": "Sobrescribir", + "entities-to-export": "Entidades a exportar", + "entities-to-restore": "Entidades a restaurar", + "sync-strategy": "Estrategia de sincronización", + "all-entities": "Todas las entidades", + "no-entities-to-export-prompt": "Por favor especifica las entidades a exportar", + "no-entities-to-restore-prompt": "Por favor especifica las entidades a restaurar", + "add-entity-type": "Agregar tipo de entidad", + "remove-all": "Eliminar todo", + "version-create-result": "{ added, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } añadida.
{ modified, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } modificada.
{ removed, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } eliminada.", + "remove-other-entities": "Eliminar otras entidades", + "find-existing-entity-by-name": "Buscar entidad existente por nombre", + "restore-entities-from-version": "Restaurar entidades desde la versión '{{versionName}}'", + "restoring-entities-from-version": "Restaurando entidades... Por favor espera", + "no-entities-restored": "No se restauraron entidades", + "created": "{{created}} creada(s)", + "updated": "{{updated}} actualizada(s)", + "deleted": "{{deleted}} eliminada(s)", + "remove-other-entities-confirm-text": "¡Cuidado! Esto eliminará permanentemente todas las entidades actuales
que no estén presentes en la versión que deseas restaurar.

Escribe \"remove other entities\" para confirmar.", + "auto-commit-to-branch": "auto-confirmar en la rama {{ branch }}", + "default-create-entity-version-name": "Actualización de {{entityName}}", + "sync-strategy-merge-hint": "Crea o actualiza las entidades seleccionadas en el repositorio. Todas las demás entidades del repositorio no se modifican.", + "sync-strategy-overwrite-hint": "Crea o actualiza las entidades seleccionadas en el repositorio. Todas las demás entidades del repositorio se eliminan.", + "device-credentials-conflict": "No se pudo cargar el dispositivo con ID externo {{entityId}}
porque las mismas credenciales ya están presentes en la base de datos para otro dispositivo.
Considera desactivar la opción cargar credenciales en el formulario de restauración.", + "missing-referenced-entity": "No se pudo cargar el {{sourceEntityTypeName}} con ID externo {{sourceEntityId}}
porque hace referencia a un {{targetEntityTypeName}} faltante con ID {{targetEntityId}}.", + "runtime-failed": "Error: {{message}}", + "auto-commit-settings-read-only-hint": "La función de auto-confirmación no funciona con la opción de solo lectura habilitada en la configuración del repositorio.", + "rollback-on-error": "Reversión en caso de error", + "rollback-on-error-hint": "Si tienes una gran cantidad de entidades para restaurar, considera desactivar esta opción para aumentar el rendimiento.\n Nota: si ocurre un error durante la carga de la versión, las entidades ya persistidas (con relaciones, atributos, etc.) permanecerán como están." + }, + "widget": { + "widget-library": "Biblioteca de widgets", + "widget-bundle": "Paquete de widgets", + "all-bundles": "Todos los paquetes", + "select-widgets-bundle": "Seleccionar paquete de widgets", + "widgets": "Widgets", + "all-widgets": "Todos los widgets", + "widget": "Widget", + "select-widget": "Seleccionar widget", + "no-widgets-matching": "No se encontraron widgets que coincidan con '{{entity}}'.", + "no-widgets": "Aún no hay widgets", + "no-widgets-text": "No se encontraron widgets", + "management": "Gestión de widgets", + "editor": "Editor de widgets", + "confirm-to-exit-editor-html": "Tienes configuraciones del widget sin guardar.
¿Estás seguro de que deseas salir de esta página?", + "widget-type-not-found": "Problema al cargar la configuración del widget.
Probablemente se haya eliminado el tipo de widget asociado.", + "widget-type-load-error": "El widget no se cargó debido a los siguientes errores:", + "remove": "Eliminar widget", + "delete": "Eliminar widget", + "edit": "Editar widget", + "remove-widget-title": "¿Estás seguro de que deseas eliminar el widget '{{widgetTitle}}'?", + "remove-widget-text": "Después de la confirmación, el widget y todos los datos relacionados serán irrecuperables.", + "replace-reference-with-widget-copy": "Reemplazar referencia con copia del widget", + "timeseries": "Serie temporal", + "search-data": "Buscar datos", + "no-data-found": "No se encontraron datos", + "latest": "Valores más recientes", + "rpc": "Widget de control", + "alarm": "Widget de alarma", + "static": "Widget estático", + "timeseries-short": "serie", + "latest-short": "recientes", + "rpc-short": "control", + "alarm-short": "alarma", + "static-short": "estático", + "select-widget-type": "Seleccionar tipo de widget", + "missing-widget-title-error": "¡Debe especificarse un título para el widget!", + "widget-saved": "Widget guardado", + "unable-to-save-widget-error": "¡No se pudo guardar el widget! ¡El widget contiene errores!", + "save": "Guardar widget", + "saveAs": "Guardar widget como", + "move": "Mover widget", + "save-widget-as": "Guardar widget como", + "save-widget-as-text": "Por favor, introduce un nuevo título para el widget", + "toggle-fullscreen": "Activar/desactivar pantalla completa", + "run": "Ejecutar widget", + "widget-title": "Título del widget", + "title": "Título", + "title-required": "Se requiere el título del widget.", + "title-max-length": "El título debe tener menos de 256 caracteres", + "system": "Sistema", + "type": "Tipo de widget", + "resources": "Recursos", + "resource-url": "URL de JavaScript/CSS", + "resource-is-extension": "Es extensión", + "remove-resource": "Eliminar recurso", + "add-resource": "Agregar recurso", + "html": "HTML", + "tidy": "Ordenar", + "css": "CSS", + "settings-form": "Formulario de configuración", + "data-key-settings-form": "Formulario de configuración de clave de datos", + "latest-data-key-settings-form": "Formulario de configuración de clave de datos más reciente", + "widget-settings": "Configuraciones del widget", + "description": "Descripción", + "tags": "Etiquetas", + "image-preview": "Vista previa de imagen", + "settings-form-selector": "Selector de formulario de configuración", + "data-key-settings-form-selector": "Selector de formulario de clave de datos", + "latest-data-key-settings-form-selector": "Selector de formulario de clave de datos más reciente", + "all": "Todos", + "actual": "Actual", + "scada": "Símbolo SCADA", + "deprecated": "Obsoleto", + "has-basic-mode": "Tiene modo básico", + "basic-mode-form-selector": "Selector de formulario de modo básico", + "basic-mode": "Básico", + "advanced-mode": "Avanzado", + "javascript": "Javascript", + "js": "JS", + "delete-widget-title": "¿Estás seguro de que deseas eliminar el widget '{{widgetName}}'?", + "delete-widget-text": "Después de la confirmación, el widget y todos los datos relacionados serán irrecuperables.", + "delete-widgets-title": "¿Estás seguro de que deseas eliminar { count, plural, =1 {1 widget} other {# widgets} }?", + "delete-widgets-text": "Ten cuidado, después de la confirmación, todos los widgets seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "delete-widget": "Eliminar widget", + "widget-template-load-failed-error": "¡Error al cargar la plantilla del widget!", + "details": "Detalles", + "widget-details": "Detalles del widget", + "add": "Agregar widget", + "add-existing-widget": "Agregar widget existente", + "add-new-widget": "Agregar nuevo widget", + "search-widgets": "Buscar widgets", + "selected-widgets": "{ count, plural, =1 {1 widget} other {# widgets} } seleccionado(s)", + "undo": "Deshacer cambios del widget", + "export": "Exportar widget", + "export-prompt": "Incluir imágenes y recursos del widget", + "export-widgets": "Exportar widgets", + "export-widgets-prompt": "Incluir imágenes y recursos de los widgets", + "import": "Importar widget", + "no-data": "No hay datos para mostrar en el widget", + "data-overflow": "El widget muestra {{count}} de {{total}} entidades", + "alarm-data-overflow": "El widget muestra alarmas para {{allowedEntities}} entidades (máximo permitido) de un total de {{totalEntities}} entidades", + "search": "Buscar widget", + "filter": "Tipo de filtro del widget", + "loading-widgets": "Cargando widgets...", + "widget-template-error": "Plantilla HTML del widget no válida.", + "reference": "Referencia" + }, + "widget-action": { + "header-button": "Botón de encabezado del widget", + "do-nothing": "No hacer nada", + "open-dashboard-state": "Navegar a un nuevo estado del tablero", + "update-dashboard-state": "Actualizar el estado actual del tablero", + "open-dashboard": "Navegar a otro tablero", + "custom": "Acción personalizada", + "custom-pretty": "Acción personalizada (con plantilla HTML)", + "custom-pretty-error-title": "Error en el diálogo personalizado", + "custom-pretty-template-error": "Plantilla del diálogo personalizado no válida.", + "custom-pretty-controller-error": "Se produjo un error al evaluar la función del diálogo personalizado.", + "mobile-action": "Acción móvil", + "target-dashboard-state": "Estado objetivo del tablero", + "target-dashboard-state-required": "Se requiere el estado objetivo del tablero", + "set-entity-from-widget": "Establecer entidad desde el widget", + "target-dashboard": "Tablero objetivo", + "select-target-dashboard": "Seleccionar tablero objetivo", + "target-dashboard-required": "Se requiere un tablero objetivo.", + "open-right-layout": "Abrir diseño derecho del tablero (vista móvil)", + "state-display-type": "Opción de visualización del estado del tablero", + "open-normal": "Normal", + "open-in-separate-dialog": "Abrir en diálogo separado", + "open-in-popover": "Abrir en ventana emergente (popover)", + "dialog-title": "Título del diálogo", + "dialog-hide-dashboard-toolbar": "Ocultar barra de herramientas del tablero en el diálogo", + "dialog-width": "Ancho del diálogo en porcentaje relativo al ancho de la ventana", + "dialog-height": "Altura del diálogo en porcentaje relativo a la altura de la ventana", + "dialog-size-range-error": "El valor del tamaño del diálogo debe estar en un rango de 1 a 100.", + "popover-preferred-placement": "Ubicación preferida del popover", + "popover-placement-top": "Arriba", + "popover-placement-topLeft": "Arriba a la izquierda", + "popover-placement-topRight": "Arriba a la derecha", + "popover-placement-right": "Derecha", + "popover-placement-rightTop": "Arriba a la derecha", + "popover-placement-rightBottom": "Abajo a la derecha", + "popover-placement-bottom": "Abajo", + "popover-placement-bottomLeft": "Abajo a la izquierda", + "popover-placement-bottomRight": "Abajo a la derecha", + "popover-placement-left": "Izquierda", + "popover-placement-leftTop": "Arriba a la izquierda", + "popover-placement-leftBottom": "Abajo a la izquierda", + "popover-hide-on-click-outside": "Ocultar popover al hacer clic fuera", + "popover-hide-dashboard-toolbar": "Ocultar barra de herramientas del tablero en el popover", + "popover-width": "Ancho del popover", + "popover-height": "Altura del popover", + "popover-style": "Estilo del popover", + "open-new-browser-tab": "Abrir en una nueva pestaña del navegador", + "open-URL": "Abrir URL", + "URL": "URL", + "url-required": "Se requiere la URL.", + "mobile": { + "device-provision": "Provisión del dispositivo", + "action-type": "Tipo de acción móvil", + "select-action-type": "Seleccionar tipo de acción móvil", + "action-type-required": "Se requiere el tipo de acción móvil", + "take-picture-from-gallery": "Seleccionar imagen de la galería", + "take-photo": "Tomar foto", + "map-direction": "Abrir direcciones en el mapa", + "map-location": "Abrir ubicación en el mapa", + "scan-qr-code": "Escanear código QR", + "make-phone-call": "Hacer llamada telefónica", + "get-location": "Obtener ubicación del teléfono", + "take-screenshot": "Tomar captura de pantalla" }, - "layout": { - "layout": "Diseño", - "layouts": "Diseños", - "manage": "Administrar diseños", - "settings": "Ajustes de diseño", - "color": "Color", - "main": "Principal", - "right": "Derecho", - "left": "Izquierdo", - "select": "Seleccionar diseño de destino", - "percentage-width": "Ancho en porcentaje (%)", - "fixed-width": "Ancho fijo (px)", - "left-width": "Columna izquierda (%)", - "right-width": "Columna derecha (%)", - "pick-fixed-side": "Lado fijo: ", - "layout-fixed-width": "Ancho fijo (px)", - "value-min-error": "El valor debe ser mayor que {{min}}{{unit}}", - "value-max-error": "El valor debe ser menor que {{max}}{{unit}}", - "layout-fixed-width-required": "Se requiere ancho fijo", - "right-width-percentage-required": "Se requiere porcentaje derecha", - "left-width-percentage-required": "Se requiere porcentaje izquierda", - "divider": "Divisor", - "right-side": "Diseño derecho", - "left-side": "Diseño izquierdo" + "custom-action-function": "Función de acción personalizada", + "custom-pretty-function": "Función de acción personalizada (con plantilla HTML)", + "map-item-type": "Tipo de elemento del mapa", + "map-item": { + "marker": "Marcador", + "polygon": "Polígono", + "rectangle": "Rectángulo", + "circle": "Círculo" }, - "legend": { - "direction": "Dirección de la leyenda", - "position": "Posición de la leyenda", - "show-values": "Mostrar valores", - "min-option": "Min", - "max-option": "Max", - "average-option": "Media", - "total-option": "Total", - "latest-option": "Últimos", - "sort-legend": "Ordenar claves en leyenda", - "show-max": "Mostrar valor máximo", - "show-min": "Mostrar valor mínimo", - "show-avg": "Mostrar valor promedio", - "show-total": "Mostrar valor total", - "show-latest": "Mostrar último valor", - "settings": "Configuración de la leyenda", - "min": "mínimo", - "max": "máximo", - "avg": "promedio", - "total": "total", - "latest": "último", - "comparison-time-ago": { - "previousInterval": "(intervalo anterior)", - "customInterval": "(intervalo personalizado)", - "days": "(hace un día)", - "weeks": "(hace una semana)", - "months": "(hace un mes)", - "years": "(hace un año)" - } + "place-map-item": "Colocar elemento en el mapa", + "map-item-tooltip": { + "customize-map-item-tooltips": "Personalizar los tooltips de los elementos del mapa", + "place-marker": "Colocar marcador", + "start-draw-rectangle": "Iniciar dibujo del rectángulo", + "finish-draw-rectangle": "Finalizar dibujo del rectángulo", + "start-draw-polygon": "Iniciar dibujo del polígono", + "continue-draw-polygon": "Continuar dibujo del polígono", + "finish-draw-polygon": "Finalizar dibujo del polígono", + "start-draw-circle": "Iniciar dibujo del círculo", + "finish-draw-circle": "Finalizar dibujo del círculo" + } + }, + "widgets-bundle": { + "current": "Paquete actual", + "widgets-bundles": "Paquetes de widgets", + "widgets-bundle-widgets": "Widgets del paquete", + "add": "Agregar paquete de widgets", + "delete": "Eliminar paquete de widgets", + "title": "Título", + "title-required": "El título es obligatorio.", + "title-max-length": "El título debe tener menos de 256 caracteres", + "description": "Descripción", + "image-preview": "Vista previa de la imagen", + "scada": "Paquete de widgets SCADA", + "order": "Orden", + "add-widgets-bundle-text": "Agregar nuevo paquete de widgets", + "no-widgets-bundles-text": "No se encontraron paquetes de widgets", + "empty": "El paquete de widgets está vacío", + "details": "Detalles", + "widgets-bundle-details": "Detalles del paquete de widgets", + "delete-widgets-bundle-title": "¿Está seguro de que desea eliminar el paquete de widgets '{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text": "Tenga cuidado, después de la confirmación, el paquete de widgets y todos los datos relacionados serán irrecuperables.", + "delete-widgets-bundles-title": "¿Está seguro de que desea eliminar { count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} }?", + "delete-widgets-bundles-action-title": "Eliminar { count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} }", + "delete-widgets-bundles-text": "Tenga cuidado, después de la confirmación, todos los paquetes de widgets seleccionados serán eliminados y todos los datos relacionados serán irrecuperables.", + "no-widgets-bundles-matching": "No se encontraron paquetes de widgets que coincidan con '{{widgetsBundle}}'.", + "widgets-bundle-required": "Se requiere un paquete de widgets.", + "system": "Sistema", + "import": "Importar paquete de widgets", + "export": "Exportar paquete de widgets", + "export-widgets-bundle-widgets-prompt": "Incluir widgets del paquete en los datos exportados (de lo contrario, solo se exportarán los FQN de los widgets referenciados)", + "export-failed-error": "No se pudo exportar el paquete de widgets: {{error}}", + "create-new-widgets-bundle": "Crear nuevo paquete de widgets", + "widgets-bundle-file": "Archivo del paquete de widgets", + "invalid-widgets-bundle-file-error": "No se pudo importar el paquete de widgets: estructura de datos del paquete no válida.", + "search": "Buscar paquetes de widgets", + "selected-widgets-bundles": "{ count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} } seleccionado(s)", + "open-widgets-bundle": "Abrir paquete de widgets", + "loading-widgets-bundles": "Cargando paquetes de widgets...", + "create-new": "Crear nuevo paquete de widgets" + }, + "widget-config": { + "data": "Datos", + "settings": "Configuración", + "advanced": "Avanzado", + "appearance": "Apariencia", + "widget-card": "Tarjeta del widget", + "mobile": "Móvil", + "title": "Título", + "title-tooltip": "Tooltip del título", + "general-settings": "Configuración general", + "display-title": "Mostrar título del widget", + "card-title": "Título de la tarjeta", + "drop-shadow": "Sombra", + "enable-fullscreen": "Habilitar pantalla completa", + "background-color": "Color de fondo", + "text-color": "Color del texto", + "border-radius": "Radio del borde", + "padding": "Relleno", + "margin": "Margen", + "widget-style": "Estilo del widget", + "widget-css": "CSS del widget", + "title-style": "Estilo del título", + "mobile-mode-settings": "Modo móvil", + "order": "Orden", + "height": "Altura", + "mobile-hide": "Ocultar widget en modo móvil", + "desktop-hide": "Ocultar widget en modo escritorio", + "units": "Símbolo especial a mostrar junto al valor", + "units-by-default": "Unidades por defecto", + "decimals": "Número de dígitos después del punto decimal", + "decimals-by-default": "Decimales por defecto", + "default-data-key-parameter-hint": "Este parámetro se aplica a todos los valores del widget, a menos que se anule mediante la configuración de la clave de datos", + "units-short": "Unidades", + "decimals-short": "Decimales", + "decimals-suffix": "decimales", + "digits-suffix": "dígitos", + "timewindow": "Ventana de tiempo", + "use-dashboard-timewindow": "Usar ventana de tiempo del tablero", + "use-widget-timewindow": "Usar ventana de tiempo del widget", + "display-timewindow": "Mostrar ventana de tiempo", + "legend": "Leyenda", + "display-legend": "Mostrar leyenda", + "datasources": "Orígenes de datos", + "datasource": "Origen de datos", + "maximum-datasources": "Máximo { count, plural, =1 {1 origen de datos permitido.} other {# orígenes de datos permitidos} }", + "timeseries-key-error": "Debe especificarse al menos una clave de datos de serie temporal", + "datasource-type": "Tipo", + "datasource-parameters": "Parámetros", + "remove-datasource": "Eliminar origen de datos", + "add-datasource": "Agregar origen de datos", + "target-device": "Dispositivo objetivo", + "alarm-source": "Fuente de alarma", + "actions": "Acciones", + "action": "Acción", + "add-action": "Agregar acción", + "search-actions": "Buscar acciones", + "no-actions-text": "No se encontraron acciones", + "action-source": "Fuente de la acción", + "select-action-source": "Seleccionar fuente de la acción", + "action-source-required": "La fuente de la acción es obligatoria.", + "column-index": "Índice de columna", + "select-column-index": "Seleccionar índice de columna", + "column-index-required": "El índice de columna es obligatorio.", + "not-set": "No establecido", + "action-name": "Nombre", + "action-name-required": "El nombre de la acción es obligatorio.", + "action-name-not-unique": "Ya existe otra acción con el mismo nombre.\nEl nombre de la acción debe ser único dentro de la misma fuente.", + "action-icon": "Ícono", + "header-button": { + "button-settings": "Configuración del botón", + "button-type": "Tipo de botón", + "button-type-basic": "Básico", + "button-type-raised": "Elevado", + "button-type-stroked": "Delimitado", + "button-type-flat": "Plano", + "button-type-icon": "Ícono", + "button-type-mini-fab": "FAB", + "colors": "Colores", + "color": "Color", + "background": "Fondo", + "border": "Borde", + "advanced-button-style": "Estilo avanzado del botón", + "button-style": "Estilo del botón" }, - "login": { - "login": "Entrar", - "request-password-reset": "Solicitar restablecer contraseña", - "reset-password": "Restablecer contraseña", - "create-password": "Crear contraseña", - "two-factor-authentication": "Autenticado de dos factores", - "passwords-mismatch-error": "¡Las contraseñas introducidas deben ser iguales!", - "password-again": "Repita la contraseña de nuevo", - "sign-in": "Por favor, inicie sesión", - "username": "Nombre de usuario (correo electrónico)", - "remember-me": "Recordarme", - "forgot-password": "¿Olvidó la contraseña?", - "password-reset": "Restablecer contraseña", - "expired-password-reset-message": "Tus credenciales han expirado! Por favor, crea una nueva contraseña.", - "new-password": "Nueva contraseña", - "new-password-again": "Repita la nueva contraseña", - "password-link-sent-message": "Se ha enviado el enlace de restablecimiento de contraseña con éxito!", - "email": "Email", - "login-with": "Login con {{name}}", - "or": "o", - "error": "Error de Login", - "verify-your-identity": "Verificar identidad", - "select-way-to-verify": "Selecciona el modo de verificación", - "resend-code": "Reenviar código", - "resend-code-wait": "Reenviar código en { time, plural, =1 {1 segundo} other {# segundos} }", - "try-another-way": "Otros modos de verificación", - "totp-auth-description": "Por favor, introduce el código de seguridad de tu aplicación de autenticación.", - "totp-auth-placeholder": "Código", - "sms-auth-description": "Se ha enviado un código de verificación a tu número: {{contact}} proporcionado.", - "sms-auth-placeholder": "Código SMS", - "email-auth-description": "Se ha enviado un código de verificación al email {{contact}} proporcionado.", - "email-auth-placeholder": "Código Email", - "backup-code-auth-description": "Por favor, introduce el código de backup.", - "backup-code-auth-placeholder": "Código de Backup" + "show-hide-action-using-function": "Mostrar/ocultar acción usando función", + "show-action-function": "Función para mostrar la acción", + "action-type": "Tipo", + "action-type-required": "El tipo de acción es obligatorio.", + "edit-action": "Editar acción", + "delete-action": "Eliminar acción", + "delete-action-title": "Eliminar acción del widget", + "delete-action-text": "¿Está seguro de que desea eliminar la acción del widget con nombre '{{actionName}}'?", + "title-icon": "Ícono del título", + "display-icon": "Mostrar ícono del título", + "card-icon": "Ícono de la tarjeta", + "icon": "Ícono", + "icon-color": "Color del ícono", + "icon-size": "Tamaño del ícono", + "advanced-settings": "Configuraciones avanzadas", + "data-settings": "Configuración de datos", + "limits": "Límites", + "no-data-display-message": "Mensaje alternativo \"Sin datos para mostrar\"", + "data-page-size": "Entidades máximas por origen de datos", + "settings-component-not-found": "Componente del formulario de configuración no encontrado para el selector '{{selector}}'", + "preview": "Previsualizar", + "set": "Establecer", + "set-message": "Establecer mensaje", + "advanced-title-style": "Estilo avanzado del título", + "card-style": "Estilo de la tarjeta", + "text": "Texto", + "background": "Fondo", + "advanced-widget-style": "Estilo avanzado del widget", + "card-buttons": "Botones de la tarjeta", + "show-card-buttons": "Mostrar botones de la tarjeta", + "card-border-radius": "Radio del borde de la tarjeta", + "card-padding": "Relleno de la tarjeta", + "card-appearance": "Apariencia de la tarjeta", + "color": "Color", + "tooltip": "Información sobre herramientas", + "units-required": "La unidad es obligatoria.", + "list-layout": "Diseño de lista", + "layout": "Diseño", + "resize-options": "Opciones de redimensionamiento", + "resizable": "Redimensionable", + "preserve-aspect-ratio": "Preservar la relación de aspecto" + }, + "widget-type": { + "import": "Importar tipo de widget", + "export": "Exportar tipo de widget", + "export-failed-error": "No se pudo exportar el widget: {{error}}", + "widget-file": "Archivo de widget", + "invalid-widget-file-error": "No se pudo importar el widget: Estructura de datos de widget inválida." + }, + "markdown": { + "edit": "Editar", + "preview": "Vista previa", + "copy-code": "Haga clic para copiar", + "copied": "¡Copiado!" + }, + "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "La configuración depende del widget de código QR de la aplicación móvil en la configuración principal de la plataforma", + "get-it-on-google-play": "Consíguelo en Google Play", + "download-on-the-app-store": "Descárgalo en el App Store" }, - "signup": { - "firstname": "Nombre", - "lastname": "Apellido", - "email": "Correo electrónico", - "signup": "Registrarse", - "create-password": "Crear una contraseña", - "repeat-password": "Repite tu contraseña", - "have-account": "¿Ya tienes una cuenta?", - "signin": "Iniciar sesión", - "no-captcha-message": "Debes confirmar que no eres un robot", - "password-length-message": "Tu contraseña debe tener al menos 6 caracteres", - "email-verification": "Verificación de correo electrónico", - "email-verification-message": "Se envió un correo electrónico con detalles de verificación a la dirección de correo electrónico especificada. Por favor, sigue las instrucciones proporcionadas en el correo electrónico para completar tu proceso de registro. Nota: si no has visto el correo electrónico durante un tiempo, por favor revisa tu carpeta de 'spam' o intenta reenviar el correo electrónico haciendo clic en el botón 'Reenviar'.", - "account-activation-title": "Activación de cuenta", - "account-activated": "¡Cuenta activada con éxito!", - "account-activated-text": "¡Felicidades! Tu cuenta ha sido activada.", - "resend": "Reenviar", - "inactive-user-exists-title": "Ya existe un usuario inactivo", - "inactive-user-exists-text": "Ya hay un usuario registrado con dirección de correo electrónico no verificada. Haz clic en el botón 'Reenviar' si deseas reenviar el correo electrónico de verificación.", - "activating-account": "Activando cuenta...", - "activating-account-text": "Tu cuenta se está activando actualmente. Por favor espera...", - "accept-privacy-policy": "Aceptar la Política de Privacidad", - "accept": "Aceptar", - "privacy-policy": "Política de Privacidad", - "accept-privacy-policy-message": "Debes aceptar nuestra Política de Privacidad", - "recaptcha-title": "reCAPTCHA", - "terms-of-use": "Términos de Uso", - "accept-terms-of-use-message": "Debes aceptar nuestros Términos de Uso" + "action-button": { + "behavior": "Comportamiento", + "on-click": "Al hacer clic", + "on-click-hint": "Acción que se activa al hacer clic en el botón", + "first-button-click": "Primer clic del botón", + "first-button-click-hint": "Acción al presionar el primer botón.", + "second-button-click": "Segundo clic del botón", + "second-button-click-hint": "Acción al presionar el segundo botón.", + "button-click-hint": "Acción al presionar en el widget." }, - "markdown": { - "edit": "Editar", - "preview": "Previsualizar", - "copy-code": "Click para copiar", - "copied": "Copiado!" + "command-button": { + "behavior": "Comportamiento", + "on-click": "Al hacer clic", + "on-click-hint": "Acción realizada al hacer clic en el botón." }, - "notification": { - "action-button": "Botón de acción", - "action-type": "Tipo de acción", - "active": "Activo", - "add-notification-recipients-group": "Añadir grupo de destinatarios", - "add-notification-template": "Añadir plantilla de notificaciones", - "add-recipient": "Añadir destinatario", - "add-recipients": "Añadir destinatarios", - "add-rule": "Añadir regla", - "add-stage": "Añadir etapa", - "add-template": "Añadir plantilla", - "after": "Después", - "alarm-assignment-trigger-settings": "Ajustes de activación de asignación de alarmas", - "alarm-comment-trigger-settings": "Ajustes de activación comentarios de alarma", - "alarm-trigger-settings": "Ajustes de activación de alarma", - "all": "Todas", - "api-feature-hint": "Si el campo está vacío, la activación se aplicará a todas las características API", - "api-usage-trigger-settings": "Ajustes de activación de uso API", - "new-platform-version-trigger-settings": "Ajustes de activación de nueva versión de plataforma", - "rate-limits-trigger-settings": "Ajustes de límites excedidos", - "at-least-one-should-be-selected": "Se debe seleccionar por lo menos uno", - "basic-settings": "Ajustes básicos", - "button-text": "Texto del botón", - "button-text-required": "Se requiere texto del botón", - "button-text-max-length": "El texto del botón debe ser menor o igual que {{ length }} caracteres", - "compose": "Redactar", - "conversation": "Conversación", - "conversation-required": "Se requiere conversación", - "copy-notification-template": "Copiar plantilla de notificación", - "copy-rule": "Copiar regla", - "copy-template": "Copiar plantilla", - "create-new": "Crear nueva", - "created": "Creada", - "delete-notification-text": "Atención, tras la confirmación, la notificación no será recuperable.", - "delete-notification-title": "Borrar la notificación, ¿Estás seguro?", - "delete-notifications-text": "Atención, tras la confirmación, las notificaciones no serán recuperables.", - "delete-notifications-title": "Estás seguro de eliminar { count, plural, =1 {1 notificación} other {# notificaciones} }?", - "delete-recipient-text": "Atención, tras la confirmación, el destinatario no será recuperable.", - "delete-recipient-title": "Estás seguro de eliminar el destinatario '{{recipientName}}'?", - "delete-recipients-text": "Atención, tras la confirmación, los destinatarios no serán recuperables.", - "delete-recipients-title": "Estás seguro de eliminar { count, plural, =1 {1 destinatario} other {# destinatarios} }?", - "delete-request-text": "Atención, tras la confirmación, la solicitud no será recuperable.", - "delete-request-title": "Estás seguro de eliminar la solicitud?", - "delete-requests-text": "Atención, tras la confirmación, las solicitudes no serán recuperables.", - "delete-requests-title": "Estás seguro de eliminar { count, plural, =1 {1 solicitud} other {# solicitudes} }?", - "delete-rule-text": "Atención, tras la confirmación, la regla no será recuperable.", - "delete-rule-title": "Estás seguro de eliminar la regla '{{ruleName}}'?", - "delete-rules-text": "Atención, tras la confirmación, las reglas no serán recuperables.", - "delete-rules-title": "Estás seguro de eliminar { count, plural, =1 {1 regla} other {# reglas} }?", - "delete-template-text": "Atención, tras la confirmación, la plantilla no será recuperable.", - "delete-template-title": "Estás seguro de eliminar la plantilla '{{templateName}}'?", - "delete-templates-text": "Atención, tras la confirmación, las plantillas no serán recuperables.", - "delete-templates-title": "Estás seguro de eliminar { count, plural, =1 {1 plantilla} other {# plantillas} }?", - "deleted": "Borrada", - "delivery-method": { - "delivery-method": "Método de entrega", - "email": "Email", - "email-preview": "Vista previa de notificación eMail", - "slack": "Slack", - "slack-preview": "Vista previa de notificación Slack", - "microsoft-teams": "Microsoft Teams", - "microsoft-teams-preview": "Vista previa de notificación por Microsoft Teams", - "sms": "SMS", - "sms-preview": "Vista previa de notificación SMS", - "web": "Web", - "web-preview": "Vista previa de notificación Web" - }, - "delivery-method-not-configure-click": "El método de entrega no está configurado. Click para configurar.", - "delivery-method-not-configure-contact": "El método de entrega no está configurado. Contacta al administrador del sistema.", - "delivery-methods": "Métodos de entrega", - "description": "Descripción", - "device-activity-trigger-settings": "Ajustes de activación de dispositivo activo", - "device-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los dispositivos", - "device-profiles-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los perfiles de dispositivo", - "disabled": "Desactivado", - "edit-notification-recipients-group": "Editar grupo de destinatarios para notificaciones", - "edit-notification-template": "Editar plantilla de notificación", - "edit-rule": "Editar regla", - "edit-template": "Editar plantilla", - "enabled": "Activado", - "entities-limit-trigger-settings": "Ajustes de disparo en límites de entidad", - "entity-action-trigger-settings": "Ajustes de disparo en acciones de entidad", - "entity-type": "Tipo de entidad", - "escalation-chain": "Cadena de escalado", - "failed-send": "Fallos en el envío", - "fails": "{ count, plural, =1 {1 fallo} other {# fallos} }", - "filter": "Filtro", - "first-recipient": "Primer destinatario", - "inactive": "Inactivo", - "inbox": "Bandeja de entrada", - "notification-inbox": "Notificaciones / Bandeja de entrada", - "input-field-support-templatization": "El campo soporta plantillas.", - "input-fields-support-templatization": "Los campos soportan plantillas.", - "link": "Enlace", - "link-required": "Se requiere enlace", - "link-type": { - "dashboard": "Abrir panel", - "link": "Abrir enlace URL" - }, - "loading-notifications": "Cargando notificaciones...", - "management": "Administración de notificaciones", - "mark-all-as-read": "Marcar todas como leídas", - "mark-as-read": "Marcar como leída", - "message": "Mensaje", - "message-required": "Se requiere mensaje", - "message-max-length": "El mensaje debe ser menor o igual que {{ length }} caracteres", - "name": "Nombre", - "name-required": "Se requiere nombre", - "new-notification": "Nueva notificación", - "no-inbox-notification": "No se encontraron notificaciones", - "no-notification-request": "Sin solicitud de notificación", - "no-notification-templates": "No se encontraron plantillas", - "no-notifications-yet": "No hay notificaciones", - "no-recipients-notification": "No hay destinatarios", - "no-rule": "No hay regla configurada", - "no-rules-notification": "Sin reglas de notificación", - "no-severity-found": "No se encontró gravedad", - "no-severity-matching": "'{{severity}}' no encontrada.", - "no-template-matching": "No hay recursos que coincidan con '{{template}}'.", - "not-found-slack-recipient": "No se ha encontrado destinatario Slack", - "notification": "Notificación", - "notification-center": "Centro de notificaciones", - "notify": "notificar", - "notify-again": "Notificar de nuevo", - "notify-alarm-action": { - "acknowledged": "Alarma acusada", - "assigned": "Alarma asignada", - "cleared": "Alarma borrada", - "created": "Alarma creada", - "severity-changed": "Gravedad cambiada", - "unassigned": "Alarma no asignada" - }, - "notify-on": "Notificar en", - "notify-on-comment-update": "Notificar cuando haya un comentario nuevo", - "notify-on-required": "Se requiere notificar en", - "notify-on-unassign": "Notificar en deasignación", - "notify-only-user-comments": "Notificar sólo cuando haya comentarios de usuario", - "only-rule-chain-lifecycle-failures": "Sólo fallos de ciclo de vida en cadena de reglas", - "only-rule-node-lifecycle-failures": "Sólo fallos de ciclo de vida en nodos de reglas", - "platform-users": "Usuarios de la plataforma", - "rate-limits": "Límites de tasa", - "rate-limits-hint": "Si el campo está vacío, el activador se aplicará a todos los límites de tasa", - "recipient": "Destinatario", - "recipient-group": "Grupo de destinatarios", - "recipient-type": { - "affected-tenant-administrators": "Administradores afectados", - "affected-user": "Usuario afectado", - "all-users": "Todos los usuarios", - "customer-users": "Usuarios del cliente", - "system-administrators": "Administradores del sistema", - "tenant-administrators": "Administradores de propietarios", - "user-filters": "Filtro de usuarios", - "user-list": "Lista de usuarios", - "users-entity-owner": "Usuarios que sean propietarios de la entidad" - }, - "recipients": "Destinatarios", - "notification-recipients": "Notificaciones / Destinatarios", - "recipients-count": "{ count, plural, =1 {1 destinatario} other {# destinatarios} }", - "recipients-required": "Se requieren destinatarios", - "refresh-allow-delivery-method": "Actualiza permitir método de entrega", - "request-search": "Búsqueda de solicitudes", - "request-status": { - "processing": "Procesando", - "scheduled": "En cola", - "sent": "Enviado" - }, - "review": "Examinar", - "rule": "Regla", - "rule-chain-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todas las cadenas de reglas", - "rule-engine-events-trigger-settings": "Ajustes de disparo en motor de reglas", - "rule-engine-filter": "Filtro de motor de reglas", - "rule-name": "Nombre de regla", - "rule-name-required": "Se requiere nombre", - "rule-disable": "Desactivar regla de notificación", - "rule-enable": "Activar regla de notificación", - "rule-node-filter": "Filtro de regla", - "rules": "Reglas", - "notification-rules": "Notificaciones / Reglas", - "scheduler-later": "Programar para más tarde", - "search-notification": "Buscar notificaciones", - "search-recipients": "Buscar destinatarios", - "search-rules": "Buscar reglas", - "search-templates": "Buscar plantillas", - "see-documentation": "Ver documentación", - "selected-notifications": "{ count, plural, =1 {1 notificación} other {# notificaciones} } seleccionadas", - "selected-recipients": "{ count, plural, =1 {1 destinatario} other {# destinatarios} } seleccionados", - "selected-requests": "{ count, plural, =1 {1 solicitud} other {# solicitudes} } seleccionados", - "selected-rules": "{ count, plural, =1 {1 regla} other {# reglas} } seleccionados", - "selected-template": "{ count, plural, =1 {1 plantilla} other {# plantillas} } seleccionados", - "send-notification": "Enviar notificación", - "sent": "Enviado", - "notification-sent": "Notificaciones / Enviadas", - "set-entity-from-notification": "Establecer entidad de notificación a estado de panel", - "slack-chanel-type": "Tipo de canal Slack", - "slack-chanel-types": { - "direct": "Mensaje directo", - "private-channel": "Canal privado", - "public-channel": "Canal público" - }, - "start-from-scratch": "Empezar de 0", - "status": "Estado", - "stop-escalation-alarm-status-become": "Detener la escalada cuando el estado de alarma se convierta en:", - "subject": "Asunto", - "subject-required": "Se requiere asunto", - "template": "Plantilla", - "template-name": "Nombre de plantilla", - "template-required": "Se requiere nombre de plantilla", - "template-type": { - "alarm": "Alarma", - "alarm-assignment": "Asignación de alarma", - "alarm-comment": "Comentario de alarma", - "api-usage-limit": "Límite en uso de API", - "device-activity": "Actividad de dispositivo", - "entities-limit": "Límites de entidades", - "entity-action": "Acciones de entidad", - "general": "General", - "rule-engine-lifecycle-event": "Evento de ciclo de vida en motor de reglas", - "rule-node": "Nodo de regla", - "new-platform-version": "Nueva versión de plataforma", - "rate-limits": "Se excedieron los límites de tasa" - }, - "templates": "Plantillas", - "notification-templates": "Notificaciones / Plantillas", - "tenant-profiles-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los perfiles de administradores propietarios", - "tenants-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los administradores propietarios", - "threshold": "Umbral", - "theme-color": "Color del tema", - "time": "Hora", - "track-rule-node-events": "Hacer seguimiento de eventos en nodos de reglas", - "trigger": { - "alarm": "Alarma", - "alarm-assignment": "Asignación de alarma", - "alarm-comment": "Comentario de alarma", - "api-usage-limit": "Límite en uso de API", - "device-activity": "Actividad de dispositivo", - "entities-limit": "Límites de entidades", - "entity-action": "Actiones de entidades", - "rule-engine-lifecycle-event": "Evento de ciclo de vida en Motor de reglas", - "new-platform-version": "Nueva versión de plataforma", - "rate-limits": "Se excedieron los límites de tasa", - "trigger": "Disparo", - "trigger-required": "Se requiere disparo" - }, - "type": "Tipo", - "unread": "No leído", - "updated": "Actualizado", - "use-template": "Usar plantilla", - "view-all": "Ver todos", - "warning": "Alerta", - "webhook-url": "URL Webhook", - "webhook-url-required": "Se requiere URL Webhook", - "channel-name": "Nombre del canal", - "channel-name-required": "Se requiere nombre del canal", - "settings": { - "notification-settings": "Ajustes de notificaciones", - "reset-all": "Restablecer ajustes", - "reset-all-title": "Estás seguro de borrar el formulario?", - "reset-all-text": "Atención, tras la confirmación, el formulario de ajustes se restablecerá a los valores por defecto.", - "type": "Tipo", - "enable-all": "Activar todos", - "disable-all": "Desactivar todos", - "delivery-not-configured": "No hay configurado un método de entrega" - } + "power-button": { + "behavior": "Comportamiento", + "power-on": "Encender", + "power-on-hint": "Acción realizada para encender el componente.", + "power-off": "Apagar", + "power-off-hint": "Acción realizada para apagar el componente.", + "on-label": "Encendido", + "off-label": "Apagado", + "layout": "Diseño", + "layout-default": "Predeterminado", + "layout-simplified": "Simplificado", + "layout-outlined": "Contorneado", + "layout-default-volume": "Predeterminado.Volumen", + "layout-simplified-volume": "Simplificado.Volumen", + "layout-outlined-volume": "Contorneado.Volumen", + "layout-default-icon": "Predeterminado.Icono", + "layout-simplified-icon": "Simplificado.Icono", + "layout-outlined-icon": "Contorneado.Icono", + "main": "Principal", + "background": "Fondo", + "button-icon-on": "Icono del botón 'Encendido'", + "button-icon-off": "Icono del botón 'Apagado'", + "power-on-colors": "Colores de encendido", + "power-off-colors": "Colores de apagado", + "disabled-colors": "Colores deshabilitados", + "button": "Botón" }, - "ota-update": { - "add": "Añadir paquete", - "assign-firmware": "Firmware asignado", - "assign-firmware-required": "Firmware asignado requerido", - "assign-software": "Software asignado", - "assign-software-required": "Software asignado requerido", - "auto-generate-checksum": "Auto-generar checksum", - "checksum": "Checksum", - "checksum-hint": "Si el checksum está vacío, se generará automáticamente", - "checksum-algorithm": "Algoritmo checksum", - "checksum-copied-message": "El checksum del paquete se ha copiado al portapapeles", - "change-firmware": "El cambio del firmware provocará la actualización de { count, plural, =1 {1 dispositivo} other {# dispositivos} }.", - "change-software": "El cambio del software provocará la actualización de { count, plural, =1 {1 dispositivo} other {# dispositivos} }.", - "chose-compatible-device-profile": "El paquete subido, sólamente estará disponible para los dispositivos con el perfil seleccionado.", - "chose-firmware-distributed-device": "Elige el firmware que se distribuirá a los dispositivos", - "chose-software-distributed-device": "Elige el software que se distribuirá a los dispositivos", - "content-type": "Tipo de contenido", - "copy-checksum": "Copiar checksum", - "copy-direct-url": "Copiar URL directa", - "copyId": "Copiar Id de paquete", - "copied": "Copiado!", - "delete": "Borrar paquete", - "delete-ota-update-text": "Atención, tras la confirmación la actualización OTA será irrecuperable.", - "delete-ota-update-title": "Estás seguro de borrar la actualización OTA '{{title}}'?", - "delete-ota-updates-text": "Atención, tras la confirmación todas las actualizaciones OTA seleccionadas se borrarán.", - "delete-ota-updates-title": "Estás seguro de borrar { count, plural, =1 {1 Actualización OTA} other {# Actualizaciones OTA} }?", - "description": "Descripción", - "direct-url": "URL Directa", - "direct-url-copied-message": "La URL directa del paquete se ha copiado al portapapeles", - "direct-url-required": "URL Directa requerida", - "download": "Descargar paquete", - "drop-file": "Arrastra un fichero o haz click para seleccionar un fichero a subir.", - "drop-package-file-or": "Arrastrar y soltar un fichero o", - "file-name": "Nombre del fichero", - "file-size": "Tamaño del fichero", - "file-size-bytes": "Tamaño del fichero en bytes", - "idCopiedMessage": "El Id del paquete se ha copiado al portapapeles", - "no-firmware-matching": "No se ha encontrado ningún paquete de firmware OTA compatible que coincidan con '{{entity}}'.", - "no-firmware-text": "No hay aprovisionado ningún paquete de firmware OTA.", - "no-packages-text": "No se han encontrado paquetes", - "no-software-matching": "No se ha encontrado ningún paquete de software OTA compatible que coincidan con '{{entity}}'.", - "no-software-text": "No hay aprovisionado ningún paquete de software OTA.", - "ota-update": "Actualización OTA", - "ota-update-details": "Detalles de actualización OTA", - "ota-updates": "Actualizaciones OTA", - "package-type": "Tipo de paquete", - "packages-repository": "Repositorio de paquetes", - "search": "Buscar paquetes", - "selected-package": "{ count, plural, =1 {1 paquete} other {# paquetes} } selected", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256", - "types": { - "firmware": "Firmware", - "software": "Software" - }, - "upload-binary-file": "Subir fichero binario", - "use-external-url": "Usar una URL externa", - "version": "Versión", - "version-required": "Versión requerida.", - "version-tag": "Tag de Versión", - "version-tag-hint": "El tag de versión debe coincidir con la versión reportada por el dispositivo.", - "version-max-length": "Versión debe ser menor que 256", - "warning-after-save-no-edit": "Una vez que el paquete se haya subido, no se podrá modificar el título, versión, perfil de dispositivo y tipo de paquete." + "toggle-button": { + "behavior": "Comportamiento", + "checked": "Seleccionado", + "unchecked": "No seleccionado", + "check": "Seleccionar", + "check-hint": "Acción realizada para seleccionar el componente.", + "uncheck": "Deseleccionar", + "uncheck-hint": "Acción realizada para deseleccionar el componente.", + "auto-scale": "Escalado automático", + "horizontal-fill": "Relleno horizontal", + "vertical-fill": "Relleno vertical", + "button-appearance": "Apariencia del botón" }, - "position": { - "top": "Superior", - "bottom": "Inferior", - "left": "Izquierda", - "right": "Derecha" + "segmented-button": { + "layout": "Diseño", + "layout-squared": "Cuadrado", + "layout-rounded": "Redondeado", + "card-border": "Borde de la tarjeta", + "button-appearance": "Apariencia del botón", + "first": "Primero", + "second": "Segundo", + "color-styles": "Estilos de color", + "selected": "Seleccionado", + "unselected": "No seleccionado" }, - "profile": { - "profile": "Perfil", - "last-login-time": "Último acceso", - "change-password": "Cambiar contraseña", - "current-password": "Contraseña actual", - "copy-jwt-token": "Copiar JWT", - "jwt-token": "Tóken JWT", - "token-valid-till": "Válido hasta {{expirationData}}", - "tokenCopiedSuccessMessage": "JWT copiado al portapapeles", - "tokenCopiedWarnMessage": "JWT caducado, por favor actualiza la página." + "button": { + "layout": "Diseño", + "outlined": "Contorneado", + "filled": "Relleno", + "underlined": "Subrayado", + "basic": "Básico", + "auto-scale": "Escalado automático", + "label": "Etiqueta", + "icon": "Icono", + "border-radius": "Radio del borde", + "color-palette": "Paleta de colores", + "main": "Principal", + "background": "Fondo", + "border": "Borde", + "custom-styles": "Estilos personalizados", + "clear-style": "Borrar estilo", + "shadow": "Sombra", + "enabled": "Habilitado", + "disabled": "Deshabilitado", + "preview": "Vista previa", + "copy-style-from": "Copiar estilo desde" }, - "profiles": { - "profiles": "Perfiles" + "value-stepper": { + "behavior": "Comportamiento", + "simplified": "Simplificado", + "filled": "Relleno", + "outlined": "Contorneado", + "volume": "Volumen", + "initial-state": "Estado inicial", + "initial-state-hint": "Acción para obtener el valor inicial.", + "disabled-state": "Estado deshabilitado", + "disabled-state-hint": "Configurar condición bajo la cual el componente está deshabilitado.", + "right-button-click": "Clic en botón derecho", + "right-button-click-hint": "Acción al presionar el botón derecho.", + "left-button-click": "Clic en botón izquierdo", + "left-button-click-hint": "Acción al presionar el botón izquierdo.", + "auto-scale": "Escalado automático", + "value-range": "Rango", + "min-range": "Mínimo", + "max-range": "Máximo", + "value-increment-decrement-step": "Paso de incremento/decremento de valor", + "value": "Valor", + "value-box-background": "Fondo del cuadro de valor", + "border": "Borde", + "button-appearance": "Apariencia del botón", + "left": "Izquierda", + "right": "Derecha", + "left-button": "Botón izquierdo", + "right-button": "Botón derecho", + "icon": "Icono", + "color-palette": "Paleta de colores", + "main": "Principal", + "background": "Fondo", + "button-icon-on": "Icono del botón 'Encendido'", + "button-on-colors": "Colores de 'Encendido'", + "disabled-colors": "Colores deshabilitados" }, - "security": { - "security": "Seguridad", - "general-settings": "Ajustes generales de seguridad", - "access-token": "Tóken de acceso", - "access-token-required": "Tóken de acceso requerido.", - "clientId": "ID cliente", - "clientId-required": "Se requiere ID cliente", - "username": "Usuario", - "username-required": "Se requiere usuario", - "ca-cert": "Certificado CA", - "2fa": { - "2fa": "Autenticación de doble factor (2FA)", - "2fa-description": "La autenticación de doble factor proteje tu cuenta de accesos no autorizados. Lo único que tienes que hacer es entrar un código de seguridad al hacer login.", - "authenticate-with": "Puedes autenticarte con:", - "disable-2fa-provider-text": "Desactivar {{name}} hará tu cuenta menos segura", - "disable-2fa-provider-title": "Estás seguro de desactivar {{name}}?", - "get-new-code": "Obtener un nuevo código", - "main-2fa-method": "Usar como método de autenticación de doble factor principal", - "dialog": { - "activation-step-description-email": "La próxima vez que hagas log-in, se deberá introducir el código de seguridad que se enviará a tu dirección de email.", - "activation-step-description-sms": "La próxima vez que hagas log-in, se deberá introducir el código de seguridad que se enviará a tu número de teléfono.", - "activation-step-description-totp": "La próxima vez que hagas log-in, se deberá introducir el código de seguridad de doble factor.", - "activation-step-label": "Activación", - "backup-code-description": "Imprime los códigos y guárdalos en un lugar seguro, se necesitarán para hacer login en tu cuenta. Puedes usar cada código de backup sólo una vez.", - "backup-code-warn": "Una vez que salgas de esta página, estos códigos no se volverán a mostrar. Guárdalos en un lugar seguro usando las opciones a continuación.", - "download-txt": "Descargar (txt)", - "email-step-description": "Introduce tu email para usar como autenticador.", - "email-step-label": "Email", - "enable-email-title": "Activar autenticador por email", - "enable-sms-title": "Activar autenticador por SMS", - "enable-totp-title": "Activar autenticador por app", - "enter-verification-code": "Entra el código de 6-dígitos", - "get-backup-code-title": "Obtener copia de seguridad", - "next": "Siguiente", - "scan-qr-code": "Escanea el código QR con tu aplicación de autenticación", - "send-code": "Enviar código", - "sms-step-description": "Introduce tu número de teléfono para usar como autenticador.", - "sms-step-label": "Número de teléfono", - "success": "Éxito!", - "totp-step-description-install": "Puedes instalar la aplicación Google Authenticator, Authy, o Duo.", - "totp-step-description-open": "Abre la aplicación de autenticación en tu teléfono móvil.", - "totp-step-label": "Obtener app", - "verification-code": "Código de 6-dígitos", - "verification-code-invalid": "Formáto de código inválido", - "verification-code-incorrect": "El código de verificación es incorrecto", - "verification-code-many-request": "Demasiadas solicitudes, revisa el código de verificación", - "verification-step-description": "Introduce el código de 6-dígitos que acabamos de enviar a {{address}}", - "verification-step-label": "Verificación" - }, - "provider": { - "email": "Email", - "email-description": "Usar un código de seguridad enviado a tu email para autenticarte.", - "email-hint": "Los códigos de autenticación se han enviado por email a {{ info }}", - "sms": "SMS", - "sms-description": "Usar tu teléfono para autenticarte. Enviaremos un código de seguridad vía SMS.", - "sms-hint": "Los códigos de autenticación se han enviado por mensaje de texto SMS a {{ info }}", - "totp": "App de autenticación", - "totp-description": "Usar aplicaciones como Google Authenticator, Authy, o Duo en tu teléfono para autenticarte. Se generará un código de seguridad para hacer login.", - "totp-hint": "La aplicación de autenticación se ha configurado en tu cuenta", - "backup_code": "Código Backup", - "backup-code-description": "Estos códigos de seguridad imprimibles son de un solo uso, te permiten identificarte cuando no tengas el teléfono a mano, útil cuando se viaja.", - "backup-code-hint": "{{ info }} códigos de un solo uso activos en este momento" - } - }, - "password-requirement": { - "at-least": "Al menos:", - "character": "{ count, plural, =1 {1 caracter} other {# caracteres} }", - "digit": "{ count, plural, =1 {1 dígito} other {# dígitos} }", - "incorrect-password-try-again": "Contraseña incorrecta. Prueba otra vez", - "lowercase-letter": "{ count, plural, =1 {1 minúscula} other {# mínusculas} }", - "new-passwords-not-match": "Las contraseñas no coinciden", - "password-should-not-contain-spaces": "La contraseña no puede contener espacios", - "password-not-meet-requirements": "La contraseña no reune los requisitos necesarios", - "password-requirements": "Requisitos de contraseña", - "password-should-difference": "La nueva contraseña debe ser diferente a la actual", - "special-character": "{ count, plural, =1 {1 caracter especial} other {# caracteres especiales} }", - "uppercase-letter": "{ count, plural, =1 {1 mayúscula} other {# mayúsculas} }" - } + "button-state": { + "activated-state": "Estado activado", + "activated-state-hint": "Configurar condición bajo la cual el botón está activo.", + "disabled-state": "Estado deshabilitado", + "disabled-state-hint": "Configurar condición bajo la cual el botón está deshabilitado.", + "selected-state": "Seleccionar botón", + "selected-state-hint": "Configurar condición bajo la cual el botón está seleccionado.", + "enabled": "Habilitado", + "hovered": "Con cursor encima", + "pressed": "Presionado", + "activated": "Activado", + "disabled": "Deshabilitado", + "initial": "Primer botón", + "first": "Primero", + "second": "Segundo" }, - "relation": { - "relations": "Relaciones", - "direction": "Dirección", - "clear-relation-type": "Borrar tipo de relación", - "search-direction": { - "FROM": "Desde", - "TO": "Hacia" - }, - "direction-type": { - "FROM": "desde", - "TO": "hacia" - }, - "from-relations": "Relaciones salientes (outbound)", - "to-relations": "Relaciones entrantes (inbound)", - "selected-relations": "{ count, plural, =1 {1 relación} other {# relaciones} } seleccionadas", - "type": "Tipo", - "to-entity-type": "Hacia tipo de entidad", - "to-entity-name": "Hacia nombre de entidad", - "from-entity-type": "Desde tipo de entidad", - "from-entity-name": "Desde nombre de entidad", - "to-entity": "Hacia entidad", - "from-entity": "Desde entidad", - "delete": "Borrar relación", - "relation-type": "Tipo de relación", - "relation-type-required": "Tipo de relación requerido.", - "relation-type-max-length": "Tipo de relación debe ser menor de 256", - "any-relation-type": "Cualquier tipo", - "add": "Añadir relación", - "edit": "Editar relación", - "delete-to-relation-title": "¿Quieres eliminar la relación con la entidad '{{entityName}}'?", - "delete-to-relation-text": "Atención, tras la confirmación la entidad '{{entityName}}' no estará relacionada con la entidad actual.", - "delete-to-relations-title": "¿Quieres eliminar { count, plural, =1 {1 relación} other {# relaciones} }?", - "delete-to-relations-text": "Atención, tras la confirmación todas las relaciones seleccionadas se eliminarán y sus entidades correspondientes no estarán relacionadas con la entidad actual.", - "delete-from-relation-title": "¿Quieres eliminar la relación con la entidad '{{entityName}}'?", - "delete-from-relation-text": "Atención, tras la confirmación la entidad actual no estará relacionada con la entidad '{{entityName}}'.", - "delete-from-relations-title": "¿Quieres eliminar { count, plural, =1 {1 relación} other {# relaciones} }?", - "delete-from-relations-text": "Atención, tras la confirmación todas las relaciones seleccionadas se eliminarán y sus entidades correspondientes no estarán relacionadas con sus entidades correspondientes.", - "remove-relation-filter": "Borrar filtro de relación", - "remove-filter": "Borrar filtro", - "add-relation-filter": "Añadir filtro de relación", - "any-relation": "Cualquier relación", - "relation-filters": "Filtro de relación", - "additional-info": "Información adicional (JSON)", - "invalid-additional-info": "Error al analizar el fichero JSON de información adicional.", - "no-relations-text": "No se encontraron relaciones" + "background": { + "background": "Fondo", + "background-settings": "Configuración de fondo", + "background-type-image": "Imagen", + "background-type-color": "Color", + "image-url": "URL de imagen", + "overlay": "Superposición", + "enable-overlay": "Habilitar superposición", + "blur": "Desenfoque", + "preview": "Vista previa" }, - "resource": { - "add": "Añadir Recurso", - "all-types": "Todos", - "copyId": "Copiar Id de recurso", - "delete": "Borrar recurso", - "delete-resource-text": "Atención, tras la confirmación el recurso será irrecuperable.", - "delete-resource-title": "Estás seguro de borrar el recurso '{{resourceTitle}}'?", - "delete-resources-action-title": "Borrar { count, plural, =1 {1 recurso} other {# recursos} }", - "delete-resources-text": "Los recursos serán borrados, incluso si están siendo usados en los perfiles de dispositivo.", - "delete-resources-title": "Estás seguro de borrar { count, plural, =1 {1 recurso} other {# recursos} }?", - "download": "Descargar recurso", - "drop-file": "Arrastra un fichero o haz click para seleccionar un fichero a subir.", - "drop-resource-file-or": "Arrastrar y soltar un fichero o", - "empty": "El recurso está vacío", - "file-name": "Nombre de fichero", - "idCopiedMessage": "El Id de recurso ha sido copiado al portapapeles", - "no-resource-matching": "No se han encontrado recursos que coincidan con '{{widgetsBundle}}'.", - "no-resource-text": "No se encontraron recursos", - "open-widgets-bundle": "Abrir paquete de widgets", - "resource": "Recurso", - "resource-library-details": "Detalles de recurso", - "resource-type": "Tipo de recurso", - "resources-library": "Librería de recursos", - "search": "Buscar recursos", - "selected-resources": "{ count, plural, =1 {1 recurso} other {# recursos} } seleccionados", - "system": "Sistema", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256", - "type": { - "jks": "JKS", - "js-module": "Módulo JS", - "lwm2m-model": "Modelo LWM2M", - "pkcs-12": "PKCS #12" - } + "bar-chart": { + "bar-appearance": "Apariencia de la barra", + "label-on-bar": "Etiqueta en la barra", + "value-on-bar": "Valor en la barra", + "bar-chart-style": "Estilo de gráfico de barras", + "bar-axis": "Eje de barra" }, - "rulechain": { - "rulechain": "Cadena de Regla", - "rulechain-events": "Eventos de cadena de reglas", - "rulechains": "Cadenas de Reglas", - "root": "Raíz", - "delete": "Borrar cadena de reglas", - "name": "Nombre", - "name-required": "Nombre requerido.", - "name-max-length": "Nombre debe ser menor de 256", - "description": "Descripción", - "add": "Añadir Cadena", - "set-root": "Hacer la cadena de reglas Raíz", - "set-root-rulechain-title": "¿Desea hacer la cadena de reglas '{{ruleChainName}}' de tipo raíz?", - "set-root-rulechain-text": "Tras la confirmación, la cadena de reglas se volverá raíz y manejará todos los mensajes de transporte entrantes.", - "delete-rulechain-title": "¿Quieres eliminar la cadena de reglas '{{ruleChainName}}'?", - "delete-rulechain-text": "Atención, tras la confirmación la cadena de reglas y todos los datos serán irrecuperables.", - "delete-rulechains-title": "¿Está seguro que quieres eliminar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }?", - "delete-rulechains-action-title": "Eliminar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }", - "delete-rulechains-text": "Atención, tras la confirmación todas las cadena de reglas seleccionadas y todos sus datos serán irrecuperables.", - "add-rulechain-text": "Añadir nueva cadena de reglas", - "no-rulechains-text": "Cadenas de reglas no encontradas", - "rulechain-details": "Detalles de la cadena de reglas", - "details": "Detalles", - "events": "Eventos", - "system": "Sistema", - "import": "Importar cadena de reglas", - "export": "Exportar cadena de reglas", - "export-failed-error": "No se puede exportar la cadena de reglas: {{error}}", - "create-new-rulechain": "Crear nueva cadena de reglas", - "rulechain-file": "Fichero de cadena de reglas", - "invalid-rulechain-file-error": "No se puede importar la cadena de reglas: Estructura de datos de la cadena de reglas inválida.", - "copyId": "Copiar ID de la cadena de reglas", - "idCopiedMessage": "ID de la cadena de reglas ha sido copiada al portapapeles", - "select-rulechain": "Seleccionar cadena de reglas", - "no-rulechains-matching": "No se encontraron cadenas de reglas que coincidan con '{{entity}}' .", - "rulechain-required": "Cadena de reglas requerida", - "management": "Gestión de reglas", - "debug-mode": "Modo Debug", - "search": "Buscar cadenas de reglas", - "selected-rulechains": "{ count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} } seleccionadas", - "open-rulechain": "Abrir cadena de reglas", - "edge-template-root": "Raíz de plantilla", - "assign-to-edge": "Asignar a Edge", - "edge-rulechain": "Cadena de reglas de Edge", - "unassign-rulechain-from-edge-text": "Después de la confirmación, la cadena de reglas quedará sin asignar y el Edge no podrá acceder a ella", - "unassign-rulechains-from-edge-title": "Estás seguro de desasignar { count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} }?", - "unassign-rulechains-from-edge-text": "Después de la confirmación, todas las cadenas de reglas seleccionadas quedarán sin asignar y el Edge no podrá acceder a ellas", - "assign-rulechain-to-edge-title": "Asignar cadena (s) de reglas a Edge", - "assign-rulechain-to-edge-text": "Seleccione las cadenas de reglas para asignar al Edge", - "set-edge-template-root-rulechain": "Hacer raíz de plantilla de Edge de cadena de reglas", - "set-edge-template-root-rulechain-title": "¿Está seguro de que desea que la cadena de reglas '{{ruleChainName}}' sea la raíz de la plantilla de Edge?", - "set-edge-template-root-rulechain-text": "Después de la confirmación, la cadena de reglas se convertirá en la raíz de la plantilla de Edge y será la cadena de reglas raíz para los Edges recién creados.", - "invalid-rulechain-type-error": "No se puede importar la cadena de reglas: Tipo de cadena de reglas no válido. El tipo esperado es {{expectedRuleChainType}}", - "set-auto-assign-to-edge": "Asignar cadena de reglas a los Edges en la creación", - "set-auto-assign-to-edge-title": "¿Está seguro de que desea asignar automáticamente la cadena de reglas de Edge '{{ruleChainName}}' a los Edges en la creación?", - "set-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de Edge se asignará automáticamente a los Edges en la creación.", - "unset-auto-assign-to-edge": "Desmarcar asignar cadena de reglas a los Edges en la creación", - "unset-auto-assign-to-edge-title": "¿Está seguro de que desea anular la asignación de la cadena de reglas de Edge '{{ruleChainName}}' a los Edges en la creación?", - "unset-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de Edge ya no se asignará automáticamente a los Edges en la creación.", - "unassign-rulechain-title": "¿Está seguro de que desea desasignar la cadena de reglas '{{ruleChainName}}'?", - "unassign-rulechains": "Anular asignación de cadenas de reglas" + "polar-area-chart": { + "polar-axis": "Eje polar", + "start-angle": "Ángulo de inicio", + "polar-area-chart-style": "Estilo de gráfico de área polar" }, - "rulenode": { - "rule-node-events": "Eventos de nodo de reglas", - "details": "Detalles", - "events": "Eventos", - "search": "Buscar nodos", - "open-node-library": "Abrir librería de nodos", - "close-node-library": "Cerrar librería de nodos", - "add": "Añadir nodo de reglas", - "name": "Nombre", - "name-required": "El nombre es requerido.", - "name-max-length": "Nombre debe ser menor de 256", - "type": "Tipo", - "rule-node-description": "Descripción de nodo de reglas", - "delete": "Eliminar nodo de reglas", - "select-all-objects": "Seleccionar todos los nodos y conexiones", - "deselect-all-objects": "Deshacer selección de todos los nodos y conexiones", - "delete-selected-objects": "Eliminar nodos y conexiones seleccionados", - "delete-selected": "Eliminar seleccionado", - "create-nested-rulechain": "Crear cadena de reglas anidada", - "select-all": "Seleccionar todos", - "copy-selected": "Copiar seleccionado", - "deselect-all": "Deshacer selección de todos", - "rulenode-details": "Detalles del nodo de reglas", - "debug-mode": "Modo Debug", - "configuration": "Configuración", - "link": "Enlace", - "link-details": "Detalles del enlace del nodo de reglas", - "add-link": "Agregar enlace", - "link-label": "Etiqueta del enlace", - "link-label-required": "Etiqueta del enlace es requerida.", - "custom-link-label": "Etiqueta del enlace personalizada", - "custom-link-label-required": "Etiqueta del enlace personalizado es requerida.", - "link-labels": "Etiquetas del enlace", - "link-labels-required": "Etiquetas del enlace son requeridas.", - "no-link-labels-found": "Etiquetas de enlaces no encontradas", - "no-link-label-matching": "'{{label}}' no encontrada.", - "create-new-link-label": "Crear una nueva!", - "type-filter": "Filtro", - "type-filter-details": "Filtrar mensajes entrantes con las condiciones configuradas", - "type-enrichment": "Enriquecimiento", - "type-enrichment-details": "Agregar información adicional en mensajes de metadatos", - "type-transformation": "Transformación", - "type-transformation-details": "Cambiar carga útil del Mensaje y Metadatos", - "type-action": "Acción", - "type-action-details": "Ejecutar acción especial", - "type-external": "Externo", - "type-external-details": "Interactuar con sistemas externos", - "type-rule-chain": "Cadena de reglas", - "type-rule-chain-details": "Reenvíar los mensajes entrantes a la cadena de reglas especificada", - "type-flow": "Flujo", - "type-flow-details": "Organiza el flujo de mensajes", - "type-input": "Entrada", - "type-input-details": "Entrada lógica de la Cadena de Reglas, reenvíar los mensajes entrantes al siguiente nodo de regla relacionado.", - "type-unknown": "Desconocido", - "type-unknown-details": "Regla de nodo no resuelta", - "directive-is-not-loaded": "La directiva de configuración definida '{{directiveName}}' no está disponible.", - "ui-resources-load-error": "Error al cargar los recursos de configuración UI.", - "invalid-target-rulechain": "No se puede resolver la cadena de reglas objetivo!", - "test-script-function": "Probar Script de función", - "script-lang-java-script": "JavaScript", - "script-lang-tbel": "TBEL", - "message": "Mensaje", - "message-type": "Tipo de mensaje", - "select-message-type": "Seleccionar tipo de mensaje", - "message-type-required": "Tipo de mensaje es requerido", - "metadata": "Metadatos", - "metadata-required": "La entradas de metadatos no pueden estar vacías.", - "output": "Salida", - "test": "Test", - "help": "Ayuda", - "test-with-this-message": "{{test}} con este mensaje" + "battery-level": { + "layout": "Disposición", + "layout-vertical-solid": "Vertical. Sólido", + "layout-horizontal-solid": "Horizontal. Sólido", + "layout-vertical-divided": "Vertical. Dividido", + "layout-horizontal-divided": "Horizontal. Dividido", + "icon": "Icono", + "value": "Valor", + "auto-scale": "Escalado automático", + "battery-level-color": "Color del nivel de batería", + "battery-shape-color": "Color de la forma de la batería", + "battery-level-card-style": "Estilo de tarjeta de nivel de batería", + "sections-count": "Número de secciones" }, - "timezone": { - "timezone": "Zona Horaria", - "select-timezone": "Seleccionar zona horaria", - "no-timezones-matching": "No hay zonas horarias que coincidan con '{{timezone}}'.", - "timezone-required": "Se requiere zona horaria.", - "browser-time": "Hora del navegador" + "signal-strength": { + "value": "Valor", + "last-update": "Última actualización", + "no-signal": "Sin señal", + "layout": "Disposición", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "Barra celular", + "icon": "Icono", + "date": "Fecha", + "active-bars-color": "Color de barras activas", + "inactive-bars-color": "Color de barras inactivas", + "signal-strength-card-style": "Estilo de tarjeta de intensidad de señal", + "no-signal-rssi-value": "Valor RSSI de \"Sin señal\"" }, - "queue": { - "queue-name": "Cola", - "no-queues-found": "No se encontraron colas.", - "no-queues-matching": "No se encontraron colas que coincidan con '{{queue}}'.", - "select-name": "Selecciona el nombre de la cola", - "name": "Nombre Cola", - "name-required": "Necesario especificar el nombre de cola", - "name-unique": "El nombre de cola ya existe!", - "name-pattern": "El nombre de cola contiene un carácter no ASCII, '.', '_' y '-'!", - "queue-required": "Cola requerida!", - "topic-required": "Topic cola requerido!", - "poll-interval-required": "Intervalo de obtención requerido!", - "poll-interval-min-value": "El intervalo no debe ser menor de 1", - "partitions-required": "Particiones requeridas!", - "partitions-min-value": "El valor de particion no debe ser menor de 1", - "pack-processing-timeout-required": "Timeout de procesamiento", - "pack-processing-timeout-min-value": "Timeout de procesamiento no puede ser menor de 1", - "batch-size-required": "Tamaño del lote requerido!", - "batch-size-min-value": "El valor de tamaño de lote no puede ser menor de 1", - "retries-required": "Reintentos requerido!", - "retries-min-value": "El valor de reintentos no puede ser negativo", - "failure-percentage-required": "Porcentaje de fallos requerido!", - "failure-percentage-min-value": "Porcentaje de fallos no puede ser menor de 0", - "failure-percentage-max-value": "Porcentaje de fallos no puede ser mayor de 100", - "pause-between-retries-required": "Pausa entre reintentos requerido!", - "pause-between-retries-min-value": "Pausa mínima entre reintentos no puede ser menor de 1", - "max-pause-between-retries-required": "Pausa máxima entre reintentos requerido!", - "max-pause-between-retries-min-value": "Pausa máxima entre reintentos no puede ser menor de 1", - "submit-strategy-type-required": "Estrategia de envío requerida!", - "processing-strategy-type-required": "Estrategia de procesamiento requerida!", - "queues": "Colas", - "selected-queues": "{ count, plural, =1 {1 cola} other {# colas} } seleccionadas", - "delete-queue-title": "Estás seguro de borrar la cola '{{queueName}}'?", - "delete-queues-title": "Estás seguro de borrar { count, plural, =1 {1 cola} other {# colas} }?", - "delete-queue-text": "Atención, tras la confirmacion la cola y todos sus datos relacionados serán irrecuperables.", - "delete-queues-text": "Atención, tras la confirmación todas las colas se borrarán y no serán accesibles.", - "search": "Buscar cola", - "add" : "Añadir cola", - "details": "Detalles cola", - "topic": "Topic", - "submit-settings": "Ajustes de envío", - "submit-strategy": "Estrategia de envío", - "grouping-parameter": "Parámetros de agrupado", - "processing-settings": "Ajustes de procesamiento", - "processing-strategy": "Estrategia de procesamiento", - "retries-settings": "Ajustes de reintentos", - "polling-settings": "Ajustes de obtención", - "batch-processing": "Procesado de lotes", - "poll-interval": "Intervalo de obtención", - "partitions": "Particiones", - "immediate-processing": "Procesado inmediato", - "consumer-per-partition": "Consumidores por partición", - "consumer-per-partition-hint": "Activar consumidores separados para cada partición", - "processing-timeout": "Timeout de procesamiento, ms", - "batch-size": "Tamaño de lote", - "retries": "Reintentos (0 - ilimitados)", - "failure-percentage": "Porcentaje de fallos", - "pause-between-retries": "Pausa entre reintentos", - "max-pause-between-retries": "Pausa máxima entre reintentos", - "delete": "Borrar cola", - "copyId": "Copiar Id de cola", - "idCopiedMessage": "La Id de cola se ha copiado al portapapeles", - "description": "Descripción", - "description-hint": "Este texto se mostrará en la descripción de la cola, en lugar de la estrategia seleccionada", - "alt-description": "Estrategia de envío: {{submitStrategy}}, Estrategia de procesamiento: {{processingStrategy}}", - "custom-properties": "Propiedades personalizadas", - "custom-properties-hint": "Propiedades de creación de tema en colas personalizadas, p.e. 'retention.ms:604800000;retention.bytes:1048576000'", - "strategies": { - "sequential-by-originator-label": "Secuencial por iniciador", - "sequential-by-originator-hint": "El nuevo mensaje por ejemplo dispositivo A, no se enviará hasta que el mensaje anterior del dispositivo A sea admitido/procesado", - "sequential-by-tenant-label": "Secuencial por propietario", - "sequential-by-tenant-hint": "El nuevo mensaje, por ejemplo Propietario A, no se enviará hasta que el mensaje anterior del Propietario A sea admitido/procesado", - "sequential-label": "Secuencial", - "sequential-hint": "El nuevo mensaje no se enviará hasta que el mensaje anterior sea admitido/procesado", - "burst-label": "Ráfaga (Burst)", - "burst-hint": "Todos los mensajes se enviarán hacia las cadenas de reglas en el órden que lleguen", - "batch-label": "Lotes (Batch)", - "batch-hint": "El nuevo lote, no se enviará hasta que el anterior lote sea admitido/procesado", - "skip-all-failures-label": "Omitir todos los fallos", - "skip-all-failures-hint": "Ignorar todos los fallos", - "skip-all-failures-and-timeouts-label": "Omitir todos los fallos y timeouts", - "skip-all-failures-and-timeouts-hint": "Ignorar todos los fallos y timeouts", - "retry-all-label": "Reintentar todos", - "retry-all-hint": "Reintentar todos los mensajes del lote de procesamiento", - "retry-failed-label": "Reintentar fallidos", - "retry-failed-hint": "Reintentar todos los mensajes fallidos del lote de procesamiento", - "retry-timeout-label": "Timeout de reintentos", - "retry-timeout-hint": "Reintentar todos los mensajes que den timeout del lote de procesamiento", - "retry-failed-and-timeout-label": "Reintentar fallidos y timeouts", - "retry-failed-and-timeout-hint": "Reintentar todos los mensajes fallidos y que den timeout del lote de procesamiento" - } + "status-widget": { + "behavior": "Comportamiento", + "layout": "Disposición", + "layout-default": "Por defecto", + "layout-center": "Centro", + "layout-icon": "Icono", + "on": "Encendido", + "off": "Apagado", + "label": "Etiqueta", + "status": "Estado", + "icon": "Icono", + "color-palette": "Paleta de colores", + "disabled-color-palette": "Paleta de colores deshabilitada", + "primary": "Primario", + "primary-color-hint": "Color del icono y la etiqueta", + "secondary": "Secundario", + "secondary-color-hint": "Color del estado", + "background": "Fondo" }, - "server-error": { - "general": "Error general de servidor", - "authentication": "Error de autenticación", - "jwt-token-expired": "Token JWT expirado", - "tenant-trial-expired": "Prueba propietario expirada", - "credentials-expired": "Credenciales expirados", - "permission-denied": "Permiso denegado", - "invalid-arguments": "Argumentos inválidos", - "bad-request-params": "Parámetros de consulta no válidos", - "item-not-found": "Item no encontrado", - "too-many-requests": "Demasiadas solicitudes", - "too-many-updates": "Demasiadas actualizaciones" + "chart": { + "common-settings": "Configuraciones comunes", + "enable-stacking-mode": "Habilitar modo de apilamiento", + "selection": "Selección de rango de tiempo", + "enable-selection-mode": "Habilitar modo de selección", + "line-shadow-size": "Tamaño de sombra de línea", + "display-smooth-lines": "Mostrar líneas suaves (curvas)", + "default-bar-width": "Ancho de barra por defecto para datos no agregados (milisegundos)", + "bar-alignment": "Alineación de barras", + "bar-alignment-left": "Izquierda", + "bar-alignment-right": "Derecha", + "bar-alignment-center": "Centro", + "default-font": "Fuente por defecto", + "default-font-size": "Tamaño de fuente por defecto", + "default-font-color": "Color de fuente por defecto", + "thresholds-line-width": "Ancho de línea por defecto para todos los umbrales", + "tooltip-settings": "Configuraciones del tooltip", + "tooltip": "Tooltip", + "show-tooltip": "Mostrar tooltip", + "hover-individual-points": "Pasar el cursor sobre puntos individuales", + "show-cumulative-values": "Mostrar valores acumulados en modo apilado", + "hide-zero-false-values": "Ocultar valores cero/falsos del tooltip", + "tooltip-value-format-function": "Función de formato de valor del tooltip", + "grid-settings": "Configuraciones de cuadrícula", + "show-vertical-lines": "Mostrar líneas verticales", + "show-horizontal-lines": "Mostrar líneas horizontales", + "grid-outline-border-width": "Ancho del borde de la cuadrícula (px)", + "primary-color": "Color primario", + "background-color": "Color de fondo", + "ticks-color": "Color de las marcas", + "xaxis-settings": "Configuraciones del eje X", + "axis-title": "Título del eje", + "xaxis-tick-labels-settings": "Configuraciones de etiquetas de marcas del eje X", + "show-tick-labels": "Mostrar etiquetas del eje", + "yaxis-settings": "Configuraciones del eje Y", + "min-scale-value": "Valor mínimo en la escala", + "max-scale-value": "Valor máximo en la escala", + "yaxis-tick-labels-settings": "Configuraciones de etiquetas de marcas del eje Y", + "tick-step-size": "Tamaño del paso entre marcas", + "number-of-decimals": "Número de decimales a mostrar", + "ticks-formatter-function": "Función de formateo de marcas", + "comparison-settings": "Configuraciones de comparación", + "enable-comparison": "Habilitar comparación", + "time-for-comparison": "Periodo de comparación", + "time-for-comparison-previous-interval": "Intervalo anterior (por defecto)", + "time-for-comparison-days": "Hace un día", + "time-for-comparison-weeks": "Hace una semana", + "time-for-comparison-months": "Hace un mes", + "time-for-comparison-years": "Hace un año", + "time-for-comparison-custom-interval": "Intervalo personalizado", + "custom-interval-value": "Valor de intervalo personalizado (ms)", + "comparison-x-axis-settings": "Configuraciones del eje X para comparación", + "axis-position": "Posición del eje", + "axis-position-top": "Superior (por defecto)", + "axis-position-bottom": "Inferior", + "custom-legend-settings": "Configuraciones personalizadas de la leyenda", + "enable-custom-legend": "Habilitar leyenda personalizada (permite usar atributos/series temporales en etiquetas de clave)", + "key-name": "Nombre de la clave", + "key-name-required": "El nombre de la clave es obligatorio", + "key-type": "Tipo de clave", + "key-type-attribute": "Atributo", + "key-type-timeseries": "Serie temporal", + "label-keys-list": "Lista de claves para usar en etiquetas", + "no-label-keys": "No se configuraron claves", + "add-label-key": "Agregar nueva clave", + "line-width": "Ancho de línea", + "color": "Color", + "data-is-hidden-by-default": "Los datos están ocultos por defecto", + "disable-data-hiding": "Desactivar ocultamiento de datos", + "remove-from-legend": "Eliminar clave de la leyenda", + "exclude-from-stacking": "Excluir del apilamiento (disponible en modo \"Apilamiento\")", + "line-settings": "Configuraciones de línea", + "show-line": "Mostrar línea", + "fill-line": "Rellenar línea", + "fill-line-opacity": "Opacidad del relleno", + "points-settings": "Configuraciones de puntos", + "show-points": "Mostrar puntos", + "points-line-width": "Ancho de línea de los puntos", + "points-radius": "Radio de los puntos", + "point-shape": "Forma del punto", + "point-shape-circle": "Círculo", + "point-shape-cross": "Cruz", + "point-shape-diamond": "Diamante", + "point-shape-square": "Cuadrado", + "point-shape-triangle": "Triángulo", + "point-shape-custom": "Función personalizada", + "point-shape-draw-function": "Función de dibujo de forma de punto", + "show-separate-axis": "Mostrar eje separado", + "axis-position-left": "Izquierda", + "axis-position-right": "Derecha", + "thresholds": "Umbrales", + "no-thresholds": "No se configuraron umbrales", + "add-threshold": "Agregar umbral", + "show-values-for-comparison": "Mostrar valores históricos para comparación", + "comparison-values-label": "Etiqueta de valores históricos", + "comparison-line-color": "Color de la línea de comparación", + "threshold-settings": "Configuraciones de umbral", + "use-as-threshold": "Usar el valor de la clave como umbral", + "threshold-line-width": "Ancho de línea del umbral", + "threshold-color": "Color del umbral", + "common-pie-settings": "Configuraciones comunes del gráfico circular", + "radius": "Radio", + "inner-radius": "Radio interno", + "tilt": "Inclinación", + "common-pie-settings-range-error": "El valor debe estar entre 0 y 1", + "stroke-settings": "Configuraciones de contorno", + "width-pixels": "Ancho (píxeles)", + "show-labels": "Mostrar etiquetas", + "animation-settings": "Configuraciones de animación", + "animated-pie": "Habilitar animación del gráfico circular (experimental)", + "border-settings": "Configuraciones de borde", + "border-width": "Ancho del borde", + "border-color": "Color del borde", + "legend-settings": "Configuraciones de la leyenda", + "display-legend": "Mostrar leyenda", + "labels-font-color": "Color de fuente de las etiquetas", + "series": "Series", + "add-series": "Agregar serie", + "series-settings": "Configuraciones de la serie", + "remove-series": "Eliminar serie", + "no-series": "No se han configurado series", + "no-series-error": "Se debe especificar al menos una serie", + "chart-appearance": "Apariencia del gráfico", + "vertical-grid-lines": "Líneas de cuadrícula verticales", + "horizontal-grid-lines": "Líneas de cuadrícula horizontales", + "chart-background": "Fondo del gráfico", + "grid-lines-color": "Color de las líneas de cuadrícula", + "border": "Borde", + "axis": "Eje", + "vertical-axis": "Eje vertical", + "ticks": "Marcas", + "horizontal-axis": "Eje horizontal", + "shape-empty-circle": "Círculo vacío", + "shape-circle": "Círculo", + "shape-rect": "Rectángulo", + "shape-round-rect": "Rectángulo redondeado", + "shape-triangle": "Triángulo", + "shape-diamond": "Diamante", + "shape-pin": "Pin", + "shape-arrow": "Flecha", + "shape-none": "Ninguno", + "line-type-solid": "Sólido", + "line-type-dashed": "Discontinuo", + "line-type-dotted": "Punteado", + "label-position-top": "Arriba", + "label-position-bottom": "Abajo", + "label-position-outside": "Fuera", + "label-position-inside": "Dentro", + "fill": "Relleno", + "fill-type-none": "Ninguno", + "fill-type-solid": "Sólido", + "fill-type-opacity": "Opacidad", + "fill-type-gradient": "Gradiente", + "background": "Fondo", + "opacity": "Opacidad", + "gradient-stops": "Puntos de detención del gradiente", + "gradient-start": "inicio", + "gradient-end": "fin", + "animation": { + "animation": "Animación", + "animation-threshold": "Umbral de animación", + "animation-duration": "Duración de la animación", + "animation-easing": "Facilitación de la animación", + "animation-delay": "Retardo de la animación", + "update-animation-duration": "Duración de la animación de actualización", + "update-animation-easing": "Facilitación de la animación de actualización", + "update-animation-delay": "Retardo de la animación de actualización" + }, + "chart-axis": { + "scale": "Escala", + "scale-min": "mín", + "scale-max": "máx", + "scale-auto": "Auto" + }, + "bar": { + "show-border": "Mostrar borde", + "border-width": "Ancho del borde", + "border-radius": "Radio del borde", + "bar-width": "Ancho de la barra", + "label": "Etiqueta", + "label-hint": "Mostrar etiqueta sobre la barra.", + "series-label-hint": "Mostrar etiqueta con valor sobre la barra.", + "label-background": "Fondo de la etiqueta" + } }, - "tenant": { - "tenant": "Propietario", - "tenants": "Propietarios", - "management": "Gestión de Propietarios", - "add": "Agregar propietario", - "admins": "Admins", - "manage-tenant-admins": "Gestionar administradores de propietario", - "delete": "Eliminar propietario", - "add-tenant-text": "Agregar nuevo propietario", - "no-tenants-text": "Ningún propietario encontrado", - "tenant-details": "Detalles del propietario", - "title-max-length": "Título debe ser menor de 256", - "delete-tenant-title": "¿Quieres eliminar el propietario '{{tenantTitle}}'?", - "delete-tenant-text": "Atención, tras la confirmación el propietario será eliminado y la información relacionada será irrecuperable.", - "delete-tenants-title": "¿Quieres eliminar { count, plural, =1 {1 propietario} other {# propietarios} }?", - "delete-tenants-action-title": "Eliminar { count, plural, =1 {1 propietario} other {# propietarios} }", - "delete-tenants-text": "Atención, tras la confirmación los propietarios seleccionados serán eliminados y la información relacionada será irrecuperable.", - "title": "Título", - "title-required": "Título requerido.", - "description": "Descripción", - "details": "Detalles", - "events": "Eventos", - "copyId": "Copiar ID de propietario", - "idCopiedMessage": "El ID de propietario se ha copiado al portapapeles", - "select-tenant": "Seleccionar propietario", - "no-tenants-matching": "No hay propietarios que coincidan con '{{entity}}' .", - "tenant-required": "Propietario requerido", - "search": "Buscar propietarios", - "selected-tenants": "{ count, plural, =1 {1 propietario} other {# propietarios} } seleccionados", - "isolated-tb-rule-engine": "Procesando en contenedor Motor de Reglas aislado", - "isolated-tb-rule-engine-details": "Requiere microservicios separados por propietario aislado" + "color": { + "color-settings": "Configuración de color", + "color-type-constant": "Constante", + "color-type-gradient": "Gradiente", + "color-type-range": "Rango", + "color-type-function": "Función", + "color": "Color", + "value-range": "Rango de valores", + "from": "Desde", + "to": "Hasta", + "color-function": "Función de color", + "copy-color-settings-from": "Copiar configuración de color desde", + "copy-from": "Copiar desde", + "settings-type": "Tipo de configuración", + "basic-mode": "Básico", + "advanced-mode": "Avanzado", + "entity-alias": "Alias de entidad", + "entity-attribute": "Atributo de entidad", + "gradient-color": "Color de gradiente", + "gradient-color-min": "Color", + "gradient-start": "Color inicial del gradiente", + "gradient-start-min": "Inicio", + "gradient-end": "Color final del gradiente", + "gradient-end-min": "Fin", + "start-value": "Valor inicial", + "end-value": "Valor final", + "gradient-type": "Tipo de gradiente" }, - "tenant-profile": { - "tenant-profile": "Perfil de propietario", - "tenant-profiles": "Perfiles de propietarios", - "add": "Añadir perfil de propietario", - "add-profile": "Añadir perfil", - "edit": "Editar perfil de propietario", - "tenant-profile-details": "Detalles perfil de propietario", - "no-tenant-profiles-text": "No se encontraron perfiles de propietario", - "name-max-length": "El nombre debe ser menor de 256", - "search": "Buscar perfiles de propietario", - "selected-tenant-profiles": "{ count, plural, =1 {1 perfil de propietario} other {# perfiles de propietario} } seleccionados", - "no-tenant-profiles-matching": "No se han encontrado perfiles de propietario que coincidan con '{{entity}}'.", - "tenant-profile-required": "Se requiere perfil de propietario", - "idCopiedMessage": "El ID de perfil de propietario se ha copiado al portapapeles", - "set-default": "Hacer perfil propietario por defecto", - "delete": "Borrar perfil", - "copyId": "Copiar ID de perfil", - "name": "Nombre", - "name-required": "Se requiere nombre.", - "data": "Datos de perfil", - "profile-configuration": "Configuración de perfil", - "description": "Descripción", - "default": "Defecto", - "delete-tenant-profile-title": "Eliminar el perfil propietario '{{tenantProfileName}}'?", - "delete-tenant-profile-text": "Atención, tras la confirmación, el perfil de propietario será borrado y su información relacionada será irrecuperable.", - "delete-tenant-profiles-title": "Eliminar { count, plural, =1 {1 perfil propietario} other {# perfiles propietarios} }?", - "delete-tenant-profiles-text": "Atención, tras la confirmación, los perfiles seleccionados se eliminarán y su información relacionada será irrecuperable.", - "set-default-tenant-profile-title": "Quieres hacer el perfil propietario '{{tenantProfileName}}' por defecto?", - "set-default-tenant-profile-text": "Tras la confirmación, el perfil propietario será marcado por defecto y será usado por los nuevos perfiles propietarios que no tengan perfil específico.", - "no-tenant-profiles-found": "No se encontraron perfiles de propietario.", - "create-new-tenant-profile": "Crear un nuevo perfil!", - "create-tenant-profile": "Crear un nuevo perfil de propietario", - "import": "Importar perfil de propietario", - "export": "Exportar perfil de propietario", - "export-failed-error": "No se ha podido exportar el perfil de propietario: {{error}}", - "tenant-profile-file": "Archivo de perfil de propietario", - "invalid-tenant-profile-file-error": "No se ha podido importar el perfil de propietario: Estructura de datos inválida.", - "advanced-settings": "Ajustes avanzados", - "entities": "Entidades", - "rule-engine": "Cadena de reglas", - "time-to-live": "Time-to-live", - "alarms-and-notifications": "Alarmas y notificaciones", - "ota-files-in-bytes": "Tamaño de fichero OTA en bytes", - "ws-title": "WS", - "unlimited": "(0 - ilimitado)", - "maximum-devices": "Nº Máximo de dispositivos (0 - sin límite)", - "maximum-devices-required": "Nº Máximo de dispositivos requerido.", - "maximum-devices-range": "Nº Máximo de dispositivos no puede ser negativo", - "maximum-assets": "Nº Máximo de activos (0 - sin límite)", - "maximum-assets-required": "Nº Máximo de activos requerido.", - "maximum-assets-range": "Nº Máximo de activos no puede ser negativo", - "maximum-customers": "Nº Máximo de clientes (0 - sin límite)", - "maximum-customers-required": "Nº Máximo de clientes requerido.", - "maximum-customers-range": "Nº Máximo de clientes no puede ser negativo", - "maximum-users": "Nº Máximo de usuarios (0 - sin límite)", - "maximum-users-required": "Nº Máximo de usuarios requerido.", - "maximum-users-range": "Nº Máximo de usuarios no puede ser negativo", - "maximum-dashboards": "Nº Máximo de paneles (0 - sin límite)", - "maximum-dashboards-required": "Nº Máximo de paneles requerido.", - "maximum-dashboards-range": "Nº Máximo de paneles no puede ser negativo", - "maximum-edges": "Nº Máximo de bordes (0 - sin límite)", - "maximum-edges-required": "Nº Máximo de bordes requerido.", - "maximum-edges-range": "Nº Máximo de bordes no puede ser negativo", - "maximum-rule-chains": "Nº Máximo de cadenas de reglas (0 - sin límite)", - "maximum-rule-chains-required": "Nº Máximo de cadenas de reglas requerido.", - "maximum-rule-chains-range": "Nº Máximo de cadenas de reglas no puede ser negativo", - "maximum-resources-sum-data-size": "Tamaño máximo de ficheros de recursos en bytes (0 - sin límite)", - "maximum-resources-sum-data-size-required": "Tamaño máximo de ficheros de recursos requerido.", - "maximum-resources-sum-data-size-range": "Tamaño máximo de ficheros de recursos no puede ser negativo", - "maximum-ota-packages-sum-data-size": "Tamaño máximo de paquetes OTA en bytes (0 - sin límite)", - "maximum-ota-package-sum-data-size-required": "Tamaño máximo de paquetes OTA requerido.", - "maximum-ota-package-sum-data-size-range": "Tamaño máximo de paquetes OTA no puede ser negativo", - "rest-requests-for-tenant": "Consultas REST por propietario", - "transport-tenant-telemetry-msg-rate-limit": "Tasa de mensajes de telemetría por propietario.", - "transport-tenant-telemetry-data-points-rate-limit": "Tasa de datapoints por propietario.", - "transport-device-msg-rate-limit": "Tasa de mensajes de dispositivo.", - "transport-device-telemetry-msg-rate-limit": "Tasa de mensajes de telemetría de dispositivo.", - "transport-device-telemetry-data-points-rate-limit": "Tasa de datapoints de telemetría de dispositivo.", - "tenant-entity-export-rate-limit": "Tasa de creación de versión de entidades", - "tenant-entity-import-rate-limit": "Tasa de carga de versión de entidades", - "tenant-notification-request-rate-limit": "Consultas de notificaciones", - "tenant-notification-requests-per-rule-rate-limit": "Consultas de notificaciones por regla de notificación", - "max-transport-messages": "Nº Máximo de mensajes de transporte (0 - sin límite)", - "max-transport-messages-required": "Nº Máximo de mensajes de transporte requerido.", - "max-transport-messages-range": "Nº Máximo de mensajes de transporte no puede ser negativo", - "max-transport-data-points": "Nº Máximo de datapoints transporte (0 - sin límite)", - "max-transport-data-points-required": "Nº Máximo de datapoints transporte requerido.", - "max-transport-data-points-range": "Nº Máximo de datapoints transporte no puede ser negativo", - "max-r-e-executions": "Nº Máximo de ejecuciones de motor de reglas (0 - sin límite)", - "max-r-e-executions-required": "Nº Máximo de ejecuciones de motor de reglas requerido.", - "max-r-e-executions-range": "Nº Máximo de ejecuciones de motor de reglas no puede ser negativo", - "max-j-s-executions": "Nº Máximo de ejecuciones JavaScript (0 - sin límite)", - "max-j-s-executions-required": "Nº Máximo de ejecuciones JavaScript requerido.", - "max-j-s-executions-range": "Nº Máximo de ejecuciones JavaScript no puede ser negativo", - "max-d-p-storage-days": "Nº Máximo de días a grabar en datapoints (0 - sin límite)", - "max-d-p-storage-days-required": "Nº Máximo de días requerido.", - "max-d-p-storage-days-range": "Nº Máximo de días no puede ser negativo", - "default-storage-ttl-days": "Días por defecto grabado TTL (0 - sin límite)", - "default-storage-ttl-days-required": "Días por defecto TTL requerido.", - "default-storage-ttl-days-range": "Días por defecto TTL no puede ser negativo", - "alarms-ttl-days": "Días de TTL alarmas (0 - sin límite)", - "alarms-ttl-days-required": "Días de TTL alarmas requerido", - "alarms-ttl-days-days-range": "Días de TTL alarmas no puede ser negativo", - "rpc-ttl-days": "Días TTL RPC (0 - sin límite)", - "rpc-ttl-days-required": "Días TTL RPC requerido", - "rpc-ttl-days-days-range": "Días TTL RPC no puede ser negativo", - "max-rule-node-executions-per-message": "Nº Máximo de ejecuciones (cadena de reglas) por mensaje (0 - sin límite)", - "max-rule-node-executions-per-message-required": "Nº Máximo de ejecuciones por mensaje requerido.", - "max-rule-node-executions-per-message-range": "Nº Máximo de ejecuciones por mensaje no puede ser negativo", - "max-emails": "Nº Máximo de emails (0 - sin límite)", - "max-emails-required": "Nº Máximo de emails requerido.", - "max-emails-range": "Nº Máximo de emails no puede ser negativo", - "sms-enabled": "SMS Activados", - "max-sms": "Nº Máximo de mensajes SMS (0 - sin límite)", - "max-sms-required": "Nº Máximo de mensajes SMS requerido.", - "max-sms-range": "Nº Máximo de mensajes SMS no puede ser negativo", - "max-created-alarms": "Nº Máximo de alarmas creadas (0 - sin límite)", - "max-created-alarms-required": "Nº Máximo de alarmas creadas requerido.", - "max-created-alarms-range": "Nº Máximo de alarmas creadas no puede ser negativo", - "no-queue": "No hay cola configurada", - "add-queue": "Añadir cola", - "queues-with-count": "({{count}}) Colas", - "tenant-rest-limits": "Límite de frecuencia de solicitudes REST por propietario", - "customer-rest-limits": "Límite de frecuencia de solicitudes REST por cliente", - "incorrect-pattern-for-rate-limits": "El formato correcto son pares de valores separados por el símbolo : capacidad y período (en segundos), ejemplo: 100:1,2000:60", - "too-small-value-zero": "El valor debe ser mayor de 0", - "too-small-value-one": "El valor debe ser mayor de 1", - "queue-size-is-limited-by-system-configuration": "El tamaño de la cola, también se limita por la configuración del sistema.", - "cassandra-tenant-limits-configuration": "Límite en tasa de consulta de Cassandra por propietario", - "ws-limit-max-sessions-per-tenant": "Número máximo de sesiones WS por propietario", - "ws-limit-max-sessions-per-customer": "Número máximo de sesiones WS por cliente", - "ws-limit-max-sessions-per-regular-user": "Número máximo de sesiones por usuario regular", - "ws-limit-max-sessions-per-public-user": "Número máximo de sesiones WS por usuario público", - "ws-limit-queue-per-session": "Tamaño máximo de cola de mensaje WS por sesión", - "ws-limit-max-subscriptions-per-tenant": "Número máximo de subscripciones WS por administrador propietario", - "ws-limit-max-subscriptions-per-customer": "Número máximo de subscripciones de WS por cliente", - "ws-limit-max-subscriptions-per-regular-user": "Número máximo de subscripciones de WS por usuario regular", - "ws-limit-max-subscriptions-per-public-user": "Número máximo de subscripciones de WS por usuario público", - "ws-limit-updates-per-session": "Frecuencia de actualizaciones WS por sesión", - "rate-limits": { - "add-limit": "Añadir límite", - "advanced-settings": "Ajustes avanzados", - "edit-limit": "Editar límite", - "but-less-than": "pero menor que", - "edit-transport-tenant-msg-title": "Editar límite de mensajes de transporte (Propietario)", - "edit-transport-tenant-telemetry-msg-title": "Editar límite de mensajes de telemetría (Propietario)", - "edit-transport-tenant-telemetry-data-points-title": "Editar límite de puntos de datos de transporte (Propietario)", - "edit-transport-device-msg-title": "Editar tasa de mensajes de dispositivo", - "edit-transport-device-telemetry-msg-title": "Editar tasa de mensajes de transporte de dispositivo", - "edit-transport-device-telemetry-data-points-title": "Editar límite de puntos de datos de transporte (Dispositivo)", - "edit-tenant-rest-limits-title": "Editar límite de solicitudes REST por propietario", - "edit-customer-rest-limits-title": "Editar límite de solicitudes REST por cliente", - "edit-ws-limit-updates-per-session-title": "Editar límite de actualizaciones WS por sesión", - "edit-cassandra-tenant-limits-configuration-title": "Editar límites de consultas en BD Cassandra por propietario", - "edit-tenant-entity-export-rate-limit-title": "Editar tasa límite de creación de versiones por entidad", - "edit-tenant-entity-import-rate-limit-title": "Editar tasa límite de carga de versión de entidad", - "edit-tenant-notification-request-rate-limit-title": "Editar tasa límite de solicitudes de notificaciones", - "edit-tenant-notification-requests-per-rule-rate-limit-title": "Editar tasa límite de solicitudes en reglas por noticación", - "messages-per": "mensajes por", - "not-set": "No configurado", - "number-of-messages": "Número de mensajes", - "number-of-messages-required": "Se requiere número de mensajes.", - "number-of-messages-min": "El valor mínimo es 1.", - "preview": "Previsualizar", - "per-seconds": "Por segundos", - "per-seconds-required": "Se requiere tasa de tiempo.", - "per-seconds-min": "El valor mínimo es 1.", - "rate-limits": "Límites de tasa", - "remove-limit": "Quitar límite", - "transport-tenant-msg": "Mensajes de transporte (propietario)", - "transport-tenant-telemetry-msg": "Mensajes de telemetría (propietario)", - "transport-tenant-telemetry-data-points": "Puntos de datos de telemetría (propietario)", - "transport-device-msg": "Mensajes de transporte (dispositivo)", - "transport-device-telemetry-msg": "Mensajes de telemetría (dispositivo)", - "transport-device-telemetry-data-points": "Puntos de datos de telemetría (dispositivo)", - "sec": "sec" - } + "dashboard-state": { + "dashboard-state-settings": "Configuración del estado del tablero", + "dashboard-state": "ID del estado del tablero", + "autofill-state-layout": "Autocompletar la altura del diseño del estado por defecto", + "default-margin": "Margen por defecto de los widgets", + "default-background-color": "Color de fondo por defecto", + "sync-parent-state-params": "Sincronizar parámetros de estado con el tablero principal" }, - "timeinterval": { - "seconds-interval": "{ seconds, plural, =1 {1 segundo} other {# segundos} }", - "minutes-interval": "{ minutes, plural, =1 {1 minuto} other {# minutos} }", - "hours-interval": "{ hours, plural, =1 {1 hora} other {# horas} }", - "days-interval": "{ days, plural, =1 {1 día} other {# días} }", - "days": "Días", - "hours": "Horas", - "minutes": "Minutos", - "seconds": "Segundos", - "advanced": "Avanzado", - "predefined": { - "yesterday": "Ayer", - "day-before-yesterday": "Anteayer", - "this-day-last-week": "Hoy hace una semana", - "previous-week": "Semana anterior (Dom - Sáb)", - "previous-week-iso": "Semana anterior (Lun - Dom)", - "previous-month": "Mes anterior", - "previous-quarter": "Anterior trimestre", - "previous-half-year": "Anterior semestre", - "previous-year": "Año anterior", - "current-hour": "Hora actual", - "current-day": "Día actual", - "current-day-so-far": "Día actual hasta ahora", - "current-week": "Semana actual (Dom - Sáb)", - "current-week-iso": "Semana actual (Lun - Dom)", - "current-week-so-far": "Semana actual hasta hoy (Dom - Sáb)", - "current-week-iso-so-far": "Semana actual hasta hoy (Lun - Dom)", - "current-month": "Mes actual", - "current-month-so-far": "Mes actual hasta hoy", - "current-quarter": "Trimestre actual", - "current-quarter-so-far": "Trimestre actual hasta hoy", - "current-half-year": "Semestre en curso", - "current-half-year-so-far": "Semestre en curso hasta hoy", - "current-year": "Año actual", - "current-year-so-far": "Año actual hasta ahora" - } + "date-range-navigator": { + "date-range-picker-settings": "Configuración del selector de rango de fechas", + "hide-date-range-picker": "Ocultar selector de rango de fechas", + "picker-one-panel": "Selector de rango de fechas con un panel", + "picker-auto-confirm": "Confirmación automática del selector de rango de fechas", + "picker-show-template": "Mostrar plantilla del selector de rango de fechas", + "first-day-of-week": "Primer día de la semana", + "interval-settings": "Configuración del intervalo", + "hide-interval": "Ocultar intervalo", + "initial-interval": "Intervalo inicial", + "interval-hour": "Hora", + "interval-day": "Día", + "interval-week": "Semana", + "interval-two-weeks": "2 semanas", + "interval-month": "Mes", + "interval-three-months": "3 meses", + "interval-six-months": "6 meses", + "step-settings": "Configuración del paso", + "hide-step-size": "Ocultar tamaño del paso", + "initial-step-size": "Tamaño de paso inicial", + "hide-labels": "Ocultar etiquetas", + "use-session-storage": "Usar almacenamiento de sesión", + "localizationMap": { + "Sun": "Dom", + "Mon": "Lun", + "Tue": "Mar", + "Wed": "Mié", + "Thu": "Jue", + "Fri": "Vie", + "Sat": "Sáb", + "Jan": "Ene", + "Feb": "Feb", + "Mar": "Mar", + "Apr": "Abr", + "May": "May", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Ago", + "Sep": "Sep", + "Oct": "Oct", + "Nov": "Nov", + "Dec": "Dic", + "January": "Enero", + "February": "Febrero", + "March": "Marzo", + "April": "Abril", + "June": "Junio", + "July": "Julio", + "August": "Agosto", + "September": "Septiembre", + "October": "Octubre", + "November": "Noviembre", + "December": "Diciembre", + "Custom Date Range": "Rango de fechas personalizado", + "Date Range Template": "Plantilla de rango de fechas", + "Today": "Hoy", + "Yesterday": "Ayer", + "This Week": "Esta semana", + "Last Week": "La semana pasada", + "This Month": "Este mes", + "Last Month": "El mes pasado", + "Year": "Año", + "This Year": "Este año", + "Last Year": "El año pasado", + "Date picker": "Selector de fecha", + "Hour": "Hora", + "Day": "Día", + "Week": "Semana", + "2 weeks": "2 semanas", + "Month": "Mes", + "3 months": "3 meses", + "6 months": "6 meses", + "Custom interval": "Intervalo personalizado", + "Interval": "Intervalo", + "Step size": "Tamaño del paso", + "Ok": "Aceptar" + } }, - "timeunit": { - "milliseconds": "Milisegundos", - "seconds": "Segundos", - "minutes": "Minutos", - "hours": "Horas", - "days": "Días" + "doughnut": { + "doughnut-appearance": "Apariencia de gráfico de anillo", + "layout": "Disposición", + "layout-default": "Predeterminado", + "layout-with-total": "Con total", + "central-total-value": "Valor total central", + "doughnut-card-style": "Estilo de tarjeta del gráfico de anillo" }, - "timewindow": { - "timewindow": "Ventana de tiempo", - "years": "{ years, plural, =1 { año } other {# años } }", - "years-short": "{{ years }}a", - "months": "{ months, plural, =1 { mes } other {# meses } }", - "months-short": "{{ months }}M", - "weeks": "{ weeks, plural, =1 { semana } other {# semanas } }", - "weeks-short": "{{ weeks }}sem", - "days": "{ days, plural, =1 { día } other {# días } }", - "days-short": "{{ days }}d", - "hours": "{ hours, plural, =0 { horas } =1 {1 hora } other {# horas } }", - "hr": "{{ hr }} hr", - "hr-short": "{{ hr }}h", - "minutes": "{ minutes, plural, =0 { minutos } =1 {1 minuto } other {# minutos } }", - "min": "{{ min }} min", - "min-short": "{{ min }}m", - "seconds": "{ seconds, plural, =0 { segundos } =1 {1 segundo } other {# segundos } }", - "sec": "{{ sec }} sec", - "sec-short": "{{ sec }}s", - "short": { - "days": "{ days, plural, =1 {1 día } other {# días } }", - "hours": "{ hours, plural, =1 {1 hora } other {# horas } }", - "minutes": "{{minutes}} min ", - "seconds": "{{seconds}} sec " - }, - "realtime": "Tiempo-real", - "history": "Histórico", - "last-prefix": "último(s)", - "period": "desde {{ startTime }} hasta {{ endTime }}", - "edit": "Editar ventana de tiempo", - "date-range": "Rango de fechas", - "for-all-time": "Desde siempre", - "last": "Últimos(s)", - "time-period": "Período de tiempo", - "hide": "Ocultar", - "interval": "Intervalo", - "just-now": "Ahora mismo", - "just-now-lower": "ahora mismo", - "ago": "hace", - "style": "Estilo de ventana de tiempo", - "icon": "Icono", - "icon-position": "Posición de icono", - "icon-position-left": "Izquierda", - "icon-position-right": "Derecha", - "font": "Fuente", - "color": "Color", - "displayTypePrefix": "Mostrar prefijo en tiempo real/histórico", - "preview": "Previsualizar" + "entities-hierarchy": { + "hierarchy-data-settings": "Configuración de datos de jerarquía", + "relations-query-function": "Función de consulta de relaciones del nodo", + "has-children-function": "Función que indica si el nodo tiene hijos", + "node-state-settings": "Configuración del estado del nodo", + "node-opened-function": "Función de nodo abierto por defecto", + "node-disabled-function": "Función de nodo deshabilitado", + "display-settings": "Configuración de visualización", + "node-icon-function": "Función de icono del nodo", + "node-text-function": "Función de texto del nodo", + "sort-settings": "Configuración de ordenación", + "nodes-sort-function": "Función de ordenación de nodos" }, - "unit": { - "millimeter": "Milímetro", - "centimeter": "Centímetro", - "angstrom": "Angstrom", - "nanometer": "Nanómetro", - "micrometer": "Micrómetro", - "meter": "Metro", - "kilometer": "Kilómetro", - "inch": "Pulgada", - "foot": "Pie", - "yard": "Yarda", - "mile": "Milla", - "nautical-mile": "Milla Náutica", - "astronomical-unit": "Unidad Astronómica", - "reciprocal-metre": "Metro recíproco", - "meter-per-meter": "Metro por metro", - "steradian": "Estereorradián", - "thou": "Tú", - "barleycorn": "Grano de cebada", - "hand": "Hand", - "chain": "Cadena", - "furlong": "Furlong", - "league": "League", - "fathom": "Fathom", - "cable": "Cable", - "link": "Enlace", - "rod": "Barra", - "nanogram": "Nanogramo", - "microgram": "Microgramo", - "milligram": "Miligramo", - "gram": "Gramo", - "kilogram": "Kilogramo", - "tonne": "Tonelada", - "ounce": "Onza", - "pound": "Libra", - "stone": "Piedra", - "hundredweight-count": "Hundredweight", - "short-tons": "Tonelada corta", - "dalton": "Dalton", - "grain": "Grano", - "drachm": "Dracma", - "quarter": "Cuarto", - "slug": "Lingote", - "carat": "Quilate", - "cubic-millimeter": "Milímetro Cúbico", - "cubic-centimeter": "Centímetro Cúbico", - "cubic-meter": "Metro Cúbico/s", - "cubic-kilometer": "Kilómetro Cúbico", - "microliter": "Microlitro", - "milliliter": "Mililitro", - "liter": "Litro", - "hectoliter": "Hectolitro", - "cubic-inch": "Pulgada Cúbica", - "cubic-foot": "Pié Cúbico", - "cubic-yard": "Yarda Cúbica", - "fluid-ounce": "Onza Fluida", - "pint": "Pinta", - "quart": "Cuarta", - "gallon": "Galón", - "oil-barrels": "Barriles de Aceite", - "cubic-meter-per-kilogram": "Metro Cúbico por Kilogramo", - "gill": "Cuarta parte de una pinta", - "hogshead": "Tonel", - "teaspoon": "Cucharilla", - "tablespoon": "Cucharada", - "cup": "Taza", - "celsius": "Celsius", - "kelvin": "Kelvin", - "rankine": "Rankine", - "fahrenheit": "Fahrenheit", - "percent": "%", - "meter-per-second": "Metros por Segundo", - "kilometer-per-hour": "Kilómetros por Hora", - "foot-per-second": "Pies por Segundo", - "mile-per-hour": "Millas por Hora", - "knot": "Nudo", - "millimeters-per-minute": "Milímetros por minuto", - "kilometer-per-hour-squared": "Kilómetros al cuadrado por hora", - "foot-per-second-squared": "Pie al cuadrado por segundo", - "pascal": "Pascal", - "kilopascal": "Kilopascale", - "megapascal": "Megapascal", - "gigapascal": "Gigapascal", - "millibar": "Milibar", - "bar": "Bar", - "kilobar": "Kilobar", - "newton": "Newton", - "newton-meter": "Newton metro", - "foot-pounds": "Libras-pie", - "inch-pounds": "Pulgadas-pie", - "newton-per-meter": "Newton por metro", - "atmospheres": "Atmósferas", - "pounds-per-square-inch": "Libras por pulgada cuadrada", - "torr": "Torr", - "inches-of-mercury": "Pulgadas de mercurio", - "pascal-per-square-meter": "Pascales por metro cuadrado", - "pound-per-square-inch": "Pie por pulgada cuadrada", - "newton-per-square-meter": "Newtons por metro cuadrado", - "kilogram-force-per-square-meter": "Kilogramo de fuerza por metro cuadrado", - "pascal-per-square-centimeter": "Pascales por centímetro cuadrado", - "ton-force-per-square-inch": "Tonelada de fuerza por pulgada cuadrada", - "kilonewton-per-square-meter": "Kilonewtons por metro cuadrado", - "newton-per-square-millimeter": "Newton por milímetro cuadrado", - - "microjoule": "Microjulio", - "millijoule": "Milijulio", - "joule": "Julio", - "kilojoule": "Kilojulio", - "megajoule": "Megajulio", - "gigajoule": "Gigajulio", - "watt-hour": "Watios-hora", - "kilowatt-hour": "Kilowatios-hora", - "electron-volts": "Electronvoltios", - "joules-per-coulomb": "Julios por Coulomb", - "british-thermal-unit": "British Thermal Units (BTU)", - "foot-pound": "Foot-pound", - "calorie": "Caloría", - "small-calorie": "Caloría-pequeña", - "kilocalorie": "Kilocaloría", - "joule-per-kelvin": "Julios por Kelvin", - "joule-per-kilogram-kelvin": "Julio por Kilogramo-Kelvin", - "joule-per-kilogram": "Julio por Kilogramo", - "watt-per-meter-kelvin": "Watios por metro Kelvin", - "joule-per-cubic-meter": "Julios por metro cúbico", - "therm": "Termia", - "electric-dipole-moment": "Momento dipolar eléctrico", - "magnetic-dipole-moment": "Momento dipolar magnético", - "debye": "Debyé", - "coulomb-per-square-meter-per-volt": "Culombio por metro cuadrado por Voltio", - "milliwatt": "Miliwatio", - "microwatt": "Microwatio", - "watt": "Watio", - "kilowatt": "Kilowatio", - "megawatt": "Megawatio", - "gigawatt": "Gigawatio", - "metric-horsepower": "Caballos métricos", - "milliwatt-per-square-centimeter": "Miliwatios por centímetro cuadrado", - "watt-per-square-centimeter": "Watios por centímetro cuadrado", - "kilowatt-per-square-centimeter": "Kilowatios por centímetro cuadrado", - "milliwatt-per-square-meter": "Miliwatios por metro cuadrado", - "watt-per-square-meter": "Watios por metro cuadrado", - "kilowatt-per-square-meter": "Kilowatios por metro cuadrado", - "watt-per-square-inch": "Watios por pulgada cuadrada", - "kilowatt-per-square-inch": "Kilowatios por pulgada cuadrada", - "horsepower": "Caballos", - "btu-per-hour": "BTU/h", - "coulomb": "Culombio", - "millicoulomb": "Miliculombios", - "microcoulomb": "Microculombios", - "picocoulomb": "Picoculombios", - "coulomb-per-meter": "Culombio por metro", - "coulomb-per-cubic-meter": "Culombio por metro cúbico", - "coulomb-per-square-meter": "Culombio por metro cuadrado", - "square-millimeter": "Milímetro cuadrado", - "square-centimeter": "Centímetro cuadrado", - "square-meter": "Metro Cuadrado", - "hectare": "Hectárea", - "square-kilometer": "Kilómetro Cuadrado", - "square-inch": "Pulgada Cuadrada", - "square-foot": "Pie Cuadrada", - "square-yard": "Yarda Cuadrada", - "acre": "Hectárea", - "square-mile": "Milla Cuadrada", - "are": "Área", - "barn": "Barnio", - "circular-inch": "Pulgada circular", - "milliampere-hour": "Miliamperios hora", - "milliampere-hour-tags": "intensidad eléctrica, flujo de corriente, carga eléctrica, capacidad actual, flujo de electricidad, flujo eléctrico, miliamperio hora, miliamperios hora, mAh", - "ampere-hours": "Amperios hora", - "ampere-hours-tags": "intensidad eléctrica, flujo de corriente, carga eléctrica, capacidad actual, flujo de electricidad, flujo eléctrico, ampere, amperio hora, Ah", - "kiloampere-hours": "Kiloamperios hora", - "kiloampere-hours-tags": "intensidad eléctrica, flujo de corriente, carga eléctrica, capacidad actual, flujo de electricidad, flujo eléctrico, kiloamperio hora, kiloampreios hora, kAh", - "nanoampere": "Nanoamperio", - "nanoampere-tags": "intensidad, amperios, nanoamperio, nA", - "picoampere": "Picoamperio", - "picoampere-tags": "intensidad, amperios, picoamperio, pA", - "microampere": "Microamperio", - "microampere-tags": "intensidad eléctrica, microamperio, microamperios, μA", - "milliampere": "Miliamperio", - "milliampere-tags": "intensidad eléctrica, miliamperio, miliamperios, mA", - "ampere": "Amperio", - "ampere-tags": "intensidad eléctrica, flujo de corriente, flujo de electricidad, flujo eléctrico, amperio, amperios, amperaje, A", - "kiloamperes": "Kiloamperios", - "kiloamperes-tags": "intensidad eléctrica, flujo de corriente, kiloamperios, kA", - "microampere-per-square-centimeter": "Microamperio por centímetro cuadrado", - "microampere-per-square-centimeter-tags": "Densidad de intensidad, microamperio por centímetro cuadrado, µA/cm²", - "ampere-per-square-meter": "Amperio por metro cuadrado", - "ampere-per-square-meter-tags": "Densidad de intensidad, intensidad por área de unidad, amperio por metro cuadrado, A/m²", - "ampere-per-meter": "Amperio por Metro", - "ampere-per-meter-tags": "fuerza del campo magnético, intensidad del campo magnético, amperios por metro, A/m", - "oersted": "Oersted", - "oersted-tags": "campo magnético, oersted, Oe", - "bohr-magneton": "Magneton de Bohr", - "bohr-magneton-tags": "física atómica, momento magnético, magnetón de bohr, μB", - "ampere-meter-squared": "Amperios metro al cuadrado", - "ampere-meter-squared-tags": "momento magnético, momento dipolar, ampero metro cuadrado, A·m²", - "ampere-meter": "Amperio Metro", - "ampere-meter-tags": "campo magnético, bucle de corriente, ampero metro, A·m", - "nanovolt": "Nanovoltio", - "picovolt": "Picovoltio", - "millivolts": "Milivoltios", - "microvolts": "Microvoltios", - "volt": "Voltio", - "kilovolts": "Kilovoltios", - "dbmV": "dBmV", - "volt-meter": "Voltio-Metro", - "kilovolt-meter": "Kilovoltio-Metro", - "megavolt-meter": "Megavoltio-Metro", - "microvolt-meter": "Microvoltio-Metro", - "millivolt-meter": "Milivoltio-Metro", - "nanovolt-meter": "Nanovoltio-Metro", - "ohm": "Ohmio", - "microohm": "Microohmio", - "milliohm": "Miliohmio", - "kilohm": "Kiloohmio", - "megohm": "Megaohmio", - "gigohm": "Gigaohmio", - "hertz": "Hertzio", - "kilohertz": "Kilohertzio", - "megahertz": "Megahertzio", - "gigahertz": "Gigahertzio", - "rpm": "Revoluciones por minuto", - "candela-per-square-meter": "Candela por metro cuadrado", - "candela": "Candela", - "lumen": "Lúmenes", - "lux": "Lux", - "foot-candle": "Candela", - "lumen-per-square-meter": "Lúmenes por metro cuadrado", - "lux-second": "Lux segundo", - "lumen-second": "Lumen segundo", - "lumens-per-watt": "Lumens por watio", - "absorbance": "Absorbancia", - "mole": "Mole", - "nanomole": "Nanomole", - "micromole": "MicroMole", - "millimole": "Milimole", - "kilomole": "Kilomole", - "mole-per-cubic-meter": "Mole por metro cúbico", - "rssi": "RSSI", - "ppm": "Partes Por Millón", - "ppb": "Partes Por Billión", - "micrograms-per-cubic-meter": "Microgramos por metro cúbico", - "aqi": "AQI", - "gram-per-cubic-meter": "Gramos por metro cúbico", - "gram-per-kilogram": "Humedad Específica", - "millimeters-per-second": "Milimetros por segundo", - "neper": "Neper", - "bel": "Belio", - "decibel": "Decibelio", - "meters-per-second-squared": "Metros por segundo al cuadrado", - "becquerel": "Becquerel", - "curie": "Curie", - "gray": "Gray", - "sievert": "Sievert", - "roentgen": "Roentgen", - "cps": "Cuentas por segundo", - "rad": "Rad", - "rem": "Rem", - "dps": "Desintegraciones por segundo", - "rutherford": "Rutherford", - "coulombs-per-kilogram": "Culombios por kilogramo", - "becquerels-per-cubic-meter": "Becquerels por metro cúbico", - "curies-per-liter": "Curies por litro", - "becquerels-per-second": "Becquerels por segundo", - "curies-per-second": "Curies por segundo", - "gy-per-second": "Gray por segundo", - "watt-per-steradian": "Watio por estereorradián", - "watt-per-square-metre-steradian": "Watios por metro cuadrado de estereorradián", - "ph-level": "Nivel de pH", - "turbidity": "Turbidez", - "mg-per-liter": "Miligramos por litro", - "microsiemens-per-centimeter": "Microsiemens por centímetro", - "millisiemens-per-meter": "Milisiemens por metro", - "siemens-per-meter": "Siemens por metro", - "kilogram-per-cubic-meter": "Kilogramos por metro cúbico", - "gram-per-cubic-centimeter": "Gramos por centímetro cúbico", - "kilogram-per-square-meter": "Kilogramo por metro cuadrado", - "milligram-per-milliliter": "Miligramo por mililitro", - "pound-per-cubic-foot": "Libra por pie cúbico", - "ounces-per-cubic-inch": "Onzas por pulgada cúbica", - "tons-per-cubic-yard": "Toneladas por yarda cúbica", - "particle-density": "Densidad de partícula", - "kilometers-per-liter": "Kilometros por litro", - "miles-per-gallon": "Millas por galón", - "liters-per-100-km": "Litros por 100 km", - "gallons-per-mile": "Galones por milla", - "liters-per-hour": "Litros por hora", - "gallons-per-hour": "Galones por hora", - "beats-per-minute": "Pulsaciones por minuto", - "millimeters-of-mercury": "Milímetros de mercurio", - "milligrams-per-deciliter": "Miligramos por decilitro", - "g-force": "Fuerza-G", - "kilonewton": "Kilonewton", - "kilogram-force": "Kilogramo-Fuerza", - "pound-force": "Pondio-Fuerza", - "kilopound-force": "Kilopondio-Fuerza", - "dyne": "Dina", - "poundal": "Poundal", - "kip": "Kip", - "gal": "Gal", - "gravity": "Gravedad", - "hectopascal": "Hectopascal", - "atmosphere": "Atmósfera", - "millibars": "Milibares", - "inch-of-mercury": "Pulgada de mercurio", - "richter-scale": "Escala de Richter", - "second": "Segundo", - "minute": "Minuto", - "hour": "Hora", - "day": "Día", - "week": "Semana", - "month": "Mes", - "year": "Año", - "cubic-foot-per-minute": "Pie cúbico por minuto", - "cubic-meters-per-hour": "Metros cúbicos por hora", - "cubic-meters-per-second": "Metros cúbicos por segundo", - "liter-per-second": "Litros por segundo", - "liter-per-minute": "Litros por minuto", - "gallons-per-minute": "Galones por minuto", - "cubic-foot-per-second": "Pie cúbico por segundo", - "milliliters-per-minute": "Mililitros por minuto", - "bit": "Bit", - "byte": "Byte", - "kilobyte": "Kilobyte", - "megabyte": "Megabyte", - "gigabyte": "Gigabyte", - "terabyte": "Terabyte", - "petabyte": "Petabyte", - "exabyte": "Exabyte", - "zettabyte": "Zettabyte", - "yottabyte": "Yottabyte", - "bit-per-second": "Bit por segundo", - "kilobit-per-second": "Kilobit por segundo", - "megabit-per-second": "Megabit por segundo", - "gigabit-per-second": "Gigabit por segundo", - "terabit-per-second": "Terabit por segundo", - "byte-per-second": "Byte por segundo", - "kilobyte-per-second": "Kilobyte por segundo", - "megabyte-per-second": "Megabyte por segundo", - "gigabyte-per-second": "Gigabyte por segundo", - "degree": "Grado", - "radian": "Radian", - "gradian": "Gradian", - "mil": "Mil", - "revolution": "Revolución", - "siemens": "Siemens", - "millisiemens": "Milisiemens", - "microsiemens": "Microsiemens", - "kilosiemens": "Kilosiemens", - "megasiemens": "Megasiemens", - "gigasiemens": "Gigasiemens", - "farad": "Faradio", - "millifarad": "Milifaradio", - "microfarad": "Microfaradio", - "nanofarad": "Nanofaradio", - "picofarad": "Picofaradio", - "kilofarad": "Kilofaradio", - "megafarad": "Megafaradio", - "gigafarad": "Gigafaradio", - "terfarad": "Terfaradio", - "farad-per-meter": "Faradio por metro", - "tesla": "Tesla", - "gauss": "Gauss", - "kilogauss": "Kilogauss", - "millitesla": "Militesla", - "microtesla": "Microtesla", - "nanotesla": "Nanotesla", - "kilotesla": "Kilotesla", - "megatesla": "Megatesla", - "millitesla-square-meters": "militesla por metro cuadrado", - "gamma": "Gamma", - "lambda": "Lambda", - "square-meter-per-second": "Metros cuadrados por segundo", - "square-centimeter-per-second": "Centímetros cuadrados por segundo", - "stoke": "Stoke", - "centistokes": "Centistokes", - "square-foot-per-second": "Pie cuadrado por segundo", - "square-inch-per-second": "Pulgada cuadrada por segundo", - "pascal-second": "Pascal-segundo", - "centipoise": "Centipoise", - "poise": "Poise", - "reynolds": "Reynolds", - "pound-per-foot-hour": "Libra por pie-hora", - "newton-second-per-square-meter": "Newton segundo por metro cuadrado", - "dyne-second-per-square-centimeter": "Dyne segundo por centímetro cuadrado", - "kilogram-per-meter-second": "Kilogramo por metro-segundo", - "tesla-square-meters": "Tesla metros cuadrados", - "maxwell": "Maxwell", - "tesla-per-meter": "Tesla por metro", - "gauss-per-centimeter": "Gauss por centímetro", - "weber": "Weber", - "microweber": "Microweber", - "milliweber": "Miliweber", - "gauss-square-centimeter": "Gauss-centímetro cuadrado", - "kilogauss-square-centimeter": "Kilogauss-centímetro cuadrado", - "henry": "Henrio", - "millihenry": "Milihenrio", - "microhenry": "Microhenrio", - "nanohenry": "Nanohenrio", - "henry-per-meter": "Henrio por metro", - "tesla-meter-per-ampere": "Tesla metro por amperio", - "gauss-per-oersted": "Gauss por Oersted", - "kilogram-per-mole": "Kilogramo por mole", - "gram-per-mole": "Gramo por mole", - "milligram-per-mole": "Miligramo por mole", - "joule-per-mole": "Julios por mole", - "joule-per-mole-kelvin": "Julios por mole-Kelvin", - "millivolts-per-meter": "Milivoltios por metro", - "volts-per-meter": "Voltios por metro", - "kilovolts-per-meter": "Kilovoltios por metro", - "radian-per-second": "Radianes por segundo", - "radian-per-second-squared": "Radianes por segundo al cuadrado", - "revolutions-per-minute-per-second": "Aceleración angular", - "revolutions-per-minute-per-second-squared": "Aceleración angular", - "deg-per-second": "Grados/s", - "degrees-brix": "Grados Brix", - "katal": "Katal", - "katal-per-cubic-metre": "Katal por metro cúbico" + "edge": { + "display-default-title": "Mostrar título por defecto" }, - "user": { - "user": "Usuario", - "users": "Usuarios", - "customer-users": "Usuarios del Cliente", - "tenant-admins": "Admins propietarios", - "sys-admin": "Administrador del Sistema", - "tenant-admin": "Administrador Propietario", - "customer": "Cliente", - "anonymous": "Anónimo", - "add": "Agregar usuario", - "delete": "Eliminar usuario", - "add-user-text": "Agregar nuevo usuario", - "no-users-text": "Ningún usuario encontrado", - "user-details": "Detalles del usuario", - "delete-user-title": "¿Eliminar el usuario '{{userEmail}}'?", - "delete-user-text": "Atención, tras la confirmación el usuario seleccionado será eliminado y la información relacionada será irrecuperable.", - "delete-users-title": "¿Eliminar { count, plural, =1 {1 usuario} other {# usuarios} }?", - "delete-users-action-title": "Borrar { count, plural, =1 {1 usuario} other {# usuarios} }", - "delete-users-text": "Atención, tras la confirmación los usuarios seleccionados serán eliminados y la información relacionada será irrecuperable.", - "activation-email-sent-message": "Mail de activación enviado con éxito!", - "resend-activation": "Reenviar activación", - "email": "Email", - "email-required": "Email requerido.", - "invalid-email-format": "Formato de email no válido.", - "first-name": "Nombre", - "last-name": "Apellido", - "description": "Descripción", - "default-dashboard": "Panel por defecto", - "always-fullscreen": "Siempre en pantalla completa", - "select-user": "Seleccionar usuario", - "no-users-matching": "No se han encontrado usuarios coindiendo con '{{entity}}' .", - "user-required": "Usuario requerido", - "activation-method": "Método de activación", - "display-activation-link": "Mostrar enlace de activación", - "send-activation-mail": "Enviar mail de activación", - "activation-link": "Enlace de activacion de usuario", - "activation-link-text": "Para activar el usuario, usa el siguiente enlace: Activar Usuario :", - "copy-activation-link": "Copiar enlace de activación", - "activation-link-copied-message": "El enlace de activación se ha copiado al portapapeles", - "details": "Detalles", - "login-as-tenant-admin": "Iniciar sesión como Administrador Propietario", - "login-as-customer-user": "Iniciar sesión como Usuario Cliente", - "search": "Buscar usuarios", - "selected-users": "{ count, plural, =1 {1 usuario} other {# usuarios} } seleccionados", - "disable-account": "Deshabilitar cuenta de usuario", - "enable-account": "Habilitar cuenta de usuario", - "enable-account-message": "¡La cuenta de usuario se ha habilitado correctamente!", - "disable-account-message": "¡La cuenta de usuario se deshabilitó correctamente!", - "copyId": "Copiar Id de usuario", - "idCopiedMessage": "El Id de usuario se ha copiado al portapapeles", - "user-list": "Lista de usuarios", - "user-list-required": "Se requiere lista de usuarios" + "gateway": { + "general-settings": "Configuración general", + "widget-title": "Título del widget", + "default-archive-file-name": "Nombre de archivo de archivo por defecto", + "device-type-for-new-gateway": "Tipo de dispositivo para nuevo gateway", + "messages-settings": "Configuración de mensajes", + "save-config-success-message": "Mensaje sobre la configuración del gateway guardada correctamente", + "device-name-exists-message": "Mensaje cuando ya existe un dispositivo con el nombre ingresado", + "gateway-title": "Formulario del gateway", + "read-only": "Solo lectura", + "events-title": "Título del formulario de eventos del gateway", + "events-filter": "Filtro de eventos", + "event-key-contains": "La clave del evento contiene...", + "show-connector": "Mostrar para el conector", + "connector-state-param-key": "Clave del parámetro de estado del conector", + "message": "Mensaje", + "level": "Nivel", + "created-time": "Hora de creación" }, - "value": { - "type": "Tipo de valor", - "string": "Cadena de texto", - "string-value": "Valor de cadena de texto", - "string-value-required": "Se requiere valor de cadena de texto", - "integer": "Nro entero", - "integer-value": "Valor de nro entero", - "integer-value-required": "Se requiere valor entero", - "invalid-integer-value": "Valor de entero inválido", - "double": "Nro decimal", - "double-value": "Valor nro decimal", - "double-value-required": "Se requiere valor nro decimal", - "boolean": "Booleano", - "boolean-value": "Valor booleano", - "false": "Falso", - "true": "Verdadero", - "long": "Nro Largo", - "json": "JSON", - "json-value": "Valor JSON", - "json-value-invalid": "El valor JSON tiene un formato inválido", - "json-value-required": "Se requiere valor JSON" + "gauge": { + "default-color": "Color predeterminado", + "radial-gauge-settings": "Configuración del medidor radial", + "ticks-settings": "Configuración de marcas", + "min-value": "Valor mínimo", + "max-value": "Valor máximo", + "min-value-short": "mín", + "max-value-short": "máx", + "start-ticks-angle": "Ángulo de inicio de marcas", + "ticks-angle": "Ángulo de marcas", + "major-ticks": "Marcas principales", + "major-ticks-count": "Cantidad de marcas principales", + "major-ticks-color": "Color de marcas principales", + "minor-ticks": "Marcas secundarias", + "minor-ticks-count": "Cantidad de marcas secundarias", + "minor-ticks-color": "Color de marcas secundarias", + "tick-numbers-font": "Fuente de números de marcas", + "unit-title-settings": "Configuración del título de unidades", + "show-unit-title": "Mostrar título de unidades", + "unit-title": "Título de unidad", + "title-font": "Fuente del texto del título", + "units-settings": "Configuración de unidades", + "units-font": "Fuente del texto de unidades", + "value-box-settings": "Configuración del cuadro de valor", + "show-value-box": "Mostrar cuadro de valor", + "value-box": "Cuadro de valor", + "value-int": "Cantidad de dígitos para la parte entera del valor", + "value-text": "Texto del valor", + "value-text-shadow": "Sombra del texto del valor", + "value-font": "Fuente del texto del valor", + "rect-stroke-color-start": "Color de trazo del rectángulo - inicio del gradiente", + "rect-stroke-color-end": "Color de trazo del rectángulo - fin del gradiente", + "background-color": "Color de fondo", + "shadow-color": "Color de la sombra", + "value-box-rect-stroke-color": "Color de trazo del rectángulo del cuadro de valor", + "value-box-rect-stroke-color-end": "Color de trazo del rectángulo del cuadro de valor - fin del gradiente", + "value-box-background-color": "Color de fondo del cuadro de valor", + "value-box-shadow-color": "Color de la sombra del cuadro de valor", + "plate-settings": "Configuración de la placa", + "show-plate-border": "Borde de la placa", + "plate-color": "Color de la placa", + "needle-settings": "Configuración de la aguja", + "needle-circle-size": "Tamaño del círculo de la aguja", + "needle-color": "Color de la aguja", + "needle-color-start": "Color de la aguja - inicio del gradiente", + "needle-color-end": "Color de la aguja - fin del gradiente", + "needle-color-shadow-up": "Color de la sombra superior de la aguja", + "needle-color-shadow-down": "Sombra inferior", + "highlights-settings": "Configuración de resaltados", + "highlights-width": "Ancho de resaltados", + "highlights": "Resaltados", + "highlight-from": "Desde", + "highlight-to": "Hasta", + "highlight-color": "Color", + "no-highlights": "No hay resaltados configurados", + "add-highlight": "Agregar resaltado", + "animation-settings": "Configuración de animación", + "enable-animation": "Animación", + "animation-duration-rule": "Duración y regla de animación", + "animation-duration": "Duración de la animación", + "animation-rule": "Regla de animación", + "animation-linear": "Lineal", + "animation-quad": "Cuadrática", + "animation-quint": "Quíntica", + "animation-cycle": "Cíclica", + "animation-bounce": "Rebote", + "animation-elastic": "Elástica", + "animation-dequad": "De-cuadrática", + "animation-dequint": "De-quíntica", + "animation-decycle": "De-cíclica", + "animation-debounce": "De-rebote", + "animation-delastic": "De-elástica", + "linear-gauge-settings": "Configuración del medidor lineal", + "bar-stroke": "Trazo de la barra", + "bar-stroke-width": "Ancho del trazo de la barra", + "bar-stroke-color": "Color del trazo de la barra", + "bar-background-color": "Color de fondo de la barra - inicio del gradiente", + "bar-background-color-end": "Color de fondo de la barra - fin del gradiente", + "progress-bar-color": "Color de la barra de progreso", + "progress-bar": "Barra de progreso", + "progress-bar-color-start": "Color de la barra de progreso - inicio del gradiente", + "progress-bar-color-end": "Color de la barra de progreso - fin del gradiente", + "major-ticks-names": "Nombres de marcas principales", + "show-stroke-ticks": "Mostrar trazo de marcas", + "major-ticks-font": "Fuente de marcas principales", + "border-color": "Color del borde", + "border-width": "Ancho del borde", + "needle-circle": "Círculo de la aguja", + "needle-circle-color": "Color del círculo de la aguja", + "animation-target": "Objetivo de la animación", + "animation-target-needle": "Aguja", + "animation-target-plate": "Placa", + "common-settings": "Configuración común del medidor", + "gauge-type": "Tipo de medidor", + "gauge-type-arc": "Arco", + "gauge-type-donut": "Rosquilla", + "gauge-type-horizontal-bar": "Barra horizontal", + "gauge-type-vertical-bar": "Barra vertical", + "donut-start-angle": "Ángulo de inicio (grados)", + "bar-settings": "Configuración de la barra del medidor", + "relative-bar-width": "Ancho relativo de la barra", + "neon-glow-brightness": "Brillo del efecto neón (0-100)", + "neon-glow-brightness-hint": "0 - desactivar efecto", + "stripes-thickness": "Grosor de las franjas", + "stripes-thickness-hint": "0 - sin franjas", + "rounded-line-cap": "Extremos redondeados", + "bar-color-settings": "Configuración de color de la barra", + "use-precise-level-color-values": "Usar niveles de color precisos", + "bar-colors": "Colores de barra, de menor a mayor", + "color": "Color", + "no-bar-colors": "No hay colores de barra configurados", + "add-bar-color": "Agregar color de barra", + "from": "Desde", + "to": "Hasta", + "fixed-level-colors": "Colores de barra usando valores límite", + "gauge-title-settings": "Configuración del título del medidor", + "show-gauge-title": "Mostrar título del medidor", + "gauge-title": "Título del medidor", + "gauge-title-font": "Fuente del título del medidor", + "unit-title-and-timestamp-settings": "Configuración del título de unidad y marca de tiempo", + "show-timestamp": "Marca de tiempo", + "timestamp-format": "Formato de marca de tiempo", + "label-font": "Fuente de la etiqueta debajo del valor", + "value-settings": "Configuración del valor", + "show-value": "Mostrar texto del valor", + "min-max-settings": "Configuración de etiquetas mínimo/máximo", + "show-min-max": "Mostrar valores mínimo y máximo", + "min-max-font": "Fuente de valores mínimo y máximo", + "show-ticks": "Mostrar marcas", + "tick-width": "Ancho de las marcas", + "tick-color": "Color de las marcas", + "tick-values": "Valores de las marcas", + "no-tick-values": "No hay valores de marcas configurados", + "add-tick-value": "Agregar valor de marca", + "gauge-appearance": "Apariencia del medidor", + "units-title": "Título de unidades", + "value": "Valor", + "ticks": "Marcas", + "arrow-and-scale-color": "Color predeterminado de flecha y escala", + "scale-settings": "Configuración de la escala", + "scale": "Escala", + "scale-color": "Colores de escala", + "compass-appearance": "Apariencia de la brújula", + "label": "Etiqueta", + "labels": "Etiquetas", + "label-style": "Estilo de etiqueta", + "simple-gauge-type": "Tipo", + "gauge-bar-background": "Fondo de la barra del medidor", + "bar-color": "Color de la barra", + "min-and-max-value": "Valor mínimo y máximo", + "min-and-max-label": "Etiqueta mínima y máxima", + "font": "Fuente", + "tick-width-and-color": "Ancho y color de marcas", + "min-max-validation-text": "El valor máximo debe ser mayor que el valor mínimo" }, - "version-control": { - "version-control": "Control de Versión", - "management": "Administrador de versiones", - "search": "Buscar versiones", - "branch": "Rama", - "default": "Por defecto", - "select-branch": "Seleccionar rama", - "branch-required": "Se requiere rama", - "create-entity-version": "Versión creación de entidad", - "version-name": "Nombre de versión", - "version-name-required": "Se requiere nombre de versión", - "author": "Autor", - "export-relations": "Exportar relaciones", - "export-attributes": "Exportar atributos", - "export-credentials": "Exportar credenciales", - "entity-versions": "Versiones de entidad", - "versions": "Versiones", - "created-time": "Hora de creación", - "version-id": "ID de versión", - "no-entity-versions-text": "No se han encontrado versiones de entidad", - "no-versions-text": "No se han encontrado versiones", - "copy-full-version-id": "Copiar el ID de versión", - "create-version": "Crear versión", - "creating-version": "Creando versión... Por favor, espere", - "nothing-to-commit": "No hay cambios a publicar", - "restore-version": "Restaurar versión", - "restore-entity-from-version": "Restaurar entidad desde versión '{{versionName}}'", - "restoring-entity-version": "Restaurando versión de entidad... Por favor, espere", - "load-relations": "Cargar relaciones", - "load-attributes": "Cargar atributos", - "load-credentials": "Cargar credencialess", - "compare-with-current": "Comparar con actual", - "diff-entity-with-version": "Diff de la versión de entidad '{{versionName}}'", - "previous-difference": "Anterior diferencia", - "next-difference": "Siguiente diferencia", - "current": "Actual", - "differences": "{ count, plural, =1 {1 diferencia} other {# diferencias} }", - "create-entities-version": "Crear versión de entidades", - "default-sync-strategy": "Estrategia de sincronización por defecto", - "sync-strategy-merge": "Combinar (Merge)", - "sync-strategy-overwrite": "Sobreescribir", - "entities-to-export": "Entidades a exportar", - "entities-to-restore": "Entidades a restaurar", - "sync-strategy": "Estrategia de sincronización", - "all-entities": "Todas las entidades", - "no-entities-to-export-prompt": "Por favor, especifica las entidades a exportar", - "no-entities-to-restore-prompt": "Por favor, especifica las entidades a restaurar", - "add-entity-type": "Añadir tipo de entidad", - "remove-all": "Borrar todo", - "version-create-result": "{ added, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } añadidas.
{ modified, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } modificadas.
{ removed, plural, =0 {Ninguna entidad} =1 {1 entidad} other {# entidades} } borradas.", - "remove-other-entities": "Borrar otras entidades", - "find-existing-entity-by-name": "Buscar entidad existente por nombre", - "restore-entities-from-version": "Restaurar entidades desde la versión '{{versionName}}'", - "restoring-entities-from-version": "Restaurando entidades... Por favor, espere", - "no-entities-restored": "No se restauraron entidades", - "created": "{{created}} creadas", - "updated": "{{updated}} actualizadas", - "deleted": "{{deleted}} borradas", - "remove-other-entities-confirm-text": "Atención! Esta acción borrará permanentemente todas las entidades actuales
no presentes en la versión a restaurar.

Escribe \"remove other entities\" para confirmar.", - "auto-commit-to-branch": "auto-publicar a la rama {{ branch }}", - "default-create-entity-version-name": "{{entityName}} actualización", - "sync-strategy-merge-hint": "Crea o actualiza las entidades seleccionadas en el repositorio. Las demás entidades no serán modificadas.", - "sync-strategy-overwrite-hint": "Crea o actualiza las entidades seleccionadas en el repositorio. Las demás entidades serán borradas.", - "device-credentials-conflict": "Fallo al cargar el dispositivo con ID externo {{entityId}}
debido a que las mismas credenciales están ya presentes en la base de datos para otro dispositivo.
Por favor, considera desactivar el ajuste cargar credenciales en el formulario de restauración.", - "missing-referenced-entity": "Fallo al cargar {{sourceEntityTypeName}} con ID externo {{sourceEntityId}}
porque hace referencia al tipo de entidad {{targetEntityTypeName}} con el ID {{targetEntityId}}.", - "runtime-failed": "Fallo: {{message}}", - "auto-commit-settings-read-only-hint": "Los ajustes de Auto-commit no funcionan, si la opción de solo lectura está activada en los ajustes del repositorio." + "gpio": { + "pin": "Pin", + "label": "Etiqueta", + "row": "Fila", + "column": "Columna", + "color": "Color", + "panel-settings": "Configuración del panel", + "background-color": "Color de fondo", + "gpio-switches": "Interruptores GPIO", + "no-gpio-switches": "No hay interruptores GPIO configurados", + "add-gpio-switch": "Agregar interruptor GPIO", + "gpio-status-request": "Solicitud de estado GPIO", + "method-name": "Nombre del método", + "method-body": "Cuerpo del método", + "gpio-status-change-request": "Solicitud de cambio de estado GPIO", + "parse-gpio-status-function": "Función para analizar estado GPIO", + "gpio-leds": "LEDs GPIO", + "no-gpio-leds": "No hay LEDs GPIO configurados", + "add-gpio-led": "Agregar LED GPIO" }, - "widget": { - "widget-library": "Bibloteca de Widgets", - "widget-bundle": "Paquetes de Widgets", - "all-bundles": "Todos los paquetes", - "select-widgets-bundle": "Seleccionar paquete de widgets", - "widgets": "Widgets", - "all-widgets": "Todos los widgets", - "widget": "Widget", - "select-widget": "Seleccionar widget", - "no-widgets-matching": "No se han encontrado widgets que coincidan con '{{entity}}'.", - "no-widgets": "No hay widgets todavía", - "no-widgets-text": "No se han encontrado widgets", - "management": "Gestión de Widgets", - "editor": "Editor de widgets", - "widget-type-not-found": "Problema al cargar la configuración del widget.
Probablemente asociado\n El tipo de widget fue eliminado.", - "widget-type-load-error": "El widget no pudo ser cargado debido a estos errores:", - "remove": "Eliminar widget", - "delete": "Borrar widget", - "edit": "Editar widget", - "edit-widget-type": "Editar tipo de widget", - "remove-widget-title": "¿Eliminar el widget '{{widgetTitle}}'?", - "remove-widget-text": "Atención, tras la confirmación el widget será eliminado y toda la información relacionada será irrecuperable..", - "timeseries": "Series de tiempo", - "search-data": "Buscar datos", - "no-data-found": "No se han encontrado datos", - "latest": "Últimos valores", - "rpc": "Widget de control", - "alarm": "Widget de Alarma", - "static": "Widget estático", - "select-widget-type": "Seleccionar tipo de widget", - "missing-widget-title-error": "El titulo del widget debe ser especificado!", - "widget-saved": "Widget guardado", - "unable-to-save-widget-error": "Imposible guardar widget! Tiene errores!", - "save": "Guardar widget", - "saveAs": "Guardar widget como", - "move": "Mover widget", - "save-widget-type-as": "Guardar tipo de widget como", - "save-widget-type-as-text": "Por favor, ingrese un nuevo titulo y/o seleccione un paquete de destino.", - "toggle-fullscreen": "Cambiar a pantalla completa", - "run": "Ejecutar widget", - "widget-title": "Título del widget", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256", - "system": "Sistema", - "type": "Tipo de widget", - "resources": "Recursos", - "resource-url": "URL JavaScript/CSS", - "remove-resource": "Eliminar recurso", - "add-resource": "Agregar recurso", - "html": "HTML", - "tidy": "Tidy", - "css": "CSS", - "settings-schema": "Esquema de configuración", - "datakey-settings-schema": "Esquema de configuración de clave de datos", - "latest-datakey-settings-schema": "Esquema de últimos valores", - "widget-settings": "Ajustes de widget", - "description": "Descripción", - "tags": "Etiquetas", - "image-preview": "Imagen previsualización", - "settings-form-selector": "Selector formulario de ajustes", - "data-key-settings-form-selector": "Selector formulario de ajustes claves de datos", - "all": "Todos", - "actual": "Actuales", - "deprecated": "Obsoletos", - "has-basic-mode": "Tienen modo básico", - "basic-mode-form-selector": "Selector formulario en modo básico", - "basic-mode": "Básico", - "advanced-mode": "Avanzado", - "latest-data-key-settings-form-selector": "Selector formulario de últimos valores", - "javascript": "Javascript", - "js": "JS", - "widget-types": "Tipos de Widget", - "delete-widget-title": "¿Eliminar de widget '{{widgetName}}'?", - "delete-widget-text": "Atención, tras la confirmación será eliminado y la información relacionada será irrecuperable.", - "delete-widgets-title": "¿Eliminar { count, plural, =1 {1 de widget} other {# de widgets} }?", - "delete-widgets-text": "Atención, tras la confirmación los de widget seleccionados serán eliminados y la información relacionada será irrecuperable.", - "delete-widget": "Borrar de widget", - "widget-template-load-failed-error": "Error al cargar la plantilla del widget!", - "details": "Detalles", - "widget-details": "Detalles de widget", - "add": "Agregar Widget", - "search-widget": "Buscar de widget", - "selected-widgets": "{ count, plural, =1 {1 de widget} other {# de widgets} } seleccionados", - "undo": "Deshacer cambios", - "export": "Exportar widget", - "no-data": "No hay datos para mostrar en widget", - "data-overflow": "El widget muestra {{count}} de {{total}} entidades", - "alarm-data-overflow": "El widget muestra alarmas para {{allowedEntities}} entidades (máximo permitido) de {{totalEntities}} entidades", - "search": "Buscar widget", - "filter": "Filtro tipo de widget", - "loading-widgets": "Cargando widgets...", - "widget-template-error": "Plantilla HTML inválida." + "html-card": { + "html": "HTML", + "css": "CSS" }, - "widget-action": { - "header-button": "Botón de encabezado widget", - "open-dashboard-state": "Navegar a un nuevo estado de panel", - "update-dashboard-state": "Actualizar el estado del panel actual", - "open-dashboard": "Navegar hacia otro panel", - "custom": "Acción personalizada", - "custom-pretty": "Acción personalizada (con plantilla HTML)", - "custom-pretty-error-title": "Diálogo de error personalizado", - "custom-pretty-template-error": "Plantilla de diálogo de error inválida.", - "custom-pretty-controller-error": "Ha ocurrido un error mientras se evaluaba la función.", - "mobile-action": "Acción en dispositivo móvil", - "target-dashboard-state": "Estado de panel de destino", - "target-dashboard-state-required": "Se requiere estado de panel de destino", - "set-entity-from-widget": "Establecer entidad desde widget", - "target-dashboard": "Panel de destino", - "open-right-layout": "Abrir diseño de panel (derecho)(vista móvil)", - "state-display-type": "Opciones de display de panel", - "open-normal": "Normal", - "open-in-separate-dialog": "Abrir en un diálogo separado", - "open-in-popover": "Abrir en popover", - "dialog-title": "Título del diálogo", - "dialog-hide-dashboard-toolbar": "Ocultar barra de herramientas en el diálogo", - "dialog-width": "Ancho de diálogo en porcentaje relativo al ancho del viewport", - "dialog-height": "Alto de diálogo en porcentaje relativo al alto del viewport", - "dialog-size-range-error": "El tamaño del diálogo debe ser entre un rango de 1 a 100", - "popover-preferred-placement": "Ubicación preferida popover", - "popover-placement-top": "Superior", - "popover-placement-topLeft": "Superior izquierda", - "popover-placement-topRight": "Superior derecha", - "popover-placement-right": "Derecha", - "popover-placement-rightTop": "Derecha superior", - "popover-placement-rightBottom": "Derecha inferior", - "popover-placement-bottom": "Inferior", - "popover-placement-bottomLeft": "Inferior izquierda", - "popover-placement-bottomRight": "Inferior derecha", - "popover-placement-left": "Izquierda", - "popover-placement-leftTop": "Izquierda superior", - "popover-placement-leftBottom": "Izquierda inferior", - "popover-hide-on-click-outside": "Ocultar en click fuera del popover", - "popover-hide-dashboard-toolbar": "Ocultar caja de herramientas en popover", - "popover-width": "Ancho de popover", - "popover-height": "Altura de popover", - "popover-style": "Estilo de popover", - "open-new-browser-tab": "Abrir en una nueva pestaña", - "mobile": { - "action-type": "Tipo de acción móvil", - "action-type-required": "Tipo de acción móvil requerida", - "take-picture-from-gallery": "Tomar foto de galería", - "take-photo": "Tomar foto", - "map-direction": "Abrir indicaciones en mapa", - "map-location": "Abrir localización en mapa", - "scan-qr-code": "Escanear código QR", - "make-phone-call": "Hacer llamada telefónica", - "get-location": "Obtener localización del teléfono", - "take-screenshot": "Obtener captura de pantalla" - } + "input-widgets": { + "attribute-not-allowed": "El parámetro de atributo no se puede usar en este widget", + "blocked-location": "La geolocalización está bloqueada en tu navegador", + "claim-device": "Reclamar dispositivo", + "claim-failed": "¡No se pudo reclamar el dispositivo!", + "claim-not-found": "¡Dispositivo no encontrado!", + "claim-successful": "¡El dispositivo fue reclamado con éxito!", + "date": "Fecha", + "device-name": "Nombre del dispositivo", + "device-name-required": "El nombre del dispositivo es obligatorio", + "discard-changes": "Descartar cambios", + "entity-attribute-required": "Se requiere el atributo de la entidad", + "entity-coordinate-required": "Se requieren ambos campos, latitud y longitud", + "entity-timeseries-required": "Se requiere la serie temporal de la entidad", + "get-location": "Obtener ubicación actual", + "invalid-date": "Fecha inválida", + "latitude": "Latitud", + "longitude": "Longitud", + "min-value-error": "El valor mínimo es {{value}}", + "max-value-error": "El valor máximo es {{value}}", + "not-allowed-entity": "La entidad seleccionada no puede tener atributos compartidos", + "no-attribute-selected": "No se ha seleccionado ningún atributo", + "no-datakey-selected": "No se ha seleccionado ninguna clave de datos", + "no-coordinate-specified": "No se ha especificado la clave de datos para latitud/longitud", + "no-entity-selected": "No se ha seleccionado ninguna entidad", + "no-image": "Sin imagen", + "no-support-geolocation": "Tu navegador no admite geolocalización", + "no-support-web-camera": "Tu navegador no admite cámaras", + "enable-https-use-widget": "Activa HTTPS para usar este widget", + "no-found-your-camera": "No se pudo encontrar tu cámara", + "no-permission-camera": "Permiso denegado por el usuario / Este sitio no tiene permiso para usar la cámara", + "no-timeseries-selected": "No se ha seleccionado ninguna serie temporal", + "secret-key": "Clave secreta", + "secret-key-required": "La clave secreta es obligatoria", + "switch-attribute-value": "Cambiar el valor del atributo de la entidad", + "switch-camera": "Cambiar cámara", + "switch-timeseries-value": "Cambiar el valor de la serie temporal de la entidad", + "take-photo": "Tomar foto", + "time": "Hora", + "timeseries-not-allowed": "El parámetro de serie temporal no se puede usar en este widget", + "update-failed": "Actualización fallida", + "update-successful": "Actualización exitosa", + "update-attribute": "Actualizar atributo", + "update-timeseries": "Actualizar serie temporal", + "value": "Valor", + "general-settings": "Configuración general", + "widget-title": "Título del widget", + "claim-button-label": "Etiqueta del botón de reclamación", + "show-secret-key-field": "Mostrar campo de 'Clave secreta'", + "labels-settings": "Configuración de etiquetas", + "show-labels": "Mostrar etiquetas", + "device-name-label": "Etiqueta para el campo de nombre del dispositivo", + "secret-key-label": "Etiqueta para el campo de clave secreta", + "messages-settings": "Configuración de mensajes", + "claim-device-success-message": "Mensaje de éxito al reclamar el dispositivo", + "claim-device-not-found-message": "Mensaje cuando el dispositivo no se encuentra", + "claim-device-failed-message": "Mensaje de error al reclamar el dispositivo", + "claim-device-name-required-message": "Mensaje de error 'Se requiere nombre del dispositivo'", + "claim-device-secret-key-required-message": "Mensaje de error 'Se requiere clave secreta'", + "show-label": "Mostrar etiqueta", + "label": "Etiqueta", + "required": "Requerido", + "required-error-message": "Mensaje de error 'Requerido'", + "show-result-message": "Mostrar mensaje de resultado", + "integer-field-settings": "Configuración del campo entero", + "min-value": "Valor mínimo", + "max-value": "Valor máximo", + "double-field-settings": "Configuración del campo doble", + "text-field-settings": "Configuración del campo de texto", + "min-length": "Longitud mínima", + "max-length": "Longitud máxima", + "checkbox-settings": "Configuración de la casilla de verificación", + "true-label": "Etiqueta cuando está marcado", + "false-label": "Etiqueta cuando no está marcado", + "image-input-settings": "Configuración de entrada de imagen", + "display-preview": "Mostrar vista previa", + "display-clear-button": "Mostrar botón de limpiar", + "display-apply-button": "Mostrar botón de aplicar", + "display-discard-button": "Mostrar botón de descartar", + "datetime-field-settings": "Configuración del campo fecha/hora", + "display-time-input": "Mostrar entrada de hora", + "latitude-key-name": "Nombre de la clave de latitud", + "longitude-key-name": "Nombre de la clave de longitud", + "show-get-location-button": "Mostrar botón 'Obtener ubicación actual'", + "use-high-accuracy": "Usar alta precisión", + "location-fields-settings": "Configuración de campos de ubicación", + "latitude-label": "Etiqueta para latitud", + "longitude-label": "Etiqueta para longitud", + "input-fields-alignment": "Alineación de los campos de entrada", + "input-fields-alignment-column": "Columna (por defecto)", + "input-fields-alignment-row": "Fila", + "layout": "Disposición", + "row-gap": "Espacio entre filas en píxeles", + "column-gap": "Espacio entre columnas en píxeles", + "latitude-field-required": "Campo de latitud requerido", + "longitude-field-required": "Campo de longitud requerido", + "attribute-settings": "Configuración del atributo", + "widget-mode": "Modo del widget", + "widget-mode-update-attribute": "Actualizar atributo", + "widget-mode-update-timeseries": "Actualizar serie temporal", + "attribute-scope": "Ámbito del atributo", + "attribute-scope-server": "Atributo del servidor", + "attribute-scope-shared": "Atributo compartido", + "value-required": "Valor requerido", + "image-settings": "Configuración de imagen", + "image-format": "Formato de imagen", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "Calidad de la imagen que usa compresión con pérdida como JPEG y WEBP", + "max-image-width": "Ancho máximo de la imagen", + "max-image-height": "Altura máxima de la imagen", + "action-buttons": "Botones de acción", + "show-action-buttons": "Mostrar botones de acción", + "update-all-values": "Actualizar todos los valores, no solo los modificados", + "save-button-label": "Etiqueta del botón 'GUARDAR'", + "reset-button-label": "Etiqueta del botón 'DESHACER'", + "group-settings": "Configuración del grupo", + "show-group-title": "Mostrar título para grupo de campos relacionados con diferentes entidades", + "group-title": "Título del grupo", + "fields-alignment": "Alineación de campos", + "fields-alignment-row": "Fila (por defecto)", + "fields-alignment-column": "Columna", + "fields-in-row": "Número de campos en la fila", + "option-value": "Valor (escriba 'null' para crear opción vacía)", + "option-label": "Etiqueta", + "hide-input-field": "Ocultar campo de entrada", + "datakey-type": "Tipo de clave de datos", + "datakey-type-server": "Atributo del servidor (por defecto)", + "datakey-type-shared": "Atributo compartido", + "datakey-type-timeseries": "Serie temporal", + "datakey-value-type": "Tipo de valor de la clave de datos", + "datakey-value-type-string": "Cadena", + "datakey-value-type-double": "Doble", + "datakey-value-type-integer": "Entero", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "Booleano (Casilla)", + "datakey-value-type-boolean-switch": "Booleano (Interruptor)", + "datakey-value-type-date-time": "Fecha y hora", + "datakey-value-type-date": "Fecha", + "datakey-value-type-time": "Hora", + "datakey-value-type-select": "Seleccionar", + "datakey-value-type-radio": "Radio", + "datakey-value-type-color": "Color", + "value-is-required": "El valor es obligatorio", + "ability-to-edit-attribute": "Capacidad para editar atributo", + "ability-to-edit-attribute-editable": "Editable (por defecto)", + "ability-to-edit-attribute-disabled": "Deshabilitado", + "ability-to-edit-attribute-readonly": "Solo lectura", + "disable-on-datakey-name": "Deshabilitar si el valor de otra clave de datos es falso (especifique el nombre)", + "field-appearance": "Apariencia del campo", + "appearance-fill": "Relleno", + "appearance-outline": "Contorno", + "subscript-sizing": "Tamaño del subíndice", + "subscript-sizing-fixed": "Fijo", + "subscript-sizing-dynamic": "Dinámico", + "slide-toggle-settings": "Configuración de alternancia deslizante", + "slide-toggle-label-position": "Posición de la etiqueta", + "slide-toggle-label-position-after": "Después", + "slide-toggle-label-position-before": "Antes", + "select-options": "Opciones de selección", + "no-select-options": "No se han configurado opciones de selección", + "add-select-option": "Agregar opción de selección", + "numeric-field-settings": "Configuración del campo numérico", + "step-interval": "Intervalo de paso entre valores", + "error-messages": "Mensajes de error", + "min-value-error-message": "Mensaje de error 'Valor mínimo'", + "max-value-error-message": "Mensaje de error 'Valor máximo'", + "invalid-date-error-message": "Mensaje de error 'Fecha inválida'", + "invalid-JSON-error-message": "Mensaje de error 'JSON inválido'", + "icon-settings": "Configuración de icono", + "dialog-editor-settings": "Configuración del editor de diálogos", + "use-custom-icon": "Usar icono personalizado", + "input-cell-icon": "Icono para mostrar antes del campo de entrada", + "value-conversion-settings": "Configuración de conversión de valores", + "get-value-settings": "Configuración para obtener valor", + "use-get-value-function": "Usar función getValue", + "get-value-function": "Función getValue", + "set-value-settings": "Configuración para establecer valor", + "use-set-value-function": "Usar función setValue", + "set-value-function": "Función setValue", + "json-invalid": "El valor JSON tiene un formato inválido", + "title": "Título", + "cancel-button-label": "Etiqueta del botón 'CANCELAR'", + "radio-button-settings": "Configuración de botón de opción", + "color": "Color", + "columns": "Columnas", + "radio-options": "Opciones de radio", + "no-radio-options": "No se han configurado opciones de radio", + "add-radio-option": "Agregar opción de radio", + "radio-label-position": "Posición de la etiqueta", + "radio-label-position-before": "Antes", + "radio-label-position-after": "Después" }, - "widgets-bundle": { - "current": "Paquete actual", - "widgets-bundles": "Paquete de Widgets", - "widgets-bundle-widgets": "Widgets del paquete de widgets", - "add": "Agregar paquete de widgets", - "delete": "Eliminar paquete de widgets", - "title": "Título", - "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256", - "description": "Descripción", - "image-preview": "Imagen previsualización", - "add-widgets-bundle-text": "Agregar nuevo paquete de widgets", - "no-widgets-bundles-text": "Ningún paquete de widgets encontrado", - "empty": "Paquete de widgets vacío.", - "details": "Detalles", - "widgets-bundle-details": "Detalles del paquete de Widgets", - "delete-widgets-bundle-title": "¿Eliminar el paquete de widgets '{{widgetsBundleTitle}}'?", - "delete-widgets-bundle-text": "Atención, tras la confirmación todos los paquetes seleccionados serán eliminados y su información relacionada será irrecuperable.", - "delete-widgets-bundles-title": "¿Eliminar { count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} }?", - "delete-widgets-bundles-action-title": "Eliminar { count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} }", - "delete-widgets-bundles-text": "Atención, tras la confirmación todos los paquetes seleccionados serán eliminados y la información relacionada será irrecuperable.", - "no-widgets-bundles-matching": "Ningún paquete '{{widgetsBundle}}' encontrado.", - "widgets-bundle-required": "Paquete de widget requerido.", - "system": "Widget de Sistema", - "import": "Importar paquete de widgets", - "export": "Exportar paquete de widgets", - "export-widgets-bundle-widgets-prompt": "Incluir de widget en los datos exportados (de lo contrario, solo se exportarán los FQN de los tipos de widget a los que hace referencia)", - "export-failed-error": "Imposible exportar paquete de widgets: {{error}}", - "create-new-widgets-bundle": "Crear nuevo paquete de widgets", - "widgets-bundle-file": "Archivo de paquete de widgets", - "invalid-widgets-bundle-file-error": "Imposible importar paquete de widgets: Estructura de datos inválida.", - "search": "Buscar paquete de widgets", - "selected-widgets-bundles": "{ count, plural, =1 {1 paquete de widgets} other {# paquetes de widgets} } seleccionados", - "open-widgets-bundle": "Abrir paquete de widgets", - "loading-widgets-bundles": "Cargando paquete de widgets..." + "invalid-qr-code-text": "Texto de entrada no válido para el código QR. La entrada debe ser de tipo cadena", + "qr-code": { + "use-qr-code-text-function": "Usar función de texto del código QR", + "qr-code-text-pattern": "Patrón de texto del código QR (por ejemplo, '${entityName} | ${keyName} - algún texto.')", + "qr-code-text-pattern-hint": "El patrón de texto del código QR utiliza el valor de la primera clave encontrada en las entidades del alias de entidad.", + "qr-code-text-pattern-required": "Se requiere el patrón de texto del código QR.", + "qr-code-text-function": "Función de texto del código QR" }, - "widget-config": { - "data": "Datos", - "settings": "Ajustes", - "advanced": "Avanzado", - "appearance": "Apariencia", - "widget-card": "Tarjeta de widget", - "mobile": "Móvil", - "title": "Título", - "title-tooltip": "Tooltip Título", - "general-settings": "Ajustes generales", - "display-title": "Mostrar titulo", - "card-title": "Título de la tarjeta", - "drop-shadow": "Sombra", - "enable-fullscreen": "Habilitar pantalla completa", - "background-color": "Color de fondo", - "text-color": "Color del texto", - "border-radius": "Radio del borde", - "padding": "Relleno", - "margin": "Margen", - "widget-style": "Estilo de widget", - "widget-css": "CSS de widget", - "title-style": "Estilo de título", - "mobile-mode-settings": "Ajustes móvil.", - "order": "Orden", - "height": "Altura", - "mobile-hide": "Ocultar widget en modo móvil", - "desktop-hide": "Ocultar widget en modo escritorio", - "units": "Caracter especial a mostrar en el siguiente valor", - "units-by-default": "Unidades por defecto", - "decimals": "Decimales", - "decimals-by-default": "Decimales por defecto", - "default-data-key-parameter-hint": "Este parámetro aplica a todos los valores del widget a menos que la configuración de la clave de datos lo anule", - "units-short": "Unidades", - "decimals-short": "Decimales", - "decimals-suffix": "decimales", - "timewindow": "Ventana de tiempo", - "use-dashboard-timewindow": "Usar ventana de tiempo del Panel", - "use-widget-timewindow": "Usar ventana de tiempo del widget", - "display-timewindow": "Mostrar ventana de tiempo", - "legend": "Leyenda", - "display-legend": "Mostrar leyenda", - "datasources": "Origen de datos", - "datasource": "Origen de dato", - "maximum-datasources": "Un máximo de { count, plural, =1 {1 set de datos es permitido.} other {# set de datos son permitidos} }", - "timeseries-key-error": "Se debe especificar al menos un datakey tipo timeseries", - "datasource-type": "Tipo", - "datasource-parameters": "Parámetros", - "remove-datasource": "Eliminar set de datos", - "add-datasource": "Agregar set de datos", - "target-device": "Dispositivo destino", - "alarm-source": "Origen de alarmas", - "actions": "Acciones", - "action": "Acción", - "add-action": "Añadir acción", - "search-actions": "Buscar acciones", - "no-actions-text": "No se encontraron actiones", - "action-source": "Origen de acción", - "action-source-required": "Origen de acción requerido.", - "action-name": "Nombre", - "action-name-required": "Nombre de accion requerido.", - "action-name-not-unique": "Existe una acción con el mismo nombre.\nEl nombre de acción debe ser único dentro de la misma fuente de acción (origen).", - "action-icon": "Icono", - "show-hide-action-using-function": "Mostrar/Ocultar acción usando función", - "action-type": "Tipo", - "action-type-required": "Tipo de acción requerido.", - "edit-action": "Editar acción", - "delete-action": "Borrar acción", - "delete-action-title": "Borrar acción de widget", - "delete-action-text": "Eliminar la acción de widget con el nombre '{{actionName}}'?", - "title-icon": "Título de icono", - "display-icon": "Mostrar título de icono", - "card-icon": "Card icon", - "icon-color": "Color del icono", - "icon-size": "Tamaño del icono", - "advanced-settings": "Ajustes avanzados", - "data-settings": "Ajustes de datos", - "limits": "Limits", - "no-data-display-message": "\"No hay datos que mostrar\" mensaje alternativo", - "data-page-size": "Nº Máximo de entidades por origen de datos", - "settings-component-not-found": "Componente de ajustes no encontrado para el selector '{{selector}}'", - "preview": "Previsualizar", - "set": "Establecer", - "set-message": "Establecer mensaje", - "advanced-title-style": "Estilo de título avanzado", - "card-style": "Estilo de tarjeta", - "text": "Texto", - "background": "Fondo", - "advanced-widget-style": "Estilo avanzado de widget", - "card-buttons": "Botones de tarjeta", - "show-card-buttons": "Mostrar botones", - "card-border-radius": "Radio del borde", - "card-appearance": "Apariencia", - "color": "Color" + "label-widget": { + "label-pattern": "Patrón", + "label-pattern-hint": "Sugerencia: por ejemplo, 'Texto ${keyName} unidades.' o ${#<key index>} unidades'", + "label-pattern-required": "Se requiere un patrón", + "label-position": "Posición (porcentaje relativo al fondo)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Color de fondo", + "font-settings": "Configuración de fuente", + "background-image": "Imagen de fondo", + "labels": "Etiquetas", + "no-labels": "No hay etiquetas configuradas", + "add-label": "Agregar etiqueta" }, - "widget-type": { - "import": "Importar tipo de widget", - "export": "Exportar tipo de widget", - "export-failed-error": "Imposible exportar de widget: {{error}}", - "widget-file": "Archivo de widget", - "invalid-widget-type-file-error": "No se puede importar tipo de widget: Estructura de datos del tipo de widget es inválida." + "navigation": { + "title": "Título", + "navigation-path": "Ruta de navegación", + "filter-type": "Tipo de filtro", + "filter-type-all": "Todos los elementos", + "filter-type-include": "Incluir elementos", + "filter-type-exclude": "Excluir elementos", + "items": "Elementos", + "enter-urls-to-filter": "Introduzca las URLs para filtrar..." }, - "widgets": { - "background": { - "background": "Fondo", - "background-settings": "Ajustes de fondo", - "background-type-image": "Subir imagen", - "background-type-image-url": "URL imagen", - "background-type-color": "Color sólido", - "image-url": "URL imagen", - "overlay": "Capa superpuesta", - "enable-overlay": "Activar capa", - "blur": "Difuminar", - "preview": "Previsualizar" - }, - "battery-level": { - "layout": "Diseño", - "layout-vertical-solid": "Vertical. Sólido", - "layout-horizontal-solid": "Horizontal. Sólido", - "layout-vertical-divided": "Vertical. Dividido", - "layout-horizontal-divided": "Horizontal. Dividido", - "icon": "Icono", - "value": "Valor", - "auto-scale": "Auto escalar", - "battery-level-color": "Color del nivel de batería", - "battery-shape-color": "Color de la forma", - "battery-level-card-style": "Estilo de tarjeta batería" - }, - "chart": { - "common-settings": "Ajustes comunes", - "enable-stacking-mode": "Activar modo de apilamiento (stacking)", - "selection": "Selección de rango de tiempo", - "enable-selection-mode": "Habilitar modo de selección", - "line-shadow-size": "Tamaño de sombra (línea)", - "display-smooth-lines": "Mostrar líneas suaves (curvas)", - "default-bar-width": "Ancho de barra por defecto para datos no agregados (millisegundos)", - "bar-alignment": "Alineación barra", - "bar-alignment-left": "Izquierda", - "bar-alignment-right": "Derecha", - "bar-alignment-center": "Centro", - "default-font": "Fuente por defecto", - "default-font-size": "Tamaño de fuente por defecto", - "default-font-color": "Color de fuente por defecto", - "thresholds-line-width": "Ancho de línea por defecto para todos los umbrales", - "tooltip-settings": "Ajustes de sugerencias (tooltip)", - "tooltip": "Tooltip (sugerencias)", - "show-tooltip": "Mostrar sugerencias", - "hover-individual-points": "Hover sobre puntos individuales", - "show-cumulative-values": "Mostrar valores acumulados en modo apilado", - "hide-zero-false-values": "Ocultar valores cero/false en sugerencias", - "tooltip-value-format-function": "Función de formateo de valores en sugerencias (tooltip)", - "grid-settings": "Ajustes de cuadrícula", - "show-vertical-lines": "Mostrar líneas verticales", - "show-horizontal-lines": "Mostrar líneas horizontales", - "grid-outline-border-width": "Ancho de cuadrícula/contorno en px", - "primary-color": "Color primario", - "background-color": "Color de fondo", - "ticks-color": "Color de ticks", - "xaxis-settings": "Ajustes eje X", - "axis-title": "Título de eje", - "xaxis-tick-labels-settings": "Ajustes de etiquetas en ticks eje X", - "show-tick-labels": "Mostrar etiquetas en ticks", - "yaxis-settings": "Ajustes eje Y", - "min-scale-value": "Valor mínimo en la escala", - "max-scale-value": "Valor máximo en la escala", - "yaxis-tick-labels-settings": "Ajustes de etiquetas en ticks eje Y", - "tick-step-size": "Tamaño de paso entre ticks", - "number-of-decimals": "Número de decimales", - "ticks-formatter-function": "Función de formateo de ticks", - "comparison-settings": "Ajustes de comparación", - "enable-comparison": "Activar comparación", - "time-for-comparison": "Período de comparación", - "time-for-comparison-previous-interval": "Intervalo anterior (por defecto)", - "time-for-comparison-days": "Hace un día", - "time-for-comparison-weeks": "Hace una semana", - "time-for-comparison-months": "Hace un mes", - "time-for-comparison-years": "Hace un año", - "time-for-comparison-custom-interval": "Intervalo personalizado", - "custom-interval-value": "Valor de intervalo personalizado (ms)", - "comparison-x-axis-settings": "Ajustes comparación eje X", - "axis-position": "Posición eje", - "axis-position-top": "Superior (por defecto)", - "axis-position-bottom": "Inferior", - "custom-legend-settings": "Ajustes leyenda", - "enable-custom-legend": "Activar leyenda personalizada (habilita poder usar valores de atributos/timeseries en las etiquetas)", - "key-name": "Nombre clave", - "key-name-required": "Nombre clave requerido", - "key-type": "Tipo clave", - "key-type-attribute": "Atributo", - "key-type-timeseries": "Timeseries", - "label-keys-list": "Lista de claves para usar en etiquetas", - "no-label-keys": "No hay claves configuradas", - "add-label-key": "Añadir nueva clave", - "line-width": "Ancho de línea", - "color": "Color", - "data-is-hidden-by-default": "Ocultar datos por defecto", - "disable-data-hiding": "Desactivar ocultación de datos", - "remove-from-legend": "Quitar clave de la leyenda", - "exclude-from-stacking": "Excluir del modo apilado(disponible en el modo \"Apilado\")", - "line-settings": "Ajustes de línea", - "show-line": "Mostrar línea", - "fill-line": "Rellenar línea", - "fill-line-opacity": "Opacidad de línea", - "points-settings": "Ajustes de puntos", - "show-points": "Mostrar puntos", - "points-line-width": "Ancho de línea en puntos", - "points-radius": "Radio de los puntos", - "point-shape": "Forma del punto", - "point-shape-circle": "Círculo", - "point-shape-cross": "Cruz", - "point-shape-diamond": "Diamante", - "point-shape-square": "Cuadrado", - "point-shape-triangle": "Triángulo", - "point-shape-custom": "Función personalizada", - "point-shape-draw-function": "Función de forma del punto", - "show-separate-axis": "Mostrar eje separado", - "axis-position-left": "Izquieda", - "axis-position-right": "Derecha", - "thresholds": "Umbrales", - "no-thresholds": "No hay umbrales configurados", - "add-threshold": "Añadir nuevo umbral", - "show-values-for-comparison": "Mostrar valores históricos para su comparación", - "comparison-values-label": "Etiqueta de valores históricos", - "comparison-line-color": "Color de línea en comparación", - "threshold-settings": "Ajustes de umbrales", - "use-as-threshold": "Usar valor de clave como umbral", - "threshold-line-width": "Ancho de línea (para umbral)", - "threshold-color": "Color umbral", - "common-pie-settings": "Ajustes comunes diagrama de sectores", - "radius": "Radio", - "inner-radius": "Radio interior", - "tilt": "Inclinación", - "common-pie-settings-range-error": "El valor debe estar en el rango de 0 a 1", - "stroke-settings": "Ajustes de trazo", - "width-pixels": "Ancho (pixels)", - "show-labels": "Mostrar etiquetas", - "animation-settings": "Ajustes de animación", - "animated-pie": "Activar animación (experimental)", - "border-settings": "Ajustes de bordes", - "border-width": "Ancho de borde", - "border-color": "Color de borde", - "legend-settings": "Ajustes de leyenda", - "display-legend": "Mostrar leyenda", - "labels-font-color": "Color de texto en leyenda", - "series": "Series", - "add-series": "Añadir series", - "series-settings": "Ajustes de series", - "remove-series": "Eliminar serie", - "no-series": "No hay series configuradas", - "no-series-error": "Al menos se debe especificar una serie", - "chart-appearance": "Apariencia del gráfico", - "vertical-grid-lines": "Líneas de cuadrícula verticales", - "horizontal-grid-lines": "Líneas de cuadrícula horizontales", - "chart-background": "Fondo del gráfico", - "grid-lines-color": "Color de cuadrícula", - "border": "Borde", - "axis": "Eje", - "vertical-axis": "Eje vertical", - "ticks": "Ticks", - "horizontal-axis": "Eje horizontal" - }, - "color": { - "color-settings": "Ajustes de color", - "color-type-constant": "Constante", - "color-type-range": "Rango", - "color-type-function": "Función", - "color": "Color", - "value-range": "Rango de valor", - "from": "Desde", - "to": "A", - "color-function": "Función de color" - }, - "dashboard-state": { - "dashboard-state-settings": "Ajustes estado de panel", - "dashboard-state": "Id de estado panel", - "autofill-state-layout": "Auto-rellenar altura por defecto (obtenida del estado)", - "default-margin": "Márgen entre widgets por defecto", - "default-background-color": "Color de fondo por defecto", - "sync-parent-state-params": "Sincronizar parámetros de estado con el panel padre" - }, - "date-range-navigator": { - "date-range-picker-settings": "Ajustes del selector de fechas", - "hide-date-range-picker": "Ocultar el selector de fechas", - "picker-one-panel": "Selector de fechas para un panel", - "picker-auto-confirm": "Auto-confirmar en selector de fechas", - "picker-show-template": "Mostrar plantilla de selector de fechas", - "first-day-of-week": "Primer día de la semana", - "interval-settings": "Ajustes de intervalo", - "hide-interval": "Ocultar intervalo", - "initial-interval": "Intervalo inicial", - "interval-hour": "Hora", - "interval-day": "Día", - "interval-week": "Semana", - "interval-two-weeks": "2 semanas", - "interval-month": "Mes", - "interval-three-months": "3 meses", - "interval-six-months": "6 meses", - "step-settings": "Ajustes de paso", - "hide-step-size": "Ocultar tamaño de paso", - "initial-step-size": "Tamaño de paso inicial", - "hide-labels": "Ocultar etiquetas", - "use-session-storage": "Usar almacenamiento de sesión", - "localizationMap": { - "Sun": "Dom.", - "Mon": "Lun.", - "Tue": "Mar.", - "Wed": "Mié", - "Thu": "Jue.", - "Fri": "Vie.", - "Sat": "Sáb.", - "Jan": "Ene.", - "Feb": "Feb.", - "Mar": "Mar.", - "Apr": "Abr.", - "May": "May.", - "Jun": "Jun.", - "Jul": "Jul.", - "Aug": "Ago.", - "Sep": "Sept.", - "Oct": "Oct.", - "Nov": "Nov.", - "Dec": "Dic.", - "January": "Enero", - "February": "Febrero", - "March": "Marzo", - "April": "Abril", - "June": "Junio", - "July": "Julio", - "August": "Agosto", - "September": "Septiembre", - "October": "Octubre", - "November": "Noviembre", - "December": "Diciembre", - "Custom Date Range": "Intervalo de fechas personalizado", - "Date Range Template": "Plantilla de rango de fechas", - "Today": "Hoy", - "Yesterday": "Ayer", - "This Week": "Esta semana", - "Last Week": "La semana pasada", - "This Month": "Este mes", - "Last Month": "El mes pasado", - "Year": "Año", - "This Year": "Este año", - "Last Year": "Último", - "Date picker": "Selector de fecha", - "Hour": "Hora", - "Day": "Día", - "Week": "Semana", - "2 weeks": "2 Semanas", - "Month": "Mes", - "3 months": "3 Meses", - "6 months": "6 Meses", - "Custom interval": "Intervalo personalizado", - "Interval": "Intervalo", - "Step size": "Número de pasos", - "Ok": "Ok" - } - }, - "entities-hierarchy": { - "hierarchy-data-settings": "Ajustes de datos de jerarquía", - "relations-query-function": "Función de obtención de relaciones", - "has-children-function": "El nodo tiene una función hija", - "node-state-settings": "Ajustes estado nodo", - "node-opened-function": "Función por defecto al abrir nodo", - "node-disabled-function": "Función con nodo desactivado", - "display-settings": "Mostrar ajustes", - "node-icon-function": "Función de icono nodo", - "node-text-function": "Función de texto nodo", - "sort-settings": "Ajustes de ordenación", - "nodes-sort-function": "Función de ordenación" - }, - "edge": { - "display-default-title": "Mostrar título por defecto" - }, - "gateway": { - "general-settings": "Ajustes generales", - "widget-title": "Título del widget", - "default-archive-file-name": "Nombre del fichero por defecto", - "device-type-for-new-gateway": "Tipo de dispositivo para nuevo gateway", - "messages-settings": "Ajustes de mensajes", - "save-config-success-message": "Mensaje de éxito grabando configuración", - "device-name-exists-message": "Mensaje de texto cuando el nombre del dispositivo ya exista", - "gateway-title": "Formulario Gateway", - "read-only": "Solo lectura", - "events-title": "Título del formulario de eventos", - "events-filter": "Filtro de eventos", - "event-key-contains": "La clave de evento contiene...", - "show-connector": "Mostrar para el conector", - "connector-state-param-key": "Clave del parámetro de estado del conector", - "status": "Estado", - "message": "Mensaje", - "created-time": "Hora de creación" - }, - "gauge": { - "default-color": "Color por defecto", - "radial-gauge-settings": "Ajustes de indicador radial", - "ticks-settings": "Ajustes de ticks", - "min-value": "Valor mínimo", - "max-value": "Valor máximo", - "start-ticks-angle": "Ángulo de inicio ticks", - "ticks-angle": "Ángulo Ticks", - "major-ticks-count": "Nº de ticks principales", - "major-ticks-color": "Color Nº de ticks principales", - "minor-ticks-count": "Nº de ticks secundarios", - "minor-ticks-color": "Color Nº de ticks secundarios", - "tick-numbers-font": "Fuente del tick", - "unit-title-settings": "Ajustes de unidad (título)", - "show-unit-title": "Mostrar unidades (título)", - "unit-title": "Título de unidad", - "title-font": "Fuente de texto del título", - "units-settings": "Ajustes de unidades", - "units-font": "Fuente de texto de las unidades", - "value-box-settings": "Ajustes de valor", - "show-value-box": "Mostrar valor", - "value-int": "Nº de dígitos para la parte entera del valor", - "value-font": "Fuente de texto del valor", - "value-box-rect-stroke-color": "Color del trazo del rectángulo (valor)", - "value-box-rect-stroke-color-end": "Color del trazo del rectángulo (valor) - gradiente final", - "value-box-background-color": "Color de fondo del valor", - "value-box-shadow-color": "Color de sombra del valor", - "plate-settings": "Ajustes de la placa", - "show-plate-border": "Mostrar borde de la placa", - "plate-color": "Color de la placa", - "needle-settings": "Ajustes de la aguja", - "needle-circle-size": "Tamaño del círculo de la aguja", - "needle-color": "Color de la aguja", - "needle-color-end": "Color de la aguja - gradiente final", - "needle-color-shadow-up": "Color de sombreado de la mitad superior de la aguja", - "needle-color-shadow-down": "Color de sombra de la aguja", - "highlights-settings": "Ajustes de resalto", - "highlights-width": "Ancho del resalto", - "highlights": "Resaltos", - "highlight-from": "De", - "highlight-to": "A", - "highlight-color": "Color", - "no-highlights": "No hay resaltos configurados", - "add-highlight": "Añadir resalto", - "animation-settings": "Ajustes de animación", - "enable-animation": "Activar animación", - "animation-duration": "Duración de animación", - "animation-rule": "Regla de animación", - "animation-linear": "Lineal", - "animation-quad": "Quad", - "animation-quint": "Quint", - "animation-cycle": "Ciclo (cycle)", - "animation-bounce": "Rebote (bounce)", - "animation-elastic": "Elastic", - "animation-dequad": "Dequad", - "animation-dequint": "Dequint", - "animation-decycle": "Decycle", - "animation-debounce": "Debounce", - "animation-delastic": "Delastic", - "linear-gauge-settings": "Ajustes de indicador lineal", - "bar-stroke-width": "Ancho del trazo", - "bar-stroke-color": "Color del trazo", - "bar-background-color": "Color de fondo del indicador", - "bar-background-color-end": "Color de fondo del indicador - gradiente final", - "progress-bar-color": "Color de la barra de progreso", - "progress-bar-color-end": "Color de la barra de progreso - gradiente final", - "major-ticks-names": "Nombre de ticks principales", - "show-stroke-ticks": "Mostrar trazo de ticks", - "major-ticks-font": "Fuente de ticks principales", - "border-color": "Color del borde", - "border-width": "Ancho del borde", - "needle-circle-color": "Color del círculo de la aguja", - "animation-target": "Destino de la animación", - "animation-target-needle": "Aguja", - "animation-target-plate": "Placa", - "common-settings": "Ajustes comunes del indicador", - "gauge-type": "Tipo de indicador", - "gauge-type-arc": "Arco", - "gauge-type-donut": "Donut", - "gauge-type-horizontal-bar": "Barra horizontal", - "gauge-type-vertical-bar": "Barra vertical", - "donut-start-angle": "Ángulo de inicio", - "bar-settings": "Ajustes de barra indicadora", - "relative-bar-width": "Ancho relativo", - "neon-glow-brightness": "Efecto brillo de neon, (0-100), 0 - desactiva efecto", - "stripes-thickness": "Grosor de las rayas, 0 - sin rayas", - "rounded-line-cap": "Mostrar tapa de línea redondeada", - "bar-color-settings": "Ajustes de color de barra", - "use-precise-level-color-values": "Usar niveles precisos de color", - "bar-colors": "Colores de la barra, del más bajo al más alto", - "color": "Color", - "no-bar-colors": "No hay colores configurados", - "add-bar-color": "Añadir color", - "from": "De", - "to": "A", - "fixed-level-colors": "Colores de barra usando valores límite", - "gauge-title-settings": "Ajustes de título del indicador", - "show-gauge-title": "Mostrar título de indicador", - "gauge-title": "Título de indicador", - "gauge-title-font": "Fuente del título del indicador", - "unit-title-and-timestamp-settings": "Ajustes del título de unidades y timestamp", - "show-timestamp": "Mostrar valor timestamp", - "timestamp-format": "Formato timestamp", - "label-font": "Fuente de la etiqueta que se muestra bajo el valor", - "value-settings": "Ajustes del valor", - "show-value": "Mostrar texto del valor", - "min-max-settings": "Etiqueta mínimo/máximo", - "show-min-max": "Mostrar valores mínimos y máximos", - "min-max-font": "Fuente de los valores mínimo y máximo", - "show-ticks": "Mostrar ticks", - "tick-width": "Ancho de tick", - "tick-color": "Color de tick", - "tick-values": "Valores de tick", - "no-tick-values": "No hay valores configurados", - "add-tick-value": "Añadir valor de tick" - }, - "gpio": { - "pin": "Pin", - "label": "Etiqueta", - "row": "Fila", - "column": "Columna", - "color": "Color", - "panel-settings": "Ajustes de panel", - "background-color": "Color de fondo", - "gpio-switches": "switches GPIO", - "no-gpio-switches": "No hay switches GPIO configurados", - "add-gpio-switch": "Añadir switch GPIO", - "gpio-status-request": "Solicitud estado GPIO", - "method-name": "Nombre del método RPC", - "method-body": "Cuerpo del método RPC", - "gpio-status-change-request": "Solicitud de cambio de estado GPIO", - "parse-gpio-status-function": "Función de parseado de estado GPIO", - "gpio-leds": "Leds GPIO", - "no-gpio-leds": "No hay Leds GPIO configurados", - "add-gpio-led": "Añadir led GPIO" - }, - "html-card": { - "html": "HTML", - "css": "CSS" - }, - "input-widgets": { - "attribute-not-allowed": "El parámetro de atributo no se puede usar en este widget", - "blocked-location": "La función de geolocalización está bloqueada en tu navegador", - "claim-device": "Reclamar dispositivo", - "claim-failed": "Error al reclamar dispositivo!", - "claim-not-found": "Dispositivo no encontrado!", - "claim-successful": "Dispositivo reclamado correctamente!", - "date": "Fecha", - "device-name": "Nombre del dispositivo", - "device-name-required": "Se requere nombre de dispositivo", - "discard-changes": "Descartar los cambios", - "entity-attribute-required": "Se requiere atributo de entidad", - "entity-coordinate-required": "Se requieren ambos campos (latitud y longitud)", - "entity-timeseries-required": "Se requiere la serie de tiempo de la entidad", - "get-location": "Obtener localización actual", - "invalid-date": "Fecha inválida", - "latitude": "Latitud", - "longitude": "Longitud", - "min-value-error": "El valor mínimo es {{value}}", - "max-value-error": "El valor máximo es {{value}}", - "not-allowed-entity": "La entidad seleccionada no puede tener atributos compartidos", - "no-attribute-selected": "No se seleccionó ningún atributo", - "no-datakey-selected": "No se seleccionó ninguna clave de datos", - "no-coordinate-specified": "No se ha especificado la clave para latitud/longitud", - "no-entity-selected": "Ninguna entidad seleccionada", - "no-image": "Sin imagen", - "no-support-geolocation": "Tu navegador no soporta geolocalización", - "no-support-web-camera": "No hay cámara web compatible", - "enable-https-use-widget": "Por favor, activa HTTPS para poder usar este widget", - "no-found-your-camera": "No es posible encontrar la cámara", - "no-permission-camera": "Permiso denegado por el usuario / Esta página no tiene permisos para usar la cámara", - "no-timeseries-selected": "No hay series de tiempo seleccionadas", - "secret-key": "Clave", - "secret-key-required": "Clave requerida", - "switch-attribute-value": "Cambiar el valor del atributo de entidad", - "switch-camera": "Cambiar de cámara", - "switch-timeseries-value": "Cambiar el valor de la serie de tiempo de la entidad", - "take-photo": "Tomar foto", - "time": "Tiempo", - "timeseries-not-allowed": "El parámetro Timeseries no se puede usar en este widget", - "update-failed": "Actualización fallida", - "update-successful": "Actualización exitosa", - "update-attribute": "Actualizar atributo", - "update-timeseries": "Actualizar series de tiempo", - "value": "Valor", - "general-settings": "Ajustes Generales", - "widget-title": "Título del widget", - "claim-button-label": "Etiqueta del botón de reclamar", - "show-secret-key-field": "Mostrar el campo 'Clave Secreta'", - "labels-settings": "Ajustes de etiquetas", - "show-labels": "Mostrar etiquetas", - "device-name-label": "Etiqueta para el campo de entrada 'Nombre de Dispositivo'", - "secret-key-label": "Etiqueta para el campo de entrara 'Clave Secreta'", - "messages-settings": "Ajustes de mensajes", - "claim-device-success-message": "Mensaje a mostrar cuando el dispositivo se haya reclamado ok", - "claim-device-not-found-message": "Mensaje a mostrar cuando el dispositivo no se encuentre", - "claim-device-failed-message": "Mensaje a mostrar cuando ocurra un error reclamando el dispositivo", - "claim-device-name-required-message": "Mensaje de error a mostrar con 'Nombre de dispositivo requerido'", - "claim-device-secret-key-required-message": "Mensaje de error a mostrar con 'Clave Secreta requerida'", - "show-label": "Mostrar etiqueta", - "label": "Etiqueta", - "required": "Requerido", - "required-error-message": "Error a mostrar con campo 'Requerido'", - "show-result-message": "Mensaje para mostrar resultado", - "integer-field-settings": "Ajustes de campos tipo entero", - "min-value": "Valor Mínimo", - "max-value": "Valor Máximo", - "double-field-settings": "Ajustes de campos tipo double", - "text-field-settings": "Ajustes de campos tipo texto", - "min-length": "Longitud mínima", - "max-length": "Longitud máxima", - "checkbox-settings": "Ajustes de campos tipo checkbox", - "true-label": "Etiqueta checked", - "false-label": "Etiqueta unchecked", - "image-input-settings": "Ajustes de campos tipo entrada de imagen", - "display-preview": "Mostrar previsualización", - "display-clear-button": "Mostrar botón de borrar", - "display-apply-button": "Mostrar botón de aplicar", - "display-discard-button": "Mostrar botón de descartar", - "datetime-field-settings": "Ajustes de campos tipo Fecha/Hora", - "display-time-input": "Mostrar entrada de hora", - "latitude-key-name": "Nombre clave latitud", - "longitude-key-name": "Nombre clave longitud", - "show-get-location-button": "Mostrar botón 'Obtener localización actual'", - "use-high-accuracy": "Usar alta precisión", - "location-fields-settings": "Ajustes de campos de localización", - "latitude-label": "Etiqueta para latitud", - "longitude-label": "Etiqueta para longitud", - "input-fields-alignment": "Alineado de campos de entrada", - "input-fields-alignment-column": "Columna (por defecto)", - "input-fields-alignment-row": "Fila", - "layout": "Diseño", - "row-gap": "Espacio entre filas en píxeles", - "column-gap": "Espacio entre columnas en píxeles", - "latitude-field-required": "Campo latitud requerido", - "longitude-field-required": "Campo longitud requerido", - "attribute-settings": "Ajustes de atributos", - "widget-mode": "Modo del widget", - "widget-mode-update-attribute": "Actualizar atributo", - "widget-mode-update-timeseries": "Actualizar timeseries", - "attribute-scope": "Alcance de atributos", - "attribute-scope-server": "Atributos de servidor", - "attribute-scope-shared": "Atributos compartidos", - "value-required": "Valor requerido", - "image-settings": "Ajustes de imagen", - "image-format": "Formato de imagen", - "image-format-jpeg": "JPEG", - "image-format-png": "PNG", - "image-format-webp": "WEBP", - "image-quality": "Calidad de imagen que usa compresión con pérdida como jpeg y webp", - "max-image-width": "Máximo ancho de imagen", - "max-image-height": "Máximo alto de imagen", - "action-buttons": "Botones de acción", - "show-action-buttons": "Mostrar botones de acción", - "update-all-values": "Actualizar todos los valores, no sólo los modificados", - "save-button-label": "Etiqueta de botón 'GRABAR'", - "reset-button-label": "Etiqueta de botón 'DESHACER'", - "group-settings": "Ajustes de grupo", - "show-group-title": "Mostrar título para el grupo de campos relacionados con diferentes entidades", - "group-title": "Título del grupo", - "fields-alignment": "Alineado de campos", - "fields-alignment-row": "Fila (por defecto)", - "fields-alignment-column": "Columna", - "fields-in-row": "Número de campos en la fila", - "option-value": "Valor (escribe 'null' para crear una opción vacía)", - "option-label": "Etiqueta", - "hide-input-field": "Ocultar campo de entrada", - "datakey-type": "Tipo de clave de datos", - "datakey-type-server": "Atributo de servidor (por defecto)", - "datakey-type-shared": "Atributo compartido", - "datakey-type-timeseries": "Timeseries", - "datakey-value-type": "Tipo de valor de la clave de datos", - "datakey-value-type-string": "String", - "datakey-value-type-double": "Double", - "datakey-value-type-integer": "Entero", - "datakey-value-type-json": "JSON", - "datakey-value-type-boolean-checkbox": "Booleano (Checkbox)", - "datakey-value-type-boolean-switch": "Booleano (Switch)", - "datakey-value-type-date-time": "Fecha & Hora", - "datakey-value-type-date": "Fecha", - "datakey-value-type-time": "Hora", - "datakey-value-type-select": "Selección", - "datakey-value-type-color": "Color", - "value-is-required": "Valor requerido", - "ability-to-edit-attribute": "Posibilidad de editar atributo", - "ability-to-edit-attribute-editable": "Editable (por defecto)", - "ability-to-edit-attribute-disabled": "Desactivado", - "ability-to-edit-attribute-readonly": "Sólo lectura", - "disable-on-datakey-name": "Desactivar cuando otra clave de datos sea false (especificar nombre de clave de datos)", - "field-appearance": "Apariencia de los campos", - "appearance-fill": "Relleno", - "appearance-outline": "Contorno", - "subscript-sizing": "Tamaño subscript", - "subscript-sizing-fixed": "Fijo", - "subscript-sizing-dynamic": "Dinámico", - "slide-toggle-settings": "Ajustes de deslizador", - "slide-toggle-label-position": "Posición de etiqueta", - "slide-toggle-label-position-after": "Después", - "slide-toggle-label-position-before": "Antes", - "select-options": "Seleccionar opciones", - "no-select-options": "No hay opciones configuradas", - "add-select-option": "Añadir opción", - "numeric-field-settings": "Ajustes de campos numéricos", - "step-interval": "Pasos entre valores (intervalo)", - "error-messages": "Mensajes de erro", - "min-value-error-message": "Mensaje de error de 'Valor Mínimo'", - "max-value-error-message": "Mensaje de error de 'Valor Máximo'", - "invalid-date-error-message": "Mensaje de error de 'Fecha Inválida'", - "invalid-JSON-error-message": "Mensaje de error de 'JSON inválido'", - "icon-settings": "Ajustes de iconos", - "dialog-editor-settings": "Ajustes de editor de diálogo", - "use-custom-icon": "Usar icono personalizado", - "input-cell-icon": "Icono a mostrar antes del campo de entrada", - "value-conversion-settings": "Ajustes de conversión de valor", - "get-value-settings": "Ajustes de obtención de valores", - "use-get-value-function": "Usar función getValue", - "get-value-function": "Función getValue", - "set-value-settings": "Ajustes de establecimiento de valores", - "use-set-value-function": "Usar función setValue", - "set-value-function": "Función setValue", - "json-invalid": "El valor JSON tiene un formato inválido", - "title": "Título", - "cancel-button-label": "Etiqueta del botón 'Cancelar'" - }, - "invalid-qr-code-text": "Texto de entrara inválido para el código QR. La entrada debe ser de tipo string", - "qr-code": { - "use-qr-code-text-function": "Usar función de texto QR", - "qr-code-text-pattern": "Patrón del código QR (por ej. '${entityName} | ${keyName} - texto adicional.')", - "qr-code-text-pattern-hint": "El patrón del código QR usa el valor de la primera clave encontrada en las entidades del alias.", - "qr-code-text-pattern-required": "Se requiere patrón del código QR.", - "qr-code-text-function": "Función del código QR" - }, - "label-widget": { - "label-pattern": "Patrón", - "label-pattern-hint": "Ayuda: por ej. 'Texto ${keyName} unidades.' o ${#<key index>} unidades'", - "label-pattern-required": "Se requiere patrón", - "label-position": "Posición (Porcentaje relativo al fondo)", - "x-pos": "X", - "y-pos": "Y", - "background-color": "Color de fondo", - "font-settings": "Ajustes de fuente", - "background-image": "Imagen de fondo", - "labels": "Etiquetas", - "no-labels": "No hay etiquetas configuradas", - "add-label": "Añadir etiqueta" - }, - "navigation": { - "title": "Título", - "navigation-path": "Ruta de navegación", - "filter-type": "Tipo de filtro", - "filter-type-all": "Todos los objetos", - "filter-type-include": "Incluir objetos", - "filter-type-exclude": "Excluir objetos", - "items": "Objetos", - "enter-urls-to-filter": "Especificar URLs a filtrar..." + "persistent-table": { + "rpc-id": "ID de RPC", + "message-type": "Tipo de mensaje", + "method": "Método", + "params": "Parámetros", + "created-time": "Fecha de creación", + "expiration-time": "Fecha de expiración", + "retries": "Reintentos", + "status": "Estado", + "filter": "Filtro", + "refresh": "Actualizar", + "add": "Agregar solicitud RPC", + "details": "Detalles", + "delete": "Eliminar", + "delete-request-title": "Eliminar solicitud RPC persistente", + "delete-request-text": "¿Está seguro de que desea eliminar la solicitud?", + "details-title": "Detalles del RPC ID: ", + "additional-info": "Información adicional", + "response": "Respuesta", + "any-status": "Cualquier estado", + "rpc-status-list": "Lista de estados RPC", + "no-request-prompt": "No hay solicitudes para mostrar", + "send-request": "Enviar solicitud", + "add-title": "Crear solicitud RPC persistente", + "method-error": "El método es obligatorio.", + "timeout-error": "El valor mínimo de espera es 5000 (5 segundos).", + "white-space-error": "No se permiten espacios en blanco.", + "rpc-status": { + "QUEUED": "EN COLA", + "SENT": "ENVIADO", + "DELIVERED": "ENTREGADO", + "SUCCESSFUL": "EXITOSO", + "TIMEOUT": "TIEMPO DE ESPERA AGOTADO", + "EXPIRED": "EXPIRADO", + "FAILED": "FALLIDO" + }, + "rpc-search-status-all": "TODOS", + "message-types": { + "false": "Bidireccional", + "true": "Unidireccional" + }, + "general-settings": "Configuraciones generales", + "enable-filter": "Habilitar filtro", + "enable-sticky-header": "Mostrar encabezado durante el desplazamiento", + "enable-sticky-action": "Mostrar columna de acciones durante el desplazamiento", + "display-request-details": "Mostrar detalles de la solicitud", + "allow-send-request": "Permitir envío de solicitud RPC", + "allow-delete-request": "Permitir eliminación de la solicitud", + "columns-settings": "Configuración de columnas", + "display-columns": "Columnas a mostrar", + "column": "Columna", + "no-columns-found": "No se encontraron columnas", + "no-columns-matching": "'{{column}}' no encontrado." + }, + "range-chart": { + "chart": "Gráfico", + "data-zoom": "Zoom de datos", + "range-chart-appearance": "Apariencia del gráfico de rango", + "range-colors": "Colores de rango", + "out-of-range-color": "Color fuera de rango", + "show-range-thresholds": "Mostrar umbrales de rango", + "range-thresholds-settings": "Configuraciones de umbrales de rango", + "fill-area": "Rellenar área", + "fill-area-opacity": "Opacidad del área rellena", + "range-chart-style": "Estilo del gráfico de rango" + }, + "rpc": { + "value-settings": "Configuración de valor", + "initial-value": "Valor inicial", + "retrieve-value-settings": "Configuración para recuperar valor de encendido/apagado", + "retrieve-value-method": "Recuperar valor usando método", + "retrieve-value-method-none": "No recuperar", + "retrieve-value-method-rpc": "Llamar método RPC para obtener valor", + "retrieve-value-method-attribute": "Suscribirse al atributo", + "retrieve-value-method-timeseries": "Suscribirse a la serie temporal", + "attribute-value-key": "Clave del atributo", + "timeseries-value-key": "Clave de la serie temporal", + "get-value-method": "Método RPC para obtener valor", + "parse-value-function": "Función para analizar valor", + "update-value-settings": "Configuración para actualizar valor", + "set-value-method": "Método RPC para establecer valor", + "convert-value-function": "Función para convertir valor", + "rpc-settings": "Configuración RPC", + "request-timeout": "Tiempo de espera de solicitud RPC (ms)", + "persistent-rpc-settings": "Configuración de RPC persistente", + "request-persistent": "Solicitud RPC persistente", + "persistent-polling-interval": "Intervalo de sondeo (ms) para obtener respuesta de comando RPC persistente", + "common-settings": "Configuraciones comunes", + "switch-title": "Título del interruptor", + "show-on-off-labels": "Mostrar etiquetas encendido/apagado", + "slide-toggle-label": "Etiqueta de interruptor deslizante", + "label-position": "Posición de la etiqueta", + "label-position-before": "Antes", + "label-position-after": "Después", + "slider-color": "Color del control deslizante", + "slider-color-primary": "Primario", + "slider-color-accent": "Acento", + "slider-color-warn": "Advertencia", + "button-style": "Estilo del botón", + "button-raised": "Botón elevado", + "button-primary": "Color primario", + "button-background-color": "Color de fondo del botón", + "button-text-color": "Color del texto del botón", + "widget-title": "Título del widget", + "button-label": "Etiqueta del botón", + "device-attribute-scope": "Alcance del atributo del dispositivo", + "server-attribute": "Atributo del servidor", + "shared-attribute": "Atributo compartido", + "device-attribute-parameters": "Parámetros del atributo del dispositivo", + "is-one-way-command": "Es un comando unidireccional", + "rpc-method": "Método RPC", + "rpc-method-params": "Parámetros del método RPC", + "show-rpc-error": "Mostrar error de ejecución del comando RPC", + "led-title": "Título del LED", + "led-color": "Color del LED", + "check-status-settings": "Configuración de verificación de estado", + "perform-rpc-status-check": "Realizar verificación de estado del dispositivo mediante RPC", + "retrieve-led-status-value-method": "Recuperar valor de estado del LED usando método", + "led-status-value-attribute": "Atributo del dispositivo que contiene el estado del LED", + "led-status-value-timeseries": "Serie temporal del dispositivo que contiene el estado del LED", + "check-status-method": "Método RPC para verificar estado del dispositivo", + "parse-led-status-value-function": "Función para analizar el valor del estado del LED", + "knob-title": "Título del control giratorio", + "min-value": "Valor mínimo", + "max-value": "Valor máximo" + }, + "maps": { + "map-type": { + "type": "Tipo de mapa", + "map": "Mapa", + "image": "Imagen" + }, + "image": { + "image-source": "Fuente de imagen", + "image-source-image": "Imagen", + "image-source-entity-key": "Clave de entidad", + "source-entity-alias": "Alias de entidad fuente", + "image-url-key": "Clave de URL de imagen", + "image-url-key-required": "Se requiere la clave de URL de imagen" + }, + "control": { + "map-controls": "Controles del mapa", + "position": "Posición", + "position-topleft": "Superior izquierda", + "position-topright": "Superior derecha", + "position-bottomleft": "Inferior izquierda", + "position-bottomright": "Inferior derecha", + "zoom-actions": "Acciones de zoom", + "zoom-scroll": "Desplazamiento", + "zoom-double-click": "Doble clic", + "zoom-control-buttons": "Botones de control", + "scale": "Escala", + "scale-metric": "Métrica", + "scale-imperial": "Imperial", + "switch-to-drag-mode-using-button": "Cambiar al modo de arrastre usando botón" + }, + "timeline": { + "control-panel": "Panel de control de línea de tiempo", + "time-step": "Paso de tiempo", + "speed-options": "Opciones de velocidad", + "timestamp": "Marca de tiempo", + "snap-to-real-location": "Ajustar a la ubicación real", + "location-snap-filter-function": "Función de filtrado de ajuste de ubicación", + "no-trips-data-available": "No hay datos de viajes disponibles" + }, + "map-action": { + "map-action-buttons": "Botones de acción del mapa", + "label": "Etiqueta", + "icon": "Ícono", + "color": "Color", + "action": "Acción", + "add-button": "Agregar botón", + "no-action-buttons-configured": "No hay botones de acción configurados", + "remove-action-button": "Eliminar botón de acción", + "map-action-button": "Botón de acción del mapa", + "button-requires": "El botón requiere una etiqueta o ícono" + }, + "common": { + "common-map-settings": "Configuraciones comunes del mapa", + "fit-map-bounds": "Ajustar límites del mapa para cubrir todos los marcadores", + "default-map-center-position": "Posición central predeterminada del mapa", + "default-map-zoom-level": "Nivel de zoom predeterminado del mapa", + "entities-limit": "Límite de entidades a cargar" + }, + "layer": { + "label": "Etiqueta", + "layer": "Capa", + "layers": "Capas", + "map-layers": "Capas del mapa", + "add-layer": "Añadir capa", + "layer-settings": "Configuraciones de la capa", + "remove-layer": "Eliminar capa", + "no-layers": "No hay capas configuradas", + "roadmap": "Mapa de carreteras", + "satellite": "Satélite", + "hybrid": "Híbrido", + "reference": { + "reference-layer": "Capa de referencia", + "no-layer": "Sin capa", + "openstreetmap-hybrid": "OpenStreetMap Híbrido", + "world-edition-hybrid": "Edición Mundial Híbrida", + "enhanced-contrast-hybrid": "Híbrido de Contraste Mejorado" }, - "persistent-table": { - "rpc-id": "ID RPC", - "message-type": "Tipo de mensaje", - "method": "Método", - "params": "Parémetros", - "created-time": "Hora de creación", - "expiration-time": "Hora de expiración", - "retries": "Reintentos", - "status": "Estado", - "filter": "Filtro", - "refresh": "Actualizar", - "add": "Añadir petición RPC", - "details": "Detalles", - "delete": "Borrar", - "delete-request-title": "Borrar petición RPC persistente", - "delete-request-text": "Estas seguro de borrar la petición?", - "details-title": "Detalles ID RPC: ", - "additional-info": "Información adicional", - "response": "Respuesta", - "any-status": "Cualquier estado", - "rpc-status-list": "Lista de estados RPC", - "no-request-prompt": "No hay peticiones a mostrar", - "send-request": "Enviar petición", - "add-title": "Crear una petición RPC persistente", - "method-error": "Se requiere método.", - "timeout-error": "El valor mínimo de timeout es 5000 (5 segundos).", - "white-space-error": "No se permiten espacios en blanco.", - "rpc-status": { - "QUEUED": "EN COLA", - "SENT": "ENVIADO", - "DELIVERED": "ENTREGADO", - "SUCCESSFUL": "CORRECTO", - "TIMEOUT": "TIMEOUT", - "EXPIRED": "EXPIRADO", - "FAILED": "FALLO" - }, - "rpc-search-status-all": "TODOS", - "message-types": { - "false": "Bidireccional (Two-way)", - "true": "Unidireccional (One-way)" - }, - "general-settings": "Ajustes Generales", - "enable-filter": "Activar filtro", - "enable-sticky-header": "Mostrar encabezado mientras se hace scroll", - "enable-sticky-action": "Mostrar columna de acciones mientras se hace scroll", - "display-request-details": "Mostrar detalles de petición", - "allow-send-request": "Permitir enviar petición RPC", - "allow-delete-request": "Permitir borrar petición", - "columns-settings": "Ajustes de columnas", - "display-columns": "Columnas a mostrar", - "column": "Columna", - "no-columns-found": "No se encontraron columnas", - "no-columns-matching": "'{{column}}' no encontrada." + "provider": { + "provider": "Proveedor", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Mapa de carreteras", + "satellite": "Satélite", + "hybrid": "Híbrido", + "terrain": "Terreno" + }, + "here": { + "title": "HERE", + "normal-day": "Día normal", + "normal-night": "Noche normal", + "hybrid-day": "Día híbrido", + "terrain-day": "Día de terreno" + }, + "tencent": { + "title": "Tencent", + "normal": "Normal", + "satellite": "Satélite", + "terrain": "Terreno" + }, + "custom": { + "title": "Personalizado", + "tile-url": "URL del mosaico" + } }, - "rpc": { - "value-settings": "Ajustes de valor", - "initial-value": "Valor inicial", - "retrieve-value-settings": "Obtener ajustes valores on/off", - "retrieve-value-method": "Obtener valor usando método", - "retrieve-value-method-none": "No obtener", - "retrieve-value-method-rpc": "Método para llamada RPC de obtención de valor", - "retrieve-value-method-attribute": "Suscribir por atributo", - "retrieve-value-method-timeseries": "Suscribir por timeseries", - "attribute-value-key": "Clave de atributo", - "timeseries-value-key": "Clave de timeseries", - "get-value-method": "Metodo para obtener valor vía RPC", - "parse-value-function": "Función de parseo de valor", - "update-value-settings": "Ajustes de actualización de valor", - "set-value-method": "Método para establecer valor vía RPC", - "convert-value-function": "Función de conversión", - "rpc-settings": "Ajustes RPC", - "request-timeout": "Timeout de petición RPC (ms)", - "persistent-rpc-settings": "Ajustes de RPC persistente", - "request-persistent": "Petición RPC persistente", - "persistent-polling-interval": "Intervalo de sondeo (ms) para obtener respuesta del comando RPC persistente", - "common-settings": "Ajustes comunes", - "switch-title": "Título del switch", - "show-on-off-labels": "Mostrar etiquetas on/off", - "slide-toggle-label": "Etiqueta de interruptor deslizante", - "label-position": "Posición de etiqueta", - "label-position-before": "Antes", - "label-position-after": "Después", - "slider-color": "Color del deslizador", - "slider-color-primary": "Primario", - "slider-color-accent": "Acento", - "slider-color-warn": "Aviso", - "button-style": "Estilo de botón", - "button-raised": "Botón levantado", - "button-primary": "Color primario", - "button-background-color": "Color de fondo", - "button-text-color": "Color de texto", - "widget-title": "Título del widget", - "button-label": "Etiqueta del botón", - "device-attribute-scope": "Alcance de atributos del dispositivo", - "server-attribute": "Atributos de servidor", - "shared-attribute": "Atributos compartidos", - "device-attribute-parameters": "Parámetros de atributos del dispositivo", - "is-one-way-command": "Es un comando de una vía (one way)", - "rpc-method": "Método RPC", - "rpc-method-params": "Parámetros método RPC", - "show-rpc-error": "Mostrar errores de ejecución de RPC", - "led-title": "Título LED", - "led-color": "Color LED", - "check-status-settings": "Ajustes de comprobación de estado", - "perform-rpc-status-check": "Realizar comprobación del dispositivo RPC", - "retrieve-led-status-value-method": "Obtener estado del led usando método", - "led-status-value-attribute": "Atributo del dispositivo que contiene el valor del estado del led", - "led-status-value-timeseries": "Timeseries del dispositivo que contiene el valor del estado del led", - "check-status-method": "Método RPC para chequear el estado del dispositivo", - "parse-led-status-value-function": "Función de parseo para el estado del led", - "knob-title": "Título de mando", - "min-value": "Valor mínimo", - "max-value": "Valor máximo" + "credentials": { + "credentials": "Credenciales", + "api-key": "Clave API" + } + }, + "overlays": { + "overlays": "Superposiciones", + "overlays-hint": "Configura orígenes de datos, apariencia, comportamiento, opciones de edición y agrupación para entidades del mapa", + "trips": "Viajes", + "markers": "Marcadores", + "polygons": "Polígonos", + "circles": "Círculos" + }, + "data-layer": { + "source": "Fuente", + "filter": "Filtro", + "additional-data-keys": "Claves de datos adicionales", + "additional-datasources": "Orígenes de datos adicionales", + "additional-datasources-hint": "Origen de datos para acceder a atributos o telemetría de entidades que no se muestran en el mapa, utilizable en funciones de superposición de mapa.", + "more-datasources": "Más orígenes de datos", + "data-keys": "Claves de datos", + "add-datasource": "Añadir origen de datos", + "no-datasources": "No hay orígenes de datos configurados", + "remove-datasource": "Eliminar origen de datos", + "behavior": "Comportamiento", + "on-click": "Al hacer clic", + "on-click-hint": "Acción invocada cuando el usuario hace clic en el elemento del mapa.", + "groups": "Grupos", + "groups-hint": "Lista de nombres de grupo asignados a la superposición, utilizados para alternar su visibilidad en el mapa.", + "color": "Color", + "color-settings": "Configuraciones de color", + "color-type-constant": "Constante", + "color-type-range": "Rango", + "color-type-function": "Función", + "color-range-source-key": "Clave fuente del rango de color", + "color-range-source-key-required": "Se requiere la clave fuente del rango de color", + "color-range": "Rango de color", + "color-function": "Función de color", + "label": "Etiqueta", + "tooltip": "Información sobre herramientas", + "pattern-type-pattern": "Patrón", + "pattern-type-function": "Función", + "label-pattern": "Etiqueta (ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)')", + "label-function": "Función de etiqueta", + "tooltip-pattern": "Tooltip (por ejemplo: 'Texto ${keyName} unidades.' o Texto del enlace)", + "tooltip-function": "Función de tooltip", + "tooltip-trigger": "Disparador de tooltip", + "tooltip-trigger-click": "Mostrar tooltip al hacer clic", + "tooltip-trigger-hover": "Mostrar tooltip al pasar el ratón", + "auto-close-tooltips": "Cerrar tooltips automáticamente", + "tooltip-offset": "Desplazamiento del tooltip", + "tooltip-offset-horizontal": "Horizontal", + "tooltip-offset-vertical": "Vertical", + "tooltip-tag-actions": "Acciones de etiqueta", + "add-tooltip-tag-action": "Añadir acción de etiqueta", + "edit-tooltip-tag-action": "Editar acción de etiqueta", + "remove-tooltip-tag-action": "Eliminar acción de etiqueta", + "action-add": "Añadir", + "action-edit": "Editar", + "action-move": "Mover", + "action-remove": "Eliminar", + "edit-instruments": "Instrumentos", + "persist-location-attribute-scope": "Ámbito del atributo para persistir la ubicación", + "enable-snapping": "Habilitar ajuste a otros vértices para dibujo preciso", + "enable-snapping-hint": "Alinea automáticamente nuevos puntos con formas existentes para facilitar y hacer más preciso el dibujo.", + "drag-drop-mode": "Modo de arrastrar y soltar", + "trip": { + "no-trips": "No hay viajes configurados", + "add-trip": "Añadir viaje", + "trip-configuration": "Configuración del viaje", + "remove-trip": "Eliminar viaje" }, - "maps": { - "select-entity": "Seleccionar entidad", - "select-entity-hint": "Ayuda: tras la selección haz click en el mapa para establecer la posición", - "tooltips": { - "placeMarker": "Click para colocar la entidad '{{entityName}}'", - "firstVertex": "Polígono para la entidad '{{entityName}}': click para colocar el primer punto", - "firstVertex-cut": "Click para colocar el primer punto", - "continueLine": "Polígono para la entidad '{{entityName}}': click para continuar dibujando", - "continueLine-cut": "Click para continuar dibujando", - "finishLine": "Click en cualquier marcador existente para finalizar", - "finishPoly": "Polígono para la entidad '{{entityName}}': click en el primer marcador para finalizar y grabar los cambios", - "finishPoly-cut": "Click en el primer marcador para finalizar y grabar los cambios", - "finishRect": "Polígono para la entidad '{{entityName}}': click para finalizar y grabar los cambios", - "startCircle": "Círculo para la entidad '{{entityName}}': click para establecer el centro del círculo", - "finishCircle": "Círculo para la entidad '{{entityName}}': click para finalizar el círculo", - "placeCircleMarker": "Click para colocar el marcador del círculo" - }, - "actions": { - "finish": "Finalizar", - "cancel": "Cancelar", - "removeLastVertex": "Quitar último punto" - }, - "buttonTitles": { - "drawMarkerButton": "Establecer entidad", - "drawPolyButton": "Crear polígono", - "drawLineButton": "Crear polilínea", - "drawCircleButton": "Crear círculo", - "drawRectButton": "Crear rectángulo", - "editButton": "Modo de edición", - "dragButton": "Modo Arrastrar-soltar", - "cutButton": "Cortar el área del polígono", - "deleteButton": "Borrar", - "drawCircleMarkerButton": "Crear marcador de círculo", - "rotateButton": "Rotar polígono" - }, - "map-provider-settings": "Ajustes proveedor de mapas", - "map-provider": "Proveedor de mapas", - "map-provider-google": "Google maps", - "map-provider-openstreet": "OpenStreet maps", - "map-provider-here": "HERE maps", - "map-provider-image": "Mapa de imagen", - "map-provider-tencent": "Tencent maps", - "openstreet-provider": "Proveedor OpenStreet map", - "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Defecto)", - "openstreet-provider-hot": "OpenStreetMap.HOT", - "openstreet-provider-esri-street": "Esri.WorldStreetMap", - "openstreet-provider-esri-topo": "Esri.WorldTopoMap", - "openstreet-provider-esri-imagery": "Esri.WorldImagery", - "openstreet-provider-cartodb-positron": "CartoDB.Positron", - "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", - "use-custom-provider": "Usar proveedor personalizado", - "custom-provider-tile-url": "URL de proveedor personalizado", - "google-maps-api-key": "API Key Google Maps", - "default-map-type": "Tipo de mapa por defecto", - "google-map-type-roadmap": "Carretera", - "google-map-type-satelite": "Satelite", - "google-map-type-hybrid": "Híbrido", - "google-map-type-terrain": "Terreno", - "map-layer": "Capa de mapa", - "here-map-normal-day": "HERE.normalDay (Defecto)", - "here-map-normal-night": "HERE.normalNight", - "here-map-hybrid-day": "HERE.hybridDay", - "here-map-terrain-day": "HERE.terrainDay", - "credentials": "Credenciales", - "here-app-id": "HERE app id", - "here-app-code": "HERE app code", - "here-api-key": "HERE API key", - "here-use-new-version-api-3": "Usar API versión 3", - "tencent-maps-api-key": "API Key Tencent Maps", - "tencent-map-type-roadmap": "Carretera", - "tencent-map-type-satelite": "Satelite", - "tencent-map-type-hybrid": "Híbrido", - "image-map-background": "Fondo de mapa de imagen", - "image-map-background-from-entity-attribute": "Obtener imagen de fondo desde un atributo de la entidad", - "image-url-source-entity-alias": "Alias de entidad para URL de imagen", - "image-url-source-entity-attribute": "Atributo de entidad para URL de imagen", - "common-map-settings": "Ajustes comunes de mapas", - "x-pos-key-name": "Clave para posición X", - "y-pos-key-name": "Clave para posición Y", - "latitude-key-name": "Clave para latitud", - "longitude-key-name": "Clave para longitud", - "default-map-zoom-level": "Nivel de zoom por defecto (0 - 20)", - "default-map-center-position": "Posición central del mapa por defecto (0,0)", - "disable-scroll-zooming": "Desactivar zoom en scroll", - "disable-double-click-zooming": "Desactivar zoom en doble click", - "disable-zoom-control-buttons": "Desactivar botones de zoom", - "fit-map-bounds": "Ajustar los límites del mapa para cubrir todos los marcadores", - "use-default-map-center-position": "Usar posición central por defecto", - "entities-limit": "Límite de entidades a cargar", - "markers-settings": "Ajustes de los marcadores", - "marker-offset-x": "Offset X relativo a la posición multiplicado por el ancho del marcador", - "marker-offset-y": "Offset Y relativo a la posición multiplicado por el alto del marcador", - "position-function": "Función de conversión de posición, debe retornar coordenadas x,y como valor double de 0 a 1", - "draggable-marker": "Marcador arrastrable", - "label": "Etiqueta", - "show-label": "Mostrar etiqueta", - "use-label-function": "Usar función de etiqueta", - "label-pattern": "Etiqueta (Ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", - "label-function": "Función de etiqueta", - "tooltip": "Sugerencias (tooltip)", - "show-tooltip": "Mostrar sugerencias", - "show-tooltip-action": "Acción para mostrar las sugerencias", - "show-tooltip-action-click": "Mostrar sugerencias tooltip en click (por defecto)", - "show-tooltip-action-hover": "Mostrar sugerencias on hover", - "auto-close-tooltips": "Auto-cerrar sugerencias", - "use-tooltip-function": "Función de sugerencias", - "tooltip-pattern": "Sugerencia (Por ej. 'Texto ${keyName} unidades.' o Texto Link')", - "tooltip-function": "Función de sugerencia", - "tooltip-offset-x": "Offset X relativo al ancla del marcador multiplicado por el ancho", - "tooltip-offset-y": "Offset Y relativo al ancla del marcador multiplicado por la altura", - "color": "Color", - "use-color-function": "Usar función de color", - "color-function": "Función de color", - "marker-image": "Imagen de marcador", - "use-marker-image-function": "Usar función de imagen de marcador", - "custom-marker-image": "Imagen de marcador personalizada", - "custom-marker-image-size": "Tamaño de imagen personalizada (px)", - "marker-image-function": "Función de imagen de marcador", - "marker-images": "Imagenes de marcador", - "polygon-settings": "Ajustes de polígono", - "show-polygon": "Mostrar polígono", - "polygon-key-name": "Clave del polígono", - "enable-polygon-edit": "Polígono editable", - "polygon-label": "Etiqueta del polígono", - "show-polygon-label": "Mostrar etiqueta del polígono", - "use-polygon-label-function": "Usar funciones de etiqueta en el polígono", - "polygon-label-pattern": "Etiqueta del polígono (Ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", - "polygon-label-function": "Función de etiqueta del polígono", - "polygon-tooltip": "Sugerencia polígono", - "show-polygon-tooltip": "Mostrar sugerencias", - "auto-close-polygon-tooltips": "Auto-cerrar sugerencias polígono", - "use-polygon-tooltip-function": "Usar función de sugerencias en el polígono", - "polygon-tooltip-pattern": "Sugerencias (por ej. 'Texto ${keyName} unidades.' o Texto Link')", - "polygon-tooltip-function": "Función de sugerencia de polígono", - "polygon-color": "Color de polígono", - "polygon-opacity": "Opacidad de polígono", - "use-polygon-color-function": "Usar función de color en polígono", - "polygon-color-function": "Función de color de polígono", - "polygon-stroke": "Trazo de polígono", - "stroke-color": "Color de trazo", - "stroke-opacity": "Opacidad de trazo", - "stroke-weight": "Peso de trazo", - "use-polygon-stroke-color-function": "Usar función de color de trazo en polígono", - "polygon-stroke-color-function": "Función de color de trazo", - "circle-settings": "Ajustes de círculo", - "show-circle": "Mostrar círculo", - "circle-key-name": "Clave de círculo", - "enable-circle-edit": "Activar edición de círculo", - "circle-label": "Etiqueta de círculo", - "show-circle-label": "Mostrar etiqueta de círculo", - "use-circle-label-function": "Usar función para etiqueta de círculo", - "circle-label-pattern": "Etiqueta de círculo (Ejemplos patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", - "circle-label-function": "Funcion de etiqueta de círculo", - "circle-tooltip": "Sugerencias de círculo", - "show-circle-tooltip": "Mostrar sugerencias de círculo", - "auto-close-circle-tooltips": "Auto-cerrar sugerencias", - "use-circle-tooltip-function": "Usar función de sugerencias en círculo", - "circle-tooltip-pattern": "Sugerencia (por ej. 'Texto ${keyName} unidades.' o Texto Link')", - "circle-tooltip-function": "Función de sugerencia círculo", - "circle-fill-color": "Color de relleno círculo", - "circle-fill-color-opacity": "Opacidad color de relleno", - "use-circle-fill-color-function": "Usar función de color de relleno", - "circle-fill-color-function": "Función de color de relleno", - "circle-stroke": "Trazo del círculo", - "use-circle-stroke-color-function": "Usar función de color de trazo", - "circle-stroke-color-function": "Función de color de trazo", - "markers-clustering-settings": "Ajustes de agrupación de marcadores", - "use-map-markers-clustering": "Usar agrupación de marcadores en mapa", - "zoom-on-cluster-click": "Zoom cuando se haga click en un grupo", - "max-cluster-zoom": "Nivel máximo de zoom en la que un marcador puede ser parte de un grupo (0 - 18)", - "max-cluster-radius-pixels": "Radio máximo que un grupo cubre en píxeles", - "cluster-zoom-animation": "Mostrar animacion en marcadores cuando se haga zoom", - "show-markers-bounds-on-cluster-mouse-over": "Mostrar los limites de los marcadores cuando el raton pase por encima de un grupo", - "spiderfy-max-zoom-level": "Spiderfy al máximo nivel de zoom (para ver todos los marcadores)", + "marker": { + "marker": "Marcador", + "latitude-key": "Clave de latitud", + "longitude-key": "Clave de longitud", + "x-pos-key": "Clave de posición X", + "y-pos-key": "Clave de posición Y", + "latitude-key-required": "Se requiere la clave de latitud", + "longitude-key-required": "Se requiere la clave de longitud", + "x-pos-key-required": "Se requiere la clave de posición X", + "y-pos-key-required": "Se requiere la clave de posición Y", + "no-markers": "No hay marcadores configurados", + "add-marker": "Añadir marcador", + "marker-configuration": "Configuración del marcador", + "remove-marker": "Eliminar marcador", + "marker-type": "Tipo de marcador", + "marker-type-shape": "Forma", + "marker-type-icon": "Ícono", + "marker-type-image": "Imagen", + "shape": "Forma", + "icon": "Ícono", + "image": "Imagen", + "marker-shapes": "Formas de marcador", + "marker-icon": "Ícono del marcador", + "marker-appearance": "Apariencia del marcador", + "marker-image": "Imagen del marcador", + "marker-image-type-image": "Imagen", + "marker-image-type-function": "Función", + "custom-marker-image-size": "Tamaño personalizado de imagen del marcador", + "marker-image-function": "Función de imagen del marcador", + "marker-images": "Imágenes del marcador", + "marker-offset": "Desplazamiento del marcador", + "offset-horizontal": "Horizontal", + "offset-vertical": "Vertical", + "rotate-marker": "Rotar marcador", + "offset-angle": "Ángulo de desplazamiento", + "position-conversion": "Conversión de posición", + "position-conversion-function": "Función de conversión de posición, debe devolver coordenadas x,y como double de 0 a 1 cada una", + "clustering": { + "use-map-markers-clustering": "Usar agrupamiento de marcadores en el mapa", + "zoom-on-cluster-click": "Acercar al hacer clic en un clúster", + "max-zoom": "Nivel máximo de zoom para que un marcador pueda formar parte de un clúster (0 - 18)", + "max-radius": "Radio máximo que cubrirá un clúster", + "zoom-animation": "Animación en los marcadores al hacer zoom", + "bounds-on-cluster-mouse-over": "Límites de los marcadores al pasar el ratón sobre un clúster", + "spiderfy-max-zoom-level": "Expandir al máximo nivel de zoom (para ver todos los marcadores del clúster)", "load-optimization": "Optimización de carga", - "cluster-chunked-loading": "Usar fragmentos para añadir marcadores para que la página no se congele", - "cluster-markers-lazy-load": "Usar lazy load al añadir marcadores", - "editor-settings": "Ajustes del editor", - "enable-snapping": "Habilitar ajuste a otros vértices para dibujar con precisión", - "init-draggable-mode": "Inicializar mapa en modo arrastre", - "hide-all-edit-buttons": "Ocultar todos los botones de edición", - "hide-draw-buttons": "Ocultar botones de dibujo", - "hide-edit-buttons": "Ocultar botones de edición", - "hide-remove-button": "Ocultar botones de borrado", - "route-map-settings": "Ajustes de mapa de ruta", - "trip-animation-settings": "Ajustes de animación de ruta", - "normalization-step": "Pasos de normalización (ms)", - "tooltip-background-color": "Color de fondo de la sugerencia", - "tooltip-font-color": "Color de fuente de las sugerencias", - "tooltip-opacity": "Opacidad de las sugerencias (0-1)", - "auto-close-tooltip": "Auto-cerrar sugerencias", - "rotation-angle": "Ángulo de rotación adicional para el marcador (grados)", - "path-settings": "Ajustes de ruta", - "path-color": "Color de ruta", - "use-path-color-function": "Usar función de color de ruta", - "path-color-function": "Función de color de ruta", - "path-decorator": "Decorador de ruta", - "use-path-decorator": "Usar decorador de ruta", - "decorator-symbol": "Símbolo del decorador", - "decorator-symbol-arrow-head": "Flecha", - "decorator-symbol-dash": "Estrella", - "decorator-symbol-size": "Tamaño del decorador (px)", - "use-path-decorator-custom-color": "Usar color personalizado en el decorador", - "decorator-custom-color": "Color personalizado del decorador", - "decorator-offset": "Offset decorador", - "end-decorator-offset": "Offset final del decorador", - "decorator-repeat": "Repetición del decorador", - "points-settings": "Ajustes de puntos", - "show-points": "Mostrar puntos", - "point-color": "Color de puntos", - "point-size": "Tamaño de puntos (px)", - "use-point-color-function": "Usar función de color de puntos", - "point-color-function": "Función de color de puntos", - "use-point-as-anchor": "Usar punto como ancla", - "point-as-anchor-function": "Función de punto como ancla", - "independent-point-tooltip": "Sugerencia independiente en punto", - "clustering-markers": "Marcadores agrupados", - "use-icon-create-function": "Usar función de color en marcadores", + "chunked-load": "Usar fragmentos para añadir marcadores y evitar que la página se congele", + "lazy-load": "Usar carga diferida para añadir marcadores", + "use-cluster-marker-color-function": "Usar función de color para los clústeres de marcadores", "marker-color-function": "Función de color del marcador" + }, + "edit": "Editar marcador", + "remove-marker-for": "Eliminar marcador para '{{entityName}}'", + "place-marker": "Colocar marcador", + "place-marker-hint": "Haz clic para colocar marcador", + "place-marker-hint-with-entity": "Haz clic para colocar la entidad '{{entityName}}'" }, - "markdown": { - "use-markdown-text-function": "Usar función markdown/HTML", - "markdown-text-function": "Función de valor Markdown/HTML", - "markdown-text-pattern": "Patrón de Markdown/HTML (markdown o HTML con variables, por ej. '${entityName} o ${keyName} - texto.')", - "apply-default-markdown-style": "Aplicar estilo por defecto en markdown", - "markdown-css": "Markdown/HTML CSS" - }, - "simple-card": { - "label": "Etiqueta", - "label-position": "Posición etiqueta", - "label-position-left": "Izquierda", - "label-position-top": "Superior" - }, - "value-card": { - "layout": "Diseño", - "layout-square": "Cuadrado", - "layout-vertical": "Vertical", - "layout-centered": "Centrado", - "layout-simplified": "Simplificado", - "layout-horizontal": "Horizontal", - "layout-horizontal-reversed": "Horizontal inverso", - "label": "Etiqueta", - "icon": "Icono", - "value": "Valor", - "date": "Fecha", - "value-card-style": "Estilo de tarjeta valores" - }, - "aggregated-value-card": { - "subtitle": "Subtítulo", - "chart": "Gráfico", - "values": "Valores", - "value-appearance": "Apariencia de valores", - "position": "Posición", - "position-center": "Centro", - "position-right-top": "Arriba a la derecha", - "position-right-bottom": "Abajo a la derecha", - "position-left-top": "Arriba a la izquierda", - "position-left-bottom": "Abajo a la izquierda", - "font": "Fuente", - "color": "Color", - "arrow": "Flecha", - "display-up-down-arrow": "Mostrar flecha Arriba/Abajo", - "add-value": "Añadir valor", - "remove-value": "Quitar valor", - "no-values": "No hay valores configurados", - "aggregation": "Agregación", - "aggregated-value-card-style": "Estilo de tarjeta valores agregados" - }, - "alarm-count": { - "alarm-count-card-style": "Estilo de tarjeta contaje de alarmas" + "path": { + "path": "Ruta", + "path-decorator": "Decorador de ruta", + "decorator-symbol": "Símbolo del decorador", + "decorator-symbol-arrow-head": "Flecha", + "decorator-symbol-dash": "Guion", + "decorator-arrangement": "Disposición del decorador", + "decorator-offset": "Inicio", + "decorator-end-offset": "Fin", + "decorator-repeat": "Repetir" }, - "entity-count": { - "entity-count-card-style": "Estilo de tarjeta contaje de entidades" + "points": { + "points": "Puntos", + "point-tooltip": "Tooltip del punto" }, - "count": { - "layout": "Diseño", - "layout-column": "Columna", - "layout-row": "Fila", - "label": "Etiqueta", - "icon": "Icono", - "icon-background": "Fondo de icono", - "value": "Valor", - "chevron": "Chevron" + "shape": { + "fill": "Relleno", + "fill-type-color": "Color", + "fill-type-stripe": "Franja", + "fill-type-image": "Imagen", + "color": "Color", + "stripe": "Franja", + "image": "Imagen", + "stroke": "Contorno", + "fill-image": "Imagen de relleno", + "fill-image-type-image": "Imagen", + "fill-image-type-function": "Función", + "preserve-aspect-ratio": "Preservar proporción", + "opacity": "Opacidad", + "angle": "Ángulo de rotación", + "scale": "Escala", + "fill-image-function": "Función de imagen de relleno de forma", + "fill-images": "Imágenes de relleno de forma", + "stripe-pattern": "Patrón de franjas", + "first-stripe": "Primera franja", + "second-stripe": "Segunda franja" }, - "table": { - "common-table-settings": "Ajustes comunes en tablas", - "enable-search": "Activar búsqueda", - "enable-sticky-header": "Mostrar siempre el encabezado", - "enable-sticky-action": "Mostrar siempre la columna de acciones", - "hidden-cell-button-display-mode": "Visualización de botones de acción oculta", - "show-empty-space-hidden-action": "Mostrar espacio vacío en lugar de celda oculta", - "dont-reserve-space-hidden-action": "No reservar espacio para los botones en celda oculta", - "display-timestamp": "Mostrar columna timestamp", - "display-pagination": "Mostrar páginas", - "default-page-size": "Tamaño de página por defecto", - "use-entity-label-tab-name": "Usar etiqueta de entidad en el nombre de la tabla", - "hide-empty-lines": "Ocultar líneas vacías", - "row-style": "Estilo de fila", - "use-row-style-function": "Usar función de estilo de fila", - "row-style-function": "Función de estilo de fila", - "cell-style": "Estilo de celda", - "use-cell-style-function": "Usar función de estilo de celda", - "cell-style-function": "Función de estilo de celda", - "cell-content": "Contenido de celda", - "use-cell-content-function": "Usar función de contenido de celda", - "cell-content-function": "Función de contenido de celda", - "show-latest-data-column": "Mostrar columna de últimos datos", - "latest-data-column-order": "Órden de columna de últimos datos", - "entities-table-title": "Título de tabla de entidades", - "enable-select-column-display": "Activar posibilidad de seleccionar columnas a mostrar", - "display-entity-name": "Mostrar columna de nombre de entidad", - "entity-name-column-title": "Título de columna en nombre de entidad", - "display-entity-label": "Mostrar columna de etiqueta de entidad", - "entity-label-column-title": "Título de columna en etiqueta de entidad", - "display-entity-type": "Mostrar columna de tipo de entidad", - "default-sort-order": "Ordenación por defecto", - "custom-title": "Título de encabezado", - "column-width": "Ancho de columna (px o %)", - "default-column-visibility": "Visibilidad por defecto en columna", - "column-visibility-visible": "Visible", - "column-visibility-hidden": "Oculta", - "column-visibility-hidden-mobile": "Oculto en modo móvil", - "column-selection-to-display": "Selección de columnas en 'Columnas a Mostrar'", - "column-selection-to-display-enabled": "Activada", - "column-selection-to-display-disabled": "Desactivada", - "alarms-table-title": "Título de tabla de alarmas", - "enable-alarms-selection": "Activar selección de alarmas", - "enable-alarms-search": "Activar búsqueda de alarmas", - "enable-alarm-filter": "Activar filtro de alarmas", - "display-alarm-details": "Mostrar detalles de alarma", - "allow-alarms-ack": "Permitir reconocimiento de alarmas", - "allow-alarms-clear": "Permitir borrado de alarmas", - "display-alarm-activity": "Mostrar actividad de alarmas", - "allow-alarms-assign": "Permitir asignar alarmas", - "columns": "Columnas", - "column-settings": "Ajustes de columnas", - "remove-column": "Quitar columna", - "add-column": "Añadir columna", - "no-columns": "No hay columnas configuradas", - "columns-to-display": "Columnas a Mostrar", - "table-header": "Encabezado de tabla", - "header-buttons": "Botones encabezado", - "table-buttons": "Botones de tabla", - "pagination": "Paginación", - "rows": "Filas", - "timeseries-column-error": "Se debe especificar al menos una columna tipo timeseries", - "table-tabs": "Pestañas de la tabla", - "show-cell-actions-menu-mobile": "Mostrar menú desplegable de acciones de celda en modo móvil" + "polygon": { + "polygon-key": "Clave de polígono", + "polygon-key-required": "Se requiere clave de polígono", + "no-polygons": "No hay polígonos configurados", + "add-polygon": "Añadir polígono", + "polygon-configuration": "Configuración del polígono", + "remove-polygon": "Eliminar polígono", + "edit": "Editar polígono", + "remove-polygon-for": "Eliminar polígono para '{{entityName}}'", + "cut": "Cortar área del polígono", + "rotate": "Rotar polígono", + "draw-rectangle": "Dibujar rectángulo", + "draw-polygon": "Dibujar polígono", + "polygon-place-first-point-cut-hint": "Haz clic para colocar el primer punto", + "continue-polygon-cut-hint": "Haz clic para continuar dibujando", + "finish-polygon-cut-hint": "Haz clic en el primer marcador para finalizar y guardar", + "polygon-place-first-point-hint": "Polígono: haz clic para colocar el primer punto", + "polygon-place-first-point-hint-with-entity": "Polígono para '{{entityName}}': haz clic para colocar el primer punto", + "continue-polygon-hint": "Polígono: haz clic para continuar dibujando", + "continue-polygon-hint-with-entity": "Polígono para '{{entityName}}': haz clic para continuar dibujando", + "finish-polygon-hint": "Polígono: haz clic en el primer marcador para finalizar el dibujo", + "finish-polygon-hint-with-entity": "Polígono para '{{entityName}}': haz clic en el primer marcador para finalizar y guardar", + "rectangle-place-first-point-hint": "Rectángulo: haz clic para colocar el primer punto", + "rectangle-place-first-point-hint-with-entity": "Rectángulo para '{{entityName}}': haz clic para colocar el primer punto", + "finish-rectangle-hint": "Rectángulo: haz clic para finalizar el dibujo", + "finish-rectangle-hint-with-entity": "Rectángulo para '{{entityName}}': haz clic para finalizar y guardar" }, - "value-source": { - "value-source": "Origen valor", - "predefined-value": "Valor predefinido", - "entity-attribute": "Valor tomado de un atributo de entidad", - "value": "Valor", - "source-entity-alias": "Alias entidad de origen", - "source-entity-attribute": "Atributo entidad de origen" + "circle": { + "circle-key": "Clave del círculo", + "circle-key-required": "Se requiere clave del círculo", + "no-circles": "No hay círculos configurados", + "add-circle": "Añadir círculo", + "circle-configuration": "Configuración del círculo", + "remove-circle": "Eliminar círculo", + "edit": "Editar círculo", + "remove-circle-for": "Eliminar círculo para '{{entityName}}'", + "draw-circle": "Dibujar círculo", + "place-circle-center-hint-with-entity": "Círculo para '{{entityName}}': haz clic para colocar el centro del círculo", + "place-circle-center-hint": "Círculo: haz clic para colocar el centro del círculo", + "finish-circle-hint-with-entity": "Círculo para '{{entityName}}': haz clic para finalizar y guardar el círculo", + "finish-circle-hint": "Círculo: haz clic para finalizar el dibujo" }, - "widget-font": { - "font-settings": "Ajustes de fuente", - "font-family": "Familia (font family)", - "size": "Tamaño", - "relative-font-size": "Tamaño fuente relativo (porcentaje)", - "font-style": "Estilo", - "font-style-normal": "Normal", - "font-style-italic": "Cursiva", - "font-style-oblique": "Subrayada", - "font-weight": "Peso", - "font-weight-normal": "Normal", - "font-weight-bold": "Negrita", - "font-weight-bolder": "Negrita+", - "font-weight-lighter": "Lighter", - "color": "Color", - "shadow-color": "Color de sombra", - "preview": "Previsualizar", - "line-height": "Altura de línea", - "auto": "Auto" - }, - "home": { - "no-data-available": "No hay datos disponibles" - }, - "system-info": { - "cpu": "CPU", - "ram": "RAM", - "disk": "Disco", - "cpu-warning-text": "Uso de CPU alto. Para evitar fallos en el sistema, optimiza el rendimiento.", - "cpu-critical-text": "Uso de CPU crítico. Para evitar fallos en el sistema, optimiza el rendimiento.", - "ram-warning-text": "Baja reserva de RAM. Para evitar fallos en el sistema, optimiza el rendimiento o incrementa el tamaño de RAM.", - "ram-critical-text": "Reserva de RAM crítica. Para evitar fallos en el sistema, optimiza el rendimiento o incrementa el tamaño de RAM.", - "disk-warning-text": "Espacio libre en disco bajo. Para evitar pérdida de datos, libera o expande el disco.", - "disk-critical-text": "Espacio libre en disco crítico. Para evitar pérdida de datos, libera o expande el disco." - }, - "cluster-info": { - "service-id": "Id. Servicio", - "service-type": "Tipo de servicio", - "no-data": "Sin datos" - }, - "transport-messages": { - "title": "Mensajes de transporte", - "info": "Todos los mensajes que llegan desde los dispositivos" + "select-entity": "Seleccionar entidad", + "select-entity-hint": "Sugerencia: después de seleccionar, haz clic en el mapa para establecer la posición" + }, + "select-entity": "Seleccionar entidad", + "select-entity-hint": "Sugerencia: después de seleccionar, haz clic en el mapa para establecer la posición", + "tooltips": { + "placeMarker": "Haz clic para colocar la entidad '{{entityName}}'", + "firstVertex": "Polígono para '{{entityName}}': haz clic para colocar el primer punto", + "firstVertex-cut": "Haz clic para colocar el primer punto", + "continueLine": "Polígono para '{{entityName}}': haz clic para continuar el dibujo", + "continueLine-cut": "Haz clic para continuar el dibujo", + "finishLine": "Haz clic en cualquier marcador existente para finalizar", + "finishPoly": "Polígono para '{{entityName}}': haz clic en el primer marcador para finalizar y guardar", + "finishPoly-cut": "Haz clic en el primer marcador para finalizar y guardar", + "finishRect": "Polígono para '{{entityName}}': haz clic para finalizar y guardar", + "startCircle": "Círculo para '{{entityName}}': haz clic para colocar el centro del círculo", + "finishCircle": "Círculo para '{{entityName}}': haz clic para finalizar el círculo", + "placeCircleMarker": "Haz clic para colocar el marcador del círculo" + }, + "actions": { + "finish": "Finalizar", + "cancel": "Cancelar", + "removeLastVertex": "Eliminar último punto" + }, + "buttonTitles": { + "drawMarkerButton": "Colocar entidad", + "drawPolyButton": "Crear polígono", + "drawLineButton": "Crear polilínea", + "drawCircleButton": "Crear círculo", + "drawRectButton": "Crear rectángulo", + "editButton": "Modo de edición", + "dragButton": "Modo arrastrar y soltar", + "cutButton": "Cortar área del polígono", + "deleteButton": "Eliminar", + "drawCircleMarkerButton": "Crear marcador circular", + "rotateButton": "Rotar polígono" + }, + "map-provider-settings": "Configuración del proveedor de mapas", + "map-provider": "Proveedor de mapas", + "map-provider-google": "Google Maps", + "map-provider-openstreet": "Mapas de OpenStreet", + "map-provider-here": "Mapas de HERE", + "map-provider-image": "Mapa de imagen", + "map-provider-tencent": "Mapas de Tencent", + "openstreet-provider": "Proveedor de mapas OpenStreet", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Predeterminado)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery": "Esri.WorldImagery", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Usar proveedor personalizado", + "custom-provider-tile-url": "URL de mosaico del proveedor personalizado", + "google-maps-api-key": "Clave API de Google Maps", + "default-map-type": "Tipo de mapa predeterminado", + "google-map-type-roadmap": "Mapa de carreteras", + "google-map-type-satelite": "Satélite", + "google-map-type-hybrid": "Híbrido", + "google-map-type-terrain": "Terreno", + "map-layer": "Capa del mapa", + "here-map-normal-day": "HERE.normalDay (Predeterminado)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "Credenciales", + "here-app-id": "ID de la aplicación HERE", + "here-app-code": "Código de la aplicación HERE", + "here-api-key": "Clave API de HERE", + "here-use-new-version-api-3": "Usar la versión 3 de la API", + "tencent-maps-api-key": "Clave API de Tencent Maps", + "tencent-map-type-roadmap": "Mapa de carreteras", + "tencent-map-type-satelite": "Satélite", + "tencent-map-type-hybrid": "Híbrido", + "image-map-background": "Fondo del mapa de imagen", + "image-map-background-from-entity-attribute": "Tomar fondo del mapa de imagen desde el atributo de la entidad", + "image-url-source-entity-alias": "Alias de entidad fuente para URL de imagen", + "image-url-source-entity-attribute": "Atributo de entidad fuente para URL de imagen", + "common-map-settings": "Configuración común del mapa", + "x-pos-key-name": "Nombre de la clave de posición X", + "y-pos-key-name": "Nombre de la clave de posición Y", + "latitude-key-name": "Nombre de la clave de latitud", + "longitude-key-name": "Nombre de la clave de longitud", + "default-map-zoom-level": "Nivel de zoom del mapa por defecto (0 - 20)", + "default-map-center-position": "Posición central por defecto del mapa (0,0)", + "disable-scroll-zooming": "Desactivar zoom con desplazamiento", + "disable-double-click-zooming": "Desactivar zoom con doble clic", + "disable-zoom-control-buttons": "Desactivar botones de control de zoom", + "fit-map-bounds": "Ajustar límites del mapa para cubrir todos los marcadores", + "use-default-map-center-position": "Usar la posición central por defecto del mapa", + "entities-limit": "Límite de entidades a cargar", + "markers-settings": "Configuración de marcadores", + "marker-offset-x": "Desplazamiento X del marcador relativo a la posición multiplicado por el ancho del marcador", + "marker-offset-y": "Desplazamiento Y del marcador relativo a la posición multiplicado por la altura del marcador", + "position-function": "Función de conversión de posición, debe devolver coordenadas x,y como dobles entre 0 y 1", + "draggable-marker": "Marcador arrastrable", + "label": "Etiqueta", + "show-label": "Mostrar etiqueta", + "use-label-function": "Usar función de etiqueta", + "label-pattern": "Etiqueta (ejemplos de patrón: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)')", + "label-function": "Función de etiqueta", + "tooltip": "Tooltip", + "show-tooltip": "Mostrar tooltip", + "show-tooltip-action": "Acción para mostrar el tooltip", + "show-tooltip-action-click": "Mostrar tooltip al hacer clic (Predeterminado)", + "show-tooltip-action-hover": "Mostrar tooltip al pasar el cursor", + "auto-close-tooltips": "Cerrar automáticamente los tooltips", + "use-tooltip-function": "Usar función de tooltip", + "tooltip-pattern": "Tooltip (por ejemplo: 'Texto ${keyName} unidades.' o Texto del enlace)", + "tooltip-function": "Función de tooltip", + "tooltip-offset-x": "Desplazamiento X del tooltip relativo al ancla del marcador multiplicado por el ancho del marcador", + "tooltip-offset-y": "Desplazamiento Y del tooltip relativo al ancla del marcador multiplicado por la altura del marcador", + "color": "Color", + "use-color-function": "Usar función de color", + "color-function": "Función de color", + "marker-image": "Imagen del marcador", + "use-marker-image-function": "Usar función de imagen del marcador", + "custom-marker-image": "Imagen de marcador personalizada", + "custom-marker-image-size": "Tamaño de imagen de marcador personalizado (px)", + "marker-image-function": "Función de imagen del marcador", + "marker-images": "Imágenes de marcador", + "polygon-settings": "Configuración del polígono", + "show-polygon": "Mostrar polígono", + "polygon-key-name": "Nombre de clave del polígono", + "enable-polygon-edit": "Habilitar edición del polígono", + "polygon-label": "Etiqueta del polígono", + "show-polygon-label": "Mostrar etiqueta del polígono", + "use-polygon-label-function": "Usar función de etiqueta del polígono", + "polygon-label-pattern": "Etiqueta del polígono (ejemplos: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", + "polygon-label-function": "Función de etiqueta del polígono", + "polygon-tooltip": "Tooltip del polígono", + "show-polygon-tooltip": "Mostrar tooltip del polígono", + "auto-close-polygon-tooltips": "Cerrar automáticamente los tooltips del polígono", + "use-polygon-tooltip-function": "Usar función de tooltip del polígono", + "polygon-tooltip-pattern": "Tooltip (por ejemplo: 'Texto ${keyName} unidades.' o Texto del enlace)", + "polygon-tooltip-function": "Función de tooltip del polígono", + "polygon-color": "Color del polígono", + "polygon-opacity": "Opacidad del polígono", + "use-polygon-color-function": "Usar función de color del polígono", + "polygon-color-function": "Función de color del polígono", + "polygon-stroke": "Contorno del polígono", + "stroke-color": "Color del contorno", + "stroke-opacity": "Opacidad del contorno", + "stroke-weight": "Grosor del contorno", + "use-polygon-stroke-color-function": "Usar función de color del contorno del polígono", + "polygon-stroke-color-function": "Función de color del contorno del polígono", + "circle-settings": "Configuración del círculo", + "show-circle": "Mostrar círculo", + "circle-key-name": "Nombre de clave del círculo", + "enable-circle-edit": "Habilitar edición del círculo", + "circle-label": "Etiqueta del círculo", + "show-circle-label": "Mostrar etiqueta del círculo", + "use-circle-label-function": "Usar función de etiqueta del círculo", + "circle-label-pattern": "Etiqueta del círculo (ejemplos: '${entityName}', '${entityName}: (Texto ${keyName} unidades.)' )", + "circle-label-function": "Función de etiqueta del círculo", + "circle-tooltip": "Tooltip del círculo", + "show-circle-tooltip": "Mostrar tooltip del círculo", + "auto-close-circle-tooltips": "Cerrar automáticamente los tooltips del círculo", + "use-circle-tooltip-function": "Usar función de tooltip del círculo", + "circle-tooltip-pattern": "Tooltip (por ejemplo: 'Texto ${keyName} unidades.' o Texto del enlace)", + "circle-tooltip-function": "Función de tooltip del círculo", + "circle-fill-color": "Color de relleno del círculo", + "circle-fill-color-opacity": "Opacidad del color de relleno del círculo", + "use-circle-fill-color-function": "Usar función de color de relleno del círculo", + "circle-fill-color-function": "Función de color de relleno del círculo", + "circle-stroke": "Contorno del círculo", + "use-circle-stroke-color-function": "Usar función de color del contorno del círculo", + "circle-stroke-color-function": "Función de color del contorno del círculo", + "markers-clustering-settings": "Configuración de agrupación de marcadores", + "use-map-markers-clustering": "Usar agrupación de marcadores en el mapa", + "zoom-on-cluster-click": "Hacer zoom al hacer clic en un clúster", + "max-cluster-zoom": "Nivel máximo de zoom en el que un marcador puede ser parte de un clúster (0 - 18)", + "max-cluster-radius-pixels": "Radio máximo que cubrirá un clúster en píxeles", + "cluster-zoom-animation": "Mostrar animación en los marcadores al hacer zoom", + "show-markers-bounds-on-cluster-mouse-over": "Mostrar los límites de los marcadores al pasar el cursor sobre un clúster", + "spiderfy-max-zoom-level": "Mostrar marcadores del clúster en el nivel de zoom máximo", + "load-optimization": "Optimización de carga", + "cluster-chunked-loading": "Usar fragmentos para agregar marcadores y evitar que la página se congele", + "cluster-markers-lazy-load": "Usar carga diferida para agregar marcadores", + "editor-settings": "Configuración del editor", + "enable-snapping": "Habilitar ajuste a otros vértices para dibujo de precisión", + "init-draggable-mode": "Inicializar el mapa en modo arrastrable", + "hide-all-edit-buttons": "Ocultar todos los botones de control de edición", + "hide-draw-buttons": "Ocultar botones de dibujo", + "hide-edit-buttons": "Ocultar botones de edición", + "hide-remove-button": "Ocultar botón de eliminar", + "route-map-settings": "Configuración del mapa de rutas", + "trip-animation-settings": "Configuración de animación de viaje", + "normalization-step": "Paso de normalización de datos (ms)", + "tooltip-background-color": "Color de fondo del tooltip", + "tooltip-font-color": "Color de fuente del tooltip", + "tooltip-opacity": "Opacidad del tooltip (0-1)", + "auto-close-tooltip": "Cerrar automáticamente el tooltip", + "rotation-angle": "Establecer ángulo de rotación adicional para el marcador (grados)", + "path-settings": "Configuración del recorrido", + "path-color": "Color del recorrido", + "use-path-color-function": "Usar función de color del recorrido", + "path-color-function": "Función de color del recorrido", + "path-decorator": "Decorador del recorrido", + "use-path-decorator": "Usar decorador del recorrido", + "decorator-symbol": "Símbolo del decorador", + "decorator-symbol-arrow-head": "Flecha", + "decorator-symbol-dash": "Guion", + "decorator-symbol-size": "Tamaño del símbolo del decorador (px)", + "use-path-decorator-custom-color": "Usar color personalizado del decorador", + "decorator-custom-color": "Color personalizado del decorador", + "decorator-offset": "Desplazamiento del decorador", + "end-decorator-offset": "Desplazamiento final del decorador", + "decorator-repeat": "Repetición del decorador", + "points-settings": "Configuración de puntos", + "show-points": "Mostrar puntos", + "point-color": "Color del punto", + "point-size": "Tamaño del punto (px)", + "use-point-color-function": "Usar función de color del punto", + "point-color-function": "Función de color del punto", + "use-point-as-anchor": "Usar punto como ancla", + "point-as-anchor-function": "Función de punto como ancla", + "independent-point-tooltip": "Tooltip de punto independiente", + "clustering-markers": "Agrupación de marcadores", + "use-icon-create-function": "Usar función de color de los marcadores", + "marker-color-function": "Función de color del marcador" + }, + "markdown": { + "use-markdown-text-function": "Usar función de valor Markdown/HTML", + "markdown-text-function": "Función de valor Markdown/HTML", + "markdown-text-pattern": "Patrón Markdown/HTML (Markdown o HTML con variables, por ejemplo: '${entityName} o ${keyName} - algún texto.')", + "apply-default-markdown-style": "Aplicar estilo Markdown predeterminado", + "markdown-css": "CSS Markdown/HTML" + }, + "simple-card": { + "label": "Etiqueta", + "label-position": "Posición de la etiqueta", + "label-position-left": "Izquierda", + "label-position-top": "Superior" + }, + "single-switch": { + "behavior": "Comportamiento", + "layout": "Diseño", + "layout-right": "Derecha", + "layout-left": "Izquierda", + "layout-centered": "Centrado", + "auto-scale": "Escalado automático", + "label": "Etiqueta", + "icon": "Ícono", + "switch-color": "Color del interruptor", + "on": "Encendido", + "off": "Apagado", + "disabled": "Desactivado", + "tumbler-color": "Color del conmutador", + "on-label": "Etiqueta de encendido", + "off-label": "Etiqueta de apagado", + "switch": "Interruptor" + }, + "slider": { + "behavior": "Comportamiento", + "initial-value": "Valor inicial", + "initial-value-hint": "Acción para obtener el valor inicial del deslizador.", + "on-value-change": "Al cambiar el valor", + "on-value-change-hint": "Acción al cambiar el valor del deslizador.", + "layout": "Diseño", + "layout-default": "Predeterminado", + "layout-extended": "Extendido", + "layout-simplified": "Simplificado", + "auto-scale": "Escalado automático", + "icon": "Ícono", + "value": "Valor", + "range": "Rango", + "min": "mín", + "max": "máx", + "range-ticks": "Marcas del rango", + "tick-marks": "Marcas de verificación", + "colors": "Colores", + "main": "Principal", + "background": "Fondo", + "left-icon": "Ícono izquierdo", + "right-icon": "Ícono derecho", + "slider": "Deslizador" + }, + "value-card": { + "layout": "Diseño", + "layout-square": "Cuadrado", + "layout-vertical": "Vertical", + "layout-centered": "Centrado", + "layout-simplified": "Simplificado", + "layout-horizontal": "Horizontal", + "layout-horizontal-reversed": "Horizontal invertido", + "label": "Etiqueta", + "icon": "Ícono", + "value": "Valor", + "date": "Fecha", + "value-card-style": "Estilo de tarjeta de valor", + "auto-scale": "Escalado automático" + }, + "label-card": { + "auto-scale": "Escalado automático", + "label": "Etiqueta", + "icon": "Ícono", + "label-card-style": "Estilo de tarjeta de etiqueta" + }, + "label-value-card": { + "value": "Valor", + "label-value-card-style": "Estilo de tarjeta de etiqueta y valor" + }, + "liquid-level-card": { + "layout-simple": "Simple", + "layout-percentage": "Porcentaje", + "layout-absolute": "Absoluto", + "layout": "Diseño", + "background-overlay": "Superposición de fondo del valor", + "total-volume": "Volumen total", + "total-volume-units": "Unidades de volumen total", + "tank": "Tanque", + "shape": "Forma", + "datasource-units": "Unidades de origen", + "widget-units": "Unidades del widget", + "decimals": "Decimales", + "liquid": "Líquido", + "liquid-color": "Color del líquido", + "value": "Valor", + "value-font": "Fuente del valor", + "level": "Nivel", + "last-update": "Última actualización", + "shape-by-attribute": "Establecer forma del tanque por nombre de atributo", + "tooltip-background": "Color de fondo", + "background-blur": "Desenfoque del fondo", + "tank-color": "Color del tanque", + "static": "Estático", + "see-examples": "Ver ejemplos", + "attribute": "Atributo", + "shape-type": "Tipo", + "v-oval": "Óvalo vertical", + "v-cylinder": "Cilindro vertical", + "v-capsule": "Cápsula vertical", + "rectangle": "Rectángulo", + "h-oval": "Óvalo horizontal", + "h-ellipse": "Elipse horizontal", + "h-dish-ends": "Extremos cóncavos horizontales", + "h-cylinder": "Cilindro horizontal", + "h-capsule": "Cápsula horizontal", + "h-elliptical_2_1": "Horizontal 2:1 Elíptico", + "icon": "Ícono de tarjeta", + "title": "Título de tarjeta", + "units": "Unidades", + "color-and-font": "Color y fuente", + "shape-attribute-name": "Nombre del atributo", + "total-volume-required": "El volumen total es obligatorio.", + "attribute-name-required": "El nombre del atributo es obligatorio.", + "attribute-key-not-set": "La clave del atributo '{{attributeName}}' no está definida", + "attribute-key-invalid": "La clave del atributo '{{attributeName}}' no es válida" + }, + "aggregated-value-card": { + "subtitle": "Subtítulo", + "chart": "Gráfico", + "values": "Valores", + "value-appearance": "Apariencia del valor", + "position": "Posición", + "position-center": "Centro", + "position-right-top": "Arriba a la derecha", + "position-right-bottom": "Abajo a la derecha", + "position-left-top": "Arriba a la izquierda", + "position-left-bottom": "Abajo a la izquierda", + "font": "Fuente", + "color": "Color", + "arrow": "Flecha", + "display-up-down-arrow": "Mostrar flecha de subida/bajada", + "add-value": "Agregar valor", + "remove-value": "Eliminar valor", + "no-values": "No hay valores configurados", + "aggregation": "Agregación", + "aggregated-value-card-style": "Estilo de tarjeta de valor agregado", + "auto-scale": "Escalado automático" + }, + "value-chart-card": { + "layout": "Diseño", + "layout-left": "Izquierda", + "layout-right": "Derecha", + "auto-scale": "Escalado automático", + "icon": "Ícono", + "value": "Valor", + "chart": "Gráfico", + "value-chart-card-style": "Estilo de tarjeta de gráfico de valor" + }, + "progress-bar": { + "layout": "Diseño", + "layout-default": "Predeterminado", + "layout-simplified": "Simplificado", + "auto-scale": "Escalado automático", + "icon": "Ícono", + "value": "Valor", + "range": "Rango", + "min": "mín", + "max": "máx", + "range-ticks": "Marcas de rango", + "bar": "Barra", + "bar-color": "Color de la barra", + "bar-background": "Fondo de la barra", + "progress-bar-card-style": "Estilo de tarjeta de barra de progreso" + }, + "notification": { + "max-notification-display": "Máximo de notificaciones a mostrar", + "counter": "Contador", + "counter-hint": "El contador se mostrará si el \"Título del widget\" está habilitado", + "icon": "Ícono", + "counter-value": "Valor", + "counter-color": "Color", + "notification-button": "Botones de notificación", + "button-view-all": "Ver todo", + "button-filter": "Filtrar", + "type-filter": "Filtro por tipo", + "button-mark-read": "Marcar todo como leído", + "notification-types": "Tipos de notificación", + "notification-type": "Tipo de notificación", + "search-type": "Tipo de búsqueda", + "any-type": "Cualquier tipo" + }, + "alarm-count": { + "alarm-count-card-style": "Estilo de tarjeta de conteo de alarmas" + }, + "entity-count": { + "entity-count-card-style": "Estilo de tarjeta de conteo de entidades" + }, + "count": { + "layout": "Diseño", + "layout-column": "Columna", + "layout-row": "Fila", + "label": "Etiqueta", + "icon": "Ícono", + "icon-background": "Fondo del ícono", + "value": "Valor", + "chevron": "Chevron", + "auto-scale": "Escalado automático" + }, + "table": { + "common-table-settings": "Configuraciones comunes de la tabla", + "enable-search": "Habilitar búsqueda", + "enable-sticky-header": "Mostrar encabezado siempre", + "enable-sticky-action": "Mostrar columna de acciones siempre", + "hidden-cell-button-display-mode": "Modo de visualización del botón de acción oculto", + "show-empty-space-hidden-action": "Mostrar espacio vacío en lugar del botón de acción oculto", + "dont-reserve-space-hidden-action": "No reservar espacio para botones de acción ocultos", + "display-timestamp": "Marca de tiempo", + "display-pagination": "Mostrar paginación", + "default-page-size": "Tamaño de página predeterminado", + "page-step-settings": "Configuraciones de pasos de página", + "page-step-count": "Número de pasos", + "page-step-increment": "Incremento de paso", + "page-step-count-format-message": "Debe ser un número entero, en el rango de 1 a 100.", + "page-step-increment-format-message": "Debe ser un número entero, mayor o igual a 1.", + "use-entity-label-tab-name": "Usar etiqueta de entidad como nombre de pestaña", + "hide-empty-lines": "Ocultar líneas vacías", + "row-style": "Estilo de fila", + "use-row-style-function": "Usar función de estilo de fila", + "row-style-function": "Función de estilo de fila", + "cell-style": "Estilo de celda", + "use-cell-style-function": "Usar función de estilo de celda", + "cell-style-function": "Función de estilo de celda", + "cell-content": "Contenido de la celda", + "use-cell-content-function": "Usar función de contenido de celda", + "cell-content-function": "Función de contenido de celda", + "show-latest-data-column": "Mostrar columna de datos más recientes", + "latest-data-column-order": "Orden de la columna de datos más recientes", + "entities-table-title": "Título de la tabla de entidades", + "enable-select-column-display": "Habilitar selección de columnas para mostrar", + "display-entity-name": "Mostrar columna de nombre de entidad", + "entity-name-column-title": "Título de columna de nombre de entidad", + "display-entity-label": "Mostrar columna de etiqueta de entidad", + "entity-label-column-title": "Título de columna de etiqueta de entidad", + "display-entity-type": "Mostrar columna de tipo de entidad", + "default-sort-order": "Orden de clasificación predeterminado", + "custom-title": "Título personalizado del encabezado", + "column-width": "Ancho de columna (px o %)", + "default-column-visibility": "Visibilidad de columna predeterminada", + "column-visibility-visible": "Visible", + "column-visibility-hidden": "Oculto", + "column-visibility-hidden-mobile": "Oculto en modo móvil", + "column-selection-to-display": "Selección de columna en 'Columnas para mostrar'", + "column-selection-to-display-enabled": "Habilitado", + "column-selection-to-display-disabled": "Deshabilitado", + "alarms-table-title": "Título de la tabla de alarmas", + "enable-alarms-selection": "Habilitar selección de alarmas", + "enable-alarms-search": "Habilitar búsqueda de alarmas", + "enable-alarm-filter": "Habilitar filtro de alarmas", + "display-alarm-details": "Mostrar detalles de alarma", + "allow-alarms-ack": "Permitir reconocimiento de alarmas", + "allow-alarms-clear": "Permitir limpiar alarmas", + "display-alarm-activity": "Mostrar actividad de la alarma", + "allow-alarms-assign": "Permitir asignación de alarmas", + "columns": "Columnas", + "column-settings": "Configuraciones de columna", + "remove-column": "Eliminar columna", + "add-column": "Agregar columna", + "no-columns": "No hay columnas configuradas", + "columns-to-display": "Columnas para mostrar", + "table-header": "Encabezado de la tabla", + "header-buttons": "Botones del encabezado", + "table-buttons": "Botones de la tabla", + "pagination": "Paginación", + "rows": "Filas", + "timeseries-column-error": "Debe especificarse al menos una columna de serie temporal", + "alarm-column-error": "Debe especificarse al menos una columna de alarma", + "table-tabs": "Pestañas de la tabla", + "show-cell-actions-menu-mobile": "Mostrar menú desplegable de acciones de celda en modo móvil", + "disable-sorting": "Deshabilitar ordenamiento" + }, + "latest-chart": { + "total": "Total", + "auto-scale": "Escalado automático", + "clockwise-layout": "Disposición en sentido horario", + "sort-series": "Ordenar series por etiqueta", + "tooltip-value-type-absolute": "Absoluto", + "tooltip-value-type-percentage": "Porcentaje" + }, + "pie-chart": { + "pie-chart-appearance": "Apariencia del gráfico circular", + "label": "Etiqueta", + "border": "Borde", + "radius": "Radio", + "pie-chart-card-style": "Estilo de tarjeta de gráfico circular" + }, + "radar-chart": { + "radar-appearance": "Apariencia del gráfico de radar", + "shape": "Forma", + "shape-polygon": "Polígono", + "shape-circle": "Círculo", + "color": "Color", + "line": "Línea", + "points": "Puntos", + "points-label": "Etiqueta de puntos", + "radar-axis": "Eje de radar", + "axis-label": "Etiqueta del eje", + "ticks-label": "Etiqueta de marcas", + "radar-chart-style": "Estilo de gráfico de radar" + }, + "time-series-chart": { + "chart": "Gráfico", + "chart-style": "Estilo de gráfico", + "data-zoom": "Zoom de datos", + "stack-mode": "Modo de apilamiento", + "stack-mode-hint": "Apila las series en el gráfico. Las series con la misma unidad se superponen.", + "axes": "Ejes", + "y-axes": "Ejes Y", + "line-type": "Tipo de línea", + "line-width": "Ancho de línea", + "type-line": "Línea", + "type-bar": "Barra", + "type-point": "Punto", + "no-aggregation-bar-width-strategy": "Estrategia de ancho de barra para datos no agregados", + "no-aggregation-bar-width-strategy-group": "Agrupar", + "no-aggregation-bar-width-strategy-separate": "Separado", + "bar-group-width": "Ancho del grupo de barras", + "bar-width": "Ancho de barra", + "bar-width-relative": "Porcentaje de la ventana temporal", + "bar-width-absolute": "Absoluto (ms)", + "comparison": { + "comparison": "Comparación", + "comparison-hint": "¡La comparación solo funciona con datos históricos!", + "show": "Mostrar", + "settings": "Configuración de comparación", + "show-values-for-comparison": "Mostrar datos históricos para la comparación", + "comparison-values-label": "Etiqueta de clave de comparación", + "comparison-values-label-auto": "Automático", + "comparison-data-color": "Color de los datos de comparación" + }, + "threshold": { + "thresholds": "Umbrales", + "source": "Fuente", + "key-value": "Clave / Valor", + "no-thresholds": "No hay umbrales configurados", + "add-threshold": "Agregar umbral", + "type-constant": "Constante", + "type-latest-key": "Clave", + "type-entity": "Entidad", + "threshold-settings": "Configuración del umbral", + "remove-threshold": "Eliminar umbral", + "threshold-value-required": "Se requiere el valor del umbral.", + "key-required": "Se requiere clave.", + "entity-key-required": "Se requiere clave de entidad.", + "line-appearance": "Apariencia de la línea", + "line-color": "Color de la línea", + "start-symbol": "Símbolo inicial", + "end-symbol": "Símbolo final", + "symbol-size": "tamaño", + "label": "Etiqueta", + "label-position-start": "Inicio", + "label-position-middle": "Medio", + "label-position-end": "Final", + "label-position-inside-start": "Interior inicio", + "label-position-inside-start-top": "Interior inicio superior", + "label-position-inside-start-bottom": "Interior inicio inferior", + "label-position-inside-middle": "Interior medio", + "label-position-inside-middle-top": "Interior medio superior", + "label-position-inside-middle-bottom": "Interior medio inferior", + "label-position-inside-end": "Interior final", + "label-position-inside-end-top": "Interior final superior", + "label-position-inside-end-bottom": "Interior final inferior", + "label-background": "Fondo de etiqueta" + }, + "state": { + "states": "Estados", + "label": "Etiqueta", + "ticks-value": "Valor de ticks", + "source": "Fuente", + "value-range": "Valor / Rango", + "no-states": "No hay estados configurados", + "add-state": "Agregar estado", + "type-constant": "Constante", + "type-range": "Rango", + "from": "Desde", + "to": "Hasta", + "remove-state": "Eliminar estado" + }, + "grid": { + "grid": "Cuadrícula", + "background-color": "Color de fondo", + "border": "Borde" + }, + "axis": { + "axes": "Ejes", + "x-axis": "Eje X", + "y-axis": "Eje Y", + "y-axis-settings": "Configuración del eje Y", + "comparison-x-axis-settings": "Configuración del eje X para comparación", + "remove-y-axis": "Eliminar eje Y", + "id": "Id", + "label": "Etiqueta", + "position": "Posición", + "position-left": "Izquierda", + "position-right": "Derecha", + "position-top": "Arriba", + "position-bottom": "Abajo", + "tick-labels": "Etiquetas de ticks", + "ticks-formatter-function": "Función de formato de ticks", + "ticks-generator-function": "Función generadora de ticks", + "show-ticks": "Mostrar ticks", + "show-line": "Mostrar línea", + "show-split-lines": "Mostrar líneas divididas", + "show-split-lines-x-axis-hint": "Si está habilitado, se mostrarán las líneas verticales en el gráfico.", + "show-split-lines-y-axis-hint": "Si está habilitado, se mostrarán las líneas horizontales en el gráfico.", + "ticks-interval": "Intervalo de ticks", + "ticks-interval-hint": "Establecer intervalo de segmentación para el eje.", + "split-number": "Número de divisiones", + "split-number-hint": "Número de segmentos en que se divide el eje.", + "min": "Mínimo", + "max": "Máximo", + "show": "Mostrar", + "add-y-axis": "Agregar eje Y" + }, + "series": { + "legend-settings": "Configuración de leyenda", + "show-in-legend": "Mostrar en la leyenda", + "show-in-legend-hint": "Mostrar nombre de la serie y datos en la leyenda.", + "hidden-by-default": "Ocultar por defecto", + "hidden-by-default-hint": "Ocultar la serie en la leyenda por defecto.", + "series-type": "Tipo de serie", + "type": "Tipo", + "type-line": "Línea", + "type-bar": "Barra", + "line": { + "line": "Línea", + "show-line": "Mostrar línea", + "step-line": "Línea escalonada", + "step-type-start": "Inicio", + "step-type-middle": "Medio", + "step-type-end": "Fin", + "smooth-line": "Línea suave" }, - "activity": { - "title": "Actividad" + "point": { + "points": "Puntos", + "show-points": "Mostrar puntos", + "point-label": "Etiqueta del punto", + "point-label-hint": "Mostrar etiqueta con valor sobre el punto de la serie.", + "point-label-background": "Fondo de etiqueta del punto", + "point-shape": "Forma del punto", + "point-size": "Tamaño del punto" + } + } + }, + "wind-speed-direction": { + "layout": "Diseño", + "layout-default": "Por defecto", + "layout-advanced": "Avanzado", + "layout-simplified": "Simplificado", + "values": "Valores", + "wind-direction": "Dirección del viento", + "center-value": "Valor central", + "icon": "Ícono", + "arrow": "Flecha", + "ticks": "Marcas", + "labels-type": "Tipo de etiquetas", + "directional-names": "Nombres direccionales", + "degrees": "Grados", + "major-ticks": "Marcas mayores", + "minor-ticks": "Marcas menores", + "wind-speed-direction-card-style": "Estilo de tarjeta de velocidad y dirección del viento", + "ticks-color": "Color de las marcas", + "ticks-labels-type": "Tipo de etiquetas de marcas", + "arrow-color": "Color de la flecha" + }, + "value-source": { + "value-source": "Fuente de valor", + "predefined-value": "Constante", + "entity-attribute": "Atributo de entidad", + "value": "Valor", + "value-required": "El valor es obligatorio.", + "key-required": "La clave es obligatoria.", + "entity-key-required": "La clave de la entidad es obligatoria.", + "source-entity-alias": "Alias de entidad de origen", + "source-entity-attribute": "Atributo de entidad de origen", + "type-constant": "Constante", + "type-latest-key": "Clave", + "type-entity": "Entidad" + }, + "rpc-state": { + "initial-state": "Estado inicial", + "initial-state-hint": "Acción para obtener el estado inicial (Encendido/Apagado) del componente.", + "disabled-state": "Estado deshabilitado", + "disabled-state-hint": "Configurar condición bajo la cual el componente está deshabilitado.", + "turn-on": "Encender", + "turn-on-hint": "Acción al cambiar el control deslizante a 'Encendido'", + "turn-off": "Apagar", + "turn-off-hint": "Acción al cambiar el control deslizante a 'Apagado'", + "on": "Encendido", + "off": "Apagado", + "disabled": "Deshabilitado" + }, + "value-action": { + "do-nothing": "No hacer nada", + "execute-rpc": "Ejecutar RPC", + "get-attribute": "Obtener atributo", + "set-attribute": "Establecer atributo", + "get-time-series": "Obtener serie temporal", + "get-alarm-status": "Obtener estado de alarma", + "get-dashboard-state": "Obtener ID del estado del tablero", + "get-dashboard-state-object": "Obtener objeto de estado del tablero", + "add-time-series": "Agregar serie temporal", + "execute-rpc-text": "Ejecutar método RPC '{{methodName}}'", + "get-time-series-text": "Usar serie temporal '{{key}}'", + "get-attribute-text": "Usar atributo '{{key}}'", + "get-alarm-status-text": "Usar estado de la alarma", + "get-dashboard-state-text": "Usar estado del tablero", + "get-dashboard-state-object-text": "Usar objeto de estado del tablero", + "when-dashboard-state-is-text": "Cuando el ID del estado del tablero es '{{state}}'", + "when-dashboard-state-function-is-text": "Cuando f(ID del estado del tablero) es '{{state}}'", + "when-dashboard-state-object-function-is-text": "Cuando f(objeto del estado del tablero) es '{{state}}'", + "set-attribute-to-value-text": "Establecer el atributo '{{key}}' a: {{value}}", + "add-time-series-value-text": "Agregar valor '{{value}}' a la serie temporal '{{key}}'", + "set-attribute-text": "Establecer atributo '{{key}}'", + "add-time-series-text": "Agregar serie temporal '{{key}}'", + "action": "Acción", + "value": "Valor", + "init-value-hint": "Valor que se establecerá hasta que el dispositivo envíe datos.", + "method": "Método", + "method-name-required": "El nombre del método es obligatorio.", + "request-timeout-ms": "Tiempo de espera de la solicitud RPC (ms)", + "request-timeout-required": "El tiempo de espera de la solicitud es obligatorio.", + "min-request-timeout-error": "El tiempo de espera debe ser mayor o igual a 5000 ms (5 segundos).", + "request-persistent": "Solicitud RPC persistente", + "persistent-polling-interval": "Intervalo de sondeo persistente (ms)", + "persistent-polling-interval-hint": "Intervalo de sondeo (ms) para obtener la respuesta del comando RPC persistente", + "persistent-polling-interval-required": "El intervalo de sondeo persistente es obligatorio.", + "min-persistent-polling-interval-error": "El valor del intervalo debe ser mayor o igual a 1000 ms (1 segundo).", + "attribute-scope": "Ámbito del atributo", + "attribute-key": "Clave del atributo", + "attribute-key-required": "La clave del atributo es obligatoria.", + "time-series-key": "Clave de la serie temporal", + "time-series-key-required": "La clave de la serie temporal es obligatoria.", + "action-result-converter": "Convertidor de resultados de la acción", + "converter-none": "Ninguno", + "converter-function": "Función", + "converter-constant": "Constante", + "converter-value": "Valor", + "parse-value-function": "Función para analizar el valor", + "state-when-result-is": "'{{state}}' cuando el resultado es", + "parameters": "Parámetros", + "convert-value-function": "Función para convertir valor", + "error": { + "target-entity-is-not-set": "¡Entidad de destino no definida!", + "failed-to-perform-action": "No se pudo realizar la acción {{ actionLabel }}.", + "invalid-attribute-scope": "El ámbito '{{scope}}' no es compatible con la entidad {{entityType}}." + } + }, + "widget-font": { + "font-settings": "Configuración de fuente", + "font-family": "Familia de fuente", + "size": "Tamaño", + "relative-font-size": "Tamaño de fuente relativo (porcentaje)", + "font-style": "Estilo", + "font-style-normal": "Normal", + "font-style-italic": "Cursiva", + "font-style-oblique": "Oblicua", + "font-weight": "Peso", + "font-weight-normal": "Normal", + "font-weight-bold": "Negrita", + "font-weight-bolder": "Más negrita", + "font-weight-lighter": "Más ligera", + "color": "Color", + "shadow-color": "Color de sombra", + "preview": "Vista previa", + "line-height": "Altura de línea", + "auto": "Auto" + }, + "home": { + "no-data-available": "No hay datos disponibles" + }, + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "Disco", + "cpu-warning-text": "Uso elevado de CPU. Para evitar fallos del sistema, optimice el rendimiento.", + "cpu-critical-text": "Uso crítico de CPU. Para evitar fallos del sistema, optimice el rendimiento.", + "ram-warning-text": "Baja reserva de RAM. Para evitar fallos del sistema, optimice el rendimiento o aumente la memoria.", + "ram-critical-text": "Reserva crítica de RAM. Para evitar fallos del sistema, optimice el rendimiento o aumente la memoria.", + "disk-warning-text": "Espacio en disco reducido. Para evitar pérdida de datos, libere o expanda el espacio.", + "disk-critical-text": "Espacio en disco críticamente bajo. Para evitar pérdida de datos, libere o expanda el espacio." + }, + "cluster-info": { + "service-id": "ID del servicio", + "service-type": "Tipo de servicio", + "no-data": "Sin datos" + }, + "transport-messages": { + "title": "Mensajes de transporte", + "info": "Todos los mensajes que llegaron desde los dispositivos" + }, + "activity": { + "title": "Actividad" + }, + "documentation": { + "title": "Documentación", + "add-link": "Agregar enlace", + "add-link-title": "Agregar enlace de documentación", + "name": "Nombre", + "name-required": "El nombre es obligatorio.", + "link": "Enlace", + "link-required": "El enlace es obligatorio.", + "columns": "Columnas" + }, + "quick-links": { + "title": "Enlaces rápidos", + "add-link": "Agregar enlace", + "add-link-title": "Agregar enlace rápido", + "quick-link": "Enlace rápido", + "quick-link-required": "El enlace rápido es obligatorio.", + "no-links-matching": "No se encontraron enlaces coincidentes con '{{name}}'.", + "columns": "Columnas" + }, + "recent-dashboards": { + "title": "Tableros", + "last": "Vistos recientemente", + "starred": "Favoritos", + "name": "Nombre", + "last-viewed": "Última visualización", + "no-last-viewed-dashboards": "Aún no se han visualizado tableros recientemente" + }, + "configured-features": { + "title": "Características configuradas", + "info": "Estado de las características que requieren configuración", + "email-feature": "Correo electrónico", + "sms-feature": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "feature-configured": "Función configurada.\nHaga clic para configurar", + "feature-not-configured": "Función no configurada.\nHaga clic para configurar" + }, + "version-info": { + "title": "Versión", + "contact-us": "Contáctenos", + "current-version": "Versión actual", + "current": "Actual", + "available-version": "Versión disponible", + "available": "Disponible", + "upgrade": "Actualizar", + "version-is-up-to-date": "La versión está actualizada" + }, + "usage-info": { + "title": "Uso", + "entities": "Entidades", + "api-calls": "Llamadas API" + }, + "functions": { + "title": "Funciones", + "pe-feature-tooltip": "Disponible solo en ThingsBoard\nProfessional Edition", + "switch-to-pe": "Cambiar a PE", + "alarms": "Alarmas", + "dashboards": "Tableros", + "entities-and-relations": "Entidades y Relaciones", + "profiles": "Perfiles", + "advanced-features": "Funciones avanzadas", + "notification-center": "Centro de notificaciones", + "api-usage": "Uso de API", + "customers": "Clientes", + "customers-hierarchy": "Jerarquía de clientes", + "roles-and-permissions": "Roles y permisos", + "groups": "Grupos", + "integrations": "Integraciones", + "solution-templates": "Plantillas de solución", + "scheduler": "Planificador", + "white-labeling": "Marca blanca" + }, + "devices": { + "view-docs": "Ver documentación", + "inactive": "Inactivo", + "active": "Activo", + "total": "Total" + }, + "alarms": { + "critical": "Crítico", + "assigned-to-me": "Asignado a mí", + "total": "Total" + }, + "getting-started": { + "get-started": "Comenzar", + "finish": "Finalizar", + "done-welcome-title": "Bienvenido a bordo", + "done-welcome-text": "¡Lo hiciste genial!", + "sys-admin": { + "step1": { + "title": "Crear Tenant y Administrador del Tenant", + "content": "

Un tenant es una persona u organización que posee o produce dispositivos y activos. El tenant puede tener múltiples usuarios administradores, clientes, dispositivos y activos.

El Administrador del Tenant puede crear y gestionar dispositivos, activos, clientes y tableros dentro de la cuenta del tenant.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-create-tenant": "Cómo crear Tenant y Administrador del Tenant" }, - "documentation": { - "title": "Documentación", - "add-link": "Añadir enlace", - "add-link-title": "Añadir enlace de documentación", - "name": "Nombre", - "name-required": "Se requiere nombre.", - "link": "Enlace", - "link-required": "Se requiere enlace.", - "columns": "Columnas" + "step2": { + "title": "Configurar función: Servidor de correo", + "content": "

La configuración del servidor de correo es esencial para la activación de usuarios, recuperación de contraseñas y entrega de notificaciones de alarmas.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-configure-mail-server": "Cómo configurar el servidor de correo" }, - "quick-links": { - "title": "Enlaces rápidos", - "add-link": "Añadir enlace", - "add-link-title": "Añadir enlace rápido", - "quick-link": "Enlace rápido", - "quick-link-required": "Se requiere enlace rápido.", - "no-links-matching": "No se encontraron enlaces que coincidan con '{{name}}'.", - "columns": "Columnas" + "step3": { + "title": "Configurar función: Proveedor de SMS", + "content": "

Configura proveedores de SMS para notificar a los clientes sobre las alarmas vía SMS.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-configure-sms-provider": "Cómo configurar el proveedor de SMS" }, - "recent-dashboards": { - "title": "Paneles", - "last": "Última visualización", - "starred": "Favorito", - "name": "Nombre", - "last-viewed": "Último visto", - "no-last-viewed-dashboards": "No hay paneles aún" + "step4": { + "title": "Configurar función: White-labeling", + "content": "

Personaliza fácilmente el logotipo y el esquema de colores de tu empresa o producto sin necesidad de codificación y sin reiniciar el servicio.

Sigue la documentación para saber cómo hacerlo:

" }, - "configured-features": { - "title": "Características configuradas", - "info": "Estado de las características que requieren configuración", - "email-feature": "Email", - "sms-feature": "SMS", - "slack-feature": "Slack", - "oauth2-feature": "OAuth 2", - "2fa-feature": "2FA", - "feature-configured": "Característica configurada.\nClick para acceder a configuración", - "feature-not-configured": "Característica no configurada.\nClick para configurar" + "step5": { + "title": "Configurar función: 2FA", + "content": "

Mejora la seguridad de las cuentas de la plataforma con autenticación de dos factores.

Sigue la documentación para saber cómo hacerlo:

" }, - "version-info": { - "title": "Versión", - "contact-us": "Contáctanos", - "current-version": "Versión actual", - "current": "Actual", - "available-version": "Versión disponible", - "available": "Disponible", - "upgrade": "Actualizar", - "version-is-up-to-date": "La versión está actualizada" + "step6": { + "title": "Configurar función: OAuth 2", + "content": "

Simplifica el inicio de sesión para los usuarios de tenant y clientes con inicio de sesión único a través de OAuth 2.0.

Sigue la documentación para saber cómo hacerlo:

" + } + }, + "tenant-admin": { + "step1": { + "title": "Crear dispositivo", + "content": "

Vamos a aprovisionar tu primer dispositivo en la plataforma mediante la interfaz de usuario. Sigue la documentación para saber cómo hacerlo:

", + "how-to-create-device": "Cómo crear un dispositivo" }, - "usage-info": { - "title": "Uso", - "entities": "Entidades", - "api-calls": "Llamadas API" + "step2": { + "title": "Conectar dispositivo", + "content-before": "

Para conectar el dispositivo necesitas obtener las credenciales del dispositivo. Recomendamos usar las credenciales autogeneradas por defecto, que en esta guía es un token de acceso.

  • Ve a la tabla de dispositivos
  • Haz clic en la fila del dispositivo para abrir los detalles
  • Presiona el botón \"Copiar token de acceso\"

Utiliza comandos simples para publicar datos por HTTP. No olvides reemplazar $ACCESS_TOKEN con el token de acceso de tu dispositivo:

", + "ubuntu": { + "install-curl": "Instalar cURL en Ubuntu:" + }, + "macos": { + "install-curl": "Instalar cURL en MacOS:" + }, + "windows": { + "install-curl": "Desde Windows 10 b17063, cURL está disponible por defecto." + }, + "replace-access-token": "Reemplaza $ACCESS_TOKEN con el token de tu dispositivo:", + "content-after": "

También puedes usar otros protocolos como MQTT, CoAP, etc.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-connect-device": "Cómo conectar un dispositivo" }, - "functions": { - "title": "Funciones", - "pe-feature-tooltip": "Sólo en ThingsBoard\nProfessional Edition", - "switch-to-pe": "Cambiar a PE", - "alarms": "Alarmas", - "dashboards": "Paneles", - "entities-and-relations": "Entidades y relaciones", - "profiles": "Perfiles", - "advanced-features": "Características avanzadas", - "notification-center": "Centro de notificaciones", - "api-usage": "Uso de API", - "customers": "Clientes", - "customers-hierarchy": "Jerarquía de clientes", - "roles-and-permissions": "Reglas y permisos", - "groups": "Grupos", - "integrations": "Integraciones", - "solution-templates": "Plantillas de solución", - "scheduler": "Programador", - "white-labeling": "Marca-blanca" + "step3": { + "title": "Crear tablero", + "content": "

Crea un tablero para visualizar datos de entidades como activos, dispositivos, etc.

Sigue la documentación para saber cómo hacerlo:

", + "how-to-create-dashboard": "Cómo crear un tablero" }, - "devices": { - "view-docs": "Ver documentación", - "inactive": "Inactivos", - "active": "Activos", - "total": "Total" + "step4": { + "title": "Configurar reglas de alarma", + "alarm-rules": "Reglas de alarma", + "content": "

Vamos a generar una alarma cuando la temperatura alcance los 25°C. Sigue la documentación para saber cómo hacerlo:

", + "how-to-configure-alarm-rules": "Cómo configurar reglas de alarma" }, - "alarms": { - "critical": "Críticas", - "assigned-to-me": "Asignadas a mí", - "total": "Total" + "step5": { + "title": "Crear alarma", + "content-before": "

Para activar la alarma, envía un nuevo valor de telemetría de 26°C o más.

", + "replace-access-token": "Reemplaza $ACCESS_TOKEN con el token de tu dispositivo:", + "content-after": "

Sigue la documentación para saber cómo hacerlo:

", + "how-to-create-alarm": "Cómo crear una alarma" }, - "getting-started": { - "get-started": "Empezar", - "finish": "Finalizar", - "done-welcome-title": "Bienvenido a bordo", - "done-welcome-text": "Fantástico, lo has terminado!", - "sys-admin": { - "step1": { - "title": "Crear un propietario y un administrador propietario", - "content": "

Un propietario es un individual o una organización, que tiene o produce dispositivos o activos. Los Propietarios, pueden crear usuarios tipo Administrador Propietario.

Sigue la documentación para proceder:

", - "how-to-create-tenant": "Cómo crear un Propietario" - }, - "step2": { - "title": "Configurar característica: Servidor emails", - "content": "

Los usuarios reciben emails de activación y reset de contraseña a través de un servidor SMTP.

Sigue la documentación para proceder:

", - "how-to-configure-mail-server": "Cómo configurar el Servidor eMail" - }, - "step3": { - "title": "Configurar característica: Proveedor SMS", - "content": "

Para distribuir mensajes SMS sobre alarmas, el administrador del sistema debe configurar proveedores SMS.

Sigue la documentación para proceder:

", - "how-to-configure-sms-provider": "Cómo configurar el proveedor SMS" - }, - "step4": { - "title": "Configurar característica: 2FA", - "content": "

Añadiendo autenticación 2FA (doble factor) a tu cuenta, incrementa la seguridad y previene accesos maliciosos.

Sigue la documentación para proceder:

", - "how-to-configure-2fa": "Cómo configurar 2FA" - }, - "step5": { - "title": "Configurar característica: OAuth 2", - "content": "

Puedes proveer a tus clientes con una solución 'Single Sign-On' y crear automáticamente administradores, clientes o subclientes usando una plataforma externa que permita OAuth 2.0.

Sigue la documentación para proceder:

", - "how-to-configure-oauth2": "Cómo configurar OAuth 2" - }, - "step6": { - "title": "Configurar característica: Slack", - "content": "

Los usuarios, seran capaces de recibir notificaciones en Slack, de los eventos que ocurran en el sistema, de acuerdo a las reglas de notificación que hayas configurado.

Sigue la documentación para proceder:

", - "how-to-configure-notifications": "Cómo configurar Slack" - } - }, - "tenant-admin": { - "step1": { - "title": "Crear dispositivo", - "content": "

Aprovisionar el dispositivo de forma manual, usando la interfaz. Sigue la documentación para proceder:

", - "how-to-create-device": "Cómo crear un dispositivo" - }, - "step2": { - "title": "Conectar dispositivo", - "content-before": "

Para conectar un dispositivo, necesitas obtener sus credenciales. Recomendamos usar las credenciales generadas por defecto.

  • Ir a la lista de dispositivos
  • Haz click en el dispositivo, para abrir los detalles
  • Presiona el botón\"Copiar access token\"

Usa comandos simples para publicar datos sobre HTTP:

", - "ubuntu": { - "install-curl": "Instalar cURL en Ubuntu:" - }, - "macos": { - "install-curl": "Instalar cURL en MacOS:" - }, - "windows": { - "install-curl": "En Windows 10 b17063, cURL está disponible por defecto." - }, - "replace-access-token": "Reemplazar $ACCESS_TOKEN con el token de tu dispositivo:", - "content-after": "

Puedes usar también otros protocolos, como MQTT, CoAP, etc.

Sigue la documentación para proceder:

", - "how-to-connect-device": "Cómo conectar un dispositivo" - }, - "step3": { - "title": "Crear un panel", - "content": "

Crear un panel para visualizar los datos de las entidades, como activos, dispositivos, etc..

Sigue la documentación para proceder:

", - "how-to-create-dashboard": "Cómo crear un panel" - }, - "step4": { - "title": "Configurar reglas de alarma", - "alarm-rules": "Reglas de alarma", - "content": "

Cuando la temperatura alcance 25°C, dispararemos una alarma. Sigue la documentación para proceder:

", - "how-to-configure-alarm-rules": "Cómo configurar reglas de alarma" - }, - "step5": { - "title": "Crear alarma", - "content-before": "

Para disparar la alarma, envía una nueva telemetría con un valor de 26°C o superior.

", - "replace-access-token": "Reemplazar $ACCESS_TOKEN con el token de tu dispositivo:", - "content-after": "

Sigue la documentación para proceder:

", - "how-to-create-alarm": "Cómo crear una alarma" - }, - "step6": { - "title": "Crear cliente y asignarle un panel", - "content": "

Creando un panel para un cliente, permitirá al cliente ver sus propios dispositivos. Los datos de otros clientes estarán ocultos.

Sigue la documentación para proceder:

", - "how-to-create-customer-and-assign-dashboard": "Cómo crear un cliente y asignarle un panel" - } - } + "step6": { + "title": "Crear cliente y compartir tablero", + "content": "

Al crear tableros para el usuario final, un usuario cliente solo podrá ver sus propios dispositivos y los datos de otro cliente estarán ocultos.

Sigue la documentación para saber cómo hacerlo:

" } - }, - "color": { - "color": "Color" - }, - "icon": { - "icon": "Icono", - "icons": "Iconos", - "select-icon": "Seleccionar icono", - "material-icons": "Iconos material-design", - "show-all": "Mostrar todos los iconos", - "search-icon": "Buscar icono", - "no-icons-found": "No se han encontrado iconos que coincidan con '{{iconSearch}}'" - }, - "phone-input": { - "phone-input-label": "Número de teléfono", - "phone-input-required": "Número de teléfono requerido", - "phone-input-validation": "El número es inválido o erróneo", - "phone-input-pattern": "Número inválido. Debe cumplir el formato E.164, ej. {{phoneNumber}}", - "phone-input-hint": "Número en el formato E.164, ej. {{phoneNumber}}" - }, - "custom": { - "widget-action": { - "action-cell-button": "Acción botón de celda", - "row-click": "En click de fila", - "polygon-click": "Clic en polígono", - "marker-click": "En click de marcador", - "circle-click": "En click de círculo", - "tooltip-tag-action": "Acción de la etiqueta Tooltip", - "node-selected": "Clic en el nodo seleccionado", - "element-click": "Clic en el elemento HTML", - "pie-slice-click": "Clic en la rebanada", - "row-double-click": "Doble clic en la fila", - "card-click": "En click de tarjeta" - } - }, - "paginator" : { - "items-per-page": "Ítems por página:", - "first-page-label": "Primera página", - "last-page-label": "Última página", - "next-page-label": "Siguiente", - "previous-page-label": "Anterior", - "items-per-page-separator": "de" - }, - "language": { - "language": "Idioma" + } + } + }, + "icon": { + "icon": "Icono", + "icons": "Iconos", + "select-icon": "Seleccionar icono", + "material-icons": "Iconos de Material", + "show-all": "Mostrar todos los iconos", + "search-icon": "Buscar icono", + "no-icons-found": "No se encontraron iconos para '{{iconSearch}}'" + }, + "phone-input": { + "phone-input-label": "Número de teléfono", + "phone-input-required": "Se requiere el número de teléfono", + "phone-input-validation": "El número de teléfono no es válido o no es posible", + "phone-input-pattern": "Número de teléfono inválido. Debe estar en formato E.164, ej. {{phoneNumber}}", + "phone-input-hint": "Número de teléfono en formato E.164, ej. {{phoneNumber}}" + }, + "custom": { + "widget-action": { + "action-cell-button": "Botón de acción en celda", + "row-click": "Al hacer clic en la fila", + "cell-click": "Al hacer clic en la celda", + "polygon-click": "Al hacer clic en el polígono", + "marker-click": "Al hacer clic en el marcador", + "circle-click": "Al hacer clic en el círculo", + "tooltip-tag-action": "Acción de etiqueta de tooltip", + "node-selected": "Al seleccionar el nodo", + "element-click": "Al hacer clic en el elemento HTML", + "pie-slice-click": "Al hacer clic en una porción", + "row-double-click": "Al hacer doble clic en la fila", + "cell-double-click": "Al hacer doble clic en la celda", + "card-click": "Al hacer clic en la tarjeta", + "click": "Al hacer clic" + } + }, + "paginator": { + "items-per-page": "Elementos por página:", + "first-page-label": "Primera página", + "last-page-label": "Última página", + "next-page-label": "Página siguiente", + "previous-page-label": "Página anterior", + "items-per-page-separator": "de" + }, + "language": { + "language": "Language", + "locales": { + "ar_AE": "العربية (الإمارات العربية المتحدة)", + "ca_ES": "català (Espanya)", + "cs_CZ": "čeština (Česko)", + "da_DK": "dansk (Danmark)", + "de_DE": "Deutsch (Deutschland)", + "el_GR": "Ελληνικά (Ελλάδα)", + "en_US": "English (United States)", + "es_ES": "español (España)", + "fa_IR": "فارسی (ایران)", + "fr_FR": "français (France)", + "it_IT": "italiano (Italia)", + "ja_JP": "日本語 (日本)", + "ka_GE": "ქართული (საქართველო)", + "ko_KR": "한국어 (대한민국)", + "lt_LT": "lietuvių (Lietuva)", + "lv_LV": "latviešu (Latvija)", + "nl_BE": "Nederlands (België)", + "pl_PL": "polski (Polska)", + "pt_BR": "português (Brasil)", + "ro_RO": "română (România)", + "sl_SI": "slovenščina (Slovenija)", + "tr_TR": "Türkçe (Türkiye)", + "uk_UA": "українська (Україна)", + "zh_CN": "中文 (中国)", + "zh_TW": "中文 (台灣)" } -} + } +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json index 70f348a769..07739906bb 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json @@ -1,2315 +1,9224 @@ { - "access": { - "access-forbidden": "Accès interdit", - "access-forbidden-text": "Vous n'avez pas accès à cet emplacement!
Essayez de vous connecter avec un autre utilisateur si vous souhaitez toujours accéder à cet emplacement.", - "refresh-token-expired": "La session a expiré", - "refresh-token-failed": "Impossible de rafraîchir la session", - "unauthorized": "Non autorisé", - "unauthorized-access": "Accès non autorisé", - "unauthorized-access-text": "Vous devez vous connecter pour avoir accès à cette ressource!", - "permission-denied": "Permission refusée", - "permission-denied-text": "Vous n'avez pas la permission de faire l'opération demandée!" - }, - "action": { - "activate": "Activer", - "add": "Ajouter", - "apply": "Appliquer", - "apply-changes": "Appliquer les modifications", - "assign": "Attribuer", - "back": "Retour", - "cancel": "Annuler", - "clear-search": "Effacer la recherche", - "close": "Fermer", - "continue": "Continuer", - "copy": "Copier", - "copy-reference": "Copier la référence", - "create": "Créer", - "decline-changes": "Refuser les modifications", - "delete": "Supprimer", - "discard-changes": "Annuler les modifications", - "drag": "Drag", - "edit": "Modifier", - "edit-mode": "Mode édition", - "enter-edit-mode": "Entrer en mode édition", - "export": "Exporter", - "import": "Importer", - "make-private": "Rendre privé", - "no": "Non", - "ok": "OK", - "paste": "coller", - "paste-reference": "Coller référence", - "refresh": "Rafraîchir", - "remove": "Supprimer", - "run": "Exécuter", - "save": "Enregistrer", - "saveAs": "Enregistrer sous", - "search": "Rechercher", - "share": "Partager", - "share-via": "Partager via {{provider}}", - "sign-in": "Connectez-vous!", - "suspend": "Suspendre", - "unassign": "Retirer", - "undo": "Annuler", - "update": "Mise à jour", - "view": "Afficher", - "yes": "Oui", - "select": "Sélectionner", - "download": "Télécharger", - "next-with-label": "Suivant: {{label}}", - "read-more": "En savoir plus", - "hide": "Masquer" - }, - "admin": { - "base-url": "URL de base", - "base-url-required": "L'URL de base est requis.", - "enable-tls": "Activer TLS", - "tls-version": "Version TLS", - "general": "Général", - "general-settings": "Paramètres généraux", - "mail-from": "Courriel de", - "mail-from-required": "Courriel de est requis.", - "outgoing-mail": "courrier sortant", - "outgoing-mail-settings": "Paramètres de courrier sortant", - "send-test-mail": "Envoyer un courriel de test", - "smtp-host": "Hôte SMTP", - "smtp-host-required": "L'hôte SMTP est requis.", - "smtp-port": "Port SMTP", - "smtp-port-invalid": "Cela ne ressemble pas à un port smtp valide.", - "smtp-port-required": "Vous devez fournir un port smtp.", - "smtp-protocol": "Protocole SMTP", - "system-settings": "Paramètres système", - "test-mail-sent": "Le courrier de test a été envoyé avec succès!", - "timeout-invalid": "Cela ne ressemble pas à un délai d'expiration valide.", - "timeout-msec": "Délai (msec)", - "timeout-required": "Le délai est requis.", - "security-settings": "Les paramètres de sécurité", - "password-policy": "Politique de mot de passe", - "minimum-password-length": "Longueur minimale du mot de passe", - "minimum-password-length-required": "La longueur minimale du mot de passe est requise", - "minimum-password-length-range": "La longueur minimale du mot de passe doit être comprise entre 5 et 50.", - "minimum-uppercase-letters": "Nombre minimum de lettres majuscules", - "minimum-uppercase-letters-range": "Le nombre minimum de lettres majuscules ne peut pas être négatif", - "minimum-lowercase-letters": "Nombre minimum de lettres minuscules", - "minimum-lowercase-letters-range": "Le nombre minimum de lettres minuscules ne peut pas être négatif", - "minimum-digits": "Nombre minimum de chiffres", - "minimum-digits-range": "Le nombre minimum de chiffres ne peut pas être négatif", - "minimum-special-characters": "Nombre minimum de caractères spéciaux", - "minimum-special-characters-range": "Le nombre minimum de caractères spéciaux ne peut pas être négatif", - "password-expiration-period-days": "Délai d'expiration du mot de passe en jours", - "password-expiration-period-days-range": "La période d'expiration du mot de passe en jours ne peut pas être négative", - "password-reuse-frequency-days": "Fréquence de réutilisation du mot de passe en jours", - "password-reuse-frequency-days-range": "La fréquence de réutilisation du mot de passe en jours ne peut être négative", - "general-policy": "Politique générale", - "max-failed-login-attempts": "Nombre maximal de tentatives de connexion infructueuses avant que le compte ne soit verrouillé", - "minimum-max-failed-login-attempts-range": "Le nombre maximal de tentatives de connexion ayant échoué ne peut pas être négatif", - "user-lockout-notification-email": "En cas de verrouillage du compte d'utilisateur, envoyez une notification par courrier électronique.", - "prohibit-different-url": "Interdire d'utiliser le nom d'hôte à partir des en-têtes de requête client", - "prohibit-different-url-hint": "Ce paramètre doit être activé pour les environnements de production. Peut causer des problèmes de sécurité lorsqu'il est désactivé.", - "enable-proxy": "Activer proxy", - "proxy-host": "Hôte proxy", - "proxy-host-required": "L'hôte proxy est requis.", - "proxy-port": "Port du proxy", - "proxy-port-required": "Port du proxy est requis.", - "proxy-port-range": "Le port proxy doit être compris entre 1 et 65535.", - "proxy-user": "Utilisateur proxy", - "proxy-password": "Mot de passe proxy", - "change-password": "Changer mot de passe", - "sms-provider": "Fournisseur SMS", - "sms-provider-settings": "Paramètres du fournisseur de SMS", - "sms-provider-type": "Type de fournisseur de SMS", - "sms-provider-type-required": "Le type de fournisseur de SMS est requis.", - "aws-access-key-id": "ID de clé d'accès AWS", - "aws-access-key-id-required": "L'ID de clé d'accès AWS est requis", - "aws-secret-access-key": "Clé d'accès secrète AWS", - "aws-secret-access-key-required": "La clé d'accès secrète AWS est requise", - "aws-region": "Région AWS", - "aws-region-required": "La région AWS est obligatoire", - "number-from": "Numéro de téléphone de", - "number-from-required": "Numéro de téléphone de est requis", - "number-to": "Numéro de téléphone à", - "number-to-required": "Numéro de téléphone à est requis.", - "phone-number-hint": "Numéro de téléphone au format E.164, ex. +19995550123", - "phone-number-hint-twilio": "Numéro de téléphone au format E.164/SID du numéro de téléphone/SID du service de messagerie, ex. +19995550123/PNXXX/MGXXX", - "phone-number-pattern": "Numéro de téléphone invalide. Doit être au format E.164, ex. +19995550123.", - "phone-number-pattern-twilio": "Numéro de téléphone invalide. Doit être au format E.164/SID du numéro de téléphone/SID du service de messagerie, ex. +19995550123/PNXXX/MGXXX.", - "sms-message": "Message SMS", - "sms-message-required": "Message SMS requis.", - "sms-message-max-length": "Le message SMS ne peut pas contenir plus de 1600 caractères", - "twilio-account-sid": "SID du compte Twilio", - "twilio-account-sid-required": "SID du compte Twilio requis", - "twilio-account-token": "Jeton du compte Twilio", - "twilio-account-token-required": "Jeton du compte Twilio est requis", - "send-test-sms": "Envoyer SMS test", - "test-sms-sent": "Le SMS de test a été envoyé avec succès !", - "allow-whitespace": "Autoriser les espaces", - "domain-name": "Nom de domaine", - "domain-name-unique": "Le nom de domaine et le protocole doivent être uniques.", - "domain-name-max-length": "Le nom de domaine doit être inférieur à 256", - "error-verification-url": "Un nom de domaine ne doit pas contenir les symboles '/' et ':'. Exemple : Thingsboard.io", - "oauth2": { - "access-token-uri": "URI du jeton d'accès", - "access-token-uri-required": "URI du jeton d'accès requis.", - "activate-user": "Activer l'utilisateur", - "add-domain": "Ajouter un domaine", - "delete-domain": "Supprimer un domaine", - "add-provider": "Ajouter un fournisseur", - "delete-provider": "Supprimer un fournisseur", - "allow-user-creation": "Autoriser la création d'utilisateurs", - "always-fullscreen": "Toujours plein écran", - "authorization-uri": "URI d'autorisation", - "authorization-uri-required": "L'URI d'autorisation est obligatoire.", - "client-authentication-method": "Méthode d'authentification client", - "client-id": "Identifiant du client", - "client-id-required": "L'identifiant client est requis.", - "client-id-max-length": "L'ID client doit être inférieur à 256", - "client-secret": "Secret client", - "client-secret-required": "Secret client requis.", - "client-secret-max-length": "Le secret client doit être inférieur à 2049", - "custom-setting": "Paramètres personnalisés", - "customer-name-pattern": "Modèle de nom de client", - "customer-name-pattern-max-length": "Le modèle de nom de client doit être inférieur à 256", - "default-dashboard-name": "Nom du tableau de bord par défaut", - "default-dashboard-name-max-length": "Le nom du tableau de bord par défaut doit être inférieur à 256", - "delete-domain-text": "Attention, après la confirmation un domaine et toutes les données du fournisseur seront indisponibles.", - "delete-domain-title": "Voulez-vous vraiment supprimer les paramètres du domaine '{{domainName}}'?", - "delete-registration-text": "Attention, après la confirmation, les données d'un fournisseur seront indisponibles.", - "delete-registration-title": "Êtes-vous sûr de vouloir supprimer le fournisseur'{{name}}'?", - "email-attribute-key": "Clé d'attribut de courriel", - "email-attribute-key-required": "La clé d'attribut de courriel est requise.", - "email-attribute-key-max-length": "La clé d'attribut de courriel doit être inférieure à 32", - "first-name-attribute-key": "Clé d'attribut du prénom", - "general": "Général", - "jwk-set-uri": "URI de la clé Web JSON", - "last-name-attribute-key": "Clé d'attribut du nom de famille", - "login-button-icon": "Icône du bouton de connexion", - "login-button-label": "Libellé du fournisseur", - "login-button-label-placeholder": "Connectez-vous avec $(Provider label)", - "login-button-label-required": "L'étiquette est obligatoire.", - "login-provider": "Fournisseur de connexion", - "new-domain": "Nouveau domaine", - "password-max-length": "Le mot de passe doit être inférieur à 256", - "redirect-uri-template": "Modèle d'URI de redirection", - "copy-redirect-uri": "Copier l'URI de redirection", - "registration-id": "ID d'enregistrement", - "registration-id-required": "L'identifiant d'enregistrement est requis.", - "registration-id-unique": "L'ID d'enregistrement doit être unique pour le système.", - "scope": "Portée", - "scope-required": "La portée est requise.", - "tenant-name-pattern": "Modèle de nom du Tenant ", - "tenant-name-pattern-required": "Un modèle de nom de Tenant est requis.", - "tenant-name-pattern-max-length": "Le modèle de nom de Tenant doit être inférieur à 256", - "tenant-name-strategy": "Stratégie de nom de Tenant", - "type": "Type de Mapper", - "uri-pattern-error": "Format d'URI invalide.", - "url-pattern": "Format d'URL non valide.", - "url-required": "L'URL est requis.", - "url-max-length": "L'URL doit être inférieure à 256", - "user-info-uri": "URI des informations utilisateur", - "user-info-uri-required": "L'URI des informations utilisateur est requise.", - "username-max-length": "Le nom d'utilisateur doit être inférieur à 256", - "user-name-attribute-name": "Clé d'attribut de nom d'utilisateur", - "user-name-attribute-name-required": "La clé d'attribut du nom d'utilisateur est requise", - "protocol": "Protocole", - "enable": "Activer les paramètres OAuth2", - "domains": "Domainse", - "mobile-apps": "Applications mobiles", - "no-mobile-apps": "Aucune application configurée", - "mobile-package": "Package d'application", - "mobile-package-placeholder": "Ex. : mon.exemple.app", - "mobile-package-hint": "Pour Android : votre propre ID d'application unique. Pour iOS : identifiant du groupe de produits.", - "mobile-package-unique": "Le package d'application doit être unique.", - "mobile-app-secret": "Secret d'application", - "invalid-mobile-app-secret": "Le secret d'application ne doit contenir que des caractères alphanumériques et doit comporter entre 16 et 2 048 caractères.", - "copy-mobile-app-secret": "Copier le secret de l'application", - "add-mobile-app": "Ajouter une application", - "delete-mobile-app": "Supprimer les informations sur l'application", - "providers": "Fournisseurs", - "all-platforms": "Toutes les plateformes", - "allowed-platforms": "Plates-formes autorisées" - } + "access": { + "unauthorized": "Non autorisé", + "unauthorized-access": "Accès non autorisé", + "unauthorized-access-text": "Vous devez vous connecter pour accéder à cette ressource !", + "access-forbidden": "Accès interdit", + "access-forbidden-text": "Vous n'avez pas les droits d'accès à cet emplacement !
Essayez de vous connecter avec un autre utilisateur si vous souhaitez toujours y accéder.", + "refresh-token-expired": "La session a expiré", + "refresh-token-failed": "Impossible de renouveler la session", + "permission-denied": "Permission refusée", + "permission-denied-text": "Vous n'avez pas la permission d'effectuer cette opération !" + }, + "account": { + "account": "Compte", + "notification-settings": "Paramètres de notification" + }, + "action": { + "activate": "Activer", + "suspend": "Suspendre", + "save": "Enregistrer", + "saveAs": "Enregistrer sous", + "move": "Déplacer", + "cancel": "Annuler", + "ok": "OK", + "delete": "Supprimer", + "add": "Ajouter", + "yes": "Oui", + "no": "Non", + "update": "Mettre à jour", + "remove": "Retirer", + "search": "Rechercher", + "clear-search": "Effacer la recherche", + "assign": "Assigner", + "unassign": "Désassigner", + "share": "Partager", + "make-private": "Rendre privé", + "apply": "Appliquer", + "apply-changes": "Appliquer les modifications", + "edit-mode": "Mode édition", + "enter-edit-mode": "Entrer en mode édition", + "decline-changes": "Refuser les modifications", + "decline": "Refuser", + "close": "Fermer", + "back": "Retour", + "run": "Exécuter", + "sign-in": "Connectez-vous !", + "edit": "Modifier", + "view": "Voir", + "create": "Créer", + "drag": "Glisser", + "refresh": "Rafraîchir", + "undo": "Annuler", + "copy": "Copier", + "paste": "Coller", + "copy-reference": "Copier la référence", + "paste-reference": "Coller la référence", + "import": "Importer", + "export": "Exporter", + "share-via": "Partager via {{provider}}", + "select": "Sélectionner", + "continue": "Continuer", + "discard-changes": "Ignorer les modifications", + "download": "Télécharger", + "next": "Suivant", + "next-with-label": "Suivant : {{label}}", + "read-more": "Lire plus", + "hide": "Masquer", + "test": "Tester", + "done": "Terminé", + "print": "Imprimer", + "restore": "Restaurer", + "confirm": "Confirmer", + "more": "Plus", + "less": "Moins", + "skip": "Passer", + "send": "Envoyer", + "reset": "Réinitialiser", + "show-more": "Afficher plus", + "dont-show-again": "Ne plus afficher", + "see-documentation": "Voir la documentation", + "clear": "Effacer", + "upload": "Téléverser", + "delete-anyway": "Supprimer quand même", + "delete-selected": "Supprimer la sélection", + "set": "Définir" + }, + "aggregation": { + "aggregation": "Agrégation", + "function": "Fonction d'agrégation des données", + "limit": "Valeurs max", + "group-interval": "Intervalle de regroupement", + "min": "Min", + "max": "Max", + "avg": "Moyenne", + "sum": "Somme", + "count": "Compte", + "none": "Aucun" + }, + "admin": { + "settings": "Paramètres", + "general": "Général", + "general-settings": "Paramètres généraux", + "home-settings": "Paramètres d'accueil", + "home": "Accueil", + "outgoing-mail": "Serveur de messagerie", + "outgoing-mail-settings": "Paramètres du serveur de messagerie sortant", + "system-settings": "Paramètres système", + "test-mail-sent": "E-mail de test envoyé avec succès !", + "base-url": "URL de base", + "base-url-required": "L'URL de base est requise.", + "prohibit-different-url": "Interdire l'utilisation du nom d'hôte des en-têtes de requête client", + "prohibit-different-url-hint": "Ce paramètre doit être activé pour les environnements de production. Peut entraîner des problèmes de sécurité lorsqu'il est désactivé", + "device-connectivity": { + "device-connectivity": "Connectivité des appareils", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPs", + "mqtt": "MQTT", + "mqtts": "MQTTs", + "coap": "COAP", + "coaps": "COAPs", + "hint": "Si les champs hôte ou port sont vides, la valeur du protocole par défaut sera utilisée.", + "host": "Hôte", + "port": "Port", + "port-pattern": "Le port doit être un entier positif.", + "port-range": "Le port doit être compris entre 1 et 65535." }, - "aggregation": { - "aggregation": "agrégation", - "avg": "Moyenne", - "count": "Compte", - "function": "Fonction d'agrégation de données", - "group-interval": "Intervalle de regroupement", - "limit": "Valeurs maximales", - "max": "Max", - "min": "Min", - "none": "Aucune", - "sum": "Somme" - }, - "alarm": { - "ack-time": "Heure d'acquittement", - "acknowledge": "Acquitter", - "aknowledge-alarm-text": "Êtes-vous sûr de vouloir reconnaître l'alarme?", - "aknowledge-alarm-title": "Reconnaître l'alarme", - "aknowledge-alarms-text": "Êtes-vous sûr de vouloir acquitter { count, plural, =1 {1 alarme} other {# alarmes} }?", - "aknowledge-alarms-title": "Acquitter { count, plural, =1 {1 alarme} other {# alarmes} }", - "alarm": "Alarme", - "alarm-details": "Détails de l'alarme", - "alarm-required": "Une alarme est requise", - "alarm-status": "État d'alarme", - "alarm-status-filter": "Filtre d'état d'alarme", - "alarms": "Alarmes", - "clear": "Effacer", - "clear-alarm-text": "Êtes-vous sûr de vouloir effacer l'alarme?", - "clear-alarm-title": "Effacer l'alarme", - "clear-alarms-text": "Êtes-vous sûr de vouloir effacer {count, plural, =1 {1 alarme} other {# alarmes} }?", - "clear-alarms-title": "Effacer {count, plural, =1 {1 alarme} other {# alarmes} }", - "clear-time": "Heure d'éffacement", - "created-time": "Heure de création", - "details": "Détails", - "display-status": { - "ACTIVE_ACK": "Active acquittée", - "ACTIVE_UNACK": "Active non acquittée", - "CLEARED_ACK": "effacée acquittée", - "CLEARED_UNACK": "effacée non acquittée" + "mail-from": "Adresse expéditrice", + "mail-from-required": "L'adresse expéditrice est requise.", + "smtp-protocol": "Protocole SMTP", + "smtp-host": "Hôte SMTP", + "smtp-host-required": "L'hôte SMTP est requis.", + "smtp-port": "Port SMTP", + "smtp-port-required": "Vous devez fournir un port SMTP.", + "smtp-port-invalid": "Ce port SMTP semble invalide.", + "timeout-msec": "Délai d'attente (ms)", + "timeout-required": "Le délai d'attente est requis.", + "timeout-invalid": "Ce délai d'attente semble invalide.", + "enable-tls": "Activer TLS", + "tls-version": "Version TLS", + "enable-proxy": "Activer le proxy", + "proxy-host": "Hôte proxy", + "proxy-host-required": "L'hôte proxy est requis.", + "proxy-port": "Port proxy", + "proxy-port-required": "Le port proxy est requis.", + "proxy-port-range": "Le port proxy doit être compris entre 1 et 65535.", + "proxy-user": "Utilisateur proxy", + "proxy-password": "Mot de passe proxy", + "change-password": "Changer le mot de passe", + "send-test-mail": "Envoyer un e-mail de test", + "sms-provider": "Fournisseur SMS", + "sms-provider-settings": "Paramètres du fournisseur SMS", + "sms-provider-type": "Type de fournisseur SMS", + "sms-provider-type-required": "Le type de fournisseur SMS est requis.", + "sms-provider-type-aws-sns": "Amazon SNS", + "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "ID de clé d'accès AWS", + "aws-access-key-id-required": "L'ID de clé d'accès AWS est requis", + "aws-secret-access-key": "Clé d'accès secrète AWS", + "aws-secret-access-key-required": "La clé d'accès secrète AWS est requise", + "aws-region": "Région AWS", + "aws-region-required": "La région AWS est requise", + "number-from": "Numéro de téléphone expéditeur", + "number-from-required": "Le numéro de téléphone expéditeur est requis.", + "number-to": "Numéro de téléphone destinataire", + "number-to-required": "Le numéro de téléphone destinataire est requis.", + "phone-number-hint": "Numéro de téléphone au format E.164, ex. +19995550123", + "phone-number-hint-twilio": "Numéro de téléphone au format E.164 / SID du numéro / SID du service de messagerie, ex. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Numéro de téléphone invalide. Doit être au format E.164, ex. +19995550123.", + "phone-number-pattern-twilio": "Numéro de téléphone invalide. Doit être au format E.164 / SID du numéro / SID du service de messagerie, ex. +19995550123/PNXXX/MGXXX.", + "sms-message": "Message SMS", + "sms-message-required": "Le message SMS est requis.", + "sms-message-max-length": "Le message SMS ne peut pas dépasser 1600 caractères", + "twilio-account-sid": "SID du compte Twilio", + "twilio-account-sid-required": "Le SID du compte Twilio est requis", + "twilio-account-token": "Jeton du compte Twilio", + "twilio-account-token-required": "Le jeton du compte Twilio est requis", + "send-test-sms": "Envoyer un SMS de test", + "test-sms-sent": "Le SMS de test a été envoyé avec succès !", + "security-settings": "Paramètres de sécurité", + "password-policy": "Politique de mot de passe", + "minimum-password-length": "Longueur minimale du mot de passe", + "minimum-password-length-required": "La longueur minimale du mot de passe est requise", + "minimum-password-length-range": "La longueur minimale du mot de passe doit être comprise entre 6 et 50", + "maximum-password-length": "Longueur maximale du mot de passe", + "maximum-password-length-min": "La longueur maximale du mot de passe doit être d'au moins 6", + "maximum-password-length-less-min": "La longueur maximale du mot de passe doit être supérieure à la longueur minimale", + "minimum-uppercase-letters": "Nombre minimum de lettres majuscules", + "minimum-uppercase-letters-range": "Le nombre minimum de lettres majuscules ne peut pas être négatif", + "minimum-lowercase-letters": "Nombre minimum de lettres minuscules", + "minimum-lowercase-letters-range": "Le nombre minimum de lettres minuscules ne peut pas être négatif", + "minimum-digits": "Nombre minimum de chiffres", + "minimum-digits-range": "Le nombre minimum de chiffres ne peut pas être négatif", + "minimum-special-characters": "Nombre minimum de caractères spéciaux", + "minimum-special-characters-range": "Le nombre minimum de caractères spéciaux ne peut pas être négatif", + "password-expiration-period-days": "Période d'expiration du mot de passe (en jours)", + "password-expiration-period-days-range": "La période d'expiration ne peut pas être négative", + "password-reuse-frequency-days": "Fréquence de réutilisation du mot de passe (en jours)", + "password-reuse-frequency-days-range": "La fréquence de réutilisation ne peut pas être négative", + "allow-whitespace": "Autoriser les espaces", + "force-reset-password-if-no-valid": "Forcer la réinitialisation du mot de passe si invalide", + "force-reset-password-if-no-valid-hint": "Attention : cette option obligera les utilisateurs avec un mot de passe invalide à le réinitialiser par e-mail.", + "general-policy": "Politique générale", + "max-failed-login-attempts": "Nombre maximal de tentatives de connexion échouées avant verrouillage du compte", + "minimum-max-failed-login-attempts-range": "Le nombre maximal de tentatives échouées ne peut pas être négatif", + "user-lockout-notification-email": "En cas de verrouillage du compte utilisateur, envoyer une notification par e-mail", + "user-activation-token-ttl": "Durée de validité du lien d'activation (en heures)", + "user-activation-token-ttl-range": "La durée doit être comprise entre 1 et 24 heures", + "password-reset-token-ttl": "Durée de validité du lien de réinitialisation (en heures)", + "password-reset-token-ttl-range": "La durée doit être comprise entre 1 et 24 heures", + "mobile-secret-key-length": "Longueur de la clé secrète mobile", + "mobile-secret-key-length-range": "La longueur de la clé doit être positive", + "domain-name": "Nom de domaine", + "domain-name-unique": "Le nom de domaine et le protocole doivent être uniques.", + "domain-name-max-length": "Le nom de domaine doit contenir moins de 256 caractères", + "error-verification-url": "Un nom de domaine ne doit pas contenir les caractères '/' et ':'. Exemple : thingsboard.io", + "connection-settings": "Paramètres de connexion", + "oauth2": { + "access-token-uri": "URI du jeton d'accès", + "access-token-uri-required": "L'URI du jeton d'accès est requis.", + "activate-user": "Activer l'utilisateur", + "add-domain": "Ajouter un domaine", + "delete-domain": "Supprimer le domaine", + "add-provider": "Ajouter un fournisseur", + "delete-provider": "Supprimer le fournisseur", + "allow-user-creation": "Autoriser la création d'utilisateurs", + "always-fullscreen": "Toujours en plein écran", + "authorization-uri": "URI d'autorisation", + "authorization-uri-required": "L'URI d'autorisation est requis.", + "add-client": "Ajouter un client OAuth 2.0", + "client-details": "Détails du client OAuth 2.0", + "client": "Client OAuth 2.0", + "clients": "Clients OAuth 2.0", + "no-oauth2-clients": "Aucun client OAuth 2.0 trouvé", + "search-oauth2-clients": "Rechercher des clients OAuth 2.0", + "delete-client-title": "Êtes-vous sûr de vouloir supprimer le client OAuth 2.0 '{{clientName}}' ?", + "delete-client-text": "Attention, après confirmation, le client et toutes les données associées seront irrécupérables.", + "delete-mobile-app-title": "Êtes-vous sûr de vouloir supprimer l'application mobile '{{applicationName}}' ?", + "delete-mobile-app-text": "Attention, après confirmation, l'application mobile et toutes les données associées seront irrécupérables.", + "title": "Titre", + "client-title-required": "Le titre est requis", + "client-title-max-length": "Le titre doit contenir moins de 100 caractères", + "advanced-settings": "Paramètres avancés", + "domain-details": "Détails du domaine", + "no-domains": "Aucun domaine trouvé", + "search-domains": "Rechercher des domaines", + "mobile-app-details": "Détails de l'application mobile", + "add-mobile-app": "Ajouter une application mobile", + "no-mobile-apps": "Aucune application mobile trouvée", + "search-mobile-apps": "Rechercher des applications mobiles", + "send-token": "Envoyer le jeton", + "create-new": "Créer nouveau", + "client-authentication-method": "Méthode d'authentification du client", + "client-id": "ID du client", + "client-id-required": "L'ID du client est requis.", + "client-id-max-length": "L'ID du client doit contenir moins de 256 caractères", + "client-secret": "Secret du client", + "client-secret-required": "Le secret du client est requis.", + "client-secret-max-length": "Le secret du client doit contenir moins de 2049 caractères", + "custom-setting": "Paramètres personnalisés", + "customer-name-pattern": "Modèle de nom de client", + "customer-name-pattern-max-length": "Le modèle de nom de client doit contenir moins de 256 caractères", + "default-dashboard-name": "Nom du tableau de bord par défaut", + "default-dashboard-name-max-length": "Le nom du tableau de bord par défaut doit contenir moins de 256 caractères", + "delete-domain-text": "Attention, après confirmation, le domaine et toutes les données du fournisseur seront indisponibles.", + "delete-domain-title": "Êtes-vous sûr de vouloir supprimer le domaine '{{domainName}}' ?", + "delete-registration-text": "Attention, après confirmation, les données du fournisseur seront indisponibles.", + "delete-registration-title": "Êtes-vous sûr de vouloir supprimer le fournisseur '{{name}}' ?", + "email-attribute-key": "Clé de l'attribut e-mail", + "email-attribute-key-required": "La clé de l'attribut e-mail est requise.", + "email-attribute-key-max-length": "La clé de l'attribut e-mail doit contenir moins de 32 caractères", + "first-name-attribute-key": "Clé de l'attribut prénom", + "first-name-attribute-key-max-length": "La clé de l'attribut prénom doit contenir moins de 32 caractères", + "general": "Général", + "jwk-set-uri": "URI de la clé Web JSON", + "last-name-attribute-key": "Clé de l'attribut nom", + "last-name-attribute-key-max-length": "La clé de l'attribut nom doit contenir moins de 32 caractères", + "login-button-icon": "Icône du bouton de connexion", + "login-button-label": "Libellé du fournisseur", + "login-button-label-placeholder": "Connexion avec $(Provider label)", + "login-button-label-required": "Le libellé est requis.", + "login-provider": "Fournisseur de connexion", + "mapper": "Mappeur", + "new-domain": "Nouveau domaine", + "oauth2": "OAuth 2.0", + "password-max-length": "Le mot de passe doit contenir moins de 256 caractères", + "redirect-uri-template": "Modèle d'URI de redirection", + "copy-redirect-uri": "Copier l'URI de redirection", + "registration-id": "ID d'enregistrement", + "registration-id-required": "L'ID d'enregistrement est requis.", + "registration-id-unique": "L'ID d'enregistrement doit être unique dans le système.", + "scope": "Portée", + "scope-required": "La portée est requise.", + "tenant-name-pattern": "Modèle de nom du locataire", + "tenant-name-pattern-required": "Le modèle de nom du locataire est requis.", + "tenant-name-pattern-max-length": "Le modèle de nom du locataire doit contenir moins de 256 caractères", + "tenant-name-strategy": "Stratégie de nom de locataire", + "type": "Type de mappeur", + "uri-pattern-error": "Format d'URI invalide.", + "url": "URL", + "url-pattern": "Format d'URL invalide.", + "url-required": "L'URL est requise.", + "url-max-length": "L'URL doit contenir moins de 256 caractères", + "user-info-uri": "URI des informations utilisateur", + "user-info-uri-required": "L'URI des informations utilisateur est requise.", + "username-max-length": "Le nom d'utilisateur doit contenir moins de 256 caractères", + "user-name-attribute-name": "Clé de l'attribut nom d'utilisateur", + "user-name-attribute-name-required": "La clé de l'attribut nom d'utilisateur est requise", + "protocol": "Protocole", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "Activer les paramètres OAuth 2.0", + "disable": "Désactiver les paramètres OAuth 2.0", + "edge": "Propager vers Edge", + "edge-enable": "Activer la propagation vers Edge", + "edge-disable": "Désactiver la propagation vers Edge", + "domains": "Domaines", + "mobile-apps": "Applications mobiles", + "mobile-package": "Package de l'application", + "mobile-package-placeholder": "Ex. : my.example.app", + "mobile-package-hint": "Pour Android : votre identifiant d'application unique. Pour iOS : identifiant du bundle du produit.", + "mobile-package-unique": "Le package de l'application doit être unique.", + "mobile-package-required": "Le package de l'application est requis.", + "mobile-package-max-length": "Le package de l'application doit contenir moins de 256 caractères", + "mobile-package-spaces": "Le package de l'application ne doit pas contenir d'espaces", + "mobile-app-secret": "Secret de l'application", + "mobile-app-secret-hint": "Chaîne encodée en base64 représentant au moins 512 bits de données.", + "mobile-app-secret-required": "Le secret de l'application est requis.", + "mobile-app-secret-min-length": "Le secret de l'application doit contenir au moins 512 bits de données.", + "mobile-app-secret-base64": "Le secret de l'application doit être au format base64.", + "invalid-mobile-app-secret": "Le secret de l'application doit contenir uniquement des caractères alphanumériques et mesurer entre 16 et 2048 caractères.", + "copy-mobile-app-secret": "Copier le secret de l'application", + "delete-mobile-app": "Supprimer les informations de l'application", + "providers": "Fournisseurs", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Toutes les plateformes", + "smtp-provider": "Fournisseur SMTP", + "allowed-platforms": "Plateformes autorisées", + "authentication": "Authentification", + "basic": "Basique", + "provider": "Fournisseur", + "redirect-url": "URI de redirection", + "domain-name": "Nom de domaine", + "domain-name-required": "Le nom de domaine est requis", + "redirect-url-template": "Modèle d'URI de redirection", + "microsoft-tenant-id": "ID du répertoire (locataire)", + "microsoft-tenant-id-required": "L'ID du répertoire (locataire) est requis", + "token-uri": "URI du jeton", + "token-uri-required": "L'URI du jeton est requis", + "redirect-uri": "URI de redirection", + "google-provider": "Google", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "Personnalisé", + "generate-access-token": "Générer le jeton d'accès", + "update-access-token": "Mettre à jour le jeton d'accès", + "access-token-status": "Statut du jeton d'accès :", + "token-status-generated": "généré", + "token-status-not-generated": "non généré" + }, + "smpp-provider": { + "smpp-version": "Version SMPP", + "smpp-host": "Hôte SMPP", + "smpp-host-required": "L'hôte SMPP est requis", + "smpp-port": "Port SMPP", + "smpp-port-required": "Le port SMPP est requis", + "system-id": "ID système", + "system-id-required": "L'ID système est requis", + "password": "Mot de passe", + "password-required": "Le mot de passe est requis", + "type-settings": "Paramètres de type", + "source-settings": "Paramètres source", + "destination-settings": "Paramètres de destination", + "additional-settings": "Paramètres supplémentaires", + "system-type": "Type de système", + "bind-type": "Type de liaison", + "service-type": "Type de service", + "source-address": "Adresse source", + "source-ton": "TON source", + "source-npi": "NPI source", + "destination-ton": "TON de destination (Type de numéro)", + "destination-npi": "NPI de destination (Identification du plan de numérotation)", + "address-range": "Plage d'adresses", + "coding-scheme": "Schéma de codage", + "bind-type-tx": "Émetteur", + "bind-type-rx": "Récepteur", + "bind-type-trx": "Émetteur-récepteur", + "ton-unknown": "Inconnu", + "ton-international": "International", + "ton-national": "National", + "ton-network-specific": "Spécifique au réseau", + "ton-subscriber-number": "Numéro d'abonné", + "ton-alphanumeric": "Alphanumérique", + "ton-abbreviated": "Abrégé", + "npi-unknown": "0 - Inconnu", + "npi-isdn": "1 - Plan de numérotation ISDN/téléphonique (E163/E164)", + "npi-data-numbering-plan": "3 - Plan de numérotation des données (X.121)", + "npi-telex-numbering-plan": "4 - Plan de numérotation Télex (F.69)", + "npi-land-mobile": "6 - Réseau mobile terrestre (E.212)", + "npi-national-numbering-plan": "8 - Plan de numérotation national", + "npi-private-numbering-plan": "9 - Plan de numérotation privé", + "npi-ermes-numbering-plan": "10 - Plan de numérotation ERMES (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internet (IP)", + "npi-wap-client-id": "18 - Identifiant client WAP (défini par le WAP Forum)", + "scheme-smsc": "0 - Alphabet par défaut du SMSC (ASCII pour code court/long, GSM pour numéro vert)", + "scheme-ia5": "1 - IA5 (ASCII pour code court/long, Latin 9 pour numéro vert (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Octet non spécifié (binaire 8 bits)", + "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Octet non spécifié (binaire 8 bits)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Cyrillique (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latin/Hébreu (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Encodage pictogramme", + "scheme-music-codes": "10 - Codes musicaux (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Kanji JIS étendu (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Jeu de caractères graphiques coréens (KS C 5601/KS X 1001)" + }, + "queue-select-name": "Sélectionner le nom de la file", + "queue-name": "Nom", + "queue-name-required": "Le nom de la file est requis !", + "queues": "Files", + "queue-partitions": "Partitions", + "queue-submit-strategy": "Stratégie de soumission", + "queue-processing-strategy": "Stratégie de traitement", + "queue-configuration": "Configuration de la file", + "repository-settings": "Paramètres du dépôt", + "repository": "Dépôt", + "repository-url": "URL du dépôt", + "repository-url-required": "L'URL du dépôt est requise.", + "default-branch": "Nom de la branche par défaut", + "repository-read-only": "Lecture seule", + "show-merge-commits": "Afficher les commits de fusion", + "authentication-settings": "Paramètres d'authentification", + "auth-method": "Méthode d'authentification", + "auth-method-username-password": "Mot de passe / jeton d'accès", + "auth-method-username-password-hint": "Les utilisateurs GitHub doivent utiliser des jetons avec des permissions d'écriture pour le dépôt.", + "auth-method-private-key": "Clé privée", + "password-access-token": "Mot de passe / jeton d'accès", + "change-password-access-token": "Modifier le mot de passe / jeton d'accès", + "private-key": "Clé privée", + "drop-private-key-file-or": "Glissez-déposez un fichier de clé privée ou", + "passphrase": "Phrase secrète", + "enter-passphrase": "Entrer la phrase secrète", + "change-passphrase": "Changer la phrase secrète", + "check-access": "Vérifier l'accès", + "check-repository-access-success": "Accès au dépôt vérifié avec succès !", + "delete-repository-settings-title": "Êtes-vous sûr de vouloir supprimer les paramètres du dépôt ?", + "delete-repository-settings-text": "Attention, après confirmation les paramètres du dépôt seront supprimés et la fonctionnalité de contrôle de version ne sera plus disponible.", + "auto-commit-settings": "Paramètres de commit automatique", + "auto-commit": "Commit automatique", + "auto-commit-entities": "Entités de commit automatique", + "no-auto-commit-entities-prompt": "Aucune entité configurée pour le commit automatique", + "delete-auto-commit-settings-title": "Êtes-vous sûr de vouloir supprimer les paramètres de commit automatique ?", + "delete-auto-commit-settings-text": "Attention, après confirmation, les paramètres de commit automatique seront supprimés et la fonctionnalité sera désactivée pour toutes les entités.", + "mobile-app": { + "mobile-app": "Application mobile", + "mobile-app-qr-code-widget-settings": "Paramètres du widget QR code de l'application mobile", + "applications": "Applications", + "default": "Par défaut", + "custom": "Personnalisé", + "android": "Android", + "ios": "iOS", + "appearance": "Apparence", + "appearance-on-home-page": "Apparence sur la page d'accueil", + "enabled": "Activé", + "disabled": "Désactivé", + "badges": "Badges", + "label": "Étiquette", + "label-required": "L'étiquette est requise", + "label-max-length": "L'étiquette doit contenir au maximum 50 caractères", + "right": "Droite", + "left": "Gauche", + "set": "Définir", + "preview": "Aperçu", + "connect-mobile-app": "Connecter l'application mobile", + "use-system-settings": "Utiliser les paramètres système" + }, + "2fa": { + "2fa": "Authentification à deux facteurs", + "available-providers": "Fournisseurs disponibles", + "issuer-name": "Nom de l'émetteur", + "issuer-name-required": "Le nom de l'émetteur est requis.", + "max-verification-failures-before-user-lockout": "Nombre maximal d'échecs de vérification avant verrouillage de l'utilisateur", + "max-verification-failures-before-user-lockout-pattern": "Le nombre maximal d'échecs doit être un entier positif.", + "number-of-checking-attempts": "Nombre de tentatives de vérification", + "number-of-checking-attempts-pattern": "Le nombre de tentatives doit être un entier positif.", + "number-of-checking-attempts-required": "Le nombre de tentatives de vérification est requis.", + "number-of-codes": "Nombre de codes", + "number-of-codes-pattern": "Le nombre de codes doit être un entier positif.", + "number-of-codes-required": "Le nombre de codes est requis.", + "provider": "Fournisseur", + "retry-verification-code-period": "Délai de réessai du code de vérification (sec)", + "retry-verification-code-period-pattern": "Le délai minimal est de 5 secondes", + "retry-verification-code-period-required": "Le délai de réessai est requis.", + "total-allowed-time-for-verification": "Durée totale autorisée pour la vérification (sec)", + "total-allowed-time-for-verification-pattern": "La durée minimale autorisée est de 60 secondes", + "total-allowed-time-for-verification-required": "La durée totale autorisée est requise.", + "use-system-two-factor-auth-settings": "Utiliser les paramètres système d'authentification à deux facteurs", + "verification-code-check-rate-limit": "Limite de fréquence de vérification du code", + "verification-code-lifetime": "Durée de vie du code de vérification (sec)", + "verification-code-lifetime-pattern": "La durée de vie du code doit être un entier positif.", + "verification-code-lifetime-required": "La durée de vie du code est requise.", + "verification-message-template": "Modèle de message de vérification", + "verification-limitations": "Limitations de la vérification", + "verification-message-template-pattern": "Le message de vérification doit contenir le modèle : ${code}", + "verification-message-template-required": "Le modèle de message de vérification est requis.", + "within-time": "Délai (sec)", + "within-time-pattern": "Le délai doit être un entier positif.", + "within-time-required": "Le délai est requis." + }, + "jwt": { + "security-settings": "Paramètres de sécurité JWT", + "issuer-name": "Nom de l'émetteur", + "issuer-name-required": "Le nom de l'émetteur est requis.", + "signings-key": "Clé de signature", + "signings-key-hint": "Chaîne encodée en base64 représentant au moins 512 bits de données.", + "signings-key-required": "La clé de signature est requise.", + "signings-key-min-length": "La clé de signature doit contenir au moins 512 bits de données.", + "signings-key-base64": "La clé de signature doit être au format base64.", + "expiration-time": "Durée de validité du jeton (sec)", + "expiration-time-required": "La durée de validité du jeton est requise.", + "expiration-time-max": "La durée maximale autorisée est de 2147483647 secondes (68 ans).", + "expiration-time-min": "Le temps minimum est de 60 secondes (1 minute).", + "refresh-expiration-time": "Durée de validité du jeton de rafraîchissement (sec)", + "refresh-expiration-time-required": "La durée de validité du jeton de rafraîchissement est requise.", + "refresh-expiration-time-max": "La durée maximale autorisée est de 2147483647 secondes (68 ans).", + "refresh-expiration-time-min": "Le temps minimum est de 900 secondes (15 minutes).", + "refresh-expiration-time-less-token": "Le temps du jeton de rafraîchissement doit être supérieur à celui du jeton principal.", + "generate-key": "Générer une clé", + "info-header": "Tous les utilisateurs devront se reconnecter", + "info-message": "La modification de la clé de signature JWT invalidera tous les jetons émis. Tous les utilisateurs devront se reconnecter. Cela affectera également les scripts utilisant l'API REST/Websockets." + }, + "resources": "Ressources", + "notifications": "Notifications", + "notifications-settings": "Paramètres des notifications", + "slack-api-token": "Jeton API Slack", + "slack": "Slack", + "slack-settings": "Paramètres Slack", + "mobile-settings": "Paramètres mobiles", + "firebase-service-account-file": "Fichier JSON des identifiants du compte de service Firebase", + "select-firebase-service-account-file": "Glissez-déposez votre fichier d'identifiants de compte de service Firebase ou " + }, + "alarm": { + "alarm": "Alarme", + "alarms": "Alarmes", + "all-alarms": "Toutes les alarmes", + "select-alarm": "Sélectionner une alarme", + "no-alarms-matching": "Aucune alarme correspondant à '{{entity}}' n'a été trouvée.", + "alarm-required": "L'alarme est requise", + "alarm-filter": "Filtre d'alarme", + "filter": "Filtre", + "alarm-status": "Statut de l'alarme", + "alarm-status-list": "Liste des statuts d'alarme", + "any-status": "N'importe quel statut", + "search-status": { + "ANY": "N'importe lequel", + "ACTIVE": "Active", + "CLEARED": "Réinitialisée", + "ACK": "Reconnu", + "UNACK": "Non reconnu" + }, + "display-status": { + "ACTIVE_UNACK": "Active non reconnue", + "ACTIVE_ACK": "Active reconnue", + "CLEARED_UNACK": "Réinitialisée non reconnue", + "CLEARED_ACK": "Réinitialisée reconnue" + }, + "no-alarms-prompt": "Aucune alarme trouvée", + "created-time": "Heure de création", + "type": "Type", + "severity": "Gravité", + "originator": "Initiateur", + "originator-type": "Type d'initiateur", + "details": "Détails", + "originator-label": "Étiquette de l'initiateur", + "assign": "Assigner", + "assignments": "Affectations", + "assignee": "Assigné", + "assignee-id": "ID de l'assigné", + "assignee-first-name": "Prénom de l'assigné", + "assignee-last-name": "Nom de l'assigné", + "assignee-email": "Email de l'assigné", + "unassigned": "Non assigné", + "user-deleted": "Utilisateur supprimé", + "assignee-not-set": "Tous", + "status": "Statut", + "alarm-details": "Détails de l'alarme", + "start-time": "Heure de début", + "assign-time": "Heure d'assignation", + "end-time": "Heure de fin", + "ack-time": "Heure de reconnaissance", + "clear-time": "Heure de réinitialisation", + "duration": "Durée", + "alarm-severity": "Gravité de l'alarme", + "alarm-severity-list": "Liste des niveaux de gravité", + "any-severity": "N'importe quelle gravité", + "severity-critical": "Critique", + "severity-major": "Majeure", + "severity-minor": "Mineure", + "severity-warning": "Avertissement", + "severity-indeterminate": "Indéterminée", + "acknowledge": "Reconnaître", + "clear": "Réinitialiser", + "delete": "Supprimer", + "search": "Rechercher des alarmes", + "selected-alarms": "{ count, plural, =1 {1 alarme} other {# alarmes} } sélectionnée(s)", + "no-data": "Aucune donnée à afficher", + "polling-interval": "Intervalle de sondage des alarmes (sec)", + "polling-interval-required": "L'intervalle de sondage des alarmes est requis.", + "min-polling-interval-message": "L'intervalle minimum autorisé est de 1 seconde.", + "aknowledge-alarms-title": "Reconnaître { count, plural, =1 {1 alarme} other {# alarmes} }", + "aknowledge-alarms-text": "Êtes-vous sûr de vouloir reconnaître { count, plural, =1 {1 alarme} other {# alarmes} } ?", + "aknowledge-alarm-title": "Reconnaître l'alarme", + "aknowledge-alarm-text": "Êtes-vous sûr de vouloir reconnaître l'alarme ?", + "selected-alarms-are-acknowledged": "Les alarmes sélectionnées sont déjà reconnues", + "clear-alarms-title": "Réinitialiser { count, plural, =1 {1 alarme} other {# alarmes} }", + "clear-alarms-text": "Êtes-vous sûr de vouloir réinitialiser { count, plural, =1 {1 alarme} other {# alarmes} } ?", + "clear-alarm-title": "Réinitialiser l'alarme", + "clear-alarm-text": "Êtes-vous sûr de vouloir réinitialiser l'alarme ?", + "delete-alarms-title": "Supprimer { count, plural, =1 {1 alarme} other {# alarmes} }", + "delete-alarms-text": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 alarme} other {# alarmes} } ?", + "selected-alarms-are-cleared": "Les alarmes sélectionnées sont déjà réinitialisées", + "alarm-status-filter": "Filtre de statut d'alarme", + "alarm-filter-title": "Filtre d'alarme", + "assigned": "Assigné", + "filter-title": "Filtre", + "max-count-load": "Nombre maximum d'alarmes à charger (0 - illimité)", + "max-count-load-required": "Le nombre maximum d'alarmes à charger est requis.", + "max-count-load-error-min": "La valeur minimale est 0.", + "fetch-size": "Taille de récupération", + "fetch-size-required": "La taille de récupération est requise.", + "fetch-size-error-min": "La valeur minimale est 10.", + "alarm-types": "Types d'alarmes", + "alarm-type-list": "Liste des types d'alarmes", + "any-type": "N'importe quel type", + "assigned-to-current-user": "Assigné à l'utilisateur actuel", + "assigned-to-me": "Assigné à moi", + "search-propagated-alarms": "Rechercher les alarmes propagées", + "comments": "Commentaires de l'alarme", + "show-more": "Afficher plus", + "additional-info": "Informations supplémentaires", + "alarm-type": "Type d'alarme", + "enter-alarm-type": "Entrer le type d'alarme", + "no-alarm-types-matching": "Aucun type d'alarme correspondant à '{{entitySubtype}}' n'a été trouvé.", + "alarm-type-list-empty": "Aucun type d'alarme sélectionné." + }, + "alarm-activity": { + "add": "Ajouter un commentaire...", + "alarm-comment": "Commentaire d'alarme", + "comments": "Commentaires", + "delete-alarm-comment": "Voulez-vous supprimer ce commentaire ?", + "refresh": "Rafraîchir", + "oldest-first": "Le plus ancien en premier", + "newest-first": "Le plus récent en premier", + "activity": "Activité", + "export": "Exporter au format CSV", + "author": "Auteur", + "created-date": "Date de création", + "edited-date": "Date de modification", + "text": "Texte", + "system": "Système" + }, + "alias": { + "add": "Ajouter un alias", + "edit": "Modifier l'alias", + "name": "Nom de l'alias", + "name-required": "Le nom de l'alias est requis", + "duplicate-alias": "Un alias portant le même nom existe déjà.", + "filter-type-single-entity": "Entité unique", + "filter-type-entity-list": "Liste d'entités", + "filter-type-entity-name": "Nom de l'entité", + "filter-type-entity-type": "Type d'entité", + "filter-type-state-entity": "Entité à partir de l'état du tableau de bord", + "filter-type-state-entity-description": "Entité extraite des paramètres d'état du tableau de bord", + "filter-type-asset-type": "Type d'actif", + "filter-type-asset-type-description": "Actifs de type '{{assetTypes}}'", + "filter-type-asset-type-and-name-description": "Actifs de type '{{assetTypes}}' dont le nom commence par '{{prefix}}'", + "filter-type-device-type": "Type d'appareil", + "filter-type-device-type-description": "Appareils de type '{{deviceTypes}}'", + "filter-type-device-type-and-name-description": "Appareils de type '{{deviceTypes}}' dont le nom commence par '{{prefix}}'", + "filter-type-entity-view-type": "Type de vue d'entité", + "filter-type-entity-view-type-description": "Vues d'entité de type '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description": "Vues d'entité de type '{{entityViewTypes}}' dont le nom commence par '{{prefix}}'", + "filter-type-edge-type": "Type d'Edge", + "filter-type-edge-type-description": "Edges de type '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description": "Edges de type '{{edgeTypes}}' dont le nom commence par '{{prefix}}'", + "filter-type-relations-query": "Requête de relations", + "filter-type-relations-query-description": "{{entities}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Requête de recherche d'Edge", + "filter-type-edge-search-query-description": "Edges de types {{edgeTypes}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query": "Requête de recherche d'actifs", + "filter-type-asset-search-query-description": "Actifs de types {{assetTypes}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query": "Requête de recherche d'appareils", + "filter-type-device-search-query-description": "Appareils de types {{deviceTypes}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query": "Requête de recherche de vue d'entité", + "filter-type-entity-view-search-query-description": "Vues d'entité de types {{entityViewTypes}} ayant une relation {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState": "État d'utilisation de l'API", + "entity-filter": "Filtre d'entité", + "resolve-multiple": "Résoudre en tant que plusieurs entités", + "resolve-multiple-hint": "Activer pour afficher les données de toutes les entités filtrées simultanément.\nSi désactivé, le widget affiche uniquement les données de l'entité sélectionnée.", + "filter-type": "Type de filtre", + "filter-type-required": "Le type de filtre est requis.", + "entity-filter-no-entity-matched": "Aucune entité ne correspond au filtre spécifié.", + "no-entity-filter-specified": "Aucun filtre d'entité spécifié", + "root-state-entity": "Utiliser l'entité d'état du tableau de bord comme racine", + "last-level-relation": "Ne récupérer que le dernier niveau de relation", + "root-entity": "Entité racine", + "state-entity-parameter-name": "Nom du paramètre d'entité d'état", + "default-state-entity": "Entité d'état par défaut", + "default-entity-parameter-name": "Par défaut", + "max-relation-level": "Niveau maximum de relation", + "unlimited-level": "Niveau illimité", + "state-entity": "Entité d'état du tableau de bord", + "all-entities": "Toutes les entités", + "any-relation": "n'importe laquelle" + }, + "asset": { + "asset": "Actif", + "assets": "Actifs", + "management": "Gestion des actifs", + "view-assets": "Voir les actifs", + "add": "Ajouter un actif", + "asset-type-max-length": "Le type d'actif doit contenir moins de 256 caractères", + "assign-to-customer": "Assigner à un client", + "assign-asset-to-customer": "Assigner des actifs à un client", + "assign-asset-to-customer-text": "Veuillez sélectionner les actifs à assigner au client", + "no-assets-text": "Aucun actif trouvé", + "assign-to-customer-text": "Veuillez sélectionner le client auquel assigner les actifs", + "public": "Public", + "assignedToCustomer": "Assigné au client", + "make-public": "Rendre l'actif public", + "make-private": "Rendre l'actif privé", + "unassign-from-customer": "Désassigner du client", + "delete": "Supprimer l'actif", + "asset-public": "L'actif est public", + "asset-type": "Type d'actif", + "asset-type-required": "Le type d'actif est requis.", + "select-asset-type": "Sélectionner le type d'actif", + "enter-asset-type": "Saisir le profil d'actif", + "any-asset": "N'importe quel actif", + "no-asset-types-matching": "Aucun type d'actif correspondant à '{{entitySubtype}}' trouvé.", + "asset-type-list-empty": "Aucun type d'actif sélectionné.", + "asset-types": "Types d'actifs", + "name": "Nom", + "name-required": "Le nom est requis.", + "name-max-length": "Le nom doit contenir moins de 256 caractères", + "label-max-length": "L'étiquette doit contenir moins de 256 caractères", + "description": "Description", + "type": "Type", + "type-required": "Le type est requis.", + "details": "Détails", + "events": "Événements", + "add-asset-text": "Ajouter un nouvel actif", + "asset-details": "Détails de l'actif", + "assign-assets": "Assigner des actifs", + "assign-assets-text": "Assigner { count, plural, =1 {1 actif} other {# actifs} } à un client", + "assign-asset-to-edge-title": "Assigner des actifs à une Edge", + "assign-asset-to-edge-text": "Veuillez sélectionner les actifs à assigner à la Edge", + "delete-assets": "Supprimer des actifs", + "unassign-assets": "Désassigner des actifs", + "unassign-assets-action-title": "Désassigner { count, plural, =1 {1 actif} other {# actifs} } du client", + "assign-new-asset": "Assigner un nouvel actif", + "delete-asset-title": "Êtes-vous sûr de vouloir supprimer l'actif '{{assetName}}' ?", + "delete-asset-text": "Attention, après confirmation l'actif et toutes les données associées seront irrécupérables.", + "delete-assets-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 actif} other {# actifs} } ?", + "delete-assets-action-title": "Supprimer { count, plural, =1 {1 actif} other {# actifs} }", + "delete-assets-text": "Attention, après confirmation tous les actifs sélectionnés seront supprimés ainsi que toutes les données associées.", + "make-public-asset-title": "Êtes-vous sûr de vouloir rendre l'actif '{{assetName}}' public ?", + "make-public-asset-text": "Après confirmation, l'actif et toutes ses données seront rendus publics et accessibles à d'autres.", + "make-private-asset-title": "Êtes-vous sûr de vouloir rendre l'actif '{{assetName}}' privé ?", + "make-private-asset-text": "Après confirmation, l'actif et toutes ses données seront rendus privés et ne seront plus accessibles à d'autres.", + "unassign-asset-title": "Êtes-vous sûr de vouloir désassigner l'actif '{{assetName}}' ?", + "unassign-asset-text": "Après confirmation, l'actif sera désassigné et ne sera plus accessible par le client.", + "unassign-asset": "Désassigner l'actif", + "unassign-assets-title": "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 actif} other {# actifs} } ?", + "unassign-assets-text": "Après confirmation, tous les actifs sélectionnés seront désassignés et ne seront plus accessibles par le client.", + "copyId": "Copier l'identifiant de l'actif", + "idCopiedMessage": "L'identifiant de l'actif a été copié dans le presse-papiers", + "select-asset": "Sélectionner un actif", + "no-assets-matching": "Aucun actif correspondant à '{{entity}}' trouvé.", + "asset-required": "L'actif est requis", + "name-starts-with": "Expression du nom de l'actif", + "help-text": "Utilisez '%' selon le besoin : '%nom_actif_contient%', '%nom_actif_termine', 'actif_commence_par'.", + "search": "Rechercher des actifs", + "import": "Importer des actifs", + "asset-file": "Fichier d'actifs", + "label": "Étiquette", + "assign-asset-to-edge": "Assigner des actifs à une Edge", + "unassign-asset-from-edge": "Désassigner l'actif", + "unassign-asset-from-edge-title": "Êtes-vous sûr de vouloir désassigner l'actif '{{assetName}}' ?", + "unassign-asset-from-edge-text": "Après confirmation, l'actif sera désassigné et ne sera plus accessible par la Edge.", + "unassign-assets-from-edge-title": "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 actif} other {# actifs} } ?", + "unassign-assets-from-edge-text": "Après confirmation, tous les actifs sélectionnés seront désassignés et ne seront plus accessibles par la Edge.", + "selected-assets": "{ count, plural, =1 {1 actif} other {# actifs} } sélectionné(s)" + }, + "attribute": { + "attributes": "Attributs", + "latest-telemetry": "Dernière télémétrie", + "no-latest-telemetry": "Aucune télémétrie récente", + "attributes-scope": "Portée des attributs d'entité", + "scope-telemetry": "Télémétrie", + "scope-latest-telemetry": "Dernière télémétrie", + "scope-client": "Attributs client", + "scope-server": "Attributs serveur", + "scope-shared": "Attributs partagés", + "scope-client-short": "Client", + "scope-server-short": "Serveur", + "scope-shared-short": "Partagé", + "scope-latest-short": "Dernier", + "scope-any": "N'importe quel", + "add": "Ajouter un attribut", + "key": "Clé", + "key-max-length": "La clé doit contenir moins de 256 caractères", + "last-update-time": "Dernière mise à jour", + "key-required": "La clé de l'attribut est requise.", + "value": "Valeur", + "value-required": "La valeur de l'attribut est requise.", + "telemetry-key-required": "La clé de télémétrie est requise", + "telemetry-value-required": "La valeur de télémétrie est requise", + "delete-attributes-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 attribut} other {# attributs} } ?", + "delete-attributes-text": "Attention, après confirmation, tous les attributs sélectionnés seront supprimés.", + "delete-attributes": "Supprimer les attributs", + "enter-attribute-value": "Entrer la valeur de l'attribut", + "show-on-widget": "Afficher sur le widget", + "widget-mode": "Mode widget", + "next-widget": "Widget suivant", + "prev-widget": "Widget précédent", + "add-to-dashboard": "Ajouter au tableau de bord", + "add-widget-to-dashboard": "Ajouter le widget au tableau de bord", + "selected-attributes": "{ count, plural, =1 {1 attribut} other {# attributs} } sélectionné(s)", + "selected-telemetry": "{ count, plural, =1 {1 unité de télémétrie} other {# unités de télémétrie} } sélectionnée(s)", + "no-attributes-text": "Aucun attribut trouvé", + "no-telemetry-text": "Aucune télémétrie trouvée", + "copy-key": "Copier la clé", + "add-telemetry": "Ajouter une télémétrie", + "copy-value": "Copier la valeur", + "delete-timeseries": { + "start-time": "Heure de début", + "ends-on": "Se termine le", + "strategy": "Stratégie", + "delete-strategy": "Stratégie de suppression", + "all-data": "Supprimer toutes les données", + "all-data-except-latest-value": "Supprimer toutes les données sauf la dernière valeur", + "latest-value": "Supprimer la dernière valeur", + "all-data-for-time-period": "Supprimer toutes les données pour une période donnée", + "rewrite-latest-value": "Réécrire la dernière valeur" + } + }, + "api-usage": { + "api-features": "Fonctionnalités de l'API", + "api-usage": "Utilisation de l'API", + "alarm": "Alarme", + "alarms-created": "Alarmes créées", + "queue-stats": "Statistiques de file", + "processing-failures-and-timeouts": "Échecs de traitement et expirations", + "exceptions": "Exceptions", + "alarms-created-daily-activity": "Activité quotidienne des alarmes créées", + "alarms-created-hourly-activity": "Activité horaire des alarmes créées", + "alarms-created-monthly-activity": "Activité mensuelle des alarmes créées", + "data-points": "Points de données", + "data-points-storage-days": "Durée de stockage des points de données (jours)", + "device-api": "API des appareils", + "email": "Email", + "email-messages": "Messages email", + "email-messages-daily-activity": "Activité quotidienne des emails", + "email-messages-monthly-activity": "Activité mensuelle des emails", + "executions": "Exécutions", + "scripts": "Scripts", + "scripts-hourly-activity": "Activité horaire des scripts", + "scripts-daily-activity": "Activité quotidienne des scripts", + "scripts-monthly-activity": "Activité mensuelle des scripts", + "javascript": "JavaScript", + "javascript-executions": "Exécutions JavaScript", + "tbel": "TBEL", + "tbel-executions": "Exécutions TBEL", + "latest-error": "Dernière erreur", + "messages": "Messages", + "notifications": "Notifications", + "notifications-email-sms": "Notifications (Email/SMS)", + "notifications-hourly-activity": "Activité horaire des notifications", + "permanent-failures": "Échecs permanents de ${entityName}", + "permanent-timeouts": "Expirations permanentes de ${entityName}", + "processing-failures": "Échecs de traitement de ${entityName}", + "processing-timeouts": "Expirations de traitement de ${entityName}", + "rule-chain": "Chaîne de règles", + "rule-engine": "Moteur de règles", + "rule-engine-daily-activity": "Activité quotidienne du moteur de règles", + "rule-engine-executions": "Exécutions du moteur de règles", + "rule-engine-hourly-activity": "Activité horaire du moteur de règles", + "rule-engine-monthly-activity": "Activité mensuelle du moteur de règles", + "rule-engine-statistics": "Statistiques du moteur de règles", + "rule-node": "Nœud de règle", + "sms": "SMS", + "sms-messages": "Messages SMS", + "sms-messages-daily-activity": "Activité quotidienne des SMS", + "sms-messages-monthly-activity": "Activité mensuelle des SMS", + "successful": "${entityName} Réussis", + "telemetry": "Télémétrie", + "telemetry-persistence": "Persistance de la télémétrie", + "telemetry-persistence-daily-activity": "Activité quotidienne de persistance", + "telemetry-persistence-hourly-activity": "Activité horaire de persistance", + "telemetry-persistence-monthly-activity": "Activité mensuelle de persistance", + "transport": "Transport", + "transport-daily-activity": "Activité quotidienne de transport", + "transport-data-points": "Points de données de transport", + "transport-hourly-activity": "Activité horaire de transport", + "transport-messages": "Messages de transport", + "transport-monthly-activity": "Activité mensuelle de transport", + "view-details": "Voir les détails", + "view-statistics": "Voir les statistiques" + }, + "api-limit": { + "cassandra-queries": "Requêtes Cassandra", + "entity-version-creation": "Création de version d'entité", + "entity-version-load": "Chargement de version d'entité", + "notification-requests": "Requêtes de notification", + "notification-requests-per-rule": "Requêtes de notification par règle", + "rest-api-requests": "Requêtes REST API", + "rest-api-requests-per-customer": "Requêtes REST API par client", + "transport-messages": "Messages de transport", + "transport-messages-per-device": "Messages de transport par appareil", + "transport-messages-per-gateway": "Messages de transport par passerelle", + "transport-messages-per-gateway-device": "Messages de transport par appareil de passerelle", + "ws-updates-per-session": "Mises à jour WS par session", + "edge-events": "Événements Edge", + "edge-events-per-edge": "Événements Edge par instance", + "edge-uplink-messages": "Messages montants Edge", + "edge-uplink-messages-per-edge": "Messages montants Edge par instance" + }, + "audit-log": { + "audit": "Audit", + "audit-logs": "Journaux d'audit", + "timestamp": "Horodatage", + "entity-type": "Type d'entité", + "entity-name": "Nom de l'entité", + "user": "Utilisateur", + "type": "Type", + "status": "Statut", + "details": "Détails", + "type-added": "Ajouté", + "type-deleted": "Supprimé", + "type-updated": "Mis à jour", + "type-attributes-updated": "Attributs mis à jour", + "type-attributes-deleted": "Attributs supprimés", + "type-rpc-call": "Appel RPC", + "type-credentials-updated": "Identifiants mis à jour", + "type-assigned-to-customer": "Assigné à un client", + "type-unassigned-from-customer": "Désassigné d'un client", + "type-assigned-to-edge": "Assigné à une Edge", + "type-unassigned-from-edge": "Désassigné d'une Edge", + "type-activated": "Activé", + "type-suspended": "Suspendu", + "type-credentials-read": "Identifiants lus", + "type-attributes-read": "Attributs lus", + "type-relation-add-or-update": "Relation mise à jour", + "type-relation-delete": "Relation supprimée", + "type-relations-delete": "Toutes les relations supprimées", + "type-alarm-ack": "Alarme reconnue", + "type-alarm-clear": "Alarme réinitialisée", + "type-alarm-delete": "Alarme supprimée", + "type-alarm-assign": "Alarme assignée", + "type-alarm-unassign": "Alarme désassignée", + "type-added-comment": "Commentaire ajouté", + "type-updated-comment": "Commentaire mis à jour", + "type-deleted-comment": "Commentaire supprimé", + "type-login": "Connexion", + "type-logout": "Déconnexion", + "type-lockout": "Verrouillage", + "status-success": "Succès", + "status-failure": "Échec", + "audit-log-details": "Détails du journal d'audit", + "no-audit-logs-prompt": "Aucun journal trouvé", + "action-data": "Données d'action", + "failure-details": "Détails de l'échec", + "search": "Rechercher dans les journaux d'audit", + "clear-search": "Effacer la recherche", + "type-assigned-from-tenant": "Assigné depuis un tenant", + "type-assigned-to-tenant": "Assigné à un tenant", + "type-provision-success": "Appareil provisionné", + "type-provision-failure": "Le provisionnement de l'appareil a échoué", + "type-timeseries-updated": "Télémétrie mise à jour", + "type-timeseries-deleted": "Télémétrie supprimée", + "type-sms-sent": "SMS envoyé" + }, + "debug-settings": { + "label": "Configuration de débogage", + "on-failure": "Échecs uniquement (24/7)", + "all-messages": "Tous les messages ({{time}})", + "failures": "Échecs", + "entity": "entité", + "hint": { + "main-limited": "Pas plus de {{msg}} messages de débogage de {{entity}} par {{time}} ne seront enregistrés.", + "on-failure": "Enregistrer uniquement les messages d'erreur.", + "all-messages": "Enregistrer tous les messages de débogage." + } + }, + "calculated-fields": { + "expression": "Expression", + "no-found": "Aucun champ calculé trouvé", + "list": "{ count, plural, =1 {Un champ calculé} other {Liste de # champs calculés} }", + "selected-fields": "{ count, plural, =1 {1 champ calculé} other {# champs calculés} } sélectionné(s)", + "type": { + "simple": "Simple", + "script": "Script" + }, + "arguments": "Arguments", + "decimals-by-default": "Décimales par défaut", + "debugging": "Débogage du champ calculé", + "argument-name": "Nom de l'argument", + "datasource": "Source de données", + "add-argument": "Ajouter un argument", + "test-script-function": "Tester la fonction script", + "no-arguments": "Aucun argument configuré", + "argument-settings": "Paramètres de l'argument", + "argument-current": "Entité actuelle", + "argument-current-tenant": "Tenant actuel", + "argument-device": "Appareil", + "argument-asset": "Actif", + "argument-customer": "Client", + "argument-tenant": "Tenant actuel", + "argument-type": "Type d'argument", + "see-debug-events": "Voir les événements de débogage", + "attribute": "Attribut", + "copy-argument-name": "Copier le nom de l'argument", + "timeseries-key": "Clé de série temporelle", + "device-name": "Nom de l'appareil", + "latest-telemetry": "Dernière télémétrie", + "rolling": "Rolling des séries temporelles", + "attribute-scope": "Portée de l'attribut", + "server-attributes": "Attributs serveur", + "client-attributes": "Attributs client", + "shared-attributes": "Attributs partagés", + "attribute-key": "Clé d'attribut", + "default-value": "Valeur par défaut", + "limit": "Valeurs max", + "time-window": "Fenêtre temporelle", + "customer-name": "Nom du client", + "asset-name": "Nom de l'actif", + "timeseries": "Séries temporelles", + "output": "Résultat", + "create": "Créer un nouveau champ calculé", + "file": "Fichier de champ calculé", + "invalid-file-error": "Format de fichier invalide. Veuillez vérifier que le fichier est bien un fichier JSON valide.", + "import": "Importer un champ calculé", + "export": "Exporter un champ calculé", + "export-failed-error": "Impossible d'exporter le champ calculé : {{error}}", + "output-type": "Type de résultat", + "delete-title": "Êtes-vous sûr de vouloir supprimer le champ calculé '{{title}}' ?", + "delete-text": "Attention, après confirmation le champ calculé et toutes les données associées seront irrécupérables.", + "delete-multiple-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 champ calculé} other {# champs calculés} } ?", + "delete-multiple-text": "Attention, après confirmation tous les champs calculés sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "test-with-this-message": "Tester avec ce message", + "hint": { + "arguments-simple-with-rolling": "Le type simple de champ calculé ne doit pas contenir de clés avec un rolling de séries temporelles.", + "arguments-empty": "Les arguments ne doivent pas être vides.", + "expression-required": "L'expression est requise.", + "expression-invalid": "L'expression est invalide", + "expression-max-length": "La longueur de l'expression doit être inférieure à 255 caractères.", + "argument-name-required": "Le nom de l'argument est requis.", + "argument-name-pattern": "Le nom de l'argument est invalide.", + "argument-name-duplicate": "Un argument portant ce nom existe déjà.", + "argument-name-max-length": "Le nom de l'argument doit contenir moins de 256 caractères.", + "argument-name-forbidden": "Le nom de l'argument est réservé et ne peut pas être utilisé.", + "argument-type-required": "Le type d'argument est requis.", + "max-args": "Nombre maximum d'arguments atteint.", + "decimals-range": "Les décimales par défaut doivent être un nombre entre 0 et 15.", + "expression": "L'expression par défaut montre comment transformer une température de Fahrenheit en Celsius.", + "arguments-entity-not-found": "L'entité cible de l'argument est introuvable." + } + }, + "confirm-on-exit": { + "message": "Vous avez des modifications non enregistrées. Êtes-vous sûr de vouloir quitter cette page ?", + "html-message": "Vous avez des modifications non enregistrées.
Êtes-vous sûr de vouloir quitter cette page ?", + "title": "Modifications non enregistrées" + }, + "contact": { + "country": "Pays", + "country-required": "Le pays est requis.", + "city": "Ville", + "state": "État / Province", + "postal-code": "Code postal / ZIP", + "postal-code-invalid": "Format de code postal / ZIP invalide.", + "address": "Adresse", + "address2": "Adresse 2", + "phone": "Téléphone", + "email": "Email", + "no-address": "Aucune adresse", + "no-country-found": "Aucun pays trouvé.", + "no-country-matching": "Aucun pays correspondant à '{{country}}' n'a été trouvé.", + "state-max-length": "La longueur de l'état doit être inférieure à 256 caractères", + "phone-max-length": "Le numéro de téléphone doit contenir moins de 256 caractères", + "city-max-length": "La ville spécifiée doit contenir moins de 256 caractères" + }, + "common": { + "name": "Nom", + "type": "Type", + "general": "Général", + "username": "Nom d'utilisateur", + "password": "Mot de passe", + "data": "Données", + "timestamp": "Horodatage", + "enter-username": "Saisir le nom d'utilisateur", + "enter-password": "Saisir le mot de passe", + "enter-search": "Saisir une recherche", + "created-time": "Date de création", + "disabled": "Désactivé", + "loading": "Chargement...", + "proceed": "Continuer", + "open-details-page": "Ouvrir la page de détails", + "not-found": "Introuvable", + "value": "Valeur", + "documentation": "Documentation", + "time-left": "{{time}} restantes", + "output": "Sortie", + "suffix": { + "s": "s", + "ms": "ms" + }, + "hint": { + "name-required": "Le nom est requis.", + "name-pattern": "Le nom est invalide.", + "name-max-length": "Le nom doit contenir moins de 256 caractères.", + "title-required": "Le titre est requis.", + "title-pattern": "Le titre est invalide.", + "title-max-length": "Le titre doit contenir moins de 256 caractères.", + "key-required": "La clé est requise.", + "key-pattern": "La clé est invalide.", + "key-max-length": "La clé doit contenir moins de 256 caractères." + }, + "required-fields": "Champs obligatoires manquants" + }, + "content-type": { + "json": "Json", + "text": "Texte", + "binary": "Binaire (Base64)" + }, + "color": { + "color": "Couleur" + }, + "customer": { + "customer": "Client", + "customers": "Clients", + "management": "Gestion des clients", + "dashboard": "Tableau de bord client", + "dashboards": "Tableaux de bord client", + "devices": "Appareils client", + "entity-views": "Vues d'entité client", + "assets": "Actifs client", + "public-dashboards": "Tableaux de bord publics", + "public-devices": "Appareils publics", + "public-assets": "Actifs publics", + "public-entity-views": "Vues d'entité publiques", + "add": "Ajouter un client", + "delete": "Supprimer le client", + "manage-customer-users": "Gérer les utilisateurs client", + "manage-customer-devices": "Gérer les appareils client", + "manage-customer-dashboards": "Gérer les tableaux de bord client", + "manage-public-devices": "Gérer les appareils publics", + "manage-public-dashboards": "Gérer les tableaux de bord publics", + "manage-customer-assets": "Gérer les actifs client", + "manage-customer-edges": "Gérer les instances Edge client", + "manage-public-assets": "Gérer les actifs publics", + "add-customer-text": "Ajouter un nouveau client", + "no-customers-text": "Aucun client trouvé", + "customer-details": "Détails du client", + "delete-customer-title": "Êtes-vous sûr de vouloir supprimer le client '{{customerTitle}}' ?", + "delete-customer-text": "Attention, après confirmation, le client et toutes les données associées seront irrécupérables.", + "delete-customers-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 client} other {# clients} } ?", + "delete-customers-action-title": "Supprimer { count, plural, =1 {1 client} other {# clients} }", + "delete-customers-text": "Attention, après confirmation, tous les clients sélectionnés seront supprimés ainsi que toutes les données associées.", + "manage-users": "Gérer les utilisateurs", + "manage-assets": "Gérer les actifs", + "manage-devices": "Gérer les appareils", + "manage-dashboards": "Gérer les tableaux de bord", + "title": "Titre", + "title-required": "Le titre est requis.", + "title-max-length": "Le titre doit contenir moins de 256 caractères", + "description": "Description", + "details": "Détails", + "events": "Événements", + "copyId": "Copier l'identifiant du client", + "idCopiedMessage": "Identifiant du client copié dans le presse-papiers", + "select-customer": "Sélectionner un client", + "no-customers-matching": "Aucun client correspondant à '{{entity}}' trouvé.", + "customer-required": "Client requis", + "select-default-customer": "Sélectionner le client par défaut", + "default-customer": "Client par défaut", + "default-customer-required": "Le client par défaut est requis pour déboguer le tableau de bord au niveau du tenant", + "search": "Rechercher des clients", + "selected-customers": "{ count, plural, =1 {1 client} other {# clients} } sélectionné(s)", + "edges": "Instances Edge du client", + "manage-edges": "Gérer les instances Edge" + }, + "css-size": { + "size-value-required": "La valeur de taille est requise", + "invalid-size-value": "Valeur de taille invalide" + }, + "date": { + "last-update-n-ago": "Dernière mise à jour il y a N", + "last-update-n-ago-text": "Dernière mise à jour {{ agoText }}", + "custom-date": "Date personnalisée", + "format": "Format", + "preview": "Aperçu", + "auto": "Auto", + "time-granularity-formats": "Formats de granularité temporelle", + "unit-year": "Années", + "unit-month": "Mois", + "unit-day": "Jours", + "unit-hour": "Heures", + "unit-minute": "Minutes", + "unit-second": "Secondes", + "unit-millisecond": "Millisecondes" + }, + "datetime": { + "date-from": "Date de début", + "time-from": "Heure de début", + "date-to": "Date de fin", + "time-to": "Heure de fin", + "from": "De", + "to": "À" + }, + "dashboard": { + "dashboard": "Tableau de bord", + "dashboards": "Tableaux de bord", + "management": "Gestion des tableaux de bord", + "view-dashboards": "Voir les tableaux de bord", + "add": "Ajouter un tableau de bord", + "assign-dashboard-to-customer": "Assigner le(s) tableau(x) de bord au client", + "assign-dashboard-to-customer-text": "Veuillez sélectionner les tableaux de bord à assigner au client", + "assign-to-customer-text": "Veuillez sélectionner le client à qui assigner le(s) tableau(x) de bord", + "assign-to-customer": "Assigner au client", + "unassign-from-customer": "Désassigner du client", + "make-public": "Rendre public le tableau de bord", + "make-private": "Rendre privé le tableau de bord", + "manage-assigned-customers": "Gérer les clients assignés", + "assigned-customers": "Clients assignés", + "assign-to-customers": "Assigner le(s) tableau(x) de bord aux clients", + "assign-to-customers-text": "Veuillez sélectionner les clients à qui assigner le(s) tableau(x) de bord", + "unassign-from-customers": "Désassigner le(s) tableau(x) de bord des clients", + "unassign-from-customers-text": "Veuillez sélectionner les clients à désassigner du tableau(x) de bord", + "no-dashboards-text": "Aucun tableau de bord trouvé", + "no-widgets": "Aucun widget configuré", + "add-widget": "Ajouter un nouveau widget", + "add-widget-button-text": "Ajouter un widget", + "title": "Titre", + "image": "Image du tableau de bord", + "mobile-app-settings": "Paramètres de l'application mobile", + "mobile-order": "Ordre du tableau de bord dans l'application mobile", + "mobile-hide": "Masquer le tableau de bord dans l'application mobile", + "update-image": "Mettre à jour l'image du tableau de bord", + "take-screenshot": "Prendre une capture d’écran", + "select-widget-title": "Sélectionner un widget", + "select-widget-value": "{{title}} : sélectionner un widget", + "select-widget-subtitle": "Liste des types de widgets disponibles", + "delete": "Supprimer le tableau de bord", + "title-required": "Le titre est requis.", + "title-max-length": "Le titre doit contenir moins de 256 caractères", + "description": "Description", + "details": "Détails", + "dashboard-details": "Détails du tableau de bord", + "add-dashboard-text": "Ajouter un nouveau tableau de bord", + "assign-dashboards": "Assigner les tableaux de bord", + "assign-new-dashboard": "Assigner un nouveau tableau de bord", + "assign-dashboards-text": "Assigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } aux clients", + "unassign-dashboards-action-text": "Désassigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } des clients", + "delete-dashboards": "Supprimer les tableaux de bord", + "unassign-dashboards": "Désassigner les tableaux de bord", + "unassign-dashboards-action-title": "Désassigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } du client", + "delete-dashboard-title": "Êtes-vous sûr de vouloir supprimer le tableau de bord '{{dashboardTitle}}' ?", + "delete-dashboard-text": "Attention, après confirmation, le tableau de bord et toutes les données associées seront irrécupérables.", + "delete-dashboards-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } ?", + "delete-dashboards-action-title": "Supprimer { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} }", + "delete-dashboards-text": "Attention, après confirmation, tous les tableaux de bord sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "unassign-dashboard-title": "Êtes-vous sûr de vouloir désassigner le tableau de bord '{{dashboardTitle}}' ?", + "unassign-dashboard-text": "Après confirmation, le tableau de bord sera désassigné et ne sera plus accessible par le client.", + "unassign-dashboard": "Désassigner le tableau de bord", + "unassign-dashboards-title": "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } ?", + "unassign-dashboards-text": "Après confirmation, tous les tableaux de bord sélectionnés seront désassignés et ne seront plus accessibles par le client.", + "public-dashboard-title": "Le tableau de bord est maintenant public", + "public-dashboard-text": "Votre tableau de bord {{dashboardTitle}} est maintenant public et accessible via le lien suivant :", + "public-dashboard-notice": "Remarque : N'oubliez pas de rendre publics les appareils concernés afin d'accéder à leurs données.", + "make-private-dashboard-title": "Êtes-vous sûr de vouloir rendre le tableau de bord '{{dashboardTitle}}' privé ?", + "make-private-dashboard-text": "Après confirmation, le tableau de bord sera rendu privé et ne sera plus accessible par d'autres.", + "make-private-dashboard": "Rendre privé le tableau de bord", + "socialshare-text": "'{{dashboardTitle}}' propulsé par ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' propulsé par ThingsBoard", + "select-dashboard": "Sélectionner un tableau de bord", + "no-dashboards-matching": "Aucun tableau de bord correspondant à '{{entity}}' trouvé.", + "dashboard-required": "Le tableau de bord est requis.", + "select-existing": "Sélectionner un tableau de bord existant", + "create-new": "Créer un nouveau tableau de bord", + "new-dashboard-title": "Titre du nouveau tableau de bord", + "open-dashboard": "Ouvrir le tableau de bord", + "set-background": "Définir l'arrière-plan", + "background-color": "Couleur d'arrière-plan", + "background-image": "Image d'arrière-plan", + "background-size-mode": "Mode de taille d’arrière-plan", + "no-image": "Aucune image sélectionnée", + "empty-image": "Aucune image", + "drop-image": "Déposez une image ou cliquez pour sélectionner un fichier à téléverser.", + "maximum-upload-file-size": "Taille maximale de fichier : {{ size }}", + "cannot-upload-file": "Impossible de téléverser le fichier", + "settings": "Paramètres", + "move-all-widgets": "Déplacer tous les widgets", + "move-by": "Déplacer de", + "cols": "colonnes", + "rows": "lignes", + "layout": "Disposition", + "layout-type-default": "Par défaut", + "layout-type-scada": "SCADA", + "layout-type-divider": "Séparateur", + "layout-settings-type": "Paramètres de disposition : point de rupture {{ type }}", + "columns-count": "Nombre de colonnes", + "columns-count-required": "Le nombre de colonnes est requis.", + "min-columns-count-message": "Un minimum de 10 colonnes est requis.", + "max-columns-count-message": "Un maximum de 1000 colonnes est autorisé.", + "min-layout-width": "Largeur minimale de la disposition", + "columns-suffix": "colonnes", + "widgets-margins": "Marge entre les widgets", + "margin-required": "La valeur de la marge est requise.", + "min-margin-message": "0 est la seule valeur minimale autorisée pour la marge.", + "max-margin-message": "50 est la seule valeur maximale autorisée pour la marge.", + "horizontal-margin": "Marge horizontale", + "horizontal-margin-required": "La valeur de la marge horizontale est requise.", + "min-horizontal-margin-message": "0 est la seule valeur minimale autorisée pour la marge horizontale.", + "max-horizontal-margin-message": "50 est la seule valeur maximale autorisée pour la marge horizontale.", + "vertical-margin": "Marge verticale", + "vertical-margin-required": "La valeur de la marge verticale est requise.", + "min-vertical-margin-message": "0 est la seule valeur minimale autorisée pour la marge verticale.", + "max-vertical-margin-message": "50 est la seule valeur maximale autorisée pour la marge verticale.", + "apply-outer-margin": "Appliquer une marge sur les côtés de la disposition", + "autofill-height": "Remplissage automatique de la hauteur de disposition", + "mobile-layout": "Paramètres de disposition mobile", + "mobile-row-height": "Hauteur de ligne mobile", + "mobile-row-height-required": "La hauteur de ligne mobile est requise.", + "min-mobile-row-height-message": "5 pixels est la seule valeur minimale autorisée pour la hauteur de ligne mobile.", + "max-mobile-row-height-message": "200 pixels est la seule valeur maximale autorisée pour la hauteur de ligne mobile.", + "row-height": "Hauteur de ligne", + "row-height-required": "La hauteur de ligne est requise.", + "min-row-height-message": "5 pixels est la seule valeur minimale autorisée pour la hauteur de ligne.", + "max-row-height-message": "200 pixels est la seule valeur maximale autorisée pour la hauteur de ligne.", + "display-first-in-mobile-view": "Afficher en premier dans la vue mobile", + "title-settings": "Paramètres du titre", + "display-title": "Afficher le titre du tableau de bord", + "title-color": "Couleur du titre", + "toolbar-settings": "Paramètres de la barre d’outils", + "hide-toolbar": "Masquer la barre d’outils", + "toolbar-always-open": "Garder la barre d’outils ouverte", + "display-dashboards-selection": "Afficher la sélection des tableaux de bord", + "display-entities-selection": "Afficher la sélection des entités", + "display-filters": "Afficher les filtres", + "display-dashboard-timewindow": "Afficher la fenêtre temporelle", + "display-dashboard-export": "Afficher l'exportation", + "display-update-dashboard-image": "Afficher la mise à jour de l'image du tableau de bord", + "dashboard-logo-settings": "Paramètres du logo du tableau de bord", + "display-dashboard-logo": "Afficher le logo en mode plein écran", + "dashboard-logo-image": "Image du logo du tableau de bord", + "advanced-settings": "Paramètres avancés", + "dashboard-css": "CSS du tableau de bord", + "import": "Importer le tableau de bord", + "export": "Exporter le tableau de bord", + "export-failed-error": "Impossible d’exporter le tableau de bord : {{error}}", + "export-prompt": "Inclure les images et ressources du tableau de bord", + "create-new-dashboard": "Créer un nouveau tableau de bord", + "dashboard-file": "Fichier du tableau de bord", + "invalid-dashboard-file-error": "Impossible d’importer le tableau de bord : structure de données invalide.", + "dashboard-import-missing-aliases-title": "Configurer les alias utilisés par le tableau de bord importé", + "create-new-widget": "Créer un nouveau widget", + "import-widget": "Importer un widget", + "widget-file": "Fichier du widget", + "invalid-widget-file-error": "Impossible d’importer le widget : structure de données invalide.", + "widget-import-missing-aliases-title": "Configurer les alias utilisés par le widget importé", + "open-toolbar": "Ouvrir la barre d’outils du tableau de bord", + "close-toolbar": "Fermer la barre d’outils", + "configuration-error": "Erreur de configuration", + "alias-resolution-error-title": "Erreur de configuration des alias du tableau de bord", + "invalid-aliases-config": "Aucun appareil ne correspond à certains filtres d'alias.
Veuillez contacter votre administrateur pour résoudre ce problème.", + "select-devices": "Sélectionner les appareils", + "assignedToCustomer": "Assigné à un client", + "assignedToCustomers": "Assigné à des clients", + "public": "Public", + "copyId": "Copier l'identifiant du tableau de bord", + "idCopiedMessage": "Identifiant du tableau de bord copié dans le presse-papiers", + "public-link": "Lien public", + "copy-public-link": "Copier le lien public", + "public-link-copied-message": "Lien public du tableau de bord copié dans le presse-papiers", + "manage-states": "Gérer les états du tableau de bord", + "states": "États du tableau de bord", + "states-short": "États", + "search-states": "Rechercher dans les états du tableau de bord", + "selected-states": "{ count, plural, =1 {1 état du tableau de bord} other {# états du tableau de bord} } sélectionné(s)", + "edit-state": "Modifier l'état du tableau de bord", + "delete-state": "Supprimer l'état du tableau de bord", + "add-state": "Ajouter un état du tableau de bord", + "no-states-text": "Aucun état trouvé", + "state": "État du tableau de bord", + "state-name": "Nom", + "state-name-required": "Le nom de l'état est requis.", + "state-id": "ID de l'état", + "state-id-required": "L'ID de l'état est requis.", + "state-id-exists": "Un état de tableau de bord avec le même ID existe déjà.", + "is-root-state": "État racine", + "delete-state-title": "Supprimer l'état du tableau de bord", + "delete-state-text": "Êtes-vous sûr de vouloir supprimer l'état du tableau de bord nommé '{{stateName}}' ?", + "show-details": "Afficher les détails", + "hide-details": "Masquer les détails", + "select-state": "Sélectionner l'état cible", + "state-controller": "Contrôleur d'état", + "state-controller-default": "statique (obsolète)", + "search": "Rechercher des tableaux de bord", + "selected-dashboards": "{ count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } sélectionné(s)", + "home-dashboard": "Tableau de bord d'accueil", + "home-dashboard-hide-toolbar": "Masquer la barre d'outils du tableau de bord d'accueil", + "unassign-dashboard-from-edge-text": "Après confirmation, le tableau de bord sera désassigné et ne sera plus accessible par l'instance edge.", + "unassign-dashboards-from-edge-title": "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } ?", + "unassign-dashboards-from-edge-text": "Après confirmation, tous les tableaux de bord sélectionnés seront désassignés et ne seront plus accessibles par l'instance edge.", + "assign-dashboard-to-edge": "Assigner le(s) tableau(x) de bord à l'instance Edge", + "assign-dashboard-to-edge-text": "Veuillez sélectionner les tableaux de bord à assigner à l'instance Edge", + "non-existent-dashboard-state-error": "L'état du tableau de bord avec l'identifiant \"{{ stateId }}\" est introuvable", + "edit-mode": "Mode édition", + "duplicate-state-action": "Dupliquer l'état", + "breakpoint-value": "Point de rupture ({{ value }})", + "breakpoints-id": { + "default": "Par défaut", + "xs": "Mobile (xs)", + "sm": "Tablette (sm)", + "md": "Ordinateur portable (md)", + "lg": "Bureau (lg)", + "xl": "Bureau (xl)" + }, + "view-format-type-grid": "Grille", + "view-format-type-list": "Liste", + "view-format": "Format d'affichage" + }, + "datakey": { + "settings": "Paramètres", + "general": "Général", + "advanced": "Avancé", + "key": "Clé", + "keys": "Clés", + "label": "Étiquette", + "color": "Couleur", + "units": "Symbole spécial à afficher à côté de la valeur", + "decimals": "Nombre de chiffres après la virgule", + "data-generation-func": "Fonction de génération de données", + "use-data-post-processing-func": "Utiliser une fonction de post-traitement des données", + "configuration": "Configuration de la clé de données", + "timeseries": "Séries temporelles", + "attributes": "Attributs", + "entity-field": "Champ de l'entité", + "alarm": "Champs d'alarme", + "timeseries-required": "Les séries temporelles de l'entité sont requises.", + "timeseries-or-attributes-required": "Séries temporelles/attributs de l'entité requis.", + "alarm-fields-timeseries-or-attributes-required": "Champs d'alarme ou séries temporelles/attributs de l'entité requis.", + "maximum-timeseries-or-attributes": "Maximum { count, plural, =1 {1 série temporelle/attribut autorisé.} other {# séries temporelles/attributs autorisés} }", + "alarm-fields-required": "Les champs d'alarme sont requis.", + "function-types": "Types de fonctions", + "function-type": "Type de fonction", + "function-types-required": "Les types de fonctions sont requis.", + "data-keys": "Clés de données", + "data-key": "Clé de données", + "data-keys-required": "Les clés de données sont requises.", + "data-key-required": "La clé de données est requise.", + "alarm-keys": "Clés de données d'alarme", + "alarm-key": "Clé de données d'alarme", + "alarm-key-functions": "Fonctions de clé d'alarme", + "alarm-key-function": "Fonction de clé d'alarme", + "latest-keys": "Dernières clés de données", + "latest-key": "Dernière clé de données", + "latest-key-functions": "Fonctions de dernière clé", + "latest-key-function": "Fonction de la dernière clé", + "timeseries-keys": "Clés de séries temporelles", + "timeseries-key": "Clé de série temporelle", + "timeseries-key-functions": "Fonctions de clé de série temporelle", + "timeseries-key-function": "Fonction de clé de série temporelle", + "maximum-function-types": "Maximum { count, plural, =1 {1 type de fonction autorisé.} other {# types de fonctions autorisés} }", + "time-description": "horodatage de la valeur actuelle ;", + "value-description": "la valeur actuelle ;", + "prev-value-description": "résultat de l'appel précédent de la fonction ;", + "time-prev-description": "horodatage de la valeur précédente ;", + "prev-orig-value-description": "valeur d'origine précédente ;", + "aggregation": "Agrégation", + "aggregation-type-hint-common": "Pour des raisons de performance, le calcul des valeurs agrégées est disponible uniquement pour des intervalles de temps fixes comme « jour actuel », « mois actuel », etc., et n'est pas disponible pour des fenêtres glissantes comme « 30 dernières minutes » ou « dernières 24 heures ».", + "aggregation-type-none-hint": "Prendre la dernière valeur.", + "aggregation-type-min-hint": "Trouver la valeur minimale parmi les points de données dans la fenêtre temporelle sélectionnée.", + "aggregation-type-max-hint": "Trouver la valeur maximale parmi les points de données dans la fenêtre temporelle sélectionnée.", + "aggregation-type-avg-hint": "Calculer la moyenne des valeurs dans la fenêtre temporelle sélectionnée.", + "aggregation-type-sum-hint": "Additionner toutes les valeurs dans la fenêtre temporelle sélectionnée.", + "aggregation-type-count-hint": "Nombre total de points de données dans la fenêtre temporelle sélectionnée.", + "delta-calculation": "Calcul du delta", + "enable-delta-calculation": "Activer le calcul du delta", + "enable-delta-calculation-hint": "Lorsqu'il est activé, la valeur de la clé de données est calculée sur la base des valeurs agrégées pour une fenêtre temporelle sélectionnée et une période de comparaison spécifiée. Pour des raisons de performance, le calcul du delta est disponible uniquement pour des fenêtres temporelles historiques et non pour des valeurs en temps réel. Par exemple, vous pouvez calculer le delta entre la consommation d'énergie d'hier et celle de l'avant-hier.", + "delta-calculation-result": "Résultat du calcul du delta", + "delta-calculation-result-previous-value": "Valeur précédente", + "delta-calculation-result-delta-absolute": "Delta (absolu)", + "delta-calculation-result-delta-percent": "Delta (pourcentage)", + "source": "Source", + "latest": "Dernier", + "latest-value": "Dernière valeur", + "delta": "delta", + "percent": "pourcentage", + "absolute": "absolu" + }, + "datasource": { + "type": "Type de source de données", + "name": "Nom", + "label": "Étiquette", + "add-datasource-prompt": "Veuillez ajouter une source de données" + }, + "details": { + "details": "Détails", + "edit-mode": "Mode édition", + "edit-json": "Éditer JSON", + "toggle-edit-mode": "Basculer le mode édition" + }, + "device": { + "device": "Appareil", + "device-required": "L'appareil est requis.", + "devices": "Appareils", + "management": "Gestion des appareils", + "view-devices": "Voir les appareils", + "device-alias": "Alias de l'appareil", + "device-type-max-length": "Le type d'appareil doit comporter moins de 256 caractères", + "aliases": "Alias d'appareils", + "no-alias-matching": "'{{alias}}' introuvable.", + "no-aliases-found": "Aucun alias trouvé.", + "no-key-matching": "'{{key}}' introuvable.", + "no-keys-found": "Aucune clé trouvée.", + "create-new-alias": "Créez-en un nouveau !", + "create-new-key": "Créez-en un nouveau !", + "duplicate-alias-error": "Alias en double trouvé '{{alias}}'.
Les alias d'appareil doivent être uniques dans le tableau de bord.", + "configure-alias": "Configurer l'alias '{{alias}}'", + "no-devices-matching": "Aucun appareil correspondant à '{{entity}}' trouvé.", + "alias": "Alias", + "alias-required": "L'alias de l'appareil est requis.", + "remove-alias": "Supprimer l'alias de l'appareil", + "add-alias": "Ajouter un alias d'appareil", + "name-starts-with": "Expression du nom de l'appareil", + "help-text": "Utilisez '%' selon le besoin : '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list": "Liste des appareils", + "use-device-name-filter": "Utiliser un filtre", + "device-list-empty": "Aucun appareil sélectionné.", + "device-name-filter-required": "Le filtre de nom d'appareil est requis.", + "device-name-filter-no-device-matched": "Aucun appareil commençant par '{{device}}' trouvé.", + "add": "Ajouter un appareil", + "assign-to-customer": "Attribuer au client", + "assign-device-to-customer": "Attribuer le(s) appareil(s) au client", + "assign-device-to-customer-text": "Veuillez sélectionner les appareils à attribuer au client", + "make-public": "Rendre public l'appareil", + "make-private": "Rendre privé l'appareil", + "no-devices-text": "Aucun appareil trouvé", + "assign-to-customer-text": "Veuillez sélectionner le client à qui attribuer le(s) appareil(s)", + "device-details": "Détails de l'appareil", + "add-device-text": "Ajouter un nouvel appareil", + "credentials": "Identifiants", + "manage-credentials": "Gérer les identifiants", + "delete": "Supprimer l'appareil", + "assign-devices": "Attribuer les appareils", + "assign-devices-text": "Attribuer { count, plural, =1 {1 appareil} other {# appareils} } au client", + "delete-devices": "Supprimer les appareils", + "unassign-from-customer": "Désattribuer du client", + "unassign-devices": "Désattribuer les appareils", + "unassign-devices-action-title": "Désattribuer { count, plural, =1 {1 appareil} other {# appareils} } du client", + "unassign-device-from-edge-title": "Êtes-vous sûr de vouloir désattribuer l'appareil '{{deviceName}}' ?", + "unassign-device-from-edge-text": "Après confirmation, l'appareil sera désattribué et ne sera plus accessible par l'edge.", + "unassign-devices-from-edge": "Désattribuer les appareils de l'edge", + "assign-new-device": "Attribuer un nouvel appareil", + "make-public-device-title": "Êtes-vous sûr de vouloir rendre l'appareil '{{deviceName}}' public ?", + "make-public-device-text": "Après confirmation, l'appareil et toutes ses données seront rendus publics et accessibles par d'autres.", + "make-private-device-title": "Êtes-vous sûr de vouloir rendre l'appareil '{{deviceName}}' privé ?", + "make-private-device-text": "Après confirmation, l'appareil et toutes ses données seront rendus privés et ne seront plus accessibles par d'autres.", + "view-credentials": "Voir les identifiants", + "delete-device-title": "Êtes-vous sûr de vouloir supprimer l'appareil '{{deviceName}}' ?", + "delete-device-text": "Attention, après confirmation, l'appareil et toutes les données associées seront irrécupérables.", + "delete-devices-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 appareil} other {# appareils} } ?", + "delete-devices-action-title": "Supprimer { count, plural, =1 {1 appareil} other {# appareils} }", + "delete-devices-text": "Attention, après confirmation, tous les appareils sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "unassign-device-title": "Êtes-vous sûr de vouloir désattribuer l'appareil '{{deviceName}}' ?", + "unassign-device-text": "Après confirmation, l'appareil sera désattribué et ne sera plus accessible par le client.", + "unassign-device": "Désattribuer l'appareil", + "unassign-devices-title": "Êtes-vous sûr de vouloir désattribuer { count, plural, =1 {1 appareil} other {# appareils} } ?", + "unassign-devices-text": "Après confirmation, tous les appareils sélectionnés seront désattribués et ne seront plus accessibles par le client.", + "device-credentials": "Identifiants de l'appareil", + "loading-device-credentials": "Chargement des identifiants de l'appareil...", + "credentials-type": "Type d'identifiants", + "access-token": "Jeton d'accès", + "access-token-required": "Le jeton d'accès est requis.", + "access-token-invalid": "La longueur du jeton d'accès doit être comprise entre 1 et 32 caractères.", + "certificate-pem-format": "Certificat au format PEM", + "certificate-pem-format-required": "Le certificat est requis.", + "copy-access-token": "Copier le jeton d'accès", + "copy-certificate": "Copier le certificat", + "copy-client-id": "Copier l'ID client", + "copy-user-name": "Copier le nom d'utilisateur", + "copy-password": "Copier le mot de passe", + "generate-client-id": "Générer l'ID client", + "generate-user-name": "Générer le nom d'utilisateur", + "generate-password": "Générer le mot de passe", + "generate-access-token": "Générer un jeton d'accès", + "lwm2m-security-config": { + "identity": "Identité du client", + "identity-required": "L'identité du client est requise.", + "identity-tooltip": "L'identifiant PSK est un identifiant arbitraire pouvant aller jusqu'à 128 octets, tel que décrit dans la norme [RFC7925].\nL'identifiant PSK DOIT d'abord être converti en chaîne de caractères puis encodé en octets à l'aide de l'encodage UTF-8.", + "client-key": "Clé du client", + "client-key-required": "La clé du client est requise.", + "client-key-tooltip-prk": "La clé publique RPK ou l'identifiant doit être conforme à la norme [RFC7250] et encodée en Base64 !", + "client-key-tooltip-psk": "La clé PSK doit être conforme à la norme [RFC4279] et au format HexDec : 32, 64, 128 caractères !", + "endpoint": "Nom du client Endpoint", + "endpoint-required": "Le nom du client Endpoint est requis.", + "client-public-key": "Clé publique du client", + "client-public-key-hint": "Si la clé publique du client est vide, le certificat de confiance sera utilisé", + "client-public-key-tooltip": "La clé publique X509 doit être au format DER-encodé X509v3, uniquement avec l’algorithme EC, et encodée ensuite en Base64 !", + "mode": "Mode de configuration de sécurité", + "client-tab": "Configuration de sécurité du client", + "client-certificate": "Certificat du client", + "bootstrap-tab": "Client Bootstrap", + "bootstrap-server": "Serveur Bootstrap", + "lwm2m-server": "Serveur LwM2M", + "client-publicKey-or-id": "Clé publique ou identifiant du client", + "client-publicKey-or-id-required": "Clé publique ou identifiant du client est requis.", + "client-publicKey-or-id-tooltip-psk": "L'identifiant PSK est un identifiant arbitraire jusqu'à 128 octets, selon la norme [RFC7925].\nIl DOIT être d'abord converti en chaîne puis encodé en UTF-8.", + "client-publicKey-or-id-tooltip-rpk": "La clé publique ou l'identifiant RPK doit respecter la norme [RFC7250] et être encodé en Base64 !", + "client-publicKey-or-id-tooltip-x509": "La clé publique X509 doit être au format DER-encodé X509v3, supportant exclusivement l’algorithme EC, et encodée en Base64.", + "client-secret-key": "Clé secrète du client", + "client-secret-key-required": "La clé secrète du client est requise.", + "client-secret-key-tooltip-psk": "La clé PSK doit respecter la norme [RFC4279] et être au format HexDec : 32, 64, 128 caractères !", + "client-secret-key-tooltip-prk": "La clé secrète RPK doit être au format PKCS_8 (encodage DER, norme [RFC5958]) puis encodée en Base64 !", + "client-secret-key-tooltip-x509": "La clé secrète X509 doit être au format PKCS_8 (encodage DER, norme [RFC5958]) puis encodée en Base64 !" + }, + "client-id": "ID du client", + "client-id-pattern": "Contient un caractère non valide.", + "user-name": "Nom d'utilisateur", + "user-name-required": "Le nom d'utilisateur est requis.", + "client-id-or-user-name-necessary": "L'ID du client et/ou le nom d'utilisateur sont nécessaires", + "password": "Mot de passe", + "secret": "Secret", + "secret-required": "Le secret est requis.", + "device-type": "Profil de l'appareil", + "device-type-required": "Le type d'appareil est requis.", + "select-device-type": "Sélectionner le type d'appareil", + "enter-device-type": "Saisir le profil de l'appareil", + "any-device": "N'importe quel appareil", + "no-device-types-matching": "Aucun profil d'appareil correspondant à '{{entitySubtype}}' n'a été trouvé.", + "device-type-list-empty": "Aucun profil d'appareil sélectionné !", + "device-profile-type-list-empty": "Au moins un profil d'appareil doit être sélectionné.", + "device-types": "Types d'appareils", + "name": "Nom", + "name-required": "Le nom est requis.", + "name-max-length": "Le nom doit comporter moins de 256 caractères", + "label-max-length": "L'étiquette doit comporter moins de 256 caractères", + "description": "Description", + "label": "Étiquette", + "events": "Événements", + "details": "Détails", + "copyId": "Copier l'ID de l'appareil", + "copyAccessToken": "Copier le jeton d'accès", + "copy-mqtt-authentication": "Copier les identifiants MQTT", + "idCopiedMessage": "L'ID de l'appareil a été copié dans le presse-papiers", + "accessTokenCopiedMessage": "Le jeton d'accès de l'appareil a été copié dans le presse-papiers", + "mqtt-authentication-copied-message": "Les identifiants MQTT de l'appareil ont été copiés dans le presse-papiers", + "assignedToCustomer": "Attribué au client", + "unable-delete-device-alias-title": "Impossible de supprimer l'alias de l'appareil", + "unable-delete-device-alias-text": "L'alias de l'appareil '{{deviceAlias}}' ne peut pas être supprimé car il est utilisé par le(s) widget(s) suivant(s) :
{{widgetsList}}", + "is-gateway": "Est une passerelle", + "overwrite-activity-time": "Écraser l'heure d'activité de l'appareil connecté", + "device-filter": "Filtre d'appareil", + "device-filter-title": "Filtre d'appareil", + "filter-title": "Filtre", + "device-state": "État de l'appareil", + "state": "État", + "any": "N'importe quel", + "active": "Actif", + "inactive": "Inactif", + "public": "Public", + "device-public": "L'appareil est public", + "select-device": "Sélectionner l'appareil", + "import": "Importer l'appareil", + "device-file": "Fichier de l'appareil", + "search": "Rechercher des appareils", + "selected-devices": "{ count, plural, =1 {1 appareil} other {# appareils} } sélectionné(s)", + "device-configuration": "Configuration de l'appareil", + "transport-configuration": "Configuration de transport", + "wizard": { + "device-details": "Détails de l'appareil" + }, + "unassign-devices-from-edge-title": "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 appareil} other {# appareils} } ?", + "unassign-devices-from-edge-text": "Après confirmation, tous les appareils sélectionnés seront désassignés et ne seront plus accessibles par l’edge.", + "time": "Temps", + "connectivity": { + "check-connectivity": "Vérifier la connectivité", + "device-created-check-connectivity": "Appareil créé. Vérifions sa connectivité !", + "loading-check-connectivity-command": "Chargement des commandes de vérification de connectivité...", + "use-following-instructions": "Utilisez les instructions suivantes pour envoyer des télémesures au nom de l'appareil via le terminal", + "execute-following-command": "Exécutez la commande suivante", + "install-curl-windows": "Depuis Windows 10 version b17063, cURL est disponible par défaut", + "install-curl-macos": "Depuis Mac OS X 10.2 6C115 (Jaguar), cURL est disponible par défaut", + "install-mqtt-windows": "Utilisez les instructions pour télécharger, installer, configurer et exécuter mosquitto_pub", + "install-coap-client": "Utilisez les instructions pour télécharger, installer, configurer et exécuter coap-client", + "install-necessary-client-tools": "Installer les outils clients nécessaires", + "mqtts-x509-command": "Utilisez la documentation suivante pour connecter l'appareil via MQTT avec authentification X509", + "coaps-x509-command": "Utilisez la documentation suivante pour connecter l'appareil via CoAP sur DTLS avec authentification X509", + "snmp-command": "Utilisez la documentation suivante pour connecter l'appareil via SNMP.", + "sparkplug-command": "Utilisez la documentation suivante pour connecter l'appareil via MQTT Sparkplug.", + "lwm2m-command": "Utilisez la documentation suivante pour connecter l'appareil via LWM2M." + } + }, + "dynamic-form": { + "property": { + "properties": "Propriétés", + "property": "Propriété", + "id": "Identifiant", + "name": "Nom", + "type": "Type", + "type-text": "Texte", + "type-password": "Mot de passe", + "type-textarea": "Zone de texte", + "type-number": "Nombre", + "type-switch": "Interrupteur", + "type-select": "Sélection", + "type-radios": "Boutons radio", + "type-datetime": "Date/Heure", + "type-image": "Image", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "Couleur", + "type-color-settings": "Paramètres de couleur", + "type-font": "Police", + "type-units": "Unités", + "type-icon": "Icône", + "type-fieldset": "Ensemble de champs", + "type-array": "Tableau", + "type-html-section": "Section HTML", + "group-title": "Titre du groupe", + "no-properties": "Aucune propriété configurée", + "add-property": "Ajouter une propriété", + "property-settings": "Paramètres de la propriété", + "remove-property": "Supprimer la propriété", + "default-value": "Valeur par défaut", + "value-required": "Valeur requise", + "number-settings": "Paramètres numériques", + "min": "Min", + "max": "Max", + "step": "Pas", + "selected-options-limit": "Limite d'options sélectionnées", + "advanced-ui-settings": "Paramètres UI avancés", + "disable-on-property": "Désactiver en fonction de la propriété", + "display-condition-function": "Fonction de condition d'affichage", + "sub-label": "Sous-étiquette", + "vertical-divider-after": "Séparateur vertical après", + "input-field-suffix": "Suffixe du champ de saisie", + "property-row-classes": "Classes de ligne de propriété", + "property-field-classes": "Classes de champ de propriété", + "not-unique-property-ids-error": "Les identifiants des propriétés doivent être uniques !", + "enable-multiple-select": "Activer la sélection multiple", + "allow-empty-select-option": "Autoriser l'option vide", + "select-options": "Options de sélection", + "not-unique-select-option-value-error": "Les valeurs des options doivent être uniques !", + "value": "Valeur", + "label": "Étiquette", + "add-option": "Ajouter une option", + "no-options": "Aucune option configurée", + "remove-option": "Supprimer l'option", + "textarea-rows": "Lignes de la zone de texte", + "help-id": "Identifiant d'aide", + "buttons-direction": "Direction des boutons", + "direction-row": "Ligne", + "direction-column": "Colonne", + "radio-button-options": "Options des boutons radio", + "datetime-type": "Type de champ Date/Heure", + "datetime-type-date": "Date", + "datetime-type-time": "Heure", + "datetime-type-datetime": "Date/Heure", + "enable-clear-button": "Activer le bouton de réinitialisation", + "html-section-settings": "Paramètres de section HTML", + "html-section-classes": "Classes de section HTML", + "html-section-content": "Contenu de la section HTML", + "array-item": "Élément du tableau", + "item-type": "Type d'élément", + "item-name": "Nom de l'élément", + "no-items": "Aucun élément" + }, + "clear-form": "Effacer le formulaire", + "clear-form-prompt": "Êtes-vous sûr de vouloir supprimer toutes les propriétés du formulaire ?", + "import-form": "Importer un formulaire depuis JSON", + "export-form": "Exporter le formulaire en JSON", + "json-file": "Fichier JSON", + "json-content": "Contenu JSON", + "invalid-form-json-file-error": "Impossible d'importer le formulaire depuis JSON : Structure de données JSON invalide." + }, + "asset-profile": { + "asset-profile": "Profil d'actif", + "asset-profiles": "Profils d'actifs", + "all-asset-profiles": "Tous", + "add": "Ajouter un profil d'actif", + "edit": "Modifier le profil d'actif", + "asset-profile-details": "Détails du profil d'actif", + "no-asset-profiles-text": "Aucun profil d'actif trouvé", + "search": "Rechercher des profils d'actifs", + "selected-asset-profiles": "{ count, plural, =1 {1 profil d'actif} other {# profils d'actifs} } sélectionné(s)", + "no-asset-profiles-matching": "Aucun profil d'actif correspondant à '{{entity}}' trouvé.", + "asset-profile-required": "Le profil d'actif est requis", + "idCopiedMessage": "L'identifiant du profil d'actif a été copié dans le presse-papiers", + "set-default": "Définir comme profil d'actif par défaut", + "delete": "Supprimer le profil d'actif", + "copyId": "Copier l'identifiant du profil d'actif", + "name-max-length": "Le nom doit contenir moins de 256 caractères", + "new-device-profile-name": "Nom du profil d'actif", + "new-device-profile-name-required": "Le nom du profil d'actif est requis.", + "name": "Nom", + "name-required": "Le nom est requis.", + "image": "Image du profil d'actif", + "description": "Description", + "default": "Par défaut", + "default-rule-chain": "Chaîne de règles par défaut", + "default-edge-rule-chain": "Chaîne de règles Edge par défaut", + "default-edge-rule-chain-hint": "Utilisé sur l'Edge comme chaîne de règles pour traiter les données entrantes des actifs de ce profil", + "mobile-dashboard": "Tableau de bord mobile", + "mobile-dashboard-hint": "Utilisé par l'application mobile comme tableau de bord des détails de l'actif", + "select-queue-hint": "Sélectionnez dans une liste déroulante.", + "delete-asset-profile-title": "Êtes-vous sûr de vouloir supprimer le profil d'actif '{{assetProfileName}}' ?", + "delete-asset-profile-text": "Attention, après confirmation, le profil d'actif et toutes les données associées seront irrécupérables.", + "delete-asset-profiles-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 profil d'actif} other {# profils d'actifs} } ?", + "delete-asset-profiles-text": "Attention, après confirmation, tous les profils d'actifs sélectionnés seront supprimés ainsi que toutes les données associées.", + "set-default-asset-profile-title": "Êtes-vous sûr de vouloir définir le profil d'actif '{{assetProfileName}}' comme par défaut ?", + "set-default-asset-profile-text": "Après confirmation, ce profil d'actif sera marqué comme par défaut et utilisé pour les nouveaux actifs sans profil spécifié.", + "no-asset-profiles-found": "Aucun profil d'actif trouvé.", + "create-new-asset-profile": "Créer un nouveau !", + "create-asset-profile": "Créer un nouveau profil d'actif", + "import": "Importer un profil d'actif", + "export": "Exporter le profil d'actif", + "export-failed-error": "Impossible d'exporter le profil d'actif : {{error}}", + "asset-profile-file": "Fichier de profil d'actif", + "invalid-asset-profile-file-error": "Impossible d'importer le profil d'actif : structure de données invalide." + }, + "device-profile": { + "device-profile": "Profil de dispositif", + "device-profiles": "Profils de dispositifs", + "all-device-profiles": "Tous", + "add": "Ajouter un profil de dispositif", + "edit": "Modifier le profil de dispositif", + "device-profile-details": "Détails du profil de dispositif", + "no-device-profiles-text": "Aucun profil de dispositif trouvé", + "search": "Rechercher des profils de dispositifs", + "selected-device-profiles": "{ count, plural, =1 {1 profil de dispositif} other {# profils de dispositifs} } sélectionné(s)", + "no-device-profiles-matching": "Aucun profil de dispositif correspondant à '{{entity}}' trouvé.", + "device-profile-required": "Le profil de dispositif est requis", + "idCopiedMessage": "L'identifiant du profil de dispositif a été copié dans le presse-papiers", + "set-default": "Définir comme profil de dispositif par défaut", + "delete": "Supprimer le profil de dispositif", + "copyId": "Copier l'identifiant du profil de dispositif", + "name-max-length": "Le nom doit contenir moins de 256 caractères", + "name": "Nom", + "name-required": "Le nom est requis.", + "type": "Type de profil", + "type-required": "Le type de profil est requis.", + "type-default": "Par défaut", + "image": "Image du profil de dispositif", + "transport-type": "Type de transport", + "transport-type-required": "Le type de transport est requis.", + "transport-type-default": "Par défaut", + "transport-type-default-hint": "Prend en charge les transports MQTT, HTTP et CoAP basiques", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "Active les paramètres avancés de transport MQTT", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "Active les paramètres avancés de transport CoAP", + "transport-type-lwm2m": "LWM2M", + "transport-type-lwm2m-hint": "Type de transport LWM2M", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "Spécifiez la configuration du transport SNMP", + "transport-type-http": "HTTP", + "description": "Description", + "default": "Par défaut", + "profile-configuration": "Configuration du profil", + "transport-configuration": "Configuration du transport", + "default-rule-chain": "Chaîne de règles par défaut", + "default-edge-rule-chain": "Chaîne de règles Edge par défaut", + "default-edge-rule-chain-hint": "Utilisé sur Edge comme chaîne de règles pour traiter les données entrantes des dispositifs de ce profil", + "mobile-dashboard": "Tableau de bord mobile", + "mobile-dashboard-hint": "Utilisé par l'application mobile comme tableau de bord des détails du dispositif", + "select-queue-hint": "Sélectionnez dans une liste déroulante.", + "delete-device-profile-title": "Êtes-vous sûr de vouloir supprimer le profil de dispositif '{{deviceProfileName}}' ?", + "delete-device-profile-text": "Attention, après confirmation, le profil de dispositif et toutes les données associées, y compris les mises à jour OTA, seront irrécupérables.", + "delete-device-profiles-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 profil de dispositif} other {# profils de dispositifs} } ?", + "delete-device-profiles-text": "Attention, après confirmation, tous les profils de dispositifs sélectionnés seront supprimés ainsi que toutes les données associées, y compris les mises à jour OTA.", + "set-default-device-profile-title": "Êtes-vous sûr de vouloir définir le profil de dispositif '{{deviceProfileName}}' comme par défaut ?", + "set-default-device-profile-text": "Après confirmation, ce profil de dispositif sera marqué comme par défaut et utilisé pour les nouveaux dispositifs sans profil spécifié.", + "no-device-profiles-found": "Aucun profil de dispositif trouvé.", + "create-new-device-profile": "Créer un nouveau !", + "mqtt-device-topic-filters": "Filtres de sujet MQTT du dispositif", + "mqtt-device-topic-filters-unique": "Les filtres de sujet MQTT doivent être uniques.", + "mqtt-device-topic-filters-spark-plug": "Nœud EoN MQTT Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-hint": "Permet les connexions de nœuds EoN avec format de charge utile et de sujet Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "Métriques SparkPlug à stocker comme attributs.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Noms des métriques SparkPlug qui seront stockées comme attributs de l'appareil. Toutes les autres seront stockées comme télémétrie.", + "mqtt-device-payload-type": "Charge utile MQTT du dispositif", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Activer la compatibilité avec d'autres formats de charge utile.", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Lorsque cette option est activée, la plateforme utilisera le format Protobuf par défaut. En cas d'échec de l'analyse, elle utilisera le format JSON. Utile pour les mises à jour de firmware. À désactiver une fois tous les dispositifs mis à jour.", + "mqtt-use-json-format-for-default-downlink-topics": "Utiliser le format JSON pour les sujets descendants par défaut", + "mqtt-use-json-format-for-default-downlink-topics-hint": "Utilise JSON pour les sujets : v1/devices/me/attributes/response/$request_id, etc. Ne s'applique pas aux sujets v2.", + "mqtt-send-ack-on-validation-exception": "Envoyer PUBACK en cas d'échec de validation", + "mqtt-send-ack-on-validation-exception-hint": "Par défaut, la session MQTT est fermée sur erreur. Si activé, envoie un accusé de réception PUBACK à la place.", + "snmp-add-mapping": "Ajouter un mappage SNMP", + "snmp-mapping-not-configured": "Aucun mappage OID vers série temporelle ou attribut configuré", + "snmp-timseries-or-attribute-name": "Nom de série temporelle/attribut pour le mappage", + "snmp-timseries-or-attribute-type": "Type de série temporelle/attribut pour le mappage", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "Le type de charge utile est requis.", + "coap-device-type": "Type d'appareil CoAP", + "coap-device-payload-type": "Charge utile de l'appareil CoAP", + "coap-device-type-required": "Le type d'appareil CoAP est requis.", + "coap-device-type-default": "Par défaut", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "Jokers à un niveau [+] et à plusieurs niveaux [#] pris en charge.", + "telemetry-topic-filter": "Filtre de sujet de télémétrie", + "telemetry-topic-filter-required": "Le filtre de sujet de télémétrie est requis.", + "attributes-topic-filter": "Filtre de publication des attributs", + "attributes-subscribe-topic-filter": "Filtre d’abonnement des attributs", + "attributes-topic-filter-required": "Le filtre de publication des attributs est requis.", + "attributes-subscribe-topic-filter-required": "Le filtre d’abonnement des attributs est requis.", + "telemetry-proto-schema": "Schéma proto de télémétrie", + "telemetry-proto-schema-required": "Le schéma proto de télémétrie est requis.", + "attributes-proto-schema": "Schéma proto des attributs", + "attributes-proto-schema-required": "Le schéma proto des attributs est requis.", + "rpc-response-proto-schema": "Schéma proto de réponse RPC", + "rpc-response-proto-schema-required": "Le schéma proto de réponse RPC est requis.", + "rpc-response-topic-filter": "Filtre de sujet de réponse RPC", + "rpc-response-topic-filter-required": "Le filtre de sujet de réponse RPC est requis.", + "rpc-request-proto-schema": "Schéma proto de requête RPC", + "rpc-request-proto-schema-required": "Le schéma proto de requête RPC est requis.", + "rpc-request-proto-schema-hint": "Le message de requête RPC doit toujours contenir les champs : string method = 1; int32 requestId = 2; et params = 3 de n'importe quel type.", + "not-valid-pattern-topic-filter": "Filtre de sujet avec motif non valide", + "not-valid-single-character": "Utilisation invalide du caractère joker à un seul niveau", + "not-valid-multi-character": "Utilisation invalide du caractère joker à plusieurs niveaux", + "single-level-wildcards-hint": "[+] convient à tout niveau de filtre de sujet. Ex. : v1/devices/+/telemetry ou +/devices/+/attributes.", + "multi-level-wildcards-hint": "[#] peut remplacer le filtre de sujet lui-même et doit être le dernier symbole du sujet. Ex. : # ou v1/devices/me/#.", + "alarm-rules": "Règles d'alarme", + "alarm-rules-with-count": "Règles d'alarme ({{count}})", + "no-alarm-rules": "Aucune règle d'alarme configurée", + "add-alarm-rule": "Ajouter une règle d'alarme", + "edit-alarm-rule": "Modifier la règle d'alarme", + "alarm-type": "Type d'alarme", + "alarm-type-required": "Le type d'alarme est requis.", + "alarm-type-unique": "Le type d'alarme doit être unique dans les règles d'alarme du profil.", + "alarm-type-max-length": "Le type d'alarme doit comporter moins de 256 caractères", + "create-alarm-pattern": "Créer une alarme {{alarmType}}", + "create-alarm-rules": "Créer des règles d'alarme", + "no-create-alarm-rules": "Aucune condition de création configurée", + "add-create-alarm-rule-prompt": "Veuillez ajouter une règle de création d'alarme", + "clear-alarm-rule": "Effacer la règle d'alarme", + "no-clear-alarm-rule": "Aucune condition d'effacement configurée", + "add-create-alarm-rule": "Ajouter une condition de création", + "add-clear-alarm-rule": "Ajouter une condition d'effacement", + "select-alarm-severity": "Sélectionner la gravité de l'alarme", + "alarm-severity-required": "La gravité de l'alarme est requise.", + "condition-duration": "Durée de la condition", + "condition-duration-value": "Valeur de la durée", + "condition-duration-time-unit": "Unité de temps", + "condition-duration-value-range": "La durée doit être comprise entre 1 et 2147483647.", + "condition-duration-value-pattern": "La durée doit être un entier.", + "condition-duration-value-required": "La valeur de la durée est requise.", + "condition-duration-time-unit-required": "L'unité de temps est requise.", + "advanced-settings": "Paramètres avancés", + "alarm-rule-additional-info": "Informations supplémentaires", + "edit-alarm-rule-additional-info": "Modifier les informations supplémentaires", + "alarm-rule-additional-info-placeholder": "Ajoutez ici vos commentaires ou ajustements à afficher dans les détails de l'alarme", + "alarm-rule-additional-info-hint": "Astuce : utilisez code>${keyName} pour insérer les valeurs des clés utilisées dans la condition.", + "alarm-rule-mobile-dashboard": "Tableau de bord mobile", + "alarm-rule-mobile-dashboard-hint": "Utilisé comme tableau de bord des détails de l'alarme dans l'application mobile", + "alarm-rule-no-mobile-dashboard": "Aucun tableau de bord sélectionné", + "propagate-alarm": "Propager l'alarme aux entités liées", + "alarm-rule-relation-types-list": "Types de relations", + "alarm-rule-relation-types-list-hint": "Définit les types de relations pour filtrer les entités liées. Si non défini, l'alarme sera propagée à toutes les entités liées.", + "propagate-alarm-to-owner": "Propager l'alarme au propriétaire de l'entité (Client ou Locataire)", + "propagate-alarm-to-tenant": "Propager l'alarme au Locataire", + "alarm-rule-condition": "Condition de la règle d'alarme", + "enter-alarm-rule-condition-prompt": "Veuillez ajouter une condition de règle d'alarme", + "edit-alarm-rule-condition": "Modifier la condition de la règle d'alarme", + "device-provisioning": "Approvisionnement de l'appareil", + "provision-strategy": "Stratégie d'approvisionnement", + "provision-strategy-required": "La stratégie d'approvisionnement est requise.", + "provision-strategy-disabled": "Désactivée", + "provision-strategy-created-new": "Permettre de créer de nouveaux appareils", + "provision-strategy-check-pre-provisioned": "Vérifier les appareils préapprovisionnés", + "provision-device-key": "Clé d'approvisionnement de l'appareil", + "provision-device-key-required": "La clé d'approvisionnement de l'appareil est requise.", + "copy-provision-key": "Copier la clé d'approvisionnement", + "provision-key-copied-message": "La clé d'approvisionnement a été copiée dans le presse-papiers", + "provision-device-secret": "Secret d'approvisionnement de l'appareil", + "provision-device-secret-required": "Le secret d'approvisionnement de l'appareil est requis.", + "copy-provision-secret": "Copier le secret d'approvisionnement", + "provision-secret-copied-message": "Le secret d'approvisionnement a été copié dans le presse-papiers", + "provision-strategy-x509": { + "certificate-chain": "Chaîne de certificats X509", + "certificate-chain-hint": "La stratégie de certificats X.509 est utilisée pour approvisionner les appareils via les certificats client en communication TLS bidirectionnelle.", + "allow-create-new-devices": "Créer de nouveaux appareils", + "allow-create-new-devices-hint": "Si sélectionné, de nouveaux appareils seront créés et le certificat client sera utilisé comme identifiants de l'appareil.", + "certificate-value": "Certificat au format PEM", + "certificate-value-required": "Le certificat au format PEM est requis", + "cn-regex-variable": "Variable d'expression régulière CN", + "cn-regex-variable-required": "La variable d'expression régulière CN est requise", + "cn-regex-variable-hint": "Requis pour récupérer le nom de l'appareil depuis le nom commun du certificat X509 de l'appareil." + }, + "condition": "Condition", + "condition-type": "Type de condition", + "condition-type-simple": "Simple", + "condition-type-duration": "Durée", + "condition-during": "Pendant {{during}}", + "condition-during-dynamic": "Pendant \"{{ attribute }}\" ({{during}})", + "condition-type-repeating": "Répétition", + "condition-type-required": "Le type de condition est requis.", + "condition-repeating-value": "Nombre d'événements", + "condition-repeating-value-range": "Le nombre d'événements doit être compris entre 1 et 2147483647.", + "condition-repeating-value-pattern": "Le nombre d'événements doit être un entier.", + "condition-repeating-value-required": "Le nombre d'événements est requis.", + "condition-repeat-times": "Se répète { count, plural, =1 {1 fois} other {# fois} }", + "condition-repeat-times-dynamic": "Se répète \"{ attribute }\" ({ count, plural, =1 {1 fois} other {# fois} })", + "schedule-type": "Type de planification", + "schedule-type-required": "Le type de planification est requis.", + "schedule": "Planification", + "edit-schedule": "Modifier la planification de l'alarme", + "schedule-any-time": "Actif en tout temps", + "schedule-specific-time": "Actif à une heure spécifique", + "schedule-custom": "Personnalisé", + "schedule-day": { + "monday": "Lundi", + "tuesday": "Mardi", + "wednesday": "Mercredi", + "thursday": "Jeudi", + "friday": "Vendredi", + "saturday": "Samedi", + "sunday": "Dimanche" + }, + "schedule-days": "Jours", + "schedule-time": "Heure", + "schedule-time-from": "De", + "schedule-time-to": "À", + "schedule-days-of-week-required": "Au moins un jour de la semaine doit être sélectionné.", + "create-device-profile": "Créer un nouveau profil d'appareil", + "import": "Importer le profil d'appareil", + "export": "Exporter le profil d'appareil", + "export-failed-error": "Impossible d'exporter le profil d'appareil : {{error}}", + "device-profile-file": "Fichier de profil d'appareil", + "invalid-device-profile-file-error": "Impossible d'importer le profil d'appareil : structure de données invalide.", + "power-saving-mode": "Mode d'économie d'énergie", + "power-saving-mode-type": { + "default": "Utiliser le mode d'économie d'énergie du profil d'appareil", + "psm": "Mode d'économie d'énergie (PSM)", + "drx": "Réception discontinue (DRX)", + "edrx": "Réception discontinue étendue (eDRX)" + }, + "edrx-cycle": "Cycle eDRX", + "edrx-cycle-required": "Le cycle eDRX est requis.", + "edrx-cycle-pattern": "Le cycle eDRX doit être un entier positif.", + "edrx-cycle-min": "Le cycle eDRX minimum est de {{ min }} secondes.", + "paging-transmission-window": "Fenêtre de transmission de pagination", + "paging-transmission-window-required": "La fenêtre de transmission de pagination est requise.", + "paging-transmission-window-pattern": "La fenêtre de transmission doit être un entier positif.", + "paging-transmission-window-min": "Le minimum pour la fenêtre de transmission est de {{ min }} secondes.", + "psm-activity-timer": "Minuteur d'activité PSM", + "psm-activity-timer-required": "Le minuteur d'activité PSM est requis.", + "psm-activity-timer-pattern": "Le minuteur PSM doit être un entier positif.", + "psm-activity-timer-min": "Le minuteur PSM doit être au minimum de {{ min }} secondes.", + "lwm2m": { + "object-list": "Liste des objets", + "object-list-empty": "Aucun objet sélectionné.", + "no-objects-found": "Aucun objet trouvé.", + "no-objects-matching": "Aucun objet correspondant à '{{object}}' trouvé.", + "model-tab": "Modèle LWM2M", + "add-new-instances": "Ajouter de nouvelles instances", + "instances-list": "Liste des instances", + "instances-list-required": "La liste des instances est requise.", + "instance-id-pattern": "L'identifiant de l'instance doit être un entier positif.", + "instance-id-max": "La valeur maximale de l'identifiant de l'instance est {{max}}.", + "instance": "Instance", + "resource-label": "#ID Nom de la ressource", + "observe-label": "Observer", + "attribute-label": "Attribut", + "telemetry-label": "Télémétrie", + "edit-observe-select": "Pour modifier, sélectionnez télémétrie ou attribut", + "edit-attributes-select": "Pour modifier les attributs, sélectionnez télémétrie ou attribut", + "no-attributes-set": "Aucun attribut défini", + "key-name": "Nom de la clé", + "key-name-required": "Le nom de la clé est requis", + "attribute-name": "Nom de l'attribut", + "attribute-name-required": "Le nom de l'attribut est requis.", + "attribute-value": "Valeur de l'attribut", + "attribute-value-required": "La valeur de l'attribut est requise.", + "attribute-value-pattern": "La valeur doit être un entier positif.", + "edit-attributes": "Modifier les attributs : {{ name }}", + "view-attributes": "Voir les attributs : {{ name }}", + "add-attribute": "Ajouter un attribut", + "edit-attribute": "Modifier l'attribut", + "view-attribute": "Voir l'attribut", + "remove-attribute": "Supprimer l'attribut", + "delete-server-text": "Attention, après confirmation, la configuration du serveur sera irrécupérable.", + "delete-server-title": "Êtes-vous sûr de vouloir supprimer ce serveur ?", + "mode": "Mode de configuration de sécurité", + "bootstrap-tab": "Bootstrap", + "bootstrap-server-legend": "Serveur Bootstrap (ShortId...)", + "lwm2m-server-legend": "Serveur LwM2M (ShortId...)", + "server": "Serveur", + "short-id": "ID court du serveur", + "short-id-tooltip": "ID court du serveur. Utilisé pour lier une instance d’objet serveur.\nCet identifiant identifie de manière unique chaque serveur LwM2M configuré pour le client.\nLa ressource DOIT être définie si Bootstrap-Server est à 'false'.\nLes ID :0 et ID :65535 ne DOIVENT PAS être utilisés.", + "short-id-tooltip-bootstrap": "ID court du serveur. Utilisé pour lier une instance d’objet serveur.\nCet identifiant identifie de manière unique chaque serveur LwM2M configuré pour le client.\nLa ressource DOIT être définie si Bootstrap-Server est à 'false'.", + "short-id-required": "L’ID court du serveur est requis.", + "short-id-range": "L’ID court du serveur doit être compris entre {{ min }} et {{ max }}.", + "short-id-pattern": "L’ID court doit être un entier positif.", + "lifetime": "Durée d’enregistrement du client", + "lifetime-required": "La durée d’enregistrement du client est requise.", + "lifetime-pattern": "La durée d’enregistrement doit être un entier positif.", + "default-min-period": "Période minimale entre deux notifications (s)", + "default-min-period-tooltip": "Valeur par défaut que le client LwM2M doit utiliser comme période minimale d'observation en l'absence de paramètre.", + "default-min-period-required": "La période minimale est requise.", + "default-min-period-pattern": "La période minimale doit être un entier positif.", + "notification-storing": "Stockage des notifications en cas de désactivation ou hors ligne", + "binding": "Liaison", + "binding-type": { + "u": "U : Le client est joignable via la liaison UDP à tout moment.", + "m": "M : Le client est joignable via la liaison MQTT à tout moment.", + "h": "H : Le client est joignable via la liaison HTTP à tout moment.", + "t": "T : Le client est joignable via la liaison TCP à tout moment.", + "s": "S : Le client est joignable via la liaison SMS à tout moment.", + "n": "N : Le client DOIT envoyer la réponse via la liaison Non-IP (prise en charge depuis LWM2M 1.1).", + "uq": "UQ : Connexion UDP en mode file d'attente (non pris en charge depuis LWM2M 1.1)", + "uqs": "UQS : Connexions UDP et SMS actives ; UDP en mode file d'attente, SMS en mode standard (non pris en charge depuis LWM2M 1.1)", + "tq": "TQ : Connexion TCP en mode file d'attente (non pris en charge depuis LWM2M 1.1)", + "tqs": "TQS : Connexions TCP et SMS actives ; TCP en mode file d'attente, SMS en mode standard (non pris en charge depuis LWM2M 1.1)", + "sq": "SQ : Connexion SMS en mode file d'attente (non pris en charge depuis LWM2M 1.1)" + }, + "binding-tooltip": "Il s'agit de la liste dans la ressource \"binding\" de l'objet serveur LwM2M - /1/x/7.\nIndique les modes de liaison pris en charge par le client LwM2M.\nCette valeur DOIT être identique à celle de la ressource “Supported Binding and Modes” dans l’objet Device (/3/0/16).\nBien que plusieurs transports soient pris en charge, un seul peut être utilisé pendant toute la session de transport.\nPar exemple, si UDP et SMS sont disponibles, le client et le serveur peuvent communiquer uniquement via l’un des deux durant la session.", + "bootstrap-server": "Serveur Bootstrap", + "lwm2m-server": "Serveur LwM2M", + "include-bootstrap-server": "Inclure les mises à jour du serveur Bootstrap", + "bootstrap-update-title": "Un serveur Bootstrap est déjà configuré. Êtes-vous sûr de vouloir exclure les mises à jour ?", + "bootstrap-update-text": "Attention, après confirmation, la configuration du serveur Bootstrap sera irrécupérable.", + "server-host": "Hôte", + "server-host-required": "L'hôte est requis.", + "server-port": "Port", + "server-port-required": "Le port est requis.", + "server-port-pattern": "Le port doit être un entier positif.", + "server-port-range": "Le port doit être compris entre 1 et 65535.", + "server-public-key": "Clé publique du serveur", + "server-public-key-required": "La clé publique du serveur est requise.", + "client-hold-off-time": "Temps d'attente du client", + "client-hold-off-time-required": "Le temps d'attente du client est requis.", + "client-hold-off-time-pattern": "Le temps d'attente doit être un entier positif.", + "client-hold-off-time-tooltip": "Temps d'attente utilisé uniquement avec le serveur Bootstrap", + "account-after-timeout": "Connexion après expiration", + "account-after-timeout-required": "La connexion après expiration est requise.", + "account-after-timeout-pattern": "La valeur doit être un entier positif.", + "account-after-timeout-tooltip": "Valeur de délai pour l’inscription au serveur Bootstrap.", + "server-type": "Type de serveur", + "add-new-server-title": "Ajouter une nouvelle configuration de serveur", + "add-server-config": "Ajouter la configuration du serveur", + "add-lwm2m-server-config": "Ajouter un serveur LwM2M", + "no-config-servers": "Aucun serveur configuré", + "others-tab": "Autres paramètres", + "client-strategy": "Stratégie client lors de la connexion", + "client-strategy-label": "Stratégie", + "client-strategy-only-observe": "Envoyer uniquement la requête d’observation après connexion initiale", + "client-strategy-read-all": "Lire toutes les ressources & envoyer la requête d’observation après enregistrement", + "fw-update": "Mise à jour du firmware", + "fw-update-strategy": "Stratégie de mise à jour du firmware", + "fw-update-strategy-data": "Pousser le firmware en tant que fichier binaire via Objet 19 Ressource 0 (Data)", + "fw-update-strategy-package": "Pousser le firmware en tant que fichier binaire via Objet 5 Ressource 0 (Package)", + "fw-update-strategy-package-uri": "Générer une URL CoAP unique et pousser le firmware via Objet 5 Ressource 1 (Package URI)", + "sw-update": "Mise à jour du logiciel", + "sw-update-strategy": "Stratégie de mise à jour du logiciel", + "sw-update-strategy-package": "Pousser le fichier binaire via Objet 9 Ressource 2 (Package)", + "sw-update-strategy-package-uri": "Générer une URL CoAP unique et pousser la mise à jour via Objet 9 Ressource 3 (Package URI)", + "fw-update-resource": "Ressource CoAP pour mise à jour du firmware", + "fw-update-resource-required": "La ressource de mise à jour du firmware est requise.", + "sw-update-resource": "Ressource CoAP pour mise à jour du logiciel", + "sw-update-resource-required": "La ressource de mise à jour du logiciel est requise.", + "config-json-tab": "Configuration JSON du profil d’appareil", + "attributes-name": { + "min-period": "Période minimale", + "max-period": "Période maximale", + "greater-than": "Supérieur à", + "less-than": "Inférieur à", + "step": "Pas", + "min-evaluation-period": "Période d’évaluation minimale", + "max-evaluation-period": "Période d’évaluation maximale" + }, + "default-object-id": "Version par défaut de l'objet (Attribut)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1" + } + }, + "snmp": { + "add-communication-config": "Ajouter une configuration de communication", + "add-mapping": "Ajouter une correspondance", + "authentication-passphrase": "Phrase secrète d'authentification", + "authentication-passphrase-required": "La phrase secrète d'authentification est requise.", + "authentication-protocol": "Protocole d'authentification", + "authentication-protocol-required": "Le protocole d'authentification est requis.", + "communication-configs": "Configurations de communication", + "community": "Chaîne de communauté", + "community-required": "La chaîne de communauté est requise.", + "context-name": "Nom du contexte", + "data-key": "Clé de données", + "data-key-required": "La clé de données est requise.", + "data-type": "Type de données", + "data-type-required": "Le type de données est requis.", + "engine-id": "ID du moteur", + "host": "Hôte", + "host-required": "L'hôte est requis.", + "oid": "OID", + "oid-pattern": "Format OID invalide.", + "oid-required": "L'OID est requis.", + "please-add-communication-config": "Veuillez ajouter une configuration de communication", + "please-add-mapping-config": "Veuillez ajouter une configuration de correspondance", + "port": "Port", + "port-format": "Format de port invalide.", + "port-required": "Le port est requis.", + "privacy-passphrase": "Phrase secrète de confidentialité", + "privacy-passphrase-required": "La phrase secrète de confidentialité est requise.", + "privacy-protocol": "Protocole de confidentialité", + "privacy-protocol-required": "Le protocole de confidentialité est requis.", + "protocol-version": "Version du protocole", + "protocol-version-required": "La version du protocole est requise.", + "querying-frequency": "Fréquence de requête, ms", + "querying-frequency-invalid-format": "La fréquence de requête doit être un entier positif.", + "querying-frequency-required": "La fréquence de requête est requise.", + "retries": "Réessais", + "retries-invalid-format": "Les réessais doivent être un entier positif.", + "retries-required": "Les réessais sont requis.", + "scope": "Portée", + "scope-required": "La portée est requise.", + "security-name": "Nom de sécurité", + "security-name-required": "Le nom de sécurité est requis.", + "timeout-ms": "Délai d'expiration, ms", + "timeout-ms-invalid-format": "Le délai d'expiration doit être un entier positif.", + "timeout-ms-required": "Le délai d'expiration est requis.", + "user-name": "Nom d'utilisateur", + "user-name-required": "Le nom d'utilisateur est requis." + } + }, + "dialog": { + "close": "Fermer la boîte de dialogue", + "error-message-title": "Message d'erreur :", + "error-details-title": "Détails de l'erreur" + }, + "direction": { + "column": "Colonne", + "row": "Ligne" + }, + "edge": { + "edge": "Edge", + "edge-instances": "Instances Edge", + "instances": "Instances", + "edge-file": "Fichier Edge", + "name-max-length": "Le nom doit contenir moins de 256 caractères", + "label-max-length": "Le label doit contenir moins de 256 caractères", + "type-max-length": "Le type doit contenir moins de 256 caractères", + "management": "Gestion des Edge", + "no-edges-matching": "Aucun Edge correspondant à '{{entity}}' trouvé.", + "add": "Ajouter un Edge", + "no-edges-text": "Aucun Edge trouvé", + "edge-details": "Détails du Edge", + "add-edge-text": "Ajouter un nouveau Edge", + "delete": "Supprimer le Edge", + "delete-edge-title": "Êtes-vous sûr de vouloir supprimer le Edge '{{edgeName}}' ?", + "delete-edge-text": "Attention, après confirmation, le Edge et toutes les données associées seront irrécupérables.", + "delete-edges-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 Edge} other {# Edge} } ?", + "delete-edges-text": "Attention, après confirmation, tous les Edge sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "name": "Nom", + "name-starts-with": "Le nom du Edge commence par", + "name-required": "Le nom est requis.", + "description": "Description", + "details": "Détails", + "events": "Événements", + "copy-id": "Copier l'ID du Edge", + "id-copied-message": "L'ID du Edge a été copié dans le presse-papiers", + "sync": "Synchroniser Edge", + "edge-required": "Edge requis", + "edge-type": "Type de Edge", + "edge-type-required": "Le type de Edge est requis.", + "event-action": "Action d'événement", + "entity-id": "ID de l'entité", + "select-edge-type": "Sélectionner le type de Edge", + "assign-to-customer": "Assigner à un client", + "assign-to-customer-text": "Veuillez sélectionner le client à qui assigner le(s) Edge", + "assign-edge-to-customer": "Assigner le(s) Edge à un client", + "assign-edge-to-customer-text": "Veuillez sélectionner les Edge à assigner au client", + "assignedToCustomer": "Assigné à un client", + "edge-public": "Le Edge est public", + "assigned-to-customer": "Assigné à : {{customerTitle}}", + "unassign-from-customer": "Désassigner du client", + "unassign-edge-title": "Êtes-vous sûr de vouloir désassigner le Edge '{{edgeName}}' ?", + "unassign-edge-text": "Après confirmation, le Edge sera désassigné et ne sera plus accessible au client.", + "unassign-edges-title": "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 Edge} other {# Edge} } ?", + "unassign-edges-text": "Après confirmation, tous les Edge sélectionnés seront désassignés et ne seront plus accessibles au client.", + "make-public": "Rendre public", + "make-public-edge-title": "Êtes-vous sûr de vouloir rendre le Edge '{{edgeName}}' public ?", + "make-public-edge-text": "Après confirmation, le Edge et toutes ses données seront rendus publics et accessibles à tous.", + "make-private": "Rendre privé", + "public": "Public", + "make-private-edge-title": "Êtes-vous sûr de vouloir rendre le Edge '{{edgeName}}' privé ?", + "make-private-edge-text": "Après confirmation, le Edge et toutes ses données seront rendus privés et ne seront plus accessibles à d'autres.", + "import": "Importer un Edge", + "install-connect-instructions": "Instructions d'installation et de connexion", + "install-connect-instructions-edge-created": "Edge créé ! Consultez les instructions d'installation et de connexion", + "loading-edge-instructions": "Chargement des instructions pour Edge...", + "label": "Label", + "load-entity-error": "Échec du chargement des données. L'entité a été supprimée.", + "assign-new-edge": "Attribuer une nouvelle passerelle", + "unassign-from-edge": "Désattribuer de la passerelle", + "edge-key": "Clé de la passerelle", + "copy-edge-key": "Copier la clé de la passerelle", + "edge-key-copied-message": "La clé de la passerelle a été copiée dans le presse-papiers", + "edge-secret": "Secret de la passerelle", + "copy-edge-secret": "Copier le secret de la passerelle", + "edge-secret-copied-message": "Le secret de la passerelle a été copié dans le presse-papiers", + "manage-assets": "Gérer les actifs", + "manage-devices": "Gérer les appareils", + "manage-entity-views": "Gérer les vues d'entité", + "manage-dashboards": "Gérer les tableaux de bord", + "manage-rulechains": "Gérer les chaînes de règles", + "assets": "Actifs de la passerelle", + "devices": "Appareils de la passerelle", + "entity-views": "Vues d'entité de la passerelle", + "dashboard": "Tableau de bord de la passerelle", + "dashboards": "Tableaux de bord de la passerelle", + "rulechain-templates": "Modèles de chaînes de règles", + "edge-rulechain-templates": "Modèles de chaînes de règles pour passerelle", + "rulechains": "Chaînes de règles de la passerelle", + "search": "Rechercher des passerelles", + "selected-edges": "{ count, plural, =1 {1 passerelle} other {# passerelles} } sélectionnée(s)", + "any-edge": "Toute passerelle", + "no-edge-types-matching": "Aucun type de passerelle correspondant à '{{entitySubtype}}' n'a été trouvé.", + "edge-type-list-empty": "Aucun type de passerelle sélectionné.", + "edge-types": "Types de passerelles", + "enter-edge-type": "Saisir un type de passerelle", + "deployed": "Déployée", + "pending": "En attente", + "downlinks": "Liaisons descendantes", + "no-downlinks-prompt": "Aucune liaison descendante trouvée", + "sync-process-started-successfully": "Le processus de synchronisation a démarré avec succès !", + "missing-related-rule-chains-title": "La passerelle a des chaînes de règles liées manquantes", + "missing-related-rule-chains-text": "Les chaînes de règles attribuées à la passerelle utilisent des nœuds de règle qui transfèrent des messages vers des chaînes de règles non attribuées à cette passerelle.

Liste des chaînes de règles manquantes :
{{missingRuleChains}}", + "widget-datasource-error": "Ce widget ne prend en charge que les sources de données de type ENTITÉ PASSERELLE", + "upgrade-instructions": "Instructions de mise à jour", + "connected": "Connectée", + "disconnected": "Déconnectée" + }, + "edge-event": { + "type-dashboard": "Tableau de bord", + "type-asset": "Actif", + "type-device": "Appareil", + "type-device-profile": "Profil de l'appareil", + "type-asset-profile": "Profil de l'actif", + "type-entity-view": "Vue d'entité", + "type-alarm": "Alarme", + "type-rule-chain": "Chaîne de règles", + "type-rule-chain-metadata": "Métadonnées de chaîne de règles", + "type-edge": "Passerelle", + "type-user": "Utilisateur", + "type-tenant": "Locataire", + "type-tenant-profile": "Profil de locataire", + "type-customer": "Client", + "type-relation": "Relation", + "type-widgets-bundle": "Paquet de widgets", + "type-widgets-type": "Type de widget", + "type-admin-settings": "Paramètres d'administration", + "type-ota-package": "Package OTA", + "type-queue": "File d'attente", + "action-type-added": "Ajouté", + "action-type-deleted": "Supprimé", + "action-type-updated": "Mis à jour", + "action-type-post-attributes": "Publier des attributs", + "action-type-attributes-updated": "Attributs mis à jour", + "action-type-attributes-deleted": "Attributs supprimés", + "action-type-timeseries-updated": "Série temporelle mise à jour", + "action-type-credentials-updated": "Identifiants mis à jour", + "action-type-assigned-to-customer": "Attribué à un client", + "action-type-unassigned-from-customer": "Désattribué d'un client", + "action-type-relation-add-or-update": "Ajout ou mise à jour d'une relation", + "action-type-relation-deleted": "Relation supprimée", + "action-type-rpc-call": "Appel RPC", + "action-type-alarm-ack": "Accusé de réception d'alarme", + "action-type-alarm-clear": "Réinitialisation de l'alarme", + "action-type-alarm-assigned": "Alarme attribuée", + "action-type-alarm-unassigned": "Alarme désattribuée", + "action-type-assigned-to-edge": "Attribué à une passerelle", + "action-type-unassigned-from-edge": "Désattribué d'une passerelle", + "action-type-credentials-request": "Demande d'identifiants", + "action-type-entity-merge-request": "Demande de fusion d'entités" + }, + "error": { + "unable-to-connect": "Impossible de se connecter au serveur ! Veuillez vérifier votre connexion Internet.", + "unhandled-error-code": "Code d'erreur non géré : {{errorCode}}", + "unknown-error": "Erreur inconnue" + }, + "entity": { + "entity": "Entité", + "entities": "Entités", + "entities-count": "Nombre d'entités", + "alarms-count": "Nombre d'alarmes", + "aliases": "Alias d'entités", + "aliases-short": "Alias", + "entity-alias": "Alias d'entité", + "unable-delete-entity-alias-title": "Impossible de supprimer l'alias d'entité", + "unable-delete-entity-alias-text": "L'alias d'entité '{{entityAlias}}' ne peut pas être supprimé car il est utilisé par le(s) widget(s) suivant(s) :
{{widgetsList}}", + "duplicate-alias-error": "Alias en double trouvé '{{alias}}'.
Les alias d'entité doivent être uniques dans le tableau de bord.", + "missing-entity-filter-error": "Filtre manquant pour l'alias '{{alias}}'.", + "configure-alias": "Configurer l'alias '{{alias}}'", + "alias": "Alias", + "alias-required": "L'alias d'entité est requis.", + "remove-alias": "Supprimer l'alias d'entité", + "add-alias": "Ajouter un alias d'entité", + "edit-alias": "Modifier l'alias d'entité", + "entity-list": "Liste des entités", + "entity-type": "Type d'entité", + "entity-types": "Types d'entités", + "entity-type-list": "Liste des types d'entités", + "any-entity": "Toute entité", + "add-entity-type": "Ajouter un type d'entité", + "enter-entity-type": "Saisir un type d'entité", + "no-entities-matching": "Aucune entité correspondant à '{{entity}}' n'a été trouvée.", + "no-entities-text": "Aucune entité trouvée", + "no-entity-types-matching": "Aucun type d'entité correspondant à '{{entityType}}' n'a été trouvé.", + "name-starts-with": "Expression du nom", + "help-text": "Utilisez '%' selon le besoin : '%nom_entité_contient%', '%nom_entité_se_termine', 'entité_commence_par'.", + "use-entity-name-filter": "Utiliser le filtre", + "entity-list-empty": "Aucune entité sélectionnée.", + "entity-type-list-required": "Au moins un type d'entité doit être sélectionné.", + "entity-name-filter-required": "Le filtre de nom d'entité est requis.", + "entity-name-filter-no-entity-matched": "Aucune entité commençant par '{{entity}}' n'a été trouvée.", + "all-subtypes": "Tous", + "select-entities": "Sélectionner des entités", + "no-aliases-found": "Aucun alias trouvé.", + "no-alias-matching": "'{{alias}}' introuvable.", + "create-new-alias": "Créer un nouveau !", + "create-new": "Créer un nouveau", + "key": "Clé", + "key-name": "Nom de la clé", + "no-keys-found": "Aucune clé trouvée.", + "no-key-matching": "'{{key}}' introuvable.", + "create-new-key": "Créer une nouvelle !", + "type": "Type", + "type-required": "Le type d'entité est requis.", + "type-device": "Appareil", + "type-devices": "Appareils", + "list-of-devices": "{ count, plural, =1 {Un appareil} other {Liste de # appareils} }", + "device-name-starts-with": "Appareils dont les noms commencent par '{{prefix}}'", + "type-device-profile": "Profil de l'appareil", + "type-device-profiles": "Profils d'appareils", + "clear-selected-profiles": "Effacer les profils sélectionnés", + "list-of-device-profiles": "{ count, plural, =1 {Un profil d'appareil} other {Liste de # profils d'appareils} }", + "device-profile-name-starts-with": "Profils d'appareils dont les noms commencent par '{{prefix}}'", + "type-asset-profile": "Profil d'actif", + "type-asset-profiles": "Profils d'actifs", + "list-of-asset-profiles": "{ count, plural, =1 {Un profil d'actif} other {Liste de # profils d'actifs} }", + "asset-profile-name-starts-with": "Profils d'actifs dont les noms commencent par '{{prefix}}'", + "type-asset": "Actif", + "type-assets": "Actifs", + "list-of-assets": "{ count, plural, =1 {Un actif} other {Liste de # actifs} }", + "asset-name-starts-with": "Actifs dont les noms commencent par '{{prefix}}'", + "type-entity-view": "Vue d'entité", + "type-entity-views": "Vues d'entité", + "list-of-entity-views": "{ count, plural, =1 {Une vue d'entité} other {Liste de # vues d'entités} }", + "entity-view-name-starts-with": "Vues d'entités dont les noms commencent par '{{prefix}}'", + "type-rule": "Règle", + "type-rules": "Règles", + "list-of-rules": "{ count, plural, =1 {Une règle} other {Liste de # règles} }", + "rule-name-starts-with": "Règles dont les noms commencent par '{{prefix}}'", + "type-plugin": "Plugin", + "type-plugins": "Plugins", + "list-of-plugins": "{ count, plural, =1 {Un plugin} other {Liste de # plugins} }", + "plugin-name-starts-with": "Plugins dont les noms commencent par '{{prefix}}'", + "type-tenant": "Locataire", + "type-tenants": "Locataires", + "list-of-tenants": "{ count, plural, =1 {Un locataire} other {Liste de # locataires} }", + "tenant-name-starts-with": "Locataires dont les noms commencent par '{{prefix}}'", + "type-tenant-profile": "Profil de locataire", + "type-tenant-profiles": "Profils de locataire", + "list-of-tenant-profiles": "{ count, plural, =1 {Un profil de locataire} other {Liste de # profils de locataire} }", + "tenant-profile-name-starts-with": "Profils de locataires dont les noms commencent par '{{prefix}}'", + "type-customer": "Client", + "type-customers": "Clients", + "list-of-customers": "{ count, plural, =1 {Un client} other {Liste de # clients} }", + "customer-name-starts-with": "Clients dont les noms commencent par '{{prefix}}'", + "type-user": "Utilisateur", + "type-users": "Utilisateurs", + "list-of-users": "{ count, plural, =1 {Un utilisateur} other {Liste de # utilisateurs} }", + "user-name-starts-with": "Utilisateurs dont les noms commencent par '{{prefix}}'", + "type-dashboard": "Tableau de bord", + "type-dashboards": "Tableaux de bord", + "list-of-dashboards": "{ count, plural, =1 {Un tableau de bord} other {Liste de # tableaux de bord} }", + "dashboard-name-starts-with": "Tableaux de bord dont les noms commencent par '{{prefix}}'", + "type-alarm": "Alarme", + "type-alarms": "Alarmes", + "list-of-alarms": "{ count, plural, =1 {Une alarme} other {Liste de # alarmes} }", + "alarm-name-starts-with": "Alarmes dont les noms commencent par '{{prefix}}'", + "type-rulechain": "Chaîne de règles", + "type-rulechains": "Chaînes de règles", + "list-of-rulechains": "{ count, plural, =1 {Une chaîne de règles} other {Liste de # chaînes de règles} }", + "rulechain-name-starts-with": "Chaînes de règles dont les noms commencent par '{{prefix}}'", + "type-rulenode": "Nœud de règle", + "type-rulenodes": "Nœuds de règle", + "list-of-rulenodes": "{ count, plural, =1 {Un nœud de règle} other {Liste de # nœuds de règle} }", + "rulenode-name-starts-with": "Nœuds de règle dont les noms commencent par '{{prefix}}'", + "type-current-customer": "Client actuel", + "type-current-tenant": "Locataire actuel", + "type-current-user": "Utilisateur actuel", + "type-current-user-owner": "Propriétaire utilisateur actuel", + "type-calculated-field": "Champ calculé", + "type-calculated-fields": "Champs calculés", + "type-widgets-bundle": "Pack de widgets", + "type-widgets-bundles": "Packs de widgets", + "list-of-widgets-bundles": "{ count, plural, =1 {Un pack de widgets} other {Liste de # packs de widgets} }", + "type-widget": "Widget", + "type-widgets": "Widgets", + "list-of-widgets": "{ count, plural, =1 {Un widget} other {Liste de # widgets} }", + "search": "Rechercher des entités", + "selected-entities": "{ count, plural, =1 {1 entité} other {# entités} } sélectionnée(s)", + "entity-name": "Nom de l'entité", + "entity-label": "Étiquette de l'entité", + "details": "Détails de l'entité", + "no-entities-prompt": "Aucune entité trouvée", + "no-data": "Aucune donnée à afficher", + "columns-to-display": "Colonnes à afficher", + "type-api-usage-state": "État d'utilisation de l'API", + "type-edge": "Edge", + "type-edges": "Edges", + "list-of-edges": "{ count, plural, =1 {Un edge} other {Liste de # edges} }", + "edge-name-starts-with": "Edges dont les noms commencent par '{{prefix}}'", + "version-conflict": { + "message": "Souhaitez-vous écraser la version existante ou annuler les modifications et charger la dernière version ?", + "link": "Vous pouvez télécharger votre version de {{entityType}} en utilisant ce", + "overwrite": "Écraser la version", + "discard": "Annuler les modifications" + }, + "type-tb-resource": "Ressource", + "type-tb-resources": "Ressources", + "list-of-tb-resources": "{ count, plural, =1 {Une ressource} other {Liste de # ressources} }", + "type-ota-package": "Paquet OTA", + "type-rpc": "RPC", + "type-queue": "File d'attente", + "type-queue-stats": "Statistiques de file d'attente", + "type-queues-stats": "Statistiques des files d'attente", + "type-notification": "Notification", + "type-notification-rule": "Règle de notification", + "type-notification-rules": "Règles de notification", + "list-of-notification-rules": "{ count, plural, =1 {Une règle de notification} other {Liste de # règles de notification} }", + "type-notification-target": "Destinataire de notification", + "type-notification-targets": "Destinataires de notification", + "list-of-notification-targets": "{ count, plural, =1 {Un destinataire de notification} other {Liste de # destinataires de notification} }", + "type-notification-request": "Demande de notification", + "type-notification-template": "Modèle de notification", + "type-notification-templates": "Modèles de notification", + "list-of-notification-templates": "{ count, plural, =1 {Un modèle de notification} other {Liste de # modèles de notification} }", + "link": "lien", + "type-oauth2-client": "Client OAuth 2.0", + "type-oauth2-clients": "Clients OAuth 2.0", + "list-of-oauth2-clients": "{ count, plural, =1 {Un client OAuth 2.0} other {Liste de # clients OAuth 2.0} }", + "type-domain": "Domaine", + "type-domains": "Domaines", + "list-of-domains": "{ count, plural, =1 {Un domaine} other {Liste de # domaines} }", + "type-mobile-app": "Application mobile", + "type-mobile-apps": "Applications mobiles", + "list-of-mobile-apps": "{ count, plural, =1 {Une application mobile} other {Liste de # applications mobiles} }", + "type-mobile-app-bundle": "Pack mobile", + "type-mobile-app-bundles": "Packs mobiles", + "list-of-mobile-app-bundles": "{ count, plural, =1 {Un pack mobile} other {Liste de # packs mobiles} }" + }, + "entity-field": { + "created-time": "Date de création", + "name": "Nom", + "type": "Type", + "first-name": "Prénom", + "last-name": "Nom", + "email": "Email", + "title": "Titre", + "country": "Pays", + "state": "État", + "city": "Ville", + "address": "Adresse", + "address2": "Adresse 2", + "zip": "Code postal", + "phone": "Téléphone", + "label": "Étiquette", + "queue-name": "Nom de la file", + "service-id": "ID du service", + "owner-name": "Nom du propriétaire", + "owner-type": "Type de propriétaire" + }, + "entity-view": { + "entity-view": "Vue d'entité", + "entity-view-required": "La vue d'entité est requise.", + "entity-views": "Vues d'entité", + "management": "Gestion des vues d'entité", + "view-entity-views": "Voir les vues d'entité", + "entity-view-alias": "Alias de la vue d'entité", + "aliases": "Alias des vues d'entité", + "no-alias-matching": "'{{alias}}' introuvable.", + "no-aliases-found": "Aucun alias trouvé.", + "no-key-matching": "'{{key}}' introuvable.", + "no-keys-found": "Aucune clé trouvée.", + "create-new-alias": "Créer un nouveau !", + "create-new-key": "Créer un nouveau !", + "duplicate-alias-error": "Alias en double '{{alias}}'.
Les alias des vues d'entité doivent être uniques dans le tableau de bord.", + "configure-alias": "Configurer l'alias '{{alias}}'", + "no-entity-views-matching": "Aucune vue d'entité correspondant à '{{entity}}' trouvée.", + "public": "Public", + "alias": "Alias", + "alias-required": "L'alias de la vue d'entité est requis.", + "remove-alias": "Supprimer l'alias de la vue d'entité", + "add-alias": "Ajouter un alias de vue d'entité", + "name-starts-with": "Expression du nom de la vue d'entité", + "help-text": "Utilisez '%' selon le besoin : '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list": "Liste des vues d'entité", + "use-entity-view-name-filter": "Utiliser un filtre", + "entity-view-list-empty": "Aucune vue d'entité sélectionnée.", + "entity-view-name-filter-required": "Le filtre de nom de vue d'entité est requis.", + "entity-view-name-filter-no-entity-view-matched": "Aucune vue d'entité commençant par '{{entityView}}' trouvée.", + "add": "Ajouter une vue d'entité", + "entity-view-public": "La vue d'entité est publique", + "assign-to-customer": "Assigner à un client", + "assign-entity-view-to-customer": "Assigner des vues d'entité au client", + "assign-entity-view-to-customer-text": "Veuillez sélectionner les vues d'entité à assigner au client", + "no-entity-views-text": "Aucune vue d'entité trouvée", + "assign-to-customer-text": "Veuillez sélectionner le client à qui assigner la/les vue(s) d'entité", + "entity-view-details": "Détails de la vue d'entité", + "add-entity-view-text": "Ajouter une nouvelle vue d'entité", + "delete": "Supprimer la vue d'entité", + "assign-entity-views": "Assigner des vues d'entité", + "assign-entity-views-text": "Assigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } au client", + "delete-entity-views": "Supprimer les vues d'entité", + "make-public": "Rendre la vue publique", + "make-private": "Rendre la vue privée", + "unassign-from-customer": "Désassigner du client", + "unassign-entity-views": "Désassigner les vues d'entité", + "unassign-entity-views-action-title": "Désassigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } du client", + "assign-new-entity-view": "Assigner une nouvelle vue d'entité", + "delete-entity-view-title": "Êtes-vous sûr de vouloir supprimer la vue d'entité '{{entityViewName}}' ?", + "delete-entity-view-text": "Attention, après confirmation, la vue d'entité et toutes ses données seront irrécupérables.", + "delete-entity-views-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } ?", + "delete-entity-views-action-title": "Supprimer { count, plural, =1 {1 vue d'entité} other {# vues d'entité} }", + "delete-entity-views-text": "Attention, après confirmation, toutes les vues d'entité sélectionnées seront supprimées avec leurs données.", + "make-public-entity-view-title": "Êtes-vous sûr de vouloir rendre la vue d'entité '{{entityViewName}}' publique ?", + "make-public-entity-view-text": "Après confirmation, la vue d'entité et ses données seront rendues publiques.", + "make-private-entity-view-title": "Êtes-vous sûr de vouloir rendre la vue d'entité '{{entityViewName}}' privée ?", + "make-private-entity-view-text": "Après confirmation, la vue d'entité et ses données seront rendues privées.", + "unassign-entity-view-title": "Êtes-vous sûr de vouloir désassigner la vue d'entité '{{entityViewName}}' ?", + "unassign-entity-view-text": "Après confirmation, la vue d'entité ne sera plus accessible au client.", + "unassign-entity-view": "Désassigner la vue d'entité", + "unassign-entity-views-title": "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } ?", + "unassign-entity-views-text": "Après confirmation, toutes les vues d'entité sélectionnées seront désassignées.", + "entity-view-type": "Type de vue d'entité", + "entity-view-type-required": "Le type de vue d'entité est requis.", + "select-entity-view-type": "Sélectionner le type de vue d'entité", + "enter-entity-view-type": "Entrer le type de vue d'entité", + "any-entity-view": "Toute vue d'entité", + "no-entity-view-types-matching": "Aucun type de vue d'entité correspondant à '{{entitySubtype}}' trouvé.", + "entity-view-type-list-empty": "Aucun type de vue d'entité sélectionné.", + "entity-view-types": "Types de vues d'entité", + "created-time": "Date de création", + "name": "Nom", + "name-required": "Le nom est requis.", + "name-max-length": "Le nom doit contenir moins de 256 caractères", + "type-max-length": "Le type de vue d'entité doit contenir moins de 256 caractères", + "description": "Description", + "events": "Événements", + "details": "Détails", + "copyId": "Copier l'ID de la vue d'entité", + "idCopiedMessage": "L'ID de la vue d'entité a été copié dans le presse-papiers", + "assignedToCustomer": "Assigné à un client", + "unable-entity-view-device-alias-title": "Impossible de supprimer l'alias de la vue d'entité", + "unable-entity-view-device-alias-text": "L'alias de l'appareil '{{entityViewAlias}}' ne peut pas être supprimé car il est utilisé par les widgets suivants :
{{widgetsList}}", + "select-entity-view": "Sélectionner une vue d'entité", + "start-ts": "Heure de début", + "end-ts": "Heure de fin", + "date-limits": "Limites de date", + "client-attributes": "Attributs client", + "shared-attributes": "Attributs partagés", + "server-attributes": "Attributs serveur", + "timeseries": "Séries temporelles", + "client-attributes-placeholder": "Attributs client", + "shared-attributes-placeholder": "Attributs partagés", + "server-attributes-placeholder": "Attributs serveur", + "timeseries-placeholder": "Séries temporelles", + "target-entity": "Entité cible", + "attributes-propagation": "Propagation des attributs", + "attributes-propagation-hint": "La vue d'entité copiera automatiquement les attributs spécifiés de l'entité cible chaque fois que vous enregistrez ou mettez à jour cette vue d'entité. Pour des raisons de performance, les attributs ne sont pas propagés automatiquement à chaque changement. Activez cette propagation via le nœud de règle « copy to view » lié aux messages « Post attributes » et « Attributes Updated ».", + "timeseries-data": "Données de séries temporelles", + "timeseries-data-hint": "Configurez les clés de séries temporelles de l'entité cible qui seront accessibles dans la vue d'entité. Ces données sont en lecture seule.", + "search": "Rechercher des vues d'entité", + "selected-entity-views": "{ count, plural, =1 {1 vue d'entité} other {# vues d'entité} } sélectionnée(s)", + "assign-entity-view-to-edge": "Assigner des vues d'entité à l'Edge", + "assign-entity-view-to-edge-text": "Veuillez sélectionner les vues d'entité à assigner à l'Edge", + "unassign-entity-view-from-edge-title": "Êtes-vous sûr de vouloir désassigner la vue d'entité '{{entityViewName}}' ?", + "unassign-entity-view-from-edge-text": "Après confirmation, la vue d'entité sera désassignée et ne sera plus accessible par l'Edge.", + "unassign-entity-views-from-edge-action-title": "Désassigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } de l'Edge", + "unassign-entity-view-from-edge": "Désassigner la vue d'entité", + "unassign-entity-views-from-edge-title": "Êtes-vous sûr de vouloir désassigner { count, plural, =1 {1 vue d'entité} other {# vues d'entité} } ?", + "unassign-entity-views-from-edge-text": "Après confirmation, toutes les vues d'entité sélectionnées seront désassignées et ne seront plus accessibles par l'Edge." + }, + "event": { + "event-type": "Type d'événement", + "events-filter": "Filtre d'événements", + "clean-events": "Effacer les événements", + "type-error": "Erreur", + "type-lc-event": "Événement de cycle de vie", + "type-stats": "Statistiques", + "type-debug-rule-node": "Débogage", + "type-debug-rule-chain": "Débogage", + "type-debug-calculated-field": "Débogage", + "arguments": "Arguments", + "result": "Résultat", + "no-events-prompt": "Aucun événement trouvé", + "error": "Erreur", + "alarm": "Alarme", + "event-time": "Heure de l'événement", + "server": "Serveur", + "body": "Contenu", + "method": "Méthode", + "type": "Type", + "metadata": "Métadonnées", + "message": "Message", + "message-id": "ID du message", + "copy-message-id": "Copier l'ID du message", + "message-type": "Type de message", + "data-type": "Type de données", + "relation-type": "Type de relation", + "data": "Données", + "event": "Événement", + "status": "Statut", + "success": "Succès", + "failed": "Échec", + "messages-processed": "Messages traités", + "max-messages-processed": "Messages traités max.", + "min-messages-processed": "Messages traités min.", + "errors-occurred": "Erreurs survenues", + "max-errors-occurred": "Erreurs max.", + "min-errors-occurred": "Erreurs min.", + "min-value": "La valeur minimale est 0.", + "all-events": "Tous", + "has-error": "Contient une erreur", + "entity-id": "ID de l'entité", + "copy-entity-id": "Copier l'ID de l'entité", + "entity-type": "Type d'entité", + "clear-filter": "Réinitialiser le filtre", + "clear-request-title": "Effacer tous les événements", + "clear-request-text": "Êtes-vous sûr de vouloir effacer tous les événements ?", + "started": "Démarré", + "updated": "Mis à jour", + "stopped": "Arrêté" + }, + "extension": { + "extensions": "Extensions", + "selected-extensions": "{ count, plural, =1 {1 extension} other {# extensions} } sélectionnée(s)", + "type": "Type", + "key": "Clé", + "value": "Valeur", + "id": "ID", + "extension-id": "ID de l'extension", + "extension-type": "Type d'extension", + "transformer-json": "JSON *", + "unique-id-required": "L'ID de l'extension existe déjà.", + "delete": "Supprimer l'extension", + "add": "Ajouter une extension", + "edit": "Modifier l'extension", + "delete-extension-title": "Êtes-vous sûr de vouloir supprimer l'extension '{{extensionId}}' ?", + "delete-extension-text": "Attention, après confirmation, l'extension et toutes les données associées seront irrécupérables.", + "delete-extensions-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 extension} other {# extensions} } ?", + "delete-extensions-text": "Attention, après confirmation, toutes les extensions sélectionnées seront supprimées.", + "converters": "Convertisseurs", + "converter-id": "ID du convertisseur", + "configuration": "Configuration", + "converter-configurations": "Configurations de convertisseur", + "token": "Jeton de sécurité", + "add-converter": "Ajouter un convertisseur", + "add-config": "Ajouter une configuration de convertisseur", + "device-name-expression": "Expression du nom de l'appareil", + "device-type-expression": "Expression du type d'appareil", + "custom": "Personnalisé", + "to-double": "Convertir en double", + "transformer": "Transformateur", + "json-required": "Le JSON du transformateur est requis.", + "json-parse": "Impossible d'analyser le JSON du transformateur.", + "attributes": "Attributs", + "add-attribute": "Ajouter un attribut", + "add-map": "Ajouter un élément de mappage", + "timeseries": "Séries temporelles", + "add-timeseries": "Ajouter une série temporelle", + "field-required": "Champ requis", + "brokers": "Courtiers", + "add-broker": "Ajouter un courtier", + "host": "Hôte", + "port": "Port", + "port-range": "Le port doit être compris entre 1 et 65535.", + "ssl": "Ssl", + "credentials": "Identifiants", + "username": "Nom d'utilisateur", + "password": "Mot de passe", + "retry-interval": "Intervalle de réessai en millisecondes", + "anonymous": "Anonyme", + "basic": "Basique", + "pem": "PEM", + "ca-cert": "Fichier de certificat CA *", + "private-key": "Fichier de clé privée *", + "cert": "Fichier de certificat *", + "no-file": "Aucun fichier sélectionné.", + "drop-file": "Déposez un fichier ou cliquez pour en sélectionner un à télécharger.", + "mapping": "Mappage", + "topic-filter": "Filtre de sujet", + "converter-type": "Type de convertisseur", + "converter-json": "Json", + "json-name-expression": "Expression JSON du nom de l'appareil", + "topic-name-expression": "Expression du nom de l'appareil dans le sujet", + "json-type-expression": "Expression JSON du type de l'appareil", + "topic-type-expression": "Expression du type de l'appareil dans le sujet", + "attribute-key-expression": "Expression de la clé d'attribut", + "attr-json-key-expression": "Expression JSON de la clé d'attribut", + "attr-topic-key-expression": "Expression de la clé d'attribut dans le sujet", + "request-id-expression": "Expression de l'ID de requête", + "request-id-json-expression": "Expression JSON de l'ID de requête", + "request-id-topic-expression": "Expression de l'ID de requête dans le sujet", + "response-topic-expression": "Expression du sujet de réponse", + "value-expression": "Expression de la valeur", + "topic": "Sujet", + "timeout": "Délai d'attente en millisecondes", + "converter-json-required": "Le JSON du convertisseur est requis.", + "converter-json-parse": "Impossible d'analyser le JSON du convertisseur.", + "filter-expression": "Expression de filtre", + "connect-requests": "Requêtes de connexion", + "add-connect-request": "Ajouter une requête de connexion", + "disconnect-requests": "Requêtes de déconnexion", + "add-disconnect-request": "Ajouter une requête de déconnexion", + "attribute-requests": "Requêtes d'attribut", + "add-attribute-request": "Ajouter une requête d'attribut", + "attribute-updates": "Mises à jour des attributs", + "add-attribute-update": "Ajouter une mise à jour d'attribut", + "server-side-rpc": "RPC côté serveur", + "add-server-side-rpc-request": "Ajouter une requête RPC côté serveur", + "device-name-filter": "Filtre de nom de l'appareil", + "attribute-filter": "Filtre d'attribut", + "method-filter": "Filtre de méthode", + "request-topic-expression": "Expression du sujet de requête", + "response-timeout": "Délai de réponse en millisecondes", + "topic-expression": "Expression du sujet", + "client-scope": "Portée du client", + "add-device": "Ajouter un appareil", + "opc-server": "Serveurs", + "opc-add-server": "Ajouter un serveur", + "opc-add-server-prompt": "Veuillez ajouter un serveur", + "opc-application-name": "Nom de l'application", + "opc-application-uri": "URI de l'application", + "opc-scan-period-in-seconds": "Période de scan en secondes", + "opc-security": "Sécurité", + "opc-identity": "Identité", + "opc-keystore": "Keystore", + "opc-type": "Type", + "opc-keystore-type": "Type", + "opc-keystore-location": "Emplacement *", + "opc-keystore-password": "Mot de passe", + "opc-keystore-alias": "Alias", + "opc-keystore-key-password": "Mot de passe de la clé", + "opc-device-node-pattern": "Motif de nœud de l'appareil", + "opc-device-name-pattern": "Motif du nom de l'appareil", + "modbus-server": "Serveurs/esclaves", + "modbus-add-server": "Ajouter un serveur/esclave", + "modbus-add-server-prompt": "Veuillez ajouter un serveur/esclave", + "modbus-transport": "Transport", + "modbus-tcp-reconnect": "Reconnecter automatiquement", + "modbus-rtu-over-tcp": "RTU sur TCP", + "modbus-port-name": "Nom du port série", + "modbus-encoding": "Encodage", + "modbus-parity": "Parité", + "modbus-baudrate": "Vitesse de transmission", + "modbus-databits": "Bits de données", + "modbus-stopbits": "Bits de stop", + "modbus-databits-range": "Les bits de données doivent être compris entre 7 et 8.", + "modbus-stopbits-range": "Les bits de stop doivent être compris entre 1 et 2.", + "modbus-unit-id": "ID d'unité", + "modbus-unit-id-range": "L'ID d'unité doit être compris entre 1 et 247.", + "modbus-device-name": "Nom de l'appareil", + "modbus-poll-period": "Période d'interrogation (ms)", + "modbus-attributes-poll-period": "Période d'interrogation des attributs (ms)", + "modbus-timeseries-poll-period": "Période d'interrogation des séries temporelles (ms)", + "modbus-poll-period-range": "La période d'interrogation doit être une valeur positive.", + "modbus-tag": "Étiquette", + "modbus-function": "Fonction", + "modbus-register-address": "Adresse du registre", + "modbus-register-address-range": "L'adresse du registre doit être comprise entre 0 et 65535.", + "modbus-register-bit-index": "Index de bit", + "modbus-register-bit-index-range": "L'index de bit doit être compris entre 0 et 15.", + "modbus-register-count": "Nombre de registres", + "modbus-register-count-range": "Le nombre de registres doit être une valeur positive.", + "modbus-byte-order": "Ordre des octets", + "sync": { + "status": "Statut", + "sync": "Synchronisé", + "not-sync": "Non synchronisé", + "last-sync-time": "Dernière synchronisation", + "not-available": "Non disponible" + }, + "export-extensions-configuration": "Exporter la configuration des extensions", + "import-extensions-configuration": "Importer la configuration des extensions", + "import-extensions": "Importer des extensions", + "import-extension": "Importer une extension", + "export-extension": "Exporter une extension", + "file": "Fichier d’extensions", + "invalid-file-error": "Fichier d’extension invalide" + }, + "feature": { + "advanced-features": "Fonctionnalités avancées" + }, + "filter": { + "add": "Ajouter un filtre", + "edit": "Modifier le filtre", + "name": "Nom du filtre", + "name-required": "Le nom du filtre est requis.", + "duplicate-filter": "Un filtre portant le même nom existe déjà.", + "filters": "Filtres", + "unable-delete-filter-title": "Impossible de supprimer le filtre", + "unable-delete-filter-text": "Le filtre '{{filter}}' ne peut pas être supprimé car il est utilisé par les widgets suivants :
{{widgetsList}}", + "duplicate-filter-error": "Filtre en double trouvé '{{filter}}'.
Les filtres doivent être uniques au sein du tableau de bord.", + "missing-key-filters-error": "Les filtres clés sont manquants pour le filtre '{{filter}}'.", + "filter": "Filtre", + "editable": "Éditable", + "no-filters-found": "Aucun filtre trouvé.", + "no-filter-text": "Aucun filtre spécifié", + "add-filter-prompt": "Veuillez ajouter un filtre", + "no-filter-matching": "'{{filter}}' introuvable.", + "create-new-filter": "Créer un nouveau !", + "create-new": "Créer nouveau", + "filter-required": "Le filtre est requis.", + "operation": { + "operation": "Opération", + "equal": "égal à", + "not-equal": "différent de", + "starts-with": "commence par", + "ends-with": "se termine par", + "contains": "contient", + "not-contains": "ne contient pas", + "greater": "supérieur à", + "less": "inférieur à", + "greater-or-equal": "supérieur ou égal à", + "less-or-equal": "inférieur ou égal à", + "and": "et", + "or": "ou", + "in": "dans", + "not-in": "pas dans" + }, + "ignore-case": "ignorer la casse", + "value": "Valeur", + "remove-filter": "Supprimer le filtre", + "duplicate-filter-action": "Dupliquer le filtre", + "preview": "Aperçu du filtre", + "no-filters": "Aucun filtre configuré", + "add-filter": "Ajouter un filtre", + "add-complex-filter": "Ajouter un filtre complexe", + "add-complex": "Ajouter complexe", + "complex-filter": "Filtre complexe", + "edit-complex-filter": "Modifier le filtre complexe", + "edit-filter-user-params": "Modifier les paramètres utilisateur du prédicat du filtre", + "filter-user-params": "Paramètres utilisateur du prédicat du filtre", + "user-parameters": "Paramètres utilisateur", + "display-label": "Libellé à afficher", + "order-priority": "Priorité d'ordre des champs", + "key-filter": "Filtre par clé", + "key-filters": "Filtres par clé", + "key-name": "Nom de la clé", + "key-name-required": "Le nom de la clé est requis.", + "key-type": { + "key-type": "Type de clé", + "attribute": "Attribut", + "timeseries": "Séries temporelles", + "entity-field": "Champ d'entité", + "constant": "Constante", + "client-attribute": "Attribut client", + "server-attribute": "Attribut serveur", + "shared-attribute": "Attribut partagé" + }, + "value-type": { + "value-type": "Type de valeur", + "string": "Chaîne", + "numeric": "Numérique", + "boolean": "Booléen", + "date-time": "Date/Heure" + }, + "value-type-required": "Le type de valeur de la clé est requis.", + "key-value-type-change-title": "Êtes-vous sûr de vouloir changer le type de valeur de la clé ?", + "key-value-type-change-message": "Si vous confirmez le nouveau type de valeur, tous les filtres de clés saisis seront supprimés.", + "no-key-filters": "Aucun filtre de clé configuré", + "add-key-filter": "Ajouter un filtre de clé", + "remove-key-filter": "Supprimer le filtre de clé", + "edit-key-filter": "Modifier le filtre de clé", + "date": "Date", + "time": "Heure", + "current-tenant": "Locataire actuel", + "current-customer": "Client actuel", + "current-user": "Utilisateur actuel", + "current-device": "Appareil actuel", + "default-value": "Valeur par défaut", + "default-comma-separated-values": "Valeurs par défaut séparées par des virgules", + "dynamic-source-type": "Type de source dynamique", + "dynamic-value": "Valeur dynamique", + "no-dynamic-value": "Aucune valeur dynamique", + "source-attribute": "Attribut source", + "switch-to-dynamic-value": "Basculer vers la valeur dynamique", + "switch-to-default-value": "Basculer vers la valeur par défaut", + "inherit-owner": "Hériter du propriétaire", + "source-attribute-not-set": "Si l'attribut source n'est pas défini" + }, + "fullscreen": { + "expand": "Agrandir en plein écran", + "exit": "Quitter le plein écran", + "toggle": "Basculer en mode plein écran", + "fullscreen": "Plein écran" + }, + "function": { + "function": "Fonction" + }, + "gateway": { + "gateway-name": "Nom de la passerelle", + "gateway-name-required": "Le nom de la passerelle est requis.", + "gateways": "Passerelles", + "create-new-gateway": "Créer une nouvelle passerelle", + "create-new-gateway-text": "Êtes-vous sûr de vouloir créer une nouvelle passerelle avec le nom : '{{gatewayName}}' ?", + "launch-command": "Commande de lancement", + "no-gateway-found": "Aucune passerelle trouvée.", + "no-gateway-matching": "'{{item}}' introuvable." + }, + "grid": { + "delete-item-title": "Êtes-vous sûr de vouloir supprimer cet élément ?", + "delete-item-text": "Attention, après confirmation, cet élément et toutes les données associées seront irrécupérables.", + "delete-items-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 élément} other {# éléments} } ?", + "delete-items-action-title": "Supprimer { count, plural, =1 {1 élément} other {# éléments} }", + "delete-items-text": "Attention, après confirmation, tous les éléments sélectionnés seront supprimés ainsi que toutes les données associées.", + "add-item-text": "Ajouter un nouvel élément", + "no-items-text": "Aucun élément trouvé", + "item-details": "Détails de l’élément", + "delete-item": "Supprimer l’élément", + "delete-items": "Supprimer les éléments", + "scroll-to-top": "Retour en haut" + }, + "help": { + "goto-help-page": "Aller à la page d'aide", + "show-help": "Afficher l’aide" + }, + "home": { + "home": "Accueil", + "profile": "Profil", + "logout": "Déconnexion", + "menu": "Menu", + "avatar": "Avatar", + "open-user-menu": "Ouvrir le menu utilisateur" + }, + "file-input": { + "browse-file": "Parcourir un fichier", + "browse-files": "Parcourir des fichiers" + }, + "image": { + "gallery": "Galerie d'images", + "search": "Rechercher une image", + "selected-images": "{ count, plural, =1 {1 image} other {# images} } sélectionnée(s)", + "created-time": "Date de création", + "name": "Nom", + "name-required": "Le nom est requis.", + "resolution": "Résolution", + "size": "Taille", + "system": "Système", + "download-image": "Télécharger l'image", + "export-image": "Exporter l'image au format JSON", + "import-image": "Importer une image depuis un fichier JSON", + "upload-image": "Téléverser une image", + "edit-image": "Modifier l'image", + "image-details": "Détails de l'image", + "no-images": "Aucune image trouvée", + "delete-image": "Supprimer l'image", + "delete-image-title": "Êtes-vous sûr de vouloir supprimer l'image '{{imageTitle}}' ?", + "delete-image-text": "Attention, après confirmation, l'image ne pourra pas être récupérée.", + "delete-images-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 image} other {# images} } ?", + "delete-images-text": "Attention, après confirmation, toutes les images sélectionnées seront supprimées ainsi que toutes les données associées.", + "list-mode": "Vue en liste", + "grid-mode": "Vue en grille", + "image-preview": "Aperçu de l'image", + "update-image": "Mettre à jour l'image", + "export-failed-error": "Impossible d'exporter l'image : {{error}}", + "image-json-file": "Fichier JSON d'image", + "invalid-image-json-file-error": "Impossible d'importer l'image : structure JSON invalide.", + "image-is-in-use": "L'image est utilisée par d'autres entités", + "images-are-in-use": "Certaines images sont utilisées par d'autres entités", + "image-is-in-use-text": "L'image '{{title}}' n'a pas été supprimée car elle est utilisée par les entités suivantes :", + "images-are-in-use-text": "Toutes les images n'ont pas été supprimées car elles sont utilisées par d'autres entités.
Vous pouvez voir les entités référencées en cliquant sur le bouton Références dans la ligne de l'image concernée.
Si vous souhaitez tout de même les supprimer, sélectionnez-les dans le tableau ci-dessous puis cliquez sur le bouton Supprimer la sélection.", + "delete-image-in-use-text": "Si vous souhaitez tout de même supprimer l'image, cliquez sur le bouton Supprimer quand même.", + "system-entities": "Entités système :", + "entities": "entités :", + "references": "Références", + "include-system-images": "Inclure les images système", + "clear-image": "Effacer l'image", + "no-image": "Aucune image", + "no-image-selected": "Aucune image sélectionnée", + "browse-from-gallery": "Parcourir la galerie", + "set-link": "Définir le lien", + "image-link": "Lien de l'image", + "link": "Lien", + "copy-image-link": "Copier le lien de l'image", + "embed-image": "Intégrer l'image", + "embed-to-html": "Intégrer dans le HTML", + "embed-to-html-hint": "Cette fonction rendra le lien accessible à tout utilisateur non autorisé.", + "embed-to-html-text": "En utilisant le code suivant, vous pouvez intégrer une image dans des composants basés sur du HTML simple.
Cela inclut les widgets de carte HTML, les fonctions de contenu de cellule, etc.", + "embed-to-angular-template": "Intégrer dans un modèle HTML Angular", + "embed-to-angular-template-text": "En utilisant le code suivant, vous pouvez intégrer une image dans un modèle HTML Angular utilisé pour les composants.
Cela inclut le widget Markdown, la section HTML dans l'éditeur de widget, les actions personnalisées, etc." + }, + "image-input": { + "drop-images-or": "Glissez-déposez des images ou", + "drag-and-drop": "Glisser & Déposer", + "or": "ou", + "browse": "Parcourir", + "no-images": "Aucune image sélectionnée", + "images": "images" + }, + "import": { + "no-file": "Aucun fichier sélectionné", + "drop-file": "Déposez un fichier JSON ou cliquez pour sélectionner un fichier à téléverser.", + "drop-json-file-or": "Glissez-déposez un fichier JSON ou", + "drop-file-csv": "Déposez un fichier CSV ou cliquez pour sélectionner un fichier à téléverser.", + "drop-file-csv-or": "Glissez-déposez un fichier CSV ou", + "column-value": "Valeur", + "column-title": "Titre", + "column-example": "Exemple de valeur", + "column-key": "Clé attribut/télémétrie", + "credentials": "Identifiants", + "csv-delimiter": "Délimiteur CSV", + "csv-first-line-header": "La première ligne contient les noms de colonnes", + "csv-update-data": "Mettre à jour les attributs/télémétrie", + "details": "Détails", + "import-csv-number-columns-error": "Un fichier doit contenir au moins deux colonnes", + "import-csv-invalid-format-error": "Format de fichier invalide. Ligne : '{{line}}'", + "column-type": { + "name": "Nom", + "type": "Type", + "label": "Étiquette", + "column-type": "Type de colonne", + "client-attribute": "Attribut client", + "shared-attribute": "Attribut partagé", + "server-attribute": "Attribut serveur", + "timeseries": "Série temporelle", + "entity-field": "Champ de l'entité", + "access-token": "Jeton d'accès", + "x509": "X.509", + "mqtt": { + "client-id": "ID client MQTT", + "user-name": "Nom d'utilisateur MQTT", + "password": "Mot de passe MQTT" + }, + "lwm2m": { + "client-endpoint": "Nom du client endpoint LwM2M", + "security-config-mode": "Mode de configuration de sécurité LwM2M", + "client-identity": "Identité client LwM2M", + "client-key": "Clé du client LwM2M", + "client-cert": "Clé publique du client LwM2M", + "bootstrap-server-security-mode": "Mode de sécurité du serveur bootstrap LwM2M", + "bootstrap-server-secret-key": "Clé secrète du serveur bootstrap LwM2M", + "bootstrap-server-public-key-id": "Clé publique ou ID du serveur bootstrap LwM2M", + "lwm2m-server-security-mode": "Mode de sécurité du serveur LwM2M", + "lwm2m-server-secret-key": "Clé secrète du serveur LwM2M", + "lwm2m-server-public-key-id": "Clé publique ou ID du serveur LwM2M" + }, + "snmp": { + "host": "Hôte SNMP", + "port": "Port SNMP", + "version": "Version SNMP (v1, v2c ou v3)", + "community-string": "Chaîne de communauté SNMP" + }, + "isgateway": "Est une passerelle", + "activity-time-from-gateway-device": "Heure d'activité depuis le périphérique passerelle", + "description": "Description", + "routing-key": "Clé Edge", + "secret": "Secret Edge" + }, + "stepper-text": { + "select-file": "Sélectionner un fichier", + "configuration": "Configuration d'import", + "column-type": "Sélectionner le type de colonnes", + "creat-entities": "Création de nouvelles entités" + }, + "message": { + "create-entities": "{{count}} nouvelles entités ont été créées avec succès.", + "update-entities": "{{count}} entités ont été mises à jour avec succès.", + "error-entities": "Une erreur est survenue lors de la création de {{count}} entités." + } + }, + "scada": { + "symbols": "Symboles SCADA", + "search": "Rechercher un symbole", + "selected-symbols": "{ count, plural, =1 {1 symbole} other {# symboles} } sélectionné(s)", + "download-symbol": "Télécharger le symbole SCADA", + "export-symbol": "Exporter le symbole SCADA en JSON", + "import-symbol": "Importer un symbole SCADA depuis un JSON", + "upload-symbol": "Téléverser un symbole SCADA", + "update-symbol": "Mettre à jour le symbole SCADA", + "edit-symbol": "Éditer le symbole SCADA", + "symbol-details": "Détails du symbole SCADA", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "Aucun symbole trouvé", + "show-hidden-elements": "Afficher les éléments masqués", + "hide-hidden-elements": "Masquer les éléments masqués", + "delete-symbol": "Supprimer le symbole SCADA", + "delete-symbol-title": "Êtes-vous sûr de vouloir supprimer le symbole SCADA '{{imageTitle}}' ?", + "delete-symbol-text": "Attention, après confirmation le symbole SCADA sera irrécupérable.", + "delete-symbols-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 symbole SCADA} other {# symboles SCADA} } ?", + "delete-symbols-text": "Attention, après confirmation tous les symboles SCADA sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "include-system-symbols": "Inclure les symboles système", + "symbol-preview": "Aperçu du symbole", + "general": "Général", + "tags": "Tags", + "properties": "Propriétés", + "title": "Titre", + "description": "Description", + "search-tags": "Rechercher des tags", + "widget-size": "Taille du widget", + "cols": "colonnes", + "rows": "rangées", + "state-render-function": "Fonction de rendu d’état", + "preview": "Aperçu", + "preview-widget-action-text": "Action du widget '{{type}}' exécutée avec succès !", + "no-symbol": "Aucun symbole SCADA", + "no-symbol-selected": "Aucun symbole SCADA sélectionné", + "clear-symbol": "Effacer le symbole SCADA", + "browse-symbol-from-gallery": "Parcourir un symbole SCADA dans la galerie", + "zoom-in": "Agrandir", + "zoom-out": "Réduire", + "create-widget": "Créer un widget", + "create-widget-from-symbol": "Créer un widget à partir du symbole SCADA", + "hidden": "masqué", + "tag": { + "tag": "Tag", + "on-click-action": "Action au clic", + "no-tags": "Aucun tag configuré", + "delete-tag-text": "Êtes-vous sûr de vouloir supprimer le tag
{{tag}} de l’élément {{elementType}} ?", + "update-tag": "Mettre à jour le tag", + "enter-tag": "Saisir le tag", + "tag-settings": "Paramètres du tag", + "remove-tag": "Supprimer le tag", + "add-tag": "Ajouter un tag" + }, + "behavior": { + "behavior": "Comportement", + "id": "Id", + "name": "Nom", + "type": "Type", + "no-behaviors": "Aucun comportement configuré", + "add-behavior": "Ajouter un comportement", + "type-action": "Action", + "type-value": "Valeur", + "type-widget-action": "Action de widget", + "behavior-settings": "Paramètres de comportement", + "remove-behavior": "Supprimer le comportement", + "hint": "Indice", + "group-title": "Titre du groupe", + "value-type": "Type de valeur", + "default-value": "Valeur par défaut", + "true-label": "Étiquette vraie", + "false-label": "Étiquette fausse", + "state-label": "Étiquette d’état", + "default-payload": "Charge utile par défaut", + "not-unique-behavior-ids-error": "Les identifiants de comportement doivent être uniques !", + "default-settings": "Paramètres par défaut" + }, + "symbol": { + "symbol": "Symbole SCADA", + "fluid-presence": "Présence de fluide", + "fluid-presence-hint": "Indique si un fluide est présent dans le tuyau.", + "fluid-present": "Fluide présent", + "present": "Présent", + "absent": "Absent", + "flow-presence": "Présence d'écoulement", + "flow-presence-hint": "Indique si un fluide s'écoule dans le tuyau.", + "flow-present": "Écoulement présent", + "flow-direction": "Direction de l'écoulement", + "flow-direction-hint": "Indique la direction de l'écoulement du fluide.", + "forward": "Avant", + "reverse": "Arrière", + "flow-animation-speed": "Vitesse de l'animation de l'écoulement", + "flow-animation-speed-hint": "Valeur décimale indiquant la vitesse de l'animation de l'écoulement. 1 - vitesse normale, 0 - pas d'animation, < 1 - animation plus lente, > 1 - animation plus rapide.", + "leak": "Fuite", + "leak-hint": "Indique s'il y a une fuite dans le tuyau.", + "leak-present": "Fuite présente", + "fluid-color": "Couleur du fluide", + "pipe-color": "Couleur du tuyau", + "horizontal-pipe": "Tuyau horizontal", + "vertical-pipe": "Tuyau vertical", + "horizontal-fluid-color": "Couleur du fluide horizontal", + "vertical-fluid-color": "Couleur du fluide vertical", + "left-pipe": "Tuyau gauche", + "right-pipe": "Tuyau droit", + "top-pipe": "Tuyau supérieur", + "bottom-pipe": "Tuyau inférieur", + "left-fluid-color": "Couleur du fluide gauche", + "right-fluid-color": "Couleur du fluide droit", + "top-fluid-color": "Couleur du fluide supérieur", + "bottom-fluid-color": "Couleur du fluide inférieur", + "display": "Affichage", + "display-format": "Format d'affichage", + "value": "Valeur", + "decimals": "Décimales", + "units": "Unités", + "flow-meter-value-hint": "Valeur décimale affichée sur le débitmètre", + "value-hint": "Valeur décimale indiquant la valeur actuelle", + "running": "En fonctionnement", + "running-hint": "Indique si le composant est en état de fonctionnement.", + "warning-state": "État d'avertissement", + "warning": "Avertissement", + "warning-click": "Clic d'avertissement", + "warning-state-hint": "Indique si le composant est en état d'avertissement.", + "critical-state": "État critique", + "critical": "Critique", + "critical-click": "Clic critique", + "critical-state-hint": "Indique si le composant est en état critique.", + "critical-state-animation": "Animation en état critique", + "critical-state-animation-hint": "Permet d'activer l'animation clignotante lorsque le composant est en état critique.", + "warning-critical-state-animation": "Animation état avertissement/critique", + "warning-critical-state-animation-hint": "Permet d'activer l'animation clignotante lorsque le composant est en état d'avertissement ou critique.", + "animation": "Animation", + "broken": "Cassé", + "broken-hint": "Indique si le composant est cassé.", + "on-display-click": "Au clic sur l'affichage", + "on-display-click-hint": "Action déclenchée lorsque l'utilisateur clique sur l'affichage.", + "pipe": "Tuyau", + "default-border-color": "Couleur de bordure par défaut", + "active-border-color": "Couleur de bordure active", + "warning-border-color": "Couleur de bordure d'avertissement", + "critical-border-color": "Couleur de bordure critique", + "background-color": "Couleur d'arrière-plan", + "rotation-animation-speed": "Vitesse de l'animation de rotation", + "rotation-animation-speed-hint": "Valeur décimale indiquant la vitesse de l'animation de rotation. 1 - vitesse normale, 0 - pas d'animation, < 1 - animation plus lente, > 1 - animation plus rapide.", + "on-click": "Au clic", + "on-click-hint": "Action déclenchée lorsque l'utilisateur clique sur le composant.", + "connectors-positions": "Positions des connecteurs", + "right-connector": "Connecteur droit", + "right-top-connector": "Connecteur supérieur droit", + "right-bottom-connector": "Connecteur inférieur droit", + "left-connector": "Connecteur gauche", + "left-top-connector": "Connecteur supérieur gauche", + "left-bottom-connector": "Connecteur inférieur gauche", + "top-left-connector": "Connecteur en haut à gauche", + "top-right-connector": "Connecteur en haut à droite", + "top-connector": "Connecteur supérieur", + "bottom-connector": "Connecteur inférieur", + "running-color": "Couleur en fonctionnement", + "stopped-color": "Couleur à l'arrêt", + "stopped": "À l'arrêt", + "warning-color": "Couleur d'avertissement", + "critical-color": "Couleur critique", + "opened": "Ouvert", + "opened-hint": "Indique si le composant est à l'état ouvert.", + "open": "Ouvrir", + "open-hint": "Action déclenchée lorsque l'utilisateur clique pour ouvrir le composant.", + "close": "Fermer", + "close-hint": "Action déclenchée lorsque l'utilisateur clique pour fermer le composant.", + "close-state-animation": "Animation en état fermé", + "close-state-animation-hint": "Permet d'activer l'animation clignotante lorsque le composant est en état fermé.", + "opened-color": "Couleur d'ouverture", + "closed-color": "Couleur de fermeture", + "opened-rotation-angle": "Angle de rotation à l'ouverture", + "closed-rotation-angle": "Angle de rotation à la fermeture", + "tank-capacity": "Capacité du réservoir", + "tank-capacity-hint": "Valeur décimale indiquant la capacité totale du réservoir.", + "current-volume": "Volume actuel", + "current-volume-hint": "Valeur décimale indiquant le volume actuellement occupé.", + "tank-color": "Couleur du réservoir", + "value-box": "Boîte de valeur", + "value-text": "Texte de valeur", + "scale": "Échelle", + "transparent-mode": "Mode transparent", + "major-ticks": "Graduations principales", + "intervals": "Intervalles", + "major-ticks-color": "Couleur des graduations principales", + "normal": "Normal", + "minor-ticks": "Graduations secondaires", + "minor-ticks-color": "Couleur des graduations secondaires", + "temperature": "Température", + "temperature-hint": "Valeur décimale indiquant la température actuelle.", + "update-temperature": "Mettre à jour la température", + "update-temperature-hint": "Action déclenchée lorsque l'utilisateur clique pour modifier la température actuelle.", + "run": "Démarrer", + "run-hint": "Action déclenchée lorsque l'utilisateur clique pour démarrer le composant.", + "stop": "Arrêter", + "stop-hint": "Action déclenchée lorsque l'utilisateur clique pour arrêter le composant.", + "temperature-step": "Incrément de température", + "heat-pump-color": "Couleur de la pompe à chaleur", + "power-button-background": "Arrière-plan du bouton de mise sous tension", + "value-box-background": "Arrière-plan de la boîte de valeur", + "value-units": "Unités de valeur", + "filtration-mode": "Mode de filtration", + "filtration-mode-hint": "Valeur entière indiquant le mode de filtration actuel.", + "filtration-mode-update": "État de mise à jour du mode de filtration", + "filtration-mode-update-hint": "Action déclenchée lorsque l'utilisateur clique pour modifier le mode de filtration actuel.", + "filter-mode": "Filtration", + "waste-mode": "Déchet", + "backwash-mode": "Contre-lavage", + "recirculate-mode": "Recirculation", + "rinse-mode": "Rinçage", + "closed-mode": "Fermé", + "sand-filter-color": "Couleur du filtre à sable", + "mode-box-background": "Arrière-plan de la boîte de mode", + "border-color": "Couleur de bordure", + "label-color": "Couleur de l'étiquette", + "water-leak-hint": "Indique s'il y a une fuite.", + "default-color": "Couleur par défaut", + "leak-color": "Couleur de fuite", + "full-value": "Valeur maximale", + "full-value-hint": "Valeur décimale indiquant la valeur maximale.", + "label": "Étiquette", + "icon": "Icône", + "button-color": "Couleur du bouton", + "on-label": "Texte de l'étiquette 'On'", + "off-label": "Texte de l'étiquette 'Off'", + "arrow-presence": "Présence de flèche", + "arrow-presence-hint": "Indique si une flèche est présente dans le connecteur.", + "arrow-present": "Flèche présente", + "arrow-direction": "Direction de la flèche/animation", + "arrow-direction-hint": "Indique la direction de l'écoulement.", + "flow-animation": "Animation d'écoulement", + "flow-animation-hint": "Indique si une animation est présente dans le connecteur.", + "flow": "Écoulement", + "flow-line": "Ligne", + "flow-line-style": "Style de ligne", + "flow-style-hint": "Définissez les valeurs de Tiret et d’Espace de manière à ce que leur somme soit divisible par 100 sans reste pour une synchronisation parfaite de l’animation.", + "flow-dash-cap": "Extrémité des tirets d'écoulement", + "dash-cap-butt": "Butée", + "dash-cap-round": "Arrondi", + "dash-cap-square": "Carré", + "dash": "Tiret", + "gap": "Espace", + "main-line": "Ligne principale", + "line": "Ligne", + "line-color": "Couleur de ligne", + "arrow-color": "Couleur de la flèche", + "target-value": "Valeur cible", + "target-value-hint": "Indique le point cible sur l'échelle.", + "min-max-value": "Valeur min et max", + "min-value": "Min", + "max-value": "Max", + "progress-bar": "Barre de progression", + "progress-arrow": "Flèche de progression", + "warning-scale-color": "Couleur d'échelle d'avertissement", + "critical-scale-color": "Couleur d'échelle critique", + "scale-color": "Couleur de l'échelle", + "target": "Cible", + "high-warning-state": "État d'avertissement élevé", + "show-high-warning-scale": "Afficher l'échelle d'avertissement élevé", + "high-warning-scale": "Échelle d'avertissement élevé", + "high-warning-state-hint": "Une valeur décimale indique une plage d'avertissement élevé jusqu'à une valeur critique élevée ou maximale.", + "low-warning-state": "État d'avertissement faible", + "show-low-warning-scale": "Afficher l'échelle d'avertissement faible", + "low-warning-scale": "Échelle d'avertissement faible", + "low-warning-state-hint": "Une valeur décimale indique une plage d'avertissement faible jusqu'à une valeur critique faible ou minimale.", + "high-critical-state": "État critique élevé", + "show-high-critical-scale": "Afficher l'échelle critique élevée", + "high-critical-scale": "Échelle critique élevée", + "high-critical-state-hint": "Une valeur décimale indique une plage critique élevée jusqu'à la valeur maximale de l'échelle.", + "low-critical-state": "État critique faible", + "show-low-critical-scale": "Afficher l'état critique faible", + "low-critical-scale": "État critique faible", + "low-critical-state-hint": "Une valeur décimale indique une plage critique faible jusqu'à la valeur minimale de l'échelle.", + "filter-color": "Couleur du filtre", + "colors": "Couleurs", + "indicator-colors": "Couleurs des indicateurs", + "enabled": "Activé", + "disabled": "Désactivé", + "on": "ON", + "off": "OFF", + "on-off-state": "État On/Off", + "on-off-state-hint": "Indique si le composant est en état activé ou désactivé.", + "on-update-state": "État de mise à jour - ON", + "on-update-state-hint": "Action déclenchée lorsque l'utilisateur clique pour passer à l'état ON.", + "off-update-state": "État de mise à jour - OFF", + "off-update-state-hint": "Action déclenchée lorsque l'utilisateur clique pour passer à l'état OFF.", + "voltage": "Tension", + "input-voltage": "Tension d'entrée", + "input-voltage-hint": "Une valeur décimale indique la tension d'entrée.", + "output-voltage": "Tension de sortie", + "output-voltage-hint": "Une valeur décimale indique la tension de sortie.", + "first-phase-voltage": "Tension de première phase", + "second-phase-voltage": "Tension de deuxième phase", + "third-phase-voltage": "Tension de troisième phase", + "phase-voltage-hint": "Une valeur décimale indique la tension de la phase en cours", + "voltage-hint": "Une valeur décimale indique la tension actuelle", + "current-voltage-color": "Couleur de la tension actuelle", + "phase-indicator-color": "Couleur de l'indicateur de phase", + "measured": "Mesuré", + "measured-hint": "Une valeur décimale indique la consommation d'énergie en kilowattheures", + "day-rate": "Tarif jour", + "night-rate": "Tarif nuit", + "off-peak-rate": "Tarif heures creuses", + "peak-rate": "Tarif de pointe", + "export-rate": "Tarif d'exportation", + "operating-mode": "Mode de fonctionnement", + "bypass-mode": "Contournement", + "operating-mode-hint": "Valeur entière indiquant le mode de fonctionnement actuel (0 - OFF, 1 - ON, 2 - BYPASS)", + "connected": "Connecté", + "connected-hint": "Indique si le composant est connecté.", + "disconnected": "Déconnecté", + "indicator": "Indicateur", + "operation-mode": "Mode opératoire", + "operation-mode-hint": "Indique si l'onduleur est en mode secteur ou onduleur.", + "operation-mode-indicators-color": "Couleur des indicateurs de mode opératoire", + "mains-on-mode": "Secteur activé", + "inverter-on-mode": "Onduleur activé", + "charging-mode": "Mode de charge", + "charging-mode-hint": "Valeur entière indiquant le mode de charge actuel (1 - Bulk, 2 - Absorption, 3 - Float)", + "charging-mode-indicators-color": "Couleur des indicateurs de charge", + "inverter-faults": "Défauts", + "inverter-fault-indicators-color": "Couleur des indicateurs de défaut", + "overload-fault": "Surcharge", + "overload-fault-hint": "Indique si l'onduleur est en surcharge.", + "low-battery-fault": "Batterie faible", + "low-battery-fault-hint": "Indique si la batterie est excessivement déchargée.", + "temperature-fault": "Température", + "temperature-fault-hint": "Indique une température élevée dans l'onduleur.", + "triangle": "Triangle", + "socket": "Prise", + "left-button": "Bouton gauche", + "right-button": "Bouton droit", + "alarm-colors": "Couleurs d'alarme", + "hook-color": "Couleur du crochet" + } + }, + "item": { + "selected": "Sélectionné" + }, + "js-func": { + "no-return-error": "La fonction doit retourner une valeur !", + "return-type-mismatch": "La fonction doit retourner une valeur de type '{{type}}' !", + "tidy": "Propre", + "mini": "Mini", + "modules": "Modules", + "remove-module": "Supprimer le module", + "no-modules": "Aucun module configuré", + "add-module": "Ajouter un module", + "module-alias": "Alias", + "invalid-module-alias-name": "Nom d'alias invalide", + "module-resource": "Ressource du module JS", + "not-unique-module-aliases-error": "Les alias des modules doivent être uniques !", + "show-module-info": "Afficher les infos du module", + "show-module-source-code": "Afficher le code source du module", + "module-members": "Membres du module", + "module-no-members": "Le module n'a aucun membre exporté", + "module-load-error": "Erreur de chargement du module", + "source-code": "Code source", + "source-code-load-error": "Erreur de chargement du code source", + "no-js-module-text": "Aucun module JS trouvé", + "no-js-module-matching": "Aucun module JS correspondant à '{{module}}' trouvé." + }, + "key-val": { + "key": "Clé", + "value": "Valeur", + "remove-entry": "Supprimer l'entrée", + "add-entry": "Ajouter une entrée", + "no-data": "Aucune entrée" + }, + "layout": { + "layout": "Disposition", + "layouts": "Dispositions", + "manage": "Gérer les dispositions", + "settings": "Paramètres de disposition", + "color": "Couleur", + "main": "Principal", + "right": "Droite", + "left": "Gauche", + "select": "Sélectionner la disposition cible", + "percentage-width": "Largeur en pourcentage (%)", + "fixed-width": "Largeur fixe (px)", + "left-width": "Colonne gauche (%)", + "right-width": "Colonne droite (%)", + "pick-fixed-side": "Côté fixe : ", + "layout-fixed-width": "Largeur fixe (px)", + "value-min-error": "La valeur doit être supérieure à {{min}}{{unit}}", + "value-max-error": "La valeur doit être inférieure à {{max}}{{unit}}", + "layout-fixed-width-required": "La largeur fixe est requise", + "right-width-percentage-required": "Le pourcentage pour la colonne de droite est requis", + "left-width-percentage-required": "Le pourcentage pour la colonne de gauche est requis", + "divider": "Séparateur", + "right-side": "Disposition côté droit", + "left-side": "Disposition côté gauche", + "add-new-breakpoint": "Ajouter un nouveau point de rupture", + "breakpoint": "Point de rupture", + "breakpoints": "Points de rupture", + "copy-from": "Copier depuis", + "size": "Taille", + "delete-breakpoint-title": "Êtes-vous sûr de vouloir supprimer le point de rupture '{{name}}' ?", + "delete-breakpoint-text": "Veuillez noter qu'après confirmation, le point de rupture sera irrécupérable et les paramètres reviendront à ceux du point de rupture par défaut." + }, + "legend": { + "direction": "Direction", + "position": "Position", + "show-values": "Afficher les valeurs", + "min-option": "Min", + "max-option": "Max", + "average-option": "Moyenne", + "total-option": "Total", + "latest-option": "Dernière", + "sort-legend": "Trier les clés de données dans la légende", + "show-max": "Afficher la valeur maximale", + "show-min": "Afficher la valeur minimale", + "show-avg": "Afficher la moyenne", + "show-total": "Afficher le total", + "show-latest": "Afficher la dernière valeur", + "settings": "Paramètres de la légende", + "min": "min", + "max": "max", + "avg": "moy", + "total": "total", + "latest": "dernière", + "Min": "Min", + "Max": "Max", + "Avg": "Moy", + "Total": "Total", + "Latest": "Dernière", + "comparison-time-ago": { + "previousInterval": "(intervalle précédent)", + "customInterval": "(intervalle personnalisé)", + "days": "(jour précédent)", + "weeks": "(semaine précédente)", + "months": "(mois précédent)", + "years": "(année précédente)" + }, + "column-title": "Titre de la colonne", + "label": "Étiquette", + "value": "Valeur" + }, + "login": { + "login": "Connexion", + "request-password-reset": "Demander une réinitialisation du mot de passe", + "reset-password": "Réinitialiser le mot de passe", + "create-password": "Créer un mot de passe", + "two-factor-authentication": "Authentification à deux facteurs", + "passwords-mismatch-error": "Les mots de passe saisis doivent être identiques !", + "password-again": "Ressaisir le mot de passe", + "sign-in": "Veuillez vous connecter", + "username": "Nom d'utilisateur (email)", + "remember-me": "Se souvenir de moi", + "forgot-password": "Mot de passe oublié ?", + "password-reset": "Réinitialisation du mot de passe", + "expired-password-reset-message": "Vos identifiants ont expiré ! Veuillez créer un nouveau mot de passe.", + "new-password": "Nouveau mot de passe", + "new-password-again": "Confirmer le nouveau mot de passe", + "password-link-sent-message": "Le lien de réinitialisation a été envoyé", + "email": "Email", + "invalid-email-format": "Format d'email invalide.", + "login-with": "Connexion avec {{name}}", + "or": "ou", + "error": "Erreur de connexion", + "verify-your-identity": "Vérifiez votre identité", + "select-way-to-verify": "Sélectionnez une méthode de vérification", + "resend-code": "Renvoyer le code", + "resend-code-wait": "Renvoyer le code dans { time, plural, =1 {1 seconde} other {# secondes} }", + "try-another-way": "Essayer une autre méthode", + "totp-auth-description": "Veuillez saisir le code de sécurité depuis votre application d'authentification.", + "totp-auth-placeholder": "Code", + "sms-auth-description": "Un code de sécurité a été envoyé sur votre téléphone au {{contact}}.", + "sms-auth-placeholder": "Code SMS", + "email-auth-description": "Un code de sécurité a été envoyé à votre adresse email à {{contact}}.", + "email-auth-placeholder": "Code email", + "backup-code-auth-description": "Veuillez saisir l’un de vos codes de secours.", + "backup-code-auth-placeholder": "Code de secours", + "activation-link-expired": "Le lien d'activation a expiré", + "activation-link-expired-message": "Le lien d’activation de votre profil a expiré. Vous pouvez revenir à la page de connexion pour recevoir un nouvel email.", + "reset-password-link-expired": "Le lien de réinitialisation du mot de passe a expiré", + "reset-password-link-expired-message": "Le lien de réinitialisation du mot de passe a expiré. Vous pouvez revenir à la page de connexion pour recevoir un nouvel email." + }, + "mobile": { + "add-application": "Ajouter une application", + "app-id": "ID de l'application", + "app-id-required": "L'ID de l'application est requis", + "app-id-pattern": "Format d'ID de l'application invalide", + "app-store-link": "Lien App Store", + "app-store-link-required": "Le lien App Store est requis", + "application-details": "Détails de l'application", + "application-package": "Paquet de l'application", + "application-secret": "Clé secrète de l'application", + "application-secret-required": "La clé secrète de l'application est requise", + "application": "Application", + "applications": "Applications", + "copy-app-id": "Copier l'ID de l'application", + "copy-app-store-link": "Copier le lien App Store", + "copy-application-package": "Copier le paquet de l'application", + "copy-application-secret": "Copier la clé secrète de l'application", + "copy-google-play-link": "Copier le lien Google Play", + "copy-sha256-certificate-fingerprints": "Copier les empreintes de certificat SHA256", + "delete-application": "Supprimer l'application", + "delete-application-button-text": "Je comprends les conséquences, supprimer l'application", + "delete-application-text": "Cette action est irréversible. Cela supprimera définitivement votre application.
Si vous ne souhaitez pas la supprimer définitivement, vous pouvez suspendre temporairement l'application.
Pour supprimer l'application quand même, veuillez saisir \"{{phrase}}\" pour confirmer.", + "delete-application-title-short": "Êtes-vous sûr de vouloir supprimer l'application '{{name}}' ?", + "delete-application-text-short": "Attention, après confirmation l'application et toutes ses données associées seront irrécupérables.", + "delete-application-phrase": "supprimer l'application", + "delete-applications-bundle-text": "Attention, après confirmation le bundle mobile et toutes les données associées seront irrécupérables.", + "delete-applications-bundle-title": "Êtes-vous sûr de vouloir supprimer le bundle mobile '{{bundleName}}' ?", + "generate-application-secret": "Générer une clé secrète", + "google-play-link": "Lien Google Play", + "google-play-link-required": "Le lien Google Play est requis", + "latest-version": "Dernière version", + "min-version": "Version minimale", + "invalid-version-pattern": "Format de version invalide. Veuillez utiliser le format : majeur.mineur.correctif (ex. : 1.0.0).", + "mobile-center": "Centre mobile", + "mobile-package": "Paquet de l'application", + "mobile-package-max-length": "Le paquet de l'application doit contenir moins de 256 caractères", + "mobile-package-required": "Le paquet de l'application est requis.", + "mobile-package-pattern": "Format du paquet de l'application invalide", + "no-application": "Aucune application trouvée", + "no-bundles": "Aucun bundle trouvé", + "platform-type": "Type de plateforme", + "search-application": "Rechercher des applications", + "search-bundles": "Rechercher des bundles", + "set": "Définir", + "sha256-certificate-fingerprints": "Empreintes de certificat SHA256", + "sha256-certificate-fingerprints-required": "Les empreintes de certificat SHA256 sont requises", + "sha256-certificate-fingerprints-pattern": "Format d'empreinte SHA256 invalide", + "show-hidden-pages": "Afficher les pages cachées", + "status": "Statut", + "status-type": { + "deprecated": "Obsolète", + "draft": "Brouillon", + "published": "Publié", + "suspended": "Suspendu" + }, + "store-information": "Informations sur la boutique", + "version-information": "Informations sur la version", + "min-version-release-notes": "Notes de version minimale", + "latest-version-release-notes": "Notes de la dernière version", + "bundle": "Bundle", + "bundles": "Bundles", + "add-bundle": "Ajouter un bundle", + "title": "Titre", + "title-required": "Le titre est requis", + "title-cannot-contain-only-spaces": "Le titre ne peut pas contenir uniquement des espaces", + "title-max-length": "Le titre doit contenir moins de 256 caractères", + "oauth-clients": "Clients OAuth 2.0", + "android-app": "Application Android", + "android-application": "Application Android", + "ios-app": "Application iOS", + "ios-application": "Application iOS", + "invalid-store-link": "Lien de boutique invalide", + "enable-oauth": "Activer OAuth 2.0", + "enable-self-registration": "Activer l'auto-enregistrement", + "edit-bundle": "Modifier le bundle", + "description": "Description", + "basic-settings": "Paramètres de base", + "no-application-matching": "Aucune application correspondant à '{{entity}}' trouvée.", + "no-bundle-matching": "Aucun bundle correspondant à '{{entity}}' trouvé.", + "application-required": "L'application est requise.", + "bundle-required": "Le bundle est requis.", + "no-application-text": "Aucune application trouvée", + "no-bundle-text": "Aucun bundle trouvé", + "layout": "Disposition", + "pages": "Pages", + "hide-all-pages": "Masquer toutes les pages", + "reset-to-default-pages": "Réinitialiser aux pages par défaut", + "add-specific-page": "Ajouter une page spécifique", + "visible": "Visible", + "hidden": "Masqué", + "reset-to-page-default": "Réinitialiser la page à sa valeur par défaut", + "mobile-599": "Mobile (max 599px)", + "tablet-959": "Tablette (max 959px)", + "max-element-number": "Nombre maximal d'éléments", + "page-name": "Nom de la page", + "page-name-required": "Le nom de la page est requis.", + "page-name-cannot-contain-only-spaces": "Le nom de la page ne peut contenir uniquement des espaces.", + "page-name-max-length": "Le nom de la page doit contenir moins de 256 caractères", + "page-type": "Type de page", + "pages-types": { + "dashboard": "Tableau de bord", + "web-view": "Vue Web", + "custom": "Personnalisé" + }, + "url": "URL", + "invalid-url-format": "Format d'URL invalide", + "path": "Chemin", + "invalid-path-format": "Format de chemin invalide", + "custom-page": "Page personnalisée", + "edit-page": "Modifier la page", + "edit-custom-page": "Modifier la page personnalisée", + "delete-page": "Supprimer la page", + "qr-code-widget": "Widget code QR", + "type-here": "Tapez ici", + "configuration-dialog": "Dialogue de configuration", + "configuration-app": "Application de configuration", + "configuration-step": { + "prepare-environment-title": "Préparer l'environnement de développement", + "prepare-environment-text": "L'application mobile Flutter ThingsBoard nécessite le SDK Flutter. Suivez les instructions pour configurer le SDK Flutter.", + "get-source-code-title": "Obtenir le code source de l'application", + "get-source-code-text": "Vous pouvez obtenir le code source de l'application mobile Flutter ThingsBoard en le clonant depuis le dépôt GitHub :", + "configure-api-title": "Configurer le point de terminaison de l'API ThingsBoard", + "configure-api-text": "Ouvrez le projet flutter_thingsboard_pe_app dans votre éditeur/IDE. Modifiez :", + "configure-api-hint": "Définissez la valeur de la constante thingsBoardApiEndpoint pour correspondre au point de terminaison API de votre instance ThingsBoard. N'utilisez pas les noms d'hôte “localhost” ou “127.0.0.1”.", + "run-app-title": "Lancer l'application", + "run-app-text": "Lancez l'application comme décrit dans votre IDE.\nSi vous utilisez le terminal, exécutez l'application avec la commande suivante :", + "more-information": "Des informations détaillées sont disponibles dans notre documentation de démarrage.", + "getting-started": "Commencer", + "configure-package-title": "Configurer le paquet de l'application", + "configure-package-text": "Vous pouvez modifier manuellement le paquet de l'application ou utiliser un outil CLI tiers.", + "configure-package-text-install": "Pour installer l'outil Rename CLI, exécutez la commande suivante :", + "configure-package-run-commands": "Exécutez ces commandes dans le répertoire racine de votre projet :" + } + }, + "notification": { + "action-button": "Bouton d'action", + "action-type": "Type d'action", + "active": "Actif", + "add-notification-recipients-group": "Ajouter un groupe de destinataires de notification", + "add-notification-template": "Ajouter un modèle de notification", + "add-recipient": "Ajouter un destinataire", + "add-recipients": "Ajouter des destinataires", + "add-rule": "Ajouter une règle", + "add-stage": "Ajouter une étape", + "add-template": "Ajouter un modèle", + "after": "Après", + "alarm-assignment-trigger-settings": "Paramètres du déclencheur d'affectation d'alarme", + "alarm-comment-trigger-settings": "Paramètres du déclencheur de commentaire d'alarme", + "alarm-trigger-settings": "Paramètres du déclencheur d'alarme", + "all": "Tous", + "api-feature-hint": "Si le champ est vide, le déclencheur s'appliquera à toutes les fonctionnalités API", + "api-usage-trigger-settings": "Paramètres du déclencheur d'utilisation de l'API", + "new-platform-version-trigger-settings": "Paramètres du déclencheur de nouvelle version de la plateforme", + "rate-limits-trigger-settings": "Paramètres du déclencheur de dépassement de limites", + "task-processing-failure-trigger-settings": "Paramètres du déclencheur d'échec du traitement de tâche", + "at-least-one-should-be-selected": "Au moins un élément doit être sélectionné", + "basic-settings": "Paramètres de base", + "button-text": "Texte du bouton", + "button-text-required": "Le texte du bouton est requis", + "button-text-max-length": "Le texte du bouton doit contenir au maximum {{ length }} caractères", + "compose": "Composer", + "conversation": "Conversation", + "conversation-required": "La conversation est requise", + "copy-notification-template": "Copier le modèle de notification", + "copy-rule": "Copier la règle", + "copy-template": "Copier le modèle", + "create-new": "Créer nouveau", + "created": "Créé", + "customize-messages": "Personnaliser les messages", + "delete-notification-text": "Attention, après confirmation, la notification sera irrécupérable.", + "delete-notification-title": "Êtes-vous sûr de vouloir supprimer la notification ?", + "delete-notifications-text": "Attention, après confirmation, les notifications seront irrécupérables.", + "delete-notifications-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 notification} other {# notifications} } ?", + "delete-recipient-text": "Attention, après confirmation, le destinataire sera irrécupérable.", + "delete-recipient-title": "Êtes-vous sûr de vouloir supprimer le destinataire '{{recipientName}}' ?", + "delete-recipients-text": "Attention, après confirmation, les destinataires seront irrécupérables.", + "delete-recipients-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 destinataire} other {# destinataires} } ?", + "delete-request-text": "Attention, après confirmation, la requête sera irrécupérable.", + "delete-request-title": "Êtes-vous sûr de vouloir supprimer la requête ?", + "delete-requests-text": "Attention, après confirmation, les requêtes seront irrécupérables.", + "delete-requests-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 requête} other {# requêtes} } ?", + "delete-rule-text": "Attention, après confirmation, la règle sera irrécupérable.", + "delete-rule-title": "Êtes-vous sûr de vouloir supprimer la règle '{{ruleName}}' ?", + "delete-rules-text": "Attention, après confirmation, les règles seront irrécupérables.", + "delete-rules-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 règle} other {# règles} } ?", + "delete-template-text": "Attention, après confirmation, le modèle sera irrécupérable.", + "delete-template-title": "Êtes-vous sûr de vouloir supprimer le modèle '{{templateName}}' ?", + "delete-templates-text": "Attention, après confirmation, les modèles seront irrécupérables.", + "delete-templates-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 modèle} other {# modèles} } ?", + "deleted": "Supprimé", + "delivery-method": { + "delivery-method": "Méthode de livraison", + "email": "Email", + "email-preview": "Aperçu de la notification email", + "slack": "Slack", + "slack-preview": "Aperçu de la notification Slack", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Aperçu de la notification Microsoft Teams", + "sms": "SMS", + "sms-preview": "Aperçu de la notification SMS", + "web": "Web", + "web-preview": "Aperçu de la notification Web", + "mobile-app": "Application mobile", + "mobile-app-preview": "Aperçu de la notification mobile" + }, + "delivery-method-not-configure-click": "Méthode de livraison non configurée. Cliquez pour configurer.", + "delivery-method-not-configure-contact": "Méthode de livraison non configurée. Veuillez contacter votre administrateur système.", + "delivery-methods": "Méthodes de livraison", + "description": "Description", + "device-activity-trigger-settings": "Paramètres du déclencheur d'activité du dispositif", + "device-list-rule-hint": "Si le champ est vide, le déclencheur s'appliquera à tous les dispositifs", + "device-profiles-list-rule-hint": "Si le champ est vide, le déclencheur s'appliquera à tous les profils de dispositifs", + "disabled": "Désactivé", + "edge-trigger-settings": "Paramètres du déclencheur Edge", + "edge-list-rule-hint": "Si le champ est vide, le déclencheur s'appliquera à toutes les instances Edge", + "edit-notification-recipients-group": "Modifier le groupe de destinataires de notification", + "edit-notification-template": "Modifier le modèle de notification", + "edit-rule": "Modifier la règle", + "edit-template": "Modifier le modèle", + "enabled": "Activé", + "entities-limit-trigger-settings": "Paramètres du déclencheur de limite d'entités", + "entity-action-trigger-settings": "Paramètres du déclencheur d'action sur entité", + "entity-type": "Type d'entité", + "escalation-chain": "Chaîne d'escalade", + "failed-send": "Échecs d'envoi", + "fails": "{ count, plural, =1 {1 échec} other {# échecs} }", + "filter": "Filtre", + "first-recipient": "Premier destinataire", + "inactive": "Inactif", + "inbox": "Boîte de réception", + "notification-inbox": "Notifications / Boîte de réception", + "input-field-support-templatization": "Le champ de saisie prend en charge la templatisation.", + "input-fields-support-templatization": "Les champs de saisie prennent en charge la templatisation.", + "link": "Lien", + "link-required": "Le lien est requis", + "link-type": { + "dashboard": "Ouvrir le tableau de bord", + "link": "Ouvrir un lien URL" + }, + "loading-notifications": "Chargement des notifications...", + "management": "Gestion des notifications", + "mark-all-as-read": "Tout marquer comme lu", + "mark-as-read": "Marquer comme lu", + "message": "Message", + "message-required": "Le message est requis", + "message-max-length": "Le message doit contenir au maximum {{ length }} caractères", + "name": "Nom", + "name-required": "Le nom est requis", + "new-notification": "Nouvelle notification", + "no-inbox-notification": "Aucune notification trouvée", + "no-notification-request": "Aucune demande de notification", + "no-notification-templates": "Aucun modèle de notification trouvé", + "no-notifications-yet": "Aucune notification pour l’instant", + "no-recipients-notification": "Aucun destinataire pour cette notification", + "no-recipients-matching": "Aucun destinataire correspondant à '{{entity}}' trouvé.", + "no-recipients-text": "Aucun destinataire trouvé", + "no-rule": "Aucune règle configurée", + "no-rules-notification": "Aucune règle de notification", + "no-severity-found": "Aucune sévérité trouvée", + "no-severity-matching": "'{{severity}}' introuvable.", + "no-template-matching": "Aucune ressource correspondant à '{{template}}' trouvée.", + "not-found-slack-recipient": "Destinataire Slack introuvable", + "notification": "Notification", + "notification-center": "Centre de notifications", + "notification-tap-action": "Action au clic sur la notification", + "notification-tap-action-hint": "Si non activé, le tableau de bord d’alarme par défaut sera utilisé", + "notify": "notifier", + "notify-again": "Notifier de nouveau", + "notify-alarm-action": { + "acknowledged": "Alarme reconnue", + "assigned": "Alarme assignée", + "cleared": "Alarme effacée", + "created": "Alarme créée", + "severity-changed": "Sévérité de l’alarme modifiée", + "unassigned": "Alarme désassignée" + }, + "notify-on": "Notifier lors de", + "notify-on-comment-update": "Notifier lors de la mise à jour d’un commentaire", + "notify-on-required": "Le champ 'Notifier lors de' est requis", + "notify-on-unassign": "Notifier lors du désassignement", + "notify-only-user-comments": "Notifier uniquement les commentaires des utilisateurs", + "only-rule-chain-lifecycle-failures": "Uniquement les échecs de cycle de vie de chaîne de règles", + "only-rule-node-lifecycle-failures": "Uniquement les échecs de cycle de vie de nœud de règles", + "platform-users": "Utilisateurs de la plateforme", + "rate-limits": "Limites de taux", + "rate-limits-hint": "Si le champ est vide, le déclencheur s’appliquera à toutes les limites de taux", + "recipient": "Destinataire", + "recipient-group": "Groupe de destinataires", + "recipient-type": { + "affected-tenant-administrators": "Administrateurs du locataire concerné", + "affected-user": "Utilisateur concerné", + "all-users": "Tous les utilisateurs", + "customer-users": "Utilisateurs client", + "system-administrators": "Administrateurs système", + "tenant-administrators": "Administrateurs du locataire", + "user-filters": "Filtre d'utilisateur", + "user-list": "Liste d’utilisateurs", + "users-entity-owner": "Utilisateurs du propriétaire de l'entité" + }, + "recipients": "Destinataires", + "notification-recipient": "Destinataire de la notification", + "notification-recipient-required": "Le destinataire de la notification est requis.", + "notification-recipients": "Notifications / Destinataires", + "recipients-count": "{ count, plural, =1 {1 destinataire} other {# destinataires} }", + "recipients-required": "Les destinataires sont requis", + "refresh-allow-delivery-method": "Actualiser les méthodes de livraison autorisées", + "request-search": "Rechercher une requête", + "request-status": { + "processing": "En traitement", + "scheduled": "Planifiée", + "sent": "Envoyée" + }, + "review": "Vérifier", + "rule": "Règle", + "rule-chain-list-rule-hint": "Si le champ est vide, le déclencheur s’appliquera à toutes les chaînes de règles", + "rule-engine-events-trigger-settings": "Paramètres du déclencheur d’événements du moteur de règles", + "rule-engine-filter": "Filtre du moteur de règles", + "rule-name": "Nom de la règle", + "rule-name-required": "Le nom est requis", + "rule-disable": "Désactiver la règle de notification", + "rule-enable": "Activer la règle de notification", + "rule-node-filter": "Filtre de nœud de règle", + "rules": "Règles", + "notification-rules": "Notifications / Règles", + "scheduler-later": "Planifier plus tard", + "search-notification": "Rechercher des notifications", + "search-recipients": "Rechercher des destinataires", + "search-rules": "Rechercher des règles", + "search-templates": "Rechercher des modèles", + "see-documentation": "Voir la documentation", + "selected-notifications": "{ count, plural, =1 {1 notification} other {# notifications} } sélectionnée(s)", + "selected-recipients": "{ count, plural, =1 {1 destinataire} other {# destinataires} } sélectionné(s)", + "selected-requests": "{ count, plural, =1 {1 requête} other {# requêtes} } sélectionnée(s)", + "selected-rules": "{ count, plural, =1 {1 règle} other {# règles} } sélectionnée(s)", + "selected-template": "{ count, plural, =1 {1 modèle} other {# modèles} } sélectionné(s)", + "send-notification": "Envoyer une notification", + "sent": "Envoyée", + "setup": "Configuration", + "notification-sent": "Notifications / Envoyées", + "set-entity-from-notification": "Définir l'entité à partir de la notification dans l'état du tableau de bord", + "slack-chanel-type": "Type de canal Slack", + "slack-chanel-types": { + "direct": "Message direct", + "private-channel": "Canal privé", + "public-channel": "Canal public" + }, + "start-from-scratch": "Commencer de zéro", + "status": "Statut", + "stop-escalation-alarm-status-become": "Arrêter l'escalade lorsque le statut de l'alarme devient :", + "subject": "Sujet", + "subject-required": "Le sujet est requis", + "subject-max-length": "Le sujet doit contenir au maximum {{ length }} caractères", + "template": "Modèle", + "template-name": "Nom du modèle", + "template-required": "Le modèle est requis", + "template-type": { + "alarm": "Alarme", + "alarm-assignment": "Attribution d'alarme", + "alarm-comment": "Commentaire d'alarme", + "api-usage-limit": "Limite d'utilisation de l'API", + "device-activity": "Activité de l'appareil", + "entities-limit": "Limite d'entités", + "entity-action": "Action sur l'entité", + "general": "Général", + "rule-engine-lifecycle-event": "Événement du cycle de vie du moteur de règles", + "rule-node": "Nœud de règle", + "new-platform-version": "Nouvelle version de la plateforme", + "rate-limits": "Limites de taux dépassées", + "edge-communication-failure": "Échec de communication avec l'Edge", + "edge-connection": "Connexion Edge", + "task-processing-failure": "Échec de traitement de tâche" + }, + "templates": "Modèles", + "notification-templates": "Notifications / Modèles", + "tenant-profiles-list-rule-hint": "Si le champ est vide, le déclencheur s’appliquera à tous les profils de locataire", + "tenants-list-rule-hint": "Si le champ est vide, le déclencheur s’appliquera à tous les locataires", + "threshold": "Seuil", + "theme-color": "Couleur du thème", + "time": "Temps", + "track-rule-node-events": "Suivre les événements des nœuds de règle", + "trigger": { + "alarm": "Alarme", + "alarm-assignment": "Attribution d'alarme", + "alarm-comment": "Commentaire d'alarme", + "api-usage-limit": "Limite d'utilisation de l'API", + "device-activity": "Activité de l'appareil", + "entities-limit": "Limite d'entités", + "entity-action": "Action sur l'entité", + "rule-engine-lifecycle-event": "Événement du cycle de vie du moteur de règles", + "new-platform-version": "Nouvelle version de la plateforme", + "rate-limits": "Limites de taux dépassées", + "edge-connection": "Connexion Edge", + "edge-communication-failure": "Échec de communication avec l'Edge", + "task-processing-failure": "Échec de traitement de tâche", + "trigger": "Déclencheur", + "trigger-required": "Le déclencheur est requis" + }, + "type": "Type", + "unread": "Non lu", + "updated": "Mis à jour", + "use-deprecated-webhook-connectors": "Utiliser les connecteurs Webhook obsolètes", + "use-old-api": "Utiliser l’ancienne API", + "use-template": "Utiliser le modèle", + "view-all": "Voir tout", + "warning": "Avertissement", + "webhook-url": "URL du Webhook", + "webhook-url-required": "L’URL du Webhook est requise", + "workflow-url": "URL du workflow", + "workflow-url-required": "L’URL du workflow est requise", + "channel-name": "Nom du canal", + "channel-name-required": "Le nom du canal est requis", + "settings": { + "notification-settings": "Paramètres de notification", + "reset-all": "Réinitialiser tous les paramètres", + "reset-all-title": "Êtes-vous sûr de vouloir réinitialiser le formulaire ?", + "reset-all-text": "Après confirmation, le formulaire de paramètres sera réinitialisé aux valeurs par défaut et sauvegardé.", + "type": "Type", + "enable-all": "Tout activer", + "disable-all": "Tout désactiver", + "delivery-not-configured": "Méthode de livraison non configurée" + } + }, + "ota-update": { + "add": "Ajouter un paquet", + "assign-firmware": "Micrologiciel assigné", + "assign-firmware-required": "Le micrologiciel assigné est requis", + "assign-software": "Logiciel assigné", + "assign-software-required": "Le logiciel assigné est requis", + "auto-generate-checksum": "Générer automatiquement la somme de contrôle", + "checksum": "Somme de contrôle", + "checksum-hint": "Si la somme de contrôle est vide, elle sera générée automatiquement", + "checksum-algorithm": "Algorithme de somme de contrôle", + "checksum-copied-message": "La somme de contrôle du paquet a été copiée dans le presse-papiers", + "change-firmware": "Changer le micrologiciel peut entraîner la mise à jour de { count, plural, =1 {1 appareil} other {# appareils} }.", + "change-software": "Changer le logiciel peut entraîner la mise à jour de { count, plural, =1 {1 appareil} other {# appareils} }.", + "chose-compatible-device-profile": "Le paquet téléchargé ne sera disponible que pour les appareils ayant le profil choisi.", + "chose-firmware-distributed-device": "Choisissez le micrologiciel à distribuer aux appareils", + "chose-software-distributed-device": "Choisissez le logiciel à distribuer aux appareils", + "content-type": "Type de contenu", + "copy-checksum": "Copier la somme de contrôle", + "copy-direct-url": "Copier l'URL directe", + "copyId": "Copier l'identifiant du paquet", + "copied": "Copié !", + "delete": "Supprimer le paquet", + "delete-ota-update-text": "Attention, après confirmation, la mise à jour OTA deviendra irrécupérable.", + "delete-ota-update-title": "Êtes-vous sûr de vouloir supprimer la mise à jour OTA '{{title}}' ?", + "delete-ota-updates-text": "Attention, après confirmation, toutes les mises à jour OTA sélectionnées seront supprimées.", + "delete-ota-updates-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 mise à jour OTA} other {# mises à jour OTA} } ?", + "description": "Description", + "direct-url": "URL directe", + "direct-url-copied-message": "L'URL directe du paquet a été copiée dans le presse-papiers", + "direct-url-required": "L'URL directe est requise", + "download": "Télécharger le paquet", + "drop-file": "Déposez un fichier de paquet ou cliquez pour en sélectionner un à téléverser.", + "drop-package-file-or": "Glissez-déposez un fichier de paquet ou", + "file-name": "Nom du fichier", + "file-size": "Taille du fichier", + "file-size-bytes": "Taille du fichier en octets", + "idCopiedMessage": "L'identifiant du paquet a été copié dans le presse-papiers", + "no-firmware-matching": "Aucun paquet OTA de micrologiciel compatible correspondant à '{{entity}}' trouvé.", + "no-firmware-text": "Aucun paquet OTA de micrologiciel compatible provisionné.", + "no-packages-text": "Aucun paquet trouvé", + "no-software-matching": "Aucun paquet OTA de logiciel compatible correspondant à '{{entity}}' trouvé.", + "no-software-text": "Aucun paquet OTA de logiciel compatible provisionné.", + "ota-update": "Mise à jour OTA", + "ota-update-details": "Détails de la mise à jour OTA", + "ota-updates": "Mises à jour OTA", + "package-file": "Fichier du paquet", + "package-type": "Type de paquet", + "packages-repository": "Dépôt de paquets", + "search": "Rechercher des paquets", + "selected-package": "{ count, plural, =1 {1 paquet} other {# paquets} } sélectionné", + "title": "Titre", + "title-required": "Le titre est requis.", + "title-max-length": "Le titre doit comporter moins de 256 caractères", + "types": { + "firmware": "Micrologiciel", + "software": "Logiciel" + }, + "upload-binary-file": "Téléverser un fichier binaire", + "use-external-url": "Utiliser une URL externe", + "version": "Version", + "version-required": "La version est requise.", + "version-tag": "Étiquette de version", + "version-tag-hint": "L'étiquette personnalisée doit correspondre à la version du paquet rapportée par votre appareil.", + "version-max-length": "La version doit comporter moins de 256 caractères", + "warning-after-save-no-edit": "Une fois le paquet téléversé, vous ne pourrez plus modifier le titre, la version, le profil de l'appareil ni le type de paquet." + }, + "position": { + "top": "Haut", + "bottom": "Bas", + "left": "Gauche", + "right": "Droite" + }, + "profile": { + "profile": "Profil", + "last-login-time": "Dernière connexion", + "change-password": "Changer le mot de passe", + "current-password": "Mot de passe actuel", + "copy-jwt-token": "Copier le jeton JWT", + "jwt-token": "Jeton JWT", + "token-valid-till": "Jeton valide jusqu'à", + "tokenCopiedSuccessMessage": "Le jeton JWT a été copié dans le presse-papiers", + "tokenCopiedWarnMessage": "Le jeton JWT est expiré ! Veuillez actualiser la page." + }, + "profiles": { + "profiles": "Profils" + }, + "security": { + "security": "Sécurité", + "general-settings": "Paramètres de sécurité généraux", + "access-token": "Jeton d'accès", + "access-token-required": "Le jeton d'accès est requis", + "clientId": "ID client", + "clientId-required": "L'ID client est requis", + "username": "Nom d'utilisateur", + "username-required": "Le nom d'utilisateur est requis", + "ca-cert": "Certificat CA", + "2fa": { + "2fa": "Authentification à deux facteurs", + "2fa-description": "L'authentification à deux facteurs protège votre compte contre les accès non autorisés. Il vous suffit de saisir un code de sécurité lors de votre connexion.", + "authenticate-with": "Vous pouvez vous authentifier avec :", + "disable-2fa-provider-text": "Désactiver {{name}} rendra votre compte moins sécurisé", + "disable-2fa-provider-title": "Êtes-vous sûr de vouloir désactiver {{name}} ?", + "get-new-code": "Obtenir un nouveau code", + "main-2fa-method": "Utiliser comme méthode principale d'authentification à deux facteurs", + "dialog": { + "activation-step-description-email": "Lors de votre prochaine connexion, un code de sécurité sera envoyé à votre adresse email.", + "activation-step-description-sms": "Lors de votre prochaine connexion, un code de sécurité sera envoyé au numéro de téléphone.", + "activation-step-description-totp": "Lors de votre prochaine connexion, vous devrez fournir un code d'authentification à deux facteurs.", + "activation-step-label": "Activation", + "backup-code-description": "Imprimez ces codes pour les avoir à portée de main lorsque vous devrez vous connecter. Chaque code peut être utilisé une seule fois.", + "backup-code-warn": "Une fois que vous quittez cette page, ces codes ne pourront plus être affichés. Conservez-les en toute sécurité à l'aide des options ci-dessous.", + "download-txt": "Télécharger (txt)", + "email-step-description": "Saisissez une adresse email à utiliser comme méthode d'authentification.", + "email-step-label": "Email", + "enable-email-title": "Activer l'authentification par email", + "enable-sms-title": "Activer l'authentification par SMS", + "enable-totp-title": "Activer l'application d'authentification", + "enter-verification-code": "Entrez le code à 6 chiffres ici", + "get-backup-code-title": "Obtenir le code de secours", + "next": "Suivant", + "scan-qr-code": "Scannez ce code QR avec votre application d'authentification", + "send-code": "Envoyer le code", + "sms-step-description": "Saisissez un numéro de téléphone à utiliser comme méthode d'authentification.", + "sms-step-label": "Numéro de téléphone", + "success": "Succès !", + "totp-step-description-install": "Vous pouvez installer des applications comme Google Authenticator, Authy ou Duo.", + "totp-step-description-open": "Ouvrez l'application d'authentification sur votre téléphone mobile.", + "totp-step-label": "Obtenir l'application", + "verification-code": "Code à 6 chiffres", + "verification-code-invalid": "Format de code de vérification invalide", + "verification-code-incorrect": "Code de vérification incorrect", + "verification-code-many-request": "Trop de requêtes. Vérifiez le code de vérification", + "verification-step-description": "Entrez un code à 6 chiffres que nous venons d'envoyer à {{address}}", + "verification-step-label": "Vérification" + }, + "provider": { + "email": "Email", + "email-description": "Utilisez un code de sécurité envoyé à votre adresse email pour vous authentifier.", + "email-hint": "Les codes d'authentification sont envoyés par email à {{ info }}", + "sms": "SMS", + "sms-description": "Utilisez votre téléphone pour vous authentifier. Nous vous enverrons un code de sécurité par SMS lors de votre connexion.", + "sms-hint": "Les codes d'authentification sont envoyés par message texte à {{ info }}", + "totp": "Application d'authentification", + "totp-description": "Utilisez des applications comme Google Authenticator, Authy ou Duo sur votre téléphone pour vous authentifier. Elles génèrent un code de sécurité pour vous connecter.", + "totp-hint": "L'application d'authentification est configurée pour votre compte", + "backup_code": "Code de secours", + "backup-code-description": "Ces codes d'accès imprimables à usage unique vous permettent de vous connecter lorsque vous n'avez pas votre téléphone, par exemple en voyage.", + "backup-code-hint": "{{ info }} codes à usage unique sont actifs pour le moment" + } + }, + "password-requirement": { + "at-least": "Au moins :", + "character": "{ count, plural, =1 {1 caractère} other {# caractères} }", + "digit": "{ count, plural, =1 {1 chiffre} other {# chiffres} }", + "incorrect-password-try-again": "Mot de passe incorrect. Veuillez réessayer", + "lowercase-letter": "{ count, plural, =1 {1 lettre minuscule} other {# lettres minuscules} }", + "new-passwords-not-match": "Les nouveaux mots de passe ne correspondent pas", + "password-should-not-contain-spaces": "Votre mot de passe ne doit pas contenir d'espaces", + "password-not-meet-requirements": "Le mot de passe ne respecte pas les exigences", + "password-requirements": "Exigences du mot de passe", + "password-should-difference": "Le nouveau mot de passe doit être différent de l'actuel", + "special-character": "{ count, plural, =1 {1 caractère spécial} other {# caractères spéciaux} }", + "uppercase-letter": "{ count, plural, =1 {1 lettre majuscule} other {# lettres majuscules} }", + "at-most": "Au plus :" + } + }, + "relation": { + "relations": "Relations", + "direction": "Direction", + "clear-relation-type": "Effacer le type de relation", + "search-direction": { + "FROM": "Depuis", + "TO": "Vers" + }, + "direction-type": { + "FROM": "de", + "TO": "vers" + }, + "from-relations": "Relations sortantes", + "to-relations": "Relations entrantes", + "selected-relations": "{ count, plural, =1 {1 relation} other {# relations} } sélectionnée(s)", + "type": "Type", + "to-entity-type": "Type d'entité cible", + "to-entity-name": "Nom de l'entité cible", + "from-entity-type": "Type d'entité source", + "from-entity-name": "Nom de l'entité source", + "to-entity": "Vers l'entité", + "from-entity": "Depuis l'entité", + "delete": "Supprimer la relation", + "relation-type": "Type de relation", + "relation-type-required": "Le type de relation est requis.", + "relation-type-max-length": "Le type de relation doit comporter moins de 256 caractères", + "any-relation-type": "Tout type", + "add": "Ajouter une relation", + "edit": "Modifier la relation", + "delete-to-relation-title": "Êtes-vous sûr de vouloir supprimer la relation vers l'entité '{{entityName}}' ?", + "delete-to-relation-text": "Attention, après confirmation, l'entité '{{entityName}}' ne sera plus liée à l'entité actuelle.", + "delete-to-relations-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 relation} other {# relations} } ?", + "delete-to-relations-text": "Attention, après confirmation, toutes les relations sélectionnées seront supprimées et les entités correspondantes ne seront plus liées à l'entité actuelle.", + "delete-from-relation-title": "Êtes-vous sûr de vouloir supprimer la relation depuis l'entité '{{entityName}}' ?", + "delete-from-relation-text": "Attention, après confirmation, l'entité actuelle ne sera plus liée à l'entité '{{entityName}}'.", + "delete-from-relations-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 relation} other {# relations} } ?", + "delete-from-relations-text": "Attention, après confirmation, toutes les relations sélectionnées seront supprimées et l'entité actuelle ne sera plus liée aux entités correspondantes.", + "remove-relation-filter": "Supprimer le filtre de relation", + "remove-filter": "Supprimer le filtre", + "add-relation-filter": "Ajouter un filtre de relation", + "any-relation": "Toute relation", + "relation-filters": "Filtres de relation", + "additional-info": "Infos supplémentaires (JSON)", + "invalid-additional-info": "Impossible d'analyser le JSON des infos supplémentaires.", + "no-relations-text": "Aucune relation trouvée", + "not": "Non" + }, + "resource": { + "add": "Ajouter une ressource", + "all-types": "Tous", + "copyId": "Copier l'identifiant de la ressource", + "delete": "Supprimer la ressource", + "delete-resource-text": "Attention, après confirmation, la ressource ne pourra pas être récupérée.", + "delete-resource-title": "Êtes-vous sûr de vouloir supprimer la ressource '{{resourceTitle}}' ?", + "delete-resources-action-title": "Supprimer { count, plural, =1 {1 ressource} other {# ressources} }", + "delete-resources-text": "Veuillez noter que les ressources sélectionnées, même si elles sont utilisées dans les profils de périphériques, seront supprimées.", + "delete-resources-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 ressource} other {# ressources} } ?", + "download": "Télécharger la ressource", + "drop-file": "Déposez un fichier ressource ou cliquez pour en sélectionner un à télécharger.", + "drop-resource-file-or": "Glissez et déposez un fichier ressource ou", + "empty": "Ressource vide", + "file-name": "Nom du fichier", + "idCopiedMessage": "L'identifiant de la ressource a été copié dans le presse-papiers", + "no-resource-matching": "Aucune ressource correspondant à '{{widgetsBundle}}' n'a été trouvée.", + "no-resource-text": "Aucune ressource trouvée", + "open-widgets-bundle": "Ouvrir la collection de widgets", + "resource": "Ressource", + "resource-file": "Fichier ressource", + "resource-files": "Fichiers ressource", + "resource-library-details": "Détails de la ressource", + "resource-type": "Type de ressource", + "resources-library": "Bibliothèque de ressources", + "search": "Rechercher des ressources", + "selected-resources": "{ count, plural, =1 {1 ressource} other {# ressources} } sélectionnée(s)", + "system": "Système", + "title": "Titre", + "title-required": "Le titre est requis.", + "title-max-length": "Le titre doit comporter moins de 256 caractères", + "type": { + "jks": "JKS", + "js-module": "Module JS", + "lwm2m-model": "Modèle LWM2M", + "pkcs-12": "PKCS #12" + }, + "resource-sub-type": "Sous-type", + "sub-type": { + "image": "Image", + "scada-symbol": "Symbole SCADA", + "extension": "Extension", + "module": "Module" + } + }, + "javascript": { + "add": "Ajouter une ressource JavaScript", + "delete": "Supprimer la ressource JavaScript", + "delete-javascript-resource-text": "Attention, après confirmation, la ressource JavaScript ne pourra pas être récupérée.", + "delete-javascript-resource-title": "Êtes-vous sûr de vouloir supprimer la ressource JavaScript '{{resourceTitle}}' ?", + "delete-javascript-resources-action-title": "Supprimer { count, plural, =1 {1 ressource JavaScript} other {# ressources JavaScript} }", + "delete-javascript-resources-text": "Veuillez noter que les ressources JavaScript sélectionnées, même si elles sont utilisées dans des fonctions JavaScript, seront supprimées.", + "delete-javascript-resources-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 ressource JavaScript} other {# ressources JavaScript} } ?", + "delete-javascript-resource-in-use-text": "Si vous souhaitez quand même supprimer la ressource JavaScript, cliquez sur le bouton Supprimer quand même.", + "download": "Télécharger la ressource JavaScript", + "upload-from-file": "Téléverser JavaScript depuis un fichier", + "resource-file": "Fichier ressource JavaScript", + "drop-file": "Déposez un fichier JavaScript ou cliquez pour en sélectionner un à téléverser.", + "drop-resource-file-or": "Glissez-déposez un fichier JavaScript ou", + "javascript-library": "Bibliothèque JavaScript", + "javascript-type": "Type JavaScript", + "javascript-resource-details": "Détails de la ressource JavaScript", + "javascript-resource-is-in-use": "La ressource JavaScript est utilisée par d'autres entités", + "javascript-resources-are-in-use": "Les ressources JavaScript sont utilisées par d'autres entités", + "javascript-resource-is-in-use-text": "La ressource JavaScript '{{title}}' n'a pas été supprimée car elle est utilisée par les entités suivantes :", + "javascript-resources-are-in-use-text": "Toutes les ressources JavaScript n'ont pas été supprimées car elles sont utilisées par d'autres entités.
Vous pouvez consulter les entités référencées en cliquant sur le bouton Références dans la ligne correspondante de la ressource.
Si vous souhaitez quand même supprimer ces ressources JavaScript, sélectionnez-les dans le tableau ci-dessous et cliquez sur le bouton Supprimer la sélection.", + "search": "Rechercher des ressources JavaScript", + "selected-javascript-resources": "{ count, plural, =1 {1 ressource JavaScript} other {# ressources JavaScript} } sélectionnée(s)", + "no-javascript-resource-text": "Aucune ressource JavaScript trouvée", + "all-types": "Tous", + "module-script": "Script module" + }, + "rpc": { + "error": { + "target-device-is-not-set": "Le périphérique cible n'est pas défini !", + "invalid-target-entity": "Les commandes RPC ne sont pas prises en charge par l'entité {{entityType}}.", + "failed-to-resolve-target-device": "Échec de résolution du périphérique cible !", + "request-timeout": "Délai d'attente de la requête dépassé", + "rpc-http-error": "Erreur : {{status}} - {{statusText}}" + } + }, + "rulechain": { + "rulechain": "Chaîne de règles", + "rulechain-events": "Événements de la chaîne de règles", + "rulechains": "Chaînes de règles", + "root": "Racine", + "delete": "Supprimer la chaîne de règles", + "name": "Nom", + "name-required": "Le nom est requis.", + "name-max-length": "Le nom doit contenir moins de 256 caractères", + "description": "Description", + "add": "Ajouter une chaîne de règles", + "set-root": "Définir comme racine", + "set-root-rulechain-title": "Êtes-vous sûr de vouloir définir la chaîne de règles '{{ruleChainName}}' comme racine ?", + "set-root-rulechain-text": "Après confirmation, la chaîne de règles deviendra la racine et gérera tous les messages de transport entrants.", + "delete-rulechain-title": "Êtes-vous sûr de vouloir supprimer la chaîne de règles '{{ruleChainName}}' ?", + "delete-rulechain-text": "Attention, après confirmation, la chaîne de règles et toutes les données associées ne pourront pas être récupérées.", + "delete-rulechains-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} } ?", + "delete-rulechains-action-title": "Supprimer { count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} }", + "delete-rulechains-text": "Attention, après confirmation, toutes les chaînes de règles sélectionnées seront supprimées ainsi que toutes les données associées.", + "add-rulechain-text": "Ajouter une nouvelle chaîne de règles", + "no-rulechains-text": "Aucune chaîne de règles trouvée", + "rulechain-details": "Détails de la chaîne de règles", + "details": "Détails", + "events": "Événements", + "system": "Système", + "import": "Importer une chaîne de règles", + "export": "Exporter la chaîne de règles", + "export-failed-error": "Impossible d'exporter la chaîne de règles : {{error}}", + "create-new-rulechain": "Créer une nouvelle chaîne de règles", + "rulechain-file": "Fichier de chaîne de règles", + "invalid-rulechain-file-error": "Impossible d'importer la chaîne de règles : structure de données invalide.", + "copyId": "Copier l'identifiant de la chaîne de règles", + "idCopiedMessage": "Identifiant de la chaîne de règles copié dans le presse-papiers", + "select-rulechain": "Sélectionner une chaîne de règles", + "no-rulechains-matching": "Aucune chaîne de règles correspondant à '{{entity}}' n'a été trouvée.", + "rulechain-required": "La chaîne de règles est requise", + "management": "Gestion des règles", + "debug-mode": "Mode débogage", + "search": "Rechercher des chaînes de règles", + "selected-rulechains": "{ count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} } sélectionnée(s)", + "open-rulechain": "Ouvrir la chaîne de règles", + "edge-template-root": "Racine du modèle Edge", + "assign-to-edge": "Attribuer à une Edge", + "edge-rulechain": "Chaîne de règles Edge", + "unassign-rulechain-from-edge-text": "Après confirmation, la chaîne de règles sera désattribuée et ne sera plus accessible depuis la Edge.", + "unassign-rulechains-from-edge-title": "Êtes-vous sûr de vouloir désattribuer { count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} } ?", + "unassign-rulechains-from-edge-text": "Après confirmation, toutes les chaînes de règles sélectionnées seront désattribuées et ne seront plus accessibles depuis la Edge.", + "assign-rulechain-to-edge-title": "Attribuer des chaînes de règles à la Edge", + "assign-rulechain-to-edge-text": "Veuillez sélectionner les chaînes de règles à attribuer à la Edge", + "set-edge-template-root-rulechain": "Définir comme racine de modèle Edge", + "set-edge-template-root-rulechain-title": "Êtes-vous sûr de vouloir définir la chaîne de règles '{{ruleChainName}}' comme racine du modèle Edge ?", + "set-edge-template-root-rulechain-text": "Après confirmation, cette chaîne de règles sera définie comme racine de modèle pour toutes les nouvelles Edges créées.", + "invalid-rulechain-type-error": "Impossible d'importer la chaîne de règles : type invalide. Type attendu : {{expectedRuleChainType}}.", + "set-auto-assign-to-edge": "Attribuer automatiquement aux Edge à la création", + "set-auto-assign-to-edge-title": "Êtes-vous sûr de vouloir attribuer automatiquement la chaîne de règles '{{ruleChainName}}' aux Edge à la création ?", + "set-auto-assign-to-edge-text": "Après confirmation, la chaîne de règles Edge sera automatiquement attribuée aux Edge lors de leur création.", + "unset-auto-assign-to-edge": "Ne pas attribuer automatiquement aux Edge", + "unset-auto-assign-to-edge-title": "Êtes-vous sûr de ne pas vouloir attribuer automatiquement la chaîne de règles '{{ruleChainName}}' aux Edge à la création ?", + "unset-auto-assign-to-edge-text": "Après confirmation, la chaîne de règles Edge ne sera plus automatiquement attribuée aux Edge lors de leur création.", + "unassign-rulechain-title": "Êtes-vous sûr de vouloir désattribuer la chaîne de règles '{{ruleChainName}}' ?", + "unassign-rulechains": "Désattribuer les chaînes de règles" + }, + "rulenode": { + "rule-node-events": "Événements du nœud de règle", + "details": "Détails", + "events": "Événements", + "search": "Rechercher des nœuds", + "open-node-library": "Ouvrir la bibliothèque de nœuds", + "close-node-library": "Fermer la bibliothèque de nœuds", + "add": "Ajouter un nœud de règle", + "name": "Nom", + "name-required": "Le nom est requis.", + "name-max-length": "Le nom doit contenir moins de 256 caractères", + "type": "Type", + "rule-node-description": "Description du nœud de règle", + "delete": "Supprimer le nœud de règle", + "select-all-objects": "Sélectionner tous les nœuds et connexions", + "deselect-all-objects": "Désélectionner tous les nœuds et connexions", + "delete-selected-objects": "Supprimer les nœuds et connexions sélectionnés", + "delete-selected": "Supprimer la sélection", + "create-nested-rulechain": "Créer une chaîne de règles imbriquée", + "select-all": "Tout sélectionner", + "copy-selected": "Copier la sélection", + "deselect-all": "Tout désélectionner", + "rulenode-details": "Détails du nœud de règle", + "debug-mode": "Mode débogage", + "singleton": "Singleton", + "configuration": "Configuration", + "link": "Lien", + "link-details": "Détails du lien du nœud de règle", + "add-link": "Ajouter un lien", + "link-label": "Étiquette du lien", + "link-label-required": "L’étiquette du lien est requise.", + "custom-link-label": "Étiquette de lien personnalisée", + "custom-link-label-required": "L’étiquette de lien personnalisée est requise.", + "link-labels": "Étiquettes de lien", + "link-labels-required": "Les étiquettes de lien sont requises.", + "no-link-labels-found": "Aucune étiquette de lien trouvée", + "no-link-label-matching": "'{{label}}' introuvable.", + "create-new-link-label": "Créer une nouvelle étiquette !", + "type-filter": "Filtre", + "type-filter-details": "Filtrer les messages entrants selon des conditions configurées", + "type-enrichment": "Enrichissement", + "type-enrichment-details": "Ajouter des informations supplémentaires dans les métadonnées du message", + "type-transformation": "Transformation", + "type-transformation-details": "Modifier la charge utile et les métadonnées du message", + "type-action": "Action", + "type-action-details": "Effectuer une action spéciale", + "type-external": "Externe", + "type-external-details": "Interagit avec un système externe", + "type-rule-chain": "Chaîne de règles", + "type-rule-chain-details": "Transmet les messages entrants à la chaîne de règles spécifiée", + "type-flow": "Flux", + "type-flow-details": "Organise le flux des messages", + "type-input": "Entrée", + "type-input-details": "Entrée logique de la chaîne de règles, transmet les messages entrants au nœud suivant", + "type-unknown": "Inconnu", + "type-unknown-details": "Nœud de règle non résolu", + "directive-is-not-loaded": "La directive de configuration définie '{{directiveName}}' n'est pas disponible.", + "ui-resources-load-error": "Échec du chargement des ressources UI de configuration.", + "invalid-target-rulechain": "Impossible de résoudre la chaîne de règles cible !", + "test-script-function": "Tester la fonction du script", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", + "message": "Message", + "message-type": "Type de message", + "select-message-type": "Sélectionner un type de message", + "message-type-required": "Le type de message est requis", + "metadata": "Métadonnées", + "metadata-required": "Les entrées de métadonnées ne peuvent pas être vides.", + "output": "Sortie", + "test": "Test", + "help": "Aide", + "reset-debug-settings": "Réinitialiser les paramètres de débogage dans tous les nœuds", + "test-with-this-message": "{{test}} avec ce message", + "queue-hint": "Sélectionner une file pour le transfert de message vers une autre file. La file 'Main' est utilisée par défaut.", + "queue-singleton-hint": "Sélectionner une file pour le transfert de message dans des environnements multi-instances. La file 'Main' est utilisée par défaut." + }, + "rule-node-config": { + "id": "Identifiant", + "additional-info": "Informations supplémentaires", + "advanced-settings": "Paramètres avancés", + "create-entity-if-not-exists": "Créer une entité si elle n'existe pas", + "create-entity-if-not-exists-hint": "Si activé, une nouvelle entité avec les paramètres spécifiés sera créée à moins qu'elle n'existe déjà. Les entités existantes seront utilisées telles quelles pour la relation.", + "select-device-connectivity-event": "Sélectionner un événement de connectivité du dispositif", + "entity-name-pattern": "Modèle de nom", + "device-name-pattern": "Nom du dispositif", + "asset-name-pattern": "Nom de l'actif", + "entity-view-name-pattern": "Nom de la vue d'entité", + "customer-title-pattern": "Titre du client", + "dashboard-name-pattern": "Titre du tableau de bord", + "user-name-pattern": "Email de l'utilisateur", + "edge-name-pattern": "Nom de l'Edge", + "entity-name-pattern-required": "Le modèle de nom est requis", + "entity-name-pattern-hint": "Le champ modèle de nom prend en charge la templatisation. Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour une valeur des métadonnées.", + "copy-message-type": "Copier le type de message", + "entity-type-pattern": "Modèle de type", + "entity-type-pattern-required": "Le modèle de type est requis", + "message-type-value": "Valeur du type de message", + "message-type-value-required": "La valeur du type de message est requise", + "message-type-value-max-length": "La valeur du type de message doit contenir moins de 256 caractères", + "output-message-type": "Type de message de sortie", + "entity-cache-expiration": "Temps d’expiration du cache des entités (s)", + "entity-cache-expiration-hint": "Spécifie l'intervalle de temps maximal pour stocker les enregistrements d'entités trouvées. Une valeur de 0 signifie que les enregistrements n'expireront jamais.", + "entity-cache-expiration-required": "Le temps d’expiration du cache des entités est requis.", + "entity-cache-expiration-range": "Le temps d’expiration du cache des entités doit être supérieur ou égal à 0.", + "customer-name-pattern": "Titre du client", + "customer-name-pattern-required": "Le titre du client est requis", + "customer-name-pattern-hint": "Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour une valeur des métadonnées.", + "create-customer-if-not-exists": "Créer un client s’il n’existe pas", + "unassign-from-customer": "Détacher d’un client spécifique si l’initiateur est un tableau de bord", + "unassign-from-customer-tooltip": "Seuls les tableaux de bord peuvent être attribués à plusieurs clients à la fois.\nSi l’initiateur du message est un tableau de bord, vous devez spécifier explicitement le titre du client pour le détacher.", + "customer-cache-expiration": "Durée d’expiration du cache des clients (s)", + "customer-cache-expiration-hint": "Spécifie la durée maximale pour stocker les enregistrements clients trouvés. Une valeur de 0 signifie que les enregistrements n’expireront jamais.", + "customer-cache-expiration-required": "La durée d’expiration du cache des clients est requise.", + "customer-cache-expiration-range": "La durée d’expiration du cache des clients doit être supérieure ou égale à 0.", + "interval-start": "Début de l’intervalle", + "interval-end": "Fin de l’intervalle", + "time-unit": "Unité de temps", + "fetch-mode": "Mode de récupération", + "order-by-timestamp": "Trier par horodatage", + "limit": "Limite", + "limit-hint": "Valeur minimale de limite : 2, maximale : 1000. Pour récupérer une seule entrée, sélectionnez le mode de récupération 'Premier' ou 'Dernier'.", + "limit-required": "La limite est requise.", + "limit-range": "La limite doit être comprise entre 2 et 1000.", + "time-unit-milliseconds": "Millisecondes", + "time-unit-seconds": "Secondes", + "time-unit-minutes": "Minutes", + "time-unit-hours": "Heures", + "time-unit-days": "Jours", + "time-value-range": "Plage autorisée de 1 à 2147483647.", + "start-interval-value-required": "Le début de l’intervalle est requis.", + "end-interval-value-required": "La fin de l’intervalle est requise.", + "filter": "Filtre", + "switch": "Commutateur", + "math-templatization-tooltip": "Ce champ prend en charge la templatisation. Utilisez $[messageKey] pour une valeur du message et ${metadataKey} pour les métadonnées.", + "add-message-type": "Ajouter un type de message", + "select-message-types-required": "Au moins un type de message doit être sélectionné.", + "select-message-types": "Sélectionner des types de message", + "no-message-types-found": "Aucun type de message trouvé", + "no-message-type-matching": "'{{messageType}}' introuvable.", + "create-new-message-type": "Créer un nouveau.", + "message-types-required": "Les types de message sont requis.", + "client-attributes": "Attributs client", + "shared-attributes": "Attributs partagés", + "server-attributes": "Attributs serveur", + "attributes-keys": "Clés d’attributs", + "attributes-keys-required": "Les clés d’attributs sont requises", + "attributes-scope": "Portée des attributs", + "attributes-scope-value": "Valeur de la portée des attributs", + "attributes-scope-value-copy": "Copier la valeur de portée des attributs", + "attributes-scope-hint": "Utilisez la clé de métadonnée 'scope' pour définir dynamiquement la portée par message. Cela remplace la portée définie dans la configuration.", + "notify-device": "Forcer la notification au dispositif", + "send-attributes-updated-notification": "Envoyer une notification d’attributs mis à jour", + "send-attributes-updated-notification-hint": "Envoyer une notification sur les attributs mis à jour comme message séparé à la file du moteur de règles.", + "send-attributes-deleted-notification": "Envoyer une notification d’attributs supprimés", + "send-attributes-deleted-notification-hint": "Envoyer une notification sur les attributs supprimés comme message séparé à la file du moteur de règles.", + "update-attributes-only-on-value-change": "Mettre à jour uniquement si la valeur change", + "update-attributes-only-on-value-change-hint": "Met à jour les attributs à chaque message entrant, même si leur valeur n’a pas changé. Cela augmente l’usage de l’API et réduit les performances.", + "update-attributes-only-on-value-change-hint-enabled": "Met à jour les attributs uniquement si leur valeur a changé. Si aucune modification, aucune mise à jour ni notification ne sera envoyée.", + "fetch-credentials-to-metadata": "Transférer les identifiants dans les métadonnées", + "notify-device-on-update-hint": "Si activé, force la notification au dispositif lors de la mise à jour des attributs partagés. Sinon, contrôlé via le paramètre 'notifyDevice' dans les métadonnées du message.", + "notify-device-on-delete-hint": "Si activé, force la notification au dispositif lors de la suppression des attributs partagés. Sinon, contrôlé via 'notifyDevice' dans les métadonnées du message.", + "latest-timeseries": "Clés de données de séries temporelles récentes", + "timeseries-keys": "Clés de séries temporelles", + "timeseries-keys-required": "Au moins une clé de série temporelle doit être sélectionnée.", + "add-timeseries-key": "Ajouter une clé de série temporelle", + "add-message-field": "Ajouter un champ de message", + "relation-search-parameters": "Paramètres de recherche de relations", + "relation-parameters": "Paramètres de relation", + "add-metadata-field": "Ajouter un champ de métadonnée", + "data-keys": "Noms des champs du message", + "copy-from": "Copier depuis", + "data-to-metadata": "Données vers métadonnées", + "metadata-to-data": "Métadonnées vers données", + "use-regular-expression-hint": "Utilisez une expression régulière pour copier des clés par motif.\n\nAstuces :\nAppuyez sur 'Entrée' pour valider un champ.\nAppuyez sur 'Retour arrière' pour le supprimer. Plusieurs champs sont pris en charge.", + "interval": "Intervalle", + "interval-required": "L’intervalle est requis", + "interval-hint": "Intervalle de déduplication en secondes.", + "interval-min-error": "La valeur minimale autorisée est 1", + "max-pending-msgs": "Nombre max. de messages en attente", + "max-pending-msgs-hint": "Nombre maximum de messages stockés en mémoire pour chaque ID de déduplication unique.", + "max-pending-msgs-required": "Le nombre max. de messages en attente est requis", + "max-pending-msgs-max-error": "Valeur max. autorisée : 1000", + "max-pending-msgs-min-error": "Valeur min. autorisée : 1", + "max-retries": "Nombre max. de tentatives", + "max-retries-required": "Le nombre max. de tentatives est requis", + "max-retries-hint": "Nombre maximum de tentatives pour pousser les messages dédupliqués dans la file. Un délai de 10 secondes est appliqué entre les tentatives.", + "max-retries-max-error": "Valeur max. autorisée : 100", + "max-retries-min-error": "Valeur min. autorisée : 0", + "strategy": "Stratégie", + "strategy-required": "La stratégie est requise", + "strategy-all-hint": "Retourne tous les messages arrivés pendant la période de déduplication sous forme de tableau JSON. Chaque élément contient les propriétés msg et metadata.", + "strategy-first-hint": "Retourne le premier message arrivé pendant la période de déduplication.", + "strategy-last-hint": "Retourne le dernier message arrivé pendant la période de déduplication.", + "first": "Premier", + "last": "Dernier", + "all": "Tous", + "output-msg-type-hint": "Type de message du résultat de la déduplication.", + "queue-name-hint": "Nom de la file où publier le résultat de la déduplication.", + "keys": "Clés", + "keys-required": "Les clés sont requises", + "rename-keys-in": "Renommer les clés dans", + "data": "Données", + "message": "Message", + "metadata": "Métadonnées", + "current-key-name": "Nom actuel de la clé", + "key-name-required": "Le nom de la clé est requis", + "new-key-name": "Nouveau nom de la clé", + "new-key-name-required": "Le nouveau nom de la clé est requis", + "metadata-keys": "Noms des champs de métadonnées", + "json-path-expression": "Expression JSONPath", + "json-path-expression-required": "L'expression JSONPath est requise", + "json-path-expression-hint": "JSONPath indique un chemin vers un ou plusieurs éléments dans une structure JSON. '$' représente la racine.", + "relations-query": "Requête sur les relations", + "device-relations-query": "Requête sur les relations du dispositif", + "max-relation-level": "Niveau de relation max.", + "max-relation-level-error": "La valeur doit être supérieure à 0 ou non définie.", + "max-relation-level-invalid": "La valeur doit être un entier.", + "relation-type": "Type de relation", + "relation-type-pattern": "Modèle du type de relation", + "relation-type-pattern-required": "Le modèle du type de relation est requis", + "relation-types-list": "Types de relation à propager", + "relation-types-list-hint": "Si aucun type de propagation n’est sélectionné, les alarmes seront propagées sans filtrage.", + "unlimited-level": "Niveau illimité", + "latest-telemetry": "Dernières télémétries", + "add-telemetry-key": "Ajouter une clé de télémétrie", + "delete-from": "Supprimer de", + "use-regular-expression-delete-hint": "Utiliser des expressions régulières pour supprimer les clés par motif.\n\nAstuces :\nAppuyez sur 'Entrée' pour valider un champ.\nAppuyez sur 'Retour arrière' pour le supprimer. Plusieurs champs sont pris en charge.", + "fetch-into": "Récupérer dans", + "attr-mapping": "Correspondance des attributs :", + "source-attribute": "Clé de l’attribut source", + "source-attribute-required": "La clé de l’attribut source est requise.", + "source-telemetry": "Clé de télémétrie source", + "source-telemetry-required": "La clé de télémétrie source est requise.", + "target-key": "Clé cible", + "target-key-required": "La clé cible est requise.", + "attr-mapping-required": "Au moins une correspondance d'attribut doit être spécifiée.", + "fields-mapping": "Correspondance des champs", + "fields-mapping-hint": "Si le champ de message est défini sur $entityId, l’ID de l’initiateur du message sera enregistré dans la colonne correspondante.", + "relations-query-config-direction-suffix": "initiateur", + "profile-name": "Nom du profil", + "fetch-circle-parameter-info-from-metadata-hint": "Le champ de métadonnée '{{perimeterKeyName}}' doit être défini au format : {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "Le champ de métadonnée '{{perimeterKeyName}}' doit être défini au format : [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour une des métadonnées.", + "fields-mapping-required": "Au moins une correspondance de champ doit être spécifiée.", + "at-least-one-field-required": "Au moins un champ d’entrée doit avoir une valeur fournie.", + "originator-fields-sv-map-hint": "Les champs de la clé cible prennent en charge la templatisation. Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour les métadonnées.", + "sv-map-hint": "Seuls les champs de clé cible prennent en charge la templatisation. Utilisez $[messageKey] pour extraire une valeur du message et ${metadataKey} pour les métadonnées.", + "source-field": "Champ source", + "source-field-required": "Le champ source est requis.", + "originator-source": "Source de l'initiateur", + "new-originator": "Nouvel initiateur", + "originator-customer": "Client", + "originator-tenant": "Tenant", + "originator-related": "Entité liée", + "originator-alarm-originator": "Origine de l’alarme", + "originator-entity": "Entité par modèle de nom", + "clone-message": "Cloner le message", + "transform": "Transformer", + "default-ttl": "Durée de vie (TTL) par défaut", + "default-ttl-required": "La durée de vie (TTL) par défaut est requise.", + "default-ttl-hint": "Le nœud de règle utilisera la valeur TTL des métadonnées du message. Si aucune valeur n'est présente, celle spécifiée dans la configuration sera utilisée. Si la valeur est 0, la valeur TTL du profil de locataire sera appliquée.", + "default-ttl-zero-hint": "Aucun TTL ne sera appliqué si la valeur est 0.", + "min-default-ttl-message": "Seule la valeur 0 est autorisée comme minimum TTL.", + "generation-parameters": "Paramètres de génération", + "message-count": "Limite de messages générés (0 - illimité)", + "message-count-required": "La limite de messages générés est requise.", + "min-message-count-message": "Seule la valeur minimale de 0 est autorisée.", + "period-seconds": "Période en secondes", + "period-seconds-required": "La période est requise.", + "generation-frequency-seconds": "Fréquence de génération (en secondes)", + "generation-frequency-required": "La fréquence de génération est requise.", + "min-generation-frequency-message": "60 secondes minimum sont requises.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Utiliser le motif de période en secondes", + "use-metadata-period-in-seconds-patterns-hint": "Si activé, le nœud de règle utilisera le motif de période en secondes depuis les métadonnées ou les données du message.", + "period-in-seconds-pattern": "Motif de période en secondes", + "period-in-seconds-pattern-required": "Le motif de période en secondes est requis", + "min-period-seconds-message": "La période minimale autorisée est de 60 secondes.", + "originator": "Initiateur", + "message-body": "Corps du message", + "message-metadata": "Métadonnées du message", + "generate": "Générer", + "current-rule-node": "Nœud de règle actuel", + "current-tenant": "Locataire actuel", + "generator-function": "Fonction génératrice", + "test-generator-function": "Tester la fonction génératrice", + "generator": "Générateur", + "test-filter-function": "Tester la fonction de filtre", + "test-switch-function": "Tester la fonction de commutation", + "test-transformer-function": "Tester la fonction de transformation", + "transformer": "Transformateur", + "alarm-create-condition": "Condition de création d’alarme", + "test-condition-function": "Tester la fonction conditionnelle", + "alarm-clear-condition": "Condition de suppression de l’alarme", + "alarm-details-builder": "Constructeur de détails d’alarme", + "test-details-function": "Tester la fonction de détails", + "alarm-type": "Type d’alarme", + "select-entity-types": "Sélectionner les types d’entités", + "alarm-type-required": "Le type d’alarme est requis.", + "alarm-severity": "Gravité de l’alarme", + "alarm-severity-required": "La gravité de l’alarme est requise", + "alarm-severity-pattern": "Motif de gravité d’alarme", + "alarm-status-filter": "Filtre d’état d’alarme", + "alarm-status-list-empty": "La liste des états d’alarme est vide", + "no-alarm-status-matching": "Aucun état d’alarme correspondant trouvé.", + "propagate": "Propager l’alarme aux entités liées", + "propagate-to-owner": "Propager l’alarme au propriétaire (Client ou Locataire)", + "propagate-to-tenant": "Propager l’alarme au locataire", + "condition": "Condition", + "details": "Détails", + "to-string": "Vers chaîne de caractères", + "test-to-string-function": "Tester la fonction toString", + "from-template": "De", + "from-template-required": "Le champ De est requis", + "message-to-metadata": "Du message vers les métadonnées", + "metadata-to-message": "Des métadonnées vers le message", + "from-message": "Depuis le message", + "from-metadata": "Depuis les métadonnées", + "to-template": "À", + "to-template-required": "Le champ À est requis", + "mail-address-list-template-hint": "Liste d’adresses séparées par des virgules, utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message.", + "cc-template": "Cc", + "bcc-template": "Cci", + "subject-template": "Objet", + "subject-template-required": "Le champ Objet est requis", + "body-template": "Corps", + "body-template-required": "Le corps du message est requis", + "dynamic-mail-body-type": "Type dynamique de corps de mail", + "mail-body-type": "Type de corps de mail", + "body-type-template": "Modèle de type de corps", + "reply-routing-configuration": "Configuration du routage de réponse", + "rpc-reply-routing-configuration-hint": "Ces paramètres spécifient les clés des métadonnées pour identifier le service, la session et la requête pour renvoyer la réponse.", + "reply-routing-configuration-hint": "Ces paramètres spécifient les clés des métadonnées pour identifier le service et la requête pour renvoyer la réponse.", + "request-id-metadata-attribute": "ID de requête", + "service-id-metadata-attribute": "ID du service", + "session-id-metadata-attribute": "ID de session", + "timeout-sec": "Délai d'expiration en secondes", + "timeout-required": "Le délai d'expiration est requis", + "min-timeout-message": "Seule une valeur minimale de 0 est autorisée.", + "endpoint-url-pattern": "Motif d'URL du point de terminaison", + "endpoint-url-pattern-required": "Le motif d'URL du point de terminaison est requis", + "request-method": "Méthode de requête", + "use-simple-client-http-factory": "Utiliser une fabrique HTTP client simple", + "ignore-request-body": "Sans corps de requête", + "parse-to-plain-text": "Analyser en texte brut", + "parse-to-plain-text-hint": "Si sélectionné, le corps du message sera converti de chaîne JSON en texte brut, par exemple msg = \"Hello,\\t\"world\"\" devient Hello, \"world\"", + "read-timeout": "Délai de lecture (ms)", + "read-timeout-hint": "Une valeur de 0 signifie un délai d'expiration infini", + "max-parallel-requests-count": "Nombre maximal de requêtes parallèles", + "max-parallel-requests-count-hint": "Une valeur de 0 signifie qu'il n'y a pas de limite de traitement parallèle", + "max-response-size": "Taille maximale de réponse (en Ko)", + "max-response-size-hint": "Quantité maximale de mémoire utilisée pour le tampon lors du décodage/encodage des messages HTTP comme les charges JSON ou XML", + "headers": "En-têtes", + "headers-hint": "Utilisez ${metadataKey} pour une valeur des métadonnées, $[messageKey] pour une valeur du corps du message", + "header": "En-tête", + "header-required": "L'en-tête est requis", + "value": "Valeur", + "value-required": "La valeur est requise", + "topic-pattern": "Motif de sujet", + "key-pattern": "Motif de clé", + "key-pattern-hint": "Facultatif. Si une partition est précisée, elle sera utilisée. Sinon, la clé est utilisée. Sans clé, un round-robin sera appliqué.", + "topic-pattern-required": "Le motif de sujet est requis", + "topic": "Sujet", + "topic-required": "Le sujet est requis", + "bootstrap-servers": "Serveurs bootstrap", + "bootstrap-servers-required": "Les serveurs bootstrap sont requis", + "other-properties": "Autres propriétés", + "key": "Clé", + "key-required": "La clé est requise", + "retries": "Réessais automatiques en cas d’échec", + "min-retries-message": "Seule une valeur minimale de 0 est autorisée.", + "batch-size-bytes": "Taille de lot produit (en octets)", + "min-batch-size-bytes-message": "Seule une taille minimale de 0 est autorisée.", + "linger-ms": "Temps de mise en tampon (ms)", + "min-linger-ms-message": "Seule une valeur minimale de 0 ms est autorisée.", + "buffer-memory-bytes": "Taille maximale du tampon client (en octets)", + "min-buffer-memory-message": "Seule une taille minimale de 0 est autorisée.", + "memory-buffer-size-range": "La taille du tampon doit être comprise entre 0 et {{max}} Ko", + "acks": "Nombre d'accusés de réception", + "topic-arn-pattern": "Motif ARN de sujet", + "topic-arn-pattern-required": "Le motif ARN de sujet est requis", + "aws-access-key-id": "ID de clé d'accès AWS", + "aws-access-key-id-required": "L'ID de clé d'accès AWS est requis", + "aws-secret-access-key": "Clé d'accès secrète AWS", + "aws-secret-access-key-required": "La clé d'accès secrète AWS est requise", + "aws-region": "Région AWS", + "aws-region-required": "La région AWS est requise", + "exchange-name-pattern": "Motif de nom d'échange", + "routing-key-pattern": "Motif de clé de routage", + "message-properties": "Propriétés du message", + "host": "Hôte", + "host-required": "L’hôte est requis", + "port": "Port", + "port-required": "Le port est requis", + "port-range": "Le port doit être compris entre 1 et 65535.", + "virtual-host": "Hôte virtuel", + "username": "Nom d'utilisateur", + "password": "Mot de passe", + "automatic-recovery": "Récupération automatique", + "connection-timeout-ms": "Délai de connexion (ms)", + "min-connection-timeout-ms-message": "Seule une valeur minimale de 0 ms est autorisée.", + "handshake-timeout-ms": "Délai de poignée de main (ms)", + "min-handshake-timeout-ms-message": "Seule une valeur minimale de 0 ms est autorisée.", + "client-properties": "Propriétés du client", + "queue-url-pattern": "Motif d'URL de file d'attente", + "queue-url-pattern-required": "Le motif d'URL de file d'attente est requis", + "delay-seconds": "Délai (secondes)", + "min-delay-seconds-message": "Seule une valeur minimale de 0 s est autorisée.", + "max-delay-seconds-message": "La valeur maximale autorisée est de 900 secondes.", + "name": "Nom", + "name-required": "Le nom est requis", + "queue-type": "Type de file d’attente", + "sqs-queue-standard": "Standard", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "ID de projet GCP", + "gcp-project-id-required": "L’ID de projet GCP est requis", + "gcp-service-account-key": "Clé de compte de service GCP", + "gcp-service-account-key-required": "La clé de compte de service GCP est requise", + "pubsub-topic-name": "Nom du sujet", + "pubsub-topic-name-required": "Le nom du sujet est requis", + "message-attributes": "Attributs du message", + "message-attributes-hint": "Utilisez ${metadataKey} pour une valeur depuis les métadonnées, $[messageKey] depuis le message", + "connect-timeout": "Délai de connexion (s)", + "connect-timeout-required": "Le délai de connexion est requis.", + "connect-timeout-range": "Le délai de connexion doit être compris entre 1 et 200.", + "client-id": "ID client", + "client-id-hint": "Optionnel. Laissez vide pour un ID client généré automatiquement. Si vous utilisez un ID explicite, veillez à ce qu'il soit unique, surtout en mode micro-services.", + "append-client-id-suffix": "Ajouter l'ID du service comme suffixe à l'ID client", + "client-id-suffix-hint": "Optionnel. Appliqué si un ID client est défini. Ajoute l'ID de service comme suffixe pour éviter les conflits en mode micro-services.", + "device-id": "ID de l'appareil", + "device-id-required": "L'ID de l'appareil est requis.", + "clean-session": "Session propre", + "enable-ssl": "Activer SSL", + "credentials": "Identifiants", + "credentials-type": "Type d'identifiants", + "credentials-type-required": "Le type d'identifiants est requis.", + "credentials-anonymous": "Anonyme", + "credentials-basic": "Basique", + "credentials-pem": "PEM", + "credentials-pem-hint": "Au moins un fichier de certificat CA serveur ou une paire certificat client + clé privée client est requis", + "credentials-sas": "Signature d'accès partagé", + "sas-key": "Clé SAS", + "sas-key-required": "La clé SAS est requise.", + "hostname": "Nom d'hôte", + "hostname-required": "Le nom d'hôte est requis.", + "azure-ca-cert": "Fichier de certificat CA", + "username-required": "Le nom d'utilisateur est requis.", + "password-required": "Le mot de passe est requis.", + "ca-cert": "Fichier certificat CA du serveur", + "private-key": "Fichier de clé privée du client", + "cert": "Fichier de certificat client", + "no-file": "Aucun fichier sélectionné.", + "drop-file": "Déposez un fichier ou cliquez pour en sélectionner un à téléverser.", + "private-key-password": "Mot de passe de la clé privée", + "use-system-smtp-settings": "Utiliser les paramètres SMTP système", + "use-metadata-dynamic-interval": "Utiliser un intervalle dynamique", + "metadata-dynamic-interval-hint": "Les champs de début et fin d’intervalle supportent la templatization. Valeurs en millisecondes. Utilisez $[messageKey] ou ${metadataKey}.", + "use-metadata-interval-patterns-hint": "Si sélectionné, le nœud utilise les motifs d’intervalle de début/fin du message ou des métadonnées en millisecondes.", + "use-message-alarm-data": "Utiliser les données d'alarme du message", + "overwrite-alarm-details": "Écraser les détails de l'alarme", + "use-alarm-severity-pattern": "Utiliser un motif de sévérité d'alarme", + "check-all-keys": "Vérifier que tous les champs sont présents", + "check-all-keys-hint": "Vérifie la présence de toutes les clés spécifiées dans les données et les métadonnées.", + "check-relation-to-specific-entity": "Vérifier la relation avec une entité spécifique", + "check-relation-to-specific-entity-tooltip": "Si activé, vérifie la relation avec une entité spécifique, sinon avec n'importe quelle entité selon direction/type.", + "check-relation-hint": "Vérifie la relation avec une entité (spécifique ou non) selon direction/type.", + "delete-relation-with-specific-entity": "Supprimer la relation avec une entité spécifique", + "delete-relation-with-specific-entity-hint": "Supprime uniquement la relation avec une entité spécifique. Sinon, supprime toutes les relations correspondantes.", + "delete-relation-hint": "Supprime la relation de l'origine du message vers une ou plusieurs entités selon direction/type.", + "remove-current-relations": "Supprimer les relations actuelles", + "remove-current-relations-hint": "Supprime les relations actuelles de l'origine du message selon direction/type.", + "change-originator-to-related-entity": "Changer l'origine en une entité liée", + "change-originator-to-related-entity-hint": "Permet de traiter un message comme venant d'une autre entité.", + "start-interval": "Début de l'intervalle", + "end-interval": "Fin de l'intervalle", + "start-interval-required": "Le début de l'intervalle est requis.", + "end-interval-required": "La fin de l'intervalle est requise.", + "smtp-protocol": "Protocole", + "smtp-host": "Hôte SMTP", + "smtp-host-required": "L’hôte SMTP est requis.", + "smtp-port": "Port SMTP", + "smtp-port-required": "Le port SMTP est requis.", + "smtp-port-range": "Le port SMTP doit être compris entre 1 et 65535.", + "timeout-msec": "Délai d'attente (ms)", + "min-timeout-msec-message": "Seule une valeur minimale de 0 ms est autorisée.", + "enter-username": "Saisir le nom d'utilisateur", + "enter-password": "Saisir le mot de passe", + "enable-tls": "Activer TLS", + "tls-version": "Version TLS", + "enable-proxy": "Activer le proxy", + "use-system-proxy-properties": "Utiliser les paramètres proxy système", + "proxy-host": "Hôte proxy", + "proxy-host-required": "L’hôte proxy est requis.", + "proxy-port": "Port proxy", + "proxy-port-required": "Le port proxy est requis.", + "proxy-port-range": "Le port proxy doit être entre 1 et 65535.", + "proxy-user": "Utilisateur proxy", + "proxy-password": "Mot de passe proxy", + "proxy-scheme": "Schéma de proxy", + "numbers-to-template": "Numéros de téléphone vers modèle", + "numbers-to-template-required": "Le modèle de numéros de téléphone est requis", + "numbers-to-template-hint": "Liste de numéros séparés par des virgules. Utilisez ${metadataKey} ou $[messageKey].", + "sms-message-template": "Modèle de message SMS", + "sms-message-template-required": "Le modèle de message SMS est requis", + "use-system-sms-settings": "Utiliser les paramètres SMS système", + "min-period-0-seconds-message": "Seule une valeur minimale de 0 seconde est autorisée.", + "max-pending-messages": "Nombre maximal de messages en attente", + "max-pending-messages-required": "Le nombre maximal de messages en attente est requis.", + "max-pending-messages-range": "La valeur doit être comprise entre 1 et 100000.", + "originator-types-filter": "Filtre de types d’origine", + "interval-seconds": "Intervalle en secondes", + "interval-seconds-required": "L'intervalle est requis.", + "int-range": "La valeur ne doit pas dépasser la limite entière maximale (2147483648)", + "min-interval-seconds-message": "Seule une valeur minimale de 1 seconde est autorisée.", + "output-timeseries-key-prefix": "Préfixe de clé des séries temporelles de sortie", + "output-timeseries-key-prefix-required": "Le préfixe de clé de sortie est requis.", + "separator-hint": "Appuyez sur \"Entrée\" pour valider la saisie.", + "select-details": "Sélectionner les détails", + "entity-details-id": "Id", + "entity-details-title": "Titre", + "entity-details-country": "Pays", + "entity-details-state": "État", + "entity-details-city": "Ville", + "entity-details-zip": "Code postal", + "entity-details-address": "Adresse", + "entity-details-address2": "Adresse 2", + "entity-details-additional_info": "Informations supplémentaires", + "entity-details-phone": "Téléphone", + "entity-details-email": "Email", + "email-sender": "Expéditeur de l'email", + "fields-to-check": "Champs à vérifier", + "add-detail": "Ajouter un détail", + "check-all-keys-tooltip": "Si activé, vérifie la présence de tous les champs listés dans les données et métadonnées du message entrant.", + "fields-to-check-hint": "Appuyez sur \"Entrée\" pour valider la saisie. Champs multiples supportés.", + "entity-details-list-empty": "Au moins un détail doit être sélectionné.", + "alarm-status": "Statut d'alarme", + "alarm-required": "Au moins un statut d'alarme doit être sélectionné.", + "no-entity-details-matching": "Aucun détail d'entité correspondant trouvé.", + "custom-table-name": "Nom de table personnalisé", + "custom-table-name-required": "Le nom de la table est requis", + "custom-table-hint": "La table doit être créée dans votre cluster Cassandra et commencer par 'cs_tb_'. Entrez ici le nom sans le préfixe.", + "message-field": "Champ du message", + "message-field-required": "Le champ du message est requis.", + "table-col": "Colonne de la table", + "table-col-required": "La colonne de la table est requise.", + "latitude-field-name": "Nom du champ de latitude", + "longitude-field-name": "Nom du champ de longitude", + "latitude-field-name-required": "Le nom du champ de latitude est requis.", + "longitude-field-name-required": "Le nom du champ de longitude est requis.", + "fetch-perimeter-info-from-metadata": "Extraire les infos de périmètre depuis les métadonnées", + "fetch-perimeter-info-from-metadata-tooltip": "Si le type de périmètre est 'Polygone', la valeur du champ '{{perimeterKeyName}}' est utilisée telle quelle. Pour 'Cercle', elle est analysée pour extraire latitude, longitude, rayon et unité.", + "perimeter-key-name": "Nom de clé de périmètre", + "perimeter-key-name-hint": "Nom du champ de métadonnées contenant les infos de périmètre.", + "perimeter-key-name-required": "Le nom de la clé de périmètre est requis.", + "perimeter-circle": "Cercle", + "perimeter-polygon": "Polygone", + "perimeter-type": "Type de périmètre", + "circle-center-latitude": "Latitude du centre", + "circle-center-latitude-required": "Latitude du centre requise.", + "circle-center-longitude": "Longitude du centre", + "circle-center-longitude-required": "Longitude du centre requise.", + "range-unit-meter": "Mètre", + "range-unit-kilometer": "Kilomètre", + "range-unit-foot": "Pied", + "range-unit-mile": "Mille", + "range-unit-nautical-mile": "Mille marin", + "range-units": "Unités de distance", + "range-units-required": "Les unités de distance sont requises.", + "range": "Portée", + "range-required": "La portée est requise.", + "polygon-definition": "Définition du polygone", + "polygon-definition-required": "La définition du polygone est requise.", + "polygon-definition-hint": "Format : [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]]", + "min-inside-duration": "Durée minimale à l’intérieur", + "min-inside-duration-value-required": "Durée minimale à l’intérieur requise", + "min-inside-duration-time-unit": "Unité de temps de la durée intérieure", + "min-outside-duration": "Durée minimale à l’extérieur", + "min-outside-duration-value-required": "Durée minimale à l’extérieur requise", + "min-outside-duration-time-unit": "Unité de temps de la durée extérieure", + "tell-failure-if-absent": "Indiquer un échec", + "tell-failure-if-absent-hint": "Si au moins une clé sélectionnée est absente, le message sortant signalera une \"Échec\".", + "get-latest-value-with-ts": "Récupérer l’horodatage des dernières valeurs", + "get-latest-value-with-ts-hint": "Inclura l'horodatage dans les valeurs télémétriques, ex : \"temp\": {\"ts\":1574329385897, \"value\":42}", + "ignore-null-strings": "Ignorer les chaînes nulles", + "ignore-null-strings-hint": "Ignore les champs avec valeur vide.", + "add-metadata-key-values-as-kafka-headers": "Ajouter les métadonnées comme en-têtes Kafka", + "add-metadata-key-values-as-kafka-headers-hint": "Les métadonnées seront ajoutées comme en-têtes en tant que tableau d'octets avec un encodage prédéfini.", + "charset-encoding": "Encodage de caractères", + "charset-encoding-required": "L'encodage de caractères est requis.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "Sélectionnez un nom de file ou entrez un nom personnalisé.", + "device-profile-node-hint": "Utile pour conserver la continuité des alarmes en cas de redémarrage.", + "persist-alarm-rules": "Persister l'état des règles d'alarme", + "persist-alarm-rules-hint": "Stocke l'état des règles dans la base de données.", + "fetch-alarm-rules": "Récupérer l'état des règles d'alarme", + "fetch-alarm-rules-hint": "Restaure l’état à l’initialisation pour gérer les alarmes même après redémarrage.", + "input-value-key": "Clé de valeur d'entrée", + "input-value-key-required": "La clé de valeur d'entrée est requise.", + "output-value-key": "Clé de valeur de sortie", + "output-value-key-required": "La clé de valeur de sortie est requise.", + "number-of-digits-after-floating-point": "Nombre de chiffres après la virgule", + "number-of-digits-after-floating-point-range": "Doit être compris entre 0 et 15.", + "failure-if-delta-negative": "Signaler un échec si le delta est négatif", + "failure-if-delta-negative-tooltip": "Force l’échec du traitement si la valeur delta est négative.", + "use-caching": "Utiliser le cache", + "use-caching-tooltip": "Met en cache la valeur de \"{{inputValueKey}}\" depuis le message entrant pour améliorer la performance. Le cache ne sera pas mis à jour si la valeur est modifiée ailleurs.", + "add-time-difference-between-readings": "Ajouter la différence de temps entre les lectures de \"{{inputValueKey}}\"", + "add-time-difference-between-readings-tooltip": "Ajoute la clé \"{{periodValueKey}}\" au message sortant si activé.", + "period-value-key": "Clé de la période", + "period-value-key-required": "La clé de la période est requise.", + "general-pattern-hint": "Utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message.", + "alarm-severity-pattern-hint": "Utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message. Les niveaux doivent être CRITICAL, MAJOR, etc.", + "output-node-name-hint": "Le nom du nœud correspond au type de relation du message de sortie.", + "use-server-ts": "Utiliser l’horodatage serveur", + "use-server-ts-hint": "Utilise le temps actuel du serveur pour les séries temporelles sans horodatage explicite.", + "kv-map-pattern-hint": "Tous les champs d’entrée supportent la templatization.", + "kv-map-single-pattern-hint": "Le champ d’entrée supporte la templatization.", + "shared-scope": "Portée partagée", + "server-scope": "Portée serveur", + "client-scope": "Portée client", + "attribute-type": "Attribut", + "attribute-type-description": "Récupère l’attribut depuis la base de données", + "attribute-type-result-description": "Stocke le résultat comme attribut", + "constant-type": "Constante", + "constant-type-description": "Définit une valeur constante", + "time-series-type": "Série temporelle", + "time-series-type-description": "Récupère la dernière valeur de série temporelle", + "time-series-type-result-description": "Stocke le résultat comme série temporelle", + "message-body-type": "Message", + "message-body-type-description": "Valeur depuis le corps du message entrant", + "message-body-type-result-description": "Ajoute le résultat au corps du message sortant", + "message-metadata-type": "Métadonnée", + "message-metadata-type-description": "Valeur depuis les métadonnées du message entrant", + "message-metadata-result-description": "Ajoute le résultat aux métadonnées sortantes", + "argument-tile": "Arguments", + "no-arguments-prompt": "Aucun argument configuré", + "result-title": "Résultat", + "functions-field-input": "Fonctions", + "no-option-found": "Aucune option trouvée", + "argument-source-field-input": "Source", + "argument-source-field-input-required": "La source de l'argument est requise.", + "argument-key-field-input": "Clé", + "argument-key-field-input-required": "La clé de l'argument est requise.", + "constant-value-field-input": "Valeur constante", + "constant-value-field-input-required": "La valeur constante est requise.", + "attribute-scope-field-input": "Portée de l'attribut", + "attribute-scope-field-input-required": "La portée est requise.", + "default-value-field-input": "Valeur par défaut", + "type-field-input": "Type", + "type-field-input-required": "Le type est requis.", + "key-field-input": "Clé", + "add-entity-type": "Ajouter un type d'entité", + "add-device-profile": "Ajouter un profil de périphérique", + "key-field-input-required": "La clé est requise.", + "number-floating-point-field-input": "Nombre de chiffres après la virgule", + "number-floating-point-field-input-hint": "Utilisez 0 pour convertir en entier", + "add-to-message-field-input": "Ajouter au message", + "add-to-metadata-field-input": "Ajouter aux métadonnées", + "custom-expression-field-input": "Expression mathématique", + "custom-expression-field-input-required": "L’expression mathématique est requise", + "custom-expression-field-input-hint": "Spécifiez une expression mathématique à évaluer.", + "retained-message": "Retenu", + "attributes-mapping": "Correspondance des attributs", + "latest-telemetry-mapping": "Correspondance des dernières télémétries", + "add-mapped-attribute-to": "Ajouter les attributs mappés à", + "add-mapped-latest-telemetry-to": "Ajouter la télémétrie mappée à", + "add-mapped-fields-to": "Ajouter les champs mappés à", + "add-selected-details-to": "Ajouter les détails sélectionnés à", + "clear-selected-types": "Effacer les types sélectionnés", + "clear-selected-details": "Effacer les détails sélectionnés", + "clear-selected-fields": "Effacer les champs sélectionnés", + "clear-selected-keys": "Effacer les clés sélectionnées", + "geofence-configuration": "Configuration de la géofence", + "coordinate-field-names": "Noms des champs de coordonnées", + "coordinate-field-hint": "Le nœud tente de récupérer ces champs depuis le message, sinon depuis les métadonnées.", + "presence-monitoring-strategy": "Stratégie de suivi de présence", + "presence-monitoring-strategy-on-first-message": "Au premier message", + "presence-monitoring-strategy-on-each-message": "À chaque message", + "presence-monitoring-strategy-on-first-message-hint": "Signale 'Inside' ou 'Outside' au premier message après une durée minimale depuis la dernière mise à jour.", + "presence-monitoring-strategy-on-each-message-hint": "Signale 'Inside' ou 'Outside' à chaque message après une mise à jour.", + "fetch-credentials-to": "Extraire les identifiants vers", + "add-originator-attributes-to": "Ajouter les attributs de l’origine à", + "originator-attributes": "Attributs de l’origine", + "fetch-latest-telemetry-with-timestamp": "Extraire la dernière télémétrie avec horodatage", + "fetch-latest-telemetry-with-timestamp-tooltip": "Ajoute l'horodatage dans les métadonnées, ex: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure": "Signaler un échec si un attribut est manquant", + "tell-failure-tooltip": "Si une des clés n’existe pas, le message signalera un échec.", + "created-time": "Date de création", + "chip-help": "Appuyez sur 'Entrée' pour valider {{inputName}}. \nAppuyez sur 'Retour arrière' pour supprimer. \nValeurs multiples supportées.", + "detail": "détail", + "field-name": "Nom du champ", + "device-profile": "Profil de périphérique", + "entity-type": "Type d'entité", + "message-type": "Type de message", + "timeseries-key": "Clé de série temporelle", + "type": "Type", + "first-name": "Prénom", + "last-name": "Nom de famille", + "label": "Étiquette", + "originator-fields-mapping": "Mappage des champs de l’origine", + "add-mapped-originator-fields-to": "Ajouter les champs de l’origine mappés à", + "fields": "Champs", + "skip-empty-fields": "Ignorer les champs vides", + "skip-empty-fields-tooltip": "Les champs vides ne seront pas ajoutés au message de sortie ou aux métadonnées de sortie.", + "fetch-interval": "Intervalle de récupération", + "fetch-strategy": "Stratégie de récupération", + "fetch-timeseries-from-to": "Récupérer les séries temporelles de {{startInterval}} {{startIntervalTimeUnit}} à {{endInterval}} {{endIntervalTimeUnit}}.", + "fetch-timeseries-from-to-invalid": "Récupération des séries invalide (« Début de l'intervalle » doit être inférieur à « Fin de l'intervalle »).", + "use-metadata-dynamic-interval-tooltip": "Utilise un intervalle dynamique basé sur le message et les métadonnées si activé.", + "all-mode-hint": "En mode « Tout », récupère la télémétrie selon l'intervalle et les paramètres définis.", + "first-mode-hint": "Récupère la télémétrie la plus proche du début de l'intervalle.", + "last-mode-hint": "Récupère la télémétrie la plus proche de la fin de l'intervalle.", + "ascending": "Croissant", + "descending": "Décroissant", + "min": "Minimum", + "max": "Maximum", + "average": "Moyenne", + "sum": "Somme", + "count": "Nombre", + "none": "Aucun", + "last-level-relation-tooltip": "Recherche des entités liées uniquement au niveau de relation maximum défini si activé.", + "last-level-device-relation-tooltip": "Recherche des périphériques liés uniquement au niveau spécifié si activé.", + "data-to-fetch": "Données à récupérer", + "mapping-of-customers": "Mappage des clients", + "map-fields-required": "Tous les champs de mappage sont requis.", + "attributes": "Attributs", + "related-device-attributes": "Attributs du périphérique lié", + "add-selected-attributes-to": "Ajouter les attributs sélectionnés à", + "device-profiles": "Profils de périphérique", + "mapping-of-tenant": "Mappage du locataire", + "add-attribute-key": "Ajouter une clé d’attribut", + "message-template": "Modèle de message", + "message-template-required": "Le modèle de message est requis", + "use-system-slack-settings": "Utiliser les paramètres Slack système", + "slack-api-token": "Jeton API Slack", + "slack-api-token-required": "Le jeton API Slack est requis", + "keys-mapping": "Mappage des clés", + "add-key": "Ajouter une clé", + "recipients": "Destinataires", + "message-subject-and-content": "Objet et contenu du message", + "template-rules-hint": "Les champs supportent la templatization avec $[messageKey] ou ${metadataKey}.", + "originator-customer-desc": "Utilise le client de l’origine du message comme nouvelle origine.", + "originator-tenant-desc": "Utilise le locataire actuel comme origine.", + "originator-related-entity-desc": "Utilise une entité liée comme origine, selon le type et la direction de relation.", + "originator-alarm-originator-desc": "Utilise l’origine de l’alarme si le message provient d’une entité d’alarme.", + "originator-entity-by-name-pattern-desc": "Utilise une entité récupérée par type et modèle de nom.", + "email-from-template-hint": "Utilisez $[messageKey] ou ${metadataKey} pour remplir dynamiquement.", + "recipients-block-main-hint": "Liste d’adresses séparées par des virgules. Supporte la templatization.", + "forward-msg-default-rule-chain": "Transférer au rule chain par défaut de l’origine", + "forward-msg-default-rule-chain-tooltip": "Transfère au rule chain par défaut ou configuré si aucun n’est défini dans le profil.", + "exclude-zero-deltas": "Exclure les deltas nuls du message sortant", + "exclude-zero-deltas-hint": "Ajoute la clé {{outputValueKey}} uniquement si sa valeur est différente de zéro.", + "exclude-zero-deltas-time-difference-hint": "Ajoute les clés {{outputValueKey}} et {{periodValueKey}} uniquement si {{outputValueKey}} ≠ 0.", + "search-direction-from": "De l’origine vers l’entité cible", + "search-direction-to": "De l’entité cible vers l’origine", + "del-relation-direction-from": "Depuis l’origine", + "del-relation-direction-to": "Vers l’origine", + "target-entity": "Entité cible", + "function-configuration": "Configuration de la fonction", + "function-name": "Nom de la fonction", + "function-name-required": "Le nom de la fonction est requis.", + "qualifier": "Qualificateur", + "qualifier-hint": "Par défaut, \"$LATEST\" sera utilisé si vide.", + "aws-credentials": "Identifiants AWS", + "connection-timeout": "Délai de connexion", + "connection-timeout-required": "Le délai de connexion est requis.", + "connection-timeout-min": "Valeur minimale : 0", + "connection-timeout-hint": "Temps d’attente (en sec) pour établir la connexion. 0 = infini (non recommandé).", + "request-timeout": "Délai de requête", + "request-timeout-required": "Le délai de requête est requis", + "request-timeout-min": "Valeur minimale : 0", + "request-timeout-hint": "Temps d’attente (en sec) pour exécution de la requête. 0 = infini (non recommandé).", + "units": "Unités", + "tell-failure-aws-lambda": "Signaler un échec si l’exécution AWS Lambda échoue", + "tell-failure-aws-lambda-hint": "Déclenche un échec du traitement si AWS Lambda lève une exception.", + "basic-mode": "Mode basique", + "advanced-mode": "Mode avancé", + "save-time-series": { + "processing-settings": "Paramètres de traitement", + "processing-settings-hint": "Définissez comment les messages entrants sont traités. Les paramètres de base permettent de sélectionner des stratégies préconfigurées, tandis que les paramètres avancés permettent de choisir des stratégies individuelles pour chaque action.", + "advanced-settings-hint": "Soyez prudent lors de la configuration des stratégies de traitement. Certaines combinaisons peuvent entraîner un comportement inattendu.", + "strategy": "Stratégie", + "deduplication-interval": "Intervalle de déduplication", + "deduplication-interval-required": "L'intervalle de déduplication est requis", + "deduplication-interval-min-max-range": "L'intervalle de déduplication doit être d'au moins 1 seconde et d'au plus 1 jour", + "strategy-type": { + "every-message": "À chaque message", + "skip": "Ignorer", + "deduplicate": "Dédupliquer", + "web-sockets-only": "WebSockets uniquement" + }, + "time-series": "Séries temporelles", + "latest": "Dernières valeurs", + "web-sockets": "WebSockets", + "calculated-fields": "Champs calculés" + }, + "save-attribute": { + "processing-settings": "Paramètres de traitement", + "processing-settings-hint": "Définissez comment les messages entrants sont traités. Les paramètres de base permettent de sélectionner des stratégies préconfigurées, tandis que les paramètres avancés permettent de choisir des stratégies individuelles pour chaque action.", + "advanced-settings-hint": "Soyez prudent lors de la configuration des stratégies de traitement. Certaines combinaisons peuvent entraîner un comportement inattendu.", + "strategy": "Stratégie", + "deduplication-interval": "Intervalle de déduplication", + "deduplication-interval-required": "L'intervalle de déduplication est requis", + "deduplication-interval-min-max-range": "L'intervalle de déduplication doit être d'au moins 1 seconde et d'au plus 1 jour", + "scope": "Portée", + "strategy-type": { + "every-message": "À chaque message", + "skip": "Ignorer", + "deduplicate": "Dédupliquer", + "web-sockets-only": "WebSockets uniquement" + }, + "attributes": "Attributs" + }, + "key-val": { + "key": "Clé", + "value": "Valeur", + "see-examples": "Voir des exemples.", + "remove-entry": "Supprimer l'entrée", + "remove-mapping-entry": "Supprimer le mappage", + "add-mapping-entry": "Ajouter un mappage", + "add-entry": "Ajouter une entrée", + "copy-key-values-from": "Copier les paires clé-valeur depuis", + "delete-key-values": "Supprimer les paires clé-valeur", + "delete-key-values-from": "Supprimer les paires clé-valeur depuis", + "at-least-one-key-error": "Au moins une clé doit être sélectionnée.", + "unique-key-value-pair-error": "'{{keyText}}' doit être différent de '{{valText}}' !" + }, + "mail-body-types": { + "plain-text": "Texte brut", + "html": "HTML", + "dynamic": "Dynamique", + "use-body-type-template": "Utiliser le modèle de type de corps", + "plain-text-description": "Texte simple, non formaté, sans style ou mise en forme particulière.", + "html-text-description": "Permet d'utiliser des balises HTML pour la mise en forme, les liens et les images dans le corps du message.", + "dynamic-text-description": "Permet d'utiliser dynamiquement le texte brut ou HTML selon le modèle.", + "after-template-evaluation-hint": "Après évaluation du modèle, la valeur doit être true pour HTML, et false pour Texte brut." + } + }, + "timezone": { + "timezone": "Fuseau horaire", + "select-timezone": "Sélectionner un fuseau horaire", + "no-timezones-matching": "Aucun fuseau horaire correspondant à '{{timezone}}' trouvé.", + "timezone-required": "Le fuseau horaire est requis.", + "browser-time": "Heure du navigateur" + }, + "queue": { + "queue-name": "File d’attente", + "no-queues-found": "Aucune file d’attente trouvée.", + "no-queues-matching": "Aucune file d’attente correspondant à '{{queue}}' trouvée.", + "select-name": "Sélectionner le nom de la file d’attente", + "name": "Nom", + "name-required": "Le nom de la file d’attente est requis !", + "name-unique": "Le nom de la file d’attente n’est pas unique !", + "name-pattern": "Le nom de la file d’attente contient un caractère non autorisé (seuls les caractères ASCII alphanumériques, '.', '_' et '-' sont permis) !", + "queue-required": "La file d’attente est requise !", + "topic-required": "Le sujet de la file d’attente est requis !", + "poll-interval-required": "L’intervalle d’interrogation est requis !", + "poll-interval-min-value": "L’intervalle d’interrogation ne peut pas être inférieur à 1", + "partitions-required": "Les partitions sont requises !", + "partitions-min-value": "La valeur des partitions ne peut pas être inférieure à 1", + "pack-processing-timeout-required": "Le délai de traitement est requis", + "pack-processing-timeout-min-value": "Le délai de traitement ne peut pas être inférieur à 1", + "batch-size-required": "La taille du lot est requise !", + "batch-size-min-value": "La taille du lot ne peut pas être inférieure à 1", + "retries-required": "Le nombre de tentatives est requis !", + "retries-min-value": "Le nombre de tentatives ne peut pas être négatif", + "failure-percentage-required": "Le pourcentage d’échec est requis !", + "failure-percentage-min-value": "Le pourcentage d’échec ne peut pas être inférieur à 0", + "failure-percentage-max-value": "Le pourcentage d’échec ne peut pas être supérieur à 100", + "pause-between-retries-required": "Le délai entre les tentatives est requis !", + "pause-between-retries-min-value": "Le délai entre les tentatives ne peut pas être inférieur à 1", + "max-pause-between-retries-required": "Le délai maximal entre les tentatives est requis !", + "max-pause-between-retries-min-value": "Le délai maximal entre les tentatives ne peut pas être inférieur à 1", + "submit-strategy-type-required": "Le type de stratégie d’envoi est requis !", + "processing-strategy-type-required": "Le type de stratégie de traitement est requis !", + "queues": "Files d’attente", + "selected-queues": "{ count, plural, =1 {1 file d’attente} other {# files d’attente} } sélectionnée(s)", + "delete-queue-title": "Êtes-vous sûr de vouloir supprimer la file d’attente '{{queueName}}' ?", + "delete-queues-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 file d’attente} other {# files d’attente} } ?", + "delete-queue-text": "Attention, après confirmation, la file d’attente et toutes les données associées seront irrécupérables.", + "delete-queues-text": "Après confirmation, toutes les files d’attente sélectionnées seront supprimées et ne seront plus accessibles.", + "search": "Rechercher une file d’attente", + "add": "Ajouter une file d’attente", + "details": "Détails de la file d’attente", + "topic": "Sujet", + "submit-settings": "Paramètres d’envoi", + "submit-strategy": "Type de stratégie *", + "grouping-parameter": "Paramètre de regroupement", + "processing-settings": "Paramètres de traitement des tentatives", + "processing-strategy": "Type de traitement *", + "retries-settings": "Paramètres de tentatives", + "polling-settings": "Paramètres d’interrogation", + "batch-processing": "Traitement par lots", + "poll-interval": "Intervalle d’interrogation", + "partitions": "Partitions", + "immediate-processing": "Traitement immédiat", + "consumer-per-partition": "Interroger pour chaque consommateur", + "consumer-per-partition-hint": "Activer un consommateur distinct pour chaque partition", + "duplicate-msg-to-all-partitions": "Dupliquer le message vers toutes les partitions", + "processing-timeout": "Délai de traitement, ms", + "batch-size": "Taille du lot", + "retries": "Nombre de tentatives (0 – illimité)", + "failure-percentage": "Échecs pour ignorer les tentatives, %", + "pause-between-retries": "Réessayer dans, sec", + "max-pause-between-retries": "Réessayer à nouveau dans, sec", + "delete": "Supprimer la file d’attente", + "copyId": "Copier l’ID de la file d’attente", + "idCopiedMessage": "L’ID de la file d’attente a été copié dans le presse-papiers", + "description": "Description", + "description-hint": "Ce texte sera affiché dans la description de la file d’attente à la place de la stratégie sélectionnée", + "alt-description": "Stratégie d’envoi : {{submitStrategy}}, Stratégie de traitement : {{processingStrategy}}", + "custom-properties": "Propriétés personnalisées", + "custom-properties-hint": "Propriétés personnalisées de création de file (topic), ex : 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies": { + "sequential-by-originator-label": "Séquentiel par initiateur", + "sequential-by-originator-hint": "Un nouveau message, par exemple pour l'appareil A, n’est pas soumis tant que le message précédent pour l'appareil A n’a pas été accusé de réception", + "sequential-by-tenant-label": "Séquentiel par locataire", + "sequential-by-tenant-hint": "Un nouveau message, par exemple pour le locataire A, n’est pas soumis tant que le message précédent pour le locataire A n’a pas été accusé de réception", + "sequential-label": "Séquentiel", + "sequential-hint": "Un nouveau message n’est pas soumis tant que le précédent n’a pas été accusé de réception", + "burst-label": "Rafale", + "burst-hint": "Tous les messages sont soumis aux chaînes de règles dans l’ordre de leur arrivée", + "batch-label": "Par lot", + "batch-hint": "Un nouveau lot n’est pas soumis tant que le lot précédent n’a pas été accusé de réception", + "skip-all-failures-label": "Ignorer tous les échecs", + "skip-all-failures-hint": "Ignorer tous les échecs", + "skip-all-failures-and-timeouts-label": "Ignorer tous les échecs et expirations", + "skip-all-failures-and-timeouts-hint": "Ignorer tous les échecs et expirations de délai", + "retry-all-label": "Réessayer tous", + "retry-all-hint": "Réessayer tous les messages du lot de traitement", + "retry-failed-label": "Réessayer les échecs", + "retry-failed-hint": "Réessayer tous les messages échoués du lot de traitement", + "retry-timeout-label": "Réessayer les expirations", + "retry-timeout-hint": "Réessayer tous les messages ayant expiré dans le lot de traitement", + "retry-failed-and-timeout-label": "Réessayer les échecs et expirations", + "retry-failed-and-timeout-hint": "Réessayer tous les messages échoués et ayant expiré dans le lot de traitement" + } + }, + "queue-statistics": { + "queue-statistics": "Statistiques de file d'attente", + "no-queue-statistics-matching": "Aucune statistique de file d'attente correspondant à '{{entity}}' n'a été trouvée.", + "queue-statistics-required": "Les statistiques de file d'attente sont requises.", + "list-of-queue-statistics": "{ count, plural, =1 {Une statistique de file d'attente} other {Liste de # statistiques de file d'attente} }", + "selected-queue-statistics": "{ count, plural, =1 {1 statistique de file d'attente} other {# statistiques de file d'attente} } sélectionnée(s)", + "no-queue-statistics-text": "Aucune statistique de file d'attente trouvée", + "queue-statistics-starts-with": "Statistiques de file d'attente dont les noms commencent par '{{prefix}}'" + }, + "server-error": { + "general": "Erreur générale du serveur", + "authentication": "Erreur d'authentification", + "jwt-token-expired": "Jeton JWT expiré", + "tenant-trial-expired": "Période d'essai du locataire expirée", + "credentials-expired": "Identifiants expirés", + "permission-denied": "Permission refusée", + "invalid-arguments": "Arguments invalides", + "bad-request-params": "Paramètres de requête incorrects", + "item-not-found": "Élément introuvable", + "too-many-requests": "Trop de requêtes", + "too-many-updates": "Trop de mises à jour" + }, + "tenant": { + "tenant": "Locataire", + "tenants": "Locataires", + "management": "Gestion des locataires", + "add": "Ajouter un locataire", + "admins": "Administrateurs", + "manage-tenant-admins": "Gérer les administrateurs du locataire", + "delete": "Supprimer le locataire", + "add-tenant-text": "Ajouter un nouveau locataire", + "no-tenants-text": "Aucun locataire trouvé", + "tenant-details": "Détails du locataire", + "title-max-length": "Le titre doit contenir moins de 256 caractères", + "delete-tenant-title": "Êtes-vous sûr de vouloir supprimer le locataire '{{tenantTitle}}' ?", + "delete-tenant-text": "Attention, après confirmation, le locataire et toutes les données associées seront irrécupérables.", + "delete-tenants-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 locataire} other {# locataires} } ?", + "delete-tenants-action-title": "Supprimer { count, plural, =1 {1 locataire} other {# locataires} }", + "delete-tenants-text": "Attention, après confirmation, tous les locataires sélectionnés seront supprimés ainsi que toutes les données associées.", + "title": "Titre", + "title-required": "Le titre est requis.", + "description": "Description", + "details": "Détails", + "events": "Événements", + "copyId": "Copier l'identifiant du locataire", + "idCopiedMessage": "Identifiant du locataire copié dans le presse-papiers", + "select-tenant": "Sélectionner un locataire", + "no-tenants-matching": "Aucun locataire correspondant à '{{entity}}' trouvé.", + "tenant-required": "Le locataire est requis", + "search": "Rechercher des locataires", + "selected-tenants": "{ count, plural, =1 {1 locataire} other {# locataires} } sélectionné(s)", + "isolated-tb-rule-engine": "Utiliser des files d’attente isolées pour le moteur de règles ThingsBoard", + "isolated-tb-rule-engine-details": "Chaque locataire aura des files d’attente dédiées pour le moteur de règles" + }, + "tenant-profile": { + "tenant-profile": "Profil de locataire", + "tenant-profiles": "Profils de locataire", + "add": "Ajouter un profil de locataire", + "add-profile": "Ajouter un profil", + "debug": "Déboguer", + "edit": "Modifier le profil de locataire", + "tenant-profile-details": "Détails du profil de locataire", + "no-tenant-profiles-text": "Aucun profil de locataire trouvé", + "name-max-length": "Le nom doit contenir moins de 256 caractères", + "search": "Rechercher des profils de locataire", + "selected-tenant-profiles": "{ count, plural, =1 {1 profil de locataire} other {# profils de locataire} } sélectionné(s)", + "no-tenant-profiles-matching": "Aucun profil de locataire correspondant à '{{entity}}' trouvé.", + "tenant-profile-required": "Le profil de locataire est requis", + "idCopiedMessage": "L'identifiant du profil de locataire a été copié dans le presse-papiers", + "set-default": "Définir comme profil par défaut", + "delete": "Supprimer le profil de locataire", + "copyId": "Copier l'identifiant du profil de locataire", + "name": "Nom", + "name-required": "Le nom est requis.", + "data": "Données du profil", + "profile-configuration": "Configuration du profil", + "description": "Description", + "default": "Défaut", + "delete-tenant-profile-title": "Êtes-vous sûr de vouloir supprimer le profil de locataire '{{tenantProfileName}}' ?", + "delete-tenant-profile-text": "Attention, après confirmation, le profil de locataire et toutes les données associées seront irrécupérables.", + "delete-tenant-profiles-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 profil de locataire} other {# profils de locataire} } ?", + "delete-tenant-profiles-text": "Attention, après confirmation, tous les profils de locataire sélectionnés seront supprimés ainsi que toutes les données associées.", + "set-default-tenant-profile-title": "Êtes-vous sûr de vouloir définir le profil de locataire '{{tenantProfileName}}' comme profil par défaut ?", + "set-default-tenant-profile-text": "Après confirmation, le profil sera défini comme profil par défaut et sera utilisé pour les nouveaux locataires sans profil spécifié.", + "no-tenant-profiles-found": "Aucun profil de locataire trouvé.", + "create-new-tenant-profile": "Créer un nouveau !", + "create-tenant-profile": "Créer un nouveau profil de locataire", + "import": "Importer un profil de locataire", + "export": "Exporter un profil de locataire", + "export-failed-error": "Impossible d’exporter le profil de locataire : {{error}}", + "tenant-profile-file": "Fichier de profil de locataire", + "invalid-tenant-profile-file-error": "Impossible d’importer le profil : structure de données invalide.", + "advanced-settings": "Paramètres avancés", + "entities": "Entités", + "rule-engine": "Moteur de règles", + "time-to-live": "Durée de vie", + "calculated-fields": "Champs calculés", + "alarms-and-notifications": "Alarmes et notifications", + "ota-files-in-bytes": "Fichiers", + "ws-title": "WS", + "unlimited": "(0 - illimité)", + "maximum-devices": "Nombre maximum d'appareils", + "maximum-devices-required": "Le nombre maximum d'appareils est requis.", + "maximum-devices-range": "Le nombre maximum d'appareils ne peut pas être négatif", + "maximum-assets": "Nombre maximum d'actifs", + "maximum-assets-required": "Le nombre maximum d'actifs est requis.", + "maximum-assets-range": "Le nombre maximum d'actifs ne peut pas être négatif", + "maximum-customers": "Nombre maximum de clients", + "maximum-customers-required": "Le nombre maximum de clients est requis.", + "maximum-customers-range": "Le nombre maximum de clients ne peut pas être négatif", + "maximum-users": "Nombre maximum d'utilisateurs", + "maximum-users-required": "Le nombre maximum d'utilisateurs est requis.", + "maximum-users-range": "Le nombre maximum d'utilisateurs ne peut pas être négatif", + "maximum-dashboards": "Nombre maximum de tableaux de bord", + "maximum-dashboards-required": "Le nombre maximum de tableaux de bord est requis.", + "maximum-dashboards-range": "Le nombre maximum de tableaux de bord ne peut pas être négatif", + "maximum-edges": "Nombre maximum d'edges", + "maximum-edges-required": "Le nombre maximum d'edges est requis.", + "maximum-edges-range": "Le nombre maximum d'edges ne peut pas être négatif", + "maximum-rule-chains": "Nombre maximum de chaînes de règles", + "maximum-rule-chains-required": "Le nombre maximum de chaînes de règles est requis.", + "maximum-rule-chains-range": "Le nombre maximum de chaînes de règles ne peut pas être négatif", + "maximum-resources-sum-data-size": "Taille maximale totale des fichiers de ressources (octets)", + "maximum-resources-sum-data-size-required": "La taille totale maximale des fichiers de ressources est requise.", + "maximum-resources-sum-data-size-range": "La taille totale maximale des fichiers de ressources ne peut pas être négative", + "maximum-resource-size": "Taille maximale du fichier ressource (octets)", + "maximum-resource-size-required": "La taille maximale du fichier ressource est requise", + "maximum-resource-size-range": "La taille maximale du fichier ressource ne peut pas être négative", + "maximum-ota-packages-sum-data-size": "Taille maximale totale des fichiers OTA (octets)", + "maximum-ota-package-sum-data-size-required": "La taille maximale totale des fichiers OTA est requise.", + "maximum-ota-package-sum-data-size-range": "La taille maximale totale des fichiers OTA ne peut pas être négative", + "maximum-debug-duration-min": "Durée maximale de débogage (min)", + "maximum-debug-duration-min-range": "La durée maximale de débogage ne peut pas être négative", + "rest-requests-for-tenant": "Requêtes REST pour le locataire", + "transport-tenant-telemetry-msg-rate-limit": "Messages de télémétrie du locataire (transport)", + "transport-tenant-telemetry-data-points-rate-limit": "Points de données de télémétrie du locataire (transport)", + "transport-device-msg-rate-limit": "Messages de l'appareil (transport)", + "transport-device-telemetry-msg-rate-limit": "Messages de télémétrie de l'appareil (transport)", + "transport-device-telemetry-data-points-rate-limit": "Points de données de télémétrie de l'appareil (transport)", + "transport-gateway-msg-rate-limit": "Messages de la passerelle (transport)", + "transport-gateway-telemetry-msg-rate-limit": "Messages de télémétrie de la passerelle (transport)", + "transport-gateway-telemetry-data-points-rate-limit": "Points de données de télémétrie de la passerelle (transport)", + "transport-gateway-device-msg-rate-limit": "Messages de l'appareil de la passerelle (transport)", + "transport-gateway-device-telemetry-msg-rate-limit": "Messages de télémétrie de l'appareil de la passerelle (transport)", + "transport-gateway-device-telemetry-data-points-rate-limit": "Points de données de télémétrie de l'appareil de la passerelle (transport)", + "tenant-entity-export-rate-limit": "Création de version d'entité", + "tenant-entity-import-rate-limit": "Chargement de version d'entité", + "tenant-notification-request-rate-limit": "Requêtes de notification", + "tenant-notification-requests-per-rule-rate-limit": "Requêtes de notification par règle", + "max-calculated-fields": "Nombre maximal de champs calculés par entité", + "max-calculated-fields-range": "Le nombre maximal de champs calculés ne peut pas être négatif", + "max-calculated-fields-required": "Le nombre maximal de champs calculés est requis", + "max-data-points-per-rolling-arg": "Nombre maximal de points de données dans les arguments glissants", + "max-data-points-per-rolling-arg-range": "Le nombre ne peut pas être négatif", + "max-data-points-per-rolling-arg-required": "Le nombre maximal de points de données est requis", + "max-arguments-per-cf": "Nombre maximal d’arguments par champ calculé", + "max-arguments-per-cf-range": "Le nombre maximal d’arguments ne peut pas être négatif", + "max-arguments-per-cf-required": "Le nombre maximal d’arguments est requis", + "max-state-size": "Taille maximale de l'état (en Ko)", + "max-state-size-range": "La taille maximale ne peut pas être négative", + "max-state-size-required": "La taille maximale de l'état est requise", + "max-value-argument-size": "Taille maximale d'un argument de valeur unique (en Ko)", + "max-value-argument-size-range": "La taille maximale ne peut pas être négative", + "max-value-argument-size-required": "La taille maximale de la valeur est requise", + "max-transport-messages": "Nombre maximal de messages de transport", + "max-transport-messages-required": "Le nombre maximal de messages de transport est requis.", + "max-transport-messages-range": "Le nombre maximal de messages de transport ne peut pas être négatif", + "max-transport-data-points": "Nombre maximal de points de données de transport", + "max-transport-data-points-required": "Le nombre maximal de points de données est requis.", + "max-transport-data-points-range": "Le nombre maximal de points de données ne peut pas être négatif", + "max-r-e-executions": "Nombre maximal d'exécutions du moteur de règles", + "max-r-e-executions-required": "Le nombre d'exécutions du moteur de règles est requis.", + "max-r-e-executions-range": "Le nombre d'exécutions ne peut pas être négatif", + "max-j-s-executions": "Nombre maximal d'exécutions JavaScript", + "max-j-s-executions-required": "Le nombre maximal d'exécutions JavaScript est requis.", + "max-j-s-executions-range": "Le nombre maximal d'exécutions JavaScript ne peut pas être négatif", + "max-tbel-executions": "Nombre maximal d'exécutions TBEL", + "max-tbel-executions-required": "Le nombre maximal d'exécutions TBEL est requis.", + "max-tbel-executions-range": "Le nombre maximal d'exécutions TBEL ne peut pas être négatif", + "max-d-p-storage-days": "Nombre maximal de jours de stockage des points de données", + "max-d-p-storage-days-required": "Le nombre de jours est requis.", + "max-d-p-storage-days-range": "Le nombre ne peut pas être négatif", + "default-storage-ttl-days": "Durée de vie par défaut du stockage (en jours)", + "default-storage-ttl-days-required": "La durée par défaut est requise.", + "default-storage-ttl-days-range": "La durée ne peut pas être négative", + "alarms-ttl-days": "Durée de vie des alarmes (en jours)", + "alarms-ttl-days-required": "La durée de vie des alarmes est requise", + "alarms-ttl-days-days-range": "La durée ne peut pas être négative", + "rpc-ttl-days": "Durée de vie RPC (en jours)", + "rpc-ttl-days-required": "La durée de vie RPC est requise", + "rpc-ttl-days-days-range": "La durée ne peut pas être négative", + "queue-stats-ttl-days": "Durée de vie des statistiques de file d'attente", + "queue-stats-ttl-days-required": "La durée est requise", + "queue-stats-ttl-days-range": "La durée ne peut pas être négative", + "rule-engine-exceptions-ttl-days": "Durée de vie des exceptions du moteur de règles", + "rule-engine-exceptions-ttl-days-required": "La durée est requise", + "rule-engine-exceptions-ttl-days-range": "La durée ne peut pas être négative", + "max-rule-node-executions-per-message": "Nombre maximal d'exécutions par message d'un nœud de règle", + "max-rule-node-executions-per-message-required": "Le nombre est requis.", + "max-rule-node-executions-per-message-range": "Le nombre ne peut pas être négatif", + "max-emails": "Nombre maximal d'e-mails envoyés", + "max-emails-required": "Le nombre d'e-mails est requis.", + "max-emails-range": "Le nombre ne peut pas être négatif", + "sms-enabled": "SMS activé", + "max-sms": "Nombre maximal de SMS envoyés", + "max-sms-required": "Le nombre de SMS est requis.", + "max-sms-range": "Le nombre ne peut pas être négatif", + "max-created-alarms": "Nombre maximal d'alarmes créées", + "max-created-alarms-required": "Le nombre est requis.", + "max-created-alarms-range": "Le nombre ne peut pas être négatif", + "no-queue": "Aucune file d’attente configurée", + "add-queue": "Ajouter une file d’attente", + "queues-with-count": "Files d’attente ({{count}})", + "tenant-rest-limits": "Requêtes REST pour le locataire", + "customer-rest-limits": "Requêtes REST pour le client", + "incorrect-pattern-for-rate-limits": "Le format est une liste de paires séparées par des virgules, contenant une capacité et une période (en secondes), séparées par deux-points, par ex. 100:1,2000:60", + "too-small-value-zero": "La valeur doit être supérieure à 0", + "too-small-value-one": "La valeur doit être supérieure à 1", + "queue-size-is-limited-by-system-configuration": "La taille de la file d’attente est également limitée par la configuration système.", + "cassandra-tenant-limits-configuration": "Requête Cassandra pour le locataire", + "ws-limit-max-sessions-per-tenant": "Nombre maximal de sessions par locataire", + "ws-limit-max-sessions-per-customer": "Nombre maximal de sessions par client", + "ws-limit-max-sessions-per-regular-user": "Nombre maximal de sessions par utilisateur régulier", + "ws-limit-max-sessions-per-public-user": "Nombre maximal de sessions par utilisateur public", + "ws-limit-queue-per-session": "Taille maximale de la file de messages par session", + "ws-limit-max-subscriptions-per-tenant": "Nombre maximal d’abonnements par locataire", + "ws-limit-max-subscriptions-per-customer": "Nombre maximal d’abonnements par client", + "ws-limit-max-subscriptions-per-regular-user": "Nombre maximal d’abonnements par utilisateur régulier", + "ws-limit-max-subscriptions-per-public-user": "Nombre maximal d’abonnements par utilisateur public", + "ws-limit-updates-per-session": "Mises à jour WS par session", + "rate-limits": { + "add-limit": "Ajouter une limite", + "advanced-settings": "Paramètres avancés", + "edit-limit": "Modifier la limite", + "but-less-than": "mais inférieur à", + "calculated-field-debug-event-rate-limit": "Événements de débogage des champs calculés", + "edit-calculated-field-debug-event-rate-limit": "Modifier la limite des événements de débogage de champ calculé", + "edit-transport-tenant-msg-title": "Modifier les limites de messages de transport du locataire", + "edit-transport-tenant-telemetry-msg-title": "Modifier les limites des messages de télémétrie du locataire", + "edit-transport-tenant-telemetry-data-points-title": "Modifier les limites des points de données de télémétrie du locataire", + "edit-transport-device-msg-title": "Modifier les limites des messages de l’appareil", + "edit-transport-device-telemetry-msg-title": "Modifier les limites des messages de télémétrie de l’appareil", + "edit-transport-device-telemetry-data-points-title": "Modifier les limites des points de données de télémétrie de l’appareil", + "edit-transport-gateway-msg-title": "Modifier les limites des messages de la passerelle", + "edit-transport-gateway-telemetry-msg-title": "Modifier les limites des messages de télémétrie de la passerelle", + "edit-transport-gateway-telemetry-data-points-title": "Modifier les limites des points de données de télémétrie de la passerelle", + "edit-transport-gateway-device-msg-title": "Modifier les limites des messages des appareils de la passerelle", + "edit-transport-gateway-device-telemetry-msg-title": "Modifier les limites des messages de télémétrie des appareils de la passerelle", + "edit-transport-gateway-device-telemetry-data-points-title": "Modifier les limites des points de données de télémétrie des appareils de la passerelle", + "edit-tenant-rest-limits-title": "Modifier les limites des requêtes REST du locataire", + "edit-customer-rest-limits-title": "Modifier les limites des requêtes REST du client", + "edit-ws-limit-updates-per-session-title": "Modifier les limites des mises à jour WS par session", + "edit-cassandra-tenant-limits-configuration-title": "Modifier les limites des requêtes Cassandra du locataire", + "edit-tenant-entity-export-rate-limit-title": "Modifier les limites de création de version d’entité", + "edit-tenant-entity-import-rate-limit-title": "Modifier les limites de chargement de version d’entité", + "edit-tenant-notification-request-rate-limit-title": "Modifier les limites des requêtes de notification", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "Modifier les limites des requêtes de notification par règle", + "edit-edge-events-rate-limit": "Modifier les limites des événements Edge", + "edit-edge-events-per-edge-rate-limit": "Modifier les limites des événements Edge par instance", + "edge-events-rate-limit": "Événements Edge", + "edge-events-per-edge-rate-limit": "Événements Edge par instance", + "edit-edge-uplink-messages-rate-limit": "Modifier les limites des messages montants Edge", + "edit-edge-uplink-messages-per-edge-rate-limit": "Modifier les limites des messages montants Edge par instance", + "edge-uplink-messages-rate-limit": "Messages montants Edge", + "edge-uplink-messages-per-edge-rate-limit": "Messages montants Edge par instance", + "messages-per": "messages par", + "not-set": "Non défini", + "number-of-messages": "Nombre de messages", + "number-of-messages-required": "Le nombre de messages est requis.", + "number-of-messages-min": "La valeur minimale est 1.", + "preview": "Aperçu", + "per-seconds": "Par secondes", + "per-seconds-required": "La durée est requise.", + "per-seconds-min": "La valeur minimale est 1.", + "rate-limits": "Limites de débit", + "remove-limit": "Supprimer la limite", + "transport-tenant-msg": "Messages de transport du locataire", + "transport-tenant-telemetry-msg": "Messages de télémétrie du locataire", + "transport-tenant-telemetry-data-points": "Points de données de télémétrie du locataire", + "transport-device-msg": "Messages de l’appareil", + "transport-device-telemetry-msg": "Messages de télémétrie de l’appareil", + "transport-device-telemetry-data-points": "Points de données de télémétrie de l’appareil", + "transport-gateway-msg": "Messages de la passerelle", + "transport-gateway-telemetry-msg": "Messages de télémétrie de la passerelle", + "transport-gateway-telemetry-data-points": "Points de données de télémétrie de la passerelle", + "transport-gateway-device-msg": "Messages des appareils de la passerelle", + "transport-gateway-device-telemetry-msg": "Messages de télémétrie des appareils de la passerelle", + "transport-gateway-device-telemetry-data-points": "Points de données de télémétrie des appareils de la passerelle", + "sec": "sec" + } + }, + "timeinterval": { + "seconds-interval": "{ seconds, plural, =1 {1 seconde} other {# secondes} }", + "minutes-interval": "{ minutes, plural, =1 {1 minute} other {# minutes} }", + "hours-interval": "{ hours, plural, =1 {1 heure} other {# heures} }", + "days-interval": "{ days, plural, =1 {1 jour} other {# jours} }", + "days": "Jours", + "hours": "Heures", + "minutes": "Minutes", + "seconds": "Secondes", + "advanced": "Avancé", + "custom": "Personnalisé", + "predefined": { + "yesterday": "Hier", + "day-before-yesterday": "Avant-hier", + "this-day-last-week": "Ce jour la semaine dernière", + "previous-week": "Semaine précédente (dim. - sam.)", + "previous-week-iso": "Semaine précédente (lun. - dim.)", + "previous-month": "Mois précédent", + "previous-quarter": "Trimestre précédent", + "previous-half-year": "Semestre précédent", + "previous-year": "Année précédente", + "current-hour": "Heure en cours", + "current-day": "Jour en cours", + "current-day-so-far": "Jour en cours (jusqu'à présent)", + "current-week": "Semaine en cours (dim. - sam.)", + "current-week-iso": "Semaine en cours (lun. - dim.)", + "current-week-so-far": "Semaine en cours (jusqu'à présent, dim. - sam.)", + "current-week-iso-so-far": "Semaine en cours (jusqu'à présent, lun. - dim.)", + "current-month": "Mois en cours", + "current-month-so-far": "Mois en cours (jusqu'à présent)", + "current-quarter": "Trimestre en cours", + "current-quarter-so-far": "Trimestre en cours (jusqu'à présent)", + "current-half-year": "Semestre en cours", + "current-half-year-so-far": "Semestre en cours (jusqu'à présent)", + "current-year": "Année en cours", + "current-year-so-far": "Année en cours (jusqu'à présent)" + }, + "type": { + "week": "Semaine (dim. - sam.)", + "week-iso": "Semaine (lun. - dim.)", + "month": "Mois", + "quarter": "Trimestre" + } + }, + "timeunit": { + "milliseconds": "Millisecondes", + "seconds": "Secondes", + "minutes": "Minutes", + "hours": "Heures", + "days": "Jours" + }, + "timewindow": { + "timewindow": "Fenêtre temporelle", + "timewindow-settings": "Paramètres de la fenêtre temporelle", + "years": "{ years, plural, =1 { année } other {# années } }", + "years-short": "{{ years }}a", + "months": "{ months, plural, =1 { mois } other {# mois } }", + "months-short": "{{ months }}M", + "weeks": "{ weeks, plural, =1 { semaine } other {# semaines } }", + "weeks-short": "{{ weeks }}s", + "days": "{ days, plural, =1 { jour } other {# jours } }", + "days-short": "{{ days }}j", + "hours": "{ hours, plural, =0 { heure } =1 {1 heure } other {# heures } }", + "hr": "{{ hr }} h", + "hr-short": "{{ hr }}h", + "minutes": "{ minutes, plural, =0 { minute } =1 {1 minute } other {# minutes } }", + "min": "{{ min }} min", + "min-short": "{{ min }}m", + "seconds": "{ seconds, plural, =0 { seconde } =1 {1 seconde } other {# secondes } }", + "sec": "{{ sec }} sec", + "sec-short": "{{ sec }}s", + "short": { + "years": "{ years, plural, =1 {1 année } other {# années } }", + "days": "{ days, plural, =1 {1 jour } other {# jours } }", + "hours": "{ hours, plural, =1 {1 heure } other {# heures } }", + "minutes": "{{minutes}} min", + "seconds": "{{seconds}} sec" + }, + "realtime": "Temps réel", + "history": "Historique", + "last-prefix": "dernier", + "period": "du {{ startTime }} au {{ endTime }}", + "edit": "Modifier la fenêtre temporelle", + "date-range": "Plage de dates", + "for-all-time": "Tout le temps", + "last": "Dernier", + "time-period": "Période de temps", + "hide": "Cacher", + "interval": "Intervalle", + "just-now": "À l’instant", + "just-now-lower": "à l’instant", + "ago": "il y a", + "style": "Style de la fenêtre temporelle", + "icon": "Icône", + "icon-position": "Position de l’icône", + "icon-position-left": "Gauche", + "icon-position-right": "Droite", + "font": "Police", + "color": "Couleur", + "displayTypePrefix": "Afficher le préfixe Temps réel / Historique", + "preview": "Aperçu", + "relative": "Relatif", + "range": "Intervalle", + "hide-timewindow-section": "Cacher la section de la fenêtre temporelle aux utilisateurs finaux", + "hide-last-interval": "Cacher le dernier intervalle aux utilisateurs finaux", + "hide-relative-interval": "Cacher l’intervalle relatif aux utilisateurs finaux", + "hide-fixed-interval": "Cacher l’intervalle fixe aux utilisateurs finaux", + "hide-aggregation": "Cacher l’agrégation aux utilisateurs finaux", + "hide-group-interval": "Cacher l’intervalle de regroupement aux utilisateurs finaux", + "hide-max-values": "Cacher les valeurs max aux utilisateurs finaux", + "hide-timezone": "Cacher le fuseau horaire aux utilisateurs finaux", + "disable-custom-interval": "Désactiver la sélection d’intervalle personnalisé", + "edit-aggregation-functions-list": "Modifier la liste des fonctions d’agrégation", + "edit-aggregation-functions-list-hint": "La liste des options disponibles peut être spécifiée.", + "allowed-aggregation-functions": "Fonctions d’agrégation autorisées", + "edit-intervals-list": "Modifier la liste des intervalles", + "allowed-agg-intervals": "Intervalles de regroupement", + "default-agg-interval": "Intervalle de regroupement par défaut", + "edit-intervals-list-hint": "Il est possible de spécifier la liste des options d’intervalle disponibles.", + "edit-grouping-intervals-list-hint": "Il est possible de configurer la liste des intervalles de regroupement et l’intervalle par défaut.", + "all": "Tous" + }, + "tooltip": { + "trigger": "Déclencheur", + "trigger-point": "Point", + "trigger-axis": "Axe", + "label": "Étiquette", + "value": "Valeur", + "date": "Date", + "show-date-time-interval": "Afficher l’intervalle date/heure", + "show-date-time-interval-hint": "Afficher l’intervalle date/heure selon l’agrégation des données.", + "background-color": "Couleur d’arrière-plan", + "background-blur": "Flou d’arrière-plan" + }, + "unit": { + "millimeter": "Millimètre", + "centimeter": "Centimètre", + "angstrom": "Angström", + "nanometer": "Nanomètre", + "micrometer": "Micromètre", + "meter": "Mètre", + "kilometer": "Kilomètre", + "inch": "Pouce", + "foot": "Pied", + "yard": "Yard", + "mile": "Mille", + "nautical-mile": "Mille nautique", + "astronomical-unit": "Unité astronomique", + "reciprocal-metre": "Mètre réciproque", + "meter-per-meter": "Mètre par mètre", + "steradian": "Stéradian", + "thou": "Thou", + "barleycorn": "Barleycorn", + "hand": "Main", + "chain": "Chaîne", + "furlong": "Furlong", + "league": "Lieue", + "fathom": "Brasse", + "cable": "Câble", + "link": "Maillon", + "rod": "Perche", + "nanogram": "Nanogramme", + "microgram": "Microgramme", + "milligram": "Milligramme", + "gram": "Gramme", + "kilogram": "Kilogramme", + "tonne": "Tonne", + "ounce": "Once", + "pound": "Livre", + "stone": "Stone", + "hundredweight-count": "Quintal", + "short-tons": "Tonnes courtes", + "dalton": "Dalton", + "grain": "Grain", + "drachm": "Drachme", + "quarter": "Quart", + "slug": "Slug", + "carat": "Carat", + "cubic-millimeter": "Millimètre cube", + "cubic-centimeter": "Centimètre cube", + "cubic-meter": "Mètre cube", + "cubic-kilometer": "Kilomètre cube", + "microliter": "Microlitre", + "milliliter": "Millilitre", + "liter": "Litre", + "hectoliter": "Hectolitre", + "cubic-inch": "Pouce cube", + "cubic-foot": "Pied cube", + "cubic-yard": "Yard cube", + "fluid-ounce": "Once liquide", + "pint": "Pinte", + "quart": "Quart", + "gallon": "Gallon", + "oil-barrels": "Baril de pétrole", + "cubic-meter-per-kilogram": "Mètre cube par kilogramme", + "gill": "Gill", + "hogshead": "Hogshead", + "teaspoon": "Cuillère à café", + "tablespoon": "Cuillère à soupe", + "cup": "Tasse", + "celsius": "Celsius", + "kelvin": "Kelvin", + "rankine": "Rankine", + "fahrenheit": "Fahrenheit", + "percent": "Pourcentage", + "meter-per-second": "Mètre par seconde", + "kilometer-per-hour": "Kilomètre par heure", + "foot-per-second": "Pied par seconde", + "mile-per-hour": "Mille par heure", + "knot": "Nœud", + "millimeters-per-minute": "Millimètres par minute", + "kilometer-per-hour-squared": "Kilomètre par heure carrée", + "foot-per-second-squared": "Pied par seconde carrée", + "pascal": "Pascal", + "kilopascal": "Kilopascal", + "megapascal": "Mégapascal", + "gigapascal": "Gigapascal", + "millibar": "Millibar", + "bar": "Bar", + "kilobar": "Kilobar", + "newton": "Newton", + "newton-meter": "Newton-mètre", + "foot-pounds": "Pieds-livres", + "inch-pounds": "Pouces-livres", + "newton-per-meter": "Newton par mètre", + "atmospheres": "Atmosphères", + "pounds-per-square-inch": "Livres par pouce carré", + "torr": "Torr", + "inches-of-mercury": "Pouces de mercure", + "pascal-per-square-meter": "Pascal par mètre carré", + "pound-per-square-inch": "Livre par pouce carré", + "newton-per-square-meter": "Newton par mètre carré", + "kilogram-force-per-square-meter": "Kilogramme-force par mètre carré", + "pascal-per-square-centimeter": "Pascal par centimètre carré", + "ton-force-per-square-inch": "Tonne-force par pouce carré", + "kilonewton-per-square-meter": "Kilonewton par mètre carré", + "newton-per-square-millimeter": "Newton par millimètre carré", + "microjoule": "Microjoule", + "millijoule": "Millijoule", + "joule": "Joule", + "kilojoule": "Kilojoule", + "megajoule": "Mégajoule", + "gigajoule": "Gigajoule", + "watt-hour": "Watt-heure", + "kilowatt-hour": "Kilowatt-heure", + "electron-volts": "Électronvolt", + "joules-per-coulomb": "Joules par coulomb", + "british-thermal-unit": "British Thermal Unit", + "foot-pound": "Pied-livre", + "calorie": "Calorie", + "small-calorie": "Petite calorie", + "kilocalorie": "Kilocalorie", + "joule-per-kelvin": "Joule par kelvin", + "joule-per-kilogram-kelvin": "Joule par kilogramme-kelvin", + "joule-per-kilogram": "Joule par kilogramme", + "watt-per-meter-kelvin": "Watt par mètre-kelvin", + "joule-per-cubic-meter": "Joule par mètre cube", + "therm": "Thermie", + "electric-dipole-moment": "Moment dipolaire électrique", + "magnetic-dipole-moment": "Moment dipolaire magnétique", + "debye": "Debye", + "coulomb-per-square-meter-per-volt": "Coulomb par mètre carré par volt", + "milliwatt": "Milliwatt", + "microwatt": "Microwatt", + "watt": "Watt", + "kilowatt": "Kilowatt", + "megawatt": "Mégawatt", + "gigawatt": "Gigawatt", + "metric-horsepower": "Cheval-vapeur métrique", + "milliwatt-per-square-centimeter": "Milliwatt par centimètre carré", + "watt-per-square-centimeter": "Watt par centimètre carré", + "kilowatt-per-square-centimeter": "Kilowatt par centimètre carré", + "milliwatt-per-square-meter": "Milliwatt par mètre carré", + "watt-per-square-meter": "Watt par mètre carré", + "kilowatt-per-square-meter": "Kilowatt par mètre carré", + "watt-per-square-inch": "Watt par pouce carré", + "kilowatt-per-square-inch": "Kilowatt par pouce carré", + "horsepower": "Cheval-vapeur", + "btu-per-hour": "BTU/heure", + "coulomb": "Coulomb", + "millicoulomb": "Millicoulomb", + "microcoulomb": "Microcoulomb", + "picocoulomb": "Picocoulomb", + "coulomb-per-meter": "Coulomb par mètre", + "coulomb-per-cubic-meter": "Coulomb par mètre cube", + "coulomb-per-square-meter": "Coulomb par mètre carré", + "square-millimeter": "Millimètre carré", + "square-centimeter": "Centimètre carré", + "square-meter": "Mètre carré", + "hectare": "Hectare", + "square-kilometer": "Kilomètre carré", + "square-inch": "Pouce carré", + "square-foot": "Pied carré", + "square-yard": "Yard carré", + "acre": "Acre", + "square-mile": "Mille carré", + "are": "Are", + "barn": "Barn", + "circular-inch": "Pouce circulaire", + "milliampere-hour": "Milliampère-heure", + "ampere-hours": "Ampères-heures", + "kiloampere-hours": "Kiloampères-heures", + "nanoampere": "Nanoampère", + "picoampere": "Picoampère", + "microampere": "Microampère", + "milliampere": "Milliampère", + "ampere": "Ampère", + "microampere-per-square-centimeter": "Microampère par centimètre carré", + "ampere-per-square-meter": "Ampère par mètre carré", + "ampere-per-meter": "Ampère par mètre", + "oersted": "Oersted", + "bohr-magneton": "Magnéton de Bohr", + "ampere-meter-squared": "Ampère-mètre carré", + "nanovolt": "Nanovolt", + "picovolt": "Picovolt", + "volt": "Volt", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "Voltmètre", + "kilovolt-meter": "Kilovolt-mètre", + "megavolt-meter": "Mégavolt-mètre", + "microvolt-meter": "Microvolt-mètre", + "millivolt-meter": "Millivolt-mètre", + "nanovolt-meter": "Nanovolt-mètre", + "ohm": "Ohm", + "microohm": "Microohm", + "milliohm": "Milliohm", + "kilohm": "Kilohm", + "megohm": "Mégohm", + "gigohm": "Gigohm", + "hertz": "Hertz", + "kilohertz": "Kilohertz", + "megahertz": "Mégahertz", + "gigahertz": "Gigahertz", + "rpm": "Tours par minute", + "candela-per-square-meter": "Candela par mètre carré", + "candela": "Candela", + "lumen": "Lumen", + "lux": "Lux", + "foot-candle": "Pied-bougie", + "lumen-per-square-meter": "Lumen par mètre carré", + "lux-second": "Lux-seconde", + "lumen-second": "Lumen-seconde", + "lumens-per-watt": "Lumens par watt", + "mole": "Mole", + "nanomole": "Nanomole", + "micromole": "Micromole", + "millimole": "Millimole", + "kilomole": "Kilomole", + "mole-per-cubic-meter": "Mole par mètre cube", + "rssi": "RSSI", + "ppm": "Parties par million", + "ppb": "Parties par milliard", + "micrograms-per-cubic-meter": "Microgrammes par mètre cube", + "aqi": "Indice de qualité de l’air (AQI)", + "gram-per-cubic-meter": "Grammes par mètre cube", + "gram-per-kilogram": "Humidité spécifique", + "millimeters-per-second": "Millimètres par seconde", + "neper": "Néper", + "bel": "Bel", + "decibel": "Décibel", + "meters-per-second-squared": "Mètres par seconde carrée", + "becquerel": "Becquerel", + "curie": "Curie", + "gray": "Gray", + "sievert": "Sievert", + "roentgen": "Roentgen", + "cps": "Comptes par seconde", + "rad": "Rad", + "rem": "Rem", + "dps": "Désintégrations par seconde", + "rutherford": "Rutherford", + "coulombs-per-kilogram": "Coulombs par kilogramme", + "becquerels-per-cubic-meter": "Becquerels par mètre cube", + "curies-per-liter": "Curies par litre", + "becquerels-per-second": "Becquerels par seconde", + "curies-per-second": "Curies par seconde", + "gy-per-second": "Gray par seconde", + "watt-per-steradian": "Watt par stéradian", + "watt-per-square-metre-steradian": "Watt par mètre carré-stéradian", + "ph-level": "Niveau de pH", + "turbidity": "Turbidité", + "mg-per-liter": "Milligrammes par litre", + "microsiemens-per-centimeter": "Microsiemens par centimètre", + "millisiemens-per-meter": "Millisiemens par mètre", + "siemens-per-meter": "Siemens par mètre", + "kilogram-per-cubic-meter": "Kilogramme par mètre cube", + "gram-per-cubic-centimeter": "Gramme par centimètre cube", + "kilogram-per-square-meter": "Kilogramme par mètre carré", + "milligram-per-milliliter": "Milligramme par millilitre", + "milligram-per-cubic-meter": "Milligramme par mètre cube", + "pound-per-cubic-foot": "Livre par pied cube", + "ounces-per-cubic-inch": "Onces par pouce cube", + "tons-per-cubic-yard": "Tonnes par yard cube", + "particle-density": "Densité des particules", + "kilometers-per-liter": "Kilomètres par litre", + "miles-per-gallon": "Miles par gallon", + "liters-per-100-km": "Litres par 100 km", + "gallons-per-mile": "Gallons par mile", + "liters-per-hour": "Litres par heure", + "gallons-per-hour": "Gallons par heure", + "beats-per-minute": "Battements par minute", + "millimeters-of-mercury": "Millimètres de mercure", + "milligrams-per-deciliter": "Milligrammes par décilitre", + "g-force": "Force g", + "kilonewton": "Kilonewton", + "kilogram-force": "Kilogramme-force", + "pound-force": "Livre-force", + "kilopound-force": "Kilolivre-force", + "dyne": "Dyne", + "poundal": "Poundal", + "kip": "Kip", + "gal": "Gal", + "gravity": "Gravité", + "hectopascal": "Hectopascal", + "atmosphere": "Atmosphère", + "millibars": "Millibars", + "inch-of-mercury": "Pouce de mercure", + "richter-scale": "Échelle de Richter", + "second": "Seconde", + "minute": "Minute", + "hour": "Heure", + "day": "Jour", + "week": "Semaine", + "month": "Mois", + "year": "Année", + "cubic-foot-per-minute": "Pied cube par minute", + "cubic-meters-per-hour": "Mètres cubes par heure", + "cubic-meters-per-second": "Mètres cubes par seconde", + "liter-per-second": "Litre par seconde", + "liter-per-minute": "Litre par minute", + "gallons-per-minute": "Gallons par minute", + "cubic-foot-per-second": "Pied cube par seconde", + "milliliters-per-minute": "Millilitres par minute", + "bit": "Bit", + "byte": "Octet", + "kilobyte": "Kilooctet", + "megabyte": "Mégaoctet", + "gigabyte": "Gigaoctet", + "terabyte": "Téraoctet", + "petabyte": "Pétaoctet", + "exabyte": "Exaoctet", + "zettabyte": "Zettaoctet", + "yottabyte": "Yottaoctet", + "bit-per-second": "Bit par seconde", + "kilobit-per-second": "Kilobit par seconde", + "megabit-per-second": "Mégabit par seconde", + "gigabit-per-second": "Gigabit par seconde", + "terabit-per-second": "Térabit par seconde", + "byte-per-second": "Octet par seconde", + "kilobyte-per-second": "Kilooctets par seconde", + "megabyte-per-second": "Mégaoctets par seconde", + "gigabyte-per-second": "Gigaoctets par seconde", + "degree": "Degré", + "radian": "Radian", + "gradian": "Gradian", + "revolution": "Révolution", + "siemens": "Siemens", + "millisiemens": "Millisiemens", + "microsiemens": "Microsiemens", + "kilosiemens": "Kilosiemens", + "megasiemens": "Mégasiemens", + "gigasiemens": "Gigasiemens", + "farad": "Farad", + "millifarad": "Millifarad", + "microfarad": "Microfarad", + "nanofarad": "Nanofarad", + "picofarad": "Picofarad", + "kilofarad": "Kilofarad", + "megafarad": "Mégafarad", + "gigafarad": "Gigafarad", + "terfarad": "Térafarad", + "farad-per-meter": "Farad par mètre", + "tesla": "Tesla", + "gauss": "Gauss", + "kilogauss": "Kilogauss", + "millitesla": "Millitesla", + "microtesla": "Microtesla", + "nanotesla": "Nanotesla", + "kilotesla": "Kilotesla", + "megatesla": "Mégatesla", + "millitesla-square-meters": "Millitesla mètre carré", + "gamma": "Gamma", + "lambda": "Lambda", + "square-meter-per-second": "Mètre carré par seconde", + "square-centimeter-per-second": "Centimètre carré par seconde", + "stoke": "Stoke", + "centistokes": "Centistokes", + "square-foot-per-second": "Pied carré par seconde", + "square-inch-per-second": "Pouce carré par seconde", + "pascal-second": "Pascal-seconde", + "centipoise": "Centipoise", + "poise": "Poise", + "reynolds": "Reynolds", + "pound-per-foot-hour": "Livre par pied-heure", + "newton-second-per-square-meter": "Newton-seconde par mètre carré", + "dyne-second-per-square-centimeter": "Dyne-seconde par centimètre carré", + "kilogram-per-meter-second": "Kilogramme par mètre-seconde", + "tesla-square-meters": "Tesla mètre carré", + "maxwell": "Maxwell", + "tesla-per-meter": "Tesla par mètre", + "gauss-per-centimeter": "Gauss par centimètre", + "weber": "Weber", + "microweber": "Microweber", + "milliweber": "Milliweber", + "gauss-square-centimeter": "Gauss centimètre carré", + "kilogauss-square-centimeter": "Kilogauss centimètre carré", + "henry": "Henry", + "millihenry": "Millihenry", + "microhenry": "Microhenry", + "nanohenry": "Nanohenry", + "henry-per-meter": "Henry par mètre", + "tesla-meter-per-ampere": "Tesla mètre par ampère", + "gauss-per-oersted": "Gauss par Oersted", + "kilogram-per-mole": "Kilogramme par mole", + "gram-per-mole": "Gramme par mole", + "milligram-per-mole": "Milligramme par mole", + "joule-per-mole": "Joule par mole", + "joule-per-mole-kelvin": "Joule par mole-kelvin", + "millivolts-per-meter": "Millivolts par mètre", + "volts-per-meter": "Volts par mètre", + "kilovolts-per-meter": "Kilovolts par mètre", + "radian-per-second": "Radian par seconde", + "radian-per-second-squared": "Radian par seconde carrée", + "revolutions-per-minute-per-second": "Accélération angulaire", + "deg-per-second": "Degrés/seconde", + "degrees-brix": "Degrés Brix", + "katal": "Katal", + "katal-per-cubic-metre": "Katal par mètre cube" + }, + "user": { + "user": "Utilisateur", + "users": "Utilisateurs", + "customer-users": "Utilisateurs client", + "tenant-admins": "Administrateurs locataire", + "sys-admin": "Administrateur système", + "tenant-admin": "Administrateur locataire", + "customer": "Client", + "anonymous": "Anonyme", + "add": "Ajouter un utilisateur", + "delete": "Supprimer l'utilisateur", + "add-user-text": "Ajouter un nouvel utilisateur", + "no-users-text": "Aucun utilisateur trouvé", + "user-details": "Détails de l'utilisateur", + "delete-user-title": "Êtes-vous sûr de vouloir supprimer l'utilisateur '{{userEmail}}' ?", + "delete-user-text": "Attention, après confirmation, l'utilisateur et toutes les données associées seront irrécupérables.", + "delete-users-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 utilisateur} other {# utilisateurs} } ?", + "delete-users-action-title": "Supprimer { count, plural, =1 {1 utilisateur} other {# utilisateurs} }", + "delete-users-text": "Attention, après confirmation, tous les utilisateurs sélectionnés seront supprimés et toutes les données associées seront irrécupérables.", + "activation-email-sent-message": "L'email d'activation a été envoyé avec succès !", + "resend-activation": "Renvoyer l'activation", + "email": "Email", + "email-required": "L'email est requis.", + "invalid-email-format": "Format d'email invalide.", + "first-name": "Prénom", + "last-name": "Nom", + "description": "Description", + "default-dashboard": "Tableau de bord par défaut", + "always-fullscreen": "Toujours en plein écran", + "select-user": "Sélectionner un utilisateur", + "no-users-matching": "Aucun utilisateur correspondant à '{{entity}}' trouvé.", + "user-required": "L'utilisateur est requis", + "activation-method": "Méthode d'activation", + "display-activation-link": "Afficher le lien d'activation", + "send-activation-mail": "Envoyer l'email d'activation", + "activation-link": "Lien d'activation utilisateur", + "activation-link-text": "Pour activer l'utilisateur, utilisez le lien d'activation suivant (expire dans {{activationLinkTtl}}) :", + "copy-activation-link": "Copier le lien d'activation", + "activation-link-copied-message": "Le lien d'activation de l'utilisateur a été copié dans le presse-papiers", + "details": "Détails", + "login-as-tenant-admin": "Se connecter en tant qu'administrateur locataire", + "login-as-customer-user": "Se connecter en tant qu'utilisateur client", + "search": "Rechercher des utilisateurs", + "selected-users": "{ count, plural, =1 {1 utilisateur} other {# utilisateurs} } sélectionné(s)", + "disable-account": "Désactiver le compte utilisateur", + "enable-account": "Activer le compte utilisateur", + "enable-account-message": "Le compte utilisateur a été activé avec succès !", + "disable-account-message": "Le compte utilisateur a été désactivé avec succès !", + "copyId": "Copier l'identifiant de l'utilisateur", + "idCopiedMessage": "L'identifiant de l'utilisateur a été copié dans le presse-papiers", + "user-list": "Liste des utilisateurs", + "user-list-required": "La liste des utilisateurs est requise" + }, + "value": { + "type": "Type de valeur", + "string": "Chaîne", + "string-value": "Valeur chaîne", + "string-value-required": "La valeur chaîne est requise", + "integer": "Entier", + "integer-value": "Valeur entière", + "integer-value-required": "La valeur entière est requise", + "invalid-integer-value": "Valeur entière invalide", + "double": "Double", + "double-value": "Valeur double", + "double-value-required": "La valeur double est requise", + "boolean": "Booléen", + "boolean-value": "Valeur booléenne", + "false": "Faux", + "true": "Vrai", + "long": "Long", + "json": "JSON", + "json-value": "Valeur JSON", + "json-value-invalid": "Le format de la valeur JSON est invalide", + "json-value-required": "La valeur JSON est requise." + }, + "version-control": { + "version-control": "Contrôle de version", + "management": "Gestion du contrôle de version", + "search": "Rechercher des versions", + "branch": "Branche", + "default": "Par défaut", + "select-branch": "Sélectionner une branche", + "branch-required": "La branche est requise", + "create-entity-version": "Créer une version d'entité", + "version-name": "Nom de la version", + "version-name-required": "Le nom de la version est requis", + "author": "Auteur", + "export-relations": "Exporter les relations", + "export-attributes": "Exporter les attributs", + "export-credentials": "Exporter les identifiants", + "export-calculated-fields": "Exporter les champs calculés", + "entity-versions": "Versions des entités", + "versions": "Versions", + "created-time": "Date de création", + "version-id": "ID de version", + "no-entity-versions-text": "Aucune version d'entité trouvée", + "no-versions-text": "Aucune version trouvée", + "copy-full-version-id": "Copier l'ID complet de la version", + "create-version": "Créer une version", + "creating-version": "Création de la version... Veuillez patienter", + "nothing-to-commit": "Aucune modification à valider", + "restore-version": "Restaurer la version", + "restore-entity-from-version": "Restaurer l'entité depuis la version '{{versionName}}'", + "restoring-entity-version": "Restauration de la version de l'entité... Veuillez patienter", + "load-relations": "Charger les relations", + "load-attributes": "Charger les attributs", + "load-credentials": "Charger les identifiants", + "load-calculated-fields": "Charger les champs calculés", + "compare-with-current": "Comparer avec l'actuel", + "diff-entity-with-version": "Comparer avec la version d'entité '{{versionName}}'", + "previous-difference": "Différence précédente", + "next-difference": "Différence suivante", + "current": "Actuel", + "differences": "{ count, plural, =1 {1 différence} other {# différences} }", + "create-entities-version": "Créer une version d'entités", + "default-sync-strategy": "Stratégie de synchronisation par défaut", + "sync-strategy-merge": "Fusionner", + "sync-strategy-overwrite": "Écraser", + "entities-to-export": "Entités à exporter", + "entities-to-restore": "Entités à restaurer", + "sync-strategy": "Stratégie de synchronisation", + "all-entities": "Toutes les entités", + "no-entities-to-export-prompt": "Veuillez spécifier les entités à exporter", + "no-entities-to-restore-prompt": "Veuillez spécifier les entités à restaurer", + "add-entity-type": "Ajouter un type d'entité", + "remove-all": "Tout supprimer", + "version-create-result": "{ added, plural, =0 {Aucune entité} =1 {1 entité} other {# entités} } ajoutée(s).
{ modified, plural, =0 {Aucune entité} =1 {1 entité} other {# entités} } modifiée(s).
{ removed, plural, =0 {Aucune entité} =1 {1 entité} other {# entités} } supprimée(s).", + "remove-other-entities": "Supprimer les autres entités", + "find-existing-entity-by-name": "Trouver une entité existante par nom", + "restore-entities-from-version": "Restaurer les entités depuis la version '{{versionName}}'", + "restoring-entities-from-version": "Restauration des entités... Veuillez patienter", + "no-entities-restored": "Aucune entité restaurée", + "created": "{{created}} créée(s)", + "updated": "{{updated}} mise(s) à jour", + "deleted": "{{deleted}} supprimée(s)", + "remove-other-entities-confirm-text": "Attention ! Cela supprimera de manière permanente toutes les entités actuelles
non présentes dans la version que vous souhaitez restaurer.

Veuillez taper \"remove other entities\" pour confirmer.", + "auto-commit-to-branch": "auto-validation sur la branche {{ branch }}", + "default-create-entity-version-name": "Mise à jour de {{entityName}}", + "sync-strategy-merge-hint": "Crée ou met à jour les entités sélectionnées dans le dépôt. Toutes les autres entités du dépôt ne sont pas modifiées.", + "sync-strategy-overwrite-hint": "Crée ou met à jour les entités sélectionnées dans le dépôt. Toutes les autres entités du dépôt sont supprimées.", + "device-credentials-conflict": "Échec du chargement de l'appareil avec l'id externe {{entityId}}
car les mêmes identifiants sont déjà présents dans la base de données pour un autre appareil.
Veuillez envisager de désactiver l'option charger les identifiants dans le formulaire de restauration.", + "missing-referenced-entity": "Échec du chargement du {{sourceEntityTypeName}} avec l'id externe {{sourceEntityId}}
car il fait référence à un {{targetEntityTypeName}} manquant avec l'id {{targetEntityId}}.", + "runtime-failed": "Échec : {{message}}", + "auto-commit-settings-read-only-hint": "La fonctionnalité d'auto-validation ne fonctionne pas avec l'option lecture seule activée dans les paramètres du dépôt.", + "rollback-on-error": "Annuler en cas d’erreur", + "rollback-on-error-hint": "Si vous avez un grand nombre d’entités à restaurer, envisagez de désactiver cette option pour améliorer les performances.\n Note : en cas d’erreur pendant le chargement de la version, les entités déjà persistées (avec relations, attributs, etc.) resteront telles quelles." + }, + "widget": { + "widget-library": "Bibliothèque de widgets", + "widget-bundle": "Pack de widgets", + "all-bundles": "Tous les packs", + "select-widgets-bundle": "Sélectionner un pack de widgets", + "widgets": "Widgets", + "all-widgets": "Tous les widgets", + "widget": "Widget", + "select-widget": "Sélectionner un widget", + "no-widgets-matching": "Aucun widget correspondant à '{{entity}}' trouvé.", + "no-widgets": "Aucun widget pour l’instant", + "no-widgets-text": "Aucun widget trouvé", + "management": "Gestion des widgets", + "editor": "Éditeur de widgets", + "confirm-to-exit-editor-html": "Vous avez des paramètres de widget non enregistrés.
Êtes-vous sûr de vouloir quitter cette page ?", + "widget-type-not-found": "Problème de chargement de la configuration du widget.
Le type de widget associé a probablement été supprimé.", + "widget-type-load-error": "Le widget n’a pas été chargé en raison des erreurs suivantes :", + "remove": "Supprimer le widget", + "delete": "Supprimer le widget", + "edit": "Modifier le widget", + "remove-widget-title": "Êtes-vous sûr de vouloir supprimer le widget '{{widgetTitle}}' ?", + "remove-widget-text": "Après confirmation, le widget et toutes les données associées seront irrécupérables.", + "replace-reference-with-widget-copy": "Remplacer la référence par une copie du widget", + "timeseries": "Séries temporelles", + "search-data": "Rechercher des données", + "no-data-found": "Aucune donnée trouvée", + "latest": "Valeurs récentes", + "rpc": "Widget de contrôle", + "alarm": "Widget d'alarme", + "static": "Widget statique", + "timeseries-short": "séries", + "latest-short": "récent", + "rpc-short": "contrôle", + "alarm-short": "alarme", + "static-short": "statique", + "select-widget-type": "Sélectionner le type de widget", + "missing-widget-title-error": "Le titre du widget doit être spécifié !", + "widget-saved": "Widget enregistré", + "unable-to-save-widget-error": "Impossible d’enregistrer le widget ! Le widget contient des erreurs !", + "save": "Enregistrer le widget", + "saveAs": "Enregistrer le widget sous", + "move": "Déplacer le widget", + "save-widget-as": "Enregistrer le widget sous", + "save-widget-as-text": "Veuillez saisir un nouveau titre pour le widget", + "toggle-fullscreen": "Basculer en plein écran", + "run": "Exécuter le widget", + "widget-title": "Titre du widget", + "title": "Titre", + "title-required": "Le titre du widget est requis.", + "title-max-length": "Le titre doit contenir moins de 256 caractères", + "system": "Système", + "type": "Type de widget", + "resources": "Ressources", + "resource-url": "URL JavaScript/CSS", + "resource-is-extension": "Est une extension", + "remove-resource": "Supprimer la ressource", + "add-resource": "Ajouter une ressource", + "html": "HTML", + "tidy": "Nettoyer", + "css": "CSS", + "settings-form": "Formulaire de paramètres", + "data-key-settings-form": "Formulaire de paramètres de clé de données", + "latest-data-key-settings-form": "Formulaire de paramètres des dernières données", + "widget-settings": "Paramètres du widget", + "description": "Description", + "tags": "Étiquettes", + "image-preview": "Aperçu de l’image", + "settings-form-selector": "Sélecteur de formulaire de paramètres", + "data-key-settings-form-selector": "Sélecteur de formulaire de clé de données", + "latest-data-key-settings-form-selector": "Sélecteur de formulaire des dernières données", + "all": "Tous", + "actual": "Actuel", + "scada": "Symbole SCADA", + "deprecated": "Obsolète", + "has-basic-mode": "Mode basique disponible", + "basic-mode-form-selector": "Sélecteur de formulaire du mode basique", + "basic-mode": "Basique", + "advanced-mode": "Avancé", + "javascript": "Javascript", + "js": "JS", + "delete-widget-title": "Êtes-vous sûr de vouloir supprimer le widget '{{widgetName}}' ?", + "delete-widget-text": "Après confirmation, le widget et toutes les données associées seront irrécupérables.", + "delete-widgets-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 widget} other {# widgets} } ?", + "delete-widgets-text": "Attention, après confirmation tous les widgets sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", + "delete-widget": "Supprimer le widget", + "widget-template-load-failed-error": "Échec du chargement du modèle de widget !", + "details": "Détails", + "widget-details": "Détails du widget", + "add": "Ajouter un widget", + "add-existing-widget": "Ajouter un widget existant", + "add-new-widget": "Ajouter un nouveau widget", + "search-widgets": "Rechercher des widgets", + "selected-widgets": "{ count, plural, =1 {1 widget} other {# widgets} } sélectionné(s)", + "undo": "Annuler les modifications du widget", + "export": "Exporter le widget", + "export-prompt": "Intégrer les images et ressources du widget", + "export-widgets": "Exporter les widgets", + "export-widgets-prompt": "Intégrer les images et ressources des widgets", + "import": "Importer un widget", + "no-data": "Aucune donnée à afficher sur le widget", + "data-overflow": "Le widget affiche {{count}} sur {{total}} entités", + "alarm-data-overflow": "Le widget affiche les alarmes pour {{allowedEntities}} entités (maximum autorisé) sur un total de {{totalEntities}} entités", + "search": "Rechercher un widget", + "filter": "Type de filtre de widget", + "loading-widgets": "Chargement des widgets...", + "widget-template-error": "Modèle HTML du widget invalide.", + "reference": "Référence" + }, + "widget-action": { + "header-button": "Bouton d'en-tête du widget", + "do-nothing": "Ne rien faire", + "open-dashboard-state": "Naviguer vers un nouvel état du tableau de bord", + "update-dashboard-state": "Mettre à jour l'état actuel du tableau de bord", + "open-dashboard": "Naviguer vers un autre tableau de bord", + "custom": "Action personnalisée", + "custom-pretty": "Action personnalisée (avec modèle HTML)", + "custom-pretty-error-title": "Erreur de dialogue personnalisé", + "custom-pretty-template-error": "Modèle de dialogue personnalisé invalide.", + "custom-pretty-controller-error": "Une erreur s'est produite lors de l'évaluation de la fonction de dialogue personnalisé.", + "mobile-action": "Action mobile", + "target-dashboard-state": "État cible du tableau de bord", + "target-dashboard-state-required": "L'état cible du tableau de bord est requis", + "set-entity-from-widget": "Définir l'entité à partir du widget", + "target-dashboard": "Tableau de bord cible", + "select-target-dashboard": "Sélectionner un tableau de bord cible", + "target-dashboard-required": "Le tableau de bord cible est requis.", + "open-right-layout": "Ouvrir la disposition droite du tableau de bord (vue mobile)", + "state-display-type": "Option d'affichage de l'état du tableau de bord", + "open-normal": "Normal", + "open-in-separate-dialog": "Ouvrir dans une boîte de dialogue séparée", + "open-in-popover": "Ouvrir dans une infobulle", + "dialog-title": "Titre de la boîte de dialogue", + "dialog-hide-dashboard-toolbar": "Masquer la barre d'outils du tableau de bord dans la boîte de dialogue", + "dialog-width": "Largeur de la boîte de dialogue en pourcentage de la largeur de l'écran", + "dialog-height": "Hauteur de la boîte de dialogue en pourcentage de la hauteur de l'écran", + "dialog-size-range-error": "La taille de la boîte de dialogue doit être comprise entre 1 et 100 %.", + "popover-preferred-placement": "Placement préféré de l'infobulle", + "popover-placement-top": "Haut", + "popover-placement-topLeft": "Haut gauche", + "popover-placement-topRight": "Haut droit", + "popover-placement-right": "Droite", + "popover-placement-rightTop": "Haut droite", + "popover-placement-rightBottom": "Bas droite", + "popover-placement-bottom": "Bas", + "popover-placement-bottomLeft": "Bas gauche", + "popover-placement-bottomRight": "Bas droite", + "popover-placement-left": "Gauche", + "popover-placement-leftTop": "Haut gauche", + "popover-placement-leftBottom": "Bas gauche", + "popover-hide-on-click-outside": "Masquer l'infobulle en cliquant à l'extérieur", + "popover-hide-dashboard-toolbar": "Masquer la barre d'outils du tableau de bord dans l'infobulle", + "popover-width": "Largeur de l'infobulle", + "popover-height": "Hauteur de l'infobulle", + "popover-style": "Style de l'infobulle", + "open-new-browser-tab": "Ouvrir dans un nouvel onglet", + "open-URL": "Ouvrir une URL", + "URL": "URL", + "url-required": "L'URL est requise.", + "mobile": { + "device-provision": "Provisionnement de l'appareil", + "action-type": "Type d'action mobile", + "select-action-type": "Sélectionner le type d'action mobile", + "action-type-required": "Le type d'action mobile est requis", + "take-picture-from-gallery": "Prendre une photo depuis la galerie", + "take-photo": "Prendre une photo", + "map-direction": "Ouvrir les directions sur la carte", + "map-location": "Ouvrir l'emplacement sur la carte", + "scan-qr-code": "Scanner le code QR", + "make-phone-call": "Passer un appel téléphonique", + "get-location": "Obtenir la localisation du téléphone", + "take-screenshot": "Faire une capture d'écran" + }, + "custom-action-function": "Fonction d'action personnalisée", + "custom-pretty-function": "Fonction d'action personnalisée (avec modèle HTML)", + "map-item-type": "Type d'élément cartographique", + "map-item": { + "marker": "Marqueur", + "polygon": "Polygone", + "rectangle": "Rectangle", + "circle": "Cercle" + }, + "place-map-item": "Placer un élément cartographique", + "map-item-tooltip": { + "customize-map-item-tooltips": "Personnaliser les infobulles des éléments cartographiques", + "place-marker": "Placer un marqueur", + "start-draw-rectangle": "Commencer à dessiner un rectangle", + "finish-draw-rectangle": "Terminer le dessin du rectangle", + "start-draw-polygon": "Commencer à dessiner un polygone", + "continue-draw-polygon": "Continuer le dessin du polygone", + "finish-draw-polygon": "Terminer le dessin du polygone", + "start-draw-circle": "Commencer à dessiner un cercle", + "finish-draw-circle": "Terminer le dessin du cercle" + } + }, + "widgets-bundle": { + "current": "Bundle actuel", + "widgets-bundles": "Bundles de widgets", + "widgets-bundle-widgets": "Widgets du bundle", + "add": "Ajouter un bundle de widgets", + "delete": "Supprimer le bundle de widgets", + "title": "Titre", + "title-required": "Le titre est requis.", + "title-max-length": "Le titre doit contenir moins de 256 caractères", + "description": "Description", + "image-preview": "Aperçu de l'image", + "scada": "Bundle de widgets SCADA", + "order": "Ordre", + "add-widgets-bundle-text": "Ajouter un nouveau bundle de widgets", + "no-widgets-bundles-text": "Aucun bundle de widgets trouvé", + "empty": "Le bundle de widgets est vide", + "details": "Détails", + "widgets-bundle-details": "Détails du bundle de widgets", + "delete-widgets-bundle-title": "Êtes-vous sûr de vouloir supprimer le bundle de widgets '{{widgetsBundleTitle}}' ?", + "delete-widgets-bundle-text": "Attention, après confirmation, le bundle et toutes ses données associées seront définitivement supprimés.", + "delete-widgets-bundles-title": "Êtes-vous sûr de vouloir supprimer { count, plural, =1 {1 bundle de widgets} other {# bundles de widgets} } ?", + "delete-widgets-bundles-action-title": "Supprimer { count, plural, =1 {1 bundle de widgets} other {# bundles de widgets} }", + "delete-widgets-bundles-text": "Attention, après confirmation, tous les bundles sélectionnés seront supprimés ainsi que toutes les données associées.", + "no-widgets-bundles-matching": "Aucun bundle de widgets correspondant à '{{widgetsBundle}}' trouvé.", + "widgets-bundle-required": "Le bundle de widgets est requis.", + "system": "Système", + "import": "Importer un bundle de widgets", + "export": "Exporter un bundle de widgets", + "export-widgets-bundle-widgets-prompt": "Inclure les widgets du bundle dans les données exportées (sinon seuls les FQN de widgets référencés seront exportés)", + "export-failed-error": "Impossible d’exporter le bundle de widgets : {{error}}", + "create-new-widgets-bundle": "Créer un nouveau bundle de widgets", + "widgets-bundle-file": "Fichier du bundle de widgets", + "invalid-widgets-bundle-file-error": "Impossible d'importer le bundle de widgets : structure de données invalide.", + "search": "Rechercher des bundles de widgets", + "selected-widgets-bundles": "{ count, plural, =1 {1 bundle de widgets} other {# bundles de widgets} } sélectionné(s)", + "open-widgets-bundle": "Ouvrir le bundle de widgets", + "loading-widgets-bundles": "Chargement des bundles de widgets...", + "create-new": "Créer un nouveau bundle de widgets" + }, + "widget-config": { + "data": "Données", + "settings": "Paramètres", + "advanced": "Avancé", + "appearance": "Apparence", + "widget-card": "Carte du widget", + "mobile": "Mobile", + "title": "Titre", + "title-tooltip": "Info-bulle du titre", + "general-settings": "Paramètres généraux", + "display-title": "Afficher le titre du widget", + "card-title": "Titre de la carte", + "drop-shadow": "Ombre portée", + "enable-fullscreen": "Activer le mode plein écran", + "background-color": "Couleur de fond", + "text-color": "Couleur du texte", + "border-radius": "Rayon de la bordure", + "padding": "Espacement interne (padding)", + "margin": "Marge", + "widget-style": "Style du widget", + "widget-css": "CSS du widget", + "title-style": "Style du titre", + "mobile-mode-settings": "Mode mobile", + "order": "Ordre", + "height": "Hauteur", + "mobile-hide": "Masquer le widget en mode mobile", + "desktop-hide": "Masquer le widget en mode bureau", + "units": "Symbole spécial à afficher à côté de la valeur", + "units-by-default": "Unités par défaut", + "decimals": "Nombre de chiffres après la virgule", + "decimals-by-default": "Décimales par défaut", + "default-data-key-parameter-hint": "Ce paramètre s’applique à toutes les valeurs du widget sauf si redéfini par la configuration de la clé de données", + "units-short": "Unités", + "decimals-short": "Décimales", + "decimals-suffix": "décimales", + "digits-suffix": "chiffres", + "timewindow": "Fenêtre temporelle", + "use-dashboard-timewindow": "Utiliser la fenêtre temporelle du tableau de bord", + "use-widget-timewindow": "Utiliser la fenêtre temporelle du widget", + "display-timewindow": "Afficher la fenêtre temporelle", + "legend": "Légende", + "display-legend": "Afficher la légende", + "datasources": "Sources de données", + "datasource": "Source de données", + "maximum-datasources": "Maximum { count, plural, =1 {1 source de données autorisée.} other {# sources de données autorisées} }", + "timeseries-key-error": "Au moins une clé de série temporelle doit être spécifiée", + "datasource-type": "Type", + "datasource-parameters": "Paramètres", + "remove-datasource": "Supprimer la source de données", + "add-datasource": "Ajouter une source de données", + "target-device": "Appareil cible", + "alarm-source": "Source d'alarme", + "actions": "Actions", + "action": "Action", + "add-action": "Ajouter une action", + "search-actions": "Rechercher des actions", + "no-actions-text": "Aucune action trouvée", + "action-source": "Source de l'action", + "select-action-source": "Sélectionner la source de l'action", + "action-source-required": "La source de l'action est requise.", + "column-index": "Index de la colonne", + "select-column-index": "Sélectionner l'index de la colonne", + "column-index-required": "L’index de colonne est requis.", + "not-set": "Non défini", + "action-name": "Nom", + "action-name-required": "Le nom de l’action est requis.", + "action-name-not-unique": "Une autre action avec le même nom existe déjà.\nLe nom de l’action doit être unique pour une même source.", + "action-icon": "Icône", + "header-button": { + "button-settings": "Paramètres du bouton", + "button-type": "Type de bouton", + "button-type-basic": "Basique", + "button-type-raised": "Surélevé", + "button-type-stroked": "Avec contour", + "button-type-flat": "Plat", + "button-type-icon": "Icône", + "button-type-mini-fab": "FAB", + "colors": "Couleurs", + "color": "Couleur", + "background": "Arrière-plan", + "border": "Bordure", + "advanced-button-style": "Style de bouton avancé", + "button-style": "Style du bouton" + }, + "show-hide-action-using-function": "Afficher/masquer l’action via une fonction", + "show-action-function": "Fonction d’affichage de l’action", + "action-type": "Type", + "action-type-required": "Le type d’action est requis.", + "edit-action": "Modifier l’action", + "delete-action": "Supprimer l’action", + "delete-action-title": "Supprimer l’action du widget", + "delete-action-text": "Êtes-vous sûr de vouloir supprimer l’action du widget nommée '{{actionName}}' ?", + "title-icon": "Icône du titre", + "display-icon": "Afficher l’icône du titre", + "card-icon": "Icône de la carte", + "icon": "Icône", + "icon-color": "Couleur de l’icône", + "icon-size": "Taille de l’icône", + "advanced-settings": "Paramètres avancés", + "data-settings": "Paramètres de données", + "limits": "Limites", + "no-data-display-message": "Message alternatif pour « Aucune donnée à afficher »", + "data-page-size": "Nombre maximal d'entités par source de données", + "settings-component-not-found": "Composant du formulaire de paramètres introuvable pour le sélecteur '{{selector}}'", + "preview": "Aperçu", + "set": "Définir", + "set-message": "Définir le message", + "advanced-title-style": "Style avancé du titre", + "card-style": "Style de la carte", + "text": "Texte", + "background": "Arrière-plan", + "advanced-widget-style": "Style avancé du widget", + "card-buttons": "Boutons de la carte", + "show-card-buttons": "Afficher les boutons de la carte", + "card-border-radius": "Rayon de bordure de la carte", + "card-padding": "Espacement interne de la carte", + "card-appearance": "Apparence de la carte", + "color": "Couleur", + "tooltip": "Info-bulle", + "units-required": "L’unité est requise.", + "list-layout": "Disposition en liste", + "layout": "Disposition", + "resize-options": "Options de redimensionnement", + "resizable": "Redimensionnable", + "preserve-aspect-ratio": "Préserver les proportions" + }, + "widget-type": { + "import": "Importer le type de widget", + "export": "Exporter le type de widget", + "export-failed-error": "Impossible d’exporter le widget : {{error}}", + "widget-file": "Fichier de widget", + "invalid-widget-file-error": "Impossible d’importer le widget : structure de données invalide." + }, + "markdown": { + "edit": "Éditer", + "preview": "Aperçu", + "copy-code": "Cliquez pour copier", + "copied": "Copié !" + }, + "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "La configuration dépend du widget de code QR de l'application mobile dans les paramètres principaux de la plateforme", + "get-it-on-google-play": "Disponible sur Google Play", + "download-on-the-app-store": "Télécharger sur l'App Store" + }, + "action-button": { + "behavior": "Comportement", + "on-click": "Au clic", + "on-click-hint": "Action déclenchée lors du clic sur le bouton", + "first-button-click": "Premier clic sur le bouton", + "first-button-click-hint": "Action lors de l’appui sur le premier bouton.", + "second-button-click": "Deuxième clic sur le bouton", + "second-button-click-hint": "Action lors de l’appui sur le deuxième bouton.", + "button-click-hint": "Action lors de l’appui sur le widget." + }, + "command-button": { + "behavior": "Comportement", + "on-click": "Au clic", + "on-click-hint": "Action effectuée lors du clic sur le bouton." + }, + "power-button": { + "behavior": "Comportement", + "power-on": "Allumer", + "power-on-hint": "Action effectuée pour allumer le composant.", + "power-off": "Éteindre", + "power-off-hint": "Action effectuée pour éteindre le composant.", + "on-label": "Marche", + "off-label": "Arrêt", + "layout": "Disposition", + "layout-default": "Par défaut", + "layout-simplified": "Simplifié", + "layout-outlined": "Contour", + "layout-default-volume": "Par défaut.Volume", + "layout-simplified-volume": "Simplifié.Volume", + "layout-outlined-volume": "Contour.Volume", + "layout-default-icon": "Par défaut.Icône", + "layout-simplified-icon": "Simplifié.Icône", + "layout-outlined-icon": "Contour.Icône", + "main": "Principal", + "background": "Arrière-plan", + "button-icon-on": "Icône du bouton 'Marche'", + "button-icon-off": "Icône du bouton 'Arrêt'", + "power-on-colors": "Couleurs 'Marche'", + "power-off-colors": "Couleurs 'Arrêt'", + "disabled-colors": "Couleurs désactivées", + "button": "Bouton" + }, + "toggle-button": { + "behavior": "Comportement", + "checked": "Coché", + "unchecked": "Décoché", + "check": "Cocher", + "check-hint": "Action effectuée pour cocher le composant.", + "uncheck": "Décocher", + "uncheck-hint": "Action effectuée pour décocher le composant.", + "auto-scale": "Redimensionnement automatique", + "horizontal-fill": "Remplissage horizontal", + "vertical-fill": "Remplissage vertical", + "button-appearance": "Apparence du bouton" + }, + "segmented-button": { + "layout": "Disposition", + "layout-squared": "Carrée", + "layout-rounded": "Arrondie", + "card-border": "Bordure de carte", + "button-appearance": "Apparence du bouton", + "first": "Premier", + "second": "Deuxième", + "color-styles": "Styles de couleur", + "selected": "Sélectionné", + "unselected": "Non sélectionné" + }, + "button": { + "layout": "Disposition", + "outlined": "Contour", + "filled": "Rempli", + "underlined": "Souligné", + "basic": "Basique", + "auto-scale": "Redimensionnement automatique", + "label": "Libellé", + "icon": "Icône", + "border-radius": "Rayon de bordure", + "color-palette": "Palette de couleurs", + "main": "Principal", + "background": "Arrière-plan", + "border": "Bordure", + "custom-styles": "Styles personnalisés", + "clear-style": "Effacer le style", + "shadow": "Ombre", + "enabled": "Activé", + "disabled": "Désactivé", + "preview": "Aperçu", + "copy-style-from": "Copier le style de" + }, + "value-stepper": { + "behavior": "Comportement", + "simplified": "Simplifié", + "filled": "Rempli", + "outlined": "Contour", + "volume": "Volume", + "initial-state": "État initial", + "initial-state-hint": "Action pour obtenir la valeur initiale.", + "disabled-state": "État désactivé", + "disabled-state-hint": "Configurer la condition de désactivation du composant.", + "right-button-click": "Clic bouton droit", + "right-button-click-hint": "Action lors de l’appui sur le bouton droit.", + "left-button-click": "Clic bouton gauche", + "left-button-click-hint": "Action lors de l’appui sur le bouton gauche.", + "auto-scale": "Redimensionnement automatique", + "value-range": "Plage de valeurs", + "min-range": "Min", + "max-range": "Max", + "value-increment-decrement-step": "Pas d'incrément/décrément", + "value": "Valeur", + "value-box-background": "Arrière-plan de la boîte de valeur", + "border": "Bordure", + "button-appearance": "Apparence du bouton", + "left": "Gauche", + "right": "Droite", + "left-button": "Bouton gauche", + "right-button": "Bouton droit", + "icon": "Icône", + "color-palette": "Palette de couleurs", + "main": "Principal", + "background": "Arrière-plan", + "button-icon-on": "Icône du bouton 'Marche'", + "button-on-colors": "Couleurs 'Marche'", + "disabled-colors": "Couleurs désactivées" + }, + "button-state": { + "activated-state": "État activé", + "activated-state-hint": "Configurer la condition d’activation du bouton.", + "disabled-state": "État désactivé", + "disabled-state-hint": "Configurer la condition de désactivation du bouton.", + "selected-state": "Sélection du bouton", + "selected-state-hint": "Configurer la condition de sélection du bouton.", + "enabled": "Activé", + "hovered": "Survolé", + "pressed": "Pressé", + "activated": "Activé", + "disabled": "Désactivé", + "initial": "Premier bouton", + "first": "Premier", + "second": "Deuxième" + }, + "background": { + "background": "Arrière-plan", + "background-settings": "Paramètres de l'arrière-plan", + "background-type-image": "Image", + "background-type-color": "Couleur", + "image-url": "URL de l'image", + "overlay": "Superposition", + "enable-overlay": "Activer la superposition", + "blur": "Flou", + "preview": "Aperçu" + }, + "bar-chart": { + "bar-appearance": "Apparence de la barre", + "label-on-bar": "Étiquette sur la barre", + "value-on-bar": "Valeur sur la barre", + "bar-chart-style": "Style du diagramme à barres", + "bar-axis": "Axe de la barre" + }, + "polar-area-chart": { + "polar-axis": "Axe polaire", + "start-angle": "Angle de départ", + "polar-area-chart-style": "Style du diagramme en aires polaires" + }, + "battery-level": { + "layout": "Disposition", + "layout-vertical-solid": "Vertical. Plein", + "layout-horizontal-solid": "Horizontal. Plein", + "layout-vertical-divided": "Vertical. Divisé", + "layout-horizontal-divided": "Horizontal. Divisé", + "icon": "Icône", + "value": "Valeur", + "auto-scale": "Redimensionnement automatique", + "battery-level-color": "Couleur du niveau de batterie", + "battery-shape-color": "Couleur de la forme de la batterie", + "battery-level-card-style": "Style de la carte du niveau de batterie", + "sections-count": "Nombre de sections" + }, + "signal-strength": { + "value": "Valeur", + "last-update": "Dernière mise à jour", + "no-signal": "Pas de signal", + "layout": "Disposition", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "Barre cellulaire", + "icon": "Icône", + "date": "Date", + "active-bars-color": "Couleur des barres actives", + "inactive-bars-color": "Couleur des barres inactives", + "signal-strength-card-style": "Style de la carte de signal", + "no-signal-rssi-value": "Valeur RSSI pour \"Pas de signal\"" + }, + "status-widget": { + "behavior": "Comportement", + "layout": "Disposition", + "layout-default": "Défaut", + "layout-center": "Centré", + "layout-icon": "Icône", + "on": "Activé", + "off": "Désactivé", + "label": "Libellé", + "status": "Statut", + "icon": "Icône", + "color-palette": "Palette de couleurs", + "disabled-color-palette": "Palette de couleurs désactivée", + "primary": "Principal", + "primary-color-hint": "Couleur de l’icône et du libellé", + "secondary": "Secondaire", + "secondary-color-hint": "Couleur du statut", + "background": "Arrière-plan" + }, + "chart": { + "common-settings": "Paramètres communs", + "enable-stacking-mode": "Activer le mode empilé", + "selection": "Sélection de la plage temporelle", + "enable-selection-mode": "Activer le mode de sélection", + "line-shadow-size": "Taille de l’ombre de la ligne", + "display-smooth-lines": "Afficher les lignes lisses (courbes)", + "default-bar-width": "Largeur de barre par défaut pour les données non agrégées (en millisecondes)", + "bar-alignment": "Alignement des barres", + "bar-alignment-left": "Gauche", + "bar-alignment-right": "Droite", + "bar-alignment-center": "Centre", + "default-font": "Police par défaut", + "default-font-size": "Taille de police par défaut", + "default-font-color": "Couleur de police par défaut", + "thresholds-line-width": "Épaisseur de ligne par défaut pour tous les seuils", + "tooltip-settings": "Paramètres de l'infobulle", + "tooltip": "Infobulle", + "show-tooltip": "Afficher l’infobulle", + "hover-individual-points": "Survoler les points individuels", + "show-cumulative-values": "Afficher les valeurs cumulées en mode empilé", + "hide-zero-false-values": "Masquer les valeurs nulles/faux de l’infobulle", + "tooltip-value-format-function": "Fonction de formatage des valeurs de l’infobulle", + "grid-settings": "Paramètres de la grille", + "show-vertical-lines": "Afficher les lignes verticales", + "show-horizontal-lines": "Afficher les lignes horizontales", + "grid-outline-border-width": "Épaisseur du contour/de la bordure de la grille (px)", + "primary-color": "Couleur principale", + "background-color": "Couleur d’arrière-plan", + "ticks-color": "Couleur des graduations", + "xaxis-settings": "Paramètres de l’axe X", + "axis-title": "Titre de l’axe", + "xaxis-tick-labels-settings": "Paramètres des étiquettes de graduation de l’axe X", + "show-tick-labels": "Afficher les étiquettes des graduations", + "yaxis-settings": "Paramètres de l’axe Y", + "min-scale-value": "Valeur minimale de l’échelle", + "max-scale-value": "Valeur maximale de l’échelle", + "yaxis-tick-labels-settings": "Paramètres des étiquettes de graduation de l’axe Y", + "tick-step-size": "Pas entre les graduations", + "number-of-decimals": "Nombre de décimales à afficher", + "ticks-formatter-function": "Fonction de formatage des graduations", + "comparison-settings": "Paramètres de comparaison", + "enable-comparison": "Activer la comparaison", + "time-for-comparison": "Période de comparaison", + "time-for-comparison-previous-interval": "Intervalle précédent (par défaut)", + "time-for-comparison-days": "Il y a un jour", + "time-for-comparison-weeks": "Il y a une semaine", + "time-for-comparison-months": "Il y a un mois", + "time-for-comparison-years": "Il y a un an", + "time-for-comparison-custom-interval": "Intervalle personnalisé", + "custom-interval-value": "Valeur d’intervalle personnalisé (ms)", + "comparison-x-axis-settings": "Paramètres de l’axe X pour la comparaison", + "axis-position": "Position de l’axe", + "axis-position-top": "En haut (par défaut)", + "axis-position-bottom": "En bas", + "custom-legend-settings": "Paramètres personnalisés de la légende", + "enable-custom-legend": "Activer la légende personnalisée (permet d'utiliser des attributs/valeurs de séries temporelles dans les libellés)", + "key-name": "Nom de la clé", + "key-name-required": "Le nom de la clé est requis", + "key-type": "Type de clé", + "key-type-attribute": "Attribut", + "key-type-timeseries": "Série temporelle", + "label-keys-list": "Liste des clés à utiliser dans les libellés", + "no-label-keys": "Aucune clé configurée", + "add-label-key": "Ajouter une nouvelle clé", + "line-width": "Épaisseur de la ligne", + "color": "Couleur", + "data-is-hidden-by-default": "Les données sont masquées par défaut", + "disable-data-hiding": "Désactiver le masquage des données", + "remove-from-legend": "Supprimer la clé de la légende", + "exclude-from-stacking": "Exclure de l'empilement (disponible en mode \"Empilé\")", + "line-settings": "Paramètres de la ligne", + "show-line": "Afficher la ligne", + "fill-line": "Remplir la ligne", + "fill-line-opacity": "Opacité du remplissage", + "points-settings": "Paramètres des points", + "show-points": "Afficher les points", + "points-line-width": "Épaisseur de la ligne des points", + "points-radius": "Rayon des points", + "point-shape": "Forme du point", + "point-shape-circle": "Cercle", + "point-shape-cross": "Croix", + "point-shape-diamond": "Losange", + "point-shape-square": "Carré", + "point-shape-triangle": "Triangle", + "point-shape-custom": "Fonction personnalisée", + "point-shape-draw-function": "Fonction de dessin de la forme du point", + "show-separate-axis": "Afficher un axe séparé", + "axis-position-left": "Gauche", + "axis-position-right": "Droite", + "thresholds": "Seuils", + "no-thresholds": "Aucun seuil configuré", + "add-threshold": "Ajouter un seuil", + "show-values-for-comparison": "Afficher les valeurs historiques pour la comparaison", + "comparison-values-label": "Étiquette des valeurs historiques", + "comparison-line-color": "Couleur de la ligne de comparaison", + "threshold-settings": "Paramètres des seuils", + "use-as-threshold": "Utiliser la valeur de la clé comme seuil", + "threshold-line-width": "Épaisseur de la ligne du seuil", + "threshold-color": "Couleur du seuil", + "common-pie-settings": "Paramètres communs du diagramme circulaire", + "radius": "Rayon", + "inner-radius": "Rayon intérieur", + "tilt": "Inclinaison", + "common-pie-settings-range-error": "La valeur doit être comprise entre 0 et 1", + "stroke-settings": "Paramètres du contour", + "width-pixels": "Largeur (pixels)", + "show-labels": "Afficher les étiquettes", + "animation-settings": "Paramètres d'animation", + "animated-pie": "Activer l’animation du diagramme (expérimental)", + "border-settings": "Paramètres de bordure", + "border-width": "Épaisseur de la bordure", + "border-color": "Couleur de la bordure", + "legend-settings": "Paramètres de la légende", + "display-legend": "Afficher la légende", + "labels-font-color": "Couleur de la police des étiquettes", + "series": "Séries", + "add-series": "Ajouter une série", + "series-settings": "Paramètres de la série", + "remove-series": "Supprimer la série", + "no-series": "Aucune série configurée", + "no-series-error": "Au moins une série doit être spécifiée", + "chart-appearance": "Apparence du graphique", + "vertical-grid-lines": "Lignes de la grille verticale", + "horizontal-grid-lines": "Lignes de la grille horizontale", + "chart-background": "Arrière-plan du graphique", + "grid-lines-color": "Couleur des lignes de la grille", + "border": "Bordure", + "axis": "Axe", + "vertical-axis": "Axe vertical", + "ticks": "Graduations", + "horizontal-axis": "Axe horizontal", + "shape-empty-circle": "Cercle vide", + "shape-circle": "Cercle", + "shape-rect": "Rectangle", + "shape-round-rect": "Rectangle arrondi", + "shape-triangle": "Triangle", + "shape-diamond": "Losange", + "shape-pin": "Épingle", + "shape-arrow": "Flèche", + "shape-none": "Aucune", + "line-type-solid": "Solide", + "line-type-dashed": "Tiretée", + "line-type-dotted": "Pointillée", + "label-position-top": "Haut", + "label-position-bottom": "Bas", + "label-position-outside": "Extérieur", + "label-position-inside": "Intérieur", + "fill": "Remplissage", + "fill-type-none": "Aucun", + "fill-type-solid": "Solide", + "fill-type-opacity": "Opacité", + "fill-type-gradient": "Dégradé", + "background": "Arrière-plan", + "opacity": "Opacité", + "gradient-stops": "Points d’arrêt du dégradé", + "gradient-start": "début", + "gradient-end": "fin", + "animation": { + "animation": "Animation", + "animation-threshold": "Seuil d’animation", + "animation-duration": "Durée de l’animation", + "animation-easing": "Courbe d’animation", + "animation-delay": "Délai de l’animation", + "update-animation-duration": "Durée de l’animation de mise à jour", + "update-animation-easing": "Courbe de l’animation de mise à jour", + "update-animation-delay": "Délai de l’animation de mise à jour" + }, + "chart-axis": { + "scale": "Échelle", + "scale-min": "min", + "scale-max": "max", + "scale-auto": "Auto" + }, + "bar": { + "show-border": "Afficher la bordure", + "border-width": "Épaisseur de la bordure", + "border-radius": "Rayon de la bordure", + "bar-width": "Largeur de la barre", + "label": "Étiquette", + "label-hint": "Afficher l’étiquette au-dessus de la barre.", + "series-label-hint": "Afficher l’étiquette avec la valeur au-dessus de la barre.", + "label-background": "Arrière-plan de l’étiquette" + } + }, + "color": { + "color-settings": "Paramètres de couleur", + "color-type-constant": "Constante", + "color-type-gradient": "Dégradé", + "color-type-range": "Plage", + "color-type-function": "Fonction", + "color": "Couleur", + "value-range": "Plage de valeurs", + "from": "De", + "to": "À", + "color-function": "Fonction de couleur", + "copy-color-settings-from": "Copier les paramètres de couleur depuis", + "copy-from": "Copier depuis", + "settings-type": "Type de paramètres", + "basic-mode": "Basique", + "advanced-mode": "Avancé", + "entity-alias": "Alias d'entité", + "entity-attribute": "Attribut d'entité", + "gradient-color": "Couleur du dégradé", + "gradient-color-min": "Couleur", + "gradient-start": "Couleur de début du dégradé", + "gradient-start-min": "Début", + "gradient-end": "Couleur de fin du dégradé", + "gradient-end-min": "Fin", + "start-value": "Valeur de départ", + "end-value": "Valeur de fin", + "gradient-type": "Type de dégradé" + }, + "dashboard-state": { + "dashboard-state-settings": "Paramètres de l'état du tableau de bord", + "dashboard-state": "ID de l'état du tableau de bord", + "autofill-state-layout": "Remplir automatiquement la hauteur de la disposition de l'état par défaut", + "default-margin": "Marge par défaut des widgets", + "default-background-color": "Couleur de fond par défaut", + "sync-parent-state-params": "Synchroniser les paramètres d'état avec le tableau de bord parent" + }, + "date-range-navigator": { + "date-range-picker-settings": "Paramètres du sélecteur de plage de dates", + "hide-date-range-picker": "Masquer le sélecteur de plage de dates", + "picker-one-panel": "Sélecteur de plage de dates à un panneau", + "picker-auto-confirm": "Confirmation automatique du sélecteur de plage de dates", + "picker-show-template": "Afficher le modèle de sélecteur de plage de dates", + "first-day-of-week": "Premier jour de la semaine", + "interval-settings": "Paramètres d'intervalle", + "hide-interval": "Masquer l'intervalle", + "initial-interval": "Intervalle initial", + "interval-hour": "Heure", + "interval-day": "Jour", + "interval-week": "Semaine", + "interval-two-weeks": "2 semaines", + "interval-month": "Mois", + "interval-three-months": "3 mois", + "interval-six-months": "6 mois", + "step-settings": "Paramètres du pas", + "hide-step-size": "Masquer la taille du pas", + "initial-step-size": "Taille du pas initiale", + "hide-labels": "Masquer les étiquettes", + "use-session-storage": "Utiliser le stockage de session", + "localizationMap": { + "Sun": "Dim", + "Mon": "Lun", + "Tue": "Mar", + "Wed": "Mer", + "Thu": "Jeu", + "Fri": "Ven", + "Sat": "Sam", + "Jan": "Jan", + "Feb": "Fév", + "Mar": "Mar", + "Apr": "Avr", + "May": "Mai", + "Jun": "Juin", + "Jul": "Juil", + "Aug": "Août", + "Sep": "Sep", + "Oct": "Oct", + "Nov": "Nov", + "Dec": "Déc", + "January": "Janvier", + "February": "Février", + "March": "Mars", + "April": "Avril", + "June": "Juin", + "July": "Juillet", + "August": "Août", + "September": "Septembre", + "October": "Octobre", + "November": "Novembre", + "December": "Décembre", + "Custom Date Range": "Plage de dates personnalisée", + "Date Range Template": "Modèle de plage de dates", + "Today": "Aujourd'hui", + "Yesterday": "Hier", + "This Week": "Cette semaine", + "Last Week": "Semaine dernière", + "This Month": "Ce mois-ci", + "Last Month": "Mois dernier", + "Year": "Année", + "This Year": "Cette année", + "Last Year": "Année dernière", + "Date picker": "Sélecteur de date", + "Hour": "Heure", + "Day": "Jour", + "Week": "Semaine", + "2 weeks": "2 semaines", + "Month": "Mois", + "3 months": "3 mois", + "6 months": "6 mois", + "Custom interval": "Intervalle personnalisé", + "Interval": "Intervalle", + "Step size": "Taille du pas", + "Ok": "OK" + } + }, + "doughnut": { + "doughnut-appearance": "Apparence du diagramme en anneau", + "layout": "Disposition", + "layout-default": "Par défaut", + "layout-with-total": "Avec total", + "central-total-value": "Valeur totale centrale", + "doughnut-card-style": "Style de carte du diagramme en anneau" + }, + "entities-hierarchy": { + "hierarchy-data-settings": "Paramètres des données hiérarchiques", + "relations-query-function": "Fonction de requête des relations des nœuds", + "has-children-function": "Fonction indiquant si le nœud a des enfants", + "node-state-settings": "Paramètres d'état du nœud", + "node-opened-function": "Fonction pour ouvrir le nœud par défaut", + "node-disabled-function": "Fonction désactivant le nœud", + "display-settings": "Paramètres d'affichage", + "node-icon-function": "Fonction d'icône de nœud", + "node-text-function": "Fonction de texte de nœud", + "sort-settings": "Paramètres de tri", + "nodes-sort-function": "Fonction de tri des nœuds" + }, + "edge": { + "display-default-title": "Afficher le titre par défaut" + }, + "gateway": { + "general-settings": "Paramètres généraux", + "widget-title": "Titre du widget", + "default-archive-file-name": "Nom de fichier d'archive par défaut", + "device-type-for-new-gateway": "Type d'appareil pour une nouvelle passerelle", + "messages-settings": "Paramètres des messages", + "save-config-success-message": "Message texte sur la configuration de passerelle enregistrée avec succès", + "device-name-exists-message": "Message texte indiquant qu'un appareil portant ce nom existe déjà", + "gateway-title": "Formulaire de passerelle", + "read-only": "Lecture seule", + "events-title": "Titre du formulaire des événements de passerelle", + "events-filter": "Filtre des événements", + "event-key-contains": "Clé d'événement contient...", + "show-connector": "Afficher pour le connecteur", + "connector-state-param-key": "Clé de paramètre d'état du connecteur", + "message": "Message", + "level": "Niveau", + "created-time": "Date de création" + }, + "gauge": { + "default-color": "Couleur par défaut", + "radial-gauge-settings": "Paramètres du jauge radial", + "ticks-settings": "Paramètres des graduations", + "min-value": "Valeur minimale", + "max-value": "Valeur maximale", + "min-value-short": "min", + "max-value-short": "max", + "start-ticks-angle": "Angle de départ des graduations", + "ticks-angle": "Angle des graduations", + "major-ticks": "Graduations majeures", + "major-ticks-count": "Nombre de graduations majeures", + "major-ticks-color": "Couleur des graduations majeures", + "minor-ticks": "Graduations mineures", + "minor-ticks-count": "Nombre de graduations mineures", + "minor-ticks-color": "Couleur des graduations mineures", + "tick-numbers-font": "Police des nombres de graduation", + "unit-title-settings": "Paramètres du titre de l'unité", + "show-unit-title": "Titre des unités", + "unit-title": "Titre de l'unité", + "title-font": "Police du titre", + "units-settings": "Paramètres des unités", + "units-font": "Police du texte des unités", + "value-box-settings": "Paramètres de la boîte de valeur", + "show-value-box": "Afficher la boîte de valeur", + "value-box": "Boîte de valeur", + "value-int": "Nombre de chiffres pour la partie entière", + "value-text": "Texte de la valeur", + "value-text-shadow": "Ombre du texte de valeur", + "value-font": "Police du texte de valeur", + "rect-stroke-color-start": "Couleur de début du contour du rectangle", + "rect-stroke-color-end": "Couleur de fin du contour du rectangle", + "background-color": "Couleur de fond", + "shadow-color": "Couleur de l'ombre", + "value-box-rect-stroke-color": "Couleur du contour de la boîte de valeur", + "value-box-rect-stroke-color-end": "Couleur de fin du contour de la boîte de valeur", + "value-box-background-color": "Couleur de fond de la boîte de valeur", + "value-box-shadow-color": "Couleur de l'ombre de la boîte de valeur", + "plate-settings": "Paramètres de la plaque", + "show-plate-border": "Bordure de la plaque", + "plate-color": "Couleur de la plaque", + "needle-settings": "Paramètres de l'aiguille", + "needle-circle-size": "Taille du cercle de l'aiguille", + "needle-color": "Couleur de l'aiguille", + "needle-color-start": "Couleur de l'aiguille - début du gradient", + "needle-color-end": "Couleur de l'aiguille - fin du gradient", + "needle-color-shadow-up": "Couleur de l'ombre supérieure de l'aiguille", + "needle-color-shadow-down": "Ombre portée", + "highlights-settings": "Paramètres des surlignages", + "highlights-width": "Largeur des surlignages", + "highlights": "Surlignages", + "highlight-from": "De", + "highlight-to": "À", + "highlight-color": "Couleur", + "no-highlights": "Aucun surlignage configuré", + "add-highlight": "Ajouter un surlignage", + "animation-settings": "Paramètres d'animation", + "enable-animation": "Animation", + "animation-duration-rule": "Durée et règle d'animation", + "animation-duration": "Durée de l'animation", + "animation-rule": "Règle d'animation", + "animation-linear": "Linéaire", + "animation-quad": "Quad", + "animation-quint": "Quint", + "animation-cycle": "Cycle", + "animation-bounce": "Rebond", + "animation-elastic": "Élastique", + "animation-dequad": "Déquad", + "animation-dequint": "Déquint", + "animation-decycle": "Décyle", + "animation-debounce": "Dérebon", + "animation-delastic": "Délastic", + "linear-gauge-settings": "Paramètres de jauge linéaire", + "bar-stroke": "Contour de la barre", + "bar-stroke-width": "Largeur du contour de la barre", + "bar-stroke-color": "Couleur du contour de la barre", + "bar-background-color": "Couleur de fond de la barre - début du gradient", + "bar-background-color-end": "Couleur de fond de la barre - fin du gradient", + "progress-bar-color": "Couleur de la barre de progression", + "progress-bar": "Barre de progression", + "progress-bar-color-start": "Couleur de la barre de progression - début du gradient", + "progress-bar-color-end": "Couleur de la barre de progression - fin du gradient", + "major-ticks-names": "Noms des graduations majeures", + "show-stroke-ticks": "Afficher les traits des graduations", + "major-ticks-font": "Police des graduations majeures", + "border-color": "Couleur de bordure", + "border-width": "Largeur de bordure", + "needle-circle": "Cercle de l'aiguille", + "needle-circle-color": "Couleur du cercle de l'aiguille", + "animation-target": "Cible d'animation", + "animation-target-needle": "Aiguille", + "animation-target-plate": "Plaque", + "common-settings": "Paramètres communs de jauge", + "gauge-type": "Type de jauge", + "gauge-type-arc": "Arc", + "gauge-type-donut": "Donut", + "gauge-type-horizontal-bar": "Barre horizontale", + "gauge-type-vertical-bar": "Barre verticale", + "donut-start-angle": "Angle de départ (degrés)", + "bar-settings": "Paramètres de barre de jauge", + "relative-bar-width": "Largeur relative de la barre", + "neon-glow-brightness": "Luminosité de l'effet néon (0-100)", + "neon-glow-brightness-hint": "0 - désactiver l'effet", + "stripes-thickness": "Épaisseur des rayures", + "stripes-thickness-hint": "0 - pas de rayures", + "rounded-line-cap": "Extrémités arrondies", + "bar-color-settings": "Paramètres de couleur de barre", + "use-precise-level-color-values": "Utiliser des niveaux de couleur précis", + "bar-colors": "Couleurs de la barre, du plus bas au plus haut", + "color": "Couleur", + "no-bar-colors": "Aucune couleur de barre configurée", + "add-bar-color": "Ajouter une couleur de barre", + "from": "De", + "to": "À", + "fixed-level-colors": "Couleurs de barre utilisant des valeurs limites", + "gauge-title-settings": "Paramètres du titre de jauge", + "show-gauge-title": "Afficher le titre de la jauge", + "gauge-title": "Titre de la jauge", + "gauge-title-font": "Police du titre de la jauge", + "unit-title-and-timestamp-settings": "Paramètres du titre d'unité et de l'horodatage", + "show-timestamp": "Horodatage", + "timestamp-format": "Format d'horodatage", + "label-font": "Police de l'étiquette sous la valeur", + "value-settings": "Paramètres de valeur", + "show-value": "Afficher le texte de valeur", + "min-max-settings": "Paramètres des étiquettes min/max", + "show-min-max": "Afficher les valeurs min et max", + "min-max-font": "Police des étiquettes min et max", + "show-ticks": "Afficher les graduations", + "tick-width": "Largeur des graduations", + "tick-color": "Couleur des graduations", + "tick-values": "Valeurs de graduation", + "no-tick-values": "Aucune valeur de graduation configurée", + "add-tick-value": "Ajouter une valeur de graduation", + "gauge-appearance": "Apparence de la jauge", + "units-title": "Titre des unités", + "value": "Valeur", + "ticks": "Graduations", + "arrow-and-scale-color": "Couleur par défaut de la flèche et de l'échelle", + "scale-settings": "Paramètres de l'échelle", + "scale": "Échelle", + "scale-color": "Couleurs de l'échelle", + "compass-appearance": "Apparence de la boussole", + "label": "Étiquette", + "labels": "Étiquettes", + "label-style": "Style de l'étiquette", + "simple-gauge-type": "Type", + "gauge-bar-background": "Fond de la barre de jauge", + "bar-color": "Couleur de la barre", + "min-and-max-value": "Valeur min et max", + "min-and-max-label": "Étiquette min et max", + "font": "Police", + "tick-width-and-color": "Largeur et couleur des graduations", + "min-max-validation-text": "La valeur maximale doit être supérieure à la valeur minimale" + }, + "gpio": { + "pin": "Broche", + "label": "Étiquette", + "row": "Ligne", + "column": "Colonne", + "color": "Couleur", + "panel-settings": "Paramètres du panneau", + "background-color": "Couleur de fond", + "gpio-switches": "Interrupteurs GPIO", + "no-gpio-switches": "Aucun interrupteur GPIO configuré", + "add-gpio-switch": "Ajouter un interrupteur GPIO", + "gpio-status-request": "Requête de statut GPIO", + "method-name": "Nom de la méthode", + "method-body": "Corps de la méthode", + "gpio-status-change-request": "Requête de changement de statut GPIO", + "parse-gpio-status-function": "Fonction de traitement du statut GPIO", + "gpio-leds": "LEDs GPIO", + "no-gpio-leds": "Aucune LED GPIO configurée", + "add-gpio-led": "Ajouter une LED GPIO" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, + "input-widgets": { + "attribute-not-allowed": "Le paramètre d'attribut ne peut pas être utilisé dans ce widget", + "blocked-location": "La géolocalisation est bloquée dans votre navigateur", + "claim-device": "Réclamer l'appareil", + "claim-failed": "Échec de la réclamation de l'appareil !", + "claim-not-found": "Appareil introuvable !", + "claim-successful": "L'appareil a été réclamé avec succès !", + "date": "Date", + "device-name": "Nom de l'appareil", + "device-name-required": "Le nom de l'appareil est requis", + "discard-changes": "Annuler les modifications", + "entity-attribute-required": "L'attribut d'entité est requis", + "entity-coordinate-required": "Les deux champs, latitude et longitude, sont requis", + "entity-timeseries-required": "La série temporelle de l'entité est requise", + "get-location": "Obtenir la position actuelle", + "invalid-date": "Date invalide", + "latitude": "Latitude", + "longitude": "Longitude", + "min-value-error": "Valeur minimale : {{value}}", + "max-value-error": "Valeur maximale : {{value}}", + "not-allowed-entity": "L'entité sélectionnée ne peut pas avoir d'attributs partagés", + "no-attribute-selected": "Aucun attribut sélectionné", + "no-datakey-selected": "Aucune clé de données sélectionnée", + "no-coordinate-specified": "La clé de données pour latitude/longitude n'est pas spécifiée", + "no-entity-selected": "Aucune entité sélectionnée", + "no-image": "Aucune image", + "no-support-geolocation": "Votre navigateur ne prend pas en charge la géolocalisation", + "no-support-web-camera": "Votre navigateur ne prend pas en charge les caméras", + "enable-https-use-widget": "Veuillez activer HTTPS pour utiliser ce widget", + "no-found-your-camera": "Impossible de trouver votre caméra", + "no-permission-camera": "Permission refusée / Ce site n'a pas l'autorisation d'utiliser la caméra", + "no-timeseries-selected": "Aucune série temporelle sélectionnée", + "secret-key": "Clé secrète", + "secret-key-required": "La clé secrète est requise", + "switch-attribute-value": "Basculer la valeur de l'attribut de l'entité", + "switch-camera": "Changer de caméra", + "switch-timeseries-value": "Basculer la valeur de la série temporelle de l'entité", + "take-photo": "Prendre une photo", + "time": "Heure", + "timeseries-not-allowed": "Le paramètre de série temporelle ne peut pas être utilisé dans ce widget", + "update-failed": "Échec de la mise à jour", + "update-successful": "Mise à jour réussie", + "update-attribute": "Mettre à jour l'attribut", + "update-timeseries": "Mettre à jour la série temporelle", + "value": "Valeur", + "general-settings": "Paramètres généraux", + "widget-title": "Titre du widget", + "claim-button-label": "Étiquette du bouton de réclamation", + "show-secret-key-field": "Afficher le champ de saisie 'Clé secrète'", + "labels-settings": "Paramètres des étiquettes", + "show-labels": "Afficher les étiquettes", + "device-name-label": "Étiquette pour le champ de nom d'appareil", + "secret-key-label": "Étiquette pour le champ de clé secrète", + "messages-settings": "Paramètres des messages", + "claim-device-success-message": "Message de succès pour la réclamation de l'appareil", + "claim-device-not-found-message": "Message lorsque l'appareil est introuvable", + "claim-device-failed-message": "Message d'échec pour la réclamation de l'appareil", + "claim-device-name-required-message": "Message d'erreur 'Nom de l'appareil requis'", + "claim-device-secret-key-required-message": "Message d'erreur 'Clé secrète requise'", + "show-label": "Afficher l'étiquette", + "label": "Étiquette", + "required": "Requis", + "required-error-message": "Message d'erreur 'Requis'", + "show-result-message": "Afficher le message de résultat", + "integer-field-settings": "Paramètres du champ entier", + "min-value": "Valeur minimale", + "max-value": "Valeur maximale", + "double-field-settings": "Paramètres du champ double", + "text-field-settings": "Paramètres du champ texte", + "min-length": "Longueur minimale", + "max-length": "Longueur maximale", + "checkbox-settings": "Paramètres de la case à cocher", + "true-label": "Étiquette cochée", + "false-label": "Étiquette décochée", + "image-input-settings": "Paramètres d'entrée d'image", + "display-preview": "Afficher l’aperçu", + "display-clear-button": "Afficher le bouton de réinitialisation", + "display-apply-button": "Afficher le bouton d’application", + "display-discard-button": "Afficher le bouton d’annulation", + "datetime-field-settings": "Paramètres du champ date/heure", + "display-time-input": "Afficher le champ d'heure", + "latitude-key-name": "Nom de la clé latitude", + "longitude-key-name": "Nom de la clé longitude", + "show-get-location-button": "Afficher le bouton 'Obtenir la position actuelle'", + "use-high-accuracy": "Utiliser une haute précision", + "location-fields-settings": "Paramètres des champs de localisation", + "latitude-label": "Étiquette de latitude", + "longitude-label": "Étiquette de longitude", + "input-fields-alignment": "Alignement des champs de saisie", + "input-fields-alignment-column": "Colonne (par défaut)", + "input-fields-alignment-row": "Ligne", + "layout": "Disposition", + "row-gap": "Espace entre les lignes (en pixels)", + "column-gap": "Espace entre les colonnes (en pixels)", + "latitude-field-required": "Le champ latitude est requis", + "longitude-field-required": "Le champ longitude est requis", + "attribute-settings": "Paramètres des attributs", + "widget-mode": "Mode du widget", + "widget-mode-update-attribute": "Mettre à jour l’attribut", + "widget-mode-update-timeseries": "Mettre à jour la série temporelle", + "attribute-scope": "Portée de l’attribut", + "attribute-scope-server": "Attribut serveur", + "attribute-scope-shared": "Attribut partagé", + "value-required": "Valeur requise", + "image-settings": "Paramètres de l’image", + "image-format": "Format d’image", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "Qualité d’image (utilise la compression avec perte pour jpeg/webp)", + "max-image-width": "Largeur maximale de l’image", + "max-image-height": "Hauteur maximale de l’image", + "action-buttons": "Boutons d'action", + "show-action-buttons": "Afficher les boutons d'action", + "update-all-values": "Mettre à jour toutes les valeurs, pas seulement les modifiées", + "save-button-label": "Étiquette du bouton 'ENREGISTRER'", + "reset-button-label": "Étiquette du bouton 'ANNULER'", + "group-settings": "Paramètres de groupe", + "show-group-title": "Afficher le titre du groupe de champs liés à différentes entités", + "group-title": "Titre du groupe", + "fields-alignment": "Alignement des champs", + "fields-alignment-row": "Ligne (par défaut)", + "fields-alignment-column": "Colonne", + "fields-in-row": "Nombre de champs par ligne", + "option-value": "Valeur (écrire 'null' pour créer une option vide)", + "option-label": "Étiquette", + "hide-input-field": "Masquer le champ de saisie", + "datakey-type": "Type de clé de données", + "datakey-type-server": "Attribut serveur (par défaut)", + "datakey-type-shared": "Attribut partagé", + "datakey-type-timeseries": "Série temporelle", + "datakey-value-type": "Type de valeur de la clé de données", + "datakey-value-type-string": "Chaîne", + "datakey-value-type-double": "Double", + "datakey-value-type-integer": "Entier", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "Booléen (Case à cocher)", + "datakey-value-type-boolean-switch": "Booléen (Interrupteur)", + "datakey-value-type-date-time": "Date et heure", + "datakey-value-type-date": "Date", + "datakey-value-type-time": "Heure", + "datakey-value-type-select": "Liste déroulante", + "datakey-value-type-radio": "Boutons radio", + "datakey-value-type-color": "Couleur", + "value-is-required": "La valeur est requise", + "ability-to-edit-attribute": "Capacité à modifier l’attribut", + "ability-to-edit-attribute-editable": "Modifiable (par défaut)", + "ability-to-edit-attribute-disabled": "Désactivé", + "ability-to-edit-attribute-readonly": "Lecture seule", + "disable-on-datakey-name": "Désactiver selon la valeur 'false' d’une autre clé (spécifiez le nom de la clé)", + "field-appearance": "Apparence du champ", + "appearance-fill": "Remplissage", + "appearance-outline": "Contour", + "subscript-sizing": "Taille des indices", + "subscript-sizing-fixed": "Fixe", + "subscript-sizing-dynamic": "Dynamique", + "slide-toggle-settings": "Paramètres de l’interrupteur coulissant", + "slide-toggle-label-position": "Position de l’étiquette de l’interrupteur", + "slide-toggle-label-position-after": "Après", + "slide-toggle-label-position-before": "Avant", + "select-options": "Options de sélection", + "no-select-options": "Aucune option de sélection configurée", + "add-select-option": "Ajouter une option de sélection", + "numeric-field-settings": "Paramètres du champ numérique", + "step-interval": "Intervalle entre les valeurs", + "error-messages": "Messages d’erreur", + "min-value-error-message": "Message d’erreur de la 'valeur minimale'", + "max-value-error-message": "Message d’erreur de la 'valeur maximale'", + "invalid-date-error-message": "Message d’erreur de 'date invalide'", + "invalid-JSON-error-message": "Message d’erreur de 'JSON invalide'", + "icon-settings": "Paramètres de l’icône", + "dialog-editor-settings": "Paramètres de l’éditeur de dialogue", + "use-custom-icon": "Utiliser une icône personnalisée", + "input-cell-icon": "Icône à afficher avant le champ de saisie", + "value-conversion-settings": "Paramètres de conversion de valeur", + "get-value-settings": "Paramètres de récupération de valeur", + "use-get-value-function": "Utiliser la fonction getValue", + "get-value-function": "Fonction getValue", + "set-value-settings": "Paramètres de définition de valeur", + "use-set-value-function": "Utiliser la fonction setValue", + "set-value-function": "Fonction setValue", + "json-invalid": "Le format de la valeur JSON est invalide", + "title": "Titre", + "cancel-button-label": "Étiquette du bouton 'Annuler'", + "radio-button-settings": "Paramètres des boutons radio", + "color": "Couleur", + "columns": "Colonnes", + "radio-options": "Options radio", + "no-radio-options": "Aucune option radio configurée", + "add-radio-option": "Ajouter une option radio", + "radio-label-position": "Position de l’étiquette", + "radio-label-position-before": "Avant", + "radio-label-position-after": "Après" + }, + "invalid-qr-code-text": "Texte saisi invalide pour le code QR. L'entrée doit être une chaîne de caractères", + "qr-code": { + "use-qr-code-text-function": "Utiliser une fonction de texte pour le code QR", + "qr-code-text-pattern": "Modèle de texte du code QR (par ex. '${entityName} | ${keyName} - texte.')", + "qr-code-text-pattern-hint": "Le modèle de texte du code QR utilise la valeur de la première clé trouvée dans les entités de l'alias d'entité.", + "qr-code-text-pattern-required": "Le modèle de texte du code QR est requis.", + "qr-code-text-function": "Fonction de texte du code QR" + }, + "label-widget": { + "label-pattern": "Modèle", + "label-pattern-hint": "Astuce : par ex. 'Texte ${keyName} unités.' ou ${#<key index>} unités'", + "label-pattern-required": "Le modèle est requis", + "label-position": "Position (pourcentage par rapport à l’arrière-plan)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Couleur de fond", + "font-settings": "Paramètres de la police", + "background-image": "Image de fond", + "labels": "Étiquettes", + "no-labels": "Aucune étiquette configurée", + "add-label": "Ajouter une étiquette" + }, + "navigation": { + "title": "Titre", + "navigation-path": "Chemin de navigation", + "filter-type": "Type de filtre", + "filter-type-all": "Tous les éléments", + "filter-type-include": "Inclure les éléments", + "filter-type-exclude": "Exclure les éléments", + "items": "Éléments", + "enter-urls-to-filter": "Saisissez les URLs à filtrer..." + }, + "persistent-table": { + "rpc-id": "ID RPC", + "message-type": "Type de message", + "method": "Méthode", + "params": "Paramètres", + "created-time": "Heure de création", + "expiration-time": "Date d’expiration", + "retries": "Tentatives", + "status": "Statut", + "filter": "Filtrer", + "refresh": "Actualiser", + "add": "Ajouter une requête RPC", + "details": "Détails", + "delete": "Supprimer", + "delete-request-title": "Supprimer la requête RPC persistante", + "delete-request-text": "Êtes-vous sûr de vouloir supprimer cette requête ?", + "details-title": "Détails ID RPC : ", + "additional-info": "Informations supplémentaires", + "response": "Réponse", + "any-status": "Tous les statuts", + "rpc-status-list": "Liste des statuts RPC", + "no-request-prompt": "Aucune requête à afficher", + "send-request": "Envoyer la requête", + "add-title": "Créer une requête RPC persistante", + "method-error": "La méthode est requise.", + "timeout-error": "La valeur minimale du délai d’attente est de 5000 (5 secondes).", + "white-space-error": "Les espaces ne sont pas autorisés.", + "rpc-status": { + "QUEUED": "EN FILE", + "SENT": "ENVOYÉ", + "DELIVERED": "LIVRÉ", + "SUCCESSFUL": "RÉUSSI", + "TIMEOUT": "DÉLAI DÉPASSÉ", + "EXPIRED": "EXPIRÉ", + "FAILED": "ÉCHOUÉ" + }, + "rpc-search-status-all": "TOUS", + "message-types": { + "false": "Bidirectionnel", + "true": "Unidirectionnel" + }, + "general-settings": "Paramètres généraux", + "enable-filter": "Activer le filtre", + "enable-sticky-header": "Afficher l’en-tête pendant le défilement", + "enable-sticky-action": "Afficher la colonne des actions pendant le défilement", + "display-request-details": "Afficher les détails de la requête", + "allow-send-request": "Autoriser l’envoi de requêtes RPC", + "allow-delete-request": "Autoriser la suppression de requêtes", + "columns-settings": "Paramètres des colonnes", + "display-columns": "Colonnes à afficher", + "column": "Colonne", + "no-columns-found": "Aucune colonne trouvée", + "no-columns-matching": "'{{column}}' introuvable." + }, + "range-chart": { + "chart": "Graphique", + "data-zoom": "Zoom des données", + "range-chart-appearance": "Apparence du graphique à plages", + "range-colors": "Couleurs de plage", + "out-of-range-color": "Couleur hors plage", + "show-range-thresholds": "Afficher les seuils de plage", + "range-thresholds-settings": "Paramètres des seuils de plage", + "fill-area": "Remplir la zone", + "fill-area-opacity": "Opacité de la zone remplie", + "range-chart-style": "Style du graphique à plages" + }, + "rpc": { + "value-settings": "Paramètres de la valeur", + "initial-value": "Valeur initiale", + "retrieve-value-settings": "Paramètres de récupération de la valeur marche/arrêt", + "retrieve-value-method": "Récupérer la valeur via la méthode", + "retrieve-value-method-none": "Ne pas récupérer", + "retrieve-value-method-rpc": "Appeler la méthode RPC de récupération", + "retrieve-value-method-attribute": "S'abonner à un attribut", + "retrieve-value-method-timeseries": "S'abonner à une série temporelle", + "attribute-value-key": "Clé de l'attribut", + "timeseries-value-key": "Clé de la série temporelle", + "get-value-method": "Méthode RPC de récupération", + "parse-value-function": "Fonction d'analyse de la valeur", + "update-value-settings": "Paramètres de mise à jour de la valeur", + "set-value-method": "Méthode RPC de mise à jour", + "convert-value-function": "Fonction de conversion de la valeur", + "rpc-settings": "Paramètres RPC", + "request-timeout": "Délai d'attente de la requête RPC (ms)", + "persistent-rpc-settings": "Paramètres RPC persistants", + "request-persistent": "Requête RPC persistante", + "persistent-polling-interval": "Intervalle de sondage (ms) pour la réponse RPC persistante", + "common-settings": "Paramètres communs", + "switch-title": "Titre de l'interrupteur", + "show-on-off-labels": "Afficher les étiquettes marche/arrêt", + "slide-toggle-label": "Étiquette du curseur", + "label-position": "Position de l’étiquette", + "label-position-before": "Avant", + "label-position-after": "Après", + "slider-color": "Couleur du curseur", + "slider-color-primary": "Principal", + "slider-color-accent": "Accent", + "slider-color-warn": "Avertissement", + "button-style": "Style de bouton", + "button-raised": "Bouton en relief", + "button-primary": "Couleur principale", + "button-background-color": "Couleur d’arrière-plan du bouton", + "button-text-color": "Couleur du texte du bouton", + "widget-title": "Titre du widget", + "button-label": "Étiquette du bouton", + "device-attribute-scope": "Portée de l'attribut de l'appareil", + "server-attribute": "Attribut serveur", + "shared-attribute": "Attribut partagé", + "device-attribute-parameters": "Paramètres d’attribut de l’appareil", + "is-one-way-command": "Commande à sens unique", + "rpc-method": "Méthode RPC", + "rpc-method-params": "Paramètres de la méthode RPC", + "show-rpc-error": "Afficher les erreurs d’exécution RPC", + "led-title": "Titre LED", + "led-color": "Couleur LED", + "check-status-settings": "Paramètres de vérification de statut", + "perform-rpc-status-check": "Effectuer une vérification de statut de l’appareil via RPC", + "retrieve-led-status-value-method": "Méthode de récupération de l'état LED", + "led-status-value-attribute": "Attribut de l’appareil contenant l’état LED", + "led-status-value-timeseries": "Série temporelle contenant l’état LED", + "check-status-method": "Méthode RPC de vérification de l’état de l’appareil", + "parse-led-status-value-function": "Fonction d’analyse de l’état LED", + "knob-title": "Titre du bouton rotatif", + "min-value": "Valeur minimale", + "max-value": "Valeur maximale" + }, + "maps": { + "map-type": { + "type": "Type de carte", + "map": "Carte", + "image": "Image" + }, + "image": { + "image-source": "Source de l’image", + "image-source-image": "Image", + "image-source-entity-key": "Clé d’entité", + "source-entity-alias": "Alias d'entité source", + "image-url-key": "Clé de l’URL de l’image", + "image-url-key-required": "La clé de l’URL de l’image est requise" + }, + "control": { + "map-controls": "Contrôles de la carte", + "position": "Position", + "position-topleft": "En haut à gauche", + "position-topright": "En haut à droite", + "position-bottomleft": "En bas à gauche", + "position-bottomright": "En bas à droite", + "zoom-actions": "Actions de zoom", + "zoom-scroll": "Défilement", + "zoom-double-click": "Double-clic", + "zoom-control-buttons": "Boutons de contrôle", + "scale": "Échelle", + "scale-metric": "Métrique", + "scale-imperial": "Impérial", + "switch-to-drag-mode-using-button": "Basculer en mode glisser via le bouton" + }, + "timeline": { + "control-panel": "Panneau de contrôle de la chronologie", + "time-step": "Intervalle de temps", + "speed-options": "Options de vitesse", + "timestamp": "Horodatage", + "snap-to-real-location": "Aligner sur la position réelle", + "location-snap-filter-function": "Fonction de filtrage pour alignement de position", + "no-trips-data-available": "Aucune donnée de trajet disponible" + }, + "map-action": { + "map-action-buttons": "Boutons d’action sur la carte", + "label": "Étiquette", + "icon": "Icône", + "color": "Couleur", + "action": "Action", + "add-button": "Ajouter un bouton", + "no-action-buttons-configured": "Aucun bouton d'action configuré", + "remove-action-button": "Supprimer le bouton d'action", + "map-action-button": "Bouton d’action sur la carte", + "button-requires": "Le bouton nécessite une étiquette ou une icône" + }, + "common": { + "common-map-settings": "Paramètres de carte communs", + "fit-map-bounds": "Ajuster les limites de la carte pour couvrir tous les marqueurs", + "default-map-center-position": "Position centrale par défaut de la carte", + "default-map-zoom-level": "Niveau de zoom par défaut", + "entities-limit": "Limite d’entités à charger" + }, + "layer": { + "label": "Étiquette", + "layer": "Couche", + "layers": "Couches", + "map-layers": "Couches de carte", + "add-layer": "Ajouter une couche", + "layer-settings": "Paramètres de la couche", + "remove-layer": "Supprimer la couche", + "no-layers": "Aucune couche configurée", + "roadmap": "Plan", + "satellite": "Satellite", + "hybrid": "Hybride", + "reference": { + "reference-layer": "Couche de référence", + "no-layer": "Aucune couche", + "openstreetmap-hybrid": "Hybride OpenStreetMap", + "world-edition-hybrid": "Hybride Édition Mondiale", + "enhanced-contrast-hybrid": "Hybride à contraste amélioré" }, - "end-time": "Heure de fin", - "min-polling-interval-message": "Un intervalle d'interrogation d'au moins 1 seconde est autorisé.", - "no-alarms-matching": "Aucune alarme correspondant à {{entity}} n'a été trouvée. ", - "no-alarms-prompt": "Aucune alarme", - "no-data": "Aucune donnée à afficher", - "originator": "Source", - "originator-type": "Type de Source", - "polling-interval": "Intervalle d'interrogation des alarmes (sec)", - "polling-interval-required": "L'intervalle d'interrogation des alarmes est requis.", - "search": "Rechercher des alarmes", - "search-status": { - "ACK": "acquitté", - "ACTIVE": "active", - "ANY": "Toutes", - "CLEARED": "effacée", - "UNACK": "non acquittée" + "provider": { + "provider": "Fournisseur", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Plan", + "satellite": "Satellite", + "hybrid": "Hybride", + "terrain": "Relief" + }, + "here": { + "title": "HERE", + "normal-day": "Jour normal", + "normal-night": "Nuit normale", + "hybrid-day": "Hybride jour", + "terrain-day": "Relief jour" + }, + "tencent": { + "title": "Tencent", + "normal": "Normal", + "satellite": "Satellite", + "terrain": "Relief" + }, + "custom": { + "title": "Personnalisé", + "tile-url": "URL des tuiles" + } }, - "select-alarm": "Sélectionnez une alarme", - "selected-alarms": "{count, plural, =1 {1 alarme} other {# alarmes} } sélectionnées", - "severity": "Gravité", - "severity-critical": "Critique", - "severity-indeterminate": "indéterminée", - "severity-major": "Majeure", - "severity-minor": "mineure", - "severity-warning": "Avertissement", - "start-time": "Heure de début", - "status": "État", - "type": "Type", - "alarm-status-list": "Liste d'état des alarmes", - "any-status": "Tout statut", - "alarm-severity-list": "Liste de gravité des alarmes", - "any-severity": "Toute gravité", - "alarm-filter": "Filtre d'alarme", - "max-count-load": "Nombre maximum d'alarmes à charger (0 - illimité)", - "max-count-load-required": "Le nombre maximum d'alarmes à charger est requis.", - "max-count-load-error-min": "La valeur minimale est 0.", - "fetch-size": "Taille de la requête", - "fetch-size-required": "La taille de la requête est requise.", - "fetch-size-error-min": "La valeur minimale est 10.", - "alarm-type-list": "Liste des types d'alarme", - "any-type": "N'importe quel type", - "search-propagated-alarms": "Rechercher les alarmes propagées" - }, - "alias": { - "add": "Ajouter un alias", - "all-entities": "Toutes les entités", - "any-relation": "toutes", - "default-entity-parameter-name": "Par défaut", - "default-state-entity": "Entité d'état par défaut", - "duplicate-alias": "Un alias portant le même nom existe déjà.", - "edit": "Modifier l'alias", - "entity-filter": "Filtre d'entité", - "entity-filter-no-entity-matched": "Aucune entité correspondant au filtre spécifié n'a été trouvée.", - "filter-type": "Type de filtre", - "filter-type-asset-search-query": "Requête de recherche d'actifs", - "filter-type-asset-search-query-description": "Actifs de types {{assetTypes}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-asset-type": "type d'actif", - "filter-type-asset-type-and-name-description": "Actifs de type '{{assetTypes}}' et dont le nom commence par '{{prefix}}'", - "filter-type-asset-type-description": "Actifs de type '{{assetTypes}}'", - "filter-type-device-search-query": "Requête de recherche de dispositif", - "filter-type-device-search-query-description": "Dispositifs de types {{deviceTypes}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-device-type": "Type de dispositif", - "filter-type-device-type-and-name-description": "Dispositifs de type '{{deviceTypes}}' et dont le nom commence par '{{prefix}}'", - "filter-type-device-type-description": "Dispositifs de type '{{deviceTypes}}'", - "filter-type-entity-list": "Liste d'entités", - "filter-type-entity-name": "Nom d'entité", - "filter-type-entity-view-search-query": "Requête de recherche vue d'entité", - "filter-type-entity-view-search-query-description": "Vues d'entité avec les types {{entityViewTypes}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-edge-search-query": "Requête de recherche de Edge", - "filter-type-edge-search-query-description": "Edges avec types {{edgeTypes}} qui ont {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-entity-view-type": "Type de vue d'entité", - "filter-type-entity-view-type-and-name-description": "Vues d'entité de type '{{entityViewTypes}}' et dont le nom commence par '{{prefix}}'", - "filter-type-entity-view-type-description": "Vues d'entité de type '{{entityViewTypes}}'", - "filter-type-edge-type": "Types de Edge", - "filter-type-edge-type-description": "Edges de type '{{edgeTypes}}'", - "filter-type-relations-query": "Interrogation des relations", - "filter-type-relations-query-description": "{{entities}} ayant {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-required": "Le type de filtre est requis.", - "filter-type-single-entity": "Entité unique", - "filter-type-state-entity": "Entité de l'état du tableau de bord", - "filter-type-state-entity-description": "Entité extraite des paramétres d'état du tableau de bord", - "max-relation-level": "Niveau de relation maximum", - "name": "Nom de l'alias", - "name-required": "Le nom d'alias est requis", - "no-entity-filter-specified": "Aucun filtre d'entité spécifié", - "resolve-multiple": "Résoudre en plusieurs entités", - "root-entity": "Entité racine", - "root-state-entity": "Utiliser l'entité d'état du tableau de bord en tant que racine", - "state-entity": "Entité d'état du tableau de bord", - "state-entity-parameter-name": "Nom du paramétre d'entité d'état", - "unlimited-level": "Niveau illimité", - "filter-type-entity-type": "Type d'entité", - "filter-type-edge-type-and-name-description": "Edges de type '{{edgeTypes}}' et dont le nom commence par '{{prefix}}'", - "filter-type-apiUsageState": "État d'utilisation de l'API", - "last-level-relation": "Récupérer uniquement la relation de dernier niveau" - }, - "asset": { - "add": "Ajouter un actif", - "add-asset-text": "Ajouter un nouvel actif", - "any-asset": "Tout actif", - "asset": "Actif", - "asset-details": "Détails de l'actif", - "asset-file": "Fichier d'actif", - "asset-public": "L'actif est public", - "asset-required": "Actif requis", - "asset-type": "Type d'actif", - "asset-type-list-empty": "Aucun type d'actif sélectionné.", - "asset-type-required": "Le type d'actif est requis.", - "asset-types": "Types d'actif", - "assets": "Actifs", - "assign-asset-to-customer": "Attribuer des actifs au client", - "assign-asset-to-customer-text": "Veuillez sélectionner les actifs à attribuer au client", - "assign-assets": "Attribuer des actifs", - "assign-assets-text": "Attribuer {count, plural, =1 {1 asset} other {# assets} } au client", - "assign-new-asset": "Attribuer un nouvel Asset", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner le client pour attribuer le ou les actifs", - "assignedToCustomer": "attribué au client", - "copyId": "Copier l'Id de l'actif", - "delete": "Supprimer un actif", - "delete-asset-text": "Faites attention, après la confirmation, l'actif et toutes les données associées deviendront irrécupérables.", - "delete-asset-title": "Êtes-vous sûr de vouloir supprimer l'actif '{{assetName}}'?", - "delete-assets": "Supprimer des actifs", - "delete-assets-action-title": "Supprimer {count, plural, =1 {1 asset} other {# assets} }", - "delete-assets-text": "Attention, après la confirmation, tous les actifs sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-assets-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 asset} other {# assets} }?", - "description": "Description", - "details": "Détails", - "enter-asset-type": "Entrez le type d'actif", - "events": "Evénements", - "idCopiedMessage": "L'Id d'actif a été copié dans le presse-papier", - "import": "Importer des actifs", - "make-private": "Rendre l'actif privé", - "make-private-asset-text": "Après la confirmation, l'actif et toutes ses données seront rendus privés et ne seront pas accessibles par d'autres.", - "make-private-asset-title": "Êtes-vous sûr de vouloir rendre l'actif '{{assetName}}' privé '?", - "make-public": "Rendre l'actif public", - "make-public-asset-text": "Après la confirmation, l'asset et toutes ses données seront rendus publics et accessibles aux autres.", - "make-public-asset-title": "Êtes-vous sûr de vouloir rendre l'actif '{{assetName}}' public '?", - "management": "Gestion d'actifs", - "name": "Nom", - "name-required": "Nom est requis.", - "name-starts-with": "Le nom de l'actif commence par", - "no-asset-types-matching": "Aucun type d'actif correspondant à {{entitySubtype}} n'a été trouvé. ", - "no-assets-matching": "Aucun actif correspondant à {{entity}} n'a été trouvé. ", - "no-assets-text": "Aucun actif trouvé", - "public": "Public", - "select-asset": "Sélectionner un actif", - "select-asset-type": "Sélectionner le type d'actif", - "type": "Type", - "type-required": "Le type est requis.", - "unassign-asset": "Retirer l'actif", - "unassign-asset-text": "Après la confirmation, l'actif sera non attribué et ne sera pas accessible au client.", - "unassign-asset-title": "Êtes-vous sûr de vouloir retirer l'attribution de l'actif '{{assetName}}'?", - "unassign-assets": "Retirer les actifs", - "unassign-assets-action-title": "Retirer {count, plural, =1 {1 asset} other {# assets} } du client", - "unassign-assets-text": "Après la confirmation, tous les actifs sélectionnés ne seront pas attribués et ne seront pas accessibles au client.", - "unassign-assets-title": "Êtes-vous sûr de vouloir retirer l'attribution de {count, plural, =1 {1 asset} other {# assets} }?", - "unassign-from-customer": "Retirer du client", - "view-assets": "Afficher les actifs", - "label": "Étiquette (label)", - "assign-asset-to-edge": "Attribuer des actifs à Edge", - "assign-asset-to-edge-text": "Veuillez sélectionner les actifs à attribuer a la bordure", - "unassign-asset-from-edge": "Désattribuer l'actif", - "unassign-asset-from-edge-title": "Voulez-vous vraiment annuler l'attribution de l'actif '{{assetName}}'?", - "unassign-asset-from-edge-text": "Après la confirmation, l'actif sera désaffecté et ne sera pas accessible par le edge.", - "unassign-assets-from-edge-action-title": "Retirer {count, plural, =1 {1 asset} other {# assets} } de la bordure", - "unassign-assets-from-edge-title": "Êtes-vous sûr de vouloir désattribuer { count, plural, =1 {1 asset} other {# assets} }?", - "unassign-assets-from-edge-text": "Après la confirmation, tous les actifs sélectionnés seront désaffectés et ne seront pas accessibles par le edge.", - "asset-type-max-length": "Le type d'actif doit être inférieur à 256", - "name-max-length": "Le nom doit être inférieur à 256", - "label-max-length": "L'étiquette doit être inférieure à 256", - "help-text": "Use '%' selon besoin : '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", - "search": "Rechercher des actifs", - "selected-assets": "{ count, plural, =1 {1 asset} other {# assets} } sélectionnés" - }, - "attribute": { - "add": "Ajouter un attribut", - "add-to-dashboard": "Ajouter au tableau de bord", - "add-widget-to-dashboard": "Ajouter un widget au tableau de bord", - "attributes": "Attributs", - "attributes-scope": "Étendue des attributs d'entité", - "delete-attributes": "Supprimer les attributs", - "delete-attributes-text": "Attention, après la confirmation, tous les attributs sélectionnés seront supprimés.", - "delete-attributes-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 attribut} other {# attributs} }?", - "enter-attribute-value": "Entrez la valeur de l'attribut", - "key": "Clé", - "key-required": "La Clé d'attribut est requise.", - "last-update-time": "Dernière mise à jour", - "latest-telemetry": "Dernière télémétrie", - "next-widget": "Widget suivant", - "prev-widget": "Widget précédent", - "scope-client": "Attributs du client", - "scope-telemetry": "Télémétrie", - "scope-latest-telemetry": "Dernière télémétrie", - "scope-server": "Attributs du serveur", - "scope-shared": "Attributs partagés", - "selected-attributes": "{count, plural, =1 {1 attribut} other {# attributs} } sélectionnés", - "selected-telemetry": "{count, plural, =1 {1 unité de télémétrie} other {# unités de télémétrie} } sélectionnées", - "show-on-widget": "Afficher sur le widget", - "value": "Valeur", - "value-required": "La valeur d'attribut est obligatoire.", - "widget-mode": "Mode du widget", - "key-max-length": "La clé doit être inférieure à 256", - "no-attributes-text": "Aucun attribut trouvé", - "no-telemetry-text": "Aucune télémétrie trouvée" - }, - "api-usage": { - "api-usage": "Usage de l'Api", - "alarm": "Alarme", - "alarms-created": "Alarmes créées", - "alarms-created-daily-activity": "Activité hebdomadaire d'alarmes créées", - "alarms-created-hourly-activity": "Activité horaire d'alarmes créées", - "alarms-created-monthly-activity": "Activités mensuelle d'alarmes créées", - "data-points": "Données", - "data-points-storage-days": "Jours de storage des données", - "email": "Courriel", - "email-messages": "Messages courriel", - "email-messages-daily-activity": "Activité hebdomadaire de courriels", - "email-messages-monthly-activity": "Activité menuselle de courriels", - "executions": "Exécutions", - "javascript-executions": "Exécutions JavaScript", - "latest-error": "Dernière erreur", - "notifications-email-sms": "Notifications (Coourriel/SMS)", - "notifications-hourly-activity": "Activité horaire de notifications", - "permanent-failures": "${entityName} Échecs permanents", - "permanent-timeouts": "${entityName} Temps d'arrêt permanents", - "processing-failures": "${entityName} Erreurs d'exécution", - "processing-failures-and-timeouts": "Erreurs d'exécution et temps d'arrêt", - "processing-timeouts": "${entityName} Temps d'arrêt d'exécution", - "queue-stats": "Stats de queue", - "rule-chain": "Chaîne de règles", - "rule-engine": "Engin de règles", - "rule-engine-daily-activity": "Activité hebdomadaire de l'engin de règles", - "rule-engine-executions": "Exécutions de l'engin de règles", - "rule-engine-hourly-activity": "Activité horaire de l'engin de règles", - "rule-engine-monthly-activity": "Activité mensuelle de l'engin de règles", - "rule-engine-statistics": "Statistiques de l'engin de règles", - "rule-node": "Node de règle", - "sms-messages": "Messages texte", - "sms-messages-daily-activity": "Activité hebdomadaire de messages texte", - "sms-messages-monthly-activity": "Activité mensuelle de messages texte", - "successful": "${entityName} réussi", - "telemetry": "Télémétrie", - "telemetry-persistence": "Persistance de télémétrie", - "telemetry-persistence-daily-activity": "Activité hebdomadaire de persistance de télémétrie", - "telemetry-persistence-hourly-activity": "Activité horaire de persistance de télémétrie", - "telemetry-persistence-monthly-activity": "Activité mensuelle de persistance de télémétrie", - "transport-daily-activity": "Activité hebdomadaire de transport", - "transport-data-points": "Données de transport", - "transport-hourly-activity": "Activité horaire de transport", - "transport-messages": "Messages de transport", - "transport-monthly-activity": "Activité mensuelle de transport", - "view-details": "Voir détails", - "view-statistics": "Voir statistiques" - }, - "audit-log": { - "action-data": "Donnée d'action", - "audit": "Audit", - "audit-log-details": "Détails du journal d'audit", - "audit-logs": "Journaux d'audit", - "clear-search": "Effacer la recherche", - "details": "Détails", - "entity-name": "Nom de l'entité", - "entity-type": "Type d'entité", - "failure-details": "Détails de l'échec", - "no-audit-logs-prompt": "Aucun journal trouvé", - "search": "Rechercher les journaux d'audit", - "status": "État", - "status-failure": "Échec", - "status-success": "Succès", - "timestamp": "Horodatage", - "type": "Type", - "type-activated": "Activé", - "type-added": "Ajouté", - "type-alarm-ack": "Acquitté", - "type-alarm-clear": "Effacé", - "type-assigned-to-customer": "Attribué au client", - "type-assigned-to-edge": "Assigné au Edge", - "type-unassigned-from-edge": "Attribution retirée du Edge", - "type-attributes-deleted": "Attributs supprimés", - "type-attributes-read": "Attributs lus", - "type-attributes-updated": "Attributs mis à jour", - "type-credentials-read": "Lecture des informations d'identification", - "type-credentials-updated": "Informations d'identification actualisées", - "type-deleted": "Supprimé", - "type-login": "Connexion", - "type-logout": "Déconnexion", - "type-lockout": "Verrouillage", - "type-relation-add-or-update": "Relation mise à jour", - "type-relation-delete": "Relation supprimée", - "type-relations-delete": "Toutes les relations ont été supprimées", - "type-rpc-call": "Appel RPC", - "type-suspended": "Suspendu", - "type-unassigned-from-customer": "Attribution retirée du client", - "type-updated": "Mise à jour", - "user": "Utilisateur", - "type-assigned-from-tenant": "Assigné par le Tenant", - "type-assigned-to-tenant": "Assigné au Tenant", - "type-provision-success": "Dispositif mis en service", - "type-provision-failure": "La mise en service du dispositif a échoué", - "type-timeseries-updated": "Telemetrie mise à jour", - "type-timeseries-deleted": "Telemetrie supprimée" - }, - "common": { - "enter-password": "Entrez le mot de passe", - "enter-search": "Entrez la recherche", - "enter-username": "Entrez le nom d'utilisateur", - "password": "Mot de passe", - "username": "Nom d'utilisateur", - "created-time": "Heure de création", - "loading": "Chargement en cours...", - "proceed": "Procéder", - "open-details-page": "Ouvrir la page détails" - }, - "confirm-on-exit": { - "html-message": "Vous avez des modifications non enregistrées.
Êtes-vous sûr de vouloir quitter cette page?", - "message": "Vous avez des modifications non enregistrées. Êtes-vous sûr de vouloir quitter cette page?", - "title": "Modifications non enregistrées" - }, - "contact": { - "address": "Adresse", - "address2": "Adresse 2", - "city": "Ville", - "country": "Pays", - "email": "Courriel", - "no-address": "Pas d'adresse", - "phone": "Téléphone", - "postal-code": "Code postal", - "postal-code-invalid": "Format de code postal / code postal invalide", - "state": "Province", - "state-max-length": "La longueur de l'état doit être moins que 256", - "phone-max-length": "La longueur du téléphone doit être moins que 256", - "city-max-length": "La ville spécifiée doit être moins que 256" - }, - "content-type": { - "binary": "Binaire (Base64)", - "json": "Json", - "text": "Texte" - }, - "custom": { - "widget-action": { - "action-cell-button": "Bouton de cellule d'action", - "marker-click": "Sur le marqueur cliquez", - "row-click": "Au rang, cliquez", - "polygon-click": "Cliquez sur le polygone", - "tooltip-tag-action": "Action de balise d'info-bulle", - "node-selected": "Sur le noeud sélectionné", - "element-click": "Sur l'élément HTML, cliquez sur", - "pie-slice-click": "Sur tranche cliquez", - "row-double-click": "Sur la ligne double clic" + "credentials": { + "credentials": "Identifiants", + "api-key": "Clé API" } - }, - "customer": { - "add": "Ajouter un client", - "add-customer-text": "Ajouter un nouveau client", - "assets": "Actifs du client", - "copyId": "Copier l'id du client", - "customer": "Client", - "customer-details": "Détails du client", - "customer-required": "Le client est requis", - "customers": "Clients", - "dashboard": "Tableau de bord du client", - "dashboards": "tableaux de bord du client", - "default-customer": "Client par défaut", - "default-customer-required": "Le client par défaut est requis pour déboguer le tableau de bord au niveau du Tenant", - "delete": "Supprimer le client", - "delete-customer-text": "Faites attention, après la confirmation, le client et toutes les données associées deviendront irrécupérables.", - "delete-customer-title": "Êtes-vous sûr de vouloir supprimer le client '{{customerTitle}}'?", - "delete-customers-action-title": "Supprimer {count, plural, =1 {1 customer} other {# customers} }", - "delete-customers-text": "Faites attention, après la confirmation, tous les clients sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-customers-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 customer} other {# customers} }?", - "description": "Description", - "details": "Détails", - "devices": "Dispositifs du client", - "edges": "Instances Edge du client", - "entity-views": "Vues de l'entité client", - "events": "Événements", - "idCopiedMessage": "L'Id du client a été copié dans le presse-papier", - "manage-assets": "Gérer les actifs", - "manage-customer-assets": "Gérer les actifs du client", - "manage-customer-edges": "Gérer les bordures du client", - "manage-customer-dashboards": "Gérer les tableaux de bord du client", - "manage-customer-devices": "Gérer les dispositifs du client", - "manage-customer-users": "Gérer les utilisateurs du client", - "manage-dashboards": "Gérer les tableaux de bord", - "manage-devices": "Gérer les dispositifs", - "manage-public-assets": "Gérer les actifs publics", - "manage-public-dashboards": "Gérer les tableaux de bord publics", - "manage-public-devices": "Gérer les dispositifs publics", - "manage-public-edges": "Gérer les bordures publics", - "manage-users": "Gérer les utilisateurs", - "management": "Gestion des clients", - "no-customers-matching": "Aucun client correspondant à '{{entity}} n'a été trouvé.", - "no-customers-text": "Aucun client trouvé", - "public-assets": "Actifs publics", - "public-dashboards": "Tableaux de bord publics", - "public-devices": "Dispositifs publics", - "public-entity-views": "Vues d'entités publiques", - "public-edges": "Bordures publics", - "select-customer": "Sélectionner un client", - "select-default-customer": "Sélectionnez le client par défaut", - "title": "Titre", - "title-required": "Le titre est requis.", - "title-max-length": "La longueur du titre doit être moins que 256", - "search": "Rechercher clients", - "selected-customers": "{ count, plural, =1 {1 customer} other {# customers} } sélectionnés", - "manage-edges": "Gérer les edges" - }, - "dashboard": { - "add": "Ajouter un tableau de bord", - "add-dashboard-text": "Ajouter un nouveau tableau de bord", - "add-state": "Ajouter un état du tableau de bord", - "add-widget": "Ajouter un nouveau widget", - "alias-resolution-error-title": "Erreur de configuration des alias de tableau de bord", - "assign-dashboard-to-customer": "Attribuer des tableaux de bord au client", - "assign-dashboard-to-customer-text": "Veuillez sélectionner les tableaux de bord à attribuer au client", - "assign-dashboards": "Attribuer des tableaux de bord", - "assign-dashboards-text": "Attribuer {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } aux clients", - "assign-new-dashboard": "Attribuer un nouveau tableau de bord", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner le client pour attribuer le ou les tableaux de bord", - "assign-to-customers": "Attribuer des tableaux de bord aux clients", - "assign-to-customers-text": "Veuillez sélectionner les clients pour attribuer les tableaux de bord", - "assigned-customers": "clients attribués", - "assignedToCustomer": "Attribué au client", - "assignedToCustomers": "Attribué aux clients", - "autofill-height": "Hauteur de remplissage automatique", - "background-color": "Couleur de fond", - "background-image": "Image d'arriére-plan", - "background-size-mode": "Mode de taille d'arriére-plan", - "close-toolbar": "Fermer la barre d'outils", - "columns-count": "Nombre de colonnes", - "columns-count-required": "Le nombre de colonnes est requis.", - "configuration-error": "Erreur de configuration", - "copy-public-link": "Copier le lien public", - "create-new": "Créer un nouveau tableau de bord", - "create-new-dashboard": "Créer un nouveau tableau de bord", - "create-new-widget": "Créer un nouveau widget", - "dashboard": "Tableau de bord", - "dashboard-details": "Détails du tableau de bord", - "dashboard-file": "Fichier du tableau de bord", - "dashboard-import-missing-aliases-title": "Configurer les alias utilisés par le tableau de bord importé", - "dashboard-required": "Le tableau de bord est requis.", - "dashboards": "Tableaux de bord", - "delete": "Supprimer le tableau de bord", - "delete-dashboard-text": "Faites attention, après la confirmation, le tableau de bord et toutes les données associées deviendront irrécupérables.", - "delete-dashboard-title": "Êtes-vous sûr de vouloir supprimer le tableau de bord '{{dashboardTitle}}'?", - "delete-dashboards": "Supprimer les tableaux de bord", - "delete-dashboards-action-title": "Supprimer {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} }", - "delete-dashboards-text": "Attention, après la confirmation, tous les tableaux de bord sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-dashboards-title": "Voulez-vous vraiment supprimer {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} }?", - "delete-state": "Supprimer l'état du tableau de bord", - "delete-state-text": "Etes-vous sûr de vouloir supprimer l'état du tableau de bord avec le nom '{{stateName}}'?", - "delete-state-title": "Supprimer l'état du tableau de bord", - "description": "Description", - "details": "Détails", - "display-dashboard-export": "Afficher l'exportation", - "display-dashboard-timewindow": "Afficher la fenêtre de temps", - "display-dashboards-selection": "Afficher la sélection des tableaux de bord", - "display-entities-selection": "Afficher la sélection des entités", - "display-title": "Afficher le titre du tableau de bord", - "drop-image": "Déposer une image ou cliquez pour sélectionner un fichier à télécharger.", - "edit-state": "Modifier l'état du tableau de bord", - "export": "Exporter le tableau de bord", - "export-failed-error": "Impossible d'exporter le tableau de bord: {{error}}", - "hide-details": "Masquer les détails", - "horizontal-margin": "Marge horizontale", - "horizontal-margin-required": "Une valeur de marge horizontale est requise.", - "import": "Importer le tableau de bord", - "import-widget": "Importer un widget", - "invalid-aliases-config": "Impossible de trouver des dispositifs correspondant à certains filtres d'alias.
Veuillez contacter votre administrateur pour résoudre ce problème.", - "invalid-dashboard-file-error": "Impossible d'importer le tableau de bord: structure de données du tableau de bord non valide", - "invalid-widget-file-error": "Impossible d'importer le widget: structure de données de widget invalide.", - "is-root-state": "État racine", - "make-private": "Rendre privé le tableau de bord", - "make-private-dashboard": "Rendre privé le tableau de bord", - "make-private-dashboard-text": "Après la confirmation, le tableau de bord sera rendu privé et ne sera plus accessible aux autres.", - "make-private-dashboard-title": "Êtes-vous sûr de vouloir rendre le tableau de bord '{{dashboardTitle}}' privé?", - "make-public": "Rendre public le tableau de bord", - "manage-assigned-customers": "Gérer les clients attribués", - "manage-states": "Gérer les états du tableau de bord", - "management": "Gestion du tableau de bord", - "max-columns-count-message": "Seulement 1000 colonnes maximum sont autorisées.", - "max-horizontal-margin-message": "50 est la valeur de marge horizontale maximale.", - "max-mobile-row-height-message": "200 pixels est la valeur maximale de hauteur de ligne mobile.", - "max-vertical-margin-message": "50 est la valeur de marge verticale maximale.", - "min-columns-count-message": "Seul un nombre minimum de 10 colonnes est autorisé.", - "min-horizontal-margin-message": "0 est la valeur de marge horizontale minimale.", - "min-mobile-row-height-message": "5 pixels est la valeur minimale de hauteur de ligne mobile.", - "min-vertical-margin-message": "0 est la valeur de marge verticale minimale.", - "mobile-layout": "Paramètres de mise en page mobiles", - "mobile-row-height": "Hauteur de ligne mobile, px", - "mobile-row-height-required": "Une valeur de hauteur de ligne mobile est requise.", - "new-dashboard-title": "Nouveau titre du tableau de bord", - "no-dashboards-matching": "Aucun tableau de bord correspondant à {{entity}} n'a été trouvé. ", - "no-dashboards-text": "Aucun tableau de bord trouvé", - "no-image": "Aucune image sélectionnée", - "no-widgets": "Aucun widget configuré", - "open-dashboard": "Ouvrir le tableau de bord", - "open-toolbar": "Ouvrir la barre d'outils du tableau de bord", - "public": "Public", - "public-dashboard-notice": " Remarque: N'oubliez pas de rendre publics les dispositifs associés pour accéder à leurs données.", - "public-dashboard-text": "Votre tableau de bord {{dashboardTitle}} est maintenant public et accessible via le lien public : ", - "public-dashboard-title": "Le tableau de bord est maintenant public", - "public-link": "Lien public", - "public-link-copied-message": "Le lien public du tableau de bord a été copié dans le presse-papier", - "search-states": "Recherche des états du tableau de bord", - "select-dashboard": "Sélectionner le tableau de bord", - "select-devices": "Selectionner les dispositifs", - "select-existing": "Sélectionnez un tableau de bord existant", - "select-state": "Sélectionnez l'état cible", - "select-widget-subtitle": "Liste des types de widgets disponibles", - "select-widget-title": "Sélectionner un widget", - "selected-states": "{count, plural, =1 {1 état du tableau de bord} other {# états du tableau de bord} } sélectionnés", - "set-background": "Définir l'arrière-plan", - "settings": "Paramètres", - "show-details": "Afficher les détails", - "socialshare-text": "'{{dashboardTitle}}' propulsé par ThingsBoard", - "socialshare-title": "'{{dashboardTitle}}' propulsé par ThingsBoard", - "state": "État du tableau de bord", - "state-controller": "Contrôleur d'état", - "state-id": "ID d'état", - "state-id-exists": "L'état du tableau de bord avec le même Id existe déjà.", - "state-id-required": "L'Id d'état du tableau de bord est requis.", - "state-name": "Nom", - "state-name-required": "Le nom de l'état du tableau de bord est requis", - "states": "États du tableau de bord", - "title": "Titre", - "title-color": "Couleur du titre", - "title-required": "Le titre est requis.", - "toolbar-always-open": "Garder la barre d'outils ouverte", - "unassign-dashboard": "Retirer le tableau de bord", - "unassign-dashboard-text": "Après la confirmation, le tableau de bord ne sera pas attribué et ne sera pas accessible au client.", - "unassign-dashboard-title": "Êtes-vous sûr de vouloir annuler l'affectation du tableau de bord '{{dashboardTitle}}'?", - "unassign-dashboards": "Retirer les tableaux de bord", - "unassign-dashboards-action-text": "Annuler l'affectation {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } des clients", - "unassign-dashboards-action-title": "Annuler l'affectation {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} } du client", - "unassign-dashboards-text": "Après la confirmation, tous les tableaux de bord sélectionnés ne seront pas attribués et ne seront pas accessibles au client.", - "unassign-dashboards-title": "Etes-vous sûr de vouloir annuler l'affectation {count, plural, =1 {1 tableau de bord} other {# tableaux de bord} }?", - "unassign-from-customer": "Retirer du client", - "unassign-from-customers": "Retirer les tableaux de bord des clients", - "unassign-from-customers-text": "Veuillez sélectionner les clients à annuler l'attribution du ou des tableaux de bord", - "vertical-margin": "Marge verticale", - "vertical-margin-required": "Une valeur de marge verticale est requise", - "view-dashboards": "Afficher les tableaux de bord", - "widget-file": "Fichier du Widget", - "widget-import-missing-aliases-title": "Configurer les alias utilisés par le widget importé", - "widgets-margins": "Marge entre les widgets", - "unassign-dashboard-from-edge-text": "Après la confirmation, tableau de bord sera non attribué et ne sera pas accessible a la bordure.", - "unassign-dashboards-from-edge-text": "Après la confirmation, tous les tableaux de bord sélectionnés ne seront pas attribués et ne seront pas accessibles a la bordure.", - "assign-dashboard-to-edge": "Attribuer des tableaux de bord a la bordure", - "assign-dashboard-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les tableaux de bord", - "image": "Image du tableau de bord", - "mobile-app-settings": "paramètres de l'application mobile", - "mobile-order": "Ordre du tableau de bord dans l'application mobile", - "mobile-hide": "Cacher le tableau de bord dans l'application mobile", - "update-image": "Mettre à jour l'image du tableau de bord", - "take-screenshot": "Prendre une capture d'écran", - "select-widget-value": "{{title}}: sélectionner widget", - "title-max-length": "La longueur du titre doit être mpoins de 256", - "empty-image": "Aucune image", - "maximum-upload-file-size": "Taille de fichier maximum: {{ size }}", - "cannot-upload-file": "Le téléchargement a échoué", - "margin-required": "Valeur de marge requise.", - "min-margin-message": "0 est la valeur minimum permise.", - "max-margin-message": "50 est la valeur maximum permise.", - "title-settings": "Paramètres du titre", - "toolbar-settings": "Paramètres de la barre d'outils", - "hide-toolbar": "Masquer la barre d'outils", - "display-filters": "Afficher les filtres", - "display-update-dashboard-image": "Afficher l'image du tableau de bord de mise à jour", - "dashboard-logo-settings": "Paramètres du logo du tableau de bord", - "display-dashboard-logo": "Afficher le logo en mode plein écran", - "dashboard-logo-image": "Image du logo du tableau de bord", - "advanced-settings": "Paramètres avancés", - "dashboard-css": "CSS du tableau de bord", - "no-states-text": "Aucun état trouvé", - "search": "Rechercher des tableaux de bord", - "selected-dashboards": "{ count, plural, =1 {1 dashboard} other {# dashboards} } sélectionné", - "home-dashboard": "Tableau de bord d'accueil", - "home-dashboard-hide-toolbar": "Masquer la barre d'outils du tableau de bord d'accueil", - "unassign-dashboards-from-edge-title": "Êtes-vous certain de vouloir désattribuer { count, plural, =1 {1 dashboard} other {# dashboards} }?", - "non-existent-dashboard-state-error": "L'état du tableau de bord avec ID \"{{ stateId }}\" non trouvé" - }, - "datakey": { - "advanced": "Avancé", - "alarm": "Champs d'alarme", - "alarm-fields-required": "Les champs d'alarme sont obligatoires.", - "attributes": "Attributs", + }, + "overlays": { + "overlays": "Superpositions", + "overlays-hint": "Configurer les sources de données, l’apparence, le comportement, les options d’édition et le regroupement pour les entités cartographiques", + "trips": "Trajets", + "markers": "Marqueurs", + "polygons": "Polygones", + "circles": "Cercles" + }, + "data-layer": { + "source": "Source", + "filter": "Filtre", + "additional-data-keys": "Clés de données supplémentaires", + "additional-datasources": "Sources de données supplémentaires", + "additional-datasources-hint": "Source de données pour accéder aux attributs ou à la télémétrie d'entités non affichées sur la carte, utilisable dans les fonctions de superposition.", + "more-datasources": "Plus de sources de données", + "data-keys": "Clés de données", + "add-datasource": "Ajouter une source de données", + "no-datasources": "Aucune source de données configurée", + "remove-datasource": "Supprimer la source de données", + "behavior": "Comportement", + "on-click": "Au clic", + "on-click-hint": "Action déclenchée lors d’un clic sur l’élément de la carte.", + "groups": "Groupes", + "groups-hint": "Liste des noms de groupes attribués à la superposition, utilisée pour basculer sa visibilité sur la carte.", "color": "Couleur", - "configuration": "Configuration de la clé de données", - "data-generation-func": "Fonction de génération de données", - "decimals": "Nombre de chiffres après virgule flottante", - "function-types": "Types de fonctions", - "function-types-required": "Les types de fonctions sont obligatoires", - "label": "Label", - "maximum-function-types": "Maximum {count, plural, =1 {1 type de fonction est autorisé.} other {# types de fonctions sont autorisés} }", - "maximum-timeseries-or-attributes": "Maximum {count, plural, =1 {1 timeseries / attribut est autorisé.} other {# timeseries / attributs sont autorisés} }", - "prev-orig-value-description": "valeur précédente d'origine;", - "prev-value-description": "résultat de l'appel de fonction précédent;", - "settings": "Paramètres", - "time-description": "horodatage de la valeur actuelle;", - "time-prev-description": "horodatage de la valeur précédente;", - "timeseries": "Timeseries", - "timeseries-or-attributes-required": "Les timeseries / attributs d'entité sont obligatoires.", - "timeseries-required": "Les Timeseries de l'entité sont obligatoires.", - "units": "Symbole spécial à afficher à côté de la valeur", - "use-data-post-processing-func": "Utiliser la fonction de post-traitement des données", - "value-description": "la valeur actuelle;", - "entity-field": "Champs d'entité", - "alarm-fields-timeseries-or-attributes-required": "Les champs d'alarmes ou l'entité timeseries/attributs sont requis." - }, - "datasource": { - "add-datasource-prompt": "Veuillez ajouter une source de données", - "name": "Nom", - "type": "Type de source de données" - }, - "datetime": { - "date-from": "Date de", - "date-to": "Date à", - "time-from": "Heure de", - "time-to": "Heure à" - }, - "details": { - "edit-mode": "Mode édition", - "toggle-edit-mode": "Activer le mode édition", - "details": "Détails", - "edit-json": "Éditer JSON" - }, - "device": { - "access-token": "Jeton d'accès", - "access-token-invalid": "La longueur du jeton d'accès doit être comprise entre 1 et 32 caractéres.", - "access-token-required": "Le jeton d'accès est requis.", - "accessTokenCopiedMessage": "Le jeton d'accès au dispositif a été copié dans le presse-papier", - "add": "Ajouter un dispositif", - "add-alias": "Ajouter un alias de dispositif", - "add-device-text": "Ajouter un nouveau dispositif", - "alias": "Alias", - "alias-required": "Un alias du dispositif est requis.", - "aliases": "Alias des dispositifs", - "any-device": "N'importe quel dispositif", - "assign-device-to-customer": "Attribuer des dispositifs au client", - "assign-device-to-customer-text": "Veuillez sélectionner les dispositif à affecter au client", - "assign-devices": "Attribuer des dispositifs", - "assign-devices-text": "Attribuer {count, plural, =1 {1 dispositif} other {# dispositifs} } au client", - "assign-new-device": "Attribuer un nouveau dispositif", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner le client pour attribuer le ou les dispositifs", - "assignedToCustomer": "Attribué au client", - "configure-alias": "Configurer alias '{{alias}}'", - "copyAccessToken": "Copier le jeton d'accès", - "copyId": "Copier l'Id du dispositif", - "create-new-alias": "Créez un nouveau!", - "create-new-key": "Créez un nouveau!", - "credentials": "Informations d'identification", - "credentials-type": "Type d'identification", - "delete": "Supprimer le dispositif", - "delete-device-text": "Faites attention, après la confirmation, le dispositif et toutes les données associées deviendront irrécupérables.", - "delete-device-title": "Êtes-vous sûr de vouloir supprimer le dispositif '{{deviceName}}'?", - "delete-devices": "Supprimer les dispositifs", - "delete-devices-action-title": "Supprimer {count, plural, =1 {1 device} other {# devices} }", - "delete-devices-text": "Faites attention, après la confirmation, tous les dispositifs sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-devices-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 device} other {# devices} }?", - "description": "Description", - "details": "Détails", - "device": "Dispositif", - "device-alias": "Alias ​​du dispositif", - "device-credentials": "Informations d'identification du dispositif", - "device-details": "Détails du dispositif", - "device-list": "Liste des dispositifs", - "device-list-empty": "Aucun dispositif sélectionné.", - "device-name-filter-no-device-matched": "Aucun dispositif commençant par '{{device}} n'a été trouvé.", - "device-name-filter-required": "Le filtre de nom de dispositif est requis.", - "device-public": "Le dispositif est public", - "device-required": "Le dispositif est requis.", - "device-type": "Type de dispositif", - "device-type-list-empty": "Aucun type de dispositif sélectionné.", - "device-type-required": "Le type de dispositif est requis.", - "device-types": "Types de dispositif", - "devices": "Dispositifs", - "duplicate-alias-error": "Alias '{{alias}}' existe déjà.
Les alias de dispositifs doivent être uniques dans le tableau de bord.", - "enter-device-type": "Entrez le type de dispositif", - "events": "Événements", - "idCopiedMessage": "l'Id du dispositif a été copié dans le presse-papiers", - "is-gateway": "Est une passerelle", - "label": "Label", - "make-private": "Rendre le dispositif privé", - "make-private-device-text": "Après la confirmation, le dispositif et toutes ses données seront rendues privées et ne seront pas accessibles par d'autres.", - "make-private-device-title": "Êtes-vous sûr de vouloir rendre le dispositif {{deviceName}} privé?", - "make-public": "Rendre le dispositif public", - "make-public-device-text": "Après la confirmation, le dispositif et toutes ses données seront rendus publics et accessibles par d'autres.", - "make-public-device-title": "Êtes-vous sûr de vouloir rendre le dispositif {{deviceName}} 'public?", - "manage-credentials": "Gérer les informations d'identification", - "management": "Gestion des dispositifs", - "name": "Nom", - "name-required": "Le nom est requis.", - "name-starts-with": "Le nom du dispositif commence par", - "no-alias-matching": "'{{alias}}' introuvable.", - "no-aliases-found": "Aucun alias trouvé.", - "no-device-types-matching": "Aucun type de dispositif correspondant à {{entitySubtype}} n'a été trouvé.", - "no-devices-matching": "Aucun dispositif correspondant à '{{entity}} n'a été trouvé.", - "no-devices-text": "Aucun dispositif trouvé", - "no-key-matching": "'{{key}}' introuvable.", - "no-keys-found": "Aucune clé trouvée", - "public": "Public", - "remove-alias": "Supprimer l'alias du dispositif", - "secret": "Secret", - "secret-required": "Code secret est requis.", - "select-device": "Selectionner un dispositif", - "select-device-type": "Sélectionner le type d'appareil", - "unable-delete-device-alias-text": "L'alias du dispositif '{{deviceAlias}}' ne peut pas être supprimé car il est utilisé par les widgets suivants:
{{widgetsList}}", - "unable-delete-device-alias-title": "Impossible de supprimer l'alias du dispositif", - "unassign-device": "Annuler l'affectation du dispositif", - "unassign-device-text": "Après la confirmation, le dispositif ne sera pas attribué et ne sera pas accessible au client.", - "unassign-device-title": "Êtes-vous sûr de vouloir annuler l'affection du dispositif {{deviceName}} '?", - "unassign-devices": "Annuler l'affectation des dispositifs", - "unassign-devices-action-title": "Annuler l'affectation de {count, plural, =1 {1 device} other {#devices} } du client", - "unassign-devices-text": "Après la confirmation, tous les dispositifs sélectionnés ne seront pas attribues et ne seront pas accessibles par le client.", - "unassign-devices-title": "Voulez-vous vraiment annuler l'affectation de {count, plural, =1 {1 device} other {# devices} }?", - "unassign-from-customer": "Retirer du client", - "use-device-name-filter": "Utiliser le filtre", - "view-credentials": "Afficher les informations d'identification", - "view-devices": "Afficher les dispositifs", - "assign-device-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", - "unassign-device-from-edge-title": "Êtes-vous sûr de vouloir annuler l'affection du dispositif {{deviceName}} '?", - "unassign-device-from-edge-text": "Après la confirmation, dispositif sera non attribué et ne sera pas accessible a la bordure.", - "unassign-devices-from-edge-title": "Voulez-vous vraiment annuler l'attribution de {count, plural, =1 {1 device} other {# devices} }?", - "unassign-devices-from-edge-text": "Après la confirmation, tous les dispositifs sélectionnés ne seront pas attribues et ne seront pas accessibles par la bordure.", - "device-type-max-length": "La longueur du type de dispositif doit être moins de 256", - "help-text": "Utilisez '%' au besoin: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", - "unassign-devices-from-edge": "Désattribuer les dispositifs du edge", - "loading-device-credentials": "Chargement des informations d'identification du dispositf...", - "certificate-pem-format": "Certificat en format PEM", - "certificate-pem-format-required": "Certificat requis.", - "lwm2m-security-config": { - "identity": "Identité du client", - "identity-required": "Identité du client requise.", - "identity-tooltip": "L'identifiant PSK est un identifiant PSK arbitraire d'une longueur maximum de 128 octets, tel qu'indiqué au standard [RFC7925].\nL'identifiant PSK DOIT d'abord être converti en format texte puis encodé en octets utilisant la norme UTF-8.", - "client-key": "Clé du client", - "client-key-required": "Clé du client est requise.", - "client-key-tooltip-prk": "Clé publique RPK ou ID doivent être en format standard [RFC7250] et encodés en format Base64!", - "client-key-tooltip-psk": "Clé PSK doit être dans le standard [RFC4279] et en format HexDec: 32, 64, 128 caractères!", - "endpoint": "Nom du client Endpoint", - "endpoint-required": "Nom du client Endpoint est requis.", - "client-public-key": "Clé publique du client", - "client-public-key-hint": "Si la clé publique du client est vide, le certificat sera utilisé", - "client-public-key-tooltip": "La clé publique X509 doit être en format X509v3 DES-encodé et supporter exclusivement l'algorithme EC puis être encodé en format Base64!", - "mode": "Mode configuration de sécurité", - "client-tab": "Config de la sécutité du client", - "client-certificate": "Certificat du client", - "bootstrap-tab": "Client Bootstrap", - "bootstrap-server": "Serveur Bootstrap", - "lwm2m-server": "Serveur LwM2M", - "client-publicKey-or-id": "Clé publique du client ou ID", - "client-publicKey-or-id-required": "Clé publique du client ou ID requise", - "client-publicKey-or-id-tooltip-psk": "L'identifiant PSK est un identifiant PSK arbitraire d'une longueur maximum de 128 octets, tel qu'indiqué au standard [RFC7925].\nL'identifiant PSK DOIT d'abord être converti en format texte puis encodé en octets utilisant la norme UTF-8.", - "client-publicKey-or-id-tooltip-rpk": "Clé publique RPK ou ID doivent être en format standard [RFC7250] et encodés en format Base64!", - "client-publicKey-or-id-tooltip-x509": "La clé publique X509 doit être en format X509v3 DES-encodé et supporter exclusivement l'algorithme EC puis être encodé en format Base64!", - "client-secret-key": "Clé secrète du client", - "client-secret-key-required": "Clé secrète du client est requise.", - "client-secret-key-tooltip-psk": "La clé PSK doit être en format stadard [RFC4279] et en format HexDec: 32, 64, 128 caractères!", - "client-secret-key-tooltip-prk": "La clé secrète RPK doit être en format PKCS_8 (DER encodée, standard [RFC5958]) puis encodée en format Base64!", - "client-secret-key-tooltip-x509": "La clé secrète X509 doit être en format PKCS_8 (DER encodée, standard [RFC5958]) puis encodée en format Base64!" + "color-settings": "Paramètres de couleur", + "color-type-constant": "Constante", + "color-type-range": "Plage", + "color-type-function": "Fonction", + "color-range-source-key": "Clé source de la plage de couleurs", + "color-range-source-key-required": "La clé source de la plage de couleurs est requise", + "color-range": "Plage de couleurs", + "color-function": "Fonction de couleur", + "label": "Étiquette", + "tooltip": "Infobulle", + "pattern-type-pattern": "Motif", + "pattern-type-function": "Fonction", + "label-pattern": "Étiquette (exemples de motifs : '${entityName}', '${entityName}: (Texte ${keyName} unités.)' )", + "label-function": "Fonction d’étiquette", + "tooltip-pattern": "Infobulle (ex. 'Texte ${keyName} unités.' ou Texte du lien)", + "tooltip-function": "Fonction d’infobulle", + "tooltip-trigger": "Déclencheur d’infobulle", + "tooltip-trigger-click": "Afficher l’infobulle au clic", + "tooltip-trigger-hover": "Afficher l’infobulle au survol", + "auto-close-tooltips": "Fermeture automatique des infobulles", + "tooltip-offset": "Décalage de l’infobulle", + "tooltip-offset-horizontal": "Horizontal", + "tooltip-offset-vertical": "Vertical", + "tooltip-tag-actions": "Actions de balise", + "add-tooltip-tag-action": "Ajouter une action de balise", + "edit-tooltip-tag-action": "Modifier l’action de balise", + "remove-tooltip-tag-action": "Supprimer l’action de balise", + "action-add": "Ajouter", + "action-edit": "Modifier", + "action-move": "Déplacer", + "action-remove": "Supprimer", + "edit-instruments": "Outils d’édition", + "persist-location-attribute-scope": "Portée de l’attribut pour la persistance de la position", + "enable-snapping": "Activer l’alignement sur d’autres sommets pour un dessin précis", + "enable-snapping-hint": "Aligne automatiquement les nouveaux points avec les formes existantes pour faciliter le dessin.", + "drag-drop-mode": "Mode glisser-déposer", + "trip": { + "no-trips": "Aucun trajet configuré", + "add-trip": "Ajouter un trajet", + "trip-configuration": "Configuration du trajet", + "remove-trip": "Supprimer le trajet" }, - "client-id": "ID client", - "client-id-pattern": "Contient des caractères invalides.", - "user-name": "Nom d'utilisateur", - "user-name-required": "Nom d'utilisateur requis.", - "client-id-or-user-name-necessary": "ID client et/ou identifiant sont requis", - "password": "Mot de passe", - "name-max-length": "La longueur du nom doit être moins de 256", - "label-max-length": "La longueur du Label doit être moins de 256", - "copy-mqtt-authentication": "Copier les authentifiants MQTT", - "mqtt-authentication-copied-message": "L'authentifiant MQTT du dispositif a été copié au presse-papier", - "overwrite-activity-time": "Passer par dessus le temps de l'activité pour dispositif connecté", - "import": "Importer dispositif", - "device-file": "Fichier du dispositif", - "search": "Rechercher des dispositifs", - "selected-devices": "{ count, plural, =1 {1 device} other {# devices} } sélectionnés", - "device-configuration": "Configuration du dipositif", - "transport-configuration": "Configuration du transport", - "wizard": { - "device-details": "Détails du dispositif" - } + "marker": { + "marker": "Marqueur", + "latitude-key": "Clé de latitude", + "longitude-key": "Clé de longitude", + "x-pos-key": "Clé de position X", + "y-pos-key": "Clé de position Y", + "latitude-key-required": "Clé de latitude requise", + "longitude-key-required": "Clé de longitude requise", + "x-pos-key-required": "Clé de position X requise", + "y-pos-key-required": "Clé de position Y requise", + "no-markers": "Aucun marqueur configuré", + "add-marker": "Ajouter un marqueur", + "marker-configuration": "Configuration du marqueur", + "remove-marker": "Supprimer le marqueur", + "marker-type": "Type de marqueur", + "marker-type-shape": "Forme", + "marker-type-icon": "Icône", + "marker-type-image": "Image", + "shape": "Forme", + "icon": "Icône", + "image": "Image", + "marker-shapes": "Formes de marqueur", + "marker-icon": "Icône du marqueur", + "marker-appearance": "Apparence du marqueur", + "marker-image": "Image du marqueur", + "marker-image-type-image": "Image", + "marker-image-type-function": "Fonction", + "custom-marker-image-size": "Taille personnalisée de l’image du marqueur", + "marker-image-function": "Fonction d’image du marqueur", + "marker-images": "Images du marqueur", + "marker-offset": "Décalage du marqueur", + "offset-horizontal": "Horizontal", + "offset-vertical": "Vertical", + "rotate-marker": "Faire pivoter le marqueur", + "offset-angle": "Angle de décalage", + "position-conversion": "Conversion de position", + "position-conversion-function": "Fonction de conversion de position, doit renvoyer les coordonnées x, y entre 0 et 1", + "clustering": { + "use-map-markers-clustering": "Utiliser le regroupement des marqueurs sur la carte", + "zoom-on-cluster-click": "Zoom lors du clic sur un cluster", + "max-zoom": "Niveau de zoom maximal pour faire partie d’un cluster (0 - 18)", + "max-radius": "Rayon maximal couvert par un cluster", + "zoom-animation": "Animation des marqueurs lors du zoom", + "bounds-on-cluster-mouse-over": "Délimitation des marqueurs lors du survol du cluster", + "spiderfy-max-zoom-level": "Déplier au niveau de zoom max (afficher tous les marqueurs du cluster)", + "load-optimization": "Optimisation du chargement", + "chunked-load": "Utiliser un chargement en morceaux pour éviter le gel de la page", + "lazy-load": "Utiliser un chargement différé des marqueurs", + "use-cluster-marker-color-function": "Utiliser une fonction de couleur pour les clusters", + "marker-color-function": "Fonction de couleur du marqueur" + }, + "edit": "Modifier le marqueur", + "remove-marker-for": "Supprimer le marqueur pour '{{entityName}}'", + "place-marker": "Placer un marqueur", + "place-marker-hint": "Cliquez pour placer un marqueur", + "place-marker-hint-with-entity": "Cliquez pour placer l’entité '{{entityName}}'" + }, + "path": { + "path": "Chemin", + "path-decorator": "Décorateur de chemin", + "decorator-symbol": "Symbole décoratif", + "decorator-symbol-arrow-head": "Flèche", + "decorator-symbol-dash": "Tiret", + "decorator-arrangement": "Disposition du décorateur", + "decorator-offset": "Début", + "decorator-end-offset": "Fin", + "decorator-repeat": "Répétition" + }, + "points": { + "points": "Points", + "point-tooltip": "Infobulle du point" + }, + "shape": { + "fill": "Remplissage", + "fill-type-color": "Couleur", + "fill-type-stripe": "Rayure", + "fill-type-image": "Image", + "color": "Couleur", + "stripe": "Rayure", + "image": "Image", + "stroke": "Contour", + "fill-image": "Image de remplissage", + "fill-image-type-image": "Image", + "fill-image-type-function": "Fonction", + "preserve-aspect-ratio": "Préserver le ratio", + "opacity": "Opacité", + "angle": "Angle de rotation", + "scale": "Échelle", + "fill-image-function": "Fonction d’image de remplissage", + "fill-images": "Images de remplissage", + "stripe-pattern": "Motif de rayure", + "first-stripe": "Première rayure", + "second-stripe": "Deuxième rayure" + }, + "polygon": { + "polygon-key": "Clé du polygone", + "polygon-key-required": "Clé du polygone requise", + "no-polygons": "Aucun polygone configuré", + "add-polygon": "Ajouter un polygone", + "polygon-configuration": "Configuration du polygone", + "remove-polygon": "Supprimer le polygone", + "edit": "Modifier le polygone", + "remove-polygon-for": "Supprimer le polygone pour '{{entityName}}'", + "cut": "Découper la zone du polygone", + "rotate": "Faire pivoter le polygone", + "draw-rectangle": "Dessiner un rectangle", + "draw-polygon": "Dessiner un polygone", + "polygon-place-first-point-cut-hint": "Cliquez pour placer le premier point", + "continue-polygon-cut-hint": "Cliquez pour continuer à dessiner", + "finish-polygon-cut-hint": "Cliquez sur le premier marqueur pour terminer et enregistrer", + "polygon-place-first-point-hint": "Polygone : cliquez pour placer le premier point", + "polygon-place-first-point-hint-with-entity": "Polygone pour '{{entityName}}' : cliquez pour placer le premier point", + "continue-polygon-hint": "Polygone : cliquez pour continuer à dessiner", + "continue-polygon-hint-with-entity": "Polygone pour '{{entityName}}' : cliquez pour continuer à dessiner", + "finish-polygon-hint": "Polygone : cliquez sur le premier marqueur pour terminer le dessin", + "finish-polygon-hint-with-entity": "Polygone pour '{{entityName}}' : cliquez sur le premier marqueur pour terminer et enregistrer", + "rectangle-place-first-point-hint": "Rectangle : cliquez pour placer le premier point", + "rectangle-place-first-point-hint-with-entity": "Rectangle pour '{{entityName}}' : cliquez pour placer le premier point", + "finish-rectangle-hint": "Rectangle : cliquez pour terminer le dessin", + "finish-rectangle-hint-with-entity": "Rectangle pour '{{entityName}}' : cliquez pour terminer et enregistrer" + }, + "circle": { + "circle-key": "Clé du cercle", + "circle-key-required": "Clé du cercle requise", + "no-circles": "Aucun cercle configuré", + "add-circle": "Ajouter un cercle", + "circle-configuration": "Configuration du cercle", + "remove-circle": "Supprimer le cercle", + "edit": "Modifier le cercle", + "remove-circle-for": "Supprimer le cercle pour '{{entityName}}'", + "draw-circle": "Dessiner un cercle", + "place-circle-center-hint-with-entity": "Cercle pour '{{entityName}}' : cliquez pour placer le centre", + "place-circle-center-hint": "Cercle : cliquez pour placer le centre", + "finish-circle-hint-with-entity": "Cercle pour '{{entityName}}' : cliquez pour terminer et enregistrer le cercle", + "finish-circle-hint": "Cercle : cliquez pour terminer le dessin" + }, + "select-entity": "Sélectionner une entité", + "select-entity-hint": "Astuce : après sélection, cliquez sur la carte pour définir la position" + }, + "select-entity": "Sélectionner une entité", + "select-entity-hint": "Astuce : après la sélection, cliquez sur la carte pour définir la position", + "tooltips": { + "placeMarker": "Cliquez pour placer l’entité '{{entityName}}'", + "firstVertex": "Polygone pour '{{entityName}}' : cliquez pour placer le premier point", + "firstVertex-cut": "Cliquez pour placer le premier point", + "continueLine": "Polygone pour '{{entityName}}' : cliquez pour continuer à dessiner", + "continueLine-cut": "Cliquez pour continuer à dessiner", + "finishLine": "Cliquez sur un marqueur existant pour terminer", + "finishPoly": "Polygone pour '{{entityName}}' : cliquez sur le premier marqueur pour terminer et enregistrer", + "finishPoly-cut": "Cliquez sur le premier marqueur pour terminer et enregistrer", + "finishRect": "Polygone pour '{{entityName}}' : cliquez pour terminer et enregistrer", + "startCircle": "Cercle pour '{{entityName}}' : cliquez pour placer le centre", + "finishCircle": "Cercle pour '{{entityName}}' : cliquez pour terminer le cercle", + "placeCircleMarker": "Cliquez pour placer le marqueur de cercle" + }, + "actions": { + "finish": "Terminer", + "cancel": "Annuler", + "removeLastVertex": "Supprimer le dernier point" + }, + "buttonTitles": { + "drawMarkerButton": "Placer une entité", + "drawPolyButton": "Créer un polygone", + "drawLineButton": "Créer une ligne", + "drawCircleButton": "Créer un cercle", + "drawRectButton": "Créer un rectangle", + "editButton": "Mode édition", + "dragButton": "Mode glisser-déposer", + "cutButton": "Découper la zone du polygone", + "deleteButton": "Supprimer", + "drawCircleMarkerButton": "Créer un marqueur circulaire", + "rotateButton": "Faire pivoter le polygone" + }, + "map-provider-settings": "Paramètres du fournisseur de cartes", + "map-provider": "Fournisseur de cartes", + "map-provider-google": "Google Maps", + "map-provider-openstreet": "OpenStreet Maps", + "map-provider-here": "HERE Maps", + "map-provider-image": "Carte image", + "map-provider-tencent": "Tencent Maps", + "openstreet-provider": "Fournisseur de cartes OpenStreet", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Par défaut)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery": "Esri.WorldImagery", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Utiliser un fournisseur personnalisé", + "custom-provider-tile-url": "URL des tuiles du fournisseur personnalisé", + "google-maps-api-key": "Clé API Google Maps", + "default-map-type": "Type de carte par défaut", + "google-map-type-roadmap": "Plan", + "google-map-type-satelite": "Satellite", + "google-map-type-hybrid": "Hybride", + "google-map-type-terrain": "Relief", + "map-layer": "Couche de carte", + "here-map-normal-day": "HERE.normalDay (Par défaut)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "Identifiants", + "here-app-id": "ID de l'application HERE", + "here-app-code": "Code de l'application HERE", + "here-api-key": "Clé API HERE", + "here-use-new-version-api-3": "Utiliser l'API version 3", + "tencent-maps-api-key": "Clé API Tencent Maps", + "tencent-map-type-roadmap": "Plan", + "tencent-map-type-satelite": "Satellite", + "tencent-map-type-hybrid": "Hybride", + "image-map-background": "Fond de carte image", + "image-map-background-from-entity-attribute": "Utiliser l'attribut d'entité comme fond de carte image", + "image-url-source-entity-alias": "Alias d'entité source de l'URL de l'image", + "image-url-source-entity-attribute": "Attribut d'entité source de l'URL de l'image", + "common-map-settings": "Paramètres communs de la carte", + "x-pos-key-name": "Nom de la clé de position X", + "y-pos-key-name": "Nom de la clé de position Y", + "latitude-key-name": "Nom de la clé de latitude", + "longitude-key-name": "Nom de la clé de longitude", + "default-map-zoom-level": "Niveau de zoom par défaut (0 - 20)", + "default-map-center-position": "Position centrale de la carte par défaut (0,0)", + "disable-scroll-zooming": "Désactiver le zoom à la molette", + "disable-double-click-zooming": "Désactiver le zoom au double-clic", + "disable-zoom-control-buttons": "Désactiver les boutons de zoom", + "fit-map-bounds": "Ajuster les limites de la carte pour couvrir tous les marqueurs", + "use-default-map-center-position": "Utiliser la position centrale par défaut de la carte", + "entities-limit": "Limite d'entités à charger", + "markers-settings": "Paramètres des marqueurs", + "marker-offset-x": "Décalage X du marqueur par rapport à la position, multiplié par la largeur du marqueur", + "marker-offset-y": "Décalage Y du marqueur par rapport à la position, multiplié par la hauteur du marqueur", + "position-function": "Fonction de conversion de position, doit retourner les coordonnées x,y entre 0 et 1", + "draggable-marker": "Marqueur déplaçable", + "label": "Étiquette", + "show-label": "Afficher l'étiquette", + "use-label-function": "Utiliser une fonction d'étiquette", + "label-pattern": "Étiquette (exemples de modèles : '${entityName}', '${entityName}: (Texte ${keyName} unités.)')", + "label-function": "Fonction d'étiquette", + "tooltip": "Info-bulle", + "show-tooltip": "Afficher l'info-bulle", + "show-tooltip-action": "Action d'affichage de l'info-bulle", + "show-tooltip-action-click": "Afficher l'info-bulle au clic (Par défaut)", + "show-tooltip-action-hover": "Afficher l'info-bulle au survol", + "auto-close-tooltips": "Fermeture automatique des info-bulles", + "use-tooltip-function": "Utiliser une fonction d'info-bulle", + "tooltip-pattern": "Info-bulle (ex. : 'Texte ${keyName} unités.' ou Texte du lien)", + "tooltip-function": "Fonction d'info-bulle", + "tooltip-offset-x": "Décalage X de l'info-bulle par rapport à l'ancrage du marqueur, multiplié par la largeur du marqueur", + "tooltip-offset-y": "Décalage Y de l'info-bulle par rapport à l'ancrage du marqueur, multiplié par la hauteur du marqueur", + "color": "Couleur", + "use-color-function": "Utiliser une fonction de couleur", + "color-function": "Fonction de couleur", + "marker-image": "Image du marqueur", + "use-marker-image-function": "Utiliser une fonction d'image du marqueur", + "custom-marker-image": "Image personnalisée du marqueur", + "custom-marker-image-size": "Taille de l'image personnalisée du marqueur (px)", + "marker-image-function": "Fonction d'image du marqueur", + "marker-images": "Images du marqueur", + "polygon-settings": "Paramètres du polygone", + "show-polygon": "Afficher le polygone", + "polygon-key-name": "Nom de la clé du polygone", + "enable-polygon-edit": "Activer la modification du polygone", + "polygon-label": "Étiquette du polygone", + "show-polygon-label": "Afficher l'étiquette du polygone", + "use-polygon-label-function": "Utiliser une fonction pour l'étiquette du polygone", + "polygon-label-pattern": "Étiquette du polygone (exemples de modèles : '${entityName}', '${entityName}: (Texte ${keyName} unités.)')", + "polygon-label-function": "Fonction de l'étiquette du polygone", + "polygon-tooltip": "Info-bulle du polygone", + "show-polygon-tooltip": "Afficher l'info-bulle du polygone", + "auto-close-polygon-tooltips": "Fermeture automatique des info-bulles du polygone", + "use-polygon-tooltip-function": "Utiliser une fonction pour l'info-bulle du polygone", + "polygon-tooltip-pattern": "Info-bulle (ex. : 'Texte ${keyName} unités.' ou Texte du lien)", + "polygon-tooltip-function": "Fonction d'info-bulle du polygone", + "polygon-color": "Couleur du polygone", + "polygon-opacity": "Opacité du polygone", + "use-polygon-color-function": "Utiliser une fonction pour la couleur du polygone", + "polygon-color-function": "Fonction de couleur du polygone", + "polygon-stroke": "Contour du polygone", + "stroke-color": "Couleur du contour", + "stroke-opacity": "Opacité du contour", + "stroke-weight": "Épaisseur du contour", + "use-polygon-stroke-color-function": "Utiliser une fonction pour la couleur du contour", + "polygon-stroke-color-function": "Fonction de couleur du contour du polygone", + "circle-settings": "Paramètres du cercle", + "show-circle": "Afficher le cercle", + "circle-key-name": "Nom de la clé du cercle", + "enable-circle-edit": "Activer la modification du cercle", + "circle-label": "Étiquette du cercle", + "show-circle-label": "Afficher l'étiquette du cercle", + "use-circle-label-function": "Utiliser une fonction pour l'étiquette du cercle", + "circle-label-pattern": "Étiquette du cercle (exemples de modèles : '${entityName}', '${entityName}: (Texte ${keyName} unités.)')", + "circle-label-function": "Fonction de l'étiquette du cercle", + "circle-tooltip": "Info-bulle du cercle", + "show-circle-tooltip": "Afficher l'info-bulle du cercle", + "auto-close-circle-tooltips": "Fermeture automatique des info-bulles du cercle", + "use-circle-tooltip-function": "Utiliser une fonction pour l'info-bulle du cercle", + "circle-tooltip-pattern": "Info-bulle (ex. : 'Texte ${keyName} unités.' ou Texte du lien)", + "circle-tooltip-function": "Fonction d'info-bulle du cercle", + "circle-fill-color": "Couleur de remplissage du cercle", + "circle-fill-color-opacity": "Opacité de la couleur de remplissage du cercle", + "use-circle-fill-color-function": "Utiliser une fonction pour la couleur de remplissage", + "circle-fill-color-function": "Fonction de couleur de remplissage du cercle", + "circle-stroke": "Contour du cercle", + "use-circle-stroke-color-function": "Utiliser une fonction pour la couleur du contour", + "circle-stroke-color-function": "Fonction de couleur du contour du cercle", + "markers-clustering-settings": "Paramètres de regroupement des marqueurs", + "use-map-markers-clustering": "Utiliser le regroupement des marqueurs sur la carte", + "zoom-on-cluster-click": "Zoomer lors du clic sur un cluster", + "max-cluster-zoom": "Niveau de zoom maximum pour inclure un marqueur dans un cluster (0 - 18)", + "max-cluster-radius-pixels": "Rayon maximal d’un cluster en pixels", + "cluster-zoom-animation": "Afficher une animation lors du zoom sur les marqueurs", + "show-markers-bounds-on-cluster-mouse-over": "Afficher les limites des marqueurs au survol d’un cluster", + "spiderfy-max-zoom-level": "Affichage de type araignée au niveau de zoom max (pour voir tous les marqueurs du cluster)", + "load-optimization": "Optimisation du chargement", + "cluster-chunked-loading": "Utiliser des lots pour l’ajout de marqueurs afin d’éviter le gel de la page", + "cluster-markers-lazy-load": "Utiliser le chargement paresseux pour l’ajout de marqueurs", + "editor-settings": "Paramètres de l’éditeur", + "enable-snapping": "Activer l'accrochage aux autres sommets pour un dessin précis", + "init-draggable-mode": "Initialiser la carte en mode déplaçable", + "hide-all-edit-buttons": "Masquer tous les boutons de contrôle d’édition", + "hide-draw-buttons": "Masquer les boutons de dessin", + "hide-edit-buttons": "Masquer les boutons d’édition", + "hide-remove-button": "Masquer le bouton de suppression", + "route-map-settings": "Paramètres de la carte des trajets", + "trip-animation-settings": "Paramètres de l’animation des trajets", + "normalization-step": "Étape de normalisation des données (ms)", + "tooltip-background-color": "Couleur de fond de l'info-bulle", + "tooltip-font-color": "Couleur de la police de l’info-bulle", + "tooltip-opacity": "Opacité de l’info-bulle (0-1)", + "auto-close-tooltip": "Fermeture automatique de l’info-bulle", + "rotation-angle": "Définir un angle de rotation supplémentaire pour le marqueur (degrés)", + "path-settings": "Paramètres du chemin", + "path-color": "Couleur du chemin", + "use-path-color-function": "Utiliser une fonction de couleur pour le chemin", + "path-color-function": "Fonction de couleur du chemin", + "path-decorator": "Décorateur de chemin", + "use-path-decorator": "Utiliser un décorateur de chemin", + "decorator-symbol": "Symbole du décorateur", + "decorator-symbol-arrow-head": "Flèche", + "decorator-symbol-dash": "Tiret", + "decorator-symbol-size": "Taille du symbole du décorateur (px)", + "use-path-decorator-custom-color": "Utiliser une couleur personnalisée pour le décorateur", + "decorator-custom-color": "Couleur personnalisée du décorateur", + "decorator-offset": "Décalage du décorateur", + "end-decorator-offset": "Décalage de fin du décorateur", + "decorator-repeat": "Répétition du décorateur", + "points-settings": "Paramètres des points", + "show-points": "Afficher les points", + "point-color": "Couleur du point", + "point-size": "Taille du point (px)", + "use-point-color-function": "Utiliser une fonction de couleur pour les points", + "point-color-function": "Fonction de couleur des points", + "use-point-as-anchor": "Utiliser le point comme ancre", + "point-as-anchor-function": "Fonction de point comme ancre", + "independent-point-tooltip": "Info-bulle de point indépendante", + "clustering-markers": "Regroupement des marqueurs", + "use-icon-create-function": "Utiliser une fonction de couleur pour les marqueurs", + "marker-color-function": "Fonction de couleur du marqueur" }, - "device-profile": { - "device-profile": "Profile du dispositif", - "device-profiles": "Profiles du dispositif", - "all-device-profiles": "Tous", - "add": "Ajouter un nouveau profile de dispositif", - "edit": "Modifier le profile de dispositif", - "device-profile-details": "Détails du profile de dispositif", - "no-device-profiles-text": "Aucun profil de dispositif trouvé", - "search": "Rechercher profil de dispositif", - "selected-device-profiles": "{ count, plural, =1 {1 device profile} other {# device profiles} } sélectionné", - "no-device-profiles-matching": "Aucun dispositif correspondant à '{{entity}}' trouvé.", - "device-profile-required": "Un profil de dispositif est requis.", - "idCopiedMessage": "L'Id du profil de dispositif a été copié au presse-papier", - "set-default": "Rendre le profil de dispositif par défaut", - "delete": "Supprimer le profil de dispositif", - "copyId": "Copier l'Identifiant du profil de dispositif", - "name-max-length": "La longueur du nom devrait être moins de 256", - "name": "Nom", - "name-required": "Nom est requis.", - "type": "Type de profile", - "type-required": "Type de profile est requis.", - "type-default": "Défault", - "image": "Image du profile de dispositif", - "transport-type": "Type de transport", - "transport-type-required": "Type de transport est requis.", - "transport-type-default": "Défault", - "transport-type-default-hint": "Supporte comme transport MQTT de base, HTTP et CoAP", - "transport-type-mqtt-hint": "Active les configurations MQTT avancées", - "transport-type-coap-hint": "Active les configurations CoAP avancées", - "transport-type-lwm2m-hint": "Type de transport LWM2M", - "transport-type-snmp-hint": "Spécifiez la configuration du transport SNMP", - "default": "Défault", - "profile-configuration": "Configuration du profile", - "transport-configuration": "Configuration du transport", - "default-rule-chain": "Chaine de règles par défaut", - "mobile-dashboard": "Tableau de bord mobile", - "mobile-dashboard-hint": "Utilisé par les applications mobiles comme tableau de bord de détails de dispositifs.", - "select-queue-hint": "Choisissez d'une liste déroulante.", - "delete-device-profile-title": "Êtes-vous certain de vouloir supprimer le profile de dispositif '{{deviceProfileName}}'?", - "delete-device-profile-text": "Attention, après confirmation le profil du dispositif et toutes les données associées deviendront irrécupérables.", - "delete-device-profiles-title": "Êtes-vous certainde vouloir supprimer { count, plural, =1 {1 device profile} other {# device profiles} }?", - "delete-device-profiles-text": "Attention, après confirmation les profils des dispositifs sélectionnés et toutes les données associées deviendront irrécupérables.", - "set-default-device-profile-title": "Êtes-vous certain de vouloir faire du profil de dispositif '{{deviceProfileName}}' le profil par défaut?", - "set-default-device-profile-text": "Après confirmation, le profil de dispositif sera le profil par défaut et sera utilisé pour les nouveaux dispositifs n'ayant pas de profil.", - "no-device-profiles-found": "Aucun profil de dispositif trouvé.", - "create-new-device-profile": "En créer un nouveau!", - "mqtt-device-topic-filters": "Filtres de sujets de dispositifs MQTT", - "mqtt-device-topic-filters-unique": "Filtres de sujets de dispositifs MQTT doivent être uniques. ", - "mqtt-device-payload-type": "Payload de dispositif MQTT", - "mqtt-enable-compatibility-with-json-payload-format": "Activer la compatibilité avec d'autres formats de payloads.", - "mqtt-enable-compatibility-with-json-payload-format-hint": "Lorsqu'activé, la plateforme utilisera un format de payload Protobuf par défaut. Si le parsing échoue, la pateforme tentera d'utiliser le format JSON. Utile pour compatibilité avec version antérieures lors de mises à jour du firmware. Par exemple, la version originale du firmware utilisait JSON, alors que la nouvelle version du firmware utilise Protobuf. Durant le processus de mise à jour du firmware pour une flotte de dispositifs, il est requis de supporter à la fois JSON et Protobuf simultanément. Ce mode de compatibilité est légèrement moins performant alors il est recommandé de le désactivé une fois tous les dispositifs mis à jour.", - "mqtt-use-json-format-for-default-downlink-topics": "Utilisez le format JSON pouir les sujets de downlink par défaut", - "mqtt-use-json-format-for-default-downlink-topics-hint": "Lorsqu'activé, la plateforme utilisera le format de payload JSON pour pousser des attributs et des RPC via les sujets suivants: v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, v1/devices/me/rpc/request/$request_id, v1/devices/me/rpc/response/$request_id. This setting does not impact attribute and rpc subscriptions sent using new (v2) topics: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id. Où $request_id est un identifiant de requête entier.", - "snmp-add-mapping": "Ajouter un mapping SNMP", - "snmp-mapping-not-configured": "Aucun mapping pour OID vers timeseries/télémétrie n'est configuré", - "snmp-timseries-or-attribute-name": "Nom timeseries/attribut pour mapping", - "snmp-timseries-or-attribute-type": "Type timeseries/attribut pour mapping", - "mqtt-payload-type-required": "Type de payload est requis.", - "coap-device-type": "Type de dispositif CoAP", - "coap-device-payload-type": "Payload de dispositif CoAP", - "coap-device-type-required": "Type de dispositif CoAP est requis.", - "coap-device-type-default": "Défaut", - "support-level-wildcards": "[+] unique et wildcards de [#] multi-niveaux supportés.", - "telemetry-topic-filter": "Filtre de sujets de télémétrie", - "telemetry-topic-filter-required": "Filtre de sujets de télémétrie est requis.", - "attributes-topic-filter": "Attributes publish topic filter", - "attributes-subscribe-topic-filter": "Attributes subscribe topic filter", - "attributes-topic-filter-required": "Attributes publish topic filter is required.", - "attributes-subscribe-topic-filter-required": "Attributes subscribe topic is required", - "telemetry-proto-schema": "Schéma proto de télémétrie", - "telemetry-proto-schema-required": "Schéma proto de télémétrie est requis.", - "attributes-proto-schema": "Schéma proto d'attributs", - "attributes-proto-schema-required": "Schéma proto d'attributs est requis.", - "rpc-response-proto-schema": "Schéma proto de réponse RPC", - "rpc-response-proto-schema-required": "Schéma proto de réponse RPC est requis.", - "rpc-response-topic-filter": "Filtre de sujets de réponse RPC", - "rpc-response-topic-filter-required": "Filtre de sujets de réponse RPC est requis.", - "rpc-request-proto-schema": "Schéma proto de requête RPC", - "rpc-request-proto-schema-required": "Schéma proto de requête RPC est requis.", - "rpc-request-proto-schema-hint": "Une requête RPC devrait toujours avoir les champs: string method = 1; int32 requestId = 2; et params = 3 de n'importe quel type de données." - }, - "dialog": { - "close": "Fermer le dialogue" + "markdown": { + "use-markdown-text-function": "Utiliser une fonction de valeur markdown/HTML", + "markdown-text-function": "Fonction de valeur markdown/HTML", + "markdown-text-pattern": "Modèle markdown/HTML (markdown ou HTML avec variables, par ex. '${entityName} ou ${keyName} - du texte.')", + "apply-default-markdown-style": "Appliquer le style markdown par défaut", + "markdown-css": "CSS pour markdown/HTML" }, - "edge": { - "edge": "Bordure", - "edge-instances": "Instances de Bord", - "edge-file": "Fichier Edge", - "management": "Gestion des bordures", - "no-edges-matching": "Aucun bordure correspondant à {{entity}} n'a été trouvé.", - "add": "Ajouter un bordure", - "no-edges-text": "Aucun bordure trouvé", - "edge-details": "Détails de la bordure", - "add-edge-text": "Ajouter une nouveau bordure", - "delete": "Supprimer la bordure", - "delete-edge-title": "Êtes-vous sûr de vouloir supprimer la bordure '{{edgeName}}'?", - "delete-edge-text": "Faites attention, après la confirmation, la bordure et toutes les données associées deviendront irrécupérables", - "delete-edges-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 bordure} other {# bordure} }?", - "delete-edges-text": "Faites attention, après la confirmation, tous les bordures sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "name": "Nom", - "name-starts-with": "Le nom du bord commence par", - "name-required": "Le nom de la bordure est requis", - "description": "Dispositifs", - "details": "Détails de l'entité", - "events": "Événements", - "copy-id": "Copier borudre Id", - "id-copied-message": "Id de la bordure a été copié dans le presse-papier", - "sync": "Sync Edge", - "edge-required": "Bordure est requise", - "edge-type": "Type de la bordure", - "edge-type-required": "Type de la bordure est requise.", - "event-action": "Action d'événement", - "entity-id": "ID d'entité", - "select-edge-type": "Selectionner un type de la bordure", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", - "assign-edge-to-customer": "Attribuer la bordure au client", - "assign-edge-to-customer-text": "Veuillez sélectionner la bordure pour attribuer le ou les dispositifs", - "assignedToCustomer": "Attribué au client", - "edge-public": "Edge est public", - "assigned-to-customer": "Attribué au client", - "unassign-from-customer": "Retirer du client", - "unassign-edge-title": "Êtes-vous sûr de vouloir annuler l'affection du dispositif {{edgeName}}", - "unassign-edge-text": "Après la confirmation, le dispositif ne sera pas attribué et ne sera pas accessible au client", - "unassign-edges-title": "Voulez-vous vraiment annuler l'attribution de {count, plural, =1 {1 bordure} other {# bordures} }?", - "unassign-edges-text": "Après la confirmation, tous les bordures sélectionnés ne seront plus attribués et ne seront pas accessibles par le client.", - "make-public": "Make edge public", - "make-public-edge-title": "Are you sure you want to make the edge '{{edgeName}}' public?", - "make-public-edge-text": "After the confirmation the edge and all its data will be made public and accessible by others.", - "make-private": "Rendre public Edge", - "public": "Public", - "make-private-edge-title": "Are you sure you want to make the edge '{{edgeName}}' private?", - "make-private-edge-text": "Après la confirmation, la bordure et toutes ses données seront rendues privées et ne seront pas accessibles par d'autres", - "import": "Importer bordure", - "label": "Etiquette", - "load-entity-error": "Entité introuvable. Échec du chargement des informations", - "assign-new-edge": "Attribuer un nouvel bordure", - "unassign-from-edge": "Retirer de la bordure", - "edge-key": "Clé de la bordure", - "copy-edge-key": "Copier clé de la bordure", - "edge-key-copied-message": "Clé de la bordure a été copié dans le presse-papier", - "edge-secret": "Secret de la bordure", - "copy-edge-secret": "Copier secret de la bordure", - "edge-secret-copied-message": "Secret de la bordure a été copié dans le presse-papier", - "edge-assets": "Gérer les actifs de la bordure", - "edge-devices": "Gérer les dispositifs de la bordure", - "edge-entity-views": "Vues de l'entité vues de l'entité", - "edge-dashboards": "Gérer les tableaux de bord", - "edge-rulechains": "Chaînes de règles Edge", - "assets": "Actifs de la bordure", - "devices": "Dispositifs de la bordure", - "entity-views": "Vues de l'entité bordure", - "dashboard": "Tableau de bord Edge", - "dashboards": "Tableau de bord de la bordure", - "rulechain-templates": "Modèles de chaîne de règles", - "rulechains": "Chaînes de règles de la bordure", - "search": "Rechercher les bords", - "selected-edges": "{count, plural, =1 {1 bordure} other {# bords} } sélectionné", - "any-edge": "Tout bord", - "no-edge-types-matching": "Aucun type d'arête correspondant à \"{{entitySubtype}}\" n'a été trouvé.", - "edge-type-list-empty": "Aucun type d'arête sélectionné.", - "edge-types": "Types de bords", - "enter-edge-type": "Entrez le type d'arête", - "deployed": "Déployé", - "pending": "En attente", - "downlinks": "Liens descendants", - "no-downlinks-prompt": "Aucun lien descendant trouvé", - "sync-process-started-successfully": "Le processus de synchronisation a démarré avec succès!", - "missing-related-rule-chains-title": "Edge n'a pas de chaîne (s) de règles associées", - "missing-related-rule-chains-text": "Les chaînes de règles affectées aux tronçons utilisent des nœuds de règles qui transfèrent les messages vers les chaînes de règles non affectées à ce tronçon.

Liste des chaînes de règles manquantes:
{{missingRuleChains}}", - "widget-datasource-error": "Ce widget prend en charge uniquement la source de données d'entité EDGE" - }, - "edge-event": { - "type-dashboard": "Dashboard", - "type-asset": "Asset", - "type-device": "Device", - "type-device-profile": "Device Profile", - "type-entity-view": "Entity View", - "type-alarm": "Alarm", - "type-rule-chain": "Rule Chain", - "type-rule-chain-metadata": "Rule Chain Metadata", - "type-edge": "Edge", - "type-user": "User", - "type-customer": "Customer", - "type-relation": "Relation", - "type-widgets-bundle": "Widgets Bundle", - "type-widgets-type": "Widgets Type", - "type-admin-settings": "Admin Settings", - "action-type-added": "Added", - "action-type-deleted": "Deleted", - "action-type-updated": "Updated", - "action-type-post-attributes": "Post Attributes", - "action-type-attributes-updated": "Attributes Updated", - "action-type-attributes-deleted": "Attributes Deleted", - "action-type-timeseries-updated": "Timeseries Updated", - "action-type-credentials-updated": "Credentials Updated", - "action-type-assigned-to-customer": "Assigned to Customer", - "action-type-unassigned-from-customer": "Unassigned from Customer", - "action-type-relation-add-or-update": "Relation Add or Update", - "action-type-relation-deleted": "Relation Deleted", - "action-type-rpc-call": "RPC Call", - "action-type-alarm-ack": "Alarm Ack", - "action-type-alarm-clear": "Alarm Clear", - "action-type-assigned-to-edge": "Assigned to Edge", - "action-type-unassigned-from-edge": "Unassigned from Edge", - "action-type-credentials-request": "Credentials Request", - "action-type-entity-merge-request": "Entity Merge Request" - }, - "entity": { - "add-alias": "Ajouter un alias d'entité", - "alarm-name-starts-with": "Les alarmes dont le nom commence par '{{prefix}}'", - "alias": "Alias", - "alias-required": "Un alias d'entité est requis.", - "aliases": "alias d'entité", - "all-subtypes": "Tout", - "any-entity": "Toute entité", - "asset-name-starts-with": "Les actifs dont le nom commence par '{{prefix}}'", - "columns-to-display": "Colonnes à afficher", - "configure-alias": "Configurer '{{alias}}' alias", - "create-new-alias": "Créez un nouveau!", - "create-new-key": "Créez un nouveau!", - "customer-name-starts-with": "Les clients dont les noms commencent par '{{prefix}}'", - "dashboard-name-starts-with": "Les tableaux de bord dont les noms commencent par '{{prefix}}'", - "details": "Détails de l'entité", - "device-name-starts-with": "Dispositifs dont le nom commence par '{{prefix}}'", - "duplicate-alias-error": "Alias ​​en double trouvé '{{alias}}'.
Les alias d'entité doivent être uniques dans le tableau de bord.", - "enter-entity-type": "Entrez le type d'entité", - "entities": "Entités", - "entity": "Entité", - "entity-alias": "Alias de l'entité", - "entity-list": "Liste d'entités", - "entity-list-empty": "Aucune entité sélectionnée.", - "entity-name": "Nom de l'entité", - "entity-name-filter-no-entity-matched": "Aucune entité commençant par '{{entity}}' n'a été trouvée.", - "entity-name-filter-required": "Le filtre de nom d'entité est requis.", - "entity-type": "Type d'entité", - "entity-type-list": "Liste de types d'entités", - "entity-types": "Types d'entité", - "entity-view-name-starts-with": "Les vues d'entité dont le nom commence par '{{prefix}}'", - "key": "Clé", - "key-name": "Nom de la clé", - "list-of-alarms": "{count, plural, =1 {Une alarme} other {Liste de # alarmes} }", - "list-of-assets": "{count, plural, =1 {Un Asset} other {Liste de # Assets} }", - "list-of-customers": "{count, plural, =1 {Un client} other {Liste de # clients} }", - "list-of-dashboards": "{count, plural, =1 {Un tableau de bord} other {Liste de # tableaux de bord} }", - "list-of-devices": "{count, plural, =1 {Un dispositif} other {Liste de # dispositifs} }", - "list-of-plugins": "{count, plural, =1 {Un plugin} other {Liste de # plugins} }", - "list-of-rulechains": "{count, plural, =1 {Une chaîne de règles} other {Liste de # chaînes de règles} }", - "list-of-rulenodes": "{count, plural, =1 {Un noeud de règles} other {Liste de # noeuds de règles} }", - "list-of-rules": "{count, plural, =1 {Une règle} other {Liste de # règles} }", - "list-of-tenants": "{count, plural, =1 {Un tenant} other {Liste de # tenants} }", - "list-of-users": "{count, plural, =1 {Un utilisateur} other {Liste de # utilisateurs} }", - "missing-entity-filter-error": "Le filtre est manquant pour l'alias '{{alias}}'.", - "name-starts-with": "Nom commence par", - "no-alias-matching": "'{{alias}}' introuvable.", - "no-aliases-found": "Aucun alias trouvé.", - "no-data": "Aucune donnée à afficher", - "no-entities-matching": "Aucune entité correspondant à '{{entity}}' n'a été trouvée.", - "no-entities-prompt": "Aucune entité trouvée", - "no-entity-types-matching": "Aucun type d'entité correspondant à {{entityType}} n'a été trouvé. ", - "no-key-matching": "'{{key}}' introuvable.", - "no-keys-found": "Aucune clé trouvée", - "plugin-name-starts-with": "Plugins dont les noms commencent par '{{prefix}}'", - "remove-alias": "Supprimer l'alias d'entité", - "rule-name-starts-with": "Régles dont les noms commencent par '{{prefix}}'", - "rulechain-name-starts-with": "Chaînes de régles dont les noms commencent par '{{prefix}}'", - "rulenode-name-starts-with": "Les noeuds de régles dont le nom commence par '{{prefix}}'", - "type-edge": "Bordure", - "type-edges": "Bordures", - "list-of-edges": "{ count, plural, =1 {Une bordure} other {Liste de # bordures} }", - "edge-name-starts-with": "Bordures dont les noms commencent par '{{prefix}}'", - "search": "Recherche d'entités", - "select-entities": "Sélectionner des entités", - "selected-entities": "{count, plural, =1 {1 entité} other {# entités} } sélectionnées", - "tenant-name-starts-with": "Les Tenant dont le nom commence par '{{prefix}}'", - "type": "Type", - "type-alarm": "Alarme", - "type-alarms": "Alarmes", - "type-asset": "Actif", - "type-assets": "Actifs", - "type-current-customer": "Client actuel", - "type-customer": "Client", - "type-customers": "Clients", - "type-dashboard": "Tableau de bord", - "type-dashboards": "Tableaux de bord", - "type-device": "Dispositif", - "type-devices": "Dispositifs", - "type-entity-view": "Vue d'entité", - "type-entity-views": "Vues d'entités", - "type-plugin": "Plugin", - "type-plugins": "Plugins", - "type-required": "Le type d'entité est obligatoire.", - "type-rule": "Régle", - "type-rulechain": "Chaîne de régles", - "type-rulechains": "Chaînes de régles", - "type-rulenode": "Noeud de régle", - "type-rulenodes": "Noeuds de régle", - "type-rules": "Régles", - "type-tenant": "Tenant", - "type-tenants": "Tenants", - "type-user": "Utilisateur", - "type-users": "Utilisateurs", - "unable-delete-entity-alias-text": "L'alias d'entité '{{entityAlias}}' ne peut pas être supprimé car il est utilisé par les widgets suivants:
{{widgetsList}}", - "unable-delete-entity-alias-title": "Impossible de supprimer l'alias d'entité", - "use-entity-name-filter": "Utiliser un filtre", - "user-name-starts-with": "Utilisateurs dont les noms commencent par '{{prefix}}'" - }, - "entity-field": { - "address": "Adresse", - "address2": "Adresse 2", - "city": "Ville", - "country": "Pays", - "created-time": "Heure de création", - "email": "Email", - "first-name": "Prénom", - "last-name": "Nom de famille", - "name": "Nom", - "phone": "Téléphone", - "state": "Prov", - "title": "Titre", - "type": "Type", - "zip": "Code postal" - }, - "entity-view": { - "add": "Ajouter une vue d'entité", - "add-alias": "Ajouter un alias de vue d'entité", - "add-entity-view-text": "Ajouter une nouvelle vue d'entité", - "alias": "Alias", - "alias-required": "Un alias de vue d'entité est requis.", - "aliases": "Alias de vue d'entité", - "any-entity-view": "Toute vue d'entité", - "assign-entity-view-to-customer": "Attribuer une (des) vue (s) d'entité à un client", - "assign-entity-view-to-customer-text": "Veuillez sélectionner les vues d'entité à affecter au client", - "assign-entity-views": "Attribuer des vues d'entité", - "assign-entity-views-text": "Attribuer { count, plural, =1 {1 entityView} other {# entityViews} } au client", - "assign-new-entity-view": "Attribuer une nouvelle vue d'entité", - "assign-to-customer": "Attribuer au client", - "assign-to-customer-text": "Veuillez sélectionner le client auquel attribuer la ou les vues d'entité.", - "assignedToCustomer": "Assigné au client", - "attributes-propagation": "Propagation des attributs", - "attributes-propagation-hint": "La vue d'entité copiera automatiquement les attributs spécifiés de l'entité cible chaque fois que vous enregistrez ou mettez à jour cette vue d'entité. Pour des raisons de performances, les attributs d'entité cible ne sont pas propagés à la vue d'entité à chaque changement d'attribut. Vous pouvez activer la propagation automatique en configurant le noeud de règle \" copier pour afficher \" dans votre chaîne de règles et en liant les messages \"Post attributs \" et \"attributs mis à jour \" au nouveau noeud de règle.", - "client-attributes": "Attributs du client", - "client-attributes-placeholder": "Attributs du client", - "configure-alias": "Configurez l'alias '{{alias}}'", - "copyId": "Copier l'ID de la vue d'entité", - "create-new-alias": "Créer un nouveau!", - "create-new-key": "Créer un nouveau!", - "date-limits": "Limites de date", - "delete": "Supprimer la vue d'entité", - "delete-entity-view-text": "Attention, après la confirmation, la vue de l'entité et toutes les données associées deviendront irrécupérables.", - "delete-entity-view-title": "Êtes-vous sûr de vouloir supprimer la vue de l'entité '{{entityViewName}}'?", - "delete-entity-views": "Supprimer les vues d'entité", - "delete-entity-views-action-title": "Supprimer { count, plural, =1 {1 entityView} other {# entityViews} }", - "delete-entity-views-text": "Attention, après la confirmation, toutes les vues d'entité sélectionnées seront supprimées et toutes les données associées deviendront irrécupérables.", - "delete-entity-views-title": "Êtes-vous sûr de vouloir voir l'entité { count, plural, =1 {1 entityView} other {# entityViews} }?", - "description": "Description", - "details": "Détails", - "duplicate-alias-error": "Alias '{{alias}}' existe déjà.
Les alias de vue d'entité doivent être uniques dans le tableau de bord.", - "end-date": "Date de fin", - "end-ts": "Heure de fin", - "enter-entity-view-type": "Entrer le type de vue d'entité", - "entity-view": "Vue d'entité", - "entity-view-alias": "Alias de vue d'entité", - "entity-view-details": "Détails de la vue d'entité", - "entity-view-list": "Liste de vues d'entités", - "entity-view-list-empty": "Aucune vue d'entité sélectionnée.", - "entity-view-name-filter-no-entity-view-matched": "Aucune vue d'entité commençant par '{{entityView}}' n'a été trouvée.", - "entity-view-name-filter-required": "Un filtre de nom de vue d'entité est requis.", - "entity-view-required": "Une vue d'entité est requise.", - "entity-view-type": "Type de vue d'entité", - "entity-view-type-list-empty": "Aucun type de vue d'entité sélectionné.", - "entity-view-type-required": "Le type d'entité est requis.", - "entity-view-types": "Types de vues d'entité", - "entity-views": "Vues d'entité", - "events": "Événements", - "make-private": "Rendre la vue d'entité privée", - "make-private-entity-view-text": "Après la confirmation, la vue de l'entité et toutes ses données seront rendues privées et ne seront pas accessibles par d'autres", - "make-private-entity-view-title": "Êtes-vous sûr de vouloir rendre la vue d'entité '{{entityViewName}}' privée?", - "make-public": "Rendre la vue d'entité publique", - "make-public-entity-view-text": "Après la confirmation, la vue de l'entité et toutes ses données seront rendues publiques et accessibles à d'autres", - "make-public-entity-view-title": "Voulez-vous vraiment que la vue de l'entité '{{entityViewName}}' soit publique?", - "assign-entity-view-to-edge": "Attribuer a la bordure", - "assign-entity-view-to-edge-text": "Veuillez sélectionner la bordure auquel attribuer la ou les vues d'entité.", - "unassign-entity-view-from-edge-title": "Voulez-vous vraiment annuler l'attribution de la vue d'entité '{{entityViewName}}'?", - "unassign-entity-view-from-edge-text": "Après la confirmation, la vue de l'entité sera non attribuée et ne sera pas accessible par la bordure.", - "unassign-entity-views-from-edge-action-title": "Annuler l'attribution { count, plural, =1 {1 entityView} other {# entityViews} } de la bordure", - "unassign-entity-view-from-edge": "Annuler l'attribution des vues d'entité", - "unassign-entity-views-from-edge-title": "Êtes-vous sûr de vouloir annuler l'attribution { count, plural, =1 {1 entityView} other {# entityViews} }?", - "unassign-entity-views-from-edge-text": "Après la confirmation, toutes les vues des entités sélectionnées seront non attribuées et ne seront pas accessibles par la bordure.", - "management": "Gestion de vue d'entité", - "name": "Nom", - "name-required": "Un nom est requis.", - "name-starts-with": "Le nom de la vue d'entité commence par", - "no-alias-matching": "'{{alias}}' non trouvé.", - "no-aliases-found": "Aucun alias trouvé.", - "no-entity-view-types-matching": "Aucun type de vue d'entité correspondant à '{{entitySubtype}}' n'a été trouvé.", - "no-entity-views-matching": "Aucune vue d'entité correspondant à '{{entity}}' n'a été trouvée.", - "no-entity-views-text": "Aucune vue d'entité trouvée.", - "no-key-matching": "'{{key}}' non trouvé.", - "no-keys-found": "Aucune clé trouvée.", - "remove-alias": "Supprimer un alias de vue d'entité", - "select-entity-view": "Sélectionner une vue d'entité", - "select-entity-view-type": "Sélectionner le type de vue d'entité", - "server-attributes": "Attributs du serveur", - "server-attributes-placeholder": "Attributs du serveur", - "shared-attributes": "Attributs partagés", - "shared-attributes-placeholder": "Attributs partagés", - "start-date": "Date de début", - "start-ts": "Heure de début", - "target-entity": "Entité cible", - "timeseries": "Séries chronologiques", - "timeseries-data": "Données de séries chronologiques", - "timeseries-data-hint": "Configurez les clés de données de séries chronologiques de l'entité cible qui seront accessibles à la vue de l'entité. Ces données temporelles sont en lecture seule.", - "timeseries-placeholder": "Séries chronologiques", - "unable-entity-view-device-alias-text": "L'alias de dispositif '{{entityViewAlias}}' ne peut pas être supprimé car il est utilisé par les widgets suivants:
{{widgetsList}}", - "unable-entity-view-device-alias-title": "Impossible de supprimer l'alias de la vue d'entité.", - "unassign-entity-view": "Annuler l'affectation de la vue d'entité", - "unassign-entity-view-text": "Après la confirmation, la vue de l'entité sera non attribuée et ne sera pas accessible par le client.", - "unassign-entity-view-title": "Voulez-vous vraiment annuler l'attribution de la vue d'entité '{{entityViewName}}'?", - "unassign-entity-views": "Annuler l'attribution des vues d'entité", - "unassign-entity-views-action-title": "Annuler l'attribution { count, plural, =1 {1 entityView} other {# entityViews} } du client", - "unassign-entity-views-text": "Après la confirmation, toutes les vues des entités sélectionnées seront non attribuées et ne seront pas accessibles par le client.", - "unassign-entity-views-title": "Êtes-vous sûr de vouloir annuler l'attribution { count, plural, =1 {1 entityView} other {# entityViews} }?", - "unassign-from-customer": "Annuler l'attribution au client", - "use-entity-view-name-filter": "Use filter", - "view-entity-views": "Voir les vues d'entité", - "idCopiedMessage": "L'ID de la vue d'entité a été copiée dans le presse-papier", - "search": "Rechercher des vues d'entité", - "selected-entity-views": "{ count, plural, =1 {1 entity view} other {# entity views} } sélectionnés" + "simple-card": { + "label": "Étiquette", + "label-position": "Position de l’étiquette", + "label-position-left": "Gauche", + "label-position-top": "Haut" }, - "error": { - "unable-to-connect": "Impossible de se connecter au serveur! Veuillez vérifier votre connexion Internet.", - "unhandled-error-code": "Code d'erreur non géré: {{errorCode}}", - "unknown-error": "Erreur inconnue" - }, - "event": { - "alarm": "Alarme", - "body": "Corps", - "data": "Données", - "data-type": "Type de données", - "error": "erreur", - "type-edge-event": "Downlink", - "errors-occurred": "Des erreurs sont survenues", - "event": "événement", - "event-time": "Heure de l'événement", - "event-type": "Type d'événement", - "failed": "Échec", - "message-id": "Message Id", - "message-type": "Type de message", - "messages-processed": "Messages traités", - "metadata": "Métadonnées", - "method": "Méthode", - "no-events-prompt": "Aucun événement trouvé", - "relation-type": "Type de relation", - "server": "Serveur", - "status": "État", - "success": "Succès", + "single-switch": { + "behavior": "Comportement", + "layout": "Disposition", + "layout-right": "Droite", + "layout-left": "Gauche", + "layout-centered": "Centré", + "auto-scale": "Ajustement automatique", + "label": "Étiquette", + "icon": "Icône", + "switch-color": "Couleur de l’interrupteur", + "on": "Activé", + "off": "Désactivé", + "disabled": "Désactivé", + "tumbler-color": "Couleur du curseur", + "on-label": "Étiquette pour Activé", + "off-label": "Étiquette pour Désactivé", + "switch": "Interrupteur" + }, + "slider": { + "behavior": "Comportement", + "initial-value": "Valeur initiale", + "initial-value-hint": "Action pour obtenir la valeur initiale du curseur.", + "on-value-change": "Au changement de valeur", + "on-value-change-hint": "Action déclenchée lorsque la valeur du curseur change.", + "layout": "Disposition", + "layout-default": "Par défaut", + "layout-extended": "Étendu", + "layout-simplified": "Simplifié", + "auto-scale": "Ajustement automatique", + "icon": "Icône", + "value": "Valeur", + "range": "Plage", + "min": "min", + "max": "max", + "range-ticks": "Graduations", + "tick-marks": "Repères", + "colors": "Couleurs", + "main": "Principal", + "background": "Arrière-plan", + "left-icon": "Icône gauche", + "right-icon": "Icône droite", + "slider": "Curseur" + }, + "value-card": { + "layout": "Disposition", + "layout-square": "Carré", + "layout-vertical": "Vertical", + "layout-centered": "Centré", + "layout-simplified": "Simplifié", + "layout-horizontal": "Horizontal", + "layout-horizontal-reversed": "Horizontal inversé", + "label": "Étiquette", + "icon": "Icône", + "value": "Valeur", + "date": "Date", + "value-card-style": "Style de la carte de valeur", + "auto-scale": "Ajustement automatique" + }, + "label-card": { + "auto-scale": "Ajustement automatique", + "label": "Étiquette", + "icon": "Icône", + "label-card-style": "Style de la carte d’étiquette" + }, + "label-value-card": { + "value": "Valeur", + "label-value-card-style": "Style de la carte étiquette & valeur" + }, + "liquid-level-card": { + "layout-simple": "Simple", + "layout-percentage": "Pourcentage", + "layout-absolute": "Absolu", + "layout": "Disposition", + "background-overlay": "Superposition de l'arrière-plan de la valeur", + "total-volume": "Volume total", + "total-volume-units": "Unités du volume total", + "tank": "Réservoir", + "shape": "Forme", + "datasource-units": "Unités de la source", + "widget-units": "Unités du widget", + "decimals": "Décimales", + "liquid": "Liquide", + "liquid-color": "Couleur du liquide", + "value": "Valeur", + "value-font": "Police de la valeur", + "level": "Niveau", + "last-update": "Dernière mise à jour", + "shape-by-attribute": "Définir la forme du réservoir via le nom d'attribut", + "tooltip-background": "Couleur d’arrière-plan", + "background-blur": "Flou d’arrière-plan", + "tank-color": "Couleur du réservoir", + "static": "Statique", + "see-examples": "Voir des exemples", + "attribute": "Attribut", + "shape-type": "Type", + "v-oval": "Ovale vertical", + "v-cylinder": "Cylindre vertical", + "v-capsule": "Capsule verticale", + "rectangle": "Rectangle", + "h-oval": "Ovale horizontal", + "h-ellipse": "Ellipse horizontale", + "h-dish-ends": "Extrémités concaves horizontales", + "h-cylinder": "Cylindre horizontal", + "h-capsule": "Capsule horizontale", + "h-elliptical_2_1": "Ellipse horizontale 2:1", + "icon": "Icône de la carte", + "title": "Titre de la carte", + "units": "Unités", + "color-and-font": "Couleur et police", + "shape-attribute-name": "Nom de l'attribut", + "total-volume-required": "Le volume total est requis.", + "attribute-name-required": "Le nom de l'attribut est requis.", + "attribute-key-not-set": "La clé de l'attribut '{{attributeName}}' n'est pas définie", + "attribute-key-invalid": "La clé de l'attribut '{{attributeName}}' est invalide" + }, + "aggregated-value-card": { + "subtitle": "Sous-titre", + "chart": "Graphique", + "values": "Valeurs", + "value-appearance": "Apparence de la valeur", + "position": "Position", + "position-center": "Centre", + "position-right-top": "En haut à droite", + "position-right-bottom": "En bas à droite", + "position-left-top": "En haut à gauche", + "position-left-bottom": "En bas à gauche", + "font": "Police", + "color": "Couleur", + "arrow": "Flèche", + "display-up-down-arrow": "Afficher les flèches haut/bas", + "add-value": "Ajouter une valeur", + "remove-value": "Supprimer la valeur", + "no-values": "Aucune valeur configurée", + "aggregation": "Agrégation", + "aggregated-value-card-style": "Style de carte de valeur agrégée", + "auto-scale": "Mise à l'échelle automatique" + }, + "value-chart-card": { + "layout": "Disposition", + "layout-left": "Gauche", + "layout-right": "Droite", + "auto-scale": "Mise à l'échelle automatique", + "icon": "Icône", + "value": "Valeur", + "chart": "Graphique", + "value-chart-card-style": "Style de carte de tableau de valeurs" + }, + "progress-bar": { + "layout": "Disposition", + "layout-default": "Par défaut", + "layout-simplified": "Simplifiée", + "auto-scale": "Mise à l'échelle automatique", + "icon": "Icône", + "value": "Valeur", + "range": "Plage", + "min": "min", + "max": "max", + "range-ticks": "Graduations de la plage", + "bar": "Barre", + "bar-color": "Couleur de la barre", + "bar-background": "Fond de la barre", + "progress-bar-card-style": "Style de carte barre de progression" + }, + "notification": { + "max-notification-display": "Nombre maximum de notifications à afficher", + "counter": "Compteur", + "counter-hint": "Le compteur sera affiché si le \"Titre du widget\" est activé", + "icon": "Icône", + "counter-value": "Valeur", + "counter-color": "Couleur", + "notification-button": "Boutons de notification", + "button-view-all": "Tout voir", + "button-filter": "Filtrer", + "type-filter": "Filtre de type", + "button-mark-read": "Tout marquer comme lu", + "notification-types": "Types de notification", + "notification-type": "Type de notification", + "search-type": "Type de recherche", + "any-type": "Tout type" + }, + "alarm-count": { + "alarm-count-card-style": "Style de carte de compteur d'alarmes" + }, + "entity-count": { + "entity-count-card-style": "Style de carte de compteur d'entités" + }, + "count": { + "layout": "Disposition", + "layout-column": "Colonne", + "layout-row": "Ligne", + "label": "Étiquette", + "icon": "Icône", + "icon-background": "Fond de l'icône", + "value": "Valeur", + "chevron": "Chevron", + "auto-scale": "Mise à l'échelle automatique" + }, + "table": { + "common-table-settings": "Paramètres généraux du tableau", + "enable-search": "Activer la recherche", + "enable-sticky-header": "Toujours afficher l'en-tête", + "enable-sticky-action": "Toujours afficher la colonne des actions", + "hidden-cell-button-display-mode": "Mode d'affichage des actions des boutons de cellule masqués", + "show-empty-space-hidden-action": "Afficher un espace vide à la place du bouton d'action masqué", + "dont-reserve-space-hidden-action": "Ne pas réserver d'espace pour les boutons d'action masqués", + "display-timestamp": "Horodatage", + "display-pagination": "Afficher la pagination", + "default-page-size": "Taille de page par défaut", + "page-step-settings": "Paramètres de pas de page", + "page-step-count": "Nombre d'étapes", + "page-step-increment": "Incrément de pas", + "page-step-count-format-message": "Doit être une valeur entière, comprise entre 1 et 100.", + "page-step-increment-format-message": "Doit être une valeur entière supérieure ou égale à 1.", + "use-entity-label-tab-name": "Utiliser le libellé de l'entité dans le nom de l'onglet", + "hide-empty-lines": "Masquer les lignes vides", + "row-style": "Style de ligne", + "use-row-style-function": "Utiliser une fonction de style de ligne", + "row-style-function": "Fonction de style de ligne", + "cell-style": "Style de cellule", + "use-cell-style-function": "Utiliser une fonction de style de cellule", + "cell-style-function": "Fonction de style de cellule", + "cell-content": "Contenu de la cellule", + "use-cell-content-function": "Utiliser une fonction de contenu de cellule", + "cell-content-function": "Fonction de contenu de cellule", + "show-latest-data-column": "Afficher la colonne de données les plus récentes", + "latest-data-column-order": "Ordre de la colonne de données les plus récentes", + "entities-table-title": "Titre du tableau des entités", + "enable-select-column-display": "Activer la sélection des colonnes à afficher", + "display-entity-name": "Afficher la colonne du nom de l'entité", + "entity-name-column-title": "Titre de la colonne du nom de l'entité", + "display-entity-label": "Afficher la colonne du libellé de l'entité", + "entity-label-column-title": "Titre de la colonne du libellé de l'entité", + "display-entity-type": "Afficher la colonne du type d'entité", + "default-sort-order": "Ordre de tri par défaut", + "custom-title": "Titre personnalisé de l'en-tête", + "column-width": "Largeur de colonne (px ou %)", + "default-column-visibility": "Visibilité par défaut de la colonne", + "column-visibility-visible": "Visible", + "column-visibility-hidden": "Masquée", + "column-visibility-hidden-mobile": "Masquée en mode mobile", + "column-selection-to-display": "Sélection de colonne dans 'Colonnes à afficher'", + "column-selection-to-display-enabled": "Activée", + "column-selection-to-display-disabled": "Désactivée", + "alarms-table-title": "Titre du tableau des alarmes", + "enable-alarms-selection": "Activer la sélection des alarmes", + "enable-alarms-search": "Activer la recherche d'alarmes", + "enable-alarm-filter": "Activer le filtre d'alarme", + "display-alarm-details": "Afficher les détails de l'alarme", + "allow-alarms-ack": "Autoriser l'accusé de réception des alarmes", + "allow-alarms-clear": "Autoriser l'effacement des alarmes", + "display-alarm-activity": "Afficher l'activité de l'alarme", + "allow-alarms-assign": "Autoriser l'assignation des alarmes", + "columns": "Colonnes", + "column-settings": "Paramètres des colonnes", + "remove-column": "Supprimer la colonne", + "add-column": "Ajouter une colonne", + "no-columns": "Aucune colonne configurée", + "columns-to-display": "Colonnes à afficher", + "table-header": "En-tête du tableau", + "header-buttons": "Boutons d'en-tête", + "table-buttons": "Boutons du tableau", + "pagination": "Pagination", + "rows": "Lignes", + "timeseries-column-error": "Au moins une colonne de séries temporelles doit être spécifiée", + "alarm-column-error": "Au moins une colonne d'alarme doit être spécifiée", + "table-tabs": "Onglets du tableau", + "show-cell-actions-menu-mobile": "Afficher le menu déroulant d'actions des cellules en mode mobile", + "disable-sorting": "Désactiver le tri" + }, + "latest-chart": { + "total": "Total", + "auto-scale": "Mise à l'échelle automatique", + "clockwise-layout": "Disposition dans le sens horaire", + "sort-series": "Trier les séries par libellé", + "tooltip-value-type-absolute": "Absolu", + "tooltip-value-type-percentage": "Pourcentage" + }, + "pie-chart": { + "pie-chart-appearance": "Apparence du graphique en secteurs", + "label": "Libellé", + "border": "Bordure", + "radius": "Rayon", + "pie-chart-card-style": "Style de la carte graphique en secteurs" + }, + "radar-chart": { + "radar-appearance": "Apparence du radar", + "shape": "Forme", + "shape-polygon": "Polygone", + "shape-circle": "Cercle", + "color": "Couleur", + "line": "Ligne", + "points": "Points", + "points-label": "Libellé des points", + "radar-axis": "Axe radar", + "axis-label": "Libellé de l'axe", + "ticks-label": "Libellé des graduations", + "radar-chart-style": "Style du graphique radar" + }, + "time-series-chart": { + "chart": "Graphique", + "chart-style": "Style du graphique", + "data-zoom": "Zoom sur les données", + "stack-mode": "Mode empilé", + "stack-mode-hint": "Empile les séries sur le graphique. Les séries ayant la même unité seront superposées.", + "axes": "Axes", + "y-axes": "Axes Y", + "line-type": "Type de ligne", + "line-width": "Épaisseur de ligne", + "type-line": "Ligne", + "type-bar": "Barre", + "type-point": "Point", + "no-aggregation-bar-width-strategy": "Stratégie de largeur de barre pour les données non agrégées", + "no-aggregation-bar-width-strategy-group": "Grouper", + "no-aggregation-bar-width-strategy-separate": "Séparer", + "bar-group-width": "Largeur du groupe de barres", + "bar-width": "Largeur de la barre", + "bar-width-relative": "Pourcentage de la fenêtre temporelle", + "bar-width-absolute": "Absolue (ms)", + "comparison": { + "comparison": "Comparaison", + "comparison-hint": "La comparaison fonctionne uniquement avec les données historiques !", + "show": "Afficher", + "settings": "Paramètres de comparaison", + "show-values-for-comparison": "Afficher les données historiques pour la comparaison", + "comparison-values-label": "Libellé de la clé de comparaison", + "comparison-values-label-auto": "Auto", + "comparison-data-color": "Couleur des données de comparaison" + }, + "threshold": { + "thresholds": "Seuils", + "source": "Source", + "key-value": "Clé / Valeur", + "no-thresholds": "Aucun seuil configuré", + "add-threshold": "Ajouter un seuil", + "type-constant": "Constante", + "type-latest-key": "Clé", + "type-entity": "Entité", + "threshold-settings": "Paramètres des seuils", + "remove-threshold": "Supprimer le seuil", + "threshold-value-required": "La valeur du seuil est requise.", + "key-required": "La clé est requise.", + "entity-key-required": "La clé de l'entité est requise.", + "line-appearance": "Apparence de la ligne", + "line-color": "Couleur de la ligne", + "start-symbol": "Symbole de début", + "end-symbol": "Symbole de fin", + "symbol-size": "Taille", + "label": "Libellé", + "label-position-start": "Début", + "label-position-middle": "Milieu", + "label-position-end": "Fin", + "label-position-inside-start": "Intérieur début", + "label-position-inside-start-top": "Intérieur début haut", + "label-position-inside-start-bottom": "Intérieur début bas", + "label-position-inside-middle": "Intérieur milieu", + "label-position-inside-middle-top": "Intérieur milieu haut", + "label-position-inside-middle-bottom": "Intérieur milieu bas", + "label-position-inside-end": "Intérieur fin", + "label-position-inside-end-top": "Intérieur fin haut", + "label-position-inside-end-bottom": "Intérieur fin bas", + "label-background": "Fond du libellé" + }, + "state": { + "states": "États", + "label": "Libellé", + "ticks-value": "Valeur des graduations", + "source": "Source", + "value-range": "Valeur / Plage", + "no-states": "Aucun état configuré", + "add-state": "Ajouter un état", + "type-constant": "Constante", + "type-range": "Plage", + "from": "De", + "to": "À", + "remove-state": "Supprimer l'état" + }, + "grid": { + "grid": "Grille", + "background-color": "Couleur de fond", + "border": "Bordure" + }, + "axis": { + "axes": "Axes", + "x-axis": "Axe X", + "y-axis": "Axe Y", + "y-axis-settings": "Paramètres de l'axe Y", + "comparison-x-axis-settings": "Paramètres de l'axe X de comparaison", + "remove-y-axis": "Supprimer l'axe Y", + "id": "ID", + "label": "Libellé", + "position": "Position", + "position-left": "Gauche", + "position-right": "Droite", + "position-top": "Haut", + "position-bottom": "Bas", + "tick-labels": "Libellés des graduations", + "ticks-formatter-function": "Fonction de formatage des graduations", + "ticks-generator-function": "Fonction de génération des graduations", + "show-ticks": "Afficher les graduations", + "show-line": "Afficher la ligne", + "show-split-lines": "Afficher les lignes de séparation", + "show-split-lines-x-axis-hint": "Si activé, les lignes verticales seront affichées sur le graphique.", + "show-split-lines-y-axis-hint": "Si activé, les lignes horizontales seront affichées sur le graphique.", + "ticks-interval": "Intervalle des graduations", + "ticks-interval-hint": "Définit de manière forcée l'intervalle de segmentation de l'axe.", + "split-number": "Nombre de divisions", + "split-number-hint": "Nombre de segments dans lesquels l'axe est divisé.", + "min": "Min", + "max": "Max", + "show": "Afficher", + "add-y-axis": "Ajouter un axe Y" + }, + "series": { + "legend-settings": "Paramètres de la légende", + "show-in-legend": "Afficher dans la légende", + "show-in-legend-hint": "Afficher le nom de la série et les données dans la légende.", + "hidden-by-default": "Masqué par défaut", + "hidden-by-default-hint": "Masquer la série dans la légende par défaut.", + "series-type": "Type de série", "type": "Type", - "type-debug-rule-chain": "Debug", - "type-debug-rule-node": "Debug", - "type-error": "Erreur", - "type-lc-event": "Evénement du cycle de vie", - "type-stats": "Statistiques", - "all-events": "Tout", - "entity-type": "Type d'entité" - }, - "extension": { - "add": "Ajouter une extension", - "add-attribute": "Ajouter un attribut", - "add-attribute-request": "Ajouter une demande d'attribut", - "add-attribute-update": "Ajouter une mise à jour d'attribut", - "add-broker": "Ajouter un Broker", - "add-config": "Ajouter une configuration de convertisseur", - "add-connect-request": "Ajouter une demande de connexion", - "add-converter": "Ajouter un convertisseur", - "add-device": "Ajouter un dispositif", - "add-disconnect-request": "Ajouter une demande de déconnexion", - "add-map": "Ajouter un élément de mappage", - "add-server-side-rpc-request": "Ajouter une requête RPC côté serveur", - "add-timeseries": "Ajouter des timeseries", - "anonymous": "Anonyme", - "attr-json-key-expression": "Expression json de la clé d'attribut", - "attr-topic-key-expression": "Expression du topic de la clé d'attribut", - "attribute-filter": "Filtre d'attribut", - "attribute-key-expression": "Expression de clé d'attribut", - "attribute-requests": "Demandes d'attributs", - "attribute-updates": "Mises à jour des attributs", - "attributes": "Attributs", - "basic": "Basic", - "brokers": "Brokers", - "ca-cert": "Fichier de certificat CA", - "cert": "Fichier de certificat *", - "client-scope": "Portée client", - "configuration": "Configuration", - "connect-requests": "Demandes de connexion", - "converter-configurations": "Configurations du convertisseur", - "converter-id": "ID du convertisseur", - "converter-json": "Json", - "converter-json-parse": "Impossible d'analyser le convertisseur json.", - "converter-json-required": "Le convertisseur json est requis.", - "converter-type": "Type de convertisseur", - "converters": "Convertisseurs", - "credentials": "Informations d'identification", - "custom": "Sur mesure", - "delete": "Supprimer l'extension", - "delete-extension-text": "Attention, après la confirmation, l'extension et toutes les données associées deviendront irrécupérables.", - "delete-extension-title": "Êtes-vous sûr de vouloir supprimer l'extension '{{extensionId}}'?", - "delete-extensions-text": "Attention, après la confirmation, toutes les extensions sélectionnées seront supprimées.", - "delete-extensions-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 extension} other {# extensions} }?", - "device-name-expression": "expression du nom du dispositif", - "device-name-filter": "Filtre de nom de dispositif", - "device-type-expression": "expression de type de dispositif", - "disconnect-requests": "Demandes de déconnection", - "drop-file": "Déposez un fichier ou cliquez pour sélectionner un fichier à télécharger.", - "edit": "Modifier l'extension", - "export-extension": "Exporter l'extension", - "export-extensions-configuration": "Exporter la configuration des extensions", - "extension-id": "Id de l'extension", - "extension-type": "Type d'extension", - "extensions": "Extensions", - "field-required": "Le champ est obligatoire", - "file": "Fichier d'extensions", - "filter-expression": "Expression du filtre", - "host": "Hôte", - "id": "Id", - "import-extension": "Importer une extension", - "import-extensions": "Importer des extensions", - "import-extensions-configuration": "Importer la configuration des extensions", - "invalid-file-error": "Fichier d'extension non valide", - "json-name-expression": "Expression json du nom du dispositif", - "json-parse": "Impossible d'analyser json transformer.", - "json-required": "Transformer json est requis.", - "json-type-expression": "Expression json du type de dispositif", - "key": "Clé", - "mapping": "Mappage", - "method-filter": "Filtre de méthode", - "modbus-add-server": "Ajouter serveur/esclave", - "modbus-add-server-prompt": "Veuillez ajouter serveur/esclave", - "modbus-attributes-poll-period": "Période d'interrogation des attributs (ms)", - "modbus-baudrate": "Débit en bauds", - "modbus-byte-order": "Ordre des octets", - "modbus-databits": "Bits de données", - "modbus-databits-range": "Les bits de données doivent être compris entre 7 et 8.", - "modbus-device-name": "Nom du dispositif", - "modbus-encoding": "Encodage", - "modbus-function": "Fonction", - "modbus-parity": "parité", - "modbus-poll-period": "Période d'interrogation (ms)", - "modbus-poll-period-range": "La période d'interrogation doit être une valeur positive.", - "modbus-port-name": "Nom du port série", - "modbus-register-address": "Adresse du registre", - "modbus-register-address-range": "L'adresse du registre doit être comprise entre 0 et 65535.", - "modbus-register-bit-index": "Bit index", - "modbus-register-bit-index-range": "L'index de bit doit être compris entre 0 et 15.", - "modbus-register-count": "Nombre de registre", - "modbus-register-count-range": "Le nombre de registres doit être une valeur positive.", - "modbus-server": "Serveurs / esclaves", - "modbus-stopbits": "Bits d'arrêt", - "modbus-stopbits-range": "Les bits d'arrêt doivent être compris entre 1 et 2.", - "modbus-tag": "Tag", - "modbus-timeseries-poll-period": "Période d'interrogation des Timeseries (ms)", - "modbus-transport": "Transport", - "modbus-unit-id": "Id de l'unité", - "modbus-unit-id-range": "L'ID de l'unité doit être compris entre 1 et 247.", - "no-file": "Aucun fichier sélectionné.", - "opc-add-server": "Ajouter un serveur", - "opc-add-server-prompt": "Veuillez ajouter un serveur", - "opc-application-name": "Nom de l'application", - "opc-application-uri": "Uri de l'application", - "opc-device-name-pattern": "modèle de nom du dispositif", - "opc-device-node-pattern": "modèle de noeud de dispositif", - "opc-identity": "Identité", - "opc-keystore": "Magasin de clés", - "opc-keystore-alias": "Alias", - "opc-keystore-key-password": "Mot de passe de la clé", - "opc-keystore-location": "Emplacement *", - "opc-keystore-password": "Mot de passe", - "opc-keystore-type": "Type", - "opc-scan-period-in-seconds": "Période d'analyse en secondes", - "opc-security": "Sécurité", - "opc-server": "Serveurs", - "opc-type": "Type", - "password": "Mot de passe", - "pem": "PEM", - "port": "Port", - "port-range": "Le port doit être compris entre 1 et 65535.", - "private-key": "Fichier de clé privée *", - "request-id-expression": "Expression de demande d'id", - "request-id-json-expression": "Expression json de la demande d'id", - "request-id-topic-expression": "Expression de la demande d'id du topic", - "request-topic-expression": "Expression de la demande du topic", - "response-timeout": "Délai de réponse en millisecondes", - "response-topic-expression": "Expression du topic de la réponse", - "retry-interval": "Intervalle de nouvelle tentative en millisecondes", - "selected-extensions": "{count, plural, =1 {1 extension} other {# extensions} } sélectionné", - "server-side-rpc": "RPC côté serveur", - "ssl": "Ssl", - "sync": { - "last-sync-time": "Dernière heure de synchronisation", - "not-available": "Non disponible", - "not-sync": "Non sync", - "status": "Status", - "sync": "Sync" + "type-line": "Ligne", + "type-bar": "Barre", + "line": { + "line": "Ligne", + "show-line": "Afficher la ligne", + "step-line": "Ligne par étapes", + "step-type-start": "Début", + "step-type-middle": "Milieu", + "step-type-end": "Fin", + "smooth-line": "Ligne lissée" }, - "timeout": "Délai d'attente en millisecondes", - "timeseries": "Timeseries", - "to-double": "Au double", - "token": "Jeton de sécurité", - "topic": "Topic", - "topic-expression": "Expression du topic", - "topic-filter": "Filtre du topic", - "topic-name-expression": "Expression du nom du dispositif (topic)", - "topic-type-expression": "Expression de type de dispositif (topic)", - "transformer": "Transformer", - "transformer-json": "JSON *", - "type": "Type", - "unique-id-required": "L'identifiant d'extension actuel existe déjà.", - "username": "Nom d'utilisateur", - "value": "Valeur", - "value-expression": "Expression de la valeur" - }, - "fullscreen": { - "exit": "Quitter le plein écran", - "expand": "Afficher en plein écran", - "fullscreen": "Plein écran", - "toggle": "Activer le mode plein écran" - }, - "function": { - "function": "Fonction" - }, - "grid": { - "add-item-text": "Ajouter un nouvel élément", - "delete-item": "Supprimer l'élément", - "delete-item-text": "Faites attention, après la confirmation, cet élément et toutes les données associées deviendront irrécupérables.", - "delete-item-title": "Êtes-vous sûr de vouloir supprimer cet élément?", - "delete-items": "Supprimer les éléments", - "delete-items-action-title": "Supprimer {count, plural, =1 {1 élément} other {# éléments} }", - "delete-items-text": "Attention, après la confirmation, tous les éléments sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-items-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 élément} other {# éléments} }?", - "item-details": "Détails de l'élément", - "no-items-text": "Aucun élément trouvé", - "scroll-to-top": "Défiler vers le haut" - }, - "help": { - "goto-help-page": "Aller à la page d'aide" + "point": { + "points": "Points", + "show-points": "Afficher les points", + "point-label": "Libellé du point", + "point-label-hint": "Afficher un libellé avec la valeur sur le point de la série.", + "point-label-background": "Fond du libellé du point", + "point-shape": "Forme du point", + "point-size": "Taille du point" + } + } + }, + "wind-speed-direction": { + "layout": "Disposition", + "layout-default": "Par défaut", + "layout-advanced": "Avancé", + "layout-simplified": "Simplifié", + "values": "Valeurs", + "wind-direction": "Direction du vent", + "center-value": "Valeur centrale", + "icon": "Icône", + "arrow": "Flèche", + "ticks": "Graduations", + "labels-type": "Type de libellés", + "directional-names": "Noms directionnels", + "degrees": "Degrés", + "major-ticks": "Graduations majeures", + "minor-ticks": "Graduations mineures", + "wind-speed-direction-card-style": "Style de carte vitesse et direction du vent", + "ticks-color": "Couleur des graduations", + "ticks-labels-type": "Type de libellés des graduations", + "arrow-color": "Couleur de la flèche" + }, + "value-source": { + "value-source": "Source de valeur", + "predefined-value": "Constante", + "entity-attribute": "Attribut de l'entité", + "value": "Valeur", + "value-required": "La valeur est requise.", + "key-required": "La clé est requise.", + "entity-key-required": "La clé de l'entité est requise.", + "source-entity-alias": "Alias de l'entité source", + "source-entity-attribute": "Attribut de l'entité source", + "type-constant": "Constante", + "type-latest-key": "Clé", + "type-entity": "Entité" + }, + "rpc-state": { + "initial-state": "État initial", + "initial-state-hint": "Action pour obtenir l'état initial (Activé/Désactivé) du composant.", + "disabled-state": "État désactivé", + "disabled-state-hint": "Configurer la condition dans laquelle le composant est désactivé.", + "turn-on": "Allumer", + "turn-on-hint": "Action déclenchée lorsque le curseur passe sur 'Activé'", + "turn-off": "Éteindre", + "turn-off-hint": "Action déclenchée lorsque le curseur passe sur 'Désactivé'", + "on": "Activé", + "off": "Désactivé", + "disabled": "Désactivé" + }, + "value-action": { + "do-nothing": "Ne rien faire", + "execute-rpc": "Exécuter RPC", + "get-attribute": "Obtenir un attribut", + "set-attribute": "Définir un attribut", + "get-time-series": "Obtenir une série temporelle", + "get-alarm-status": "Obtenir le statut de l'alarme", + "get-dashboard-state": "Obtenir l'identifiant de l'état du tableau de bord", + "get-dashboard-state-object": "Obtenir l'objet d'état du tableau de bord", + "add-time-series": "Ajouter une série temporelle", + "execute-rpc-text": "Exécuter la méthode RPC '{{methodName}}'", + "get-time-series-text": "Utiliser la série temporelle '{{key}}'", + "get-attribute-text": "Utiliser l'attribut '{{key}}'", + "get-alarm-status-text": "Utiliser le statut de l'alarme", + "get-dashboard-state-text": "Utiliser l'état du tableau de bord", + "get-dashboard-state-object-text": "Utiliser l'objet d'état du tableau de bord", + "when-dashboard-state-is-text": "Lorsque l'identifiant d'état du tableau de bord est '{{state}}'", + "when-dashboard-state-function-is-text": "Lorsque f(identifiant d'état du tableau de bord) est '{{state}}'", + "when-dashboard-state-object-function-is-text": "Lorsque f(objet d'état du tableau de bord) est '{{state}}'", + "set-attribute-to-value-text": "Définir l'attribut '{{key}}' sur : {{value}}", + "add-time-series-value-text": "Ajouter la valeur {{value}} à la série temporelle '{{key}}'", + "set-attribute-text": "Définir l'attribut '{{key}}'", + "add-time-series-text": "Ajouter la série temporelle '{{key}}'", + "action": "Action", + "value": "Valeur", + "init-value-hint": "Valeur qui sera définie jusqu'à ce que l'appareil envoie des données.", + "method": "Méthode", + "method-name-required": "Le nom de la méthode est requis.", + "request-timeout-ms": "Délai d'attente de la requête RPC (ms)", + "request-timeout-required": "Le délai d'attente est requis.", + "min-request-timeout-error": "La valeur du délai d'attente doit être supérieure ou égale à 5000 ms (5 secondes).", + "request-persistent": "Requête RPC persistante", + "persistent-polling-interval": "Intervalle d'interrogation persistant (ms)", + "persistent-polling-interval-hint": "Intervalle (ms) pour obtenir une réponse de commande RPC persistante", + "persistent-polling-interval-required": "L'intervalle d'interrogation persistant est requis.", + "min-persistent-polling-interval-error": "L'intervalle d'interrogation persistant doit être supérieur ou égal à 1000 ms (1 seconde).", + "attribute-scope": "Portée de l'attribut", + "attribute-key": "Clé de l'attribut", + "attribute-key-required": "La clé de l'attribut est requise.", + "time-series-key": "Clé de la série temporelle", + "time-series-key-required": "La clé de la série temporelle est requise.", + "action-result-converter": "Convertisseur de résultat d'action", + "converter-none": "Aucun", + "converter-function": "Fonction", + "converter-constant": "Constante", + "converter-value": "Valeur", + "parse-value-function": "Fonction d'analyse de la valeur", + "state-when-result-is": "'{{state}}' lorsque le résultat est", + "parameters": "Paramètres", + "convert-value-function": "Fonction de conversion de la valeur", + "error": { + "target-entity-is-not-set": "L'entité cible n'est pas définie !", + "failed-to-perform-action": "Échec de l'exécution de l'action {{ actionLabel }}.", + "invalid-attribute-scope": "La portée de l'attribut '{{scope}}' n'est pas prise en charge pour l'entité {{entityType}}." + } + }, + "widget-font": { + "font-settings": "Paramètres de police", + "font-family": "Famille de police", + "size": "Taille", + "relative-font-size": "Taille de police relative (pourcentage)", + "font-style": "Style", + "font-style-normal": "Normal", + "font-style-italic": "Italique", + "font-style-oblique": "Oblique", + "font-weight": "Poids", + "font-weight-normal": "Normal", + "font-weight-bold": "Gras", + "font-weight-bolder": "Plus gras", + "font-weight-lighter": "Plus léger", + "color": "Couleur", + "shadow-color": "Couleur de l'ombre", + "preview": "Aperçu", + "line-height": "Hauteur de ligne", + "auto": "Auto" }, "home": { - "avatar": "Avatar", - "home": "Accueil", - "logout": "Déconnexion", - "menu": "Menu", - "open-user-menu": "Ouvrir le menu utilisateur", - "profile": "Profile" - }, - "icon": { - "icon": "Icône", - "material-icons": "Icônes matérielles", - "select-icon": "Sélectionner l'icône", - "show-all": "Afficher toutes les icônes" + "no-data-available": "Aucune donnée disponible" }, - "import": { - "drop-file": "Déposez un fichier JSON ou cliquez pour sélectionner un fichier à télécharger.", - "no-file": "Aucun fichier sélectionné" + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "Disque", + "cpu-warning-text": "Utilisation élevée du processeur. Pour éviter une panne du système, optimisez les performances.", + "cpu-critical-text": "Utilisation critique du processeur. Pour éviter une panne du système, optimisez les performances.", + "ram-warning-text": "Réserve de RAM faible. Pour éviter une panne du système, optimisez les performances ou augmentez la taille de la RAM.", + "ram-critical-text": "Réserve critique de RAM. Pour éviter une panne du système, optimisez les performances ou augmentez la taille de la RAM.", + "disk-warning-text": "Espace disque faible. Pour éviter une perte de données, libérez ou augmentez l'espace disque.", + "disk-critical-text": "Espace disque critique. Pour éviter une perte de données, libérez ou augmentez l'espace disque." }, - "item": { - "selected": "Sélectionné" + "cluster-info": { + "service-id": "ID du service", + "service-type": "Type de service", + "no-data": "Aucune donnée" }, - "js-func": { - "no-return-error": "La fonction doit renvoyer une valeur!", - "return-type-mismatch": "La fonction doit renvoyer une valeur de type '{{type}}' !", - "tidy": "Nettoyer" + "transport-messages": { + "title": "Messages de transport", + "info": "Tous les messages provenant des appareils" }, - "key-val": { - "add-entry": "Ajouter une entrée", - "key": "Clé", - "no-data": "Aucune entrée", - "remove-entry": "Supprimer l'entrée", - "value": "Valeur" + "activity": { + "title": "Activité" }, - "language": { - "language": "Language" + "documentation": { + "title": "Documentation", + "add-link": "Ajouter un lien", + "add-link-title": "Ajouter un lien de documentation", + "name": "Nom", + "name-required": "Le nom est requis.", + "link": "Lien", + "link-required": "Le lien est requis.", + "columns": "Colonnes" }, - "layout": { - "color": "Couleur", - "layout": "Mise en page", - "main": "Principal", - "manage": "Gérer les mises en page", - "right": "Droite", - "select": "Sélectionner la mise en page cible", - "settings": "Paramètres de mise en page" - }, - "legend": { - "avg": "moy", - "max": "max", - "min": "min", - "position": "Position de la légende", - "settings": "Paramètres de la légende", - "show-avg": "Afficher la valeur moyenne", - "show-max": "Afficher la valeur maximale", - "show-min": "Afficher la valeur min", - "show-total": "Afficher la valeur totale", - "total": "total" - }, - "login": { - "create-password": "Créer un mot de passe", - "email": "Email", - "forgot-password": "Mot de passe oublié?", - "login": "Connexion", - "new-password": "Nouveau mot de passe", - "new-password-again": "nouveau mot de passe", - "password-again": "Mot de passe à nouveau", - "password-link-sent-message": "Le lien de réinitialisation du mot de passe a été envoyé avec succès!", - "password-reset": "Mot de passe réinitialisé", - "passwords-mismatch-error": "Les mots de passe saisis doivent être identiques!", - "remember-me": "Se souvenir de moi", - "request-password-reset": "Demander la réinitialisation du mot de passe", - "reset-password": "Réinitialiser le mot de passe", - "sign-in": "Veuillez vous connecter", - "username": "Nom d'utilisateur (courriel)", - "login-with": "Se connecter avec {{name}}", - "or": "ou" - }, - "position": { - "bottom": "Bas", - "left": "Gauche", - "right": "Droite", - "top": "Haut" - }, - "profile": { - "change-password": "Modifier le mot de passe", - "current-password": "Mot de passe actuel", - "last-login-time": "Dernière connexion", - "profile": "Profile" - }, - "relation": { - "add": "Ajouter une relation", - "add-relation-filter": "Ajouter un filtre de relation", - "additional-info": "Informations supplémentaires (JSON)", - "any-relation": "toute relation", - "any-relation-type": "N'importe quel type", - "delete": "Supprimer la relation", - "delete-from-relation-text": "Attention, après la confirmation, l'entité actuelle ne sera pas liée à l'entité '{{entityName}}'.", - "delete-from-relation-title": "Êtes-vous sûr de vouloir supprimer la relation de l'entité '{{entityName}}'?", - "delete-from-relations-text": "Attention, après la confirmation, toutes les relations sélectionnées seront supprimées et l'entité actuelle ne sera pas liée aux entités correspondantes.", - "delete-from-relations-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 relation} other {# relations} }?", - "delete-to-relation-text": "Attention, après la confirmation, l'entité '{{entityName}} ne sera plus liée à l'entité actuelle.", - "delete-to-relation-title": "Êtes-vous sûr de vouloir supprimer la relation avec l'entité '{{entityName}}'?", - "delete-to-relations-text": "Attention, après la confirmation, toutes les relations sélectionnées seront supprimées et les entités correspondantes ne seront pas liées à l'entité en cours.", - "delete-to-relations-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 relation} other {# relations} }?", - "direction": "Sens", - "direction-type": { - "FROM": "de", - "TO": "à" + "quick-links": { + "title": "Liens rapides", + "add-link": "Ajouter un lien", + "add-link-title": "Ajouter un lien rapide", + "quick-link": "Lien rapide", + "quick-link-required": "Le lien rapide est requis.", + "no-links-matching": "Aucun lien correspondant à '{{name}}' trouvé.", + "columns": "Colonnes" + }, + "recent-dashboards": { + "title": "Tableaux de bord", + "last": "Dernière consultation", + "starred": "Favoris", + "name": "Nom", + "last-viewed": "Dernière consultation", + "no-last-viewed-dashboards": "Aucun tableau de bord consulté récemment" + }, + "configured-features": { + "title": "Fonctionnalités configurées", + "info": "Statut des fonctionnalités nécessitant une configuration", + "email-feature": "E-mail", + "sms-feature": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "Authentification à deux facteurs", + "feature-configured": "Fonctionnalité configurée.\nCliquez pour configurer", + "feature-not-configured": "Fonctionnalité non configurée.\nCliquez pour configurer" + }, + "version-info": { + "title": "Version", + "contact-us": "Contactez-nous", + "current-version": "Version actuelle", + "current": "Actuelle", + "available-version": "Version disponible", + "available": "Disponible", + "upgrade": "Mettre à jour", + "version-is-up-to-date": "La version est à jour" + }, + "usage-info": { + "title": "Utilisation", + "entities": "Entités", + "api-calls": "Appels API" + }, + "functions": { + "title": "Fonctions", + "pe-feature-tooltip": "Disponible uniquement dans ThingsBoard\nÉdition Professionnelle", + "switch-to-pe": "Passer à l'Édition Pro", + "alarms": "Alarmes", + "dashboards": "Tableaux de bord", + "entities-and-relations": "Entités et relations", + "profiles": "Profils", + "advanced-features": "Fonctionnalités avancées", + "notification-center": "Centre de notifications", + "api-usage": "Utilisation de l'API", + "customers": "Clients", + "customers-hierarchy": "Hiérarchie des clients", + "roles-and-permissions": "Rôles et permissions", + "groups": "Groupes", + "integrations": "Intégrations", + "solution-templates": "Modèles de solution", + "scheduler": "Planificateur", + "white-labeling": "Personnalisation de marque" + }, + "devices": { + "view-docs": "Voir la documentation", + "inactive": "Inactif", + "active": "Actif", + "total": "Total" + }, + "alarms": { + "critical": "Critique", + "assigned-to-me": "Assignées à moi", + "total": "Total" + }, + "getting-started": { + "get-started": "Commencer", + "finish": "Terminer", + "done-welcome-title": "Bienvenue à bord", + "done-welcome-text": "Vous avez réussi avec brio !", + "sys-admin": { + "step1": { + "title": "Créer un Locataire et un Administrateur de Locataire", + "content": "

Un locataire est une personne ou une organisation qui possède ou produit des dispositifs et des actifs. Le locataire peut avoir plusieurs administrateurs, clients, dispositifs et actifs.

L'administrateur de locataire peut créer et gérer des dispositifs, actifs, clients et tableaux de bord dans le compte du locataire.

Suivez la documentation pour savoir comment procéder :

", + "how-to-create-tenant": "Comment créer un Locataire et un Administrateur de Locataire" }, - "edit": "Modifier la relation", - "from-entity": "De l'entité", - "from-entity-name": "Du nom d'entité", - "from-entity-type": "Du type d'entité", - "from-relations": "Relations sortantes", - "invalid-additional-info": "Impossible d'analyser les informations supplémentaires json.", - "relation-filters": "Filtres de relation", - "relation-type": "Type de relation", - "relation-type-required": "Le type de relation est requis.", - "relations": "Relations", - "remove-relation-filter": "Supprimer le filtre de relation", - "search-direction": { - "FROM": "De", - "TO": "Vers" + "step2": { + "title": "Configurer la fonctionnalité : Serveur de messagerie", + "content": "

La configuration du serveur de messagerie est essentielle pour l'activation des utilisateurs, la récupération de mot de passe et la livraison des notifications d'alarme.

Suivez la documentation pour savoir comment procéder :

", + "how-to-configure-mail-server": "Comment configurer le serveur de messagerie" }, - "selected-relations": "{count, plural, =1 {1 relation} other {# relations} } sélectionné", - "to-entity": "Vers l'entité", - "to-entity-name": "vers le nom de l'entité", - "to-entity-type": "Vers le type d'entité", - "to-relations": "Relations entrantes", - "type": "Type" - }, - "rulechain": { - "add": "Ajouter une chaîne de règles", - "add-rulechain-text": "Ajouter une nouvelle chaîne de règles", - "copyId": "Copier l'identifiant de la chaîne de règles", - "create-new-rulechain": "Créer une nouvelle chaîne de règles", - "debug-mode": "Mode de débogage", - "delete": "Supprimer la chaîne de règles", - "delete-rulechain-text": "Attention, après la confirmation, la chaîne de règles et toutes les données associées deviendront irrécupérables.", - "delete-rulechain-title": "Voulez-vous vraiment supprimer la chaîne de règles '{{ruleChainName}}'?", - "delete-rulechains-action-title": "Supprimer {count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} }", - "delete-rulechains-text": "Attention, après la confirmation, toutes les chaînes de règles sélectionnées seront supprimées et toutes les données associées deviendront irrécupérables.", - "delete-rulechains-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} }?", - "description": "Description", - "details": "Détails", - "events": "Evénements", - "export": "Exporter la chaîne de règles", - "export-failed-error": "Impossible d'exporter la chaîne de règles: {{error}}", - "idCopiedMessage": "L'ID de la chaîne de règles a été copié dans le presse-papier", - "import": "Importer la chaîne de règles", - "invalid-rulechain-file-error": "Impossible d'importer la chaîne de règles: structure de données de la chaîne de règles non valide", - "management": "Gestion des règles", - "name": "Nom", - "name-required": "Le nom est requis.", - "no-rulechains-matching": "Aucune chaîne de règles correspondant à {{entity}} n'a été trouvée.", - "no-rulechains-text": "Aucune chaîne de règles trouvée", - "root": "Racine", - "rulechain": "Chaîne de règles", - "rulechain-details": "Détails de la chaîne de règles", - "rulechain-file": "Fichier de chaîne de règles", - "rulechain-required": "Chaîne de règles requise", - "rulechains": "Chaînes de règles", - "select-rulechain": "Sélectionner la chaîne de règles", - "set-root": "Rendre la chaîne de règles racine (root) ", - "set-root-rulechain-text": "Après la confirmation, la chaîne de règles deviendra racine (root) et gérera tous les messages de transport entrants.", - "set-root-rulechain-title": "Voulez-vous vraiment que la chaîne de règles '{{ruleChainName}} soit racine (root) ?", - "system": "Système", - "assign-rulechains": "Attribuer aux chaînes de règles", - "delete-rulechains": "Supprimer une chaînes de règles", - "unassign-rulechain": "Retirer chaîne de règles", - "unassign-rulechains": "Retirer chaînes de règles", - "unassign-rulechain-title": "AÊtes-vous sûr de vouloir retirer l'attribution de chaînes de règles '{{ruleChainName}}'?", - "unassign-rulechain-from-edge-text": "Après la confirmation, l'actif sera non attribué et ne sera pas accessible a la bordure.", - "unassign-rulechains-from-edge-action-title": "Retirer {count, plural, =1 {1 chaîne de règles} other {# chaînes de règles} } de la bordure", - "unassign-rulechains-from-edge-text": "Après la confirmation, tous les chaînes de règles sélectionnés ne seront pas attribués et ne seront pas accessibles a la bordure.", - "assign-rulechain-to-edge-title": "Attribuer les chaînes de règles a la bordure", - "assign-rulechain-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les chaînes de règles", - "set-edge-template-root-rulechain": "Rendre le modèle de bord de chaîne de règles racine", - "set-edge-template-root-rulechain-title": "Voulez-vous vraiment définir la racine du modèle d'arête de la chaîne de règles '{{ruleChainName}}'?", - "set-edge-template-root-rulechain-text": "Après la confirmation, la chaîne de règles deviendra la racine du modèle d'arête et sera la chaîne de règles racine pour les arêtes nouvellement créées.", - "invalid-rulechain-type-error": "Impossible d'importer la chaîne de règles: type de chaîne de règles non valide. Le type attendu est {{attenduRuleChainType}}.", - "set-auto-assign-to-edge": "Attribuer une chaîne de règles aux arêtes lors de la création", - "set-auto-assign-to-edge-title": "Voulez-vous vraiment attribuer automatiquement la chaîne de règles d'arête '{{ruleChainName}}' à l'arête (s) lors de la création?", - "set-auto-assign-to-edge-text": "Après la confirmation, la chaîne de règles d'arête sera automatiquement affectée à l'arête (s) lors de la création.", - "unset-auto-assign-to-edge": "Non défini, attribuer une chaîne de règles aux arêtes lors de la création", - "unset-auto-assign-to-edge-title": "Voulez-vous vraiment annuler l'attribution de la chaîne de règles d'arête \"{{ruleChainName}}\" aux arêtes lors de la création?", - "unset-auto-assign-to-edge-text": "Après la confirmation, la chaîne de règles d'arêtes ne sera plus automatiquement affectée aux arêtes lors de la création.", - "edge-template-root": "Racine du modèle", - "search": "Rechercher des chaînes de règles", - "selected-rulechains": "{count, plural, =1 {1 rule chain} other {# rule chains} } sélectionné", - "open-rulechain": "Ouvrir la Chaîne de règles", - "assign-to-edge": "Attribuer à Bordure", - "edge-rulechain": "Chaîne de règles Bordure", - "unassign-rulechains-from-edge-title": "Voulez-vous vraiment annuler l'attribution de {count, plural, =1 {1 rulechain} other {# rulechains} }?" - }, - "rulenode": { - "add": "Ajouter un noeud de règle", - "add-link": "Ajouter un lien", - "configuration": "Configuration", - "copy-selected": "Copier les éléments sélectionnés", - "create-new-link-label": "Créez un nouveau!", - "custom-link-label": "Etiquette de lien personnalisée", - "custom-link-label-required": "Une étiquette de lien personnalisée est requise", - "debug-mode": "Mode de débogage", - "delete": "Supprimer le noeud de règle", - "delete-selected": "Supprimer les éléments sélectionnés", - "delete-selected-objects": "Supprimer les nœuds et les connexions sélectionnés", - "deselect-all": "Désélectionner tout", - "deselect-all-objects": "Désélectionnez tous les nœuds et toutes les connexions", - "details": "Détails", - "directive-is-not-loaded": "La directive de configuration définie '{{directiveName}} n'est pas disponible.", - "events": "Événements", - "help": "Aide", - "invalid-target-rulechain": "Impossible de résoudre la chaîne de règles cible!", - "link": "Lien", - "link-details": "Détails du lien du noeud de la règle", - "link-label": "Étiquette du lien", - "link-label-required": "L'étiquette du lien est obligatoire", - "link-labels": "Étiquettes de lien", - "link-labels-required": "Les étiquettes de lien sont obligatoires", - "message": "Message", - "message-type": "Type de message", - "message-type-required": "Le type de message est obligatoire", - "metadata": "Métadonnées", - "metadata-required": "Les entrées de métadonnées ne peuvent pas être vides.", - "name": "Nom", - "name-required": "Le nom est requis.", - "no-link-label-matching": "'{{label}}' introuvable.", - "no-link-labels-found": "Aucune étiquette de lien trouvée", - "open-node-library": "Ouvrir la bibliothèque de noeud", - "output": "Output", - "rulenode-details": "Détails du noeud de la régle", - "search": "Recherche de noeuds", - "select-all": "Tout sélectionner", - "select-all-objects": "Sélectionnez tous les noeuds et connexions", - "select-message-type": "Sélectionner le type de message", - "test": "Test", - "test-script-function": "Tester le script", - "type": "Type", - "type-action": "Action", - "type-action-details": "Effectuer une action spéciale", - "type-enrichment": "Enrichissement", - "type-enrichment-details": "Ajouter des informations supplémentaires dans les métadonnées de message", - "type-external": "Externe", - "type-external-details": "Interagit avec le systéme externe", - "type-filter": "Filtre", - "type-filter-details": "Filtrer les messages entrants avec des conditions configurées", - "type-input": "Input", - "type-input-details": "Entrée logique de la chaîne de règles, transmet les messages entrants au prochain nœud de règle associé", - "type-rule-chain": "Chaîne de régles", - "type-rule-chain-details": "Transmet les messages entrants à la chaîne de régles spécifiée", - "type-transformation": "Transformation", - "type-transformation-details": "Modifier le payload du message et les métadonnées ", - "type-unknown": "Inconnu", - "type-unknown-details": "Noeud de règle non résolu", - "ui-resources-load-error": "Impossible de charger les ressources de configuration de l'interface utilisateur." - }, - "timezone": { - "timezone": "Fuseau horaire", - "select-timezone": "Sélectionnez le fuseau horaire", - "no-timezones-matching": "Aucun fuseau horaire correspondant à '{{timezone}}' n'a été trouvé.", - "timezone-required": "Le fuseau horaire est requis." - }, - "tenant": { - "add": "Ajouter un Tenant", - "add-tenant-text": "Ajouter un nouveau Tenant", - "admins": "Admins", - "copyId": "Copier l'Id du Tenant", - "delete": "Supprimer le Tenant", - "delete-tenant-text": "Attention, après la confirmation, le Tenant et toutes les données associées deviendront irrécupérables.", - "delete-tenant-title": "Êtes-vous sûr de vouloir supprimer le tenant '{{tenantTitle}}'?", - "delete-tenants-action-title": "Supprimer {count, plural, =1 {1 tenant} other {# tenants} }", - "delete-tenants-text": "Attention, après la confirmation, tous les Tenants sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-tenants-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 tenant} other {# tenants} }?", - "description": "Description", - "details": "Détails", - "events": "Événements", - "idCopiedMessage": "L'Id du Tenant a été copié dans le Presse-papiers", - "manage-tenant-admins": "Gérer les administrateurs du Tenant", - "management": "Gestion des Tenants", - "no-tenants-matching": "Aucun Tenant correspondant à {{entity}} n'a été trouvé. ", - "no-tenants-text": "Aucun Tenant trouvé", - "select-tenant": "Sélectionner un Tenant", - "tenant": "Tenant", - "tenant-details": "Détails du Tenant", - "tenant-required": "Tenant requis", - "tenants": "Tenants", - "title": "Titre", - "title-required": "Le titre est requis.", - "search": "Rechercher les Tenants", - "selected-tenants": "{ count, plural, =1 {1 tenant} other {# tenants} } sélectionnés" - }, - "timeinterval": { - "advanced": "Avancé", - "days": "Jours", - "days-interval": "{days, plural, =1 {1 jour} other {# jours} }", - "hours": "Heures", - "hours-interval": "{hours, plural, =1 {1 heure} other {# heures} }", - "minutes": "Minutes", - "minutes-interval": "{minutes, plural, =1 {1 minute} other {# minutes} }", - "seconds": "Secondes", - "seconds-interval": "{seconds, plural, =1 {1 seconde} other {# secondes} }" - }, - "timewindow": { - "date-range": "Plage de dates", - "days": "{days, plural, =1 {jour} other {# jours} }", - "edit": "Modifier timewindow", - "history": "Historique", - "hours": "{hours, plural, =0 {heure} =1 {1 heure} other {# heures} }", - "last": "Dernier", - "last-prefix": "dernier", - "minutes": "{minutes, plural, =0 {minute} =1 {1 minute} other {# minutes} }", - "period": "de {{startTime}} à {{endTime}}", - "realtime": "Temps réel", - "seconds": "{seconds, plural, =0 {second} =1 {1 second} other {# seconds} }", - "time-period": "Période", - "hide": "Masquer" - }, - "user": { - "activation-email-sent-message": "Le courriel d'activation a été envoyé avec succès!", - "activation-link": "Lien d'activation utilisateur", - "activation-link-copied-message": "le lien d'activation de l'utilisateur a été copié dans le presse-papier", - "activation-link-text": "Pour activer l'utilisateur, utilisez le lien d'activation suivant: ", - "activation-method": "Méthode d'activation", - "add": "Ajouter un utilisateur", - "add-user-text": "Ajouter un nouvel utilisateur", - "always-fullscreen": "Toujours en plein écran", - "anonymous": "Anonyme", - "copy-activation-link": "Copier le lien d'activation", - "customer": "Client", - "customer-users": "Utilisateurs du client", - "default-dashboard": "Tableau de bord par défaut", - "delete": "Supprimer l'utilisateur", - "delete-user-text": "Attention, après la confirmation, l'utilisateur et toutes les données associées deviendront irrécupérables.", - "delete-user-title": "Êtes-vous sûr de vouloir supprimer l'utilisateur '{{userEmail}}'?", - "delete-users-action-title": "Supprimer {count, plural, =1 {1 utilisateur} other {# utilisateurs} }", - "delete-users-text": "Attention, après la confirmation, tous les utilisateurs sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-users-title": "Êtes-vous sûr de vouloir supprimer {count, plural, =1 {1 utilisateur} other {# utilisateurs} }?", - "description": "Description", - "details": "Détails", - "disable-account": "Désactiver le compte d'utilisateur", - "disable-account-message": "Le compte d'utilisateur a été désactivé avec succès!", - "display-activation-link": "Afficher le lien d'activation", - "email": "Email", - "email-required": "Email est requis.", - "enable-account": "Activer le compte d'utilisateur", - "enable-account-message": "Le compte d'utilisateur a été activé avec succès!", - "first-name": "Prénom", - "invalid-email-format": "Format de courrier électronique non valide", - "last-name": "Nom de famille", - "login-as-customer-user": "Se connecter en tant qu'utilisateur client", - "login-as-tenant-admin": "Connectez-vous en tant qu'administrateur Tenant", - "no-users-matching": "Aucun utilisateur correspondant à '{{entity}}' n'a été trouvé.", - "no-users-text": "Aucun utilisateur trouvé", - "resend-activation": "Renvoyer l'activation", - "select-user": "Sélectionner l'utilisateur", - "send-activation-mail": "Envoyer un mail d'activation", - "sys-admin": "Administrateur du système", - "tenant-admin": "Administrateur du Tenant", - "tenant-admins": "administrateurs du Tenant", - "user": "utilisateur", - "user-details": "Détails de l'utilisateur", - "user-required": "L'utilisateur est requis", - "users": "Utilisateurs", - "search": "Rechercher des utilisateurs", - "selected-users": "{ count, plural, =1 {1 user} other {# users} } sélectionnés" - }, - "value": { - "boolean": "booléen", - "boolean-value": "Valeur booléenne", - "double": "Double", - "double-value": "Valeur double", - "false": "Faux", - "integer": "Entier", - "integer-value": "Valeur entière", - "invalid-integer-value": "Valeur entière invalide", - "long": "Long", - "string": "String", - "string-value": "Valeur String", - "true": "Vrai", - "type": "Type de valeur" - }, - "widget": { - "add": "Ajouter un widget", - "add-resource": "Ajouter une ressource", - "add-widget-type": "Ajouter un nouveau type de widget", - "alarm": "Widget d'alarme", - "css": "CSS", - "datakey-settings-schema": "Schéma des paramètres de Data key", - "edit": "Modifier le widget", - "editor": " Editeur de widget", - "export": "Exporter widget", - "html": "HTML", - "javascript": "Javascript", - "latest": "Dernières valeurs", - "management": "Gestion des widgets", - "missing-widget-title-error": "Le titre du widget doit être spécifié!", - "no-data-found": "Aucune donnée trouvée", - "remove": "Supprimer le widget", - "remove-resource": "Supprimer une ressource", - "remove-widget-text": "Après la confirmation, le widget et toutes les données associées deviendront irrécupérables.", - "remove-widget-title": "Êtes-vous sûr de vouloir supprimer le widget '{{widgetTitle}}'?", - "resource-url": "URL JavaScript / CSS", - "resources": "Ressources", - "rpc": "Widget de contrôle", - "run": "Exécuter un widget", - "save": "Enregistrer le widget", - "save-widget-type-as": "Enregistrer le type de widget sous", - "save-widget-type-as-text": "Veuillez saisir un nouveau titre de widget et / ou sélectionner un ensemble de widgets cibles", - "saveAs": "Enregistrer le widget sous", - "search-data": "Rechercher des données", - "select-widget-type": "Sélectionnez le type de widget", - "select-widgets-bundle": "Sélectionner un ensemble de widgets", - "settings-schema": "Schéma des paramétres", - "static": "Widget statique", - "tidy": "Nettoyer", - "timeseries": "Séries chronologiques", - "title": "Titre du widget", - "title-required": "Le titre du widget est requis.", - "toggle-fullscreen": "Basculer le mode plein écran", - "type": "Type de widget", - "unable-to-save-widget-error": "Impossible de sauvegarder le widget! Le widget a des erreurs!", - "undo": "Annuler les modifications du widget", - "widget-bundle": "Ensemble de widget", - "widget-library": "Bibliothèque de widgets", - "widget-saved": "Widget enregistré", - "widget-template-load-failed-error": "Impossible de charger le modéle de widget!", - "widget-type-load-error": "Le widget n'a pas été chargé à cause des erreurs suivantes:", - "widget-type-not-found": "Problème de chargement de la configuration du widget.
Le type de widget associé a probablement été supprimé.", - "no-data": "Aucune donnée à afficher sur le widget" - }, - "widget-action": { - "custom": "Action personnalisée", - "header-button": "Bouton d'en-tête de widget", - "open-dashboard": "Naviguer vers un autre tableau de bord", - "open-dashboard-state": "Naviguer vers un nouvel état du tableau de bord", - "open-right-layout": "Ouvrir la disposition du tableau de bord droite (vue mobile)", - "set-entity-from-widget": "Définir l'entité à partir du widget", - "target-dashboard": "Tableau de bord cible", - "target-dashboard-state": "État du tableau de bord cible", - "target-dashboard-state-required": "L'état du tableau de bord cible est requis", - "update-dashboard-state": "Mettre à jour l'état actuel du tableau de bord" - }, - "widget-config": { - "action": "Action", - "action-icon": "Icône", - "action-name": "Nom", - "action-name-not-unique": "Une autre action portant le même nom existe déjà. \n Le nom de l'action doit être unique dans la même source d'action.", - "action-name-required": "Le nom de l'action est requis", - "action-source": "Source de l'action", - "action-source-required": "Une source d'action est requise.", - "action-type": "Type", - "action-type-required": "Le type d'action est requis.", - "actions": "Actions", - "add-action": "Ajouter une action", - "add-datasource": "Ajouter une source de données", - "advanced": "Avancé", - "alarm-source": "Source d'alarme", - "background-color": "couleur de fond", - "data": "Données", - "datasource-parameters": "Paramètres", - "datasource-type": "Type", - "datasources": "Sources de données", - "decimals": "Nombre de chiffres après virgule flottante", - "delete-action": "Supprimer l'action", - "delete-action-text": "Êtes-vous sûr de vouloir supprimer l'action du widget nommé '{{actionName}}'?", - "delete-action-title": "Supprimer l'action du widget", - "display-timewindow": "Afficher fenêtre de temps", - "display-legend": "Afficher la légende", - "display-title": "Afficher le titre", - "drop-shadow": "Ombre portée", - "edit-action": "Modifier l'action", - "enable-fullscreen": "Activer le plein écran", - "general-settings": "Paramètres généraux", - "height": "Hauteur", - "margin": "Marge", - "maximum-datasources": "Maximum {count, plural, =1 {1 datasource est autorisé.} other {# datasources sont autorisés} }", - "mobile-mode-settings": "Paramètres du mode mobile", - "order": "Ordre", - "padding": "Padding", - "remove-datasource": "Supprimer la source de données", - "search-actions": "Recherche d'actions", - "settings": "Paramètres", - "target-device": "Dispositif cible", - "text-color": "Couleur du texte", - "timewindow": "Fenêtre de temps", - "title": "Titre", - "title-style": "Style de titre", - "title-tooltip": "Tooltip de titre", - "units": "Symbole spécial à afficher à côté de la valeur", - "use-dashboard-timewindow": "Utiliser la fenêtre de temps du tableau de bord", - "widget-style": "Style du widget", - "display-icon": "Afficher l'icône du titre", - "icon-color": "Couleur de l'icône", - "icon-size": "Taille de l'icône" - }, - "widget-type": { - "create-new-widget-type": "Créer un nouveau type de widget", - "export": "Exporter le type de widget", - "export-failed-error": "Impossible d'exporter le type de widget: {{error}}", - "import": "Importer le type de widget", - "invalid-widget-type-file-error": "Impossible d'importer le type de widget: structure de données de type widget invalide.", - "widget-type-file": "Fichier de type Widget" - }, - "widgets": { - "date-range-navigator": { - "localizationMap": { - "Sun": "Dim.", - "Mon": "Lun.", - "Tue": "Mar.", - "Wed": "Mer.", - "Thu": "Jeu.", - "Fri": "Ven.", - "Sat": "Sam.", - "Jan": "Janv.", - "Feb": "Févr.", - "Mar": "Mars", - "Apr": "Avr.", - "May": "Mai", - "Jun": "Juin", - "Jul": "Juil.", - "Aug": "Août", - "Sep": "Sept.", - "Oct": "Oct.", - "Nov": "Nov.", - "Dec": "Déc.", - "January": "Janvier", - "February": "Février", - "March": "Mars", - "April": "Avril", - "June": "Juin", - "July": "Juillet", - "August": "Août", - "September": "Septembre", - "October": "Octobre", - "November": "Novembre", - "December": "Décembre", - "Custom Date Range": "Plage de dates personnalisée", - "Date Range Template": "Modèle de plage de dates", - "Today": "Aujourd'hui", - "Yesterday": "Hier", - "This Week": "Cette semaine", - "Last Week": "La semaine dernière", - "This Month": "Ce mois-ci", - "Last Month": "Le mois dernier", - "Year": "Année", - "This Year": "Cette année", - "Last Year": "L'année dernière", - "Date picker": "Sélecteur de date", - "Hour": "Heure", - "Day": "Journée", - "Week": "La semaine", - "2 weeks": "2 Semaines", - "Month": "Mois", - "3 months": "3 Mois", - "6 months": "6 Mois", - "Custom interval": "Intervalle personnalisé", - "Interval": "Intervalle", - "Step size": "Taille de pas", - "Ok": "Ok" - } + "step3": { + "title": "Configurer la fonctionnalité : Fournisseur SMS", + "content": "

Configurez les fournisseurs SMS pour notifier les clients des alarmes par SMS.

Suivez la documentation pour savoir comment procéder :

", + "how-to-configure-sms-provider": "Comment configurer le fournisseur SMS" }, - "input-widgets": { - "attribute-not-allowed": "Le paramètre d'attribut ne peut pas être utilisé dans ce widget", - "date": "Date", - "discard-changes": "Annuler les modifications", - "entity-attribute-required": "L'attribut d'entité est requis", - "entity-timeseries-required": "Entité timeseries est requis", - "not-allowed-entity": "L'entité sélectionnée ne peut pas avoir d'attributs partagés", - "no-attribute-selected": "Aucun attribut n'est sélectionné", - "no-datakey-selected": "Aucune date n'est sélectionnée", - "no-entity-selected": "Aucune entité sélectionnée", - "no-image": "Pas d'image", - "no-support-web-camera": "Pas de webcam supportée", - "no-timeseries-selected": "Aucune série temporelle sélectionnée", - "switch-attribute-value": "Changer la valeur de l'attribut d'entité", - "switch-camera": "Changer de caméra", - "switch-timeseries-value": "Changer la valeur de l'entité série temporelle", - "take-photo": "Prendre une photo", - "time": "Temps", - "timeseries-not-allowed": "Le paramètre série temporelle ne peut pas être utilisé dans ce widget", - "update-failed": "Mise à jour a échoué", - "update-successful": "Mise à jour réussie", - "update-attribute": "Attribut de mise à jour", - "update-timeseries": "Mise à jour de la série temporelle", - "value": "Valeur" + "step4": { + "title": "Configurer la fonctionnalité : Marque blanche", + "content": "

Personnalisez facilement le logo et le schéma de couleurs de votre entreprise ou produit, sans codage et sans redémarrer le service.

Suivez la documentation pour savoir comment procéder :

" + }, + "step5": { + "title": "Configurer la fonctionnalité : 2FA", + "content": "

Améliorez la sécurité des comptes de la plateforme avec l'authentification à deux facteurs.

Suivez la documentation pour savoir comment procéder :

" + }, + "step6": { + "title": "Configurer la fonctionnalité : OAuth 2", + "content": "

Simplifiez la connexion pour les utilisateurs locataires et clients avec la fonctionnalité Single Sign-On via OAuth 2.0.

Suivez la documentation pour savoir comment procéder :

" } - }, - "widgets-bundle": { - "add": "Ajouter un groupe de widgets", - "add-widgets-bundle-text": "Ajouter un nouveau groupe de widgets", - "create-new-widgets-bundle": "Créer un nouveau groupe de widgets", - "current": "Groupe actuel", - "delete": "Supprimer le groupe de widgets", - "delete-widgets-bundle-text": "Attention, après la confirmation, le groupe de widgets et toutes les données associées deviendront irrécupérables.", - "delete-widgets-bundle-title": "Êtes-vous sûr de vouloir supprimer le groupe de widgets '{{widgetsBundleTitle}}'?", - "delete-widgets-bundles-action-title": "Supprimer {count, plural, =1 {1 groupe de widgets} other {# groupes de widgets} }", - "delete-widgets-bundles-text": "Attention, après la confirmation, tous les groupes de widgets sélectionnés seront supprimés et toutes les données associées deviendront irrécupérables.", - "delete-widgets-bundles-title": "Voulez-vous vraiment supprimer {count, plural, =1 {1 groupe de widgets} other {# groupes de widgets} }?", - "details": "Détails", - "empty": "Le groupe de widgets est vide", - "export": "Exporter le groupe de widgets", - "export-failed-error": "Impossible d'exporter le groupe de widgets: {{error}}", - "import": "Importer un groupe de widgets", - "invalid-widgets-bundle-file-error": "Impossible d'importer un groupe de widgets: structure de données du groupe de widgets non valides.", - "no-widgets-bundles-matching": "Aucun groupe de widgets correspondant à {{widgetsBundle}} n'a été trouvé.", - "no-widgets-bundles-text": "Aucun groupe de widgets trouvé", - "system": "Système", - "title": "Titre", - "title-required": "Le titre est requis.", - "widgets-bundle-details": "Détails des groupes de widgets", - "widgets-bundle-file": "Fichier de groupe de widgets", - "widgets-bundle-required": "Un groupe de widgets est requis.", - "widgets-bundles": "Groupes de widgets" + }, + "tenant-admin": { + "step1": { + "title": "Créer un dispositif", + "content": "

Approvisionnons votre premier dispositif sur la plateforme via l'interface utilisateur. Suivez la documentation pour savoir comment procéder :

", + "how-to-create-device": "Comment créer un dispositif" + }, + "step2": { + "title": "Connecter le dispositif", + "content-before": "

Pour connecter le dispositif, vous devez obtenir ses identifiants. Nous recommandons d'utiliser les identifiants auto-générés par défaut, à savoir le jeton d'accès pour ce guide.

  • Allez dans le tableau des dispositifs
  • Cliquez sur la ligne du dispositif pour ouvrir les détails
  • Appuyez sur le bouton \"Copier le jeton d'accès\"

Utilisez les commandes simples suivantes pour publier des données via HTTP. N'oubliez pas de remplacer $ACCESS_TOKEN par le jeton d'accès de votre dispositif :

", + "ubuntu": { + "install-curl": "Installer cURL pour Ubuntu :" + }, + "macos": { + "install-curl": "Installer cURL pour macOS :" + }, + "windows": { + "install-curl": "Depuis Windows 10 b17063, cURL est disponible par défaut." + }, + "replace-access-token": "Remplacez $ACCESS_TOKEN par le jeton de votre dispositif :", + "content-after": "

Vous pouvez également utiliser d'autres protocoles tels que MQTT, CoAP, etc.

Suivez la documentation pour savoir comment procéder :

", + "how-to-connect-device": "Comment connecter un dispositif" + }, + "step3": { + "title": "Créer un tableau de bord", + "content": "

Créez un tableau de bord pour visualiser les données des entités telles que les dispositifs, les actifs, etc.

Suivez la documentation pour savoir comment procéder :

", + "how-to-create-dashboard": "Comment créer un tableau de bord" + }, + "step4": { + "title": "Configurer des règles d'alarme", + "alarm-rules": "Règles d'alarme", + "content": "

Déclenchons une alarme lorsque la température atteint 25°C. Suivez la documentation pour savoir comment procéder :

", + "how-to-configure-alarm-rules": "Comment configurer des règles d'alarme" + }, + "step5": { + "title": "Créer une alarme", + "content-before": "

Pour déclencher l'alarme, envoyez une nouvelle valeur de télémétrie de 26°C ou plus.

", + "replace-access-token": "Remplacez $ACCESS_TOKEN par le jeton de votre dispositif :", + "content-after": "

Suivez la documentation pour savoir comment procéder :

", + "how-to-create-alarm": "Comment créer une alarme" + }, + "step6": { + "title": "Créer un client et partager le tableau de bord", + "content": "

En créant des tableaux de bord pour les utilisateurs finaux, un utilisateur client ne peut voir que ses propres dispositifs. Les données d'autres clients lui seront invisibles.

Suivez la documentation pour savoir comment procéder :

" + } + } + } + }, + "icon": { + "icon": "Icône", + "icons": "Icônes", + "select-icon": "Sélectionner une icône", + "material-icons": "Icônes Material", + "show-all": "Afficher toutes les icônes", + "search-icon": "Rechercher une icône", + "no-icons-found": "Aucune icône trouvée pour '{{iconSearch}}'" + }, + "phone-input": { + "phone-input-label": "Numéro de téléphone", + "phone-input-required": "Le numéro de téléphone est requis", + "phone-input-validation": "Le numéro de téléphone est invalide ou impossible", + "phone-input-pattern": "Numéro de téléphone invalide. Doit être au format E.164, ex. {{phoneNumber}}", + "phone-input-hint": "Numéro de téléphone au format E.164, ex. {{phoneNumber}}" + }, + "custom": { + "widget-action": { + "action-cell-button": "Bouton d'action de cellule", + "row-click": "Au clic sur la ligne", + "cell-click": "Au clic sur la cellule", + "polygon-click": "Au clic sur le polygone", + "marker-click": "Au clic sur le marqueur", + "circle-click": "Au clic sur le cercle", + "tooltip-tag-action": "Action de balise info-bulle", + "node-selected": "Lorsqu'un nœud est sélectionné", + "element-click": "Au clic sur l'élément HTML", + "pie-slice-click": "Au clic sur une tranche", + "row-double-click": "Double clic sur la ligne", + "cell-double-click": "Double clic sur la cellule", + "card-click": "Au clic sur la carte", + "click": "Au clic" + } + }, + "paginator": { + "items-per-page": "Éléments par page :", + "first-page-label": "Première page", + "last-page-label": "Dernière page", + "next-page-label": "Page suivante", + "previous-page-label": "Page précédente", + "items-per-page-separator": "sur" + }, + "language": { + "language": "Langue", + "locales": { + "ar_AE": "العربية (الإمارات العربية المتحدة)", + "ca_ES": "català (Espanya)", + "cs_CZ": "čeština (Česko)", + "da_DK": "dansk (Danmark)", + "de_DE": "Deutsch (Deutschland)", + "el_GR": "Ελληνικά (Ελλάδα)", + "en_US": "English (United States)", + "es_ES": "español (España)", + "fa_IR": "فارسی (ایران)", + "fr_FR": "français (France)", + "it_IT": "italiano (Italia)", + "ja_JP": "日本語 (日本)", + "ka_GE": "ქართული (საქართველო)", + "ko_KR": "한국어 (대한민국)", + "lt_LT": "lietuvių (Lietuva)", + "lv_LV": "latviešu (Latvija)", + "nl_BE": "Nederlands (België)", + "pl_PL": "polski (Polska)", + "pt_BR": "português (Brasil)", + "ro_RO": "română (România)", + "sl_SI": "slovenščina (Slovenija)", + "tr_TR": "Türkçe (Türkiye)", + "uk_UA": "українська (Україна)", + "zh_CN": "中文 (中国)", + "zh_TW": "中文 (台灣)" } -} + } +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-it_IT.json b/ui-ngx/src/assets/locale/locale.constant-it_IT.json index 9b00f36842..f976a76052 100644 --- a/ui-ngx/src/assets/locale/locale.constant-it_IT.json +++ b/ui-ngx/src/assets/locale/locale.constant-it_IT.json @@ -1,1703 +1,9224 @@ { - "access": { - "unauthorized": "Non autorizzato", - "unauthorized-access": "Accesso non autorizzato", - "unauthorized-access-text": "Devi effettuare il login per accedere a questa risorsa!", - "access-forbidden": "Accesso Vietato", - "access-forbidden-text": "Non hai i diritti di accesso a questa posizione!
Prova ad effettuare il login con un diverso account.", - "refresh-token-expired": "Sessione scaduta", - "refresh-token-failed": "Impossibile aggiornare la sessione" - }, - "action": { - "activate": "Attiva", - "suspend": "Sospendi", - "save": "Salva", - "saveAs": "Salva come", - "cancel": "Cancella", - "ok": "OK", - "delete": "Elimina", - "add": "Aggiungi", - "yes": "Sì", - "no": "No", - "update": "Aggiorna", - "remove": "Rimuovi", - "search": "Cerca", - "clear-search": "Cancella ricerca", - "assign": "Assegna", - "unassign": "Annulla assegnazione", - "share": "Condividi", - "make-private": "Rendi privato", - "apply": "Applica", - "apply-changes": "Applica modifiche", - "edit-mode": "Modalità modifica", - "enter-edit-mode": "Attiva modalità di modifica", - "decline-changes": "Annulla modifiche", - "close": "Chiudi", - "back": "Indietro", - "run": "Esegui", - "sign-in": "Registrati!", - "edit": "Modifica", - "view": "Visualizza", - "create": "Crea", - "drag": "Trascina", - "refresh": "Aggiorna", - "undo": "Annulla", - "copy": "Copia", - "paste": "Incolla", - "copy-reference": "Copia riferimento", - "paste-reference": "Incolla riferimento", - "import": "Importa", - "export": "Esporta", - "share-via": "Condividi con {{provider}}", - "discard-changes": "Annulla le modifiche" - }, - "aggregation": { - "aggregation": "Aggregazione", - "function": "Funzione di aggregazione dati", - "limit": "Valori max", - "group-interval": "Intervallo di raggruppamento", - "min": "Min", - "max": "Max", - "avg": "Media", - "sum": "Somma", - "count": "Conteggio", - "none": "Nessuna" - }, - "admin": { - "general": "Generale", - "general-settings": "Impostazioni Generali", - "outgoing-mail": "Posta in uscita", - "outgoing-mail-settings": "Impostazioni Posta in uscita", - "system-settings": "Impostazioni di sistema", - "test-mail-sent": "Mail di test inviata con successo!", - "base-url": "URL di base", - "base-url-required": "URL di base obbligatoria.", - "mail-from": "Mittente", - "mail-from-required": "Mittente obbligatorio.", - "smtp-protocol": "Protocollo SMTP", - "smtp-host": "Host SMTP", - "smtp-host-required": "Host SMTP obbligatorio.", - "smtp-port": "Porta SMTP", - "smtp-port-required": "Porta SMTP obbligatoria.", - "smtp-port-invalid": "Numero di porta SMTP non valido.", - "timeout-msec": "Timeout (msec)", - "timeout-required": "Timeout obbligatorio.", - "timeout-invalid": "Timeout non valido.", - "enable-tls": "Abilita TLS", - "tls-version" : "Versione TLS", - "send-test-mail": "Invia mail di test", - "security-settings": "Settaggi di sicurezza", - "password-policy": "Politica password", - "minimum-password-length": "Lunghezza minima password", - "minimum-password-length-required": "È richiesta una lunghezza minima della password", - "minimum-password-length-range": "La lunghezza minima della password deve essere compresa tra 5 e 50", - "minimum-uppercase-letters": "Numero minimo di lettere maiuscole", - "minimum-uppercase-letters-range": "Il numero minimo di lettere maiuscole non può essere negativo", - "minimum-lowercase-letters": "Numero minimo di lettere minuscole", - "minimum-lowercase-letters-range": "Il numero minimo di lettere minuscole non può essere negativo", - "minimum-digits": "Numero minimo di cifre", - "minimum-digits-range": "Il numero minimo di cifre non può essere negativo", - "minimum-special-characters": "Numero minimo di caratteri speciali", - "minimum-special-characters-range": "Il numero minimo di caratteri speciali non può essere negativo", - "password-expiration-period-days": "Periodo di scadenza della password in giorni", - "password-expiration-period-days-range": "Il periodo di scadenza della password in giorni non può essere negativo", - "password-reuse-frequency-days": "Frequenza di riutilizzo della password in giorni", - "password-reuse-frequency-days-range": "La frequenza di riutilizzo della password in giorni non può essere negativa", - "general-policy": "Politica generale", - "max-failed-login-attempts": "Numero massimo di tentativi di accesso non riusciti, prima che l'account sia bloccato", - "minimum-max-failed-login-attempts-range": "Il numero massimo di tentativi di accesso non riusciti non può essere negativo", - "user-lockout-notification-email": "In caso di blocco dell'account utente, inviare una notifica via e-mail" - }, - "alarm": { - "alarm": "Allarme", - "alarms": "Allarmi", - "select-alarm": "Seleziona un allarme", - "no-alarms-matching": "Nessun allarme corrispondente a '{{entity}}' è stato trovato.", - "alarm-required": "Allarme richiesto", - "alarm-status": "Stato Allarme", - "search-status": { - "ANY": "Qualsiasi", - "ACTIVE": "Attivo", - "CLEARED": "Cancellato", - "ACK": "Riconosciuto", - "UNACK": "Non riconosciuto" - }, - "display-status": { - "ACTIVE_UNACK": "Attivo Non riconosciuto", - "ACTIVE_ACK": "Attivo Riconosciuto", - "CLEARED_UNACK": "Cancellato Non riconosciuto", - "CLEARED_ACK": "Cancellato Riconosciuto" - }, - "no-alarms-prompt": "Nessun allarme trovato", - "created-time": "Orario di creazione", - "type": "Tipo", - "severity": "Livello di gravità", - "originator": "Origine", - "originator-type": "Tipo origine", - "details": "Dettagli", - "status": "Stato", - "alarm-details": "Dettagli allarme", - "start-time": "Ora inizio", - "end-time": "Ora fine", - "ack-time": "Ora conferma", - "clear-time": "Ora cancellazione", - "severity-critical": "Critico", - "severity-major": "Maggiore", - "severity-minor": "Minore", - "severity-warning": "Avviso", - "severity-indeterminate": "Indeterminato", - "acknowledge": "Conferma", - "clear": "Cancella", - "search": "Cerca allarmi", - "selected-alarms": "{ count, plural, =1 {1 allarme selezionato} other {# allarmi selezionati} }", - "no-data": "Nessun dato da visualizzare", - "polling-interval": "Intervallo di polling (sec) Allarmi", - "polling-interval-required": "Intervallo di polling Allarmi richiesto.", - "min-polling-interval-message": "L'intervallo di polling deve essere di almeno 1 sec.", - "aknowledge-alarms-title": "Conferma { count, plural, =1 {1 allarme} other {# allarmi} }", - "aknowledge-alarms-text": "Sei sicuro di voler confermare { count, plural, =1 {1 allarme} other {# allarmi} }?", - "aknowledge-alarm-title": "Conferma allarme", - "aknowledge-alarm-text": "Sei sicuro di voler confermare l'allarme?", - "clear-alarms-title": "Elimina { count, plural, =1 {1 allarme} other {# allarmi} }", - "clear-alarms-text": "Sei sicuro di voler eliminare { count, plural, =1 {1 allarme} other {# allarmi} }?", - "clear-alarm-title": "Elimina allarme", - "clear-alarm-text": "Sei sicuro di voler eliminare l'allarme?", - "alarm-status-filter": "Filtro stato allarme" - }, - "alias": { - "add": "Aggiungi alias", - "edit": "Modifica alias", - "name": "Nome Alias", - "name-required": "Nome Alias obbligatorio", - "duplicate-alias": "Un Alias con lo stesso nome è già presente.", - "filter-type-single-entity": "Singola entità", - "filter-type-entity-list": "Lista Entità", - "filter-type-entity-name": "Nome Entità", - "filter-type-state-entity": "Entità dallo stato della dashboard", - "filter-type-state-entity-description": "Entità prelevata dai parametri di stato della dashboard", - "filter-type-asset-type": "Tipo di Asset", - "filter-type-asset-type-description": "Asset di tipo '{{assetType}}'", - "filter-type-asset-type-and-name-description": "Asset di tipo '{{assetTypes}}' e con un nome che inizia per '{{prefix}}'", - "filter-type-device-type": "Tipo di dispositivo", - "filter-type-device-type-description": "Dispositivi di tipo '{{deviceTypes}}'", - "filter-type-device-type-and-name-description": "Dispositivi di tipo '{{deviceTypes}}' e con un nome che inizia per '{{prefix}}'", - "filter-type-entity-view-type": "Tipo vista entità", - "filter-type-entity-view-type-description": "Viste entità di tipo '{{entityViewTypes}}'", - "filter-type-entity-view-type-and-name-description": "Viste entità di tipo '{{entityViewTypes}}' e con un nome che inizia per '{{prefix}}'", - "filter-type-relations-query": "Query relazioni", - "filter-type-relations-query-description": "{{entities}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-asset-search-query": "Query ricerca asset", - "filter-type-asset-search-query-description": "Asset di tipo {{assetTypes}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-device-search-query": "Query ricerca dispositivo", - "filter-type-device-search-query-description": "Dispositivi di tipo {{deviceTypes}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-entity-view-search-query": "Query ricerca Vista entità", - "filter-type-entity-view-search-query-description": "Viste entità di tipo {{entityViewTypes}} che hanno una relazione {{relationType}} {{direction}} {{rootEntity}}", - "entity-filter": "Filtro entità", - "resolve-multiple": "Risolvi come entità multiple", - "filter-type": "Tipo di filtro", - "filter-type-required": "Tipo di filtro richiesto.", - "entity-filter-no-entity-matched": "Nessuna entità corrispondente al filtro specificato è stata trovata.", - "no-entity-filter-specified": "Nessun filtro di entità specificato", - "root-state-entity": "Usa l'entità di stato della dashboard come radice", - "root-entity": "Entità radice", - "state-entity-parameter-name": "Nome parametro entità di stato", - "default-state-entity": "Entità di stato predefinita", - "default-entity-parameter-name": "Predefinito", - "max-relation-level": "Massimo livello relazione", - "unlimited-level": "Illimitato", - "state-entity": "Entità di stato della dashboard", - "all-entities": "Tutte le entità", - "any-relation": "qualsiasi" - }, - "asset": { - "asset": "Asset", - "assets": "Asset", - "management": "Gestione Asset", - "view-assets": "Visualizza Asset", - "add": "Aggiungi Asset", - "assign-to-customer": "Assegna a cliente", - "assign-asset-to-customer": "Assegna Asset al Cliente", - "assign-asset-to-customer-text": "Seleziona gli asset da assegnare al cliente", - "no-assets-text": "Nessun asset trovato", - "assign-to-customer-text": "Seleziona il cliente a cui assegnare l'asset / gli asset", - "public": "Pubblico", - "assignedToCustomer": "Assegnato al cliente", - "make-public": "Rendi pubblico l'asset", - "make-private": "Rendi privato l'asset", - "unassign-from-customer": "Assegnazione annullata dal cliente", - "delete": "Cancella asset", - "asset-public": "L'Asset è pubblico", - "asset-type": "Tipo di Asset", - "asset-type-required": "Tipo di Asset richiesto.", - "select-asset-type": "Seleziona tipo di asset", - "enter-asset-type": "Inserisci tipo di asset", - "any-asset": "Qualsiasi asset", - "no-asset-types-matching": "Nessun asset corrispondente al tipo '{{entitySubtype}}' è stato trovato.", - "asset-type-list-empty": "Nessun tipo di asset selezionato.", - "asset-types": "Tipi di Asset", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "description": "Descrizione", - "type": "Tipo", - "type-required": "Tipo obbligatorio.", - "details": "Dettagli", - "events": "Eventi", - "add-asset-text": "Aggiungi un nuovo asset", - "asset-details": "Dettagli Asset", - "assign-assets": "Assegna asset", - "assign-assets-text": "Assegna { count, plural, =1 {1 asset} other {# asset} } al cliente", - "delete-assets": "Cancella asset", - "unassign-assets": "Annulla assegnazione asset", - "unassign-assets-action-title": "Annulla assegnazione { count, plural, =1 {1 asset} other {# asset} } al cliente", - "assign-new-asset": "Assegna un nuovo asset", - "delete-asset-title": "Sei sicuro di voler cancellare l'asset '{{assetName}}'?", - "delete-asset-text": "Attenzione, dopo la conferma l'asset e tutti i relativi dati non saranno più recuperabili.", - "delete-assets-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 asset} other {# asset} }?", - "delete-assets-action-title": "Elimina { count, plural, =1 {1 asset} other {# asset} }", - "delete-assets-text": "Attenzione, dopo la modifica tutti gli asset selezionati saranno rimossi e tutti i relativi dati non saranno più recuperabili.", - "make-public-asset-title": "Sei sicuro di voler rendere pubblico l'asset '{{assetName}}'?", - "make-public-asset-text": "Dopo la conferma l'asset e tutti i suoi dati saranno resi pubblici e accessibili dagli altri.", - "make-private-asset-title": "Sei sicuro di voler rendere privato l'asset '{{assetName}}'?", - "make-private-asset-text": "Dopo la conferma l'asset e tutti i suoi dati saranno resi privati e non accessibili dagli altri.", - "unassign-asset-title": "Sei sicuro di voler annullare l'assegnazione dell'asset '{{assetName}}'?", - "unassign-asset-text": "Dopo la conferma l'assegnazione dell'asset sarà annullata e l'asset non sarà più accessibile dal cliente.", - "unassign-asset": "Annulla assegnazione asset", - "unassign-assets-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, =1 {1 asset} other {# asset} }?", - "unassign-assets-text": "Dopo la conferma sarà annullata l'assegnazione di tutti gli asset selezionati e questi non saranno più accessibili dal cliente.", - "copyId": "Copia Id asset", - "idCopiedMessage": "Id Asset copiato negli Appunti", - "select-asset": "Seleziona asset", - "no-assets-matching": "Nessun asset corrispondente a '{{entity}}' è stato trovato.", - "asset-required": "Asset obbligatorio", - "name-starts-with": "Asset con nome che inizia per", - "label": "Etichetta" - }, - "attribute": { - "attributes": "Attributi", - "latest-telemetry": "Ultima telemetria", - "attributes-scope": "Visibilità attributi entità", - "scope-telemetry": "Telemetria", - "scope-latest-telemetry": "Ultima telemetria", - "scope-client": "Attributi client", - "scope-server": "Attributi server", - "scope-shared": "Attributi condivisi", - "add": "Aggiungi attributo", - "key": "Chiave", - "last-update-time": "Ultimo aggiornamento", - "key-required": "Attributo chiave richiesto.", - "value": "Valore", - "value-required": "Attributo valore richiesto.", - "delete-attributes-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 attributo} other {# attributi} }?", - "delete-attributes-text": "Attenzione, dopo la conferma tutti gli attributi selezionati saranno rimossi.", - "delete-attributes": "Elimina attributi", - "enter-attribute-value": "Inserisci il valore dell'attributo", - "show-on-widget": "Mostra sul widget", - "widget-mode": "Modalità Widget", - "next-widget": "Widget successivo", - "prev-widget": "Widget precedente", - "add-to-dashboard": "Aggiungi alla dashboard", - "add-widget-to-dashboard": "Aggiungi widget alla dashboard", - "selected-attributes": "{ count, plural, =1 {1 attributo selezionato} other {# attributi selezionati} }", - "selected-telemetry": "{ count, plural, =1 {1 unità di telemetria selezionata} other {# unità di telemetria selezionate} }" - }, - "audit-log": { - "audit": "Audit", - "audit-logs": "Log Audit", - "timestamp": "Timestamp", - "entity-type": "Tipo Entità", - "entity-name": "Nome Entità", - "user": "Utente", - "type": "Tipo", - "status": "Stato", - "details": "Dettagli", - "type-added": "Aggiunto", - "type-deleted": "Eliminato", - "type-updated": "Aggiornato", - "type-attributes-updated": "Attributi aggiornati", - "type-attributes-deleted": "Attributi eliminati", - "type-rpc-call": "Chiamata RPC", - "type-credentials-updated": "Credenziali aggiornate", - "type-assigned-to-customer": "Assegnato al Cliente", - "type-unassigned-from-customer": "Assegnazione annullata dal Cliente", - "type-activated": "Attivato", - "type-suspended": "Sospeso", - "type-credentials-read": "Credenziali lette", - "type-attributes-read": "Attributi letti", - "type-relation-add-or-update": "Relazione aggiornata", - "type-relation-delete": "Relazione eliminata", - "type-relations-delete": "Eliminate tutte le relazioni", - "type-alarm-ack": "Confermato", - "type-alarm-clear": "Eliminato", - "type-login": "Accesso", - "type-logout": "Disconnettersi", - "type-lockout": "Bloccato", - "status-success": "Successo", - "status-failure": "Fallito", - "audit-log-details": "Dettaglio log audit", - "no-audit-logs-prompt": "Log non trovati", - "action-data": "Action data", - "failure-details": "Dettagli fallimento", - "search": "Cerca log audit", - "clear-search": "Cancella ricerca" - }, - "confirm-on-exit": { - "message": "Alcune modifiche non sono state salvate. Sei sicuro di voler abbandonare questa pagina?", - "html-message": "Alcune modifiche non sono state salvate.
Sei sicuro di voler abbandonare questa pagina?", - "title": "Modifiche non salvate" - }, - "contact": { - "country": "Nazione", - "city": "Città", - "state": "Stato / Provincia", - "postal-code": "CAP", - "postal-code-invalid": "Formato CAP non valido.", - "address": "Indirizzo", - "address2": "Indirizzo 2", - "phone": "Telefono", + "access": { + "unauthorized": "Non autorizzato", + "unauthorized-access": "Accesso non autorizzato", + "unauthorized-access-text": "Devi accedere per avere accesso a questa risorsa!", + "access-forbidden": "Accesso vietato", + "access-forbidden-text": "Non hai i diritti di accesso a questa posizione!
Prova ad accedere con un altro utente se desideri comunque accedere a questa posizione.", + "refresh-token-expired": "La sessione è scaduta", + "refresh-token-failed": "Impossibile aggiornare la sessione", + "permission-denied": "Permesso negato", + "permission-denied-text": "Non hai i permessi per eseguire questa operazione!" + }, + "account": { + "account": "Account", + "notification-settings": "Impostazioni di notifica" + }, + "action": { + "activate": "Attiva", + "suspend": "Sospendi", + "save": "Salva", + "saveAs": "Salva come", + "move": "Sposta", + "cancel": "Annulla", + "ok": "OK", + "delete": "Elimina", + "add": "Aggiungi", + "yes": "Sì", + "no": "No", + "update": "Aggiorna", + "remove": "Rimuovi", + "search": "Cerca", + "clear-search": "Cancella ricerca", + "assign": "Assegna", + "unassign": "Revoca assegnazione", + "share": "Condividi", + "make-private": "Rendi privato", + "apply": "Applica", + "apply-changes": "Applica modifiche", + "edit-mode": "Modalità modifica", + "enter-edit-mode": "Entra in modalità modifica", + "decline-changes": "Rifiuta modifiche", + "decline": "Rifiuta", + "close": "Chiudi", + "back": "Indietro", + "run": "Esegui", + "sign-in": "Accedi!", + "edit": "Modifica", + "view": "Visualizza", + "create": "Crea", + "drag": "Trascina", + "refresh": "Aggiorna", + "undo": "Annulla", + "copy": "Copia", + "paste": "Incolla", + "copy-reference": "Copia riferimento", + "paste-reference": "Incolla riferimento", + "import": "Importa", + "export": "Esporta", + "share-via": "Condividi tramite {{provider}}", + "select": "Seleziona", + "continue": "Continua", + "discard-changes": "Ignora modifiche", + "download": "Scarica", + "next": "Avanti", + "next-with-label": "Avanti: {{label}}", + "read-more": "Leggi di più", + "hide": "Nascondi", + "test": "Test", + "done": "Fatto", + "print": "Stampa", + "restore": "Ripristina", + "confirm": "Conferma", + "more": "Altro", + "less": "Meno", + "skip": "Salta", + "send": "Invia", + "reset": "Resetta", + "show-more": "Mostra di più", + "dont-show-again": "Non mostrare più", + "see-documentation": "Vedi documentazione", + "clear": "Pulisci", + "upload": "Carica", + "delete-anyway": "Elimina comunque", + "delete-selected": "Elimina selezionati", + "set": "Imposta" + }, + "aggregation": { + "aggregation": "Aggregazione", + "function": "Funzione di aggregazione dei dati", + "limit": "Valori massimi", + "group-interval": "Intervallo di raggruppamento", + "min": "Minimo", + "max": "Massimo", + "avg": "Media", + "sum": "Somma", + "count": "Conteggio", + "none": "Nessuno" + }, + "admin": { + "settings": "Impostazioni", + "general": "Generale", + "general-settings": "Impostazioni generali", + "home-settings": "Impostazioni home", + "home": "Home", + "outgoing-mail": "Server di posta", + "outgoing-mail-settings": "Impostazioni del server di posta in uscita", + "system-settings": "Impostazioni di sistema", + "test-mail-sent": "La mail di test è stata inviata con successo!", + "base-url": "URL base", + "base-url-required": "L'URL base è obbligatorio.", + "prohibit-different-url": "Proibisci l'utilizzo del nome host dall'intestazione della richiesta del client", + "prohibit-different-url-hint": "Questa impostazione dovrebbe essere abilitata negli ambienti di produzione. Può causare problemi di sicurezza se disabilitata", + "device-connectivity": { + "device-connectivity": "Connettività del dispositivo", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPs", + "mqtt": "MQTT", + "mqtts": "MQTTs", + "coap": "COAP", + "coaps": "COAPs", + "hint": "Se i campi host o porta sono vuoti, verrà utilizzato il valore predefinito del protocollo.", + "host": "Host", + "port": "Porta", + "port-pattern": "La porta deve essere un numero intero positivo.", + "port-range": "La porta deve essere compresa tra 1 e 65535." + }, + "mail-from": "Mittente", + "mail-from-required": "Il campo Mittente è obbligatorio.", + "smtp-protocol": "Protocollo SMTP", + "smtp-host": "Host SMTP", + "smtp-host-required": "L'host SMTP è obbligatorio.", + "smtp-port": "Porta SMTP", + "smtp-port-required": "È necessario fornire una porta SMTP.", + "smtp-port-invalid": "Non sembra una porta SMTP valida.", + "timeout-msec": "Timeout (msec)", + "timeout-required": "Il timeout è obbligatorio.", + "timeout-invalid": "Non sembra un valore di timeout valido.", + "enable-tls": "Abilita TLS", + "tls-version": "Versione TLS", + "enable-proxy": "Abilita proxy", + "proxy-host": "Host proxy", + "proxy-host-required": "L'host proxy è obbligatorio.", + "proxy-port": "Porta proxy", + "proxy-port-required": "La porta proxy è obbligatoria.", + "proxy-port-range": "La porta proxy deve essere compresa tra 1 e 65535.", + "proxy-user": "Utente proxy", + "proxy-password": "Password proxy", + "change-password": "Cambia password", + "send-test-mail": "Invia mail di test", + "sms-provider": "Provider SMS", + "sms-provider-settings": "Impostazioni provider SMS", + "sms-provider-type": "Tipo di provider SMS", + "sms-provider-type-required": "Il tipo di provider SMS è obbligatorio.", + "sms-provider-type-aws-sns": "Amazon SNS", + "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "ID Chiave di Accesso AWS", + "aws-access-key-id-required": "L'ID Chiave di Accesso AWS è obbligatorio", + "aws-secret-access-key": "Chiave di Accesso Segreta AWS", + "aws-secret-access-key-required": "La Chiave di Accesso Segreta AWS è obbligatoria", + "aws-region": "Regione AWS", + "aws-region-required": "La regione AWS è obbligatoria", + "number-from": "Numero di telefono mittente", + "number-from-required": "Il numero di telefono mittente è obbligatorio.", + "number-to": "Numero di telefono destinatario", + "number-to-required": "Il numero di telefono destinatario è obbligatorio.", + "phone-number-hint": "Numero di telefono nel formato E.164, es. +19995550123", + "phone-number-hint-twilio": "Numero di telefono nel formato E.164/SID del numero/SID del servizio di messaggistica, es. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Numero di telefono non valido. Deve essere nel formato E.164, es. +19995550123.", + "phone-number-pattern-twilio": "Numero di telefono non valido. Deve essere nel formato E.164/SID del numero/SID del servizio di messaggistica, es. +19995550123/PNXXX/MGXXX.", + "sms-message": "Messaggio SMS", + "sms-message-required": "Il messaggio SMS è obbligatorio.", + "sms-message-max-length": "Il messaggio SMS non può superare i 1600 caratteri", + "twilio-account-sid": "SID Account Twilio", + "twilio-account-sid-required": "Il SID dell'account Twilio è obbligatorio", + "twilio-account-token": "Token Account Twilio", + "twilio-account-token-required": "Il token dell'account Twilio è obbligatorio", + "send-test-sms": "Invia SMS di test", + "test-sms-sent": "L'SMS di test è stato inviato con successo!", + "security-settings": "Impostazioni di sicurezza", + "password-policy": "Politica sulla password", + "minimum-password-length": "Lunghezza minima della password", + "minimum-password-length-required": "La lunghezza minima della password è obbligatoria", + "minimum-password-length-range": "La lunghezza minima della password deve essere compresa tra 6 e 50", + "maximum-password-length": "Lunghezza massima della password", + "maximum-password-length-min": "La lunghezza massima della password deve essere almeno 6", + "maximum-password-length-less-min": "La lunghezza massima della password deve essere maggiore della lunghezza minima", + "minimum-uppercase-letters": "Numero minimo di lettere maiuscole", + "minimum-uppercase-letters-range": "Il numero minimo di lettere maiuscole non può essere negativo", + "minimum-lowercase-letters": "Numero minimo di lettere minuscole", + "minimum-lowercase-letters-range": "Il numero minimo di lettere minuscole non può essere negativo", + "minimum-digits": "Numero minimo di cifre", + "minimum-digits-range": "Il numero minimo di cifre non può essere negativo", + "minimum-special-characters": "Numero minimo di caratteri speciali", + "minimum-special-characters-range": "Il numero minimo di caratteri speciali non può essere negativo", + "password-expiration-period-days": "Periodo di scadenza della password (in giorni)", + "password-expiration-period-days-range": "Il periodo di scadenza della password non può essere negativo", + "password-reuse-frequency-days": "Frequenza di riutilizzo della password (in giorni)", + "password-reuse-frequency-days-range": "La frequenza di riutilizzo della password non può essere negativa", + "allow-whitespace": "Consenti spazi", + "force-reset-password-if-no-valid": "Forza il reset della password se non valida", + "force-reset-password-if-no-valid-hint": "Prestare attenzione quando si abilita questa funzione: richiederà agli utenti con password non valide di reimpostarla tramite email.", + "general-policy": "Politica generale", + "max-failed-login-attempts": "Numero massimo di tentativi di accesso falliti prima del blocco dell'account", + "minimum-max-failed-login-attempts-range": "Il numero massimo di tentativi di accesso falliti non può essere negativo", + "user-lockout-notification-email": "In caso di blocco dell'account utente, inviare una notifica via email", + "user-activation-token-ttl": "Durata TTL del link di attivazione utente (in ore)", + "user-activation-token-ttl-range": "La durata TTL del link di attivazione deve essere compresa tra 1 e 24 ore", + "password-reset-token-ttl": "Durata TTL del link di reimpostazione password (in ore)", + "password-reset-token-ttl-range": "La durata TTL del link di reimpostazione password deve essere compresa tra 1 e 24 ore", + "mobile-secret-key-length": "Lunghezza della chiave segreta mobile", + "mobile-secret-key-length-range": "La lunghezza della chiave segreta mobile deve essere positiva", + "domain-name": "Nome dominio", + "domain-name-unique": "Nome dominio e protocollo devono essere univoci.", + "domain-name-max-length": "Il nome dominio deve essere inferiore a 256 caratteri", + "error-verification-url": "Un nome dominio non deve contenere i simboli '/' e ':'. Esempio: thingsboard.io", + "connection-settings": "Impostazioni di connessione", + "oauth2": { + "access-token-uri": "URI del token di accesso", + "access-token-uri-required": "L'URI del token di accesso è obbligatorio.", + "activate-user": "Attiva utente", + "add-domain": "Aggiungi dominio", + "delete-domain": "Elimina dominio", + "add-provider": "Aggiungi provider", + "delete-provider": "Elimina provider", + "allow-user-creation": "Consenti creazione utente", + "always-fullscreen": "Sempre a schermo intero", + "authorization-uri": "URI di autorizzazione", + "authorization-uri-required": "L'URI di autorizzazione è obbligatorio.", + "add-client": "Aggiungi client OAuth 2.0", + "client-details": "Dettagli client OAuth 2.0", + "client": "Client OAuth 2.0", + "clients": "Client OAuth 2.0", + "no-oauth2-clients": "Nessun client OAuth 2.0 trovato", + "search-oauth2-clients": "Cerca client OAuth 2.0", + "delete-client-title": "Sei sicuro di voler eliminare il client OAuth 2.0 '{{clientName}}'?", + "delete-client-text": "Attenzione, dopo la conferma il client e tutti i dati correlati saranno irrimediabilmente persi.", + "delete-mobile-app-title": "Sei sicuro di voler eliminare l'applicazione mobile '{{applicationName}}'?", + "delete-mobile-app-text": "Attenzione, dopo la conferma l'applicazione mobile e tutti i dati correlati saranno irrimediabilmente persi.", + "title": "Titolo", + "client-title-required": "Il titolo è obbligatorio", + "client-title-max-length": "Il titolo deve essere inferiore a 100 caratteri", + "advanced-settings": "Impostazioni avanzate", + "domain-details": "Dettagli dominio", + "no-domains": "Nessun dominio trovato", + "search-domains": "Cerca domini", + "mobile-app-details": "Dettagli applicazione mobile", + "add-mobile-app": "Aggiungi applicazione mobile", + "no-mobile-apps": "Nessuna applicazione mobile trovata", + "search-mobile-apps": "Cerca applicazioni mobili", + "send-token": "Invia token", + "create-new": "Crea nuovo", + "client-authentication-method": "Metodo di autenticazione client", + "client-id": "ID client", + "client-id-required": "L'ID client è obbligatorio.", + "client-id-max-length": "L'ID client deve essere inferiore a 256 caratteri", + "client-secret": "Segreto client", + "client-secret-required": "Il segreto client è obbligatorio.", + "client-secret-max-length": "Il segreto client deve essere inferiore a 2049 caratteri", + "custom-setting": "Impostazioni personalizzate", + "customer-name-pattern": "Pattern nome cliente", + "customer-name-pattern-max-length": "Il pattern del nome cliente deve essere inferiore a 256 caratteri", + "default-dashboard-name": "Nome dashboard predefinita", + "default-dashboard-name-max-length": "Il nome della dashboard predefinita deve essere inferiore a 256 caratteri", + "delete-domain-text": "Attenzione, dopo la conferma il dominio e tutti i dati del provider non saranno più disponibili.", + "delete-domain-title": "Sei sicuro di voler eliminare il dominio '{{domainName}}'?", + "delete-registration-text": "Attenzione, dopo la conferma i dati del provider non saranno più disponibili.", + "delete-registration-title": "Sei sicuro di voler eliminare il provider '{{name}}'?", + "email-attribute-key": "Chiave attributo email", + "email-attribute-key-required": "La chiave attributo email è obbligatoria.", + "email-attribute-key-max-length": "La chiave attributo email deve essere inferiore a 32 caratteri", + "first-name-attribute-key": "Chiave attributo nome", + "first-name-attribute-key-max-length": "La chiave attributo nome deve essere inferiore a 32 caratteri", + "general": "Generale", + "jwk-set-uri": "URI JSON Web Key", + "last-name-attribute-key": "Chiave attributo cognome", + "last-name-attribute-key-max-length": "La chiave attributo cognome deve essere inferiore a 32 caratteri", + "login-button-icon": "Icona del pulsante di accesso", + "login-button-label": "Etichetta provider", + "login-button-label-placeholder": "Accedi con $(Provider label)", + "login-button-label-required": "L'etichetta è obbligatoria.", + "login-provider": "Provider di accesso", + "mapper": "Mapper", + "new-domain": "Nuovo dominio", + "oauth2": "OAuth 2.0", + "password-max-length": "La password deve essere inferiore a 256 caratteri", + "redirect-uri-template": "Template URI di reindirizzamento", + "copy-redirect-uri": "Copia URI di reindirizzamento", + "registration-id": "ID registrazione", + "registration-id-required": "L'ID di registrazione è obbligatorio.", + "registration-id-unique": "L'ID di registrazione deve essere univoco per il sistema.", + "scope": "Scope", + "scope-required": "Lo scope è obbligatorio.", + "tenant-name-pattern": "Pattern nome tenant", + "tenant-name-pattern-required": "Il pattern nome tenant è obbligatorio.", + "tenant-name-pattern-max-length": "Il pattern nome tenant deve essere inferiore a 256 caratteri", + "tenant-name-strategy": "Strategia nome tenant", + "type": "Tipo di mapper", + "uri-pattern-error": "Formato URI non valido.", + "url": "URL", + "url-pattern": "Formato URL non valido.", + "url-required": "L'URL è obbligatorio.", + "url-max-length": "L'URL deve essere inferiore a 256 caratteri", + "user-info-uri": "URI informazioni utente", + "user-info-uri-required": "L'URI informazioni utente è obbligatorio.", + "username-max-length": "Il nome utente deve essere inferiore a 256 caratteri", + "user-name-attribute-name": "Chiave attributo nome utente", + "user-name-attribute-name-required": "La chiave attributo nome utente è obbligatoria", + "protocol": "Protocollo", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "Abilita impostazioni OAuth 2.0", + "disable": "Disabilita impostazioni OAuth 2.0", + "edge": "Propaga a Edge", + "edge-enable": "Abilita propagazione su Edge", + "edge-disable": "Disabilita propagazione su Edge", + "domains": "Domini", + "mobile-apps": "Applicazioni mobili", + "mobile-package": "Pacchetto applicazione", + "mobile-package-placeholder": "Es.: my.example.app", + "mobile-package-hint": "Per Android: il tuo Application ID univoco. Per iOS: identificatore del bundle di prodotto.", + "mobile-package-unique": "Il pacchetto applicazione deve essere univoco.", + "mobile-package-required": "Il pacchetto applicazione è obbligatorio.", + "mobile-package-max-length": "Il pacchetto applicazione deve essere inferiore a 256 caratteri", + "mobile-package-spaces": "Il pacchetto applicazione non deve contenere spazi", + "mobile-app-secret": "Segreto applicazione", + "mobile-app-secret-hint": "Stringa codificata in Base64 che rappresenta almeno 512 bit di dati.", + "mobile-app-secret-required": "Il segreto dell'applicazione è obbligatorio.", + "mobile-app-secret-min-length": "Il segreto dell'applicazione deve essere di almeno 512 bit di dati.", + "mobile-app-secret-base64": "Il segreto dell'applicazione deve essere in formato base64.", + "invalid-mobile-app-secret": "Il segreto dell'applicazione deve contenere solo caratteri alfanumerici e avere una lunghezza compresa tra 16 e 2048 caratteri.", + "copy-mobile-app-secret": "Copia segreto applicazione", + "delete-mobile-app": "Elimina informazioni applicazione", + "providers": "Provider", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Tutte le piattaforme", + "smtp-provider": "Provider SMTP", + "allowed-platforms": "Piattaforme consentite", + "authentication": "Autenticazione", + "basic": "Base", + "provider": "Provider", + "redirect-url": "URI di reindirizzamento", + "domain-name": "Nome dominio", + "domain-name-required": "Il nome dominio è obbligatorio", + "redirect-url-template": "Template URI di reindirizzamento", + "microsoft-tenant-id": "ID Directory (tenant)", + "microsoft-tenant-id-required": "L'ID della Directory (tenant) è obbligatorio", + "token-uri": "URI token", + "token-uri-required": "L'URI token è obbligatorio", + "redirect-uri": "URI di reindirizzamento", + "google-provider": "Google", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "Personalizzato", + "generate-access-token": "Genera token di accesso", + "update-access-token": "Aggiorna token di accesso", + "access-token-status": "Stato del token di accesso:", + "token-status-generated": "generato", + "token-status-not-generated": "non generato" + }, + "smpp-provider": { + "smpp-version": "Versione SMPP", + "smpp-host": "Host SMPP", + "smpp-host-required": "L'host SMPP è obbligatorio", + "smpp-port": "Porta SMPP", + "smpp-port-required": "La porta SMPP è obbligatoria", + "system-id": "ID sistema", + "system-id-required": "L'ID sistema è obbligatorio", + "password": "Password", + "password-required": "La password è obbligatoria", + "type-settings": "Impostazioni tipo", + "source-settings": "Impostazioni sorgente", + "destination-settings": "Impostazioni destinazione", + "additional-settings": "Impostazioni aggiuntive", + "system-type": "Tipo di sistema", + "bind-type": "Tipo di bind", + "service-type": "Tipo di servizio", + "source-address": "Indirizzo sorgente", + "source-ton": "TON sorgente", + "source-npi": "NPI sorgente", + "destination-ton": "TON destinazione (Tipo di Numero)", + "destination-npi": "NPI destinazione (Identificazione del Piano di Numerazione)", + "address-range": "Intervallo indirizzi", + "coding-scheme": "Schema di codifica", + "bind-type-tx": "Trasmettitore", + "bind-type-rx": "Ricevitore", + "bind-type-trx": "Ricetrasmettitore", + "ton-unknown": "Sconosciuto", + "ton-international": "Internazionale", + "ton-national": "Nazionale", + "ton-network-specific": "Specifico della rete", + "ton-subscriber-number": "Numero abbonato", + "ton-alphanumeric": "Alfanumerico", + "ton-abbreviated": "Abbreviato", + "npi-unknown": "0 - Sconosciuto", + "npi-isdn": "1 - ISDN/piano di numerazione telefonica (E163/E164)", + "npi-data-numbering-plan": "3 - Piano di numerazione dati (X.121)", + "npi-telex-numbering-plan": "4 - Piano di numerazione Telex (F.69)", + "npi-land-mobile": "6 - Mobile terrestre (E.212)", + "npi-national-numbering-plan": "8 - Piano di numerazione nazionale", + "npi-private-numbering-plan": "9 - Piano di numerazione privato", + "npi-ermes-numbering-plan": "10 - Piano di numerazione ERMES (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internet (IP)", + "npi-wap-client-id": "18 - ID client WAP (definito dal WAP Forum)", + "scheme-smsc": "0 - Alfabeto predefinito SMSC (ASCII per short/long code e GSM per numero verde)", + "scheme-ia5": "1 - IA5 (ASCII per short/long code, Latin 9 per numero verde (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Ottetto non specificato (8-bit binario)", + "scheme-latin-1": "3 - Latino 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Ottetto non specificato (8-bit binario)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Cirillico (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latino/Ebraico (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Codifica pittogrammi", + "scheme-music-codes": "10 - Codici musicali (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Kanji JIS esteso (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Set grafico coreano (KS C 5601/KS X 1001)" + }, + "queue-select-name": "Seleziona nome coda", + "queue-name": "Nome", + "queue-name-required": "Il nome della coda è obbligatorio!", + "queues": "Code", + "queue-partitions": "Partizioni", + "queue-submit-strategy": "Strategia di invio", + "queue-processing-strategy": "Strategia di elaborazione", + "queue-configuration": "Configurazione della coda", + "repository-settings": "Impostazioni del repository", + "repository": "Repository", + "repository-url": "URL del repository", + "repository-url-required": "L'URL del repository è obbligatorio.", + "default-branch": "Nome del branch predefinito", + "repository-read-only": "Sola lettura", + "show-merge-commits": "Mostra commit di merge", + "authentication-settings": "Impostazioni di autenticazione", + "auth-method": "Metodo di autenticazione", + "auth-method-username-password": "Password / token di accesso", + "auth-method-username-password-hint": "Gli utenti GitHub devono usare token di accesso con permessi di scrittura al repository.", + "auth-method-private-key": "Chiave privata", + "password-access-token": "Password / token di accesso", + "change-password-access-token": "Cambia password / token di accesso", + "private-key": "Chiave privata", + "drop-private-key-file-or": "Trascina e rilascia un file con chiave privata oppure", + "passphrase": "Passphrase", + "enter-passphrase": "Inserisci passphrase", + "change-passphrase": "Cambia passphrase", + "check-access": "Verifica accesso", + "check-repository-access-success": "Accesso al repository verificato con successo!", + "delete-repository-settings-title": "Sei sicuro di voler eliminare le impostazioni del repository?", + "delete-repository-settings-text": "Attenzione, dopo la conferma le impostazioni del repository verranno rimosse e la funzionalità di controllo versione non sarà più disponibile.", + "auto-commit-settings": "Impostazioni auto-commit", + "auto-commit": "Auto-commit", + "auto-commit-entities": "Entità auto-commit", + "no-auto-commit-entities-prompt": "Nessuna entità configurata per l'auto-commit", + "delete-auto-commit-settings-title": "Sei sicuro di voler eliminare le impostazioni di auto-commit?", + "delete-auto-commit-settings-text": "Attenzione, dopo la conferma le impostazioni di auto-commit verranno rimosse e l'auto-commit sarà disabilitato per tutte le entità.", + "mobile-app": { + "mobile-app": "App mobile", + "mobile-app-qr-code-widget-settings": "Impostazioni widget QR code app mobile", + "applications": "Applicazioni", + "default": "Predefinito", + "custom": "Personalizzato", + "android": "Android", + "ios": "iOS", + "appearance": "Aspetto", + "appearance-on-home-page": "Aspetto sulla pagina Home", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "badges": "Badge", + "label": "Etichetta", + "label-required": "L'etichetta è obbligatoria", + "label-max-length": "L'etichetta deve essere lunga al massimo 50 caratteri", + "right": "Destra", + "left": "Sinistra", + "set": "Imposta", + "preview": "Anteprima", + "connect-mobile-app": "Collega app mobile", + "use-system-settings": "Usa impostazioni di sistema" + }, + "2fa": { + "2fa": "Autenticazione a due fattori", + "available-providers": "Provider disponibili", + "issuer-name": "Nome dell'emittente", + "issuer-name-required": "Il nome dell'emittente è obbligatorio.", + "max-verification-failures-before-user-lockout": "Numero massimo di verifiche fallite prima del blocco dell'utente", + "max-verification-failures-before-user-lockout-pattern": "Il numero massimo di verifiche deve essere un numero intero positivo.", + "number-of-checking-attempts": "Numero di tentativi di verifica", + "number-of-checking-attempts-pattern": "Il numero di tentativi di verifica deve essere un numero intero positivo.", + "number-of-checking-attempts-required": "Il numero di tentativi di verifica è obbligatorio.", + "number-of-codes": "Numero di codici", + "number-of-codes-pattern": "Il numero di codici deve essere un numero intero positivo.", + "number-of-codes-required": "Il numero di codici è obbligatorio.", + "provider": "Provider", + "retry-verification-code-period": "Periodo per il reinvio del codice di verifica (sec)", + "retry-verification-code-period-pattern": "Il tempo minimo è di 5 secondi", + "retry-verification-code-period-required": "Il periodo per il reinvio del codice di verifica è obbligatorio.", + "total-allowed-time-for-verification": "Tempo totale consentito per la verifica (sec)", + "total-allowed-time-for-verification-pattern": "Il tempo minimo totale consentito è di 60 secondi", + "total-allowed-time-for-verification-required": "Il tempo totale consentito è obbligatorio.", + "use-system-two-factor-auth-settings": "Usa le impostazioni di autenticazione a due fattori di sistema", + "verification-code-check-rate-limit": "Limite di frequenza dei controlli del codice di verifica", + "verification-code-lifetime": "Durata del codice di verifica (sec)", + "verification-code-lifetime-pattern": "La durata del codice di verifica deve essere un numero intero positivo.", + "verification-code-lifetime-required": "La durata del codice di verifica è obbligatoria.", + "verification-message-template": "Template del messaggio di verifica", + "verification-limitations": "Limitazioni della verifica", + "verification-message-template-pattern": "Il messaggio di verifica deve contenere il pattern: ${code}", + "verification-message-template-required": "Il template del messaggio di verifica è obbligatorio.", + "within-time": "Entro il tempo (sec)", + "within-time-pattern": "Il tempo deve essere un numero intero positivo.", + "within-time-required": "Il tempo è obbligatorio." + }, + "jwt": { + "security-settings": "Impostazioni di sicurezza JWT", + "issuer-name": "Nome dell'emittente", + "issuer-name-required": "Il nome dell'emittente è obbligatorio.", + "signings-key": "Chiave di firma", + "signings-key-hint": "Stringa codificata in Base64 che rappresenta almeno 512 bit di dati.", + "signings-key-required": "La chiave di firma è obbligatoria.", + "signings-key-min-length": "La chiave di firma deve essere di almeno 512 bit di dati.", + "signings-key-base64": "La chiave di firma deve essere in formato base64.", + "expiration-time": "Tempo di scadenza del token (sec)", + "expiration-time-required": "Il tempo di scadenza del token è obbligatorio.", + "expiration-time-max": "Il tempo massimo consentito è di 2147483647 secondi (68 anni).", + "expiration-time-min": "Il tempo minimo è di 60 secondi (1 minuto).", + "refresh-expiration-time": "Tempo di scadenza del token di aggiornamento (sec)", + "refresh-expiration-time-required": "Il tempo di scadenza del token di aggiornamento è obbligatorio.", + "refresh-expiration-time-max": "Il tempo massimo consentito è di 2147483647 secondi (68 anni).", + "refresh-expiration-time-min": "Il tempo minimo è di 900 secondi (15 minuti).", + "refresh-expiration-time-less-token": "Il tempo del token di aggiornamento deve essere maggiore di quello del token.", + "generate-key": "Genera chiave", + "info-header": "Tutti gli utenti dovranno effettuare nuovamente il login", + "info-message": "La modifica della chiave di firma JWT invaliderà tutti i token emessi. Tutti gli utenti dovranno effettuare nuovamente l'accesso. Questo influenzerà anche gli script che utilizzano l'API REST/Websockets." + }, + "resources": "Risorse", + "notifications": "Notifiche", + "notifications-settings": "Impostazioni notifiche", + "slack-api-token": "Token API Slack", + "slack": "Slack", + "slack-settings": "Impostazioni Slack", + "mobile-settings": "Impostazioni mobile", + "firebase-service-account-file": "File JSON delle credenziali dell'account di servizio Firebase", + "select-firebase-service-account-file": "Trascina e rilascia il file delle credenziali dell'account di servizio Firebase oppure " + }, + "alarm": { + "alarm": "Allarme", + "alarms": "Allarmi", + "all-alarms": "Tutti gli allarmi", + "select-alarm": "Seleziona allarme", + "no-alarms-matching": "Nessun allarme corrispondente a '{{entity}}' trovato.", + "alarm-required": "Allarme obbligatorio", + "alarm-filter": "Filtro allarmi", + "filter": "Filtro", + "alarm-status": "Stato allarme", + "alarm-status-list": "Elenco stati allarme", + "any-status": "Qualsiasi stato", + "search-status": { + "ANY": "Qualsiasi", + "ACTIVE": "Attivo", + "CLEARED": "Ripristinato", + "ACK": "Confermato", + "UNACK": "Non confermato" + }, + "display-status": { + "ACTIVE_UNACK": "Attivo non confermato", + "ACTIVE_ACK": "Attivo confermato", + "CLEARED_UNACK": "Ripristinato non confermato", + "CLEARED_ACK": "Ripristinato confermato" + }, + "no-alarms-prompt": "Nessun allarme trovato", + "created-time": "Ora di creazione", + "type": "Tipo", + "severity": "Gravità", + "originator": "Originatore", + "originator-type": "Tipo originatore", + "details": "Dettagli", + "originator-label": "Etichetta originatore", + "assign": "Assegna", + "assignments": "Assegnazioni", + "assignee": "Assegnatario", + "assignee-id": "ID assegnatario", + "assignee-first-name": "Nome assegnatario", + "assignee-last-name": "Cognome assegnatario", + "assignee-email": "Email assegnatario", + "unassigned": "Non assegnato", + "user-deleted": "Utente eliminato", + "assignee-not-set": "Tutti", + "status": "Stato", + "alarm-details": "Dettagli allarme", + "start-time": "Ora inizio", + "assign-time": "Ora assegnazione", + "end-time": "Ora fine", + "ack-time": "Ora conferma", + "clear-time": "Ora ripristino", + "duration": "Durata", + "alarm-severity": "Gravità allarme", + "alarm-severity-list": "Elenco gravità allarme", + "any-severity": "Qualsiasi gravità", + "severity-critical": "Critica", + "severity-major": "Maggiore", + "severity-minor": "Minore", + "severity-warning": "Avviso", + "severity-indeterminate": "Indeterminata", + "acknowledge": "Conferma", + "clear": "Ripristina", + "delete": "Elimina", + "search": "Cerca allarmi", + "selected-alarms": "{ count, plural, =1 {1 allarme} other {# allarmi} } selezionato/i", + "no-data": "Nessun dato da visualizzare", + "polling-interval": "Intervallo di polling allarmi (sec)", + "polling-interval-required": "L'intervallo di polling degli allarmi è obbligatorio.", + "min-polling-interval-message": "L'intervallo minimo consentito è 1 secondo.", + "aknowledge-alarms-title": "Conferma { count, plural, =1 {1 allarme} other {# allarmi} }", + "aknowledge-alarms-text": "Sei sicuro di voler confermare { count, plural, =1 {1 allarme} other {# allarmi} }?", + "aknowledge-alarm-title": "Conferma Allarme", + "aknowledge-alarm-text": "Sei sicuro di voler confermare l'allarme?", + "selected-alarms-are-acknowledged": "Gli allarmi selezionati sono già stati confermati", + "clear-alarms-title": "Ripristina { count, plural, =1 {1 allarme} other {# allarmi} }", + "clear-alarms-text": "Sei sicuro di voler ripristinare { count, plural, =1 {1 allarme} other {# allarmi} }?", + "clear-alarm-title": "Ripristina Allarme", + "clear-alarm-text": "Sei sicuro di voler ripristinare l'allarme?", + "delete-alarms-title": "Elimina { count, plural, =1 {1 allarme} other {# allarmi} }", + "delete-alarms-text": "Sei sicuro di voler eliminare { count, plural, =1 {1 allarme} other {# allarmi} }?", + "selected-alarms-are-cleared": "Gli allarmi selezionati sono già stati ripristinati", + "alarm-status-filter": "Filtro stato allarme", + "alarm-filter-title": "Filtro allarme", + "assigned": "Assegnato", + "filter-title": "Filtro", + "max-count-load": "Numero massimo di allarmi da caricare (0 - illimitato)", + "max-count-load-required": "Il numero massimo di allarmi da caricare è obbligatorio.", + "max-count-load-error-min": "Il valore minimo è 0.", + "fetch-size": "Dimensione fetch", + "fetch-size-required": "La dimensione del fetch è obbligatoria.", + "fetch-size-error-min": "Il valore minimo è 10.", + "alarm-types": "Tipi di allarme", + "alarm-type-list": "Elenco tipi di allarme", + "any-type": "Qualsiasi tipo", + "assigned-to-current-user": "Assegnato all'utente corrente", + "assigned-to-me": "Assegnato a me", + "search-propagated-alarms": "Cerca allarmi propagati", + "comments": "Commenti allarme", + "show-more": "Mostra di più", + "additional-info": "Informazioni aggiuntive", + "alarm-type": "Tipo di allarme", + "enter-alarm-type": "Inserisci tipo di allarme", + "no-alarm-types-matching": "Nessun tipo di allarme corrispondente a '{{entitySubtype}}' trovato.", + "alarm-type-list-empty": "Nessun tipo di allarme selezionato." + }, + "alarm-activity": { + "add": "Aggiungi un commento...", + "alarm-comment": "Commento allarme", + "comments": "Commenti", + "delete-alarm-comment": "Vuoi eliminare questo commento?", + "refresh": "Aggiorna", + "oldest-first": "Più vecchi prima", + "newest-first": "Più recenti prima", + "activity": "Attività", + "export": "Esporta in CSV", + "author": "Autore", + "created-date": "Data di creazione", + "edited-date": "Data di modifica", + "text": "Testo", + "system": "Sistema" + }, + "alias": { + "add": "Aggiungi alias", + "edit": "Modifica alias", + "name": "Nome alias", + "name-required": "Il nome dell'alias è obbligatorio", + "duplicate-alias": "Esiste già un alias con lo stesso nome.", + "filter-type-single-entity": "Singola entità", + "filter-type-entity-list": "Lista di entità", + "filter-type-entity-name": "Nome entità", + "filter-type-entity-type": "Tipo entità", + "filter-type-state-entity": "Entità da stato della dashboard", + "filter-type-state-entity-description": "Entità presa dai parametri dello stato della dashboard", + "filter-type-asset-type": "Tipo di asset", + "filter-type-asset-type-description": "Asset di tipo '{{assetTypes}}'", + "filter-type-asset-type-and-name-description": "Asset di tipo '{{assetTypes}}' con nome che inizia con '{{prefix}}'", + "filter-type-device-type": "Tipo di dispositivo", + "filter-type-device-type-description": "Dispositivi di tipo '{{deviceTypes}}'", + "filter-type-device-type-and-name-description": "Dispositivi di tipo '{{deviceTypes}}' con nome che inizia con '{{prefix}}'", + "filter-type-entity-view-type": "Tipo di Entity View", + "filter-type-entity-view-type-description": "Entity View di tipo '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description": "Entity View di tipo '{{entityViewTypes}}' con nome che inizia con '{{prefix}}'", + "filter-type-edge-type": "Tipo Edge", + "filter-type-edge-type-description": "Edge di tipo '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description": "Edge di tipo '{{edgeTypes}}' con nome che inizia con '{{prefix}}'", + "filter-type-relations-query": "Query relazioni", + "filter-type-relations-query-description": "{{entities}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Query ricerca Edge", + "filter-type-edge-search-query-description": "Edge di tipo {{edgeTypes}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query": "Query ricerca asset", + "filter-type-asset-search-query-description": "Asset di tipo {{assetTypes}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query": "Query ricerca dispositivo", + "filter-type-device-search-query-description": "Dispositivi di tipo {{deviceTypes}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query": "Query ricerca Entity View", + "filter-type-entity-view-search-query-description": "Entity View di tipo {{entityViewTypes}} con relazione di tipo {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState": "Stato utilizzo API", + "entity-filter": "Filtro entità", + "resolve-multiple": "Risolvi come entità multiple", + "resolve-multiple-hint": "Abilita per visualizzare dati da tutte le entità filtrate simultaneamente.\nSe disabilitato, il widget mostra i dati della sola entità selezionata.", + "filter-type": "Tipo di filtro", + "filter-type-required": "Il tipo di filtro è obbligatorio.", + "entity-filter-no-entity-matched": "Nessuna entità corrispondente al filtro specificato trovata.", + "no-entity-filter-specified": "Nessun filtro entità specificato", + "root-state-entity": "Usa entità stato dashboard come radice", + "last-level-relation": "Recupera solo ultima relazione di livello", + "root-entity": "Entità radice", + "state-entity-parameter-name": "Nome parametro entità stato", + "default-state-entity": "Entità stato predefinita", + "default-entity-parameter-name": "Predefinito", + "max-relation-level": "Livello massimo relazione", + "unlimited-level": "Livello illimitato", + "state-entity": "Entità stato della dashboard", + "all-entities": "Tutte le entità", + "any-relation": "qualsiasi" + }, + "asset": { + "asset": "Asset", + "assets": "Asset", + "management": "Gestione asset", + "view-assets": "Visualizza asset", + "add": "Aggiungi asset", + "asset-type-max-length": "Il tipo di asset deve essere inferiore a 256 caratteri", + "assign-to-customer": "Assegna al cliente", + "assign-asset-to-customer": "Assegna asset al cliente", + "assign-asset-to-customer-text": "Seleziona gli asset da assegnare al cliente", + "no-assets-text": "Nessun asset trovato", + "assign-to-customer-text": "Seleziona il cliente a cui assegnare l'asset", + "public": "Pubblico", + "assignedToCustomer": "Assegnato al cliente", + "make-public": "Rendi pubblico l'asset", + "make-private": "Rendi privato l'asset", + "unassign-from-customer": "Rimuovi assegnazione dal cliente", + "delete": "Elimina asset", + "asset-public": "L'asset è pubblico", + "asset-type": "Tipo asset", + "asset-type-required": "Il tipo di asset è obbligatorio.", + "select-asset-type": "Seleziona tipo asset", + "enter-asset-type": "Inserisci profilo asset", + "any-asset": "Qualsiasi asset", + "no-asset-types-matching": "Nessun tipo di asset corrispondente a '{{entitySubtype}}' trovato.", + "asset-type-list-empty": "Nessun tipo di asset selezionato.", + "asset-types": "Tipi di asset", + "name": "Nome", + "name-required": "Il nome è obbligatorio.", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri", + "label-max-length": "L'etichetta deve essere inferiore a 256 caratteri", + "description": "Descrizione", + "type": "Tipo", + "type-required": "Il tipo è obbligatorio.", + "details": "Dettagli", + "events": "Eventi", + "add-asset-text": "Aggiungi nuovo asset", + "asset-details": "Dettagli asset", + "assign-assets": "Assegna asset", + "assign-assets-text": "Assegna { count, plural, =1 {1 asset} other {# asset} } al cliente", + "assign-asset-to-edge-title": "Assegna asset all'edge", + "assign-asset-to-edge-text": "Seleziona gli asset da assegnare all'edge", + "delete-assets": "Elimina asset", + "unassign-assets": "Rimuovi assegnazione asset", + "unassign-assets-action-title": "Rimuovi assegnazione di { count, plural, =1 {1 asset} other {# asset} } dal cliente", + "assign-new-asset": "Assegna nuovo asset", + "delete-asset-title": "Sei sicuro di voler eliminare l'asset '{{assetName}}'?", + "delete-asset-text": "Attenzione, dopo la conferma l'asset e tutti i dati correlati saranno irrimediabilmente persi.", + "delete-assets-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 asset} other {# asset} }?", + "delete-assets-action-title": "Elimina { count, plural, =1 {1 asset} other {# asset} }", + "delete-assets-text": "Attenzione, dopo la conferma tutti gli asset selezionati saranno rimossi e tutti i dati correlati saranno irrimediabilmente persi.", + "make-public-asset-title": "Sei sicuro di voler rendere pubblico l'asset '{{assetName}}'?", + "make-public-asset-text": "Dopo la conferma l'asset e tutti i suoi dati saranno resi pubblici e accessibili ad altri.", + "make-private-asset-title": "Sei sicuro di voler rendere privato l'asset '{{assetName}}'?", + "make-private-asset-text": "Dopo la conferma l'asset e tutti i suoi dati saranno resi privati e non saranno accessibili ad altri.", + "unassign-asset-title": "Sei sicuro di voler rimuovere l'assegnazione dell'asset '{{assetName}}'?", + "unassign-asset-text": "Dopo la conferma l'asset sarà disassegnato e non sarà più accessibile dal cliente.", + "unassign-asset": "Disassegna asset", + "unassign-assets-title": "Sei sicuro di voler disassegnare { count, plural, =1 {1 asset} other {# asset} }?", + "unassign-assets-text": "Dopo la conferma tutti gli asset selezionati saranno disassegnati e non saranno accessibili dal cliente.", + "copyId": "Copia ID asset", + "idCopiedMessage": "ID asset copiato negli appunti", + "select-asset": "Seleziona asset", + "no-assets-matching": "Nessun asset corrispondente a '{{entity}}' trovato.", + "asset-required": "L'asset è obbligatorio", + "name-starts-with": "Espressione nome asset", + "help-text": "Usa '%' secondo necessità: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search": "Cerca asset", + "import": "Importa asset", + "asset-file": "File asset", + "label": "Etichetta", + "assign-asset-to-edge": "Assegna asset all'edge", + "unassign-asset-from-edge": "Disassegna asset", + "unassign-asset-from-edge-title": "Sei sicuro di voler disassegnare l'asset '{{assetName}}'?", + "unassign-asset-from-edge-text": "Dopo la conferma l'asset sarà disassegnato e non sarà più accessibile dall'edge.", + "unassign-assets-from-edge-title": "Sei sicuro di voler disassegnare { count, plural, =1 {1 asset} other {# asset} }?", + "unassign-assets-from-edge-text": "Dopo la conferma tutti gli asset selezionati saranno disassegnati e non saranno più accessibili dall'edge.", + "selected-assets": "{ count, plural, =1 {1 asset} other {# asset} } selezionato/i" + }, + "attribute": { + "attributes": "Attributi", + "latest-telemetry": "Ultima telemetria", + "no-latest-telemetry": "Nessuna ultima telemetria", + "attributes-scope": "Ambito attributi entità", + "scope-telemetry": "Telemetria", + "scope-latest-telemetry": "Ultima telemetria", + "scope-client": "Attributi client", + "scope-server": "Attributi server", + "scope-shared": "Attributi condivisi", + "scope-client-short": "Client", + "scope-server-short": "Server", + "scope-shared-short": "Condivisi", + "scope-latest-short": "Ultimi", + "scope-any": "Qualsiasi", + "add": "Aggiungi attributo", + "key": "Chiave", + "key-max-length": "La chiave deve essere inferiore a 256 caratteri", + "last-update-time": "Ultimo aggiornamento", + "key-required": "La chiave dell'attributo è obbligatoria.", + "value": "Valore", + "value-required": "Il valore dell'attributo è obbligatorio.", + "telemetry-key-required": "La chiave della telemetria è obbligatoria", + "telemetry-value-required": "Il valore della telemetria è obbligatorio", + "delete-attributes-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 attributo} other {# attributi} }?", + "delete-attributes-text": "Attenzione, dopo la conferma tutti gli attributi selezionati verranno rimossi.", + "delete-attributes": "Elimina attributi", + "enter-attribute-value": "Inserisci valore attributo", + "show-on-widget": "Mostra nel widget", + "widget-mode": "Modalità widget", + "next-widget": "Widget successivo", + "prev-widget": "Widget precedente", + "add-to-dashboard": "Aggiungi alla dashboard", + "add-widget-to-dashboard": "Aggiungi widget alla dashboard", + "selected-attributes": "{ count, plural, =1 {1 attributo} other {# attributi} } selezionato/i", + "selected-telemetry": "{ count, plural, =1 {1 unità di telemetria} other {# unità di telemetria} } selezionata/e", + "no-attributes-text": "Nessun attributo trovato", + "no-telemetry-text": "Nessuna telemetria trovata", + "copy-key": "Copia chiave", + "add-telemetry": "Aggiungi telemetria", + "copy-value": "Copia valore", + "delete-timeseries": { + "start-time": "Ora inizio", + "ends-on": "Ora fine", + "strategy": "Strategia", + "delete-strategy": "Strategia di eliminazione", + "all-data": "Elimina tutti i dati", + "all-data-except-latest-value": "Elimina tutti i dati tranne l'ultimo valore", + "latest-value": "Elimina ultimo valore", + "all-data-for-time-period": "Elimina tutti i dati per il periodo di tempo", + "rewrite-latest-value": "Riscrivi ultimo valore" + } + }, + "api-usage": { + "api-features": "Funzionalità API", + "api-usage": "Utilizzo API", + "alarm": "Allarme", + "alarms-created": "Allarmi creati", + "queue-stats": "Statistiche coda", + "processing-failures-and-timeouts": "Errori e timeout di elaborazione", + "exceptions": "Eccezioni", + "alarms-created-daily-activity": "Attività giornaliera allarmi creati", + "alarms-created-hourly-activity": "Attività oraria allarmi creati", + "alarms-created-monthly-activity": "Attività mensile allarmi creati", + "data-points": "Punti dati", + "data-points-storage-days": "Giorni di conservazione dei punti dati", + "device-api": "API dispositivo", + "email": "Email", + "email-messages": "Messaggi email", + "email-messages-daily-activity": "Attività giornaliera messaggi email", + "email-messages-monthly-activity": "Attività mensile messaggi email", + "executions": "Esecuzioni", + "scripts": "Script", + "scripts-hourly-activity": "Attività oraria script", + "scripts-daily-activity": "Attività giornaliera script", + "scripts-monthly-activity": "Attività mensile script", + "javascript": "JavaScript", + "javascript-executions": "Esecuzioni JavaScript", + "tbel": "TBEL", + "tbel-executions": "Esecuzioni TBEL", + "latest-error": "Ultimo errore", + "messages": "Messaggi", + "notifications": "Notifiche", + "notifications-email-sms": "Notifiche (Email/SMS)", + "notifications-hourly-activity": "Attività oraria notifiche", + "permanent-failures": "Errori permanenti ${entityName}", + "permanent-timeouts": "Timeout permanenti ${entityName}", + "processing-failures": "Errori di elaborazione ${entityName}", + "processing-timeouts": "Timeout di elaborazione ${entityName}", + "rule-chain": "Catena di regole", + "rule-engine": "Motore di regole", + "rule-engine-daily-activity": "Attività giornaliera motore di regole", + "rule-engine-executions": "Esecuzioni motore di regole", + "rule-engine-hourly-activity": "Attività oraria motore di regole", + "rule-engine-monthly-activity": "Attività mensile motore di regole", + "rule-engine-statistics": "Statistiche motore di regole", + "rule-node": "Nodo regola", + "sms": "SMS", + "sms-messages": "Messaggi SMS", + "sms-messages-daily-activity": "Attività giornaliera messaggi SMS", + "sms-messages-monthly-activity": "Attività mensile messaggi SMS", + "successful": "${entityName} riusciti", + "telemetry": "Telemetria", + "telemetry-persistence": "Persistenza telemetria", + "telemetry-persistence-daily-activity": "Attività giornaliera persistenza telemetria", + "telemetry-persistence-hourly-activity": "Attività oraria persistenza telemetria", + "telemetry-persistence-monthly-activity": "Attività mensile persistenza telemetria", + "transport": "Trasporto", + "transport-daily-activity": "Attività giornaliera trasporto", + "transport-data-points": "Punti dati trasporto", + "transport-hourly-activity": "Attività oraria trasporto", + "transport-messages": "Messaggi trasporto", + "transport-monthly-activity": "Attività mensile trasporto", + "view-details": "Visualizza dettagli", + "view-statistics": "Visualizza statistiche" + }, + "api-limit": { + "cassandra-queries": "Query Cassandra", + "entity-version-creation": "Creazione versione entità", + "entity-version-load": "Caricamento versione entità", + "notification-requests": "Richieste di notifica", + "notification-requests-per-rule": "Richieste di notifica per regola", + "rest-api-requests": "Richieste REST API", + "rest-api-requests-per-customer": "Richieste REST API per cliente", + "transport-messages": "Messaggi trasporto", + "transport-messages-per-device": "Messaggi trasporto per dispositivo", + "transport-messages-per-gateway": "Messaggi trasporto per gateway", + "transport-messages-per-gateway-device": "Messaggi trasporto per dispositivo gateway", + "ws-updates-per-session": "Aggiornamenti WS per sessione", + "edge-events": "Eventi edge", + "edge-events-per-edge": "Eventi per edge", + "edge-uplink-messages": "Messaggi uplink edge", + "edge-uplink-messages-per-edge": "Messaggi uplink per edge" + }, + "audit-log": { + "audit": "Audit", + "audit-logs": "Log di audit", + "timestamp": "Timestamp", + "entity-type": "Tipo entità", + "entity-name": "Nome entità", + "user": "Utente", + "type": "Tipo", + "status": "Stato", + "details": "Dettagli", + "type-added": "Aggiunto", + "type-deleted": "Eliminato", + "type-updated": "Aggiornato", + "type-attributes-updated": "Attributi aggiornati", + "type-attributes-deleted": "Attributi eliminati", + "type-rpc-call": "Chiamata RPC", + "type-credentials-updated": "Credenziali aggiornate", + "type-assigned-to-customer": "Assegnato al cliente", + "type-unassigned-from-customer": "Rimosso dal cliente", + "type-assigned-to-edge": "Assegnato all'Edge", + "type-unassigned-from-edge": "Rimosso dall'Edge", + "type-activated": "Attivato", + "type-suspended": "Sospeso", + "type-credentials-read": "Credenziali lette", + "type-attributes-read": "Attributi letti", + "type-relation-add-or-update": "Relazione aggiornata", + "type-relation-delete": "Relazione eliminata", + "type-relations-delete": "Tutte le relazioni eliminate", + "type-alarm-ack": "Allarme confermato", + "type-alarm-clear": "Allarme ripristinato", + "type-alarm-delete": "Allarme eliminato", + "type-alarm-assign": "Allarme assegnato", + "type-alarm-unassign": "Allarme rimosso", + "type-added-comment": "Commento aggiunto", + "type-updated-comment": "Commento aggiornato", + "type-deleted-comment": "Commento eliminato", + "type-login": "Accesso", + "type-logout": "Disconnessione", + "type-lockout": "Blocco", + "status-success": "Successo", + "status-failure": "Fallimento", + "audit-log-details": "Dettagli log di audit", + "no-audit-logs-prompt": "Nessun log trovato", + "action-data": "Dati azione", + "failure-details": "Dettagli errore", + "search": "Cerca nei log di audit", + "clear-search": "Cancella ricerca", + "type-assigned-from-tenant": "Assegnato dal Tenant", + "type-assigned-to-tenant": "Assegnato al Tenant", + "type-provision-success": "Dispositivo fornito", + "type-provision-failure": "Provisioning dispositivo fallito", + "type-timeseries-updated": "Telemetria aggiornata", + "type-timeseries-deleted": "Telemetria eliminata", + "type-sms-sent": "SMS inviato" + }, + "debug-settings": { + "label": "Configurazione debug", + "on-failure": "Solo fallimenti (24/7)", + "all-messages": "Tutti i messaggi ({{time}})", + "failures": "Fallimenti", + "entity": "entità", + "hint": { + "main-limited": "Tutti i messaggi di debug di {{entity}} saranno limitati nel tempo, con un massimo di {{msg}} messaggi consentiti ogni {{time}}.", + "on-failure": "Salva tutti gli eventi di debug in caso di errore senza limite di tempo.", + "all-messages": "Salva tutti gli eventi di debug entro il limite di tempo." + } + }, + "calculated-fields": { + "expression": "Espressione", + "no-found": "Nessun campo calcolato trovato", + "list": "{ count, plural, =1 {Un campo calcolato} other {Elenco di # campi calcolati} }", + "selected-fields": "{ count, plural, =1 {1 campo calcolato} other {# campi calcolati} } selezionato/i", + "type": { + "simple": "Semplice", + "script": "Script" + }, + "arguments": "Argomenti", + "decimals-by-default": "Decimali di default", + "debugging": "Debug del campo calcolato", + "argument-name": "Nome argomento", + "datasource": "Fonte dati", + "add-argument": "Aggiungi argomento", + "test-script-function": "Testa funzione script", + "no-arguments": "Nessun argomento configurato", + "argument-settings": "Impostazioni argomento", + "argument-current": "Entità corrente", + "argument-current-tenant": "Tenant corrente", + "argument-device": "Dispositivo", + "argument-asset": "Asset", + "argument-customer": "Cliente", + "argument-tenant": "Tenant corrente", + "argument-type": "Tipo argomento", + "see-debug-events": "Vedi eventi di debug", + "attribute": "Attributo", + "copy-argument-name": "Copia nome argomento", + "timeseries-key": "Chiave serie temporale", + "device-name": "Nome dispositivo", + "latest-telemetry": "Ultima telemetria", + "rolling": "Rolling serie temporale", + "attribute-scope": "Ambito attributo", + "server-attributes": "Attributi server", + "client-attributes": "Attributi client", + "shared-attributes": "Attributi condivisi", + "attribute-key": "Chiave attributo", + "default-value": "Valore di default", + "limit": "Valori massimi", + "time-window": "Finestra temporale", + "customer-name": "Nome cliente", + "asset-name": "Nome asset", + "timeseries": "Serie temporale", + "output": "Output", + "create": "Crea nuovo campo calcolato", + "file": "File campo calcolato", + "invalid-file-error": "Formato file non valido. Assicurati che il file sia un file JSON valido.", + "import": "Importa campo calcolato", + "export": "Esporta campo calcolato", + "export-failed-error": "Impossibile esportare il campo calcolato: {{error}}", + "output-type": "Tipo output", + "delete-title": "Sei sicuro di voler eliminare il campo calcolato '{{title}}'?", + "delete-text": "Attenzione, dopo la conferma il campo calcolato e tutti i dati correlati saranno irrimediabilmente persi.", + "delete-multiple-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 campo calcolato} other {# campi calcolati} }?", + "delete-multiple-text": "Attenzione, dopo la conferma tutti i campi calcolati selezionati saranno eliminati e tutti i dati correlati saranno irrimediabilmente persi.", + "test-with-this-message": "Testa con questo messaggio", + "hint": { + "arguments-simple-with-rolling": "Il campo calcolato di tipo semplice non deve contenere chiavi con tipo rolling su serie temporale.", + "arguments-empty": "Gli argomenti non devono essere vuoti.", + "expression-required": "L'espressione è obbligatoria.", + "expression-invalid": "Espressione non valida", + "expression-max-length": "La lunghezza dell'espressione deve essere inferiore a 255 caratteri.", + "argument-name-required": "Il nome dell'argomento è obbligatorio.", + "argument-name-pattern": "Il nome dell'argomento non è valido.", + "argument-name-duplicate": "Esiste già un argomento con questo nome.", + "argument-name-max-length": "Il nome dell'argomento deve essere inferiore a 256 caratteri.", + "argument-name-forbidden": "Il nome dell'argomento è riservato e non può essere utilizzato.", + "argument-type-required": "Il tipo dell'argomento è obbligatorio.", + "max-args": "Numero massimo di argomenti raggiunto.", + "decimals-range": "I decimali di default devono essere un numero compreso tra 0 e 15.", + "expression": "L'espressione di default dimostra come trasformare una temperatura da Fahrenheit a Celsius.", + "arguments-entity-not-found": "Entità di destinazione dell'argomento non trovata." + } + }, + "confirm-on-exit": { + "message": "Hai modifiche non salvate. Sei sicuro di voler lasciare questa pagina?", + "html-message": "Hai modifiche non salvate.
Sei sicuro di voler lasciare questa pagina?", + "title": "Modifiche non salvate" + }, + "contact": { + "country": "Paese", + "country-required": "Il paese è obbligatorio.", + "city": "Città", + "state": "Stato / Provincia", + "postal-code": "CAP / Codice postale", + "postal-code-invalid": "Formato CAP / Codice postale non valido.", + "address": "Indirizzo", + "address2": "Indirizzo 2", + "phone": "Telefono", + "email": "Email", + "no-address": "Nessun indirizzo", + "no-country-found": "Nessun paese trovato.", + "no-country-matching": "Nessun paese corrispondente a '{{country}}' trovato.", + "state-max-length": "La lunghezza dello stato deve essere inferiore a 256 caratteri", + "phone-max-length": "Il numero di telefono deve essere inferiore a 256 caratteri", + "city-max-length": "La città specificata deve essere inferiore a 256 caratteri" + }, + "common": { + "name": "Nome", + "type": "Tipo", + "general": "Generale", + "username": "Nome utente", + "password": "Password", + "data": "Dati", + "timestamp": "Timestamp", + "enter-username": "Inserisci nome utente", + "enter-password": "Inserisci password", + "enter-search": "Inserisci ricerca", + "created-time": "Ora di creazione", + "disabled": "Disabilitato", + "loading": "Caricamento...", + "proceed": "Procedi", + "open-details-page": "Apri pagina dettagli", + "not-found": "Non trovato", + "value": "Valore", + "documentation": "Documentazione", + "time-left": "{{time}} rimanenti", + "output": "Output", + "suffix": { + "s": "s", + "ms": "ms" + }, + "hint": { + "name-required": "Il nome è obbligatorio.", + "name-pattern": "Il nome non è valido.", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri.", + "title-required": "Il titolo è obbligatorio.", + "title-pattern": "Il titolo non è valido.", + "title-max-length": "Il titolo deve essere inferiore a 256 caratteri.", + "key-required": "La chiave è obbligatoria.", + "key-pattern": "La chiave non è valida.", + "key-max-length": "La chiave deve essere inferiore a 256 caratteri." + }, + "required-fields": "Campi obbligatori mancanti" + }, + "content-type": { + "json": "Json", + "text": "Testo", + "binary": "Binario (Base64)" + }, + "color": { + "color": "Colore" + }, + "customer": { + "customer": "Cliente", + "customers": "Clienti", + "management": "Gestione clienti", + "dashboard": "Dashboard cliente", + "dashboards": "Dashboard clienti", + "devices": "Dispositivi cliente", + "entity-views": "Viste entità cliente", + "assets": "Asset cliente", + "public-dashboards": "Dashboard pubbliche", + "public-devices": "Dispositivi pubblici", + "public-assets": "Asset pubblici", + "public-entity-views": "Viste entità pubbliche", + "add": "Aggiungi cliente", + "delete": "Elimina cliente", + "manage-customer-users": "Gestisci utenti cliente", + "manage-customer-devices": "Gestisci dispositivi cliente", + "manage-customer-dashboards": "Gestisci dashboard cliente", + "manage-public-devices": "Gestisci dispositivi pubblici", + "manage-public-dashboards": "Gestisci dashboard pubbliche", + "manage-customer-assets": "Gestisci asset cliente", + "manage-customer-edges": "Gestisci edge cliente", + "manage-public-assets": "Gestisci asset pubblici", + "add-customer-text": "Aggiungi nuovo cliente", + "no-customers-text": "Nessun cliente trovato", + "customer-details": "Dettagli cliente", + "delete-customer-title": "Sei sicuro di voler eliminare il cliente '{{customerTitle}}'?", + "delete-customer-text": "Attenzione, dopo la conferma il cliente e tutti i dati associati saranno irrimediabilmente persi.", + "delete-customers-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 cliente} other {# clienti} }?", + "delete-customers-action-title": "Elimina { count, plural, =1 {1 cliente} other {# clienti} }", + "delete-customers-text": "Attenzione, dopo la conferma tutti i clienti selezionati saranno eliminati e tutti i dati associati saranno irrimediabilmente persi.", + "manage-users": "Gestisci utenti", + "manage-assets": "Gestisci asset", + "manage-devices": "Gestisci dispositivi", + "manage-dashboards": "Gestisci dashboard", + "title": "Titolo", + "title-required": "Il titolo è obbligatorio.", + "title-max-length": "Il titolo deve essere inferiore a 256", + "description": "Descrizione", + "details": "Dettagli", + "events": "Eventi", + "copyId": "Copia ID cliente", + "idCopiedMessage": "ID cliente copiato negli appunti", + "select-customer": "Seleziona cliente", + "no-customers-matching": "Nessun cliente corrispondente a '{{entity}}' trovato.", + "customer-required": "Il cliente è obbligatorio", + "select-default-customer": "Seleziona cliente predefinito", + "default-customer": "Cliente predefinito", + "default-customer-required": "Il cliente predefinito è obbligatorio per eseguire il debug del dashboard a livello di Tenant", + "search": "Cerca clienti", + "selected-customers": "{ count, plural, =1 {1 cliente} other {# clienti} } selezionato/i", + "edges": "Istanza edge cliente", + "manage-edges": "Gestisci edge" + }, + "css-size": { + "size-value-required": "Il valore della dimensione è obbligatorio", + "invalid-size-value": "Valore della dimensione non valido" + }, + "date": { + "last-update-n-ago": "Ultimo aggiornamento N fa", + "last-update-n-ago-text": "Ultimo aggiornamento {{ agoText }}", + "custom-date": "Data personalizzata", + "format": "Formato", + "preview": "Anteprima", + "auto": "Auto", + "time-granularity-formats": "Formati di granularità temporale", + "unit-year": "Anni", + "unit-month": "Mesi", + "unit-day": "Giorni", + "unit-hour": "Ore", + "unit-minute": "Minuti", + "unit-second": "Secondi", + "unit-millisecond": "Millisecondi" + }, + "datetime": { + "date-from": "Data da", + "time-from": "Ora da", + "date-to": "Data a", + "time-to": "Ora a", + "from": "Da", + "to": "A" + }, + "dashboard": { + "dashboard": "Dashboard", + "dashboards": "Dashboard", + "management": "Gestione dashboard", + "view-dashboards": "Visualizza dashboard", + "add": "Aggiungi dashboard", + "assign-dashboard-to-customer": "Assegna dashboard al cliente", + "assign-dashboard-to-customer-text": "Seleziona le dashboard da assegnare al cliente", + "assign-to-customer-text": "Seleziona il cliente a cui assegnare la dashboard", + "assign-to-customer": "Assegna al cliente", + "unassign-from-customer": "Disassegna dal cliente", + "make-public": "Rendi dashboard pubblica", + "make-private": "Rendi dashboard privata", + "manage-assigned-customers": "Gestisci clienti assegnati", + "assigned-customers": "Clienti assegnati", + "assign-to-customers": "Assegna dashboard ai clienti", + "assign-to-customers-text": "Seleziona i clienti a cui assegnare la dashboard", + "unassign-from-customers": "Disassegna dashboard dai clienti", + "unassign-from-customers-text": "Seleziona i clienti da cui disassegnare la dashboard", + "no-dashboards-text": "Nessuna dashboard trovata", + "no-widgets": "Nessun widget configurato", + "add-widget": "Aggiungi nuovo widget", + "add-widget-button-text": "Aggiungi widget", + "title": "Titolo", + "image": "Immagine dashboard", + "mobile-app-settings": "Impostazioni applicazione mobile", + "mobile-order": "Ordine della dashboard nell'app mobile", + "mobile-hide": "Nascondi dashboard nell'app mobile", + "update-image": "Aggiorna immagine dashboard", + "take-screenshot": "Cattura screenshot", + "select-widget-title": "Seleziona widget", + "select-widget-value": "{{title}}: seleziona widget", + "select-widget-subtitle": "Elenco dei tipi di widget disponibili", + "delete": "Elimina dashboard", + "title-required": "Il titolo è obbligatorio.", + "title-max-length": "Il titolo deve essere inferiore a 256", + "description": "Descrizione", + "details": "Dettagli", + "dashboard-details": "Dettagli dashboard", + "add-dashboard-text": "Aggiungi nuova dashboard", + "assign-dashboards": "Assegna dashboard", + "assign-new-dashboard": "Assegna nuova dashboard", + "assign-dashboards-text": "Assegna { count, plural, =1 {1 dashboard} other {# dashboard} } ai clienti", + "unassign-dashboards-action-text": "Disassegna { count, plural, =1 {1 dashboard} other {# dashboard} } dai clienti", + "delete-dashboards": "Elimina dashboard", + "unassign-dashboards": "Disassegna dashboard", + "unassign-dashboards-action-title": "Disassegna { count, plural, =1 {1 dashboard} other {# dashboard} } dal cliente", + "delete-dashboard-title": "Sei sicuro di voler eliminare la dashboard '{{dashboardTitle}}'?", + "delete-dashboard-text": "Attenzione, dopo la conferma la dashboard e tutti i dati associati saranno irrimediabilmente persi.", + "delete-dashboards-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 dashboard} other {# dashboard} }?", + "delete-dashboards-action-title": "Elimina { count, plural, =1 {1 dashboard} other {# dashboard} }", + "delete-dashboards-text": "Attenzione, dopo la conferma tutte le dashboard selezionate saranno eliminate e tutti i dati correlati saranno irrimediabilmente persi.", + "unassign-dashboard-title": "Sei sicuro di voler disassegnare la dashboard '{{dashboardTitle}}'?", + "unassign-dashboard-text": "Dopo la conferma la dashboard sarà disassegnata e non sarà accessibile dal cliente.", + "unassign-dashboard": "Disassegna dashboard", + "unassign-dashboards-title": "Sei sicuro di voler disassegnare { count, plural, =1 {1 dashboard} other {# dashboard} }?", + "unassign-dashboards-text": "Dopo la conferma tutte le dashboard selezionate saranno disassegnate e non saranno accessibili dal cliente.", + "public-dashboard-title": "La dashboard è ora pubblica", + "public-dashboard-text": "La tua dashboard {{dashboardTitle}} è ora pubblica e accessibile tramite il seguente link:", + "public-dashboard-notice": "Nota: Non dimenticare di rendere pubblici i dispositivi correlati per accedere ai loro dati.", + "make-private-dashboard-title": "Sei sicuro di voler rendere privata la dashboard '{{dashboardTitle}}'?", + "make-private-dashboard-text": "Dopo la conferma la dashboard sarà resa privata e non sarà accessibile da altri.", + "make-private-dashboard": "Rendi dashboard privata", + "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", + "select-dashboard": "Seleziona dashboard", + "no-dashboards-matching": "Nessuna dashboard corrispondente a '{{entity}}' trovata.", + "dashboard-required": "La dashboard è obbligatoria.", + "select-existing": "Seleziona dashboard esistente", + "create-new": "Crea nuova dashboard", + "new-dashboard-title": "Titolo nuova dashboard", + "open-dashboard": "Apri dashboard", + "set-background": "Imposta sfondo", + "background-color": "Colore sfondo", + "background-image": "Immagine di sfondo", + "background-size-mode": "Modalità dimensione sfondo", + "no-image": "Nessuna immagine selezionata", + "empty-image": "Nessuna immagine", + "drop-image": "Trascina un'immagine o fai clic per selezionare un file da caricare.", + "maximum-upload-file-size": "Dimensione massima file caricabile: {{ size }}", + "cannot-upload-file": "Impossibile caricare il file", + "settings": "Impostazioni", + "move-all-widgets": "Sposta tutti i widget", + "move-by": "Sposta di", + "cols": "colonne", + "rows": "righe", + "layout": "Layout", + "layout-type-default": "Predefinito", + "layout-type-scada": "SCADA", + "layout-type-divider": "Divisore", + "layout-settings-type": "Impostazioni layout: breakpoint {{ type }}", + "columns-count": "Numero colonne", + "columns-count-required": "Il numero di colonne è obbligatorio.", + "min-columns-count-message": "Il numero minimo consentito di colonne è 10.", + "max-columns-count-message": "Il numero massimo consentito di colonne è 1000.", + "min-layout-width": "Larghezza minima layout", + "columns-suffix": "colonne", + "widgets-margins": "Margine tra i widget", + "margin-required": "Il valore del margine è obbligatorio.", + "min-margin-message": "Il valore minimo del margine è 0.", + "max-margin-message": "Il valore massimo del margine è 50.", + "horizontal-margin": "Margine orizzontale", + "horizontal-margin-required": "Il valore del margine orizzontale è obbligatorio.", + "min-horizontal-margin-message": "Il valore minimo del margine orizzontale è 0.", + "max-horizontal-margin-message": "Il valore massimo del margine orizzontale è 50.", + "vertical-margin": "Margine verticale", + "vertical-margin-required": "Il valore del margine verticale è obbligatorio.", + "min-vertical-margin-message": "Il valore minimo del margine verticale è 0.", + "max-vertical-margin-message": "Il valore massimo del margine verticale è 50.", + "apply-outer-margin": "Applica margine ai bordi del layout", + "autofill-height": "Altezza layout automatica", + "mobile-layout": "Impostazioni layout mobile", + "mobile-row-height": "Altezza riga mobile", + "mobile-row-height-required": "L'altezza della riga mobile è obbligatoria.", + "min-mobile-row-height-message": "Il valore minimo per l'altezza della riga mobile è 5 pixel.", + "max-mobile-row-height-message": "Il valore massimo per l'altezza della riga mobile è 200 pixel.", + "row-height": "Altezza riga", + "row-height-required": "Il valore dell'altezza riga è obbligatorio.", + "min-row-height-message": "Il valore minimo per l'altezza riga è 5 pixel.", + "max-row-height-message": "Il valore massimo per l'altezza riga è 200 pixel.", + "display-first-in-mobile-view": "Visualizza per primo nella vista mobile", + "title-settings": "Impostazioni titolo", + "display-title": "Visualizza titolo dashboard", + "title-color": "Colore titolo", + "toolbar-settings": "Impostazioni barra strumenti", + "hide-toolbar": "Nascondi barra strumenti", + "toolbar-always-open": "Mantieni barra strumenti aperta", + "display-dashboards-selection": "Visualizza selezione dashboard", + "display-entities-selection": "Visualizza selezione entità", + "display-filters": "Visualizza filtri", + "display-dashboard-timewindow": "Visualizza intervallo temporale", + "display-dashboard-export": "Visualizza esportazione", + "display-update-dashboard-image": "Visualizza aggiornamento immagine dashboard", + "dashboard-logo-settings": "Impostazioni logo dashboard", + "display-dashboard-logo": "Visualizza logo in modalità schermo intero", + "dashboard-logo-image": "Immagine logo dashboard", + "advanced-settings": "Impostazioni avanzate", + "dashboard-css": "CSS dashboard", + "import": "Importa dashboard", + "export": "Esporta dashboard", + "export-failed-error": "Impossibile esportare la dashboard: {{error}}", + "export-prompt": "Incorpora immagini e risorse della dashboard", + "create-new-dashboard": "Crea nuova dashboard", + "dashboard-file": "File dashboard", + "invalid-dashboard-file-error": "Impossibile importare la dashboard: struttura dati della dashboard non valida.", + "dashboard-import-missing-aliases-title": "Configura alias utilizzati dalla dashboard importata", + "create-new-widget": "Crea nuovo widget", + "import-widget": "Importa widget", + "widget-file": "File widget", + "invalid-widget-file-error": "Impossibile importare il widget: struttura dati del widget non valida.", + "widget-import-missing-aliases-title": "Configura alias utilizzati dal widget importato", + "open-toolbar": "Apri barra strumenti dashboard", + "close-toolbar": "Chiudi barra strumenti", + "configuration-error": "Errore di configurazione", + "alias-resolution-error-title": "Errore configurazione alias dashboard", + "invalid-aliases-config": "Impossibile trovare dispositivi corrispondenti ad alcuni filtri alias.
Contatta l'amministratore per risolvere il problema.", + "select-devices": "Seleziona dispositivi", + "assignedToCustomer": "Assegnato al cliente", + "assignedToCustomers": "Assegnato ai clienti", + "public": "Pubblico", + "copyId": "Copia ID dashboard", + "idCopiedMessage": "ID dashboard copiato negli appunti", + "public-link": "Link pubblico", + "copy-public-link": "Copia link pubblico", + "public-link-copied-message": "Link pubblico della dashboard copiato negli appunti", + "manage-states": "Gestisci stati della dashboard", + "states": "Stati dashboard", + "states-short": "Stati", + "search-states": "Cerca stati dashboard", + "selected-states": "{ count, plural, =1 {1 stato dashboard} other {# stati dashboard} } selezionati", + "edit-state": "Modifica stato dashboard", + "delete-state": "Elimina stato dashboard", + "add-state": "Aggiungi stato dashboard", + "no-states-text": "Nessuno stato trovato", + "state": "Stato dashboard", + "state-name": "Nome", + "state-name-required": "Il nome dello stato dashboard è obbligatorio.", + "state-id": "ID Stato", + "state-id-required": "L'ID dello stato dashboard è obbligatorio.", + "state-id-exists": "Uno stato dashboard con lo stesso ID esiste già.", + "is-root-state": "Stato radice", + "delete-state-title": "Elimina stato dashboard", + "delete-state-text": "Sei sicuro di voler eliminare lo stato della dashboard con nome '{{stateName}}'?", + "show-details": "Mostra dettagli", + "hide-details": "Nascondi dettagli", + "select-state": "Seleziona stato di destinazione", + "state-controller": "Controllore stato", + "state-controller-default": "statico (deprecato)", + "search": "Cerca dashboard", + "selected-dashboards": "{ count, plural, =1 {1 dashboard} other {# dashboard} } selezionate", + "home-dashboard": "Dashboard principale", + "home-dashboard-hide-toolbar": "Nascondi barra strumenti della dashboard principale", + "unassign-dashboard-from-edge-text": "Dopo la conferma la dashboard sarà disassegnata e non sarà più accessibile dall'edge.", + "unassign-dashboards-from-edge-title": "Sei sicuro di voler disassegnare { count, plural, =1 {1 dashboard} other {# dashboard} }?", + "unassign-dashboards-from-edge-text": "Dopo la conferma tutte le dashboard selezionate saranno disassegnate e non saranno più accessibili dall'edge.", + "assign-dashboard-to-edge": "Assegna Dashboard a Edge", + "assign-dashboard-to-edge-text": "Seleziona le dashboard da assegnare all'edge", + "non-existent-dashboard-state-error": "Stato dashboard con ID \"{{ stateId }}\" non trovato", + "edit-mode": "Modalità modifica", + "duplicate-state-action": "Duplica stato", + "breakpoint-value": "Breakpoint ({{ value }})", + "breakpoints-id": { + "default": "Predefinito", + "xs": "Mobile (xs)", + "sm": "Tablet (sm)", + "md": "Laptop (md)", + "lg": "Desktop (lg)", + "xl": "Desktop (xl)" + }, + "view-format-type-grid": "Griglia", + "view-format-type-list": "Elenco", + "view-format": "Formato visualizzazione" + }, + "datakey": { + "settings": "Impostazioni", + "general": "Generale", + "advanced": "Avanzate", + "key": "Chiave", + "keys": "Chiavi", + "label": "Etichetta", + "color": "Colore", + "units": "Simbolo speciale da mostrare accanto al valore", + "decimals": "Numero di cifre dopo il punto decimale", + "data-generation-func": "Funzione di generazione dati", + "use-data-post-processing-func": "Usa funzione di post-elaborazione dati", + "configuration": "Configurazione chiave dati", + "timeseries": "Serie temporale", + "attributes": "Attributi", + "entity-field": "Campo entità", + "alarm": "Campi allarme", + "timeseries-required": "Le serie temporali dell'entità sono richieste.", + "timeseries-or-attributes-required": "Serie temporali o attributi dell'entità richiesti.", + "alarm-fields-timeseries-or-attributes-required": "Campi allarme o serie temporali/attributi dell'entità richiesti.", + "maximum-timeseries-or-attributes": "Massimo { count, plural, =1 {1 serie temporale/attributo consentito.} other {# serie temporali/attributi consentiti} }", + "alarm-fields-required": "I campi allarme sono richiesti.", + "function-types": "Tipi di funzione", + "function-type": "Tipo di funzione", + "function-types-required": "Tipi di funzione richiesti.", + "data-keys": "Chiavi dati", + "data-key": "Chiave dati", + "data-keys-required": "Chiavi dati richieste.", + "data-key-required": "Chiave dati richiesta.", + "alarm-keys": "Chiavi dati allarme", + "alarm-key": "Chiave dati allarme", + "alarm-key-functions": "Funzioni chiave allarme", + "alarm-key-function": "Funzione chiave allarme", + "latest-keys": "Ultime chiavi dati", + "latest-key": "Ultima chiave dati", + "latest-key-functions": "Funzioni ultima chiave", + "latest-key-function": "Funzione ultima chiave", + "timeseries-keys": "Chiavi dati serie temporale", + "timeseries-key": "Chiave dati serie temporale", + "timeseries-key-functions": "Funzioni chiave serie temporale", + "timeseries-key-function": "Funzione chiave serie temporale", + "maximum-function-types": "Massimo { count, plural, =1 {1 tipo di funzione consentito.} other {# tipi di funzione consentiti} }", + "time-description": "timestamp del valore corrente;", + "value-description": "valore corrente;", + "prev-value-description": "risultato della chiamata precedente della funzione;", + "time-prev-description": "timestamp del valore precedente;", + "prev-orig-value-description": "valore originale precedente;", + "aggregation": "Aggregazione", + "aggregation-type-hint-common": "Per motivi di prestazioni, il calcolo dei valori aggregati è disponibile solo per intervalli di tempo fissi come \"oggi\", \"mese corrente\", ecc., e non per finestre temporali mobili come 'ultimi 30 minuti' o 'ultime 24 ore'.", + "aggregation-type-none-hint": "Prendi l'ultimo valore.", + "aggregation-type-min-hint": "Trova il valore minimo tra i punti dati nella finestra temporale selezionata.", + "aggregation-type-max-hint": "Trova il valore massimo tra i punti dati nella finestra temporale selezionata.", + "aggregation-type-avg-hint": "Calcola il valore medio tra i punti dati nella finestra temporale selezionata.", + "aggregation-type-sum-hint": "Somma tutti i valori dei punti dati nella finestra temporale selezionata.", + "aggregation-type-count-hint": "Numero totale di punti dati nella finestra temporale selezionata.", + "delta-calculation": "Calcolo delta", + "enable-delta-calculation": "Abilita calcolo delta", + "enable-delta-calculation-hint": "Quando abilitato, il valore della chiave dati è calcolato in base ai valori aggregati per una finestra temporale selezionata e un periodo di confronto specificato. Per motivi di prestazioni, il calcolo delta è disponibile solo per finestre storiche e non per valori in tempo reale. Ad esempio, è possibile calcolare il delta tra il consumo energetico di ieri rispetto al giorno prima.", + "delta-calculation-result": "Risultato calcolo delta", + "delta-calculation-result-previous-value": "Valore precedente", + "delta-calculation-result-delta-absolute": "Delta (assoluto)", + "delta-calculation-result-delta-percent": "Delta (percentuale)", + "source": "Fonte", + "latest": "Ultimo", + "latest-value": "Ultimo valore", + "delta": "delta", + "percent": "percentuale", + "absolute": "assoluto" + }, + "datasource": { + "type": "Tipo sorgente dati", + "name": "Nome", + "label": "Etichetta", + "add-datasource-prompt": "Aggiungi una sorgente dati" + }, + "details": { + "details": "Dettagli", + "edit-mode": "Modalità modifica", + "edit-json": "Modifica JSON", + "toggle-edit-mode": "Attiva/disattiva modalità modifica" + }, + "device": { + "device": "Dispositivo", + "device-required": "Dispositivo richiesto.", + "devices": "Dispositivi", + "management": "Gestione dispositivi", + "view-devices": "Visualizza dispositivi", + "device-alias": "Alias del dispositivo", + "device-type-max-length": "Il tipo di dispositivo deve essere inferiore a 256 caratteri", + "aliases": "Alias dei dispositivi", + "no-alias-matching": "'{{alias}}' non trovato.", + "no-aliases-found": "Nessun alias trovato.", + "no-key-matching": "'{{key}}' non trovato.", + "no-keys-found": "Nessuna chiave trovata.", + "create-new-alias": "Creane uno nuovo!", + "create-new-key": "Creane uno nuovo!", + "duplicate-alias-error": "Alias duplicato trovato '{{alias}}'.
Gli alias dei dispositivi devono essere univoci all'interno della dashboard.", + "configure-alias": "Configura alias '{{alias}}'", + "no-devices-matching": "Nessun dispositivo corrispondente a '{{entity}}' trovato.", + "alias": "Alias", + "alias-required": "Alias del dispositivo richiesto.", + "remove-alias": "Rimuovi alias del dispositivo", + "add-alias": "Aggiungi alias del dispositivo", + "name-starts-with": "Espressione nome dispositivo", + "help-text": "Usa '%' secondo necessità: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list": "Elenco dispositivi", + "use-device-name-filter": "Usa filtro", + "device-list-empty": "Nessun dispositivo selezionato.", + "device-name-filter-required": "Filtro nome dispositivo richiesto.", + "device-name-filter-no-device-matched": "Nessun dispositivo trovato con nome che inizia per '{{device}}'.", + "add": "Aggiungi dispositivo", + "assign-to-customer": "Assegna al cliente", + "assign-device-to-customer": "Assegna dispositivo(i) al cliente", + "assign-device-to-customer-text": "Seleziona i dispositivi da assegnare al cliente", + "make-public": "Rendi pubblico il dispositivo", + "make-private": "Rendi privato il dispositivo", + "no-devices-text": "Nessun dispositivo trovato", + "assign-to-customer-text": "Seleziona il cliente a cui assegnare i dispositivi", + "device-details": "Dettagli dispositivo", + "add-device-text": "Aggiungi nuovo dispositivo", + "credentials": "Credenziali", + "manage-credentials": "Gestisci credenziali", + "delete": "Elimina dispositivo", + "assign-devices": "Assegna dispositivi", + "assign-devices-text": "Assegna { count, plural, =1 {1 dispositivo} other {# dispositivi} } al cliente", + "delete-devices": "Elimina dispositivi", + "unassign-from-customer": "Rimuovi assegnazione dal cliente", + "unassign-devices": "Rimuovi assegnazione dispositivi", + "unassign-devices-action-title": "Rimuovi assegnazione di { count, plural, =1 {1 dispositivo} other {# dispositivi} } dal cliente", + "unassign-device-from-edge-title": "Sei sicuro di voler rimuovere il dispositivo '{{deviceName}}' dall'edge?", + "unassign-device-from-edge-text": "Dopo la conferma il dispositivo non sarà più accessibile dall'edge.", + "unassign-devices-from-edge": "Rimuovi dispositivi dall'edge", + "assign-new-device": "Assegna nuovo dispositivo", + "make-public-device-title": "Sei sicuro di voler rendere pubblico il dispositivo '{{deviceName}}'?", + "make-public-device-text": "Dopo la conferma il dispositivo e tutti i suoi dati saranno pubblici e accessibili.", + "make-private-device-title": "Sei sicuro di voler rendere privato il dispositivo '{{deviceName}}'?", + "make-private-device-text": "Dopo la conferma il dispositivo e tutti i suoi dati saranno privati e non accessibili da altri.", + "view-credentials": "Visualizza credenziali", + "delete-device-title": "Sei sicuro di voler eliminare il dispositivo '{{deviceName}}'?", + "delete-device-text": "Attenzione, dopo la conferma il dispositivo e tutti i dati correlati non saranno più recuperabili.", + "delete-devices-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", + "delete-devices-action-title": "Elimina { count, plural, =1 {1 dispositivo} other {# dispositivi} }", + "delete-devices-text": "Attenzione, dopo la conferma tutti i dispositivi selezionati verranno rimossi e i dati correlati non saranno più recuperabili.", + "unassign-device-title": "Sei sicuro di voler rimuovere l'assegnazione del dispositivo '{{deviceName}}'?", + "unassign-device-text": "Dopo la conferma il dispositivo non sarà più accessibile dal cliente.", + "unassign-device": "Rimuovi assegnazione dispositivo", + "unassign-devices-title": "Sei sicuro di voler rimuovere l'assegnazione di { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", + "unassign-devices-text": "Dopo la conferma tutti i dispositivi selezionati saranno rimossi dal cliente.", + "device-credentials": "Credenziali del dispositivo", + "loading-device-credentials": "Caricamento credenziali del dispositivo...", + "credentials-type": "Tipo di credenziali", + "access-token": "Token di accesso", + "access-token-required": "Token di accesso richiesto.", + "access-token-invalid": "La lunghezza del token di accesso deve essere compresa tra 1 e 32 caratteri.", + "certificate-pem-format": "Certificato in formato PEM", + "certificate-pem-format-required": "Certificato richiesto.", + "copy-access-token": "Copia token di accesso", + "copy-certificate": "Copia certificato", + "copy-client-id": "Copia ID client", + "copy-user-name": "Copia nome utente", + "copy-password": "Copia password", + "generate-client-id": "Genera ID client", + "generate-user-name": "Genera nome utente", + "generate-password": "Genera password", + "generate-access-token": "Genera token di accesso", + "lwm2m-security-config": { + "identity": "Identità del client", + "identity-required": "L'identità del client è obbligatoria.", + "identity-tooltip": "L'identificatore PSK è un identificatore arbitrario fino a 128 byte, come descritto nello standard [RFC7925].\nL'identificatore PSK DEVE essere prima convertito in una stringa di caratteri e poi codificato in ottetti usando UTF-8.", + "client-key": "Chiave del client", + "client-key-required": "La chiave del client è obbligatoria.", + "client-key-tooltip-prk": "La chiave pubblica o l'id RPK devono essere conformi allo standard [RFC7250] e codificati in formato Base64!", + "client-key-tooltip-psk": "La chiave PSK deve rispettare lo standard [RFC4279] e deve essere in formato HexDec: 32, 64, 128 caratteri!", + "endpoint": "Nome endpoint del client", + "endpoint-required": "Il nome endpoint del client è obbligatorio.", + "client-public-key": "Chiave pubblica del client", + "client-public-key-hint": "Se la chiave pubblica del client è vuota, verrà utilizzato il certificato attendibile", + "client-public-key-tooltip": "La chiave pubblica X509 deve essere in formato DER-encoded X509v3, supportare esclusivamente l'algoritmo EC e deve essere codificata in Base64!", + "mode": "Modalità configurazione sicurezza", + "client-tab": "Configurazione sicurezza client", + "client-certificate": "Certificato del client", + "bootstrap-tab": "Client bootstrap", + "bootstrap-server": "Server Bootstrap", + "lwm2m-server": "Server LwM2M", + "client-publicKey-or-id": "Chiave pubblica o ID del client", + "client-publicKey-or-id-required": "Chiave pubblica o ID del client è obbligatoria.", + "client-publicKey-or-id-tooltip-psk": "L'identificatore PSK è un identificatore arbitrario fino a 128 byte, come descritto nello standard [RFC7925].\nL'identificatore PSK DEVE essere prima convertito in una stringa e poi codificato in UTF-8.", + "client-publicKey-or-id-tooltip-rpk": "La chiave pubblica o l'id RPK devono essere nello standard [RFC7250] e codificati in formato Base64!", + "client-publicKey-or-id-tooltip-x509": "La chiave pubblica X509 deve essere in formato DER-encoded X509v3, supportare solo l'algoritmo EC e codificata in Base64", + "client-secret-key": "Chiave segreta del client", + "client-secret-key-required": "Chiave segreta del client è obbligatoria.", + "client-secret-key-tooltip-psk": "La chiave PSK deve rispettare lo standard [RFC4279] e deve essere in formato HexDec: 32, 64, 128 caratteri!", + "client-secret-key-tooltip-prk": "La chiave segreta RPK deve essere in formato PKCS_8 (codifica DER, standard [RFC5958]) e codificata in Base64!", + "client-secret-key-tooltip-x509": "La chiave segreta X509 deve essere in formato PKCS_8 (codifica DER, standard [RFC5958]) e codificata in Base64!" + }, + "client-id": "ID client", + "client-id-pattern": "Contiene carattere non valido.", + "user-name": "Nome utente", + "user-name-required": "Nome utente richiesto.", + "client-id-or-user-name-necessary": "ID client e/o nome utente sono obbligatori", + "password": "Password", + "secret": "Segreto", + "secret-required": "Segreto richiesto.", + "device-type": "Profilo dispositivo", + "device-type-required": "Tipo di dispositivo richiesto.", + "select-device-type": "Seleziona tipo di dispositivo", + "enter-device-type": "Inserisci profilo dispositivo", + "any-device": "Qualsiasi dispositivo", + "no-device-types-matching": "Nessun profilo dispositivo corrispondente a '{{entitySubtype}}' trovato.", + "device-type-list-empty": "Nessun profilo dispositivo selezionato!", + "device-profile-type-list-empty": "Deve essere selezionato almeno un profilo dispositivo.", + "device-types": "Tipi di dispositivo", + "name": "Nome", + "name-required": "Nome richiesto.", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri", + "label-max-length": "L'etichetta deve essere inferiore a 256 caratteri", + "description": "Descrizione", + "label": "Etichetta", + "events": "Eventi", + "details": "Dettagli", + "copyId": "Copia ID dispositivo", + "copyAccessToken": "Copia token di accesso", + "copy-mqtt-authentication": "Copia credenziali MQTT", + "idCopiedMessage": "ID dispositivo copiato negli appunti", + "accessTokenCopiedMessage": "Token di accesso del dispositivo copiato negli appunti", + "mqtt-authentication-copied-message": "Credenziali MQTT del dispositivo copiate negli appunti", + "assignedToCustomer": "Assegnato al cliente", + "unable-delete-device-alias-title": "Impossibile eliminare alias dispositivo", + "unable-delete-device-alias-text": "L'alias del dispositivo '{{deviceAlias}}' non può essere eliminato poiché è utilizzato dai seguenti widget:
{{widgetsList}}", + "is-gateway": "È un gateway", + "overwrite-activity-time": "Sovrascrivi tempo di attività per il dispositivo connesso", + "device-filter": "Filtro dispositivo", + "device-filter-title": "Filtro dispositivo", + "filter-title": "Filtro", + "device-state": "Stato dispositivo", + "state": "Stato", + "any": "Qualsiasi", + "active": "Attivo", + "inactive": "Inattivo", + "public": "Pubblico", + "device-public": "Il dispositivo è pubblico", + "select-device": "Seleziona dispositivo", + "import": "Importa dispositivo", + "device-file": "File del dispositivo", + "search": "Cerca dispositivi", + "selected-devices": "{ count, plural, =1 {1 dispositivo} other {# dispositivi} } selezionati", + "device-configuration": "Configurazione dispositivo", + "transport-configuration": "Configurazione trasporto", + "wizard": { + "device-details": "Dettagli del dispositivo" + }, + "unassign-devices-from-edge-title": "Sei sicuro di voler disassociare { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", + "unassign-devices-from-edge-text": "Dopo la conferma, tutti i dispositivi selezionati saranno disassociati e non saranno più accessibili dall'edge.", + "time": "Tempo", + "connectivity": { + "check-connectivity": "Verifica connettività", + "device-created-check-connectivity": "Dispositivo creato. Verifichiamo la connettività!", + "loading-check-connectivity-command": "Caricamento dei comandi per la verifica della connettività...", + "use-following-instructions": "Usa le seguenti istruzioni per inviare la telemetria per conto del dispositivo tramite shell", + "execute-following-command": "Esegui il seguente comando", + "install-curl-windows": "A partire da Windows 10 b17063, cURL è disponibile di default", + "install-curl-macos": "A partire da Mac OS X 10.2 6C115 (Jaguar), cURL è disponibile di default", + "install-mqtt-windows": "Utilizza le istruzioni per scaricare, installare, configurare ed eseguire mosquitto_pub", + "install-coap-client": "Utilizza le istruzioni per scaricare, installare, configurare ed eseguire coap-client", + "install-necessary-client-tools": "Installa gli strumenti client necessari", + "mqtts-x509-command": "Consulta la seguente documentazione per connettere il dispositivo tramite MQTT con autorizzazione X509", + "coaps-x509-command": "Consulta la seguente documentazione per connettere il dispositivo tramite CoAP su DTLS con autorizzazione X509", + "snmp-command": "Consulta la seguente documentazione per connettere il dispositivo tramite SNMP.", + "sparkplug-command": "Consulta la seguente documentazione per connettere il dispositivo tramite MQTT Sparkplug.", + "lwm2m-command": "Consulta la seguente documentazione per connettere il dispositivo tramite LWM2M." + } + }, + "dynamic-form": { + "property": { + "properties": "Proprietà", + "property": "Proprietà", + "id": "Id", + "name": "Nome", + "type": "Tipo", + "type-text": "Testo", + "type-password": "Password", + "type-textarea": "Area di testo", + "type-number": "Numero", + "type-switch": "Interruttore", + "type-select": "Selezione", + "type-radios": "Pulsanti radio", + "type-datetime": "Data/Ora", + "type-image": "Immagine", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "Colore", + "type-color-settings": "Impostazioni colore", + "type-font": "Carattere", + "type-units": "Unità", + "type-icon": "Icona", + "type-fieldset": "Fieldset", + "type-array": "Array", + "type-html-section": "Sezione HTML", + "group-title": "Titolo del gruppo", + "no-properties": "Nessuna proprietà configurata", + "add-property": "Aggiungi proprietà", + "property-settings": "Impostazioni proprietà", + "remove-property": "Rimuovi proprietà", + "default-value": "Valore predefinito", + "value-required": "Valore richiesto", + "number-settings": "Impostazioni numeriche", + "min": "Min", + "max": "Max", + "step": "Passo", + "selected-options-limit": "Limite opzioni selezionate", + "advanced-ui-settings": "Impostazioni UI avanzate", + "disable-on-property": "Disabilita in base alla proprietà", + "display-condition-function": "Funzione di condizione di visualizzazione", + "sub-label": "Sotto etichetta", + "vertical-divider-after": "Divisore verticale dopo", + "input-field-suffix": "Suffisso campo di input", + "property-row-classes": "Classi riga proprietà", + "property-field-classes": "Classi campo proprietà", + "not-unique-property-ids-error": "Gli ID delle proprietà devono essere univoci!", + "enable-multiple-select": "Abilita selezione multipla", + "allow-empty-select-option": "Consenti opzione vuota", + "select-options": "Opzioni di selezione", + "not-unique-select-option-value-error": "I valori delle opzioni devono essere univoci!", + "value": "Valore", + "label": "Etichetta", + "add-option": "Aggiungi opzione", + "no-options": "Nessuna opzione configurata", + "remove-option": "Rimuovi opzione", + "textarea-rows": "Righe dell'area di testo", + "help-id": "ID di aiuto", + "buttons-direction": "Direzione dei pulsanti", + "direction-row": "Riga", + "direction-column": "Colonna", + "radio-button-options": "Opzioni pulsanti radio", + "datetime-type": "Tipo campo Data/Ora", + "datetime-type-date": "Data", + "datetime-type-time": "Ora", + "datetime-type-datetime": "Data/Ora", + "enable-clear-button": "Abilita pulsante di cancellazione", + "html-section-settings": "Impostazioni sezione HTML", + "html-section-classes": "Classi sezione HTML", + "html-section-content": "Contenuto sezione HTML", + "array-item": "Elemento dell'array", + "item-type": "Tipo di elemento", + "item-name": "Nome dell'elemento", + "no-items": "Nessun elemento" + }, + "clear-form": "Cancella modulo", + "clear-form-prompt": "Sei sicuro di voler rimuovere tutte le proprietà del modulo?", + "import-form": "Importa modulo da JSON", + "export-form": "Esporta modulo in JSON", + "json-file": "File JSON", + "json-content": "Contenuto JSON", + "invalid-form-json-file-error": "Impossibile importare il modulo da JSON: struttura dei dati JSON non valida." + }, + "asset-profile": { + "asset-profile": "Profilo asset", + "asset-profiles": "Profili asset", + "all-asset-profiles": "Tutti", + "add": "Aggiungi profilo asset", + "edit": "Modifica profilo asset", + "asset-profile-details": "Dettagli del profilo asset", + "no-asset-profiles-text": "Nessun profilo asset trovato", + "search": "Cerca profili asset", + "selected-asset-profiles": "{ count, plural, =1 {1 profilo asset} other {# profili asset} } selezionati", + "no-asset-profiles-matching": "Nessun profilo asset corrispondente a '{{entity}}' trovato.", + "asset-profile-required": "Il profilo asset è obbligatorio", + "idCopiedMessage": "ID del profilo asset copiato negli appunti", + "set-default": "Imposta come predefinito", + "delete": "Elimina profilo asset", + "copyId": "Copia ID profilo asset", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri", + "new-device-profile-name": "Nome del profilo asset", + "new-device-profile-name-required": "Il nome del profilo asset è obbligatorio.", + "name": "Nome", + "name-required": "Il nome è obbligatorio.", + "image": "Immagine profilo asset", + "description": "Descrizione", + "default": "Predefinito", + "default-rule-chain": "Catena di regole predefinita", + "default-edge-rule-chain": "Catena di regole Edge predefinita", + "default-edge-rule-chain-hint": "Utilizzato su Edge come catena di regole per elaborare i dati in arrivo per gli asset di questo profilo", + "mobile-dashboard": "Dashboard mobile", + "mobile-dashboard-hint": "Utilizzato dall'app mobile come dashboard dei dettagli dell'asset", + "select-queue-hint": "Seleziona dall'elenco a discesa.", + "delete-asset-profile-title": "Sei sicuro di voler eliminare il profilo asset '{{assetProfileName}}'?", + "delete-asset-profile-text": "Attenzione, dopo la conferma il profilo asset e tutti i dati correlati saranno irrecuperabili.", + "delete-asset-profiles-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 profilo asset} other {# profili asset} }?", + "delete-asset-profiles-text": "Attenzione, dopo la conferma tutti i profili asset selezionati saranno eliminati e tutti i dati correlati saranno irrecuperabili.", + "set-default-asset-profile-title": "Sei sicuro di voler rendere il profilo asset '{{assetProfileName}}' predefinito?", + "set-default-asset-profile-text": "Dopo la conferma, il profilo asset sarà contrassegnato come predefinito e sarà utilizzato per i nuovi asset senza profilo specificato.", + "no-asset-profiles-found": "Nessun profilo asset trovato.", + "create-new-asset-profile": "Creane uno nuovo!", + "create-asset-profile": "Crea nuovo profilo asset", + "import": "Importa profilo asset", + "export": "Esporta profilo asset", + "export-failed-error": "Impossibile esportare il profilo asset: {{error}}", + "asset-profile-file": "File profilo asset", + "invalid-asset-profile-file-error": "Impossibile importare il profilo asset: struttura dati non valida." + }, + "device-profile": { + "device-profile": "Profilo dispositivo", + "device-profiles": "Profili dispositivo", + "all-device-profiles": "Tutti", + "add": "Aggiungi profilo dispositivo", + "edit": "Modifica profilo dispositivo", + "device-profile-details": "Dettagli profilo dispositivo", + "no-device-profiles-text": "Nessun profilo dispositivo trovato", + "search": "Cerca profili dispositivo", + "selected-device-profiles": "{ count, plural, =1 {1 profilo dispositivo} other {# profili dispositivo} } selezionati", + "no-device-profiles-matching": "Nessun profilo dispositivo corrispondente a '{{entity}}' trovato.", + "device-profile-required": "Il profilo dispositivo è obbligatorio", + "idCopiedMessage": "ID profilo dispositivo copiato negli appunti", + "set-default": "Imposta come predefinito", + "delete": "Elimina profilo dispositivo", + "copyId": "Copia ID profilo dispositivo", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri", + "name": "Nome", + "name-required": "Il nome è obbligatorio.", + "type": "Tipo di profilo", + "type-required": "Il tipo di profilo è obbligatorio.", + "type-default": "Predefinito", + "image": "Immagine profilo dispositivo", + "transport-type": "Tipo di trasporto", + "transport-type-required": "Il tipo di trasporto è obbligatorio.", + "transport-type-default": "Predefinito", + "transport-type-default-hint": "Supporta trasporto base MQTT, HTTP e CoAP", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "Abilita impostazioni avanzate MQTT", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "Abilita impostazioni avanzate CoAP", + "transport-type-lwm2m": "LWM2M", + "transport-type-lwm2m-hint": "Tipo di trasporto LWM2M", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "Specifica configurazione trasporto SNMP", + "transport-type-http": "HTTP", + "description": "Descrizione", + "default": "Predefinito", + "profile-configuration": "Configurazione profilo", + "transport-configuration": "Configurazione trasporto", + "default-rule-chain": "Catena di regole predefinita", + "default-edge-rule-chain": "Catena di regole Edge predefinita", + "default-edge-rule-chain-hint": "Utilizzato su Edge come catena di regole per elaborare i dati in arrivo per dispositivi di questo profilo", + "mobile-dashboard": "Dashboard mobile", + "mobile-dashboard-hint": "Utilizzato dall'app mobile come dashboard dei dettagli del dispositivo", + "select-queue-hint": "Seleziona dall'elenco a discesa.", + "delete-device-profile-title": "Sei sicuro di voler eliminare il profilo dispositivo '{{deviceProfileName}}'?", + "delete-device-profile-text": "Attenzione, dopo la conferma il profilo dispositivo e tutti i dati correlati, inclusi gli aggiornamenti OTA associati, saranno irrecuperabili.", + "delete-device-profiles-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 profilo dispositivo} other {# profili dispositivo} }?", + "delete-device-profiles-text": "Attenzione, dopo la conferma tutti i profili dispositivo selezionati saranno rimossi e tutti i dati correlati, inclusi gli aggiornamenti OTA associati, saranno irrecuperabili.", + "set-default-device-profile-title": "Sei sicuro di voler rendere il profilo dispositivo '{{deviceProfileName}}' predefinito?", + "set-default-device-profile-text": "Dopo la conferma, il profilo dispositivo sarà impostato come predefinito e verrà utilizzato per i nuovi dispositivi senza profilo specificato.", + "no-device-profiles-found": "Nessun profilo dispositivo trovato.", + "create-new-device-profile": "Creane uno nuovo!", + "mqtt-device-topic-filters": "Filtri topic dispositivo MQTT", + "mqtt-device-topic-filters-unique": "I filtri topic del dispositivo MQTT devono essere unici.", + "mqtt-device-topic-filters-spark-plug": "Nodo Edge of Network (EoN) MQTT Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-hint": "Consente connessioni da nodi EoN con payload e formato topic Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "Metriche SparkPlug da salvare come attributi.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Nomi delle metriche SparkPlug che verranno salvate come attributi del dispositivo. Tutte le altre metriche saranno archiviate come telemetria.", + "mqtt-device-payload-type": "Payload dispositivo MQTT", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Abilita compatibilità con altri formati payload.", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Se abilitato, la piattaforma utilizzerà di default il formato payload Protobuf. In caso di errore di parsing, tenterà di utilizzare il formato JSON. Utile per la retrocompatibilità durante aggiornamenti firmware. Modalità soggetta a un leggero degrado delle prestazioni.", + "mqtt-use-json-format-for-default-downlink-topics": "Usa formato Json per topic di downlink predefiniti", + "mqtt-use-json-format-for-default-downlink-topics-hint": "Usato per inviare attributi e RPC via Json su v1/devices/me/.... Non influisce sui topic v2.", + "mqtt-send-ack-on-validation-exception": "Invia PUBACK in caso di errore di validazione PUBLISH", + "mqtt-send-ack-on-validation-exception-hint": "Se abilitato, invia l'acknowledgment invece di chiudere la sessione MQTT in caso di errore di validazione.", + "snmp-add-mapping": "Aggiungi mapping SNMP", + "snmp-mapping-not-configured": "Nessun mapping OID configurato per time series/telemetria", + "snmp-timseries-or-attribute-name": "Nome time series/attributo per mapping", + "snmp-timseries-or-attribute-type": "Tipo time series/attributo per mapping", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "Tipo payload obbligatorio.", + "coap-device-type": "Tipo dispositivo CoAP", + "coap-device-payload-type": "Payload dispositivo CoAP", + "coap-device-type-required": "Tipo dispositivo CoAP obbligatorio.", + "coap-device-type-default": "Predefinito", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "Supportati i caratteri jolly singolo [+] e multilivello [#].", + "telemetry-topic-filter": "Filtro topic telemetria", + "telemetry-topic-filter-required": "Filtro topic telemetria obbligatorio.", + "attributes-topic-filter": "Filtro topic pubblicazione attributi", + "attributes-subscribe-topic-filter": "Filtro topic sottoscrizione attributi", + "attributes-topic-filter-required": "Filtro topic pubblicazione attributi obbligatorio.", + "attributes-subscribe-topic-filter-required": "Filtro topic sottoscrizione attributi obbligatorio.", + "telemetry-proto-schema": "Schema proto telemetria", + "telemetry-proto-schema-required": "Schema proto telemetria obbligatorio.", + "attributes-proto-schema": "Schema proto attributi", + "attributes-proto-schema-required": "Schema proto attributi obbligatorio.", + "rpc-response-proto-schema": "Schema proto risposta RPC", + "rpc-response-proto-schema-required": "Schema proto risposta RPC obbligatorio.", + "rpc-response-topic-filter": "Filtro topic risposta RPC", + "rpc-response-topic-filter-required": "Filtro topic risposta RPC obbligatorio.", + "rpc-request-proto-schema": "Schema proto richiesta RPC", + "rpc-request-proto-schema-required": "Schema proto richiesta RPC obbligatorio.", + "rpc-request-proto-schema-hint": "Messaggio RPC deve contenere: string method = 1; int32 requestId = 2; params = 3 (qualsiasi tipo).", + "not-valid-pattern-topic-filter": "Filtro topic con pattern non valido", + "not-valid-single-character": "Utilizzo non valido del carattere jolly singolo", + "not-valid-multi-character": "Utilizzo non valido del carattere jolly multilivello", + "single-level-wildcards-hint": "[+] è adatto per qualsiasi livello del filtro del topic. Es.: v1/devices/+/telemetry oppure +/devices/+/attributes.", + "multi-level-wildcards-hint": "[#] può sostituire l'intero filtro del topic e deve essere l'ultimo simbolo del topic. Es.: # oppure v1/devices/me/#.", + "alarm-rules": "Regole allarme", + "alarm-rules-with-count": "Regole allarme ({{count}})", + "no-alarm-rules": "Nessuna regola allarme configurata", + "add-alarm-rule": "Aggiungi regola allarme", + "edit-alarm-rule": "Modifica regola allarme", + "alarm-type": "Tipo allarme", + "alarm-type-required": "Tipo allarme obbligatorio.", + "alarm-type-unique": "Tipo allarme deve essere unico nel profilo dispositivo.", + "alarm-type-max-length": "Tipo allarme deve essere inferiore a 256 caratteri", + "create-alarm-pattern": "Crea allarme {{alarmType}}", + "create-alarm-rules": "Crea regole allarme", + "no-create-alarm-rules": "Nessuna condizione di creazione configurata", + "add-create-alarm-rule-prompt": "Aggiungi una regola di creazione allarme", + "clear-alarm-rule": "Regola di cancellazione allarme", + "no-clear-alarm-rule": "Nessuna condizione di cancellazione configurata", + "add-create-alarm-rule": "Aggiungi condizione di creazione", + "add-clear-alarm-rule": "Aggiungi condizione di cancellazione", + "select-alarm-severity": "Seleziona severità allarme", + "alarm-severity-required": "Severità allarme obbligatoria.", + "condition-duration": "Durata condizione", + "condition-duration-value": "Valore durata", + "condition-duration-time-unit": "Unità di tempo", + "condition-duration-value-range": "Valore durata deve essere compreso tra 1 e 2147483647.", + "condition-duration-value-pattern": "Valore durata deve essere un numero intero.", + "condition-duration-value-required": "Valore durata obbligatorio.", + "condition-duration-time-unit-required": "Unità di tempo obbligatoria.", + "advanced-settings": "Impostazioni avanzate", + "alarm-rule-additional-info": "Informazioni aggiuntive", + "edit-alarm-rule-additional-info": "Modifica informazioni aggiuntive", + "alarm-rule-additional-info-placeholder": "Fornisci qui i tuoi commenti e le modifiche per visualizzarli nei dettagli dell'allarme sotto 'Informazioni aggiuntive'", + "alarm-rule-additional-info-hint": "Suggerimento: usa ${keyName} per sostituire i valori delle chiavi attributo o telemetria usate nella condizione dell'allarme.", + "alarm-rule-mobile-dashboard": "Dashboard mobile", + "alarm-rule-mobile-dashboard-hint": "Usata dall'app mobile come dashboard dei dettagli dell'allarme", + "alarm-rule-no-mobile-dashboard": "Nessuna dashboard selezionata", + "propagate-alarm": "Propaga allarme alle entità correlate", + "alarm-rule-relation-types-list": "Tipi di relazione", + "alarm-rule-relation-types-list-hint": "Definisce i tipi di relazione per filtrare le entità correlate. Se non impostato, l'allarme verrà propagato a tutte le entità correlate.", + "propagate-alarm-to-owner": "Propaga allarme al proprietario dell'entità (Cliente o Tenant)", + "propagate-alarm-to-tenant": "Propaga allarme al Tenant", + "alarm-rule-condition": "Condizione regola allarme", + "enter-alarm-rule-condition-prompt": "Aggiungi una condizione per la regola dell'allarme", + "edit-alarm-rule-condition": "Modifica condizione regola allarme", + "device-provisioning": "Provisioning dispositivo", + "provision-strategy": "Strategia di provisioning", + "provision-strategy-required": "Strategia di provisioning obbligatoria.", + "provision-strategy-disabled": "Disabilitato", + "provision-strategy-created-new": "Consenti la creazione di nuovi dispositivi", + "provision-strategy-check-pre-provisioned": "Verifica dispositivi pre-provisioned", + "provision-device-key": "Chiave dispositivo di provisioning", + "provision-device-key-required": "Chiave dispositivo di provisioning obbligatoria.", + "copy-provision-key": "Copia chiave di provisioning", + "provision-key-copied-message": "Chiave di provisioning copiata negli appunti", + "provision-device-secret": "Segreto dispositivo di provisioning", + "provision-device-secret-required": "Segreto dispositivo di provisioning obbligatorio.", + "copy-provision-secret": "Copia segreto di provisioning", + "provision-secret-copied-message": "Segreto di provisioning copiato negli appunti", + "provision-strategy-x509": { + "certificate-chain": "Catena di certificati X509", + "certificate-chain-hint": "La strategia X.509 è utilizzata per effettuare il provisioning dei dispositivi tramite certificati client in comunicazione TLS a doppia via.", + "allow-create-new-devices": "Crea nuovi dispositivi", + "allow-create-new-devices-hint": "Se selezionato, verranno creati nuovi dispositivi e il certificato client sarà utilizzato come credenziali del dispositivo.", + "certificate-value": "Certificato in formato PEM", + "certificate-value-required": "Certificato in formato PEM obbligatorio", + "cn-regex-variable": "Variabile espressione regolare CN", + "cn-regex-variable-required": "Variabile CN espressione regolare obbligatoria", + "cn-regex-variable-hint": "Necessaria per estrarre il nome del dispositivo dal CN del certificato X509 del dispositivo." + }, + "condition": "Condizione", + "condition-type": "Tipo di condizione", + "condition-type-simple": "Semplice", + "condition-type-duration": "Durata", + "condition-during": "Durante {{during}}", + "condition-during-dynamic": "Durante \"{{ attribute }}\" ({{during}})", + "condition-type-repeating": "Ripetuta", + "condition-type-required": "Tipo di condizione obbligatorio.", + "condition-repeating-value": "Numero di eventi", + "condition-repeating-value-range": "Il numero di eventi deve essere compreso tra 1 e 2147483647.", + "condition-repeating-value-pattern": "Il numero di eventi deve essere un numero intero.", + "condition-repeating-value-required": "Numero di eventi obbligatorio.", + "condition-repeat-times": "Ripetuto { count, plural, =1 {1 volta} other {# volte} }", + "condition-repeat-times-dynamic": "Ripetuto \"{ attribute }\" ({ count, plural, =1 {1 volta} other {# volte} })", + "schedule-type": "Tipo di pianificazione", + "schedule-type-required": "Tipo di pianificazione obbligatorio.", + "schedule": "Pianificazione", + "edit-schedule": "Modifica pianificazione allarme", + "schedule-any-time": "Attivo sempre", + "schedule-specific-time": "Attivo in un momento specifico", + "schedule-custom": "Personalizzato", + "schedule-day": { + "monday": "Lunedì", + "tuesday": "Martedì", + "wednesday": "Mercoledì", + "thursday": "Giovedì", + "friday": "Venerdì", + "saturday": "Sabato", + "sunday": "Domenica" + }, + "schedule-days": "Giorni", + "schedule-time": "Orario", + "schedule-time-from": "Da", + "schedule-time-to": "A", + "schedule-days-of-week-required": "Deve essere selezionato almeno un giorno della settimana.", + "create-device-profile": "Crea nuovo profilo dispositivo", + "import": "Importa profilo dispositivo", + "export": "Esporta profilo dispositivo", + "export-failed-error": "Impossibile esportare il profilo dispositivo: {{error}}", + "device-profile-file": "File del profilo dispositivo", + "invalid-device-profile-file-error": "Impossibile importare il profilo dispositivo: struttura dati non valida.", + "power-saving-mode": "Modalità risparmio energetico", + "power-saving-mode-type": { + "default": "Usa modalità risparmio energetico del profilo dispositivo", + "psm": "Modalità risparmio energetico (PSM)", + "drx": "Ricezione discontinua (DRX)", + "edrx": "Ricezione discontinua estesa (eDRX)" + }, + "edrx-cycle": "Ciclo eDRX", + "edrx-cycle-required": "Il ciclo eDRX è obbligatorio.", + "edrx-cycle-pattern": "Il ciclo eDRX deve essere un numero intero positivo.", + "edrx-cycle-min": "Il numero minimo del ciclo eDRX è {{ min }} secondi.", + "paging-transmission-window": "Finestra di trasmissione Paging", + "paging-transmission-window-required": "La finestra di trasmissione Paging è obbligatoria.", + "paging-transmission-window-pattern": "La finestra di trasmissione Paging deve essere un numero intero positivo.", + "paging-transmission-window-min": "Il numero minimo della finestra di trasmissione Paging è {{ min }} secondi.", + "psm-activity-timer": "Timer attività PSM", + "psm-activity-timer-required": "Il timer attività PSM è obbligatorio.", + "psm-activity-timer-pattern": "Il timer attività PSM deve essere un numero intero positivo.", + "psm-activity-timer-min": "Il numero minimo del timer attività PSM è {{ min }} secondi.", + "lwm2m": { + "object-list": "Elenco oggetti", + "object-list-empty": "Nessun oggetto selezionato.", + "no-objects-found": "Nessun oggetto trovato.", + "no-objects-matching": "Nessun oggetto corrispondente a '{{object}}' trovato.", + "model-tab": "Modello LWM2M", + "add-new-instances": "Aggiungi nuove istanze", + "instances-list": "Elenco istanze", + "instances-list-required": "L'elenco delle istanze è obbligatorio.", + "instance-id-pattern": "L'ID istanza deve essere un numero intero positivo.", + "instance-id-max": "Valore massimo dell'ID istanza {{max}}.", + "instance": "Istanza", + "resource-label": "#ID Nome risorsa", + "observe-label": "Osserva", + "attribute-label": "Attributo", + "telemetry-label": "Telemetria", + "edit-observe-select": "Per modificare l’osservazione, seleziona telemetria o attributo", + "edit-attributes-select": "Per modificare gli attributi, seleziona telemetria o attributo", + "no-attributes-set": "Nessun attributo impostato", + "key-name": "Nome chiave", + "key-name-required": "Nome chiave obbligatorio", + "attribute-name": "Nome attributo", + "attribute-name-required": "Nome attributo obbligatorio.", + "attribute-value": "Valore attributo", + "attribute-value-required": "Valore attributo obbligatorio.", + "attribute-value-pattern": "Il valore dell’attributo deve essere un numero intero positivo.", + "edit-attributes": "Modifica attributi: {{ name }}", + "view-attributes": "Visualizza attributi: {{ name }}", + "add-attribute": "Aggiungi attributo", + "edit-attribute": "Modifica attributo", + "view-attribute": "Visualizza attributo", + "remove-attribute": "Rimuovi attributo", + "delete-server-text": "Attenzione, dopo la conferma la configurazione del server non sarà più recuperabile.", + "delete-server-title": "Sei sicuro di voler eliminare il server?", + "mode": "Modalità di configurazione della sicurezza", + "bootstrap-tab": "Bootstrap", + "bootstrap-server-legend": "Server Bootstrap (ShortId...)", + "lwm2m-server-legend": "Server LwM2M (ShortId...)", + "server": "Server", + "short-id": "ID server breve", + "short-id-tooltip": "ID breve del server. Usato come collegamento per associare l'istanza dell’oggetto server.\nIdentifica in modo univoco ogni server LwM2M configurato per il client LwM2M.\nLa risorsa DEVE essere impostata quando la risorsa Bootstrap-Server è impostata su 'false'.\nGli ID 0 e 65535 NON DEVONO essere utilizzati per identificare un server LwM2M.", + "short-id-tooltip-bootstrap": "ID breve del server. Usato come collegamento per associare l'istanza dell’oggetto server.\nIdentifica in modo univoco ogni server LwM2M configurato per il client LwM2M.\nLa risorsa DEVE essere impostata quando la risorsa Bootstrap-Server è impostata su 'false'.", + "short-id-required": "ID server breve obbligatorio.", + "short-id-range": "L'ID server breve deve essere compreso tra {{ min }} e {{ max }}.", + "short-id-pattern": "L'ID server breve deve essere un numero intero positivo.", + "lifetime": "Durata registrazione client", + "lifetime-required": "Durata registrazione client obbligatoria.", + "lifetime-pattern": "La durata della registrazione deve essere un numero intero positivo.", + "default-min-period": "Periodo minimo tra due notifiche (s)", + "default-min-period-tooltip": "Valore predefinito che il client LwM2M dovrebbe usare per il periodo minimo di un’osservazione, in assenza di parametro specifico.", + "default-min-period-required": "Periodo minimo obbligatorio.", + "default-min-period-pattern": "Il periodo minimo deve essere un numero intero positivo.", + "notification-storing": "Memorizzazione notifiche quando disabilitato o offline", + "binding": "Binding", + "binding-type": { + "u": "U: Il client è raggiungibile tramite binding UDP in qualsiasi momento.", + "m": "M: Il client è raggiungibile tramite binding MQTT in qualsiasi momento.", + "h": "H: Il client è raggiungibile tramite binding HTTP in qualsiasi momento.", + "t": "T: Il client è raggiungibile tramite binding TCP in qualsiasi momento.", + "s": "S: Il client è raggiungibile tramite binding SMS in qualsiasi momento.", + "n": "N: Il client DEVE rispondere tramite binding Non-IP (supportato da LwM2M 1.1).", + "uq": "UQ: Connessione UDP in modalità coda (non supportato da LwM2M 1.1)", + "uqs": "UQS: connessioni UDP e SMS attive; UDP in modalità coda, SMS in modalità standard (non supportato da LwM2M 1.1)", + "tq": "TQ: Connessione TCP in modalità coda (non supportato da LwM2M 1.1)", + "tqs": "TQS: connessioni TCP e SMS attive; TCP in modalità coda, SMS in modalità standard (non supportato da LwM2M 1.1)", + "sq": "SQ: Connessione SMS in modalità coda (non supportato da LwM2M 1.1)" + }, + "binding-tooltip": "Questo è l'elenco nella risorsa \"binding\" dell'oggetto server LwM2M - /1/x/7.\nIndica le modalità di binding supportate nel client LwM2M.\nQuesto valore DOVREBBE essere uguale al valore nella risorsa “Supported Binding and Modes” dell’oggetto Device (/3/0/16).\nSebbene siano supportati più trasporti, solo uno può essere utilizzato durante l'intera sessione di trasporto.\nAd esempio, quando UDP e SMS sono entrambi supportati, il client e il server LwM2M possono scegliere di comunicare solo tramite UDP o SMS per l’intera sessione.", + "bootstrap-server": "Server Bootstrap", + "lwm2m-server": "Server LwM2M", + "include-bootstrap-server": "Includi aggiornamenti del Server Bootstrap", + "bootstrap-update-title": "Hai già configurato il Server Bootstrap. Sei sicuro di voler escludere gli aggiornamenti?", + "bootstrap-update-text": "Attenzione, dopo la conferma la configurazione del Server Bootstrap non sarà più recuperabile.", + "server-host": "Host", + "server-host-required": "Host obbligatorio.", + "server-port": "Porta", + "server-port-required": "Porta obbligatoria.", + "server-port-pattern": "La porta deve essere un numero intero positivo.", + "server-port-range": "La porta deve essere compresa tra 1 e 65535.", + "server-public-key": "Chiave pubblica del server", + "server-public-key-required": "Chiave pubblica del server obbligatoria.", + "client-hold-off-time": "Tempo di attesa client", + "client-hold-off-time-required": "Tempo di attesa client obbligatorio.", + "client-hold-off-time-pattern": "Il tempo di attesa deve essere un numero intero positivo.", + "client-hold-off-time-tooltip": "Tempo di attesa client da usare solo con il Server Bootstrap", + "account-after-timeout": "Contabilizza dopo il timeout", + "account-after-timeout-required": "Contabilizzazione dopo il timeout obbligatoria.", + "account-after-timeout-pattern": "Deve essere un numero intero positivo.", + "account-after-timeout-tooltip": "Valore del timeout del Server Bootstrap dopo il quale avviene la contabilizzazione.", + "server-type": "Tipo di server", + "add-new-server-title": "Aggiungi nuova configurazione server", + "add-server-config": "Aggiungi configurazione server", + "add-lwm2m-server-config": "Aggiungi server LwM2M", + "no-config-servers": "Nessun server configurato", + "others-tab": "Altre impostazioni", + "client-strategy": "Strategia client alla connessione", + "client-strategy-label": "Strategia", + "client-strategy-only-observe": "Solo richiesta di osservazione dopo la connessione iniziale", + "client-strategy-read-all": "Leggi tutte le risorse e invia richiesta di osservazione dopo la registrazione", + "fw-update": "Aggiornamento firmware", + "fw-update-strategy": "Strategia di aggiornamento firmware", + "fw-update-strategy-data": "Invia aggiornamento come file binario usando Oggetto 19, Risorsa 0 (Data)", + "fw-update-strategy-package": "Invia aggiornamento come file binario usando Oggetto 5, Risorsa 0 (Package)", + "fw-update-strategy-package-uri": "Genera URL CoAP univoco e invia aggiornamento come Oggetto 5, Risorsa 1 (Package URI)", + "sw-update": "Aggiornamento software", + "sw-update-strategy": "Strategia di aggiornamento software", + "sw-update-strategy-package": "Invia file binario usando Oggetto 9, Risorsa 2 (Package)", + "sw-update-strategy-package-uri": "Genera URL CoAP univoco per scaricare il pacchetto e invia aggiornamento usando Oggetto 9, Risorsa 3 (Package URI)", + "fw-update-resource": "Risorsa CoAP aggiornamento firmware", + "fw-update-resource-required": "Risorsa CoAP aggiornamento firmware obbligatoria.", + "sw-update-resource": "Risorsa CoAP aggiornamento software", + "sw-update-resource-required": "Risorsa CoAP aggiornamento software obbligatoria.", + "config-json-tab": "Configurazione JSON profilo dispositivo", + "attributes-name": { + "min-period": "Periodo minimo", + "max-period": "Periodo massimo", + "greater-than": "Maggiore di", + "less-than": "Minore di", + "step": "Passo", + "min-evaluation-period": "Periodo minimo di valutazione", + "max-evaluation-period": "Periodo massimo di valutazione" + }, + "default-object-id": "Versione oggetto predefinita (Attributo)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1" + } + }, + "snmp": { + "add-communication-config": "Aggiungi configurazione di comunicazione", + "add-mapping": "Aggiungi mappatura", + "authentication-passphrase": "Passphrase di autenticazione", + "authentication-passphrase-required": "La passphrase di autenticazione è obbligatoria.", + "authentication-protocol": "Protocollo di autenticazione", + "authentication-protocol-required": "Il protocollo di autenticazione è obbligatorio.", + "communication-configs": "Configurazioni di comunicazione", + "community": "Stringa di community", + "community-required": "La stringa di community è obbligatoria.", + "context-name": "Nome contesto", + "data-key": "Chiave dati", + "data-key-required": "La chiave dati è obbligatoria.", + "data-type": "Tipo di dato", + "data-type-required": "Il tipo di dato è obbligatorio.", + "engine-id": "ID motore", + "host": "Host", + "host-required": "L'host è obbligatorio.", + "oid": "OID", + "oid-pattern": "Formato OID non valido.", + "oid-required": "OID è obbligatorio.", + "please-add-communication-config": "Aggiungi una configurazione di comunicazione", + "please-add-mapping-config": "Aggiungi una configurazione di mappatura", + "port": "Porta", + "port-format": "Formato porta non valido.", + "port-required": "La porta è obbligatoria.", + "privacy-passphrase": "Passphrase di privacy", + "privacy-passphrase-required": "La passphrase di privacy è obbligatoria.", + "privacy-protocol": "Protocollo di privacy", + "privacy-protocol-required": "Il protocollo di privacy è obbligatorio.", + "protocol-version": "Versione del protocollo", + "protocol-version-required": "La versione del protocollo è obbligatoria.", + "querying-frequency": "Frequenza di interrogazione, ms", + "querying-frequency-invalid-format": "La frequenza di interrogazione deve essere un intero positivo.", + "querying-frequency-required": "La frequenza di interrogazione è obbligatoria.", + "retries": "Tentativi", + "retries-invalid-format": "I tentativi devono essere un intero positivo.", + "retries-required": "I tentativi sono obbligatori.", + "scope": "Ambito", + "scope-required": "L'ambito è obbligatorio.", + "security-name": "Nome di sicurezza", + "security-name-required": "Il nome di sicurezza è obbligatorio.", + "timeout-ms": "Timeout, ms", + "timeout-ms-invalid-format": "Il timeout deve essere un intero positivo.", + "timeout-ms-required": "Il timeout è obbligatorio.", + "user-name": "Nome utente", + "user-name-required": "Il nome utente è obbligatorio." + } + }, + "dialog": { + "close": "Chiudi finestra di dialogo", + "error-message-title": "Messaggio di errore:", + "error-details-title": "Dettagli dell'errore" + }, + "direction": { + "column": "Colonna", + "row": "Riga" + }, + "edge": { + "edge": "Edge", + "edge-instances": "Istanza edge", + "instances": "Istanze", + "edge-file": "File edge", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri", + "label-max-length": "L'etichetta deve essere inferiore a 256 caratteri", + "type-max-length": "Il tipo deve essere inferiore a 256 caratteri", + "management": "Gestione edge", + "no-edges-matching": "Nessun edge corrispondente a '{{entity}}' trovato.", + "add": "Aggiungi edge", + "no-edges-text": "Nessun edge trovato", + "edge-details": "Dettagli edge", + "add-edge-text": "Aggiungi nuovo edge", + "delete": "Elimina edge", + "delete-edge-title": "Sei sicuro di voler eliminare l'edge '{{edgeName}}'?", + "delete-edge-text": "Attenzione, dopo la conferma l'edge e tutti i dati correlati saranno irrimediabilmente persi.", + "delete-edges-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 edge} other {# edges} }?", + "delete-edges-text": "Attenzione, dopo la conferma tutti gli edge selezionati saranno eliminati e tutti i dati correlati saranno irrimediabilmente persi.", + "name": "Nome", + "name-starts-with": "Il nome dell'edge inizia con", + "name-required": "Il nome è obbligatorio.", + "description": "Descrizione", + "details": "Dettagli", + "events": "Eventi", + "copy-id": "Copia ID edge", + "id-copied-message": "ID edge copiato negli appunti", + "sync": "Sincronizza edge", + "edge-required": "Edge obbligatorio", + "edge-type": "Tipo di edge", + "edge-type-required": "Il tipo di edge è obbligatorio.", + "event-action": "Azione evento", + "entity-id": "ID entità", + "select-edge-type": "Seleziona tipo di edge", + "assign-to-customer": "Assegna al cliente", + "assign-to-customer-text": "Seleziona il cliente a cui assegnare l'edge", + "assign-edge-to-customer": "Assegna Edge al Cliente", + "assign-edge-to-customer-text": "Seleziona gli edge da assegnare al cliente", + "assignedToCustomer": "Assegnato al cliente", + "edge-public": "Edge è pubblico", + "assigned-to-customer": "Assegnato a: {{customerTitle}}", + "unassign-from-customer": "Disassegna dal cliente", + "unassign-edge-title": "Sei sicuro di voler disassegnare l'edge '{{edgeName}}'?", + "unassign-edge-text": "Dopo la conferma, l'edge sarà disassegnato e non sarà più accessibile dal cliente.", + "unassign-edges-title": "Sei sicuro di voler disassegnare { count, plural, =1 {1 edge} other {# edges} }?", + "unassign-edges-text": "Dopo la conferma, tutti gli edge selezionati saranno disassegnati e non più accessibili dal cliente.", + "make-public": "Rendi edge pubblico", + "make-public-edge-title": "Sei sicuro di voler rendere pubblico l'edge '{{edgeName}}'?", + "make-public-edge-text": "Dopo la conferma, l'edge e tutti i suoi dati saranno resi pubblici e accessibili da altri.", + "make-private": "Rendi edge privato", + "public": "Pubblico", + "make-private-edge-title": "Sei sicuro di voler rendere privato l'edge '{{edgeName}}'?", + "make-private-edge-text": "Dopo la conferma, l'edge e tutti i suoi dati saranno resi privati e non più accessibili da altri.", + "import": "Importa edge", + "install-connect-instructions": "Istruzioni di installazione e connessione", + "install-connect-instructions-edge-created": "Edge creato! Controlla le istruzioni di installazione e connessione", + "loading-edge-instructions": "Caricamento istruzioni edge...", + "label": "Etichetta", + "load-entity-error": "Errore nel caricamento dei dati. L'entità è stata eliminata.", + "assign-new-edge": "Assegna nuovo edge", + "unassign-from-edge": "Disassegna da edge", + "edge-key": "Chiave edge", + "copy-edge-key": "Copia chiave edge", + "edge-key-copied-message": "Chiave edge copiata negli appunti", + "edge-secret": "Segreto edge", + "copy-edge-secret": "Copia segreto edge", + "edge-secret-copied-message": "Segreto edge copiato negli appunti", + "manage-assets": "Gestisci asset", + "manage-devices": "Gestisci dispositivi", + "manage-entity-views": "Gestisci viste entità", + "manage-dashboards": "Gestisci dashboard", + "manage-rulechains": "Gestisci catene di regole", + "assets": "Asset edge", + "devices": "Dispositivi Edge", + "entity-views": "Viste entità Edge", + "dashboard": "Dashboard Edge", + "dashboards": "Dashboard Edge", + "rulechain-templates": "Template di catene di regole", + "edge-rulechain-templates": "Template catene di regole Edge", + "rulechains": "Catene di regole Edge", + "search": "Cerca edge", + "selected-edges": "{ count, plural, =1 {1 edge} other {# edge} } selezionati", + "any-edge": "Qualsiasi edge", + "no-edge-types-matching": "Nessun tipo di edge corrispondente a '{{entitySubtype}}' trovato.", + "edge-type-list-empty": "Nessun tipo di edge selezionato.", + "edge-types": "Tipi di edge", + "enter-edge-type": "Inserisci tipo di edge", + "deployed": "Distribuito", + "pending": "In attesa", + "downlinks": "Downlink", + "no-downlinks-prompt": "Nessun downlink trovato", + "sync-process-started-successfully": "Processo di sincronizzazione avviato con successo!", + "missing-related-rule-chains-title": "Mancano catene di regole correlate nell'edge", + "missing-related-rule-chains-text": "Le catene di regole assegnate all'edge usano nodi che inoltrano messaggi a catene non assegnate.

Elenco delle catene mancanti:
{{missingRuleChains}}", + "widget-datasource-error": "Questo widget supporta solo sorgenti dati di tipo EDGE", + "upgrade-instructions": "Istruzioni di aggiornamento", + "connected": "Connesso", + "disconnected": "Disconnesso" + }, + "edge-event": { + "type-dashboard": "Dashboard", + "type-asset": "Asset", + "type-device": "Dispositivo", + "type-device-profile": "Profilo dispositivo", + "type-asset-profile": "Profilo asset", + "type-entity-view": "Vista entità", + "type-alarm": "Allarme", + "type-rule-chain": "Catena di regole", + "type-rule-chain-metadata": "Metadati catena di regole", + "type-edge": "Edge", + "type-user": "Utente", + "type-tenant": "Tenant", + "type-tenant-profile": "Profilo tenant", + "type-customer": "Cliente", + "type-relation": "Relazione", + "type-widgets-bundle": "Pacchetto widget", + "type-widgets-type": "Tipo di widget", + "type-admin-settings": "Impostazioni amministratore", + "type-ota-package": "Pacchetto OTA", + "type-queue": "Coda", + "action-type-added": "Aggiunto", + "action-type-deleted": "Eliminato", + "action-type-updated": "Aggiornato", + "action-type-post-attributes": "Post attributi", + "action-type-attributes-updated": "Attributi aggiornati", + "action-type-attributes-deleted": "Attributi eliminati", + "action-type-timeseries-updated": "Serie temporali aggiornate", + "action-type-credentials-updated": "Credenziali aggiornate", + "action-type-assigned-to-customer": "Assegnato al cliente", + "action-type-unassigned-from-customer": "Disassegnato dal cliente", + "action-type-relation-add-or-update": "Relazione aggiunta o aggiornata", + "action-type-relation-deleted": "Relazione eliminata", + "action-type-rpc-call": "Chiamata RPC", + "action-type-alarm-ack": "Conferma allarme", + "action-type-alarm-clear": "Reset allarme", + "action-type-alarm-assigned": "Allarme assegnato", + "action-type-alarm-unassigned": "Allarme disassegnato", + "action-type-assigned-to-edge": "Assegnato a Edge", + "action-type-unassigned-from-edge": "Disassegnato da Edge", + "action-type-credentials-request": "Richiesta credenziali", + "action-type-entity-merge-request": "Richiesta unione entità" + }, + "error": { + "unable-to-connect": "Impossibile connettersi al server! Controlla la connessione a Internet.", + "unhandled-error-code": "Codice di errore non gestito: {{errorCode}}", + "unknown-error": "Errore sconosciuto" + }, + "entity": { + "entity": "Entità", + "entities": "Entità", + "entities-count": "Conteggio entità", + "alarms-count": "Conteggio allarmi", + "aliases": "Alias entità", + "aliases-short": "Alias", + "entity-alias": "Alias entità", + "unable-delete-entity-alias-title": "Impossibile eliminare l'alias dell'entità", + "unable-delete-entity-alias-text": "L'alias dell'entità '{{entityAlias}}' non può essere eliminato poiché è utilizzato dai seguenti widget:
{{widgetsList}}", + "duplicate-alias-error": "Alias duplicato trovato '{{alias}}'.
Gli alias devono essere univoci all'interno del dashboard.", + "missing-entity-filter-error": "Filtro mancante per l'alias '{{alias}}'.", + "configure-alias": "Configura alias '{{alias}}'", + "alias": "Alias", + "alias-required": "L'alias dell'entità è obbligatorio.", + "remove-alias": "Rimuovi alias entità", + "add-alias": "Aggiungi alias entità", + "edit-alias": "Modifica alias entità", + "entity-list": "Elenco entità", + "entity-type": "Tipo entità", + "entity-types": "Tipi entità", + "entity-type-list": "Elenco tipi entità", + "any-entity": "Qualsiasi entità", + "add-entity-type": "Aggiungi tipo entità", + "enter-entity-type": "Inserisci tipo entità", + "no-entities-matching": "Nessuna entità corrispondente a '{{entity}}' trovata.", + "no-entities-text": "Nessuna entità trovata", + "no-entity-types-matching": "Nessun tipo entità corrispondente a '{{entityType}}' trovato.", + "name-starts-with": "Espressione nome", + "help-text": "Usa '%' secondo necessità: '%nome_entità_contiene%', '%nome_entità_finisce', 'entità_inizia_con'.", + "use-entity-name-filter": "Usa filtro", + "entity-list-empty": "Nessuna entità selezionata.", + "entity-type-list-required": "È richiesto almeno un tipo di entità.", + "entity-name-filter-required": "Il filtro sul nome dell'entità è obbligatorio.", + "entity-name-filter-no-entity-matched": "Nessuna entità che inizia con '{{entity}}' trovata.", + "all-subtypes": "Tutti", + "select-entities": "Seleziona entità", + "no-aliases-found": "Nessun alias trovato.", + "no-alias-matching": "'{{alias}}' non trovato.", + "create-new-alias": "Crea un nuovo alias!", + "create-new": "Crea nuovo", + "key": "Chiave", + "key-name": "Nome chiave", + "no-keys-found": "Nessuna chiave trovata.", + "no-key-matching": "'{{key}}' non trovato.", + "create-new-key": "Crea una nuova chiave!", + "type": "Tipo", + "type-required": "Il tipo di entità è obbligatorio.", + "type-device": "Dispositivo", + "type-devices": "Dispositivi", + "list-of-devices": "{ count, plural, =1 {Un dispositivo} other {Elenco di # dispositivi} }", + "device-name-starts-with": "Dispositivi con nomi che iniziano con '{{prefix}}'", + "type-device-profile": "Profilo dispositivo", + "type-device-profiles": "Profili dispositivo", + "clear-selected-profiles": "Cancella profili selezionati", + "list-of-device-profiles": "{ count, plural, =1 {Un profilo dispositivo} other {Elenco di # profili dispositivo} }", + "device-profile-name-starts-with": "Profili dispositivo con nomi che iniziano con '{{prefix}}'", + "type-asset-profile": "Profilo asset", + "type-asset-profiles": "Profili asset", + "list-of-asset-profiles": "{ count, plural, =1 {Un profilo asset} other {Elenco di # profili asset} }", + "asset-profile-name-starts-with": "Profili asset con nomi che iniziano con '{{prefix}}'", + "type-asset": "Asset", + "type-assets": "Asset", + "list-of-assets": "{ count, plural, =1 {Un asset} other {Elenco di # asset} }", + "asset-name-starts-with": "Asset con nomi che iniziano con '{{prefix}}'", + "type-entity-view": "Vista entità", + "type-entity-views": "Viste entità", + "list-of-entity-views": "{ count, plural, =1 {Una vista entità} other {Elenco di # viste entità} }", + "entity-view-name-starts-with": "Viste entità con nomi che iniziano con '{{prefix}}'", + "type-rule": "Regola", + "type-rules": "Regole", + "list-of-rules": "{ count, plural, =1 {Una regola} other {Elenco di # regole} }", + "rule-name-starts-with": "Regole con nomi che iniziano con '{{prefix}}'", + "type-plugin": "Plugin", + "type-plugins": "Plugin", + "list-of-plugins": "{ count, plural, =1 {Un plugin} other {Elenco di # plugin} }", + "plugin-name-starts-with": "Plugin con nomi che iniziano con '{{prefix}}'", + "type-tenant": "Tenant", + "type-tenants": "Tenant", + "list-of-tenants": "{ count, plural, =1 {Un tenant} other {Elenco di # tenant} }", + "tenant-name-starts-with": "Tenant con nomi che iniziano con '{{prefix}}'", + "type-tenant-profile": "Profilo tenant", + "type-tenant-profiles": "Profili tenant", + "list-of-tenant-profiles": "{ count, plural, =1 {Un profilo tenant} other {Elenco di # profili tenant} }", + "tenant-profile-name-starts-with": "Profili tenant con nomi che iniziano con '{{prefix}}'", + "type-customer": "Cliente", + "type-customers": "Clienti", + "list-of-customers": "{ count, plural, =1 {Un cliente} other {Elenco di # clienti} }", + "customer-name-starts-with": "Clienti i cui nomi iniziano con '{{prefix}}'", + "type-user": "Utente", + "type-users": "Utenti", + "list-of-users": "{ count, plural, =1 {Un utente} other {Elenco di # utenti} }", + "user-name-starts-with": "Utenti i cui nomi iniziano con '{{prefix}}'", + "type-dashboard": "Dashboard", + "type-dashboards": "Dashboard", + "list-of-dashboards": "{ count, plural, =1 {Una dashboard} other {Elenco di # dashboard} }", + "dashboard-name-starts-with": "Dashboard i cui nomi iniziano con '{{prefix}}'", + "type-alarm": "Allarme", + "type-alarms": "Allarmi", + "list-of-alarms": "{ count, plural, =1 {Un allarme} other {Elenco di # allarmi} }", + "alarm-name-starts-with": "Allarmi i cui nomi iniziano con '{{prefix}}'", + "type-rulechain": "Catena di regole", + "type-rulechains": "Catene di regole", + "list-of-rulechains": "{ count, plural, =1 {Una catena di regole} other {Elenco di # catene di regole} }", + "rulechain-name-starts-with": "Catene di regole i cui nomi iniziano con '{{prefix}}'", + "type-rulenode": "Nodo regola", + "type-rulenodes": "Nodi regola", + "list-of-rulenodes": "{ count, plural, =1 {Un nodo regola} other {Elenco di # nodi regola} }", + "rulenode-name-starts-with": "Nodi regola i cui nomi iniziano con '{{prefix}}'", + "type-current-customer": "Cliente corrente", + "type-current-tenant": "Tenant corrente", + "type-current-user": "Utente corrente", + "type-current-user-owner": "Proprietario utente corrente", + "type-calculated-field": "Campo calcolato", + "type-calculated-fields": "Campi calcolati", + "type-widgets-bundle": "Pacchetto widget", + "type-widgets-bundles": "Pacchetti widget", + "list-of-widgets-bundles": "{ count, plural, =1 {Un pacchetto widget} other {Elenco di # pacchetti widget} }", + "type-widget": "Widget", + "type-widgets": "Widget", + "list-of-widgets": "{ count, plural, =1 {Un widget} other {Elenco di # widget} }", + "search": "Cerca entità", + "selected-entities": "{ count, plural, =1 {1 entità} other {# entità} } selezionate", + "entity-name": "Nome entità", + "entity-label": "Etichetta entità", + "details": "Dettagli entità", + "no-entities-prompt": "Nessuna entità trovata", + "no-data": "Nessun dato da visualizzare", + "columns-to-display": "Colonne da visualizzare", + "type-api-usage-state": "Stato utilizzo API", + "type-edge": "Edge", + "type-edges": "Edge", + "list-of-edges": "{ count, plural, =1 {Un edge} other {Elenco di # edge} }", + "edge-name-starts-with": "Edge i cui nomi iniziano con '{{prefix}}'", + "version-conflict": { + "message": "Vuoi sovrascrivere la versione esistente o annullare le modifiche e caricare l'ultima versione?", + "link": "Puoi scaricare la tua versione di {{entityType}} usando questo", + "overwrite": "Sovrascrivi versione", + "discard": "Annulla modifiche" + }, + "type-tb-resource": "Risorsa", + "type-tb-resources": "Risorse", + "list-of-tb-resources": "{ count, plural, =1 {Una risorsa} other {Elenco di # risorse} }", + "type-ota-package": "Pacchetto OTA", + "type-rpc": "RPC", + "type-queue": "Coda", + "type-queue-stats": "Statistiche coda", + "type-queues-stats": "Statistiche code", + "type-notification": "Notifica", + "type-notification-rule": "Regola notifica", + "type-notification-rules": "Regole notifica", + "list-of-notification-rules": "{ count, plural, =1 {Una regola di notifica} other {Elenco di # regole di notifica} }", + "type-notification-target": "Destinatario della notifica", + "type-notification-targets": "Destinatari della notifica", + "list-of-notification-targets": "{ count, plural, =1 {Un destinatario della notifica} other {Elenco di # destinatari della notifica} }", + "type-notification-request": "Richiesta di notifica", + "type-notification-template": "Modello di notifica", + "type-notification-templates": "Modelli di notifica", + "list-of-notification-templates": "{ count, plural, =1 {Un modello di notifica} other {Elenco di # modelli di notifica} }", + "link": "link", + "type-oauth2-client": "Client OAuth 2.0", + "type-oauth2-clients": "Client OAuth 2.0", + "list-of-oauth2-clients": "{ count, plural, =1 {Un client OAuth 2.0} other {Elenco di # client OAuth 2.0} }", + "type-domain": "Dominio", + "type-domains": "Domini", + "list-of-domains": "{ count, plural, =1 {Un dominio} other {Elenco di # domini} }", + "type-mobile-app": "Applicazione mobile", + "type-mobile-apps": "Applicazioni mobili", + "list-of-mobile-apps": "{ count, plural, =1 {Un'applicazione mobile} other {Elenco di # applicazioni mobili} }", + "type-mobile-app-bundle": "Pacchetto mobile", + "type-mobile-app-bundles": "Pacchetti mobili", + "list-of-mobile-app-bundles": "{ count, plural, =1 {Un pacchetto mobile} other {Elenco di # pacchetti mobili} }" + }, + "entity-field": { + "created-time": "Data di creazione", + "name": "Nome", + "type": "Tipo", + "first-name": "Nome", + "last-name": "Cognome", + "email": "Email", + "title": "Titolo", + "country": "Paese", + "state": "Stato", + "city": "Città", + "address": "Indirizzo", + "address2": "Indirizzo 2", + "zip": "CAP", + "phone": "Telefono", + "label": "Etichetta", + "queue-name": "Nome della coda", + "service-id": "ID del servizio", + "owner-name": "Nome del proprietario", + "owner-type": "Tipo di proprietario" + }, + "entity-view": { + "entity-view": "Vista entità", + "entity-view-required": "Vista entità obbligatoria.", + "entity-views": "Viste entità", + "management": "Gestione viste entità", + "view-entity-views": "Visualizza viste entità", + "entity-view-alias": "Alias vista entità", + "aliases": "Alias viste entità", + "no-alias-matching": "'{{alias}}' non trovato.", + "no-aliases-found": "Nessun alias trovato.", + "no-key-matching": "'{{key}}' non trovato.", + "no-keys-found": "Nessuna chiave trovata.", + "create-new-alias": "Creane uno nuovo!", + "create-new-key": "Creane una nuova!", + "duplicate-alias-error": "Trovato alias duplicato '{{alias}}'.
Gli alias delle viste entità devono essere univoci all'interno della dashboard.", + "configure-alias": "Configura alias '{{alias}}'", + "no-entity-views-matching": "Nessuna vista entità corrispondente a '{{entity}}' trovata.", + "public": "Pubblica", + "alias": "Alias", + "alias-required": "Alias vista entità obbligatorio.", + "remove-alias": "Rimuovi alias vista entità", + "add-alias": "Aggiungi alias vista entità", + "name-starts-with": "Espressione nome vista entità", + "help-text": "Usa '%' se necessario: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list": "Elenco viste entità", + "use-entity-view-name-filter": "Usa filtro", + "entity-view-list-empty": "Nessuna vista entità selezionata.", + "entity-view-name-filter-required": "Filtro nome vista entità obbligatorio.", + "entity-view-name-filter-no-entity-view-matched": "Nessuna vista entità che inizia con '{{entityView}}' trovata.", + "add": "Aggiungi vista entità", + "entity-view-public": "La vista entità è pubblica", + "assign-to-customer": "Assegna al cliente", + "assign-entity-view-to-customer": "Assegna viste entità al cliente", + "assign-entity-view-to-customer-text": "Seleziona le viste entità da assegnare al cliente", + "no-entity-views-text": "Nessuna vista entità trovata", + "assign-to-customer-text": "Seleziona il cliente a cui assegnare le viste entità", + "entity-view-details": "Dettagli vista entità", + "add-entity-view-text": "Aggiungi nuova vista entità", + "delete": "Elimina vista entità", + "assign-entity-views": "Assegna viste entità", + "assign-entity-views-text": "Assegna { count, plural, =1 {1 vista entità} other {# viste entità} } al cliente", + "delete-entity-views": "Elimina viste entità", + "make-public": "Rendi vista entità pubblica", + "make-private": "Rendi vista entità privata", + "unassign-from-customer": "Disassegna dal cliente", + "unassign-entity-views": "Disassegna viste entità", + "unassign-entity-views-action-title": "Disassegna { count, plural, =1 {1 vista entità} other {# viste entità} } dal cliente", + "assign-new-entity-view": "Assegna nuova vista entità", + "delete-entity-view-title": "Sei sicuro di voler eliminare la vista entità '{{entityViewName}}'?", + "delete-entity-view-text": "Attenzione, dopo la conferma la vista entità e tutti i dati correlati saranno irreversibili.", + "delete-entity-views-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 vista entità} other {# viste entità} }?", + "delete-entity-views-action-title": "Elimina { count, plural, =1 {1 vista entità} other {# viste entità} }", + "delete-entity-views-text": "Attenzione, dopo la conferma tutte le viste entità selezionate saranno eliminate e i relativi dati saranno irreversibili.", + "make-public-entity-view-title": "Sei sicuro di voler rendere pubblica la vista entità '{{entityViewName}}'?", + "make-public-entity-view-text": "Dopo la conferma, la vista entità e tutti i suoi dati saranno resi pubblici e accessibili da altri.", + "make-private-entity-view-title": "Sei sicuro di voler rendere privata la vista entità '{{entityViewName}}'?", + "make-private-entity-view-text": "Dopo la conferma, la vista entità e tutti i suoi dati saranno resi privati e non accessibili da altri.", + "unassign-entity-view-title": "Sei sicuro di voler disassegnare la vista entità '{{entityViewName}}'?", + "unassign-entity-view-text": "Dopo la conferma, la vista entità sarà disassegnata e non accessibile dal cliente.", + "unassign-entity-view": "Disassegna vista entità", + "unassign-entity-views-title": "Sei sicuro di voler disassegnare { count, plural, =1 {1 vista entità} other {# viste entità} }?", + "unassign-entity-views-text": "Dopo la conferma, tutte le viste entità selezionate saranno disassegnate e non saranno accessibili dal cliente.", + "entity-view-type": "Tipo di vista entità", + "entity-view-type-required": "Tipo di vista entità obbligatorio.", + "select-entity-view-type": "Seleziona tipo vista entità", + "enter-entity-view-type": "Inserisci tipo vista entità", + "any-entity-view": "Qualsiasi vista entità", + "no-entity-view-types-matching": "Nessun tipo vista entità corrispondente a '{{entitySubtype}}' trovato.", + "entity-view-type-list-empty": "Nessun tipo vista entità selezionato.", + "entity-view-types": "Tipi di vista entità", + "created-time": "Data di creazione", + "name": "Nome", + "name-required": "Il nome è obbligatorio.", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri", + "type-max-length": "Il tipo di vista entità deve essere inferiore a 256 caratteri", + "description": "Descrizione", + "events": "Eventi", + "details": "Dettagli", + "copyId": "Copia ID vista entità", + "idCopiedMessage": "ID vista entità copiato negli appunti", + "assignedToCustomer": "Assegnato al cliente", + "unable-entity-view-device-alias-title": "Impossibile eliminare l'alias della vista entità", + "unable-entity-view-device-alias-text": "L'alias del dispositivo '{{entityViewAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:
{{widgetsList}}", + "select-entity-view": "Seleziona vista entità", + "start-ts": "Ora di inizio", + "end-ts": "Ora di fine", + "date-limits": "Limiti di data", + "client-attributes": "Attributi client", + "shared-attributes": "Attributi condivisi", + "server-attributes": "Attributi server", + "timeseries": "Serie temporali", + "client-attributes-placeholder": "Attributi client", + "shared-attributes-placeholder": "Attributi condivisi", + "server-attributes-placeholder": "Attributi server", + "timeseries-placeholder": "Serie temporali", + "target-entity": "Entità di destinazione", + "attributes-propagation": "Propagazione degli attributi", + "attributes-propagation-hint": "La vista entità copierà automaticamente gli attributi specificati dall'entità di destinazione ogni volta che viene salvata o aggiornata. Per motivi di prestazioni, gli attributi dell'entità di destinazione non vengono propagati ad ogni modifica. È possibile abilitare la propagazione automatica configurando un nodo 'copy to view' nella catena di regole e collegando i messaggi 'Post attributes' e 'Attributes Updated' a tale nodo.", + "timeseries-data": "Dati serie temporali", + "timeseries-data-hint": "Configura le chiavi di dati delle serie temporali dell'entità di destinazione accessibili alla vista entità. Questi dati sono di sola lettura.", + "search": "Cerca viste entità", + "selected-entity-views": "{ count, plural, =1 {1 vista entità} other {# viste entità} } selezionate", + "assign-entity-view-to-edge": "Assegna vista(e) entità all'Edge", + "assign-entity-view-to-edge-text": "Seleziona le viste entità da assegnare all'edge", + "unassign-entity-view-from-edge-title": "Sei sicuro di voler disassegnare la vista entità '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text": "Dopo la conferma, la vista entità verrà disassegnata e non sarà accessibile dall'edge.", + "unassign-entity-views-from-edge-action-title": "Disassegna { count, plural, =1 {1 vista entità} other {# viste entità} } dall'edge", + "unassign-entity-view-from-edge": "Disassegna vista entità", + "unassign-entity-views-from-edge-title": "Sei sicuro di voler disassegnare { count, plural, =1 {1 vista entità} other {# viste entità} }?", + "unassign-entity-views-from-edge-text": "Dopo la conferma, tutte le viste entità selezionate verranno disassegnate e non saranno più accessibili dall'edge." + }, + "event": { + "event-type": "Tipo di evento", + "events-filter": "Filtro eventi", + "clean-events": "Cancella eventi", + "type-error": "Errore", + "type-lc-event": "Evento di ciclo di vita", + "type-stats": "Statistiche", + "type-debug-rule-node": "Debug", + "type-debug-rule-chain": "Debug", + "type-debug-calculated-field": "Debug", + "arguments": "Argomenti", + "result": "Risultato", + "no-events-prompt": "Nessun evento trovato", + "error": "Errore", + "alarm": "Allarme", + "event-time": "Ora evento", + "server": "Server", + "body": "Corpo", + "method": "Metodo", + "type": "Tipo", + "metadata": "Metadati", + "message": "Messaggio", + "message-id": "ID messaggio", + "copy-message-id": "Copia ID messaggio", + "message-type": "Tipo messaggio", + "data-type": "Tipo dati", + "relation-type": "Tipo relazione", + "data": "Dati", + "event": "Evento", + "status": "Stato", + "success": "Successo", + "failed": "Fallito", + "messages-processed": "Messaggi elaborati", + "max-messages-processed": "Numero massimo di messaggi elaborati", + "min-messages-processed": "Numero minimo di messaggi elaborati", + "errors-occurred": "Errori rilevati", + "max-errors-occurred": "Numero massimo di errori", + "min-errors-occurred": "Numero minimo di errori", + "min-value": "Il valore minimo è 0.", + "all-events": "Tutti", + "has-error": "Contiene errori", + "entity-id": "ID entità", + "copy-entity-id": "Copia ID entità", + "entity-type": "Tipo entità", + "clear-filter": "Cancella filtro", + "clear-request-title": "Cancella tutti gli eventi", + "clear-request-text": "Sei sicuro di voler cancellare tutti gli eventi?", + "started": "Avviato", + "updated": "Aggiornato", + "stopped": "Interrotto" + }, + "extension": { + "extensions": "Estensioni", + "selected-extensions": "{ count, plural, =1 {1 estensione} other {# estensioni} } selezionate", + "type": "Tipo", + "key": "Chiave", + "value": "Valore", + "id": "Id", + "extension-id": "ID estensione", + "extension-type": "Tipo estensione", + "transformer-json": "JSON *", + "unique-id-required": "L'ID estensione attuale esiste già.", + "delete": "Elimina estensione", + "add": "Aggiungi estensione", + "edit": "Modifica estensione", + "delete-extension-title": "Sei sicuro di voler eliminare l'estensione '{{extensionId}}'?", + "delete-extension-text": "Attenzione, dopo la conferma l'estensione e tutti i dati collegati non saranno più recuperabili.", + "delete-extensions-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 estensione} other {# estensioni} }?", + "delete-extensions-text": "Attenzione, dopo la conferma tutte le estensioni selezionate verranno eliminate.", + "converters": "Convertitori", + "converter-id": "ID convertitore", + "configuration": "Configurazione", + "converter-configurations": "Configurazioni convertitore", + "token": "Token di sicurezza", + "add-converter": "Aggiungi convertitore", + "add-config": "Aggiungi configurazione convertitore", + "device-name-expression": "Espressione nome dispositivo", + "device-type-expression": "Espressione tipo dispositivo", + "custom": "Personalizzato", + "to-double": "Converti in double", + "transformer": "Trasformatore", + "json-required": "Il JSON del trasformatore è obbligatorio.", + "json-parse": "Impossibile analizzare il JSON del trasformatore.", + "attributes": "Attributi", + "add-attribute": "Aggiungi attributo", + "add-map": "Aggiungi elemento di mappatura", + "timeseries": "Serie temporali", + "add-timeseries": "Aggiungi serie temporale", + "field-required": "Il campo è obbligatorio", + "brokers": "Broker", + "add-broker": "Aggiungi broker", + "host": "Host", + "port": "Porta", + "port-range": "La porta deve essere compresa tra 1 e 65535.", + "ssl": "Ssl", + "credentials": "Credenziali", + "username": "Nome utente", + "password": "Password", + "retry-interval": "Intervallo di retry in millisecondi", + "anonymous": "Anonimo", + "basic": "Base", + "pem": "PEM", + "ca-cert": "File certificato CA *", + "private-key": "File chiave privata *", + "cert": "File certificato *", + "no-file": "Nessun file selezionato.", + "drop-file": "Trascina un file o fai clic per selezionarlo e caricarlo.", + "mapping": "Mappatura", + "topic-filter": "Filtro topic", + "converter-type": "Tipo convertitore", + "converter-json": "Json", + "json-name-expression": "Espressione nome dispositivo JSON", + "topic-name-expression": "Espressione nome dispositivo topic", + "json-type-expression": "Espressione tipo dispositivo JSON", + "topic-type-expression": "Espressione tipo dispositivo topic", + "attribute-key-expression": "Espressione chiave attributo", + "attr-json-key-expression": "Espressione chiave attributo JSON", + "attr-topic-key-expression": "Espressione chiave attributo topic", + "request-id-expression": "Espressione ID richiesta", + "request-id-json-expression": "Espressione ID richiesta JSON", + "request-id-topic-expression": "Espressione ID richiesta topic", + "response-topic-expression": "Espressione topic risposta", + "value-expression": "Espressione valore", + "topic": "Topic", + "timeout": "Timeout in millisecondi", + "converter-json-required": "Il convertitore JSON è obbligatorio.", + "converter-json-parse": "Impossibile analizzare il JSON del convertitore.", + "filter-expression": "Espressione filtro", + "connect-requests": "Richieste di connessione", + "add-connect-request": "Aggiungi richiesta di connessione", + "disconnect-requests": "Richieste di disconnessione", + "add-disconnect-request": "Aggiungi richiesta di disconnessione", + "attribute-requests": "Richieste attributo", + "add-attribute-request": "Aggiungi richiesta attributo", + "attribute-updates": "Aggiornamenti attributo", + "add-attribute-update": "Aggiungi aggiornamento attributo", + "server-side-rpc": "RPC lato server", + "add-server-side-rpc-request": "Aggiungi richiesta RPC lato server", + "device-name-filter": "Filtro nome dispositivo", + "attribute-filter": "Filtro attributo", + "method-filter": "Filtro metodo", + "request-topic-expression": "Espressione topic richiesta", + "response-timeout": "Timeout di risposta in millisecondi", + "topic-expression": "Espressione topic", + "client-scope": "Ambito client", + "add-device": "Aggiungi dispositivo", + "opc-server": "Server", + "opc-add-server": "Aggiungi server", + "opc-add-server-prompt": "Aggiungi server", + "opc-application-name": "Nome applicazione", + "opc-application-uri": "URI applicazione", + "opc-scan-period-in-seconds": "Periodo di scansione in secondi", + "opc-security": "Sicurezza", + "opc-identity": "Identità", + "opc-keystore": "Keystore", + "opc-type": "Tipo", + "opc-keystore-type": "Tipo", + "opc-keystore-location": "Percorso *", + "opc-keystore-password": "Password", + "opc-keystore-alias": "Alias", + "opc-keystore-key-password": "Password chiave", + "opc-device-node-pattern": "Pattern nodo dispositivo", + "opc-device-name-pattern": "Pattern nome dispositivo", + "modbus-server": "Server/slave", + "modbus-add-server": "Aggiungi server/slave", + "modbus-add-server-prompt": "Aggiungi server/slave", + "modbus-transport": "Trasporto", + "modbus-tcp-reconnect": "Riconnessione automatica", + "modbus-rtu-over-tcp": "RTU su TCP", + "modbus-port-name": "Nome porta seriale", + "modbus-encoding": "Codifica", + "modbus-parity": "Parità", + "modbus-baudrate": "Velocità di trasmissione", + "modbus-databits": "Bit di dati", + "modbus-stopbits": "Bit di stop", + "modbus-databits-range": "I bit di dati devono essere compresi tra 7 e 8.", + "modbus-stopbits-range": "I bit di stop devono essere compresi tra 1 e 2.", + "modbus-unit-id": "ID unità", + "modbus-unit-id-range": "L'ID unità deve essere compreso tra 1 e 247.", + "modbus-device-name": "Nome dispositivo", + "modbus-poll-period": "Periodo polling (ms)", + "modbus-attributes-poll-period": "Periodo polling attributi (ms)", + "modbus-timeseries-poll-period": "Periodo polling serie temporali (ms)", + "modbus-poll-period-range": "Il periodo di polling deve essere un valore positivo.", + "modbus-tag": "Tag", + "modbus-function": "Funzione", + "modbus-register-address": "Indirizzo registro", + "modbus-register-address-range": "L'indirizzo del registro deve essere compreso tra 0 e 65535.", + "modbus-register-bit-index": "Indice bit", + "modbus-register-bit-index-range": "L'indice bit deve essere compreso tra 0 e 15.", + "modbus-register-count": "Conteggio registri", + "modbus-register-count-range": "Il conteggio dei registri deve essere un valore positivo.", + "modbus-byte-order": "Ordine byte", + "sync": { + "status": "Stato", + "sync": "Sincronizza", + "not-sync": "Non sincronizzato", + "last-sync-time": "Ultimo tempo di sincronizzazione", + "not-available": "Non disponibile" + }, + "export-extensions-configuration": "Esporta configurazione estensioni", + "import-extensions-configuration": "Importa configurazione estensioni", + "import-extensions": "Importa estensioni", + "import-extension": "Importa estensione", + "export-extension": "Esporta estensione", + "file": "File estensioni", + "invalid-file-error": "File estensione non valido" + }, + "feature": { + "advanced-features": "Funzionalità avanzate" + }, + "filter": { + "add": "Aggiungi filtro", + "edit": "Modifica filtro", + "name": "Nome filtro", + "name-required": "Il nome del filtro è obbligatorio.", + "duplicate-filter": "Esiste già un filtro con lo stesso nome.", + "filters": "Filtri", + "unable-delete-filter-title": "Impossibile eliminare il filtro", + "unable-delete-filter-text": "Il filtro '{{filter}}' non può essere eliminato poiché è utilizzato dai seguenti widget:
{{widgetsList}}", + "duplicate-filter-error": "Filtro duplicato trovato '{{filter}}'.
I filtri devono essere univoci all'interno della dashboard.", + "missing-key-filters-error": "I filtri chiave sono mancanti per il filtro '{{filter}}'.", + "filter": "Filtro", + "editable": "Modificabile", + "no-filters-found": "Nessun filtro trovato.", + "no-filter-text": "Nessun filtro specificato", + "add-filter-prompt": "Aggiungi un filtro", + "no-filter-matching": "'{{filter}}' non trovato.", + "create-new-filter": "Creane uno nuovo!", + "create-new": "Crea nuovo", + "filter-required": "Il filtro è obbligatorio.", + "operation": { + "operation": "Operazione", + "equal": "uguale", + "not-equal": "diverso", + "starts-with": "inizia con", + "ends-with": "termina con", + "contains": "contiene", + "not-contains": "non contiene", + "greater": "maggiore di", + "less": "minore di", + "greater-or-equal": "maggiore o uguale", + "less-or-equal": "minore o uguale", + "and": "e", + "or": "o", + "in": "in", + "not-in": "non in" + }, + "ignore-case": "ignora maiuscole/minuscole", + "value": "Valore", + "remove-filter": "Rimuovi filtro", + "duplicate-filter-action": "Duplica filtro", + "preview": "Anteprima filtro", + "no-filters": "Nessun filtro configurato", + "add-filter": "Aggiungi filtro", + "add-complex-filter": "Aggiungi filtro complesso", + "add-complex": "Aggiungi complesso", + "complex-filter": "Filtro complesso", + "edit-complex-filter": "Modifica filtro complesso", + "edit-filter-user-params": "Modifica parametri utente predicato filtro", + "filter-user-params": "Parametri utente predicato filtro", + "user-parameters": "Parametri utente", + "display-label": "Etichetta da visualizzare", + "order-priority": "Priorità di ordinamento del campo", + "key-filter": "Filtro chiave", + "key-filters": "Filtri chiave", + "key-name": "Nome chiave", + "key-name-required": "Il nome chiave è obbligatorio.", + "key-type": { + "key-type": "Tipo di chiave", + "attribute": "Attributo", + "timeseries": "Serie temporale", + "entity-field": "Campo entità", + "constant": "Costante", + "client-attribute": "Attributo client", + "server-attribute": "Attributo server", + "shared-attribute": "Attributo condiviso" + }, + "value-type": { + "value-type": "Tipo di valore", + "string": "Stringa", + "numeric": "Numerico", + "boolean": "Booleano", + "date-time": "Data/Ora" + }, + "value-type-required": "Il tipo di valore della chiave è obbligatorio.", + "key-value-type-change-title": "Sei sicuro di voler cambiare il tipo di valore della chiave?", + "key-value-type-change-message": "Se confermi il nuovo tipo di valore, tutti i filtri chiave inseriti verranno rimossi.", + "no-key-filters": "Nessun filtro chiave configurato", + "add-key-filter": "Aggiungi filtro chiave", + "remove-key-filter": "Rimuovi filtro chiave", + "edit-key-filter": "Modifica filtro chiave", + "date": "Data", + "time": "Ora", + "current-tenant": "Tenant corrente", + "current-customer": "Cliente corrente", + "current-user": "Utente corrente", + "current-device": "Dispositivo corrente", + "default-value": "Valore predefinito", + "default-comma-separated-values": "Valori predefiniti separati da virgola", + "dynamic-source-type": "Tipo di sorgente dinamica", + "dynamic-value": "Valore dinamico", + "no-dynamic-value": "Nessun valore dinamico", + "source-attribute": "Attributo sorgente", + "switch-to-dynamic-value": "Passa al valore dinamico", + "switch-to-default-value": "Passa al valore predefinito", + "inherit-owner": "Eredita dal proprietario", + "source-attribute-not-set": "Se l'attributo sorgente non è impostato" + }, + "fullscreen": { + "expand": "Espandi a schermo intero", + "exit": "Esci da schermo intero", + "toggle": "Attiva/disattiva modalità schermo intero", + "fullscreen": "Schermo intero" + }, + "function": { + "function": "Funzione" + }, + "gateway": { + "gateway-name": "Nome gateway", + "gateway-name-required": "Il nome del gateway è obbligatorio.", + "gateways": "Gateway", + "create-new-gateway": "Crea un nuovo gateway", + "create-new-gateway-text": "Sei sicuro di voler creare un nuovo gateway con il nome: '{{gatewayName}}'?", + "launch-command": "Comando di avvio", + "no-gateway-found": "Nessun gateway trovato.", + "no-gateway-matching": "'{{item}}' non trovato." + }, + "grid": { + "delete-item-title": "Sei sicuro di voler eliminare questo elemento?", + "delete-item-text": "Attenzione, dopo la conferma questo elemento e tutti i dati correlati non saranno recuperabili.", + "delete-items-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 elemento} other {# elementi} }?", + "delete-items-action-title": "Elimina { count, plural, =1 {1 elemento} other {# elementi} }", + "delete-items-text": "Attenzione, dopo la conferma tutti gli elementi selezionati saranno rimossi e i dati correlati non saranno recuperabili.", + "add-item-text": "Aggiungi nuovo elemento", + "no-items-text": "Nessun elemento trovato", + "item-details": "Dettagli elemento", + "delete-item": "Elimina elemento", + "delete-items": "Elimina elementi", + "scroll-to-top": "Scorri verso l'alto" + }, + "help": { + "goto-help-page": "Vai alla pagina di aiuto", + "show-help": "Mostra aiuto" + }, + "home": { + "home": "Home", + "profile": "Profilo", + "logout": "Esci", + "menu": "Menu", + "avatar": "Avatar", + "open-user-menu": "Apri menu utente" + }, + "file-input": { + "browse-file": "Sfoglia file", + "browse-files": "Sfoglia file" + }, + "image": { + "gallery": "Galleria immagini", + "search": "Cerca immagine", + "selected-images": "{ count, plural, =1 {1 immagine} other {# immagini} } selezionate", + "created-time": "Data di creazione", + "name": "Nome", + "name-required": "Il nome è obbligatorio.", + "resolution": "Risoluzione", + "size": "Dimensione", + "system": "Sistema", + "download-image": "Scarica immagine", + "export-image": "Esporta immagine in JSON", + "import-image": "Importa immagine da JSON", + "upload-image": "Carica immagine", + "edit-image": "Modifica immagine", + "image-details": "Dettagli immagine", + "no-images": "Nessuna immagine trovata", + "delete-image": "Elimina immagine", + "delete-image-title": "Sei sicuro di voler eliminare l'immagine '{{imageTitle}}'?", + "delete-image-text": "Attenzione, dopo la conferma l'immagine non sarà più recuperabile.", + "delete-images-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 immagine} other {# immagini} }?", + "delete-images-text": "Attenzione, dopo la conferma tutte le immagini selezionate saranno rimosse e i dati correlati non saranno più recuperabili.", + "list-mode": "Vista elenco", + "grid-mode": "Vista griglia", + "image-preview": "Anteprima immagine", + "update-image": "Aggiorna immagine", + "export-failed-error": "Impossibile esportare l'immagine: {{error}}", + "image-json-file": "File JSON immagine", + "invalid-image-json-file-error": "Impossibile importare immagine da JSON: struttura dati JSON immagine non valida.", + "image-is-in-use": "L'immagine è utilizzata da altre entità", + "images-are-in-use": "Le immagini sono utilizzate da altre entità", + "image-is-in-use-text": "L'immagine '{{title}}' non è stata eliminata perché è utilizzata dalle seguenti entità:", + "images-are-in-use-text": "Non tutte le immagini sono state eliminate perché sono utilizzate da altre entità.
Puoi visualizzare le entità di riferimento cliccando sul pulsante Riferimenti nella riga corrispondente.
Se desideri comunque eliminarle, selezionale nella tabella sottostante e clicca su Elimina selezionate.", + "delete-image-in-use-text": "Se desideri comunque eliminare l'immagine, clicca su Elimina comunque.", + "system-entities": "Entità di sistema:", + "entities": "entità:", + "references": "Riferimenti", + "include-system-images": "Includi immagini di sistema", + "clear-image": "Rimuovi immagine", + "no-image": "Nessuna immagine", + "no-image-selected": "Nessuna immagine selezionata", + "browse-from-gallery": "Sfoglia dalla galleria", + "set-link": "Imposta collegamento", + "image-link": "Link immagine", + "link": "Link", + "copy-image-link": "Copia link immagine", + "embed-image": "Incorpora immagine", + "embed-to-html": "Incorpora in HTML", + "embed-to-html-hint": "Questa funzione renderà il link disponibile a qualsiasi utente non autorizzato.", + "embed-to-html-text": "Utilizzando il seguente frammento di codice, puoi incorporare un'immagine nei componenti basati su HTML semplice.
Tali componenti includono i widget di tipo card HTML, funzioni di contenuto cella, ecc.", + "embed-to-angular-template": "Incorpora in modello HTML Angular", + "embed-to-angular-template-text": "Utilizzando il seguente frammento di codice, puoi incorporare un'immagine nel modello HTML Angular che sarà usato per i componenti.
Tali componenti includono il widget Markdown, la sezione HTML nell'editor widget, azioni personalizzate, ecc." + }, + "image-input": { + "drop-images-or": "Trascina le immagini o", + "drag-and-drop": "Trascina e rilascia", + "or": "oppure", + "browse": "Sfoglia", + "no-images": "Nessuna immagine selezionata", + "images": "immagini" + }, + "import": { + "no-file": "Nessun file selezionato", + "drop-file": "Trascina un file JSON o clicca per selezionarlo e caricarlo.", + "drop-json-file-or": "Trascina un file JSON o", + "drop-file-csv": "Trascina un file CSV o clicca per selezionarlo e caricarlo.", + "drop-file-csv-or": "Trascina un file CSV o", + "column-value": "Valore", + "column-title": "Titolo", + "column-example": "Esempio di valore", + "column-key": "Chiave attributo/telemetria", + "credentials": "Credenziali", + "csv-delimiter": "Delimitatore CSV", + "csv-first-line-header": "La prima riga contiene i nomi delle colonne", + "csv-update-data": "Aggiorna attributi/telemetria", + "details": "Dettagli", + "import-csv-number-columns-error": "Il file deve contenere almeno due colonne", + "import-csv-invalid-format-error": "Formato file non valido. Riga: '{{line}}'", + "column-type": { + "name": "Nome", + "type": "Tipo", + "label": "Etichetta", + "column-type": "Tipo colonna", + "client-attribute": "Attributo client", + "shared-attribute": "Attributo condiviso", + "server-attribute": "Attributo server", + "timeseries": "Serie temporali", + "entity-field": "Campo entità", + "access-token": "Token di accesso", + "x509": "X.509", + "mqtt": { + "client-id": "ID client MQTT", + "user-name": "Nome utente MQTT", + "password": "Password MQTT" + }, + "lwm2m": { + "client-endpoint": "Nome endpoint client LwM2M", + "security-config-mode": "Modalità configurazione sicurezza LwM2M", + "client-identity": "Identità client LwM2M", + "client-key": "Chiave client LwM2M", + "client-cert": "Chiave pubblica client LwM2M", + "bootstrap-server-security-mode": "Modalità di sicurezza server bootstrap LwM2M", + "bootstrap-server-secret-key": "Chiave segreta server bootstrap LwM2M", + "bootstrap-server-public-key-id": "Chiave pubblica o ID server bootstrap LwM2M", + "lwm2m-server-security-mode": "Modalità di sicurezza server LwM2M", + "lwm2m-server-secret-key": "Chiave segreta server LwM2M", + "lwm2m-server-public-key-id": "Chiave pubblica o ID server LwM2M" + }, + "snmp": { + "host": "Host SNMP", + "port": "Porta SNMP", + "version": "Versione SNMP (v1, v2c o v3)", + "community-string": "Stringa community SNMP" + }, + "isgateway": "È un gateway", + "activity-time-from-gateway-device": "Orario attività dal dispositivo gateway", + "description": "Descrizione", + "routing-key": "Chiave Edge", + "secret": "Segreto Edge" + }, + "stepper-text": { + "select-file": "Seleziona un file", + "configuration": "Configurazione importazione", + "column-type": "Seleziona tipo colonne", + "creat-entities": "Creazione nuove entità" + }, + "message": { + "create-entities": "{{count}} nuove entità create con successo.", + "update-entities": "{{count}} entità aggiornate con successo.", + "error-entities": "Errore durante la creazione di {{count}} entità." + } + }, + "scada": { + "symbols": "Simboli SCADA", + "search": "Cerca simbolo", + "selected-symbols": "{ count, plural, =1 {1 simbolo} other {# simboli} } selezionato/i", + "download-symbol": "Scarica simbolo SCADA", + "export-symbol": "Esporta simbolo SCADA in JSON", + "import-symbol": "Importa simbolo SCADA da JSON", + "upload-symbol": "Carica simbolo SCADA", + "update-symbol": "Aggiorna simbolo SCADA", + "edit-symbol": "Modifica simbolo SCADA", + "symbol-details": "Dettagli del simbolo SCADA", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "Nessun simbolo trovato", + "show-hidden-elements": "Mostra elementi nascosti", + "hide-hidden-elements": "Nascondi elementi nascosti", + "delete-symbol": "Elimina simbolo SCADA", + "delete-symbol-title": "Sei sicuro di voler eliminare il simbolo SCADA '{{imageTitle}}'?", + "delete-symbol-text": "Attenzione, dopo la conferma il simbolo SCADA non sarà recuperabile.", + "delete-symbols-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 simbolo SCADA} other {# simboli SCADA} }?", + "delete-symbols-text": "Attenzione, dopo la conferma tutti i simboli SCADA selezionati verranno rimossi e i dati correlati non saranno recuperabili.", + "include-system-symbols": "Includi simboli di sistema", + "symbol-preview": "Anteprima simbolo", + "general": "Generale", + "tags": "Tag", + "properties": "Proprietà", + "title": "Titolo", + "description": "Descrizione", + "search-tags": "Cerca tag", + "widget-size": "Dimensione widget", + "cols": "colonne", + "rows": "righe", + "state-render-function": "Funzione di rendering dello stato", + "preview": "Anteprima", + "preview-widget-action-text": "Azione widget '{{type}}' eseguita con successo!", + "no-symbol": "Nessun simbolo SCADA", + "no-symbol-selected": "Nessun simbolo SCADA selezionato", + "clear-symbol": "Cancella simbolo SCADA", + "browse-symbol-from-gallery": "Sfoglia simbolo SCADA dalla galleria", + "zoom-in": "Ingrandisci", + "zoom-out": "Riduci", + "create-widget": "Crea widget", + "create-widget-from-symbol": "Crea widget dal simbolo SCADA", + "hidden": "nascosto", + "tag": { + "tag": "Tag", + "on-click-action": "Azione al clic", + "no-tags": "Nessun tag configurato", + "delete-tag-text": "Sei sicuro di voler eliminare il tag
{{tag}} dall'elemento {{elementType}}?", + "update-tag": "Aggiorna tag", + "enter-tag": "Inserisci tag", + "tag-settings": "Impostazioni tag", + "remove-tag": "Rimuovi tag", + "add-tag": "Aggiungi tag" + }, + "behavior": { + "behavior": "Comportamento", + "id": "Id", + "name": "Nome", + "type": "Tipo", + "no-behaviors": "Nessun comportamento configurato", + "add-behavior": "Aggiungi comportamento", + "type-action": "Azione", + "type-value": "Valore", + "type-widget-action": "Azione widget", + "behavior-settings": "Impostazioni comportamento", + "remove-behavior": "Rimuovi comportamento", + "hint": "Suggerimento", + "group-title": "Titolo del gruppo", + "value-type": "Tipo di valore", + "default-value": "Valore predefinito", + "true-label": "Etichetta vero", + "false-label": "Etichetta falso", + "state-label": "Etichetta stato", + "default-payload": "Payload predefinito", + "not-unique-behavior-ids-error": "Gli ID dei comportamenti devono essere univoci!", + "default-settings": "Impostazioni predefinite" + }, + "symbol": { + "symbol": "Simbolo SCADA", + "fluid-presence": "Presenza di fluido", + "fluid-presence-hint": "Indica se è presente fluido nel tubo.", + "fluid-present": "Fluido presente", + "present": "Presente", + "absent": "Assente", + "flow-presence": "Presenza di flusso", + "flow-presence-hint": "Indica se il fluido scorre nel tubo.", + "flow-present": "Flusso presente", + "flow-direction": "Direzione del flusso", + "flow-direction-hint": "Indica la direzione del flusso del fluido.", + "forward": "Avanti", + "reverse": "Indietro", + "flow-animation-speed": "Velocità animazione del flusso", + "flow-animation-speed-hint": "Valore decimale che indica la velocità dell'animazione del flusso. 1 - velocità normale, 0 - nessuna animazione, < 1 - animazione più lenta, > 1 - animazione più veloce.", + "leak": "Perdita", + "leak-hint": "Indica se è presente una perdita nel tubo.", + "leak-present": "Perdita presente", + "fluid-color": "Colore del fluido", + "pipe-color": "Colore del tubo", + "horizontal-pipe": "Tubo orizzontale", + "vertical-pipe": "Tubo verticale", + "horizontal-fluid-color": "Colore del fluido orizzontale", + "vertical-fluid-color": "Colore del fluido verticale", + "left-pipe": "Tubo sinistro", + "right-pipe": "Tubo destro", + "top-pipe": "Tubo superiore", + "bottom-pipe": "Tubo inferiore", + "left-fluid-color": "Colore fluido sinistro", + "right-fluid-color": "Colore fluido destro", + "top-fluid-color": "Colore fluido superiore", + "bottom-fluid-color": "Colore fluido inferiore", + "display": "Display", + "display-format": "Formato del display", + "value": "Valore", + "decimals": "Decimali", + "units": "Unità", + "flow-meter-value-hint": "Valore decimale visualizzato sul flussometro", + "value-hint": "Valore decimale che indica il valore attuale", + "running": "In funzione", + "running-hint": "Indica se il componente è in funzione.", + "warning-state": "Stato di avviso", + "warning": "Avviso", + "warning-click": "Clic avviso", + "warning-state-hint": "Indica se il componente è in stato di avviso.", + "critical-state": "Stato critico", + "critical": "Critico", + "critical-click": "Clic critico", + "critical-state-hint": "Indica se il componente è in stato critico.", + "critical-state-animation": "Animazione stato critico", + "critical-state-animation-hint": "Attiva animazione lampeggiante quando il componente è in stato critico.", + "warning-critical-state-animation": "Animazione stato avviso/critico", + "warning-critical-state-animation-hint": "Attiva animazione lampeggiante quando il componente è in stato di avviso o critico.", + "animation": "Animazione", + "broken": "Guasto", + "broken-hint": "Indica se il componente è guasto.", + "on-display-click": "Al clic sul display", + "on-display-click-hint": "Azione eseguita quando l'utente clicca sul display.", + "pipe": "Tubo", + "default-border-color": "Colore bordo predefinito", + "active-border-color": "Colore bordo attivo", + "warning-border-color": "Colore bordo di avviso", + "critical-border-color": "Colore bordo critico", + "background-color": "Colore di sfondo", + "rotation-animation-speed": "Velocità animazione rotazione", + "rotation-animation-speed-hint": "Valore decimale che indica la velocità di rotazione. 1 - velocità normale, 0 - nessuna animazione, < 1 - animazione più lenta, > 1 - animazione più veloce.", + "on-click": "Al clic", + "on-click-hint": "Azione eseguita quando l'utente clicca sul componente.", + "connectors-positions": "Posizioni connettori", + "right-connector": "Connettore destro", + "right-top-connector": "Connettore in alto a destra", + "right-bottom-connector": "Connettore in basso a destra", + "left-connector": "Connettore sinistro", + "left-top-connector": "Connettore in alto a sinistra", + "left-bottom-connector": "Connettore in basso a sinistra", + "top-left-connector": "Connettore in alto a sinistra", + "top-right-connector": "Connettore in alto a destra", + "top-connector": "Connettore superiore", + "bottom-connector": "Connettore inferiore", + "running-color": "Colore funzionamento", + "stopped-color": "Colore arresto", + "stopped": "Fermo", + "warning-color": "Colore avviso", + "critical-color": "Colore critico", + "opened": "Aperto", + "opened-hint": "Indica se il componente è aperto.", + "open": "Apri", + "open-hint": "Azione eseguita quando l'utente clicca per aprire il componente.", + "close": "Chiudi", + "close-hint": "Azione eseguita quando l'utente clicca per chiudere il componente.", + "close-state-animation": "Animazione stato chiuso", + "close-state-animation-hint": "Attiva animazione lampeggiante quando il componente è in stato chiuso.", + "opened-color": "Colore aperto", + "closed-color": "Colore chiuso", + "opened-rotation-angle": "Angolo di rotazione aperto", + "closed-rotation-angle": "Angolo di rotazione chiuso", + "tank-capacity": "Capacità del serbatoio", + "tank-capacity-hint": "Valore decimale che indica la capacità totale del serbatoio.", + "current-volume": "Volume attuale", + "current-volume-hint": "Valore decimale che indica il volume attualmente occupato.", + "tank-color": "Colore del serbatoio", + "value-box": "Riquadro del valore", + "value-text": "Testo del valore", + "scale": "Scala", + "transparent-mode": "Modalità trasparente", + "major-ticks": "Graduazioni principali", + "intervals": "Intervalli", + "major-ticks-color": "Colore graduazioni principali", + "normal": "Normale", + "minor-ticks": "Graduazioni minori", + "minor-ticks-color": "Colore graduazioni minori", + "temperature": "Temperatura", + "temperature-hint": "Valore decimale che indica la temperatura attuale.", + "update-temperature": "Aggiorna temperatura", + "update-temperature-hint": "Azione eseguita quando l'utente clicca per cambiare la temperatura attuale.", + "run": "Avvia", + "run-hint": "Azione eseguita quando l'utente clicca per avviare il componente.", + "stop": "Ferma", + "stop-hint": "Azione eseguita quando l'utente clicca per fermare il componente.", + "temperature-step": "Incremento della temperatura", + "heat-pump-color": "Colore della pompa di calore", + "power-button-background": "Sfondo del pulsante di accensione", + "value-box-background": "Sfondo del riquadro del valore", + "value-units": "Unità di valore", + "filtration-mode": "Modalità di filtrazione", + "filtration-mode-hint": "Valore intero che indica la modalità di filtrazione attuale.", + "filtration-mode-update": "Stato aggiornamento modalità di filtrazione", + "filtration-mode-update-hint": "Azione eseguita quando l'utente clicca per cambiare la modalità di filtrazione attuale.", + "filter-mode": "Filtro", + "waste-mode": "Scarico", + "backwash-mode": "Controlavaggio", + "recirculate-mode": "Ricircolo", + "rinse-mode": "Risciacquo", + "closed-mode": "Chiuso", + "sand-filter-color": "Colore filtro a sabbia", + "mode-box-background": "Sfondo del box modalità", + "border-color": "Colore del bordo", + "label-color": "Colore dell'etichetta", + "water-leak-hint": "Indica se è presente una perdita.", + "default-color": "Colore predefinito", + "leak-color": "Colore perdita", + "full-value": "Valore massimo", + "full-value-hint": "Valore decimale che indica il valore massimo.", + "label": "Etichetta", + "icon": "Icona", + "button-color": "Colore del pulsante", + "on-label": "Testo etichetta 'On'", + "off-label": "Testo etichetta 'Off'", + "arrow-presence": "Presenza freccia", + "arrow-presence-hint": "Indica se è presente una freccia nel connettore.", + "arrow-present": "Freccia presente", + "arrow-direction": "Direzione freccia/animazione", + "arrow-direction-hint": "Indica la direzione del flusso.", + "flow-animation": "Animazione del flusso", + "flow-animation-hint": "Indica se è presente un'animazione nel connettore.", + "flow": "Flusso", + "flow-line": "Linea", + "flow-line-style": "Stile della linea", + "flow-style-hint": "Imposta i valori Dash e Gap in modo che la loro somma sia divisibile per 100 senza resto per una perfetta sincronizzazione dell'animazione.", + "flow-dash-cap": "Termine tratteggio flusso", + "dash-cap-butt": "Piato", + "dash-cap-round": "Arrotondato", + "dash-cap-square": "Quadrato", + "dash": "Tratteggio", + "gap": "Spazio", + "main-line": "Linea principale", + "line": "Linea", + "line-color": "Colore della linea", + "arrow-color": "Colore della freccia", + "target-value": "Valore obiettivo", + "target-value-hint": "Indica il punto target sulla scala.", + "min-max-value": "Valore minimo e massimo", + "min-value": "Min", + "max-value": "Max", + "progress-bar": "Barra di progresso", + "progress-arrow": "Freccia di progresso", + "warning-scale-color": "Colore scala di avviso", + "critical-scale-color": "Colore scala critica", + "scale-color": "Colore della scala", + "target": "Obiettivo", + "high-warning-state": "Stato di avviso alto", + "show-high-warning-scale": "Mostra scala di avviso alto", + "high-warning-scale": "Scala di avviso alto", + "high-warning-state-hint": "Valore decimale che indica l'intervallo di avviso alto fino a valore critico alto o massimo.", + "low-warning-state": "Stato di avviso basso", + "show-low-warning-scale": "Mostra scala di avviso basso", + "low-warning-scale": "Scala di avviso basso", + "low-warning-state-hint": "Valore decimale che indica l'intervallo di avviso basso fino a valore critico basso o minimo.", + "high-critical-state": "Stato critico alto", + "show-high-critical-scale": "Mostra scala critica alta", + "high-critical-scale": "Scala critica alta", + "high-critical-state-hint": "Valore decimale che indica l'intervallo critico alto fino al valore massimo della scala.", + "low-critical-state": "Stato critico basso", + "show-low-critical-scale": "Mostra scala critica bassa", + "low-critical-scale": "Scala critica bassa", + "low-critical-state-hint": "Valore decimale che indica un intervallo critico basso fino al valore minimo della scala.", + "filter-color": "Colore del filtro", + "colors": "Colori", + "indicator-colors": "Colori indicatori", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "on": "ON", + "off": "OFF", + "on-off-state": "Stato On/Off", + "on-off-state-hint": "Indica se il componente è nello stato On o Off.", + "on-update-state": "Aggiorna stato su On", + "on-update-state-hint": "Azione eseguita quando l'utente clicca per aggiornare lo stato a On.", + "off-update-state": "Aggiorna stato su Off", + "off-update-state-hint": "Azione eseguita quando l'utente clicca per aggiornare lo stato a Off.", + "voltage": "Tensione", + "input-voltage": "Tensione in ingresso", + "input-voltage-hint": "Valore decimale che indica la tensione in ingresso.", + "output-voltage": "Tensione in uscita", + "output-voltage-hint": "Valore decimale che indica la tensione in uscita.", + "first-phase-voltage": "Tensione prima fase", + "second-phase-voltage": "Tensione seconda fase", + "third-phase-voltage": "Tensione terza fase", + "phase-voltage-hint": "Valore decimale che indica la tensione della fase corrente", + "voltage-hint": "Valore decimale che indica la tensione corrente", + "current-voltage-color": "Colore della tensione attuale", + "phase-indicator-color": "Colore indicatore di fase", + "measured": "Misurato", + "measured-hint": "Valore decimale che indica l'energia consumata in kilowattora", + "day-rate": "Tariffa diurna", + "night-rate": "Tariffa notturna", + "off-peak-rate": "Tariffa fuori punta", + "peak-rate": "Tariffa di picco", + "export-rate": "Tariffa di esportazione", + "operating-mode": "Modalità operativa", + "bypass-mode": "Bypass", + "operating-mode-hint": "Valore intero che indica la modalità operativa attuale (0 - OFF, 1 - ON, 2 - BYPASS)", + "connected": "Connesso", + "connected-hint": "Indica se il componente è nello stato connesso.", + "disconnected": "Disconnesso", + "indicator": "Indicatore", + "operation-mode": "Modalità di funzionamento", + "operation-mode-hint": "Indica se l'inverter è in modalità Rete o Inverter.", + "operation-mode-indicators-color": "Colore degli indicatori della modalità di funzionamento", + "mains-on-mode": "Rete attiva", + "inverter-on-mode": "Inverter attivo", + "charging-mode": "Modalità di carica", + "charging-mode-hint": "Valore intero che indica la modalità di carica attuale (1 - Bulk, 2 - Assorbimento, 3 - Mantenimento)", + "charging-mode-indicators-color": "Colore degli indicatori della modalità di carica", + "inverter-faults": "Guasti", + "inverter-fault-indicators-color": "Colore degli indicatori di guasto", + "overload-fault": "Sovraccarico", + "overload-fault-hint": "Indica se l'inverter è in condizione di sovraccarico.", + "low-battery-fault": "Batteria scarica", + "low-battery-fault-hint": "Indica se la batteria è eccessivamente scarica.", + "temperature-fault": "Temperatura", + "temperature-fault-hint": "Indica se c'è alta temperatura nell'inverter.", + "triangle": "Triangolo", + "socket": "Presa", + "left-button": "Pulsante sinistro", + "right-button": "Pulsante destro", + "alarm-colors": "Colori allarme", + "hook-color": "Colore gancio" + } + }, + "item": { + "selected": "Selezionato" + }, + "js-func": { + "no-return-error": "La funzione deve restituire un valore!", + "return-type-mismatch": "La funzione deve restituire un valore di tipo '{{type}}'!", + "tidy": "Pulito", + "mini": "Mini", + "modules": "Moduli", + "remove-module": "Rimuovi modulo", + "no-modules": "Nessun modulo configurato", + "add-module": "Aggiungi modulo", + "module-alias": "Alias", + "invalid-module-alias-name": "Nome alias non valido", + "module-resource": "Risorsa modulo JS", + "not-unique-module-aliases-error": "Gli alias dei moduli devono essere unici!", + "show-module-info": "Mostra informazioni del modulo", + "show-module-source-code": "Mostra codice sorgente del modulo", + "module-members": "Membri del modulo", + "module-no-members": "Il modulo non ha membri esportati", + "module-load-error": "Errore durante il caricamento del modulo", + "source-code": "Codice sorgente", + "source-code-load-error": "Errore durante il caricamento del codice sorgente", + "no-js-module-text": "Nessun modulo JS trovato", + "no-js-module-matching": "Nessun modulo JS corrispondente a '{{module}}' trovato." + }, + "key-val": { + "key": "Chiave", + "value": "Valore", + "remove-entry": "Rimuovi voce", + "add-entry": "Aggiungi voce", + "no-data": "Nessuna voce" + }, + "layout": { + "layout": "Layout", + "layouts": "Layout", + "manage": "Gestisci layout", + "settings": "Impostazioni layout", + "color": "Colore", + "main": "Principale", + "right": "Destra", + "left": "Sinistra", + "select": "Seleziona layout di destinazione", + "percentage-width": "Larghezza percentuale (%)", + "fixed-width": "Larghezza fissa (px)", + "left-width": "Colonna sinistra (%)", + "right-width": "Colonna destra (%)", + "pick-fixed-side": "Lato fisso: ", + "layout-fixed-width": "Larghezza fissa (px)", + "value-min-error": "Il valore deve essere maggiore di {{min}}{{unit}}", + "value-max-error": "Il valore deve essere minore di {{max}}{{unit}}", + "layout-fixed-width-required": "La larghezza fissa è obbligatoria", + "right-width-percentage-required": "La percentuale di destra è obbligatoria", + "left-width-percentage-required": "La percentuale di sinistra è obbligatoria", + "divider": "Divisore", + "right-side": "Layout lato destro", + "left-side": "Layout lato sinistro", + "add-new-breakpoint": "Aggiungi nuovo punto di interruzione", + "breakpoint": "Punto di interruzione", + "breakpoints": "Punti di interruzione", + "copy-from": "Copia da", + "size": "Dimensione", + "delete-breakpoint-title": "Sei sicuro di voler eliminare il punto di interruzione '{{name}}'?", + "delete-breakpoint-text": "Nota: dopo la conferma, il punto di interruzione non sarà più recuperabile e le impostazioni torneranno al punto di interruzione predefinito." + }, + "legend": { + "direction": "Direzione", + "position": "Posizione", + "show-values": "Mostra valori", + "min-option": "Min", + "max-option": "Max", + "average-option": "Media", + "total-option": "Totale", + "latest-option": "Ultimo", + "sort-legend": "Ordina le chiavi dati nella legenda", + "show-max": "Mostra valore massimo", + "show-min": "Mostra valore minimo", + "show-avg": "Mostra valore medio", + "show-total": "Mostra valore totale", + "show-latest": "Mostra ultimo valore", + "settings": "Impostazioni legenda", + "min": "min", + "max": "max", + "avg": "media", + "total": "totale", + "latest": "ultimo", + "Min": "Min", + "Max": "Max", + "Avg": "Media", + "Total": "Totale", + "Latest": "Ultimo", + "comparison-time-ago": { + "previousInterval": "(intervallo precedente)", + "customInterval": "(intervallo personalizzato)", + "days": "(giorno fa)", + "weeks": "(settimana fa)", + "months": "(mese fa)", + "years": "(anno fa)" + }, + "column-title": "Titolo colonna", + "label": "Etichetta", + "value": "Valore" + }, + "login": { + "login": "Accesso", + "request-password-reset": "Richiedi reimpostazione password", + "reset-password": "Reimposta password", + "create-password": "Crea password", + "two-factor-authentication": "Autenticazione a due fattori", + "passwords-mismatch-error": "Le password inserite devono coincidere!", + "password-again": "Ripeti password", + "sign-in": "Accedi", + "username": "Nome utente (email)", + "remember-me": "Ricordami", + "forgot-password": "Password dimenticata?", + "password-reset": "Reimpostazione password", + "expired-password-reset-message": "Le tue credenziali sono scadute! Crea una nuova password.", + "new-password": "Nuova password", + "new-password-again": "Conferma nuova password", + "password-link-sent-message": "Link di reimpostazione inviato", + "email": "Email", + "invalid-email-format": "Formato email non valido.", + "login-with": "Accedi con {{name}}", + "or": "oppure", + "error": "Errore di accesso", + "verify-your-identity": "Verifica la tua identità", + "select-way-to-verify": "Seleziona un metodo di verifica", + "resend-code": "Reinvia codice", + "resend-code-wait": "Reinvia codice in { time, plural, =1 {1 secondo} other {# secondi} }", + "try-another-way": "Prova un altro metodo", + "totp-auth-description": "Inserisci il codice di sicurezza dalla tua app di autenticazione.", + "totp-auth-placeholder": "Codice", + "sms-auth-description": "Un codice di sicurezza è stato inviato al tuo telefono al numero {{contact}}.", + "sms-auth-placeholder": "Codice SMS", + "email-auth-description": "Un codice di sicurezza è stato inviato al tuo indirizzo email {{contact}}.", + "email-auth-placeholder": "Codice email", + "backup-code-auth-description": "Inserisci uno dei tuoi codici di backup.", + "backup-code-auth-placeholder": "Codice di backup", + "activation-link-expired": "Il link di attivazione è scaduto", + "activation-link-expired-message": "Il link per attivare il tuo profilo è scaduto. Puoi tornare alla pagina di accesso per ricevere una nuova email.", + "reset-password-link-expired": "Il link per reimpostare la password è scaduto", + "reset-password-link-expired-message": "Il link per reimpostare la password è scaduto. Puoi tornare alla pagina di accesso per ricevere una nuova email." + }, + "mobile": { + "add-application": "Aggiungi applicazione", + "app-id": "ID applicazione", + "app-id-required": "ID applicazione richiesto", + "app-id-pattern": "Formato ID applicazione non valido", + "app-store-link": "Link App Store", + "app-store-link-required": "Link App Store richiesto", + "application-details": "Dettagli applicazione", + "application-package": "Pacchetto applicazione", + "application-secret": "Segreto applicazione", + "application-secret-required": "Segreto applicazione richiesto", + "application": "Applicazione", + "applications": "Applicazioni", + "copy-app-id": "Copia ID applicazione", + "copy-app-store-link": "Copia link App Store", + "copy-application-package": "Copia pacchetto applicazione", + "copy-application-secret": "Copia segreto applicazione", + "copy-google-play-link": "Copia link Google Play", + "copy-sha256-certificate-fingerprints": "Copia impronta SHA256 del certificato", + "delete-application": "Elimina applicazione", + "delete-application-button-text": "Comprendo le conseguenze, elimina applicazione", + "delete-application-text": "Questa azione è irreversibile. L'applicazione sarà eliminata definitivamente.
Se non desideri eliminarla in modo permanente puoi sospendere temporaneamente l'applicazione.
Per eliminare comunque l'applicazione digita \"{{phrase}}\" per confermare.", + "delete-application-title-short": "Sei sicuro di voler eliminare l'applicazione '{{name}}'?", + "delete-application-text-short": "Attenzione, dopo la conferma l'applicazione e tutti i dati correlati saranno irreversibili.", + "delete-application-phrase": "elimina applicazione", + "delete-applications-bundle-text": "Attenzione, dopo la conferma il pacchetto mobile e tutti i dati correlati saranno irreversibili.", + "delete-applications-bundle-title": "Sei sicuro di voler eliminare il pacchetto mobile '{{bundleName}}'?", + "generate-application-secret": "Genera segreto applicazione", + "google-play-link": "Link Google Play", + "google-play-link-required": "Link Google Play richiesto", + "latest-version": "Ultima versione", + "min-version": "Versione minima", + "invalid-version-pattern": "Formato versione non valido. Usa il formato: major.minor.patch (es. 1.0.0).", + "mobile-center": "Centro mobile", + "mobile-package": "Pacchetto applicazione", + "mobile-package-max-length": "Il pacchetto applicazione deve contenere meno di 256 caratteri", + "mobile-package-required": "Pacchetto applicazione richiesto.", + "mobile-package-pattern": "Formato pacchetto applicazione non valido", + "no-application": "Nessuna applicazione trovata", + "no-bundles": "Nessun pacchetto trovato", + "platform-type": "Tipo di piattaforma", + "search-application": "Cerca applicazioni", + "search-bundles": "Cerca pacchetti", + "set": "Imposta", + "sha256-certificate-fingerprints": "Impronta certificato SHA256", + "sha256-certificate-fingerprints-required": "Impronta certificato SHA256 richiesta", + "sha256-certificate-fingerprints-pattern": "Formato impronta SHA256 non valido", + "show-hidden-pages": "Mostra pagine nascoste", + "status": "Stato", + "status-type": { + "deprecated": "Deprecato", + "draft": "Bozza", + "published": "Pubblicato", + "suspended": "Sospeso" + }, + "store-information": "Informazioni dello store", + "version-information": "Informazioni sulla versione", + "min-version-release-notes": "Note di rilascio versione minima", + "latest-version-release-notes": "Note di rilascio ultima versione", + "bundle": "Pacchetto", + "bundles": "Pacchetti", + "add-bundle": "Aggiungi pacchetto", + "title": "Titolo", + "title-required": "Il titolo è obbligatorio", + "title-cannot-contain-only-spaces": "Il titolo non può contenere solo spazi", + "title-max-length": "Il titolo deve contenere meno di 256 caratteri", + "oauth-clients": "Client OAuth 2.0", + "android-app": "App Android", + "android-application": "Applicazione Android", + "ios-app": "App iOS", + "ios-application": "Applicazione iOS", + "invalid-store-link": "Link dello store non valido", + "enable-oauth": "Abilita OAuth 2.0", + "enable-self-registration": "Abilita autoregistrazione", + "edit-bundle": "Modifica pacchetto", + "description": "Descrizione", + "basic-settings": "Impostazioni di base", + "no-application-matching": "Nessuna applicazione corrispondente a '{{entity}}' trovata.", + "no-bundle-matching": "Nessun pacchetto corrispondente a '{{entity}}' trovato.", + "application-required": "Applicazione obbligatoria.", + "bundle-required": "Pacchetto obbligatorio.", + "no-application-text": "Nessuna applicazione trovata", + "no-bundle-text": "Nessun pacchetto trovato", + "layout": "Layout", + "pages": "Pagine", + "hide-all-pages": "Nascondi tutte le pagine", + "reset-to-default-pages": "Reimposta alle pagine predefinite", + "add-specific-page": "Aggiungi pagina specifica", + "visible": "Visibile", + "hidden": "Nascosto", + "reset-to-page-default": "Ripristina pagina ai valori predefiniti", + "mobile-599": "Mobile (max 599px)", + "tablet-959": "Tablet (max 959px)", + "max-element-number": "Numero massimo di elementi", + "page-name": "Nome pagina", + "page-name-required": "Il nome della pagina è obbligatorio.", + "page-name-cannot-contain-only-spaces": "Il nome della pagina non può contenere solo spazi.", + "page-name-max-length": "Il nome della pagina deve contenere meno di 256 caratteri", + "page-type": "Tipo di pagina", + "pages-types": { + "dashboard": "Dashboard", + "web-view": "Vista web", + "custom": "Personalizzato" + }, + "url": "URL", + "invalid-url-format": "Formato URL non valido", + "path": "Percorso", + "invalid-path-format": "Formato del percorso non valido", + "custom-page": "Pagina personalizzata", + "edit-page": "Modifica pagina", + "edit-custom-page": "Modifica pagina personalizzata", + "delete-page": "Elimina pagina", + "qr-code-widget": "Widget codice QR", + "type-here": "Digita qui", + "configuration-dialog": "Finestra di configurazione", + "configuration-app": "App di configurazione", + "configuration-step": { + "prepare-environment-title": "Prepara l'ambiente di sviluppo", + "prepare-environment-text": "L'app mobile Flutter ThingsBoard richiede Flutter SDK. Segui le istruzioni per configurare Flutter SDK.", + "get-source-code-title": "Ottieni il codice sorgente dell'app", + "get-source-code-text": "Puoi ottenere il codice sorgente dell'app mobile Flutter ThingsBoard clonandolo dal repository GitHub:", + "configure-api-title": "Configura endpoint API di ThingsBoard", + "configure-api-text": "Apri il progetto flutter_thingsboard_pe_app nel tuo editor/IDE. Modifica:", + "configure-api-hint": "Imposta il valore della costante thingsBoardApiEndpoint per corrispondere all'endpoint API della tua istanza ThingsBoard. Non utilizzare gli hostname 'localhost' o '127.0.0.1'.", + "run-app-title": "Esegui l'app", + "run-app-text": "Esegui l'app come descritto nel tuo IDE.\nSe usi il terminale, esegui il comando seguente:", + "more-information": "Informazioni dettagliate disponibili nella nostra documentazione introduttiva.", + "getting-started": "Guida introduttiva", + "configure-package-title": "Configura il pacchetto dell'applicazione", + "configure-package-text": "Puoi modificare manualmente il pacchetto dell'applicazione o usare uno strumento CLI di terze parti.", + "configure-package-text-install": "Per installare lo strumento CLI Rename, esegui il seguente comando:", + "configure-package-run-commands": "Esegui questi comandi nella directory root del tuo progetto:" + } + }, + "notification": { + "action-button": "Pulsante di azione", + "action-type": "Tipo di azione", + "active": "Attivo", + "add-notification-recipients-group": "Aggiungi gruppo destinatari notifica", + "add-notification-template": "Aggiungi modello di notifica", + "add-recipient": "Aggiungi destinatario", + "add-recipients": "Aggiungi destinatari", + "add-rule": "Aggiungi regola", + "add-stage": "Aggiungi fase", + "add-template": "Aggiungi modello", + "after": "Dopo", + "alarm-assignment-trigger-settings": "Impostazioni trigger assegnazione allarme", + "alarm-comment-trigger-settings": "Impostazioni trigger commento allarme", + "alarm-trigger-settings": "Impostazioni trigger allarme", + "all": "Tutti", + "api-feature-hint": "Se il campo è vuoto, il trigger sarà applicato a tutte le funzionalità API", + "api-usage-trigger-settings": "Impostazioni trigger utilizzo API", + "new-platform-version-trigger-settings": "Impostazioni trigger nuova versione piattaforma", + "rate-limits-trigger-settings": "Impostazioni trigger superamento limiti", + "task-processing-failure-trigger-settings": "Impostazioni trigger errore elaborazione attività", + "at-least-one-should-be-selected": "Almeno uno deve essere selezionato", + "basic-settings": "Impostazioni di base", + "button-text": "Testo pulsante", + "button-text-required": "Il testo del pulsante è obbligatorio", + "button-text-max-length": "Il testo del pulsante deve essere lungo al massimo {{ length }} caratteri", + "compose": "Componi", + "conversation": "Conversazione", + "conversation-required": "La conversazione è obbligatoria", + "copy-notification-template": "Copia modello di notifica", + "copy-rule": "Copia regola", + "copy-template": "Copia modello", + "create-new": "Crea nuovo", + "created": "Creato", + "customize-messages": "Personalizza messaggi", + "delete-notification-text": "Attenzione, dopo la conferma la notifica non sarà più recuperabile.", + "delete-notification-title": "Sei sicuro di voler eliminare la notifica?", + "delete-notifications-text": "Attenzione, dopo la conferma le notifiche non saranno più recuperabili.", + "delete-notifications-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 notifica} other {# notifiche} }?", + "delete-recipient-text": "Attenzione, dopo la conferma il destinatario non sarà più recuperabile.", + "delete-recipient-title": "Sei sicuro di voler eliminare il destinatario '{{recipientName}}'?", + "delete-recipients-text": "Attenzione, dopo la conferma i destinatari non saranno più recuperabili.", + "delete-recipients-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 destinatario} other {# destinatari} }?", + "delete-request-text": "Attenzione, dopo la conferma la richiesta non sarà più recuperabile.", + "delete-request-title": "Sei sicuro di voler eliminare la richiesta?", + "delete-requests-text": "Attenzione, dopo la conferma le richieste non saranno più recuperabili.", + "delete-requests-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 richiesta} other {# richieste} }?", + "delete-rule-text": "Attenzione, dopo la conferma la regola non sarà più recuperabile.", + "delete-rule-title": "Sei sicuro di voler eliminare la regola '{{ruleName}}'?", + "delete-rules-text": "Attenzione, dopo la conferma le regole non saranno più recuperabili.", + "delete-rules-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 regola} other {# regole} }?", + "delete-template-text": "Attenzione, dopo la conferma il modello non sarà più recuperabile.", + "delete-template-title": "Sei sicuro di voler eliminare il modello '{{templateName}}'?", + "delete-templates-text": "Attenzione, dopo la conferma i modelli non saranno più recuperabili.", + "delete-templates-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 modello} other {# modelli} }?", + "deleted": "Eliminato", + "delivery-method": { + "delivery-method": "Metodo di consegna", + "email": "Email", + "email-preview": "Anteprima notifica email", + "slack": "Slack", + "slack-preview": "Anteprima notifica Slack", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Anteprima notifica Microsoft Teams", + "sms": "SMS", + "sms-preview": "Anteprima notifica SMS", + "web": "Web", + "web-preview": "Anteprima notifica Web", + "mobile-app": "App mobile", + "mobile-app-preview": "Anteprima notifica app mobile" + }, + "delivery-method-not-configure-click": "Metodo di consegna non configurato. Clicca per configurarlo.", + "delivery-method-not-configure-contact": "Metodo di consegna non configurato. Contatta l'amministratore di sistema.", + "delivery-methods": "Metodi di consegna", + "description": "Descrizione", + "device-activity-trigger-settings": "Impostazioni trigger attività dispositivo", + "device-list-rule-hint": "Se il campo è vuoto, il trigger sarà applicato a tutti i dispositivi", + "device-profiles-list-rule-hint": "Se il campo è vuoto, il trigger sarà applicato a tutti i profili dispositivo", + "disabled": "Disabilitato", + "edge-trigger-settings": "Impostazioni trigger edge", + "edge-list-rule-hint": "Se il campo è vuoto, il trigger sarà applicato a tutte le istanze edge", + "edit-notification-recipients-group": "Modifica gruppo destinatari notifica", + "edit-notification-template": "Modifica modello di notifica", + "edit-rule": "Modifica regola", + "edit-template": "Modifica modello", + "enabled": "Abilitato", + "entities-limit-trigger-settings": "Impostazioni trigger limite entità", + "entity-action-trigger-settings": "Impostazioni trigger azione entità", + "entity-type": "Tipo entità", + "escalation-chain": "Catena di escalation", + "failed-send": "Errori di invio", + "fails": "{ count, plural, =1 {1 errore} other {# errori} }", + "filter": "Filtro", + "first-recipient": "Primo destinatario", + "inactive": "Inattivo", + "inbox": "Posta in arrivo", + "notification-inbox": "Notifiche / Posta in arrivo", + "input-field-support-templatization": "Il campo di input supporta la templatizzazione.", + "input-fields-support-templatization": "I campi di input supportano la templatizzazione.", + "link": "Link", + "link-required": "Il link è obbligatorio", + "link-type": { + "dashboard": "Apri dashboard", + "link": "Apri URL" + }, + "loading-notifications": "Caricamento notifiche...", + "management": "Gestione notifiche", + "mark-all-as-read": "Segna tutte come lette", + "mark-as-read": "Segna come letta", + "message": "Messaggio", + "message-required": "Il messaggio è obbligatorio", + "message-max-length": "Il messaggio deve contenere al massimo {{ length }} caratteri", + "name": "Nome", + "name-required": "Il nome è obbligatorio", + "new-notification": "Nuova notifica", + "no-inbox-notification": "Nessuna notifica trovata", + "no-notification-request": "Nessuna richiesta di notifica", + "no-notification-templates": "Nessun modello di notifica trovato", + "no-notifications-yet": "Nessuna notifica al momento", + "no-recipients-notification": "Nessuna notifica di destinatari", + "no-recipients-matching": "Nessun destinatario corrispondente a '{{entity}}' trovato.", + "no-recipients-text": "Nessun destinatario trovato", + "no-rule": "Nessuna regola configurata", + "no-rules-notification": "Nessuna regola di notifica", + "no-severity-found": "Nessuna gravità trovata", + "no-severity-matching": "'{{severity}}' non trovata.", + "no-template-matching": "Nessuna risorsa corrispondente a '{{template}}' trovata.", + "not-found-slack-recipient": "Destinatario Slack non trovato", + "notification": "Notifica", + "notification-center": "Centro notifiche", + "notification-tap-action": "Azione al tocco della notifica", + "notification-tap-action-hint": "Se non abilitata, verrà utilizzata la dashboard di default dell'allarme", + "notify": "notifica", + "notify-again": "Notifica di nuovo", + "notify-alarm-action": { + "acknowledged": "Allarme riconosciuto", + "assigned": "Allarme assegnato", + "cleared": "Allarme risolto", + "created": "Allarme creato", + "severity-changed": "Gravità allarme cambiata", + "unassigned": "Allarme non assegnato" + }, + "notify-on": "Notifica su", + "notify-on-comment-update": "Notifica su aggiornamento commento", + "notify-on-required": "Campo 'Notifica su' obbligatorio", + "notify-on-unassign": "Notifica su disassegnazione", + "notify-only-user-comments": "Notifica solo commenti utente", + "only-rule-chain-lifecycle-failures": "Solo fallimenti del ciclo di vita della rule chain", + "only-rule-node-lifecycle-failures": "Solo fallimenti del ciclo di vita del nodo", + "platform-users": "Utenti piattaforma", + "rate-limits": "Limiti di velocità", + "rate-limits-hint": "Se il campo è vuoto, il trigger sarà applicato a tutti i limiti", + "recipient": "Destinatario", + "recipient-group": "Gruppo destinatari", + "recipient-type": { + "affected-tenant-administrators": "Amministratori tenant coinvolti", + "affected-user": "Utente coinvolto", + "all-users": "Tutti gli utenti", + "customer-users": "Utenti cliente", + "system-administrators": "Amministratori di sistema", + "tenant-administrators": "Amministratori tenant", + "user-filters": "Filtro utente", + "user-list": "Lista utenti", + "users-entity-owner": "Utenti del proprietario dell'entità" + }, + "recipients": "Destinatari", + "notification-recipient": "Destinatario notifica", + "notification-recipient-required": "Il destinatario della notifica è obbligatorio.", + "notification-recipients": "Notifiche / Destinatari", + "recipients-count": "{ count, plural, =1 {1 destinatario} other {# destinatari} }", + "recipients-required": "I destinatari sono obbligatori", + "refresh-allow-delivery-method": "Aggiorna metodo di consegna consentito", + "request-search": "Cerca richiesta", + "request-status": { + "processing": "Elaborazione in corso", + "scheduled": "Pianificato", + "sent": "Inviato" + }, + "review": "Revisione", + "rule": "Regola", + "rule-chain-list-rule-hint": "Se il campo è vuoto, il trigger verrà applicato a tutte le rule chain", + "rule-engine-events-trigger-settings": "Impostazioni trigger eventi rule engine", + "rule-engine-filter": "Filtro rule engine", + "rule-name": "Nome regola", + "rule-name-required": "Il nome è obbligatorio", + "rule-disable": "Disattiva regola di notifica", + "rule-enable": "Attiva regola di notifica", + "rule-node-filter": "Filtro nodo regola", + "rules": "Regole", + "notification-rules": "Notifiche / Regole", + "scheduler-later": "Pianifica per dopo", + "search-notification": "Cerca notifiche", + "search-recipients": "Cerca destinatari", + "search-rules": "Cerca regole", + "search-templates": "Cerca modelli", + "see-documentation": "Vedi documentazione", + "selected-notifications": "{ count, plural, =1 {1 notifica} other {# notifiche} } selezionata", + "selected-recipients": "{ count, plural, =1 {1 destinatario} other {# destinatari} } selezionato", + "selected-requests": "{ count, plural, =1 {1 richiesta} other {# richieste} } selezionata", + "selected-rules": "{ count, plural, =1 {1 regola} other {# regole} } selezionata", + "selected-template": "{ count, plural, =1 {1 modello} other {# modelli} } selezionato", + "send-notification": "Invia notifica", + "sent": "Inviato", + "setup": "Configurazione", + "notification-sent": "Notifiche / Inviate", + "set-entity-from-notification": "Imposta entità dalla notifica allo stato della dashboard", + "slack-chanel-type": "Tipo di canale Slack", + "slack-chanel-types": { + "direct": "Messaggio diretto", + "private-channel": "Canale privato", + "public-channel": "Canale pubblico" + }, + "start-from-scratch": "Inizia da zero", + "status": "Stato", + "stop-escalation-alarm-status-become": "Ferma l'escalation quando lo stato dell'allarme diventa:", + "subject": "Oggetto", + "subject-required": "L'oggetto è obbligatorio", + "subject-max-length": "L'oggetto deve contenere al massimo {{ length }} caratteri", + "template": "Modello", + "template-name": "Nome modello", + "template-required": "Il modello è obbligatorio", + "template-type": { + "alarm": "Allarme", + "alarm-assignment": "Assegnazione allarme", + "alarm-comment": "Commento allarme", + "api-usage-limit": "Limite utilizzo API", + "device-activity": "Attività dispositivo", + "entities-limit": "Limite entità", + "entity-action": "Azione entità", + "general": "Generale", + "rule-engine-lifecycle-event": "Evento ciclo di vita rule engine", + "rule-node": "Nodo regola", + "new-platform-version": "Nuova versione piattaforma", + "rate-limits": "Limiti superati", + "edge-communication-failure": "Errore comunicazione edge", + "edge-connection": "Connessione edge", + "task-processing-failure": "Errore elaborazione attività" + }, + "templates": "Modelli", + "notification-templates": "Notifiche / Modelli", + "tenant-profiles-list-rule-hint": "Se il campo è vuoto, il trigger sarà applicato a tutti i profili tenant", + "tenants-list-rule-hint": "Se il campo è vuoto, il trigger sarà applicato a tutti i tenant", + "threshold": "Soglia", + "theme-color": "Colore tema", + "time": "Tempo", + "track-rule-node-events": "Traccia eventi nodo regola", + "trigger": { + "alarm": "Allarme", + "alarm-assignment": "Assegnazione allarme", + "alarm-comment": "Commento allarme", + "api-usage-limit": "Limite utilizzo API", + "device-activity": "Attività dispositivo", + "entities-limit": "Limite entità", + "entity-action": "Azione entità", + "rule-engine-lifecycle-event": "Evento ciclo di vita rule engine", + "new-platform-version": "Nuova versione della piattaforma", + "rate-limits": "Limiti superati", + "edge-connection": "Connessione edge", + "edge-communication-failure": "Errore comunicazione edge", + "task-processing-failure": "Errore elaborazione attività", + "trigger": "Trigger", + "trigger-required": "Il trigger è obbligatorio" + }, + "type": "Tipo", + "unread": "Non letti", + "updated": "Aggiornato", + "use-deprecated-webhook-connectors": "Usa connettori webhook deprecati", + "use-old-api": "Usa API vecchia", + "use-template": "Usa modello", + "view-all": "Visualizza tutto", + "warning": "Avviso", + "webhook-url": "URL webhook", + "webhook-url-required": "L'URL webhook è obbligatorio", + "workflow-url": "URL flusso di lavoro", + "workflow-url-required": "L'URL del flusso di lavoro è obbligatorio", + "channel-name": "Nome del canale", + "channel-name-required": "Il nome del canale è obbligatorio", + "settings": { + "notification-settings": "Impostazioni notifica", + "reset-all": "Reimposta tutte", + "reset-all-title": "Sei sicuro di voler reimpostare il modulo?", + "reset-all-text": "Dopo la conferma, il modulo delle impostazioni verrà reimpostato ai valori predefiniti e salvato.", + "type": "Tipo", + "enable-all": "Abilita tutte", + "disable-all": "Disabilita tutte", + "delivery-not-configured": "Metodo di consegna non configurato" + } + }, + "ota-update": { + "add": "Aggiungi pacchetto", + "assign-firmware": "Firmware assegnato", + "assign-firmware-required": "Il firmware assegnato è obbligatorio", + "assign-software": "Software assegnato", + "assign-software-required": "Il software assegnato è obbligatorio", + "auto-generate-checksum": "Genera automaticamente checksum", + "checksum": "Checksum", + "checksum-hint": "Se il checksum è vuoto, verrà generato automaticamente", + "checksum-algorithm": "Algoritmo di checksum", + "checksum-copied-message": "Checksum del pacchetto copiato negli appunti", + "change-firmware": "La modifica del firmware può causare l'aggiornamento di { count, plural, =1 {1 dispositivo} other {# dispositivi} }.", + "change-software": "La modifica del software può causare l'aggiornamento di { count, plural, =1 {1 dispositivo} other {# dispositivi} }.", + "chose-compatible-device-profile": "Il pacchetto caricato sarà disponibile solo per dispositivi con il profilo selezionato.", + "chose-firmware-distributed-device": "Scegli il firmware da distribuire ai dispositivi", + "chose-software-distributed-device": "Scegli il software da distribuire ai dispositivi", + "content-type": "Tipo di contenuto", + "copy-checksum": "Copia checksum", + "copy-direct-url": "Copia URL diretto", + "copyId": "Copia ID pacchetto", + "copied": "Copiato!", + "delete": "Elimina pacchetto", + "delete-ota-update-text": "Attenzione, dopo la conferma l'aggiornamento OTA non sarà più recuperabile.", + "delete-ota-update-title": "Sei sicuro di voler eliminare l'aggiornamento OTA '{{title}}'?", + "delete-ota-updates-text": "Attenzione, dopo la conferma tutti gli aggiornamenti OTA selezionati saranno rimossi.", + "delete-ota-updates-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 aggiornamento OTA} other {# aggiornamenti OTA} }?", + "description": "Descrizione", + "direct-url": "URL diretto", + "direct-url-copied-message": "URL diretto del pacchetto copiato negli appunti", + "direct-url-required": "L'URL diretto è obbligatorio", + "download": "Scarica pacchetto", + "drop-file": "Trascina un file pacchetto o fai clic per selezionare un file da caricare.", + "drop-package-file-or": "Trascina un file pacchetto o", + "file-name": "Nome file", + "file-size": "Dimensione file", + "file-size-bytes": "Dimensione file in byte", + "idCopiedMessage": "ID del pacchetto copiato negli appunti", + "no-firmware-matching": "Nessun pacchetto OTA Firmware compatibile corrispondente a '{{entity}}' trovato.", + "no-firmware-text": "Nessun pacchetto OTA Firmware compatibile disponibile.", + "no-packages-text": "Nessun pacchetto trovato", + "no-software-matching": "Nessun pacchetto OTA Software compatibile corrispondente a '{{entity}}' trovato.", + "no-software-text": "Nessun pacchetto OTA Software compatibile disponibile.", + "ota-update": "Aggiornamento OTA", + "ota-update-details": "Dettagli aggiornamento OTA", + "ota-updates": "Aggiornamenti OTA", + "package-file": "File pacchetto", + "package-type": "Tipo di pacchetto", + "packages-repository": "Repository pacchetti", + "search": "Cerca pacchetti", + "selected-package": "{ count, plural, =1 {1 pacchetto} other {# pacchetti} } selezionati", + "title": "Titolo", + "title-required": "Il titolo è obbligatorio.", + "title-max-length": "Il titolo deve essere inferiore a 256 caratteri", + "types": { + "firmware": "Firmware", + "software": "Software" + }, + "upload-binary-file": "Carica file binario", + "use-external-url": "Usa URL esterno", + "version": "Versione", + "version-required": "La versione è obbligatoria.", + "version-tag": "Tag versione", + "version-tag-hint": "Il tag personalizzato deve corrispondere alla versione segnalata dal dispositivo.", + "version-max-length": "La versione deve essere inferiore a 256 caratteri", + "warning-after-save-no-edit": "Una volta caricato il pacchetto, non sarà possibile modificare titolo, versione, profilo dispositivo e tipo di pacchetto." + }, + "position": { + "top": "Alto", + "bottom": "Basso", + "left": "Sinistra", + "right": "Destra" + }, + "profile": { + "profile": "Profilo", + "last-login-time": "Ultimo accesso", + "change-password": "Cambia password", + "current-password": "Password attuale", + "copy-jwt-token": "Copia token JWT", + "jwt-token": "Token JWT", + "token-valid-till": "Token valido fino a", + "tokenCopiedSuccessMessage": "Token JWT copiato negli appunti", + "tokenCopiedWarnMessage": "Il token JWT è scaduto! Per favore, ricarica la pagina." + }, + "profiles": { + "profiles": "Profili" + }, + "security": { + "security": "Sicurezza", + "general-settings": "Impostazioni di sicurezza generali", + "access-token": "Token di accesso", + "access-token-required": "Il token di accesso è obbligatorio", + "clientId": "ID client", + "clientId-required": "L'ID client è obbligatorio", + "username": "Nome utente", + "username-required": "Il nome utente è obbligatorio", + "ca-cert": "Certificato CA", + "2fa": { + "2fa": "Autenticazione a due fattori", + "2fa-description": "L'autenticazione a due fattori protegge il tuo account da accessi non autorizzati. Tutto ciò che devi fare è inserire un codice di sicurezza durante l'accesso.", + "authenticate-with": "Puoi autenticarti con:", + "disable-2fa-provider-text": "La disattivazione di {{name}} renderà il tuo account meno sicuro", + "disable-2fa-provider-title": "Sei sicuro di voler disattivare {{name}}?", + "get-new-code": "Ottieni nuovo codice", + "main-2fa-method": "Usa come metodo principale di autenticazione a due fattori", + "dialog": { + "activation-step-description-email": "La prossima volta che accederai, ti verrà chiesto di inserire il codice di sicurezza inviato al tuo indirizzo email.", + "activation-step-description-sms": "La prossima volta che accederai, ti verrà chiesto di inserire il codice di sicurezza inviato al numero di telefono.", + "activation-step-description-totp": "La prossima volta che accederai, dovrai fornire un codice di autenticazione a due fattori.", + "activation-step-label": "Attivazione", + "backup-code-description": "Stampa i codici per averli a portata di mano quando dovrai accedere al tuo account. Ogni codice può essere utilizzato una sola volta.", + "backup-code-warn": "Una volta lasciata questa pagina, i codici non potranno essere visualizzati di nuovo. Conservali in modo sicuro usando le opzioni sotto.", + "download-txt": "Scarica (txt)", + "email-step-description": "Inserisci un'email da usare come autenticatore.", + "email-step-label": "Email", + "enable-email-title": "Abilita autenticatore email", + "enable-sms-title": "Abilita autenticatore SMS", + "enable-totp-title": "Abilita app autenticatrice", + "enter-verification-code": "Inserisci qui il codice a 6 cifre", + "get-backup-code-title": "Ottieni codice di backup", + "next": "Avanti", + "scan-qr-code": "Scansiona questo codice QR con la tua app di verifica", + "send-code": "Invia codice", + "sms-step-description": "Inserisci un numero di telefono da usare come autenticatore.", + "sms-step-label": "Numero di telefono", + "success": "Successo!", + "totp-step-description-install": "Puoi installare app come Google Authenticator, Authy o Duo.", + "totp-step-description-open": "Apri l'app di autenticazione sul tuo smartphone.", + "totp-step-label": "Ottieni app", + "verification-code": "Codice a 6 cifre", + "verification-code-invalid": "Formato del codice di verifica non valido", + "verification-code-incorrect": "Codice di verifica errato", + "verification-code-many-request": "Troppe richieste, controlla il codice di verifica", + "verification-step-description": "Inserisci il codice a 6 cifre che abbiamo appena inviato a {{address}}", + "verification-step-label": "Verifica" + }, + "provider": { "email": "Email", - "no-address": "Nessun indirizzo" - }, - "common": { - "username": "Nome utente", - "password": "Password", - "enter-username": "Inserisci nome utente", - "enter-password": "Inserisci password", - "enter-search": "Cerca ...", - "created-time": "Ora di creazione" - }, - "content-type": { - "json": "Json", - "text": "Testo", - "binary": "Binario (Base64)" - }, - "customer": { - "customer": "Cliente", - "customers": "Clienti", - "management": "Gestione cliente", - "dashboard": "Dashboard cliente", - "dashboards": "Dashboard cliente", - "devices": "Dispositivi cliente", - "entity-views": "Viste entità cliente", - "assets": "Asset cliente", - "public-dashboards": "Dashboard pubbliche", - "public-devices": "Dispositivi pubblici", - "public-assets": "Asset pubblici", - "public-entity-views": "Viste entità pubbliche", - "add": "Aggiungi cliente", - "delete": "Elimina cliente", - "manage-customer-users": "Gestisci utenti cliente", - "manage-customer-devices": "Gestisci dispositivi cliente", - "manage-customer-dashboards": "Gestisci dashboard cliente", - "manage-public-devices": "Gestisci dispositivi pubblici", - "manage-public-dashboards": "Gestisci dashboard pubbliche", - "manage-customer-assets": "Gestisci asset cliente", - "manage-public-assets": "Gestisci asset pubblici", - "add-customer-text": "Aggiungi nuovo cliente", - "no-customers-text": "Nessun cliente trovato", - "customer-details": "Dettagli cliente", - "delete-customer-title": "Sei sicuro di voler eliminare il cliente '{{customerTitle}}'?", - "delete-customer-text": "Attenzione, dopo la conferma il cliente e tutti i suoi dati non saranno più recuperabili.", - "delete-customers-title": "Sei sicuro di voler cancellare { count, plural, =1 {1 cliente} other {# clienti} }?", - "delete-customers-action-title": "Elimina { count, plural, =1 {1 cliente} other {# clienti} }", - "delete-customers-text": "Attenzione, dopo la conferma tutti i clienti selezionati saranno rimossi e i loro dati non saranno più recuperabili.", - "manage-users": "Gestisci utenti", - "manage-assets": "Gestisci asset", - "manage-devices": "Gestisci dispositivi", - "manage-dashboards": "Gestisci dashboard", - "title": "Titolo", - "title-required": "Titolo obbligatorio.", - "description": "Descrizione", - "details": "Dettagli", - "events": "Eventi", - "copyId": "Copia Id cliente", - "idCopiedMessage": "Id cliente copiato negli appunti", - "select-customer": "Seleziona cliente", - "no-customers-matching": "Nessun cliente corrispondente a '{{entity}}' è stato trovato.", - "customer-required": "Cliente obbligatorio", - "select-default-customer": "Seleziona cliente di default", - "default-customer": "Cliente di default", - "default-customer-required": "Il cliente di default è obbligatorio per il debug della dashboard a livello di Tenant" - }, - "datetime": { - "date-from": "Data da", - "time-from": "Ora da", - "date-to": "Data a", - "time-to": "Ora a" - }, - "dashboard": { - "dashboard": "Dashboard", - "dashboards": "Dashboard", - "management": "Gestione Dashboard", - "view-dashboards": "Mostra Dashboard", - "add": "Aggiungi Dashboard", - "assign-dashboard-to-customer": "Assegna Dashboard al cliente", - "assign-dashboard-to-customer-text": "Seleziona le dashboard da assegnare al client", - "assign-to-customer-text": "Seleziona il cliente a cui assegnare la/le dashboard", - "assign-to-customer": "Assegna al cliente", - "unassign-from-customer": "Annulla assegnazione al cliente", - "make-public": "Rendi pubblica la dashboard", - "make-private": "Rendi privata la dashboard", - "manage-assigned-customers": "Gestisci clienti assegnati", - "assigned-customers": "Clienti assegnati", - "assign-to-customers": "Assegna Dashboard ai Clienti", - "assign-to-customers-text": "Seleziona i clienti da assegnare alla/alle dashboard", - "unassign-from-customers": "Annulla assegnazione Dashboard ai Clienti", - "unassign-from-customers-text": "Seleziona i clienti di cui annullare l'assegnazione alla/alle dashboard", - "no-dashboards-text": "Nessuna dashboard trovata", - "no-widgets": "Nessun widget configurato", - "add-widget": "Aggiungi nuovo widget", - "title": "Titolo", - "select-widget-title": "Seleziona widget", - "select-widget-subtitle": "Elenco tipi di widget disponibili", - "delete": "Elimina dashboard", - "title-required": "Titolo obbligatorio.", - "description": "Descrizione", - "details": "Dettagli", - "dashboard-details": "Dettagli dashboard", - "add-dashboard-text": "Aggiungi nuova dashboard", - "assign-dashboards": "Assegna dashboard", - "assign-new-dashboard": "Assegna nuova dashboard", - "assign-dashboards-text": "Assegna { count, plural, =1 {1 dashboard} other {# dashboard} } ai clienti", - "unassign-dashboards-action-text": "Annulla assegnazione { count, plural, =1 {1 dashboard} other {# dashboard} } ai clienti", - "delete-dashboards": "Elimina dashboard", - "unassign-dashboards": "Annulla assegnazione dashboard", - "unassign-dashboards-action-title": "Annulla assegnazione { count, plural, =1 {1 dashboard} other {# dashboard} } al cliente", - "delete-dashboard-title": "Sei sicuro di voler cancellare la dashboard '{{dashboardTitle}}'?", - "delete-dashboard-text": "Attenzione, dopo la conferma la dashboard e tutti i suoi dati non saranno più recuperabili.", - "delete-dashboards-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 dashboard} other {# dashboard} }?", - "delete-dashboards-action-title": "Cancella { count, plural, =1 {1 dashboard} other {# dashboard} }", - "delete-dashboards-text": "Attenzione, dopo la conferma tutte le dashboard selezionate saranno eliminate e tutti i loro dati non saranno più recuperabili.", - "unassign-dashboard-title": "Sei sicuro di voler annullare l'assegnazione della dashboard '{{dashboardTitle}}'?", - "unassign-dashboard-text": "Dopo la conferma sarà annullata l'assegnazione della dashboard e questa non sarà più accessibile dal cliente.", - "unassign-dashboard": "Annulla assegnazione dashboard", - "unassign-dashboards-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, =1 {1 dashboard} other {# dashboard} }?", - "unassign-dashboards-text": "Dopo la conferma sarà annullata l'assegnazione di tutte le dashboard selezionate e queste non saranno più accessibili dal cliente.", - "public-dashboard-title": "La Dashboard è ora pubblica", - "public-dashboard-text": "La dashboard {{dashboardTitle}} è ora pubblica e accessibile al link:", - "public-dashboard-notice": "Nota: Ricorda di rendere pubblici i relativi dispositivi per accedere ai loro dati.", - "make-private-dashboard-title": "Sei sicuro di voler rendere privata la dashboard '{{dashboardTitle}}'?", - "make-private-dashboard-text": "Dopo la conferma la dashboard sarà resa privata e non più accessibile dagli altri.", - "make-private-dashboard": "Rendi privata la dashboard", - "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", - "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", - "select-dashboard": "Seleziona dashboard", - "no-dashboards-matching": "Nessuna dashboard corrispondente a '{{entity}}' è stata trovata.", - "dashboard-required": "Dashboard obbligatoria.", - "select-existing": "Seleziona una dashboard esistente", - "create-new": "Crea nuova dashboard", - "new-dashboard-title": "Titolo nuova dashboard", - "open-dashboard": "Apri dashboard", - "set-background": "Imposta sfondo", - "background-color": "Colore sfondo", - "background-image": "Immagine sfondo", - "background-size-mode": "Modalità dimensione sfondo", - "no-image": "Nessuna immagine selezionata", - "drop-image": "Trascina un'immagine o fai clic per selezionare un file da caricare.", - "settings": "Impostazioni", - "columns-count": "Numero colonne", - "columns-count-required": "Numero colonne obbligatorio.", - "min-columns-count-message": "Ammesso un numero minimo di colonne pari a 10.", - "max-columns-count-message": "Ammesso un numero massimo di colonne pari a 1000.", - "widgets-margins": "Margine tra i widget", - "horizontal-margin": "Margine orizzontale", - "horizontal-margin-required": "Margine orizzontale obbligatorio.", - "min-horizontal-margin-message": "Ammesso un margine orizzontale minimo pari a 0.", - "max-horizontal-margin-message": "Ammesso un margine orizzontale massimo pari a 50.", - "vertical-margin": "Margine verticale", - "vertical-margin-required": "Margine verticale obbligatorio.", - "min-vertical-margin-message": "Ammesso un margine verticale minimo pari a 0.", - "max-vertical-margin-message": "Ammesso un margine verticale massimo pari a 50.", - "autofill-height": "Riempi automaticamente altezza layout", - "mobile-layout": "Impostazioni layout mobile", - "mobile-row-height": "Altezza riga mobile (px)", - "mobile-row-height-required": "Altezza riga mobile è richiesta.", - "min-mobile-row-height-message": "5 pixel è il minimo concesso al valore altezza riga mobile.", - "max-mobile-row-height-message": "200 pixel è il massimo concesso al valore altezza riga mobile.", - "display-title": "Mostra titolo dashboard", - "toolbar-always-open": "Mantieni aperta la barra degli strumenti", - "title-color": "Colore titolo", - "display-dashboards-selection": "Mostra selezione dashboard", - "display-entities-selection": "Mostra selezione entità", - "display-dashboard-timewindow": "Mostra intervallo temporale", - "display-dashboard-export": "Mostra esportazione", - "import": "Importa dashboard", - "export": "Esporta dashboard", - "export-failed-error": "Impossibile esportare la dashboard: {{error}}", - "create-new-dashboard": "Crea nuova dashboard", - "dashboard-file": "File dashboard", - "invalid-dashboard-file-error": "Impossibile importare la dashboard: struttura dati della dashboard non valida.", - "dashboard-import-missing-aliases-title": "Configura alias utilizzati dalla dashboard importata", - "create-new-widget": "Crea nuovo widget", - "import-widget": "Importa widget", - "widget-file": "Widget file", - "invalid-widget-file-error": "Impossibile importare il widget: struttura dati del widget non valida.", - "widget-import-missing-aliases-title": "Configura gli alias utilizzati dai widget importati", - "open-toolbar": "Apri barra degli strumenti", - "close-toolbar": "Chiudi barra degli strumenti", - "configuration-error": "Errore di configurazione", - "alias-resolution-error-title": "Errore di configurazione degli alias della dashboard", - "invalid-aliases-config": "Impossibile trovare un dispositivo corrispondente ad un qualche filtro degli alias.
Contatta l'amministratore per risolvere il problema.", - "select-devices": "Seleziona dispositivi", - "assignedToCustomer": "Assegnato al cliente", - "assignedToCustomers": "Assegnato ai clienti", - "public": "Pubblico", - "public-link": "Link pubblico", - "copy-public-link": "Copia link pubblico", - "public-link-copied-message": "Link pubblico della dashboard copiato negli appunti", - "manage-states": "Gestisci stati dashboard", - "states": "Stati dashboard", - "search-states": "Ricerca stati dashboard", - "selected-states": "{ count, plural, =1 {1 stato dashboard selezionato} other {# stati dashboard selezionati} }", - "edit-state": "Modifica stato dashboard", - "delete-state": "Elimina stato dashboard", - "add-state": "Aggiungi stato dashboard", - "state": "Stato dashboard", - "state-name": "Nome", - "state-name-required": "Nome stato dashboard obbligatorio.", - "state-id": "Id stato", - "state-id-required": "Id stato dashboard obbligatorio.", - "state-id-exists": "Uno stato della dashboard con lo stesso id è già presente.", - "is-root-state": "Stato radice", - "delete-state-title": "Elimina stato dashboard", - "delete-state-text": "Sei sicuro di voler eliminare lo stato della dashboard di nome '{{stateName}}'?", - "show-details": "Mostra dettagli", - "hide-details": "Nascondi dettagli", - "select-state": "Seleziona stato target", - "state-controller": "Stato controller" - }, - "datakey": { - "settings": "Impostazioni", - "advanced": "Avanzate", + "email-description": "Usa un codice di sicurezza inviato al tuo indirizzo email per autenticarti.", + "email-hint": "I codici di autenticazione vengono inviati via email a {{ info }}", + "sms": "SMS", + "sms-description": "Usa il tuo telefono per autenticarti. Ti invieremo un codice di sicurezza via SMS al momento dell'accesso.", + "sms-hint": "I codici di autenticazione vengono inviati via SMS a {{ info }}", + "totp": "App autenticatrice", + "totp-description": "Usa app come Google Authenticator, Authy o Duo sul tuo telefono per autenticarti. Genereranno un codice di sicurezza per l'accesso.", + "totp-hint": "L'app autenticatrice è configurata per il tuo account", + "backup_code": "Codice di backup", + "backup-code-description": "Questi codici di accesso monouso stampabili ti consentono di accedere quando sei lontano dal telefono, ad esempio durante un viaggio.", + "backup-code-hint": "{{ info }} codici monouso sono attivi in questo momento" + } + }, + "password-requirement": { + "at-least": "Almeno:", + "character": "{ count, plural, =1 {1 carattere} other {# caratteri} }", + "digit": "{ count, plural, =1 {1 cifra} other {# cifre} }", + "incorrect-password-try-again": "Password errata. Riprova", + "lowercase-letter": "{ count, plural, =1 {1 lettera minuscola} other {# lettere minuscole} }", + "new-passwords-not-match": "La nuova password non corrisponde", + "password-should-not-contain-spaces": "La password non deve contenere spazi", + "password-not-meet-requirements": "La password non soddisfa i requisiti", + "password-requirements": "Requisiti della password", + "password-should-difference": "La nuova password deve essere diversa da quella attuale", + "special-character": "{ count, plural, =1 {1 carattere speciale} other {# caratteri speciali} }", + "uppercase-letter": "{ count, plural, =1 {1 lettera maiuscola} other {# lettere maiuscole} }", + "at-most": "Al massimo:" + } + }, + "relation": { + "relations": "Relazioni", + "direction": "Direzione", + "clear-relation-type": "Cancella tipo di relazione", + "search-direction": { + "FROM": "Da", + "TO": "A" + }, + "direction-type": { + "FROM": "da", + "TO": "a" + }, + "from-relations": "Relazioni in uscita", + "to-relations": "Relazioni in entrata", + "selected-relations": "{ count, plural, =1 {1 relazione} other {# relazioni} } selezionata", + "type": "Tipo", + "to-entity-type": "Tipo di entità di destinazione", + "to-entity-name": "Nome entità di destinazione", + "from-entity-type": "Tipo di entità di origine", + "from-entity-name": "Nome entità di origine", + "to-entity": "A entità", + "from-entity": "Da entità", + "delete": "Elimina relazione", + "relation-type": "Tipo di relazione", + "relation-type-required": "Il tipo di relazione è obbligatorio.", + "relation-type-max-length": "Il tipo di relazione deve essere inferiore a 256 caratteri", + "any-relation-type": "Qualsiasi tipo", + "add": "Aggiungi relazione", + "edit": "Modifica relazione", + "delete-to-relation-title": "Sei sicuro di voler eliminare la relazione con l'entità '{{entityName}}'?", + "delete-to-relation-text": "Attenzione, dopo la conferma l'entità '{{entityName}}' non sarà più collegata all'entità corrente.", + "delete-to-relations-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 relazione} other {# relazioni} }?", + "delete-to-relations-text": "Attenzione, dopo la conferma tutte le relazioni selezionate saranno rimosse e le entità corrispondenti non saranno più collegate all'entità corrente.", + "delete-from-relation-title": "Sei sicuro di voler eliminare la relazione dall'entità '{{entityName}}'?", + "delete-from-relation-text": "Attenzione, dopo la conferma l'entità corrente non sarà più collegata all'entità '{{entityName}}'.", + "delete-from-relations-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 relazione} other {# relazioni} }?", + "delete-from-relations-text": "Attenzione, dopo la conferma tutte le relazioni selezionate saranno rimosse e l'entità corrente non sarà più collegata alle entità corrispondenti.", + "remove-relation-filter": "Rimuovi filtro di relazione", + "remove-filter": "Rimuovi filtro", + "add-relation-filter": "Aggiungi filtro di relazione", + "any-relation": "Qualsiasi relazione", + "relation-filters": "Filtri di relazione", + "additional-info": "Informazioni aggiuntive (JSON)", + "invalid-additional-info": "Impossibile analizzare il JSON delle informazioni aggiuntive.", + "no-relations-text": "Nessuna relazione trovata", + "not": "Non" + }, + "resource": { + "add": "Aggiungi risorsa", + "all-types": "Tutti", + "copyId": "Copia ID risorsa", + "delete": "Elimina risorsa", + "delete-resource-text": "Attenzione, dopo la conferma la risorsa non sarà più recuperabile.", + "delete-resource-title": "Sei sicuro di voler eliminare la risorsa '{{resourceTitle}}'?", + "delete-resources-action-title": "Elimina { count, plural, =1 {1 risorsa} other {# risorse} }", + "delete-resources-text": "Nota: le risorse selezionate, anche se utilizzate nei profili dispositivo, verranno eliminate.", + "delete-resources-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 risorsa} other {# risorse} }?", + "download": "Scarica risorsa", + "drop-file": "Trascina un file risorsa o clicca per selezionarlo da caricare.", + "drop-resource-file-or": "Trascina un file risorsa o", + "empty": "La risorsa è vuota", + "file-name": "Nome file", + "idCopiedMessage": "ID risorsa copiato negli appunti", + "no-resource-matching": "Nessuna risorsa corrispondente a '{{widgetsBundle}}' trovata.", + "no-resource-text": "Nessuna risorsa trovata", + "open-widgets-bundle": "Apri bundle di widget", + "resource": "Risorsa", + "resource-file": "File risorsa", + "resource-files": "File di risorsa", + "resource-library-details": "Dettagli risorsa", + "resource-type": "Tipo di risorsa", + "resources-library": "Libreria risorse", + "search": "Cerca risorse", + "selected-resources": "{ count, plural, =1 {1 risorsa} other {# risorse} } selezionata", + "system": "Sistema", + "title": "Titolo", + "title-required": "Il titolo è obbligatorio.", + "title-max-length": "Il titolo deve essere inferiore a 256 caratteri", + "type": { + "jks": "JKS", + "js-module": "Modulo JS", + "lwm2m-model": "Modello LWM2M", + "pkcs-12": "PKCS #12" + }, + "resource-sub-type": "Sottotipo", + "sub-type": { + "image": "immagine", + "scada-symbol": "Simbolo SCADA", + "extension": "Estensione", + "module": "Modulo" + } + }, + "javascript": { + "add": "Aggiungi risorsa JavaScript", + "delete": "Elimina risorsa JavaScript", + "delete-javascript-resource-text": "Attenzione, dopo la conferma la risorsa JavaScript non sarà più recuperabile.", + "delete-javascript-resource-title": "Sei sicuro di voler eliminare la risorsa JavaScript '{{resourceTitle}}'?", + "delete-javascript-resources-action-title": "Elimina { count, plural, =1 {1 risorsa} other {# risorse} } JavaScript", + "delete-javascript-resources-text": "Nota: le risorse JavaScript selezionate, anche se usate nelle funzioni JavaScript, verranno eliminate.", + "delete-javascript-resources-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 risorsa} other {# risorse} } JavaScript?", + "delete-javascript-resource-in-use-text": "Se desideri comunque eliminare la risorsa JavaScript, clicca sul pulsante Elimina comunque.", + "download": "Scarica risorsa JavaScript", + "upload-from-file": "Carica JavaScript da file", + "resource-file": "File risorsa JavaScript", + "drop-file": "Trascina un file JavaScript o clicca per selezionarlo da caricare.", + "drop-resource-file-or": "Trascina un file JavaScript o", + "javascript-library": "Libreria JavaScript", + "javascript-type": "Tipo JavaScript", + "javascript-resource-details": "Dettagli risorsa JavaScript", + "javascript-resource-is-in-use": "La risorsa JavaScript è utilizzata da altre entità", + "javascript-resources-are-in-use": "Le risorse JavaScript sono utilizzate da altre entità", + "javascript-resource-is-in-use-text": "La risorsa JavaScript '{{title}}' non è stata eliminata perché è utilizzata dalle seguenti entità:", + "javascript-resources-are-in-use-text": "Non tutte le risorse JavaScript sono state eliminate perché sono utilizzate da altre entità.
Puoi visualizzare le entità facendo clic sul pulsante Riferimenti nella riga corrispondente.
Se vuoi comunque eliminarle, selezionale nella tabella sottostante e clicca su Elimina selezionati.", + "search": "Cerca risorse JavaScript", + "selected-javascript-resources": "{ count, plural, =1 {1 risorsa JavaScript} other {# risorse JavaScript} } selezionata", + "no-javascript-resource-text": "Nessuna risorsa JavaScript trovata", + "all-types": "Tutti", + "module-script": "Script modulo" + }, + "rpc": { + "error": { + "target-device-is-not-set": "Dispositivo di destinazione non impostato!", + "invalid-target-entity": "I comandi RPC non sono supportati per l'entità {{entityType}}.", + "failed-to-resolve-target-device": "Impossibile risolvere il dispositivo di destinazione!", + "request-timeout": "Timeout della richiesta", + "rpc-http-error": "Errore: {{status}} - {{statusText}}" + } + }, + "rulechain": { + "rulechain": "Catena di regole", + "rulechain-events": "Eventi della catena di regole", + "rulechains": "Catene di regole", + "root": "Radice", + "delete": "Elimina catena di regole", + "name": "Nome", + "name-required": "Il nome è obbligatorio.", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri", + "description": "Descrizione", + "add": "Aggiungi catena di regole", + "set-root": "Rendi radice la catena di regole", + "set-root-rulechain-title": "Sei sicuro di voler rendere radice la catena di regole '{{ruleChainName}}'?", + "set-root-rulechain-text": "Dopo la conferma, la catena di regole diventerà radice e gestirà tutti i messaggi di trasporto in arrivo.", + "delete-rulechain-title": "Sei sicuro di voler eliminare la catena di regole '{{ruleChainName}}'?", + "delete-rulechain-text": "Attenzione, dopo la conferma la catena di regole e tutti i dati correlati non saranno più recuperabili.", + "delete-rulechains-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 catena di regole} other {# catene di regole} }?", + "delete-rulechains-action-title": "Elimina { count, plural, =1 {1 catena di regole} other {# catene di regole} }", + "delete-rulechains-text": "Attenzione, dopo la conferma tutte le catene di regole selezionate saranno rimosse e tutti i dati correlati non saranno più recuperabili.", + "add-rulechain-text": "Aggiungi nuova catena di regole", + "no-rulechains-text": "Nessuna catena di regole trovata", + "rulechain-details": "Dettagli catena di regole", + "details": "Dettagli", + "events": "Eventi", + "system": "Sistema", + "import": "Importa catena di regole", + "export": "Esporta catena di regole", + "export-failed-error": "Impossibile esportare la catena di regole: {{error}}", + "create-new-rulechain": "Crea nuova catena di regole", + "rulechain-file": "File catena di regole", + "invalid-rulechain-file-error": "Impossibile importare la catena di regole: struttura dati non valida.", + "copyId": "Copia ID catena di regole", + "idCopiedMessage": "ID catena di regole copiato negli appunti", + "select-rulechain": "Seleziona catena di regole", + "no-rulechains-matching": "Nessuna catena di regole corrispondente a '{{entity}}' trovata.", + "rulechain-required": "Catena di regole obbligatoria", + "management": "Gestione regole", + "debug-mode": "Modalità debug", + "search": "Cerca catene di regole", + "selected-rulechains": "{ count, plural, =1 {1 catena di regole} other {# catene di regole} } selezionata", + "open-rulechain": "Apri catena di regole", + "edge-template-root": "Modello radice", + "assign-to-edge": "Assegna all'Edge", + "edge-rulechain": "Catena di regole Edge", + "unassign-rulechain-from-edge-text": "Dopo la conferma la catena di regole sarà disassegnata e non sarà più accessibile dall'edge.", + "unassign-rulechains-from-edge-title": "Sei sicuro di voler disassegnare { count, plural, =1 {1 catena} other {# catene} }?", + "unassign-rulechains-from-edge-text": "Dopo la conferma tutte le catene di regole selezionate saranno disassegnate e non saranno più accessibili dall'edge.", + "assign-rulechain-to-edge-title": "Assegna catena(e) di regole all'Edge", + "assign-rulechain-to-edge-text": "Seleziona le catene di regole da assegnare all'edge", + "set-edge-template-root-rulechain": "Rendi catena di regole come modello radice edge", + "set-edge-template-root-rulechain-title": "Sei sicuro di voler rendere '{{ruleChainName}}' modello radice per l'edge?", + "set-edge-template-root-rulechain-text": "Dopo la conferma, la catena sarà modello radice per gli edge creati successivamente.", + "invalid-rulechain-type-error": "Impossibile importare la catena di regole: tipo non valido. Tipo atteso: {{expectedRuleChainType}}.", + "set-auto-assign-to-edge": "Assegna catena di regole all'edge alla creazione", + "set-auto-assign-to-edge-title": "Vuoi assegnare automaticamente la catena '{{ruleChainName}}' all'edge alla creazione?", + "set-auto-assign-to-edge-text": "Dopo la conferma la catena sarà assegnata automaticamente all'edge alla creazione.", + "unset-auto-assign-to-edge": "Non assegnare catena all'edge alla creazione", + "unset-auto-assign-to-edge-title": "Vuoi disabilitare l'assegnazione automatica della catena '{{ruleChainName}}'?", + "unset-auto-assign-to-edge-text": "Dopo la conferma, la catena non sarà più assegnata automaticamente agli edge in fase di creazione.", + "unassign-rulechain-title": "Sei sicuro di voler disassegnare la catena '{{ruleChainName}}'?", + "unassign-rulechains": "Disassegna catene di regole" + }, + "rulenode": { + "rule-node-events": "Eventi del nodo regola", + "details": "Dettagli", + "events": "Eventi", + "search": "Cerca nodi", + "open-node-library": "Apri libreria nodi", + "close-node-library": "Chiudi libreria nodi", + "add": "Aggiungi nodo regola", + "name": "Nome", + "name-required": "Il nome è obbligatorio.", + "name-max-length": "Il nome deve essere inferiore a 256 caratteri", + "type": "Tipo", + "rule-node-description": "Descrizione nodo regola", + "delete": "Elimina nodo regola", + "select-all-objects": "Seleziona tutti i nodi e le connessioni", + "deselect-all-objects": "Deseleziona tutti i nodi e le connessioni", + "delete-selected-objects": "Elimina nodi e connessioni selezionati", + "delete-selected": "Elimina selezionati", + "create-nested-rulechain": "Crea catena di regole nidificata", + "select-all": "Seleziona tutto", + "copy-selected": "Copia selezionati", + "deselect-all": "Deseleziona tutto", + "rulenode-details": "Dettagli nodo regola", + "debug-mode": "Modalità debug", + "singleton": "Singleton", + "configuration": "Configurazione", + "link": "Collegamento", + "link-details": "Dettagli collegamento nodo regola", + "add-link": "Aggiungi collegamento", + "link-label": "Etichetta collegamento", + "link-label-required": "Etichetta collegamento obbligatoria.", + "custom-link-label": "Etichetta collegamento personalizzata", + "custom-link-label-required": "Etichetta collegamento personalizzata obbligatoria.", + "link-labels": "Etichette collegamento", + "link-labels-required": "Etichette collegamento obbligatorie.", + "no-link-labels-found": "Nessuna etichetta collegamento trovata", + "no-link-label-matching": "'{{label}}' non trovata.", + "create-new-link-label": "Creane una nuova!", + "type-filter": "Filtro", + "type-filter-details": "Filtra i messaggi in ingresso con condizioni configurate", + "type-enrichment": "Arricchimento", + "type-enrichment-details": "Aggiungi informazioni nel Metadata del Messaggio", + "type-transformation": "Trasformazione", + "type-transformation-details": "Modifica il payload e i metadati del Messaggio", + "type-action": "Azione", + "type-action-details": "Esegui un'azione specifica", + "type-external": "Esterno", + "type-external-details": "Interagisce con un sistema esterno", + "type-rule-chain": "Catena di regole", + "type-rule-chain-details": "Inoltra i messaggi in ingresso a una Catena di Regole specificata", + "type-flow": "Flusso", + "type-flow-details": "Organizza il flusso dei messaggi", + "type-input": "Input", + "type-input-details": "Input logico della Catena di Regole, inoltra i messaggi al nodo regola successivo", + "type-unknown": "Sconosciuto", + "type-unknown-details": "Nodo regola non risolto", + "directive-is-not-loaded": "La direttiva '{{directiveName}}' definita non è disponibile.", + "ui-resources-load-error": "Errore nel caricamento delle risorse UI di configurazione.", + "invalid-target-rulechain": "Impossibile risolvere la catena di regole di destinazione!", + "test-script-function": "Test funzione script", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", + "message": "Messaggio", + "message-type": "Tipo di messaggio", + "select-message-type": "Seleziona tipo di messaggio", + "message-type-required": "Tipo di messaggio obbligatorio", + "metadata": "Metadati", + "metadata-required": "Le voci di metadati non possono essere vuote.", + "output": "Output", + "test": "Test", + "help": "Aiuto", + "reset-debug-settings": "Reimposta le impostazioni di debug in tutti i nodi", + "test-with-this-message": "{{test}} con questo messaggio", + "queue-hint": "Seleziona una coda per l'inoltro del messaggio a un'altra coda. La coda 'Main' è usata per impostazione predefinita.", + "queue-singleton-hint": "Seleziona una coda per l'inoltro in ambienti multiistanza. La coda 'Main' è usata per impostazione predefinita." + }, + "rule-node-config": { + "id": "Id", + "additional-info": "Informazioni aggiuntive", + "advanced-settings": "Impostazioni avanzate", + "create-entity-if-not-exists": "Crea una nuova entità se non esiste", + "create-entity-if-not-exists-hint": "Se abilitato, verrà creata una nuova entità con i parametri specificati, a meno che non esista già. Le entità esistenti verranno utilizzate così come sono per la relazione.", + "select-device-connectivity-event": "Seleziona evento di connettività del dispositivo", + "entity-name-pattern": "Modello nome", + "device-name-pattern": "Nome dispositivo", + "asset-name-pattern": "Nome asset", + "entity-view-name-pattern": "Nome vista entità", + "customer-title-pattern": "Titolo cliente", + "dashboard-name-pattern": "Titolo dashboard", + "user-name-pattern": "Email utente", + "edge-name-pattern": "Nome edge", + "entity-name-pattern-required": "Il modello nome è obbligatorio", + "entity-name-pattern-hint": "Il campo supporta la templatizzazione. Usa $[messageKey] per estrarre valore dal messaggio e ${metadataKey} per estrarre valore dai metadati.", + "copy-message-type": "Copia tipo di messaggio", + "entity-type-pattern": "Modello tipo", + "entity-type-pattern-required": "Il modello tipo è obbligatorio", + "message-type-value": "Valore tipo di messaggio", + "message-type-value-required": "Valore tipo di messaggio obbligatorio", + "message-type-value-max-length": "Valore tipo di messaggio deve essere inferiore a 256", + "output-message-type": "Tipo di messaggio in uscita", + "entity-cache-expiration": "Tempo di scadenza cache entità (sec)", + "entity-cache-expiration-hint": "Specifica l’intervallo massimo di tempo consentito per memorizzare le entità trovate. Il valore 0 significa che non scadranno mai.", + "entity-cache-expiration-required": "Tempo di scadenza cache entità obbligatorio.", + "entity-cache-expiration-range": "Il tempo di scadenza deve essere maggiore o uguale a 0.", + "customer-name-pattern": "Titolo cliente", + "customer-name-pattern-required": "Titolo cliente obbligatorio", + "customer-name-pattern-hint": "Usa $[messageKey] per estrarre valore dal messaggio e ${metadataKey} per estrarre valore dai metadati.", + "create-customer-if-not-exists": "Crea un nuovo cliente se non esiste", + "unassign-from-customer": "Rimuovi assegnazione da cliente specifico se l'origine è una dashboard", + "unassign-from-customer-tooltip": "Solo le dashboard possono essere assegnate a più clienti contemporaneamente. \nSe il mittente è una dashboard, è necessario specificare esplicitamente il titolo del cliente da cui rimuovere l'assegnazione.", + "customer-cache-expiration": "Tempo di scadenza cache clienti (sec)", + "customer-cache-expiration-hint": "Specifica l’intervallo massimo di tempo consentito per memorizzare i clienti trovati. Il valore 0 significa che non scadranno mai.", + "customer-cache-expiration-required": "Tempo di scadenza cache clienti obbligatorio.", + "customer-cache-expiration-range": "Il tempo di scadenza deve essere maggiore o uguale a 0.", + "interval-start": "Inizio intervallo", + "interval-end": "Fine intervallo", + "time-unit": "Unità di tempo", + "fetch-mode": "Modalità di recupero", + "order-by-timestamp": "Ordina per timestamp", + "limit": "Limite", + "limit-hint": "Il valore minimo del limite è 2, massimo 1000. Per ottenere una singola voce, seleziona 'First' o 'Last'.", + "limit-required": "Limite obbligatorio.", + "limit-range": "Il limite deve essere compreso tra 2 e 1000.", + "time-unit-milliseconds": "Millisecondi", + "time-unit-seconds": "Secondi", + "time-unit-minutes": "Minuti", + "time-unit-hours": "Ore", + "time-unit-days": "Giorni", + "time-value-range": "Intervallo consentito da 1 a 2147483647.", + "start-interval-value-required": "Inizio intervallo obbligatorio.", + "end-interval-value-required": "Fine intervallo obbligatorio.", + "filter": "Filtro", + "switch": "Interruttore", + "math-templatization-tooltip": "Il campo supporta la templatizzazione. Usa $[messageKey] per estrarre valore dal messaggio e ${metadataKey} per estrarre valore dai metadati.", + "add-message-type": "Aggiungi tipo di messaggio", + "select-message-types-required": "Deve essere selezionato almeno un tipo di messaggio.", + "select-message-types": "Seleziona tipi di messaggio", + "no-message-types-found": "Nessun tipo di messaggio trovato", + "no-message-type-matching": "'{{messageType}}' non trovato.", + "create-new-message-type": "Creane uno nuovo.", + "message-types-required": "Tipi di messaggio obbligatori.", + "client-attributes": "Attributi client", + "shared-attributes": "Attributi condivisi", + "server-attributes": "Attributi server", + "attributes-keys": "Chiavi attributi", + "attributes-keys-required": "Chiavi attributi obbligatorie", + "attributes-scope": "Ambito attributi", + "attributes-scope-value": "Valore ambito attributi", + "attributes-scope-value-copy": "Copia valore ambito attributi", + "attributes-scope-hint": "Usa la chiave 'scope' nei metadati per impostare dinamicamente l'ambito per messaggio. Se presente, sovrascrive l’ambito impostato nella configurazione.", + "notify-device": "Forza notifica al dispositivo", + "send-attributes-updated-notification": "Invia notifica di attributi aggiornati", + "send-attributes-updated-notification-hint": "Invia una notifica degli attributi aggiornati come messaggio separato alla coda del motore di regole.", + "send-attributes-deleted-notification": "Invia notifica di attributi eliminati", + "send-attributes-deleted-notification-hint": "Invia una notifica degli attributi eliminati come messaggio separato alla coda del motore di regole.", + "update-attributes-only-on-value-change": "Salva attributi solo se il valore cambia", + "update-attributes-only-on-value-change-hint": "Aggiorna gli attributi ad ogni messaggio, indipendentemente dal fatto che il valore sia cambiato. Aumenta l'uso delle API e riduce le prestazioni.", + "update-attributes-only-on-value-change-hint-enabled": "Aggiorna gli attributi solo se il valore è cambiato. Se il valore non cambia, non verrà inviato alcun aggiornamento né notifica.", + "fetch-credentials-to-metadata": "Recupera credenziali nei metadati", + "notify-device-on-update-hint": "Se abilitato, forza la notifica al dispositivo dell’aggiornamento di attributi condivisi. Se disabilitato, il comportamento dipende dal parametro 'notifyDevice' nei metadati del messaggio.", + "notify-device-on-delete-hint": "Se abilitato, forza la notifica al dispositivo della rimozione di attributi condivisi. Se disabilitato, il comportamento dipende dal parametro 'notifyDevice' nei metadati.", + "latest-timeseries": "Chiavi serie temporali più recenti", + "timeseries-keys": "Chiavi serie temporali", + "timeseries-keys-required": "Deve essere selezionata almeno una chiave della serie temporale.", + "add-timeseries-key": "Aggiungi chiave della serie temporale", + "add-message-field": "Aggiungi campo del messaggio", + "relation-search-parameters": "Parametri di ricerca relazione", + "relation-parameters": "Parametri relazione", + "add-metadata-field": "Aggiungi campo metadati", + "data-keys": "Nomi dei campi del messaggio", + "copy-from": "Copia da", + "data-to-metadata": "Dati verso metadati", + "metadata-to-data": "Metadati verso dati", + "use-regular-expression-hint": "Utilizza espressioni regolari per copiare le chiavi in base al pattern.\n\nSuggerimenti:\nPremi 'Enter' per confermare il nome del campo.\nPremi 'Backspace' per eliminare il nome del campo. Sono supportati più nomi.", + "interval": "Intervallo", + "interval-required": "Intervallo richiesto", + "interval-hint": "Intervallo di deduplicazione in secondi.", + "interval-min-error": "Valore minimo consentito: 1", + "max-pending-msgs": "Messaggi in attesa massimi", + "max-pending-msgs-hint": "Numero massimo di messaggi memorizzati in memoria per ciascun ID di deduplicazione unico.", + "max-pending-msgs-required": "Messaggi in attesa massimi richiesti", + "max-pending-msgs-max-error": "Valore massimo consentito: 1000", + "max-pending-msgs-min-error": "Valore minimo consentito: 1", + "max-retries": "Massimo tentativi", + "max-retries-required": "Massimo tentativi richiesto", + "max-retries-hint": "Numero massimo di tentativi per inviare i messaggi deduplicati nella coda. Viene utilizzato un ritardo di 10 secondi tra i tentativi", + "max-retries-max-error": "Valore massimo consentito: 100", + "max-retries-min-error": "Valore minimo consentito: 0", + "strategy": "Strategia", + "strategy-required": "Strategia richiesta", + "strategy-all-hint": "Restituisce tutti i messaggi arrivati durante il periodo di deduplicazione come singolo messaggio JSON array. Ogni elemento rappresenta un oggetto con proprietà msg e metadata.", + "strategy-first-hint": "Restituisce il primo messaggio arrivato durante il periodo di deduplicazione.", + "strategy-last-hint": "Restituisce l’ultimo messaggio arrivato durante il periodo di deduplicazione.", + "first": "Primo", + "last": "Ultimo", + "all": "Tutti", + "output-msg-type-hint": "Tipo di messaggio del risultato della deduplicazione.", + "queue-name-hint": "Nome della coda in cui verrà pubblicato il risultato della deduplicazione.", + "keys": "Chiavi", + "keys-required": "Chiavi richieste", + "rename-keys-in": "Rinomina chiavi in", + "data": "Dati", + "message": "Messaggio", + "metadata": "Metadati", + "current-key-name": "Nome chiave attuale", + "key-name-required": "Nome chiave richiesto", + "new-key-name": "Nuovo nome chiave", + "new-key-name-required": "Nuovo nome chiave richiesto", + "metadata-keys": "Nomi dei campi metadati", + "json-path-expression": "Espressione JSON path", + "json-path-expression-required": "Espressione JSON path richiesta", + "json-path-expression-hint": "JSONPath specifica un percorso verso uno o più elementi in una struttura JSON. '$' rappresenta la radice.", + "relations-query": "Query relazioni", + "device-relations-query": "Query relazioni dispositivo", + "max-relation-level": "Livello massimo relazione", + "max-relation-level-error": "Il valore deve essere maggiore di 0 o non specificato.", + "max-relation-level-invalid": "Il valore deve essere un intero.", + "relation-type": "Tipo relazione", + "relation-type-pattern": "Pattern tipo relazione", + "relation-type-pattern-required": "Pattern tipo relazione richiesto", + "relation-types-list": "Tipi relazione da propagare", + "relation-types-list-hint": "Se i tipi di propagazione non sono selezionati, gli allarmi verranno propagati senza filtrare per tipo relazione.", + "unlimited-level": "Livello illimitato", + "latest-telemetry": "Telemetria più recente", + "add-telemetry-key": "Aggiungi chiave telemetria", + "delete-from": "Elimina da", + "use-regular-expression-delete-hint": "Utilizza espressioni regolari per eliminare chiavi per pattern.\n\nSuggerimenti:\nPremi 'Enter' per confermare nome campo.\nPremi 'Backspace' per eliminare nome campo.\nSupportati più nomi.", + "fetch-into": "Recupera in", + "attr-mapping": "Mappatura attributi:", + "source-attribute": "Chiave attributo sorgente", + "source-attribute-required": "Chiave attributo sorgente richiesta.", + "source-telemetry": "Chiave telemetria sorgente", + "source-telemetry-required": "Chiave telemetria sorgente richiesta.", + "target-key": "Chiave di destinazione", + "target-key-required": "Chiave di destinazione richiesta.", + "attr-mapping-required": "Deve essere specificata almeno una voce di mappatura.", + "fields-mapping": "Mappatura campi", + "fields-mapping-hint": "Se il campo messaggio è impostato su $entityId, l'id dell’origine sarà salvato nella colonna specificata.", + "relations-query-config-direction-suffix": "originatore", + "profile-name": "Nome profilo", + "fetch-circle-parameter-info-from-metadata-hint": "Il campo metadati '{{perimeterKeyName}}' deve essere definito con il formato: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "Il campo metadati '{{perimeterKeyName}}' deve essere definito con il formato: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "Usa $[messageKey] per estrarre valore dal messaggio e ${metadataKey} dai metadati.", + "fields-mapping-required": "Deve essere specificata almeno una mappatura di campo.", + "at-least-one-field-required": "Deve essere fornito almeno un valore per un campo di input.", + "originator-fields-sv-map-hint": "I campi chiave di destinazione supportano la templatizzazione. Usa $[messageKey] o ${metadataKey}.", + "sv-map-hint": "Solo i campi chiave di destinazione supportano la templatizzazione. Usa $[messageKey] o ${metadataKey}.", + "source-field": "Campo sorgente", + "source-field-required": "Campo sorgente richiesto.", + "originator-source": "Origine dell’origine", + "new-originator": "Nuovo originatore", + "originator-customer": "Cliente", + "originator-tenant": "Tenant", + "originator-related": "Entità correlata", + "originator-alarm-originator": "Origine dell'allarme", + "originator-entity": "Entità tramite nome", + "clone-message": "Clona messaggio", + "transform": "Trasforma", + "default-ttl": "TTL predefinito", + "default-ttl-required": "TTL predefinito richiesto.", + "default-ttl-hint": "Il nodo regola recupererà il valore Time-to-Live (TTL) dai metadati del messaggio. Se non presente, verrà utilizzato quello specificato nella configurazione. Se impostato su 0, verrà applicato il TTL dalla configurazione del profilo tenant.", + "default-ttl-zero-hint": "Il TTL non verrà applicato se il suo valore è impostato su 0.", + "min-default-ttl-message": "È consentito solo il valore minimo TTL pari a 0.", + "generation-parameters": "Parametri di generazione", + "message-count": "Limite messaggi generati (0 - illimitato)", + "message-count-required": "Limite messaggi generati richiesto.", + "min-message-count-message": "È consentito solo il valore minimo pari a 0.", + "period-seconds": "Periodo in secondi", + "period-seconds-required": "Periodo richiesto.", + "generation-frequency-seconds": "Frequenza generazione in secondi", + "generation-frequency-required": "Frequenza generazione richiesta.", + "min-generation-frequency-message": "È consentito solo un minimo di 60 secondi.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Utilizza il pattern periodo in secondi", + "use-metadata-period-in-seconds-patterns-hint": "Se selezionato, il nodo regola utilizzerà il pattern periodo in secondi dai metadati del messaggio o dai dati, assumendo che gli intervalli siano espressi in secondi.", + "period-in-seconds-pattern": "Pattern periodo in secondi", + "period-in-seconds-pattern-required": "Pattern periodo in secondi richiesto", + "min-period-seconds-message": "È consentito solo un periodo minimo di 60 secondi.", + "originator": "Originatore", + "message-body": "Corpo del messaggio", + "message-metadata": "Metadati del messaggio", + "generate": "Genera", + "current-rule-node": "Nodo regola corrente", + "current-tenant": "Tenant corrente", + "generator-function": "Funzione generatrice", + "test-generator-function": "Test funzione generatrice", + "generator": "Generatore", + "test-filter-function": "Test funzione filtro", + "test-switch-function": "Test funzione switch", + "test-transformer-function": "Test funzione trasformazione", + "transformer": "Trasformatore", + "alarm-create-condition": "Condizione di creazione allarme", + "test-condition-function": "Test funzione condizione", + "alarm-clear-condition": "Condizione di cancellazione allarme", + "alarm-details-builder": "Generatore dettagli allarme", + "test-details-function": "Test funzione dettagli", + "alarm-type": "Tipo di allarme", + "select-entity-types": "Seleziona tipi entità", + "alarm-type-required": "Tipo di allarme richiesto.", + "alarm-severity": "Gravità dell’allarme", + "alarm-severity-required": "Gravità dell’allarme richiesta", + "alarm-severity-pattern": "Pattern gravità dell’allarme", + "alarm-status-filter": "Filtro stato allarme", + "alarm-status-list-empty": "Elenco stato allarme vuoto", + "no-alarm-status-matching": "Nessuna corrispondenza stato allarme trovata.", + "propagate": "Propaga allarme alle entità correlate", + "propagate-to-owner": "Propaga allarme al proprietario dell'entità (Cliente o Tenant)", + "propagate-to-tenant": "Propaga allarme al Tenant", + "condition": "Condizione", + "details": "Dettagli", + "to-string": "Converti in stringa", + "test-to-string-function": "Test funzione di conversione in stringa", + "from-template": "Da", + "from-template-required": "Campo 'Da' richiesto", + "message-to-metadata": "Messaggio a metadati", + "metadata-to-message": "Metadati a messaggio", + "from-message": "Dal messaggio", + "from-metadata": "Dai metadati", + "to-template": "A", + "to-template-required": "Campo 'A' richiesto", + "mail-address-list-template-hint": "Elenco di indirizzi separati da virgole. Usa ${metadataKey} per valori dai metadati, $[messageKey] per valori dal corpo del messaggio", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "Oggetto", + "subject-template-required": "Oggetto richiesto", + "body-template": "Corpo", + "body-template-required": "Corpo richiesto", + "dynamic-mail-body-type": "Tipo dinamico corpo email", + "mail-body-type": "Tipo corpo email", + "body-type-template": "Template tipo corpo", + "reply-routing-configuration": "Configurazione routing di risposta", + "rpc-reply-routing-configuration-hint": "Queste configurazioni specificano i nomi delle chiavi nei metadati per identificare servizio, sessione e richiesta per la risposta.", + "reply-routing-configuration-hint": "Queste configurazioni specificano i nomi delle chiavi nei metadati per identificare servizio e richiesta per la risposta.", + "request-id-metadata-attribute": "Id richiesta", + "service-id-metadata-attribute": "Id servizio", + "session-id-metadata-attribute": "Id sessione", + "timeout-sec": "Timeout in secondi", + "timeout-required": "Timeout richiesto", + "min-timeout-message": "È consentito solo un valore minimo di 0 per il timeout.", + "endpoint-url-pattern": "Pattern URL endpoint", + "endpoint-url-pattern-required": "Pattern URL endpoint richiesto", + "request-method": "Metodo richiesta", + "use-simple-client-http-factory": "Utilizza client HTTP semplice", + "ignore-request-body": "Senza corpo richiesta", + "parse-to-plain-text": "Analizza come testo semplice", + "parse-to-plain-text-hint": "Se selezionato, il payload del corpo del messaggio della richiesta verrà trasformato da stringa JSON a testo semplice, ad esempio msg = \"Hello,\\t\"world\"\" verrà analizzato come Hello, \"world\"", + "read-timeout": "Timeout di lettura in millisecondi", + "read-timeout-hint": "Il valore 0 significa timeout infinito", + "max-parallel-requests-count": "Numero massimo di richieste parallele", + "max-parallel-requests-count-hint": "Il valore 0 specifica nessun limite nell'elaborazione parallela", + "max-response-size": "Dimensione massima della risposta (in KB)", + "max-response-size-hint": "Quantità massima di memoria allocata per il buffering dei dati durante la decodifica o codifica di messaggi HTTP, come payload JSON o XML", + "headers": "Intestazioni", + "headers-hint": "Usa ${metadataKey} per ottenere un valore dai metadati, $[messageKey] per ottenere un valore dal corpo del messaggio nei campi intestazione/valore", + "header": "Intestazione", + "header-required": "Intestazione richiesto", + "value": "Valore", + "value-required": "Valore richiesto", + "topic-pattern": "Pattern del topic", + "key-pattern": "Pattern della chiave", + "key-pattern-hint": "Opzionale. Se viene specificato un numero di partizione valido, verrà utilizzato per l'invio del record. Se non è specificata alcuna partizione, verrà usata la chiave. Se nessuno dei due è specificato, una partizione verrà assegnata in modo round-robin.", + "topic-pattern-required": "Pattern del topic richiesto", + "topic": "Topic", + "topic-required": "Topic richiesto", + "bootstrap-servers": "Server bootstrap", + "bootstrap-servers-required": "Valore dei server bootstrap richiesto", + "other-properties": "Altre proprietà", + "key": "Chiave", + "key-required": "Chiave richiesta", + "retries": "Numero di tentativi automatici in caso di errore", + "min-retries-message": "È consentito solo un valore minimo di 0 per i tentativi.", + "batch-size-bytes": "Dimensione del batch di produzione in byte", + "min-batch-size-bytes-message": "È consentito solo un valore minimo di 0 per la dimensione del batch.", + "linger-ms": "Tempo di buffer locale (ms)", + "min-linger-ms-message": "È consentito solo un valore minimo di 0 ms.", + "buffer-memory-bytes": "Dimensione massima del buffer client in byte", + "min-buffer-memory-message": "È consentito solo un valore minimo di 0 per il buffer.", + "memory-buffer-size-range": "La dimensione del buffer deve essere tra 0 e {{max}} KB", + "acks": "Numero di conferme", + "topic-arn-pattern": "Pattern ARN del topic", + "topic-arn-pattern-required": "Pattern ARN del topic richiesto", + "aws-access-key-id": "ID chiave di accesso AWS", + "aws-access-key-id-required": "ID chiave di accesso AWS richiesto", + "aws-secret-access-key": "Chiave di accesso segreta AWS", + "aws-secret-access-key-required": "Chiave di accesso segreta AWS richiesta", + "aws-region": "Regione AWS", + "aws-region-required": "Regione AWS richiesta", + "exchange-name-pattern": "Pattern del nome dell’exchange", + "routing-key-pattern": "Pattern della chiave di routing", + "message-properties": "Proprietà del messaggio", + "host": "Host", + "host-required": "Host richiesto", + "port": "Porta", + "port-required": "Porta richiesta", + "port-range": "La porta deve essere compresa tra 1 e 65535.", + "virtual-host": "Host virtuale", + "username": "Nome utente", + "password": "Password", + "automatic-recovery": "Ripristino automatico", + "connection-timeout-ms": "Timeout di connessione (ms)", + "min-connection-timeout-ms-message": "È consentito solo un valore minimo di 0 ms.", + "handshake-timeout-ms": "Timeout handshake (ms)", + "min-handshake-timeout-ms-message": "È consentito solo un valore minimo di 0 ms.", + "client-properties": "Proprietà client", + "queue-url-pattern": "Pattern URL della coda", + "queue-url-pattern-required": "Pattern URL della coda richiesto", + "delay-seconds": "Ritardo (secondi)", + "min-delay-seconds-message": "È consentito solo un valore minimo di 0 secondi.", + "max-delay-seconds-message": "È consentito solo un valore massimo di 900 secondi.", + "name": "Nome", + "name-required": "Nome richiesto", + "queue-type": "Tipo di coda", + "sqs-queue-standard": "Standard", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "ID progetto GCP", + "gcp-project-id-required": "ID progetto GCP richiesto", + "gcp-service-account-key": "File chiave account di servizio GCP", + "gcp-service-account-key-required": "File chiave account di servizio GCP richiesto", + "pubsub-topic-name": "Nome topic", + "pubsub-topic-name-required": "Nome topic richiesto", + "message-attributes": "Attributi del messaggio", + "message-attributes-hint": "Usa ${metadataKey} per il valore dai metadati, $[messageKey] per il valore dal corpo del messaggio nei campi nome/valore", + "connect-timeout": "Timeout di connessione (sec)", + "connect-timeout-required": "Timeout di connessione richiesto.", + "connect-timeout-range": "Il timeout di connessione deve essere compreso tra 1 e 200.", + "client-id": "ID client", + "client-id-hint": "Opzionale. Lasciare vuoto per generare automaticamente l'ID client. Attenzione quando si specifica l'ID client. La maggior parte dei broker MQTT non consente più connessioni con lo stesso ID client. Per connettersi a tali broker, l'ID client MQTT deve essere univoco. Quando la piattaforma è in modalità micro-servizi, una copia del nodo regola viene avviata in ciascun micro-servizio, il che porterà automaticamente a più client MQTT con lo stesso ID e potrebbe causare errori del nodo regola. Per evitare tali errori, abilitare l'opzione \"Aggiungi ID Servizio come suffisso all'ID Client\" qui sotto.", + "append-client-id-suffix": "Aggiungi ID Servizio come suffisso all'ID Client", + "client-id-suffix-hint": "Opzionale. Applicato quando \"ID Client\" è specificato esplicitamente. Se selezionato, l'ID Servizio sarà aggiunto all'ID Client come suffisso. Aiuta a evitare errori quando la piattaforma è in modalità micro-servizi.", + "device-id": "ID Dispositivo", + "device-id-required": "ID Dispositivo richiesto.", + "clean-session": "Sessione pulita", + "enable-ssl": "Abilita SSL", + "credentials": "Credenziali", + "credentials-type": "Tipo di credenziali", + "credentials-type-required": "Tipo di credenziali richiesto.", + "credentials-anonymous": "Anonimo", + "credentials-basic": "Base", + "credentials-pem": "PEM", + "credentials-pem-hint": "È richiesto almeno il file del certificato CA del server o una coppia di file certificato e chiave privata del client", + "credentials-sas": "Firma di accesso condivisa", + "sas-key": "Chiave SAS", + "sas-key-required": "Chiave SAS richiesta.", + "hostname": "Nome host", + "hostname-required": "Nome host richiesto.", + "azure-ca-cert": "File certificato CA", + "username-required": "Nome utente richiesto.", + "password-required": "Password richiesta.", + "ca-cert": "File certificato CA del server", + "private-key": "File chiave privata del client", + "cert": "File certificato del client", + "no-file": "Nessun file selezionato.", + "drop-file": "Trascina un file o clicca per selezionarlo da caricare.", + "private-key-password": "Password della chiave privata", + "use-system-smtp-settings": "Usa impostazioni SMTP di sistema", + "use-metadata-dynamic-interval": "Usa intervallo dinamico", + "metadata-dynamic-interval-hint": "I campi di input dell'intervallo iniziale e finale supportano la templatizzazione. Il valore del template sostituito deve essere impostato in millisecondi. Usa $[messageKey] per estrarre il valore dal messaggio e ${metadataKey} per estrarre il valore dai metadati.", + "use-metadata-interval-patterns-hint": "Se selezionato, il nodo regola utilizza i pattern dell'intervallo iniziale e finale dai metadati o dati del messaggio, assumendo che siano in millisecondi.", + "use-message-alarm-data": "Usa i dati dell'allarme dal messaggio", + "overwrite-alarm-details": "Sovrascrivi i dettagli dell'allarme", + "use-alarm-severity-pattern": "Usa pattern per la severità dell'allarme", + "check-all-keys": "Verifica che tutti i campi specificati siano presenti", + "check-all-keys-hint": "Se selezionato, controlla che tutte le chiavi specificate siano presenti nei dati e metadati del messaggio.", + "check-relation-to-specific-entity": "Verifica relazione con entità specifica", + "check-relation-to-specific-entity-tooltip": "Se abilitato, verifica la presenza di una relazione con una specifica entità, altrimenti con qualsiasi entità. In entrambi i casi, la ricerca della relazione si basa su direzione e tipo configurati.", + "check-relation-hint": "Verifica l'esistenza di una relazione con entità specifica o con qualsiasi entità in base a direzione e tipo.", + "delete-relation-with-specific-entity": "Elimina relazione con entità specifica", + "delete-relation-with-specific-entity-hint": "Se abilitato, eliminerà solo la relazione con una specifica entità. Altrimenti, verrà rimossa con tutte le entità corrispondenti.", + "delete-relation-hint": "Elimina la relazione dall’origine del messaggio in arrivo all’entità specificata o alla lista di entità basata su direzione e tipo.", + "remove-current-relations": "Rimuovi relazioni attuali", + "remove-current-relations-hint": "Rimuove le relazioni attuali dall’origine del messaggio in arrivo in base a direzione e tipo.", + "change-originator-to-related-entity": "Cambia origine con entità correlata", + "change-originator-to-related-entity-hint": "Utilizzato per elaborare un messaggio come proveniente da un'altra entità.", + "start-interval": "Inizio intervallo", + "end-interval": "Fine intervallo", + "start-interval-required": "Inizio intervallo richiesto.", + "end-interval-required": "Fine intervallo richiesto.", + "smtp-protocol": "Protocollo", + "smtp-host": "Host SMTP", + "smtp-host-required": "Host SMTP richiesto.", + "smtp-port": "Porta SMTP", + "smtp-port-required": "Devi specificare una porta SMTP.", + "smtp-port-range": "La porta SMTP deve essere compresa tra 1 e 65535.", + "timeout-msec": "Timeout (ms)", + "min-timeout-msec-message": "È consentito solo un valore minimo di 0 ms.", + "enter-username": "Inserisci nome utente", + "enter-password": "Inserisci password", + "enable-tls": "Abilita TLS", + "tls-version": "Versione TLS", + "enable-proxy": "Abilita proxy", + "use-system-proxy-properties": "Usa proprietà proxy di sistema", + "proxy-host": "Host proxy", + "proxy-host-required": "Host proxy richiesto.", + "proxy-port": "Porta proxy", + "proxy-port-required": "Porta proxy richiesta.", + "proxy-port-range": "La porta proxy deve essere compresa tra 1 e 65535.", + "proxy-user": "Utente proxy", + "proxy-password": "Password proxy", + "proxy-scheme": "Schema proxy", + "numbers-to-template": "Numeri di telefono al template", + "numbers-to-template-required": "Numeri di telefono al template richiesti", + "numbers-to-template-hint": "Numeri di telefono separati da virgola, usa ${metadataKey} per il valore dai metadati, $[messageKey] per il valore dal corpo del messaggio", + "sms-message-template": "Template messaggio SMS", + "sms-message-template-required": "Template messaggio SMS richiesto", + "use-system-sms-settings": "Usa impostazioni provider SMS di sistema", + "min-period-0-seconds-message": "È consentito solo un periodo minimo di 0 secondi.", + "max-pending-messages": "Messaggi in attesa massimi", + "max-pending-messages-required": "Messaggi in attesa massimi richiesti.", + "max-pending-messages-range": "Messaggi in attesa massimi devono essere compresi tra 1 e 100000.", + "originator-types-filter": "Filtro tipi originatore", + "interval-seconds": "Intervallo in secondi", + "interval-seconds-required": "Intervallo richiesto.", + "int-range": "Il valore non deve superare il limite massimo di un intero (2147483648)", + "min-interval-seconds-message": "È consentito solo un intervallo minimo di 1 secondo.", + "output-timeseries-key-prefix": "Prefisso chiave serie temporale in uscita", + "output-timeseries-key-prefix-required": "Prefisso chiave serie temporale in uscita richiesto.", + "separator-hint": "Premere \"Invio\" per completare l'inserimento del campo.", + "select-details": "Seleziona dettagli", + "entity-details-id": "Id", + "entity-details-title": "Titolo", + "entity-details-country": "Paese", + "entity-details-state": "Stato", + "entity-details-city": "Città", + "entity-details-zip": "CAP", + "entity-details-address": "Indirizzo", + "entity-details-address2": "Indirizzo 2", + "entity-details-additional_info": "Informazioni aggiuntive", + "entity-details-phone": "Telefono", + "entity-details-email": "Email", + "email-sender": "Mittente email", + "fields-to-check": "Campi da verificare", + "add-detail": "Aggiungi dettaglio", + "check-all-keys-tooltip": "Se abilitato, verifica la presenza di tutti i campi elencati nei nomi dei campi del messaggio e dei metadati all'interno del messaggio in arrivo e dei suoi metadati.", + "fields-to-check-hint": "Premi \"Invio\" per completare l'inserimento del nome campo. Supportati più nomi campo.", + "entity-details-list-empty": "Almeno un dettaglio deve essere selezionato.", + "alarm-status": "Stato allarme", + "alarm-required": "Almeno uno stato di allarme deve essere selezionato.", + "no-entity-details-matching": "Nessun dettaglio entità corrispondente trovato.", + "custom-table-name": "Nome tabella personalizzato", + "custom-table-name-required": "Il nome della tabella è richiesto", + "custom-table-hint": "La tabella deve essere creata nel tuo cluster Cassandra e il suo nome deve iniziare con il prefisso 'cs_tb_' per evitare l'inserimento dei dati nelle tabelle TB comuni. Inserisci qui il nome della tabella senza il prefisso 'cs_tb_'.", + "message-field": "Campo messaggio", + "message-field-required": "Campo messaggio richiesto.", + "table-col": "Colonna tabella", + "table-col-required": "Colonna tabella richiesta.", + "latitude-field-name": "Nome campo latitudine", + "longitude-field-name": "Nome campo longitudine", + "latitude-field-name-required": "Nome campo latitudine richiesto.", + "longitude-field-name-required": "Nome campo longitudine richiesto.", + "fetch-perimeter-info-from-metadata": "Recupera informazioni perimetro dai metadati", + "fetch-perimeter-info-from-metadata-tooltip": "Se il tipo di perimetro è 'Poligono', il valore del campo metadati '{{perimeterKeyName}}' sarà usato come definizione del perimetro senza ulteriori analisi. Se è 'Cerchio', il valore verrà analizzato per estrarre i campi 'latitude', 'longitude', 'radius', 'radiusUnit' per la definizione del cerchio.", + "perimeter-key-name": "Nome chiave perimetro", + "perimeter-key-name-hint": "Nome campo metadati contenente le informazioni sul perimetro.", + "perimeter-key-name-required": "Nome chiave perimetro richiesto.", + "perimeter-circle": "Cerchio", + "perimeter-polygon": "Poligono", + "perimeter-type": "Tipo di perimetro", + "circle-center-latitude": "Latitudine centro", + "circle-center-latitude-required": "Latitudine centro richiesta.", + "circle-center-longitude": "Longitudine centro", + "circle-center-longitude-required": "Longitudine centro richiesta.", + "range-unit-meter": "Metro", + "range-unit-kilometer": "Chilometro", + "range-unit-foot": "Piede", + "range-unit-mile": "Miglio", + "range-unit-nautical-mile": "Miglio nautico", + "range-units": "Unità di misura distanza", + "range-units-required": "Unità di misura distanza richiesta.", + "range": "Raggio", + "range-required": "Raggio richiesto.", + "polygon-definition": "Definizione poligono", + "polygon-definition-required": "Definizione poligono richiesta.", + "polygon-definition-hint": "Usa il seguente formato per la definizione manuale del poligono: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration": "Durata minima all'interno", + "min-inside-duration-value-required": "Durata minima all'interno richiesta", + "min-inside-duration-time-unit": "Unità di tempo durata interna", + "min-outside-duration": "Durata minima all'esterno", + "min-outside-duration-value-required": "Durata minima all'esterno richiesta", + "min-outside-duration-time-unit": "Unità di tempo durata esterna", + "tell-failure-if-absent": "Segnala fallimento", + "tell-failure-if-absent-hint": "Se almeno una delle chiavi selezionate non esiste, il messaggio in uscita riporterà \"Fallimento\".", + "get-latest-value-with-ts": "Recupera timestamp per gli ultimi valori di telemetria", + "get-latest-value-with-ts-hint": "Se selezionato, i valori più recenti della telemetria includeranno anche il timestamp, es: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings": "Ignora stringhe nulle", + "ignore-null-strings-hint": "Se selezionato, il nodo regola ignorerà i campi entità con valore vuoto.", + "add-metadata-key-values-as-kafka-headers": "Aggiungi metadati come header Kafka", + "add-metadata-key-values-as-kafka-headers-hint": "Se selezionato, le coppie chiave-valore dei metadati verranno aggiunte agli header dei record Kafka in uscita come array di byte con codifica charset predefinita.", + "charset-encoding": "Codifica charset", + "charset-encoding-required": "Codifica charset richiesta.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "Il nome della coda può essere selezionato da un elenco a discesa o inserito personalizzato.", + "device-profile-node-hint": "Utile se hai condizioni di durata o ripetizione per garantire la continuità della valutazione dello stato dell'allarme.", + "persist-alarm-rules": "Memorizza stato delle regole allarme", + "persist-alarm-rules-hint": "Se abilitato, il nodo regola salverà lo stato dell'elaborazione nel database.", + "fetch-alarm-rules": "Recupera stato delle regole allarme", + "fetch-alarm-rules-hint": "Se abilitato, il nodo regola ripristinerà lo stato dell'elaborazione all'inizializzazione e garantirà che gli allarmi vengano generati anche dopo riavvii del server.", + "input-value-key": "Chiave valore di input", + "input-value-key-required": "Chiave del valore di input richiesta.", + "output-value-key": "Chiave del valore di output", + "output-value-key-required": "Chiave del valore di output richiesta.", + "number-of-digits-after-floating-point": "Numero di cifre dopo il punto decimale", + "number-of-digits-after-floating-point-range": "Il numero di cifre dopo il punto decimale deve essere compreso tra 0 e 15.", + "failure-if-delta-negative": "Segnala errore se il delta è negativo", + "failure-if-delta-negative-tooltip": "Il nodo regola forza l'errore del messaggio se il valore delta è negativo.", + "use-caching": "Usa caching", + "use-caching-tooltip": "Il nodo regola memorizzerà nella cache il valore di \"{{inputValueKey}}\" proveniente dal messaggio in arrivo per migliorare le prestazioni. La cache non verrà aggiornata se il valore di \"{{inputValueKey}}\" viene modificato altrove.", + "add-time-difference-between-readings": "Aggiungi la differenza di tempo tra le letture di \"{{inputValueKey}}\"", + "add-time-difference-between-readings-tooltip": "Se abilitato, il nodo regola aggiungerà \"{{periodValueKey}}\" al messaggio in uscita.", + "period-value-key": "Chiave del valore del periodo", + "period-value-key-required": "Chiave del valore del periodo richiesta.", + "general-pattern-hint": "Usa ${metadataKey} per il valore dai metadati, $[messageKey] per il valore dal corpo del messaggio.", + "alarm-severity-pattern-hint": "Usa ${metadataKey} per il valore dai metadati, $[messageKey] per il valore dal corpo del messaggio. La severità dell'allarme dovrebbe essere di sistema (CRITICAL, MAJOR ecc.)", + "output-node-name-hint": "Il nome del nodo regola corrisponde al tipo di relazione del messaggio in uscita ed è utilizzato per inoltrare i messaggi ad altri nodi regola nella catena di regole chiamante.", + "use-server-ts": "Usa timestamp del server", + "use-server-ts-hint": "Utilizza il timestamp corrente del server per i dati della serie temporale che non hanno timestamp esplicito. Utile per mantenere l'ordine dei messaggi da fonti multiple o fuori sequenza.", + "kv-map-pattern-hint": "Tutti i campi di input supportano la templatizzazione. Usa $[messageKey] per estrarre il valore dal messaggio e ${metadataKey} per estrarlo dai metadati.", + "kv-map-single-pattern-hint": "Il campo di input supporta la templatizzazione. Usa $[messageKey] per estrarre il valore dal messaggio e ${metadataKey} per estrarlo dai metadati.", + "shared-scope": "Ambito condiviso", + "server-scope": "Ambito server", + "client-scope": "Ambito client", + "attribute-type": "Attributo", + "attribute-type-description": "Recupera il valore dell'attributo dal database", + "attribute-type-result-description": "Memorizza il risultato come attributo dell'entità nel database", + "constant-type": "Costante", + "constant-type-description": "Definisce un valore costante", + "time-series-type": "Serie temporale", + "time-series-type-description": "Recupera l'ultimo valore della serie temporale dal database", + "time-series-type-result-description": "Memorizza il risultato come serie temporale dell'entità nel database", + "message-body-type": "Messaggio", + "message-body-type-description": "Recupera il valore dell'argomento dal messaggio in arrivo", + "message-body-type-result-description": "Aggiunge il risultato al messaggio in uscita", + "message-metadata-type": "Metadati", + "message-metadata-type-description": "Recupera il valore dell'argomento dai metadati del messaggio", + "message-metadata-result-description": "Aggiunge il risultato ai metadati del messaggio in uscita", + "argument-tile": "Argomenti", + "no-arguments-prompt": "Nessun argomento configurato", + "result-title": "Risultato", + "functions-field-input": "Funzioni", + "no-option-found": "Nessuna opzione trovata", + "argument-source-field-input": "Origine", + "argument-source-field-input-required": "Origine dell'argomento richiesta.", + "argument-key-field-input": "Chiave", + "argument-key-field-input-required": "Chiave dell'argomento richiesta.", + "constant-value-field-input": "Valore costante", + "constant-value-field-input-required": "Valore costante richiesto.", + "attribute-scope-field-input": "Ambito attributo", + "attribute-scope-field-input-required": "Ambito attributo richiesto.", + "default-value-field-input": "Valore predefinito", + "type-field-input": "Tipo", + "type-field-input-required": "Tipo richiesto.", + "key-field-input": "Chiave", + "add-entity-type": "Aggiungi tipo di entità", + "add-device-profile": "Aggiungi profilo dispositivo", + "key-field-input-required": "Chiave richiesta.", + "number-floating-point-field-input": "Numero di cifre dopo la virgola", + "number-floating-point-field-input-hint": "Usa 0 per convertire il risultato in intero", + "add-to-message-field-input": "Aggiungi al messaggio", + "add-to-metadata-field-input": "Aggiungi ai metadati", + "custom-expression-field-input": "Espressione matematica", + "custom-expression-field-input-required": "Espressione matematica richiesta", + "custom-expression-field-input-hint": "Specifica un'espressione matematica da valutare. L'espressione predefinita mostra la conversione da Fahrenheit a Celsius", + "retained-message": "Messaggio mantenuto", + "attributes-mapping": "Mappatura attributi", + "latest-telemetry-mapping": "Mappatura ultima telemetria", + "add-mapped-attribute-to": "Aggiungi attributi mappati a", + "add-mapped-latest-telemetry-to": "Aggiungi ultima telemetria mappata a", + "add-mapped-fields-to": "Aggiungi campi mappati a", + "add-selected-details-to": "Aggiungi dettagli selezionati a", + "clear-selected-types": "Pulisci tipi selezionati", + "clear-selected-details": "Pulisci dettagli selezionati", + "clear-selected-fields": "Pulisci campi selezionati", + "clear-selected-keys": "Pulisci chiavi selezionate", + "geofence-configuration": "Configurazione geofence", + "coordinate-field-names": "Nomi dei campi di coordinate", + "coordinate-field-hint": "Il nodo regola cerca di recuperare i campi specificati dal messaggio. Se non presenti, li cerca nei metadati.", + "presence-monitoring-strategy": "Strategia di monitoraggio della presenza", + "presence-monitoring-strategy-on-first-message": "Al primo messaggio", + "presence-monitoring-strategy-on-each-message": "A ogni messaggio", + "presence-monitoring-strategy-on-first-message-hint": "Segnala stato 'Dentro' o 'Fuori' al primo messaggio dopo che è trascorso il tempo minimo dalla precedente modifica dello stato di presenza.", + "presence-monitoring-strategy-on-each-message-hint": "Segnala stato 'Dentro' o 'Fuori' a ogni messaggio dopo una modifica dello stato di presenza.", + "fetch-credentials-to": "Recupera credenziali in", + "add-originator-attributes-to": "Aggiungi attributi dell'origine a", + "originator-attributes": "Attributi dell'origine", + "fetch-latest-telemetry-with-timestamp": "Recupera ultima telemetria con timestamp", + "fetch-latest-telemetry-with-timestamp-tooltip": "Se selezionato, i valori più recenti della telemetria verranno aggiunti ai metadati in uscita con timestamp, ad es: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure": "Segnala errore se mancano attributi", + "tell-failure-tooltip": "Se almeno una chiave selezionata non esiste, il messaggio in uscita riporterà \"Errore\".", + "created-time": "Ora di creazione", + "chip-help": "Premi 'Invio' per completare l'inserimento di {{inputName}}. \nPremi 'Backspace' per cancellare {{inputName}}. \nSupportati valori multipli.", + "detail": "dettaglio", + "field-name": "Nome campo", + "device-profile": "Profilo dispositivo", + "entity-type": "Tipo di entità", + "message-type": "Tipo di messaggio", + "timeseries-key": "Chiave serie temporale", + "type": "Tipo", + "first-name": "Nome", + "last-name": "Cognome", + "label": "Etichetta", + "originator-fields-mapping": "Mappatura campi dell'origine", + "add-mapped-originator-fields-to": "Aggiungi campi dell'origine mappati a", + "fields": "Campi", + "skip-empty-fields": "Ignora campi vuoti", + "skip-empty-fields-tooltip": "I campi con valori vuoti non verranno aggiunti al messaggio/metadati in uscita.", + "fetch-interval": "Intervallo di recupero", + "fetch-strategy": "Strategia di recupero", + "fetch-timeseries-from-to": "Recupera serie temporale da {{startInterval}} {{startIntervalTimeUnit}} fa a {{endInterval}} {{endIntervalTimeUnit}} fa.", + "fetch-timeseries-from-to-invalid": "Recupero serie temporale non valido (\"Intervallo iniziale\" deve essere inferiore a \"Intervallo finale\").", + "use-metadata-dynamic-interval-tooltip": "Se selezionato, il nodo regola utilizzerà un intervallo dinamico basato su modelli di messaggio e metadati.", + "all-mode-hint": "Se selezionata la modalità 'Tutto', il nodo regola recupererà la telemetria dall'intervallo di recupero con parametri configurabili.", + "first-mode-hint": "Se selezionata la modalità 'Primo', il nodo regola recupererà la telemetria più vicina all'inizio dell'intervallo.", + "last-mode-hint": "Se selezionata la modalità 'Ultimo', il nodo regola recupererà la telemetria più vicina alla fine dell'intervallo.", + "ascending": "Ascendente", + "descending": "Discendente", + "min": "Minimo", + "max": "Massimo", + "average": "Media", + "sum": "Somma", + "count": "Conteggio", + "none": "Nessuno", + "last-level-relation-tooltip": "Se selezionato, verranno cercate entità correlate solo al livello massimo configurato.", + "last-level-device-relation-tooltip": "Se selezionato, verranno cercati dispositivi correlati solo al livello massimo configurato.", + "data-to-fetch": "Dati da recuperare", + "mapping-of-customers": "Mappatura del cliente", + "map-fields-required": "Tutti i campi di mappatura sono richiesti.", + "attributes": "Attributi", + "related-device-attributes": "Attributi del dispositivo correlato", + "add-selected-attributes-to": "Aggiungi attributi selezionati a", + "device-profiles": "Profili dispositivi", + "mapping-of-tenant": "Mappatura del tenant", + "add-attribute-key": "Aggiungi chiave attributo", + "message-template": "Template del messaggio", + "message-template-required": "Il template del messaggio è richiesto", + "use-system-slack-settings": "Usa impostazioni di sistema per Slack", + "slack-api-token": "Token API di Slack", + "slack-api-token-required": "Token API di Slack richiesto", + "keys-mapping": "Mappatura chiavi", + "add-key": "Aggiungi chiave", + "recipients": "Destinatari", + "message-subject-and-content": "Oggetto e contenuto del messaggio", + "template-rules-hint": "Entrambi i campi supportano la templatizzazione. Usa $[messageKey] per estrarre dal messaggio e ${metadataKey} dai metadati.", + "originator-customer-desc": "Usa il cliente dell'origine come nuovo originatore.", + "originator-tenant-desc": "Usa il tenant attuale come nuovo originatore.", + "originator-related-entity-desc": "Usa un'entità correlata come nuovo originatore. Ricerca basata su tipo e direzione della relazione.", + "originator-alarm-originator-desc": "Usa l'origine dell'allarme come nuovo originatore. Solo se l'origine del messaggio è un'entità di allarme.", + "originator-entity-by-name-pattern-desc": "Usa l'entità recuperata dal DB come nuovo originatore. Ricerca basata su tipo e modello di nome.", + "email-from-template-hint": "Usa $[messageKey] per estrarre dal messaggio e ${metadataKey} dai metadati.", + "recipients-block-main-hint": "Lista di indirizzi separati da virgole. Tutti i campi supportano la templatizzazione.", + "forward-msg-default-rule-chain": "Inoltra messaggio alla regola predefinita dell'origine", + "forward-msg-default-rule-chain-tooltip": "Se abilitato, il messaggio sarà inoltrato alla regola predefinita dell'origine o a quella configurata.", + "exclude-zero-deltas": "Escludi delta zero dal messaggio in uscita", + "exclude-zero-deltas-hint": "Se abilitato, la chiave \"{{outputValueKey}}\" sarà aggiunta solo se il valore non è zero.", + "exclude-zero-deltas-time-difference-hint": "Se abilitato, le chiavi \"{{outputValueKey}}\" e \"{{periodValueKey}}\" saranno aggiunte solo se \"{{outputValueKey}}\" non è zero.", + "search-direction-from": "Dall'origine all'entità di destinazione", + "search-direction-to": "Dall'entità di destinazione all'origine", + "del-relation-direction-from": "Dall'origine", + "del-relation-direction-to": "All'origine", + "target-entity": "Entità di destinazione", + "function-configuration": "Configurazione funzione", + "function-name": "Nome funzione", + "function-name-required": "Nome funzione richiesto.", + "qualifier": "Qualificatore", + "qualifier-hint": "Se non specificato, verrà usato \"$LATEST\".", + "aws-credentials": "Credenziali AWS", + "connection-timeout": "Timeout connessione", + "connection-timeout-required": "Timeout connessione richiesto.", + "connection-timeout-min": "Timeout minimo connessione è 0.", + "connection-timeout-hint": "Tempo massimo in secondi per stabilire connessione. 0 significa infinito (sconsigliato).", + "request-timeout": "Timeout richiesta", + "request-timeout-required": "Timeout richiesta richiesto.", + "request-timeout-min": "Timeout minimo richiesta è 0", + "request-timeout-hint": "Tempo massimo in secondi per completare la richiesta. 0 significa infinito (sconsigliato).", + "units": "Unità", + "tell-failure-aws-lambda": "Segnala errore se AWS Lambda solleva eccezione", + "tell-failure-aws-lambda-hint": "Il nodo forza un errore se l'esecuzione della funzione Lambda solleva un'eccezione.", + "basic-mode": "Base", + "advanced-mode": "Avanzato", + "save-time-series": { + "processing-settings": "Impostazioni di elaborazione", + "processing-settings-hint": "Definisci come vengono elaborati i messaggi in arrivo. Le impostazioni di base consentono di selezionare strategie preconfigurate, mentre le impostazioni avanzate permettono di selezionare strategie individuali per ciascuna azione.", + "advanced-settings-hint": "Fai attenzione quando configuri le strategie di elaborazione. Alcune combinazioni possono portare a comportamenti imprevisti.", + "strategy": "Strategia", + "deduplication-interval": "Intervallo di deduplicazione", + "deduplication-interval-required": "L'intervallo di deduplicazione è richiesto", + "deduplication-interval-min-max-range": "L'intervallo di deduplicazione deve essere almeno di 1 secondo e massimo di 1 giorno", + "strategy-type": { + "every-message": "Su ogni messaggio", + "skip": "Salta", + "deduplicate": "Deduplica", + "web-sockets-only": "Solo WebSockets" + }, + "time-series": "Serie temporale", + "latest": "Ultimi valori", + "web-sockets": "WebSockets", + "calculated-fields": "Campi calcolati" + }, + "save-attribute": { + "processing-settings": "Impostazioni di elaborazione", + "processing-settings-hint": "Definisci come vengono elaborati i messaggi in arrivo. Le impostazioni di base consentono di selezionare strategie preconfigurate, mentre le impostazioni avanzate permettono di selezionare strategie individuali per ciascuna azione.", + "advanced-settings-hint": "Fai attenzione quando configuri le strategie di elaborazione. Alcune combinazioni possono portare a comportamenti imprevisti.", + "strategy": "Strategia", + "deduplication-interval": "Intervallo di deduplicazione", + "deduplication-interval-required": "L'intervallo di deduplicazione è richiesto", + "deduplication-interval-min-max-range": "L'intervallo di deduplicazione deve essere almeno di 1 secondo e massimo di 1 giorno", + "scope": "Ambito", + "strategy-type": { + "every-message": "Su ogni messaggio", + "skip": "Salta", + "deduplicate": "Deduplica", + "web-sockets-only": "Solo WebSockets" + }, + "attributes": "Attributi" + }, + "key-val": { + "key": "Chiave", + "value": "Valore", + "see-examples": "Vedi esempi.", + "remove-entry": "Rimuovi voce", + "remove-mapping-entry": "Rimuovi mappatura", + "add-mapping-entry": "Aggiungi mappatura", + "add-entry": "Aggiungi voce", + "copy-key-values-from": "Copia coppie chiave-valore da", + "delete-key-values": "Elimina coppie chiave-valore", + "delete-key-values-from": "Elimina coppie chiave-valore da", + "at-least-one-key-error": "Almeno una chiave deve essere selezionata.", + "unique-key-value-pair-error": "'{{keyText}}' deve essere diverso da '{{valText}}'!" + }, + "mail-body-types": { + "plain-text": "Testo semplice", + "html": "HTML", + "dynamic": "Dinamico", + "use-body-type-template": "Usa template tipo di corpo", + "plain-text-description": "Testo semplice, senza formattazione o stile speciale.", + "html-text-description": "Permette di usare tag HTML per formattazione, link e immagini nel corpo dell'email.", + "dynamic-text-description": "Permette di usare testo semplice o HTML dinamicamente in base alla templatizzazione.", + "after-template-evaluation-hint": "Dopo la valutazione del template il valore deve essere true per HTML e false per testo semplice." + } + }, + "timezone": { + "timezone": "Fuso orario", + "select-timezone": "Seleziona fuso orario", + "no-timezones-matching": "Nessun fuso orario corrispondente a '{{timezone}}' trovato.", + "timezone-required": "Il fuso orario è obbligatorio.", + "browser-time": "Ora del browser" + }, + "queue": { + "queue-name": "Coda", + "no-queues-found": "Nessuna coda trovata.", + "no-queues-matching": "Nessuna coda corrispondente a '{{queue}}' trovata.", + "select-name": "Seleziona nome coda", + "name": "Nome", + "name-required": "Il nome della coda è obbligatorio!", + "name-unique": "Il nome della coda non è univoco!", + "name-pattern": "Il nome della coda contiene caratteri non validi (solo alfanumerici ASCII, '.', '_' e '-')!", + "queue-required": "La coda è obbligatoria!", + "topic-required": "Il topic della coda è obbligatorio!", + "poll-interval-required": "L'intervallo di polling è obbligatorio!", + "poll-interval-min-value": "Il valore dell'intervallo di polling non può essere inferiore a 1", + "partitions-required": "Le partizioni sono obbligatorie!", + "partitions-min-value": "Il valore delle partizioni non può essere inferiore a 1", + "pack-processing-timeout-required": "Il timeout di elaborazione è obbligatorio", + "pack-processing-timeout-min-value": "Il valore del timeout non può essere inferiore a 1", + "batch-size-required": "La dimensione del batch è obbligatoria!", + "batch-size-min-value": "La dimensione del batch non può essere inferiore a 1", + "retries-required": "Il numero di tentativi è obbligatorio!", + "retries-min-value": "Il numero di tentativi non può essere negativo", + "failure-percentage-required": "La percentuale di errore è obbligatoria!", + "failure-percentage-min-value": "La percentuale di errore non può essere inferiore a 0", + "failure-percentage-max-value": "La percentuale di errore non può superare 100", + "pause-between-retries-required": "La pausa tra i tentativi è obbligatoria!", + "pause-between-retries-min-value": "La pausa tra i tentativi non può essere inferiore a 1", + "max-pause-between-retries-required": "Il valore massimo della pausa tra i tentativi è obbligatorio!", + "max-pause-between-retries-min-value": "Il valore massimo della pausa tra i tentativi non può essere inferiore a 1", + "submit-strategy-type-required": "Il tipo di strategia di invio è obbligatorio!", + "processing-strategy-type-required": "Il tipo di strategia di elaborazione è obbligatorio!", + "queues": "Code", + "selected-queues": "{ count, plural, =1 {1 coda} other {# code} } selezionata", + "delete-queue-title": "Sei sicuro di voler eliminare la coda '{{queueName}}'?", + "delete-queues-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 coda} other {# code} }?", + "delete-queue-text": "Attenzione, dopo la conferma la coda e tutti i dati correlati saranno irrecuperabili.", + "delete-queues-text": "Dopo la conferma tutte le code selezionate verranno eliminate e non saranno più accessibili.", + "search": "Cerca coda", + "add": "Aggiungi coda", + "details": "Dettagli coda", + "topic": "Topic", + "submit-settings": "Impostazioni invio", + "submit-strategy": "Tipo di strategia *", + "grouping-parameter": "Parametro di raggruppamento", + "processing-settings": "Impostazioni elaborazione tentativi", + "processing-strategy": "Tipo di elaborazione *", + "retries-settings": "Impostazioni dei tentativi", + "polling-settings": "Impostazioni polling", + "batch-processing": "Elaborazione batch", + "poll-interval": "Intervallo di polling", + "partitions": "Partizioni", + "immediate-processing": "Elaborazione immediata", + "consumer-per-partition": "Polling separato per ciascun consumer", + "consumer-per-partition-hint": "Abilita consumer separati per ogni partizione", + "duplicate-msg-to-all-partitions": "Duplica messaggio su tutte le partizioni", + "processing-timeout": "Tempo di elaborazione, ms", + "batch-size": "Dimensione batch", + "retries": "Numero di tentativi (0 – illimitato)", + "failure-percentage": "Messaggi di errore per saltare i tentativi, %", + "pause-between-retries": "Tentativo successivo tra, sec", + "max-pause-between-retries": "Tentativo successivo aggiuntivo tra, sec", + "delete": "Elimina coda", + "copyId": "Copia ID coda", + "idCopiedMessage": "ID della coda copiato negli appunti", + "description": "Descrizione", + "description-hint": "Questo testo verrà visualizzato nella descrizione della coda anziché la strategia selezionata", + "alt-description": "Strategia di invio: {{submitStrategy}}, Strategia di elaborazione: {{processingStrategy}}", + "custom-properties": "Proprietà personalizzate", + "custom-properties-hint": "Proprietà di creazione della coda personalizzata (topic), es. 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies": { + "sequential-by-originator-label": "Sequenziale per originatore", + "sequential-by-originator-hint": "Un nuovo messaggio, ad esempio per il dispositivo A, non viene inviato finché il messaggio precedente per il dispositivo A non viene confermato", + "sequential-by-tenant-label": "Sequenziale per tenant", + "sequential-by-tenant-hint": "Un nuovo messaggio, ad esempio per il tenant A, non viene inviato finché il messaggio precedente per il tenant A non viene confermato", + "sequential-label": "Sequenziale", + "sequential-hint": "Un nuovo messaggio non viene inviato finché il messaggio precedente non viene confermato", + "burst-label": "Raffica", + "burst-hint": "Tutti i messaggi vengono inviati alle rule chain nell’ordine in cui arrivano", + "batch-label": "Batch", + "batch-hint": "Un nuovo batch non viene inviato finché il batch precedente non viene confermato", + "skip-all-failures-label": "Ignora tutti gli errori", + "skip-all-failures-hint": "Ignora tutti gli errori", + "skip-all-failures-and-timeouts-label": "Ignora tutti gli errori e timeout", + "skip-all-failures-and-timeouts-hint": "Ignora tutti gli errori e i timeout", + "retry-all-label": "Ritenta tutto", + "retry-all-hint": "Ritenta tutti i messaggi dal pacchetto di elaborazione", + "retry-failed-label": "Ritenta falliti", + "retry-failed-hint": "Ritenta tutti i messaggi falliti dal pacchetto di elaborazione", + "retry-timeout-label": "Ritenta timeout", + "retry-timeout-hint": "Ritenta tutti i messaggi andati in timeout dal pacchetto di elaborazione", + "retry-failed-and-timeout-label": "Ritenta falliti e timeout", + "retry-failed-and-timeout-hint": "Ritenta tutti i messaggi falliti e andati in timeout dal pacchetto di elaborazione" + } + }, + "queue-statistics": { + "queue-statistics": "Statistiche delle code", + "no-queue-statistics-matching": "Nessuna statistica delle code trovata corrispondente a '{{entity}}'.", + "queue-statistics-required": "Statistiche delle code richieste.", + "list-of-queue-statistics": "{ count, plural, =1 {Una statistica di coda} other {Elenco di # statistiche di coda} }", + "selected-queue-statistics": "{ count, plural, =1 {1 statistica di coda} other {# statistiche di coda} } selezionate", + "no-queue-statistics-text": "Nessuna statistica di coda trovata", + "queue-statistics-starts-with": "Statistiche delle code i cui nomi iniziano con '{{prefix}}'" + }, + "server-error": { + "general": "Errore generale del server", + "authentication": "Errore di autenticazione", + "jwt-token-expired": "Token JWT scaduto", + "tenant-trial-expired": "Periodo di prova del tenant scaduto", + "credentials-expired": "Credenziali scadute", + "permission-denied": "Permesso negato", + "invalid-arguments": "Argomenti non validi", + "bad-request-params": "Parametri di richiesta errati", + "item-not-found": "Elemento non trovato", + "too-many-requests": "Troppe richieste", + "too-many-updates": "Troppe modifiche" + }, + "tenant": { + "tenant": "Tenant", + "tenants": "Tenant", + "management": "Gestione tenant", + "add": "Aggiungi tenant", + "admins": "Amministratori", + "manage-tenant-admins": "Gestisci amministratori tenant", + "delete": "Elimina tenant", + "add-tenant-text": "Aggiungi nuovo tenant", + "no-tenants-text": "Nessun tenant trovato", + "tenant-details": "Dettagli tenant", + "title-max-length": "Il titolo deve contenere meno di 256 caratteri", + "delete-tenant-title": "Sei sicuro di voler eliminare il tenant '{{tenantTitle}}'?", + "delete-tenant-text": "Attenzione, dopo la conferma il tenant e tutti i dati correlati non saranno recuperabili.", + "delete-tenants-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 tenant} other {# tenant} }?", + "delete-tenants-action-title": "Elimina { count, plural, =1 {1 tenant} other {# tenant} }", + "delete-tenants-text": "Attenzione, dopo la conferma tutti i tenant selezionati verranno rimossi e tutti i dati correlati non saranno recuperabili.", + "title": "Titolo", + "title-required": "Il titolo è obbligatorio.", + "description": "Descrizione", + "details": "Dettagli", + "events": "Eventi", + "copyId": "Copia Id del tenant", + "idCopiedMessage": "Id del tenant copiato negli appunti", + "select-tenant": "Seleziona tenant", + "no-tenants-matching": "Nessun tenant corrispondente a '{{entity}}' trovato.", + "tenant-required": "Tenant richiesto", + "search": "Cerca tenant", + "selected-tenants": "{ count, plural, =1 {1 tenant} other {# tenant} } selezionato/i", + "isolated-tb-rule-engine": "Usa code isolate per il Rule Engine di ThingsBoard", + "isolated-tb-rule-engine-details": "Ogni tenant avrà code dedicate del Rule Engine" + }, + "tenant-profile": { + "tenant-profile": "Profilo tenant", + "tenant-profiles": "Profili tenant", + "add": "Aggiungi profilo tenant", + "add-profile": "Aggiungi profilo", + "debug": "Debug", + "edit": "Modifica profilo tenant", + "tenant-profile-details": "Dettagli del profilo tenant", + "no-tenant-profiles-text": "Nessun profilo tenant trovato", + "name-max-length": "Il nome deve contenere meno di 256 caratteri", + "search": "Cerca profili tenant", + "selected-tenant-profiles": "{ count, plural, =1 {1 profilo tenant} other {# profili tenant} } selezionato/i", + "no-tenant-profiles-matching": "Nessun profilo tenant corrispondente a '{{entity}}' trovato.", + "tenant-profile-required": "Profilo tenant richiesto", + "idCopiedMessage": "Id del profilo tenant copiato negli appunti", + "set-default": "Imposta come profilo tenant predefinito", + "delete": "Elimina profilo tenant", + "copyId": "Copia Id del profilo tenant", + "name": "Nome", + "name-required": "Il nome è obbligatorio.", + "data": "Dati del profilo", + "profile-configuration": "Configurazione del profilo", + "description": "Descrizione", + "default": "Predefinito", + "delete-tenant-profile-title": "Sei sicuro di voler eliminare il profilo tenant '{{tenantProfileName}}'?", + "delete-tenant-profile-text": "Attenzione, dopo la conferma il profilo tenant e tutti i dati correlati non saranno recuperabili.", + "delete-tenant-profiles-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 profilo tenant} other {# profili tenant} }?", + "delete-tenant-profiles-text": "Attenzione, dopo la conferma tutti i profili tenant selezionati saranno rimossi e tutti i dati correlati non saranno recuperabili.", + "set-default-tenant-profile-title": "Sei sicuro di voler impostare '{{tenantProfileName}}' come profilo tenant predefinito?", + "set-default-tenant-profile-text": "Dopo la conferma, il profilo tenant sarà contrassegnato come predefinito e sarà utilizzato per i nuovi tenant senza profilo specificato.", + "no-tenant-profiles-found": "Nessun profilo tenant trovato.", + "create-new-tenant-profile": "Creane uno nuovo!", + "create-tenant-profile": "Crea nuovo profilo tenant", + "import": "Importa profilo tenant", + "export": "Esporta profilo tenant", + "export-failed-error": "Impossibile esportare il profilo tenant: {{error}}", + "tenant-profile-file": "File del profilo tenant", + "invalid-tenant-profile-file-error": "Impossibile importare il profilo tenant: struttura dati non valida.", + "advanced-settings": "Impostazioni avanzate", + "entities": "Entità", + "rule-engine": "Rule Engine", + "time-to-live": "Time-to-live", + "calculated-fields": "Campi calcolati", + "alarms-and-notifications": "Allarmi e notifiche", + "ota-files-in-bytes": "File", + "ws-title": "WS", + "unlimited": "(0 - illimitato)", + "maximum-devices": "Numero massimo di dispositivi", + "maximum-devices-required": "È richiesto il numero massimo di dispositivi.", + "maximum-devices-range": "Il numero massimo di dispositivi non può essere negativo", + "maximum-assets": "Numero massimo di asset", + "maximum-assets-required": "È richiesto il numero massimo di asset.", + "maximum-assets-range": "Il numero massimo di asset non può essere negativo", + "maximum-customers": "Numero massimo di clienti", + "maximum-customers-required": "È richiesto il numero massimo di clienti.", + "maximum-customers-range": "Il numero massimo di clienti non può essere negativo", + "maximum-users": "Numero massimo di utenti", + "maximum-users-required": "È richiesto il numero massimo di utenti.", + "maximum-users-range": "Il numero massimo di utenti non può essere negativo", + "maximum-dashboards": "Numero massimo di dashboard", + "maximum-dashboards-required": "È richiesto il numero massimo di dashboard.", + "maximum-dashboards-range": "Il numero massimo di dashboard non può essere negativo", + "maximum-edges": "Numero massimo di edge", + "maximum-edges-required": "È richiesto il numero massimo di edge.", + "maximum-edges-range": "Il numero massimo di edge non può essere negativo", + "maximum-rule-chains": "Numero massimo di rule chain", + "maximum-rule-chains-required": "È richiesto il numero massimo di rule chain.", + "maximum-rule-chains-range": "Il numero massimo di rule chain non può essere negativo", + "maximum-resources-sum-data-size": "Dimensione totale massima dei file risorsa (byte)", + "maximum-resources-sum-data-size-required": "La dimensione totale massima dei file risorsa è richiesta.", + "maximum-resources-sum-data-size-range": "La dimensione totale massima dei file risorsa non può essere negativa", + "maximum-resource-size": "Dimensione massima del singolo file risorsa (byte)", + "maximum-resource-size-required": "La dimensione massima del file risorsa è richiesta", + "maximum-resource-size-range": "La dimensione massima del file risorsa non può essere negativa", + "maximum-ota-packages-sum-data-size": "Dimensione totale massima dei file OTA (byte)", + "maximum-ota-package-sum-data-size-required": "La dimensione totale massima dei file OTA è richiesta.", + "maximum-ota-package-sum-data-size-range": "La dimensione totale massima dei file OTA non può essere negativa", + "maximum-debug-duration-min": "Durata massima di debug (min)", + "maximum-debug-duration-min-range": "La durata massima di debug non può essere negativa", + "rest-requests-for-tenant": "Richieste REST per tenant", + "transport-tenant-telemetry-msg-rate-limit": "Messaggi di telemetria del tenant (trasporto)", + "transport-tenant-telemetry-data-points-rate-limit": "Punti dati di telemetria del tenant (trasporto)", + "transport-device-msg-rate-limit": "Messaggi del dispositivo (trasporto)", + "transport-device-telemetry-msg-rate-limit": "Messaggi di telemetria del dispositivo (trasporto)", + "transport-device-telemetry-data-points-rate-limit": "Punti dati di telemetria del dispositivo (trasporto)", + "transport-gateway-msg-rate-limit": "Messaggi del gateway (trasporto)", + "transport-gateway-telemetry-msg-rate-limit": "Messaggi di telemetria del gateway (trasporto)", + "transport-gateway-telemetry-data-points-rate-limit": "Punti dati di telemetria del gateway (trasporto)", + "transport-gateway-device-msg-rate-limit": "Messaggi del dispositivo gateway (trasporto)", + "transport-gateway-device-telemetry-msg-rate-limit": "Messaggi di telemetria del dispositivo gateway (trasporto)", + "transport-gateway-device-telemetry-data-points-rate-limit": "Punti dati di telemetria del dispositivo gateway (trasporto)", + "tenant-entity-export-rate-limit": "Creazione versione entità", + "tenant-entity-import-rate-limit": "Caricamento versione entità", + "tenant-notification-request-rate-limit": "Richieste di notifica", + "tenant-notification-requests-per-rule-rate-limit": "Richieste di notifica per regola", + "max-calculated-fields": "Numero massimo di campi calcolati per entità", + "max-calculated-fields-range": "Il numero massimo di campi calcolati per entità non può essere negativo", + "max-calculated-fields-required": "Il numero massimo di campi calcolati per entità è richiesto", + "max-data-points-per-rolling-arg": "Numero massimo di punti dati negli argomenti rolling", + "max-data-points-per-rolling-arg-range": "Il numero massimo di punti dati negli argomenti rolling non può essere negativo", + "max-data-points-per-rolling-arg-required": "Il numero massimo di punti dati negli argomenti rolling è richiesto", + "max-arguments-per-cf": "Numero massimo di argomenti per campo calcolato", + "max-arguments-per-cf-range": "Il numero massimo di argomenti per campo calcolato non può essere negativo", + "max-arguments-per-cf-required": "Il numero massimo di argomenti per campo calcolato è richiesto", + "max-state-size": "Dimensione massima dello stato in KB", + "max-state-size-range": "La dimensione massima dello stato in KB non può essere negativa", + "max-state-size-required": "La dimensione massima dello stato in KB è richiesta", + "max-value-argument-size": "Dimensione massima del singolo argomento di valore in KB", + "max-value-argument-size-range": "La dimensione massima dell'argomento di valore in KB non può essere negativa", + "max-value-argument-size-required": "La dimensione massima dell'argomento di valore in KB è richiesta", + "max-transport-messages": "Numero massimo di messaggi (trasporto)", + "max-transport-messages-required": "È richiesto il numero massimo di messaggi (trasporto).", + "max-transport-messages-range": "Il numero massimo di messaggi (trasporto) non può essere negativo", + "max-transport-data-points": "Numero massimo di punti dati (trasporto)", + "max-transport-data-points-required": "È richiesto il numero massimo di punti dati (trasporto).", + "max-transport-data-points-range": "Il numero massimo di punti dati (trasporto) non può essere negativo", + "max-r-e-executions": "Numero massimo di esecuzioni Rule Engine", + "max-r-e-executions-required": "È richiesto il numero massimo di esecuzioni Rule Engine.", + "max-r-e-executions-range": "Il numero massimo di esecuzioni Rule Engine non può essere negativo", + "max-j-s-executions": "Numero massimo di esecuzioni JavaScript", + "max-j-s-executions-required": "È richiesto il numero massimo di esecuzioni JavaScript.", + "max-j-s-executions-range": "Il numero massimo di esecuzioni JavaScript non può essere negativo", + "max-tbel-executions": "Numero massimo di esecuzioni TBEL", + "max-tbel-executions-required": "È richiesto il numero massimo di esecuzioni TBEL.", + "max-tbel-executions-range": "Il numero massimo di esecuzioni TBEL non può essere negativo", + "max-d-p-storage-days": "Numero massimo di giorni di archiviazione dei dati", + "max-d-p-storage-days-required": "È richiesto il numero massimo di giorni di archiviazione dei dati.", + "max-d-p-storage-days-range": "Il numero massimo di giorni di archiviazione dei dati non può essere negativo", + "default-storage-ttl-days": "TTL archiviazione predefinito (giorni)", + "default-storage-ttl-days-required": "È richiesto il TTL predefinito per l'archiviazione.", + "default-storage-ttl-days-range": "Il TTL di archiviazione predefinito non può essere negativo", + "alarms-ttl-days": "TTL degli allarmi (giorni)", + "alarms-ttl-days-required": "TTL degli allarmi richiesto", + "alarms-ttl-days-days-range": "Il TTL degli allarmi non può essere negativo", + "rpc-ttl-days": "TTL delle RPC (giorni)", + "rpc-ttl-days-required": "TTL delle RPC richiesto", + "rpc-ttl-days-days-range": "Il TTL delle RPC non può essere negativo", + "queue-stats-ttl-days": "TTL delle statistiche della coda (giorni)", + "queue-stats-ttl-days-required": "TTL delle statistiche della coda richiesto", + "queue-stats-ttl-days-range": "Il TTL delle statistiche della coda non può essere negativo", + "rule-engine-exceptions-ttl-days": "TTL delle eccezioni del Rule Engine (giorni)", + "rule-engine-exceptions-ttl-days-required": "TTL delle eccezioni del Rule Engine richiesto", + "rule-engine-exceptions-ttl-days-range": "Il TTL delle eccezioni del Rule Engine non può essere negativo", + "max-rule-node-executions-per-message": "Numero massimo di esecuzioni per nodo per messaggio", + "max-rule-node-executions-per-message-required": "È richiesto il numero massimo di esecuzioni per nodo per messaggio.", + "max-rule-node-executions-per-message-range": "Il numero massimo di esecuzioni per nodo per messaggio non può essere negativo", + "max-emails": "Numero massimo di email inviate", + "max-emails-required": "È richiesto il numero massimo di email inviate.", + "max-emails-range": "Il numero massimo di email inviate non può essere negativo", + "sms-enabled": "SMS abilitati", + "max-sms": "Numero massimo di SMS inviati", + "max-sms-required": "È richiesto il numero massimo di SMS inviati.", + "max-sms-range": "Il numero massimo di SMS inviati non può essere negativo", + "max-created-alarms": "Numero massimo di allarmi creati", + "max-created-alarms-required": "È richiesto il numero massimo di allarmi creati.", + "max-created-alarms-range": "Il numero massimo di allarmi creati non può essere negativo", + "no-queue": "Nessuna coda configurata", + "add-queue": "Aggiungi coda", + "queues-with-count": "Code ({{count}})", + "tenant-rest-limits": "Richieste REST per tenant", + "customer-rest-limits": "Richieste REST per cliente", + "incorrect-pattern-for-rate-limits": "Il formato è coppie separate da virgole di capacità e periodo (in secondi) con i due punti, ad es. 100:1,2000:60", + "too-small-value-zero": "Il valore deve essere maggiore di 0", + "too-small-value-one": "Il valore deve essere maggiore di 1", + "queue-size-is-limited-by-system-configuration": "La dimensione della coda è anche limitata dalla configurazione di sistema.", + "cassandra-tenant-limits-configuration": "Query Cassandra per tenant", + "ws-limit-max-sessions-per-tenant": "Numero massimo di sessioni per tenant", + "ws-limit-max-sessions-per-customer": "Numero massimo di sessioni per cliente", + "ws-limit-max-sessions-per-regular-user": "Numero massimo di sessioni per utente regolare", + "ws-limit-max-sessions-per-public-user": "Numero massimo di sessioni per utente pubblico", + "ws-limit-queue-per-session": "Dimensione massima della coda per sessione", + "ws-limit-max-subscriptions-per-tenant": "Numero massimo di sottoscrizioni per tenant", + "ws-limit-max-subscriptions-per-customer": "Numero massimo di sottoscrizioni per cliente", + "ws-limit-max-subscriptions-per-regular-user": "Numero massimo di sottoscrizioni per utente regolare", + "ws-limit-max-subscriptions-per-public-user": "Numero massimo di sottoscrizioni per utente pubblico", + "ws-limit-updates-per-session": "Aggiornamenti WS per sessione", + "rate-limits": { + "add-limit": "Aggiungi limite", + "advanced-settings": "Impostazioni avanzate", + "edit-limit": "Modifica limite", + "but-less-than": "ma inferiore a", + "calculated-field-debug-event-rate-limit": "Eventi di debug del campo calcolato", + "edit-calculated-field-debug-event-rate-limit": "Modifica i limiti di eventi di debug del campo calcolato", + "edit-transport-tenant-msg-title": "Modifica i limiti di messaggi di trasporto del tenant", + "edit-transport-tenant-telemetry-msg-title": "Modifica i limiti di messaggi di telemetria del tenant", + "edit-transport-tenant-telemetry-data-points-title": "Modifica i limiti dei punti dati di telemetria del tenant", + "edit-transport-device-msg-title": "Modifica i limiti di messaggi del dispositivo", + "edit-transport-device-telemetry-msg-title": "Modifica i limiti di messaggi di telemetria del dispositivo", + "edit-transport-device-telemetry-data-points-title": "Modifica i limiti dei punti dati di telemetria del dispositivo", + "edit-transport-gateway-msg-title": "Modifica i limiti di messaggi del gateway", + "edit-transport-gateway-telemetry-msg-title": "Modifica i limiti di messaggi di telemetria del gateway", + "edit-transport-gateway-telemetry-data-points-title": "Modifica i limiti dei punti dati di telemetria del gateway", + "edit-transport-gateway-device-msg-title": "Modifica i limiti di messaggi dei dispositivi gateway", + "edit-transport-gateway-device-telemetry-msg-title": "Modifica i limiti di messaggi di telemetria dei dispositivi gateway", + "edit-transport-gateway-device-telemetry-data-points-title": "Modifica i limiti dei punti dati di telemetria dei dispositivi gateway", + "edit-tenant-rest-limits-title": "Modifica i limiti delle richieste REST per il tenant", + "edit-customer-rest-limits-title": "Modifica i limiti delle richieste REST per il cliente", + "edit-ws-limit-updates-per-session-title": "Modifica i limiti degli aggiornamenti WS per sessione", + "edit-cassandra-tenant-limits-configuration-title": "Modifica i limiti delle query Cassandra per tenant", + "edit-tenant-entity-export-rate-limit-title": "Modifica i limiti di creazione delle versioni delle entità", + "edit-tenant-entity-import-rate-limit-title": "Modifica i limiti di caricamento delle versioni delle entità", + "edit-tenant-notification-request-rate-limit-title": "Modifica i limiti delle richieste di notifica", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "Modifica i limiti delle richieste di notifica per regola", + "edit-edge-events-rate-limit": "Modifica i limiti degli eventi edge", + "edit-edge-events-per-edge-rate-limit": "Modifica i limiti degli eventi edge per edge", + "edge-events-rate-limit": "Eventi edge", + "edge-events-per-edge-rate-limit": "Eventi edge per edge", + "edit-edge-uplink-messages-rate-limit": "Modifica i limiti dei messaggi uplink edge", + "edit-edge-uplink-messages-per-edge-rate-limit": "Modifica i limiti dei messaggi uplink edge per edge", + "edge-uplink-messages-rate-limit": "Messaggi uplink edge", + "edge-uplink-messages-per-edge-rate-limit": "Messaggi uplink edge per edge", + "messages-per": "messaggi per", + "not-set": "Non impostato", + "number-of-messages": "Numero di messaggi", + "number-of-messages-required": "È richiesto il numero di messaggi.", + "number-of-messages-min": "Il valore minimo è 1.", + "preview": "Anteprima", + "per-seconds": "Ogni secondi", + "per-seconds-required": "È richiesta la frequenza temporale.", + "per-seconds-min": "Il valore minimo è 1.", + "rate-limits": "Limiti di frequenza", + "remove-limit": "Rimuovi limite", + "transport-tenant-msg": "Messaggi di trasporto del tenant", + "transport-tenant-telemetry-msg": "Messaggi di telemetria del tenant", + "transport-tenant-telemetry-data-points": "Punti dati di telemetria del tenant", + "transport-device-msg": "Messaggi di trasporto del dispositivo", + "transport-device-telemetry-msg": "Messaggi di telemetria del dispositivo", + "transport-device-telemetry-data-points": "Punti dati di telemetria del dispositivo", + "transport-gateway-msg": "Messaggi di trasporto del gateway", + "transport-gateway-telemetry-msg": "Messaggi di telemetria del gateway", + "transport-gateway-telemetry-data-points": "Punti dati di telemetria del gateway", + "transport-gateway-device-msg": "Messaggi dei dispositivi gateway", + "transport-gateway-device-telemetry-msg": "Messaggi di telemetria dei dispositivi gateway", + "transport-gateway-device-telemetry-data-points": "Punti dati di telemetria dei dispositivi gateway", + "sec": "sec" + } + }, + "timeinterval": { + "seconds-interval": "{ seconds, plural, =1 {1 secondo} other {# secondi} }", + "minutes-interval": "{ minutes, plural, =1 {1 minuto} other {# minuti} }", + "hours-interval": "{ hours, plural, =1 {1 ora} other {# ore} }", + "days-interval": "{ days, plural, =1 {1 giorno} other {# giorni} }", + "days": "Giorni", + "hours": "Ore", + "minutes": "Minuti", + "seconds": "Secondi", + "advanced": "Avanzato", + "custom": "Personalizzato", + "predefined": { + "yesterday": "Ieri", + "day-before-yesterday": "L'altro ieri", + "this-day-last-week": "Questo giorno la scorsa settimana", + "previous-week": "Settimana precedente (dom - sab)", + "previous-week-iso": "Settimana precedente (lun - dom)", + "previous-month": "Mese precedente", + "previous-quarter": "Trimestre precedente", + "previous-half-year": "Semestre precedente", + "previous-year": "Anno precedente", + "current-hour": "Ora corrente", + "current-day": "Giorno corrente", + "current-day-so-far": "Giorno corrente fino ad ora", + "current-week": "Settimana corrente (dom - sab)", + "current-week-iso": "Settimana corrente (lun - dom)", + "current-week-so-far": "Settimana corrente fino ad ora (dom - sab)", + "current-week-iso-so-far": "Settimana corrente fino ad ora (lun - dom)", + "current-month": "Mese corrente", + "current-month-so-far": "Mese corrente fino ad ora", + "current-quarter": "Trimestre corrente", + "current-quarter-so-far": "Trimestre corrente fino ad ora", + "current-half-year": "Semestre corrente", + "current-half-year-so-far": "Semestre corrente fino ad ora", + "current-year": "Anno corrente", + "current-year-so-far": "Anno corrente fino ad ora" + }, + "type": { + "week": "Settimana (dom - sab)", + "week-iso": "Settimana (lun - dom)", + "month": "Mese", + "quarter": "Trimestre" + } + }, + "timeunit": { + "milliseconds": "Millisecondi", + "seconds": "Secondi", + "minutes": "Minuti", + "hours": "Ore", + "days": "Giorni" + }, + "timewindow": { + "timewindow": "Finestra temporale", + "timewindow-settings": "Impostazioni della finestra temporale", + "years": "{ years, plural, =1 { anno } other {# anni } }", + "years-short": "{{ years }}a", + "months": "{ months, plural, =1 { mese } other {# mesi } }", + "months-short": "{{ months }}M", + "weeks": "{ weeks, plural, =1 { settimana } other {# settimane } }", + "weeks-short": "{{ weeks }}s", + "days": "{ days, plural, =1 { giorno } other {# giorni } }", + "days-short": "{{ days }}g", + "hours": "{ hours, plural, =0 { ora } =1 {1 ora } other {# ore } }", + "hr": "{{ hr }} ore", + "hr-short": "{{ hr }}h", + "minutes": "{ minutes, plural, =0 { minuto } =1 {1 minuto } other {# minuti } }", + "min": "{{ min }} min", + "min-short": "{{ min }}m", + "seconds": "{ seconds, plural, =0 { secondo } =1 {1 secondo } other {# secondi } }", + "sec": "{{ sec }} sec", + "sec-short": "{{ sec }}s", + "short": { + "years": "{ years, plural, =1 {1 anno } other {# anni } }", + "days": "{ days, plural, =1 {1 giorno } other {# giorni } }", + "hours": "{ hours, plural, =1 {1 ora } other {# ore } }", + "minutes": "{{minutes}} min ", + "seconds": "{{seconds}} sec " + }, + "realtime": "Tempo reale", + "history": "Cronologia", + "last-prefix": "ultimo", + "period": "da {{ startTime }} a {{ endTime }}", + "edit": "Modifica finestra temporale", + "date-range": "Intervallo di date", + "for-all-time": "Per tutto il tempo", + "last": "Ultimo", + "time-period": "Periodo di tempo", + "hide": "Nascondi", + "interval": "Intervallo", + "just-now": "Proprio ora", + "just-now-lower": "proprio ora", + "ago": "fa", + "style": "Stile della finestra temporale", + "icon": "Icona", + "icon-position": "Posizione icona", + "icon-position-left": "Sinistra", + "icon-position-right": "Destra", + "font": "Font", + "color": "Colore", + "displayTypePrefix": "Mostra prefisso Tempo reale/Cronologia", + "preview": "Anteprima", + "relative": "Relativo", + "range": "Intervallo", + "hide-timewindow-section": "Nascondi sezione finestra temporale agli utenti finali", + "hide-last-interval": "Nascondi l'ultimo intervallo agli utenti finali", + "hide-relative-interval": "Nascondi intervallo relativo agli utenti finali", + "hide-fixed-interval": "Nascondi intervallo fisso agli utenti finali", + "hide-aggregation": "Nascondi aggregazione agli utenti finali", + "hide-group-interval": "Nascondi intervallo di raggruppamento agli utenti finali", + "hide-max-values": "Nascondi valori massimi agli utenti finali", + "hide-timezone": "Nascondi fuso orario agli utenti finali", + "disable-custom-interval": "Disabilita selezione intervallo personalizzato", + "edit-aggregation-functions-list": "Modifica elenco funzioni di aggregazione", + "edit-aggregation-functions-list-hint": "È possibile specificare un elenco di opzioni disponibili.", + "allowed-aggregation-functions": "Funzioni di aggregazione consentite", + "edit-intervals-list": "Modifica elenco intervalli", + "allowed-agg-intervals": "Intervalli di raggruppamento", + "default-agg-interval": "Intervallo di raggruppamento predefinito", + "edit-intervals-list-hint": "È possibile specificare un elenco di opzioni di intervallo disponibili.", + "edit-grouping-intervals-list-hint": "È possibile configurare l'elenco degli intervalli di raggruppamento e l'intervallo di raggruppamento predefinito.", + "all": "Tutti" + }, + "tooltip": { + "trigger": "Trigger", + "trigger-point": "Punto", + "trigger-axis": "Asse", + "label": "Etichetta", + "value": "Valore", + "date": "Data", + "show-date-time-interval": "Mostra intervallo data e ora", + "show-date-time-interval-hint": "Mostra intervallo data e ora in base all'aggregazione dei dati.", + "background-color": "Colore di sfondo", + "background-blur": "Sfocatura sfondo" + }, + "unit": { + "millimeter": "Millimetro", + "centimeter": "Centimetro", + "angstrom": "Ångström", + "nanometer": "Nanometro", + "micrometer": "Micrometro", + "meter": "Metro", + "kilometer": "Chilometro", + "inch": "Pollice", + "foot": "Piede", + "yard": "Iarda", + "mile": "Miglio", + "nautical-mile": "Miglio nautico", + "astronomical-unit": "Unità astronomica", + "reciprocal-metre": "Metro reciproco", + "meter-per-meter": "Metro per metro", + "steradian": "Steradiante", + "thou": "Millesimo di pollice", + "barleycorn": "Chicco d'orzo", + "hand": "Mano", + "chain": "Catena", + "furlong": "Furlong", + "league": "Lega", + "fathom": "Braccio", + "cable": "Cavo", + "link": "Collegamento", + "rod": "Asta", + "nanogram": "Nanogrammo", + "microgram": "Microgrammo", + "milligram": "Milligrammo", + "gram": "Grammo", + "kilogram": "Chilogrammo", + "tonne": "Tonnellata", + "ounce": "Oncia", + "pound": "Libbra", + "stone": "Stone", + "hundredweight-count": "Centoventi libbre", + "short-tons": "Tonnellate corte", + "dalton": "Dalton", + "grain": "Grano", + "drachm": "Dramma", + "quarter": "Quarto", + "slug": "Slug", + "carat": "Carato", + "cubic-millimeter": "Millimetro cubo", + "cubic-centimeter": "Centimetro cubo", + "cubic-meter": "Metro cubo", + "cubic-kilometer": "Chilometro cubo", + "microliter": "Microlitro", + "milliliter": "Millilitro", + "liter": "Litro", + "hectoliter": "Ettolitro", + "cubic-inch": "Pollice cubo", + "cubic-foot": "Piede cubo", + "cubic-yard": "Iarda cubica", + "fluid-ounce": "Oncia liquida", + "pint": "Pinta", + "quart": "Quarto", + "gallon": "Gallone", + "oil-barrels": "Barile di petrolio", + "cubic-meter-per-kilogram": "Metro cubo per chilogrammo", + "gill": "Gill", + "hogshead": "Hogshead", + "teaspoon": "Cucchiaino", + "tablespoon": "Cucchiaio", + "cup": "Tazza", + "celsius": "Celsius", + "kelvin": "Kelvin", + "rankine": "Rankine", + "fahrenheit": "Fahrenheit", + "percent": "Percentuale", + "meter-per-second": "Metro al secondo", + "kilometer-per-hour": "Chilometro all'ora", + "foot-per-second": "Piede al secondo", + "mile-per-hour": "Miglio all'ora", + "knot": "Nodo", + "millimeters-per-minute": "Millimetri al minuto", + "kilometer-per-hour-squared": "Chilometro all'ora quadrato", + "foot-per-second-squared": "Piede al secondo quadrato", + "pascal": "Pascal", + "kilopascal": "Kilopascal", + "megapascal": "Megapascal", + "gigapascal": "Gigapascal", + "millibar": "Millibar", + "bar": "Bar", + "kilobar": "Kilobar", + "newton": "Newton", + "newton-meter": "Newton metro", + "foot-pounds": "Piedi-libbra", + "inch-pounds": "Pollici-libbra", + "newton-per-meter": "Newton per metro", + "atmospheres": "Atmosfere", + "pounds-per-square-inch": "Libbre per pollice quadrato", + "torr": "Torr", + "inches-of-mercury": "Pollici di mercurio", + "pascal-per-square-meter": "Pascal per metro quadrato", + "pound-per-square-inch": "Libbra per pollice quadrato", + "newton-per-square-meter": "Newton per metro quadrato", + "kilogram-force-per-square-meter": "Forza di chilogrammo per metro quadrato", + "pascal-per-square-centimeter": "Pascal per centimetro quadrato", + "ton-force-per-square-inch": "Tonnellata-forza per pollice quadrato", + "kilonewton-per-square-meter": "Kilonewton per metro quadrato", + "newton-per-square-millimeter": "Newton per millimetro quadrato", + "microjoule": "Microjoule", + "millijoule": "Millijoule", + "joule": "Joule", + "kilojoule": "Kilojoule", + "megajoule": "Megajoule", + "gigajoule": "Gigajoule", + "watt-hour": "Wattora", + "kilowatt-hour": "Kilowattora", + "electron-volts": "Elettronvolt", + "joules-per-coulomb": "Joule per Coulomb", + "british-thermal-unit": "Unità termica britannica", + "foot-pound": "Piede-libbra", + "calorie": "Caloria", + "small-calorie": "Piccola caloria", + "kilocalorie": "Chilocaloria", + "joule-per-kelvin": "Joule per Kelvin", + "joule-per-kilogram-kelvin": "Joule per chilogrammo-Kelvin", + "joule-per-kilogram": "Joule per chilogrammo", + "watt-per-meter-kelvin": "Watt per metro-Kelvin", + "joule-per-cubic-meter": "Joule per metro cubo", + "therm": "Therm", + "electric-dipole-moment": "Momento di dipolo elettrico", + "magnetic-dipole-moment": "Momento di dipolo magnetico", + "debye": "Debye", + "coulomb-per-square-meter-per-volt": "Coulomb per metro quadrato per Volt", + "milliwatt": "Milliwatt", + "microwatt": "Microwatt", + "watt": "Watt", + "kilowatt": "Kilowatt", + "megawatt": "Megawatt", + "gigawatt": "Gigawatt", + "metric-horsepower": "Cavallo vapore metrico", + "milliwatt-per-square-centimeter": "Milliwatt per centimetro quadrato", + "watt-per-square-centimeter": "Watt per centimetro quadrato", + "kilowatt-per-square-centimeter": "Kilowatt per centimetro quadrato", + "milliwatt-per-square-meter": "Milliwatt per metro quadrato", + "watt-per-square-meter": "Watt per metro quadrato", + "kilowatt-per-square-meter": "Kilowatt per metro quadrato", + "watt-per-square-inch": "Watt per pollice quadrato", + "kilowatt-per-square-inch": "Kilowatt per pollice quadrato", + "horsepower": "Cavallo vapore", + "btu-per-hour": "Unità termiche britanniche/ora", + "coulomb": "Coulomb", + "millicoulomb": "Millicoulomb", + "microcoulomb": "Microcoulomb", + "picocoulomb": "Picocoulomb", + "coulomb-per-meter": "Coulomb per metro", + "coulomb-per-cubic-meter": "Coulomb per metro cubo", + "coulomb-per-square-meter": "Coulomb per metro quadrato", + "square-millimeter": "Millimetro quadrato", + "square-centimeter": "Centimetro quadrato", + "square-meter": "Metro quadrato", + "hectare": "Ettaro", + "square-kilometer": "Chilometro quadrato", + "square-inch": "Pollice quadrato", + "square-foot": "Piede quadrato", + "square-yard": "Iarda quadrata", + "acre": "Acri", + "square-mile": "Miglio quadrato", + "are": "Ara", + "barn": "Barn", + "circular-inch": "Pollice circolare", + "milliampere-hour": "Milliampere-ora", + "ampere-hours": "Ampere-ora", + "kiloampere-hours": "Chiloampere-ora", + "nanoampere": "Nanoampere", + "picoampere": "Picoampere", + "microampere": "Microampere", + "milliampere": "Milliampere", + "ampere": "Ampere", + "microampere-per-square-centimeter": "Microampere per centimetro quadrato", + "ampere-per-square-meter": "Ampere per metro quadrato", + "ampere-per-meter": "Ampere per metro", + "oersted": "Oersted", + "bohr-magneton": "Magnetone di Bohr", + "ampere-meter-squared": "Ampere metro quadrato", + "nanovolt": "Nanovolt", + "picovolt": "Picovolt", + "volt": "Volt", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "Volt-metro", + "kilovolt-meter": "Kilovolt-metro", + "megavolt-meter": "Megavolt-metro", + "microvolt-meter": "Microvolt-metro", + "millivolt-meter": "Millivolt-metro", + "nanovolt-meter": "Nanovolt-metro", + "ohm": "Ohm", + "microohm": "Microohm", + "milliohm": "Milliohm", + "kilohm": "Kilohm", + "megohm": "Megohm", + "gigohm": "Gigohm", + "hertz": "Hertz", + "kilohertz": "Kilohertz", + "megahertz": "Megahertz", + "gigahertz": "Gigahertz", + "rpm": "Giri al minuto", + "candela-per-square-meter": "Candela per metro quadrato", + "candela": "Candela", + "lumen": "Lumen", + "lux": "Lux", + "foot-candle": "Foot-candle", + "lumen-per-square-meter": "Lumen per metro quadrato", + "lux-second": "Secondo lux", + "lumen-second": "Secondo lumen", + "lumens-per-watt": "Lumen per watt", + "mole": "Mole", + "nanomole": "Nanomole", + "micromole": "Micromole", + "millimole": "Millimole", + "kilomole": "Chilomole", + "mole-per-cubic-meter": "Mole per metro cubo", + "rssi": "RSSI", + "ppm": "Parti per milione", + "ppb": "Parti per miliardo", + "micrograms-per-cubic-meter": "Microgrammi per metro cubo", + "aqi": "Indice di qualità dell'aria (AQI)", + "gram-per-cubic-meter": "Grammo per metro cubo", + "gram-per-kilogram": "Umidità specifica", + "millimeters-per-second": "Millimetri al secondo", + "neper": "Neper", + "bel": "Bel", + "decibel": "Decibel", + "meters-per-second-squared": "Metri al secondo quadrato", + "becquerel": "Becquerel", + "curie": "Curie", + "gray": "Gray", + "sievert": "Sievert", + "roentgen": "Roentgen", + "cps": "Conteggi al secondo", + "rad": "Rad", + "rem": "Rem", + "dps": "Disintegrazioni al secondo", + "rutherford": "Rutherford", + "coulombs-per-kilogram": "Coulomb per chilogrammo", + "becquerels-per-cubic-meter": "Becquerel per metro cubo", + "curies-per-liter": "Curie per litro", + "becquerels-per-second": "Becquerel al secondo", + "curies-per-second": "Curie al secondo", + "gy-per-second": "Gray al secondo", + "watt-per-steradian": "Watt per steradiante", + "watt-per-square-metre-steradian": "Watt per metro quadrato-steradiante", + "ph-level": "Livello di pH", + "turbidity": "Torbidità", + "mg-per-liter": "Milligrammi per litro", + "microsiemens-per-centimeter": "Microsiemens per centimetro", + "millisiemens-per-meter": "Millisiemens per metro", + "siemens-per-meter": "Siemens per metro", + "kilogram-per-cubic-meter": "Chilogrammo per metro cubo", + "gram-per-cubic-centimeter": "Grammo per centimetro cubo", + "kilogram-per-square-meter": "Chilogrammo per metro quadrato", + "milligram-per-milliliter": "Milligrammo per millilitro", + "milligram-per-cubic-meter": "Milligrammo per metro cubo", + "pound-per-cubic-foot": "Libbra per piede cubo", + "ounces-per-cubic-inch": "Once per pollice cubo", + "tons-per-cubic-yard": "Tonnellate per iarda cubica", + "particle-density": "Densità di particelle", + "kilometers-per-liter": "Chilometri per litro", + "miles-per-gallon": "Miglia per gallone", + "liters-per-100-km": "Litri per 100 km", + "gallons-per-mile": "Galloni per miglio", + "liters-per-hour": "Litri all'ora", + "gallons-per-hour": "Galloni all'ora", + "beats-per-minute": "Battiti per minuto", + "millimeters-of-mercury": "Millimetri di mercurio", + "milligrams-per-deciliter": "Milligrammi per decilitro", + "g-force": "Forza G", + "kilonewton": "Chilonewton", + "kilogram-force": "Chilogrammo-forza", + "pound-force": "Libbra-forza", + "kilopound-force": "Chilolibbra-forza", + "dyne": "Dina", + "poundal": "Poundal", + "kip": "Kip", + "gal": "Gal", + "gravity": "Gravità", + "hectopascal": "Ettopascal", + "atmosphere": "Atmosfera", + "millibars": "Millibar", + "inch-of-mercury": "Pollice di mercurio", + "richter-scale": "Scala Richter", + "second": "Secondo", + "minute": "Minuto", + "hour": "Ora", + "day": "Giorno", + "week": "Settimana", + "month": "Mese", + "year": "Anno", + "cubic-foot-per-minute": "Piede cubo al minuto", + "cubic-meters-per-hour": "Metri cubi all'ora", + "cubic-meters-per-second": "Metri cubi al secondo", + "liter-per-second": "Litri al secondo", + "liter-per-minute": "Litri al minuto", + "gallons-per-minute": "Galloni al minuto", + "cubic-foot-per-second": "Piede cubo al secondo", + "milliliters-per-minute": "Millilitri al minuto", + "bit": "Bit", + "byte": "Byte", + "kilobyte": "Kilobyte", + "megabyte": "Megabyte", + "gigabyte": "Gigabyte", + "terabyte": "Terabyte", + "petabyte": "Petabyte", + "exabyte": "Exabyte", + "zettabyte": "Zettabyte", + "yottabyte": "Yottabyte", + "bit-per-second": "Bit al secondo", + "kilobit-per-second": "Kilobit al secondo", + "megabit-per-second": "Megabit al secondo", + "gigabit-per-second": "Gigabit al secondo", + "terabit-per-second": "Terabit al secondo", + "byte-per-second": "Byte al secondo", + "kilobyte-per-second": "Kilobyte al secondo", + "megabyte-per-second": "Megabyte al secondo", + "gigabyte-per-second": "Gigabyte al secondo", + "degree": "Grado", + "radian": "Radianti", + "gradian": "Gradi centesimali", + "revolution": "Rivoluzione", + "siemens": "Siemens", + "millisiemens": "Millisiemens", + "microsiemens": "Microsiemens", + "kilosiemens": "Kilosiemens", + "megasiemens": "Megasiemens", + "gigasiemens": "Gigasiemens", + "farad": "Farad", + "millifarad": "Millifarad", + "microfarad": "Microfarad", + "nanofarad": "Nanofarad", + "picofarad": "Picofarad", + "kilofarad": "Kilofarad", + "megafarad": "Megafarad", + "gigafarad": "Gigafarad", + "terfarad": "Terfarad", + "farad-per-meter": "Farad per metro", + "tesla": "Tesla", + "gauss": "Gauss", + "kilogauss": "Kilogauss", + "millitesla": "Millitesla", + "microtesla": "Microtesla", + "nanotesla": "Nanotesla", + "kilotesla": "Kilotesla", + "megatesla": "Megatesla", + "millitesla-square-meters": "Millitesla per metro quadrato", + "gamma": "Gamma", + "lambda": "Lambda", + "square-meter-per-second": "Metro quadrato al secondo", + "square-centimeter-per-second": "Centimetro quadrato al secondo", + "stoke": "Stoke", + "centistokes": "Centistoke", + "square-foot-per-second": "Piede quadrato al secondo", + "square-inch-per-second": "Pollice quadrato al secondo", + "pascal-second": "Pascal per secondo", + "centipoise": "Centipoise", + "poise": "Poise", + "reynolds": "Reynolds", + "pound-per-foot-hour": "Libbra per piede-ora", + "newton-second-per-square-meter": "Newton secondo per metro quadrato", + "dyne-second-per-square-centimeter": "Dina secondo per centimetro quadrato", + "kilogram-per-meter-second": "Chilogrammo per metro-secondo", + "tesla-square-meters": "Tesla per metro quadrato", + "maxwell": "Maxwell", + "tesla-per-meter": "Tesla per metro", + "gauss-per-centimeter": "Gauss per centimetro", + "weber": "Weber", + "microweber": "Microweber", + "milliweber": "Milliweber", + "gauss-square-centimeter": "Gauss per centimetro quadrato", + "kilogauss-square-centimeter": "Kilogauss per centimetro quadrato", + "henry": "Henry", + "millihenry": "Millihenry", + "microhenry": "Microhenry", + "nanohenry": "Nanohenry", + "henry-per-meter": "Henry per metro", + "tesla-meter-per-ampere": "Tesla metro per ampere", + "gauss-per-oersted": "Gauss per Oersted", + "kilogram-per-mole": "Chilogrammo per mole", + "gram-per-mole": "Gramma per mole", + "milligram-per-mole": "Milligrammo per mole", + "joule-per-mole": "Joule per mole", + "joule-per-mole-kelvin": "Joule per mole-Kelvin", + "millivolts-per-meter": "Millivolt per metro", + "volts-per-meter": "Volt per metro", + "kilovolts-per-meter": "Kilovolt per metro", + "radian-per-second": "Radianti al secondo", + "radian-per-second-squared": "Radianti al secondo quadrato", + "revolutions-per-minute-per-second": "Accelerazione angolare", + "deg-per-second": "gradi al secondo (°/s)", + "degrees-brix": "Gradi Brix", + "katal": "Katal", + "katal-per-cubic-metre": "Katal per metro cubo" + }, + "user": { + "user": "Utente", + "users": "Utenti", + "customer-users": "Utenti cliente", + "tenant-admins": "Amministratori tenant", + "sys-admin": "Amministratore di sistema", + "tenant-admin": "Amministratore tenant", + "customer": "Cliente", + "anonymous": "Anonimo", + "add": "Aggiungi utente", + "delete": "Elimina utente", + "add-user-text": "Aggiungi nuovo utente", + "no-users-text": "Nessun utente trovato", + "user-details": "Dettagli utente", + "delete-user-title": "Sei sicuro di voler eliminare l'utente '{{userEmail}}'?", + "delete-user-text": "Attenzione, dopo la conferma l'utente e tutti i dati correlati non saranno più recuperabili.", + "delete-users-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 utente} other {# utenti} }?", + "delete-users-action-title": "Elimina { count, plural, =1 {1 utente} other {# utenti} }", + "delete-users-text": "Attenzione, dopo la conferma tutti gli utenti selezionati saranno rimossi e tutti i dati correlati non saranno più recuperabili.", + "activation-email-sent-message": "Email di attivazione inviata con successo!", + "resend-activation": "Reinvia attivazione", + "email": "Email", + "email-required": "Email obbligatoria.", + "invalid-email-format": "Formato email non valido.", + "first-name": "Nome", + "last-name": "Cognome", + "description": "Descrizione", + "default-dashboard": "Dashboard predefinita", + "always-fullscreen": "Sempre a schermo intero", + "select-user": "Seleziona utente", + "no-users-matching": "Nessun utente corrispondente a '{{entity}}' trovato.", + "user-required": "Utente obbligatorio", + "activation-method": "Metodo di attivazione", + "display-activation-link": "Visualizza link di attivazione", + "send-activation-mail": "Invia email di attivazione", + "activation-link": "Link di attivazione utente", + "activation-link-text": "Per attivare l'utente utilizza il seguente link di attivazione (scade tra {{activationLinkTtl}}):", + "copy-activation-link": "Copia link di attivazione", + "activation-link-copied-message": "Il link di attivazione utente è stato copiato negli appunti", + "details": "Dettagli", + "login-as-tenant-admin": "Accedi come amministratore tenant", + "login-as-customer-user": "Accedi come utente cliente", + "search": "Cerca utenti", + "selected-users": "{ count, plural, =1 {1 utente} other {# utenti} } selezionato", + "disable-account": "Disabilita account utente", + "enable-account": "Abilita account utente", + "enable-account-message": "Account utente abilitato con successo!", + "disable-account-message": "Account utente disabilitato con successo!", + "copyId": "Copia ID utente", + "idCopiedMessage": "L'ID utente è stato copiato negli appunti", + "user-list": "Elenco utenti", + "user-list-required": "L'elenco utenti è obbligatorio" + }, + "value": { + "type": "Tipo di valore", + "string": "Stringa", + "string-value": "Valore stringa", + "string-value-required": "Il valore stringa è obbligatorio", + "integer": "Intero", + "integer-value": "Valore intero", + "integer-value-required": "Il valore intero è obbligatorio", + "invalid-integer-value": "Valore intero non valido", + "double": "Double", + "double-value": "Valore double", + "double-value-required": "Il valore double è obbligatorio", + "boolean": "Booleano", + "boolean-value": "Valore booleano", + "false": "Falso", + "true": "Vero", + "long": "Long", + "json": "JSON", + "json-value": "Valore JSON", + "json-value-invalid": "Il valore JSON ha un formato non valido", + "json-value-required": "Il valore JSON è obbligatorio." + }, + "version-control": { + "version-control": "Controllo versioni", + "management": "Gestione controllo versioni", + "search": "Cerca versioni", + "branch": "Ramo", + "default": "Predefinito", + "select-branch": "Seleziona ramo", + "branch-required": "Il ramo è obbligatorio", + "create-entity-version": "Crea versione entità", + "version-name": "Nome versione", + "version-name-required": "Il nome versione è obbligatorio", + "author": "Autore", + "export-relations": "Esporta relazioni", + "export-attributes": "Esporta attributi", + "export-credentials": "Esporta credenziali", + "export-calculated-fields": "Esporta campi calcolati", + "entity-versions": "Versioni entità", + "versions": "Versioni", + "created-time": "Ora di creazione", + "version-id": "ID versione", + "no-entity-versions-text": "Nessuna versione entità trovata", + "no-versions-text": "Nessuna versione trovata", + "copy-full-version-id": "Copia ID versione completo", + "create-version": "Crea versione", + "creating-version": "Creazione versione... Attendere", + "nothing-to-commit": "Nessuna modifica da inviare", + "restore-version": "Ripristina versione", + "restore-entity-from-version": "Ripristina entità dalla versione '{{versionName}}'", + "restoring-entity-version": "Ripristino versione entità... Attendere", + "load-relations": "Carica relazioni", + "load-attributes": "Carica attributi", + "load-credentials": "Carica credenziali", + "load-calculated-fields": "Carica campi calcolati", + "compare-with-current": "Confronta con l'attuale", + "diff-entity-with-version": "Differenze con la versione '{{versionName}}'", + "previous-difference": "Differenza precedente", + "next-difference": "Prossima differenza", + "current": "Attuale", + "differences": "{ count, plural, =1 {1 differenza} other {# differenze} }", + "create-entities-version": "Crea versione entità multiple", + "default-sync-strategy": "Strategia di sincronizzazione predefinita", + "sync-strategy-merge": "Unisci", + "sync-strategy-overwrite": "Sovrascrivi", + "entities-to-export": "Entità da esportare", + "entities-to-restore": "Entità da ripristinare", + "sync-strategy": "Strategia di sincronizzazione", + "all-entities": "Tutte le entità", + "no-entities-to-export-prompt": "Specificare le entità da esportare", + "no-entities-to-restore-prompt": "Specificare le entità da ripristinare", + "add-entity-type": "Aggiungi tipo entità", + "remove-all": "Rimuovi tutto", + "version-create-result": "{ added, plural, =0 {Nessuna entità} =1 {1 entità} other {# entità} } aggiunta.
{ modified, plural, =0 {Nessuna entità} =1 {1 entità} other {# entità} } modificata.
{ removed, plural, =0 {Nessuna entità} =1 {1 entità} other {# entità} } rimossa.", + "remove-other-entities": "Rimuovi altre entità", + "find-existing-entity-by-name": "Trova entità esistente per nome", + "restore-entities-from-version": "Ripristina entità dalla versione '{{versionName}}'", + "restoring-entities-from-version": "Ripristino entità... Attendere", + "no-entities-restored": "Nessuna entità ripristinata", + "created": "{{created}} create", + "updated": "{{updated}} aggiornate", + "deleted": "{{deleted}} eliminate", + "remove-other-entities-confirm-text": "Attenzione! Questo eliminerà in modo permanente tutte le entità attuali
non presenti nella versione che si desidera ripristinare.

Digitare \"remove other entities\" per confermare.", + "auto-commit-to-branch": "auto-commit al ramo {{ branch }}", + "default-create-entity-version-name": "Aggiornamento {{entityName}}", + "sync-strategy-merge-hint": "Crea o aggiorna le entità selezionate nel repository. Tutte le altre entità nel repository non vengono modificate.", + "sync-strategy-overwrite-hint": "Crea o aggiorna le entità selezionate nel repository. Tutte le altre entità nel repository vengono eliminate.", + "device-credentials-conflict": "Impossibile caricare il dispositivo con ID esterno {{entityId}}
poiché le stesse credenziali sono già presenti nel database per un altro dispositivo.
Valutare la possibilità di disabilitare l'opzione carica credenziali nel modulo di ripristino.", + "missing-referenced-entity": "Impossibile caricare {{sourceEntityTypeName}} con ID esterno {{sourceEntityId}}
perché fa riferimento a {{targetEntityTypeName}} mancante con ID {{targetEntityId}}.", + "runtime-failed": "Errore: {{message}}", + "auto-commit-settings-read-only-hint": "La funzione di auto-commit non funziona quando l'opzione di sola lettura è abilitata nelle impostazioni del repository.", + "rollback-on-error": "Rollback in caso di errore", + "rollback-on-error-hint": "Se devi ripristinare un numero elevato di entità, valuta la possibilità di disabilitare questa opzione per migliorare le prestazioni.\nNota: in caso di errore durante il caricamento della versione, le entità già salvate (con relazioni, attributi, ecc.) rimarranno invariate." + }, + "widget": { + "widget-library": "Libreria widget", + "widget-bundle": "Bundle di widget", + "all-bundles": "Tutti i bundle", + "select-widgets-bundle": "Seleziona bundle di widget", + "widgets": "Widget", + "all-widgets": "Tutti i widget", + "widget": "Widget", + "select-widget": "Seleziona widget", + "no-widgets-matching": "Nessun widget corrispondente a '{{entity}}' trovato.", + "no-widgets": "Nessun widget", + "no-widgets-text": "Nessun widget trovato", + "management": "Gestione widget", + "editor": "Editor widget", + "confirm-to-exit-editor-html": "Hai modifiche non salvate al widget.
Sei sicuro di voler lasciare questa pagina?", + "widget-type-not-found": "Problema nel caricamento della configurazione del widget.
Probabilmente il tipo di widget associato è stato rimosso.", + "widget-type-load-error": "Il widget non è stato caricato a causa dei seguenti errori:", + "remove": "Rimuovi widget", + "delete": "Elimina widget", + "edit": "Modifica widget", + "remove-widget-title": "Sei sicuro di voler rimuovere il widget '{{widgetTitle}}'?", + "remove-widget-text": "Dopo la conferma, il widget e tutti i dati correlati non saranno più recuperabili.", + "replace-reference-with-widget-copy": "Sostituisci riferimento con copia del widget", + "timeseries": "Serie temporali", + "search-data": "Cerca dati", + "no-data-found": "Nessun dato trovato", + "latest": "Valori recenti", + "rpc": "Widget di controllo", + "alarm": "Widget allarme", + "static": "Widget statico", + "timeseries-short": "serie", + "latest-short": "recenti", + "rpc-short": "controllo", + "alarm-short": "allarme", + "static-short": "statico", + "select-widget-type": "Seleziona tipo di widget", + "missing-widget-title-error": "Il titolo del widget deve essere specificato!", + "widget-saved": "Widget salvato", + "unable-to-save-widget-error": "Impossibile salvare il widget! Il widget contiene errori!", + "save": "Salva widget", + "saveAs": "Salva widget come", + "move": "Sposta widget", + "save-widget-as": "Salva widget come", + "save-widget-as-text": "Inserisci il nuovo titolo del widget", + "toggle-fullscreen": "Attiva/disattiva schermo intero", + "run": "Esegui widget", + "widget-title": "Titolo del widget", + "title": "Titolo", + "title-required": "Il titolo del widget è obbligatorio.", + "title-max-length": "Il titolo deve contenere meno di 256 caratteri", + "system": "Sistema", + "type": "Tipo di widget", + "resources": "Risorse", + "resource-url": "URL JavaScript/CSS", + "resource-is-extension": "È un'estensione", + "remove-resource": "Rimuovi risorsa", + "add-resource": "Aggiungi risorsa", + "html": "HTML", + "tidy": "Tidy", + "css": "CSS", + "settings-form": "Modulo impostazioni", + "data-key-settings-form": "Modulo impostazioni chiavi dati", + "latest-data-key-settings-form": "Modulo impostazioni dati recenti", + "widget-settings": "Impostazioni widget", + "description": "Descrizione", + "tags": "Tag", + "image-preview": "Anteprima immagine", + "settings-form-selector": "Selettore modulo impostazioni", + "data-key-settings-form-selector": "Selettore modulo chiavi dati", + "latest-data-key-settings-form-selector": "Selettore modulo dati recenti", + "all": "Tutti", + "actual": "Attuale", + "scada": "Simbolo SCADA", + "deprecated": "Obsoleto", + "has-basic-mode": "Ha modalità base", + "basic-mode-form-selector": "Selettore modulo modalità base", + "basic-mode": "Base", + "advanced-mode": "Avanzata", + "javascript": "Javascript", + "js": "JS", + "delete-widget-title": "Sei sicuro di voler eliminare il widget '{{widgetName}}'?", + "delete-widget-text": "Dopo la conferma, il widget e tutti i dati correlati non saranno più recuperabili.", + "delete-widgets-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 widget} other {# widget} }?", + "delete-widgets-text": "Attenzione, dopo la conferma tutti i widget selezionati verranno eliminati e i relativi dati non saranno più recuperabili.", + "delete-widget": "Elimina widget", + "widget-template-load-failed-error": "Impossibile caricare il template del widget!", + "details": "Dettagli", + "widget-details": "Dettagli del widget", + "add": "Aggiungi widget", + "add-existing-widget": "Aggiungi widget esistente", + "add-new-widget": "Aggiungi nuovo widget", + "search-widgets": "Cerca widget", + "selected-widgets": "{ count, plural, =1 {1 widget} other {# widget} } selezionato", + "undo": "Annulla modifiche al widget", + "export": "Esporta widget", + "export-prompt": "Incorpora immagini e risorse del widget", + "export-widgets": "Esporta widget", + "export-widgets-prompt": "Incorpora immagini e risorse dei widget", + "import": "Importa widget", + "no-data": "Nessun dato da visualizzare nel widget", + "data-overflow": "Il widget visualizza {{count}} su {{total}} entità", + "alarm-data-overflow": "Il widget visualizza allarmi per {{allowedEntities}} entità (massimo consentito) su un totale di {{totalEntities}} entità", + "search": "Cerca widget", + "filter": "Tipo di filtro del widget", + "loading-widgets": "Caricamento widget...", + "widget-template-error": "Template HTML del widget non valido.", + "reference": "Riferimento" + }, + "widget-action": { + "header-button": "Pulsante dell'intestazione del widget", + "do-nothing": "Non fare nulla", + "open-dashboard-state": "Naviga verso un nuovo stato della dashboard", + "update-dashboard-state": "Aggiorna lo stato corrente della dashboard", + "open-dashboard": "Naviga verso un'altra dashboard", + "custom": "Azione personalizzata", + "custom-pretty": "Azione personalizzata (con template HTML)", + "custom-pretty-error-title": "Errore del dialog personalizzato", + "custom-pretty-template-error": "Template del dialog personalizzato non valido.", + "custom-pretty-controller-error": "Si è verificato un errore durante la valutazione della funzione del dialog personalizzato.", + "mobile-action": "Azione mobile", + "target-dashboard-state": "Stato della dashboard di destinazione", + "target-dashboard-state-required": "Lo stato della dashboard di destinazione è obbligatorio", + "set-entity-from-widget": "Imposta entità dal widget", + "target-dashboard": "Dashboard di destinazione", + "select-target-dashboard": "Seleziona la dashboard di destinazione", + "target-dashboard-required": "La dashboard di destinazione è obbligatoria.", + "open-right-layout": "Apri layout destro della dashboard (vista mobile)", + "state-display-type": "Opzione di visualizzazione dello stato della dashboard", + "open-normal": "Normale", + "open-in-separate-dialog": "Apri in una finestra di dialogo separata", + "open-in-popover": "Apri in un popover", + "dialog-title": "Titolo del dialog", + "dialog-hide-dashboard-toolbar": "Nascondi la barra strumenti della dashboard nel dialog", + "dialog-width": "Larghezza del dialog in percentuale rispetto alla larghezza della finestra", + "dialog-height": "Altezza del dialog in percentuale rispetto all'altezza della finestra", + "dialog-size-range-error": "Il valore percentuale della dimensione del dialog deve essere compreso tra 1 e 100.", + "popover-preferred-placement": "Posizionamento preferito del popover", + "popover-placement-top": "In alto", + "popover-placement-topLeft": "In alto a sinistra", + "popover-placement-topRight": "In alto a destra", + "popover-placement-right": "A destra", + "popover-placement-rightTop": "In alto a destra", + "popover-placement-rightBottom": "In basso a destra", + "popover-placement-bottom": "In basso", + "popover-placement-bottomLeft": "In basso a sinistra", + "popover-placement-bottomRight": "In basso a destra", + "popover-placement-left": "A sinistra", + "popover-placement-leftTop": "In alto a sinistra", + "popover-placement-leftBottom": "In basso a sinistra", + "popover-hide-on-click-outside": "Chiudi popover cliccando all'esterno", + "popover-hide-dashboard-toolbar": "Nascondi la barra strumenti della dashboard nel popover", + "popover-width": "Larghezza del popover", + "popover-height": "Altezza del popover", + "popover-style": "Stile del popover", + "open-new-browser-tab": "Apri in una nuova scheda del browser", + "open-URL": "Apri URL", + "URL": "URL", + "url-required": "L'URL è obbligatorio.", + "mobile": { + "device-provision": "Provisioning dispositivo", + "action-type": "Tipo di azione mobile", + "select-action-type": "Seleziona tipo di azione mobile", + "action-type-required": "Il tipo di azione mobile è obbligatorio", + "take-picture-from-gallery": "Scatta foto dalla galleria", + "take-photo": "Scatta foto", + "map-direction": "Apri indicazioni mappa", + "map-location": "Apri posizione sulla mappa", + "scan-qr-code": "Scansiona codice QR", + "make-phone-call": "Effettua una chiamata", + "get-location": "Ottieni la posizione del telefono", + "take-screenshot": "Cattura schermata" + }, + "custom-action-function": "Funzione azione personalizzata", + "custom-pretty-function": "Funzione azione personalizzata (con template HTML)", + "map-item-type": "Tipo di elemento mappa", + "map-item": { + "marker": "Indicatore", + "polygon": "Poligono", + "rectangle": "Rettangolo", + "circle": "Cerchio" + }, + "place-map-item": "Posiziona elemento sulla mappa", + "map-item-tooltip": { + "customize-map-item-tooltips": "Personalizza tooltip dell'elemento mappa", + "place-marker": "Posiziona indicatore", + "start-draw-rectangle": "Inizia a disegnare rettangolo", + "finish-draw-rectangle": "Termina disegno rettangolo", + "start-draw-polygon": "Inizia a disegnare poligono", + "continue-draw-polygon": "Continua disegno poligono", + "finish-draw-polygon": "Termina disegno poligono", + "start-draw-circle": "Inizia a disegnare cerchio", + "finish-draw-circle": "Termina disegno cerchio" + } + }, + "widgets-bundle": { + "current": "Bundle corrente", + "widgets-bundles": "Bundle di widget", + "widgets-bundle-widgets": "Widget del bundle", + "add": "Aggiungi bundle di widget", + "delete": "Elimina bundle di widget", + "title": "Titolo", + "title-required": "Il titolo è obbligatorio.", + "title-max-length": "Il titolo deve contenere meno di 256 caratteri", + "description": "Descrizione", + "image-preview": "Anteprima immagine", + "scada": "Bundle di widget SCADA", + "order": "Ordine", + "add-widgets-bundle-text": "Aggiungi nuovo bundle di widget", + "no-widgets-bundles-text": "Nessun bundle di widget trovato", + "empty": "Il bundle di widget è vuoto", + "details": "Dettagli", + "widgets-bundle-details": "Dettagli del bundle di widget", + "delete-widgets-bundle-title": "Sei sicuro di voler eliminare il bundle di widget '{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text": "Attenzione, dopo la conferma il bundle di widget e tutti i dati correlati saranno irrecuperabili.", + "delete-widgets-bundles-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 bundle di widget} other {# bundle di widget} }?", + "delete-widgets-bundles-action-title": "Elimina { count, plural, =1 {1 bundle di widget} other {# bundle di widget} }", + "delete-widgets-bundles-text": "Attenzione, dopo la conferma tutti i bundle di widget selezionati verranno rimossi e tutti i dati correlati saranno irrecuperabili.", + "no-widgets-bundles-matching": "Nessun bundle di widget corrispondente a '{{widgetsBundle}}' trovato.", + "widgets-bundle-required": "Il bundle di widget è obbligatorio.", + "system": "Sistema", + "import": "Importa bundle di widget", + "export": "Esporta bundle di widget", + "export-widgets-bundle-widgets-prompt": "Includi i widget del bundle nei dati esportati (in alternativa verranno esportati solo i FQN dei widget)", + "export-failed-error": "Impossibile esportare il bundle di widget: {{error}}", + "create-new-widgets-bundle": "Crea nuovo bundle di widget", + "widgets-bundle-file": "File del bundle di widget", + "invalid-widgets-bundle-file-error": "Impossibile importare il bundle di widget: struttura dati del file non valida.", + "search": "Cerca bundle di widget", + "selected-widgets-bundles": "{ count, plural, =1 {1 bundle di widget} other {# bundle di widget} } selezionato", + "open-widgets-bundle": "Apri bundle di widget", + "loading-widgets-bundles": "Caricamento dei bundle di widget...", + "create-new": "Crea nuovo bundle di widget" + }, + "widget-config": { + "data": "Dati", + "settings": "Impostazioni", + "advanced": "Avanzate", + "appearance": "Aspetto", + "widget-card": "Scheda del widget", + "mobile": "Mobile", + "title": "Titolo", + "title-tooltip": "Tooltip del titolo", + "general-settings": "Impostazioni generali", + "display-title": "Mostra titolo del widget", + "card-title": "Titolo della scheda", + "drop-shadow": "Ombra esterna", + "enable-fullscreen": "Abilita modalità a schermo intero", + "background-color": "Colore di sfondo", + "text-color": "Colore del testo", + "border-radius": "Raggio del bordo", + "padding": "Padding", + "margin": "Margine", + "widget-style": "Stile del widget", + "widget-css": "CSS del widget", + "title-style": "Stile del titolo", + "mobile-mode-settings": "Modalità mobile", + "order": "Ordine", + "height": "Altezza", + "mobile-hide": "Nascondi il widget in modalità mobile", + "desktop-hide": "Nascondi il widget in modalità desktop", + "units": "Simbolo speciale da mostrare accanto al valore", + "units-by-default": "Unità predefinite", + "decimals": "Numero di cifre dopo la virgola", + "decimals-by-default": "Decimali predefiniti", + "default-data-key-parameter-hint": "Questo parametro si applica a tutti i valori del widget a meno che non sia sovrascritto dalla configurazione della chiave dati", + "units-short": "Unità", + "decimals-short": "Decimali", + "decimals-suffix": "decimali", + "digits-suffix": "cifre", + "timewindow": "Finestra temporale", + "use-dashboard-timewindow": "Usa la finestra temporale della dashboard", + "use-widget-timewindow": "Usa la finestra temporale del widget", + "display-timewindow": "Mostra finestra temporale", + "legend": "Legenda", + "display-legend": "Mostra legenda", + "datasources": "Origini dati", + "datasource": "Origine dati", + "maximum-datasources": "Massimo { count, plural, =1 {è consentita 1 origine dati.} other {sono consentite # origini dati} }", + "timeseries-key-error": "È necessario specificare almeno una chiave dati di serie temporali", + "datasource-type": "Tipo", + "datasource-parameters": "Parametri", + "remove-datasource": "Rimuovi origine dati", + "add-datasource": "Aggiungi origine dati", + "target-device": "Dispositivo di destinazione", + "alarm-source": "Fonte dell'allarme", + "actions": "Azioni", + "action": "Azione", + "add-action": "Aggiungi azione", + "search-actions": "Cerca azioni", + "no-actions-text": "Nessuna azione trovata", + "action-source": "Fonte dell'azione", + "select-action-source": "Seleziona fonte dell'azione", + "action-source-required": "La fonte dell'azione è obbligatoria.", + "column-index": "Indice colonna", + "select-column-index": "Seleziona indice colonna", + "column-index-required": "L'indice colonna è obbligatorio.", + "not-set": "Non impostato", + "action-name": "Nome", + "action-name-required": "Il nome dell'azione è obbligatorio.", + "action-name-not-unique": "Esiste già un'altra azione con lo stesso nome.\nIl nome dell'azione deve essere univoco all'interno della stessa fonte dell'azione.", + "action-icon": "Icona", + "header-button": { + "button-settings": "Impostazioni pulsante", + "button-type": "Tipo di pulsante", + "button-type-basic": "Base", + "button-type-raised": "Sollecitato", + "button-type-stroked": "Contornato", + "button-type-flat": "Piatto", + "button-type-icon": "Icona", + "button-type-mini-fab": "FAB", + "colors": "Colori", + "color": "Colore", + "background": "Sfondo", + "border": "Bordo", + "advanced-button-style": "Stile avanzato del pulsante", + "button-style": "Stile del pulsante" + }, + "show-hide-action-using-function": "Mostra/nascondi azione usando una funzione", + "show-action-function": "Funzione per mostrare azione", + "action-type": "Tipo", + "action-type-required": "Il tipo di azione è obbligatorio.", + "edit-action": "Modifica azione", + "delete-action": "Elimina azione", + "delete-action-title": "Elimina azione del widget", + "delete-action-text": "Sei sicuro di voler eliminare l'azione del widget con nome '{{actionName}}'?", + "title-icon": "Icona del titolo", + "display-icon": "Mostra icona del titolo", + "card-icon": "Icona della scheda", + "icon": "Icona", + "icon-color": "Colore icona", + "icon-size": "Dimensione icona", + "advanced-settings": "Impostazioni avanzate", + "data-settings": "Impostazioni dati", + "limits": "Limiti", + "no-data-display-message": "Messaggio alternativo \"Nessun dato da visualizzare\"", + "data-page-size": "Entità massime per origine dati", + "settings-component-not-found": "Componente del modulo impostazioni non trovato per il selettore '{{selector}}'", + "preview": "Anteprima", + "set": "Imposta", + "set-message": "Imposta messaggio", + "advanced-title-style": "Stile avanzato del titolo", + "card-style": "Stile della scheda", + "text": "Testo", + "background": "Sfondo", + "advanced-widget-style": "Stile avanzato del widget", + "card-buttons": "Pulsanti della scheda", + "show-card-buttons": "Mostra pulsanti della scheda", + "card-border-radius": "Raggio del bordo della scheda", + "card-padding": "Spaziatura interna della scheda", + "card-appearance": "Aspetto della scheda", + "color": "Colore", + "tooltip": "Tooltip", + "units-required": "L'unità è obbligatoria.", + "list-layout": "Layout a elenco", + "layout": "Layout", + "resize-options": "Opzioni di ridimensionamento", + "resizable": "Ridimensionabile", + "preserve-aspect-ratio": "Mantieni proporzioni" + }, + "widget-type": { + "import": "Importa tipo di widget", + "export": "Esporta tipo di widget", + "export-failed-error": "Impossibile esportare il widget: {{error}}", + "widget-file": "File del widget", + "invalid-widget-file-error": "Impossibile importare il widget: struttura dati del widget non valida." + }, + "markdown": { + "edit": "Modifica", + "preview": "Anteprima", + "copy-code": "Clicca per copiare", + "copied": "Copiato!" + }, + "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "La configurazione dipende dal widget Codice QR dell'app mobile nelle impostazioni principali della piattaforma", + "get-it-on-google-play": "Disponibile su Google Play", + "download-on-the-app-store": "Scarica su App Store" + }, + "action-button": { + "behavior": "Comportamento", + "on-click": "Al clic", + "on-click-hint": "Azione attivata quando si fa clic sul pulsante", + "first-button-click": "Clic primo pulsante", + "first-button-click-hint": "Azione eseguita alla pressione del primo pulsante.", + "second-button-click": "Clic secondo pulsante", + "second-button-click-hint": "Azione eseguita alla pressione del secondo pulsante.", + "button-click-hint": "Azione eseguita alla pressione del widget." + }, + "command-button": { + "behavior": "Comportamento", + "on-click": "Al clic", + "on-click-hint": "Azione eseguita quando si fa clic sul pulsante." + }, + "power-button": { + "behavior": "Comportamento", + "power-on": "Accensione 'On'", + "power-on-hint": "Azione eseguita per accendere il componente.", + "power-off": "Spegnimento 'Off'", + "power-off-hint": "Azione eseguita per spegnere il componente.", + "on-label": "Acceso", + "off-label": "Spento", + "layout": "Layout", + "layout-default": "Predefinito", + "layout-simplified": "Semplificato", + "layout-outlined": "Contornato", + "layout-default-volume": "Predefinito.Volume", + "layout-simplified-volume": "Semplificato.Volume", + "layout-outlined-volume": "Contornato.Volume", + "layout-default-icon": "Predefinito.Icona", + "layout-simplified-icon": "Semplificato.Icona", + "layout-outlined-icon": "Contornato.Icona", + "main": "Principale", + "background": "Sfondo", + "button-icon-on": "Icona del pulsante 'On'", + "button-icon-off": "Icona del pulsante 'Off'", + "power-on-colors": "Colori 'On'", + "power-off-colors": "Colori 'Off'", + "disabled-colors": "Colori disabilitati", + "button": "Pulsante" + }, + "toggle-button": { + "behavior": "Comportamento", + "checked": "Selezionato", + "unchecked": "Deselezionato", + "check": "Seleziona", + "check-hint": "Azione eseguita per selezionare il componente.", + "uncheck": "Deseleziona", + "uncheck-hint": "Azione eseguita per deselezionare il componente.", + "auto-scale": "Ridimensionamento automatico", + "horizontal-fill": "Riempimento orizzontale", + "vertical-fill": "Riempimento verticale", + "button-appearance": "Aspetto del pulsante" + }, + "segmented-button": { + "layout": "Layout", + "layout-squared": "Quadrato", + "layout-rounded": "Arrotondato", + "card-border": "Bordo della scheda", + "button-appearance": "Aspetto del pulsante", + "first": "Primo", + "second": "Secondo", + "color-styles": "Stili di colore", + "selected": "Selezionato", + "unselected": "Non selezionato" + }, + "button": { + "layout": "Layout", + "outlined": "Contornato", + "filled": "Riempito", + "underlined": "Sottolineato", + "basic": "Base", + "auto-scale": "Ridimensionamento automatico", + "label": "Etichetta", + "icon": "Icona", + "border-radius": "Raggio del bordo", + "color-palette": "Palette di colori", + "main": "Principale", + "background": "Sfondo", + "border": "Bordo", + "custom-styles": "Stili personalizzati", + "clear-style": "Cancella stile", + "shadow": "Ombra", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "preview": "Anteprima", + "copy-style-from": "Copia stile da" + }, + "value-stepper": { + "behavior": "Comportamento", + "simplified": "Semplificato", + "filled": "Riempito", + "outlined": "Contornato", + "volume": "Volume", + "initial-state": "Stato iniziale", + "initial-state-hint": "Azione per ottenere il valore iniziale.", + "disabled-state": "Stato disabilitato", + "disabled-state-hint": "Configura la condizione in cui il componente è disabilitato.", + "right-button-click": "Clic pulsante destro", + "right-button-click-hint": "Azione eseguita alla pressione del pulsante destro.", + "left-button-click": "Clic pulsante sinistro", + "left-button-click-hint": "Azione eseguita alla pressione del pulsante sinistro.", + "auto-scale": "Ridimensionamento automatico", + "value-range": "Intervallo", + "min-range": "Minimo", + "max-range": "Massimo", + "value-increment-decrement-step": "Passo di incremento/decremento del valore", + "value": "Valore", + "value-box-background": "Sfondo del riquadro valore", + "border": "Bordo", + "button-appearance": "Aspetto del pulsante", + "left": "Sinistra", + "right": "Destra", + "left-button": "Pulsante sinistro", + "right-button": "Pulsante destro", + "icon": "Icona", + "color-palette": "Palette di colori", + "main": "Principale", + "background": "Sfondo", + "button-icon-on": "Icona pulsante 'On'", + "button-on-colors": "Colori pulsante 'On'", + "disabled-colors": "Colori disabilitati" + }, + "button-state": { + "activated-state": "Stato attivato", + "activated-state-hint": "Configura la condizione in cui il pulsante è attivo.", + "disabled-state": "Stato disabilitato", + "disabled-state-hint": "Configura la condizione in cui il pulsante è disabilitato.", + "selected-state": "Stato selezionato", + "selected-state-hint": "Configura la condizione in cui il pulsante è selezionato.", + "enabled": "Abilitato", + "hovered": "Passaggio del mouse", + "pressed": "Premuto", + "activated": "Attivato", + "disabled": "Disabilitato", + "initial": "Primo pulsante", + "first": "Primo", + "second": "Secondo" + }, + "background": { + "background": "Sfondo", + "background-settings": "Impostazioni sfondo", + "background-type-image": "Immagine", + "background-type-color": "Colore", + "image-url": "URL immagine", + "overlay": "Sovrapposizione", + "enable-overlay": "Abilita sovrapposizione", + "blur": "Sfocatura", + "preview": "Anteprima" + }, + "bar-chart": { + "bar-appearance": "Aspetto della barra", + "label-on-bar": "Etichetta sulla barra", + "value-on-bar": "Valore sulla barra", + "bar-chart-style": "Stile grafico a barre", + "bar-axis": "Asse delle barre" + }, + "polar-area-chart": { + "polar-axis": "Asse polare", + "start-angle": "Angolo di partenza", + "polar-area-chart-style": "Stile del grafico ad area polare" + }, + "battery-level": { + "layout": "Layout", + "layout-vertical-solid": "Verticale. Solido", + "layout-horizontal-solid": "Orizzontale. Solido", + "layout-vertical-divided": "Verticale. Diviso", + "layout-horizontal-divided": "Orizzontale. Diviso", + "icon": "Icona", + "value": "Valore", + "auto-scale": "Ridimensionamento automatico", + "battery-level-color": "Colore del livello batteria", + "battery-shape-color": "Colore della forma della batteria", + "battery-level-card-style": "Stile scheda livello batteria", + "sections-count": "Numero di sezioni" + }, + "signal-strength": { + "value": "Valore", + "last-update": "Ultimo aggiornamento", + "no-signal": "Nessun segnale", + "layout": "Layout", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "Barra segnale cellulare", + "icon": "Icona", + "date": "Data", + "active-bars-color": "Colore barre attive", + "inactive-bars-color": "Colore barre inattive", + "signal-strength-card-style": "Stile della scheda segnale", + "no-signal-rssi-value": "Valore RSSI per \"Nessun segnale\"" + }, + "status-widget": { + "behavior": "Comportamento", + "layout": "Layout", + "layout-default": "Predefinito", + "layout-center": "Centrato", + "layout-icon": "Icona", + "on": "Acceso", + "off": "Spento", + "label": "Etichetta", + "status": "Stato", + "icon": "Icona", + "color-palette": "Palette colori", + "disabled-color-palette": "Palette colori disabilitata", + "primary": "Primario", + "primary-color-hint": "Colore di icona ed etichetta", + "secondary": "Secondario", + "secondary-color-hint": "Colore dello stato", + "background": "Sfondo" + }, + "chart": { + "common-settings": "Impostazioni comuni", + "enable-stacking-mode": "Abilita modalità accumulo", + "selection": "Selezione intervallo temporale", + "enable-selection-mode": "Abilita modalità selezione", + "line-shadow-size": "Dimensione ombra linea", + "display-smooth-lines": "Mostra linee curve", + "default-bar-width": "Larghezza barra predefinita per dati non aggregati (millisecondi)", + "bar-alignment": "Allineamento barra", + "bar-alignment-left": "Sinistra", + "bar-alignment-right": "Destra", + "bar-alignment-center": "Centro", + "default-font": "Font predefinito", + "default-font-size": "Dimensione font predefinita", + "default-font-color": "Colore font predefinito", + "thresholds-line-width": "Spessore linea soglia predefinito", + "tooltip-settings": "Impostazioni tooltip", + "tooltip": "Tooltip", + "show-tooltip": "Mostra tooltip", + "hover-individual-points": "Evidenzia punti singoli", + "show-cumulative-values": "Mostra valori cumulativi in modalità accumulo", + "hide-zero-false-values": "Nascondi valori zero/falsi dal tooltip", + "tooltip-value-format-function": "Funzione formato valore tooltip", + "grid-settings": "Impostazioni griglia", + "show-vertical-lines": "Mostra linee verticali", + "show-horizontal-lines": "Mostra linee orizzontali", + "grid-outline-border-width": "Spessore contorno griglia (px)", + "primary-color": "Colore primario", + "background-color": "Colore di sfondo", + "ticks-color": "Colore tacche", + "xaxis-settings": "Impostazioni asse X", + "axis-title": "Titolo asse", + "xaxis-tick-labels-settings": "Etichette tacche asse X", + "show-tick-labels": "Mostra etichette tacche", + "yaxis-settings": "Impostazioni asse Y", + "min-scale-value": "Valore minimo scala", + "max-scale-value": "Valore massimo scala", + "yaxis-tick-labels-settings": "Etichette tacche asse Y", + "tick-step-size": "Intervallo tacche", + "number-of-decimals": "Numero di decimali da visualizzare", + "ticks-formatter-function": "Funzione di formattazione tacche", + "comparison-settings": "Impostazioni confronto", + "enable-comparison": "Abilita confronto", + "time-for-comparison": "Periodo di confronto", + "time-for-comparison-previous-interval": "Intervallo precedente (predefinito)", + "time-for-comparison-days": "Giorno fa", + "time-for-comparison-weeks": "Settimana fa", + "time-for-comparison-months": "Mese fa", + "time-for-comparison-years": "Anno fa", + "time-for-comparison-custom-interval": "Intervallo personalizzato", + "custom-interval-value": "Valore intervallo personalizzato (ms)", + "comparison-x-axis-settings": "Impostazioni asse X confronto", + "axis-position": "Posizione asse", + "axis-position-top": "Alto (predefinito)", + "axis-position-bottom": "Basso", + "custom-legend-settings": "Impostazioni legenda personalizzata", + "enable-custom-legend": "Abilita legenda personalizzata (consente l'uso di attributi/serie temporali nelle etichette)", + "key-name": "Nome chiave", + "key-name-required": "Il nome della chiave è obbligatorio", + "key-type": "Tipo di chiave", + "key-type-attribute": "Attributo", + "key-type-timeseries": "Serie temporale", + "label-keys-list": "Elenco chiavi da usare nelle etichette", + "no-label-keys": "Nessuna chiave configurata", + "add-label-key": "Aggiungi nuova chiave", + "line-width": "Spessore linea", + "color": "Colore", + "data-is-hidden-by-default": "I dati sono nascosti per impostazione predefinita", + "disable-data-hiding": "Disabilita nascondimento dati", + "remove-from-legend": "Rimuovi la chiave dati dalla legenda", + "exclude-from-stacking": "Escludi dall'accumulo (disponibile in modalità \"Accumulamento\")", + "line-settings": "Impostazioni linea", + "show-line": "Mostra linea", + "fill-line": "Riempimento linea", + "fill-line-opacity": "Opacità riempimento", + "points-settings": "Impostazioni punti", + "show-points": "Mostra punti", + "points-line-width": "Spessore linea dei punti", + "points-radius": "Raggio dei punti", + "point-shape": "Forma del punto", + "point-shape-circle": "Cerchio", + "point-shape-cross": "Croce", + "point-shape-diamond": "Diamante", + "point-shape-square": "Quadrato", + "point-shape-triangle": "Triangolo", + "point-shape-custom": "Funzione personalizzata", + "point-shape-draw-function": "Funzione di disegno forma punto", + "show-separate-axis": "Mostra asse separato", + "axis-position-left": "Sinistra", + "axis-position-right": "Destra", + "thresholds": "Soglie", + "no-thresholds": "Nessuna soglia configurata", + "add-threshold": "Aggiungi soglia", + "show-values-for-comparison": "Mostra valori storici per confronto", + "comparison-values-label": "Etichetta valori storici", + "comparison-line-color": "Colore linea di confronto", + "threshold-settings": "Impostazioni soglia", + "use-as-threshold": "Usa il valore chiave come soglia", + "threshold-line-width": "Spessore linea soglia", + "threshold-color": "Colore soglia", + "common-pie-settings": "Impostazioni comuni torta", + "radius": "Raggio", + "inner-radius": "Raggio interno", + "tilt": "Inclinazione", + "common-pie-settings-range-error": "Il valore deve essere compreso tra 0 e 1", + "stroke-settings": "Impostazioni contorno", + "width-pixels": "Larghezza (pixel)", + "show-labels": "Mostra etichette", + "animation-settings": "Impostazioni animazione", + "animated-pie": "Abilita animazione torta (sperimentale)", + "border-settings": "Impostazioni bordo", + "border-width": "Spessore bordo", + "border-color": "Colore bordo", + "legend-settings": "Impostazioni legenda", + "display-legend": "Visualizza legenda", + "labels-font-color": "Colore carattere etichette", + "series": "Serie", + "add-series": "Aggiungi serie", + "series-settings": "Impostazioni serie", + "remove-series": "Rimuovi serie", + "no-series": "Nessuna serie configurata", + "no-series-error": "Deve essere specificata almeno una serie", + "chart-appearance": "Aspetto grafico", + "vertical-grid-lines": "Linee griglia verticali", + "horizontal-grid-lines": "Linee griglia orizzontali", + "chart-background": "Sfondo grafico", + "grid-lines-color": "Colore linee griglia", + "border": "Bordo", + "axis": "Asse", + "vertical-axis": "Asse verticale", + "ticks": "Tacche", + "horizontal-axis": "Asse orizzontale", + "shape-empty-circle": "Cerchio vuoto", + "shape-circle": "Cerchio", + "shape-rect": "Rettangolo", + "shape-round-rect": "Rettangolo arrotondato", + "shape-triangle": "Triangolo", + "shape-diamond": "Diamante", + "shape-pin": "Puntina", + "shape-arrow": "Freccia", + "shape-none": "Nessuna", + "line-type-solid": "Solido", + "line-type-dashed": "Tratteggiato", + "line-type-dotted": "Punteggiato", + "label-position-top": "Alto", + "label-position-bottom": "Basso", + "label-position-outside": "Esterno", + "label-position-inside": "Interno", + "fill": "Riempimento", + "fill-type-none": "Nessuno", + "fill-type-solid": "Solido", + "fill-type-opacity": "Opacità", + "fill-type-gradient": "Gradiente", + "background": "Sfondo", + "opacity": "Opacità", + "gradient-stops": "Punti di gradiente", + "gradient-start": "inizio", + "gradient-end": "fine", + "animation": { + "animation": "Animazione", + "animation-threshold": "Soglia di animazione", + "animation-duration": "Durata animazione", + "animation-easing": "Easing animazione", + "animation-delay": "Ritardo animazione", + "update-animation-duration": "Durata aggiornamento animazione", + "update-animation-easing": "Easing aggiornamento animazione", + "update-animation-delay": "Ritardo aggiornamento animazione" + }, + "chart-axis": { + "scale": "Scala", + "scale-min": "min", + "scale-max": "max", + "scale-auto": "Auto" + }, + "bar": { + "show-border": "Mostra bordo", + "border-width": "Spessore bordo", + "border-radius": "Raggio bordo", + "bar-width": "Larghezza barra", + "label": "Etichetta", + "label-hint": "Visualizza etichetta sopra la barra.", + "series-label-hint": "Visualizza etichetta con valore sopra la barra.", + "label-background": "Sfondo etichetta" + } + }, + "color": { + "color-settings": "Impostazioni colore", + "color-type-constant": "Costante", + "color-type-gradient": "Gradiente", + "color-type-range": "Intervallo", + "color-type-function": "Funzione", + "color": "Colore", + "value-range": "Intervallo di valori", + "from": "Da", + "to": "A", + "color-function": "Funzione colore", + "copy-color-settings-from": "Copia impostazioni colore da", + "copy-from": "Copia da", + "settings-type": "Tipo di impostazioni", + "basic-mode": "Base", + "advanced-mode": "Avanzato", + "entity-alias": "Alias entità", + "entity-attribute": "Attributo entità", + "gradient-color": "Colore gradiente", + "gradient-color-min": "Colore", + "gradient-start": "Colore iniziale del gradiente", + "gradient-start-min": "Inizio", + "gradient-end": "Colore finale del gradiente", + "gradient-end-min": "Fine", + "start-value": "Valore iniziale", + "end-value": "Valore finale", + "gradient-type": "Tipo di gradiente" + }, + "dashboard-state": { + "dashboard-state-settings": "Impostazioni stato dashboard", + "dashboard-state": "ID stato dashboard", + "autofill-state-layout": "Compila automaticamente altezza layout stato per impostazione predefinita", + "default-margin": "Margine predefinito dei widget", + "default-background-color": "Colore di sfondo predefinito", + "sync-parent-state-params": "Sincronizza parametri dello stato con il dashboard padre" + }, + "date-range-navigator": { + "date-range-picker-settings": "Impostazioni selezione intervallo date", + "hide-date-range-picker": "Nascondi selezione intervallo date", + "picker-one-panel": "Selettore intervallo date con un pannello", + "picker-auto-confirm": "Conferma automatica selezione intervallo date", + "picker-show-template": "Mostra template selezione intervallo date", + "first-day-of-week": "Primo giorno della settimana", + "interval-settings": "Impostazioni intervallo", + "hide-interval": "Nascondi intervallo", + "initial-interval": "Intervallo iniziale", + "interval-hour": "Ora", + "interval-day": "Giorno", + "interval-week": "Settimana", + "interval-two-weeks": "2 settimane", + "interval-month": "Mese", + "interval-three-months": "3 mesi", + "interval-six-months": "6 mesi", + "step-settings": "Impostazioni passo", + "hide-step-size": "Nascondi dimensione passo", + "initial-step-size": "Dimensione passo iniziale", + "hide-labels": "Nascondi etichette", + "use-session-storage": "Usa storage della sessione", + "localizationMap": { + "Sun": "Dom", + "Mon": "Lun", + "Tue": "Mar", + "Wed": "Mer", + "Thu": "Gio", + "Fri": "Ven", + "Sat": "Sab", + "Jan": "Gen", + "Feb": "Feb", + "Mar": "Mar", + "Apr": "Apr", + "May": "Mag", + "Jun": "Giu", + "Jul": "Lug", + "Aug": "Ago", + "Sep": "Set", + "Oct": "Ott", + "Nov": "Nov", + "Dec": "Dic", + "January": "Gennaio", + "February": "Febbraio", + "March": "Marzo", + "April": "Aprile", + "June": "Giugno", + "July": "Luglio", + "August": "Agosto", + "September": "Settembre", + "October": "Ottobre", + "November": "Novembre", + "December": "Dicembre", + "Custom Date Range": "Intervallo date personalizzato", + "Date Range Template": "Template intervallo date", + "Today": "Oggi", + "Yesterday": "Ieri", + "This Week": "Questa settimana", + "Last Week": "Settimana scorsa", + "This Month": "Questo mese", + "Last Month": "Mese scorso", + "Year": "Anno", + "This Year": "Quest'anno", + "Last Year": "Anno scorso", + "Date picker": "Selettore data", + "Hour": "Ora", + "Day": "Giorno", + "Week": "Settimana", + "2 weeks": "2 settimane", + "Month": "Mese", + "3 months": "3 mesi", + "6 months": "6 mesi", + "Custom interval": "Intervallo personalizzato", + "Interval": "Intervallo", + "Step size": "Dimensione passo", + "Ok": "Ok" + } + }, + "doughnut": { + "doughnut-appearance": "Aspetto del doughnut", + "layout": "Layout", + "layout-default": "Predefinito", + "layout-with-total": "Con totale", + "central-total-value": "Valore totale centrale", + "doughnut-card-style": "Stile della scheda doughnut" + }, + "entities-hierarchy": { + "hierarchy-data-settings": "Impostazioni dei dati della gerarchia", + "relations-query-function": "Funzione di query delle relazioni del nodo", + "has-children-function": "Funzione che verifica se il nodo ha figli", + "node-state-settings": "Impostazioni dello stato del nodo", + "node-opened-function": "Funzione predefinita per nodo aperto", + "node-disabled-function": "Funzione nodo disabilitato", + "display-settings": "Impostazioni di visualizzazione", + "node-icon-function": "Funzione icona del nodo", + "node-text-function": "Funzione testo del nodo", + "sort-settings": "Impostazioni di ordinamento", + "nodes-sort-function": "Funzione di ordinamento dei nodi" + }, + "edge": { + "display-default-title": "Visualizza titolo predefinito" + }, + "gateway": { + "general-settings": "Impostazioni generali", + "widget-title": "Titolo del widget", + "default-archive-file-name": "Nome file di archivio predefinito", + "device-type-for-new-gateway": "Tipo di dispositivo per nuovo gateway", + "messages-settings": "Impostazioni dei messaggi", + "save-config-success-message": "Messaggio di conferma salvataggio configurazione gateway", + "device-name-exists-message": "Messaggio di errore: nome dispositivo già esistente", + "gateway-title": "Modulo gateway", + "read-only": "Sola lettura", + "events-title": "Titolo modulo eventi gateway", + "events-filter": "Filtro eventi", + "event-key-contains": "Chiave evento contiene...", + "show-connector": "Mostra per il connettore", + "connector-state-param-key": "Chiave parametro stato connettore", + "message": "Messaggio", + "level": "Livello", + "created-time": "Data di creazione" + }, + "gauge": { + "default-color": "Colore predefinito", + "radial-gauge-settings": "Impostazioni del gauge radiale", + "ticks-settings": "Impostazioni dei tick", + "min-value": "Valore minimo", + "max-value": "Valore massimo", + "min-value-short": "min", + "max-value-short": "max", + "start-ticks-angle": "Angolo di inizio dei tick", + "ticks-angle": "Angolo dei tick", + "major-ticks": "Tick principali", + "major-ticks-count": "Numero di tick principali", + "major-ticks-color": "Colore dei tick principali", + "minor-ticks": "Tick secondari", + "minor-ticks-count": "Numero di tick secondari", + "minor-ticks-color": "Colore dei tick secondari", + "tick-numbers-font": "Font dei numeri dei tick", + "unit-title-settings": "Impostazioni del titolo dell'unità", + "show-unit-title": "Titolo unità", + "unit-title": "Titolo unità", + "title-font": "Font del testo del titolo", + "units-settings": "Impostazioni delle unità", + "units-font": "Font del testo delle unità", + "value-box-settings": "Impostazioni della casella valore", + "show-value-box": "Mostra casella valore", + "value-box": "Casella valore", + "value-int": "Numero di cifre per la parte intera del valore", + "value-text": "Testo del valore", + "value-text-shadow": "Ombra del testo del valore", + "value-font": "Font del testo del valore", + "rect-stroke-color-start": "Colore iniziale tratto rettangolo", + "rect-stroke-color-end": "Colore finale tratto rettangolo", + "background-color": "Colore di sfondo", + "shadow-color": "Colore dell'ombra", + "value-box-rect-stroke-color": "Colore tratto rettangolo casella valore", + "value-box-rect-stroke-color-end": "Colore finale tratto rettangolo casella valore", + "value-box-background-color": "Colore di sfondo della casella valore", + "value-box-shadow-color": "Colore dell'ombra della casella valore", + "plate-settings": "Impostazioni della piastra", + "show-plate-border": "Bordo della piastra", + "plate-color": "Colore della piastra", + "needle-settings": "Impostazioni dell'indicatore", + "needle-circle-size": "Dimensione cerchio indicatore", + "needle-color": "Colore dell'indicatore", + "needle-color-start": "Colore iniziale dell'indicatore", + "needle-color-end": "Colore finale dell'indicatore", + "needle-color-shadow-up": "Colore dell'ombra superiore dell'indicatore", + "needle-color-shadow-down": "Ombra", + "highlights-settings": "Impostazioni evidenziazioni", + "highlights-width": "Larghezza evidenziazioni", + "highlights": "Evidenziazioni", + "highlight-from": "Da", + "highlight-to": "A", + "highlight-color": "Colore", + "no-highlights": "Nessuna evidenziazione configurata", + "add-highlight": "Aggiungi evidenziazione", + "animation-settings": "Impostazioni dell'animazione", + "enable-animation": "Animazione", + "animation-duration-rule": "Durata e regola dell'animazione", + "animation-duration": "Durata dell'animazione", + "animation-rule": "Regola dell'animazione", + "animation-linear": "Lineare", + "animation-quad": "Quad", + "animation-quint": "Quint", + "animation-cycle": "Ciclo", + "animation-bounce": "Rimbalzo", + "animation-elastic": "Elastica", + "animation-dequad": "Dequad", + "animation-dequint": "Dequint", + "animation-decycle": "Deciclo", + "animation-debounce": "Debounce", + "animation-delastic": "Delastica", + "linear-gauge-settings": "Impostazioni gauge lineare", + "bar-stroke": "Tratto della barra", + "bar-stroke-width": "Larghezza tratto della barra", + "bar-stroke-color": "Colore tratto della barra", + "bar-background-color": "Colore di sfondo della barra - gradiente iniziale", + "bar-background-color-end": "Colore di sfondo della barra - gradiente finale", + "progress-bar-color": "Colore della barra di avanzamento", + "progress-bar": "Barra di avanzamento", + "progress-bar-color-start": "Colore barra di avanzamento - gradiente iniziale", + "progress-bar-color-end": "Colore barra di avanzamento - gradiente finale", + "major-ticks-names": "Nomi dei tick principali", + "show-stroke-ticks": "Mostra tratto dei tick", + "major-ticks-font": "Font dei tick principali", + "border-color": "Colore del bordo", + "border-width": "Larghezza del bordo", + "needle-circle": "Cerchio dell'indicatore", + "needle-circle-color": "Colore del cerchio dell'indicatore", + "animation-target": "Obiettivo dell'animazione", + "animation-target-needle": "Indicatore", + "animation-target-plate": "Piastra", + "common-settings": "Impostazioni comuni del gauge", + "gauge-type": "Tipo di gauge", + "gauge-type-arc": "Arco", + "gauge-type-donut": "Ciambella", + "gauge-type-horizontal-bar": "Barra orizzontale", + "gauge-type-vertical-bar": "Barra verticale", + "donut-start-angle": "Angolo di inizio (gradi)", + "bar-settings": "Impostazioni della barra del gauge", + "relative-bar-width": "Larghezza relativa della barra", + "neon-glow-brightness": "Luminosità dell'effetto bagliore neon (0-100)", + "neon-glow-brightness-hint": "0 - disabilita effetto", + "stripes-thickness": "Spessore delle strisce", + "stripes-thickness-hint": "0 - nessuna striscia", + "rounded-line-cap": "Estremità linea arrotondata", + "bar-color-settings": "Impostazioni colore della barra", + "use-precise-level-color-values": "Usa valori di colore precisi", + "bar-colors": "Colori della barra, da minore a maggiore", + "color": "Colore", + "no-bar-colors": "Nessun colore della barra configurato", + "add-bar-color": "Aggiungi colore della barra", + "from": "Da", + "to": "A", + "fixed-level-colors": "Colori della barra con valori limite", + "gauge-title-settings": "Impostazioni del titolo del gauge", + "show-gauge-title": "Mostra il titolo del gauge", + "gauge-title": "Titolo del gauge", + "gauge-title-font": "Font del titolo del gauge", + "unit-title-and-timestamp-settings": "Impostazioni unità e timestamp", + "show-timestamp": "Timestamp", + "timestamp-format": "Formato timestamp", + "label-font": "Font dell'etichetta sotto il valore", + "value-settings": "Impostazioni del valore", + "show-value": "Mostra testo del valore", + "min-max-settings": "Impostazioni etichette minimo/massimo", + "show-min-max": "Mostra valori minimo e massimo", + "min-max-font": "Font delle etichette minimo e massimo", + "show-ticks": "Mostra i tick", + "tick-width": "Larghezza del tick", + "tick-color": "Colore del tick", + "tick-values": "Valori dei tick", + "no-tick-values": "Nessun valore tick configurato", + "add-tick-value": "Aggiungi valore tick", + "gauge-appearance": "Aspetto del gauge", + "units-title": "Titolo delle unità", + "value": "Valore", + "ticks": "Tick", + "arrow-and-scale-color": "Colore predefinito di freccia e scala", + "scale-settings": "Impostazioni della scala", + "scale": "Scala", + "scale-color": "Colori della scala", + "compass-appearance": "Aspetto della bussola", + "label": "Etichetta", + "labels": "Etichette", + "label-style": "Stile etichetta", + "simple-gauge-type": "Tipo", + "gauge-bar-background": "Sfondo della barra del gauge", + "bar-color": "Colore della barra", + "min-and-max-value": "Valore minimo e massimo", + "min-and-max-label": "Etichetta minimo e massimo", + "font": "Font", + "tick-width-and-color": "Spessore e colore del tick", + "min-max-validation-text": "Il valore massimo deve essere maggiore del valore minimo" + }, + "gpio": { + "pin": "Pin", + "label": "Etichetta", + "row": "Riga", + "column": "Colonna", + "color": "Colore", + "panel-settings": "Impostazioni del pannello", + "background-color": "Colore di sfondo", + "gpio-switches": "Interruttori GPIO", + "no-gpio-switches": "Nessun interruttore GPIO configurato", + "add-gpio-switch": "Aggiungi interruttore GPIO", + "gpio-status-request": "Richiesta stato GPIO", + "method-name": "Nome del metodo", + "method-body": "Corpo del metodo", + "gpio-status-change-request": "Richiesta di modifica stato GPIO", + "parse-gpio-status-function": "Funzione per l'analisi dello stato GPIO", + "gpio-leds": "LED GPIO", + "no-gpio-leds": "Nessun LED GPIO configurato", + "add-gpio-led": "Aggiungi LED GPIO" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, + "input-widgets": { + "attribute-not-allowed": "Il parametro attributo non può essere usato in questo widget", + "blocked-location": "La geolocalizzazione è bloccata nel tuo browser", + "claim-device": "Rivendica dispositivo", + "claim-failed": "Rivendicazione del dispositivo non riuscita!", + "claim-not-found": "Dispositivo non trovato!", + "claim-successful": "Dispositivo rivendicato con successo!", + "date": "Data", + "device-name": "Nome del dispositivo", + "device-name-required": "Il nome del dispositivo è obbligatorio", + "discard-changes": "Annulla modifiche", + "entity-attribute-required": "Attributo dell'entità obbligatorio", + "entity-coordinate-required": "Entrambi i campi, latitudine e longitudine, sono obbligatori", + "entity-timeseries-required": "Serie temporale dell'entità obbligatoria", + "get-location": "Ottieni posizione attuale", + "invalid-date": "Data non valida", + "latitude": "Latitudine", + "longitude": "Longitudine", + "min-value-error": "Valore minimo è {{value}}", + "max-value-error": "Valore massimo è {{value}}", + "not-allowed-entity": "L'entità selezionata non può avere attributi condivisi", + "no-attribute-selected": "Nessun attributo selezionato", + "no-datakey-selected": "Nessuna chiave dati selezionata", + "no-coordinate-specified": "Chiave dati per latitudine/longitudine non specificata", + "no-entity-selected": "Nessuna entità selezionata", + "no-image": "Nessuna immagine", + "no-support-geolocation": "Il tuo browser non supporta la geolocalizzazione", + "no-support-web-camera": "Il tuo browser non supporta le telecamere", + "enable-https-use-widget": "Abilita HTTPS per usare questo widget", + "no-found-your-camera": "Telecamera non trovata", + "no-permission-camera": "Permesso negato dall'utente / Questo sito non ha il permesso per usare la telecamera", + "no-timeseries-selected": "Nessuna serie temporale selezionata", + "secret-key": "Chiave segreta", + "secret-key-required": "Chiave segreta obbligatoria", + "switch-attribute-value": "Commuta valore attributo entità", + "switch-camera": "Cambia fotocamera", + "switch-timeseries-value": "Commuta valore serie temporale entità", + "take-photo": "Scatta foto", + "time": "Ora", + "timeseries-not-allowed": "Il parametro serie temporale non può essere usato in questo widget", + "update-failed": "Aggiornamento fallito", + "update-successful": "Aggiornamento riuscito", + "update-attribute": "Aggiorna attributo", + "update-timeseries": "Aggiorna serie temporale", + "value": "Valore", + "general-settings": "Impostazioni generali", + "widget-title": "Titolo del widget", + "claim-button-label": "Etichetta del pulsante di rivendicazione", + "show-secret-key-field": "Mostra campo di inserimento 'Chiave segreta'", + "labels-settings": "Impostazioni etichette", + "show-labels": "Mostra etichette", + "device-name-label": "Etichetta per il campo nome del dispositivo", + "secret-key-label": "Etichetta per il campo chiave segreta", + "messages-settings": "Impostazioni messaggi", + "claim-device-success-message": "Messaggio di successo per la rivendicazione del dispositivo", + "claim-device-not-found-message": "Messaggio quando il dispositivo non viene trovato", + "claim-device-failed-message": "Messaggio di errore nella rivendicazione del dispositivo", + "claim-device-name-required-message": "Messaggio di errore 'Nome del dispositivo obbligatorio'", + "claim-device-secret-key-required-message": "Messaggio di errore 'Chiave segreta obbligatoria'", + "show-label": "Mostra etichetta", + "label": "Etichetta", + "required": "Obbligatorio", + "required-error-message": "Messaggio di errore 'Obbligatorio'", + "show-result-message": "Mostra messaggio di risultato", + "integer-field-settings": "Impostazioni campo intero", + "min-value": "Valore minimo", + "max-value": "Valore massimo", + "double-field-settings": "Impostazioni campo decimale", + "text-field-settings": "Impostazioni campo di testo", + "min-length": "Lunghezza minima", + "max-length": "Lunghezza massima", + "checkbox-settings": "Impostazioni casella di controllo", + "true-label": "Etichetta selezionata", + "false-label": "Etichetta deselezionata", + "image-input-settings": "Impostazioni input immagine", + "display-preview": "Mostra anteprima", + "display-clear-button": "Mostra pulsante 'Cancella'", + "display-apply-button": "Mostra pulsante 'Applica'", + "display-discard-button": "Mostra pulsante 'Annulla'", + "datetime-field-settings": "Impostazioni campo data/ora", + "display-time-input": "Mostra input dell'ora", + "latitude-key-name": "Nome chiave latitudine", + "longitude-key-name": "Nome chiave longitudine", + "show-get-location-button": "Mostra pulsante 'Ottieni posizione attuale'", + "use-high-accuracy": "Usa alta precisione", + "location-fields-settings": "Impostazioni campi posizione", + "latitude-label": "Etichetta per latitudine", + "longitude-label": "Etichetta per longitudine", + "input-fields-alignment": "Allineamento campi di input", + "input-fields-alignment-column": "Colonna (predefinito)", + "input-fields-alignment-row": "Riga", + "layout": "Layout", + "row-gap": "Spazio tra le righe in pixel", + "column-gap": "Spazio tra le colonne in pixel", + "latitude-field-required": "Campo latitudine obbligatorio", + "longitude-field-required": "Campo longitudine obbligatorio", + "attribute-settings": "Impostazioni attributo", + "widget-mode": "Modalità widget", + "widget-mode-update-attribute": "Aggiorna attributo", + "widget-mode-update-timeseries": "Aggiorna serie temporale", + "attribute-scope": "Ambito attributo", + "attribute-scope-server": "Attributo del server", + "attribute-scope-shared": "Attributo condiviso", + "value-required": "Valore obbligatorio", + "image-settings": "Impostazioni immagine", + "image-format": "Formato immagine", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "Qualità immagine per formati compressi come jpeg e webp", + "max-image-width": "Larghezza massima immagine", + "max-image-height": "Altezza massima immagine", + "action-buttons": "Pulsanti azione", + "show-action-buttons": "Mostra pulsanti azione", + "update-all-values": "Aggiorna tutti i valori, non solo quelli modificati", + "save-button-label": "Etichetta pulsante 'SALVA'", + "reset-button-label": "Etichetta pulsante 'ANNULLA'", + "group-settings": "Impostazioni gruppo", + "show-group-title": "Mostra titolo per gruppo di campi relativi a entità diverse", + "group-title": "Titolo del gruppo", + "fields-alignment": "Allineamento campi", + "fields-alignment-row": "Riga (predefinito)", + "fields-alignment-column": "Colonna", + "fields-in-row": "Numero di campi nella riga", + "option-value": "Valore (scrivi 'null' per creare un'opzione vuota)", + "option-label": "Etichetta", + "hide-input-field": "Nascondi campo di input", + "datakey-type": "Tipo di chiave dati", + "datakey-type-server": "Attributo del server (predefinito)", + "datakey-type-shared": "Attributo condiviso", + "datakey-type-timeseries": "Serie temporale", + "datakey-value-type": "Tipo di valore della chiave dati", + "datakey-value-type-string": "Stringa", + "datakey-value-type-double": "Double", + "datakey-value-type-integer": "Intero", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "Booleano (Casella di controllo)", + "datakey-value-type-boolean-switch": "Booleano (Interruttore)", + "datakey-value-type-date-time": "Data e ora", + "datakey-value-type-date": "Data", + "datakey-value-type-time": "Ora", + "datakey-value-type-select": "Selezione", + "datakey-value-type-radio": "Radio", + "datakey-value-type-color": "Colore", + "value-is-required": "Valore obbligatorio", + "ability-to-edit-attribute": "Possibilità di modificare l'attributo", + "ability-to-edit-attribute-editable": "Modificabile (predefinito)", + "ability-to-edit-attribute-disabled": "Disabilitato", + "ability-to-edit-attribute-readonly": "Sola lettura", + "disable-on-datakey-name": "Disabilita in base al valore falso di un'altra chiave dati (specificare il nome della chiave)", + "field-appearance": "Aspetto del campo", + "appearance-fill": "Riempimento", + "appearance-outline": "Contorno", + "subscript-sizing": "Dimensionamento del pedice", + "subscript-sizing-fixed": "Fisso", + "subscript-sizing-dynamic": "Dinamico", + "slide-toggle-settings": "Impostazioni interruttore scorrevole", + "slide-toggle-label-position": "Posizione etichetta interruttore", + "slide-toggle-label-position-after": "Dopo", + "slide-toggle-label-position-before": "Prima", + "select-options": "Opzioni di selezione", + "no-select-options": "Nessuna opzione di selezione configurata", + "add-select-option": "Aggiungi opzione di selezione", + "numeric-field-settings": "Impostazioni campo numerico", + "step-interval": "Intervallo di passo tra i valori", + "error-messages": "Messaggi di errore", + "min-value-error-message": "Messaggio di errore 'Valore minimo'", + "max-value-error-message": "Messaggio di errore 'Valore massimo'", + "invalid-date-error-message": "Messaggio di errore 'Data non valida'", + "invalid-JSON-error-message": "Messaggio di errore 'JSON non valido'", + "icon-settings": "Impostazioni icona", + "dialog-editor-settings": "Impostazioni editor dialogo", + "use-custom-icon": "Usa icona personalizzata", + "input-cell-icon": "Icona da mostrare prima della cella di input", + "value-conversion-settings": "Impostazioni di conversione del valore", + "get-value-settings": "Impostazioni per ottenere il valore", + "use-get-value-function": "Usa la funzione getValue", + "get-value-function": "Funzione getValue", + "set-value-settings": "Impostazioni per impostare il valore", + "use-set-value-function": "Usa la funzione setValue", + "set-value-function": "Funzione setValue", + "json-invalid": "Il valore JSON ha un formato non valido", + "title": "Titolo", + "cancel-button-label": "Etichetta del pulsante 'Annulla'", + "radio-button-settings": "Impostazioni pulsante radio", + "color": "Colore", + "columns": "Colonne", + "radio-options": "Opzioni radio", + "no-radio-options": "Nessuna opzione radio configurata", + "add-radio-option": "Aggiungi opzione radio", + "radio-label-position": "Posizione dell'etichetta", + "radio-label-position-before": "Prima", + "radio-label-position-after": "Dopo" + }, + "invalid-qr-code-text": "Testo non valido per codice QR. L'input deve essere di tipo stringa", + "qr-code": { + "use-qr-code-text-function": "Usa la funzione testo del codice QR", + "qr-code-text-pattern": "Modello di testo del codice QR (ad es. '${entityName} | ${keyName} - some text.')", + "qr-code-text-pattern-hint": "Il modello di testo del codice QR usa il valore della prima chiave trovata nelle entità dell'alias dell'entità.", + "qr-code-text-pattern-required": "Il modello di testo del codice QR è obbligatorio.", + "qr-code-text-function": "Funzione testo del codice QR" + }, + "label-widget": { + "label-pattern": "Modello", + "label-pattern-hint": "Suggerimento: ad esempio, 'Testo ${keyName} unità.' oppure ${#<indice chiave>} unità'", + "label-pattern-required": "Il pattern è obbligatorio", + "label-position": "Posizione (percentuale rispetto allo sfondo)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Colore di sfondo", + "font-settings": "Impostazioni font", + "background-image": "Immagine di sfondo", + "labels": "Etichette", + "no-labels": "Nessuna etichetta configurata", + "add-label": "Aggiungi etichetta" + }, + "navigation": { + "title": "Titolo", + "navigation-path": "Percorso di navigazione", + "filter-type": "Tipo di filtro", + "filter-type-all": "Tutti gli elementi", + "filter-type-include": "Includi elementi", + "filter-type-exclude": "Escludi elementi", + "items": "Elementi", + "enter-urls-to-filter": "Inserisci URL da filtrare..." + }, + "persistent-table": { + "rpc-id": "ID RPC", + "message-type": "Tipo di messaggio", + "method": "Metodo", + "params": "Parametri", + "created-time": "Data creazione", + "expiration-time": "Data scadenza", + "retries": "Tentativi", + "status": "Stato", + "filter": "Filtro", + "refresh": "Aggiorna", + "add": "Aggiungi richiesta RPC", + "details": "Dettagli", + "delete": "Elimina", + "delete-request-title": "Elimina richiesta RPC persistente", + "delete-request-text": "Sei sicuro di voler eliminare la richiesta?", + "details-title": "Dettagli ID RPC: ", + "additional-info": "Informazioni aggiuntive", + "response": "Risposta", + "any-status": "Qualsiasi stato", + "rpc-status-list": "Elenco stati RPC", + "no-request-prompt": "Nessuna richiesta da visualizzare", + "send-request": "Invia richiesta", + "add-title": "Crea richiesta RPC persistente", + "method-error": "Il metodo è obbligatorio.", + "timeout-error": "Il valore minimo del timeout è 5000 (5 secondi).", + "white-space-error": "Gli spazi bianchi non sono consentiti.", + "rpc-status": { + "QUEUED": "IN CODA", + "SENT": "INVIATO", + "DELIVERED": "CONSEGNATO", + "SUCCESSFUL": "RIUSCITO", + "TIMEOUT": "TIMEOUT", + "EXPIRED": "SCADUTO", + "FAILED": "FALLITO" + }, + "rpc-search-status-all": "TUTTI", + "message-types": { + "false": "Bidirezionale", + "true": "Monodirezionale" + }, + "general-settings": "Impostazioni generali", + "enable-filter": "Abilita filtro", + "enable-sticky-header": "Visualizza intestazione durante lo scorrimento", + "enable-sticky-action": "Visualizza colonna azioni durante lo scorrimento", + "display-request-details": "Visualizza dettagli richiesta", + "allow-send-request": "Permetti invio richiesta RPC", + "allow-delete-request": "Permetti eliminazione richiesta", + "columns-settings": "Impostazioni colonne", + "display-columns": "Colonne da visualizzare", + "column": "Colonna", + "no-columns-found": "Nessuna colonna trovata", + "no-columns-matching": "'{{column}}' non trovata." + }, + "range-chart": { + "chart": "Grafico", + "data-zoom": "Zoom dati", + "range-chart-appearance": "Aspetto grafico intervallo", + "range-colors": "Colori intervallo", + "out-of-range-color": "Colore fuori intervallo", + "show-range-thresholds": "Mostra soglie di intervallo", + "range-thresholds-settings": "Impostazioni soglie intervallo", + "fill-area": "Area riempita", + "fill-area-opacity": "Opacità area riempita", + "range-chart-style": "Stile grafico intervallo" + }, + "rpc": { + "value-settings": "Impostazioni valore", + "initial-value": "Valore iniziale", + "retrieve-value-settings": "Impostazioni per recupero valore on/off", + "retrieve-value-method": "Recupera valore usando metodo", + "retrieve-value-method-none": "Non recuperare", + "retrieve-value-method-rpc": "Chiama metodo RPC per ottenere valore", + "retrieve-value-method-attribute": "Sottoscrivi a un attributo", + "retrieve-value-method-timeseries": "Sottoscrivi a una serie temporale", + "attribute-value-key": "Chiave attributo", + "timeseries-value-key": "Chiave serie temporale", + "get-value-method": "Metodo RPC per ottenere valore", + "parse-value-function": "Funzione per analizzare valore", + "update-value-settings": "Impostazioni aggiornamento valore", + "set-value-method": "Metodo RPC per impostare valore", + "convert-value-function": "Funzione per convertire valore", + "rpc-settings": "Impostazioni RPC", + "request-timeout": "Timeout richiesta RPC (ms)", + "persistent-rpc-settings": "Impostazioni RPC persistente", + "request-persistent": "Richiesta RPC persistente", + "persistent-polling-interval": "Intervallo polling (ms) per risposta comando RPC persistente", + "common-settings": "Impostazioni comuni", + "switch-title": "Titolo switch", + "show-on-off-labels": "Mostra etichette on/off", + "slide-toggle-label": "Etichetta slide toggle", + "label-position": "Posizione etichetta", + "label-position-before": "Prima", + "label-position-after": "Dopo", + "slider-color": "Colore slider", + "slider-color-primary": "Primario", + "slider-color-accent": "Accentato", + "slider-color-warn": "Avviso", + "button-style": "Stile pulsante", + "button-raised": "Pulsante in rilievo", + "button-primary": "Colore primario", + "button-background-color": "Colore sfondo pulsante", + "button-text-color": "Colore testo pulsante", + "widget-title": "Titolo widget", + "button-label": "Etichetta pulsante", + "device-attribute-scope": "Ambito attributo dispositivo", + "server-attribute": "Attributo server", + "shared-attribute": "Attributo condiviso", + "device-attribute-parameters": "Parametri attributo dispositivo", + "is-one-way-command": "È comando monodirezionale", + "rpc-method": "Metodo RPC", + "rpc-method-params": "Parametri metodo RPC", + "show-rpc-error": "Mostra errore esecuzione comando RPC", + "led-title": "Titolo LED", + "led-color": "Colore LED", + "check-status-settings": "Impostazioni controllo stato", + "perform-rpc-status-check": "Esegui controllo stato dispositivo tramite RPC", + "retrieve-led-status-value-method": "Recupera valore stato LED usando metodo", + "led-status-value-attribute": "Attributo dispositivo con valore stato LED", + "led-status-value-timeseries": "Serie temporale dispositivo con valore stato LED", + "check-status-method": "Metodo RPC per controllo stato dispositivo", + "parse-led-status-value-function": "Funzione per analisi valore stato LED", + "knob-title": "Titolo manopola", + "min-value": "Valore minimo", + "max-value": "Valore massimo" + }, + "maps": { + "map-type": { + "type": "Tipo mappa", + "map": "Mappa", + "image": "Immagine" + }, + "image": { + "image-source": "Fonte immagine", + "image-source-image": "Immagine", + "image-source-entity-key": "Chiave entità", + "source-entity-alias": "Alias entità sorgente", + "image-url-key": "Chiave URL immagine", + "image-url-key-required": "Chiave URL immagine obbligatoria" + }, + "control": { + "map-controls": "Controlli mappa", + "position": "Posizione", + "position-topleft": "In alto a sinistra", + "position-topright": "In alto a destra", + "position-bottomleft": "In basso a sinistra", + "position-bottomright": "In basso a destra", + "zoom-actions": "Azioni zoom", + "zoom-scroll": "Scroll", + "zoom-double-click": "Doppio clic", + "zoom-control-buttons": "Pulsanti di controllo", + "scale": "Scala", + "scale-metric": "Metrico", + "scale-imperial": "Imperiale", + "switch-to-drag-mode-using-button": "Passa alla modalità trascinamento usando il pulsante" + }, + "timeline": { + "control-panel": "Pannello di controllo timeline", + "time-step": "Passo temporale", + "speed-options": "Opzioni velocità", + "timestamp": "Timestamp", + "snap-to-real-location": "Allinea alla posizione reale", + "location-snap-filter-function": "Funzione filtro allineamento posizione", + "no-trips-data-available": "Nessun dato di percorso disponibile" + }, + "map-action": { + "map-action-buttons": "Pulsanti azione mappa", + "label": "Etichetta", + "icon": "Icona", + "color": "Colore", + "action": "Azione", + "add-button": "Aggiungi pulsante", + "no-action-buttons-configured": "Nessun pulsante azione configurato", + "remove-action-button": "Rimuovi pulsante azione", + "map-action-button": "Pulsante azione mappa", + "button-requires": "Il pulsante richiede un'etichetta o un'icona" + }, + "common": { + "common-map-settings": "Impostazioni comuni mappa", + "fit-map-bounds": "Adatta i limiti della mappa per coprire tutti i marker", + "default-map-center-position": "Posizione centrale predefinita della mappa", + "default-map-zoom-level": "Livello di zoom predefinito della mappa", + "entities-limit": "Limite di entità da caricare" + }, + "layer": { "label": "Etichetta", + "layer": "Livello", + "layers": "Livelli", + "map-layers": "Livelli della mappa", + "add-layer": "Aggiungi livello", + "layer-settings": "Impostazioni livello", + "remove-layer": "Rimuovi livello", + "no-layers": "Nessun livello configurato", + "roadmap": "Stradale", + "satellite": "Satellitare", + "hybrid": "Ibrido", + "reference": { + "reference-layer": "Livello di riferimento", + "no-layer": "Nessun livello", + "openstreetmap-hybrid": "OpenStreetMap Ibrido", + "world-edition-hybrid": "Edizione Mondiale Ibrido", + "enhanced-contrast-hybrid": "Ibrido a Contrasto Aumentato" + }, + "provider": { + "provider": "Fornitore", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Stradale", + "satellite": "Satellitare", + "hybrid": "Ibrido", + "terrain": "Terreno" + }, + "here": { + "title": "HERE", + "normal-day": "Giorno normale", + "normal-night": "Notte normale", + "hybrid-day": "Giorno ibrido", + "terrain-day": "Terreno diurno" + }, + "tencent": { + "title": "Tencent", + "normal": "Normale", + "satellite": "Satellitare", + "terrain": "Terreno" + }, + "custom": { + "title": "Personalizzato", + "tile-url": "URL tile" + } + }, + "credentials": { + "credentials": "Credenziali", + "api-key": "Chiave API" + } + }, + "overlays": { + "overlays": "Overlay", + "overlays-hint": "Configura sorgenti dati, aspetto, comportamento, opzioni di modifica e raggruppamento per le entità della mappa", + "trips": "Percorsi", + "markers": "Marker", + "polygons": "Poligoni", + "circles": "Cerchi" + }, + "data-layer": { + "source": "Sorgente", + "filter": "Filtro", + "additional-data-keys": "Chiavi dati aggiuntive", + "additional-datasources": "Sorgenti dati aggiuntive", + "additional-datasources-hint": "Sorgenti dati per accedere ad attributi o telemetria da entità non visualizzate sulla mappa, utilizzabili nelle funzioni degli overlay.", + "more-datasources": "Altre sorgenti dati", + "data-keys": "Chiavi dati", + "add-datasource": "Aggiungi sorgente dati", + "no-datasources": "Nessuna sorgente dati configurata", + "remove-datasource": "Rimuovi sorgente dati", + "behavior": "Comportamento", + "on-click": "Al clic", + "on-click-hint": "Azione invocata quando l'utente clicca sull'elemento della mappa.", + "groups": "Gruppi", + "groups-hint": "Elenco dei nomi dei gruppi assegnati all'overlay, utilizzati per alternarne la visibilità sulla mappa.", "color": "Colore", - "units": "Simbolo speciale da mostrare accanto al valore", - "decimals": "Numero cifre decimali", - "data-generation-func": "Funzione generazione dati", - "use-data-post-processing-func": "Usa funzione dopo il processamento dei dati", - "configuration": "Configurazione data key", - "timeseries": "Serie temporali", - "attributes": "Attributi", - "alarm": "Campi allarme", - "timeseries-required": "Le serie temporali dell'entità sono richieste.", - "timeseries-or-attributes-required": "Le serie temporali o gli attributi dell'entità sono richiesti.", - "maximum-timeseries-or-attributes": "Massimo { count, plural, =1 {1 serie temporale/attributo consentito.} other {# serie temporali/attributi consentiti.} }", - "alarm-fields-required": "Campi allarme obbligatori.", - "function-types": "Tipi funzione", - "function-types-required": "Tipi funzione obbligatorio.", - "maximum-function-types": "Massimo { count, plural, =1 {1 tipo di funzione consentito.} other {# tipi di funzione consentiti} }", - "time-description": "timestamp del valore corrente;", - "value-description": "il valore corrente;", - "prev-value-description": "risultato della precedente chiamata alla funzione;", - "time-prev-description": "timestamp del valore precedente;", - "prev-orig-value-description": "valore precedente originale;" - }, - "datasource": { - "type": "Tipo sorgente dati", - "name": "Nome", - "add-datasource-prompt": "Aggiungi una sorgente dati" - }, - "details": { - "edit-mode": "Modalità modifica", - "toggle-edit-mode": "Attiva/disattiva modalità di modifica" - }, - "device": { - "device": "Dispositivo", - "device-required": "Dispositivo richiesto.", - "devices": "Dispositivi", - "management": "Gestione dispositivo", - "view-devices": "Visualizza Dispositivi", - "device-alias": "Alias dispositivo", - "aliases": "Alias dispositivo", - "no-alias-matching": "'{{alias}}' non trovato.", - "no-aliases-found": "Nessun alias trovato.", - "no-key-matching": "'{{key}}' non trovata.", - "no-keys-found": "Nessuna chiave trovata.", - "create-new-alias": "Creane uno nuovo!", - "create-new-key": "Creane una nuova!", - "duplicate-alias-error": "Sono stati trovati dei duplicati dell'alias '{{alias}}'.
Gli alias di un dispositivo devono essere univoci all'interno della dashboard.", - "configure-alias": "Configura alias '{{alias}}'", - "no-devices-matching": "Nessun dispositivo corrispondente a '{{entity}}' è stato trovato.", - "alias": "Alias", - "alias-required": "Alias dispositivo richiesto.", - "remove-alias": "Rimuovi alias dispositivo", - "add-alias": "Aggiungi alias dispositivo", - "name-starts-with": "Dispositivo il cui nome inizia per", - "device-list": "Lista dispositivi", - "use-device-name-filter": "Usa filtro", - "device-list-empty": "Nessun dispositivo selezionato.", - "device-name-filter-required": "Filtro nome dispositivo obbligatorio.", - "device-name-filter-no-device-matched": "Nessun dispositivo il cui nome inizia per '{{device}}' è stato trovato.", - "add": "Aggiungi Dispositivo", - "assign-to-customer": "Assegna al cliente", - "assign-device-to-customer": "Assegna dispositivo/dispositivi al Cliente", - "assign-device-to-customer-text": "Seleziona i dispositivi da assegnare al cliente", - "make-public": "Rendi pubblico il dispositivo", - "make-private": "Rendi privato il dispositivo", - "no-devices-text": "Nessun dispositivo trovato", - "assign-to-customer-text": "Seleziona il cliente a cui assegnare il dispositivo/i dispositivi", - "device-details": "Dettagli dispositivo", - "add-device-text": "Aggiungi nuovo dispositivo", - "credentials": "Credenziali", - "manage-credentials": "Gestisci credenziali", - "delete": "Elimina dispositivo", - "assign-devices": "Assegna dispositivi", - "assign-devices-text": "Assegna { count, plural, =1 {1 dispositivo} other {# dispositivi} } al cliente", - "delete-devices": "Elimina dispositivi", - "unassign-from-customer": "Annulla assegnazione al cliente", - "unassign-devices": "Annulla assegnazione dispositivi", - "unassign-devices-action-title": "Annulla assegnazione { count, plural, =1 {1 dispositivo} other {# dispositivi} } al cliente", - "assign-new-device": "Assegna nuovo dispositivo", - "make-public-device-title": "Sei sicuro di voler rendere pubblico il dispositivo '{{deviceName}}'?", - "make-public-device-text": "Dopo la conferma il dispositivo e tutti i suoi dati saranno resi pubblici e accessibili dagli altri.", - "make-private-device-title": "Sei sicuro di voler rendere privato il dispositivo '{{deviceName}}'?", - "make-private-device-text": "Dopo la conferma il dispositivo e tutti i suoi dati saranno resi privati e non più accessibili da altri utenti.", - "view-credentials": "Visualizza credenziali", - "delete-device-title": "Sei sicuro di voler eliminare il dispositivo '{{deviceName}}'?", - "delete-device-text": "Attenzione, dopo la conferma il dispositivo e tutti i suoi dati non saranno più recuperabili.", - "delete-devices-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", - "delete-devices-action-title": "Elimina { count, plural, =1 {1 dispositivo} other {# dispositivi} }", - "delete-devices-text": "Attenzione, dopo la conferma tutti i dispositivi selezionati saranno eliminati e i relativi dati non saranno più recuperabili.", - "unassign-device-title": "Sei sicuro di voler annullare l'assegnazione del dispositivo '{{deviceName}}'?", - "unassign-device-text": "Dopo la conferma sarà annullata l'assegnazione del dispositivo e questo non sarà più accessibile dal cliente.", - "unassign-device": "Annulla assegnazione dispositivo", - "unassign-devices-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, =1 {1 dispositivo} other {# dispositivi} }?", - "unassign-devices-text": "Dopo la conferma sarà annullata l'assegnazione di tutti i dispositivi selezionati e questi non saranno più accessibili dal cliente.", - "device-credentials": "Credenziali Dispositivo", - "credentials-type": "Tipo credenziali", - "access-token": "Token di accesso", - "access-token-required": "Token di accesso obbligatorio.", - "access-token-invalid": "Il token di accesso deve avere una lunghezza compresa tra 1 e 32 caratteri.", - "secret": "Secret", - "secret-required": "Secret obbligatorio.", - "device-type": "Tipo dispositivo", - "device-type-required": "Tipo dispositivo obbligatorio.", - "select-device-type": "Seleziona tipo dispositivo", - "enter-device-type": "Inserisci typo dispositivo", - "any-device": "Qualsiasi dispositivo", - "no-device-types-matching": "Nessun dispositivo corrispondente a '{{entitySubtype}}' è stato trovato.", - "device-type-list-empty": "Nessun tipo di dispositivo selezionato.", - "device-types": "Tipi dispositivo", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "description": "Descrizione", - "events": "Eventi", - "details": "Dettagli", - "copyId": "Copia Id dispositivo", - "copyAccessToken": "Copia token di accesso", - "idCopiedMessage": "Id dispositivo copiato negli Appunti", - "accessTokenCopiedMessage": "Token di accesso del dispositivo copiato negli Appunti", - "assignedToCustomer": "Assegnato al cliente", - "unable-delete-device-alias-title": "Impossibile rimuovere l'alias del dispositivo", - "unable-delete-device-alias-text": "L'alias del dispositivo '{{deviceAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:
{{widgetsList}}", - "is-gateway": "È un gateway", - "public": "Pubblico", - "device-public": "Il dispositivo è pubblico", - "select-device": "Seleziona dispositivo" - }, - "dialog": { - "close": "Close dialog" - }, - "direction": { - "column": "Colonna", - "row": "Riga" + "color-settings": "Impostazioni colore", + "color-type-constant": "Costante", + "color-type-range": "Intervallo", + "color-type-function": "Funzione", + "color-range-source-key": "Chiave sorgente intervallo colori", + "color-range-source-key-required": "Chiave sorgente intervallo colori richiesta", + "color-range": "Intervallo colori", + "color-function": "Funzione colore", + "label": "Etichetta", + "tooltip": "Suggerimento", + "pattern-type-pattern": "Modello", + "pattern-type-function": "Funzione", + "label-pattern": "Etichetta (esempi di modello: '${entityName}', '${entityName}: (Testo ${keyName} unità.)')", + "label-function": "Funzione etichetta", + "tooltip-pattern": "Suggerimento (per es. 'Testo ${keyName} unità.' oppure Testo link)", + "tooltip-function": "Funzione suggerimento", + "tooltip-trigger": "Attivatore suggerimento", + "tooltip-trigger-click": "Mostra suggerimento al clic", + "tooltip-trigger-hover": "Mostra suggerimento al passaggio del mouse", + "auto-close-tooltips": "Chiudi automaticamente i suggerimenti", + "tooltip-offset": "Scostamento suggerimento", + "tooltip-offset-horizontal": "Orizzontale", + "tooltip-offset-vertical": "Verticale", + "tooltip-tag-actions": "Azioni tag", + "add-tooltip-tag-action": "Aggiungi azione tag", + "edit-tooltip-tag-action": "Modifica azione tag", + "remove-tooltip-tag-action": "Rimuovi azione tag", + "action-add": "Aggiungi", + "action-edit": "Modifica", + "action-move": "Sposta", + "action-remove": "Rimuovi", + "edit-instruments": "Strumenti", + "persist-location-attribute-scope": "Ambito dell'attributo per la persistenza della posizione", + "enable-snapping": "Abilita aggancio ai vertici per disegno preciso", + "enable-snapping-hint": "Allinea automaticamente i nuovi punti alle forme esistenti per facilitare e rendere più preciso il disegno.", + "drag-drop-mode": "Modalità trascina e rilascia", + "trip": { + "no-trips": "Nessun percorso configurato", + "add-trip": "Aggiungi percorso", + "trip-configuration": "Configurazione percorso", + "remove-trip": "Rimuovi percorso" + }, + "marker": { + "marker": "Indicatore", + "latitude-key": "Chiave latitudine", + "longitude-key": "Chiave longitudine", + "x-pos-key": "Chiave posizione X", + "y-pos-key": "Chiave posizione Y", + "latitude-key-required": "Chiave latitudine obbligatoria", + "longitude-key-required": "Chiave longitudine obbligatoria", + "x-pos-key-required": "Chiave posizione X obbligatoria", + "y-pos-key-required": "Chiave posizione Y obbligatoria", + "no-markers": "Nessun indicatore configurato", + "add-marker": "Aggiungi indicatore", + "marker-configuration": "Configurazione indicatore", + "remove-marker": "Rimuovi indicatore", + "marker-type": "Tipo di indicatore", + "marker-type-shape": "Forma", + "marker-type-icon": "Icona", + "marker-type-image": "Immagine", + "shape": "Forma", + "icon": "Icona", + "image": "Immagine", + "marker-shapes": "Forme dell'indicatore", + "marker-icon": "Icona indicatore", + "marker-appearance": "Aspetto dell'indicatore", + "marker-image": "Immagine indicatore", + "marker-image-type-image": "Immagine", + "marker-image-type-function": "Funzione", + "custom-marker-image-size": "Dimensione immagine personalizzata", + "marker-image-function": "Funzione immagine indicatore", + "marker-images": "Immagini indicatore", + "marker-offset": "Offset dell'indicatore", + "offset-horizontal": "Orizzontale", + "offset-vertical": "Verticale", + "rotate-marker": "Ruota indicatore", + "offset-angle": "Angolo di offset", + "position-conversion": "Conversione posizione", + "position-conversion-function": "Funzione di conversione posizione, deve restituire coordinate x,y come numeri tra 0 e 1", + "clustering": { + "use-map-markers-clustering": "Usa raggruppamento indicatori mappa", + "zoom-on-cluster-click": "Zoom al clic su un cluster", + "max-zoom": "Zoom massimo per includere un indicatore in un cluster (0 - 18)", + "max-radius": "Raggio massimo coperto da un cluster", + "zoom-animation": "Animazione durante lo zoom", + "bounds-on-cluster-mouse-over": "Limiti del cluster al passaggio del mouse", + "spiderfy-max-zoom-level": "Mostra tutti gli indicatori del cluster allo zoom massimo", + "load-optimization": "Ottimizzazione del caricamento", + "chunked-load": "Usa blocchi per aggiungere indicatori senza bloccare la pagina", + "lazy-load": "Usa caricamento ritardato per gli indicatori", + "use-cluster-marker-color-function": "Usa funzione colore per indicatori cluster", + "marker-color-function": "Funzione colore indicatore" + }, + "edit": "Modifica indicatore", + "remove-marker-for": "Rimuovi indicatore per '{{entityName}}'", + "place-marker": "Posiziona indicatore", + "place-marker-hint": "Clicca per posizionare l'indicatore", + "place-marker-hint-with-entity": "Clicca per posizionare l'entità '{{entityName}}'" + }, + "path": { + "path": "Percorso", + "path-decorator": "Decoratore percorso", + "decorator-symbol": "Simbolo decoratore", + "decorator-symbol-arrow-head": "Freccia", + "decorator-symbol-dash": "Tratto", + "decorator-arrangement": "Disposizione decoratore", + "decorator-offset": "Inizio", + "decorator-end-offset": "Fine", + "decorator-repeat": "Ripeti" + }, + "points": { + "points": "Punti", + "point-tooltip": "Suggerimento punto" + }, + "shape": { + "fill": "Riempimento", + "fill-type-color": "Colore", + "fill-type-stripe": "Striscia", + "fill-type-image": "Immagine", + "color": "Colore", + "stripe": "Striscia", + "image": "Immagine", + "stroke": "Contorno", + "fill-image": "Immagine di riempimento", + "fill-image-type-image": "Immagine", + "fill-image-type-function": "Funzione", + "preserve-aspect-ratio": "Preserva proporzioni", + "opacity": "Opacità", + "angle": "Angolo di rotazione", + "scale": "Scala", + "fill-image-function": "Funzione immagine di riempimento forma", + "fill-images": "Immagini di riempimento forma", + "stripe-pattern": "Motivo a strisce", + "first-stripe": "Prima striscia", + "second-stripe": "Seconda striscia" + }, + "polygon": { + "polygon-key": "Chiave poligono", + "polygon-key-required": "Chiave poligono obbligatoria", + "no-polygons": "Nessun poligono configurato", + "add-polygon": "Aggiungi poligono", + "polygon-configuration": "Configurazione poligono", + "remove-polygon": "Rimuovi poligono", + "edit": "Modifica poligono", + "remove-polygon-for": "Rimuovi poligono per '{{entityName}}'", + "cut": "Taglia area del poligono", + "rotate": "Ruota poligono", + "draw-rectangle": "Disegna rettangolo", + "draw-polygon": "Disegna poligono", + "polygon-place-first-point-cut-hint": "Clicca per posizionare il primo punto", + "continue-polygon-cut-hint": "Clicca per continuare il disegno", + "finish-polygon-cut-hint": "Clicca sul primo marcatore per terminare e salvare", + "polygon-place-first-point-hint": "Poligono: clicca per posizionare il primo punto", + "polygon-place-first-point-hint-with-entity": "Poligono per '{{entityName}}': clicca per posizionare il primo punto", + "continue-polygon-hint": "Poligono: clicca per continuare il disegno", + "continue-polygon-hint-with-entity": "Poligono per '{{entityName}}': clicca per continuare il disegno", + "finish-polygon-hint": "Poligono: clicca sul primo marcatore per terminare il disegno", + "finish-polygon-hint-with-entity": "Poligono per '{{entityName}}': clicca sul primo marcatore per terminare e salvare", + "rectangle-place-first-point-hint": "Rettangolo: clicca per posizionare il primo punto", + "rectangle-place-first-point-hint-with-entity": "Rettangolo per '{{entityName}}': clicca per posizionare il primo punto", + "finish-rectangle-hint": "Rettangolo: clicca per terminare il disegno", + "finish-rectangle-hint-with-entity": "Rettangolo per '{{entityName}}': clicca per terminare e salvare" + }, + "circle": { + "circle-key": "Chiave cerchio", + "circle-key-required": "Chiave cerchio obbligatoria", + "no-circles": "Nessun cerchio configurato", + "add-circle": "Aggiungi cerchio", + "circle-configuration": "Configurazione cerchio", + "remove-circle": "Rimuovi cerchio", + "edit": "Modifica cerchio", + "remove-circle-for": "Rimuovi cerchio per '{{entityName}}'", + "draw-circle": "Disegna cerchio", + "place-circle-center-hint-with-entity": "Cerchio per '{{entityName}}': clicca per posizionare il centro", + "place-circle-center-hint": "Cerchio: clicca per posizionare il centro", + "finish-circle-hint-with-entity": "Cerchio per '{{entityName}}': clicca per terminare e salvare", + "finish-circle-hint": "Cerchio: clicca per terminare il disegno" + }, + "select-entity": "Seleziona entità", + "select-entity-hint": "Suggerimento: dopo la selezione, clicca sulla mappa per impostare la posizione" + }, + "select-entity": "Seleziona entità", + "select-entity-hint": "Suggerimento: dopo la selezione clicca sulla mappa per impostare la posizione", + "tooltips": { + "placeMarker": "Clicca per posizionare l'entità '{{entityName}}'", + "firstVertex": "Poligono per '{{entityName}}': clicca per posizionare il primo punto", + "firstVertex-cut": "Clicca per posizionare il primo punto", + "continueLine": "Poligono per '{{entityName}}': clicca per continuare il disegno", + "continueLine-cut": "Clicca per continuare il disegno", + "finishLine": "Clicca su un marcatore esistente per terminare", + "finishPoly": "Poligono per '{{entityName}}': clicca sul primo marcatore per terminare e salvare", + "finishPoly-cut": "Clicca sul primo marcatore per terminare e salvare", + "finishRect": "Poligono per '{{entityName}}': clicca per terminare e salvare", + "startCircle": "Cerchio per '{{entityName}}': clicca per posizionare il centro", + "finishCircle": "Cerchio per '{{entityName}}': clicca per terminare il cerchio", + "placeCircleMarker": "Clicca per posizionare il marcatore del cerchio" + }, + "actions": { + "finish": "Termina", + "cancel": "Annulla", + "removeLastVertex": "Rimuovi ultimo punto" + }, + "buttonTitles": { + "drawMarkerButton": "Posiziona entità", + "drawPolyButton": "Crea poligono", + "drawLineButton": "Crea polilinea", + "drawCircleButton": "Crea cerchio", + "drawRectButton": "Crea rettangolo", + "editButton": "Modalità modifica", + "dragButton": "Modalità trascina e rilascia", + "cutButton": "Taglia area del poligono", + "deleteButton": "Rimuovi", + "drawCircleMarkerButton": "Crea marcatore del cerchio", + "rotateButton": "Ruota poligono" + }, + "map-provider-settings": "Impostazioni provider mappa", + "map-provider": "Provider mappa", + "map-provider-google": "Google Maps", + "map-provider-openstreet": "OpenStreet Maps", + "map-provider-here": "HERE Maps", + "map-provider-image": "Mappa immagine", + "map-provider-tencent": "Tencent Maps", + "openstreet-provider": "Provider mappa OpenStreet", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Predefinito)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery": "Esri.WorldImagery", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Usa provider personalizzato", + "custom-provider-tile-url": "URL tile del provider personalizzato", + "google-maps-api-key": "Chiave API Google Maps", + "default-map-type": "Tipo di mappa predefinito", + "google-map-type-roadmap": "Stradale", + "google-map-type-satelite": "Satellitare", + "google-map-type-hybrid": "Ibrido", + "google-map-type-terrain": "Terreno", + "map-layer": "Layer mappa", + "here-map-normal-day": "HERE.normalDay (Predefinito)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "Credenziali", + "here-app-id": "ID app HERE", + "here-app-code": "Codice app HERE", + "here-api-key": "Chiave API HERE", + "here-use-new-version-api-3": "Usa API versione 3", + "tencent-maps-api-key": "Chiave API Tencent Maps", + "tencent-map-type-roadmap": "Stradale", + "tencent-map-type-satelite": "Satellitare", + "tencent-map-type-hybrid": "Ibrido", + "image-map-background": "Sfondo mappa immagine", + "image-map-background-from-entity-attribute": "Prendi sfondo mappa immagine da attributo entità", + "image-url-source-entity-alias": "Alias entità sorgente URL immagine", + "image-url-source-entity-attribute": "Attributo entità sorgente URL immagine", + "common-map-settings": "Impostazioni comuni mappa", + "x-pos-key-name": "Nome chiave posizione X", + "y-pos-key-name": "Nome chiave posizione Y", + "latitude-key-name": "Nome chiave latitudine", + "longitude-key-name": "Nome chiave longitudine", + "default-map-zoom-level": "Livello di zoom mappa predefinito (0 - 20)", + "default-map-center-position": "Posizione centrale mappa predefinita (0,0)", + "disable-scroll-zooming": "Disabilita zoom con rotellina", + "disable-double-click-zooming": "Disabilita zoom con doppio clic", + "disable-zoom-control-buttons": "Disabilita pulsanti di controllo zoom", + "fit-map-bounds": "Adatta i limiti della mappa per coprire tutti i marker", + "use-default-map-center-position": "Usa posizione centrale mappa predefinita", + "entities-limit": "Limite entità da caricare", + "markers-settings": "Impostazioni marker", + "marker-offset-x": "Offset X marker relativo alla posizione moltiplicato per la larghezza marker", + "marker-offset-y": "Offset Y marker relativo alla posizione moltiplicato per l'altezza marker", + "position-function": "Funzione di conversione posizione, deve restituire coordinate x,y come double da 0 a 1 ciascuna", + "draggable-marker": "Marker trascinabile", + "label": "Etichetta", + "show-label": "Mostra etichetta", + "use-label-function": "Usa funzione etichetta", + "label-pattern": "Etichetta (esempi di pattern: '${entityName}', '${entityName}: (Testo ${keyName} unità.)' )", + "label-function": "Funzione etichetta", + "tooltip": "Tooltip", + "show-tooltip": "Mostra tooltip", + "show-tooltip-action": "Azione per mostrare il tooltip", + "show-tooltip-action-click": "Mostra tooltip al clic (Predefinito)", + "show-tooltip-action-hover": "Mostra tooltip al passaggio del mouse", + "auto-close-tooltips": "Chiudi automaticamente i tooltip", + "use-tooltip-function": "Usa funzione tooltip", + "tooltip-pattern": "Tooltip (es.: 'Testo ${keyName} unità.' o Testo link)", + "tooltip-function": "Funzione tooltip", + "tooltip-offset-x": "Offset X tooltip relativo all'ancora del marker moltiplicato per la larghezza marker", + "tooltip-offset-y": "Offset Y tooltip relativo all'ancora del marker moltiplicato per l'altezza marker", + "color": "Colore", + "use-color-function": "Usa funzione colore", + "color-function": "Funzione colore", + "marker-image": "Immagine marker", + "use-marker-image-function": "Usa funzione immagine marker", + "custom-marker-image": "Immagine marker personalizzata", + "custom-marker-image-size": "Dimensione immagine marker personalizzata (px)", + "marker-image-function": "Funzione immagine marker", + "marker-images": "Immagini marker", + "polygon-settings": "Impostazioni poligono", + "show-polygon": "Mostra poligono", + "polygon-key-name": "Nome chiave poligono", + "enable-polygon-edit": "Abilita modifica poligono", + "polygon-label": "Etichetta poligono", + "show-polygon-label": "Mostra etichetta poligono", + "use-polygon-label-function": "Usa funzione etichetta poligono", + "polygon-label-pattern": "Etichetta poligono (esempi di pattern: '${entityName}', '${entityName}: (Testo ${keyName} unità.)')", + "polygon-label-function": "Funzione etichetta poligono", + "polygon-tooltip": "Tooltip poligono", + "show-polygon-tooltip": "Mostra tooltip poligono", + "auto-close-polygon-tooltips": "Chiudi automaticamente tooltip poligono", + "use-polygon-tooltip-function": "Usa funzione tooltip poligono", + "polygon-tooltip-pattern": "Tooltip (es.: 'Testo ${keyName} unità.' o Testo link)", + "polygon-tooltip-function": "Funzione tooltip poligono", + "polygon-color": "Colore poligono", + "polygon-opacity": "Opacità poligono", + "use-polygon-color-function": "Usa funzione colore poligono", + "polygon-color-function": "Funzione colore poligono", + "polygon-stroke": "Bordo poligono", + "stroke-color": "Colore bordo", + "stroke-opacity": "Opacità bordo", + "stroke-weight": "Spessore bordo", + "use-polygon-stroke-color-function": "Usa funzione colore bordo poligono", + "polygon-stroke-color-function": "Funzione colore bordo poligono", + "circle-settings": "Impostazioni cerchio", + "show-circle": "Mostra cerchio", + "circle-key-name": "Nome chiave cerchio", + "enable-circle-edit": "Abilita modifica cerchio", + "circle-label": "Etichetta cerchio", + "show-circle-label": "Mostra etichetta cerchio", + "use-circle-label-function": "Usa funzione etichetta cerchio", + "circle-label-pattern": "Etichetta cerchio (esempi di pattern: '${entityName}', '${entityName}: (Testo ${keyName} unità.)')", + "circle-label-function": "Funzione etichetta cerchio", + "circle-tooltip": "Tooltip cerchio", + "show-circle-tooltip": "Mostra tooltip cerchio", + "auto-close-circle-tooltips": "Chiudi automaticamente tooltip cerchio", + "use-circle-tooltip-function": "Usa funzione tooltip cerchio", + "circle-tooltip-pattern": "Tooltip (es.: 'Testo ${keyName} unità.' o Testo link)", + "circle-tooltip-function": "Funzione tooltip cerchio", + "circle-fill-color": "Colore riempimento cerchio", + "circle-fill-color-opacity": "Opacità riempimento cerchio", + "use-circle-fill-color-function": "Usa funzione colore riempimento cerchio", + "circle-fill-color-function": "Funzione colore riempimento cerchio", + "circle-stroke": "Bordo cerchio", + "use-circle-stroke-color-function": "Usa funzione colore bordo cerchio", + "circle-stroke-color-function": "Funzione colore bordo cerchio", + "markers-clustering-settings": "Impostazioni clustering marker", + "use-map-markers-clustering": "Usa clustering marker mappa", + "zoom-on-cluster-click": "Zoom al clic su un cluster", + "max-cluster-zoom": "Livello di zoom massimo per includere marker in un cluster (0 - 18)", + "max-cluster-radius-pixels": "Raggio massimo coperto da un cluster in pixel", + "cluster-zoom-animation": "Mostra animazione su marker durante zoom", + "show-markers-bounds-on-cluster-mouse-over": "Mostra limiti marker al passaggio su cluster", + "spiderfy-max-zoom-level": "Espandi cluster al massimo livello di zoom", + "load-optimization": "Ottimizzazione caricamento", + "cluster-chunked-loading": "Usa caricamento a blocchi per evitare blocchi della pagina", + "cluster-markers-lazy-load": "Usa caricamento ritardato per i marker", + "editor-settings": "Impostazioni editor", + "enable-snapping": "Abilita allineamento ai vertici per disegno preciso", + "init-draggable-mode": "Inizializza mappa in modalità trascinamento", + "hide-all-edit-buttons": "Nascondi tutti i pulsanti di modifica", + "hide-draw-buttons": "Nascondi pulsanti disegno", + "hide-edit-buttons": "Nascondi pulsanti modifica", + "hide-remove-button": "Nascondi pulsante rimozione", + "route-map-settings": "Impostazioni mappa percorso", + "trip-animation-settings": "Impostazioni animazione percorso", + "normalization-step": "Passo di normalizzazione dati (ms)", + "tooltip-background-color": "Colore sfondo tooltip", + "tooltip-font-color": "Colore font tooltip", + "tooltip-opacity": "Opacità tooltip (0-1)", + "auto-close-tooltip": "Chiudi automaticamente tooltip", + "rotation-angle": "Imposta angolo di rotazione aggiuntivo marker (gradi)", + "path-settings": "Impostazioni percorso", + "path-color": "Colore percorso", + "use-path-color-function": "Usa funzione colore percorso", + "path-color-function": "Funzione colore percorso", + "path-decorator": "Decoratore percorso", + "use-path-decorator": "Usa decoratore percorso", + "decorator-symbol": "Simbolo decoratore", + "decorator-symbol-arrow-head": "Freccia", + "decorator-symbol-dash": "Tratto", + "decorator-symbol-size": "Dimensione simbolo decoratore (px)", + "use-path-decorator-custom-color": "Usa colore personalizzato decoratore", + "decorator-custom-color": "Colore personalizzato decoratore", + "decorator-offset": "Offset decoratore", + "end-decorator-offset": "Offset fine decoratore", + "decorator-repeat": "Ripetizione decoratore", + "points-settings": "Impostazioni punti", + "show-points": "Mostra punti", + "point-color": "Colore punto", + "point-size": "Dimensione punto (px)", + "use-point-color-function": "Usa funzione colore punto", + "point-color-function": "Funzione colore punto", + "use-point-as-anchor": "Usa punto come ancoraggio", + "point-as-anchor-function": "Funzione ancoraggio punto", + "independent-point-tooltip": "Tooltip punto indipendente", + "clustering-markers": "Clustering marker", + "use-icon-create-function": "Usa funzione di creazione icona", + "marker-color-function": "Funzione colore marker" }, - "error": { - "unable-to-connect": "Impossibile connettersi al server! Controlla la connessione ad Internet.", - "unhandled-error-code": "Codice errore non gestito: {{errorCode}}", - "unknown-error": "Errore sconosciuto" - }, - "entity": { - "entity": "Entità", - "entities": "Entità", - "aliases": "Alias entità", - "entity-alias": "Alias entità", - "unable-delete-entity-alias-title": "Impossibile eliminare alias entità", - "unable-delete-entity-alias-text": "L'alias dell'entità '{{entityAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:
{{widgetsList}}", - "duplicate-alias-error": "Trovato un duplicato dell'alias '{{alias}}'.
Gli alias dell'entità devono essere univoci all'interno della dashboard.", - "missing-entity-filter-error": "Manca il filtro per l'alias '{{alias}}'.", - "configure-alias": "Configura '{{alias}}' alias", - "alias": "Alias", - "alias-required": "Alias entità obbligatorio.", - "remove-alias": "Rimuovi alias entità", - "add-alias": "Aggiungi alias entità", - "entity-list": "Lista entità", - "entity-type": "Tipo entità", - "entity-types": "Tipi entità", - "entity-type-list": "Lista tipo entità", - "any-entity": "Qualsiasi entità", - "enter-entity-type": "Inserisci tipo entità", - "no-entities-matching": "Nessuna entità corrispondente a '{{entity}}' è stata trovata.", - "no-entity-types-matching": "Nessun tipo di entità corrispondente a '{{entityType}}' è stato trovato.", - "name-starts-with": "Nome inizia per", - "use-entity-name-filter": "Usa filtro", - "entity-list-empty": "Nessuna entità selezionata.", - "entity-name-filter-required": "Filtro nome entità obbligatorio.", - "entity-name-filter-no-entity-matched": "Nessuna entità che inizia per '{{entity}}' è stata trovata.", - "all-subtypes": "Tutte", - "select-entities": "Seleziona entità", - "no-aliases-found": "Nessun alias trovato.", - "no-alias-matching": "'{{alias}}' non trovato.", - "create-new-alias": "Creane uno nuovo!", - "key": "Chiave", - "key-name": "Nome chiave", - "no-keys-found": "Nessuna chiave trovata.", - "no-key-matching": "'{{key}}' non trovata.", - "create-new-key": "Creane una nuova!", - "type": "Tipo", - "type-required": "Tipo entità obbligatorio.", - "type-device": "Dispositivo", - "type-devices": "Dispositivi", - "list-of-devices": "{ count, plural, =1 {Un dispositivo} other {Lista di # dispositivi} }", - "device-name-starts-with": "Dispositivi i cui nomi iniziano per '{{prefix}}'", - "type-asset": "Asset", - "type-assets": "Asset", - "list-of-assets": "{ count, plural, =1 {Un asset} other {Lista di # asset} }", - "asset-name-starts-with": "Asset i cui nomi iniziano per '{{prefix}}'", - "type-entity-view": "Vista entità", - "type-entity-views": "Viste entità", - "list-of-entity-views": "{ count, plural, =1 {Una vista entità} other {Lista di # viste entità} }", - "entity-view-name-starts-with": "Viste entità i cui nomi iniziano per '{{prefix}}'", - "type-rule": "Regola", - "type-rules": "Regole", - "list-of-rules": "{ count, plural, =1 {Una regola} other {Lista di # regole} }", - "rule-name-starts-with": "Regole i cui nomi iniziano per '{{prefix}}'", - "type-plugin": "Plugin", - "type-plugins": "Plugin", - "list-of-plugins": "{ count, plural, =1 {Un plugin} other {Lista di # plugin} }", - "plugin-name-starts-with": "Plugin i cui nomi iniziano per '{{prefix}}'", - "type-tenant": "Tenant", - "type-tenants": "Tenants", - "list-of-tenants": "{ count, plural, =1 {One tenant} other {Lista di # tenants} }", - "tenant-name-starts-with": "Tenants whose names start with '{{prefix}}'", - "type-customer": "Cliente", - "type-customers": "Clienti", - "list-of-customers": "{ count, plural, =1 {Un cliente} other {Lista di # clienti} }", - "customer-name-starts-with": "Clienti i cui nomi iniziano per '{{prefix}}'", - "type-user": "Utente", - "type-users": "Utenti", - "list-of-users": "{ count, plural, =1 {Un utente} other {Lista di # utenti} }", - "user-name-starts-with": "Utenti i cui nomi iniziano per '{{prefix}}'", - "type-dashboard": "Dashboard", - "type-dashboards": "Dashboard", - "list-of-dashboards": "{ count, plural, =1 {Una dashboard} other {Lista di # dashboard} }", - "dashboard-name-starts-with": "Dashboard i cui nomi iniziano per '{{prefix}}'", - "type-alarm": "Allarme", - "type-alarms": "Allarmi", - "list-of-alarms": "{ count, plural, =1 {Un allarme} other {Lista di # allarmi} }", - "alarm-name-starts-with": "Allarmi i cui nomi iniziano per '{{prefix}}'", - "type-rulechain": "Rule chain", - "type-rulechains": "Rule chain", - "list-of-rulechains": "{ count, plural, =1 {Una rule chain} other {Lista di # catene di regole} }", - "rulechain-name-starts-with": "Catene di regole i cui nomi iniziano per '{{prefix}}'", - "type-rulenode": "Nodo regola", - "type-rulenodes": "Nodi regola", - "list-of-rulenodes": "{ count, plural, =1 {Un nodo regola} other {Lista di # nodi regola} }", - "rulenode-name-starts-with": "Nodi regola i cui nomi iniziano per '{{prefix}}'", - "type-current-customer": "Cliente attuale", - "search": "Ricerca entità", - "selected-entities": "{ count, plural, =1 {1 entità selezionata} other {# entità selezionate} }", - "entity-name": "Nome entità", - "details": "Dettagli entità", - "no-entities-prompt": "Nessuna entità trovata", - "no-data": "Nessun dato da mostrare", - "columns-to-display": "Colonne da mostrare" - }, - "entity-view": { - "entity-view": "Vista entità", - "entity-view-required": "Vista entità richiesta.", - "entity-views": "Viste entità", - "management": "Gestione viste entità", - "view-entity-views": "Visualizza Viste entità", - "entity-view-alias": "Alias vista entità", - "aliases": "Alias vista entità", - "no-alias-matching": "'{{alias}}' non trovato.", - "no-aliases-found": "Nessun alias trovato.", - "no-key-matching": "'{{key}}' non trovata.", - "no-keys-found": "Nessuna chiave trovata.", - "create-new-alias": "Creane uno nuovo!", - "create-new-key": "Creane una nuova!", - "duplicate-alias-error": "Sono stati trovati dei duplicati dell'alias '{{alias}}'.
Gli alias di una vista entità devono essere univoci all'interno della dashboard.", - "configure-alias": "Configura alias '{{alias}}'", - "no-entity-views-matching": "Nessuna vista entità corrispondente a '{{entity}}' è stata trovato.", - "alias": "Alias", - "alias-required": "Alias vista entità richiesto.", - "remove-alias": "Rimuovi alias vista entità", - "add-alias": "Aggiungi alias vista entità", - "name-starts-with": "Vista entità il cui nome inizia per", - "entity-view-list": "Lista viste entità", - "use-entity-view-name-filter": "Usa filtro", - "entity-view-list-empty": "Nessuna vista entità selezionata.", - "entity-view-name-filter-required": "Filtro nome vista entità obbligatorio.", - "entity-view-name-filter-no-entity-view-matched": "Nessuna vista entità il cui nome inizia per '{{entity-view}}' è stata trovata.", - "add": "Aggiungi Vista entità", - "assign-to-customer": "Assegna al cliente", - "assign-entity-view-to-customer": "Assegna vista entità/viste entità al Cliente", - "assign-entity-view-to-customer-text": "Seleziona la vista entità da assegnare al cliente", - "no-entity-views-text": "Nessuna vista entità trovata", - "assign-to-customer-text": "Seleziona il cliente a cui assegnare la vista entità/le vista entità", - "entity-view-details": "Dettagli vista entità", - "add-entity-view-text": "Aggiungi nuova vista entità", - "delete": "Elimina vista entità", - "assign-entity-views": "Assegna viste entità", - "assign-entity-views-text": "Assegna { count, plural, =1 {1 vista entità} other {# viste entità} } al cliente", - "delete-entity-views": "Elimina viste entità", - "unassign-from-customer": "Annulla assegnazione al cliente", - "unassign-entity-views": "Annulla assegnazione viste entità", - "unassign-entity-views-action-title": "Annulla assegnazione { count, plural, =1 {1 vista entità} other {# viste entità} } al cliente", - "assign-new-entity-view": "Assegna nuova vista entità", - "delete-entity-view-title": "Sei sicuro di voler eliminare la vista entità '{{entity-viewName}}'?", - "delete-entity-view-text": "Attenzione, dopo la conferma la vista entità e tutti i suoi dati non saranno più recuperabili.", - "delete-entity-views-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 vista entità} other {# viste entità} }?", - "delete-entity-views-action-title": "Elimina { count, plural, =1 {1 vista entità} other {# viste entità} }", - "delete-entity-views-text": "Attenzione, dopo la conferma tutte le vista entità selezionati saranno eliminate e i relativi dati non saranno più recuperabili.", - "unassign-entity-view-title": "Sei sicuro di voler annullare l'assegnazione della vista entità '{{entity-viewName}}'?", - "unassign-entity-view-text": "Dopo la conferma sarà annullata l'assegnazione della vista entità e questa non sarà più accessibile dal cliente.", - "unassign-entity-view": "Annulla assegnazione vista entità", - "unassign-entity-views-title": "Sei sicuro di voler annullare l'assegnazione di { count, plural, =1 {1 vista entità} other {# viste entità} }?", - "unassign-entity-views-text": "Dopo la conferma sarà annullata l'assegnazione di tutte le vista entità selezionate e queste non saranno più accessibili dal cliente.", - "entity-view-type": "Tipo vista entità", - "entity-view-type-required": "Tipo vista entità obbligatorio.", - "select-entity-view-type": "Seleziona tipo vista entità", - "enter-entity-view-type": "Inserisci tipo vista entità", - "any-entity-view": "Qualsiasi vista entità", - "no-entity-view-types-matching": "Nessuna vista entità corrispondente a '{{entitySubtype}}' è stata trovata.", - "entity-view-type-list-empty": "Nessun tipo di vista entità selezionato.", - "entity-view-types": "Tipi vista entità", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "description": "Descrizione", - "events": "Eventi", - "details": "Dettagli", - "copyId": "Copia Id vista entità", - "assignedToCustomer": "Assegnata al cliente", - "unable-entity-view-device-alias-title": "Impossibile rimuovere l'alias del vista entità", - "unable-entity-view-device-alias-text": "L'alias del vista entità '{{entity-viewAlias}}' non può essere eliminato perché utilizzato dai seguenti widget:
{{widgetsList}}", - "select-entity-view": "Seleziona vista entità", - "make-public": "Rendi pubblica la vista entità", - "make-private": "Rendi privata la vista entità", - "start-date": "Data inizio", - "start-ts": "Ora inizio", - "end-date": "Data fine", - "end-ts": "Ora fine", - "date-limits": "Limiti temporali", - "client-attributes": "Attributi cliente", - "shared-attributes": "Attributi condivisi", - "server-attributes": "Attributi server", - "timeseries": "Serie temporali", - "client-attributes-placeholder": "Attributi cliente", - "shared-attributes-placeholder": "Attributi condivisi", - "server-attributes-placeholder": "Attributi server", - "timeseries-placeholder": "Serie temporali", - "target-entity": "Entità target", - "attributes-propagation": "Propagazione degli attributi", - "attributes-propagation-hint": "La vista entità copierà automaticamente gli attributi specificati dall'entità target ogni volta che questa vista entità sarà salvata e aggiornata. Per ragioni di performance, gli attributi dell'entità target non sono propagati alle viste entità ogni cambiamento di attributo. È possibile abilitare la propagazione automatica configurando il nodo regola \"Copia alla vista\" nella rule chain e collegando i messaggi \"Post attributes\" a \"Attributes Updated\" al nuovo nodo regola.", - "timeseries-data": "Dati delle serie temporali", - "timeseries-data-hint": "Imposta le chiavi delle serie temporali dell'entità target che saranno accessibili alla vista entità. Questi dati sono di sola lettura.", - "make-public-entity-view-title": "Sei sicuro di voler rendere pubblica la vista entità '{{entity-viewName}}'?", - "make-public-entity-view-text": "Dopo la conferma la vista entità e tutti i suoi dati saranno resi pubblici e accessibili dagli altri.", - "make-private-entity-view-title": "Sei sicuro di voler rendere privata la vista entità '{{entity-viewName}}'?", - "make-private-entity-view-text": "Dopo la conferma la vista entità e tutti i suoi dati saranno resi privati e non più accessibili da altri utenti." - }, - "event": { - "event-type": "Tipo evento", - "type-error": "Errore", - "type-lc-event": "Ciclo di vita evento", - "type-stats": "Statistiche", - "type-debug-rule-node": "Debug", - "type-debug-rule-chain": "Debug", - "no-events-prompt": "Nessun evento trovato", - "error": "Errore", - "alarm": "Allarme", - "event-time": "Orario evento", - "server": "Server", - "body": "Body", - "method": "Metodo", - "type": "Tipo", - "message-id": "Id Messaggio", - "message-type": "Tipo Messaggio", - "data-type": "Tipo di dato", - "relation-type": "Tipo di relazione", - "metadata": "Metadati", - "data": "Dati", - "event": "Evento", - "status": "Stato", - "success": "Success", - "failed": "Failed", - "messages-processed": "Messaggi elaborati", - "errors-occurred": "Si sono verificati degli errori", - "all-events": "Tutte", - "entity-type": "Tipo entità" - }, - "extension": { - "extensions": "Estensioni", - "selected-extensions": "{ count, plural, =1 {1 estensione selezionata} other {# estensioni selezionate} }", + "markdown": { + "use-markdown-text-function": "Usa funzione per valore markdown/HTML", + "markdown-text-function": "Funzione valore markdown/HTML", + "markdown-text-pattern": "Pattern markdown/HTML (markdown o HTML con variabili, ad es. '${entityName} o ${keyName} - del testo.')", + "apply-default-markdown-style": "Applica stile markdown predefinito", + "markdown-css": "CSS per markdown/HTML" + }, + "simple-card": { + "label": "Etichetta", + "label-position": "Posizione etichetta", + "label-position-left": "Sinistra", + "label-position-top": "In alto" + }, + "single-switch": { + "behavior": "Comportamento", + "layout": "Layout", + "layout-right": "Destra", + "layout-left": "Sinistra", + "layout-centered": "Centrato", + "auto-scale": "Auto scala", + "label": "Etichetta", + "icon": "Icona", + "switch-color": "Colore interruttore", + "on": "Acceso", + "off": "Spento", + "disabled": "Disabilitato", + "tumbler-color": "Colore del commutatore", + "on-label": "Etichetta acceso", + "off-label": "Etichetta spento", + "switch": "Interruttore" + }, + "slider": { + "behavior": "Comportamento", + "initial-value": "Valore iniziale", + "initial-value-hint": "Azione per ottenere il valore iniziale del cursore.", + "on-value-change": "Al cambio valore", + "on-value-change-hint": "Azione eseguita quando il valore del cursore cambia.", + "layout": "Layout", + "layout-default": "Predefinito", + "layout-extended": "Esteso", + "layout-simplified": "Semplificato", + "auto-scale": "Auto scala", + "icon": "Icona", + "value": "Valore", + "range": "Intervallo", + "min": "min", + "max": "max", + "range-ticks": "Indicatori intervallo", + "tick-marks": "Tacche", + "colors": "Colori", + "main": "Principale", + "background": "Sfondo", + "left-icon": "Icona sinistra", + "right-icon": "Icona destra", + "slider": "Cursore" + }, + "value-card": { + "layout": "Layout", + "layout-square": "Quadrato", + "layout-vertical": "Verticale", + "layout-centered": "Centrato", + "layout-simplified": "Semplificato", + "layout-horizontal": "Orizzontale", + "layout-horizontal-reversed": "Orizzontale inverso", + "label": "Etichetta", + "icon": "Icona", + "value": "Valore", + "date": "Data", + "value-card-style": "Stile scheda valore", + "auto-scale": "Auto scala" + }, + "label-card": { + "auto-scale": "Auto scala", + "label": "Etichetta", + "icon": "Icona", + "label-card-style": "Stile scheda etichetta" + }, + "label-value-card": { + "value": "Valore", + "label-value-card-style": "Stile scheda etichetta & valore" + }, + "liquid-level-card": { + "layout-simple": "Semplice", + "layout-percentage": "Percentuale", + "layout-absolute": "Assoluto", + "layout": "Layout", + "background-overlay": "Sovrapposizione dello sfondo del valore", + "total-volume": "Volume totale", + "total-volume-units": "Unità volume totale", + "tank": "Serbatoio", + "shape": "Forma", + "datasource-units": "Unità sorgente", + "widget-units": "Unità widget", + "decimals": "Decimali", + "liquid": "Liquido", + "liquid-color": "Colore del liquido", + "value": "Valore", + "value-font": "Font del valore", + "level": "Livello", + "last-update": "Ultimo aggiornamento", + "shape-by-attribute": "Imposta forma del serbatoio per nome attributo", + "tooltip-background": "Colore dello sfondo", + "background-blur": "Sfocatura dello sfondo", + "tank-color": "Colore del serbatoio", + "static": "Statico", + "see-examples": "Vedi esempi", + "attribute": "Attributo", + "shape-type": "Tipo", + "v-oval": "Ovale verticale", + "v-cylinder": "Cilindro verticale", + "v-capsule": "Capsula verticale", + "rectangle": "Rettangolo", + "h-oval": "Ovale orizzontale", + "h-ellipse": "Ellisse orizzontale", + "h-dish-ends": "Estremità a piatto orizzontale", + "h-cylinder": "Cilindro orizzontale", + "h-capsule": "Capsula orizzontale", + "h-elliptical_2_1": "Ellittica orizzontale 2:1", + "icon": "Icona della scheda", + "title": "Titolo della scheda", + "units": "Unità", + "color-and-font": "Colore e carattere", + "shape-attribute-name": "Nome attributo", + "total-volume-required": "Il volume totale è obbligatorio.", + "attribute-name-required": "Il nome dell'attributo è obbligatorio.", + "attribute-key-not-set": "Chiave dell'attributo '{{attributeName}}' non impostata", + "attribute-key-invalid": "Chiave dell'attributo '{{attributeName}}' non valida" + }, + "aggregated-value-card": { + "subtitle": "Sottotitolo", + "chart": "Grafico", + "values": "Valori", + "value-appearance": "Aspetto del valore", + "position": "Posizione", + "position-center": "Centro", + "position-right-top": "Alto a destra", + "position-right-bottom": "Basso a destra", + "position-left-top": "Alto a sinistra", + "position-left-bottom": "Basso a sinistra", + "font": "Carattere", + "color": "Colore", + "arrow": "Freccia", + "display-up-down-arrow": "Mostra freccia Su/Giù", + "add-value": "Aggiungi valore", + "remove-value": "Rimuovi valore", + "no-values": "Nessun valore configurato", + "aggregation": "Aggregazione", + "aggregated-value-card-style": "Stile scheda valore aggregato", + "auto-scale": "Auto scala" + }, + "value-chart-card": { + "layout": "Layout", + "layout-left": "Sinistra", + "layout-right": "Destra", + "auto-scale": "Auto scala", + "icon": "Icona", + "value": "Valore", + "chart": "Grafico", + "value-chart-card-style": "Stile scheda valore con grafico" + }, + "progress-bar": { + "layout": "Layout", + "layout-default": "Predefinito", + "layout-simplified": "Semplificato", + "auto-scale": "Auto scala", + "icon": "Icona", + "value": "Valore", + "range": "Intervallo", + "min": "min", + "max": "max", + "range-ticks": "Intervallo tick", + "bar": "Barra", + "bar-color": "Colore della barra", + "bar-background": "Sfondo della barra", + "progress-bar-card-style": "Stile scheda barra di avanzamento" + }, + "notification": { + "max-notification-display": "Numero massimo di notifiche da visualizzare", + "counter": "Contatore", + "counter-hint": "Il contatore verrà mostrato se è abilitato il \"Titolo del widget\"", + "icon": "Icona", + "counter-value": "Valore", + "counter-color": "Colore", + "notification-button": "Pulsanti di notifica", + "button-view-all": "Visualizza tutto", + "button-filter": "Filtro", + "type-filter": "Filtro per tipo", + "button-mark-read": "Segna tutto come letto", + "notification-types": "Tipi di notifica", + "notification-type": "Tipo di notifica", + "search-type": "Tipo di ricerca", + "any-type": "Qualsiasi tipo" + }, + "alarm-count": { + "alarm-count-card-style": "Stile scheda conteggio allarmi" + }, + "entity-count": { + "entity-count-card-style": "Stile scheda conteggio entità" + }, + "count": { + "layout": "Layout", + "layout-column": "Colonna", + "layout-row": "Riga", + "label": "Etichetta", + "icon": "Icona", + "icon-background": "Sfondo icona", + "value": "Valore", + "chevron": "Chevron", + "auto-scale": "Auto scala" + }, + "table": { + "common-table-settings": "Impostazioni comuni della tabella", + "enable-search": "Abilita la ricerca", + "enable-sticky-header": "Visualizza sempre l'intestazione", + "enable-sticky-action": "Visualizza sempre la colonna delle azioni", + "hidden-cell-button-display-mode": "Modalità di visualizzazione dei pulsanti nascosti nella cella", + "show-empty-space-hidden-action": "Mostra spazio vuoto al posto del pulsante di azione nascosto", + "dont-reserve-space-hidden-action": "Non riservare spazio per i pulsanti di azione nascosti", + "display-timestamp": "Timestamp", + "display-pagination": "Mostra paginazione", + "default-page-size": "Dimensione pagina predefinita", + "page-step-settings": "Impostazioni passo di pagina", + "page-step-count": "Numero di passi", + "page-step-increment": "Incremento del passo", + "page-step-count-format-message": "Deve essere un valore intero, nell'intervallo da 1 a 100.", + "page-step-increment-format-message": "Deve essere un valore intero, maggiore o uguale a 1.", + "use-entity-label-tab-name": "Usa l'etichetta dell'entità nel nome della scheda", + "hide-empty-lines": "Nascondi righe vuote", + "row-style": "Stile riga", + "use-row-style-function": "Usa funzione per stile riga", + "row-style-function": "Funzione per stile riga", + "cell-style": "Stile cella", + "use-cell-style-function": "Usa funzione per stile cella", + "cell-style-function": "Funzione per stile cella", + "cell-content": "Contenuto cella", + "use-cell-content-function": "Usa funzione per contenuto cella", + "cell-content-function": "Funzione per contenuto cella", + "show-latest-data-column": "Mostra colonna dati più recenti", + "latest-data-column-order": "Ordine colonna dati più recenti", + "entities-table-title": "Titolo tabella entità", + "enable-select-column-display": "Abilita selezione colonne da visualizzare", + "display-entity-name": "Mostra colonna nome entità", + "entity-name-column-title": "Titolo colonna nome entità", + "display-entity-label": "Mostra colonna etichetta entità", + "entity-label-column-title": "Titolo colonna etichetta entità", + "display-entity-type": "Mostra colonna tipo entità", + "default-sort-order": "Ordine di ordinamento predefinito", + "custom-title": "Titolo intestazione personalizzato", + "column-width": "Larghezza colonna (px o %)", + "default-column-visibility": "Visibilità predefinita colonna", + "column-visibility-visible": "Visibile", + "column-visibility-hidden": "Nascosta", + "column-visibility-hidden-mobile": "Nascosta in modalità mobile", + "column-selection-to-display": "Selezione colonna in 'Colonne da visualizzare'", + "column-selection-to-display-enabled": "Abilitata", + "column-selection-to-display-disabled": "Disabilitata", + "alarms-table-title": "Titolo tabella allarmi", + "enable-alarms-selection": "Abilita selezione allarmi", + "enable-alarms-search": "Abilita ricerca allarmi", + "enable-alarm-filter": "Abilita filtro allarmi", + "display-alarm-details": "Mostra dettagli allarmi", + "allow-alarms-ack": "Consenti conferma allarmi", + "allow-alarms-clear": "Consenti eliminazione allarmi", + "display-alarm-activity": "Mostra attività degli allarmi", + "allow-alarms-assign": "Consenti assegnazione allarmi", + "columns": "Colonne", + "column-settings": "Impostazioni colonna", + "remove-column": "Rimuovi colonna", + "add-column": "Aggiungi colonna", + "no-columns": "Nessuna colonna configurata", + "columns-to-display": "Colonne da visualizzare", + "table-header": "Intestazione tabella", + "header-buttons": "Pulsanti intestazione", + "table-buttons": "Pulsanti tabella", + "pagination": "Paginazione", + "rows": "Righe", + "timeseries-column-error": "Deve essere specificata almeno una colonna time series", + "alarm-column-error": "Deve essere specificata almeno una colonna allarmi", + "table-tabs": "Schede della tabella", + "show-cell-actions-menu-mobile": "Mostra menu azioni cella in modalità mobile", + "disable-sorting": "Disabilita ordinamento" + }, + "latest-chart": { + "total": "Totale", + "auto-scale": "Auto scala", + "clockwise-layout": "Disposizione oraria", + "sort-series": "Ordina serie per etichetta", + "tooltip-value-type-absolute": "Assoluto", + "tooltip-value-type-percentage": "Percentuale" + }, + "pie-chart": { + "pie-chart-appearance": "Aspetto grafico a torta", + "label": "Etichetta", + "border": "Bordo", + "radius": "Raggio", + "pie-chart-card-style": "Stile scheda grafico a torta" + }, + "radar-chart": { + "radar-appearance": "Aspetto radar", + "shape": "Forma", + "shape-polygon": "Poligono", + "shape-circle": "Cerchio", + "color": "Colore", + "line": "Linea", + "points": "Punti", + "points-label": "Etichetta punti", + "radar-axis": "Asse radar", + "axis-label": "Etichetta asse", + "ticks-label": "Etichetta tacche", + "radar-chart-style": "Stile grafico radar" + }, + "time-series-chart": { + "chart": "Grafico", + "chart-style": "Stile grafico", + "data-zoom": "Zoom dati", + "stack-mode": "Modalità impilamento", + "stack-mode-hint": "Impila le serie nel grafico. Le serie con la stessa unità verranno sovrapposte.", + "axes": "Assi", + "y-axes": "Assi Y", + "line-type": "Tipo linea", + "line-width": "Spessore linea", + "type-line": "Linea", + "type-bar": "Barra", + "type-point": "Punto", + "no-aggregation-bar-width-strategy": "Strategia larghezza barra per dati non aggregati", + "no-aggregation-bar-width-strategy-group": "Gruppo", + "no-aggregation-bar-width-strategy-separate": "Separato", + "bar-group-width": "Larghezza gruppo barre", + "bar-width": "Larghezza barra", + "bar-width-relative": "Percentuale della finestra temporale", + "bar-width-absolute": "Assoluto (ms)", + "comparison": { + "comparison": "Confronto", + "comparison-hint": "Il confronto funziona solo con dati storici!", + "show": "Mostra", + "settings": "Impostazioni confronto", + "show-values-for-comparison": "Mostra dati storici per il confronto", + "comparison-values-label": "Etichetta chiave di confronto", + "comparison-values-label-auto": "Auto", + "comparison-data-color": "Colore dati confronto" + }, + "threshold": { + "thresholds": "Soglie", + "source": "Fonte", + "key-value": "Chiave / Valore", + "no-thresholds": "Nessuna soglia configurata", + "add-threshold": "Aggiungi soglia", + "type-constant": "Costante", + "type-latest-key": "Chiave", + "type-entity": "Entità", + "threshold-settings": "Impostazioni soglia", + "remove-threshold": "Rimuovi soglia", + "threshold-value-required": "Valore soglia richiesto.", + "key-required": "Chiave richiesta.", + "entity-key-required": "Chiave entità richiesta.", + "line-appearance": "Aspetto linea", + "line-color": "Colore linea", + "start-symbol": "Simbolo inizio", + "end-symbol": "Simbolo fine", + "symbol-size": "dimensione", + "label": "Etichetta", + "label-position-start": "Inizio", + "label-position-middle": "Centro", + "label-position-end": "Fine", + "label-position-inside-start": "Dentro inizio", + "label-position-inside-start-top": "Dentro inizio sopra", + "label-position-inside-start-bottom": "Dentro inizio sotto", + "label-position-inside-middle": "Dentro centro", + "label-position-inside-middle-top": "Dentro centro sopra", + "label-position-inside-middle-bottom": "Dentro centro sotto", + "label-position-inside-end": "Dentro fine", + "label-position-inside-end-top": "Dentro fine sopra", + "label-position-inside-end-bottom": "Dentro fine sotto", + "label-background": "Sfondo etichetta" + }, + "state": { + "states": "Stati", + "label": "Etichetta", + "ticks-value": "Valore delle tacche", + "source": "Fonte", + "value-range": "Valore / Intervallo", + "no-states": "Nessuno stato configurato", + "add-state": "Aggiungi stato", + "type-constant": "Costante", + "type-range": "Intervallo", + "from": "Da", + "to": "A", + "remove-state": "Rimuovi stato" + }, + "grid": { + "grid": "Griglia", + "background-color": "Colore di sfondo", + "border": "Bordo" + }, + "axis": { + "axes": "Assi", + "x-axis": "Asse X", + "y-axis": "Asse Y", + "y-axis-settings": "Impostazioni asse Y", + "comparison-x-axis-settings": "Impostazioni asse X per confronto", + "remove-y-axis": "Rimuovi asse Y", + "id": "ID", + "label": "Etichetta", + "position": "Posizione", + "position-left": "Sinistra", + "position-right": "Destra", + "position-top": "In alto", + "position-bottom": "In basso", + "tick-labels": "Etichette delle tacche", + "ticks-formatter-function": "Funzione di formattazione delle tacche", + "ticks-generator-function": "Funzione generatore di tacche", + "show-ticks": "Mostra tacche", + "show-line": "Mostra linea", + "show-split-lines": "Mostra linee di divisione", + "show-split-lines-x-axis-hint": "Se abilitato, verranno mostrate le linee verticali nel grafico.", + "show-split-lines-y-axis-hint": "Se abilitato, verranno mostrate le linee orizzontali nel grafico.", + "ticks-interval": "Intervallo delle tacche", + "ticks-interval-hint": "Imposta forzatamente l'intervallo di segmentazione per l'asse.", + "split-number": "Numero di divisioni", + "split-number-hint": "Numero di segmenti in cui l'asse è diviso.", + "min": "Min", + "max": "Max", + "show": "Mostra", + "add-y-axis": "Aggiungi asse Y" + }, + "series": { + "legend-settings": "Impostazioni legenda", + "show-in-legend": "Mostra nella legenda", + "show-in-legend-hint": "Mostra nome e dati della serie nella legenda.", + "hidden-by-default": "Nascosto di default", + "hidden-by-default-hint": "Rende la serie nascosta nella legenda per impostazione predefinita.", + "series-type": "Tipo di serie", "type": "Tipo", - "key": "Chiave", - "value": "Valore", - "id": "Id", - "extension-id": "Id Estensione", - "extension-type": "Tipo Estensione", - "transformer-json": "JSON *", - "unique-id-required": "Id estensione corrente già esistente.", - "delete": "Elimina estensione", - "add": "Aggiungi estensione", - "edit": "Modifica estensione", - "delete-extension-title": "Sei sicuro di voler eliminare l'estensione '{{extensionId}}'?", - "delete-extension-text": "Attenzione, dopo la conferma l'estensione e tutti i suoi data non saranno più recuperabili.", - "delete-extensions-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 estensione} other {# estensioni} }?", - "delete-extensions-text": "Attenzione, dopo la conferma tutte le estensioni selezionate saranno eliminate.", - "converters": "Convertitori", - "converter-id": "Id convertitore", - "configuration": "Configurazione", - "converter-configurations": "Configurazioni convertitore", - "token": "Token di sicurezza", - "add-converter": "Aggiungi convertitore", - "add-config": "Aggiungi configurazione convertitore", - "device-name-expression": "Espressione nome dispositivo", - "device-type-expression": "Espressione tipo dispositivo", - "custom": "Custom", - "to-double": "To Double", - "transformer": "Transformer", - "json-required": "Transformer json is required.", - "json-parse": "Unable to parse transformer json.", - "attributes": "Attributi", - "add-attribute": "Aggiungi attributo", - "add-map": "Add mapping element", - "timeseries": "Serie temporali", - "add-timeseries": "Add timeseries", - "field-required": "Campo obbligatorio", - "brokers": "Broker", - "add-broker": "Aggiungi broker", - "host": "Host", - "port": "Porta", - "port-range": "Il numero di porta deve essere compreso tra 1 e 65535.", - "ssl": "Ssl", - "credentials": "Credenziali", - "username": "Nome utente", - "password": "Password", - "retry-interval": "Intervallo di ripetizione in millisecondi", - "anonymous": "Anonimo", - "basic": "Basic", - "pem": "PEM", - "ca-cert": "File certificato CA *", - "private-key": "File chiave privata *", - "cert": "File certificato *", - "no-file": "Nessun file selezionato.", - "drop-file": "Trascina un file o fai clic per selezionare un file da caricare.", - "mapping": "Mapping", - "topic-filter": "Filtro topic", - "converter-type": "Tipo convertitore", - "converter-json": "Json", - "json-name-expression": "Device name json expression", - "topic-name-expression": "Device name topic expression", - "json-type-expression": "Device type json expression", - "topic-type-expression": "Device type topic expression", - "attribute-key-expression": "Attribute key expression", - "attr-json-key-expression": "Attribute key json expression", - "attr-topic-key-expression": "Attribute key topic expression", - "request-id-expression": "Request id expression", - "request-id-json-expression": "Request id json expression", - "request-id-topic-expression": "Request id topic expression", - "response-topic-expression": "Response topic expression", - "value-expression": "Value expression", - "topic": "Topic", - "timeout": "Timeout in millisecondi", - "converter-json-required": "Convertitore json obbligatorio.", - "converter-json-parse": "Unable to parse converter json.", - "filter-expression": "Filter expression", - "connect-requests": "Richieste di connessione", - "add-connect-request": "Aggiungi richiesta di connessione", - "disconnect-requests": "Richieste di disconnessione", - "add-disconnect-request": "Aggiungi richiesta di disconnessione", - "attribute-requests": "Attribute requests", - "add-attribute-request": "Add attribute request", - "attribute-updates": "Attribute updates", - "add-attribute-update": "Add attribute update", - "server-side-rpc": "RPC lato server", - "add-server-side-rpc-request": "Aggiungi richiesta RPC server-side", - "device-name-filter": "Filtro nome dispositivo", - "attribute-filter": "Filtro attributo", - "method-filter": "Filtro metodo", - "request-topic-expression": "Request topic expression", - "response-timeout": "Timeout risposta in millisecondi", - "topic-expression": "Topic expression", - "client-scope": "Visibilità client", - "add-device": "Aggiungi dispositivo", - "opc-server": "Server", - "opc-add-server": "Aggiungi server", - "opc-add-server-prompt": "Aggiungi server", - "opc-application-name": "Nome applicazione", - "opc-application-uri": "Uri applicazione", - "opc-scan-period-in-seconds": "Intervallo di scansione in secondi", - "opc-security": "Sicurezza", - "opc-identity": "Identità", - "opc-keystore": "Keystore", - "opc-type": "Tipo", - "opc-keystore-type": "Tipo", - "opc-keystore-location": "Location *", - "opc-keystore-password": "Password", - "opc-keystore-alias": "Alias", - "opc-keystore-key-password": "Chiave password", - "opc-device-node-pattern": "Device node pattern", - "opc-device-name-pattern": "Device name pattern", - "modbus-server": "Server/slave", - "modbus-add-server": "Aggiungi server/slave", - "modbus-add-server-prompt": "Aggiungi server/slave", - "modbus-transport": "Transport", - "modbus-tcp-reconnect": "Riconnessione automatica", - "modbus-rtu-over-tcp": "RTU over TCP", - "modbus-port-name": "Nome porta seriale", - "modbus-encoding": "Codifica", - "modbus-parity": "Parità", - "modbus-baudrate": "Baud rate", - "modbus-databits": "Data bits", - "modbus-stopbits": "Stop bits", - "modbus-databits-range": "Data bits deve essere compreso nell'intervallo 7-8.", - "modbus-stopbits-range": "Stop bits deve essere compreso nell'intervallo 1-2.", - "modbus-unit-id": "ID unità", - "modbus-unit-id-range": "ID unità deve essere compreso nell'intervallo 1-247.", - "modbus-device-name": "Nome dispositivo", - "modbus-poll-period": "Intervallo di polling (ms)", - "modbus-attributes-poll-period": "Intervallo di polling degli attributi (ms)", - "modbus-timeseries-poll-period": "Intervallo di polling delle serie temporali (ms)", - "modbus-poll-period-range": "L'intervallo di polling deve essere un valore positivo.", - "modbus-tag": "Tag", - "modbus-function": "Funzione", - "modbus-register-address": "Indirizzo registro", - "modbus-register-address-range": "Indirizzo registro deve essere compreso tra 0 e 65535.", - "modbus-register-bit-index": "Bit index", - "modbus-register-bit-index-range": "Bit index deve essere compreso tra 0 e 15.", - "modbus-register-count": "Register count", - "modbus-register-count-range": "Register count dovrebbe essereun valore positivo.", - "modbus-byte-order": "Byte order", - "sync": { - "status": "Stato", - "sync": "Sincronizzato", - "not-sync": "Non sincronizzato", - "last-sync-time": "Ultima sincronizzazione", - "not-available": "Non disponibile" + "type-line": "Linea", + "type-bar": "Barra", + "line": { + "line": "Linea", + "show-line": "Mostra linea", + "step-line": "Linea a scalini", + "step-type-start": "Inizio", + "step-type-middle": "Centro", + "step-type-end": "Fine", + "smooth-line": "Linea smussata" }, - "export-extensions-configuration": "Esporta configurazione estensioni", - "import-extensions-configuration": "Importa configurazione estensioni", - "import-extensions": "Importa estensione", - "import-extension": "Importa estensione", - "export-extension": "Esporta estensione", - "file": "File estensione", - "invalid-file-error": "File estensione non valido" - }, - "fullscreen": { - "expand": "Espandi a tutto schermo", - "exit": "Esci da schermo intero", - "toggle": "Commuta modalità schermo intero", - "fullscreen": "Schermo intero" - }, - "function": { - "function": "Funzione" - }, - "grid": { - "delete-item-title": "Sei sicuro di voler eliminare questo elemento?", - "delete-item-text": "Attenzione, dopo la conferma questo elemento e tutti i suoi dati non saranno più recuperabili.", - "delete-items-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 elemento} other {# elementi} }?", - "delete-items-action-title": "Elimina { count, plural, =1 {1 elemento} other {# elementi} }", - "delete-items-text": "Attenzione, dopo la conferma tutti gli elementi selezionati saranno rimossi e i relativi dati non saranno più recuperabili.", - "add-item-text": "Aggiungi nuovo elemento", - "no-items-text": "Nessun elemento trovato", - "item-details": "Dettagli elemento", - "delete-item": "Elimina elemento", - "delete-items": "Elimina elementi", - "scroll-to-top": "Scorri verso l'alto" - }, - "help": { - "goto-help-page": "Vai all'help" + "point": { + "points": "Punti", + "show-points": "Mostra punti", + "point-label": "Etichetta punto", + "point-label-hint": "Visualizza etichetta con valore sopra il punto della serie.", + "point-label-background": "Sfondo etichetta punto", + "point-shape": "Forma del punto", + "point-size": "Dimensione del punto" + } + } + }, + "wind-speed-direction": { + "layout": "Layout", + "layout-default": "Predefinito", + "layout-advanced": "Avanzato", + "layout-simplified": "Semplificato", + "values": "Valori", + "wind-direction": "Direzione del vento", + "center-value": "Valore centrale", + "icon": "Icona", + "arrow": "Freccia", + "ticks": "Tacche", + "labels-type": "Tipo di etichette", + "directional-names": "Nomi direzionali", + "degrees": "Gradi", + "major-ticks": "Tacche principali", + "minor-ticks": "Tacche minori", + "wind-speed-direction-card-style": "Stile scheda velocità e direzione del vento", + "ticks-color": "Colore delle tacche", + "ticks-labels-type": "Tipo di etichette delle tacche", + "arrow-color": "Colore della freccia" + }, + "value-source": { + "value-source": "Fonte del valore", + "predefined-value": "Costante", + "entity-attribute": "Attributo dell'entità", + "value": "Valore", + "value-required": "Il valore è obbligatorio.", + "key-required": "La chiave è obbligatoria.", + "entity-key-required": "La chiave dell'entità è obbligatoria.", + "source-entity-alias": "Alias dell'entità sorgente", + "source-entity-attribute": "Attributo dell'entità sorgente", + "type-constant": "Costante", + "type-latest-key": "Chiave", + "type-entity": "Entità" + }, + "rpc-state": { + "initial-state": "Stato iniziale", + "initial-state-hint": "Azione per ottenere lo stato iniziale (On/Off) del componente.", + "disabled-state": "Stato disabilitato", + "disabled-state-hint": "Configura la condizione in cui il componente è disabilitato.", + "turn-on": "Accendi", + "turn-on-hint": "Azione attivata quando il cursore viene impostato su 'Acceso'", + "turn-off": "Spegni", + "turn-off-hint": "Azione attivata quando il cursore viene impostato su 'Spento'", + "on": "Acceso", + "off": "Spento", + "disabled": "Disabilitato" + }, + "value-action": { + "do-nothing": "Non fare nulla", + "execute-rpc": "Esegui RPC", + "get-attribute": "Ottieni attributo", + "set-attribute": "Imposta attributo", + "get-time-series": "Ottieni serie temporale", + "get-alarm-status": "Ottieni stato allarme", + "get-dashboard-state": "Ottieni ID stato dashboard", + "get-dashboard-state-object": "Ottieni oggetto stato dashboard", + "add-time-series": "Aggiungi serie temporale", + "execute-rpc-text": "Esegui metodo RPC '{{methodName}}'", + "get-time-series-text": "Usa serie temporale '{{key}}'", + "get-attribute-text": "Usa attributo '{{key}}'", + "get-alarm-status-text": "Usa stato allarme", + "get-dashboard-state-text": "Usa stato dashboard", + "get-dashboard-state-object-text": "Usa oggetto stato dashboard", + "when-dashboard-state-is-text": "Quando l'ID stato dashboard è '{{state}}'", + "when-dashboard-state-function-is-text": "Quando f(ID stato dashboard) è '{{state}}'", + "when-dashboard-state-object-function-is-text": "Quando f(oggetto stato dashboard) è '{{state}}'", + "set-attribute-to-value-text": "Imposta attributo '{{key}}' su: {{value}}", + "add-time-series-value-text": "Aggiungi valore alla serie temporale '{{key}}': {{value}}", + "set-attribute-text": "Imposta attributo '{{key}}'", + "add-time-series-text": "Aggiungi serie temporale '{{key}}'", + "action": "Azione", + "value": "Valore", + "init-value-hint": "Valore che sarà impostato finché il dispositivo non invia dati.", + "method": "Metodo", + "method-name-required": "Il nome del metodo è obbligatorio.", + "request-timeout-ms": "Timeout richiesta RPC (ms)", + "request-timeout-required": "Il timeout della richiesta è obbligatorio.", + "min-request-timeout-error": "Il timeout della richiesta deve essere maggiore o uguale a 5000 ms (5 secondi).", + "request-persistent": "Richiesta RPC persistente", + "persistent-polling-interval": "Intervallo di polling persistente (ms)", + "persistent-polling-interval-hint": "Intervallo di polling (ms) per ottenere la risposta del comando RPC persistente", + "persistent-polling-interval-required": "Intervallo di polling persistente obbligatorio.", + "min-persistent-polling-interval-error": "L'intervallo di polling persistente deve essere maggiore o uguale a 1000 ms (1 secondo).", + "attribute-scope": "Ambito attributo", + "attribute-key": "Chiave attributo", + "attribute-key-required": "La chiave dell'attributo è obbligatoria.", + "time-series-key": "Chiave serie temporale", + "time-series-key-required": "La chiave della serie temporale è obbligatoria.", + "action-result-converter": "Convertitore risultato azione", + "converter-none": "Nessuno", + "converter-function": "Funzione", + "converter-constant": "Costante", + "converter-value": "Valore", + "parse-value-function": "Funzione di parsing del valore", + "state-when-result-is": "'{{state}}' quando il risultato è", + "parameters": "Parametri", + "convert-value-function": "Funzione di conversione del valore", + "error": { + "target-entity-is-not-set": "Entità di destinazione non impostata!", + "failed-to-perform-action": "Impossibile eseguire l'azione {{ actionLabel }}.", + "invalid-attribute-scope": "L'ambito dell'attributo {{scope}} non è supportato per l'entità {{entityType}}." + } + }, + "widget-font": { + "font-settings": "Impostazioni carattere", + "font-family": "Famiglia carattere", + "size": "Dimensione", + "relative-font-size": "Dimensione relativa del carattere (percentuale)", + "font-style": "Stile", + "font-style-normal": "Normale", + "font-style-italic": "Corsivo", + "font-style-oblique": "Obliquo", + "font-weight": "Peso", + "font-weight-normal": "Normale", + "font-weight-bold": "Grassetto", + "font-weight-bolder": "Più grassetto", + "font-weight-lighter": "Più leggero", + "color": "Colore", + "shadow-color": "Colore ombra", + "preview": "Anteprima", + "line-height": "Altezza linea", + "auto": "Auto" }, "home": { - "home": "Home", - "profile": "Profilo", - "logout": "Logout", - "menu": "Menu", - "avatar": "Avatar", - "open-user-menu": "Apri menu utente" + "no-data-available": "Nessun dato disponibile" }, - "import": { - "no-file": "Nessun file selezionato", - "drop-file": "Trascina un file JSON o fai clic per selezionare un file da caricare." + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "Disco", + "cpu-warning-text": "Utilizzo elevato della CPU. Per evitare guasti, ottimizza le prestazioni del sistema.", + "cpu-critical-text": "Utilizzo critico della CPU. Per evitare guasti, ottimizza le prestazioni del sistema.", + "ram-warning-text": "Scarsa riserva di RAM. Per evitare guasti, ottimizza le prestazioni del sistema o aumenta la RAM.", + "ram-critical-text": "Riserva di RAM criticamente bassa. Per evitare guasti, ottimizza le prestazioni del sistema o aumenta la RAM.", + "disk-warning-text": "Spazio su disco insufficiente. Per evitare perdite di dati, libera o espandi lo spazio su disco.", + "disk-critical-text": "Spazio su disco criticamente insufficiente. Per evitare perdite di dati, libera o espandi lo spazio su disco." }, - "item": { - "selected": "Selezionata" + "cluster-info": { + "service-id": "ID servizio", + "service-type": "Tipo di servizio", + "no-data": "Nessun dato" }, - "js-func": { - "no-return-error": "La funzione deve restituire un valore!", - "return-type-mismatch": "La funzione deve restituire un valore di tipo '{{type}}'!", - "tidy": "Tidy" + "transport-messages": { + "title": "Messaggi di trasporto", + "info": "Tutti i messaggi provenienti dai dispositivi" }, - "key-val": { - "key": "Chiave", - "value": "Valore", - "remove-entry": "Rimuovi voce", - "add-entry": "Aggiungi voce", - "no-data": "Nessuna voce" - }, - "layout": { - "layout": "Layout", - "manage": "Gestisci layout", - "settings": "Impostazioni layout", - "color": "Colore", - "main": "Main", - "right": "Destra", - "select": "Select target layout" - }, - "legend": { - "direction": "Direzione", - "position": "Posizione Legenda", - "show-max": "Mostra valore max", - "show-min": "Mostra valore min", - "show-avg": "Mostra valore medio", - "show-total": "Mostra valore totale", - "settings": "Impostazioni legenda", - "min": "min", - "max": "max", - "avg": "avg", - "total": "totale" - }, - "login": { - "login": "Accedi", - "request-password-reset": "Richiesta reset password", - "reset-password": "Reset Password", - "create-password": "Crea Password", - "passwords-mismatch-error": "Le password inserite devono corrispondere!", - "password-again": "Ripeti Password", - "sign-in": "Please sign in", - "username": "Nome utente (email)", - "remember-me": "Ricordami", - "forgot-password": "Password dimenticata?", - "password-reset": "Password reset", - "new-password": "Nuova password", - "new-password-again": "Ripeti nuova password", - "password-link-sent-message": "Link reset password inviato con successo!", - "email": "Email", - "login-with": "Accedi con {{name}}", - "or": "o" - }, - "position": { - "top": "Alto", - "bottom": "Basso", - "left": "Sinistra", - "right": "Destra" - }, - "profile": { - "profile": "Profilo", - "last-login-time": "Ultimo accesso", - "change-password": "Modifica Password", - "current-password": "Password attuale" - }, - "relation": { - "relations": "Relazioni", - "direction": "Direzione", - "search-direction": { - "FROM": "Da", - "TO": "A" + "activity": { + "title": "Attività" + }, + "documentation": { + "title": "Documentazione", + "add-link": "Aggiungi link", + "add-link-title": "Aggiungi link alla documentazione", + "name": "Nome", + "name-required": "Il nome è obbligatorio.", + "link": "Link", + "link-required": "Il link è obbligatorio.", + "columns": "Colonne" + }, + "quick-links": { + "title": "Link rapidi", + "add-link": "Aggiungi link", + "add-link-title": "Aggiungi link rapido", + "quick-link": "Link rapido", + "quick-link-required": "Il link rapido è obbligatorio.", + "no-links-matching": "Nessun link corrispondente a '{{name}}' trovato.", + "columns": "Colonne" + }, + "recent-dashboards": { + "title": "Dashboard", + "last": "Ultima visualizzazione", + "starred": "Preferiti", + "name": "Nome", + "last-viewed": "Ultima visualizzazione", + "no-last-viewed-dashboards": "Nessuna dashboard visualizzata di recente" + }, + "configured-features": { + "title": "Funzionalità configurate", + "info": "Stato delle funzionalità che richiedono configurazione", + "email-feature": "Email", + "sms-feature": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "feature-configured": "Funzionalità configurata.\nClicca per configurare", + "feature-not-configured": "Funzionalità non configurata.\nClicca per configurare" + }, + "version-info": { + "title": "Versione", + "contact-us": "Contattaci", + "current-version": "Versione attuale", + "current": "Attuale", + "available-version": "Versione disponibile", + "available": "Disponibile", + "upgrade": "Aggiorna", + "version-is-up-to-date": "La versione è aggiornata" + }, + "usage-info": { + "title": "Utilizzo", + "entities": "Entità", + "api-calls": "Chiamate API" + }, + "functions": { + "title": "Funzionalità", + "pe-feature-tooltip": "Solo su ThingsBoard\nProfessional Edition", + "switch-to-pe": "Passa alla PE", + "alarms": "Allarmi", + "dashboards": "Dashboard", + "entities-and-relations": "Entità e relazioni", + "profiles": "Profili", + "advanced-features": "Funzionalità avanzate", + "notification-center": "Centro notifiche", + "api-usage": "Utilizzo API", + "customers": "Clienti", + "customers-hierarchy": "Gerarchia clienti", + "roles-and-permissions": "Ruoli e permessi", + "groups": "Gruppi", + "integrations": "Integrazioni", + "solution-templates": "Template di soluzioni", + "scheduler": "Pianificatore", + "white-labeling": "Personalizzazione del marchio" + }, + "devices": { + "view-docs": "Vedi documentazione", + "inactive": "Inattivo", + "active": "Attivo", + "total": "Totale" + }, + "alarms": { + "critical": "Critico", + "assigned-to-me": "Assegnato a me", + "total": "Totale" + }, + "getting-started": { + "get-started": "Inizia", + "finish": "Fine", + "done-welcome-title": "Benvenuto a bordo", + "done-welcome-text": "Hai fatto un ottimo lavoro!", + "sys-admin": { + "step1": { + "title": "Crea Tenant e Amministratore Tenant", + "content": "

Un tenant è un individuo o un'organizzazione che possiede o gestisce dispositivi e asset. Il tenant può avere più utenti amministratori, clienti, dispositivi e asset.

L'Amministratore Tenant può creare e gestire dispositivi, asset, clienti e dashboard all'interno dell'account tenant.

Segui la documentazione per sapere come fare:

", + "how-to-create-tenant": "Come creare Tenant e Amministratore Tenant" }, - "direction-type": { - "FROM": "da", - "TO": "a" + "step2": { + "title": "Configura la funzionalità: Server di posta", + "content": "

La configurazione del server di posta è essenziale per l'attivazione degli utenti, il recupero password e l'invio delle notifiche di allarme.

Segui la documentazione per sapere come fare:

", + "how-to-configure-mail-server": "Come configurare il Server di posta" }, - "from-relations": "Relazioni in uscita", - "to-relations": "Relazioni in ingresso", - "selected-relations": "{ count, plural, =1 {1 relazione selezionata} other {# relazioni selezionate} }", - "type": "Tipo", - "to-entity-type": "A tipo entità", - "to-entity-name": "A nome entità", - "from-entity-type": "Da tipo entità", - "from-entity-name": "Da nome entità", - "to-entity": "A entità", - "from-entity": "Da entità", - "delete": "Elimina relazione", - "relation-type": "Tipo di relazione", - "relation-type-required": "Tipo di relazione obbligatorio.", - "any-relation-type": "Ogni tipo", - "add": "Aggiungi relazione", - "edit": "Modifica relazione", - "delete-to-relation-title": "Sei sicuro di voler eliminare la relazione con l'entità '{{entityName}}'?", - "delete-to-relation-text": "Attenzione, dopo la conferma l'entità '{{entityName}}' sarà scollegata dall'entità corrente.", - "delete-to-relations-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 relazione} other {# relazioni} }?", - "delete-to-relations-text": "Attenzione, dopo la conferma tutte le relazioni selezionate saranno rimosse e le corrispondenti entità scollegate da quella corrente.", - "delete-from-relation-title": "Sei sicuro di voler eliminare la relazione dall'entità '{{entityName}}'?", - "delete-from-relation-text": "Attenzione, dopo la conferma l'entità corrente sarà scollegata dall'entità '{{entityName}}'.", - "delete-from-relations-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 relazione} other {# relazioni} }?", - "delete-from-relations-text": "Attenzione, dopo la conferma tutte le relazioni selezionate saranno rimosse e l'entità corrente scollegata dalle corrispondenti entità.", - "remove-relation-filter": "Rimuovi filtro relazioni", - "add-relation-filter": "Aggiungi filtro relazioni", - "any-relation": "Qualsiasi relazione", - "relation-filters": "Filtri relazioni", - "additional-info": "Informazioni aggiuntive (JSON)", - "invalid-additional-info": "Impossibile analizzare le informazioni aggiuntive in JSON." - }, - "rulechain": { - "rulechain": "Rule chain", - "rulechains": "Rule chain", - "root": "Root", - "delete": "Cancella rule chain", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "description": "Descrizione", - "add": "Aggiungi Rule Chain", - "set-root": "Imposta la rule chain come root", - "set-root-rulechain-title": "Sei sicuro di voler impostare la rule chain '{{ruleChainName}}' come root?", - "set-root-rulechain-text": "Dopo la conferma la rule chain diverrà root a gestirà tutti i messaggi in arrivo.", - "delete-rulechain-title": "Sei sicuro di voler eliminare la rule chain '{{ruleChainName}}'?", - "delete-rulechain-text": "Attenzione, dopo la conferma la rule chain e tutti i dati relativi non saranno più recuperabili.", - "delete-rulechains-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 rule chain} other {# rule chain} }?", - "delete-rulechains-action-title": "Elimina { count, plural, =1 {1 rule chain} other {# rule chain} }", - "delete-rulechains-text": "Attenzione, dopo la conferma tutte le rule chain selezionate saranno rimosse e tutti i relativi dati non saranno più recuperabili.", - "add-rulechain-text": "Aggiungi nuova rule chain", - "no-rulechains-text": "Nessuna rule chain trovata", - "rulechain-details": "Dettagli rule chain", - "details": "Dettagli", - "events": "Eventi", - "system": "Sistema", - "import": "Importa rule chain", - "export": "Esporta rule chain", - "export-failed-error": "Impossibile esportare rule chain: {{error}}", - "create-new-rulechain": "Crea nuova rule chain", - "rulechain-file": "File rule chain", - "invalid-rulechain-file-error": "Impossibile importare rule chain: struttura dati rule chain non valida.", - "copyId": "Copia Id rule chain", - "idCopiedMessage": "Id rule chain copiato negli appunti", - "select-rulechain": "Seleziona rule chain", - "no-rulechains-matching": "Nessuna rule chain corrispondente a '{{entity}}' è stata trovata.", - "rulechain-required": "Rule chain obbligatoria", - "management": "Gestione regole", - "debug-mode": "Modalità debug" - }, - "rulenode": { - "details": "Dettagli", - "events": "Eventi", - "search": "Ricerca nodi", - "open-node-library": "Apri libreria nodi", - "add": "Aggiungi nodo regola", - "name": "Nome", - "name-required": "Nome obbligatorio.", - "type": "Tipo", - "delete": "Elimina nodo regola", - "select-all-objects": "Seleziona tutti i nodi e le connessioni", - "deselect-all-objects": "Deseleziona tutti i nodi e le connessioni", - "delete-selected-objects": "Cancella nodi e connessioni selezionate", - "delete-selected": "Elimina selezionati", - "select-all": "Seleziona tutto", - "copy-selected": "Copia selezionata", - "deselect-all": "Deseleziona tutto", - "rulenode-details": "Dettagli nodo regola", - "debug-mode": "Modalità debug", - "configuration": "Configurazione", - "link": "Link", - "link-details": "Dettagli link nodo regola", - "add-link": "Aggiungi link", - "link-label": "Etichetta link", - "link-label-required": "Etichetta link obbligatoria.", - "custom-link-label": "Etichetta link personalizzata", - "custom-link-label-required": "Etichetta link personalizzata obbligatoria.", - "link-labels": "Etichette link", - "link-labels-required": "Etichette link richieste.", - "no-link-labels-found": "Nessuna etichetta link trovata.", - "no-link-label-matching": "'{{label}}' non trovata.", - "create-new-link-label": "Creane una nuova!", - "type-filter": "Filtro", - "type-filter-details": "Filtra i messaggi in arrivo con le condizioni configurate", - "type-enrichment": "Enrichment", - "type-enrichment-details": "Aggiungi informazioni addizionali nei metadati del messaggio", - "type-transformation": "Trasformazione", - "type-transformation-details": "Modifica payload messaggio e metadati", - "type-action": "Azioni", - "type-action-details": "Perform special action", - "type-external": "External", - "type-external-details": "Interagisci con un sistema esterno", - "type-rule-chain": "Rule Chain", - "type-rule-chain-details": "Inoltra i messaggi in arrivo ad una specifica Rule Chain", - "type-input": "Input", - "type-input-details": "Logical input of Rule Chain, forwards incoming messages to next related Rule Node", - "type-unknown": "Sconosciuto", - "type-unknown-details": "Nodo regola non trovato", - "directive-is-not-loaded": "Defined configuration directive '{{directiveName}}' is not available.", - "ui-resources-load-error": "Failed to load configuration ui resources.", - "invalid-target-rulechain": "Unable to resolve target rule chain!", - "test-script-function": "Test script function", - "message": "Messaggio", - "message-type": "Tipo messaggio", - "select-message-type": "Seleziona tipo messaggio", - "message-type-required": "Tipo messaggio obbligatorio", - "metadata": "Metadata", - "metadata-required": "Metadata entries can't be empty.", - "output": "Output", - "test": "Test", - "help": "Aiuto" - }, - "tenant": { - "tenant": "Tenant", - "tenants": "Tenant", - "management": "Gestione Tenant", - "add": "Aggiungi Tenant", - "admins": "Amministratori", - "manage-tenant-admins": "Gestisci amministratori tenant", - "delete": "Cancella tenant", - "add-tenant-text": "Aggiungi nuovo tenant", - "no-tenants-text": "Nessun tenant trovato", - "tenant-details": "Dettagli tenant", - "delete-tenant-title": "Sei sicuro di voler eliminare il tenant '{{tenantTitle}}'?", - "delete-tenant-text": "Attenzione, dopo la conferma il tenant e tutti i suoi dati non saranno più recuperabili.", - "delete-tenants-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 tenant} other {# tenant} }?", - "delete-tenants-action-title": "Elimina { count, plural, =1 {1 tenant} other {# tenant} }", - "delete-tenants-text": "Attenzione, dopo la conferma tutti i tenant selezionati saranno eliminati e tutti i loro dati non saranno più recuperabili.", - "title": "Titolo", - "title-required": "Titolo obbligatorio.", - "description": "Descrizione", - "details": "Dettagli", - "events": "Eventi", - "copyId": "Copia Id tenant", - "idCopiedMessage": "Id tenant copiato negli appunti", - "select-tenant": "Seleziona tenant", - "no-tenants-matching": "Nessun tenant corrispondente a '{{entity}}' è stato trovato.", - "tenant-required": "Tenant obbligatorio" - }, - "timeinterval": { - "seconds-interval": "{ seconds, plural, =1 {1 secondo} other {# secondi} }", - "minutes-interval": "{ minutes, plural, =1 {1 minuto} other {# minuti} }", - "hours-interval": "{ hours, plural, =1 {1 ora} other {# ore} }", - "days-interval": "{ days, plural, =1 {1 giorno} other {# giorni} }", - "days": "Giorni", - "hours": "Ore", - "minutes": "Minuti", - "seconds": "Secondi", - "advanced": "Avanzate" - }, - "timewindow": { - "days": "{ days, plural, =1 { giorno } other {# giorni } }", - "hours": "{ hours, plural, =0 { ora } =1 {1 ora } other {# ore } }", - "minutes": "{ minutes, plural, =0 { minuto } =1 {1 minuto } other {# minuti } }", - "seconds": "{ seconds, plural, =0 { secondo } =1 {1 secondo } other {# secondi } }", - "realtime": "Realtime", - "history": "Cronologia", - "last-prefix": "ultimo", - "period": "da {{ startTime }} a {{ endTime }}", - "edit": "Modifica intervallo temporale", - "date-range": "Intervallo date", - "last": "Ultimo", - "time-period": "Intervallo temporale", - "hide": "Nascondi" - }, - "user": { - "user": "Utente", - "users": "Utenti", - "customer-users": "Utente cliente", - "tenant-admins": "Amministratori Tenant", - "sys-admin": "Amministratore di sistema", - "tenant-admin": "Amministratore tenant", - "customer": "Cliente", - "anonymous": "Anonimo", - "add": "Aggiungi Utente", - "delete": "Elimina utente", - "add-user-text": "Aggiungi nuovo utente", - "no-users-text": "Nessun utente trovato", - "user-details": "Dettagli utente", - "delete-user-title": "Sei sicuro di voler eliminare l'utente '{{userEmail}}'?", - "delete-user-text": "Attenzione, dopo la conferma l'utente e tutti i suoi dati non saranno più recuperabili.", - "delete-users-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 utente} other {# utenti} }?", - "delete-users-action-title": "Elimina { count, plural, =1 {1 utente} other {# utenti} }", - "delete-users-text": "Attenzione, dopo la conferma tutti gli utenti selezionati saranno eliminati e tutti i relativi dati non saranno più recuperabili.", - "activation-email-sent-message": "Email di attivazione inviata con successo!", - "resend-activation": "Invia di nuovo attivazione", - "email": "Email", - "email-required": "Email obbligatoria.", - "invalid-email-format": "Formato email non valido.", - "first-name": "Nome", - "last-name": "Cognome", - "description": "Descrizione", - "default-dashboard": "Dashboard di default", - "always-fullscreen": "Sempre a schermo intero", - "select-user": "Seleziona utente", - "no-users-matching": "Nessun utente corrispondente a '{{entity}}' è stato trovato.", - "user-required": "Utente obbligatorio", - "activation-method": "Metodo di attivazione", - "display-activation-link": "Mostra link di attivazione", - "send-activation-mail": "Invia email di attivazione", - "activation-link": "Link attivazione utente", - "activation-link-text": "Per attivare l'utente utilizza il seguente link di attivazione :", - "copy-activation-link": "Copia link di attivazione", - "activation-link-copied-message": "Link di attivazione utente copiato negli appunti", - "details": "Dettagli", - "login-as-tenant-admin": "Accedi come Amministratore tenant", - "login-as-customer-user": "Accedi come Utente cliente", - "disable-account": "Disabilita account utente", - "enable-account": "Abilita account utente", - "enable-account-message": "L'account utente è stato abilitato correttamente!", - "disable-account-message": "L'account utente è stato disabilitato correttamente!" - }, - "value": { - "type": "Tipo valore", - "string": "String", - "string-value": "Valore string", - "integer": "Integer", - "integer-value": "Valore integer", - "invalid-integer-value": "Valore integer non valido", - "double": "Double", - "double-value": "Valore double", - "boolean": "Boolean", - "boolean-value": "Valore boolean", - "false": "Falso", - "true": "Vero", - "long": "Long" - }, - "widget": { - "widget-library": "Libreria Widget", - "widget-bundle": "Bundle widget", - "select-widgets-bundle": "Seleziona bundle widget", - "management": "Gestione widget", - "editor": "Editor Widget", - "widget-type-not-found": "Problem loading widget configuration.
Probably associated\n widget type was removed.", - "widget-type-load-error": "Widget non caricato a causa dei seguenti errori:", - "remove": "Elimina widget", - "edit": "Modifica widget", - "remove-widget-title": "sei sicuro di voler eliminare il widget '{{widgetTitle}}'?", - "remove-widget-text": "Dopo la conferma il widget e tutti i suoi dati non saranno più recuperabili.", - "timeseries": "Time series", - "search-data": "Cerca dati", - "no-data-found": "Nessun dato trovato", - "latest": "Ultimi valori", - "rpc": "Control widget", - "alarm": "Alarm widget", - "static": "Static widget", - "select-widget-type": "Seleziona tipo widget", - "missing-widget-title-error": "Il tiolo del widget deve essere specificato!", - "widget-saved": "Widget salvato", - "unable-to-save-widget-error": "Impossibile salvare il widget! Sono presenti degli errori!", - "save": "Salva widget", - "saveAs": "Salva widget come", - "save-widget-type-as": "Salva tipo widget come", - "save-widget-type-as-text": "Please enter new widget title and/or select target widgets bundle", - "toggle-fullscreen": "Commuta modalità schermo intero", - "run": "Esegui widget", - "title": "Titolo widget", - "title-required": "Titolo widget obbligatorio.", - "type": "Tipo widget", - "resources": "Risorse", - "resource-url": "JavaScript/CSS URL", - "remove-resource": "Rimuovi risorsa", - "add-resource": "Aggiungi risorsa", - "html": "HTML", - "tidy": "Tidy", - "css": "CSS", - "settings-schema": "Impostazioni schema", - "datakey-settings-schema": "Impostazioni Data key schema", - "javascript": "Javascript", - "add-widget-type": "Aggiungi nuovo tipo widget", - "widget-template-load-failed-error": "Caricamento template widget fallito!", - "add": "Aggiungi Widget", - "undo": "Annulla modifiche widget", - "export": "Esporta widget" - }, - "widget-action": { - "header-button": "Widget header button", - "open-dashboard-state": "Navigate to new dashboard state", - "update-dashboard-state": "Aggiorna lo stato della dashboard attuale", - "open-dashboard": "Navigate to other dashboard", - "custom": "Custom action", - "target-dashboard-state": "Target dashboard state", - "target-dashboard-state-required": "Target dashboard state is required", - "set-entity-from-widget": "Set entity from widget", - "target-dashboard": "Target dashboard", - "open-right-layout": "Open right dashboard layout (mobile view)" - }, - "widgets-bundle": { - "current": "Bundle corrente", - "widgets-bundles": "Bundle Widget", - "add": "Aggiungi Bundle Widget", - "delete": "Cancella bundle widget", - "title": "Titolo", - "title-required": "Titolo obbligatorio.", - "add-widgets-bundle-text": "Aggiungi nuovo bundle widget", - "no-widgets-bundles-text": "Nessun bundle widget trovato", - "empty": "Bundle widget vuoto", - "details": "Dettagli", - "widgets-bundle-details": "Dettagli bundle widget", - "delete-widgets-bundle-title": "Sei sicuro di voler eliminare il bundle widget '{{widgetsBundleTitle}}'?", - "delete-widgets-bundle-text": "Attenzione, dopo la conferma il bundle widget e tutti i suoi dati non saranno più recuperabili.", - "delete-widgets-bundles-title": "Sei sicuro di voler eliminare { count, plural, =1 {1 bundle widget} other {# bundle widget} }?", - "delete-widgets-bundles-action-title": "Elimina { count, plural, =1 {1 bundle widget} other {# bundle widget} }", - "delete-widgets-bundles-text": "Attenzione, dopo la conferma tutti i bundle widget selezionati saranno rimossi e tutti i loro dati non saranno più recuperabili.", - "no-widgets-bundles-matching": "Nessun bundle widget corrispondente a '{{widgetsBundle}}' è stato trovato.", - "widgets-bundle-required": "Bundle widget obbligatorio.", - "system": "Sistema", - "import": "Importa bundle widget", - "export": "Esporta bundle widget", - "export-failed-error": "Impossibile esportare bundle widget: {{error}}", - "create-new-widgets-bundle": "Crea nuovo bundle widget", - "widgets-bundle-file": "File bundle widget", - "invalid-widgets-bundle-file-error": "Impossibile importare bundle widget: struttura dati non valida." - }, - "widget-config": { - "data": "Dati", - "settings": "Impostazioni", - "advanced": "Avanzate", - "title": "Titolo", - "title-tooltip": "Tooltip titolo", - "general-settings": "Impostazioni generali", - "display-title": "Mostra titolo", - "drop-shadow": "Drop shadow", - "enable-fullscreen": "Abilita schermo intero", - "background-color": "Colore sfondo", - "text-color": "Colore testo", - "padding": "Padding", - "margin": "Margin", - "widget-style": "Stile Widget", - "title-style": "Stile titolo", - "mobile-mode-settings": "Impostazioni modalità mobile", - "order": "Ordinamento", - "height": "Altezza", - "units": "Simbolo speciale da mostrare accanto al valore", - "decimals": "Numero di cifre decimali", - "timewindow": "Intervallo temporale", - "use-dashboard-timewindow": "Usa intervallo temporale dashboard", - "display-timewindow": "Mostra intervallo temporale", - "display-legend": "Mostra legenda", - "datasources": "Sorgenti dei dati", - "maximum-datasources": "Massimo { count, plural, =1 {1 sorgente dati consentita.} other {# sorgenti dati consentite} }", - "datasource-type": "Tipo", - "datasource-parameters": "Parametri", - "remove-datasource": "Rimuovi sorgente dati", - "add-datasource": "Aggiungi sorgente dati", - "target-device": "Dispositivo Target", - "alarm-source": "Sorgente Allarme", - "actions": "Azioni", - "action": "Azione", - "add-action": "Aggiungi azione", - "search-actions": "Ricerca azioni", - "action-source": "Sorgente azione", - "action-source-required": "Sorgente azione obbligatoria.", - "action-name": "Nome", - "action-name-required": "Nome azione obbligatorio.", - "action-name-not-unique": "Un'altra azione con lo stesso nome è già presente.\nIl nome di una azione dovrebbe essere univoco all'interno della stessa sorgente.", - "action-icon": "Icona", - "action-type": "Tipo", - "action-type-required": "Tipo azione obbligatorio.", - "edit-action": "Modifica azione", - "delete-action": "Cancella azione", - "delete-action-title": "Cancella azione del widget", - "delete-action-text": "Sei sicuro di voler cancellare l'azione del widget '{{actionName}}'?", - "display-icon": "Mostra icona titolo", - "icon-color": "Colore dell'icona", - "icon-size": "Dimensione dell'icona" - }, - "widget-type": { - "import": "Importa un tipo di widget", - "export": "Esporta un tipo di widget", - "export-failed-error": "Impossibile esportare il tipo di widget: {{error}}", - "create-new-widget-type": "Crea un nuovo tipo di widget", - "widget-type-file": "File tipo di widget", - "invalid-widget-type-file-error": "Impossibile importare un tipo di widget: struttura dati del widget non valida." - }, - "widgets": { - "date-range-navigator": { - "localizationMap": { - "Sun": "Dom", - "Mon": "Lun", - "Tue": "Mar", - "Wed": "Mer", - "Thu": "Gio", - "Fri": "Ven", - "Sat": "Sab", - "Jan": "Gen", - "Feb": "Feb", - "Mar": "Mar", - "Apr": "Apr", - "May": "Mag", - "Jun": "Giu", - "Jul": "Lug", - "Aug": "Ago", - "Sep": "Set", - "Oct": "Ott", - "Nov": "Nov", - "Dec": "Dic", - "January": "Gennaio", - "February": "Febbraio", - "March": "Marzo", - "April": "Aprile", - "June": "Giugno", - "July": "Luglio", - "August": "Agosto", - "September": "Settembre", - "October": "Ottobre", - "November": "Novembre", - "December": "Dicembre", - "Custom Date Range": "Intervallo date personalizzato", - "Date Range Template": "Modello intervallo date", - "Today": "Oggi", - "Yesterday": "Ieri", - "This Week": "Questa settimana", - "Last Week": "La settimana scorsa", - "This Month": "Questo mese", - "Last Month": "Lo scorso mese", - "Year": "Anno", - "This Year": "Quest'anno", - "Last Year": "L'anno scorso", - "Date picker": "Date picker", - "Hour": "Ora", - "Day": "Giorno", - "Week": "Settimana", - "2 weeks": "2 Settimane", - "Month": "Mese", - "3 months": "3 Mesi", - "6 months": "6 Mesi", - "Custom interval": "Intervallo personalizzato", - "Interval": "Intervallo", - "Step size": "Dimensione del passo", - "Ok": "Ok" - } + "step3": { + "title": "Configura la funzionalità: Fornitore SMS", + "content": "

Configura i fornitori SMS per notificare i clienti sugli allarmi tramite SMS.

Segui la documentazione per sapere come fare:

", + "how-to-configure-sms-provider": "Come configurare il Fornitore SMS" + }, + "step4": { + "title": "Configura la funzionalità: Personalizzazione marchio", + "content": "

Personalizza facilmente il logo e lo schema colori della tua azienda o prodotto senza codifica e senza riavviare il servizio.

Segui la documentazione per sapere come fare:

" }, - "input-widgets": { - "attribute-not-allowed": "Questo widget non può usare un parametro di tipo attributo", - "date": "Data", - "discard-changes": "Annulla modifiche", - "entity-attribute-required": "E' richiesta un'entità di tipo attributo", - "entity-timeseries-required": "E' richiesta un'entità di tipo serie temporale", - "not-allowed-entity": "L'entità selezionata non può avere attributi condivisi", - "no-attribute-selected": "Nessun attributo selezionato", - "no-datakey-selected": "Nessuna datakey selezionata", - "no-entity-selected": "Nessuna entità selezionata", - "no-image": "Nessuna immagine", - "no-support-web-camera": "Web camera non supportata", - "no-timeseries-selected": "Nessuna serie temporale selezionata", - "switch-attribute-value": "Cambia il valore dell'attributo", - "switch-camera": "Cambia camera", - "switch-timeseries-value": "Cambia il valore della serie temporale", - "take-photo": "Fai una foto", - "time": "Tempo", - "timeseries-not-allowed": "Questo widget non può usare un parametro di tipo serie temporale", - "update-failed": "Aggiornamento fallito", - "update-successful": "Aggiornamento eseguito con successo", - "update-attribute": "Aggiorna attributo", - "update-timeseries": "Aggiorna serie temporale", - "value": "Valore" + "step5": { + "title": "Configura la funzionalità: 2FA", + "content": "

Migliora la sicurezza degli account sulla piattaforma con l'autenticazione a due fattori.

Segui la documentazione per sapere come fare:

" + }, + "step6": { + "title": "Configura la funzionalità: OAuth 2", + "content": "

Semplifica l'accesso per utenti tenant e clienti con Single Sign-On tramite OAuth 2.0.

Segui la documentazione per sapere come fare:

" } - }, - "icon": { - "icon": "Icona", - "select-icon": "Seleziona icona", - "material-icons": "Icone Material", - "show-all": "Mostra tutte le icone" - }, - "custom": { - "widget-action": { - "action-cell-button": "Pulsante azione cella", - "row-click": "Click sulla riga", - "polygon-click": "Click sul poligono", - "marker-click": "Click sul marker", - "tooltip-tag-action": "Azione tooltip", - "node-selected": "Click su nodo selezionato", - "element-click": "Click su elemento HTML", - "pie-slice-click": "Click sulla fetta", - "row-double-click": "Doppio click sulla riga" + }, + "tenant-admin": { + "step1": { + "title": "Crea dispositivo", + "content": "

Proviamo a fornire il tuo primo dispositivo alla piattaforma tramite UI. Segui la documentazione per sapere come fare:

", + "how-to-create-device": "Come creare un Dispositivo" + }, + "step2": { + "title": "Connetti dispositivo", + "content-before": "

Per connettere il dispositivo devi ottenere le credenziali. Per questa guida consigliamo di usare le credenziali auto-generate (token di accesso).

  • Vai alla tabella dispositivi
  • Clicca sulla riga del dispositivo per aprire i dettagli
  • Premi il pulsante \"Copia token di accesso\"

Usa semplici comandi per inviare dati via HTTP. Ricorda di sostituire $ACCESS_TOKEN con il token del tuo dispositivo:

", + "ubuntu": { + "install-curl": "Installa cURL per Ubuntu:" + }, + "macos": { + "install-curl": "Installa cURL per MacOS:" + }, + "windows": { + "install-curl": "Da Windows 10 b17063, cURL è disponibile di default." + }, + "replace-access-token": "Sostituisci $ACCESS_TOKEN con il token del tuo dispositivo:", + "content-after": "

Puoi anche usare altri protocolli come MQTT, CoAP, ecc.

Segui la documentazione per sapere come fare:

", + "how-to-connect-device": "Come connettere un Dispositivo" + }, + "step3": { + "title": "Crea dashboard", + "content": "

Crea una dashboard per visualizzare i dati delle entità come dispositivi, asset, ecc.

Segui la documentazione per sapere come fare:

", + "how-to-create-dashboard": "Come creare una Dashboard" + }, + "step4": { + "title": "Configura regole di allarme", + "alarm-rules": "Regole di allarme", + "content": "

Generiamo un allarme quando la temperatura raggiunge i 25°C. Segui la documentazione per sapere come fare:

", + "how-to-configure-alarm-rules": "Come configurare le Regole di Allarme" + }, + "step5": { + "title": "Crea allarme", + "content-before": "

Per attivare l'allarme, invia un nuovo valore di telemetria di 26°C o superiore.

", + "replace-access-token": "Sostituisci $ACCESS_TOKEN con il token del tuo dispositivo:", + "content-after": "

Segui la documentazione per sapere come fare:

", + "how-to-create-alarm": "Come creare un Allarme" + }, + "step6": { + "title": "Crea cliente e condividi dashboard", + "content": "

Creando dashboard per gli utenti finali, ogni cliente può vedere solo i propri dispositivi; i dati degli altri clienti saranno nascosti.

Segui la documentazione per sapere come fare:

" } - }, - "language": { - "language": "Lingua" + } + } + }, + "icon": { + "icon": "Icona", + "icons": "Icone", + "select-icon": "Seleziona icona", + "material-icons": "Icone Material", + "show-all": "Mostra tutte le icone", + "search-icon": "Cerca icona", + "no-icons-found": "Nessuna icona trovata per '{{iconSearch}}'" + }, + "phone-input": { + "phone-input-label": "Numero di telefono", + "phone-input-required": "Il numero di telefono è obbligatorio", + "phone-input-validation": "Il numero di telefono non è valido o non è possibile", + "phone-input-pattern": "Numero di telefono non valido. Deve essere nel formato E.164, es. {{phoneNumber}}", + "phone-input-hint": "Numero di telefono in formato E.164, es. {{phoneNumber}}" + }, + "custom": { + "widget-action": { + "action-cell-button": "Pulsante nella cella azione", + "row-click": "Al clic sulla riga", + "cell-click": "Al clic sulla cella", + "polygon-click": "Al clic sul poligono", + "marker-click": "Al clic sul marcatore", + "circle-click": "Al clic sul cerchio", + "tooltip-tag-action": "Azione sull'etichetta del tooltip", + "node-selected": "Al nodo selezionato", + "element-click": "Al clic sull'elemento HTML", + "pie-slice-click": "Al clic sulla fetta", + "row-double-click": "Al doppio clic sulla riga", + "cell-double-click": "Al doppio clic sulla cella", + "card-click": "Al clic sulla scheda", + "click": "Al clic" + } + }, + "paginator": { + "items-per-page": "Elementi per pagina:", + "first-page-label": "Prima pagina", + "last-page-label": "Ultima pagina", + "next-page-label": "Pagina successiva", + "previous-page-label": "Pagina precedente", + "items-per-page-separator": "di" + }, + "language": { + "language": "Lingua", + "locales": { + "ar_AE": "العربية (الإمارات العربية المتحدة)", + "ca_ES": "català (Espanya)", + "cs_CZ": "čeština (Česko)", + "da_DK": "dansk (Danmark)", + "de_DE": "Deutsch (Deutschland)", + "el_GR": "Ελληνικά (Ελλάδα)", + "en_US": "English (United States)", + "es_ES": "español (España)", + "fa_IR": "فارسی (ایران)", + "fr_FR": "français (France)", + "it_IT": "italiano (Italia)", + "ja_JP": "日本語 (日本)", + "ka_GE": "ქართული (საქართველო)", + "ko_KR": "한국어 (대한민국)", + "lt_LT": "lietuvių (Lietuva)", + "lv_LV": "latviešu (Latvija)", + "nl_BE": "Nederlands (België)", + "pl_PL": "polski (Polska)", + "pt_BR": "português (Brasil)", + "ro_RO": "română (România)", + "sl_SI": "slovenščina (Slovenija)", + "tr_TR": "Türkçe (Türkiye)", + "uk_UA": "українська (Україна)", + "zh_CN": "中文 (中国)", + "zh_TW": "中文 (台灣)" } -} + } +} \ No newline at end of file From 693d53924a398744514ddedf0f4d2f77874423f5 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 19 May 2025 17:19:13 +0300 Subject: [PATCH 193/335] License header --- .../client/tools/i18n/TranslationPruner.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java index 9791a67d1f..a84adb94ec 100644 --- a/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java +++ b/tools/src/main/java/org/thingsboard/client/tools/i18n/TranslationPruner.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2025 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. + */ package org.thingsboard.client.tools.i18n; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; From b618249c6246ef7bb784f57715009da5ad290131 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 20 May 2025 13:50:18 +0300 Subject: [PATCH 194/335] Introduce entity id for jobs --- .../server/controller/JobController.java | 10 +- .../processor/JobsDeletionTaskProcessor.java | 43 ++++ .../server/service/job/DefaultJobManager.java | 32 +-- .../server/service/job/JobProcessor.java | 2 +- .../server/controller/AbstractWebTest.java | 5 +- .../server/service/job/JobManagerTest.java | 236 ++++++------------ .../server/dao/job/JobService.java | 3 + .../data/housekeeper/HousekeeperTask.java | 4 + .../data/housekeeper/HousekeeperTaskType.java | 3 +- .../common/data/job/DummyJobResult.java | 8 - .../server/common/data/job/Job.java | 15 +- .../server/common/data/job/JobFilter.java | 4 + .../server/common/data/job/JobResult.java | 3 - .../dao/housekeeper/CleanUpService.java | 4 + .../server/dao/job/DefaultJobService.java | 11 +- .../thingsboard/server/dao/job/JobDao.java | 7 +- .../server/dao/model/ModelConstants.java | 3 +- .../server/dao/model/sql/JobEntity.java | 15 +- .../server/dao/sql/job/JobRepository.java | 32 ++- .../server/dao/sql/job/JpaJobDao.java | 21 +- .../main/resources/sql/schema-entities.sql | 3 +- .../rule/engine/api/JobManager.java | 2 +- 22 files changed, 237 insertions(+), 229 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/housekeeper/processor/JobsDeletionTaskProcessor.java diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java index 9719306823..55c68a461f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/JobController.java +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.rule.engine.api.JobManager; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.job.Job; @@ -36,7 +37,6 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.rule.engine.api.JobManager; import java.util.List; import java.util.UUID; @@ -76,12 +76,18 @@ public class JobController extends BaseController { @Parameter(description = SORT_ORDER_DESCRIPTION) @RequestParam(required = false) String sortOrder, @RequestParam(required = false) List types, - @RequestParam(required = false) List statuses) throws ThingsboardException { + @RequestParam(required = false) List statuses, + @RequestParam(required = false) List entities, + @RequestParam(required = false) Long startTime, + @RequestParam(required = false) Long endTime) throws ThingsboardException { // todo check permissions PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); JobFilter filter = JobFilter.builder() .types(types) .statuses(statuses) + .entities(entities) + .startTime(startTime) + .endTime(endTime) .build(); return jobService.findJobsByFilter(getTenantId(), filter, pageLink); } diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/JobsDeletionTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/JobsDeletionTaskProcessor.java new file mode 100644 index 0000000000..f0f829770a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/JobsDeletionTaskProcessor.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.service.housekeeper.processor; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.housekeeper.HousekeeperTask; +import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType; +import org.thingsboard.server.dao.job.JobService; + +@Component +@RequiredArgsConstructor +@Slf4j +public class JobsDeletionTaskProcessor extends HousekeeperTaskProcessor { + + private final JobService jobService; + + @Override + public void process(HousekeeperTask task) throws Exception { + int deletedCount = jobService.deleteJobsByEntityId(task.getTenantId(), task.getEntityId()); + log.debug("[{}][{}][{}] Deleted {} jobs", task.getTenantId(), task.getEntityId().getEntityType(), task.getEntityId(), deletedCount); + } + + @Override + public HousekeeperTaskType getTaskType() { + return HousekeeperTaskType.DELETE_JOBS; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 314581acef..379e72d28b 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -22,7 +22,6 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.rule.engine.api.JobManager; -import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; @@ -32,13 +31,9 @@ import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.Task; import org.thingsboard.server.common.data.job.task.TaskResult; -import org.thingsboard.server.common.data.notification.info.GeneralNotificationInfo; -import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; -import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.job.JobService; -import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.gen.transport.TransportProtos.TaskProto; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; @@ -63,19 +58,17 @@ public class DefaultJobManager implements JobManager { private final JobService jobService; private final JobStatsService jobStatsService; - private final NotificationCenter notificationCenter; private final PartitionService partitionService; private final TasksQueueConfig queueConfig; private final Map jobProcessors; private final Map>> taskProducers; private final ExecutorService executor; - public DefaultJobManager(JobService jobService, JobStatsService jobStatsService, NotificationCenter notificationCenter, - PartitionService partitionService, TaskProducerQueueFactory queueFactory, TasksQueueConfig queueConfig, + public DefaultJobManager(JobService jobService, JobStatsService jobStatsService, PartitionService partitionService, + TaskProducerQueueFactory queueFactory, TasksQueueConfig queueConfig, List jobProcessors) { this.jobService = jobService; this.jobStatsService = jobStatsService; - this.notificationCenter = notificationCenter; this.partitionService = partitionService; this.queueConfig = queueConfig; this.jobProcessors = jobProcessors.stream().collect(Collectors.toMap(JobProcessor::getType, Function.identity())); @@ -105,10 +98,7 @@ public class DefaultJobManager implements JobManager { case COMPLETED, FAILED -> { executor.execute(() -> { try { - if (status == JobStatus.COMPLETED) { - getJobProcessor(job.getType()).onJobCompleted(job); - } - sendJobFinishedNotification(job); + getJobProcessor(job.getType()).onJobFinished(job); } catch (Throwable e) { log.error("Failed to process job update: {}", job, e); } @@ -203,22 +193,6 @@ public class DefaultJobManager implements JobManager { }); } - private void sendJobFinishedNotification(Job job) { - NotificationTemplate template = DefaultNotifications.DefaultNotification.builder() - .name("Job finished") - .subject("${type} task ${status}") - .text("${description} ${status}: ${result}") - .build().toTemplate(); - GeneralNotificationInfo info = new GeneralNotificationInfo(Map.of( - "type", job.getType().getTitle(), - "description", job.getDescription(), - "status", job.getStatus().name().toLowerCase(), - "result", job.getResult().getDescription() - )); - // todo: button to see details (forward to jobs page) - notificationCenter.sendGeneralWebNotification(job.getTenantId(), new TenantAdministratorsFilter(), template, info); - } - private JobProcessor getJobProcessor(JobType jobType) { return jobProcessors.get(jobType); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java index 16ef5e404f..e33878d008 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/JobProcessor.java @@ -29,7 +29,7 @@ public interface JobProcessor { void reprocess(Job job, List taskFailures, Consumer> taskConsumer) throws Exception; - default void onJobCompleted(Job job) {} + default void onJobFinished(Job job) {} JobType getType(); diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index ef658fc17a..b93ff0f623 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -1272,8 +1272,9 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return doGetTypedWithPageLink("/api/jobs?", new TypeReference>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); } - protected List findJobs(JobType... types) throws Exception { - return doGetTypedWithPageLink("/api/jobs?types=" + Arrays.stream(types).map(Enum::name).collect(Collectors.joining(",")) + "&", + protected List findJobs(List types, List entities) throws Exception { + return doGetTypedWithPageLink("/api/jobs?types=" + types.stream().map(Enum::name).collect(Collectors.joining(",")) + + "&entities=" + entities.stream().map(UUID::toString).collect(Collectors.joining(",")) + "&", new TypeReference>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); } diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index dd19ddcea1..14c0e4e847 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.job; -import org.assertj.core.api.ThrowingConsumer; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -24,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; import org.thingsboard.rule.engine.api.JobManager; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.DummyJobConfiguration; @@ -34,7 +34,6 @@ import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.DummyTaskResult; import org.thingsboard.server.common.data.job.task.DummyTaskResult.DummyTaskFailure; -import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.job.JobDao; @@ -53,6 +52,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest @TestPropertySource(properties = { @@ -72,9 +72,14 @@ public class JobManagerTest extends AbstractControllerTest { @Autowired private JobDao jobDao; + private TenantId tenantId; + private Device jobEntity; + @Before public void setUp() throws Exception { loginTenantAdmin(); + tenantId = super.tenantId; + jobEntity = createDevice("Test", "Test"); } @After @@ -84,15 +89,9 @@ public class JobManagerTest extends AbstractControllerTest { @Test public void testSubmitJob_allTasksSuccessful() { int tasksCount = 5; - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("Test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(tasksCount) - .taskProcessingTimeMs(1000) - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(1000) .build()).getId(); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -108,29 +107,18 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(job.getResult().getResults()).isEmpty(); assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount); }); - - checkJobNotification(notification -> { - assertThat(notification.getSubject()).isEqualTo("Dummy job task completed"); - assertThat(notification.getText()).isEqualTo("Test job completed: 5/5 successful, 0 failed"); - }); } @Test public void testSubmitJob_someTasksPermanentlyFailed() { int successfulTasks = 3; int failedTasks = 2; - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("Test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(successfulTasks) - .failedTasksCount(failedTasks) - .errors(List.of("error1", "error2", "error3")) - .retries(2) - .taskProcessingTimeMs(100) - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(successfulTasks) + .failedTasksCount(failedTasks) + .errors(List.of("error1", "error2", "error3")) + .retries(2) + .taskProcessingTimeMs(100) .build()).getId(); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -145,24 +133,13 @@ public class JobManagerTest extends AbstractControllerTest { }); assertThat(jobResult.getCompletedCount()).isEqualTo(jobResult.getTotalCount()); }); - - checkJobNotification(notification -> { - assertThat(notification.getSubject()).isEqualTo("Dummy job task failed"); - assertThat(notification.getText()).isEqualTo("Test job failed: 3/5 successful, 2 failed"); - }); } @Test public void testSubmitJob_taskTimeout() { - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("Test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(1) - .taskProcessingTimeMs(5000) // bigger than DummyTaskProcessor.getTaskProcessingTimeout() - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(1) + .taskProcessingTimeMs(5000) // bigger than DummyTaskProcessor.getTaskProcessingTimeout() .build()).getId(); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -177,15 +154,9 @@ public class JobManagerTest extends AbstractControllerTest { @Test public void testCancelJob_whileRunning() throws Exception { int tasksCount = 100; - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(tasksCount) - .taskProcessingTimeMs(100) - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(100) .build()).getId(); Thread.sleep(500); @@ -203,15 +174,9 @@ public class JobManagerTest extends AbstractControllerTest { @Test public void testCancelJob_simulateTaskProcessorRestart() throws Exception { int tasksCount = 10; - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(tasksCount) - .taskProcessingTimeMs(500) - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(500) .build()).getId(); // simulate cancelled jobs are forgotten @@ -239,16 +204,10 @@ public class JobManagerTest extends AbstractControllerTest { loginSysAdmin(); createDifferentTenant(); - TenantId tenantId = this.differentTenantId; - jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(1000) - .taskProcessingTimeMs(500) - .build()) + this.tenantId = this.differentTenantId; + submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(1000) + .taskProcessingTimeMs(500) .build()); Thread.sleep(2000); @@ -261,25 +220,18 @@ public class JobManagerTest extends AbstractControllerTest { } @Test - public void testSubmitMultipleJobs() { + public void testSubmitMultipleJobs() throws Exception { int tasksCount = 3; int jobsCount = 3; for (int i = 1; i <= jobsCount; i++) { - Job job = Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job-" + i) - .description("test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(tasksCount) - .taskProcessingTimeMs(1000) - .build()) - .build(); - jobManager.submitJob(job); + submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(1000) + .build(), "test-job-" + i); } await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { - List jobs = findJobs(JobType.DUMMY); + List jobs = findJobs(List.of(JobType.DUMMY), List.of(jobEntity.getUuidId())); assertThat(jobs).hasSize(jobsCount); Job firstJob = jobs.get(2); // ordered by createdTime descending assertThat(firstJob.getStatus()).isEqualTo(JobStatus.RUNNING); @@ -297,6 +249,11 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(job.getResult().getTotalCount()).isEqualTo(tasksCount); } }); + + doDelete("/api/device/" + jobEntity.getId()).andExpect(status().isOk()); + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(findJobs(List.of(JobType.DUMMY), List.of(jobEntity.getUuidId()))).isEmpty(); + }); } @Test @@ -305,17 +262,11 @@ public class JobManagerTest extends AbstractControllerTest { int jobsCount = 3; List jobIds = new ArrayList<>(); for (int i = 1; i <= jobsCount; i++) { - Job job = Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job-" + i) - .description("test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(tasksCount) - .taskProcessingTimeMs(1000) - .build()) - .build(); - jobIds.add(jobManager.submitJob(job).getId()); + Job job = submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(tasksCount) + .taskProcessingTimeMs(1000) + .build(), "test-job-" + i); + jobIds.add(job.getId()); } for (int i = 1; i < jobIds.size(); i++) { @@ -343,16 +294,10 @@ public class JobManagerTest extends AbstractControllerTest { @Test public void testSubmitJob_generalError() { int submittedTasks = 100; - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("Test job") - .configuration(DummyJobConfiguration.builder() - .generalError("Some error while submitting tasks") - .submittedTasksBeforeGeneralError(submittedTasks) - .taskProcessingTimeMs(10) - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .generalError("Some error while submitting tasks") + .submittedTasksBeforeGeneralError(submittedTasks) + .taskProcessingTimeMs(10) .build()).getId(); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -362,24 +307,13 @@ public class JobManagerTest extends AbstractControllerTest { assertThat(job.getResult().getDiscardedCount()).isZero(); assertThat(job.getResult().getTotalCount()).isNull(); }); - - checkJobNotification(notification -> { - assertThat(notification.getSubject()).isEqualTo("Dummy job task failed"); - assertThat(notification.getText()).isEqualTo("Test job failed: Some error while submitting tasks"); - }); } @Test public void testSubmitJob_immediateGeneralError() { - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("Test job") - .configuration(DummyJobConfiguration.builder() - .generalError("Some error while submitting tasks") - .submittedTasksBeforeGeneralError(0) - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .generalError("Some error while submitting tasks") + .submittedTasksBeforeGeneralError(0) .build()).getId(); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -395,16 +329,10 @@ public class JobManagerTest extends AbstractControllerTest { @Test public void testReprocessJob_generalError() throws Exception { int submittedTasks = 100; - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("Test job") - .configuration(DummyJobConfiguration.builder() - .generalError("Some error while submitting tasks") - .submittedTasksBeforeGeneralError(submittedTasks) - .taskProcessingTimeMs(10) - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .generalError("Some error while submitting tasks") + .submittedTasksBeforeGeneralError(submittedTasks) + .taskProcessingTimeMs(10) .build()).getId(); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -437,17 +365,11 @@ public class JobManagerTest extends AbstractControllerTest { int successfulTasks = 3; int failedTasks = 2; int totalTasksCount = successfulTasks + failedTasks; - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(successfulTasks) - .failedTasksCount(failedTasks) - .errors(List.of("error")) - .taskProcessingTimeMs(100) - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(successfulTasks) + .failedTasksCount(failedTasks) + .errors(List.of("error")) + .taskProcessingTimeMs(100) .build()).getId(); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -484,18 +406,12 @@ public class JobManagerTest extends AbstractControllerTest { int failedTasks = 2; int permanentlyFailedTasks = 1; int totalTasksCount = successfulTasks + failedTasks + permanentlyFailedTasks; - JobId jobId = jobManager.submitJob(Job.builder() - .tenantId(tenantId) - .type(JobType.DUMMY) - .key("test-job") - .description("test job") - .configuration(DummyJobConfiguration.builder() - .successfulTasksCount(successfulTasks) - .failedTasksCount(failedTasks) - .permanentlyFailedTasksCount(permanentlyFailedTasks) - .errors(List.of("error")) - .taskProcessingTimeMs(100) - .build()) + JobId jobId = submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(successfulTasks) + .failedTasksCount(failedTasks) + .permanentlyFailedTasksCount(permanentlyFailedTasks) + .errors(List.of("error")) + .taskProcessingTimeMs(100) .build()).getId(); await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { @@ -534,14 +450,18 @@ public class JobManagerTest extends AbstractControllerTest { }); } - private void checkJobNotification(ThrowingConsumer assertFunction) { - await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { - Notification notification = getMyNotifications(true, 100).stream() - .findFirst().orElse(null); - assertThat(notification).isNotNull(); + private Job submitJob(DummyJobConfiguration configuration) { + return submitJob(configuration, "test-job"); + } - assertFunction.accept(notification); - }); + private Job submitJob(DummyJobConfiguration configuration, String key) { + return jobManager.submitJob(Job.builder() + .tenantId(tenantId) + .type(JobType.DUMMY) + .key(key) + .entityId(jobEntity.getId()) + .configuration(configuration) + .build()); } private List getFailures(JobResult jobResult) { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java index 7d4c68f6a4..4f3f9548d8 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/job/JobService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.job; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; @@ -42,4 +43,6 @@ public interface JobService extends EntityDaoService { void deleteJob(TenantId tenantId, JobId jobId); + int deleteJobsByEntityId(TenantId tenantId, EntityId entityId); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java index ed02d22a74..2d88cef037 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java @@ -85,6 +85,10 @@ public class HousekeeperTask implements Serializable { return new HousekeeperTask(tenantId, entityId, HousekeeperTaskType.DELETE_CALCULATED_FIELDS); } + public static HousekeeperTask deleteJobs(TenantId tenantId, EntityId entityId) { + return new HousekeeperTask(tenantId, entityId, HousekeeperTaskType.DELETE_JOBS); + } + @JsonIgnore public String getDescription() { return taskType.getDescription() + " for " + entityId.getEntityType().getNormalName().toLowerCase() + " " + entityId.getId(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java index ef217debc3..e84642b599 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java @@ -31,7 +31,8 @@ public enum HousekeeperTaskType { UNASSIGN_ALARMS("alarms unassigning"), DELETE_TENANT_ENTITIES("tenant entities deletion"), DELETE_ENTITIES("entities deletion"), - DELETE_CALCULATED_FIELDS("calculated fields deletion"); + DELETE_CALCULATED_FIELDS("calculated fields deletion"), + DELETE_JOBS("jobs deletion"); private final String description; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java index 3a9aabae76..031a733d51 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobResult.java @@ -17,14 +17,6 @@ package org.thingsboard.server.common.data.job; public class DummyJobResult extends JobResult { - @Override - public String getDescription() { - if (getGeneralError() != null) { - return getGeneralError(); - } - return getSuccessfulCount() + "/" + getTotalCount() + " successful, " + getFailedCount() + " failed"; - } - @Override public JobType getJobType() { return JobType.DUMMY; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java index f914067be3..3cfb55b388 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/Job.java @@ -24,10 +24,13 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; +import java.util.Set; import java.util.UUID; @Data @@ -42,8 +45,8 @@ public class Job extends BaseData implements HasTenantId { private JobType type; @NotBlank private String key; - @NotBlank - private String description; + @NotNull + private EntityId entityId; @NotNull private JobStatus status; @NotNull @@ -52,12 +55,16 @@ public class Job extends BaseData implements HasTenantId { @NotNull private JobResult result; + public static final Set SUPPORTED_ENTITY_TYPES = Set.of( + EntityType.DEVICE, EntityType.ASSET, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE + ); + @Builder(toBuilder = true) - public Job(TenantId tenantId, JobType type, String key, String description, JobConfiguration configuration) { + public Job(TenantId tenantId, JobType type, String key, EntityId entityId, JobConfiguration configuration) { this.tenantId = tenantId; this.type = type; this.key = key; - this.description = description; + this.entityId = entityId; this.configuration = configuration; this.configuration.setTasksKey(UUID.randomUUID().toString()); presetResult(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobFilter.java index 6cc9a636e8..1a678ba34b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobFilter.java @@ -19,6 +19,7 @@ import lombok.Builder; import lombok.Data; import java.util.List; +import java.util.UUID; @Data @Builder @@ -26,5 +27,8 @@ public class JobFilter { private final List types; private final List statuses; + private final List entities; + private final Long startTime; + private final Long endTime; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java index 285143dfa4..bd2c73d0d8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/JobResult.java @@ -66,9 +66,6 @@ public abstract class JobResult implements Serializable { } } - @JsonIgnore - public abstract String getDescription(); - @JsonIgnore public abstract JobType getJobType(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java b/dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java index 1ca2973936..0340cd0fde 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.housekeeper.HousekeeperTask; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.msg.housekeeper.HousekeeperClient; import org.thingsboard.server.dao.eventsourcing.ActionCause; import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; @@ -76,6 +77,9 @@ public class CleanUpService { submitTask(HousekeeperTask.deleteEvents(tenantId, entityId)); submitTask(HousekeeperTask.deleteAlarms(tenantId, entityId)); submitTask(HousekeeperTask.deleteCalculatedFields(tenantId, entityId)); + if (Job.SUPPORTED_ENTITY_TYPES.contains(entityId.getEntityType())) { + submitTask(HousekeeperTask.deleteJobs(tenantId, entityId)); + } } public void removeTenantEntities(TenantId tenantId, EntityType... entityTypes) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index cd001b61b4..5afac21e5a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -157,6 +157,10 @@ public class DefaultJobService extends AbstractEntityService implements JobServi private Job saveJob(TenantId tenantId, Job job, boolean publishEvent, JobStatus prevStatus) { ConstraintValidator.validateFields(job); + if (!Job.SUPPORTED_ENTITY_TYPES.contains(job.getEntityId().getEntityType())) { + throw new IllegalArgumentException("Unsupported entity type " + job.getEntityId().getEntityType()); + } + job = jobDao.save(tenantId, job); if (publishEvent) { eventPublisher.publishEvent(SaveEntityEvent.builder() @@ -203,6 +207,11 @@ public class DefaultJobService extends AbstractEntityService implements JobServi jobDao.removeById(tenantId, jobId.getId()); } + @Override + public int deleteJobsByEntityId(TenantId tenantId, EntityId entityId) { // TODO: cancel all jobs for this entity + return jobDao.removeByEntityId(tenantId, entityId); + } + private Job findForUpdate(TenantId tenantId, JobId jobId) { return jobDao.findByIdForUpdate(tenantId, jobId); } @@ -219,7 +228,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi @Override public void deleteByTenantId(TenantId tenantId) { - jobDao.deleteByTenantId(tenantId); + jobDao.removeByTenantId(tenantId); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java index 46cc70aea8..498fc9f2de 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/JobDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.job; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; @@ -37,8 +38,12 @@ public interface JobDao extends Dao { boolean existsByTenantIdAndTypeAndStatusOneOf(TenantId tenantId, JobType type, JobStatus... statuses); + boolean existsByTenantIdAndEntityIdAndStatusOneOf(TenantId tenantId, EntityId entityId, JobStatus... statuses); + Job findOldestByTenantIdAndTypeAndStatusForUpdate(TenantId tenantId, JobType type, JobStatus status); - void deleteByTenantId(TenantId tenantId); + void removeByTenantId(TenantId tenantId); + + int removeByEntityId(TenantId tenantId, EntityId entityId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 707dcdc3a5..23324eccb5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -745,7 +745,8 @@ public class ModelConstants { public static final String JOB_TABLE_NAME = "job"; public static final String JOB_TYPE_PROPERTY = "type"; public static final String JOB_KEY_PROPERTY = "key"; - public static final String JOB_DESCRIPTION_PROPERTY = "description"; + public static final String JOB_ENTITY_ID_PROPERTY = "entity_id"; + public static final String JOB_ENTITY_TYPE_PROPERTY = "entity_type"; public static final String JOB_STATUS_PROPERTY = "status"; public static final String JOB_CONFIGURATION_PROPERTY = "configuration"; public static final String JOB_RESULT_PROPERTY = "result"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java index 498541962b..16d7d33edc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/JobEntity.java @@ -25,6 +25,8 @@ import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.job.JobConfiguration; @@ -54,8 +56,12 @@ public class JobEntity extends BaseSqlEntity { @Column(name = ModelConstants.JOB_KEY_PROPERTY, nullable = false) private String key; - @Column(name = ModelConstants.JOB_DESCRIPTION_PROPERTY, nullable = false) - private String description; + @Column(name = ModelConstants.JOB_ENTITY_ID_PROPERTY, nullable = false) + private UUID entityId; + + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.JOB_ENTITY_TYPE_PROPERTY, nullable = false) + private EntityType entityType; @Enumerated(EnumType.STRING) @Column(name = ModelConstants.JOB_STATUS_PROPERTY, nullable = false) @@ -74,7 +80,8 @@ public class JobEntity extends BaseSqlEntity { this.tenantId = getTenantUuid(job.getTenantId()); this.type = job.getType(); this.key = job.getKey(); - this.description = job.getDescription(); + this.entityId = job.getEntityId().getId(); + this.entityType = job.getEntityId().getEntityType(); this.status = job.getStatus(); this.configuration = toJson(job.getConfiguration()); this.result = toJson(job.getResult()); @@ -88,7 +95,7 @@ public class JobEntity extends BaseSqlEntity { job.setTenantId(getTenantId(tenantId)); job.setType(type); job.setKey(key); - job.setDescription(description); + job.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId)); job.setStatus(status); job.setConfiguration(fromJson(configuration, JobConfiguration.class)); job.setResult(fromJson(result, JobResult.class)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java index 72d569c94b..df391be1b1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JobRepository.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.sql.job; +import org.springframework.data.domain.Limit; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -34,26 +35,34 @@ import java.util.UUID; public interface JobRepository extends JpaRepository { @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId " + - "AND (:types IS NULL OR j.type IN (:types)) AND (:statuses IS NULL OR j.status IN (:statuses)) " + - "AND (:searchText IS NULL OR ilike(j.key, concat('%', :searchText, '%')) = true " + - "OR ilike(j.description, concat('%', :searchText, '%')) = true)") - Page findByTenantIdAndTypesAndStatusesAndSearchText(@Param("tenantId") UUID tenantId, - @Param("types") List types, - @Param("statuses") List statuses, - @Param("searchText") String searchText, - Pageable pageable); + "AND (:types IS NULL OR j.type IN (:types)) " + + "AND (:statuses IS NULL OR j.status IN (:statuses)) " + + "AND (:entities IS NULL OR j.entityId IN :entities) " + + "AND (:startTime <= 0 OR j.createdTime >= :startTime) " + + "AND (:endTime <= 0 OR j.createdTime <= :endTime) " + + "AND (:searchText IS NULL OR ilike(j.key, concat('%', :searchText, '%')) = true)") + Page findByTenantIdAndTypesAndStatusesAndEntitiesAndTimeAndSearchText(@Param("tenantId") UUID tenantId, + @Param("types") List types, + @Param("statuses") List statuses, + @Param("entities") List entities, + @Param("startTime") long startTime, + @Param("endTime") long endTime, + @Param("searchText") String searchText, + Pageable pageable); @Query(value = "SELECT * FROM job j WHERE j.id = :id FOR UPDATE", nativeQuery = true) JobEntity findByIdForUpdate(UUID id); @Query("SELECT j FROM JobEntity j WHERE j.tenantId = :tenantId AND j.key = :key " + "ORDER BY j.createdTime DESC") - JobEntity findLatestByTenantIdAndKey(@Param("tenantId") UUID tenantId, @Param("key") String key); + JobEntity findLatestByTenantIdAndKey(@Param("tenantId") UUID tenantId, @Param("key") String key, Limit limit); boolean existsByTenantIdAndKeyAndStatusIn(UUID tenantId, String key, List statuses); boolean existsByTenantIdAndTypeAndStatusIn(UUID tenantId, JobType type, List statuses); + boolean existsByTenantIdAndEntityIdAndStatusIn(UUID tenantId, UUID entityId, List statuses); + @Query(value = "SELECT * FROM job j WHERE j.tenant_id = :tenantId AND j.type = :type " + "AND j.status = :status ORDER BY j.created_time ASC, j.id ASC LIMIT 1 FOR UPDATE", nativeQuery = true) JobEntity findOldestByTenantIdAndTypeAndStatusForUpdate(UUID tenantId, String type, String status); @@ -63,4 +72,9 @@ public interface JobRepository extends JpaRepository { @Query("DELETE FROM JobEntity j WHERE j.tenantId = :tenantId") void deleteByTenantId(UUID tenantId); + @Transactional + @Modifying + @Query("DELETE FROM JobEntity j WHERE j.entityId = :entityId") + int deleteByEntityId(UUID entityId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java index 5c9fe230e8..40d5177ad6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java @@ -17,9 +17,11 @@ package org.thingsboard.server.dao.sql.job; import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Limit; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; @@ -47,9 +49,12 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao @Override public PageData findByTenantIdAndFilter(TenantId tenantId, JobFilter filter, PageLink pageLink) { - return DaoUtil.toPageData(jobRepository.findByTenantIdAndTypesAndStatusesAndSearchText(tenantId.getId(), + return DaoUtil.toPageData(jobRepository.findByTenantIdAndTypesAndStatusesAndEntitiesAndTimeAndSearchText(tenantId.getId(), CollectionsUtil.isEmpty(filter.getTypes()) ? null : filter.getTypes(), CollectionsUtil.isEmpty(filter.getStatuses()) ? null : filter.getStatuses(), + CollectionsUtil.isEmpty(filter.getEntities()) ? null : filter.getEntities(), + filter.getStartTime() != null ? filter.getStartTime() : 0, + filter.getEndTime() != null ? filter.getEndTime() : 0, Strings.emptyToNull(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); } @@ -60,7 +65,7 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao @Override public Job findLatestByTenantIdAndKey(TenantId tenantId, String key) { - return DaoUtil.getData(jobRepository.findLatestByTenantIdAndKey(tenantId.getId(), key)); + return DaoUtil.getData(jobRepository.findLatestByTenantIdAndKey(tenantId.getId(), key, Limit.of(1))); } @Override @@ -73,16 +78,26 @@ public class JpaJobDao extends JpaAbstractDao implements JobDao return jobRepository.existsByTenantIdAndTypeAndStatusIn(tenantId.getId(), type, Arrays.stream(statuses).toList()); } + @Override + public boolean existsByTenantIdAndEntityIdAndStatusOneOf(TenantId tenantId, EntityId entityId, JobStatus... statuses) { + return jobRepository.existsByTenantIdAndEntityIdAndStatusIn(tenantId.getId(), entityId.getId(), Arrays.stream(statuses).toList()); + } + @Override public Job findOldestByTenantIdAndTypeAndStatusForUpdate(TenantId tenantId, JobType type, JobStatus status) { return DaoUtil.getData(jobRepository.findOldestByTenantIdAndTypeAndStatusForUpdate(tenantId.getId(), type.name(), status.name())); } @Override - public void deleteByTenantId(TenantId tenantId) { + public void removeByTenantId(TenantId tenantId) { jobRepository.deleteByTenantId(tenantId.getId()); } + @Override + public int removeByEntityId(TenantId tenantId, EntityId entityId) { + return jobRepository.deleteByEntityId(entityId.getId()); + } + @Override public EntityType getEntityType() { return EntityType.JOB; diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 62ce86a76c..f2a0bc26c1 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -955,7 +955,8 @@ CREATE TABLE IF NOT EXISTS job ( tenant_id uuid NOT NULL, type varchar NOT NULL, key varchar NOT NULL, - description varchar NOT NULL, + entity_id uuid NOT NULL, + entity_type varchar NOT NULL, status varchar NOT NULL, configuration varchar NOT NULL, result varchar diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java index 3ee29dd7c0..89434b20cf 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.job.Job; public interface JobManager { - Job submitJob(Job job); + Job submitJob(Job job); // TODO: rate limits void cancelJob(TenantId tenantId, JobId jobId); From 2bb6eab0edbfa0c116a86f327f7297f05cbb4f14 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 20 May 2025 14:30:04 +0300 Subject: [PATCH 195/335] Make submitJob async --- .../thingsboard/server/service/job/DefaultJobManager.java | 5 +++-- .../org/thingsboard/server/service/job/JobManagerTest.java | 4 +++- .../java/org/thingsboard/rule/engine/api/JobManager.java | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java index 379e72d28b..2ed8ed8a42 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DefaultJobManager.java @@ -49,6 +49,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import java.util.function.Function; import java.util.stream.Collectors; @@ -77,9 +78,9 @@ public class DefaultJobManager implements JobManager { } @Override - public Job submitJob(Job job) { + public Future submitJob(Job job) { log.debug("Submitting job: {}", job); - return jobService.saveJob(job.getTenantId(), job); + return executor.submit(() -> jobService.saveJob(job.getTenantId(), job)); } @Override diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 14c0e4e847..0243d119b1 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.job; +import lombok.SneakyThrows; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -454,6 +455,7 @@ public class JobManagerTest extends AbstractControllerTest { return submitJob(configuration, "test-job"); } + @SneakyThrows private Job submitJob(DummyJobConfiguration configuration, String key) { return jobManager.submitJob(Job.builder() .tenantId(tenantId) @@ -461,7 +463,7 @@ public class JobManagerTest extends AbstractControllerTest { .key(key) .entityId(jobEntity.getId()) .configuration(configuration) - .build()); + .build()).get(); } private List getFailures(JobResult jobResult) { diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java index 89434b20cf..aa48f9a9fb 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/JobManager.java @@ -19,9 +19,11 @@ import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.Job; +import java.util.concurrent.Future; + public interface JobManager { - Job submitJob(Job job); // TODO: rate limits + Future submitJob(Job job); // TODO: rate limits void cancelJob(TenantId tenantId, JobId jobId); From 8af37beb4a7df2d51cbca5a8654329939f2db34f Mon Sep 17 00:00:00 2001 From: dshvaika Date: Tue, 20 May 2025 16:19:44 +0300 Subject: [PATCH 196/335] New Cassandra rate limits: separated for Read and Write + Core and Rule Engine --- .../service/limits/RateLimitServiceTest.java | 12 +- .../server/common/data/limit/LimitedApi.java | 13 +- .../common/data/limit/LimitedApiEntry.java | 34 ++++++ .../common/data/limit/LimitedApiUtil.java | 67 +++++++++++ .../DefaultTenantProfileConfiguration.java | 6 +- .../common/data/limit/LimitedApiUtilTest.java | 113 ++++++++++++++++++ .../server/common/msg/tools/TbRateLimits.java | 27 +++-- .../common/msg/tools/TbRateLimitsTest.java | 63 ++++++++++ .../DefaultTbServiceInfoProvider.java | 13 +- .../discovery/TbServiceInfoProvider.java | 2 + dao/pom.xml | 4 + .../CassandraBufferedRateReadExecutor.java | 13 +- .../CassandraBufferedRateWriteExecutor.java | 13 +- .../util/AbstractBufferedRateExecutor.java | 24 +++- .../dao/util/BufferedRateExecutorType.java | 40 +++++++ 15 files changed, 407 insertions(+), 37 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiUtil.java create mode 100644 common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiUtilTest.java create mode 100644 common/message/src/test/java/org/thingsboard/server/common/msg/tools/TbRateLimitsTest.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/util/BufferedRateExecutorType.java diff --git a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java index 3eb5793c74..f85ad848ab 100644 --- a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java @@ -52,7 +52,7 @@ public class RateLimitServiceTest { public void beforeEach() { tenantProfileCache = Mockito.mock(DefaultTbTenantProfileCache.class); rateLimitService = new DefaultRateLimitService(tenantProfileCache, mock(NotificationRuleProcessor.class), 60, 100); - tenantId = new TenantId(UUID.randomUUID()); + tenantId = TenantId.fromUUID(UUID.randomUUID()); } @Test @@ -67,7 +67,10 @@ public class RateLimitServiceTest { profileConfiguration.setTenantServerRestLimitsConfiguration(rateLimit); profileConfiguration.setCustomerServerRestLimitsConfiguration(rateLimit); profileConfiguration.setWsUpdatesPerSessionRateLimit(rateLimit); - profileConfiguration.setCassandraQueryTenantRateLimitsConfiguration(rateLimit); + profileConfiguration.setCassandraReadQueryTenantCoreRateLimits(rateLimit); + profileConfiguration.setCassandraWriteQueryTenantCoreRateLimits(rateLimit); + profileConfiguration.setCassandraReadQueryTenantRuleEngineRateLimits(rateLimit); + profileConfiguration.setCassandraWriteQueryTenantRuleEngineRateLimits(rateLimit); profileConfiguration.setEdgeEventRateLimits(rateLimit); profileConfiguration.setEdgeEventRateLimitsPerEdge(rateLimit); profileConfiguration.setEdgeUplinkMessagesRateLimits(rateLimit); @@ -79,7 +82,10 @@ public class RateLimitServiceTest { LimitedApi.ENTITY_IMPORT, LimitedApi.NOTIFICATION_REQUESTS, LimitedApi.REST_REQUESTS_PER_CUSTOMER, - LimitedApi.CASSANDRA_QUERIES, + LimitedApi.CASSANDRA_READ_QUERIES_CORE, + LimitedApi.CASSANDRA_WRITE_QUERIES_CORE, + LimitedApi.CASSANDRA_READ_QUERIES_RULE_ENGINE, + LimitedApi.CASSANDRA_WRITE_QUERIES_RULE_ENGINE, LimitedApi.EDGE_EVENTS, LimitedApi.EDGE_EVENTS_PER_EDGE, LimitedApi.EDGE_UPLINK_MESSAGES, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java index db7f14171b..766a1f4eb3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java @@ -30,7 +30,18 @@ public enum LimitedApi { REST_REQUESTS_PER_TENANT(DefaultTenantProfileConfiguration::getTenantServerRestLimitsConfiguration, "REST API requests", true), REST_REQUESTS_PER_CUSTOMER(DefaultTenantProfileConfiguration::getCustomerServerRestLimitsConfiguration, "REST API requests per customer", false), WS_UPDATES_PER_SESSION(DefaultTenantProfileConfiguration::getWsUpdatesPerSessionRateLimit, "WS updates per session", true), - CASSANDRA_QUERIES(DefaultTenantProfileConfiguration::getCassandraQueryTenantRateLimitsConfiguration, "Cassandra queries", true), + CASSANDRA_WRITE_QUERIES_CORE(DefaultTenantProfileConfiguration::getCassandraReadQueryTenantCoreRateLimits, "Rest API and WS telemetry read queries", true), + CASSANDRA_READ_QUERIES_CORE(DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantCoreRateLimits, "Rest API and WS telemetry write queries", true), + CASSANDRA_WRITE_QUERIES_RULE_ENGINE(DefaultTenantProfileConfiguration::getCassandraReadQueryTenantRuleEngineRateLimits, "Rule Engine telemetry read queries", true), + CASSANDRA_READ_QUERIES_RULE_ENGINE(DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantRuleEngineRateLimits, "Rule Engine telemetry write queries", true), + CASSANDRA_READ_QUERIES_MONOLITH( + LimitedApiUtil.merge( + DefaultTenantProfileConfiguration::getCassandraReadQueryTenantCoreRateLimits, + DefaultTenantProfileConfiguration::getCassandraReadQueryTenantRuleEngineRateLimits), "Telemetry read queries", true), + CASSANDRA_WRITE_QUERIES_MONOLITH( + LimitedApiUtil.merge( + DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantCoreRateLimits, + DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantRuleEngineRateLimits), "Telemetry write queries", true), EDGE_EVENTS(DefaultTenantProfileConfiguration::getEdgeEventRateLimits, "Edge events", true), EDGE_EVENTS_PER_EDGE(DefaultTenantProfileConfiguration::getEdgeEventRateLimitsPerEdge, "Edge events per edge", false), EDGE_UPLINK_MESSAGES(DefaultTenantProfileConfiguration::getEdgeUplinkMessagesRateLimits, "Edge uplink messages", true), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java new file mode 100644 index 0000000000..3082b521b6 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.limit; + +public record LimitedApiEntry(long capacity, long durationSeconds) { + + public static LimitedApiEntry parse(String s) { + String[] parts = s.split(":"); + return new LimitedApiEntry(Long.parseLong(parts[0]), Long.parseLong(parts[1])); + } + + public double rps() { + return (double) capacity / durationSeconds; + } + + @Override + public String toString() { + return capacity + ":" + durationSeconds; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiUtil.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiUtil.java new file mode 100644 index 0000000000..a90c7eb825 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiUtil.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.limit; + +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class LimitedApiUtil { + + public static List parseConfig(String config) { + if (config == null || config.isEmpty()) { + return Collections.emptyList(); + } + return Arrays.stream(config.split(",")) + .map(LimitedApiEntry::parse) + .toList(); + } + + public static Function merge( + Function configExtractor1, + Function configExtractor2) { + return config -> { + String config1 = configExtractor1.apply(config); + String config2 = configExtractor2.apply(config); + return LimitedApiUtil.mergeStrConfigs(config1, config2); // merges the configs + }; + } + + private static String mergeStrConfigs(String firstConfig, String secondConfig) { + List all = new ArrayList<>(); + all.addAll(parseConfig(firstConfig)); + all.addAll(parseConfig(secondConfig)); + + Map merged = new HashMap<>(); + + for (LimitedApiEntry entry : all) { + merged.merge(entry.durationSeconds(), entry.capacity(), Long::sum); + } + + return merged.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) // optional: sort by duration + .map(e -> e.getValue() + ":" + e.getKey()) + .collect(Collectors.joining(",")); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index f256b02d9a..d61f48b148 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -121,7 +121,11 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxWsSubscriptionsPerPublicUser; private String wsUpdatesPerSessionRateLimit; - private String cassandraQueryTenantRateLimitsConfiguration; + private String cassandraReadQueryTenantCoreRateLimits; + private String cassandraWriteQueryTenantCoreRateLimits; + + private String cassandraReadQueryTenantRuleEngineRateLimits; + private String cassandraWriteQueryTenantRuleEngineRateLimits; private String edgeEventRateLimits; private String edgeEventRateLimitsPerEdge; diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiUtilTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiUtilTest.java new file mode 100644 index 0000000000..6486de3221 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiUtilTest.java @@ -0,0 +1,113 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.limit; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; + +import java.util.List; +import java.util.function.Function; + +import static org.assertj.core.api.Assertions.assertThat; + +class LimitedApiUtilTest { + + @Test + @DisplayName("LimitedApiUtil should parse single entry correctly") + void testParseSingleEntry() { + List entries = LimitedApiUtil.parseConfig("100:60"); + + assertThat(entries).hasSize(1); + assertThat(entries.get(0).capacity()).isEqualTo(100); + assertThat(entries.get(0).durationSeconds()).isEqualTo(60); + } + + @Test + @DisplayName("LimitedApiUtil should parse multiple entries correctly") + void testParseMultipleEntries() { + List entries = LimitedApiUtil.parseConfig("100:60,200:30"); + + assertThat(entries).hasSize(2); + assertThat(entries.get(0).capacity()).isEqualTo(100); + assertThat(entries.get(0).durationSeconds()).isEqualTo(60); + assertThat(entries.get(1).capacity()).isEqualTo(200); + assertThat(entries.get(1).durationSeconds()).isEqualTo(30); + } + + @Test + @DisplayName("LimitedApiUtil should return empty list for null or empty config") + void testParseEmptyConfig() { + assertThat(LimitedApiUtil.parseConfig(null)).isEmpty(); + assertThat(LimitedApiUtil.parseConfig("")).isEmpty(); + } + + @Test + @DisplayName("LimitedApiUtil should merge two configs by summing capacities with same durations") + void testMergeStrConfigs() { + Function extractor1 = cfg -> "100:60,50:30"; + Function extractor2 = cfg -> "200:60,25:10"; + + // Fake config instance (not used directly in lambda logic) + DefaultTenantProfileConfiguration config = new DefaultTenantProfileConfiguration(); + + String result = LimitedApiUtil.merge(extractor1, extractor2).apply(config); + + // Should be: 300:60 (100+200), 50:30, 25:10 + assertThat(result).isEqualTo("25:10,50:30,300:60"); + } + + @Test + @DisplayName("LimitedApiUtil should merge configs when one is empty") + void testMergeWithEmptyOne() { + Function extractor1 = cfg -> "100:60"; + Function extractor2 = cfg -> ""; + + // Fake config instance (not used directly in lambda logic) + DefaultTenantProfileConfiguration config = new DefaultTenantProfileConfiguration(); + String result = LimitedApiUtil.merge(extractor1, extractor2).apply(config); + + assertThat(result).isEqualTo("100:60"); + } + + @Test + @DisplayName("LimitedApiUtil should merge configs when both have distinct durations") + void testMergeWithDistinctDurations() { + Function extractor1 = cfg -> "100:60"; + Function extractor2 = cfg -> "200:10"; + + // Fake config instance (not used directly in lambda logic) + DefaultTenantProfileConfiguration config = new DefaultTenantProfileConfiguration(); + String result = LimitedApiUtil.merge(extractor1, extractor2).apply(config); + + assertThat(result).isEqualTo("200:10,100:60"); + } + + @Test + @DisplayName("LimitedApiUtil shouldn't have duplicate durations in the same config!") + void testMergeHandlesDuplicatesInSingleConfig() { + Function extractor1 = cfg -> "100:60,200:60"; + Function extractor2 = cfg -> ""; + + // Fake config instance (not used directly in lambda logic) + DefaultTenantProfileConfiguration config = new DefaultTenantProfileConfiguration(); + String result = LimitedApiUtil.merge(extractor1, extractor2).apply(config); + + // 100+200 = 300 for duration 60. Currently possible to save the same "per seconds" config from the UI. + // This must be fixed, so we will merge only two different rate limits. + assertThat(result).isEqualTo("300:60"); + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java b/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java index 8e8933c324..1e2d8dd4ce 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java @@ -16,13 +16,17 @@ package org.thingsboard.server.common.msg.tools; import io.github.bucket4j.Bandwidth; +import io.github.bucket4j.BandwidthBuilder; import io.github.bucket4j.Bucket; import io.github.bucket4j.Refill; import io.github.bucket4j.local.LocalBucket; import io.github.bucket4j.local.LocalBucketBuilder; import lombok.Getter; +import org.thingsboard.server.common.data.limit.LimitedApiEntry; +import org.thingsboard.server.common.data.limit.LimitedApiUtil; import java.time.Duration; +import java.util.List; /** * Created by ashvayka on 22.10.18. @@ -38,20 +42,19 @@ public class TbRateLimits { } public TbRateLimits(String limitsConfiguration, boolean refillIntervally) { - LocalBucketBuilder builder = Bucket.builder(); - boolean initialized = false; - for (String limitSrc : limitsConfiguration.split(",")) { - long capacity = Long.parseLong(limitSrc.split(":")[0]); - long duration = Long.parseLong(limitSrc.split(":")[1]); - Refill refill = refillIntervally ? Refill.intervally(capacity, Duration.ofSeconds(duration)) : Refill.greedy(capacity, Duration.ofSeconds(duration)); - builder.addLimit(Bandwidth.classic(capacity, refill)); - initialized = true; - } - if (initialized) { - bucket = builder.build(); - } else { + List limitedApiEntries = LimitedApiUtil.parseConfig(limitsConfiguration); + if (limitedApiEntries.isEmpty()) { throw new IllegalArgumentException("Failed to parse rate limits configuration: " + limitsConfiguration); } + LocalBucketBuilder localBucket = Bucket.builder(); + for (LimitedApiEntry entry : limitedApiEntries) { + BandwidthBuilder.BandwidthBuilderRefillStage bandwidthBuilder = Bandwidth.builder().capacity(entry.capacity()); + Bandwidth bandwidth = refillIntervally ? + bandwidthBuilder.refillIntervally(entry.capacity(), Duration.ofSeconds(entry.durationSeconds())).build() : + bandwidthBuilder.refillGreedy(entry.capacity(), Duration.ofSeconds(entry.durationSeconds())).build(); + localBucket.addLimit(bandwidth); + } + this.bucket = localBucket.build(); this.configuration = limitsConfiguration; } diff --git a/common/message/src/test/java/org/thingsboard/server/common/msg/tools/TbRateLimitsTest.java b/common/message/src/test/java/org/thingsboard/server/common/msg/tools/TbRateLimitsTest.java new file mode 100644 index 0000000000..a6a95da9c1 --- /dev/null +++ b/common/message/src/test/java/org/thingsboard/server/common/msg/tools/TbRateLimitsTest.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.msg.tools; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class TbRateLimitsTest { + + @Test + @DisplayName("TbRateLimits should construct with single rate limit") + void testSingleLimitConstructor() { + TbRateLimits limits = new TbRateLimits("10:1", false); + assertThat(limits.getConfiguration()).isEqualTo("10:1"); + } + + @Test + @DisplayName("TbRateLimits should construct with multiple rate limits") + void testMultipleLimitConstructor() { + String config = "10:1,100:10"; + TbRateLimits limits = new TbRateLimits(config, false); + assertThat(limits.getConfiguration()).isEqualTo(config); + } + + @Test + @DisplayName("TbRateLimits should throw IllegalArgumentException on empty string") + void testEmptyConfigThrows() { + assertThatThrownBy(() -> new TbRateLimits("", false)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Failed to parse rate limits configuration: "); + } + + @Test + @DisplayName("TbRateLimits should throw NumberFormatException on malformed value") + void testMalformedConfigThrows() { + assertThatThrownBy(() -> new TbRateLimits("not_a_number:second", false)) + .isInstanceOf(NumberFormatException.class); + } + + @Test + @DisplayName("TbRateLimits should throw ArrayIndexOutOfBoundsException on missing colon") + void testColonMissingThrows() { + assertThatThrownBy(() -> new TbRateLimits("100", false)) + .isInstanceOf(ArrayIndexOutOfBoundsException.class); + } + +} \ No newline at end of file diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java index 609d3f8eee..7ff6fafbca 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java @@ -78,11 +78,9 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { } } log.info("Current Service ID: {}", serviceId); - if (serviceType.equalsIgnoreCase("monolith")) { - serviceTypes = List.of(ServiceType.values()); - } else { - serviceTypes = Collections.singletonList(ServiceType.of(serviceType)); - } + serviceTypes = isMonolith() ? + List.of(ServiceType.values()) : + Collections.singletonList(ServiceType.of(serviceType)); if (!serviceTypes.contains(ServiceType.TB_RULE_ENGINE) || assignedTenantProfiles == null) { assignedTenantProfiles = Collections.emptySet(); } @@ -113,6 +111,11 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { return serviceInfo; } + @Override + public boolean isMonolith() { + return serviceType.equalsIgnoreCase("monolith"); + } + @Override public boolean isService(ServiceType serviceType) { return serviceTypes.contains(serviceType); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java index 51a6d808dd..a844440a21 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java @@ -29,6 +29,8 @@ public interface TbServiceInfoProvider { ServiceInfo getServiceInfo(); + boolean isMonolith(); + boolean isService(ServiceType serviceType); ServiceInfo generateNewServiceInfoWithCurrentSystemInfo(); diff --git a/dao/pom.xml b/dao/pom.xml index 2206d8a7d7..67b5a36c92 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -59,6 +59,10 @@ org.thingsboard.common util + + org.thingsboard.common + queue + com.networknt json-schema-validator diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java index 259cd908fc..a7aa0dc5dc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java @@ -28,7 +28,9 @@ import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor; import org.thingsboard.server.dao.util.AsyncTaskContext; +import org.thingsboard.server.dao.util.BufferedRateExecutorType; import org.thingsboard.server.dao.util.NoSqlAnyDao; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; /** * Created by ashvayka on 24.10.18. @@ -38,8 +40,6 @@ import org.thingsboard.server.dao.util.NoSqlAnyDao; @NoSqlAnyDao public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecutor { - static final String BUFFER_NAME = "Read"; - public CassandraBufferedRateReadExecutor( @Value("${cassandra.query.buffer_size}") int queueLimit, @Value("${cassandra.query.concurrent_limit}") int concurrencyLimit, @@ -51,9 +51,10 @@ public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecu @Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq, @Autowired StatsFactory statsFactory, @Autowired EntityService entityService, - @Autowired RateLimitService rateLimitService) { + @Autowired RateLimitService rateLimitService, + @Autowired TbServiceInfoProvider serviceInfoProvider) { super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, statsFactory, - entityService, rateLimitService, printTenantNames); + entityService, rateLimitService, serviceInfoProvider, printTenantNames); } @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}") @@ -68,8 +69,8 @@ public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecu } @Override - public String getBufferName() { - return BUFFER_NAME; + protected BufferedRateExecutorType getBufferedRateExecutorType() { + return BufferedRateExecutorType.READ; } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java index 9c894a7eac..c61148e51f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java @@ -28,7 +28,9 @@ import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor; import org.thingsboard.server.dao.util.AsyncTaskContext; +import org.thingsboard.server.dao.util.BufferedRateExecutorType; import org.thingsboard.server.dao.util.NoSqlAnyDao; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; /** * Created by ashvayka on 24.10.18. @@ -38,8 +40,6 @@ import org.thingsboard.server.dao.util.NoSqlAnyDao; @NoSqlAnyDao public class CassandraBufferedRateWriteExecutor extends AbstractBufferedRateExecutor { - static final String BUFFER_NAME = "Write"; - public CassandraBufferedRateWriteExecutor( @Value("${cassandra.query.buffer_size}") int queueLimit, @Value("${cassandra.query.concurrent_limit}") int concurrencyLimit, @@ -51,9 +51,10 @@ public class CassandraBufferedRateWriteExecutor extends AbstractBufferedRateExec @Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq, @Autowired StatsFactory statsFactory, @Autowired EntityService entityService, - @Autowired RateLimitService rateLimitService) { + @Autowired RateLimitService rateLimitService, + @Autowired TbServiceInfoProvider serviceInfoProvider) { super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, statsFactory, - entityService, rateLimitService, printTenantNames); + entityService, rateLimitService, serviceInfoProvider, printTenantNames); } @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}") @@ -68,8 +69,8 @@ public class CassandraBufferedRateWriteExecutor extends AbstractBufferedRateExec } @Override - public String getBufferName() { - return BUFFER_NAME; + protected BufferedRateExecutorType getBufferedRateExecutorType() { + return BufferedRateExecutorType.WRITE; } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java index a87e9312e3..0f6170f25d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java @@ -34,12 +34,14 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.limit.LimitedApi; +import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.stats.DefaultCounter; import org.thingsboard.server.common.stats.StatsCounter; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.common.stats.StatsType; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import java.util.HashMap; import java.util.Map; @@ -78,13 +80,14 @@ public abstract class AbstractBufferedRateExecutor tenantNamesCache = new HashMap<>(); public AbstractBufferedRateExecutor(int queueLimit, int concurrencyLimit, long maxWaitTime, int dispatcherThreads, int callbackThreads, long pollMs, int printQueriesFreq, StatsFactory statsFactory, - EntityService entityService, RateLimitService rateLimitService, boolean printTenantNames) { + EntityService entityService, RateLimitService rateLimitService, TbServiceInfoProvider serviceInfoProvider, boolean printTenantNames) { this.maxWaitTime = maxWaitTime; this.pollMs = pollMs; this.concurrencyLimit = concurrencyLimit; @@ -99,6 +102,7 @@ public abstract class AbstractBufferedRateExecutor execute(AsyncTaskContext taskCtx); - public abstract String getBufferName(); + private String getBufferName() { + return getBufferedRateExecutorType().getDisplayName(); + } + + protected abstract BufferedRateExecutorType getBufferedRateExecutorType(); private void dispatch() { log.info("[{}] Buffered rate executor thread started", getBufferName()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/BufferedRateExecutorType.java b/dao/src/main/java/org/thingsboard/server/dao/util/BufferedRateExecutorType.java new file mode 100644 index 0000000000..bf76235e97 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/util/BufferedRateExecutorType.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.dao.util; + +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.thingsboard.server.common.data.limit.LimitedApi; + +@Getter +public enum BufferedRateExecutorType { + + READ(LimitedApi.CASSANDRA_READ_QUERIES_CORE, LimitedApi.CASSANDRA_READ_QUERIES_RULE_ENGINE, LimitedApi.CASSANDRA_READ_QUERIES_MONOLITH), + WRITE(LimitedApi.CASSANDRA_WRITE_QUERIES_CORE, LimitedApi.CASSANDRA_WRITE_QUERIES_RULE_ENGINE, LimitedApi.CASSANDRA_WRITE_QUERIES_MONOLITH); + + private final LimitedApi coreLimitedApi; + private final LimitedApi ruleEngineLimitedApi; + private final LimitedApi monolithLimitedApi; + + private final String displayName = StringUtils.capitalize(name().toLowerCase()); + + BufferedRateExecutorType(LimitedApi coreLimitedApi, LimitedApi ruleEngineLimitedApi, LimitedApi monolithLimitedApi) { + this.coreLimitedApi = coreLimitedApi; + this.ruleEngineLimitedApi = ruleEngineLimitedApi; + this.monolithLimitedApi = monolithLimitedApi; + } + +} From a242d3a43b98ffb4124ff52190d3220501ba66b0 Mon Sep 17 00:00:00 2001 From: dshvaika Date: Tue, 20 May 2025 16:31:01 +0300 Subject: [PATCH 197/335] Added missing test for RateLimitServiceTest && refactoring --- .../service/limits/RateLimitServiceTest.java | 7 ++ .../common/data/limit/LimitedApiEntry.java | 4 - .../common/msg/tools/RateLimitsTest.java | 86 ------------------- .../common/msg/tools/TbRateLimitsTest.java | 66 +++++++++++++- 4 files changed, 71 insertions(+), 92 deletions(-) delete mode 100644 common/message/src/test/java/org/thingsboard/server/common/msg/tools/RateLimitsTest.java diff --git a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java index f85ad848ab..674b7bfdb9 100644 --- a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java @@ -94,6 +94,13 @@ public class RateLimitServiceTest { testRateLimits(limitedApi, max, tenantId); } + for (LimitedApi limitedApi : List.of( + LimitedApi.CASSANDRA_READ_QUERIES_MONOLITH, + LimitedApi.CASSANDRA_WRITE_QUERIES_MONOLITH + )) { + testRateLimits(limitedApi, max * 2, tenantId); + } + CustomerId customerId = new CustomerId(UUID.randomUUID()); testRateLimits(LimitedApi.REST_REQUESTS_PER_CUSTOMER, max, customerId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java index 3082b521b6..16084b7af9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java @@ -22,10 +22,6 @@ public record LimitedApiEntry(long capacity, long durationSeconds) { return new LimitedApiEntry(Long.parseLong(parts[0]), Long.parseLong(parts[1])); } - public double rps() { - return (double) capacity / durationSeconds; - } - @Override public String toString() { return capacity + ":" + durationSeconds; diff --git a/common/message/src/test/java/org/thingsboard/server/common/msg/tools/RateLimitsTest.java b/common/message/src/test/java/org/thingsboard/server/common/msg/tools/RateLimitsTest.java deleted file mode 100644 index 9d27687cd9..0000000000 --- a/common/message/src/test/java/org/thingsboard/server/common/msg/tools/RateLimitsTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright © 2016-2025 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. - */ -package org.thingsboard.server.common.msg.tools; - -import org.awaitility.pollinterval.FixedPollInterval; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -public class RateLimitsTest { - - @Test - public void testRateLimits_greedyRefill() { - testRateLimitWithGreedyRefill(3, 10); - testRateLimitWithGreedyRefill(3, 3); - testRateLimitWithGreedyRefill(4, 2); - } - - private void testRateLimitWithGreedyRefill(int capacity, int period) { - String rateLimitConfig = capacity + ":" + period; - TbRateLimits rateLimits = new TbRateLimits(rateLimitConfig); - - rateLimits.tryConsume(capacity); - assertThat(rateLimits.tryConsume()).as("new token is available").isFalse(); - - int expectedRefillTime = (int) (((double) period / capacity) * 1000); - int gap = 500; - - for (int i = 0; i < capacity; i++) { - await("token refill for rate limit " + rateLimitConfig) - .pollInterval(new FixedPollInterval(10, TimeUnit.MILLISECONDS)) - .atLeast(expectedRefillTime - gap, TimeUnit.MILLISECONDS) - .atMost(expectedRefillTime + gap, TimeUnit.MILLISECONDS) - .untilAsserted(() -> { - assertThat(rateLimits.tryConsume()).as("token is available").isTrue(); - }); - assertThat(rateLimits.tryConsume()).as("new token is available").isFalse(); - } - } - - @Test - public void testRateLimits_intervalRefill() { - testRateLimitWithIntervalRefill(10, 5); - testRateLimitWithIntervalRefill(3, 3); - testRateLimitWithIntervalRefill(4, 2); - } - - private void testRateLimitWithIntervalRefill(int capacity, int period) { - String rateLimitConfig = capacity + ":" + period; - TbRateLimits rateLimits = new TbRateLimits(rateLimitConfig, true); - - rateLimits.tryConsume(capacity); - assertThat(rateLimits.tryConsume()).as("new token is available").isFalse(); - - int expectedRefillTime = period * 1000; - int gap = 500; - - await("tokens refill for rate limit " + rateLimitConfig) - .pollInterval(new FixedPollInterval(10, TimeUnit.MILLISECONDS)) - .atLeast(expectedRefillTime - gap, TimeUnit.MILLISECONDS) - .atMost(expectedRefillTime + gap, TimeUnit.MILLISECONDS) - .untilAsserted(() -> { - for (int i = 0; i < capacity; i++) { - assertThat(rateLimits.tryConsume()).as("token is available").isTrue(); - } - assertThat(rateLimits.tryConsume()).as("new token is available").isFalse(); - }); - } - -} diff --git a/common/message/src/test/java/org/thingsboard/server/common/msg/tools/TbRateLimitsTest.java b/common/message/src/test/java/org/thingsboard/server/common/msg/tools/TbRateLimitsTest.java index a6a95da9c1..27a2fe6286 100644 --- a/common/message/src/test/java/org/thingsboard/server/common/msg/tools/TbRateLimitsTest.java +++ b/common/message/src/test/java/org/thingsboard/server/common/msg/tools/TbRateLimitsTest.java @@ -15,13 +15,75 @@ */ package org.thingsboard.server.common.msg.tools; +import org.awaitility.pollinterval.FixedPollInterval; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.util.concurrent.TimeUnit; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.awaitility.Awaitility.await; + +public class TbRateLimitsTest { + + @Test + public void testRateLimits_greedyRefill() { + testRateLimitWithGreedyRefill(3, 10); + testRateLimitWithGreedyRefill(3, 3); + testRateLimitWithGreedyRefill(4, 2); + } -class TbRateLimitsTest { + private void testRateLimitWithGreedyRefill(int capacity, int period) { + String rateLimitConfig = capacity + ":" + period; + TbRateLimits rateLimits = new TbRateLimits(rateLimitConfig); + + rateLimits.tryConsume(capacity); + assertThat(rateLimits.tryConsume()).as("new token is available").isFalse(); + + int expectedRefillTime = (int) (((double) period / capacity) * 1000); + int gap = 500; + + for (int i = 0; i < capacity; i++) { + await("token refill for rate limit " + rateLimitConfig) + .pollInterval(new FixedPollInterval(10, TimeUnit.MILLISECONDS)) + .atLeast(expectedRefillTime - gap, TimeUnit.MILLISECONDS) + .atMost(expectedRefillTime + gap, TimeUnit.MILLISECONDS) + .untilAsserted(() -> { + assertThat(rateLimits.tryConsume()).as("token is available").isTrue(); + }); + assertThat(rateLimits.tryConsume()).as("new token is available").isFalse(); + } + } + + @Test + public void testRateLimits_intervalRefill() { + testRateLimitWithIntervalRefill(10, 5); + testRateLimitWithIntervalRefill(3, 3); + testRateLimitWithIntervalRefill(4, 2); + } + + private void testRateLimitWithIntervalRefill(int capacity, int period) { + String rateLimitConfig = capacity + ":" + period; + TbRateLimits rateLimits = new TbRateLimits(rateLimitConfig, true); + + rateLimits.tryConsume(capacity); + assertThat(rateLimits.tryConsume()).as("new token is available").isFalse(); + + int expectedRefillTime = period * 1000; + int gap = 500; + + await("tokens refill for rate limit " + rateLimitConfig) + .pollInterval(new FixedPollInterval(10, TimeUnit.MILLISECONDS)) + .atLeast(expectedRefillTime - gap, TimeUnit.MILLISECONDS) + .atMost(expectedRefillTime + gap, TimeUnit.MILLISECONDS) + .untilAsserted(() -> { + for (int i = 0; i < capacity; i++) { + assertThat(rateLimits.tryConsume()).as("token is available").isTrue(); + } + assertThat(rateLimits.tryConsume()).as("new token is available").isFalse(); + }); + } @Test @DisplayName("TbRateLimits should construct with single rate limit") @@ -60,4 +122,4 @@ class TbRateLimitsTest { .isInstanceOf(ArrayIndexOutOfBoundsException.class); } -} \ No newline at end of file +} From f990cad134747a831deed49f139801591133af20 Mon Sep 17 00:00:00 2001 From: Yevhenii Date: Wed, 21 May 2025 12:37:42 +0300 Subject: [PATCH 198/335] PostgreSQL View for active Edges - removed testSearch --- .../server/dao/sql/edge/EdgeRepository.java | 11 +++++------ .../thingsboard/server/dao/sql/edge/JpaEdgeDao.java | 5 +---- .../main/resources/sql/schema-views-and-functions.sql | 3 ++- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java index 66d65cf602..09802e9ca6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java @@ -44,12 +44,10 @@ public interface EdgeRepository extends JpaRepository { "WHERE d.id = :edgeId") EdgeInfoEntity findEdgeInfoById(@Param("edgeId") UUID edgeId); - @Query(value = "SELECT * " + - "FROM edge_active_attribute_view edge_active " + - "WHERE (:textSearch IS NULL OR edge_active.name ILIKE CONCAT('%', :textSearch, '%')) " + - "ORDER BY edge_active.id", nativeQuery = true) - Page findActiveEdges(@Param("textSearch") String textSearch, - Pageable pageable); + @Query(value = "SELECT * FROM edge_active_attribute_view edge_active", + countQuery = "SELECT count(*) FROM edge_active_attribute_view", + nativeQuery = true) + Page findActiveEdges(Pageable pageable); @Query("SELECT d.id FROM EdgeEntity d WHERE d.tenantId = :tenantId " + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true)") @@ -166,4 +164,5 @@ public interface EdgeRepository extends JpaRepository { @Query("SELECT new org.thingsboard.server.common.data.edqs.fields.EdgeFields(e.id, e.createdTime, e.tenantId, e.customerId," + "e.name, e.version, e.type, e.label, e.additionalInfo) FROM EdgeEntity e WHERE e.id > :id ORDER BY e.id") List findNextBatch(@Param("id") UUID id, Limit limit); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index 50b8092731..53ba6cf357 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -68,10 +68,7 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge @Override public PageData findActiveEdges(PageLink pageLink) { - return DaoUtil.toPageData( - edgeRepository.findActiveEdges( - pageLink.getTextSearch(), - DaoUtil.toPageable(pageLink))); + return DaoUtil.toPageData(edgeRepository.findActiveEdges(DaoUtil.toPageable(pageLink))); } @Override diff --git a/dao/src/main/resources/sql/schema-views-and-functions.sql b/dao/src/main/resources/sql/schema-views-and-functions.sql index b65dc41a90..a6a7ae43cb 100644 --- a/dao/src/main/resources/sql/schema-views-and-functions.sql +++ b/dao/src/main/resources/sql/schema-views-and-functions.sql @@ -89,7 +89,8 @@ SELECT ee.id FROM edge ee JOIN attribute_kv ON ee.id = attribute_kv.entity_id JOIN key_dictionary ON attribute_kv.attribute_key = key_dictionary.key_id -WHERE attribute_kv.bool_v = true AND key_dictionary.key = 'active'; +WHERE attribute_kv.bool_v = true AND key_dictionary.key = 'active' +ORDER BY ee.id; CREATE OR REPLACE FUNCTION create_or_update_active_alarm( t_id uuid, c_id uuid, a_id uuid, a_created_ts bigint, From b2452af6ce92e6c10bcbf98f7578c35372a970ba Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 21 May 2025 15:18:22 +0300 Subject: [PATCH 199/335] splitted logic for POST_TELEMETRY_REQUEST and TIMESERIES_UPDATED --- .../server/common/adaptor/JsonConverter.java | 4 +-- .../rule/engine/profile/DeviceState.java | 27 +++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java index f1575f8a1c..a504c287b8 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java @@ -60,6 +60,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.function.Consumer; @@ -590,8 +591,7 @@ public class JsonConverter { public static Map> convertToSortedTelemetry(JsonElement jsonElement, long systemTs) throws JsonSyntaxException { - JsonElement timeseriesElement = jsonElement.isJsonObject() ? jsonElement.getAsJsonObject().get("timeseries") : null; - return convertToTelemetry(timeseriesElement != null ? timeseriesElement : jsonElement, systemTs, true); + return convertToTelemetry(jsonElement, systemTs, true); } public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs, boolean sorted) throws diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java index bbce5e5dfe..193c179df2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java @@ -15,6 +15,7 @@ */ package org.thingsboard.rule.engine.profile; +import com.google.gson.JsonElement; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; @@ -49,6 +50,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -149,8 +151,10 @@ class DeviceState { latestValues = fetchLatestValues(ctx, deviceId); } boolean stateChanged = false; - if (msg.isTypeOf(POST_TELEMETRY_REQUEST) || msg.isTypeOf(TIMESERIES_UPDATED)) { - stateChanged = processTelemetry(ctx, msg); + if (msg.isTypeOf(POST_TELEMETRY_REQUEST)) { + stateChanged = processTelemetryRequest(ctx, msg); + } else if (msg.isTypeOf(TIMESERIES_UPDATED)) { + stateChanged = processTelemetryUpdatedNotification(ctx, msg); } else if (msg.isTypeOf(POST_ATTRIBUTES_REQUEST)) { stateChanged = processAttributesUpdateRequest(ctx, msg); } else if (msg.isTypeOneOf(ACTIVITY_EVENT, INACTIVITY_EVENT)) { @@ -180,7 +184,7 @@ class DeviceState { private boolean processDeviceActivityEvent(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { String scope = msg.getMetaData().getValue(DataConstants.SCOPE); if (StringUtils.isEmpty(scope)) { - return processTelemetry(ctx, msg); + return processTelemetryRequest(ctx, msg); } else { return processAttributes(ctx, msg, scope); } @@ -267,9 +271,22 @@ class DeviceState { return stateChanged; } - protected boolean processTelemetry(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { + protected boolean processTelemetryRequest(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { + return processTelemetryUpdate(ctx, msg, JsonParser.parseString(msg.getData())); + } + + protected boolean processTelemetryUpdatedNotification(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { + JsonElement msgData = JsonParser.parseString(msg.getData()); + JsonElement telemetryData = Optional.ofNullable(JsonParser.parseString(msg.getData())) + .filter(JsonElement::isJsonObject) + .map(e -> e.getAsJsonObject().get("timeseries")) + .orElse(msgData); + return processTelemetryUpdate(ctx, msg, telemetryData); + } + + private boolean processTelemetryUpdate(TbContext ctx, TbMsg msg, JsonElement telemetryData) throws ExecutionException, InterruptedException { boolean stateChanged = false; - Map> tsKvMap = JsonConverter.convertToSortedTelemetry(JsonParser.parseString(msg.getData()), msg.getMetaDataTs()); + Map> tsKvMap = JsonConverter.convertToSortedTelemetry(telemetryData, msg.getMetaDataTs()); // iterate over data by ts (ASC order). for (Map.Entry> entry : tsKvMap.entrySet()) { Long ts = entry.getKey(); From 6e5e6b064743e5c1b94eb4eed921344eba8bfa74 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 21 May 2025 15:19:38 +0300 Subject: [PATCH 200/335] clean imports --- .../org/thingsboard/server/common/adaptor/JsonConverter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java index a504c287b8..2a208923d9 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java @@ -60,7 +60,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.function.Consumer; From 81c1c2f11828ad2c4362f3508a8d6eb274549f86 Mon Sep 17 00:00:00 2001 From: Denys Sumin Date: Wed, 21 May 2025 16:14:29 +0300 Subject: [PATCH 201/335] redis to valkey --- docker/.env | 6 +- docker/.gitignore | 20 +-- docker/cache-redis-cluster.env | 5 - docker/cache-redis.env | 2 - docker/cache-valkey-cluster.env | 5 + ...sentinel.env => cache-valkey-sentinel.env} | 4 +- docker/cache-valkey.env | 2 + docker/compose-utils.sh | 62 +++---- .../docker-compose.redis-cluster.volumes.yml | 58 ------- docker/docker-compose.redis-cluster.yml | 151 ----------------- docker/docker-compose.redis-sentinel.yml | 120 -------------- .../docker-compose.valkey-cluster.volumes.yml | 58 +++++++ docker/docker-compose.valkey-cluster.yml | 152 ++++++++++++++++++ ...ocker-compose.valkey-sentinel.volumes.yml} | 26 +-- docker/docker-compose.valkey-sentinel.yml | 120 ++++++++++++++ ....yml => docker-compose.valkey.volumes.yml} | 8 +- ...se.redis.yml => docker-compose.valkey.yml} | 62 +++---- 17 files changed, 431 insertions(+), 430 deletions(-) delete mode 100644 docker/cache-redis-cluster.env delete mode 100644 docker/cache-redis.env create mode 100644 docker/cache-valkey-cluster.env rename docker/{cache-redis-sentinel.env => cache-valkey-sentinel.env} (70%) create mode 100644 docker/cache-valkey.env delete mode 100644 docker/docker-compose.redis-cluster.volumes.yml delete mode 100644 docker/docker-compose.redis-cluster.yml delete mode 100644 docker/docker-compose.redis-sentinel.yml create mode 100644 docker/docker-compose.valkey-cluster.volumes.yml create mode 100644 docker/docker-compose.valkey-cluster.yml rename docker/{docker-compose.redis-sentinel.volumes.yml => docker-compose.valkey-sentinel.volumes.yml} (59%) create mode 100644 docker/docker-compose.valkey-sentinel.yml rename docker/{docker-compose.redis.volumes.yml => docker-compose.valkey.volumes.yml} (86%) rename docker/{docker-compose.redis.yml => docker-compose.valkey.yml} (67%) diff --git a/docker/.env b/docker/.env index 71722247df..dc387dd517 100644 --- a/docker/.env +++ b/docker/.env @@ -1,7 +1,7 @@ TB_QUEUE_TYPE=kafka -# redis or redis-cluster or redis-sentinel -CACHE=redis +# valkey or valkey-cluster or valkey-sentinel +CACHE=valkey DOCKER_REPO=thingsboard @@ -17,7 +17,7 @@ TB_VC_EXECUTOR_DOCKER_NAME=tb-vc-executor EDQS_DOCKER_NAME=tb-edqs EDQS_ENABLED=false -TB_VERSION=latest +TB_VERSION=4.0.0-RC # Database used by ThingsBoard, can be either postgres (PostgreSQL) or hybrid (PostgreSQL for entities database and Cassandra for timeseries database). # According to the database type corresponding docker service will be deployed (see docker-compose.postgres.yml, docker-compose.hybrid.yml for details). diff --git a/docker/.gitignore b/docker/.gitignore index b6c9fcf18c..527b3c2a3b 100644 --- a/docker/.gitignore +++ b/docker/.gitignore @@ -6,15 +6,15 @@ tb-node/postgres/** tb-node/cassandra/** tb-transports/*/log tb-vc-executor/log/** -tb-node/redis-cluster-data-0/** -tb-node/redis-cluster-data-1/** -tb-node/redis-cluster-data-2/** -tb-node/redis-cluster-data-3/** -tb-node/redis-cluster-data-4/** -tb-node/redis-cluster-data-5/** -tb-node/redis-sentinel-data-master/** -tb-node/redis-sentinel-data-slave/** -tb-node/redis-sentinel-data-sentinel/** -tb-node/redis-data/** +tb-node/valkey-cluster-data-0/** +tb-node/valkey-cluster-data-1/** +tb-node/valkey-cluster-data-2/** +tb-node/valkey-cluster-data-3/** +tb-node/valkey-cluster-data-4/** +tb-node/valkey-cluster-data-5/** +tb-node/valkey-sentinel-data-primary/** +tb-node/valkey-sentinel-data-replica/** +tb-node/valkey-sentinel-data-sentinel/** +tb-node/valkey-data/** !.env diff --git a/docker/cache-redis-cluster.env b/docker/cache-redis-cluster.env deleted file mode 100644 index a3b516063b..0000000000 --- a/docker/cache-redis-cluster.env +++ /dev/null @@ -1,5 +0,0 @@ -CACHE_TYPE=redis -REDIS_CONNECTION_TYPE=cluster -REDIS_NODES=redis-node-0:6379,redis-node-1:6379,redis-node-2:6379,redis-node-3:6379,redis-node-4:6379,redis-node-5:6379 -REDIS_USE_DEFAULT_POOL_CONFIG=false -REDIS_PASSWORD=thingsboard diff --git a/docker/cache-redis.env b/docker/cache-redis.env deleted file mode 100644 index 7b92620666..0000000000 --- a/docker/cache-redis.env +++ /dev/null @@ -1,2 +0,0 @@ -CACHE_TYPE=redis -REDIS_HOST=redis diff --git a/docker/cache-valkey-cluster.env b/docker/cache-valkey-cluster.env new file mode 100644 index 0000000000..86f4682dfd --- /dev/null +++ b/docker/cache-valkey-cluster.env @@ -0,0 +1,5 @@ +CACHE_TYPE=redis +REDIS_CONNECTION_TYPE=cluster +REDIS_NODES=valkey-node-0:6379,valkey-node-1:6379,valkey-node-2:6379,valkey-node-3:6379,valkey-node-4:6379,valkey-node-5:6379 +REDIS_USE_DEFAULT_POOL_CONFIG=false +REDIS_PASSWORD=thingsboard diff --git a/docker/cache-redis-sentinel.env b/docker/cache-valkey-sentinel.env similarity index 70% rename from docker/cache-redis-sentinel.env rename to docker/cache-valkey-sentinel.env index 39a1246d9c..44c867d93d 100644 --- a/docker/cache-redis-sentinel.env +++ b/docker/cache-valkey-sentinel.env @@ -1,7 +1,7 @@ CACHE_TYPE=redis REDIS_CONNECTION_TYPE=sentinel -REDIS_MASTER=mymaster -REDIS_SENTINELS=redis-sentinel:26379 +REDIS_MASTER=myprimary +REDIS_SENTINELS=valkey-sentinel:26379 REDIS_SENTINEL_PASSWORD=sentinel REDIS_USE_DEFAULT_POOL_CONFIG=false REDIS_PASSWORD=thingsboard diff --git a/docker/cache-valkey.env b/docker/cache-valkey.env new file mode 100644 index 0000000000..3971ab4f18 --- /dev/null +++ b/docker/cache-valkey.env @@ -0,0 +1,2 @@ +CACHE_TYPE=redis +REDIS_HOST=valkey diff --git a/docker/compose-utils.sh b/docker/compose-utils.sh index 3862024786..bd2964b72b 100755 --- a/docker/compose-utils.sh +++ b/docker/compose-utils.sh @@ -76,19 +76,19 @@ function additionalComposeMonitoringArgs() { function additionalComposeCacheArgs() { source .env CACHE_COMPOSE_ARGS="" - CACHE="${CACHE:-redis}" + CACHE="${CACHE:-valkey}" case $CACHE in - redis) - CACHE_COMPOSE_ARGS="-f docker-compose.redis.yml" + valkey) + CACHE_COMPOSE_ARGS="-f docker-compose.valkey.yml" ;; - redis-cluster) - CACHE_COMPOSE_ARGS="-f docker-compose.redis-cluster.yml" + valkey-cluster) + CACHE_COMPOSE_ARGS="-f docker-compose.valkey-cluster.yml" ;; - redis-sentinel) - CACHE_COMPOSE_ARGS="-f docker-compose.redis-sentinel.yml" + valkey-sentinel) + CACHE_COMPOSE_ARGS="-f docker-compose.valkey-sentinel.yml" ;; *) - echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'redis' or 'redis-cluster' or 'redis-sentinel'." >&2 + echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'valkey' or 'valkey-cluster' or 'valkey-sentinel'." >&2 exit 1 esac echo $CACHE_COMPOSE_ARGS @@ -109,19 +109,19 @@ function additionalStartupServices() { exit 1 esac - CACHE="${CACHE:-redis}" + CACHE="${CACHE:-valkey}" case $CACHE in - redis) - ADDITIONAL_STARTUP_SERVICES="$ADDITIONAL_STARTUP_SERVICES redis" + valkey) + ADDITIONAL_STARTUP_SERVICES="$ADDITIONAL_STARTUP_SERVICES valkey" ;; - redis-cluster) - ADDITIONAL_STARTUP_SERVICES="$ADDITIONAL_STARTUP_SERVICES redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5" + valkey-cluster) + ADDITIONAL_STARTUP_SERVICES="$ADDITIONAL_STARTUP_SERVICES valkey-node-0 valkey-node-1 valkey-node-2 valkey-node-3 valkey-node-4 valkey-node-5" ;; - redis-sentinel) - ADDITIONAL_STARTUP_SERVICES="$ADDITIONAL_STARTUP_SERVICES redis-master redis-slave redis-sentinel" + valkey-sentinel) + ADDITIONAL_STARTUP_SERVICES="$ADDITIONAL_STARTUP_SERVICES valkey-primary valkey-replica valkey-sentinel" ;; *) - echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'redis' or 'redis-cluster' or 'redis-sentinel'." >&2 + echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'valkey' or 'valkey-cluster' or 'valkey-sentinel'." >&2 exit 1 esac @@ -166,32 +166,32 @@ function permissionList() { " fi - CACHE="${CACHE:-redis}" + CACHE="${CACHE:-valkey}" case $CACHE in - redis) + valkey) PERMISSION_LIST="$PERMISSION_LIST - 1001 1001 tb-node/redis-data + 1001 1001 tb-node/valkey-data " ;; - redis-cluster) + valkey-cluster) PERMISSION_LIST="$PERMISSION_LIST - 1001 1001 tb-node/redis-cluster-data-0 - 1001 1001 tb-node/redis-cluster-data-1 - 1001 1001 tb-node/redis-cluster-data-2 - 1001 1001 tb-node/redis-cluster-data-3 - 1001 1001 tb-node/redis-cluster-data-4 - 1001 1001 tb-node/redis-cluster-data-5 + 1001 1001 tb-node/valkey-cluster-data-0 + 1001 1001 tb-node/valkey-cluster-data-1 + 1001 1001 tb-node/valkey-cluster-data-2 + 1001 1001 tb-node/valkey-cluster-data-3 + 1001 1001 tb-node/valkey-cluster-data-4 + 1001 1001 tb-node/valkey-cluster-data-5 " ;; - redis-sentinel) + valkey-sentinel) PERMISSION_LIST="$PERMISSION_LIST - 1001 1001 tb-node/redis-sentinel-data-master - 1001 1001 tb-node/redis-sentinel-data-slave - 1001 1001 tb-node/redis-sentinel-data-sentinel + 1001 1001 tb-node/valkey-sentinel-data-primary + 1001 1001 tb-node/valkey-sentinel-data-replica + 1001 1001 tb-node/valkey-sentinel-data-sentinel " ;; *) - echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'redis' or 'redis-cluster' or 'redis-sentinel'." >&2 + echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'valkey' or 'valkey-cluster' or 'valkey-sentinel'." >&2 exit 1 esac diff --git a/docker/docker-compose.redis-cluster.volumes.yml b/docker/docker-compose.redis-cluster.volumes.yml deleted file mode 100644 index 5a735f9eeb..0000000000 --- a/docker/docker-compose.redis-cluster.volumes.yml +++ /dev/null @@ -1,58 +0,0 @@ -# -# Copyright © 2016-2025 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. -# - -version: '3.0' - -services: - # Redis cluster - redis-node-0: - volumes: - - redis-cluster-data-0:/bitnami/redis/data - redis-node-1: - volumes: - - redis-cluster-data-1:/bitnami/redis/data - redis-node-2: - volumes: - - redis-cluster-data-2:/bitnami/redis/data - redis-node-3: - volumes: - - redis-cluster-data-3:/bitnami/redis/data - redis-node-4: - volumes: - - redis-cluster-data-4:/bitnami/redis/data - redis-node-5: - volumes: - - redis-cluster-data-5:/bitnami/redis/data - -volumes: - redis-cluster-data-0: - external: - name: ${REDIS_CLUSTER_DATA_VOLUME_0} - redis-cluster-data-1: - external: - name: ${REDIS_CLUSTER_DATA_VOLUME_1} - redis-cluster-data-2: - external: - name: ${REDIS_CLUSTER_DATA_VOLUME_2} - redis-cluster-data-3: - external: - name: ${REDIS_CLUSTER_DATA_VOLUME_3} - redis-cluster-data-4: - external: - name: ${REDIS_CLUSTER_DATA_VOLUME_4} - redis-cluster-data-5: - external: - name: ${REDIS_CLUSTER_DATA_VOLUME_5} diff --git a/docker/docker-compose.redis-cluster.yml b/docker/docker-compose.redis-cluster.yml deleted file mode 100644 index 63051176c9..0000000000 --- a/docker/docker-compose.redis-cluster.yml +++ /dev/null @@ -1,151 +0,0 @@ -# -# Copyright © 2016-2025 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. -# - -version: '3.0' - -services: -# Redis cluster -# The latest version of Redis compatible with ThingsBoard is 7.2 - redis-node-0: - image: bitnami/valkey-cluster:8.0 - volumes: - - ./tb-node/redis-cluster-data-0:/bitnami/redis/data - environment: - - 'VALKEY_PASSWORD=thingsboard' - - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' - - redis-node-1: - image: bitnami/valkey-cluster:8.0 - volumes: - - ./tb-node/redis-cluster-data-1:/bitnami/redis/data - depends_on: - - redis-node-0 - environment: - - 'VALKEY_PASSWORD=thingsboard' - - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' - - redis-node-2: - image: bitnami/valkey-cluster:8.0 - volumes: - - ./tb-node/redis-cluster-data-2:/bitnami/redis/data - depends_on: - - redis-node-1 - environment: - - 'VALKEY_PASSWORD=thingsboard' - - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' - - redis-node-3: - image: bitnami/valkey-cluster:8.0 - volumes: - - ./tb-node/redis-cluster-data-3:/bitnami/redis/data - depends_on: - - redis-node-2 - environment: - - 'VALKEY_PASSWORD=thingsboard' - - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' - - redis-node-4: - image: bitnami/valkey-cluster:8.0 - volumes: - - ./tb-node/redis-cluster-data-4:/bitnami/redis/data - depends_on: - - redis-node-3 - environment: - - 'VALKEY_PASSWORD=thingsboard' - - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' - - redis-node-5: - image: bitnami/valkey-cluster:8.0 - volumes: - - ./tb-node/redis-cluster-data-5:/bitnami/redis/data - depends_on: - - redis-node-0 - - redis-node-1 - - redis-node-2 - - redis-node-3 - - redis-node-4 - environment: - - 'VALKEY_PASSWORD=thingsboard' - - 'VALKEY_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' - - 'VALKEY_CLUSTER_REPLICAS=1' - - 'VALKEY_CLUSTER_CREATOR=yes' - -# ThingsBoard setup to use redis-cluster - tb-core1: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-core2: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-rule-engine1: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-rule-engine2: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-mqtt-transport1: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-mqtt-transport2: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-http-transport1: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-http-transport2: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-coap-transport: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-lwm2m-transport: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-snmp-transport: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-vc-executor1: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 - tb-vc-executor2: - env_file: - - cache-redis-cluster.env - depends_on: - - redis-node-5 diff --git a/docker/docker-compose.redis-sentinel.yml b/docker/docker-compose.redis-sentinel.yml deleted file mode 100644 index ed8f314bcd..0000000000 --- a/docker/docker-compose.redis-sentinel.yml +++ /dev/null @@ -1,120 +0,0 @@ -# -# Copyright © 2016-2025 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. -# - -version: '3.0' - -services: - # Redis sentinel - # The latest version of Redis compatible with ThingsBoard is 7.2 - redis-master: - image: 'bitnami/redis:7.2' - volumes: - - ./tb-node/redis-sentinel-data-master:/bitnami/redis/data - environment: - - 'REDIS_REPLICATION_MODE=master' - - 'REDIS_PASSWORD=thingsboard' - - redis-slave: - image: 'bitnami/redis:7.2' - volumes: - - ./tb-node/redis-sentinel-data-slave:/bitnami/redis/data - environment: - - 'REDIS_REPLICATION_MODE=slave' - - 'REDIS_MASTER_HOST=redis-master' - - 'REDIS_MASTER_PASSWORD=thingsboard' - - 'REDIS_PASSWORD=thingsboard' - depends_on: - - redis-master - - redis-sentinel: - image: 'bitnami/redis-sentinel:7.2' - volumes: - - ./tb-node/redis-sentinel-data-sentinel:/bitnami/redis/data - environment: - - 'REDIS_MASTER_HOST=redis-master' - - 'REDIS_MASTER_SET=mymaster' - - 'REDIS_SENTINEL_PASSWORD=sentinel' - - 'REDIS_MASTER_PASSWORD=thingsboard' - depends_on: - - redis-master - - redis-slave - - # ThingsBoard setup to use redis-sentinel - tb-core1: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-core2: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-rule-engine1: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-rule-engine2: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-mqtt-transport1: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-mqtt-transport2: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-http-transport1: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-http-transport2: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-coap-transport: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-lwm2m-transport: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-snmp-transport: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-vc-executor1: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel - tb-vc-executor2: - env_file: - - cache-redis-sentinel.env - depends_on: - - redis-sentinel diff --git a/docker/docker-compose.valkey-cluster.volumes.yml b/docker/docker-compose.valkey-cluster.volumes.yml new file mode 100644 index 0000000000..7e78d04505 --- /dev/null +++ b/docker/docker-compose.valkey-cluster.volumes.yml @@ -0,0 +1,58 @@ +# +# Copyright © 2016-2025 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. +# + +version: '3.0' + +services: + # Valkey cluster + valkey-node-0: + volumes: + - valkey-cluster-data-0:/bitnami/valkey/data + valkey-node-1: + volumes: + - valkey-cluster-data-1:/bitnami/valkey/data + valkey-node-2: + volumes: + - valkey-cluster-data-2:/bitnami/valkey/data + valkey-node-3: + volumes: + - valkey-cluster-data-3:/bitnami/valkey/data + valkey-node-4: + volumes: + - valkey-cluster-data-4:/bitnami/valkey/data + valkey-node-5: + volumes: + - valkey-cluster-data-5:/bitnami/valkey/data + +volumes: + valkey-cluster-data-0: + external: + name: ${VALKEY_CLUSTER_DATA_VOLUME_0} + valkey-cluster-data-1: + external: + name: ${VALKEY_CLUSTER_DATA_VOLUME_1} + valkey-cluster-data-2: + external: + name: ${VALKEY_CLUSTER_DATA_VOLUME_2} + valkey-cluster-data-3: + external: + name: ${VALKEY_CLUSTER_DATA_VOLUME_3} + valkey-cluster-data-4: + external: + name: ${VALKEY_CLUSTER_DATA_VOLUME_4} + valkey-cluster-data-5: + external: + name: ${VALKEY_CLUSTER_DATA_VOLUME_5} diff --git a/docker/docker-compose.valkey-cluster.yml b/docker/docker-compose.valkey-cluster.yml new file mode 100644 index 0000000000..02757240dc --- /dev/null +++ b/docker/docker-compose.valkey-cluster.yml @@ -0,0 +1,152 @@ +# +# Copyright © 2016-2025 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. +# + +version: '3.0' + +services: +# Valkey cluster +# The latest version of Valkey compatible with ThingsBoard is 8.0 + valkey-node-0: + image: bitnami/valkey-cluster:8.0 + volumes: + - ./tb-node/valkey-cluster-data-0:/bitnami/valkey/data + environment: + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=valkey-node-0 valkey-node-1 valkey-node-2 valkey-node-3 valkey-node-4 valkey-node-5' + + valkey-node-1: + image: bitnami/valkey-cluster:8.0 + volumes: + - ./tb-node/valkey-cluster-data-1:/bitnami/valkey/data + depends_on: + - valkey-node-0 + environment: + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=valkey-node-0 valkey-node-1 valkey-node-2 valkey-node-3 valkey-node-4 valkey-node-5' + + valkey-node-2: + image: bitnami/valkey-cluster:8.0 + volumes: + - ./tb-node/valkey-cluster-data-2:/bitnami/valkey/data + depends_on: + - valkey-node-1 + environment: + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=valkey-node-0 valkey-node-1 valkey-node-2 valkey-node-3 valkey-node-4 valkey-node-5' + + valkey-node-3: + image: bitnami/valkey-cluster:8.0 + volumes: + - ./tb-node/valkey-cluster-data-3:/bitnami/valkey/data + depends_on: + - valkey-node-2 + environment: + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=valkey-node-0 valkey-node-1 valkey-node-2 valkey-node-3 valkey-node-4 valkey-node-5' + + valkey-node-4: + image: bitnami/valkey-cluster:8.0 + volumes: + - ./tb-node/valkey-cluster-data-4:/bitnami/valkey/data + depends_on: + - valkey-node-3 + environment: + - 'VALKEY_PASSWORD=thingsboard' + - 'VALKEY_NODES=valkey-node-0 valkey-node-1 valkey-node-2 valkey-node-3 valkey-node-4 valkey-node-5' + + valkey-node-5: + image: bitnami/valkey-cluster:8.0 + volumes: + - ./tb-node/valkey-cluster-data-5:/bitnami/valkey/data + depends_on: + - valkey-node-0 + - valkey-node-1 + - valkey-node-2 + - valkey-node-3 + - valkey-node-4 + environment: + - 'VALKEY_PASSWORD=thingsboard' + - 'REDISCLI_AUTH=thingsboard' + - 'VALKEY_NODES=valkey-node-0 valkey-node-1 valkey-node-2 valkey-node-3 valkey-node-4 valkey-node-5' + - 'VALKEY_CLUSTER_REPLICAS=1' + - 'VALKEY_CLUSTER_CREATOR=yes' + +# ThingsBoard setup to use valkey-cluster + tb-core1: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-core2: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-rule-engine1: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-rule-engine2: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-mqtt-transport1: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-mqtt-transport2: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-http-transport1: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-http-transport2: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-coap-transport: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-lwm2m-transport: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-snmp-transport: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-vc-executor1: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 + tb-vc-executor2: + env_file: + - cache-valkey-cluster.env + depends_on: + - valkey-node-5 diff --git a/docker/docker-compose.redis-sentinel.volumes.yml b/docker/docker-compose.valkey-sentinel.volumes.yml similarity index 59% rename from docker/docker-compose.redis-sentinel.volumes.yml rename to docker/docker-compose.valkey-sentinel.volumes.yml index 485291d905..1f29d9edaa 100644 --- a/docker/docker-compose.redis-sentinel.volumes.yml +++ b/docker/docker-compose.valkey-sentinel.volumes.yml @@ -17,24 +17,24 @@ version: '3.0' services: - # Redis sentinel - redis-master: + # Valkey sentinel + valkey-primary: volumes: - - redis-sentinel-data-master:/bitnami/redis/data - redis-slave: + - valkey-sentinel-data-primary:/bitnami/valkey/data + valkey-replica: volumes: - - redis-sentinel-data-slave:/bitnami/redis/data - redis-sentinel: + - valkey-sentinel-data-replica:/bitnami/valkey/data + valkey-sentinel: volumes: - - redis-sentinel-data-sentinel:/bitnami/redis/data + - valkey-sentinel-data-sentinel:/bitnami/valkey/data volumes: - redis-sentinel-data-master: + valkey-sentinel-data-primary: external: - name: ${REDIS_SENTINEL_DATA_VOLUME_MASTER} - redis-sentinel-data-slave: + name: ${VALKEY_SENTINEL_DATA_VOLUME_MASTER} + valkey-sentinel-data-replica: external: - name: ${REDIS_SENTINEL_DATA_VOLUME_SLAVE} - redis-sentinel-data-sentinel: + name: ${VALKEY_SENTINEL_DATA_VOLUME_SLAVE} + valkey-sentinel-data-sentinel: external: - name: ${REDIS_SENTINEL_DATA_VOLUME_SENTINEL} + name: ${VALKEY_SENTINEL_DATA_VOLUME_SENTINEL} diff --git a/docker/docker-compose.valkey-sentinel.yml b/docker/docker-compose.valkey-sentinel.yml new file mode 100644 index 0000000000..d4b791643a --- /dev/null +++ b/docker/docker-compose.valkey-sentinel.yml @@ -0,0 +1,120 @@ +# +# Copyright © 2016-2025 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. +# + +version: '3.0' + +services: + # Valkey sentinel + # The latest version of Valkey compatible with ThingsBoard is 8.0 + valkey-primary: + image: 'bitnami/valkey:8.0' + volumes: + - ./tb-node/valkey-sentinel-data-primary:/bitnami/valkey/data + environment: + - 'VALKEY_REPLICATION_MODE=primary' + - 'VALKEY_PASSWORD=thingsboard' + + valkey-replica: + image: 'bitnami/valkey:8.0' + volumes: + - ./tb-node/valkey-sentinel-data-replica:/bitnami/valkey/data + environment: + - 'VALKEY_REPLICATION_MODE=replica' + - 'VALKEY_PRIMARY_HOST=valkey-primary' + - 'VALKEY_PRIMARY_PASSWORD=thingsboard' + - 'VALKEY_PASSWORD=thingsboard' + depends_on: + - valkey-primary + + valkey-sentinel: + image: 'bitnami/valkey-sentinel:8.0' + volumes: + - ./tb-node/valkey-sentinel-data-sentinel:/bitnami/valkey/data + environment: + - 'VALKEY_PRIMARY_HOST=valkey-primary' + - 'VALKEY_MASTER_SET=myprimary' + - 'VALKEY_SENTINEL_PASSWORD=sentinel' + - 'VALKEY_PRIMARY_PASSWORD=thingsboard' + depends_on: + - valkey-primary + - valkey-replica + + # ThingsBoard setup to use valkey-sentinel + tb-core1: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-core2: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-rule-engine1: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-rule-engine2: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-mqtt-transport1: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-mqtt-transport2: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-http-transport1: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-http-transport2: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-coap-transport: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-lwm2m-transport: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-snmp-transport: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-vc-executor1: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel + tb-vc-executor2: + env_file: + - cache-valkey-sentinel.env + depends_on: + - valkey-sentinel diff --git a/docker/docker-compose.redis.volumes.yml b/docker/docker-compose.valkey.volumes.yml similarity index 86% rename from docker/docker-compose.redis.volumes.yml rename to docker/docker-compose.valkey.volumes.yml index 3b23511501..7da5f154a6 100644 --- a/docker/docker-compose.redis.volumes.yml +++ b/docker/docker-compose.valkey.volumes.yml @@ -17,11 +17,11 @@ version: '3.0' services: - redis: + valkey: volumes: - - redis-data:/bitnami/redis/data + - valkey-data:/bitnami/valkey/data volumes: - redis-data: + valkey-data: external: - name: ${REDIS_DATA_VOLUME} + name: ${VALKEY_DATA_VOLUME} diff --git a/docker/docker-compose.redis.yml b/docker/docker-compose.valkey.yml similarity index 67% rename from docker/docker-compose.redis.yml rename to docker/docker-compose.valkey.yml index 3e8934d9a0..6b8cf3feb3 100644 --- a/docker/docker-compose.redis.yml +++ b/docker/docker-compose.valkey.yml @@ -17,9 +17,9 @@ version: '3.0' services: -# Redis standalone -# The latest version of Redis compatible with ThingsBoard is 7.2 - redis: +# Valkey standalone +# The latest version of Valkey compatible with ThingsBoard is 8.0 + valkey: restart: always image: bitnami/valkey:8.0 environment: @@ -28,71 +28,71 @@ services: ports: - '6379:6379' volumes: - - ./tb-node/redis-data:/bitnami/redis/data + - ./tb-node/valkey-data:/bitnami/valkey/data -# ThingsBoard setup to use redis-standalone +# ThingsBoard setup to use valkey-standalone tb-core1: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-core2: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-rule-engine1: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-rule-engine2: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-mqtt-transport1: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-mqtt-transport2: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-http-transport1: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-http-transport2: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-coap-transport: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-lwm2m-transport: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-snmp-transport: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-vc-executor1: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey tb-vc-executor2: env_file: - - cache-redis.env + - cache-valkey.env depends_on: - - redis + - valkey From 456e32abd0d95de46dff5675d466dca0a9a6dab6 Mon Sep 17 00:00:00 2001 From: Denys Sumin Date: Wed, 21 May 2025 16:19:29 +0300 Subject: [PATCH 202/335] fix typo --- docker/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/.env b/docker/.env index dc387dd517..58006db8fc 100644 --- a/docker/.env +++ b/docker/.env @@ -17,7 +17,7 @@ TB_VC_EXECUTOR_DOCKER_NAME=tb-vc-executor EDQS_DOCKER_NAME=tb-edqs EDQS_ENABLED=false -TB_VERSION=4.0.0-RC +TB_VERSION=latest # Database used by ThingsBoard, can be either postgres (PostgreSQL) or hybrid (PostgreSQL for entities database and Cassandra for timeseries database). # According to the database type corresponding docker service will be deployed (see docker-compose.postgres.yml, docker-compose.hybrid.yml for details). From 6b651e199275e2d92f95ad8628eac70c98c771ae Mon Sep 17 00:00:00 2001 From: Anatolii Davydko Date: Thu, 22 May 2025 12:49:11 +0100 Subject: [PATCH 203/335] add api key --- .../controller/TrendzControllerTest.java | 8 +++++++- .../common/data/trendz/TrendzSettings.java | 6 +++++- .../admin/trendz-settings.component.html | 10 +++++++--- .../admin/trendz-settings.component.scss | 10 ++++++++++ .../pages/admin/trendz-settings.component.ts | 20 +++++++++++-------- .../shared/models/trendz-settings.models.ts | 6 ++++-- .../assets/locale/locale.constant-en_US.json | 1 + 7 files changed, 46 insertions(+), 15 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/TrendzControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TrendzControllerTest.java index 115b895521..3612c4a584 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TrendzControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TrendzControllerTest.java @@ -27,6 +27,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. public class TrendzControllerTest extends AbstractControllerTest { private final String trendzUrl = "https://some.domain.com:18888/also_necessary_prefix"; + private final String apiKey = "$2a$10$iDjfqYmnrw9gkdw4XhgzFOU.R/pVz3OKgXOdpbR2LuXaKatGcGLiG"; @Before public void setUp() throws Exception { @@ -35,6 +36,7 @@ public class TrendzControllerTest extends AbstractControllerTest { TrendzSettings trendzSettings = new TrendzSettings(); trendzSettings.setEnabled(true); trendzSettings.setBaseUrl(trendzUrl); + trendzSettings.setApiKey(apiKey); doPost("/api/trendz/settings", trendzSettings).andExpect(status().isOk()); } @@ -48,9 +50,12 @@ public class TrendzControllerTest extends AbstractControllerTest { assertThat(trendzSettings).isNotNull(); assertThat(trendzSettings.isEnabled()).isTrue(); assertThat(trendzSettings.getBaseUrl()).isEqualTo(trendzUrl); + trendzSettings.setApiKey(apiKey); String updatedUrl = "https://some.domain.com:18888/tenant_trendz"; + String updatedApiKey = "$2a$10$aRR0bHa8rtzP5jRcE72vp.hRFsGQz4MGIs62oogLbfOCFK3.RIESG"; trendzSettings.setBaseUrl(updatedUrl); + trendzSettings.setApiKey(updatedApiKey); doPost("/api/trendz/settings", trendzSettings).andExpect(status().isOk()); @@ -65,6 +70,7 @@ public class TrendzControllerTest extends AbstractControllerTest { TrendzSettings newTrendzSettings = new TrendzSettings(); newTrendzSettings.setEnabled(true); newTrendzSettings.setBaseUrl("https://some.domain.com:18888/customer_trendz"); + newTrendzSettings.setApiKey("some_api_key"); doPost("/api/trendz/settings", newTrendzSettings).andExpect(status().isForbidden()); @@ -72,6 +78,6 @@ public class TrendzControllerTest extends AbstractControllerTest { assertThat(fetchedTrendzSettings).isNotNull(); assertThat(fetchedTrendzSettings.isEnabled()).isTrue(); assertThat(fetchedTrendzSettings.getBaseUrl()).isEqualTo(trendzUrl); + assertThat(fetchedTrendzSettings.getApiKey()).isEqualTo(apiKey); } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/trendz/TrendzSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/trendz/TrendzSettings.java index 3c3b49399c..3f00d7062e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/trendz/TrendzSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/trendz/TrendzSettings.java @@ -15,12 +15,16 @@ */ package org.thingsboard.server.common.data.trendz; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@NoArgsConstructor +@AllArgsConstructor public class TrendzSettings { private boolean enabled; private String baseUrl; - + private String apiKey; } diff --git a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html index 45ba62dace..ba6fb21ed4 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/trendz-settings.component.html @@ -31,13 +31,17 @@
+ + {{ 'admin.trendz-enable' | translate }} + admin.trendz-url - - {{ 'admin.trendz-enable' | translate }} - + + admin.trendz-api-key + +
-
+
- -
+ +
- - -
{{ computedTimewindowStyle.icon }}
+ diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.ts b/ui-ngx/src/app/shared/components/time/timewindow.component.ts index ea3f49cf9f..a52e3eb091 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.ts @@ -17,6 +17,7 @@ import { ChangeDetectorRef, Component, + DestroyRef, ElementRef, forwardRef, HostBinding, @@ -26,6 +27,7 @@ import { OnInit, SimpleChanges, StaticProvider, + ViewChild, ViewContainerRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @@ -63,6 +65,7 @@ import { } from '@shared/models/widget-settings.models'; import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models'; import { fromEvent } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; // @dynamic @Component({ @@ -79,6 +82,8 @@ import { fromEvent } from 'rxjs'; }) export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChanges { + @ViewChild('panelContainer', { read: ViewContainerRef, static: true }) panelContainer: ViewContainerRef; + historyOnlyValue = false; @Input() @@ -180,6 +185,10 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan @coerceBoolean() disabled: boolean; + @Input() + @coerceBoolean() + panelMode = true; + innerValue: Timewindow; timewindowDisabled: boolean; @@ -197,7 +206,8 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan private datePipe: DatePipe, private cd: ChangeDetectorRef, private nativeElement: ElementRef, - public viewContainerRef: ViewContainerRef) { + private viewContainerRef: ViewContainerRef, + private destroyRef: DestroyRef) { } ngOnInit() { @@ -249,7 +259,8 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan quickIntervalOnly: this.quickIntervalOnly, aggregation: this.aggregation, timezone: this.timezone, - isEdit: this.isEdit + isEdit: this.isEdit, + panelMode: this.panelMode, } as TimewindowPanelData }, { @@ -317,6 +328,9 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan } else { this.updateDisplayValue(); } + if (!this.panelMode) { + this.createPanel(); + } } notifyChanged() { @@ -328,6 +342,9 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan } updateDisplayValue() { + if (!this.panelMode) { + return + } if (this.innerValue.selectedTab === TimewindowType.REALTIME && !this.historyOnly) { this.innerValue.displayValue = this.displayTypePrefix ? (this.translate.instant('timewindow.realtime') + ' - ') : ''; if (this.innerValue.realtime.realtimeType === RealtimeWindowType.INTERVAL) { @@ -373,4 +390,29 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan ))); } + private createPanel() { + this.panelContainer.clear(); + const panelData = { + timewindow: deepClone(this.innerValue), + historyOnly: this.historyOnly, + forAllTimeEnabled: this.forAllTimeEnabled, + quickIntervalOnly: this.quickIntervalOnly, + aggregation: this.aggregation, + timezone: this.timezone, + isEdit: this.isEdit, + panelMode: this.panelMode, + } + const injector = Injector.create({ + providers: [{ provide: TIMEWINDOW_PANEL_DATA, useValue: panelData }], + parent: this.viewContainerRef.injector + }); + const componentRef = this.panelContainer.createComponent(TimewindowPanelComponent, {index: 0, injector}); + componentRef.instance.changeTimewindow.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(value => { + this.innerValue = value; + this.timewindowDisabled = this.isTimewindowDisabled(); + this.notifyChanged(); + }) + } } diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index c63bd8da52..acb62d9d2a 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -1406,3 +1406,28 @@ export const calculateInterval = (startTime: number, endTime: number, export const getCurrentTimeForComparison = (timeForComparison: moment_.unitOfTime.DurationConstructor, tz?: string): moment_.Moment => getCurrentTime(tz).subtract(1, timeForComparison); + +export const getTimePageLinkInterval = (timewindow: Timewindow): {startTime?: number; endTime?: number} => { + const interval: {startTime?: number; endTime?: number} = {}; + switch (timewindow.history.historyType) { + case HistoryWindowType.LAST_INTERVAL: + const currentTime = Date.now(); + interval.startTime = currentTime - timewindow.history.timewindowMs; + interval.endTime = currentTime; + break; + case HistoryWindowType.FIXED: + interval.startTime = timewindow.history.fixedTimewindow.startTimeMs; + interval.endTime = timewindow.history.fixedTimewindow.endTimeMs; + break; + case HistoryWindowType.INTERVAL: + const startEndTime = calculateIntervalStartEndTime(timewindow.history.quickInterval); + interval.startTime = startEndTime[0]; + interval.endTime = startEndTime[1]; + break; + case HistoryWindowType.FOR_ALL_TIME: + interval.startTime = null; + interval.endTime = null; + break; + } + return interval; +} From acc28245adcf74d845397161e7774d1e93c7bdbc Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 27 May 2025 12:15:06 +0300 Subject: [PATCH 214/335] UI: Hide zero tooltip for time series charts --- .../chart/time-series-chart-basic-config.component.html | 5 +++++ .../chart/time-series-chart-basic-config.component.ts | 4 ++++ .../widget/lib/chart/time-series-chart-tooltip.models.ts | 9 ++++++++- .../time-series-chart-widget-settings.component.html | 5 +++++ .../chart/time-series-chart-widget-settings.component.ts | 3 +++ ui-ngx/src/assets/locale/locale.constant-en_US.json | 1 + 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html index 61c9f89c7c..ddf7216896 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html @@ -311,6 +311,11 @@
+
+ + {{ 'tooltip.hide-zero-false-tooltip-values' | translate }} + +
{{ 'tooltip.background-color' | translate }}
+
+ + {{ 'tooltip.hide-zero-false-tooltip-values' | translate }} + +
{{ 'tooltip.background-color' | translate }}
Date: Tue, 27 May 2025 13:24:44 +0300 Subject: [PATCH 215/335] UI: Fixed calculated twice cell content function for entities table --- .../widget/lib/entity/entities-table-widget.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts index 93d9d09b6d..fadd5c7679 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts @@ -275,7 +275,6 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni public onDataUpdated() { this.entityDatasource.dataUpdated(); this.clearCache(); - this.ctx.detectChanges(); } public onEditModeChanged() { From a49f8a6d67016fcbffb0a67f4f8794a9a399e1ac Mon Sep 17 00:00:00 2001 From: dshvaika Date: Tue, 27 May 2025 16:34:38 +0300 Subject: [PATCH 216/335] renamed env variables and docker related items from redis to valkey --- docker/README.md | 8 +- ...docker-compose.valkey-sentinel.volumes.yml | 4 +- msa/black-box-tests/README.md | 8 +- .../server/msa/ContainerTestSuite.java | 54 ++++----- .../server/msa/ThingsBoardDbInstaller.java | 110 +++++++++--------- ... => docker-compose.valkey-ssl.volumes.yml} | 8 +- ...-ssl.yml => docker-compose.valkey-ssl.yml} | 96 +++++++-------- .../redis.crt => valkey-certs/valkey.crt} | 0 .../redis.key => valkey-certs/valkey.key} | 0 .../redisCA.crt => valkey-certs/valkeyCA.crt} | 0 10 files changed, 144 insertions(+), 144 deletions(-) rename msa/black-box-tests/src/test/resources/{docker-compose.redis-ssl.volumes.yml => docker-compose.valkey-ssl.volumes.yml} (86%) rename msa/black-box-tests/src/test/resources/{docker-compose.redis-ssl.yml => docker-compose.valkey-ssl.yml} (55%) rename msa/black-box-tests/src/test/resources/{redis-certs/redis.crt => valkey-certs/valkey.crt} (100%) rename msa/black-box-tests/src/test/resources/{redis-certs/redis.key => valkey-certs/valkey.key} (100%) rename msa/black-box-tests/src/test/resources/{redis-certs/redisCA.crt => valkey-certs/valkeyCA.crt} (100%) diff --git a/docker/README.md b/docker/README.md index 1a852c80eb..ee8a932857 100644 --- a/docker/README.md +++ b/docker/README.md @@ -19,11 +19,11 @@ In order to set database type change the value of `DATABASE` variable in `.env` In order to set cache type change the value of `CACHE` variable in `.env` file to one of the following: -- `redis` - use Redis standalone cache (1 node - 1 master); -- `redis-cluster` - use Redis cluster cache (6 nodes - 3 masters, 3 slaves); -- `redis-sentinel` - use Redis sentinel cache (3 nodes - 1 master, 1 slave, 1 sentinel) +- `valkey` - use Valkey standalone cache (1 node - 1 primary); +- `valkey-cluster` - use Valkey cluster cache (6 nodes - 3 primaries, 3 replicas); +- `valkey-sentinel` - use Valkey sentinel cache (3 nodes - 1 primary, 1 replica, 1 sentinel) -**NOTE**: According to the cache type corresponding docker service will be deployed (see `docker-compose.redis.yml`, `docker-compose.redis-cluster.yml`, `docker-compose.redis-sentinel.yml` for details). +**NOTE**: According to the cache type corresponding docker service will be deployed (see `docker-compose.valkey.yml`, `docker-compose.valkey-cluster.yml`, `docker-compose.valkey-sentinel.yml` for details). Execute the following command to create log folders for the services and chown of these folders to the docker container users. To be able to change user, **chown** command is used, which requires sudo permissions (script will request password for a sudo access): diff --git a/docker/docker-compose.valkey-sentinel.volumes.yml b/docker/docker-compose.valkey-sentinel.volumes.yml index 1f29d9edaa..0f53ee005d 100644 --- a/docker/docker-compose.valkey-sentinel.volumes.yml +++ b/docker/docker-compose.valkey-sentinel.volumes.yml @@ -31,10 +31,10 @@ services: volumes: valkey-sentinel-data-primary: external: - name: ${VALKEY_SENTINEL_DATA_VOLUME_MASTER} + name: ${VALKEY_SENTINEL_DATA_VOLUME_PRIMARY} valkey-sentinel-data-replica: external: - name: ${VALKEY_SENTINEL_DATA_VOLUME_SLAVE} + name: ${VALKEY_SENTINEL_DATA_VOLUME_REPLICA} valkey-sentinel-data-sentinel: external: name: ${VALKEY_SENTINEL_DATA_VOLUME_SENTINEL} diff --git a/msa/black-box-tests/README.md b/msa/black-box-tests/README.md index 31277e2a55..f777ce7cdf 100644 --- a/msa/black-box-tests/README.md +++ b/msa/black-box-tests/README.md @@ -18,19 +18,19 @@ As result, in REPOSITORY column, next images should be present: thingsboard/tb-web-ui thingsboard/tb-js-executor -- Run the black box tests (without ui tests) in the [msa/black-box-tests](../black-box-tests) directory with Redis standalone: +- Run the black box tests (without ui tests) in the [msa/black-box-tests](../black-box-tests) directory with Valkey standalone: mvn clean install -DblackBoxTests.skip=false -- Run the black box tests (without ui tests) in the [msa/black-box-tests](../black-box-tests) directory with Redis standalone with TLS: +- Run the black box tests (without ui tests) in the [msa/black-box-tests](../black-box-tests) directory with Valkey standalone with TLS: mvn clean install -DblackBoxTests.skip=false -DblackBoxTests.redisSsl=true -- Run the black box tests in the [msa/black-box-tests](../black-box-tests) directory with Redis cluster: +- Run the black box tests in the [msa/black-box-tests](../black-box-tests) directory with the Valkey cluster: mvn clean install -DblackBoxTests.skip=false -DblackBoxTests.redisCluster=true -- Run the black box tests in the [msa/black-box-tests](../black-box-tests) directory with Redis sentinel: +- Run the black box tests in the [msa/black-box-tests](../black-box-tests) directory with Valkey sentinel: mvn clean install -DblackBoxTests.skip=false -DblackBoxTests.redisSentinel=true diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java index 65def6a964..4bc3223754 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java @@ -42,9 +42,9 @@ import static org.testng.Assert.fail; @Slf4j public class ContainerTestSuite { - final static boolean IS_REDIS_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster")); - final static boolean IS_REDIS_SENTINEL = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisSentinel")); - final static boolean IS_REDIS_SSL = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisSsl")); + final static boolean IS_VALKEY_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster")); + final static boolean IS_VALKEY_SENTINEL = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisSentinel")); + final static boolean IS_VALKEY_SSL = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisSsl")); final static boolean IS_HYBRID_MODE = Boolean.parseBoolean(System.getProperty("blackBoxTests.hybridMode")); final static String QUEUE_TYPE = System.getProperty("blackBoxTests.queue", "kafka"); private static final String SOURCE_DIR = "./../../docker/"; @@ -82,9 +82,9 @@ public class ContainerTestSuite { public void start() { installTb = new ThingsBoardDbInstaller(); installTb.createVolumes(); - log.info("System property of blackBoxTests.redisCluster is {}", IS_REDIS_CLUSTER); - log.info("System property of blackBoxTests.redisSentinel is {}", IS_REDIS_SENTINEL); - log.info("System property of blackBoxTests.redisSsl is {}", IS_REDIS_SSL); + log.info("System property of blackBoxTests.redisCluster is {}", IS_VALKEY_CLUSTER); + log.info("System property of blackBoxTests.redisSentinel is {}", IS_VALKEY_SENTINEL); + log.info("System property of blackBoxTests.redisSsl is {}", IS_VALKEY_SSL); log.info("System property of blackBoxTests.hybridMode is {}", IS_HYBRID_MODE); boolean skipTailChildContainers = Boolean.parseBoolean(System.getProperty("blackBoxTests.skipTailChildContainers")); try { @@ -107,10 +107,10 @@ public class ContainerTestSuite { } } - if (IS_REDIS_SSL) { - addToFile(targetDir, "cache-redis.env", + if (IS_VALKEY_SSL) { + addToFile(targetDir, "cache-valkey.env", Map.of("TB_REDIS_SSL_ENABLED", "true", - "TB_REDIS_SSL_PEM_CERT", "/redis/certs/redisCA.crt")); + "TB_REDIS_SSL_PEM_CERT", "/valkey/certs/valkeyCA.crt")); } List composeFiles = new ArrayList<>(Arrays.asList( @@ -123,8 +123,8 @@ public class ContainerTestSuite { new File(targetDir + (IS_HYBRID_MODE ? "docker-compose.hybrid-test-extras.yml" : "docker-compose.postgres-test-extras.yml")), new File(targetDir + "docker-compose.postgres.volumes.yml"), new File(targetDir + "docker-compose." + QUEUE_TYPE + ".yml"), - new File(targetDir + resolveRedisComposeFile()), - new File(targetDir + resolveRedisComposeVolumesFile()), + new File(targetDir + resolveValkeyComposeFile()), + new File(targetDir + resolveValkeyComposeVolumesFile()), new File(targetDir + ("docker-selenium.yml")) )); @@ -198,30 +198,30 @@ public class ContainerTestSuite { } } - private static String resolveRedisComposeFile() { - if (IS_REDIS_CLUSTER) { - return "docker-compose.redis-cluster.yml"; + private static String resolveValkeyComposeFile() { + if (IS_VALKEY_CLUSTER) { + return "docker-compose.valkey-cluster.yml"; } - if (IS_REDIS_SENTINEL) { - return "docker-compose.redis-sentinel.yml"; + if (IS_VALKEY_SENTINEL) { + return "docker-compose.valkey-sentinel.yml"; } - if (IS_REDIS_SSL) { - return "docker-compose.redis-ssl.yml"; + if (IS_VALKEY_SSL) { + return "docker-compose.valkey-ssl.yml"; } - return "docker-compose.redis.yml"; + return "docker-compose.valkey.yml"; } - private static String resolveRedisComposeVolumesFile() { - if (IS_REDIS_CLUSTER) { - return "docker-compose.redis-cluster.volumes.yml"; + private static String resolveValkeyComposeVolumesFile() { + if (IS_VALKEY_CLUSTER) { + return "docker-compose.valkey-cluster.volumes.yml"; } - if (IS_REDIS_SENTINEL) { - return "docker-compose.redis-sentinel.volumes.yml"; + if (IS_VALKEY_SENTINEL) { + return "docker-compose.valkey-sentinel.volumes.yml"; } - if (IS_REDIS_SSL) { - return "docker-compose.redis-ssl.volumes.yml"; + if (IS_VALKEY_SSL) { + return "docker-compose.valkey-ssl.volumes.yml"; } - return "docker-compose.redis.volumes.yml"; + return "docker-compose.valkey.volumes.yml"; } public void stop() { diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java index 9c1a90a4f1..3c57e08487 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java @@ -32,15 +32,15 @@ import java.util.stream.IntStream; @Slf4j public class ThingsBoardDbInstaller { - final static boolean IS_REDIS_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster")); - final static boolean IS_REDIS_SENTINEL = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisSentinel")); + final static boolean IS_VALKEY_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster")); + final static boolean IS_VALKEY_SENTINEL = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisSentinel")); final static boolean IS_HYBRID_MODE = Boolean.parseBoolean(System.getProperty("blackBoxTests.hybridMode")); private final static String POSTGRES_DATA_VOLUME = "tb-postgres-test-data-volume"; private final static String CASSANDRA_DATA_VOLUME = "tb-cassandra-test-data-volume"; - private final static String REDIS_DATA_VOLUME = "tb-redis-data-volume"; - private final static String REDIS_CLUSTER_DATA_VOLUME = "tb-redis-cluster-data-volume"; - private final static String REDIS_SENTINEL_DATA_VOLUME = "tb-redis-sentinel-data-volume"; + private final static String VALKEY_DATA_VOLUME = "tb-valkey-data-volume"; + private final static String VALKEY_CLUSTER_DATA_VOLUME = "tb-valkey-cluster-data-volume"; + private final static String VALKEY_SENTINEL_DATA_VOLUME = "tb-valkey-sentinel-data-volume"; private final static String TB_LOG_VOLUME = "tb-log-test-volume"; private final static String TB_COAP_TRANSPORT_LOG_VOLUME = "tb-coap-transport-log-test-volume"; private final static String TB_LWM2M_TRANSPORT_LOG_VOLUME = "tb-lwm2m-transport-log-test-volume"; @@ -56,9 +56,9 @@ public class ThingsBoardDbInstaller { private final String postgresDataVolume; private final String cassandraDataVolume; - private final String redisDataVolume; - private final String redisClusterDataVolume; - private final String redisSentinelDataVolume; + private final String valkeyDataVolume; + private final String valkeyClusterDataVolume; + private final String valkeySentinelDataVolume; private final String tbLogVolume; private final String tbCoapTransportLogVolume; private final String tbLwm2mTransportLogVolume; @@ -70,8 +70,8 @@ public class ThingsBoardDbInstaller { private final Map env; public ThingsBoardDbInstaller() { - log.info("System property of blackBoxTests.redisCluster is {}", IS_REDIS_CLUSTER); - log.info("System property of blackBoxTests.redisCluster is {}", IS_REDIS_SENTINEL); + log.info("System property of blackBoxTests.redisCluster is {}", IS_VALKEY_CLUSTER); + log.info("System property of blackBoxTests.redisCluster is {}", IS_VALKEY_SENTINEL); log.info("System property of blackBoxTests.hybridMode is {}", IS_HYBRID_MODE); List composeFiles = new ArrayList<>(Arrays.asList( new File("./../../docker/docker-compose.yml"), @@ -80,8 +80,8 @@ public class ThingsBoardDbInstaller { ? new File("./../../docker/docker-compose.hybrid.yml") : new File("./../../docker/docker-compose.postgres.yml"), new File("./../../docker/docker-compose.postgres.volumes.yml"), - resolveRedisComposeFile(), - resolveRedisComposeVolumesFile() + resolveValkeyComposeFile(), + resolveValkeyComposeVolumesFile() )); if (IS_HYBRID_MODE) { composeFiles.add(new File("./../../docker/docker-compose.cassandra.volumes.yml")); @@ -95,9 +95,9 @@ public class ThingsBoardDbInstaller { postgresDataVolume = project + "_" + POSTGRES_DATA_VOLUME; cassandraDataVolume = project + "_" + CASSANDRA_DATA_VOLUME; - redisDataVolume = project + "_" + REDIS_DATA_VOLUME; - redisClusterDataVolume = project + "_" + REDIS_CLUSTER_DATA_VOLUME; - redisSentinelDataVolume = project + "_" + REDIS_SENTINEL_DATA_VOLUME; + valkeyDataVolume = project + "_" + VALKEY_DATA_VOLUME; + valkeyClusterDataVolume = project + "_" + VALKEY_CLUSTER_DATA_VOLUME; + valkeySentinelDataVolume = project + "_" + VALKEY_SENTINEL_DATA_VOLUME; tbLogVolume = project + "_" + TB_LOG_VOLUME; tbCoapTransportLogVolume = project + "_" + TB_COAP_TRANSPORT_LOG_VOLUME; tbLwm2mTransportLogVolume = project + "_" + TB_LWM2M_TRANSPORT_LOG_VOLUME; @@ -123,38 +123,38 @@ public class ThingsBoardDbInstaller { env.put("TB_SNMP_TRANSPORT_LOG_VOLUME", tbSnmpTransportLogVolume); env.put("TB_VC_EXECUTOR_LOG_VOLUME", tbVcExecutorLogVolume); env.put("TB_EDQS_LOG_VOLUME", tbEdqsLogVolume); - if (IS_REDIS_CLUSTER) { + if (IS_VALKEY_CLUSTER) { for (int i = 0; i < 6; i++) { - env.put("REDIS_CLUSTER_DATA_VOLUME_" + i, redisClusterDataVolume + '-' + i); + env.put("VALKEY_CLUSTER_DATA_VOLUME_" + i, valkeyClusterDataVolume + '-' + i); } - } else if (IS_REDIS_SENTINEL) { - env.put("REDIS_SENTINEL_DATA_VOLUME_MASTER", redisSentinelDataVolume + "-" + "master"); - env.put("REDIS_SENTINEL_DATA_VOLUME_SLAVE", redisSentinelDataVolume + "-" + "slave"); - env.put("REDIS_SENTINEL_DATA_VOLUME_SENTINEL", redisSentinelDataVolume + "-" + "sentinel"); + } else if (IS_VALKEY_SENTINEL) { + env.put("VALKEY_SENTINEL_DATA_VOLUME_PRIMARY", valkeySentinelDataVolume + "-" + "primary"); + env.put("VALKEY_SENTINEL_DATA_VOLUME_REPLICA", valkeySentinelDataVolume + "-" + "replica"); + env.put("VALKEY_SENTINEL_DATA_VOLUME_SENTINEL", valkeySentinelDataVolume + "-" + "sentinel"); } else { - env.put("REDIS_DATA_VOLUME", redisDataVolume); + env.put("VALKEY_DATA_VOLUME", valkeyDataVolume); } dockerCompose.withEnv(env); } - private static File resolveRedisComposeVolumesFile() { - if (IS_REDIS_CLUSTER) { - return new File("./../../docker/docker-compose.redis-cluster.volumes.yml"); + private static File resolveValkeyComposeVolumesFile() { + if (IS_VALKEY_CLUSTER) { + return new File("./../../docker/docker-compose.valkey-cluster.volumes.yml"); } - if (IS_REDIS_SENTINEL) { - return new File("./../../docker/docker-compose.redis-sentinel.volumes.yml"); + if (IS_VALKEY_SENTINEL) { + return new File("./../../docker/docker-compose.valkey-sentinel.volumes.yml"); } - return new File("./../../docker/docker-compose.redis.volumes.yml"); + return new File("./../../docker/docker-compose.valkey.volumes.yml"); } - private static File resolveRedisComposeFile() { - if (IS_REDIS_CLUSTER) { - return new File("./../../docker/docker-compose.redis-cluster.yml"); + private static File resolveValkeyComposeFile() { + if (IS_VALKEY_CLUSTER) { + return new File("./../../docker/docker-compose.valkey-cluster.yml"); } - if (IS_REDIS_SENTINEL) { - return new File("./../../docker/docker-compose.redis-sentinel.yml"); + if (IS_VALKEY_SENTINEL) { + return new File("./../../docker/docker-compose.valkey-sentinel.yml"); } - return new File("./../../docker/docker-compose.redis.yml"); + return new File("./../../docker/docker-compose.valkey.yml"); } public Map getEnv() { @@ -200,27 +200,27 @@ public class ThingsBoardDbInstaller { if (IS_HYBRID_MODE) { additionalServices.append(" cassandra"); } - if (IS_REDIS_CLUSTER) { + if (IS_VALKEY_CLUSTER) { for (int i = 0; i < 6; i++) { - additionalServices.append(" redis-node-").append(i); - dockerCompose.withCommand("volume create " + redisClusterDataVolume + '-' + i); + additionalServices.append(" valkey-node-").append(i); + dockerCompose.withCommand("volume create " + valkeyClusterDataVolume + '-' + i); dockerCompose.invokeDocker(); } - } else if (IS_REDIS_SENTINEL) { - additionalServices.append(" redis-master"); - dockerCompose.withCommand("volume create " + redisSentinelDataVolume + "-" + "master"); + } else if (IS_VALKEY_SENTINEL) { + additionalServices.append(" valkey-primary"); + dockerCompose.withCommand("volume create " + valkeySentinelDataVolume + "-" + "primary"); dockerCompose.invokeDocker(); - additionalServices.append(" redis-slave"); - dockerCompose.withCommand("volume create " + redisSentinelDataVolume + '-' + "slave"); + additionalServices.append(" valkey-replica"); + dockerCompose.withCommand("volume create " + valkeySentinelDataVolume + '-' + "replica"); dockerCompose.invokeDocker(); - additionalServices.append(" redis-sentinel"); - dockerCompose.withCommand("volume create " + redisSentinelDataVolume + '-' + "sentinel"); + additionalServices.append(" valkey-sentinel"); + dockerCompose.withCommand("volume create " + valkeySentinelDataVolume + '-' + "sentinel"); dockerCompose.invokeDocker(); } else { - additionalServices.append(" redis"); - dockerCompose.withCommand("volume create " + redisDataVolume); + additionalServices.append(" valkey"); + dockerCompose.withCommand("volume create " + valkeyDataVolume); dockerCompose.invokeDocker(); } @@ -261,7 +261,7 @@ public class ThingsBoardDbInstaller { .add(tbSnmpTransportLogVolume) .add(tbVcExecutorLogVolume) .add(tbEdqsLogVolume) - .add(resolveRedisComposeVolumeLog()); + .add(resolveValkeyComposeVolumeLog()); if (IS_HYBRID_MODE) { rmVolumesCommand.add(cassandraDataVolume); @@ -270,16 +270,16 @@ public class ThingsBoardDbInstaller { dockerCompose.withCommand(rmVolumesCommand.toString()); } - private String resolveRedisComposeVolumeLog() { - if (IS_REDIS_CLUSTER) { - return IntStream.range(0, 6).mapToObj(i -> " " + redisClusterDataVolume + "-" + i).collect(Collectors.joining()); + private String resolveValkeyComposeVolumeLog() { + if (IS_VALKEY_CLUSTER) { + return IntStream.range(0, 6).mapToObj(i -> " " + valkeyClusterDataVolume + "-" + i).collect(Collectors.joining()); } - if (IS_REDIS_SENTINEL) { - return redisSentinelDataVolume + "-" + "master " + " " + - redisSentinelDataVolume + "-" + "slave" + " " + - redisSentinelDataVolume + " " + "sentinel"; + if (IS_VALKEY_SENTINEL) { + return valkeySentinelDataVolume + "-" + "primary " + " " + + valkeySentinelDataVolume + "-" + "replica" + " " + + valkeySentinelDataVolume + " " + "sentinel"; } - return redisDataVolume; + return valkeyDataVolume; } private void copyLogs(String volumeName, String targetDir) { diff --git a/msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.volumes.yml b/msa/black-box-tests/src/test/resources/docker-compose.valkey-ssl.volumes.yml similarity index 86% rename from msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.volumes.yml rename to msa/black-box-tests/src/test/resources/docker-compose.valkey-ssl.volumes.yml index 3b23511501..7da5f154a6 100644 --- a/msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.volumes.yml +++ b/msa/black-box-tests/src/test/resources/docker-compose.valkey-ssl.volumes.yml @@ -17,11 +17,11 @@ version: '3.0' services: - redis: + valkey: volumes: - - redis-data:/bitnami/redis/data + - valkey-data:/bitnami/valkey/data volumes: - redis-data: + valkey-data: external: - name: ${REDIS_DATA_VOLUME} + name: ${VALKEY_DATA_VOLUME} diff --git a/msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.yml b/msa/black-box-tests/src/test/resources/docker-compose.valkey-ssl.yml similarity index 55% rename from msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.yml rename to msa/black-box-tests/src/test/resources/docker-compose.valkey-ssl.yml index 6ff24564b7..73de64b303 100644 --- a/msa/black-box-tests/src/test/resources/docker-compose.redis-ssl.yml +++ b/msa/black-box-tests/src/test/resources/docker-compose.valkey-ssl.yml @@ -17,114 +17,114 @@ version: '3.0' services: -# Redis standalone -# The latest version of Redis compatible with ThingsBoard is 7.2 - redis: +# Valkey standalone +# The latest version of Valkey compatible with ThingsBoard is 8.0 + valkey: restart: always image: bitnami/valkey:8.0 environment: # ALLOW_EMPTY_PASSWORD is recommended only for development. - 'ALLOW_EMPTY_PASSWORD=yes' - 'VALKEY_TLS_ENABLED=yes' - - 'VALKEY_TLS_CERT_FILE=/redis/certs/redis.crt' - - 'VALKEY_TLS_KEY_FILE=/redis/certs/redis.key' - - 'VALKEY_TLS_CA_FILE=/redis/certs/redisCA.crt' + - 'VALKEY_TLS_CERT_FILE=/valkey/certs/valkey.crt' + - 'VALKEY_TLS_KEY_FILE=/valkey/certs/valkey.key' + - 'VALKEY_TLS_CA_FILE=/valkey/certs/valkeyCA.crt' - 'VALKEY_TLS_AUTH_CLIENTS=no' ports: - '6379:6379' volumes: - - ./tb-node/redis-data:/bitnami/redis/data - - ./redis-certs:/redis/certs + - ./tb-node/valkey-data:/bitnami/valkey/data + - ./valkey-certs:/valkey/certs -# ThingsBoard setup to use redis-standalone +# ThingsBoard setup to use valkey-standalone tb-core1: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-core2: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-rule-engine1: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-rule-engine2: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-mqtt-transport1: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-mqtt-transport2: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-http-transport1: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-http-transport2: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-coap-transport: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-lwm2m-transport: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-snmp-transport: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-vc-executor1: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey tb-vc-executor2: env_file: - - cache-redis.env + - cache-valkey.env volumes: - - ./redis-certs:/redis/certs + - ./valkey-certs:/valkey/certs depends_on: - - redis + - valkey diff --git a/msa/black-box-tests/src/test/resources/redis-certs/redis.crt b/msa/black-box-tests/src/test/resources/valkey-certs/valkey.crt similarity index 100% rename from msa/black-box-tests/src/test/resources/redis-certs/redis.crt rename to msa/black-box-tests/src/test/resources/valkey-certs/valkey.crt diff --git a/msa/black-box-tests/src/test/resources/redis-certs/redis.key b/msa/black-box-tests/src/test/resources/valkey-certs/valkey.key similarity index 100% rename from msa/black-box-tests/src/test/resources/redis-certs/redis.key rename to msa/black-box-tests/src/test/resources/valkey-certs/valkey.key diff --git a/msa/black-box-tests/src/test/resources/redis-certs/redisCA.crt b/msa/black-box-tests/src/test/resources/valkey-certs/valkeyCA.crt similarity index 100% rename from msa/black-box-tests/src/test/resources/redis-certs/redisCA.crt rename to msa/black-box-tests/src/test/resources/valkey-certs/valkeyCA.crt From 92e81ad233f78c91dc03b5e244ef1ce7536cde55 Mon Sep 17 00:00:00 2001 From: dshvaika Date: Tue, 27 May 2025 16:42:21 +0300 Subject: [PATCH 217/335] renamed module service-info-api to discovery-api && fixed LimitedApi typos --- application/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- .../thingsboard/server/common/data/limit/LimitedApi.java | 8 ++++---- common/{service-info-api => discovery-api}/pom.xml | 2 +- .../server/queue/discovery/TbServiceInfoProvider.java | 0 common/edqs/pom.xml | 2 +- common/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- dao/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) rename common/{service-info-api => discovery-api}/pom.xml (98%) rename common/{service-info-api => discovery-api}/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java (100%) diff --git a/application/pom.xml b/application/pom.xml index 4f1d75cde2..34218956fc 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -130,7 +130,7 @@ org.thingsboard.common - service-info-api + discovery-api org.thingsboard diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 44f16f7329..b5ca9c1b9c 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -48,7 +48,7 @@ org.thingsboard.common - service-info-api + discovery-api org.thingsboard.common.transport diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java index 19eb4b510f..2a9991a1cc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java @@ -30,10 +30,10 @@ public enum LimitedApi { REST_REQUESTS_PER_TENANT(DefaultTenantProfileConfiguration::getTenantServerRestLimitsConfiguration, "REST API requests", true), REST_REQUESTS_PER_CUSTOMER(DefaultTenantProfileConfiguration::getCustomerServerRestLimitsConfiguration, "REST API requests per customer", false), WS_UPDATES_PER_SESSION(DefaultTenantProfileConfiguration::getWsUpdatesPerSessionRateLimit, "WS updates per session", true), - CASSANDRA_WRITE_QUERIES_CORE(DefaultTenantProfileConfiguration::getCassandraReadQueryTenantCoreRateLimits, "Rest API Cassandra write queries", true), - CASSANDRA_READ_QUERIES_CORE(DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantCoreRateLimits, "Rest API and WS telemetry Cassandra read queries", true), - CASSANDRA_WRITE_QUERIES_RULE_ENGINE(DefaultTenantProfileConfiguration::getCassandraReadQueryTenantRuleEngineRateLimits, "Rule Engine telemetry Cassandra write queries", true), - CASSANDRA_READ_QUERIES_RULE_ENGINE(DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantRuleEngineRateLimits, "Rule Engine telemetry Cassandra read queries", true), + CASSANDRA_WRITE_QUERIES_CORE(DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantCoreRateLimits, "Rest API Cassandra write queries", true), + CASSANDRA_READ_QUERIES_CORE(DefaultTenantProfileConfiguration::getCassandraReadQueryTenantCoreRateLimits, "Rest API and WS telemetry Cassandra read queries", true), + CASSANDRA_WRITE_QUERIES_RULE_ENGINE(DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantRuleEngineRateLimits, "Rule Engine telemetry Cassandra write queries", true), + CASSANDRA_READ_QUERIES_RULE_ENGINE(DefaultTenantProfileConfiguration::getCassandraReadQueryTenantRuleEngineRateLimits, "Rule Engine telemetry Cassandra read queries", true), CASSANDRA_READ_QUERIES_MONOLITH( LimitedApiUtil.merge( DefaultTenantProfileConfiguration::getCassandraReadQueryTenantCoreRateLimits, diff --git a/common/service-info-api/pom.xml b/common/discovery-api/pom.xml similarity index 98% rename from common/service-info-api/pom.xml rename to common/discovery-api/pom.xml index c1eff7c468..5852761c4a 100644 --- a/common/service-info-api/pom.xml +++ b/common/discovery-api/pom.xml @@ -24,7 +24,7 @@ common org.thingsboard.common - service-info-api + discovery-api jar Thingsboard Server Service Info Provider API diff --git a/common/service-info-api/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java b/common/discovery-api/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java similarity index 100% rename from common/service-info-api/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java rename to common/discovery-api/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java diff --git a/common/edqs/pom.xml b/common/edqs/pom.xml index 3b72a26442..e349ae3087 100644 --- a/common/edqs/pom.xml +++ b/common/edqs/pom.xml @@ -70,7 +70,7 @@ org.thingsboard.common - service-info-api + discovery-api org.springframework.boot diff --git a/common/pom.xml b/common/pom.xml index 514e3eea38..0915f47f1a 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -50,7 +50,7 @@ version-control script edqs - service-info-api + discovery-api diff --git a/common/queue/pom.xml b/common/queue/pom.xml index e689cb3dc5..f2f3091999 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -62,7 +62,7 @@ org.thingsboard.common - service-info-api + discovery-api org.apache.kafka diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 064c5f8ea2..7f54dcbc85 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -62,7 +62,7 @@ org.thingsboard.common - service-info-api + discovery-api com.google.code.gson diff --git a/dao/pom.xml b/dao/pom.xml index a11e4e4fe3..9f411f8302 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -61,7 +61,7 @@ org.thingsboard.common - service-info-api + discovery-api com.networknt diff --git a/pom.xml b/pom.xml index f8137196e4..334be1850e 100755 --- a/pom.xml +++ b/pom.xml @@ -966,7 +966,7 @@ org.thingsboard.common - service-info-api + discovery-api ${project.version} From e09dd19e96388984ec384b6726ed5ac390252b1c Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 27 May 2025 17:23:25 +0300 Subject: [PATCH 218/335] Add Serializable to TrendzSettings --- .../thingsboard/server/common/data/trendz/TrendzSettings.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/trendz/TrendzSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/trendz/TrendzSettings.java index 3c3b49399c..ab637efff7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/trendz/TrendzSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/trendz/TrendzSettings.java @@ -17,8 +17,10 @@ package org.thingsboard.server.common.data.trendz; import lombok.Data; +import java.io.Serializable; + @Data -public class TrendzSettings { +public class TrendzSettings implements Serializable { private boolean enabled; private String baseUrl; From 80a33492d587c932452acd3584ee82aec04e1cf6 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 27 May 2025 18:26:19 +0300 Subject: [PATCH 219/335] Add proto number for ComponentLifecycleEvent --- .../server/common/data/EntityType.java | 17 ++++++++ .../data/plugin/ComponentLifecycleEvent.java | 40 ++++++++++++++++--- .../server/common/util/ProtoUtils.java | 21 +++++----- .../server/common/util/ProtoUtilsTest.java | 7 ++++ 4 files changed, 69 insertions(+), 16 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index 93e754eb2c..7582b377f0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data; import lombok.Getter; import org.apache.commons.lang3.StringUtils; +import java.util.Arrays; import java.util.EnumSet; import java.util.List; @@ -76,6 +77,15 @@ public enum EntityType { public static final List NORMAL_NAMES = EnumSet.allOf(EntityType.class).stream() .map(EntityType::getNormalName).toList(); + private static final EntityType[] BY_PROTO; + + static { + BY_PROTO = new EntityType[Arrays.stream(values()).mapToInt(EntityType::getProtoNumber).max().orElse(0) + 1]; + for (EntityType entityType : values()) { + BY_PROTO[entityType.getProtoNumber()] = entityType; + } + } + EntityType(int protoNumber) { this.protoNumber = protoNumber; this.tableName = name().toLowerCase(); @@ -98,4 +108,11 @@ public enum EntityType { return false; } + public static EntityType forProtoNumber(int protoNumber) { + if (protoNumber < 0 || protoNumber >= BY_PROTO.length) { + throw new IllegalArgumentException("Invalid EntityType proto number " + protoNumber); + } + return BY_PROTO[protoNumber]; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java index 32ced72292..5d13db2348 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java @@ -15,12 +15,42 @@ */ package org.thingsboard.server.common.data.plugin; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + import java.io.Serializable; +import java.util.Arrays; -/** - * @author Andrew Shvayka - */ +@RequiredArgsConstructor public enum ComponentLifecycleEvent implements Serializable { - // In sync with ComponentLifecycleEvent proto - CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED, FAILED, DEACTIVATED + + CREATED(0), + STARTED(1), + ACTIVATED(2), + SUSPENDED(3), + UPDATED(4), + STOPPED(5), + DELETED(6), + FAILED(7), + DEACTIVATED(8); + + @Getter + private final int protoNumber; // corresponds to ComponentLifecycleEvent proto + + private static final ComponentLifecycleEvent[] BY_PROTO; + + static { + BY_PROTO = new ComponentLifecycleEvent[Arrays.stream(values()).mapToInt(ComponentLifecycleEvent::getProtoNumber).max().orElse(0) + 1]; + for (ComponentLifecycleEvent event : values()) { + BY_PROTO[event.getProtoNumber()] = event; + } + } + + public static ComponentLifecycleEvent forProtoNumber(int protoNumber) { + if (protoNumber < 0 || protoNumber >= BY_PROTO.length) { + throw new IllegalArgumentException("Invalid ComponentLifecycleEvent proto number " + protoNumber); + } + return BY_PROTO[protoNumber]; + } + } diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java index 502884eb31..77250486de 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java @@ -102,7 +102,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ApiUsageRecordKeyPro import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.UUID; @@ -114,14 +113,6 @@ import static org.thingsboard.server.common.data.DataConstants.GATEWAY_PARAMETER @Slf4j public class ProtoUtils { - private static final EntityType[] entityTypeByProtoNumber; - - static { - int arraySize = Arrays.stream(EntityType.values()).mapToInt(EntityType::getProtoNumber).max().orElse(0); - entityTypeByProtoNumber = new EntityType[arraySize + 1]; - Arrays.stream(EntityType.values()).forEach(entityType -> entityTypeByProtoNumber[entityType.getProtoNumber()] = entityType); - } - public static TransportProtos.ComponentLifecycleMsgProto toProto(ComponentLifecycleMsg msg) { var builder = TransportProtos.ComponentLifecycleMsgProto.newBuilder() .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) @@ -129,7 +120,7 @@ public class ProtoUtils { .setEntityType(toProto(msg.getEntityId().getEntityType())) .setEntityIdMSB(msg.getEntityId().getId().getMostSignificantBits()) .setEntityIdLSB(msg.getEntityId().getId().getLeastSignificantBits()) - .setEvent(TransportProtos.ComponentLifecycleEvent.forNumber(msg.getEvent().ordinal())); + .setEvent(TransportProtos.ComponentLifecycleEvent.forNumber(msg.getEvent().getProtoNumber())); if (msg.getProfileId() != null) { builder.setProfileIdMSB(msg.getProfileId().getId().getMostSignificantBits()); builder.setProfileIdLSB(msg.getProfileId().getId().getLeastSignificantBits()); @@ -175,7 +166,15 @@ public class ProtoUtils { } public static EntityType fromProto(TransportProtos.EntityTypeProto entityType) { - return entityTypeByProtoNumber[entityType.getNumber()]; + return EntityType.forProtoNumber(entityType.getNumber()); + } + + public static TransportProtos.ComponentLifecycleEvent toProto(ComponentLifecycleEvent event) { + return TransportProtos.ComponentLifecycleEvent.forNumber(event.getProtoNumber()); + } + + public static ComponentLifecycleEvent fromProto(TransportProtos.ComponentLifecycleEvent eventProto) { + return ComponentLifecycleEvent.forProtoNumber(eventProto.getNumber()); } public static TransportProtos.ToEdgeSyncRequestMsgProto toProto(ToEdgeSyncRequest request) { diff --git a/common/proto/src/test/java/org/thingsboard/server/common/util/ProtoUtilsTest.java b/common/proto/src/test/java/org/thingsboard/server/common/util/ProtoUtilsTest.java index e1714a9335..25788721dc 100644 --- a/common/proto/src/test/java/org/thingsboard/server/common/util/ProtoUtilsTest.java +++ b/common/proto/src/test/java/org/thingsboard/server/common/util/ProtoUtilsTest.java @@ -114,6 +114,13 @@ class ProtoUtilsTest { } } + @Test + void protoComponentLifecycleEventSerialization() { + for (ComponentLifecycleEvent event : ComponentLifecycleEvent.values()) { + assertThat(ProtoUtils.fromProto(ProtoUtils.toProto(event))).isEqualTo(event); + } + } + @Test void protoEdgeHighPrioritySerialization() { EdgeHighPriorityMsg msg = new EdgeHighPriorityMsg(tenantId, EdgeUtils.constructEdgeEvent(tenantId, edgeId, From b350dc0efe9789f117569ec57991000fb03eda5d Mon Sep 17 00:00:00 2001 From: dshvaika Date: Wed, 28 May 2025 09:44:54 +0300 Subject: [PATCH 220/335] Updated valkey certs. Not after validity: May 28 06:15:29 2026 GMT --- .../test/resources/valkey-certs/valkey.crt | 33 ++++------------ .../test/resources/valkey-certs/valkey.key | 36 ++++------------- .../test/resources/valkey-certs/valkeyCA.crt | 39 +++++-------------- 3 files changed, 24 insertions(+), 84 deletions(-) diff --git a/msa/black-box-tests/src/test/resources/valkey-certs/valkey.crt b/msa/black-box-tests/src/test/resources/valkey-certs/valkey.crt index 8fce48c0a1..8f06395b44 100755 --- a/msa/black-box-tests/src/test/resources/valkey-certs/valkey.crt +++ b/msa/black-box-tests/src/test/resources/valkey-certs/valkey.crt @@ -1,28 +1,9 @@ -----BEGIN CERTIFICATE----- -MIIE1TCCAr2gAwIBAgIUFdFLz/q0EosJc39HhIac9+XpyRkwDQYJKoZIhvcNAQEL -BQAwWTELMAkGA1UEBhMCdWExDTALBgNVBAgMBGt5aXYxDTALBgNVBAcMBGt5aXYx -CzAJBgNVBAoMAnRiMQswCQYDVQQLDAJ0YjESMBAGA1UEAwwJbG9jYWxob3N0MB4X -DTIzMDkwNDE1NTA0MVoXDTI0MDkwMzE1NTA0MVowYjELMAkGA1UEBhMCQ1IxEzAR -BgNVBAgMCkNoYW5kcmlsbGExEzARBgNVBAcMCkNoYW5kcmlsbGExEjAQBgNVBAMM -CUhPU1RfTkFNRTEVMBMGA1UECgwMTmV3IFJlcHVibGljMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA+ICaK/aSklkUIih3cl4k4RYJL8rLS68d5JVSxpCQ -8MwuAakdU+ptD0b6X4+CcNtR96UlcO3cR15GLLT6s29Kw4Ta5SME+yhuFLUIrWxA -/gJ/pkJGkq1vXYZzdUFjtMlF+VbIw+r2hhSkbTR1hV08iRlvflafS8JB/tznqTFy -QIXu08heRtxVaC6SMHLeHmZdgdJrSOulwg/ctcP6tki+ZU9v+TH71M3mTIOLzuSz -7sqnFMPgW7ER0Utc4fndRfz17LA1NZdSrN0Ch5IO+EZ9gf/25w8makbx7lZoZASm -sAd0Uyq9ZPC0ok+oJjeDwanl/Bo7CGEgdxaFYNKpnizyUQIDAQABo4GLMIGIMAsG -A1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGgYDVR0R -BBMwEYIJbG9jYWxob3N0hwR/AAABMB0GA1UdDgQWBBRAIkMMxT30wZYoNQuDM7uT -qwb+VzAfBgNVHSMEGDAWgBSfpEhvBqWx6usjDlGx7lEx8Fl21DANBgkqhkiG9w0B -AQsFAAOCAgEAMeaZ5K0w2kPSqcZbzV4WwGShrhnSYdsA4dlZhAUNNxsoXX590Ppe -lla+vhFSdk/IwjFxLzmiau5+JlCySeOJv2AaG56JvBBU1Wl8LOk01a7qRctiKRth -AGFGKZoJ50h5W1A0NV7KwcGIkQdebAFPdMmkOd6Do98ZhkzYLRuAK3U0K4wNQJuf -gPt9XtIugRNQOwxolXAj81FfhjZ5CnoaCQYJBFyIenwg4uGjg+D0F9WtAlRq8ww+ -XpWpVw8QgPDk+SVoGXUsBs+wMCDlGu4ozN2lIvG1N5n1q0qn39SUYOSBkEsdM+AS -Yu6LMP4J1SPOwT5UJN2jK7fAYBdChF39nV/xiatfUSy8rWUFQSwGv6JD3X0MAYfT -DyOiYdX8o+AnetjfBHHwVXDobh5d1GiC2DwoUNW7KoEdj5mmhDZKiB2S47/5J7El -UA9CevmmDvf/tN9itPrutSwcb7uwLYRsf7Gx3D3P/2+nQHUKyNcyQCNtgR7RKHBS -EjeedMgtKvrqsdPnk6Ygwj8EMh4owDIDcieqnPZAxhqJOJT2ZORdzyelmpv5aDGc -0XnnRHRInSUgQStfPa9ghOBpSXlhxL1EJFFik+yFOjH4GivhoynCb7zjW+MjlPDV -LAsnmMukBR95ZkpxMnRUEoLTEvTaxmg4Vr/mqXeQUKdU8A812Wrk2hc= +MIIBLTCB1AIUSQ2Ce8LOpzqOJhL2aXCTVrd45twwCgYIKoZIzj0EAwIwHDEaMBgG +A1UEAwwRVmFsa2V5IEVDIFJvb3QgQ0EwHhcNMjUwNTI4MDYxNTI5WhcNMjYwNTI4 +MDYxNTI5WjAXMRUwEwYDVQQDDAx2YWxrZXkubG9jYWwwWTATBgcqhkjOPQIBBggq +hkjOPQMBBwNCAATSpimU25+2yIKGNwSQgPN2QaEcfWnj4c8PcTjeXg/1C2+fpMyl +jgknA64GUM2VeoVbG0mTkywlRU57+2yVLKYrMAoGCCqGSM49BAMCA0gAMEUCIDi0 +ri1mBU64a8C0L/QZet0C/+HsbhItq40AmtZyEKWlAiEA8FJG51ZvncgYx8PCLGS7 +WmBCqfnG58W4ga4M6YhEnYA= -----END CERTIFICATE----- diff --git a/msa/black-box-tests/src/test/resources/valkey-certs/valkey.key b/msa/black-box-tests/src/test/resources/valkey-certs/valkey.key index cbbf83ca2b..eba80567dc 100755 --- a/msa/black-box-tests/src/test/resources/valkey-certs/valkey.key +++ b/msa/black-box-tests/src/test/resources/valkey-certs/valkey.key @@ -1,28 +1,8 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD4gJor9pKSWRQi -KHdyXiThFgkvystLrx3klVLGkJDwzC4BqR1T6m0PRvpfj4Jw21H3pSVw7dxHXkYs -tPqzb0rDhNrlIwT7KG4UtQitbED+An+mQkaSrW9dhnN1QWO0yUX5VsjD6vaGFKRt -NHWFXTyJGW9+Vp9LwkH+3OepMXJAhe7TyF5G3FVoLpIwct4eZl2B0mtI66XCD9y1 -w/q2SL5lT2/5MfvUzeZMg4vO5LPuyqcUw+BbsRHRS1zh+d1F/PXssDU1l1Ks3QKH -kg74Rn2B//bnDyZqRvHuVmhkBKawB3RTKr1k8LSiT6gmN4PBqeX8GjsIYSB3FoVg -0qmeLPJRAgMBAAECggEAE6sCCMa8NQ8N0+JGCew/mP0Ifxra2kOi5wuWgJbCkfxn -C8SZyKF+Pj5M5LFUDqCdLS+J9hUtYQyqGzG7weXmEfF67bXG2CYMCGGHrUorHq+N -8NfABC3r6YgRrU8emBlyC1j+DNuU5WnO1cHYJ1UIzIUR2Pr8Ip/eX1CWmUKLm2WW -YvUGzvTG0mM0l9/Q8pcTndAxDuL90GG6TrxtQoy7Ir6dYxTVKHgOwa6RX86VrCkm -jl6Wu7Bvo2fnPvPVx8p1mgWNgtBbnc/VAYjeF7Yi7CETWHTxGM4iGtZNL8/xxlW7 -sm2SmWEu72EupLYgytA9w+EptjbwWfcTaWbWUXTFHQKBgQD5nmYLVvZUR1oc9g2a -Q7XpFQ/jVvwedeKGolMbWTFrEhl1+D8lKN/S3SoRCOALOUJFUCU+L1snN3MsJ5Es -Gb0FLSH1LBBfuHqP+aHn/uGLMW8e7P9thh4DQ1tIzKE5xhh+JDJJqd15YJlsDy1F -0aKWyulS4o9XK5q86rs5QbI4MwKBgQD+2uXEAiFwEXq7pw2e8STrnt0jp09KU6b1 -z/ykyArBdcVkrEudZ+jIrR/6rlSKK+SKQxtx8MG9M/Nm09KFRCAWblxHBwQ87ZnU -8tMAmPHrikLKk1dbbU3BQ3cZkIWMryCo4wzuxeT4mc2goTGNZpNZDpjqjCXbnCgP -T29aPHG3awKBgGFv8UlP4su3JnfTnC+xaprXO+J0G+oP/iKrzmEIif/Pity/0HZC -5Eu9RSRtIHeBHFtOE5uYhK5kOLLtpv9d9KjGm1DGqIWUz1LQEOEsXwIkg8nAnVw1 -VBXV/xYFupGAwCLNIkwa4Hb2vCywJ+3vDNZr0nQmN+nA/Z/syLRq7pR9AoGBALqp -l3pd2SHdG5jP/VD57IHLRMs1YwTcikAmizQh9IbH/MEE1QlALya0buTLxM3C4kxG -ZJaqsSwkHdWltd64DAyB3oKDaB48JNzs0ZDxdNeA1/TJwEUNpNK12EjYKojlSDWK -v1Evjspq1EofZkzb4XZsE6JO7feQw2KbWsKr3NprAoGBAKSSAjDpncxb+9Pr0Lwa -AS7ATgMhot6+lbtZZV6egTGUVvtgd1LyE2ZJkE+5XJRu49X1lSJdDpFT66rG5eNV -+rYnDqXL8c0Z/j1L96z1UMY6DLPj3n+07zgLiNIrOR4UKP/+TGB9MHHFMEpYnij0 -m/f6dg0Ujw6CW31Hq2QdJ9P7 ------END PRIVATE KEY----- +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIHsaG9ozysmDm7xv+d+i/vl7xJUHcgL93/9rvQfd3PWDoAoGCCqGSM49 +AwEHoUQDQgAE0qYplNuftsiChjcEkIDzdkGhHH1p4+HPD3E43l4P9Qtvn6TMpY4J +JwOuBlDNlXqFWxtJk5MsJUVOe/tslSymKw== +-----END EC PRIVATE KEY----- diff --git a/msa/black-box-tests/src/test/resources/valkey-certs/valkeyCA.crt b/msa/black-box-tests/src/test/resources/valkey-certs/valkeyCA.crt index 444dac130b..6210085236 100644 --- a/msa/black-box-tests/src/test/resources/valkey-certs/valkeyCA.crt +++ b/msa/black-box-tests/src/test/resources/valkey-certs/valkeyCA.crt @@ -1,32 +1,11 @@ -----BEGIN CERTIFICATE----- -MIIFkzCCA3ugAwIBAgIUK9fjeDv+ESrdFSHMqsod5djzTh4wDQYJKoZIhvcNAQEL -BQAwWTELMAkGA1UEBhMCdWExDTALBgNVBAgMBGt5aXYxDTALBgNVBAcMBGt5aXYx -CzAJBgNVBAoMAnRiMQswCQYDVQQLDAJ0YjESMBAGA1UEAwwJbG9jYWxob3N0MB4X -DTIzMDkwNDE1MDUxN1oXDTMzMDkwMTE1MDUxN1owWTELMAkGA1UEBhMCdWExDTAL -BgNVBAgMBGt5aXYxDTALBgNVBAcMBGt5aXYxCzAJBgNVBAoMAnRiMQswCQYDVQQL -DAJ0YjESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEAsaxpM/OLfx0jNVHAiD4dmJ4t5vZQNINtaI/GgnXNv9iM3aDvbYr/ -Cp8mJgkOvI9BZqDEcLscBv+H4mxCqS2IFBqJbtkYHExBgg7V13NRtJwqsWVz0rBG -V1SFou2JSzPkQ6IDxJI+AUW6DfsbCs3o6VRPbriMfY06rNagerG1osaD9yn/EsH9 -BTdALravcvU8mxOghzWH54EzwDUA0mKUetgvgfqkzjDlqzXFnOsIinnDi0Ia3idF -RaBVs7bKokl0Zp1mdtvsEpG4lcmhDtNIogcmeH2LW2zEneHuDX2BGsN9CCwEGj3k -cftuxEck2mQVDoDgX1IpIUGBhIaAixj+UXh8RNSwU96GBwKbFNy/CXNj5+34DcL0 -kBh7p77rcrzm6xEGpP/3YYPoRVBRAX64x4QqzqF5oj6Sf2NGKH9RILmLWdqmq/up -6cMYyzAEOr3nIQ/7OfkdvxOt0oUzeB7QPYbTvM9bCqe3XA+JH1pnLhgySPxdTeVe -nUExge8WPJmdTlH1McminFDiJruFRa069hbky/b5z8BjIBN5S3lC4PdEE9YmDSL1 -u26HnRVwtqC32fkDNI1PT/4p2VaB+DmIxkFXrVUnV0TiyCyxu4jIgXhUiJRHgBn9 -zwB7lEZUhMriOBGJqHa0H0TtnH8z/5GYmipeJZllmQbhI+sMyYBS/+cCAwEAAaNT -MFEwHQYDVR0OBBYEFJ+kSG8GpbHq6yMOUbHuUTHwWXbUMB8GA1UdIwQYMBaAFJ+k -SG8GpbHq6yMOUbHuUTHwWXbUMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL -BQADggIBABCt/bx7/YbvR/0PE4nIzxVovPItR+oYcInRrbwT+VWvL22Su7rf71lc -1vlil4xjdVxSEi6s5KZ69PLJKKXukt1MBCUStDK1HKPPB1SAhtD6nuvkh7YL+2im -A5gmtg3KkD2ZD2mWCHAa8K7NEMah1XiMVMo+ByFNPQExqOk2i3+5kjBrlfElsm0Q -ixM++93T62gTibOjuO8uPP0NUcIHI+RcEalc1hJjFof4gWLnIulaeuUydXw7RhzE -HzeOeZiZWrvW2mjfiANMn27TV5K0dZoh/4+YLqkKn1bdQsYVIWxWx8jZ0Nj0yzMb -Ekkodny3F+BHqkSUb3whRWDKN82valnCSJFAKZFZzueAJgCjANTNdr7S7UUIxZyi -QKll59T4O1yhawRu/cZ6TQWzV7RWdTerFfIjHMwsohDUxlkoACJebLahsBG9IHGN -Tn+P2djY6CXBctbTXhRiYqeb79/TPU0EETv7/ilNHS/tssWcKWkFdai3yMzLvxeH -YTVPMzeAWW/PnQOwYTkgeaj7SIK5bbm5n/gpWk31R5gRWhgJc9FZSa9+oZWbWeYh -3XfsuCuTH+jSs0g+jJUx/cpIVrO38r2hSuhDPugmHgM6yEnKMubhChCPCyXjO0z9 -brEMQ1T9r2sKOYuyNDhN/W9/QsTb/RO4Ug2lYlzRTdehqvimHspW +MIIBjDCCATOgAwIBAgIUd7nfNuCY22WidK3CdazyMQUzVrAwCgYIKoZIzj0EAwIw +HDEaMBgGA1UEAwwRVmFsa2V5IEVDIFJvb3QgQ0EwHhcNMjUwNTI4MDYxNTE4WhcN +MjcwNTI4MDYxNTE4WjAcMRowGAYDVQQDDBFWYWxrZXkgRUMgUm9vdCBDQTBZMBMG +ByqGSM49AgEGCCqGSM49AwEHA0IABA4DTobDPtQtjBxYR+szO6p/pen9JWYPuXww +AyYMDEvD5ue3itruuDz0td0BMvn5YO+t1d03VKFVGP1ZpB4XO7ujUzBRMB0GA1Ud +DgQWBBSwBnnqUCfMUoaq7adEANr614SLFDAfBgNVHSMEGDAWgBSwBnnqUCfMUoaq +7adEANr614SLFDAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0cAMEQCIFCp +9xuuj8Z6iSozSA/3cIugBnQqUSMw9BrS+2Qr6v69AiBz2R+DGXZAKTkOzqgxBXjv +fcdX4LrBpLCn1Pw/IDD+Ug== -----END CERTIFICATE----- From 731e8df5acfa6c7952a202a5111bef20147ab8fa Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 28 May 2025 11:08:01 +0300 Subject: [PATCH 221/335] Fix ComponentLifecycleMsg toProto --- .../java/org/thingsboard/server/common/util/ProtoUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java index 77250486de..bb2e0fb2d1 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java @@ -120,7 +120,7 @@ public class ProtoUtils { .setEntityType(toProto(msg.getEntityId().getEntityType())) .setEntityIdMSB(msg.getEntityId().getId().getMostSignificantBits()) .setEntityIdLSB(msg.getEntityId().getId().getLeastSignificantBits()) - .setEvent(TransportProtos.ComponentLifecycleEvent.forNumber(msg.getEvent().getProtoNumber())); + .setEvent(toProto(msg.getEvent())); if (msg.getProfileId() != null) { builder.setProfileIdMSB(msg.getProfileId().getId().getMostSignificantBits()); builder.setProfileIdLSB(msg.getProfileId().getId().getLeastSignificantBits()); @@ -147,7 +147,7 @@ public class ProtoUtils { var builder = ComponentLifecycleMsg.builder() .tenantId(TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB()))) .entityId(entityId) - .event(ComponentLifecycleEvent.values()[proto.getEventValue()]); + .event(fromProto(proto.getEvent())); if (!StringUtils.isEmpty(proto.getName())) { builder.name(proto.getName()); } From 34dc67cf3314b7f5489cb62fd0ce920059c9569a Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 28 May 2025 12:16:41 +0300 Subject: [PATCH 222/335] UI: Imp radar axes common mode --- .../chart/bar-chart-basic-config.component.ts | 3 +++ .../chart/latest-chart-basic-config.component.html | 11 +++++++++++ .../chart/radar-chart-basic-config.component.ts | 2 ++ .../widget/lib/chart/radar-chart-widget.models.ts | 3 +++ .../widget/lib/chart/radar-chart.models.ts | 2 ++ .../components/widget/lib/chart/radar-chart.ts | 14 +++++++++++++- .../latest-chart-widget-settings.component.html | 11 +++++++++++ .../chart/radar-chart-widget-settings.component.ts | 1 + .../src/assets/locale/locale.constant-en_US.json | 6 +++++- 9 files changed, 51 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts index ba9c41893e..2a6cb1bf51 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts @@ -29,6 +29,7 @@ import { import { LatestChartBasicConfigComponent } from '@home/components/widget/config/basic/chart/latest-chart-basic-config.component'; +import { chartFillTypes } from '@home/components/widget/lib/chart/chart.models'; @Component({ selector: 'tb-bar-chart-basic-config', @@ -77,4 +78,6 @@ export class BarChartBasicConfigComponent extends LatestChartBasicConfigComponen this.widgetConfig.config.settings.axisTickLabelFont = config.axisTickLabelFont; this.widgetConfig.config.settings.axisTickLabelColor = config.axisTickLabelColor; } + + protected readonly chartFillTypes = chartFillTypes; } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html index 1f7a02a6f9..dc0f467a16 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html @@ -501,6 +501,17 @@
widgets.radar-chart.radar-axis
+
+
+
+ {{ 'widgets.radar-chart.max-axes-scaling' | translate }} +
+ + {{ 'widgets.radar-chart.separate' | translate }} + {{ 'widgets.radar-chart.common' | translate }} + +
+
{{ 'widgets.radar-chart.axis-label' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts index d66e2fa35c..98b6cf8fbf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts @@ -75,6 +75,7 @@ export class RadarChartBasicConfigComponent extends LatestChartBasicConfigCompon latestChartWidgetConfigForm.addControl('labelColor', this.fb.control(settings.labelColor, [])); latestChartWidgetConfigForm.addControl('fillAreaSettings', this.fb.control(settings.fillAreaSettings, [])); + latestChartWidgetConfigForm.addControl('normalizeAxes', this.fb.control(settings.normalizeAxes, [])); latestChartWidgetConfigForm.addControl('axisShowLabel', this.fb.control(settings.axisShowLabel, [])); latestChartWidgetConfigForm.addControl('axisLabelFont', this.fb.control(settings.axisLabelFont, [])); latestChartWidgetConfigForm.addControl('axisShowTickLabels', this.fb.control(settings.axisShowTickLabels, [])); @@ -97,6 +98,7 @@ export class RadarChartBasicConfigComponent extends LatestChartBasicConfigCompon this.widgetConfig.config.settings.labelColor = config.labelColor; this.widgetConfig.config.settings.fillAreaSettings = config.fillAreaSettings; + this.widgetConfig.config.settings.normalizeAxes = config.normalizeAxes; this.widgetConfig.config.settings.axisShowLabel = config.axisShowLabel; this.widgetConfig.config.settings.axisLabelFont = config.axisLabelFont; this.widgetConfig.config.settings.axisShowTickLabels = config.axisShowTickLabels; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart-widget.models.ts index 019c5b86f4..ec42955b4d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart-widget.models.ts @@ -50,6 +50,7 @@ export interface RadarChartWidgetSettings extends LatestChartWidgetSettings { labelFont: Font; labelColor: string; fillAreaSettings: ChartFillSettings; + normalizeAxes: boolean; axisShowLabel: boolean; axisLabelFont: Font; axisShowTickLabels: boolean; @@ -88,6 +89,7 @@ export const radarChartWidgetDefaultSettings: RadarChartWidgetSettings = { end: 20 } }, + normalizeAxes: false, axisShowLabel: true, axisLabelFont: { family: 'Roboto', @@ -123,6 +125,7 @@ export const radarChartWidgetRadarChartSettings = (settings: RadarChartWidgetSet labelFont: settings.labelFont, labelColor: settings.labelColor, fillAreaSettings: settings.fillAreaSettings, + normalizeAxes: settings.normalizeAxes, axisShowLabel: settings.axisShowLabel, axisLabelFont: settings.axisLabelFont, axisShowTickLabels: settings.axisShowTickLabels, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.models.ts index 04008c8832..9d1dcbde47 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.models.ts @@ -56,6 +56,7 @@ export interface RadarChartSettings extends LatestChartSettings { labelFont: Font; labelColor: string; fillAreaSettings: ChartFillSettings; + normalizeAxes: boolean; axisShowLabel: boolean; axisLabelFont: Font; axisShowTickLabels: boolean; @@ -100,6 +101,7 @@ export const radarChartDefaultSettings: RadarChartSettings = { end: 20 } }, + normalizeAxes: false, axisShowLabel: true, axisLabelFont: { family: 'Roboto', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts index 27aa1597bd..0e28f198f4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts @@ -31,6 +31,7 @@ import { ComponentStyle } from '@shared/models/widget-settings.models'; import { AreaStyleOption, SeriesLabelOption } from 'echarts/types/src/util/types'; import { RadarIndicatorOption } from 'echarts/types/src/coord/radar/RadarModel'; import { DataKey } from '@shared/models/widget.models'; +import { LatestChartDataItem } from '@home/components/widget/lib/chart/latest-chart.models'; export class TbRadarChart extends TbLatestChart { @@ -141,7 +142,7 @@ export class TbRadarChart extends TbLatestChart { if (dataItem.enabled && dataItem.hasValue) { indicator.push({ name: dataItem.dataKey.label, - color: dataItem.dataKey.color + color: dataItem.dataKey.color, }); value.push(dataItem.value); } @@ -149,10 +150,21 @@ export class TbRadarChart extends TbLatestChart { if (!indicator.length) { indicator.push({}); } + if (this.settings.normalizeAxes && indicator.length > 1) { + const maxDataItem = this.findMaxDataItem(this.dataItems); + indicator.map(value => value.max = maxDataItem.value); + } this.latestChartOption.radar[0].indicator = indicator; this.latestChartOption.series[0].data[0].value = value; } + private findMaxDataItem(array: LatestChartDataItem[]) { + if (!array || array.length === 0) return null; + return array.reduce((maxObj, currentObj) => { + return currentObj.value > maxObj.value ? currentObj : maxObj; + }, array[0]); + } + protected forceRedrawOnResize(): boolean { return true; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.html index 04476e69e2..fd5403984d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.html @@ -434,6 +434,17 @@
widgets.radar-chart.radar-axis
+
+
+
+ {{ 'widgets.radar-chart.max-axes-scaling' | translate }} +
+ + {{ 'widgets.radar-chart.separate' | translate }} + {{ 'widgets.radar-chart.common' | translate }} + +
+
{{ 'widgets.radar-chart.axis-label' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/radar-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/radar-chart-widget-settings.component.ts index 5e43b62983..94f93984c3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/radar-chart-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/radar-chart-widget-settings.component.ts @@ -65,6 +65,7 @@ export class RadarChartWidgetSettingsComponent extends LatestChartWidgetSettings latestChartWidgetSettingsForm.addControl('labelColor', this.fb.control(settings.labelColor, [])); latestChartWidgetSettingsForm.addControl('fillAreaSettings', this.fb.control(settings.fillAreaSettings, [])); + latestChartWidgetSettingsForm.addControl('normalizeAxes', this.fb.control(settings.normalizeAxes, [])); latestChartWidgetSettingsForm.addControl('axisShowLabel', this.fb.control(settings.axisShowLabel, [])); latestChartWidgetSettingsForm.addControl('axisLabelFont', this.fb.control(settings.axisLabelFont, [])); latestChartWidgetSettingsForm.addControl('axisShowTickLabels', this.fb.control(settings.axisShowTickLabels, [])); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5f6f882a49..788ad21876 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -8858,7 +8858,11 @@ "radar-axis": "Radar axis", "axis-label": "Axis label", "ticks-label": "Ticks label", - "radar-chart-style": "Radar chart style" + "radar-chart-style": "Radar chart style", + "max-axes-scaling": "Max axes scaling", + "max-axes-scaling-hint": "Choose whether each radar axis has its own maximum value (Separate) or shares the highest value across all axes based on the widget dataset (Common).", + "separate": "Separate", + "common": "Common" }, "time-series-chart": { "chart": "Chart", From a5f2b42d1592e0f7a0f6f41638a54229fefdf21f Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 28 May 2025 12:23:45 +0300 Subject: [PATCH 223/335] UI: Ref --- .../config/basic/chart/bar-chart-basic-config.component.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts index 2a6cb1bf51..ba9c41893e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-basic-config.component.ts @@ -29,7 +29,6 @@ import { import { LatestChartBasicConfigComponent } from '@home/components/widget/config/basic/chart/latest-chart-basic-config.component'; -import { chartFillTypes } from '@home/components/widget/lib/chart/chart.models'; @Component({ selector: 'tb-bar-chart-basic-config', @@ -78,6 +77,4 @@ export class BarChartBasicConfigComponent extends LatestChartBasicConfigComponen this.widgetConfig.config.settings.axisTickLabelFont = config.axisTickLabelFont; this.widgetConfig.config.settings.axisTickLabelColor = config.axisTickLabelColor; } - - protected readonly chartFillTypes = chartFillTypes; } From 45dee234c8e62f25cc43da6bb0794a7bb5b76570 Mon Sep 17 00:00:00 2001 From: dshvaika Date: Wed, 28 May 2025 12:31:04 +0300 Subject: [PATCH 224/335] fixes after review + new tests added --- .../TenantProfileControllerTest.java | 38 +++++++ .../server/common/data/limit/LimitedApi.java | 8 +- ...mitedApiEntry.java => RateLimitEntry.java} | 6 +- ...LimitedApiUtil.java => RateLimitUtil.java} | 18 ++-- .../DefaultTenantProfileConfiguration.java | 56 +++++----- .../common/data/limit/LimitedApiTest.java | 102 ++++++++++++++++++ ...piUtilTest.java => RateLimitUtilTest.java} | 16 +-- .../server/common/msg/tools/TbRateLimits.java | 8 +- .../CassandraBufferedRateReadExecutor.java | 9 +- .../CassandraBufferedRateWriteExecutor.java | 9 +- .../dao/service/RateLimitValidator.java | 4 +- .../util/AbstractBufferedRateExecutor.java | 57 +++++----- 12 files changed, 227 insertions(+), 104 deletions(-) rename common/data/src/main/java/org/thingsboard/server/common/data/limit/{LimitedApiEntry.java => RateLimitEntry.java} (79%) rename common/data/src/main/java/org/thingsboard/server/common/data/limit/{LimitedApiUtil.java => RateLimitUtil.java} (84%) create mode 100644 common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiTest.java rename common/data/src/test/java/org/thingsboard/server/common/data/limit/{LimitedApiUtilTest.java => RateLimitUtilTest.java} (86%) diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java index 0daa56728b..8f83f2c186 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java @@ -36,9 +36,11 @@ import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; +import org.thingsboard.server.common.data.validation.RateLimit; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.queue.TbQueueCallback; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -314,6 +316,42 @@ public class TenantProfileControllerTest extends AbstractControllerTest { Assert.assertEquals(1, pageData.getTotalElements()); } + @Test + public void testRateLimitValidationAllFields() throws Exception { + loginSysAdmin(); + Mockito.reset(tbClusterService); + + List failedFields = new ArrayList<>(); + + for (Field field : DefaultTenantProfileConfiguration.class.getDeclaredFields()) { + RateLimit rateLimit = field.getAnnotation(RateLimit.class); + if (rateLimit == null) continue; + + String fieldName = field.getName(); + String expectedLabel = rateLimit.fieldName(); + + + TenantProfile tenantProfile = createTenantProfile("Invalid RateLimit - " + fieldName); + DefaultTenantProfileConfiguration config = (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); + + field.setAccessible(true); + field.set(config, "10:1,10:1"); // Set invalid duplicate value + + try { + doPost("/api/tenantProfile", tenantProfile) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(expectedLabel + " rate limit has duplicate 'Per seconds' configuration."))); + } catch (AssertionError e) { + failedFields.add(fieldName + " (label: " + expectedLabel + ")"); + } + } + + if (!failedFields.isEmpty()) { + throw new AssertionError("RateLimit validation failed for fields: " + String.join(", ", failedFields)); + } + testBroadcastEntityStateChangeEventNeverTenantProfile(); + } + private TenantProfile createTenantProfile(String name) { TenantProfile tenantProfile = new TenantProfile(); tenantProfile.setName(name); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java index 2a9991a1cc..a2582db9f3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import java.util.Optional; import java.util.function.Function; +@Getter public enum LimitedApi { ENTITY_EXPORT(DefaultTenantProfileConfiguration::getTenantEntityExportRateLimit, "entity version creation", true), @@ -35,11 +36,11 @@ public enum LimitedApi { CASSANDRA_WRITE_QUERIES_RULE_ENGINE(DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantRuleEngineRateLimits, "Rule Engine telemetry Cassandra write queries", true), CASSANDRA_READ_QUERIES_RULE_ENGINE(DefaultTenantProfileConfiguration::getCassandraReadQueryTenantRuleEngineRateLimits, "Rule Engine telemetry Cassandra read queries", true), CASSANDRA_READ_QUERIES_MONOLITH( - LimitedApiUtil.merge( + RateLimitUtil.merge( DefaultTenantProfileConfiguration::getCassandraReadQueryTenantCoreRateLimits, DefaultTenantProfileConfiguration::getCassandraReadQueryTenantRuleEngineRateLimits), "Telemetry read queries", true), CASSANDRA_WRITE_QUERIES_MONOLITH( - LimitedApiUtil.merge( + RateLimitUtil.merge( DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantCoreRateLimits, DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantRuleEngineRateLimits), "Telemetry write queries", true), EDGE_EVENTS(DefaultTenantProfileConfiguration::getEdgeEventRateLimits, "Edge events", true), @@ -58,11 +59,8 @@ public enum LimitedApi { CALCULATED_FIELD_DEBUG_EVENTS("calculated field debug events", true); private final Function configExtractor; - @Getter private final boolean perTenant; - @Getter private final boolean refillRateLimitIntervally; - @Getter private final String label; LimitedApi(Function configExtractor, String label, boolean perTenant) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/RateLimitEntry.java similarity index 79% rename from common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java rename to common/data/src/main/java/org/thingsboard/server/common/data/limit/RateLimitEntry.java index 16084b7af9..566ff96c75 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/RateLimitEntry.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.common.data.limit; -public record LimitedApiEntry(long capacity, long durationSeconds) { +public record RateLimitEntry(long capacity, long durationSeconds) { - public static LimitedApiEntry parse(String s) { + public static RateLimitEntry parse(String s) { String[] parts = s.split(":"); - return new LimitedApiEntry(Long.parseLong(parts[0]), Long.parseLong(parts[1])); + return new RateLimitEntry(Long.parseLong(parts[0]), Long.parseLong(parts[1])); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiUtil.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/RateLimitUtil.java similarity index 84% rename from common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiUtil.java rename to common/data/src/main/java/org/thingsboard/server/common/data/limit/RateLimitUtil.java index 9c99d065d4..f2ee164dcd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApiUtil.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/RateLimitUtil.java @@ -28,14 +28,14 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -public class LimitedApiUtil { +public class RateLimitUtil { - public static List parseConfig(String config) { + public static List parseConfig(String config) { if (config == null || config.isEmpty()) { return Collections.emptyList(); } return Arrays.stream(config.split(",")) - .map(LimitedApiEntry::parse) + .map(RateLimitEntry::parse) .toList(); } @@ -45,18 +45,18 @@ public class LimitedApiUtil { return config -> { String config1 = configExtractor1.apply(config); String config2 = configExtractor2.apply(config); - return LimitedApiUtil.mergeStrConfigs(config1, config2); // merges the configs + return RateLimitUtil.mergeStrConfigs(config1, config2); // merges the configs }; } private static String mergeStrConfigs(String firstConfig, String secondConfig) { - List all = new ArrayList<>(); + List all = new ArrayList<>(); all.addAll(parseConfig(firstConfig)); all.addAll(parseConfig(secondConfig)); Map merged = new HashMap<>(); - for (LimitedApiEntry entry : all) { + for (RateLimitEntry entry : all) { merged.merge(entry.durationSeconds(), entry.capacity(), Long::sum); } @@ -67,9 +67,9 @@ public class LimitedApiUtil { } public static boolean isValid(String configStr) { - List limitedApiEntries = parseConfig(configStr); + List limitedApiEntries = parseConfig(configStr); Set distinctDurations = new HashSet<>(); - for (LimitedApiEntry entry : limitedApiEntries) { + for (RateLimitEntry entry : limitedApiEntries) { if (!distinctDurations.add(entry.durationSeconds())) { return false; } @@ -85,7 +85,7 @@ public class LimitedApiUtil { Set distinctDurations = new HashSet<>(); return parseConfig(configStr).stream() .filter(entry -> distinctDurations.add(entry.durationSeconds())) - .map(LimitedApiEntry::toString) + .map(RateLimitEntry::toString) .collect(Collectors.joining(",")); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 00fecfe800..a4ff47c340 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -24,7 +24,7 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.TenantProfileType; -import org.thingsboard.server.common.data.limit.LimitedApiUtil; +import org.thingsboard.server.common.data.limit.RateLimitUtil; import org.thingsboard.server.common.data.validation.RateLimit; import java.io.Serial; @@ -238,41 +238,41 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura @Deprecated(forRemoval = true, since = "4.1") public void deduplicateRateLimitsConfigs() { - this.transportTenantMsgRateLimit = LimitedApiUtil.deduplicateByDuration(transportTenantMsgRateLimit); - this.transportTenantTelemetryMsgRateLimit = LimitedApiUtil.deduplicateByDuration(transportTenantTelemetryMsgRateLimit); - this.transportTenantTelemetryDataPointsRateLimit = LimitedApiUtil.deduplicateByDuration(transportTenantTelemetryDataPointsRateLimit); + this.transportTenantMsgRateLimit = RateLimitUtil.deduplicateByDuration(transportTenantMsgRateLimit); + this.transportTenantTelemetryMsgRateLimit = RateLimitUtil.deduplicateByDuration(transportTenantTelemetryMsgRateLimit); + this.transportTenantTelemetryDataPointsRateLimit = RateLimitUtil.deduplicateByDuration(transportTenantTelemetryDataPointsRateLimit); - this.transportDeviceMsgRateLimit = LimitedApiUtil.deduplicateByDuration(transportDeviceMsgRateLimit); - this.transportDeviceTelemetryMsgRateLimit = LimitedApiUtil.deduplicateByDuration(transportDeviceTelemetryMsgRateLimit); - this.transportDeviceTelemetryDataPointsRateLimit = LimitedApiUtil.deduplicateByDuration(transportDeviceTelemetryDataPointsRateLimit); + this.transportDeviceMsgRateLimit = RateLimitUtil.deduplicateByDuration(transportDeviceMsgRateLimit); + this.transportDeviceTelemetryMsgRateLimit = RateLimitUtil.deduplicateByDuration(transportDeviceTelemetryMsgRateLimit); + this.transportDeviceTelemetryDataPointsRateLimit = RateLimitUtil.deduplicateByDuration(transportDeviceTelemetryDataPointsRateLimit); - this.transportGatewayMsgRateLimit = LimitedApiUtil.deduplicateByDuration(transportGatewayMsgRateLimit); - this.transportGatewayTelemetryMsgRateLimit = LimitedApiUtil.deduplicateByDuration(transportGatewayTelemetryMsgRateLimit); - this.transportGatewayTelemetryDataPointsRateLimit = LimitedApiUtil.deduplicateByDuration(transportGatewayTelemetryDataPointsRateLimit); + this.transportGatewayMsgRateLimit = RateLimitUtil.deduplicateByDuration(transportGatewayMsgRateLimit); + this.transportGatewayTelemetryMsgRateLimit = RateLimitUtil.deduplicateByDuration(transportGatewayTelemetryMsgRateLimit); + this.transportGatewayTelemetryDataPointsRateLimit = RateLimitUtil.deduplicateByDuration(transportGatewayTelemetryDataPointsRateLimit); - this.transportGatewayDeviceMsgRateLimit = LimitedApiUtil.deduplicateByDuration(transportGatewayDeviceMsgRateLimit); - this.transportGatewayDeviceTelemetryMsgRateLimit = LimitedApiUtil.deduplicateByDuration(transportGatewayDeviceTelemetryMsgRateLimit); - this.transportGatewayDeviceTelemetryDataPointsRateLimit = LimitedApiUtil.deduplicateByDuration(transportGatewayDeviceTelemetryDataPointsRateLimit); + this.transportGatewayDeviceMsgRateLimit = RateLimitUtil.deduplicateByDuration(transportGatewayDeviceMsgRateLimit); + this.transportGatewayDeviceTelemetryMsgRateLimit = RateLimitUtil.deduplicateByDuration(transportGatewayDeviceTelemetryMsgRateLimit); + this.transportGatewayDeviceTelemetryDataPointsRateLimit = RateLimitUtil.deduplicateByDuration(transportGatewayDeviceTelemetryDataPointsRateLimit); - this.tenantEntityExportRateLimit = LimitedApiUtil.deduplicateByDuration(tenantEntityExportRateLimit); - this.tenantEntityImportRateLimit = LimitedApiUtil.deduplicateByDuration(tenantEntityImportRateLimit); - this.tenantNotificationRequestsRateLimit = LimitedApiUtil.deduplicateByDuration(tenantNotificationRequestsRateLimit); - this.tenantNotificationRequestsPerRuleRateLimit = LimitedApiUtil.deduplicateByDuration(tenantNotificationRequestsPerRuleRateLimit); + this.tenantEntityExportRateLimit = RateLimitUtil.deduplicateByDuration(tenantEntityExportRateLimit); + this.tenantEntityImportRateLimit = RateLimitUtil.deduplicateByDuration(tenantEntityImportRateLimit); + this.tenantNotificationRequestsRateLimit = RateLimitUtil.deduplicateByDuration(tenantNotificationRequestsRateLimit); + this.tenantNotificationRequestsPerRuleRateLimit = RateLimitUtil.deduplicateByDuration(tenantNotificationRequestsPerRuleRateLimit); - this.cassandraReadQueryTenantCoreRateLimits = LimitedApiUtil.deduplicateByDuration(cassandraReadQueryTenantCoreRateLimits); - this.cassandraWriteQueryTenantCoreRateLimits = LimitedApiUtil.deduplicateByDuration(cassandraWriteQueryTenantCoreRateLimits); - this.cassandraReadQueryTenantRuleEngineRateLimits = LimitedApiUtil.deduplicateByDuration(cassandraReadQueryTenantRuleEngineRateLimits); - this.cassandraWriteQueryTenantRuleEngineRateLimits = LimitedApiUtil.deduplicateByDuration(cassandraWriteQueryTenantRuleEngineRateLimits); + this.cassandraReadQueryTenantCoreRateLimits = RateLimitUtil.deduplicateByDuration(cassandraReadQueryTenantCoreRateLimits); + this.cassandraWriteQueryTenantCoreRateLimits = RateLimitUtil.deduplicateByDuration(cassandraWriteQueryTenantCoreRateLimits); + this.cassandraReadQueryTenantRuleEngineRateLimits = RateLimitUtil.deduplicateByDuration(cassandraReadQueryTenantRuleEngineRateLimits); + this.cassandraWriteQueryTenantRuleEngineRateLimits = RateLimitUtil.deduplicateByDuration(cassandraWriteQueryTenantRuleEngineRateLimits); - this.edgeEventRateLimits = LimitedApiUtil.deduplicateByDuration(edgeEventRateLimits); - this.edgeEventRateLimitsPerEdge = LimitedApiUtil.deduplicateByDuration(edgeEventRateLimitsPerEdge); - this.edgeUplinkMessagesRateLimits = LimitedApiUtil.deduplicateByDuration(edgeUplinkMessagesRateLimits); - this.edgeUplinkMessagesRateLimitsPerEdge = LimitedApiUtil.deduplicateByDuration(edgeUplinkMessagesRateLimitsPerEdge); + this.edgeEventRateLimits = RateLimitUtil.deduplicateByDuration(edgeEventRateLimits); + this.edgeEventRateLimitsPerEdge = RateLimitUtil.deduplicateByDuration(edgeEventRateLimitsPerEdge); + this.edgeUplinkMessagesRateLimits = RateLimitUtil.deduplicateByDuration(edgeUplinkMessagesRateLimits); + this.edgeUplinkMessagesRateLimitsPerEdge = RateLimitUtil.deduplicateByDuration(edgeUplinkMessagesRateLimitsPerEdge); - this.wsUpdatesPerSessionRateLimit = LimitedApiUtil.deduplicateByDuration(wsUpdatesPerSessionRateLimit); + this.wsUpdatesPerSessionRateLimit = RateLimitUtil.deduplicateByDuration(wsUpdatesPerSessionRateLimit); - this.tenantServerRestLimitsConfiguration = LimitedApiUtil.deduplicateByDuration(tenantServerRestLimitsConfiguration); - this.customerServerRestLimitsConfiguration = LimitedApiUtil.deduplicateByDuration(customerServerRestLimitsConfiguration); + this.tenantServerRestLimitsConfiguration = RateLimitUtil.deduplicateByDuration(tenantServerRestLimitsConfiguration); + this.customerServerRestLimitsConfiguration = RateLimitUtil.deduplicateByDuration(customerServerRestLimitsConfiguration); } } diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiTest.java new file mode 100644 index 0000000000..d650017fd4 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiTest.java @@ -0,0 +1,102 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.limit; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class LimitedApiTest { + + private DefaultTenantProfileConfiguration config; + + @BeforeEach + void setUp() { + config = mock(DefaultTenantProfileConfiguration.class); + } + + @Test + void testCorrectConfigExtractorsUsed() { + Map verifierMap = Map.ofEntries( + Map.entry(LimitedApi.ENTITY_EXPORT, () -> + verify(config).getTenantEntityExportRateLimit()), + Map.entry(LimitedApi.ENTITY_IMPORT, () -> + verify(config).getTenantEntityImportRateLimit()), + Map.entry(LimitedApi.NOTIFICATION_REQUESTS, () -> + verify(config).getTenantNotificationRequestsRateLimit()), + Map.entry(LimitedApi.NOTIFICATION_REQUESTS_PER_RULE, () -> + verify(config).getTenantNotificationRequestsPerRuleRateLimit()), + Map.entry(LimitedApi.REST_REQUESTS_PER_TENANT, () -> + verify(config).getTenantServerRestLimitsConfiguration()), + Map.entry(LimitedApi.REST_REQUESTS_PER_CUSTOMER, () -> + verify(config).getCustomerServerRestLimitsConfiguration()), + Map.entry(LimitedApi.WS_UPDATES_PER_SESSION, () -> + verify(config).getWsUpdatesPerSessionRateLimit()), + Map.entry(LimitedApi.CASSANDRA_WRITE_QUERIES_CORE, () -> + verify(config).getCassandraWriteQueryTenantCoreRateLimits()), + Map.entry(LimitedApi.CASSANDRA_READ_QUERIES_CORE, () -> + verify(config).getCassandraReadQueryTenantCoreRateLimits()), + Map.entry(LimitedApi.CASSANDRA_WRITE_QUERIES_RULE_ENGINE, () -> + verify(config).getCassandraWriteQueryTenantRuleEngineRateLimits()), + Map.entry(LimitedApi.CASSANDRA_READ_QUERIES_RULE_ENGINE, () -> + verify(config).getCassandraReadQueryTenantRuleEngineRateLimits()), + Map.entry(LimitedApi.CASSANDRA_READ_QUERIES_MONOLITH, () -> { + verify(config).getCassandraReadQueryTenantCoreRateLimits(); + verify(config).getCassandraReadQueryTenantRuleEngineRateLimits(); + }), + Map.entry(LimitedApi.CASSANDRA_WRITE_QUERIES_MONOLITH, () -> { + verify(config).getCassandraWriteQueryTenantCoreRateLimits(); + verify(config).getCassandraWriteQueryTenantRuleEngineRateLimits(); + }), + Map.entry(LimitedApi.EDGE_EVENTS, () -> + verify(config).getEdgeEventRateLimits()), + Map.entry(LimitedApi.EDGE_EVENTS_PER_EDGE, () -> + verify(config).getEdgeEventRateLimitsPerEdge()), + Map.entry(LimitedApi.EDGE_UPLINK_MESSAGES, () -> + verify(config).getEdgeUplinkMessagesRateLimits()), + Map.entry(LimitedApi.EDGE_UPLINK_MESSAGES_PER_EDGE, () -> + verify(config).getEdgeUplinkMessagesRateLimitsPerEdge()) + ); + + Set expected = verifierMap.keySet(); + Set actual = Arrays.stream(LimitedApi.values()) + .filter(api -> api.getConfigExtractor() != null) + .collect(Collectors.toSet()); + + assertThat(expected) + .as("Verifier map should cover all LimitedApis with extractors") + .containsExactlyInAnyOrderElementsOf(actual); + + for (Map.Entry entry : verifierMap.entrySet()) { + LimitedApi api = entry.getKey(); + api.getLimitConfig(config); + entry.getValue().run(); + clearInvocations(config); + } + } + + +} \ No newline at end of file diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiUtilTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/limit/RateLimitUtilTest.java similarity index 86% rename from common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiUtilTest.java rename to common/data/src/test/java/org/thingsboard/server/common/data/limit/RateLimitUtilTest.java index c275df790c..a410785126 100644 --- a/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiUtilTest.java +++ b/common/data/src/test/java/org/thingsboard/server/common/data/limit/RateLimitUtilTest.java @@ -24,12 +24,12 @@ import java.util.function.Function; import static org.assertj.core.api.Assertions.assertThat; -class LimitedApiUtilTest { +class RateLimitUtilTest { @Test @DisplayName("LimitedApiUtil should parse single entry correctly") void testParseSingleEntry() { - List entries = LimitedApiUtil.parseConfig("100:60"); + List entries = RateLimitUtil.parseConfig("100:60"); assertThat(entries).hasSize(1); assertThat(entries.get(0).capacity()).isEqualTo(100); @@ -39,7 +39,7 @@ class LimitedApiUtilTest { @Test @DisplayName("LimitedApiUtil should parse multiple entries correctly") void testParseMultipleEntries() { - List entries = LimitedApiUtil.parseConfig("100:60,200:30"); + List entries = RateLimitUtil.parseConfig("100:60,200:30"); assertThat(entries).hasSize(2); assertThat(entries.get(0).capacity()).isEqualTo(100); @@ -51,8 +51,8 @@ class LimitedApiUtilTest { @Test @DisplayName("LimitedApiUtil should return empty list for null or empty config") void testParseEmptyConfig() { - assertThat(LimitedApiUtil.parseConfig(null)).isEmpty(); - assertThat(LimitedApiUtil.parseConfig("")).isEmpty(); + assertThat(RateLimitUtil.parseConfig(null)).isEmpty(); + assertThat(RateLimitUtil.parseConfig("")).isEmpty(); } @Test @@ -64,7 +64,7 @@ class LimitedApiUtilTest { // Fake config instance (not used directly in lambda logic) DefaultTenantProfileConfiguration config = new DefaultTenantProfileConfiguration(); - String result = LimitedApiUtil.merge(extractor1, extractor2).apply(config); + String result = RateLimitUtil.merge(extractor1, extractor2).apply(config); // Should be: 300:60 (100+200), 50:30, 25:10 assertThat(result).isEqualTo("25:10,50:30,300:60"); @@ -78,7 +78,7 @@ class LimitedApiUtilTest { // Fake config instance (not used directly in lambda logic) DefaultTenantProfileConfiguration config = new DefaultTenantProfileConfiguration(); - String result = LimitedApiUtil.merge(extractor1, extractor2).apply(config); + String result = RateLimitUtil.merge(extractor1, extractor2).apply(config); assertThat(result).isEqualTo("100:60"); } @@ -91,7 +91,7 @@ class LimitedApiUtilTest { // Fake config instance (not used directly in lambda logic) DefaultTenantProfileConfiguration config = new DefaultTenantProfileConfiguration(); - String result = LimitedApiUtil.merge(extractor1, extractor2).apply(config); + String result = RateLimitUtil.merge(extractor1, extractor2).apply(config); assertThat(result).isEqualTo("200:10,100:60"); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java b/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java index c65c90706a..182cb21ab8 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimits.java @@ -21,8 +21,8 @@ import io.github.bucket4j.Bucket; import io.github.bucket4j.local.LocalBucket; import io.github.bucket4j.local.LocalBucketBuilder; import lombok.Getter; -import org.thingsboard.server.common.data.limit.LimitedApiEntry; -import org.thingsboard.server.common.data.limit.LimitedApiUtil; +import org.thingsboard.server.common.data.limit.RateLimitEntry; +import org.thingsboard.server.common.data.limit.RateLimitUtil; import java.time.Duration; import java.util.List; @@ -41,12 +41,12 @@ public class TbRateLimits { } public TbRateLimits(String limitsConfiguration, boolean refillIntervally) { - List limitedApiEntries = LimitedApiUtil.parseConfig(limitsConfiguration); + List limitedApiEntries = RateLimitUtil.parseConfig(limitsConfiguration); if (limitedApiEntries.isEmpty()) { throw new IllegalArgumentException("Failed to parse rate limits configuration: " + limitsConfiguration); } LocalBucketBuilder localBucket = Bucket.builder(); - for (LimitedApiEntry entry : limitedApiEntries) { + for (RateLimitEntry entry : limitedApiEntries) { BandwidthBuilder.BandwidthBuilderRefillStage bandwidthBuilder = Bandwidth.builder().capacity(entry.capacity()); Bandwidth bandwidth = refillIntervally ? bandwidthBuilder.refillIntervally(entry.capacity(), Duration.ofSeconds(entry.durationSeconds())).build() : diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java index 98cfef7f7d..8698429b60 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java @@ -53,8 +53,8 @@ public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecu @Autowired EntityService entityService, @Autowired RateLimitService rateLimitService, @Autowired(required = false) TbServiceInfoProvider serviceInfoProvider) { - super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, statsFactory, - entityService, rateLimitService, serviceInfoProvider, printTenantNames); + super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, + BufferedRateExecutorType.READ, entityService, rateLimitService, serviceInfoProvider, statsFactory, printTenantNames); } @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}") @@ -68,11 +68,6 @@ public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecu super.stop(); } - @Override - protected BufferedRateExecutorType getBufferedRateExecutorType() { - return BufferedRateExecutorType.READ; - } - @Override protected SettableFuture create() { return SettableFuture.create(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java index 1fb4b8dea5..6eb313be96 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java @@ -53,8 +53,8 @@ public class CassandraBufferedRateWriteExecutor extends AbstractBufferedRateExec @Autowired EntityService entityService, @Autowired RateLimitService rateLimitService, @Autowired(required = false) TbServiceInfoProvider serviceInfoProvider) { - super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, statsFactory, - entityService, rateLimitService, serviceInfoProvider, printTenantNames); + super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, + BufferedRateExecutorType.WRITE, entityService, rateLimitService, serviceInfoProvider, statsFactory, printTenantNames); } @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}") @@ -68,11 +68,6 @@ public class CassandraBufferedRateWriteExecutor extends AbstractBufferedRateExec super.stop(); } - @Override - protected BufferedRateExecutorType getBufferedRateExecutorType() { - return BufferedRateExecutorType.WRITE; - } - @Override protected SettableFuture create() { return SettableFuture.create(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/RateLimitValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/RateLimitValidator.java index 0703f8b65d..bf776d4dc7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/RateLimitValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/RateLimitValidator.java @@ -18,7 +18,7 @@ package org.thingsboard.server.dao.service; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.limit.LimitedApiUtil; +import org.thingsboard.server.common.data.limit.RateLimitUtil; import org.thingsboard.server.common.data.validation.RateLimit; @Slf4j @@ -26,7 +26,7 @@ public class RateLimitValidator implements ConstraintValidator> queue; private final ExecutorService dispatcherExecutor; private final ExecutorService callbackExecutor; @@ -80,29 +81,32 @@ public abstract class AbstractBufferedRateExecutor tenantNamesCache = new HashMap<>(); + private final LimitedApi myLimitedApi; + public AbstractBufferedRateExecutor(int queueLimit, int concurrencyLimit, long maxWaitTime, int dispatcherThreads, - int callbackThreads, long pollMs, int printQueriesFreq, StatsFactory statsFactory, - EntityService entityService, RateLimitService rateLimitService, TbServiceInfoProvider serviceInfoProvider, boolean printTenantNames) { + int callbackThreads, long pollMs, int printQueriesFreq, BufferedRateExecutorType executorType, + EntityService entityService, RateLimitService rateLimitService, TbServiceInfoProvider serviceInfoProvider, + StatsFactory statsFactory, boolean printTenantNames) { this.maxWaitTime = maxWaitTime; this.pollMs = pollMs; + this.bufferName = executorType.getDisplayName(); this.concurrencyLimit = concurrencyLimit; this.printQueriesFreq = printQueriesFreq; this.queue = new LinkedBlockingDeque<>(queueLimit); - this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-" + getBufferName() + "-dispatcher")); - this.callbackExecutor = ThingsBoardExecutors.newWorkStealingPool(callbackThreads, "nosql-" + getBufferName() + "-callback"); - this.timeoutExecutor = ThingsBoardExecutors.newSingleThreadScheduledExecutor("nosql-" + getBufferName() + "-timeout"); + this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-" + bufferName + "-dispatcher")); + this.callbackExecutor = ThingsBoardExecutors.newWorkStealingPool(callbackThreads, "nosql-" + bufferName + "-callback"); + this.timeoutExecutor = ThingsBoardExecutors.newSingleThreadScheduledExecutor("nosql-" + bufferName + "-timeout"); this.stats = new BufferedRateExecutorStats(statsFactory); - String concurrencyLevelKey = StatsType.RATE_EXECUTOR.getName() + "." + CONCURRENCY_LEVEL + getBufferName(); //metric name may change with buffer name suffix + String concurrencyLevelKey = StatsType.RATE_EXECUTOR.getName() + "." + CONCURRENCY_LEVEL + bufferName; //metric name may change with buffer name suffix this.concurrencyLevel = statsFactory.createGauge(concurrencyLevelKey, new AtomicInteger(0)); this.entityService = entityService; this.rateLimitService = rateLimitService; - this.serviceInfoProvider = serviceInfoProvider; + this.myLimitedApi = resolveLimitedApi(serviceInfoProvider, executorType); this.printTenantNames = printTenantNames; for (int i = 0; i < dispatcherThreads; i++) { @@ -118,14 +122,14 @@ public abstract class AbstractBufferedRateExecutor execute(AsyncTaskContext taskCtx); - private String getBufferName() { - return getBufferedRateExecutorType().getDisplayName(); - } - - protected abstract BufferedRateExecutorType getBufferedRateExecutorType(); - private void dispatch() { - log.info("[{}] Buffered rate executor thread started", getBufferName()); + log.info("[{}] Buffered rate executor thread started", bufferName); while (!Thread.interrupted()) { int curLvl = concurrencyLevel.get(); AsyncTaskContext taskCtx = null; @@ -190,7 +185,7 @@ public abstract class AbstractBufferedRateExecutor= printQueriesFreq) { printQueriesIdx.set(0); String query = queryToString(finalTaskCtx); - log.info("[{}][{}] Cassandra query: {}", getBufferName(), taskCtx.getId(), query); + log.info("[{}][{}] Cassandra query: {}", bufferName, taskCtx.getId(), query); } } logTask("Processing", finalTaskCtx); @@ -243,7 +238,7 @@ public abstract class AbstractBufferedRateExecutor taskCtx) { @@ -319,7 +314,7 @@ public abstract class AbstractBufferedRateExecutor Date: Wed, 28 May 2025 12:38:13 +0300 Subject: [PATCH 225/335] fix typos --- .../server/common/data/limit/LimitedApiTest.java | 3 +-- .../dao/nosql/CassandraBufferedRateReadExecutor.java | 2 +- .../dao/nosql/CassandraBufferedRateWriteExecutor.java | 2 +- .../server/dao/util/AbstractBufferedRateExecutor.java | 7 +++---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiTest.java index d650017fd4..6c5e62e456 100644 --- a/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiTest.java +++ b/common/data/src/test/java/org/thingsboard/server/common/data/limit/LimitedApiTest.java @@ -98,5 +98,4 @@ class LimitedApiTest { } } - -} \ No newline at end of file +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java index 8698429b60..7befb1db62 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java @@ -54,7 +54,7 @@ public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecu @Autowired RateLimitService rateLimitService, @Autowired(required = false) TbServiceInfoProvider serviceInfoProvider) { super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, - BufferedRateExecutorType.READ, entityService, rateLimitService, serviceInfoProvider, statsFactory, printTenantNames); + BufferedRateExecutorType.READ, serviceInfoProvider, rateLimitService, statsFactory, entityService, printTenantNames); } @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}") diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java index 6eb313be96..9b7db4fae4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java @@ -54,7 +54,7 @@ public class CassandraBufferedRateWriteExecutor extends AbstractBufferedRateExec @Autowired RateLimitService rateLimitService, @Autowired(required = false) TbServiceInfoProvider serviceInfoProvider) { super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, printQueriesFreq, - BufferedRateExecutorType.WRITE, entityService, rateLimitService, serviceInfoProvider, statsFactory, printTenantNames); + BufferedRateExecutorType.WRITE, serviceInfoProvider, rateLimitService, statsFactory, entityService, printTenantNames); } @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}") diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java index 0668c1dd23..cbcf3e81ec 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java @@ -88,12 +88,12 @@ public abstract class AbstractBufferedRateExecutor(queueLimit); @@ -106,7 +106,6 @@ public abstract class AbstractBufferedRateExecutor Date: Wed, 28 May 2025 14:21:47 +0300 Subject: [PATCH 226/335] Fix dao tests init --- .../org/thingsboard/server/controller/JobController.java | 5 ----- .../org/thingsboard/server/dao/config/JpaDaoConfig.java | 2 +- .../thingsboard/server/dao/entity/BaseEntityService.java | 6 +++--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java index 55c68a461f..3f3ca5e64f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/JobController.java +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -59,7 +59,6 @@ public class JobController extends BaseController { @GetMapping("/job/{id}") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public Job getJobById(@PathVariable UUID id) throws ThingsboardException { - // todo check permissions return jobService.findJobById(getTenantId(), new JobId(id)); } @@ -80,7 +79,6 @@ public class JobController extends BaseController { @RequestParam(required = false) List entities, @RequestParam(required = false) Long startTime, @RequestParam(required = false) Long endTime) throws ThingsboardException { - // todo check permissions PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); JobFilter filter = JobFilter.builder() .types(types) @@ -95,21 +93,18 @@ public class JobController extends BaseController { @PostMapping("/job/{id}/cancel") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public void cancelJob(@PathVariable UUID id) throws ThingsboardException { - // todo check permissions jobManager.cancelJob(getTenantId(), new JobId(id)); } @PostMapping("/job/{id}/reprocess") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public void reprocessJob(@PathVariable UUID id) throws ThingsboardException { - // todo check permissions jobManager.reprocessJob(getTenantId(), new JobId(id)); } @DeleteMapping("/job/{id}") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public void deleteJob(@PathVariable UUID id) throws ThingsboardException { - // todo check permissions jobService.deleteJob(getTenantId(), new JobId(id)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/config/JpaDaoConfig.java b/dao/src/main/java/org/thingsboard/server/dao/config/JpaDaoConfig.java index 164e4cdb3c..963a114e8f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/config/JpaDaoConfig.java +++ b/dao/src/main/java/org/thingsboard/server/dao/config/JpaDaoConfig.java @@ -45,7 +45,7 @@ import java.util.Objects; @Configuration @TbAutoConfiguration -@ComponentScan({"org.thingsboard.server.dao.sql", "org.thingsboard.server.dao.attributes", "org.thingsboard.server.dao.sqlts.dictionary", "org.thingsboard.server.dao.cache", "org.thingsboard.server.cache"}) +@ComponentScan({"org.thingsboard.server.dao.sql", "org.thingsboard.server.dao.attributes", "org.thingsboard.server.dao.sqlts.dictionary", "org.thingsboard.server.dao.cache", "org.thingsboard.server.cache", "org.thingsboard.server.dao.entity"}) @EnableJpaRepositories(value = {"org.thingsboard.server.dao.sql", "org.thingsboard.server.dao.sqlts.dictionary"}, excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {EventRepository.class, AuditLogRepository.class}), bootstrapMode = BootstrapMode.LAZY) diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 2fca546fc9..5df926faf2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -103,7 +103,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe private EdqsApiService edqsApiService; @Autowired - private EdqsStatsService edqsStatsService; + private Optional edqsStatsService; @Override public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { @@ -123,7 +123,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe } else { result = entityQueryDao.countEntitiesByQuery(tenantId, customerId, query); } - edqsStatsService.reportEntityCountQuery(tenantId, query, System.nanoTime() - startNs); + edqsStatsService.ifPresent(statsService -> statsService.reportEntityCountQuery(tenantId, query, System.nanoTime() - startNs)); return result; } @@ -157,7 +157,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe } } } - edqsStatsService.reportEntityDataQuery(tenantId, query, System.nanoTime() - startNs); + edqsStatsService.ifPresent(statsService -> statsService.reportEntityDataQuery(tenantId, query, System.nanoTime() - startNs)); return result; } From 8e6e687c5df0607d63acd024f5bafc146332a451 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 28 May 2025 14:53:11 +0300 Subject: [PATCH 227/335] Refactor find jobs by filter --- .../server/dao/config/JpaDaoConfig.java | 2 +- .../server/dao/entity/BaseEntityService.java | 6 +++--- .../server/dao/job/DefaultJobService.java | 20 ++++++++++++++++++- .../server/dao/sql/job/JpaJobDao.java | 20 +------------------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/config/JpaDaoConfig.java b/dao/src/main/java/org/thingsboard/server/dao/config/JpaDaoConfig.java index 963a114e8f..164e4cdb3c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/config/JpaDaoConfig.java +++ b/dao/src/main/java/org/thingsboard/server/dao/config/JpaDaoConfig.java @@ -45,7 +45,7 @@ import java.util.Objects; @Configuration @TbAutoConfiguration -@ComponentScan({"org.thingsboard.server.dao.sql", "org.thingsboard.server.dao.attributes", "org.thingsboard.server.dao.sqlts.dictionary", "org.thingsboard.server.dao.cache", "org.thingsboard.server.cache", "org.thingsboard.server.dao.entity"}) +@ComponentScan({"org.thingsboard.server.dao.sql", "org.thingsboard.server.dao.attributes", "org.thingsboard.server.dao.sqlts.dictionary", "org.thingsboard.server.dao.cache", "org.thingsboard.server.cache"}) @EnableJpaRepositories(value = {"org.thingsboard.server.dao.sql", "org.thingsboard.server.dao.sqlts.dictionary"}, excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {EventRepository.class, AuditLogRepository.class}), bootstrapMode = BootstrapMode.LAZY) diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 5df926faf2..2fca546fc9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -103,7 +103,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe private EdqsApiService edqsApiService; @Autowired - private Optional edqsStatsService; + private EdqsStatsService edqsStatsService; @Override public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { @@ -123,7 +123,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe } else { result = entityQueryDao.countEntitiesByQuery(tenantId, customerId, query); } - edqsStatsService.ifPresent(statsService -> statsService.reportEntityCountQuery(tenantId, query, System.nanoTime() - startNs)); + edqsStatsService.reportEntityCountQuery(tenantId, query, System.nanoTime() - startNs); return result; } @@ -157,7 +157,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe } } } - edqsStatsService.ifPresent(statsService -> statsService.reportEntityDataQuery(tenantId, query, System.nanoTime() - startNs)); + edqsStatsService.reportEntityDataQuery(tenantId, query, System.nanoTime() - startNs); return result; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index 5afac21e5a..153e95a404 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; @@ -34,10 +35,14 @@ import org.thingsboard.server.common.data.job.task.TaskResult; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.service.ConstraintValidator; +import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import static org.thingsboard.server.common.data.job.JobStatus.CANCELLED; import static org.thingsboard.server.common.data.job.JobStatus.COMPLETED; @@ -52,6 +57,7 @@ import static org.thingsboard.server.common.data.job.JobStatus.RUNNING; public class DefaultJobService extends AbstractEntityService implements JobService { private final JobDao jobDao; + private final EntityService entityService; @Transactional @Override @@ -190,7 +196,19 @@ public class DefaultJobService extends AbstractEntityService implements JobServi @Override public PageData findJobsByFilter(TenantId tenantId, JobFilter filter, PageLink pageLink) { - return jobDao.findByTenantIdAndFilter(tenantId, filter, pageLink); + PageData jobs = jobDao.findByTenantIdAndFilter(tenantId, filter, pageLink); + + Set entityIds = jobs.getData().stream() + .map(Job::getEntityId) + .collect(Collectors.toSet()); + Map entityInfos = entityService.fetchEntityInfos(tenantId, null, entityIds); + jobs.getData().forEach(job -> { + EntityInfo entityInfo = entityInfos.get(job.getEntityId()); + if (entityInfo != null) { + job.setEntityName(entityInfo.getName()); + } + }); + return jobs; } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java index 339f6af033..40d5177ad6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/job/JpaJobDao.java @@ -20,7 +20,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Limit; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.JobId; @@ -33,17 +32,13 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.job.JobDao; import org.thingsboard.server.dao.model.sql.JobEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.Arrays; -import java.util.Map; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; @Component @SqlDao @@ -51,29 +46,16 @@ import java.util.stream.Collectors; public class JpaJobDao extends JpaAbstractDao implements JobDao { private final JobRepository jobRepository; - private final EntityService entityService; @Override public PageData findByTenantIdAndFilter(TenantId tenantId, JobFilter filter, PageLink pageLink) { - PageData jobs = DaoUtil.toPageData(jobRepository.findByTenantIdAndTypesAndStatusesAndEntitiesAndTimeAndSearchText(tenantId.getId(), + return DaoUtil.toPageData(jobRepository.findByTenantIdAndTypesAndStatusesAndEntitiesAndTimeAndSearchText(tenantId.getId(), CollectionsUtil.isEmpty(filter.getTypes()) ? null : filter.getTypes(), CollectionsUtil.isEmpty(filter.getStatuses()) ? null : filter.getStatuses(), CollectionsUtil.isEmpty(filter.getEntities()) ? null : filter.getEntities(), filter.getStartTime() != null ? filter.getStartTime() : 0, filter.getEndTime() != null ? filter.getEndTime() : 0, Strings.emptyToNull(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); - - Set entityIds = jobs.getData().stream() - .map(Job::getEntityId) - .collect(Collectors.toSet()); - Map entityInfos = entityService.fetchEntityInfos(tenantId, null, entityIds); - jobs.getData().forEach(job -> { - EntityInfo entityInfo = entityInfos.get(job.getEntityId()); - if (entityInfo != null) { - job.setEntityName(entityInfo.getName()); - } - }); - return jobs; } @Override From e17004ebb33dfd42fd004146fcf25fa182e2fb27 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 28 May 2025 16:26:43 +0300 Subject: [PATCH 228/335] Fix circular references --- .../service/queue/DefaultTenantRoutingInfoService.java | 10 +++++----- .../server/dao/service/validator/QueueValidator.java | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java index 381d49a8ec..2d9b737f25 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java @@ -16,7 +16,9 @@ package org.thingsboard.server.service.queue; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.exception.TenantNotFoundException; @@ -30,11 +32,9 @@ import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; @ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core' || '${service.type:null}'=='tb-rule-engine'") public class DefaultTenantRoutingInfoService implements TenantRoutingInfoService { - private final TbTenantProfileCache tenantProfileCache; - - public DefaultTenantRoutingInfoService(TbTenantProfileCache tenantProfileCache) { - this.tenantProfileCache = tenantProfileCache; - } + @Lazy + @Autowired + private TbTenantProfileCache tenantProfileCache; @Override public TenantRoutingInfo getRoutingInfo(TenantId tenantId) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java index 6216b5a222..2aa750ef99 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.service.validator; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.TenantId; @@ -34,6 +35,7 @@ public class QueueValidator extends DataValidator { @Autowired private QueueDao queueDao; + @Lazy @Autowired private TbTenantProfileCache tenantProfileCache; From eb97bd7db7d461949e861d44b53d54fd8af22b02 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 28 May 2025 17:08:09 +0300 Subject: [PATCH 229/335] Refactor EdqsState serialization --- .../server/service/edqs/DefaultEdqsService.java | 4 ++-- .../controller/EdqsEntityQueryControllerTest.java | 11 ++++++++--- .../server/common/data/edqs/EdqsState.java | 5 ++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edqs/DefaultEdqsService.java b/application/src/main/java/org/thingsboard/server/service/edqs/DefaultEdqsService.java index 92ae38f7e9..617902bd3d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edqs/DefaultEdqsService.java +++ b/application/src/main/java/org/thingsboard/server/service/edqs/DefaultEdqsService.java @@ -140,7 +140,7 @@ public class DefaultEdqsService implements EdqsService { .filter(serviceInfo -> serviceInfo.getServiceTypesList().contains(ServiceType.EDQS.name())) .filter(ServiceInfo::getReady) .toList(); - boolean changed = state.setEdqsReady(!readyEdqsServers.isEmpty()); + boolean changed = state.updateEdqsReady(!readyEdqsServers.isEmpty()); if (changed) { broadcastEdqsReady(state.getEdqsReady()); } @@ -236,7 +236,7 @@ public class DefaultEdqsService implements EdqsService { } private void onEdqsReady(boolean ready) { - state.setEdqsReady(ready); + state.updateEdqsReady(ready); checkState(); } diff --git a/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java index 1bea463159..89bbe5e499 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdqsEntityQueryControllerTest.java @@ -77,8 +77,9 @@ public class EdqsEntityQueryControllerTest extends EntityQueryControllerTest { } @Test - public void testEdqsState() { - assertThat(edqsService.getState().getApiMode()).isEqualTo(EdqsApiMode.AUTO_ENABLED); + public void testEdqsState() throws Exception { + loginSysAdmin(); + assertThat(getEdqsState().getApiMode()).isEqualTo(EdqsApiMode.AUTO_ENABLED); // notifying EDQS is not ready: API should be auto-disabled discoveryService.setReady(false); @@ -129,8 +130,12 @@ public class EdqsEntityQueryControllerTest extends EntityQueryControllerTest { private void verifyState(ThrowingConsumer assertion) { await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { - assertThat(edqsService.getState()).satisfies(assertion); + assertThat(getEdqsState()).satisfies(assertion); }); } + private EdqsState getEdqsState() throws Exception { + return doGet("/api/edqs/state", EdqsState.class); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsState.java b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsState.java index 7c3b328022..3df7fc92fe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsState.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsState.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.edqs; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Getter; import lombok.NoArgsConstructor; @@ -32,16 +33,18 @@ public class EdqsState { @Setter private EdqsApiMode apiMode; - public boolean setEdqsReady(boolean ready) { + public boolean updateEdqsReady(boolean ready) { boolean changed = BooleanUtils.toBooleanDefaultIfNull(this.edqsReady, false) != ready; this.edqsReady = ready; return changed; } + @JsonIgnore public boolean isApiReady() { return edqsReady && syncStatus == EdqsSyncStatus.FINISHED; } + @JsonIgnore public boolean isApiEnabled() { return apiMode != null && (apiMode == EdqsApiMode.ENABLED || apiMode == EdqsApiMode.AUTO_ENABLED); } From bb14eda16c7f395c94b6d5931e9d52cafed42541 Mon Sep 17 00:00:00 2001 From: Ruslan Vasylkiv <87172504+rusikv@users.noreply.github.com> Date: Wed, 28 May 2025 17:25:17 +0300 Subject: [PATCH 230/335] Update README.md Added link to ThingsBoard social networks. And SCADA Swimming pool use case link with GIF. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 60f172d5a8..74f023703d 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ ThingsBoard is an open-source IoT platform for data collection, processing, visu +[**LinkedIn**](https://linkedin.com/company/thingsboard) | [**YouTube**](https://www.youtube.com/thingsboard) | [**Blog**](https://thingsboard.io/blog/) + ## Documentation ThingsBoard documentation is hosted on [thingsboard.io](https://thingsboard.io/docs). @@ -14,6 +16,9 @@ ThingsBoard documentation is hosted on [thingsboard.io](https://thingsboard.io/d [**Smart energy**](https://thingsboard.io/smart-energy/) [![Smart energy](https://user-images.githubusercontent.com/8308069/152984256-eb48564a-645c-468d-912b-f554b63104a5.gif "Smart energy")](https://thingsboard.io/smart-energy/) +[**SCADA Swimming pool**](https://thingsboard.io/use-cases/scada/) +[![SCADA Swimming pool](https://github.com/user-attachments/assets/0878a2f5-d358-47c5-b295-03b4533685cf "SCADA Swimming pool")](https://thingsboard.io/use-cases/scada/) + [**Fleet tracking**](https://thingsboard.io/fleet-tracking/) [![Fleet tracking](https://user-images.githubusercontent.com/8308069/152984528-0054ed55-8b8b-4cda-ba45-02fe95a81222.gif "Fleet tracking")](https://thingsboard.io/fleet-tracking/) From ca132a559009eddb7b3b7e71794dd3895772496d Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 28 May 2025 17:58:22 +0300 Subject: [PATCH 231/335] Remove redundant docker-compose import --- .../test/java/org/thingsboard/server/msa/ContainerTestSuite.java | 1 - 1 file changed, 1 deletion(-) diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java index b410d4e6df..3a337dbb8b 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java @@ -121,7 +121,6 @@ public class ContainerTestSuite { new File(targetDir + (IS_HYBRID_MODE ? "docker-compose.hybrid-test-extras.yml" : "docker-compose.postgres-test-extras.yml")), new File(targetDir + "docker-compose.postgres.volumes.yml"), new File(targetDir + "docker-compose.kafka.yml"), - new File(targetDir + "docker-compose." + QUEUE_TYPE + ".yml"), new File(targetDir + resolveValkeyComposeFile()), new File(targetDir + resolveValkeyComposeVolumesFile()), new File(targetDir + ("docker-selenium.yml")) From 0bbec75e7550eb427f9f62b67d2001cec14cd8c4 Mon Sep 17 00:00:00 2001 From: Artem Barysh Date: Wed, 28 May 2025 18:16:54 +0300 Subject: [PATCH 232/335] Fixed channel disconnection --- .../src/main/java/org/thingsboard/mqtt/MqttClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java index 543553951d..65c195ea03 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java @@ -462,7 +462,7 @@ final class MqttClientImpl implements MqttClient { MqttMessage message = new MqttMessage(new MqttFixedHeader(MqttMessageType.DISCONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0)); ChannelFuture channelFuture = this.sendAndFlushPacket(message); eventLoop.schedule(() -> { - if (!channelFuture.isDone()) { + if (channel.isOpen()) { this.channel.close(); } }, 500, TimeUnit.MILLISECONDS); From cdf85333e99c2a6c41ee7a8169ee33a7630c0409 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Thu, 29 May 2025 10:41:05 +0300 Subject: [PATCH 233/335] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 74f023703d..bffd87ffa5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ ThingsBoard is an open-source IoT platform for data collection, processing, visu -[**LinkedIn**](https://linkedin.com/company/thingsboard) | [**YouTube**](https://www.youtube.com/thingsboard) | [**Blog**](https://thingsboard.io/blog/) ## Documentation From c53eadd9242b05546ad3a56914f4ca978780c08e Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 29 May 2025 11:06:51 +0300 Subject: [PATCH 234/335] UI: Refactoring --- .../widget/lib/chart/time-series-chart-tooltip.models.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts index f40e95276e..713e7b9373 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts @@ -130,11 +130,7 @@ export class TimeSeriesChartTooltip { this.renderer.appendChild(tooltipItemsElement, this.constructTooltipDateElement(items[0].param, interval)); } for (const item of items) { - if (this.settings.tooltipHideZeroFalse) { - if (item.param.value[1]) { - this.renderer.appendChild(tooltipItemsElement, this.constructTooltipSeriesElement(item)); - } - } else { + if (!this.settings.tooltipHideZeroFalse || item.param.value[1]) { this.renderer.appendChild(tooltipItemsElement, this.constructTooltipSeriesElement(item)); } } From 58c207ffdda0ca5b5bd6d39bc95087d0ed4e6c44 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 29 May 2025 11:35:07 +0300 Subject: [PATCH 235/335] Fix after review --- .../help/en_US/notification/resources_shortage.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md index eb4ef57f7a..74a5beb881 100644 --- a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md +++ b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md @@ -1,4 +1,4 @@ -#### Resource usage shortage notification templatization +#### Resources shortage notification templatization

@@ -9,16 +9,14 @@ See the available types and parameters below: Available template parameters: -* `cpuThreshold` - the CPU shortage threshold; -* `ramThreshold` - the RAM shortage threshold; -* `storageThreshold` - the Storage shortage threshold; +* `resource` - the shortage threshold; -Parameter names must be wrapped using `${...}`. For example: `${entityType}`. +Parameter names must be wrapped using `${...}`. For example: `${resource}`. You may also modify the value of the parameter with one of the suffixes: -* `upperCase`, for example - `${entityType:upperCase}` -* `lowerCase`, for example - `${entityType:lowerCase}` -* `capitalize`, for example - `${entityType:capitalize}` +* `upperCase`, for example - `${resource:upperCase}` +* `lowerCase`, for example - `${resource:lowerCase}` +* `capitalize`, for example - `${resource:capitalize}`
From a6997cb4a1e61ebae64ab05a63d8dff4b6f0dded Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 23 May 2025 11:13:18 +0300 Subject: [PATCH 236/335] Docker file improvements to support installation without mounting the configuration folder --- msa/tb-node/docker/Dockerfile | 4 ++- msa/tb-node/docker/logback.xml | 38 +++++++++++++++++++++++++++++ msa/tb-node/docker/start-tb-node.sh | 18 ++++++++------ 3 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 msa/tb-node/docker/logback.xml diff --git a/msa/tb-node/docker/Dockerfile b/msa/tb-node/docker/Dockerfile index dfca56acc7..013a37ef9c 100644 --- a/msa/tb-node/docker/Dockerfile +++ b/msa/tb-node/docker/Dockerfile @@ -16,12 +16,14 @@ FROM thingsboard/openjdk17:bookworm-slim -COPY start-tb-node.sh ${pkg.name}.deb /tmp/ +COPY logback.xml start-tb-node.sh ${pkg.name}.deb /tmp/ RUN chmod a+x /tmp/*.sh \ && mv /tmp/start-tb-node.sh /usr/bin && \ (yes | dpkg -i /tmp/${pkg.name}.deb) && \ rm /tmp/${pkg.name}.deb && \ + mv /tmp/logback.xml ${pkg.installFolder}/conf && \ + chown -R ${pkg.user}:${pkg.user} ${pkg.installFolder}/conf/logback.xml && \ (systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :) && \ chown -R ${pkg.user}:${pkg.user} /tmp && \ chmod 555 ${pkg.installFolder}/bin/${pkg.name}.jar diff --git a/msa/tb-node/docker/logback.xml b/msa/tb-node/docker/logback.xml new file mode 100644 index 0000000000..269cb89396 --- /dev/null +++ b/msa/tb-node/docker/logback.xml @@ -0,0 +1,38 @@ + + + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + diff --git a/msa/tb-node/docker/start-tb-node.sh b/msa/tb-node/docker/start-tb-node.sh index 7de30564c9..77221398be 100755 --- a/msa/tb-node/docker/start-tb-node.sh +++ b/msa/tb-node/docker/start-tb-node.sh @@ -15,14 +15,21 @@ # limitations under the License. # -CONF_FOLDER="/config" jarfile=${pkg.installFolder}/bin/${pkg.name}.jar configfile=${pkg.name}.conf run_user=${pkg.user} -source "${CONF_FOLDER}/${configfile}" +CONF_FOLDER="/config" +if [ -d "${CONF_FOLDER}" ]; then + LOGGING_CONFIG="${CONF_FOLDER}/logback.xml" + source "${CONF_FOLDER}/${configfile}" + export LOADER_PATH=${CONF_FOLDER},${LOADER_PATH} +else + CONF_FOLDER="/usr/share/${pkg.name}/conf" + LOGGING_CONFIG="/usr/share/${pkg.name}/conf/logback.xml" + source "${CONF_FOLDER}/${configfile}" +fi -export LOADER_PATH=/config,${LOADER_PATH} cd ${pkg.installFolder}/bin @@ -38,7 +45,6 @@ if [ "$INSTALL_TB" == "true" ]; then exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ -Dinstall.load_demo=${loadDemo} \ - -Dspring.jpa.hibernate.ddl-auto=none \ -Dinstall.upgrade=false \ -Dlogging.config=/usr/share/thingsboard/bin/install/logback.xml \ org.springframework.boot.loader.launch.PropertiesLauncher @@ -51,7 +57,6 @@ elif [ "$UPGRADE_TB" == "true" ]; then fromVersion="${FROM_VERSION// }" exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ - -Dspring.jpa.hibernate.ddl-auto=none \ -Dinstall.upgrade=true \ -Dinstall.upgrade.from_version=${fromVersion} \ -Dlogging.config=/usr/share/thingsboard/bin/install/logback.xml \ @@ -62,8 +67,7 @@ else echo "Starting '${project.name}' ..." exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardServerApplication \ - -Dspring.jpa.hibernate.ddl-auto=none \ - -Dlogging.config=/config/logback.xml \ + -Dlogging.config=${LOGGING_CONFIG} \ org.springframework.boot.loader.launch.PropertiesLauncher fi From 071baaaad312f3b748e8fbdb37d4f3203a8e0289 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 29 May 2025 11:55:53 +0300 Subject: [PATCH 237/335] fixed customer creation for NULL customer id --- .../main/java/org/thingsboard/server/edqs/repo/TenantRepo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/repo/TenantRepo.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/repo/TenantRepo.java index ffba7583c9..10bab528b9 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/repo/TenantRepo.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/repo/TenantRepo.java @@ -191,7 +191,7 @@ public class TenantRepo { getEntitySet(entityType).add(entityData); } - UUID newCustomerId = fields.getCustomerId(); + UUID newCustomerId = CustomerId.NULL_UUID.equals(fields.getCustomerId()) ? null : fields.getCustomerId(); UUID oldCustomerId = entityData.getCustomerId(); entityData.setCustomerId(newCustomerId); if (entityIdMismatch(oldCustomerId, newCustomerId)) { From 53c41707bfe47965be40d23e325bf9c5e542fac7 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 29 May 2025 12:14:34 +0300 Subject: [PATCH 238/335] UI: Fixed typo; change logics clear units in panel --- .../components/unit-settings-panel.component.ts | 8 +++++++- .../src/app/shared/models/widget-settings.models.ts | 4 +--- ui-ngx/src/assets/locale/locale.constant-en_US.json | 12 ++++++------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ui-ngx/src/app/shared/components/unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/unit-settings-panel.component.ts index f378ec59f0..9d32abd46e 100644 --- a/ui-ngx/src/app/shared/components/unit-settings-panel.component.ts +++ b/ui-ngx/src/app/shared/components/unit-settings-panel.component.ts @@ -139,7 +139,13 @@ export class UnitSettingsPanelComponent implements OnInit { } clearUnit() { - this.unitSettingsApplied.emit(null); + this.unitSettingForm.reset({ + convertUnit: false + }); + if (this.required) { + this.unitSettingForm.markAllAsTouched(); + } + this.unitSettingForm.markAsDirty(); } cancel() { diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index 193a18cf89..a9f3cd2ed8 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -907,8 +907,6 @@ export abstract class ValueFormatProcessor { export class SimpleValueFormatProcessor extends ValueFormatProcessor { - private readonly isDefinedUnit: boolean; - constructor(protected settings: ValueFormatSettings) { super(settings); this.unitSymbol = !settings.ignoreUnitSymbol && isNotEmptyStr(settings.units) ? (settings.units as string) : null; @@ -917,7 +915,7 @@ export class SimpleValueFormatProcessor extends ValueFormatProcessor { } format(value: any): string { - if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDecimals || this.isDefinedUnit || Number(value).toString() === value)) { + if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDecimals || this.unitSymbol || Number(value).toString() === value)) { return this.formatValue(Number(value)); } return value ?? ''; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index e3f67fe8f7..369706b578 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5924,7 +5924,7 @@ "energy-density": "Energy density", "force": "Force", "frequency": "Frequency", - "fuel-efficiency": "fuel efficiency", + "fuel-efficiency": "Fuel efficiency", "heat-capacity": "Heat capacity", "illuminance": "Illuminance", "inductance": "Inductance", @@ -6180,8 +6180,8 @@ "volt": "Volt", "kilovolt": "Kilovolt", "megavolt": "Megavolt", - "dbmV": "dBmV", - "dbm": "dBm", + "dbmV": "Decibel volt", + "dbm": "Decibel-milliwatts", "volt-meter": "Volt-Meter", "kilovolt-meter": "Kilovolt-Meter", "megavolt-meter": "Megavolt-Meter", @@ -6216,7 +6216,7 @@ "millimole": "Millimole", "kilomole": "Kilomole", "mole-per-cubic-meter": "Mole per Cubic Meter", - "rssi": "RSSI", + "rssi": "Received signal strength indicator", "ppm": "Parts Per Million", "ppb": "Parts Per Billion", "micrograms-per-cubic-meter": "Micrograms per Cubic Meter", @@ -6398,9 +6398,9 @@ "radian-per-second": "Radian per second", "radian-per-second-squared": "Radian per second squared", "revolutions-per-minute-per-second": "Angular acceleration", - "deg-per-second": "deg/s", + "deg-per-second": "Degrees per second", "rotation-per-minute": "Rotation per minute", - "degrees-brix": "Degrees Brix", + "degrees-brix": "Degrees brix", "katal": "Katal", "katal-per-cubic-metre": "Katal per Cubic Metre", "paris-inch": "Paris inch" From e22da2e206005960384ef6943f6ce8571bdadcab Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 29 May 2025 12:15:58 +0300 Subject: [PATCH 239/335] UI: Disable UnitConversion in state chart widgets --- .../basic/chart/time-series-chart-basic-config.component.html | 4 +++- .../chart/time-series-chart-widget-settings.component.html | 4 +++- .../chart/time-series-chart-threshold-row.component.html | 3 ++- .../common/chart/time-series-chart-threshold-row.component.ts | 4 ++++ .../chart/time-series-chart-thresholds-panel.component.html | 1 + .../chart/time-series-chart-thresholds-panel.component.ts | 4 ++++ .../chart/time-series-chart-y-axes-panel.component.html | 1 + .../common/chart/time-series-chart-y-axes-panel.component.ts | 4 ++++ .../common/chart/time-series-chart-y-axis-row.component.html | 2 +- .../common/chart/time-series-chart-y-axis-row.component.ts | 4 ++++ 10 files changed, 27 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html index 61c9f89c7c..4e635f476e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html @@ -104,6 +104,7 @@ + [yAxisIds]="yAxisIds" + [supportsUnitConversion]="widgetConfig.typeParameters.supportsUnitConversion">
widget-config.appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html index 9bbe3f6ec6..807755d3f0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html @@ -63,6 +63,7 @@ + [yAxisIds]="yAxisIds" + [supportsUnitConversion]="widgetConfig.typeParameters.supportsUnitConversion">
widgets.time-series-chart.chart-style
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html index c017d3bc0a..bc7185c476 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html @@ -89,7 +89,8 @@
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts index c57b5b2557..1d7497f70a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts @@ -106,6 +106,10 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso @coerceBoolean() hideYAxis = false; + @Input() + @coerceBoolean() + supportsUnitConversion = false; + @Output() thresholdRemoved = new EventEmitter(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html index 8c5c1dd92f..efd87a4d76 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html @@ -33,6 +33,7 @@ [formControl]="thresholdControl" [hideYAxis]="hideYAxis" [yAxisIds]="yAxisIds" + [supportsUnitConversion]="supportsUnitConversion" (thresholdRemoved)="removeThreshold($index)"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts index 4a9a01edf0..9d07ecda88 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts @@ -84,6 +84,10 @@ export class TimeSeriesChartThresholdsPanelComponent implements ControlValueAcce @coerceBoolean() hideYAxis = false; + @Input() + @coerceBoolean() + supportsUnitConversion = true; + thresholdsFormGroup: UntypedFormGroup; private propagateChange = (_val: any) => {}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.html index 533ee3fb05..c88dc9c4b9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.html @@ -39,6 +39,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts index 9c210fc28d..12ac24f9e3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts @@ -75,6 +75,10 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, @coerceBoolean() advanced = false; + @Input() + @coerceBoolean() + supportsUnitConversion = false; + @Output() axisRemoved = new EventEmitter(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html index bfa10c68d1..420e834106 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html @@ -41,7 +41,7 @@
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts index dec7148b30..ebc24ddd22 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts @@ -69,6 +69,10 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O @coerceBoolean() advanced = false; + @Input() + @coerceBoolean() + supportsUnitConversion = false; + @Output() axisRemoved = new EventEmitter(); From 0c3c8e3dcb34f1acd651dd2a73e882950a45ccd7 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 29 May 2025 12:49:25 +0300 Subject: [PATCH 240/335] UI: Refactoring --- .../app/modules/home/components/widget/lib/chart/radar-chart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts index 0e28f198f4..0cfc10d4cf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts @@ -158,7 +158,7 @@ export class TbRadarChart extends TbLatestChart { this.latestChartOption.series[0].data[0].value = value; } - private findMaxDataItem(array: LatestChartDataItem[]) { + private findMaxDataItem(array: LatestChartDataItem[]): LatestChartDataItem { if (!array || array.length === 0) return null; return array.reduce((maxObj, currentObj) => { return currentObj.value > maxObj.value ? currentObj : maxObj; From 6f31165f6027ac9c876323ba63803ca8bfed7123 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 29 May 2025 13:25:34 +0300 Subject: [PATCH 241/335] UI: Fixed entities table widgets value formatter for legacy config --- .../widget/lib/entity/entities-table-widget.component.ts | 6 +++--- .../home/components/widget/lib/table-widget.models.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts index 93d9d09b6d..f95c8c26d7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts @@ -167,7 +167,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni private defaultPageSize; private defaultSortOrder = 'entityName'; - private contentsInfo: {[key: string]: CellContentInfo} = {}; + private contentsInfo: {[key: string]: WithOptional} = {}; private stylesInfo: {[key: string]: Observable} = {}; private columnWidth: {[key: string]: string} = {}; private columnDefaultVisibility: {[key: string]: boolean} = {}; @@ -760,7 +760,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni return content$; } - private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any { + private defaultContent(key: EntityColumn, contentInfo: WithOptional, value: any): any { if (isDefined(value)) { const entityField = entityFields[key.name]; if (entityField) { @@ -768,7 +768,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni return this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss'); } } - return contentInfo.valueFormat.format(value); + return contentInfo.valueFormat?.format(value) ?? value; } else { return ''; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts index ca57a9e26a..7e91a2f043 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts @@ -107,7 +107,7 @@ export interface CellContentFunctionInfo { export interface CellContentInfo { contentFunction: Observable; - valueFormat?: ValueFormatProcessor + valueFormat: ValueFormatProcessor } export type CellStyleFunction = (...args: any[]) => any; From e112077cb0da9dbbd74dab20aa076c8997a3794a Mon Sep 17 00:00:00 2001 From: Artem Barysh Date: Thu, 29 May 2025 13:52:08 +0300 Subject: [PATCH 242/335] fixed --- .../org/thingsboard/mqtt/MqttClientImpl.java | 15 ++++++++-- .../org/thingsboard/mqtt/MqttClientTest.java | 30 +++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java index 65c195ea03..5e2c5d44cf 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java @@ -456,16 +456,25 @@ final class MqttClientImpl implements MqttClient { @Override public void disconnect() { + if (disconnected) { + return; + } + log.trace("[{}] Disconnecting from server", channel != null ? channel.id() : "UNKNOWN"); - disconnected = true; if (this.channel != null) { MqttMessage message = new MqttMessage(new MqttFixedHeader(MqttMessageType.DISCONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0)); - ChannelFuture channelFuture = this.sendAndFlushPacket(message); + + sendAndFlushPacket(message).addListener((ChannelFutureListener) future -> { + future.channel().close(); + disconnected = true; + }); + eventLoop.schedule(() -> { if (channel.isOpen()) { this.channel.close(); + disconnected = true; } - }, 500, TimeUnit.MILLISECONDS); + }, 1, TimeUnit.SECONDS); } } diff --git a/netty-mqtt/src/test/java/org/thingsboard/mqtt/MqttClientTest.java b/netty-mqtt/src/test/java/org/thingsboard/mqtt/MqttClientTest.java index 1481b354ee..a65c9fb4c7 100644 --- a/netty-mqtt/src/test/java/org/thingsboard/mqtt/MqttClientTest.java +++ b/netty-mqtt/src/test/java/org/thingsboard/mqtt/MqttClientTest.java @@ -119,6 +119,36 @@ class MqttClientTest { assertThat(client.isConnected()).isTrue(); } + @Test + void testDisconnectFromBroker() { + // GIVEN + var clientConfig = new MqttClientConfig(); + clientConfig.setOwnerId("Test[ConnectToBroker]"); + clientConfig.setClientId("connect"); + + client = MqttClient.create(clientConfig, null, handlerExecutor); + + // WHEN + Promise connectFuture = client.connect(broker.getHost(), broker.getMqttPort()); + + // THEN + assertThat(connectFuture).isNotNull(); + + Awaitility.await("waiting for client to connect") + .atMost(Duration.ofSeconds(10L)) + .until(connectFuture::isDone); + + assertThat(connectFuture.isSuccess()).isTrue(); + + // WHEN + client.disconnect(); + + // THEN + Awaitility.await("waiting for client to disconnect") + .atMost(Duration.ofSeconds(5)) + .untilAsserted(() -> assertThat(client.isConnected()).isFalse()); + } + @Test void testDisconnectDueToKeepAliveIfNoActivity() { // GIVEN From eaa0073003741144e7a78de9469645f2cf1415e0 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 29 May 2025 17:47:34 +0300 Subject: [PATCH 243/335] Cancel currently running tasks when the job is discarded --- .../KafkaCalculatedFieldStateService.java | 2 +- .../server/service/job/DummyJobProcessor.java | 1 + .../service/job/task/DummyTaskProcessor.java | 15 +++-- ...faultTbCalculatedFieldConsumerService.java | 2 +- .../queue/DefaultTbCoreConsumerService.java | 2 +- .../queue/DefaultTbEdgeConsumerService.java | 2 +- .../TbRuleEngineQueueConsumerManager.java | 1 + .../server/service/job/JobManagerTest.java | 26 +++++++++ .../ruleengine/TbRuleEngineStrategyTest.java | 2 +- .../data/job/DummyJobConfiguration.java | 1 + .../common/data/job/task/DummyTask.java | 1 + .../server/edqs/processor/EdqsProcessor.java | 2 +- .../edqs/state/KafkaEdqsStateService.java | 2 +- .../PartitionedQueueResponseTemplate.java | 2 +- .../consumer/MainQueueConsumerManager.java | 12 ++-- .../server/queue/task/TaskProcessor.java | 56 ++++++++++++++++--- 16 files changed, 103 insertions(+), 26 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java index 533b487d38..ded7adbc34 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java @@ -73,7 +73,7 @@ public class KafkaCalculatedFieldStateService extends AbstractCalculatedFieldSta .queueKey(queueKey) .topic(partitionService.getTopic(queueKey)) .pollInterval(pollInterval) - .msgPackProcessor((msgs, consumer, config) -> { + .msgPackProcessor((msgs, consumer, consumerKey, config) -> { for (TbProtoQueueMsg msg : msgs) { try { if (msg.getValue() != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java index 373bc4afee..d38cdfb6f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/DummyJobProcessor.java @@ -82,6 +82,7 @@ public class DummyJobProcessor implements JobProcessor { .processingTimeMs(configuration.getTaskProcessingTimeMs()) .errors(errors) .failAlways(failAlways) + .processingTimeoutMs(configuration.getTaskProcessingTimeoutMs()) .build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java index 5c88083146..0177e70b10 100644 --- a/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/job/task/DummyTaskProcessor.java @@ -15,13 +15,16 @@ */ package org.thingsboard.server.service.job.task; -import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.tuple.Pair; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.DummyTask; import org.thingsboard.server.common.data.job.task.DummyTaskResult; +import org.thingsboard.server.common.data.job.task.Task; import org.thingsboard.server.queue.task.TaskProcessor; -@RequiredArgsConstructor +import java.util.Map; +import java.util.concurrent.Future; + public class DummyTaskProcessor extends TaskProcessor { @Override @@ -40,8 +43,12 @@ public class DummyTaskProcessor extends TaskProcessor 0 ? task.getProcessingTimeoutMs() : 2000; + } + + public Map, Future>> getCurrentTasks() { + return currentTasks; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java index f8c398b016..8d4ab25578 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java @@ -141,7 +141,7 @@ public class DefaultTbCalculatedFieldConsumerService extends AbstractPartitionBa } } - private void processMsgs(List> msgs, TbQueueConsumer> consumer, QueueConfig config) throws Exception { + private void processMsgs(List> msgs, TbQueueConsumer> consumer, Object consumerKey, QueueConfig config) throws Exception { List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).toList(); ConcurrentMap> pendingMap = orderedMsgList.stream().collect( Collectors.toConcurrentMap(IdMsgPair::getUuid, IdMsgPair::getMsg)); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 264fa86456..f0b1a4d7d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -255,7 +255,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> msgs, TbQueueConsumer> consumer, QueueConfig config) throws Exception { + private void processMsgs(List> msgs, TbQueueConsumer> consumer, Object consumerKey, QueueConfig config) throws Exception { List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).toList(); ConcurrentMap> pendingMap = orderedMsgList.stream().collect( Collectors.toConcurrentMap(IdMsgPair::getUuid, IdMsgPair::getMsg)); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbEdgeConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbEdgeConsumerService.java index a9ae18a11f..3dd6993962 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbEdgeConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbEdgeConsumerService.java @@ -126,7 +126,7 @@ public class DefaultTbEdgeConsumerService extends AbstractConsumerService> msgs, TbQueueConsumer> consumer, QueueConfig edgeQueueConfig) throws InterruptedException { + private void processMsgs(List> msgs, TbQueueConsumer> consumer, Object consumerKey, QueueConfig edgeQueueConfig) throws InterruptedException { List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).toList(); ConcurrentMap> pendingMap = orderedMsgList.stream().collect( Collectors.toConcurrentMap(IdMsgPair::getUuid, IdMsgPair::getMsg)); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java index 3aa0d2eabd..d067be49a0 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java @@ -127,6 +127,7 @@ public class TbRuleEngineQueueConsumerManager extends MainQueueConsumerManager> msgs, TbQueueConsumer> consumer, + Object consumerKey, Queue queue) throws Exception { TbRuleEngineSubmitStrategy submitStrategy = getSubmitStrategy(queue); TbRuleEngineProcessingStrategy ackStrategy = getProcessingStrategy(queue); diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 0278e910d9..b23722afd5 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -172,6 +172,32 @@ public class JobManagerTest extends AbstractControllerTest { }); } + @Test + public void testCancelJob_whileTaskRunning() throws Exception { + JobId jobId = submitJob(DummyJobConfiguration.builder() + .successfulTasksCount(1) + .taskProcessingTimeMs(TimeUnit.HOURS.toMillis(1)) + .taskProcessingTimeoutMs(TimeUnit.HOURS.toMillis(1)) + .build()).getId(); + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(taskProcessor.getCurrentTasks()).isNotEmpty(); + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.RUNNING); + assertThat(job.getResult().getTotalCount()).isEqualTo(1); + assertThat(job.getResult().getCompletedCount()).isZero(); + }); + + cancelJob(jobId); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + Job job = findJobById(jobId); + assertThat(job.getStatus()).isEqualTo(JobStatus.CANCELLED); + assertThat(job.getResult().getTotalCount()).isEqualTo(1); + assertThat(job.getResult().getDiscardedCount()).isEqualTo(1); + assertThat(job.getResult().getFailedCount()).isZero(); + }); + } + @Test public void testCancelJob_simulateTaskProcessorRestart() throws Exception { int tasksCount = 10; diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java index 8c019029a5..1106fad5b6 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java @@ -238,7 +238,7 @@ public class TbRuleEngineStrategyTest { .map(this::toProto) .toList(); - consumerManager.processMsgs(protoMsgs, consumer, queue); + consumerManager.processMsgs(protoMsgs, consumer, queueKey, queue); processingData.forEach(data -> { verify(actorContext, times(data.attempts)).tell(argThat(msg -> diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java index a9ff9e7f4f..decdc71177 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/DummyJobConfiguration.java @@ -38,6 +38,7 @@ public class DummyJobConfiguration extends JobConfiguration { private int permanentlyFailedTasksCount; private List errors; private int retries; + private long taskProcessingTimeoutMs; private String generalError; private int submittedTasksBeforeGeneralError; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java index e0f670ad9e..66f0fc9ff9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTask.java @@ -36,6 +36,7 @@ public class DummyTask extends Task { private int number; private long processingTimeMs; + private long processingTimeoutMs; private List errors; // errors for each attempt private boolean failAlways; diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/processor/EdqsProcessor.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/processor/EdqsProcessor.java index 9fe6b2843e..510d2c3a41 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/processor/EdqsProcessor.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/processor/EdqsProcessor.java @@ -120,7 +120,7 @@ public class EdqsProcessor implements TbQueueHandler, .queueKey(new QueueKey(ServiceType.EDQS, config.getEventsTopic())) .topic(topicService.buildTopicName(config.getEventsTopic())) .pollInterval(config.getPollInterval()) - .msgPackProcessor((msgs, consumer, config) -> { + .msgPackProcessor((msgs, consumer, consumerKey, config) -> { for (TbProtoQueueMsg queueMsg : msgs) { if (consumer.isStopped()) { return; diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/state/KafkaEdqsStateService.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/state/KafkaEdqsStateService.java index 0c90235ecd..66bbb7a68a 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/state/KafkaEdqsStateService.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/state/KafkaEdqsStateService.java @@ -91,7 +91,7 @@ public class KafkaEdqsStateService implements EdqsStateService { .queueKey(new QueueKey(ServiceType.EDQS, config.getStateTopic())) .topic(topicService.buildTopicName(config.getStateTopic())) .pollInterval(config.getPollInterval()) - .msgPackProcessor((msgs, consumer, config) -> { + .msgPackProcessor((msgs, consumer, consumerKey, config) -> { for (TbProtoQueueMsg queueMsg : msgs) { try { ToEdqsMsg msg = queueMsg.getValue(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/PartitionedQueueResponseTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/PartitionedQueueResponseTemplate.java index d1e1be708f..7e913009f0 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/PartitionedQueueResponseTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/PartitionedQueueResponseTemplate.java @@ -74,7 +74,7 @@ public class PartitionedQueueResponseTemplate processRequests(requests, consumer)) + .msgPackProcessor((requests, consumer, consumerKey, config) -> processRequests(requests, consumer)) .consumerCreator((config, tpi) -> consumerCreator.apply(tpi)) .consumerExecutor(consumerExecutor) .scheduler(scheduler) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/MainQueueConsumerManager.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/MainQueueConsumerManager.java index 86e0721dd8..db5bac7170 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/MainQueueConsumerManager.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/consumer/MainQueueConsumerManager.java @@ -203,7 +203,7 @@ public class MainQueueConsumerManager consumerLoop = consumerExecutor.submit(() -> { ThingsBoardThreadFactory.updateCurrentThreadName(consumerTask.getKey().toString()); - consumerLoop(consumerTask.getConsumer()); + consumerLoop(consumerTask.getKey(), consumerTask.getConsumer()); log.info("[{}] Consumer stopped", consumerTask.getKey()); try { @@ -218,7 +218,7 @@ public class MainQueueConsumerManager consumer) { + private void consumerLoop(Object consumerKey, TbQueueConsumer consumer) { try { while (!stopped && !consumer.isStopped()) { try { @@ -226,7 +226,7 @@ public class MainQueueConsumerManager msgs, TbQueueConsumer consumer, C config) throws Exception { + protected void processMsgs(List msgs, TbQueueConsumer consumer, Object consumerKey, C config) throws Exception { log.trace("Processing {} messages", msgs.size()); - msgPackProcessor.process(msgs, consumer, config); + msgPackProcessor.process(msgs, consumer, consumerKey, config); log.trace("Processed {} messages", msgs.size()); } @@ -273,7 +273,7 @@ public class MainQueueConsumerManager { - void process(List msgs, TbQueueConsumer consumer, C config) throws Exception; + void process(List msgs, TbQueueConsumer consumer, Object consumerKey, C config) throws Exception; } public interface ConsumerWrapper { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index ef0e48e30a..62ca19a05f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.task; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; +import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -25,6 +26,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.SetCache; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.job.task.Task; import org.thingsboard.server.common.data.job.task.TaskResult; @@ -42,14 +44,18 @@ import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.settings.TasksQueueConfig; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; public abstract class TaskProcessor, R extends TaskResult> { @@ -68,6 +74,8 @@ public abstract class TaskProcessor, R extends TaskResult> { private MainQueueConsumerManager, QueueConfig> taskConsumer; private final ExecutorService taskExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName(getJobType().name().toLowerCase() + "-task-processor")); + protected final Map, Future>> currentTasks = new ConcurrentHashMap<>(); + private final SetCache discarded = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); private final SetCache failed = new SetCache<>(TimeUnit.MINUTES.toMillis(60)); @@ -104,21 +112,24 @@ public abstract class TaskProcessor, R extends TaskResult> { if (event.getEvent() == ComponentLifecycleEvent.STOPPED) { log.info("Adding job {} ({}) to discarded", entityId, tasksKey); addToDiscarded(tasksKey); + cancelRunningTasks(tasksKey); } else if (event.getEvent() == ComponentLifecycleEvent.FAILED) { log.info("Adding job {} ({}) to failed", entityId, tasksKey); failed.add(tasksKey); + cancelRunningTasks(tasksKey); } } case TENANT -> { if (event.getEvent() == ComponentLifecycleEvent.DELETED) { - deletedTenants.add(entityId.getId()); log.info("Adding tenant {} to deleted", entityId); + deletedTenants.add(entityId.getId()); + cancelRunningTasks((TenantId) entityId); } } } } - private void processMsgs(List> msgs, TbQueueConsumer> consumer, QueueConfig queueConfig) throws Exception { + private void processMsgs(List> msgs, TbQueueConsumer> consumer, Object consumerKey, QueueConfig queueConfig) throws Exception { for (TbProtoQueueMsg msg : msgs) { try { @SuppressWarnings("unchecked") @@ -135,7 +146,7 @@ public abstract class TaskProcessor, R extends TaskResult> { continue; } - processTask(task); + processTask(task, consumerKey); } catch (InterruptedException e) { throw e; } catch (Exception e) { @@ -145,30 +156,39 @@ public abstract class TaskProcessor, R extends TaskResult> { consumer.commit(); } - private void processTask(T task) throws InterruptedException { + private void processTask(T task, Object consumerKey) throws InterruptedException { task.setAttempt(task.getAttempt() + 1); log.debug("Processing task: {}", task); Future future = null; try { long startNs = System.nanoTime(); + long timeoutMs = getProcessingTimeout(task); + future = taskExecutor.submit(() -> process(task)); + currentTasks.put(consumerKey, Pair.of(task, future)); + R result; try { - result = future.get(getTaskProcessingTimeout(), TimeUnit.MILLISECONDS); + result = future.get(timeoutMs, TimeUnit.MILLISECONDS); } catch (ExecutionException e) { throw e.getCause(); } catch (TimeoutException e) { - throw new TimeoutException("Timeout after " + getTaskProcessingTimeout() + " ms"); + throw new TimeoutException("Timeout after " + timeoutMs + " ms"); } + long timingNs = System.nanoTime() - startNs; log.info("Processed task in {} ms: {}", timingNs / 1000000.0, task); reportTaskResult(task, result); } catch (InterruptedException e) { throw e; + } catch (CancellationException e) { + if (!failed.contains(task.getKey()) && !deletedTenants.contains(task.getTenantId().getId())) { + reportTaskDiscarded(task); + } } catch (Throwable e) { log.error("Failed to process task (attempt {}): {}", task.getAttempt(), task, e); if (task.getAttempt() <= task.getRetries()) { - processTask(task); + processTask(task, consumerKey); } else { reportTaskFailure(task, e); } @@ -176,11 +196,31 @@ public abstract class TaskProcessor, R extends TaskResult> { if (future != null && !future.isDone()) { future.cancel(true); } + currentTasks.remove(consumerKey); } } public abstract R process(T task) throws Exception; + private void cancelRunningTasks(String tasksKey) { + cancelRunningTasks(task -> task.getKey().equals(tasksKey)); + } + + private void cancelRunningTasks(TenantId tenantId) { + cancelRunningTasks(task -> task.getTenantId().equals(tenantId)); + } + + private void cancelRunningTasks(Predicate> filter) { + currentTasks.values().forEach(entry -> { + Task task = entry.getKey(); + Future future = entry.getValue(); + if (filter.test(task)) { + log.debug("Cancelling running task {}", task); + future.cancel(true); + } + }); + } + private void reportTaskFailure(T task, Throwable error) { R taskResult = task.toFailed(error); reportTaskResult(task, taskResult); @@ -215,7 +255,7 @@ public abstract class TaskProcessor, R extends TaskResult> { taskExecutor.shutdownNow(); } - public abstract long getTaskProcessingTimeout(); + public abstract long getProcessingTimeout(T task); public abstract JobType getJobType(); From 830f022f7d3f1b5a4439c5bec6e97781db030a06 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 29 May 2025 17:54:23 +0300 Subject: [PATCH 244/335] Add permission checks for jobs api --- .../server/controller/BaseController.java | 12 ++++++++++-- .../server/controller/JobController.java | 18 ++++++++++++------ .../service/security/permission/Resource.java | 3 ++- .../permission/TenantAdminPermissions.java | 1 + 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index e5ea69dcb5..2443037ca0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -23,8 +23,6 @@ import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.ConstraintViolation; import lombok.Getter; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.hibernate.exception.ConstraintViolationException; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -92,6 +90,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.JobId; import org.thingsboard.server.common.data.id.MobileAppBundleId; import org.thingsboard.server.common.data.id.MobileAppId; import org.thingsboard.server.common.data.id.NotificationTargetId; @@ -108,6 +107,7 @@ import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.job.Job; import org.thingsboard.server.common.data.mobile.app.MobileApp; import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; @@ -146,6 +146,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; +import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.dao.mobile.MobileAppBundleService; import org.thingsboard.server.dao.mobile.MobileAppService; import org.thingsboard.server.dao.model.ModelConstants; @@ -370,6 +371,9 @@ public abstract class BaseController { @Autowired protected NotificationTargetService notificationTargetService; + @Autowired + protected JobService jobService; + @Autowired protected CalculatedFieldService calculatedFieldService; @@ -825,6 +829,10 @@ public abstract class BaseController { return checkEntityId(notificationTargetId, notificationTargetService::findNotificationTargetById, operation); } + Job checkJobId(JobId jobId, Operation operation) throws ThingsboardException { + return checkEntityId(jobId, jobService::findJobById, operation); + } + protected I emptyId(EntityType entityType) { return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); } diff --git a/application/src/main/java/org/thingsboard/server/controller/JobController.java b/application/src/main/java/org/thingsboard/server/controller/JobController.java index 3f3ca5e64f..fbd75dfc63 100644 --- a/application/src/main/java/org/thingsboard/server/controller/JobController.java +++ b/application/src/main/java/org/thingsboard/server/controller/JobController.java @@ -35,8 +35,8 @@ import org.thingsboard.server.common.data.job.JobStatus; import org.thingsboard.server.common.data.job.JobType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.job.JobService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.permission.Operation; import java.util.List; import java.util.UUID; @@ -53,13 +53,13 @@ import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERT @Slf4j public class JobController extends BaseController { - private final JobService jobService; private final JobManager jobManager; @GetMapping("/job/{id}") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public Job getJobById(@PathVariable UUID id) throws ThingsboardException { - return jobService.findJobById(getTenantId(), new JobId(id)); + JobId jobId = new JobId(id); + return checkJobId(jobId, Operation.READ); } @GetMapping("/jobs") @@ -93,19 +93,25 @@ public class JobController extends BaseController { @PostMapping("/job/{id}/cancel") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public void cancelJob(@PathVariable UUID id) throws ThingsboardException { - jobManager.cancelJob(getTenantId(), new JobId(id)); + JobId jobId = new JobId(id); + checkJobId(jobId, Operation.WRITE); + jobManager.cancelJob(getTenantId(), jobId); } @PostMapping("/job/{id}/reprocess") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public void reprocessJob(@PathVariable UUID id) throws ThingsboardException { - jobManager.reprocessJob(getTenantId(), new JobId(id)); + JobId jobId = new JobId(id); + checkJobId(jobId, Operation.WRITE); + jobManager.reprocessJob(getTenantId(), jobId); } @DeleteMapping("/job/{id}") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") public void deleteJob(@PathVariable UUID id) throws ThingsboardException { - jobService.deleteJob(getTenantId(), new JobId(id)); + JobId jobId = new JobId(id); + checkJobId(jobId, Operation.DELETE); + jobService.deleteJob(getTenantId(), jobId); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 4cb281a719..2a92c040e3 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -50,7 +50,8 @@ public enum Resource { VERSION_CONTROL, NOTIFICATION(EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE), - MOBILE_APP_SETTINGS; + MOBILE_APP_SETTINGS, + JOB(EntityType.JOB); private final Set entityTypes; diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index 7a67d6739e..58023be34d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -55,6 +55,7 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, new PermissionChecker.GenericPermissionChecker(Operation.READ)); put(Resource.MOBILE_APP, tenantEntityPermissionChecker); put(Resource.MOBILE_APP_BUNDLE, tenantEntityPermissionChecker); + put(Resource.JOB, tenantEntityPermissionChecker); } public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { From 39ad13db0f55eda844519c404ee090be05c37be0 Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Fri, 30 May 2025 00:11:19 +0300 Subject: [PATCH 245/335] UI: Fixed in OTA package save checksum config for external URL --- .../ota-update-table-config.resolve.ts | 41 ++++++++++--------- .../pages/ota-update/ota-update.component.ts | 4 +- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts index 1122285045..cc3ca4046b 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts @@ -36,8 +36,6 @@ import { PageLink } from '@shared/models/page/page-link'; import { OtaUpdateComponent } from '@home/pages/ota-update/ota-update.component'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { FileSizePipe } from '@shared/pipe/file-size.pipe'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; @Injectable() export class OtaUpdateTableConfigResolve { @@ -47,7 +45,6 @@ export class OtaUpdateTableConfigResolve { constructor(private translate: TranslateService, private datePipe: DatePipe, - private store: Store, private otaPackageService: OtaPackageService, private router: Router, private fileSize: FileSizePipe) { @@ -68,7 +65,9 @@ export class OtaUpdateTableConfigResolve { }), new EntityTableColumn('url', 'ota-update.direct-url', '20%', entity => { return entity.url ? (entity.url.length > 20 ? `${entity.url.slice(0, 20)}…` : entity.url) : ''; - }, () => ({}), true, () => ({}), () => undefined, false, + }, () => ({ + 'text-wrap': 'nowrap' + }), true, () => ({}), () => undefined, false, { name: this.translate.instant('ota-update.copy-direct-url'), icon: 'content_paste', @@ -78,7 +77,7 @@ export class OtaUpdateTableConfigResolve { color: 'rgba(0,0,0,.87)' }, isEnabled: (otaPackage) => !!otaPackage.url, - onAction: ($event, entity) => entity.url, + onAction: (_$event, entity) => entity.url, type: CellActionDescriptorType.COPY_BUTTON }), new EntityTableColumn('fileName', 'ota-update.file-name', '20%'), @@ -86,20 +85,22 @@ export class OtaUpdateTableConfigResolve { return entity.dataSize ? this.fileSize.transform(entity.dataSize) : ''; }), new EntityTableColumn('checksum', 'ota-update.checksum', '220px', entity => { - return entity.checksum ? this.checksumText(entity) : ''; - }, () => ({}), true, () => ({}), () => undefined, false, - { - name: this.translate.instant('ota-update.copy-checksum'), - icon: 'content_paste', - style: { - padding: '4px', - 'font-size': '16px', - color: 'rgba(0,0,0,.87)' - }, - isEnabled: (otaPackage) => !!otaPackage.checksum, - onAction: ($event, entity) => entity.checksum, - type: CellActionDescriptorType.COPY_BUTTON - }) + return entity.checksum ? this.checksumText(entity) : ''; + }, () => ({ + 'text-wrap': 'nowrap' + }), true, () => ({}), () => undefined, false, + { + name: this.translate.instant('ota-update.copy-checksum'), + icon: 'content_paste', + style: { + padding: '4px', + 'font-size': '16px', + color: 'rgba(0,0,0,.87)' + }, + isEnabled: (otaPackage) => !!otaPackage.checksum, + onAction: (_$event, entity) => entity.checksum, + type: CellActionDescriptorType.COPY_BUTTON + }) ); this.config.cellActionDescriptors.push( @@ -149,7 +150,7 @@ export class OtaUpdateTableConfigResolve { } } - checksumText(entity): string { + checksumText(entity: OtaPackageInfo): string { let text = `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`; if (text.length > 20) { text = `${text.slice(0, 20)}…`; diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts index 3adf439346..401c660f52 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts @@ -185,9 +185,11 @@ export class OtaUpdateComponent extends EntityComponent implements O })); } - prepareFormValue(formValue: any): any { + prepareFormValue(formValue: OtaPackage & {generateChecksum?: boolean}): any { if (formValue.isURL) { delete formValue.file; + delete formValue.checksumAlgorithm; + delete formValue.checksum; } else { delete formValue.url; } From 49f5fc023fe26403827b759d91639a1579b89ca7 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 30 May 2025 08:54:12 +0300 Subject: [PATCH 246/335] UI: Fixed twice call cell content func for alarm table widget --- .../components/widget/lib/alarm/alarms-table-widget.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts index b1c0f38ef2..45825a0294 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts @@ -340,7 +340,6 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, public onDataUpdated() { this.alarmsDatasource.updateAlarms(); this.clearCache(); - this.ctx.detectChanges(); } public onEditModeChanged() { From f12ded5f9b21f2aa5f15860c8697cdfd13494b9f Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 30 May 2025 11:57:05 +0300 Subject: [PATCH 247/335] Fix getTenantId for AbstractOAuth2ClientMapper --- .../oauth2/AbstractOAuth2ClientMapper.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java index 522255e012..d1c485fb7d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java @@ -34,9 +34,10 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2Client; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; @@ -52,13 +53,13 @@ import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; -import java.util.List; import java.util.Optional; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @Slf4j public abstract class AbstractOAuth2ClientMapper { + private static final int DASHBOARDS_REQUEST_LIMIT = 10; @Autowired @@ -174,16 +175,16 @@ public abstract class AbstractOAuth2ClientMapper { } private TenantId getTenantId(String tenantName) throws Exception { - List tenants = tenantService.findTenants(new PageLink(1, 0, tenantName)).getData(); - Tenant tenant; - if (tenants == null || tenants.isEmpty()) { - tenant = new Tenant(); - tenant.setTitle(tenantName); - tenant = tbTenantService.save(tenant); - } else { - tenant = tenants.get(0); + PageDataIterable tenantIterator = new PageDataIterable<>(tenantService::findTenants, 1024); + for (Tenant tenant : tenantIterator) { + if (tenant.getTitle().equals(tenantName)) { + return tenant.getId(); + } } - return tenant.getTenantId(); + Tenant tenant = new Tenant(); + tenant.setTitle(tenantName); + tenant = tbTenantService.save(tenant); + return tenant.getId(); } private CustomerId getCustomerId(TenantId tenantId, String customerName) { @@ -220,4 +221,5 @@ public abstract class AbstractOAuth2ClientMapper { } while (dashboardsPage.hasNext()); return Optional.empty(); } + } From 5fb317c57d361c6670bc9e94824292ec2279c3b7 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 30 May 2025 12:24:17 +0300 Subject: [PATCH 248/335] Introduce findTenantByName --- .../oauth2/AbstractOAuth2ClientMapper.java | 26 +++++-------------- .../server/dao/tenant/TenantService.java | 3 +++ .../server/dao/sql/tenant/JpaTenantDao.java | 10 ++++--- .../dao/sql/tenant/TenantRepository.java | 5 +++- .../server/dao/tenant/TenantDao.java | 14 ++-------- .../server/dao/tenant/TenantServiceImpl.java | 6 +++++ 6 files changed, 28 insertions(+), 36 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java index d1c485fb7d..e397a6f1a2 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java @@ -20,7 +20,6 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -37,7 +36,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2Client; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; @@ -49,7 +47,6 @@ import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.entitiy.tenant.TbTenantService; import org.thingsboard.server.service.entitiy.user.TbUserService; -import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; @@ -80,18 +77,12 @@ public abstract class AbstractOAuth2ClientMapper { @Autowired private DashboardService dashboardService; - @Autowired - private InstallScripts installScripts; - @Autowired private TbUserService tbUserService; @Autowired protected TbTenantProfileCache tenantProfileCache; - @Autowired - private ApplicationEventPublisher eventPublisher; - @Value("${edges.enabled}") @Getter private boolean edgesEnabled; @@ -121,8 +112,7 @@ public abstract class AbstractOAuth2ClientMapper { } else { user.setAuthority(Authority.CUSTOMER_USER); } - TenantId tenantId = oauth2User.getTenantId() != null ? - oauth2User.getTenantId() : getTenantId(oauth2User.getTenantName()); + TenantId tenantId = oauth2User.getTenantId() != null ? oauth2User.getTenantId() : getTenantId(oauth2User.getTenantName()); user.setTenantId(tenantId); CustomerId customerId = oauth2User.getCustomerId() != null ? oauth2User.getCustomerId() : getCustomerId(user.getTenantId(), oauth2User.getCustomerName()); @@ -174,15 +164,13 @@ public abstract class AbstractOAuth2ClientMapper { } } - private TenantId getTenantId(String tenantName) throws Exception { - PageDataIterable tenantIterator = new PageDataIterable<>(tenantService::findTenants, 1024); - for (Tenant tenant : tenantIterator) { - if (tenant.getTitle().equals(tenantName)) { - return tenant.getId(); - } + private TenantId getTenantId(String name) throws Exception { + Tenant tenant = tenantService.findTenantByName(name); + if (tenant != null) { + return tenant.getId(); } - Tenant tenant = new Tenant(); - tenant.setTitle(tenantName); + tenant = new Tenant(); + tenant.setTitle(name); tenant = tbTenantService.save(tenant); return tenant.getId(); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java index a7a774b83a..d70bcef209 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TenantService.java @@ -49,7 +49,10 @@ public interface TenantService extends EntityDaoService { List findTenantIdsByTenantProfileId(TenantProfileId tenantProfileId); + Tenant findTenantByName(String name); + void deleteTenants(); PageData findTenantsIds(PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java index d031dbb8ee..c125de60a7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java @@ -38,10 +38,6 @@ import java.util.List; import java.util.UUID; import java.util.stream.Collectors; - -/** - * Created by Valerii Sosliuk on 4/30/2017. - */ @Component @SqlDao public class JpaTenantDao extends JpaAbstractDao implements TenantDao { @@ -97,8 +93,14 @@ public class JpaTenantDao extends JpaAbstractDao implement .collect(Collectors.toList()); } + @Override + public Tenant findTenantByName(TenantId tenantId, String name) { + return DaoUtil.getData(tenantRepository.findTenantByTitle(name)); + } + @Override public List findNextBatch(UUID id, int batchSize) { return tenantRepository.findNextBatch(id, Limit.of(batchSize)); } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java index bafa9a6fe6..491257b318 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java @@ -48,7 +48,7 @@ public interface TenantRepository extends JpaRepository { "LEFT JOIN TenantProfileEntity p on p.id = t.tenantProfileId " + "WHERE (:textSearch IS NULL OR ilike(t.title, CONCAT('%', :textSearch, '%')) = true)") Page findTenantInfosNextPage(@Param("textSearch") String textSearch, - Pageable pageable); + Pageable pageable); @Query("SELECT t.id FROM TenantEntity t") Page findTenantsIds(Pageable pageable); @@ -59,4 +59,7 @@ public interface TenantRepository extends JpaRepository { @Query("SELECT new org.thingsboard.server.common.data.edqs.fields.TenantFields(t.id, t.createdTime, t.title, t.version," + "t.additionalInfo, t.country, t.state, t.city, t.address, t.address2, t.zip, t.phone, t.email, t.region) FROM TenantEntity t WHERE t.id > :id ORDER BY t.id") List findNextBatch(@Param("id") UUID id, Limit limit); + + TenantEntity findTenantByTitle(String name); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java index 2a8269b15d..8ff3999c4e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java @@ -30,20 +30,8 @@ public interface TenantDao extends Dao { TenantInfo findTenantInfoById(TenantId tenantId, UUID id); - /** - * Save or update tenant object - * - * @param tenant the tenant object - * @return saved tenant object - */ Tenant save(TenantId tenantId, Tenant tenant); - /** - * Find tenants by page link. - * - * @param pageLink the page link - * @return the list of tenant objects - */ PageData findTenants(TenantId tenantId, PageLink pageLink); PageData findTenantInfos(TenantId tenantId, PageLink pageLink); @@ -52,4 +40,6 @@ public interface TenantDao extends Dao { List findTenantIdsByTenantProfileId(TenantProfileId tenantProfileId); + Tenant findTenantByName(TenantId tenantId, String name); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 02bfe5defb..b9d09e55ba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -206,6 +206,12 @@ public class TenantServiceImpl extends AbstractCachedEntityService Date: Fri, 30 May 2025 12:59:48 +0300 Subject: [PATCH 249/335] Fix findTenantByName as tenant tile is not unique --- .../org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java | 2 +- .../org/thingsboard/server/dao/sql/tenant/TenantRepository.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java index c125de60a7..fb558dab0c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java @@ -95,7 +95,7 @@ public class JpaTenantDao extends JpaAbstractDao implement @Override public Tenant findTenantByName(TenantId tenantId, String name) { - return DaoUtil.getData(tenantRepository.findTenantByTitle(name)); + return DaoUtil.getData(tenantRepository.findFirstByTitle(name)); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java index 491257b318..08859d5f63 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java @@ -60,6 +60,6 @@ public interface TenantRepository extends JpaRepository { "t.additionalInfo, t.country, t.state, t.city, t.address, t.address2, t.zip, t.phone, t.email, t.region) FROM TenantEntity t WHERE t.id > :id ORDER BY t.id") List findNextBatch(@Param("id") UUID id, Limit limit); - TenantEntity findTenantByTitle(String name); + TenantEntity findFirstByTitle(String name); } From 58ef5447eda8c5292c3e34a310278e6a9880a431 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 30 May 2025 15:20:50 +0300 Subject: [PATCH 250/335] Minor improvement --- .../system/DefaultSystemInfoService.java | 14 +++++------ .../notification/NotificationRuleApiTest.java | 24 +++++++++++-------- .../notification/DefaultNotifications.java | 2 +- .../en_US/notification/resources_shortage.md | 3 ++- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index 4cda9782c3..adc87957d3 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -95,7 +95,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener clusterSystemData = getSystemData(serviceInfoProvider.getServiceInfo()); clusterSystemData.forEach(data -> { - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(data.getCpuUsage()).build()); - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(data.getMemoryUsage()).build()); - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(data.getDiscUsage()).build()); + notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(data.getCpuUsage()).build()); + notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(data.getMemoryUsage()).build()); + notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(data.getDiscUsage()).build()); }); BasicTsKvEntry clusterDataKv = new BasicTsKvEntry(ts, new JsonDataEntry("clusterSystemData", JacksonUtil.toString(clusterSystemData))); doSave(Arrays.asList(new BasicTsKvEntry(ts, new BooleanDataEntry("clusterMode", true)), clusterDataKv)); @@ -200,17 +200,17 @@ public class DefaultSystemInfoService extends TbApplicationEventListener { long value = (long) v; tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", value))); - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(value).build()); + notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(value).build()); }); getMemoryUsage().ifPresent(v -> { long value = (long) v; tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", value))); - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(value).build()); + notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(value).build()); }); getDiscSpaceUsage().ifPresent(v -> { long value = (long) v; tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("discUsage", value))); - notificationRule.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(value).build()); + notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(value).build()); }); getCpuCount().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuCount", (long) v)))); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 518df47b34..dafcebc63c 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -94,6 +94,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; +import org.thingsboard.server.controller.TbTestWebSocketClient; import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -137,7 +138,7 @@ import static org.thingsboard.server.common.data.notification.rule.trigger.confi public class NotificationRuleApiTest extends AbstractNotificationApiTest { @SpyBean - private AlarmSubscriptionService alarmSubscriptionService;; + private AlarmSubscriptionService alarmSubscriptionService; @Autowired private DefaultSystemInfoService systemInfoService; @Autowired @@ -261,7 +262,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { assertThat(info.getAlarmStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK); }); - clients.values().forEach(wsClient -> wsClient.registerWaitForUpdate()); + clients.values().forEach(TbTestWebSocketClient::registerWaitForUpdate); alarmSubscriptionService.acknowledgeAlarm(tenantId, alarm.getId(), System.currentTimeMillis()); AlarmStatus expectedStatus = AlarmStatus.ACTIVE_ACK; AlarmSeverity expectedSeverity = AlarmSeverity.CRITICAL; @@ -645,7 +646,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { rule = saveNotificationRule(rule); NotificationRuleInfo ruleInfo = findNotificationRules().getData().get(0); - assertThat(ruleInfo.getId()).isEqualTo(ruleInfo.getId()); + assertThat(ruleInfo.getId()).isEqualTo(rule.getId()); assertThat(ruleInfo.getTemplateName()).isEqualTo(template.getName()); assertThat(ruleInfo.getDeliveryMethods()).containsOnly(deliveryMethods); } @@ -795,16 +796,17 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .cpuThreshold(1f) .storageThreshold(1f) .build(); - createNotificationRule(triggerConfig, "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); + createNotificationRule(triggerConfig, "Warning: ${resource} shortage", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId()); loginTenantAdmin(); Method method = DefaultSystemInfoService.class.getDeclaredMethod("saveCurrentMonolithSystemInfo"); method.setAccessible(true); method.invoke(systemInfoService); - TimeUnit.SECONDS.sleep(5); - - assertThat(getMyNotifications(false, 100)).size().isOne(); + await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); + Notification notification = getMyNotifications(false, 100).get(0); + assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); + assertThat(notification.getText()).isEqualTo("RAM shortage"); } @Test @@ -815,7 +817,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .cpuThreshold(1f) .storageThreshold(1f) .build(); - createNotificationRule(triggerConfig, "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); + createNotificationRule(triggerConfig, "Test", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId()); loginTenantAdmin(); assertThat(getMyNotifications(false, 100)).size().isZero(); @@ -826,8 +828,10 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .build()); TimeUnit.MILLISECONDS.sleep(300); } - TimeUnit.SECONDS.sleep(5); - assertThat(getMyNotifications(false, 100)).size().isOne(); + await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); + Notification notification = getMyNotifications(false, 100).get(0); + assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); + assertThat(notification.getText()).isEqualTo("RAM shortage"); // deduplication is 5 minute, no new message is exp notificationRuleProcessor.process(ResourcesShortageTrigger.builder() diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index 960cbfcd73..9b8b8b255e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -383,7 +383,7 @@ public class DefaultNotifications { .rule(DefaultRule.builder() .name("Resources shortage") .triggerConfig(ResourcesShortageNotificationRuleTriggerConfig.builder().cpuThreshold(0.8f).storageThreshold(0.8f).ramThreshold(0.8f).build()) - .description("Send notification to system admins when resources shortage is running low") + .description("Send notification to system admins on resource shortage") .build()) .color(RED_COLOR) .build(); diff --git a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md index 74a5beb881..9b469c0f85 100644 --- a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md +++ b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md @@ -9,7 +9,8 @@ See the available types and parameters below: Available template parameters: -* `resource` - the shortage threshold; +* `resource` - the resource name; +* `usage` - the resource usage value; Parameter names must be wrapped using `${...}`. For example: `${resource}`. You may also modify the value of the parameter with one of the suffixes: From 70f1f1834fcd749bc00be1f3d2a510cda4509e36 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 30 May 2025 15:22:03 +0300 Subject: [PATCH 251/335] Fix testNotificationsDeduplication_resourcesShortage --- .../server/service/notification/NotificationRuleApiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index dafcebc63c..cf4f7d0836 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -817,7 +817,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .cpuThreshold(1f) .storageThreshold(1f) .build(); - createNotificationRule(triggerConfig, "Test", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId()); + createNotificationRule(triggerConfig, "Warning: ${resource} shortage", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId());it a loginTenantAdmin(); assertThat(getMyNotifications(false, 100)).size().isZero(); From a9fd66f7c85c099b9f75853033220da14d5f736b Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 30 May 2025 16:34:40 +0300 Subject: [PATCH 252/335] Fix compile error --- .../server/service/notification/NotificationRuleApiTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index cf4f7d0836..282e959fcd 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -817,7 +817,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { .cpuThreshold(1f) .storageThreshold(1f) .build(); - createNotificationRule(triggerConfig, "Warning: ${resource} shortage", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId());it a + createNotificationRule(triggerConfig, "Warning: ${resource} shortage", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId()); loginTenantAdmin(); assertThat(getMyNotifications(false, 100)).size().isZero(); @@ -833,7 +833,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage"); - // deduplication is 5 minute, no new message is exp + // deduplication is 1 minute, no new message is exp notificationRuleProcessor.process(ResourcesShortageTrigger.builder() .resource(Resource.RAM) .usage(5L) From 32e9efec83562ad87a15c1b041b80101f5f0cc2c Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 30 May 2025 16:36:58 +0300 Subject: [PATCH 253/335] KafkaEdgeGrpcSession - improvements for stability during rollout restart of force restart of tb-core services --- .../service/edge/rpc/EdgeGrpcService.java | 21 +++++ .../service/edge/rpc/EdgeGrpcSession.java | 59 ++++++------ .../edge/rpc/KafkaEdgeGrpcSession.java | 90 +++++++++++-------- 3 files changed, 102 insertions(+), 68 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index 73da3694f9..9b70c9bd72 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -405,6 +405,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i EdgeId edgeId = session.getEdge().getId(); TenantId tenantId = session.getEdge().getTenantId(); + destroyKafkaSessionIfDisconnectedAndConsumerActive(tenantId, edgeId, session); + cancelScheduleEdgeEventsCheck(edgeId); if (sessions.containsKey(edgeId)) { @@ -459,16 +461,35 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i private void processEdgeEventMigrationIfNeeded(EdgeGrpcSession session, EdgeId edgeId) throws Exception { boolean isMigrationProcessed = edgeEventsMigrationProcessed.getOrDefault(edgeId, Boolean.FALSE); if (!isMigrationProcessed) { + log.info("Starting edge event migration for edge [{}]", edgeId.getId()); Boolean eventsExist = session.migrateEdgeEvents().get(); if (Boolean.TRUE.equals(eventsExist)) { + log.info("Migration still in progress for edge [{}]", edgeId.getId()); sessionNewEvents.put(edgeId, true); scheduleEdgeEventsCheck(session); } else if (Boolean.FALSE.equals(eventsExist)) { + log.info("Migration completed for edge [{}]", edgeId.getId()); edgeEventsMigrationProcessed.put(edgeId, true); } } } + private void destroyKafkaSessionIfDisconnectedAndConsumerActive(TenantId tenantId, EdgeId edgeId, EdgeGrpcSession session) { + try { + if (session instanceof KafkaEdgeGrpcSession kafkaSession) { + if (!kafkaSession.isConnected() + && kafkaSession.getConsumer() != null + && kafkaSession.getConsumer().getConsumer() != null + && !kafkaSession.getConsumer().getConsumer().isStopped()) { + sessions.remove(edgeId); + kafkaSession.destroy(); + } + } + } catch (Exception e) { + log.warn("[{}] Failed to destroy kafka session for edge [{}]", tenantId, edgeId, e); + } + } + private void cancelScheduleEdgeEventsCheck(EdgeId edgeId) { log.trace("[{}] cancelling edge event check for edge", edgeId); if (sessionEdgeEventChecks.containsKey(edgeId)) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 4a9b68fc6d..e4b78a5ad8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -42,7 +42,6 @@ import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.notification.rule.trigger.EdgeCommunicationFailureTrigger; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg; @@ -292,11 +291,11 @@ public abstract class EdgeGrpcSession implements Closeable { protected void processEdgeEvents(EdgeEventFetcher fetcher, PageLink pageLink, SettableFuture> result) { try { - log.trace("[{}] Start processing edge events, fetcher = {}, pageLink = {}", sessionId, fetcher.getClass().getSimpleName(), pageLink); + log.trace("[{}] Start processing edge events, fetcher = {}, pageLink = {}", edge.getId(), fetcher.getClass().getSimpleName(), pageLink); processHighPriorityEvents(); PageData pageData = fetcher.fetchEdgeEvents(edge.getTenantId(), edge, pageLink); if (isConnected() && !pageData.getData().isEmpty()) { - log.trace("[{}][{}][{}] event(s) are going to be processed.", tenantId, sessionId, pageData.getData().size()); + log.trace("[{}][{}][{}] event(s) are going to be processed.", tenantId, edge.getId(), pageData.getData().size()); List downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData()); Futures.addCallback(sendDownlinkMsgsPack(downlinkMsgsPack), new FutureCallback<>() { @Override @@ -323,16 +322,16 @@ public abstract class EdgeGrpcSession implements Closeable { @Override public void onFailure(Throwable t) { - log.error("[{}] Failed to send downlink msgs pack", sessionId, t); + log.error("[{}] Failed to send downlink msgs pack", edge.getId(), t); result.setException(t); } }, ctx.getGrpcCallbackExecutorService()); } else { - log.trace("[{}] no event(s) found. Stop processing edge events, fetcher = {}, pageLink = {}", sessionId, fetcher.getClass().getSimpleName(), pageLink); + log.trace("[{}] no event(s) found. Stop processing edge events, fetcher = {}, pageLink = {}", edge.getId(), fetcher.getClass().getSimpleName(), pageLink); result.set(null); } } catch (Exception e) { - log.error("[{}] Failed to fetch edge events", sessionId, e); + log.error("[{}] Failed to fetch edge events", edge.getId(), e); result.setException(e); } } @@ -459,9 +458,9 @@ public abstract class EdgeGrpcSession implements Closeable { ctx.getRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId) .edgeId(edge.getId()).customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg).error(error).build()); } - log.warn("[{}][{}] {}, attempt: {}", tenantId, sessionId, failureMsg, attempt); + log.warn("[{}][{}] {}, attempt: {}", tenantId, edge.getId(), failureMsg, attempt); } - log.trace("[{}][{}][{}] downlink msg(s) are going to be send.", tenantId, sessionId, copy.size()); + log.trace("[{}][{}][{}] downlink msg(s) are going to be send.", tenantId, edge.getId(), copy.size()); for (DownlinkMsg downlinkMsg : copy) { if (clientMaxInboundMessageSize != 0 && downlinkMsg.getSerializedSize() > clientMaxInboundMessageSize) { String error = String.format("Client max inbound message size %s is exceeded. Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE " + @@ -483,7 +482,7 @@ public abstract class EdgeGrpcSession implements Closeable { } else { String failureMsg = String.format("Failed to deliver messages: %s", copy); log.warn("[{}][{}] Failed to deliver the batch after {} attempts. Next messages are going to be discarded {}", - tenantId, sessionId, MAX_DOWNLINK_ATTEMPTS, copy); + tenantId, edge.getId(), MAX_DOWNLINK_ATTEMPTS, copy); ctx.getRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) .customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg) .error("Failed to deliver messages after " + MAX_DOWNLINK_ATTEMPTS + " attempts").build()); @@ -493,7 +492,7 @@ public abstract class EdgeGrpcSession implements Closeable { stopCurrentSendDownlinkMsgsTask(false); } } catch (Exception e) { - log.warn("[{}][{}] Failed to send downlink msgs. Error msg {}", tenantId, sessionId, e.getMessage(), e); + log.warn("[{}][{}] Failed to send downlink msgs. Error msg {}", tenantId, edge.getId(), e.getMessage(), e); stopCurrentSendDownlinkMsgsTask(true); } }; @@ -540,7 +539,7 @@ public abstract class EdgeGrpcSession implements Closeable { stopCurrentSendDownlinkMsgsTask(false); } } catch (Exception e) { - log.error("[{}][{}] Can't process downlink response message [{}]", tenantId, sessionId, msg, e); + log.error("[{}][{}] Can't process downlink response message [{}]", tenantId, edge.getId(), msg, e); } } @@ -555,12 +554,12 @@ public abstract class EdgeGrpcSession implements Closeable { while ((event = highPriorityQueue.poll()) != null) { highPriorityEvents.add(event); } - log.trace("[{}][{}] Sending high priority events {}", tenantId, sessionId, highPriorityEvents.size()); + log.trace("[{}][{}] Sending high priority events {}", tenantId, edge.getId(), highPriorityEvents.size()); List downlinkMsgsPack = convertToDownlinkMsgsPack(highPriorityEvents); sendDownlinkMsgsPack(downlinkMsgsPack).get(); } } catch (Exception e) { - log.error("[{}] Failed to process high priority events", sessionId, e); + log.error("[{}] Failed to process high priority events", edge.getId(), e); } } @@ -577,7 +576,7 @@ public abstract class EdgeGrpcSession implements Closeable { Integer.toUnsignedLong(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount()), ctx.getEdgeEventService()); log.trace("[{}][{}] starting processing edge events, previousStartTs = {}, previousStartSeqId = {}", - tenantId, sessionId, previousStartTs, previousStartSeqId); + tenantId, edge.getId(), previousStartTs, previousStartSeqId); Futures.addCallback(startProcessingEdgeEvents(fetcher), new FutureCallback<>() { @Override public void onSuccess(@Nullable Pair newStartTsAndSeqId) { @@ -586,7 +585,7 @@ public abstract class EdgeGrpcSession implements Closeable { Futures.addCallback(updateFuture, new FutureCallback<>() { @Override public void onSuccess(@Nullable List list) { - log.debug("[{}][{}] queue offset was updated [{}]", tenantId, sessionId, newStartTsAndSeqId); + log.debug("[{}][{}] queue offset was updated [{}]", tenantId, edge.getId(), newStartTsAndSeqId); boolean newEventsAvailable; if (fetcher.isSeqIdNewCycleStarted()) { newEventsAvailable = isNewEdgeEventsAvailable(); @@ -601,28 +600,28 @@ public abstract class EdgeGrpcSession implements Closeable { @Override public void onFailure(Throwable t) { - log.error("[{}][{}] Failed to update queue offset [{}]", tenantId, sessionId, newStartTsAndSeqId, t); + log.error("[{}][{}] Failed to update queue offset [{}]", tenantId, edge.getId(), newStartTsAndSeqId, t); result.setException(t); } }, ctx.getGrpcCallbackExecutorService()); } else { - log.trace("[{}][{}] newStartTsAndSeqId is null. Skipping iteration without db update", tenantId, sessionId); + log.trace("[{}][{}] newStartTsAndSeqId is null. Skipping iteration without db update", tenantId, edge.getId()); result.set(Boolean.FALSE); } } @Override public void onFailure(Throwable t) { - log.error("[{}][{}] Failed to process events", tenantId, sessionId, t); + log.error("[{}][{}] Failed to process events", tenantId, edge.getId(), t); result.setException(t); } }, ctx.getGrpcCallbackExecutorService()); } else { if (isSyncInProgress()) { - log.trace("[{}][{}] edge sync is not completed yet. Skipping iteration", tenantId, sessionId); + log.trace("[{}][{}] edge sync is not completed yet. Skipping iteration", tenantId, edge.getId()); result.set(Boolean.TRUE); } else { - log.trace("[{}][{}] edge is not connected. Skipping iteration", tenantId, sessionId); + log.trace("[{}][{}] edge is not connected. Skipping iteration", tenantId, edge.getId()); result.set(null); } } @@ -632,7 +631,7 @@ public abstract class EdgeGrpcSession implements Closeable { protected List convertToDownlinkMsgsPack(List edgeEvents) { List result = new ArrayList<>(); for (EdgeEvent edgeEvent : edgeEvents) { - log.trace("[{}][{}] converting edge event to downlink msg [{}]", tenantId, sessionId, edgeEvent); + log.trace("[{}][{}] converting edge event to downlink msg [{}]", tenantId, edge.getId(), edgeEvent); DownlinkMsg downlinkMsg = null; try { switch (edgeEvent.getAction()) { @@ -641,17 +640,17 @@ public abstract class EdgeGrpcSession implements Closeable { ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, ADDED_COMMENT, UPDATED_COMMENT, DELETED_COMMENT -> { downlinkMsg = convertEntityEventToDownlink(edgeEvent); if (downlinkMsg != null && downlinkMsg.getWidgetTypeUpdateMsgCount() > 0) { - log.trace("[{}][{}] widgetTypeUpdateMsg message processed, downlinkMsgId = {}", tenantId, sessionId, downlinkMsg.getDownlinkMsgId()); + log.trace("[{}][{}] widgetTypeUpdateMsg message processed, downlinkMsgId = {}", tenantId, edge.getId(), downlinkMsg.getDownlinkMsgId()); } else { - log.trace("[{}][{}] entity message processed [{}]", tenantId, sessionId, downlinkMsg); + log.trace("[{}][{}] entity message processed [{}]", tenantId, edge.getId(), downlinkMsg); } } case ATTRIBUTES_UPDATED, POST_ATTRIBUTES, ATTRIBUTES_DELETED, TIMESERIES_UPDATED -> downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edge, edgeEvent); - default -> log.warn("[{}][{}] Unsupported action type [{}]", tenantId, sessionId, edgeEvent.getAction()); + default -> log.warn("[{}][{}] Unsupported action type [{}]", tenantId, edge.getId(), edgeEvent.getAction()); } } catch (Exception e) { - log.trace("[{}][{}] Exception during converting edge event to downlink msg", tenantId, sessionId, e); + log.trace("[{}][{}] Exception during converting edge event to downlink msg", tenantId, edge.getId(), e); } if (downlinkMsg != null) { result.add(downlinkMsg); @@ -757,19 +756,19 @@ public abstract class EdgeGrpcSession implements Closeable { private void sendDownlinkMsg(ResponseMsg responseMsg) { if (isConnected()) { String responseMsgStr = StringUtils.truncate(responseMsg.toString(), 10000); - log.trace("[{}][{}] Sending downlink msg [{}]", tenantId, sessionId, responseMsgStr); + log.trace("[{}][{}] Sending downlink msg [{}]", tenantId, edge.getId(), responseMsgStr); downlinkMsgLock.lock(); String downlinkMsgStr = responseMsg.hasDownlinkMsg() ? String.valueOf(responseMsg.getDownlinkMsg().getDownlinkMsgId()) : responseMsgStr; try { outputStream.onNext(responseMsg); } catch (Exception e) { - log.trace("[{}][{}] Failed to send downlink message [{}]", tenantId, sessionId, downlinkMsgStr, e); + log.trace("[{}][{}] Failed to send downlink message [{}]", tenantId, edge.getId(), downlinkMsgStr, e); connected = false; sessionCloseListener.accept(edge, sessionId); } finally { downlinkMsgLock.unlock(); } - log.trace("[{}][{}] downlink msg successfully sent [{}]", tenantId, sessionId, downlinkMsgStr); + log.trace("[{}][{}] downlink msg successfully sent [{}]", tenantId, edge.getId(), downlinkMsgStr); } } @@ -909,8 +908,8 @@ public abstract class EdgeGrpcSession implements Closeable { } } catch (Exception e) { String failureMsg = String.format("Can't process uplink msg [%s] from edge", uplinkMsg); - log.trace("[{}][{}] Can't process uplink msg [{}]", edge.getTenantId(), sessionId, uplinkMsg, e); - ctx.getRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(edge.getTenantId()).edgeId(edge.getId()) + log.trace("[{}][{}] Can't process uplink msg [{}]", tenantId, edge.getId(), uplinkMsg, e); + ctx.getRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) .customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg).error(e.getMessage()).build()); return Futures.immediateFailedFuture(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/KafkaEdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/KafkaEdgeGrpcSession.java index 37687f14a9..daffe9db11 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/KafkaEdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/KafkaEdgeGrpcSession.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import io.grpc.stub.StreamObserver; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.edge.Edge; @@ -56,6 +57,7 @@ public class KafkaEdgeGrpcSession extends EdgeGrpcSession { private volatile boolean isHighPriorityProcessing; + @Getter private QueueConsumerManager> consumer; private ExecutorService consumerExecutor; @@ -72,31 +74,28 @@ public class KafkaEdgeGrpcSession extends EdgeGrpcSession { } private void processMsgs(List> msgs, TbQueueConsumer> consumer) { - log.trace("[{}][{}] starting processing edge events", tenantId, sessionId); - if (isConnected() && !isSyncInProgress() && !isHighPriorityProcessing) { - List edgeEvents = new ArrayList<>(); - for (TbProtoQueueMsg msg : msgs) { - EdgeEvent edgeEvent = ProtoUtils.fromProto(msg.getValue().getEdgeEventMsg()); - edgeEvents.add(edgeEvent); - } - List downlinkMsgsPack = convertToDownlinkMsgsPack(edgeEvents); - try { - boolean isInterrupted = sendDownlinkMsgsPack(downlinkMsgsPack).get(); - if (isInterrupted) { - log.debug("[{}][{}][{}] Send downlink messages task was interrupted", tenantId, edge.getId(), sessionId); - } else { - consumer.commit(); - } - } catch (Exception e) { - log.error("[{}] Failed to process all downlink messages", sessionId, e); - } - } else { - try { - Thread.sleep(ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval()); - } catch (InterruptedException interruptedException) { - log.trace("Failed to wait until the server has capacity to handle new requests", interruptedException); + log.trace("[{}][{}] starting processing edge events", tenantId, edge.getId()); + if (!isConnected() || isSyncInProgress() || isHighPriorityProcessing) { + log.debug("[{}][{}] edge not connected, edge sync is not completed or high priority processing in progress, " + + "connected = {}, sync in progress = {}, high priority in progress = {}. Skipping iteration", + tenantId, edge.getId(), isConnected(), isSyncInProgress(), isHighPriorityProcessing); + return; + } + List edgeEvents = new ArrayList<>(); + for (TbProtoQueueMsg msg : msgs) { + EdgeEvent edgeEvent = ProtoUtils.fromProto(msg.getValue().getEdgeEventMsg()); + edgeEvents.add(edgeEvent); + } + List downlinkMsgsPack = convertToDownlinkMsgsPack(edgeEvents); + try { + boolean isInterrupted = sendDownlinkMsgsPack(downlinkMsgsPack).get(); + if (isInterrupted) { + log.debug("[{}][{}] Send downlink messages task was interrupted", tenantId, edge.getId()); + } else { + consumer.commit(); } - log.trace("[{}][{}] edge is not connected or sync is not completed. Skipping iteration", tenantId, sessionId); + } catch (Exception e) { + log.error("[{}][{}] Failed to process downlink messages", tenantId, edge.getId(), e); } } @@ -107,18 +106,23 @@ public class KafkaEdgeGrpcSession extends EdgeGrpcSession { @Override public ListenableFuture processEdgeEvents() { - if (consumer == null) { - this.consumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("edge-event-consumer")); - this.consumer = QueueConsumerManager.>builder() - .name("TB Edge events") - .msgPackProcessor(this::processMsgs) - .pollInterval(ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval()) - .consumerCreator(() -> tbCoreQueueFactory.createEdgeEventMsgConsumer(tenantId, edge.getId())) - .consumerExecutor(consumerExecutor) - .threadPrefix("edge-events") - .build(); - consumer.subscribe(); - consumer.launch(); + if (consumer == null || (consumer.getConsumer() != null && consumer.getConsumer().isStopped())) { + try { + this.consumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("edge-event-consumer")); + this.consumer = QueueConsumerManager.>builder() + .name("TB Edge events [" + edge.getId() + "]") + .msgPackProcessor(this::processMsgs) + .pollInterval(ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval()) + .consumerCreator(() -> tbCoreQueueFactory.createEdgeEventMsgConsumer(tenantId, edge.getId())) + .consumerExecutor(consumerExecutor) + .threadPrefix("edge-events-" + edge.getId()) + .build(); + consumer.subscribe(); + consumer.launch(); + } catch (Exception e) { + destroy(); + log.warn("[{}][{}] Failed to start edge event consumer", sessionId, edge.getId(), e); + } } return Futures.immediateFuture(Boolean.FALSE); } @@ -132,8 +136,18 @@ public class KafkaEdgeGrpcSession extends EdgeGrpcSession { @Override public void destroy() { - consumer.stop(); - consumerExecutor.shutdown(); + try { + if (consumer != null) { + consumer.stop(); + } + } finally { + consumer = null; + } + try { + if (consumerExecutor != null) { + consumerExecutor.shutdown(); + } + } catch (Exception ignored) {} } @Override From 9fa71bff549e53e4ff97e07e30fbf1f603cccad5 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Mon, 2 Jun 2025 11:22:39 +0300 Subject: [PATCH 254/335] destroyKafkaSessionIfDisconnectedAndConsumerActive runs for all sessions - edge can be connected to different node and scheduled will not be invoked --- .../service/edge/rpc/EdgeGrpcService.java | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index 9b70c9bd72..d45d872aef 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -69,7 +69,9 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -193,6 +195,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i this.edgeEventProcessingExecutorService = ThingsBoardExecutors.newScheduledThreadPool(schedulerPoolSize, "edge-event-check-scheduler"); this.sendDownlinkExecutorService = ThingsBoardExecutors.newScheduledThreadPool(sendSchedulerPoolSize, "edge-send-scheduler"); this.executorService = ThingsBoardExecutors.newSingleThreadScheduledExecutor("edge-service"); + this.executorService.scheduleAtFixedRate(this::destroyKafkaSessionIfDisconnectedAndConsumerActive, 60, 60, TimeUnit.SECONDS); log.info("Edge RPC service initialized!"); } @@ -405,8 +408,6 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i EdgeId edgeId = session.getEdge().getId(); TenantId tenantId = session.getEdge().getTenantId(); - destroyKafkaSessionIfDisconnectedAndConsumerActive(tenantId, edgeId, session); - cancelScheduleEdgeEventsCheck(edgeId); if (sessions.containsKey(edgeId)) { @@ -474,22 +475,6 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i } } - private void destroyKafkaSessionIfDisconnectedAndConsumerActive(TenantId tenantId, EdgeId edgeId, EdgeGrpcSession session) { - try { - if (session instanceof KafkaEdgeGrpcSession kafkaSession) { - if (!kafkaSession.isConnected() - && kafkaSession.getConsumer() != null - && kafkaSession.getConsumer().getConsumer() != null - && !kafkaSession.getConsumer().getConsumer().isStopped()) { - sessions.remove(edgeId); - kafkaSession.destroy(); - } - } - } catch (Exception e) { - log.warn("[{}] Failed to destroy kafka session for edge [{}]", tenantId, edgeId, e); - } - } - private void cancelScheduleEdgeEventsCheck(EdgeId edgeId) { log.trace("[{}] cancelling edge event check for edge", edgeId); if (sessionEdgeEventChecks.containsKey(edgeId)) { @@ -631,4 +616,27 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i } } + private void destroyKafkaSessionIfDisconnectedAndConsumerActive() { + try { + List toRemove = new ArrayList<>(); + for (EdgeGrpcSession session : sessions.values()) { + if (session instanceof KafkaEdgeGrpcSession kafkaSession && + !kafkaSession.isConnected() && + kafkaSession.getConsumer() != null && + kafkaSession.getConsumer().getConsumer() != null && + !kafkaSession.getConsumer().getConsumer().isStopped()) { + toRemove.add(kafkaSession.getEdge().getId()); + } + } + for (EdgeId edgeId : toRemove) { + log.info("[{}] Destroying session for edge because edge is not connected", edgeId); + EdgeGrpcSession removed = sessions.remove(edgeId); + if (removed instanceof KafkaEdgeGrpcSession kafkaSession) { + kafkaSession.destroy(); + } + } + } catch (Exception e) { + log.warn("Failed to cleanup kafka sessions", e); + } + } } From 01f65f624b3a3760a09d625238f1746a1281fc1f Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 2 Jun 2025 12:28:33 +0300 Subject: [PATCH 255/335] UI: Improved rate limits text and added validation for duplicate time --- .../rate-limits-list.component.html | 36 ++++----- .../rate-limits-list.component.scss | 2 +- .../rate-limits/rate-limits-list.component.ts | 75 +++++++++++-------- .../tenant/rate-limits/rate-limits.models.ts | 18 ++--- .../assets/locale/locale.constant-ar_AE.json | 1 - .../assets/locale/locale.constant-da_DK.json | 3 +- .../assets/locale/locale.constant-de_DE.json | 3 +- .../assets/locale/locale.constant-en_US.json | 3 +- .../assets/locale/locale.constant-es_ES.json | 3 +- .../assets/locale/locale.constant-fr_FR.json | 3 +- .../assets/locale/locale.constant-it_IT.json | 3 +- .../assets/locale/locale.constant-lt_LT.json | 1 - .../assets/locale/locale.constant-nl_BE.json | 1 - .../assets/locale/locale.constant-pl_PL.json | 1 - .../assets/locale/locale.constant-zh_CN.json | 1 - .../assets/locale/locale.constant-zh_TW.json | 1 - 16 files changed, 81 insertions(+), 74 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.html index 238baaf835..93fe799f09 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits-list.component.html @@ -17,31 +17,33 @@ -->
-
- tenant-profile.rate-limits.but-less-than -
+ @if($index > 0) { +
tenant-profile.rate-limits.and-also-less-than
+ }
- + tenant-profile.rate-limits.number-of-messages - - {{ 'tenant-profile.rate-limits.number-of-messages-required' | translate }} - - - {{ 'tenant-profile.rate-limits.number-of-messages-min' | translate }} - + + @if (rateLimit.get('value').hasError('required')) { + tenant-profile.rate-limits.number-of-messages-required + } @else if(rateLimit.get('value').hasError('min')) { + tenant-profile.rate-limits.number-of-messages-min + } - + tenant-profile.rate-limits.per-seconds - - {{ 'tenant-profile.rate-limits.per-seconds-required' | translate }} - - - {{ 'tenant-profile.rate-limits.per-seconds-min' | translate }} - + + @if (rateLimit.get('time').hasError('required')) { + tenant-profile.rate-limits.per-seconds-required + } @else if (rateLimit.get('time').hasError('min')) { + tenant-profile.rate-limits.per-seconds-min + } @else if (rateLimit.get('time').hasError('duplicateTime')) { + tenant-profile.rate-limits.per-seconds-duplicate + }
-
- -
{{ title }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts index 6dedab0bf2..8c1fc3ab2a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts @@ -14,37 +14,27 @@ /// limitations under the License. /// -import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { PageComponent } from '@shared/components/page.component'; +import { ChangeDetectorRef, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { WidgetContext } from '@home/models/widget-component.models'; -import { UtilsService } from '@core/services/utils.service'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { isDefined, isNumber } from '@core/utils'; +import { deepClone, isDefined } from '@core/utils'; import { CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; import tinycolor from 'tinycolor2'; -import { ColorProcessor, gradientColor } from '@shared/models/widget-settings.models'; -import { getSourceTbUnitSymbol } from '@shared/models/unit.models'; +import { ColorProcessor, gradientColor, ValueFormatProcessor } from '@shared/models/widget-settings.models'; +import { + KnobSettings, + knobWidgetDefaultSettings, + prepareKnobSettings +} from '@shared/models/widget/rpc/knob.component.models'; +import { ValueType } from '@shared/models/constants'; +import { BasicActionWidgetComponent, ValueSetter } from '@home/components/widget/lib/action/action-widget.models'; import GenericOptions = CanvasGauges.GenericOptions; -interface KnobSettings { - minValue: number; - maxValue: number; - initialValue: number; - title: string; - getValueMethod: string; - setValueMethod: string; - requestTimeout: number; - requestPersistent: boolean; - persistentPollingInterval: number; -} - @Component({ selector: 'tb-knob', templateUrl: './knob.component.html', styleUrls: ['./knob.component.scss'] }) -export class KnobComponent extends PageComponent implements OnInit, OnDestroy { +export class KnobComponent extends BasicActionWidgetComponent implements OnInit, OnDestroy { @ViewChild('knob', {static: true}) knobRef: ElementRef; @ViewChild('knobContainer', {static: true}) knobContainerRef: ElementRef; @@ -52,8 +42,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { @ViewChild('knobTopPointer', {static: true}) knobTopPointerRef: ElementRef; @ViewChild('knobValueContainer', {static: true}) knobValueContainerRef: ElementRef; @ViewChild('knobValue', {static: true}) knobValueRef: ElementRef; - @ViewChild('knobErrorContainer', {static: true}) knobErrorContainerRef: ElementRef; - @ViewChild('knobError', {static: true}) knobErrorRef: ElementRef; @ViewChild('knobTitleContainer', {static: true}) knobTitleContainerRef: ElementRef; @ViewChild('knobTitle', {static: true}) knobTitleRef: ElementRef; @ViewChild('knobMinmaxContainer', {static: true}) knobMinmaxContainerRef: ElementRef; @@ -64,7 +52,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { ctx: WidgetContext; value = '0'; - error = ''; title = ''; minValue: number; maxValue: number; @@ -79,13 +66,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { private minDeg = -45; private maxDeg = 225; - private isSimulated: boolean; - private requestTimeout: number; - private requestPersistent: boolean; - private persistentPollingInterval: number; - private getValueMethod: string; - private setValueMethod: string; - private executingUpdateValue: boolean; private scheduledValue: number; private rpcValue: number; @@ -98,8 +78,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { private knobValue: JQuery; private knobTitleContainer: JQuery; private knobTitle: JQuery; - private knobErrorContainer: JQuery; - private knobError: JQuery; private knobMinmaxContainer: JQuery; private minmaxLabel: JQuery; private textMeasure: JQuery; @@ -109,9 +87,11 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { private knobResize$: ResizeObserver; - constructor(private utils: UtilsService, - protected store: Store) { - super(store); + private valueSetter: ValueSetter; + private valueFormat: ValueFormatProcessor; + + constructor(protected cd: ChangeDetectorRef) { + super(cd); } ngOnInit(): void { @@ -123,8 +103,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { this.knobValue = $(this.knobValueRef.nativeElement); this.knobTitleContainer = $(this.knobTitleContainerRef.nativeElement); this.knobTitle = $(this.knobTitleRef.nativeElement); - this.knobErrorContainer = $(this.knobErrorContainerRef.nativeElement); - this.knobError = $(this.knobErrorRef.nativeElement); this.knobMinmaxContainer = $(this.knobMinmaxContainerRef.nativeElement); this.minmaxLabel = this.knobMinmaxContainer.find('.minmax-label'); this.textMeasure = $(this.textMeasureRef.nativeElement); @@ -145,7 +123,31 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { } private init() { - const settings: KnobSettings = this.ctx.settings; + const settings: KnobSettings = prepareKnobSettings({...deepClone(knobWidgetDefaultSettings), ...this.ctx.settings}); + + let initialValue = isDefined(settings.initialValue) ? settings.initialValue : this.minValue; + + const getInitialStateSettings = + {...settings.initialState, actionLabel: this.ctx.translate.instant('widgets.slider.initial-value')}; + this.createValueGetter(getInitialStateSettings, ValueType.DOUBLE, { + next: (value) => { + if (this.canvasBar) { + this.setValue(value); + } else { + initialValue = value; + } + } + }); + + const valueChangeSettings = {...settings.valueChange, + actionLabel: this.ctx.translate.instant('widgets.slider.on-value-change')}; + this.valueSetter = this.createValueSetter(valueChangeSettings); + + this.valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, { + units: this.ctx.units, + decimals: this.ctx.decimals, + showZeroDecimals: true + }); this.minValue = isDefined(settings.minValue) ? settings.minValue : 0; this.maxValue = isDefined(settings.maxValue) ? settings.maxValue : 100; @@ -279,44 +281,10 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { }); - const subscription = this.ctx.defaultSubscription; - const rpcEnabled = subscription.rpcEnabled; - - this.isSimulated = this.utils.widgetEditMode; - - this.requestTimeout = 500; - if (settings.requestTimeout) { - this.requestTimeout = settings.requestTimeout; - } - this.requestPersistent = false; - if (settings.requestPersistent) { - this.requestPersistent = settings.requestPersistent; - } - this.persistentPollingInterval = 5000; - if (settings.persistentPollingInterval) { - this.persistentPollingInterval = settings.persistentPollingInterval; - } - this.getValueMethod = 'getValue'; - if (settings.getValueMethod && settings.getValueMethod.length) { - this.getValueMethod = settings.getValueMethod; - } - this.setValueMethod = 'setValue'; - if (settings.setValueMethod && settings.setValueMethod.length) { - this.setValueMethod = settings.setValueMethod; - } - import('@home/components/widget/lib/canvas-digital-gauge').then( (gauge) => { this.canvasBar = new gauge.CanvasDigitalGauge(canvasBarData).draw(); - const initialValue = isDefined(settings.initialValue) ? settings.initialValue : this.minValue; this.setValue(initialValue); - if (!rpcEnabled) { - this.onError('Target device is not set!'); - } else { - if (!this.isSimulated) { - this.rpcRequestValue(); - } - } } ); @@ -348,7 +316,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { this.canvasBar.update({width: size, height: size} as GenericOptions); } this.setFontSize(this.knobTitle, this.title, this.knobTitleContainer.height(), this.knobTitleContainer.width()); - this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width()); const minmaxHeight = this.knobMinmaxContainer.height(); this.minmaxLabel.css({fontSize: minmaxHeight + 'px', lineHeight: minmaxHeight + 'px'}); this.checkValueSize(); @@ -408,27 +375,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { } private formatValue(value: any): string { - return this.ctx.utils.formatValue(value, this.ctx.decimals, getSourceTbUnitSymbol(this.ctx.units), true); - } - - private rpcRequestValue() { - this.error = ''; - this.ctx.controlApi.sendTwoWayCommand(this.getValueMethod, null, this.requestTimeout, - this.requestPersistent, this.persistentPollingInterval).subscribe( - (responseBody) => { - if (isNumber(responseBody)) { - const numValue = Number(Number(responseBody).toFixed(this.ctx.decimals)); - this.setValue(numValue); - } else { - const errorText = `Unable to parse response: ${responseBody}`; - this.onError(errorText); - } - }, - () => { - const errorText = this.ctx.defaultSubscription.rpcErrorText; - this.onError(errorText); - } - ); + return this.valueFormat.format(value); } private rpcUpdateValue(value: number) { @@ -440,27 +387,19 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { this.rpcValue = value; this.executingUpdateValue = true; } - this.error = ''; - this.ctx.controlApi.sendOneWayCommand(this.setValueMethod, value, this.requestTimeout, - this.requestPersistent, this.persistentPollingInterval).subscribe( - () => { - this.executingUpdateValue = false; - if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) { - this.rpcUpdateValue(this.scheduledValue); + if (!this.ctx.isEdit && !this.ctx.isPreview) { + this.updateValue(this.valueSetter, value, { + next: () => { + this.executingUpdateValue = false; + if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) { + this.rpcUpdateValue(this.scheduledValue); + } + }, + error: () => { + this.executingUpdateValue = false; } - }, - () => { - this.executingUpdateValue = false; - const errorText = this.ctx.defaultSubscription.rpcErrorText; - this.onError(errorText); - } - ); - } - - private onError(error: string) { - this.error = error; - this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width()); - this.ctx.detectChanges(); + }); + } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.html index 6d2dfe2187..5016361604 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.html @@ -15,66 +15,66 @@ limitations under the License. --> -
-
- widgets.rpc.common-settings - - widgets.rpc.knob-title - - -
-
- widgets.rpc.value-settings - - widgets.rpc.initial-value - - -
- - widgets.rpc.min-value - + +
+
widgets.knob.behavior
+
+
widgets.knob.initial-value
+ +
+
+
widgets.knob.on-value-change
+ +
+
+
+
widget-config.appearance
+
+
widgets.rpc.knob-title
+ + - - widgets.rpc.max-value - +
+
+
{{ 'widgets.knob.range' | translate }}
+
+
widgets.knob.min
+ + + +
widgets.knob.max
+ + + +
+
+
+
{{ 'widgets.knob.value' | translate }}
+
+ + + +
widget-config.decimals-suffix
+
+
+
+
+
widgets.knob.fallback-initial-value
+ + -
- - widgets.rpc.get-value-method - - - - widgets.rpc.set-value-method - - -
-
- widgets.rpc.rpc-settings - - widgets.rpc.request-timeout - - -
- widgets.rpc.persistent-rpc-settings - - - - - {{ 'widgets.rpc.request-persistent' | translate }} - - - - widget-config.advanced-settings - - - - - widgets.rpc.persistent-polling-interval - - - - -
-
-
+
+
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts index 5ef0bebc73..e5c57474c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts @@ -15,10 +15,13 @@ /// import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetType } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; +import { knobWidgetDefaultSettings, prepareKnobSettings } from '@shared/models/widget/rpc/knob.component.models'; +import { ValueType } from '@shared/models/constants'; +import { deepClone } from '@core/utils'; @Component({ selector: 'tb-knob-control-widget-settings', @@ -27,6 +30,16 @@ import { AppState } from '@core/core.state'; }) export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent { + get targetDevice(): TargetDevice { + return this.widgetConfig?.config?.targetDevice; + } + + get widgetType(): widgetType { + return this.widgetConfig?.widgetType; + } + + valueType = ValueType; + knobControlWidgetSettingsForm: UntypedFormGroup; constructor(protected store: Store, @@ -39,17 +52,25 @@ export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent } protected defaultSettings(): WidgetSettings { - return { - title: '', - minValue: 0, - maxValue: 100, - initialValue: 50, - getValueMethod: 'getValue', - setValueMethod: 'setValue', - requestTimeout: 500, - requestPersistent: false, - persistentPollingInterval: 5000 - }; + return knobWidgetDefaultSettings; + } + + protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { + const knobSettings = prepareKnobSettings(deepClone(settings) as any) as WidgetSettings; + knobSettings.valueDecimals = this.widgetConfig?.config?.decimals ?? 2; + knobSettings.valueUnits = deepClone(this.widgetConfig?.config?.units); + return super.prepareInputSettings(knobSettings); + } + + protected prepareOutputSettings(settings: any): WidgetSettings { + const newSettings = deepClone(settings); + if (this.widgetConfig?.config) { + this.widgetConfig.config.units = settings.valueUnits; + this.widgetConfig.config.decimals = settings.valueDecimals; + } + delete newSettings.valueUnits; + delete newSettings.valueDecimals; + return super.prepareOutputSettings(newSettings); } protected onSettingsSet(settings: WidgetSettings) { @@ -61,35 +82,16 @@ export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent // Value settings - initialValue: [settings.initialValue, []], + initialState: [settings.initialState, []], + valueChange: [settings.valueChange, []], + minValue: [settings.minValue, [Validators.required]], maxValue: [settings.maxValue, [Validators.required]], - getValueMethod: [settings.getValueMethod, [Validators.required]], - setValueMethod: [settings.setValueMethod, [Validators.required]], - - // RPC settings - - requestTimeout: [settings.requestTimeout, [Validators.min(0), Validators.required]], - - // --> Persistent RPC settings + valueUnits: [settings.valueUnits, []], + valueDecimals: [settings.valueDecimals, []], + initialValue: [settings.initialValue, []], - requestPersistent: [settings.requestPersistent, []], - persistentPollingInterval: [settings.persistentPollingInterval, [Validators.min(1000)]] }); } - - protected validatorTriggers(): string[] { - return ['requestPersistent']; - } - - protected updateValidators(emitEvent: boolean): void { - const requestPersistent: boolean = this.knobControlWidgetSettingsForm.get('requestPersistent').value; - if (requestPersistent) { - this.knobControlWidgetSettingsForm.get('persistentPollingInterval').enable({emitEvent}); - } else { - this.knobControlWidgetSettingsForm.get('persistentPollingInterval').disable({emitEvent}); - } - this.knobControlWidgetSettingsForm.get('persistentPollingInterval').updateValueAndValidity({emitEvent: false}); - } } diff --git a/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts b/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts new file mode 100644 index 0000000000..2bc34666a5 --- /dev/null +++ b/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts @@ -0,0 +1,120 @@ +/// +/// Copyright © 2016-2025 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 { + DataToValueType, + GetValueAction, + GetValueSettings, + SetValueAction, + SetValueSettings, + ValueToDataType +} from '@shared/models/action-widget-settings.models'; +import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { isDefinedAndNotNull } from '@core/utils'; + +export interface KnobSettings { + initialState: GetValueSettings; + valueChange: SetValueSettings; + minValue: number; + maxValue: number; + initialValue: number; + title: string; + getValueMethod?: string; //deprecated + setValueMethod?: string; //deprecated + requestTimeout?: number; //deprecated + requestPersistent?: boolean; //deprecated + persistentPollingInterval?: number; //deprecated +} + +export const knobWidgetDefaultSettings: KnobSettings = { + initialState: { + action: GetValueAction.EXECUTE_RPC, + defaultValue: 50, + executeRpc: { + method: 'getValue', + requestTimeout: 500, + requestPersistent: false, + persistentPollingInterval: 5000 + }, + getAttribute: { + key: 'value', + scope: null + }, + getTimeSeries: { + key: 'value' + }, + getAlarmStatus: { + severityList: null, + typeList: null + }, + dataToValue: { + type: DataToValueType.NONE, + compareToValue: true, + dataToValueFunction: '/* Should return double value */\nreturn data;' + } + }, + valueChange: { + action: SetValueAction.EXECUTE_RPC, + executeRpc: { + method: 'setValue', + requestTimeout: 500, + requestPersistent: false, + persistentPollingInterval: 5000 + }, + setAttribute: { + key: 'value', + scope: AttributeScope.SERVER_SCOPE + }, + putTimeSeries: { + key: 'value' + }, + valueToData: { + type: ValueToDataType.VALUE, + constantValue: 0, + valueToDataFunction: '/* Convert input double value to RPC parameters or attribute/time-series value */\nreturn value;' + } + }, + title: '', + minValue: 0, + maxValue: 100, + initialValue: 50 +} + +export const prepareKnobSettings = (settings: KnobSettings): KnobSettings => { + if (isDefinedAndNotNull(settings.getValueMethod)) { + settings.initialState.executeRpc.method = settings.getValueMethod; + } + + if (isDefinedAndNotNull(settings.setValueMethod)) { + settings.valueChange.executeRpc.method = settings.setValueMethod; + } + + if (isDefinedAndNotNull(settings.requestPersistent)) { + settings.initialState.executeRpc.requestPersistent = settings.requestPersistent; + settings.valueChange.executeRpc.requestPersistent = settings.requestPersistent; + } + + if (isDefinedAndNotNull(settings.persistentPollingInterval)) { + settings.initialState.executeRpc.persistentPollingInterval = settings.persistentPollingInterval; + settings.valueChange.executeRpc.persistentPollingInterval = settings.persistentPollingInterval; + } + + if (isDefinedAndNotNull(settings.requestTimeout)) { + settings.initialState.executeRpc.requestTimeout = settings.requestTimeout; + settings.valueChange.executeRpc.requestTimeout = settings.requestTimeout; + } + return settings; +} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 59b81edb63..1c213674c6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -7960,6 +7960,17 @@ "fill-area-opacity": "Fill area opacity", "range-chart-style": "Range chart style" }, + "knob": { + "behavior": "Behavior", + "initial-value": "Initial value", + "initial-value-hint": "Action to get the initial value of the knob.", + "on-value-change": "On value change", + "on-value-change-hint": "Action triggered when the knob value is changed.", + "range": "Range", + "min": "min", + "max": "max", + "fallback-initial-value": "Fallback initial value" + }, "rpc": { "value-settings": "Value settings", "initial-value": "Initial value", @@ -8015,10 +8026,7 @@ "led-status-value-attribute": "Device attribute containing led status value", "led-status-value-timeseries": "Device time series containing led status value", "check-status-method": "RPC check device status method", - "parse-led-status-value-function": "Parse led status value function", - "knob-title": "Knob title", - "min-value": "Minimum value", - "max-value": "Maximum value" + "parse-led-status-value-function": "Parse led status value function" }, "maps": { "map-type": { From 5ff3a76e0686bb9a6d4411612fd7744a38b4c01a Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 2 Jun 2025 18:25:28 +0300 Subject: [PATCH 265/335] UI: Add clear knob settings; Fixed translation --- .../app/shared/models/widget/rpc/knob.component.models.ts | 5 +++++ ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts b/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts index 2bc34666a5..47b73f5a08 100644 --- a/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts +++ b/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts @@ -96,25 +96,30 @@ export const knobWidgetDefaultSettings: KnobSettings = { export const prepareKnobSettings = (settings: KnobSettings): KnobSettings => { if (isDefinedAndNotNull(settings.getValueMethod)) { settings.initialState.executeRpc.method = settings.getValueMethod; + delete settings.getValueMethod; } if (isDefinedAndNotNull(settings.setValueMethod)) { settings.valueChange.executeRpc.method = settings.setValueMethod; + delete settings.setValueMethod; } if (isDefinedAndNotNull(settings.requestPersistent)) { settings.initialState.executeRpc.requestPersistent = settings.requestPersistent; settings.valueChange.executeRpc.requestPersistent = settings.requestPersistent; + delete settings.requestPersistent; } if (isDefinedAndNotNull(settings.persistentPollingInterval)) { settings.initialState.executeRpc.persistentPollingInterval = settings.persistentPollingInterval; settings.valueChange.executeRpc.persistentPollingInterval = settings.persistentPollingInterval; + delete settings.persistentPollingInterval; } if (isDefinedAndNotNull(settings.requestTimeout)) { settings.initialState.executeRpc.requestTimeout = settings.requestTimeout; settings.valueChange.executeRpc.requestTimeout = settings.requestTimeout; + delete settings.requestTimeout; } return settings; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 1c213674c6..012cd6f44c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -7969,6 +7969,7 @@ "range": "Range", "min": "min", "max": "max", + "value": "Value", "fallback-initial-value": "Fallback initial value" }, "rpc": { @@ -8026,7 +8027,8 @@ "led-status-value-attribute": "Device attribute containing led status value", "led-status-value-timeseries": "Device time series containing led status value", "check-status-method": "RPC check device status method", - "parse-led-status-value-function": "Parse led status value function" + "parse-led-status-value-function": "Parse led status value function", + "knob-title": "Knob title" }, "maps": { "map-type": { From dfac02b14eb13a42028202578499e4289b2875ec Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 2 Jun 2025 18:58:34 +0300 Subject: [PATCH 266/335] UI: Add allow created/view relation to rule chain --- .../relation/relation-dialog.component.html | 1 + .../relation/relation-dialog.component.ts | 23 ++++++++++++++----- .../entity/entity-list-select.component.html | 1 + .../entity/entity-list-select.component.ts | 3 +++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.html b/ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.html index 91a0357947..da0a5c742c 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.html @@ -38,6 +38,7 @@ 'relation.to-entity' : 'relation.from-entity') | translate}} implements OnInit, ErrorStateMatcher { - relationFormGroup: UntypedFormGroup; + relationFormGroup: FormGroup; + + additionEntityTypes: {[key in string]: string} = {}; isAdd: boolean; direction: EntitySearchDirection; entitySearchDirection = EntitySearchDirection; - additionalInfo: UntypedFormControl; + additionalInfo: FormControl; @ViewChild('additionalInfoEdit', {static: true}) additionalInfoEdit: JsonObjectEditComponent; submitted = false; + private authUser = getCurrentAuthUser(this.store); + constructor(protected store: Store, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: RelationDialogData, private entityRelationService: EntityRelationService, @SkipSelf() private errorStateMatcher: ErrorStateMatcher, public dialogRef: MatDialogRef, - public fb: UntypedFormBuilder, + public fb: FormBuilder, private destroyRef: DestroyRef) { super(store, router, dialogRef); this.isAdd = data.isAdd; @@ -74,13 +81,17 @@ export class RelationDialogComponent extends DialogComponent Date: Tue, 3 Jun 2025 11:03:43 +0300 Subject: [PATCH 267/335] Translation improvements --- .../assets/locale/locale.constant-da_DK.json | 30 +- .../assets/locale/locale.constant-de_DE.json | 54 +- .../assets/locale/locale.constant-el_GR.json | 10608 +++++++++++++--- .../assets/locale/locale.constant-es_ES.json | 2 +- .../assets/locale/locale.constant-fr_FR.json | 18 +- .../assets/locale/locale.constant-it_IT.json | 2 +- 6 files changed, 8669 insertions(+), 2045 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index d9a5172664..fd93ee2a0e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -4808,7 +4808,7 @@ "use-simple-client-http-factory": "Brug simpel HTTP-klientfabrik", "ignore-request-body": "Uden forespørgselsindhold", "parse-to-plain-text": "Fortolk som almindelig tekst", - "parse-to-plain-text-hint": "Hvis valgt, transformeres JSON-strengen i beskedens indhold til almindelig tekst.", + "parse-to-plain-text-hint": "Hvis valgt, vil meddelelsens anmodningsindhold blive konverteret fra JSON-streng til almindelig tekst, f.eks. vil msg = \"Hello,\\t\"world\"\" blive analyseret til Hello, \"world\"", "read-timeout": "Læs timeout i millisekunder", "read-timeout-hint": "Værdien 0 betyder uendelig timeout", "max-parallel-requests-count": "Maksimalt antal parallelle forespørgsler", @@ -4816,7 +4816,7 @@ "max-response-size": "Maks. svarstørrelse (i KB)", "max-response-size-hint": "Maksimal hukommelse tildelt til buffering ved afkodning/enkodning af HTTP-meddelelser", "headers": "Headers", - "headers-hint": "Brug ${metadataKey} for metadata eller $[messageKey] for beskeddata i header/værdi felter", + "headers-hint": "Brug ${metadataKey} for værdi fra metadata, $[messageKey] for værdi fra meddelelsesindhold i header/værdifelter", "header": "Header", "header-required": "Header er påkrævet", "value": "Værdi", @@ -4884,7 +4884,7 @@ "pubsub-topic-name": "Emnenavn", "pubsub-topic-name-required": "Emnenavn er påkrævet", "message-attributes": "Beskedattributter", - "message-attributes-hint": "Brug ${metadataKey} eller $[messageKey] i navn/værdi felter", + "message-attributes-hint": "Brug ${metadataKey} for værdi fra metadata, $[messageKey] for værdi fra meddelelsesindhold i navn-/værdifelter", "connect-timeout": "Forbindelsestimeout (sek)", "connect-timeout-required": "Forbindelsestimeout er påkrævet.", "connect-timeout-range": "Timeout skal være mellem 1 og 200.", @@ -5073,18 +5073,18 @@ "failure-if-delta-negative": "Rapportér fejl ved negativ delta", "failure-if-delta-negative-tooltip": "Fejl ved negativ forskel mellem værdier.", "use-caching": "Brug caching", - "use-caching-tooltip": "Cacher input-værdi for performance.", - "add-time-difference-between-readings": "Tilføj tidsforskel mellem aflæsninger", - "add-time-difference-between-readings-tooltip": "Tilføjer \"{{periodValueKey}}\" til beskeden.", - "period-value-key": "Tidsforskel nøgle", - "period-value-key-required": "Tidsforskel nøgle er påkrævet.", - "general-pattern-hint": "Brug ${metadataKey} eller $[messageKey]", - "alarm-severity-pattern-hint": "Angiv alarmniveau (CRITICAL, MAJOR, etc.) via metadata eller besked.", - "output-node-name-hint": "Navn på regelnode bruges som relationstype til videresendelse.", - "use-server-ts": "Brug server-tidsstempel", - "use-server-ts-hint": "Sikrer korrekt rækkefølge ved manglende tidsstempel.", - "kv-map-pattern-hint": "Alle felter understøtter templatization.", - "kv-map-single-pattern-hint": "Felt understøtter templatization.", + "use-caching-tooltip": "Regelknuden vil cache værdien af \"{{inputValueKey}}\" fra den indkommende besked for at forbedre ydeevnen. Bemærk, at cachen ikke opdateres, hvis \"{{inputValueKey}}\" ændres et andet sted.", + "add-time-difference-between-readings": "Tilføj tidsforskel mellem aflæsninger af \"{{inputValueKey}}\"", + "add-time-difference-between-readings-tooltip": "Hvis aktiveret, tilføjer regelknuden \"{{periodValueKey}}\" til den udgående besked.", + "period-value-key": "Tidsintervalnøgle", + "period-value-key-required": "Tidsintervalnøgle er påkrævet.", + "general-pattern-hint": "Brug ${metadataKey} for værdi fra metadata, $[messageKey] for værdi fra meddelelsesindhold.", + "alarm-severity-pattern-hint": "Brug ${metadataKey} for værdi fra metadata, $[messageKey] for værdi fra meddelelsesindhold. Alarmens alvorlighed skal være et systemniveau (CRITICAL, MAJOR osv.)", + "output-node-name-hint": "Regelknudens navn svarer til relationstypen for den udgående besked og bruges til at videresende beskeder til andre regelknuder i den kaldende regelkæde.", + "use-server-ts": "Brug serverens tidsstempel", + "use-server-ts-hint": "Brug serverens aktuelle tidsstempel for tidsseriedata, der mangler et eksplicit tidsstempel. Dette hjælper med korrekt rækkefølge, når der behandles beskeder fra flere kilder eller ved uordnet ankomst.", + "kv-map-pattern-hint": "Alle inputfelter understøtter skabelonisering. Brug $[messageKey] for værdi fra beskeden og ${metadataKey} for værdi fra metadata.", + "kv-map-single-pattern-hint": "Inputfelt understøtter skabelonisering. Brug $[messageKey] for værdi fra beskeden og ${metadataKey} for værdi fra metadata.", "shared-scope": "Delt område", "server-scope": "Serverområde", "client-scope": "Klientområde", diff --git a/ui-ngx/src/assets/locale/locale.constant-de_DE.json b/ui-ngx/src/assets/locale/locale.constant-de_DE.json index 766cac37f2..4b4bcab4d1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-de_DE.json +++ b/ui-ngx/src/assets/locale/locale.constant-de_DE.json @@ -4814,7 +4814,7 @@ "max-parallel-requests-count": "Max. Anzahl paralleler Anfragen", "max-parallel-requests-count-hint": "Wert 0 bedeutet unbegrenzte Parallelität", "max-response-size": "Maximale Antwortgröße (in KB)", - "max-response-size-hint": "Maximaler Speicher für das Puffern von Daten beim Kodieren/Decodieren von HTTP-Nachrichten (z. B. JSON, XML)", + "max-response-size-hint": "Die maximale Menge an Speicher, die zum Puffern von Daten beim Dekodieren oder Kodieren von HTTP-Nachrichten wie JSON- oder XML-Nutzdaten reserviert wird", "headers": "Header", "headers-hint": "Verwenden Sie ${metadataKey} für Werte aus den Metadaten und $[messageKey] für Werte aus dem Nachrichtentext in Header-/Wert-Feldern.", "header": "Header", @@ -4964,7 +4964,7 @@ "proxy-scheme": "Proxy-Schema", "numbers-to-template": "Telefonnummern zum Template", "numbers-to-template-required": "Telefonnummern zum Template sind erforderlich", - "numbers-to-template-hint": "Kommagetrennte Telefonnummern. Verwenden Sie ${metadataKey} für Werte aus Metadaten und $[messageKey] für Werte aus dem Nachrichtentext", + "numbers-to-template-hint": "Kommagetrennte Telefonnummern. Verwenden Sie ${metadataKey} für Werte aus Metadaten und $[messageKey] für Werte aus dem Nachrichtentext", "sms-message-template": "SMS-Nachrichtenvorlage", "sms-message-template-required": "SMS-Nachrichtenvorlage ist erforderlich", "use-system-sms-settings": "Systemweite SMS-Anbietereinstellungen verwenden", @@ -5045,7 +5045,7 @@ "tell-failure-if-absent": "Fehler melden", "tell-failure-if-absent-hint": "Wenn mindestens ein ausgewählter Schlüssel nicht vorhanden ist, wird die ausgehende Nachricht mit \"Fehler\" gekennzeichnet.", "get-latest-value-with-ts": "Zeitstempel für letzte Telemetriewerte abrufen", - "get-latest-value-with-ts-hint": "Wenn ausgewählt, enthalten die letzten Telemetriewerte auch einen Zeitstempel, z. B.: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "get-latest-value-with-ts-hint": "Wenn ausgewählt, enthalten die neuesten Telemetriedaten auch einen Zeitstempel, z.B.: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", "ignore-null-strings": "Leere Zeichenfolgen ignorieren", "ignore-null-strings-hint": "Wenn ausgewählt, ignoriert der Regelknoten Entitätsfelder mit leerem Wert.", "add-metadata-key-values-as-kafka-headers": "Metadaten-Schlüssel-Werte-Paare als Kafka-Header hinzufügen", @@ -5079,7 +5079,7 @@ "period-value-key": "Zeitraum-Wert-Schlüssel", "period-value-key-required": "Zeitraum-Wert-Schlüssel ist erforderlich.", "general-pattern-hint": "Verwenden Sie ${metadataKey} für Metadatenwert, $[messageKey] für Nachrichtenwert.", - "alarm-severity-pattern-hint": "Verwenden Sie ${metadataKey} für Metadatenwert, $[messageKey] für Nachrichtenwert. Alarm-Schweregrade sollten systemdefiniert sein (CRITICAL, MAJOR usw.)", + "alarm-severity-pattern-hint": "Verwenden Sie ${metadataKey} für Metadatenwert, $[messageKey] für Nachrichtenwert. Alarm-Schweregrade sollten systemdefiniert sein (CRITICAL, MAJOR usw.)", "output-node-name-hint": "Der Name des Regelknotens entspricht dem Beziehungstyp der Ausgabemeldung und wird verwendet, um Nachrichten an andere Knoten in der Regelkette weiterzuleiten.", "use-server-ts": "Server-Zeitstempel verwenden", "use-server-ts-hint": "Verwenden Sie den aktuellen Zeitstempel des Servers für Zeitreihen ohne expliziten Zeitstempel. Dies hilft, die Reihenfolge bei mehreren Quellen oder verspäteten Nachrichten zu wahren.", @@ -9195,30 +9195,30 @@ "language": "Sprache", "locales": { "ar_AE": "العربية (الإمارات العربية المتحدة)", - "ca_ES": "katalanisch (Spanien)", - "cs_CZ": "tschechisch (Tschechien)", - "da_DK": "dänisch (Dänemark)", + "ca_ES": "català (Espanya)", + "cs_CZ": "čeština (Česko)", + "da_DK": "dansk (Danmark)", "de_DE": "Deutsch (Deutschland)", - "el_GR": "griechisch (Griechenland)", - "en_US": "Englisch (USA)", - "es_ES": "spanisch (Spanien)", - "fa_IR": "persisch (Iran)", - "fr_FR": "französisch (Frankreich)", - "it_IT": "italienisch (Italien)", - "ja_JP": "japanisch (Japan)", - "ka_GE": "georgisch (Georgien)", - "ko_KR": "koreanisch (Südkorea)", - "lt_LT": "litauisch (Litauen)", - "lv_LV": "lettisch (Lettland)", - "nl_BE": "niederländisch (Belgien)", - "pl_PL": "polnisch (Polen)", - "pt_BR": "portugiesisch (Brasilien)", - "ro_RO": "rumänisch (Rumänien)", - "sl_SI": "slowenisch (Slowenien)", - "tr_TR": "türkisch (Türkei)", - "uk_UA": "ukrainisch (Ukraine)", - "zh_CN": "chinesisch (China)", - "zh_TW": "chinesisch (Taiwan)" + "el_GR": "Ελληνικά (Ελλάδα)", + "en_US": "English (United States)", + "es_ES": "español (España)", + "fa_IR": "فارسی (ایران)", + "fr_FR": "français (France)", + "it_IT": "italiano (Italia)", + "ja_JP": "日本語 (日本)", + "ka_GE": "ქართული (საქართველო)", + "ko_KR": "한국어 (대한민국)", + "lt_LT": "lietuvių (Lietuva)", + "lv_LV": "latviešu (Latvija)", + "nl_BE": "Nederlands (België)", + "pl_PL": "polski (Polska)", + "pt_BR": "português (Brasil)", + "ro_RO": "română (România)", + "sl_SI": "slovenščina (Slovenija)", + "tr_TR": "Türkçe (Türkiye)", + "uk_UA": "українська (Україна)", + "zh_CN": "中文 (中国)", + "zh_TW": "中文 (台灣)" } } } \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-el_GR.json b/ui-ngx/src/assets/locale/locale.constant-el_GR.json index 164b5486b9..e05a2a5d8e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-el_GR.json +++ b/ui-ngx/src/assets/locale/locale.constant-el_GR.json @@ -1,20 +1,25 @@ { "access": { - "unauthorized": "Χωρίς δικαιώματα πρόσβασης", + "unauthorized": "Μη εξουσιοδοτημένο", "unauthorized-access": "Μη εξουσιοδοτημένη πρόσβαση", - "unauthorized-access-text": "Θα πρέπει να συνδεθείτε για να έχετε πρόσβαση σε αυτόν τον πόρο!", - "access-forbidden": "Απαγορεύεται η πρόσβαση!", - "access-forbidden-text": "Δεν έχετε δικαιώματα πρόσβασης σε αυτήν την τοποθεσία!
Συνδεθείτε με διαφορετικό όνομα χρήστη, αν εξακολουθείτε να θέλετε να έχετε πρόσβαση σε αυτήν την τοποθεσία.", - "refresh-token-expired": "Η περίοδος χρήσης έχει λήξει", - "refresh-token-failed": "Δεν είναι δυνατή η ανανέωση της περιόδου χρήσης", - "permission-denied": "Άρνηση πρόσβασης!", - "permission-denied-text": "Δεν έχετε δικαίωμα εκτέλεσης αυτής της λειτουργίας!" + "unauthorized-access-text": "Πρέπει να συνδεθείτε για να έχετε πρόσβαση σε αυτόν τον πόρο!", + "access-forbidden": "Απαγορευμένη πρόσβαση", + "access-forbidden-text": "Δεν έχετε δικαιώματα πρόσβασης σε αυτήν την τοποθεσία!
Δοκιμάστε να συνδεθείτε με διαφορετικό χρήστη αν εξακολουθείτε να θέλετε να αποκτήσετε πρόσβαση.", + "refresh-token-expired": "Η συνεδρία έληξε", + "refresh-token-failed": "Αδυναμία ανανέωσης συνεδρίας", + "permission-denied": "Η άδεια απορρίφθηκε", + "permission-denied-text": "Δεν έχετε άδεια να εκτελέσετε αυτήν τη λειτουργία!" + }, + "account": { + "account": "Λογαριασμός", + "notification-settings": "Ρυθμίσεις ειδοποιήσεων" }, "action": { "activate": "Ενεργοποίηση", "suspend": "Αναστολή", "save": "Αποθήκευση", "saveAs": "Αποθήκευση ως", + "move": "Μετακίνηση", "cancel": "Ακύρωση", "ok": "OK", "delete": "Διαγραφή", @@ -22,306 +27,919 @@ "yes": "Ναι", "no": "Όχι", "update": "Ενημέρωση", - "remove": "Διαγραφή", + "remove": "Αφαίρεση", "search": "Αναζήτηση", - "clear-search": "Καθαρισμός Αναζήτησης", + "clear-search": "Καθαρισμός αναζήτησης", "assign": "Ανάθεση", - "unassign": "Ακύρωση Ανάθεσης", - "share": "Διαμοιρασμός", - "make-private": "Ιδιωτικό", + "unassign": "Αποανάθεση", + "share": "Κοινή χρήση", + "make-private": "Ορισμός ως ιδιωτικό", "apply": "Εφαρμογή", - "apply-changes": "Εφααρμογή Αλλαγών", - "edit-mode": "Λειτουργία Επεξεργασίας", - "enter-edit-mode": "Έναρξη Επεξεργασίας", - "decline-changes": "Απόρριψη Αλλαγών", + "apply-changes": "Εφαρμογή αλλαγών", + "edit-mode": "Λειτουργία επεξεργασίας", + "enter-edit-mode": "Είσοδος σε λειτουργία επεξεργασίας", + "decline-changes": "Απόρριψη αλλαγών", + "decline": "Απόρριψη", "close": "Κλείσιμο", "back": "Πίσω", "run": "Εκτέλεση", "sign-in": "Σύνδεση!", "edit": "Επεξεργασία", - "view": "Επισκόπηση", + "view": "Προβολή", "create": "Δημιουργία", - "drag": "Drag", + "drag": "Σύρσιμο", "refresh": "Ανανέωση", "undo": "Αναίρεση", "copy": "Αντιγραφή", "paste": "Επικόλληση", - "copy-reference": "Αντιγραφή παραπομπής", - "paste-reference": "Επικόλληση παραπομπής", + "copy-reference": "Αντιγραφή αναφοράς", + "paste-reference": "Επικόλληση αναφοράς", "import": "Εισαγωγή", "export": "Εξαγωγή", - "share-via": "Διαμοίραση μέσω {{provider}}", - "move": "Μετακίνηση", + "share-via": "Κοινή χρήση μέσω {{provider}}", "select": "Επιλογή", "continue": "Συνέχεια", - "done": "Ολοκληρώθηκε" + "discard-changes": "Απόρριψη αλλαγών", + "download": "Λήψη", + "next": "Επόμενο", + "next-with-label": "Επόμενο: {{label}}", + "read-more": "Διαβάστε περισσότερα", + "hide": "Απόκρυψη", + "test": "Δοκιμή", + "done": "Ολοκληρώθηκε", + "print": "Εκτύπωση", + "restore": "Επαναφορά", + "confirm": "Επιβεβαίωση", + "more": "Περισσότερα", + "less": "Λιγότερα", + "skip": "Παράλειψη", + "send": "Αποστολή", + "reset": "Επαναφορά", + "show-more": "Εμφάνιση περισσότερων", + "dont-show-again": "Να μην εμφανιστεί ξανά", + "see-documentation": "Δείτε την τεκμηρίωση", + "clear": "Καθαρισμός", + "upload": "Μεταφόρτωση", + "delete-anyway": "Διαγραφή ούτως ή άλλως", + "delete-selected": "Διαγραφή επιλεγμένων", + "set": "Ορισμός" }, "aggregation": { - "aggregation": "Συνάθροιση", - "function": "Συνάρτηση συνάθροισης δεδομένων", + "aggregation": "Συσσώρευση", + "function": "Συνάρτηση συσσώρευσης δεδομένων", "limit": "Μέγιστες τιμές", "group-interval": "Διάστημα ομαδοποίησης", - "min": "Min", - "max": "Max", - "avg": "Μέσος Όρος", + "min": "Ελάχιστο", + "max": "Μέγιστο", + "avg": "Μέσος όρος", "sum": "Άθροισμα", - "count": "Καταμέτρηση", - "none": "Κανένα" + "count": "Πλήθος", + "none": "Καμία" }, "admin": { + "settings": "Ρυθμίσεις", "general": "Γενικά", - "general-settings": "Γενικές Ρυθμίσεις", - "outgoing-mail": "Διακομιστής Αλληλογραφίας", - "outgoing-mail-settings": "Διακομιστής Εξερχόμενης Αλληλογραφίας", - "system-settings": "Ρυθμίσεις Συστήματος", - "test-mail-sent": "Το δοκιμαστικό μήνυμα στάλθηκε με επιτυχία!", + "general-settings": "Γενικές ρυθμίσεις", + "home-settings": "Ρυθμίσεις αρχικής σελίδας", + "home": "Αρχική", + "outgoing-mail": "Διακομιστής αλληλογραφίας", + "outgoing-mail-settings": "Ρυθμίσεις εξερχόμενης αλληλογραφίας", + "system-settings": "Ρυθμίσεις συστήματος", + "test-mail-sent": "Το δοκιμαστικό email εστάλη με επιτυχία!", "base-url": "Βασική διεύθυνση URL", - "base-url-required": "Απαιτείται ορισμός Base URL.", - "mail-from": "Αποστολέας", - "mail-from-required": "Απαιτείται βασική διεύθυνση URL.", + "base-url-required": "Η βασική διεύθυνση URL είναι υποχρεωτική.", + "prohibit-different-url": "Απαγόρευση χρήσης ονόματος κεντρικού υπολογιστή από τις κεφαλίδες αιτήσεων του πελάτη", + "prohibit-different-url-hint": "Αυτή η ρύθμιση θα πρέπει να είναι ενεργοποιημένη σε περιβάλλοντα παραγωγής. Ενδέχεται να προκαλέσει προβλήματα ασφαλείας όταν είναι απενεργοποιημένη", + "device-connectivity": { + "device-connectivity": "Συνδεσιμότητα συσκευής", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPs", + "mqtt": "MQTT", + "mqtts": "MQTTs", + "coap": "COAP", + "coaps": "COAPs", + "hint": "Αν τα πεδία κεντρικού υπολογιστή ή θύρας είναι κενά, θα χρησιμοποιηθεί η προεπιλεγμένη τιμή πρωτοκόλλου.", + "host": "Κεντρικός υπολογιστής", + "port": "Θύρα", + "port-pattern": "Η θύρα πρέπει να είναι θετικός ακέραιος αριθμός.", + "port-range": "Η θύρα πρέπει να είναι στην περιοχή από 1 έως 65535." + }, + "mail-from": "Από διεύθυνση email", + "mail-from-required": "Η από διεύθυνση email είναι υποχρεωτική.", "smtp-protocol": "Πρωτόκολλο SMTP", - "smtp-host": "SMTP host", - "smtp-host-required": "Απαιτείται ορισμός SMTP host.", + "smtp-host": "Διακομιστής SMTP", + "smtp-host-required": "Ο διακομιστής SMTP είναι υποχρεωτικός.", "smtp-port": "Θύρα SMTP", - "smtp-port-required": "Πρέπει να εισάγετε SMTP port.", - "smtp-port-invalid": "Αυτή δε φαίνεται να είναι μια έγκυρη SMTP port.", - "timeout-msec": "Timeout (msec)", - "timeout-required": "Απαιτείται τιμή Timeout.", - "timeout-invalid": "Αυτή δε φαίνεται να είναι μια έγκυρη τιμή timeout.", + "smtp-port-required": "Πρέπει να καθορίσετε θύρα SMTP.", + "smtp-port-invalid": "Αυτό δεν φαίνεται να είναι έγκυρη θύρα SMTP.", + "timeout-msec": "Χρονικό όριο (msec)", + "timeout-required": "Το χρονικό όριο είναι υποχρεωτικό.", + "timeout-invalid": "Αυτό δεν φαίνεται να είναι έγκυρο χρονικό όριο.", "enable-tls": "Ενεργοποίηση TLS", "tls-version": "Έκδοση 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": "Μήνυμα επαναφοράς κωδικού πρόσβασης", - "user-activated": "Μήνυμα ενεργοποίησης χρήστη", - "user-registered": "Μήνυμα καταχώρησης χρήστη" - }, - "mail-subject": "Θέμα μηνύματος (subject)", - "mail-body": "Κυρίως μήνυμα (body)" + "enable-proxy": "Ενεργοποίηση μεσολάβησης", + "proxy-host": "Διακομιστής μεσολάβησης", + "proxy-host-required": "Ο διακομιστής μεσολάβησης είναι υποχρεωτικός.", + "proxy-port": "Θύρα μεσολάβησης", + "proxy-port-required": "Η θύρα μεσολάβησης είναι υποχρεωτική.", + "proxy-port-range": "Η θύρα μεσολάβησης πρέπει να είναι μεταξύ 1 και 65535.", + "proxy-user": "Χρήστης μεσολάβησης", + "proxy-password": "Κωδικός μεσολάβησης", + "change-password": "Αλλαγή κωδικού", + "send-test-mail": "Αποστολή δοκιμαστικού email", + "sms-provider": "Πάροχος SMS", + "sms-provider-settings": "Ρυθμίσεις παρόχου SMS", + "sms-provider-type": "Τύπος παρόχου SMS", + "sms-provider-type-required": "Ο τύπος παρόχου SMS είναι υποχρεωτικός.", + "sms-provider-type-aws-sns": "Amazon SNS", + "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "Κλειδί πρόσβασης AWS", + "aws-access-key-id-required": "Το κλειδί πρόσβασης AWS είναι υποχρεωτικό", + "aws-secret-access-key": "Μυστικό κλειδί AWS", + "aws-secret-access-key-required": "Το μυστικό κλειδί AWS είναι υποχρεωτικό", + "aws-region": "Περιοχή AWS", + "aws-region-required": "Η περιοχή AWS είναι υποχρεωτική", + "number-from": "Αριθμός αποστολέα", + "number-from-required": "Ο αριθμός αποστολέα είναι υποχρεωτικός.", + "number-to": "Αριθμός παραλήπτη", + "number-to-required": "Ο αριθμός παραλήπτη είναι υποχρεωτικός.", + "phone-number-hint": "Αριθμός σε μορφή E.164, π.χ. +19995550123", + "phone-number-hint-twilio": "Αριθμός σε μορφή E.164/SID αριθμού/Messaging Service SID, π.χ. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Μη έγκυρος αριθμός τηλεφώνου. Πρέπει να είναι σε μορφή E.164, π.χ. +19995550123.", + "phone-number-pattern-twilio": "Μη έγκυρος αριθμός. Πρέπει να είναι σε μορφή E.164/SID αριθμού/Messaging Service SID, π.χ. +19995550123/PNXXX/MGXXX.", + "sms-message": "Μήνυμα SMS", + "sms-message-required": "Το μήνυμα SMS είναι υποχρεωτικό.", + "sms-message-max-length": "Το μήνυμα SMS δεν μπορεί να υπερβαίνει τους 1600 χαρακτήρες", + "twilio-account-sid": "Twilio Account SID", + "twilio-account-sid-required": "Το Twilio Account SID είναι υποχρεωτικό", + "twilio-account-token": "Twilio Account Token", + "twilio-account-token-required": "Το Twilio Account Token είναι υποχρεωτικό", + "send-test-sms": "Αποστολή δοκιμαστικού SMS", + "test-sms-sent": "Το δοκιμαστικό SMS στάλθηκε με επιτυχία!", + "security-settings": "Ρυθμίσεις ασφαλείας", + "password-policy": "Πολιτική κωδικών πρόσβασης", + "minimum-password-length": "Ελάχιστο μήκος κωδικού", + "minimum-password-length-required": "Το ελάχιστο μήκος κωδικού είναι υποχρεωτικό", + "minimum-password-length-range": "Το ελάχιστο μήκος πρέπει να είναι από 6 έως 50 χαρακτήρες", + "maximum-password-length": "Μέγιστο μήκος κωδικού", + "maximum-password-length-min": "Το μέγιστο μήκος πρέπει να είναι τουλάχιστον 6 χαρακτήρες", + "maximum-password-length-less-min": "Το μέγιστο μήκος πρέπει να είναι μεγαλύτερο από το ελάχιστο", + "minimum-uppercase-letters": "Ελάχιστος αριθμός κεφαλαίων γραμμάτων", + "minimum-uppercase-letters-range": "Ο ελάχιστος αριθμός κεφαλαίων δεν μπορεί να είναι αρνητικός", + "minimum-lowercase-letters": "Ελάχιστος αριθμός πεζών γραμμάτων", + "minimum-lowercase-letters-range": "Ο ελάχιστος αριθμός πεζών δεν μπορεί να είναι αρνητικός", + "minimum-digits": "Ελάχιστος αριθμός ψηφίων", + "minimum-digits-range": "Ο ελάχιστος αριθμός ψηφίων δεν μπορεί να είναι αρνητικός", + "minimum-special-characters": "Ελάχιστος αριθμός ειδικών χαρακτήρων", + "minimum-special-characters-range": "Ο ελάχιστος αριθμός ειδικών χαρακτήρων δεν μπορεί να είναι αρνητικός", + "password-expiration-period-days": "Περίοδος λήξης κωδικού σε ημέρες", + "password-expiration-period-days-range": "Η περίοδος λήξης δεν μπορεί να είναι αρνητική", + "password-reuse-frequency-days": "Συχνότητα επαναχρησιμοποίησης κωδικού σε ημέρες", + "password-reuse-frequency-days-range": "Η συχνότητα επαναχρησιμοποίησης δεν μπορεί να είναι αρνητική", + "allow-whitespace": "Να επιτρέπονται κενά", + "force-reset-password-if-no-valid": "Εξαναγκασμός επαναφοράς κωδικού αν δεν είναι έγκυρος", + "force-reset-password-if-no-valid-hint": "Προσοχή κατά την ενεργοποίηση αυτής της δυνατότητας: θα απαιτείται από τους χρήστες με μη έγκυρο κωδικό να επαναφέρουν τον κωδικό τους μέσω email.", + "general-policy": "Γενική πολιτική", + "max-failed-login-attempts": "Μέγιστος αριθμός αποτυχημένων προσπαθειών σύνδεσης πριν από το κλείδωμα λογαριασμού", + "minimum-max-failed-login-attempts-range": "Ο μέγιστος αριθμός αποτυχημένων προσπαθειών δεν μπορεί να είναι αρνητικός", + "user-lockout-notification-email": "Σε περίπτωση κλειδώματος λογαριασμού χρήστη, αποστολή ειδοποίησης μέσω email", + "user-activation-token-ttl": "Χρόνος ζωής συνδέσμου ενεργοποίησης λογαριασμού (σε ώρες)", + "user-activation-token-ttl-range": "Ο χρόνος ζωής πρέπει να είναι από 1 έως 24 ώρες", + "password-reset-token-ttl": "Χρόνος ζωής συνδέσμου επαναφοράς κωδικού (σε ώρες)", + "password-reset-token-ttl-range": "Ο χρόνος ζωής συνδέσμου πρέπει να είναι από 1 έως 24 ώρες", + "mobile-secret-key-length": "Μήκος μυστικού κλειδιού κινητού", + "mobile-secret-key-length-range": "Το μήκος του μυστικού κλειδιού πρέπει να είναι θετικό", + "domain-name": "Όνομα τομέα", + "domain-name-unique": "Το όνομα τομέα και το πρωτόκολλο πρέπει να είναι μοναδικά.", + "domain-name-max-length": "Το όνομα τομέα πρέπει να έχει λιγότερους από 256 χαρακτήρες", + "error-verification-url": "Ένα όνομα τομέα δεν πρέπει να περιέχει τα σύμβολα '/' και ':'. Παράδειγμα: thingsboard.io", + "connection-settings": "Ρυθμίσεις σύνδεσης", + "oauth2": { + "access-token-uri": "URI διακριτικού πρόσβασης", + "access-token-uri-required": "Το URI διακριτικού πρόσβασης είναι υποχρεωτικό.", + "activate-user": "Ενεργοποίηση χρήστη", + "add-domain": "Προσθήκη τομέα", + "delete-domain": "Διαγραφή τομέα", + "add-provider": "Προσθήκη παρόχου", + "delete-provider": "Διαγραφή παρόχου", + "allow-user-creation": "Να επιτρέπεται η δημιουργία χρήστη", + "always-fullscreen": "Πάντα πλήρης οθόνη", + "authorization-uri": "URI εξουσιοδότησης", + "authorization-uri-required": "Το URI εξουσιοδότησης είναι υποχρεωτικό.", + "add-client": "Προσθήκη πελάτη OAuth 2.0", + "client-details": "Λεπτομέρειες πελάτη OAuth 2.0", + "client": "Πελάτης OAuth 2.0", + "clients": "Πελάτες OAuth 2.0", + "no-oauth2-clients": "Δεν βρέθηκαν πελάτες OAuth 2.0", + "search-oauth2-clients": "Αναζήτηση πελατών OAuth 2.0", + "delete-client-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον πελάτη OAuth 2.0 '{{clientName}}';", + "delete-client-text": "Προσοχή, μετά την επιβεβαίωση ο πελάτης και όλα τα σχετικά δεδομένα δεν θα είναι ανακτήσιμα.", + "delete-mobile-app-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την εφαρμογή κινητού '{{applicationName}}';", + "delete-mobile-app-text": "Προσοχή, μετά την επιβεβαίωση η εφαρμογή κινητού και όλα τα σχετικά δεδομένα δεν θα είναι ανακτήσιμα.", + "title": "Τίτλος", + "client-title-required": "Ο τίτλος είναι υποχρεωτικός", + "client-title-max-length": "Ο τίτλος πρέπει να είναι μικρότερος από 100 χαρακτήρες", + "advanced-settings": "Σύνθετες ρυθμίσεις", + "domain-details": "Λεπτομέρειες τομέα", + "no-domains": "Δεν βρέθηκαν τομείς", + "search-domains": "Αναζήτηση τομέων", + "mobile-app-details": "Λεπτομέρειες εφαρμογής κινητού", + "add-mobile-app": "Προσθήκη εφαρμογής κινητού", + "no-mobile-apps": "Δεν βρέθηκαν εφαρμογές κινητού", + "search-mobile-apps": "Αναζήτηση εφαρμογών κινητού", + "send-token": "Αποστολή διακριτικού", + "create-new": "Δημιουργία νέου", + "client-authentication-method": "Μέθοδος ταυτοποίησης πελάτη", + "client-id": "Client ID", + "client-id-required": "Το Client ID είναι υποχρεωτικό.", + "client-id-max-length": "Το Client ID πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "client-secret": "Client secret", + "client-secret-required": "Το Client secret είναι υποχρεωτικό.", + "client-secret-max-length": "Το Client secret πρέπει να είναι μικρότερο από 2049 χαρακτήρες", + "custom-setting": "Προσαρμοσμένες ρυθμίσεις", + "customer-name-pattern": "Μοτίβο ονόματος πελάτη", + "customer-name-pattern-max-length": "Το μοτίβο ονόματος πελάτη πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "default-dashboard-name": "Προεπιλεγμένο όνομα πίνακα ελέγχου", + "default-dashboard-name-max-length": "Το όνομα πίνακα ελέγχου πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "delete-domain-text": "Προσοχή, μετά την επιβεβαίωση ο τομέας και όλα τα δεδομένα παρόχου δεν θα είναι διαθέσιμα.", + "delete-domain-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον τομέα '{{domainName}}';", + "delete-registration-text": "Προσοχή, μετά την επιβεβαίωση τα δεδομένα του παρόχου δεν θα είναι διαθέσιμα.", + "delete-registration-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον πάροχο '{{name}}';", + "email-attribute-key": "Κλειδί γνωρίσματος email", + "email-attribute-key-required": "Το κλειδί γνωρίσματος email είναι υποχρεωτικό.", + "email-attribute-key-max-length": "Το κλειδί email πρέπει να είναι μικρότερο από 32 χαρακτήρες", + "first-name-attribute-key": "Κλειδί γνωρίσματος μικρού ονόματος", + "first-name-attribute-key-max-length": "Το κλειδί μικρού ονόματος πρέπει να είναι μικρότερο από 32 χαρακτήρες", + "general": "Γενικά", + "jwk-set-uri": "URI συνόλου JWK", + "last-name-attribute-key": "Κλειδί γνωρίσματος επωνύμου", + "last-name-attribute-key-max-length": "Το κλειδί επωνύμου πρέπει να είναι μικρότερο από 32 χαρακτήρες", + "login-button-icon": "Εικονίδιο κουμπιού σύνδεσης", + "login-button-label": "Ετικέτα παρόχου", + "login-button-label-placeholder": "Σύνδεση με $(Provider label)", + "login-button-label-required": "Η ετικέτα είναι υποχρεωτική.", + "login-provider": "Πάροχος σύνδεσης", + "mapper": "Χαρτογράφος", + "new-domain": "Νέος τομέας", + "oauth2": "OAuth 2.0", + "password-max-length": "Ο κωδικός πρόσβασης πρέπει να είναι μικρότερος από 256 χαρακτήρες", + "redirect-uri-template": "Πρότυπο URI ανακατεύθυνσης", + "copy-redirect-uri": "Αντιγραφή URI ανακατεύθυνσης", + "registration-id": "Αναγνωριστικό εγγραφής", + "registration-id-required": "Το αναγνωριστικό εγγραφής είναι υποχρεωτικό.", + "registration-id-unique": "Το αναγνωριστικό εγγραφής πρέπει να είναι μοναδικό στο σύστημα.", + "scope": "Πεδίο", + "scope-required": "Το πεδίο είναι υποχρεωτικό.", + "tenant-name-pattern": "Μοτίβο ονόματος ενοικιαστή", + "tenant-name-pattern-required": "Το μοτίβο ονόματος ενοικιαστή είναι υποχρεωτικό.", + "tenant-name-pattern-max-length": "Το μοτίβο ονόματος ενοικιαστή πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "tenant-name-strategy": "Στρατηγική ονοματοδοσίας ενοικιαστή", + "type": "Τύπος χαρτογράφου", + "uri-pattern-error": "Μη έγκυρη μορφή URI.", + "url": "URL", + "url-pattern": "Μη έγκυρη μορφή URL.", + "url-required": "Το URL είναι υποχρεωτικό.", + "url-max-length": "Το URL πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "user-info-uri": "URI πληροφοριών χρήστη", + "user-info-uri-required": "Το URI πληροφοριών χρήστη είναι υποχρεωτικό.", + "username-max-length": "Το όνομα χρήστη πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "user-name-attribute-name": "Κλειδί γνωρίσματος ονόματος χρήστη", + "user-name-attribute-name-required": "Το κλειδί γνωρίσματος ονόματος χρήστη είναι υποχρεωτικό", + "protocol": "Πρωτόκολλο", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "Ενεργοποίηση ρυθμίσεων OAuth 2.0", + "disable": "Απενεργοποίηση ρυθμίσεων OAuth 2.0", + "edge": "Διάδοση στο Edge", + "edge-enable": "Ενεργοποίηση διάδοσης στο Edge", + "edge-disable": "Απενεργοποίηση διάδοσης στο Edge", + "domains": "Τομείς", + "mobile-apps": "Εφαρμογές κινητού", + "mobile-package": "Πακέτο εφαρμογής", + "mobile-package-placeholder": "Π.χ.: my.example.app", + "mobile-package-hint": "Για Android: το μοναδικό Application ID. Για iOS: Αναγνωριστικό πακέτου προϊόντος.", + "mobile-package-unique": "Το πακέτο εφαρμογής πρέπει να είναι μοναδικό.", + "mobile-package-required": "Το πακέτο εφαρμογής είναι υποχρεωτικό.", + "mobile-package-max-length": "Το πακέτο εφαρμογής πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "mobile-package-spaces": "Το πακέτο εφαρμογής δεν πρέπει να περιέχει κενά", + "mobile-app-secret": "Μυστικό εφαρμογής", + "mobile-app-secret-hint": "Συμβολοσειρά σε μορφή Base64 που αναπαριστά τουλάχιστον 512 bits δεδομένων.", + "mobile-app-secret-required": "Το μυστικό εφαρμογής είναι υποχρεωτικό.", + "mobile-app-secret-min-length": "Το μυστικό εφαρμογής πρέπει να είναι τουλάχιστον 512 bits.", + "mobile-app-secret-base64": "Το μυστικό εφαρμογής πρέπει να είναι σε μορφή base64.", + "invalid-mobile-app-secret": "Το μυστικό εφαρμογής πρέπει να περιέχει μόνο αλφαριθμητικούς χαρακτήρες και να έχει μήκος μεταξύ 16 και 2048 χαρακτήρων.", + "copy-mobile-app-secret": "Αντιγραφή μυστικού εφαρμογής", + "delete-mobile-app": "Διαγραφή πληροφοριών εφαρμογής", + "providers": "Πάροχοι", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Όλες οι πλατφόρμες", + "smtp-provider": "Πάροχος SMTP", + "allowed-platforms": "Επιτρεπόμενες πλατφόρμες", + "authentication": "Ταυτοποίηση", + "basic": "Βασική", + "provider": "Πάροχος", + "redirect-url": "URI ανακατεύθυνσης", + "domain-name": "Όνομα τομέα", + "domain-name-required": "Το όνομα τομέα είναι υποχρεωτικό", + "redirect-url-template": "Πρότυπο URI ανακατεύθυνσης", + "microsoft-tenant-id": "Αναγνωριστικό καταλόγου (ενοικιαστής)", + "microsoft-tenant-id-required": "Το αναγνωριστικό καταλόγου (ενοικιαστής) είναι υποχρεωτικό", + "token-uri": "URI διακριτικού", + "token-uri-required": "Το URI διακριτικού είναι υποχρεωτικό", + "redirect-uri": "URI ανακατεύθυνσης", + "google-provider": "Google", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "Προσαρμοσμένος", + "generate-access-token": "Δημιουργία διακριτικού πρόσβασης", + "update-access-token": "Ενημέρωση διακριτικού πρόσβασης", + "access-token-status": "Κατάσταση διακριτικού πρόσβασης:", + "token-status-generated": "δημιουργήθηκε", + "token-status-not-generated": "δεν δημιουργήθηκε" + }, + "smpp-provider": { + "smpp-version": "Έκδοση SMPP", + "smpp-host": "Διακομιστής SMPP", + "smpp-host-required": "Ο διακομιστής SMPP είναι υποχρεωτικός", + "smpp-port": "Θύρα SMPP", + "smpp-port-required": "Η θύρα SMPP είναι υποχρεωτική", + "system-id": "Αναγνωριστικό συστήματος", + "system-id-required": "Το αναγνωριστικό συστήματος είναι υποχρεωτικό", + "password": "Κωδικός πρόσβασης", + "password-required": "Ο κωδικός πρόσβασης είναι υποχρεωτικός", + "type-settings": "Ρυθμίσεις τύπου", + "source-settings": "Ρυθμίσεις προέλευσης", + "destination-settings": "Ρυθμίσεις προορισμού", + "additional-settings": "Πρόσθετες ρυθμίσεις", + "system-type": "Τύπος συστήματος", + "bind-type": "Τύπος σύνδεσης (Bind)", + "service-type": "Τύπος υπηρεσίας", + "source-address": "Διεύθυνση προέλευσης", + "source-ton": "TON προέλευσης", + "source-npi": "NPI προέλευσης", + "destination-ton": "TON προορισμού (Τύπος Αριθμού)", + "destination-npi": "NPI προορισμού (Προσδιορισμός Σχεδίου Αρίθμησης)", + "address-range": "Εύρος διευθύνσεων", + "coding-scheme": "Σχέδιο κωδικοποίησης", + "bind-type-tx": "Αποστολέας (Transmitter)", + "bind-type-rx": "Δέκτης (Receiver)", + "bind-type-trx": "Αμφίδρομος (Transceiver)", + "ton-unknown": "Άγνωστο", + "ton-international": "Διεθνές", + "ton-national": "Εθνικό", + "ton-network-specific": "Ειδικό για δίκτυο", + "ton-subscriber-number": "Αριθμός συνδρομητή", + "ton-alphanumeric": "Αλφαριθμητικό", + "ton-abbreviated": "Συντομογραφημένο", + "npi-unknown": "0 - Άγνωστο", + "npi-isdn": "1 - ISDN/σχέδιο τηλεφωνικής αρίθμησης (E163/E164)", + "npi-data-numbering-plan": "3 - Σχέδιο αρίθμησης δεδομένων (X.121)", + "npi-telex-numbering-plan": "4 - Σχέδιο αρίθμησης Telex (F.69)", + "npi-land-mobile": "6 - Κινητή τηλεφωνία (E.212)", + "npi-national-numbering-plan": "8 - Εθνικό σχέδιο αρίθμησης", + "npi-private-numbering-plan": "9 - Ιδιωτικό σχέδιο αρίθμησης", + "npi-ermes-numbering-plan": "10 - Σχέδιο αρίθμησης ERMES (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internet (IP)", + "npi-wap-client-id": "18 - Αναγνωριστικό πελάτη WAP (ορίζεται από το WAP Forum)", + "scheme-smsc": "0 - Προεπιλεγμένο αλφάβητο SMSC (ASCII για σύντομο και μακρύ κωδικό και GSM για δωρεάν)", + "scheme-ia5": "1 - IA5 (ASCII για σύντομο και μακρύ κωδικό, Latin 9 για δωρεάν - ISO-8859-9)", + "scheme-octet-unspecified-2": "2 - Octet απροσδιόριστο (8-bit δυαδικό)", + "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Octet απροσδιόριστο (8-bit δυαδικό)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Κυριλλικό (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latin/Εβραϊκό (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Κωδικοποίηση εικονογραμμάτων", + "scheme-music-codes": "10 - Μουσικοί Κωδικοί (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Επεκταμένο Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Κορεατικό σετ γραφικών χαρακτήρων (KS C 5601/KS X 1001)" + }, + "queue-select-name": "Επιλέξτε όνομα ουράς", + "queue-name": "Όνομα", + "queue-name-required": "Το όνομα της ουράς είναι υποχρεωτικό!", + "queues": "Ουρές", + "queue-partitions": "Κατατμήσεις", + "queue-submit-strategy": "Στρατηγική υποβολής", + "queue-processing-strategy": "Στρατηγική επεξεργασίας", + "queue-configuration": "Ρύθμιση ουράς", + "repository-settings": "Ρυθμίσεις αποθετηρίου", + "repository": "Αποθετήριο", + "repository-url": "URL αποθετηρίου", + "repository-url-required": "Το URL αποθετηρίου είναι υποχρεωτικό.", + "default-branch": "Όνομα προεπιλεγμένου κλάδου", + "repository-read-only": "Μόνο για ανάγνωση", + "show-merge-commits": "Εμφάνιση συγχωνευμένων commits", + "authentication-settings": "Ρυθμίσεις ταυτοποίησης", + "auth-method": "Μέθοδος ταυτοποίησης", + "auth-method-username-password": "Κωδικός / διακριτικό πρόσβασης", + "auth-method-username-password-hint": "Οι χρήστες GitHub πρέπει να χρησιμοποιούν διακριτικά με δικαιώματα εγγραφής στο αποθετήριο.", + "auth-method-private-key": "Ιδιωτικό κλειδί", + "password-access-token": "Κωδικός / διακριτικό πρόσβασης", + "change-password-access-token": "Αλλαγή κωδικού / διακριτικού πρόσβασης", + "private-key": "Ιδιωτικό κλειδί", + "drop-private-key-file-or": "Σύρετε και αφήστε ένα αρχείο ιδιωτικού κλειδιού ή", + "passphrase": "Φράση πρόσβασης", + "enter-passphrase": "Εισάγετε φράση πρόσβασης", + "change-passphrase": "Αλλαγή φράσης πρόσβασης", + "check-access": "Έλεγχος πρόσβασης", + "check-repository-access-success": "Η πρόσβαση στο αποθετήριο επαληθεύτηκε επιτυχώς!", + "delete-repository-settings-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τις ρυθμίσεις αποθετηρίου;", + "delete-repository-settings-text": "Προσοχή, μετά την επιβεβαίωση οι ρυθμίσεις του αποθετηρίου θα διαγραφούν και η δυνατότητα ελέγχου έκδοσης δεν θα είναι διαθέσιμη.", + "auto-commit-settings": "Ρυθμίσεις αυτόματης δέσμευσης", + "auto-commit": "Αυτόματη δέσμευση", + "auto-commit-entities": "Οντότητες αυτόματης δέσμευσης", + "no-auto-commit-entities-prompt": "Δεν έχουν ρυθμιστεί οντότητες για αυτόματη δέσμευση", + "delete-auto-commit-settings-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τις ρυθμίσεις αυτόματης δέσμευσης;", + "delete-auto-commit-settings-text": "Προσοχή, μετά την επιβεβαίωση οι ρυθμίσεις αυτόματης δέσμευσης θα διαγραφούν και η δυνατότητα θα απενεργοποιηθεί για όλες τις οντότητες.", + "mobile-app": { + "mobile-app": "Εφαρμογή κινητού", + "mobile-app-qr-code-widget-settings": "Ρυθμίσεις widget QR κώδικα για κινητά", + "applications": "Εφαρμογές", + "default": "Προεπιλογή", + "custom": "Προσαρμοσμένο", + "android": "Android", + "ios": "iOS", + "appearance": "Εμφάνιση", + "appearance-on-home-page": "Εμφάνιση στην αρχική σελίδα", + "enabled": "Ενεργοποιημένο", + "disabled": "Απενεργοποιημένο", + "badges": "Σήματα", + "label": "Ετικέτα", + "label-required": "Η ετικέτα είναι υποχρεωτική", + "label-max-length": "Η ετικέτα πρέπει να έχει έως 50 χαρακτήρες", + "right": "Δεξιά", + "left": "Αριστερά", + "set": "Ορισμός", + "preview": "Προεπισκόπηση", + "connect-mobile-app": "Σύνδεση εφαρμογής κινητού", + "use-system-settings": "Χρήση ρυθμίσεων συστήματος" + }, + "2fa": { + "2fa": "Έλεγχος ταυτότητας δύο παραγόντων", + "available-providers": "Διαθέσιμοι πάροχοι", + "issuer-name": "Όνομα εκδότη", + "issuer-name-required": "Το όνομα εκδότη είναι υποχρεωτικό.", + "max-verification-failures-before-user-lockout": "Μέγιστος αριθμός αποτυχημένων ελέγχων πριν από κλείδωμα χρήστη", + "max-verification-failures-before-user-lockout-pattern": "Ο μέγιστος αριθμός αποτυχιών πρέπει να είναι θετικός ακέραιος.", + "number-of-checking-attempts": "Αριθμός προσπαθειών ελέγχου", + "number-of-checking-attempts-pattern": "Ο αριθμός προσπαθειών πρέπει να είναι θετικός ακέραιος.", + "number-of-checking-attempts-required": "Ο αριθμός προσπαθειών είναι υποχρεωτικός.", + "number-of-codes": "Αριθμός κωδικών", + "number-of-codes-pattern": "Ο αριθμός κωδικών πρέπει να είναι θετικός ακέραιος.", + "number-of-codes-required": "Ο αριθμός κωδικών είναι υποχρεωτικός.", + "provider": "Πάροχος", + "retry-verification-code-period": "Χρονικό διάστημα επανάληψης εισαγωγής κωδικού (δευτ.)", + "retry-verification-code-period-pattern": "Ο ελάχιστος χρόνος είναι 5 δευτερόλεπτα", + "retry-verification-code-period-required": "Το χρονικό διάστημα επανάληψης είναι υποχρεωτικό.", + "total-allowed-time-for-verification": "Συνολικός επιτρεπόμενος χρόνος επαλήθευσης (δευτ.)", + "total-allowed-time-for-verification-pattern": "Ο ελάχιστος συνολικός χρόνος είναι 60 δευτερόλεπτα", + "total-allowed-time-for-verification-required": "Ο συνολικός χρόνος είναι υποχρεωτικός.", + "use-system-two-factor-auth-settings": "Χρήση ρυθμίσεων ελέγχου ταυτότητας δύο παραγόντων συστήματος", + "verification-code-check-rate-limit": "Όριο ρυθμού ελέγχου κωδικού επαλήθευσης", + "verification-code-lifetime": "Διάρκεια ζωής κωδικού επαλήθευσης (δευτ.)", + "verification-code-lifetime-pattern": "Η διάρκεια ζωής πρέπει να είναι θετικός ακέραιος.", + "verification-code-lifetime-required": "Η διάρκεια ζωής του κωδικού επαλήθευσης είναι υποχρεωτική.", + "verification-message-template": "Πρότυπο μηνύματος επαλήθευσης", + "verification-limitations": "Περιορισμοί επαλήθευσης", + "verification-message-template-pattern": "Το μήνυμα πρέπει να περιέχει το πρότυπο: ${code}", + "verification-message-template-required": "Το πρότυπο μηνύματος επαλήθευσης είναι υποχρεωτικό.", + "within-time": "Εντός χρόνου (δευτ.)", + "within-time-pattern": "Ο χρόνος πρέπει να είναι θετικός ακέραιος.", + "within-time-required": "Ο χρόνος είναι υποχρεωτικός." + }, + "jwt": { + "security-settings": "Ρυθμίσεις ασφαλείας JWT", + "issuer-name": "Όνομα εκδότη", + "issuer-name-required": "Το όνομα εκδότη είναι υποχρεωτικό.", + "signings-key": "Κλειδί υπογραφής", + "signings-key-hint": "Συμβολοσειρά σε μορφή Base64 που αναπαριστά τουλάχιστον 512 bits δεδομένων.", + "signings-key-required": "Το κλειδί υπογραφής είναι υποχρεωτικό.", + "signings-key-min-length": "Το κλειδί υπογραφής πρέπει να είναι τουλάχιστον 512 bits.", + "signings-key-base64": "Το κλειδί υπογραφής πρέπει να είναι σε μορφή base64.", + "expiration-time": "Χρόνος λήξης διακριτικού (δευτ.)", + "expiration-time-required": "Ο χρόνος λήξης διακριτικού είναι υποχρεωτικός.", + "expiration-time-max": "Μέγιστος επιτρεπόμενος χρόνος είναι 2147483647 δευτερόλεπτα (68 έτη).", + "expiration-time-min": "Ελάχιστος χρόνος είναι 60 δευτερόλεπτα (1 λεπτό).", + "refresh-expiration-time": "Χρόνος λήξης διακριτικού ανανέωσης (δευτ.)", + "refresh-expiration-time-required": "Ο χρόνος λήξης διακριτικού ανανέωσης είναι υποχρεωτικός.", + "refresh-expiration-time-max": "Μέγιστος επιτρεπόμενος χρόνος είναι 2147483647 δευτερόλεπτα (68 έτη).", + "refresh-expiration-time-min": "Ελάχιστος χρόνος είναι 900 δευτερόλεπτα (15 λεπτά).", + "refresh-expiration-time-less-token": "Ο χρόνος του διακριτικού ανανέωσης πρέπει να είναι μεγαλύτερος από τον χρόνο του αρχικού διακριτικού.", + "generate-key": "Δημιουργία κλειδιού", + "info-header": "Όλοι οι χρήστες θα πρέπει να επανασυνδεθούν", + "info-message": "Η αλλαγή του κλειδιού υπογραφής JWT θα καταστήσει όλα τα εκδοθέντα διακριτικά άκυρα. Όλοι οι χρήστες θα πρέπει να επανασυνδεθούν. Αυτό θα επηρεάσει επίσης scripts που χρησιμοποιούν Rest API/Websockets." + }, + "resources": "Πόροι", + "notifications": "Ειδοποιήσεις", + "notifications-settings": "Ρυθμίσεις ειδοποιήσεων", + "slack-api-token": "Slack API διακριτικό", + "slack": "Slack", + "slack-settings": "Ρυθμίσεις Slack", + "mobile-settings": "Ρυθμίσεις κινητού", + "firebase-service-account-file": "Αρχείο διαπιστευτηρίων λογαριασμού υπηρεσίας Firebase (JSON)", + "select-firebase-service-account-file": "Σύρετε και αποθέστε το αρχείο διαπιστευτηρίων Firebase ή " }, "alarm": { - "alarm": "Alarm", - "alarms": "Alarms", - "select-alarm": "Επιλογή alarm", - "no-alarms-matching": "Δεν βρέθηκαν alarms σχετικά με '{{entity}}'.", - "alarm-required": "Απαιτείται Alarm", - "alarm-status": "Κατάσταση Alarm", + "alarm": "Συναγερμός", + "alarms": "Συναγερμοί", + "all-alarms": "Όλοι οι συναγερμοί", + "select-alarm": "Επιλογή συναγερμού", + "no-alarms-matching": "Δεν βρέθηκαν συναγερμοί που να ταιριάζουν με '{{entity}}'.", + "alarm-required": "Ο συναγερμός είναι υποχρεωτικός", + "alarm-filter": "Φίλτρο συναγερμών", + "filter": "Φίλτρο", + "alarm-status": "Κατάσταση συναγερμού", + "alarm-status-list": "Λίστα κατάστασης συναγερμών", + "any-status": "Οποιαδήποτε κατάσταση", "search-status": { - "ANY": "'Ολες", - "ACTIVE": "Ενεργό", - "CLEARED": "Εκκαθαρίστηκε", - "ACK": "Επιβεβαιώθηκε", - "UNACK": "Χωρίς επιβεβαίωση" + "ANY": "Οποιαδήποτε", + "ACTIVE": "Ενεργός", + "CLEARED": "Εκκαθαρισμένος", + "ACK": "Αναγνωρισμένος", + "UNACK": "Μη αναγνωρισμένος" }, "display-status": { - "ACTIVE_UNACK": "Ενεργό χωρίς επιβεβαίωση", - "ACTIVE_ACK": "Ενεργό επιβεβαιωμένο", - "CLEARED_UNACK": "Εκκαθαρίστηκε χωρίς επιβεβαίωση", - "CLEARED_ACK": "Εκκαθαρίστηκε επιβεβαιωμένο" + "ACTIVE_UNACK": "Ενεργός μη αναγνωρισμένος", + "ACTIVE_ACK": "Ενεργός αναγνωρισμένος", + "CLEARED_UNACK": "Εκκαθαρισμένος μη αναγνωρισμένος", + "CLEARED_ACK": "Εκκαθαρισμένος αναγνωρισμένος" }, - "no-alarms-prompt": "Δεν βρέθηκαν alarm", - "created-time": "Ώρα δημιουργίας", + "no-alarms-prompt": "Δεν βρέθηκαν συναγερμοί", + "created-time": "Χρόνος δημιουργίας", "type": "Τύπος", - "severity": "Βαρύτητα", - "originator": "Δημιουργός", - "originator-type": "Τύπος δημιουργού", + "severity": "Σοβαρότητα", + "originator": "Προέλευση", + "originator-type": "Τύπος προέλευσης", "details": "Λεπτομέρειες", + "originator-label": "Ετικέτα προέλευσης", + "assign": "Ανάθεση", + "assignments": "Αναθέσεις", + "assignee": "Ανατεθειμένος", + "assignee-id": "ID ανατεθειμένου", + "assignee-first-name": "Όνομα ανατεθειμένου", + "assignee-last-name": "Επώνυμο ανατεθειμένου", + "assignee-email": "Email ανατεθειμένου", + "unassigned": "Μη ανατεθειμένος", + "user-deleted": "Ο χρήστης διαγράφηκε", + "assignee-not-set": "Όλοι", "status": "Κατάσταση", - "alarm-details": "Λεπτομέρειες Alarm", - "start-time": "Ώρα έναρξης", - "end-time": "Ώρα λήξης", - "ack-time": "Ώρα επιβεβαίωσης", - "clear-time": "Ώρα εκκαθάρισης", - "severity-critical": "Critical", - "severity-major": "Κρίσιμο", - "severity-minor": "Ασήμαντο", + "alarm-details": "Λεπτομέρειες συναγερμού", + "start-time": "Χρόνος έναρξης", + "assign-time": "Χρόνος ανάθεσης", + "end-time": "Χρόνος λήξης", + "ack-time": "Χρόνος αναγνώρισης", + "clear-time": "Χρόνος εκκαθάρισης", + "duration": "Διάρκεια", + "alarm-severity": "Σοβαρότητα συναγερμού", + "alarm-severity-list": "Λίστα σοβαρότητας συναγερμού", + "any-severity": "Οποιαδήποτε σοβαρότητα", + "severity-critical": "Κρίσιμη", + "severity-major": "Σημαντική", + "severity-minor": "Δευτερεύουσα", "severity-warning": "Προειδοποίηση", - "severity-indeterminate": "Απροσδιόριστο", - "acknowledge": "Επιβεβαίωση", + "severity-indeterminate": "Απροσδιόριστη", + "acknowledge": "Αναγνώριση", "clear": "Εκκαθάριση", - "search": "Αναζήτηση alarm", - "selected-alarms": "{ count, plural, =1 {1 alarm} other {# alarms} } επιλέχθηκαν", - "no-data": "Δεν υπάρχουν δεδομένα για εμφάνιση", - "polling-interval": "Διάστημα δειγματοληψίας alarm(sec)", - "polling-interval-required": "Απαιτείται ορισμός διαστήματος δειγματοληψίας alarm.", - "min-polling-interval-message": "Η ελάχιστη επιτρεπόμενη τιμή διαστήματος δειγματοληψίας alarm είναι 1 sec.", - "aknowledge-alarms-title": "Επιβεβαίωση { count, plural, =1 {1 alarm} other {# alarms} }", - "aknowledge-alarms-text": "Είστε σίγουρος ότι θέλετε να επιβεβαιώσετε { count, plural, =1 {1 alarm} other {# alarms} };", - "aknowledge-alarm-title": "Επιβεβαίωση Alarm", - "aknowledge-alarm-text": "Είστε σίγουρος ότι θέλετε να επιβεβαιώσετε το Alarm?", - "clear-alarms-title": "Εκκαθάριση { count, plural, =1 {1 alarm} other {# alarms} }", - "clear-alarms-text": "Είστε σίγουρος ότι θέλετε να εκκαθαρίσετε { count, plural, =1 {1 alarm} other {# alarms} }?", - "clear-alarm-title": "Εκκαθάριση Alarm", - "clear-alarm-text": "Είστε σίγουρος ότι θέλετε να εκκαθαρίσετε το Alarm?", - "alarm-status-filter": "Φίλτρο κατάστασης Alarm" + "delete": "Διαγραφή", + "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": "Είστε βέβαιοι ότι θέλετε να αναγνωρίσετε τον συναγερμό;", + "selected-alarms-are-acknowledged": "Οι επιλεγμένοι συναγερμοί έχουν ήδη αναγνωριστεί", + "clear-alarms-title": "Εκκαθάριση { count, plural, =1 {1 συναγερμού} other {# συναγερμών} }", + "clear-alarms-text": "Είστε βέβαιοι ότι θέλετε να εκκαθαρίσετε { count, plural, =1 {1 συναγερμό} other {# συναγερμούς} };", + "clear-alarm-title": "Εκκαθάριση Συναγερμού", + "clear-alarm-text": "Είστε βέβαιοι ότι θέλετε να εκκαθαρίσετε τον συναγερμό;", + "delete-alarms-title": "Διαγραφή { count, plural, =1 {1 συναγερμού} other {# συναγερμών} }", + "delete-alarms-text": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 συναγερμό} other {# συναγερμούς} };", + "selected-alarms-are-cleared": "Οι επιλεγμένοι συναγερμοί έχουν ήδη εκκαθαριστεί", + "alarm-status-filter": "Φίλτρο κατάστασης συναγερμού", + "alarm-filter-title": "Φίλτρο συναγερμού", + "assigned": "Ανατεθειμένος", + "filter-title": "Φίλτρο", + "max-count-load": "Μέγιστος αριθμός συναγερμών για φόρτωση (0 - απεριόριστο)", + "max-count-load-required": "Ο μέγιστος αριθμός συναγερμών είναι υποχρεωτικός.", + "max-count-load-error-min": "Ελάχιστη τιμή είναι 0.", + "fetch-size": "Μέγεθος φόρτωσης", + "fetch-size-required": "Το μέγεθος φόρτωσης είναι υποχρεωτικό.", + "fetch-size-error-min": "Η ελάχιστη τιμή είναι 10.", + "alarm-types": "Τύποι συναγερμών", + "alarm-type-list": "Λίστα τύπων συναγερμών", + "any-type": "Οποιοσδήποτε τύπος", + "assigned-to-current-user": "Ανατέθηκε στον τρέχοντα χρήστη", + "assigned-to-me": "Ανατέθηκε σε εμένα", + "search-propagated-alarms": "Αναζήτηση διαδομένων συναγερμών", + "comments": "Σχόλια συναγερμού", + "show-more": "Προβολή περισσότερων", + "additional-info": "Πρόσθετες πληροφορίες", + "alarm-type": "Τύπος συναγερμού", + "enter-alarm-type": "Εισαγωγή τύπου συναγερμού", + "no-alarm-types-matching": "Δεν βρέθηκαν τύποι συναγερμού που να ταιριάζουν με '{{entitySubtype}}'.", + "alarm-type-list-empty": "Δεν έχουν επιλεγεί τύποι συναγερμού." + }, + "alarm-activity": { + "add": "Προσθήκη σχολίου...", + "alarm-comment": "Σχόλιο συναγερμού", + "comments": "Σχόλια", + "delete-alarm-comment": "Θέλετε να διαγράψετε αυτό το σχόλιο;", + "refresh": "Ανανέωση", + "oldest-first": "Παλαιότερα πρώτα", + "newest-first": "Νεότερα πρώτα", + "activity": "Δραστηριότητα", + "export": "Εξαγωγή σε CSV", + "author": "Συντάκτης", + "created-date": "Ημερομηνία δημιουργίας", + "edited-date": "Ημερομηνία επεξεργασίας", + "text": "Κείμενο", + "system": "Σύστημα" }, "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": "Οντότητα από την κατάσταση του dashboard", - "filter-type-state-entity-description": "Οντότητα που λαμβάνεται από τις παραμέτρους κατάστασης του dashboard", - "filter-type-asset-type": "Τύπος Asset", - "filter-type-asset-type-description": "Assets του τύπου '{{assetTypes}}'", - "filter-type-asset-type-and-name-description": "Assets του τύπου '{{assetTypes}}' με όνομα που αρχίζει από '{{prefix}}'", - "filter-type-device-type": "Τύπος Συσκευής", - "filter-type-device-type-description": "Συσκευές του τύπου '{{deviceTypes}}'", - "filter-type-device-type-and-name-description": "Συσκευές του τύπου '{{deviceTypes}}' με όνομα που αρχίζει από '{{prefix}}'", - "filter-type-entity-view-type": "Τύπος προβολής Οντοτήτων", - "filter-type-entity-view-type-description": "Τύπος προβολής Οντοτήτων '{{entityViewTypes}}'", - "filter-type-entity-view-type-and-name-description": "Τύπος προβολής Οντοτήτων '{{entityViewTypes}}' με όνομα που αρχίζει από '{{prefix}}'", - "filter-type-relations-query": "Ερώτημα Σχέσεων", + "name": "Όνομα ψευδωνύμου", + "name-required": "Το όνομα ψευδωνύμου είναι υποχρεωτικό", + "duplicate-alias": "Υπάρχει ήδη ψευδώνυμο με το ίδιο όνομα.", + "filter-type-single-entity": "Μοναδική οντότητα", + "filter-type-entity-list": "Λίστα οντοτήτων", + "filter-type-entity-name": "Όνομα οντότητας", + "filter-type-entity-type": "Τύπος οντότητας", + "filter-type-state-entity": "Οντότητα από την κατάσταση του πίνακα ελέγχου", + "filter-type-state-entity-description": "Οντότητα από παραμέτρους κατάστασης πίνακα ελέγχου", + "filter-type-asset-type": "Τύπος περιουσιακού στοιχείου", + "filter-type-asset-type-description": "Περιουσιακά στοιχεία τύπου '{{assetTypes}}'", + "filter-type-asset-type-and-name-description": "Περιουσιακά στοιχεία τύπου '{{assetTypes}}' και με όνομα που ξεκινά με '{{prefix}}'", + "filter-type-device-type": "Τύπος συσκευής", + "filter-type-device-type-description": "Συσκευές τύπου '{{deviceTypes}}'", + "filter-type-device-type-and-name-description": "Συσκευές τύπου '{{deviceTypes}}' και με όνομα που ξεκινά με '{{prefix}}'", + "filter-type-entity-view-type": "Τύπος προβολής οντότητας", + "filter-type-entity-view-type-description": "Προβολές οντοτήτων τύπου '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description": "Προβολές οντοτήτων τύπου '{{entityViewTypes}}' και με όνομα που ξεκινά με '{{prefix}}'", + "filter-type-edge-type": "Τύπος Edge", + "filter-type-edge-type-description": "Edges τύπου '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description": "Edges τύπου '{{edgeTypes}}' και με όνομα που ξεκινά με '{{prefix}}'", + "filter-type-relations-query": "Ερώτημα σχέσεων", "filter-type-relations-query-description": "{{entities}} που έχουν σχέση {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-asset-search-query": "Ερώτημα αναζήτησης Asset", - "filter-type-asset-search-query-description": "Asset με τύπο {{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": "Δεν έχει οριστεί Φίλτρο Οντοτήτων", - "root-state-entity": "Χρήση της οντότητας κατάστασης του dashboard ως ρίζα", - "group-state-entity": "Χρήση της οντότητας κατάστασης του dashboard ως ομάδας οντοτήτων", - "root-entity": "Οντότητα ρίζας", + "filter-type-edge-search-query": "Ερώτημα αναζήτησης Edge", + "filter-type-edge-search-query-description": "Edges τύπου {{edgeTypes}} με σχέση {{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}}", + "filter-type-apiUsageState": "Κατάσταση χρήσης API", + "entity-filter": "Φίλτρο οντοτήτων", + "resolve-multiple": "Επίλυση ως πολλαπλές οντότητες", + "resolve-multiple-hint": "Ενεργοποιήστε για ταυτόχρονη εμφάνιση δεδομένων από όλες τις φιλτραρισμένες οντότητες.\nΑν απενεργοποιηθεί, το widget θα εμφανίζει δεδομένα μόνο από την επιλεγμένη οντότητα.", + "filter-type": "Τύπος φίλτρου", + "filter-type-required": "Ο τύπος φίλτρου είναι υποχρεωτικός.", + "entity-filter-no-entity-matched": "Δεν βρέθηκαν οντότητες που να ταιριάζουν με το καθορισμένο φίλτρο.", + "no-entity-filter-specified": "Δεν έχει καθοριστεί φίλτρο οντοτήτων", + "root-state-entity": "Χρήση οντότητας κατάστασης πίνακα ελέγχου ως ριζική", + "last-level-relation": "Ανάκτηση μόνο σχέσης τελευταίου επιπέδου", + "root-entity": "Ριζική οντότητα", "state-entity-parameter-name": "Όνομα παραμέτρου οντότητας κατάστασης", "default-state-entity": "Προεπιλεγμένη οντότητα κατάστασης", - "default-state-entity-group": "Προεπιλεγμένη ομάδα καταστάσεων οντοτήτων", - "default-entity-parameter-name": "Εξ ορισμού", - "max-relation-level": "Μέγιστος βαθμός συσχέτισης", - "unlimited-level": "Απεριόριστος βαθμός", - "state-entity": "Οντότητα κατάστασης του Dashboard", - "entities-of-group-state-entity": "Οντότητες από την ομάδα οντοτήτων κατάστασης του dashboard", - "all-entities": "Όλες οι Οντότητες", - "any-relation": "Οποιαδήποτε" + "default-entity-parameter-name": "Προεπιλογή", + "max-relation-level": "Μέγιστο επίπεδο σχέσης", + "unlimited-level": "Απεριόριστο επίπεδο", + "state-entity": "Οντότητα κατάστασης πίνακα ελέγχου", + "all-entities": "Όλες οι οντότητες", + "any-relation": "οποιαδήποτε" }, "asset": { - "asset": "Asset", - "assets": "Assets", - "management": "Διαχείριση Asset", - "view-assets": "Προβολή Asset", - "add": "Προσθήκη Asset", - "assign-to-customer": "Ανάθεση σε Πελάτη", - "assign-asset-to-customer": "Ανάθεση Asset(s) σε Πελάτη", - "assign-asset-to-customer-text": "Επιλέξτε τα Asset που θα αναθέσετε στον πελάτη", - "no-assets-text": "Δεν βρέθηκαν Asset", - "assign-to-customer-text": "Παρακαλώ επιλέξτε πελάτη για να αναθέσετε το Asset(s)", + "asset": "Περιουσιακό στοιχείο", + "assets": "Περιουσιακά στοιχεία", + "management": "Διαχείριση περιουσιακών στοιχείων", + "view-assets": "Προβολή περιουσιακών στοιχείων", + "add": "Προσθήκη περιουσιακού στοιχείου", + "asset-type-max-length": "Ο τύπος περιουσιακού στοιχείου πρέπει να είναι μικρότερος από 256 χαρακτήρες", + "assign-to-customer": "Ανάθεση σε πελάτη", + "assign-asset-to-customer": "Ανάθεση περιουσιακού στοιχείου(ων) σε πελάτη", + "assign-asset-to-customer-text": "Επιλέξτε τα περιουσιακά στοιχεία για ανάθεση στον πελάτη", + "no-assets-text": "Δεν βρέθηκαν περιουσιακά στοιχεία", + "assign-to-customer-text": "Επιλέξτε πελάτη για την ανάθεση του(των) περιουσιακού(ών) στοιχείου(ων)", "public": "Δημόσιο", - "assignedToCustomer": "Συνδεδεμένο με πελάτη", - "make-public": "Κάνε το Asset δημόσιο", - "make-private": "Κάνε το Asset ιδιωτικό", - "unassign-from-customer": "Αποσύνδεση από τον πελάτη", - "delete": "Διαγραφή Αsset", - "asset-public": "Το Asset είναι δημόσιο", - "asset-type": "Τύπος Asset", - "asset-type-required": "Απαιτείται ορισμός είδους Asset.", - "select-asset-type": "Επιλογή τύπου Αsset", - "enter-asset-type": "Εισαγωγή τύπου Αsset", - "any-asset": "Οποιοδήποτε Αsset", - "no-asset-types-matching": "Δεν βρέθηκαν τύποι Asset που να τιαιριάζουν με '{{entitySubtype}}'.", - "asset-type-list-empty": "Δεν επιλέχθηκε τύπος Asset.", - "asset-types": "Τύποι Asset", + "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": "Απαιτείται Όνομα.", + "name-required": "Το όνομα είναι υποχρεωτικό.", + "name-max-length": "Το όνομα πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "label-max-length": "Η ετικέτα πρέπει να είναι μικρότερη από 256 χαρακτήρες", "description": "Περιγραφή", "type": "Τύπος", - "type-required": "Απαιτείται Τύπος.", + "type-required": "Ο τύπος είναι υποχρεωτικός.", "details": "Λεπτομέρειες", - "events": "Γεγονότα", - "add-asset-text": "Προσθήκη νέου Asset", - "asset-details": "Λεπτομέρειες Asset", - "assign-assets": "Ανάθεση Assets", - "assign-assets-text": "Ανάθεση { count, plural, =1 {1 asset} other {# assets} } σε πελάτη", - "delete-assets": "Διαγραφή Assets", - "unassign-assets": "Αποσύνδεση Assets", - "unassign-assets-action-title": "Αποσύνδεση { count, plural, =1 {1 asset} other {# assets} } από πελάτη", - "assign-new-asset": "Ανάθεση νέου Asset", - "delete-asset-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το Asset '{{assetName}}'?", - "delete-asset-text": "Προσοχή!, Μετά την επιβεβαίωση, το Asset και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-assets-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 asset} other {# assets} };", - "delete-assets-action-title": "Διαγραφή { count, plural, =1 {1 asset} other {# assets} }", - "delete-assets-text": "Προσοχή! Μετά την επιβεβαίωση, όλα τα επιλεγμένα Asset και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "make-public-asset-title": "Είστε βέβαιοι ότι θέλετε να κάνετε το Asset '{{assetName}}' δημόσιο;", - "make-public-asset-text": "Μετά την επιβεβαίωση, το Asset και όλα τα στοιχεία του θα είναι προσβάσιμα από άλλους.", - "make-private-asset-title": "Είστε βέβαιοι ότι θέλετε να κάνετε το Asset '{{assetName}}' ιδιωτικό;", - "make-private-asset-text": "Μετά την επιβεβαίωση, το Asset και όλα τα στοιχεία του θα είναι ιδιωτικά και μη προσβάσιμα από άλλους.", - "unassign-asset-title": "Είστε βέβαιοι ότι θέλετε να αποσυνδέσετε το Asset '{{assetName}}';", - "unassign-asset-text": "Μετά την επιβεβαίωση, το Asset θα αποσυνδεθεί και δεν θα είναι προσβάσιμο από τον πελάτη.", - "unassign-asset": "Αποσύνδεση Asset", - "unassign-assets-title": "Είστε βέβαιοι ότι θέλετε να αποσυνδέσετε { count, plural, =1 {1 asset} other {# assets} };", - "unassign-assets-text": "Μετά την επιβεβαίωση, όλα τα επιλεγμένα Asset θα αποσυνδεθούν και δεν θα είναι προσβάσιμα από τον πελάτη.", - "copyId": "Αντιγραφή Asset Id", - "idCopiedMessage": "Το Asset Id αντιγράφηκε στο πρόχειρο", - "select-asset": "Επιλογή Asset", - "no-assets-matching": "Δεν βρέθηκαν Asset που να ταιριάζουν με '{{entity}}'.", - "asset-required": "Απαιτείται Asset", - "name-starts-with": "Όνομα Asset που ξεκινάει με", - "selected-assets": "{ count, plural, =1 {1 asset} other {# assets} } επιλέχθηκαν", - "search": "Αναζήτηση Asset", - "select-group-to-add": "Επιλέξτε ομάδα στόχο για να προσθέσετε επιλεγμένα Asset", - "select-group-to-move": "Επιλέξτε ομάδα στόχο για να μετακινήσετε επιλεγμένα assets", - "remove-assets-from-group": "Είστε βέβαιοι ότι θέλετε να καταργήσετε { count, plural, =1 {1 asset} other {# assets} } από την ομάδα '{entityGroup}';", - "group": "Ομάδα Asset", - "list-of-groups": "{ count, plural, =1 {One asset group} other {List of # asset groups} }", - "group-name-starts-with": "Ομάδες Asset των οποίων τα ονόματα ξεκινούν με '{{prefix}}'", - "import": "Εισαγωγή Asset", - "asset-file": "Αρχείο Asset" + "events": "Συμβάντα", + "add-asset-text": "Προσθήκη νέου περιουσιακού στοιχείου", + "asset-details": "Λεπτομέρειες περιουσιακού στοιχείου", + "assign-assets": "Ανάθεση περιουσιακών στοιχείων", + "assign-assets-text": "Ανάθεση { count, plural, =1 {1 περιουσιακού στοιχείου} other {# περιουσιακών στοιχείων} } σε πελάτη", + "assign-asset-to-edge-title": "Ανάθεση περιουσιακού στοιχείου(ων) σε Edge", + "assign-asset-to-edge-text": "Επιλέξτε τα περιουσιακά στοιχεία για ανάθεση στο Edge", + "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": "Αντιγραφή αναγνωριστικού περιουσιακού στοιχείου", + "idCopiedMessage": "Το αναγνωριστικό του περιουσιακού στοιχείου αντιγράφηκε στο πρόχειρο", + "select-asset": "Επιλογή περιουσιακού στοιχείου", + "no-assets-matching": "Δεν βρέθηκαν περιουσιακά στοιχεία που να ταιριάζουν με '{{entity}}'.", + "asset-required": "Απαιτείται περιουσιακό στοιχείο", + "name-starts-with": "Έκφραση ονόματος περιουσιακού στοιχείου", + "help-text": "Χρησιμοποιήστε το '%' κατά περίπτωση: '%περιουσιακό_όνομα%', '%περιουσιακό_όνομα_λήγει', 'περιουσιακό_ξεκινά_με'.", + "search": "Αναζήτηση περιουσιακών στοιχείων", + "import": "Εισαγωγή περιουσιακών στοιχείων", + "asset-file": "Αρχείο περιουσιακών στοιχείων", + "label": "Ετικέτα", + "assign-asset-to-edge": "Ανάθεση περιουσιακού στοιχείου(ων) σε Edge", + "unassign-asset-from-edge": "Αφαίρεση από Edge", + "unassign-asset-from-edge-title": "Είστε βέβαιοι ότι θέλετε να αφαιρέσετε το περιουσιακό στοιχείο '{{assetName}}' από το Edge;", + "unassign-asset-from-edge-text": "Μετά την επιβεβαίωση, το περιουσιακό στοιχείο θα αποανατεθεί και δεν θα είναι προσβάσιμο από το Edge.", + "unassign-assets-from-edge-title": "Είστε βέβαιοι ότι θέλετε να αποαναθέσετε { count, plural, =1 {1 περιουσιακό στοιχείο} other {# περιουσιακά στοιχεία} };", + "unassign-assets-from-edge-text": "Μετά την επιβεβαίωση, όλα τα επιλεγμένα περιουσιακά στοιχεία θα αποανατεθούν και δεν θα είναι προσβάσιμα από το Edge.", + "selected-assets": "{ count, plural, =1 {1 περιουσιακό στοιχείο} other {# περιουσιακά στοιχεία} } επιλεγμένα" }, "attribute": { "attributes": "Χαρακτηριστικά", - "latest-telemetry": "Τελευταία τηλεμετρία", - "attributes-scope": "Πεδίο εφαρμογής Χαρακτηριστικών Οντότητας", + "latest-telemetry": "Πρόσφατο τηλεμετρικό", + "no-latest-telemetry": "Δεν υπάρχει πρόσφατο τηλεμετρικό", + "attributes-scope": "Πεδίο χαρακτηριστικών οντότητας", "scope-telemetry": "Τηλεμετρία", - "scope-latest-telemetry": "Τελευταία τηλεμετρία", - "scope-client": "Χαρακτηριστικά Client", - "scope-server": "Χαρακτηριστικά Server", - "scope-shared": "Κοινόχρηστα Χαρακτηριστικά", - "add": "Προσθήκη Χαρακτηριστικού", - "add-attribute-prompt": "Προσθέστε Χαρακτηριστικό", - "key": "Όνομα", - "last-update-time": "Ώρα τελευταίας ενημέρωσης", - "key-required": "Απαιτείται μεταβλητή χαρακτηριστικού.", + "scope-latest-telemetry": "Πρόσφατο τηλεμετρικό", + "scope-client": "Χαρακτηριστικά πελάτη", + "scope-server": "Χαρακτηριστικά διακομιστή", + "scope-shared": "Κοινόχρηστα χαρακτηριστικά", + "scope-client-short": "Πελάτης", + "scope-server-short": "Διακομιστής", + "scope-shared-short": "Κοινόχρηστο", + "scope-latest-short": "Πρόσφατο", + "scope-any": "Οποιοδήποτε", + "add": "Προσθήκη χαρακτηριστικού", + "key": "Κλειδί", + "key-max-length": "Το κλειδί πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "last-update-time": "Χρόνος τελευταίας ενημέρωσης", + "key-required": "Το κλειδί χαρακτηριστικού είναι υποχρεωτικό.", "value": "Τιμή", - "value-required": "Απαιτείται ορισμός τιμής χαρακτηριστικού.", - "delete-attributes-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 attribute} other {# attributes} };", - "delete-attributes-text": "Προσοχή! Μετά την επιβεβαίωση θα αφαιρεθούν όλα τα επιλεγμένα χαρακτηριστικά.", + "value-required": "Η τιμή χαρακτηριστικού είναι υποχρεωτική.", + "telemetry-key-required": "Το κλειδί τηλεμετρίας είναι υποχρεωτικό", + "telemetry-value-required": "Η τιμή τηλεμετρίας είναι υποχρεωτική", + "delete-attributes-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 χαρακτηριστικό} other {# χαρακτηριστικά} };", + "delete-attributes-text": "Προσοχή, μετά την επιβεβαίωση όλα τα επιλεγμένα χαρακτηριστικά θα διαγραφούν.", "delete-attributes": "Διαγραφή χαρακτηριστικών", - "enter-attribute-value": "Καταχωρίστε την τιμή του χαρακτηριστικού", - "show-on-widget": "Εμφάνιση σε widget", - "widget-mode": "Είδος λειτουργίας widget", + "enter-attribute-value": "Εισαγωγή τιμής χαρακτηριστικού", + "show-on-widget": "Εμφάνιση στο widget", + "widget-mode": "Λειτουργία widget", "next-widget": "Επόμενο widget", "prev-widget": "Προηγούμενο widget", - "add-to-dashboard": "Προσθήκη σε dashboard", - "add-widget-to-dashboard": "Προσθήκη widget στο dashboard", - "selected-attributes": "{ count, plural, =1 {1 attribute} other {# attributes} } επιλέχθηκαν", - "selected-telemetry": "{ count, plural, =1 {1 telemetry unit} other {# telemetry units} } επιλέχθηκαν" + "add-to-dashboard": "Προσθήκη στον πίνακα ελέγχου", + "add-widget-to-dashboard": "Προσθήκη widget στον πίνακα ελέγχου", + "selected-attributes": "{ count, plural, =1 {1 χαρακτηριστικό} other {# χαρακτηριστικά} } επιλεγμένα", + "selected-telemetry": "{ count, plural, =1 {1 μονάδα τηλεμετρίας} other {# μονάδες τηλεμετρίας} } επιλεγμένες", + "no-attributes-text": "Δεν βρέθηκαν χαρακτηριστικά", + "no-telemetry-text": "Δεν βρέθηκε τηλεμετρία", + "copy-key": "Αντιγραφή κλειδιού", + "add-telemetry": "Προσθήκη τηλεμετρίας", + "copy-value": "Αντιγραφή τιμής", + "delete-timeseries": { + "start-time": "Χρόνος έναρξης", + "ends-on": "Λήγει στις", + "strategy": "Στρατηγική", + "delete-strategy": "Στρατηγική διαγραφής", + "all-data": "Διαγραφή όλων των δεδομένων", + "all-data-except-latest-value": "Διαγραφή όλων των δεδομένων εκτός από την πιο πρόσφατη τιμή", + "latest-value": "Διαγραφή πιο πρόσφατης τιμής", + "all-data-for-time-period": "Διαγραφή όλων των δεδομένων για την επιλεγμένη χρονική περίοδο", + "rewrite-latest-value": "Επανεγγραφή πιο πρόσφατης τιμής" + } + }, + "api-usage": { + "api-features": "Λειτουργίες API", + "api-usage": "Χρήση API", + "alarm": "Συναγερμός", + "alarms-created": "Δημιουργήθηκαν συναγερμοί", + "queue-stats": "Στατιστικά ουράς", + "processing-failures-and-timeouts": "Αποτυχίες επεξεργασίας και χρονικά όρια", + "exceptions": "Εξαιρέσεις", + "alarms-created-daily-activity": "Ημερήσια δραστηριότητα δημιουργίας συναγερμών", + "alarms-created-hourly-activity": "Ωριαία δραστηριότητα δημιουργίας συναγερμών", + "alarms-created-monthly-activity": "Μηνιαία δραστηριότητα δημιουργίας συναγερμών", + "data-points": "Σημεία δεδομένων", + "data-points-storage-days": "Ημέρες αποθήκευσης σημείων δεδομένων", + "device-api": "API συσκευής", + "email": "Email", + "email-messages": "Μηνύματα Email", + "email-messages-daily-activity": "Ημερήσια δραστηριότητα Email", + "email-messages-monthly-activity": "Μηνιαία δραστηριότητα Email", + "executions": "Εκτελέσεις", + "scripts": "Σενάρια", + "scripts-hourly-activity": "Ωριαία δραστηριότητα σεναρίων", + "scripts-daily-activity": "Ημερήσια δραστηριότητα σεναρίων", + "scripts-monthly-activity": "Μηνιαία δραστηριότητα σεναρίων", + "javascript": "JavaScript", + "javascript-executions": "Εκτελέσεις JavaScript", + "tbel": "TBEL", + "tbel-executions": "Εκτελέσεις TBEL", + "latest-error": "Πρόσφατο σφάλμα", + "messages": "Μηνύματα", + "notifications": "Ειδοποιήσεις", + "notifications-email-sms": "Ειδοποιήσεις (Email/SMS)", + "notifications-hourly-activity": "Ωριαία δραστηριότητα ειδοποιήσεων", + "permanent-failures": "Μόνιμες αποτυχίες ${entityName}", + "permanent-timeouts": "Μόνιμα χρονικά όρια ${entityName}", + "processing-failures": "Αποτυχίες επεξεργασίας ${entityName}", + "processing-timeouts": "Χρονικά όρια επεξεργασίας ${entityName}", + "rule-chain": "Αλυσίδα κανόνων", + "rule-engine": "Μηχανή κανόνων", + "rule-engine-daily-activity": "Ημερήσια δραστηριότητα μηχανής κανόνων", + "rule-engine-executions": "Εκτελέσεις μηχανής κανόνων", + "rule-engine-hourly-activity": "Ωριαία δραστηριότητα μηχανής κανόνων", + "rule-engine-monthly-activity": "Μηνιαία δραστηριότητα μηχανής κανόνων", + "rule-engine-statistics": "Στατιστικά μηχανής κανόνων", + "rule-node": "Κόμβος κανόνα", + "sms": "SMS", + "sms-messages": "Μηνύματα SMS", + "sms-messages-daily-activity": "Ημερήσια δραστηριότητα SMS", + "sms-messages-monthly-activity": "Μηνιαία δραστηριότητα SMS", + "successful": "Επιτυχίες ${entityName}", + "telemetry": "Τηλεμετρία", + "telemetry-persistence": "Διατήρηση τηλεμετρίας", + "telemetry-persistence-daily-activity": "Ημερήσια δραστηριότητα διατήρησης τηλεμετρίας", + "telemetry-persistence-hourly-activity": "Ωριαία δραστηριότητα διατήρησης τηλεμετρίας", + "telemetry-persistence-monthly-activity": "Μηνιαία δραστηριότητα διατήρησης τηλεμετρίας", + "transport": "Μεταφορά", + "transport-daily-activity": "Ημερήσια δραστηριότητα μεταφοράς", + "transport-data-points": "Σημεία δεδομένων μεταφοράς", + "transport-hourly-activity": "Ωριαία δραστηριότητα μεταφοράς", + "transport-messages": "Μηνύματα μεταφοράς", + "transport-monthly-activity": "Μηνιαία δραστηριότητα μεταφοράς", + "view-details": "Προβολή λεπτομερειών", + "view-statistics": "Προβολή στατιστικών" + }, + "api-limit": { + "cassandra-queries": "Ερωτήματα Cassandra", + "entity-version-creation": "Δημιουργία έκδοσης οντότητας", + "entity-version-load": "Φόρτωση έκδοσης οντότητας", + "notification-requests": "Αιτήματα ειδοποίησης", + "notification-requests-per-rule": "Αιτήματα ειδοποίησης ανά κανόνα", + "rest-api-requests": "Αιτήματα REST API", + "rest-api-requests-per-customer": "Αιτήματα REST API ανά πελάτη", + "transport-messages": "Μηνύματα μεταφοράς", + "transport-messages-per-device": "Μηνύματα μεταφοράς ανά συσκευή", + "transport-messages-per-gateway": "Μηνύματα μεταφοράς ανά πύλη", + "transport-messages-per-gateway-device": "Μηνύματα μεταφοράς ανά συσκευή πύλης", + "ws-updates-per-session": "Ενημερώσεις WS ανά συνεδρία", + "edge-events": "Γεγονότα Edge", + "edge-events-per-edge": "Γεγονότα Edge ανά Edge", + "edge-uplink-messages": "Μηνύματα uplink Edge", + "edge-uplink-messages-per-edge": "Μηνύματα uplink ανά Edge" }, "audit-log": { - "audit": "Καταγραφή", - "audit-logs": "Ημερολόγια Καταγραφής", - "timestamp": "Ώρα", - "entity-type": "Είδος Οντότητας", - "entity-name": "Όνομα Οντότητας", + "audit": "Έλεγχος", + "audit-logs": "Αρχεία καταγραφής ελέγχου", + "timestamp": "Χρονική σήμανση", + "entity-type": "Τύπος οντότητας", + "entity-name": "Όνομα οντότητας", "user": "Χρήστης", "type": "Τύπος", "status": "Κατάσταση", @@ -329,2182 +947,6268 @@ "type-added": "Προστέθηκε", "type-deleted": "Διαγράφηκε", "type-updated": "Ενημερώθηκε", - "type-attributes-updated": "Τα χαρακτηριστικά ενημερώθηκαν", - "type-attributes-deleted": "Τα χαρακτηριστικά διαγράφηκαν", + "type-attributes-updated": "Ενημερώθηκαν χαρακτηριστικά", + "type-attributes-deleted": "Διαγράφηκαν χαρακτηριστικά", "type-rpc-call": "Κλήση RPC", - "type-credentials-updated": "Τα διαπιστευτήρια ενημερώθηκαν", - "type-assigned-to-customer": "Ανατέθηκε σε Πελάτη", - "type-unassigned-from-customer": "Αποσυνδέθηκε από Πελάτη", + "type-credentials-updated": "Ενημερώθηκαν διαπιστευτήρια", + "type-assigned-to-customer": "Ανατέθηκε σε πελάτη", + "type-unassigned-from-customer": "Αποανατέθηκε από πελάτη", + "type-assigned-to-edge": "Ανατέθηκε σε Edge", + "type-unassigned-from-edge": "Αποανατέθηκε από Edge", "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": "Relation updated", - "type-relation-delete": "Η συσχέτιση ενημερώθηκε", - "type-relations-delete": "Όλες οι συσχετίσεις διαγράφηκαν", - "type-alarm-ack": "Επιβεβαιώθηκε", - "type-alarm-clear": "Εκκαθαρίστηκε", - "type-rest-api-rule-engine-call": "Κλήση Rule engine REST API", - "type-made-public": "Έγινε δημόσιο", - "type-made-private": "Έγινε ιδιωτικό", + "type-suspended": "Ανεστάλη", + "type-credentials-read": "Αναγνώστηκαν διαπιστευτήρια", + "type-attributes-read": "Αναγνώστηκαν χαρακτηριστικά", + "type-relation-add-or-update": "Η σχέση ενημερώθηκε", + "type-relation-delete": "Η σχέση διαγράφηκε", + "type-relations-delete": "Όλες οι σχέσεις διαγράφηκαν", + "type-alarm-ack": "Ο συναγερμός αναγνωρίστηκε", + "type-alarm-clear": "Ο συναγερμός εκκαθαρίστηκε", + "type-alarm-delete": "Ο συναγερμός διαγράφηκε", + "type-alarm-assign": "Ο συναγερμός ανατέθηκε", + "type-alarm-unassign": "Ο συναγερμός αποανατέθηκε", + "type-added-comment": "Προστέθηκε σχόλιο", + "type-updated-comment": "Ενημερώθηκε σχόλιο", + "type-deleted-comment": "Διαγράφηκε σχόλιο", + "type-login": "Είσοδος", + "type-logout": "Έξοδος", + "type-lockout": "Κλείδωμα", "status-success": "Επιτυχία", "status-failure": "Αποτυχία", - "audit-log-details": "΄Λεπτομέρειες καταγραφής", - "no-audit-logs-prompt": "Δεν βρέθηκαν αρχεία καταγραφής", - "action-data": "Δεδομένα ενεργειών", + "audit-log-details": "Λεπτομέρειες αρχείου καταγραφής ελέγχου", + "no-audit-logs-prompt": "Δεν βρέθηκαν καταγραφές", + "action-data": "Δεδομένα ενέργειας", "failure-details": "Λεπτομέρειες αποτυχίας", - "search": "Αναζήτηση αρχείων καταγραφής", - "clear-search": "΄Καθαρισμός αναζήτησης" + "search": "Αναζήτηση καταγραφών ελέγχου", + "clear-search": "Καθαρισμός αναζήτησης", + "type-assigned-from-tenant": "Ανατέθηκε από ενοικιαστή", + "type-assigned-to-tenant": "Ανατέθηκε σε ενοικιαστή", + "type-provision-success": "Η συσκευή εγκαταστάθηκε", + "type-provision-failure": "Η εγκατάσταση της συσκευής απέτυχε", + "type-timeseries-updated": "Ενημερώθηκε τηλεμετρία", + "type-timeseries-deleted": "Διαγράφηκε τηλεμετρία", + "type-sms-sent": "SMS στάλθηκε" + }, + "debug-settings": { + "label": "Ρυθμίσεις εντοπισμού σφαλμάτων", + "on-failure": "Μόνο αποτυχίες (24/7)", + "all-messages": "Όλα τα μηνύματα ({{time}})", + "failures": "Αποτυχίες", + "entity": "οντότητα", + "hint": { + "main-limited": "Δεν θα καταγραφούν περισσότερα από {{msg}} μηνύματα εντοπισμού για την {{entity}} ανά {{time}}.", + "on-failure": "Καταγραφή μόνο μηνυμάτων σφάλματος.", + "all-messages": "Καταγραφή όλων των μηνυμάτων εντοπισμού." + } + }, + "calculated-fields": { + "expression": "Έκφραση", + "no-found": "Δεν βρέθηκαν υπολογιζόμενα πεδία", + "list": "{ count, plural, =1 {Ένα υπολογιζόμενο πεδίο} other {Λίστα με # υπολογιζόμενα πεδία} }", + "selected-fields": "{ count, plural, =1 {1 υπολογιζόμενο πεδίο} other {# υπολογιζόμενα πεδία} } επιλεγμένα", + "type": { + "simple": "Απλό", + "script": "Script" + }, + "arguments": "Ορίσματα", + "decimals-by-default": "Αριθμοί μετά την υποδιαστολή (προεπιλογή)", + "debugging": "Εντοπισμός σφαλμάτων υπολογιζόμενου πεδίου", + "argument-name": "Όνομα ορίσματος", + "datasource": "Πηγή δεδομένων", + "add-argument": "Προσθήκη ορίσματος", + "test-script-function": "Δοκιμή συνάρτησης script", + "no-arguments": "Δεν έχουν οριστεί ορίσματα", + "argument-settings": "Ρυθμίσεις ορίσματος", + "argument-current": "Τρέχουσα οντότητα", + "argument-current-tenant": "Τρέχων ενοικιαστής", + "argument-device": "Συσκευή", + "argument-asset": "Περιουσιακό στοιχείο", + "argument-customer": "Πελάτης", + "argument-tenant": "Τρέχων ενοικιαστής", + "argument-type": "Τύπος ορίσματος", + "see-debug-events": "Προβολή γεγονότων εντοπισμού σφαλμάτων", + "attribute": "Χαρακτηριστικό", + "copy-argument-name": "Αντιγραφή ονόματος ορίσματος", + "timeseries-key": "Κλειδί χρονοσειράς", + "device-name": "Όνομα συσκευής", + "latest-telemetry": "Πρόσφατο τηλεμετρικό", + "rolling": "Συσσώρευση χρονοσειράς", + "attribute-scope": "Πεδίο χαρακτηριστικού", + "server-attributes": "Χαρακτηριστικά διακομιστή", + "client-attributes": "Χαρακτηριστικά πελάτη", + "shared-attributes": "Κοινόχρηστα χαρακτηριστικά", + "attribute-key": "Κλειδί χαρακτηριστικού", + "default-value": "Προεπιλεγμένη τιμή", + "limit": "Μέγιστες τιμές", + "time-window": "Παράθυρο χρόνου", + "customer-name": "Όνομα πελάτη", + "asset-name": "Όνομα περιουσιακού στοιχείου", + "timeseries": "Χρονοσειρά", + "output": "Έξοδος", + "create": "Δημιουργία νέου υπολογιζόμενου πεδίου", + "file": "Αρχείο υπολογιζόμενου πεδίου", + "invalid-file-error": "Μη έγκυρη μορφή αρχείου. Βεβαιωθείτε ότι είναι έγκυρο αρχείο JSON.", + "import": "Εισαγωγή υπολογιζόμενου πεδίου", + "export": "Εξαγωγή υπολογιζόμενου πεδίου", + "export-failed-error": "Αποτυχία εξαγωγής υπολογιζόμενου πεδίου: {{error}}", + "output-type": "Τύπος εξόδου", + "delete-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το υπολογιζόμενο πεδίο '{{title}}';", + "delete-text": "Προσοχή, μετά την επιβεβαίωση το υπολογιζόμενο πεδίο και όλα τα σχετικά δεδομένα δεν θα είναι ανακτήσιμα.", + "delete-multiple-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 υπολογιζόμενο πεδίο} other {# υπολογιζόμενα πεδία} };", + "delete-multiple-text": "Προσοχή, μετά την επιβεβαίωση όλα τα επιλεγμένα υπολογιζόμενα πεδία θα διαγραφούν και όλα τα σχετικά δεδομένα δεν θα είναι ανακτήσιμα.", + "test-with-this-message": "Δοκιμή με αυτό το μήνυμα", + "hint": { + "arguments-simple-with-rolling": "Το απλού τύπου υπολογιζόμενο πεδίο δεν πρέπει να περιλαμβάνει κλειδιά με τύπο συσσώρευσης χρονοσειράς.", + "arguments-empty": "Τα ορίσματα δεν πρέπει να είναι κενά.", + "expression-required": "Η έκφραση είναι υποχρεωτική.", + "expression-invalid": "Η έκφραση δεν είναι έγκυρη", + "expression-max-length": "Το μήκος της έκφρασης πρέπει να είναι μικρότερο από 255 χαρακτήρες.", + "argument-name-required": "Το όνομα ορίσματος είναι υποχρεωτικό.", + "argument-name-pattern": "Το όνομα ορίσματος δεν είναι έγκυρο.", + "argument-name-duplicate": "Υπάρχει ήδη όρισμα με αυτό το όνομα.", + "argument-name-max-length": "Το όνομα ορίσματος πρέπει να είναι μικρότερο από 256 χαρακτήρες.", + "argument-name-forbidden": "Το όνομα ορίσματος είναι δεσμευμένο και δεν μπορεί να χρησιμοποιηθεί.", + "argument-type-required": "Ο τύπος ορίσματος είναι υποχρεωτικός.", + "max-args": "Έχει επιτευχθεί ο μέγιστος αριθμός ορισμάτων.", + "decimals-range": "Οι δεκαδικοί στην προεπιλογή πρέπει να είναι αριθμός από 0 έως 15.", + "expression": "Η προεπιλεγμένη έκφραση δείχνει πώς να μετατραπεί η θερμοκρασία από Φαρενάιτ σε Κελσίου.", + "arguments-entity-not-found": "Η οντότητα-στόχος του ορίσματος δεν βρέθηκε." + } }, "confirm-on-exit": { - "message": "Έχετε μη αποθηκευμένες αλλαγές. Είστε βέβαιοι ότι θέλετε να φύγετε από τη σελίδα;", - "html-message": "Έχετε μη αποθηκευμένες αλλαγές.
Είστε βέβαιοι ότι θέλετε να φύγετε από τη σελίδα;", + "message": "Έχετε μη αποθηκευμένες αλλαγές. Είστε βέβαιοι ότι θέλετε να φύγετε από αυτήν τη σελίδα;", + "html-message": "Έχετε μη αποθηκευμένες αλλαγές.
Είστε βέβαιοι ότι θέλετε να φύγετε από αυτήν τη σελίδα;", "title": "Μη αποθηκευμένες αλλαγές" }, "contact": { "country": "Χώρα", + "country-required": "Η χώρα είναι υποχρεωτική.", "city": "Πόλη", - "state": "Περιφέρεια", + "state": "Πολιτεία / Περιφέρεια", "postal-code": "Ταχυδρομικός Κώδικας", "postal-code-invalid": "Μη έγκυρη μορφή ταχυδρομικού κώδικα.", - "address": "Διεύθυνση (γραμμή 1)", - "address2": "Διεύθυνση (γραμμή 2)", + "address": "Διεύθυνση", + "address2": "Διεύθυνση 2", "phone": "Τηλέφωνο", "email": "Email", - "no-address": "Χωρίς διεύθυνση" + "no-address": "Δεν υπάρχει διεύθυνση", + "no-country-found": "Δεν βρέθηκαν χώρες.", + "no-country-matching": "Δεν βρέθηκε χώρα που να ταιριάζει με '{{country}}'.", + "state-max-length": "Το μήκος της πολιτείας πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "phone-max-length": "Ο αριθμός τηλεφώνου πρέπει να είναι μικρότερος από 256 χαρακτήρες", + "city-max-length": "Η πόλη πρέπει να είναι μικρότερη από 256 χαρακτήρες" }, "common": { + "name": "Όνομα", + "type": "Τύπος", + "general": "Γενικά", "username": "Όνομα χρήστη", "password": "Κωδικός πρόσβασης", - "enter-username": "Εισάγετε Όνομα χρήστη", - "enter-password": "Εισάγετε Κωδικό πρόσβασης", - "enter-search": "Αναζήτηση", - "created-time": "Δημιουργήθηκε" - }, - "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 data converter} other {# data converters} } επιλέχθηκαν", - "delete-converter-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον μετατροπέα δεδομένων '{{converterName}}'?", - "delete-converter-text": "Προσέξτε, μετά την επιβεβαίωση, ο μετατροπέας δεδομένων και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-converters-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 data converter} other {# data converters} };", - "delete-converters-action-title": "Διαγραφή { count, plural, =1 {1 data converter} other {# data converters} }", - "delete-converters-text": "Προσοχή! Μετά την επιβεβαίωση, όλοι οι επιλεγμένοι μετατροπείς δεδομένων και όλα τα σχετικά δεδομένα θα καταστούν μη ανακτήσιμα.", - "events": "Γεγονότα", - "add": "Προσθήκη μετατροπέα δεδομένων", - "converter-details": "Λεπτομέρειες μετατροπέα δεδομένων", - "details": "Λεπτομέρειες", - "copyId": "Αντιγραφή ID μετατροπέα δεδομένων", - "idCopiedMessage": "Το ID του μετατροπέα δεδομένων αντιγράφηκε στο πρόχειρο.", - "debug-mode": "Λειτουργία εντοπισμού σφαλμάτων", - "name": "Όνομα", - "name-required": "Απαιτείται Όνομα.", - "description": "Περιγραφή", - "decoder": "Αποκωδικοποιητής", - "encoder": "Kωδικοποιητής", - "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": "Οι καταχωρήσεις μεταδεδομένων ενσωμάτωσης δεν μπορούν να είναι κενές.", + "data": "Δεδομένα", + "timestamp": "Χρονική σήμανση", + "enter-username": "Εισάγετε όνομα χρήστη", + "enter-password": "Εισάγετε κωδικό πρόσβασης", + "enter-search": "Εισάγετε αναζήτηση", + "created-time": "Χρόνος δημιουργίας", + "disabled": "Απενεργοποιημένο", + "loading": "Φόρτωση...", + "proceed": "Συνέχεια", + "open-details-page": "Άνοιγμα σελίδας λεπτομερειών", + "not-found": "Δεν βρέθηκε", + "value": "Τιμή", + "documentation": "Τεκμηρίωση", + "time-left": "Απομένει {{time}}", "output": "Έξοδος", - "import": "Εισαγωγή μετατροπέα", - "export": "Εξαγωγή μετατροπέα", - "export-failed-error": "Δεν είναι δυνατή η εξαγωγή του μετατροπέα: {{error}}", - "create-new-converter": "Δημιουργία νέου μετατροπέα", - "converter-file": "Αρχείο μετατροπέα", - "invalid-converter-file-error": "Δεν είναι δυνατή η εισαγωγή του μετατροπέα: Μη έγκυρη δομή δεδομένων μετατροπέα.", - "type": "Τύπος", - "type-required": "Απαιτείται τύπος.", - "type-uplink": "Uplink", - "type-downlink": "Downlink" + "suffix": { + "s": "δ", + "ms": "ms" + }, + "hint": { + "name-required": "Το όνομα είναι υποχρεωτικό.", + "name-pattern": "Το όνομα δεν είναι έγκυρο.", + "name-max-length": "Το όνομα πρέπει να είναι μικρότερο από 256 χαρακτήρες.", + "title-required": "Ο τίτλος είναι υποχρεωτικός.", + "title-pattern": "Ο τίτλος δεν είναι έγκυρος.", + "title-max-length": "Ο τίτλος πρέπει να είναι μικρότερος από 256 χαρακτήρες.", + "key-required": "Το κλειδί είναι υποχρεωτικό.", + "key-pattern": "Το κλειδί δεν είναι έγκυρο.", + "key-max-length": "Το κλειδί πρέπει να είναι μικρότερο από 256 χαρακτήρες." + }, + "required-fields": "Λείπουν υποχρεωτικά πεδία" }, "content-type": { "json": "Json", - "text": "Text", - "binary": "Binary (Base64)" + "text": "Κείμενο", + "binary": "Δυαδικό (Base64)" + }, + "color": { + "color": "Χρώμα" }, "customer": { "customer": "Πελάτης", "customers": "Πελάτες", - "management": "Διαχείριση Πελατών", - "dashboard": "Dashboard Πελάτη", - "dashboards": "Dashboards Πελάτη", - "devices": "Συσκευές Πελάτη", - "entity-views": "Προβολές Οντοτήτων ΠελάτηCustomer Entity Views", - "assets": "Assets Πελάτη", - "public-dashboards": "Δημόσια Dashboards", - "public-devices": "Δημόσιες Devices", - "public-assets": "Δημόσια Assets", - "public-entity-views": "Δημόσιες Προβολές Οντοτήτων", - "add": "Προσθήκη Πελάτη", + "management": "Διαχείριση πελατών", + "dashboard": "Πίνακας ελέγχου πελάτη", + "dashboards": "Πίνακες ελέγχου πελάτη", + "devices": "Συσκευές πελάτη", + "entity-views": "Προβολές οντοτήτων πελάτη", + "assets": "Περιουσιακά στοιχεία πελάτη", + "public-dashboards": "Δημόσιοι πίνακες ελέγχου", + "public-devices": "Δημόσιες συσκευές", + "public-assets": "Δημόσια περιουσιακά στοιχεία", + "public-entity-views": "Δημόσιες προβολές οντοτήτων", + "add": "Προσθήκη πελάτη", "delete": "Διαγραφή πελάτη", - "manage-customer-user-groups": "Διαχείριση ομάδων χρηστών πελατών", - "manage-customer-groups": "Διαχείριση ομάδων πελατών", - "manage-customer-device-groups": "Διαχείριση ομάδων συσκευών πελατών", - "manage-customer-asset-groups": "Διαχείριση ομάδων Asset πελατών", - "manage-customer-entity-view-groups": "Διαχείριση ομάδων προβολής οντότητας πελατών", - "manage-customer-dashboard-groups": "Διαχείριση ομάδων dashboard πελατών", - "manage-customer-users": "Διαχείριση χρηστών πελατών", - "manage-customers": "Διαχείριση πελατών", - "manage-customer-devices": "Διαχείρηση συσκευών πελατών", - "manage-customer-entity-views": "Διαχείριση προβολής οντότητας πελατών", - "manage-customer-dashboards": "Διαχείριση dashboards πελατών", + "manage-customer-users": "Διαχείριση χρηστών πελάτη", + "manage-customer-devices": "Διαχείριση συσκευών πελάτη", + "manage-customer-dashboards": "Διαχείριση πινάκων ελέγχου πελάτη", "manage-public-devices": "Διαχείριση δημόσιων συσκευών", - "manage-public-dashboards": "Διαχείριση δημόσιων συσκευών", - "manage-customer-assets": "Διαχείριση οντοτήτων πελατών", - "manage-public-assets": "Διαχείριση δημόσιων οντοτήτων", + "manage-public-dashboards": "Διαχείριση δημόσιων πινάκων ελέγχου", + "manage-customer-assets": "Διαχείριση περιουσιακών στοιχείων πελάτη", + "manage-customer-edges": "Διαχείριση Edge πελάτη", + "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 customer} other {# customers} }?", - "delete-customers-action-title": "Διαγραφή { count, plural, =1 {1 customer} other {# customers} }", - "delete-customers-text": "Προσέξτε, μετά την επιβεβαίωση, οι πελάτες και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "manage-user-groups": "Διαχείρηση ομαδών χρηστών", - "manage-asset-groups": "Διαχείρηση ομαδών οντοτήτων", - "manage-device-groups": "Διαχείρηση ομαδών συσκευών", - "manage-dashboard-groups": "Διαχείρηση ομαδών dashboards", - "manage-entity-view-groups": "Διαχείρηση ομαδών προβολής οντοτήτων", - "manage-users": "Διαχείρηση χρηστών", - "manage-assets": "Διαχείρηση οντοτήτων", - "manage-devices": "Διαχείρηση συσκευών", - "manage-dashboards": "Διαχείρηση dashboards", + "delete-customer-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον πελάτη '{{customerTitle}}';", + "delete-customer-text": "Προσοχή, μετά την επιβεβαίωση ο πελάτης και όλα τα σχετικά δεδομένα δεν θα είναι ανακτήσιμα.", + "delete-customers-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 πελάτη} other {# πελάτες} };", + "delete-customers-action-title": "Διαγραφή { count, plural, =1 {1 πελάτη} other {# πελατών} }", + "delete-customers-text": "Προσοχή, μετά την επιβεβαίωση όλοι οι επιλεγμένοι πελάτες θα διαγραφούν και όλα τα σχετικά δεδομένα δεν θα είναι ανακτήσιμα.", + "manage-users": "Διαχείριση χρηστών", + "manage-assets": "Διαχείριση περιουσιακών στοιχείων", + "manage-devices": "Διαχείριση συσκευών", + "manage-dashboards": "Διαχείριση πινάκων ελέγχου", "title": "Τίτλος", - "title-required": "Απαιτείται ένας τίτλος.", + "title-required": "Ο τίτλος είναι υποχρεωτικός.", + "title-max-length": "Ο τίτλος πρέπει να είναι μικρότερος από 256 χαρακτήρες", "description": "Περιγραφή", "details": "Λεπτομέρειες", - "events": "Γεγονότα", - "copyId": "Αντιγραφή ID πελάτη", - "idCopiedMessage": "Το ID του πελάτη έχει αντιγραφεί στο πρόχειρο", + "events": "Συμβάντα", + "copyId": "Αντιγραφή αναγνωριστικού πελάτη", + "idCopiedMessage": "Το αναγνωριστικό πελάτη αντιγράφηκε στο πρόχειρο", "select-customer": "Επιλέξτε πελάτη", - "no-customers-matching": "Δεν βρέθηκαν πελάτες που να αντιστοιχούν σε '{{entity}}'.", + "no-customers-matching": "Δεν βρέθηκαν πελάτες που να ταιριάζουν με '{{entity}}'.", "customer-required": "Απαιτείται πελάτης", - "selected-customers": "{ count, plural, =1 {1 customer} other {# customers} } επιλέχθηκαν", - "search": "Αναζήτηση πελατών", - "select-group-to-add": "Επιλέξτε ομάδα για να προσθέσετε τους επιλεγμένους πελάτες", - "select-group-to-move": "Επιλέξτε ομάδα για να μετακινήσετε τους επιλεγμένους πελάτες", - "remove-customers-from-group": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε { count, plural, =1 {1 customer} other {# customers} } από την ομάδα '{entityGroup}'?", - "group": "Ομάδα πελατών", - "list-of-groups": "{ count, plural, =1 {One customer group} other {List of # customer groups} }", - "group-name-starts-with": "Πελάτες των οποίων το όνομα αρχίζει από '{{prefix}}'", - "select-default-customer": "Επιλογή προεπιλεγμένου πελάτη", + "select-default-customer": "Επιλέξτε προεπιλεγμένο πελάτη", "default-customer": "Προεπιλεγμένος πελάτης", - "default-customer-required": "Ο προεπιλεγμένος πελάτης είναι υποχρεωτικός για να είναι δυνατή η απασφαλμάτωση του dashboard από τον Tenant", - "allow-white-labeling": "Επιτρέψτε το White Labeling" - }, - "customers-hierarchy": { - "customers-hierarchy": "Ιεραρχία Πελατών", - "open-nav-tree": "Άνοιγμα διακλάδωσης", - "return-to-top-level": "Επιστροφή στο αρχικό επίπεδο" + "default-customer-required": "Απαιτείται προεπιλεγμένος πελάτης για εντοπισμό σφαλμάτων στον πίνακα ελέγχου επιπέδου ενοικιαστή", + "search": "Αναζήτηση πελατών", + "selected-customers": "{ count, plural, =1 {1 πελάτης} other {# πελάτες} } επιλεγμένοι", + "edges": "Παραδείγματα Edge πελάτη", + "manage-edges": "Διαχείριση Edge" }, - "custom-menu": { - "custom-menu": "Προσαρμοσμένο Μενού", - "custom-menu-hint": "Ορίστε το προσαρμοσμένο μενού (JSON) παρακάτω. Αυτό το JSON περιέχει μια λίστα με προσαρμοσμένα στοιχεία μενού." + "css-size": { + "size-value-required": "Η τιμή μεγέθους είναι υποχρεωτική", + "invalid-size-value": "Μη έγκυρη τιμή μεγέθους" }, - "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": "Λήψη αρχείου γλώσσας" + "date": { + "last-update-n-ago": "Τελευταία ενημέρωση πριν από Ν", + "last-update-n-ago-text": "Τελευταία ενημέρωση πριν από {{ agoText }}", + "custom-date": "Προσαρμοσμένη ημερομηνία", + "format": "Μορφή", + "preview": "Προεπισκόπηση", + "auto": "Αυτόματο", + "time-granularity-formats": "Μορφές χρονικής ακρίβειας", + "unit-year": "Χρόνια", + "unit-month": "Μήνες", + "unit-day": "Ημέρες", + "unit-hour": "Ώρες", + "unit-minute": "Λεπτά", + "unit-second": "Δευτερόλεπτα", + "unit-millisecond": "Χιλιοστά του δευτερολέπτου" }, "datetime": { - "date-from": "Ημ/νία από", + "date-from": "Ημερομηνία από", "time-from": "Ώρα από", - "date-to": "Ημ/νία έως", - "time-to": "Ώρα έως" + "date-to": "Ημερομηνία έως", + "time-to": "Ώρα έως", + "from": "Από", + "to": "Έως" }, "dashboard": { "dashboard": "Dashboard", "dashboards": "Dashboards", - "management": "Διαχείριση Dashboard", - "view-dashboards": "Προβολή Dashboards", - "add": "Προσθήκη Dashboard", - "assign-dashboard-to-customer": "Ανάθεση Dashboard(s) Σε Πελάτη", - "assign-dashboard-to-customer-text": "Παρακαλώ επιλέξτε τα dashboards που θέλετε να αναθέσετε σε πελάτη", - "assign-to-customer-text": "Παρακαλώ επιλέξτε τον πελάτη στον οποίο θέλετε να αναθέσετε το/τα dashboard(s)", - "assign-to-customer": "Ανάθεση σε πελάτη", - "unassign-from-customer": "Αφαίρεση από πελάτη", - "make-public": "Δημοσιοποιήση Dashboard", - "make-private": "Ιδιωτικοποίηση Dashboard", - "manage-assigned-customers": "Διαχείρηση ανατεθειμένων πελατών", - "assigned-customers": "Ανατεθειμένοι πελάτες", - "assign-to-customers": "Ανάθεση Dashboard(s) Σε Πελάτες", - "assign-to-customers-text": "Παρακαλώ επιλέξτε τους πελάτες στους οποίους θέλετε να αναθέσετε το/τα dashboard(s)", - "unassign-from-customers": "Μη Ανατεθειμένα Dashboard(s) Από Πελάτες", - "unassign-from-customers-text": "Παρακαλώ επιλέξτε τους πελάτες τους οποίους θέλετε να αφαιρέσετε από το/τα dashboard(s)", - "no-dashboards-text": "Δεν βρέθηκαν dashboards", - "no-widgets": "Δεν έχουν ρυθμιστεί widgets", - "add-widget": "Προσθέστε νέο widget", - "title": "Τίτλος", - "select-widget-title": "Επιλογή widget", - "select-widget-subtitle": "Λίστα με τους διαθέσιμους τύπους widget", - "delete": "Διαγραφή dashboard", - "title-required": "Απαιτείται ένας τίτλος.", - "description": "Περιγραφή", - "details": "Λεπτομέρειες", - "dashboard-details": "Λεπτομέρειες Dashboard", - "add-dashboard-text": "Προσθέστε νέο dashboard", - "assign-dashboards": "Αναθέστε dashboards", - "assign-new-dashboard": "Ανάθεση νέου dashboard", - "assign-dashboards-text": "Ανάθεση { count, plural, =1 {1 dashboard} other {# dashboards} } σε πελάτες", - "unassign-dashboards-action-text": "Μη ανατεθειμένο/α { count, plural, =1 {1 dashboard} other {# dashboards} } από πελάτες", - "delete-dashboards": "Διαγραφή dashboards", - "unassign-dashboards": "Αφαίρεση dashboards", - "unassign-dashboards-action-title": "Αφαίρεση { count, plural, =1 {1 dashboard} other {# dashboards} } από πελάτη", - "delete-dashboard-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε το dashboard '{{dashboardTitle}}'?", - "delete-dashboard-text": "Προσοχή, μετά την επιβεβαίωση, το dashboard και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-dashboards-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 dashboard} other {# dashboards} }?", - "delete-dashboards-action-title": "Διααγραφή { count, plural, =1 {1 dashboard} other {# dashboards} }", - "delete-dashboards-text": "Προσοχή, μετά την επιβεβαίωση, όλα τα επιλεγμένα dashboards και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "unassign-dashboard-title": "Είστε σίγουρου ότι θέλετε να αφαιρέσετε το dashboard '{{dashboardTitle}}'?", - "unassign-dashboard-text": "Μετά την επιβεβαίωση το dashboard θα αφαιρεθεί και δεν θα είναι διαθέσιμο στον πελάτη.", - "unassign-dashboard": "Αφαίρεση dashboard", - "unassign-dashboards-title": "Είστε σίγουρου ότι θέλετε να αφαιρέσετε { count, plural, =1 {1 dashboard} other {# dashboards} }?", - "unassign-dashboards-text": "Μετά την επιβεβαίωση όλα τα επιλεγμένα dashboards θα αφαιρεθούν και δεν θα είναι διαθέσιμα στον πελάτη.", - "public-dashboard-title": "Το dashboard είναι δημόσιο", - "public-dashboard-text": "Το dashboard {{dashboardTitle}} είναι δημόσιο και διαθέσιμο μέσω του παρακάτω συνδέσμου:", - "public-dashboard-notice": "Σημείωση: Μην ξεχνάτε να κάνετε δημόσιες τις συσκευές που συσχετίζονται ώστε να είναι δυνατή η πρόσβαση στα δεδομένα τους.", - "public-dashboard-link": "Σύνδεσμος δημόσιου dashboard", - "public-dashboard-link-text": "Το δημόσιο dashboard {{dashboardTitle}} είναι πλέον διαθέσιμο μέσω μέσω του παρακάτω συνδέσμου:", - "public-dashboard-link-notice": "Σημείωση: Μην ξεχνάτε να κάνετε δημόσιες τις συσκευές, τα assets και την προβολή οντοτήτων που συσχετίζονται ώστε να είναι δυνατή η πρόσβαση στα δεδομένα τους.", - "make-private-dashboard-title": "Είστε σίγουροι ότι θέλετε να κάνετε το dashboard '{{dashboardTitle}}' ιδιωτικό", - "make-private-dashboard-text": "Μετά την επιβεβαίωση το dashboard θα γίνουν ιδιωτικά και δεν θα είναι διαθέσιμα από τρίτους.", - "make-private-dashboard": "Κάνε το dashboard ιδιωτικό", + "management": "Dashboard management", + "view-dashboards": "View Dashboards", + "add": "Add dashboard", + "assign-dashboard-to-customer": "Assign Dashboard(s) To Customer", + "assign-dashboard-to-customer-text": "Please select the dashboards to assign to the customer", + "assign-to-customer-text": "Please select the customer to assign the dashboard(s)", + "assign-to-customer": "Assign to customer", + "unassign-from-customer": "Unassign from customer", + "make-public": "Make dashboard public", + "make-private": "Make dashboard private", + "manage-assigned-customers": "Manage assigned customers", + "assigned-customers": "Assigned customers", + "assign-to-customers": "Assign Dashboard(s) To Customers", + "assign-to-customers-text": "Please select the customers to assign the dashboard(s)", + "unassign-from-customers": "Unassign Dashboard(s) From Customers", + "unassign-from-customers-text": "Please select the customers to unassign from the dashboard(s)", + "no-dashboards-text": "No dashboards found", + "no-widgets": "No widgets configured", + "add-widget": "Add new widget", + "add-widget-button-text": "Add widget", + "title": "Title", + "image": "Dashboard image", + "mobile-app-settings": "Mobile application settings", + "mobile-order": "Dashboard order in mobile application", + "mobile-hide": "Hide dashboard in mobile application", + "update-image": "Update dashboard image", + "take-screenshot": "Take screenshot", + "select-widget-title": "Select widget", + "select-widget-value": "{{title}}: select widget", + "select-widget-subtitle": "List of available widget types", + "delete": "Delete dashboard", + "title-required": "Title is required.", + "title-max-length": "Title should be less than 256", + "description": "Description", + "details": "Details", + "dashboard-details": "Dashboard details", + "add-dashboard-text": "Add new dashboard", + "assign-dashboards": "Assign dashboards", + "assign-new-dashboard": "Assign new dashboard", + "assign-dashboards-text": "Assign { count, plural, =1 {1 dashboard} other {# dashboards} } to customers", + "unassign-dashboards-action-text": "Unassign { count, plural, =1 {1 dashboard} other {# dashboards} } from customers", + "delete-dashboards": "Delete dashboards", + "unassign-dashboards": "Unassign dashboards", + "unassign-dashboards-action-title": "Unassign { count, plural, =1 {1 dashboard} other {# dashboards} } from customer", + "delete-dashboard-title": "Are you sure you want to delete the dashboard '{{dashboardTitle}}'?", + "delete-dashboard-text": "Be careful, after the confirmation the dashboard and all related data will become unrecoverable.", + "delete-dashboards-title": "Are you sure you want to delete { count, plural, =1 {1 dashboard} other {# dashboards} }?", + "delete-dashboards-action-title": "Delete { count, plural, =1 {1 dashboard} other {# dashboards} }", + "delete-dashboards-text": "Be careful, after the confirmation all selected dashboards will be removed and all related data will become unrecoverable.", + "unassign-dashboard-title": "Are you sure you want to unassign the dashboard '{{dashboardTitle}}'?", + "unassign-dashboard-text": "After the confirmation the dashboard will be unassigned and won't be accessible by the customer.", + "unassign-dashboard": "Unassign dashboard", + "unassign-dashboards-title": "Are you sure you want to unassign { count, plural, =1 {1 dashboard} other {# dashboards} }?", + "unassign-dashboards-text": "After the confirmation all selected dashboards will be unassigned and won't be accessible by the customer.", + "public-dashboard-title": "Dashboard is now public", + "public-dashboard-text": "Your dashboard {{dashboardTitle}} is now public and accessible via next public link:", + "public-dashboard-notice": "Note: Do not forget to make related devices public in order to access their data.", + "make-private-dashboard-title": "Are you sure you want to make the dashboard '{{dashboardTitle}}' private?", + "make-private-dashboard-text": "After the confirmation the dashboard will be made private and won't be accessible by others.", + "make-private-dashboard": "Make dashboard private", "socialshare-text": "'{{dashboardTitle}}' powered by ThingsBoard", "socialshare-title": "'{{dashboardTitle}}' powered by ThingsBoard", - "select-dashboard": "Επιλογή dashboard", - "no-dashboards-matching": "Δεν βρέθηκαν dashboards που να αντιστοιχούν σε '{{entity}}'.", - "dashboard-required": "Απαιτείται dashboard.", - "select-existing": "Επιλέξτε ένα υπάρχον dashboard", - "create-new": "Δημιουργία νέου dashboard", - "new-dashboard-title": "Τίτλος νέου dashboard", - "open-dashboard": "Άνοιγμα 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.", + "select-dashboard": "Select dashboard", + "no-dashboards-matching": "No dashboards matching '{{entity}}' were found.", + "dashboard-required": "Dashboard is required.", + "select-existing": "Select existing dashboard", + "create-new": "Create new dashboard", + "new-dashboard-title": "New dashboard title", + "open-dashboard": "Open dashboard", + "set-background": "Set background", + "background-color": "Background color", + "background-image": "Background image", + "background-size-mode": "Background size mode", + "no-image": "No image selected", + "empty-image": "No image", + "drop-image": "Drop an image or click to select a file to upload.", + "maximum-upload-file-size": "Maximum upload file size: {{ size }}", + "cannot-upload-file": "Cannot upload file", + "settings": "Settings", + "move-all-widgets": "Move all widgets", + "move-by": "Move by", + "cols": "cols", + "rows": "rows", + "layout": "Διάταξη", + "layout-type-default": "Προεπιλογή", + "layout-type-scada": "SCADA", + "layout-type-divider": "Διαχωριστικό", + "layout-settings-type": "Ρυθμίσεις διάταξης: {{ type }} σημείο αλλαγής", + "columns-count": "Αριθμός στηλών", + "columns-count-required": "Ο αριθμός στηλών είναι υποχρεωτικός.", + "min-columns-count-message": "Επιτρέπονται τουλάχιστον 10 στήλες.", + "max-columns-count-message": "Επιτρέπονται έως 1000 στήλες.", + "min-layout-width": "Ελάχιστο πλάτος διάταξης", + "columns-suffix": "στήλες", "widgets-margins": "Περιθώριο μεταξύ widgets", + "margin-required": "Απαιτείται τιμή περιθωρίου.", + "min-margin-message": "Το ελάχιστο περιθώριο είναι 0.", + "max-margin-message": "Το μέγιστο περιθώριο είναι 50.", "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.", + "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.", + "apply-outer-margin": "Εφαρμογή περιθωρίου στις άκρες της διάταξης", "autofill-height": "Αυτόματη συμπλήρωση ύψους διάταξης", - "mobile-layout": "Ρυθμίσεις διάταξης mobile", - "mobile-row-height": "ύψος γραμμής mobile, σε px", - "mobile-row-height-required": "Απαιτείται ύψος γραμμής mobile.", - "min-mobile-row-height-message": "Ελάχιστη τιμή ύψους γραμμής mobile είναι 5.", - "max-mobile-row-height-message": "Μέγιστη τιμή ύψους γραμμής mobile είναι 200.", - "display-title": "Τίτλος εμφάνισης dashboard", - "toolbar-always-open": "Γραμμή εργαλείων πάντα ανοιχτή", + "mobile-layout": "Ρυθμίσεις διάταξης για κινητά", + "mobile-row-height": "Ύψος γραμμής σε κινητό", + "mobile-row-height-required": "Απαιτείται τιμή ύψους γραμμής κινητού.", + "min-mobile-row-height-message": "Ελάχιστο ύψος γραμμής για κινητό είναι 5 pixels.", + "max-mobile-row-height-message": "Μέγιστο ύψος γραμμής για κινητό είναι 200 pixels.", + "row-height": "Ύψος γραμμής", + "row-height-required": "Απαιτείται τιμή ύψους γραμμής.", + "min-row-height-message": "Ελάχιστο ύψος γραμμής είναι 5 pixels.", + "max-row-height-message": "Μέγιστο ύψος γραμμής είναι 200 pixels.", + "display-first-in-mobile-view": "Εμφάνιση πρώτα στην προβολή κινητού", + "title-settings": "Ρυθμίσεις τίτλου", + "display-title": "Εμφάνιση τίτλου πίνακα", "title-color": "Χρώμα τίτλου", - "display-dashboards-selection": "Εμφάνηση επιλογής dashboards", + "toolbar-settings": "Ρυθμίσεις γραμμής εργαλείων", + "hide-toolbar": "Απόκρυψη γραμμής εργαλείων", + "toolbar-always-open": "Διατήρηση γραμμής εργαλείων ανοιχτής", + "display-dashboards-selection": "Εμφάνιση επιλογής πινάκων", "display-entities-selection": "Εμφάνιση επιλογής οντοτήτων", - "display-dashboard-timewindow": "Εμφάνιση χρονικού πλαισίου", + "display-filters": "Εμφάνιση φίλτρων", + "display-dashboard-timewindow": "Εμφάνιση χρονικού παραθύρου", "display-dashboard-export": "Εμφάνιση εξαγωγής", - "import": "Εισαγωγή dashboard", - "export": "Εξαγωγή dashboard", - "export-failed-error": "Δεν ήταν δυνατή η εξαγωγή dashboard: {{error}}", - "export-pdf": "Εξαγωγή ως PDF", - "export-png": "Εξαγωγή ως PNG", - "export-jpg": "Εξαγωγή ως JPEG", - "export-json-config": "Εξαγωγή ρυθμίσεις JSON", - "download-dashboard-progress": "Δημιουργία dashboard {{reportType}} ...", - "create-new-dashboard": "Δημιουργία νέου dashboard", - "dashboard-file": "Αρχείο dashboard", - "invalid-dashboard-file-error": "Δεν ήταν δυνατή η εισαγωγή dashboard: Μη έγκυρη δομή δεδομένων dashboard.", - "dashboard-import-missing-aliases-title": "Διαμορφώστε τα ψευδώνυμα που χρησιμοποιούνται από το dashboard που εισαγάγατε", + "display-update-dashboard-image": "Εμφάνιση ενημέρωσης εικόνας πίνακα", + "dashboard-logo-settings": "Ρυθμίσεις λογότυπου πίνακα", + "display-dashboard-logo": "Εμφάνιση λογότυπου σε πλήρη οθόνη", + "dashboard-logo-image": "Εικόνα λογότυπου πίνακα", + "advanced-settings": "Προχωρημένες ρυθμίσεις", + "dashboard-css": "CSS πίνακα", + "import": "Εισαγωγή πίνακα", + "export": "Εξαγωγή πίνακα", + "export-failed-error": "Αδυναμία εξαγωγής πίνακα: {{error}}", + "export-prompt": "Ενσωμάτωση εικόνων και πόρων πίνακα", + "create-new-dashboard": "Δημιουργία νέου πίνακα", + "dashboard-file": "Αρχείο πίνακα", + "invalid-dashboard-file-error": "Αδυναμία εισαγωγής πίνακα: Μη έγκυρη δομή δεδομένων πίνακα.", + "dashboard-import-missing-aliases-title": "Διαμόρφωση ψευδωνύμων που χρησιμοποιούνται στον εισαγόμενο πίνακα", "create-new-widget": "Δημιουργία νέου widget", "import-widget": "Εισαγωγή widget", "widget-file": "Αρχείο widget", - "invalid-widget-file-error": "Δεν ήταν δυνατή η εισαγωγή widget: Μη έγκυρη δομή δεδομένων dashboard.", - "widget-import-missing-aliases-title": "Διαμορφώστε τα ψευδώνυμα που χρησιμοποιούνται από το widget που εισαγάγατε", - "open-toolbar": "Άνοιγμα γραμμής εργαλείων dashboard", + "invalid-widget-file-error": "Αδυναμία εισαγωγής widget: Μη έγκυρη δομή δεδομένων widget.", + "widget-import-missing-aliases-title": "Διαμόρφωση ψευδωνύμων που χρησιμοποιούνται στο εισαγόμενο widget", + "open-toolbar": "Άνοιγμα γραμμής εργαλείων", "close-toolbar": "Κλείσιμο γραμμής εργαλείων", - "configuration-error": "Σφάλμα ρυθμίσεων", - "alias-resolution-error-title": "Σφάλμα ρυθμίσεων ψευδωνύμων dashboard", - "invalid-aliases-config": "Δεν βρέθηκε καμία συσκευή που να ταιριάζει με κάποιο από τα φίλτρα.
Παρακαλούμε επικοινωνήστε με τον Διαχειριστή για την επίλυση του ζητήματος.", - "select-devices": "Επιλογή συσκευών", + "configuration-error": "Σφάλμα διαμόρφωσης", + "alias-resolution-error-title": "Σφάλμα διαμόρφωσης ψευδωνύμων πίνακα", + "invalid-aliases-config": "Αδυναμία εύρεσης συσκευών που να αντιστοιχούν σε ορισμένα φίλτρα ψευδωνύμων.
Παρακαλώ επικοινωνήστε με τον διαχειριστή σας για επίλυση.", + "select-devices": "Επιλέξτε συσκευές", "assignedToCustomer": "Ανατεθειμένο σε πελάτη", "assignedToCustomers": "Ανατεθειμένο σε πελάτες", "public": "Δημόσιο", + "copyId": "Αντιγραφή αναγνωριστικού πίνακα", + "idCopiedMessage": "Το αναγνωριστικό πίνακα αντιγράφηκε στο πρόχειρο", "public-link": "Δημόσιος σύνδεσμος", "copy-public-link": "Αντιγραφή δημόσιου συνδέσμου", - "public-link-copied-message": "Ο δημόσιος σύνδεσμος έχει αντιγραφεί στο πρόχειρο", - "manage-states": "Διαχείρηση κατάστασης dashboard", - "states": "Κατάσταση dashboard", - "search-states": "Αναζήτηση σε κατάσταση dashboard", - "selected-states": "{ count, plural, =1 {1 dashboard state} other {# dashboard states} } επιλεγμένα", - "edit-state": "Επεξεργασία κατάστασης dashboard", - "delete-state": "Διαγραφή κατάστασης dashboard", - "add-state": "Προσθήκη κατάστασης dashboard", - "state": "Κατάσταση Dashboard", + "public-link-copied-message": "Ο δημόσιος σύνδεσμος αντιγράφηκε στο πρόχειρο", + "manage-states": "Διαχείριση καταστάσεων πίνακα", + "states": "Καταστάσεις πίνακα", + "states-short": "Καταστάσεις", + "search-states": "Αναζήτηση καταστάσεων πίνακα", + "selected-states": "{ count, plural, =1 {1 κατάσταση πίνακα} other {# καταστάσεις πίνακα} } επιλεγμένες", + "edit-state": "Επεξεργασία κατάστασης πίνακα", + "delete-state": "Διαγραφή κατάστασης πίνακα", + "add-state": "Προσθήκη κατάστασης πίνακα", + "no-states-text": "Δεν βρέθηκαν καταστάσεις", + "state": "Κατάσταση πίνακα", "state-name": "Όνομα", - "state-name-required": "Απαιτείται όνομα κατάστασης dashboard.", - "state-id": "ID Κατάστασης", - "state-id-required": "Απαιτείται ID κατάστασης dashboard.", - "state-id-exists": "Υπάρχει ήδη κατάσταση dashboard με το ίδιο ID.", - "is-root-state": "Root state", - "delete-state-title": "Διαγραφή κατάστασης dashboard", - "delete-state-text": "Είστε σίγουροι ότι θέλετε να διαγράψετε την κατάσταση dashboard με όνομα '{{stateName}}'?", + "state-name-required": "Το όνομα της κατάστασης του πίνακα είναι υποχρεωτικό.", + "state-id": "Αναγνωριστικό κατάστασης", + "state-id-required": "Το αναγνωριστικό κατάστασης του πίνακα είναι υποχρεωτικό.", + "state-id-exists": "Υπάρχει ήδη κατάσταση πίνακα με το ίδιο αναγνωριστικό.", + "is-root-state": "Βασική κατάσταση", + "delete-state-title": "Διαγραφή κατάστασης πίνακα", + "delete-state-text": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την κατάσταση πίνακα με όνομα '{{stateName}}';", "show-details": "Εμφάνιση λεπτομερειών", "hide-details": "Απόκρυψη λεπτομερειών", - "select-state": "Επιλογή κατάστασης", - "state-controller": "Έλεγχος κατάστασης", - "selected-dashboards": "{ count, plural, =1 {1 dashboard} other {# dashboards} } επιλεγμένα", - "search": "Αναζήτηση dashboards", - "select-group-to-add": "Επιλογή ομάδας για να προστεθεί στα επιλεγμένα dashboards", - "select-group-to-move": "Επιλογή ομάδας για να μετακινηθεί στα επιλεγμένα dashboards", - "remove-dashboards-from-group": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε { count, plural, =1 {1 dashboard} other {# dashboards} } from group '{entityGroup}'?", - "group": "Ομάδα dashboards", - "list-of-groups": "{ count, plural, =1 {One dashboard group} other {List of # dashboard groups} }", - "group-name-starts-with": "Ομάδες dashboard των οποίων το όνομα αρχίζει από '{{prefix}}'" + "select-state": "Επιλέξτε κατάσταση στόχου", + "state-controller": "Ελεγκτής κατάστασης", + "state-controller-default": "στατικό (παρωχημένο)", + "search": "Αναζήτηση πινάκων", + "selected-dashboards": "{ count, plural, =1 {1 πίνακας} other {# πίνακες} } επιλεγμένοι", + "home-dashboard": "Αρχικός πίνακας", + "home-dashboard-hide-toolbar": "Απόκρυψη γραμμής εργαλείων στον αρχικό πίνακα", + "unassign-dashboard-from-edge-text": "Μετά την επιβεβαίωση ο πίνακας θα αποανατεθεί και δεν θα είναι προσβάσιμος από το Edge.", + "unassign-dashboards-from-edge-title": "Είστε βέβαιοι ότι θέλετε να αποαναθέσετε { count, plural, =1 {1 πίνακα} other {# πίνακες} };", + "unassign-dashboards-from-edge-text": "Μετά την επιβεβαίωση όλοι οι επιλεγμένοι πίνακες θα αποανατεθούν και δεν θα είναι προσβάσιμοι από το Edge.", + "assign-dashboard-to-edge": "Ανάθεση πίνακα(ων) σε Edge", + "assign-dashboard-to-edge-text": "Παρακαλώ επιλέξτε τους πίνακες για ανάθεση στο Edge", + "non-existent-dashboard-state-error": "Η κατάσταση πίνακα με id \"{{ stateId }}\" δεν βρέθηκε", + "edit-mode": "Λειτουργία επεξεργασίας", + "duplicate-state-action": "Αντιγραφή κατάστασης", + "breakpoint-value": "Breakpoint ({{ value }})", + "breakpoints-id": { + "default": "Προεπιλογή", + "xs": "Κινητό (xs)", + "sm": "Tablet (sm)", + "md": "Laptop (md)", + "lg": "Desktop (lg)", + "xl": "Desktop (xl)" + }, + "view-format-type-grid": "Πλέγμα", + "view-format-type-list": "Λίστα", + "view-format": "Μορφή προβολής" }, "datakey": { "settings": "Ρυθμίσεις", - "advanced": "Προχωρημένες", + "general": "Γενικά", + "advanced": "Προχωρημένα", + "key": "Κλειδί", + "keys": "Κλειδιά", "label": "Ετικέτα", "color": "Χρώμα", - "units": "Ειδικό σύμβολο που εμφανίζεται δίπλα από την τιμή", + "units": "Ειδικό σύμβολο που εμφανίζεται δίπλα στην τιμή", "decimals": "Αριθμός ψηφίων μετά την υποδιαστολή", - "data-generation-func": "Λειτουργία δημιουργίας δεδομένων", - "use-data-post-processing-func": "Χρησιμοποιήστε τη λειτουργία μετα-επεξεργασίας δεδομένων", - "configuration": "Ρυθμίσεις ονομάτων δεδομένων", - "timeseries": "Χρονική σειρά", - "attributes": "Ιδιώτητες", - "alarm": "Πεδία alarm", - "timeseries-required": "Απαιτείται χρονική σειρά οντοτήτων.", - "timeseries-or-attributes-required": "Απαιτείται χρονική σειρά/ιδιώτητες οντοτήτων.", - "maximum-timeseries-or-attributes": "Μέγιστο { count, plural, =1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }", - "alarm-fields-required": "Απαιτούνται πεδία alarm.", - "function-types": "Τύποι λειτουργιών", - "function-types-required": "Απαιτούνται τύποι λειτουργιών.", - "maximum-function-types": "Μέγιστο { 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": "αρχική προηγούμενη τιμή" + "data-generation-func": "Συνάρτηση δημιουργίας δεδομένων", + "use-data-post-processing-func": "Χρήση συνάρτησης μετα-επεξεργασίας δεδομένων", + "configuration": "Διαμόρφωση κλειδιού δεδομένων", + "timeseries": "Χρονικές σειρές", + "attributes": "Χαρακτηριστικά", + "entity-field": "Πεδίο οντότητας", + "alarm": "Πεδία ειδοποίησης", + "timeseries-required": "Απαιτούνται χρονικές σειρές οντότητας.", + "timeseries-or-attributes-required": "Απαιτούνται χρονικές σειρές/χαρακτηριστικά οντότητας.", + "alarm-fields-timeseries-or-attributes-required": "Απαιτούνται πεδία ειδοποίησης ή χρονικές σειρές/χαρακτηριστικά οντότητας.", + "maximum-timeseries-or-attributes": "Μέγιστο { count, plural, =1 {1 χρονική σειρά/χαρακτηριστικό επιτρέπεται.} other {# χρονικές σειρές/χαρακτηριστικά επιτρέπονται} }", + "alarm-fields-required": "Απαιτούνται πεδία ειδοποίησης.", + "function-types": "Τύποι συναρτήσεων", + "function-type": "Τύπος συνάρτησης", + "function-types-required": "Απαιτούνται τύποι συναρτήσεων.", + "data-keys": "Κλειδιά δεδομένων", + "data-key": "Κλειδί δεδομένων", + "data-keys-required": "Απαιτούνται κλειδιά δεδομένων.", + "data-key-required": "Απαιτείται κλειδί δεδομένων.", + "alarm-keys": "Κλειδιά δεδομένων ειδοποίησης", + "alarm-key": "Κλειδί δεδομένων ειδοποίησης", + "alarm-key-functions": "Συναρτήσεις κλειδιών ειδοποίησης", + "alarm-key-function": "Συνάρτηση κλειδιού ειδοποίησης", + "latest-keys": "Τελευταία κλειδιά δεδομένων", + "latest-key": "Τελευταίο κλειδί δεδομένων", + "latest-key-functions": "Συναρτήσεις τελευταίων κλειδιών", + "latest-key-function": "Συνάρτηση τελευταίου κλειδιού", + "timeseries-keys": "Κλειδιά χρονικών σειρών", + "timeseries-key": "Κλειδί χρονικής σειράς", + "timeseries-key-functions": "Συναρτήσεις κλειδιών χρονικών σειρών", + "timeseries-key-function": "Συνάρτηση κλειδιού χρονικής σειράς", + "maximum-function-types": "Μέγιστο { count, plural, =1 {1 τύπος συνάρτησης επιτρέπεται.} other {# τύποι συναρτήσεων επιτρέπονται} }", + "time-description": "χρονοσφραγίδα της τρέχουσας τιμής;", + "value-description": "η τρέχουσα τιμή;", + "prev-value-description": "αποτέλεσμα της προηγούμενης κλήσης συνάρτησης;", + "time-prev-description": "χρονοσφραγίδα της προηγούμενης τιμής;", + "prev-orig-value-description": "αρχική προηγούμενη τιμή;", + "aggregation": "Συσσώρευση", + "aggregation-type-hint-common": "Για λόγους απόδοσης, η συσσωρευμένη τιμή είναι διαθέσιμη μόνο για σταθερά χρονικά διαστήματα όπως \"τρέχουσα ημέρα\", \"τρέχων μήνας\" κ.λπ. και δεν είναι διαθέσιμη για κινούμενα χρονικά παράθυρα όπως 'τελευταία 30 λεπτά' ή 'τελευταίες 24 ώρες'.", + "aggregation-type-none-hint": "Λήψη τελευταίας τιμής.", + "aggregation-type-min-hint": "Εύρεση ελάχιστης τιμής μεταξύ των σημείων δεδομένων σε επιλεγμένο χρονικό παράθυρο.", + "aggregation-type-max-hint": "Εύρεση μέγιστης τιμής μεταξύ των σημείων δεδομένων σε επιλεγμένο χρονικό παράθυρο.", + "aggregation-type-avg-hint": "Υπολογισμός μέσου όρου μεταξύ των σημείων δεδομένων σε επιλεγμένο χρονικό παράθυρο.", + "aggregation-type-sum-hint": "Άθροιση όλων των τιμών των σημείων δεδομένων σε επιλεγμένο χρονικό παράθυρο.", + "aggregation-type-count-hint": "Συνολικός αριθμός σημείων δεδομένων σε επιλεγμένο χρονικό παράθυρο.", + "delta-calculation": "Υπολογισμός διαφοράς", + "enable-delta-calculation": "Ενεργοποίηση υπολογισμού διαφοράς", + "enable-delta-calculation-hint": "Όταν είναι ενεργοποιημένο, η τιμή του κλειδιού δεδομένων υπολογίζεται με βάση τις συσσωρευμένες τιμές για ένα επιλεγμένο χρονικό παράθυρο και μια καθορισμένη περίοδο σύγκρισης. Για λόγους απόδοσης, ο υπολογισμός διαφοράς είναι διαθέσιμος μόνο για ιστορικά χρονικά παράθυρα και όχι για τιμές σε πραγματικό χρόνο.", + "delta-calculation-result": "Αποτέλεσμα υπολογισμού διαφοράς", + "delta-calculation-result-previous-value": "Προηγούμενη τιμή", + "delta-calculation-result-delta-absolute": "Διαφορά (απόλυτη)", + "delta-calculation-result-delta-percent": "Διαφορά (ποσοστιαία)", + "source": "Πηγή", + "latest": "Τελευταίο", + "latest-value": "Τελευταία τιμή", + "delta": "διαφορά", + "percent": "ποσοστό", + "absolute": "απόλυτη" }, "datasource": { "type": "Τύπος πηγής δεδομένων", "name": "Όνομα", - "add-datasource-prompt": "Παρακαλούμε προσθέστε πηγή δεδομένων" + "label": "Ετικέτα", + "add-datasource-prompt": "Παρακαλώ προσθέστε πηγή δεδομένων" }, "details": { "details": "Λεπτομέρειες", - "edit-mode": "Λειτουργία Επεξεργασίας", + "edit-mode": "Λειτουργία επεξεργασίας", + "edit-json": "Επεξεργασία JSON", "toggle-edit-mode": "Εναλλαγή λειτουργίας επεξεργασίας" }, "device": { "device": "Συσκευή", - "device-required": "Απαιτείται ορισμός συσκευής.", + "device-required": "Απαιτείται συσκευή.", "devices": "Συσκευές", - "management": "Διαχείριση Συσκευών", - "view-devices": "Προβολή Συσκευών", + "management": "Διαχείριση συσκευών", + "view-devices": "Προβολή συσκευών", "device-alias": "Ψευδώνυμο συσκευής", + "device-type-max-length": "Ο τύπος συσκευής πρέπει να είναι μικρότερος από 256 χαρακτήρες", "aliases": "Ψευδώνυμα συσκευής", - "no-alias-matching": "Δεν βρέθηκε '{{alias}}'.", + "no-alias-matching": "'{{alias}}' δεν βρέθηκε.", "no-aliases-found": "Δεν βρέθηκαν ψευδώνυμα.", - "no-key-matching": "Δεν βρέθηκε '{{key}}'.", - "no-keys-found": "Δεν βρέθηκαν ονόματα", - "create-new-alias": "Δημιουργήστε ένα νέο ψευδώνυμο!", - "create-new-key": "Δημιουργήστε ένα νέο!", - "duplicate-alias-error": "Βρέθηκε διπλότυπο το ψευδώνυμο '{{alias}}'.
Τα ψευδώνυμα συσκευών πρέπει να είναι μοναδικά σε ένα dashboard.", - "configure-alias": "Ρύθμιση ψευδώνυμου '{{alias}}'", - "no-devices-matching": "Δεν βρέθηκε συσκευή η οποία να αντιστοιχεί σε '{{entity}}'.", + "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": "Το όνομα συσκευής αρχίζει με", + "remove-alias": "Αφαίρεση ψευδωνύμου συσκευής", + "add-alias": "Προσθήκη ψευδωνύμου συσκευής", + "name-starts-with": "Έκφραση ονόματος συσκευής", + "help-text": "Χρησιμοποιήστε '%' όπως απαιτείται: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", "device-list": "Λίστα συσκευών", "use-device-name-filter": "Χρήση φίλτρου", - "device-list-empty": "Δεν επιλέχθηκαν συσκευές.", - "device-name-filter-required": "Απαιτείται φίλτρο ονόματος συσκυεής.", - "device-name-filter-no-device-matched": "Δεν βρέθηκαν συσκευές οι οποίες να αρχίζουν από '{{device}}'.", + "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": "Ιδιωτικοποίηση συσκευών", + "assign-device-to-customer": "Ανάθεση συσκευής/ών σε πελάτη", + "assign-device-to-customer-text": "Παρακαλώ επιλέξτε τις συσκευές για ανάθεση στον πελάτη", + "make-public": "Κάντε τη συσκευή δημόσια", + "make-private": "Κάντε τη συσκευή ιδιωτική", "no-devices-text": "Δεν βρέθηκαν συσκευές", - "assign-to-customer-text": "Παρακαλώ επιλέξτε πελάτη για να αναθέσετε τις συσκευές", - "device-details": "Λεπτομέρειες συσκευές", + "assign-to-customer-text": "Παρακαλώ επιλέξτε τον πελάτη για την ανάθεση της συσκευής/ών", + "device-details": "Λεπτομέρειες συσκευής", "add-device-text": "Προσθήκη νέας συσκευής", "credentials": "Διαπιστευτήρια", - "manage-credentials": "Διαχείρηση διαπιστευτηρίων", + "manage-credentials": "Διαχείριση διαπιστευτηρίων", "delete": "Διαγραφή συσκευής", "assign-devices": "Ανάθεση συσκευών", - "assign-devices-text": "Ανάθεση { count, plural, =1 {1 device} other {# devices} } σε πελάτη", + "assign-devices-text": "Ανάθεση { count, plural, =1 {1 συσκευής} other {# συσκευών} } σε πελάτη", "delete-devices": "Διαγραφή συσκευών", "unassign-from-customer": "Αφαίρεση από πελάτη", "unassign-devices": "Αφαίρεση συσκευών", - "unassign-devices-action-title": "Αφαίρεση { count, plural, =1 {1 device} other {# devices} } από πελάτη", - "assign-new-device": "Ανάθεση νέων συσκευών", - "make-public-device-title": "Είστε σίγουροι ότι θέλετε να κάνετε τη συσκευή '{{deviceName}}' δημόσια;", - "make-public-device-text": "Μετά από την επιβεβαίωσή σας η συσκευή και όλα τα δεδομένα της θα είναι δημόσια και διαθέσιμα σε τρίτους.", - "make-private-device-title": "Είστε σίγουροι ότι θέλετε να κάνετε τη συσκευή '{{deviceName}}' ιδιωτική;", - "make-private-device-text": "Μετά από την επιβεβαίωσή σας η συσκευή και όλα τα δεδομένα της θα είναι ιδιωτικά και δεν θα είναι διαθέσιμα σε τριτους.", + "unassign-devices-action-title": "Αφαίρεση { count, plural, =1 {1 συσκευής} other {# συσκευών} } από πελάτη", + "unassign-device-from-edge-title": "Είστε βέβαιοι ότι θέλετε να αποαναθέσετε τη συσκευή '{{deviceName}}';", + "unassign-device-from-edge-text": "Μετά την επιβεβαίωση η συσκευή δεν θα είναι πλέον προσβάσιμη από το Edge.", + "unassign-devices-from-edge": "Αποαναθέτηση συσκευών από το Edge", + "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 device} other {# devices} };", - "delete-devices-action-title": "Διαγραφή { count, plural, =1 {1 device} other {# devices} }", - "delete-devices-text": "Προσοχή, μετά την επιβεβαίωση, όλες οι επιλεγμένες συσκευές θα αφαιρεθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "unassign-device-title": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε την συσκευή '{{deviceName}}'?", - "unassign-device-text": "Μετά την επιβεβαίωση, η συσκευή θα αφαιρεθεί και δεν θα είναι προσβάσιμες από τον πελάτη.", - "unassign-device": "Αφαίρεση συσκευής", - "unassign-devices-title": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε { count, plural, =1 {1 device} other {# devices} }?", - "unassign-devices-text": "Μετά την επιβεβαίωση, όλες οι επιλεγμένες συσκευές θα καταργηθούν και δεν θα είναι προσβάσιμες από τον πελάτη.", - "device-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": "Διαπιστευτήρια συσκευής", + "loading-device-credentials": "Φόρτωση διαπιστευτηρίων συσκευής...", "credentials-type": "Τύπος διαπιστευτηρίων", - "access-token": "Διακριτικό πρόσβασης", - "access-token-required": "Απαιτείται διακριτικό πρόσβασης.", - "access-token-invalid": "Access token length must be from 1 to 32 characters.", - "secret": "Secret", - "secret-required": "Απαιτείται secret.", - "device-type": "Τύπος συσκευής", - "device-type-required": "Απαιτείται τύπος συσκεύης.", - "select-device-type": "Επιλογή τύπου συσκευής", - "enter-device-type": "Είσοδο τύπου συσκευής", + "access-token": "Access token", + "access-token-required": "Το access token είναι υποχρεωτικό.", + "access-token-invalid": "Το μήκος του access token πρέπει να είναι από 1 έως 32 χαρακτήρες.", + "certificate-pem-format": "Πιστοποιητικό σε μορφή PEM", + "certificate-pem-format-required": "Το πιστοποιητικό είναι υποχρεωτικό.", + "copy-access-token": "Αντιγραφή Access token", + "copy-certificate": "Αντιγραφή πιστοποιητικού", + "copy-client-id": "Αντιγραφή Client ID", + "copy-user-name": "Αντιγραφή ονόματος χρήστη", + "copy-password": "Αντιγραφή κωδικού πρόσβασης", + "generate-client-id": "Δημιουργία Client ID", + "generate-user-name": "Δημιουργία ονόματος χρήστη", + "generate-password": "Δημιουργία κωδικού πρόσβασης", + "generate-access-token": "Δημιουργία Access Token", + "lwm2m-security-config": { + "identity": "Ταυτότητα πελάτη", + "identity-required": "Απαιτείται η ταυτότητα πελάτη.", + "identity-tooltip": "Το αναγνωριστικό PSK είναι ένα αυθαίρετο αναγνωριστικό PSK έως 128 byte, όπως περιγράφεται στο πρότυπο [RFC7925].\nΤο αναγνωριστικό PSK ΠΡΕΠΕΙ να μετατραπεί πρώτα σε συμβολοσειρά χαρακτήρων και στη συνέχεια να κωδικοποιηθεί σε οκτάδες χρησιμοποιώντας UTF-8.", + "client-key": "Κλειδί πελάτη", + "client-key-required": "Απαιτείται το κλειδί πελάτη.", + "client-key-tooltip-prk": "Το δημόσιο κλειδί RPK ή id πρέπει να είναι σύμφωνα με το πρότυπο [RFC7250] και να είναι κωδικοποιημένο σε μορφή Base64!", + "client-key-tooltip-psk": "Το κλειδί PSK πρέπει να είναι σύμφωνα με το πρότυπο [RFC4279] και σε δεκαεξαδική μορφή: 32, 64, 128 χαρακτήρες!", + "endpoint": "Όνομα πελάτη τελικού σημείου", + "endpoint-required": "Απαιτείται όνομα πελάτη τελικού σημείου.", + "client-public-key": "Δημόσιο κλειδί πελάτη", + "client-public-key-hint": "Αν δεν έχει οριστεί δημόσιο κλειδί πελάτη, θα χρησιμοποιηθεί το αξιόπιστο πιστοποιητικό", + "client-public-key-tooltip": "Το δημόσιο κλειδί X509 πρέπει να είναι σε μορφή DER-κωδικοποιημένου X509v3 και να υποστηρίζει αποκλειστικά τον αλγόριθμο EC και να είναι κωδικοποιημένο σε μορφή Base64!", + "mode": "Λειτουργία διαμόρφωσης ασφάλειας", + "client-tab": "Διαμόρφωση ασφάλειας πελάτη", + "client-certificate": "Πιστοποιητικό πελάτη", + "bootstrap-tab": "Bootstrap πελάτη", + "bootstrap-server": "Διακομιστής Bootstrap", + "lwm2m-server": "Διακομιστής LwM2M", + "client-publicKey-or-id": "Δημόσιο κλειδί ή Id πελάτη", + "client-publicKey-or-id-required": "Απαιτείται Δημόσιο κλειδί ή Id πελάτη.", + "client-publicKey-or-id-tooltip-psk": "Το αναγνωριστικό PSK είναι ένα αυθαίρετο αναγνωριστικό PSK έως 128 byte, όπως περιγράφεται στο πρότυπο [RFC7925].\nΤο αναγνωριστικό PSK ΠΡΕΠΕΙ να μετατραπεί πρώτα σε συμβολοσειρά χαρακτήρων και στη συνέχεια να κωδικοποιηθεί σε οκτάδες χρησιμοποιώντας UTF-8.", + "client-publicKey-or-id-tooltip-rpk": "Το δημόσιο κλειδί RPK ή id πρέπει να είναι σύμφωνα με το πρότυπο [RFC7250] και να είναι κωδικοποιημένο σε μορφή Base64!", + "client-publicKey-or-id-tooltip-x509": "Το δημόσιο κλειδί X509 πρέπει να είναι σε μορφή DER-κωδικοποιημένου X509v3 και να υποστηρίζει αποκλειστικά τον αλγόριθμο EC και να είναι κωδικοποιημένο σε μορφή Base64", + "client-secret-key": "Μυστικό κλειδί πελάτη", + "client-secret-key-required": "Απαιτείται το μυστικό κλειδί πελάτη.", + "client-secret-key-tooltip-psk": "Το κλειδί PSK πρέπει να είναι σύμφωνα με το πρότυπο [RFC4279] και σε δεκαεξαδική μορφή: 32, 64, 128 χαρακτήρες!", + "client-secret-key-tooltip-prk": "Το μυστικό κλειδί RPK πρέπει να είναι σε μορφή PKCS_8 (κωδικοποίηση DER, πρότυπο [RFC5958]) και να είναι κωδικοποιημένο σε μορφή Base64!", + "client-secret-key-tooltip-x509": "Το μυστικό κλειδί X509 πρέπει να είναι σε μορφή PKCS_8 (κωδικοποίηση DER, πρότυπο [RFC5958]) και να είναι κωδικοποιημένο σε μορφή Base64!" + }, + "client-id": "Client ID", + "client-id-pattern": "Περιέχει μη έγκυρο χαρακτήρα.", + "user-name": "Όνομα χρήστη", + "user-name-required": "Το όνομα χρήστη είναι υποχρεωτικό.", + "client-id-or-user-name-necessary": "Απαιτείται Client ID ή/και Όνομα χρήστη", + "password": "Κωδικός πρόσβασης", + "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": "Τύποι συσκευής", + "no-device-types-matching": "Δεν βρέθηκαν προφίλ συσκευής που να ταιριάζουν με '{{entitySubtype}}'.", + "device-type-list-empty": "Δεν έχει επιλεγεί προφίλ συσκευής!", + "device-profile-type-list-empty": "Πρέπει να επιλεγεί τουλάχιστον ένα προφίλ συσκευής.", + "device-types": "Τύποι συσκευών", "name": "Όνομα", - "name-required": "Απαιτείται όνομα.", + "name-required": "Το όνομα είναι υποχρεωτικό.", + "name-max-length": "Το όνομα πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "label-max-length": "Η ετικέτα πρέπει να είναι μικρότερη από 256 χαρακτήρες", "description": "Περιγραφή", "label": "Ετικέτα", - "events": "Γεγονότα", + "events": "Συμβάντα", "details": "Λεπτομέρειες", - "copyId": "Αντιγραφή ID συσκευής", - "copyAccessToken": "Αντιγραφή διακριτικού διαπιστευτηρίου", - "idCopiedMessage": "Το ID της συσκευής έχει αντιγραφεί στο πρόχειρο", - "accessTokenCopiedMessage": "Το διακριτικό διαπιστευτήριο έχει αντιγραφεί στο πρόχειρο", - "assignedToCustomer": "Ανάθεση σε πελάτη", - "unable-delete-device-alias-title": "Αδύνατο να διαγραφεί το ψευδώνυμο συσκευής", - "unable-delete-device-alias-text": "Το ψευδώνυμο συσκευής '{{deviceAlias}}' δεν μπορεί να διαγραφεί όσο εξακωλουθεί να χρησιμοποιείται από τα ακόλουθα widget(s):
{{widgetsList}}", - "is-gateway": "είναι gateway", - "public": "Δημόσιο", + "copyId": "Αντιγραφή Id συσκευής", + "copyAccessToken": "Αντιγραφή access token", + "copy-mqtt-authentication": "Αντιγραφή διαπιστευτηρίων MQTT", + "idCopiedMessage": "Το Id της συσκευής αντιγράφηκε στο πρόχειρο", + "accessTokenCopiedMessage": "Το access token της συσκευής αντιγράφηκε στο πρόχειρο", + "mqtt-authentication-copied-message": "Η αυθεντικοποίηση MQTT της συσκευής αντιγράφηκε στο πρόχειρο", + "assignedToCustomer": "Ανατεθειμένη σε πελάτη", + "unable-delete-device-alias-title": "Αδυναμία διαγραφής ψευδωνύμου συσκευής", + "unable-delete-device-alias-text": "Το ψευδώνυμο συσκευής '{{deviceAlias}}' δεν μπορεί να διαγραφεί επειδή χρησιμοποιείται από τα παρακάτω widget(s):
{{widgetsList}}", + "is-gateway": "Είναι gateway", + "overwrite-activity-time": "Αντικατάσταση χρόνου δραστηριότητας για συνδεδεμένη συσκευή", + "device-filter": "Φίλτρο συσκευής", + "device-filter-title": "Φίλτρο συσκευής", + "filter-title": "Φίλτρο", + "device-state": "Κατάσταση συσκευής", + "state": "Κατάσταση", + "any": "Οποιαδήποτε", + "active": "Ενεργή", + "inactive": "Ανενεργή", + "public": "Δημόσια", "device-public": "Η συσκευή είναι δημόσια", - "select-device": "Επιλογή συσκευής", - "selected-devices": "{ count, plural, =1 {1 device} other {# devices} } επιλέχθηκαν", - "search": "Αναζήτηση συσκευών", - "select-group-to-add": "Επιλέξτε την ομάδα στην οποία θα προστεθουν οι επιλεγμένες συσκευές", - "select-group-to-move": "Επιλέξτε την ομάδα στην οποία θα μετακινηθούν οι επιλεγμένες συσκευές", - "remove-devices-from-group": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε { count, plural, =1 {1 device} other {# devices} } from group '{entityGroup}'?", - "group": "Ομάδα Συσκευών", - "list-of-groups": "{ count, plural, =1 {One device group} other {List of # device groups} }", - "group-name-starts-with": "Ομάδες συσκευών των οποίων το όνομα αρχίζει από '{{prefix}}'", + "select-device": "Επιλέξτε συσκευή", "import": "Εισαγωγή συσκευής", - "device-file": "Αρχείο συσκευής" + "device-file": "Αρχείο συσκευής", + "search": "Αναζήτηση συσκευών", + "selected-devices": "{ count, plural, =1 {1 συσκευή} other {# συσκευές} } επιλεγμένες", + "device-configuration": "Ρύθμιση συσκευής", + "transport-configuration": "Ρύθμιση μεταφοράς", + "wizard": { + "device-details": "Λεπτομέρειες συσκευής" + }, + "unassign-devices-from-edge-title": "Είστε βέβαιοι ότι θέλετε να αποαναθέσετε { count, plural, =1 {1 συσκευή} other {# συσκευές} };", + "unassign-devices-from-edge-text": "Μετά την επιβεβαίωση όλες οι επιλεγμένες συσκευές θα αποανατεθούν και δεν θα είναι προσβάσιμες από το Edge.", + "time": "Χρόνος", + "connectivity": { + "check-connectivity": "Έλεγχος συνδεσιμότητας", + "device-created-check-connectivity": "Η συσκευή δημιουργήθηκε. Ας ελέγξουμε τη συνδεσιμότητα!", + "loading-check-connectivity-command": "Φόρτωση εντολών ελέγχου συνδεσιμότητας...", + "use-following-instructions": "Χρησιμοποιήστε τις παρακάτω οδηγίες για την αποστολή τηλεμετρίας εκ μέρους της συσκευής μέσω shell", + "execute-following-command": "Εκτελέστε την παρακάτω εντολή", + "install-curl-windows": "Από τα Windows 10 b17063 και μετά, το cURL είναι διαθέσιμο από προεπιλογή", + "install-curl-macos": "Από το Mac OS X 10.2 6C115 (Jaguar), το cURL είναι διαθέσιμο από προεπιλογή", + "install-mqtt-windows": "Χρησιμοποιήστε τις οδηγίες για λήψη, εγκατάσταση, ρύθμιση και εκτέλεση του mosquitto_pub", + "install-coap-client": "Χρησιμοποιήστε τις οδηγίες για λήψη, εγκατάσταση, ρύθμιση και εκτέλεση του coap-client", + "install-necessary-client-tools": "Εγκαταστήστε τα απαραίτητα εργαλεία πελάτη", + "mqtts-x509-command": "Χρησιμοποιήστε την παρακάτω τεκμηρίωση για να συνδέσετε τη συσκευή μέσω MQTT με εξουσιοδότηση X509", + "coaps-x509-command": "Χρησιμοποιήστε την παρακάτω τεκμηρίωση για να συνδέσετε τη συσκευή μέσω CoAP over DTLS με εξουσιοδότηση X509", + "snmp-command": "Χρησιμοποιήστε την παρακάτω τεκμηρίωση για να συνδέσετε τη συσκευή μέσω SNMP.", + "sparkplug-command": "Χρησιμοποιήστε την παρακάτω τεκμηρίωση για να συνδέσετε τη συσκευή μέσω MQTT Sparkplug.", + "lwm2m-command": "Χρησιμοποιήστε την παρακάτω τεκμηρίωση για να συνδέσετε τη συσκευή μέσω LWM2M." + } + }, + "dynamic-form": { + "property": { + "properties": "Ιδιότητες", + "property": "Ιδιότητα", + "id": "Id", + "name": "Όνομα", + "type": "Τύπος", + "type-text": "Κείμενο", + "type-password": "Κωδικός πρόσβασης", + "type-textarea": "Περιοχή κειμένου", + "type-number": "Αριθμός", + "type-switch": "Διακόπτης", + "type-select": "Επιλογή", + "type-radios": "Κουμπιά επιλογής", + "type-datetime": "Ημερομηνία/Ώρα", + "type-image": "Εικόνα", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "Χρώμα", + "type-color-settings": "Ρυθμίσεις χρώματος", + "type-font": "Γραμματοσειρά", + "type-units": "Μονάδες", + "type-icon": "Εικονίδιο", + "type-fieldset": "Ομάδα πεδίων", + "type-array": "Πίνακας", + "type-html-section": "Ενότητα HTML", + "group-title": "Τίτλος ομάδας", + "no-properties": "Δεν έχουν διαμορφωθεί ιδιότητες", + "add-property": "Προσθήκη ιδιότητας", + "property-settings": "Ρυθμίσεις ιδιότητας", + "remove-property": "Αφαίρεση ιδιότητας", + "default-value": "Προεπιλεγμένη τιμή", + "value-required": "Απαιτείται τιμή", + "number-settings": "Ρυθμίσεις αριθμού", + "min": "Ελάχιστο", + "max": "Μέγιστο", + "step": "Βήμα", + "selected-options-limit": "Όριο επιλεγμένων επιλογών", + "advanced-ui-settings": "Προηγμένες ρυθμίσεις UI", + "disable-on-property": "Απενεργοποίηση με βάση ιδιότητα", + "display-condition-function": "Συνάρτηση συνθήκης εμφάνισης", + "sub-label": "Υποετικέτα", + "vertical-divider-after": "Κατακόρυφος διαχωριστής μετά", + "input-field-suffix": "Κατάληξη πεδίου εισόδου", + "property-row-classes": "Κλάσεις γραμμής ιδιότητας", + "property-field-classes": "Κλάσεις πεδίου ιδιότητας", + "not-unique-property-ids-error": "Τα Id των ιδιοτήτων πρέπει να είναι μοναδικά!", + "enable-multiple-select": "Ενεργοποίηση πολλαπλής επιλογής", + "allow-empty-select-option": "Επιτρέπεται κενή επιλογή", + "select-options": "Επιλογές λίστας", + "not-unique-select-option-value-error": "Οι τιμές επιλογών πρέπει να είναι μοναδικές!", + "value": "Τιμή", + "label": "Ετικέτα", + "add-option": "Προσθήκη επιλογής", + "no-options": "Δεν έχουν διαμορφωθεί επιλογές", + "remove-option": "Αφαίρεση επιλογής", + "textarea-rows": "Γραμμές περιοχής κειμένου", + "help-id": "Id βοήθειας", + "buttons-direction": "Κατεύθυνση κουμπιών", + "direction-row": "Γραμμή", + "direction-column": "Στήλη", + "radio-button-options": "Επιλογές κουμπιών επιλογής", + "datetime-type": "Τύπος πεδίου Ημερομηνία/Ώρα", + "datetime-type-date": "Ημερομηνία", + "datetime-type-time": "Ώρα", + "datetime-type-datetime": "Ημερομηνία/Ώρα", + "enable-clear-button": "Ενεργοποίηση κουμπιού εκκαθάρισης", + "html-section-settings": "Ρυθμίσεις ενότητας HTML", + "html-section-classes": "Κλάσεις ενότητας HTML", + "html-section-content": "Περιεχόμενο ενότητας HTML", + "array-item": "Στοιχείο πίνακα", + "item-type": "Τύπος στοιχείου", + "item-name": "Όνομα στοιχείου", + "no-items": "Δεν υπάρχουν στοιχεία" + }, + "clear-form": "Εκκαθάριση φόρμας", + "clear-form-prompt": "Είστε βέβαιοι ότι θέλετε να αφαιρέσετε όλες τις ιδιότητες της φόρμας;", + "import-form": "Εισαγωγή φόρμας από JSON", + "export-form": "Εξαγωγή φόρμας σε JSON", + "json-file": "Αρχείο JSON", + "json-content": "Περιεχόμενο JSON", + "invalid-form-json-file-error": "Αδυναμία εισαγωγής φόρμας από JSON: Μη έγκυρη δομή δεδομένων φόρμας JSON." + }, + "asset-profile": { + "asset-profile": "Προφίλ περιουσιακού στοιχείου", + "asset-profiles": "Προφίλ περιουσιακών στοιχείων", + "all-asset-profiles": "Όλα", + "add": "Προσθήκη προφίλ περιουσιακού στοιχείου", + "edit": "Επεξεργασία προφίλ περιουσιακού στοιχείου", + "asset-profile-details": "Λεπτομέρειες προφίλ περιουσιακού στοιχείου", + "no-asset-profiles-text": "Δεν βρέθηκαν προφίλ περιουσιακών στοιχείων", + "search": "Αναζήτηση προφίλ περιουσιακών στοιχείων", + "selected-asset-profiles": "{ count, plural, =1 {1 προφίλ περιουσιακού στοιχείου} other {# προφίλ περιουσιακών στοιχείων} } επιλεγμένα", + "no-asset-profiles-matching": "Δεν βρέθηκε προφίλ περιουσιακού στοιχείου που να ταιριάζει με '{{entity}}'.", + "asset-profile-required": "Απαιτείται προφίλ περιουσιακού στοιχείου", + "idCopiedMessage": "Το Id του προφίλ περιουσιακού στοιχείου αντιγράφηκε στο πρόχειρο", + "set-default": "Ορισμός ως προεπιλεγμένο", + "delete": "Διαγραφή προφίλ περιουσιακού στοιχείου", + "copyId": "Αντιγραφή Id προφίλ περιουσιακού στοιχείου", + "name-max-length": "Το όνομα πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "new-device-profile-name": "Όνομα προφίλ περιουσιακού στοιχείου", + "new-device-profile-name-required": "Απαιτείται όνομα προφίλ περιουσιακού στοιχείου.", + "name": "Όνομα", + "name-required": "Το όνομα είναι υποχρεωτικό.", + "image": "Εικόνα προφίλ περιουσιακού στοιχείου", + "description": "Περιγραφή", + "default": "Προεπιλογή", + "default-rule-chain": "Προεπιλεγμένη αλυσίδα κανόνων", + "default-edge-rule-chain": "Προεπιλεγμένη αλυσίδα κανόνων Edge", + "default-edge-rule-chain-hint": "Χρησιμοποιείται στο Edge ως αλυσίδα κανόνων για την επεξεργασία εισερχόμενων δεδομένων για περιουσιακά στοιχεία αυτού του προφίλ", + "mobile-dashboard": "Πίνακας ελέγχου για κινητό", + "mobile-dashboard-hint": "Χρησιμοποιείται από την εφαρμογή για κινητό ως πίνακας λεπτομερειών περιουσιακού στοιχείου", + "select-queue-hint": "Επιλέξτε από αναπτυσσόμενη λίστα.", + "delete-asset-profile-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το προφίλ περιουσιακού στοιχείου '{{assetProfileName}}';", + "delete-asset-profile-text": "Προσοχή, μετά την επιβεβαίωση το προφίλ και όλα τα σχετικά δεδομένα δεν θα είναι ανακτήσιμα.", + "delete-asset-profiles-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 προφίλ περιουσιακού στοιχείου} other {# προφίλ περιουσιακών στοιχείων} };", + "delete-asset-profiles-text": "Προσοχή, μετά την επιβεβαίωση όλα τα επιλεγμένα προφίλ θα διαγραφούν και όλα τα σχετικά δεδομένα δεν θα είναι ανακτήσιμα.", + "set-default-asset-profile-title": "Είστε βέβαιοι ότι θέλετε να ορίσετε το προφίλ '{{assetProfileName}}' ως προεπιλεγμένο;", + "set-default-asset-profile-text": "Μετά την επιβεβαίωση, το προφίλ θα οριστεί ως προεπιλογή και θα χρησιμοποιείται για νέα περιουσιακά στοιχεία χωρίς καθορισμένο προφίλ.", + "no-asset-profiles-found": "Δεν βρέθηκαν προφίλ περιουσιακών στοιχείων.", + "create-new-asset-profile": "Δημιουργία νέου!", + "create-asset-profile": "Δημιουργία νέου προφίλ περιουσιακού στοιχείου", + "import": "Εισαγωγή προφίλ περιουσιακού στοιχείου", + "export": "Εξαγωγή προφίλ περιουσιακού στοιχείου", + "export-failed-error": "Αδυναμία εξαγωγής προφίλ: {{error}}", + "asset-profile-file": "Αρχείο προφίλ περιουσιακού στοιχείου", + "invalid-asset-profile-file-error": "Αδυναμία εισαγωγής προφίλ: Μη έγκυρη δομή δεδομένων προφίλ περιουσιακού στοιχείου." + }, + "device-profile": { + "device-profile": "Προφίλ συσκευής", + "device-profiles": "Προφίλ συσκευών", + "all-device-profiles": "Όλα", + "add": "Προσθήκη προφίλ συσκευής", + "edit": "Επεξεργασία προφίλ συσκευής", + "device-profile-details": "Λεπτομέρειες προφίλ συσκευής", + "no-device-profiles-text": "Δεν βρέθηκαν προφίλ συσκευών", + "search": "Αναζήτηση προφίλ συσκευών", + "selected-device-profiles": "{ count, plural, =1 {1 προφίλ συσκευής} other {# προφίλ συσκευών} } επιλεγμένα", + "no-device-profiles-matching": "Δεν βρέθηκε προφίλ συσκευής που να ταιριάζει με '{{entity}}'.", + "device-profile-required": "Απαιτείται προφίλ συσκευής", + "idCopiedMessage": "Το Id του προφίλ συσκευής αντιγράφηκε στο πρόχειρο", + "set-default": "Ορισμός ως προεπιλεγμένο", + "delete": "Διαγραφή προφίλ συσκευής", + "copyId": "Αντιγραφή Id προφίλ συσκευής", + "name-max-length": "Το όνομα πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "name": "Όνομα", + "name-required": "Το όνομα είναι υποχρεωτικό.", + "type": "Τύπος προφίλ", + "type-required": "Απαιτείται τύπος προφίλ.", + "type-default": "Προεπιλεγμένο", + "image": "Εικόνα προφίλ συσκευής", + "transport-type": "Τύπος μεταφοράς", + "transport-type-required": "Απαιτείται τύπος μεταφοράς.", + "transport-type-default": "Προεπιλεγμένο", + "transport-type-default-hint": "Υποστηρίζει βασικό μεταφορέα MQTT, HTTP και CoAP", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "Ενεργοποιεί σύνθετες ρυθμίσεις μεταφοράς MQTT", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "Ενεργοποιεί σύνθετες ρυθμίσεις μεταφοράς CoAP", + "transport-type-lwm2m": "LWM2M", + "transport-type-lwm2m-hint": "Τύπος μεταφοράς LWM2M", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "Καθορίστε ρυθμίσεις μεταφοράς SNMP", + "transport-type-http": "HTTP", + "description": "Περιγραφή", + "default": "Προεπιλογή", + "profile-configuration": "Ρυθμίσεις προφίλ", + "transport-configuration": "Ρυθμίσεις μεταφοράς", + "default-rule-chain": "Προεπιλεγμένη αλυσίδα κανόνων", + "default-edge-rule-chain": "Προεπιλεγμένη αλυσίδα κανόνων Edge", + "default-edge-rule-chain-hint": "Χρησιμοποιείται στο Edge για την επεξεργασία εισερχόμενων δεδομένων συσκευών αυτού του προφίλ", + "mobile-dashboard": "Πίνακας ελέγχου κινητού", + "mobile-dashboard-hint": "Χρησιμοποιείται από την εφαρμογή κινητού ως πίνακας λεπτομερειών συσκευής", + "select-queue-hint": "Επιλέξτε από αναπτυσσόμενη λίστα.", + "delete-device-profile-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το προφίλ συσκευής '{{deviceProfileName}}';", + "delete-device-profile-text": "Προσοχή, μετά την επιβεβαίωση το προφίλ και όλα τα σχετικά δεδομένα, περιλαμβανομένων των OTA ενημερώσεων, δεν θα είναι ανακτήσιμα.", + "delete-device-profiles-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 προφίλ συσκευής} other {# προφίλ συσκευών} };", + "delete-device-profiles-text": "Προσοχή, μετά την επιβεβαίωση όλα τα επιλεγμένα προφίλ θα διαγραφούν και όλα τα σχετικά δεδομένα, περιλαμβανομένων των OTA ενημερώσεων, δεν θα είναι ανακτήσιμα.", + "set-default-device-profile-title": "Είστε βέβαιοι ότι θέλετε να ορίσετε το προφίλ συσκευής '{{deviceProfileName}}' ως προεπιλεγμένο;", + "set-default-device-profile-text": "Μετά την επιβεβαίωση, το προφίλ θα οριστεί ως προεπιλογή και θα χρησιμοποιείται για νέες συσκευές χωρίς καθορισμένο προφίλ.", + "no-device-profiles-found": "Δεν βρέθηκαν προφίλ συσκευών.", + "create-new-device-profile": "Δημιουργία νέου!", + "mqtt-device-topic-filters": "Φίλτρα θεμάτων συσκευής MQTT", + "mqtt-device-topic-filters-unique": "Τα φίλτρα θεμάτων MQTT πρέπει να είναι μοναδικά.", + "mqtt-device-topic-filters-spark-plug": "Κόμβος Sparkplug B Edge of Network (EoN).", + "mqtt-device-topic-filters-spark-plug-hint": "Επιτρέπει συνδέσεις από κόμβους EoN με payload και μορφή θεμάτων Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "Μετρικές SparkPlug προς αποθήκευση ως χαρακτηριστικά.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Ονόματα μετρικών SparkPlug που θα αποθηκευτούν ως χαρακτηριστικά συσκευής. Όλες οι υπόλοιπες θα αποθηκευτούν ως τηλεμετρία.", + "mqtt-device-payload-type": "Payload συσκευής MQTT", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Ενεργοποίηση συμβατότητας με άλλες μορφές payload", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Όταν είναι ενεργοποιημένο, χρησιμοποιείται Protobuf ως προεπιλογή και γίνεται fallback σε JSON αν αποτύχει η ανάλυση. Χρήσιμο για υποστήριξη συσκευών κατά τη διάρκεια αναβάθμισης firmware.", + "mqtt-use-json-format-for-default-downlink-topics": "Χρήση μορφής JSON για τα προεπιλεγμένα θέματα downlink", + "mqtt-use-json-format-for-default-downlink-topics-hint": "Όταν ενεργοποιηθεί, χρησιμοποιείται JSON payload για αποστολή attributes και RPC μέσω θεμάτων v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, v1/devices/me/rpc/request/$request_id, v1/devices/me/rpc/response/$request_id. Δεν επηρεάζει νέα θέματα (v2): v2/a/res/$request_id κ.λπ.", + "mqtt-send-ack-on-validation-exception": "Αποστολή PUBACK σε αποτυχία επικύρωσης PUBLISH μηνύματος", + "mqtt-send-ack-on-validation-exception-hint": "Αντί για τερματισμό της συνεδρίας, η πλατφόρμα θα στείλει PUBACK αν είναι ενεργοποιημένο.", + "snmp-add-mapping": "Προσθήκη SNMP αντιστοίχισης", + "snmp-mapping-not-configured": "Δεν έχει διαμορφωθεί αντιστοίχιση OID σε χρονική σειρά/χαρακτηριστικό", + "snmp-timseries-or-attribute-name": "Όνομα για αντιστοίχιση χρονικής σειράς/χαρακτηριστικού", + "snmp-timseries-or-attribute-type": "Τύπος για αντιστοίχιση χρονικής σειράς/χαρακτηριστικού", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "Απαιτείται τύπος payload.", + "coap-device-type": "Τύπος συσκευής CoAP", + "coap-device-payload-type": "Payload συσκευής CoAP", + "coap-device-type-required": "Απαιτείται τύπος συσκευής CoAP.", + "coap-device-type-default": "Προεπιλεγμένος", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "Υποστηρίζονται μονοεπίπεδοι [+] και πολυεπίπεδοι [#] χαρακτήρες υποκατάστασης.", + "telemetry-topic-filter": "Φίλτρο θεμάτων τηλεμετρίας", + "telemetry-topic-filter-required": "Απαιτείται φίλτρο θεμάτων τηλεμετρίας.", + "attributes-topic-filter": "Φίλτρο θεμάτων δημοσίευσης χαρακτηριστικών", + "attributes-subscribe-topic-filter": "Φίλτρο θεμάτων εγγραφής χαρακτηριστικών", + "attributes-topic-filter-required": "Απαιτείται φίλτρο θεμάτων δημοσίευσης χαρακτηριστικών.", + "attributes-subscribe-topic-filter-required": "Απαιτείται φίλτρο θεμάτων εγγραφής χαρακτηριστικών.", + "telemetry-proto-schema": "Σχήμα proto τηλεμετρίας", + "telemetry-proto-schema-required": "Απαιτείται σχήμα proto τηλεμετρίας.", + "attributes-proto-schema": "Σχήμα proto χαρακτηριστικών", + "attributes-proto-schema-required": "Απαιτείται σχήμα proto χαρακτηριστικών.", + "rpc-response-proto-schema": "Σχήμα proto απόκρισης RPC", + "rpc-response-proto-schema-required": "Απαιτείται σχήμα proto απόκρισης RPC.", + "rpc-response-topic-filter": "Φίλτρο θεμάτων απόκρισης RPC", + "rpc-response-topic-filter-required": "Απαιτείται φίλτρο θεμάτων απόκρισης RPC.", + "rpc-request-proto-schema": "Σχήμα proto αιτήματος RPC", + "rpc-request-proto-schema-required": "Απαιτείται σχήμα proto αιτήματος RPC.", + "rpc-request-proto-schema-hint": "Το μήνυμα RPC πρέπει να περιέχει πάντα: string method = 1; int32 requestId = 2; και params = 3 οποιουδήποτε τύπου δεδομένων.", + "not-valid-pattern-topic-filter": "Μη έγκυρο μοτίβο φίλτρου θεμάτων", + "not-valid-single-character": "Μη έγκυρη χρήση μονοεπίπεδου χαρακτήρα υποκατάστασης", + "not-valid-multi-character": "Μη έγκυρη χρήση πολυεπίπεδου χαρακτήρα υποκατάστασης", + "single-level-wildcards-hint": "[+] είναι κατάλληλο για οποιοδήποτε επίπεδο στο φίλτρο θεμάτων. Π.χ.: v1/devices/+/telemetry ή +/devices/+/attributes.", + "multi-level-wildcards-hint": "[#] μπορεί να αντικαταστήσει ολόκληρο το φίλτρο και πρέπει να είναι το τελευταίο σύμβολο. Π.χ.: # ή v1/devices/me/#.", + "alarm-rules": "Κανόνες συναγερμού", + "alarm-rules-with-count": "Κανόνες συναγερμού ({{count}})", + "no-alarm-rules": "Δεν έχουν διαμορφωθεί κανόνες συναγερμού", + "add-alarm-rule": "Προσθήκη κανόνα συναγερμού", + "edit-alarm-rule": "Επεξεργασία κανόνα συναγερμού", + "alarm-type": "Τύπος συναγερμού", + "alarm-type-required": "Απαιτείται τύπος συναγερμού.", + "alarm-type-unique": "Ο τύπος συναγερμού πρέπει να είναι μοναδικός στο προφίλ.", + "alarm-type-max-length": "Ο τύπος συναγερμού πρέπει να είναι μικρότερος από 256 χαρακτήρες.", + "create-alarm-pattern": "Δημιουργία συναγερμού {{alarmType}}", + "create-alarm-rules": "Δημιουργία κανόνων συναγερμού", + "no-create-alarm-rules": "Δεν έχουν διαμορφωθεί συνθήκες δημιουργίας", + "add-create-alarm-rule-prompt": "Προσθέστε κανόνα δημιουργίας συναγερμού", + "clear-alarm-rule": "Εκκαθάριση κανόνα συναγερμού", + "no-clear-alarm-rule": "Δεν έχει διαμορφωθεί συνθήκη εκκαθάρισης", + "add-create-alarm-rule": "Προσθήκη συνθήκης δημιουργίας", + "add-clear-alarm-rule": "Προσθήκη συνθήκης εκκαθάρισης", + "select-alarm-severity": "Επιλέξτε σοβαρότητα συναγερμού", + "alarm-severity-required": "Απαιτείται σοβαρότητα συναγερμού.", + "condition-duration": "Διάρκεια συνθήκης", + "condition-duration-value": "Τιμή διάρκειας", + "condition-duration-time-unit": "Μονάδα χρόνου", + "condition-duration-value-range": "Η διάρκεια πρέπει να είναι από 1 έως 2147483647.", + "condition-duration-value-pattern": "Η διάρκεια πρέπει να είναι ακέραιος αριθμός.", + "condition-duration-value-required": "Απαιτείται τιμή διάρκειας.", + "condition-duration-time-unit-required": "Απαιτείται μονάδα χρόνου.", + "advanced-settings": "Προηγμένες ρυθμίσεις", + "alarm-rule-additional-info": "Επιπλέον πληροφορίες", + "edit-alarm-rule-additional-info": "Επεξεργασία επιπλέον πληροφοριών", + "alarm-rule-additional-info-placeholder": "Προσθέστε σχόλια ή ρυθμίσεις που θα εμφανίζονται στις λεπτομέρειες του συναγερμού", + "alarm-rule-additional-info-hint": "Υπόδειξη: χρησιμοποιήστε ${keyName} για την υποκατάσταση τιμών από χαρακτηριστικά ή τηλεμετρία.", + "alarm-rule-mobile-dashboard": "Πίνακας ελέγχου για κινητό", + "alarm-rule-mobile-dashboard-hint": "Χρησιμοποιείται ως ταμπλό λεπτομερειών συναγερμού στην εφαρμογή κινητού", + "alarm-rule-no-mobile-dashboard": "Δεν επιλέχθηκε ταμπλό", + "propagate-alarm": "Διάδοση συναγερμού σε σχετιζόμενες οντότητες", + "alarm-rule-relation-types-list": "Τύποι σχέσεων", + "alarm-rule-relation-types-list-hint": "Καθορίζει τους τύπους σχέσεων για το φιλτράρισμα σχετικών οντοτήτων. Αν δεν οριστεί, ο συναγερμός διαδίδεται σε όλες.", + "propagate-alarm-to-owner": "Διάδοση στον κάτοχο της οντότητας (Πελάτης ή Μισθωτής)", + "propagate-alarm-to-tenant": "Διάδοση στον Μισθωτή", + "alarm-rule-condition": "Συνθήκη κανόνα συναγερμού", + "enter-alarm-rule-condition-prompt": "Προσθέστε συνθήκη κανόνα συναγερμού", + "edit-alarm-rule-condition": "Επεξεργασία συνθήκης κανόνα συναγερμού", + "device-provisioning": "Διαμόρφωση συσκευής", + "provision-strategy": "Στρατηγική διαμόρφωσης", + "provision-strategy-required": "Απαιτείται στρατηγική διαμόρφωσης.", + "provision-strategy-disabled": "Απενεργοποιημένη", + "provision-strategy-created-new": "Επιτρέπεται δημιουργία νέων συσκευών", + "provision-strategy-check-pre-provisioned": "Έλεγχος για προκαθορισμένες συσκευές", + "provision-device-key": "Κλειδί διαμόρφωσης", + "provision-device-key-required": "Απαιτείται κλειδί διαμόρφωσης.", + "copy-provision-key": "Αντιγραφή κλειδιού διαμόρφωσης", + "provision-key-copied-message": "Το κλειδί διαμόρφωσης αντιγράφηκε στο πρόχειρο", + "provision-device-secret": "Μυστικό διαμόρφωσης", + "provision-device-secret-required": "Απαιτείται μυστικό διαμόρφωσης.", + "copy-provision-secret": "Αντιγραφή μυστικού διαμόρφωσης", + "provision-secret-copied-message": "Το μυστικό διαμόρφωσης αντιγράφηκε στο πρόχειρο", + "provision-strategy-x509": { + "certificate-chain": "Αλυσίδα Πιστοποιητικών X509", + "certificate-chain-hint": "Η στρατηγική X.509 χρησιμοποιείται για διαμόρφωση μέσω πιστοποιητικών πελάτη σε διπλής κατεύθυνσης TLS.", + "allow-create-new-devices": "Δημιουργία νέων συσκευών", + "allow-create-new-devices-hint": "Αν επιλεγεί, θα δημιουργούνται νέες συσκευές και το πιστοποιητικό θα χρησιμοποιείται ως διαπιστευτήρια.", + "certificate-value": "Πιστοποιητικό σε μορφή PEM", + "certificate-value-required": "Απαιτείται πιστοποιητικό σε μορφή PEM", + "cn-regex-variable": "Μεταβλητή CN με Regular Expression", + "cn-regex-variable-required": "Απαιτείται μεταβλητή CN με Regular Expression", + "cn-regex-variable-hint": "Απαραίτητη για την εξαγωγή ονόματος συσκευής από το κοινό όνομα του πιστοποιητικού X509." + }, + "condition": "Συνθήκη", + "condition-type": "Τύπος συνθήκης", + "condition-type-simple": "Απλή", + "condition-type-duration": "Διάρκεια", + "condition-during": "Κατά τη διάρκεια {{during}}", + "condition-during-dynamic": "Κατά τη διάρκεια \"{{ attribute }}\" ({{during}})", + "condition-type-repeating": "Επανάληψη", + "condition-type-required": "Απαιτείται τύπος συνθήκης.", + "condition-repeating-value": "Αριθμός γεγονότων", + "condition-repeating-value-range": "Ο αριθμός γεγονότων πρέπει να είναι μεταξύ 1 και 2147483647.", + "condition-repeating-value-pattern": "Ο αριθμός γεγονότων πρέπει να είναι ακέραιος αριθμός.", + "condition-repeating-value-required": "Απαιτείται αριθμός γεγονότων.", + "condition-repeat-times": "Επαναλαμβάνεται { count, plural, =1 {1 φορά} other {# φορές} }", + "condition-repeat-times-dynamic": "Επαναλαμβάνεται \"{ attribute }\" ({ count, plural, =1 {1 φορά} other {# φορές} })", + "schedule-type": "Τύπος προγραμματιστή", + "schedule-type-required": "Απαιτείται τύπος προγραμματιστή.", + "schedule": "Πρόγραμμα", + "edit-schedule": "Επεξεργασία προγράμματος συναγερμού", + "schedule-any-time": "Ενεργό ανά πάσα στιγμή", + "schedule-specific-time": "Ενεργό σε συγκεκριμένο χρόνο", + "schedule-custom": "Προσαρμοσμένο", + "schedule-day": { + "monday": "Δευτέρα", + "tuesday": "Τρίτη", + "wednesday": "Τετάρτη", + "thursday": "Πέμπτη", + "friday": "Παρασκευή", + "saturday": "Σάββατο", + "sunday": "Κυριακή" + }, + "schedule-days": "Ημέρες", + "schedule-time": "Ώρα", + "schedule-time-from": "Από", + "schedule-time-to": "Έως", + "schedule-days-of-week-required": "Πρέπει να επιλεγεί τουλάχιστον μία ημέρα της εβδομάδας.", + "create-device-profile": "Δημιουργία νέου προφίλ συσκευής", + "import": "Εισαγωγή προφίλ συσκευής", + "export": "Εξαγωγή προφίλ συσκευής", + "export-failed-error": "Αποτυχία εξαγωγής προφίλ συσκευής: {{error}}", + "device-profile-file": "Αρχείο προφίλ συσκευής", + "invalid-device-profile-file-error": "Αποτυχία εισαγωγής προφίλ συσκευής: Μη έγκυρη δομή δεδομένων.", + "power-saving-mode": "Λειτουργία εξοικονόμησης ενέργειας", + "power-saving-mode-type": { + "default": "Χρήση λειτουργίας εξοικονόμησης από το προφίλ", + "psm": "Λειτουργία εξοικονόμησης ενέργειας (PSM)", + "drx": "Διακοπτόμενη λήψη (DRX)", + "edrx": "Εκτεταμένη διακοπτόμενη λήψη (eDRX)" + }, + "edrx-cycle": "Κύκλος eDRX", + "edrx-cycle-required": "Απαιτείται κύκλος eDRX.", + "edrx-cycle-pattern": "Ο κύκλος eDRX πρέπει να είναι θετικός ακέραιος.", + "edrx-cycle-min": "Ελάχιστος αριθμός κύκλου eDRX είναι {{ min }} δευτερόλεπτα.", + "paging-transmission-window": "Παράθυρο μετάδοσης σελίδας", + "paging-transmission-window-required": "Απαιτείται παράθυρο μετάδοσης σελίδας.", + "paging-transmission-window-pattern": "Το παράθυρο μετάδοσης σελίδας πρέπει να είναι θετικός ακέραιος.", + "paging-transmission-window-min": "Ελάχιστος αριθμός παραθύρου μετάδοσης σελίδας είναι {{ min }} δευτερόλεπτα.", + "psm-activity-timer": "Χρονοδιακόπτης δραστηριότητας PSM", + "psm-activity-timer-required": "Απαιτείται χρονοδιακόπτης δραστηριότητας PSM.", + "psm-activity-timer-pattern": "Ο χρονοδιακόπτης δραστηριότητας PSM πρέπει να είναι θετικός ακέραιος.", + "psm-activity-timer-min": "Ελάχιστος χρόνος δραστηριότητας PSM είναι {{ min }} δευτερόλεπτα.", + "lwm2m": { + "object-list": "Λίστα αντικειμένων", + "object-list-empty": "Δεν έχουν επιλεγεί αντικείμενα.", + "no-objects-found": "Δεν βρέθηκαν αντικείμενα.", + "no-objects-matching": "Δεν βρέθηκαν αντικείμενα που να ταιριάζουν με '{{object}}'.", + "model-tab": "Μοντέλο LWM2M", + "add-new-instances": "Προσθήκη νέων στιγμιοτύπων", + "instances-list": "Λίστα στιγμιοτύπων", + "instances-list-required": "Απαιτείται λίστα στιγμιοτύπων.", + "instance-id-pattern": "Το ID του στιγμιοτύπου πρέπει να είναι θετικός ακέραιος.", + "instance-id-max": "Μέγιστη τιμή ID στιγμιοτύπου {{max}}.", + "instance": "Στιγμιότυπο", + "resource-label": "#ID Όνομα πόρου", + "observe-label": "Παρακολούθηση", + "attribute-label": "Χαρακτηριστικό", + "telemetry-label": "Τηλεμετρία", + "edit-observe-select": "Για επεξεργασία παρακολούθησης επιλέξτε τηλεμετρία ή χαρακτηριστικό", + "edit-attributes-select": "Για επεξεργασία χαρακτηριστικών επιλέξτε τηλεμετρία ή χαρακτηριστικό", + "no-attributes-set": "Δεν έχουν οριστεί χαρακτηριστικά", + "key-name": "Όνομα κλειδιού", + "key-name-required": "Απαιτείται όνομα κλειδιού", + "attribute-name": "Όνομα χαρακτηριστικού", + "attribute-name-required": "Απαιτείται όνομα χαρακτηριστικού.", + "attribute-value": "Τιμή χαρακτηριστικού", + "attribute-value-required": "Απαιτείται τιμή χαρακτηριστικού.", + "attribute-value-pattern": "Η τιμή χαρακτηριστικού πρέπει να είναι θετικός ακέραιος.", + "edit-attributes": "Επεξεργασία χαρακτηριστικών: {{ name }}", + "view-attributes": "Προβολή χαρακτηριστικών: {{ name }}", + "add-attribute": "Προσθήκη χαρακτηριστικού", + "edit-attribute": "Επεξεργασία χαρακτηριστικού", + "view-attribute": "Προβολή χαρακτηριστικού", + "remove-attribute": "Αφαίρεση χαρακτηριστικού", + "delete-server-text": "Προσοχή, μετά την επιβεβαίωση οι ρυθμίσεις του διακομιστή δεν θα είναι ανακτήσιμες.", + "delete-server-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε τον διακομιστή;", + "mode": "Λειτουργία ρυθμίσεων ασφαλείας", + "bootstrap-tab": "Bootstrap", + "bootstrap-server-legend": "Bootstrap διακομιστής (ShortId...)", + "lwm2m-server-legend": "LwM2M διακομιστής (ShortId...)", + "server": "Διακομιστής", + "short-id": "Σύντομο ID διακομιστή", + "short-id-tooltip": "Σύντομο Id διακομιστή. Χρησιμοποιείται για σύνδεση με στιγμιότυπο αντικειμένου.\nΠρέπει να οριστεί όταν το πεδίο Bootstrap-Server έχει τιμή 'false'.\nΔεν επιτρέπεται η χρήση των ID:0 και ID:65535.", + "short-id-tooltip-bootstrap": "Χρησιμοποιείται για σύνδεση με στιγμιότυπο αντικειμένου διακομιστή.\nΠρέπει να οριστεί όταν το πεδίο Bootstrap-Server έχει τιμή 'false'.", + "short-id-required": "Απαιτείται σύντομο ID διακομιστή.", + "short-id-range": "Το σύντομο ID διακομιστή πρέπει να είναι μεταξύ {{ min }} και {{ max }}.", + "short-id-pattern": "Το σύντομο ID διακομιστή πρέπει να είναι θετικός ακέραιος.", + "lifetime": "Διάρκεια εγγραφής πελάτη", + "lifetime-required": "Απαιτείται διάρκεια εγγραφής πελάτη.", + "lifetime-pattern": "Η διάρκεια πρέπει να είναι θετικός ακέραιος.", + "default-min-period": "Ελάχιστη περίοδος μεταξύ ειδοποιήσεων (δ)", + "default-min-period-tooltip": "Χρησιμοποιείται αν δεν περιλαμβάνεται η παράμετρος στην παρατήρηση.", + "default-min-period-required": "Απαιτείται ελάχιστη περίοδος.", + "default-min-period-pattern": "Η ελάχιστη περίοδος πρέπει να είναι θετικός ακέραιος.", + "notification-storing": "Αποθήκευση ειδοποιήσεων όταν είναι απενεργοποιημένο ή εκτός σύνδεσης", + "binding": "Δεσμευτικό", + "binding-type": { + "u": "U: Το client είναι προσβάσιμο μέσω UDP ανά πάσα στιγμή.", + "m": "M: Το client είναι προσβάσιμο μέσω MQTT ανά πάσα στιγμή.", + "h": "H: Το client είναι προσβάσιμο μέσω HTTP ανά πάσα στιγμή.", + "t": "T: Το client είναι προσβάσιμο μέσω TCP ανά πάσα στιγμή.", + "s": "S: Το client είναι προσβάσιμο μέσω SMS ανά πάσα στιγμή.", + "n": "N: Το client πρέπει να απαντήσει μέσω Non-IP (υποστηρίζεται από LWM2M 1.1).", + "uq": "UQ: UDP σε queue mode (δεν υποστηρίζεται από LWM2M 1.1)", + "uqs": "UQS: UDP + SMS ενεργά· UDP σε queue mode, SMS σε standard (δεν υποστηρίζεται από LWM2M 1.1)", + "tq": "TQ: TCP σε queue mode (δεν υποστηρίζεται από LWM2M 1.1)", + "tqs": "TQS: TCP + SMS ενεργά· TCP σε queue mode, SMS σε standard (δεν υποστηρίζεται από LWM2M 1.1)", + "sq": "SQ: SMS σε queue mode (δεν υποστηρίζεται από LWM2M 1.1)" + }, + "binding-tooltip": "Ορίζει τα υποστηριζόμενα binding modes για το LwM2M Client. Πρέπει να είναι ίδιο με το \"Supported Binding and Modes\" στο αντικείμενο Συσκευής. Υποστηρίζεται ένα binding ανά συνεδρία μεταφοράς.", + "bootstrap-server": "Bootstrap διακομιστής", + "lwm2m-server": "LwM2M διακομιστής", + "include-bootstrap-server": "Συμπερίληψη ενημερώσεων Bootstrap Server", + "bootstrap-update-title": "Ο Bootstrap Server έχει ήδη διαμορφωθεί. Θέλετε σίγουρα να εξαιρεθεί;", + "bootstrap-update-text": "Προσοχή, τα δεδομένα διαμόρφωσης θα χαθούν μετά την επιβεβαίωση.", + "server-host": "Διεύθυνση υποδοχής", + "server-host-required": "Απαιτείται διεύθυνση υποδοχής.", + "server-port": "Θύρα", + "server-port-required": "Απαιτείται θύρα.", + "server-port-pattern": "Η θύρα πρέπει να είναι θετικός ακέραιος.", + "server-port-range": "Η θύρα πρέπει να είναι από 1 έως 65535.", + "server-public-key": "Δημόσιο κλειδί διακομιστή", + "server-public-key-required": "Απαιτείται δημόσιο κλειδί διακομιστή.", + "client-hold-off-time": "Χρόνος αναμονής πελάτη", + "client-hold-off-time-required": "Απαιτείται χρόνος αναμονής πελάτη.", + "client-hold-off-time-pattern": "Ο χρόνος αναμονής πρέπει να είναι θετικός ακέραιος.", + "client-hold-off-time-tooltip": "Για χρήση μόνο με Bootstrap-Server", + "account-after-timeout": "Λογαριασμός μετά το χρονικό όριο", + "account-after-timeout-required": "Απαιτείται ρύθμιση λογαριασμού μετά το χρονικό όριο.", + "account-after-timeout-pattern": "Η τιμή πρέπει να είναι θετικός ακέραιος.", + "account-after-timeout-tooltip": "Ρύθμιση Bootstrap-Server για χρονικό όριο", + "server-type": "Τύπος διακομιστή", + "add-new-server-title": "Προσθήκη νέας ρύθμισης διακομιστή", + "add-server-config": "Προσθήκη ρύθμισης διακομιστή", + "add-lwm2m-server-config": "Προσθήκη διακομιστή LwM2M", + "no-config-servers": "Δεν έχουν διαμορφωθεί διακομιστές", + "others-tab": "Άλλες ρυθμίσεις", + "client-strategy": "Στρατηγική πελάτη κατά τη σύνδεση", + "client-strategy-label": "Στρατηγική", + "client-strategy-only-observe": "Μόνο αίτημα παρακολούθησης μετά την αρχική σύνδεση", + "client-strategy-read-all": "Ανάγνωση όλων των πόρων και αίτημα παρακολούθησης μετά την εγγραφή", + "fw-update": "Αναβάθμιση firmware", + "fw-update-strategy": "Στρατηγική αναβάθμισης firmware", + "fw-update-strategy-data": "Αποστολή του αρχείου firmware ως δυαδικό αρχείο χρησιμοποιώντας Object 19 και Resource 0 (Data)", + "fw-update-strategy-package": "Αποστολή του αρχείου firmware ως δυαδικό αρχείο χρησιμοποιώντας Object 5 και Resource 0 (Package)", + "fw-update-strategy-package-uri": "Αυτόματη δημιουργία μοναδικού CoAP URL για λήψη και αποστολή αναβάθμισης firmware μέσω Object 5 και Resource 1 (Package URI)", + "sw-update": "Αναβάθμιση λογισμικού", + "sw-update-strategy": "Στρατηγική αναβάθμισης λογισμικού", + "sw-update-strategy-package": "Αποστολή του αρχείου λογισμικού χρησιμοποιώντας Object 9 και Resource 2 (Package)", + "sw-update-strategy-package-uri": "Αυτόματη δημιουργία μοναδικού CoAP URL για λήψη και αποστολή αναβάθμισης λογισμικού μέσω Object 9 και Resource 3 (Package URI)", + "fw-update-resource": "Πόρος CoAP αναβάθμισης firmware", + "fw-update-resource-required": "Απαιτείται πόρος CoAP αναβάθμισης firmware.", + "sw-update-resource": "Πόρος CoAP αναβάθμισης λογισμικού", + "sw-update-resource-required": "Απαιτείται πόρος CoAP αναβάθμισης λογισμικού.", + "config-json-tab": "Json διαμόρφωση προφίλ συσκευής", + "attributes-name": { + "min-period": "Ελάχιστη περίοδος", + "max-period": "Μέγιστη περίοδος", + "greater-than": "Μεγαλύτερο από", + "less-than": "Μικρότερο από", + "step": "Βήμα", + "min-evaluation-period": "Ελάχιστη περίοδος αξιολόγησης", + "max-evaluation-period": "Μέγιστη περίοδος αξιολόγησης" + }, + "default-object-id": "Προεπιλεγμένη έκδοση αντικειμένου (χαρακτηριστικό)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1" + } + }, + "snmp": { + "add-communication-config": "Προσθήκη ρύθμισης επικοινωνίας", + "add-mapping": "Προσθήκη αντιστοίχισης", + "authentication-passphrase": "Κωδικός ελέγχου ταυτότητας", + "authentication-passphrase-required": "Απαιτείται κωδικός ελέγχου ταυτότητας.", + "authentication-protocol": "Πρωτόκολλο ελέγχου ταυτότητας", + "authentication-protocol-required": "Απαιτείται πρωτόκολλο ελέγχου ταυτότητας.", + "communication-configs": "Ρυθμίσεις επικοινωνίας", + "community": "Community string", + "community-required": "Απαιτείται το community string.", + "context-name": "Όνομα πλαισίου (Context name)", + "data-key": "Κλειδί δεδομένων", + "data-key-required": "Απαιτείται κλειδί δεδομένων.", + "data-type": "Τύπος δεδομένων", + "data-type-required": "Απαιτείται τύπος δεδομένων.", + "engine-id": "ID μηχανής", + "host": "Διακομιστής", + "host-required": "Απαιτείται διακομιστής.", + "oid": "OID", + "oid-pattern": "Μη έγκυρη μορφή OID.", + "oid-required": "Απαιτείται OID.", + "please-add-communication-config": "Προσθέστε ρύθμιση επικοινωνίας", + "please-add-mapping-config": "Προσθέστε ρύθμιση αντιστοίχισης", + "port": "Θύρα", + "port-format": "Μη έγκυρη μορφή θύρας.", + "port-required": "Απαιτείται θύρα.", + "privacy-passphrase": "Κωδικός απορρήτου", + "privacy-passphrase-required": "Απαιτείται κωδικός απορρήτου.", + "privacy-protocol": "Πρωτόκολλο απορρήτου", + "privacy-protocol-required": "Απαιτείται πρωτόκολλο απορρήτου.", + "protocol-version": "Έκδοση πρωτοκόλλου", + "protocol-version-required": "Απαιτείται έκδοση πρωτοκόλλου.", + "querying-frequency": "Συχνότητα ερωτήσεων, ms", + "querying-frequency-invalid-format": "Η συχνότητα πρέπει να είναι θετικός ακέραιος.", + "querying-frequency-required": "Απαιτείται συχνότητα ερωτήσεων.", + "retries": "Επαναλήψεις", + "retries-invalid-format": "Οι επαναλήψεις πρέπει να είναι θετικός ακέραιος.", + "retries-required": "Απαιτούνται επαναλήψεις.", + "scope": "Εμβέλεια", + "scope-required": "Απαιτείται εμβέλεια.", + "security-name": "Όνομα ασφαλείας", + "security-name-required": "Απαιτείται όνομα ασφαλείας.", + "timeout-ms": "Χρονικό όριο, ms", + "timeout-ms-invalid-format": "Το χρονικό όριο πρέπει να είναι θετικός ακέραιος.", + "timeout-ms-required": "Απαιτείται χρονικό όριο.", + "user-name": "Όνομα χρήστη", + "user-name-required": "Απαιτείται όνομα χρήστη." + } }, "dialog": { - "close": "Κλείσιμο διαλόγου" + "close": "Κλείσιμο παραθύρου", + "error-message-title": "Μήνυμα σφάλματος:", + "error-details-title": "Λεπτομέρειες σφάλματος" }, "direction": { "column": "Στήλη", "row": "Γραμμή" }, + "edge": { + "edge": "Edge", + "edge-instances": "Παραδείγματα Edge", + "instances": "Παραδείγματα", + "edge-file": "Αρχείο Edge", + "name-max-length": "Το όνομα πρέπει να έχει λιγότερους από 256 χαρακτήρες", + "label-max-length": "Η ετικέτα πρέπει να έχει λιγότερους από 256 χαρακτήρες", + "type-max-length": "Ο τύπος πρέπει να έχει λιγότερους από 256 χαρακτήρες", + "management": "Διαχείριση Edge", + "no-edges-matching": "Δεν βρέθηκαν εγκαταστάσεις Edge που να ταιριάζουν με '{{entity}}'.", + "add": "Προσθήκη Edge", + "no-edges-text": "Δεν βρέθηκαν εγκαταστάσεις Edge", + "edge-details": "Λεπτομέρειες Edge", + "add-edge-text": "Προσθήκη νέας εγκατάστασης Edge", + "delete": "Διαγραφή Edge", + "delete-edge-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την εγκατάσταση Edge '{{edgeName}}';", + "delete-edge-text": "Προσοχή, μετά την επιβεβαίωση η εγκατάσταση και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", + "delete-edges-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 εγκατάσταση Edge} other {# εγκαταστάσεις Edge} };", + "delete-edges-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες εγκαταστάσεις Edge και τα σχετικά δεδομένα θα διαγραφούν οριστικά.", + "name": "Όνομα", + "name-starts-with": "Το όνομα Edge ξεκινά με", + "name-required": "Το όνομα είναι υποχρεωτικό.", + "description": "Περιγραφή", + "details": "Λεπτομέρειες", + "events": "Συμβάντα", + "copy-id": "Αντιγραφή Id Edge", + "id-copied-message": "Το Id Edge αντιγράφηκε στο πρόχειρο", + "sync": "Συγχρονισμός Edge", + "edge-required": "Η εγκατάσταση Edge είναι υποχρεωτική", + "edge-type": "Τύπος Edge", + "edge-type-required": "Ο τύπος Edge είναι υποχρεωτικός.", + "event-action": "Ενέργεια συμβάντος", + "entity-id": "ID οντότητας", + "select-edge-type": "Επιλογή τύπου Edge", + "assign-to-customer": "Ανάθεση σε πελάτη", + "assign-to-customer-text": "Επιλέξτε τον πελάτη στον οποίο θα ανατεθεί η εγκατάσταση Edge", + "assign-edge-to-customer": "Ανάθεση Edge σε πελάτη", + "assign-edge-to-customer-text": "Επιλέξτε τις εγκαταστάσεις Edge που θα ανατεθούν στον πελάτη", + "assignedToCustomer": "Ανατέθηκε σε πελάτη", + "edge-public": "Η εγκατάσταση Edge είναι δημόσια", + "assigned-to-customer": "Ανατέθηκε στον: {{customerTitle}}", + "unassign-from-customer": "Αποκαθήλωση από πελάτη", + "unassign-edge-title": "Είστε βέβαιοι ότι θέλετε να αποκαθηλώσετε την εγκατάσταση Edge '{{edgeName}}';", + "unassign-edge-text": "Μετά την επιβεβαίωση, η εγκατάσταση Edge δεν θα είναι πλέον προσβάσιμη από τον πελάτη.", + "unassign-edges-title": "Είστε βέβαιοι ότι θέλετε να αποκαθηλώσετε { count, plural, =1 {1 εγκατάσταση Edge} other {# εγκαταστάσεις Edge} };", + "unassign-edges-text": "Μετά την επιβεβαίωση, οι επιλεγμένες εγκαταστάσεις Edge δεν θα είναι πλέον προσβάσιμες από τον πελάτη.", + "make-public": "Δημοσιοποίηση Edge", + "make-public-edge-title": "Είστε βέβαιοι ότι θέλετε να κάνετε την εγκατάσταση Edge '{{edgeName}}' δημόσια;", + "make-public-edge-text": "Μετά την επιβεβαίωση, η εγκατάσταση και όλα τα δεδομένα της θα είναι δημόσια και προσβάσιμα από τρίτους.", + "make-private": "Ιδιωτικοποίηση Edge", + "public": "Δημόσιο", + "make-private-edge-title": "Είστε βέβαιοι ότι θέλετε να κάνετε την εγκατάσταση Edge '{{edgeName}}' ιδιωτική;", + "make-private-edge-text": "Μετά την επιβεβαίωση, η εγκατάσταση και όλα τα δεδομένα της θα είναι ιδιωτικά και δεν θα είναι προσβάσιμα από τρίτους.", + "import": "Εισαγωγή Edge", + "install-connect-instructions": "Οδηγίες εγκατάστασης και σύνδεσης", + "install-connect-instructions-edge-created": "Η εγκατάσταση Edge δημιουργήθηκε! Ελέγξτε τις Οδηγίες Εγκατάστασης και Σύνδεσης", + "loading-edge-instructions": "Φόρτωση οδηγιών Edge...", + "label": "Ετικέτα", + "load-entity-error": "Αποτυχία φόρτωσης δεδομένων. Η οντότητα έχει διαγραφεί.", + "assign-new-edge": "Ανάθεση νέου Edge", + "unassign-from-edge": "Αποκαθήλωση από Edge", + "edge-key": "Κλειδί Edge", + "copy-edge-key": "Αντιγραφή κλειδιού Edge", + "edge-key-copied-message": "Το κλειδί Edge αντιγράφηκε στο πρόχειρο", + "edge-secret": "Μυστικό Edge", + "copy-edge-secret": "Αντιγραφή μυστικού Edge", + "edge-secret-copied-message": "Το μυστικό Edge αντιγράφηκε στο πρόχειρο", + "manage-assets": "Διαχείριση assets", + "manage-devices": "Διαχείριση συσκευών", + "manage-entity-views": "Διαχείριση προβολών οντοτήτων", + "manage-dashboards": "Διαχείριση dashboards", + "manage-rulechains": "Διαχείριση αλυσίδων κανόνων", + "assets": "Assets Edge", + "devices": "Συσκευές Edge", + "entity-views": "Προβολές οντοτήτων Edge", + "dashboard": "Dashboard Edge", + "dashboards": "Dashboards Edge", + "rulechain-templates": "Πρότυπα rule chain", + "edge-rulechain-templates": "Πρότυπα rule chain Edge", + "rulechains": "Rule chains Edge", + "search": "Αναζήτηση Edge", + "selected-edges": "{ count, plural, =1 {1 εγκατάσταση Edge} other {# εγκαταστάσεις Edge} } επιλεγμένες", + "any-edge": "Οποιοδήποτε Edge", + "no-edge-types-matching": "Δεν βρέθηκαν τύποι Edge που να ταιριάζουν με '{{entitySubtype}}'.", + "edge-type-list-empty": "Δεν έχουν επιλεγεί τύποι Edge.", + "edge-types": "Τύποι Edge", + "enter-edge-type": "Εισάγετε τύπο Edge", + "deployed": "Αναπτυγμένο", + "pending": "Εκκρεμεί", + "downlinks": "Downlinks", + "no-downlinks-prompt": "Δεν βρέθηκαν downlinks", + "sync-process-started-successfully": "Η διαδικασία συγχρονισμού ξεκίνησε με επιτυχία!", + "missing-related-rule-chains-title": "Το Edge έχει ελλείποντα σχετικά rule chains", + "missing-related-rule-chains-text": "Τα ανατεθειμένα rule chains χρησιμοποιούν rule nodes που προωθούν μηνύματα σε άλλα rule chains που δεν είναι ανατεθειμένα σε αυτό το Edge.

Λίστα ελλειπόντων rule chains:
{{missingRuleChains}}", + "widget-datasource-error": "Αυτό το widget υποστηρίζει μόνο datasource τύπου EDGE entity", + "upgrade-instructions": "Οδηγίες αναβάθμισης", + "connected": "Συνδεδεμένο", + "disconnected": "Αποσυνδεδεμένο" + }, + "edge-event": { + "type-dashboard": "Πίνακας ελέγχου", + "type-asset": "Πόρος", + "type-device": "Συσκευή", + "type-device-profile": "Προφίλ συσκευής", + "type-asset-profile": "Προφίλ πόρου", + "type-entity-view": "Προβολή οντότητας", + "type-alarm": "Συναγερμός", + "type-rule-chain": "Αλυσίδα κανόνων", + "type-rule-chain-metadata": "Μεταδεδομένα αλυσίδας κανόνων", + "type-edge": "Edge", + "type-user": "Χρήστης", + "type-tenant": "Ενοικιαστής", + "type-tenant-profile": "Προφίλ ενοικιαστή", + "type-customer": "Πελάτης", + "type-relation": "Σχέση", + "type-widgets-bundle": "Πακέτο γραφικών", + "type-widgets-type": "Τύπος γραφικού", + "type-admin-settings": "Ρυθμίσεις διαχειριστή", + "type-ota-package": "Πακέτο OTA", + "type-queue": "Ουρά", + "action-type-added": "Προστέθηκε", + "action-type-deleted": "Διαγράφηκε", + "action-type-updated": "Ενημερώθηκε", + "action-type-post-attributes": "Αποστολή χαρακτηριστικών", + "action-type-attributes-updated": "Τα χαρακτηριστικά ενημερώθηκαν", + "action-type-attributes-deleted": "Τα χαρακτηριστικά διαγράφηκαν", + "action-type-timeseries-updated": "Οι χρονοσειρές ενημερώθηκαν", + "action-type-credentials-updated": "Τα διαπιστευτήρια ενημερώθηκαν", + "action-type-assigned-to-customer": "Ανατέθηκε σε πελάτη", + "action-type-unassigned-from-customer": "Αποκαθηλώθηκε από πελάτη", + "action-type-relation-add-or-update": "Προσθήκη ή ενημέρωση σχέσης", + "action-type-relation-deleted": "Η σχέση διαγράφηκε", + "action-type-rpc-call": "Κλήση RPC", + "action-type-alarm-ack": "Αναγνώριση συναγερμού", + "action-type-alarm-clear": "Εκκαθάριση συναγερμού", + "action-type-alarm-assigned": "Ο συναγερμός ανατέθηκε", + "action-type-alarm-unassigned": "Ο συναγερμός αποκαθηλώθηκε", + "action-type-assigned-to-edge": "Ανατέθηκε σε Edge", + "action-type-unassigned-from-edge": "Αποκαθηλώθηκε από Edge", + "action-type-credentials-request": "Αίτηση διαπιστευτηρίων", + "action-type-entity-merge-request": "Αίτηση συγχώνευσης οντοτήτων" + }, "error": { - "unable-to-connect": "Αδύνατον να συνδεθεί στον διακομιστή! Παρακαλούμε ελέγξτε την σύνδεσή σας στο ίντερνετ.", - "unhandled-error-code": "Μη διορθωμένος κωδικός σφάλματος: {{errorCode}}", + "unable-to-connect": "Αδυναμία σύνδεσης με τον διακομιστή! Ελέγξτε τη σύνδεσή σας στο διαδίκτυο.", + "unhandled-error-code": "Μη διαχειριζόμενος κωδικός σφάλματος: {{errorCode}}", "unknown-error": "Άγνωστο σφάλμα" }, "entity": { "entity": "Οντότητα", "entities": "Οντότητες", - "aliases": "Ψευδώνυμα οντότητας", + "entities-count": "Αριθμός οντοτήτων", + "alarms-count": "Αριθμός συναγερμών", + "aliases": "Ψευδώνυμα οντοτήτων", + "aliases-short": "Ψευδώνυμα", "entity-alias": "Ψευδώνυμο οντότητας", - "unable-delete-entity-alias-title": "Αδύνατο να διαγραφεί το ψευδώνυμο οντότητας", - "unable-delete-entity-alias-text": "Το ψευδώνυμο οντότητας '{{entityAlias}}' δεν μπορεί να διαγραφεί όσο εξακολουθεί να χρησιμοποιείται από τα παρακάτω widget(s):
{{widgetsList}}", - "duplicate-alias-error": "Βρέθηκε διπλότυπο το ψευδώνυμο '{{alias}}'.
Τα ψευδώνυμα οντοτήτων πρέπει να είναι μοναδικά σε ένα dashboard.", - "missing-entity-filter-error": "Λείπει φίλτρο από το ψευδώνυμο '{{alias}}'.", - "configure-alias": "Ρύθμιση ψευδώνυμου '{{alias}}'", + "unable-delete-entity-alias-title": "Αδυναμία διαγραφής ψευδωνύμου οντότητας", + "unable-delete-entity-alias-text": "Το ψευδώνυμο οντότητας '{{entityAlias}}' δεν μπορεί να διαγραφεί καθώς χρησιμοποιείται από τα ακόλουθα γραφικά:
{{widgetsList}}", + "duplicate-alias-error": "Βρέθηκε διπλό ψευδώνυμο '{{alias}}'.
Τα ψευδώνυμα οντοτήτων πρέπει να είναι μοναδικά εντός του πίνακα ελέγχου.", + "missing-entity-filter-error": "Λείπει το φίλτρο για το ψευδώνυμο '{{alias}}'.", + "configure-alias": "Ρύθμιση του ψευδωνύμου '{{alias}}'", "alias": "Ψευδώνυμο", "alias-required": "Απαιτείται ψευδώνυμο οντότητας.", - "remove-alias": "Αφαίρεση ψευδώνυμου οντότητας", - "add-alias": "Προσθήκη ψευδώνυμου οντότητας", + "remove-alias": "Αφαίρεση ψευδωνύμου οντότητας", + "add-alias": "Προσθήκη ψευδωνύμου οντότητας", + "edit-alias": "Επεξεργασία ψευδωνύμου οντότητας", "entity-list": "Λίστα οντοτήτων", - "entity-type": "Τύπος οντοτήτων", + "entity-type": "Τύπος οντότητας", "entity-types": "Τύποι οντοτήτων", "entity-type-list": "Λίστα τύπων οντοτήτων", "any-entity": "Οποιαδήποτε οντότητα", + "add-entity-type": "Προσθήκη τύπου οντότητας", "enter-entity-type": "Εισαγωγή τύπου οντότητας", - "no-entities-matching": "Δεν βρέθηκαν οντότητες ο οποίες να σχετίζονται με '{{entity}}'.", - "no-entity-types-matching": "Δεν βρέθηκαν τύποι οντότητας ο οποίοι να σχετίζονται με '{{entityType}}'.", - "name-starts-with": "το όνομα αρχίζει από", - "use-entity-name-filter": "χρήση φίλτρου", - "entity-list-empty": "Δεν έχουν επιλεγεί οντότητες.", + "no-entities-matching": "Δεν βρέθηκαν οντότητες που να ταιριάζουν με '{{entity}}'.", + "no-entities-text": "Δεν βρέθηκαν οντότητες", + "no-entity-types-matching": "Δεν βρέθηκαν τύποι οντοτήτων που να ταιριάζουν με '{{entityType}}'.", + "name-starts-with": "Έκφραση ονόματος", + "help-text": "Χρησιμοποιήστε '%' ανάλογα με την ανάγκη: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter": "Χρήση φίλτρου", + "entity-list-empty": "Δεν επιλέχθηκαν οντότητες.", + "entity-type-list-required": "Πρέπει να επιλεγεί τουλάχιστον ένας τύπος οντότητας.", "entity-name-filter-required": "Απαιτείται φίλτρο ονόματος οντότητας.", - "entity-name-filter-no-entity-matched": "Δεν βρέθηκαν οντότητες οι οποίες αρχίζουν από '{{entity}}'.", - "all-subtypes": "Όλοι", + "entity-name-filter-no-entity-matched": "Δεν βρέθηκαν οντότητες που να ξεκινούν με '{{entity}}'.", + "all-subtypes": "Όλα", "select-entities": "Επιλογή οντοτήτων", "no-aliases-found": "Δεν βρέθηκαν ψευδώνυμα.", - "no-alias-matching": "Δεν βρέθηκε '{{alias}}'.", - "create-new-alias": "Δημιουργείστε ένα νέο!", + "no-alias-matching": "'{{alias}}' δεν βρέθηκε.", + "create-new-alias": "Δημιουργία νέου!", + "create-new": "Δημιουργία νέου", "key": "Κλειδί", - "key-name": "όνομα κλειδιού", - "no-keys-found": "δεν βρέθηκαν κλειδιά.", - "no-key-matching": "Δεν βρέθηκε '{{key}}'.", - "create-new-key": "Δημιουργείστε ένα νέο!", + "key-name": "Όνομα κλειδιού", + "no-keys-found": "Δεν βρέθηκαν κλειδιά.", + "no-key-matching": "'{{key}}' δεν βρέθηκε.", + "create-new-key": "Δημιουργία νέου!", "type": "Τύπος", "type-required": "Απαιτείται τύπος οντότητας.", "type-device": "Συσκευή", "type-devices": "Συσκευές", - "list-of-devices": "{ count, plural, =1 {One device} other {List of # devices} }", - "device-name-starts-with": "Συσκευές των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-asset": "Asset", - "type-assets": "Assets", - "list-of-assets": "{ count, plural, =1 {One asset} other {List of # assets} }", - "asset-name-starts-with": "Assets των οποίων το όνομα αρχίζει από '{{prefix}}'", + "list-of-devices": "{ count, plural, =1 {Μία συσκευή} other {Λίστα με # συσκευές} }", + "device-name-starts-with": "Συσκευές με ονόματα που ξεκινούν με '{{prefix}}'", + "type-device-profile": "Προφίλ συσκευής", + "type-device-profiles": "Προφίλ συσκευών", + "clear-selected-profiles": "Καθαρισμός επιλεγμένων προφίλ", + "list-of-device-profiles": "{ count, plural, =1 {Ένα προφίλ συσκευής} other {Λίστα με # προφίλ συσκευών} }", + "device-profile-name-starts-with": "Προφίλ συσκευών με ονόματα που ξεκινούν με '{{prefix}}'", + "type-asset-profile": "Προφίλ πόρου", + "type-asset-profiles": "Προφίλ πόρων", + "list-of-asset-profiles": "{ count, plural, =1 {Ένα προφίλ πόρου} other {Λίστα με # προφίλ πόρων} }", + "asset-profile-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 {One entity view} other {List of # entity views} }", - "entity-view-name-starts-with": "Προβολές οντότητας των οποίων το όνομα αρχίζει από '{{prefix}}'", + "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 {One rule} other {List of # rules} }", - "rule-name-starts-with": "Κανόνες των οποίων το όνομα αρχίζει από '{{prefix}}'", + "list-of-rules": "{ count, plural, =1 {Ένας κανόνας} other {Λίστα με # κανόνες} }", + "rule-name-starts-with": "Κανόνες με ονόματα που ξεκινούν με '{{prefix}}'", "type-plugin": "Πρόσθετο", - "type-plugins": "Προσθετα", - "list-of-plugins": "{ count, plural, =1 {One plugin} other {List of # plugins} }", - "plugin-name-starts-with": "Πρόσθετα των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-tenant": "Μισθωτής", - "type-tenants": "Μισθωτές", - "list-of-tenants": "{ count, plural, =1 {One tenant} other {List of # tenants} }", - "tenant-name-starts-with": "Μισθωτές των οποίων το όνομα αρχίζει από '{{prefix}}'", + "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-tenant-profile": "Προφίλ ενοικιαστή", + "type-tenant-profiles": "Προφίλ ενοικιαστών", + "list-of-tenant-profiles": "{ count, plural, =1 {Ένα προφίλ ενοικιαστή} other {Λίστα με # προφίλ ενοικιαστών} }", + "tenant-profile-name-starts-with": "Προφίλ ενοικιαστών με ονόματα που ξεκινούν με '{{prefix}}'", "type-customer": "Πελάτης", "type-customers": "Πελάτες", - "list-of-customers": "{ count, plural, =1 {One customer} other {List of # customers} }", - "customer-name-starts-with": "Πελάτες των οποίων το όνομα αρχίζει από '{{prefix}}'", + "list-of-customers": "{ count, plural, =1 {Ένας πελάτης} other {Λίστα με # πελάτες} }", + "customer-name-starts-with": "Πελάτες με ονόματα που ξεκινούν με '{{prefix}}'", "type-user": "Χρήστης", "type-users": "Χρήστες", - "list-of-users": "{ count, plural, =1 {One user} other {List of # users} }", - "user-name-starts-with": "Χρήστες των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-dashboard": "Dashboard", - "type-dashboards": "Dashboards", - "list-of-dashboards": "{ count, plural, =1 {One dashboard} other {List of # dashboards} }", - "dashboard-name-starts-with": "Dashboards των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-alarm": "Alarm", - "type-alarms": "Alarms", - "list-of-alarms": "{ count, plural, =1 {One alarms} other {List of # alarms} }", - "alarm-name-starts-with": "Alarms των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-rulechain": "Αλυσίδα Κανόνων", - "type-rulechains": "Αλυσίδες Κανόνων", - "list-of-rulechains": "{ count, plural, =1 {One rule chain} other {List of # rule chains} }", - "rulechain-name-starts-with": "Αλυσίδες Κανόνων των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-scheduler-event": "Προγραμματιστής", - "type-scheduler-events": "Προγραμματιστής", - "list-of-scheduler-events": "{ count, plural, =1 {One scheduler event} other {List of # scheduler events} }", - "scheduler-event-name-starts-with": "Προγραμματιστές γεγονότων των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-blob-entity": "Ογκώδη οντότητα", - "type-blob-entities": "Ογκώδεις οντότητες", - "list-of-blob-entities": "{ count, plural, =1 {One blob entity} other {List of # blob entities} }", - "blob-entity-name-starts-with": "Ογκώδεις οντότητες των οποίων το όνομα αρχίζει από '{{prefix}}'", + "list-of-users": "{ count, plural, =1 {Ένας χρήστης} other {Λίστα με # χρήστες} }", + "user-name-starts-with": "Χρήστες με ονόματα που ξεκινούν με '{{prefix}}'", + "type-dashboard": "Πίνακας ελέγχου", + "type-dashboards": "Πίνακες ελέγχου", + "list-of-dashboards": "{ count, plural, =1 {Ένας πίνακας ελέγχου} other {Λίστα με # πίνακες ελέγχου} }", + "dashboard-name-starts-with": "Πίνακες ελέγχου με ονόματα που ξεκινούν με '{{prefix}}'", + "type-alarm": "Συναγερμός", + "type-alarms": "Συναγερμοί", + "list-of-alarms": "{ count, plural, =1 {Ένας συναγερμός} other {Λίστα με # συναγερμούς} }", + "alarm-name-starts-with": "Συναγερμοί με ονόματα που ξεκινούν με '{{prefix}}'", + "type-rulechain": "Αλυσίδα κανόνων", + "type-rulechains": "Αλυσίδες κανόνων", + "list-of-rulechains": "{ count, plural, =1 {Μία αλυσίδα κανόνων} other {Λίστα με # αλυσίδες κανόνων} }", + "rulechain-name-starts-with": "Αλυσίδες κανόνων με ονόματα που ξεκινούν με '{{prefix}}'", "type-rulenode": "Κόμβος κανόνα", - "type-rulenodes": "Κόμβοι κανόνων", - "list-of-rulenodes": "{ count, plural, =1 {One rule node} other {List of # rule nodes} }", - "rulenode-name-starts-with": "Κόμβοι κανόνων των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-current-customer": "Τρέχον Πελάτης", - "search": "Αναζήτηση Οντότητες", - "selected-entities": "{ count, plural, =1 {1 entity} other {# entities} } επιλεγμένα", + "type-rulenodes": "Κόμβοι κανόνα", + "list-of-rulenodes": "{ count, plural, =1 {Ένας κόμβος κανόνα} other {Λίστα με # κόμβους κανόνα} }", + "rulenode-name-starts-with": "Κόμβοι κανόνα με ονόματα που ξεκινούν με '{{prefix}}'", + "type-current-customer": "Τρέχων πελάτης", + "type-current-tenant": "Τρέχων ενοικιαστής", + "type-current-user": "Τρέχων χρήστης", + "type-current-user-owner": "Ιδιοκτήτης τρέχοντος χρήστη", + "type-calculated-field": "Υπολογιζόμενο πεδίο", + "type-calculated-fields": "Υπολογιζόμενα πεδία", + "type-widgets-bundle": "Πακέτο γραφικών", + "type-widgets-bundles": "Πακέτα γραφικών", + "list-of-widgets-bundles": "{ count, plural, =1 {Ένα πακέτο γραφικών} other {Λίστα με # πακέτα γραφικών} }", + "type-widget": "Γραφικό", + "type-widgets": "Γραφικά", + "list-of-widgets": "{ count, plural, =1 {Ένα γραφικό} other {Λίστα με # γραφικά} }", + "search": "Αναζήτηση οντοτήτων", + "selected-entities": "{ count, plural, =1 {1 οντότητα} other {# οντότητες} } επιλεγμένες", "entity-name": "Όνομα οντότητας", + "entity-label": "Ετικέτα οντότητας", "details": "Λεπτομέρειες οντότητας", "no-entities-prompt": "Δεν βρέθηκαν οντότητες", - "no-data": "Δεν υπάρχουν δεδομένα προς προβολή", - "columns-to-display": "στήλες που προβάλονται", - "type-entity-group": "Ομάδα Οντότητας", - "type-converter": "Μετατροπέας Δεδομένων", - "type-converters": "Μετατροπείς Δεδομένων", - "list-of-converters": "{ count, plural, =1 {One data converter} other {List of # data converters} }", - "converter-name-starts-with": "Μετατροπείς δεδομένων των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-integration": "Ενσωμάτωση", - "type-integrations": "Ενσωματώσεις", - "list-of-integrations": "{ count, plural, =1 {One integration} other {List of # integrations} }", - "integration-name-starts-with": "Ενσωματώσεις των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-role": "Ρόλος", - "type-roles": "Ρόλοι", - "list-of-roles": "{ count, plural, =1 {One role} other {List of # roles} }", - "role-name-starts-with": "Ρόλοι των οποίων το όνομα αρχίζει από '{{prefix}}'", - "type-group-permission": "Άδεια Ομάδας" - }, - "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": "κρυφό σε mobile", - "use-cell-style-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 entity group} other {# entity groups} }?", - "delete-entity-groups-action-title": "Διαγραφή { count, plural, =1 {1 entity group} other {# entity groups} }", - "delete-entity-groups-text": "Προσοχή, αφού ολοκληρωθεί η επιβεβαίωση, όλες οι επιλεγμένες ομάδες οντοτήτων θα αφαιρεθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "device-groups": "Ομάδες Συσκευών", - "asset-groups": "Ομάδες Asset", - "customer-groups": "Ομάδες Πελατών", - "device-group": "Ομάδα Πελατών", - "asset-group": "Ομάδα Asset", - "user-group": "Ομάδα Χρηστών", - "user-groups": "Ομάδες Χρηστών", - "customer-group": "Ομάδα Χρηστών", - "entity-view-groups": "Ομάδες Όψεων", - "entity-view-group": "Προβολή ομαδών οντοτήτων", - "dashboard-groups": "Ομάδες Dashboard", - "dashboard-group": "Ομάδα Dashboard", - "fetch-more": "Περισσότερα", - "column-type": { - "column-type": "Τύπος στήλης", - "client-attribute": "Χαρακτηριστικά Client", - "shared-attribute": "Κοινόχρηστα Χαρακτηριστικά", - "server-attribute": "Χαρακτηριστικό Server", - "timeseries": "Χρονική σειρά", - "entity-field": "Πεδίο οντότητας" - }, - "column-type-required": "Απαιτείται τύπος στήλης.", - "entity-field": { - "created-time": "Δημιουργήθηκε", - "name": "Όνομα", - "type": "Τύπος", - "assigned_customer": "Ανατεθειμένος πελάτης", - "authority": "Εξουσιοδότηση", - "first_name": "Όνομα", - "last_name": "Επίθετο", - "email": "Email", - "title": "Τίτλος", - "country": "Χώρα", - "state": "Νομός", - "city": "Πόλη", - "address": "Διεύθυνση", - "address2": "Διεύθυνση 2", - "zip": "Τ.Κ.", - "phone": "Τηλέφωνο" - }, - "sort-order": { - "asc": "Αύξουσα", - "desc": "Φθίνουσα", - "none": "Καμία" - }, - "details-mode": { - "on-row-click": "στο κλικ στη γραμμη", - "on-action-button-click": "στο κλικ στο κουμπί λεπτομέρειες", - "disabled": "μη διαθέσιμο" - }, - "change-owner": "Αλλαγή ιδιοκτήτη", - "select-target-owner": "Επιλογή ιδιοκτήτη", - "no-owners-matching": "Δεν βρέθηκε ιδιοκτήτης που να αντιστοιχεί σε '{{owner}}'.", - "target-owner-required": "Απαιτείται επιλεγμένος ιδιοκτήτης.", - "confirm-change-owner-title": "Είστε σίγουροι ότι θέλετε να αλλάξετε ιδιοκτήτη για { count, plural, =1 {1 selected entity} other {# selected entities} }?", - "confirm-change-owner-text": "Προσοχή, μετά την επιβεβαίωση, όλες οι επιλεγμένες οντότητες θα αφαιρεθούν από τον τρέχοντα ιδιοκτήτη και θα τοποθετηθούν στην ομάδα 'Όλα' του επιλεγμένου ιδιοκτήτη.", - "add-to-group": "Προσθήκη σε ομάδα", - "move-to-group": "Μετακίνηση σε ομάδα", - "select-entity-group": "Επιλογή ομάδας οντοτήτων", - "no-entity-groups-matching": "Δεν βρέθηκαν ομάδες οντοτήτων που να αντιστοιχούν με '{{entityGroup}}'.", - "target-entity-group-required": "Απαιτείται επιλεγμένη ομάδα οντοτήτων.", - "select-user-group": "Επιλογή ομάδας χρηστών", - "no-user-groups-matching": "Δεν βρέθηκαν ομάδες χρηστών οι οποίες να αντιστοιχούν σε '{{entityGroup}}'.", - "target-user-group-required": "Απαιτείται επιλεγμένη ομάδα χρηστών.", - "remove-from-group": "Αφαίρεση από ομάδα", - "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-login-as-user": "Ενεργοποίηση εισόδου ως χρήστη", - "enable-users-management": "Ενεργοποίηση διαχείρισης χρήστών", - "enable-customers-management": "Ενεργοποίηση διαχείρησης πελατών", - "enable-assets-management": "Ενεργοποίηση διαχείρησης assets", - "enable-devices-management": "Ενεργοποίηση διαχείρησης συσκευών", - "enable-entity-views-management": "Ενεργοποίηση διαχείρησης προβολής οντοτήτων", - "enable-dashboards-management": "Ενεργοποίηση διαχείρησης dashboard", - "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": "Απαιτείται φίλτρο ονόματος ομάδας οντοτήτων.", - "roles": "Ρόλοι", - "permissions": "Δικαιώματα", - "public": "Δημόσιο", - "entity-group-public": "Η ομάδα οντοτήτων είναι δημόσια", - "make-public": "Δημοσιοποίηση ομάδας οντοτήτων", - "make-private": "Ιδιωτικοποίηση ομάδας οντοτήτων", - "make-public-entity-group-title": "Είστε σίγουροι ότι θέλετε να κάνετε την ομάδα οντοτήτων '{{entityGroupName}}' δημόσια;", - "make-public-entity-group-text": "Μετά την επιβεβαίωση, η ομάδα οντοτήτων και όλες οι οντότητές της θα δημοσιοποιηθούν και θα είναι προσβάσιμες από τρίτους.", - "make-private-entity-group-title": "Είστε σίγουροι ότι θέλετε να κάνετε την ομάδα οντοτήτων '{{entityGroupName}}' ιδιωτική?", - "make-private-entity-group-text": "Μετά την επιβεβαίωση, η ομάδα οντοτήτων και όλες οι οντότητές της θα γίνουν ιδιωτικές και δεν θα είναι προσβάσιμες από τρίτους.", - "copyId": "Αντιγραφή ID ομάδας οντοτήτων", - "idCopiedMessage": "Το ID της ομάδας οντοτήτων έχει αντιγραφεί στο πρόχειρο" + "no-data": "Δεν υπάρχουν δεδομένα για εμφάνιση", + "columns-to-display": "Στήλες προς εμφάνιση", + "type-api-usage-state": "Κατάσταση Χρήσης API", + "type-edge": "Edge", + "type-edges": "Edges", + "list-of-edges": "{ count, plural, =1 {Ένα Edge} other {Λίστα με # Edges} }", + "edge-name-starts-with": "Edges με ονόματα που ξεκινούν με '{{prefix}}'", + "version-conflict": { + "message": "Θέλετε να αντικαταστήσετε την υπάρχουσα έκδοση ή να απορρίψετε τις αλλαγές και να φορτώσετε την τελευταία έκδοση;", + "link": "Μπορείτε να κατεβάσετε τη δική σας έκδοση του {{entityType}} χρησιμοποιώντας αυτό", + "overwrite": "Αντικατάσταση έκδοσης", + "discard": "Απόρριψη αλλαγών" + }, + "type-tb-resource": "Πόρος", + "type-tb-resources": "Πόροι", + "list-of-tb-resources": "{ count, plural, =1 {Ένας πόρος} other {Λίστα με # πόρους} }", + "type-ota-package": "Πακέτο OTA", + "type-rpc": "RPC", + "type-queue": "Ουρά", + "type-queue-stats": "Στατιστικά ουράς", + "type-queues-stats": "Στατιστικά ουρών", + "type-notification": "Ειδοποίηση", + "type-notification-rule": "Κανόνας ειδοποίησης", + "type-notification-rules": "Κανόνες ειδοποιήσεων", + "list-of-notification-rules": "{ count, plural, =1 {Ένας κανόνας ειδοποίησης} other {Λίστα με # κανόνες ειδοποίησης} }", + "type-notification-target": "Παραλήπτης ειδοποίησης", + "type-notification-targets": "Παραλήπτες ειδοποιήσεων", + "list-of-notification-targets": "{ count, plural, =1 {Ένας παραλήπτης ειδοποίησης} other {Λίστα με # παραλήπτες ειδοποίησης} }", + "type-notification-request": "Αίτημα ειδοποίησης", + "type-notification-template": "Πρότυπο ειδοποίησης", + "type-notification-templates": "Πρότυπα ειδοποιήσεων", + "list-of-notification-templates": "{ count, plural, =1 {Ένα πρότυπο ειδοποίησης} other {Λίστα με # πρότυπα ειδοποιήσεων} }", + "link": "σύνδεσμος", + "type-oauth2-client": "Πελάτης OAuth 2.0", + "type-oauth2-clients": "Πελάτες OAuth 2.0", + "list-of-oauth2-clients": "{ count, plural, =1 {Ένας πελάτης OAuth 2.0} other {Λίστα με # πελάτες OAuth 2.0} }", + "type-domain": "Τομέας", + "type-domains": "Τομείς", + "list-of-domains": "{ count, plural, =1 {Ένας τομέας} other {Λίστα με # τομείς} }", + "type-mobile-app": "Εφαρμογή κινητού", + "type-mobile-apps": "Εφαρμογές κινητού", + "list-of-mobile-apps": "{ count, plural, =1 {Μία εφαρμογή κινητού} other {Λίστα με # εφαρμογές κινητού} }", + "type-mobile-app-bundle": "Πακέτο εφαρμογής κινητού", + "type-mobile-app-bundles": "Πακέτα εφαρμογής κινητού", + "list-of-mobile-app-bundles": "{ count, plural, =1 {Ένα πακέτο κινητού} other {Λίστα με # πακέτα κινητού} }" }, "entity-field": { - "created-time": "Δημιουργήθηκε", + "created-time": "Χρόνος δημιουργίας", "name": "Όνομα", "type": "Τύπος", "first-name": "Όνομα", - "last-name": "Επίθετο", + "last-name": "Επώνυμο", "email": "Email", "title": "Τίτλος", "country": "Χώρα", - "state": "Νομός", + "state": "Πολιτεία", "city": "Πόλη", "address": "Διεύθυνση", "address2": "Διεύθυνση 2", - "zip": "Τ.Κ.", - "phone": "Τηλέφωνο" + "zip": "Ταχ. Κώδικας", + "phone": "Τηλέφωνο", + "label": "Ετικέτα", + "queue-name": "Όνομα ουράς", + "service-id": "Service Id", + "owner-name": "Όνομα ιδιοκτήτη", + "owner-type": "Τύπος ιδιοκτήτη" }, "entity-view": { - "entity-view": "Όψη Οντότητας", + "entity-view": "Προβολή οντότητας", "entity-view-required": "Απαιτείται προβολή οντότητας.", - "entity-views": "Όψεις Οντοτήτων", - "management": "Διαχείριση Όψεων Οντοτήτων", - "view-entity-views": "Προβολή οντοτήτων", + "entity-views": "Προβολές οντοτήτων", + "management": "Διαχείριση προβολών οντοτήτων", + "view-entity-views": "Προβολή προβολών οντοτήτων", "entity-view-alias": "Ψευδώνυμο προβολής οντότητας", - "aliases": "Ψευδώνυμα προβολής οντότητας", - "no-alias-matching": "'Δεν βρέθηκαν {{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}}'.
Τα ψευδώνυμα προβολής οντότητας πρέπει να είναι μοναδικά εντός του dashboard.", - "configure-alias": "Ρύθμιση ψευδώνυμου '{{alias}}'", - "no-entity-views-matching": "Δεν βρέθηκαν προβολές οντοτήτων οι οποίες να αντιστοιχούν σε '{{entity}}'.", + "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}}'.", + "public": "Δημόσιο", "alias": "Ψευδώνυμο", - "alias-required": "Απαιτείται ψευδώνυμο προβολής οντοτήτων.", - "remove-alias": "Αφαίρεση ψευδωνύμου προβολής οντοτήτων", - "add-alias": "Προβολή ψευδωνύμου προβολής οντοτήτων", - "name-starts-with": "Το όνομα προβολής οντότητας αρχίζει από", - "entity-view-list": "Λίστα προβολής οντότητας", + "alias-required": "Απαιτείται ψευδώνυμο προβολής οντότητας.", + "remove-alias": "Αφαίρεση ψευδωνύμου προβολής οντότητας", + "add-alias": "Προσθήκη ψευδωνύμου προβολής οντότητας", + "name-starts-with": "Έκφραση ονόματος προβολής οντότητας", + "help-text": "Χρησιμοποιήστε '%' όπως απαιτείται: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list": "Λίστα προβολών οντοτήτων", "use-entity-view-name-filter": "Χρήση φίλτρου", - "entity-view-list-empty": "Δεν έχουν επιλεχθεί προβολές οντότητας.", + "entity-view-list-empty": "Δεν έχουν επιλεγεί προβολές οντοτήτων.", "entity-view-name-filter-required": "Απαιτείται φίλτρο ονόματος προβολής οντότητας.", - "entity-view-name-filter-no-entity-view-matched": "Δεν βρέθηκαν προβολές οντότητας οι οποίες να αρχίζουν από '{{entityView}}'.", - "add": "Προσθήκη Προβολής Οντότητας", + "entity-view-name-filter-no-entity-view-matched": "Δεν βρέθηκαν προβολές οντοτήτων που να ξεκινούν με '{{entityView}}'.", + "add": "Προσθήκη προβολής οντότητας", + "entity-view-public": "Η προβολή οντότητας είναι δημόσια", "assign-to-customer": "Ανάθεση σε πελάτη", - "assign-entity-view-to-customer": "Ανάθεση προβολής/ών οντότητας σε πελάτη", - "assign-entity-view-to-customer-text": "Παρακαλώ επιλέξτε τις προβολές οντότητας για να αναθέσετε σε πελάτη", - "no-entity-views-text": "Δεν βρέθηκαν προβολές οντότητας", - "assign-to-customer-text": "Παρακαλώ επιλέξτε πελάτη για να ανάθεση προβολών οντότητας", + "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 entityView} other {# entityViews} } σε πελάτη", - "delete-entity-views": "Διαγραφή προβολών οντότητας", - "make-public": "Δημοσιοποίηση προβολής οντότητας", - "make-private": "Ιδιωτικοποίηση προβολής οντότητας", - "unassign-from-customer": "Αφαίρεση από πελάτη", - "unassign-entity-views": "Αφαίρεση προβολών οντότητας", - "unassign-entity-views-action-title": "Αφαίρεση { count, plural, =1 {1 entityView} other {# entityViews} } από πελάτη", + "assign-entity-views": "Ανάθεση προβολών οντοτήτων", + "assign-entity-views-text": "Ανάθεση { count, plural, =1 {1 προβολή οντότητας} other {# προβολές οντοτήτων} } σε πελάτη", + "delete-entity-views": "Διαγραφή προβολών οντοτήτων", + "make-public": "Κάντε την προβολή δημόσια", + "make-private": "Κάντε την προβολή ιδιωτική", + "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 entityView} other {# entityViews} };", - "delete-entity-views-action-title": "Διαγραφή { count, plural, =1 {1 entityView} other {# entityViews} }", - "delete-entity-views-text": "Προσοχή, μετά την επιβεβαίωση, όλες οι επιλεγμένες προβολές της οντότητας και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "make-public-entity-view-title": "Είστε σίγουροι ότι θέλετε να κάνετε την προβολή οντότητας '{{entityViewName}}' δημόσια?", - "make-public-entity-view-text": "Μετά την επιβεβαίωση, η προβολή της οντότητας και όλα τα δεδομένα της θα δημοσιοποιηθούν και θα είναι προσβάσιμα από τρίτους.", - "make-private-entity-view-title": "Είστε σίγουροι ότι θέλετε να κάνετε την προβολή οντότητας '{{entityViewName}}' ιδιωτική;", - "make-private-entity-view-text": "Μετά την επιβεβαίωση, η προβολή της οντότητας και όλα τα δεδομένα της θα γίνουν ιδιωτικά και θα δεν είναι προσβάσιμα από τρίτους.", - "unassign-entity-view-title": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε την προβολη οντότητας '{{entityViewName}}';", - "unassign-entity-view-text": "Μετά την επιβεβαίωση, η προβολή της οντότητας θα καταργηθεί και δεν θα είναι προσβάσιμη από τον πελάτη.", - "unassign-entity-view": "Αφαίρεση προβολής οντότητας", - "unassign-entity-views-title": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε { count, plural, =1 {1 entityView} other {# entityViews} };", - "unassign-entity-views-text": "Μετά την επιβεβαίωση, όλες οι επιλεγμένες προβολές οντοτήτων θα αφαιρεθούν και δεν θα είναι προσβάσιμες από τον πελάτη.", - "entity-view-type": "Τύπος Προβολής Οντότητας", + "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": "Προσοχή, μετά την επιβεβαίωση, όλες οι επιλεγμένες προβολές οντοτήτων θα αφαιρεθούν και όλα τα σχετικά δεδομένα δεν θα είναι ανακτήσιμα.", + "make-public-entity-view-title": "Είστε βέβαιοι ότι θέλετε να κάνετε την προβολή οντότητας '{{entityViewName}}' δημόσια;", + "make-public-entity-view-text": "Μετά την επιβεβαίωση, η προβολή οντότητας και όλα τα δεδομένα της θα γίνουν δημόσια και προσβάσιμα από άλλους.", + "make-private-entity-view-title": "Είστε βέβαιοι ότι θέλετε να κάνετε την προβολή οντότητας '{{entityViewName}}' ιδιωτική;", + "make-private-entity-view-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": "Εισαγωγή τύπου προβολής οντοτήτων", + "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": "Τύποι Προβολής Οντότητας", + "no-entity-view-types-matching": "Δεν βρέθηκαν τύποι προβολής οντότητας που να αντιστοιχούν στο '{{entitySubtype}}'.", + "entity-view-type-list-empty": "Δεν έχουν επιλεγεί τύποι προβολής οντοτήτων.", + "entity-view-types": "Τύποι προβολής οντοτήτων", + "created-time": "Χρόνος δημιουργίας", "name": "Όνομα", "name-required": "Απαιτείται όνομα.", + "name-max-length": "Το όνομα πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "type-max-length": "Ο τύπος προβολής οντότητας πρέπει να είναι μικρότερος από 256 χαρακτήρες", "description": "Περιγραφή", - "events": "Γεγονότα", + "events": "Συμβάντα", "details": "Λεπτομέρειες", "copyId": "Αντιγραφή ID προβολής οντότητας", - "idCopiedMessage": "Το ID της προβολής οντότητας έχει αντιγραφεί στο πρόχειρο", - "assignedToCustomer": "Αναθέση σε πελάτη", - "unable-entity-view-device-alias-title": "Αδύνατον να διαγραφεί το ψευδώνυμο προβολής οντότητας", - "unable-entity-view-device-alias-text": "Το ψευδώνυμο συσκευής '{{entityViewAlias}}' δεν μπορεί να διαγραφεί όσο εξακωλουθεί να χρησιμοποιείτε από τα παρακάτω widget(s):
{{widgetsList}}", - "select-entity-view": "Επιλογή προβολής οντότητας", - "start-date": "Ημερομηνία έναρξης", - "start-ts": "Ώρα έναρξης", - "end-date": "Ημερομηνία λήξης", - "end-ts": "Ώρα λήξης", - "date-limits": "Όρια ημερομηνίας", - "client-attributes": "Χαρακτηριστικά Client", - "shared-attributes": "Κοινόχρηστα Χαρακτηριστικά", - "server-attributes": "Χαρακτηριστικά Server", - "timeseries": "Χρονική σειρά", - "client-attributes-placeholder": "Χαρακτηριστικά Client", - "shared-attributes-placeholder": "Κοινόχρηστα Χαρακτηριστικά", - "server-attributes-placeholder": "Χαρακτηριστικά Server", - "timeseries-placeholder": "Χρονική σειρά", - "target-entity": "Στοχευμένη οντότητα", + "idCopiedMessage": "Το ID της προβολής οντότητας αντιγράφηκε στο πρόχειρο", + "assignedToCustomer": "Ανατέθηκε σε πελάτη", + "unable-entity-view-device-alias-title": "Δεν είναι δυνατή η διαγραφή του ψευδωνύμου προβολής οντότητας", + "unable-entity-view-device-alias-text": "Το ψευδώνυμο '{{entityViewAlias}}' δεν μπορεί να διαγραφεί επειδή χρησιμοποιείται από το(α) εξής widget(s):
{{widgetsList}}", + "select-entity-view": "Επιλέξτε προβολή οντότητας", + "start-ts": "Χρόνος έναρξης", + "end-ts": "Χρόνος λήξης", + "date-limits": "Όρια ημερομηνιών", + "client-attributes": "Χαρακτηριστικά πελάτη", + "shared-attributes": "Κοινόχρηστα χαρακτηριστικά", + "server-attributes": "Χαρακτηριστικά διακομιστή", + "timeseries": "Χρονοσειρές", + "client-attributes-placeholder": "Χαρακτηριστικά πελάτη", + "shared-attributes-placeholder": "Κοινόχρηστα χαρακτηριστικά", + "server-attributes-placeholder": "Χαρακτηριστικά διακομιστή", + "timeseries-placeholder": "Χρονοσειρές", + "target-entity": "Οντότητα στόχος", "attributes-propagation": "Διάδοση χαρακτηριστικών", - "attributes-propagation-hint": "Η προβολή οντοτήτων θα αντιγράφει αυτόματα καθορισμένα χαρακτηριστικά από την στοχευμένη οντότητα κάθε φορά που αποθηκεύετε ή ενημερώνετε αυτήν την προβολή οντότητας. Για λόγους απόδοσης, τα χαρακτηριστικά της στοχευμένης οντότητας δεν μεταδίδονται στην προβολή οντότητας με κάθε αλλαγή χαρακτηριστικών. Μπορείτε να ενεργοποιήσετε την αυτόματη διάδοση ρυθμίζοντας τον κόμβο \"αντιγραφή για προβολή \" στην αλυσίδα κανόνων σας και συνδέοντας τα μηνύματα \"Χαρακτηριστικά Post \" και \"Ενημερωμένα Χαρακτηριστικά \" στον νέο κόμβο.", - "timeseries-data": "Δεδομένα χρονικής σειράς", - "timeseries-data-hint": "Ρυθμίστε τα δεδομένα της χρονικής σειράς της στοχευμένης οντότητας που θα είναι διαθέσιμα στην προβολή οντοτητας. Αυτά τα δεδομένα χρονικής σειράς είναι μόνο για ανάγνωση.", - "selected-entity-views": "{ count, plural, =1 {1 entity view} other {# entity views} } επιλεγμένα", - "search": "Αναζήτηση προβολών οντότητας", - "select-group-to-add": "Επιλογή ομάδας για προσθήκη των επιλεγμένων προβολών οντότητας", - "select-group-to-move": "Επιλογή ομάδας για μετακίνηση των επιλεγμένων προβολών οντότητας", - "remove-entity-views-from-group": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε { count, plural, =1 {1 entity view} other {# entity views} } from group '{entityGroup}'?", - "group": "Ομάδα προβολών οντότητας", - "list-of-groups": "{ count, plural, =1 {One entity view group} other {List of # entity view groups} }", - "group-name-starts-with": "Ομάδες προβολής οντότητας των οποίων το όνομα αρχίζει από '{{prefix}}'" + "attributes-propagation-hint": "Η Προβολή Οντότητας θα αντιγράφει αυτόματα τα καθορισμένα χαρακτηριστικά από την Οντότητα Στόχο κάθε φορά που αποθηκεύετε ή ενημερώνετε αυτή την προβολή. Για λόγους απόδοσης, τα χαρακτηριστικά της οντότητας στόχου δεν μεταφέρονται στην προβολή οντότητας με κάθε αλλαγή χαρακτηριστικού. Μπορείτε να ενεργοποιήσετε την αυτόματη διάδοση ρυθμίζοντας το node κανόνα \"copy to view\" και συνδέοντας τα μηνύματα \"Post attributes\" και \"Attributes Updated\" με το νέο node.", + "timeseries-data": "Δεδομένα χρονοσειρών", + "timeseries-data-hint": "Διαμορφώστε τα κλειδιά δεδομένων χρονοσειρών της οντότητας στόχου που θα είναι προσβάσιμα από την προβολή οντότητας. Αυτά τα δεδομένα χρονοσειρών είναι μόνο για ανάγνωση.", + "search": "Αναζήτηση προβολών οντοτήτων", + "selected-entity-views": "{ count, plural, =1 {1 προβολή οντότητας} other {# προβολές οντοτήτων} } επιλέχθηκαν", + "assign-entity-view-to-edge": "Ανάθεση προβολών οντοτήτων σε Edge", + "assign-entity-view-to-edge-text": "Επιλέξτε τις προβολές οντοτήτων για ανάθεση στο edge", + "unassign-entity-view-from-edge-title": "Είστε βέβαιοι ότι θέλετε να απο-αναθέσετε την προβολή οντότητας '{{entityViewName}}';", + "unassign-entity-view-from-edge-text": "Μετά την επιβεβαίωση, η προβολή οντότητας θα απο-ανατεθεί και δεν θα είναι προσβάσιμη από το edge.", + "unassign-entity-views-from-edge-action-title": "Απο-ανάθεση { count, plural, =1 {1 προβολής οντότητας} other {# προβολών οντοτήτων} } από το edge", + "unassign-entity-view-from-edge": "Απο-ανάθεση προβολής οντότητας", + "unassign-entity-views-from-edge-title": "Είστε βέβαιοι ότι θέλετε να απο-αναθέσετε { count, plural, =1 {1 προβολή οντότητας} other {# προβολές οντοτήτων} };", + "unassign-entity-views-from-edge-text": "Μετά την επιβεβαίωση, όλες οι επιλεγμένες προβολές οντοτήτων θα απο-ανατεθούν και δεν θα είναι προσβάσιμες από το edge." }, "event": { - "events": "Γεγονότα", - "event-type": "Τύπος Γεγονότος", + "event-type": "Τύπος συμβάντος", + "events-filter": "Φίλτρο συμβάντων", + "clean-events": "Καθαρισμός συμβάντων", "type-error": "Σφάλμα", - "type-lc-event": "Γεγονός κύκλου ζωής", - "type-stats": "Στατιστική", - "type-debug-converter": "Αποσφαλμάτωση", - "type-debug-integration": "Αποσφαλμάτωση", + "type-lc-event": "Συμβάν κύκλου ζωής", + "type-stats": "Στατιστικά", "type-debug-rule-node": "Αποσφαλμάτωση", "type-debug-rule-chain": "Αποσφαλμάτωση", - "no-events-prompt": "Δεν βρέθηκαν γεγονότα", + "type-debug-calculated-field": "Αποσφαλμάτωση", + "arguments": "Ορίσματα", + "result": "Αποτέλεσμα", + "no-events-prompt": "Δεν βρέθηκαν συμβάντα", "error": "Σφάλμα", - "alarm": "Alarm", - "event-time": "Ώρα Γεγονότος", + "alarm": "Συναγερμός", + "event-time": "Χρόνος συμβάντος", "server": "Διακομιστής", - "body": "Σώμα (body)", + "body": "Σώμα", "method": "Μέθοδος", "type": "Τύπος", - "in": "Είσοδος", - "out": "Έξοδος", "metadata": "Μεταδεδομένα", "message": "Μήνυμα", - "message-id": "ID Μηνύματος", - "message-type": "Τύπος Μηνύματος", - "data-type": "Τύπος Δεδομένων", - "relation-type": "Τύπος Σχέσης", - "data": "Δεδομέναα", - "event": "Γεγονός", + "message-id": "Αναγνωριστικό μηνύματος", + "copy-message-id": "Αντιγραφή αναγνωριστικού μηνύματος", + "message-type": "Τύπος μηνύματος", + "data-type": "Τύπος δεδομένων", + "relation-type": "Τύπος σχέσης", + "data": "Δεδομένα", + "event": "Συμβάν", "status": "Κατάσταση", "success": "Επιτυχία", - "failed": "Απέτυχε", - "messages-processed": "Επεξεργασμένα μηνύματα", + "failed": "Αποτυχία", + "messages-processed": "Μηνύματα που επεξεργάστηκαν", + "max-messages-processed": "Μέγιστος αριθμός επεξεργασμένων μηνυμάτων", + "min-messages-processed": "Ελάχιστος αριθμός επεξεργασμένων μηνυμάτων", "errors-occurred": "Παρουσιάστηκαν σφάλματα", - "all-events": "Όλοι", - "entity-type": "Τύπος οντοτήτων" + "max-errors-occurred": "Μέγιστος αριθμός σφαλμάτων", + "min-errors-occurred": "Ελάχιστος αριθμός σφαλμάτων", + "min-value": "Η ελάχιστη τιμή είναι 0.", + "all-events": "Όλα", + "has-error": "Έχει σφάλμα", + "entity-id": "Αναγνωριστικό οντότητας", + "copy-entity-id": "Αντιγραφή αναγνωριστικού οντότητας", + "entity-type": "Τύπος οντότητας", + "clear-filter": "Καθαρισμός φίλτρου", + "clear-request-title": "Καθαρισμός όλων των συμβάντων", + "clear-request-text": "Είστε βέβαιοι ότι θέλετε να καθαρίσετε όλα τα συμβάντα;", + "started": "Ξεκίνησε", + "updated": "Ενημερώθηκε", + "stopped": "Σταμάτησε" }, "extension": { "extensions": "Επεκτάσεις", - "selected-extensions": "{ count, plural, =1 {1 extension} other {# extensions} } επιλέχθηκαν", + "selected-extensions": "{ count, plural, =1 {1 επέκταση} other {# επεκτάσεις} } επιλεγμένες", "type": "Τύπος", "key": "Κλειδί", "value": "Τιμή", - "id": "ID", - "extension-id": "ID επέκτασης", + "id": "Αναγνωριστικό", + "extension-id": "Αναγνωριστικό επέκτασης", "extension-type": "Τύπος επέκτασης", "transformer-json": "JSON *", - "unique-id-required": "Το τρέχον ID επέκτασης υπάρχει ήδη.", + "unique-id-required": "Το τρέχον αναγνωριστικό επέκτασης υπάρχει ήδη.", "delete": "Διαγραφή επέκτασης", "add": "Προσθήκη επέκτασης", "edit": "Επεξεργασία επέκτασης", - "view": "Προβολή επέκτασης", - "delete-extension-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε την επέκταση '{{extensionId}}';", - "delete-extension-text": "Προσοχή, μετά την επιβεβαίωση, η επέκταση και όλα τα σχετικά δεδομένα θα διαγραφούν μόνιμα.", - "delete-extensions-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 extension} other {# extensions} }?", - "delete-extensions-text": "Προσέξτε, μετά την επιβεβαίωση θα αφαιρεθούν όλες οι επιλεγμένες επεκτάσεις.", + "delete-extension-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την επέκταση '{{extensionId}}';", + "delete-extension-text": "Προσοχή, μετά την επιβεβαίωση η επέκταση και όλα τα σχετικά δεδομένα θα χαθούν οριστικά.", + "delete-extensions-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 επέκταση} other {# επεκτάσεις} };", + "delete-extensions-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες επεκτάσεις θα αφαιρεθούν.", "converters": "Μετατροπείς", - "converter-id": "ID Μετατροπέα", + "converter-id": "Αναγνωριστικό μετατροπέα", "configuration": "Διαμόρφωση", - "converter-configurations": "Ρυθμίσεις μετατροπέα", - "token": "Ετικέτα ασφαλείας", + "converter-configurations": "Διαμορφώσεις μετατροπέα", + "token": "Διακριτικό ασφαλείας", "add-converter": "Προσθήκη μετατροπέα", - "add-config": "Προσθήκη ρυθμήσεων μετατροπέα", + "add-config": "Προσθήκη διαμόρφωσης μετατροπέα", "device-name-expression": "Έκφραση ονόματος συσκευής", "device-type-expression": "Έκφραση τύπου συσκευής", "custom": "Προσαρμοσμένο", - "to-double": "Διπλασιασμός", - "transformer": "Μετατροπέας", - "json-required": "Απαιτείται μετατροπέας JSON.", - "json-parse": "Αδύνατον να γίνει αναλύση του μετατροπέα JSON.", + "to-double": "Σε Διπλό", + "transformer": "Μετασχηματιστής", + "json-required": "Απαιτείται transformer json.", + "json-parse": "Αδύνατη η ανάλυση του transformer json.", "attributes": "Χαρακτηριστικά", "add-attribute": "Προσθήκη χαρακτηριστικού", - "add-map": "Προσθήκη στοιχείου χαρτογράφισης", - "timeseries": "Χρονική σειρά", - "add-timeseries": "Προσθήκη χρονικής σειράς", - "field-required": "Απαιτείται το πεδίο", - "brokers": "Brokers", - "add-broker": "Προσθήκη broker", - "host": "Host", - "port": "Port", - "port-range": "Η Port πρέπει να είναι από 1 ως 65535.", + "add-map": "Προσθήκη στοιχείου αντιστοίχισης", + "timeseries": "Χρονοσειρές", + "add-timeseries": "Προσθήκη χρονοσειράς", + "field-required": "Το πεδίο είναι υποχρεωτικό", + "brokers": "Μεσάζοντες", + "add-broker": "Προσθήκη μεσάζοντα", + "host": "Κεντρικός υπολογιστής", + "port": "Θύρα", + "port-range": "Η θύρα πρέπει να είναι μεταξύ 1 και 65535.", "ssl": "Ssl", "credentials": "Διαπιστευτήρια", - "username": "Όνομα Χρήστη", - "password": "Κωδικός", - "retry-interval": "Διάστημα επανάληψης σε χιλιοστά του δευτερολέπτου", - "anonymous": "Ανώνυμα", - "basic": "Βασικά", + "username": "Όνομα χρήστη", + "password": "Κωδικός πρόσβασης", + "retry-interval": "Διάστημα επανάληψης σε milliseconds", + "anonymous": "Ανώνυμος", + "basic": "Βασικός", "pem": "PEM", "ca-cert": "Αρχείο πιστοποιητικού CA *", "private-key": "Αρχείο ιδιωτικού κλειδιού *", "cert": "Αρχείο πιστοποιητικού *", - "no-file": "Δεν έχει επιλεχθεί αρχείο.", - "drop-file": "Αποθέστε ένα αρχείο ή κάνετε κλικ για να επιλέξετε ένα αρχείο για ανέβασμα.", - "mapping": "χαρτογράφιση", + "no-file": "Δεν έχει επιλεγεί αρχείο.", + "drop-file": "Σύρετε ένα αρχείο ή κάντε κλικ για επιλογή αρχείου για μεταφόρτωση.", + "mapping": "Αντιστοίχιση", "topic-filter": "Φίλτρο θέματος", "converter-type": "Τύπος μετατροπέα", - "converter-json": "JSON", - "json-name-expression": "Έκφραση json ονόματος συσκευής", - "topic-name-expression": "Έκφραση θέματος ονόματος συσκευής", - "json-type-expression": "Έκφραση json τύπου συσκευής", - "topic-type-expression": "Έκφραση θέματος τύπου συσκευής", - "attribute-key-expression": "Έκφραση χαρακτηριστικού κλειδιού", - "attr-json-key-expression": "Έκφραση JSON χαρακτηριστικού κλειδιού", - "attr-topic-key-expression": "Έκφραση θέματος χαρακτηριστικού κλειδιού", - "request-id-expression": "Αίτημα έκφρασης ID", - "request-id-json-expression": "Αίτημα ID έκφρασης JSON", - "request-id-topic-expression": "Αίτημα ID έκφρασης θέματος", - "response-topic-expression": "Απάντηση έκφρασης θέματος", + "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": "Έκφραση αναγνωριστικού αιτήματος", + "request-id-json-expression": "Έκφραση json αναγνωριστικού αιτήματος", + "request-id-topic-expression": "Έκφραση θέματος αναγνωριστικού αιτήματος", + "response-topic-expression": "Έκφραση θέματος απόκρισης", "value-expression": "Έκφραση τιμής", "topic": "Θέμα", - "timeout": "Λήξη σε χιλιοστά του δευτερολέπτου", - "converter-json-required": "Απαιτείται μετατροπέας JSON.", - "converter-json-parse": "Αδύνατον να αναλυθεί ο μετατροπέας JSON.", + "timeout": "Χρονικό όριο σε milliseconds", + "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": "Server side RPC", - "add-server-side-rpc-request": "Προσθήκη αιτήματος server-side RPC", + "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": "ίλτρο χαρακτηριστικού", + "attribute-filter": "Φίλτρο χαρακτηριστικών", "method-filter": "Φίλτρο μεθόδου", - "request-topic-expression": "Αίτημα έκφρασης θέματος", - "response-timeout": "Λήξη απάντησης σε χιλιοστά του δευτερολέπτου", - "topic-expression": "Έκφρασου θέματος", - "client-scope": "Πεδίο εφαρμογής πελάτη", + "request-topic-expression": "Έκφραση θέματος αιτήματος", + "response-timeout": "Χρονικό όριο απόκρισης σε milliseconds", + "topic-expression": "Έκφραση θέματος", + "client-scope": "Πεδίο πελάτη", "add-device": "Προσθήκη συσκευής", - "opc-server": "Servers", - "opc-add-server": "Προσθήκη server", - "opc-add-server-prompt": "Παρακαλούμε προσθέστε server", + "opc-server": "Διακομιστές", + "opc-add-server": "Προσθήκη διακομιστή", + "opc-add-server-prompt": "Παρακαλώ προσθέστε διακομιστή", "opc-application-name": "Όνομα εφαρμογής", - "opc-application-uri": "URI Εφαρμογής", + "opc-application-uri": "URI εφαρμογής", "opc-scan-period-in-seconds": "Περίοδος σάρωσης σε δευτερόλεπτα", "opc-security": "Ασφάλεια", "opc-identity": "Ταυτότητα", - "opc-keystore": "Keystore", + "opc-keystore": "Αποθήκη κλειδιών", "opc-type": "Τύπος", "opc-keystore-type": "Τύπος", - "opc-keystore-location": "τοποθεσία *", - "opc-keystore-password": "Κωδικός", + "opc-keystore-location": "Τοποθεσία *", + "opc-keystore-password": "Κωδικός πρόσβασης", "opc-keystore-alias": "Ψευδώνυμο", - "opc-keystore-key-password": "Κωδικός Κλειδί", + "opc-keystore-key-password": "Κωδικός κλειδιού", "opc-device-node-pattern": "Μοτίβο κόμβου συσκευής", "opc-device-name-pattern": "Μοτίβο ονόματος συσκευής", - "modbus-server": "Servers/slaves", - "modbus-add-server": "Προσθήκη server/slave", - "modbus-add-server-prompt": "Παρακαλούμε προσθέστε server/slave", + "modbus-server": "Διακομιστές/σκλάβοι", + "modbus-add-server": "Προσθήκη διακομιστή/σκλάβου", + "modbus-add-server-prompt": "Παρακαλώ προσθέστε διακομιστή/σκλάβο", "modbus-transport": "Μεταφορά", "modbus-tcp-reconnect": "Αυτόματη επανασύνδεση", - "modbus-rtu-over-tcp": "RTU over TCP", - "modbus-port-name": "Όνομα serial port", - "modbus-encoding": "Encoding", - "modbus-parity": "Parity", - "modbus-baudrate": "Baud rate", - "modbus-databits": "Data bits", - "modbus-stopbits": "Stop bits", - "modbus-databits-range": "Τα Data bits πρέπει να κυμαίνονται από 7 εώς 8.", - "modbus-stopbits-range": "Τα Stop bits πρέπει να κυμαίνονται από 1 εώς 2.", - "modbus-unit-id": "ID Μονάδας", - "modbus-unit-id-range": "Το ID Μονάδας πρέπει να κυμαίνεται από 1 εώς 247.", + "modbus-rtu-over-tcp": "RTU μέσω TCP", + "modbus-port-name": "Όνομα σειριακής θύρας", + "modbus-encoding": "Κωδικοποίηση", + "modbus-parity": "Ισοτιμία", + "modbus-baudrate": "Ρυθμός μετάδοσης", + "modbus-databits": "Bits δεδομένων", + "modbus-stopbits": "Bits παύσης", + "modbus-databits-range": "Τα bits δεδομένων πρέπει να είναι από 7 έως 8.", + "modbus-stopbits-range": "Τα bits παύσης πρέπει να είναι από 1 έως 2.", + "modbus-unit-id": "Μονάδα ID", + "modbus-unit-id-range": "Το Unit ID πρέπει να είναι μεταξύ 1 και 247.", "modbus-device-name": "Όνομα συσκευής", - "modbus-poll-period": "Poll period (ms)", - "modbus-attributes-poll-period": "Χαρακτηριστικά poll period (ms)", - "modbus-timeseries-poll-period": "Χρονική σειρά poll period (ms)", - "modbus-poll-period-range": "Το Poll period θα πρέπει να έχει θετική τιμή.", + "modbus-poll-period": "Περίοδος ελέγχου (ms)", + "modbus-attributes-poll-period": "Περίοδος ελέγχου χαρακτηριστικών (ms)", + "modbus-timeseries-poll-period": "Περίοδος ελέγχου χρονοσειρών (ms)", + "modbus-poll-period-range": "Η περίοδος ελέγχου πρέπει να είναι θετική τιμή.", "modbus-tag": "Ετικέτα", "modbus-function": "Λειτουργία", - "modbus-register-address": "Καταχώριση διεύθυνσης", - "modbus-register-address-range": "η καταχωριμένη διεύθυνση πρεπει να κυμαίνεται από 0 ως 65535.", - "modbus-register-bit-index": "Bit index", - "modbus-register-bit-index-range": "Το Bit index πρέπει να κυμαίνεται από 0 ως 15.", - "modbus-register-count": "Μετρητής καταχώρησης", - "modbus-register-count-range": "Ο μετρητής καταχώρησης πρεέπει να έχει θετική τιμή.", - "modbus-byte-order": "Byte order", + "modbus-register-address": "Διεύθυνση καταχωρητή", + "modbus-register-address-range": "Η διεύθυνση καταχωρητή πρέπει να είναι από 0 έως 65535.", + "modbus-register-bit-index": "Δείκτης bit", + "modbus-register-bit-index-range": "Ο δείκτης bit πρέπει να είναι από 0 έως 15.", + "modbus-register-count": "Πλήθος καταχωρητών", + "modbus-register-count-range": "Το πλήθος καταχωρητών πρέπει να είναι θετική τιμή.", + "modbus-byte-order": "Σειρά byte", "sync": { "status": "Κατάσταση", - "sync": "Σε Συγχρονισμό", - "not-sync": "Δεν Συγχρονίζεται", - "last-sync-time": "Τελευταία φορά συγχρονισμού", + "sync": "Συγχρονισμός", + "not-sync": "Μη συγχρονισμένο", + "last-sync-time": "Τελευταίος συγχρονισμός", "not-available": "Μη διαθέσιμο" }, - "export-extensions-configuration": "Εξαγωγή διαμόρφωσης επεκτάσεων", - "import-extensions-configuration": "Εισαγωγή διαμόρφωσης επεκτάσεων", + "export-extensions-configuration": "Εξαγωγή ρυθμίσεων επεκτάσεων", + "import-extensions-configuration": "Εισαγωγή ρυθμίσεων επεκτάσεων", "import-extensions": "Εισαγωγή επεκτάσεων", "import-extension": "Εισαγωγή επέκτασης", "export-extension": "Εξαγωγή επέκτασης", - "file": "Επεκτάσεις αρχείου", - "invalid-file-error": "Μη έγκυρη επέκταση αρχείου" + "file": "Αρχείο επεκτάσεων", + "invalid-file-error": "Μη έγκυρο αρχείο επέκτασης" + }, + "feature": { + "advanced-features": "Προηγμένες δυνατότητες" + }, + "filter": { + "add": "Προσθήκη φίλτρου", + "edit": "Επεξεργασία φίλτρου", + "name": "Όνομα φίλτρου", + "name-required": "Το όνομα φίλτρου είναι υποχρεωτικό.", + "duplicate-filter": "Υπάρχει ήδη φίλτρο με το ίδιο όνομα.", + "filters": "Φίλτρα", + "unable-delete-filter-title": "Δεν είναι δυνατή η διαγραφή φίλτρου", + "unable-delete-filter-text": "Το φίλτρο '{{filter}}' δεν μπορεί να διαγραφεί επειδή χρησιμοποιείται από τα παρακάτω widget(s):
{{widgetsList}}", + "duplicate-filter-error": "Βρέθηκε διπλό φίλτρο '{{filter}}'.
Τα φίλτρα πρέπει να είναι μοναδικά μέσα στον πίνακα ελέγχου.", + "missing-key-filters-error": "Λείπει φίλτρο κλειδιού για το φίλτρο '{{filter}}'.", + "filter": "Φίλτρο", + "editable": "Επεξεργάσιμο", + "no-filters-found": "Δεν βρέθηκαν φίλτρα.", + "no-filter-text": "Δεν έχει καθοριστεί φίλτρο", + "add-filter-prompt": "Παρακαλώ προσθέστε φίλτρο", + "no-filter-matching": "'{{filter}}' δεν βρέθηκε.", + "create-new-filter": "Δημιουργία νέου φίλτρου!", + "create-new": "Δημιουργία νέου", + "filter-required": "Το φίλτρο είναι υποχρεωτικό.", + "operation": { + "operation": "Λειτουργία", + "equal": "ίσο με", + "not-equal": "όχι ίσο με", + "starts-with": "ξεκινά με", + "ends-with": "τελειώνει με", + "contains": "περιέχει", + "not-contains": "δεν περιέχει", + "greater": "μεγαλύτερο από", + "less": "μικρότερο από", + "greater-or-equal": "μεγαλύτερο ή ίσο", + "less-or-equal": "μικρότερο ή ίσο", + "and": "και", + "or": "ή", + "in": "είναι μέσα σε", + "not-in": "δεν είναι μέσα σε" + }, + "ignore-case": "αγνόηση πεζών/κεφαλαίων", + "value": "Τιμή", + "remove-filter": "Αφαίρεση φίλτρου", + "duplicate-filter-action": "Αντιγραφή φίλτρου", + "preview": "Προεπισκόπηση φίλτρου", + "no-filters": "Δεν έχουν ρυθμιστεί φίλτρα", + "add-filter": "Προσθήκη φίλτρου", + "add-complex-filter": "Προσθήκη σύνθετου φίλτρου", + "add-complex": "Προσθήκη σύνθετου", + "complex-filter": "Σύνθετο φίλτρο", + "edit-complex-filter": "Επεξεργασία σύνθετου φίλτρου", + "edit-filter-user-params": "Επεξεργασία παραμέτρων χρήστη φίλτρου", + "filter-user-params": "Παράμετροι χρήστη φίλτρου", + "user-parameters": "Παράμετροι χρήστη", + "display-label": "Ετικέτα εμφάνισης", + "order-priority": "Προτεραιότητα πεδίου", + "key-filter": "Φίλτρο κλειδιού", + "key-filters": "Φίλτρα κλειδιών", + "key-name": "Όνομα κλειδιού", + "key-name-required": "Το όνομα του κλειδιού είναι υποχρεωτικό.", + "key-type": { + "key-type": "Τύπος κλειδιού", + "attribute": "Χαρακτηριστικό", + "timeseries": "Χρονοσειρά", + "entity-field": "Πεδίο οντότητας", + "constant": "Σταθερά", + "client-attribute": "Χαρακτηριστικό πελάτη", + "server-attribute": "Χαρακτηριστικό διακομιστή", + "shared-attribute": "Κοινό χαρακτηριστικό" + }, + "value-type": { + "value-type": "Τύπος τιμής", + "string": "Κείμενο", + "numeric": "Αριθμητικό", + "boolean": "Λογικό", + "date-time": "Ημερομηνία και ώρα" + }, + "value-type-required": "Ο τύπος τιμής είναι υποχρεωτικός.", + "key-value-type-change-title": "Είστε σίγουροι ότι θέλετε να αλλάξετε τον τύπο τιμής του κλειδιού;", + "key-value-type-change-message": "Αν επιβεβαιώσετε, όλα τα φίλτρα κλειδιών που έχουν εισαχθεί θα διαγραφούν.", + "no-key-filters": "Δεν έχουν ρυθμιστεί φίλτρα κλειδιών", + "add-key-filter": "Προσθήκη φίλτρου κλειδιού", + "remove-key-filter": "Αφαίρεση φίλτρου κλειδιού", + "edit-key-filter": "Επεξεργασία φίλτρου κλειδιού", + "date": "Ημερομηνία", + "time": "Ώρα", + "current-tenant": "Τρέχων ενοικιαστής", + "current-customer": "Τρέχων πελάτης", + "current-user": "Τρέχων χρήστης", + "current-device": "Τρέχουσα συσκευή", + "default-value": "Προεπιλεγμένη τιμή", + "default-comma-separated-values": "Προεπιλεγμένες τιμές χωρισμένες με κόμμα", + "dynamic-source-type": "Τύπος δυναμικής πηγής", + "dynamic-value": "Δυναμική τιμή", + "no-dynamic-value": "Δεν υπάρχει δυναμική τιμή", + "source-attribute": "Χαρακτηριστικό πηγής", + "switch-to-dynamic-value": "Μετάβαση σε δυναμική τιμή", + "switch-to-default-value": "Μετάβαση σε προεπιλεγμένη τιμή", + "inherit-owner": "Κληρονόμηση από ιδιοκτήτη", + "source-attribute-not-set": "Εάν δεν έχει οριστεί χαρακτηριστικό πηγής" }, "fullscreen": { - "expand": "Ανάπτυξη σε πλήρη οθόνη", + "expand": "Επέκταση σε πλήρη οθόνη", "exit": "Έξοδος από πλήρη οθόνη", - "toggle": "Εναλλαγή σε πλήρη οθόνη", + "toggle": "Εναλλαγή λειτουργίας πλήρους οθόνης", "fullscreen": "Πλήρης οθόνη" }, "function": { - "function": "Λειτουργία" + "function": "Συνάρτηση" + }, + "gateway": { + "gateway-name": "Όνομα πύλης", + "gateway-name-required": "Απαιτείται όνομα πύλης.", + "gateways": "Πύλες", + "create-new-gateway": "Δημιουργία νέας πύλης", + "create-new-gateway-text": "Είστε σίγουροι ότι θέλετε να δημιουργήσετε μια νέα πύλη με όνομα: '{{gatewayName}}';", + "launch-command": "Εντολή εκκίνησης", + "no-gateway-found": "Δεν βρέθηκε πύλη.", + "no-gateway-matching": "'{{item}}' δεν βρέθηκε." }, "grid": { - "delete-item-title": "Είστε σίγουροι ότι θέλετε να διαγραφεί αυτό το αντικείμενο?", - "delete-item-text": "Προσοχή, μετά την επιβεβαίωση, αυτό το στοιχείο και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-items-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 item} other {# items} }?", - "delete-items-action-title": "Διαγραφή { count, plural, =1 {1 item} other {# items} }", - "delete-items-text": "Προσοχή, μετά την επιβεβαίωση, όλα τα επιλεγμένα στοιχεία θα καταργηθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "add-item-text": "Προσθήκη νέου αντικειμένου", - "no-items-text": "Δεν βρέθηκαν αντικείμενα", - "item-details": "λεπτομέρειες αντικείμενου", - "delete-item": "Διαγραφή Αντικειμένου", - "delete-items": "Διαγραφή Αντικειμένων", - "scroll-to-top": "Κύλιση προς τα πάνω" + "delete-item-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το στοιχείο;", + "delete-item-text": "Προσοχή, μετά την επιβεβαίωση αυτό το στοιχείο και όλα τα σχετικά δεδομένα θα καταστούν μη ανακτήσιμα.", + "delete-items-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 στοιχείο} other {# στοιχεία} };", + "delete-items-action-title": "Διαγραφή { count, plural, =1 {1 στοιχείου} other {# στοιχείων} }", + "delete-items-text": "Προσοχή, μετά την επιβεβαίωση όλα τα επιλεγμένα στοιχεία θα διαγραφούν και όλα τα σχετικά δεδομένα θα καταστούν μη ανακτήσιμα.", + "add-item-text": "Προσθήκη νέου στοιχείου", + "no-items-text": "Δεν βρέθηκαν στοιχεία", + "item-details": "Λεπτομέρειες στοιχείου", + "delete-item": "Διαγραφή στοιχείου", + "delete-items": "Διαγραφή στοιχείων", + "scroll-to-top": "Μετακίνηση στην κορυφή" }, "help": { - "goto-help-page": "Πηγαίνετε στη σελίδα" + "goto-help-page": "Μετάβαση στη σελίδα βοήθειας", + "show-help": "Εμφάνιση βοήθειας" }, "home": { - "home": "Αρχικη", + "home": "Αρχική", "profile": "Προφίλ", - "logout": "Αποσυνδέση", + "logout": "Αποσύνδεση", "menu": "Μενού", - "avatar": "Avatar", + "avatar": "Άβαταρ", "open-user-menu": "Άνοιγμα μενού χρήστη" }, + "file-input": { + "browse-file": "Αναζήτηση αρχείου", + "browse-files": "Αναζήτηση αρχείων" + }, + "image": { + "gallery": "Συλλογή εικόνων", + "search": "Αναζήτηση εικόνας", + "selected-images": "{ count, plural, =1 {1 εικόνα} other {# εικόνες} } επιλεγμένες", + "created-time": "Ημερομηνία δημιουργίας", + "name": "Όνομα", + "name-required": "Απαιτείται όνομα.", + "resolution": "Ανάλυση", + "size": "Μέγεθος", + "system": "Σύστημα", + "download-image": "Λήψη εικόνας", + "export-image": "Εξαγωγή εικόνας σε JSON", + "import-image": "Εισαγωγή εικόνας από JSON", + "upload-image": "Ανέβασμα εικόνας", + "edit-image": "Επεξεργασία εικόνας", + "image-details": "Λεπτομέρειες εικόνας", + "no-images": "Δεν βρέθηκαν εικόνες", + "delete-image": "Διαγραφή εικόνας", + "delete-image-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε την εικόνα '{{imageTitle}}';", + "delete-image-text": "Προσοχή, μετά την επιβεβαίωση η εικόνα δεν θα μπορεί να ανακτηθεί.", + "delete-images-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 εικόνα} other {# εικόνες} };", + "delete-images-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες εικόνες θα διαγραφούν και όλα τα σχετικά δεδομένα θα χαθούν.", + "list-mode": "Προβολή λίστας", + "grid-mode": "Προβολή πλέγματος", + "image-preview": "Προεπισκόπηση εικόνας", + "update-image": "Ενημέρωση εικόνας", + "export-failed-error": "Αδυναμία εξαγωγής εικόνας: {{error}}", + "image-json-file": "Αρχείο εικόνας JSON", + "invalid-image-json-file-error": "Αδυναμία εισαγωγής εικόνας από JSON: Μη έγκυρη δομή δεδομένων εικόνας.", + "image-is-in-use": "Η εικόνα χρησιμοποιείται από άλλες οντότητες", + "images-are-in-use": "Οι εικόνες χρησιμοποιούνται από άλλες οντότητες", + "image-is-in-use-text": "Η εικόνα '{{title}}' δεν διαγράφηκε επειδή χρησιμοποιείται από τις εξής οντότητες:", + "images-are-in-use-text": "Δεν διαγράφηκαν όλες οι εικόνες επειδή χρησιμοποιούνται από άλλες οντότητες.
Μπορείτε να δείτε τις αναφερόμενες οντότητες κάνοντας κλικ στο κουμπί Αναφορές στη σχετική σειρά εικόνας.
Αν θέλετε να διαγράψετε τις εικόνες, επιλέξτε τις και κάντε κλικ στο κουμπί Διαγραφή επιλεγμένων.", + "delete-image-in-use-text": "Αν θέλετε να διαγράψετε την εικόνα, κάντε κλικ στο κουμπί Διαγραφή ούτως ή άλλως.", + "system-entities": "Οντότητες συστήματος:", + "entities": "οντότητες:", + "references": "Αναφορές", + "include-system-images": "Συμπερίληψη εικόνων συστήματος", + "clear-image": "Αφαίρεση εικόνας", + "no-image": "Καμία εικόνα", + "no-image-selected": "Δεν έχει επιλεγεί εικόνα", + "browse-from-gallery": "Αναζήτηση από συλλογή", + "set-link": "Ορισμός συνδέσμου", + "image-link": "Σύνδεσμος εικόνας", + "link": "Σύνδεσμος", + "copy-image-link": "Αντιγραφή συνδέσμου εικόνας", + "embed-image": "Ενσωμάτωση εικόνας", + "embed-to-html": "Ενσωμάτωση σε HTML", + "embed-to-html-hint": "Αυτή η δυνατότητα θα καταστήσει το σύνδεσμο προσβάσιμο σε μη εξουσιοδοτημένους χρήστες.", + "embed-to-html-text": "Χρησιμοποιώντας το παρακάτω απόσπασμα κώδικα, μπορείτε να ενσωματώσετε μια εικόνα σε στοιχεία που βασίζονται σε απλό HTML.
Αυτά τα στοιχεία περιλαμβάνουν widgets HTML, συναρτήσεις περιεχομένου κελιών, κλπ.", + "embed-to-angular-template": "Ενσωμάτωση σε Angular HTML πρότυπο", + "embed-to-angular-template-text": "Χρησιμοποιώντας το παρακάτω απόσπασμα κώδικα, μπορείτε να ενσωματώσετε μια εικόνα στο Angular HTML πρότυπο που χρησιμοποιείται από τα στοιχεία.
Αυτά περιλαμβάνουν το widget Markdown, το HTML section στον επεξεργαστή widget, προσαρμοσμένες ενέργειες, κλπ." + }, + "image-input": { + "drop-images-or": "Σύρετε και αποθέστε εικόνες ή", + "drag-and-drop": "Σύρετε & Αποθέστε", + "or": "ή", + "browse": "Αναζήτηση", + "no-images": "Δεν έχουν επιλεγεί εικόνες", + "images": "εικόνες" + }, "import": { - "no-file": "Δεν έχει επιλεχθεί αρχείο", - "drop-file": "Αποθέστε ένα αρχείο JSON ή κάνετε κλικ για να επιλέξετε ένα αρχείο για ανέβασμα.", - "drop-csv-file": "Αποθέστε ένα αρχείο CVS ή κάνετε κλικ για να επιλέξετε ένα αρχείο για ανέβασμα.", - "drop-file-csv": "Αποθέστε ένα αρχείο CSV ή κάνετε κλικ για να επιλέξετε ένα αρχείο για ανέβασμα.", + "no-file": "Δεν έχει επιλεγεί αρχείο", + "drop-file": "Αποθέστε ένα αρχείο JSON ή κάντε κλικ για να επιλέξετε ένα αρχείο για αποστολή.", + "drop-json-file-or": "Σύρετε και αποθέστε ένα αρχείο JSON ή", + "drop-file-csv": "Αποθέστε ένα αρχείο CSV ή κάντε κλικ για να επιλέξετε ένα αρχείο για αποστολή.", + "drop-file-csv-or": "Σύρετε και αποθέστε ένα αρχείο CSV ή", "column-value": "Τιμή", "column-title": "Τίτλος", - "column-example": "Παράδειγμα δεδομένων", - "column-key": "Κλειδί χαρακτηριστικού/τηλεμετρίας", - "csv-delimiter": "Οριοθέτηση CSV", - "csv-first-line-header": "Η πρώτη σειρά περιέχει ονόματα στηλών", - "csv-update-data": "Ανανέωση χαρακτηριστικών/τηλεμετρίας", - "import-csv-number-columns-error": "Ένα αρχείο θα πρέπει να περιέχει τουλάχιστον δυο στήλες", - "import-csv-invalid-format-error": "Μη έγκυρη μορφή αρχείου. Σειρά: '{{line}}'", + "column-example": "Παράδειγμα δεδομένων τιμής", + "column-key": "Κλειδί χαρακτηριστικού/χρονοσειράς", + "credentials": "Διαπιστευτήρια", + "csv-delimiter": "Διαχωριστικό CSV", + "csv-first-line-header": "Η πρώτη γραμμή περιέχει ονόματα στηλών", + "csv-update-data": "Ενημέρωση χαρακτηριστικών/χρονοσειρών", + "details": "Λεπτομέρειες", + "import-csv-number-columns-error": "Ένα αρχείο πρέπει να περιέχει τουλάχιστον δύο στήλες", + "import-csv-invalid-format-error": "Μη έγκυρη μορφή αρχείου. Γραμμή: '{{line}}'", "column-type": { "name": "Όνομα", "type": "Τύπος", + "label": "Ετικέτα", "column-type": "Τύπος στήλης", - "client-attribute": "Χαρακτηριστικό Client", - "shared-attribute": "Χαρακτηριστικό Shared", - "server-attribute": "Χαρακτηριστικό Server", - "timeseries": "Χρονική σειρά", - "entity-field": "Πεδίο Οντότητας", - "access-token": "Διακριτικό πρόσβασης" + "client-attribute": "Χαρακτηριστικό πελάτη", + "shared-attribute": "Κοινό χαρακτηριστικό", + "server-attribute": "Χαρακτηριστικό διακομιστή", + "timeseries": "Χρονοσειρά", + "entity-field": "Πεδίο οντότητας", + "access-token": "Διακριτικό πρόσβασης", + "x509": "X.509", + "mqtt": { + "client-id": "MQTT αναγνωριστικό πελάτη", + "user-name": "MQTT όνομα χρήστη", + "password": "MQTT κωδικός πρόσβασης" + }, + "lwm2m": { + "client-endpoint": "Όνομα πελάτη LwM2M", + "security-config-mode": "Λειτουργία διαμόρφωσης ασφάλειας LwM2M", + "client-identity": "Ταυτότητα πελάτη LwM2M", + "client-key": "Κλειδί πελάτη LwM2M", + "client-cert": "Δημόσιο κλειδί πελάτη LwM2M", + "bootstrap-server-security-mode": "Λειτουργία ασφάλειας διακομιστή εκκίνησης LwM2M", + "bootstrap-server-secret-key": "Μυστικό κλειδί διακομιστή εκκίνησης LwM2M", + "bootstrap-server-public-key-id": "Δημόσιο κλειδί ή ταυτότητα διακομιστή εκκίνησης LwM2M", + "lwm2m-server-security-mode": "Λειτουργία ασφάλειας διακομιστή LwM2M", + "lwm2m-server-secret-key": "Μυστικό κλειδί διακομιστή LwM2M", + "lwm2m-server-public-key-id": "Δημόσιο κλειδί ή ταυτότητα διακομιστή LwM2M" + }, + "snmp": { + "host": "SNMP host", + "port": "SNMP port", + "version": "SNMP έκδοση (v1, v2c ή v3)", + "community-string": "SNMP κοινόχρηστη συμβολοσειρά" + }, + "isgateway": "Είναι Πύλη", + "activity-time-from-gateway-device": "Χρόνος δραστηριότητας από συσκευή πύλης", + "description": "Περιγραφή", + "routing-key": "Κλειδί Edge", + "secret": "Μυστικό Edge" }, "stepper-text": { - "select-file": "Επιλογή αρχείου", - "configuration": "Εισαγωγή ρύθμισης", - "column-type": "Επιλογή τύπου στήλης", + "select-file": "Επιλέξτε ένα αρχείο", + "configuration": "Διαμόρφωση εισαγωγής", + "column-type": "Επιλέξτε τύπο στηλών", "creat-entities": "Δημιουργία νέων οντοτήτων" }, "message": { "create-entities": "{{count}} νέες οντότητες δημιουργήθηκαν με επιτυχία.", "update-entities": "{{count}} οντότητες ενημερώθηκαν με επιτυχία.", - "error-entities": "Υπήρξε κάποιο σφάλμα κατά τη δημιουργία {{count}} οντοτήτων." + "error-entities": "Παρουσιάστηκε σφάλμα κατά τη δημιουργία {{count}} οντοτήτων." } }, - "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 integration} other {# integrations} } επιλέχθηκαν", - "delete-integration-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε την ενσωμάτωση '{{integrationName}}';", - "delete-integration-text": "Προσοχή, μετά την επιβεβαίωση, η ενσωμάτωση και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-integrations-title": "Είστε σίγουροι ότι θέλετε { count, plural, =1 {1 integration} other {# integrations} };", - "delete-integrations-action-title": "Διαγραφή { count, plural, =1 {1 integration} other {# integrations} }", - "delete-integrations-text": "Προσοχή, αφού ολοκληρωθεί η επιβεβαίωση, όλες οι επιλεγμένες ενσωματώσεις θα αφαιρεθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "events": "Γεγονότα", - "add": "Προσθήκη Ενσωμάτωσης", - "integration-details": "Λεπτομέρειες Ενσωμάτωσης", - "details": "Λεπτομέρειες", - "copyId": "Αντιγραη ID ενσωμάτωσης", - "idCopiedMessage": "Το ID της ενσωμάτωσης έχει αντιγραφεί στο πρόχειρο.", - "debug-mode": "Λειτουργία απασφαλμάτωσης", - "enable-security": "Ενεργοποίηση ασφάλειας", - "headers-filter": "Φίλτρο Headers", - "header": "Header", - "no-headers-filter": "Όχι φίλτρο headers", - "downlink-url": "URL Λήψης", - "application-uri": "URI Εφαρμογής", - "as-id": "AS ID", - "as-id-required": "Απαιτείται AS ID.", - "as-key": "AS Key", - "as-key-required": "Απαιτείται AS Key.", - "max-time-diff-in-seconds": "Μέγιστη χρονική διαφορά (δευτερόλεπτα)", - "max-time-diff-in-seconds-required": "Απαιτείται μέγιστη χρονική διαφορά.", - "name": "Όνομα", - "name-required": "Απαιτείται όνομα.", + "scada": { + "symbols": "Σύμβολα SCADA", + "search": "Αναζήτηση συμβόλου", + "selected-symbols": "{ count, plural, =1 {1 σύμβολο} other {# σύμβολα} } επιλέχθηκαν", + "download-symbol": "Λήψη συμβόλου SCADA", + "export-symbol": "Εξαγωγή συμβόλου SCADA σε JSON", + "import-symbol": "Εισαγωγή συμβόλου SCADA από JSON", + "upload-symbol": "Μεταφόρτωση συμβόλου SCADA", + "update-symbol": "Ενημέρωση συμβόλου SCADA", + "edit-symbol": "Επεξεργασία συμβόλου SCADA", + "symbol-details": "Λεπτομέρειες συμβόλου SCADA", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "Δεν βρέθηκαν σύμβολα", + "show-hidden-elements": "Εμφάνιση κρυφών στοιχείων", + "hide-hidden-elements": "Απόκρυψη κρυφών στοιχείων", + "delete-symbol": "Διαγραφή συμβόλου SCADA", + "delete-symbol-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το σύμβολο SCADA '{{imageTitle}}';", + "delete-symbol-text": "Μετά την επιβεβαίωση το σύμβολο SCADA δεν θα ανακτήσιμο.", + "delete-symbols-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 σύμβολο SCADA} other {# σύμβολα SCADA} };", + "delete-symbols-text": "Μετά την επιβεβαίωση όλα τα επιλεγμένα σύμβολα SCADA θα διαγραφούν και δεν θα ανακτηθούν.", + "include-system-symbols": "Συμπερίληψη συμβόλων συστήματος", + "symbol-preview": "Προεπισκόπηση συμβόλου", + "general": "Γενικά", + "tags": "Ετικέτες", + "properties": "Ιδιότητες", + "title": "Τίτλος", "description": "Περιγραφή", - "base-url": "Base URL", - "base-url-required": "Απαιτείται Base URL", - "security-key": "Κλειδί ασφάλειας", - "http-endpoint": "HTTP endpoint URL", - "copy-http-endpoint-url": "Αντιγραφή HTTP endpoint URL", - "http-endpoint-url-copied-message": "Το HTTP endpoint URL έχει αντιγραφεί στο πρόχειρο", - "host": "Host", - "host-required": "Απαιτείται Host.", - "host-type": "Τύπος Host", - "host-type-required": "Απαιτείται τύπος Host.", - "custom-host": "Προσαρμοσμένο host", - "custom-host-required": "Απαιτείται προσαρμοσμένο host.", - "port": "Port", - "port-required": "Απαιτείται Port.", - "port-range": "Η Port πρέπει να κυμαίνεται από 1 ως 65535.", - "connect-timeout": "Λήξη σύνδεσης (δευτερόλεπτα)", - "connect-timeout-required": "Απαιτείται λήξη σύνδεσης.", - "connect-timeout-range": "Η λήξη σύνδεσης πρέπει να κυμαίνεται από 1 ως 200.", - "client-id": "Client ID", - "clean-session": "Καθαρή συνεδρία 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-required": "Απαιτείται μοτίβο θέματος σύνδεσης.", - "aws-iot-endpoint": "AWS IoT Endpoint", - "aws-iot-endpoint-required": "Απαιτείται AWS IoT Endpoint.", - "aws-iot-credentials": "AWS IoT Διαπιστευτήρια", - "application-credentials": "Διαπιστευτήρια Εφαρμογής", - "api-key": "API Key", - "api-key-required": "Απαιτείται API Key.", - "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.", - "event-hub-name": "Όνομα Event Hub", - "event-hub-name-required": "Απαιτείται όνομα Event Hub.", - "sas-key-name": "Όνομα SAS Key", - "sas-key-name-required": "Απαιτείται όνομα SAS Key.", - "sas-key": "SAS Key", - "sas-key-required": "Απαιτείται SAS Key.", - "iot-hub-name": "IoT Hub Name (απαιτείται για downlink)", - "metadata": "Μεταδεδομένα", - "type": "Τύπος", - "type-required": "Απαιτείται τύπος.", - "uplink-converter": "Μετατροπέας δεδομένων uplink", - "uplink-converter-required": "Απαιτείται μετατροπέας δεδομένων uplink.", - "downlink-converter": "Μετατροπέας δεδομένων downlink", - "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", - "opc-ua-application-name": "Όνομα εφαρμογής", - "opc-ua-application-uri": "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": "Keystore", - "add-opc-ua-keystore-prompt": "Παρακαλούμε προσθέστε αρχείο keystore", - "opc-ua-keystore-required": "Απαιτείται keystore", - "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-namespace": "Namespace", - "opc-ua-add-map": "Προσθήκη στοιχείου χαρτογράφισης", - "subscription-tags": "Ετικέτες εγγραφής", - "remove-subscription-tag": "Αφαίρεση ετικέτας εγγραφής", - "add-subscription-tag": "Προσθήκη ετικέτας εγγραφής", - "add-subscription-tag-prompt": "Παρακαλούμε προσθέστε ετικέτα εγγραφής", - "key": "Κλειδί", - "path": "Μονοπάτι", - "required": "Απαιτείται" + "search-tags": "Αναζήτηση ετικετών", + "widget-size": "Μέγεθος widget", + "cols": "στήλες", + "rows": "γραμμές", + "state-render-function": "Λειτουργία απόδοσης κατάστασης", + "preview": "Προεπισκόπηση", + "preview-widget-action-text": "Η ενέργεια widget '{{type}}' εκτελέστηκε επιτυχώς!", + "no-symbol": "Δεν υπάρχει σύμβολο SCADA", + "no-symbol-selected": "Δεν έχει επιλεγεί σύμβολο SCADA", + "clear-symbol": "Εκκαθάριση συμβόλου SCADA", + "browse-symbol-from-gallery": "Αναζήτηση συμβόλου SCADA από τη συλλογή", + "zoom-in": "Μεγέθυνση", + "zoom-out": "Σμίκρυνση", + "create-widget": "Δημιουργία widget", + "create-widget-from-symbol": "Δημιουργία widget από σύμβολο SCADA", + "hidden": "κρυφό", + "tag": { + "tag": "Ετικέτα", + "on-click-action": "Ενέργεια κατά το κλικ", + "no-tags": "Δεν έχουν ρυθμιστεί ετικέτες", + "delete-tag-text": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την ετικέτα
{{tag}} από το στοιχείο {{elementType}};", + "update-tag": "Ενημέρωση ετικέτας", + "enter-tag": "Εισαγωγή ετικέτας", + "tag-settings": "Ρυθμίσεις ετικέτας", + "remove-tag": "Αφαίρεση ετικέτας", + "add-tag": "Προσθήκη ετικέτας" + }, + "behavior": { + "behavior": "Συμπεριφορά", + "id": "Id", + "name": "Όνομα", + "type": "Τύπος", + "no-behaviors": "Δεν έχουν ρυθμιστεί συμπεριφορές", + "add-behavior": "Προσθήκη συμπεριφοράς", + "type-action": "Ενέργεια", + "type-value": "Τιμή", + "type-widget-action": "Ενέργεια widget", + "behavior-settings": "Ρυθμίσεις συμπεριφοράς", + "remove-behavior": "Αφαίρεση συμπεριφοράς", + "hint": "Συμβουλή", + "group-title": "Τίτλος ομάδας", + "value-type": "Τύπος τιμής", + "default-value": "Προεπιλεγμένη τιμή", + "true-label": "Ετικέτα true", + "false-label": "Ετικέτα false", + "state-label": "Ετικέτα κατάστασης", + "default-payload": "Προεπιλεγμένο payload", + "not-unique-behavior-ids-error": "Τα Id συμπεριφορών πρέπει να είναι μοναδικά!", + "default-settings": "Προεπιλεγμένες ρυθμίσεις" + }, + "symbol": { + "symbol": "Σύμβολο SCADA", + "fluid-presence": "Παρουσία υγρού", + "fluid-presence-hint": "Δηλώνει εάν υπάρχει υγρό στον σωλήνα.", + "fluid-present": "Υγρό παρόν", + "present": "Παρόν", + "absent": "Απόν", + "flow-presence": "Παρουσία ροής", + "flow-presence-hint": "Δηλώνει εάν υπάρχει ροή υγρού στον σωλήνα.", + "flow-present": "Ροή παρούσα", + "flow-direction": "Κατεύθυνση ροής", + "flow-direction-hint": "Δηλώνει την κατεύθυνση ροής του υγρού.", + "forward": "Εμπρός", + "reverse": "Αντίστροφα", + "flow-animation-speed": "Ταχύτητα κινούμενης ροής", + "flow-animation-speed-hint": "Διπλή τιμή για την ταχύτητα κίνησης. 1 - κανονική, 0 - καμία, < 1 - πιο αργή, > 1 - πιο γρήγορη.", + "leak": "Διαρροή", + "leak-hint": "Δηλώνει εάν υπάρχει διαρροή στον σωλήνα.", + "leak-present": "Διαρροή παρούσα", + "fluid-color": "Χρώμα υγρού", + "pipe-color": "Χρώμα σωλήνα", + "horizontal-pipe": "Οριζόντιος σωλήνας", + "vertical-pipe": "Κατακόρυφος σωλήνας", + "horizontal-fluid-color": "Χρώμα υγρού (οριζόντιο)", + "vertical-fluid-color": "Χρώμα υγρού (κατακόρυφο)", + "left-pipe": "Αριστερός σωλήνας", + "right-pipe": "Δεξιός σωλήνας", + "top-pipe": "Άνω σωλήνας", + "bottom-pipe": "Κάτω σωλήνας", + "left-fluid-color": "Αριστερό χρώμα υγρού", + "right-fluid-color": "Δεξί χρώμα υγρού", + "top-fluid-color": "Άνω χρώμα υγρού", + "bottom-fluid-color": "Κάτω χρώμα υγρού", + "display": "Οθόνη", + "display-format": "Μορφή εμφάνισης", + "value": "Τιμή", + "decimals": "Δεκαδικά", + "units": "Μονάδες", + "flow-meter-value-hint": "Διπλή τιμή στην ένδειξη ροόμετρου", + "value-hint": "Διπλή τιμή που δηλώνει την τρέχουσα τιμή", + "running": "Σε λειτουργία", + "running-hint": "Δηλώνει εάν το εξάρτημα είναι σε λειτουργία.", + "warning-state": "Κατάσταση προειδοποίησης", + "warning": "Προειδοποίηση", + "warning-click": "Κλικ προειδοποίησης", + "warning-state-hint": "Δηλώνει εάν το εξάρτημα είναι σε κατάσταση προειδοποίησης.", + "critical-state": "Κρίσιμη κατάσταση", + "critical": "Κρίσιμο", + "critical-click": "Κλικ κρίσιμης κατάστασης", + "critical-state-hint": "Δηλώνει εάν το εξάρτημα είναι σε κρίσιμη κατάσταση.", + "critical-state-animation": "Κινούμενη εικόνα κρίσιμης κατάστασης", + "critical-state-animation-hint": "Ενεργοποίηση εφέ αναβοσβήματος σε κρίσιμη κατάσταση.", + "warning-critical-state-animation": "Κινούμενη εικόνα προειδοποίησης/κρίσιμης κατάστασης", + "warning-critical-state-animation-hint": "Ενεργοποίηση αναβοσβήματος όταν υπάρχει προειδοποίηση ή κρίσιμη κατάσταση.", + "animation": "Κινούμενη εικόνα", + "broken": "Κατεστραμμένο", + "broken-hint": "Δηλώνει εάν το εξάρτημα είναι κατεστραμμένο.", + "on-display-click": "Κατά το κλικ στην οθόνη", + "on-display-click-hint": "Ενέργεια κατά το κλικ του χρήστη στην οθόνη.", + "pipe": "Σωλήνας", + "default-border-color": "Προεπιλεγμένο χρώμα περιγράμματος", + "active-border-color": "Ενεργό χρώμα περιγράμματος", + "warning-border-color": "Χρώμα περιγράμματος προειδοποίησης", + "critical-border-color": "Χρώμα περιγράμματος κρίσης", + "background-color": "Χρώμα φόντου", + "rotation-animation-speed": "Ταχύτητα περιστροφικής κινούμενης εικόνας", + "rotation-animation-speed-hint": "Διπλή τιμή που δείχνει την ταχύτητα περιστροφής. 1 - κανονική, 0 - χωρίς κινούμενη εικόνα, < 1 - πιο αργή, > 1 - πιο γρήγορη.", + "on-click": "Κατά το κλικ", + "on-click-hint": "Ενέργεια που καλείται όταν ο χρήστης κάνει κλικ στο στοιχείο.", + "connectors-positions": "Θέσεις συνδέσμων", + "right-connector": "Δεξιός σύνδεσμος", + "right-top-connector": "Άνω δεξιός σύνδεσμος", + "right-bottom-connector": "Κάτω δεξιός σύνδεσμος", + "left-connector": "Αριστερός σύνδεσμος", + "left-top-connector": "Άνω αριστερός σύνδεσμος", + "left-bottom-connector": "Κάτω αριστερός σύνδεσμος", + "top-left-connector": "Άνω αριστερός σύνδεσμος", + "top-right-connector": "Άνω δεξιός σύνδεσμος", + "top-connector": "Άνω σύνδεσμος", + "bottom-connector": "Κάτω σύνδεσμος", + "running-color": "Χρώμα λειτουργίας", + "stopped-color": "Χρώμα διακοπής", + "stopped": "Διακοπή", + "warning-color": "Χρώμα προειδοποίησης", + "critical-color": "Χρώμα κρίσης", + "opened": "Ανοιχτό", + "opened-hint": "Υποδεικνύει αν το στοιχείο είναι σε κατάσταση ανοίγματος.", + "open": "Άνοιγμα", + "open-hint": "Ενέργεια που καλείται όταν ο χρήστης κάνει κλικ για άνοιγμα.", + "close": "Κλείσιμο", + "close-hint": "Ενέργεια που καλείται όταν ο χρήστης κάνει κλικ για κλείσιμο.", + "close-state-animation": "Κινούμενη εικόνα κατάστασης κλεισίματος", + "close-state-animation-hint": "Ενεργοποίηση αναβοσβήματος όταν το στοιχείο είναι κλειστό.", + "opened-color": "Χρώμα ανοίγματος", + "closed-color": "Χρώμα κλεισίματος", + "opened-rotation-angle": "Γωνία περιστροφής (ανοιχτό)", + "closed-rotation-angle": "Γωνία περιστροφής (κλειστό)", + "tank-capacity": "Χωρητικότητα δεξαμενής", + "tank-capacity-hint": "Διπλή τιμή που δείχνει τη συνολική χωρητικότητα.", + "current-volume": "Τρέχων όγκος", + "current-volume-hint": "Διπλή τιμή που δείχνει τον τρέχοντα κατειλημμένο όγκο.", + "tank-color": "Χρώμα δεξαμενής", + "value-box": "Πλαίσιο τιμής", + "value-text": "Κείμενο τιμής", + "scale": "Κλίμακα", + "transparent-mode": "Διαφανής λειτουργία", + "major-ticks": "Κύριες διαβαθμίσεις", + "intervals": "Διαστήματα", + "major-ticks-color": "Χρώμα κύριων διαβαθμίσεων", + "normal": "Κανονικό", + "minor-ticks": "Δευτερεύουσες διαβαθμίσεις", + "minor-ticks-color": "Χρώμα δευτερευουσών διαβαθμίσεων", + "temperature": "Θερμοκρασία", + "temperature-hint": "Διπλή τιμή που δείχνει την τρέχουσα θερμοκρασία.", + "update-temperature": "Ενημέρωση θερμοκρασίας", + "update-temperature-hint": "Ενέργεια κατά το κλικ για αλλαγή θερμοκρασίας.", + "run": "Εκκίνηση", + "run-hint": "Ενέργεια κατά το κλικ για εκκίνηση στοιχείου.", + "stop": "Διακοπή", + "stop-hint": "Ενέργεια κατά το κλικ για διακοπή στοιχείου.", + "temperature-step": "Βήμα αύξησης θερμοκρασίας", + "heat-pump-color": "Χρώμα αντλίας θερμότητας", + "power-button-background": "Φόντο κουμπιού ισχύος", + "value-box-background": "Φόντο πλαισίου τιμής", + "value-units": "Μονάδες τιμής", + "filtration-mode": "Λειτουργία φιλτραρίσματος", + "filtration-mode-hint": "Ακέραια τιμή που δηλώνει την τρέχουσα λειτουργία.", + "filtration-mode-update": "Κατάσταση ενημέρωσης λειτουργίας", + "filtration-mode-update-hint": "Ενέργεια κατά το κλικ για αλλαγή λειτουργίας.", + "filter-mode": "Φίλτρο", + "waste-mode": "Απόβλητα", + "backwash-mode": "Αντίστροφη πλύση", + "recirculate-mode": "Ανακύκλωση", + "rinse-mode": "Ξέβγαλμα", + "closed-mode": "Κλειστό", + "sand-filter-color": "Χρώμα φίλτρου άμμου", + "mode-box-background": "Φόντο πλαισίου λειτουργίας", + "border-color": "Χρώμα περιγράμματος", + "label-color": "Χρώμα ετικέτας", + "water-leak-hint": "Υποδεικνύει αν υπάρχει διαρροή.", + "default-color": "Προεπιλεγμένο χρώμα", + "leak-color": "Χρώμα διαρροής", + "full-value": "Πλήρης τιμή", + "full-value-hint": "Διπλή τιμή που δείχνει την πλήρη ποσότητα.", + "label": "Ετικέτα", + "icon": "Εικονίδιο", + "button-color": "Χρώμα κουμπιού", + "on-label": "Κείμενο για 'Ενεργό'", + "off-label": "Κείμενο για 'Ανενεργό'", + "arrow-presence": "Παρουσία βέλους", + "arrow-presence-hint": "Υποδεικνύει αν υπάρχει βέλος στον σύνδεσμο.", + "arrow-present": "Το βέλος είναι παρόν", + "arrow-direction": "Κατεύθυνση ροής", + "arrow-direction-hint": "Υποδεικνύει την κατεύθυνση της ροής.", + "flow-animation": "Παρουσία ροής", + "flow-animation-hint": "Υποδεικνύει αν το υγρό ρέει στον σύνδεσμο.", + "flow": "Ροή", + "flow-line": "Γραμμή", + "flow-line-style": "Στυλ γραμμής", + "flow-style-hint": "Ορίστε τιμές Dash και Gap ώστε το άθροισμά τους να διαιρείται ακριβώς με το 100 για άψογη συγχρονισμένη κινούμενη εικόνα.", + "flow-dash-cap": "Καπάκι παύλας", + "dash-cap-butt": "Επίπεδο", + "dash-cap-round": "Στρογγυλό", + "dash-cap-square": "Τετράγωνο", + "dash": "Παύλα", + "gap": "Κενό", + "main-line": "Κύρια γραμμή", + "line": "Γραμμή", + "line-color": "Χρώμα γραμμής", + "arrow-color": "Χρώμα βέλους", + "target-value": "Τιμή στόχου", + "target-value-hint": "Υποδεικνύει σημείο στόχο στην κλίμακα.", + "min-max-value": "Ελάχιστη και μέγιστη τιμή", + "min-value": "Ελάχιστη", + "max-value": "Μέγιστη", + "progress-bar": "Γραμμή προόδου", + "progress-arrow": "Βέλος προόδου", + "warning-scale-color": "Χρώμα κλίμακας προειδοποίησης", + "critical-scale-color": "Χρώμα κλίμακας κρίσης", + "scale-color": "Χρώμα κλίμακας", + "target": "Στόχος", + "high-warning-state": "Κατάσταση υψηλής προειδοποίησης", + "show-high-warning-scale": "Εμφάνιση κλίμακας υψηλής προειδοποίησης", + "high-warning-scale": "Κλίμακα υψηλής προειδοποίησης", + "high-warning-state-hint": "Διπλή τιμή που υποδεικνύει περιοχή υψηλής προειδοποίησης έως κρίσιμη ή μέγιστη τιμή.", + "low-warning-state": "Κατάσταση χαμηλής προειδοποίησης", + "show-low-warning-scale": "Εμφάνιση κλίμακας χαμηλής προειδοποίησης", + "low-warning-scale": "Κλίμακα χαμηλής προειδοποίησης", + "low-warning-state-hint": "Διπλή τιμή που υποδεικνύει περιοχή χαμηλής προειδοποίησης έως κρίσιμη ή ελάχιστη τιμή.", + "high-critical-state": "Κατάσταση υψηλής κρίσης", + "show-high-critical-scale": "Εμφάνιση κλίμακας υψηλής κρίσης", + "high-critical-scale": "Κλίμακα υψηλής κρίσης", + "high-critical-state-hint": "Διπλή τιμή που υποδεικνύει περιοχή υψηλής κρίσης έως μέγιστη τιμή.", + "low-critical-state": "Κατάσταση χαμηλής κρίσης", + "show-low-critical-scale": "Εμφάνιση κατάστασης χαμηλής κρίσης", + "low-critical-scale": "Κατάσταση χαμηλής κρίσης", + "low-critical-state-hint": "Διπλή τιμή που υποδεικνύει περιοχή χαμηλής κρίσης έως ελάχιστη τιμή.", + "filter-color": "Χρώμα φίλτρου", + "colors": "Χρώματα", + "indicator-colors": "Χρώματα ενδείξεων", + "enabled": "Ενεργοποιημένο", + "disabled": "Απενεργοποιημένο", + "on": "ΕΝΕΡΓΟ", + "off": "ΑΝΕΝΕΡΓΟ", + "on-off-state": "Κατάσταση ενεργοποίησης/απενεργοποίησης", + "on-off-state-hint": "Υποδεικνύει αν το στοιχείο είναι ενεργοποιημένο ή όχι.", + "on-update-state": "Κατάσταση ενημέρωσης σε ενεργό", + "on-update-state-hint": "Ενέργεια κατά το κλικ για ενημέρωση κατάστασης σε ενεργό.", + "off-update-state": "Κατάσταση ενημέρωσης σε ανενεργό", + "off-update-state-hint": "Ενέργεια κατά το κλικ για ενημέρωση κατάστασης σε ανενεργό.", + "voltage": "Τάση", + "input-voltage": "Τάση εισόδου", + "input-voltage-hint": "Διπλή τιμή που υποδεικνύει την τάση εισόδου.", + "output-voltage": "Τάση εξόδου", + "output-voltage-hint": "Διπλή τιμή που υποδεικνύει την τάση εξόδου.", + "first-phase-voltage": "Τάση πρώτης φάσης", + "second-phase-voltage": "Τάση δεύτερης φάσης", + "third-phase-voltage": "Τάση τρίτης φάσης", + "phase-voltage-hint": "Διπλή τιμή που υποδεικνύει την τάση της τρέχουσας φάσης", + "voltage-hint": "Διπλή τιμή που υποδεικνύει την τρέχουσα τάση", + "current-voltage-color": "Χρώμα τρέχουσας τάσης", + "phase-indicator-color": "Χρώμα ένδειξης φάσης", + "measured": "Μετρημένο", + "measured-hint": "Διπλή τιμή που υποδεικνύει την κατανάλωση ενέργειας σε κιλοβατώρες", + "day-rate": "Ημερήσιο τιμολόγιο", + "night-rate": "Νυχτερινό τιμολόγιο", + "off-peak-rate": "Εκτός αιχμής", + "peak-rate": "Ώρα αιχμής", + "export-rate": "Ρυθμός εξαγωγής", + "operating-mode": "Λειτουργία", + "bypass-mode": "Παράκαμψη", + "operating-mode-hint": "Ακέραια τιμή για την τρέχουσα λειτουργία (0 - OFF, 1 - ON, 2 - BYPASS)", + "connected": "Συνδεδεμένο", + "connected-hint": "Υποδεικνύει αν το στοιχείο είναι σε κατάσταση σύνδεσης.", + "disconnected": "Αποσυνδεδεμένο", + "indicator": "Ένδειξη", + "operation-mode": "Λειτουργία συσκευής", + "operation-mode-hint": "Υποδεικνύει αν ο μετατροπέας είναι σε λειτουργία Δικτύου ή Μετατροπέα.", + "operation-mode-indicators-color": "Χρώμα ενδείξεων λειτουργίας", + "mains-on-mode": "Λειτουργία Δικτύου", + "inverter-on-mode": "Λειτουργία Μετατροπέα", + "charging-mode": "Λειτουργία φόρτισης", + "charging-mode-hint": "Ακέραια τιμή που υποδεικνύει την τρέχουσα λειτουργία φόρτισης (1 - Γρήγορη, 2 - Απορρόφηση, 3 - Συντήρηση)", + "charging-mode-indicators-color": "Χρώμα ενδείξεων λειτουργίας φόρτισης", + "inverter-faults": "Σφάλματα", + "inverter-fault-indicators-color": "Χρώμα ενδείξεων σφάλματος", + "overload-fault": "Υπερφόρτωση", + "overload-fault-hint": "Υποδεικνύει αν ο μετατροπέας είναι σε κατάσταση υπερφόρτωσης.", + "low-battery-fault": "Χαμηλή μπαταρία", + "low-battery-fault-hint": "Υποδεικνύει αν η μπαταρία έχει υπερβολική εκφόρτιση.", + "temperature-fault": "Θερμοκρασία", + "temperature-fault-hint": "Υποδεικνύει υψηλή θερμοκρασία στον μετατροπέα.", + "triangle": "Τρίγωνο", + "socket": "Υποδοχή", + "left-button": "Αριστερό κουμπί", + "right-button": "Δεξί κουμπί", + "alarm-colors": "Χρώματα συναγερμού", + "hook-color": "Χρώμα άγκιστρου" + } }, "item": { "selected": "Επιλέχθηκε" }, "js-func": { - "no-return-error": "Η λειτουργία πρέπει να επιστρέφει τιμή!", - "return-type-mismatch": "Η λειτουργία πρέπει να επιστρέφει τιμή από '{{type}}' τύπο!", - "tidy": "Tidy" + "no-return-error": "Η συνάρτηση πρέπει να επιστρέφει τιμή!", + "return-type-mismatch": "Η συνάρτηση πρέπει να επιστρέφει τιμή τύπου '{{type}}'!", + "tidy": "Καθαρισμός", + "mini": "Μίνι", + "modules": "Modules", + "remove-module": "Αφαίρεση module", + "no-modules": "Δεν έχουν οριστεί modules", + "add-module": "Προσθήκη module", + "module-alias": "Ψευδώνυμο", + "invalid-module-alias-name": "Μη έγκυρο όνομα ψευδωνύμου", + "module-resource": "Πηγή JS module", + "not-unique-module-aliases-error": "Τα ψευδώνυμα των modules πρέπει να είναι μοναδικά!", + "show-module-info": "Εμφάνιση πληροφοριών module", + "show-module-source-code": "Εμφάνιση πηγαίου κώδικα module", + "module-members": "Μέλη module", + "module-no-members": "Το module δεν έχει εξαγόμενα μέλη", + "module-load-error": "Σφάλμα φόρτωσης module", + "source-code": "Πηγαίος κώδικας", + "source-code-load-error": "Σφάλμα φόρτωσης πηγαίου κώδικα", + "no-js-module-text": "Δεν βρέθηκαν JS modules", + "no-js-module-matching": "Δεν βρέθηκαν JS modules με όνομα '{{module}}'." }, "key-val": { - "key": "Όνομα", + "key": "Κλειδί", "value": "Τιμή", "remove-entry": "Αφαίρεση καταχώρησης", - "add-entry": "Προσθήκη καταχωρησης", - "no-data": "Καμία καταχώρηση" + "add-entry": "Προσθήκη καταχώρησης", + "no-data": "Δεν υπάρχουν καταχωρήσεις" }, "layout": { "layout": "Διάταξη", - "manage": "Διαχείριση Διατάξεων", - "settings": "Ρυθμίσεις Διάταξης", + "layouts": "Διατάξεις", + "manage": "Διαχείριση διατάξεων", + "settings": "Ρυθμίσεις διάταξης", "color": "Χρώμα", - "main": "Κεντρικά", + "main": "Κύριο", "right": "Δεξιά", - "select": "Επιλογή διάταξης" + "left": "Αριστερά", + "select": "Επιλέξτε διάταξη προορισμού", + "percentage-width": "Ποσοστό πλάτους (%)", + "fixed-width": "Σταθερό πλάτος (px)", + "left-width": "Αριστερή στήλη (%)", + "right-width": "Δεξιά στήλη (%)", + "pick-fixed-side": "Σταθερή πλευρά: ", + "layout-fixed-width": "Σταθερό πλάτος (px)", + "value-min-error": "Η τιμή πρέπει να είναι μεγαλύτερη από {{min}}{{unit}}", + "value-max-error": "Η τιμή πρέπει να είναι μικρότερη από {{max}}{{unit}}", + "layout-fixed-width-required": "Απαιτείται σταθερό πλάτος", + "right-width-percentage-required": "Απαιτείται ποσοστό για δεξιά πλευρά", + "left-width-percentage-required": "Απαιτείται ποσοστό για αριστερή πλευρά", + "divider": "Διαχωριστικό", + "right-side": "Διάταξη δεξιάς πλευράς", + "left-side": "Διάταξη αριστερής πλευράς", + "add-new-breakpoint": "Προσθήκη νέου breakpoint", + "breakpoint": "Breakpoint", + "breakpoints": "Breakpoints", + "copy-from": "Αντιγραφή από", + "size": "Μέγεθος", + "delete-breakpoint-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το breakpoint '{{name}}';", + "delete-breakpoint-text": "Σημειώστε ότι μετά την επιβεβαίωση, το breakpoint δεν θα ανακτηθεί και οι ρυθμίσεις θα επανέλθουν στο προεπιλεγμένο." }, "legend": { - "direction": "Κατεύθυνση Λεζάντας", - "position": "Θέση λεζάντας", - "show-max": "Προβολή μέγιστης τιμής", - "show-min": "Προβολή ελάχιστης τιμής", - "show-avg": "Προβολή μέσης τιμής", - "show-total": "Προβολή συνολικής τιμής", - "settings": "Ρυθμίσεις λεζάντας", - "min": "min", - "max": "max", - "avg": "Μ.Ο.", - "total": "Σύνολο" + "direction": "Κατεύθυνση", + "position": "Θέση", + "show-values": "Εμφάνιση τιμών", + "min-option": "Ελάχιστο", + "max-option": "Μέγιστο", + "average-option": "Μέσος όρος", + "total-option": "Σύνολο", + "latest-option": "Πιο πρόσφατο", + "sort-legend": "Ταξινόμηση δεδομένων στο υπόμνημα", + "show-max": "Εμφάνιση μέγιστης τιμής", + "show-min": "Εμφάνιση ελάχιστης τιμής", + "show-avg": "Εμφάνιση μέσης τιμής", + "show-total": "Εμφάνιση συνολικής τιμής", + "show-latest": "Εμφάνιση τελευταίας τιμής", + "settings": "Ρυθμίσεις υπομνήματος", + "min": "ελάχ.", + "max": "μέγ.", + "avg": "μέσος", + "total": "σύνολο", + "latest": "τελευταίο", + "Min": "Ελάχιστο", + "Max": "Μέγιστο", + "Avg": "Μέσος όρος", + "Total": "Σύνολο", + "Latest": "Τελευταίο", + "comparison-time-ago": { + "previousInterval": "(προηγούμενο διάστημα)", + "customInterval": "(προσαρμοσμένο διάστημα)", + "days": "(πριν μία μέρα)", + "weeks": "(πριν μία εβδομάδα)", + "months": "(πριν ένα μήνα)", + "years": "(πριν ένα έτος)" + }, + "column-title": "Τίτλος στήλης", + "label": "Ετικέτα", + "value": "Τιμή" }, "login": { "login": "Σύνδεση", - "request-password-reset": "Αίτημα επαναφοράς κωδικού πρόσβασης", - "reset-password": "Επαναφορά κωδικού πρόσβασης", - "create-password": "Δημιουργία κωδικού πρόσβασης", - "passwords-mismatch-error": "Οι καταχωρημένοι κωδικοί πρόσβασης πρέπει να είναι ίδιοι!", - "password-again": "Επανάληψη κωδικού πρόσβασης", - "sign-in": "Παρακαλώ, συνδεθείτε", - "username": "Όνομα Χρήστη (email)", - "remember-me": "Να με Θυμάσαι", - "forgot-password": "Ξεχάσατε τον κωδικό πρόσβασης;", - "password-reset": "Επαναφορά κωδικού πρόσβασης", - "new-password": "Νέος κωδικός πρόσβασης", - "new-password-again": "Επανάληψη νέου κωδικού πρόσβασης", - "password-link-sent-message": "Ο σύνδεσμος επαναφοράς κωδικού πρόσβασης στάλθηκε με επιτυχία!", - "email": "Email", - "no-account": "Δεν έχετε λογαριασμό;", - "create-account": "Δημιουργία λογαριασμού", - "login-with": "Σύνδεση μέσω {{name}}", - "or": "ή" - }, - "signup": { - "firstname": "Όνομα", - "lastname": "Επίθετο", - "email": "Email", - "signup": "Εγγραφή", + "request-password-reset": "Αίτηση επαναφοράς κωδικού", + "reset-password": "Επαναφορά κωδικού", "create-password": "Δημιουργία κωδικού", - "repeat-password": "Επαναλάβετε τον κωδικό", - "have-account": "Έχετε ήδη έναν λογαριασμό;", - "signin": "Είσοδος", - "no-captcha-message": "Πρέπει να επιβεβαιώσετε ότι δεν είστε ρομπότ", - "password-length-message": "Ο κωδικός σας πρεπει να περιλαμβάνει τουλάχιστον 6 χαρακτήρες", - "email-verification": "Επιβεβαίωση email", - "email-verification-message": "Έχει σταλεί ένα email επιβεβαίωσης στην διεύθυνση email που καθορίσατε.
Παρακαλούμε ακολουθήστε τις οδηγίες που συμπεριλαμβανονται στο email ώστε να ολοκληρωθεί η εγγραφή σας.
Σημείωση: Αν δεν έχει εμφανιστεί σχετικά άμεσα το email, παρακαλούμε ελέγξτε στον φάκελο με την 'Ανεπιθύμητη Αλληλογραφία' (spam) ή ξαναπροσπαθείστε να στείλετε το email κάνοντας κλικ στο κουμπί 'Αποστολή εκ νέου'", - "account-activation-title": "Ενεργοποίηση λογαριασμού", - "account-activated": "Ο λογαριασμός ενεργοποιήθηκε με επιτυχία!", - "account-activated-text": "Συγχαρητήρια!
Ο λογαριασμός σας έχει ενεργοποιηθεί.
Τώρα μπορείται να κάνετε είσοδο στην πλατφόρμα.", - "resend": "Αποστολή εκ νέου", - "inactive-user-exists-title": "Ο ανενεργός χρήστης υπάρχει ήδη", - "inactive-user-exists-text": "Υπάρχει ήδη εγγεγραμμένος χρήστης με μη επιβεβαιωμένη διεύθυνση email.
Click Πατήστε το κουμπί 'Επαναποστολή', αν θέλετε να στείλετε ξανά email επιβεβαίωσης.", - "activating-account": "Ενεργοποίηση λογαριασμού...", - "activating-account-text": "Ο λογαριασμός σας ενεργοποιείται αυτήν τη στιγμή. Παρακαλούμε περιμένετε...", - "accept-privacy-policy": "Αποδοχή Πολιτικής Απορρήτου", - "accept": "Αποδοχή", - "privacy-policy": "Πολιτική Απορρήτου" + "two-factor-authentication": "Έλεγχος ταυτότητας δύο παραγόντων", + "passwords-mismatch-error": "Οι κωδικοί πρέπει να είναι ίδιοι!", + "password-again": "Επαλήθευση κωδικού", + "sign-in": "Παρακαλώ συνδεθείτε", + "username": "Όνομα χρήστη (email)", + "remember-me": "Να με θυμάσαι", + "forgot-password": "Ξεχάσατε τον κωδικό;", + "password-reset": "Επαναφορά κωδικού", + "expired-password-reset-message": "Τα διαπιστευτήριά σας έχουν λήξει! Παρακαλώ δημιουργήστε νέο κωδικό.", + "new-password": "Νέος κωδικός", + "new-password-again": "Επιβεβαίωση νέου κωδικού", + "password-link-sent-message": "Ο σύνδεσμος επαναφοράς έχει σταλεί", + "email": "Email", + "invalid-email-format": "Μη έγκυρη μορφή email.", + "login-with": "Σύνδεση με {{name}}", + "or": "ή", + "error": "Σφάλμα σύνδεσης", + "verify-your-identity": "Επαληθεύστε την ταυτότητά σας", + "select-way-to-verify": "Επιλέξτε τρόπο επαλήθευσης", + "resend-code": "Επαναποστολή κωδικού", + "resend-code-wait": "Επαναποστολή σε { time, plural, =1 {1 δευτερόλεπτο} other {# δευτερόλεπτα} }", + "try-another-way": "Δοκιμάστε άλλο τρόπο", + "totp-auth-description": "Παρακαλώ εισάγετε τον κωδικό από την εφαρμογή ελέγχου ταυτότητας.", + "totp-auth-placeholder": "Κωδικός", + "sms-auth-description": "Ένας κωδικός έχει σταλεί στο τηλέφωνο σας στο {{contact}}.", + "sms-auth-placeholder": "Κωδικός SMS", + "email-auth-description": "Ένας κωδικός έχει σταλεί στο email σας στο {{contact}}.", + "email-auth-placeholder": "Κωδικός email", + "backup-code-auth-description": "Παρακαλώ εισάγετε έναν από τους εφεδρικούς σας κωδικούς.", + "backup-code-auth-placeholder": "Εφεδρικός κωδικός", + "activation-link-expired": "Ο σύνδεσμος ενεργοποίησης έχει λήξει", + "activation-link-expired-message": "Ο σύνδεσμος ενεργοποίησης του προφίλ σας έχει λήξει. Μπορείτε να επιστρέψετε στη σελίδα σύνδεσης για να λάβετε νέο email.", + "reset-password-link-expired": "Ο σύνδεσμος επαναφοράς κωδικού έχει λήξει", + "reset-password-link-expired-message": "Ο σύνδεσμος για την επαναφορά του κωδικού έχει λήξει. Μπορείτε να επιστρέψετε στη σελίδα σύνδεσης για να λάβετε νέο email." + }, + "mobile": { + "add-application": "Προσθήκη εφαρμογής", + "app-id": "App ID", + "app-id-required": "Το App ID είναι υποχρεωτικό", + "app-id-pattern": "Μη έγκυρη μορφή App ID", + "app-store-link": "Σύνδεσμος App Store", + "app-store-link-required": "Ο σύνδεσμος App Store είναι υποχρεωτικός", + "application-details": "Λεπτομέρειες εφαρμογής", + "application-package": "Πακέτο εφαρμογής", + "application-secret": "Μυστικό εφαρμογής", + "application-secret-required": "Το μυστικό εφαρμογής είναι υποχρεωτικό", + "application": "Εφαρμογή", + "applications": "Εφαρμογές", + "copy-app-id": "Αντιγραφή App ID", + "copy-app-store-link": "Αντιγραφή συνδέσμου App Store", + "copy-application-package": "Αντιγραφή πακέτου εφαρμογής", + "copy-application-secret": "Αντιγραφή μυστικού εφαρμογής", + "copy-google-play-link": "Αντιγραφή συνδέσμου Google Play", + "copy-sha256-certificate-fingerprints": "Αντιγραφή αποτυπωμάτων πιστοποιητικού SHA256", + "delete-application": "Διαγραφή εφαρμογής", + "delete-application-button-text": "Κατανοώ τις συνέπειες, διαγραφή εφαρμογής", + "delete-application-text": "Αυτή η ενέργεια δεν μπορεί να αναιρεθεί. Η εφαρμογή σας θα διαγραφεί οριστικά.
Αν δεν θέλετε να τη διαγράψετε μόνιμα μπορείτε να την αναστείλετε προσωρινά.
Για να διαγράψετε την εφαρμογή πληκτρολογήστε \"{{phrase}}\" για επιβεβαίωση.", + "delete-application-title-short": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την εφαρμογή '{{name}}';", + "delete-application-text-short": "Προσοχή, μετά την επιβεβαίωση η εφαρμογή και όλα τα σχετικά δεδομένα θα χαθούν οριστικά.", + "delete-application-phrase": "delete application", + "delete-applications-bundle-text": "Προσοχή, μετά την επιβεβαίωση το πακέτο κινητής και όλα τα σχετικά δεδομένα θα χαθούν οριστικά.", + "delete-applications-bundle-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το πακέτο κινητής '{{bundleName}}';", + "generate-application-secret": "Δημιουργία μυστικού εφαρμογής", + "google-play-link": "Σύνδεσμος Google Play", + "google-play-link-required": "Ο σύνδεσμος Google Play είναι υποχρεωτικός", + "latest-version": "Τελευταία έκδοση", + "min-version": "Ελάχιστη έκδοση", + "invalid-version-pattern": "Μη έγκυρη μορφή έκδοσης. Χρησιμοποιήστε τη μορφή: major.minor.patch (π.χ. 1.0.0).", + "mobile-center": "Κέντρο κινητής", + "mobile-package": "Πακέτο εφαρμογής", + "mobile-package-max-length": "Το πακέτο εφαρμογής πρέπει να είναι λιγότερο από 256 χαρακτήρες", + "mobile-package-required": "Το πακέτο εφαρμογής είναι υποχρεωτικό.", + "mobile-package-pattern": "Μη έγκυρη μορφή πακέτου εφαρμογής", + "no-application": "Δεν βρέθηκαν εφαρμογές", + "no-bundles": "Δεν βρέθηκαν πακέτα", + "platform-type": "Τύπος πλατφόρμας", + "search-application": "Αναζήτηση εφαρμογών", + "search-bundles": "Αναζήτηση πακέτων", + "set": "Ορισμός", + "sha256-certificate-fingerprints": "Αποτυπώματα πιστοποιητικού SHA256", + "sha256-certificate-fingerprints-required": "Τα αποτυπώματα πιστοποιητικού SHA256 είναι υποχρεωτικά", + "sha256-certificate-fingerprints-pattern": "Μη έγκυρη μορφή αποτυπώματος SHA256", + "show-hidden-pages": "Εμφάνιση κρυφών σελίδων", + "status": "Κατάσταση", + "status-type": { + "deprecated": "Καταργημένο", + "draft": "Πρόχειρο", + "published": "Δημοσιευμένο", + "suspended": "Ανασταλμένο" + }, + "store-information": "Πληροφορίες καταστήματος", + "version-information": "Πληροφορίες έκδοσης", + "min-version-release-notes": "Σημειώσεις έκδοσης ελάχιστης έκδοσης", + "latest-version-release-notes": "Σημειώσεις έκδοσης τελευταίας έκδοσης", + "bundle": "Πακέτο", + "bundles": "Πακέτα", + "add-bundle": "Προσθήκη πακέτου", + "title": "Τίτλος", + "title-required": "Ο τίτλος είναι υποχρεωτικός", + "title-cannot-contain-only-spaces": "Ο τίτλος δεν μπορεί να περιέχει μόνο κενά", + "title-max-length": "Ο τίτλος πρέπει να είναι μικρότερος από 256 χαρακτήρες", + "oauth-clients": "Πελάτες OAuth 2.0", + "android-app": "Εφαρμογή Android", + "android-application": "Εφαρμογή Android", + "ios-app": "Εφαρμογή iOS", + "ios-application": "Εφαρμογή iOS", + "invalid-store-link": "Μη έγκυρος σύνδεσμος καταστήματος", + "enable-oauth": "Ενεργοποίηση OAuth 2.0", + "enable-self-registration": "Ενεργοποίηση αυτόματης εγγραφής", + "edit-bundle": "Επεξεργασία πακέτου", + "description": "Περιγραφή", + "basic-settings": "Βασικές ρυθμίσεις", + "no-application-matching": "Δεν βρέθηκε εφαρμογή που να ταιριάζει με '{{entity}}'", + "no-bundle-matching": "Δεν βρέθηκε πακέτο που να ταιριάζει με '{{entity}}'", + "application-required": "Η εφαρμογή είναι υποχρεωτική.", + "bundle-required": "Το πακέτο είναι υποχρεωτικό.", + "no-application-text": "Δεν βρέθηκαν εφαρμογές", + "no-bundle-text": "Δεν βρέθηκαν πακέτα", + "layout": "Διάταξη", + "pages": "Σελίδες", + "hide-all-pages": "Απόκρυψη όλων των σελίδων", + "reset-to-default-pages": "Επαναφορά σε προεπιλεγμένες σελίδες", + "add-specific-page": "Προσθήκη συγκεκριμένης σελίδας", + "visible": "Ορατό", + "hidden": "Κρυφό", + "reset-to-page-default": "Επαναφορά σελίδας στις προεπιλογές", + "mobile-599": "Κινητό (μέγιστο 599px)", + "tablet-959": "Tablet (μέγιστο 959px)", + "max-element-number": "Μέγιστος αριθμός στοιχείων", + "page-name": "Όνομα σελίδας", + "page-name-required": "Το όνομα σελίδας είναι υποχρεωτικό.", + "page-name-cannot-contain-only-spaces": "Το όνομα σελίδας δεν μπορεί να περιέχει μόνο κενά.", + "page-name-max-length": "Το όνομα σελίδας πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "page-type": "Τύπος σελίδας", + "pages-types": { + "dashboard": "Πίνακας ελέγχου", + "web-view": "Web προβολή", + "custom": "Προσαρμοσμένη" + }, + "url": "URL", + "invalid-url-format": "Μη έγκυρη μορφή URL", + "path": "Διαδρομή", + "invalid-path-format": "Μη έγκυρη μορφή διαδρομής", + "custom-page": "Προσαρμοσμένη σελίδα", + "edit-page": "Επεξεργασία σελίδας", + "edit-custom-page": "Επεξεργασία προσαρμοσμένης σελίδας", + "delete-page": "Διαγραφή σελίδας", + "qr-code-widget": "Widget QR κώδικα", + "type-here": "Πληκτρολογήστε εδώ", + "configuration-dialog": "Διάλογος ρυθμίσεων", + "configuration-app": "Εφαρμογή ρυθμίσεων", + "configuration-step": { + "prepare-environment-title": "Προετοιμασία περιβάλλοντος ανάπτυξης", + "prepare-environment-text": "Η εφαρμογή ThingsBoard Mobile απαιτεί το Flutter SDK. Ακολουθήστε τις οδηγίες για να ρυθμίσετε το Flutter SDK.", + "get-source-code-title": "Λήψη πηγαίου κώδικα", + "get-source-code-text": "Μπορείτε να λάβετε τον πηγαίο κώδικα της εφαρμογής ThingsBoard Mobile με cloning από το GitHub:", + "configure-api-title": "Διαμόρφωση ThingsBoard API endpoint", + "configure-api-text": "Ανοίξτε το έργο flutter_thingsboard_pe_app στον επεξεργαστή σας. Επεξεργαστείτε:", + "configure-api-hint": "Ορίστε την τιμή της σταθεράς thingsBoardApiEndpoint ώστε να ταιριάζει με το API endpoint του ThingsBoard server. Μην χρησιμοποιείτε 'localhost' ή '127.0.0.1'.", + "run-app-title": "Εκτέλεση εφαρμογής", + "run-app-text": "Εκτελέστε την εφαρμογή μέσω του IDE σας ή μέσω τερματικού με την ακόλουθη εντολή:", + "more-information": "Περισσότερες πληροφορίες βρίσκονται στην τεκμηρίωση έναρξης.", + "getting-started": "Ξεκινώντας", + "configure-package-title": "Διαμόρφωση πακέτου εφαρμογής", + "configure-package-text": "Μπορείτε να αλλάξετε χειροκίνητα το Πακέτο Εφαρμογής ή να χρησιμοποιήσετε εργαλείο CLI τρίτων.", + "configure-package-text-install": "Για να εγκαταστήσετε το εργαλείο Rename CLI, εκτελέστε την ακόλουθη εντολή:", + "configure-package-run-commands": "Εκτελέστε αυτές τις εντολές στον root φάκελο του έργου σας:" + } + }, + "notification": { + "action-button": "Κουμπί ενέργειας", + "action-type": "Τύπος ενέργειας", + "active": "Ενεργό", + "add-notification-recipients-group": "Προσθήκη ομάδας παραληπτών ειδοποιήσεων", + "add-notification-template": "Προσθήκη προτύπου ειδοποίησης", + "add-recipient": "Προσθήκη παραλήπτη", + "add-recipients": "Προσθήκη παραληπτών", + "add-rule": "Προσθήκη κανόνα", + "add-stage": "Προσθήκη σταδίου", + "add-template": "Προσθήκη προτύπου", + "after": "Μετά", + "alarm-assignment-trigger-settings": "Ρυθμίσεις σκανδάλης ανάθεσης συναγερμού", + "alarm-comment-trigger-settings": "Ρυθμίσεις σκανδάλης σχολίων συναγερμού", + "alarm-trigger-settings": "Ρυθμίσεις σκανδάλης συναγερμού", + "all": "Όλα", + "api-feature-hint": "Αν το πεδίο είναι κενό, η σκανδάλη θα εφαρμοστεί σε όλα τα χαρακτηριστικά API", + "api-usage-trigger-settings": "Ρυθμίσεις σκανδάλης χρήσης API", + "new-platform-version-trigger-settings": "Ρυθμίσεις σκανδάλης νέας έκδοσης πλατφόρμας", + "rate-limits-trigger-settings": "Ρυθμίσεις σκανδάλης υπέρβασης ορίων ρυθμού", + "task-processing-failure-trigger-settings": "Ρυθμίσεις σκανδάλης αποτυχίας επεξεργασίας εργασίας", + "at-least-one-should-be-selected": "Πρέπει να επιλεγεί τουλάχιστον ένα", + "basic-settings": "Βασικές ρυθμίσεις", + "button-text": "Κείμενο κουμπιού", + "button-text-required": "Το κείμενο κουμπιού είναι υποχρεωτικό", + "button-text-max-length": "Το κείμενο κουμπιού πρέπει να είναι έως {{ length }} χαρακτήρες", + "compose": "Σύνθεση", + "conversation": "Συνομιλία", + "conversation-required": "Απαιτείται συνομιλία", + "copy-notification-template": "Αντιγραφή προτύπου ειδοποίησης", + "copy-rule": "Αντιγραφή κανόνα", + "copy-template": "Αντιγραφή προτύπου", + "create-new": "Δημιουργία νέου", + "created": "Δημιουργήθηκε", + "customize-messages": "Προσαρμογή μηνυμάτων", + "delete-notification-text": "Προσοχή, μετά την επιβεβαίωση η ειδοποίηση δεν θα είναι ανακτήσιμη.", + "delete-notification-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την ειδοποίηση;", + "delete-notifications-text": "Προσοχή, μετά την επιβεβαίωση οι ειδοποιήσεις δεν θα είναι ανακτήσιμες.", + "delete-notifications-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 ειδοποίηση} other {# ειδοποιήσεις} };", + "delete-recipient-text": "Προσοχή, μετά την επιβεβαίωση ο παραλήπτης δεν θα είναι ανακτήσιμος.", + "delete-recipient-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον παραλήπτη '{{recipientName}}';", + "delete-recipients-text": "Προσοχή, μετά την επιβεβαίωση οι παραλήπτες δεν θα είναι ανακτήσιμοι.", + "delete-recipients-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 παραλήπτη} other {# παραλήπτες} };", + "delete-request-text": "Προσοχή, μετά την επιβεβαίωση το αίτημα δεν θα είναι ανακτήσιμο.", + "delete-request-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το αίτημα;", + "delete-requests-text": "Προσοχή, μετά την επιβεβαίωση τα αιτήματα δεν θα είναι ανακτήσιμα.", + "delete-requests-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 αίτημα} other {# αιτήματα} };", + "delete-rule-text": "Προσοχή, μετά την επιβεβαίωση ο κανόνας δεν θα είναι ανακτήσιμος.", + "delete-rule-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον κανόνα '{{ruleName}}';", + "delete-rules-text": "Προσοχή, μετά την επιβεβαίωση οι κανόνες δεν θα είναι ανακτήσιμοι.", + "delete-rules-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 κανόνα} other {# κανόνες} };", + "delete-template-text": "Προσοχή, μετά την επιβεβαίωση το πρότυπο δεν θα είναι ανακτήσιμο.", + "delete-template-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το πρότυπο '{{templateName}}';", + "delete-templates-text": "Προσοχή, μετά την επιβεβαίωση τα πρότυπα δεν θα είναι ανακτήσιμα.", + "delete-templates-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 πρότυπο} other {# πρότυπα} };", + "deleted": "Διαγράφηκε", + "delivery-method": { + "delivery-method": "Μέθοδος παράδοσης", + "email": "Email", + "email-preview": "Προεπισκόπηση ειδοποίησης Email", + "slack": "Slack", + "slack-preview": "Προεπισκόπηση ειδοποίησης Slack", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Προεπισκόπηση ειδοποίησης Microsoft Teams", + "sms": "SMS", + "sms-preview": "Προεπισκόπηση ειδοποίησης SMS", + "web": "Web", + "web-preview": "Προεπισκόπηση ειδοποίησης Web", + "mobile-app": "Κινητή εφαρμογή", + "mobile-app-preview": "Προεπισκόπηση ειδοποίησης κινητής εφαρμογής" + }, + "delivery-method-not-configure-click": "Η μέθοδος παράδοσης δεν έχει ρυθμιστεί. Κάντε κλικ για ρύθμιση.", + "delivery-method-not-configure-contact": "Η μέθοδος παράδοσης δεν έχει ρυθμιστεί. Επικοινωνήστε με τον διαχειριστή του συστήματος.", + "delivery-methods": "Μέθοδοι παράδοσης", + "description": "Περιγραφή", + "device-activity-trigger-settings": "Ρυθμίσεις σκανδάλης δραστηριότητας συσκευής", + "device-list-rule-hint": "Αν το πεδίο είναι κενό, η σκανδάλη θα εφαρμοστεί σε όλες τις συσκευές", + "device-profiles-list-rule-hint": "Αν το πεδίο είναι κενό, η σκανδάλη θα εφαρμοστεί σε όλα τα προφίλ συσκευών", + "disabled": "Ανενεργό", + "edge-trigger-settings": "Ρυθμίσεις σκανδάλης Edge", + "edge-list-rule-hint": "Αν το πεδίο είναι κενό, η σκανδάλη θα εφαρμοστεί σε όλα τα Edge instances", + "edit-notification-recipients-group": "Επεξεργασία ομάδας παραληπτών ειδοποιήσεων", + "edit-notification-template": "Επεξεργασία προτύπου ειδοποίησης", + "edit-rule": "Επεξεργασία κανόνα", + "edit-template": "Επεξεργασία προτύπου", + "enabled": "Ενεργοποιημένο", + "entities-limit-trigger-settings": "Ρυθμίσεις σκανδάλης ορίου οντοτήτων", + "entity-action-trigger-settings": "Ρυθμίσεις σκανδάλης ενέργειας οντοτήτων", + "entity-type": "Τύπος οντότητας", + "escalation-chain": "Αλυσίδα κλιμάκωσης", + "failed-send": "Αποτυχίες αποστολής", + "fails": "{ count, plural, =1 {1 αποτυχία} other {# αποτυχίες} }", + "filter": "Φίλτρο", + "first-recipient": "Πρώτος παραλήπτης", + "inactive": "Ανενεργό", + "inbox": "Εισερχόμενα", + "notification-inbox": "Ειδοποιήσεις / Εισερχόμενα", + "input-field-support-templatization": "Το πεδίο εισαγωγής υποστηρίζει δυναμική μορφοποίηση.", + "input-fields-support-templatization": "Τα πεδία εισαγωγής υποστηρίζουν δυναμική μορφοποίηση.", + "link": "Σύνδεσμος", + "link-required": "Ο σύνδεσμος είναι υποχρεωτικός", + "link-type": { + "dashboard": "Άνοιγμα πίνακα ελέγχου", + "link": "Άνοιγμα συνδέσμου URL" + }, + "loading-notifications": "Φόρτωση ειδοποιήσεων...", + "management": "Διαχείριση ειδοποιήσεων", + "mark-all-as-read": "Σημείωση όλων ως αναγνωσμένα", + "mark-as-read": "Σήμανση ως αναγνωσμένο", + "message": "Μήνυμα", + "message-required": "Το μήνυμα είναι υποχρεωτικό", + "message-max-length": "Το μήνυμα πρέπει να είναι μικρότερο ή ίσο με {{ length }} χαρακτήρες", + "name": "Όνομα", + "name-required": "Το όνομα είναι υποχρεωτικό", + "new-notification": "Νέα ειδοποίηση", + "no-inbox-notification": "Δεν βρέθηκε ειδοποίηση", + "no-notification-request": "Δεν υπάρχει αίτημα ειδοποίησης", + "no-notification-templates": "Δεν βρέθηκαν πρότυπα ειδοποιήσεων", + "no-notifications-yet": "Δεν υπάρχουν ειδοποιήσεις ακόμα", + "no-recipients-notification": "Δεν υπάρχουν παραλήπτες ειδοποίησης", + "no-recipients-matching": "Δεν βρέθηκαν παραλήπτες που να ταιριάζουν με '{{entity}}'", + "no-recipients-text": "Δεν βρέθηκε παραλήπτης", + "no-rule": "Δεν έχει ρυθμιστεί κανόνας", + "no-rules-notification": "Δεν υπάρχουν ειδοποιήσεις κανόνων", + "no-severity-found": "Δεν βρέθηκε σοβαρότητα", + "no-severity-matching": "'{{severity}}' δεν βρέθηκε.", + "no-template-matching": "Δεν βρέθηκε πόρος που να ταιριάζει με '{{template}}'", + "not-found-slack-recipient": "Δεν βρέθηκε παραλήπτης Slack", + "notification": "Ειδοποίηση", + "notification-center": "Κέντρο ειδοποιήσεων", + "notification-tap-action": "Ενέργεια πάτησης ειδοποίησης", + "notification-tap-action-hint": "Αν δεν είναι ενεργοποιημένη, θα χρησιμοποιηθεί ο προεπιλεγμένος πίνακας συναγερμών", + "notify": "ειδοποίηση", + "notify-again": "Ειδοποίηση ξανά", + "notify-alarm-action": { + "acknowledged": "Ο συναγερμός επιβεβαιώθηκε", + "assigned": "Ο συναγερμός ανατέθηκε", + "cleared": "Ο συναγερμός καθαρίστηκε", + "created": "Δημιουργήθηκε συναγερμός", + "severity-changed": "Η σοβαρότητα του συναγερμού άλλαξε", + "unassigned": "Ο συναγερμός αποεπιλέχθηκε" + }, + "notify-on": "Ειδοποίηση για", + "notify-on-comment-update": "Ειδοποίηση για ενημέρωση σχολίου", + "notify-on-required": "Το πεδίο ειδοποίησης για είναι υποχρεωτικό", + "notify-on-unassign": "Ειδοποίηση για αποεπιλογή", + "notify-only-user-comments": "Ειδοποίηση μόνο για σχόλια χρηστών", + "only-rule-chain-lifecycle-failures": "Μόνο αποτυχίες κύκλου ζωής αλυσίδας κανόνων", + "only-rule-node-lifecycle-failures": "Μόνο αποτυχίες κύκλου ζωής κόμβου κανόνων", + "platform-users": "Χρήστες πλατφόρμας", + "rate-limits": "Όρια ρυθμού", + "rate-limits-hint": "Αν το πεδίο είναι κενό, η σκανδάλη θα εφαρμοστεί σε όλα τα όρια ρυθμού", + "recipient": "Παραλήπτης", + "recipient-group": "Ομάδα παραληπτών", + "recipient-type": { + "affected-tenant-administrators": "Διαχειριστές ενοικιαστών που επηρεάζονται", + "affected-user": "Χρήστης που επηρεάζεται", + "all-users": "Όλοι οι χρήστες", + "customer-users": "Χρήστες πελατών", + "system-administrators": "Διαχειριστές συστήματος", + "tenant-administrators": "Διαχειριστές ενοικιαστών", + "user-filters": "Φίλτρο χρηστών", + "user-list": "Λίστα χρηστών", + "users-entity-owner": "Χρήστες ιδιοκτήτη οντότητας" + }, + "recipients": "Παραλήπτες", + "notification-recipient": "Παραλήπτης ειδοποίησης", + "notification-recipient-required": "Ο παραλήπτης ειδοποίησης είναι υποχρεωτικός.", + "notification-recipients": "Ειδοποιήσεις / Παραλήπτες", + "recipients-count": "{ count, plural, =1 {1 παραλήπτης} other {# παραλήπτες} }", + "recipients-required": "Οι παραλήπτες είναι υποχρεωτικοί", + "refresh-allow-delivery-method": "Ανανέωση μεθόδου παράδοσης", + "request-search": "Αναζήτηση αιτήματος", + "request-status": { + "processing": "Επεξεργασία", + "scheduled": "Προγραμματισμένο", + "sent": "Απεσταλμένο" + }, + "review": "Ανασκόπηση", + "rule": "Κανόνας", + "rule-chain-list-rule-hint": "Αν το πεδίο είναι κενό, η σκανδάλη θα εφαρμοστεί σε όλες τις αλυσίδες κανόνων", + "rule-engine-events-trigger-settings": "Ρυθμίσεις σκανδάλης γεγονότων μηχανής κανόνων", + "rule-engine-filter": "Φίλτρο μηχανής κανόνων", + "rule-name": "Όνομα κανόνα", + "rule-name-required": "Το όνομα είναι υποχρεωτικό", + "rule-disable": "Απενεργοποίηση κανόνα ειδοποίησης", + "rule-enable": "Ενεργοποίηση κανόνα ειδοποίησης", + "rule-node-filter": "Φίλτρο κόμβου κανόνα", + "rules": "Κανόνες", + "notification-rules": "Ειδοποιήσεις / Κανόνες", + "scheduler-later": "Προγραμματισμός για αργότερα", + "search-notification": "Αναζήτηση ειδοποιήσεων", + "search-recipients": "Αναζήτηση παραληπτών", + "search-rules": "Αναζήτηση κανόνων", + "search-templates": "Αναζήτηση προτύπων", + "see-documentation": "Δείτε την τεκμηρίωση", + "selected-notifications": "{ count, plural, =1 {1 ειδοποίηση} other {# ειδοποιήσεις} } επιλέχθηκαν", + "selected-recipients": "{ count, plural, =1 {1 παραλήπτης} other {# παραλήπτες} } επιλέχθηκαν", + "selected-requests": "{ count, plural, =1 {1 αίτημα} other {# αιτήματα} } επιλέχθηκαν", + "selected-rules": "{ count, plural, =1 {1 κανόνας} other {# κανόνες} } επιλέχθηκαν", + "selected-template": "{ count, plural, =1 {1 πρότυπο} other {# πρότυπα} } επιλέχθηκαν", + "send-notification": "Αποστολή ειδοποίησης", + "sent": "Απεσταλμένο", + "setup": "Ρύθμιση", + "notification-sent": "Ειδοποιήσεις / Απεσταλμένα", + "set-entity-from-notification": "Ορισμός οντότητας από ειδοποίηση σε κατάσταση πίνακα", + "slack-chanel-type": "Τύπος καναλιού Slack", + "slack-chanel-types": { + "direct": "Άμεσο μήνυμα", + "private-channel": "Ιδιωτικό κανάλι", + "public-channel": "Δημόσιο κανάλι" + }, + "start-from-scratch": "Ξεκινήστε από την αρχή", + "status": "Κατάσταση", + "stop-escalation-alarm-status-become": "Διακοπή κλιμάκωσης όταν η κατάσταση του συναγερμού γίνει:", + "subject": "Θέμα", + "subject-required": "Το θέμα είναι υποχρεωτικό", + "subject-max-length": "Το θέμα πρέπει να είναι μικρότερο ή ίσο με {{ length }} χαρακτήρες", + "template": "Πρότυπο", + "template-name": "Όνομα προτύπου", + "template-required": "Το πρότυπο είναι υποχρεωτικό", + "template-type": { + "alarm": "Συναγερμός", + "alarm-assignment": "Ανάθεση συναγερμού", + "alarm-comment": "Σχόλιο συναγερμού", + "api-usage-limit": "Όριο χρήσης API", + "device-activity": "Δραστηριότητα συσκευής", + "entities-limit": "Όριο οντοτήτων", + "entity-action": "Ενέργεια οντότητας", + "general": "Γενικό", + "rule-engine-lifecycle-event": "Γεγονός κύκλου ζωής μηχανής κανόνων", + "rule-node": "Κόμβος κανόνα", + "new-platform-version": "Νέα έκδοση πλατφόρμας", + "rate-limits": "Υπέρβαση ορίων ρυθμού", + "edge-communication-failure": "Αποτυχία επικοινωνίας Edge", + "edge-connection": "Σύνδεση Edge", + "task-processing-failure": "Αποτυχία επεξεργασίας εργασίας" + }, + "templates": "Πρότυπα", + "notification-templates": "Ειδοποιήσεις / Πρότυπα", + "tenant-profiles-list-rule-hint": "Αν το πεδίο είναι κενό, η σκανδάλη θα εφαρμοστεί σε όλα τα προφίλ ενοικιαστών", + "tenants-list-rule-hint": "Αν το πεδίο είναι κενό, η σκανδάλη θα εφαρμοστεί σε όλους τους ενοικιαστές", + "threshold": "Κατώφλι", + "theme-color": "Χρώμα θέματος", + "time": "Ώρα", + "track-rule-node-events": "Παρακολούθηση γεγονότων κόμβου κανόνα", + "trigger": { + "alarm": "Συναγερμός", + "alarm-assignment": "Ανάθεση συναγερμού", + "alarm-comment": "Σχόλιο συναγερμού", + "api-usage-limit": "Όριο χρήσης API", + "device-activity": "Δραστηριότητα συσκευής", + "entities-limit": "Όριο οντοτήτων", + "entity-action": "Ενέργεια οντότητας", + "rule-engine-lifecycle-event": "Γεγονός κύκλου ζωής μηχανής κανόνων", + "new-platform-version": "Νέα έκδοση πλατφόρμας", + "rate-limits": "Υπέρβαση ορίων ρυθμού", + "edge-connection": "Σύνδεση Edge", + "edge-communication-failure": "Αποτυχία επικοινωνίας Edge", + "task-processing-failure": "Αποτυχία επεξεργασίας εργασίας", + "trigger": "Σκανδάλη", + "trigger-required": "Η σκανδάλη είναι υποχρεωτική" + }, + "type": "Τύπος", + "unread": "Μη αναγνωσμένο", + "updated": "Ενημερώθηκε", + "use-deprecated-webhook-connectors": "Χρήση παρωχημένων συνδετών Webhook", + "use-old-api": "Χρήση παλιού API", + "use-template": "Χρήση προτύπου", + "view-all": "Προβολή όλων", + "warning": "Προειδοποίηση", + "webhook-url": "Webhook URL", + "webhook-url-required": "Το Webhook URL είναι υποχρεωτικό", + "workflow-url": "Workflow URL", + "workflow-url-required": "Το Workflow URL είναι υποχρεωτικό", + "channel-name": "Όνομα καναλιού", + "channel-name-required": "Το όνομα καναλιού είναι υποχρεωτικό", + "settings": { + "notification-settings": "Ρυθμίσεις ειδοποιήσεων", + "reset-all": "Επαναφορά όλων των ρυθμίσεων", + "reset-all-title": "Είστε βέβαιοι ότι θέλετε να επαναφέρετε τη φόρμα;", + "reset-all-text": "Μετά την επιβεβαίωση, η φόρμα ρυθμίσεων θα επανέλθει στην προεπιλεγμένη τιμή και θα αποθηκευτεί.", + "type": "Τύπος", + "enable-all": "Ενεργοποίηση όλων", + "disable-all": "Απενεργοποίηση όλων", + "delivery-not-configured": "Η μέθοδος παράδοσης δεν έχει ρυθμιστεί" + } + }, + "ota-update": { + "add": "Προσθήκη πακέτου", + "assign-firmware": "Εκχωρημένο firmware", + "assign-firmware-required": "Το εκχωρημένο firmware είναι υποχρεωτικό", + "assign-software": "Εκχωρημένο λογισμικό", + "assign-software-required": "Το εκχωρημένο λογισμικό είναι υποχρεωτικό", + "auto-generate-checksum": "Αυτόματη δημιουργία αθροίσματος ελέγχου", + "checksum": "Άθροισμα ελέγχου", + "checksum-hint": "Αν το άθροισμα ελέγχου είναι κενό, θα δημιουργηθεί αυτόματα", + "checksum-algorithm": "Αλγόριθμος αθροίσματος ελέγχου", + "checksum-copied-message": "Το άθροισμα ελέγχου του πακέτου έχει αντιγραφεί στο πρόχειρο", + "change-firmware": "Η αλλαγή του firmware μπορεί να προκαλέσει ενημέρωση σε { count, plural, =1 {1 συσκευή} other {# συσκευές} }.", + "change-software": "Η αλλαγή του λογισμικού μπορεί να προκαλέσει ενημέρωση σε { count, plural, =1 {1 συσκευή} other {# συσκευές} }.", + "chose-compatible-device-profile": "Το ανεβασμένο πακέτο θα είναι διαθέσιμο μόνο για συσκευές με το επιλεγμένο προφίλ.", + "chose-firmware-distributed-device": "Επιλέξτε το firmware που θα διανεμηθεί στις συσκευές", + "chose-software-distributed-device": "Επιλέξτε το λογισμικό που θα διανεμηθεί στις συσκευές", + "content-type": "Τύπος περιεχομένου", + "copy-checksum": "Αντιγραφή αθροίσματος ελέγχου", + "copy-direct-url": "Αντιγραφή άμεσου URL", + "copyId": "Αντιγραφή ID πακέτου", + "copied": "Αντιγράφηκε!", + "delete": "Διαγραφή πακέτου", + "delete-ota-update-text": "Προσοχή, μετά την επιβεβαίωση η ενημέρωση OTA δεν θα είναι ανακτήσιμη.", + "delete-ota-update-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την ενημέρωση OTA '{{title}}';", + "delete-ota-updates-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες ενημερώσεις OTA θα διαγραφούν.", + "delete-ota-updates-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 ενημέρωση OTA} other {# ενημερώσεις OTA} };", + "description": "Περιγραφή", + "direct-url": "Άμεσο URL", + "direct-url-copied-message": "Το άμεσο URL του πακέτου έχει αντιγραφεί στο πρόχειρο", + "direct-url-required": "Το άμεσο URL είναι υποχρεωτικό", + "download": "Λήψη πακέτου", + "drop-file": "Αφήστε ένα αρχείο πακέτου ή κάντε κλικ για να επιλέξετε ένα αρχείο για μεταφόρτωση.", + "drop-package-file-or": "Σύρετε και αφήστε ένα αρχείο πακέτου ή", + "file-name": "Όνομα αρχείου", + "file-size": "Μέγεθος αρχείου", + "file-size-bytes": "Μέγεθος αρχείου σε bytes", + "idCopiedMessage": "Το ID του πακέτου έχει αντιγραφεί στο πρόχειρο", + "no-firmware-matching": "Δεν βρέθηκαν συμβατά πακέτα OTA ενημερώσεων firmware που να ταιριάζουν με '{{entity}}'.", + "no-firmware-text": "Δεν έχουν προβλεφθεί συμβατά πακέτα OTA ενημερώσεων firmware.", + "no-packages-text": "Δεν βρέθηκαν πακέτα", + "no-software-matching": "Δεν βρέθηκαν συμβατά πακέτα OTA ενημερώσεων λογισμικού που να ταιριάζουν με '{{entity}}'.", + "no-software-text": "Δεν έχουν προβλεφθεί συμβατά πακέτα OTA ενημερώσεων λογισμικού.", + "ota-update": "Ενημέρωση OTA", + "ota-update-details": "Λεπτομέρειες ενημέρωσης OTA", + "ota-updates": "Ενημερώσεις OTA", + "package-file": "Αρχείο πακέτου", + "package-type": "Τύπος πακέτου", + "packages-repository": "Αποθετήριο πακέτων", + "search": "Αναζήτηση πακέτων", + "selected-package": "{ count, plural, =1 {1 πακέτο} other {# πακέτα} } επιλέχθηκαν", + "title": "Τίτλος", + "title-required": "Ο τίτλος είναι υποχρεωτικός.", + "title-max-length": "Ο τίτλος πρέπει να είναι μικρότερος από 256 χαρακτήρες", + "types": { + "firmware": "Firmware", + "software": "Λογισμικό" + }, + "upload-binary-file": "Μεταφόρτωση δυαδικού αρχείου", + "use-external-url": "Χρήση εξωτερικού URL", + "version": "Έκδοση", + "version-required": "Η έκδοση είναι υποχρεωτική.", + "version-tag": "Ετικέτα έκδοσης", + "version-tag-hint": "Η προσαρμοσμένη ετικέτα πρέπει να ταιριάζει με την έκδοση του πακέτου που αναφέρεται από τη συσκευή σας.", + "version-max-length": "Η έκδοση πρέπει να είναι μικρότερη από 256 χαρακτήρες", + "warning-after-save-no-edit": "Μόλις το πακέτο μεταφορτωθεί, δεν θα μπορείτε να τροποποιήσετε τον τίτλο, την έκδοση, το προφίλ συσκευής και τον τύπο πακέτου." }, "position": { - "top": "Κορυφή", - "bottom": "Κάτω μέρος", + "top": "Πάνω", + "bottom": "Κάτω", "left": "Αριστερά", "right": "Δεξιά" }, "profile": { "profile": "Προφίλ", + "last-login-time": "Τελευταία σύνδεση", "change-password": "Αλλαγή κωδικού", - "current-password": "Τρέχων κωδικός" + "current-password": "Τρέχων κωδικός", + "copy-jwt-token": "Αντιγραφή JWT token", + "jwt-token": "JWT token", + "token-valid-till": "Το token είναι έγκυρο έως", + "tokenCopiedSuccessMessage": "Το JWT token αντιγράφηκε στο πρόχειρο", + "tokenCopiedWarnMessage": "Το JWT token έχει λήξει! Παρακαλώ, ανανεώστε τη σελίδα." + }, + "profiles": { + "profiles": "Προφίλ" + }, + "security": { + "security": "Ασφάλεια", + "general-settings": "Γενικές ρυθμίσεις ασφαλείας", + "access-token": "Access token", + "access-token-required": "Το access token είναι υποχρεωτικό", + "clientId": "Client ID", + "clientId-required": "Το Client ID είναι υποχρεωτικό", + "username": "Όνομα χρήστη", + "username-required": "Το όνομα χρήστη είναι υποχρεωτικό", + "ca-cert": "Πιστοποιητικό CA", + "2fa": { + "2fa": "Διπλή επαλήθευση", + "2fa-description": "Η διπλή επαλήθευση προστατεύει τον λογαριασμό σας από μη εξουσιοδοτημένη πρόσβαση. Απλώς εισάγετε έναν κωδικό ασφαλείας κατά τη σύνδεση.", + "authenticate-with": "Μπορείτε να επαληθευτείτε με:", + "disable-2fa-provider-text": "Η απενεργοποίηση του {{name}} θα κάνει τον λογαριασμό σας λιγότερο ασφαλή", + "disable-2fa-provider-title": "Είστε βέβαιοι ότι θέλετε να απενεργοποιήσετε το {{name}};", + "get-new-code": "Λήψη νέου κωδικού", + "main-2fa-method": "Χρήση ως κύρια μέθοδος διπλής επαλήθευσης", + "dialog": { + "activation-step-description-email": "Την επόμενη φορά που θα συνδεθείτε, θα σας ζητηθεί να εισάγετε τον κωδικό ασφαλείας που θα σταλεί στο email σας.", + "activation-step-description-sms": "Την επόμενη φορά που θα συνδεθείτε, θα σας ζητηθεί να εισάγετε τον κωδικό ασφαλείας που θα σταλεί στον αριθμό τηλεφώνου.", + "activation-step-description-totp": "Την επόμενη φορά που θα συνδεθείτε, θα πρέπει να παρέχετε έναν κωδικό επαλήθευσης.", + "activation-step-label": "Ενεργοποίηση", + "backup-code-description": "Εκτυπώστε τους κωδικούς για χρήση όταν δεν έχετε πρόσβαση στο τηλέφωνό σας.", + "backup-code-warn": "Μόλις φύγετε από αυτή τη σελίδα, οι κωδικοί δεν θα είναι πλέον ορατοί. Αποθηκεύστε τους με ασφάλεια.", + "download-txt": "Λήψη (txt)", + "email-step-description": "Εισάγετε email για χρήση ως μέθοδος επαλήθευσης.", + "email-step-label": "Email", + "enable-email-title": "Ενεργοποίηση επαλήθευσης μέσω email", + "enable-sms-title": "Ενεργοποίηση επαλήθευσης μέσω SMS", + "enable-totp-title": "Ενεργοποίηση εφαρμογής επαλήθευσης", + "enter-verification-code": "Εισάγετε τον 6-ψήφιο κωδικό εδώ", + "get-backup-code-title": "Λήψη εφεδρικού κωδικού", + "next": "Επόμενο", + "scan-qr-code": "Σαρώστε τον κωδικό QR με την εφαρμογή επαλήθευσης", + "send-code": "Αποστολή κωδικού", + "sms-step-description": "Εισάγετε αριθμό τηλεφώνου για χρήση ως μέθοδος επαλήθευσης.", + "sms-step-label": "Αριθμός τηλεφώνου", + "success": "Επιτυχία!", + "totp-step-description-install": "Μπορείτε να εγκαταστήσετε εφαρμογές όπως Google Authenticator, Authy ή Duo.", + "totp-step-description-open": "Ανοίξτε την εφαρμογή επαλήθευσης στο κινητό σας.", + "totp-step-label": "Λήψη εφαρμογής", + "verification-code": "6-ψήφιος κωδικός", + "verification-code-invalid": "Μη έγκυρη μορφή κωδικού", + "verification-code-incorrect": "Ο κωδικός είναι λανθασμένος", + "verification-code-many-request": "Πολλές προσπάθειες, ελέγξτε τον κωδικό", + "verification-step-description": "Εισάγετε τον 6-ψήφιο κωδικό που μόλις σας στείλαμε στο {{address}}", + "verification-step-label": "Επαλήθευση" + }, + "provider": { + "email": "Email", + "email-description": "Χρησιμοποιήστε κωδικό που αποστέλλεται στο email σας.", + "email-hint": "Οι κωδικοί αποστέλλονται στο {{ info }}", + "sms": "SMS", + "sms-description": "Χρησιμοποιήστε το κινητό σας για επαλήθευση. Θα σας αποστείλουμε έναν κωδικό μέσω SMS.", + "sms-hint": "Οι κωδικοί αποστέλλονται στο {{ info }}", + "totp": "Εφαρμογή επαλήθευσης", + "totp-description": "Χρησιμοποιήστε εφαρμογές όπως Google Authenticator, Authy ή Duo για την επαλήθευση.", + "totp-hint": "Η εφαρμογή επαλήθευσης έχει ρυθμιστεί για τον λογαριασμό σας", + "backup_code": "Εφεδρικός κωδικός", + "backup-code-description": "Οι εφεδρικοί κωδικοί επιτρέπουν σύνδεση όταν δεν έχετε πρόσβαση στο κινητό σας.", + "backup-code-hint": "{{ info }} μονο-χρησιμοποιούμενοι κωδικοί είναι ενεργοί αυτή τη στιγμή" + } + }, + "password-requirement": { + "at-least": "Τουλάχιστον:", + "character": "{ count, plural, =1 {1 χαρακτήρας} other {# χαρακτήρες} }", + "digit": "{ count, plural, =1 {1 ψηφίο} other {# ψηφία} }", + "incorrect-password-try-again": "Λανθασμένος κωδικός. Προσπαθήστε ξανά", + "lowercase-letter": "{ count, plural, =1 {1 πεζό γράμμα} other {# πεζά γράμματα} }", + "new-passwords-not-match": "Οι νέοι κωδικοί δεν ταιριάζουν", + "password-should-not-contain-spaces": "Ο κωδικός σας δεν πρέπει να περιέχει κενά", + "password-not-meet-requirements": "Ο κωδικός δεν πληροί τις απαιτήσεις", + "password-requirements": "Απαιτήσεις κωδικού", + "password-should-difference": "Ο νέος κωδικός πρέπει να διαφέρει από τον τρέχοντα", + "special-character": "{ count, plural, =1 {1 ειδικός χαρακτήρας} other {# ειδικοί χαρακτήρες} }", + "uppercase-letter": "{ count, plural, =1 {1 κεφαλαίο γράμμα} other {# κεφαλαία γράμματα} }", + "at-most": "Το πολύ:" + } }, "relation": { - "relations": "Σχέσεις", + "relations": "Συσχετίσεις", "direction": "Κατεύθυνση", + "clear-relation-type": "Καθαρισμός τύπου συσχέτισης", "search-direction": { "FROM": "Από", - "TO": "Σε" + "TO": "Προς" }, "direction-type": { "FROM": "από", - "TO": "σε" + "TO": "προς" }, - "from-relations": "Εξωτερικές σχέσεις", - "to-relations": "Εσωτερικές σχέσεις", - "selected-relations": "{ count, plural, =1 {1 relation} other {# relations} } επιλέχθηκαν", + "from-relations": "Εξερχόμενες συσχετίσεις", + "to-relations": "Εισερχόμενες συσχετίσεις", + "selected-relations": "{ count, plural, =1 {1 συσχέτιση} other {# συσχετίσεις} } επιλέχθηκαν", "type": "Τύπος", - "to-entity-type": "Στον τύπο οντότητας", - "to-entity-name": "Στο όνομα οντότητας", - "from-entity-type": "Από τύπο οντότητας", + "to-entity-type": "Προς τύπος οντότητας", + "to-entity-name": "Προς όνομα οντότητας", + "from-entity-type": "Από τύπος οντότητας", "from-entity-name": "Από όνομα οντότητας", - "to-entity": "Σε οντότητα", + "to-entity": "Προς οντότητα", "from-entity": "Από οντότητα", - "delete": "Διαγραφή σχέσης", - "relation-type": "Relation type", - "relation-type-required": "Απαιτείται τύπος σχέσης.", + "delete": "Διαγραφή συσχέτισης", + "relation-type": "Τύπος συσχέτισης", + "relation-type-required": "Ο τύπος συσχέτισης είναι υποχρεωτικός.", + "relation-type-max-length": "Ο τύπος συσχέτισης πρέπει να είναι μικρότερος από 256 χαρακτήρες", "any-relation-type": "Οποιοσδήποτε τύπος", - "add": "Προσθήκη σχέσης", - "edit": "Επεξεργασία σχέσης", - "delete-to-relation-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε τη σχέση με την οντότητα '{{entityName}}'?", + "add": "Προσθήκη συσχέτισης", + "edit": "Επεξεργασία συσχέτισης", + "delete-to-relation-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε τη συσχέτιση προς την οντότητα '{{entityName}}';", "delete-to-relation-text": "Προσοχή, μετά την επιβεβαίωση η οντότητα '{{entityName}}' δεν θα σχετίζεται με την τρέχουσα οντότητα.", - "delete-to-relations-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 relation} other {# relations} };", - "delete-to-relations-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες σχέσεις θα αφαιρεθούν και οι αντίστοιχες οντότητες δεν θα σχετίζονται με την τρέχουσα οντότητα.", - "delete-from-relation-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε σχέση από την οντότητα '{{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 relation} other {# relations} };", - "delete-from-relations-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες σχέσεις θα αφαιρεθούν και η τρέχουσα οντότητα δεν θα σχετίζεται με τις αντίστοιχες οντότητες.", - "remove-relation-filter": "Αφαίρεση φίλτρου σχέσης", - "add-relation-filter": "Προσθήκη φίλτρου σχέσης", - "any-relation": "Οποιαδήποτε σχέση", - "relation-filters": "Φίλτρα σχέσεων", - "additional-info": "Συμπληρωματικές πληροφορίες (JSON)", - "invalid-additional-info": "Δεν είναι δυνατή η ανάλυση των πρόσθετων πληροφοριών json." + "delete-from-relations-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 συσχέτιση} other {# συσχετίσεις} };", + "delete-from-relations-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες συσχετίσεις θα διαγραφούν και η τρέχουσα οντότητα δεν θα σχετίζεται με τις αντίστοιχες οντότητες.", + "remove-relation-filter": "Αφαίρεση φίλτρου συσχέτισης", + "remove-filter": "Αφαίρεση φίλτρου", + "add-relation-filter": "Προσθήκη φίλτρου συσχέτισης", + "any-relation": "Οποιαδήποτε συσχέτιση", + "relation-filters": "Φίλτρα συσχετίσεων", + "additional-info": "Επιπλέον πληροφορίες (JSON)", + "invalid-additional-info": "Δεν ήταν δυνατή η ανάλυση του JSON των επιπλέον πληροφοριών.", + "no-relations-text": "Δεν βρέθηκαν συσχετίσεις", + "not": "Όχι" + }, + "resource": { + "add": "Προσθήκη πόρου", + "all-types": "Όλα", + "copyId": "Αντιγραφή αναγνωριστικού πόρου", + "delete": "Διαγραφή πόρου", + "delete-resource-text": "Προσοχή, μετά την επιβεβαίωση ο πόρος δεν θα είναι ανακτήσιμος.", + "delete-resource-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε τον πόρο '{{resourceTitle}}';", + "delete-resources-action-title": "Διαγραφή { count, plural, =1 {1 πόρου} other {# πόρων} }", + "delete-resources-text": "Σημειώστε ότι οι επιλεγμένοι πόροι, ακόμα και αν χρησιμοποιούνται σε προφίλ συσκευών, θα διαγραφούν.", + "delete-resources-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 πόρο} other {# πόρους} };", + "download": "Λήψη πόρου", + "drop-file": "Σύρετε ένα αρχείο πόρου ή κάντε κλικ για να επιλέξετε ένα αρχείο για μεταφόρτωση.", + "drop-resource-file-or": "Σύρετε ένα αρχείο πόρου ή", + "empty": "Ο πόρος είναι κενός", + "file-name": "Όνομα αρχείου", + "idCopiedMessage": "Το αναγνωριστικό του πόρου αντιγράφηκε στο πρόχειρο", + "no-resource-matching": "Δεν βρέθηκαν πόροι που να αντιστοιχούν στο '{{widgetsBundle}}'.", + "no-resource-text": "Δεν βρέθηκαν πόροι", + "open-widgets-bundle": "Άνοιγμα πακέτου γραφικών", + "resource": "Πόρος", + "resource-file": "Αρχείο πόρου", + "resource-files": "Αρχεία πόρων", + "resource-library-details": "Λεπτομέρειες πόρου", + "resource-type": "Τύπος πόρου", + "resources-library": "Βιβλιοθήκη πόρων", + "search": "Αναζήτηση πόρων", + "selected-resources": "{ count, plural, =1 {1 πόρος} other {# πόροι} } επιλέχθηκαν", + "system": "Σύστημα", + "title": "Τίτλος", + "title-required": "Ο τίτλος είναι υποχρεωτικός.", + "title-max-length": "Ο τίτλος πρέπει να είναι μικρότερος από 256 χαρακτήρες", + "type": { + "jks": "JKS", + "js-module": "JS module", + "lwm2m-model": "Μοντέλο LWM2M", + "pkcs-12": "PKCS #12" + }, + "resource-sub-type": "Υποτύπος", + "sub-type": { + "image": "εικόνα", + "scada-symbol": "Σύμβολο SCADA", + "extension": "Επέκταση", + "module": "Μονάδα" + } + }, + "javascript": { + "add": "Προσθήκη πόρου JavaScript", + "delete": "Διαγραφή πόρου JavaScript", + "delete-javascript-resource-text": "Προσοχή, μετά την επιβεβαίωση ο πόρος JavaScript δεν θα είναι ανακτήσιμος.", + "delete-javascript-resource-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε τον πόρο JavaScript '{{resourceTitle}}';", + "delete-javascript-resources-action-title": "Διαγραφή JavaScript { count, plural, =1 {1 πόρου} other {# πόρων} }", + "delete-javascript-resources-text": "Σημειώστε ότι οι επιλεγμένοι πόροι JavaScript, ακόμα και αν χρησιμοποιούνται σε συναρτήσεις JavaScript, θα διαγραφούν.", + "delete-javascript-resources-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε JavaScript { count, plural, =1 {1 πόρο} other {# πόρους} };", + "delete-javascript-resource-in-use-text": "Εάν εξακολουθείτε να θέλετε να διαγράψετε τον πόρο JavaScript, κάντε κλικ στο κουμπί Διαγραφή ούτως ή άλλως.", + "download": "Λήψη πόρου JavaScript", + "upload-from-file": "Μεταφόρτωση JavaScript από αρχείο", + "resource-file": "Αρχείο πόρου JavaScript", + "drop-file": "Σύρετε ένα αρχείο JavaScript ή κάντε κλικ για να επιλέξετε ένα αρχείο για μεταφόρτωση.", + "drop-resource-file-or": "Σύρετε ένα αρχείο JavaScript ή", + "javascript-library": "Βιβλιοθήκη JavaScript", + "javascript-type": "Τύπος JavaScript", + "javascript-resource-details": "Λεπτομέρειες πόρου JavaScript", + "javascript-resource-is-in-use": "Ο πόρος JavaScript χρησιμοποιείται από άλλες οντότητες", + "javascript-resources-are-in-use": "Οι πόροι JavaScript χρησιμοποιούνται από άλλες οντότητες", + "javascript-resource-is-in-use-text": "Ο πόρος JavaScript '{{title}}' δεν διαγράφηκε επειδή χρησιμοποιείται από τις ακόλουθες οντότητες:", + "javascript-resources-are-in-use-text": "Δεν διαγράφηκαν όλοι οι πόροι JavaScript επειδή χρησιμοποιούνται από άλλες οντότητες.
Μπορείτε να δείτε τις αναφορές κάνοντας κλικ στο κουμπί Αναφορές στη σειρά του αντίστοιχου πόρου.
Αν θέλετε να τους διαγράψετε, επιλέξτε τους στον παρακάτω πίνακα και κάντε κλικ στο κουμπί Διαγραφή επιλεγμένων.", + "search": "Αναζήτηση πόρων JavaScript", + "selected-javascript-resources": "{ count, plural, =1 {1 πόρος JavaScript} other {# πόροι JavaScript} } επιλέχθηκαν", + "no-javascript-resource-text": "Δεν βρέθηκαν πόροι JavaScript", + "all-types": "Όλα", + "module-script": "Σενάριο μονάδας" + }, + "rpc": { + "error": { + "target-device-is-not-set": "Η συσκευή στόχος δεν έχει οριστεί!", + "invalid-target-entity": "Οι εντολές RPC δεν υποστηρίζονται από την οντότητα {{entityType}}.", + "failed-to-resolve-target-device": "Αποτυχία εύρεσης της συσκευής στόχου!", + "request-timeout": "Λήξη χρονικού ορίου αιτήματος", + "rpc-http-error": "Σφάλμα: {{status}} - {{statusText}}" + } }, "rulechain": { - "rulechain": "Αλυσίδα Κανόνων", - "rulechains": "Κανόνες", + "rulechain": "Αλυσίδα κανόνων", + "rulechain-events": "Συμβάντα αλυσίδας κανόνων", + "rulechains": "Αλυσίδες κανόνων", "root": "Ρίζα", - "delete": "Διαγραφή Αλυσίδας Κανόνων", + "delete": "Διαγραφή αλυσίδας κανόνων", "name": "Όνομα", - "name-required": "Απαιτείται όνομα.", + "name-required": "Το όνομα είναι υποχρεωτικό.", + "name-max-length": "Το όνομα πρέπει να είναι μικρότερο από 256 χαρακτήρες", "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 rule chain} other {# rule chains} };", - "delete-rulechains-action-title": "Διαγραφή { count, plural, =1 {1 rule chain} other {# rule chains} }", - "delete-rulechains-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες αλυσίδες κανόνων θα καταργηθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "add-rulechain-text": "Προσθήκη νέας Αλυσίδας Κανόνων", - "no-rulechains-text": "Δεν βρέθηκαν Αλυσίδες Κανόνων", - "rulechain-details": "Λεπτομέρειες Αλυσίδας Κανόνων", - "details": "Λεπομέρειες", - "events": "Γεγονότα", + "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": "Αντιγραφή ταυτότητας Αλυσίδας Κανόνων", - "idCopiedMessage": "Η ταυτότητα της Αλυσίδας Κανόνων έχει αντιγραφεί στο πρόχειρο", - "select-rulechain": "Επιλογή Αλυσίδας Κανόνων", - "no-rulechains-matching": "Δεν βρέθηκαν Αλυσίδες Κανόνων που να ταιριάζουν '{{entity}}'.", - "rulechain-required": "Απαιτείται Αλυσίδα Κανόνων", + "import": "Εισαγωγή αλυσίδας κανόνων", + "export": "Εξαγωγή αλυσίδας κανόνων", + "export-failed-error": "Αδυναμία εξαγωγής αλυσίδας κανόνων: {{error}}", + "create-new-rulechain": "Δημιουργία νέας αλυσίδας κανόνων", + "rulechain-file": "Αρχείο αλυσίδας κανόνων", + "invalid-rulechain-file-error": "Αδυναμία εισαγωγής αλυσίδας κανόνων: Μη έγκυρη δομή δεδομένων.", + "copyId": "Αντιγραφή αναγνωριστικού αλυσίδας κανόνων", + "idCopiedMessage": "Το αναγνωριστικό της αλυσίδας κανόνων αντιγράφηκε στο πρόχειρο", + "select-rulechain": "Επιλέξτε αλυσίδα κανόνων", + "no-rulechains-matching": "Δεν βρέθηκαν αλυσίδες κανόνων που να ταιριάζουν με '{{entity}}'.", + "rulechain-required": "Η αλυσίδα κανόνων είναι υποχρεωτική", "management": "Διαχείριση κανόνων", - "debug-mode": "Λειτουργία Εκσφαλμάτωσης" + "debug-mode": "Λειτουργία αποσφαλμάτωσης", + "search": "Αναζήτηση αλυσίδων κανόνων", + "selected-rulechains": "{ count, plural, =1 {1 αλυσίδα κανόνων} other {# αλυσίδες κανόνων} } επιλέχθηκαν", + "open-rulechain": "Άνοιγμα αλυσίδας κανόνων", + "edge-template-root": "Ρίζα προτύπου edge", + "assign-to-edge": "Ανάθεση σε edge", + "edge-rulechain": "Αλυσίδα κανόνων edge", + "unassign-rulechain-from-edge-text": "Μετά την επιβεβαίωση η αλυσίδα κανόνων θα αποανατεθεί και δεν θα είναι προσβάσιμη από το edge.", + "unassign-rulechains-from-edge-title": "Είστε σίγουροι ότι θέλετε να αποαναθέσετε { count, plural, =1 {1 αλυσίδα κανόνων} other {# αλυσίδες κανόνων} };", + "unassign-rulechains-from-edge-text": "Μετά την επιβεβαίωση όλες οι επιλεγμένες αλυσίδες κανόνων θα αποανατεθούν και δεν θα είναι προσβάσιμες από το edge.", + "assign-rulechain-to-edge-title": "Ανάθεση αλυσίδας(ών) κανόνων στο Edge", + "assign-rulechain-to-edge-text": "Επιλέξτε τις αλυσίδες κανόνων που θα ανατεθούν στο edge", + "set-edge-template-root-rulechain": "Ορισμός ως ρίζα προτύπου edge", + "set-edge-template-root-rulechain-title": "Είστε σίγουροι ότι θέλετε να ορίσετε την αλυσίδα κανόνων '{{ruleChainName}}' ως ρίζα προτύπου edge;", + "set-edge-template-root-rulechain-text": "Μετά την επιβεβαίωση, η αλυσίδα κανόνων θα γίνει ρίζα προτύπου edge και θα είναι η βασική αλυσίδα για νέα edge.", + "invalid-rulechain-type-error": "Αδυναμία εισαγωγής: Μη έγκυρος τύπος αλυσίδας κανόνων. Αναμενόμενος τύπος: {{expectedRuleChainType}}.", + "set-auto-assign-to-edge": "Ανάθεση σε edge κατά τη δημιουργία", + "set-auto-assign-to-edge-title": "Είστε σίγουροι ότι θέλετε να αναθέσετε την αλυσίδα '{{ruleChainName}}' σε edge κατά τη δημιουργία;", + "set-auto-assign-to-edge-text": "Μετά την επιβεβαίωση, η αλυσίδα θα ανατίθεται αυτόματα σε νέο edge.", + "unset-auto-assign-to-edge": "Μη αυτόματη ανάθεση σε edge κατά τη δημιουργία", + "unset-auto-assign-to-edge-title": "Είστε σίγουροι ότι δεν θέλετε την αυτόματη ανάθεση της αλυσίδας '{{ruleChainName}}';", + "unset-auto-assign-to-edge-text": "Μετά την επιβεβαίωση η αλυσίδα δεν θα ανατίθεται αυτόματα σε νέο edge.", + "unassign-rulechain-title": "Είστε σίγουροι ότι θέλετε να αποαναθέσετε την αλυσίδα '{{ruleChainName}}';", + "unassign-rulechains": "Αποανάθεση αλυσίδων κανόνων" }, "rulenode": { + "rule-node-events": "Συμβάντα κόμβου κανόνα", "details": "Λεπτομέρειες", - "events": "Γεγονότα", + "events": "Συμβάντα", "search": "Αναζήτηση κόμβων", "open-node-library": "Άνοιγμα βιβλιοθήκης κόμβων", - "add": "Προσθήκη κανόνα κόμβου", + "close-node-library": "Κλείσιμο βιβλιοθήκης κόμβων", + "add": "Προσθήκη κόμβου κανόνα", "name": "Όνομα", - "name-required": "Απαιτείται όνομα.", + "name-required": "Το όνομα είναι υποχρεωτικό.", + "name-max-length": "Το όνομα πρέπει να είναι μικρότερο από 256 χαρακτήρες", "type": "Τύπος", - "delete": "Διαγραφή κανόνα κόμβου", + "rule-node-description": "Περιγραφή κόμβου κανόνα", + "delete": "Διαγραφή κόμβου κανόνα", "select-all-objects": "Επιλογή όλων των κόμβων και συνδέσεων", "deselect-all-objects": "Αποεπιλογή όλων των κόμβων και συνδέσεων", "delete-selected-objects": "Διαγραφή επιλεγμένων κόμβων και συνδέσεων", - "delete-selected": "Delete selected", - "select-all": "Select all", - "copy-selected": "Επιλογή αντιγραφής", + "delete-selected": "Διαγραφή επιλεγμένων", + "create-nested-rulechain": "Δημιουργία ένθετης αλυσίδας κανόνων", + "select-all": "Επιλογή όλων", + "copy-selected": "Αντιγραφή επιλεγμένων", "deselect-all": "Αποεπιλογή όλων", - "rulenode-details": "Λεπτομέρειες κανόνα κόμβου", - "debug-mode": "Λειτουργία εντοπισμού σφαλμάτων", - "configuration": "Διαμόρφωση", + "rulenode-details": "Λεπτομέρειες κόμβου κανόνα", + "debug-mode": "Λειτουργία αποσφαλμάτωσης", + "singleton": "Μοναδικός", + "configuration": "Ρύθμιση", "link": "Σύνδεσμος", - "link-details": "Λεπτομέρειες συνδέσμου κανόνα κόμβου", + "link-details": "Λεπτομέρειες συνδέσμου κόμβου κανόνα", "add-link": "Προσθήκη συνδέσμου", "link-label": "Ετικέτα συνδέσμου", - "link-label-required": "Απαιτείται ετικετα συνδέσμου.", - "custom-link-label": "Ετικέτα προσαρμοσμένου συνδέσμου", - "custom-link-label-required": "Απαιτείται ετικέτα προσαρμοσμένου συνδέσμου.", + "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": "Δημιουργήστε μια νέα!", + "no-link-label-matching": "'{{label}}' δεν βρέθηκε.", + "create-new-link-label": "Δημιουργία νέας!", "type-filter": "Φίλτρο", - "type-filter-details": "Φιλτράρετε εισερχόμενα μηνύματα με διαμορφωμένες συνθήκες", + "type-filter-details": "Φιλτράρει εισερχόμενα μηνύματα με καθορισμένες συνθήκες", "type-enrichment": "Εμπλουτισμός", - "type-enrichment-details": "Προσθέστε επιπλέον πληροφορίες στα Μεταδεδομένα του μηνύματος", - "type-transformation": "Μεταμόρφωση", - "type-transformation-details": "Αλλαγή ωφέλιμου φορτίου και μεταδεδομένων μηνύματος", + "type-enrichment-details": "Προσθέτει επιπλέον πληροφορίες στα μεταδεδομένα του μηνύματος", + "type-transformation": "Μετασχηματισμός", + "type-transformation-details": "Αλλάζει το περιεχόμενο και τα μεταδεδομένα του μηνύματος", "type-action": "Ενέργεια", - "type-action-details": "Εκτέλεση ειδικής ενέργειας", - "type-analytics": "Analytics", - "type-analytics-details": "Εκτελέστε ανάλυση δεδομένων που διαβιβάζονται με ροή ή συνεχίζονται", + "type-action-details": "Εκτελεί ειδικές ενέργειες", "type-external": "Εξωτερικός", - "type-external-details": "Αλληλεπίδραση με εξωτερικό σύστημα", + "type-external-details": "Αλληλεπιδρά με εξωτερικά συστήματα", "type-rule-chain": "Αλυσίδα κανόνων", - "type-rule-chain-details": "Προωθεί τα εισερχόμενα μηνύματα σε συγκεκριμένη αλυσίδα κανόνων", - "type-input": "Εισαγωγήγή", - "type-input-details": "Λογική εισαγωγή της αλυσίδας κανόνων, προωθεί τα εισερχόμενα μηνύματα στον επόμενο σχετικό κανόνα κόμβου", + "type-rule-chain-details": "Προωθεί μηνύματα σε καθορισμένη αλυσίδα κανόνων", + "type-flow": "Ροή", + "type-flow-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": "Λειτουργία σεναρίου δοκιμής", + "type-unknown-details": "Μη αναγνωρισμένος κόμβος κανόνα", + "directive-is-not-loaded": "Η καθορισμένη οδηγία ρύθμισης '{{directiveName}}' δεν είναι διαθέσιμη.", + "ui-resources-load-error": "Αποτυχία φόρτωσης πόρων διεπαφής ρύθμισης.", + "invalid-target-rulechain": "Αδυναμία εύρεσης της στοχευμένης αλυσίδας κανόνων!", + "test-script-function": "Δοκιμή συνάρτησης script", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", "message": "Μήνυμα", "message-type": "Τύπος μηνύματος", - "select-message-type": "Επιλογή τύπου μηνύματος", - "message-type-required": "Απαιτείται τύπος μηνύματος", + "select-message-type": "Επιλέξτε τύπο μηνύματος", + "message-type-required": "Ο τύπος μηνύματος είναι υποχρεωτικός", "metadata": "Μεταδεδομένα", - "metadata-required": "Οι καταχωρίσεις μεταδεδομένων δεν μπορούν να είναι κενές.", - "output": "Απόδοση", - "test": "Τεστ", - "help": "Βοήθεια" - }, - "role": { - "role": "Ρόλος", - "roles": "Ρόλοι", - "management": "Διαχείριση ρόλων", - "view-roles": "Προβολή ρόλων", - "no-roles-matching": "Δεν βρέθηκαν ρόλοι που να ταιριάζουν '{{entity}}'.", - "role-list": "Λίστα ρόλων", - "add": "Προσθήκη ρόλου", - "view": "Προβολή ρόλου", - "no-roles-text": "Δεν βρέθηκαν ρόλοι", - "role-details": "Λεπτομέρειες ρόλου", - "add-role-text": "Προσθήκη νέου ρόλου", - "delete": "Διαγραφή ρόλου", - "delete-roles": "Διαγραφή ρόλων", - "delete-role-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε το ρόλο '{{roleName}}';", - "delete-role-text": "Προσοχή, μετά την επιβεβαίωση ο ρόλος και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-roles-title": "Είστε σίγουροι ότι θέλετε να παίξετε { count, plural, =1 {1 role} other {# roles} };", - "delete-roles-action-title": "Διαγραφή { count, plural, =1 {1 role} other {# roles} }", - "delete-roles-text": "Προσοχή, μετά την επιβεβαίωση όλοι οι επιλεγμένοι ρόλοι θα καταργηθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "role-type": "΄Τύπος ρόλου", - "role-type-required": "Απαιτείται τύπος ρόλου.", - "select-role-type": "Επιλογή τύπου ρόλου", - "enter-role-type": "Εισαγωγή τύπου ρόλου", - "any-role": "Οποιοσδήποτε ρόλος", - "no-role-types-matching": "Δεν βρέθηκαν τύποι ρόλων που να ταιριάζουν '{{entitySubtype}}'.", - "role-type-list-empty": "Δεν έγινε επιλογή τύπου ρόλου.", - "role-types": "Τύποι ρόλου", - "name": "Όνομα", - "name-required": "Απαιτείται όνομα.", - "description": "Περιγραφή", - "events": "Γεγονότα", - "details": "Λεπτομέρειες", - "copyId": "Αντιγραφή ταυτότητας ρόλου", - "idCopiedMessage": "Η ταυτότητα ρόλου έχει αντιγραφεί στο πρόχειρο", - "permissions": "Άδειες", - "role-required": "Απαιτείται ρόλος", - "display-type": { - "GENERIC": "Γενικός", - "GROUP": "Ομάδα" - } + "metadata-required": "Οι καταχωρήσεις μεταδεδομένων δεν μπορούν να είναι κενές.", + "output": "Έξοδος", + "test": "Δοκιμή", + "help": "Βοήθεια", + "reset-debug-settings": "Επαναφορά ρυθμίσεων αποσφαλμάτωσης σε όλους τους κόμβους", + "test-with-this-message": "{{test}} με αυτό το μήνυμα", + "queue-hint": "Επιλέξτε ουρά για προώθηση μηνυμάτων σε άλλη ουρά. Η 'Κύρια' ουρά χρησιμοποιείται από προεπιλογή.", + "queue-singleton-hint": "Επιλέξτε ουρά για προώθηση μηνυμάτων σε περιβάλλοντα πολλαπλών παρουσιών. Η 'Κύρια' ουρά χρησιμοποιείται από προεπιλογή." }, - "group-permission": { - "user-group-roles": "Ρόλοι ομάδας χρηστών", - "entity-group-permissions": "Άδειες ομάδας Οντοτήτων", - "role-type": "Τύπος ρόλου", - "role-name": "Όνομα ρόλου", - "group-type": "Τύπος ομάδας", - "group-name": "Όνομα ομάδας", - "group-owner": "Κάτοχος ομάδας", - "user-group-name": "Όνομα χρήστη ομάδας", - "user-group-owner": "Κάτοχος χρήστη ομάδας", - "edit": "Επεξεργασία Αδειών", - "delete": "Διαγραφή αδειών", - "selected-group-permissions": "{ count, plural, =1 {1 group permission} other {# group permissions} } επιλέχθηκαν", - "delete-group-permission-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε την άδεια ομάδας '{{roleName}}';", - "delete-group-permission-text": "Προσοχή, μετά την επιβεβαίωση η άδεια ομάδας και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-group-permission": "Διαγραφή άδειας ομάδας", - "delete-group-permissions-title": "Έίστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 group permission} other {# group permission} };", - "delete-group-permissions-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες άδειες ομάδας θα καταργηθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-group-permissions": "Διαγραφή αδειών ομάδας", - "add-group-permission": "Προσθήκη ομαδικής άδειας", - "edit-group-permission": "Επεξεργασία άδειας ομάδας", - "entity-group": "Ομάδα οντοτήτων", - "user-group": "Χρήστης ομάδας", - "no-owners-matching": "Δεν βρέθηκαν κάτοχοι που να ταιριάζουν '{{owner}}'.", - "target-owner-required": " Απαιτείται κάτοχος της ομάδας οντότητας.", - "target-user-group-owner-required": "Απαιτείται κάτοχος χρήστη ομάδας." - }, - "permission": { - "permissions-required": "Τουλάχιστον μία εγγραφή άδειας πρέπει να οριστεί.", - "remove-permission": "Καταργήστε την καταχώριση δικαιωμάτων", - "add-permission": "Προσθήκη καταχώρησης άδειας", - "resource": { - "resource": "Πηγή", - "select-resource": "Επιλογή πηγής", - "resource-required": "Απαιτείται πηγή", - "no-resources-matching": "Δεν βρέθηκαν πηγές που να ταιριάζουν '{{resource}}'.", - "display-type": { - "ALL": "Όλες", - "PROFILE": "Προφίλ", - "ADMIN_SETTINGS": "Ρυθμίσεις Διαχειριστή", - "ALARM": "Alarm", - "DEVICE": "Συσκευή", - "ASSET": "Asset", - "CUSTOMER": "Πελάτης", - "DASHBOARD": "Dashboard", - "ENTITY_VIEW": "Προβολή οντοτήτων", - "TENANT": "Μισθωτής", - "RULE_CHAIN": "Αλυσίδα Κανόνα", - "USER": "Χρήστης", - "WIDGETS_BUNDLE": "Δέσμη Widgets", - "WIDGET_TYPE": "Τύπος Widget", - "CONVERTER": "Μετατροπέας", - "INTEGRATION": "Ενσωμάτωση", - "SCHEDULER_EVENT": "Πρόγραμμα εκδηλώσεων", - "BLOB_ENTITY": "Δέσμη Οντότητας", - "CUSTOMER_GROUP": "Ομάδα Πελατών", - "DEVICE_GROUP": "Ομάδα Συσκευών", - "ASSET_GROUP": "Ομάδες Asset", - "USER_GROUP": "Ομάδα Χρηστών", - "ENTITY_VIEW_GROUP": "Ομάδα Οντοτήτων", - "DASHBOARD_GROUP": "Ομάδα Dashboard", - "ROLE": "Ρόλος", - "GROUP_PERMISSION": "Ομαδική Άδεια", - "WHITE_LABELING": "Εμφάνιση", - "AUDIT_LOG": "Αρχείο Ελέγχου" - } + "rule-node-config": { + "id": "Id", + "additional-info": "Πρόσθετες πληροφορίες", + "advanced-settings": "Προηγμένες ρυθμίσεις", + "create-entity-if-not-exists": "Δημιουργία οντότητας αν δεν υπάρχει", + "create-entity-if-not-exists-hint": "Αν είναι ενεργοποιημένο, θα δημιουργηθεί νέα οντότητα με τα καθορισμένα χαρακτηριστικά, εκτός αν υπάρχει ήδη. Οι υπάρχουσες οντότητες θα χρησιμοποιηθούν ως έχουν για τη σχέση.", + "select-device-connectivity-event": "Επιλέξτε γεγονός συνδεσιμότητας συσκευής", + "entity-name-pattern": "Μοτίβο ονόματος", + "device-name-pattern": "Όνομα συσκευής", + "asset-name-pattern": "Όνομα περιουσιακού στοιχείου", + "entity-view-name-pattern": "Όνομα προβολής οντότητας", + "customer-title-pattern": "Τίτλος πελάτη", + "dashboard-name-pattern": "Τίτλος πίνακα ελέγχου", + "user-name-pattern": "Email χρήστη", + "edge-name-pattern": "Όνομα Edge", + "entity-name-pattern-required": "Απαιτείται μοτίβο ονόματος", + "entity-name-pattern-hint": "Το πεδίο μοτίβου ονόματος υποστηρίζει templatization. Χρησιμοποιήστε $[messageKey] για να εξάγετε τιμή από το μήνυμα και ${metadataKey} από τα μεταδεδομένα.", + "copy-message-type": "Αντιγραφή τύπου μηνύματος", + "entity-type-pattern": "Μοτίβο τύπου", + "entity-type-pattern-required": "Απαιτείται μοτίβο τύπου", + "message-type-value": "Τιμή τύπου μηνύματος", + "message-type-value-required": "Απαιτείται τιμή τύπου μηνύματος", + "message-type-value-max-length": "Η τιμή τύπου μηνύματος πρέπει να είναι μικρότερη από 256 χαρακτήρες", + "output-message-type": "Τύπος μηνύματος εξόδου", + "entity-cache-expiration": "Χρόνος λήξης προσωρινής αποθήκευσης οντοτήτων (δευτ)", + "entity-cache-expiration-hint": "Καθορίζει το μέγιστο χρονικό διάστημα αποθήκευσης των οντοτήτων. Η τιμή 0 σημαίνει ότι δεν λήγουν ποτέ.", + "entity-cache-expiration-required": "Απαιτείται χρόνος λήξης προσωρινής αποθήκευσης οντοτήτων.", + "entity-cache-expiration-range": "Ο χρόνος πρέπει να είναι μεγαλύτερος ή ίσος με 0.", + "customer-name-pattern": "Τίτλος πελάτη", + "customer-name-pattern-required": "Ο τίτλος πελάτη είναι υποχρεωτικός", + "customer-name-pattern-hint": "Χρησιμοποιήστε $[messageKey] και ${metadataKey} για τιμές από το μήνυμα ή μεταδεδομένα.", + "create-customer-if-not-exists": "Δημιουργία νέου πελάτη αν δεν υπάρχει", + "unassign-from-customer": "Αποσυσχέτιση από συγκεκριμένο πελάτη αν το αρχικό είναι πίνακας ελέγχου", + "unassign-from-customer-tooltip": "Μόνο πίνακες ελέγχου μπορούν να συσχετιστούν με πολλούς πελάτες. Αν ο αποστολέας είναι πίνακας, καθορίστε τίτλο πελάτη για αποσυσχέτιση.", + "customer-cache-expiration": "Χρόνος λήξης προσωρινής αποθήκευσης πελατών (δευτ)", + "customer-cache-expiration-hint": "Ορίζει το μέγιστο χρονικό διάστημα αποθήκευσης πελατών. 0 σημαίνει χωρίς λήξη.", + "customer-cache-expiration-required": "Απαιτείται χρόνος λήξης προσωρινής αποθήκευσης πελατών.", + "customer-cache-expiration-range": "Ο χρόνος πρέπει να είναι μεγαλύτερος ή ίσος με 0.", + "interval-start": "Έναρξη διαστήματος", + "interval-end": "Τέλος διαστήματος", + "time-unit": "Μονάδα χρόνου", + "fetch-mode": "Λειτουργία ανάκτησης", + "order-by-timestamp": "Ταξινόμηση κατά χρονική σήμανση", + "limit": "Όριο", + "limit-hint": "Ελάχιστο 2, μέγιστο 1000. Για μία καταχώρηση, επιλέξτε 'Πρώτο' ή 'Τελευταίο'.", + "limit-required": "Απαιτείται όριο.", + "limit-range": "Το όριο πρέπει να είναι μεταξύ 2 και 1000.", + "time-unit-milliseconds": "Χιλιοστά του δευτερολέπτου", + "time-unit-seconds": "Δευτερόλεπτα", + "time-unit-minutes": "Λεπτά", + "time-unit-hours": "Ώρες", + "time-unit-days": "Ημέρες", + "time-value-range": "Επιτρεπτό εύρος 1 έως 2147483647.", + "start-interval-value-required": "Απαιτείται η έναρξη διαστήματος.", + "end-interval-value-required": "Απαιτείται το τέλος διαστήματος.", + "filter": "Φίλτρο", + "switch": "Εναλλαγή", + "math-templatization-tooltip": "Υποστηρίζει templatization με $[messageKey] και ${metadataKey}.", + "add-message-type": "Προσθήκη τύπου μηνύματος", + "select-message-types-required": "Απαιτείται τουλάχιστον ένας τύπος μηνύματος.", + "select-message-types": "Επιλογή τύπων μηνύματος", + "no-message-types-found": "Δεν βρέθηκαν τύποι μηνυμάτων", + "no-message-type-matching": "'{{messageType}}' δεν βρέθηκε.", + "create-new-message-type": "Δημιουργία νέου.", + "message-types-required": "Απαιτούνται τύποι μηνυμάτων.", + "client-attributes": "Χαρακτηριστικά πελάτη", + "shared-attributes": "Κοινά χαρακτηριστικά", + "server-attributes": "Χαρακτηριστικά διακομιστή", + "attributes-keys": "Κλειδιά χαρακτηριστικών", + "attributes-keys-required": "Απαιτούνται κλειδιά χαρακτηριστικών", + "attributes-scope": "Πεδίο χαρακτηριστικών", + "attributes-scope-value": "Τιμή πεδίου χαρακτηριστικών", + "attributes-scope-value-copy": "Αντιγραφή τιμής πεδίου", + "attributes-scope-hint": "Χρησιμοποιήστε το μεταδεδομένο 'scope' για δυναμική ρύθμιση. Εάν υπάρχει, παρακάμπτει τη ρύθμιση.", + "notify-device": "Εξαναγκασμένη ειδοποίηση συσκευής", + "send-attributes-updated-notification": "Αποστολή ειδοποίησης ενημέρωσης χαρακτηριστικών", + "send-attributes-updated-notification-hint": "Στέλνει ειδοποίηση ενημέρωσης ως ξεχωριστό μήνυμα στην ουρά.", + "send-attributes-deleted-notification": "Αποστολή ειδοποίησης διαγραφής χαρακτηριστικών", + "send-attributes-deleted-notification-hint": "Στέλνει ειδοποίηση διαγραφής ως ξεχωριστό μήνυμα στην ουρά.", + "update-attributes-only-on-value-change": "Αποθήκευση μόνο σε αλλαγή τιμής", + "update-attributes-only-on-value-change-hint": "Ενημερώνει σε κάθε μήνυμα ανεξαρτήτως αλλαγής. Μειώνει απόδοση.", + "update-attributes-only-on-value-change-hint-enabled": "Ενημερώνει μόνο αν αλλάξει τιμή. Διαφορετικά, δεν ενημερώνεται ούτε timestamp ούτε αποστέλλεται ειδοποίηση.", + "fetch-credentials-to-metadata": "Ανάκτηση διαπιστευτηρίων στα μεταδεδομένα", + "notify-device-on-update-hint": "Αν είναι ενεργό, ειδοποιεί τη συσκευή για ενημέρωση κοινών χαρακτηριστικών. Αλλιώς, ελέγχεται από το metadata 'notifyDevice'.", + "notify-device-on-delete-hint": "Αν είναι ενεργό, ειδοποιεί τη συσκευή για διαγραφή κοινών χαρακτηριστικών. Αλλιώς, ελέγχεται από το metadata 'notifyDevice'.", + "latest-timeseries": "Τελευταία δεδομένα χρονικής σειράς", + "timeseries-keys": "Κλειδιά χρονικής σειράς", + "timeseries-keys-required": "Απαιτείται τουλάχιστον ένα κλειδί χρονικής σειράς.", + "add-timeseries-key": "Προσθήκη κλειδιού χρονικής σειράς", + "add-message-field": "Προσθήκη πεδίου μηνύματος", + "relation-search-parameters": "Παράμετροι αναζήτησης σχέσεων", + "relation-parameters": "Παράμετροι σχέσης", + "add-metadata-field": "Προσθήκη πεδίου μεταδεδομένων", + "data-keys": "Ονόματα πεδίων μηνύματος", + "copy-from": "Αντιγραφή από", + "data-to-metadata": "Δεδομένα σε μεταδεδομένα", + "metadata-to-data": "Μεταδεδομένα σε δεδομένα", + "use-regular-expression-hint": "Χρησιμοποιήστε κανονική έκφραση για αντιγραφή κλειδιών με βάση μοτίβα.\n\nΣυμβουλές:\nΠατήστε 'Enter' για να ολοκληρώσετε την εισαγωγή ονόματος πεδίου.\nΠατήστε 'Backspace' για διαγραφή.\nΥποστηρίζονται πολλαπλά ονόματα.", + "interval": "Διάστημα", + "interval-required": "Απαιτείται διάστημα", + "interval-hint": "Χρονικό διάστημα αποφυγής διπλοτύπων σε δευτερόλεπτα.", + "interval-min-error": "Ελάχιστη επιτρεπτή τιμή είναι 1", + "max-pending-msgs": "Μέγιστος αριθμός εκκρεμών μηνυμάτων", + "max-pending-msgs-hint": "Μέγιστος αριθμός μηνυμάτων στη μνήμη για κάθε μοναδικό id αποφυγής διπλοτύπων.", + "max-pending-msgs-required": "Απαιτείται μέγιστος αριθμός εκκρεμών μηνυμάτων", + "max-pending-msgs-max-error": "Μέγιστη επιτρεπτή τιμή είναι 1000", + "max-pending-msgs-min-error": "Ελάχιστη επιτρεπτή τιμή είναι 1", + "max-retries": "Μέγιστες επαναλήψεις", + "max-retries-required": "Απαιτούνται μέγιστες επαναλήψεις", + "max-retries-hint": "Μέγιστες προσπάθειες για ώθηση μηνυμάτων στη σειρά. Καθυστέρηση 10 δευτερολέπτων ανά επανάληψη.", + "max-retries-max-error": "Μέγιστη επιτρεπτή τιμή είναι 100", + "max-retries-min-error": "Ελάχιστη επιτρεπτή τιμή είναι 0", + "strategy": "Στρατηγική", + "strategy-required": "Απαιτείται στρατηγική", + "strategy-all-hint": "Επιστρέφει όλα τα μηνύματα της περιόδου αποφυγής διπλοτύπων ως πίνακα JSON. Κάθε στοιχείο περιλαμβάνει msg και metadata.", + "strategy-first-hint": "Επιστρέφει το πρώτο μήνυμα της περιόδου.", + "strategy-last-hint": "Επιστρέφει το τελευταίο μήνυμα της περιόδου.", + "first": "Πρώτο", + "last": "Τελευταίο", + "all": "Όλα", + "output-msg-type-hint": "Τύπος μηνύματος του αποτελέσματος αποφυγής διπλοτύπων.", + "queue-name-hint": "Όνομα ουράς για δημοσίευση αποτελέσματος αποφυγής διπλοτύπων.", + "keys": "Κλειδιά", + "keys-required": "Απαιτούνται κλειδιά", + "rename-keys-in": "Μετονομασία κλειδιών σε", + "data": "Δεδομένα", + "message": "Μήνυμα", + "metadata": "Μεταδεδομένα", + "current-key-name": "Τρέχον όνομα κλειδιού", + "key-name-required": "Απαιτείται όνομα κλειδιού", + "new-key-name": "Νέο όνομα κλειδιού", + "new-key-name-required": "Απαιτείται νέο όνομα κλειδιού", + "metadata-keys": "Ονόματα πεδίων μεταδεδομένων", + "json-path-expression": "Έκφραση JSON path", + "json-path-expression-required": "Απαιτείται έκφραση JSON path", + "json-path-expression-hint": "Το JSONPath καθορίζει διαδρομή προς στοιχείο JSON. Το '$' είναι η ρίζα.", + "relations-query": "Ερώτημα σχέσεων", + "device-relations-query": "Ερώτημα σχέσεων συσκευής", + "max-relation-level": "Μέγιστο επίπεδο σχέσης", + "max-relation-level-error": "Η τιμή πρέπει να είναι > 0 ή να μην οριστεί.", + "max-relation-level-invalid": "Η τιμή πρέπει να είναι ακέραιος.", + "relation-type": "Τύπος σχέσης", + "relation-type-pattern": "Μοτίβο τύπου σχέσης", + "relation-type-pattern-required": "Απαιτείται μοτίβο τύπου σχέσης", + "relation-types-list": "Τύποι σχέσεων για διάδοση", + "relation-types-list-hint": "Αν δεν επιλεγούν τύποι, διαδίδονται χωρίς φιλτράρισμα.", + "unlimited-level": "Απεριόριστο επίπεδο", + "latest-telemetry": "Τελευταία τηλεμετρία", + "add-telemetry-key": "Προσθήκη κλειδιού τηλεμετρίας", + "delete-from": "Διαγραφή από", + "use-regular-expression-delete-hint": "Χρησιμοποιήστε κανονικές εκφράσεις για διαγραφή με βάση μοτίβα.\n\nΣυμβουλές:\n'Enter' για επιβεβαίωση, 'Backspace' για διαγραφή.\nΥποστηρίζονται πολλά πεδία.", + "fetch-into": "Ανάκτηση σε", + "attr-mapping": "Αντιστοίχιση χαρακτηριστικών:", + "source-attribute": "Κλειδί προέλευσης χαρακτηριστικού", + "source-attribute-required": "Απαιτείται κλειδί προέλευσης χαρακτηριστικού", + "source-telemetry": "Κλειδί προέλευσης τηλεμετρίας", + "source-telemetry-required": "Απαιτείται κλειδί προέλευσης τηλεμετρίας", + "target-key": "Κλειδί προορισμού", + "target-key-required": "Απαιτείται κλειδί προορισμού", + "attr-mapping-required": "Πρέπει να οριστεί τουλάχιστον μία αντιστοίχιση.", + "fields-mapping": "Αντιστοίχιση πεδίων", + "fields-mapping-hint": "Αν το πεδίο είναι $entityId, αποθηκεύεται το id της αρχικής οντότητας.", + "relations-query-config-direction-suffix": "προέλευση", + "profile-name": "Όνομα προφίλ", + "fetch-circle-parameter-info-from-metadata-hint": "Το μεταδεδομένο '{{perimeterKeyName}}' πρέπει να έχει μορφή: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "Το μεταδεδομένο '{{perimeterKeyName}}' πρέπει να έχει μορφή: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "Χρησιμοποιήστε $[messageKey] και ${metadataKey} για εξαγωγή τιμής.", + "fields-mapping-required": "Απαιτείται τουλάχιστον μία αντιστοίχιση πεδίου.", + "at-least-one-field-required": "Πρέπει να καθοριστεί τουλάχιστον ένα πεδίο με τιμή.", + "originator-fields-sv-map-hint": "Τα πεδία προορισμού υποστηρίζουν templatization με $[messageKey] και ${metadataKey}.", + "sv-map-hint": "Μόνο τα πεδία προορισμού υποστηρίζουν templatization με $[messageKey] και ${metadataKey}.", + "source-field": "Πεδίο προέλευσης", + "source-field-required": "Απαιτείται πεδίο προέλευσης.", + "originator-source": "Προέλευση", + "new-originator": "Νέα προέλευση", + "originator-customer": "Πελάτης", + "originator-tenant": "Ενοικιαστής", + "originator-related": "Συσχετιζόμενη οντότητα", + "originator-alarm-originator": "Προέλευση συναγερμού", + "originator-entity": "Οντότητα με μοτίβο ονόματος", + "clone-message": "Κλωνοποίηση μηνύματος", + "transform": "Μετασχηματισμός", + "default-ttl": "Προεπιλεγμένο TTL", + "default-ttl-required": "Απαιτείται προεπιλεγμένο TTL.", + "default-ttl-hint": "Το node θα πάρει το TTL από τα μεταδεδομένα. Αν λείπει, θα χρησιμοποιήσει την τιμή από τη διαμόρφωση. Αν είναι 0, θα εφαρμοστεί η τιμή από το προφίλ του ενοικιαστή.", + "default-ttl-zero-hint": "Το TTL δεν θα εφαρμοστεί αν είναι 0.", + "min-default-ttl-message": "Μόνο TTL 0 επιτρέπεται ως ελάχιστο.", + "generation-parameters": "Παράμετροι δημιουργίας", + "message-count": "Όριο παραγόμενων μηνυμάτων (0 - απεριόριστο)", + "message-count-required": "Απαιτείται όριο παραγόμενων μηνυμάτων.", + "min-message-count-message": "Μόνο 0 επιτρέπεται ως ελάχιστος αριθμός.", + "period-seconds": "Περίοδος σε δευτερόλεπτα", + "period-seconds-required": "Απαιτείται περίοδος.", + "generation-frequency-seconds": "Συχνότητα δημιουργίας σε δευτερόλεπτα", + "generation-frequency-required": "Απαιτείται συχνότητα δημιουργίας.", + "min-generation-frequency-message": "Ελάχιστο 60 δευτερόλεπτα επιτρέπεται.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Χρήση μοτίβου περιόδου από μεταδεδομένα", + "use-metadata-period-in-seconds-patterns-hint": "Αν επιλεγεί, θα χρησιμοποιείται μοτίβο περιόδου σε δευτερόλεπτα από μεταδεδομένα ή δεδομένα.", + "period-in-seconds-pattern": "Μοτίβο περιόδου σε δευτερόλεπτα", + "period-in-seconds-pattern-required": "Απαιτείται μοτίβο περιόδου.", + "min-period-seconds-message": "Μόνο 60 δευτερόλεπτα ελάχιστο επιτρέπεται.", + "originator": "Προέλευση", + "message-body": "Σώμα μηνύματος", + "message-metadata": "Μεταδεδομένα μηνύματος", + "generate": "Δημιουργία", + "current-rule-node": "Τρέχον Node Κανόνα", + "current-tenant": "Τρέχων ενοικιαστής", + "generator-function": "Συνάρτηση δημιουργίας", + "test-generator-function": "Δοκιμή συνάρτησης δημιουργίας", + "generator": "Γεννήτρια", + "test-filter-function": "Δοκιμή συνάρτησης φίλτρου", + "test-switch-function": "Δοκιμή συνάρτησης διακόπτη", + "test-transformer-function": "Δοκιμή συνάρτησης μετασχηματιστή", + "transformer": "Μετασχηματιστής", + "alarm-create-condition": "Συνθήκη δημιουργίας συναγερμού", + "test-condition-function": "Δοκιμή συνθήκης", + "alarm-clear-condition": "Συνθήκη καθαρισμού συναγερμού", + "alarm-details-builder": "Κατασκευή λεπτομερειών συναγερμού", + "test-details-function": "Δοκιμή συνάρτησης λεπτομερειών", + "alarm-type": "Τύπος συναγερμού", + "select-entity-types": "Επιλέξτε τύπους οντοτήτων", + "alarm-type-required": "Απαιτείται τύπος συναγερμού.", + "alarm-severity": "Σοβαρότητα συναγερμού", + "alarm-severity-required": "Απαιτείται σοβαρότητα συναγερμού", + "alarm-severity-pattern": "Μοτίβο σοβαρότητας συναγερμού", + "alarm-status-filter": "Φίλτρο κατάστασης συναγερμού", + "alarm-status-list-empty": "Η λίστα κατάστασης είναι κενή", + "no-alarm-status-matching": "Δεν βρέθηκε αντίστοιχη κατάσταση συναγερμού.", + "propagate": "Διάδοση συναγερμού σε σχετικές οντότητες", + "propagate-to-owner": "Διάδοση σε κάτοχο (Πελάτη ή Ενοικιαστή)", + "propagate-to-tenant": "Διάδοση στον ενοικιαστή", + "condition": "Συνθήκη", + "details": "Λεπτομέρειες", + "to-string": "Σε string", + "test-to-string-function": "Δοκιμή toString συνάρτησης", + "from-template": "Από", + "from-template-required": "Απαιτείται πεδίο Από", + "message-to-metadata": "Μήνυμα σε μεταδεδομένα", + "metadata-to-message": "Μεταδεδομένα σε μήνυμα", + "from-message": "Από μήνυμα", + "from-metadata": "Από μεταδεδομένα", + "to-template": "Προς", + "to-template-required": "Απαιτείται πεδίο Προς", + "mail-address-list-template-hint": "Λίστα διευθύνσεων χωρισμένη με κόμμα, χρησιμοποιήστε ${metadataKey} για τιμή από τα μεταδεδομένα, $[messageKey] για τιμή από το σώμα του μηνύματος", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "Θέμα", + "subject-template-required": "Απαιτείται πεδίο Θέμα", + "body-template": "Σώμα", + "body-template-required": "Απαιτείται πεδίο Σώμα", + "dynamic-mail-body-type": "Δυναμικός τύπος σώματος", + "mail-body-type": "Τύπος σώματος email", + "body-type-template": "Πρότυπο τύπου σώματος", + "reply-routing-configuration": "Διαμόρφωση δρομολόγησης απάντησης", + "rpc-reply-routing-configuration-hint": "Καθορίζει metadata κλειδιά για προσδιορισμό υπηρεσίας, συνεδρίας και αιτήματος για αποστολή απάντησης.", + "reply-routing-configuration-hint": "Καθορίζει metadata κλειδιά για προσδιορισμό υπηρεσίας και αιτήματος για απάντηση.", + "request-id-metadata-attribute": "Id Αιτήματος", + "service-id-metadata-attribute": "Id Υπηρεσίας", + "session-id-metadata-attribute": "Id Συνεδρίας", + "timeout-sec": "Χρονικό όριο (δευτερόλεπτα)", + "timeout-required": "Απαιτείται χρονικό όριο", + "min-timeout-message": "Μόνο τιμή 0 επιτρέπεται ως ελάχιστο χρονικό όριο.", + "endpoint-url-pattern": "Μοτίβο URL τελικού σημείου", + "endpoint-url-pattern-required": "Απαιτείται μοτίβο URL τελικού σημείου", + "request-method": "Μέθοδος αιτήματος", + "use-simple-client-http-factory": "Χρήση απλού HTTP client factory", + "ignore-request-body": "Χωρίς σώμα αιτήματος", + "parse-to-plain-text": "Μετατροπή σε απλό κείμενο", + "parse-to-plain-text-hint": "Αν επιλεγεί, το σώμα αιτήματος θα μετατραπεί από JSON σε απλό κείμενο, π.χ. msg = \"Hello,\\t\"world\"\" γίνεται Hello, \"world\"", + "read-timeout": "Χρονικό όριο ανάγνωσης (ms)", + "read-timeout-hint": "Τιμή 0 σημαίνει άπειρο χρονικό όριο", + "max-parallel-requests-count": "Μέγιστος αριθμός παραλλήλων αιτημάτων", + "max-parallel-requests-count-hint": "Τιμή 0 σημαίνει χωρίς περιορισμό", + "max-response-size": "Μέγιστο μέγεθος απόκρισης (KB)", + "max-response-size-hint": "Μέγιστο μέγεθος μνήμης για αποκωδικοποίηση/κωδικοποίηση HTTP", + "headers": "Κεφαλίδες", + "headers-hint": "Χρησιμοποιήστε ${metadataKey} για τιμή από τα μεταδεδομένα, $[messageKey] για τιμή από το σώμα του μηνύματος στα πεδία κεφαλίδας/τιμής", + "header": "Κεφαλίδα", + "header-required": "Απαιτείται κεφαλίδα", + "value": "Τιμή", + "value-required": "Απαιτείται τιμή", + "topic-pattern": "Μοτίβο θέματος", + "key-pattern": "Μοτίβο κλειδιού", + "key-pattern-hint": "Αν δοθεί partition number, θα χρησιμοποιηθεί. Διαφορετικά, θα χρησιμοποιηθεί το κλειδί.", + "topic-pattern-required": "Απαιτείται μοτίβο θέματος", + "topic": "Θέμα", + "topic-required": "Απαιτείται θέμα", + "bootstrap-servers": "Bootstrap servers", + "bootstrap-servers-required": "Απαιτείται τιμή bootstrap servers", + "other-properties": "Άλλες ιδιότητες", + "key": "Κλειδί", + "key-required": "Απαιτείται κλειδί", + "retries": "Αυτόματες επαναπροσπάθειες σε αποτυχία", + "min-retries-message": "Μόνο 0 επιτρέπεται ως ελάχιστο", + "batch-size-bytes": "Μέγεθος παρτίδας σε bytes", + "min-batch-size-bytes-message": "Μόνο 0 επιτρέπεται ως ελάχιστο", + "linger-ms": "Χρόνος τοπικής προσωρινής αποθήκευσης (ms)", + "min-linger-ms-message": "Μόνο 0 επιτρέπεται ως ελάχιστο", + "buffer-memory-bytes": "Μέγιστο μέγεθος buffer client σε bytes", + "min-buffer-memory-message": "Μόνο 0 επιτρέπεται ως ελάχιστο", + "memory-buffer-size-range": "Το μέγεθος buffer πρέπει να είναι μεταξύ 0 και {{max}} KB", + "acks": "Αριθμός επιβεβαιώσεων", + "topic-arn-pattern": "Μοτίβο ARN θέματος", + "topic-arn-pattern-required": "Απαιτείται μοτίβο ARN θέματος", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "Απαιτείται AWS Access Key ID", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "Απαιτείται AWS Secret Access Key", + "aws-region": "Περιοχή AWS", + "aws-region-required": "Απαιτείται περιοχή AWS", + "exchange-name-pattern": "Μοτίβο ονόματος ανταλλαγής", + "routing-key-pattern": "Μοτίβο routing key", + "message-properties": "Ιδιότητες μηνύματος", + "host": "Host", + "host-required": "Απαιτείται Host", + "port": "Θύρα", + "port-required": "Απαιτείται θύρα", + "port-range": "Η θύρα πρέπει να είναι από 1 έως 65535.", + "virtual-host": "Εικονικός host", + "username": "Όνομα χρήστη", + "password": "Κωδικός", + "automatic-recovery": "Αυτόματη επαναφορά", + "connection-timeout-ms": "Χρονικό όριο σύνδεσης (ms)", + "min-connection-timeout-ms-message": "Μόνο 0 επιτρέπεται ως ελάχιστο", + "handshake-timeout-ms": "Χρονικό όριο handshake (ms)", + "min-handshake-timeout-ms-message": "Μόνο 0 επιτρέπεται ως ελάχιστο", + "client-properties": "Ιδιότητες πελάτη", + "queue-url-pattern": "Μοτίβο URL ουράς", + "queue-url-pattern-required": "Απαιτείται μοτίβο URL ουράς", + "delay-seconds": "Καθυστέρηση (δευτ)", + "min-delay-seconds-message": "Ελάχιστο 0 δευτερόλεπτα επιτρέπεται", + "max-delay-seconds-message": "Μέγιστο 900 δευτερόλεπτα επιτρέπεται", + "name": "Όνομα", + "name-required": "Απαιτείται όνομα", + "queue-type": "Τύπος ουράς", + "sqs-queue-standard": "Standard", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "GCP Project ID", + "gcp-project-id-required": "Απαιτείται GCP Project ID", + "gcp-service-account-key": "GCP αρχείο κλειδιού λογαριασμού υπηρεσίας", + "gcp-service-account-key-required": "Απαιτείται αρχείο κλειδιού GCP", + "pubsub-topic-name": "Όνομα θέματος", + "pubsub-topic-name-required": "Απαιτείται όνομα θέματος", + "message-attributes": "Ιδιότητες μηνύματος", + "message-attributes-hint": "Χρησιμοποιήστε ${metadataKey} για τιμή από τα μεταδεδομένα, $[messageKey] για τιμή από το σώμα του μηνύματος στα πεδία ονόματος/τιμής", + "connect-timeout": "Χρονικό όριο σύνδεσης (δευτ)", + "connect-timeout-required": "Απαιτείται χρονικό όριο σύνδεσης.", + "connect-timeout-range": "Το χρονικό όριο πρέπει να είναι από 1 έως 200.", + "client-id": "Client ID", + "client-id-hint": "Προαιρετικό. Αφήστε κενό για αυτόματη δημιουργία. Αν δοθεί, να είναι μοναδικό.", + "append-client-id-suffix": "Προσθήκη ID υπηρεσίας στο Client ID", + "client-id-suffix-hint": "Εφαρμόζεται όταν δίνεται Client ID. Προσθέτει το ID υπηρεσίας ως επίθημα για αποφυγή συγκρούσεων.", + "device-id": "ID Συσκευής", + "device-id-required": "Απαιτείται ID Συσκευής", + "clean-session": "Καθαρή συνεδρία", + "enable-ssl": "Ενεργοποίηση SSL", + "credentials": "Διαπιστευτήρια", + "credentials-type": "Τύπος διαπιστευτηρίων", + "credentials-type-required": "Απαιτείται τύπος διαπιστευτηρίων.", + "credentials-anonymous": "Ανώνυμα", + "credentials-basic": "Βασικά", + "credentials-pem": "PEM", + "credentials-pem-hint": "Απαιτείται τουλάχιστον το αρχείο πιστοποιητικού Server CA ή το ζεύγος Client certificate και Client private key", + "credentials-sas": "Shared Access Signature", + "sas-key": "SAS Κλειδί", + "sas-key-required": "Απαιτείται SAS Κλειδί.", + "hostname": "Hostname", + "hostname-required": "Απαιτείται hostname.", + "azure-ca-cert": "Αρχείο πιστοποιητικού CA", + "username-required": "Απαιτείται όνομα χρήστη.", + "password-required": "Απαιτείται κωδικός πρόσβασης.", + "ca-cert": "Αρχείο πιστοποιητικού Server CA", + "private-key": "Αρχείο ιδιωτικού κλειδιού πελάτη", + "cert": "Αρχείο πιστοποιητικού πελάτη", + "no-file": "Δεν έχει επιλεγεί αρχείο.", + "drop-file": "Αποθέστε ένα αρχείο ή κάντε κλικ για να επιλέξετε.", + "private-key-password": "Κωδικός ιδιωτικού κλειδιού", + "use-system-smtp-settings": "Χρήση ρυθμίσεων SMTP του συστήματος", + "use-metadata-dynamic-interval": "Χρήση δυναμικού διαστήματος", + "metadata-dynamic-interval-hint": "Υποστηρίζεται τεμπλοποίηση στα πεδία. Τιμές σε milliseconds.", + "use-metadata-interval-patterns-hint": "Αν επιλεγεί, χρησιμοποιούνται μοτίβα από metadata για αρχή/τέλος διαστήματος (ms).", + "use-message-alarm-data": "Χρήση δεδομένων συναγερμού από το μήνυμα", + "overwrite-alarm-details": "Αντικατάσταση λεπτομερειών συναγερμού", + "use-alarm-severity-pattern": "Χρήση μοτίβου σοβαρότητας συναγερμού", + "check-all-keys": "Έλεγχος παρουσίας όλων των πεδίων", + "check-all-keys-hint": "Αν επιλεγεί, ελέγχεται η παρουσία όλων των καθορισμένων κλειδιών.", + "check-relation-to-specific-entity": "Έλεγχος σχέσης με συγκεκριμένη οντότητα", + "check-relation-to-specific-entity-tooltip": "Αν ενεργοποιηθεί, ελέγχει για συγκεκριμένη οντότητα αντί για οποιαδήποτε.", + "check-relation-hint": "Ελέγχει ύπαρξη σχέσης με βάση κατεύθυνση και τύπο.", + "delete-relation-with-specific-entity": "Διαγραφή σχέσης με συγκεκριμένη οντότητα", + "delete-relation-with-specific-entity-hint": "Αν ενεργοποιηθεί, διαγράφει μόνο τη συγκεκριμένη σχέση.", + "delete-relation-hint": "Διαγράφει σχέσεις από originator προς οντότητα/οντότητες με βάση κατεύθυνση/τύπο.", + "remove-current-relations": "Αφαίρεση τρεχουσών σχέσεων", + "remove-current-relations-hint": "Αφαιρεί σχέσεις από τον originator.", + "change-originator-to-related-entity": "Αλλαγή originator σε σχετική οντότητα", + "change-originator-to-related-entity-hint": "Επεξεργασία μηνύματος ως από άλλη οντότητα.", + "start-interval": "Έναρξη διαστήματος", + "end-interval": "Τέλος διαστήματος", + "start-interval-required": "Απαιτείται έναρξη διαστήματος.", + "end-interval-required": "Απαιτείται τέλος διαστήματος.", + "smtp-protocol": "Πρωτόκολλο", + "smtp-host": "SMTP host", + "smtp-host-required": "Απαιτείται SMTP host.", + "smtp-port": "SMTP port", + "smtp-port-required": "Πρέπει να καθοριστεί θύρα SMTP.", + "smtp-port-range": "Η θύρα SMTP πρέπει να είναι από 1 έως 65535.", + "timeout-msec": "Timeout ms", + "min-timeout-msec-message": "Μόνο τιμή 0 επιτρέπεται ως ελάχιστο.", + "enter-username": "Εισαγωγή ονόματος χρήστη", + "enter-password": "Εισαγωγή κωδικού πρόσβασης", + "enable-tls": "Ενεργοποίηση TLS", + "tls-version": "Έκδοση TLS", + "enable-proxy": "Ενεργοποίηση proxy", + "use-system-proxy-properties": "Χρήση proxy ρυθμίσεων συστήματος", + "proxy-host": "Host proxy", + "proxy-host-required": "Απαιτείται proxy host.", + "proxy-port": "Θύρα proxy", + "proxy-port-required": "Απαιτείται θύρα proxy.", + "proxy-port-range": "Η θύρα proxy πρέπει να είναι από 1 έως 65535.", + "proxy-user": "Χρήστης proxy", + "proxy-password": "Κωδικός proxy", + "proxy-scheme": "Σχήμα proxy", + "numbers-to-template": "Πρότυπο τηλεφώνων", + "numbers-to-template-required": "Απαιτείται πρότυπο τηλεφώνων", + "numbers-to-template-hint": "Αριθμοί τηλεφώνου χωρισμένοι με κόμμα, χρησιμοποιήστε ${metadataKey} για τιμή από τα μεταδεδομένα, $[messageKey] για τιμή από το σώμα του μηνύματος", + "sms-message-template": "Πρότυπο μηνύματος SMS", + "sms-message-template-required": "Απαιτείται πρότυπο μηνύματος SMS", + "use-system-sms-settings": "Χρήση ρυθμίσεων SMS συστήματος", + "min-period-0-seconds-message": "Μόνο τιμή 0 επιτρέπεται ως ελάχιστη περίοδος.", + "max-pending-messages": "Μέγιστος αριθμός εκκρεμών μηνυμάτων", + "max-pending-messages-required": "Απαιτείται μέγιστος αριθμός εκκρεμών μηνυμάτων.", + "max-pending-messages-range": "Ο μέγιστος αριθμός εκκρεμών μηνυμάτων πρέπει να είναι εντός του εύρους από 1 έως 100000.", + "originator-types-filter": "Φίλτρο τύπων αποστολέα", + "interval-seconds": "Διάστημα σε δευτερόλεπτα", + "interval-seconds-required": "Απαιτείται διάστημα.", + "int-range": "Η τιμή δεν πρέπει να υπερβαίνει το μέγιστο όριο ακέραιου (2147483648)", + "min-interval-seconds-message": "Επιτρέπεται μόνο ελάχιστο διάστημα 1 δευτερολέπτου.", + "output-timeseries-key-prefix": "Πρόθεμα κλειδιού χρονοσειράς εξόδου", + "output-timeseries-key-prefix-required": "Απαιτείται πρόθεμα κλειδιού χρονοσειράς εξόδου.", + "separator-hint": "Πατήστε \"Enter\" για να ολοκληρώσετε την εισαγωγή.", + "select-details": "Επιλογή λεπτομερειών", + "entity-details-id": "Id", + "entity-details-title": "Τίτλος", + "entity-details-country": "Χώρα", + "entity-details-state": "Πολιτεία", + "entity-details-city": "Πόλη", + "entity-details-zip": "Ταχ. Κώδικας", + "entity-details-address": "Διεύθυνση", + "entity-details-address2": "Διεύθυνση 2", + "entity-details-additional_info": "Επιπλέον πληροφορίες", + "entity-details-phone": "Τηλέφωνο", + "entity-details-email": "Email", + "email-sender": "Αποστολέας Email", + "fields-to-check": "Πεδία προς έλεγχο", + "add-detail": "Προσθήκη λεπτομέρειας", + "check-all-keys-tooltip": "Εάν ενεργοποιηθεί, ελέγχει την παρουσία όλων των πεδίων που αναφέρονται στα ονόματα πεδίων μηνύματος και μεταδεδομένων στο εισερχόμενο μήνυμα και τα μεταδεδομένα του.", + "fields-to-check-hint": "Πατήστε \"Enter\" για να ολοκληρώσετε την εισαγωγή πεδίου. Υποστηρίζονται πολλά πεδία.", + "entity-details-list-empty": "Πρέπει να επιλεγεί τουλάχιστον μία λεπτομέρεια.", + "alarm-status": "Κατάσταση συναγερμού", + "alarm-required": "Πρέπει να επιλεγεί τουλάχιστον μία κατάσταση συναγερμού.", + "no-entity-details-matching": "Δεν βρέθηκαν αντίστοιχες λεπτομέρειες οντοτήτων.", + "custom-table-name": "Προσαρμοσμένο όνομα πίνακα", + "custom-table-name-required": "Απαιτείται όνομα πίνακα", + "custom-table-hint": "Ο πίνακας πρέπει να έχει δημιουργηθεί στον Cassandra cluster και το όνομά του να ξεκινά με 'cs_tb_'. Εισαγάγετε το όνομα χωρίς το πρόθεμα.", + "message-field": "Πεδίο μηνύματος", + "message-field-required": "Απαιτείται πεδίο μηνύματος.", + "table-col": "Στήλη πίνακα", + "table-col-required": "Απαιτείται στήλη πίνακα.", + "latitude-field-name": "Όνομα πεδίου γεωγραφικού πλάτους", + "longitude-field-name": "Όνομα πεδίου γεωγραφικού μήκους", + "latitude-field-name-required": "Απαιτείται όνομα πεδίου γεωγραφικού πλάτους.", + "longitude-field-name-required": "Απαιτείται όνομα πεδίου γεωγραφικού μήκους.", + "fetch-perimeter-info-from-metadata": "Ανάκτηση πληροφοριών περιμέτρου από μεταδεδομένα", + "fetch-perimeter-info-from-metadata-tooltip": "Εάν ο τύπος περιμέτρου είναι 'Polygon', η τιμή του πεδίου '{{perimeterKeyName}}' χρησιμοποιείται απευθείας. Αν είναι 'Circle', το πεδίο αναλύεται για εξαγωγή 'latitude', 'longitude', 'radius', 'radiusUnit'.", + "perimeter-key-name": "Όνομα κλειδιού περιμέτρου", + "perimeter-key-name-hint": "Όνομα πεδίου μεταδεδομένων που περιέχει πληροφορίες περιμέτρου.", + "perimeter-key-name-required": "Απαιτείται όνομα κλειδιού περιμέτρου.", + "perimeter-circle": "Κύκλος", + "perimeter-polygon": "Πολύγωνο", + "perimeter-type": "Τύπος περιμέτρου", + "circle-center-latitude": "Γεωγραφικό πλάτος κέντρου", + "circle-center-latitude-required": "Απαιτείται γεωγραφικό πλάτος κέντρου.", + "circle-center-longitude": "Γεωγραφικό μήκος κέντρου", + "circle-center-longitude-required": "Απαιτείται γεωγραφικό μήκος κέντρου.", + "range-unit-meter": "Μέτρο", + "range-unit-kilometer": "Χιλιόμετρο", + "range-unit-foot": "Πόδι", + "range-unit-mile": "Μίλι", + "range-unit-nautical-mile": "Ναυτικό μίλι", + "range-units": "Μονάδες απόστασης", + "range-units-required": "Απαιτούνται μονάδες απόστασης.", + "range": "Απόσταση", + "range-required": "Απαιτείται απόσταση.", + "polygon-definition": "Ορισμός πολυγώνου", + "polygon-definition-required": "Απαιτείται ορισμός πολυγώνου.", + "polygon-definition-hint": "Χρησιμοποιήστε μορφή: [[lat1,lon1],[lat2,lon2],...,[latN,lonN]].", + "min-inside-duration": "Ελάχιστη διάρκεια εντός", + "min-inside-duration-value-required": "Απαιτείται ελάχιστη διάρκεια εντός.", + "min-inside-duration-time-unit": "Μονάδα χρόνου ελάχιστης διάρκειας εντός", + "min-outside-duration": "Ελάχιστη διάρκεια εκτός", + "min-outside-duration-value-required": "Απαιτείται ελάχιστη διάρκεια εκτός.", + "min-outside-duration-time-unit": "Μονάδα χρόνου ελάχιστης διάρκειας εκτός", + "tell-failure-if-absent": "Αναφορά αποτυχίας αν απουσιάζει", + "tell-failure-if-absent-hint": "Αν λείπει κάποιο κλειδί, θα επιστραφεί μήνυμα \"Failure\".", + "get-latest-value-with-ts": "Ανάκτηση χρονικής σήμανσης για την τελευταία τιμή", + "get-latest-value-with-ts-hint": "Αν ενεργοποιηθεί, οι τιμές τηλεμετρίας θα περιλαμβάνουν και χρονική σήμανση.", + "ignore-null-strings": "Αγνόηση κενών συμβολοσειρών", + "ignore-null-strings-hint": "Αν ενεργοποιηθεί, οι κενές τιμές αγνοούνται.", + "add-metadata-key-values-as-kafka-headers": "Προσθήκη μεταδεδομένων στα Kafka headers", + "add-metadata-key-values-as-kafka-headers-hint": "Τα ζεύγη μεταδεδομένων προστίθενται ως headers στην Kafka εγγραφή.", + "charset-encoding": "Κωδικοποίηση χαρακτήρων", + "charset-encoding-required": "Απαιτείται κωδικοποίηση χαρακτήρων.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "Μπορείτε να επιλέξετε από τη λίστα ή να προσθέσετε δικό σας όνομα.", + "device-profile-node-hint": "Χρήσιμο για συνθήκες διάρκειας/επαναλήψεων.", + "persist-alarm-rules": "Αποθήκευση κατάστασης κανόνων συναγερμού", + "persist-alarm-rules-hint": "Εάν ενεργοποιηθεί, η κατάσταση θα αποθηκεύεται στη βάση.", + "fetch-alarm-rules": "Ανάκτηση κατάστασης κανόνων συναγερμού", + "fetch-alarm-rules-hint": "Εάν ενεργοποιηθεί, η κατάσταση αποκαθίσταται κατά την αρχικοποίηση.", + "input-value-key": "Κλειδί εισερχόμενης τιμής", + "input-value-key-required": "Απαιτείται κλειδί εισερχόμενης τιμής.", + "output-value-key": "Κλειδί εξόδου τιμής", + "output-value-key-required": "Απαιτείται το κλειδί εξόδου τιμής.", + "number-of-digits-after-floating-point": "Αριθμός ψηφίων μετά την υποδιαστολή", + "number-of-digits-after-floating-point-range": "Ο αριθμός ψηφίων πρέπει να είναι από 0 έως 15.", + "failure-if-delta-negative": "Αναφορά αποτυχίας αν η διαφορά είναι αρνητική", + "failure-if-delta-negative-tooltip": "Ο κόμβος κανόνα αποτυγχάνει την επεξεργασία αν η τιμή διαφοράς είναι αρνητική.", + "use-caching": "Χρήση προσωρινής αποθήκευσης", + "use-caching-tooltip": "Ο κόμβος κανόνα θα αποθηκεύει προσωρινά την τιμή του \"{{inputValueKey}}\" για βελτίωση απόδοσης.", + "add-time-difference-between-readings": "Προσθήκη χρονικής διαφοράς μεταξύ των αναγνώσεων \"{{inputValueKey}}\"", + "add-time-difference-between-readings-tooltip": "Αν ενεργοποιηθεί, η διαφορά χρόνου θα προστεθεί στο εξερχόμενο μήνυμα ως \"{{periodValueKey}}\".", + "period-value-key": "Κλειδί τιμής περιόδου", + "period-value-key-required": "Απαιτείται κλειδί τιμής περιόδου.", + "general-pattern-hint": "Χρησιμοποιήστε ${metadataKey} για τιμή από μεταδεδομένα και $[messageKey] για τιμή από το σώμα μηνύματος.", + "alarm-severity-pattern-hint": "Χρησιμοποιήστε ${metadataKey} για τιμή από τα μεταδεδομένα, $[messageKey] για τιμή από το σώμα του μηνύματος. Η σοβαρότητα του συναγερμού πρέπει να είναι συστημική (CRITICAL, MAJOR κ.λπ.)", + "output-node-name-hint": "Το όνομα κόμβου αντιστοιχεί στον τύπο σχέσης για την προώθηση μηνυμάτων σε άλλους κόμβους.", + "use-server-ts": "Χρήση χρονικής σήμανσης διακομιστή", + "use-server-ts-hint": "Χρησιμοποιεί χρονική σήμανση διακομιστή όταν δεν ορίζεται στα δεδομένα.", + "kv-map-pattern-hint": "Όλα τα πεδία εισόδου υποστηρίζουν template. Χρησιμοποιήστε $[messageKey] ή ${metadataKey}.", + "kv-map-single-pattern-hint": "Το πεδίο εισόδου υποστηρίζει template.", + "shared-scope": "Κοινόχρηστο scope", + "server-scope": "Scope διακομιστή", + "client-scope": "Scope πελάτη", + "attribute-type": "Ιδιότητα", + "attribute-type-description": "Ανάκτηση τιμής από βάση δεδομένων", + "attribute-type-result-description": "Αποθήκευση ως ιδιότητα οντότητας", + "constant-type": "Σταθερά", + "constant-type-description": "Καθορισμός σταθερής τιμής", + "time-series-type": "Χρονοσειρά", + "time-series-type-description": "Ανάκτηση τελευταίας τιμής χρονοσειράς", + "time-series-type-result-description": "Αποθήκευση ως χρονοσειρά οντότητας", + "message-body-type": "Μήνυμα", + "message-body-type-description": "Ανάκτηση τιμής από σώμα μηνύματος", + "message-body-type-result-description": "Προσθήκη στο εξερχόμενο μήνυμα", + "message-metadata-type": "Μεταδεδομένα", + "message-metadata-type-description": "Ανάκτηση από μεταδεδομένα μηνύματος", + "message-metadata-result-description": "Προσθήκη στα μεταδεδομένα εξόδου", + "argument-tile": "Ορίσματα", + "no-arguments-prompt": "Δεν έχουν οριστεί ορίσματα", + "result-title": "Αποτέλεσμα", + "functions-field-input": "Συναρτήσεις", + "no-option-found": "Δεν βρέθηκε επιλογή", + "argument-source-field-input": "Πηγή", + "argument-source-field-input-required": "Απαιτείται πηγή ορίσματος.", + "argument-key-field-input": "Κλειδί", + "argument-key-field-input-required": "Απαιτείται κλειδί ορίσματος.", + "constant-value-field-input": "Σταθερή τιμή", + "constant-value-field-input-required": "Απαιτείται σταθερή τιμή.", + "attribute-scope-field-input": "Scope ιδιότητας", + "attribute-scope-field-input-required": "Απαιτείται scope ιδιότητας.", + "default-value-field-input": "Προεπιλεγμένη τιμή", + "type-field-input": "Τύπος", + "type-field-input-required": "Απαιτείται τύπος.", + "key-field-input": "Κλειδί", + "add-entity-type": "Προσθήκη τύπου οντότητας", + "add-device-profile": "Προσθήκη προφίλ συσκευής", + "key-field-input-required": "Απαιτείται κλειδί.", + "number-floating-point-field-input": "Ψηφία μετά την υποδιαστολή", + "number-floating-point-field-input-hint": "Χρησιμοποιήστε 0 για μετατροπή σε ακέραιο", + "add-to-message-field-input": "Προσθήκη στο μήνυμα", + "add-to-metadata-field-input": "Προσθήκη στα μεταδεδομένα", + "custom-expression-field-input": "Μαθηματική έκφραση", + "custom-expression-field-input-required": "Απαιτείται μαθηματική έκφραση", + "custom-expression-field-input-hint": "Ορισμός έκφρασης (π.χ. Fahrenheit σε Celsius)", + "retained-message": "Διατηρούμενο", + "attributes-mapping": "Αντιστοίχιση ιδιοτήτων", + "latest-telemetry-mapping": "Αντιστοίχιση τελευταίας τηλεμετρίας", + "add-mapped-attribute-to": "Προσθήκη ιδιοτήτων σε", + "add-mapped-latest-telemetry-to": "Προσθήκη τηλεμετρίας σε", + "add-mapped-fields-to": "Προσθήκη πεδίων σε", + "add-selected-details-to": "Προσθήκη επιλεγμένων λεπτομερειών σε", + "clear-selected-types": "Απαλοιφή τύπων", + "clear-selected-details": "Απαλοιφή λεπτομερειών", + "clear-selected-fields": "Απαλοιφή πεδίων", + "clear-selected-keys": "Απαλοιφή κλειδιών", + "geofence-configuration": "Ρυθμίσεις γεωφράχτη", + "coordinate-field-names": "Ονόματα πεδίων συντεταγμένων", + "coordinate-field-hint": "Αναζήτηση πεδίων από μήνυμα ή μεταδεδομένα", + "presence-monitoring-strategy": "Στρατηγική παρακολούθησης παρουσίας", + "presence-monitoring-strategy-on-first-message": "Στο πρώτο μήνυμα", + "presence-monitoring-strategy-on-each-message": "Σε κάθε μήνυμα", + "presence-monitoring-strategy-on-first-message-hint": "Αναφέρει 'Inside/Outside' μετά το πρώτο μήνυμα και την ελάχιστη διάρκεια.", + "presence-monitoring-strategy-on-each-message-hint": "Αναφέρει 'Inside/Outside' σε κάθε μήνυμα μετά από αλλαγή παρουσίας.", + "fetch-credentials-to": "Ανάκτηση διαπιστευτηρίων σε", + "add-originator-attributes-to": "Προσθήκη ιδιοτήτων αποστολέα σε", + "originator-attributes": "Ιδιότητες αποστολέα", + "fetch-latest-telemetry-with-timestamp": "Ανάκτηση τελευταίας τηλεμετρίας με timestamp", + "fetch-latest-telemetry-with-timestamp-tooltip": "Οι τιμές προστίθενται στα μεταδεδομένα με χρονική σήμανση.", + "tell-failure": "Αναφορά αποτυχίας αν λείπουν ιδιότητες", + "tell-failure-tooltip": "Αν λείπει κάποιο κλειδί, το μήνυμα εξόδου αποτυγχάνει.", + "created-time": "Ώρα δημιουργίας", + "chip-help": "Πατήστε 'Enter' για να ολοκληρώσετε την εισαγωγή {{inputName}}. \nΠατήστε 'Backspace' για να διαγράψετε το {{inputName}}. \nΥποστηρίζονται πολλαπλές τιμές.", + "detail": "λεπτομέρεια", + "field-name": "όνομα πεδίου", + "device-profile": "προφίλ συσκευής", + "entity-type": "τύπος οντότητας", + "message-type": "τύπος μηνύματος", + "timeseries-key": "κλειδί χρονοσειράς", + "type": "Τύπος", + "first-name": "Όνομα", + "last-name": "Επώνυμο", + "label": "Ετικέτα", + "originator-fields-mapping": "Χαρτογράφηση πεδίων αποστολέα", + "add-mapped-originator-fields-to": "Προσθήκη χαρτογραφημένων πεδίων αποστολέα σε", + "fields": "Πεδία", + "skip-empty-fields": "Παράλειψη κενών πεδίων", + "skip-empty-fields-tooltip": "Τα πεδία με κενές τιμές δεν θα προστεθούν στο μήνυμα εξόδου ή τα μεταδεδομένα εξόδου.", + "fetch-interval": "Διάστημα ανάκτησης", + "fetch-strategy": "Στρατηγική ανάκτησης", + "fetch-timeseries-from-to": "Ανάκτηση χρονοσειρών από {{startInterval}} {{startIntervalTimeUnit}} πριν έως {{endInterval}} {{endIntervalTimeUnit}} πριν.", + "fetch-timeseries-from-to-invalid": "Μη έγκυρη ανάκτηση χρονοσειρών (\"Η αρχή του διαστήματος\" πρέπει να είναι μικρότερη από \"το τέλος του διαστήματος\").", + "use-metadata-dynamic-interval-tooltip": "Εάν επιλεγεί, ο κόμβος κανόνα θα χρησιμοποιήσει δυναμικά πρότυπα για την έναρξη και λήξη του διαστήματος βάσει μηνύματος και μεταδεδομένων.", + "all-mode-hint": "Αν επιλεγεί 'Όλα', θα ανακτηθεί τηλεμετρία εντός του καθορισμένου διαστήματος με παραμέτρους ερωτήματος.", + "first-mode-hint": "Αν επιλεγεί 'Πρώτο', θα ανακτηθεί η πιο κοντινή τηλεμετρία στην αρχή του διαστήματος.", + "last-mode-hint": "Αν επιλεγεί 'Τελευταίο', θα ανακτηθεί η πιο κοντινή τηλεμετρία στο τέλος του διαστήματος.", + "ascending": "Αύξουσα", + "descending": "Φθίνουσα", + "min": "Ελάχιστο", + "max": "Μέγιστο", + "average": "Μέσος όρος", + "sum": "Άθροισμα", + "count": "Πλήθος", + "none": "Καμία", + "last-level-relation-tooltip": "Αν επιλεγεί, η αναζήτηση σχετικών οντοτήτων περιορίζεται στο καθορισμένο επίπεδο σχέσεων.", + "last-level-device-relation-tooltip": "Αν επιλεγεί, η αναζήτηση σχετικών συσκευών περιορίζεται στο καθορισμένο επίπεδο σχέσεων.", + "data-to-fetch": "Δεδομένα προς ανάκτηση", + "mapping-of-customers": "Χαρτογράφηση πελατών", + "map-fields-required": "Απαιτούνται όλα τα πεδία χαρτογράφησης.", + "attributes": "Ιδιότητες", + "related-device-attributes": "Ιδιότητες σχετικής συσκευής", + "add-selected-attributes-to": "Προσθήκη επιλεγμένων ιδιοτήτων σε", + "device-profiles": "Προφίλ συσκευών", + "mapping-of-tenant": "Χαρτογράφηση ενοικιαστή", + "add-attribute-key": "Προσθήκη κλειδιού ιδιότητας", + "message-template": "Πρότυπο μηνύματος", + "message-template-required": "Απαιτείται πρότυπο μηνύματος", + "use-system-slack-settings": "Χρήση ρυθμίσεων συστήματος για Slack", + "slack-api-token": "Slack API token", + "slack-api-token-required": "Απαιτείται Slack API token", + "keys-mapping": "Χαρτογράφηση κλειδιών", + "add-key": "Προσθήκη κλειδιού", + "recipients": "Παραλήπτες", + "message-subject-and-content": "Θέμα και περιεχόμενο μηνύματος", + "template-rules-hint": "Και τα δύο πεδία υποστηρίζουν template. Χρησιμοποιήστε $[messageKey] ή ${metadataKey}.", + "originator-customer-desc": "Χρήση πελάτη αποστολέα ως νέος αποστολέας.", + "originator-tenant-desc": "Χρήση τρέχοντος ενοικιαστή ως νέος αποστολέας.", + "originator-related-entity-desc": "Χρήση σχετικής οντότητας ως νέος αποστολέας βάσει σχέσης.", + "originator-alarm-originator-desc": "Χρήση αποστολέα συναγερμού ως νέος αποστολέας (μόνο αν είναι αποστολέας συναγερμού).", + "originator-entity-by-name-pattern-desc": "Χρήση οντότητας από βάση δεδομένων ως αποστολέας βάσει ονόματος.", + "email-from-template-hint": "Χρησιμοποιήστε $[messageKey] ή ${metadataKey} για τιμές.", + "recipients-block-main-hint": "Διευθύνσεις διαχωρισμένες με κόμμα. Υποστηρίζονται templates.", + "forward-msg-default-rule-chain": "Προώθηση μηνύματος στην προεπιλεγμένη αλυσίδα κανόνων αποστολέα", + "forward-msg-default-rule-chain-tooltip": "Αν δεν υπάρχει προεπιλεγμένη αλυσίδα, χρησιμοποιείται αυτή από τη διαμόρφωση προφίλ.", + "exclude-zero-deltas": "Αποκλεισμός μηδενικών διαφορών από μήνυμα εξόδου", + "exclude-zero-deltas-hint": "Το {{outputValueKey}} προστίθεται μόνο αν η τιμή δεν είναι 0.", + "exclude-zero-deltas-time-difference-hint": "Το {{outputValueKey}} και {{periodValueKey}} προστίθενται μόνο αν η τιμή {{outputValueKey}} δεν είναι 0.", + "search-direction-from": "Από αποστολέα προς στόχο", + "search-direction-to": "Από στόχο προς αποστολέα", + "del-relation-direction-from": "Από αποστολέα", + "del-relation-direction-to": "Προς αποστολέα", + "target-entity": "Οντότητα στόχος", + "function-configuration": "Διαμόρφωση συνάρτησης", + "function-name": "Όνομα συνάρτησης", + "function-name-required": "Απαιτείται όνομα συνάρτησης.", + "qualifier": "Καταληκτικό", + "qualifier-hint": "Αν δεν καθοριστεί, χρησιμοποιείται το \"$LATEST\".", + "aws-credentials": "Διαπιστευτήρια AWS", + "connection-timeout": "Χρονικό όριο σύνδεσης", + "connection-timeout-required": "Απαιτείται χρονικό όριο σύνδεσης.", + "connection-timeout-min": "Ελάχιστο χρονικό όριο σύνδεσης είναι 0.", + "connection-timeout-hint": "Ο χρόνος αναμονής σε δευτερόλεπτα κατά την αρχική σύνδεση πριν από διακοπή. Τιμή 0 σημαίνει απεριόριστος χρόνος και δεν συνιστάται.", + "request-timeout": "Χρονικό όριο αιτήματος", + "request-timeout-required": "Απαιτείται χρονικό όριο αιτήματος", + "request-timeout-min": "Ελάχιστο χρονικό όριο αιτήματος είναι 0", + "request-timeout-hint": "Ο χρόνος αναμονής σε δευτερόλεπτα για την ολοκλήρωση του αιτήματος πριν από διακοπή. Τιμή 0 σημαίνει απεριόριστος χρόνος και δεν συνιστάται.", + "units": "Μονάδες", + "tell-failure-aws-lambda": "Αναφορά αποτυχίας αν η AWS Lambda επιστρέψει εξαίρεση", + "tell-failure-aws-lambda-hint": "Ο κόμβος αναγκάζει αποτυχία στην επεξεργασία αν η AWS Lambda επιστρέψει εξαίρεση.", + "basic-mode": "Βασικό", + "advanced-mode": "Για προχωρημένους", + "save-time-series": { + "processing-settings": "Ρυθμίσεις επεξεργασίας", + "processing-settings-hint": "Καθορίστε πώς θα γίνει η επεξεργασία των εισερχόμενων μηνυμάτων. Οι βασικές ρυθμίσεις επιτρέπουν προκαθορισμένες στρατηγικές, ενώ οι προχωρημένες δίνουν περισσότερη ευελιξία.", + "advanced-settings-hint": "Να είστε προσεκτικοί. Ορισμένοι συνδυασμοί μπορεί να προκαλέσουν απροσδόκητη συμπεριφορά.", + "strategy": "Στρατηγική", + "deduplication-interval": "Διάστημα απαλοιφής διπλότυπων", + "deduplication-interval-required": "Απαιτείται διάστημα απαλοιφής διπλότυπων", + "deduplication-interval-min-max-range": "Το διάστημα πρέπει να είναι τουλάχιστον 1 δευτερόλεπτο και το μέγιστο 1 ημέρα", + "strategy-type": { + "every-message": "Σε κάθε μήνυμα", + "skip": "Παράλειψη", + "deduplicate": "Απαλοιφή διπλότυπων", + "web-sockets-only": "Μόνο WebSockets" + }, + "time-series": "Χρονοσειρές", + "latest": "Τελευταίες τιμές", + "web-sockets": "WebSockets", + "calculated-fields": "Υπολογιζόμενα πεδία" }, - "operation": { - "operation": "Εργασία", - "operations": "Εργασίες", - "operations-required": "Τουλάχιστον μία εργασία πρέπει να καθοριστεί.", - "enter-operation": "Εισαγωγή εργασίας", - "no-operations-matching": "Δεν βρέθηκαν εργασίες που να ταιριάζουν '{{operation}}'.", - "display-type": { - "ALL": "'Ολες", - "CREATE": "Δημιουργία", - "READ": "Ανάγνωση", - "WRITE": "Εγγραφή", - "DELETE": "Διαγραφή", - "ASSIGN_TO_CUSTOMER": "Ανάθεση σε Πελάτη", - "UNASSIGN_FROM_CUSTOMER": "Αποσύνδεση από Πελάτη", - "RPC_CALL": "Κλήση RPC", - "READ_CREDENTIALS": "Ανάγνωση Διαπιστευτηρίων", - "WRITE_CREDENTIALS": "Γράψτε Διαπιστευτήρια", - "READ_ATTRIBUTES": "Ανάγνωση Χαρακτηριστικών", - "WRITE_ATTRIBUTES": "Γράψτε Χαρακτηριστικά", - "READ_TELEMETRY": "Ανάγνωση Τηλεμετρίας", - "WRITE_TELEMETRY": "Γράψτε Τηλεμετρία", - "CLAIM_DEVICES": "Αιτήματα Συσκευών", - "IMPERSONATE": "Impersonate", - "CHANGE_OWNER": "Αλλαγή Κατόχου", - "ADD_TO_GROUP": "Προσθήκη στην Ομάδα", - "REMOVE_FROM_GROUP": "Αφαίρεση από την Ομάδα" - } + "save-attribute": { + "processing-settings": "Ρυθμίσεις επεξεργασίας", + "processing-settings-hint": "Καθορίστε πώς θα γίνει η επεξεργασία των εισερχόμενων μηνυμάτων.", + "advanced-settings-hint": "Να είστε προσεκτικοί κατά τη διαμόρφωση στρατηγικών.", + "strategy": "Στρατηγική", + "deduplication-interval": "Διάστημα απαλοιφής διπλότυπων", + "deduplication-interval-required": "Απαιτείται διάστημα απαλοιφής διπλότυπων", + "deduplication-interval-min-max-range": "Το διάστημα πρέπει να είναι τουλάχιστον 1 δευτερόλεπτο και το μέγιστο 1 ημέρα", + "scope": "Πεδίο", + "strategy-type": { + "every-message": "Σε κάθε μήνυμα", + "skip": "Παράλειψη", + "deduplicate": "Απαλοιφή διπλότυπων", + "web-sockets-only": "Μόνο WebSockets" + }, + "attributes": "Ιδιότητες" + }, + "key-val": { + "key": "Κλειδί", + "value": "Τιμή", + "see-examples": "Δείτε παραδείγματα.", + "remove-entry": "Αφαίρεση καταχώρησης", + "remove-mapping-entry": "Αφαίρεση χαρτογράφησης", + "add-mapping-entry": "Προσθήκη χαρτογράφησης", + "add-entry": "Προσθήκη καταχώρησης", + "copy-key-values-from": "Αντιγραφή τιμών από", + "delete-key-values": "Διαγραφή τιμών", + "delete-key-values-from": "Διαγραφή τιμών από", + "at-least-one-key-error": "Πρέπει να επιλεγεί τουλάχιστον ένα κλειδί.", + "unique-key-value-pair-error": "Το '{{keyText}}' πρέπει να διαφέρει από το '{{valText}}'!" + }, + "mail-body-types": { + "plain-text": "Απλό κείμενο", + "html": "HTML", + "dynamic": "Δυναμικό", + "use-body-type-template": "Χρήση προτύπου τύπου σώματος", + "plain-text-description": "Απλό, μη μορφοποιημένο κείμενο χωρίς στυλ.", + "html-text-description": "Υποστηρίζει ετικέτες HTML για μορφοποίηση, συνδέσμους και εικόνες.", + "dynamic-text-description": "Υποστηρίζει δυναμική εναλλαγή μεταξύ Plain Text και HTML βάσει προτύπων.", + "after-template-evaluation-hint": "Μετά την αξιολόγηση, αν η τιμή είναι true τότε είναι HTML, αλλιώς Plain text." } }, - "scheduler": { - "scheduler": "Προγραμματιστής", - "scheduler-event": "Προγραμματισμένο γεγονός", - "select-scheduler-event": "Επιλέξτε προγραμματισμένα γεγονότα που να ταιριάζουν '{{entity}}'", - "scheduler-event-required": "Απαιτείται προγραμματισμένο γεγονός", - "management": "Διαχείριση Προγράμματος", - "scheduler-events": "Προγραμματισμένα γεγονότα", - "add-scheduler-event": "Προσθήκη προγραμματισμένου γεγονότος", - "search-scheduler-events": "Αναζήτηση προγραμματισμένων γεγονότων", - "created-time": "Χρόνος που δημιουργήθηκε", - "name": "Όνομα", - "type": "Τύπος", - "created_customer": "Πελάτης που δημιουργήθηκε", - "edit-scheduler-event": "Επεξεργασία προγραμματισμένου γεγονότος", - "view-scheduler-event": "Προβολή προγραμματισμένου γεγονότος", - "delete-scheduler-event": "Διαγραφή προγραμματισμένου γεγονότος", - "no-scheduler-events": "Δεν βρέθηκαν προγραμματισμένα γεγονότα", - "selected-scheduler-events": "{ count, plural, =1 {1 scheduler event} other {# scheduler events} } επιλέχθηκαν", - "delete-scheduler-event-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε το προγραμματισμένο γεγονός '{{schedulerEventName}}';", - "delete-scheduler-event-text": "Προσοχή, μετά την επιβεβαίωση το προγραμματισμένο γεγονός και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-scheduler-events-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 scheduler event} other {# scheduler events} };", - "delete-scheduler-events-text": "Προσοχή, μετά την επιβεβαίωση όλα τα επιλεγμένα προγραμματισμένα γεγονότα θα καταργηθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "create": "Δημιουργία προγραμματισμένου γεγονότος", - "edit": "Επεξεργασία προγραμματισμένου γεγονότος", - "view": "Προβολή προγραμματισμένου γεγονότος", - "name-required": "Απαιτείται Όνομα", - "configuration": "Διαμόρφωση", - "schedule": "Πρόγραμμα", - "start-time": "Ώρα έναρξης", - "repeat": "Επανάληψη", - "repeats": "Επαναλήψεις", - "daily": "Καθημερινά", - "weekly": "Εβδομαδιαία", - "timer": "Χρονομετρητής", - "repeats-required": "Απαιτούνται επαναλήψεις.", - "repeat-on": "Επανάληψη σε", - "repeat-every": "Επανάληψη κάθε", - "ends-on": "Τελειώνει σε", - "sunday-label": "K", - "monday-label": "Δ", - "tuesday-label": "T", - "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": "Επιλογή κατάστασης πίνακα ελέγχου", - "hours": "Ώρες", - "minutes": "Λεπτά", - "seconds": "Δευτερόλεπτα", - "time-interval-required": "Απαιτείται χρονικό διάστημα", - "time-unit-required": "Απαιτείται μονάδα ώρας" - }, - "report": { - "report-config": "Διαμόρφωση αναφοράς", - "email-config": "Διαμόρφωση email", - "dashboard-state-param": "Τιμή παραμέτρου κατάστασης πίνακα", - "base-url": "Βάση URL", - "base-url-required": "Απαιτείται Βάση URL.", - "use-dashboard-timewindow": "Χρησιμοποιήστε το χρονικό πλαίσιο του πίνακα ελέγχου", - "timewindow": "Χρονικό πλαίσιο", - "name-pattern": "Αναφορά πρότυπου ονόματος", - "name-pattern-required": "Απαιτείται αναφορά πρότυπου ονόματος", - "type": "Τύπος αναφοράς", - "use-current-user-credentials": "Χρησιμοποιήστε τα τρέχοντα διαπιστευτήρια του χρήστη", - "customer-user-credentials": "Διαπιστευτήρια του χρήστη του πελάτη", - "customer-user-credentials-required": "Απαιτούνται διαπιστευτήρια του χρήστη του πελάτη", - "generate-test-report": "Δημιουργία αναφοράς δοκιμής", - "send-email": "Αποστολή email", - "from": "Από", - "from-required": "Απαιτείται Από.", - "to": "Σε", - "to-required": "Απαιτείται Σε.", - "cc": "Cc", - "bcc": "Bcc", - "subject": "Θέμα", - "subject-required": "Απαιτείται Θέμα.", - "body": "Σώμα", - "body-required": "Απαιτείται Σώμα." - }, - "blob-entity": { - "blob-entity": "Ογκώδης οντότητα", - "select-blob-entity": "Επιλογή ογκώδους οντότητας", - "no-blob-entities-matching": "Δεν βρέθηκαν ογκώδεις οντότητες που να ταιριάζουν '{{entity}}'.", - "blob-entity-required": "Απαιτείται ογκώδης οντότητα", - "files": "Αρχεία", - "search": "Αναζήτηση αρχείων", - "clear-search": "Εκκαθάριση αναζήτησης", - "no-blob-entities-prompt": "Δεν βρέθηκαν αρχεία", - "report": "Αναφορά", - "created-time": "Χρόνος που δημιουργήθηκε", + "timezone": { + "timezone": "Ζώνη ώρας", + "select-timezone": "Επιλέξτε ζώνη ώρας", + "no-timezones-matching": "Δεν βρέθηκαν ζώνες ώρας που να ταιριάζουν με '{{timezone}}'.", + "timezone-required": "Απαιτείται ζώνη ώρας.", + "browser-time": "Ώρα προγράμματος περιήγησης" + }, + "queue": { + "queue-name": "Ουρά", + "no-queues-found": "Δεν βρέθηκαν ουρές.", + "no-queues-matching": "Δεν βρέθηκαν ουρές που να ταιριάζουν με '{{queue}}'.", + "select-name": "Επιλέξτε όνομα ουράς", "name": "Όνομα", - "type": "Τύπος", - "created_customer": "Δημιουργήθηκε από Πελάτη", - "download-blob-entity": "Λήψη αρχείου", - "delete-blob-entity": "Διαγραφή αρχείου", - "delete-blob-entity-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε το αρχείο '{{blobEntityName}}';", - "delete-blob-entity-text": "Προσοχή, μετά την επιβεβαίωση τα δεδομένα αρχείου θα διαγραφούν οριστικά." + "name-required": "Απαιτείται όνομα ουράς!", + "name-unique": "Το όνομα ουράς δεν είναι μοναδικό!", + "name-pattern": "Το όνομα ουράς περιέχει χαρακτήρες εκτός ASCII αλφαριθμητικών, '.', '_' και '-'!", + "queue-required": "Απαιτείται ουρά!", + "topic-required": "Απαιτείται θέμα ουράς!", + "poll-interval-required": "Απαιτείται διάστημα ανάκτησης!", + "poll-interval-min-value": "Η τιμή διαστήματος ανάκτησης δεν μπορεί να είναι μικρότερη από 1", + "partitions-required": "Απαιτούνται κατατμήσεις!", + "partitions-min-value": "Η τιμή κατατμήσεων δεν μπορεί να είναι μικρότερη από 1", + "pack-processing-timeout-required": "Απαιτείται χρονικό όριο επεξεργασίας", + "pack-processing-timeout-min-value": "Η τιμή χρονικού ορίου επεξεργασίας δεν μπορεί να είναι μικρότερη από 1", + "batch-size-required": "Απαιτείται μέγεθος παρτίδας!", + "batch-size-min-value": "Η τιμή μεγέθους παρτίδας δεν μπορεί να είναι μικρότερη από 1", + "retries-required": "Απαιτούνται προσπάθειες!", + "retries-min-value": "Ο αριθμός προσπαθειών δεν μπορεί να είναι αρνητικός", + "failure-percentage-required": "Απαιτείται ποσοστό αποτυχίας!", + "failure-percentage-min-value": "Το ποσοστό αποτυχίας δεν μπορεί να είναι μικρότερο από 0", + "failure-percentage-max-value": "Το ποσοστό αποτυχίας δεν μπορεί να υπερβαίνει το 100", + "pause-between-retries-required": "Απαιτείται παύση μεταξύ προσπαθειών!", + "pause-between-retries-min-value": "Η παύση δεν μπορεί να είναι μικρότερη από 1", + "max-pause-between-retries-required": "Απαιτείται μέγιστη παύση μεταξύ προσπαθειών!", + "max-pause-between-retries-min-value": "Η μέγιστη παύση δεν μπορεί να είναι μικρότερη από 1", + "submit-strategy-type-required": "Απαιτείται τύπος στρατηγικής αποστολής!", + "processing-strategy-type-required": "Απαιτείται τύπος στρατηγικής επεξεργασίας!", + "queues": "Ουρές", + "selected-queues": "{ count, plural, =1 {1 ουρά} other {# ουρές} } επιλέχθηκαν", + "delete-queue-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την ουρά '{{queueName}}';", + "delete-queues-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 ουρά} other {# ουρές} };", + "delete-queue-text": "Προσοχή, μετά την επιβεβαίωση η ουρά και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", + "delete-queues-text": "Μετά την επιβεβαίωση όλες οι επιλεγμένες ουρές θα διαγραφούν και δεν θα είναι προσβάσιμες.", + "search": "Αναζήτηση ουράς", + "add": "Προσθήκη ουράς", + "details": "Λεπτομέρειες ουράς", + "topic": "Θέμα", + "submit-settings": "Ρυθμίσεις αποστολής", + "submit-strategy": "Τύπος στρατηγικής *", + "grouping-parameter": "Παράμετρος ομαδοποίησης", + "processing-settings": "Ρυθμίσεις επαναλήψεων", + "processing-strategy": "Τύπος επεξεργασίας *", + "retries-settings": "Ρυθμίσεις επαναλήψεων", + "polling-settings": "Ρυθμίσεις ανάκτησης", + "batch-processing": "Επεξεργασία παρτίδας", + "poll-interval": "Διάστημα ανάκτησης", + "partitions": "Κατατμήσεις", + "immediate-processing": "Άμεση επεξεργασία", + "consumer-per-partition": "Ανάκτηση ανά καταναλωτή", + "consumer-per-partition-hint": "Ενεργοποίηση ξεχωριστού καταναλωτή για κάθε κατάτμηση", + "duplicate-msg-to-all-partitions": "Αντιγραφή μηνύματος σε όλες τις κατατμήσεις", + "processing-timeout": "Χρονικό όριο επεξεργασίας, ms", + "batch-size": "Μέγεθος παρτίδας", + "retries": "Αριθμός προσπαθειών (0 – απεριόριστο)", + "failure-percentage": "Ποσοστό αποτυχίας, %", + "pause-between-retries": "Επανάληψη μετά από, δευτ.", + "max-pause-between-retries": "Επιπλέον επανάληψη μετά από, δευτ.", + "delete": "Διαγραφή ουράς", + "copyId": "Αντιγραφή Id ουράς", + "idCopiedMessage": "Το Id της ουράς αντιγράφηκε στο πρόχειρο", + "description": "Περιγραφή", + "description-hint": "Αυτό το κείμενο θα εμφανίζεται στην περιγραφή ουράς αντί για τη στρατηγική", + "alt-description": "Στρατηγική αποστολής: {{submitStrategy}}, Στρατηγική επεξεργασίας: {{processingStrategy}}", + "custom-properties": "Προσαρμοσμένες ιδιότητες", + "custom-properties-hint": "Προσαρμοσμένες ιδιότητες δημιουργίας ουράς (θέματος), π.χ. 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies": { + "sequential-by-originator-label": "Σειριακά ανά δημιουργό", + "sequential-by-originator-hint": "Νέο μήνυμα για π.χ. συσκευή Α δεν υποβάλλεται μέχρι να αναγνωριστεί το προηγούμενο μήνυμα για τη συσκευή Α", + "sequential-by-tenant-label": "Σειριακά ανά ενοικιαστή", + "sequential-by-tenant-hint": "Νέο μήνυμα για π.χ. ενοικιαστή Α δεν υποβάλλεται μέχρι να αναγνωριστεί το προηγούμενο μήνυμα για τον ενοικιαστή Α", + "sequential-label": "Σειριακό", + "sequential-hint": "Νέο μήνυμα δεν υποβάλλεται μέχρι να αναγνωριστεί το προηγούμενο", + "burst-label": "Ροή", + "burst-hint": "Όλα τα μηνύματα υποβάλλονται με τη σειρά άφιξής τους", + "batch-label": "Παρτίδα", + "batch-hint": "Νέα παρτίδα δεν υποβάλλεται μέχρι να αναγνωριστεί η προηγούμενη", + "skip-all-failures-label": "Παράλειψη όλων αποτυχιών", + "skip-all-failures-hint": "Παράβλεψη όλων αποτυχιών", + "skip-all-failures-and-timeouts-label": "Παράλειψη αποτυχιών και χρονικών ορίων", + "skip-all-failures-and-timeouts-hint": "Παράβλεψη όλων αποτυχιών και χρονικών ορίων", + "retry-all-label": "Επανάληψη όλων", + "retry-all-hint": "Επανάληψη όλων των μηνυμάτων της παρτίδας", + "retry-failed-label": "Επανάληψη αποτυχημένων", + "retry-failed-hint": "Επανάληψη όλων των αποτυχημένων μηνυμάτων της παρτίδας", + "retry-timeout-label": "Επανάληψη λόγω χρονικού ορίου", + "retry-timeout-hint": "Επανάληψη όλων των μηνυμάτων που απέτυχαν λόγω χρονικού ορίου", + "retry-failed-and-timeout-label": "Επανάληψη αποτυχημένων και χρονομετρημένων", + "retry-failed-and-timeout-hint": "Επανάληψη όλων των αποτυχημένων και χρονομετρημένων μηνυμάτων" + } }, - "timezone": { - "timezone": "Ζώνη Ώρας", - "select-timezone": "Επιλογή Ζώνης Ώρας", - "no-timezones-matching": "Δεν βρέθηκαν Ζώνες ώρας που να ταιριάζουν'{{timezone}}' .", - "timezone-required": "Απαιτείται Ζώνη Ώρας." + "queue-statistics": { + "queue-statistics": "Στατιστικά ουράς", + "no-queue-statistics-matching": "Δεν βρέθηκαν στατιστικά ουράς που να ταιριάζουν με '{{entity}}'.", + "queue-statistics-required": "Απαιτούνται στατιστικά ουράς.", + "list-of-queue-statistics": "{ count, plural, =1 {Ένα στατιστικό ουράς} other {Λίστα με # στατιστικά ουράς} }", + "selected-queue-statistics": "{ count, plural, =1 {1 στατιστικό ουράς} other {# στατιστικά ουράς} } επιλέχθηκαν", + "no-queue-statistics-text": "Δεν βρέθηκαν στατιστικά ουράς", + "queue-statistics-starts-with": "Στατιστικά ουράς με ονόματα που ξεκινούν με '{{prefix}}'" + }, + "server-error": { + "general": "Γενικό σφάλμα διακομιστή", + "authentication": "Σφάλμα ταυτοποίησης", + "jwt-token-expired": "Το JWT token έχει λήξει", + "tenant-trial-expired": "Η δοκιμή του ενοικιαστή έχει λήξει", + "credentials-expired": "Τα διαπιστευτήρια έχουν λήξει", + "permission-denied": "Άρνηση πρόσβασης", + "invalid-arguments": "Μη έγκυρα επιχειρήματα", + "bad-request-params": "Μη έγκυρες παράμετροι αιτήματος", + "item-not-found": "Το στοιχείο δεν βρέθηκε", + "too-many-requests": "Πάρα πολλά αιτήματα", + "too-many-updates": "Πάρα πολλές ενημερώσεις" }, "tenant": { - "tenant": "Μισθωτής", - "tenants": "Μισθωτές", - "management": "Διαχείριση Μισθωτών", - "add": "Πρόσθεση Μισθωτή", + "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 tenant} other {# tenants} };", - "delete-tenants-action-title": "Διαγραφή { count, plural, =1 {1 tenant} other {# tenants} }", - "delete-tenants-text": "Προσοχή, μετά την επιβεβαίωση όλοι οι επιλεγμένοι Μισθωτές θα αφαιρεθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", + "manage-tenant-admins": "Διαχείριση διαχειριστών ενοικιαστή", + "delete": "Διαγραφή ενοικιαστή", + "add-tenant-text": "Προσθήκη νέου ενοικιαστή", + "no-tenants-text": "Δεν βρέθηκαν ενοικιαστές", + "tenant-details": "Λεπτομέρειες ενοικιαστή", + "title-max-length": "Ο τίτλος πρέπει να είναι μικρότερος από 256 χαρακτήρες", + "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": "Απαιτείται Τίτλος.", + "title-required": "Απαιτείται τίτλος.", "description": "Περιγραφή", "details": "Λεπτομέρειες", - "events": "Γεγονότα", - "copyId": "Αντιγραφή Ταυτότητας του Μισθωτή", - "idCopiedMessage": "Η Ταυτότητα του Μισθωτή έχει αντιγραφεί στο πρόχειρο", - "select-tenant": "Επιλογή Μισθωτή", - "no-tenants-matching": "Δεν βρέθηκαν Μισθωτές που να ταιριάζουν '{{entity}}'.", - "tenant-required": "Απαιτείται Μισθωτής", - "selected-tenants": "{ count, plural, =1 {1 tenant} other {# tenants} } επιλέχθηκαν", - "search": "Αναζήτηση Μισθωτών", - "allow-white-labeling": "Επιτρέπεται Προσαρμογή Εμφάνισης", - "allow-customer-white-labeling": "Επιτρέπεται Προσαρμογή Εμφάνισης Πελάτη" + "events": "Συμβάντα", + "copyId": "Αντιγραφή Id ενοικιαστή", + "idCopiedMessage": "Το Id του ενοικιαστή αντιγράφηκε στο πρόχειρο", + "select-tenant": "Επιλέξτε ενοικιαστή", + "no-tenants-matching": "Δεν βρέθηκαν ενοικιαστές που να ταιριάζουν με '{{entity}}'.", + "tenant-required": "Απαιτείται ενοικιαστής", + "search": "Αναζήτηση ενοικιαστών", + "selected-tenants": "{ count, plural, =1 {1 ενοικιαστής} other {# ενοικιαστές} } επιλέχθηκαν", + "isolated-tb-rule-engine": "Χρήση απομονωμένων ουρών Rule Engine του ThingsBoard", + "isolated-tb-rule-engine-details": "Κάθε ενοικιαστής θα έχει αφιερωμένες ουρές Rule Engine" + }, + "tenant-profile": { + "tenant-profile": "Προφίλ ενοικιαστή", + "tenant-profiles": "Προφίλ ενοικιαστών", + "add": "Προσθήκη προφίλ ενοικιαστή", + "add-profile": "Προσθήκη προφίλ", + "debug": "Αποσφαλμάτωση", + "edit": "Επεξεργασία προφίλ ενοικιαστή", + "tenant-profile-details": "Λεπτομέρειες προφίλ ενοικιαστή", + "no-tenant-profiles-text": "Δεν βρέθηκαν προφίλ ενοικιαστών", + "name-max-length": "Το όνομα πρέπει να είναι μικρότερο από 256 χαρακτήρες", + "search": "Αναζήτηση προφίλ ενοικιαστών", + "selected-tenant-profiles": "{ count, plural, =1 {1 προφίλ ενοικιαστή} other {# προφίλ ενοικιαστών} } επιλέχθηκαν", + "no-tenant-profiles-matching": "Δεν βρέθηκε προφίλ ενοικιαστή που να ταιριάζει με '{{entity}}'.", + "tenant-profile-required": "Απαιτείται προφίλ ενοικιαστή", + "idCopiedMessage": "Το Id του προφίλ ενοικιαστή αντιγράφηκε στο πρόχειρο", + "set-default": "Ορισμός προεπιλεγμένου προφίλ ενοικιαστή", + "delete": "Διαγραφή προφίλ ενοικιαστή", + "copyId": "Αντιγραφή Id προφίλ ενοικιαστή", + "name": "Όνομα", + "name-required": "Το όνομα είναι υποχρεωτικό.", + "data": "Δεδομένα προφίλ", + "profile-configuration": "Διαμόρφωση προφίλ", + "description": "Περιγραφή", + "default": "Προεπιλογή", + "delete-tenant-profile-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το προφίλ ενοικιαστή '{{tenantProfileName}}';", + "delete-tenant-profile-text": "Προσοχή, μετά την επιβεβαίωση το προφίλ ενοικιαστή και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", + "delete-tenant-profiles-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 προφίλ ενοικιαστή} other {# προφίλ ενοικιαστών} };", + "delete-tenant-profiles-text": "Προσοχή, μετά την επιβεβαίωση όλα τα επιλεγμένα προφίλ ενοικιαστών θα αφαιρεθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", + "set-default-tenant-profile-title": "Είστε βέβαιοι ότι θέλετε να ορίσετε το προφίλ ενοικιαστή '{{tenantProfileName}}' ως προεπιλογή;", + "set-default-tenant-profile-text": "Μετά την επιβεβαίωση, το προφίλ ενοικιαστή θα επισημανθεί ως προεπιλεγμένο και θα χρησιμοποιηθεί για νέους ενοικιαστές χωρίς ορισμένο προφίλ.", + "no-tenant-profiles-found": "Δεν βρέθηκαν προφίλ ενοικιαστών.", + "create-new-tenant-profile": "Δημιουργήστε ένα νέο!", + "create-tenant-profile": "Δημιουργία νέου προφίλ ενοικιαστή", + "import": "Εισαγωγή προφίλ ενοικιαστή", + "export": "Εξαγωγή προφίλ ενοικιαστή", + "export-failed-error": "Αδυναμία εξαγωγής προφίλ ενοικιαστή: {{error}}", + "tenant-profile-file": "Αρχείο προφίλ ενοικιαστή", + "invalid-tenant-profile-file-error": "Αδυναμία εισαγωγής προφίλ ενοικιαστή: Μη έγκυρη δομή δεδομένων προφίλ.", + "advanced-settings": "Προηγμένες ρυθμίσεις", + "entities": "Οντότητες", + "rule-engine": "Μηχανή κανόνων", + "time-to-live": "Διάρκεια ζωής", + "calculated-fields": "Υπολογισμένα πεδία", + "alarms-and-notifications": "Συναγερμοί και ειδοποιήσεις", + "ota-files-in-bytes": "Αρχεία", + "ws-title": "WS", + "unlimited": "(0 - απεριόριστο)", + "maximum-devices": "Μέγιστος αριθμός συσκευών", + "maximum-devices-required": "Απαιτείται μέγιστος αριθμός συσκευών.", + "maximum-devices-range": "Ο μέγιστος αριθμός συσκευών δεν μπορεί να είναι αρνητικός", + "maximum-assets": "Μέγιστος αριθμός πόρων", + "maximum-assets-required": "Απαιτείται μέγιστος αριθμός πόρων.", + "maximum-assets-range": "Ο μέγιστος αριθμός πόρων δεν μπορεί να είναι αρνητικός", + "maximum-customers": "Μέγιστος αριθμός πελατών", + "maximum-customers-required": "Απαιτείται μέγιστος αριθμός πελατών.", + "maximum-customers-range": "Ο μέγιστος αριθμός πελατών δεν μπορεί να είναι αρνητικός", + "maximum-users": "Μέγιστος αριθμός χρηστών", + "maximum-users-required": "Απαιτείται μέγιστος αριθμός χρηστών.", + "maximum-users-range": "Ο μέγιστος αριθμός χρηστών δεν μπορεί να είναι αρνητικός", + "maximum-dashboards": "Μέγιστος αριθμός πινάκων ελέγχου", + "maximum-dashboards-required": "Απαιτείται μέγιστος αριθμός πινάκων ελέγχου.", + "maximum-dashboards-range": "Ο μέγιστος αριθμός πινάκων ελέγχου δεν μπορεί να είναι αρνητικός", + "maximum-edges": "Μέγιστος αριθμός Edge", + "maximum-edges-required": "Απαιτείται μέγιστος αριθμός Edge.", + "maximum-edges-range": "Ο μέγιστος αριθμός Edge δεν μπορεί να είναι αρνητικός", + "maximum-rule-chains": "Μέγιστος αριθμός αλυσίδων κανόνων", + "maximum-rule-chains-required": "Απαιτείται μέγιστος αριθμός αλυσίδων κανόνων.", + "maximum-rule-chains-range": "Ο μέγιστος αριθμός αλυσίδων κανόνων δεν μπορεί να είναι αρνητικός", + "maximum-resources-sum-data-size": "Μέγιστο συνολικό μέγεθος αρχείων πόρων (bytes)", + "maximum-resources-sum-data-size-required": "Απαιτείται μέγιστο συνολικό μέγεθος αρχείων πόρων.", + "maximum-resources-sum-data-size-range": "Το μέγιστο συνολικό μέγεθος αρχείων πόρων δεν μπορεί να είναι αρνητικό", + "maximum-resource-size": "Μέγιστο μέγεθος αρχείου πόρου (bytes)", + "maximum-resource-size-required": "Απαιτείται μέγιστο μέγεθος αρχείου πόρου", + "maximum-resource-size-range": "Το μέγιστο μέγεθος αρχείου πόρου δεν μπορεί να είναι αρνητικό", + "maximum-ota-packages-sum-data-size": "Μέγιστο συνολικό μέγεθος OTA αρχείων (bytes)", + "maximum-ota-package-sum-data-size-required": "Απαιτείται μέγιστο συνολικό μέγεθος OTA αρχείων.", + "maximum-ota-package-sum-data-size-range": "Το μέγιστο συνολικό μέγεθος OTA αρχείων δεν μπορεί να είναι αρνητικό", + "maximum-debug-duration-min": "Μέγιστη διάρκεια αποσφαλμάτωσης (λεπτά)", + "maximum-debug-duration-min-range": "Η μέγιστη διάρκεια αποσφαλμάτωσης δεν μπορεί να είναι αρνητική", + "rest-requests-for-tenant": "REST αιτήματα για τον ενοικιαστή", + "transport-tenant-telemetry-msg-rate-limit": "Μηνύματα τηλεμετρίας μεταφοράς ενοικιαστή", + "transport-tenant-telemetry-data-points-rate-limit": "Σημεία δεδομένων τηλεμετρίας μεταφοράς ενοικιαστή", + "transport-device-msg-rate-limit": "Μηνύματα μεταφοράς συσκευής", + "transport-device-telemetry-msg-rate-limit": "Μηνύματα τηλεμετρίας μεταφοράς συσκευής", + "transport-device-telemetry-data-points-rate-limit": "Σημεία δεδομένων τηλεμετρίας μεταφοράς συσκευής", + "transport-gateway-msg-rate-limit": "Μηνύματα μεταφοράς gateway", + "transport-gateway-telemetry-msg-rate-limit": "Μηνύματα τηλεμετρίας μεταφοράς gateway", + "transport-gateway-telemetry-data-points-rate-limit": "Σημεία δεδομένων τηλεμετρίας μεταφοράς gateway", + "transport-gateway-device-msg-rate-limit": "Μηνύματα συσκευής gateway", + "transport-gateway-device-telemetry-msg-rate-limit": "Μηνύματα τηλεμετρίας συσκευής gateway", + "transport-gateway-device-telemetry-data-points-rate-limit": "Σημεία δεδομένων τηλεμετρίας συσκευής gateway", + "tenant-entity-export-rate-limit": "Δημιουργία έκδοσης οντότητας", + "tenant-entity-import-rate-limit": "Φόρτωση έκδοσης οντότητας", + "tenant-notification-request-rate-limit": "Αιτήματα ειδοποιήσεων", + "tenant-notification-requests-per-rule-rate-limit": "Αιτήματα ειδοποιήσεων ανά κανόνα", + "max-calculated-fields": "Μέγιστος αριθμός υπολογισμένων πεδίων ανά οντότητα", + "max-calculated-fields-range": "Ο μέγιστος αριθμός υπολογισμένων πεδίων δεν μπορεί να είναι αρνητικός", + "max-calculated-fields-required": "Απαιτείται ο μέγιστος αριθμός υπολογισμένων πεδίων", + "max-data-points-per-rolling-arg": "Μέγιστος αριθμός σημείων δεδομένων σε rolling παραμέτρους", + "max-data-points-per-rolling-arg-range": "Ο μέγιστος αριθμός σημείων δεδομένων δεν μπορεί να είναι αρνητικός", + "max-data-points-per-rolling-arg-required": "Απαιτείται μέγιστος αριθμός σημείων δεδομένων σε rolling παραμέτρους", + "max-arguments-per-cf": "Μέγιστος αριθμός παραμέτρων ανά υπολογισμένο πεδίο", + "max-arguments-per-cf-range": "Ο μέγιστος αριθμός παραμέτρων δεν μπορεί να είναι αρνητικός", + "max-arguments-per-cf-required": "Απαιτείται μέγιστος αριθμός παραμέτρων ανά υπολογισμένο πεδίο", + "max-state-size": "Μέγιστο μέγεθος κατάστασης σε KB", + "max-state-size-range": "Το μέγιστο μέγεθος κατάστασης δεν μπορεί να είναι αρνητικό", + "max-state-size-required": "Απαιτείται μέγιστο μέγεθος κατάστασης", + "max-value-argument-size": "Μέγιστο μέγεθος παραμέτρου μονής τιμής σε KB", + "max-value-argument-size-range": "Το μέγιστο μέγεθος παραμέτρου μονής τιμής δεν μπορεί να είναι αρνητικό", + "max-value-argument-size-required": "Απαιτείται μέγιστο μέγεθος παραμέτρου μονής τιμής", + "max-transport-messages": "Μέγιστος αριθμός μηνυμάτων μεταφοράς", + "max-transport-messages-required": "Απαιτείται μέγιστος αριθμός μηνυμάτων μεταφοράς", + "max-transport-messages-range": "Ο μέγιστος αριθμός μηνυμάτων μεταφοράς δεν μπορεί να είναι αρνητικός", + "max-transport-data-points": "Μέγιστος αριθμός σημείων δεδομένων μεταφοράς", + "max-transport-data-points-required": "Απαιτείται μέγιστος αριθμός σημείων δεδομένων μεταφοράς", + "max-transport-data-points-range": "Ο μέγιστος αριθμός σημείων δεδομένων μεταφοράς δεν μπορεί να είναι αρνητικός", + "max-r-e-executions": "Μέγιστος αριθμός εκτελέσεων Rule Engine", + "max-r-e-executions-required": "Απαιτείται μέγιστος αριθμός εκτελέσεων Rule Engine", + "max-r-e-executions-range": "Ο μέγιστος αριθμός εκτελέσεων Rule Engine δεν μπορεί να είναι αρνητικός", + "max-j-s-executions": "Μέγιστος αριθμός εκτελέσεων JavaScript", + "max-j-s-executions-required": "Απαιτείται μέγιστος αριθμός εκτελέσεων JavaScript", + "max-j-s-executions-range": "Ο μέγιστος αριθμός εκτελέσεων JavaScript δεν μπορεί να είναι αρνητικός", + "max-tbel-executions": "Μέγιστος αριθμός εκτελέσεων TBEL", + "max-tbel-executions-required": "Απαιτείται μέγιστος αριθμός εκτελέσεων TBEL", + "max-tbel-executions-range": "Ο μέγιστος αριθμός εκτελέσεων TBEL δεν μπορεί να είναι αρνητικός", + "max-d-p-storage-days": "Μέγιστες ημέρες αποθήκευσης σημείων δεδομένων", + "max-d-p-storage-days-required": "Απαιτούνται μέγιστες ημέρες αποθήκευσης σημείων δεδομένων", + "max-d-p-storage-days-range": "Οι μέγιστες ημέρες αποθήκευσης σημείων δεδομένων δεν μπορεί να είναι αρνητικές", + "default-storage-ttl-days": "Προεπιλεγμένες ημέρες TTL αποθήκευσης", + "default-storage-ttl-days-required": "Απαιτούνται προεπιλεγμένες ημέρες TTL αποθήκευσης", + "default-storage-ttl-days-range": "Οι προεπιλεγμένες ημέρες TTL αποθήκευσης δεν μπορεί να είναι αρνητικές", + "alarms-ttl-days": "Ημέρες TTL συναγερμών", + "alarms-ttl-days-required": "Απαιτούνται ημέρες TTL συναγερμών", + "alarms-ttl-days-days-range": "Οι ημέρες TTL συναγερμών δεν μπορεί να είναι αρνητικές", + "rpc-ttl-days": "Ημέρες TTL RPC", + "rpc-ttl-days-required": "Απαιτούνται ημέρες TTL RPC", + "rpc-ttl-days-days-range": "Οι ημέρες TTL RPC δεν μπορεί να είναι αρνητικές", + "queue-stats-ttl-days": "Ημέρες TTL στατιστικών ουράς", + "queue-stats-ttl-days-required": "Απαιτούνται ημέρες TTL στατιστικών ουράς", + "queue-stats-ttl-days-range": "Οι ημέρες TTL στατιστικών ουράς δεν μπορεί να είναι αρνητικές", + "rule-engine-exceptions-ttl-days": "Ημέρες TTL εξαιρέσεων Rule Engine", + "rule-engine-exceptions-ttl-days-required": "Απαιτούνται ημέρες TTL εξαιρέσεων Rule Engine", + "rule-engine-exceptions-ttl-days-range": "Οι ημέρες TTL εξαιρέσεων Rule Engine δεν μπορεί να είναι αρνητικές", + "max-rule-node-executions-per-message": "Μέγιστος αριθμός εκτελέσεων rule node ανά μήνυμα", + "max-rule-node-executions-per-message-required": "Απαιτείται μέγιστος αριθμός εκτελέσεων rule node ανά μήνυμα", + "max-rule-node-executions-per-message-range": "Ο μέγιστος αριθμός εκτελέσεων rule node ανά μήνυμα δεν μπορεί να είναι αρνητικός", + "max-emails": "Μέγιστος αριθμός αποσταλμένων emails", + "max-emails-required": "Απαιτείται μέγιστος αριθμός αποσταλμένων emails", + "max-emails-range": "Ο μέγιστος αριθμός αποσταλμένων emails δεν μπορεί να είναι αρνητικός", + "sms-enabled": "SMS ενεργοποιημένο", + "max-sms": "Μέγιστος αριθμός αποσταλμένων SMS", + "max-sms-required": "Απαιτείται μέγιστος αριθμός αποσταλμένων SMS.", + "max-sms-range": "Ο μέγιστος αριθμός αποσταλμένων SMS δεν μπορεί να είναι αρνητικός", + "max-created-alarms": "Μέγιστος αριθμός δημιουργημένων συναγερμών", + "max-created-alarms-required": "Απαιτείται μέγιστος αριθμός δημιουργημένων συναγερμών.", + "max-created-alarms-range": "Ο μέγιστος αριθμός δημιουργημένων συναγερμών δεν μπορεί να είναι αρνητικός", + "no-queue": "Δεν έχει διαμορφωθεί ουρά", + "add-queue": "Προσθήκη ουράς", + "queues-with-count": "Ουρές ({{count}})", + "tenant-rest-limits": "Αιτήματα REST για ενοικιαστή", + "customer-rest-limits": "Αιτήματα REST για πελάτη", + "incorrect-pattern-for-rate-limits": "Η μορφή είναι ζεύγη χωρητικότητας και περιόδου (σε δευτερόλεπτα) διαχωρισμένα με κόμμα και άνω-κάτω τελεία, π.χ. 100:1,2000:60", + "too-small-value-zero": "Η τιμή πρέπει να είναι μεγαλύτερη από 0", + "too-small-value-one": "Η τιμή πρέπει να είναι μεγαλύτερη από 1", + "queue-size-is-limited-by-system-configuration": "Το μέγεθος της ουράς περιορίζεται επίσης από τη διαμόρφωση του συστήματος.", + "cassandra-tenant-limits-configuration": "Ερώτημα Cassandra για ενοικιαστή", + "ws-limit-max-sessions-per-tenant": "Μέγιστος αριθμός συνεδριών ανά ενοικιαστή", + "ws-limit-max-sessions-per-customer": "Μέγιστος αριθμός συνεδριών ανά πελάτη", + "ws-limit-max-sessions-per-regular-user": "Μέγιστος αριθμός συνεδριών ανά χρήστη", + "ws-limit-max-sessions-per-public-user": "Μέγιστος αριθμός συνεδριών ανά δημόσιο χρήστη", + "ws-limit-queue-per-session": "Μέγιστο μέγεθος ουράς ανά συνεδρία", + "ws-limit-max-subscriptions-per-tenant": "Μέγιστος αριθμός εγγραφών ανά ενοικιαστή", + "ws-limit-max-subscriptions-per-customer": "Μέγιστος αριθμός εγγραφών ανά πελάτη", + "ws-limit-max-subscriptions-per-regular-user": "Μέγιστος αριθμός εγγραφών ανά χρήστη", + "ws-limit-max-subscriptions-per-public-user": "Μέγιστος αριθμός εγγραφών ανά δημόσιο χρήστη", + "ws-limit-updates-per-session": "WS ενημερώσεις ανά συνεδρία", + "rate-limits": { + "add-limit": "Προσθήκη ορίου", + "advanced-settings": "Προχωρημένες ρυθμίσεις", + "edit-limit": "Επεξεργασία ορίου", + "but-less-than": "αλλά λιγότερο από", + "calculated-field-debug-event-rate-limit": "Σφάλματα πεδίου υπολογισμού", + "edit-calculated-field-debug-event-rate-limit": "Επεξεργασία ορίων σφαλμάτων πεδίου υπολογισμού", + "edit-transport-tenant-msg-title": "Επεξεργασία ορίων μηνυμάτων μεταφοράς ενοικιαστή", + "edit-transport-tenant-telemetry-msg-title": "Επεξεργασία ορίων τηλεμετρικών μηνυμάτων μεταφοράς ενοικιαστή", + "edit-transport-tenant-telemetry-data-points-title": "Επεξεργασία ορίων σημείων δεδομένων τηλεμετρίας μεταφοράς ενοικιαστή", + "edit-transport-device-msg-title": "Επεξεργασία ορίων μηνυμάτων μεταφοράς συσκευής", + "edit-transport-device-telemetry-msg-title": "Επεξεργασία ορίων τηλεμετρικών μηνυμάτων μεταφοράς συσκευής", + "edit-transport-device-telemetry-data-points-title": "Επεξεργασία ορίων σημείων δεδομένων τηλεμετρίας μεταφοράς συσκευής", + "edit-transport-gateway-msg-title": "Επεξεργασία ορίων μηνυμάτων μεταφοράς πύλης", + "edit-transport-gateway-telemetry-msg-title": "Επεξεργασία ορίων τηλεμετρικών μηνυμάτων μεταφοράς πύλης", + "edit-transport-gateway-telemetry-data-points-title": "Επεξεργασία ορίων σημείων δεδομένων τηλεμετρίας μεταφοράς πύλης", + "edit-transport-gateway-device-msg-title": "Επεξεργασία ορίων μηνυμάτων συσκευής πύλης", + "edit-transport-gateway-device-telemetry-msg-title": "Επεξεργασία ορίων τηλεμετρίας συσκευής πύλης", + "edit-transport-gateway-device-telemetry-data-points-title": "Επεξεργασία ορίων σημείων δεδομένων τηλεμετρίας συσκευής πύλης", + "edit-tenant-rest-limits-title": "Επεξεργασία ορίων αιτημάτων REST για τον ενοικιαστή", + "edit-customer-rest-limits-title": "Επεξεργασία ορίων αιτημάτων REST για πελάτη", + "edit-ws-limit-updates-per-session-title": "Επεξεργασία ορίων ενημερώσεων WS ανά συνεδρία", + "edit-cassandra-tenant-limits-configuration-title": "Επεξεργασία ορίων ερωτημάτων Cassandra για ενοικιαστή", + "edit-tenant-entity-export-rate-limit-title": "Επεξεργασία ορίων δημιουργίας εκδόσεων οντοτήτων", + "edit-tenant-entity-import-rate-limit-title": "Επεξεργασία ορίων φόρτωσης εκδόσεων οντοτήτων", + "edit-tenant-notification-request-rate-limit-title": "Επεξεργασία ορίων αιτημάτων ειδοποίησης", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "Επεξεργασία ορίων αιτημάτων ειδοποίησης ανά κανόνα", + "edit-edge-events-rate-limit": "Επεξεργασία ορίων συμβάντων άκρης", + "edit-edge-events-per-edge-rate-limit": "Επεξεργασία ορίων συμβάντων ανά άκρη", + "edge-events-rate-limit": "Συμβάντα άκρης", + "edge-events-per-edge-rate-limit": "Συμβάντα ανά άκρη", + "edit-edge-uplink-messages-rate-limit": "Επεξεργασία ορίων μηνυμάτων uplink άκρης", + "edit-edge-uplink-messages-per-edge-rate-limit": "Επεξεργασία ορίων μηνυμάτων uplink ανά άκρη", + "edge-uplink-messages-rate-limit": "Μηνύματα uplink άκρης", + "edge-uplink-messages-per-edge-rate-limit": "Μηνύματα uplink ανά άκρη", + "messages-per": "μηνύματα ανά", + "not-set": "Δεν έχει οριστεί", + "number-of-messages": "Αριθμός μηνυμάτων", + "number-of-messages-required": "Απαιτείται αριθμός μηνυμάτων.", + "number-of-messages-min": "Η ελάχιστη τιμή είναι 1.", + "preview": "Προεπισκόπηση", + "per-seconds": "Ανά δευτερόλεπτα", + "per-seconds-required": "Απαιτείται χρονικός ρυθμός.", + "per-seconds-min": "Η ελάχιστη τιμή είναι 1.", + "rate-limits": "Όρια ρυθμού", + "remove-limit": "Αφαίρεση ορίου", + "transport-tenant-msg": "Μηνύματα μεταφοράς ενοικιαστή", + "transport-tenant-telemetry-msg": "Τηλεμετρικά μηνύματα ενοικιαστή", + "transport-tenant-telemetry-data-points": "Σημεία τηλεμετρίας ενοικιαστή", + "transport-device-msg": "Μηνύματα μεταφοράς συσκευής", + "transport-device-telemetry-msg": "Τηλεμετρικά μηνύματα συσκευής", + "transport-device-telemetry-data-points": "Σημεία τηλεμετρίας συσκευής", + "transport-gateway-msg": "Μηνύματα μεταφοράς πύλης", + "transport-gateway-telemetry-msg": "Τηλεμετρικά μηνύματα πύλης", + "transport-gateway-telemetry-data-points": "Σημεία τηλεμετρίας πύλης", + "transport-gateway-device-msg": "Μηνύματα συσκευής πύλης", + "transport-gateway-device-telemetry-msg": "Τηλεμετρικά μηνύματα συσκευής πύλης", + "transport-gateway-device-telemetry-data-points": "Σημεία τηλεμετρίας συσκευής πύλης", + "sec": "δευτ." + } }, "timeinterval": { - "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} }", + "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": "Προηγμένος" + "advanced": "Προχωρημένο", + "custom": "Προσαρμοσμένο", + "predefined": { + "yesterday": "Χθες", + "day-before-yesterday": "Προχθές", + "this-day-last-week": "Αυτή η ημέρα την προηγούμενη εβδομάδα", + "previous-week": "Προηγούμενη εβδομάδα (Κυρ - Σάβ)", + "previous-week-iso": "Προηγούμενη εβδομάδα (Δευ - Κυρ)", + "previous-month": "Προηγούμενος μήνας", + "previous-quarter": "Προηγούμενο τρίμηνο", + "previous-half-year": "Προηγούμενο εξάμηνο", + "previous-year": "Προηγούμενο έτος", + "current-hour": "Τρέχουσα ώρα", + "current-day": "Τρέχουσα ημέρα", + "current-day-so-far": "Τρέχουσα ημέρα μέχρι τώρα", + "current-week": "Τρέχουσα εβδομάδα (Κυρ - Σάβ)", + "current-week-iso": "Τρέχουσα εβδομάδα (Δευ - Κυρ)", + "current-week-so-far": "Τρέχουσα εβδομάδα μέχρι τώρα (Κυρ - Σάβ)", + "current-week-iso-so-far": "Τρέχουσα εβδομάδα μέχρι τώρα (Δευ - Κυρ)", + "current-month": "Τρέχων μήνας", + "current-month-so-far": "Τρέχων μήνας μέχρι τώρα", + "current-quarter": "Τρέχον τρίμηνο", + "current-quarter-so-far": "Τρέχον τρίμηνο μέχρι τώρα", + "current-half-year": "Τρέχον εξάμηνο", + "current-half-year-so-far": "Τρέχον εξάμηνο μέχρι τώρα", + "current-year": "Τρέχον έτος", + "current-year-so-far": "Τρέχον έτος μέχρι τώρα" + }, + "type": { + "week": "Εβδομάδα (Κυρ - Σάβ)", + "week-iso": "Εβδομάδα (Δευ - Κυρ)", + "month": "Μήνας", + "quarter": "Τρίμηνο" + } + }, + "timeunit": { + "milliseconds": "Χιλιοστά του δευτερολέπτου", + "seconds": "Δευτερόλεπτα", + "minutes": "Λεπτά", + "hours": "Ώρες", + "days": "Ημέρες" }, "timewindow": { - "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": "Πραγματικός Χρόνος", + "timewindow": "Χρονικό παράθυρο", + "timewindow-settings": "Ρυθμίσεις χρονικού παραθύρου", + "years": "{ years, plural, =1 {1 έτος} other {# έτη} }", + "years-short": "{{ years }}χρ", + "months": "{ months, plural, =1 {1 μήνας} other {# μήνες} }", + "months-short": "{{ months }}Μ", + "weeks": "{ weeks, plural, =1 {1 εβδομάδα} other {# εβδομάδες} }", + "weeks-short": "{{ weeks }}εβδ", + "days": "{ days, plural, =1 {1 ημέρα} other {# ημέρες} }", + "days-short": "{{ days }}ημ", + "hours": "{ hours, plural, =0 {0 ώρες} =1 {1 ώρα} other {# ώρες} }", + "hr": "{{ hr }} ώρ", + "hr-short": "{{ hr }}ώ", + "minutes": "{ minutes, plural, =0 {0 λεπτά} =1 {1 λεπτό} other {# λεπτά} }", + "min": "{{ min }} λεπ", + "min-short": "{{ min }}λ", + "seconds": "{ seconds, plural, =0 {0 δευτερόλεπτα} =1 {1 δευτερόλεπτο} other {# δευτερόλεπτα} }", + "sec": "{{ sec }} δευτ", + "sec-short": "{{ sec }}δ", + "short": { + "years": "{ years, plural, =1 {1 έτος} other {# έτη} }", + "days": "{ days, plural, =1 {1 ημέρα} other {# ημέρες} }", + "hours": "{ hours, plural, =1 {1 ώρα} other {# ώρες} }", + "minutes": "{{minutes}} λ", + "seconds": "{{seconds}} δ" + }, + "realtime": "Σε πραγματικό χρόνο", "history": "Ιστορικό", - "last-prefix": "Τελευταίος", - "period": "από {{ startTime }} σε {{ endTime }}", - "edit": "Επεξεργασία Χρονικού Πλαισίου", + "last-prefix": "τελευταία", + "period": "από {{ startTime }} έως {{ endTime }}", + "edit": "Επεξεργασία χρονικού παραθύρου", "date-range": "Εύρος ημερομηνιών", - "last": "Τελευταίος", - "time-period": "Χρονική Περίοδος" + "for-all-time": "Για όλο τον χρόνο", + "last": "Τελευταίο", + "time-period": "Χρονική περίοδος", + "hide": "Απόκρυψη", + "interval": "Διάστημα", + "just-now": "Μόλις τώρα", + "just-now-lower": "μόλις τώρα", + "ago": "πριν", + "style": "Στυλ χρονικού παραθύρου", + "icon": "Εικονίδιο", + "icon-position": "Θέση εικονιδίου", + "icon-position-left": "Αριστερά", + "icon-position-right": "Δεξιά", + "font": "Γραμματοσειρά", + "color": "Χρώμα", + "displayTypePrefix": "Εμφάνιση προθέματος Πραγματικού χρόνου/Ιστορικού", + "preview": "Προεπισκόπηση", + "relative": "Σχετικό", + "range": "Εύρος", + "hide-timewindow-section": "Απόκρυψη ενότητας χρονικού παραθύρου από τους τελικούς χρήστες", + "hide-last-interval": "Απόκρυψη τελευταίου διαστήματος από τους τελικούς χρήστες", + "hide-relative-interval": "Απόκρυψη σχετικού διαστήματος από τους τελικούς χρήστες", + "hide-fixed-interval": "Απόκρυψη σταθερού διαστήματος από τους τελικούς χρήστες", + "hide-aggregation": "Απόκρυψη ομαδοποίησης από τους τελικούς χρήστες", + "hide-group-interval": "Απόκρυψη διαστήματος ομαδοποίησης από τους τελικούς χρήστες", + "hide-max-values": "Απόκρυψη μέγιστων τιμών από τους τελικούς χρήστες", + "hide-timezone": "Απόκρυψη ζώνης ώρας από τους τελικούς χρήστες", + "disable-custom-interval": "Απενεργοποίηση επιλογής προσαρμοσμένου διαστήματος", + "edit-aggregation-functions-list": "Επεξεργασία λίστας συναρτήσεων ομαδοποίησης", + "edit-aggregation-functions-list-hint": "Μπορεί να καθοριστεί λίστα διαθέσιμων επιλογών.", + "allowed-aggregation-functions": "Επιτρεπόμενες συναρτήσεις ομαδοποίησης", + "edit-intervals-list": "Επεξεργασία λίστας διαστημάτων", + "allowed-agg-intervals": "Διαστήματα ομαδοποίησης", + "default-agg-interval": "Προεπιλεγμένο διάστημα ομαδοποίησης", + "edit-intervals-list-hint": "Μπορεί να καθοριστεί λίστα διαθέσιμων διαστημάτων.", + "edit-grouping-intervals-list-hint": "Δυνατότητα διαμόρφωσης λίστας και προεπιλογής διαστημάτων ομαδοποίησης.", + "all": "Όλα" + }, + "tooltip": { + "trigger": "Ενεργοποίηση", + "trigger-point": "Σημείο", + "trigger-axis": "Άξονας", + "label": "Ετικέτα", + "value": "Τιμή", + "date": "Ημερομηνία", + "show-date-time-interval": "Εμφάνιση χρονικού διαστήματος", + "show-date-time-interval-hint": "Εμφάνιση χρονικού διαστήματος σύμφωνα με την ομαδοποίηση δεδομένων.", + "background-color": "Χρώμα φόντου", + "background-blur": "Θόλωση φόντου" + }, + "unit": { + "millimeter": "Χιλιοστόμετρο", + "centimeter": "Εκατοστόμετρο", + "angstrom": "Άνγκστρομ", + "nanometer": "Νανομέτρο", + "micrometer": "Μικρόμετρο", + "meter": "Μέτρο", + "kilometer": "Χιλιόμετρο", + "inch": "Ίντσα", + "foot": "Πόδι", + "yard": "Γιάρδα", + "mile": "Μίλι", + "nautical-mile": "Ναυτικό μίλι", + "astronomical-unit": "Αστρονομική μονάδα", + "reciprocal-metre": "Αντίστροφο μέτρο", + "meter-per-meter": "Μέτρο ανά μέτρο", + "steradian": "Στερακάνιο", + "thou": "Χιλιοστό της ίντσας", + "barleycorn": "Κόκκος κριθαριού", + "hand": "Χέρι (μονάδα μέτρησης)", + "chain": "Αλυσίδα", + "furlong": "Φερλόνγκ", + "league": "Λέγγα", + "fathom": "Οργιά", + "cable": "Καλώδιο (μονάδα)", + "link": "Σύνδεσμος (μονάδα)", + "rod": "Ράβδος", + "nanogram": "Νανογραμμάριο", + "microgram": "Μικρογραμμάριο", + "milligram": "Χιλιοστόγραμμο", + "gram": "Γραμμάριο", + "kilogram": "Κιλό", + "tonne": "Τόννος", + "ounce": "Ουγγιά", + "pound": "Λίβρα", + "stone": "Στόουν", + "hundredweight-count": "Κεντ. μονάδα βάρους", + "short-tons": "Αμερικανικοί τόνοι", + "dalton": "Ντάλτον", + "grain": "Σπυρί (μονάδα)", + "drachm": "Δραχμή (παλιά μονάδα βάρους)", + "quarter": "Κουόρτερ", + "slug": "Σλαγκ", + "carat": "Καράτι", + "cubic-millimeter": "Κυβικό χιλιοστόμετρο", + "cubic-centimeter": "Κυβικό εκατοστόμετρο", + "cubic-meter": "Κυβικό μέτρο", + "cubic-kilometer": "Κυβικό χιλιόμετρο", + "microliter": "Μικρολίτρο", + "milliliter": "Χιλιοστόλιτρο", + "liter": "Λίτρο", + "hectoliter": "Εκατόλιτρο", + "cubic-inch": "Κυβική ίντσα", + "cubic-foot": "Κυβικό πόδι", + "cubic-yard": "Κυβική γιάρδα", + "fluid-ounce": "Υγρή ουγγιά", + "pint": "Πίντα", + "quart": "Κουάρτο", + "gallon": "Γαλόνι", + "oil-barrels": "Βαρέλι πετρελαίου", + "cubic-meter-per-kilogram": "Κυβικό μέτρο ανά κιλό", + "gill": "Τέταρτο πίντας", + "hogshead": "Βαρέλι (hogshead)", + "teaspoon": "Κουταλάκι του γλυκού", + "tablespoon": "Κουτάλι της σούπας", + "cup": "Κούπα", + "celsius": "Κελσίου", + "kelvin": "Κέλβιν", + "rankine": "Ράνκιν", + "fahrenheit": "Φαρενάιτ", + "percent": "Ποσοστό", + "meter-per-second": "Μέτρο ανά δευτερόλεπτο", + "kilometer-per-hour": "Χιλιόμετρα ανά ώρα", + "foot-per-second": "Πόδια ανά δευτερόλεπτο", + "mile-per-hour": "Μίλια ανά ώρα", + "knot": "Κόμβος", + "millimeters-per-minute": "Χιλιοστά ανά λεπτό", + "kilometer-per-hour-squared": "Χιλιόμετρα ανά ώρα τετράγωνο", + "foot-per-second-squared": "Πόδια ανά δευτερόλεπτο τετράγωνο", + "pascal": "Πασκάλ", + "kilopascal": "Κιλοπασκάλ", + "megapascal": "Μεγαπασκάλ", + "gigapascal": "Γιγαπασκάλ", + "millibar": "Μιλιμπάρ", + "bar": "Μπάρ", + "kilobar": "Κιλομπάρ", + "newton": "Νιούτον", + "newton-meter": "Νιούτον μέτρο", + "foot-pounds": "Πόδι-λίβρες", + "inch-pounds": "Ίντσα-λίβρες", + "newton-per-meter": "Νιούτον ανά μέτρο", + "atmospheres": "Ατμόσφαιρες", + "pounds-per-square-inch": "Λίβρες ανά τετραγωνική ίντσα", + "torr": "Τορρ", + "inches-of-mercury": "Ίντσες υδραργύρου", + "pascal-per-square-meter": "Πασκάλ ανά τετραγωνικό μέτρο", + "pound-per-square-inch": "Λίβρα ανά τετραγωνική ίντσα", + "newton-per-square-meter": "Νιούτον ανά τετραγωνικό μέτρο", + "kilogram-force-per-square-meter": "Κιλογραμμοδύναμη ανά τετραγωνικό μέτρο", + "pascal-per-square-centimeter": "Πασκάλ ανά τετραγωνικό εκατοστό", + "ton-force-per-square-inch": "Τόνος δύναμης ανά τετραγωνική ίντσα", + "kilonewton-per-square-meter": "Κιλονιούτον ανά τετραγωνικό μέτρο", + "newton-per-square-millimeter": "Νιούτον ανά τετραγωνικό χιλιοστόμετρο", + "microjoule": "Μικροτζάουλ", + "millijoule": "Χιλιοστοτζάουλ", + "joule": "Τζάουλ", + "kilojoule": "Κιλοτζάουλ", + "megajoule": "Μεγατζάουλ", + "gigajoule": "Γιγκατζάουλ", + "watt-hour": "Βατ-ώρα", + "kilowatt-hour": "Κιλοβατώρα", + "electron-volts": "Ηλεκτρονιοβόλτ", + "joules-per-coulomb": "Τζάουλ ανά κουλόμπ", + "british-thermal-unit": "Βρετανικές θερμικές μονάδες", + "foot-pound": "Πόδι-λίβρα", + "calorie": "Θερμίδα", + "small-calorie": "Μικρή θερμίδα", + "kilocalorie": "Θερμίδα (kcal)", + "joule-per-kelvin": "Τζάουλ ανά Κέλβιν", + "joule-per-kilogram-kelvin": "Τζάουλ ανά κιλό-Κέλβιν", + "joule-per-kilogram": "Τζάουλ ανά κιλό", + "watt-per-meter-kelvin": "Βατ ανά μέτρο-Κέλβιν", + "joule-per-cubic-meter": "Τζάουλ ανά κυβικό μέτρο", + "therm": "Θερμίδα (Therm)", + "electric-dipole-moment": "Ηλεκτρική διπολική ροπή", + "magnetic-dipole-moment": "Μαγνητική διπολική ροπή", + "debye": "Ντεμπάι", + "coulomb-per-square-meter-per-volt": "Κουλόμπ ανά τετρ. μέτρο ανά Βολτ", + "milliwatt": "Μιλιβάτ", + "microwatt": "Μικροβάτ", + "watt": "Βατ", + "kilowatt": "Κιλοβάτ", + "megawatt": "Μεγαβάτ", + "gigawatt": "Γιγαβάτ", + "metric-horsepower": "Μετρικός ίππος", + "milliwatt-per-square-centimeter": "Μιλιβάτ ανά τετραγωνικό εκατοστό", + "watt-per-square-centimeter": "Βατ ανά τετραγωνικό εκατοστό", + "kilowatt-per-square-centimeter": "Κιλοβάτ ανά τετραγωνικό εκατοστό", + "milliwatt-per-square-meter": "Μιλιβάτ ανά τετραγωνικό μέτρο", + "watt-per-square-meter": "Βατ ανά τετραγωνικό μέτρο", + "kilowatt-per-square-meter": "Κιλοβάτ ανά τετραγωνικό μέτρο", + "watt-per-square-inch": "Βατ ανά τετραγωνική ίντσα", + "kilowatt-per-square-inch": "Κιλοβάτ ανά τετραγωνική ίντσα", + "horsepower": "Ίππος", + "btu-per-hour": "BTU ανά ώρα", + "coulomb": "Κουλόμπ", + "millicoulomb": "Μιλικουλόμπ", + "microcoulomb": "Μικροκουλόμπ", + "picocoulomb": "Πικοκουλόμπ", + "coulomb-per-meter": "Κουλόμπ ανά μέτρο", + "coulomb-per-cubic-meter": "Κουλόμπ ανά κυβικό μέτρο", + "coulomb-per-square-meter": "Κουλόμπ ανά τετραγωνικό μέτρο", + "square-millimeter": "Τετραγωνικό χιλιοστόμετρο", + "square-centimeter": "Τετραγωνικό εκατοστόμετρο", + "square-meter": "Τετραγωνικό μέτρο", + "hectare": "Εκτάριο", + "square-kilometer": "Τετραγωνικό χιλιόμετρο", + "square-inch": "Τετραγωνική ίντσα", + "square-foot": "Τετραγωνικό πόδι", + "square-yard": "Τετραγωνική γιάρδα", + "acre": "Ακρ", + "square-mile": "Τετραγωνικό μίλι", + "are": "Άρ", + "barn": "Μπαρν", + "circular-inch": "Κυκλική Ίντσα", + "milliampere-hour": "Μιλιαμπερώρια", + "ampere-hours": "Αμπερώρια", + "kiloampere-hours": "Κιλοαμπερώρια", + "nanoampere": "Νανοαμπέρ", + "picoampere": "Πικοαμπέρ", + "microampere": "Μικροαμπέρ", + "milliampere": "Μιλιαμπέρ", + "ampere": "Αμπέρ", + "microampere-per-square-centimeter": "Μικροαμπέρ ανά τετραγωνικό εκατοστό", + "ampere-per-square-meter": "Αμπέρ ανά τετραγωνικό μέτρο", + "ampere-per-meter": "Αμπέρ ανά μέτρο", + "oersted": "Όερστεντ", + "bohr-magneton": "Μπορ μαγνητόνιο", + "ampere-meter-squared": "Αμπέρ-μέτρο τετραγωνικό", + "nanovolt": "Νανοβόλτ", + "picovolt": "Πικοβόλτ", + "volt": "Βολτ", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "Βολτόμετρο", + "kilovolt-meter": "Κιλοβολτόμετρο", + "megavolt-meter": "Μεγαβολτόμετρο", + "microvolt-meter": "Μικροβολτόμετρο", + "millivolt-meter": "Μιλιβολτόμετρο", + "nanovolt-meter": "Νανοβολτόμετρο", + "ohm": "Ωμ", + "microohm": "Μικροωμ", + "milliohm": "Μιλιωμ", + "kilohm": "Κιλοωμ", + "megohm": "Μεγαωμ", + "gigohm": "Γιγαωμ", + "hertz": "Χερτζ", + "kilohertz": "Κιλοχέρτζ", + "megahertz": "Μεγαχέρτζ", + "gigahertz": "Γιγαχέρτζ", + "rpm": "Στροφές ανά λεπτό", + "candela-per-square-meter": "Καντέλα ανά τετραγωνικό μέτρο", + "candela": "Καντέλα", + "lumen": "Λούμεν", + "lux": "Λουξ", + "foot-candle": "Πόδι-κερί", + "lumen-per-square-meter": "Λούμεν ανά τετραγωνικό μέτρο", + "lux-second": "Λουξ-δευτερόλεπτο", + "lumen-second": "Λούμεν-δευτερόλεπτο", + "lumens-per-watt": "Λούμεν ανά βατ", + "mole": "Μόλιο", + "nanomole": "Νανομόλιο", + "micromole": "Μικρομόλιο", + "millimole": "Μιλιμόλιο", + "kilomole": "Κιλομόλιο", + "mole-per-cubic-meter": "Μόλιο ανά κυβικό μέτρο", + "rssi": "RSSI", + "ppm": "Μέρη ανά εκατομμύριο", + "ppb": "Μέρη ανά δισεκατομμύριο", + "micrograms-per-cubic-meter": "Μικρογραμμάρια ανά κυβικό μέτρο", + "aqi": "Δείκτης Ποιότητας Αέρα (AQI)", + "gram-per-cubic-meter": "Γραμμάριο ανά κυβικό μέτρο", + "gram-per-kilogram": "Ειδική υγρασία", + "millimeters-per-second": "Χιλιοστά ανά δευτερόλεπτο", + "neper": "Νέπερ", + "bel": "Μπελ", + "decibel": "Ντεσιμπέλ", + "meters-per-second-squared": "Μέτρα ανά δευτερόλεπτο τετράγωνο", + "becquerel": "Μπεκερέλ", + "curie": "Κιουρί", + "gray": "Γκρέι", + "sievert": "Σίβερτ", + "roentgen": "Ρέντγκεν", + "cps": "Καταμετρήσεις ανά δευτερόλεπτο", + "rad": "Ραντ", + "rem": "Ρεμ", + "dps": "Αποσυνθέσεις ανά δευτερόλεπτο", + "rutherford": "Ράδερφορντ", + "coulombs-per-kilogram": "Κουλόμπ ανά κιλό", + "becquerels-per-cubic-meter": "Μπεκερέλ ανά κυβικό μέτρο", + "curies-per-liter": "Κιουρί ανά λίτρο", + "becquerels-per-second": "Μπεκερέλ ανά δευτερόλεπτο", + "curies-per-second": "Κιουρί ανά δευτερόλεπτο", + "gy-per-second": "Γκρέι ανά δευτερόλεπτο", + "watt-per-steradian": "Βατ ανά στερακτίδιο", + "watt-per-square-metre-steradian": "Βατ ανά τετραγωνικό μέτρο-στερακτίδιο", + "ph-level": "Επίπεδο pH", + "turbidity": "Θολότητα", + "mg-per-liter": "Μιλιγραμμάρια ανά λίτρο", + "microsiemens-per-centimeter": "Μικροσίμενς ανά εκατοστό", + "millisiemens-per-meter": "Μιλισίμενς ανά μέτρο", + "siemens-per-meter": "Σίμενς ανά μέτρο", + "kilogram-per-cubic-meter": "Κιλό ανά κυβικό μέτρο", + "gram-per-cubic-centimeter": "Γραμμάριο ανά κυβικό εκατοστό", + "kilogram-per-square-meter": "Κιλό ανά τετραγωνικό μέτρο", + "milligram-per-milliliter": "Μιλιγραμμάριο ανά χιλιοστόλιτρο", + "milligram-per-cubic-meter": "Μιλιγραμμάριο ανά κυβικό μέτρο", + "pound-per-cubic-foot": "Λίβρα ανά κυβικό πόδι", + "ounces-per-cubic-inch": "Ουγγιά ανά κυβική ίντσα", + "tons-per-cubic-yard": "Τόνοι ανά κυβική γιάρδα", + "particle-density": "Πυκνότητα σωματιδίων", + "kilometers-per-liter": "Χιλιόμετρα ανά λίτρο", + "miles-per-gallon": "Μίλια ανά γαλόνι", + "liters-per-100-km": "Λίτρα ανά 100 χιλιόμετρα", + "gallons-per-mile": "Γαλόνια ανά μίλι", + "liters-per-hour": "Λίτρα ανά ώρα", + "gallons-per-hour": "Γαλόνια ανά ώρα", + "beats-per-minute": "Κτύποι ανά λεπτό", + "millimeters-of-mercury": "Χιλιοστά υδραργύρου", + "milligrams-per-deciliter": "Μιλιγραμμάρια ανά δεκατόλιτρο", + "g-force": "Επιτάχυνση βαρύτητας (g-force)", + "kilonewton": "Κιλονιούτον", + "kilogram-force": "Δύναμη κιλού", + "pound-force": "Δύναμη λίβρας", + "kilopound-force": "Κιλολίβρα-δύναμη", + "dyne": "Ντίν", + "poundal": "Πάουνταλ", + "kip": "Κιπ", + "gal": "Γκαλ", + "gravity": "Βαρύτητα", + "hectopascal": "Εκτοπασκάλ", + "atmosphere": "Ατμόσφαιρα", + "millibars": "Μιλιμπάρ", + "inch-of-mercury": "Ίντσα υδραργύρου", + "richter-scale": "Κλίμακα Ρίχτερ", + "second": "Δευτερόλεπτο", + "minute": "Λεπτό", + "hour": "Ώρα", + "day": "Ημέρα", + "week": "Εβδομάδα", + "month": "Μήνας", + "year": "Έτος", + "cubic-foot-per-minute": "Κυβικό πόδι ανά λεπτό", + "cubic-meters-per-hour": "Κυβικά μέτρα ανά ώρα", + "cubic-meters-per-second": "Κυβικά μέτρα ανά δευτερόλεπτο", + "liter-per-second": "Λίτρο ανά δευτερόλεπτο", + "liter-per-minute": "Λίτρο ανά λεπτό", + "gallons-per-minute": "Γαλόνια ανά λεπτό", + "cubic-foot-per-second": "Κυβικό πόδι ανά δευτερόλεπτο", + "milliliters-per-minute": "Χιλιοστόλιτρα ανά λεπτό", + "bit": "Bit", + "byte": "Byte", + "kilobyte": "Kilobyte", + "megabyte": "Megabyte", + "gigabyte": "Gigabyte", + "terabyte": "Terabyte", + "petabyte": "Petabyte", + "exabyte": "Exabyte", + "zettabyte": "Zettabyte", + "yottabyte": "Yottabyte", + "bit-per-second": "Bit ανά δευτερόλεπτο", + "kilobit-per-second": "Kilobit ανά δευτερόλεπτο", + "megabit-per-second": "Megabit ανά δευτερόλεπτο", + "gigabit-per-second": "Gigabit ανά δευτερόλεπτο", + "terabit-per-second": "Terabit ανά δευτερόλεπτο", + "byte-per-second": "Byte ανά δευτερόλεπτο", + "kilobyte-per-second": "Kilobyte ανά δευτερόλεπτο", + "megabyte-per-second": "Megabyte ανά δευτερόλεπτο", + "gigabyte-per-second": "Gigabyte ανά δευτερόλεπτο", + "degree": "Μοίρα", + "radian": "Ακτίνιο", + "gradian": "Γκραντ", + "revolution": "Περιστροφή", + "siemens": "Σίμενς", + "millisiemens": "Μιλισίμενς", + "microsiemens": "Μικροσίμενς", + "kilosiemens": "Κιλοσίμενς", + "megasiemens": "Μεγασίμενς", + "gigasiemens": "Γιγασίμενς", + "farad": "Φαράντ", + "millifarad": "Μιλιφαράντ", + "microfarad": "Μικροφαράντ", + "nanofarad": "Νανοφαράντ", + "picofarad": "Πικοφαράντ", + "kilofarad": "Κιλοφαράντ", + "megafarad": "Μεγαφαράντ", + "gigafarad": "Γιγαφαράντ", + "terfarad": "Τεραφαράντ", + "farad-per-meter": "Φαράντ ανά μέτρο", + "tesla": "Τέσλα", + "gauss": "Γκάους", + "kilogauss": "Κιλογκάους", + "millitesla": "Μιλιτέσλα", + "microtesla": "Μικροτέσλα", + "nanotesla": "Νανοτέσλα", + "kilotesla": "Κιλοτέσλα", + "megatesla": "Μεγατέσλα", + "millitesla-square-meters": "Μιλιτέσλα τετραγωνικά μέτρα", + "gamma": "Γάμα", + "lambda": "Λάμδα", + "square-meter-per-second": "Τετραγωνικό μέτρο ανά δευτερόλεπτο", + "square-centimeter-per-second": "Τετραγωνικό εκατοστό ανά δευτερόλεπτο", + "stoke": "Στόουκς", + "centistokes": "Σεντιστόουκς", + "square-foot-per-second": "Τετραγωνικό πόδι ανά δευτερόλεπτο", + "square-inch-per-second": "Τετραγωνική ίντσα ανά δευτερόλεπτο", + "pascal-second": "Πασκάλ-δευτερόλεπτο", + "centipoise": "Σεντιπόιζ", + "poise": "Πόιζ", + "reynolds": "Ρέινολντς", + "pound-per-foot-hour": "Λίβρα ανά πόδι-ώρα", + "newton-second-per-square-meter": "Νιούτον-δευτερόλεπτο ανά τετραγωνικό μέτρο", + "dyne-second-per-square-centimeter": "Ντάιν-δευτερόλεπτο ανά τετραγωνικό εκατοστό", + "kilogram-per-meter-second": "Κιλό ανά μέτρο-δευτερόλεπτο", + "tesla-square-meters": "Τέσλα τετραγωνικά μέτρα", + "maxwell": "Μάξγουελ", + "tesla-per-meter": "Τέσλα ανά μέτρο", + "gauss-per-centimeter": "Γκάους ανά εκατοστό", + "weber": "Βέμπερ", + "microweber": "Μικροβέμπερ", + "milliweber": "Μιλιβέμπερ", + "gauss-square-centimeter": "Γκάους-τετραγωνικό εκατοστό", + "kilogauss-square-centimeter": "Κιλογκάους-τετραγωνικό εκατοστό", + "henry": "Χένρι", + "millihenry": "Μιλιχένρι", + "microhenry": "Μικροχένρι", + "nanohenry": "Νανοχένρι", + "henry-per-meter": "Χένρι ανά μέτρο", + "tesla-meter-per-ampere": "Τέσλα μέτρο ανά Αμπέρ", + "gauss-per-oersted": "Γκάους ανά Όερστεντ", + "kilogram-per-mole": "Κιλό ανά μολ", + "gram-per-mole": "Γραμμάριο ανά μολ", + "milligram-per-mole": "Μιλιγραμμάριο ανά μολ", + "joule-per-mole": "Τζάουλ ανά μολ", + "joule-per-mole-kelvin": "Τζάουλ ανά μολ-Κέλβιν", + "millivolts-per-meter": "Μιλιβόλτ ανά μέτρο", + "volts-per-meter": "Βόλτ ανά μέτρο", + "kilovolts-per-meter": "Κιλοβόλτ ανά μέτρο", + "radian-per-second": "Ακτίνιο ανά δευτερόλεπτο", + "radian-per-second-squared": "Ακτίνιο ανά δευτερόλεπτο τετράγωνο", + "revolutions-per-minute-per-second": "Γωνιακή επιτάχυνση", + "deg-per-second": "μοίρες/δευτ.", + "degrees-brix": "Μοίρες Brix", + "katal": "Κατάλ", + "katal-per-cubic-metre": "Κατάλ ανά κυβικό μέτρο" }, "user": { "user": "Χρήστης", "users": "Χρήστες", - "management": "Διαχείριση Χρηστών", - "customer-users": "Χρήστες του Πελάτη", - "tenant-admins": "Διαχειριστές Μισθωτή", - "sys-admin": "Διαχειριστής Συστήματος", - "tenant-admin": "Διαχειριστής Μισθωτή", + "customer-users": "Χρήστες πελάτη", + "tenant-admins": "Διαχειριστές ενοικιαστή", + "sys-admin": "Διαχειριστής συστήματος", + "tenant-admin": "Διαχειριστής ενοικιαστή", "customer": "Πελάτης", "anonymous": "Ανώνυμος", "add": "Προσθήκη χρήστη", "delete": "Διαγραφή χρήστη", - "add-user-text": "Προσθήκη νέου Χρήστη", - "no-users-text": "Δεν βρέθηκαν Χρήστες", - "user-details": "Λεπτομέρειες Χρήστη", - "delete-users": "Διαγραφή Χρηστών", - "delete-user-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε το Χρήστη '{{userEmail}}'?", - "delete-user-text": "Προσοχή, μετά την επιβεβαίωση ο Χρήστης και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-users-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 user} other {# users} };", - "delete-users-action-title": "Διαγραφή { count, plural, =1 {1 user} other {# users} }", - "delete-users-text": "Προσοχή, μετά την επιβεβαίωση όλοι οι επιλεγμένοι Χρήστες θα αφαιρεθούν και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", + "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": "Το email ενεργοποίησης στάλθηκε με επιτυχία!", - "resend-activation": "Επανάληψη ενεργοποίησης", + "resend-activation": "Επαναποστολή ενεργοποίησης", "email": "Email", "email-required": "Απαιτείται email.", "invalid-email-format": "Μη έγκυρη μορφή email.", "first-name": "Όνομα", - "last-name": "Επίθετο", + "last-name": "Επώνυμο", "description": "Περιγραφή", - "default-dashboard": "Προκαθορισμένος πίνακας ελέγχου", - "always-fullscreen": "Πάντα με πλήρη οθόνη", - "select-user": "Επιλογή Χρήστη", - "no-users-matching": "Δεν βρέθηκαν χρήστες που να ταιριάζουν '{{entity}}'.", - "user-required": "Απαιτείται Χρήστης", + "default-dashboard": "Προεπιλεγμένος πίνακας ελέγχου", + "always-fullscreen": "Πάντα σε πλήρη οθόνη", + "select-user": "Επιλέξτε χρήστη", + "no-users-matching": "Δεν βρέθηκαν χρήστες που να ταιριάζουν με '{{entity}}'.", + "user-required": "Απαιτείται χρήστης", "activation-method": "Μέθοδος ενεργοποίησης", "display-activation-link": "Εμφάνιση συνδέσμου ενεργοποίησης", - "send-activation-mail": "Αποστολή mail ενεργοποίησης", - "activation-link": "Σύνδεσμος ενεργοποίησης Χρήστη", - "activation-link-text": "΄Προκειμένου να ενεργοποιήσετε το Χρήστη, χρησιμοποιήστε το εξής activation link :", + "send-activation-mail": "Αποστολή email ενεργοποίησης", + "activation-link": "Σύνδεσμος ενεργοποίησης χρήστη", + "activation-link-text": "Για να ενεργοποιήσετε τον χρήστη, χρησιμοποιήστε τον παρακάτω σύνδεσμο ενεργοποίησης (λήγει σε {{activationLinkTtl}}) :", "copy-activation-link": "Αντιγραφή συνδέσμου ενεργοποίησης", - "activation-link-copied-message": "Ο σύνδεσμος ενεργοποίησης χρήστη έχει αντιγραφεί στο πρόχειρο", - "selected-users": "{ count, plural, =1 {1 user} other {# users} } επιλέχθηκαν", - "search": "Αναζήτηση Χρηστών", + "activation-link-copied-message": "Ο σύνδεσμος ενεργοποίησης χρήστη αντιγράφηκε στο πρόχειρο", "details": "Λεπτομέρειες", - "login-as-tenant-admin": "Συνδεθείτε ως Διαχειριστής Μισθωτή", - "login-as-customer-user": "Συνδεθείτε ως Χρήστης του Πελάτη", - "select-group-to-add": "Επιλέξτε την ομάδα προορισμού για να προσθέσετε επιλεγμένους Χρήστες", - "select-group-to-move": "Επιλέξτε την ομάδα προορισμού για να μετακινήσετε επιλεγμένους χρήστες", - "remove-users-from-group": "Είστε σίγουροι ότι θέλετε να καταργήσετε { count, plural, =1 {1 user} other {# users} } από την ομάδα '{entityGroup}';", - "group": "Ομάδα από Χρήστες", - "list-of-groups": "{ count, plural, =1 {One user group} other {List of # user groups} }", - "group-name-starts-with": "Ομάδες Χρηστών των οποίων τα ονόματα ξεκινούν με'{{prefix}}'" + "login-as-tenant-admin": "Σύνδεση ως διαχειριστής ενοικιαστή", + "login-as-customer-user": "Σύνδεση ως χρήστης πελάτη", + "search": "Αναζήτηση χρηστών", + "selected-users": "{ count, plural, =1 {1 χρήστης} other {# χρήστες} } επιλέχθηκαν", + "disable-account": "Απενεργοποίηση λογαριασμού χρήστη", + "enable-account": "Ενεργοποίηση λογαριασμού χρήστη", + "enable-account-message": "Ο λογαριασμός χρήστη ενεργοποιήθηκε επιτυχώς!", + "disable-account-message": "Ο λογαριασμός χρήστη απενεργοποιήθηκε επιτυχώς!", + "copyId": "Αντιγραφή αναγνωριστικού χρήστη", + "idCopiedMessage": "Το αναγνωριστικό χρήστη αντιγράφηκε στο πρόχειρο", + "user-list": "Λίστα χρηστών", + "user-list-required": "Απαιτείται λίστα χρηστών" }, "value": { - "type": "Είδος τιμής", + "type": "Τύπος τιμής", "string": "Συμβολοσειρά", "string-value": "Τιμή συμβολοσειράς", + "string-value-required": "Απαιτείται τιμή συμβολοσειράς", "integer": "Ακέραιος", - "integer-value": "Ακέραια τιμή", - "invalid-integer-value": "Μη έγκυρη ακέραια τιμή", - "double": "Πραγματικός", - "double-value": "Πραγματική τιμή", - "boolean": "Λογικό", - "boolean-value": "Λογική τιμή", - "false": "Εσφαλμένος", - "true": "Αληθής", - "long": "Μακρύς" + "integer-value": "Τιμή ακέραιου", + "integer-value-required": "Απαιτείται τιμή ακέραιου", + "invalid-integer-value": "Μη έγκυρη τιμή ακέραιου", + "double": "Δεκαδικός", + "double-value": "Τιμή δεκαδικού", + "double-value-required": "Απαιτείται τιμή δεκαδικού", + "boolean": "Λογική τιμή", + "boolean-value": "Τιμή λογικής", + "false": "Ψευδές", + "true": "Αληθές", + "long": "Long", + "json": "JSON", + "json-value": "Τιμή JSON", + "json-value-invalid": "Η τιμή JSON έχει μη έγκυρη μορφή", + "json-value-required": "Απαιτείται τιμή JSON." + }, + "version-control": { + "version-control": "Έλεγχος εκδόσεων", + "management": "Διαχείριση ελέγχου εκδόσεων", + "search": "Αναζήτηση εκδόσεων", + "branch": "Κλάδος", + "default": "Προεπιλογή", + "select-branch": "Επιλέξτε κλάδο", + "branch-required": "Απαιτείται κλάδος", + "create-entity-version": "Δημιουργία έκδοσης οντότητας", + "version-name": "Όνομα έκδοσης", + "version-name-required": "Απαιτείται όνομα έκδοσης", + "author": "Συγγραφέας", + "export-relations": "Εξαγωγή σχέσεων", + "export-attributes": "Εξαγωγή ιδιοτήτων", + "export-credentials": "Εξαγωγή διαπιστευτηρίων", + "export-calculated-fields": "Εξαγωγή υπολογιζόμενων πεδίων", + "entity-versions": "Εκδόσεις οντοτήτων", + "versions": "Εκδόσεις", + "created-time": "Ώρα δημιουργίας", + "version-id": "Αναγνωριστικό έκδοσης", + "no-entity-versions-text": "Δεν βρέθηκαν εκδόσεις οντοτήτων", + "no-versions-text": "Δεν βρέθηκαν εκδόσεις", + "copy-full-version-id": "Αντιγραφή πλήρους αναγνωριστικού έκδοσης", + "create-version": "Δημιουργία έκδοσης", + "creating-version": "Δημιουργία έκδοσης... Παρακαλώ περιμένετε", + "nothing-to-commit": "Δεν υπάρχουν αλλαγές για καταχώρηση", + "restore-version": "Επαναφορά έκδοσης", + "restore-entity-from-version": "Επαναφορά οντότητας από την έκδοση '{{versionName}}'", + "restoring-entity-version": "Επαναφορά έκδοσης οντότητας... Παρακαλώ περιμένετε", + "load-relations": "Φόρτωση σχέσεων", + "load-attributes": "Φόρτωση ιδιοτήτων", + "load-credentials": "Φόρτωση διαπιστευτηρίων", + "load-calculated-fields": "Φόρτωση υπολογιζόμενων πεδίων", + "compare-with-current": "Σύγκριση με την τρέχουσα", + "diff-entity-with-version": "Διαφορά με την έκδοση οντότητας '{{versionName}}'", + "previous-difference": "Προηγούμενη διαφορά", + "next-difference": "Επόμενη διαφορά", + "current": "Τρέχουσα", + "differences": "{ count, plural, =1 {1 διαφορά} other {# διαφορές} }", + "create-entities-version": "Δημιουργία έκδοσης οντοτήτων", + "default-sync-strategy": "Προεπιλεγμένη στρατηγική συγχρονισμού", + "sync-strategy-merge": "Συγχώνευση", + "sync-strategy-overwrite": "Αντικατάσταση", + "entities-to-export": "Οντότητες προς εξαγωγή", + "entities-to-restore": "Οντότητες προς επαναφορά", + "sync-strategy": "Στρατηγική συγχρονισμού", + "all-entities": "Όλες οι οντότητες", + "no-entities-to-export-prompt": "Καθορίστε οντότητες για εξαγωγή", + "no-entities-to-restore-prompt": "Καθορίστε οντότητες για επαναφορά", + "add-entity-type": "Προσθήκη τύπου οντότητας", + "remove-all": "Αφαίρεση όλων", + "version-create-result": "{ added, plural, =0 {Καμία οντότητα} =1 {1 οντότητα} other {# οντότητες} } προστέθηκαν.
{ modified, plural, =0 {Καμία οντότητα} =1 {1 οντότητα} other {# οντότητες} } τροποποιήθηκαν.
{ removed, plural, =0 {Καμία οντότητα} =1 {1 οντότητα} other {# οντότητες} } αφαιρέθηκαν.", + "remove-other-entities": "Αφαίρεση άλλων οντοτήτων", + "find-existing-entity-by-name": "Εύρεση υπάρχουσας οντότητας βάσει ονόματος", + "restore-entities-from-version": "Επαναφορά οντοτήτων από την έκδοση '{{versionName}}'", + "restoring-entities-from-version": "Επαναφορά οντοτήτων... Παρακαλώ περιμένετε", + "no-entities-restored": "Δεν επαναφέρθηκαν οντότητες", + "created": "{{created}} δημιουργήθηκαν", + "updated": "{{updated}} ενημερώθηκαν", + "deleted": "{{deleted}} διαγράφηκαν", + "remove-other-entities-confirm-text": "Προσοχή! Αυτό θα διαγράψει οριστικά όλες τις τρέχουσες οντότητες
που δεν υπάρχουν στην έκδοση που θέλετε να επαναφέρετε.

Παρακαλώ πληκτρολογήστε \"remove other entities\" για επιβεβαίωση.", + "auto-commit-to-branch": "αυτόματη καταχώρηση στον κλάδο {{branch}}", + "default-create-entity-version-name": "Ενημέρωση {{entityName}}", + "sync-strategy-merge-hint": "Δημιουργεί ή ενημερώνει τις επιλεγμένες οντότητες στο αποθετήριο. Όλες οι άλλες οντότητες δεν τροποποιούνται.", + "sync-strategy-overwrite-hint": "Δημιουργεί ή ενημερώνει τις επιλεγμένες οντότητες στο αποθετήριο. Όλες οι άλλες οντότητες διαγράφονται.", + "device-credentials-conflict": "Αποτυχία φόρτωσης της συσκευής με εξωτερικό ID {{entityId}}
λόγω ύπαρξης των ίδιων διαπιστευτηρίων σε άλλη συσκευή.
Σκεφτείτε να απενεργοποιήσετε την επιλογή φόρτωση διαπιστευτηρίων στη φόρμα επαναφοράς.", + "missing-referenced-entity": "Αποτυχία φόρτωσης της {{sourceEntityTypeName}} με εξωτερικό ID {{sourceEntityId}}
επειδή αναφέρεται σε ανύπαρκτη {{targetEntityTypeName}} με ID {{targetEntityId}}.", + "runtime-failed": "Αποτυχία: {{message}}", + "auto-commit-settings-read-only-hint": "Η δυνατότητα αυτόματης καταχώρησης δεν λειτουργεί όταν έχει ενεργοποιηθεί η επιλογή μόνο για ανάγνωση στις ρυθμίσεις αποθετηρίου.", + "rollback-on-error": "Αναίρεση σε περίπτωση σφάλματος", + "rollback-on-error-hint": "Αν έχετε μεγάλο αριθμό οντοτήτων για επαναφορά, εξετάστε το ενδεχόμενο να απενεργοποιήσετε αυτήν την επιλογή για αυξημένη απόδοση.\n Σημειώστε ότι, αν παρουσιαστεί σφάλμα κατά τη διάρκεια της φόρτωσης έκδοσης, οι ήδη αποθηκευμένες οντότητες (με σχέσεις, ιδιότητες, κ.λπ.) θα παραμείνουν ως έχουν" }, "widget": { - "widget-library": "Βιβλιοθήκη Widget", - "widget-bundle": "Δέσμη Widget", - "select-widgets-bundle": "Επιλογή δέσμης Widgets", - "management": "Διαχείριση Widget", - "editor": "Συντάκτης Widget", - "widget-type-not-found": "Πρόβλημα φόρτωσης διαμόρφωσης Widget.
Probably associated\n widget type was removed.", - "widget-type-load-error": "Το Widget δεν φορτώθηκε λόγω των παρακάτω σφαλμάτων:", - "remove": "Αφαίρεση Widget", - "edit": "Επεξεργασία Widget", - "remove-widget-title": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε το Widget '{{widgetTitle}}'?", - "remove-widget-text": "Προσοχή, μετά την επιβεβαίωση το widget και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "timeseries": "Χρονική σειρά", + "widget-library": "Βιβλιοθήκη widgets", + "widget-bundle": "Πακέτο widgets", + "all-bundles": "Όλα τα πακέτα", + "select-widgets-bundle": "Επιλέξτε πακέτο widgets", + "widgets": "Widgets", + "all-widgets": "Όλα τα widgets", + "widget": "Widget", + "select-widget": "Επιλέξτε widget", + "no-widgets-matching": "Δεν βρέθηκαν widgets που να ταιριάζουν με '{{entity}}'.", + "no-widgets": "Δεν υπάρχουν ακόμα widgets", + "no-widgets-text": "Δεν βρέθηκαν widgets", + "management": "Διαχείριση widgets", + "editor": "Επεξεργαστής widget", + "confirm-to-exit-editor-html": "Έχετε μη αποθηκευμένες ρυθμίσεις widget.
Είστε σίγουροι ότι θέλετε να φύγετε από αυτή τη σελίδα;", + "widget-type-not-found": "Πρόβλημα κατά τη φόρτωση της διαμόρφωσης του widget.
Πιθανόν ο τύπος widget να έχει αφαιρεθεί.", + "widget-type-load-error": "Το widget δεν φορτώθηκε λόγω των εξής σφαλμάτων:", + "remove": "Αφαίρεση widget", + "delete": "Διαγραφή widget", + "edit": "Επεξεργασία widget", + "remove-widget-title": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε το widget '{{widgetTitle}}';", + "remove-widget-text": "Μετά την επιβεβαίωση, το widget και όλα τα σχετικά δεδομένα δεν θα μπορούν να ανακτηθούν.", + "replace-reference-with-widget-copy": "Αντικατάσταση αναφοράς με αντίγραφο widget", + "timeseries": "Χρονοσειρά", "search-data": "Αναζήτηση δεδομένων", "no-data-found": "Δεν βρέθηκαν δεδομένα", - "latest": "Τελευταίες αξίες", - "rpc": "Έλεγχος Widget", - "alarm": "Alarm widget", + "latest": "Τελευταίες τιμές", + "rpc": "Widget ελέγχου", + "alarm": "Widget συναγερμού", "static": "Στατικό widget", - "select-widget-type": "Επιλογή τύπου Widget", - "missing-widget-title-error": "Ο τίτλος Widget πρέπει να καθοριστεί!", - "widget-saved": "Το Widget αποθηκεύτηκε", - "unable-to-save-widget-error": "Δεν είναι δυνατή η αποθήκευση του Widget! Το Widget έχει σφάλματα!", + "timeseries-short": "σειρά", + "latest-short": "τελευταία", + "rpc-short": "έλεγχος", + "alarm-short": "συναγερμός", + "static-short": "στατικό", + "select-widget-type": "Επιλέξτε τύπο widget", + "missing-widget-title-error": "Πρέπει να καθοριστεί τίτλος widget!", + "widget-saved": "Το widget αποθηκεύτηκε", + "unable-to-save-widget-error": "Αδυναμία αποθήκευσης του widget! Περιέχει σφάλματα!", "save": "Αποθήκευση widget", "saveAs": "Αποθήκευση widget ως", - "save-widget-type-as": "Αποθήκευση τύπου Widget type ως", - "save-widget-type-as-text": "Παρακαλούμε εισάγετε νέο τίτλο Widget και/ή επιλέξετε στοχευμένη δέσμη widgets", - "toggle-fullscreen": "Λειτουργεία πλήρους οθόνης", - "run": "Εκτέλεση Widget", - "title": "Τίτλος Widget", - "title-required": "Απαιτείται τίτλος Widget.", - "type": "Τύπος Widget", + "move": "Μετακίνηση widget", + "save-widget-as": "Αποθήκευση widget ως", + "save-widget-as-text": "Εισάγετε νέο τίτλο widget", + "toggle-fullscreen": "Εναλλαγή πλήρους οθόνης", + "run": "Εκτέλεση widget", + "widget-title": "Τίτλος widget", + "title": "Τίτλος", + "title-required": "Απαιτείται τίτλος widget.", + "title-max-length": "Ο τίτλος πρέπει να είναι μικρότερος από 256 χαρακτήρες", + "system": "Σύστημα", + "type": "Τύπος widget", "resources": "Πόροι", "resource-url": "JavaScript/CSS URL", - "remove-resource": "Αφαίρεση πηγής", - "add-resource": "Προσθήκη πηγής", + "resource-is-extension": "Είναι επέκταση", + "remove-resource": "Αφαίρεση πόρου", + "add-resource": "Προσθήκη πόρου", "html": "HTML", - "tidy": "Τακτοποιημένος", + "tidy": "Τακτοποίηση", "css": "CSS", - "settings-schema": "Ρυθμίσεις σχήματος", - "datakey-settings-schema": "Πλήκτρο δεδομένων σχήματος ρυθμίσεων", + "settings-form": "Φόρμα ρυθμίσεων", + "data-key-settings-form": "Φόρμα ρυθμίσεων data key", + "latest-data-key-settings-form": "Φόρμα ρυθμίσεων τελευταίων data key", + "widget-settings": "Ρυθμίσεις widget", + "description": "Περιγραφή", + "tags": "Ετικέτες", + "image-preview": "Προεπισκόπηση εικόνας", + "settings-form-selector": "Επιλογέας φόρμας ρυθμίσεων", + "data-key-settings-form-selector": "Επιλογέας φόρμας ρυθμίσεων data key", + "latest-data-key-settings-form-selector": "Επιλογέας φόρμας ρυθμίσεων τελευταίων data key", + "all": "Όλα", + "actual": "Πραγματικό", + "scada": "Σύμβολο SCADA", + "deprecated": "Καταργημένο", + "has-basic-mode": "Διαθέτει βασική λειτουργία", + "basic-mode-form-selector": "Επιλογέας φόρμας βασικής λειτουργίας", + "basic-mode": "Βασική", + "advanced-mode": "Προχωρημένη", "javascript": "Javascript", - "add-widget-type": "Προσθήκη νέου τύπου Widget", - "widget-template-load-failed-error": "Αποτυχία φόρτωσης προτύπου Widget!", - "add": "Προσθήκη Widget", - "undo": "Αναίρεση αλλαγών Widget", - "export": "Εξαγωγή Widget", - "export-data": "Εξαγωγή δεδομένων Widget", - "export-to-csv": "Εξαγωγή δεδομένων σε CSV...", - "export-to-excel": "Εξαγωγή δεδομένων σε XLS...", - "no-data": "Δεν υπάρχουν δεδομένα για εμφάνιση στο Widget" + "js": "JS", + "delete-widget-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε το widget '{{widgetName}}';", + "delete-widget-text": "Μετά την επιβεβαίωση, το widget και όλα τα σχετικά δεδομένα δεν θα μπορούν να ανακτηθούν.", + "delete-widgets-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 widget} other {# widgets} };", + "delete-widgets-text": "Προσοχή, μετά την επιβεβαίωση όλα τα επιλεγμένα widgets θα διαγραφούν και όλα τα σχετικά δεδομένα δεν θα μπορούν να ανακτηθούν.", + "delete-widget": "Διαγραφή widget", + "widget-template-load-failed-error": "Αποτυχία φόρτωσης προτύπου widget!", + "details": "Λεπτομέρειες", + "widget-details": "Λεπτομέρειες widget", + "add": "Προσθήκη widget", + "add-existing-widget": "Προσθήκη υπάρχοντος widget", + "add-new-widget": "Προσθήκη νέου widget", + "search-widgets": "Αναζήτηση widgets", + "selected-widgets": "{ count, plural, =1 {1 widget} other {# widgets} } επιλεγμένο", + "undo": "Αναίρεση αλλαγών widget", + "export": "Εξαγωγή widget", + "export-prompt": "Ενσωμάτωση εικόνων και πόρων του widget", + "export-widgets": "Εξαγωγή widgets", + "export-widgets-prompt": "Ενσωμάτωση εικόνων και πόρων των widgets", + "import": "Εισαγωγή widget", + "no-data": "Δεν υπάρχουν δεδομένα για εμφάνιση στο widget", + "data-overflow": "Το widget εμφανίζει {{count}} από {{total}} οντότητες", + "alarm-data-overflow": "Το widget εμφανίζει συναγερμούς για {{allowedEntities}} (μέγιστο επιτρεπόμενο) οντότητες από {{totalEntities}} οντότητες", + "search": "Αναζήτηση widget", + "filter": "Τύπος φίλτρου widget", + "loading-widgets": "Φόρτωση widgets...", + "widget-template-error": "Μη έγκυρο πρότυπο HTML του widget.", + "reference": "Αναφορά" }, "widget-action": { - "header-button": "Κουμπί κεφαλίδας στη νέα κατάσταση του πίνακα ελέγχου", - "update-dashboard-state": "Ενημέρωση της τρέχουσας κατάστασης του dashboard", - "open-dashboard": "Πλοήγηση σε άλλο dashboard", + "header-button": "Κουμπί κεφαλίδας widget", + "do-nothing": "Καμία ενέργεια", + "open-dashboard-state": "Μετάβαση σε νέα κατάσταση πίνακα ελέγχου", + "update-dashboard-state": "Ενημέρωση τρέχουσας κατάστασης πίνακα ελέγχου", + "open-dashboard": "Μετάβαση σε άλλο πίνακα ελέγχου", "custom": "Προσαρμοσμένη ενέργεια", - "target-dashboard-state": "Κατάσταση προορισμού dashboard", - "target-dashboard-state-required": "Απαιτείται κατάσταση προορισμού dashboard", - "set-entity-from-widget": "Ορισμός οντότητας από Widget", - "target-dashboard": "dashboard προορισμού", - "open-right-layout": "Ανοίξτε τη δεξιά διάταξη του dashboard (προβολή κινητού)" + "custom-pretty": "Προσαρμοσμένη ενέργεια (με πρότυπο HTML)", + "custom-pretty-error-title": "Σφάλμα προσαρμοσμένου διαλόγου", + "custom-pretty-template-error": "Μη έγκυρο πρότυπο προσαρμοσμένου διαλόγου.", + "custom-pretty-controller-error": "Παρουσιάστηκε σφάλμα κατά την αξιολόγηση της συνάρτησης προσαρμοσμένου διαλόγου.", + "mobile-action": "Ενέργεια κινητής συσκευής", + "target-dashboard-state": "Στοχευμένη κατάσταση πίνακα ελέγχου", + "target-dashboard-state-required": "Απαιτείται στοχευμένη κατάσταση πίνακα ελέγχου", + "set-entity-from-widget": "Ορισμός οντότητας από widget", + "target-dashboard": "Στοχευμένος πίνακας ελέγχου", + "select-target-dashboard": "Επιλογή στοχευμένου πίνακα ελέγχου", + "target-dashboard-required": "Απαιτείται στοχευμένος πίνακας ελέγχου.", + "open-right-layout": "Άνοιγμα δεξιάς διάταξης πίνακα ελέγχου (προβολή κινητού)", + "state-display-type": "Επιλογή εμφάνισης κατάστασης πίνακα ελέγχου", + "open-normal": "Κανονική", + "open-in-separate-dialog": "Άνοιγμα σε ξεχωριστό διάλογο", + "open-in-popover": "Άνοιγμα σε αναδυόμενο παράθυρο", + "dialog-title": "Τίτλος διαλόγου", + "dialog-hide-dashboard-toolbar": "Απόκρυψη γραμμής εργαλείων πίνακα ελέγχου στο διάλογο", + "dialog-width": "Πλάτος διαλόγου σε ποσοστό σε σχέση με το πλάτος του παραθύρου προβολής", + "dialog-height": "Ύψος διαλόγου σε ποσοστό σε σχέση με το ύψος του παραθύρου προβολής", + "dialog-size-range-error": "Η τιμή ποσοστού μεγέθους διαλόγου πρέπει να είναι μεταξύ 1 και 100.", + "popover-preferred-placement": "Προτιμώμενη θέση αναδυόμενου παραθύρου", + "popover-placement-top": "Πάνω", + "popover-placement-topLeft": "Πάνω αριστερά", + "popover-placement-topRight": "Πάνω δεξιά", + "popover-placement-right": "Δεξιά", + "popover-placement-rightTop": "Δεξιά πάνω", + "popover-placement-rightBottom": "Δεξιά κάτω", + "popover-placement-bottom": "Κάτω", + "popover-placement-bottomLeft": "Κάτω αριστερά", + "popover-placement-bottomRight": "Κάτω δεξιά", + "popover-placement-left": "Αριστερά", + "popover-placement-leftTop": "Αριστερά πάνω", + "popover-placement-leftBottom": "Αριστερά κάτω", + "popover-hide-on-click-outside": "Απόκρυψη αναδυόμενου παραθύρου με κλικ εκτός", + "popover-hide-dashboard-toolbar": "Απόκρυψη γραμμής εργαλείων πίνακα ελέγχου στο αναδυόμενο παράθυρο", + "popover-width": "Πλάτος αναδυόμενου παραθύρου", + "popover-height": "Ύψος αναδυόμενου παραθύρου", + "popover-style": "Στυλ αναδυόμενου παραθύρου", + "open-new-browser-tab": "Άνοιγμα σε νέα καρτέλα προγράμματος περιήγησης", + "open-URL": "Άνοιγμα URL", + "URL": "URL", + "url-required": "Απαιτείται URL.", + "mobile": { + "device-provision": "Παροχή συσκευής", + "action-type": "Τύπος ενέργειας κινητής συσκευής", + "select-action-type": "Επιλογή τύπου ενέργειας κινητής συσκευής", + "action-type-required": "Απαιτείται τύπος ενέργειας κινητής συσκευής", + "take-picture-from-gallery": "Επιλογή εικόνας από τη συλλογή", + "take-photo": "Λήψη φωτογραφίας", + "map-direction": "Άνοιγμα οδηγιών χάρτη", + "map-location": "Άνοιγμα τοποθεσίας χάρτη", + "scan-qr-code": "Σάρωση κωδικού QR", + "make-phone-call": "Πραγματοποίηση τηλεφωνικής κλήσης", + "get-location": "Λήψη τοποθεσίας τηλεφώνου", + "take-screenshot": "Λήψη στιγμιότυπου οθόνης" + }, + "custom-action-function": "Συνάρτηση προσαρμοσμένης ενέργειας", + "custom-pretty-function": "Συνάρτηση προσαρμοσμένης ενέργειας (με πρότυπο HTML)", + "map-item-type": "Τύπος στοιχείου χάρτη", + "map-item": { + "marker": "Δείκτης", + "polygon": "Πολύγωνο", + "rectangle": "Ορθογώνιο", + "circle": "Κύκλος" + }, + "place-map-item": "Τοποθέτηση στοιχείου χάρτη", + "map-item-tooltip": { + "customize-map-item-tooltips": "Προσαρμογή υποδείξεων στοιχείων χάρτη", + "place-marker": "Τοποθέτηση δείκτη", + "start-draw-rectangle": "Έναρξη σχεδίασης ορθογωνίου", + "finish-draw-rectangle": "Ολοκλήρωση σχεδίασης ορθογωνίου", + "start-draw-polygon": "Έναρξη σχεδίασης πολυγώνου", + "continue-draw-polygon": "Συνέχεια σχεδίασης πολυγώνου", + "finish-draw-polygon": "Ολοκλήρωση σχεδίασης πολυγώνου", + "start-draw-circle": "Έναρξη σχεδίασης κύκλου", + "finish-draw-circle": "Ολοκλήρωση σχεδίασης κύκλου" + } }, "widgets-bundle": { - "current": "Τρέχουσα δέσμη", - "widgets-bundles": "Δέσμες Widgets", - "add": "Προσθήκη δέσμης Widgets", - "delete": "Διαγραφή δέσμης Widgets", + "current": "Τρέχον πακέτο widgets", + "widgets-bundles": "Πακέτα widgets", + "widgets-bundle-widgets": "Widgets του πακέτου", + "add": "Προσθήκη πακέτου widgets", + "delete": "Διαγραφή πακέτου widgets", "title": "Τίτλος", "title-required": "Απαιτείται τίτλος.", - "add-widgets-bundle-text": "Προσθήκη νέας δέσμης widgets", - "no-widgets-bundles-text": "Δεν βρέθηκαν δέσμες widgets", - "empty": "Η δέσμη Widgets είναι κενή", + "title-max-length": "Ο τίτλος πρέπει να έχει λιγότερους από 256 χαρακτήρες", + "description": "Περιγραφή", + "image-preview": "Προεπισκόπηση εικόνας", + "scada": "Πακέτο widgets SCADA", + "order": "Σειρά", + "add-widgets-bundle-text": "Προσθήκη νέου πακέτου widgets", + "no-widgets-bundles-text": "Δεν βρέθηκαν πακέτα widgets", + "empty": "Το πακέτο widgets είναι κενό", "details": "Λεπτομέρειες", - "widgets-bundle-details": "Λεπτομέρειες δέσμης Widgets", - "delete-widgets-bundle-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε τη δέσμη Widgets '{{widgetsBundleTitle}}';", - "delete-widgets-bundle-text": "Προσοχή, μετά την επιβεβαίωση η δέσμη Widget και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "delete-widgets-bundles-title": "Είστε σίγουροι ότι θέλετε να διαγράψετε { count, plural, =1 {1 widgets bundle} other {# widgets bundles} };", - "delete-widgets-bundles-action-title": "Διαγραφή { count, plural, =1 {1 widgets bundle} other {# widgets bundles} }", - "delete-widgets-bundles-text": "Προσοχή, μετά την επιβεβαίωση όλες οι επιλεγμένες δέσμες Widget και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "no-widgets-bundles-matching": "Δεν βρέθηκαν δέσμες Widgets που να ταιριάζουν'{{widgetsBundle}}'.", - "widgets-bundle-required": "Απαιτείται δέσμη Widgets.", + "widgets-bundle-details": "Λεπτομέρειες πακέτου widgets", + "delete-widgets-bundle-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το πακέτο widgets '{{widgetsBundleTitle}}';", + "delete-widgets-bundle-text": "Προσοχή, μετά την επιβεβαίωση το πακέτο widgets και όλα τα σχετικά δεδομένα θα γίνουν μη ανακτήσιμα.", + "delete-widgets-bundles-title": "Είστε βέβαιοι ότι θέλετε να διαγράψετε { count, plural, =1 {1 πακέτο widgets} other {# πακέτα widgets} };", + "delete-widgets-bundles-action-title": "Διαγραφή { count, plural, =1 {1 πακέτου widgets} other {# πακέτων widgets} }", + "delete-widgets-bundles-text": "Προσοχή, μετά την επιβεβαίωση όλα τα επιλεγμένα πακέτα widgets θα αφαιρεθούν και όλα τα σχετικά δεδομένα θα γίνουν μη ανακτήσιμα.", + "no-widgets-bundles-matching": "Δεν βρέθηκαν πακέτα widgets που να ταιριάζουν με '{{widgetsBundle}}'.", + "widgets-bundle-required": "Απαιτείται πακέτο widgets.", "system": "Σύστημα", - "import": "Εισαγωγή δέσμης Widgets", - "export": "Εξαγωγή δέσμης Widgets", - "export-failed-error": "Δεν είναι δυνατή η εξαγωγή δέσμης Widgets: {{error}}", - "create-new-widgets-bundle": "Δημιουργία νέας δέσμης Widgets", - "widgets-bundle-file": "Αρχείο δέσμης Widgets", - "invalid-widgets-bundle-file-error": "Δεν είναι δυνατή η εισαγωγή δέσμης Widgets: Μη έγκυρη δομή δεδομένων Widgets." + "import": "Εισαγωγή πακέτου widgets", + "export": "Εξαγωγή πακέτου widgets", + "export-widgets-bundle-widgets-prompt": "Συμπερίληψη των widgets του πακέτου στα εξαγόμενα δεδομένα (διαφορετικά θα εξαχθούν μόνο οι αναφορές στα FQNs των widgets)", + "export-failed-error": "Αδυναμία εξαγωγής πακέτου widgets: {{error}}", + "create-new-widgets-bundle": "Δημιουργία νέου πακέτου widgets", + "widgets-bundle-file": "Αρχείο πακέτου widgets", + "invalid-widgets-bundle-file-error": "Αδυναμία εισαγωγής πακέτου widgets: Μη έγκυρη δομή δεδομένων πακέτου.", + "search": "Αναζήτηση πακέτων widgets", + "selected-widgets-bundles": "{ count, plural, =1 {1 πακέτο widgets} other {# πακέτα widgets} } επιλεγμένα", + "open-widgets-bundle": "Άνοιγμα πακέτου widgets", + "loading-widgets-bundles": "Φόρτωση πακέτων widgets...", + "create-new": "Δημιουργία νέου πακέτου widgets" }, "widget-config": { "data": "Δεδομένα", "settings": "Ρυθμίσεις", - "advanced": "Προηγμένος", + "advanced": "Για προχωρημένους", + "appearance": "Εμφάνιση", + "widget-card": "Κάρτα widget", + "mobile": "Κινητό", "title": "Τίτλος", + "title-tooltip": "Υπόδειξη τίτλου", "general-settings": "Γενικές ρυθμίσεις", - "display-title": "Εμφάνιση τίτλου", - "drop-shadow": "Σκίαση", + "display-title": "Εμφάνιση τίτλου widget", + "card-title": "Τίτλος κάρτας", + "drop-shadow": "Σκιά κάρτας", "enable-fullscreen": "Ενεργοποίηση πλήρους οθόνης", - "enable-data-export": "Ενεργοποίηση εξαγωγής δεδομένων", "background-color": "Χρώμα φόντου", "text-color": "Χρώμα κειμένου", + "border-radius": "Ακτίνα περιγράμματος", "padding": "Εσωτερικό περιθώριο", - "margin": "Περιθώριο", - "widget-style": "Στυλ Widget", - "title-style": "Στυλ τίτλου", - "mobile-mode-settings": "Ρυθμίσεις λειτουργίας κινητού", - "order": "Εντολή", + "margin": "Εξωτερικό περιθώριο", + "widget-style": "Στιλ widget", + "widget-css": "CSS widget", + "title-style": "Στιλ τίτλου", + "mobile-mode-settings": "Λειτουργία κινητού", + "order": "Σειρά", "height": "Ύψος", - "units": "Ειδικό σύμβολο για εμφάνιση δίπλα στην αξία", - "decimals": "Αριθμός ψηφίων μετά το κυμαινόμενο σημείο", - "timewindow": "Timewindow", - "use-dashboard-timewindow": "Χρήση dashboard timewindow", - "display-timewindow": "Απεικόνιση timewindow", - "display-legend": "Απεικόνιση λεζάντας", + "mobile-hide": "Απόκρυψη widget σε κινητή προβολή", + "desktop-hide": "Απόκρυψη widget σε προβολή επιφάνειας εργασίας", + "units": "Ειδικό σύμβολο δίπλα στην τιμή", + "units-by-default": "Μονάδες εξ ορισμού", + "decimals": "Αριθμός ψηφίων μετά την υποδιαστολή", + "decimals-by-default": "Δεκαδικά εξ ορισμού", + "default-data-key-parameter-hint": "Αυτή η ρύθμιση εφαρμόζεται σε όλες τις τιμές του widget εκτός αν παρακαμφθεί από τις ρυθμίσεις κλειδιού δεδομένων", + "units-short": "Μονάδες", + "decimals-short": "Δεκαδικά", + "decimals-suffix": "δεκαδικά", + "digits-suffix": "ψηφία", + "timewindow": "Χρονικό παράθυρο", + "use-dashboard-timewindow": "Χρήση χρονικού παραθύρου πίνακα ελέγχου", + "use-widget-timewindow": "Χρήση χρονικού παραθύρου widget", + "display-timewindow": "Εμφάνιση χρονικού παραθύρου", + "legend": "Υπόμνημα", + "display-legend": "Εμφάνιση υπομνήματος", "datasources": "Πηγές δεδομένων", - "maximum-datasources": "Το μέγιστο { count, plural, =1 {1 datasource is allowed.} other {# datasources are allowed} }", + "datasource": "Πηγή δεδομένων", + "maximum-datasources": "Μέγιστο { count, plural, =1 {1 πηγή δεδομένων επιτρέπεται.} other {επιτρέπονται # πηγές δεδομένων} }", + "timeseries-key-error": "Πρέπει να καθοριστεί τουλάχιστον ένα κλειδί δεδομένων χρονοσειράς", "datasource-type": "Τύπος", "datasource-parameters": "Παράμετροι", - "remove-datasource": "Κατάργηση της πηγής δεδομένων", + "remove-datasource": "Αφαίρεση πηγής δεδομένων", "add-datasource": "Προσθήκη πηγής δεδομένων", - "target-device": "Target device", - "alarm-source": "Πηγή Alarm", + "target-device": "Συσκευή στόχος", + "alarm-source": "Πηγή συναγερμού", "actions": "Ενέργειες", - "action": "ενέργεια", + "action": "Ενέργεια", "add-action": "Προσθήκη ενέργειας", "search-actions": "Αναζήτηση ενεργειών", + "no-actions-text": "Δεν βρέθηκαν ενέργειες", "action-source": "Πηγή ενέργειας", + "select-action-source": "Επιλογή πηγής ενέργειας", "action-source-required": "Απαιτείται πηγή ενέργειας.", + "column-index": "Δείκτης στήλης", + "select-column-index": "Επιλογή δείκτη στήλης", + "column-index-required": "Απαιτείται δείκτης στήλης.", + "not-set": "Μη καθορισμένο", "action-name": "Όνομα", "action-name-required": "Απαιτείται όνομα ενέργειας.", - "action-name-not-unique": "Μια άλλη ενέργεια με το ίδιο όνομα υπάρχει ήδη.\nΤο όνομα ενέργειας πρέπει να είναι μοναδικό μέσα στην ίδια πηγή ενέργειας.", + "action-name-not-unique": "Υπάρχει ήδη άλλη ενέργεια με το ίδιο όνομα.\nΤο όνομα της ενέργειας πρέπει να είναι μοναδικό εντός της ίδιας πηγής.", "action-icon": "Εικονίδιο", + "header-button": { + "button-settings": "Ρυθμίσεις κουμπιού", + "button-type": "Τύπος κουμπιού", + "button-type-basic": "Βασικό", + "button-type-raised": "Ανυψωμένο", + "button-type-stroked": "Περίγραμμα", + "button-type-flat": "Επίπεδο", + "button-type-icon": "Εικονίδιο", + "button-type-mini-fab": "FAB", + "colors": "Χρώματα", + "color": "Χρώμα", + "background": "Φόντο", + "border": "Περίγραμμα", + "advanced-button-style": "Για προχωρημένους στιλ κουμπιού", + "button-style": "Στιλ κουμπιού" + }, + "show-hide-action-using-function": "Εμφάνιση/Απόκρυψη ενέργειας με χρήση συνάρτησης", + "show-action-function": "Συνάρτηση εμφάνισης ενέργειας", "action-type": "Τύπος", "action-type-required": "Απαιτείται τύπος ενέργειας.", "edit-action": "Επεξεργασία ενέργειας", "delete-action": "Διαγραφή ενέργειας", - "delete-action-title": "Διαγραφή ενέργειας Widget", - "delete-action-text": "Είστε σίγουροι ότι θέλετε να διαγράψετε δράση widget με όνομα '{{actionName}}';" + "delete-action-title": "Διαγραφή ενέργειας widget", + "delete-action-text": "Είστε βέβαιοι ότι θέλετε να διαγράψετε την ενέργεια widget με όνομα '{{actionName}}';", + "title-icon": "Εικονίδιο τίτλου", + "display-icon": "Εμφάνιση εικονιδίου τίτλου", + "card-icon": "Εικονίδιο κάρτας", + "icon": "Εικονίδιο", + "icon-color": "Χρώμα εικονιδίου", + "icon-size": "Μέγεθος εικονιδίου", + "advanced-settings": "Για προχωρημένους", + "data-settings": "Ρυθμίσεις δεδομένων", + "limits": "Όρια", + "no-data-display-message": "Εναλλακτικό μήνυμα \"Δεν υπάρχουν δεδομένα προς εμφάνιση\"", + "data-page-size": "Μέγιστος αριθμός οντοτήτων ανά πηγή δεδομένων", + "settings-component-not-found": "Το στοιχείο φόρμας ρυθμίσεων δεν βρέθηκε για τον επιλεγμένο selector '{{selector}}'", + "preview": "Προεπισκόπηση", + "set": "Ορισμός", + "set-message": "Ορισμός μηνύματος", + "advanced-title-style": "Προηγμένο στιλ τίτλου", + "card-style": "Στιλ κάρτας", + "text": "Κείμενο", + "background": "Φόντο", + "advanced-widget-style": "Προηγμένο στιλ widget", + "card-buttons": "Κουμπιά κάρτας", + "show-card-buttons": "Εμφάνιση κουμπιών κάρτας", + "card-border-radius": "Ακτίνα περιγράμματος κάρτας", + "card-padding": "Περιθώριο κάρτας", + "card-appearance": "Εμφάνιση κάρτας", + "color": "Χρώμα", + "tooltip": "Υπόδειξη", + "units-required": "Απαιτείται μονάδα.", + "list-layout": "Διάταξη λίστας", + "layout": "Διάταξη", + "resize-options": "Επιλογές αλλαγής μεγέθους", + "resizable": "Με δυνατότητα αλλαγής μεγέθους", + "preserve-aspect-ratio": "Διατήρηση αναλογίας διαστάσεων" }, "widget-type": { - "import": "Εισαγωγή τύπου Widget", - "export": "Εξαγωγή τύπου Widget", - "export-failed-error": "Δεν είναι δυνατή η εξαγωγή τύπου Widget: {{error}}", - "create-new-widget-type": "Δημιουργία νέου τύπου Widget", - "widget-type-file": "Αρχείο τύπου Widget", - "invalid-widget-type-file-error": "Δεν είναι δυνατή η εισαγωγή τύπου Widget: Μη έγκυρη δομή δεδομένων τύπου Widget." - }, - "self-registration": { - "self-registration": "Αυτόματη εγγραφή", - "self-registration-url": "Αυτόματη εγγραφή URL", - "captcha-site-key": "reCAPTCHA κλειδί ιστότοπου", - "captcha-secret-key": "reCAPTCHA μυστικό κλειδί", - "notification-email": "Εmail γνωστοποίησης", - "privacy-policy-text": "Κείμενο πολιτικής απορρήτου", - "text-message-page": "Μήνυμα κειμένου για τη σελίδα εγγραφής" - }, - "white-labeling": { - "white-labeling": "Εμφάνιση", - "login-white-labeling": "Εμφάνιση Σύδεσης", + "import": "Εισαγωγή τύπου widget", + "export": "Εξαγωγή τύπου widget", + "export-failed-error": "Αδυναμία εξαγωγής του widget: {{error}}", + "widget-file": "Αρχείο widget", + "invalid-widget-file-error": "Αδυναμία εισαγωγής widget: Μη έγκυρη δομή δεδομένων widget." + }, + "markdown": { + "edit": "Επεξεργασία", "preview": "Προεπισκόπηση", - "app-title": "Τίτλος εφαρμογής", - "favicon": "Εικονίδιο Ιστότοπου", - "favicon-description": "Εικόνα *.ico, *.gif or *.png με μέγιστο μέγεθος {{kbSize}} KBytes.", - "favicon-size-error": "Η εικόνα ιστότοπου είναι πολύ μεγάλη. Μέγιστο επιτρεπόμενο μέγεθος {{kbSize}} KBytes.", - "favicon-type-error": "Μη έγκυρη μορφή αρχείου εικόνας ιστότοπου. Μόνο εικόνες ICO, GIF ή PNG γίνονται αποδεκτές.", - "drop-favicon-image": "Σύρετε ένα εικονίδιο ιστότοπου ή κάντε κλικ για να επιλέξετε ένα αρχείο για μεταφόρτωση.", - "no-favicon-image": "Δεν έχει επιλεχθεί εικονίδιο", - "logo": "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": "Όνομα Domain", - "help-link-base-url": "Base url για συνδέσμους βοηθείας", - "enable-help-links": "Ενεργοποίηση συνδέσμων βοηθείας", - "error-verification-url": "Ένα όνομα domain δεν πρέπει να περιέχει σύμβολα '/' και ':'. Παράδειγμα: thingsboard.io", - "show-platform-name-version": "Εμφάνιση ονόματος και έκδοσης πλατφόρμας", - "platform-name": "Όνομα πλατφόρμας", - "platform-version": "Έκδοση πλατφόρμας", - "version-mask": "{{name}} v.{{verion}}", - "position": { - "label": "Όνομα πλατφόρμας και θέση έκδοσης", - "under-logo": "Κάτω από το λογότυπο", - "bottom": "Στο κάτω μέρος της φόρμας σύνδεσης" - } + "copy-code": "Κάντε κλικ για αντιγραφή", + "copied": "Αντιγράφηκε!" }, "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "Η διαμόρφωση εξαρτάται από το widget QR code της εφαρμογής κινητού στις βασικές ρυθμίσεις της πλατφόρμας", + "get-it-on-google-play": "Λήψη από το Google Play", + "download-on-the-app-store": "Λήψη από το App Store" + }, + "action-button": { + "behavior": "Συμπεριφορά", + "on-click": "Κατά το κλικ", + "on-click-hint": "Ενέργεια που εκτελείται όταν γίνεται κλικ στο κουμπί", + "first-button-click": "Κλικ στο πρώτο κουμπί", + "first-button-click-hint": "Ενέργεια κατά την πίεση του πρώτου κουμπιού.", + "second-button-click": "Κλικ στο δεύτερο κουμπί", + "second-button-click-hint": "Ενέργεια κατά την πίεση του δεύτερου κουμπιού.", + "button-click-hint": "Ενέργεια κατά την πίεση στο widget." + }, + "command-button": { + "behavior": "Συμπεριφορά", + "on-click": "Κατά το κλικ", + "on-click-hint": "Ενέργεια που εκτελείται όταν γίνεται κλικ στο κουμπί." + }, + "power-button": { + "behavior": "Συμπεριφορά", + "power-on": "Ενεργοποίηση", + "power-on-hint": "Ενέργεια για ενεργοποίηση του στοιχείου.", + "power-off": "Απενεργοποίηση", + "power-off-hint": "Ενέργεια για απενεργοποίηση του στοιχείου.", + "on-label": "Ενεργό", + "off-label": "Ανενεργό", + "layout": "Διάταξη", + "layout-default": "Προεπιλεγμένη", + "layout-simplified": "Απλοποιημένη", + "layout-outlined": "Περίγραμμα", + "layout-default-volume": "Προεπιλεγμένη.Ένταση", + "layout-simplified-volume": "Απλοποιημένη.Ένταση", + "layout-outlined-volume": "Περίγραμμα.Ένταση", + "layout-default-icon": "Προεπιλεγμένη.Εικονίδιο", + "layout-simplified-icon": "Απλοποιημένη.Εικονίδιο", + "layout-outlined-icon": "Περίγραμμα.Εικονίδιο", + "main": "Κύριο", + "background": "Φόντο", + "button-icon-on": "Εικονίδιο κουμπιού 'On'", + "button-icon-off": "Εικονίδιο κουμπιού 'Off'", + "power-on-colors": "Χρώματα κατά την ενεργοποίηση", + "power-off-colors": "Χρώματα κατά την απενεργοποίηση", + "disabled-colors": "Χρώματα απενεργοποίησης", + "button": "Κουμπί" + }, + "toggle-button": { + "behavior": "Συμπεριφορά", + "checked": "Επιλεγμένο", + "unchecked": "Μη επιλεγμένο", + "check": "Επιλογή", + "check-hint": "Ενέργεια για επιλογή του στοιχείου.", + "uncheck": "Αποεπιλογή", + "uncheck-hint": "Ενέργεια για αποεπιλογή του στοιχείου.", + "auto-scale": "Αυτόματη κλιμάκωση", + "horizontal-fill": "Οριζόντια κάλυψη", + "vertical-fill": "Κάθετη κάλυψη", + "button-appearance": "Εμφάνιση κουμπιού" + }, + "segmented-button": { + "layout": "Διάταξη", + "layout-squared": "Τετραγωνισμένο", + "layout-rounded": "Στρογγυλεμένο", + "card-border": "Περίγραμμα κάρτας", + "button-appearance": "Εμφάνιση κουμπιού", + "first": "Πρώτο", + "second": "Δεύτερο", + "color-styles": "Στυλ χρώματος", + "selected": "Επιλεγμένο", + "unselected": "Μη επιλεγμένο" + }, + "button": { + "layout": "Διάταξη", + "outlined": "Με περίγραμμα", + "filled": "Γεμάτο", + "underlined": "Υπογραμμισμένο", + "basic": "Βασικό", + "auto-scale": "Αυτόματη κλιμάκωση", + "label": "Ετικέτα", + "icon": "Εικονίδιο", + "border-radius": "Ακτίνα περιγράμματος", + "color-palette": "Παλέτα χρωμάτων", + "main": "Κύριο", + "background": "Φόντο", + "border": "Περίγραμμα", + "custom-styles": "Προσαρμοσμένα στυλ", + "clear-style": "Καθαρισμός στυλ", + "shadow": "Σκιά", + "enabled": "Ενεργοποιημένο", + "disabled": "Απενεργοποιημένο", + "preview": "Προεπισκόπηση", + "copy-style-from": "Αντιγραφή στυλ από" + }, + "value-stepper": { + "behavior": "Συμπεριφορά", + "simplified": "Απλοποιημένο", + "filled": "Συμπληρωμένο", + "outlined": "Περίγραμμα", + "volume": "Ένταση", + "initial-state": "Αρχική κατάσταση", + "initial-state-hint": "Ενέργεια για λήψη της αρχικής τιμής.", + "disabled-state": "Απενεργοποιημένη κατάσταση", + "disabled-state-hint": "Διαμόρφωση συνθήκης κατά την οποία το στοιχείο είναι απενεργοποιημένο.", + "right-button-click": "Κλικ δεξιού κουμπιού", + "right-button-click-hint": "Ενέργεια κατά το πάτημα του δεξιού κουμπιού.", + "left-button-click": "Κλικ αριστερού κουμπιού", + "left-button-click-hint": "Ενέργεια κατά το πάτημα του αριστερού κουμπιού.", + "auto-scale": "Αυτόματη κλιμάκωση", + "value-range": "Εύρος", + "min-range": "Ελάχιστο", + "max-range": "Μέγιστο", + "value-increment-decrement-step": "Βήμα αύξησης/μείωσης τιμής", + "value": "Τιμή", + "value-box-background": "Φόντο πλαισίου τιμής", + "border": "Περίγραμμα", + "button-appearance": "Εμφάνιση κουμπιού", + "left": "Αριστερά", + "right": "Δεξιά", + "left-button": "Αριστερό κουμπί", + "right-button": "Δεξί κουμπί", + "icon": "Εικονίδιο", + "color-palette": "Παλέτα χρωμάτων", + "main": "Κύριο", + "background": "Φόντο", + "button-icon-on": "Εικονίδιο κουμπιού 'Ενεργό'", + "button-on-colors": "Χρώματα κατά την ενεργοποίηση", + "disabled-colors": "Χρώματα απενεργοποίησης" + }, + "button-state": { + "activated-state": "Κατάσταση ενεργοποίησης", + "activated-state-hint": "Ρύθμιση συνθήκης κατά την οποία το κουμπί είναι ενεργό.", + "disabled-state": "Απενεργοποιημένη κατάσταση", + "disabled-state-hint": "Ρύθμιση συνθήκης κατά την οποία το κουμπί είναι απενεργοποιημένο.", + "selected-state": "Επιλογή κουμπιού", + "selected-state-hint": "Ρύθμιση συνθήκης κατά την οποία το κουμπί επιλέγεται.", + "enabled": "Ενεργοποιημένο", + "hovered": "Με αιώρηση", + "pressed": "Πατημένο", + "activated": "Ενεργοποιημένο", + "disabled": "Απενεργοποιημένο", + "initial": "Πρώτο κουμπί", + "first": "Πρώτο", + "second": "Δεύτερο" + }, + "background": { + "background": "Φόντο", + "background-settings": "Ρυθμίσεις φόντου", + "background-type-image": "Εικόνα", + "background-type-color": "Χρώμα", + "image-url": "URL εικόνας", + "overlay": "Επικάλυψη", + "enable-overlay": "Ενεργοποίηση επικάλυψης", + "blur": "Θόλωμα", + "preview": "Προεπισκόπηση" + }, + "bar-chart": { + "bar-appearance": "Εμφάνιση γραμμής", + "label-on-bar": "Ετικέτα στη γραμμή", + "value-on-bar": "Τιμή στη γραμμή", + "bar-chart-style": "Στυλ ραβδογράμματος", + "bar-axis": "Άξονας ράβδων" + }, + "polar-area-chart": { + "polar-axis": "Πολικός άξονας", + "start-angle": "Γωνία εκκίνησης", + "polar-area-chart-style": "Στυλ πολικού διαγράμματος" + }, + "battery-level": { + "layout": "Διάταξη", + "layout-vertical-solid": "Κατακόρυφη. Συμπαγής", + "layout-horizontal-solid": "Οριζόντια. Συμπαγής", + "layout-vertical-divided": "Κατακόρυφη. Διαχωρισμένη", + "layout-horizontal-divided": "Οριζόντια. Διαχωρισμένη", + "icon": "Εικονίδιο", + "value": "Τιμή", + "auto-scale": "Αυτόματη κλιμάκωση", + "battery-level-color": "Χρώμα στάθμης μπαταρίας", + "battery-shape-color": "Χρώμα σχήματος μπαταρίας", + "battery-level-card-style": "Στυλ κάρτας στάθμης μπαταρίας", + "sections-count": "Αριθμός τμημάτων" + }, + "signal-strength": { + "value": "Τιμή", + "last-update": "Τελευταία ενημέρωση", + "no-signal": "Χωρίς σήμα", + "layout": "Διάταξη", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "Γραμμή κινητής", + "icon": "Εικονίδιο", + "date": "Ημερομηνία", + "active-bars-color": "Χρώμα ενεργών γραμμών σήματος", + "inactive-bars-color": "Χρώμα ανενεργών γραμμών σήματος", + "signal-strength-card-style": "Στυλ κάρτας έντασης σήματος", + "no-signal-rssi-value": "Τιμή rssi για \"Χωρίς σήμα\"" + }, + "status-widget": { + "behavior": "Συμπεριφορά", + "layout": "Διάταξη", + "layout-default": "Προεπιλεγμένη", + "layout-center": "Κέντρο", + "layout-icon": "Εικονίδιο", + "on": "Ενεργό", + "off": "Ανενεργό", + "label": "Ετικέτα", + "status": "Κατάσταση", + "icon": "Εικονίδιο", + "color-palette": "Παλέτα χρωμάτων", + "disabled-color-palette": "Παλέτα χρωμάτων απενεργοποίησης", + "primary": "Πρωτεύον", + "primary-color-hint": "Χρώμα εικονιδίου και ετικέτας", + "secondary": "Δευτερεύον", + "secondary-color-hint": "Χρώμα κατάστασης", + "background": "Φόντο" + }, + "chart": { + "common-settings": "Κοινές ρυθμίσεις", + "enable-stacking-mode": "Ενεργοποίηση λειτουργίας στοίβαξης", + "selection": "Επιλογή εύρους χρόνου", + "enable-selection-mode": "Ενεργοποίηση λειτουργίας επιλογής", + "line-shadow-size": "Μέγεθος σκιάς γραμμής", + "display-smooth-lines": "Εμφάνιση ομαλών (καμπύλων) γραμμών", + "default-bar-width": "Προεπιλεγμένο πλάτος ράβδου για μη συγκεντρωμένα δεδομένα (milliseconds)", + "bar-alignment": "Στοίχιση ράβδων", + "bar-alignment-left": "Αριστερά", + "bar-alignment-right": "Δεξιά", + "bar-alignment-center": "Κέντρο", + "default-font": "Προεπιλεγμένη γραμματοσειρά", + "default-font-size": "Προεπιλεγμένο μέγεθος γραμματοσειράς", + "default-font-color": "Προεπιλεγμένο χρώμα γραμματοσειράς", + "thresholds-line-width": "Προεπιλεγμένο πάχος γραμμής για όλα τα όρια", + "tooltip-settings": "Ρυθμίσεις υπόδειξης", + "tooltip": "Υπόδειξη", + "show-tooltip": "Εμφάνιση υπόδειξης", + "hover-individual-points": "Επισήμανση μεμονωμένων σημείων", + "show-cumulative-values": "Εμφάνιση αθροιστικών τιμών σε λειτουργία στοίβαξης", + "hide-zero-false-values": "Απόκρυψη μηδενικών/ψευδών τιμών από την υπόδειξη", + "tooltip-value-format-function": "Συνάρτηση μορφοποίησης τιμής υπόδειξης", + "grid-settings": "Ρυθμίσεις πλέγματος", + "show-vertical-lines": "Εμφάνιση κάθετων γραμμών", + "show-horizontal-lines": "Εμφάνιση οριζόντιων γραμμών", + "grid-outline-border-width": "Πάχος περιγράμματος πλέγματος (px)", + "primary-color": "Πρωτεύον χρώμα", + "background-color": "Χρώμα φόντου", + "ticks-color": "Χρώμα τιμών άξονα", + "xaxis-settings": "Ρυθμίσεις άξονα Χ", + "axis-title": "Τίτλος άξονα", + "xaxis-tick-labels-settings": "Ρυθμίσεις ετικετών άξονα Χ", + "show-tick-labels": "Εμφάνιση ετικετών άξονα", + "yaxis-settings": "Ρυθμίσεις άξονα Υ", + "min-scale-value": "Ελάχιστη τιμή κλίμακας", + "max-scale-value": "Μέγιστη τιμή κλίμακας", + "yaxis-tick-labels-settings": "Ρυθμίσεις ετικετών άξονα Υ", + "tick-step-size": "Μέγεθος βήματος τιμών", + "number-of-decimals": "Αριθμός δεκαδικών ψηφίων", + "ticks-formatter-function": "Συνάρτηση μορφοποίησης τιμών", + "comparison-settings": "Ρυθμίσεις σύγκρισης", + "enable-comparison": "Ενεργοποίηση σύγκρισης", + "time-for-comparison": "Περίοδος σύγκρισης", + "time-for-comparison-previous-interval": "Προηγούμενο διάστημα (προεπιλογή)", + "time-for-comparison-days": "Πριν από μία ημέρα", + "time-for-comparison-weeks": "Πριν από μία εβδομάδα", + "time-for-comparison-months": "Πριν από έναν μήνα", + "time-for-comparison-years": "Πριν από έναν χρόνο", + "time-for-comparison-custom-interval": "Προσαρμοσμένο διάστημα", + "custom-interval-value": "Τιμή προσαρμοσμένου διαστήματος (ms)", + "comparison-x-axis-settings": "Ρυθμίσεις άξονα Χ για σύγκριση", + "axis-position": "Θέση άξονα", + "axis-position-top": "Πάνω (προεπιλογή)", + "axis-position-bottom": "Κάτω", + "custom-legend-settings": "Ρυθμίσεις προσαρμοσμένου υπομνήματος", + "enable-custom-legend": "Ενεργοποίηση προσαρμοσμένου υπομνήματος (επιτρέπει τη χρήση τιμών χαρακτηριστικών/σειρών χρόνου σε ετικέτες κλειδιών)", + "key-name": "Όνομα κλειδιού", + "key-name-required": "Απαιτείται όνομα κλειδιού", + "key-type": "Τύπος κλειδιού", + "key-type-attribute": "Χαρακτηριστικό", + "key-type-timeseries": "Σειρά χρόνου", + "label-keys-list": "Λίστα κλειδιών για χρήση στις ετικέτες", + "no-label-keys": "Δεν έχουν διαμορφωθεί κλειδιά", + "add-label-key": "Προσθήκη νέου κλειδιού", + "line-width": "Πάχος γραμμής", + "color": "Χρώμα", + "data-is-hidden-by-default": "Τα δεδομένα είναι κρυφά από προεπιλογή", + "disable-data-hiding": "Απενεργοποίηση απόκρυψης δεδομένων", + "remove-from-legend": "Αφαίρεση κλειδιού δεδομένων από το υπόμνημα", + "exclude-from-stacking": "Εξαίρεση από στοίβαξη (διαθέσιμο σε λειτουργία \"Στοίβαξη\")", + "line-settings": "Ρυθμίσεις γραμμής", + "show-line": "Εμφάνιση γραμμής", + "fill-line": "Συμπλήρωση γραμμής", + "fill-line-opacity": "Αδιαφάνεια συμπλήρωσης", + "points-settings": "Ρυθμίσεις σημείων", + "show-points": "Εμφάνιση σημείων", + "points-line-width": "Πάχος γραμμής σημείων", + "points-radius": "Ακτίνα σημείων", + "point-shape": "Σχήμα σημείου", + "point-shape-circle": "Κύκλος", + "point-shape-cross": "Σταυρός", + "point-shape-diamond": "Ρόμβος", + "point-shape-square": "Τετράγωνο", + "point-shape-triangle": "Τρίγωνο", + "point-shape-custom": "Προσαρμοσμένη συνάρτηση", + "point-shape-draw-function": "Συνάρτηση σχεδίασης σχήματος σημείου", + "show-separate-axis": "Εμφάνιση ξεχωριστού άξονα", + "axis-position-left": "Αριστερά", + "axis-position-right": "Δεξιά", + "thresholds": "Όρια", + "no-thresholds": "Δεν έχουν διαμορφωθεί όρια", + "add-threshold": "Προσθήκη ορίου", + "show-values-for-comparison": "Εμφάνιση ιστορικών τιμών για σύγκριση", + "comparison-values-label": "Ετικέτα ιστορικών τιμών", + "comparison-line-color": "Χρώμα γραμμής σύγκρισης", + "threshold-settings": "Ρυθμίσεις ορίων", + "use-as-threshold": "Χρήση τιμής κλειδιού ως όριο", + "threshold-line-width": "Πάχος γραμμής ορίου", + "threshold-color": "Χρώμα ορίου", + "common-pie-settings": "Κοινές ρυθμίσεις πίτας", + "radius": "Ακτίνα", + "inner-radius": "Εσωτερική ακτίνα", + "tilt": "Κλίση", + "common-pie-settings-range-error": "Η τιμή πρέπει να είναι στο εύρος από 0 έως 1", + "stroke-settings": "Ρυθμίσεις περιγράμματος", + "width-pixels": "Πλάτος (pixel)", + "show-labels": "Εμφάνιση ετικετών", + "animation-settings": "Ρυθμίσεις κίνησης", + "animated-pie": "Ενεργοποίηση κίνησης πίτας (πειραματικό)", + "border-settings": "Ρυθμίσεις περιγράμματος", + "border-width": "Πλάτος περιγράμματος", + "border-color": "Χρώμα περιγράμματος", + "legend-settings": "Ρυθμίσεις υπομνήματος", + "display-legend": "Εμφάνιση υπομνήματος", + "labels-font-color": "Χρώμα γραμματοσειράς ετικετών", + "series": "Σειρές", + "add-series": "Προσθήκη σειράς", + "series-settings": "Ρυθμίσεις σειράς", + "remove-series": "Αφαίρεση σειράς", + "no-series": "Δεν έχουν διαμορφωθεί σειρές", + "no-series-error": "Πρέπει να καθοριστεί τουλάχιστον μία σειρά", + "chart-appearance": "Εμφάνιση γραφήματος", + "vertical-grid-lines": "Κάθετες γραμμές πλέγματος", + "horizontal-grid-lines": "Οριζόντιες γραμμές πλέγματος", + "chart-background": "Φόντο γραφήματος", + "grid-lines-color": "Χρώμα γραμμών πλέγματος", + "border": "Περίγραμμα", + "axis": "Άξονας", + "vertical-axis": "Κάθετος άξονας", + "ticks": "Διαβαθμίσεις", + "horizontal-axis": "Οριζόντιος άξονας", + "shape-empty-circle": "Κενός κύκλος", + "shape-circle": "Κύκλος", + "shape-rect": "Ορθογώνιο", + "shape-round-rect": "Στρογγυλεμένο ορθογώνιο", + "shape-triangle": "Τρίγωνο", + "shape-diamond": "Ρόμβος", + "shape-pin": "Καρφίτσα", + "shape-arrow": "Βέλος", + "shape-none": "Κανένα", + "line-type-solid": "Συνεχής", + "line-type-dashed": "Διακεκομμένη", + "line-type-dotted": "Διαστιγμένη", + "label-position-top": "Πάνω", + "label-position-bottom": "Κάτω", + "label-position-outside": "Εξωτερικά", + "label-position-inside": "Εσωτερικά", + "fill": "Γέμισμα", + "fill-type-none": "Κανένα", + "fill-type-solid": "Συμπαγές", + "fill-type-opacity": "Αδιαφάνεια", + "fill-type-gradient": "Διαβάθμιση", + "background": "Φόντο", + "opacity": "Αδιαφάνεια", + "gradient-stops": "Στάσεις διαβάθμισης", + "gradient-start": "αρχή", + "gradient-end": "τέλος", + "animation": { + "animation": "Κίνηση", + "animation-threshold": "Όριο κίνησης", + "animation-duration": "Διάρκεια κίνησης", + "animation-easing": "Ομαλότητα κίνησης", + "animation-delay": "Καθυστέρηση κίνησης", + "update-animation-duration": "Διάρκεια κίνησης κατά την ενημέρωση", + "update-animation-easing": "Ομαλότητα κίνησης κατά την ενημέρωση", + "update-animation-delay": "Καθυστέρηση κίνησης κατά την ενημέρωση" + }, + "chart-axis": { + "scale": "Κλίμακα", + "scale-min": "ελάχιστο", + "scale-max": "μέγιστο", + "scale-auto": "Αυτόματα" + }, + "bar": { + "show-border": "Εμφάνιση περιγράμματος", + "border-width": "Πλάτος περιγράμματος", + "border-radius": "Ακτίνα περιγράμματος", + "bar-width": "Πλάτος μπάρας", + "label": "Ετικέτα", + "label-hint": "Εμφάνιση ετικέτας πάνω στη μπάρα.", + "series-label-hint": "Εμφάνιση ετικέτας με την τιμή πάνω στη μπάρα.", + "label-background": "Φόντο ετικέτας" + } + }, + "color": { + "color-settings": "Ρυθμίσεις χρώματος", + "color-type-constant": "Σταθερό", + "color-type-gradient": "Διαβάθμιση", + "color-type-range": "Εύρος", + "color-type-function": "Συνάρτηση", + "color": "Χρώμα", + "value-range": "Εύρος τιμής", + "from": "Από", + "to": "Προς", + "color-function": "Συνάρτηση χρώματος", + "copy-color-settings-from": "Αντιγραφή ρυθμίσεων χρώματος από", + "copy-from": "Αντιγραφή από", + "settings-type": "Τύπος ρυθμίσεων", + "basic-mode": "Βασική", + "advanced-mode": "Προχωρημένη", + "entity-alias": "Ψευδώνυμο οντότητας", + "entity-attribute": "Χαρακτηριστικό οντότητας", + "gradient-color": "Χρώμα διαβάθμισης", + "gradient-color-min": "Χρώμα", + "gradient-start": "Αρχικό χρώμα διαβάθμισης", + "gradient-start-min": "Έναρξη", + "gradient-end": "Τελικό χρώμα διαβάθμισης", + "gradient-end-min": "Τέλος", + "start-value": "Τιμή εκκίνησης", + "end-value": "Τιμή λήξης", + "gradient-type": "Τύπος διαβάθμισης" + }, + "dashboard-state": { + "dashboard-state-settings": "Ρυθμίσεις κατάστασης πίνακα ελέγχου", + "dashboard-state": "Αναγνωριστικό κατάστασης πίνακα ελέγχου", + "autofill-state-layout": "Αυτόματη συμπλήρωση ύψους διάταξης κατάστασης από προεπιλογή", + "default-margin": "Προεπιλεγμένο περιθώριο widget", + "default-background-color": "Προεπιλεγμένο χρώμα φόντου", + "sync-parent-state-params": "Συγχρονισμός παραμέτρων κατάστασης με τον γονικό πίνακα" + }, "date-range-navigator": { + "date-range-picker-settings": "Ρυθμίσεις επιλογέα εύρους ημερομηνιών", + "hide-date-range-picker": "Απόκρυψη επιλογέα εύρους ημερομηνιών", + "picker-one-panel": "Επιλογέας εύρους ημερομηνιών με ένα πάνελ", + "picker-auto-confirm": "Αυτόματη επιβεβαίωση επιλογέα εύρους ημερομηνιών", + "picker-show-template": "Εμφάνιση προτύπου επιλογέα εύρους ημερομηνιών", + "first-day-of-week": "Πρώτη ημέρα της εβδομάδας", + "interval-settings": "Ρυθμίσεις διαστήματος", + "hide-interval": "Απόκρυψη διαστήματος", + "initial-interval": "Αρχικό διάστημα", + "interval-hour": "Ώρα", + "interval-day": "Ημέρα", + "interval-week": "Εβδομάδα", + "interval-two-weeks": "2 εβδομάδες", + "interval-month": "Μήνας", + "interval-three-months": "3 μήνες", + "interval-six-months": "6 μήνες", + "step-settings": "Ρυθμίσεις βήματος", + "hide-step-size": "Απόκρυψη μεγέθους βήματος", + "initial-step-size": "Αρχικό μέγεθος βήματος", + "hide-labels": "Απόκρυψη ετικετών", + "use-session-storage": "Χρήση αποθήκευσης συνεδρίας", "localizationMap": { "Sun": "Κυρ", "Mon": "Δευ", - "Tue": "Τρι", + "Tue": "Τρ", "Wed": "Τετ", "Thu": "Πεμ", "Fri": "Παρ", @@ -2513,7 +7217,7 @@ "Feb": "Φεβ", "Mar": "Μαρ", "Apr": "Απρ", - "May": "Μάιος", + "May": "Μαϊ", "Jun": "Ιουν", "Jul": "Ιουλ", "Aug": "Αυγ", @@ -2536,65 +7240,1985 @@ "Date Range Template": "Πρότυπο εύρους ημερομηνιών", "Today": "Σήμερα", "Yesterday": "Χθες", - "This Week": "Αυτή την εβοδομάδα", - "Last Week": "Την προηγούμενη εβδομάδα", - "This Month": "Αυτόν τον μήνα", - "Last Month": "Τον προηγούμενο μήνα", + "This Week": "Αυτή την εβδομάδα", + "Last Week": "Προηγούμενη εβδομάδα", + "This Month": "Αυτός ο μήνας", + "Last Month": "Προηγούμενος μήνας", "Year": "Έτος", - "This Year": "Αυτό το χρόνο", - "Last Year": "Τον προηγούμενο χρόνο", + "This Year": "Αυτό το έτος", + "Last Year": "Προηγούμενο έτος", "Date picker": "Επιλογέας ημερομηνίας", "Hour": "Ώρα", "Day": "Ημέρα", "Week": "Εβδομάδα", - "2 weeks": "2 Εβδομάδες", + "2 weeks": "2 εβδομάδες", "Month": "Μήνας", - "3 months": "3 Μήνες", - "6 months": "6 Μήνες", + "3 months": "3 μήνες", + "6 months": "6 μήνες", "Custom interval": "Προσαρμοσμένο διάστημα", "Interval": "Διάστημα", "Step size": "Μέγεθος βήματος", - "Ok": "Ok" + "Ok": "ΟΚ" + } + }, + "doughnut": { + "doughnut-appearance": "Εμφάνιση δακτυλίου", + "layout": "Διάταξη", + "layout-default": "Προεπιλογή", + "layout-with-total": "Με συνολικό άθροισμα", + "central-total-value": "Κεντρική συνολική τιμή", + "doughnut-card-style": "Στυλ κάρτας δακτυλίου" + }, + "entities-hierarchy": { + "hierarchy-data-settings": "Ρυθμίσεις δεδομένων ιεραρχίας", + "relations-query-function": "Συνάρτηση αναζήτησης σχέσεων κόμβου", + "has-children-function": "Συνάρτηση ύπαρξης παιδιών κόμβου", + "node-state-settings": "Ρυθμίσεις κατάστασης κόμβου", + "node-opened-function": "Συνάρτηση προεπιλεγμένου ανοιχτού κόμβου", + "node-disabled-function": "Συνάρτηση απενεργοποιημένου κόμβου", + "display-settings": "Ρυθμίσεις εμφάνισης", + "node-icon-function": "Συνάρτηση εικονιδίου κόμβου", + "node-text-function": "Συνάρτηση κειμένου κόμβου", + "sort-settings": "Ρυθμίσεις ταξινόμησης", + "nodes-sort-function": "Συνάρτηση ταξινόμησης κόμβων" + }, + "edge": { + "display-default-title": "Εμφάνιση προεπιλεγμένου τίτλου" + }, + "gateway": { + "general-settings": "Γενικές ρυθμίσεις", + "widget-title": "Τίτλος widget", + "default-archive-file-name": "Προεπιλεγμένο όνομα αρχείου αρχειοθέτησης", + "device-type-for-new-gateway": "Τύπος συσκευής για νέο gateway", + "messages-settings": "Ρυθμίσεις μηνυμάτων", + "save-config-success-message": "Μήνυμα για επιτυχή αποθήκευση ρυθμίσεων gateway", + "device-name-exists-message": "Μήνυμα όταν η συσκευή με το καταχωρημένο όνομα υπάρχει ήδη", + "gateway-title": "Φόρμα gateway", + "read-only": "Μόνο για ανάγνωση", + "events-title": "Τίτλος φόρμας συμβάντων gateway", + "events-filter": "Φίλτρο συμβάντων", + "event-key-contains": "Το κλειδί συμβάντος περιέχει...", + "show-connector": "Εμφάνιση για τον connector", + "connector-state-param-key": "Κλειδί παραμέτρου κατάστασης connector", + "message": "Μήνυμα", + "level": "Επίπεδο", + "created-time": "Ώρα δημιουργίας" + }, + "gauge": { + "default-color": "Προεπιλεγμένο χρώμα", + "radial-gauge-settings": "Ρυθμίσεις κυκλικού δείκτη", + "ticks-settings": "Ρυθμίσεις δεικτών", + "min-value": "Ελάχιστη τιμή", + "max-value": "Μέγιστη τιμή", + "min-value-short": "ελάχ", + "max-value-short": "μέγ", + "start-ticks-angle": "Γωνία εκκίνησης δεικτών", + "ticks-angle": "Γωνία δεικτών", + "major-ticks": "Κύριοι δείκτες", + "major-ticks-count": "Αριθμός κύριων δεικτών", + "major-ticks-color": "Χρώμα κύριων δεικτών", + "minor-ticks": "Δευτερεύοντες δείκτες", + "minor-ticks-count": "Αριθμός δευτερευόντων δεικτών", + "minor-ticks-color": "Χρώμα δευτερευόντων δεικτών", + "tick-numbers-font": "Γραμματοσειρά αριθμών δεικτών", + "unit-title-settings": "Ρυθμίσεις τίτλου μονάδας", + "show-unit-title": "Τίτλος μονάδας", + "unit-title": "Τίτλος μονάδας", + "title-font": "Γραμματοσειρά τίτλου", + "units-settings": "Ρυθμίσεις μονάδων", + "units-font": "Γραμματοσειρά μονάδων", + "value-box-settings": "Ρυθμίσεις πλαισίου τιμής", + "show-value-box": "Εμφάνιση πλαισίου τιμής", + "value-box": "Πλαίσιο τιμής", + "value-int": "Αριθμός ψηφίων για ακέραιο μέρος της τιμής", + "value-text": "Κείμενο τιμής", + "value-text-shadow": "Σκίαση κειμένου τιμής", + "value-font": "Γραμματοσειρά κειμένου τιμής", + "rect-stroke-color-start": "Χρώμα περιγράμματος παραλληλογράμμου - αρχή διαβάθμισης", + "rect-stroke-color-end": "Χρώμα περιγράμματος παραλληλογράμμου - τέλος διαβάθμισης", + "background-color": "Χρώμα φόντου", + "shadow-color": "Χρώμα σκιάς", + "value-box-rect-stroke-color": "Χρώμα περιγράμματος πλαισίου τιμής", + "value-box-rect-stroke-color-end": "Χρώμα περιγράμματος πλαισίου τιμής - τέλος διαβάθμισης", + "value-box-background-color": "Χρώμα φόντου πλαισίου τιμής", + "value-box-shadow-color": "Χρώμα σκιάς πλαισίου τιμής", + "plate-settings": "Ρυθμίσεις βάσης", + "show-plate-border": "Περίγραμμα βάσης", + "plate-color": "Χρώμα βάσης", + "needle-settings": "Ρυθμίσεις δείκτη", + "needle-circle-size": "Μέγεθος κύκλου δείκτη", + "needle-color": "Χρώμα δείκτη", + "needle-color-start": "Χρώμα δείκτη - αρχή διαβάθμισης", + "needle-color-end": "Χρώμα δείκτη - τέλος διαβάθμισης", + "needle-color-shadow-up": "Χρώμα σκιάς άνω μισού δείκτη", + "needle-color-shadow-down": "Σκίαση κάτω μέρους", + "highlights-settings": "Ρυθμίσεις επισημάνσεων", + "highlights-width": "Πλάτος επισημάνσεων", + "highlights": "Επισημάνσεις", + "highlight-from": "Από", + "highlight-to": "Έως", + "highlight-color": "Χρώμα", + "no-highlights": "Δεν έχουν οριστεί επισημάνσεις", + "add-highlight": "Προσθήκη επισήμανσης", + "animation-settings": "Ρυθμίσεις κίνησης", + "enable-animation": "Κίνηση", + "animation-duration-rule": "Διάρκεια και κανόνας κίνησης", + "animation-duration": "Διάρκεια κίνησης", + "animation-rule": "Κανόνας κίνησης", + "animation-linear": "Γραμμική", + "animation-quad": "Διτετραγωνική", + "animation-quint": "Πενταγωνική", + "animation-cycle": "Κυκλική", + "animation-bounce": "Αναπήδηση", + "animation-elastic": "Ελαστική", + "animation-dequad": "Αποδιτετραγωνική", + "animation-dequint": "Αποπενταγωνική", + "animation-decycle": "Αποκυκλική", + "animation-debounce": "Αποαναπήδηση", + "animation-delastic": "Αποελαστική", + "linear-gauge-settings": "Ρυθμίσεις γραμμικού δείκτη", + "bar-stroke": "Περίγραμμα μπάρας", + "bar-stroke-width": "Πλάτος περιγράμματος μπάρας", + "bar-stroke-color": "Χρώμα περιγράμματος μπάρας", + "bar-background-color": "Χρώμα φόντου μπάρας - αρχή διαβάθμισης", + "bar-background-color-end": "Χρώμα φόντου μπάρας - τέλος διαβάθμισης", + "progress-bar-color": "Χρώμα γραμμής προόδου", + "progress-bar": "Γραμμή προόδου", + "progress-bar-color-start": "Χρώμα γραμμής προόδου - αρχή διαβάθμισης", + "progress-bar-color-end": "Χρώμα γραμμής προόδου - τέλος διαβάθμισης", + "major-ticks-names": "Ονόματα κύριων δεικτών", + "show-stroke-ticks": "Εμφάνιση περιγράμματος δεικτών", + "major-ticks-font": "Γραμματοσειρά κύριων δεικτών", + "border-color": "Χρώμα περιγράμματος", + "border-width": "Πλάτος περιγράμματος", + "needle-circle": "Κύκλος δείκτη", + "needle-circle-color": "Χρώμα κύκλου δείκτη", + "animation-target": "Στόχος κίνησης", + "animation-target-needle": "Δείκτης", + "animation-target-plate": "Βάση", + "common-settings": "Γενικές ρυθμίσεις δείκτη", + "gauge-type": "Τύπος δείκτη", + "gauge-type-arc": "Τόξο", + "gauge-type-donut": "Ντόνατ", + "gauge-type-horizontal-bar": "Οριζόντια μπάρα", + "gauge-type-vertical-bar": "Κάθετη μπάρα", + "donut-start-angle": "Γωνία έναρξης (μοίρες)", + "bar-settings": "Ρυθμίσεις μπάρας δείκτη", + "relative-bar-width": "Σχετικό πλάτος μπάρας", + "neon-glow-brightness": "Φωτεινότητα νεον εφέ (0-100)", + "neon-glow-brightness-hint": "0 - απενεργοποίηση εφέ", + "stripes-thickness": "Πάχος γραμμών", + "stripes-thickness-hint": "0 - χωρίς γραμμές", + "rounded-line-cap": "Στρογγυλεμένο άκρο γραμμής", + "bar-color-settings": "Ρυθμίσεις χρώματος μπάρας", + "use-precise-level-color-values": "Χρήση ακριβών επιπέδων χρώματος", + "bar-colors": "Χρώματα μπάρας, από χαμηλότερο προς υψηλότερο", + "color": "Χρώμα", + "no-bar-colors": "Δεν έχουν οριστεί χρώματα μπάρας", + "add-bar-color": "Προσθήκη χρώματος μπάρας", + "from": "Από", + "to": "Έως", + "fixed-level-colors": "Χρώματα μπάρας με χρήση ορίων", + "gauge-title-settings": "Ρυθμίσεις τίτλου δείκτη", + "show-gauge-title": "Εμφάνιση τίτλου δείκτη", + "gauge-title": "Τίτλος δείκτη", + "gauge-title-font": "Γραμματοσειρά τίτλου δείκτη", + "unit-title-and-timestamp-settings": "Ρυθμίσεις τίτλου μονάδων και χρονικής σήμανσης", + "show-timestamp": "Χρονική σήμανση", + "timestamp-format": "Μορφή χρονικής σήμανσης", + "label-font": "Γραμματοσειρά ετικέτας κάτω από την τιμή", + "value-settings": "Ρυθμίσεις τιμής", + "show-value": "Εμφάνιση κειμένου τιμής", + "min-max-settings": "Ρυθμίσεις ετικετών ελάχιστης/μέγιστης τιμής", + "show-min-max": "Εμφάνιση ελάχιστης και μέγιστης τιμής", + "min-max-font": "Γραμματοσειρά ετικετών ελάχιστης και μέγιστης τιμής", + "show-ticks": "Εμφάνιση δεικτών", + "tick-width": "Πλάτος δείκτη", + "tick-color": "Χρώμα δείκτη", + "tick-values": "Τιμές δεικτών", + "no-tick-values": "Δεν έχουν οριστεί τιμές δεικτών", + "add-tick-value": "Προσθήκη τιμής δείκτη", + "gauge-appearance": "Εμφάνιση δείκτη", + "units-title": "Τίτλος μονάδων", + "value": "Τιμή", + "ticks": "Δείκτες", + "arrow-and-scale-color": "Προεπιλεγμένο χρώμα δείκτη και κλίμακας", + "scale-settings": "Ρυθμίσεις κλίμακας", + "scale": "Κλίμακα", + "scale-color": "Χρώματα κλίμακας", + "compass-appearance": "Εμφάνιση πυξίδας", + "label": "Ετικέτα", + "labels": "Ετικέτες", + "label-style": "Στιλ ετικέτας", + "simple-gauge-type": "Τύπος", + "gauge-bar-background": "Φόντο μπάρας δείκτη", + "bar-color": "Χρώμα μπάρας", + "min-and-max-value": "Ελάχιστη και μέγιστη τιμή", + "min-and-max-label": "Ετικέτες ελάχιστης και μέγιστης τιμής", + "font": "Γραμματοσειρά", + "tick-width-and-color": "Πλάτος και χρώμα δείκτη", + "min-max-validation-text": "Η μέγιστη τιμή πρέπει να είναι μεγαλύτερη από την ελάχιστη" + }, + "gpio": { + "pin": "Ακροδέκτης", + "label": "Ετικέτα", + "row": "Γραμμή", + "column": "Στήλη", + "color": "Χρώμα", + "panel-settings": "Ρυθμίσεις πίνακα", + "background-color": "Χρώμα φόντου", + "gpio-switches": "Διακόπτες GPIO", + "no-gpio-switches": "Δεν έχουν διαμορφωθεί διακόπτες GPIO", + "add-gpio-switch": "Προσθήκη διακόπτη GPIO", + "gpio-status-request": "Αίτημα κατάστασης GPIO", + "method-name": "Όνομα μεθόδου", + "method-body": "Σώμα μεθόδου", + "gpio-status-change-request": "Αίτημα αλλαγής κατάστασης GPIO", + "parse-gpio-status-function": "Συνάρτηση ανάλυσης κατάστασης GPIO", + "gpio-leds": "LEDs GPIO", + "no-gpio-leds": "Δεν έχουν διαμορφωθεί LEDs GPIO", + "add-gpio-led": "Προσθήκη LED GPIO" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, + "input-widgets": { + "attribute-not-allowed": "Η παράμετρος χαρακτηριστικού δεν μπορεί να χρησιμοποιηθεί σε αυτό το widget", + "blocked-location": "Η γεωτοποθεσία είναι αποκλεισμένη στο πρόγραμμα περιήγησής σας", + "claim-device": "Διεκδίκηση συσκευής", + "claim-failed": "Αποτυχία διεκδίκησης της συσκευής!", + "claim-not-found": "Η συσκευή δεν βρέθηκε!", + "claim-successful": "Η συσκευή διεκδικήθηκε με επιτυχία!", + "date": "Ημερομηνία", + "device-name": "Όνομα συσκευής", + "device-name-required": "Το όνομα συσκευής είναι υποχρεωτικό", + "discard-changes": "Απόρριψη αλλαγών", + "entity-attribute-required": "Απαιτείται χαρακτηριστικό οντότητας", + "entity-coordinate-required": "Απαιτούνται και τα δύο πεδία, γεωγραφικό πλάτος και μήκος", + "entity-timeseries-required": "Απαιτείται χρονοσειρά οντότητας", + "get-location": "Λήψη τρέχουσας τοποθεσίας", + "invalid-date": "Μη έγκυρη ημερομηνία", + "latitude": "Γεωγραφικό πλάτος", + "longitude": "Γεωγραφικό μήκος", + "min-value-error": "Ελάχιστη τιμή είναι {{value}}", + "max-value-error": "Μέγιστη τιμή είναι {{value}}", + "not-allowed-entity": "Η επιλεγμένη οντότητα δεν μπορεί να έχει κοινόχρηστα χαρακτηριστικά", + "no-attribute-selected": "Δεν έχει επιλεγεί χαρακτηριστικό", + "no-datakey-selected": "Δεν έχει επιλεγεί κλειδί δεδομένων", + "no-coordinate-specified": "Δεν έχει καθοριστεί κλειδί δεδομένων για γεωγραφικό πλάτος/μήκος", + "no-entity-selected": "Δεν έχει επιλεγεί οντότητα", + "no-image": "Καμία εικόνα", + "no-support-geolocation": "Το πρόγραμμα περιήγησής σας δεν υποστηρίζει γεωτοποθεσία", + "no-support-web-camera": "Το πρόγραμμα περιήγησής σας δεν υποστηρίζει κάμερες", + "enable-https-use-widget": "Ενεργοποιήστε το HTTPS για να χρησιμοποιήσετε αυτό το widget", + "no-found-your-camera": "Δεν βρέθηκε η κάμερά σας", + "no-permission-camera": "Η άδεια απορρίφθηκε από τον χρήστη / Αυτός ο ιστότοπος δεν έχει άδεια χρήσης της κάμερας", + "no-timeseries-selected": "Δεν έχει επιλεγεί χρονοσειρά", + "secret-key": "Μυστικό κλειδί", + "secret-key-required": "Το μυστικό κλειδί είναι υποχρεωτικό", + "switch-attribute-value": "Αλλαγή τιμής χαρακτηριστικού οντότητας", + "switch-camera": "Εναλλαγή κάμερας", + "switch-timeseries-value": "Αλλαγή τιμής χρονοσειράς οντότητας", + "take-photo": "Λήψη φωτογραφίας", + "time": "Ώρα", + "timeseries-not-allowed": "Η παράμετρος χρονοσειράς δεν μπορεί να χρησιμοποιηθεί σε αυτό το widget", + "update-failed": "Η ενημέρωση απέτυχε", + "update-successful": "Η ενημέρωση ήταν επιτυχής", + "update-attribute": "Ενημέρωση χαρακτηριστικού", + "update-timeseries": "Ενημέρωση χρονοσειράς", + "value": "Τιμή", + "general-settings": "Γενικές ρυθμίσεις", + "widget-title": "Τίτλος widget", + "claim-button-label": "Ετικέτα κουμπιού διεκδίκησης", + "show-secret-key-field": "Εμφάνιση πεδίου 'Μυστικό κλειδί'", + "labels-settings": "Ρυθμίσεις ετικετών", + "show-labels": "Εμφάνιση ετικετών", + "device-name-label": "Ετικέτα για το πεδίο εισαγωγής ονόματος συσκευής", + "secret-key-label": "Ετικέτα για το πεδίο εισαγωγής μυστικού κλειδιού", + "messages-settings": "Ρυθμίσεις μηνυμάτων", + "claim-device-success-message": "Μήνυμα επιτυχούς διεκδίκησης συσκευής", + "claim-device-not-found-message": "Μήνυμα όταν δεν βρεθεί η συσκευή", + "claim-device-failed-message": "Μήνυμα αποτυχίας διεκδίκησης συσκευής", + "claim-device-name-required-message": "Μήνυμα σφάλματος 'Απαιτείται όνομα συσκευής'", + "claim-device-secret-key-required-message": "Μήνυμα σφάλματος 'Απαιτείται μυστικό κλειδί'", + "show-label": "Εμφάνιση ετικέτας", + "label": "Ετικέτα", + "required": "Υποχρεωτικό", + "required-error-message": "Μήνυμα σφάλματος 'Υποχρεωτικό'", + "show-result-message": "Εμφάνιση μηνύματος αποτελέσματος", + "integer-field-settings": "Ρυθμίσεις πεδίου ακέραιου αριθμού", + "min-value": "Ελάχιστη τιμή", + "max-value": "Μέγιστη τιμή", + "double-field-settings": "Ρυθμίσεις πεδίου δεκαδικού αριθμού", + "text-field-settings": "Ρυθμίσεις πεδίου κειμένου", + "min-length": "Ελάχιστο μήκος", + "max-length": "Μέγιστο μήκος", + "checkbox-settings": "Ρυθμίσεις πλαισίου ελέγχου", + "true-label": "Ετικέτα όταν είναι επιλεγμένο", + "false-label": "Ετικέτα όταν δεν είναι επιλεγμένο", + "image-input-settings": "Ρυθμίσεις εισόδου εικόνας", + "display-preview": "Εμφάνιση προεπισκόπησης", + "display-clear-button": "Εμφάνιση κουμπιού εκκαθάρισης", + "display-apply-button": "Εμφάνιση κουμπιού εφαρμογής", + "display-discard-button": "Εμφάνιση κουμπιού απόρριψης", + "datetime-field-settings": "Ρυθμίσεις πεδίου ημερομηνίας/ώρας", + "display-time-input": "Εμφάνιση πεδίου ώρας", + "latitude-key-name": "Όνομα κλειδιού γεωγραφικού πλάτους", + "longitude-key-name": "Όνομα κλειδιού γεωγραφικού μήκους", + "show-get-location-button": "Εμφάνιση κουμπιού 'Λήψη τρέχουσας τοποθεσίας'", + "use-high-accuracy": "Χρήση υψηλής ακρίβειας", + "location-fields-settings": "Ρυθμίσεις πεδίων τοποθεσίας", + "latitude-label": "Ετικέτα γεωγραφικού πλάτους", + "longitude-label": "Ετικέτα γεωγραφικού μήκους", + "input-fields-alignment": "Στοίχιση πεδίων εισόδου", + "input-fields-alignment-column": "Στήλη (προεπιλογή)", + "input-fields-alignment-row": "Γραμμή", + "layout": "Διάταξη", + "row-gap": "Κενό μεταξύ γραμμών σε pixel", + "column-gap": "Κενό μεταξύ στηλών σε pixel", + "latitude-field-required": "Απαιτείται πεδίο γεωγραφικού πλάτους", + "longitude-field-required": "Απαιτείται πεδίο γεωγραφικού μήκους", + "attribute-settings": "Ρυθμίσεις χαρακτηριστικού", + "widget-mode": "Λειτουργία widget", + "widget-mode-update-attribute": "Ενημέρωση χαρακτηριστικού", + "widget-mode-update-timeseries": "Ενημέρωση χρονοσειράς", + "attribute-scope": "Εύρος χαρακτηριστικού", + "attribute-scope-server": "Χαρακτηριστικό διακομιστή", + "attribute-scope-shared": "Κοινόχρηστο χαρακτηριστικό", + "value-required": "Απαιτείται τιμή", + "image-settings": "Ρυθμίσεις εικόνας", + "image-format": "Μορφή εικόνας", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "Ποιότητα εικόνας για συμπίεση τύπου jpeg/webp", + "max-image-width": "Μέγιστο πλάτος εικόνας", + "max-image-height": "Μέγιστο ύψος εικόνας", + "action-buttons": "Κουμπιά ενεργειών", + "show-action-buttons": "Εμφάνιση κουμπιών ενεργειών", + "update-all-values": "Ενημέρωση όλων των τιμών, όχι μόνο των τροποποιημένων", + "save-button-label": "Ετικέτα κουμπιού 'ΑΠΟΘΗΚΕΥΣΗ'", + "reset-button-label": "Ετικέτα κουμπιού 'ΑΝΑΚΛΗΣΗ'", + "group-settings": "Ρυθμίσεις ομάδας", + "show-group-title": "Εμφάνιση τίτλου για ομάδα πεδίων που σχετίζονται με διαφορετικές οντότητες", + "group-title": "Τίτλος ομάδας", + "fields-alignment": "Στοίχιση πεδίων", + "fields-alignment-row": "Γραμμή (προεπιλογή)", + "fields-alignment-column": "Στήλη", + "fields-in-row": "Αριθμός πεδίων ανά γραμμή", + "option-value": "Τιμή (γράψτε 'null' για κενή επιλογή)", + "option-label": "Ετικέτα", + "hide-input-field": "Απόκρυψη πεδίου εισόδου", + "datakey-type": "Τύπος κλειδιού δεδομένων", + "datakey-type-server": "Χαρακτηριστικό διακομιστή (προεπιλογή)", + "datakey-type-shared": "Κοινόχρηστο χαρακτηριστικό", + "datakey-type-timeseries": "Χρονοσειρά", + "datakey-value-type": "Τύπος τιμής κλειδιού δεδομένων", + "datakey-value-type-string": "Συμβολοσειρά", + "datakey-value-type-double": "Δεκαδικός", + "datakey-value-type-integer": "Ακέραιος", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "Λογική (πλαίσιο ελέγχου)", + "datakey-value-type-boolean-switch": "Λογική (διακόπτης)", + "datakey-value-type-date-time": "Ημερομηνία & Ώρα", + "datakey-value-type-date": "Ημερομηνία", + "datakey-value-type-time": "Ώρα", + "datakey-value-type-select": "Επιλογή", + "datakey-value-type-radio": "Κουμπί επιλογής", + "datakey-value-type-color": "Χρώμα", + "value-is-required": "Απαιτείται τιμή", + "ability-to-edit-attribute": "Δυνατότητα επεξεργασίας χαρακτηριστικού", + "ability-to-edit-attribute-editable": "Επεξεργάσιμο (προεπιλογή)", + "ability-to-edit-attribute-disabled": "Απενεργοποιημένο", + "ability-to-edit-attribute-readonly": "Μόνο για ανάγνωση", + "disable-on-datakey-name": "Απενεργοποίηση όταν άλλη τιμή κλειδιού δεδομένων είναι false (εισάγετε όνομα)", + "field-appearance": "Εμφάνιση πεδίου", + "appearance-fill": "Συμπληρωμένο", + "appearance-outline": "Περίγραμμα", + "subscript-sizing": "Μέγεθος υποσελίδας", + "subscript-sizing-fixed": "Σταθερό", + "subscript-sizing-dynamic": "Δυναμικό", + "slide-toggle-settings": "Ρυθμίσεις εναλλαγής διαφάνειας", + "slide-toggle-label-position": "Θέση ετικέτας διακόπτη", + "slide-toggle-label-position-after": "Μετά", + "slide-toggle-label-position-before": "Πριν", + "select-options": "Επιλογές επιλογής", + "no-select-options": "Δεν έχουν ρυθμιστεί επιλογές", + "add-select-option": "Προσθήκη επιλογής", + "numeric-field-settings": "Ρυθμίσεις αριθμητικού πεδίου", + "step-interval": "Βήμα μεταξύ τιμών", + "error-messages": "Μηνύματα σφάλματος", + "min-value-error-message": "Μήνυμα σφάλματος για 'Ελάχιστη τιμή'", + "max-value-error-message": "Μήνυμα σφάλματος για 'Μέγιστη τιμή'", + "invalid-date-error-message": "Μήνυμα σφάλματος για 'Μη έγκυρη ημερομηνία'", + "invalid-JSON-error-message": "Μήνυμα σφάλματος για 'Μη έγκυρο JSON'", + "icon-settings": "Ρυθμίσεις εικονιδίου", + "dialog-editor-settings": "Ρυθμίσεις επεξεργαστή διαλόγου", + "use-custom-icon": "Χρήση προσαρμοσμένου εικονιδίου", + "input-cell-icon": "Εικονίδιο πριν από το πεδίο εισόδου", + "value-conversion-settings": "Ρυθμίσεις μετατροπής τιμής", + "get-value-settings": "Ρυθμίσεις λήψης τιμής", + "use-get-value-function": "Χρήση συνάρτησης getValue", + "get-value-function": "Συνάρτηση getValue", + "set-value-settings": "Ρυθμίσεις ορισμού τιμής", + "use-set-value-function": "Χρήση συνάρτησης setValue", + "set-value-function": "Συνάρτηση setValue", + "json-invalid": "Η τιμή JSON έχει μη έγκυρη μορφή", + "title": "Τίτλος", + "cancel-button-label": "Ετικέτα κουμπιού 'Ακύρωση'", + "radio-button-settings": "Ρυθμίσεις κουμπιών επιλογής", + "color": "Χρώμα", + "columns": "Στήλες", + "radio-options": "Επιλογές κουμπιών επιλογής", + "no-radio-options": "Δεν έχουν ρυθμιστεί επιλογές κουμπιών", + "add-radio-option": "Προσθήκη επιλογής κουμπιού", + "radio-label-position": "Θέση ετικέτας", + "radio-label-position-before": "Πριν", + "radio-label-position-after": "Μετά" + }, + "invalid-qr-code-text": "Μη έγκυρο κείμενο εισόδου για τον QR κώδικα. Η είσοδος πρέπει να είναι τύπου string", + "qr-code": { + "use-qr-code-text-function": "Χρήση συνάρτησης κειμένου QR κώδικα", + "qr-code-text-pattern": "Μοτίβο κειμένου QR κώδικα (π.χ. '${entityName} | ${keyName} - some text.')", + "qr-code-text-pattern-hint": "Το μοτίβο κειμένου QR κώδικα χρησιμοποιεί την τιμή του πρώτου εντοπισμένου κλειδιού στις οντότητες του ψευδωνύμου.", + "qr-code-text-pattern-required": "Απαιτείται μοτίβο κειμένου QR κώδικα.", + "qr-code-text-function": "Συνάρτηση κειμένου QR κώδικα" + }, + "label-widget": { + "label-pattern": "Μοτίβο", + "label-pattern-hint": "Υπόδειξη: π.χ. 'Κείμενο ${keyName} μονάδες.' ή ${#<key index>} μονάδες", + "label-pattern-required": "Απαιτείται μοτίβο", + "label-position": "Θέση (ποσοστό σε σχέση με το φόντο)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Χρώμα φόντου", + "font-settings": "Ρυθμίσεις γραμματοσειράς", + "background-image": "Εικόνα φόντου", + "labels": "Ετικέτες", + "no-labels": "Δεν έχουν ρυθμιστεί ετικέτες", + "add-label": "Προσθήκη ετικέτας" + }, + "navigation": { + "title": "Τίτλος", + "navigation-path": "Διαδρομή πλοήγησης", + "filter-type": "Τύπος φίλτρου", + "filter-type-all": "Όλα τα στοιχεία", + "filter-type-include": "Συμπερίληψη στοιχείων", + "filter-type-exclude": "Εξαίρεση στοιχείων", + "items": "Στοιχεία", + "enter-urls-to-filter": "Εισαγάγετε διευθύνσεις URL για φιλτράρισμα..." + }, + "persistent-table": { + "rpc-id": "Αναγνωριστικό RPC", + "message-type": "Τύπος μηνύματος", + "method": "Μέθοδος", + "params": "Παράμετροι", + "created-time": "Χρόνος δημιουργίας", + "expiration-time": "Χρόνος λήξης", + "retries": "Προσπάθειες", + "status": "Κατάσταση", + "filter": "Φίλτρο", + "refresh": "Ανανέωση", + "add": "Προσθήκη RPC αιτήματος", + "details": "Λεπτομέρειες", + "delete": "Διαγραφή", + "delete-request-title": "Διαγραφή μόνιμου RPC αιτήματος", + "delete-request-text": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το αίτημα;", + "details-title": "Λεπτομέρειες RPC ID:", + "additional-info": "Πρόσθετες πληροφορίες", + "response": "Απάντηση", + "any-status": "Οποιαδήποτε κατάσταση", + "rpc-status-list": "Λίστα καταστάσεων RPC", + "no-request-prompt": "Δεν υπάρχει διαθέσιμο αίτημα για εμφάνιση", + "send-request": "Αποστολή αιτήματος", + "add-title": "Δημιουργία μόνιμου RPC αιτήματος", + "method-error": "Η μέθοδος είναι υποχρεωτική.", + "timeout-error": "Η ελάχιστη τιμή χρονικού ορίου είναι 5000 (5 δευτερόλεπτα).", + "white-space-error": "Τα κενά δεν επιτρέπονται.", + "rpc-status": { + "QUEUED": "ΣΕ ΟΥΡΑ", + "SENT": "ΑΠΕΣΤΑΛΜΕΝΟ", + "DELIVERED": "ΠΑΡΑΔΟΘΗΚΕ", + "SUCCESSFUL": "ΕΠΙΤΥΧΕΣ", + "TIMEOUT": "ΛΗΞΗ ΧΡΟΝΟΥ", + "EXPIRED": "ΕΛΗΞΕ", + "FAILED": "ΑΠΕΤΥΧΕ" + }, + "rpc-search-status-all": "ΟΛΑ", + "message-types": { + "false": "Δύο κατευθύνσεων", + "true": "Μονής κατεύθυνσης" + }, + "general-settings": "Γενικές ρυθμίσεις", + "enable-filter": "Ενεργοποίηση φίλτρου", + "enable-sticky-header": "Εμφάνιση κεφαλίδας κατά την κύλιση", + "enable-sticky-action": "Εμφάνιση στήλης ενεργειών κατά την κύλιση", + "display-request-details": "Εμφάνιση λεπτομερειών αιτήματος", + "allow-send-request": "Να επιτρέπεται η αποστολή RPC αιτήματος", + "allow-delete-request": "Να επιτρέπεται η διαγραφή αιτήματος", + "columns-settings": "Ρυθμίσεις στηλών", + "display-columns": "Στήλες για εμφάνιση", + "column": "Στήλη", + "no-columns-found": "Δεν βρέθηκαν στήλες", + "no-columns-matching": "Δεν βρέθηκε η '{{column}}'." + }, + "range-chart": { + "chart": "Διάγραμμα", + "data-zoom": "Μεγέθυνση δεδομένων", + "range-chart-appearance": "Εμφάνιση διαγράμματος εύρους", + "range-colors": "Χρώματα εύρους", + "out-of-range-color": "Χρώμα εκτός εύρους", + "show-range-thresholds": "Εμφάνιση ορίων εύρους", + "range-thresholds-settings": "Ρυθμίσεις ορίων εύρους", + "fill-area": "Γέμισμα περιοχής", + "fill-area-opacity": "Διαφάνεια γεμίσματος περιοχής", + "range-chart-style": "Στυλ διαγράμματος εύρους" + }, + "rpc": { + "value-settings": "Ρυθμίσεις τιμής", + "initial-value": "Αρχική τιμή", + "retrieve-value-settings": "Ρυθμίσεις ανάκτησης τιμής on/off", + "retrieve-value-method": "Ανάκτηση τιμής μέσω μεθόδου", + "retrieve-value-method-none": "Χωρίς ανάκτηση", + "retrieve-value-method-rpc": "Κλήση RPC μεθόδου για ανάκτηση τιμής", + "retrieve-value-method-attribute": "Εγγραφή σε χαρακτηριστικό", + "retrieve-value-method-timeseries": "Εγγραφή σε χρονοσειρά", + "attribute-value-key": "Κλειδί χαρακτηριστικού", + "timeseries-value-key": "Κλειδί χρονοσειράς", + "get-value-method": "RPC μέθοδος ανάκτησης τιμής", + "parse-value-function": "Συνάρτηση ανάλυσης τιμής", + "update-value-settings": "Ρυθμίσεις ενημέρωσης τιμής", + "set-value-method": "RPC μέθοδος ορισμού τιμής", + "convert-value-function": "Συνάρτηση μετατροπής τιμής", + "rpc-settings": "Ρυθμίσεις RPC", + "request-timeout": "Χρονικό όριο RPC αιτήματος (ms)", + "persistent-rpc-settings": "Ρυθμίσεις επίμονου RPC", + "request-persistent": "Επίμονο RPC αίτημα", + "persistent-polling-interval": "Διάστημα ερωτήσεων (ms) για απάντηση επίμονου RPC", + "common-settings": "Γενικές ρυθμίσεις", + "switch-title": "Τίτλος διακόπτη", + "show-on-off-labels": "Εμφάνιση ετικετών on/off", + "slide-toggle-label": "Ετικέτα διακόπτη εναλλαγής", + "label-position": "Θέση ετικέτας", + "label-position-before": "Πριν", + "label-position-after": "Μετά", + "slider-color": "Χρώμα διακόπτη", + "slider-color-primary": "Πρωτεύον", + "slider-color-accent": "Έμφαση", + "slider-color-warn": "Προειδοποίηση", + "button-style": "Στυλ κουμπιού", + "button-raised": "Ανασηκωμένο κουμπί", + "button-primary": "Πρωτεύον χρώμα", + "button-background-color": "Χρώμα φόντου κουμπιού", + "button-text-color": "Χρώμα κειμένου κουμπιού", + "widget-title": "Τίτλος γραφικού στοιχείου", + "button-label": "Ετικέτα κουμπιού", + "device-attribute-scope": "Πεδίο χαρακτηριστικού συσκευής", + "server-attribute": "Χαρακτηριστικό διακομιστή", + "shared-attribute": "Κοινόχρηστο χαρακτηριστικό", + "device-attribute-parameters": "Παράμετροι χαρακτηριστικού συσκευής", + "is-one-way-command": "Είναι μονόδρομη εντολή", + "rpc-method": "RPC μέθοδος", + "rpc-method-params": "Παράμετροι RPC μεθόδου", + "show-rpc-error": "Εμφάνιση σφάλματος εκτέλεσης RPC εντολής", + "led-title": "Τίτλος LED", + "led-color": "Χρώμα LED", + "check-status-settings": "Ρυθμίσεις ελέγχου κατάστασης", + "perform-rpc-status-check": "Εκτέλεση ελέγχου κατάστασης συσκευής μέσω RPC", + "retrieve-led-status-value-method": "Ανάκτηση τιμής κατάστασης LED μέσω μεθόδου", + "led-status-value-attribute": "Χαρακτηριστικό συσκευής με τιμή κατάστασης LED", + "led-status-value-timeseries": "Χρονοσειρά συσκευής με τιμή κατάστασης LED", + "check-status-method": "RPC μέθοδος ελέγχου κατάστασης συσκευής", + "parse-led-status-value-function": "Συνάρτηση ανάλυσης τιμής κατάστασης LED", + "knob-title": "Τίτλος περιστροφικού ελεγκτή", + "min-value": "Ελάχιστη τιμή", + "max-value": "Μέγιστη τιμή" + }, + "maps": { + "map-type": { + "type": "Τύπος χάρτη", + "map": "Χάρτης", + "image": "Εικόνα" + }, + "image": { + "image-source": "Πηγή εικόνας", + "image-source-image": "Εικόνα", + "image-source-entity-key": "Κλειδί οντότητας", + "source-entity-alias": "Ψευδώνυμο πηγής οντότητας", + "image-url-key": "Κλειδί URL εικόνας", + "image-url-key-required": "Το κλειδί URL εικόνας είναι υποχρεωτικό" + }, + "control": { + "map-controls": "Χειριστήρια χάρτη", + "position": "Θέση", + "position-topleft": "Πάνω αριστερά", + "position-topright": "Πάνω δεξιά", + "position-bottomleft": "Κάτω αριστερά", + "position-bottomright": "Κάτω δεξιά", + "zoom-actions": "Ενέργειες εστίασης", + "zoom-scroll": "Κύλιση", + "zoom-double-click": "Διπλό κλικ", + "zoom-control-buttons": "Κουμπιά ελέγχου", + "scale": "Κλίμακα", + "scale-metric": "Μέτρικο", + "scale-imperial": "Αγγλοσαξονικό", + "switch-to-drag-mode-using-button": "Εναλλαγή σε λειτουργία μεταφοράς μέσω κουμπιού" + }, + "timeline": { + "control-panel": "Πίνακας ελέγχου χρονολογίου", + "time-step": "Βήμα χρόνου", + "speed-options": "Επιλογές ταχύτητας", + "timestamp": "Χρονική σήμανση", + "snap-to-real-location": "Σύμπτωση με την πραγματική τοποθεσία", + "location-snap-filter-function": "Συνάρτηση φίλτρου τοποθέτησης", + "no-trips-data-available": "Δεν υπάρχουν διαθέσιμα δεδομένα διαδρομών" + }, + "map-action": { + "map-action-buttons": "Κουμπιά ενεργειών χάρτη", + "label": "Ετικέτα", + "icon": "Εικονίδιο", + "color": "Χρώμα", + "action": "Ενέργεια", + "add-button": "Προσθήκη κουμπιού", + "no-action-buttons-configured": "Δεν έχουν διαμορφωθεί κουμπιά ενεργειών", + "remove-action-button": "Αφαίρεση κουμπιού ενέργειας", + "map-action-button": "Κουμπί ενέργειας χάρτη", + "button-requires": "Το κουμπί απαιτεί ετικέτα ή εικονίδιο" + }, + "common": { + "common-map-settings": "Κοινές ρυθμίσεις χάρτη", + "fit-map-bounds": "Προσαρμογή ορίων χάρτη για κάλυψη όλων των δεικτών", + "default-map-center-position": "Προεπιλεγμένη κεντρική θέση χάρτη", + "default-map-zoom-level": "Προεπιλεγμένο επίπεδο μεγέθυνσης", + "entities-limit": "Όριο φορτωμένων οντοτήτων" + }, + "layer": { + "label": "Ετικέτα", + "layer": "Επίπεδο", + "layers": "Επίπεδα", + "map-layers": "Επίπεδα χάρτη", + "add-layer": "Προσθήκη επιπέδου", + "layer-settings": "Ρυθμίσεις επιπέδου", + "remove-layer": "Αφαίρεση επιπέδου", + "no-layers": "Δεν υπάρχουν διαμορφωμένα επίπεδα", + "roadmap": "Οδικός χάρτης", + "satellite": "Δορυφορικός", + "hybrid": "Υβριδικός", + "reference": { + "reference-layer": "Επίπεδο αναφοράς", + "no-layer": "Χωρίς επίπεδο", + "openstreetmap-hybrid": "OpenStreetMap Υβριδικός", + "world-edition-hybrid": "Παγκόσμια Έκδοση Υβριδική", + "enhanced-contrast-hybrid": "Υβριδικός με ενισχυμένη αντίθεση" + }, + "provider": { + "provider": "Πάροχος", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Οδικός χάρτης", + "satellite": "Δορυφορικός", + "hybrid": "Υβριδικός", + "terrain": "Ανάγλυφο" + }, + "here": { + "title": "HERE", + "normal-day": "Κανονική ημέρα", + "normal-night": "Κανονική νύχτα", + "hybrid-day": "Υβριδική ημέρα", + "terrain-day": "Ανάγλυφο ημέρας" + }, + "tencent": { + "title": "Tencent", + "normal": "Κανονικό", + "satellite": "Δορυφορικό", + "terrain": "Ανάγλυφο" + }, + "custom": { + "title": "Προσαρμοσμένο", + "tile-url": "Tile URL" + } + }, + "credentials": { + "credentials": "Διαπιστευτήρια", + "api-key": "API Key" + } + }, + "overlays": { + "overlays": "Επικαλύψεις", + "overlays-hint": "Διαμορφώστε πηγές δεδομένων, εμφάνιση, συμπεριφορά, επιλογές επεξεργασίας και ομαδοποίηση για οντότητες του χάρτη", + "trips": "Διαδρομές", + "markers": "Δείκτες", + "polygons": "Πολύγωνα", + "circles": "Κύκλοι" + }, + "data-layer": { + "source": "Πηγή", + "filter": "Φίλτρο", + "additional-data-keys": "Πρόσθετα κλειδιά δεδομένων", + "additional-datasources": "Πρόσθετες πηγές δεδομένων", + "additional-datasources-hint": "Πηγή δεδομένων για πρόσβαση σε ιδιότητες ή τηλεμετρία από οντότητες που δεν εμφανίζονται στο χάρτη, χρησιμοποιήσιμη σε λειτουργίες επικαλύψεων.", + "more-datasources": "Περισσότερες πηγές δεδομένων", + "data-keys": "Κλειδιά δεδομένων", + "add-datasource": "Προσθήκη πηγής δεδομένων", + "no-datasources": "Δεν έχουν διαμορφωθεί πηγές δεδομένων", + "remove-datasource": "Αφαίρεση πηγής δεδομένων", + "behavior": "Συμπεριφορά", + "on-click": "Με κλικ", + "on-click-hint": "Ενέργεια που εκτελείται όταν ο χρήστης κάνει κλικ στο στοιχείο του χάρτη.", + "groups": "Ομάδες", + "groups-hint": "Λίστα ονομάτων ομάδων που έχουν εκχωρηθεί στην επικάλυψη, χρησιμοποιείται για την εναλλαγή της ορατότητάς της στον χάρτη.", + "color": "Χρώμα", + "color-settings": "Ρυθμίσεις χρώματος", + "color-type-constant": "Σταθερό", + "color-type-range": "Εύρος", + "color-type-function": "Συνάρτηση", + "color-range-source-key": "Κλειδί πηγής εύρους χρώματος", + "color-range-source-key-required": "Απαιτείται κλειδί πηγής εύρους χρώματος", + "color-range": "Εύρος χρώματος", + "color-function": "Συνάρτηση χρώματος", + "label": "Ετικέτα", + "tooltip": "Υπόδειξη", + "pattern-type-pattern": "Πρότυπο", + "pattern-type-function": "Συνάρτηση", + "label-pattern": "Ετικέτα (παραδείγματα προτύπων: '${entityName}', '${entityName}: (Text ${keyName} units.)' )", + "label-function": "Συνάρτηση ετικέτας", + "tooltip-pattern": "Υπόδειξη (π.χ. 'Text ${keyName} units.' ή Link text)", + "tooltip-function": "Συνάρτηση υπόδειξης", + "tooltip-trigger": "Ενεργοποίηση υπόδειξης", + "tooltip-trigger-click": "Εμφάνιση υπόδειξης με κλικ", + "tooltip-trigger-hover": "Εμφάνιση υπόδειξης με αιώρηση", + "auto-close-tooltips": "Αυτόματο κλείσιμο υποδείξεων", + "tooltip-offset": "Μετατόπιση υπόδειξης", + "tooltip-offset-horizontal": "Οριζόντια", + "tooltip-offset-vertical": "Κάθετη", + "tooltip-tag-actions": "Ενέργειες ετικετών", + "add-tooltip-tag-action": "Προσθήκη ενέργειας ετικέτας", + "edit-tooltip-tag-action": "Επεξεργασία ενέργειας ετικέτας", + "remove-tooltip-tag-action": "Αφαίρεση ενέργειας ετικέτας", + "action-add": "Προσθήκη", + "action-edit": "Επεξεργασία", + "action-move": "Μετακίνηση", + "action-remove": "Αφαίρεση", + "edit-instruments": "Εργαλεία", + "persist-location-attribute-scope": "Εύρος της ιδιότητας για αποθήκευση τοποθεσίας", + "enable-snapping": "Ενεργοποίηση στοίχισης για ακριβές σχέδιο", + "enable-snapping-hint": "Ευθυγραμμίζει αυτόματα τα νέα σημεία με υπάρχοντα σχήματα για ευκολότερη και ακριβέστερη σχεδίαση.", + "drag-drop-mode": "Λειτουργία μεταφοράς και απόθεσης", + "trip": { + "no-trips": "Δεν έχουν διαμορφωθεί διαδρομές", + "add-trip": "Προσθήκη διαδρομής", + "trip-configuration": "Διαμόρφωση διαδρομής", + "remove-trip": "Αφαίρεση διαδρομής" + }, + "marker": { + "marker": "Δείκτης", + "latitude-key": "Κλειδί γεωγραφικού πλάτους", + "longitude-key": "Κλειδί γεωγραφικού μήκους", + "x-pos-key": "Κλειδί θέσης X", + "y-pos-key": "Κλειδί θέσης Y", + "latitude-key-required": "Απαιτείται κλειδί γεωγραφικού πλάτους", + "longitude-key-required": "Απαιτείται κλειδί γεωγραφικού μήκους", + "x-pos-key-required": "Απαιτείται κλειδί θέσης X", + "y-pos-key-required": "Απαιτείται κλειδί θέσης Y", + "no-markers": "Δεν έχουν διαμορφωθεί δείκτες", + "add-marker": "Προσθήκη δείκτη", + "marker-configuration": "Διαμόρφωση δείκτη", + "remove-marker": "Αφαίρεση δείκτη", + "marker-type": "Τύπος δείκτη", + "marker-type-shape": "Σχήμα", + "marker-type-icon": "Εικονίδιο", + "marker-type-image": "Εικόνα", + "shape": "Σχήμα", + "icon": "Εικονίδιο", + "image": "Εικόνα", + "marker-shapes": "Σχήματα δείκτη", + "marker-icon": "Εικονίδιο δείκτη", + "marker-appearance": "Εμφάνιση δείκτη", + "marker-image": "Εικόνα δείκτη", + "marker-image-type-image": "Εικόνα", + "marker-image-type-function": "Συνάρτηση", + "custom-marker-image-size": "Προσαρμοσμένο μέγεθος εικόνας δείκτη", + "marker-image-function": "Συνάρτηση εικόνας δείκτη", + "marker-images": "Εικόνες δείκτη", + "marker-offset": "Μετατόπιση δείκτη", + "offset-horizontal": "Οριζόντια", + "offset-vertical": "Κάθετη", + "rotate-marker": "Περιστροφή δείκτη", + "offset-angle": "Γωνία μετατόπισης", + "position-conversion": "Μετατροπή θέσης", + "position-conversion-function": "Συνάρτηση μετατροπής θέσης, πρέπει να επιστρέφει x,y συντεταγμένες από 0 έως 1 ως δεκαδικούς", + "clustering": { + "use-map-markers-clustering": "Χρήση ομαδοποίησης δεικτών", + "zoom-on-cluster-click": "Εστίαση με κλικ σε ομάδα", + "max-zoom": "Μέγιστο επίπεδο εστίασης για ένταξη σε ομάδα (0 - 18)", + "max-radius": "Μέγιστη ακτίνα κάλυψης ομάδας", + "zoom-animation": "Κινούμενη εικόνα κατά την εστίαση", + "bounds-on-cluster-mouse-over": "Όρια δεικτών όταν αιωρείται ο δείκτης πάνω σε ομάδα", + "spiderfy-max-zoom-level": "Εξάπλωση ομάδας στο μέγιστο επίπεδο εστίασης (για εμφάνιση όλων των δεικτών)", + "load-optimization": "Βελτιστοποίηση φόρτωσης", + "chunked-load": "Χρήση τμηματικής φόρτωσης δεικτών για αποφυγή παγώματος", + "lazy-load": "Χρήση τεμπέλικης φόρτωσης για προσθήκη δεικτών", + "use-cluster-marker-color-function": "Χρήση συνάρτησης χρώματος για δείκτες ομάδας", + "marker-color-function": "Συνάρτηση χρώματος δείκτη" + }, + "edit": "Επεξεργασία δείκτη", + "remove-marker-for": "Αφαίρεση δείκτη για '{{entityName}}'", + "place-marker": "Τοποθέτηση δείκτη", + "place-marker-hint": "Κάντε κλικ για τοποθέτηση δείκτη", + "place-marker-hint-with-entity": "Κάντε κλικ για τοποθέτηση οντότητας '{{entityName}}'" + }, + "path": { + "path": "Διαδρομή", + "path-decorator": "Διακοσμητής διαδρομής", + "decorator-symbol": "Σύμβολο διακοσμητή", + "decorator-symbol-arrow-head": "Βέλος", + "decorator-symbol-dash": "Παύλα", + "decorator-arrangement": "Διάταξη διακοσμητή", + "decorator-offset": "Αρχή", + "decorator-end-offset": "Τέλος", + "decorator-repeat": "Επανάληψη" + }, + "points": { + "points": "Σημεία", + "point-tooltip": "Υπόδειξη σημείου" + }, + "shape": { + "fill": "Γέμισμα", + "fill-type-color": "Χρώμα", + "fill-type-stripe": "Ρίγα", + "fill-type-image": "Εικόνα", + "color": "Χρώμα", + "stripe": "Ρίγα", + "image": "Εικόνα", + "stroke": "Περίγραμμα", + "fill-image": "Εικόνα γεμίσματος", + "fill-image-type-image": "Εικόνα", + "fill-image-type-function": "Συνάρτηση", + "preserve-aspect-ratio": "Διατήρηση αναλογίας", + "opacity": "Διαφάνεια", + "angle": "Γωνία περιστροφής", + "scale": "Κλίμακα", + "fill-image-function": "Συνάρτηση γεμίσματος σχήματος", + "fill-images": "Εικόνες γεμίσματος σχήματος", + "stripe-pattern": "Μοτίβο ρίγας", + "first-stripe": "Πρώτη ρίγα", + "second-stripe": "Δεύτερη ρίγα" + }, + "polygon": { + "polygon-key": "Κλειδί πολυγώνου", + "polygon-key-required": "Απαιτείται κλειδί πολυγώνου", + "no-polygons": "Δεν έχουν διαμορφωθεί πολύγωνα", + "add-polygon": "Προσθήκη πολυγώνου", + "polygon-configuration": "Διαμόρφωση πολυγώνου", + "remove-polygon": "Αφαίρεση πολυγώνου", + "edit": "Επεξεργασία πολυγώνου", + "remove-polygon-for": "Αφαίρεση πολυγώνου για '{{entityName}}'", + "cut": "Αποκοπή περιοχής πολυγώνου", + "rotate": "Περιστροφή πολυγώνου", + "draw-rectangle": "Σχεδίαση ορθογωνίου", + "draw-polygon": "Σχεδίαση πολυγώνου", + "polygon-place-first-point-cut-hint": "Κάντε κλικ για τοποθέτηση του πρώτου σημείου", + "continue-polygon-cut-hint": "Κάντε κλικ για να συνεχίσετε τη σχεδίαση", + "finish-polygon-cut-hint": "Κάντε κλικ στον πρώτο δείκτη για να ολοκληρώσετε και να αποθηκεύσετε", + "polygon-place-first-point-hint": "Πολύγωνο: κάντε κλικ για τοποθέτηση πρώτου σημείου", + "polygon-place-first-point-hint-with-entity": "Πολύγωνο για '{{entityName}}': κάντε κλικ για τοποθέτηση πρώτου σημείου", + "continue-polygon-hint": "Πολύγωνο: κάντε κλικ για να συνεχίσετε τη σχεδίαση", + "continue-polygon-hint-with-entity": "Πολύγωνο για '{{entityName}}': κάντε κλικ για να συνεχίσετε τη σχεδίαση", + "finish-polygon-hint": "Πολύγωνο: κάντε κλικ στον πρώτο δείκτη για ολοκλήρωση", + "finish-polygon-hint-with-entity": "Πολύγωνο για '{{entityName}}': κάντε κλικ στον πρώτο δείκτη για ολοκλήρωση και αποθήκευση", + "rectangle-place-first-point-hint": "Ορθογώνιο: κάντε κλικ για τοποθέτηση πρώτου σημείου", + "rectangle-place-first-point-hint-with-entity": "Ορθογώνιο για '{{entityName}}': κάντε κλικ για τοποθέτηση πρώτου σημείου", + "finish-rectangle-hint": "Ορθογώνιο: κάντε κλικ για ολοκλήρωση", + "finish-rectangle-hint-with-entity": "Ορθογώνιο για '{{entityName}}': κάντε κλικ για ολοκλήρωση και αποθήκευση" + }, + "circle": { + "circle-key": "Κλειδί κύκλου", + "circle-key-required": "Απαιτείται κλειδί κύκλου", + "no-circles": "Δεν έχουν διαμορφωθεί κύκλοι", + "add-circle": "Προσθήκη κύκλου", + "circle-configuration": "Διαμόρφωση κύκλου", + "remove-circle": "Αφαίρεση κύκλου", + "edit": "Επεξεργασία κύκλου", + "remove-circle-for": "Αφαίρεση κύκλου για '{{entityName}}'", + "draw-circle": "Σχεδίαση κύκλου", + "place-circle-center-hint-with-entity": "Κύκλος για '{{entityName}}': κάντε κλικ για τοποθέτηση κέντρου", + "place-circle-center-hint": "Κύκλος: κάντε κλικ για τοποθέτηση κέντρου", + "finish-circle-hint-with-entity": "Κύκλος για '{{entityName}}': κάντε κλικ για ολοκλήρωση και αποθήκευση", + "finish-circle-hint": "Κύκλος: κάντε κλικ για ολοκλήρωση σχεδίασης" + }, + "select-entity": "Επιλέξτε οντότητα", + "select-entity-hint": "Υπόδειξη: μετά την επιλογή κάντε κλικ στον χάρτη για ορισμό θέσης" + }, + "select-entity": "Επιλέξτε οντότητα", + "select-entity-hint": "Υπόδειξη: μετά την επιλογή κάντε κλικ στον χάρτη για ορισμό θέσης", + "tooltips": { + "placeMarker": "Κάντε κλικ για τοποθέτηση οντότητας '{{entityName}}'", + "firstVertex": "Πολύγωνο για '{{entityName}}': κάντε κλικ για τοποθέτηση πρώτου σημείου", + "firstVertex-cut": "Κάντε κλικ για τοποθέτηση πρώτου σημείου", + "continueLine": "Πολύγωνο για '{{entityName}}': κάντε κλικ για να συνεχίσετε τη σχεδίαση", + "continueLine-cut": "Κάντε κλικ για να συνεχίσετε τη σχεδίαση", + "finishLine": "Κάντε κλικ σε υπάρχον δείκτη για ολοκλήρωση", + "finishPoly": "Πολύγωνο για '{{entityName}}': κάντε κλικ στον πρώτο δείκτη για ολοκλήρωση και αποθήκευση", + "finishPoly-cut": "Κάντε κλικ στον πρώτο δείκτη για ολοκλήρωση και αποθήκευση", + "finishRect": "Πολύγωνο για '{{entityName}}': κάντε κλικ για ολοκλήρωση και αποθήκευση", + "startCircle": "Κύκλος για '{{entityName}}': κάντε κλικ για τοποθέτηση κέντρου", + "finishCircle": "Κύκλος για '{{entityName}}': κάντε κλικ για ολοκλήρωση κύκλου", + "placeCircleMarker": "Κάντε κλικ για τοποθέτηση δείκτη κύκλου" + }, + "actions": { + "finish": "Ολοκλήρωση", + "cancel": "Ακύρωση", + "removeLastVertex": "Αφαίρεση τελευταίου σημείου" + }, + "buttonTitles": { + "drawMarkerButton": "Τοποθέτηση οντότητας", + "drawPolyButton": "Δημιουργία πολυγώνου", + "drawLineButton": "Δημιουργία πολυγραμμής", + "drawCircleButton": "Δημιουργία κύκλου", + "drawRectButton": "Δημιουργία ορθογωνίου", + "editButton": "Λειτουργία επεξεργασίας", + "dragButton": "Λειτουργία μεταφοράς και απόθεσης", + "cutButton": "Αποκοπή περιοχής πολυγώνου", + "deleteButton": "Αφαίρεση", + "drawCircleMarkerButton": "Δημιουργία δείκτη κύκλου", + "rotateButton": "Περιστροφή πολυγώνου" + }, + "map-provider-settings": "Ρυθμίσεις παρόχου χάρτη", + "map-provider": "Πάροχος χάρτη", + "map-provider-google": "Χάρτες Google", + "map-provider-openstreet": "Χάρτες OpenStreet", + "map-provider-here": "Χάρτες HERE", + "map-provider-image": "Χάρτης εικόνας", + "map-provider-tencent": "Χάρτες Tencent", + "openstreet-provider": "Πάροχος χάρτη OpenStreet", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Προεπιλογή)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery": "Esri.WorldImagery", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Χρήση προσαρμοσμένου παρόχου", + "custom-provider-tile-url": "Προσαρμοσμένο URL πλακιδίων παρόχου", + "google-maps-api-key": "Κλειδί API Google Maps", + "default-map-type": "Προεπιλεγμένος τύπος χάρτη", + "google-map-type-roadmap": "Οδικός χάρτης", + "google-map-type-satelite": "Δορυφορικός", + "google-map-type-hybrid": "Υβριδικός", + "google-map-type-terrain": "Τοπογραφικός", + "map-layer": "Επίπεδο χάρτη", + "here-map-normal-day": "HERE.normalDay (Προεπιλογή)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "Διαπιστευτήρια", + "here-app-id": "HERE app id", + "here-app-code": "HERE app code", + "here-api-key": "HERE API key", + "here-use-new-version-api-3": "Χρήση API έκδοσης 3", + "tencent-maps-api-key": "Κλειδί API Tencent Maps", + "tencent-map-type-roadmap": "Οδικός χάρτης", + "tencent-map-type-satelite": "Δορυφορικός", + "tencent-map-type-hybrid": "Υβριδικός", + "image-map-background": "Φόντο χάρτη εικόνας", + "image-map-background-from-entity-attribute": "Λήψη φόντου χάρτη εικόνας από χαρακτηριστικό οντότητας", + "image-url-source-entity-alias": "Ψευδώνυμο οντότητας προέλευσης URL εικόνας", + "image-url-source-entity-attribute": "Χαρακτηριστικό οντότητας προέλευσης URL εικόνας", + "common-map-settings": "Κοινές ρυθμίσεις χάρτη", + "x-pos-key-name": "Όνομα κλειδιού θέσης X", + "y-pos-key-name": "Όνομα κλειδιού θέσης Y", + "latitude-key-name": "Όνομα κλειδιού γεωγραφικού πλάτους", + "longitude-key-name": "Όνομα κλειδιού γεωγραφικού μήκους", + "default-map-zoom-level": "Προεπιλεγμένο επίπεδο εστίασης χάρτη (0 - 20)", + "default-map-center-position": "Προεπιλεγμένη κεντρική θέση χάρτη (0,0)", + "disable-scroll-zooming": "Απενεργοποίηση εστίασης με κύλιση", + "disable-double-click-zooming": "Απενεργοποίηση εστίασης με διπλό κλικ", + "disable-zoom-control-buttons": "Απενεργοποίηση κουμπιών ελέγχου εστίασης", + "fit-map-bounds": "Προσαρμογή ορίων χάρτη ώστε να περιλαμβάνουν όλους τους δείκτες", + "use-default-map-center-position": "Χρήση προεπιλεγμένης κεντρικής θέσης χάρτη", + "entities-limit": "Όριο οντοτήτων προς φόρτωση", + "markers-settings": "Ρυθμίσεις δεικτών", + "marker-offset-x": "Οριζόντια μετατόπιση δείκτη σε σχέση με το πλάτος του δείκτη", + "marker-offset-y": "Κατακόρυφη μετατόπιση δείκτη σε σχέση με το ύψος του δείκτη", + "position-function": "Συνάρτηση μετατροπής θέσης, επιστρέφει συντεταγμένες x,y από 0 έως 1", + "draggable-marker": "Μετακινούμενος δείκτης", + "label": "Ετικέτα", + "show-label": "Εμφάνιση ετικέτας", + "use-label-function": "Χρήση συνάρτησης ετικέτας", + "label-pattern": "Ετικέτα (παραδείγματα: '${entityName}', '${entityName}: (Text ${keyName} units.)')", + "label-function": "Συνάρτηση ετικέτας", + "tooltip": "Υπόδειξη", + "show-tooltip": "Εμφάνιση υπόδειξης", + "show-tooltip-action": "Ενέργεια για εμφάνιση υπόδειξης", + "show-tooltip-action-click": "Εμφάνιση υπόδειξης με κλικ (Προεπιλογή)", + "show-tooltip-action-hover": "Εμφάνιση υπόδειξης με αιώρηση", + "auto-close-tooltips": "Αυτόματο κλείσιμο υποδείξεων", + "use-tooltip-function": "Χρήση συνάρτησης υπόδειξης", + "tooltip-pattern": "Υπόδειξη (π.χ. 'Text ${keyName} units.' ή Link text)", + "tooltip-function": "Συνάρτηση υπόδειξης", + "tooltip-offset-x": "Οριζόντια μετατόπιση υπόδειξης ως προς το άγκιστρο δείκτη", + "tooltip-offset-y": "Κατακόρυφη μετατόπιση υπόδειξης ως προς το άγκιστρο δείκτη", + "color": "Χρώμα", + "use-color-function": "Χρήση συνάρτησης χρώματος", + "color-function": "Συνάρτηση χρώματος", + "marker-image": "Εικόνα δείκτη", + "use-marker-image-function": "Χρήση συνάρτησης εικόνας δείκτη", + "custom-marker-image": "Προσαρμοσμένη εικόνα δείκτη", + "custom-marker-image-size": "Μέγεθος προσαρμοσμένης εικόνας δείκτη (px)", + "marker-image-function": "Συνάρτηση εικόνας δείκτη", + "marker-images": "Εικόνες δείκτη", + "polygon-settings": "Ρυθμίσεις πολυγώνου", + "show-polygon": "Εμφάνιση πολυγώνου", + "polygon-key-name": "Όνομα κλειδιού πολυγώνου", + "enable-polygon-edit": "Ενεργοποίηση επεξεργασίας πολυγώνου", + "polygon-label": "Ετικέτα πολυγώνου", + "show-polygon-label": "Εμφάνιση ετικέτας πολυγώνου", + "use-polygon-label-function": "Χρήση συνάρτησης ετικέτας πολυγώνου", + "polygon-label-pattern": "Ετικέτα πολυγώνου (παραδείγματα: '${entityName}', '${entityName}: (Text ${keyName} units.)')", + "polygon-label-function": "Συνάρτηση ετικέτας πολυγώνου", + "polygon-tooltip": "Υπόδειξη πολυγώνου", + "show-polygon-tooltip": "Εμφάνιση υπόδειξης πολυγώνου", + "auto-close-polygon-tooltips": "Αυτόματο κλείσιμο υποδείξεων πολυγώνου", + "use-polygon-tooltip-function": "Χρήση συνάρτησης υπόδειξης πολυγώνου", + "polygon-tooltip-pattern": "Υπόδειξη (π.χ. 'Text ${keyName} units.' ή Link text)", + "polygon-tooltip-function": "Συνάρτηση υπόδειξης πολυγώνου", + "polygon-color": "Χρώμα πολυγώνου", + "polygon-opacity": "Αδιαφάνεια πολυγώνου", + "use-polygon-color-function": "Χρήση συνάρτησης χρώματος πολυγώνου", + "polygon-color-function": "Συνάρτηση χρώματος πολυγώνου", + "polygon-stroke": "Περίγραμμα πολυγώνου", + "stroke-color": "Χρώμα περιγράμματος", + "stroke-opacity": "Αδιαφάνεια περιγράμματος", + "stroke-weight": "Πάχος περιγράμματος", + "use-polygon-stroke-color-function": "Χρήση συνάρτησης χρώματος περιγράμματος πολυγώνου", + "polygon-stroke-color-function": "Συνάρτηση χρώματος περιγράμματος πολυγώνου", + "circle-settings": "Ρυθμίσεις κύκλου", + "show-circle": "Εμφάνιση κύκλου", + "circle-key-name": "Όνομα κλειδιού κύκλου", + "enable-circle-edit": "Ενεργοποίηση επεξεργασίας κύκλου", + "circle-label": "Ετικέτα κύκλου", + "show-circle-label": "Εμφάνιση ετικέτας κύκλου", + "use-circle-label-function": "Χρήση συνάρτησης ετικέτας κύκλου", + "circle-label-pattern": "Ετικέτα κύκλου (παραδείγματα: '${entityName}', '${entityName}: (Text ${keyName} units.)')", + "circle-label-function": "Συνάρτηση ετικέτας κύκλου", + "circle-tooltip": "Υπόδειξη κύκλου", + "show-circle-tooltip": "Εμφάνιση υπόδειξης κύκλου", + "auto-close-circle-tooltips": "Αυτόματο κλείσιμο υποδείξεων κύκλου", + "use-circle-tooltip-function": "Χρήση συνάρτησης υπόδειξης κύκλου", + "circle-tooltip-pattern": "Υπόδειξη (π.χ. 'Text ${keyName} units.' ή Link text)", + "circle-tooltip-function": "Συνάρτηση υπόδειξης κύκλου", + "circle-fill-color": "Χρώμα γεμίσματος κύκλου", + "circle-fill-color-opacity": "Αδιαφάνεια χρώματος γεμίσματος κύκλου", + "use-circle-fill-color-function": "Χρήση συνάρτησης γεμίσματος χρώματος κύκλου", + "circle-fill-color-function": "Συνάρτηση γεμίσματος χρώματος κύκλου", + "circle-stroke": "Περίγραμμα κύκλου", + "use-circle-stroke-color-function": "Χρήση συνάρτησης χρώματος περιγράμματος κύκλου", + "circle-stroke-color-function": "Συνάρτηση χρώματος περιγράμματος κύκλου", + "markers-clustering-settings": "Ρυθμίσεις ομαδοποίησης δεικτών", + "use-map-markers-clustering": "Χρήση ομαδοποίησης δεικτών στον χάρτη", + "zoom-on-cluster-click": "Εστίαση κατά το κλικ σε ομάδα", + "max-cluster-zoom": "Μέγιστο επίπεδο εστίασης για ένταξη δείκτη σε ομάδα (0 - 18)", + "max-cluster-radius-pixels": "Μέγιστη ακτίνα που θα καλύπτει μια ομάδα (σε pixels)", + "cluster-zoom-animation": "Εμφάνιση κινούμενης μετάβασης δεικτών κατά την εστίαση", + "show-markers-bounds-on-cluster-mouse-over": "Εμφάνιση ορίων δεικτών κατά την αιώρηση πάνω από ομάδα", + "spiderfy-max-zoom-level": "Εμφάνιση όλων των δεικτών ομάδας στο μέγιστο επίπεδο εστίασης", + "load-optimization": "Βελτιστοποίηση φόρτωσης", + "cluster-chunked-loading": "Χρήση τμηματικής φόρτωσης για αποφυγή παγώματος σελίδας", + "cluster-markers-lazy-load": "Χρήση lazy load για προσθήκη δεικτών", + "editor-settings": "Ρυθμίσεις επεξεργαστή", + "enable-snapping": "Ενεργοποίηση ευθυγράμμισης για ακριβή σχεδίαση", + "init-draggable-mode": "Εκκίνηση χάρτη σε λειτουργία μεταφοράς", + "hide-all-edit-buttons": "Απόκρυψη όλων των κουμπιών επεξεργασίας", + "hide-draw-buttons": "Απόκρυψη κουμπιών σχεδίασης", + "hide-edit-buttons": "Απόκρυψη κουμπιών επεξεργασίας", + "hide-remove-button": "Απόκρυψη κουμπιού αφαίρεσης", + "route-map-settings": "Ρυθμίσεις χάρτη διαδρομής", + "trip-animation-settings": "Ρυθμίσεις κινούμενης διαδρομής", + "normalization-step": "Βήμα κανονικοποίησης δεδομένων (ms)", + "tooltip-background-color": "Χρώμα φόντου υπόδειξης", + "tooltip-font-color": "Χρώμα γραμματοσειράς υπόδειξης", + "tooltip-opacity": "Αδιαφάνεια υπόδειξης (0-1)", + "auto-close-tooltip": "Αυτόματο κλείσιμο υπόδειξης", + "rotation-angle": "Ορισμός επιπλέον γωνίας περιστροφής για δείκτη (μοίρες)", + "path-settings": "Ρυθμίσεις διαδρομής", + "path-color": "Χρώμα διαδρομής", + "use-path-color-function": "Χρήση συνάρτησης χρώματος διαδρομής", + "path-color-function": "Συνάρτηση χρώματος διαδρομής", + "path-decorator": "Διακοσμητικό διαδρομής", + "use-path-decorator": "Χρήση διακοσμητικού διαδρομής", + "decorator-symbol": "Σύμβολο διακοσμητικού", + "decorator-symbol-arrow-head": "Βέλος", + "decorator-symbol-dash": "Παύλα", + "decorator-symbol-size": "Μέγεθος συμβόλου διακοσμητικού (px)", + "use-path-decorator-custom-color": "Χρήση προσαρμοσμένου χρώματος διακοσμητικού", + "decorator-custom-color": "Προσαρμοσμένο χρώμα διακοσμητικού", + "decorator-offset": "Μετατόπιση διακοσμητικού", + "end-decorator-offset": "Τελική μετατόπιση διακοσμητικού", + "decorator-repeat": "Επανάληψη διακοσμητικού", + "points-settings": "Ρυθμίσεις σημείων", + "show-points": "Εμφάνιση σημείων", + "point-color": "Χρώμα σημείου", + "point-size": "Μέγεθος σημείου (px)", + "use-point-color-function": "Χρήση συνάρτησης χρώματος σημείου", + "point-color-function": "Συνάρτηση χρώματος σημείου", + "use-point-as-anchor": "Χρήση σημείου ως άγκυρας", + "point-as-anchor-function": "Συνάρτηση σημείου ως άγκυρας", + "independent-point-tooltip": "Αυτόνομη υπόδειξη σημείου", + "clustering-markers": "Ομαδοποίηση δεικτών", + "use-icon-create-function": "Χρήση συνάρτησης χρώματος δεικτών", + "marker-color-function": "Συνάρτηση χρώματος δείκτη" + }, + "markdown": { + "use-markdown-text-function": "Χρήση συνάρτησης τιμής markdown/HTML", + "markdown-text-function": "Συνάρτηση τιμής markdown/HTML", + "markdown-text-pattern": "Πρότυπο markdown/HTML (markdown ή HTML με μεταβλητές, π.χ. '${entityName} ή ${keyName} - κάποιο κείμενο.')", + "apply-default-markdown-style": "Εφαρμογή προεπιλεγμένου στυλ markdown", + "markdown-css": "CSS για markdown/HTML" + }, + "simple-card": { + "label": "Ετικέτα", + "label-position": "Θέση ετικέτας", + "label-position-left": "Αριστερά", + "label-position-top": "Πάνω" + }, + "single-switch": { + "behavior": "Συμπεριφορά", + "layout": "Διάταξη", + "layout-right": "Δεξιά", + "layout-left": "Αριστερά", + "layout-centered": "Κεντραρισμένο", + "auto-scale": "Αυτόματη κλιμάκωση", + "label": "Ετικέτα", + "icon": "Εικονίδιο", + "switch-color": "Χρώμα διακόπτη", + "on": "Ενεργό", + "off": "Ανενεργό", + "disabled": "Απενεργοποιημένο", + "tumbler-color": "Χρώμα εναλλασσόμενου διακόπτη", + "on-label": "Ετικέτα ενεργοποίησης", + "off-label": "Ετικέτα απενεργοποίησης", + "switch": "Διακόπτης" + }, + "slider": { + "behavior": "Συμπεριφορά", + "initial-value": "Αρχική τιμή", + "initial-value-hint": "Ενέργεια για λήψη της αρχικής τιμής του ρυθμιστικού.", + "on-value-change": "Κατά την αλλαγή τιμής", + "on-value-change-hint": "Ενέργεια που ενεργοποιείται όταν αλλάζει η τιμή του ρυθμιστικού.", + "layout": "Διάταξη", + "layout-default": "Προεπιλεγμένη", + "layout-extended": "Εκτεταμένη", + "layout-simplified": "Απλοποιημένη", + "auto-scale": "Αυτόματη κλιμάκωση", + "icon": "Εικονίδιο", + "value": "Τιμή", + "range": "Εύρος", + "min": "ελάχιστο", + "max": "μέγιστο", + "range-ticks": "Ενδείξεις εύρους", + "tick-marks": "Σημάδια ένδειξης", + "colors": "Χρώματα", + "main": "Κύριο", + "background": "Φόντο", + "left-icon": "Αριστερό εικονίδιο", + "right-icon": "Δεξί εικονίδιο", + "slider": "Ρυθμιστικό" + }, + "value-card": { + "layout": "Διάταξη", + "layout-square": "Τετράγωνο", + "layout-vertical": "Κατακόρυφο", + "layout-centered": "Κεντραρισμένο", + "layout-simplified": "Απλοποιημένο", + "layout-horizontal": "Οριζόντιο", + "layout-horizontal-reversed": "Αντίστροφα οριζόντιο", + "label": "Ετικέτα", + "icon": "Εικονίδιο", + "value": "Τιμή", + "date": "Ημερομηνία", + "value-card-style": "Στυλ κάρτας τιμής", + "auto-scale": "Αυτόματη κλιμάκωση" + }, + "label-card": { + "auto-scale": "Αυτόματη κλιμάκωση", + "label": "Ετικέτα", + "icon": "Εικονίδιο", + "label-card-style": "Στυλ κάρτας ετικέτας" + }, + "label-value-card": { + "value": "Τιμή", + "label-value-card-style": "Στυλ κάρτας ετικέτας και τιμής" + }, + "liquid-level-card": { + "layout-simple": "Απλό", + "layout-percentage": "Ποσοστό", + "layout-absolute": "Απόλυτο", + "layout": "Διάταξη", + "background-overlay": "Επικάλυψη φόντου τιμής", + "total-volume": "Συνολικός όγκος", + "total-volume-units": "Μονάδες συνολικού όγκου", + "tank": "Δοχείο", + "shape": "Σχήμα", + "datasource-units": "Μονάδες πηγής", + "widget-units": "Μονάδες γραφικού στοιχείου", + "decimals": "Δεκαδικά", + "liquid": "Υγρό", + "liquid-color": "Χρώμα υγρού", + "value": "Τιμή", + "value-font": "Γραμματοσειρά τιμής", + "level": "Επίπεδο", + "last-update": "Τελευταία ενημέρωση", + "shape-by-attribute": "Ορισμός σχήματος δοχείου με βάση το όνομα γνωρίσματος", + "tooltip-background": "Χρώμα φόντου", + "background-blur": "Θόλωση φόντου", + "tank-color": "Χρώμα δοχείου", + "static": "Στατικό", + "see-examples": "Δείτε παραδείγματα", + "attribute": "Γνώρισμα", + "shape-type": "Τύπος", + "v-oval": "Κατακόρυφο οβάλ", + "v-cylinder": "Κατακόρυφος κύλινδρος", + "v-capsule": "Κατακόρυφη κάψουλα", + "rectangle": "Ορθογώνιο", + "h-oval": "Οριζόντιο οβάλ", + "h-ellipse": "Οριζόντια έλλειψη", + "h-dish-ends": "Οριζόντιο με λοξές άκρες", + "h-cylinder": "Οριζόντιος κύλινδρος", + "h-capsule": "Οριζόντια κάψουλα", + "h-elliptical_2_1": "Οριζόντια 2:1 έλλειψη", + "icon": "Εικονίδιο κάρτας", + "title": "Τίτλος κάρτας", + "units": "Μονάδες", + "color-and-font": "Χρώμα και γραμματοσειρά", + "shape-attribute-name": "Όνομα γνωρίσματος", + "total-volume-required": "Απαιτείται συνολικός όγκος.", + "attribute-name-required": "Απαιτείται όνομα γνωρίσματος.", + "attribute-key-not-set": "Το κλειδί του γνωρίσματος '{{attributeName}}' δεν έχει οριστεί", + "attribute-key-invalid": "Το κλειδί του γνωρίσματος '{{attributeName}}' δεν είναι έγκυρο" + }, + "aggregated-value-card": { + "subtitle": "Υπότιτλος", + "chart": "Διάγραμμα", + "values": "Τιμές", + "value-appearance": "Εμφάνιση τιμής", + "position": "Θέση", + "position-center": "Κέντρο", + "position-right-top": "Δεξιά επάνω", + "position-right-bottom": "Δεξιά κάτω", + "position-left-top": "Αριστερά επάνω", + "position-left-bottom": "Αριστερά κάτω", + "font": "Γραμματοσειρά", + "color": "Χρώμα", + "arrow": "Βέλος", + "display-up-down-arrow": "Εμφάνιση βέλους ανόδου/καθόδου", + "add-value": "Προσθήκη τιμής", + "remove-value": "Αφαίρεση τιμής", + "no-values": "Δεν έχουν διαμορφωθεί τιμές", + "aggregation": "Συσσώρευση", + "aggregated-value-card-style": "Στυλ κάρτας συσσωρευμένης τιμής", + "auto-scale": "Αυτόματη κλιμάκωση" + }, + "value-chart-card": { + "layout": "Διάταξη", + "layout-left": "Αριστερά", + "layout-right": "Δεξιά", + "auto-scale": "Αυτόματη κλιμάκωση", + "icon": "Εικονίδιο", + "value": "Τιμή", + "chart": "Διάγραμμα", + "value-chart-card-style": "Στυλ κάρτας τιμής με διάγραμμα" + }, + "progress-bar": { + "layout": "Διάταξη", + "layout-default": "Προεπιλογή", + "layout-simplified": "Απλοποιημένο", + "auto-scale": "Αυτόματη κλιμάκωση", + "icon": "Εικονίδιο", + "value": "Τιμή", + "range": "Εύρος", + "min": "ελάχιστο", + "max": "μέγιστο", + "range-ticks": "Διαχωριστικά εύρους", + "bar": "Μπάρα", + "bar-color": "Χρώμα μπάρας", + "bar-background": "Φόντο μπάρας", + "progress-bar-card-style": "Στυλ κάρτας γραμμής προόδου" + }, + "notification": { + "max-notification-display": "Μέγιστος αριθμός ειδοποιήσεων για εμφάνιση", + "counter": "Μετρητής", + "counter-hint": "Ο μετρητής θα εμφανίζεται εάν είναι ενεργοποιημένος ο \"Τίτλος γραφικού στοιχείου\"", + "icon": "Εικονίδιο", + "counter-value": "Τιμή", + "counter-color": "Χρώμα", + "notification-button": "Κουμπιά ειδοποίησης", + "button-view-all": "Προβολή όλων", + "button-filter": "Φίλτρο", + "type-filter": "Φίλτρο τύπου", + "button-mark-read": "Σήμανση όλων ως αναγνωσμένων", + "notification-types": "Τύποι ειδοποιήσεων", + "notification-type": "Τύπος ειδοποίησης", + "search-type": "Τύπος αναζήτησης", + "any-type": "Οποιοσδήποτε τύπος" + }, + "alarm-count": { + "alarm-count-card-style": "Στυλ κάρτας πλήθους συναγερμών" + }, + "entity-count": { + "entity-count-card-style": "Στυλ κάρτας πλήθους οντοτήτων" + }, + "count": { + "layout": "Διάταξη", + "layout-column": "Στήλη", + "layout-row": "Γραμμή", + "label": "Ετικέτα", + "icon": "Εικονίδιο", + "icon-background": "Φόντο εικονιδίου", + "value": "Τιμή", + "chevron": "Βέλος", + "auto-scale": "Αυτόματη κλιμάκωση" + }, + "table": { + "common-table-settings": "Κοινές ρυθμίσεις πίνακα", + "enable-search": "Ενεργοποίηση αναζήτησης", + "enable-sticky-header": "Πάντα εμφάνιση κεφαλίδας", + "enable-sticky-action": "Πάντα εμφάνιση στήλης ενεργειών", + "hidden-cell-button-display-mode": "Λειτουργία εμφάνισης κουμπιών σε κρυφά κελιά", + "show-empty-space-hidden-action": "Εμφάνιση κενού χώρου αντί κουμπιού σε κρυφό κελί", + "dont-reserve-space-hidden-action": "Χωρίς κράτηση χώρου για κρυφές ενέργειες", + "display-timestamp": "Χρονική σήμανση", + "display-pagination": "Εμφάνιση σελιδοποίησης", + "default-page-size": "Προεπιλεγμένο μέγεθος σελίδας", + "page-step-settings": "Ρυθμίσεις βήματος σελίδας", + "page-step-count": "Αριθμός βημάτων", + "page-step-increment": "Αύξηση βήματος", + "page-step-count-format-message": "Πρέπει να είναι ακέραιος αριθμός από 1 έως 100.", + "page-step-increment-format-message": "Πρέπει να είναι ακέραιος αριθμός, τουλάχιστον 1.", + "use-entity-label-tab-name": "Χρήση ετικέτας οντότητας στην καρτέλα", + "hide-empty-lines": "Απόκρυψη κενών γραμμών", + "row-style": "Στυλ γραμμής", + "use-row-style-function": "Χρήση συνάρτησης στυλ γραμμής", + "row-style-function": "Συνάρτηση στυλ γραμμής", + "cell-style": "Στυλ κελιού", + "use-cell-style-function": "Χρήση συνάρτησης στυλ κελιού", + "cell-style-function": "Συνάρτηση στυλ κελιού", + "cell-content": "Περιεχόμενο κελιού", + "use-cell-content-function": "Χρήση συνάρτησης περιεχομένου κελιού", + "cell-content-function": "Συνάρτηση περιεχομένου κελιού", + "show-latest-data-column": "Εμφάνιση στήλης τελευταίων δεδομένων", + "latest-data-column-order": "Σειρά στήλης τελευταίων δεδομένων", + "entities-table-title": "Τίτλος πίνακα οντοτήτων", + "enable-select-column-display": "Ενεργοποίηση επιλογής στηλών για εμφάνιση", + "display-entity-name": "Εμφάνιση στήλης ονόματος οντότητας", + "entity-name-column-title": "Τίτλος στήλης ονόματος οντότητας", + "display-entity-label": "Εμφάνιση στήλης ετικέτας οντότητας", + "entity-label-column-title": "Τίτλος στήλης ετικέτας οντότητας", + "display-entity-type": "Εμφάνιση στήλης τύπου οντότητας", + "default-sort-order": "Προεπιλεγμένη σειρά ταξινόμησης", + "custom-title": "Προσαρμοσμένος τίτλος κεφαλίδας", + "column-width": "Πλάτος στήλης (px ή %)", + "default-column-visibility": "Προεπιλεγμένη ορατότητα στήλης", + "column-visibility-visible": "Ορατή", + "column-visibility-hidden": "Κρυφή", + "column-visibility-hidden-mobile": "Κρυφή σε κινητά", + "column-selection-to-display": "Επιλογή στηλών για εμφάνιση", + "column-selection-to-display-enabled": "Ενεργοποιημένο", + "column-selection-to-display-disabled": "Απενεργοποιημένο", + "alarms-table-title": "Τίτλος πίνακα συναγερμών", + "enable-alarms-selection": "Ενεργοποίηση επιλογής συναγερμών", + "enable-alarms-search": "Ενεργοποίηση αναζήτησης συναγερμών", + "enable-alarm-filter": "Ενεργοποίηση φίλτρου συναγερμών", + "display-alarm-details": "Εμφάνιση λεπτομερειών συναγερμού", + "allow-alarms-ack": "Να επιτρέπεται επιβεβαίωση συναγερμών", + "allow-alarms-clear": "Να επιτρέπεται εκκαθάριση συναγερμών", + "display-alarm-activity": "Εμφάνιση δραστηριότητας συναγερμών", + "allow-alarms-assign": "Να επιτρέπεται ανάθεση συναγερμών", + "columns": "Στήλες", + "column-settings": "Ρυθμίσεις στηλών", + "remove-column": "Αφαίρεση στήλης", + "add-column": "Προσθήκη στήλης", + "no-columns": "Καμία στήλη δεν έχει ρυθμιστεί", + "columns-to-display": "Στήλες προς εμφάνιση", + "table-header": "Κεφαλίδα πίνακα", + "header-buttons": "Κουμπιά κεφαλίδας", + "table-buttons": "Κουμπιά πίνακα", + "pagination": "Σελιδοποίηση", + "rows": "Γραμμές", + "timeseries-column-error": "Πρέπει να καθοριστεί τουλάχιστον μία στήλη χρονικών σειρών", + "alarm-column-error": "Πρέπει να καθοριστεί τουλάχιστον μία στήλη συναγερμών", + "table-tabs": "Καρτέλες πίνακα", + "show-cell-actions-menu-mobile": "Εμφάνιση μενού ενεργειών κελιού σε κινητά", + "disable-sorting": "Απενεργοποίηση ταξινόμησης" + }, + "latest-chart": { + "total": "Σύνολο", + "auto-scale": "Αυτόματη κλιμάκωση", + "clockwise-layout": "Διάταξη δεξιόστροφα", + "sort-series": "Ταξινόμηση σειρών κατά ετικέτα", + "tooltip-value-type-absolute": "Απόλυτο", + "tooltip-value-type-percentage": "Ποσοστό" + }, + "pie-chart": { + "pie-chart-appearance": "Εμφάνιση διαγράμματος πίτας", + "label": "Ετικέτα", + "border": "Περίγραμμα", + "radius": "Ακτίνα", + "pie-chart-card-style": "Στυλ κάρτας πίτας" + }, + "radar-chart": { + "radar-appearance": "Εμφάνιση ραντάρ", + "shape": "Σχήμα", + "shape-polygon": "Πολύγωνο", + "shape-circle": "Κύκλος", + "color": "Χρώμα", + "line": "Γραμμή", + "points": "Σημεία", + "points-label": "Ετικέτα σημείων", + "radar-axis": "Άξονας ραντάρ", + "axis-label": "Ετικέτα άξονα", + "ticks-label": "Ετικέτα διαβαθμίσεων", + "radar-chart-style": "Στυλ διαγράμματος ραντάρ" + }, + "time-series-chart": { + "chart": "Διάγραμμα", + "chart-style": "Στυλ διαγράμματος", + "data-zoom": "Εστίαση δεδομένων", + "stack-mode": "Λειτουργία στοίβαξης", + "stack-mode-hint": "Οι σειρές με την ίδια μονάδα στοιβάζονται μεταξύ τους στο διάγραμμα.", + "axes": "Άξονες", + "y-axes": "Άξονες Υ", + "line-type": "Τύπος γραμμής", + "line-width": "Πλάτος γραμμής", + "type-line": "Γραμμή", + "type-bar": "Ράβδος", + "type-point": "Σημείο", + "no-aggregation-bar-width-strategy": "Στρατηγική πλάτους ράβδου για μη συγκεντρωμένα δεδομένα", + "no-aggregation-bar-width-strategy-group": "Ομαδοποίηση", + "no-aggregation-bar-width-strategy-separate": "Διαχωρισμός", + "bar-group-width": "Πλάτος ομάδας ράβδων", + "bar-width": "Πλάτος ράβδου", + "bar-width-relative": "Ποσοστό παραθύρου χρόνου", + "bar-width-absolute": "Απόλυτο (ms)", + "comparison": { + "comparison": "Σύγκριση", + "comparison-hint": "Η σύγκριση λειτουργεί μόνο με ιστορικά δεδομένα!", + "show": "Εμφάνιση", + "settings": "Ρυθμίσεις σύγκρισης", + "show-values-for-comparison": "Εμφάνιση ιστορικών δεδομένων για σύγκριση", + "comparison-values-label": "Ετικέτα κλειδιού σύγκρισης", + "comparison-values-label-auto": "Αυτόματο", + "comparison-data-color": "Χρώμα δεδομένων σύγκρισης" + }, + "threshold": { + "thresholds": "Όρια", + "source": "Πηγή", + "key-value": "Κλειδί / Τιμή", + "no-thresholds": "Δεν έχουν ρυθμιστεί όρια", + "add-threshold": "Προσθήκη ορίου", + "type-constant": "Σταθερό", + "type-latest-key": "Κλειδί", + "type-entity": "Οντότητα", + "threshold-settings": "Ρυθμίσεις ορίου", + "remove-threshold": "Αφαίρεση ορίου", + "threshold-value-required": "Απαιτείται τιμή ορίου.", + "key-required": "Απαιτείται κλειδί.", + "entity-key-required": "Απαιτείται κλειδί οντότητας.", + "line-appearance": "Εμφάνιση γραμμής", + "line-color": "Χρώμα γραμμής", + "start-symbol": "Σύμβολο αρχής", + "end-symbol": "Σύμβολο τέλους", + "symbol-size": "μέγεθος", + "label": "Ετικέτα", + "label-position-start": "Αρχή", + "label-position-middle": "Μέση", + "label-position-end": "Τέλος", + "label-position-inside-start": "Εσωτερικά στην αρχή", + "label-position-inside-start-top": "Εσωτερικά στην αρχή - πάνω", + "label-position-inside-start-bottom": "Εσωτερικά στην αρχή - κάτω", + "label-position-inside-middle": "Εσωτερικά στο μέσο", + "label-position-inside-middle-top": "Εσωτερικά στο μέσο - πάνω", + "label-position-inside-middle-bottom": "Εσωτερικά στο μέσο - κάτω", + "label-position-inside-end": "Εσωτερικά στο τέλος", + "label-position-inside-end-top": "Εσωτερικά στο τέλος - πάνω", + "label-position-inside-end-bottom": "Εσωτερικά στο τέλος - κάτω", + "label-background": "Φόντο ετικέτας" + }, + "state": { + "states": "Καταστάσεις", + "label": "Ετικέτα", + "ticks-value": "Τιμή διαβαθμίσεων", + "source": "Πηγή", + "value-range": "Τιμή / Εύρος", + "no-states": "Δεν έχουν ρυθμιστεί καταστάσεις", + "add-state": "Προσθήκη κατάστασης", + "type-constant": "Σταθερό", + "type-range": "Εύρος", + "from": "Από", + "to": "Έως", + "remove-state": "Αφαίρεση κατάστασης" + }, + "grid": { + "grid": "Πλέγμα", + "background-color": "Χρώμα φόντου", + "border": "Περίγραμμα" + }, + "axis": { + "axes": "Άξονες", + "x-axis": "Άξονας Χ", + "y-axis": "Άξονας Υ", + "y-axis-settings": "Ρυθμίσεις άξονα Υ", + "comparison-x-axis-settings": "Ρυθμίσεις άξονα Χ για σύγκριση", + "remove-y-axis": "Αφαίρεση άξονα Υ", + "id": "Αναγνωριστικό", + "label": "Ετικέτα", + "position": "Θέση", + "position-left": "Αριστερά", + "position-right": "Δεξιά", + "position-top": "Πάνω", + "position-bottom": "Κάτω", + "tick-labels": "Ετικέτες διαβαθμίσεων", + "ticks-formatter-function": "Συνάρτηση μορφοποίησης διαβαθμίσεων", + "ticks-generator-function": "Συνάρτηση δημιουργίας διαβαθμίσεων", + "show-ticks": "Εμφάνιση διαβαθμίσεων", + "show-line": "Εμφάνιση γραμμής", + "show-split-lines": "Εμφάνιση γραμμών διαχωρισμού", + "show-split-lines-x-axis-hint": "Αν ενεργοποιηθεί, θα εμφανίζονται κατακόρυφες γραμμές στο διάγραμμα.", + "show-split-lines-y-axis-hint": "Αν ενεργοποιηθεί, θα εμφανίζονται οριζόντιες γραμμές στο διάγραμμα.", + "ticks-interval": "Διάστημα διαβαθμίσεων", + "ticks-interval-hint": "Ορισμός διαστήματος διαχωρισμού για τον άξονα.", + "split-number": "Αριθμός διαχωρισμών", + "split-number-hint": "Αριθμός διαχωρισμών στον άξονα.", + "min": "Ελάχιστο", + "max": "Μέγιστο", + "show": "Εμφάνιση", + "add-y-axis": "Προσθήκη άξονα Υ" + }, + "series": { + "legend-settings": "Ρυθμίσεις υπόμνησης", + "show-in-legend": "Εμφάνιση στην υπόμνηση", + "show-in-legend-hint": "Εμφάνιση ονόματος και δεδομένων σειράς στην υπόμνηση.", + "hidden-by-default": "Απόκρυψη από προεπιλογή", + "hidden-by-default-hint": "Κάνει τη σειρά κρυφή στην υπόμνηση από προεπιλογή.", + "series-type": "Τύπος σειράς", + "type": "Τύπος", + "type-line": "Γραμμή", + "type-bar": "Ράβδος", + "line": { + "line": "Γραμμή", + "show-line": "Εμφάνιση γραμμής", + "step-line": "Βηματική γραμμή", + "step-type-start": "Αρχή", + "step-type-middle": "Μέση", + "step-type-end": "Τέλος", + "smooth-line": "Ομαλή γραμμή" + }, + "point": { + "points": "Σημεία", + "show-points": "Εμφάνιση σημείων", + "point-label": "Ετικέτα σημείου", + "point-label-hint": "Εμφάνιση ετικέτας με τιμή πάνω από το σημείο της σειράς.", + "point-label-background": "Φόντο ετικέτας σημείου", + "point-shape": "Σχήμα σημείου", + "point-size": "Μέγεθος σημείου" + } + } + }, + "wind-speed-direction": { + "layout": "Διάταξη", + "layout-default": "Προεπιλεγμένη", + "layout-advanced": "Προηγμένη", + "layout-simplified": "Απλοποιημένη", + "values": "Τιμές", + "wind-direction": "Κατεύθυνση ανέμου", + "center-value": "Κεντρική τιμή", + "icon": "Εικονίδιο", + "arrow": "Βέλος", + "ticks": "Διαβαθμίσεις", + "labels-type": "Τύπος ετικετών", + "directional-names": "Κατευθυντικά ονόματα", + "degrees": "Μοίρες", + "major-ticks": "Κύριες διαβαθμίσεις", + "minor-ticks": "Δευτερεύουσες διαβαθμίσεις", + "wind-speed-direction-card-style": "Στυλ κάρτας ταχύτητας και κατεύθυνσης ανέμου", + "ticks-color": "Χρώμα διαβαθμίσεων", + "ticks-labels-type": "Τύπος ετικετών διαβαθμίσεων", + "arrow-color": "Χρώμα βέλους" + }, + "value-source": { + "value-source": "Πηγή τιμής", + "predefined-value": "Σταθερή", + "entity-attribute": "Γνώρισμα οντότητας", + "value": "Τιμή", + "value-required": "Απαιτείται τιμή.", + "key-required": "Απαιτείται κλειδί.", + "entity-key-required": "Απαιτείται κλειδί οντότητας.", + "source-entity-alias": "Ψευδώνυμο πηγαίας οντότητας", + "source-entity-attribute": "Γνώρισμα πηγαίας οντότητας", + "type-constant": "Σταθερή", + "type-latest-key": "Κλειδί", + "type-entity": "Οντότητα" + }, + "rpc-state": { + "initial-state": "Αρχική κατάσταση", + "initial-state-hint": "Ενέργεια για λήψη της αρχικής κατάστασης (On/Off) του στοιχείου.", + "disabled-state": "Απενεργοποιημένη κατάσταση", + "disabled-state-hint": "Διαμόρφωση συνθήκης κατά την οποία το στοιχείο είναι απενεργοποιημένο.", + "turn-on": "Ενεργοποίηση", + "turn-on-hint": "Ενέργεια που ενεργοποιείται όταν ο διακόπτης τίθεται σε 'On'", + "turn-off": "Απενεργοποίηση", + "turn-off-hint": "Ενέργεια που ενεργοποιείται όταν ο διακόπτης τίθεται σε 'Off'", + "on": "Ενεργό", + "off": "Ανενεργό", + "disabled": "Απενεργοποιημένο" + }, + "value-action": { + "do-nothing": "Καμία ενέργεια", + "execute-rpc": "Εκτέλεση RPC", + "get-attribute": "Λήψη γνωρίσματος", + "set-attribute": "Ορισμός γνωρίσματος", + "get-time-series": "Λήψη χρονικής σειράς", + "get-alarm-status": "Λήψη κατάστασης συναγερμού", + "get-dashboard-state": "Λήψη ταυτότητας κατάστασης ταμπλό", + "get-dashboard-state-object": "Λήψη αντικειμένου κατάστασης ταμπλό", + "add-time-series": "Προσθήκη χρονικής σειράς", + "execute-rpc-text": "Εκτέλεση μεθόδου RPC '{{methodName}}'", + "get-time-series-text": "Χρήση χρονικής σειράς '{{key}}'", + "get-attribute-text": "Χρήση γνωρίσματος '{{key}}'", + "get-alarm-status-text": "Χρήση κατάστασης συναγερμού", + "get-dashboard-state-text": "Χρήση κατάστασης ταμπλό", + "get-dashboard-state-object-text": "Χρήση αντικειμένου κατάστασης ταμπλό", + "when-dashboard-state-is-text": "Όταν η ταυτότητα κατάστασης ταμπλό είναι '{{state}}'", + "when-dashboard-state-function-is-text": "Όταν f(ταυτότητα κατάστασης ταμπλό) είναι '{{state}}'", + "when-dashboard-state-object-function-is-text": "Όταν f(αντικείμενο κατάστασης ταμπλό) είναι '{{state}}'", + "set-attribute-to-value-text": "Ορισμός γνωρίσματος '{{key}}' σε: {{value}}", + "add-time-series-value-text": "Προσθήκη τιμής χρονικής σειράς '{{key}}': {{value}}", + "set-attribute-text": "Ορισμός γνωρίσματος '{{key}}'", + "add-time-series-text": "Προσθήκη χρονικής σειράς '{{key}}'", + "action": "Ενέργεια", + "value": "Τιμή", + "init-value-hint": "Τιμή που θα οριστεί έως ότου η συσκευή στείλει δεδομένα.", + "method": "Μέθοδος", + "method-name-required": "Απαιτείται όνομα μεθόδου.", + "request-timeout-ms": "Χρονικό όριο RPC (ms)", + "request-timeout-required": "Απαιτείται χρονικό όριο.", + "min-request-timeout-error": "Η τιμή χρονικού ορίου πρέπει να είναι τουλάχιστον 5000 ms (5 δευτερόλεπτα).", + "request-persistent": "Επίμονη RPC αίτηση", + "persistent-polling-interval": "Διάστημα ερωτήσεων για επίμονη RPC (ms)", + "persistent-polling-interval-hint": "Διάστημα (ms) για ερώτηση επίμονης RPC εντολής", + "persistent-polling-interval-required": "Απαιτείται διάστημα ερωτήσεων.", + "min-persistent-polling-interval-error": "Η τιμή διαστήματος ερωτήσεων πρέπει να είναι τουλάχιστον 1000 ms (1 δευτερόλεπτο).", + "attribute-scope": "Εμβέλεια γνωρίσματος", + "attribute-key": "Κλειδί γνωρίσματος", + "attribute-key-required": "Απαιτείται κλειδί γνωρίσματος.", + "time-series-key": "Κλειδί χρονικής σειράς", + "time-series-key-required": "Απαιτείται κλειδί χρονικής σειράς.", + "action-result-converter": "Μετατροπέας αποτελέσματος ενέργειας", + "converter-none": "Κανένας", + "converter-function": "Συνάρτηση", + "converter-constant": "Σταθερά", + "converter-value": "Τιμή", + "parse-value-function": "Συνάρτηση ανάλυσης τιμής", + "state-when-result-is": "'{{state}}' όταν το αποτέλεσμα είναι", + "parameters": "Παράμετροι", + "convert-value-function": "Συνάρτηση μετατροπής τιμής", + "error": { + "target-entity-is-not-set": "Η οντότητα στόχος δεν έχει οριστεί!", + "failed-to-perform-action": "Απέτυχε η εκτέλεση της ενέργειας {{ actionLabel }}.", + "invalid-attribute-scope": "Η εμβέλεια γνωρίσματος {{scope}} δεν υποστηρίζεται από την οντότητα {{entityType}}." + } + }, + "widget-font": { + "font-settings": "Ρυθμίσεις γραμματοσειράς", + "font-family": "Οικογένεια γραμματοσειράς", + "size": "Μέγεθος", + "relative-font-size": "Σχετικό μέγεθος γραμματοσειράς (%)", + "font-style": "Στυλ", + "font-style-normal": "Κανονικό", + "font-style-italic": "Πλάγιο", + "font-style-oblique": "Λοξό", + "font-weight": "Βάρος", + "font-weight-normal": "Κανονικό", + "font-weight-bold": "Έντονο", + "font-weight-bolder": "Εντονότερο", + "font-weight-lighter": "Ελαφρύτερο", + "color": "Χρώμα", + "shadow-color": "Χρώμα σκιάς", + "preview": "Προεπισκόπηση", + "line-height": "Ύψος γραμμής", + "auto": "Αυτόματο" + }, + "home": { + "no-data-available": "Δεν υπάρχουν διαθέσιμα δεδομένα" + }, + "system-info": { + "cpu": "Επεξεργαστής", + "ram": "RAM", + "disk": "Δίσκος", + "cpu-warning-text": "Υψηλή χρήση επεξεργαστή. Βελτιστοποιήστε την απόδοση του συστήματος για να αποφύγετε αποτυχία.", + "cpu-critical-text": "Κρίσιμα υψηλή χρήση επεξεργαστή. Βελτιστοποιήστε την απόδοση του συστήματος για να αποφύγετε αποτυχία.", + "ram-warning-text": "Χαμηλή διαθεσιμότητα RAM. Βελτιστοποιήστε ή αυξήστε τη RAM για να αποφύγετε αποτυχία.", + "ram-critical-text": "Κρίσιμα χαμηλή RAM. Βελτιστοποιήστε ή αυξήστε τη RAM για να αποφύγετε αποτυχία.", + "disk-warning-text": "Χαμηλός διαθέσιμος χώρος δίσκου. Απελευθερώστε ή αυξήστε τον αποθηκευτικό χώρο.", + "disk-critical-text": "Κρίσιμα χαμηλός χώρος δίσκου. Απελευθερώστε ή αυξήστε τον αποθηκευτικό χώρο." + }, + "cluster-info": { + "service-id": "Αναγνωριστικό υπηρεσίας", + "service-type": "Τύπος υπηρεσίας", + "no-data": "Δεν υπάρχουν δεδομένα" + }, + "transport-messages": { + "title": "Μηνύματα μεταφοράς", + "info": "Όλα τα μηνύματα που ελήφθησαν από συσκευές" + }, + "activity": { + "title": "Δραστηριότητα" + }, + "documentation": { + "title": "Τεκμηρίωση", + "add-link": "Προσθήκη συνδέσμου", + "add-link-title": "Προσθήκη συνδέσμου τεκμηρίωσης", + "name": "Όνομα", + "name-required": "Απαιτείται όνομα.", + "link": "Σύνδεσμος", + "link-required": "Απαιτείται σύνδεσμος.", + "columns": "Στήλες" + }, + "quick-links": { + "title": "Γρήγοροι σύνδεσμοι", + "add-link": "Προσθήκη συνδέσμου", + "add-link-title": "Προσθήκη γρήγορου συνδέσμου", + "quick-link": "Γρήγορος σύνδεσμος", + "quick-link-required": "Απαιτείται γρήγορος σύνδεσμος.", + "no-links-matching": "Δεν βρέθηκαν σύνδεσμοι που να ταιριάζουν με '{{name}}'.", + "columns": "Στήλες" + }, + "recent-dashboards": { + "title": "Πίνακες ελέγχου", + "last": "Τελευταία προβολή", + "starred": "Με αστέρι", + "name": "Όνομα", + "last-viewed": "Τελευταία προβολή", + "no-last-viewed-dashboards": "Δεν υπάρχουν πίνακες ελέγχου με τελευταία προβολή" + }, + "configured-features": { + "title": "Διαμορφωμένα χαρακτηριστικά", + "info": "Κατάσταση χαρακτηριστικών που απαιτούν ρύθμιση", + "email-feature": "Email", + "sms-feature": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "feature-configured": "Το χαρακτηριστικό έχει ρυθμιστεί.\nΚάντε κλικ για ρύθμιση", + "feature-not-configured": "Το χαρακτηριστικό δεν έχει ρυθμιστεί.\nΚάντε κλικ για ρύθμιση" + }, + "version-info": { + "title": "Έκδοση", + "contact-us": "Επικοινωνήστε μαζί μας", + "current-version": "Τρέχουσα έκδοση", + "current": "Τρέχουσα", + "available-version": "Διαθέσιμη έκδοση", + "available": "Διαθέσιμη", + "upgrade": "Αναβάθμιση", + "version-is-up-to-date": "Η έκδοση είναι ενημερωμένη" + }, + "usage-info": { + "title": "Χρήση", + "entities": "Οντότητες", + "api-calls": "Κλήσεις API" + }, + "functions": { + "title": "Λειτουργίες", + "pe-feature-tooltip": "Μόνο στην ThingsBoard\nProfessional Edition", + "switch-to-pe": "Μετάβαση σε PE", + "alarms": "Συναγερμοί", + "dashboards": "Πίνακες ελέγχου", + "entities-and-relations": "Οντότητες & Σχέσεις", + "profiles": "Προφίλ", + "advanced-features": "Προηγμένα χαρακτηριστικά", + "notification-center": "Κέντρο ειδοποιήσεων", + "api-usage": "Χρήση API", + "customers": "Πελάτες", + "customers-hierarchy": "Ιεραρχία πελατών", + "roles-and-permissions": "Ρόλοι & Δικαιώματα", + "groups": "Ομάδες", + "integrations": "Ενσωματώσεις", + "solution-templates": "Πρότυπα λύσεων", + "scheduler": "Προγραμματιστής", + "white-labeling": "Λευκή ετικετοποίηση" + }, + "devices": { + "view-docs": "Προβολή τεκμηρίωσης", + "inactive": "Ανενεργό", + "active": "Ενεργό", + "total": "Σύνολο" + }, + "alarms": { + "critical": "Κρίσιμο", + "assigned-to-me": "Ανατέθηκε σε μένα", + "total": "Σύνολο" + }, + "getting-started": { + "get-started": "Ξεκινήστε", + "finish": "Ολοκλήρωση", + "done-welcome-title": "Καλωσήρθατε", + "done-welcome-text": "Τα καταφέρατε υπέροχα!", + "sys-admin": { + "step1": { + "title": "Δημιουργία Μισθωτή & Διαχειριστή Μισθωτή", + "content": "

Ένας μισθωτής είναι ένα άτομο ή ένας οργανισμός που κατέχει ή παράγει συσκευές και περιουσιακά στοιχεία. Ο μισθωτής μπορεί να έχει πολλούς διαχειριστές, πελάτες, συσκευές και περιουσιακά στοιχεία.

Ο Διαχειριστής Μισθωτή μπορεί να δημιουργήσει και να διαχειριστεί συσκευές, περιουσιακά στοιχεία, πελάτες και πίνακες ελέγχου στον λογαριασμό του μισθωτή.

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

", + "how-to-create-tenant": "Πώς να δημιουργήσετε Μισθωτή & Διαχειριστή Μισθωτή" + }, + "step2": { + "title": "Διαμόρφωση λειτουργίας: Mail server", + "content": "

Η διαμόρφωση του mail server είναι απαραίτητη για ενεργοποίηση χρήστη, ανάκτηση κωδικού και αποστολή ειδοποιήσεων.

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

", + "how-to-configure-mail-server": "Πώς να διαμορφώσετε τον Mail server" + }, + "step3": { + "title": "Διαμόρφωση λειτουργίας: Πάροχος SMS", + "content": "

Ρυθμίστε τους παρόχους SMS για ειδοποίηση πελατών μέσω SMS.

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

", + "how-to-configure-sms-provider": "Πώς να διαμορφώσετε τον πάροχο SMS" + }, + "step4": { + "title": "Διαμόρφωση λειτουργίας: Λευκή ετικετοποίηση", + "content": "

Προσαρμόστε το λογότυπο και το χρωματικό σχήμα της εταιρείας ή του προϊόντος σας χωρίς κώδικα ή επανεκκίνηση της υπηρεσίας.

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

" + }, + "step5": { + "title": "Διαμόρφωση λειτουργίας: 2FA", + "content": "

Ενισχύστε την ασφάλεια των λογαριασμών πλατφόρμας με έλεγχο ταυτότητας δύο παραγόντων.

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

" + }, + "step6": { + "title": "Διαμόρφωση λειτουργίας: OAuth 2", + "content": "

Απλοποιήστε τη σύνδεση για τους χρήστες με τη λειτουργία SSO μέσω OAuth 2.0.

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

" + } + }, + "tenant-admin": { + "step1": { + "title": "Δημιουργία συσκευής", + "content": "

Ας καταχωρήσουμε την πρώτη σας συσκευή στην πλατφόρμα μέσω του UI. Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

", + "how-to-create-device": "Πώς να δημιουργήσετε Συσκευή" + }, + "step2": { + "title": "Σύνδεση συσκευής", + "content-before": "

Για να συνδέσετε τη συσκευή, πρέπει να αποκτήσετε τα διαπιστευτήρια της. Συνιστούμε τη χρήση του προεπιλεγμένου αυτόματα παραγόμενου διακριτικού πρόσβασης.

  • Μεταβείτε στον πίνακα συσκευών
  • Κάντε κλικ στη σειρά της συσκευής για να ανοίξετε τις λεπτομέρειες
  • Πατήστε το κουμπί \"Αντιγραφή διακριτικού πρόσβασης\"

Χρησιμοποιήστε απλές εντολές για αποστολή δεδομένων μέσω HTTP. Μην ξεχάσετε να αντικαταστήσετε το $ACCESS_TOKEN με το διακριτικό της συσκευής σας:

", + "ubuntu": { + "install-curl": "Εγκατάσταση cURL για Ubuntu:" + }, + "macos": { + "install-curl": "Εγκατάσταση cURL για MacOS:" + }, + "windows": { + "install-curl": "Από τα Windows 10 b17063, το cURL είναι διαθέσιμο από προεπιλογή." + }, + "replace-access-token": "Αντικαταστήστε το $ACCESS_TOKEN με το διακριτικό της συσκευής σας:", + "content-after": "

Μπορείτε επίσης να χρησιμοποιήσετε άλλα πρωτόκολλα όπως MQTT, CoAP, κ.λπ.

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

", + "how-to-connect-device": "Πώς να συνδέσετε Συσκευή" + }, + "step3": { + "title": "Δημιουργία πίνακα ελέγχου", + "content": "

Δημιουργήστε έναν πίνακα ελέγχου για να απεικονίσετε δεδομένα από οντότητες όπως συσκευές, περιουσιακά στοιχεία, κ.λπ.

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

", + "how-to-create-dashboard": "Πώς να δημιουργήσετε Πίνακα ελέγχου" + }, + "step4": { + "title": "Διαμόρφωση κανόνων συναγερμού", + "alarm-rules": "Κανόνες συναγερμού", + "content": "

Ας ενεργοποιήσουμε συναγερμό όταν η θερμοκρασία φτάσει τους 25°C. Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

", + "how-to-configure-alarm-rules": "Πώς να διαμορφώσετε Κανόνες συναγερμού" + }, + "step5": { + "title": "Δημιουργία συναγερμού", + "content-before": "

Για να ενεργοποιήσετε τον συναγερμό, στείλτε νέα τιμή τηλεμετρίας 26°C ή μεγαλύτερη.

", + "replace-access-token": "Αντικαταστήστε το $ACCESS_TOKEN με το διακριτικό της συσκευής σας:", + "content-after": "

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

", + "how-to-create-alarm": "Πώς να δημιουργήσετε Συναγερμό" + }, + "step6": { + "title": "Δημιουργία πελάτη και διαμοιρασμός πίνακα ελέγχου", + "content": "

Δημιουργώντας πίνακες ελέγχου τελικού χρήστη, ένας πελάτης μπορεί να δει μόνο τις δικές του συσκευές. Τα δεδομένα άλλων πελατών θα είναι κρυφά.

Ακολουθήστε την τεκμηρίωση για το πώς να το κάνετε:

" + } } } }, "icon": { "icon": "Εικονίδιο", - "select-icon": "Επιλογή εικονιδίου ", - "material-icons": "Υλικά εικονίδια", - "show-all": "Προβολή όλων των εικονιδίων" - }, - "subscription": { - "entity-limit-text": "Ωστόσο, μπορείτε να αναβαθμίσετε το πρόγραμμα εγγραφής σας για να αυξήσετε τα όριά σας.", - "upgrade-your-plan": "Αναβάθμιση του σχεδίου συνδρομής", - "white-labeling-feature": "Εμφάνιση χαρακτηριστικού", - "white-labeling-text-full": "Αλλάξτε το διακριτικό τίτλο της πλατφόρμας με το λογότυπο της εταιρείας ή του προϊόντος σας και το συνδυασμό χρωμάτων σε 2 λεπτά.

Αφαιρέστε το \"Powered By\" στο κάτω μέρος του dashboard.
Δεν απαιτείται κώδικας ή επανεκκίνηση υπηρεσίας. Επιτρέψτε και στους πελάτες σας να αλλάξουν το περιβάλλον τους.", - "enable-white-labeling": "Ενεργοποίηση εμφάνισης χαρακτηριστικού τώρα αναβαθμίζοντας το σχέδιο εγγραφής σας!", - "read-more": "Διαβάστε περισσότερα", - "white-labeling-video-text": "Δείτε το εκπαιδευτικό βίντεο παρακάτω για να δείτε πώς λειτουργεί αυτό το χαρακτηριστικό!" + "icons": "Εικονίδια", + "select-icon": "Επιλέξτε εικονίδιο", + "material-icons": "Material εικονίδια", + "show-all": "Εμφάνιση όλων των εικονιδίων", + "search-icon": "Αναζήτηση εικονιδίου", + "no-icons-found": "Δεν βρέθηκαν εικονίδια για '{{iconSearch}}'" }, - "subscription-error": { - "title": "Παραβίαση συνδρομής", - "warning-title": "Προειδοποίηση συνδρομής", - "limit-reached": { - "device-count": "Έχετε φτάσει τις μέγιστες συσκευές ({{value}}) που επιτρέπονται από το πρόγραμμα εγγραφής σας!", - "asset-count": "Έχετε φτάσει τα μέγιστα assets ({{value}}) που επιτρέπονται από το πρόγραμμα εγγραφής σας!" - }, - "feature-disabled": { - "white-labeling": "Η εμφάνιση χαρακτηριστικού δεν επιτρέπεται από το σχέδιο εγγραφής σας!" - } + "phone-input": { + "phone-input-label": "Αριθμός τηλεφώνου", + "phone-input-required": "Απαιτείται αριθμός τηλεφώνου", + "phone-input-validation": "Ο αριθμός τηλεφώνου δεν είναι έγκυρος ή δεν είναι δυνατός", + "phone-input-pattern": "Μη έγκυρος αριθμός τηλεφώνου. Πρέπει να είναι σε μορφή E.164, π.χ. {{phoneNumber}}", + "phone-input-hint": "Αριθμός τηλεφώνου σε μορφή E.164, π.χ. {{phoneNumber}}" }, "custom": { "widget-action": { - "action-cell-button": "Κουμπί ενέργειας κελιών", - "row-click": "Κλικ στη σειρά", - "polygon-click": "Κλικ στο πολύγωνο", - "marker-click": "Κλικ στο δείκτη", - "tooltip-tag-action": "Ετικέτα εργαλείου ενέργειας", - "node-selected": "Στον επιλεγμένο κόμβο", - "element-click": "Κλικ στο στοιχείο HTML" + "action-cell-button": "Κουμπί ενέργειας κελιού", + "row-click": "Κατά το κλικ στη σειρά", + "cell-click": "Κατά το κλικ στο κελί", + "polygon-click": "Κατά το κλικ στο πολύγωνο", + "marker-click": "Κατά το κλικ στο δείκτη", + "circle-click": "Κατά το κλικ στον κύκλο", + "tooltip-tag-action": "Ενέργεια ετικέτας συμβουλής", + "node-selected": "Κατά την επιλογή κόμβου", + "element-click": "Κατά το κλικ στο HTML στοιχείο", + "pie-slice-click": "Κατά το κλικ στο κομμάτι", + "row-double-click": "Κατά το διπλό κλικ στη σειρά", + "cell-double-click": "Κατά το διπλό κλικ στο κελί", + "card-click": "Κατά το κλικ στην κάρτα", + "click": "Κατά το κλικ" } }, + "paginator": { + "items-per-page": "Στοιχεία ανά σελίδα:", + "first-page-label": "Πρώτη σελίδα", + "last-page-label": "Τελευταία σελίδα", + "next-page-label": "Επόμενη σελίδα", + "previous-page-label": "Προηγούμενη σελίδα", + "items-per-page-separator": "από" + }, "language": { + "language": "Γλώσσα", + "locales": { + "ar_AE": "العربية (الإمارات العربية المتحدة)", + "ca_ES": "català (Espanya)", + "cs_CZ": "čeština (Česko)", + "da_DK": "dansk (Danmark)", + "de_DE": "Deutsch (Deutschland)", + "el_GR": "Ελληνικά (Ελλάδα)", + "en_US": "English (United States)", + "es_ES": "español (España)", + "fa_IR": "فارسی (ایران)", + "fr_FR": "français (France)", + "it_IT": "italiano (Italia)", + "ja_JP": "日本語 (日本)", + "ka_GE": "ქართული (საქართველო)", + "ko_KR": "한국어 (대한민국)", + "lt_LT": "lietuvių (Lietuva)", + "lv_LV": "latviešu (Latvija)", + "nl_BE": "Nederlands (België)", + "pl_PL": "polski (Polska)", + "pt_BR": "português (Brasil)", + "ro_RO": "română (România)", + "sl_SI": "slovenščina (Slovenija)", + "tr_TR": "Türkçe (Türkiye)", + "uk_UA": "українська (Україна)", + "zh_CN": "中文 (中国)", + "zh_TW": "中文 (台灣)" + } } -} +} \ No newline at end of file diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index bb6f70c2aa..1308631be4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -9192,7 +9192,7 @@ "items-per-page-separator": "de" }, "language": { - "language": "Language", + "language": "Idioma", "locales": { "ar_AE": "العربية (الإمارات العربية المتحدة)", "ca_ES": "català (Espanya)", diff --git a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json index 07739906bb..ddb462c5e0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json @@ -1979,7 +1979,7 @@ "alarm-rule-additional-info": "Informations supplémentaires", "edit-alarm-rule-additional-info": "Modifier les informations supplémentaires", "alarm-rule-additional-info-placeholder": "Ajoutez ici vos commentaires ou ajustements à afficher dans les détails de l'alarme", - "alarm-rule-additional-info-hint": "Astuce : utilisez code>${keyName} pour insérer les valeurs des clés utilisées dans la condition.", + "alarm-rule-additional-info-hint": "Astuce : utilisez ${keyName} pour insérer les valeurs des clés utilisées dans la condition.", "alarm-rule-mobile-dashboard": "Tableau de bord mobile", "alarm-rule-mobile-dashboard-hint": "Utilisé comme tableau de bord des détails de l'alarme dans l'application mobile", "alarm-rule-no-mobile-dashboard": "Aucun tableau de bord sélectionné", @@ -4783,7 +4783,7 @@ "from-metadata": "Depuis les métadonnées", "to-template": "À", "to-template-required": "Le champ À est requis", - "mail-address-list-template-hint": "Liste d’adresses séparées par des virgules, utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message.", + "mail-address-list-template-hint": "Liste d’adresses séparées par des virgules, utilisez ${metadataKey} pour une valeur depuis les métadonnées, $[messageKey] pour une valeur depuis le corps du message", "cc-template": "Cc", "bcc-template": "Cci", "subject-template": "Objet", @@ -4816,7 +4816,7 @@ "max-response-size": "Taille maximale de réponse (en Ko)", "max-response-size-hint": "Quantité maximale de mémoire utilisée pour le tampon lors du décodage/encodage des messages HTTP comme les charges JSON ou XML", "headers": "En-têtes", - "headers-hint": "Utilisez ${metadataKey} pour une valeur des métadonnées, $[messageKey] pour une valeur du corps du message", + "headers-hint": "Utilisez ${metadataKey} pour une valeur provenant des métadonnées, $[messageKey] pour une valeur provenant du corps du message dans les champs en-tête/valeur", "header": "En-tête", "header-required": "L'en-tête est requis", "value": "Valeur", @@ -4884,7 +4884,7 @@ "pubsub-topic-name": "Nom du sujet", "pubsub-topic-name-required": "Le nom du sujet est requis", "message-attributes": "Attributs du message", - "message-attributes-hint": "Utilisez ${metadataKey} pour une valeur depuis les métadonnées, $[messageKey] depuis le message", + "message-attributes-hint": "Utilisez ${metadataKey} pour une valeur provenant des métadonnées, $[messageKey] pour une valeur provenant du corps du message dans les champs nom/valeur", "connect-timeout": "Délai de connexion (s)", "connect-timeout-required": "Le délai de connexion est requis.", "connect-timeout-range": "Le délai de connexion doit être compris entre 1 et 200.", @@ -4964,7 +4964,7 @@ "proxy-scheme": "Schéma de proxy", "numbers-to-template": "Numéros de téléphone vers modèle", "numbers-to-template-required": "Le modèle de numéros de téléphone est requis", - "numbers-to-template-hint": "Liste de numéros séparés par des virgules. Utilisez ${metadataKey} ou $[messageKey].", + "numbers-to-template-hint": "Numéros de téléphone séparés par des virgules, utilisez ${metadataKey} pour une valeur provenant des métadonnées, $[messageKey] pour une valeur provenant du corps du message", "sms-message-template": "Modèle de message SMS", "sms-message-template-required": "Le modèle de message SMS est requis", "use-system-sms-settings": "Utiliser les paramètres SMS système", @@ -5079,7 +5079,7 @@ "period-value-key": "Clé de la période", "period-value-key-required": "La clé de la période est requise.", "general-pattern-hint": "Utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message.", - "alarm-severity-pattern-hint": "Utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message. Les niveaux doivent être CRITICAL, MAJOR, etc.", + "alarm-severity-pattern-hint": "Utilisez ${metadataKey} pour une valeur provenant des métadonnées, $[messageKey] pour une valeur provenant du corps du message. La gravité de l'alarme doit être un niveau système (CRITICAL, MAJOR, etc.)", "output-node-name-hint": "Le nom du nœud correspond au type de relation du message de sortie.", "use-server-ts": "Utiliser l’horodatage serveur", "use-server-ts-hint": "Utilise le temps actuel du serveur pour les séries temporelles sans horodatage explicite.", @@ -6435,8 +6435,8 @@ "tidy": "Nettoyer", "css": "CSS", "settings-form": "Formulaire de paramètres", - "data-key-settings-form": "Formulaire de paramètres de clé de données", - "latest-data-key-settings-form": "Formulaire de paramètres des dernières données", + "data-key-settings-form": "Formulaire des paramètres de clé de données", + "latest-data-key-settings-form": "Formulaire des paramètres de la dernière clé de données", "widget-settings": "Paramètres du widget", "description": "Description", "tags": "Étiquettes", @@ -7687,7 +7687,7 @@ }, "label-widget": { "label-pattern": "Modèle", - "label-pattern-hint": "Astuce : par ex. 'Texte ${keyName} unités.' ou ${#<key index>} unités'", + "label-pattern-hint": "Astuce: par ex. 'Texte ${keyName} unités.' ou ${#<key index>} unités", "label-pattern-required": "Le modèle est requis", "label-position": "Position (pourcentage par rapport à l’arrière-plan)", "x-pos": "X", diff --git a/ui-ngx/src/assets/locale/locale.constant-it_IT.json b/ui-ngx/src/assets/locale/locale.constant-it_IT.json index f976a76052..ed38a76f6d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-it_IT.json +++ b/ui-ngx/src/assets/locale/locale.constant-it_IT.json @@ -7687,7 +7687,7 @@ }, "label-widget": { "label-pattern": "Modello", - "label-pattern-hint": "Suggerimento: ad esempio, 'Testo ${keyName} unità.' oppure ${#<indice chiave>} unità'", + "label-pattern-hint": "Suggerimento: ad esempio, 'Testo ${keyName} unità.' oppure ${#<key index>} unità'", "label-pattern-required": "Il pattern è obbligatorio", "label-position": "Posizione (percentuale rispetto allo sfondo)", "x-pos": "X", From 916a4e0fd842f2077668937db9dfd4d7e9f84e29 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 3 Jun 2025 11:21:32 +0300 Subject: [PATCH 268/335] removed locale list from all files except en_US --- .../assets/locale/locale.constant-da_DK.json | 29 +- .../assets/locale/locale.constant-de_DE.json | 29 +- .../assets/locale/locale.constant-el_GR.json | 31 +- .../assets/locale/locale.constant-en_US.json | 2 + .../assets/locale/locale.constant-es_ES.json | 29 +- .../assets/locale/locale.constant-fr_FR.json | 29 +- .../assets/locale/locale.constant-it_IT.json | 29 +- .../assets/locale/locale.constant-nl_BE.json | 25 +- .../assets/locale/locale.constant-nl_NL.json | 9197 +++++++++++++++++ .../assets/locale/locale.constant-no_NO.json | 9197 +++++++++++++++++ 10 files changed, 18404 insertions(+), 193 deletions(-) create mode 100644 ui-ngx/src/assets/locale/locale.constant-nl_NL.json create mode 100644 ui-ngx/src/assets/locale/locale.constant-no_NO.json diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index 9e679335b0..8bff960467 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -9191,33 +9191,6 @@ "items-per-page-separator": "af" }, "language": { - "language": "Language", - "locales": { - "ar_AE": "العربية (الإمارات العربية المتحدة)", - "ca_ES": "català (Espanya)", - "cs_CZ": "čeština (Česko)", - "da_DK": "dansk (Danmark)", - "de_DE": "Deutsch (Deutschland)", - "el_GR": "Ελληνικά (Ελλάδα)", - "en_US": "English (United States)", - "es_ES": "español (España)", - "fa_IR": "فارسی (ایران)", - "fr_FR": "français (France)", - "it_IT": "italiano (Italia)", - "ja_JP": "日本語 (日本)", - "ka_GE": "ქართული (საქართველო)", - "ko_KR": "한국어 (대한민국)", - "lt_LT": "lietuvių (Lietuva)", - "lv_LV": "latviešu (Latvija)", - "nl_BE": "Nederlands (België)", - "pl_PL": "polski (Polska)", - "pt_BR": "português (Brasil)", - "ro_RO": "română (România)", - "sl_SI": "slovenščina (Slovenija)", - "tr_TR": "Türkçe (Türkiye)", - "uk_UA": "українська (Україна)", - "zh_CN": "中文 (中国)", - "zh_TW": "中文 (台灣)" - } + "language": "Language" } } diff --git a/ui-ngx/src/assets/locale/locale.constant-de_DE.json b/ui-ngx/src/assets/locale/locale.constant-de_DE.json index 2a7444d955..f48081bee3 100644 --- a/ui-ngx/src/assets/locale/locale.constant-de_DE.json +++ b/ui-ngx/src/assets/locale/locale.constant-de_DE.json @@ -9191,33 +9191,6 @@ "items-per-page-separator": "von" }, "language": { - "language": "Sprache", - "locales": { - "ar_AE": "العربية (الإمارات العربية المتحدة)", - "ca_ES": "català (Espanya)", - "cs_CZ": "čeština (Česko)", - "da_DK": "dansk (Danmark)", - "de_DE": "Deutsch (Deutschland)", - "el_GR": "Ελληνικά (Ελλάδα)", - "en_US": "English (United States)", - "es_ES": "español (España)", - "fa_IR": "فارسی (ایران)", - "fr_FR": "français (France)", - "it_IT": "italiano (Italia)", - "ja_JP": "日本語 (日本)", - "ka_GE": "ქართული (საქართველო)", - "ko_KR": "한국어 (대한민국)", - "lt_LT": "lietuvių (Lietuva)", - "lv_LV": "latviešu (Latvija)", - "nl_BE": "Nederlands (België)", - "pl_PL": "polski (Polska)", - "pt_BR": "português (Brasil)", - "ro_RO": "română (România)", - "sl_SI": "slovenščina (Slovenija)", - "tr_TR": "Türkçe (Türkiye)", - "uk_UA": "українська (Україна)", - "zh_CN": "中文 (中国)", - "zh_TW": "中文 (台灣)" - } + "language": "Sprache" } } diff --git a/ui-ngx/src/assets/locale/locale.constant-el_GR.json b/ui-ngx/src/assets/locale/locale.constant-el_GR.json index e05a2a5d8e..151dbeb528 100644 --- a/ui-ngx/src/assets/locale/locale.constant-el_GR.json +++ b/ui-ngx/src/assets/locale/locale.constant-el_GR.json @@ -9192,33 +9192,6 @@ "items-per-page-separator": "από" }, "language": { - "language": "Γλώσσα", - "locales": { - "ar_AE": "العربية (الإمارات العربية المتحدة)", - "ca_ES": "català (Espanya)", - "cs_CZ": "čeština (Česko)", - "da_DK": "dansk (Danmark)", - "de_DE": "Deutsch (Deutschland)", - "el_GR": "Ελληνικά (Ελλάδα)", - "en_US": "English (United States)", - "es_ES": "español (España)", - "fa_IR": "فارسی (ایران)", - "fr_FR": "français (France)", - "it_IT": "italiano (Italia)", - "ja_JP": "日本語 (日本)", - "ka_GE": "ქართული (საქართველო)", - "ko_KR": "한국어 (대한민국)", - "lt_LT": "lietuvių (Lietuva)", - "lv_LV": "latviešu (Latvija)", - "nl_BE": "Nederlands (België)", - "pl_PL": "polski (Polska)", - "pt_BR": "português (Brasil)", - "ro_RO": "română (România)", - "sl_SI": "slovenščina (Slovenija)", - "tr_TR": "Türkçe (Türkiye)", - "uk_UA": "українська (Україна)", - "zh_CN": "中文 (中国)", - "zh_TW": "中文 (台灣)" - } + "language": "Γλώσσα" } -} \ No newline at end of file +} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 9a2e8ac0d4..7227854182 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -9418,6 +9418,8 @@ "lt_LT": "lietuvių (Lietuva)", "lv_LV": "latviešu (Latvija)", "nl_BE": "Nederlands (België)", + "nl_NL": "Nederlands (Nederland)", + "no_NO": "norsk (Norge)", "pl_PL": "polski (Polska)", "pt_BR": "português (Brasil)", "ro_RO": "română (România)", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index cd2c4e4c01..daab8822fd 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -9191,33 +9191,6 @@ "items-per-page-separator": "de" }, "language": { - "language": "Idioma", - "locales": { - "ar_AE": "العربية (الإمارات العربية المتحدة)", - "ca_ES": "català (Espanya)", - "cs_CZ": "čeština (Česko)", - "da_DK": "dansk (Danmark)", - "de_DE": "Deutsch (Deutschland)", - "el_GR": "Ελληνικά (Ελλάδα)", - "en_US": "English (United States)", - "es_ES": "español (España)", - "fa_IR": "فارسی (ایران)", - "fr_FR": "français (France)", - "it_IT": "italiano (Italia)", - "ja_JP": "日本語 (日本)", - "ka_GE": "ქართული (საქართველო)", - "ko_KR": "한국어 (대한민국)", - "lt_LT": "lietuvių (Lietuva)", - "lv_LV": "latviešu (Latvija)", - "nl_BE": "Nederlands (België)", - "pl_PL": "polski (Polska)", - "pt_BR": "português (Brasil)", - "ro_RO": "română (România)", - "sl_SI": "slovenščina (Slovenija)", - "tr_TR": "Türkçe (Türkiye)", - "uk_UA": "українська (Україна)", - "zh_CN": "中文 (中国)", - "zh_TW": "中文 (台灣)" - } + "language": "Idioma" } } diff --git a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json index 3695562dbc..d715c60187 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json @@ -9191,33 +9191,6 @@ "items-per-page-separator": "sur" }, "language": { - "language": "Langue", - "locales": { - "ar_AE": "العربية (الإمارات العربية المتحدة)", - "ca_ES": "català (Espanya)", - "cs_CZ": "čeština (Česko)", - "da_DK": "dansk (Danmark)", - "de_DE": "Deutsch (Deutschland)", - "el_GR": "Ελληνικά (Ελλάδα)", - "en_US": "English (United States)", - "es_ES": "español (España)", - "fa_IR": "فارسی (ایران)", - "fr_FR": "français (France)", - "it_IT": "italiano (Italia)", - "ja_JP": "日本語 (日本)", - "ka_GE": "ქართული (საქართველო)", - "ko_KR": "한국어 (대한민국)", - "lt_LT": "lietuvių (Lietuva)", - "lv_LV": "latviešu (Latvija)", - "nl_BE": "Nederlands (België)", - "pl_PL": "polski (Polska)", - "pt_BR": "português (Brasil)", - "ro_RO": "română (România)", - "sl_SI": "slovenščina (Slovenija)", - "tr_TR": "Türkçe (Türkiye)", - "uk_UA": "українська (Україна)", - "zh_CN": "中文 (中国)", - "zh_TW": "中文 (台灣)" - } + "language": "Langue" } } diff --git a/ui-ngx/src/assets/locale/locale.constant-it_IT.json b/ui-ngx/src/assets/locale/locale.constant-it_IT.json index 1f1628a429..add8432b40 100644 --- a/ui-ngx/src/assets/locale/locale.constant-it_IT.json +++ b/ui-ngx/src/assets/locale/locale.constant-it_IT.json @@ -9191,33 +9191,6 @@ "items-per-page-separator": "di" }, "language": { - "language": "Lingua", - "locales": { - "ar_AE": "العربية (الإمارات العربية المتحدة)", - "ca_ES": "català (Espanya)", - "cs_CZ": "čeština (Česko)", - "da_DK": "dansk (Danmark)", - "de_DE": "Deutsch (Deutschland)", - "el_GR": "Ελληνικά (Ελλάδα)", - "en_US": "English (United States)", - "es_ES": "español (España)", - "fa_IR": "فارسی (ایران)", - "fr_FR": "français (France)", - "it_IT": "italiano (Italia)", - "ja_JP": "日本語 (日本)", - "ka_GE": "ქართული (საქართველო)", - "ko_KR": "한국어 (대한민국)", - "lt_LT": "lietuvių (Lietuva)", - "lv_LV": "latviešu (Latvija)", - "nl_BE": "Nederlands (België)", - "pl_PL": "polski (Polska)", - "pt_BR": "português (Brasil)", - "ro_RO": "română (România)", - "sl_SI": "slovenščina (Slovenija)", - "tr_TR": "Türkçe (Türkiye)", - "uk_UA": "українська (Україна)", - "zh_CN": "中文 (中国)", - "zh_TW": "中文 (台灣)" - } + "language": "Lingua" } } diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index d644a927df..3c8bfbde60 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -7196,29 +7196,6 @@ "items-per-page-separator": "van" }, "language": { - "language": "Taal", - "locales": { - "ca_ES": "Catalaans", - "cs_CZ": "Česky", - "da_DK": "Dansk", - "de_DE": "Deutsch", - "el_GR": "Ελληνικά", - "en_US": "Engels", - "es_ES": "Español", - "fa_IR": "فارسي", - "fr_FR": "Français", - "it_IT": "Italiano", - "ja_JP": "日本語", - "ka_GE": "ქართული", - "ko_KR": "한국어", - "lv_LV": "Kanton Latviešu", - "pt_BR": "Português do Brasil", - "ro_RO": "Română", - "sl_SI": "Slovenščina", - "tr_TR": "Türkçe", - "uk_UA": "Українська", - "zh_CN": "简体中文", - "zh_TW": "繁體中文" - } + "language": "Taal" } } diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_NL.json b/ui-ngx/src/assets/locale/locale.constant-nl_NL.json new file mode 100644 index 0000000000..379f36b9de --- /dev/null +++ b/ui-ngx/src/assets/locale/locale.constant-nl_NL.json @@ -0,0 +1,9197 @@ +{ + "access": { + "unauthorized": "Ongeautoriseerd", + "unauthorized-access": "Ongeautoriseerde toegang", + "unauthorized-access-text": "U moet inloggen om toegang tot deze bron te krijgen!", + "access-forbidden": "Toegang verboden", + "access-forbidden-text": "U heeft geen toegangsrechten tot deze locatie!
Probeer in te loggen met een andere gebruiker als u nog steeds toegang wilt krijgen tot deze locatie.", + "refresh-token-expired": "Sessie is verlopen", + "refresh-token-failed": "Kan sessie niet vernieuwen", + "permission-denied": "Toestemming geweigerd", + "permission-denied-text": "U heeft geen toestemming om deze handeling uit te voeren!" + }, + "account": { + "account": "Account", + "notification-settings": "Meldingsinstellingen" + }, + "action": { + "activate": "Activeren", + "suspend": "Opschorten", + "save": "Opslaan", + "saveAs": "Opslaan als", + "move": "Verplaatsen", + "cancel": "Annuleren", + "ok": "OK", + "delete": "Verwijderen", + "add": "Toevoegen", + "yes": "Ja", + "no": "Nee", + "update": "Bijwerken", + "remove": "Verwijderen", + "search": "Zoeken", + "clear-search": "Zoekopdracht wissen", + "assign": "Toewijzen", + "unassign": "Toewijzing ongedaan maken", + "share": "Delen", + "make-private": "Privé maken", + "apply": "Toepassen", + "apply-changes": "Wijzigingen toepassen", + "edit-mode": "Bewerkmodus", + "enter-edit-mode": "Bewerkmodus starten", + "decline-changes": "Wijzigingen afwijzen", + "decline": "Afwijzen", + "close": "Sluiten", + "back": "Terug", + "run": "Uitvoeren", + "sign-in": "Inloggen!", + "edit": "Bewerken", + "view": "Bekijken", + "create": "Aanmaken", + "drag": "Slepen", + "refresh": "Vernieuwen", + "undo": "Ongedaan maken", + "copy": "Kopiëren", + "paste": "Plakken", + "copy-reference": "Referentie kopiëren", + "paste-reference": "Referentie plakken", + "import": "Importeren", + "export": "Exporteren", + "share-via": "Delen via {{provider}}", + "select": "Selecteren", + "continue": "Doorgaan", + "discard-changes": "Wijzigingen negeren", + "download": "Downloaden", + "next": "Volgende", + "next-with-label": "Volgende: {{label}}", + "read-more": "Meer lezen", + "hide": "Verbergen", + "test": "Testen", + "done": "Gereed", + "print": "Afdrukken", + "restore": "Herstellen", + "confirm": "Bevestigen", + "more": "Meer", + "less": "Minder", + "skip": "Overslaan", + "send": "Verzenden", + "reset": "Resetten", + "show-more": "Meer weergeven", + "dont-show-again": "Niet opnieuw weergeven", + "see-documentation": "Bekijk documentatie", + "clear": "Wissen", + "upload": "Uploaden", + "delete-anyway": "Toch verwijderen", + "delete-selected": "Geselecteerde verwijderen", + "set": "Instellen" + }, + "aggregation": { + "aggregation": "Aggregatie", + "function": "Functie voor gegevensaggregatie", + "limit": "Maximale waarden", + "group-interval": "Groepeerinterval", + "min": "Min", + "max": "Max", + "avg": "Gemiddelde", + "sum": "Som", + "count": "Aantal", + "none": "Geen" + }, + "admin": { + "settings": "Instellingen", + "general": "Algemeen", + "general-settings": "Algemene instellingen", + "home-settings": "Startpagina-instellingen", + "home": "Startpagina", + "outgoing-mail": "Mailserver", + "outgoing-mail-settings": "Instellingen uitgaande mailserver", + "system-settings": "Systeeminstellingen", + "test-mail-sent": "Testmail is succesvol verzonden!", + "base-url": "Basis-URL", + "base-url-required": "Basis-URL is verplicht.", + "prohibit-different-url": "Gebruik van hostnaam uit clientaanvraagheaders verbieden", + "prohibit-different-url-hint": "Deze instelling moet ingeschakeld zijn in productieomgevingen. Uitschakelen kan beveiligingsrisico’s veroorzaken.", + "device-connectivity": { + "device-connectivity": "Apparaatconnectiviteit", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPS", + "mqtt": "MQTT", + "mqtts": "MQTTS", + "coap": "COAP", + "coaps": "COAPS", + "hint": "Als de velden host of poort leeg zijn, wordt de standaardprotocolwaarde gebruikt.", + "host": "Host", + "port": "Poort", + "port-pattern": "Poort moet een positief geheel getal zijn.", + "port-range": "Poort moet tussen 1 en 65535 liggen." + }, + "mail-from": "Afzenderadres", + "mail-from-required": "Afzenderadres is verplicht.", + "smtp-protocol": "SMTP-protocol", + "smtp-host": "SMTP-host", + "smtp-host-required": "SMTP-host is verplicht.", + "smtp-port": "SMTP-poort", + "smtp-port-required": "U moet een SMTP-poort opgeven.", + "smtp-port-invalid": "Dit lijkt geen geldige SMTP-poort.", + "timeout-msec": "Timeout (msec)", + "timeout-required": "Timeout is verplicht.", + "timeout-invalid": "Dit lijkt geen geldige timeout.", + "enable-tls": "TLS inschakelen", + "tls-version": "TLS-versie", + "enable-proxy": "Proxy inschakelen", + "proxy-host": "Proxyhost", + "proxy-host-required": "Proxyhost is verplicht.", + "proxy-port": "Proxypoort", + "proxy-port-required": "Proxypoort is verplicht.", + "proxy-port-range": "Proxypoort moet tussen 1 en 65535 liggen.", + "proxy-user": "Proxygebruiker", + "proxy-password": "Proxywachtwoord", + "change-password": "Wachtwoord wijzigen", + "send-test-mail": "Testmail verzenden", + "sms-provider": "SMS-provider", + "sms-provider-settings": "Instellingen SMS-provider", + "sms-provider-type": "Type SMS-provider", + "sms-provider-type-required": "Type SMS-provider is verplicht.", + "sms-provider-type-aws-sns": "Amazon SNS", + "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID is verplicht", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key is verplicht", + "aws-region": "AWS-regio", + "aws-region-required": "AWS-regio is verplicht", + "number-from": "Afzender telefoonnummer", + "number-from-required": "Afzender telefoonnummer is verplicht.", + "number-to": "Ontvanger telefoonnummer", + "number-to-required": "Ontvanger telefoonnummer is verplicht.", + "phone-number-hint": "Telefoonnummer in E.164-formaat, bijv. +19995550123", + "phone-number-hint-twilio": "Telefoonnummer in E.164-formaat/SID van telefoonnummer/Messaging Service SID, bijv. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Ongeldig telefoonnummer. Moet in E.164-formaat zijn, bijv. +19995550123.", + "phone-number-pattern-twilio": "Ongeldig telefoonnummer. Moet in E.164-formaat/SID van telefoonnummer/Messaging Service SID zijn, bijv. +19995550123/PNXXX/MGXXX.", + "sms-message": "SMS-bericht", + "sms-message-required": "SMS-bericht is verplicht.", + "sms-message-max-length": "SMS-bericht mag niet langer zijn dan 1600 tekens", + "twilio-account-sid": "Twilio Account SID", + "twilio-account-sid-required": "Twilio Account SID is verplicht", + "twilio-account-token": "Twilio Account Token", + "twilio-account-token-required": "Twilio Account Token is verplicht", + "send-test-sms": "Test-SMS verzenden", + "test-sms-sent": "Test-SMS is succesvol verzonden!", + "security-settings": "Beveiligingsinstellingen", + "password-policy": "Wachtwoordbeleid", + "minimum-password-length": "Minimale wachtwoordlengte", + "minimum-password-length-required": "Minimale wachtwoordlengte is verplicht", + "minimum-password-length-range": "Minimale wachtwoordlengte moet tussen 6 en 50 liggen", + "maximum-password-length": "Maximale wachtwoordlengte", + "maximum-password-length-min": "Maximale wachtwoordlengte moet ten minste 6 zijn", + "maximum-password-length-less-min": "Maximale wachtwoordlengte moet groter zijn dan minimale lengte", + "minimum-uppercase-letters": "Minimaal aantal hoofdletters", + "minimum-uppercase-letters-range": "Minimaal aantal hoofdletters mag niet negatief zijn", + "minimum-lowercase-letters": "Minimaal aantal kleine letters", + "minimum-lowercase-letters-range": "Minimaal aantal kleine letters mag niet negatief zijn", + "minimum-digits": "Minimaal aantal cijfers", + "minimum-digits-range": "Minimaal aantal cijfers mag niet negatief zijn", + "minimum-special-characters": "Minimaal aantal speciale tekens", + "minimum-special-characters-range": "Minimaal aantal speciale tekens mag niet negatief zijn", + "password-expiration-period-days": "Wachtwoordvervalperiode in dagen", + "password-expiration-period-days-range": "Vervaldagen wachtwoord mogen niet negatief zijn", + "password-reuse-frequency-days": "Hergebruikfrequentie wachtwoord in dagen", + "password-reuse-frequency-days-range": "Hergebruikfrequentie wachtwoord in dagen mag niet negatief zijn", + "allow-whitespace": "Spaties toestaan", + "force-reset-password-if-no-valid": "Wachtwoordreset forceren als ongeldig", + "force-reset-password-if-no-valid-hint": "Wees voorzichtig met deze functie: gebruikers met een ongeldig wachtwoord moeten hun wachtwoord via e-mail opnieuw instellen.", + "general-policy": "Algemeen beleid", + "max-failed-login-attempts": "Maximaal aantal mislukte inlogpogingen voordat account wordt vergrendeld", + "minimum-max-failed-login-attempts-range": "Maximum aantal mislukte pogingen mag niet negatief zijn", + "user-lockout-notification-email": "Stuur e-mailmelding bij accountvergrendeling", + "user-activation-token-ttl": "Levensduur activatielink gebruiker in uren", + "user-activation-token-ttl-range": "Levensduur activatielink moet tussen 1 en 24 uur liggen", + "password-reset-token-ttl": "Levensduur wachtwoordherstellink in uren", + "password-reset-token-ttl-range": "Levensduur herstellink moet tussen 1 en 24 uur liggen", + "mobile-secret-key-length": "Lengte mobiele geheime sleutel", + "mobile-secret-key-length-range": "Lengte van mobiele geheime sleutel moet positief zijn", + "domain-name": "Domeinnaam", + "domain-name-unique": "Domeinnaam en protocol moeten uniek zijn.", + "domain-name-max-length": "Domeinnaam moet minder dan 256 tekens bevatten", + "error-verification-url": "Domeinnaam mag geen '/' of ':' bevatten. Voorbeeld: thingsboard.io", + "connection-settings": "Verbindingsinstellingen", + "oauth2": { + "access-token-uri": "Access token URI", + "access-token-uri-required": "Access token URI is verplicht.", + "activate-user": "Gebruiker activeren", + "add-domain": "Domein toevoegen", + "delete-domain": "Domein verwijderen", + "add-provider": "Provider toevoegen", + "delete-provider": "Provider verwijderen", + "allow-user-creation": "Gebruikerscreatie toestaan", + "always-fullscreen": "Altijd volledig scherm", + "authorization-uri": "Autorisatie URI", + "authorization-uri-required": "Autorisatie URI is verplicht.", + "add-client": "OAuth 2.0-client toevoegen", + "client-details": "OAuth 2.0-clientdetails", + "client": "OAuth 2.0-client", + "clients": "OAuth 2.0-clients", + "no-oauth2-clients": "Geen OAuth 2.0-clients gevonden", + "search-oauth2-clients": "Zoek OAuth 2.0-clients", + "delete-client-title": "Weet u zeker dat u de OAuth 2.0-client '{{clientName}}' wilt verwijderen?", + "delete-client-text": "Let op: na bevestiging worden de client en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-mobile-app-title": "Weet u zeker dat u de mobiele applicatie '{{applicationName}}' wilt verwijderen?", + "delete-mobile-app-text": "Let op: na bevestiging worden de mobiele applicatie en alle gerelateerde gegevens onherstelbaar verwijderd.", + "title": "Titel", + "client-title-required": "Titel is verplicht", + "client-title-max-length": "Titel mag maximaal 100 tekens bevatten", + "advanced-settings": "Geavanceerde instellingen", + "domain-details": "Domeindetails", + "no-domains": "Geen domeinen gevonden", + "search-domains": "Zoek domeinen", + "mobile-app-details": "Details mobiele applicatie", + "add-mobile-app": "Mobiele applicatie toevoegen", + "no-mobile-apps": "Geen mobiele applicaties gevonden", + "search-mobile-apps": "Zoek mobiele applicaties", + "send-token": "Token verzenden", + "create-new": "Nieuw aanmaken", + "client-authentication-method": "Authenticatiemethode client", + "client-id": "Client ID", + "client-id-required": "Client ID is verplicht.", + "client-id-max-length": "Client ID mag maximaal 256 tekens bevatten", + "client-secret": "Clientgeheim", + "client-secret-required": "Clientgeheim is verplicht.", + "client-secret-max-length": "Clientgeheim mag maximaal 2049 tekens bevatten", + "custom-setting": "Aangepaste instellingen", + "customer-name-pattern": "Naam patroon klant", + "customer-name-pattern-max-length": "Naam patroon klant mag maximaal 256 tekens bevatten", + "default-dashboard-name": "Standaard dashboardnaam", + "default-dashboard-name-max-length": "Standaard dashboardnaam mag maximaal 256 tekens bevatten", + "delete-domain-text": "Let op: na bevestiging worden het domein en alle providergegevens ontoegankelijk.", + "delete-domain-title": "Weet u zeker dat u het domein '{{domainName}}' wilt verwijderen?", + "delete-registration-text": "Let op: na bevestiging worden de gegevens van de provider ontoegankelijk.", + "delete-registration-title": "Weet u zeker dat u de provider '{{name}}' wilt verwijderen?", + "email-attribute-key": "Attribuutsleutel e-mailadres", + "email-attribute-key-required": "Attribuutsleutel e-mailadres is verplicht.", + "email-attribute-key-max-length": "Attribuutsleutel e-mailadres mag maximaal 32 tekens bevatten", + "first-name-attribute-key": "Attribuutsleutel voornaam", + "first-name-attribute-key-max-length": "Attribuutsleutel voornaam mag maximaal 32 tekens bevatten", + "general": "Algemeen", + "jwk-set-uri": "JSON Web Key URI", + "last-name-attribute-key": "Attribuutsleutel achternaam", + "last-name-attribute-key-max-length": "Attribuutsleutel achternaam mag maximaal 32 tekens bevatten", + "login-button-icon": "Pictogram login-knop", + "login-button-label": "Providerlabel", + "login-button-label-placeholder": "Inloggen met $(Provider label)", + "login-button-label-required": "Label is verplicht.", + "login-provider": "Loginprovider", + "mapper": "Mapper", + "new-domain": "Nieuw domein", + "oauth2": "OAuth 2.0", + "password-max-length": "Wachtwoord mag maximaal 256 tekens bevatten", + "redirect-uri-template": "Redirect URI-sjabloon", + "copy-redirect-uri": "Redirect URI kopiëren", + "registration-id": "Registratie-ID", + "registration-id-required": "Registratie-ID is verplicht.", + "registration-id-unique": "Registratie-ID moet uniek zijn binnen het systeem.", + "scope": "Scope", + "scope-required": "Scope is verplicht.", + "tenant-name-pattern": "Naam patroon tenant", + "tenant-name-pattern-required": "Naam patroon tenant is verplicht.", + "tenant-name-pattern-max-length": "Naam patroon tenant mag maximaal 256 tekens bevatten", + "tenant-name-strategy": "Naamstrategie tenant", + "type": "Mappertype", + "uri-pattern-error": "Ongeldig URI-formaat.", + "url": "URL", + "url-pattern": "Ongeldig URL-formaat.", + "url-required": "URL is verplicht.", + "url-max-length": "URL mag maximaal 256 tekens bevatten", + "user-info-uri": "Gebruikersinformatie URI", + "user-info-uri-required": "Gebruikersinformatie URI is verplicht.", + "username-max-length": "Gebruikersnaam mag maximaal 256 tekens bevatten", + "user-name-attribute-name": "Attribuutsleutel gebruikersnaam", + "user-name-attribute-name-required": "Attribuutsleutel gebruikersnaam is verplicht", + "protocol": "Protocol", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "OAuth 2.0-instellingen inschakelen", + "disable": "OAuth 2.0-instellingen uitschakelen", + "edge": "Naar Edge propageren", + "edge-enable": "Propagatie naar Edge inschakelen", + "edge-disable": "Propagatie naar Edge uitschakelen", + "domains": "Domeinen", + "mobile-apps": "Mobiele applicaties", + "mobile-package": "Applicatiepakket", + "mobile-package-placeholder": "Bijv.: my.example.app", + "mobile-package-hint": "Voor Android: uw unieke Applicatie-ID. Voor iOS: Product bundle identifier.", + "mobile-package-unique": "Applicatiepakket moet uniek zijn.", + "mobile-package-required": "Applicatiepakket is verplicht.", + "mobile-package-max-length": "Applicatiepakket mag maximaal 256 tekens bevatten", + "mobile-package-spaces": "Applicatiepakket mag geen spaties bevatten", + "mobile-app-secret": "Applicatiegeheim", + "mobile-app-secret-hint": "Base64-gecodeerde tekenreeks van ten minste 512 bits.", + "mobile-app-secret-required": "Applicatiegeheim is verplicht.", + "mobile-app-secret-min-length": "Applicatiegeheim moet ten minste 512 bits zijn.", + "mobile-app-secret-base64": "Applicatiegeheim moet in base64-formaat zijn.", + "invalid-mobile-app-secret": "Applicatiegeheim mag alleen alfanumerieke tekens bevatten en moet tussen 16 en 2048 tekens lang zijn.", + "copy-mobile-app-secret": "Applicatiegeheim kopiëren", + "delete-mobile-app": "Applicatiegegevens verwijderen", + "providers": "Providers", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Alle platforms", + "smtp-provider": "SMTP-provider", + "allowed-platforms": "Toegestane platforms", + "authentication": "Authenticatie", + "basic": "Basis", + "provider": "Provider", + "redirect-url": "Redirect URI", + "domain-name": "Domeinnaam", + "domain-name-required": "Domeinnaam is verplicht", + "redirect-url-template": "Redirect URI-sjabloon", + "microsoft-tenant-id": "Directory (tenant) ID", + "microsoft-tenant-id-required": "Directory (tenant) ID is verplicht", + "token-uri": "Token URI", + "token-uri-required": "Token URI is verplicht", + "redirect-uri": "Redirect URI", + "google-provider": "Google", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "Aangepast", + "generate-access-token": "Access token genereren", + "update-access-token": "Access token bijwerken", + "access-token-status": "Status access token:", + "token-status-generated": "gegenereerd", + "token-status-not-generated": "niet gegenereerd" + }, + "smpp-provider": { + "smpp-version": "SMPP-versie", + "smpp-host": "SMPP-host", + "smpp-host-required": "SMPP-host is verplicht", + "smpp-port": "SMPP-poort", + "smpp-port-required": "SMPP-poort is verplicht", + "system-id": "Systeem-ID", + "system-id-required": "Systeem-ID is verplicht", + "password": "Wachtwoord", + "password-required": "Wachtwoord is verplicht", + "type-settings": "Type-instellingen", + "source-settings": "Broninstellingen", + "destination-settings": "Bestemmingsinstellingen", + "additional-settings": "Aanvullende instellingen", + "system-type": "Systeemtype", + "bind-type": "Bindtype", + "service-type": "Servicetype", + "source-address": "Bronadres", + "source-ton": "Bron-TON", + "source-npi": "Bron-NPI", + "destination-ton": "Bestemming TON (Type of Number)", + "destination-npi": "Bestemming NPI (Numbering Plan Identification)", + "address-range": "Adresbereik", + "coding-scheme": "Coderingstype", + "bind-type-tx": "Zender", + "bind-type-rx": "Ontvanger", + "bind-type-trx": "Zender/Ontvanger", + "ton-unknown": "Onbekend", + "ton-international": "Internationaal", + "ton-national": "Nationaal", + "ton-network-specific": "Netwerkspecifiek", + "ton-subscriber-number": "Abonneenummer", + "ton-alphanumeric": "Alfanumeriek", + "ton-abbreviated": "Afgekort", + "npi-unknown": "0 - Onbekend", + "npi-isdn": "1 - ISDN/telefoonnummerplan (E163/E164)", + "npi-data-numbering-plan": "3 - Data-nummerplan (X.121)", + "npi-telex-numbering-plan": "4 - Telex-nummerplan (F.69)", + "npi-land-mobile": "6 - Mobiele landtelefoon (E.212)", + "npi-national-numbering-plan": "8 - Nationaal nummerplan", + "npi-private-numbering-plan": "9 - Privénummerplan", + "npi-ermes-numbering-plan": "10 - ERMES-nummerplan (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internet (IP)", + "npi-wap-client-id": "18 - WAP-client-ID (te definiëren door WAP Forum)", + "scheme-smsc": "0 - SMSC Standaardalfabet (ASCII voor korte/lange code en GSM voor gratis nummers)", + "scheme-ia5": "1 - IA5 (ASCII voor korte/lange code, Latin 9 voor gratis nummers (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Octet Ongespecificeerd (8-bit binair)", + "scheme-latin-1": "3 - Latijn 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Octet Ongespecificeerd (8-bit binair)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Cyrillisch (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latijns/Hebreeuws (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Pictogramcodering", + "scheme-music-codes": "10 - Muziekcodes (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Uitgebreid Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Koreaanse grafische tekenset (KS C 5601/KS X 1001)" + }, + "queue-select-name": "Selecteer wachtrijnaam", + "queue-name": "Naam", + "queue-name-required": "Wachtrijnaam is verplicht!", + "queues": "Wachtrijen", + "queue-partitions": "Partities", + "queue-submit-strategy": "Indienstrategie", + "queue-processing-strategy": "Verwerkingsstrategie", + "queue-configuration": "Wachtrijconfiguratie", + "repository-settings": "Repository-instellingen", + "repository": "Repository", + "repository-url": "Repository-URL", + "repository-url-required": "Repository-URL is verplicht.", + "default-branch": "Standaard branchnaam", + "repository-read-only": "Alleen-lezen", + "show-merge-commits": "Merge commits tonen", + "authentication-settings": "Authenticatie-instellingen", + "auth-method": "Authenticatiemethode", + "auth-method-username-password": "Wachtwoord / access token", + "auth-method-username-password-hint": "GitHub-gebruikers moeten gebruik maken van tokens met schrijfrechten op de repository.", + "auth-method-private-key": "Privésleutel", + "password-access-token": "Wachtwoord / access token", + "change-password-access-token": "Wachtwoord / access token wijzigen", + "private-key": "Privésleutel", + "drop-private-key-file-or": "Sleep een privésleutelbestand hierheen of", + "passphrase": "Wachtwoordzin", + "enter-passphrase": "Voer wachtwoordzin in", + "change-passphrase": "Wachtwoordzin wijzigen", + "check-access": "Toegang controleren", + "check-repository-access-success": "Toegang tot repository is succesvol geverifieerd!", + "delete-repository-settings-title": "Weet u zeker dat u de repository-instellingen wilt verwijderen?", + "delete-repository-settings-text": "Let op: na bevestiging worden de instellingen verwijderd en is versiebeheer niet meer beschikbaar.", + "auto-commit-settings": "Instellingen automatisch vastleggen", + "auto-commit": "Automatisch vastleggen", + "auto-commit-entities": "Entiteiten voor automatisch vastleggen", + "no-auto-commit-entities-prompt": "Geen entiteiten geconfigureerd voor automatisch vastleggen", + "delete-auto-commit-settings-title": "Weet u zeker dat u de instellingen voor automatisch vastleggen wilt verwijderen?", + "delete-auto-commit-settings-text": "Let op: na bevestiging worden de instellingen verwijderd en wordt automatisch vastleggen voor alle entiteiten uitgeschakeld.", + "mobile-app": { + "mobile-app": "Mobiele app", + "mobile-app-qr-code-widget-settings": "Mobiele app QR-code widget instellingen", + "applications": "Applicaties", + "default": "Standaard", + "custom": "Aangepast", + "android": "Android", + "ios": "iOS", + "appearance": "Uiterlijk", + "appearance-on-home-page": "Weergave op startpagina", + "enabled": "Ingeschakeld", + "disabled": "Uitgeschakeld", + "badges": "Badges", + "label": "Label", + "label-required": "Label is verplicht", + "label-max-length": "Label mag maximaal 50 tekens bevatten", + "right": "Rechts", + "left": "Links", + "set": "Instellen", + "preview": "Voorbeeld", + "connect-mobile-app": "Verbind mobiele app", + "use-system-settings": "Gebruik systeeminstellingen" + }, + "2fa": { + "2fa": "Twee-factor-authenticatie", + "available-providers": "Beschikbare providers", + "issuer-name": "Naam uitgever", + "issuer-name-required": "Naam uitgever is verplicht.", + "max-verification-failures-before-user-lockout": "Maximaal aantal verificatiefouten vóór accountvergrendeling", + "max-verification-failures-before-user-lockout-pattern": "Maximaal aantal fouten moet een positief geheel getal zijn.", + "number-of-checking-attempts": "Aantal controlepogingen", + "number-of-checking-attempts-pattern": "Aantal pogingen moet een positief geheel getal zijn.", + "number-of-checking-attempts-required": "Aantal controlepogingen is verplicht.", + "number-of-codes": "Aantal codes", + "number-of-codes-pattern": "Aantal codes moet een positief geheel getal zijn.", + "number-of-codes-required": "Aantal codes is verplicht.", + "provider": "Provider", + "retry-verification-code-period": "Herhaalperiode verificatiecode (sec)", + "retry-verification-code-period-pattern": "Minimale periode is 5 seconden", + "retry-verification-code-period-required": "Herhaalperiode is verplicht.", + "total-allowed-time-for-verification": "Totale toegestane verificatietijd (sec)", + "total-allowed-time-for-verification-pattern": "Minimale toegestane tijd is 60 seconden", + "total-allowed-time-for-verification-required": "Totale verificatietijd is verplicht.", + "use-system-two-factor-auth-settings": "Gebruik systeeminstellingen voor twee-factor-authenticatie", + "verification-code-check-rate-limit": "Beperkingssnelheid controle verificatiecode", + "verification-code-lifetime": "Levensduur verificatiecode (sec)", + "verification-code-lifetime-pattern": "Levensduur moet een positief geheel getal zijn.", + "verification-code-lifetime-required": "Levensduur van verificatiecode is verplicht.", + "verification-message-template": "Sjabloon verificatiebericht", + "verification-limitations": "Verificatiebeperkingen", + "verification-message-template-pattern": "Verificatiebericht moet patroon bevatten: ${code}", + "verification-message-template-required": "Sjabloon verificatiebericht is verplicht.", + "within-time": "Binnen tijd (sec)", + "within-time-pattern": "Tijd moet een positief geheel getal zijn.", + "within-time-required": "Tijd is verplicht." + }, + "jwt": { + "security-settings": "JWT-beveiligingsinstellingen", + "issuer-name": "Naam uitgever", + "issuer-name-required": "Naam uitgever is verplicht.", + "signings-key": "Ondertekeningssleutel", + "signings-key-hint": "Base64-gecodeerde tekenreeks die ten minste 512 bits vertegenwoordigt.", + "signings-key-required": "Ondertekeningssleutel is verplicht.", + "signings-key-min-length": "Ondertekeningssleutel moet minstens 512 bits zijn.", + "signings-key-base64": "Ondertekeningssleutel moet in base64-formaat zijn.", + "expiration-time": "Verlooptijd token (sec)", + "expiration-time-required": "Verlooptijd token is verplicht.", + "expiration-time-max": "Maximaal toegestane tijd is 2147483647 seconden (68 jaar).", + "expiration-time-min": "Minimale tijd is 60 seconden (1 minuut).", + "refresh-expiration-time": "Verlooptijd vernieuwingstoken (sec)", + "refresh-expiration-time-required": "Verlooptijd vernieuwingstoken is verplicht.", + "refresh-expiration-time-max": "Maximaal toegestane tijd is 2147483647 seconden (68 jaar).", + "refresh-expiration-time-min": "Minimale tijd is 900 seconden (15 minuten).", + "refresh-expiration-time-less-token": "Vernieuwingstoken moet een langere duur hebben dan access token.", + "generate-key": "Sleutel genereren", + "info-header": "Alle gebruikers moeten opnieuw inloggen", + "info-message": "Wijziging van de JWT-ondertekeningssleutel maakt alle uitgegeven tokens ongeldig. Alle gebruikers moeten opnieuw inloggen. Dit heeft ook invloed op scripts die gebruikmaken van de REST API/Websockets." + }, + "resources": "Bronnen", + "notifications": "Meldingen", + "notifications-settings": "Meldingsinstellingen", + "slack-api-token": "Slack API-token", + "slack": "Slack", + "slack-settings": "Slack-instellingen", + "mobile-settings": "Mobiele instellingen", + "firebase-service-account-file": "Firebase serviceaccount referenties JSON-bestand", + "select-firebase-service-account-file": "Sleep uw Firebase serviceaccountbestand hierheen of " + }, + "alarm": { + "alarm": "Alarm", + "alarms": "Alarmen", + "all-alarms": "Alle alarmen", + "select-alarm": "Selecteer alarm", + "no-alarms-matching": "Geen alarmen gevonden voor '{{entity}}'.", + "alarm-required": "Alarm is verplicht", + "alarm-filter": "Alarmfilter", + "filter": "Filter", + "alarm-status": "Alarmstatus", + "alarm-status-list": "Lijst van alarmstatussen", + "any-status": "Elke status", + "search-status": { + "ANY": "Elke", + "ACTIVE": "Actief", + "CLEARED": "Gezuiverd", + "ACK": "Bevestigd", + "UNACK": "Niet bevestigd" + }, + "display-status": { + "ACTIVE_UNACK": "Actief niet bevestigd", + "ACTIVE_ACK": "Actief bevestigd", + "CLEARED_UNACK": "Gezuiverd niet bevestigd", + "CLEARED_ACK": "Gezuiverd bevestigd" + }, + "no-alarms-prompt": "Geen alarmen gevonden", + "created-time": "Aangemaakt op", + "type": "Type", + "severity": "Ernst", + "originator": "Oorsprong", + "originator-type": "Type oorsprong", + "details": "Details", + "originator-label": "Label oorsprong", + "assign": "Toewijzen", + "assignments": "Toewijzingen", + "assignee": "Toegewezen aan", + "assignee-id": "ID van toegewezene", + "assignee-first-name": "Voornaam toegewezene", + "assignee-last-name": "Achternaam toegewezene", + "assignee-email": "E-mail toegewezene", + "unassigned": "Niet toegewezen", + "user-deleted": "Gebruiker verwijderd", + "assignee-not-set": "Alle", + "status": "Status", + "alarm-details": "Alarmdetails", + "start-time": "Starttijd", + "assign-time": "Toewijzingstijd", + "end-time": "Eindtijd", + "ack-time": "Bevestigingstijd", + "clear-time": "Zuiveringstijd", + "duration": "Duur", + "alarm-severity": "Alarmernst", + "alarm-severity-list": "Lijst van alarmernst", + "any-severity": "Elke ernst", + "severity-critical": "Kritiek", + "severity-major": "Groot", + "severity-minor": "Klein", + "severity-warning": "Waarschuwing", + "severity-indeterminate": "Onbepaald", + "acknowledge": "Bevestigen", + "clear": "Zuiveren", + "delete": "Verwijderen", + "search": "Alarmen zoeken", + "selected-alarms": "{ count, plural, =1 {1 alarm} other {# alarmen} } geselecteerd", + "no-data": "Geen gegevens om weer te geven", + "polling-interval": "Polling-interval voor alarmen (sec)", + "polling-interval-required": "Polling-interval voor alarmen is verplicht.", + "min-polling-interval-message": "Minimaal toegestaan polling-interval is 1 seconde.", + "aknowledge-alarms-title": "{ count, plural, =1 {1 alarm bevestigen} other {# alarmen bevestigen} }", + "aknowledge-alarms-text": "Weet u zeker dat u { count, plural, =1 {1 alarm} other {# alarmen} } wilt bevestigen?", + "aknowledge-alarm-title": "Alarm bevestigen", + "aknowledge-alarm-text": "Weet u zeker dat u het alarm wilt bevestigen?", + "selected-alarms-are-acknowledged": "Geselecteerde alarmen zijn al bevestigd", + "clear-alarms-title": "{ count, plural, =1 {1 alarm zuiveren} other {# alarmen zuiveren} }", + "clear-alarms-text": "Weet u zeker dat u { count, plural, =1 {1 alarm} other {# alarmen} } wilt zuiveren?", + "clear-alarm-title": "Alarm zuiveren", + "clear-alarm-text": "Weet u zeker dat u het alarm wilt zuiveren?", + "delete-alarms-title": "{ count, plural, =1 {1 alarm verwijderen} other {# alarmen verwijderen} }", + "delete-alarms-text": "Weet u zeker dat u { count, plural, =1 {1 alarm} other {# alarmen} } wilt verwijderen?", + "selected-alarms-are-cleared": "Geselecteerde alarmen zijn al gezuiverd", + "alarm-status-filter": "Alarmstatusfilter", + "alarm-filter-title": "Alarmfilter", + "assigned": "Toegewezen", + "filter-title": "Filter", + "max-count-load": "Maximaal aantal te laden alarmen (0 - onbeperkt)", + "max-count-load-required": "Maximaal aantal te laden alarmen is verplicht.", + "max-count-load-error-min": "Minimale waarde is 0.", + "fetch-size": "Fetch-grootte", + "fetch-size-required": "Fetch-grootte is verplicht.", + "fetch-size-error-min": "Minimale waarde is 10.", + "alarm-types": "Alarmtypen", + "alarm-type-list": "Lijst van alarmtypen", + "any-type": "Elk type", + "assigned-to-current-user": "Toegewezen aan huidige gebruiker", + "assigned-to-me": "Toegewezen aan mij", + "search-propagated-alarms": "Gepropageerde alarmen zoeken", + "comments": "Alarmopmerkingen", + "show-more": "Meer weergeven", + "additional-info": "Aanvullende info", + "alarm-type": "Alarmtype", + "enter-alarm-type": "Voer alarmtype in", + "no-alarm-types-matching": "Geen overeenkomende alarmtypen gevonden voor '{{entitySubtype}}'.", + "alarm-type-list-empty": "Geen alarmtypen geselecteerd." + }, + "alarm-activity": { + "add": "Voeg een opmerking toe...", + "alarm-comment": "Alarmopmerking", + "comments": "Opmerkingen", + "delete-alarm-comment": "Wilt u deze opmerking verwijderen?", + "refresh": "Vernieuwen", + "oldest-first": "Oudste eerst", + "newest-first": "Nieuwste eerst", + "activity": "Activiteit", + "export": "Exporteren naar CSV", + "author": "Auteur", + "created-date": "Aanmaakdatum", + "edited-date": "Bewerkingsdatum", + "text": "Tekst", + "system": "Systeem" + }, + "alias": { + "add": "Alias toevoegen", + "edit": "Alias bewerken", + "name": "Aliasnaam", + "name-required": "Aliasnaam is verplicht", + "duplicate-alias": "Er bestaat al een alias met dezelfde naam.", + "filter-type-single-entity": "Enkelvoudige entiteit", + "filter-type-entity-list": "Entiteitenlijst", + "filter-type-entity-name": "Entiteitsnaam", + "filter-type-entity-type": "Entiteitstype", + "filter-type-state-entity": "Entiteit uit dashboardstatus", + "filter-type-state-entity-description": "Entiteit afkomstig uit statusparameters van het dashboard", + "filter-type-asset-type": "Assettype", + "filter-type-asset-type-description": "Assets van het type '{{assetTypes}}'", + "filter-type-asset-type-and-name-description": "Assets van het type '{{assetTypes}}' met naam die begint met '{{prefix}}'", + "filter-type-device-type": "Apparaattype", + "filter-type-device-type-description": "Apparaten van het type '{{deviceTypes}}'", + "filter-type-device-type-and-name-description": "Apparaten van het type '{{deviceTypes}}' met naam die begint met '{{prefix}}'", + "filter-type-entity-view-type": "Type entiteitsoverzicht", + "filter-type-entity-view-type-description": "Entiteitsoverzichten van het type '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description": "Entiteitsoverzichten van het type '{{entityViewTypes}}' met naam die begint met '{{prefix}}'", + "filter-type-edge-type": "Edge-type", + "filter-type-edge-type-description": "Edges van het type '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description": "Edges van het type '{{edgeTypes}}' met naam die begint met '{{prefix}}'", + "filter-type-relations-query": "Relatiequery", + "filter-type-relations-query-description": "{{entities}} die een {{relationType}}-relatie {{direction}} {{rootEntity}} hebben", + "filter-type-edge-search-query": "Edge-zoekquery", + "filter-type-edge-search-query-description": "Edges met typen {{edgeTypes}} die een {{relationType}}-relatie {{direction}} {{rootEntity}} hebben", + "filter-type-asset-search-query": "Asset zoekquery", + "filter-type-asset-search-query-description": "Assets met typen {{assetTypes}} die een {{relationType}}-relatie {{direction}} {{rootEntity}} hebben", + "filter-type-device-search-query": "Apparaat zoekquery", + "filter-type-device-search-query-description": "Apparaten met typen {{deviceTypes}} die een {{relationType}}-relatie {{direction}} {{rootEntity}} hebben", + "filter-type-entity-view-search-query": "Entiteitsoverzicht zoekquery", + "filter-type-entity-view-search-query-description": "Entiteitsoverzichten met typen {{entityViewTypes}} die een {{relationType}}-relatie {{direction}} {{rootEntity}} hebben", + "filter-type-apiUsageState": "API-gebruiksstatus", + "entity-filter": "Entiteitenfilter", + "resolve-multiple": "Oplossen als meerdere entiteiten", + "resolve-multiple-hint": "Inschakelen om gegevens van alle gefilterde entiteiten tegelijkertijd weer te geven.\nAls uitgeschakeld, toont de widget alleen gegevens van de geselecteerde entiteit.", + "filter-type": "Filtertype", + "filter-type-required": "Filtertype is verplicht.", + "entity-filter-no-entity-matched": "Geen entiteiten gevonden die overeenkomen met het opgegeven filter.", + "no-entity-filter-specified": "Geen entiteitenfilter opgegeven", + "root-state-entity": "Gebruik dashboardstatus-entiteit als root", + "last-level-relation": "Alleen laatste relatieniveau ophalen", + "root-entity": "Root-entiteit", + "state-entity-parameter-name": "Parameternaam status-entiteit", + "default-state-entity": "Standaard status-entiteit", + "default-entity-parameter-name": "Standaard", + "max-relation-level": "Maximaal relatieniveau", + "unlimited-level": "Ongelimiteerd niveau", + "state-entity": "Dashboardstatus-entiteit", + "all-entities": "Alle entiteiten", + "any-relation": "elke" + }, + "asset": { + "asset": "Asset", + "assets": "Assets", + "management": "Assetbeheer", + "view-assets": "Assets bekijken", + "add": "Asset toevoegen", + "asset-type-max-length": "Assettype mag maximaal 256 tekens bevatten", + "assign-to-customer": "Toewijzen aan klant", + "assign-asset-to-customer": "Asset(s) aan klant toewijzen", + "assign-asset-to-customer-text": "Selecteer de assets die u aan de klant wilt toewijzen", + "no-assets-text": "Geen assets gevonden", + "assign-to-customer-text": "Selecteer de klant waaraan u de asset(s) wilt toewijzen", + "public": "Openbaar", + "assignedToCustomer": "Toegewezen aan klant", + "make-public": "Asset openbaar maken", + "make-private": "Asset privé maken", + "unassign-from-customer": "Toewijzing aan klant ongedaan maken", + "delete": "Asset verwijderen", + "asset-public": "Asset is openbaar", + "asset-type": "Assettype", + "asset-type-required": "Assettype is verplicht.", + "select-asset-type": "Selecteer assettype", + "enter-asset-type": "Voer assetprofiel in", + "any-asset": "Elke asset", + "no-asset-types-matching": "Geen overeenkomende assettypen gevonden voor '{{entitySubtype}}'.", + "asset-type-list-empty": "Geen assettypen geselecteerd.", + "asset-types": "Assettypen", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam mag maximaal 256 tekens bevatten", + "label-max-length": "Label mag maximaal 256 tekens bevatten", + "description": "Beschrijving", + "type": "Type", + "type-required": "Type is verplicht.", + "details": "Details", + "events": "Gebeurtenissen", + "add-asset-text": "Nieuwe asset toevoegen", + "asset-details": "Assetdetails", + "assign-assets": "Assets toewijzen", + "assign-assets-text": "{ count, plural, =1 {1 asset} other {# assets} } aan klant toewijzen", + "assign-asset-to-edge-title": "Asset(s) toewijzen aan Edge", + "assign-asset-to-edge-text": "Selecteer de assets die u aan de Edge wilt toewijzen", + "delete-assets": "Assets verwijderen", + "unassign-assets": "Assets ongedaan maken", + "unassign-assets-action-title": "{ count, plural, =1 {1 asset} other {# assets} } toewijzing aan klant ongedaan maken", + "assign-new-asset": "Nieuwe asset toewijzen", + "delete-asset-title": "Weet u zeker dat u de asset '{{assetName}}' wilt verwijderen?", + "delete-asset-text": "Let op: na bevestiging worden de asset en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-assets-title": "Weet u zeker dat u { count, plural, =1 {1 asset} other {# assets} } wilt verwijderen?", + "delete-assets-action-title": "{ count, plural, =1 {1 asset verwijderen} other {# assets verwijderen} }", + "delete-assets-text": "Let op: na bevestiging worden alle geselecteerde assets verwijderd en alle gerelateerde gegevens onherstelbaar.", + "make-public-asset-title": "Weet u zeker dat u de asset '{{assetName}}' openbaar wilt maken?", + "make-public-asset-text": "Na bevestiging wordt de asset en alle bijbehorende gegevens openbaar en toegankelijk voor anderen.", + "make-private-asset-title": "Weet u zeker dat u de asset '{{assetName}}' privé wilt maken?", + "make-private-asset-text": "Na bevestiging wordt de asset privé en niet meer toegankelijk voor anderen.", + "unassign-asset-title": "Weet u zeker dat u de asset '{{assetName}}' wilt onttrekken?", + "unassign-asset-text": "Na bevestiging wordt de asset ontkoppeld en is deze niet meer toegankelijk voor de klant.", + "unassign-asset": "Asset ontkoppelen", + "unassign-assets-title": "Weet u zeker dat u { count, plural, =1 {1 asset} other {# assets} } wilt onttrekken?", + "unassign-assets-text": "Na bevestiging worden alle geselecteerde assets ontkoppeld en zijn niet meer toegankelijk voor de klant.", + "copyId": "Asset-ID kopiëren", + "idCopiedMessage": "Asset-ID is gekopieerd naar klembord", + "select-asset": "Selecteer asset", + "no-assets-matching": "Geen overeenkomende assets gevonden voor '{{entity}}'.", + "asset-required": "Asset is verplicht", + "name-starts-with": "Assetnaam-expressie", + "help-text": "Gebruik '%' indien nodig: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search": "Assets zoeken", + "import": "Assets importeren", + "asset-file": "Assetbestand", + "label": "Label", + "assign-asset-to-edge": "Asset(s) toewijzen aan Edge", + "unassign-asset-from-edge": "Asset ontkoppelen", + "unassign-asset-from-edge-title": "Weet u zeker dat u de asset '{{assetName}}' wilt ontkoppelen van Edge?", + "unassign-asset-from-edge-text": "Na bevestiging wordt de asset ontkoppeld en niet meer toegankelijk voor de Edge.", + "unassign-assets-from-edge-title": "Weet u zeker dat u { count, plural, =1 {1 asset} other {# assets} } wilt ontkoppelen?", + "unassign-assets-from-edge-text": "Na bevestiging worden alle geselecteerde assets ontkoppeld en zijn ze niet meer toegankelijk voor de Edge.", + "selected-assets": "{ count, plural, =1 {1 asset} other {# assets} } geselecteerd" + }, + "attribute": { + "attributes": "Attribuuten", + "latest-telemetry": "Laatste telemetrie", + "no-latest-telemetry": "Geen laatste telemetrie", + "attributes-scope": "Bereik van entiteitsattributen", + "scope-telemetry": "Telemetrie", + "scope-latest-telemetry": "Laatste telemetrie", + "scope-client": "Clientattributen", + "scope-server": "Serverattributen", + "scope-shared": "Gedeelde attributen", + "scope-client-short": "Client", + "scope-server-short": "Server", + "scope-shared-short": "Gedeeld", + "scope-latest-short": "Laatste", + "scope-any": "Elke", + "add": "Attribuut toevoegen", + "key": "Sleutel", + "key-max-length": "Sleutel mag maximaal 256 tekens bevatten", + "last-update-time": "Laatste update", + "key-required": "Attribuutsleutel is verplicht.", + "value": "Waarde", + "value-required": "Attribuutwaarde is verplicht.", + "telemetry-key-required": "Telemetriesleutel is verplicht", + "telemetry-value-required": "Telemetrie waarde is verplicht", + "delete-attributes-title": "Weet u zeker dat u { count, plural, =1 {1 attribuut} other {# attributen} } wilt verwijderen?", + "delete-attributes-text": "Let op: na bevestiging worden alle geselecteerde attributen verwijderd.", + "delete-attributes": "Attributen verwijderen", + "enter-attribute-value": "Voer attribuutwaarde in", + "show-on-widget": "Weergeven op widget", + "widget-mode": "Widgetmodus", + "next-widget": "Volgende widget", + "prev-widget": "Vorige widget", + "add-to-dashboard": "Toevoegen aan dashboard", + "add-widget-to-dashboard": "Widget aan dashboard toevoegen", + "selected-attributes": "{ count, plural, =1 {1 attribuut} other {# attributen} } geselecteerd", + "selected-telemetry": "{ count, plural, =1 {1 telemetrie-eenheid} other {# telemetrie-eenheden} } geselecteerd", + "no-attributes-text": "Geen attributen gevonden", + "no-telemetry-text": "Geen telemetrie gevonden", + "copy-key": "Sleutel kopiëren", + "add-telemetry": "Telemetrie toevoegen", + "copy-value": "Waarde kopiëren", + "delete-timeseries": { + "start-time": "Starttijd", + "ends-on": "Eindigt op", + "strategy": "Strategie", + "delete-strategy": "Verwijderstrategie", + "all-data": "Alle gegevens verwijderen", + "all-data-except-latest-value": "Alle gegevens verwijderen behalve laatste waarde", + "latest-value": "Laatste waarde verwijderen", + "all-data-for-time-period": "Alle gegevens voor tijdsperiode verwijderen", + "rewrite-latest-value": "Laatste waarde overschrijven" + } + }, + "api-usage": { + "api-features": "API-functies", + "api-usage": "API-gebruik", + "alarm": "Alarm", + "alarms-created": "Gegenereerde alarmen", + "queue-stats": "Wachtrijstatistieken", + "processing-failures-and-timeouts": "Verwerkingsfouten en time-outs", + "exceptions": "Uitzonderingen", + "alarms-created-daily-activity": "Dagelijkse activiteit gegenereerde alarmen", + "alarms-created-hourly-activity": "Uurlijkse activiteit gegenereerde alarmen", + "alarms-created-monthly-activity": "Maandelijkse activiteit gegenereerde alarmen", + "data-points": "Datapunten", + "data-points-storage-days": "Opslagduur van datapunten (dagen)", + "device-api": "Apparaat-API", + "email": "E-mail", + "email-messages": "E-mailberichten", + "email-messages-daily-activity": "Dagelijkse activiteit e-mailberichten", + "email-messages-monthly-activity": "Maandelijkse activiteit e-mailberichten", + "executions": "Uitvoeringen", + "scripts": "Scripts", + "scripts-hourly-activity": "Uurlijkse activiteit scripts", + "scripts-daily-activity": "Dagelijkse activiteit scripts", + "scripts-monthly-activity": "Maandelijkse activiteit scripts", + "javascript": "JavaScript", + "javascript-executions": "JavaScript-uitvoeringen", + "tbel": "TBEL", + "tbel-executions": "TBEL-uitvoeringen", + "latest-error": "Laatste fout", + "messages": "Berichten", + "notifications": "Meldingen", + "notifications-email-sms": "Meldingen (E-mail/SMS)", + "notifications-hourly-activity": "Uurlijkse meldingsactiviteit", + "permanent-failures": "${entityName} Permanente fouten", + "permanent-timeouts": "${entityName} Permanente time-outs", + "processing-failures": "${entityName} Verwerkingsfouten", + "processing-timeouts": "${entityName} Verwerking time-outs", + "rule-chain": "Rule chain", + "rule-engine": "Rule Engine", + "rule-engine-daily-activity": "Dagelijkse activiteit Rule Engine", + "rule-engine-executions": "Rule Engine-uitvoeringen", + "rule-engine-hourly-activity": "Uurlijkse activiteit Rule Engine", + "rule-engine-monthly-activity": "Maandelijkse activiteit Rule Engine", + "rule-engine-statistics": "Rule Engine-statistieken", + "rule-node": "Rule node", + "sms": "SMS", + "sms-messages": "SMS-berichten", + "sms-messages-daily-activity": "Dagelijkse activiteit SMS-berichten", + "sms-messages-monthly-activity": "Maandelijkse activiteit SMS-berichten", + "successful": "${entityName} Succesvol", + "telemetry": "Telemetrie", + "telemetry-persistence": "Telemetrie-opslag", + "telemetry-persistence-daily-activity": "Dagelijkse activiteit telemetrie-opslag", + "telemetry-persistence-hourly-activity": "Uurlijkse activiteit telemetrie-opslag", + "telemetry-persistence-monthly-activity": "Maandelijkse activiteit telemetrie-opslag", + "transport": "Transport", + "transport-daily-activity": "Dagelijkse activiteit transport", + "transport-data-points": "Transport datapunten", + "transport-hourly-activity": "Uurlijkse activiteit transport", + "transport-messages": "Transportberichten", + "transport-monthly-activity": "Maandelijkse activiteit transport", + "view-details": "Bekijk details", + "view-statistics": "Bekijk statistieken" + }, + "api-limit": { + "cassandra-queries": "Cassandra-query's", + "entity-version-creation": "Versiecreatie van entiteit", + "entity-version-load": "Versieladen van entiteit", + "notification-requests": "Meldingsverzoeken", + "notification-requests-per-rule": "Meldingsverzoeken per regel", + "rest-api-requests": "REST API-verzoeken", + "rest-api-requests-per-customer": "REST API-verzoeken per klant", + "transport-messages": "Transportberichten", + "transport-messages-per-device": "Transportberichten per apparaat", + "transport-messages-per-gateway": "Transportberichten per gateway", + "transport-messages-per-gateway-device": "Transportberichten per gateway-apparaat", + "ws-updates-per-session": "WS-updates per sessie", + "edge-events": "Edge-gebeurtenissen", + "edge-events-per-edge": "Edge-gebeurtenissen per edge", + "edge-uplink-messages": "Edge uplink-berichten", + "edge-uplink-messages-per-edge": "Edge uplink-berichten per edge" + }, + "audit-log": { + "audit": "Audit", + "audit-logs": "Auditlogs", + "timestamp": "Tijdstempel", + "entity-type": "Entiteitstype", + "entity-name": "Entiteitsnaam", + "user": "Gebruiker", + "type": "Type", + "status": "Status", + "details": "Details", + "type-added": "Toegevoegd", + "type-deleted": "Verwijderd", + "type-updated": "Bijgewerkt", + "type-attributes-updated": "Attributen bijgewerkt", + "type-attributes-deleted": "Attributen verwijderd", + "type-rpc-call": "RPC-aanroep", + "type-credentials-updated": "Inloggegevens bijgewerkt", + "type-assigned-to-customer": "Toegewezen aan klant", + "type-unassigned-from-customer": "Ontkoppeld van klant", + "type-assigned-to-edge": "Toegewezen aan Edge", + "type-unassigned-from-edge": "Ontkoppeld van Edge", + "type-activated": "Geactiveerd", + "type-suspended": "Opgeschort", + "type-credentials-read": "Inloggegevens gelezen", + "type-attributes-read": "Attributen gelezen", + "type-relation-add-or-update": "Relatie bijgewerkt", + "type-relation-delete": "Relatie verwijderd", + "type-relations-delete": "Alle relaties verwijderd", + "type-alarm-ack": "Alarm bevestigd", + "type-alarm-clear": "Alarm gewist", + "type-alarm-delete": "Alarm verwijderd", + "type-alarm-assign": "Alarm toegewezen", + "type-alarm-unassign": "Alarm ontkoppeld", + "type-added-comment": "Opmerking toegevoegd", + "type-updated-comment": "Opmerking bijgewerkt", + "type-deleted-comment": "Opmerking verwijderd", + "type-login": "Inloggen", + "type-logout": "Uitloggen", + "type-lockout": "Geblokkeerd", + "status-success": "Succes", + "status-failure": "Mislukt", + "audit-log-details": "Details auditlogboek", + "no-audit-logs-prompt": "Geen logs gevonden", + "action-data": "Actiegegevens", + "failure-details": "Foutdetails", + "search": "Auditlogs zoeken", + "clear-search": "Zoekopdracht wissen", + "type-assigned-from-tenant": "Toegewezen vanuit tenant", + "type-assigned-to-tenant": "Toegewezen aan tenant", + "type-provision-success": "Apparaat geprovisioneerd", + "type-provision-failure": "Provisioning apparaat mislukt", + "type-timeseries-updated": "Telemetrie bijgewerkt", + "type-timeseries-deleted": "Telemetrie verwijderd", + "type-sms-sent": "SMS verzonden" + }, + "debug-settings": { + "label": "Debugconfiguratie", + "on-failure": "Alleen fouten (24/7)", + "all-messages": "Alle berichten ({{time}})", + "failures": "Fouten", + "entity": "entiteit", + "hint": { + "main-limited": "Er worden niet meer dan {{msg}} {{entity}} debugberichten per {{time}} opgeslagen.", + "on-failure": "Alleen foutmeldingen loggen.", + "all-messages": "Alle debugberichten loggen." + } + }, + "calculated-fields": { + "expression": "Expressie", + "no-found": "Geen berekende velden gevonden", + "list": "{ count, plural, =1 {Eén berekend veld} other {Lijst van # berekende velden} }", + "selected-fields": "{ count, plural, =1 {1 berekend veld} other {# berekende velden} } geselecteerd", + "type": { + "simple": "Eenvoudig", + "script": "Script" + }, + "arguments": "Argumenten", + "decimals-by-default": "Standaard aantal decimalen", + "debugging": "Debuggen berekend veld", + "argument-name": "Argumentnaam", + "datasource": "Databron", + "add-argument": "Argument toevoegen", + "test-script-function": "Scriptfunctie testen", + "no-arguments": "Geen argumenten geconfigureerd", + "argument-settings": "Argumentinstellingen", + "argument-current": "Huidige entiteit", + "argument-current-tenant": "Huidige tenant", + "argument-device": "Apparaat", + "argument-asset": "Asset", + "argument-customer": "Klant", + "argument-tenant": "Huidige tenant", + "argument-type": "Argumenttype", + "see-debug-events": "Debuggebeurtenissen bekijken", + "attribute": "Attribuut", + "copy-argument-name": "Argumentnaam kopiëren", + "timeseries-key": "Tijdreeks-sleutel", + "device-name": "Apparaatnaam", + "latest-telemetry": "Laatste telemetrie", + "rolling": "Tijdreeks rolling", + "attribute-scope": "Attributenbereik", + "server-attributes": "Serverattributen", + "client-attributes": "Clientattributen", + "shared-attributes": "Gedeelde attributen", + "attribute-key": "Attribuutsleutel", + "default-value": "Standaardwaarde", + "limit": "Maximale waarden", + "time-window": "Tijdsvenster", + "customer-name": "Klantnaam", + "asset-name": "Assetnaam", + "timeseries": "Tijdreeks", + "output": "Uitvoer", + "create": "Nieuw berekend veld aanmaken", + "file": "Berekend veld-bestand", + "invalid-file-error": "Ongeldig bestandsformaat. Zorg ervoor dat het een geldig JSON-bestand is.", + "import": "Berekend veld importeren", + "export": "Berekend veld exporteren", + "export-failed-error": "Kan berekend veld niet exporteren: {{error}}", + "output-type": "Uitvoertype", + "delete-title": "Weet u zeker dat u het berekend veld '{{title}}' wilt verwijderen?", + "delete-text": "Let op: na bevestiging worden het berekend veld en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-multiple-title": "Weet u zeker dat u { count, plural, =1 {1 berekend veld} other {# berekende velden} } wilt verwijderen?", + "delete-multiple-text": "Let op: na bevestiging worden alle geselecteerde berekende velden verwijderd en zijn de gegevens onherstelbaar.", + "test-with-this-message": "Test met dit bericht", + "hint": { + "arguments-simple-with-rolling": "Berekend veld van het type eenvoudig mag geen sleutels met tijdreeks rolling bevatten.", + "arguments-empty": "Argumenten mogen niet leeg zijn.", + "expression-required": "Expressie is verplicht.", + "expression-invalid": "Expressie is ongeldig", + "expression-max-length": "Lengte van de expressie moet minder dan 255 tekens zijn.", + "argument-name-required": "Argumentnaam is verplicht.", + "argument-name-pattern": "Ongeldige argumentnaam.", + "argument-name-duplicate": "Er bestaat al een argument met deze naam.", + "argument-name-max-length": "Argumentnaam moet minder dan 256 tekens zijn.", + "argument-name-forbidden": "Argumentnaam is gereserveerd en mag niet worden gebruikt.", + "argument-type-required": "Argumenttype is verplicht.", + "max-args": "Maximum aantal argumenten bereikt.", + "decimals-range": "Standaard aantal decimalen moet tussen 0 en 15 liggen.", + "expression": "Standaardexpressie toont hoe je temperatuur converteert van Fahrenheit naar Celsius.", + "arguments-entity-not-found": "Doelentiteit van argument niet gevonden." + } + }, + "confirm-on-exit": { + "message": "U heeft niet-opgeslagen wijzigingen. Weet u zeker dat u deze pagina wilt verlaten?", + "html-message": "U heeft niet-opgeslagen wijzigingen.
Weet u zeker dat u deze pagina wilt verlaten?", + "title": "Niet-opgeslagen wijzigingen" + }, + "contact": { + "country": "Land", + "country-required": "Land is verplicht.", + "city": "Stad", + "state": "Staat / Provincie", + "postal-code": "Postcode", + "postal-code-invalid": "Ongeldig postcodeformaat.", + "address": "Adres", + "address2": "Adresregel 2", + "phone": "Telefoon", + "email": "E-mail", + "no-address": "Geen adres", + "no-country-found": "Geen landen gevonden.", + "no-country-matching": "Geen overeenkomstig land gevonden voor '{{country}}'.", + "state-max-length": "Lengte van staat mag maximaal 256 tekens zijn", + "phone-max-length": "Telefoonnummer mag maximaal 256 tekens zijn", + "city-max-length": "Opgegeven stad mag maximaal 256 tekens zijn" + }, + "common": { + "name": "Naam", + "type": "Type", + "general": "Algemeen", + "username": "Gebruikersnaam", + "password": "Wachtwoord", + "data": "Gegevens", + "timestamp": "Tijdstempel", + "enter-username": "Voer gebruikersnaam in", + "enter-password": "Voer wachtwoord in", + "enter-search": "Voer zoekopdracht in", + "created-time": "Aanmaaktijd", + "disabled": "Uitgeschakeld", + "loading": "Laden...", + "proceed": "Doorgaan", + "open-details-page": "Details bekijken", + "not-found": "Niet gevonden", + "value": "Waarde", + "documentation": "Documentatie", + "time-left": "{{time}} resterend", + "output": "Uitvoer", + "suffix": { + "s": "s", + "ms": "ms" + }, + "hint": { + "name-required": "Naam is verplicht.", + "name-pattern": "Naam is ongeldig.", + "name-max-length": "Naam mag maximaal 256 tekens bevatten.", + "title-required": "Titel is verplicht.", + "title-pattern": "Titel is ongeldig.", + "title-max-length": "Titel mag maximaal 256 tekens bevatten.", + "key-required": "Sleutel is verplicht.", + "key-pattern": "Sleutel is ongeldig.", + "key-max-length": "Sleutel mag maximaal 256 tekens bevatten." + }, + "required-fields": "Verplichte velden ontbreken" + }, + "content-type": { + "json": "Json", + "text": "Tekst", + "binary": "Binair (Base64)" + }, + "color": { + "color": "Kleur" + }, + "customer": { + "customer": "Klant", + "customers": "Klanten", + "management": "Klantbeheer", + "dashboard": "Klantdashboard", + "dashboards": "Klantdashboards", + "devices": "Klantapparaten", + "entity-views": "Klantentiteitsoverzichten", + "assets": "Klantassets", + "public-dashboards": "Openbare dashboards", + "public-devices": "Openbare apparaten", + "public-assets": "Openbare assets", + "public-entity-views": "Openbare entiteitsoverzichten", + "add": "Klant toevoegen", + "delete": "Klant verwijderen", + "manage-customer-users": "Klantgebruikers beheren", + "manage-customer-devices": "Klantapparaten beheren", + "manage-customer-dashboards": "Klantdashboards beheren", + "manage-public-devices": "Openbare apparaten beheren", + "manage-public-dashboards": "Openbare dashboards beheren", + "manage-customer-assets": "Klantassets beheren", + "manage-customer-edges": "Klantedges beheren", + "manage-public-assets": "Openbare assets beheren", + "add-customer-text": "Nieuwe klant toevoegen", + "no-customers-text": "Geen klanten gevonden", + "customer-details": "Klantdetails", + "delete-customer-title": "Weet u zeker dat u de klant '{{customerTitle}}' wilt verwijderen?", + "delete-customer-text": "Let op: na bevestiging worden de klant en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-customers-title": "Weet u zeker dat u { count, plural, =1 {1 klant} other {# klanten} } wilt verwijderen?", + "delete-customers-action-title": "{ count, plural, =1 {1 klant verwijderen} other {# klanten verwijderen} }", + "delete-customers-text": "Let op: na bevestiging worden alle geselecteerde klanten en hun gegevens onherstelbaar verwijderd.", + "manage-users": "Gebruikers beheren", + "manage-assets": "Assets beheren", + "manage-devices": "Apparaten beheren", + "manage-dashboards": "Dashboards beheren", + "title": "Titel", + "title-required": "Titel is verplicht.", + "title-max-length": "Titel mag maximaal 256 tekens bevatten", + "description": "Beschrijving", + "details": "Details", + "events": "Gebeurtenissen", + "copyId": "Klant-ID kopiëren", + "idCopiedMessage": "Klant-ID is gekopieerd naar klembord", + "select-customer": "Selecteer klant", + "no-customers-matching": "Geen klanten gevonden voor '{{entity}}'.", + "customer-required": "Klant is verplicht", + "select-default-customer": "Selecteer standaardklant", + "default-customer": "Standaardklant", + "default-customer-required": "Standaardklant is vereist om dashboard op tenantniveau te debuggen", + "search": "Klanten zoeken", + "selected-customers": "{ count, plural, =1 {1 klant} other {# klanten} } geselecteerd", + "edges": "Klant edge-instanties", + "manage-edges": "Edges beheren" + }, + "css-size": { + "size-value-required": "Waarde van grootte is verplicht", + "invalid-size-value": "Ongeldige groottewaarde" + }, + "date": { + "last-update-n-ago": "Laatste update N geleden", + "last-update-n-ago-text": "Laatste update {{ agoText }}", + "custom-date": "Aangepaste datum", + "format": "Formaat", + "preview": "Voorbeeld", + "auto": "Auto", + "time-granularity-formats": "Tijdgranulariteit-formaten", + "unit-year": "Jaren", + "unit-month": "Maanden", + "unit-day": "Dagen", + "unit-hour": "Uren", + "unit-minute": "Minuten", + "unit-second": "Seconden", + "unit-millisecond": "Milliseconden" + }, + "datetime": { + "date-from": "Datum vanaf", + "time-from": "Tijd vanaf", + "date-to": "Datum tot", + "time-to": "Tijd tot", + "from": "Van", + "to": "Tot" + }, + "dashboard": { + "dashboard": "Dashboard", + "dashboards": "Dashboards", + "management": "Dashboardbeheer", + "view-dashboards": "Dashboards bekijken", + "add": "Dashboard toevoegen", + "assign-dashboard-to-customer": "Dashboard(s) toewijzen aan klant", + "assign-dashboard-to-customer-text": "Selecteer de dashboards om toe te wijzen aan de klant", + "assign-to-customer-text": "Selecteer de klant om het dashboard toe te wijzen", + "assign-to-customer": "Toewijzen aan klant", + "unassign-from-customer": "Ontkoppelen van klant", + "make-public": "Dashboard openbaar maken", + "make-private": "Dashboard privé maken", + "manage-assigned-customers": "Toegewezen klanten beheren", + "assigned-customers": "Toegewezen klanten", + "assign-to-customers": "Dashboard(s) toewijzen aan klanten", + "assign-to-customers-text": "Selecteer de klanten om dashboard(s) toe te wijzen", + "unassign-from-customers": "Dashboard(s) ontkoppelen van klanten", + "unassign-from-customers-text": "Selecteer de klanten om dashboard(s) te ontkoppelen", + "no-dashboards-text": "Geen dashboards gevonden", + "no-widgets": "Geen widgets geconfigureerd", + "add-widget": "Nieuwe widget toevoegen", + "add-widget-button-text": "Widget toevoegen", + "title": "Titel", + "image": "Dashboardafbeelding", + "mobile-app-settings": "Mobiele app-instellingen", + "mobile-order": "Volgorde in mobiele applicatie", + "mobile-hide": "Verberg dashboard in mobiele applicatie", + "update-image": "Dashboardafbeelding bijwerken", + "take-screenshot": "Schermafbeelding maken", + "select-widget-title": "Selecteer widget", + "select-widget-value": "{{title}}: selecteer widget", + "select-widget-subtitle": "Beschikbare widgettypes", + "delete": "Dashboard verwijderen", + "title-required": "Titel is verplicht.", + "title-max-length": "Titel mag maximaal 256 tekens bevatten", + "description": "Beschrijving", + "details": "Details", + "dashboard-details": "Dashboarddetails", + "add-dashboard-text": "Nieuw dashboard toevoegen", + "assign-dashboards": "Dashboards toewijzen", + "assign-new-dashboard": "Nieuw dashboard toewijzen", + "assign-dashboards-text": "{ count, plural, =1 {1 dashboard} other {# dashboards} } toewijzen aan klanten", + "unassign-dashboards-action-text": "{ count, plural, =1 {1 dashboard} other {# dashboards} } ontkoppelen van klanten", + "delete-dashboards": "Dashboards verwijderen", + "unassign-dashboards": "Dashboards ontkoppelen", + "unassign-dashboards-action-title": "{ count, plural, =1 {1 dashboard} other {# dashboards} } ontkoppelen van klant", + "delete-dashboard-title": "Weet u zeker dat u het dashboard '{{dashboardTitle}}' wilt verwijderen?", + "delete-dashboard-text": "Let op: na bevestiging worden het dashboard en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-dashboards-title": "Weet u zeker dat u { count, plural, =1 {1 dashboard} other {# dashboards} } wilt verwijderen?", + "delete-dashboards-action-title": "{ count, plural, =1 {1 dashboard verwijderen} other {# dashboards verwijderen} }", + "delete-dashboards-text": "Let op: na bevestiging worden alle geselecteerde dashboards en gerelateerde gegevens onherstelbaar verwijderd.", + "unassign-dashboard-title": "Weet u zeker dat u het dashboard '{{dashboardTitle}}' wilt ontkoppelen?", + "unassign-dashboard-text": "Na bevestiging wordt het dashboard ontkoppeld en is het niet langer toegankelijk voor de klant.", + "unassign-dashboard": "Dashboard ontkoppelen", + "unassign-dashboards-title": "Weet u zeker dat u { count, plural, =1 {1 dashboard} other {# dashboards} } wilt ontkoppelen?", + "unassign-dashboards-text": "Na bevestiging worden alle geselecteerde dashboards ontkoppeld en niet langer toegankelijk voor de klant.", + "public-dashboard-title": "Dashboard is nu openbaar", + "public-dashboard-text": "Uw dashboard {{dashboardTitle}} is nu openbaar en bereikbaar via deze link:", + "public-dashboard-notice": "Let op: Vergeet niet om gerelateerde apparaten openbaar te maken om toegang tot gegevens te geven.", + "make-private-dashboard-title": "Weet u zeker dat u het dashboard '{{dashboardTitle}}' privé wilt maken?", + "make-private-dashboard-text": "Na bevestiging wordt het dashboard privé en niet langer toegankelijk voor anderen.", + "make-private-dashboard": "Dashboard privé maken", + "socialshare-text": "'{{dashboardTitle}}' aangedreven door ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' aangedreven door ThingsBoard", + "select-dashboard": "Selecteer dashboard", + "no-dashboards-matching": "Geen dashboards gevonden voor '{{entity}}'.", + "dashboard-required": "Dashboard is verplicht.", + "select-existing": "Bestaand dashboard selecteren", + "create-new": "Nieuw dashboard aanmaken", + "new-dashboard-title": "Titel nieuw dashboard", + "open-dashboard": "Dashboard openen", + "set-background": "Achtergrond instellen", + "background-color": "Achtergrondkleur", + "background-image": "Achtergrondafbeelding", + "background-size-mode": "Formaatmodus achtergrond", + "no-image": "Geen afbeelding geselecteerd", + "empty-image": "Geen afbeelding", + "drop-image": "Sleep een afbeelding hierheen of klik om een bestand te selecteren.", + "maximum-upload-file-size": "Maximale uploadgrootte: {{ size }}", + "cannot-upload-file": "Kan bestand niet uploaden", + "settings": "Instellingen", + "move-all-widgets": "Alle widgets verplaatsen", + "move-by": "Verplaatsen met", + "cols": "kolommen", + "rows": "rijen", + "layout": "Lay-out", + "layout-type-default": "Standaard", + "layout-type-scada": "SCADA", + "layout-type-divider": "Scheiding", + "layout-settings-type": "Lay-outinstellingen: {{ type }} breakpoint", + "columns-count": "Aantal kolommen", + "columns-count-required": "Aantal kolommen is verplicht.", + "min-columns-count-message": "Minimaal toegestane aantal kolommen is 10.", + "max-columns-count-message": "Maximaal toegestane aantal kolommen is 1000.", + "min-layout-width": "Minimale lay-outbreedte", + "columns-suffix": "kolommen", + "widgets-margins": "Marge tussen widgets", + "margin-required": "Margenwaarde is verplicht.", + "min-margin-message": "Minimale waarde is 0.", + "max-margin-message": "Maximale waarde is 50.", + "horizontal-margin": "Horizontale marge", + "horizontal-margin-required": "Horizontale margenwaarde is verplicht.", + "min-horizontal-margin-message": "Minimale horizontale marge is 0.", + "max-horizontal-margin-message": "Maximale horizontale marge is 50.", + "vertical-margin": "Verticale marge", + "vertical-margin-required": "Verticale margenwaarde is verplicht.", + "min-vertical-margin-message": "Minimale verticale marge is 0.", + "max-vertical-margin-message": "Maximale verticale marge is 50.", + "apply-outer-margin": "Marge toepassen aan zijkanten van de lay-out", + "autofill-height": "Automatisch lay-outhoogte vullen", + "mobile-layout": "Mobiele lay-outinstellingen", + "mobile-row-height": "Mobiele rijhoogte", + "mobile-row-height-required": "Mobiele rijhoogte is verplicht.", + "min-mobile-row-height-message": "Minimale mobiele rijhoogte is 5 pixels.", + "max-mobile-row-height-message": "Maximale mobiele rijhoogte is 200 pixels.", + "row-height": "Rijhoogte", + "row-height-required": "Rijhoogte is verplicht.", + "min-row-height-message": "Minimale rijhoogte is 5 pixels.", + "max-row-height-message": "Maximale rijhoogte is 200 pixels.", + "display-first-in-mobile-view": "Eerst tonen in mobiele weergave", + "title-settings": "Titelinstellingen", + "display-title": "Dashboardtitel weergeven", + "title-color": "Titelkleur", + "toolbar-settings": "Werkbalkinstellingen", + "hide-toolbar": "Werkbalk verbergen", + "toolbar-always-open": "Werkbalk altijd geopend houden", + "display-dashboards-selection": "Dashboardsselectie weergeven", + "display-entities-selection": "Entiteitenselectie weergeven", + "display-filters": "Filters weergeven", + "display-dashboard-timewindow": "Tijdvenster weergeven", + "display-dashboard-export": "Exporteren weergeven", + "display-update-dashboard-image": "Dashboardafbeelding bijwerken weergeven", + "dashboard-logo-settings": "Dashboardlogo-instellingen", + "display-dashboard-logo": "Logo weergeven in volledig scherm", + "dashboard-logo-image": "Afbeelding dashboardlogo", + "advanced-settings": "Geavanceerde instellingen", + "dashboard-css": "Dashboard CSS", + "import": "Dashboard importeren", + "export": "Dashboard exporteren", + "export-failed-error": "Kan dashboard niet exporteren: {{error}}", + "export-prompt": "Dashboardafbeeldingen en bronnen insluiten", + "create-new-dashboard": "Nieuw dashboard aanmaken", + "dashboard-file": "Dashboardbestand", + "invalid-dashboard-file-error": "Kan dashboard niet importeren: Ongeldige datastructuur.", + "dashboard-import-missing-aliases-title": "Configureer aliassen die worden gebruikt in geïmporteerd dashboard", + "create-new-widget": "Nieuwe widget aanmaken", + "import-widget": "Widget importeren", + "widget-file": "Widgetbestand", + "invalid-widget-file-error": "Kan widget niet importeren: Ongeldige datastructuur.", + "widget-import-missing-aliases-title": "Configureer aliassen die worden gebruikt in geïmporteerde widget", + "open-toolbar": "Dashboardwerkbalk openen", + "close-toolbar": "Werkbalk sluiten", + "configuration-error": "Configuratiefout", + "alias-resolution-error-title": "Fout in dashboard aliassen", + "invalid-aliases-config": "Kan geen apparaten vinden die overeenkomen met sommige aliasfilters.
Neem contact op met uw beheerder om dit probleem op te lossen.", + "select-devices": "Apparaten selecteren", + "assignedToCustomer": "Toegewezen aan klant", + "assignedToCustomers": "Toegewezen aan klanten", + "public": "Openbaar", + "copyId": "Dashboard-ID kopiëren", + "idCopiedMessage": "Dashboard-ID is gekopieerd naar klembord", + "public-link": "Openbare link", + "copy-public-link": "Openbare link kopiëren", + "public-link-copied-message": "Openbare dashboardlink is gekopieerd naar klembord", + "manage-states": "Dashboardtoestanden beheren", + "states": "Dashboardtoestanden", + "states-short": "Toestanden", + "search-states": "Zoek dashboardtoestanden", + "selected-states": "{ count, plural, =1 {1 dashboardtoestand} other {# dashboardtoestanden} } geselecteerd", + "edit-state": "Dashboardtoestand bewerken", + "delete-state": "Dashboardtoestand verwijderen", + "add-state": "Dashboardtoestand toevoegen", + "no-states-text": "Geen toestanden gevonden", + "state": "Dashboardtoestand", + "state-name": "Naam", + "state-name-required": "Naam van dashboardtoestand is verplicht.", + "state-id": "Toestands-ID", + "state-id-required": "Toestands-ID is verplicht.", + "state-id-exists": "Er bestaat al een dashboardtoestand met dezelfde ID.", + "is-root-state": "Hoofdtoestand", + "delete-state-title": "Dashboardtoestand verwijderen", + "delete-state-text": "Weet u zeker dat u de dashboardtoestand met naam '{{stateName}}' wilt verwijderen?", + "show-details": "Details tonen", + "hide-details": "Details verbergen", + "select-state": "Selecteer doeltoestand", + "state-controller": "Toestandscontroller", + "state-controller-default": "statisch (verouderd)", + "search": "Dashboards zoeken", + "selected-dashboards": "{ count, plural, =1 {1 dashboard} other {# dashboards} } geselecteerd", + "home-dashboard": "Startdashboard", + "home-dashboard-hide-toolbar": "Werkbalk van startdashboard verbergen", + "unassign-dashboard-from-edge-text": "Na bevestiging wordt het dashboard ontkoppeld en is het niet meer toegankelijk voor de edge.", + "unassign-dashboards-from-edge-title": "Weet u zeker dat u { count, plural, =1 {1 dashboard} other {# dashboards} } wilt ontkoppelen?", + "unassign-dashboards-from-edge-text": "Na bevestiging worden alle geselecteerde dashboards ontkoppeld en niet meer toegankelijk voor de edge.", + "assign-dashboard-to-edge": "Dashboard(s) toewijzen aan edge", + "assign-dashboard-to-edge-text": "Selecteer de dashboards om toe te wijzen aan de edge", + "non-existent-dashboard-state-error": "Dashboardtoestand met ID \"{{ stateId }}\" is niet gevonden", + "edit-mode": "Bewerkmodus", + "duplicate-state-action": "Toestand dupliceren", + "breakpoint-value": "Breakpoint ({{ value }})", + "breakpoints-id": { + "default": "Standaard", + "xs": "Mobiel (xs)", + "sm": "Tablet (sm)", + "md": "Laptop (md)", + "lg": "Desktop (lg)", + "xl": "Desktop (xl)" + }, + "view-format-type-grid": "Raster", + "view-format-type-list": "Lijst", + "view-format": "Weergaveformaat" + }, + "datakey": { + "settings": "Instellingen", + "general": "Algemeen", + "advanced": "Geavanceerd", + "key": "Sleutel", + "keys": "Sleutels", + "label": "Label", + "color": "Kleur", + "units": "Symbool weer te geven naast waarde", + "decimals": "Aantal decimalen na de komma", + "data-generation-func": "Functie voor gegevensgeneratie", + "use-data-post-processing-func": "Gebruik functie voor gegevensnabewerking", + "configuration": "Configuratie van gegevenssleutel", + "timeseries": "Tijdreeks", + "attributes": "Attributen", + "entity-field": "Entiteitsveld", + "alarm": "Alarmvelden", + "timeseries-required": "Entiteit tijdreeks is verplicht.", + "timeseries-or-attributes-required": "Tijdreeks/attributen van entiteit zijn vereist.", + "alarm-fields-timeseries-or-attributes-required": "Alarmvelden of entiteit tijdreeks/attributen zijn vereist.", + "maximum-timeseries-or-attributes": "Maximaal { count, plural, =1 {1 tijdreeks/attribuut toegestaan.} other {# tijdreeksen/attributen toegestaan} }", + "alarm-fields-required": "Alarmvelden zijn verplicht.", + "function-types": "Functietypes", + "function-type": "Functietype", + "function-types-required": "Functietypes zijn verplicht.", + "data-keys": "Gegevenssleutels", + "data-key": "Gegevenssleutel", + "data-keys-required": "Gegevenssleutels zijn verplicht.", + "data-key-required": "Gegevenssleutel is verplicht.", + "alarm-keys": "Alarmgegevenssleutels", + "alarm-key": "Alarmgegevenssleutel", + "alarm-key-functions": "Alarm sleutel functies", + "alarm-key-function": "Alarm sleutel functie", + "latest-keys": "Laatste gegevenssleutels", + "latest-key": "Laatste gegevenssleutel", + "latest-key-functions": "Functies voor laatste sleutel", + "latest-key-function": "Functie voor laatste sleutel", + "timeseries-keys": "Tijdreeksgegevenssleutels", + "timeseries-key": "Tijdreeksgegevenssleutel", + "timeseries-key-functions": "Functies voor tijdreekssleutel", + "timeseries-key-function": "Functie voor tijdreekssleutel", + "maximum-function-types": "Maximaal { count, plural, =1 {1 functietype toegestaan.} other {# functietypes toegestaan} }", + "time-description": "tijdstempel van de huidige waarde;", + "value-description": "de huidige waarde;", + "prev-value-description": "resultaat van vorige functieaanroep;", + "time-prev-description": "tijdstempel van vorige waarde;", + "prev-orig-value-description": "oorspronkelijke vorige waarde;", + "aggregation": "Aggregatie", + "aggregation-type-hint-common": "Om prestatie redenen, is aggregatie alleen beschikbaar voor vaste tijdsintervallen zoals \"huidige dag\", \"huidige maand\", enz., niet voor schuivende vensters zoals 'laatste 30 minuten'.", + "aggregation-type-none-hint": "Gebruik laatste waarde.", + "aggregation-type-min-hint": "Zoek minimale waarde binnen geselecteerd tijdvenster.", + "aggregation-type-max-hint": "Zoek maximale waarde binnen geselecteerd tijdvenster.", + "aggregation-type-avg-hint": "Bereken gemiddelde waarde binnen geselecteerd tijdvenster.", + "aggregation-type-sum-hint": "Sommeer alle waarden binnen geselecteerd tijdvenster.", + "aggregation-type-count-hint": "Aantal gegevenspunten binnen geselecteerd tijdvenster.", + "delta-calculation": "Delta-berekening", + "enable-delta-calculation": "Delta-berekening inschakelen", + "enable-delta-calculation-hint": "Bij inschakeling wordt waarde berekend op basis van geaggregeerde gegevens in gekozen tijdsvenster en vergelijkingsperiode. Alleen beschikbaar voor historische vensters.", + "delta-calculation-result": "Resultaat delta-berekening", + "delta-calculation-result-previous-value": "Vorige waarde", + "delta-calculation-result-delta-absolute": "Delta (absoluut)", + "delta-calculation-result-delta-percent": "Delta (procent)", + "source": "Bron", + "latest": "Laatste", + "latest-value": "Laatste waarde", + "delta": "delta", + "percent": "procent", + "absolute": "absoluut" + }, + "datasource": { + "type": "Type gegevensbron", + "name": "Naam", + "label": "Label", + "add-datasource-prompt": "Voeg gegevensbron toe" + }, + "details": { + "details": "Details", + "edit-mode": "Bewerkmodus", + "edit-json": "JSON bewerken", + "toggle-edit-mode": "Schakel bewerkmodus" + }, + "device": { + "device": "Apparaat", + "device-required": "Apparaat is verplicht.", + "devices": "Apparaten", + "management": "Apparaatbeheer", + "view-devices": "Apparaten bekijken", + "device-alias": "Apparaatalias", + "device-type-max-length": "Apparaattype moet minder dan 256 tekens zijn", + "aliases": "Apparaataliasen", + "no-alias-matching": "'{{alias}}' niet gevonden.", + "no-aliases-found": "Geen aliassen gevonden.", + "no-key-matching": "'{{key}}' niet gevonden.", + "no-keys-found": "Geen sleutels gevonden.", + "create-new-alias": "Een nieuwe aanmaken!", + "create-new-key": "Een nieuwe aanmaken!", + "duplicate-alias-error": "Duplicaat alias gevonden '{{alias}}'.
Apparaataliasen moeten uniek zijn binnen het dashboard.", + "configure-alias": "Alias '{{alias}}' configureren", + "no-devices-matching": "Geen apparaten gevonden die overeenkomen met '{{entity}}'.", + "alias": "Alias", + "alias-required": "Apparaatalias is verplicht.", + "remove-alias": "Apparaatalias verwijderen", + "add-alias": "Apparaatalias toevoegen", + "name-starts-with": "Apparaatnaam expressie", + "help-text": "Gebruik '%' indien nodig: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list": "Apparatenlijst", + "use-device-name-filter": "Filter gebruiken", + "device-list-empty": "Geen apparaten geselecteerd.", + "device-name-filter-required": "Apparaatnaamfilter is verplicht.", + "device-name-filter-no-device-matched": "Geen apparaten gevonden die beginnen met '{{device}}'.", + "add": "Apparaat toevoegen", + "assign-to-customer": "Toewijzen aan klant", + "assign-device-to-customer": "Appara(a)t(en) toewijzen aan klant", + "assign-device-to-customer-text": "Selecteer de apparaten om toe te wijzen aan de klant", + "make-public": "Apparaat openbaar maken", + "make-private": "Apparaat privé maken", + "no-devices-text": "Geen apparaten gevonden", + "assign-to-customer-text": "Selecteer de klant om het apparaat toe te wijzen", + "device-details": "Apparaatdetails", + "add-device-text": "Nieuw apparaat toevoegen", + "credentials": "Referenties", + "manage-credentials": "Referenties beheren", + "delete": "Apparaat verwijderen", + "assign-devices": "Apparaten toewijzen", + "assign-devices-text": "{ count, plural, =1 {1 apparaat} other {# apparaten} } toewijzen aan klant", + "delete-devices": "Apparaten verwijderen", + "unassign-from-customer": "Ontkoppelen van klant", + "unassign-devices": "Apparaten ontkoppelen", + "unassign-devices-action-title": "{ count, plural, =1 {1 apparaat} other {# apparaten} } ontkoppelen van klant", + "unassign-device-from-edge-title": "Weet u zeker dat u het apparaat '{{deviceName}}' wilt ontkoppelen?", + "unassign-device-from-edge-text": "Na bevestiging wordt het apparaat ontkoppeld en is het niet meer toegankelijk voor de edge.", + "unassign-devices-from-edge": "Apparaten ontkoppelen van edge", + "assign-new-device": "Nieuw apparaat toewijzen", + "make-public-device-title": "Weet u zeker dat u het apparaat '{{deviceName}}' openbaar wilt maken?", + "make-public-device-text": "Na bevestiging wordt het apparaat en alle bijbehorende gegevens openbaar en toegankelijk voor anderen.", + "make-private-device-title": "Weet u zeker dat u het apparaat '{{deviceName}}' privé wilt maken?", + "make-private-device-text": "Na bevestiging wordt het apparaat en alle bijbehorende gegevens privé en niet toegankelijk voor anderen.", + "view-credentials": "Referenties bekijken", + "delete-device-title": "Weet u zeker dat u het apparaat '{{deviceName}}' wilt verwijderen?", + "delete-device-text": "Let op: na bevestiging wordt het apparaat en alle bijbehorende gegevens onherstelbaar verwijderd.", + "delete-devices-title": "Weet u zeker dat u { count, plural, =1 {1 apparaat} other {# apparaten} } wilt verwijderen?", + "delete-devices-action-title": "{ count, plural, =1 {1 apparaat verwijderen} other {# apparaten verwijderen} }", + "delete-devices-text": "Let op: na bevestiging worden alle geselecteerde apparaten en gegevens onherstelbaar verwijderd.", + "unassign-device-title": "Weet u zeker dat u het apparaat '{{deviceName}}' wilt ontkoppelen?", + "unassign-device-text": "Na bevestiging wordt het apparaat ontkoppeld en is het niet meer toegankelijk voor de klant.", + "unassign-device": "Apparaat ontkoppelen", + "unassign-devices-title": "Weet u zeker dat u { count, plural, =1 {1 apparaat} other {# apparaten} } wilt ontkoppelen?", + "unassign-devices-text": "Na bevestiging worden alle geselecteerde apparaten ontkoppeld en niet meer toegankelijk voor de klant.", + "device-credentials": "Apparaatreferenties", + "loading-device-credentials": "Apparaatreferenties laden...", + "credentials-type": "Type referentie", + "access-token": "Access token", + "access-token-required": "Access token is verplicht.", + "access-token-invalid": "Lengte access token moet tussen 1 en 32 tekens zijn.", + "certificate-pem-format": "Certificaat in PEM-formaat", + "certificate-pem-format-required": "Certificaat is verplicht.", + "copy-access-token": "Access token kopiëren", + "copy-certificate": "Certificaat kopiëren", + "copy-client-id": "Client-ID kopiëren", + "copy-user-name": "Gebruikersnaam kopiëren", + "copy-password": "Wachtwoord kopiëren", + "generate-client-id": "Genereer Client-ID", + "generate-user-name": "Genereer gebruikersnaam", + "generate-password": "Genereer wachtwoord", + "generate-access-token": "Genereer Access Token", + "lwm2m-security-config": { + "identity": "Clientidentiteit", + "identity-required": "Clientidentiteit is verplicht.", + "identity-tooltip": "De PSK-identificatie is een willekeurige identificatie tot 128 bytes volgens [RFC7925]. Moet eerst naar een tekenreeks worden omgezet en vervolgens in UTF-8 worden gecodeerd.", + "client-key": "Clientsleutel", + "client-key-required": "Clientsleutel is verplicht.", + "client-key-tooltip-prk": "RPK publieke sleutel of ID moet voldoen aan [RFC7250] en gecodeerd zijn in Base64-formaat!", + "client-key-tooltip-psk": "PSK-sleutel moet voldoen aan [RFC4279] en in HexDec-formaat zijn: 32, 64 of 128 tekens!", + "endpoint": "Endpoint clientnaam", + "endpoint-required": "Endpoint clientnaam is verplicht.", + "client-public-key": "Publieke client sleutel", + "client-public-key-hint": "Als leeg, wordt vertrouwd certificaat gebruikt", + "client-public-key-tooltip": "X509 publieke sleutel moet DER-gecodeerd X509v3-formaat zijn, EC-algoritme ondersteunen en Base64-gecodeerd zijn!", + "mode": "Beveiligingsconfiguratie-modus", + "client-tab": "Clientbeveiliging", + "client-certificate": "Clientcertificaat", + "bootstrap-tab": "Bootstrap-client", + "bootstrap-server": "Bootstrap-server", + "lwm2m-server": "LwM2M-server", + "client-publicKey-or-id": "Client publieke sleutel of ID", + "client-publicKey-or-id-required": "Client publieke sleutel of ID is verplicht.", + "client-publicKey-or-id-tooltip-psk": "PSK-ID max. 128 bytes volgens [RFC7925], omzetten naar string en UTF-8 coderen.", + "client-publicKey-or-id-tooltip-rpk": "RPK moet [RFC7250] volgen en Base64-gecodeerd zijn!", + "client-publicKey-or-id-tooltip-x509": "X509 moet DER-gecodeerd X509v3 zijn met EC-algoritme en Base64-gecodeerd!", + "client-secret-key": "Clientgeheime sleutel", + "client-secret-key-required": "Clientgeheime sleutel is verplicht.", + "client-secret-key-tooltip-psk": "PSK moet [RFC4279] volgen en HexDec (32, 64, 128 tekens) zijn!", + "client-secret-key-tooltip-prk": "RPK geheime sleutel moet in PKCS_8 (DER, [RFC5958]) zijn en Base64-gecodeerd!", + "client-secret-key-tooltip-x509": "X509 geheime sleutel moet in PKCS_8 (DER, [RFC5958]) zijn en Base64-gecodeerd!" + }, + "client-id": "Client-ID", + "client-id-pattern": "Bevat ongeldig teken.", + "user-name": "Gebruikersnaam", + "user-name-required": "Gebruikersnaam is verplicht.", + "client-id-or-user-name-necessary": "Client-ID en/of gebruikersnaam vereist", + "password": "Wachtwoord", + "secret": "Geheim", + "secret-required": "Geheim is verplicht.", + "device-type": "Apparaatprofiel", + "device-type-required": "Apparaattype is verplicht.", + "select-device-type": "Selecteer apparaattype", + "enter-device-type": "Voer apparaatprofiel in", + "any-device": "Elk apparaat", + "no-device-types-matching": "Geen apparaatprofielen gevonden voor '{{entitySubtype}}'.", + "device-type-list-empty": "Geen apparaatprofielen geselecteerd!", + "device-profile-type-list-empty": "Minstens één apparaatprofiel is vereist.", + "device-types": "Apparaattypes", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet minder dan 256 tekens zijn", + "label-max-length": "Label moet minder dan 256 tekens zijn", + "description": "Beschrijving", + "label": "Label", + "events": "Gebeurtenissen", + "details": "Details", + "copyId": "Apparaat-ID kopiëren", + "copyAccessToken": "Access token kopiëren", + "copy-mqtt-authentication": "MQTT-referenties kopiëren", + "idCopiedMessage": "Apparaat-ID is gekopieerd", + "accessTokenCopiedMessage": "Access token is gekopieerd", + "mqtt-authentication-copied-message": "MQTT-authenticatie is gekopieerd", + "assignedToCustomer": "Toegewezen aan klant", + "unable-delete-device-alias-title": "Kan apparaatalias niet verwijderen", + "unable-delete-device-alias-text": "Alias '{{deviceAlias}}' kan niet worden verwijderd omdat deze wordt gebruikt door de volgende widget(s):
{{widgetsList}}", + "is-gateway": "Is gateway", + "overwrite-activity-time": "Overschrijf activiteitsmoment verbonden apparaat", + "device-filter": "Apparaatfilter", + "device-filter-title": "Apparaatfilter", + "filter-title": "Filter", + "device-state": "Apparaatstatus", + "state": "Status", + "any": "Elke", + "active": "Actief", + "inactive": "Inactief", + "public": "Openbaar", + "device-public": "Apparaat is openbaar", + "select-device": "Selecteer apparaat", + "import": "Apparaat importeren", + "device-file": "Apparaatbestand", + "search": "Apparaten zoeken", + "selected-devices": "{ count, plural, =1 {1 apparaat} other {# apparaten} } geselecteerd", + "device-configuration": "Apparaatconfiguratie", + "transport-configuration": "Transportconfiguratie", + "wizard": { + "device-details": "Apparaatdetails" + }, + "unassign-devices-from-edge-title": "Weet u zeker dat u { count, plural, =1 {1 apparaat} other {# apparaten} } wilt ontkoppelen?", + "unassign-devices-from-edge-text": "Na bevestiging worden alle geselecteerde apparaten ontkoppeld en zijn ze niet meer toegankelijk voor de edge.", + "time": "Tijd", + "connectivity": { + "check-connectivity": "Controleer connectiviteit", + "device-created-check-connectivity": "Apparaat aangemaakt. Laten we connectiviteit controleren!", + "loading-check-connectivity-command": "Commando's voor connectiviteitscontrole laden...", + "use-following-instructions": "Gebruik onderstaande instructies om telemetrie te verzenden namens het apparaat via shell", + "execute-following-command": "Voer het volgende commando uit", + "install-curl-windows": "Vanaf Windows 10 b17063 is cURL standaard beschikbaar", + "install-curl-macos": "Vanaf Mac OS X 10.2 6C115 (Jaguar) is cURL standaard beschikbaar", + "install-mqtt-windows": "Gebruik de instructies om mosquitto_pub te downloaden, installeren, configureren en uitvoeren", + "install-coap-client": "Gebruik de instructies om coap-client te downloaden, installeren, configureren en uitvoeren", + "install-necessary-client-tools": "Installeer noodzakelijke clienthulpmiddelen", + "mqtts-x509-command": "Gebruik de volgende documentatie om apparaat te verbinden via MQTT met X509-autorisatie", + "coaps-x509-command": "Gebruik de volgende documentatie om apparaat te verbinden via CoAP over DTLS met X509-autorisatie", + "snmp-command": "Gebruik de volgende documentatie om apparaat te verbinden via SNMP.", + "sparkplug-command": "Gebruik de volgende documentatie om apparaat te verbinden via MQTT Sparkplug.", + "lwm2m-command": "Gebruik de volgende documentatie om apparaat te verbinden via LWM2M." + } + }, + "dynamic-form": { + "property": { + "properties": "Eigenschappen", + "property": "Eigenschap", + "id": "Id", + "name": "Naam", + "type": "Type", + "type-text": "Tekst", + "type-password": "Wachtwoord", + "type-textarea": "Tekstgebied", + "type-number": "Nummer", + "type-switch": "Schakelaar", + "type-select": "Selectie", + "type-radios": "Keuzerondjes", + "type-datetime": "Datum/tijd", + "type-image": "Afbeelding", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "Kleur", + "type-color-settings": "Kleurinstellingen", + "type-font": "Lettertype", + "type-units": "Eenheden", + "type-icon": "Pictogram", + "type-fieldset": "Veldenset", + "type-array": "Array", + "type-html-section": "HTML-sectie", + "group-title": "Groepstitel", + "no-properties": "Geen eigenschappen geconfigureerd", + "add-property": "Eigenschap toevoegen", + "property-settings": "Eigenschapsinstellingen", + "remove-property": "Eigenschap verwijderen", + "default-value": "Standaardwaarde", + "value-required": "Waarde vereist", + "number-settings": "Nummerinstellingen", + "min": "Min", + "max": "Max", + "step": "Stap", + "selected-options-limit": "Limiet geselecteerde opties", + "advanced-ui-settings": "Geavanceerde UI-instellingen", + "disable-on-property": "Uitschakelen op eigenschap", + "display-condition-function": "Weergavevoorwaarde functie", + "sub-label": "Sublabel", + "vertical-divider-after": "Verticale scheiding na", + "input-field-suffix": "Achtervoegsel invoerveld", + "property-row-classes": "Rijklassen eigenschap", + "property-field-classes": "Veldklassen eigenschap", + "not-unique-property-ids-error": "Eigenschaps-Ids moeten uniek zijn!", + "enable-multiple-select": "Meerdere selecties inschakelen", + "allow-empty-select-option": "Lege optie toestaan", + "select-options": "Selectieopties", + "not-unique-select-option-value-error": "Waarden van selectieopties moeten uniek zijn!", + "value": "Waarde", + "label": "Label", + "add-option": "Optie toevoegen", + "no-options": "Geen opties geconfigureerd", + "remove-option": "Optie verwijderen", + "textarea-rows": "Rijen tekstgebied", + "help-id": "Help-id", + "buttons-direction": "Knoppenrichting", + "direction-row": "Rij", + "direction-column": "Kolom", + "radio-button-options": "Opties keuzerondjes", + "datetime-type": "Datum/tijd veldtype", + "datetime-type-date": "Datum", + "datetime-type-time": "Tijd", + "datetime-type-datetime": "Datum/tijd", + "enable-clear-button": "Wis-knop inschakelen", + "html-section-settings": "HTML-sectie instellingen", + "html-section-classes": "HTML-sectie klassen", + "html-section-content": "HTML-sectie inhoud", + "array-item": "Array-item", + "item-type": "Itemtype", + "item-name": "Itemnaam", + "no-items": "Geen items" + }, + "clear-form": "Formulier wissen", + "clear-form-prompt": "Weet u zeker dat u alle formulier-eigenschappen wilt verwijderen?", + "import-form": "Formulier importeren uit JSON", + "export-form": "Formulier exporteren naar JSON", + "json-file": "JSON-bestand", + "json-content": "JSON-inhoud", + "invalid-form-json-file-error": "Kan formulier niet importeren uit JSON: Ongeldige formulierstructuur in JSON." + }, + "asset-profile": { + "asset-profile": "Assetprofiel", + "asset-profiles": "Assetprofielen", + "all-asset-profiles": "Alles", + "add": "Assetprofiel toevoegen", + "edit": "Assetprofiel bewerken", + "asset-profile-details": "Assetprofiel details", + "no-asset-profiles-text": "Geen assetprofielen gevonden", + "search": "Assetprofielen zoeken", + "selected-asset-profiles": "{ count, plural, =1 {1 assetprofiel} other {# assetprofielen} } geselecteerd", + "no-asset-profiles-matching": "Geen overeenkomend assetprofiel gevonden voor '{{entity}}'.", + "asset-profile-required": "Assetprofiel is verplicht", + "idCopiedMessage": "Assetprofiel-ID is gekopieerd naar klembord", + "set-default": "Assetprofiel als standaard instellen", + "delete": "Assetprofiel verwijderen", + "copyId": "Assetprofiel-ID kopiëren", + "name-max-length": "Naam moet minder dan 256 tekens zijn", + "new-device-profile-name": "Naam van assetprofiel", + "new-device-profile-name-required": "Naam van assetprofiel is verplicht.", + "name": "Naam", + "name-required": "Naam is verplicht.", + "image": "Assetprofielafbeelding", + "description": "Beschrijving", + "default": "Standaard", + "default-rule-chain": "Standaard regelketen", + "default-edge-rule-chain": "Standaard edge-regelketen", + "default-edge-rule-chain-hint": "Wordt op edge gebruikt als regelketen voor verwerking van inkomende gegevens voor assets van dit profiel", + "mobile-dashboard": "Mobiel dashboard", + "mobile-dashboard-hint": "Wordt door mobiele app gebruikt als detaildashboard voor asset", + "select-queue-hint": "Selecteer uit een keuzelijst.", + "delete-asset-profile-title": "Weet u zeker dat u het assetprofiel '{{assetProfileName}}' wilt verwijderen?", + "delete-asset-profile-text": "Wees voorzichtig, na bevestiging worden het assetprofiel en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-asset-profiles-title": "Weet u zeker dat u { count, plural, =1 {1 assetprofiel} other {# assetprofielen} } wilt verwijderen?", + "delete-asset-profiles-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde assetprofielen en gerelateerde gegevens onherstelbaar verwijderd.", + "set-default-asset-profile-title": "Weet u zeker dat u '{{assetProfileName}}' als standaard wilt instellen?", + "set-default-asset-profile-text": "Na bevestiging wordt het assetprofiel als standaard gemarkeerd en gebruikt voor nieuwe assets zonder gespecificeerd profiel.", + "no-asset-profiles-found": "Geen assetprofielen gevonden.", + "create-new-asset-profile": "Nieuwe aanmaken!", + "create-asset-profile": "Nieuw assetprofiel aanmaken", + "import": "Assetprofiel importeren", + "export": "Assetprofiel exporteren", + "export-failed-error": "Kan assetprofiel niet exporteren: {{error}}", + "asset-profile-file": "Assetprofielbestand", + "invalid-asset-profile-file-error": "Kan assetprofiel niet importeren: Ongeldige datastructuur voor assetprofiel." + }, + "device-profile": { + "device-profile": "Apparaatprofiel", + "device-profiles": "Apparaatprofielen", + "all-device-profiles": "Alles", + "add": "Apparaatprofiel toevoegen", + "edit": "Apparaatprofiel bewerken", + "device-profile-details": "Apparaatprofiel details", + "no-device-profiles-text": "Geen apparaatprofielen gevonden", + "search": "Apparaatprofielen zoeken", + "selected-device-profiles": "{ count, plural, =1 {1 apparaatprofiel} other {# apparaatprofielen} } geselecteerd", + "no-device-profiles-matching": "Geen overeenkomend apparaatprofiel gevonden voor '{{entity}}'.", + "device-profile-required": "Apparaatprofiel is verplicht", + "idCopiedMessage": "Apparaatprofiel-ID is gekopieerd naar klembord", + "set-default": "Instellen als standaard apparaatprofiel", + "delete": "Apparaatprofiel verwijderen", + "copyId": "Apparaatprofiel-ID kopiëren", + "name-max-length": "Naam moet minder dan 256 tekens zijn", + "name": "Naam", + "name-required": "Naam is verplicht.", + "type": "Profieltype", + "type-required": "Profieltype is verplicht.", + "type-default": "Standaard", + "image": "Apparaatprofielafbeelding", + "transport-type": "Transporttype", + "transport-type-required": "Transporttype is verplicht.", + "transport-type-default": "Standaard", + "transport-type-default-hint": "Ondersteunt basis MQTT, HTTP en CoAP transport", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "Maakt geavanceerde MQTT-instellingen mogelijk", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "Maakt geavanceerde CoAP-instellingen mogelijk", + "transport-type-lwm2m": "LWM2M", + "transport-type-lwm2m-hint": "LWM2M transporttype", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "Specificeer SNMP transportconfiguratie", + "transport-type-http": "HTTP", + "description": "Beschrijving", + "default": "Standaard", + "profile-configuration": "Profielconfiguratie", + "transport-configuration": "Transportconfiguratie", + "default-rule-chain": "Standaard regelketen", + "default-edge-rule-chain": "Standaard edge regelketen", + "default-edge-rule-chain-hint": "Gebruikt op edge voor verwerking van gegevens voor apparaten met dit profiel", + "mobile-dashboard": "Mobiel dashboard", + "mobile-dashboard-hint": "Gebruikt door mobiele app als apparaatoverzicht", + "select-queue-hint": "Selecteer uit de keuzelijst.", + "delete-device-profile-title": "Weet u zeker dat u het apparaatprofiel '{{deviceProfileName}}' wilt verwijderen?", + "delete-device-profile-text": "Let op: het apparaatprofiel en gerelateerde gegevens, inclusief OTA-updates, worden onherstelbaar verwijderd.", + "delete-device-profiles-title": "Weet u zeker dat u { count, plural, =1 {1 apparaatprofiel} other {# apparaatprofielen} } wilt verwijderen?", + "delete-device-profiles-text": "Let op: alle geselecteerde profielen en gerelateerde gegevens inclusief OTA-updates worden onherstelbaar verwijderd.", + "set-default-device-profile-title": "Wilt u '{{deviceProfileName}}' instellen als standaardprofiel?", + "set-default-device-profile-text": "Na bevestiging wordt dit profiel gebruikt voor nieuwe apparaten zonder specifiek profiel.", + "no-device-profiles-found": "Geen apparaatprofielen gevonden.", + "create-new-device-profile": "Nieuwe aanmaken!", + "mqtt-device-topic-filters": "MQTT apparaattopicfilters", + "mqtt-device-topic-filters-unique": "MQTT apparaattopicfilters moeten uniek zijn.", + "mqtt-device-topic-filters-spark-plug": "MQTT Sparkplug B Edge of Network (EoN) node.", + "mqtt-device-topic-filters-spark-plug-hint": "Sta verbindingen toe van EoN-nodes met Sparkplug B payload en topicstructuur.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "SparkPlug-metingen als attributen opslaan.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "SparkPlug-metingen worden opgeslagen als attributen; overige als telemetrie.", + "mqtt-device-payload-type": "MQTT apparaatgegevensformaat", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Compatibiliteit met ander formaat inschakelen", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Bij inschakelen wordt standaard Protobuf gebruikt. Bij fout wordt JSON geprobeerd. Handig tijdens firmware-overgangen. Kan prestaties beïnvloeden.", + "mqtt-use-json-format-for-default-downlink-topics": "Gebruik JSON voor standaard downlink topics", + "mqtt-use-json-format-for-default-downlink-topics-hint": "Indien ingeschakeld, worden JSON payloads gebruikt voor standaard topics zoals v1/devices/me/attributes. Dit heeft geen invloed op v2-topics.", + "mqtt-send-ack-on-validation-exception": "Stuur PUBACK bij validatiefout", + "mqtt-send-ack-on-validation-exception-hint": "Standaard sluit platform sessie. Indien ingeschakeld, wordt PUBACK gestuurd bij fout.", + "snmp-add-mapping": "SNMP-mapping toevoegen", + "snmp-mapping-not-configured": "Geen mapping geconfigureerd van OID naar telemetrie", + "snmp-timseries-or-attribute-name": "Naam tijdreeks/attribuut voor mapping", + "snmp-timseries-or-attribute-type": "Type tijdreeks/attribuut voor mapping", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "Payloadtype is verplicht.", + "coap-device-type": "CoAP apparaattype", + "coap-device-payload-type": "CoAP payload", + "coap-device-type-required": "CoAP apparaattype is verplicht.", + "coap-device-type-default": "Standaard", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "Ondersteuning voor enkelvoudige [+] en meervoudige [#] wildcards.", + "telemetry-topic-filter": "Telemetrie topicfilter", + "telemetry-topic-filter-required": "Telemetrie topicfilter is verplicht.", + "attributes-topic-filter": "Attributen publiceer topicfilter", + "attributes-subscribe-topic-filter": "Attributen subscribe topicfilter", + "attributes-topic-filter-required": "Attributen publiceer topicfilter is verplicht.", + "attributes-subscribe-topic-filter-required": "Attributen subscribe topic is verplicht", + "telemetry-proto-schema": "Telemetrie Protobuf-schema", + "telemetry-proto-schema-required": "Telemetrie Protobuf-schema is verplicht.", + "attributes-proto-schema": "Attributen Protobuf-schema", + "attributes-proto-schema-required": "Attributen Protobuf-schema is verplicht.", + "rpc-response-proto-schema": "RPC respons Protobuf-schema", + "rpc-response-proto-schema-required": "RPC respons Protobuf-schema is verplicht.", + "rpc-response-topic-filter": "RPC respons topicfilter", + "rpc-response-topic-filter-required": "RPC respons topicfilter is verplicht.", + "rpc-request-proto-schema": "RPC verzoek Protobuf-schema", + "rpc-request-proto-schema-required": "RPC verzoek Protobuf-schema is verplicht.", + "rpc-request-proto-schema-hint": "RPC-verzoekbericht moet altijd de volgende velden bevatten: string method = 1; int32 requestId = 2; en params = 3 van elk gegevenstype.", + "not-valid-pattern-topic-filter": "Ongeldig patroon voor topicfilter", + "not-valid-single-character": "Ongeldig gebruik van enkelvoudig wildcard-teken", + "not-valid-multi-character": "Ongeldig gebruik van meervoudig wildcard-teken", + "single-level-wildcards-hint": "[+] is geschikt voor elk niveau in topicfilter. Bijv.: v1/devices/+/telemetry of +/devices/+/attributes.", + "multi-level-wildcards-hint": "[#] kan het hele topicfilter vervangen en moet het laatste teken zijn. Bijv.: # of v1/devices/me/#.", + "alarm-rules": "Alarmregels", + "alarm-rules-with-count": "Alarmregels ({{count}})", + "no-alarm-rules": "Geen alarmregels geconfigureerd", + "add-alarm-rule": "Alarmregel toevoegen", + "edit-alarm-rule": "Alarmregel bewerken", + "alarm-type": "Alarmtype", + "alarm-type-required": "Alarmtype is verplicht.", + "alarm-type-unique": "Alarmtype moet uniek zijn binnen het apparaatprofiel.", + "alarm-type-max-length": "Alarmtype moet minder dan 256 tekens zijn", + "create-alarm-pattern": "Maak {{alarmType}} alarm aan", + "create-alarm-rules": "Alarmregels aanmaken", + "no-create-alarm-rules": "Geen aanmaakvoorwaarden geconfigureerd", + "add-create-alarm-rule-prompt": "Voeg een aanmaakvoorwaarde toe", + "clear-alarm-rule": "Wis alarmregel", + "no-clear-alarm-rule": "Geen wisvoorwaarde geconfigureerd", + "add-create-alarm-rule": "Aanmaakvoorwaarde toevoegen", + "add-clear-alarm-rule": "Wisvoorwaarde toevoegen", + "select-alarm-severity": "Selecteer alarmernst", + "alarm-severity-required": "Alarmernst is verplicht.", + "condition-duration": "Voorwaarde duur", + "condition-duration-value": "Duurwaarde", + "condition-duration-time-unit": "Tijdeenheid", + "condition-duration-value-range": "Duurwaarde moet tussen 1 en 2147483647 liggen.", + "condition-duration-value-pattern": "Duurwaarde moet een geheel getal zijn.", + "condition-duration-value-required": "Duurwaarde is verplicht.", + "condition-duration-time-unit-required": "Tijdeenheid is verplicht.", + "advanced-settings": "Geavanceerde instellingen", + "alarm-rule-additional-info": "Aanvullende informatie", + "edit-alarm-rule-additional-info": "Bewerk aanvullende informatie", + "alarm-rule-additional-info-placeholder": "Voer hier opmerkingen of aanpassingen in die onder 'Aanvullende informatie' in de alarmdetails worden weergegeven", + "alarm-rule-additional-info-hint": "Hint: gebruik ${keyName} om waarden van attributen of telemetrie te vervangen in de regelvoorwaarde.", + "alarm-rule-mobile-dashboard": "Mobiel dashboard", + "alarm-rule-mobile-dashboard-hint": "Gebruikt door mobiele applicatie als dashboard voor alarmdetails", + "alarm-rule-no-mobile-dashboard": "Geen dashboard geselecteerd", + "propagate-alarm": "Alarm doorsturen naar gerelateerde entiteiten", + "alarm-rule-relation-types-list": "Relatietypes", + "alarm-rule-relation-types-list-hint": "Definieer relatietypes om gerelateerde entiteiten te filteren. Indien leeg, wordt het alarm naar alle gerelateerde entiteiten doorgezet.", + "propagate-alarm-to-owner": "Alarm doorsturen naar eigenaar (Klant of Tenant)", + "propagate-alarm-to-tenant": "Alarm doorsturen naar Tenant", + "alarm-rule-condition": "Alarmregelvoorwaarde", + "enter-alarm-rule-condition-prompt": "Voeg een alarmregelvoorwaarde toe", + "edit-alarm-rule-condition": "Bewerk alarmregelvoorwaarde", + "device-provisioning": "Apparaatvoorziening", + "provision-strategy": "Voorzieningsstrategie", + "provision-strategy-required": "Voorzieningsstrategie is verplicht.", + "provision-strategy-disabled": "Uitgeschakeld", + "provision-strategy-created-new": "Nieuwe apparaten toestaan", + "provision-strategy-check-pre-provisioned": "Controleren op vooraf geconfigureerde apparaten", + "provision-device-key": "Voorzieningssleutel", + "provision-device-key-required": "Voorzieningssleutel is verplicht.", + "copy-provision-key": "Voorzieningssleutel kopiëren", + "provision-key-copied-message": "Voorzieningssleutel is gekopieerd naar klembord", + "provision-device-secret": "Voorzieningsgeheim", + "provision-device-secret-required": "Voorzieningsgeheim is verplicht.", + "copy-provision-secret": "Voorzieningsgeheim kopiëren", + "provision-secret-copied-message": "Voorzieningsgeheim is gekopieerd naar klembord", + "provision-strategy-x509": { + "certificate-chain": "X509 certificaatketen", + "certificate-chain-hint": "X.509-certificaatstrategie wordt gebruikt om apparaten te voorzien via clientcertificaten in tweerichtings-TLS-communicatie.", + "allow-create-new-devices": "Nieuwe apparaten aanmaken", + "allow-create-new-devices-hint": "Indien geselecteerd, worden nieuwe apparaten aangemaakt en wordt het clientcertificaat gebruikt als apparaatreferentie.", + "certificate-value": "Certificaat in PEM-indeling", + "certificate-value-required": "Certificaat in PEM-indeling is verplicht", + "cn-regex-variable": "CN reguliere expressievariabele", + "cn-regex-variable-required": "CN reguliere expressievariabele is verplicht", + "cn-regex-variable-hint": "Nodig om de apparaatnaam te extraheren uit de algemene naam (CN) van het X509-certificaat van het apparaat." + }, + "condition": "Voorwaarde", + "condition-type": "Voorwaardetype", + "condition-type-simple": "Eenvoudig", + "condition-type-duration": "Duur", + "condition-during": "Gedurende {{during}}", + "condition-during-dynamic": "Gedurende \"{{ attribute }}\" ({{during}})", + "condition-type-repeating": "Herhaling", + "condition-type-required": "Voorwaardetype is verplicht.", + "condition-repeating-value": "Aantal gebeurtenissen", + "condition-repeating-value-range": "Aantal gebeurtenissen moet tussen 1 en 2147483647 liggen.", + "condition-repeating-value-pattern": "Aantal gebeurtenissen moet een geheel getal zijn.", + "condition-repeating-value-required": "Aantal gebeurtenissen is verplicht.", + "condition-repeat-times": "Herhaalt { count, plural, =1 {1 keer} other {# keer} }", + "condition-repeat-times-dynamic": "Herhaalt \"{ attribute }\" ({ count, plural, =1 {1 keer} other {# keer} })", + "schedule-type": "Plannertype", + "schedule-type-required": "Plannertype is verplicht.", + "schedule": "Planning", + "edit-schedule": "Alarmplanning bewerken", + "schedule-any-time": "Altijd actief", + "schedule-specific-time": "Actief op specifiek tijdstip", + "schedule-custom": "Aangepast", + "schedule-day": { + "monday": "Maandag", + "tuesday": "Dinsdag", + "wednesday": "Woensdag", + "thursday": "Donderdag", + "friday": "Vrijdag", + "saturday": "Zaterdag", + "sunday": "Zondag" + }, + "schedule-days": "Dagen", + "schedule-time": "Tijd", + "schedule-time-from": "Van", + "schedule-time-to": "Tot", + "schedule-days-of-week-required": "Minimaal één weekdag moet worden geselecteerd.", + "create-device-profile": "Nieuw apparaatprofiel aanmaken", + "import": "Apparaatprofiel importeren", + "export": "Apparaatprofiel exporteren", + "export-failed-error": "Kan apparaatprofiel niet exporteren: {{error}}", + "device-profile-file": "Apparaatprofielbestand", + "invalid-device-profile-file-error": "Kan apparaatprofiel niet importeren: Ongeldige datastructuur.", + "power-saving-mode": "Energie besparende modus", + "power-saving-mode-type": { + "default": "Gebruik energiebesparende modus van profiel", + "psm": "Power Saving Mode (PSM)", + "drx": "Discontinuous Reception (DRX)", + "edrx": "Extended Discontinuous Reception (eDRX)" + }, + "edrx-cycle": "eDRX-cyclus", + "edrx-cycle-required": "eDRX-cyclus is verplicht.", + "edrx-cycle-pattern": "eDRX-cyclus moet een positief geheel getal zijn.", + "edrx-cycle-min": "Minimaal aantal seconden voor eDRX-cyclus is {{ min }}.", + "paging-transmission-window": "Paging transmissievenster", + "paging-transmission-window-required": "Paging transmissievenster is verplicht.", + "paging-transmission-window-pattern": "Paging transmissievenster moet een positief geheel getal zijn.", + "paging-transmission-window-min": "Minimaal aantal seconden voor transmissievenster is {{ min }}.", + "psm-activity-timer": "PSM Activiteitstimer", + "psm-activity-timer-required": "PSM activiteitstimer is verplicht.", + "psm-activity-timer-pattern": "PSM activiteitstimer moet een positief geheel getal zijn.", + "psm-activity-timer-min": "Minimaal aantal seconden voor activiteitstimer is {{ min }}.", + "lwm2m": { + "object-list": "Objectlijst", + "object-list-empty": "Geen objecten geselecteerd.", + "no-objects-found": "Geen objecten gevonden.", + "no-objects-matching": "Geen objecten gevonden die overeenkomen met '{{object}}'.", + "model-tab": "LWM2M-model", + "add-new-instances": "Nieuwe instanties toevoegen", + "instances-list": "Instantielijst", + "instances-list-required": "Instantielijst is verplicht.", + "instance-id-pattern": "Instantie-ID moet een positief geheel getal zijn.", + "instance-id-max": "Maximale waarde voor instantie-ID is {{max}}.", + "instance": "Instantie", + "resource-label": "#ID Resourcenaam", + "observe-label": "Observeer", + "attribute-label": "Attribuut", + "telemetry-label": "Telemetrie", + "edit-observe-select": "Om observatie te bewerken, selecteer telemetrie of attribuut", + "edit-attributes-select": "Om attributen te bewerken, selecteer telemetrie of attribuut", + "no-attributes-set": "Geen attributen ingesteld", + "key-name": "Sleutelnaam", + "key-name-required": "Sleutelnaam is verplicht", + "attribute-name": "Naam attribuut", + "attribute-name-required": "Naam attribuut is verplicht.", + "attribute-value": "Attribuutwaarde", + "attribute-value-required": "Attribuutwaarde is verplicht.", + "attribute-value-pattern": "Attribuutwaarde moet een positief geheel getal zijn.", + "edit-attributes": "Attributen bewerken: {{ name }}", + "view-attributes": "Attributen bekijken: {{ name }}", + "add-attribute": "Attribuut toevoegen", + "edit-attribute": "Attribuut bewerken", + "view-attribute": "Attribuut bekijken", + "remove-attribute": "Attribuut verwijderen", + "delete-server-text": "Wees voorzichtig, na bevestiging wordt de serverconfiguratie onherroepelijk verwijderd.", + "delete-server-title": "Weet je zeker dat je de server wilt verwijderen?", + "mode": "Beveiligingsconfiguratiemodus", + "bootstrap-tab": "Bootstrap", + "bootstrap-server-legend": "Bootstrapserver (ShortId...)", + "lwm2m-server-legend": "LwM2M-server (ShortId...)", + "server": "Server", + "short-id": "Korte server-ID", + "short-id-tooltip": "Korte server-ID. Wordt gebruikt om serverobjectinstanties te koppelen.\nDeze identifier identificeert uniek elke LwM2M-server geconfigureerd voor de LwM2M-client.\nMoet worden ingesteld als de Bootstrap-Server-resource 'false' is.\nWaarden ID:0 en ID:65535 mogen niet worden gebruikt.", + "short-id-tooltip-bootstrap": "Korte server-ID. Vereist voor associatie van objectinstanties.\nMag niet 0 of 65535 zijn.", + "short-id-required": "Korte server-ID is verplicht.", + "short-id-range": "Korte server-ID moet tussen {{ min }} en {{ max }} liggen.", + "short-id-pattern": "Korte server-ID moet een positief geheel getal zijn.", + "lifetime": "Registratieduur client", + "lifetime-required": "Registratieduur is verplicht.", + "lifetime-pattern": "Registratieduur moet een positief geheel getal zijn.", + "default-min-period": "Minimale periode tussen meldingen (s)", + "default-min-period-tooltip": "Standaard minimumperiode voor observatie indien geen waarde opgegeven is.", + "default-min-period-required": "Minimale periode is verplicht.", + "default-min-period-pattern": "Minimale periode moet een positief geheel getal zijn.", + "notification-storing": "Notificaties opslaan bij uitgeschakeld of offline", + "binding": "Binding", + "binding-type": { + "u": "U: Client is bereikbaar via UDP-binding.", + "m": "M: Client is bereikbaar via MQTT-binding.", + "h": "H: Client is bereikbaar via HTTP-binding.", + "t": "T: Client is bereikbaar via TCP-binding.", + "s": "S: Client is bereikbaar via SMS-binding.", + "n": "N: Client MOET antwoorden via Non-IP-binding (sinds LWM2M 1.1).", + "uq": "UQ: UDP in wachtrijmodus (niet ondersteund sinds LWM2M 1.1)", + "uqs": "UQS: UDP + SMS actief; UDP in wachtrijmodus (niet ondersteund sinds LWM2M 1.1)", + "tq": "TQ: TCP in wachtrijmodus (niet ondersteund sinds LWM2M 1.1)", + "tqs": "TQS: TCP + SMS actief; TCP in wachtrijmodus (niet ondersteund sinds LWM2M 1.1)", + "sq": "SQ: SMS in wachtrijmodus (niet ondersteund sinds LWM2M 1.1)" + }, + "binding-tooltip": "Bindingmodus zoals gedefinieerd in resource /1/x/7 van het LwM2M-serverobject.\nGeeft ondersteunde bindingen aan in de client. Deze waarde moet overeenkomen met waarde in resource /3/0/16.\nSlechts één binding kan gebruikt worden per sessie.", + "bootstrap-server": "Bootstrapserver", + "lwm2m-server": "LwM2M-server", + "include-bootstrap-server": "Bootstrapserver-updates insluiten", + "bootstrap-update-title": "Er is al een Bootstrapserver geconfigureerd. Weet je zeker dat je de updates wilt uitsluiten?", + "bootstrap-update-text": "Wees voorzichtig, na bevestiging wordt de configuratie van de Bootstrapserver verwijderd.", + "server-host": "Host", + "server-host-required": "Host is verplicht.", + "server-port": "Poort", + "server-port-required": "Poort is verplicht.", + "server-port-pattern": "Poort moet een positief geheel getal zijn.", + "server-port-range": "Poort moet tussen 1 en 65535 liggen.", + "server-public-key": "Server openbare sleutel", + "server-public-key-required": "Server openbare sleutel is verplicht.", + "client-hold-off-time": "Wachttijd client", + "client-hold-off-time-required": "Wachttijd client is verplicht.", + "client-hold-off-time-pattern": "Wachttijd client moet een positief geheel getal zijn.", + "client-hold-off-time-tooltip": "Wachttijd van de client, alleen te gebruiken met een Bootstrap-server", + "account-after-timeout": "Account na time-out", + "account-after-timeout-required": "Account na time-out is verplicht.", + "account-after-timeout-pattern": "Account na time-out moet een positief geheel getal zijn.", + "account-after-timeout-tooltip": "Accountinstelling van de Bootstrap-server na de opgegeven time-out.", + "server-type": "Servertype", + "add-new-server-title": "Nieuwe serverconfiguratie toevoegen", + "add-server-config": "Serverconfiguratie toevoegen", + "add-lwm2m-server-config": "LwM2M-server toevoegen", + "no-config-servers": "Geen servers geconfigureerd", + "others-tab": "Overige instellingen", + "client-strategy": "Clientstrategie bij verbinding", + "client-strategy-label": "Strategie", + "client-strategy-only-observe": "Alleen Observe-verzoek na eerste verbinding", + "client-strategy-read-all": "Lees alle bronnen & Observe-verzoek na registratie", + "fw-update": "Firmware-update", + "fw-update-strategy": "Firmware-updatestrategie", + "fw-update-strategy-data": "Firmware als binair bestand pushen via Object 19, Resource 0 (Data)", + "fw-update-strategy-package": "Firmware als binair bestand pushen via Object 5, Resource 0 (Package)", + "fw-update-strategy-package-uri": "Unieke CoAP-URL automatisch genereren om firmwarepakket te downloaden en pushen via Object 5, Resource 1 (Package URI)", + "sw-update": "Software-update", + "sw-update-strategy": "Software-updatestrategie", + "sw-update-strategy-package": "Binaire bestanden pushen via Object 9, Resource 2 (Package)", + "sw-update-strategy-package-uri": "Unieke CoAP-URL automatisch genereren om softwarepakket te downloaden en pushen via Object 9, Resource 3 (Package URI)", + "fw-update-resource": "Firmware-update CoAP-resource", + "fw-update-resource-required": "Firmware-update CoAP-resource is verplicht.", + "sw-update-resource": "Software-update CoAP-resource", + "sw-update-resource-required": "Software-update CoAP-resource is verplicht.", + "config-json-tab": "Json Config Profielapparaat", + "attributes-name": { + "min-period": "Minimale periode", + "max-period": "Maximale periode", + "greater-than": "Groter dan", + "less-than": "Kleiner dan", + "step": "Stap", + "min-evaluation-period": "Minimale evaluatieperiode", + "max-evaluation-period": "Maximale evaluatieperiode" + }, + "default-object-id": "Standaard objectversie (Attribuut)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1" + } + }, + "snmp": { + "add-communication-config": "Communicatieconfiguratie toevoegen", + "add-mapping": "Mapping toevoegen", + "authentication-passphrase": "Authenticatie wachtzin", + "authentication-passphrase-required": "Authenticatie wachtzin is verplicht.", + "authentication-protocol": "Authenticatieprotocol", + "authentication-protocol-required": "Authenticatieprotocol is verplicht.", + "communication-configs": "Communicatieconfiguraties", + "community": "Community string", + "community-required": "Community string is verplicht.", + "context-name": "Contextnaam", + "data-key": "Gegevenssleutel", + "data-key-required": "Gegevenssleutel is verplicht.", + "data-type": "Gegevenstype", + "data-type-required": "Gegevenstype is verplicht.", + "engine-id": "Engine ID", + "host": "Host", + "host-required": "Host is verplicht.", + "oid": "OID", + "oid-pattern": "Ongeldig OID-formaat.", + "oid-required": "OID is verplicht.", + "please-add-communication-config": "Voeg een communicatieconfiguratie toe", + "please-add-mapping-config": "Voeg een mappingconfiguratie toe", + "port": "Poort", + "port-format": "Ongeldig poortformaat.", + "port-required": "Poort is verplicht.", + "privacy-passphrase": "Privacy wachtzin", + "privacy-passphrase-required": "Privacy wachtzin is verplicht.", + "privacy-protocol": "Privacyprotocol", + "privacy-protocol-required": "Privacyprotocol is verplicht.", + "protocol-version": "Protocolversie", + "protocol-version-required": "Protocolversie is verplicht.", + "querying-frequency": "Queryfrequentie, ms", + "querying-frequency-invalid-format": "Queryfrequentie moet een positief geheel getal zijn.", + "querying-frequency-required": "Queryfrequentie is verplicht.", + "retries": "Pogingen", + "retries-invalid-format": "Aantal pogingen moet een positief geheel getal zijn.", + "retries-required": "Aantal pogingen is verplicht.", + "scope": "Scope", + "scope-required": "Scope is verplicht.", + "security-name": "Beveiligingsnaam", + "security-name-required": "Beveiligingsnaam is verplicht.", + "timeout-ms": "Time-out, ms", + "timeout-ms-invalid-format": "Time-out moet een positief geheel getal zijn.", + "timeout-ms-required": "Time-out is verplicht.", + "user-name": "Gebruikersnaam", + "user-name-required": "Gebruikersnaam is verplicht." + } + }, + "dialog": { + "close": "Dialoog sluiten", + "error-message-title": "Foutmelding:", + "error-details-title": "Foutdetails" + }, + "direction": { + "column": "Kolom", + "row": "Rij" + }, + "edge": { + "edge": "Edge", + "edge-instances": "Edge-instanties", + "instances": "Instanties", + "edge-file": "Edge-bestand", + "name-max-length": "Naam mag niet langer zijn dan 256 tekens", + "label-max-length": "Label mag niet langer zijn dan 256 tekens", + "type-max-length": "Type mag niet langer zijn dan 256 tekens", + "management": "Edgebeheer", + "no-edges-matching": "Geen edge-instanties gevonden die overeenkomen met '{{entity}}'.", + "add": "Edge toevoegen", + "no-edges-text": "Geen edge-instanties gevonden", + "edge-details": "Edge-details", + "add-edge-text": "Nieuwe edge toevoegen", + "delete": "Edge verwijderen", + "delete-edge-title": "Weet je zeker dat je de edge '{{edgeName}}' wilt verwijderen?", + "delete-edge-text": "Wees voorzichtig, na bevestiging wordt de edge en alle gerelateerde gegevens permanent verwijderd.", + "delete-edges-title": "Weet je zeker dat je { count, plural, =1 {1 edge} other {# edge-instanties} } wilt verwijderen?", + "delete-edges-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde edge-instanties verwijderd inclusief alle gerelateerde gegevens.", + "name": "Naam", + "name-starts-with": "Edge-naam begint met", + "name-required": "Naam is verplicht.", + "description": "Beschrijving", + "details": "Details", + "events": "Gebeurtenissen", + "copy-id": "Kopieer Edge-ID", + "id-copied-message": "Edge-ID is gekopieerd naar klembord", + "sync": "Edge synchroniseren", + "edge-required": "Edge is verplicht", + "edge-type": "Edge-type", + "edge-type-required": "Edge-type is verplicht.", + "event-action": "Gebeurtenisactie", + "entity-id": "Entiteit-ID", + "select-edge-type": "Selecteer edge-type", + "assign-to-customer": "Toewijzen aan klant", + "assign-to-customer-text": "Selecteer de klant aan wie je de edge(s) wilt toewijzen", + "assign-edge-to-customer": "Wijs Edge(s) toe aan klant", + "assign-edge-to-customer-text": "Selecteer de edge-instanties die je aan de klant wilt toewijzen", + "assignedToCustomer": "Toegewezen aan klant", + "edge-public": "Edge is openbaar", + "assigned-to-customer": "Toegewezen aan: {{customerTitle}}", + "unassign-from-customer": "Toewijzing aan klant ongedaan maken", + "unassign-edge-title": "Weet je zeker dat je de edge '{{edgeName}}' wilt ontkoppelen?", + "unassign-edge-text": "Na bevestiging wordt de edge ontkoppeld en is deze niet meer toegankelijk voor de klant.", + "unassign-edges-title": "Weet je zeker dat je { count, plural, =1 {1 edge} other {# edge-instanties} } wilt ontkoppelen?", + "unassign-edges-text": "Na bevestiging worden alle geselecteerde edge-instanties ontkoppeld en zijn ze niet meer toegankelijk voor de klant.", + "make-public": "Maak edge openbaar", + "make-public-edge-title": "Weet je zeker dat je de edge '{{edgeName}}' openbaar wilt maken?", + "make-public-edge-text": "Na bevestiging wordt de edge en alle gerelateerde gegevens openbaar gemaakt.", + "make-private": "Maak edge privé", + "public": "Openbaar", + "make-private-edge-title": "Weet je zeker dat je de edge '{{edgeName}}' privé wilt maken?", + "make-private-edge-text": "Na bevestiging wordt de edge en alle gerelateerde gegevens privé gemaakt.", + "import": "Edge importeren", + "install-connect-instructions": "Installatie- en verbindingsinstructies", + "install-connect-instructions-edge-created": "Edge aangemaakt! Raadpleeg installatie- en verbindingsinstructies", + "loading-edge-instructions": "Edge-instructies laden...", + "label": "Label", + "load-entity-error": "Kan gegevens niet laden. Entiteit is verwijderd.", + "assign-new-edge": "Nieuwe edge toewijzen", + "unassign-from-edge": "Ontkoppelen van edge", + "edge-key": "Edge-sleutel", + "copy-edge-key": "Kopieer Edge-sleutel", + "edge-key-copied-message": "Edge-sleutel is gekopieerd naar klembord", + "edge-secret": "Edge-geheim", + "copy-edge-secret": "Kopieer Edge-geheim", + "edge-secret-copied-message": "Edge-geheim is gekopieerd naar klembord", + "manage-assets": "Beheer assets", + "manage-devices": "Beheer apparaten", + "manage-entity-views": "Beheer entiteitweergaven", + "manage-dashboards": "Beheer dashboards", + "manage-rulechains": "Beheer rule chains", + "assets": "Edge-assets", + "devices": "Edge-apparaten", + "entity-views": "Edge-entiteitweergaven", + "dashboard": "Edge-dashboard", + "dashboards": "Edge-dashboards", + "rulechain-templates": "Rule chain-sjablonen", + "edge-rulechain-templates": "Edge rule chain-sjablonen", + "rulechains": "Edge rule chains", + "search": "Zoek edges", + "selected-edges": "{ count, plural, =1 {1 edge} other {# edges} } geselecteerd", + "any-edge": "Elke edge", + "no-edge-types-matching": "Geen edge-types gevonden die overeenkomen met '{{entitySubtype}}'.", + "edge-type-list-empty": "Geen edge-types geselecteerd.", + "edge-types": "Edge-types", + "enter-edge-type": "Voer edge-type in", + "deployed": "Ingezet", + "pending": "In afwachting", + "downlinks": "Downlinks", + "no-downlinks-prompt": "Geen downlinks gevonden", + "sync-process-started-successfully": "Synchronisatieproces succesvol gestart!", + "missing-related-rule-chains-title": "Edge mist gerelateerde rule chain(s)", + "missing-related-rule-chains-text": "Aan edge toegewezen rule chain(s) gebruiken knooppunten die berichten doorsturen naar niet-toegewezen rule chain(s).

Lijst van ontbrekende rule chain(s):
{{missingRuleChains}}", + "widget-datasource-error": "Deze widget ondersteunt alleen EDGE-entiteit als gegevensbron", + "upgrade-instructions": "Upgrade-instructies", + "connected": "Verbonden", + "disconnected": "Verbinding verbroken" + }, + "edge-event": { + "type-dashboard": "Dashboard", + "type-asset": "Asset", + "type-device": "Apparaat", + "type-device-profile": "Apparaatprofiel", + "type-asset-profile": "Assetprofiel", + "type-entity-view": "Entiteitsweergave", + "type-alarm": "Alarm", + "type-rule-chain": "Regelketen", + "type-rule-chain-metadata": "Regelketen Metadata", + "type-edge": "Edge", + "type-user": "Gebruiker", + "type-tenant": "Tenant", + "type-tenant-profile": "Tenantprofiel", + "type-customer": "Klant", + "type-relation": "Relatie", + "type-widgets-bundle": "Widgetbundel", + "type-widgets-type": "Widgettype", + "type-admin-settings": "Beheerinstellingen", + "type-ota-package": "OTA-pakket", + "type-queue": "Wachtrij", + "action-type-added": "Toegevoegd", + "action-type-deleted": "Verwijderd", + "action-type-updated": "Bijgewerkt", + "action-type-post-attributes": "Attribuut verzonden", + "action-type-attributes-updated": "Attributen bijgewerkt", + "action-type-attributes-deleted": "Attributen verwijderd", + "action-type-timeseries-updated": "Tijdreeks bijgewerkt", + "action-type-credentials-updated": "Referenties bijgewerkt", + "action-type-assigned-to-customer": "Toegewezen aan klant", + "action-type-unassigned-from-customer": "Ontkoppeld van klant", + "action-type-relation-add-or-update": "Relatie toegevoegd/bijgewerkt", + "action-type-relation-deleted": "Relatie verwijderd", + "action-type-rpc-call": "RPC-oproep", + "action-type-alarm-ack": "Alarm bevestigd", + "action-type-alarm-clear": "Alarm gewist", + "action-type-alarm-assigned": "Alarm toegewezen", + "action-type-alarm-unassigned": "Alarm ontkoppeld", + "action-type-assigned-to-edge": "Toegewezen aan edge", + "action-type-unassigned-from-edge": "Ontkoppeld van edge", + "action-type-credentials-request": "Referentieaanvraag", + "action-type-entity-merge-request": "Entiteitsmerge-aanvraag" + }, + "error": { + "unable-to-connect": "Kan geen verbinding maken met de server! Controleer uw internetverbinding.", + "unhandled-error-code": "Onbehandelde foutcode: {{errorCode}}", + "unknown-error": "Onbekende fout" + }, + "entity": { + "entity": "Entiteit", + "entities": "Entiteiten", + "entities-count": "Aantal entiteiten", + "alarms-count": "Aantal alarmen", + "aliases": "Entiteit-aliasen", + "aliases-short": "Aliassen", + "entity-alias": "Entiteit-alias", + "unable-delete-entity-alias-title": "Kan entiteit-alias niet verwijderen", + "unable-delete-entity-alias-text": "Entiteit-alias '{{entityAlias}}' kan niet worden verwijderd omdat het wordt gebruikt door de volgende widget(s):
{{widgetsList}}", + "duplicate-alias-error": "Dubbele alias gevonden '{{alias}}'.
Entiteit-aliasen moeten uniek zijn binnen het dashboard.", + "missing-entity-filter-error": "Filter ontbreekt voor alias '{{alias}}'.", + "configure-alias": "Configureer alias '{{alias}}'", + "alias": "Alias", + "alias-required": "Entiteit-alias is verplicht.", + "remove-alias": "Verwijder entiteit-alias", + "add-alias": "Voeg entiteit-alias toe", + "edit-alias": "Bewerk entiteit-alias", + "entity-list": "Entiteitenlijst", + "entity-type": "Entiteittype", + "entity-types": "Entiteittypes", + "entity-type-list": "Lijst met entiteittypes", + "any-entity": "Elke entiteit", + "add-entity-type": "Voeg entiteittype toe", + "enter-entity-type": "Voer entiteittype in", + "no-entities-matching": "Geen entiteiten gevonden die overeenkomen met '{{entity}}'.", + "no-entities-text": "Geen entiteiten gevonden", + "no-entity-types-matching": "Geen entiteittypes gevonden die overeenkomen met '{{entityType}}'.", + "name-starts-with": "Naamexpressie", + "help-text": "Gebruik '%' indien nodig: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter": "Gebruik filter", + "entity-list-empty": "Geen entiteiten geselecteerd.", + "entity-type-list-required": "Minstens één entiteittype moet worden geselecteerd.", + "entity-name-filter-required": "Entiteitnaamfilter is vereist.", + "entity-name-filter-no-entity-matched": "Geen entiteiten gevonden die beginnen met '{{entity}}'.", + "all-subtypes": "Alle", + "select-entities": "Selecteer entiteiten", + "no-aliases-found": "Geen aliassen gevonden.", + "no-alias-matching": "'{{alias}}' niet gevonden.", + "create-new-alias": "Maak een nieuwe!", + "create-new": "Nieuw aanmaken", + "key": "Sleutel", + "key-name": "Sleutelnaam", + "no-keys-found": "Geen sleutels gevonden.", + "no-key-matching": "'{{key}}' niet gevonden.", + "create-new-key": "Maak een nieuwe!", + "type": "Type", + "type-required": "Entiteittype is vereist.", + "type-device": "Apparaat", + "type-devices": "Apparaten", + "list-of-devices": "{ count, plural, =1 {Een apparaat} other {Lijst van # apparaten} }", + "device-name-starts-with": "Apparaten met namen die beginnen met '{{prefix}}'", + "type-device-profile": "Apparaatprofiel", + "type-device-profiles": "Apparaatprofielen", + "clear-selected-profiles": "Geselecteerde profielen wissen", + "list-of-device-profiles": "{ count, plural, =1 {Een apparaatprofiel} other {Lijst van # apparaatprofielen} }", + "device-profile-name-starts-with": "Apparaatprofielen met namen die beginnen met '{{prefix}}'", + "type-asset-profile": "Assetprofiel", + "type-asset-profiles": "Assetprofielen", + "list-of-asset-profiles": "{ count, plural, =1 {Een assetprofiel} other {Lijst van # assetprofielen} }", + "asset-profile-name-starts-with": "Assetprofielen met namen die beginnen met '{{prefix}}'", + "type-asset": "Asset", + "type-assets": "Assets", + "list-of-assets": "{ count, plural, =1 {Een asset} other {Lijst van # assets} }", + "asset-name-starts-with": "Assets met namen die beginnen met '{{prefix}}'", + "type-entity-view": "Entiteitsweergave", + "type-entity-views": "Entiteitsweergaven", + "list-of-entity-views": "{ count, plural, =1 {Een entiteitsweergave} other {Lijst van # entiteitsweergaven} }", + "entity-view-name-starts-with": "Entiteitsweergaven met namen die beginnen met '{{prefix}}'", + "type-rule": "Regel", + "type-rules": "Regels", + "list-of-rules": "{ count, plural, =1 {Een regel} other {Lijst van # regels} }", + "rule-name-starts-with": "Regels met namen die beginnen met '{{prefix}}'", + "type-plugin": "Plugin", + "type-plugins": "Plugins", + "list-of-plugins": "{ count, plural, =1 {Een plugin} other {Lijst van # plugins} }", + "plugin-name-starts-with": "Plugins met namen die beginnen met '{{prefix}}'", + "type-tenant": "Tenant", + "type-tenants": "Tenants", + "list-of-tenants": "{ count, plural, =1 {Een tenant} other {Lijst van # tenants} }", + "tenant-name-starts-with": "Tenants met namen die beginnen met '{{prefix}}'", + "type-tenant-profile": "Tenantprofiel", + "type-tenant-profiles": "Tenantprofielen", + "list-of-tenant-profiles": "{ count, plural, =1 {Een tenantprofiel} other {Lijst van # tenantprofielen} }", + "tenant-profile-name-starts-with": "Tenantprofielen met namen die beginnen met '{{prefix}}'", + "type-customer": "Klant", + "type-customers": "Klanten", + "list-of-customers": "{ count, plural, =1 {Eén klant} other {Lijst van # klanten} }", + "customer-name-starts-with": "Klanten met namen die beginnen met '{{prefix}}'", + "type-user": "Gebruiker", + "type-users": "Gebruikers", + "list-of-users": "{ count, plural, =1 {Eén gebruiker} other {Lijst van # gebruikers} }", + "user-name-starts-with": "Gebruikers met namen die beginnen met '{{prefix}}'", + "type-dashboard": "Dashboard", + "type-dashboards": "Dashboards", + "list-of-dashboards": "{ count, plural, =1 {Eén dashboard} other {Lijst van # dashboards} }", + "dashboard-name-starts-with": "Dashboards met namen die beginnen met '{{prefix}}'", + "type-alarm": "Alarm", + "type-alarms": "Alarmen", + "list-of-alarms": "{ count, plural, =1 {Eén alarm} other {Lijst van # alarmen} }", + "alarm-name-starts-with": "Alarmen met namen die beginnen met '{{prefix}}'", + "type-rulechain": "Regelketen", + "type-rulechains": "Regelketens", + "list-of-rulechains": "{ count, plural, =1 {Eén regelketen} other {Lijst van # regelketens} }", + "rulechain-name-starts-with": "Regelketens met namen die beginnen met '{{prefix}}'", + "type-rulenode": "Regelknooppunt", + "type-rulenodes": "Regelknooppunten", + "list-of-rulenodes": "{ count, plural, =1 {Eén regelknooppunt} other {Lijst van # regelknooppunten} }", + "rulenode-name-starts-with": "Regelknooppunten met namen die beginnen met '{{prefix}}'", + "type-current-customer": "Huidige klant", + "type-current-tenant": "Huidige tenant", + "type-current-user": "Huidige gebruiker", + "type-current-user-owner": "Eigenaar huidige gebruiker", + "type-calculated-field": "Berekend veld", + "type-calculated-fields": "Berekende velden", + "type-widgets-bundle": "Widget-bundel", + "type-widgets-bundles": "Widget-bundels", + "list-of-widgets-bundles": "{ count, plural, =1 {Eén widget-bundel} other {Lijst van # widget-bundels} }", + "type-widget": "Widget", + "type-widgets": "Widgets", + "list-of-widgets": "{ count, plural, =1 {Eén widget} other {Lijst van # widgets} }", + "search": "Zoek entiteiten", + "selected-entities": "{ count, plural, =1 {1 entiteit} other {# entiteiten} } geselecteerd", + "entity-name": "Entiteitnaam", + "entity-label": "Entiteitlabel", + "details": "Entiteitdetails", + "no-entities-prompt": "Geen entiteiten gevonden", + "no-data": "Geen gegevens om weer te geven", + "columns-to-display": "Kolommen om weer te geven", + "type-api-usage-state": "API-gebruikstoestand", + "type-edge": "Edge", + "type-edges": "Edges", + "list-of-edges": "{ count, plural, =1 {Eén edge} other {Lijst van # edges} }", + "edge-name-starts-with": "Edges met namen die beginnen met '{{prefix}}'", + "version-conflict": { + "message": "Wilt u de bestaande versie overschrijven of de wijzigingen negeren en de laatste versie laden?", + "link": "U kunt uw versie van de {{entityType}} downloaden via deze", + "overwrite": "Versie overschrijven", + "discard": "Wijzigingen negeren" + }, + "type-tb-resource": "Resource", + "type-tb-resources": "Resources", + "list-of-tb-resources": "{ count, plural, =1 {Eén resource} other {Lijst van # resources} }", + "type-ota-package": "OTA-pakket", + "type-rpc": "RPC", + "type-queue": "Wachtrij", + "type-queue-stats": "Wachtrijstatistieken", + "type-queues-stats": "Wachtrijenstatistieken", + "type-notification": "Melding", + "type-notification-rule": "Meldingsregel", + "type-notification-rules": "Meldingsregels", + "list-of-notification-rules": "{ count, plural, =1 {Eén meldingsregel} other {Lijst van # meldingsregels} }", + "type-notification-target": "Meldingsontvanger", + "type-notification-targets": "Meldingsontvangers", + "list-of-notification-targets": "{ count, plural, =1 {Eén meldingsontvanger} other {Lijst van # meldingsontvangers} }", + "type-notification-request": "Meldingsverzoek", + "type-notification-template": "Meldingssjabloon", + "type-notification-templates": "Meldingssjablonen", + "list-of-notification-templates": "{ count, plural, =1 {Eén meldingssjabloon} other {Lijst van # meldingssjablonen} }", + "link": "link", + "type-oauth2-client": "OAuth 2.0-client", + "type-oauth2-clients": "OAuth 2.0-clients", + "list-of-oauth2-clients": "{ count, plural, =1 {Eén OAuth 2.0-client} other {Lijst van # OAuth 2.0-clients} }", + "type-domain": "Domein", + "type-domains": "Domeinen", + "list-of-domains": "{ count, plural, =1 {Eén domein} other {Lijst van # domeinen} }", + "type-mobile-app": "Mobiele applicatie", + "type-mobile-apps": "Mobiele applicaties", + "list-of-mobile-apps": "{ count, plural, =1 {Eén mobiele applicatie} other {Lijst van # mobiele applicaties} }", + "type-mobile-app-bundle": "Mobiele bundel", + "type-mobile-app-bundles": "Mobiele bundels", + "list-of-mobile-app-bundles": "{ count, plural, =1 {Eén mobiele bundel} other {Lijst van # mobiele bundels} }" + }, + "entity-field": { + "created-time": "Aangemaakt op", + "name": "Naam", + "type": "Type", + "first-name": "Voornaam", + "last-name": "Achternaam", + "email": "E-mail", + "title": "Titel", + "country": "Land", + "state": "Staat / Provincie", + "city": "Stad", + "address": "Adres", + "address2": "Adres 2", + "zip": "Postcode", + "phone": "Telefoon", + "label": "Label", + "queue-name": "Naam wachtrij", + "service-id": "Service-ID", + "owner-name": "Naam eigenaar", + "owner-type": "Eigenaartype" + }, + "entity-view": { + "entity-view": "Entity view", + "entity-view-required": "Entity view is vereist.", + "entity-views": "Entity views", + "management": "Beheer van Entity Views", + "view-entity-views": "Bekijk Entity Views", + "entity-view-alias": "Entity view-alias", + "aliases": "Entity view-aliasen", + "no-alias-matching": "'{{alias}}' niet gevonden.", + "no-aliases-found": "Geen aliasen gevonden.", + "no-key-matching": "'{{key}}' niet gevonden.", + "no-keys-found": "Geen sleutels gevonden.", + "create-new-alias": "Maak een nieuwe aan!", + "create-new-key": "Maak een nieuwe aan!", + "duplicate-alias-error": "Dubbele alias gevonden '{{alias}}'.
Entity view-aliasen moeten uniek zijn binnen het dashboard.", + "configure-alias": "Configureer alias '{{alias}}'", + "no-entity-views-matching": "Geen entity views gevonden die overeenkomen met '{{entity}}'.", + "public": "Openbaar", + "alias": "Alias", + "alias-required": "Entity view-alias is vereist.", + "remove-alias": "Verwijder entity view-alias", + "add-alias": "Voeg entity view-alias toe", + "name-starts-with": "Entity view naamexpressie", + "help-text": "Gebruik '%' naar behoefte: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list": "Entity view-lijst", + "use-entity-view-name-filter": "Gebruik filter", + "entity-view-list-empty": "Geen entity views geselecteerd.", + "entity-view-name-filter-required": "Entity view naamfilter is vereist.", + "entity-view-name-filter-no-entity-view-matched": "Geen entity views gevonden die beginnen met '{{entityView}}'.", + "add": "Voeg entity view toe", + "entity-view-public": "Entity view is openbaar", + "assign-to-customer": "Toewijzen aan klant", + "assign-entity-view-to-customer": "Wijs entity view(s) toe aan klant", + "assign-entity-view-to-customer-text": "Selecteer de entity views die u aan de klant wilt toewijzen", + "no-entity-views-text": "Geen entity views gevonden", + "assign-to-customer-text": "Selecteer de klant om de entity view(s) aan toe te wijzen", + "entity-view-details": "Details van entity view", + "add-entity-view-text": "Voeg nieuwe entity view toe", + "delete": "Verwijder entity view", + "assign-entity-views": "Wijs entity views toe", + "assign-entity-views-text": "{ count, plural, =1 {1 entity view} other {# entity views} } toewijzen aan klant", + "delete-entity-views": "Verwijder entity views", + "make-public": "Maak entity view openbaar", + "make-private": "Maak entity view privé", + "unassign-from-customer": "Ontkoppel van klant", + "unassign-entity-views": "Ontkoppel entity views", + "unassign-entity-views-action-title": "{ count, plural, =1 {1 entity view} other {# entity views} } ontkoppelen van klant", + "assign-new-entity-view": "Wijs nieuwe entity view toe", + "delete-entity-view-title": "Weet u zeker dat u de entity view '{{entityViewName}}' wilt verwijderen?", + "delete-entity-view-text": "Wees voorzichtig, na bevestiging wordt de entity view en alle gerelateerde gegevens permanent verwijderd.", + "delete-entity-views-title": "Weet u zeker dat u { count, plural, =1 {1 entity view} other {# entity views} } wilt verwijderen?", + "delete-entity-views-action-title": "Verwijder { count, plural, =1 {1 entity view} other {# entity views} }", + "delete-entity-views-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde entity views verwijderd en worden gerelateerde gegevens permanent verwijderd.", + "make-public-entity-view-title": "Weet u zeker dat u de entity view '{{entityViewName}}' openbaar wilt maken?", + "make-public-entity-view-text": "Na bevestiging wordt de entity view en alle gegevens openbaar en toegankelijk voor anderen.", + "make-private-entity-view-title": "Weet u zeker dat u de entity view '{{entityViewName}}' privé wilt maken?", + "make-private-entity-view-text": "Na bevestiging wordt de entity view en alle gegevens privé en niet langer toegankelijk voor anderen.", + "unassign-entity-view-title": "Weet u zeker dat u de entity view '{{entityViewName}}' wilt ontkoppelen?", + "unassign-entity-view-text": "Na bevestiging wordt de entity view ontkoppeld en niet langer toegankelijk voor de klant.", + "unassign-entity-view": "Ontkoppel entity view", + "unassign-entity-views-title": "Weet u zeker dat u { count, plural, =1 {1 entity view} other {# entity views} } wilt ontkoppelen?", + "unassign-entity-views-text": "Na bevestiging worden alle geselecteerde entity views ontkoppeld en niet langer toegankelijk voor de klant.", + "entity-view-type": "Entity view-type", + "entity-view-type-required": "Entity view-type is vereist.", + "select-entity-view-type": "Selecteer entity view-type", + "enter-entity-view-type": "Voer entity view-type in", + "any-entity-view": "Elke entity view", + "no-entity-view-types-matching": "Geen entity view-types gevonden die overeenkomen met '{{entitySubtype}}'.", + "entity-view-type-list-empty": "Geen entity view-types geselecteerd.", + "entity-view-types": "Entity view-types", + "created-time": "Aangemaakt op", + "name": "Naam", + "name-required": "Naam is vereist.", + "name-max-length": "Naam moet minder dan 256 tekens zijn", + "type-max-length": "Entity view-type moet minder dan 256 tekens zijn", + "description": "Beschrijving", + "events": "Gebeurtenissen", + "details": "Details", + "copyId": "Kopieer entity view-ID", + "idCopiedMessage": "Entity view-ID is gekopieerd naar klembord", + "assignedToCustomer": "Toegewezen aan klant", + "unable-entity-view-device-alias-title": "Kan entity view-alias niet verwijderen", + "unable-entity-view-device-alias-text": "Device-alias '{{entityViewAlias}}' kan niet worden verwijderd omdat deze wordt gebruikt door de volgende widget(s):
{{widgetsList}}", + "select-entity-view": "Selecteer entity view", + "start-ts": "Starttijd", + "end-ts": "Eindtijd", + "date-limits": "Datumbeperkingen", + "client-attributes": "Clientattributen", + "shared-attributes": "Gedeelde attributen", + "server-attributes": "Serverattributen", + "timeseries": "Tijdreeks", + "client-attributes-placeholder": "Clientattributen", + "shared-attributes-placeholder": "Gedeelde attributen", + "server-attributes-placeholder": "Serverattributen", + "timeseries-placeholder": "Tijdreeks", + "target-entity": "Doelentiteit", + "attributes-propagation": "Attributenpropagatie", + "attributes-propagation-hint": "Entity View zal automatisch gespecificeerde attributen kopiëren van de Doelentiteit telkens wanneer u deze entity view opslaat of bijwerkt. Omwille van prestatie-redenen worden attributen van de doelentiteit niet automatisch gesynchroniseerd bij elke wijziging. U kunt automatische propagatie inschakelen door een \"copy to view\" regelnode te configureren in uw regelketen en \"Post attributes\" en \"Attributes Updated\" berichten te koppelen aan de nieuwe regelnode.", + "timeseries-data": "Tijdreeksgegevens", + "timeseries-data-hint": "Configureer tijdreeksgegevenssleutels van de doelentiteit die toegankelijk zullen zijn voor de entity view. Deze tijdreeksgegevens zijn alleen-lezen.", + "search": "Zoek entity views", + "selected-entity-views": "{ count, plural, =1 {1 entity view} other {# entity views} } geselecteerd", + "assign-entity-view-to-edge": "Wijs entity view(s) toe aan Edge", + "assign-entity-view-to-edge-text": "Selecteer de entity views die u aan de edge wilt toewijzen", + "unassign-entity-view-from-edge-title": "Weet u zeker dat u de entity view '{{entityViewName}}' wilt ontkoppelen?", + "unassign-entity-view-from-edge-text": "Na bevestiging wordt de entity view ontkoppeld en niet langer toegankelijk door de edge.", + "unassign-entity-views-from-edge-action-title": "{ count, plural, =1 {1 entity view} other {# entity views} } ontkoppelen van edge", + "unassign-entity-view-from-edge": "Ontkoppel entity view", + "unassign-entity-views-from-edge-title": "Weet u zeker dat u { count, plural, =1 {1 entity view} other {# entity views} } wilt ontkoppelen?", + "unassign-entity-views-from-edge-text": "Na bevestiging worden alle geselecteerde entity views ontkoppeld en niet langer toegankelijk door de edge." + }, + "event": { + "event-type": "Gebeurtenistype", + "events-filter": "Gebeurtenissenfilter", + "clean-events": "Gebeurtenissen wissen", + "type-error": "Fout", + "type-lc-event": "Levenscyclusgebeurtenis", + "type-stats": "Statistieken", + "type-debug-rule-node": "Debug", + "type-debug-rule-chain": "Debug", + "type-debug-calculated-field": "Debug", + "arguments": "Argumenten", + "result": "Resultaat", + "no-events-prompt": "Geen gebeurtenissen gevonden", + "error": "Fout", + "alarm": "Alarm", + "event-time": "Gebeurtenistijd", + "server": "Server", + "body": "Inhoud", + "method": "Methode", + "type": "Type", + "metadata": "Metadata", + "message": "Bericht", + "message-id": "Bericht-ID", + "copy-message-id": "Kopieer bericht-ID", + "message-type": "Berichttype", + "data-type": "Gegevenstype", + "relation-type": "Relatietype", + "data": "Gegevens", + "event": "Gebeurtenis", + "status": "Status", + "success": "Succes", + "failed": "Mislukt", + "messages-processed": "Verwerkte berichten", + "max-messages-processed": "Maximum verwerkte berichten", + "min-messages-processed": "Minimum verwerkte berichten", + "errors-occurred": "Fouten opgetreden", + "max-errors-occurred": "Maximum fouten opgetreden", + "min-errors-occurred": "Minimum fouten opgetreden", + "min-value": "Minimale waarde is 0.", + "all-events": "Alle", + "has-error": "Heeft fout", + "entity-id": "Entiteit-ID", + "copy-entity-id": "Kopieer entiteit-ID", + "entity-type": "Entiteittype", + "clear-filter": "Filter wissen", + "clear-request-title": "Alle gebeurtenissen wissen", + "clear-request-text": "Weet u zeker dat u alle gebeurtenissen wilt wissen?", + "started": "Gestart", + "updated": "Bijgewerkt", + "stopped": "Gestopt" + }, + "extension": { + "extensions": "Extensies", + "selected-extensions": "{ count, plural, =1 {1 extensie} other {# extensies} } geselecteerd", + "type": "Type", + "key": "Sleutel", + "value": "Waarde", + "id": "Id", + "extension-id": "Extensie-id", + "extension-type": "Extensietype", + "transformer-json": "JSON *", + "unique-id-required": "Huidige extensie-id bestaat al.", + "delete": "Verwijder extensie", + "add": "Voeg extensie toe", + "edit": "Bewerk extensie", + "delete-extension-title": "Weet u zeker dat u de extensie '{{extensionId}}' wilt verwijderen?", + "delete-extension-text": "Wees voorzichtig, na bevestiging wordt de extensie en alle gerelateerde gegevens onherroepelijk verwijderd.", + "delete-extensions-title": "Weet u zeker dat u { count, plural, =1 {1 extensie} other {# extensies} } wilt verwijderen?", + "delete-extensions-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde extensies verwijderd.", + "converters": "Converters", + "converter-id": "Converter-id", + "configuration": "Configuratie", + "converter-configurations": "Converterconfiguraties", + "token": "Beveiligingstoken", + "add-converter": "Voeg converter toe", + "add-config": "Voeg converterconfiguratie toe", + "device-name-expression": "Apparaatnaamexpressie", + "device-type-expression": "Apparaattype-expressie", + "custom": "Aangepast", + "to-double": "Naar double", + "transformer": "Transformer", + "json-required": "Transformer JSON is verplicht.", + "json-parse": "Kan transformer JSON niet parseren.", + "attributes": "Attributen", + "add-attribute": "Voeg attribuut toe", + "add-map": "Voeg mappingelement toe", + "timeseries": "Tijdreeks", + "add-timeseries": "Voeg tijdreeks toe", + "field-required": "Veld is verplicht", + "brokers": "Brokers", + "add-broker": "Voeg broker toe", + "host": "Host", + "port": "Poort", + "port-range": "Poort moet tussen 1 en 65535 liggen.", + "ssl": "Ssl", + "credentials": "Referenties", + "username": "Gebruikersnaam", + "password": "Wachtwoord", + "retry-interval": "Herhaalinterval in milliseconden", + "anonymous": "Anoniem", + "basic": "Basis", + "pem": "PEM", + "ca-cert": "CA-certificaatbestand *", + "private-key": "Privésleutelbestand *", + "cert": "Certificaatbestand *", + "no-file": "Geen bestand geselecteerd.", + "drop-file": "Sleep een bestand of klik om een bestand te selecteren en te uploaden.", + "mapping": "Mapping", + "topic-filter": "Onderwerpfilter", + "converter-type": "Convertertype", + "converter-json": "Json", + "json-name-expression": "Apparaatnaam JSON-expressie", + "topic-name-expression": "Apparaatnaam topic-expressie", + "json-type-expression": "Apparaattype JSON-expressie", + "topic-type-expression": "Apparaattype topic-expressie", + "attribute-key-expression": "Attribuutsleutelexpressie", + "attr-json-key-expression": "Attribuutsleutel JSON-expressie", + "attr-topic-key-expression": "Attribuutsleutel topic-expressie", + "request-id-expression": "Verzoek-id-expressie", + "request-id-json-expression": "Verzoek-id JSON-expressie", + "request-id-topic-expression": "Verzoek-id topic-expressie", + "response-topic-expression": "Antwoord topic-expressie", + "value-expression": "Waarde-expressie", + "topic": "Onderwerp", + "timeout": "Timeout in milliseconden", + "converter-json-required": "Converter JSON is verplicht.", + "converter-json-parse": "Kan converter JSON niet parseren.", + "filter-expression": "Filterexpressie", + "connect-requests": "Verbindingsverzoeken", + "add-connect-request": "Voeg verbindingsverzoek toe", + "disconnect-requests": "Verbrekingsverzoeken", + "add-disconnect-request": "Voeg verbreekverzoek toe", + "attribute-requests": "Attribuutverzoeken", + "add-attribute-request": "Voeg attribuutverzoek toe", + "attribute-updates": "Attribuutupdates", + "add-attribute-update": "Voeg attribuutupdate toe", + "server-side-rpc": "Server-side RPC", + "add-server-side-rpc-request": "Voeg server-side RPC-verzoek toe", + "device-name-filter": "Apparaatnaamfilter", + "attribute-filter": "Attribuutfilter", + "method-filter": "Methodfilter", + "request-topic-expression": "Verzoekonderwerp-expressie", + "response-timeout": "Antwoord-timeout in milliseconden", + "topic-expression": "Onderwerp-expressie", + "client-scope": "Clientscope", + "add-device": "Voeg apparaat toe", + "opc-server": "Servers", + "opc-add-server": "Voeg server toe", + "opc-add-server-prompt": "Voeg een server toe", + "opc-application-name": "Applicatienaam", + "opc-application-uri": "Applicatie-URI", + "opc-scan-period-in-seconds": "Scanperiode in seconden", + "opc-security": "Beveiliging", + "opc-identity": "Identiteit", + "opc-keystore": "Sleutellocatie", + "opc-type": "Type", + "opc-keystore-type": "Type", + "opc-keystore-location": "Locatie *", + "opc-keystore-password": "Wachtwoord", + "opc-keystore-alias": "Alias", + "opc-keystore-key-password": "Sleutelwachtwoord", + "opc-device-node-pattern": "Apparaatnodepatroon", + "opc-device-name-pattern": "Apparaatnaam patroon", + "modbus-server": "Servers/slaves", + "modbus-add-server": "Voeg server/slave toe", + "modbus-add-server-prompt": "Voeg server/slave toe", + "modbus-transport": "Transport", + "modbus-tcp-reconnect": "Automatisch opnieuw verbinden", + "modbus-rtu-over-tcp": "RTU via TCP", + "modbus-port-name": "Seriële poortnaam", + "modbus-encoding": "Codering", + "modbus-parity": "Pariteit", + "modbus-baudrate": "Baudrate", + "modbus-databits": "Databits", + "modbus-stopbits": "Stopbits", + "modbus-databits-range": "Databits moeten tussen 7 en 8 liggen.", + "modbus-stopbits-range": "Stopbits moeten tussen 1 en 2 liggen.", + "modbus-unit-id": "Unit-ID", + "modbus-unit-id-range": "Unit-ID moet tussen 1 en 247 liggen.", + "modbus-device-name": "Apparaatnaam", + "modbus-poll-period": "Pollingperiode (ms)", + "modbus-attributes-poll-period": "Attribuut pollingperiode (ms)", + "modbus-timeseries-poll-period": "Tijdreeks pollingperiode (ms)", + "modbus-poll-period-range": "Pollingperiode moet een positieve waarde zijn.", + "modbus-tag": "Tag", + "modbus-function": "Functie", + "modbus-register-address": "Registeradres", + "modbus-register-address-range": "Registeradres moet tussen 0 en 65535 liggen.", + "modbus-register-bit-index": "Bitindex", + "modbus-register-bit-index-range": "Bitindex moet tussen 0 en 15 liggen.", + "modbus-register-count": "Registeraantal", + "modbus-register-count-range": "Registeraantal moet een positieve waarde zijn.", + "modbus-byte-order": "Bytevolgorde", + "sync": { + "status": "Status", + "sync": "Synchroniseren", + "not-sync": "Niet gesynchroniseerd", + "last-sync-time": "Laatste synchronisatietijd", + "not-available": "Niet beschikbaar" + }, + "export-extensions-configuration": "Exporteer extensieconfiguratie", + "import-extensions-configuration": "Importeer extensieconfiguratie", + "import-extensions": "Importeer extensies", + "import-extension": "Importeer extensie", + "export-extension": "Exporteer extensie", + "file": "Extensiebestand", + "invalid-file-error": "Ongeldig extensiebestand" + }, + "feature": { + "advanced-features": "Geavanceerde functies" + }, + "filter": { + "add": "Filter toevoegen", + "edit": "Filter bewerken", + "name": "Filternaam", + "name-required": "Filternaam is vereist.", + "duplicate-filter": "Er bestaat al een filter met dezelfde naam.", + "filters": "Filters", + "unable-delete-filter-title": "Kan filter niet verwijderen", + "unable-delete-filter-text": "Filter '{{filter}}' kan niet worden verwijderd omdat deze wordt gebruikt door de volgende widget(s):
{{widgetsList}}", + "duplicate-filter-error": "Duplicaatfilter gevonden '{{filter}}'.
Filters moeten uniek zijn binnen het dashboard.", + "missing-key-filters-error": "Sleutelfilters ontbreken voor filter '{{filter}}'.", + "filter": "Filter", + "editable": "Bewerkbaar", + "no-filters-found": "Geen filters gevonden.", + "no-filter-text": "Geen filter opgegeven", + "add-filter-prompt": "Voeg een filter toe", + "no-filter-matching": "'{{filter}}' niet gevonden.", + "create-new-filter": "Maak een nieuwe aan!", + "create-new": "Maak nieuw", + "filter-required": "Filter is vereist.", + "operation": { + "operation": "Operatie", + "equal": "gelijk aan", + "not-equal": "niet gelijk aan", + "starts-with": "begint met", + "ends-with": "eindigt met", + "contains": "bevat", + "not-contains": "bevat niet", + "greater": "groter dan", + "less": "kleiner dan", + "greater-or-equal": "groter of gelijk aan", + "less-or-equal": "kleiner of gelijk aan", + "and": "en", + "or": "of", + "in": "in", + "not-in": "niet in" + }, + "ignore-case": "negeer hoofdlettergebruik", + "value": "Waarde", + "remove-filter": "Verwijder filter", + "duplicate-filter-action": "Dupliceer filter", + "preview": "Filtervoorbeeld", + "no-filters": "Geen filters geconfigureerd", + "add-filter": "Filter toevoegen", + "add-complex-filter": "Complex filter toevoegen", + "add-complex": "Voeg complex toe", + "complex-filter": "Complex filter", + "edit-complex-filter": "Complex filter bewerken", + "edit-filter-user-params": "Filtergebruikersparameters bewerken", + "filter-user-params": "Filtergebruikersparameters", + "user-parameters": "Gebruikersparameters", + "display-label": "Label om weer te geven", + "order-priority": "Volgordeprioriteit van veld", + "key-filter": "Sleutelfilter", + "key-filters": "Sleutelfilters", + "key-name": "Sleutelnaam", + "key-name-required": "Sleutelnaam is vereist.", + "key-type": { + "key-type": "Sleuteltype", + "attribute": "Attribuut", + "timeseries": "Tijdreeks", + "entity-field": "Entiteitsveld", + "constant": "Constante", + "client-attribute": "Clientattribuut", + "server-attribute": "Serverattribuut", + "shared-attribute": "Gedeeld attribuut" + }, + "value-type": { + "value-type": "Waarde type", + "string": "Tekenreeks", + "numeric": "Numeriek", + "boolean": "Boolean", + "date-time": "Datumtijd" + }, + "value-type-required": "Waarde type is vereist.", + "key-value-type-change-title": "Weet u zeker dat u het waardetype wilt wijzigen?", + "key-value-type-change-message": "Als u bevestigt, worden alle ingevoerde sleutelfilters verwijderd.", + "no-key-filters": "Geen sleutelfilters geconfigureerd", + "add-key-filter": "Sleutelfilter toevoegen", + "remove-key-filter": "Sleutelfilter verwijderen", + "edit-key-filter": "Sleutelfilter bewerken", + "date": "Datum", + "time": "Tijd", + "current-tenant": "Huidige huurder", + "current-customer": "Huidige klant", + "current-user": "Huidige gebruiker", + "current-device": "Huidig apparaat", + "default-value": "Standaardwaarde", + "default-comma-separated-values": "Standaard komma-gescheiden waarden", + "dynamic-source-type": "Dynamische bron type", + "dynamic-value": "Dynamische waarde", + "no-dynamic-value": "Geen dynamische waarde", + "source-attribute": "Bronattribuut", + "switch-to-dynamic-value": "Overschakelen naar dynamische waarde", + "switch-to-default-value": "Overschakelen naar standaardwaarde", + "inherit-owner": "Overnemen van eigenaar", + "source-attribute-not-set": "Als het bronattribuut niet is ingesteld" + }, + "fullscreen": { + "expand": "Vergroot naar volledig scherm", + "exit": "Verlaat volledig scherm", + "toggle": "Schakel volledig schermmodus in", + "fullscreen": "Volledig scherm" + }, + "function": { + "function": "Functie" + }, + "gateway": { + "gateway-name": "Gateway naam", + "gateway-name-required": "Gateway naam is vereist.", + "gateways": "Gateways", + "create-new-gateway": "Maak een nieuwe gateway aan", + "create-new-gateway-text": "Weet u zeker dat u een nieuwe gateway wilt aanmaken met naam: '{{gatewayName}}'?", + "launch-command": "Startcommando", + "no-gateway-found": "Geen gateway gevonden.", + "no-gateway-matching": "'{{item}}' niet gevonden." + }, + "grid": { + "delete-item-title": "Weet u zeker dat u dit item wilt verwijderen?", + "delete-item-text": "Wees voorzichtig, na bevestiging worden dit item en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-items-title": "Weet u zeker dat u { count, plural, =1 {1 item} other {# items} } wilt verwijderen?", + "delete-items-action-title": "Verwijder { count, plural, =1 {1 item} other {# items} }", + "delete-items-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde items verwijderd en zijn alle gerelateerde gegevens onherstelbaar.", + "add-item-text": "Nieuw item toevoegen", + "no-items-text": "Geen items gevonden", + "item-details": "Itemdetails", + "delete-item": "Item verwijderen", + "delete-items": "Items verwijderen", + "scroll-to-top": "Scroll naar boven" + }, + "help": { + "goto-help-page": "Ga naar help pagina", + "show-help": "Toon help" + }, + "home": { + "home": "Home", + "profile": "Profiel", + "logout": "Uitloggen", + "menu": "Menu", + "avatar": "Avatar", + "open-user-menu": "Open gebruikersmenu" + }, + "file-input": { + "browse-file": "Blader bestand", + "browse-files": "Blader bestanden" + }, + "image": { + "gallery": "Afbeeldingengalerij", + "search": "Afbeelding zoeken", + "selected-images": "{ count, plural, =1 {1 afbeelding} other {# afbeeldingen} } geselecteerd", + "created-time": "Aangemaakt op", + "name": "Naam", + "name-required": "Naam is vereist.", + "resolution": "Resolutie", + "size": "Grootte", + "system": "Systeem", + "download-image": "Download afbeelding", + "export-image": "Exporteer afbeelding naar JSON", + "import-image": "Importeer afbeelding uit JSON", + "upload-image": "Upload afbeelding", + "edit-image": "Bewerk afbeelding", + "image-details": "Afbeeldingsdetails", + "no-images": "Geen afbeeldingen gevonden", + "delete-image": "Verwijder afbeelding", + "delete-image-title": "Weet u zeker dat u afbeelding '{{imageTitle}}' wilt verwijderen?", + "delete-image-text": "Wees voorzichtig, na bevestiging wordt de afbeelding onherstelbaar verwijderd.", + "delete-images-title": "Weet u zeker dat u { count, plural, =1 {1 afbeelding} other {# afbeeldingen} } wilt verwijderen?", + "delete-images-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde afbeeldingen verwijderd en zijn alle gerelateerde gegevens onherstelbaar.", + "list-mode": "Lijstweergave", + "grid-mode": "Rasterweergave", + "image-preview": "Afbeelding voorbeeld", + "update-image": "Afbeelding bijwerken", + "export-failed-error": "Kan afbeelding niet exporteren: {{error}}", + "image-json-file": "Afbeelding JSON bestand", + "invalid-image-json-file-error": "Kan afbeelding niet importeren uit JSON: Ongeldige afbeelding JSON datastructuur.", + "image-is-in-use": "Afbeelding wordt gebruikt door andere entiteiten", + "images-are-in-use": "Afbeeldingen worden gebruikt door andere entiteiten", + "image-is-in-use-text": "De afbeelding '{{title}}' is niet verwijderd omdat deze wordt gebruikt door de volgende entiteiten:", + "images-are-in-use-text": "Niet alle afbeeldingen zijn verwijderd omdat ze worden gebruikt door andere entiteiten.
U kunt de verwijzingen bekijken door op de knop Referenties in de overeenkomstige afbeeldingsrij te klikken.
Als u deze afbeeldingen toch wilt verwijderen, selecteer ze dan in de onderstaande tabel en klik op de knop Geselecteerde verwijderen.", + "delete-image-in-use-text": "Als u de afbeelding toch wilt verwijderen, klik dan op de knop Toch verwijderen.", + "system-entities": "Systeementiteiten:", + "entities": "entiteiten:", + "references": "Referenties", + "include-system-images": "Systeemafbeeldingen opnemen", + "clear-image": "Afbeelding wissen", + "no-image": "Geen afbeelding", + "no-image-selected": "Geen afbeelding geselecteerd", + "browse-from-gallery": "Blader door galerij", + "set-link": "Link instellen", + "image-link": "Afbeeldingslink", + "link": "Link", + "copy-image-link": "Kopieer afbeeldingslink", + "embed-image": "Afbeelding insluiten", + "embed-to-html": "Insluiten in HTML", + "embed-to-html-hint": "Deze functie maakt de link beschikbaar voor elke niet-geautoriseerde gebruiker.", + "embed-to-html-text": "Gebruik de volgende code om een afbeelding in te sluiten in componenten op basis van eenvoudige HTML.
Dergelijke componenten omvatten HTML-kaartwidgets, celinhoudfuncties, enz.", + "embed-to-angular-template": "Insluiten in Angular HTML-sjabloon", + "embed-to-angular-template-text": "Gebruik de volgende code om een afbeelding in te sluiten in de Angular HTML-sjabloon die wordt gebruikt voor componenten.
Dergelijke componenten omvatten de Markdown-widget, HTML-sectie in de widget-editor, aangepaste acties, enz." + }, + "image-input": { + "drop-images-or": "Sleep afbeeldingen of", + "drag-and-drop": "Slepen & Neerzetten", + "or": "of", + "browse": "Bladeren", + "no-images": "Geen afbeeldingen geselecteerd", + "images": "afbeeldingen" + }, + "import": { + "no-file": "Geen bestand geselecteerd", + "drop-file": "Sleep een JSON-bestand of klik om een bestand te uploaden.", + "drop-json-file-or": "Sleep een JSON-bestand of", + "drop-file-csv": "Sleep een CSV-bestand of klik om een bestand te uploaden.", + "drop-file-csv-or": "Sleep een CSV-bestand of", + "column-value": "Waarde", + "column-title": "Titel", + "column-example": "Voorbeeldwaarde", + "column-key": "Attribuut/telemetriesleutel", + "credentials": "Referenties", + "csv-delimiter": "CSV-scheidingsteken", + "csv-first-line-header": "Eerste regel bevat kolomnamen", + "csv-update-data": "Attribuut/telemetrie bijwerken", + "details": "Details", + "import-csv-number-columns-error": "Een bestand moet minimaal twee kolommen bevatten", + "import-csv-invalid-format-error": "Ongeldig bestandsformaat. Regel: '{{line}}'", + "column-type": { + "name": "Naam", + "type": "Type", + "label": "Label", + "column-type": "Kolomtype", + "client-attribute": "Clientattribuut", + "shared-attribute": "Gedeeld attribuut", + "server-attribute": "Serverattribuut", + "timeseries": "Tijdreeks", + "entity-field": "Entiteitsveld", + "access-token": "Toegangstoken", + "x509": "X.509", + "mqtt": { + "client-id": "MQTT-client-ID", + "user-name": "MQTT-gebruikersnaam", + "password": "MQTT-wachtwoord" + }, + "lwm2m": { + "client-endpoint": "LwM2M endpoint clientnaam", + "security-config-mode": "LwM2M beveiligingsmodus", + "client-identity": "LwM2M clientidentiteit", + "client-key": "LwM2M clientsleutel", + "client-cert": "LwM2M client publieke sleutel", + "bootstrap-server-security-mode": "LwM2M bootstrap server beveiligingsmodus", + "bootstrap-server-secret-key": "LwM2M bootstrap server geheime sleutel", + "bootstrap-server-public-key-id": "LwM2M bootstrap server publieke sleutel of id", + "lwm2m-server-security-mode": "LwM2M server beveiligingsmodus", + "lwm2m-server-secret-key": "LwM2M server geheime sleutel", + "lwm2m-server-public-key-id": "LwM2M server publieke sleutel of id" + }, + "snmp": { + "host": "SNMP-host", + "port": "SNMP-poort", + "version": "SNMP-versie (v1, v2c of v3)", + "community-string": "SNMP community string" + }, + "isgateway": "Is Gateway", + "activity-time-from-gateway-device": "Activiteitstijd van gatewayapparaat", + "description": "Beschrijving", + "routing-key": "Edge sleutel", + "secret": "Edge geheim" + }, + "stepper-text": { + "select-file": "Selecteer een bestand", + "configuration": "Importconfiguratie", + "column-type": "Selecteer kolomtype", + "creat-entities": "Nieuwe entiteiten aanmaken" + }, + "message": { + "create-entities": "{{count}} nieuwe entiteiten zijn succesvol aangemaakt.", + "update-entities": "{{count}} entiteiten zijn succesvol bijgewerkt.", + "error-entities": "Er is een fout opgetreden bij het aanmaken van {{count}} entiteiten." + } + }, + "scada": { + "symbols": "SCADA-symbolen", + "search": "Zoek symbool", + "selected-symbols": "{ count, plural, =1 {1 symbool} other {# symbolen} } geselecteerd", + "download-symbol": "Download SCADA-symbool", + "export-symbol": "Exporteer SCADA-symbool naar JSON", + "import-symbol": "Importeer SCADA-symbool van JSON", + "upload-symbol": "Upload SCADA-symbool", + "update-symbol": "Update SCADA-symbool", + "edit-symbol": "Bewerk SCADA-symbool", + "symbol-details": "SCADA-symbooldetails", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "Geen symbolen gevonden", + "show-hidden-elements": "Toon verborgen elementen", + "hide-hidden-elements": "Verberg verborgen elementen", + "delete-symbol": "Verwijder SCADA-symbool", + "delete-symbol-title": "Weet u zeker dat u het SCADA-symbool '{{imageTitle}}' wilt verwijderen?", + "delete-symbol-text": "Wees voorzichtig, na bevestiging wordt het SCADA-symbool onherstelbaar verwijderd.", + "delete-symbols-title": "Weet u zeker dat u { count, plural, =1 {1 SCADA-symbool} other {# SCADA-symbolen} } wilt verwijderen?", + "delete-symbols-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde SCADA-symbolen verwijderd en alle gerelateerde gegevens gaan onherroepelijk verloren.", + "include-system-symbols": "Inclusief systeem symbolen", + "symbol-preview": "Symboolvoorbeeld", + "general": "Algemeen", + "tags": "Tags", + "properties": "Eigenschappen", + "title": "Titel", + "description": "Beschrijving", + "search-tags": "Zoek tags", + "widget-size": "Widgetgrootte", + "cols": "kolommen", + "rows": "rijen", + "state-render-function": "Toestandsweergavefunctie", + "preview": "Voorbeeld", + "preview-widget-action-text": "Widgetactie '{{type}}' succesvol uitgevoerd!", + "no-symbol": "Geen SCADA-symbool", + "no-symbol-selected": "Geen SCADA-symbool geselecteerd", + "clear-symbol": "Wis SCADA-symbool", + "browse-symbol-from-gallery": "Blader SCADA-symbool uit galerij", + "zoom-in": "Inzoomen", + "zoom-out": "Uitzoomen", + "create-widget": "Widget maken", + "create-widget-from-symbol": "Widget maken van SCADA-symbool", + "hidden": "verborgen", + "tag": { + "tag": "Tag", + "on-click-action": "Actie bij klikken", + "no-tags": "Geen tags geconfigureerd", + "delete-tag-text": "Weet u zeker dat u tag
{{tag}} van {{elementType}} element wilt verwijderen?", + "update-tag": "Tag bijwerken", + "enter-tag": "Voer tag in", + "tag-settings": "Taginstellingen", + "remove-tag": "Tag verwijderen", + "add-tag": "Tag toevoegen" + }, + "behavior": { + "behavior": "Gedrag", + "id": "Id", + "name": "Naam", + "type": "Type", + "no-behaviors": "Geen gedragingen geconfigureerd", + "add-behavior": "Gedrag toevoegen", + "type-action": "Actie", + "type-value": "Waarde", + "type-widget-action": "Widgetactie", + "behavior-settings": "Gedragsinstellingen", + "remove-behavior": "Verwijder gedrag", + "hint": "Tip", + "group-title": "Groepstitel", + "value-type": "Waarde type", + "default-value": "Standaardwaarde", + "true-label": "Label voor waar", + "false-label": "Label voor onwaar", + "state-label": "Toestand label", + "default-payload": "Standaardpayload", + "not-unique-behavior-ids-error": "Gedrag-IDs moeten uniek zijn!", + "default-settings": "Standaardinstellingen" + }, + "symbol": { + "symbol": "SCADA-symbool", + "fluid-presence": "Vloeistofaanwezigheid", + "fluid-presence-hint": "Geeft aan of er vloeistof in de pijp aanwezig is.", + "fluid-present": "Vloeistof aanwezig", + "present": "Aanwezig", + "absent": "Afwezig", + "flow-presence": "Stroomaanwezigheid", + "flow-presence-hint": "Geeft aan of vloeistof in de pijp stroomt.", + "flow-present": "Stroom aanwezig", + "flow-direction": "Stroomrichting", + "flow-direction-hint": "Geeft de stroomrichting van de vloeistof aan.", + "forward": "Vooruit", + "reverse": "Achteruit", + "flow-animation-speed": "Stroomsnelheid animatie", + "flow-animation-speed-hint": "Dubbele waarde die de animatiesnelheid van de stroom aangeeft. 1 - normale snelheid, 0 - geen animatie, < 1 - tragere animatie, > 1 - snellere animatie.", + "leak": "Lek", + "leak-hint": "Geeft aan of er een lek aanwezig is in de pijp.", + "leak-present": "Lek aanwezig", + "fluid-color": "Vloeistofkleur", + "pipe-color": "Pijpkleur", + "horizontal-pipe": "Horizontale pijp", + "vertical-pipe": "Verticale pijp", + "horizontal-fluid-color": "Horizontale vloeistofkleur", + "vertical-fluid-color": "Verticale vloeistofkleur", + "left-pipe": "Linkerpijp", + "right-pipe": "Rechterpijp", + "top-pipe": "Bovenpijp", + "bottom-pipe": "Onderpijp", + "left-fluid-color": "Linker vloeistofkleur", + "right-fluid-color": "Rechter vloeistofkleur", + "top-fluid-color": "Boven vloeistofkleur", + "bottom-fluid-color": "Onder vloeistofkleur", + "display": "Weergave", + "display-format": "Weergaveformaat", + "value": "Waarde", + "decimals": "Decimalen", + "units": "Eenheden", + "flow-meter-value-hint": "Dubbele waarde weergegeven op het flowmeterdisplay", + "value-hint": "Dubbele waarde die de huidige waarde aangeeft", + "running": "Actief", + "running-hint": "Geeft aan of het onderdeel actief is.", + "warning-state": "Waarschuwingsstatus", + "warning": "Waarschuwing", + "warning-click": "Waarschuwingsklik", + "warning-state-hint": "Geeft aan of het onderdeel in een waarschuwingsstatus verkeert.", + "critical-state": "Kritieke status", + "critical": "Kritiek", + "critical-click": "Kritieke klik", + "critical-state-hint": "Geeft aan of het onderdeel in een kritieke status verkeert.", + "critical-state-animation": "Kritieke status animatie", + "critical-state-animation-hint": "Knipperende animatie inschakelen wanneer het onderdeel in een kritieke status verkeert.", + "warning-critical-state-animation": "Waarschuwing/kritieke status animatie", + "warning-critical-state-animation-hint": "Knipperende animatie inschakelen bij waarschuwing of kritieke status.", + "animation": "Animatie", + "broken": "Defect", + "broken-hint": "Geeft aan of het onderdeel defect is.", + "on-display-click": "Bij klik op weergave", + "on-display-click-hint": "Actie die wordt uitgevoerd wanneer gebruiker op de weergave klikt.", + "pipe": "Pijp", + "default-border-color": "Standaard randkleur", + "active-border-color": "Actieve randkleur", + "warning-border-color": "Waarschuwingsrandkleur", + "critical-border-color": "Kritieke randkleur", + "background-color": "Achtergrondkleur", + "rotation-animation-speed": "Rotatie animatiesnelheid", + "rotation-animation-speed-hint": "Dubbele waarde voor de rotatie animatiesnelheid. 1 - normale snelheid, 0 - geen animatie, < 1 - tragere animatie, > 1 - snellere animatie.", + "on-click": "Bij klik", + "on-click-hint": "Actie uitgevoerd bij klik op component.", + "connectors-positions": "Connectorposities", + "right-connector": "Rechterconnector", + "right-top-connector": "Rechtsboven connector", + "right-bottom-connector": "Rechtsonder connector", + "left-connector": "Linkerconnector", + "left-top-connector": "Linksboven connector", + "left-bottom-connector": "Linksonder connector", + "top-left-connector": "Boven-links connector", + "top-right-connector": "Boven-rechts connector", + "top-connector": "Bovenconnector", + "bottom-connector": "Onderconnector", + "running-color": "Actieve kleur", + "stopped-color": "Gestopte kleur", + "stopped": "Gestopt", + "warning-color": "Waarschuwingskleur", + "critical-color": "Kritieke kleur", + "opened": "Geopend", + "opened-hint": "Geeft aan of het onderdeel geopend is.", + "open": "Openen", + "open-hint": "Actie uitgevoerd wanneer gebruiker het onderdeel opent.", + "close": "Sluiten", + "close-hint": "Actie uitgevoerd wanneer gebruiker op sluiten klikt.", + "close-state-animation": "Animatie bij gesloten status", + "close-state-animation-hint": "Knipperende animatie inschakelen bij gesloten status van het component.", + "opened-color": "Geopende kleur", + "closed-color": "Gesloten kleur", + "opened-rotation-angle": "Rotatiehoek bij openen", + "closed-rotation-angle": "Rotatiehoek bij sluiten", + "tank-capacity": "Tankcapaciteit", + "tank-capacity-hint": "Dubbele waarde die de totale tankcapaciteit aangeeft.", + "current-volume": "Huidig volume", + "current-volume-hint": "Dubbele waarde die het huidige bezette volume aangeeft.", + "tank-color": "Tankkleur", + "value-box": "Waardeveld", + "value-text": "Waarde tekst", + "scale": "Schaal", + "transparent-mode": "Transparante modus", + "major-ticks": "Hoofdindelingen", + "intervals": "Intervallen", + "major-ticks-color": "Kleur hoofdindelingen", + "normal": "Normaal", + "minor-ticks": "Kleine indelingen", + "minor-ticks-color": "Kleur kleine indelingen", + "temperature": "Temperatuur", + "temperature-hint": "Dubbele waarde die de huidige temperatuur aangeeft.", + "update-temperature": "Temperatuur bijwerken", + "update-temperature-hint": "Actie uitgevoerd bij wijziging van de temperatuur.", + "run": "Starten", + "run-hint": "Actie uitgevoerd bij starten van component.", + "stop": "Stoppen", + "stop-hint": "Actie uitgevoerd bij stoppen van component.", + "temperature-step": "Temperatuurstap", + "heat-pump-color": "Warmtepompkleur", + "power-button-background": "Achtergrond stroomknop", + "value-box-background": "Achtergrond waardeveld", + "value-units": "Waarde-eenheden", + "filtration-mode": "Filtratiemodus", + "filtration-mode-hint": "Geheel getal dat huidige filtratiemodus aangeeft.", + "filtration-mode-update": "Filtratiemodus bijwerken", + "filtration-mode-update-hint": "Actie uitgevoerd bij wijziging van filtratiemodus.", + "filter-mode": "Filter", + "waste-mode": "Afval", + "backwash-mode": "Terugspoelen", + "recirculate-mode": "Recirculeren", + "rinse-mode": "Spoelen", + "closed-mode": "Gesloten", + "sand-filter-color": "Zandfilterkleur", + "mode-box-background": "Achtergrond modusvak", + "border-color": "Randkleur", + "label-color": "Labelkleur", + "water-leak-hint": "Geeft aan of er een lek is.", + "default-color": "Standaardkleur", + "leak-color": "Lek kleur", + "full-value": "Volledige waarde", + "full-value-hint": "Dubbele waarde die de maximale waarde aangeeft.", + "label": "Label", + "icon": "Pictogram", + "button-color": "Knopkleur", + "on-label": "'Aan'-labeltekst", + "off-label": "'Uit'-labeltekst", + "arrow-presence": "Pijl aanwezig", + "arrow-presence-hint": "Geeft aan of er een pijl is in de connector.", + "arrow-present": "Pijl aanwezig", + "arrow-direction": "Stroomrichting", + "arrow-direction-hint": "Geeft stroomrichting aan.", + "flow-animation": "Stroomaanwezigheid", + "flow-animation-hint": "Geeft aan of vloeistof in de connector stroomt.", + "flow": "Stroom", + "flow-line": "Lijn", + "flow-line-style": "Lijnstijl", + "flow-style-hint": "Stel Dash- en Gap-waarden zo in dat hun som zonder rest deelbaar is door 100 voor perfecte animatiesynchronisatie.", + "flow-dash-cap": "Dash eindstijl", + "dash-cap-butt": "Recht", + "dash-cap-round": "Rond", + "dash-cap-square": "Vierkant", + "dash": "Streep", + "gap": "Ruimte", + "main-line": "Hoofdlijn", + "line": "Lijn", + "line-color": "Lijnkleur", + "arrow-color": "Pijlkleur", + "target-value": "Doelwaarde", + "target-value-hint": "Geeft het doelpunt op de schaal aan.", + "min-max-value": "Min en max waarde", + "min-value": "Min", + "max-value": "Max", + "progress-bar": "Voortgangsbalk", + "progress-arrow": "Voortgangspijl", + "warning-scale-color": "Waarschuwingsschaalkleur", + "critical-scale-color": "Kritieke schaalkleur", + "scale-color": "Schaalkleur", + "target": "Doel", + "high-warning-state": "Hoge waarschuwingstoestand", + "show-high-warning-scale": "Toon hoge waarschuwing schaal", + "high-warning-scale": "Hoge waarschuwing schaal", + "high-warning-state-hint": "Dubbele waarde geeft een hoge waarschuwing bereik tot aan een hoge kritieke of maximale waarde aan.", + "low-warning-state": "Lage waarschuwingstoestand", + "show-low-warning-scale": "Toon lage waarschuwing schaal", + "low-warning-scale": "Lage waarschuwing schaal", + "low-warning-state-hint": "Dubbele waarde geeft een lage waarschuwing bereik tot aan een lage kritieke of minimale waarde aan.", + "high-critical-state": "Hoge kritieke toestand", + "show-high-critical-scale": "Toon hoge kritieke schaal", + "high-critical-scale": "Hoge kritieke schaal", + "high-critical-state-hint": "Dubbele waarde geeft een hoog kritiek bereik tot de maximale schaalwaarde aan.", + "low-critical-state": "Lage kritieke toestand", + "show-low-critical-scale": "Toon lage kritieke schaal", + "low-critical-scale": "Lage kritieke schaal", + "low-critical-state-hint": "Dubbele waarde geeft een laag kritiek bereik tot de minimale schaalwaarde aan.", + "filter-color": "Filterkleur", + "colors": "Kleuren", + "indicator-colors": "Indicator kleuren", + "enabled": "Ingeschakeld", + "disabled": "Uitgeschakeld", + "on": "AAN", + "off": "UIT", + "on-off-state": "Aan/Uit-toestand", + "on-off-state-hint": "Geeft aan of het component in AAN of UIT toestand is.", + "on-update-state": "Bijwerken naar AAN", + "on-update-state-hint": "Actie uitgevoerd bij bijwerken naar AAN.", + "off-update-state": "Bijwerken naar UIT", + "off-update-state-hint": "Actie uitgevoerd bij bijwerken naar UIT.", + "voltage": "Spanning", + "input-voltage": "Ingangsspanning", + "input-voltage-hint": "Dubbele waarde geeft ingangsspanning aan.", + "output-voltage": "Uitgangsspanning", + "output-voltage-hint": "Dubbele waarde geeft uitgangsspanning aan.", + "first-phase-voltage": "Eerste fase spanning", + "second-phase-voltage": "Tweede fase spanning", + "third-phase-voltage": "Derde fase spanning", + "phase-voltage-hint": "Dubbele waarde geeft de spanning voor de huidige fase aan", + "voltage-hint": "Dubbele waarde geeft huidige spanning aan", + "current-voltage-color": "Huidige spanningskleur", + "phase-indicator-color": "Fase indicator kleur", + "measured": "Gemeten", + "measured-hint": "Dubbele waarde geeft energieverbruik in kilowattuur aan", + "day-rate": "Dagtarief", + "night-rate": "Nachttarief", + "off-peak-rate": "Laag tarief", + "peak-rate": "Piektarief", + "export-rate": "Exporteertarief", + "operating-mode": "Bedrijfsmodus", + "bypass-mode": "Bypass", + "operating-mode-hint": "Geheel getal dat de huidige bedrijfsmodus aangeeft (0 - UIT, 1 - AAN, 2 - BYPASS)", + "connected": "Verbonden", + "connected-hint": "Geeft aan of component in verbonden toestand is.", + "disconnected": "Verbroken", + "indicator": "Indicator", + "operation-mode": "Bedrijfsmodus", + "operation-mode-hint": "Geeft aan of de omvormer in netmodus of omvormermodus werkt.", + "operation-mode-indicators-color": "Bedrijfsmodus indicator kleur", + "mains-on-mode": "Netstroom ingeschakeld", + "inverter-on-mode": "Omvormer ingeschakeld", + "charging-mode": "Laadmodus", + "charging-mode-hint": "Geheel getal dat de huidige laadmodus aangeeft (1 - Bulk, 2 - Absorptie, 3 - Drijfvermogen)", + "charging-mode-indicators-color": "Laadmodus indicator kleur", + "inverter-faults": "Storingen", + "inverter-fault-indicators-color": "Storing indicator kleur", + "overload-fault": "Overbelasting", + "overload-fault-hint": "Geeft aan of de omvormer overbelast is.", + "low-battery-fault": "Lage batterij", + "low-battery-fault-hint": "Geeft aan of de batterij te ver ontladen is.", + "temperature-fault": "Temperatuur", + "temperature-fault-hint": "Geeft hoge temperatuur in een omvormer aan.", + "triangle": "Driehoek", + "socket": "Stopcontact", + "left-button": "Linkerknop", + "right-button": "Rechterknop", + "alarm-colors": "Alarmkleuren", + "hook-color": "Haakkleur" + } + }, + "item": { + "selected": "Geselecteerd" + }, + "js-func": { + "no-return-error": "Functie moet een waarde retourneren!", + "return-type-mismatch": "Functie moet een waarde van het type '{{type}}' retourneren!", + "tidy": "Netjes", + "mini": "Mini", + "modules": "Modules", + "remove-module": "Module verwijderen", + "no-modules": "Geen modules geconfigureerd", + "add-module": "Module toevoegen", + "module-alias": "Alias", + "invalid-module-alias-name": "Ongeldige aliasnaam", + "module-resource": "JS modulebron", + "not-unique-module-aliases-error": "Module-aliasen moeten uniek zijn!", + "show-module-info": "Toon module-info", + "show-module-source-code": "Toon modulesourcecode", + "module-members": "Moduleleden", + "module-no-members": "Module heeft geen geëxporteerde leden", + "module-load-error": "Fout bij laden van module", + "source-code": "Broncode", + "source-code-load-error": "Fout bij laden van broncode", + "no-js-module-text": "Geen JS-modules gevonden", + "no-js-module-matching": "Geen JS-modules gevonden die overeenkomen met '{{module}}'." + }, + "key-val": { + "key": "Sleutel", + "value": "Waarde", + "remove-entry": "Invoer verwijderen", + "add-entry": "Invoer toevoegen", + "no-data": "Geen invoer" + }, + "layout": { + "layout": "Lay-out", + "layouts": "Lay-outs", + "manage": "Lay-outs beheren", + "settings": "Lay-outinstellingen", + "color": "Kleur", + "main": "Hoofd", + "right": "Rechts", + "left": "Links", + "select": "Selecteer doellay-out", + "percentage-width": "Percentagebreedte (%)", + "fixed-width": "Vaste breedte (px)", + "left-width": "Linkerkolom (%)", + "right-width": "Rechterkolom (%)", + "pick-fixed-side": "Vaste zijde: ", + "layout-fixed-width": "Vaste breedte (px)", + "value-min-error": "Waarde moet groter zijn dan {{min}}{{unit}}", + "value-max-error": "Waarde moet kleiner zijn dan {{max}}{{unit}}", + "layout-fixed-width-required": "Vaste breedte is vereist", + "right-width-percentage-required": "Percentage voor rechts is vereist", + "left-width-percentage-required": "Percentage voor links is vereist", + "divider": "Verdeler", + "right-side": "Lay-out rechterzijde", + "left-side": "Lay-out linkerzijde", + "add-new-breakpoint": "Nieuwe breekpunt toevoegen", + "breakpoint": "Breekpunt", + "breakpoints": "Breekpunten", + "copy-from": "Kopieer van", + "size": "Grootte", + "delete-breakpoint-title": "Weet je zeker dat je het breekpunt '{{name}}' wilt verwijderen?", + "delete-breakpoint-text": "Let op, na bevestiging wordt het breekpunt onherstelbaar verwijderd en worden de instellingen teruggezet naar het standaardbreekpunt." + }, + "legend": { + "direction": "Richting", + "position": "Positie", + "show-values": "Waarden tonen", + "min-option": "Min", + "max-option": "Max", + "average-option": "Gemiddelde", + "total-option": "Totaal", + "latest-option": "Laatste", + "sort-legend": "Sorteer gegevenssleutels in legenda", + "show-max": "Toon maximale waarde", + "show-min": "Toon minimale waarde", + "show-avg": "Toon gemiddelde waarde", + "show-total": "Toon totale waarde", + "show-latest": "Toon laatste waarde", + "settings": "Legenda-instellingen", + "min": "min", + "max": "max", + "avg": "gem", + "total": "totaal", + "latest": "laatste", + "Min": "Min", + "Max": "Max", + "Avg": "Gem", + "Total": "Totaal", + "Latest": "Laatste", + "comparison-time-ago": { + "previousInterval": "(vorig interval)", + "customInterval": "(aangepast interval)", + "days": "(dag geleden)", + "weeks": "(week geleden)", + "months": "(maand geleden)", + "years": "(jaar geleden)" + }, + "column-title": "Kolomtitel", + "label": "Label", + "value": "Waarde" + }, + "login": { + "login": "Inloggen", + "request-password-reset": "Wachtwoord reset aanvragen", + "reset-password": "Wachtwoord opnieuw instellen", + "create-password": "Wachtwoord aanmaken", + "two-factor-authentication": "Twee-factor authenticatie", + "passwords-mismatch-error": "Ingevoerde wachtwoorden moeten gelijk zijn!", + "password-again": "Wachtwoord opnieuw", + "sign-in": "Meld u aan", + "username": "Gebruikersnaam (e-mail)", + "remember-me": "Onthoud mij", + "forgot-password": "Wachtwoord vergeten?", + "password-reset": "Wachtwoordherstel", + "expired-password-reset-message": "Uw inloggegevens zijn verlopen! Maak een nieuw wachtwoord aan.", + "new-password": "Nieuw wachtwoord", + "new-password-again": "Bevestig nieuw wachtwoord", + "password-link-sent-message": "Resetlink is verzonden", + "email": "E-mail", + "invalid-email-format": "Ongeldig e-mailformaat.", + "login-with": "Inloggen met {{name}}", + "or": "of", + "error": "Inlogfout", + "verify-your-identity": "Verifieer uw identiteit", + "select-way-to-verify": "Selecteer een verificatiemethode", + "resend-code": "Code opnieuw verzenden", + "resend-code-wait": "Code opnieuw verzenden over { time, plural, =1 {1 seconde} other {# seconden} }", + "try-another-way": "Probeer een andere manier", + "totp-auth-description": "Voer de beveiligingscode in uit uw authenticator-app.", + "totp-auth-placeholder": "Code", + "sms-auth-description": "Een beveiligingscode is verzonden naar uw telefoon op {{contact}}.", + "sms-auth-placeholder": "SMS-code", + "email-auth-description": "Een beveiligingscode is verzonden naar uw e-mailadres op {{contact}}.", + "email-auth-placeholder": "E-mailcode", + "backup-code-auth-description": "Voer een van uw back-upcodes in.", + "backup-code-auth-placeholder": "Back-upcode", + "activation-link-expired": "Activatielink is verlopen", + "activation-link-expired-message": "De link om uw profiel te activeren is verlopen. U kunt terugkeren naar de inlogpagina om een nieuwe e-mail te ontvangen.", + "reset-password-link-expired": "Wachtwoord resetlink is verlopen", + "reset-password-link-expired-message": "De link om uw wachtwoord opnieuw in te stellen is verlopen. U kunt terugkeren naar de inlogpagina om een nieuwe e-mail te ontvangen." + }, + "mobile": { + "add-application": "Applicatie toevoegen", + "app-id": "App-ID", + "app-id-required": "App-ID is verplicht", + "app-id-pattern": "Ongeldig formaat App-ID", + "app-store-link": "App Store-link", + "app-store-link-required": "App Store-link is verplicht", + "application-details": "Applicatiegegevens", + "application-package": "Applicatiepakket", + "application-secret": "Applicatiegeheim", + "application-secret-required": "Applicatiegeheim is verplicht", + "application": "Applicatie", + "applications": "Applicaties", + "copy-app-id": "App-ID kopiëren", + "copy-app-store-link": "App Store-link kopiëren", + "copy-application-package": "Applicatiepakket kopiëren", + "copy-application-secret": "Applicatiegeheim kopiëren", + "copy-google-play-link": "Google Play-link kopiëren", + "copy-sha256-certificate-fingerprints": "SHA256-certificaatvingerafdrukken kopiëren", + "delete-application": "Applicatie verwijderen", + "delete-application-button-text": "Ik begrijp de gevolgen, verwijder de applicatie", + "delete-application-text": "Deze actie kan niet ongedaan worden gemaakt. Dit verwijdert uw applicatie permanent.
Als u het niet permanent wilt verwijderen, kunt u de applicatie tijdelijk opschorten.
Om de applicatie te verwijderen, typ \"{{phrase}}\" ter bevestiging.", + "delete-application-title-short": "Weet u zeker dat u de applicatie '{{name}}' wilt verwijderen?", + "delete-application-text-short": "Wees voorzichtig, na bevestiging worden de applicatie en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-application-phrase": "applicatie verwijderen", + "delete-applications-bundle-text": "Wees voorzichtig, na bevestiging worden het mobiele pakket en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-applications-bundle-title": "Weet u zeker dat u het mobiele pakket '{{bundleName}}' wilt verwijderen?", + "generate-application-secret": "Applicatiegeheim genereren", + "google-play-link": "Google Play-link", + "google-play-link-required": "Google Play-link is verplicht", + "latest-version": "Laatste versie", + "min-version": "Minimale versie", + "invalid-version-pattern": "Ongeldig versieformaat. Gebruik het formaat: major.minor.patch (bijv. 1.0.0).", + "mobile-center": "Mobiel centrum", + "mobile-package": "Applicatiepakket", + "mobile-package-max-length": "Applicatiepakket mag niet langer zijn dan 256 tekens", + "mobile-package-required": "Applicatiepakket is verplicht.", + "mobile-package-pattern": "Ongeldig formaat applicatiepakket", + "no-application": "Geen applicaties gevonden", + "no-bundles": "Geen pakketten gevonden", + "platform-type": "Platformtype", + "search-application": "Zoek applicaties", + "search-bundles": "Zoek pakketten", + "set": "Instellen", + "sha256-certificate-fingerprints": "SHA256-certificaatvingerafdrukken", + "sha256-certificate-fingerprints-required": "SHA256-certificaatvingerafdrukken zijn verplicht", + "sha256-certificate-fingerprints-pattern": "Ongeldig SHA256-certificaatvingerafdrukformaat", + "show-hidden-pages": "Verborgen pagina’s tonen", + "status": "Status", + "status-type": { + "deprecated": "Verouderd", + "draft": "Concept", + "published": "Gepubliceerd", + "suspended": "Geschorst" + }, + "store-information": "Winkelinformatie", + "version-information": "Versie-informatie", + "min-version-release-notes": "Release-opmerkingen voor minimale versie", + "latest-version-release-notes": "Release-opmerkingen voor laatste versie", + "bundle": "Pakket", + "bundles": "Pakketten", + "add-bundle": "Pakket toevoegen", + "title": "Titel", + "title-required": "Titel is verplicht", + "title-cannot-contain-only-spaces": "Titel mag niet alleen spaties bevatten", + "title-max-length": "Titel mag niet langer zijn dan 256 tekens", + "oauth-clients": "OAuth 2.0-clients", + "android-app": "Android-app", + "android-application": "Android-applicatie", + "ios-app": "iOS-app", + "ios-application": "iOS-applicatie", + "invalid-store-link": "Ongeldige winkel-link", + "enable-oauth": "OAuth 2.0 inschakelen", + "enable-self-registration": "Zelfregistratie inschakelen", + "edit-bundle": "Pakket bewerken", + "description": "Beschrijving", + "basic-settings": "Basisinstellingen", + "no-application-matching": "Geen overeenkomende applicatie '{{entity}}' gevonden.", + "no-bundle-matching": "Geen overeenkomend pakket '{{entity}}' gevonden.", + "application-required": "Applicatie is verplicht.", + "bundle-required": "Pakket is verplicht.", + "no-application-text": "Geen applicaties gevonden", + "no-bundle-text": "Geen pakketten gevonden", + "layout": "Indeling", + "pages": "Pagina’s", + "hide-all-pages": "Alle pagina’s verbergen", + "reset-to-default-pages": "Reset naar standaardpagina’s", + "add-specific-page": "Specifieke pagina toevoegen", + "visible": "Zichtbaar", + "hidden": "Verborgen", + "reset-to-page-default": "Pagina terugzetten naar standaard", + "mobile-599": "Mobiel (max 599px)", + "tablet-959": "Tablet (max 959px)", + "max-element-number": "Max aantal elementen", + "page-name": "Paginanaam", + "page-name-required": "Paginanaam is verplicht.", + "page-name-cannot-contain-only-spaces": "Paginanaam mag niet alleen spaties bevatten.", + "page-name-max-length": "Paginanaam mag niet langer zijn dan 256 tekens", + "page-type": "Paginatype", + "pages-types": { + "dashboard": "Dashboard", + "web-view": "Webweergave", + "custom": "Aangepast" + }, + "url": "URL", + "invalid-url-format": "Ongeldig URL-formaat", + "path": "Pad", + "invalid-path-format": "Ongeldig padformaat", + "custom-page": "Aangepaste pagina", + "edit-page": "Pagina bewerken", + "edit-custom-page": "Aangepaste pagina bewerken", + "delete-page": "Pagina verwijderen", + "qr-code-widget": "QR-code widget", + "type-here": "Typ hier", + "configuration-dialog": "Configuratiedialoog", + "configuration-app": "Configuratie-app", + "configuration-step": { + "prepare-environment-title": "Ontwikkelomgeving voorbereiden", + "prepare-environment-text": "Flutter ThingsBoard Mobile App vereist Flutter SDK. Volg de instructies om de Flutter SDK in te stellen.", + "get-source-code-title": "App-broncode verkrijgen", + "get-source-code-text": "Je kunt de broncode van de Flutter ThingsBoard Mobile App verkrijgen door deze uit de GitHub-repository te klonen:", + "configure-api-title": "ThingsBoard API-eindpunt configureren", + "configure-api-text": "Open het flutter_thingsboard_pe_app-project in je editor/IDE. Bewerk:", + "configure-api-hint": "Stel de waarde van de constant thingsBoardApiEndpoint in op het API-eindpunt van je ThingsBoard-serverinstantie. Gebruik geen 'localhost' of '127.0.0.1'.", + "run-app-title": "De app uitvoeren", + "run-app-text": "Voer de app uit zoals beschreven in je IDE.\nGebruik je de terminal, voer de app dan uit met het volgende commando:", + "more-information": "Gedetailleerde informatie vind je in onze Getting Started-documentatie.", + "getting-started": "Aan de slag", + "configure-package-title": "Applicatiepakket configureren", + "configure-package-text": "Je kunt het applicatiepakket handmatig wijzigen of een CLI-hulpprogramma van derden gebruiken.", + "configure-package-text-install": "Voer het volgende commando uit om de Rename CLI Tool te installeren:", + "configure-package-run-commands": "Voer deze commando’s uit in de hoofdmap van je project:" + } + }, + "notification": { + "action-button": "Actieknop", + "action-type": "Actietype", + "active": "Actief", + "add-notification-recipients-group": "Meldingsontvangersgroep toevoegen", + "add-notification-template": "Meldingssjabloon toevoegen", + "add-recipient": "Ontvanger toevoegen", + "add-recipients": "Ontvangers toevoegen", + "add-rule": "Regel toevoegen", + "add-stage": "Fase toevoegen", + "add-template": "Sjabloon toevoegen", + "after": "Na", + "alarm-assignment-trigger-settings": "Instellingen voor alarmtoewijzingsactivering", + "alarm-comment-trigger-settings": "Instellingen voor alarmcommentaaractivering", + "alarm-trigger-settings": "Instellingen voor alarmaanleiding", + "all": "Alle", + "api-feature-hint": "Als dit veld leeg is, wordt de trigger toegepast op alle API-functies", + "api-usage-trigger-settings": "Instellingen voor API-gebruikstrigger", + "new-platform-version-trigger-settings": "Triggerinstellingen voor nieuwe platformversie", + "rate-limits-trigger-settings": "Triggerinstellingen voor overschreden snelheidslimieten", + "task-processing-failure-trigger-settings": "Triggerinstellingen voor taakverwerkingsfouten", + "at-least-one-should-be-selected": "Ten minste één moet worden geselecteerd", + "basic-settings": "Basisinstellingen", + "button-text": "Knoptekst", + "button-text-required": "Knoptekst is verplicht", + "button-text-max-length": "Knoptekst mag niet langer zijn dan {{ length }} tekens", + "compose": "Samenstellen", + "conversation": "Gesprek", + "conversation-required": "Gesprek is verplicht", + "copy-notification-template": "Meldingssjabloon kopiëren", + "copy-rule": "Regel kopiëren", + "copy-template": "Sjabloon kopiëren", + "create-new": "Nieuw aanmaken", + "created": "Gemaakt", + "customize-messages": "Berichten aanpassen", + "delete-notification-text": "Wees voorzichtig, na bevestiging is de melding niet meer herstelbaar.", + "delete-notification-title": "Weet u zeker dat u de melding wilt verwijderen?", + "delete-notifications-text": "Wees voorzichtig, na bevestiging zijn meldingen niet meer herstelbaar.", + "delete-notifications-title": "Weet u zeker dat u { count, plural, =1 {1 melding} other {# meldingen} } wilt verwijderen?", + "delete-recipient-text": "Wees voorzichtig, na bevestiging is de ontvanger niet meer herstelbaar.", + "delete-recipient-title": "Weet u zeker dat u de ontvanger '{{recipientName}}' wilt verwijderen?", + "delete-recipients-text": "Wees voorzichtig, na bevestiging zijn ontvangers niet meer herstelbaar.", + "delete-recipients-title": "Weet u zeker dat u { count, plural, =1 {1 ontvanger} other {# ontvangers} } wilt verwijderen?", + "delete-request-text": "Wees voorzichtig, na bevestiging is het verzoek niet meer herstelbaar.", + "delete-request-title": "Weet u zeker dat u het verzoek wilt verwijderen?", + "delete-requests-text": "Wees voorzichtig, na bevestiging zijn verzoeken niet meer herstelbaar.", + "delete-requests-title": "Weet u zeker dat u { count, plural, =1 {1 verzoek} other {# verzoeken} } wilt verwijderen?", + "delete-rule-text": "Wees voorzichtig, na bevestiging is de regel niet meer herstelbaar.", + "delete-rule-title": "Weet u zeker dat u de regel '{{ruleName}}' wilt verwijderen?", + "delete-rules-text": "Wees voorzichtig, na bevestiging zijn regels niet meer herstelbaar.", + "delete-rules-title": "Weet u zeker dat u { count, plural, =1 {1 regel} other {# regels} } wilt verwijderen?", + "delete-template-text": "Wees voorzichtig, na bevestiging is het sjabloon niet meer herstelbaar.", + "delete-template-title": "Weet u zeker dat u het sjabloon '{{templateName}}' wilt verwijderen?", + "delete-templates-text": "Wees voorzichtig, na bevestiging zijn sjablonen niet meer herstelbaar.", + "delete-templates-title": "Weet u zeker dat u { count, plural, =1 {1 sjabloon} other {# sjablonen} } wilt verwijderen?", + "deleted": "Verwijderd", + "delivery-method": { + "delivery-method": "Bezorgmethode", + "email": "E-mail", + "email-preview": "Voorbeeld e-mailmelding", + "slack": "Slack", + "slack-preview": "Voorbeeld Slack-melding", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Voorbeeld Microsoft Teams-melding", + "sms": "SMS", + "sms-preview": "Voorbeeld SMS-melding", + "web": "Web", + "web-preview": "Voorbeeld webmelding", + "mobile-app": "Mobiele app", + "mobile-app-preview": "Voorbeeld mobiele app-melding" + }, + "delivery-method-not-configure-click": "Bezorgmethode is niet geconfigureerd. Klik om in te stellen.", + "delivery-method-not-configure-contact": "Bezorgmethode is niet geconfigureerd. Neem contact op met uw systeembeheerder.", + "delivery-methods": "Bezorgmethodes", + "description": "Beschrijving", + "device-activity-trigger-settings": "Triggerinstellingen voor apparaatactiviteit", + "device-list-rule-hint": "Als dit veld leeg is, wordt de trigger toegepast op alle apparaten", + "device-profiles-list-rule-hint": "Als dit veld leeg is, wordt de trigger toegepast op alle apparaatprofielen", + "disabled": "Uitgeschakeld", + "edge-trigger-settings": "Triggerinstellingen voor Edge", + "edge-list-rule-hint": "Als dit veld leeg is, wordt de trigger toegepast op alle Edge-instanties", + "edit-notification-recipients-group": "Ontvangersgroep voor melding bewerken", + "edit-notification-template": "Meldingssjabloon bewerken", + "edit-rule": "Regel bewerken", + "edit-template": "Sjabloon bewerken", + "enabled": "Ingeschakeld", + "entities-limit-trigger-settings": "Triggerinstellingen voor entiteitenlimiet", + "entity-action-trigger-settings": "Triggerinstellingen voor entiteitacties", + "entity-type": "Entiteittype", + "escalation-chain": "Escalatieketen", + "failed-send": "Verzending mislukt", + "fails": "{ count, plural, =1 {1 mislukking} other {# mislukkingen} }", + "filter": "Filter", + "first-recipient": "Eerste ontvanger", + "inactive": "Inactief", + "inbox": "Inbox", + "notification-inbox": "Meldingen / Inbox", + "input-field-support-templatization": "Invoerveld ondersteunt templatization.", + "input-fields-support-templatization": "Invoervelden ondersteunen templatization.", + "link": "Link", + "link-required": "Link is verplicht", + "link-type": { + "dashboard": "Dashboard openen", + "link": "URL-link openen" + }, + "loading-notifications": "Meldingen laden...", + "management": "Meldingenbeheer", + "mark-all-as-read": "Markeer alles als gelezen", + "mark-as-read": "Markeer als gelezen", + "message": "Bericht", + "message-required": "Bericht is verplicht", + "message-max-length": "Bericht mag niet langer zijn dan {{ length }} tekens", + "name": "Naam", + "name-required": "Naam is verplicht", + "new-notification": "Nieuwe melding", + "no-inbox-notification": "Geen meldingen gevonden", + "no-notification-request": "Geen meldingsverzoek", + "no-notification-templates": "Geen meldingssjablonen gevonden", + "no-notifications-yet": "Nog geen meldingen", + "no-recipients-notification": "Geen ontvangersmelding", + "no-recipients-matching": "Geen ontvangers gevonden die overeenkomen met '{{entity}}'.", + "no-recipients-text": "Geen ontvangers gevonden", + "no-rule": "Geen regel geconfigureerd", + "no-rules-notification": "Geen meldingsregels", + "no-severity-found": "Geen ernstniveau gevonden", + "no-severity-matching": "'{{severity}}' niet gevonden.", + "no-template-matching": "Geen bron gevonden die overeenkomt met '{{template}}'.", + "not-found-slack-recipient": "Slack-ontvanger niet gevonden", + "notification": "Melding", + "notification-center": "Meldingscentrum", + "notification-tap-action": "Actie bij tikken op melding", + "notification-tap-action-hint": "Indien niet ingeschakeld, wordt het standaard alarmdashboard gebruikt", + "notify": "Melden", + "notify-again": "Opnieuw melden", + "notify-alarm-action": { + "acknowledged": "Alarm bevestigd", + "assigned": "Alarm toegewezen", + "cleared": "Alarm gewist", + "created": "Alarm aangemaakt", + "severity-changed": "Alarmernst gewijzigd", + "unassigned": "Alarm niet-toegewezen" + }, + "notify-on": "Melden bij", + "notify-on-comment-update": "Melden bij commentaarupdate", + "notify-on-required": "Meldingsactie is verplicht", + "notify-on-unassign": "Melden bij ontkoppelen", + "notify-only-user-comments": "Alleen gebruikersopmerkingen melden", + "only-rule-chain-lifecycle-failures": "Alleen fouten in levenscyclus van regelketen", + "only-rule-node-lifecycle-failures": "Alleen fouten in levenscyclus van regelknooppunt", + "platform-users": "Platformgebruikers", + "rate-limits": "Snelheidslimieten", + "rate-limits-hint": "Als dit veld leeg is, wordt de trigger toegepast op alle snelheidslimieten", + "recipient": "Ontvanger", + "recipient-group": "Ontvangersgroep", + "recipient-type": { + "affected-tenant-administrators": "Betrokken tenantbeheerders", + "affected-user": "Betrokken gebruiker", + "all-users": "Alle gebruikers", + "customer-users": "Klantgebruikers", + "system-administrators": "Systeembeheerders", + "tenant-administrators": "Tenantbeheerders", + "user-filters": "Gebruikersfilter", + "user-list": "Gebruikerslijst", + "users-entity-owner": "Gebruikers van de entiteiteigenaar" + }, + "recipients": "Ontvangers", + "notification-recipient": "Ontvanger van melding", + "notification-recipient-required": "Ontvanger van melding is verplicht.", + "notification-recipients": "Meldingen / Ontvangers", + "recipients-count": "{ count, plural, =1 {1 ontvanger} other {# ontvangers} }", + "recipients-required": "Ontvangers zijn verplicht", + "refresh-allow-delivery-method": "Bezorgmethode verversen toestaan", + "request-search": "Verzoek zoeken", + "request-status": { + "processing": "Verwerken", + "scheduled": "Gepland", + "sent": "Verzonden" + }, + "review": "Beoordelen", + "rule": "Regel", + "rule-chain-list-rule-hint": "Als het veld leeg is, wordt de trigger toegepast op alle regelketens", + "rule-engine-events-trigger-settings": "Triggerinstellingen voor regelenginegebeurtenissen", + "rule-engine-filter": "Filter voor regelengine", + "rule-name": "Regelnaam", + "rule-name-required": "Naam is verplicht", + "rule-disable": "Meldingsregel uitschakelen", + "rule-enable": "Meldingsregel inschakelen", + "rule-node-filter": "Regelknooppuntfilter", + "rules": "Regels", + "notification-rules": "Meldingen / Regels", + "scheduler-later": "Plan voor later", + "search-notification": "Meldingen zoeken", + "search-recipients": "Ontvangers zoeken", + "search-rules": "Regels zoeken", + "search-templates": "Sjablonen zoeken", + "see-documentation": "Bekijk documentatie", + "selected-notifications": "{ count, plural, =1 {1 melding} other {# meldingen} } geselecteerd", + "selected-recipients": "{ count, plural, =1 {1 ontvanger} other {# ontvangers} } geselecteerd", + "selected-requests": "{ count, plural, =1 {1 verzoek} other {# verzoeken} } geselecteerd", + "selected-rules": "{ count, plural, =1 {1 regel} other {# regels} } geselecteerd", + "selected-template": "{ count, plural, =1 {1 sjabloon} other {# sjablonen} } geselecteerd", + "send-notification": "Verzend melding", + "sent": "Verzonden", + "setup": "Instellen", + "notification-sent": "Meldingen / Verzonden", + "set-entity-from-notification": "Stel entiteit in van melding naar dashboardstatus", + "slack-chanel-type": "Slack kanaaltype", + "slack-chanel-types": { + "direct": "Direct bericht", + "private-channel": "Privékanaal", + "public-channel": "Publiek kanaal" + }, + "start-from-scratch": "Begin opnieuw", + "status": "Status", + "stop-escalation-alarm-status-become": "Stop escalatie wanneer de alarmstatus wordt:", + "subject": "Onderwerp", + "subject-required": "Onderwerp is verplicht", + "subject-max-length": "Onderwerp mag maximaal {{ length }} tekens bevatten", + "template": "Sjabloon", + "template-name": "Sjabloonnaam", + "template-required": "Sjabloon is verplicht", + "template-type": { + "alarm": "Alarm", + "alarm-assignment": "Alarmaanwijzing", + "alarm-comment": "Alarmopmerking", + "api-usage-limit": "API-gebruiksgrens", + "device-activity": "Apparaatactiviteit", + "entities-limit": "Entiteitenlimiet", + "entity-action": "Entiteitactie", + "general": "Algemeen", + "rule-engine-lifecycle-event": "Levenscyclusgebeurtenis van regelengine", + "rule-node": "Regelknooppunt", + "new-platform-version": "Nieuwe platformversie", + "rate-limits": "Overschreden snelheidslimieten", + "edge-communication-failure": "Edge communicatie storing", + "edge-connection": "Edge verbinding", + "task-processing-failure": "Taakverwerkingsfout" + }, + "templates": "Sjablonen", + "notification-templates": "Meldingen / Sjablonen", + "tenant-profiles-list-rule-hint": "Als het veld leeg is, wordt de trigger toegepast op alle tenantprofielen", + "tenants-list-rule-hint": "Als het veld leeg is, wordt de trigger toegepast op alle tenants", + "threshold": "Drempelwaarde", + "theme-color": "Themakleur", + "time": "Tijd", + "track-rule-node-events": "Volg gebeurtenissen van regelknooppunten", + "trigger": { + "alarm": "Alarm", + "alarm-assignment": "Alarmaanwijzing", + "alarm-comment": "Alarmopmerking", + "api-usage-limit": "API-gebruiksgrens", + "device-activity": "Apparaatactiviteit", + "entities-limit": "Entiteitenlimiet", + "entity-action": "Entiteitactie", + "rule-engine-lifecycle-event": "Levenscyclusgebeurtenis van regelengine", + "new-platform-version": "Nieuwe platformversie", + "rate-limits": "Overschreden snelheidslimieten", + "edge-connection": "Edge verbinding", + "edge-communication-failure": "Edge communicatie storing", + "task-processing-failure": "Taakverwerkingsfout", + "trigger": "Trigger", + "trigger-required": "Trigger is verplicht" + }, + "type": "Type", + "unread": "Ongelezen", + "updated": "Bijgewerkt", + "use-deprecated-webhook-connectors": "Verouderde Webhook-connectoren gebruiken", + "use-old-api": "Oude API gebruiken", + "use-template": "Sjabloon gebruiken", + "view-all": "Alles bekijken", + "warning": "Waarschuwing", + "webhook-url": "Webhook URL", + "webhook-url-required": "Webhook URL is verplicht", + "workflow-url": "Workflow URL", + "workflow-url-required": "Workflow URL is verplicht", + "channel-name": "Kanaalnaam", + "channel-name-required": "Kanaalnaam is verplicht", + "settings": { + "notification-settings": "Meldingsinstellingen", + "reset-all": "Alle instellingen resetten", + "reset-all-title": "Weet je zeker dat je het formulier wilt resetten?", + "reset-all-text": "Na bevestiging wordt het instellingenformulier teruggezet naar de standaardwaarde en opgeslagen.", + "type": "Type", + "enable-all": "Alles inschakelen", + "disable-all": "Alles uitschakelen", + "delivery-not-configured": "Bezorgmethode is niet geconfigureerd" + } + }, + "ota-update": { + "add": "Pakket toevoegen", + "assign-firmware": "Firmware toewijzen", + "assign-firmware-required": "Toegewezen firmware is vereist", + "assign-software": "Software toewijzen", + "assign-software-required": "Toegewezen software is vereist", + "auto-generate-checksum": "Checksum automatisch genereren", + "checksum": "Checksum", + "checksum-hint": "Als checksum leeg is, wordt deze automatisch gegenereerd", + "checksum-algorithm": "Checksum-algoritme", + "checksum-copied-message": "Pakket-checksum is gekopieerd naar het klembord", + "change-firmware": "Het wijzigen van de firmware kan een update van { count, plural, =1 {1 apparaat} other {# apparaten} } veroorzaken.", + "change-software": "Het wijzigen van de software kan een update van { count, plural, =1 {1 apparaat} other {# apparaten} } veroorzaken.", + "chose-compatible-device-profile": "Het geüploade pakket is alleen beschikbaar voor apparaten met het gekozen profiel.", + "chose-firmware-distributed-device": "Kies firmware die wordt verspreid naar apparaten", + "chose-software-distributed-device": "Kies software die wordt verspreid naar apparaten", + "content-type": "Inhoudstype", + "copy-checksum": "Checksum kopiëren", + "copy-direct-url": "Directe URL kopiëren", + "copyId": "Pakket-ID kopiëren", + "copied": "Gekopieerd!", + "delete": "Pakket verwijderen", + "delete-ota-update-text": "Wees voorzichtig, na bevestiging kan de OTA-update niet worden hersteld.", + "delete-ota-update-title": "Weet je zeker dat je de OTA-update '{{title}}' wilt verwijderen?", + "delete-ota-updates-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde OTA-updates verwijderd.", + "delete-ota-updates-title": "Weet je zeker dat je { count, plural, =1 {1 OTA-update} other {# OTA-updates} } wilt verwijderen?", + "description": "Beschrijving", + "direct-url": "Directe URL", + "direct-url-copied-message": "Directe URL van het pakket is gekopieerd naar het klembord", + "direct-url-required": "Directe URL is vereist", + "download": "Pakket downloaden", + "drop-file": "Sleep een pakketbestand hierheen of klik om een bestand te selecteren om te uploaden.", + "drop-package-file-or": "Sleep een pakketbestand hierheen of", + "file-name": "Bestandsnaam", + "file-size": "Bestandsgrootte", + "file-size-bytes": "Bestandsgrootte in bytes", + "idCopiedMessage": "Pakket-ID is gekopieerd naar het klembord", + "no-firmware-matching": "Geen compatibele Firmware OTA Update pakketten gevonden die overeenkomen met '{{entity}}'.", + "no-firmware-text": "Geen compatibele Firmware OTA Update pakketten beschikbaar.", + "no-packages-text": "Geen pakketten gevonden", + "no-software-matching": "Geen compatibele Software OTA Update pakketten gevonden die overeenkomen met '{{entity}}'.", + "no-software-text": "Geen compatibele Software OTA Update pakketten beschikbaar.", + "ota-update": "OTA-update", + "ota-update-details": "OTA-update details", + "ota-updates": "OTA-updates", + "package-file": "Pakketbestand", + "package-type": "Pakkettype", + "packages-repository": "Pakketopslag", + "search": "Zoek pakketten", + "selected-package": "{ count, plural, =1 {1 pakket} other {# pakketten} } geselecteerd", + "title": "Titel", + "title-required": "Titel is vereist.", + "title-max-length": "Titel moet minder dan 256 tekens zijn", + "types": { + "firmware": "Firmware", + "software": "Software" + }, + "upload-binary-file": "Binair bestand uploaden", + "use-external-url": "Externe URL gebruiken", + "version": "Versie", + "version-required": "Versie is vereist.", + "version-tag": "Versietag", + "version-tag-hint": "Aangepaste tag moet overeenkomen met de pakketversie die door je apparaat is gerapporteerd.", + "version-max-length": "Versie moet minder dan 256 tekens zijn", + "warning-after-save-no-edit": "Nadat het pakket is geüpload, kunnen titel, versie, apparaatprofiel en pakkettype niet meer worden gewijzigd." + }, + "position": { + "top": "Boven", + "bottom": "Onder", + "left": "Links", + "right": "Rechts" + }, + "profile": { + "profile": "Profiel", + "last-login-time": "Laatste login", + "change-password": "Wachtwoord wijzigen", + "current-password": "Huidig wachtwoord", + "copy-jwt-token": "JWT-token kopiëren", + "jwt-token": "JWT-token", + "token-valid-till": "Token is geldig tot", + "tokenCopiedSuccessMessage": "JWT-token is gekopieerd naar het klembord", + "tokenCopiedWarnMessage": "JWT-token is verlopen! Vernieuw de pagina." + }, + "profiles": { + "profiles": "Profielen" + }, + "security": { + "security": "Beveiliging", + "general-settings": "Algemene beveiligingsinstellingen", + "access-token": "Toegangstoken", + "access-token-required": "Toegangstoken is vereist", + "clientId": "Client ID", + "clientId-required": "Client ID is vereist", + "username": "Gebruikersnaam", + "username-required": "Gebruikersnaam is vereist", + "ca-cert": "CA-certificaat", + "2fa": { + "2fa": "Twee-factor-authenticatie", + "2fa-description": "Twee-factor-authenticatie beschermt je account tegen ongeautoriseerde toegang. Je hoeft alleen een beveiligingscode in te voeren bij het inloggen.", + "authenticate-with": "Je kunt authenticeren met:", + "disable-2fa-provider-text": "Het uitschakelen van {{name}} maakt je account minder veilig", + "disable-2fa-provider-title": "Weet je zeker dat je {{name}} wilt uitschakelen?", + "get-new-code": "Nieuwe code opvragen", + "main-2fa-method": "Gebruiken als hoofd authenticatiemethode", + "dialog": { + "activation-step-description-email": "De volgende keer dat je inlogt, moet je een beveiligingscode invoeren die naar je e-mailadres wordt gestuurd.", + "activation-step-description-sms": "De volgende keer dat je inlogt, moet je een beveiligingscode invoeren die naar je telefoonnummer wordt gestuurd.", + "activation-step-description-totp": "De volgende keer dat je inlogt, moet je een twee-factor authenticatiecode invoeren.", + "activation-step-label": "Activering", + "backup-code-description": "Print de codes uit zodat je ze bij de hand hebt wanneer je ze nodig hebt om in te loggen op je account. Je kunt elke back-upcode één keer gebruiken.", + "backup-code-warn": "Zodra je deze pagina verlaat, kunnen deze codes niet opnieuw worden weergegeven. Bewaar ze veilig met de onderstaande opties.", + "download-txt": "Download (txt)", + "email-step-description": "Voer een e-mailadres in dat wordt gebruikt als je authenticator.", + "email-step-label": "E-mail", + "enable-email-title": "E-mailauthenticator inschakelen", + "enable-sms-title": "SMS-authenticator inschakelen", + "enable-totp-title": "Authenticator-app inschakelen", + "enter-verification-code": "Voer hier de 6-cijferige code in", + "get-backup-code-title": "Back-upcode verkrijgen", + "next": "Volgende", + "scan-qr-code": "Scan deze QR-code met je verificatie-app", + "send-code": "Code verzenden", + "sms-step-description": "Voer een telefoonnummer in dat wordt gebruikt als je authenticator.", + "sms-step-label": "Telefoonnummer", + "success": "Succes!", + "totp-step-description-install": "Je kunt apps zoals Google Authenticator, Authy of Duo installeren.", + "totp-step-description-open": "Open de authenticator-app op je mobiele telefoon.", + "totp-step-label": "App verkrijgen", + "verification-code": "6-cijferige code", + "verification-code-invalid": "Ongeldig verificatiecodeformaat", + "verification-code-incorrect": "Verificatiecode is onjuist", + "verification-code-many-request": "Te veel verzoeken voor verificatiecode", + "verification-step-description": "Voer een 6-cijferige code in die we net naar {{address}} hebben gestuurd", + "verification-step-label": "Verificatie" + }, + "provider": { + "email": "E-mail", + "email-description": "Gebruik een beveiligingscode die naar je e-mailadres wordt gestuurd om te authenticeren.", + "email-hint": "Authenticatiecodes worden via e-mail verzonden naar {{ info }}", + "sms": "SMS", + "sms-description": "Gebruik je telefoon om te authenticeren. We sturen je een beveiligingscode via SMS wanneer je inlogt.", + "sms-hint": "Authenticatiecodes worden via tekstbericht verzonden naar {{ info }}", + "totp": "Authenticator-app", + "totp-description": "Gebruik apps zoals Google Authenticator, Authy of Duo op je telefoon om te authenticeren. Hiermee wordt een beveiligingscode gegenereerd om in te loggen.", + "totp-hint": "Authenticator-app is ingesteld voor je account", + "backup_code": "Back-upcode", + "backup-code-description": "Deze afdrukbare eenmalige codes stellen je in staat om in te loggen wanneer je je telefoon niet bij je hebt, bijvoorbeeld tijdens het reizen.", + "backup-code-hint": "{{ info }} eenmalige codes zijn momenteel actief" + } + }, + "password-requirement": { + "at-least": "Ten minste:", + "character": "{ count, plural, =1 {1 teken} other {# tekens} }", + "digit": "{ count, plural, =1 {1 cijfer} other {# cijfers} }", + "incorrect-password-try-again": "Onjuist wachtwoord. Probeer opnieuw", + "lowercase-letter": "{ count, plural, =1 {1 kleine letter} other {# kleine letters} }", + "new-passwords-not-match": "Nieuw wachtwoord komt niet overeen", + "password-should-not-contain-spaces": "Je wachtwoord mag geen spaties bevatten", + "password-not-meet-requirements": "Wachtwoord voldoet niet aan de vereisten", + "password-requirements": "Wachtwoordeisen", + "password-should-difference": "Nieuw wachtwoord moet verschillen van het huidige", + "special-character": "{ count, plural, =1 {1 speciaal teken} other {# speciale tekens} }", + "uppercase-letter": "{ count, plural, =1 {1 hoofdletter} other {# hoofdletters} }", + "at-most": "Ten hoogste:" + } + }, + "relation": { + "relations": "Relaties", + "direction": "Richting", + "clear-relation-type": "Relatietype wissen", + "search-direction": { + "FROM": "Van", + "TO": "Naar" + }, + "direction-type": { + "FROM": "van", + "TO": "naar" + }, + "from-relations": "Uitgaande relaties", + "to-relations": "Inkomende relaties", + "selected-relations": "{ count, plural, =1 {1 relatie} other {# relaties} } geselecteerd", + "type": "Type", + "to-entity-type": "Naar entiteitstype", + "to-entity-name": "Naar entiteitsnaam", + "from-entity-type": "Van entiteitstype", + "from-entity-name": "Van entiteitsnaam", + "to-entity": "Naar entiteit", + "from-entity": "Van entiteit", + "delete": "Relatie verwijderen", + "relation-type": "Relatietype", + "relation-type-required": "Relatietype is vereist.", + "relation-type-max-length": "Relatietype mag niet langer zijn dan 256 tekens", + "any-relation-type": "Elk type", + "add": "Relatie toevoegen", + "edit": "Relatie bewerken", + "delete-to-relation-title": "Weet je zeker dat je de relatie naar entiteit '{{entityName}}' wilt verwijderen?", + "delete-to-relation-text": "Wees voorzichtig, na bevestiging wordt de entiteit '{{entityName}}' ontkoppeld van de huidige entiteit.", + "delete-to-relations-title": "Weet je zeker dat je { count, plural, =1 {1 relatie} other {# relaties} } wilt verwijderen?", + "delete-to-relations-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde relaties verwijderd en de bijbehorende entiteiten ontkoppeld van de huidige entiteit.", + "delete-from-relation-title": "Weet je zeker dat je de relatie van entiteit '{{entityName}}' wilt verwijderen?", + "delete-from-relation-text": "Wees voorzichtig, na bevestiging wordt de huidige entiteit ontkoppeld van entiteit '{{entityName}}'.", + "delete-from-relations-title": "Weet je zeker dat je { count, plural, =1 {1 relatie} other {# relaties} } wilt verwijderen?", + "delete-from-relations-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde relaties verwijderd en wordt de huidige entiteit ontkoppeld van de bijbehorende entiteiten.", + "remove-relation-filter": "Relatiefilter verwijderen", + "remove-filter": "Filter verwijderen", + "add-relation-filter": "Relatiefilter toevoegen", + "any-relation": "Elke relatie", + "relation-filters": "Relatiefilters", + "additional-info": "Aanvullende info (JSON)", + "invalid-additional-info": "Kan aanvullende info JSON niet verwerken.", + "no-relations-text": "Geen relaties gevonden", + "not": "Niet" + }, + "resource": { + "add": "Bron toevoegen", + "all-types": "Alle", + "copyId": "Kopieer bron-ID", + "delete": "Bron verwijderen", + "delete-resource-text": "Wees voorzichtig, na bevestiging wordt de bron onherstelbaar verwijderd.", + "delete-resource-title": "Weet je zeker dat je de bron '{{resourceTitle}}' wilt verwijderen?", + "delete-resources-action-title": "Verwijder { count, plural, =1 {1 bron} other {# bronnen} }", + "delete-resources-text": "Let op: de geselecteerde bronnen worden verwijderd, zelfs als ze worden gebruikt in apparaatsprofielen.", + "delete-resources-title": "Weet je zeker dat je { count, plural, =1 {1 bron} other {# bronnen} } wilt verwijderen?", + "download": "Bron downloaden", + "drop-file": "Sleep een bronbestand of klik om een bestand te selecteren om te uploaden.", + "drop-resource-file-or": "Sleep een bronbestand of", + "empty": "Bron is leeg", + "file-name": "Bestandsnaam", + "idCopiedMessage": "Bron-ID is gekopieerd naar klembord", + "no-resource-matching": "Geen bron die overeenkomt met '{{widgetsBundle}}' gevonden.", + "no-resource-text": "Geen bronnen gevonden", + "open-widgets-bundle": "Open widgets bundel", + "resource": "Bron", + "resource-file": "Bronbestand", + "resource-files": "Bronbestanden", + "resource-library-details": "Brondetails", + "resource-type": "Brontype", + "resources-library": "Bronbibliotheek", + "search": "Zoek bronnen", + "selected-resources": "{ count, plural, =1 {1 bron} other {# bronnen} } geselecteerd", + "system": "Systeem", + "title": "Titel", + "title-required": "Titel is vereist.", + "title-max-length": "Titel mag niet langer zijn dan 256 tekens", + "type": { + "jks": "JKS", + "js-module": "JS-module", + "lwm2m-model": "LWM2M-model", + "pkcs-12": "PKCS #12" + }, + "resource-sub-type": "Subtype", + "sub-type": { + "image": "afbeelding", + "scada-symbol": "Scada-symbool", + "extension": "Extensie", + "module": "Module" + } + }, + "javascript": { + "add": "JavaScript-bron toevoegen", + "delete": "JavaScript-bron verwijderen", + "delete-javascript-resource-text": "Wees voorzichtig, na bevestiging wordt de JavaScript-bron onherstelbaar verwijderd.", + "delete-javascript-resource-title": "Weet je zeker dat je de JavaScript-bron '{{resourceTitle}}' wilt verwijderen?", + "delete-javascript-resources-action-title": "Verwijder JavaScript { count, plural, =1 {1 bron} other {# bronnen} }", + "delete-javascript-resources-text": "Let op: de geselecteerde JavaScript-bronnen worden verwijderd, zelfs als ze worden gebruikt in JavaScript-functies.", + "delete-javascript-resources-title": "Weet je zeker dat je JavaScript { count, plural, =1 {1 bron} other {# bronnen} } wilt verwijderen?", + "delete-javascript-resource-in-use-text": "Als je de JavaScript-bron toch wilt verwijderen, klik op de knop Toch verwijderen.", + "download": "JavaScript-bron downloaden", + "upload-from-file": "Upload JavaScript vanaf bestand", + "resource-file": "JavaScript-bronbestand", + "drop-file": "Sleep een JavaScript-bestand of klik om een bestand te selecteren om te uploaden.", + "drop-resource-file-or": "Sleep een JavaScript-bestand of", + "javascript-library": "JavaScript-bibliotheek", + "javascript-type": "JavaScript-type", + "javascript-resource-details": "Details van JavaScript-bron", + "javascript-resource-is-in-use": "JavaScript-bron wordt gebruikt door andere entiteiten", + "javascript-resources-are-in-use": "JavaScript-bronnen worden gebruikt door andere entiteiten", + "javascript-resource-is-in-use-text": "De JavaScript-bron '{{title}}' is niet verwijderd omdat deze wordt gebruikt door de volgende entiteiten:", + "javascript-resources-are-in-use-text": "Niet alle JavaScript-bronnen zijn verwijderd omdat ze worden gebruikt door andere entiteiten.
Je kunt de verwijzende entiteiten bekijken door op de knop Referenties te klikken in de bijbehorende rij.
Als je deze bronnen toch wilt verwijderen, selecteer ze dan in de onderstaande tabel en klik op de knop Geselecteerde verwijderen.", + "search": "Zoek JavaScript-bronnen", + "selected-javascript-resources": "{ count, plural, =1 {1 JavaScript-bron} other {# JavaScript-bronnen} } geselecteerd", + "no-javascript-resource-text": "Geen JavaScript-bronnen gevonden", + "all-types": "Alle", + "module-script": "Modulescript" + }, + "rpc": { + "error": { + "target-device-is-not-set": "Doelapparaat is niet ingesteld!", + "invalid-target-entity": "RPC-opdrachten worden niet ondersteund door {{entityType}} entiteit.", + "failed-to-resolve-target-device": "Kan doelapparaat niet bepalen!", + "request-timeout": "Verzoek time-out", + "rpc-http-error": "Fout: {{status}} - {{statusText}}" + } + }, + "rulechain": { + "rulechain": "Regelketen", + "rulechain-events": "Regelketengebeurtenissen", + "rulechains": "Regelketens", + "root": "Hoofd", + "delete": "Verwijder regelketen", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet minder dan 256 tekens zijn", + "description": "Beschrijving", + "add": "Voeg regelketen toe", + "set-root": "Maak regelketen hoofd", + "set-root-rulechain-title": "Weet je zeker dat je de regelketen '{{ruleChainName}}' als hoofd wilt instellen?", + "set-root-rulechain-text": "Na bevestiging wordt de regelketen hoofd en zal alle inkomende transportberichten verwerken.", + "delete-rulechain-title": "Weet je zeker dat je de regelketen '{{ruleChainName}}' wilt verwijderen?", + "delete-rulechain-text": "Wees voorzichtig, na bevestiging wordt de regelketen en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-rulechains-title": "Weet je zeker dat je { count, plural, =1 {1 regelketen} other {# regelketens} } wilt verwijderen?", + "delete-rulechains-action-title": "Verwijder { count, plural, =1 {1 regelketen} other {# regelketens} }", + "delete-rulechains-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde regelketens verwijderd en alle gerelateerde gegevens onherstelbaar.", + "add-rulechain-text": "Nieuwe regelketen toevoegen", + "no-rulechains-text": "Geen regelketens gevonden", + "rulechain-details": "Details van regelketen", + "details": "Details", + "events": "Gebeurtenissen", + "system": "Systeem", + "import": "Importeer regelketen", + "export": "Exporteer regelketen", + "export-failed-error": "Kan regelketen niet exporteren: {{error}}", + "create-new-rulechain": "Maak nieuwe regelketen", + "rulechain-file": "Regelketenbestand", + "invalid-rulechain-file-error": "Kan regelketen niet importeren: Ongeldige gegevensstructuur.", + "copyId": "Kopieer regelketen-ID", + "idCopiedMessage": "Regelketen-ID is gekopieerd naar klembord", + "select-rulechain": "Selecteer regelketen", + "no-rulechains-matching": "Geen regelketens gevonden die overeenkomen met '{{entity}}'.", + "rulechain-required": "Regelketen is verplicht", + "management": "Regelbeheer", + "debug-mode": "Debug-modus", + "search": "Zoek regelketens", + "selected-rulechains": "{ count, plural, =1 {1 regelketen} other {# regelketens} } geselecteerd", + "open-rulechain": "Open regelketen", + "edge-template-root": "Sjabloon-hoofd", + "assign-to-edge": "Toewijzen aan edge", + "edge-rulechain": "Edge regelketen", + "unassign-rulechain-from-edge-text": "Na bevestiging wordt de regelketen losgekoppeld en is niet meer toegankelijk voor de edge.", + "unassign-rulechains-from-edge-title": "Weet je zeker dat je { count, plural, =1 {1 regelketen} other {# regelketens} } wilt loskoppelen?", + "unassign-rulechains-from-edge-text": "Na bevestiging worden alle geselecteerde regelketens losgekoppeld en niet meer toegankelijk voor de edge.", + "assign-rulechain-to-edge-title": "Regelketen(s) toewijzen aan edge", + "assign-rulechain-to-edge-text": "Selecteer de regelketens om aan de edge toe te wijzen", + "set-edge-template-root-rulechain": "Maak regelketen sjabloon-hoofd", + "set-edge-template-root-rulechain-title": "Weet je zeker dat je de regelketen '{{ruleChainName}}' als sjabloon-hoofd wilt instellen?", + "set-edge-template-root-rulechain-text": "Na bevestiging wordt de regelketen ingesteld als sjabloon-hoofd en gebruikt voor nieuwe edges.", + "invalid-rulechain-type-error": "Kan regelketen niet importeren: Ongeldig type. Verwacht type is {{expectedRuleChainType}}.", + "set-auto-assign-to-edge": "Regelketen automatisch toewijzen bij edge-creatie", + "set-auto-assign-to-edge-title": "Weet je zeker dat je de regelketen '{{ruleChainName}}' automatisch wilt toewijzen bij edge-creatie?", + "set-auto-assign-to-edge-text": "Na bevestiging wordt de edge regelketen automatisch toegewezen bij edge-creatie.", + "unset-auto-assign-to-edge": "Niet automatisch toewijzen bij edge-creatie", + "unset-auto-assign-to-edge-title": "Weet je zeker dat je de regelketen '{{ruleChainName}}' niet automatisch wilt toewijzen bij edge-creatie?", + "unset-auto-assign-to-edge-text": "Na bevestiging wordt de edge regelketen niet meer automatisch toegewezen bij edge-creatie.", + "unassign-rulechain-title": "Weet je zeker dat je de regelketen '{{ruleChainName}}' wilt loskoppelen?", + "unassign-rulechains": "Regelketens loskoppelen" + }, + "rulenode": { + "rule-node-events": "Regelknoopgebeurtenissen", + "details": "Details", + "events": "Gebeurtenissen", + "search": "Zoek knopen", + "open-node-library": "Open knopenbibliotheek", + "close-node-library": "Sluit knopenbibliotheek", + "add": "Voeg regelknoop toe", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet minder dan 256 tekens zijn", + "type": "Type", + "rule-node-description": "Beschrijving van regelknoop", + "delete": "Verwijder regelknoop", + "select-all-objects": "Selecteer alle knopen en verbindingen", + "deselect-all-objects": "Deselecteer alle knopen en verbindingen", + "delete-selected-objects": "Verwijder geselecteerde knopen en verbindingen", + "delete-selected": "Verwijder geselecteerde", + "create-nested-rulechain": "Maak geneste regelketen", + "select-all": "Selecteer alles", + "copy-selected": "Kopieer geselecteerde", + "deselect-all": "Deselecteer alles", + "rulenode-details": "Details van regelknoop", + "debug-mode": "Debug-modus", + "singleton": "Singleton", + "configuration": "Configuratie", + "link": "Verbinding", + "link-details": "Details van regelknoopverbinding", + "add-link": "Voeg verbinding toe", + "link-label": "Verbindingslabel", + "link-label-required": "Verbindingslabel is verplicht.", + "custom-link-label": "Aangepast verbindingslabel", + "custom-link-label-required": "Aangepast verbindingslabel is verplicht.", + "link-labels": "Verbindingslabels", + "link-labels-required": "Verbindingslabels zijn verplicht.", + "no-link-labels-found": "Geen verbindingslabels gevonden", + "no-link-label-matching": "'{{label}}' niet gevonden.", + "create-new-link-label": "Maak een nieuwe!", + "type-filter": "Filter", + "type-filter-details": "Filter inkomende berichten met geconfigureerde voorwaarden", + "type-enrichment": "Verrijking", + "type-enrichment-details": "Voeg extra informatie toe aan berichtmetadata", + "type-transformation": "Transformatie", + "type-transformation-details": "Wijzig berichtinhoud en metadata", + "type-action": "Actie", + "type-action-details": "Voer speciale actie uit", + "type-external": "Extern", + "type-external-details": "Interageert met extern systeem", + "type-rule-chain": "Regelketen", + "type-rule-chain-details": "Stuurt inkomende berichten door naar opgegeven regelketen", + "type-flow": "Stroom", + "type-flow-details": "Organiseert berichtenstroom", + "type-input": "Invoer", + "type-input-details": "Logische invoer van regelketen, stuurt berichten door naar volgende regelknoop", + "type-unknown": "Onbekend", + "type-unknown-details": "Niet-opgeloste regelknoop", + "directive-is-not-loaded": "Gedefinieerde configuratierichtlijn '{{directiveName}}' is niet beschikbaar.", + "ui-resources-load-error": "Kan configuratie UI-bronnen niet laden.", + "invalid-target-rulechain": "Kan doelregelketen niet oplossen!", + "test-script-function": "Test scriptfunctie", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", + "message": "Bericht", + "message-type": "Berichttype", + "select-message-type": "Selecteer berichttype", + "message-type-required": "Berichttype is verplicht", + "metadata": "Metadata", + "metadata-required": "Metadata-items mogen niet leeg zijn.", + "output": "Uitvoer", + "test": "Test", + "help": "Hulp", + "reset-debug-settings": "Reset debug-instellingen in alle knopen", + "test-with-this-message": "{{test}} met dit bericht", + "queue-hint": "Selecteer een wachtrij voor doorsturen naar een andere wachtrij. Standaard wordt 'Main' gebruikt.", + "queue-singleton-hint": "Selecteer een wachtrij voor doorsturen in multi-instantie omgevingen. Standaard wordt 'Main' gebruikt." + }, + "rule-node-config": { + "id": "Id", + "additional-info": "Aanvullende informatie", + "advanced-settings": "Geavanceerde instellingen", + "create-entity-if-not-exists": "Maak nieuwe entiteit aan indien deze niet bestaat", + "create-entity-if-not-exists-hint": "Indien ingeschakeld, wordt een nieuwe entiteit met opgegeven parameters aangemaakt tenzij deze al bestaat. Bestaande entiteiten worden gebruikt zoals ze zijn voor de relatie.", + "select-device-connectivity-event": "Selecteer apparaatconnectiviteitsgebeurtenis", + "entity-name-pattern": "Naam patroon", + "device-name-pattern": "Apparaatnaam", + "asset-name-pattern": "Assetnaam", + "entity-view-name-pattern": "Entity view naam", + "customer-title-pattern": "Klanttitel", + "dashboard-name-pattern": "Dashboardtitel", + "user-name-pattern": "Gebruiker e-mail", + "edge-name-pattern": "Edge naam", + "entity-name-pattern-required": "Naam patroon is verplicht", + "entity-name-pattern-hint": "Naam patroon veld ondersteunt templatization. Gebruik $[messageKey] om waarde uit bericht te halen en ${metadataKey} om waarde uit metadata te halen.", + "copy-message-type": "Kopieer berichttype", + "entity-type-pattern": "Type patroon", + "entity-type-pattern-required": "Type patroon is verplicht", + "message-type-value": "Berichttype waarde", + "message-type-value-required": "Berichttype waarde is verplicht", + "message-type-value-max-length": "Berichttype waarde moet minder dan 256 tekens zijn", + "output-message-type": "Uitgaand berichttype", + "entity-cache-expiration": "Vervaltermijn entiteitencache (sec)", + "entity-cache-expiration-hint": "Specificeert maximale tijdsinterval om gevonden entiteiten op te slaan. Waarde 0 betekent dat records nooit vervallen.", + "entity-cache-expiration-required": "Vervaltermijn entiteitencache is verplicht.", + "entity-cache-expiration-range": "Vervaltermijn entiteitencache moet groter dan of gelijk zijn aan 0.", + "customer-name-pattern": "Klanttitel", + "customer-name-pattern-required": "Klanttitel is verplicht", + "customer-name-pattern-hint": "Gebruik $[messageKey] om waarde uit bericht te halen en ${metadataKey} om waarde uit metadata te halen.", + "create-customer-if-not-exists": "Maak nieuwe klant aan indien deze niet bestaat", + "unassign-from-customer": "Ontkoppel van specifieke klant als afzender dashboard is", + "unassign-from-customer-tooltip": "Alleen dashboards kunnen aan meerdere klanten tegelijk worden toegewezen. \nIndien afzender een dashboard is, moet de klanttitel expliciet worden opgegeven om te ontkoppelen.", + "customer-cache-expiration": "Vervaltermijn klantencache (sec)", + "customer-cache-expiration-hint": "Specificeert maximale tijdsinterval om gevonden klantrecords op te slaan. Waarde 0 betekent dat records nooit vervallen.", + "customer-cache-expiration-required": "Vervaltermijn klantencache is verplicht.", + "customer-cache-expiration-range": "Vervaltermijn klantencache moet groter dan of gelijk zijn aan 0.", + "interval-start": "Interval start", + "interval-end": "Interval einde", + "time-unit": "Tijdseenheid", + "fetch-mode": "Ophaalmodus", + "order-by-timestamp": "Orden op tijdstempel", + "limit": "Limiet", + "limit-hint": "Minimale limietwaarde is 2, maximaal - 1000. Om één item op te halen, selecteer ophaalmodus 'Eerste' of 'Laatste'.", + "limit-required": "Limiet is verplicht.", + "limit-range": "Limiet moet tussen 2 en 1000 liggen.", + "time-unit-milliseconds": "Milliseconden", + "time-unit-seconds": "Seconden", + "time-unit-minutes": "Minuten", + "time-unit-hours": "Uren", + "time-unit-days": "Dagen", + "time-value-range": "Toegestane bereik van 1 tot 2147483647.", + "start-interval-value-required": "Interval start is verplicht.", + "end-interval-value-required": "Interval einde is verplicht.", + "filter": "Filter", + "switch": "Schakelaar", + "math-templatization-tooltip": "Dit veld ondersteunt templatization. Gebruik $[messageKey] om waarde uit bericht te halen en ${metadataKey} om waarde uit metadata te halen.", + "add-message-type": "Voeg berichttype toe", + "select-message-types-required": "Ten minste één berichttype moet worden geselecteerd.", + "select-message-types": "Selecteer berichttypes", + "no-message-types-found": "Geen berichttypes gevonden", + "no-message-type-matching": "'{{messageType}}' niet gevonden.", + "create-new-message-type": "Maak een nieuwe aan.", + "message-types-required": "Berichttypes zijn verplicht.", + "client-attributes": "Client attributen", + "shared-attributes": "Gedeelde attributen", + "server-attributes": "Server attributen", + "attributes-keys": "Attribuutsleutels", + "attributes-keys-required": "Attribuutsleutels zijn verplicht", + "attributes-scope": "Attribuutscope", + "attributes-scope-value": "Attribuutscope waarde", + "attributes-scope-value-copy": "Kopieer attribuutscope waarde", + "attributes-scope-hint": "Gebruik de 'scope' metadata sleutel om dynamisch de attribuutscope per bericht te bepalen. Indien opgegeven, overschrijft dit de scope in de configuratie.", + "notify-device": "Forceer notificatie naar apparaat", + "send-attributes-updated-notification": "Verzend melding bij bijgewerkte attributen", + "send-attributes-updated-notification-hint": "Stuur melding over bijgewerkte attributen als apart bericht naar regelengine wachtrij.", + "send-attributes-deleted-notification": "Verzend melding bij verwijderde attributen", + "send-attributes-deleted-notification-hint": "Stuur melding over verwijderde attributen als apart bericht naar regelengine wachtrij.", + "update-attributes-only-on-value-change": "Sla attributen alleen op bij waarde wijziging", + "update-attributes-only-on-value-change-hint": "Werkt attributen bij bij elk inkomend bericht, ongeacht of de waarde veranderd is. Verhoogt API-gebruik en vermindert prestaties.", + "update-attributes-only-on-value-change-hint-enabled": "Werkt attributen alleen bij als hun waarde veranderd is. Geen update indien waarde hetzelfde blijft.", + "fetch-credentials-to-metadata": "Haal referenties op naar metadata", + "notify-device-on-update-hint": "Indien ingeschakeld, forceer melding naar apparaat bij update gedeelde attributen. Anders wordt dit geregeld door 'notifyDevice' parameter in metadata.", + "notify-device-on-delete-hint": "Indien ingeschakeld, forceer melding naar apparaat bij verwijdering gedeelde attributen. Anders wordt dit geregeld door 'notifyDevice' parameter in metadata.", + "latest-timeseries": "Laatste tijdreeks gegevenssleutels", + "timeseries-keys": "Tijdreeks sleutels", + "timeseries-keys-required": "Ten minste één tijdreeks sleutel moet worden geselecteerd.", + "add-timeseries-key": "Voeg tijdreeks sleutel toe", + "add-message-field": "Voeg berichtveld toe", + "relation-search-parameters": "Relatie zoekparameters", + "relation-parameters": "Relatieparameters", + "add-metadata-field": "Voeg metadata veld toe", + "data-keys": "Berichtveld namen", + "copy-from": "Kopieer van", + "data-to-metadata": "Data naar metadata", + "metadata-to-data": "Metadata naar data", + "use-regular-expression-hint": "Gebruik reguliere expressie om sleutels op patroon te kopiëren.\n\nTips & trucs:\nDruk op 'Enter' om het veldnaam in te voeren.\nDruk op 'Backspace' om veldnaam te verwijderen. Meerdere veldnamen worden ondersteund.", + "interval": "Interval", + "interval-required": "Interval is verplicht", + "interval-hint": "Deduplicatie-interval in seconden.", + "interval-min-error": "Minimale toegestane waarde is 1", + "max-pending-msgs": "Max in behandeling zijnde berichten", + "max-pending-msgs-hint": "Maximaal aantal berichten dat in het geheugen wordt opgeslagen per unieke deduplicatie-ID.", + "max-pending-msgs-required": "Max in behandeling zijnde berichten is verplicht", + "max-pending-msgs-max-error": "Maximale toegestane waarde is 1000", + "max-pending-msgs-min-error": "Minimale toegestane waarde is 1", + "max-retries": "Max pogingen", + "max-retries-required": "Max pogingen is verplicht", + "max-retries-hint": "Maximaal aantal pogingen om deduplicatieberichten naar de wachtrij te pushen. Tussen pogingen zit 10 seconden vertraging", + "max-retries-max-error": "Maximale toegestane waarde is 100", + "max-retries-min-error": "Minimale toegestane waarde is 0", + "strategy": "Strategie", + "strategy-required": "Strategie is verplicht", + "strategy-all-hint": "Retourneer alle berichten die tijdens de deduplicatieperiode zijn aangekomen als één JSON-arraybericht. Elk element bevat 'msg' en 'metadata' eigenschappen.", + "strategy-first-hint": "Retourneer het eerste bericht dat aankwam tijdens de deduplicatieperiode.", + "strategy-last-hint": "Retourneer het laatste bericht dat aankwam tijdens de deduplicatieperiode.", + "first": "Eerste", + "last": "Laatste", + "all": "Alle", + "output-msg-type-hint": "Het berichttype van het deduplicatieresultaat.", + "queue-name-hint": "De naam van de wachtrij waarin het deduplicatieresultaat wordt gepubliceerd.", + "keys": "Sleutels", + "keys-required": "Sleutels zijn verplicht", + "rename-keys-in": "Hernoem sleutels in", + "data": "Data", + "message": "Bericht", + "metadata": "Metadata", + "current-key-name": "Huidige sleutelnaam", + "key-name-required": "Sleutelnaam is verplicht", + "new-key-name": "Nieuwe sleutelnaam", + "new-key-name-required": "Nieuwe sleutelnaam is verplicht", + "metadata-keys": "Metadata veldnamen", + "json-path-expression": "JSON pad expressie", + "json-path-expression-required": "JSON pad expressie is verplicht", + "json-path-expression-hint": "JSONPath specificeert een pad naar een element in een JSON-structuur. '$' vertegenwoordigt het rootobject of array.", + "relations-query": "Relaties query", + "device-relations-query": "Apparaatrelaties query", + "max-relation-level": "Maximale relatieniveau", + "max-relation-level-error": "Waarde moet groter dan 0 of leeg zijn.", + "max-relation-level-invalid": "Waarde moet een geheel getal zijn.", + "relation-type": "Relatietype", + "relation-type-pattern": "Relatietype patroon", + "relation-type-pattern-required": "Relatietype patroon is verplicht", + "relation-types-list": "Relatietypes om te propageren", + "relation-types-list-hint": "Als er geen propagatierelatietypes zijn geselecteerd, worden alarmen zonder filtering gepropageerd.", + "unlimited-level": "Onbeperkt niveau", + "latest-telemetry": "Laatste telemetrie", + "add-telemetry-key": "Voeg telemetriesleutel toe", + "delete-from": "Verwijder uit", + "use-regular-expression-delete-hint": "Gebruik reguliere expressie om sleutels op patroon te verwijderen.\n\nTips & trucs:\nDruk op 'Enter' om veldnaam in te voeren.\nDruk op 'Backspace' om veldnaam te verwijderen.\nMeerdere veldnamen worden ondersteund.", + "fetch-into": "Haal op in", + "attr-mapping": "Attribuuttoewijzing:", + "source-attribute": "Bronattribuut sleutel", + "source-attribute-required": "Bronattribuut sleutel is verplicht.", + "source-telemetry": "Brontelemetriesleutel", + "source-telemetry-required": "Brontelemetriesleutel is verplicht.", + "target-key": "Doelsleutel", + "target-key-required": "Doelsleutel is verplicht.", + "attr-mapping-required": "Ten minste één toewijzing moet worden opgegeven.", + "fields-mapping": "Velden toewijzing", + "fields-mapping-hint": "Indien veld op $entityId staat, wordt ID van afzender opgeslagen in opgegeven tabelkolom.", + "relations-query-config-direction-suffix": "afzender", + "profile-name": "Profielnaam", + "fetch-circle-parameter-info-from-metadata-hint": "Metadata veld '{{perimeterKeyName}}' moet het volgende formaat hebben: {\"latitude\":48.196, \"longitude\":24.6532, \"radius\":100.0, \"radiusUnit\":\"METER\"}", + "fetch-poligon-parameter-info-from-metadata-hint": "Metadata veld '{{perimeterKeyName}}' moet het volgende formaat hebben: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "Gebruik $[messageKey] om waarde uit bericht te halen en ${metadataKey} om waarde uit metadata te halen.", + "fields-mapping-required": "Ten minste één veldtoewijzing moet worden opgegeven.", + "at-least-one-field-required": "Minstens één invoerveld moet een waarde bevatten.", + "originator-fields-sv-map-hint": "Doelsleutels ondersteunen templatization. Gebruik $[messageKey] of ${metadataKey}.", + "sv-map-hint": "Alleen doelsleutels ondersteunen templatization. Gebruik $[messageKey] of ${metadataKey}.", + "source-field": "Bronveld", + "source-field-required": "Bronveld is verplicht.", + "originator-source": "Afzender bron", + "new-originator": "Nieuwe afzender", + "originator-customer": "Klant", + "originator-tenant": "Huurder", + "originator-related": "Gerelateerde entiteit", + "originator-alarm-originator": "Afzender van alarm", + "originator-entity": "Entiteit via naam patroon", + "clone-message": "Bericht klonen", + "transform": "Transformeren", + "default-ttl": "Standaard TTL", + "default-ttl-required": "Standaard TTL is verplicht.", + "default-ttl-hint": "Rule node haalt de Time-to-Live (TTL) waarde uit de metadata van het bericht. Indien niet aanwezig, wordt de TTL uit de configuratie gebruikt. Bij waarde 0 wordt de TTL uit het tenantprofiel toegepast.", + "default-ttl-zero-hint": "TTL wordt niet toegepast als de waarde 0 is.", + "min-default-ttl-message": "Alleen minimale TTL van 0 is toegestaan.", + "generation-parameters": "Genereerparameters", + "message-count": "Limiet gegenereerde berichten (0 - onbeperkt)", + "message-count-required": "Limiet voor gegenereerde berichten is verplicht.", + "min-message-count-message": "Alleen 0 is toegestaan als minimum berichtenaantal.", + "period-seconds": "Periode in seconden", + "period-seconds-required": "Periode is verplicht.", + "generation-frequency-seconds": "Genereerfrequentie in seconden", + "generation-frequency-required": "Genereerfrequentie is verplicht.", + "min-generation-frequency-message": "Minimaal 60 seconden is toegestaan.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Gebruik periode in seconden patroon", + "use-metadata-period-in-seconds-patterns-hint": "Indien geselecteerd, gebruikt de rule node de interval van de metadata of data, aangenomen dat deze in seconden is.", + "period-in-seconds-pattern": "Periode in seconden patroon", + "period-in-seconds-pattern-required": "Periode in seconden patroon is verplicht", + "min-period-seconds-message": "Minimale toegestane periode is 60 seconden.", + "originator": "Afzender", + "message-body": "Berichtinhoud", + "message-metadata": "Berichtmetadata", + "generate": "Genereren", + "current-rule-node": "Huidige Rule Node", + "current-tenant": "Huidige Huurder", + "generator-function": "Genereerfunctie", + "test-generator-function": "Test genereerfunctie", + "generator": "Generator", + "test-filter-function": "Test filterfunctie", + "test-switch-function": "Test switchfunctie", + "test-transformer-function": "Test transformatorfunctie", + "transformer": "Transformator", + "alarm-create-condition": "Voorwaarde voor alarm aanmaken", + "test-condition-function": "Test voorwaarde-functie", + "alarm-clear-condition": "Voorwaarde voor alarm wissen", + "alarm-details-builder": "Alarm details samensteller", + "test-details-function": "Test details functie", + "alarm-type": "Alarmtype", + "select-entity-types": "Selecteer entiteitstypen", + "alarm-type-required": "Alarmtype is verplicht.", + "alarm-severity": "Alarm ernst", + "alarm-severity-required": "Alarm ernst is verplicht", + "alarm-severity-pattern": "Alarm ernst patroon", + "alarm-status-filter": "Alarmstatusfilter", + "alarm-status-list-empty": "Alarmstatuslijst is leeg", + "no-alarm-status-matching": "Geen overeenkomende alarmstatus gevonden.", + "propagate": "Alarm propageren naar gerelateerde entiteiten", + "propagate-to-owner": "Propageren naar entiteitseigenaar (Klant of Huurder)", + "propagate-to-tenant": "Propageren naar Huurder", + "condition": "Voorwaarde", + "details": "Details", + "to-string": "Naar tekenreeks", + "test-to-string-function": "Test naar-tekenreeks functie", + "from-template": "Van", + "from-template-required": "Van is verplicht", + "message-to-metadata": "Bericht naar metadata", + "metadata-to-message": "Metadata naar bericht", + "from-message": "Van bericht", + "from-metadata": "Van metadata", + "to-template": "Naar", + "to-template-required": "Naar is verplicht", + "mail-address-list-template-hint": "Komma-gescheiden lijst van adressen, gebruik ${metadataKey} voor metadatawaarde, $[messageKey] voor berichtwaarde", + "cc-template": "Cc", + "bcc-template": "Bcc", + "subject-template": "Onderwerp", + "subject-template-required": "Onderwerp is verplicht", + "body-template": "Inhoud", + "body-template-required": "Inhoud is verplicht", + "dynamic-mail-body-type": "Dynamisch e-mailinhoudtype", + "mail-body-type": "E-mailinhoudtype", + "body-type-template": "Inhoudstype sjabloon", + "reply-routing-configuration": "Configuratie voor antwoordroutering", + "rpc-reply-routing-configuration-hint": "Deze configuratieparameters geven metadata-sleutelnamen aan voor service, sessie en verzoek-ID om een antwoord te versturen.", + "reply-routing-configuration-hint": "Deze configuratieparameters geven metadata-sleutelnamen aan voor service en verzoek-ID om een antwoord te versturen.", + "request-id-metadata-attribute": "Verzoek ID", + "service-id-metadata-attribute": "Service ID", + "session-id-metadata-attribute": "Sessie ID", + "timeout-sec": "Timeout in seconden", + "timeout-required": "Timeout is verplicht", + "min-timeout-message": "Minimale toegestane timeout is 0.", + "endpoint-url-pattern": "Endpoint URL patroon", + "endpoint-url-pattern-required": "Endpoint URL patroon is verplicht", + "request-method": "Aanvraagmethode", + "use-simple-client-http-factory": "Gebruik eenvoudige HTTP-clientfactory", + "ignore-request-body": "Zonder request body", + "parse-to-plain-text": "Omzetten naar platte tekst", + "parse-to-plain-text-hint": "Indien geselecteerd, wordt de payload van het bericht als JSON-string omgezet naar platte tekst, bijv. msg = \"Hello,\\t\"world\"\" wordt Hello, \"world\"", + "read-timeout": "Leestime-out in milliseconden", + "read-timeout-hint": "Waarde 0 betekent oneindige time-out", + "max-parallel-requests-count": "Maximaal aantal parallelle verzoeken", + "max-parallel-requests-count-hint": "Waarde 0 betekent geen limiet op parallelle verwerking", + "max-response-size": "Maximale responsgrootte (in KB)", + "max-response-size-hint": "Maximale geheugengrootte voor het bufferen van gegevens bij decoderen of coderen van HTTP-berichten zoals JSON of XML", + "headers": "Headers", + "headers-hint": "Gebruik ${metadataKey} voor waarde uit metadata, $[messageKey] voor waarde uit berichtinhoud", + "header": "Header", + "header-required": "Header is verplicht", + "value": "Waarde", + "value-required": "Waarde is verplicht", + "topic-pattern": "Onderwerppatroon", + "key-pattern": "Sleutelpatteron", + "key-pattern-hint": "Optioneel. Indien partitie opgegeven, wordt deze gebruikt. Anders wordt de sleutel gebruikt of wordt een partitie willekeurig toegewezen.", + "topic-pattern-required": "Onderwerppatroon is verplicht", + "topic": "Onderwerp", + "topic-required": "Onderwerp is verplicht", + "bootstrap-servers": "Bootstrap-servers", + "bootstrap-servers-required": "Bootstrap-servers zijn verplicht", + "other-properties": "Overige eigenschappen", + "key": "Sleutel", + "key-required": "Sleutel is verplicht", + "retries": "Aantal automatische pogingen bij mislukking", + "min-retries-message": "Minimale waarde is 0", + "batch-size-bytes": "Batchgrootte in bytes", + "min-batch-size-bytes-message": "Minimale batchgrootte is 0", + "linger-ms": "Bufferperiode lokaal (ms)", + "min-linger-ms-message": "Minimale waarde is 0 ms", + "buffer-memory-bytes": "Maximale clientbuffer in bytes", + "min-buffer-memory-message": "Minimale buffer is 0", + "memory-buffer-size-range": "Buffergrootte moet tussen 0 en {{max}} KB liggen", + "acks": "Aantal bevestigingen", + "topic-arn-pattern": "ARN-patroon van onderwerp", + "topic-arn-pattern-required": "ARN-patroon van onderwerp is verplicht", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID is verplicht", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key is verplicht", + "aws-region": "AWS-regio", + "aws-region-required": "AWS-regio is verplicht", + "exchange-name-pattern": "Exchange-naam patroon", + "routing-key-pattern": "Routeringssleutel patroon", + "message-properties": "Berichteigenschappen", + "host": "Host", + "host-required": "Host is verplicht", + "port": "Poort", + "port-required": "Poort is verplicht", + "port-range": "Poort moet tussen 1 en 65535 liggen", + "virtual-host": "Virtuele host", + "username": "Gebruikersnaam", + "password": "Wachtwoord", + "automatic-recovery": "Automatisch herstel", + "connection-timeout-ms": "Verbindingstime-out (ms)", + "min-connection-timeout-ms-message": "Minimale waarde is 0 ms", + "handshake-timeout-ms": "Handshake-time-out (ms)", + "min-handshake-timeout-ms-message": "Minimale waarde is 0 ms", + "client-properties": "Clienteigenschappen", + "queue-url-pattern": "Wachtrij URL patroon", + "queue-url-pattern-required": "Wachtrij URL patroon is verplicht", + "delay-seconds": "Vertraging (seconden)", + "min-delay-seconds-message": "Minimale waarde is 0 seconden", + "max-delay-seconds-message": "Maximale waarde is 900 seconden", + "name": "Naam", + "name-required": "Naam is verplicht", + "queue-type": "Wachtrijtype", + "sqs-queue-standard": "Standaard", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "GCP-project-ID", + "gcp-project-id-required": "GCP-project-ID is verplicht", + "gcp-service-account-key": "GCP serviceaccount-sleutelbestand", + "gcp-service-account-key-required": "GCP serviceaccount-sleutelbestand is verplicht", + "pubsub-topic-name": "Onderwerpnaam", + "pubsub-topic-name-required": "Onderwerpnaam is verplicht", + "message-attributes": "Berichtattributen", + "message-attributes-hint": "Gebruik ${metadataKey} voor waarden uit metadata, $[messageKey] voor waarden uit het berichtlichaam in naam/waarde-velden", + "connect-timeout": "Verbindingstime-out (sec)", + "connect-timeout-required": "Verbindingstime-out is verplicht.", + "connect-timeout-range": "Verbindingstime-out moet tussen 1 en 200 liggen.", + "client-id": "Client ID", + "client-id-hint": "Optioneel. Leeg laten voor automatisch gegenereerde ID. Zorg voor unieke ID in microservices-modus. Gebruik de optie hieronder om service-ID als suffix toe te voegen.", + "append-client-id-suffix": "Voeg service-ID toe als suffix aan Client ID", + "client-id-suffix-hint": "Optioneel. Alleen van toepassing als 'Client ID' is opgegeven. Toevoegen van service-ID als suffix helpt bij microservices-omgevingen.", + "device-id": "Apparaat ID", + "device-id-required": "Apparaat ID is verplicht.", + "clean-session": "Schone sessie", + "enable-ssl": "SSL inschakelen", + "credentials": "Referenties", + "credentials-type": "Type referenties", + "credentials-type-required": "Type referenties is verplicht.", + "credentials-anonymous": "Anoniem", + "credentials-basic": "Basis", + "credentials-pem": "PEM", + "credentials-pem-hint": "Minstens een Server CA-certificaatbestand of een paar Client-certificaat en Client privésleutelbestanden zijn vereist", + "credentials-sas": "Gedeelde Toegangshandtekening", + "sas-key": "SAS-sleutel", + "sas-key-required": "SAS-sleutel is verplicht.", + "hostname": "Hostnaam", + "hostname-required": "Hostnaam is verplicht.", + "azure-ca-cert": "CA-certificaatbestand", + "username-required": "Gebruikersnaam is verplicht.", + "password-required": "Wachtwoord is verplicht.", + "ca-cert": "Server CA-certificaatbestand", + "private-key": "Client privésleutelbestand", + "cert": "Client certificaatbestand", + "no-file": "Geen bestand geselecteerd.", + "drop-file": "Sleep een bestand of klik om te selecteren.", + "private-key-password": "Privésleutel wachtwoord", + "use-system-smtp-settings": "Gebruik systeem SMTP-instellingen", + "use-metadata-dynamic-interval": "Gebruik dynamisch interval", + "metadata-dynamic-interval-hint": "De velden voor intervalstart en -einde ondersteunen templatization. De vervangende waarde moet in milliseconden zijn.", + "use-metadata-interval-patterns-hint": "Indien geselecteerd, gebruikt regelknoop start- en eindintervalpatronen uit berichtmetadata of gegevens in milliseconden.", + "use-message-alarm-data": "Gebruik alarmgegevens uit bericht", + "overwrite-alarm-details": "Overschrijf alarmdetails", + "use-alarm-severity-pattern": "Gebruik alarmernstpatroon", + "check-all-keys": "Controleer dat alle gespecificeerde velden aanwezig zijn", + "check-all-keys-hint": "Controleert of alle gespecificeerde sleutels aanwezig zijn in de berichtgegevens en metadata.", + "check-relation-to-specific-entity": "Controleer relatie met specifieke entiteit", + "check-relation-to-specific-entity-tooltip": "Indien ingeschakeld, controleert op relatie met specifieke entiteit; anders met een willekeurige entiteit.", + "check-relation-hint": "Controleert het bestaan van een relatie op basis van richting en type.", + "delete-relation-with-specific-entity": "Verwijder relatie met specifieke entiteit", + "delete-relation-with-specific-entity-hint": "Verwijdert alleen relatie met één specifieke entiteit als ingeschakeld.", + "delete-relation-hint": "Verwijdert relatie van afzender naar entiteit(en) gebaseerd op richting en type.", + "remove-current-relations": "Verwijder huidige relaties", + "remove-current-relations-hint": "Verwijdert huidige relaties gebaseerd op richting en type.", + "change-originator-to-related-entity": "Wijzig afzender naar gerelateerde entiteit", + "change-originator-to-related-entity-hint": "Verwerkt bericht als afkomstig van een andere entiteit.", + "start-interval": "Intervalstart", + "end-interval": "Intervaleinde", + "start-interval-required": "Intervalstart is verplicht.", + "end-interval-required": "Intervaleinde is verplicht.", + "smtp-protocol": "Protocol", + "smtp-host": "SMTP-host", + "smtp-host-required": "SMTP-host is verplicht.", + "smtp-port": "SMTP-poort", + "smtp-port-required": "SMTP-poort is verplicht.", + "smtp-port-range": "SMTP-poort moet tussen 1 en 65535 liggen.", + "timeout-msec": "Timeout ms", + "min-timeout-msec-message": "Minimale waarde is 0 ms.", + "enter-username": "Voer gebruikersnaam in", + "enter-password": "Voer wachtwoord in", + "enable-tls": "TLS inschakelen", + "tls-version": "TLS-versie", + "enable-proxy": "Proxy inschakelen", + "use-system-proxy-properties": "Gebruik systeemeigenschappen voor proxy", + "proxy-host": "Proxyhost", + "proxy-host-required": "Proxyhost is verplicht.", + "proxy-port": "Proxypoort", + "proxy-port-required": "Proxypoort is verplicht.", + "proxy-port-range": "Proxypoort moet tussen 1 en 65535 liggen.", + "proxy-user": "Proxygebruiker", + "proxy-password": "Proxywachtwoord", + "proxy-scheme": "Proxyschema", + "numbers-to-template": "Telefoonnummers voor template", + "numbers-to-template-required": "Telefoonnummers voor template is verplicht", + "numbers-to-template-hint": "Kommagescheiden telefoonnummers, gebruik ${metadataKey} voor de waarde uit de metadata, $[messageKey] voor de waarde uit de berichtinhoud", + "sms-message-template": "SMS-berichttemplate", + "sms-message-template-required": "SMS-berichttemplate is verplicht", + "use-system-sms-settings": "Gebruik systeem SMS-instellingen", + "min-period-0-seconds-message": "Minimale periode is 0 seconden.", + "max-pending-messages": "Maximaal aantal wachtrijberichten", + "max-pending-messages-required": "Maximaal aantal wachtrijberichten is verplicht.", + "max-pending-messages-range": "Aantal berichten moet tussen 1 en 100000 liggen.", + "originator-types-filter": "Filter op oorsprongtype", + "interval-seconds": "Interval in seconden", + "interval-seconds-required": "Interval is verplicht.", + "int-range": "Waarde mag maximumwaarde van integer niet overschrijden (2147483648)", + "min-interval-seconds-message": "Minimaal 1 seconde interval is vereist.", + "output-timeseries-key-prefix": "Prefix voor output tijdreeksleutel", + "output-timeseries-key-prefix-required": "Prefix voor tijdreeksleutel is verplicht.", + "separator-hint": "Druk op \"Enter\" om het veldinvoer te voltooien.", + "select-details": "Selecteer details", + "entity-details-id": "Id", + "entity-details-title": "Titel", + "entity-details-country": "Land", + "entity-details-state": "Staat", + "entity-details-city": "Stad", + "entity-details-zip": "Postcode", + "entity-details-address": "Adres", + "entity-details-address2": "Adres 2", + "entity-details-additional_info": "Aanvullende info", + "entity-details-phone": "Telefoon", + "entity-details-email": "E-mail", + "email-sender": "E-mailsender", + "fields-to-check": "Velden om te controleren", + "add-detail": "Detail toevoegen", + "check-all-keys-tooltip": "Controleer of alle opgegeven velden aanwezig zijn in gegevens en metadata van het bericht.", + "fields-to-check-hint": "Druk op 'Enter' om veldnaam te voltooien. Meerdere namen ondersteund.", + "entity-details-list-empty": "Selecteer ten minste één detail.", + "alarm-status": "Alarmstatus", + "alarm-required": "Minstens één alarmstatus is vereist.", + "no-entity-details-matching": "Geen overeenkomende entiteitsdetails gevonden.", + "custom-table-name": "Aangepaste tabelnaam", + "custom-table-name-required": "Tabelnaam is verplicht", + "custom-table-hint": "Tabel moet bestaan in Cassandra-cluster en beginnen met 'cs_tb_'. Geef naam zonder prefix op.", + "message-field": "Berichtveld", + "message-field-required": "Berichtveld is verplicht.", + "table-col": "Tabelkolom", + "table-col-required": "Tabelkolom is verplicht.", + "latitude-field-name": "Breedtegraadveldnaam", + "longitude-field-name": "Lengtegraadveldnaam", + "latitude-field-name-required": "Breedtegraadveldnaam is verplicht.", + "longitude-field-name-required": "Lengtegraadveldnaam is verplicht.", + "fetch-perimeter-info-from-metadata": "Perimeterinformatie uit metadata ophalen", + "fetch-perimeter-info-from-metadata-tooltip": "Bij 'Polygon' wordt waarde direct gebruikt. Bij 'Circle' worden velden 'latitude', 'longitude', 'radius' en 'radiusUnit' verwacht.", + "perimeter-key-name": "Perimeter sleutelnaam", + "perimeter-key-name-hint": "Metadata veldnaam met perimeterinformatie.", + "perimeter-key-name-required": "Perimeter sleutelnaam is verplicht.", + "perimeter-circle": "Cirkel", + "perimeter-polygon": "Polygoon", + "perimeter-type": "Perimetertype", + "circle-center-latitude": "Middelpunt breedtegraad", + "circle-center-latitude-required": "Middelpunt breedtegraad is verplicht.", + "circle-center-longitude": "Middelpunt lengtegraad", + "circle-center-longitude-required": "Middelpunt lengtegraad is verplicht.", + "range-unit-meter": "Meter", + "range-unit-kilometer": "Kilometer", + "range-unit-foot": "Voet", + "range-unit-mile": "Mijl", + "range-unit-nautical-mile": "Zeemijl", + "range-units": "Afstandseenheden", + "range-units-required": "Afstandseenheden zijn verplicht.", + "range": "Bereik", + "range-required": "Bereik is verplicht.", + "polygon-definition": "Polygoondefinitie", + "polygon-definition-required": "Polygoondefinitie is verplicht.", + "polygon-definition-hint": "Gebruik formaat: [[lat1,lon1],[lat2,lon2],...,[latN,lonN]].", + "min-inside-duration": "Minimale binnentijdsduur", + "min-inside-duration-value-required": "Minimale binnentijdsduur is verplicht", + "min-inside-duration-time-unit": "Tijdseenheid minimale binnentijdsduur", + "min-outside-duration": "Minimale buitentijdsduur", + "min-outside-duration-value-required": "Minimale buitentijdsduur is verplicht", + "min-outside-duration-time-unit": "Tijdseenheid minimale buitentijdsduur", + "tell-failure-if-absent": "Meld mislukking", + "tell-failure-if-absent-hint": "Indien geselecteerd, genereert fout bij ontbrekende sleutelwaarden.", + "get-latest-value-with-ts": "Ophalen met tijdstempel", + "get-latest-value-with-ts-hint": "Geeft ook tijdstempel bij laatste telemetrie, bijv.: \"temp\": {\"ts\":1574329385897, \"value\":42}", + "ignore-null-strings": "Negeer lege strings", + "ignore-null-strings-hint": "Indien geselecteerd, worden lege waarden genegeerd.", + "add-metadata-key-values-as-kafka-headers": "Voeg metadata toe aan Kafka headers", + "add-metadata-key-values-as-kafka-headers-hint": "Metadata key-value paren worden als byte arrays met charset toegevoegd.", + "charset-encoding": "Tekenset codering", + "charset-encoding-required": "Tekenset codering is verplicht.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "De naam van de wachtrij kan uit de lijst gekozen worden of als aangepaste naam worden ingevoerd.", + "device-profile-node-hint": "Nuttig bij duur- of herhalingscondities om consistentie van alarmstatus te waarborgen.", + "persist-alarm-rules": "Alarmregelstatus opslaan", + "persist-alarm-rules-hint": "Indien ingeschakeld, slaat deze knooppunt de status van verwerking op in de database.", + "fetch-alarm-rules": "Haal alarmregelstatus op", + "fetch-alarm-rules-hint": "Herstelt status bij initialisatie, zodat alarmen na herstart blijven functioneren.", + "input-value-key": "Sleutel invoerwaarde", + "input-value-key-required": "Sleutel invoerwaarde is verplicht.", + "output-value-key": "Sleutel uitvoerwaarde", + "output-value-key-required": "Sleutel uitvoerwaarde is verplicht.", + "number-of-digits-after-floating-point": "Aantal decimalen na komma", + "number-of-digits-after-floating-point-range": "Moet tussen 0 en 15 liggen.", + "failure-if-delta-negative": "Foutmelding bij negatieve delta", + "failure-if-delta-negative-tooltip": "Veroorzaakt fout als delta negatief is.", + "use-caching": "Gebruik caching", + "use-caching-tooltip": "Cache waarde van \"{{inputValueKey}}\" voor betere prestaties.", + "add-time-difference-between-readings": "Voeg tijdsverschil toe tussen \"{{inputValueKey}}\" metingen", + "add-time-difference-between-readings-tooltip": "Voegt \"{{periodValueKey}}\" toe aan uitgaand bericht.", + "period-value-key": "Periode sleutel", + "period-value-key-required": "Periode sleutel is verplicht.", + "general-pattern-hint": "Gebruik ${metadataKey} voor metadata en $[messageKey] voor berichtgegevens.", + "alarm-severity-pattern-hint": "Gebruik ${metadataKey} voor de waarde uit de metadata, $[messageKey] voor de waarde uit de berichtinhoud. De alarmernst moet een systeemwaarde zijn (CRITICAL, MAJOR, enz.)", + "output-node-name-hint": "Naam van rule node is het relatietype dat berichten doorstuurt naar andere nodes.", + "use-server-ts": "Gebruik server tijdstempel", + "use-server-ts-hint": "Gebruik server tijdstempel bij ontbreken van expliciete tijd in gegevens.", + "kv-map-pattern-hint": "Alle invoervelden ondersteunen templates. Gebruik $[messageKey] of ${metadataKey}.", + "kv-map-single-pattern-hint": "Ondersteunt templates. Gebruik $[messageKey] of ${metadataKey}.", + "shared-scope": "Gedeeld bereik", + "server-scope": "Serverbereik", + "client-scope": "Clientbereik", + "attribute-type": "Attribuut", + "attribute-type-description": "Waarde uit database ophalen", + "attribute-type-result-description": "Resultaat opslaan als attribuut", + "constant-type": "Constante", + "constant-type-description": "Stel vaste waarde in", + "time-series-type": "Tijdreeks", + "time-series-type-description": "Laatste tijdreekswaarde ophalen", + "time-series-type-result-description": "Resultaat opslaan als tijdreeks", + "message-body-type": "Bericht", + "message-body-type-description": "Waarde uit bericht ophalen", + "message-body-type-result-description": "Resultaat toevoegen aan bericht", + "message-metadata-type": "Metadata", + "message-metadata-type-description": "Waarde uit metadata ophalen", + "message-metadata-result-description": "Resultaat toevoegen aan metadata", + "argument-tile": "Argumenten", + "no-arguments-prompt": "Geen argumenten geconfigureerd", + "result-title": "Resultaat", + "functions-field-input": "Functies", + "no-option-found": "Geen optie gevonden", + "argument-source-field-input": "Bron", + "argument-source-field-input-required": "Bron is verplicht.", + "argument-key-field-input": "Sleutel", + "argument-key-field-input-required": "Sleutel is verplicht.", + "constant-value-field-input": "Constante waarde", + "constant-value-field-input-required": "Constante waarde is verplicht.", + "attribute-scope-field-input": "Attribuutbereik", + "attribute-scope-field-input-required": "Attribuutbereik is verplicht.", + "default-value-field-input": "Standaardwaarde", + "type-field-input": "Type", + "type-field-input-required": "Type is verplicht.", + "key-field-input": "Sleutel", + "add-entity-type": "Voeg entiteitstype toe", + "add-device-profile": "Voeg apparaatprofiel toe", + "key-field-input-required": "Sleutel is verplicht.", + "number-floating-point-field-input": "Aantal decimalen", + "number-floating-point-field-input-hint": "Gebruik 0 voor gehele getallen", + "add-to-message-field-input": "Voeg toe aan bericht", + "add-to-metadata-field-input": "Voeg toe aan metadata", + "custom-expression-field-input": "Wiskundige uitdrukking", + "custom-expression-field-input-required": "Wiskundige uitdrukking is verplicht", + "custom-expression-field-input-hint": "Bijv. om Fahrenheit naar Celsius om te rekenen", + "retained-message": "Behouden bericht", + "attributes-mapping": "Attribuuttoewijzing", + "latest-telemetry-mapping": "Laatste telemetrie-toewijzing", + "add-mapped-attribute-to": "Voeg toegewezen attributen toe aan", + "add-mapped-latest-telemetry-to": "Voeg toegewezen telemetrie toe aan", + "add-mapped-fields-to": "Voeg toegewezen velden toe aan", + "add-selected-details-to": "Voeg geselecteerde details toe aan", + "clear-selected-types": "Geselecteerde types wissen", + "clear-selected-details": "Geselecteerde details wissen", + "clear-selected-fields": "Geselecteerde velden wissen", + "clear-selected-keys": "Geselecteerde sleutels wissen", + "geofence-configuration": "Geofence-configuratie", + "coordinate-field-names": "Coördinaat veldnamen", + "coordinate-field-hint": "Node probeert deze velden uit het bericht te halen. Indien niet aanwezig, wordt metadata gebruikt.", + "presence-monitoring-strategy": "Aanwezigheidsbewakingsstrategie", + "presence-monitoring-strategy-on-first-message": "Bij eerste bericht", + "presence-monitoring-strategy-on-each-message": "Bij elk bericht", + "presence-monitoring-strategy-on-first-message-hint": "Meldt status 'Binnen' of 'Buiten' bij eerste bericht na ingestelde minimale duur sinds laatste statuswijziging.", + "presence-monitoring-strategy-on-each-message-hint": "Meldt status 'Binnen' of 'Buiten' bij elk bericht na status 'Binnengekomen' of 'Verlaten'.", + "fetch-credentials-to": "Haal inloggegevens op naar", + "add-originator-attributes-to": "Voeg attributen van bron toe aan", + "originator-attributes": "Bronattributen", + "fetch-latest-telemetry-with-timestamp": "Haal laatste telemetrie op met tijdstempel", + "fetch-latest-telemetry-with-timestamp-tooltip": "Voegt tijdstempel toe aan metadata bij laatste telemetrie, bijv: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure": "Melding van mislukking indien attributen ontbreken", + "tell-failure-tooltip": "Indien ten minste één geselecteerde sleutel ontbreekt, wordt bericht als 'Mislukt' gemarkeerd.", + "created-time": "Aangemaakt op", + "chip-help": "Druk op 'Enter' om {{inputName}} in te voeren. Backspace wist invoer. Meerdere waarden ondersteund.", + "detail": "detail", + "field-name": "veldnaam", + "device-profile": "apparaatprofiel", + "entity-type": "entiteitstype", + "message-type": "berichttype", + "timeseries-key": "tijdreeks-sleutel", + "type": "Type", + "first-name": "Voornaam", + "last-name": "Achternaam", + "label": "Label", + "originator-fields-mapping": "Bronveld-mapping", + "add-mapped-originator-fields-to": "Voeg gemapte bronvelden toe aan", + "fields": "Velden", + "skip-empty-fields": "Lege velden overslaan", + "skip-empty-fields-tooltip": "Velden zonder waarde worden niet toegevoegd aan bericht of metadata.", + "fetch-interval": "Ophaalinterval", + "fetch-strategy": "Ophaalstrategie", + "fetch-timeseries-from-to": "Ophaal tijdreeks van {{startInterval}} {{startIntervalTimeUnit}} geleden tot {{endInterval}} {{endIntervalTimeUnit}} geleden.", + "fetch-timeseries-from-to-invalid": "\"Interval start\" moet kleiner zijn dan \"Interval einde\".", + "use-metadata-dynamic-interval-tooltip": "Gebruikt dynamisch interval op basis van bericht/metadata.", + "all-mode-hint": "Met 'All' worden alle tijdreeksen binnen het interval opgehaald.", + "first-mode-hint": "'First' haalt dichtstbijzijnde tijdreeks op bij start van interval.", + "last-mode-hint": "'Last' haalt dichtstbijzijnde tijdreeks op bij einde van interval.", + "ascending": "Oplopend", + "descending": "Aflopend", + "min": "Min", + "max": "Max", + "average": "Gemiddelde", + "sum": "Som", + "count": "Aantal", + "none": "Geen", + "last-level-relation-tooltip": "Zoekt alleen gerelateerde entiteiten op laatste niveau.", + "last-level-device-relation-tooltip": "Zoekt alleen gerelateerde apparaten op laatste niveau.", + "data-to-fetch": "Gegevens op te halen", + "mapping-of-customers": "Mapping van klant", + "map-fields-required": "Alle mapping-velden zijn verplicht.", + "attributes": "Attributen", + "related-device-attributes": "Gerelateerde apparaat attributen", + "add-selected-attributes-to": "Voeg geselecteerde attributen toe aan", + "device-profiles": "Apparaatprofielen", + "mapping-of-tenant": "Mapping van huurder", + "add-attribute-key": "Attribuutsleutel toevoegen", + "message-template": "Berichtsjabloon", + "message-template-required": "Berichtsjabloon is verplicht", + "use-system-slack-settings": "Gebruik systeem Slack-instellingen", + "slack-api-token": "Slack API-token", + "slack-api-token-required": "Slack API-token is verplicht", + "keys-mapping": "Sleutelmapping", + "add-key": "Sleutel toevoegen", + "recipients": "Ontvangers", + "message-subject-and-content": "Onderwerp en inhoud van bericht", + "template-rules-hint": "Gebruik $[messageKey] of ${metadataKey} om waarden te extraheren.", + "originator-customer-desc": "Gebruik klant van oorspronkelijke bron als nieuwe bron.", + "originator-tenant-desc": "Gebruik huidige huurder als nieuwe bron.", + "originator-related-entity-desc": "Gebruik gerelateerde entiteit als nieuwe bron.", + "originator-alarm-originator-desc": "Gebruik alarmbron als nieuwe bron.", + "originator-entity-by-name-pattern-desc": "Zoek entiteit op naam en gebruik als nieuwe bron.", + "email-from-template-hint": "Gebruik $[messageKey] of ${metadataKey} om waarden op te halen.", + "recipients-block-main-hint": "Komma-gescheiden lijst, ondersteunt templates.", + "forward-msg-default-rule-chain": "Stuur bericht naar standaard regelketen van bron", + "forward-msg-default-rule-chain-tooltip": "Gebruikt standaard of geconfigureerde regelketen van bron.", + "exclude-zero-deltas": "Negeer nulverschillen", + "exclude-zero-deltas-hint": "Voegt sleutel toe aan bericht indien waarde niet nul is.", + "exclude-zero-deltas-time-difference-hint": "Voegt alleen toe als waarde niet nul is.", + "search-direction-from": "Van bron naar doelentiteit", + "search-direction-to": "Van doelentiteit naar bron", + "del-relation-direction-from": "Van bron", + "del-relation-direction-to": "Naar bron", + "target-entity": "Doelentiteit", + "function-configuration": "Functieconfiguratie", + "function-name": "Functienaam", + "function-name-required": "Functienaam is verplicht.", + "qualifier": "Kwalificatie", + "qualifier-hint": "Als niet opgegeven, wordt de standaardkwalificatie \"$LATEST\" gebruikt.", + "aws-credentials": "AWS-referenties", + "connection-timeout": "Verbindingstime-out", + "connection-timeout-required": "Verbindingstime-out is verplicht.", + "connection-timeout-min": "Minimale verbindingstime-out is 0.", + "connection-timeout-hint": "Wachttijd in seconden voor het tot stand brengen van een verbinding. 0 betekent oneindig (niet aanbevolen).", + "request-timeout": "Verzoek-time-out", + "request-timeout-required": "Verzoek-time-out is verplicht.", + "request-timeout-min": "Minimale verzoek-time-out is 0", + "request-timeout-hint": "Wachttijd in seconden voor voltooiing van het verzoek. 0 betekent oneindig (niet aanbevolen).", + "units": "Eenheden", + "tell-failure-aws-lambda": "Mislukking melden bij uitzondering in AWS Lambda-uitvoering", + "tell-failure-aws-lambda-hint": "Forceert verwerkingsmislukking als de AWS Lambda-functie een uitzondering geeft.", + "basic-mode": "Basis", + "advanced-mode": "Geavanceerd", + "save-time-series": { + "processing-settings": "Verwerkingsinstellingen", + "processing-settings-hint": "Definieer hoe inkomende berichten worden verwerkt.", + "advanced-settings-hint": "Let op met het configureren van strategieën. Onverwacht gedrag mogelijk.", + "strategy": "Strategie", + "deduplication-interval": "Deduplicatie-interval", + "deduplication-interval-required": "Deduplicatie-interval is verplicht", + "deduplication-interval-min-max-range": "Minimaal 1 seconde en maximaal 1 dag", + "strategy-type": { + "every-message": "Bij elk bericht", + "skip": "Overslaan", + "deduplicate": "Dedupliceren", + "web-sockets-only": "Alleen WebSockets" + }, + "time-series": "Tijdreeksen", + "latest": "Laatste waarden", + "web-sockets": "WebSockets", + "calculated-fields": "Berekende velden" + }, + "save-attribute": { + "processing-settings": "Verwerkingsinstellingen", + "processing-settings-hint": "Definieer hoe inkomende berichten worden verwerkt.", + "advanced-settings-hint": "Let op met het configureren van strategieën. Onverwacht gedrag mogelijk.", + "strategy": "Strategie", + "deduplication-interval": "Deduplicatie-interval", + "deduplication-interval-required": "Deduplicatie-interval is verplicht", + "deduplication-interval-min-max-range": "Minimaal 1 seconde en maximaal 1 dag", + "scope": "Bereik", + "strategy-type": { + "every-message": "Bij elk bericht", + "skip": "Overslaan", + "deduplicate": "Dedupliceren", + "web-sockets-only": "Alleen WebSockets" + }, + "attributes": "Attributen" + }, + "key-val": { + "key": "Sleutel", + "value": "Waarde", + "see-examples": "Zie voorbeelden.", + "remove-entry": "Verwijder invoer", + "remove-mapping-entry": "Verwijder mapping", + "add-mapping-entry": "Voeg mapping toe", + "add-entry": "Voeg invoer toe", + "copy-key-values-from": "Kopieer sleutel-waarden van", + "delete-key-values": "Verwijder sleutel-waarden", + "delete-key-values-from": "Verwijder sleutel-waarden van", + "at-least-one-key-error": "Minstens één sleutel moet geselecteerd zijn.", + "unique-key-value-pair-error": "'{{keyText}}' moet verschillend zijn van '{{valText}}'!" + }, + "mail-body-types": { + "plain-text": "Platte tekst", + "html": "HTML", + "dynamic": "Dynamisch", + "use-body-type-template": "Gebruik body-type sjabloon", + "plain-text-description": "Eenvoudige tekst zonder opmaak.", + "html-text-description": "Gebruik HTML-tags voor opmaak, links en afbeeldingen.", + "dynamic-text-description": "Gebruik dynamisch platte tekst of HTML via sjabloon.", + "after-template-evaluation-hint": "Na sjabloonevaluatie: true voor HTML, false voor platte tekst." + } + }, + "timezone": { + "timezone": "Tijdzone", + "select-timezone": "Selecteer tijdzone", + "no-timezones-matching": "Geen tijdzones gevonden die overeenkomen met '{{timezone}}'.", + "timezone-required": "Tijdzone is verplicht.", + "browser-time": "Browsertijd" + }, + "queue": { + "queue-name": "Wachtrij", + "no-queues-found": "Geen wachtrijen gevonden.", + "no-queues-matching": "Geen wachtrijen gevonden die overeenkomen met '{{queue}}'.", + "select-name": "Selecteer wachtrijnaam", + "name": "Naam", + "name-required": "Wachtrijnaam is verplicht!", + "name-unique": "Wachtrijnaam is niet uniek!", + "name-pattern": "Wachtrijnaam bevat een ongeldig teken. Alleen ASCII-alfanumeriek, '.', '_' en '-' zijn toegestaan!", + "queue-required": "Wachtrij is verplicht!", + "topic-required": "Onderwerp van wachtrij is verplicht!", + "poll-interval-required": "Poll-interval is verplicht!", + "poll-interval-min-value": "Poll-interval mag niet kleiner zijn dan 1", + "partitions-required": "Partities is verplicht!", + "partitions-min-value": "Aantal partities mag niet kleiner zijn dan 1", + "pack-processing-timeout-required": "Verwerkingstijdslimiet is verplicht", + "pack-processing-timeout-min-value": "Verwerkingstijdslimiet mag niet kleiner zijn dan 1", + "batch-size-required": "Batchgrootte is verplicht!", + "batch-size-min-value": "Batchgrootte mag niet kleiner zijn dan 1", + "retries-required": "Aantal pogingen is verplicht!", + "retries-min-value": "Aantal pogingen mag niet negatief zijn", + "failure-percentage-required": "Faalpercentage is verplicht!", + "failure-percentage-min-value": "Faalpercentage mag niet kleiner zijn dan 0", + "failure-percentage-max-value": "Faalpercentage mag niet groter zijn dan 100", + "pause-between-retries-required": "Pauze tussen pogingen is verplicht!", + "pause-between-retries-min-value": "Pauze tussen pogingen mag niet kleiner zijn dan 1", + "max-pause-between-retries-required": "Maximale pauze tussen pogingen is verplicht!", + "max-pause-between-retries-min-value": "Maximale pauze mag niet kleiner zijn dan 1", + "submit-strategy-type-required": "Indientypstrategie is verplicht!", + "processing-strategy-type-required": "Verwerkingstype is verplicht!", + "queues": "Wachtrijen", + "selected-queues": "{ count, plural, =1 {1 wachtrij} other {# wachtrijen} } geselecteerd", + "delete-queue-title": "Weet je zeker dat je de wachtrij '{{queueName}}' wilt verwijderen?", + "delete-queues-title": "Weet je zeker dat je { count, plural, =1 {1 wachtrij} other {# wachtrijen} } wilt verwijderen?", + "delete-queue-text": "Let op: na bevestiging wordt de wachtrij en alle gerelateerde gegevens permanent verwijderd.", + "delete-queues-text": "Na bevestiging worden alle geselecteerde wachtrijen verwijderd en zijn ze niet meer toegankelijk.", + "search": "Zoek wachtrij", + "add": "Wachtrij toevoegen", + "details": "Wachtrijdetails", + "topic": "Onderwerp", + "submit-settings": "Indieninstellingen", + "submit-strategy": "Strategietype *", + "grouping-parameter": "Groepeerparameter", + "processing-settings": "Verwerkingsinstellingen voor pogingen", + "processing-strategy": "Verwerkingstype *", + "retries-settings": "Instellingen voor pogingen", + "polling-settings": "Pollinginstellingen", + "batch-processing": "Batchverwerking", + "poll-interval": "Poll-interval", + "partitions": "Partities", + "immediate-processing": "Directe verwerking", + "consumer-per-partition": "Poll-bericht per consument", + "consumer-per-partition-hint": "Schakel aparte consument(en) in voor elke partitie", + "duplicate-msg-to-all-partitions": "Dupliceer bericht naar alle partities", + "processing-timeout": "Verwerking binnen, ms", + "batch-size": "Batchgrootte", + "retries": "Aantal pogingen (0 – onbeperkt)", + "failure-percentage": "Percentage fouten om pogingen over te slaan, %", + "pause-between-retries": "Poging opnieuw binnen, sec", + "max-pause-between-retries": "Extra poging binnen, sec", + "delete": "Wachtrij verwijderen", + "copyId": "Kopieer wachtrij-ID", + "idCopiedMessage": "Wachtrij-ID is gekopieerd naar het klembord", + "description": "Beschrijving", + "description-hint": "Deze tekst wordt weergegeven in de wachtrijbeschrijving in plaats van de geselecteerde strategie", + "alt-description": "Indienstrategie: {{submitStrategy}}, Verwerkingsstrategie: {{processingStrategy}}", + "custom-properties": "Aangepaste eigenschappen", + "custom-properties-hint": "Aangepaste eigenschappen voor het maken van wachtrijen (onderwerp), bijv. 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies": { + "sequential-by-originator-label": "Sequentieel per verzender", + "sequential-by-originator-hint": "Nieuw bericht voor bijv. apparaat A wordt niet ingediend totdat het vorige bericht voor apparaat A is bevestigd", + "sequential-by-tenant-label": "Sequentieel per tenant", + "sequential-by-tenant-hint": "Nieuw bericht voor bijv. tenant A wordt niet ingediend totdat het vorige bericht voor tenant A is bevestigd", + "sequential-label": "Sequentieel", + "sequential-hint": "Nieuw bericht wordt niet ingediend totdat het vorige bericht is bevestigd", + "burst-label": "Burst", + "burst-hint": "Alle berichten worden in de volgorde van binnenkomst naar de regels doorgestuurd", + "batch-label": "Batch", + "batch-hint": "Nieuwe batch wordt niet ingediend totdat vorige batch is bevestigd", + "skip-all-failures-label": "Sla alle fouten over", + "skip-all-failures-hint": "Negeer alle fouten", + "skip-all-failures-and-timeouts-label": "Sla alle fouten en time-outs over", + "skip-all-failures-and-timeouts-hint": "Negeer alle fouten en time-outs", + "retry-all-label": "Alles opnieuw proberen", + "retry-all-hint": "Probeer alle berichten uit het verwerkingspakket opnieuw", + "retry-failed-label": "Mislukte opnieuw proberen", + "retry-failed-hint": "Probeer alle mislukte berichten uit het verwerkingspakket opnieuw", + "retry-timeout-label": "Time-outs opnieuw proberen", + "retry-timeout-hint": "Probeer alle time-out berichten uit het verwerkingspakket opnieuw", + "retry-failed-and-timeout-label": "Mislukte en time-outs opnieuw proberen", + "retry-failed-and-timeout-hint": "Probeer alle mislukte en time-out berichten uit het verwerkingspakket opnieuw" + } + }, + "queue-statistics": { + "queue-statistics": "Wachtrijstatistieken", + "no-queue-statistics-matching": "Geen wachtrijstatistieken gevonden die overeenkomen met '{{entity}}'.", + "queue-statistics-required": "Wachtrijstatistieken zijn vereist.", + "list-of-queue-statistics": "{ count, plural, =1 {Eén wachtrijstatistiek} other {Lijst van # wachtrijstatistieken} }", + "selected-queue-statistics": "{ count, plural, =1 {1 wachtrijstatistiek} other {# wachtrijstatistieken} } geselecteerd", + "no-queue-statistics-text": "Geen wachtrijstatistieken gevonden", + "queue-statistics-starts-with": "Wachtrijstatistieken die beginnen met '{{prefix}}'" + }, + "server-error": { + "general": "Algemene serverfout", + "authentication": "Authenticatiefout", + "jwt-token-expired": "JWT-token is verlopen", + "tenant-trial-expired": "Proefperiode van tenant is verlopen", + "credentials-expired": "Inloggegevens verlopen", + "permission-denied": "Toegang geweigerd", + "invalid-arguments": "Ongeldige argumenten", + "bad-request-params": "Ongeldige aanvraagparameters", + "item-not-found": "Item niet gevonden", + "too-many-requests": "Te veel aanvragen", + "too-many-updates": "Te veel updates" + }, + "tenant": { + "tenant": "Tenant", + "tenants": "Tenants", + "management": "Tenantbeheer", + "add": "Tenant toevoegen", + "admins": "Beheerders", + "manage-tenant-admins": "Beheer tenantbeheerders", + "delete": "Tenant verwijderen", + "add-tenant-text": "Nieuwe tenant toevoegen", + "no-tenants-text": "Geen tenants gevonden", + "tenant-details": "Tenantgegevens", + "title-max-length": "Titel moet minder dan 256 tekens zijn", + "delete-tenant-title": "Weet je zeker dat je de tenant '{{tenantTitle}}' wilt verwijderen?", + "delete-tenant-text": "Wees voorzichtig, na bevestiging wordt de tenant en alle gerelateerde gegevens permanent verwijderd.", + "delete-tenants-title": "Weet je zeker dat je { count, plural, =1 {1 tenant} other {# tenants} } wilt verwijderen?", + "delete-tenants-action-title": "Verwijder { count, plural, =1 {1 tenant} other {# tenants} }", + "delete-tenants-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde tenants en hun gegevens permanent verwijderd.", + "title": "Titel", + "title-required": "Titel is verplicht.", + "description": "Beschrijving", + "details": "Details", + "events": "Gebeurtenissen", + "copyId": "Kopieer tenant-ID", + "idCopiedMessage": "Tenant-ID is gekopieerd naar het klembord", + "select-tenant": "Selecteer tenant", + "no-tenants-matching": "Geen tenants gevonden die overeenkomen met '{{entity}}'.", + "tenant-required": "Tenant is verplicht", + "search": "Zoek tenants", + "selected-tenants": "{ count, plural, =1 {1 tenant} other {# tenants} } geselecteerd", + "isolated-tb-rule-engine": "Gebruik geïsoleerde ThingsBoard Rule Engine-wachtrijen", + "isolated-tb-rule-engine-details": "Elke tenant krijgt toegewezen Rule Engine-wachtrijen" + }, + "tenant-profile": { + "tenant-profile": "Tenantprofiel", + "tenant-profiles": "Tenantprofielen", + "add": "Tenantprofiel toevoegen", + "add-profile": "Profiel toevoegen", + "debug": "Debuggen", + "edit": "Tenantprofiel bewerken", + "tenant-profile-details": "Details van tenantprofiel", + "no-tenant-profiles-text": "Geen tenantprofielen gevonden", + "name-max-length": "Naam moet minder dan 256 tekens zijn", + "search": "Zoek tenantprofielen", + "selected-tenant-profiles": "{ count, plural, =1 {1 tenantprofiel} other {# tenantprofielen} } geselecteerd", + "no-tenant-profiles-matching": "Geen tenantprofiel gevonden die overeenkomt met '{{entity}}'.", + "tenant-profile-required": "Tenantprofiel is verplicht", + "idCopiedMessage": "Tenantprofiel-ID is gekopieerd naar klembord", + "set-default": "Maak tenantprofiel standaard", + "delete": "Tenantprofiel verwijderen", + "copyId": "Kopieer tenantprofiel-ID", + "name": "Naam", + "name-required": "Naam is verplicht.", + "data": "Profielgegevens", + "profile-configuration": "Profielconfiguratie", + "description": "Beschrijving", + "default": "Standaard", + "delete-tenant-profile-title": "Weet je zeker dat je het tenantprofiel '{{tenantProfileName}}' wilt verwijderen?", + "delete-tenant-profile-text": "Wees voorzichtig, na bevestiging worden het tenantprofiel en alle bijbehorende gegevens permanent verwijderd.", + "delete-tenant-profiles-title": "Weet je zeker dat je { count, plural, =1 {1 tenantprofiel} other {# tenantprofielen} } wilt verwijderen?", + "delete-tenant-profiles-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde tenantprofielen en hun gegevens permanent verwijderd.", + "set-default-tenant-profile-title": "Weet je zeker dat je het tenantprofiel '{{tenantProfileName}}' als standaard wilt instellen?", + "set-default-tenant-profile-text": "Na bevestiging wordt het tenantprofiel als standaard gemarkeerd en gebruikt voor nieuwe tenants zonder opgegeven profiel.", + "no-tenant-profiles-found": "Geen tenantprofielen gevonden.", + "create-new-tenant-profile": "Maak een nieuw profiel aan!", + "create-tenant-profile": "Nieuw tenantprofiel aanmaken", + "import": "Tenantprofiel importeren", + "export": "Tenantprofiel exporteren", + "export-failed-error": "Tenantprofiel kan niet worden geëxporteerd: {{error}}", + "tenant-profile-file": "Tenantprofielbestand", + "invalid-tenant-profile-file-error": "Kan tenantprofiel niet importeren: Ongeldige datastructuur van tenantprofiel.", + "advanced-settings": "Geavanceerde instellingen", + "entities": "Entiteiten", + "rule-engine": "Regelengine", + "time-to-live": "Levensduur (TTL)", + "calculated-fields": "Berekende velden", + "alarms-and-notifications": "Alarmen en meldingen", + "ota-files-in-bytes": "Bestanden", + "ws-title": "WS", + "unlimited": "(0 - onbeperkt)", + "maximum-devices": "Maximaal aantal apparaten", + "maximum-devices-required": "Maximaal aantal apparaten is verplicht.", + "maximum-devices-range": "Maximaal aantal apparaten mag niet negatief zijn", + "maximum-assets": "Maximaal aantal assets", + "maximum-assets-required": "Maximaal aantal assets is verplicht.", + "maximum-assets-range": "Maximaal aantal assets mag niet negatief zijn", + "maximum-customers": "Maximaal aantal klanten", + "maximum-customers-required": "Maximaal aantal klanten is verplicht.", + "maximum-customers-range": "Maximaal aantal klanten mag niet negatief zijn", + "maximum-users": "Maximaal aantal gebruikers", + "maximum-users-required": "Maximaal aantal gebruikers is verplicht.", + "maximum-users-range": "Maximaal aantal gebruikers mag niet negatief zijn", + "maximum-dashboards": "Maximaal aantal dashboards", + "maximum-dashboards-required": "Maximaal aantal dashboards is verplicht.", + "maximum-dashboards-range": "Maximaal aantal dashboards mag niet negatief zijn", + "maximum-edges": "Maximaal aantal edges", + "maximum-edges-required": "Maximaal aantal edges is verplicht.", + "maximum-edges-range": "Maximaal aantal edges mag niet negatief zijn", + "maximum-rule-chains": "Maximaal aantal regelketens", + "maximum-rule-chains-required": "Maximaal aantal regelketens is verplicht.", + "maximum-rule-chains-range": "Maximaal aantal regelketens mag niet negatief zijn", + "maximum-resources-sum-data-size": "Maximale totale grootte van resourcebestanden (bytes)", + "maximum-resources-sum-data-size-required": "Maximale totale grootte van resourcebestanden is verplicht.", + "maximum-resources-sum-data-size-range": "Maximale totale grootte van resourcebestanden mag niet negatief zijn", + "maximum-resource-size": "Maximale grootte van resourcebestand (bytes)", + "maximum-resource-size-required": "Maximale grootte van resourcebestand is verplicht", + "maximum-resource-size-range": "Maximale grootte van resourcebestand mag niet negatief zijn", + "maximum-ota-packages-sum-data-size": "Maximale totale grootte van OTA-pakketbestanden (bytes)", + "maximum-ota-package-sum-data-size-required": "Maximale totale grootte van OTA-pakketbestanden is verplicht.", + "maximum-ota-package-sum-data-size-range": "Maximale totale grootte van OTA-pakketbestanden mag niet negatief zijn", + "maximum-debug-duration-min": "Maximale debugduur (minuten)", + "maximum-debug-duration-min-range": "Maximale debugduur mag niet negatief zijn", + "rest-requests-for-tenant": "REST-verzoeken voor tenant", + "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetrieberichten", + "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetriegegevenspunten", + "transport-device-msg-rate-limit": "Transport apparaatberichten", + "transport-device-telemetry-msg-rate-limit": "Transport apparaat telemetrieberichten", + "transport-device-telemetry-data-points-rate-limit": "Transport apparaat telemetriegegevenspunten", + "transport-gateway-msg-rate-limit": "Transport gatewayberichten", + "transport-gateway-telemetry-msg-rate-limit": "Transport gateway telemetrieberichten", + "transport-gateway-telemetry-data-points-rate-limit": "Transport gateway telemetriegegevenspunten", + "transport-gateway-device-msg-rate-limit": "Transport gateway-apparaatberichten", + "transport-gateway-device-telemetry-msg-rate-limit": "Transport gateway-apparaat telemetrieberichten", + "transport-gateway-device-telemetry-data-points-rate-limit": "Transport gateway-apparaat telemetriegegevenspunten", + "tenant-entity-export-rate-limit": "Entiteitsversie maken", + "tenant-entity-import-rate-limit": "Entiteitsversie laden", + "tenant-notification-request-rate-limit": "Meldingsverzoeken", + "tenant-notification-requests-per-rule-rate-limit": "Meldingsverzoeken per meldingsregel", + "max-calculated-fields": "Maximaal aantal berekende velden per entiteit", + "max-calculated-fields-range": "Maximaal aantal berekende velden mag niet negatief zijn", + "max-calculated-fields-required": "Maximaal aantal berekende velden is verplicht", + "max-data-points-per-rolling-arg": "Maximaal aantal gegevenspunten in rollende argumenten", + "max-data-points-per-rolling-arg-range": "Maximaal aantal gegevenspunten in rollende argumenten mag niet negatief zijn", + "max-data-points-per-rolling-arg-required": "Maximaal aantal gegevenspunten in rollende argumenten is verplicht", + "max-arguments-per-cf": "Maximaal aantal argumenten per berekend veld", + "max-arguments-per-cf-range": "Maximaal aantal argumenten mag niet negatief zijn", + "max-arguments-per-cf-required": "Maximaal aantal argumenten is verplicht", + "max-state-size": "Maximale staatsgrootte in KB", + "max-state-size-range": "Staatsgrootte mag niet negatief zijn", + "max-state-size-required": "Staatsgrootte is verplicht", + "max-value-argument-size": "Maximale grootte van enkel waardeargument in KB", + "max-value-argument-size-range": "Maximale grootte mag niet negatief zijn", + "max-value-argument-size-required": "Maximale grootte is verplicht", + "max-transport-messages": "Maximaal aantal transportberichten", + "max-transport-messages-required": "Maximaal aantal transportberichten is verplicht.", + "max-transport-messages-range": "Maximaal aantal transportberichten mag niet negatief zijn", + "max-transport-data-points": "Maximaal aantal transportgegevenspunten", + "max-transport-data-points-required": "Maximaal aantal transportgegevenspunten is verplicht.", + "max-transport-data-points-range": "Maximaal aantal transportgegevenspunten mag niet negatief zijn", + "max-r-e-executions": "Maximaal aantal Rule Engine-uitvoeringen", + "max-r-e-executions-required": "Maximaal aantal Rule Engine-uitvoeringen is verplicht.", + "max-r-e-executions-range": "Maximaal aantal Rule Engine-uitvoeringen mag niet negatief zijn", + "max-j-s-executions": "Maximaal aantal JavaScript-uitvoeringen", + "max-j-s-executions-required": "Maximaal aantal JavaScript-uitvoeringen is verplicht.", + "max-j-s-executions-range": "Maximaal aantal JavaScript-uitvoeringen mag niet negatief zijn", + "max-tbel-executions": "Maximaal aantal TBEL-uitvoeringen", + "max-tbel-executions-required": "Maximaal aantal TBEL-uitvoeringen is verplicht.", + "max-tbel-executions-range": "Maximaal aantal TBEL-uitvoeringen mag niet negatief zijn", + "max-d-p-storage-days": "Maximaal aantal dagen voor opslag van gegevenspunten", + "max-d-p-storage-days-required": "Maximaal aantal opslagdagen is verplicht.", + "max-d-p-storage-days-range": "Opslagdagen mogen niet negatief zijn", + "default-storage-ttl-days": "Standaard opslag-TTL in dagen", + "default-storage-ttl-days-required": "Standaard opslag-TTL is verplicht.", + "default-storage-ttl-days-range": "Opslag-TTL mag niet negatief zijn", + "alarms-ttl-days": "Alarmen TTL in dagen", + "alarms-ttl-days-required": "Alarmen TTL is verplicht", + "alarms-ttl-days-days-range": "Alarmen TTL mag niet negatief zijn", + "rpc-ttl-days": "RPC TTL in dagen", + "rpc-ttl-days-required": "RPC TTL is verplicht", + "rpc-ttl-days-days-range": "RPC TTL mag niet negatief zijn", + "queue-stats-ttl-days": "Queue-statistieken TTL in dagen", + "queue-stats-ttl-days-required": "Queue-statistieken TTL is verplicht", + "queue-stats-ttl-days-range": "Queue-statistieken TTL mag niet negatief zijn", + "rule-engine-exceptions-ttl-days": "Rule Engine-uitzonderingen TTL in dagen", + "rule-engine-exceptions-ttl-days-required": "TTL van Rule Engine-uitzonderingen is verplicht", + "rule-engine-exceptions-ttl-days-range": "TTL van Rule Engine-uitzonderingen mag niet negatief zijn", + "max-rule-node-executions-per-message": "Maximaal aantal rule node-uitvoeringen per bericht", + "max-rule-node-executions-per-message-required": "Maximaal aantal uitvoeringen per bericht is verplicht.", + "max-rule-node-executions-per-message-range": "Maximaal aantal uitvoeringen per bericht mag niet negatief zijn", + "max-emails": "Maximaal aantal verzonden e-mails", + "max-emails-required": "Maximaal aantal verzonden e-mails is verplicht.", + "max-emails-range": "Maximaal aantal e-mails mag niet negatief zijn", + "sms-enabled": "SMS ingeschakeld", + "max-sms": "Maximaal aantal verzonden SMS", + "max-sms-required": "Maximaal aantal verzonden SMS is verplicht.", + "max-sms-range": "Maximaal aantal SMS mag niet negatief zijn", + "max-created-alarms": "Maximaal aantal aangemaakte alarmen", + "max-created-alarms-required": "Maximaal aantal aangemaakte alarmen is verplicht.", + "max-created-alarms-range": "Maximaal aantal alarmen mag niet negatief zijn", + "no-queue": "Geen queue geconfigureerd", + "add-queue": "Queue toevoegen", + "queues-with-count": "Queues ({{count}})", + "tenant-rest-limits": "REST-verzoeken voor tenant", + "customer-rest-limits": "REST-verzoeken voor klant", + "incorrect-pattern-for-rate-limits": "Het formaat is komma-gescheiden paren van capaciteit en periode (in seconden), bijv. 100:1,2000:60", + "too-small-value-zero": "De waarde moet groter zijn dan 0", + "too-small-value-one": "De waarde moet groter zijn dan 1", + "queue-size-is-limited-by-system-configuration": "De grootte van de wachtrij is ook beperkt door de systeemconfiguratie.", + "cassandra-tenant-limits-configuration": "Cassandra-query voor tenant", + "ws-limit-max-sessions-per-tenant": "Maximaal aantal sessies per tenant", + "ws-limit-max-sessions-per-customer": "Maximaal aantal sessies per klant", + "ws-limit-max-sessions-per-regular-user": "Maximaal aantal sessies per reguliere gebruiker", + "ws-limit-max-sessions-per-public-user": "Maximaal aantal sessies per publieke gebruiker", + "ws-limit-queue-per-session": "Maximale wachtrijgrootte per sessie", + "ws-limit-max-subscriptions-per-tenant": "Maximaal aantal abonnementen per tenant", + "ws-limit-max-subscriptions-per-customer": "Maximaal aantal abonnementen per klant", + "ws-limit-max-subscriptions-per-regular-user": "Maximaal aantal abonnementen per reguliere gebruiker", + "ws-limit-max-subscriptions-per-public-user": "Maximaal aantal abonnementen per publieke gebruiker", + "ws-limit-updates-per-session": "WS-updates per sessie", + "rate-limits": { + "add-limit": "Limiet toevoegen", + "advanced-settings": "Geavanceerde instellingen", + "edit-limit": "Limiet bewerken", + "but-less-than": "maar minder dan", + "calculated-field-debug-event-rate-limit": "Debuggebeurtenissen van berekende velden", + "edit-calculated-field-debug-event-rate-limit": "Bewerk rate-limieten voor debuggebeurtenissen van berekende velden", + "edit-transport-tenant-msg-title": "Bewerk rate-limieten voor tenanttransportberichten", + "edit-transport-tenant-telemetry-msg-title": "Bewerk rate-limieten voor tenanttelemetrieberichten", + "edit-transport-tenant-telemetry-data-points-title": "Bewerk rate-limieten voor tenant telemetriegegevenspunten", + "edit-transport-device-msg-title": "Bewerk rate-limieten voor apparaatberichten", + "edit-transport-device-telemetry-msg-title": "Bewerk rate-limieten voor apparaattelmetrieberichten", + "edit-transport-device-telemetry-data-points-title": "Bewerk rate-limieten voor apparaattelmetriegegevenspunten", + "edit-transport-gateway-msg-title": "Bewerk rate-limieten voor gatewayberichten", + "edit-transport-gateway-telemetry-msg-title": "Bewerk rate-limieten voor gatewaytelemetrieberichten", + "edit-transport-gateway-telemetry-data-points-title": "Bewerk rate-limieten voor gatewaytelemetriegegevenspunten", + "edit-transport-gateway-device-msg-title": "Bewerk rate-limieten voor gatewayapparaatberichten", + "edit-transport-gateway-device-telemetry-msg-title": "Bewerk rate-limieten voor gatewayapparaattelemetrieberichten", + "edit-transport-gateway-device-telemetry-data-points-title": "Bewerk rate-limieten voor gatewayapparaattelemetriegegevenspunten", + "edit-tenant-rest-limits-title": "Bewerk REST-verzoeken voor tenant", + "edit-customer-rest-limits-title": "Bewerk REST-verzoeken voor klant", + "edit-ws-limit-updates-per-session-title": "Bewerk WS-updates per sessie", + "edit-cassandra-tenant-limits-configuration-title": "Bewerk Cassandra-query voor tenant", + "edit-tenant-entity-export-rate-limit-title": "Bewerk rate-limieten voor entiteitsversie-aanmaak", + "edit-tenant-entity-import-rate-limit-title": "Bewerk rate-limieten voor entiteitsversielading", + "edit-tenant-notification-request-rate-limit-title": "Bewerk rate-limieten voor meldingsverzoeken", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "Bewerk rate-limieten per meldingsregel", + "edit-edge-events-rate-limit": "Bewerk rate-limieten voor edge-gebeurtenissen", + "edit-edge-events-per-edge-rate-limit": "Bewerk rate-limieten voor edge-gebeurtenissen per edge", + "edge-events-rate-limit": "Edge-gebeurtenissen", + "edge-events-per-edge-rate-limit": "Edge-gebeurtenissen per edge", + "edit-edge-uplink-messages-rate-limit": "Bewerk rate-limieten voor uplinkberichten van edge", + "edit-edge-uplink-messages-per-edge-rate-limit": "Bewerk rate-limieten voor uplinkberichten per edge", + "edge-uplink-messages-rate-limit": "Edge-uplinkberichten", + "edge-uplink-messages-per-edge-rate-limit": "Edge-uplinkberichten per edge", + "messages-per": "berichten per", + "not-set": "Niet ingesteld", + "number-of-messages": "Aantal berichten", + "number-of-messages-required": "Aantal berichten is verplicht.", + "number-of-messages-min": "Minimale waarde is 1.", + "preview": "Voorbeeld", + "per-seconds": "Per seconden", + "per-seconds-required": "Tijdslimiet is verplicht.", + "per-seconds-min": "Minimale waarde is 1.", + "rate-limits": "Rate-limieten", + "remove-limit": "Limiet verwijderen", + "transport-tenant-msg": "Transport tenantberichten", + "transport-tenant-telemetry-msg": "Transport tenanttelemetrieberichten", + "transport-tenant-telemetry-data-points": "Transport tenanttelemetriegegevenspunten", + "transport-device-msg": "Transport apparaatberichten", + "transport-device-telemetry-msg": "Transport apparaattelmetrieberichten", + "transport-device-telemetry-data-points": "Transport apparaattelmetriegegevenspunten", + "transport-gateway-msg": "Transport gatewayberichten", + "transport-gateway-telemetry-msg": "Transport gatewaytelemetrieberichten", + "transport-gateway-telemetry-data-points": "Transport gatewaytelemetriegegevenspunten", + "transport-gateway-device-msg": "Transport gatewayapparaatberichten", + "transport-gateway-device-telemetry-msg": "Transport gatewayapparaattelemetrieberichten", + "transport-gateway-device-telemetry-data-points": "Transport gatewayapparaattelemetriegegevenspunten", + "sec": "sec" + } + }, + "timeinterval": { + "seconds-interval": "{ seconds, plural, =1 {1 seconde} other {# seconden} }", + "minutes-interval": "{ minutes, plural, =1 {1 minuut} other {# minuten} }", + "hours-interval": "{ hours, plural, =1 {1 uur} other {# uur} }", + "days-interval": "{ days, plural, =1 {1 dag} other {# dagen} }", + "days": "Dagen", + "hours": "Uren", + "minutes": "Minuten", + "seconds": "Seconden", + "advanced": "Geavanceerd", + "custom": "Aangepast", + "predefined": { + "yesterday": "Gisteren", + "day-before-yesterday": "Eergisteren", + "this-day-last-week": "Deze dag vorige week", + "previous-week": "Vorige week (zon - zat)", + "previous-week-iso": "Vorige week (maa - zon)", + "previous-month": "Vorige maand", + "previous-quarter": "Vorig kwartaal", + "previous-half-year": "Vorige halfjaar", + "previous-year": "Vorig jaar", + "current-hour": "Huidig uur", + "current-day": "Huidige dag", + "current-day-so-far": "Tot nu toe vandaag", + "current-week": "Huidige week (zon - zat)", + "current-week-iso": "Huidige week (maa - zon)", + "current-week-so-far": "Tot nu toe deze week (zon - zat)", + "current-week-iso-so-far": "Tot nu toe deze week (maa - zon)", + "current-month": "Huidige maand", + "current-month-so-far": "Tot nu toe deze maand", + "current-quarter": "Huidige kwartaal", + "current-quarter-so-far": "Tot nu toe dit kwartaal", + "current-half-year": "Huidige halfjaar", + "current-half-year-so-far": "Tot nu toe dit halfjaar", + "current-year": "Huidig jaar", + "current-year-so-far": "Tot nu toe dit jaar" + }, + "type": { + "week": "Week (zon - zat)", + "week-iso": "Week (maa - zon)", + "month": "Maand", + "quarter": "Kwartaal" + } + }, + "timeunit": { + "milliseconds": "Milliseconden", + "seconds": "Seconden", + "minutes": "Minuten", + "hours": "Uren", + "days": "Dagen" + }, + "timewindow": { + "timewindow": "Tijdvenster", + "timewindow-settings": "Tijdvensterinstellingen", + "years": "{ years, plural, =1 { jaar } other {# jaren } }", + "years-short": "{{ years }}j", + "months": "{ months, plural, =1 { maand } other {# maanden } }", + "months-short": "{{ months }}M", + "weeks": "{ weeks, plural, =1 { week } other {# weken } }", + "weeks-short": "{{ weeks }}w", + "days": "{ days, plural, =1 { dag } other {# dagen } }", + "days-short": "{{ days }}d", + "hours": "{ hours, plural, =0 { uur } =1 {1 uur } other {# uren } }", + "hr": "{{ hr }} uur", + "hr-short": "{{ hr }}u", + "minutes": "{ minutes, plural, =0 { minuut } =1 {1 minuut } other {# minuten } }", + "min": "{{ min }} min", + "min-short": "{{ min }}m", + "seconds": "{ seconds, plural, =0 { seconde } =1 {1 seconde } other {# seconden } }", + "sec": "{{ sec }} sec", + "sec-short": "{{ sec }}s", + "short": { + "years": "{ years, plural, =1 {1 jaar } other {# jaren } }", + "days": "{ days, plural, =1 {1 dag } other {# dagen } }", + "hours": "{ hours, plural, =1 {1 uur } other {# uren } }", + "minutes": "{{minutes}} min", + "seconds": "{{seconds}} sec" + }, + "realtime": "Realtime", + "history": "Geschiedenis", + "last-prefix": "laatste", + "period": "van {{ startTime }} tot {{ endTime }}", + "edit": "Tijdvenster bewerken", + "date-range": "Datumbereik", + "for-all-time": "Voor alle tijd", + "last": "Laatste", + "time-period": "Tijdperiode", + "hide": "Verbergen", + "interval": "Interval", + "just-now": "Zojuist", + "just-now-lower": "zojuist", + "ago": "geleden", + "style": "Tijdvensterstijl", + "icon": "Icoon", + "icon-position": "Icoonpositie", + "icon-position-left": "Links", + "icon-position-right": "Rechts", + "font": "Lettertype", + "color": "Kleur", + "displayTypePrefix": "Toon realtime/geschiedenisvoorvoegsel", + "preview": "Voorbeeld", + "relative": "Relatief", + "range": "Bereik", + "hide-timewindow-section": "Verberg tijdvenstergedeelte voor eindgebruikers", + "hide-last-interval": "Verberg laatste interval voor eindgebruikers", + "hide-relative-interval": "Verberg relatieve interval voor eindgebruikers", + "hide-fixed-interval": "Verberg vast interval voor eindgebruikers", + "hide-aggregation": "Verberg aggregatie voor eindgebruikers", + "hide-group-interval": "Verberg groeperingsinterval voor eindgebruikers", + "hide-max-values": "Verberg maximumwaarden voor eindgebruikers", + "hide-timezone": "Verberg tijdzone voor eindgebruikers", + "disable-custom-interval": "Schakel aangepaste intervalselectie uit", + "edit-aggregation-functions-list": "Bewerk lijst met aggregatiefuncties", + "edit-aggregation-functions-list-hint": "Lijst met beschikbare opties kan worden gespecificeerd.", + "allowed-aggregation-functions": "Toegestane aggregatiefuncties", + "edit-intervals-list": "Bewerk intervallijst", + "allowed-agg-intervals": "Groeperingsintervallen", + "default-agg-interval": "Standaard groeperingsinterval", + "edit-intervals-list-hint": "Lijst met beschikbare intervalopties kan worden gespecificeerd.", + "edit-grouping-intervals-list-hint": "Het is mogelijk om de lijst met groeperingsintervallen en het standaardinterval te configureren.", + "all": "Alles" + }, + "tooltip": { + "trigger": "Trigger", + "trigger-point": "Punt", + "trigger-axis": "As", + "label": "Label", + "value": "Waarde", + "date": "Datum", + "show-date-time-interval": "Toon datum-tijdinterval", + "show-date-time-interval-hint": "Toon datum-tijdinterval volgens de gegevensaggregatie.", + "background-color": "Achtergrondkleur", + "background-blur": "Achtergrondvervaging" + }, + "unit": { + "millimeter": "Millimeter", + "centimeter": "Centimeter", + "angstrom": "Angström", + "nanometer": "Nanometer", + "micrometer": "Micrometer", + "meter": "Meter", + "kilometer": "Kilometer", + "inch": "Inch", + "foot": "Voet", + "yard": "Yard", + "mile": "Mijl", + "nautical-mile": "Zeemijl", + "astronomical-unit": "Astronomische eenheid", + "reciprocal-metre": "Reciproque meter", + "meter-per-meter": "Meter per meter", + "steradian": "Steradiaal", + "thou": "Duizendste inch", + "barleycorn": "Barleycorn", + "hand": "Hand", + "chain": "Ketting", + "furlong": "Furlong", + "league": "League", + "fathom": "Vadem", + "cable": "Kabel", + "link": "Schakel", + "rod": "Roede", + "nanogram": "Nanogram", + "microgram": "Microgram", + "milligram": "Milligram", + "gram": "Gram", + "kilogram": "Kilogram", + "tonne": "Ton", + "ounce": "Ons", + "pound": "Pond", + "stone": "Stone", + "hundredweight-count": "Honderdpond", + "short-tons": "Amerikaanse ton", + "dalton": "Dalton", + "grain": "Graan", + "drachm": "Drachme", + "quarter": "Quarter", + "slug": "Slug", + "carat": "Karaat", + "cubic-millimeter": "Kubieke millimeter", + "cubic-centimeter": "Kubieke centimeter", + "cubic-meter": "Kubieke meter", + "cubic-kilometer": "Kubieke kilometer", + "microliter": "Microliter", + "milliliter": "Milliliter", + "liter": "Liter", + "hectoliter": "Hectoliter", + "cubic-inch": "Kubieke inch", + "cubic-foot": "Kubieke voet", + "cubic-yard": "Kubieke yard", + "fluid-ounce": "Fluid ounce", + "pint": "Pint", + "quart": "Quart", + "gallon": "Gallon", + "oil-barrels": "Olievat", + "cubic-meter-per-kilogram": "Kubieke meter per kilogram", + "gill": "Gill", + "hogshead": "Hogshead", + "teaspoon": "Theelepel", + "tablespoon": "Eetlepel", + "cup": "Kopje", + "celsius": "Celsius", + "kelvin": "Kelvin", + "rankine": "Rankine", + "fahrenheit": "Fahrenheit", + "percent": "Procent", + "meter-per-second": "Meter per seconde", + "kilometer-per-hour": "Kilometer per uur", + "foot-per-second": "Voet per seconde", + "mile-per-hour": "Mijl per uur", + "knot": "Knoop", + "millimeters-per-minute": "Millimeter per minuut", + "kilometer-per-hour-squared": "Kilometer per uur kwadraat", + "foot-per-second-squared": "Voet per seconde kwadraat", + "pascal": "Pascal", + "kilopascal": "Kilopascal", + "megapascal": "Megapascal", + "gigapascal": "Gigapascal", + "millibar": "Millibar", + "bar": "Bar", + "kilobar": "Kilobar", + "newton": "Newton", + "newton-meter": "Newtonmeter", + "foot-pounds": "Voet-pond", + "inch-pounds": "Inch-pond", + "newton-per-meter": "Newton per meter", + "atmospheres": "Atmospferen", + "pounds-per-square-inch": "Ponden per vierkante inch", + "torr": "Torr", + "inches-of-mercury": "Inches kwik", + "pascal-per-square-meter": "Pascal per vierkante meter", + "pound-per-square-inch": "Pond per vierkante inch", + "newton-per-square-meter": "Newton per vierkante meter", + "kilogram-force-per-square-meter": "Kilogramkracht per vierkante meter", + "pascal-per-square-centimeter": "Pascal per vierkante centimeter", + "ton-force-per-square-inch": "Ton-kracht per vierkante inch", + "kilonewton-per-square-meter": "Kilonewton per vierkante meter", + "newton-per-square-millimeter": "Newton per vierkante millimeter", + "microjoule": "Microjoule", + "millijoule": "Millijoule", + "joule": "Joule", + "kilojoule": "Kilojoule", + "megajoule": "Megajoule", + "gigajoule": "Gigajoule", + "watt-hour": "Wattuur", + "kilowatt-hour": "Kilowattuur", + "electron-volts": "Elektronvolt", + "joules-per-coulomb": "Joules per coulomb", + "british-thermal-unit": "Britse thermische eenheid", + "foot-pound": "Voet-pond", + "calorie": "Calorie", + "small-calorie": "Kleine calorie", + "kilocalorie": "Kilocalorie", + "joule-per-kelvin": "Joule per Kelvin", + "joule-per-kilogram-kelvin": "Joule per kilogram-Kelvin", + "joule-per-kilogram": "Joule per kilogram", + "watt-per-meter-kelvin": "Watt per meter-Kelvin", + "joule-per-cubic-meter": "Joule per kubieke meter", + "therm": "Therm", + "electric-dipole-moment": "Elektrisch dipoolmoment", + "magnetic-dipole-moment": "Magnetisch dipoolmoment", + "debye": "Debye", + "coulomb-per-square-meter-per-volt": "Coulomb per vierkante meter per volt", + "milliwatt": "Milliwatt", + "microwatt": "Microwatt", + "watt": "Watt", + "kilowatt": "Kilowatt", + "megawatt": "Megawatt", + "gigawatt": "Gigawatt", + "metric-horsepower": "Metrisch paardekracht", + "milliwatt-per-square-centimeter": "Milliwatt per vierkante centimeter", + "watt-per-square-centimeter": "Watt per vierkante centimeter", + "kilowatt-per-square-centimeter": "Kilowatt per vierkante centimeter", + "milliwatt-per-square-meter": "Milliwatt per vierkante meter", + "watt-per-square-meter": "Watt per vierkante meter", + "kilowatt-per-square-meter": "Kilowatt per vierkante meter", + "watt-per-square-inch": "Watt per vierkante inch", + "kilowatt-per-square-inch": "Kilowatt per vierkante inch", + "horsepower": "Paardenkracht", + "btu-per-hour": "BTU per uur", + "coulomb": "Coulomb", + "millicoulomb": "Millicoulomb", + "microcoulomb": "Microcoulomb", + "picocoulomb": "Picocoulomb", + "coulomb-per-meter": "Coulomb per meter", + "coulomb-per-cubic-meter": "Coulomb per kubieke meter", + "coulomb-per-square-meter": "Coulomb per vierkante meter", + "square-millimeter": "Vierkante millimeter", + "square-centimeter": "Vierkante centimeter", + "square-meter": "Vierkante meter", + "hectare": "Hectare", + "square-kilometer": "Vierkante kilometer", + "square-inch": "Vierkante inch", + "square-foot": "Vierkante voet", + "square-yard": "Vierkante yard", + "acre": "Acre", + "square-mile": "Vierkante mijl", + "are": "Are", + "barn": "Barn", + "circular-inch": "Circulaire inch", + "milliampere-hour": "Milliampère-uur", + "ampere-hours": "Ampère-uren", + "kiloampere-hours": "Kiloampère-uren", + "nanoampere": "Nanoampère", + "picoampere": "Picoampère", + "microampere": "Microampère", + "milliampere": "Milliampère", + "ampere": "Ampère", + "microampere-per-square-centimeter": "Microampère per vierkante centimeter", + "ampere-per-square-meter": "Ampère per vierkante meter", + "ampere-per-meter": "Ampère per meter", + "oersted": "Oersted", + "bohr-magneton": "Bohr Magneton", + "ampere-meter-squared": "Ampère-meter in het kwadraat", + "nanovolt": "Nanovolt", + "picovolt": "Picovolt", + "volt": "Volt", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "Volt-meter", + "kilovolt-meter": "Kilovolt-meter", + "megavolt-meter": "Megavolt-meter", + "microvolt-meter": "Microvolt-meter", + "millivolt-meter": "Millivolt-meter", + "nanovolt-meter": "Nanovolt-meter", + "ohm": "Ohm", + "microohm": "Micro-ohm", + "milliohm": "Milli-ohm", + "kilohm": "Kilo-ohm", + "megohm": "Mega-ohm", + "gigohm": "Giga-ohm", + "hertz": "Hertz", + "kilohertz": "Kilohertz", + "megahertz": "Megahertz", + "gigahertz": "Gigahertz", + "rpm": "Omwentelingen per minuut", + "candela-per-square-meter": "Candela per vierkante meter", + "candela": "Candela", + "lumen": "Lumen", + "lux": "Lux", + "foot-candle": "Voet-candela", + "lumen-per-square-meter": "Lumen per vierkante meter", + "lux-second": "Lux-seconde", + "lumen-second": "Lumen-seconde", + "lumens-per-watt": "Lumen per watt", + "mole": "Mol", + "nanomole": "Nanomol", + "micromole": "Micromol", + "millimole": "Millimol", + "kilomole": "Kilomol", + "mole-per-cubic-meter": "Mol per kubieke meter", + "rssi": "RSSI", + "ppm": "Deeltjes per miljoen (ppm)", + "ppb": "Deeltjes per miljard (ppb)", + "micrograms-per-cubic-meter": "Microgram per kubieke meter", + "aqi": "Luchtkwaliteitsindex (AQI)", + "gram-per-cubic-meter": "Gram per kubieke meter", + "gram-per-kilogram": "Specifieke vochtigheid", + "millimeters-per-second": "Millimeter per seconde", + "neper": "Neper", + "bel": "Bel", + "decibel": "Decibel", + "meters-per-second-squared": "Meter per seconde kwadraat", + "becquerel": "Becquerel", + "curie": "Curie", + "gray": "Gray", + "sievert": "Sievert", + "roentgen": "Röntgen", + "cps": "Tellingen per seconde", + "rad": "Rad", + "rem": "Rem", + "dps": "Desintegraties per seconde", + "rutherford": "Rutherford", + "coulombs-per-kilogram": "Coulomb per kilogram", + "becquerels-per-cubic-meter": "Becquerel per kubieke meter", + "curies-per-liter": "Curie per liter", + "becquerels-per-second": "Becquerel per seconde", + "curies-per-second": "Curie per seconde", + "gy-per-second": "Gray per seconde", + "watt-per-steradian": "Watt per steradiaal", + "watt-per-square-metre-steradian": "Watt per vierkante meter-steradiaal", + "ph-level": "pH-waarde", + "turbidity": "Troebelheid", + "mg-per-liter": "Milligram per liter", + "microsiemens-per-centimeter": "Microsiemens per centimeter", + "millisiemens-per-meter": "Millisiemens per meter", + "siemens-per-meter": "Siemens per meter", + "kilogram-per-cubic-meter": "Kilogram per kubieke meter", + "gram-per-cubic-centimeter": "Gram per kubieke centimeter", + "kilogram-per-square-meter": "Kilogram per vierkante meter", + "milligram-per-milliliter": "Milligram per milliliter", + "milligram-per-cubic-meter": "Milligram per kubieke meter", + "pound-per-cubic-foot": "Pond per kubieke voet", + "ounces-per-cubic-inch": "Ons per kubieke inch", + "tons-per-cubic-yard": "Ton per kubieke yard", + "particle-density": "Deeltjesdichtheid", + "kilometers-per-liter": "Kilometer per liter", + "miles-per-gallon": "Mijl per gallon", + "liters-per-100-km": "Liter per 100 km", + "gallons-per-mile": "Gallon per mijl", + "liters-per-hour": "Liter per uur", + "gallons-per-hour": "Gallon per uur", + "beats-per-minute": "Slagen per minuut", + "millimeters-of-mercury": "Millimeter kwik", + "milligrams-per-deciliter": "Milligram per deciliter", + "g-force": "G-kracht", + "kilonewton": "Kilonewton", + "kilogram-force": "Kilogramkracht", + "pound-force": "Pondkracht", + "kilopound-force": "Kilopondkracht", + "dyne": "Dyne", + "poundal": "Poundal", + "kip": "Kip", + "gal": "Gal", + "gravity": "Zwaartekracht", + "hectopascal": "Hectopascal", + "atmosphere": "Atmosfeer", + "millibars": "Millibar", + "inch-of-mercury": "Inch kwik", + "richter-scale": "Richterschaal", + "second": "Seconde", + "minute": "Minuut", + "hour": "Uur", + "day": "Dag", + "week": "Week", + "month": "Maand", + "year": "Jaar", + "cubic-foot-per-minute": "Kubieke voet per minuut", + "cubic-meters-per-hour": "Kubieke meter per uur", + "cubic-meters-per-second": "Kubieke meter per seconde", + "liter-per-second": "Liter per seconde", + "liter-per-minute": "Liter per minuut", + "gallons-per-minute": "Gallon per minuut", + "cubic-foot-per-second": "Kubieke voet per seconde", + "milliliters-per-minute": "Milliliter per minuut", + "bit": "Bit", + "byte": "Byte", + "kilobyte": "Kilobyte", + "megabyte": "Megabyte", + "gigabyte": "Gigabyte", + "terabyte": "Terabyte", + "petabyte": "Petabyte", + "exabyte": "Exabyte", + "zettabyte": "Zettabyte", + "yottabyte": "Yottabyte", + "bit-per-second": "Bit per seconde", + "kilobit-per-second": "Kilobit per seconde", + "megabit-per-second": "Megabit per seconde", + "gigabit-per-second": "Gigabit per seconde", + "terabit-per-second": "Terabit per seconde", + "byte-per-second": "Byte per seconde", + "kilobyte-per-second": "Kilobyte per seconde", + "megabyte-per-second": "Megabyte per seconde", + "gigabyte-per-second": "Gigabyte per seconde", + "degree": "Graad", + "radian": "Radiaal", + "gradian": "Gradiënt", + "revolution": "Omwenteling", + "siemens": "Siemens", + "millisiemens": "Millisiemens", + "microsiemens": "Microsiemens", + "kilosiemens": "Kilosiemens", + "megasiemens": "Megasiemens", + "gigasiemens": "Gigasiemens", + "farad": "Farad", + "millifarad": "Millifarad", + "microfarad": "Microfarad", + "nanofarad": "Nanofarad", + "picofarad": "Picofarad", + "kilofarad": "Kilofarad", + "megafarad": "Megafarad", + "gigafarad": "Gigafarad", + "terfarad": "Terafarad", + "farad-per-meter": "Farad per meter", + "tesla": "Tesla", + "gauss": "Gauss", + "kilogauss": "Kilogauss", + "millitesla": "Millitesla", + "microtesla": "Microtesla", + "nanotesla": "Nanotesla", + "kilotesla": "Kilotesla", + "megatesla": "Megatesla", + "millitesla-square-meters": "Millitesla vierkante meter", + "gamma": "Gamma", + "lambda": "Lambda", + "square-meter-per-second": "Vierkante meter per seconde", + "square-centimeter-per-second": "Vierkante centimeter per seconde", + "stoke": "Stoke", + "centistokes": "Centistokes", + "square-foot-per-second": "Vierkante voet per seconde", + "square-inch-per-second": "Vierkante inch per seconde", + "pascal-second": "Pascal-seconde", + "centipoise": "Centipoise", + "poise": "Poise", + "reynolds": "Reynolds", + "pound-per-foot-hour": "Pond per voet-uur", + "newton-second-per-square-meter": "Newton-seconde per vierkante meter", + "dyne-second-per-square-centimeter": "Dyne-seconde per vierkante centimeter", + "kilogram-per-meter-second": "Kilogram per meter-seconde", + "tesla-square-meters": "Tesla vierkante meters", + "maxwell": "Maxwell", + "tesla-per-meter": "Tesla per meter", + "gauss-per-centimeter": "Gauss per centimeter", + "weber": "Weber", + "microweber": "Microweber", + "milliweber": "Milliweber", + "gauss-square-centimeter": "Gauss-vierkante centimeter", + "kilogauss-square-centimeter": "Kilogauss-vierkante centimeter", + "henry": "Henry", + "millihenry": "Millihenry", + "microhenry": "Microhenry", + "nanohenry": "Nanohenry", + "henry-per-meter": "Henry per meter", + "tesla-meter-per-ampere": "Tesla-meter per ampère", + "gauss-per-oersted": "Gauss per oersted", + "kilogram-per-mole": "Kilogram per mol", + "gram-per-mole": "Gram per mol", + "milligram-per-mole": "Milligram per mol", + "joule-per-mole": "Joule per mol", + "joule-per-mole-kelvin": "Joule per mol-Kelvin", + "millivolts-per-meter": "Millivolt per meter", + "volts-per-meter": "Volt per meter", + "kilovolts-per-meter": "Kilovolt per meter", + "radian-per-second": "Radiaal per seconde", + "radian-per-second-squared": "Radiaal per seconde kwadraat", + "revolutions-per-minute-per-second": "Hoeksversnelling", + "deg-per-second": "Graden per seconde", + "degrees-brix": "Graden Brix", + "katal": "Katal", + "katal-per-cubic-metre": "Katal per kubieke meter" + }, + "user": { + "user": "Gebruiker", + "users": "Gebruikers", + "customer-users": "Klantgebruikers", + "tenant-admins": "Tenantbeheerders", + "sys-admin": "Systeembeheerder", + "tenant-admin": "Tenantbeheerder", + "customer": "Klant", + "anonymous": "Anoniem", + "add": "Gebruiker toevoegen", + "delete": "Gebruiker verwijderen", + "add-user-text": "Nieuwe gebruiker toevoegen", + "no-users-text": "Geen gebruikers gevonden", + "user-details": "Gebruikersgegevens", + "delete-user-title": "Weet je zeker dat je gebruiker '{{userEmail}}' wilt verwijderen?", + "delete-user-text": "Wees voorzichtig, na bevestiging worden de gebruiker en alle gerelateerde gegevens permanent verwijderd.", + "delete-users-title": "Weet je zeker dat je { count, plural, =1 {1 gebruiker} other {# gebruikers} } wilt verwijderen?", + "delete-users-action-title": "Verwijder { count, plural, =1 {1 gebruiker} other {# gebruikers} }", + "delete-users-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde gebruikers en gerelateerde gegevens permanent verwijderd.", + "activation-email-sent-message": "Activatie-e-mail is succesvol verzonden!", + "resend-activation": "Activatie opnieuw verzenden", + "email": "E-mail", + "email-required": "E-mail is verplicht.", + "invalid-email-format": "Ongeldig e-mailformaat.", + "first-name": "Voornaam", + "last-name": "Achternaam", + "description": "Beschrijving", + "default-dashboard": "Standaarddashboard", + "always-fullscreen": "Altijd volledig scherm", + "select-user": "Selecteer gebruiker", + "no-users-matching": "Geen gebruikers gevonden die overeenkomen met '{{entity}}'.", + "user-required": "Gebruiker is verplicht", + "activation-method": "Activatiemethode", + "display-activation-link": "Toon activatielink", + "send-activation-mail": "Stuur activatie-e-mail", + "activation-link": "Gebruikersactivatielink", + "activation-link-text": "Om de gebruiker te activeren, gebruik de volgende activatielink (vervalt over {{activationLinkTtl}}) :", + "copy-activation-link": "Kopieer activatielink", + "activation-link-copied-message": "Gebruikersactivatielink is gekopieerd naar klembord", + "details": "Details", + "login-as-tenant-admin": "Inloggen als tenantbeheerder", + "login-as-customer-user": "Inloggen als klantgebruiker", + "search": "Zoek gebruikers", + "selected-users": "{ count, plural, =1 {1 gebruiker} other {# gebruikers} } geselecteerd", + "disable-account": "Gebruikersaccount uitschakelen", + "enable-account": "Gebruikersaccount inschakelen", + "enable-account-message": "Gebruikersaccount is succesvol ingeschakeld!", + "disable-account-message": "Gebruikersaccount is succesvol uitgeschakeld!", + "copyId": "Kopieer gebruikers-ID", + "idCopiedMessage": "Gebruikers-ID is gekopieerd naar klembord", + "user-list": "Gebruikerslijst", + "user-list-required": "Gebruikerslijst is verplicht" + }, + "value": { + "type": "Waarde type", + "string": "Tekenreeks", + "string-value": "Tekenreekswaarde", + "string-value-required": "Tekenreekswaarde is verplicht", + "integer": "Geheel getal", + "integer-value": "Geheel getalwaarde", + "integer-value-required": "Geheel getalwaarde is verplicht", + "invalid-integer-value": "Ongeldige geheel getalwaarde", + "double": "Dubbel", + "double-value": "Dubbele waarde", + "double-value-required": "Dubbele waarde is verplicht", + "boolean": "Boolean", + "boolean-value": "Boolean waarde", + "false": "Onwaar", + "true": "Waar", + "long": "Lang", + "json": "JSON", + "json-value": "JSON waarde", + "json-value-invalid": "JSON waarde heeft een ongeldig formaat", + "json-value-required": "JSON waarde is verplicht." + }, + "version-control": { + "version-control": "Versiebeheer", + "management": "Beheer van versies", + "search": "Zoek versies", + "branch": "Branch", + "default": "Standaard", + "select-branch": "Selecteer branch", + "branch-required": "Branch is verplicht", + "create-entity-version": "Maak entiteitversie aan", + "version-name": "Versienaam", + "version-name-required": "Versienaam is verplicht", + "author": "Auteur", + "export-relations": "Exporteer relaties", + "export-attributes": "Exporteer attributen", + "export-credentials": "Exporteer inloggegevens", + "export-calculated-fields": "Exporteer berekende velden", + "entity-versions": "Entiteitversies", + "versions": "Versies", + "created-time": "Aangemaakt op", + "version-id": "Versie-ID", + "no-entity-versions-text": "Geen entiteitversies gevonden", + "no-versions-text": "Geen versies gevonden", + "copy-full-version-id": "Kopieer volledige versie-ID", + "create-version": "Versie aanmaken", + "creating-version": "Versie wordt aangemaakt... Even geduld", + "nothing-to-commit": "Geen wijzigingen om op te slaan", + "restore-version": "Herstel versie", + "restore-entity-from-version": "Herstel entiteit vanuit versie '{{versionName}}'", + "restoring-entity-version": "Entiteitversie wordt hersteld... Even geduld", + "load-relations": "Relaties laden", + "load-attributes": "Attributen laden", + "load-credentials": "Inloggegevens laden", + "load-calculated-fields": "Berekende velden laden", + "compare-with-current": "Vergelijk met huidige", + "diff-entity-with-version": "Verschil met entiteitversie '{{versionName}}'", + "previous-difference": "Vorige verschil", + "next-difference": "Volgende verschil", + "current": "Huidig", + "differences": "{ count, plural, =1 {1 verschil} other {# verschillen} }", + "create-entities-version": "Maak versie van entiteiten", + "default-sync-strategy": "Standaard synchronisatiestrategie", + "sync-strategy-merge": "Samenvoegen", + "sync-strategy-overwrite": "Overschrijven", + "entities-to-export": "Te exporteren entiteiten", + "entities-to-restore": "Te herstellen entiteiten", + "sync-strategy": "Synchronisatiestrategie", + "all-entities": "Alle entiteiten", + "no-entities-to-export-prompt": "Specificeer entiteiten om te exporteren", + "no-entities-to-restore-prompt": "Specificeer entiteiten om te herstellen", + "add-entity-type": "Voeg entiteittype toe", + "remove-all": "Alles verwijderen", + "version-create-result": "{ added, plural, =0 {Geen entiteiten} =1 {1 entiteit} other {# entiteiten} } toegevoegd.
{ modified, plural, =0 {Geen entiteiten} =1 {1 entiteit} other {# entiteiten} } aangepast.
{ removed, plural, =0 {Geen entiteiten} =1 {1 entiteit} other {# entiteiten} } verwijderd.", + "remove-other-entities": "Andere entiteiten verwijderen", + "find-existing-entity-by-name": "Zoek bestaande entiteit op naam", + "restore-entities-from-version": "Herstel entiteiten vanuit versie '{{versionName}}'", + "restoring-entities-from-version": "Entiteiten worden hersteld... Even geduld", + "no-entities-restored": "Geen entiteiten hersteld", + "created": "{{created}} aangemaakt", + "updated": "{{updated}} bijgewerkt", + "deleted": "{{deleted}} verwijderd", + "remove-other-entities-confirm-text": "Wees voorzichtig! Dit zal permanent alle huidige entiteiten verwijderen
die niet aanwezig zijn in de versie die u wilt herstellen.

Typ \"remove other entities\" om te bevestigen.", + "auto-commit-to-branch": "automatisch vastleggen naar {{ branch }} branch", + "default-create-entity-version-name": "{{entityName}} update", + "sync-strategy-merge-hint": "Maakt of werkt geselecteerde entiteiten bij in de repository. Alle andere entiteiten in de repository worden niet gewijzigd.", + "sync-strategy-overwrite-hint": "Maakt of werkt geselecteerde entiteiten bij in de repository. Alle andere entiteiten in de repository worden verwijderd.", + "device-credentials-conflict": "Kan apparaat met externe id {{entityId}} niet laden
omdat dezelfde inloggegevens al bestaan in de database voor een ander apparaat.
Overweeg om de instelling inloggegevens laden uit te schakelen in het herstelformulier.", + "missing-referenced-entity": "Kan {{sourceEntityTypeName}} met externe id {{sourceEntityId}} niet laden
omdat het verwijst naar ontbrekende {{targetEntityTypeName}} met id {{targetEntityId}}.", + "runtime-failed": "Mislukt: {{message}}", + "auto-commit-settings-read-only-hint": "De functie voor automatisch vastleggen werkt niet wanneer de optie alleen-lezen is ingeschakeld in de repository-instellingen.", + "rollback-on-error": "Rollback bij fout", + "rollback-on-error-hint": "Als u een groot aantal entiteiten moet herstellen, overweeg dan om deze optie uit te schakelen om de prestaties te verbeteren.\n Let op: als er tijdens het laden van de versie een fout optreedt, blijven reeds opgeslagen entiteiten (met relaties, attributen, enz.) ongewijzigd." + }, + "widget": { + "widget-library": "Widgetbibliotheek", + "widget-bundle": "Widgetbundel", + "all-bundles": "Alle bundels", + "select-widgets-bundle": "Selecteer widgetbundel", + "widgets": "Widgets", + "all-widgets": "Alle widgets", + "widget": "Widget", + "select-widget": "Selecteer widget", + "no-widgets-matching": "Geen widgets gevonden die overeenkomen met '{{entity}}'.", + "no-widgets": "Nog geen widgets", + "no-widgets-text": "Geen widgets gevonden", + "management": "Widgetbeheer", + "editor": "Widgeteditor", + "confirm-to-exit-editor-html": "U heeft niet-opgeslagen widgetinstellingen.
Weet u zeker dat u deze pagina wilt verlaten?", + "widget-type-not-found": "Probleem bij het laden van widgetconfiguratie.
Mogelijk is het bijbehorende widgettype verwijderd.", + "widget-type-load-error": "Widget kon niet worden geladen vanwege de volgende fouten:", + "remove": "Widget verwijderen", + "delete": "Widget verwijderen", + "edit": "Widget bewerken", + "remove-widget-title": "Weet u zeker dat u de widget '{{widgetTitle}}' wilt verwijderen?", + "remove-widget-text": "Na bevestiging worden de widget en alle gerelateerde gegevens onherstelbaar verwijderd.", + "replace-reference-with-widget-copy": "Vervang referentie door widgetkopie", + "timeseries": "Tijdreeksen", + "search-data": "Gegevens zoeken", + "no-data-found": "Geen gegevens gevonden", + "latest": "Laatste waarden", + "rpc": "Bedieningswidget", + "alarm": "Alarmwidget", + "static": "Statische widget", + "timeseries-short": "reeksen", + "latest-short": "laatste", + "rpc-short": "bediening", + "alarm-short": "alarm", + "static-short": "statisch", + "select-widget-type": "Selecteer widgettype", + "missing-widget-title-error": "Widgettitel moet worden opgegeven!", + "widget-saved": "Widget opgeslagen", + "unable-to-save-widget-error": "Kan widget niet opslaan! Widget bevat fouten!", + "save": "Widget opslaan", + "saveAs": "Widget opslaan als", + "move": "Widget verplaatsen", + "save-widget-as": "Widget opslaan als", + "save-widget-as-text": "Voer nieuwe widgettitel in", + "toggle-fullscreen": "Volledig scherm wisselen", + "run": "Widget uitvoeren", + "widget-title": "Widgettitel", + "title": "Titel", + "title-required": "Widgettitel is verplicht.", + "title-max-length": "Titel mag niet langer zijn dan 256 tekens", + "system": "Systeem", + "type": "Widgettype", + "resources": "Resources", + "resource-url": "JavaScript/CSS URL", + "resource-is-extension": "Is extensie", + "remove-resource": "Resource verwijderen", + "add-resource": "Resource toevoegen", + "html": "HTML", + "tidy": "Netjes maken", + "css": "CSS", + "settings-form": "Instellingenformulier", + "data-key-settings-form": "Instellingenformulier voor gegevenssleutel", + "latest-data-key-settings-form": "Instellingenformulier voor laatste gegevenssleutel", + "widget-settings": "Widgetinstellingen", + "description": "Beschrijving", + "tags": "Tags", + "image-preview": "Voorbeeldafbeelding", + "settings-form-selector": "Instellingenformulierkiezer", + "data-key-settings-form-selector": "Kiezer voor instellingenformulier voor gegevenssleutel", + "latest-data-key-settings-form-selector": "Kiezer voor instellingenformulier voor laatste gegevenssleutel", + "all": "Alles", + "actual": "Actueel", + "scada": "SCADA-symbool", + "deprecated": "Verouderd", + "has-basic-mode": "Heeft basisweergave", + "basic-mode-form-selector": "Formulierkiezer basisweergave", + "basic-mode": "Basis", + "advanced-mode": "Geavanceerd", + "javascript": "Javascript", + "js": "JS", + "delete-widget-title": "Weet u zeker dat u de widget '{{widgetName}}' wilt verwijderen?", + "delete-widget-text": "Na bevestiging worden de widget en alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-widgets-title": "Weet u zeker dat u { count, plural, =1 {1 widget} other {# widgets} } wilt verwijderen?", + "delete-widgets-text": "Let op, na bevestiging worden alle geselecteerde widgets verwijderd en worden alle gerelateerde gegevens onherstelbaar verwijderd.", + "delete-widget": "Widget verwijderen", + "widget-template-load-failed-error": "Kan widgettemplate niet laden!", + "details": "Details", + "widget-details": "Widgetdetails", + "add": "Widget toevoegen", + "add-existing-widget": "Bestaande widget toevoegen", + "add-new-widget": "Nieuwe widget toevoegen", + "search-widgets": "Widgets zoeken", + "selected-widgets": "{ count, plural, =1 {1 widget} other {# widgets} } geselecteerd", + "undo": "Widgetwijzigingen ongedaan maken", + "export": "Widget exporteren", + "export-prompt": "Widgetafbeeldingen en bronnen insluiten", + "export-widgets": "Widgets exporteren", + "export-widgets-prompt": "Widgetafbeeldingen en bronnen insluiten", + "import": "Widget importeren", + "no-data": "Geen gegevens om weer te geven op de widget", + "data-overflow": "Widget toont {{count}} van de {{total}} entiteiten", + "alarm-data-overflow": "Widget toont alarmen voor {{allowedEntities}} (maximaal toegestaan) entiteiten van in totaal {{totalEntities}} entiteiten", + "search": "Widget zoeken", + "filter": "Widgetfiltertype", + "loading-widgets": "Widgets laden...", + "widget-template-error": "Ongeldige HTML-template van widget.", + "reference": "Referentie" + }, + "widget-action": { + "header-button": "Widgetkopknop", + "do-nothing": "Niets doen", + "open-dashboard-state": "Navigeer naar nieuwe dashboardstatus", + "update-dashboard-state": "Huidige dashboardstatus bijwerken", + "open-dashboard": "Navigeer naar ander dashboard", + "custom": "Aangepaste actie", + "custom-pretty": "Aangepaste actie (met HTML-sjabloon)", + "custom-pretty-error-title": "Fout in aangepaste dialoog", + "custom-pretty-template-error": "Ongeldige aangepaste dialoogsjabloon.", + "custom-pretty-controller-error": "Er is een fout opgetreden bij het evalueren van de aangepaste dialoogfunctie.", + "mobile-action": "Mobiele actie", + "target-dashboard-state": "Doeldashboardstatus", + "target-dashboard-state-required": "Doeldashboardstatus is verplicht", + "set-entity-from-widget": "Entiteit instellen vanuit widget", + "target-dashboard": "Doeldashboard", + "select-target-dashboard": "Selecteer doeldashboard", + "target-dashboard-required": "Doeldashboard is verplicht.", + "open-right-layout": "Open rechter dashboardlayout (mobiele weergave)", + "state-display-type": "Weergaveoptie dashboardstatus", + "open-normal": "Normaal", + "open-in-separate-dialog": "Open in aparte dialoog", + "open-in-popover": "Open in popover", + "dialog-title": "Dialoogtitel", + "dialog-hide-dashboard-toolbar": "Verberg dashboardwerkbalk in dialoog", + "dialog-width": "Dialoogbreedte in procenten van weergavebreedte", + "dialog-height": "Dialooghoogte in procenten van weergavehoogte", + "dialog-size-range-error": "Dialoogmaat moet tussen 1 en 100 procent zijn.", + "popover-preferred-placement": "Voorkeurspositie van popover", + "popover-placement-top": "Boven", + "popover-placement-topLeft": "Boven links", + "popover-placement-topRight": "Boven rechts", + "popover-placement-right": "Rechts", + "popover-placement-rightTop": "Rechts boven", + "popover-placement-rightBottom": "Rechts onder", + "popover-placement-bottom": "Onder", + "popover-placement-bottomLeft": "Onder links", + "popover-placement-bottomRight": "Onder rechts", + "popover-placement-left": "Links", + "popover-placement-leftTop": "Links boven", + "popover-placement-leftBottom": "Links onder", + "popover-hide-on-click-outside": "Verberg popover bij klikken buiten", + "popover-hide-dashboard-toolbar": "Verberg dashboardwerkbalk in popover", + "popover-width": "Popoverbreedte", + "popover-height": "Popoverhoogte", + "popover-style": "Popoverstijl", + "open-new-browser-tab": "Open in nieuw browsertabblad", + "open-URL": "Open URL", + "URL": "URL", + "url-required": "URL is verplicht.", + "mobile": { + "device-provision": "Apparaatconfiguratie", + "action-type": "Mobiele actietype", + "select-action-type": "Selecteer mobiele actietype", + "action-type-required": "Mobiele actietype is verplicht", + "take-picture-from-gallery": "Afbeelding uit galerij kiezen", + "take-photo": "Foto maken", + "map-direction": "Open kaartnavigatie", + "map-location": "Open kaartlocatie", + "scan-qr-code": "QR-code scannen", + "make-phone-call": "Telefoongesprek voeren", + "get-location": "Telefoonlocatie ophalen", + "take-screenshot": "Schermafbeelding maken" + }, + "custom-action-function": "Aangepaste actiefunctie", + "custom-pretty-function": "Aangepaste actie (met HTML-sjabloon) functie", + "map-item-type": "Kaartobjecttype", + "map-item": { + "marker": "Markering", + "polygon": "Polygoon", + "rectangle": "Rechthoek", + "circle": "Cirkel" + }, + "place-map-item": "Kaartobject plaatsen", + "map-item-tooltip": { + "customize-map-item-tooltips": "Kaartobjecttooltips aanpassen", + "place-marker": "Markering plaatsen", + "start-draw-rectangle": "Teken rechthoek starten", + "finish-draw-rectangle": "Teken rechthoek beëindigen", + "start-draw-polygon": "Teken polygoon starten", + "continue-draw-polygon": "Doorgaan met polygoon tekenen", + "finish-draw-polygon": "Teken polygoon beëindigen", + "start-draw-circle": "Teken cirkel starten", + "finish-draw-circle": "Teken cirkel beëindigen" + } + }, + "widgets-bundle": { + "current": "Huidige bundel", + "widgets-bundles": "Widgetbundels", + "widgets-bundle-widgets": "Widgets van de bundel", + "add": "Widgetbundel toevoegen", + "delete": "Widgetbundel verwijderen", + "title": "Titel", + "title-required": "Titel is verplicht.", + "title-max-length": "Titel moet minder dan 256 tekens bevatten", + "description": "Beschrijving", + "image-preview": "Afbeeldingsvoorbeeld", + "scada": "SCADA-widgetbundel", + "order": "Volgorde", + "add-widgets-bundle-text": "Nieuwe widgetbundel toevoegen", + "no-widgets-bundles-text": "Geen widgetbundels gevonden", + "empty": "Widgetbundel is leeg", + "details": "Details", + "widgets-bundle-details": "Details van de widgetbundel", + "delete-widgets-bundle-title": "Weet je zeker dat je de widgetbundel '{{widgetsBundleTitle}}' wilt verwijderen?", + "delete-widgets-bundle-text": "Wees voorzichtig, na bevestiging worden de widgetbundel en alle gerelateerde gegevens onherroepelijk verwijderd.", + "delete-widgets-bundles-title": "Weet je zeker dat je { count, plural, =1 {1 widgetbundel} other {# widgetbundels} } wilt verwijderen?", + "delete-widgets-bundles-action-title": "Verwijder { count, plural, =1 {1 widgetbundel} other {# widgetbundels} }", + "delete-widgets-bundles-text": "Wees voorzichtig, na bevestiging worden alle geselecteerde widgetbundels en alle gerelateerde gegevens onherroepelijk verwijderd.", + "no-widgets-bundles-matching": "Geen widgetbundels gevonden die overeenkomen met '{{widgetsBundle}}'.", + "widgets-bundle-required": "Widgetbundel is verplicht.", + "system": "Systeem", + "import": "Widgetbundel importeren", + "export": "Widgetbundel exporteren", + "export-widgets-bundle-widgets-prompt": "Inclusief bundelwidgets in geëxporteerde gegevens (anders worden alleen verwijzingen naar widget FQNs geëxporteerd)", + "export-failed-error": "Kan widgetbundel niet exporteren: {{error}}", + "create-new-widgets-bundle": "Nieuwe widgetbundel maken", + "widgets-bundle-file": "Widgetbundelbestand", + "invalid-widgets-bundle-file-error": "Kan widgetbundel niet importeren: Ongeldige datastructuur van widgetbundel.", + "search": "Zoek widgetbundels", + "selected-widgets-bundles": "{ count, plural, =1 {1 widgetbundel} other {# widgetbundels} } geselecteerd", + "open-widgets-bundle": "Widgetbundel openen", + "loading-widgets-bundles": "Widgetbundels worden geladen...", + "create-new": "Nieuwe widgetbundel maken" + }, + "widget-config": { + "data": "Gegevens", + "settings": "Instellingen", + "advanced": "Geavanceerd", + "appearance": "Uiterlijk", + "widget-card": "Widgetkaart", + "mobile": "Mobiel", + "title": "Titel", + "title-tooltip": "Titeltooltip", + "general-settings": "Algemene instellingen", + "display-title": "Toon widgettitel", + "card-title": "Kaarttitel", + "drop-shadow": "Schaduw", + "enable-fullscreen": "Activeer volledig scherm", + "background-color": "Achtergrondkleur", + "text-color": "Tekstkleur", + "border-radius": "Randradius", + "padding": "Opvulling", + "margin": "Marge", + "widget-style": "Widgetstijl", + "widget-css": "Widget CSS", + "title-style": "Titelstijl", + "mobile-mode-settings": "Mobiele modus", + "order": "Volgorde", + "height": "Hoogte", + "mobile-hide": "Verberg widget in mobiele modus", + "desktop-hide": "Verberg widget in desktopmodus", + "units": "Speciaal symbool naast de waarde", + "units-by-default": "Standaardeenheden", + "decimals": "Aantal decimalen", + "decimals-by-default": "Standaard decimalen", + "default-data-key-parameter-hint": "Deze parameter geldt voor alle widgetwaarden tenzij overschreven door de gegevenssleutelconfiguratie", + "units-short": "Eenheden", + "decimals-short": "Decimalen", + "decimals-suffix": "decimalen", + "digits-suffix": "cijfers", + "timewindow": "Tijdsvenster", + "use-dashboard-timewindow": "Gebruik tijdsvenster van dashboard", + "use-widget-timewindow": "Gebruik tijdsvenster van widget", + "display-timewindow": "Toon tijdsvenster", + "legend": "Legenda", + "display-legend": "Toon legenda", + "datasources": "Gegevensbronnen", + "datasource": "Gegevensbron", + "maximum-datasources": "Maximaal { count, plural, =1 {1 gegevensbron toegestaan.} other {# gegevensbronnen toegestaan} }", + "timeseries-key-error": "Minimaal één tijdreeksgegevenssleutel moet worden opgegeven", + "datasource-type": "Type", + "datasource-parameters": "Parameters", + "remove-datasource": "Verwijder gegevensbron", + "add-datasource": "Gegevensbron toevoegen", + "target-device": "Doelapparaat", + "alarm-source": "Alarmbron", + "actions": "Acties", + "action": "Actie", + "add-action": "Actie toevoegen", + "search-actions": "Zoek acties", + "no-actions-text": "Geen acties gevonden", + "action-source": "Actiebron", + "select-action-source": "Selecteer actiebron", + "action-source-required": "Actiebron is verplicht.", + "column-index": "Kolomindex", + "select-column-index": "Selecteer kolomindex", + "column-index-required": "Kolomindex is verplicht.", + "not-set": "Niet ingesteld", + "action-name": "Naam", + "action-name-required": "Actienaam is verplicht.", + "action-name-not-unique": "Er bestaat al een andere actie met dezelfde naam.\nActienaam moet uniek zijn binnen dezelfde actiebron.", + "action-icon": "Pictogram", + "header-button": { + "button-settings": "Knopinstellingen", + "button-type": "Knoptype", + "button-type-basic": "Basis", + "button-type-raised": "Verhoogd", + "button-type-stroked": "Omtrokken", + "button-type-flat": "Vlak", + "button-type-icon": "Pictogram", + "button-type-mini-fab": "FAB", + "colors": "Kleuren", + "color": "Kleur", + "background": "Achtergrond", + "border": "Rand", + "advanced-button-style": "Geavanceerde knopstijl", + "button-style": "Knopstijl" + }, + "show-hide-action-using-function": "Toon/verberg actie met functie", + "show-action-function": "Toon actiefunctie", + "action-type": "Type", + "action-type-required": "Actietype is verplicht.", + "edit-action": "Bewerk actie", + "delete-action": "Verwijder actie", + "delete-action-title": "Verwijder widgetactie", + "delete-action-text": "Weet je zeker dat je de widgetactie met de naam '{{actionName}}' wilt verwijderen?", + "title-icon": "Titelpictogram", + "display-icon": "Toon titelpictogram", + "card-icon": "Kaartpictogram", + "icon": "Pictogram", + "icon-color": "Pictogramkleur", + "icon-size": "Pictogramgrootte", + "advanced-settings": "Geavanceerde instellingen", + "data-settings": "Gegevensinstellingen", + "limits": "Limieten", + "no-data-display-message": "Alternatief bericht voor \"Geen gegevens om weer te geven\"", + "data-page-size": "Maximaal aantal entiteiten per gegevensbron", + "settings-component-not-found": "Instellingencomponent niet gevonden voor selector '{{selector}}'", + "preview": "Voorbeeld", + "set": "Instellen", + "set-message": "Bericht instellen", + "advanced-title-style": "Geavanceerde titelstijl", + "card-style": "Kaartstijl", + "text": "Tekst", + "background": "Achtergrond", + "advanced-widget-style": "Geavanceerde widgetstijl", + "card-buttons": "Kaartknoppen", + "show-card-buttons": "Toon kaartknoppen", + "card-border-radius": "Randradius kaart", + "card-padding": "Kaartopvulling", + "card-appearance": "Kaartuiterlijk", + "color": "Kleur", + "tooltip": "Tooltip", + "units-required": "Eenheid is verplicht.", + "list-layout": "Lijstindeling", + "layout": "Indeling", + "resize-options": "Grootte-aanpassingsopties", + "resizable": "Grootte aanpasbaar", + "preserve-aspect-ratio": "Beeldverhouding behouden" + }, + "widget-type": { + "import": "Widgettype importeren", + "export": "Widgettype exporteren", + "export-failed-error": "Kan widget niet exporteren: {{error}}", + "widget-file": "Widgetbestand", + "invalid-widget-file-error": "Kan widget niet importeren: Ongeldige widgetgegevensstructuur." + }, + "markdown": { + "edit": "Bewerken", + "preview": "Voorbeeld", + "copy-code": "Klik om te kopiëren", + "copied": "Gekopieerd!" + }, + "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "De configuratie is afhankelijk van de Mobile app QR-code widget in de hoofdinstellingen van het platform", + "get-it-on-google-play": "Download in Google Play", + "download-on-the-app-store": "Download in de App Store" + }, + "action-button": { + "behavior": "Gedrag", + "on-click": "Bij klikken", + "on-click-hint": "Actie die wordt geactiveerd wanneer op de knop wordt geklikt", + "first-button-click": "Klik op eerste knop", + "first-button-click-hint": "Actie bij indrukken van de eerste knop.", + "second-button-click": "Klik op tweede knop", + "second-button-click-hint": "Actie bij indrukken van de tweede knop.", + "button-click-hint": "Actie bij klikken op de widget." + }, + "command-button": { + "behavior": "Gedrag", + "on-click": "Bij klikken", + "on-click-hint": "Actie uitgevoerd wanneer op de knop wordt geklikt." + }, + "power-button": { + "behavior": "Gedrag", + "power-on": "Stroom 'Aan'", + "power-on-hint": "Actie uitgevoerd om de component AAN te zetten.", + "power-off": "Stroom 'Uit'", + "power-off-hint": "Actie uitgevoerd om de component UIT te zetten.", + "on-label": "Aan", + "off-label": "Uit", + "layout": "Lay-out", + "layout-default": "Standaard", + "layout-simplified": "Vereenvoudigd", + "layout-outlined": "Omlijnd", + "layout-default-volume": "Standaard.Volume", + "layout-simplified-volume": "Vereenvoudigd.Volume", + "layout-outlined-volume": "Omlijnd.Volume", + "layout-default-icon": "Standaard.Icoon", + "layout-simplified-icon": "Vereenvoudigd.Icoon", + "layout-outlined-icon": "Omlijnd.Icoon", + "main": "Hoofd", + "background": "Achtergrond", + "button-icon-on": "Knop icoon 'Aan'", + "button-icon-off": "Knop icoon 'Uit'", + "power-on-colors": "Kleuren bij 'Aan'", + "power-off-colors": "Kleuren bij 'Uit'", + "disabled-colors": "Uitgeschakelde kleuren", + "button": "Knop" + }, + "toggle-button": { + "behavior": "Gedrag", + "checked": "Aangevinkt", + "unchecked": "Niet aangevinkt", + "check": "Aankruisen", + "check-hint": "Actie uitgevoerd om de component aan te vinken.", + "uncheck": "Uitschakelen", + "uncheck-hint": "Actie uitgevoerd om de component uit te schakelen.", + "auto-scale": "Automatisch schalen", + "horizontal-fill": "Horizontale vulling", + "vertical-fill": "Verticale vulling", + "button-appearance": "Knopweergave" + }, + "segmented-button": { + "layout": "Lay-out", + "layout-squared": "Vierkant", + "layout-rounded": "Afgerond", + "card-border": "Kaartomranding", + "button-appearance": "Knopweergave", + "first": "Eerste", + "second": "Tweede", + "color-styles": "Kleurenstijlen", + "selected": "Geselecteerd", + "unselected": "Niet geselecteerd" + }, + "button": { + "layout": "Lay-out", + "outlined": "Omlijnd", + "filled": "Ingevuld", + "underlined": "Onderlijnd", + "basic": "Basis", + "auto-scale": "Automatisch schalen", + "label": "Label", + "icon": "Icoon", + "border-radius": "Randradius", + "color-palette": "Kleurenpalet", + "main": "Hoofd", + "background": "Achtergrond", + "border": "Rand", + "custom-styles": "Aangepaste stijlen", + "clear-style": "Stijl wissen", + "shadow": "Schaduw", + "enabled": "Ingeschakeld", + "disabled": "Uitgeschakeld", + "preview": "Voorbeeld", + "copy-style-from": "Stijl kopiëren van" + }, + "value-stepper": { + "behavior": "Gedrag", + "simplified": "Vereenvoudigd", + "filled": "Ingevuld", + "outlined": "Omlijnd", + "volume": "Volume", + "initial-state": "Initiële status", + "initial-state-hint": "Actie om de initiële waarde op te halen.", + "disabled-state": "Uitgeschakelde status", + "disabled-state-hint": "Stel de voorwaarde in waaronder het onderdeel is uitgeschakeld.", + "right-button-click": "Klik op rechterknop", + "right-button-click-hint": "Actie bij indrukken van de rechterknop.", + "left-button-click": "Klik op linkerknop", + "left-button-click-hint": "Actie bij indrukken van de linkerknop.", + "auto-scale": "Automatisch schalen", + "value-range": "Bereik", + "min-range": "Min", + "max-range": "Max", + "value-increment-decrement-step": "Stapgrootte waarde verhogen/verlagen", + "value": "Waarde", + "value-box-background": "Achtergrond van waardeveld", + "border": "Rand", + "button-appearance": "Knopweergave", + "left": "Links", + "right": "Rechts", + "left-button": "Linkerknop", + "right-button": "Rechterknop", + "icon": "Icoon", + "color-palette": "Kleurenpalet", + "main": "Hoofd", + "background": "Achtergrond", + "button-icon-on": "Knop icoon 'Aan'", + "button-on-colors": "Kleuren bij 'Aan'", + "disabled-colors": "Uitgeschakelde kleuren" + }, + "button-state": { + "activated-state": "Geactiveerde status", + "activated-state-hint": "Stel de voorwaarde in waaronder de knop actief is.", + "disabled-state": "Uitgeschakelde status", + "disabled-state-hint": "Stel de voorwaarde in waaronder de knop is uitgeschakeld.", + "selected-state": "Knop geselecteerd", + "selected-state-hint": "Stel de voorwaarde in waaronder de knop is geselecteerd.", + "enabled": "Ingeschakeld", + "hovered": "Gehoverd", + "pressed": "Ingedrukt", + "activated": "Geactiveerd", + "disabled": "Uitgeschakeld", + "initial": "Eerste knop", + "first": "Eerste", + "second": "Tweede" + }, + "background": { + "background": "Achtergrond", + "background-settings": "Achtergrondinstellingen", + "background-type-image": "Afbeelding", + "background-type-color": "Kleur", + "image-url": "Afbeeldings-URL", + "overlay": "Overlay", + "enable-overlay": "Overlay inschakelen", + "blur": "Vervagen", + "preview": "Voorbeeld" + }, + "bar-chart": { + "bar-appearance": "Staafweergave", + "label-on-bar": "Label op staaf", + "value-on-bar": "Waarde op staaf", + "bar-chart-style": "Stijl van staafdiagram", + "bar-axis": "Staafas" + }, + "polar-area-chart": { + "polar-axis": "Polaire as", + "start-angle": "Starthoek", + "polar-area-chart-style": "Stijl van polair gebieddiagram" + }, + "battery-level": { + "layout": "Lay-out", + "layout-vertical-solid": "Verticaal. Solide", + "layout-horizontal-solid": "Horizontaal. Solide", + "layout-vertical-divided": "Verticaal. Verdeeld", + "layout-horizontal-divided": "Horizontaal. Verdeeld", + "icon": "Icoon", + "value": "Waarde", + "auto-scale": "Automatisch schalen", + "battery-level-color": "Kleur batterijniveau", + "battery-shape-color": "Kleur batterijvorm", + "battery-level-card-style": "Stijl batterijkaart", + "sections-count": "Aantal secties" + }, + "signal-strength": { + "value": "Waarde", + "last-update": "Laatste update", + "no-signal": "Geen signaal", + "layout": "Lay-out", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "Mobiele balk", + "icon": "Icoon", + "date": "Datum", + "active-bars-color": "Kleur actieve signaalbalken", + "inactive-bars-color": "Kleur inactieve signaalbalken", + "signal-strength-card-style": "Stijl signaalsterktekaart", + "no-signal-rssi-value": "\"Geen signaal\" RSSI-waarde" + }, + "status-widget": { + "behavior": "Gedrag", + "layout": "Lay-out", + "layout-default": "Standaard", + "layout-center": "Midden", + "layout-icon": "Icoon", + "on": "Aan", + "off": "Uit", + "label": "Label", + "status": "Status", + "icon": "Icoon", + "color-palette": "Kleurenpalet", + "disabled-color-palette": "Kleurenpalet uitgeschakeld", + "primary": "Primair", + "primary-color-hint": "Kleur van icoon en label", + "secondary": "Secundair", + "secondary-color-hint": "Kleur van status", + "background": "Achtergrond" + }, + "chart": { + "common-settings": "Algemene instellingen", + "enable-stacking-mode": "Stapelmethode inschakelen", + "selection": "Tijdselectie", + "enable-selection-mode": "Selectiemodus inschakelen", + "line-shadow-size": "Lijnschaduw grootte", + "display-smooth-lines": "Glad (gebogen) lijnen weergeven", + "default-bar-width": "Standaard staafbreedte voor niet-geaggregeerde gegevens (milliseconden)", + "bar-alignment": "Uitlijning van staaf", + "bar-alignment-left": "Links", + "bar-alignment-right": "Rechts", + "bar-alignment-center": "Midden", + "default-font": "Standaardlettertype", + "default-font-size": "Standaard lettergrootte", + "default-font-color": "Standaard letterkleur", + "thresholds-line-width": "Standaard lijndikte voor alle drempels", + "tooltip-settings": "Tooltip-instellingen", + "tooltip": "Tooltip", + "show-tooltip": "Tooltip weergeven", + "hover-individual-points": "Individuele punten aanwijzen", + "show-cumulative-values": "Cumulatieve waarden tonen in stapelmodus", + "hide-zero-false-values": "Verberg nul/onwaar waarden in tooltip", + "tooltip-value-format-function": "Waardeformaatfunctie voor tooltip", + "grid-settings": "Rasterinstellingen", + "show-vertical-lines": "Verticale lijnen weergeven", + "show-horizontal-lines": "Horizontale lijnen weergeven", + "grid-outline-border-width": "Rasteromtrek/lijndikte (px)", + "primary-color": "Primaire kleur", + "background-color": "Achtergrondkleur", + "ticks-color": "Kleur van de ticks", + "xaxis-settings": "X-as instellingen", + "axis-title": "As titel", + "xaxis-tick-labels-settings": "Instellingen voor X-as ticks", + "show-tick-labels": "Toon ticks-labels", + "yaxis-settings": "Y-as instellingen", + "min-scale-value": "Minimale schaalwaarde", + "max-scale-value": "Maximale schaalwaarde", + "yaxis-tick-labels-settings": "Instellingen voor Y-as ticks", + "tick-step-size": "Stapsgewijs interval tussen ticks", + "number-of-decimals": "Aantal decimalen om weer te geven", + "ticks-formatter-function": "Ticks formatter functie", + "comparison-settings": "Vergelijkingsinstellingen", + "enable-comparison": "Vergelijking inschakelen", + "time-for-comparison": "Vergelijkingsperiode", + "time-for-comparison-previous-interval": "Vorige interval (standaard)", + "time-for-comparison-days": "Dag geleden", + "time-for-comparison-weeks": "Week geleden", + "time-for-comparison-months": "Maand geleden", + "time-for-comparison-years": "Jaar geleden", + "time-for-comparison-custom-interval": "Aangepaste interval", + "custom-interval-value": "Aangepaste intervalwaarde (ms)", + "comparison-x-axis-settings": "Vergelijking X-as instellingen", + "axis-position": "As positie", + "axis-position-top": "Boven (standaard)", + "axis-position-bottom": "Onder", + "custom-legend-settings": "Aangepaste legenda instellingen", + "enable-custom-legend": "Aangepaste legenda inschakelen (gebruik van attribuut/tijdreekswaarden in sleutel labels)", + "key-name": "Sleutelnaam", + "key-name-required": "Sleutelnaam is vereist", + "key-type": "Sleuteltype", + "key-type-attribute": "Attribuut", + "key-type-timeseries": "Tijdreeks", + "label-keys-list": "Sleutellijst om in labels te gebruiken", + "no-label-keys": "Geen sleutels geconfigureerd", + "add-label-key": "Nieuwe sleutel toevoegen", + "line-width": "Lijndikte", + "color": "Kleur", + "data-is-hidden-by-default": "Gegevens standaard verborgen", + "disable-data-hiding": "Gegevens verbergen uitschakelen", + "remove-from-legend": "Sleutel uit legenda verwijderen", + "exclude-from-stacking": "Uitsluiten van stapeling (beschikbaar in \"Stapelmodus\")", + "line-settings": "Lijninstellingen", + "show-line": "Lijn weergeven", + "fill-line": "Lijn invullen", + "fill-line-opacity": "Invuldoorzichtigheid", + "points-settings": "Punteninstellingen", + "show-points": "Punten weergeven", + "points-line-width": "Lijndikte van punten", + "points-radius": "Straal van punten", + "point-shape": "Vorm van punt", + "point-shape-circle": "Cirkel", + "point-shape-cross": "Kruis", + "point-shape-diamond": "Ruit", + "point-shape-square": "Vierkant", + "point-shape-triangle": "Driehoek", + "point-shape-custom": "Aangepaste functie", + "point-shape-draw-function": "Tekeningfunctie voor puntvorm", + "show-separate-axis": "Aparte as weergeven", + "axis-position-left": "Links", + "axis-position-right": "Rechts", + "thresholds": "Drempels", + "no-thresholds": "Geen drempels geconfigureerd", + "add-threshold": "Drempel toevoegen", + "show-values-for-comparison": "Historische waarden weergeven ter vergelijking", + "comparison-values-label": "Label voor historische waarden", + "comparison-line-color": "Vergelijkingslijnkleur", + "threshold-settings": "Drempelinstellingen", + "use-as-threshold": "Gebruik sleutelwaarde als drempel", + "threshold-line-width": "Lijndikte van drempel", + "threshold-color": "Drempelkleur", + "common-pie-settings": "Algemene cirkeldiagraminstellingen", + "radius": "Straal", + "inner-radius": "Binnenstraal", + "tilt": "Kantel", + "common-pie-settings-range-error": "Waarde moet tussen 0 en 1 liggen", + "stroke-settings": "Lijninstellingen", + "width-pixels": "Breedte (pixels)", + "show-labels": "Labels weergeven", + "animation-settings": "Animatie-instellingen", + "animated-pie": "Animatie inschakelen voor cirkel (experimenteel)", + "border-settings": "Randinstellingen", + "border-width": "Randbreedte", + "border-color": "Randkleur", + "legend-settings": "Legenda-instellingen", + "display-legend": "Legenda weergeven", + "labels-font-color": "Kleur van lettertype voor labels", + "series": "Reeksen", + "add-series": "Reeks toevoegen", + "series-settings": "Reeksinstellingen", + "remove-series": "Reeks verwijderen", + "no-series": "Geen reeksen geconfigureerd", + "no-series-error": "Minstens één reeks moet worden opgegeven", + "chart-appearance": "Grafiekweergave", + "vertical-grid-lines": "Verticale rasterlijnen", + "horizontal-grid-lines": "Horizontale rasterlijnen", + "chart-background": "Achtergrond van de grafiek", + "grid-lines-color": "Rasterlijntjes kleur", + "border": "Rand", + "axis": "As", + "vertical-axis": "Verticale as", + "ticks": "Streepjes", + "horizontal-axis": "Horizontale as", + "shape-empty-circle": "Lege cirkel", + "shape-circle": "Cirkel", + "shape-rect": "Rechthoek", + "shape-round-rect": "Afgeronde rechthoek", + "shape-triangle": "Driehoek", + "shape-diamond": "Ruit", + "shape-pin": "Pin", + "shape-arrow": "Pijl", + "shape-none": "Geen", + "line-type-solid": "Vol", + "line-type-dashed": "Gestreept", + "line-type-dotted": "Gestippeld", + "label-position-top": "Boven", + "label-position-bottom": "Onder", + "label-position-outside": "Buiten", + "label-position-inside": "Binnen", + "fill": "Vulling", + "fill-type-none": "Geen", + "fill-type-solid": "Vol", + "fill-type-opacity": "Doorzichtigheid", + "fill-type-gradient": "Verloop", + "background": "Achtergrond", + "opacity": "Transparantie", + "gradient-stops": "Verloop stops", + "gradient-start": "start", + "gradient-end": "einde", + "animation": { + "animation": "Animatie", + "animation-threshold": "Animatiedrempel", + "animation-duration": "Duur van animatie", + "animation-easing": "Animatieverloop", + "animation-delay": "Animatievertraging", + "update-animation-duration": "Duur update-animatie", + "update-animation-easing": "Update-animatieverloop", + "update-animation-delay": "Vertraging update-animatie" + }, + "chart-axis": { + "scale": "Schaal", + "scale-min": "min", + "scale-max": "max", + "scale-auto": "Automatisch" + }, + "bar": { + "show-border": "Toon rand", + "border-width": "Randbreedte", + "border-radius": "Randradius", + "bar-width": "Staafbreedte", + "label": "Label", + "label-hint": "Label tonen boven de staaf.", + "series-label-hint": "Label met waarde tonen boven de staaf.", + "label-background": "Achtergrond label" + } + }, + "color": { + "color-settings": "Kleurinstellingen", + "color-type-constant": "Constante", + "color-type-gradient": "Verloop", + "color-type-range": "Bereik", + "color-type-function": "Functie", + "color": "Kleur", + "value-range": "Waardenbereik", + "from": "Van", + "to": "Tot", + "color-function": "Kleurfunctie", + "copy-color-settings-from": "Kleurinstellingen kopiëren van", + "copy-from": "Kopiëren van", + "settings-type": "Instellingstype", + "basic-mode": "Basis", + "advanced-mode": "Geavanceerd", + "entity-alias": "Entiteit-alias", + "entity-attribute": "Entiteitattribuut", + "gradient-color": "Verloopkleur", + "gradient-color-min": "Kleur", + "gradient-start": "Startkleur verloop", + "gradient-start-min": "Start", + "gradient-end": "Eindkleur verloop", + "gradient-end-min": "Einde", + "start-value": "Startwaarde", + "end-value": "Eindwaarde", + "gradient-type": "Verlooptype" + }, + "dashboard-state": { + "dashboard-state-settings": "Dashboardstatusinstellingen", + "dashboard-state": "Dashboardstatus-id", + "autofill-state-layout": "Statuslayouthoogte automatisch invullen standaard", + "default-margin": "Standaard marge voor widgets", + "default-background-color": "Standaard achtergrondkleur", + "sync-parent-state-params": "Synchroniseer statusparameters met hoofd-dashboard" + }, + "date-range-navigator": { + "date-range-picker-settings": "Instellingen voor datumbereikselectie", + "hide-date-range-picker": "Verberg datumbereikselector", + "picker-one-panel": "Datumbereikselector met één paneel", + "picker-auto-confirm": "Automatische bevestiging bij datumbereikselector", + "picker-show-template": "Toon sjabloon bij datumbereikselector", + "first-day-of-week": "Eerste dag van de week", + "interval-settings": "Intervalinstellingen", + "hide-interval": "Verberg interval", + "initial-interval": "Initiële interval", + "interval-hour": "Uur", + "interval-day": "Dag", + "interval-week": "Week", + "interval-two-weeks": "2 weken", + "interval-month": "Maand", + "interval-three-months": "3 maanden", + "interval-six-months": "6 maanden", + "step-settings": "Stapinstellingen", + "hide-step-size": "Verberg stapgrootte", + "initial-step-size": "Initiële stapgrootte", + "hide-labels": "Verberg labels", + "use-session-storage": "Gebruik sessieopslag", + "localizationMap": { + "Sun": "Zo", + "Mon": "Ma", + "Tue": "Di", + "Wed": "Wo", + "Thu": "Do", + "Fri": "Vr", + "Sat": "Za", + "Jan": "Jan", + "Feb": "Feb", + "Mar": "Mrt", + "Apr": "Apr", + "May": "Mei", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Aug", + "Sep": "Sep", + "Oct": "Okt", + "Nov": "Nov", + "Dec": "Dec", + "January": "Januari", + "February": "Februari", + "March": "Maart", + "April": "April", + "June": "Juni", + "July": "Juli", + "August": "Augustus", + "September": "September", + "October": "Oktober", + "November": "November", + "December": "December", + "Custom Date Range": "Aangepast datumbereik", + "Date Range Template": "Sjabloon datumbereik", + "Today": "Vandaag", + "Yesterday": "Gisteren", + "This Week": "Deze week", + "Last Week": "Vorige week", + "This Month": "Deze maand", + "Last Month": "Vorige maand", + "Year": "Jaar", + "This Year": "Dit jaar", + "Last Year": "Vorig jaar", + "Date picker": "Datumkiezer", + "Hour": "Uur", + "Day": "Dag", + "Week": "Week", + "2 weeks": "2 weken", + "Month": "Maand", + "3 months": "3 maanden", + "6 months": "6 maanden", + "Custom interval": "Aangepast interval", + "Interval": "Interval", + "Step size": "Stapgrootte", + "Ok": "Oké" + } + }, + "doughnut": { + "doughnut-appearance": "Donut-uiterlijk", + "layout": "Lay-out", + "layout-default": "Standaard", + "layout-with-total": "Met totaal", + "central-total-value": "Centrale totaalwaarde", + "doughnut-card-style": "Donut-kaartstijl" + }, + "entities-hierarchy": { + "hierarchy-data-settings": "Hiërarchische gegevensinstellingen", + "relations-query-function": "Functie voor relaties van knoop", + "has-children-function": "Functie of knoop kinderen heeft", + "node-state-settings": "Instellingen knoopstatus", + "node-opened-function": "Functie voor standaard geopende knoop", + "node-disabled-function": "Functie voor uitgeschakelde knoop", + "display-settings": "Weergave-instellingen", + "node-icon-function": "Functie voor knoopicoon", + "node-text-function": "Functie voor knooptekst", + "sort-settings": "Sorteerinstellingen", + "nodes-sort-function": "Functie voor sorteren van knopen" + }, + "edge": { + "display-default-title": "Standaardtitel weergeven" + }, + "gateway": { + "general-settings": "Algemene instellingen", + "widget-title": "Widgettitel", + "default-archive-file-name": "Standaard naam archiefbestand", + "device-type-for-new-gateway": "Type apparaat voor nieuwe gateway", + "messages-settings": "Berichtinstellingen", + "save-config-success-message": "Bericht bij succesvol opslaan van gatewayconfiguratie", + "device-name-exists-message": "Bericht wanneer apparaatnaam al bestaat", + "gateway-title": "Gatewayformulier", + "read-only": "Alleen-lezen", + "events-title": "Titel gatewaygebeurtenissenformulier", + "events-filter": "Filter gebeurtenissen", + "event-key-contains": "Gebeurtenissleutel bevat...", + "show-connector": "Tonen voor de connector", + "connector-state-param-key": "Sleutel statusparameter connector", + "message": "Bericht", + "level": "Niveau", + "created-time": "Aangemaakt op" + }, + "gauge": { + "default-color": "Standaardkleur", + "radial-gauge-settings": "Instellingen radiale meter", + "ticks-settings": "Tikken instellingen", + "min-value": "Minimumwaarde", + "max-value": "Maximumwaarde", + "min-value-short": "min", + "max-value-short": "max", + "start-ticks-angle": "Starthoek tikken", + "ticks-angle": "Hoek tikken", + "major-ticks": "Grote tikken", + "major-ticks-count": "Aantal grote tikken", + "major-ticks-color": "Kleur grote tikken", + "minor-ticks": "Kleine tikken", + "minor-ticks-count": "Aantal kleine tikken", + "minor-ticks-color": "Kleur kleine tikken", + "tick-numbers-font": "Lettertype tiknummers", + "unit-title-settings": "Instellingen eenheidstitel", + "show-unit-title": "Titel eenheden", + "unit-title": "Titel eenheid", + "title-font": "Lettertype titel", + "units-settings": "Instellingen eenheden", + "units-font": "Lettertype eenheden", + "value-box-settings": "Instellingen waardeveld", + "show-value-box": "Waardeveld weergeven", + "value-box": "Waardeveld", + "value-int": "Aantal cijfers voor geheel getal", + "value-text": "Waarde tekst", + "value-text-shadow": "Schaduw tekst waarde", + "value-font": "Lettertype waarde", + "rect-stroke-color-start": "Kaderkleur - start verloop", + "rect-stroke-color-end": "Kaderkleur - eind verloop", + "background-color": "Achtergrondkleur", + "shadow-color": "Schaduwkleur", + "value-box-rect-stroke-color": "Kaderkleur waardeveld", + "value-box-rect-stroke-color-end": "Kaderkleur waardeveld - eind verloop", + "value-box-background-color": "Achtergrondkleur waardeveld", + "value-box-shadow-color": "Schaduwkleur waardeveld", + "plate-settings": "Instellingen plaat", + "show-plate-border": "Rand plaat", + "plate-color": "Plaatkleur", + "needle-settings": "Instellingen naald", + "needle-circle-size": "Grootte naaldcirkel", + "needle-color": "Kleur naald", + "needle-color-start": "Kleur naald - start verloop", + "needle-color-end": "Kleur naald - eind verloop", + "needle-color-shadow-up": "Bovenste schaduwkleur naald", + "needle-color-shadow-down": "Onderschaduw naald", + "highlights-settings": "Markeringen instellingen", + "highlights-width": "Breedte markeringen", + "highlights": "Markeringen", + "highlight-from": "Van", + "highlight-to": "Tot", + "highlight-color": "Kleur", + "no-highlights": "Geen markeringen geconfigureerd", + "add-highlight": "Markering toevoegen", + "animation-settings": "Animatie-instellingen", + "enable-animation": "Animatie", + "animation-duration-rule": "Duur en regel van animatie", + "animation-duration": "Animatieduur", + "animation-rule": "Animatieregel", + "animation-linear": "Lineair", + "animation-quad": "Kwadratisch", + "animation-quint": "Quint", + "animation-cycle": "Cyclus", + "animation-bounce": "Bounce", + "animation-elastic": "Elastisch", + "animation-dequad": "Dequad", + "animation-dequint": "Dequint", + "animation-decycle": "Decyclus", + "animation-debounce": "Debounce", + "animation-delastic": "Delastisch", + "linear-gauge-settings": "Lineaire meter instellingen", + "bar-stroke": "Staafomtrek", + "bar-stroke-width": "Staafomtrek breedte", + "bar-stroke-color": "Staafomtrek kleur", + "bar-background-color": "Achtergrondkleur staaf - start verloop", + "bar-background-color-end": "Achtergrondkleur staaf - eind verloop", + "progress-bar-color": "Kleur voortgangsbalk", + "progress-bar": "Voortgangsbalk", + "progress-bar-color-start": "Voortgangsbalk kleur - start verloop", + "progress-bar-color-end": "Voortgangsbalk kleur - eind verloop", + "major-ticks-names": "Namen van hoofdverdelingen", + "show-stroke-ticks": "Toon omtrekverdelingen", + "major-ticks-font": "Lettertype hoofdverdelingen", + "border-color": "Randkleur", + "border-width": "Randdikte", + "needle-circle": "Naaldcirkel", + "needle-circle-color": "Kleur naaldcirkel", + "animation-target": "Animatiedoel", + "animation-target-needle": "Naald", + "animation-target-plate": "Plaat", + "common-settings": "Algemene meter instellingen", + "gauge-type": "Type meter", + "gauge-type-arc": "Boog", + "gauge-type-donut": "Donut", + "gauge-type-horizontal-bar": "Horizontale balk", + "gauge-type-vertical-bar": "Verticale balk", + "donut-start-angle": "Starthoek (graden)", + "bar-settings": "Balkinstellingen", + "relative-bar-width": "Relatieve balkbreedte", + "neon-glow-brightness": "Neon gloeieffect helderheid (0-100)", + "neon-glow-brightness-hint": "0 - effect uitschakelen", + "stripes-thickness": "Streepdikte", + "stripes-thickness-hint": "0 - geen strepen", + "rounded-line-cap": "Afgeronde lijnkop", + "bar-color-settings": "Instellingen balkkleur", + "use-precise-level-color-values": "Gebruik precieze kleurwaarden", + "bar-colors": "Balkkleuren, van laag naar hoog", + "color": "Kleur", + "no-bar-colors": "Geen balkkleuren geconfigureerd", + "add-bar-color": "Balkkleur toevoegen", + "from": "Van", + "to": "Tot", + "fixed-level-colors": "Balkkleuren op basis van grenswaarden", + "gauge-title-settings": "Instellingen metertitel", + "show-gauge-title": "Toon metertitel", + "gauge-title": "Metertitel", + "gauge-title-font": "Lettertype metertitel", + "unit-title-and-timestamp-settings": "Instellingen eenheidstitel en tijdstempel", + "show-timestamp": "Tijdstempel", + "timestamp-format": "Tijdstempelformaat", + "label-font": "Lettertype label onder waarde", + "value-settings": "Waarde instellingen", + "show-value": "Toon waardetekst", + "min-max-settings": "Instellingen minimum/maximum labels", + "show-min-max": "Toon min- en max-waarden", + "min-max-font": "Lettertype voor min- en max-labels", + "show-ticks": "Toon verdelingen", + "tick-width": "Breedte verdeling", + "tick-color": "Kleur verdeling", + "tick-values": "Waarden van verdelingen", + "no-tick-values": "Geen waarden voor verdelingen geconfigureerd", + "add-tick-value": "Waarde voor verdeling toevoegen", + "gauge-appearance": "Uiterlijk van de meter", + "units-title": "Titel van eenheden", + "value": "Waarde", + "ticks": "Verdelingen", + "arrow-and-scale-color": "Standaardkleur pijl en schaal", + "scale-settings": "Schaalinstellingen", + "scale": "Schaal", + "scale-color": "Schaalkleuren", + "compass-appearance": "Uiterlijk kompas", + "label": "Label", + "labels": "Labels", + "label-style": "Stijl van label", + "simple-gauge-type": "Type", + "gauge-bar-background": "Achtergrond balk van meter", + "bar-color": "Balkkleur", + "min-and-max-value": "Min- en maxwaarde", + "min-and-max-label": "Label min- en max", + "font": "Lettertype", + "tick-width-and-color": "Breedte en kleur verdeling", + "min-max-validation-text": "Maxwaarde moet groter zijn dan minwaarde" + }, + "gpio": { + "pin": "Pin", + "label": "Label", + "row": "Rij", + "column": "Kolom", + "color": "Kleur", + "panel-settings": "Paneelinstellingen", + "background-color": "Achtergrondkleur", + "gpio-switches": "GPIO-schakelaars", + "no-gpio-switches": "Geen GPIO-schakelaars geconfigureerd", + "add-gpio-switch": "GPIO-schakelaar toevoegen", + "gpio-status-request": "GPIO-statusaanvraag", + "method-name": "Methodenaam", + "method-body": "Methode body", + "gpio-status-change-request": "GPIO-statuswijzigingsaanvraag", + "parse-gpio-status-function": "GPIO-statusfunctie parseren", + "gpio-leds": "GPIO LEDs", + "no-gpio-leds": "Geen GPIO LEDs geconfigureerd", + "add-gpio-led": "GPIO LED toevoegen" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, + "input-widgets": { + "attribute-not-allowed": "Attribuutparameter mag niet worden gebruikt in deze widget", + "blocked-location": "Locatie is geblokkeerd in je browser", + "claim-device": "Apparaat claimen", + "claim-failed": "Apparaat claimen is mislukt!", + "claim-not-found": "Apparaat niet gevonden!", + "claim-successful": "Apparaat succesvol geclaimd!", + "date": "Datum", + "device-name": "Apparaatnaam", + "device-name-required": "Apparaatnaam is verplicht", + "discard-changes": "Wijzigingen negeren", + "entity-attribute-required": "Entiteitattribuut is verplicht", + "entity-coordinate-required": "Beide velden, breedte- en lengtegraad, zijn verplicht", + "entity-timeseries-required": "Entiteit tijdreeks is verplicht", + "get-location": "Huidige locatie ophalen", + "invalid-date": "Ongeldige datum", + "latitude": "Breedtegraad", + "longitude": "Lengtegraad", + "min-value-error": "Minimale waarde is {{value}}", + "max-value-error": "Maximale waarde is {{value}}", + "not-allowed-entity": "Geselecteerde entiteit kan geen gedeelde attributen hebben", + "no-attribute-selected": "Geen attribuut geselecteerd", + "no-datakey-selected": "Geen datatoets geselecteerd", + "no-coordinate-specified": "Datatoets voor breedte-/lengtegraad niet opgegeven", + "no-entity-selected": "Geen entiteit geselecteerd", + "no-image": "Geen afbeelding", + "no-support-geolocation": "Je browser ondersteunt geen geolocatie", + "no-support-web-camera": "Je browser ondersteunt geen camera’s", + "enable-https-use-widget": "Activeer HTTPS om deze widget te gebruiken", + "no-found-your-camera": "Kan je camera niet vinden", + "no-permission-camera": "Toestemming geweigerd of geen toegang tot camera", + "no-timeseries-selected": "Geen tijdreeks geselecteerd", + "secret-key": "Geheime sleutel", + "secret-key-required": "Geheime sleutel is verplicht", + "switch-attribute-value": "Schakel attribuutwaarde van entiteit", + "switch-camera": "Schakel camera", + "switch-timeseries-value": "Schakel tijdreekswaarde van entiteit", + "take-photo": "Foto maken", + "time": "Tijd", + "timeseries-not-allowed": "Tijdreeksparameter mag niet worden gebruikt in deze widget", + "update-failed": "Update mislukt", + "update-successful": "Update succesvol", + "update-attribute": "Attribuut bijwerken", + "update-timeseries": "Tijdreeks bijwerken", + "value": "Waarde", + "general-settings": "Algemene instellingen", + "widget-title": "Widgettitel", + "claim-button-label": "Label claimknop", + "show-secret-key-field": "Toon invoerveld 'Geheime sleutel'", + "labels-settings": "Labelinstellingen", + "show-labels": "Toon labels", + "device-name-label": "Label voor apparaatnaamveld", + "secret-key-label": "Label voor geheime sleutelveld", + "messages-settings": "Berichteninstellingen", + "claim-device-success-message": "Bericht bij succesvol apparaat claimen", + "claim-device-not-found-message": "Bericht wanneer apparaat niet gevonden is", + "claim-device-failed-message": "Bericht bij mislukte apparaatclaim", + "claim-device-name-required-message": "Foutbericht 'Apparaatnaam verplicht'", + "claim-device-secret-key-required-message": "Foutbericht 'Geheime sleutel verplicht'", + "show-label": "Toon label", + "label": "Label", + "required": "Verplicht", + "required-error-message": "Foutmelding 'Verplicht'", + "show-result-message": "Toon resultaatbericht", + "integer-field-settings": "Instellingen voor geheel getal", + "min-value": "Minimale waarde", + "max-value": "Maximale waarde", + "double-field-settings": "Instellingen voor decimaal getal", + "text-field-settings": "Instellingen voor tekstveld", + "min-length": "Minimale lengte", + "max-length": "Maximale lengte", + "checkbox-settings": "Instellingen voor selectievakje", + "true-label": "Label indien aangevinkt", + "false-label": "Label indien niet aangevinkt", + "image-input-settings": "Instellingen voor afbeeldingsinvoer", + "display-preview": "Toon voorbeeld", + "display-clear-button": "Toon wissen-knop", + "display-apply-button": "Toon toepassen-knop", + "display-discard-button": "Toon annuleren-knop", + "datetime-field-settings": "Instellingen voor datum/tijd", + "display-time-input": "Toon tijdinvoer", + "latitude-key-name": "Naam van breedtegraadsleutel", + "longitude-key-name": "Naam van lengtegraadsleutel", + "show-get-location-button": "Toon knop 'Huidige locatie ophalen'", + "use-high-accuracy": "Gebruik hoge nauwkeurigheid", + "location-fields-settings": "Instellingen voor locatievelden", + "latitude-label": "Label voor breedtegraad", + "longitude-label": "Label voor lengtegraad", + "input-fields-alignment": "Uitlijning invoervelden", + "input-fields-alignment-column": "Kolom (standaard)", + "input-fields-alignment-row": "Rij", + "layout": "Indeling", + "row-gap": "Afstand tussen rijen (pixels)", + "column-gap": "Afstand tussen kolommen (pixels)", + "latitude-field-required": "Breedtegraad is verplicht", + "longitude-field-required": "Lengtegraad is verplicht", + "attribute-settings": "Attribuutinstellingen", + "widget-mode": "Widgetmodus", + "widget-mode-update-attribute": "Attribuut bijwerken", + "widget-mode-update-timeseries": "Tijdreeks bijwerken", + "attribute-scope": "Attribuutscope", + "attribute-scope-server": "Serverattribuut", + "attribute-scope-shared": "Gedeeld attribuut", + "value-required": "Waarde is verplicht", + "image-settings": "Afbeeldingsinstellingen", + "image-format": "Afbeeldingsformaat", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "Afbeeldingskwaliteit voor verliescompressie (zoals jpeg/webp)", + "max-image-width": "Maximale afbeeldingsbreedte", + "max-image-height": "Maximale afbeeldingshoogte", + "action-buttons": "Actieknoppen", + "show-action-buttons": "Toon actieknoppen", + "update-all-values": "Alle waarden bijwerken, niet alleen gewijzigde", + "save-button-label": "Label voor 'OPSLAAN'-knop", + "reset-button-label": "Label voor 'ONGEDAAAN MAKEN'-knop", + "group-settings": "Groepsinstellingen", + "show-group-title": "Toon titel voor groep van velden", + "group-title": "Groepstitel", + "fields-alignment": "Velduitlijning", + "fields-alignment-row": "Rij (standaard)", + "fields-alignment-column": "Kolom", + "fields-in-row": "Aantal velden per rij", + "option-value": "Waarde (schrijf 'null' voor lege optie)", + "option-label": "Label", + "hide-input-field": "Verberg invoerveld", + "datakey-type": "Type datatoets", + "datakey-type-server": "Serverattribuut (standaard)", + "datakey-type-shared": "Gedeeld attribuut", + "datakey-type-timeseries": "Tijdreeks", + "datakey-value-type": "Datatoets waarde type", + "datakey-value-type-string": "Tekenreeks", + "datakey-value-type-double": "Decimaal", + "datakey-value-type-integer": "Geheel getal", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "Boolean (Selectievakje)", + "datakey-value-type-boolean-switch": "Boolean (Schakelaar)", + "datakey-value-type-date-time": "Datum & Tijd", + "datakey-value-type-date": "Datum", + "datakey-value-type-time": "Tijd", + "datakey-value-type-select": "Keuzelijst", + "datakey-value-type-radio": "Radio", + "datakey-value-type-color": "Kleur", + "value-is-required": "Waarde is verplicht", + "ability-to-edit-attribute": "Mogelijkheid om attribuut te bewerken", + "ability-to-edit-attribute-editable": "Bewerkbaar (standaard)", + "ability-to-edit-attribute-disabled": "Uitgeschakeld", + "ability-to-edit-attribute-readonly": "Alleen-lezen", + "disable-on-datakey-name": "Uitschakelen bij false-waarde van andere datatoets (geef naam op)", + "field-appearance": "Veldweergave", + "appearance-fill": "Opvulling", + "appearance-outline": "Omlijning", + "subscript-sizing": "Subscript grootte", + "subscript-sizing-fixed": "Vast", + "subscript-sizing-dynamic": "Dynamisch", + "slide-toggle-settings": "Schakelaarinstellingen", + "slide-toggle-label-position": "Labelpositie van de schakelaar", + "slide-toggle-label-position-after": "Na", + "slide-toggle-label-position-before": "Voor", + "select-options": "Selecteer opties", + "no-select-options": "Geen selecteeropties geconfigureerd", + "add-select-option": "Voeg selecteeroptie toe", + "numeric-field-settings": "Instellingen voor numeriek veld", + "step-interval": "Stapinterval tussen waarden", + "error-messages": "Foutmeldingen", + "min-value-error-message": "'Minimale waarde' foutmelding", + "max-value-error-message": "'Maximale waarde' foutmelding", + "invalid-date-error-message": "'Ongeldige datum' foutmelding", + "invalid-JSON-error-message": "'Ongeldige JSON' foutmelding", + "icon-settings": "Pictograminstellingen", + "dialog-editor-settings": "Instellingen voor dialoogbewerker", + "use-custom-icon": "Gebruik aangepast pictogram", + "input-cell-icon": "Pictogram vóór invoerveld", + "value-conversion-settings": "Waardeconversie-instellingen", + "get-value-settings": "Waarde ophalen-instellingen", + "use-get-value-function": "Gebruik getValue-functie", + "get-value-function": "getValue-functie", + "set-value-settings": "Waarde instellen-instellingen", + "use-set-value-function": "Gebruik setValue-functie", + "set-value-function": "setValue-functie", + "json-invalid": "JSON-waarde heeft een ongeldig formaat", + "title": "Titel", + "cancel-button-label": "Label voor 'Annuleren'-knop", + "radio-button-settings": "Instellingen voor keuzerondje", + "color": "Kleur", + "columns": "Kolommen", + "radio-options": "Radio-opties", + "no-radio-options": "Geen radio-opties geconfigureerd", + "add-radio-option": "Voeg radio-optie toe", + "radio-label-position": "Labelpositie", + "radio-label-position-before": "Voor", + "radio-label-position-after": "Na" + }, + "invalid-qr-code-text": "Ongeldige invoertekst voor QR-code. Invoer moet van het type string zijn", + "qr-code": { + "use-qr-code-text-function": "Gebruik QR-code tekstfunctie", + "qr-code-text-pattern": "QR-code tekstpatroon (bijv. '${entityName} | ${keyName} - some text.')", + "qr-code-text-pattern-hint": "QR-code tekstpatroon gebruikt de waarde van de eerste gevonden sleutel in de entiteiten van de entiteitsalias.", + "qr-code-text-pattern-required": "QR-code tekstpatroon is verplicht.", + "qr-code-text-function": "QR-code tekstfunctie" + }, + "label-widget": { + "label-pattern": "Patroon", + "label-pattern-hint": "Tip: bijvoorbeeld 'Tekst ${keyName} eenheden.' of ${#<key index>} eenheden'", + "label-pattern-required": "Patroon is verplicht", + "label-position": "Positie (percentage t.o.v. achtergrond)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Achtergrondkleur", + "font-settings": "Lettertype-instellingen", + "background-image": "Achtergrondafbeelding", + "labels": "Labels", + "no-labels": "Geen labels geconfigureerd", + "add-label": "Label toevoegen" + }, + "navigation": { + "title": "Titel", + "navigation-path": "Navigatiepad", + "filter-type": "Filtertype", + "filter-type-all": "Alle items", + "filter-type-include": "Items opnemen", + "filter-type-exclude": "Items uitsluiten", + "items": "Items", + "enter-urls-to-filter": "Voer URL's in om te filteren..." + }, + "persistent-table": { + "rpc-id": "RPC-ID", + "message-type": "Berichttype", + "method": "Methode", + "params": "Parameters", + "created-time": "Aangemaakt op", + "expiration-time": "Vervaltijd", + "retries": "Herhalingen", + "status": "Status", + "filter": "Filter", + "refresh": "Vernieuwen", + "add": "RPC-verzoek toevoegen", + "details": "Details", + "delete": "Verwijderen", + "delete-request-title": "Persistent RPC-verzoek verwijderen", + "delete-request-text": "Weet je zeker dat je dit verzoek wilt verwijderen?", + "details-title": "Details RPC-ID:", + "additional-info": "Aanvullende informatie", + "response": "Antwoord", + "any-status": "Elke status", + "rpc-status-list": "RPC-statuslijst", + "no-request-prompt": "Geen verzoek om weer te geven", + "send-request": "Verzoek verzenden", + "add-title": "Persistent RPC-verzoek aanmaken", + "method-error": "Methode is verplicht.", + "timeout-error": "Minimale time-outwaarde is 5000 (5 seconden).", + "white-space-error": "Spaties zijn niet toegestaan.", + "rpc-status": { + "QUEUED": "IN WACHTRIJ", + "SENT": "VERZONDEN", + "DELIVERED": "GELEVERD", + "SUCCESSFUL": "GELUKT", + "TIMEOUT": "TIME-OUT", + "EXPIRED": "VERLOPEN", + "FAILED": "MISLUKT" + }, + "rpc-search-status-all": "ALLES", + "message-types": { + "false": "Tweewegs", + "true": "Eenwegs" + }, + "general-settings": "Algemene instellingen", + "enable-filter": "Filter inschakelen", + "enable-sticky-header": "Kop weergeven tijdens scrollen", + "enable-sticky-action": "Actiekolom weergeven tijdens scrollen", + "display-request-details": "Verzoekdetails weergeven", + "allow-send-request": "Sta RPC-verzoek toe", + "allow-delete-request": "Sta verwijderen van verzoek toe", + "columns-settings": "Kolominstellingen", + "display-columns": "Weer te geven kolommen", + "column": "Kolom", + "no-columns-found": "Geen kolommen gevonden", + "no-columns-matching": "'{{column}}' niet gevonden." + }, + "range-chart": { + "chart": "Grafiek", + "data-zoom": "Gegevenszoomen", + "range-chart-appearance": "Weergavebereik grafiek", + "range-colors": "Bereikkleuren", + "out-of-range-color": "Kleur buiten bereik", + "show-range-thresholds": "Bereikdrempels weergeven", + "range-thresholds-settings": "Instellingen voor bereikdrempels", + "fill-area": "Gebied vullen", + "fill-area-opacity": "Opaciteit van het gevulde gebied", + "range-chart-style": "Stijl van bereikgrafiek" + }, + "rpc": { + "value-settings": "Waardesinstellingen", + "initial-value": "Initiële waarde", + "retrieve-value-settings": "Ophalen aan/uit waarde instellingen", + "retrieve-value-method": "Waarde ophalen via methode", + "retrieve-value-method-none": "Niet ophalen", + "retrieve-value-method-rpc": "RPC-methode voor ophalen aanroepen", + "retrieve-value-method-attribute": "Abonneren op attribuut", + "retrieve-value-method-timeseries": "Abonneren op tijdreeks", + "attribute-value-key": "Attribuut sleutel", + "timeseries-value-key": "Tijdreeks sleutel", + "get-value-method": "RPC methode voor ophalen waarde", + "parse-value-function": "Waarde parsing functie", + "update-value-settings": "Waarde update instellingen", + "set-value-method": "RPC methode voor waarde instellen", + "convert-value-function": "Waarde conversie functie", + "rpc-settings": "RPC-instellingen", + "request-timeout": "RPC-verzoek time-out (ms)", + "persistent-rpc-settings": "Persistente RPC-instellingen", + "request-persistent": "Persistente RPC-verzoek", + "persistent-polling-interval": "Polling-interval (ms) voor RPC-respons", + "common-settings": "Algemene instellingen", + "switch-title": "Schakeltitel", + "show-on-off-labels": "Toon aan/uit labels", + "slide-toggle-label": "Schakelaarlabel", + "label-position": "Labelpositie", + "label-position-before": "Voor", + "label-position-after": "Na", + "slider-color": "Schuifregelaar kleur", + "slider-color-primary": "Primair", + "slider-color-accent": "Accent", + "slider-color-warn": "Waarschuwing", + "button-style": "Knopstijl", + "button-raised": "Verhoogde knop", + "button-primary": "Primaire kleur", + "button-background-color": "Achtergrondkleur van knop", + "button-text-color": "Tekstkleur van knop", + "widget-title": "Widgettitel", + "button-label": "Knoplabel", + "device-attribute-scope": "Apparaatattribuutscope", + "server-attribute": "Serverattribuut", + "shared-attribute": "Gedeeld attribuut", + "device-attribute-parameters": "Apparaatattribuutparameters", + "is-one-way-command": "Is eenrichtingscommando", + "rpc-method": "RPC-methode", + "rpc-method-params": "RPC-methode parameters", + "show-rpc-error": "RPC-uitvoeringsfout weergeven", + "led-title": "LED-titel", + "led-color": "LED-kleur", + "check-status-settings": "Statuscontrole-instellingen", + "perform-rpc-status-check": "RPC-statuscontrole uitvoeren", + "retrieve-led-status-value-method": "LED-status ophalen via methode", + "led-status-value-attribute": "Attribuut met LED-statuswaarde", + "led-status-value-timeseries": "Tijdreeks met LED-statuswaarde", + "check-status-method": "RPC-methode voor statuscontrole", + "parse-led-status-value-function": "LED-statuswaarde parsing functie", + "knob-title": "Knoptitel", + "min-value": "Minimale waarde", + "max-value": "Maximale waarde" + }, + "maps": { + "map-type": { + "type": "Kaarttype", + "map": "Kaart", + "image": "Afbeelding" + }, + "image": { + "image-source": "Afbeeldingsbron", + "image-source-image": "Afbeelding", + "image-source-entity-key": "Entiteitssleutel", + "source-entity-alias": "Alias van bronentiteit", + "image-url-key": "Afbeelding URL sleutel", + "image-url-key-required": "Afbeelding URL sleutel is verplicht" + }, + "control": { + "map-controls": "Kaartbediening", + "position": "Positie", + "position-topleft": "Linksboven", + "position-topright": "Rechtsboven", + "position-bottomleft": "Linksonder", + "position-bottomright": "Rechtsonder", + "zoom-actions": "Zoomacties", + "zoom-scroll": "Scrollen", + "zoom-double-click": "Dubbelklik", + "zoom-control-buttons": "Bedieningsknoppen", + "scale": "Schaal", + "scale-metric": "Metrisch", + "scale-imperial": "Imperiaal", + "switch-to-drag-mode-using-button": "Schakel naar sleepmodus via knop" + }, + "timeline": { + "control-panel": "Tijdlijnbedieningspaneel", + "time-step": "Tijdstap", + "speed-options": "Snelheidsopties", + "timestamp": "Tijdstempel", + "snap-to-real-location": "Vastzetten op werkelijke locatie", + "location-snap-filter-function": "Filterfunctie voor locatievergrendeling", + "no-trips-data-available": "Geen ritgegevens beschikbaar" + }, + "map-action": { + "map-action-buttons": "Kaartactieknoppen", + "label": "Label", + "icon": "Pictogram", + "color": "Kleur", + "action": "Actie", + "add-button": "Knop toevoegen", + "no-action-buttons-configured": "Geen actieknoppen geconfigureerd", + "remove-action-button": "Verwijder actieknop", + "map-action-button": "Kaartactieknop", + "button-requires": "Knop vereist label of pictogram" + }, + "common": { + "common-map-settings": "Algemene kaartinstellingen", + "fit-map-bounds": "Pas kaartweergave aan om alle markeringen te tonen", + "default-map-center-position": "Standaard centrale positie van de kaart", + "default-map-zoom-level": "Standaard zoomniveau van de kaart", + "entities-limit": "Beperking van het aantal te laden entiteiten" + }, + "layer": { + "label": "Label", + "layer": "Laag", + "layers": "Lagen", + "map-layers": "Kaartlagen", + "add-layer": "Laag toevoegen", + "layer-settings": "Laaginstellingen", + "remove-layer": "Laag verwijderen", + "no-layers": "Geen lagen geconfigureerd", + "roadmap": "Wegenkaart", + "satellite": "Satelliet", + "hybrid": "Hybride", + "reference": { + "reference-layer": "Referentielaag", + "no-layer": "Geen laag", + "openstreetmap-hybrid": "OpenStreetMap Hybride", + "world-edition-hybrid": "Wereldeditie Hybride", + "enhanced-contrast-hybrid": "Hybride met verbeterd contrast" + }, + "provider": { + "provider": "Provider", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WereldStraatKaart", + "esri-topo": "WereldTopoKaart", + "esri-imagery": "WereldBeeld", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Wegenkaart", + "satellite": "Satelliet", + "hybrid": "Hybride", + "terrain": "Terrein" + }, + "here": { + "title": "HERE", + "normal-day": "Normale dag", + "normal-night": "Normale nacht", + "hybrid-day": "Hybride dag", + "terrain-day": "Terrein dag" + }, + "tencent": { + "title": "Tencent", + "normal": "Normaal", + "satellite": "Satelliet", + "terrain": "Terrein" + }, + "custom": { + "title": "Aangepast", + "tile-url": "Tegel-URL" + } + }, + "credentials": { + "credentials": "Inloggegevens", + "api-key": "API-sleutel" + } + }, + "overlays": { + "overlays": "Overlays", + "overlays-hint": "Configureer gegevensbronnen, uiterlijk, gedrag, bewerkingsopties en groepering voor kaartentiteiten", + "trips": "Reizen", + "markers": "Markeringen", + "polygons": "Polygonen", + "circles": "Cirkels" + }, + "data-layer": { + "source": "Bron", + "filter": "Filter", + "additional-data-keys": "Aanvullende gegevenssleutels", + "additional-datasources": "Aanvullende gegevensbronnen", + "additional-datasources-hint": "Gegevensbron voor toegang tot attributen of telemetrie van entiteiten die niet op de kaart worden weergegeven, bruikbaar in kaartoverlayfuncties.", + "more-datasources": "Meer gegevensbronnen", + "data-keys": "Gegevenssleutels", + "add-datasource": "Gegevensbron toevoegen", + "no-datasources": "Geen gegevensbronnen geconfigureerd", + "remove-datasource": "Gegevensbron verwijderen", + "behavior": "Gedrag", + "on-click": "Bij klikken", + "on-click-hint": "Actie die wordt uitgevoerd wanneer de gebruiker op het kaartitem klikt.", + "groups": "Groepen", + "groups-hint": "Lijst van groepsnamen toegewezen aan de overlay, gebruikt om de zichtbaarheid op de kaart te schakelen.", + "color": "Kleur", + "color-settings": "Kleurinstellingen", + "color-type-constant": "Constant", + "color-type-range": "Bereik", + "color-type-function": "Functie", + "color-range-source-key": "Kleurbereik bronkey", + "color-range-source-key-required": "Kleurbereik bronkey is vereist", + "color-range": "Kleurbereik", + "color-function": "Kleurfunctie", + "label": "Label", + "tooltip": "Tooltip", + "pattern-type-pattern": "Patroon", + "pattern-type-function": "Functie", + "label-pattern": "Label (voorbeelden: '${entityName}', '${entityName}: (Tekst ${keyName} eenheden.)')", + "label-function": "Labelfunctie", + "tooltip-pattern": "Tooltip (bijv. 'Tekst ${keyName} eenheden.' of Linktekst)", + "tooltip-function": "Tooltipfunctie", + "tooltip-trigger": "Tooltip-trigger", + "tooltip-trigger-click": "Tooltip tonen bij klik", + "tooltip-trigger-hover": "Tooltip tonen bij hover", + "auto-close-tooltips": "Tooltip automatisch sluiten", + "tooltip-offset": "Tooltip-offset", + "tooltip-offset-horizontal": "Horizontaal", + "tooltip-offset-vertical": "Verticaal", + "tooltip-tag-actions": "Tagacties", + "add-tooltip-tag-action": "Tagactie toevoegen", + "edit-tooltip-tag-action": "Tagactie bewerken", + "remove-tooltip-tag-action": "Tagactie verwijderen", + "action-add": "Toevoegen", + "action-edit": "Bewerken", + "action-move": "Verplaatsen", + "action-remove": "Verwijderen", + "edit-instruments": "Instrumenten", + "persist-location-attribute-scope": "Reikwijdte van attribuut om locatie op te slaan", + "enable-snapping": "Uitlijnen met andere punten inschakelen", + "enable-snapping-hint": "Lijnt automatisch nieuwe punten uit met bestaande vormen om tekenen eenvoudiger en nauwkeuriger te maken.", + "drag-drop-mode": "Sleepmodus", + "trip": { + "no-trips": "Geen reizen geconfigureerd", + "add-trip": "Reis toevoegen", + "trip-configuration": "Reisconfiguratie", + "remove-trip": "Reis verwijderen" + }, + "marker": { + "marker": "Marker", + "latitude-key": "Breedtegraad-key", + "longitude-key": "Lengtegraad-key", + "x-pos-key": "X-positie-key", + "y-pos-key": "Y-positie-key", + "latitude-key-required": "Breedtegraad-key is vereist", + "longitude-key-required": "Lengtegraad-key is vereist", + "x-pos-key-required": "X-positie-key is vereist", + "y-pos-key-required": "Y-positie-key is vereist", + "no-markers": "Geen markers geconfigureerd", + "add-marker": "Marker toevoegen", + "marker-configuration": "Markerconfiguratie", + "remove-marker": "Marker verwijderen", + "marker-type": "Markertype", + "marker-type-shape": "Vorm", + "marker-type-icon": "Pictogram", + "marker-type-image": "Afbeelding", + "shape": "Vorm", + "icon": "Pictogram", + "image": "Afbeelding", + "marker-shapes": "Markervormen", + "marker-icon": "Markerpictogram", + "marker-appearance": "Markeruiterlijk", + "marker-image": "Markerafbeelding", + "marker-image-type-image": "Afbeelding", + "marker-image-type-function": "Functie", + "custom-marker-image-size": "Aangepaste markerafbeeldingsgrootte", + "marker-image-function": "Markerafbeeldingfunctie", + "marker-images": "Markerafbeeldingen", + "marker-offset": "Markeroffset", + "offset-horizontal": "Horizontaal", + "offset-vertical": "Verticaal", + "rotate-marker": "Marker roteren", + "offset-angle": "Offsethoek", + "position-conversion": "Positieconversie", + "position-conversion-function": "Positieconversiefunctie, moet x,y-coördinaten tussen 0 en 1 als double teruggeven", + "clustering": { + "use-map-markers-clustering": "Gebruik clustering van kaartmarkers", + "zoom-on-cluster-click": "Inzoomen bij klikken op cluster", + "max-zoom": "Maximale zoomniveau waarbij een marker deel kan uitmaken van een cluster (0 - 18)", + "max-radius": "Maximale straal die een cluster dekt", + "zoom-animation": "Animatie van markers bij inzoomen", + "bounds-on-cluster-mouse-over": "Markergrenzen bij hoveren over cluster", + "spiderfy-max-zoom-level": "Spiderfy op maximaal zoomniveau (om alle clustermarkers te tonen)", + "load-optimization": "Laadoptimalisatie", + "chunked-load": "Gebruik chunks om markers toe te voegen zodat de pagina niet vastloopt", + "lazy-load": "Gebruik lazy load om markers toe te voegen", + "use-cluster-marker-color-function": "Gebruik kleurfunctie voor clustermarkers", + "marker-color-function": "Kleurfunctie voor marker" + }, + "edit": "Marker bewerken", + "remove-marker-for": "Verwijder marker voor '{{entityName}}'", + "place-marker": "Marker plaatsen", + "place-marker-hint": "Klik om marker te plaatsen", + "place-marker-hint-with-entity": "Klik om entiteit '{{entityName}}' te plaatsen" + }, + "path": { + "path": "Pad", + "path-decorator": "Paddecoratie", + "decorator-symbol": "Decoratiesymbool", + "decorator-symbol-arrow-head": "Pijl", + "decorator-symbol-dash": "Streep", + "decorator-arrangement": "Decoratieplaatsing", + "decorator-offset": "Begin", + "decorator-end-offset": "Einde", + "decorator-repeat": "Herhaal" + }, + "points": { + "points": "Punten", + "point-tooltip": "Tooltip bij punt" + }, + "shape": { + "fill": "Vulling", + "fill-type-color": "Kleur", + "fill-type-stripe": "Streep", + "fill-type-image": "Afbeelding", + "color": "Kleur", + "stripe": "Streep", + "image": "Afbeelding", + "stroke": "Omtrek", + "fill-image": "Vulafbeelding", + "fill-image-type-image": "Afbeelding", + "fill-image-type-function": "Functie", + "preserve-aspect-ratio": "Beeldverhouding behouden", + "opacity": "Dekking", + "angle": "Rotatiehoek", + "scale": "Schaal", + "fill-image-function": "Vulafbeeldingfunctie voor vorm", + "fill-images": "Vulafbeeldingen voor vorm", + "stripe-pattern": "Streeppatroon", + "first-stripe": "Eerste streep", + "second-stripe": "Tweede streep" + }, + "polygon": { + "polygon-key": "Polygon sleutel", + "polygon-key-required": "Polygon sleutel is verplicht", + "no-polygons": "Geen polygonen geconfigureerd", + "add-polygon": "Polygon toevoegen", + "polygon-configuration": "Polygonconfiguratie", + "remove-polygon": "Verwijder polygon", + "edit": "Bewerk polygon", + "remove-polygon-for": "Verwijder polygon voor '{{entityName}}'", + "cut": "Snijd polygongebied", + "rotate": "Roteer polygon", + "draw-rectangle": "Rechthoek tekenen", + "draw-polygon": "Polygon tekenen", + "polygon-place-first-point-cut-hint": "Klik om het eerste punt te plaatsen", + "continue-polygon-cut-hint": "Klik om verder te tekenen", + "finish-polygon-cut-hint": "Klik op eerste marker om te voltooien en op te slaan", + "polygon-place-first-point-hint": "Polygon: klik om eerste punt te plaatsen", + "polygon-place-first-point-hint-with-entity": "Polygon voor '{{entityName}}': klik om eerste punt te plaatsen", + "continue-polygon-hint": "Polygon: klik om verder te tekenen", + "continue-polygon-hint-with-entity": "Polygon voor '{{entityName}}': klik om verder te tekenen", + "finish-polygon-hint": "Polygon: klik op eerste marker om te voltooien", + "finish-polygon-hint-with-entity": "Polygon voor '{{entityName}}': klik op eerste marker om te voltooien en op te slaan", + "rectangle-place-first-point-hint": "Rechthoek: klik om eerste punt te plaatsen", + "rectangle-place-first-point-hint-with-entity": "Rechthoek voor '{{entityName}}': klik om eerste punt te plaatsen", + "finish-rectangle-hint": "Rechthoek: klik om tekenen te voltooien", + "finish-rectangle-hint-with-entity": "Rechthoek voor '{{entityName}}': klik om te voltooien en op te slaan" + }, + "circle": { + "circle-key": "Cirkel sleutel", + "circle-key-required": "Cirkel sleutel is verplicht", + "no-circles": "Geen cirkels geconfigureerd", + "add-circle": "Cirkel toevoegen", + "circle-configuration": "Cirkelconfiguratie", + "remove-circle": "Verwijder cirkel", + "edit": "Bewerk cirkel", + "remove-circle-for": "Verwijder cirkel voor '{{entityName}}'", + "draw-circle": "Teken cirkel", + "place-circle-center-hint-with-entity": "Cirkel voor '{{entityName}}': klik om middelpunt te plaatsen", + "place-circle-center-hint": "Cirkel: klik om middelpunt te plaatsen", + "finish-circle-hint-with-entity": "Cirkel voor '{{entityName}}': klik om te voltooien en op te slaan", + "finish-circle-hint": "Cirkel: klik om tekenen te voltooien" + }, + "select-entity": "Selecteer entiteit", + "select-entity-hint": "Tip: klik op de kaart na selectie om positie in te stellen" + }, + "select-entity": "Selecteer entiteit", + "select-entity-hint": "Tip: klik na selectie op de kaart om de positie in te stellen", + "tooltips": { + "placeMarker": "Klik om entiteit '{{entityName}}' te plaatsen", + "firstVertex": "Polygon voor '{{entityName}}': klik om eerste punt te plaatsen", + "firstVertex-cut": "Klik om eerste punt te plaatsen", + "continueLine": "Polygon voor '{{entityName}}': klik om verder te tekenen", + "continueLine-cut": "Klik om verder te tekenen", + "finishLine": "Klik op bestaande marker om te voltooien", + "finishPoly": "Polygon voor '{{entityName}}': klik op eerste marker om te voltooien en op te slaan", + "finishPoly-cut": "Klik op eerste marker om te voltooien en op te slaan", + "finishRect": "Polygon voor '{{entityName}}': klik om te voltooien en op te slaan", + "startCircle": "Cirkel voor '{{entityName}}': klik om middelpunt te plaatsen", + "finishCircle": "Cirkel voor '{{entityName}}': klik om te voltooien", + "placeCircleMarker": "Klik om cirkelmarker te plaatsen" + }, + "actions": { + "finish": "Voltooien", + "cancel": "Annuleren", + "removeLastVertex": "Verwijder laatste punt" + }, + "buttonTitles": { + "drawMarkerButton": "Entiteit plaatsen", + "drawPolyButton": "Maak polygon", + "drawLineButton": "Maak polyline", + "drawCircleButton": "Maak cirkel", + "drawRectButton": "Maak rechthoek", + "editButton": "Bewerkmodus", + "dragButton": "Slepen-modus", + "cutButton": "Snijd polygongebied", + "deleteButton": "Verwijderen", + "drawCircleMarkerButton": "Maak cirkelmarker", + "rotateButton": "Roteer polygon" + }, + "map-provider-settings": "Kaartproviderinstellingen", + "map-provider": "Kaartprovider", + "map-provider-google": "Google Maps", + "map-provider-openstreet": "OpenStreet Maps", + "map-provider-here": "HERE Maps", + "map-provider-image": "Afbeelding kaart", + "map-provider-tencent": "Tencent Maps", + "openstreet-provider": "OpenStreet kaartprovider", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Standaard)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery": "Esri.WorldImagery", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Gebruik aangepaste provider", + "custom-provider-tile-url": "Tegel-URL van aangepaste provider", + "google-maps-api-key": "Google Maps API-sleutel", + "default-map-type": "Standaard kaarttype", + "google-map-type-roadmap": "Wegenkaart", + "google-map-type-satelite": "Satelliet", + "google-map-type-hybrid": "Hybride", + "google-map-type-terrain": "Terrein", + "map-layer": "Kaartlaag", + "here-map-normal-day": "HERE.normalDay (Standaard)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "Inloggegevens", + "here-app-id": "HERE app ID", + "here-app-code": "HERE app-code", + "here-api-key": "HERE API-sleutel", + "here-use-new-version-api-3": "Gebruik API-versie 3", + "tencent-maps-api-key": "Tencent Maps API-sleutel", + "tencent-map-type-roadmap": "Wegenkaart", + "tencent-map-type-satelite": "Satelliet", + "tencent-map-type-hybrid": "Hybride", + "image-map-background": "Afbeelding als kaartachtergrond", + "image-map-background-from-entity-attribute": "Gebruik afbeelding van entiteitattribuut als kaartachtergrond", + "image-url-source-entity-alias": "Alias van entiteit met afbeeldings-URL", + "image-url-source-entity-attribute": "Attribuut van entiteit met afbeeldings-URL", + "common-map-settings": "Algemene kaartinstellingen", + "x-pos-key-name": "Naam X-positie sleutel", + "y-pos-key-name": "Naam Y-positie sleutel", + "latitude-key-name": "Naam van latitude sleutel", + "longitude-key-name": "Naam van longitude sleutel", + "default-map-zoom-level": "Standaard zoomniveau kaart (0 - 20)", + "default-map-center-position": "Standaard middenpositie kaart (0,0)", + "disable-scroll-zooming": "Scrollzoom uitschakelen", + "disable-double-click-zooming": "Dubbelklik-zoom uitschakelen", + "disable-zoom-control-buttons": "Zoomknoppen uitschakelen", + "fit-map-bounds": "Pas kaartgrenzen aan om alle markeringen te tonen", + "use-default-map-center-position": "Gebruik standaard kaartmiddenpositie", + "entities-limit": "Limiet op te laden entiteiten", + "markers-settings": "Markering instellingen", + "marker-offset-x": "X-offset markering relatief aan breedte", + "marker-offset-y": "Y-offset markering relatief aan hoogte", + "position-function": "Functie voor positierekening, moet x,y-coördinaten (0-1) teruggeven", + "draggable-marker": "Versleepbare markering", + "label": "Label", + "show-label": "Toon label", + "use-label-function": "Gebruik labelfunctie", + "label-pattern": "Label (voorbeeld: '${entityName}', '${entityName}: (Tekst ${keyName} eenheden.)')", + "label-function": "Labelfunctie", + "tooltip": "Tooltip", + "show-tooltip": "Toon tooltip", + "show-tooltip-action": "Actie voor tooltip weergeven", + "show-tooltip-action-click": "Toon tooltip bij klik (Standaard)", + "show-tooltip-action-hover": "Toon tooltip bij zweven", + "auto-close-tooltips": "Sluit tooltips automatisch", + "use-tooltip-function": "Gebruik tooltipfunctie", + "tooltip-pattern": "Tooltip (bijv. 'Tekst ${keyName} eenheden.' of Linktekst)", + "tooltip-function": "Tooltipfunctie", + "tooltip-offset-x": "X-offset tooltip relatief aan anker", + "tooltip-offset-y": "Y-offset tooltip relatief aan anker", + "color": "Kleur", + "use-color-function": "Gebruik kleurfunctie", + "color-function": "Kleurfunctie", + "marker-image": "Markering afbeelding", + "use-marker-image-function": "Gebruik markeringsafbeeldingsfunctie", + "custom-marker-image": "Aangepaste markeringsafbeelding", + "custom-marker-image-size": "Afmeting aangepaste markeringsafbeelding (px)", + "marker-image-function": "Markeringsafbeeldingsfunctie", + "marker-images": "Markeringsafbeeldingen", + "polygon-settings": "Polygooninstellingen", + "show-polygon": "Toon polygoon", + "polygon-key-name": "Naam polygoonsleutel", + "enable-polygon-edit": "Bewerkbare polygoon inschakelen", + "polygon-label": "Polygoonlabel", + "show-polygon-label": "Toon polygoonlabel", + "use-polygon-label-function": "Gebruik polygoonlabelfunctie", + "polygon-label-pattern": "Polygoonlabel (bijv. '${entityName}', '${entityName}: (Tekst ${keyName} eenheden.)')", + "polygon-label-function": "Polygoonlabelfunctie", + "polygon-tooltip": "Polygoontooltip", + "show-polygon-tooltip": "Toon polygoontooltip", + "auto-close-polygon-tooltips": "Sluit polygoontooltips automatisch", + "use-polygon-tooltip-function": "Gebruik polygoontooltipfunctie", + "polygon-tooltip-pattern": "Tooltip (bijv. 'Tekst ${keyName} eenheden.' of Linktekst)", + "polygon-tooltip-function": "Polygoontooltipfunctie", + "polygon-color": "Polygoonkleur", + "polygon-opacity": "Polygoontransparantie", + "use-polygon-color-function": "Gebruik polygoonkleurfunctie", + "polygon-color-function": "Polygoonkleurfunctie", + "polygon-stroke": "Polygoonrand", + "stroke-color": "Randkleur", + "stroke-opacity": "Randtransparantie", + "stroke-weight": "Randdikte", + "use-polygon-stroke-color-function": "Gebruik polygoonrandkleurfunctie", + "polygon-stroke-color-function": "Polygoonrandkleurfunctie", + "circle-settings": "Cirkelinstellingen", + "show-circle": "Toon cirkel", + "circle-key-name": "Cirkelsleutelnaam", + "enable-circle-edit": "Cirkelbewerking inschakelen", + "circle-label": "Cirkellabel", + "show-circle-label": "Toon cirkellabel", + "use-circle-label-function": "Gebruik cirkellabelfunctie", + "circle-label-pattern": "Cirkellabel (voorbeeld: '${entityName}', '${entityName}: (Tekst ${keyName} eenheden.)')", + "circle-label-function": "Cirkellabelfunctie", + "circle-tooltip": "Cirkeltooltip", + "show-circle-tooltip": "Toon cirkeltooltip", + "auto-close-circle-tooltips": "Sluit cirkeltooltips automatisch", + "use-circle-tooltip-function": "Gebruik cirkeltooltipfunctie", + "circle-tooltip-pattern": "Tooltip (bijv. 'Tekst ${keyName} eenheden.' of Linktekst)", + "circle-tooltip-function": "Cirkeltooltipfunctie", + "circle-fill-color": "Cirkelvulkleur", + "circle-fill-color-opacity": "Transparantie vulkleur cirkel", + "use-circle-fill-color-function": "Gebruik cirkelvulkleurfunctie", + "circle-fill-color-function": "Cirkelvulkleurfunctie", + "circle-stroke": "Cirkelrand", + "use-circle-stroke-color-function": "Gebruik cirkelrandkleurfunctie", + "circle-stroke-color-function": "Cirkelrandkleurfunctie", + "markers-clustering-settings": "Instellingen voor groepering van markeringen", + "use-map-markers-clustering": "Gebruik groepering van kaartmarkeringen", + "zoom-on-cluster-click": "Zoom bij klikken op een cluster", + "max-cluster-zoom": "Maximale zoomniveau voor clustering (0 - 18)", + "max-cluster-radius-pixels": "Maximale straal die een cluster dekt in pixels", + "cluster-zoom-animation": "Toon animatie bij inzoomen op markeringen", + "show-markers-bounds-on-cluster-mouse-over": "Toon grenzen van markeringen bij zweven over cluster", + "spiderfy-max-zoom-level": "Spiderfy op maximale zoomniveau (om alle markeringen te zien)", + "load-optimization": "Laadoptimalisatie", + "cluster-chunked-loading": "Gebruik batches om markeringen toe te voegen", + "cluster-markers-lazy-load": "Gebruik lazy loading voor markeringen", + "editor-settings": "Editorinstellingen", + "enable-snapping": "Snapping naar andere punten inschakelen", + "init-draggable-mode": "Kaart starten in sleepmodus", + "hide-all-edit-buttons": "Verberg alle bewerkingsknoppen", + "hide-draw-buttons": "Verberg tekentools", + "hide-edit-buttons": "Verberg bewerkingsknoppen", + "hide-remove-button": "Verberg verwijderknop", + "route-map-settings": "Routekaartinstellingen", + "trip-animation-settings": "Instellingen voor route-animatie", + "normalization-step": "Normalisatie stap (ms)", + "tooltip-background-color": "Achtergrondkleur tooltip", + "tooltip-font-color": "Letterkleur tooltip", + "tooltip-opacity": "Tooltiptransparantie (0-1)", + "auto-close-tooltip": "Tooltip automatisch sluiten", + "rotation-angle": "Extra rotatiehoek voor markering (graden)", + "path-settings": "Padinstellingen", + "path-color": "Padkleur", + "use-path-color-function": "Gebruik padkleurfunctie", + "path-color-function": "Padkleurfunctie", + "path-decorator": "Paddecoratie", + "use-path-decorator": "Gebruik paddecoratie", + "decorator-symbol": "Decoratiesymbool", + "decorator-symbol-arrow-head": "Pijl", + "decorator-symbol-dash": "Streep", + "decorator-symbol-size": "Grootte decoratiesymbool (px)", + "use-path-decorator-custom-color": "Gebruik aangepaste kleur voor decoratiesymbool", + "decorator-custom-color": "Aangepaste decoratiekleur", + "decorator-offset": "Decoratie-offset", + "end-decorator-offset": "Einde decoratie-offset", + "decorator-repeat": "Herhaling decoratie", + "points-settings": "Puntinstellingen", + "show-points": "Toon punten", + "point-color": "Puntkleur", + "point-size": "Puntgrootte (px)", + "use-point-color-function": "Gebruik puntkleurfunctie", + "point-color-function": "Puntkleurfunctie", + "use-point-as-anchor": "Gebruik punt als anker", + "point-as-anchor-function": "Functie voor punt als anker", + "independent-point-tooltip": "Onafhankelijke punttooltip", + "clustering-markers": "Markeringen groeperen", + "use-icon-create-function": "Gebruik markeringskleurfunctie", + "marker-color-function": "Markeringskleurfunctie" + }, + "markdown": { + "use-markdown-text-function": "Gebruik markdown/HTML-waardefunctie", + "markdown-text-function": "Markdown/HTML-waardefunctie", + "markdown-text-pattern": "Markdown/HTML-patroon (markdown of HTML met variabelen, bijv. '${entityName} of ${keyName} - wat tekst.')", + "apply-default-markdown-style": "Pas standaard markdown-stijl toe", + "markdown-css": "Markdown/HTML CSS" + }, + "simple-card": { + "label": "Label", + "label-position": "Labelpositie", + "label-position-left": "Links", + "label-position-top": "Boven" + }, + "single-switch": { + "behavior": "Gedrag", + "layout": "Indeling", + "layout-right": "Rechts", + "layout-left": "Links", + "layout-centered": "Gecentreerd", + "auto-scale": "Automatisch schalen", + "label": "Label", + "icon": "Icoon", + "switch-color": "Schakelkleur", + "on": "Aan", + "off": "Uit", + "disabled": "Uitgeschakeld", + "tumbler-color": "Schuifkleur", + "on-label": "Label aan", + "off-label": "Label uit", + "switch": "Schakelaar" + }, + "slider": { + "behavior": "Gedrag", + "initial-value": "Initiële waarde", + "initial-value-hint": "Actie om de initiële waarde van de schuifregelaar op te halen.", + "on-value-change": "Bij waarde wijziging", + "on-value-change-hint": "Actie die wordt uitgevoerd wanneer de waarde van de schuifregelaar wordt gewijzigd.", + "layout": "Indeling", + "layout-default": "Standaard", + "layout-extended": "Uitgebreid", + "layout-simplified": "Vereenvoudigd", + "auto-scale": "Automatisch schalen", + "icon": "Icoon", + "value": "Waarde", + "range": "Bereik", + "min": "min", + "max": "max", + "range-ticks": "Bereik-stappen", + "tick-marks": "Stapmarkeringen", + "colors": "Kleuren", + "main": "Hoofd", + "background": "Achtergrond", + "left-icon": "Linkericoon", + "right-icon": "Rechtericoon", + "slider": "Schuifregelaar" + }, + "value-card": { + "layout": "Indeling", + "layout-square": "Vierkant", + "layout-vertical": "Verticaal", + "layout-centered": "Gecentreerd", + "layout-simplified": "Vereenvoudigd", + "layout-horizontal": "Horizontaal", + "layout-horizontal-reversed": "Horizontaal omgekeerd", + "label": "Label", + "icon": "Icoon", + "value": "Waarde", + "date": "Datum", + "value-card-style": "Waardekaartstijl", + "auto-scale": "Automatisch schalen" + }, + "label-card": { + "auto-scale": "Automatisch schalen", + "label": "Label", + "icon": "Icoon", + "label-card-style": "Labelkaartstijl" + }, + "label-value-card": { + "value": "Waarde", + "label-value-card-style": "Label- & waardekaartstijl" + }, + "liquid-level-card": { + "layout-simple": "Eenvoudig", + "layout-percentage": "Percentage", + "layout-absolute": "Absoluut", + "layout": "Indeling", + "background-overlay": "Waarde achtergrond overlay", + "total-volume": "Totaal volume", + "total-volume-units": "Totaal volume-eenheden", + "tank": "Tank", + "shape": "Vorm", + "datasource-units": "Bron-eenheden", + "widget-units": "Widget-eenheden", + "decimals": "Decimalen", + "liquid": "Vloeistof", + "liquid-color": "Vloeistofkleur", + "value": "Waarde", + "value-font": "Waardefont", + "level": "Niveau", + "last-update": "Laatste update", + "shape-by-attribute": "Tankvorm instellen via attribuutnaam", + "tooltip-background": "Achtergrondkleur", + "background-blur": "Achtergrondvervaging", + "tank-color": "Tankkleur", + "static": "Statisch", + "see-examples": "Zie voorbeelden", + "attribute": "Attribuut", + "shape-type": "Type", + "v-oval": "Verticaal ovaal", + "v-cylinder": "Verticale cilinder", + "v-capsule": "Verticale capsule", + "rectangle": "Rechthoek", + "h-oval": "Horizontaal ovaal", + "h-ellipse": "Horizontale ellips", + "h-dish-ends": "Horizontale afgeronde uiteinden", + "h-cylinder": "Horizontale cilinder", + "h-capsule": "Horizontale capsule", + "h-elliptical_2_1": "Horizontale 2:1 ellips", + "icon": "Kaarticoon", + "title": "Kaarttitel", + "units": "Eenheden", + "color-and-font": "Kleur en lettertype", + "shape-attribute-name": "Attribuutnaam", + "total-volume-required": "Totaal volume is vereist.", + "attribute-name-required": "Attribuutnaam is vereist.", + "attribute-key-not-set": "Attribuut '{{attributeName}}' is niet ingesteld", + "attribute-key-invalid": "Attribuut '{{attributeName}}' is ongeldig" + }, + "aggregated-value-card": { + "subtitle": "Ondertitel", + "chart": "Grafiek", + "values": "Waarden", + "value-appearance": "Weergave van waarde", + "position": "Positie", + "position-center": "Midden", + "position-right-top": "Rechtsboven", + "position-right-bottom": "Rechtsonder", + "position-left-top": "Linksboven", + "position-left-bottom": "Linksonder", + "font": "Lettertype", + "color": "Kleur", + "arrow": "Pijl", + "display-up-down-arrow": "Toon omhoog/omlaag-pijl", + "add-value": "Waarde toevoegen", + "remove-value": "Waarde verwijderen", + "no-values": "Geen waarden geconfigureerd", + "aggregation": "Aggregatie", + "aggregated-value-card-style": "Samengevoegde waarde kaartstijl", + "auto-scale": "Automatisch schalen" + }, + "value-chart-card": { + "layout": "Indeling", + "layout-left": "Links", + "layout-right": "Rechts", + "auto-scale": "Automatisch schalen", + "icon": "Icoon", + "value": "Waarde", + "chart": "Grafiek", + "value-chart-card-style": "Stijl van waarde-grafiekkaart" + }, + "progress-bar": { + "layout": "Indeling", + "layout-default": "Standaard", + "layout-simplified": "Vereenvoudigd", + "auto-scale": "Automatisch schalen", + "icon": "Icoon", + "value": "Waarde", + "range": "Bereik", + "min": "min", + "max": "max", + "range-ticks": "Tikken binnen bereik", + "bar": "Balk", + "bar-color": "Balkkleur", + "bar-background": "Achtergrond van de balk", + "progress-bar-card-style": "Stijl van voortgangsbalkkaart" + }, + "notification": { + "max-notification-display": "Maximaal aantal meldingen om weer te geven", + "counter": "Teller", + "counter-hint": "Teller wordt weergegeven als \"Widgettitel\" is ingeschakeld", + "icon": "Icoon", + "counter-value": "Waarde", + "counter-color": "Kleur", + "notification-button": "Meldingsknoppen", + "button-view-all": "Bekijk alles", + "button-filter": "Filter", + "type-filter": "Typefilter", + "button-mark-read": "Markeer alles als gelezen", + "notification-types": "Meldingstypes", + "notification-type": "Meldingstype", + "search-type": "Zoektype", + "any-type": "Elk type" + }, + "alarm-count": { + "alarm-count-card-style": "Stijl van alarmtellerkaart" + }, + "entity-count": { + "entity-count-card-style": "Stijl van entiteittellerkaart" + }, + "count": { + "layout": "Indeling", + "layout-column": "Kolom", + "layout-row": "Rij", + "label": "Label", + "icon": "Icoon", + "icon-background": "Achtergrond van icoon", + "value": "Waarde", + "chevron": "Chevron", + "auto-scale": "Automatisch schalen" + }, + "table": { + "common-table-settings": "Algemene tabelinstellingen", + "enable-search": "Zoeken inschakelen", + "enable-sticky-header": "Kop altijd weergeven", + "enable-sticky-action": "Actiekolom altijd weergeven", + "hidden-cell-button-display-mode": "Weergavemodus voor verborgen knopacties in cel", + "show-empty-space-hidden-action": "Lege ruimte tonen in plaats van verborgen knopactie", + "dont-reserve-space-hidden-action": "Geen ruimte reserveren voor verborgen actieknoppen", + "display-timestamp": "Tijdstempel", + "display-pagination": "Paginering weergeven", + "default-page-size": "Standaard paginagrootte", + "page-step-settings": "Instellingen paginastappen", + "page-step-count": "Aantal stappen", + "page-step-increment": "Stapincremente", + "page-step-count-format-message": "Moet een geheel getal zijn tussen 1 en 100.", + "page-step-increment-format-message": "Moet een geheel getal zijn, groter dan of gelijk aan 1.", + "use-entity-label-tab-name": "Gebruik entiteitlabel in tabbladnaam", + "hide-empty-lines": "Lege regels verbergen", + "row-style": "Rijstijl", + "use-row-style-function": "Gebruik rijstijlfunctie", + "row-style-function": "Rijstijlfunctie", + "cell-style": "Celstijl", + "use-cell-style-function": "Gebruik celstijlfunctie", + "cell-style-function": "Celstijlfunctie", + "cell-content": "Celinhoud", + "use-cell-content-function": "Gebruik celinhoudfunctie", + "cell-content-function": "Celinhoudfunctie", + "show-latest-data-column": "Laatste gegevenskolom tonen", + "latest-data-column-order": "Volgorde van laatste gegevenskolom", + "entities-table-title": "Titel van entiteitentabel", + "enable-select-column-display": "Kolomselectie inschakelen", + "display-entity-name": "Kolom met entiteitsnaam weergeven", + "entity-name-column-title": "Titel kolom entiteitsnaam", + "display-entity-label": "Kolom met entiteitlabel weergeven", + "entity-label-column-title": "Titel kolom entiteitlabel", + "display-entity-type": "Kolom met entiteitstype weergeven", + "default-sort-order": "Standaard sorteervolgorde", + "custom-title": "Aangepaste koptekst", + "column-width": "Kolombreedte (px of %)", + "default-column-visibility": "Standaard kolomzichtbaarheid", + "column-visibility-visible": "Zichtbaar", + "column-visibility-hidden": "Verborgen", + "column-visibility-hidden-mobile": "Verborgen op mobiel", + "column-selection-to-display": "Kolomselectie in 'Te tonen kolommen'", + "column-selection-to-display-enabled": "Ingeschakeld", + "column-selection-to-display-disabled": "Uitgeschakeld", + "alarms-table-title": "Titel van alarmtabel", + "enable-alarms-selection": "Alarmselectie inschakelen", + "enable-alarms-search": "Alarm zoeken inschakelen", + "enable-alarm-filter": "Alarmfilter inschakelen", + "display-alarm-details": "Alarmdetails tonen", + "allow-alarms-ack": "Erkenning van alarmen toestaan", + "allow-alarms-clear": "Opruiming van alarmen toestaan", + "display-alarm-activity": "Alarmactiviteit weergeven", + "allow-alarms-assign": "Toewijzing van alarmen toestaan", + "columns": "Kolommen", + "column-settings": "Kolominstellingen", + "remove-column": "Kolom verwijderen", + "add-column": "Kolom toevoegen", + "no-columns": "Geen kolommen geconfigureerd", + "columns-to-display": "Te tonen kolommen", + "table-header": "Tabelkop", + "header-buttons": "Koptekstknoppen", + "table-buttons": "Tabelknoppen", + "pagination": "Paginering", + "rows": "Rijen", + "timeseries-column-error": "Ten minste één tijdreekskolom moet worden opgegeven", + "alarm-column-error": "Ten minste één alarmkolom moet worden opgegeven", + "table-tabs": "Tabeltabs", + "show-cell-actions-menu-mobile": "Toon keuzemenu celacties in mobiele modus", + "disable-sorting": "Sortering uitschakelen" + }, + "latest-chart": { + "total": "Totaal", + "auto-scale": "Automatisch schalen", + "clockwise-layout": "Layout met de klok mee", + "sort-series": "Series sorteren op label", + "tooltip-value-type-absolute": "Absoluut", + "tooltip-value-type-percentage": "Percentage" + }, + "pie-chart": { + "pie-chart-appearance": "Uiterlijk van cirkeldiagram", + "label": "Label", + "border": "Rand", + "radius": "Straal", + "pie-chart-card-style": "Stijl van cirkeldiagramkaart" + }, + "radar-chart": { + "radar-appearance": "Uiterlijk van radardiagram", + "shape": "Vorm", + "shape-polygon": "Veelhoek", + "shape-circle": "Cirkel", + "color": "Kleur", + "line": "Lijn", + "points": "Punten", + "points-label": "Puntlabel", + "radar-axis": "Radar-as", + "axis-label": "Aslabel", + "ticks-label": "Label van streepjes", + "radar-chart-style": "Stijl van radardiagram" + }, + "time-series-chart": { + "chart": "Diagram", + "chart-style": "Diagramstijl", + "data-zoom": "Gegevens inzoomen", + "stack-mode": "Stapelmethode", + "stack-mode-hint": "Stapelt series in het diagram. Series met dezelfde eenheid worden boven elkaar geplaatst.", + "axes": "Assen", + "y-axes": "Y-assen", + "line-type": "Lijntype", + "line-width": "Lijnbreedte", + "type-line": "Lijn", + "type-bar": "Staaf", + "type-point": "Punt", + "no-aggregation-bar-width-strategy": "Staafbreedtestrategie voor niet-geaggregeerde gegevens", + "no-aggregation-bar-width-strategy-group": "Groep", + "no-aggregation-bar-width-strategy-separate": "Gescheiden", + "bar-group-width": "Staafgroepbreedte", + "bar-width": "Staafbreedte", + "bar-width-relative": "Percentage van tijdsvenster", + "bar-width-absolute": "Absoluut (ms)", + "comparison": { + "comparison": "Vergelijking", + "comparison-hint": "Vergelijking werkt alleen met historische gegevens!", + "show": "Tonen", + "settings": "Instellingen vergelijking", + "show-values-for-comparison": "Toon historische gegevens voor vergelijking", + "comparison-values-label": "Label van vergelijkingssleutel", + "comparison-values-label-auto": "Automatisch", + "comparison-data-color": "Kleur van vergelijkingsgegevens" + }, + "threshold": { + "thresholds": "Drempelwaarden", + "source": "Bron", + "key-value": "Sleutel / Waarde", + "no-thresholds": "Geen drempelwaarden geconfigureerd", + "add-threshold": "Drempel toevoegen", + "type-constant": "Constante", + "type-latest-key": "Sleutel", + "type-entity": "Entiteit", + "threshold-settings": "Instellingen drempelwaarde", + "remove-threshold": "Drempel verwijderen", + "threshold-value-required": "Drempelwaarde is vereist.", + "key-required": "Sleutel is vereist.", + "entity-key-required": "Entiteitssleutel is vereist.", + "line-appearance": "Lijnweergave", + "line-color": "Lijnkleur", + "start-symbol": "Startsymbool", + "end-symbol": "Eindsymbool", + "symbol-size": "Grootte", + "label": "Label", + "label-position-start": "Start", + "label-position-middle": "Midden", + "label-position-end": "Einde", + "label-position-inside-start": "Binnenkant begin", + "label-position-inside-start-top": "Binnenkant begin boven", + "label-position-inside-start-bottom": "Binnenkant begin onder", + "label-position-inside-middle": "Binnenkant midden", + "label-position-inside-middle-top": "Binnenkant midden boven", + "label-position-inside-middle-bottom": "Binnenkant midden onder", + "label-position-inside-end": "Binnenkant einde", + "label-position-inside-end-top": "Binnenkant einde boven", + "label-position-inside-end-bottom": "Binnenkant einde onder", + "label-background": "Labelachtergrond" + }, + "state": { + "states": "Toestanden", + "label": "Label", + "ticks-value": "Waarde van streepjes", + "source": "Bron", + "value-range": "Waarde / Bereik", + "no-states": "Geen toestanden geconfigureerd", + "add-state": "Toestand toevoegen", + "type-constant": "Constante", + "type-range": "Bereik", + "from": "Van", + "to": "Tot", + "remove-state": "Toestand verwijderen" + }, + "grid": { + "grid": "Raster", + "background-color": "Achtergrondkleur", + "border": "Rand" + }, + "axis": { + "axes": "Assen", + "x-axis": "X-as", + "y-axis": "Y-as", + "y-axis-settings": "Y-as instellingen", + "comparison-x-axis-settings": "Vergelijkings-X-as instellingen", + "remove-y-axis": "Y-as verwijderen", + "id": "Id", + "label": "Label", + "position": "Positie", + "position-left": "Links", + "position-right": "Rechts", + "position-top": "Boven", + "position-bottom": "Onder", + "tick-labels": "Streepjeslabels", + "ticks-formatter-function": "Streepjes formatter functie", + "ticks-generator-function": "Streepjes generator functie", + "show-ticks": "Streepjes tonen", + "show-line": "Lijn tonen", + "show-split-lines": "Scheidingslijnen tonen", + "show-split-lines-x-axis-hint": "Indien ingeschakeld, worden de verticale lijnen in de grafiek weergegeven.", + "show-split-lines-y-axis-hint": "Indien ingeschakeld, worden de horizontale lijnen in de grafiek weergegeven.", + "ticks-interval": "Streepjesinterval", + "ticks-interval-hint": "Segmenteringsinterval voor de as forceren.", + "split-number": "Aantal segmenten", + "split-number-hint": "Aantal segmenten waarin de as is opgedeeld.", + "min": "Min", + "max": "Max", + "show": "Tonen", + "add-y-axis": "Y-as toevoegen" + }, + "series": { + "legend-settings": "Legenda instellingen", + "show-in-legend": "In legenda tonen", + "show-in-legend-hint": "Toon seriesnaam en gegevens in de legenda.", + "hidden-by-default": "Standaard verborgen", + "hidden-by-default-hint": "Maak de serie standaard verborgen in de legenda.", + "series-type": "Serietype", + "type": "Type", + "type-line": "Lijn", + "type-bar": "Staaf", + "line": { + "line": "Lijn", + "show-line": "Lijn tonen", + "step-line": "Staplijn", + "step-type-start": "Start", + "step-type-middle": "Midden", + "step-type-end": "Einde", + "smooth-line": "Vloeiende lijn" + }, + "point": { + "points": "Punten", + "show-points": "Punten tonen", + "point-label": "Puntlabel", + "point-label-hint": "Toon een label met waarde boven het punt van de serie.", + "point-label-background": "Achtergrond van puntlabel", + "point-shape": "Vorm van het punt", + "point-size": "Puntgrootte" + } + } + }, + "wind-speed-direction": { + "layout": "Indeling", + "layout-default": "Standaard", + "layout-advanced": "Geavanceerd", + "layout-simplified": "Vereenvoudigd", + "values": "Waarden", + "wind-direction": "Windrichting", + "center-value": "Middelste waarde", + "icon": "Icoon", + "arrow": "Pijl", + "ticks": "Streepjes", + "labels-type": "Type labels", + "directional-names": "Richtingnamen", + "degrees": "Graden", + "major-ticks": "Grote streepjes", + "minor-ticks": "Kleine streepjes", + "wind-speed-direction-card-style": "Stijl voor windrichting en -snelheid kaart", + "ticks-color": "Kleur van streepjes", + "ticks-labels-type": "Type labels voor streepjes", + "arrow-color": "Kleur van de pijl" + }, + "value-source": { + "value-source": "Bron van waarde", + "predefined-value": "Constante", + "entity-attribute": "Entiteitsattribuut", + "value": "Waarde", + "value-required": "Waarde is verplicht.", + "key-required": "Sleutel is verplicht.", + "entity-key-required": "Entiteitssleutel is verplicht.", + "source-entity-alias": "Alias van bronentiteit", + "source-entity-attribute": "Attribuut van bronentiteit", + "type-constant": "Constante", + "type-latest-key": "Sleutel", + "type-entity": "Entiteit" + }, + "rpc-state": { + "initial-state": "Initiële toestand", + "initial-state-hint": "Actie om de initiële toestand (Aan/Uit) van de component op te halen.", + "disabled-state": "Uitgeschakelde toestand", + "disabled-state-hint": "Configureer de voorwaarde waaronder de component wordt uitgeschakeld.", + "turn-on": "Inschakelen", + "turn-on-hint": "Actie bij inschakelen van de schuifregelaar", + "turn-off": "Uitschakelen", + "turn-off-hint": "Actie bij uitschakelen van de schuifregelaar", + "on": "Aan", + "off": "Uit", + "disabled": "Uitgeschakeld" + }, + "value-action": { + "do-nothing": "Niets doen", + "execute-rpc": "RPC uitvoeren", + "get-attribute": "Attribuut ophalen", + "set-attribute": "Attribuut instellen", + "get-time-series": "Tijdreeks ophalen", + "get-alarm-status": "Alarmstatus ophalen", + "get-dashboard-state": "Dashboard status-id ophalen", + "get-dashboard-state-object": "Dashboard statusobject ophalen", + "add-time-series": "Tijdreeks toevoegen", + "execute-rpc-text": "Voer RPC-methode '{{methodName}}' uit", + "get-time-series-text": "Gebruik tijdreeks '{{key}}'", + "get-attribute-text": "Gebruik attribuut '{{key}}'", + "get-alarm-status-text": "Gebruik alarmstatus", + "get-dashboard-state-text": "Gebruik dashboardstatus", + "get-dashboard-state-object-text": "Gebruik dashboard statusobject", + "when-dashboard-state-is-text": "Wanneer dashboardstatus-id '{{state}}' is", + "when-dashboard-state-function-is-text": "Wanneer f(dashboard status-id) '{{state}}' is", + "when-dashboard-state-object-function-is-text": "Wanneer f(dashboard statusobject) '{{state}}' is", + "set-attribute-to-value-text": "Stel attribuut '{{key}}' in op: {{value}}", + "add-time-series-value-text": "Voeg '{{key}}' tijdreekswaarde toe: {{value}}", + "set-attribute-text": "Stel attribuut '{{key}}' in", + "add-time-series-text": "Voeg tijdreeks '{{key}}' toe", + "action": "Actie", + "value": "Waarde", + "init-value-hint": "Waarde die wordt ingesteld totdat het apparaat gegevens verzendt.", + "method": "Methode", + "method-name-required": "Methodenaam is verplicht.", + "request-timeout-ms": "RPC-verzoek time-out (ms)", + "request-timeout-required": "Time-out voor verzoek is verplicht.", + "min-request-timeout-error": "Time-outwaarde moet minimaal 5000 ms (5 seconden) zijn.", + "request-persistent": "Persistent RPC-verzoek", + "persistent-polling-interval": "Persistent polling-interval (ms)", + "persistent-polling-interval-hint": "Polling-interval (ms) voor antwoord op persistent RPC-commando", + "persistent-polling-interval-required": "Persistent polling-interval is verplicht.", + "min-persistent-polling-interval-error": "Polling-interval moet minimaal 1000 ms (1 seconde) zijn.", + "attribute-scope": "Attribuutscope", + "attribute-key": "Attribuutsleutel", + "attribute-key-required": "Attribuutsleutel is verplicht.", + "time-series-key": "Tijdreekssleutel", + "time-series-key-required": "Tijdreekssleutel is verplicht.", + "action-result-converter": "Resultaatomzetter voor actie", + "converter-none": "Geen", + "converter-function": "Functie", + "converter-constant": "Constante", + "converter-value": "Waarde", + "parse-value-function": "Waarde parseringsfunctie", + "state-when-result-is": "'{{state}}' wanneer resultaat is", + "parameters": "Parameters", + "convert-value-function": "Waardeconversiefunctie", + "error": { + "target-entity-is-not-set": "Doelentiteit is niet ingesteld!", + "failed-to-perform-action": "Kon de actie {{ actionLabel }} niet uitvoeren.", + "invalid-attribute-scope": "Attribuutscope {{scope}} wordt niet ondersteund voor {{entityType}} entiteit." + } + }, + "widget-font": { + "font-settings": "Lettertype-instellingen", + "font-family": "Lettertypefamilie", + "size": "Grootte", + "relative-font-size": "Relatieve lettergrootte (procenten)", + "font-style": "Stijl", + "font-style-normal": "Normaal", + "font-style-italic": "Cursief", + "font-style-oblique": "Schuin", + "font-weight": "Dikte", + "font-weight-normal": "Normaal", + "font-weight-bold": "Vet", + "font-weight-bolder": "Vetgedrukt", + "font-weight-lighter": "Lichter", + "color": "Kleur", + "shadow-color": "Schaduwkleur", + "preview": "Voorbeeld", + "line-height": "Regelhoogte", + "auto": "Auto" + }, + "home": { + "no-data-available": "Geen gegevens beschikbaar" + }, + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "Schijf", + "cpu-warning-text": "Hoge CPU-belasting. Optimaliseer de systeemprestaties om systeemfouten te voorkomen.", + "cpu-critical-text": "Kritieke CPU-belasting. Optimaliseer de systeemprestaties om systeemfouten te voorkomen.", + "ram-warning-text": "Weinig RAM beschikbaar. Optimaliseer de systeemprestaties of verhoog de RAM om systeemfouten te voorkomen.", + "ram-critical-text": "Kritiek laag RAM-geheugen. Optimaliseer de systeemprestaties of verhoog de RAM om systeemfouten te voorkomen.", + "disk-warning-text": "Weinig schijfruimte beschikbaar. Maak ruimte vrij of breid de schijfruimte uit om dataverlies te voorkomen.", + "disk-critical-text": "Kritiek weinig schijfruimte beschikbaar. Maak ruimte vrij of breid de schijfruimte uit om dataverlies te voorkomen." + }, + "cluster-info": { + "service-id": "Service-ID", + "service-type": "Servicetype", + "no-data": "Geen gegevens" + }, + "transport-messages": { + "title": "Transportberichten", + "info": "Alle berichten afkomstig van apparaten" + }, + "activity": { + "title": "Activiteit" + }, + "documentation": { + "title": "Documentatie", + "add-link": "Link toevoegen", + "add-link-title": "Documentatielink toevoegen", + "name": "Naam", + "name-required": "Naam is verplicht.", + "link": "Link", + "link-required": "Link is verplicht.", + "columns": "Kolommen" + }, + "quick-links": { + "title": "Snelle links", + "add-link": "Link toevoegen", + "add-link-title": "Snelle link toevoegen", + "quick-link": "Snelle link", + "quick-link-required": "Snelle link is verplicht.", + "no-links-matching": "Geen links gevonden die overeenkomen met '{{name}}'.", + "columns": "Kolommen" + }, + "recent-dashboards": { + "title": "Dashboards", + "last": "Laatst bekeken", + "starred": "Gemarkeerd", + "name": "Naam", + "last-viewed": "Laatst bekeken", + "no-last-viewed-dashboards": "Nog geen laatst bekeken dashboards" + }, + "configured-features": { + "title": "Geconfigureerde functies", + "info": "Status van functies die configuratie vereisen", + "email-feature": "E-mail", + "sms-feature": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "feature-configured": "Functie is geconfigureerd.\nKlik om in te stellen", + "feature-not-configured": "Functie is niet geconfigureerd.\nKlik om in te stellen" + }, + "version-info": { + "title": "Versie", + "contact-us": "Neem contact op", + "current-version": "Huidige versie", + "current": "Huidig", + "available-version": "Beschikbare versie", + "available": "Beschikbaar", + "upgrade": "Upgraden", + "version-is-up-to-date": "Versie is up-to-date" + }, + "usage-info": { + "title": "Gebruik", + "entities": "Entiteiten", + "api-calls": "API-aanroepen" + }, + "functions": { + "title": "Functies", + "pe-feature-tooltip": "Alleen beschikbaar in ThingsBoard\nProfessional Edition", + "switch-to-pe": "Overschakelen naar PE", + "alarms": "Alarmen", + "dashboards": "Dashboards", + "entities-and-relations": "Entiteiten & Relaties", + "profiles": "Profielen", + "advanced-features": "Geavanceerde functies", + "notification-center": "Meldingscentrum", + "api-usage": "API-gebruik", + "customers": "Klanten", + "customers-hierarchy": "Klantenhiërarchie", + "roles-and-permissions": "Rollen & Rechten", + "groups": "Groepen", + "integrations": "Integraties", + "solution-templates": "Oplossingssjablonen", + "scheduler": "Planner", + "white-labeling": "White-labeling" + }, + "devices": { + "view-docs": "Bekijk documentatie", + "inactive": "Inactief", + "active": "Actief", + "total": "Totaal" + }, + "alarms": { + "critical": "Kritiek", + "assigned-to-me": "Aan mij toegewezen", + "total": "Totaal" + }, + "getting-started": { + "get-started": "Aan de slag", + "finish": "Voltooien", + "done-welcome-title": "Welkom aan boord", + "done-welcome-text": "Je hebt het geweldig gedaan!", + "sys-admin": { + "step1": { + "title": "Maak Tenant & Tenantbeheerder aan", + "content": "

Een tenant is een individu of organisatie die apparaten en middelen bezit of beheert. Een tenant kan meerdere beheerders, klanten, apparaten en middelen hebben.

De tenantbeheerder kan apparaten, middelen, klanten en dashboards beheren binnen het tenantaccount.

Volg de documentatie om dit in te stellen:

", + "how-to-create-tenant": "Hoe Tenant & Tenantbeheerder aan te maken" + }, + "step2": { + "title": "Configureer functie: Mailserver", + "content": "

Configuratie van de mailserver is essentieel voor gebruikersactivatie, wachtwoordherstel en het verzenden van alarmmeldingen.

Volg de documentatie om dit in te stellen:

", + "how-to-configure-mail-server": "Hoe mailserver te configureren" + }, + "step3": { + "title": "Configureer functie: SMS-provider", + "content": "

Configureer SMS-providers om klanten te waarschuwen voor alarmen via SMS.

Volg de documentatie om dit in te stellen:

", + "how-to-configure-sms-provider": "Hoe SMS-provider te configureren" + }, + "step4": { + "title": "Configureer functie: White-labeling", + "content": "

Pas eenvoudig het logo en kleurenschema van jouw bedrijf of product aan zonder codering of herstart van de dienst.

Volg de documentatie om dit in te stellen:

" + }, + "step5": { + "title": "Configureer functie: 2FA", + "content": "

Verhoog de veiligheid van platformaccounts met twee-factor-authenticatie.

Volg de documentatie om dit in te stellen:

" + }, + "step6": { + "title": "Configureer functie: OAuth 2", + "content": "

Vereenvoudig de aanmelding van tenant- en klantgebruikers met Single Sign-On via OAuth 2.0.

Volg de documentatie om dit in te stellen:

" + } + }, + "tenant-admin": { + "step1": { + "title": "Apparaat aanmaken", + "content": "

Laten we je eerste apparaat via de UI aan het platform toevoegen. Volg de documentatie om dit te doen:

", + "how-to-create-device": "Hoe een apparaat aan te maken" + }, + "step2": { + "title": "Apparaat verbinden", + "content-before": "

Om het apparaat te verbinden, heb je de apparaatreferenties nodig. Voor deze handleiding raden we aan om de standaard automatisch gegenereerde toegangstoken te gebruiken.

  • Ga naar de apparaattabel
  • Klik op de apparaatregel om de apparaatgegevens te openen
  • Druk op de knop \"Toegangstoken kopiëren\"

Gebruik eenvoudige commando's om gegevens via HTTP te verzenden. Vergeet niet $ACCESS_TOKEN te vervangen door het toegangstoken van je apparaat:

", + "ubuntu": { + "install-curl": "Installeer cURL voor Ubuntu:" + }, + "macos": { + "install-curl": "Installeer cURL voor MacOS:" + }, + "windows": { + "install-curl": "Vanaf Windows 10 b17063 is cURL standaard beschikbaar." + }, + "replace-access-token": "Vervang $ACCESS_TOKEN door het toegangstoken van je apparaat:", + "content-after": "

Je kunt ook andere protocollen gebruiken zoals MQTT, CoAP, enz.

Volg de documentatie om dit te doen:

", + "how-to-connect-device": "Hoe een apparaat te verbinden" + }, + "step3": { + "title": "Dashboard aanmaken", + "content": "

Maak een dashboard aan om gegevens van entiteiten zoals apparaten, middelen, enz. te visualiseren.

Volg de documentatie om dit te doen:

", + "how-to-create-dashboard": "Hoe een dashboard aan te maken" + }, + "step4": { + "title": "Alarmregels configureren", + "alarm-rules": "Alarmregels", + "content": "

Laten we een alarm genereren wanneer de temperatuur 25°C bereikt. Volg de documentatie om dit te doen:

", + "how-to-configure-alarm-rules": "Hoe alarmregels te configureren" + }, + "step5": { + "title": "Alarm aanmaken", + "content-before": "

Om het alarm te activeren, verzend een nieuwe telemetrie waarde van 26°C of hoger.

", + "replace-access-token": "Vervang $ACCESS_TOKEN door het toegangstoken van je apparaat:", + "content-after": "

Volg de documentatie om dit te doen:

", + "how-to-create-alarm": "Hoe een alarm aan te maken" + }, + "step6": { + "title": "Klant aanmaken en dashboard delen", + "content": "

Door dashboards voor eindgebruikers aan te maken, kan een klantgebruiker alleen zijn eigen apparaten zien. Gegevens van andere klanten blijven verborgen.

Volg de documentatie om dit te doen:

" + } + } + } + }, + "icon": { + "icon": "Pictogram", + "icons": "Pictogrammen", + "select-icon": "Selecteer pictogram", + "material-icons": "Material pictogrammen", + "show-all": "Toon alle pictogrammen", + "search-icon": "Zoek pictogram", + "no-icons-found": "Geen pictogrammen gevonden voor '{{iconSearch}}'" + }, + "phone-input": { + "phone-input-label": "Telefoonnummer", + "phone-input-required": "Telefoonnummer is verplicht", + "phone-input-validation": "Telefoonnummer is ongeldig of niet mogelijk", + "phone-input-pattern": "Ongeldig telefoonnummer. Moet in E.164-formaat zijn, bijv. {{phoneNumber}}", + "phone-input-hint": "Telefoonnummer in E.164-formaat, bijv. {{phoneNumber}}" + }, + "custom": { + "widget-action": { + "action-cell-button": "Actieknop in cel", + "row-click": "Bij rijklik", + "cell-click": "Bij celklik", + "polygon-click": "Bij klik op polygoon", + "marker-click": "Bij klik op marker", + "circle-click": "Bij klik op cirkel", + "tooltip-tag-action": "Tooltip tag-actie", + "node-selected": "Bij geselecteerde knoop", + "element-click": "Bij klik op HTML-element", + "pie-slice-click": "Bij klik op taartdiagramsegment", + "row-double-click": "Bij dubbelklik op rij", + "cell-double-click": "Bij dubbelklik op cel", + "card-click": "Bij klik op kaart", + "click": "Bij klik" + } + }, + "paginator": { + "items-per-page": "Items per pagina:", + "first-page-label": "Eerste pagina", + "last-page-label": "Laatste pagina", + "next-page-label": "Volgende pagina", + "previous-page-label": "Vorige pagina", + "items-per-page-separator": "van" + }, + "language": { + "language": "Taal" + } +} diff --git a/ui-ngx/src/assets/locale/locale.constant-no_NO.json b/ui-ngx/src/assets/locale/locale.constant-no_NO.json new file mode 100644 index 0000000000..9b2a91f75e --- /dev/null +++ b/ui-ngx/src/assets/locale/locale.constant-no_NO.json @@ -0,0 +1,9197 @@ +{ + "access": { + "unauthorized": "Uautorisert", + "unauthorized-access": "Uautorisert tilgang", + "unauthorized-access-text": "Du må logge inn for å få tilgang til denne ressursen!", + "access-forbidden": "Tilgang nektet", + "access-forbidden-text": "Du har ikke tilgangsrettigheter til denne plasseringen!
Prøv å logge inn med en annen bruker hvis du fortsatt ønsker tilgang.", + "refresh-token-expired": "Økten er utløpt", + "refresh-token-failed": "Kunne ikke oppdatere økten", + "permission-denied": "Tillatelse nektet", + "permission-denied-text": "Du har ikke tillatelse til å utføre denne operasjonen!" + }, + "account": { + "account": "Konto", + "notification-settings": "Varslingsinnstillinger" + }, + "action": { + "activate": "Aktiver", + "suspend": "Suspender", + "save": "Lagre", + "saveAs": "Lagre som", + "move": "Flytt", + "cancel": "Avbryt", + "ok": "OK", + "delete": "Slett", + "add": "Legg til", + "yes": "Ja", + "no": "Nei", + "update": "Oppdater", + "remove": "Fjern", + "search": "Søk", + "clear-search": "Tøm søk", + "assign": "Tildel", + "unassign": "Fjern tildeling", + "share": "Del", + "make-private": "Gjør privat", + "apply": "Bruk", + "apply-changes": "Bruk endringer", + "edit-mode": "Redigeringsmodus", + "enter-edit-mode": "Gå til redigeringsmodus", + "decline-changes": "Avvis endringer", + "decline": "Avvis", + "close": "Lukk", + "back": "Tilbake", + "run": "Kjør", + "sign-in": "Logg inn!", + "edit": "Rediger", + "view": "Vis", + "create": "Opprett", + "drag": "Dra", + "refresh": "Oppdater", + "undo": "Angre", + "copy": "Kopier", + "paste": "Lim inn", + "copy-reference": "Kopier referanse", + "paste-reference": "Lim inn referanse", + "import": "Importer", + "export": "Eksporter", + "share-via": "Del via {{provider}}", + "select": "Velg", + "continue": "Fortsett", + "discard-changes": "Forkast endringer", + "download": "Last ned", + "next": "Neste", + "next-with-label": "Neste: {{label}}", + "read-more": "Les mer", + "hide": "Skjul", + "test": "Test", + "done": "Ferdig", + "print": "Skriv ut", + "restore": "Gjenopprett", + "confirm": "Bekreft", + "more": "Mer", + "less": "Mindre", + "skip": "Hopp over", + "send": "Send", + "reset": "Tilbakestill", + "show-more": "Vis mer", + "dont-show-again": "Ikke vis igjen", + "see-documentation": "Se dokumentasjon", + "clear": "Tøm", + "upload": "Last opp", + "delete-anyway": "Slett likevel", + "delete-selected": "Slett valgte", + "set": "Sett" + }, + "aggregation": { + "aggregation": "Aggregering", + "function": "Funksjon for data-aggregering", + "limit": "Maks verdier", + "group-interval": "Grupperingsintervall", + "min": "Min", + "max": "Maks", + "avg": "Gjennomsnitt", + "sum": "Sum", + "count": "Antall", + "none": "Ingen" + }, + "admin": { + "settings": "Innstillinger", + "general": "Generelt", + "general-settings": "Generelle innstillinger", + "home-settings": "Hjeminnstillinger", + "home": "Hjem", + "outgoing-mail": "E-postserver", + "outgoing-mail-settings": "Innstillinger for utgående e-postserver", + "system-settings": "Systeminnstillinger", + "test-mail-sent": "Testmeldingen ble sendt!", + "base-url": "Base-URL", + "base-url-required": "Base-URL er påkrevd.", + "prohibit-different-url": "Forby bruk av vertsnavn fra klientforespørsler", + "prohibit-different-url-hint": "Denne innstillingen bør være aktivert i produksjonsmiljøer. Kan forårsake sikkerhetsproblemer når den er deaktivert.", + "device-connectivity": { + "device-connectivity": "Enhetstilkobling", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPs", + "mqtt": "MQTT", + "mqtts": "MQTTs", + "coap": "COAP", + "coaps": "COAPs", + "hint": "Hvis felt for vert eller port er tomme, brukes standardverdi for protokoll.", + "host": "Vert", + "port": "Port", + "port-pattern": "Porten må være et positivt heltall.", + "port-range": "Porten må være i området fra 1 til 65535." + }, + "mail-from": "Avsenderadresse", + "mail-from-required": "Avsenderadresse er påkrevd.", + "smtp-protocol": "SMTP-protokoll", + "smtp-host": "SMTP-vert", + "smtp-host-required": "SMTP-vert er påkrevd.", + "smtp-port": "SMTP-port", + "smtp-port-required": "Du må angi en SMTP-port.", + "smtp-port-invalid": "Dette ser ikke ut som en gyldig SMTP-port.", + "timeout-msec": "Tidsavbrudd (ms)", + "timeout-required": "Tidsavbrudd er påkrevd.", + "timeout-invalid": "Dette ser ikke ut som en gyldig tidsavbruddverdi.", + "enable-tls": "Aktiver TLS", + "tls-version": "TLS-versjon", + "enable-proxy": "Aktiver proxy", + "proxy-host": "Proxy-vert", + "proxy-host-required": "Proxy-vert er påkrevd.", + "proxy-port": "Proxy-port", + "proxy-port-required": "Proxy-port er påkrevd.", + "proxy-port-range": "Proxy-porten må være i området 1 til 65535.", + "proxy-user": "Proxy-bruker", + "proxy-password": "Proxy-passord", + "change-password": "Endre passord", + "send-test-mail": "Send test-e-post", + "sms-provider": "SMS-leverandør", + "sms-provider-settings": "Innstillinger for SMS-leverandør", + "sms-provider-type": "SMS-leverandørtype", + "sms-provider-type-required": "SMS-leverandørtype er påkrevd.", + "sms-provider-type-aws-sns": "Amazon SNS", + "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID er påkrevd", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key er påkrevd", + "aws-region": "AWS-region", + "aws-region-required": "AWS-region er påkrevd", + "number-from": "Telefonnummer fra", + "number-from-required": "Telefonnummer fra er påkrevd.", + "number-to": "Telefonnummer til", + "number-to-required": "Telefonnummer til er påkrevd.", + "phone-number-hint": "Telefonnummer i E.164-format, f.eks. +19995550123", + "phone-number-hint-twilio": "Telefonnummer i E.164-format/Telefonnummerets SID/Meldingstjeneste SID, f.eks. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Ugyldig telefonnummer. Skal være i E.164-format, f.eks. +19995550123.", + "phone-number-pattern-twilio": "Ugyldig telefonnummer. Skal være i E.164-format/Telefonnummerets SID/Meldingstjeneste SID, f.eks. +19995550123/PNXXX/MGXXX.", + "sms-message": "SMS-melding", + "sms-message-required": "SMS-melding er påkrevd.", + "sms-message-max-length": "SMS-meldingen kan ikke være lengre enn 1600 tegn", + "twilio-account-sid": "Twilio-konto SID", + "twilio-account-sid-required": "Twilio-konto SID er påkrevd", + "twilio-account-token": "Twilio-konto Token", + "twilio-account-token-required": "Twilio-konto Token er påkrevd", + "send-test-sms": "Send test-SMS", + "test-sms-sent": "Test-SMS ble sendt!", + "security-settings": "Sikkerhetsinnstillinger", + "password-policy": "Passordpolicy", + "minimum-password-length": "Minimum passordlengde", + "minimum-password-length-required": "Minimum passordlengde er påkrevd", + "minimum-password-length-range": "Minimum passordlengde må være mellom 6 og 50", + "maximum-password-length": "Maksimum passordlengde", + "maximum-password-length-min": "Maksimum passordlengde må være minst 6", + "maximum-password-length-less-min": "Maksimum passordlengde må være større enn minimum", + "minimum-uppercase-letters": "Minimum antall store bokstaver", + "minimum-uppercase-letters-range": "Minimum antall store bokstaver kan ikke være negativt", + "minimum-lowercase-letters": "Minimum antall små bokstaver", + "minimum-lowercase-letters-range": "Minimum antall små bokstaver kan ikke være negativt", + "minimum-digits": "Minimum antall sifre", + "minimum-digits-range": "Minimum antall sifre kan ikke være negativt", + "minimum-special-characters": "Minimum antall spesialtegn", + "minimum-special-characters-range": "Minimum antall spesialtegn kan ikke være negativt", + "password-expiration-period-days": "Utløpsperiode for passord i dager", + "password-expiration-period-days-range": "Utløpsperiode for passord kan ikke være negativ", + "password-reuse-frequency-days": "Gjenbrukshyppighet for passord i dager", + "password-reuse-frequency-days-range": "Gjenbrukshyppighet for passord kan ikke være negativ", + "allow-whitespace": "Tillat mellomrom", + "force-reset-password-if-no-valid": "Tving tilbakestilling av passord hvis ugyldig", + "force-reset-password-if-no-valid-hint": "Vær forsiktig når du aktiverer denne funksjonen: brukere med ugyldige passord må tilbakestille passordet sitt via e-post.", + "general-policy": "Generell policy", + "max-failed-login-attempts": "Maks antall mislykkede innloggingsforsøk før kontoen låses", + "minimum-max-failed-login-attempts-range": "Maks antall mislykkede innloggingsforsøk kan ikke være negativt", + "user-lockout-notification-email": "Ved kontolåsing sendes varsel til e-post", + "user-activation-token-ttl": "Gyldighetstid for aktiveringslenke (timer)", + "user-activation-token-ttl-range": "Gyldighetstiden for aktiveringslenke må være mellom 1 og 24 timer", + "password-reset-token-ttl": "Gyldighetstid for tilbakestillingslenke (timer)", + "password-reset-token-ttl-range": "Gyldighetstiden for tilbakestillingslenke må være mellom 1 og 24 timer", + "mobile-secret-key-length": "Lengde på mobil hemmelig nøkkel", + "mobile-secret-key-length-range": "Lengden på den mobile hemmelige nøkkelen må være positiv", + "domain-name": "Domenenavn", + "domain-name-unique": "Domenenavn og protokoll må være unike.", + "domain-name-max-length": "Domenenavn skal være kortere enn 256 tegn", + "error-verification-url": "Et domenenavn skal ikke inneholde symbolene '/' og ':'. Eksempel: thingsboard.io", + "connection-settings": "Tilkoblingsinnstillinger", + "oauth2": { + "access-token-uri": "URI for tilgangstoken", + "access-token-uri-required": "URI for tilgangstoken er påkrevd.", + "activate-user": "Aktiver bruker", + "add-domain": "Legg til domene", + "delete-domain": "Slett domene", + "add-provider": "Legg til leverandør", + "delete-provider": "Slett leverandør", + "allow-user-creation": "Tillat opprettelse av brukere", + "always-fullscreen": "Alltid fullskjerm", + "authorization-uri": "Autoriserings-URI", + "authorization-uri-required": "Autoriserings-URI er påkrevd.", + "add-client": "Legg til OAuth 2.0-klient", + "client-details": "Detaljer for OAuth 2.0-klient", + "client": "OAuth 2.0-klient", + "clients": "OAuth 2.0-klienter", + "no-oauth2-clients": "Ingen OAuth 2.0-klienter funnet", + "search-oauth2-clients": "Søk i OAuth 2.0-klienter", + "delete-client-title": "Er du sikker på at du vil slette OAuth 2.0-klienten '{{clientName}}'?", + "delete-client-text": "Vær forsiktig, etter bekreftelse vil klienten og alle tilknyttede data ikke kunne gjenopprettes.", + "delete-mobile-app-title": "Er du sikker på at du vil slette mobilapplikasjonen '{{applicationName}}'?", + "delete-mobile-app-text": "Vær forsiktig, etter bekreftelse vil applikasjonen og alle tilhørende data ikke kunne gjenopprettes.", + "title": "Tittel", + "client-title-required": "Tittel er påkrevd", + "client-title-max-length": "Tittel må være kortere enn 100 tegn", + "advanced-settings": "Avanserte innstillinger", + "domain-details": "Detaljer for domene", + "no-domains": "Ingen domener funnet", + "search-domains": "Søk i domener", + "mobile-app-details": "Detaljer for mobilapplikasjon", + "add-mobile-app": "Legg til mobilapplikasjon", + "no-mobile-apps": "Ingen mobilapplikasjoner funnet", + "search-mobile-apps": "Søk i mobilapplikasjoner", + "send-token": "Send token", + "create-new": "Opprett ny", + "client-authentication-method": "Metode for klientautentisering", + "client-id": "Klient-ID", + "client-id-required": "Klient-ID er påkrevd.", + "client-id-max-length": "Klient-ID må være kortere enn 256 tegn", + "client-secret": "Klienthemmelighet", + "client-secret-required": "Klienthemmelighet er påkrevd.", + "client-secret-max-length": "Klienthemmelighet må være kortere enn 2049 tegn", + "custom-setting": "Egendefinerte innstillinger", + "customer-name-pattern": "Mønster for kundenavn", + "customer-name-pattern-max-length": "Mønster for kundenavn må være kortere enn 256 tegn", + "default-dashboard-name": "Standard dashboard-navn", + "default-dashboard-name-max-length": "Standard dashboard-navn må være kortere enn 256 tegn", + "delete-domain-text": "Vær forsiktig, etter bekreftelse vil domenet og all leverandørdata bli utilgjengelig.", + "delete-domain-title": "Er du sikker på at du vil slette domenet '{{domainName}}'?", + "delete-registration-text": "Vær forsiktig, etter bekreftelse vil leverandørdata bli utilgjengelig.", + "delete-registration-title": "Er du sikker på at du vil slette leverandøren '{{name}}'?", + "email-attribute-key": "E-postattributtnøkkel", + "email-attribute-key-required": "E-postattributtnøkkel er påkrevd.", + "email-attribute-key-max-length": "E-postattributtnøkkel må være kortere enn 32 tegn", + "first-name-attribute-key": "Fornavnattributtnøkkel", + "first-name-attribute-key-max-length": "Fornavnattributtnøkkel må være kortere enn 32 tegn", + "general": "Generelt", + "jwk-set-uri": "JSON Web Key URI", + "last-name-attribute-key": "Etternavnattributtnøkkel", + "last-name-attribute-key-max-length": "Etternavnattributtnøkkel må være kortere enn 32 tegn", + "login-button-icon": "Ikon for innloggingsknapp", + "login-button-label": "Leverandøretikett", + "login-button-label-placeholder": "Logg inn med $(Provider label)", + "login-button-label-required": "Etikett er påkrevd.", + "login-provider": "Innloggingsleverandør", + "mapper": "Mapper", + "new-domain": "Nytt domene", + "oauth2": "OAuth 2.0", + "password-max-length": "Passord må være kortere enn 256 tegn", + "redirect-uri-template": "Mønster for omdirigerings-URI", + "copy-redirect-uri": "Kopier omdirigerings-URI", + "registration-id": "Registrerings-ID", + "registration-id-required": "Registrerings-ID er påkrevd.", + "registration-id-unique": "Registrerings-ID må være unik i systemet.", + "scope": "Omfang", + "scope-required": "Omfang er påkrevd.", + "tenant-name-pattern": "Mønster for leietakernavn", + "tenant-name-pattern-required": "Mønster for leietakernavn er påkrevd.", + "tenant-name-pattern-max-length": "Mønster for leietakernavn må være kortere enn 256 tegn", + "tenant-name-strategy": "Strategi for leietakernavn", + "type": "Mappertype", + "uri-pattern-error": "Ugyldig URI-format.", + "url": "URL", + "url-pattern": "Ugyldig URL-format.", + "url-required": "URL er påkrevd.", + "url-max-length": "URL må være kortere enn 256 tegn", + "user-info-uri": "Brukerinformasjon URI", + "user-info-uri-required": "Brukerinformasjon URI er påkrevd.", + "username-max-length": "Brukernavn må være kortere enn 256 tegn", + "user-name-attribute-name": "Brukernavnattributtnøkkel", + "user-name-attribute-name-required": "Brukernavnattributtnøkkel er påkrevd", + "protocol": "Protokoll", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "Aktiver OAuth 2.0-innstillinger", + "disable": "Deaktiver OAuth 2.0-innstillinger", + "edge": "Repliker til Edge", + "edge-enable": "Aktiver replikering til Edge", + "edge-disable": "Deaktiver replikering til Edge", + "domains": "Domener", + "mobile-apps": "Mobilapplikasjoner", + "mobile-package": "Applikasjonspakke", + "mobile-package-placeholder": "F.eks.: my.example.app", + "mobile-package-hint": "For Android: din unike applikasjons-ID. For iOS: produktets bundle-identifikator.", + "mobile-package-unique": "Applikasjonspakken må være unik.", + "mobile-package-required": "Applikasjonspakken er påkrevd.", + "mobile-package-max-length": "Applikasjonspakken må være kortere enn 256 tegn", + "mobile-package-spaces": "Applikasjonspakken skal ikke inneholde mellomrom", + "mobile-app-secret": "Applikasjonsnøkkel", + "mobile-app-secret-hint": "Base64-kodet streng som representerer minst 512 bits med data.", + "mobile-app-secret-required": "Applikasjonsnøkkel er påkrevd.", + "mobile-app-secret-min-length": "Applikasjonsnøkkelen må representere minst 512 bits med data.", + "mobile-app-secret-base64": "Applikasjonsnøkkelen må være i base64-format.", + "invalid-mobile-app-secret": "Applikasjonsnøkkelen må kun inneholde alfanumeriske tegn og være mellom 16 og 2048 tegn lang.", + "copy-mobile-app-secret": "Kopier applikasjonsnøkkel", + "delete-mobile-app": "Slett applikasjonsinformasjon", + "providers": "Leverandører", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Alle plattformer", + "smtp-provider": "SMTP-leverandør", + "allowed-platforms": "Tillatte plattformer", + "authentication": "Autentisering", + "basic": "Grunnleggende", + "provider": "Leverandør", + "redirect-url": "Omdirigerings-URI", + "domain-name": "Domenenavn", + "domain-name-required": "Domenenavn er påkrevd", + "redirect-url-template": "Mal for omdirigerings-URI", + "microsoft-tenant-id": "Katalog-ID (leietaker-ID)", + "microsoft-tenant-id-required": "Katalog-ID (leietaker-ID) er påkrevd", + "token-uri": "Token-URI", + "token-uri-required": "Token-URI er påkrevd", + "redirect-uri": "Omdirigerings-URI", + "google-provider": "Google", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "Tilpasset", + "generate-access-token": "Generer tilgangstoken", + "update-access-token": "Oppdater tilgangstoken", + "access-token-status": "Status for tilgangstoken:", + "token-status-generated": "generert", + "token-status-not-generated": "ikke generert" + }, + "smpp-provider": { + "smpp-version": "SMPP-versjon", + "smpp-host": "SMPP-vert", + "smpp-host-required": "SMPP-vert er påkrevd", + "smpp-port": "SMPP-port", + "smpp-port-required": "SMPP-port er påkrevd", + "system-id": "System-ID", + "system-id-required": "System-ID er påkrevd", + "password": "Passord", + "password-required": "Passord er påkrevd", + "type-settings": "Typeinnstillinger", + "source-settings": "Innstillinger for kilde", + "destination-settings": "Innstillinger for destinasjon", + "additional-settings": "Tilleggsinnstillinger", + "system-type": "Systemtype", + "bind-type": "Bindetype", + "service-type": "Tjenestetype", + "source-address": "Kildeadresse", + "source-ton": "Kilde-TON", + "source-npi": "Kilde-NPI", + "destination-ton": "Destinasjon TON (Type of Number)", + "destination-npi": "Destinasjon NPI (Numbering Plan Identification)", + "address-range": "Adresseområde", + "coding-scheme": "Kodingsskjema", + "bind-type-tx": "Sender", + "bind-type-rx": "Mottaker", + "bind-type-trx": "Transceiver", + "ton-unknown": "Ukjent", + "ton-international": "Internasjonal", + "ton-national": "Nasjonal", + "ton-network-specific": "Nettverksspesifikk", + "ton-subscriber-number": "Abonnentnummer", + "ton-alphanumeric": "Alfanumerisk", + "ton-abbreviated": "Forkortet", + "npi-unknown": "0 - Ukjent", + "npi-isdn": "1 - ISDN/telefonnummerplan (E163/E164)", + "npi-data-numbering-plan": "3 - Datanumreplan (X.121)", + "npi-telex-numbering-plan": "4 - Telexnummerplan (F.69)", + "npi-land-mobile": "6 - Landmobil (E.212)", + "npi-national-numbering-plan": "8 - Nasjonal nummerplan", + "npi-private-numbering-plan": "9 - Privat nummerplan", + "npi-ermes-numbering-plan": "10 - ERMES-nummerplan (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internett (IP)", + "npi-wap-client-id": "18 - WAP-klient-ID (defineres av WAP Forum)", + "scheme-smsc": "0 - SMSC Standardalfabet (ASCII for korte og lange koder, GSM for gratisnummer)", + "scheme-ia5": "1 - IA5 (ASCII for korte og lange koder, Latin 9 for gratisnummer (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Oktett uspesifisert (8-bit binær)", + "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Oktett uspesifisert (8-bit binær)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Kyrillisk (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latin/Hebraisk (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Piktogramkoding", + "scheme-music-codes": "10 - Musikkkoder (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Utvidet Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Koreansk grafisk tegnsett (KS C 5601/KS X 1001)" + }, + "queue-select-name": "Velg kønavn", + "queue-name": "Navn", + "queue-name-required": "Kønavn er påkrevd!", + "queues": "Køer", + "queue-partitions": "Partisjoner", + "queue-submit-strategy": "Innsendingsstrategi", + "queue-processing-strategy": "Behandlingsstrategi", + "queue-configuration": "Køkonfigurasjon", + "repository-settings": "Depotinnstillinger", + "repository": "Depot", + "repository-url": "Depot-URL", + "repository-url-required": "Depot-URL er påkrevd.", + "default-branch": "Standard branch-navn", + "repository-read-only": "Kun lesbar", + "show-merge-commits": "Vis sammenslåingscommits", + "authentication-settings": "Autentiseringsinnstillinger", + "auth-method": "Autentiseringsmetode", + "auth-method-username-password": "Passord / tilgangstoken", + "auth-method-username-password-hint": "GitHub-brukere bruke tilgangstoken med skrive-tilgang til depotet.", + "auth-method-private-key": "Privat nøkkel", + "password-access-token": "Passord / tilgangstoken", + "change-password-access-token": "Endre passord / tilgangstoken", + "private-key": "Privat nøkkel", + "drop-private-key-file-or": "Dra og slipp en fil med privat nøkkel eller", + "passphrase": "Passfrase", + "enter-passphrase": "Skriv inn passfrase", + "change-passphrase": "Endre passfrase", + "check-access": "Sjekk tilgang", + "check-repository-access-success": "Depottilgang er verifisert!", + "delete-repository-settings-title": "Er du sikker på at du vil slette depotinnstillingene?", + "delete-repository-settings-text": "Vær forsiktig, etter bekreftelse vil depotinnstillingene bli fjernet og versjonskontrollfunksjonen vil være utilgjengelig.", + "auto-commit-settings": "Innstillinger for auto-commit", + "auto-commit": "Auto-commit", + "auto-commit-entities": "Auto-commit-enheter", + "no-auto-commit-entities-prompt": "Ingen enheter konfigurert for auto-commit", + "delete-auto-commit-settings-title": "Er du sikker på at du vil slette auto-commit-innstillingene?", + "delete-auto-commit-settings-text": "Vær forsiktig, etter bekreftelse vil auto-commit-innstillingene bli fjernet og auto-commit vil bli deaktivert for alle enheter.", + "mobile-app": { + "mobile-app": "Mobilapp", + "mobile-app-qr-code-widget-settings": "Innstillinger for QR-kode-widget til mobilapp", + "applications": "Applikasjoner", + "default": "Standard", + "custom": "Tilpasset", + "android": "Android", + "ios": "iOS", + "appearance": "Utseende", + "appearance-on-home-page": "Visning på Hjem-siden", + "enabled": "Aktivert", + "disabled": "Deaktivert", + "badges": "Merker", + "label": "Etikett", + "label-required": "Etikett er påkrevd", + "label-max-length": "Etiketten må være 50 tegn eller færre", + "right": "Høyre", + "left": "Venstre", + "set": "Sett", + "preview": "Forhåndsvisning", + "connect-mobile-app": "Koble til mobilapp", + "use-system-settings": "Bruk systeminnstillinger" + }, + "2fa": { + "2fa": "Tofaktorautentisering", + "available-providers": "Tilgjengelige leverandører", + "issuer-name": "Utstederens navn", + "issuer-name-required": "Utstederens navn er påkrevd.", + "max-verification-failures-before-user-lockout": "Maks antall mislykkede verifiseringer før brukerkonto låses", + "max-verification-failures-before-user-lockout-pattern": "Antall mislykkede forsøk må være et positivt heltall.", + "number-of-checking-attempts": "Antall verifiseringsforsøk", + "number-of-checking-attempts-pattern": "Antall forsøk må være et positivt heltall.", + "number-of-checking-attempts-required": "Antall verifiseringsforsøk er påkrevd.", + "number-of-codes": "Antall koder", + "number-of-codes-pattern": "Antall koder må være et positivt heltall.", + "number-of-codes-required": "Antall koder er påkrevd.", + "provider": "Leverandør", + "retry-verification-code-period": "Ventetid før ny verifiseringskode (sek)", + "retry-verification-code-period-pattern": "Minste tid er 5 sek", + "retry-verification-code-period-required": "Ventetid før ny kode er påkrevd.", + "total-allowed-time-for-verification": "Totalt tillatt tid for verifisering (sek)", + "total-allowed-time-for-verification-pattern": "Minstetid er 60 sek", + "total-allowed-time-for-verification-required": "Totalt tillatt tid er påkrevd.", + "use-system-two-factor-auth-settings": "Bruk systemets tofaktorautentiseringsinnstillinger", + "verification-code-check-rate-limit": "Grense for verifiseringskodekontroll", + "verification-code-lifetime": "Levetid for verifiseringskode (sek)", + "verification-code-lifetime-pattern": "Levetiden må være et positivt heltall.", + "verification-code-lifetime-required": "Levetid for verifiseringskode er påkrevd.", + "verification-message-template": "Meldingsmal for verifisering", + "verification-limitations": "Begrensninger for verifisering", + "verification-message-template-pattern": "Verifiseringsmeldingen må inneholde mønsteret: ${code}", + "verification-message-template-required": "Meldingsmal for verifisering er påkrevd.", + "within-time": "Innen tid (sek)", + "within-time-pattern": "Tiden må være et positivt heltall.", + "within-time-required": "Tid er påkrevd." + }, + "jwt": { + "security-settings": "JWT sikkerhetsinnstillinger", + "issuer-name": "Utstederens navn", + "issuer-name-required": "Utstederens navn er påkrevd.", + "signings-key": "Signeringsnøkkel", + "signings-key-hint": "Base64-kodet streng som representerer minst 512 bits med data.", + "signings-key-required": "Signeringsnøkkel er påkrevd.", + "signings-key-min-length": "Signeringsnøkkelen må være minst 512 bits med data.", + "signings-key-base64": "Signeringsnøkkelen må være i base64-format.", + "expiration-time": "Utløpstid for token (sek)", + "expiration-time-required": "Utløpstid for token er påkrevd.", + "expiration-time-max": "Maksimalt tillatt tid er 2147483647 sekunder (68 år).", + "expiration-time-min": "Minimum tid er 60 sekunder (1 minutt).", + "refresh-expiration-time": "Utløpstid for oppfriskingstoken (sek)", + "refresh-expiration-time-required": "Utløpstid for oppfriskingstoken er påkrevd.", + "refresh-expiration-time-max": "Maksimalt tillatt tid er 2147483647 sekunder (68 år).", + "refresh-expiration-time-min": "Minimum tid er 900 sekunder (15 minutter).", + "refresh-expiration-time-less-token": "Oppfriskingstoken må ha lengre varighet enn token.", + "generate-key": "Generer nøkkel", + "info-header": "Alle brukere må logge inn på nytt", + "info-message": "Endring av JWT-signeringsnøkkel vil ugyldiggjøre alle utstedte tokens. Alle brukere må logge inn på nytt. Dette vil også påvirke skript som bruker Rest API/Websockets." + }, + "resources": "Ressurser", + "notifications": "Varsler", + "notifications-settings": "Innstillinger for varsler", + "slack-api-token": "Slack API-token", + "slack": "Slack", + "slack-settings": "Slack-innstillinger", + "mobile-settings": "Mobilinnstillinger", + "firebase-service-account-file": "Firebase service-konto legitimasjonsfil (JSON)", + "select-firebase-service-account-file": "Dra og slipp din Firebase service-konto legitimasjonsfil eller " + }, + "alarm": { + "alarm": "Alarm", + "alarms": "Alarmer", + "all-alarms": "Alle alarmer", + "select-alarm": "Velg alarm", + "no-alarms-matching": "Ingen alarmer som samsvarer med '{{entity}}' ble funnet.", + "alarm-required": "Alarm er påkrevd", + "alarm-filter": "Alarmfilter", + "filter": "Filter", + "alarm-status": "Alarmstatus", + "alarm-status-list": "Liste over alarmstatus", + "any-status": "Enhver status", + "search-status": { + "ANY": "Enhver", + "ACTIVE": "Aktiv", + "CLEARED": "Fjernet", + "ACK": "Bekreftet", + "UNACK": "Ikke bekreftet" + }, + "display-status": { + "ACTIVE_UNACK": "Aktiv - ikke bekreftet", + "ACTIVE_ACK": "Aktiv - bekreftet", + "CLEARED_UNACK": "Fjernet - ikke bekreftet", + "CLEARED_ACK": "Fjernet - bekreftet" + }, + "no-alarms-prompt": "Ingen alarmer funnet", + "created-time": "Opprettelsestid", + "type": "Type", + "severity": "Alvorlighetsgrad", + "originator": "Opphav", + "originator-type": "Opphavstype", + "details": "Detaljer", + "originator-label": "Opphavsetikett", + "assign": "Tildel", + "assignments": "Tildelinger", + "assignee": "Tildelt", + "assignee-id": "Tildelt ID", + "assignee-first-name": "Tildelts fornavn", + "assignee-last-name": "Tildelts etternavn", + "assignee-email": "Tildelts e-post", + "unassigned": "Ikke tildelt", + "user-deleted": "Bruker slettet", + "assignee-not-set": "Alle", + "status": "Status", + "alarm-details": "Alarmdetaljer", + "start-time": "Starttid", + "assign-time": "Tidspunkt for tildeling", + "end-time": "Sluttid", + "ack-time": "Tidspunkt for bekreftelse", + "clear-time": "Fjernet tid", + "duration": "Varighet", + "alarm-severity": "Alvorlighetsgrad for alarm", + "alarm-severity-list": "Liste over alvorlighetsgrad for alarmer", + "any-severity": "Enhver alvorlighetsgrad", + "severity-critical": "Kritisk", + "severity-major": "Alvorlig", + "severity-minor": "Mindre", + "severity-warning": "Advarsel", + "severity-indeterminate": "Ubestemt", + "acknowledge": "Bekreft", + "clear": "Fjern", + "delete": "Slett", + "search": "Søk i alarmer", + "selected-alarms": "{ count, plural, =1 {1 alarm} other {# alarmer} } valgt", + "no-data": "Ingen data å vise", + "polling-interval": "Spørringsintervall for alarmer (sek)", + "polling-interval-required": "Spørringsintervall for alarmer er påkrevd.", + "min-polling-interval-message": "Minimum tillatt spørringsintervall er 1 sek.", + "aknowledge-alarms-title": "Bekreft { count, plural, =1 {1 alarm} other {# alarmer} }", + "aknowledge-alarms-text": "Er du sikker på at du vil bekrefte { count, plural, =1 {1 alarm} other {# alarmer} }?", + "aknowledge-alarm-title": "Bekreft alarm", + "aknowledge-alarm-text": "Er du sikker på at du vil bekrefte alarmen?", + "selected-alarms-are-acknowledged": "Valgte alarmer er allerede bekreftet", + "clear-alarms-title": "Fjern { count, plural, =1 {1 alarm} other {# alarmer} }", + "clear-alarms-text": "Er du sikker på at du vil fjerne { count, plural, =1 {1 alarm} other {# alarmer} }?", + "clear-alarm-title": "Fjern alarm", + "clear-alarm-text": "Er du sikker på at du vil fjerne alarmen?", + "delete-alarms-title": "Slett { count, plural, =1 {1 alarm} other {# alarmer} }", + "delete-alarms-text": "Er du sikker på at du vil slette { count, plural, =1 {1 alarm} other {# alarmer} }?", + "selected-alarms-are-cleared": "Valgte alarmer er allerede fjernet", + "alarm-status-filter": "Filter for alarmstatus", + "alarm-filter-title": "Alarmfilter", + "assigned": "Tildelt", + "filter-title": "Filter", + "max-count-load": "Maks antall alarmer å laste (0 - ubegrenset)", + "max-count-load-required": "Maks antall alarmer å laste er påkrevd.", + "max-count-load-error-min": "Minimumsverdi er 0.", + "fetch-size": "Hentestørrelse", + "fetch-size-required": "Hentestørrelse er påkrevd.", + "fetch-size-error-min": "Minimumsverdi er 10.", + "alarm-types": "Alarmtyper", + "alarm-type-list": "Liste over alarmtyper", + "any-type": "Enhver type", + "assigned-to-current-user": "Tildelt nåværende bruker", + "assigned-to-me": "Tildelt meg", + "search-propagated-alarms": "Søk etter propagerte alarmer", + "comments": "Alarmkommentarer", + "show-more": "Vis mer", + "additional-info": "Tilleggsinformasjon", + "alarm-type": "Alarmtype", + "enter-alarm-type": "Angi alarmtype", + "no-alarm-types-matching": "Ingen alarmtyper som samsvarer med '{{entitySubtype}}' ble funnet.", + "alarm-type-list-empty": "Ingen alarmtyper valgt." + }, + "alarm-activity": { + "add": "Legg til en kommentar...", + "alarm-comment": "Alarmkommentar", + "comments": "Kommentarer", + "delete-alarm-comment": "Vil du slette denne kommentaren?", + "refresh": "Oppdater", + "oldest-first": "Eldst først", + "newest-first": "Nyest først", + "activity": "Aktivitet", + "export": "Eksporter til CSV", + "author": "Forfatter", + "created-date": "Opprettelsesdato", + "edited-date": "Endret dato", + "text": "Tekst", + "system": "System" + }, + "alias": { + "add": "Legg til alias", + "edit": "Rediger alias", + "name": "Aliasnavn", + "name-required": "Aliasnavn er påkrevd", + "duplicate-alias": "Alias med samme navn eksisterer allerede.", + "filter-type-single-entity": "Enkelt enhet", + "filter-type-entity-list": "Enhetsliste", + "filter-type-entity-name": "Enhetsnavn", + "filter-type-entity-type": "Enhetstype", + "filter-type-state-entity": "Enhet fra dashboard-tilstand", + "filter-type-state-entity-description": "Enhet hentet fra dashboard-tilstandsparametere", + "filter-type-asset-type": "Asset-type", + "filter-type-asset-type-description": "Assets av typen '{{assetTypes}}'", + "filter-type-asset-type-and-name-description": "Assets av typen '{{assetTypes}}' og med navn som begynner med '{{prefix}}'", + "filter-type-device-type": "Enhetstype", + "filter-type-device-type-description": "Enheter av typen '{{deviceTypes}}'", + "filter-type-device-type-and-name-description": "Enheter av typen '{{deviceTypes}}' og med navn som begynner med '{{prefix}}'", + "filter-type-entity-view-type": "Enhetsvisningstype", + "filter-type-entity-view-type-description": "Enhetsvisninger av typen '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description": "Enhetsvisninger av typen '{{entityViewTypes}}' og med navn som begynner med '{{prefix}}'", + "filter-type-edge-type": "Kanttype", + "filter-type-edge-type-description": "Kanter av typen '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description": "Kanter av typen '{{edgeTypes}}' og med navn som begynner med '{{prefix}}'", + "filter-type-relations-query": "Relasjonsforespørsel", + "filter-type-relations-query-description": "{{entities}} som har relasjonen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Søk etter kanter", + "filter-type-edge-search-query-description": "Kanter med type {{edgeTypes}} som har relasjonen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query": "Søk etter assets", + "filter-type-asset-search-query-description": "Assets med typer {{assetTypes}} som har relasjonen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query": "Søk etter enheter", + "filter-type-device-search-query-description": "Enheter med typer {{deviceTypes}} som har relasjonen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query": "Søk etter enhetsvisning", + "filter-type-entity-view-search-query-description": "Enhetsvisninger med typer {{entityViewTypes}} som har relasjonen {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState": "API-bruksstatus", + "entity-filter": "Enhetsfilter", + "resolve-multiple": "Løs som flere enheter", + "resolve-multiple-hint": "Aktiver for å vise data fra alle filtrerte enheter samtidig.\nHvis deaktivert, viser widgeten kun data fra valgt enhet.", + "filter-type": "Filtertype", + "filter-type-required": "Filtertype er påkrevd.", + "entity-filter-no-entity-matched": "Ingen enheter samsvarer med spesifisert filter.", + "no-entity-filter-specified": "Ingen enhetsfilter spesifisert", + "root-state-entity": "Bruk dashboard-tilstandsenhet som rot", + "last-level-relation": "Hent kun siste relasjonsnivå", + "root-entity": "Rotenhet", + "state-entity-parameter-name": "Parameter for tilstandsenhet", + "default-state-entity": "Standard tilstandsenhet", + "default-entity-parameter-name": "Som standard", + "max-relation-level": "Maks relasjonsnivå", + "unlimited-level": "Ubegrenset nivå", + "state-entity": "Dashboard tilstandsenhet", + "all-entities": "Alle enheter", + "any-relation": "enhver" + }, + "asset": { + "asset": "Eiendel", + "assets": "Eiendeler", + "management": "Eiendelsadministrasjon", + "view-assets": "Vis eiendeler", + "add": "Legg til eiendel", + "asset-type-max-length": "Eiendelstypen må være kortere enn 256 tegn", + "assign-to-customer": "Tildel til kunde", + "assign-asset-to-customer": "Tildel eiendel(er) til kunde", + "assign-asset-to-customer-text": "Velg eiendelene som skal tildeles kunden", + "no-assets-text": "Ingen eiendeler funnet", + "assign-to-customer-text": "Velg kunden for å tildele eiendel(en)", + "public": "Offentlig", + "assignedToCustomer": "Tildelt kunde", + "make-public": "Gjør eiendelen offentlig", + "make-private": "Gjør eiendelen privat", + "unassign-from-customer": "Fjern fra kunde", + "delete": "Slett eiendel", + "asset-public": "Eiendelen er offentlig", + "asset-type": "Eiendelstype", + "asset-type-required": "Eiendelstype er påkrevd.", + "select-asset-type": "Velg eiendelstype", + "enter-asset-type": "Angi eiendelsprofil", + "any-asset": "Enhver eiendel", + "no-asset-types-matching": "Ingen eiendelstyper som samsvarer med '{{entitySubtype}}' ble funnet.", + "asset-type-list-empty": "Ingen eiendelstyper valgt.", + "asset-types": "Eiendelstyper", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "name-max-length": "Navnet må være kortere enn 256 tegn", + "label-max-length": "Etiketten må være kortere enn 256 tegn", + "description": "Beskrivelse", + "type": "Type", + "type-required": "Type er påkrevd.", + "details": "Detaljer", + "events": "Hendelser", + "add-asset-text": "Legg til ny eiendel", + "asset-details": "Eiendelsdetaljer", + "assign-assets": "Tildel eiendeler", + "assign-assets-text": "Tildel { count, plural, =1 {1 eiendel} other {# eiendeler} } til kunde", + "assign-asset-to-edge-title": "Tildel eiendel(er) til kant", + "assign-asset-to-edge-text": "Velg eiendeler som skal tildeles kanten", + "delete-assets": "Slett eiendeler", + "unassign-assets": "Fjern eiendeler", + "unassign-assets-action-title": "Fjern { count, plural, =1 {1 eiendel} other {# eiendeler} } fra kunde", + "assign-new-asset": "Tildel ny eiendel", + "delete-asset-title": "Er du sikker på at du vil slette eiendelen '{{assetName}}'?", + "delete-asset-text": "Vær forsiktig, etter bekreftelse vil eiendelen og all tilknyttet data ikke kunne gjenopprettes.", + "delete-assets-title": "Er du sikker på at du vil slette { count, plural, =1 {1 eiendel} other {# eiendeler} }?", + "delete-assets-action-title": "Slett { count, plural, =1 {1 eiendel} other {# eiendeler} }", + "delete-assets-text": "Vær forsiktig, etter bekreftelse vil alle valgte eiendeler og tilknyttet data bli fjernet permanent.", + "make-public-asset-title": "Er du sikker på at du vil gjøre eiendelen '{{assetName}}' offentlig?", + "make-public-asset-text": "Etter bekreftelse vil eiendelen og alle data gjøres offentlig og tilgjengelig for andre.", + "make-private-asset-title": "Er du sikker på at du vil gjøre eiendelen '{{assetName}}' privat?", + "make-private-asset-text": "Etter bekreftelse vil eiendelen og alle data bli private og ikke være tilgjengelige for andre.", + "unassign-asset-title": "Er du sikker på at du vil fjerne eiendelen '{{assetName}}'?", + "unassign-asset-text": "Etter bekreftelse vil eiendelen bli fjernet og ikke være tilgjengelig for kunden.", + "unassign-asset": "Fjern eiendel", + "unassign-assets-title": "Er du sikker på at du vil fjerne { count, plural, =1 {1 eiendel} other {# eiendeler} }?", + "unassign-assets-text": "Etter bekreftelse vil alle valgte eiendeler bli fjernet og ikke være tilgjengelige for kunden.", + "copyId": "Kopier eiendels-ID", + "idCopiedMessage": "Eiendels-ID er kopiert til utklippstavlen", + "select-asset": "Velg eiendel", + "no-assets-matching": "Ingen eiendeler som samsvarer med '{{entity}}' ble funnet.", + "asset-required": "Eiendel er påkrevd", + "name-starts-with": "Navneuttrykk for eiendel", + "help-text": "Bruk '%' etter behov: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search": "Søk etter eiendeler", + "import": "Importer eiendeler", + "asset-file": "Eiendelsfil", + "label": "Etikett", + "assign-asset-to-edge": "Tildel eiendel(er) til kant", + "unassign-asset-from-edge": "Fjern eiendel", + "unassign-asset-from-edge-title": "Er du sikker på at du vil fjerne eiendelen '{{assetName}}'?", + "unassign-asset-from-edge-text": "Etter bekreftelse vil eiendelen bli fjernet og ikke være tilgjengelig for kanten.", + "unassign-assets-from-edge-title": "Er du sikker på at du vil fjerne { count, plural, =1 {1 eiendel} other {# eiendeler} }?", + "unassign-assets-from-edge-text": "Etter bekreftelse vil alle valgte eiendeler bli fjernet og ikke være tilgjengelige for kanten.", + "selected-assets": "{ count, plural, =1 {1 eiendel} other {# eiendeler} } valgt" + }, + "attribute": { + "attributes": "Attributter", + "latest-telemetry": "Siste telemetri", + "no-latest-telemetry": "Ingen siste telemetri", + "attributes-scope": "Omfang for enhetens attributter", + "scope-telemetry": "Telemetri", + "scope-latest-telemetry": "Siste telemetri", + "scope-client": "Klientattributter", + "scope-server": "Serverattributter", + "scope-shared": "Delte attributter", + "scope-client-short": "Klient", + "scope-server-short": "Server", + "scope-shared-short": "Delt", + "scope-latest-short": "Siste", + "scope-any": "Enhver", + "add": "Legg til attributt", + "key": "Nøkkel", + "key-max-length": "Nøkkelen må være kortere enn 256 tegn", + "last-update-time": "Siste oppdateringstid", + "key-required": "Attributtnøkkel er påkrevd.", + "value": "Verdi", + "value-required": "Attributtverdi er påkrevd.", + "telemetry-key-required": "Telemetrinøkkel er påkrevd", + "telemetry-value-required": "Telemetrivid er påkrevd", + "delete-attributes-title": "Er du sikker på at du vil slette { count, plural, =1 {1 attributt} other {# attributter} }?", + "delete-attributes-text": "Vær forsiktig, etter bekreftelse vil alle valgte attributter bli fjernet.", + "delete-attributes": "Slett attributter", + "enter-attribute-value": "Angi attributtverdi", + "show-on-widget": "Vis på widget", + "widget-mode": "Widget-modus", + "next-widget": "Neste widget", + "prev-widget": "Forrige widget", + "add-to-dashboard": "Legg til på dashboard", + "add-widget-to-dashboard": "Legg til widget på dashboard", + "selected-attributes": "{ count, plural, =1 {1 attributt} other {# attributter} } valgt", + "selected-telemetry": "{ count, plural, =1 {1 telemetrienhet} other {# telemetrienheter} } valgt", + "no-attributes-text": "Ingen attributter funnet", + "no-telemetry-text": "Ingen telemetri funnet", + "copy-key": "Kopier nøkkel", + "add-telemetry": "Legg til telemetri", + "copy-value": "Kopier verdi", + "delete-timeseries": { + "start-time": "Starttid", + "ends-on": "Slutter på", + "strategy": "Strategi", + "delete-strategy": "Slettestrategi", + "all-data": "Slett alle data", + "all-data-except-latest-value": "Slett alle data unntatt siste verdi", + "latest-value": "Slett siste verdi", + "all-data-for-time-period": "Slett alle data for tidsperiode", + "rewrite-latest-value": "Overskriv siste verdi" + } + }, + "api-usage": { + "api-features": "API-funksjoner", + "api-usage": "API-bruk", + "alarm": "Alarm", + "alarms-created": "Opprettede alarmer", + "queue-stats": "Køstatistikk", + "processing-failures-and-timeouts": "Behandlingsfeil og tidsavbrudd", + "exceptions": "Unntak", + "alarms-created-daily-activity": "Daglig aktivitet for opprettede alarmer", + "alarms-created-hourly-activity": "Timebasert aktivitet for opprettede alarmer", + "alarms-created-monthly-activity": "Månedlig aktivitet for opprettede alarmer", + "data-points": "Datapunkter", + "data-points-storage-days": "Lagringsdager for datapunkter", + "device-api": "Enhets-API", + "email": "E-post", + "email-messages": "E-postmeldinger", + "email-messages-daily-activity": "Daglig aktivitet for e-postmeldinger", + "email-messages-monthly-activity": "Månedlig aktivitet for e-postmeldinger", + "executions": "Utførelser", + "scripts": "Skript", + "scripts-hourly-activity": "Timebasert aktivitet for skript", + "scripts-daily-activity": "Daglig aktivitet for skript", + "scripts-monthly-activity": "Månedlig aktivitet for skript", + "javascript": "JavaScript", + "javascript-executions": "JavaScript-utførelser", + "tbel": "TBEL", + "tbel-executions": "TBEL-utførelser", + "latest-error": "Siste feil", + "messages": "Meldinger", + "notifications": "Varsler", + "notifications-email-sms": "Varsler (E-post/SMS)", + "notifications-hourly-activity": "Timebasert aktivitet for varsler", + "permanent-failures": "${entityName} Permanente feil", + "permanent-timeouts": "${entityName} Permanente tidsavbrudd", + "processing-failures": "${entityName} Behandlingsfeil", + "processing-timeouts": "${entityName} Behandlingstidsavbrudd", + "rule-chain": "Regelkjede", + "rule-engine": "Regelmotor", + "rule-engine-daily-activity": "Daglig aktivitet for regelmotor", + "rule-engine-executions": "Utførelser av regelmotor", + "rule-engine-hourly-activity": "Timebasert aktivitet for regelmotor", + "rule-engine-monthly-activity": "Månedlig aktivitet for regelmotor", + "rule-engine-statistics": "Statistikk for regelmotor", + "rule-node": "Regelnode", + "sms": "SMS", + "sms-messages": "SMS-meldinger", + "sms-messages-daily-activity": "Daglig aktivitet for SMS-meldinger", + "sms-messages-monthly-activity": "Månedlig aktivitet for SMS-meldinger", + "successful": "${entityName} Vellykket", + "telemetry": "Telemetri", + "telemetry-persistence": "Telemetrilagring", + "telemetry-persistence-daily-activity": "Daglig aktivitet for telemetrilagring", + "telemetry-persistence-hourly-activity": "Timebasert aktivitet for telemetrilagring", + "telemetry-persistence-monthly-activity": "Månedlig aktivitet for telemetrilagring", + "transport": "Transport", + "transport-daily-activity": "Daglig aktivitet for transport", + "transport-data-points": "Transportdatapunkter", + "transport-hourly-activity": "Timebasert aktivitet for transport", + "transport-messages": "Transportmeldinger", + "transport-monthly-activity": "Månedlig aktivitet for transport", + "view-details": "Vis detaljer", + "view-statistics": "Vis statistikk" + }, + "api-limit": { + "cassandra-queries": "Cassandra-forespørsler", + "entity-version-creation": "Oppretting av enhetsversjon", + "entity-version-load": "Innlasting av enhetsversjon", + "notification-requests": "Varslingsforespørsler", + "notification-requests-per-rule": "Varslingsforespørsler per regel", + "rest-api-requests": "REST API-forespørsler", + "rest-api-requests-per-customer": "REST API-forespørsler per kunde", + "transport-messages": "Transportmeldinger", + "transport-messages-per-device": "Transportmeldinger per enhet", + "transport-messages-per-gateway": "Transportmeldinger per gateway", + "transport-messages-per-gateway-device": "Transportmeldinger per gateway-enhet", + "ws-updates-per-session": "WebSocket-oppdateringer per økt", + "edge-events": "Edge-hendelser", + "edge-events-per-edge": "Edge-hendelser per edge", + "edge-uplink-messages": "Edge-opplinkmeldinger", + "edge-uplink-messages-per-edge": "Edge-opplinkmeldinger per edge" + }, + "audit-log": { + "audit": "Revisjon", + "audit-logs": "Revisjonslogger", + "timestamp": "Tidsstempel", + "entity-type": "Enhetstype", + "entity-name": "Enhetsnavn", + "user": "Bruker", + "type": "Type", + "status": "Status", + "details": "Detaljer", + "type-added": "Lagt til", + "type-deleted": "Slettet", + "type-updated": "Oppdatert", + "type-attributes-updated": "Attributter oppdatert", + "type-attributes-deleted": "Attributter slettet", + "type-rpc-call": "RPC-kall", + "type-credentials-updated": "Legitimasjon oppdatert", + "type-assigned-to-customer": "Tildelt kunde", + "type-unassigned-from-customer": "Fjernet fra kunde", + "type-assigned-to-edge": "Tildelt edge", + "type-unassigned-from-edge": "Fjernet fra edge", + "type-activated": "Aktivert", + "type-suspended": "Suspendert", + "type-credentials-read": "Legitimasjon lest", + "type-attributes-read": "Attributter lest", + "type-relation-add-or-update": "Relasjon oppdatert", + "type-relation-delete": "Relasjon slettet", + "type-relations-delete": "Alle relasjoner slettet", + "type-alarm-ack": "Alarm bekreftet", + "type-alarm-clear": "Alarm fjernet", + "type-alarm-delete": "Alarm slettet", + "type-alarm-assign": "Alarm tildelt", + "type-alarm-unassign": "Alarm fjernet", + "type-added-comment": "Kommentar lagt til", + "type-updated-comment": "Kommentar oppdatert", + "type-deleted-comment": "Kommentar slettet", + "type-login": "Innlogging", + "type-logout": "Utlogging", + "type-lockout": "Utestengt", + "status-success": "Vellykket", + "status-failure": "Mislykket", + "audit-log-details": "Detaljer for revisjonslogg", + "no-audit-logs-prompt": "Ingen logger funnet", + "action-data": "Handlingsdata", + "failure-details": "Feildetaljer", + "search": "Søk i revisjonslogger", + "clear-search": "Fjern søk", + "type-assigned-from-tenant": "Tildelt fra leietaker", + "type-assigned-to-tenant": "Tildelt til leietaker", + "type-provision-success": "Enhet klargjort", + "type-provision-failure": "Klargjøring av enhet mislyktes", + "type-timeseries-updated": "Telemetri oppdatert", + "type-timeseries-deleted": "Telemetri slettet", + "type-sms-sent": "SMS sendt" + }, + "debug-settings": { + "label": "Feilsøkingskonfigurasjon", + "on-failure": "Kun feil (24/7)", + "all-messages": "Alle meldinger ({{time}})", + "failures": "Feil", + "entity": "enhet", + "hint": { + "main-limited": "Ikke mer enn {{msg}} {{entity}} feilsøkingsmeldinger per {{time}} vil bli registrert.", + "on-failure": "Logg kun feilmeldinger.", + "all-messages": "Logg alle feilsøkingsmeldinger." + } + }, + "calculated-fields": { + "expression": "Uttrykk", + "no-found": "Ingen beregnede felt funnet", + "list": "{ count, plural, =1 {Ett beregnet felt} other {Liste med # beregnede felt} }", + "selected-fields": "{ count, plural, =1 {1 beregnet felt} other {# beregnede felt} } valgt", + "type": { + "simple": "Enkel", + "script": "Skript" + }, + "arguments": "Argumenter", + "decimals-by-default": "Desimaler som standard", + "debugging": "Feilsøking av beregnet felt", + "argument-name": "Argumentnavn", + "datasource": "Datakilde", + "add-argument": "Legg til argument", + "test-script-function": "Test skriptfunksjon", + "no-arguments": "Ingen argumenter konfigurert", + "argument-settings": "Innstillinger for argument", + "argument-current": "Gjeldende enhet", + "argument-current-tenant": "Gjeldende leietaker", + "argument-device": "Enhet", + "argument-asset": "Eiendel", + "argument-customer": "Kunde", + "argument-tenant": "Gjeldende leietaker", + "argument-type": "Argumenttype", + "see-debug-events": "Se feilsøkingshendelser", + "attribute": "Attributt", + "copy-argument-name": "Kopier argumentnavn", + "timeseries-key": "Tidsserienøkkel", + "device-name": "Enhetsnavn", + "latest-telemetry": "Siste telemetri", + "rolling": "Rullende tidsserie", + "attribute-scope": "Attributtomfang", + "server-attributes": "Serverattributter", + "client-attributes": "Klientattributter", + "shared-attributes": "Delte attributter", + "attribute-key": "Attributtnøkkel", + "default-value": "Standardverdi", + "limit": "Maksverdier", + "time-window": "Tidsvindu", + "customer-name": "Kundenavn", + "asset-name": "Eiendelsnavn", + "timeseries": "Tidsserie", + "output": "Utdata", + "create": "Opprett nytt beregnet felt", + "file": "Fil for beregnet felt", + "invalid-file-error": "Ugyldig filformat. Vennligst sørg for at filen er en gyldig JSON-fil.", + "import": "Importer beregnet felt", + "export": "Eksporter beregnet felt", + "export-failed-error": "Kan ikke eksportere beregnet felt: {{error}}", + "output-type": "Utdataformat", + "delete-title": "Er du sikker på at du vil slette det beregnede feltet '{{title}}'?", + "delete-text": "Vær forsiktig, etter bekreftelse vil det beregnede feltet og all relatert data ikke kunne gjenopprettes.", + "delete-multiple-title": "Er du sikker på at du vil slette { count, plural, =1 {1 beregnet felt} other {# beregnede felt} }?", + "delete-multiple-text": "Vær forsiktig, etter bekreftelse vil alle valgte beregnede felt bli fjernet og all relatert data vil ikke kunne gjenopprettes.", + "test-with-this-message": "Test med denne meldingen", + "hint": { + "arguments-simple-with-rolling": "Beregnede felt av enkel type bør ikke inneholde nøkler med rullende tidsserietype.", + "arguments-empty": "Argumenter skal ikke være tomme.", + "expression-required": "Uttrykk er påkrevd.", + "expression-invalid": "Uttrykk er ugyldig", + "expression-max-length": "Uttrykket må være kortere enn 255 tegn.", + "argument-name-required": "Argumentnavn er påkrevd.", + "argument-name-pattern": "Argumentnavn er ugyldig.", + "argument-name-duplicate": "Et argument med dette navnet finnes allerede.", + "argument-name-max-length": "Argumentnavn må være kortere enn 256 tegn.", + "argument-name-forbidden": "Argumentnavnet er reservert og kan ikke brukes.", + "argument-type-required": "Argumenttype er påkrevd.", + "max-args": "Maksimalt antall argumenter er nådd.", + "decimals-range": "Desimaler som standard må være et tall mellom 0 og 15.", + "expression": "Standarduttrykket viser hvordan man konverterer temperatur fra Fahrenheit til Celsius.", + "arguments-entity-not-found": "Målenheten for argument ikke funnet." + } + }, + "confirm-on-exit": { + "message": "Du har ulagrede endringer. Er du sikker på at du vil forlate denne siden?", + "html-message": "Du har ulagrede endringer.
Er du sikker på at du vil forlate denne siden?", + "title": "Ulagrede endringer" + }, + "contact": { + "country": "Land", + "country-required": "Land er påkrevd.", + "city": "By", + "state": "Stat / Provins", + "postal-code": "Postnummer", + "postal-code-invalid": "Ugyldig format på postnummer.", + "address": "Adresse", + "address2": "Adresse 2", + "phone": "Telefon", + "email": "E-post", + "no-address": "Ingen adresse", + "no-country-found": "Ingen land funnet.", + "no-country-matching": "Ingen land som matcher '{{country}}' ble funnet.", + "state-max-length": "Statens navn må være kortere enn 256 tegn", + "phone-max-length": "Telefonnummeret må være kortere enn 256 tegn", + "city-max-length": "Spesifisert by må være kortere enn 256 tegn" + }, + "common": { + "name": "Navn", + "type": "Type", + "general": "Generelt", + "username": "Brukernavn", + "password": "Passord", + "data": "Data", + "timestamp": "Tidsstempel", + "enter-username": "Skriv inn brukernavn", + "enter-password": "Skriv inn passord", + "enter-search": "Skriv inn søk", + "created-time": "Opprettet tid", + "disabled": "Deaktivert", + "loading": "Laster...", + "proceed": "Fortsett", + "open-details-page": "Åpne detaljside", + "not-found": "Ikke funnet", + "value": "Verdi", + "documentation": "Dokumentasjon", + "time-left": "{{time}} igjen", + "output": "Utdata", + "suffix": { + "s": "s", + "ms": "ms" + }, + "hint": { + "name-required": "Navn er påkrevd.", + "name-pattern": "Navnet er ugyldig.", + "name-max-length": "Navnet må være kortere enn 256 tegn.", + "title-required": "Tittel er påkrevd.", + "title-pattern": "Tittelen er ugyldig.", + "title-max-length": "Tittelen må være kortere enn 256 tegn.", + "key-required": "Nøkkel er påkrevd.", + "key-pattern": "Nøkkelen er ugyldig.", + "key-max-length": "Nøkkelen må være kortere enn 256 tegn." + }, + "required-fields": "Påkrevde felt mangler" + }, + "content-type": { + "json": "Json", + "text": "Tekst", + "binary": "Binær (Base64)" + }, + "color": { + "color": "Farge" + }, + "customer": { + "customer": "Kunde", + "customers": "Kunder", + "management": "Kundeadministrasjon", + "dashboard": "Kundedashboard", + "dashboards": "Kundedashboarder", + "devices": "Kundeenheter", + "entity-views": "Kunde Entity Views", + "assets": "Kundeeiendeler", + "public-dashboards": "Offentlige dashboarder", + "public-devices": "Offentlige enheter", + "public-assets": "Offentlige eiendeler", + "public-entity-views": "Offentlige Entity Views", + "add": "Legg til kunde", + "delete": "Slett kunde", + "manage-customer-users": "Administrer kundebrukere", + "manage-customer-devices": "Administrer kundeenheter", + "manage-customer-dashboards": "Administrer kundedashboarder", + "manage-public-devices": "Administrer offentlige enheter", + "manage-public-dashboards": "Administrer offentlige dashboarder", + "manage-customer-assets": "Administrer kundeeiendeler", + "manage-customer-edges": "Administrer kunde edge-enheter", + "manage-public-assets": "Administrer offentlige eiendeler", + "add-customer-text": "Legg til ny kunde", + "no-customers-text": "Ingen kunder funnet", + "customer-details": "Kundedetaljer", + "delete-customer-title": "Er du sikker på at du vil slette kunden '{{customerTitle}}'?", + "delete-customer-text": "Vær forsiktig, etter bekreftelse vil kunden og all tilknyttet data ikke kunne gjenopprettes.", + "delete-customers-title": "Er du sikker på at du vil slette { count, plural, =1 {1 kunde} other {# kunder} }?", + "delete-customers-action-title": "Slett { count, plural, =1 {1 kunde} other {# kunder} }", + "delete-customers-text": "Vær forsiktig, etter bekreftelse vil alle valgte kunder bli fjernet og tilknyttet data vil ikke kunne gjenopprettes.", + "manage-users": "Administrer brukere", + "manage-assets": "Administrer eiendeler", + "manage-devices": "Administrer enheter", + "manage-dashboards": "Administrer dashboarder", + "title": "Tittel", + "title-required": "Tittel er påkrevd.", + "title-max-length": "Tittel bør være mindre enn 256 tegn", + "description": "Beskrivelse", + "details": "Detaljer", + "events": "Hendelser", + "copyId": "Kopier kunde-ID", + "idCopiedMessage": "Kunde-ID er kopiert til utklippstavlen", + "select-customer": "Velg kunde", + "no-customers-matching": "Ingen kunder som samsvarer med '{{entity}}' ble funnet.", + "customer-required": "Kunde er påkrevd", + "select-default-customer": "Velg standard kunde", + "default-customer": "Standard kunde", + "default-customer-required": "Standard kunde er påkrevd for å feilsøke dashboard på leiernivå", + "search": "Søk kunder", + "selected-customers": "{ count, plural, =1 {1 kunde} other {# kunder} } valgt", + "edges": "Kundens edge-instanser", + "manage-edges": "Administrer edge-enheter" + }, + "css-size": { + "size-value-required": "Størrelsesverdi er påkrevd", + "invalid-size-value": "Ugyldig størrelsesverdi" + }, + "date": { + "last-update-n-ago": "Sist oppdatert for N siden", + "last-update-n-ago-text": "Sist oppdatert {{ agoText }}", + "custom-date": "Egendefinert dato", + "format": "Format", + "preview": "Forhåndsvisning", + "auto": "Auto", + "time-granularity-formats": "Tidsgranularitetsformater", + "unit-year": "År", + "unit-month": "Måneder", + "unit-day": "Dager", + "unit-hour": "Timer", + "unit-minute": "Minutter", + "unit-second": "Sekunder", + "unit-millisecond": "Millisekunder" + }, + "datetime": { + "date-from": "Dato fra", + "time-from": "Tid fra", + "date-to": "Dato til", + "time-to": "Tid til", + "from": "Fra", + "to": "Til" + }, + "dashboard": { + "dashboard": "Dashbord", + "dashboards": "Dashbord", + "management": "Dashbordadministrasjon", + "view-dashboards": "Vis dashbord", + "add": "Legg til dashbord", + "assign-dashboard-to-customer": "Tildel dashbord til kunde", + "assign-dashboard-to-customer-text": "Velg dashbord som skal tildeles kunden", + "assign-to-customer-text": "Velg kunden som dashbordet skal tildeles", + "assign-to-customer": "Tildel til kunde", + "unassign-from-customer": "Fjern tildeling fra kunde", + "make-public": "Gjør dashbord offentlig", + "make-private": "Gjør dashbord privat", + "manage-assigned-customers": "Administrer tildelte kunder", + "assigned-customers": "Tildelte kunder", + "assign-to-customers": "Tildel dashbord til kunder", + "assign-to-customers-text": "Velg kunder som dashbordet skal tildeles", + "unassign-from-customers": "Fjern dashbord fra kunder", + "unassign-from-customers-text": "Velg kunder som dashbordet skal fjernes fra", + "no-dashboards-text": "Ingen dashbord funnet", + "no-widgets": "Ingen widgets konfigurert", + "add-widget": "Legg til ny widget", + "add-widget-button-text": "Legg til widget", + "title": "Tittel", + "image": "Dashbordbilde", + "mobile-app-settings": "Innstillinger for mobilapp", + "mobile-order": "Rekkefølge i mobilapp", + "mobile-hide": "Skjul dashbord i mobilapp", + "update-image": "Oppdater dashbordbilde", + "take-screenshot": "Ta skjermbilde", + "select-widget-title": "Velg widget", + "select-widget-value": "{{title}}: velg widget", + "select-widget-subtitle": "Liste over tilgjengelige widgettyper", + "delete": "Slett dashbord", + "title-required": "Tittel er påkrevd.", + "title-max-length": "Tittelen bør være under 256 tegn", + "description": "Beskrivelse", + "details": "Detaljer", + "dashboard-details": "Dashborddetaljer", + "add-dashboard-text": "Legg til nytt dashbord", + "assign-dashboards": "Tildel dashbord", + "assign-new-dashboard": "Tildel nytt dashbord", + "assign-dashboards-text": "Tildel { count, plural, =1 {1 dashbord} other {# dashbord} } til kunder", + "unassign-dashboards-action-text": "Fjern { count, plural, =1 {1 dashbord} other {# dashbord} } fra kunder", + "delete-dashboards": "Slett dashbord", + "unassign-dashboards": "Fjern dashbord", + "unassign-dashboards-action-title": "Fjern { count, plural, =1 {1 dashbord} other {# dashbord} } fra kunde", + "delete-dashboard-title": "Er du sikker på at du vil slette dashbordet '{{dashboardTitle}}'?", + "delete-dashboard-text": "Vær forsiktig, etter bekreftelse blir dashbordet og alle relaterte data ikke gjenopprettbare.", + "delete-dashboards-title": "Er du sikker på at du vil slette { count, plural, =1 {1 dashbord} other {# dashbord} }?", + "delete-dashboards-action-title": "Slett { count, plural, =1 {1 dashbord} other {# dashbord} }", + "delete-dashboards-text": "Vær forsiktig, etter bekreftelse blir alle valgte dashbord og relaterte data fjernet permanent.", + "unassign-dashboard-title": "Er du sikker på at du vil fjerne dashbordet '{{dashboardTitle}}' fra kunden?", + "unassign-dashboard-text": "Etter bekreftelse vil dashbordet ikke være tilgjengelig for kunden.", + "unassign-dashboard": "Fjern dashbord", + "unassign-dashboards-title": "Er du sikker på at du vil fjerne { count, plural, =1 {1 dashbord} other {# dashbord} }?", + "unassign-dashboards-text": "Etter bekreftelse blir alle valgte dashbord fjernet fra kunden.", + "public-dashboard-title": "Dashbordet er nå offentlig", + "public-dashboard-text": "Ditt dashbord {{dashboardTitle}} er nå offentlig og tilgjengelig via denne lenken:", + "public-dashboard-notice": "Merk: Husk å gjøre tilhørende enheter offentlige for å få tilgang til dataene.", + "make-private-dashboard-title": "Er du sikker på at du vil gjøre dashbordet '{{dashboardTitle}}' privat?", + "make-private-dashboard-text": "Etter bekreftelse blir dashbordet privat og ikke tilgjengelig for andre.", + "make-private-dashboard": "Gjør dashbord privat", + "socialshare-text": "'{{dashboardTitle}}' levert av ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' levert av ThingsBoard", + "select-dashboard": "Velg dashbord", + "no-dashboards-matching": "Ingen dashbord som samsvarer med '{{entity}}' ble funnet.", + "dashboard-required": "Dashbord er påkrevd.", + "select-existing": "Velg eksisterende dashbord", + "create-new": "Opprett nytt dashbord", + "new-dashboard-title": "Tittel for nytt dashbord", + "open-dashboard": "Åpne dashbord", + "set-background": "Angi bakgrunn", + "background-color": "Bakgrunnsfarge", + "background-image": "Bakgrunnsbilde", + "background-size-mode": "Bakgrunnsstørrelsesmodus", + "no-image": "Ingen bilde valgt", + "empty-image": "Ingen bilde", + "drop-image": "Slipp et bilde eller klikk for å velge en fil å laste opp.", + "maximum-upload-file-size": "Maksimal filstørrelse for opplasting: {{ size }}", + "cannot-upload-file": "Kan ikke laste opp fil", + "settings": "Innstillinger", + "move-all-widgets": "Flytt alle widgets", + "move-by": "Flytt med", + "cols": "kolonner", + "rows": "rader", + "layout": "Oppsett", + "layout-type-default": "Standard", + "layout-type-scada": "SCADA", + "layout-type-divider": "Skillelinje", + "layout-settings-type": "Oppsettinnstillinger: {{ type }} breakpoint", + "columns-count": "Antall kolonner", + "columns-count-required": "Antall kolonner er påkrevd.", + "min-columns-count-message": "Minimum 10 kolonner er tillatt.", + "max-columns-count-message": "Maksimum 1000 kolonner er tillatt.", + "min-layout-width": "Minimum oppsettbredde", + "columns-suffix": "kolonner", + "widgets-margins": "Mellomrom mellom widgets", + "margin-required": "Margverdi er påkrevd.", + "min-margin-message": "Minimum tillatt margverdi er 0.", + "max-margin-message": "Maksimum tillatt margverdi er 50.", + "horizontal-margin": "Horisontal marg", + "horizontal-margin-required": "Horisontal margverdi er påkrevd.", + "min-horizontal-margin-message": "Minimum tillatt horisontal margverdi er 0.", + "max-horizontal-margin-message": "Maksimum tillatt horisontal margverdi er 50.", + "vertical-margin": "Vertikal marg", + "vertical-margin-required": "Vertikal margverdi er påkrevd.", + "min-vertical-margin-message": "Minimum tillatt vertikal margverdi er 0.", + "max-vertical-margin-message": "Maksimum tillatt vertikal margverdi er 50.", + "apply-outer-margin": "Bruk marg på sidene av oppsettet", + "autofill-height": "Autofyll oppsetthøyde", + "mobile-layout": "Mobiloppsettinnstillinger", + "mobile-row-height": "Radhøyde for mobil", + "mobile-row-height-required": "Radhøydeverdi for mobil er påkrevd.", + "min-mobile-row-height-message": "Minimum 5 piksler er tillatt for radhøyde på mobil.", + "max-mobile-row-height-message": "Maksimum 200 piksler er tillatt for radhøyde på mobil.", + "row-height": "Radhøyde", + "row-height-required": "Radhøydeverdi er påkrevd.", + "min-row-height-message": "Minimum 5 piksler er tillatt for radhøyde.", + "max-row-height-message": "Maksimum 200 piksler er tillatt for radhøyde.", + "display-first-in-mobile-view": "Vis først i mobilvisning", + "title-settings": "Tittelinnstillinger", + "display-title": "Vis dashbordtittel", + "title-color": "Tittelfarge", + "toolbar-settings": "Verktøylinjeinnstillinger", + "hide-toolbar": "Skjul verktøylinje", + "toolbar-always-open": "Hold verktøylinje åpen", + "display-dashboards-selection": "Vis dashbordvalg", + "display-entities-selection": "Vis enhetsvalg", + "display-filters": "Vis filtre", + "display-dashboard-timewindow": "Vis tidsvindu", + "display-dashboard-export": "Vis eksport", + "display-update-dashboard-image": "Vis oppdater dashbordbilde", + "dashboard-logo-settings": "Logo-innstillinger for dashbord", + "display-dashboard-logo": "Vis logo i dashbordets fullskjermmodus", + "dashboard-logo-image": "Dashbordlogo bilde", + "advanced-settings": "Avanserte innstillinger", + "dashboard-css": "Dashbord CSS", + "import": "Importer dashbord", + "export": "Eksporter dashbord", + "export-failed-error": "Kan ikke eksportere dashbord: {{error}}", + "export-prompt": "Bygg inn bilder og ressurser i dashbord", + "create-new-dashboard": "Opprett nytt dashbord", + "dashboard-file": "Dashbordfil", + "invalid-dashboard-file-error": "Kan ikke importere dashbord: Ugyldig datastruktur.", + "dashboard-import-missing-aliases-title": "Konfigurer alias brukt av det importerte dashbordet", + "create-new-widget": "Opprett ny widget", + "import-widget": "Importer widget", + "widget-file": "Widgetfil", + "invalid-widget-file-error": "Kan ikke importere widget: Ugyldig datastruktur for widget.", + "widget-import-missing-aliases-title": "Konfigurer alias brukt av importert widget", + "open-toolbar": "Åpne dashbordverktøylinje", + "close-toolbar": "Lukk verktøylinje", + "configuration-error": "Konfigurasjonsfeil", + "alias-resolution-error-title": "Feil i dashbordalias-konfigurasjon", + "invalid-aliases-config": "Kan ikke finne noen enheter som samsvarer med noen av aliasfiltrene.
Vennligst kontakt administrator for å løse dette problemet.", + "select-devices": "Velg enheter", + "assignedToCustomer": "Tildelt kunde", + "assignedToCustomers": "Tildelt kunder", + "public": "Offentlig", + "copyId": "Kopier dashbord-id", + "idCopiedMessage": "Dashbord-id har blitt kopiert til utklippstavlen", + "public-link": "Offentlig lenke", + "copy-public-link": "Kopier offentlig lenke", + "public-link-copied-message": "Offentlig dashbordlenke har blitt kopiert til utklippstavlen", + "manage-states": "Administrer dashbordtilstander", + "states": "Dashbordtilstander", + "states-short": "Tilstander", + "search-states": "Søk i dashbordtilstander", + "selected-states": "{ count, plural, =1 {1 dashbordtilstand} other {# dashbordtilstander} } valgt", + "edit-state": "Rediger dashbordtilstand", + "delete-state": "Slett dashbordtilstand", + "add-state": "Legg til dashbordtilstand", + "no-states-text": "Ingen tilstander funnet", + "state": "Dashbordtilstand", + "state-name": "Navn", + "state-name-required": "Navn på dashbordtilstand er påkrevd.", + "state-id": "Tilstands-id", + "state-id-required": "Tilstands-id for dashbord er påkrevd.", + "state-id-exists": "Dashbordtilstand med samme id finnes allerede.", + "is-root-state": "Rottilstand", + "delete-state-title": "Slett dashbordtilstand", + "delete-state-text": "Er du sikker på at du vil slette dashbordtilstanden med navn '{{stateName}}'?", + "show-details": "Vis detaljer", + "hide-details": "Skjul detaljer", + "select-state": "Velg måtilstand", + "state-controller": "Tilstandskontroller", + "state-controller-default": "statisk (utdatert)", + "search": "Søk i dashbord", + "selected-dashboards": "{ count, plural, =1 {1 dashbord} other {# dashbord} } valgt", + "home-dashboard": "Hjemmedashbord", + "home-dashboard-hide-toolbar": "Skjul verktøylinje for hjemmedashbord", + "unassign-dashboard-from-edge-text": "Etter bekreftelse vil dashbordet bli fjernet og ikke lenger være tilgjengelig for edge.", + "unassign-dashboards-from-edge-title": "Er du sikker på at du vil fjerne { count, plural, =1 {1 dashbord} other {# dashbord} }?", + "unassign-dashboards-from-edge-text": "Etter bekreftelse vil alle valgte dashbord bli fjernet og ikke lenger være tilgjengelig for edge.", + "assign-dashboard-to-edge": "Tildel dashbord til edge", + "assign-dashboard-to-edge-text": "Vennligst velg dashbordene du vil tildele til edge", + "non-existent-dashboard-state-error": "Dashbordtilstand med id \"{{ stateId }}\" ble ikke funnet", + "edit-mode": "Redigeringsmodus", + "duplicate-state-action": "Dupliser tilstand", + "breakpoint-value": "Brytpunkt ({{ value }})", + "breakpoints-id": { + "default": "Standard", + "xs": "Mobil (xs)", + "sm": "Nettbrett (sm)", + "md": "Bærbar (md)", + "lg": "Stasjonær (lg)", + "xl": "Stasjonær (xl)" + }, + "view-format-type-grid": "Rutenett", + "view-format-type-list": "Liste", + "view-format": "Visningsformat" + }, + "datakey": { + "settings": "Innstillinger", + "general": "Generelt", + "advanced": "Avansert", + "key": "Nøkkel", + "keys": "Nøkler", + "label": "Etikett", + "color": "Farge", + "units": "Spesialtegn for visning ved siden av verdi", + "decimals": "Antall sifre etter desimalpunkt", + "data-generation-func": "Datagenereringsfunksjon", + "use-data-post-processing-func": "Bruk funksjon for etterbehandling av data", + "configuration": "Datakonfigurasjon", + "timeseries": "Tidsserie", + "attributes": "Attributter", + "entity-field": "Enhetsfelt", + "alarm": "Alarmfelt", + "timeseries-required": "Enhetstidsserier er påkrevd.", + "timeseries-or-attributes-required": "Enhetstidsserier/-attributter er påkrevd.", + "alarm-fields-timeseries-or-attributes-required": "Alarmfelt eller enhetstidsserier/-attributter er påkrevd.", + "maximum-timeseries-or-attributes": "Maksimalt { count, plural, =1 {1 tidsserie/attributt tillatt.} other {# tidsserier/attributter tillatt} }", + "alarm-fields-required": "Alarmfelt er påkrevd.", + "function-types": "Funksjonstyper", + "function-type": "Funksjonstype", + "function-types-required": "Funksjonstyper er påkrevd.", + "data-keys": "Datataster", + "data-key": "Datatast", + "data-keys-required": "Datataster er påkrevd.", + "data-key-required": "Datatast er påkrevd.", + "alarm-keys": "Alarmdatataster", + "alarm-key": "Alarmdatatast", + "alarm-key-functions": "Alarmnøkkelfunksjoner", + "alarm-key-function": "Alarmnøkkelfunksjon", + "latest-keys": "Nyeste datataster", + "latest-key": "Nyeste datatast", + "latest-key-functions": "Nyeste nøkkelfunksjoner", + "latest-key-function": "Nyeste nøkkelfunksjon", + "timeseries-keys": "Tidsseriedatataster", + "timeseries-key": "Tidsseriedatatast", + "timeseries-key-functions": "Tidsserienøkkelfunksjoner", + "timeseries-key-function": "Tidsserienøkkelfunksjon", + "maximum-function-types": "Maksimalt { count, plural, =1 {1 funksjonstype tillatt.} other {# funksjonstyper tillatt} }", + "time-description": "tidsstempel for gjeldende verdi;", + "value-description": "gjeldende verdi;", + "prev-value-description": "resultat fra forrige funksjonskall;", + "time-prev-description": "tidsstempel for forrige verdi;", + "prev-orig-value-description": "opprinnelig forrige verdi;", + "aggregation": "Aggregering", + "aggregation-type-hint-common": "Av ytelsesgrunner er beregning av aggregerte verdier kun tilgjengelig for faste tidsintervaller som \"nåværende dag\", \"nåværende måned\" osv., og ikke tilgjengelig for glidende intervaller som 'siste 30 minutter' eller 'siste 24 timer'.", + "aggregation-type-none-hint": "Bruk siste verdi.", + "aggregation-type-min-hint": "Finn minste verdi blant datapunktene i valgt tidsvindu.", + "aggregation-type-max-hint": "Finn største verdi blant datapunktene i valgt tidsvindu.", + "aggregation-type-avg-hint": "Beregn gjennomsnittlig verdi blant datapunktene i valgt tidsvindu.", + "aggregation-type-sum-hint": "Summer alle verdier i datapunktene i valgt tidsvindu.", + "aggregation-type-count-hint": "Totalt antall datapunkter i valgt tidsvindu.", + "delta-calculation": "Delta-beregning", + "enable-delta-calculation": "Aktiver delta-beregning", + "enable-delta-calculation-hint": "Når aktivert, beregnes datanøkkelverdien basert på aggregerte verdier for valgt tidsvindu og spesifisert sammenligningsperiode. Av ytelsesgrunner er delta-beregning bare tilgjengelig for historiske tidsvinduer og ikke for sanntidsverdier.", + "delta-calculation-result": "Resultat av delta-beregning", + "delta-calculation-result-previous-value": "Forrige verdi", + "delta-calculation-result-delta-absolute": "Delta (absolutt)", + "delta-calculation-result-delta-percent": "Delta (prosent)", + "source": "Kilde", + "latest": "Nyeste", + "latest-value": "Nyeste verdi", + "delta": "delta", + "percent": "prosent", + "absolute": "absolutt" + }, + "datasource": { + "type": "Datakildetype", + "name": "Navn", + "label": "Etikett", + "add-datasource-prompt": "Vennligst legg til datakilde" + }, + "details": { + "details": "Detaljer", + "edit-mode": "Redigeringsmodus", + "edit-json": "Rediger JSON", + "toggle-edit-mode": "Bytt redigeringsmodus" + }, + "device": { + "device": "Enhet", + "device-required": "Enhet er påkrevd.", + "devices": "Enheter", + "management": "Enhetsadministrasjon", + "view-devices": "Vis enheter", + "device-alias": "Enhetsalias", + "device-type-max-length": "Enhetstype bør være mindre enn 256 tegn", + "aliases": "Enhetsaliaser", + "no-alias-matching": "'{{alias}}' ikke funnet.", + "no-aliases-found": "Ingen aliaser funnet.", + "no-key-matching": "'{{key}}' ikke funnet.", + "no-keys-found": "Ingen nøkler funnet.", + "create-new-alias": "Opprett en ny!", + "create-new-key": "Opprett en ny!", + "duplicate-alias-error": "Duplisert alias funnet '{{alias}}'.
Enhetsaliaser må være unike innenfor dashbordet.", + "configure-alias": "Konfigurer alias '{{alias}}'", + "no-devices-matching": "Ingen enheter som matcher '{{entity}}' ble funnet.", + "alias": "Alias", + "alias-required": "Enhetsalias er påkrevd.", + "remove-alias": "Fjern enhetsalias", + "add-alias": "Legg til enhetsalias", + "name-starts-with": "Enhetsnavnuttrykk", + "help-text": "Bruk '%' etter behov: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list": "Enhetsliste", + "use-device-name-filter": "Bruk filter", + "device-list-empty": "Ingen enheter valgt.", + "device-name-filter-required": "Enhetsnavnfilter er påkrevd.", + "device-name-filter-no-device-matched": "Ingen enheter som starter med '{{device}}' ble funnet.", + "add": "Legg til enhet", + "assign-to-customer": "Tildel til kunde", + "assign-device-to-customer": "Tildel enhet(er) til kunde", + "assign-device-to-customer-text": "Vennligst velg enhetene som skal tildeles kunden", + "make-public": "Gjør enhet offentlig", + "make-private": "Gjør enhet privat", + "no-devices-text": "Ingen enheter funnet", + "assign-to-customer-text": "Vennligst velg kunden for å tildele enheten(e)", + "device-details": "Enhetsdetaljer", + "add-device-text": "Legg til ny enhet", + "credentials": "Legitimasjon", + "manage-credentials": "Administrer legitimasjon", + "delete": "Slett enhet", + "assign-devices": "Tildel enheter", + "assign-devices-text": "Tildel { count, plural, =1 {1 enhet} other {# enheter} } til kunde", + "delete-devices": "Slett enheter", + "unassign-from-customer": "Fjern fra kunde", + "unassign-devices": "Fjern enheter", + "unassign-devices-action-title": "Fjern { count, plural, =1 {1 enhet} other {# enheter} } fra kunde", + "unassign-device-from-edge-title": "Er du sikker på at du vil fjerne enheten '{{deviceName}}'?", + "unassign-device-from-edge-text": "Etter bekreftelse vil enheten bli fjernet og vil ikke være tilgjengelig for edge.", + "unassign-devices-from-edge": "Fjern enheter fra edge", + "assign-new-device": "Tildel ny enhet", + "make-public-device-title": "Er du sikker på at du vil gjøre enheten '{{deviceName}}' offentlig?", + "make-public-device-text": "Etter bekreftelse vil enheten og alle dens data bli gjort offentlig og tilgjengelig for andre.", + "make-private-device-title": "Er du sikker på at du vil gjøre enheten '{{deviceName}}' privat?", + "make-private-device-text": "Etter bekreftelse vil enheten og alle dens data bli gjort privat og ikke tilgjengelig for andre.", + "view-credentials": "Vis legitimasjon", + "delete-device-title": "Er du sikker på at du vil slette enheten '{{deviceName}}'?", + "delete-device-text": "Vær forsiktig, etter bekreftelse vil enheten og alle relaterte data bli permanent slettet.", + "delete-devices-title": "Er du sikker på at du vil slette { count, plural, =1 {1 enhet} other {# enheter} }?", + "delete-devices-action-title": "Slett { count, plural, =1 {1 enhet} other {# enheter} }", + "delete-devices-text": "Vær forsiktig, etter bekreftelse vil alle valgte enheter og relaterte data bli permanent slettet.", + "unassign-device-title": "Er du sikker på at du vil fjerne enheten '{{deviceName}}'?", + "unassign-device-text": "Etter bekreftelse vil enheten bli fjernet og ikke lenger være tilgjengelig for kunden.", + "unassign-device": "Fjern enhet", + "unassign-devices-title": "Er du sikker på at du vil fjerne { count, plural, =1 {1 enhet} other {# enheter} }?", + "unassign-devices-text": "Etter bekreftelse vil alle valgte enheter bli fjernet og ikke lenger være tilgjengelige for kunden.", + "device-credentials": "Enhetslegitimasjon", + "loading-device-credentials": "Laster enhetslegitimasjon...", + "credentials-type": "Legitimasjonstype", + "access-token": "Tilgangstoken", + "access-token-required": "Tilgangstoken er påkrevd.", + "access-token-invalid": "Tilgangstoken må være mellom 1 og 32 tegn langt.", + "certificate-pem-format": "Sertifikat i PEM-format", + "certificate-pem-format-required": "Sertifikat er påkrevd.", + "copy-access-token": "Kopier tilgangstoken", + "copy-certificate": "Kopier sertifikat", + "copy-client-id": "Kopier klient-ID", + "copy-user-name": "Kopier brukernavn", + "copy-password": "Kopier passord", + "generate-client-id": "Generer klient-ID", + "generate-user-name": "Generer brukernavn", + "generate-password": "Generer passord", + "generate-access-token": "Generer tilgangstoken", + "lwm2m-security-config": { + "identity": "Klientidentitet", + "identity-required": "Klientidentitet er påkrevd.", + "identity-tooltip": "PSK-identifikatoren er en vilkårlig PSK-ID opptil 128 byte, som beskrevet i standarden [RFC7925].\nPSK-identifikatoren MÅ først konverteres til en tegnstreng og deretter kodes til oktetter ved bruk av UTF-8.", + "client-key": "Klientnøkkel", + "client-key-required": "Klientnøkkel er påkrevd.", + "client-key-tooltip-prk": "RPK offentlig nøkkel eller ID må være i standard [RFC7250] og kodes i Base64-format!", + "client-key-tooltip-psk": "PSK-nøkkel må være i standard [RFC4279] og i HexDesimal-format: 32, 64, 128 tegn!", + "endpoint": "Klientens endepunktnavn", + "endpoint-required": "Klientens endepunktnavn er påkrevd.", + "client-public-key": "Klientens offentlige nøkkel", + "client-public-key-hint": "Hvis klientens offentlige nøkkel er tom, vil det betrodde sertifikatet bli brukt", + "client-public-key-tooltip": "X509 offentlig nøkkel må være i DER-kodet X509v3-format, kun støtte EC-algoritme og kodes i Base64-format!", + "mode": "Sikkerhetskonfigurasjonsmodus", + "client-tab": "Klientsikkerhetskonfigurasjon", + "client-certificate": "Klientsertifikat", + "bootstrap-tab": "Bootstrap-klient", + "bootstrap-server": "Bootstrap-server", + "lwm2m-server": "LwM2M-server", + "client-publicKey-or-id": "Klient offentlig nøkkel eller ID", + "client-publicKey-or-id-required": "Klient offentlig nøkkel eller ID er påkrevd.", + "client-publicKey-or-id-tooltip-psk": "PSK-identifikatoren er en vilkårlig PSK-ID opptil 128 byte, som beskrevet i standarden [RFC7925].\nMÅ konverteres til en tegnstreng og kodes i UTF-8.", + "client-publicKey-or-id-tooltip-rpk": "RPK offentlig nøkkel eller ID må være i standard [RFC7250] og kodes i Base64-format!", + "client-publicKey-or-id-tooltip-x509": "X509 offentlig nøkkel må være i DER-kodet X509v3-format, støtte kun EC-algoritme og kodes i Base64-format", + "client-secret-key": "Klientens hemmelige nøkkel", + "client-secret-key-required": "Klientens hemmelige nøkkel er påkrevd.", + "client-secret-key-tooltip-psk": "PSK-nøkkel må være i standard [RFC4279] og i HexDesimal-format: 32, 64, 128 tegn!", + "client-secret-key-tooltip-prk": "RPK hemmelig nøkkel må være i PKCS_8-format (DER-koding, standard [RFC5958]) og deretter kodes i Base64-format!", + "client-secret-key-tooltip-x509": "X509 hemmelig nøkkel må være i PKCS_8-format (DER-koding, standard [RFC5958]) og deretter kodes i Base64-format!" + }, + "client-id": "Klient-ID", + "client-id-pattern": "Inneholder ugyldig tegn.", + "user-name": "Brukernavn", + "user-name-required": "Brukernavn er påkrevd.", + "client-id-or-user-name-necessary": "Klient-ID og/eller brukernavn er nødvendige", + "password": "Passord", + "secret": "Hemmelighet", + "secret-required": "Hemmelighet er påkrevd.", + "device-type": "Enhetsprofil", + "device-type-required": "Enhetstype er påkrevd.", + "select-device-type": "Velg enhetstype", + "enter-device-type": "Skriv inn enhetsprofil", + "any-device": "Enhver enhet", + "no-device-types-matching": "Ingen enhetsprofiler som matcher '{{entitySubtype}}' ble funnet.", + "device-type-list-empty": "Ingen enhetsprofiler valgt!", + "device-profile-type-list-empty": "Minst én enhetsprofil må velges.", + "device-types": "Enhetstyper", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "name-max-length": "Navn bør være mindre enn 256 tegn", + "label-max-length": "Etikett bør være mindre enn 256 tegn", + "description": "Beskrivelse", + "label": "Etikett", + "events": "Hendelser", + "details": "Detaljer", + "copyId": "Kopier enhets-ID", + "copyAccessToken": "Kopier tilgangstoken", + "copy-mqtt-authentication": "Kopier MQTT-legitimasjon", + "idCopiedMessage": "Enhets-ID har blitt kopiert til utklippstavlen", + "accessTokenCopiedMessage": "Enhetens tilgangstoken har blitt kopiert til utklippstavlen", + "mqtt-authentication-copied-message": "MQTT-legitimasjonen for enheten har blitt kopiert til utklippstavlen", + "assignedToCustomer": "Tildelt kunde", + "unable-delete-device-alias-title": "Kan ikke slette enhetsalias", + "unable-delete-device-alias-text": "Enhetsalias '{{deviceAlias}}' kan ikke slettes da det brukes av følgende widget(er):
{{widgetsList}}", + "is-gateway": "Er gateway", + "overwrite-activity-time": "Overstyr aktivitetstid for tilkoblet enhet", + "device-filter": "Enhetsfilter", + "device-filter-title": "Enhetsfilter", + "filter-title": "Filter", + "device-state": "Enhetstilstand", + "state": "Tilstand", + "any": "Enhver", + "active": "Aktiv", + "inactive": "Inaktiv", + "public": "Offentlig", + "device-public": "Enheten er offentlig", + "select-device": "Velg enhet", + "import": "Importer enhet", + "device-file": "Enhetsfil", + "search": "Søk etter enheter", + "selected-devices": "{ count, plural, =1 {1 enhet} other {# enheter} } valgt", + "device-configuration": "Enhetskonfigurasjon", + "transport-configuration": "Transportkonfigurasjon", + "wizard": { + "device-details": "Enhetsdetaljer" + }, + "unassign-devices-from-edge-title": "Er du sikker på at du vil fjerne tildelingen av { count, plural, =1 {1 enhet} other {# enheter} }?", + "unassign-devices-from-edge-text": "Etter bekreftelse vil alle valgte enheter bli fjernet fra tildelingen og ikke være tilgjengelige via edge.", + "time": "Tid", + "connectivity": { + "check-connectivity": "Sjekk tilkobling", + "device-created-check-connectivity": "Enhet opprettet. La oss sjekke tilkoblingen!", + "loading-check-connectivity-command": "Laster tilkoblingssjekk-kommandoer...", + "use-following-instructions": "Bruk følgende instruksjoner for å sende telemetri på vegne av enheten ved bruk av kommandolinje", + "execute-following-command": "Kjør følgende kommando", + "install-curl-windows": "Fra og med Windows 10 b17063 er cURL tilgjengelig som standard", + "install-curl-macos": "Fra og med Mac OS X 10.2 6C115 (Jaguar) er cURL tilgjengelig som standard", + "install-mqtt-windows": "Bruk instruksjonene for å laste ned, installere, konfigurere og kjøre mosquitto_pub", + "install-coap-client": "Bruk instruksjonene for å laste ned, installere, konfigurere og kjøre coap-client", + "install-necessary-client-tools": "Installer nødvendige klientverktøy", + "mqtts-x509-command": "Bruk følgende dokumentasjon for å koble enheten via MQTT med X509-autentisering", + "coaps-x509-command": "Bruk følgende dokumentasjon for å koble enheten via CoAP over DTLS med X509-autentisering", + "snmp-command": "Bruk følgende dokumentasjon for å koble enheten via SNMP.", + "sparkplug-command": "Bruk følgende dokumentasjon for å koble enheten via MQTT Sparkplug.", + "lwm2m-command": "Bruk følgende dokumentasjon for å koble enheten via LWM2M." + } + }, + "dynamic-form": { + "property": { + "properties": "Egenskaper", + "property": "Egenskap", + "id": "Id", + "name": "Navn", + "type": "Type", + "type-text": "Tekst", + "type-password": "Passord", + "type-textarea": "Tekstområde", + "type-number": "Tall", + "type-switch": "Bryter", + "type-select": "Velg", + "type-radios": "Radioknapper", + "type-datetime": "Dato/Tid", + "type-image": "Bilde", + "type-javascript": "JavaScript", + "type-json": "JSON", + "type-html": "HTML", + "type-css": "CSS", + "type-markdown": "Markdown", + "type-color": "Farge", + "type-color-settings": "Fargeinnstillinger", + "type-font": "Skrift", + "type-units": "Enheter", + "type-icon": "Ikon", + "type-fieldset": "Feltsamling", + "type-array": "Array", + "type-html-section": "HTML-seksjon", + "group-title": "Grupptittel", + "no-properties": "Ingen egenskaper konfigurert", + "add-property": "Legg til egenskap", + "property-settings": "Egenskapsinnstillinger", + "remove-property": "Fjern egenskap", + "default-value": "Standardverdi", + "value-required": "Verdi kreves", + "number-settings": "Tallinnstillinger", + "min": "Min", + "max": "Maks", + "step": "Steg", + "selected-options-limit": "Begrensning for valgte alternativer", + "advanced-ui-settings": "Avanserte grensesnittinnstillinger", + "disable-on-property": "Deaktiver ved egenskap", + "display-condition-function": "Visningsbetingelsesfunksjon", + "sub-label": "Underetikett", + "vertical-divider-after": "Vertikal skillelinje etter", + "input-field-suffix": "Suffiks for inndatafelt", + "property-row-classes": "Radklasser for egenskap", + "property-field-classes": "Feltklasser for egenskap", + "not-unique-property-ids-error": "Egenskaps-IDer må være unike!", + "enable-multiple-select": "Aktiver flervalg", + "allow-empty-select-option": "Tillat tomt valg", + "select-options": "Valgalternativer", + "not-unique-select-option-value-error": "Verdier for valgalternativer må være unike!", + "value": "Verdi", + "label": "Etikett", + "add-option": "Legg til alternativ", + "no-options": "Ingen alternativer konfigurert", + "remove-option": "Fjern alternativ", + "textarea-rows": "Rader i tekstområde", + "help-id": "Hjelpe-ID", + "buttons-direction": "Knappers retning", + "direction-row": "Rad", + "direction-column": "Kolonne", + "radio-button-options": "Alternativer for radioknapper", + "datetime-type": "Feltetype for dato/tid", + "datetime-type-date": "Dato", + "datetime-type-time": "Tid", + "datetime-type-datetime": "Dato/Tid", + "enable-clear-button": "Aktiver fjern-knapp", + "html-section-settings": "Innstillinger for HTML-seksjon", + "html-section-classes": "Klasser for HTML-seksjon", + "html-section-content": "Innhold i HTML-seksjon", + "array-item": "Array-element", + "item-type": "Elementtype", + "item-name": "Elementnavn", + "no-items": "Ingen elementer" + }, + "clear-form": "Tøm skjema", + "clear-form-prompt": "Er du sikker på at du vil fjerne alle skjemaegenskaper?", + "import-form": "Importer skjema fra JSON", + "export-form": "Eksporter skjema til JSON", + "json-file": "JSON-fil", + "json-content": "JSON-innhold", + "invalid-form-json-file-error": "Kan ikke importere skjema fra JSON: Ugyldig datastruktur i JSON-skjema." + }, + "asset-profile": { + "asset-profile": "Eiendomsprofil", + "asset-profiles": "Eiendomsprofiler", + "all-asset-profiles": "Alle", + "add": "Legg til eiendomsprofil", + "edit": "Rediger eiendomsprofil", + "asset-profile-details": "Detaljer om eiendomsprofil", + "no-asset-profiles-text": "Ingen eiendomsprofiler funnet", + "search": "Søk i eiendomsprofiler", + "selected-asset-profiles": "{ count, plural, =1 {1 eiendomsprofil} other {# eiendomsprofiler} } valgt", + "no-asset-profiles-matching": "Ingen eiendomsprofiler samsvarer med '{{entity}}'.", + "asset-profile-required": "Eiendomsprofil er påkrevd", + "idCopiedMessage": "Eiendomsprofil-ID er kopiert til utklippstavlen", + "set-default": "Gjør eiendomsprofil til standard", + "delete": "Slett eiendomsprofil", + "copyId": "Kopier eiendomsprofil-ID", + "name-max-length": "Navnet må være mindre enn 256 tegn", + "new-device-profile-name": "Navn på eiendomsprofil", + "new-device-profile-name-required": "Navn på eiendomsprofil er påkrevd.", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "image": "Bilde av eiendomsprofil", + "description": "Beskrivelse", + "default": "Standard", + "default-rule-chain": "Standard regelkjede", + "default-edge-rule-chain": "Standard edge-regelkjede", + "default-edge-rule-chain-hint": "Brukes på edge som regelkjede for behandling av innkommende data for eiendommer av denne profilen", + "mobile-dashboard": "Mobil dashbord", + "mobile-dashboard-hint": "Brukes av mobilappen som dashbord for eiendomsdetaljer", + "select-queue-hint": "Velg fra rullegardinmenyen.", + "delete-asset-profile-title": "Er du sikker på at du vil slette eiendomsprofilen '{{assetProfileName}}'?", + "delete-asset-profile-text": "Vær forsiktig, etter bekreftelse vil eiendomsprofilen og alle tilknyttede data bli ugjenopprettelige.", + "delete-asset-profiles-title": "Er du sikker på at du vil slette { count, plural, =1 {1 eiendomsprofil} other {# eiendomsprofiler} }?", + "delete-asset-profiles-text": "Vær forsiktig, etter bekreftelse vil alle valgte eiendomsprofiler og alle tilknyttede data bli ugjenopprettelige.", + "set-default-asset-profile-title": "Er du sikker på at du vil gjøre eiendomsprofilen '{{assetProfileName}}' til standard?", + "set-default-asset-profile-text": "Etter bekreftelse vil eiendomsprofilen bli merket som standard og vil bli brukt for nye eiendommer uten spesifisert profil.", + "no-asset-profiles-found": "Ingen eiendomsprofiler funnet.", + "create-new-asset-profile": "Opprett en ny!", + "create-asset-profile": "Opprett ny eiendomsprofil", + "import": "Importer eiendomsprofil", + "export": "Eksporter eiendomsprofil", + "export-failed-error": "Kan ikke eksportere eiendomsprofil: {{error}}", + "asset-profile-file": "Eiendomsprofil-fil", + "invalid-asset-profile-file-error": "Kan ikke importere eiendomsprofil: Ugyldig datastruktur for eiendomsprofil." + }, + "device-profile": { + "device-profile": "Enhetsprofil", + "device-profiles": "Enhetsprofiler", + "all-device-profiles": "Alle", + "add": "Legg til enhetsprofil", + "edit": "Rediger enhetsprofil", + "device-profile-details": "Detaljer om enhetsprofil", + "no-device-profiles-text": "Ingen enhetsprofiler funnet", + "search": "Søk i enhetsprofiler", + "selected-device-profiles": "{ count, plural, =1 {1 enhetsprofil} other {# enhetsprofiler} } valgt", + "no-device-profiles-matching": "Ingen enhetsprofiler samsvarer med '{{entity}}'.", + "device-profile-required": "Enhetsprofil er påkrevd", + "idCopiedMessage": "Enhetsprofil-ID er kopiert til utklippstavlen", + "set-default": "Gjør enhetsprofil til standard", + "delete": "Slett enhetsprofil", + "copyId": "Kopier enhetsprofil-ID", + "name-max-length": "Navnet må være mindre enn 256 tegn", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "type": "Profiltype", + "type-required": "Profiltype er påkrevd.", + "type-default": "Standard", + "image": "Bilde av enhetsprofil", + "transport-type": "Transporttype", + "transport-type-required": "Transporttype er påkrevd.", + "transport-type-default": "Standard", + "transport-type-default-hint": "Støtter grunnleggende MQTT-, HTTP- og CoAP-transport", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "Aktiverer avanserte MQTT-transportinnstillinger", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "Aktiverer avanserte CoAP-transportinnstillinger", + "transport-type-lwm2m": "LWM2M", + "transport-type-lwm2m-hint": "LWM2M-transporttype", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "Angi SNMP transportkonfigurasjon", + "transport-type-http": "HTTP", + "description": "Beskrivelse", + "default": "Standard", + "profile-configuration": "Profilkonfigurasjon", + "transport-configuration": "Transportkonfigurasjon", + "default-rule-chain": "Standard regelkjede", + "default-edge-rule-chain": "Standard edge-regelkjede", + "default-edge-rule-chain-hint": "Brukes på edge som regelkjede for behandling av innkommende data for enheter av denne profilen", + "mobile-dashboard": "Mobil dashbord", + "mobile-dashboard-hint": "Brukes av mobilappen som dashbord for enhetsdetaljer", + "select-queue-hint": "Velg fra rullegardinmenyen.", + "delete-device-profile-title": "Er du sikker på at du vil slette enhetsprofilen '{{deviceProfileName}}'?", + "delete-device-profile-text": "Vær forsiktig, etter bekreftelse vil enhetsprofilen og alle relaterte data inkludert tilknyttede OTA-oppdateringer bli ugjenopprettelige.", + "delete-device-profiles-title": "Er du sikker på at du vil slette { count, plural, =1 {1 enhetsprofil} other {# enhetsprofiler} }?", + "delete-device-profiles-text": "Vær forsiktig, etter bekreftelse vil alle valgte enhetsprofiler og alle relaterte data inkludert tilknyttede OTA-oppdateringer bli ugjenopprettelige.", + "set-default-device-profile-title": "Er du sikker på at du vil gjøre enhetsprofilen '{{deviceProfileName}}' til standard?", + "set-default-device-profile-text": "Etter bekreftelse vil enhetsprofilen bli merket som standard og brukes for nye enheter uten spesifisert profil.", + "no-device-profiles-found": "Ingen enhetsprofiler funnet.", + "create-new-device-profile": "Opprett en ny!", + "mqtt-device-topic-filters": "MQTT enhetstema-filtre", + "mqtt-device-topic-filters-unique": "MQTT enhetstema-filtre må være unike.", + "mqtt-device-topic-filters-spark-plug": "MQTT Sparkplug B Edge of Network (EoN)-node.", + "mqtt-device-topic-filters-spark-plug-hint": "Tillat tilkoblinger fra EoN-noder med Sparkplug B nyttelast og temastruktur.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "SparkPlug-målinger som skal lagres som attributter.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Navn på SparkPlug-målinger som vil bli lagret som enhetsattributter. Alle andre målinger lagres som telemetri.", + "mqtt-device-payload-type": "MQTT enhetsnyttelast", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Aktiver kompatibilitet med andre nyttelastformater.", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Når aktivert, vil plattformen bruke Protobuf som standard nyttelastformat. Hvis parsing mislykkes, vil JSON benyttes. Nyttig for bakoverkompatibilitet ved firmwareoppdateringer. Kompatibilitetsmodus kan gi redusert ytelse, og bør deaktiveres etter fullført oppdatering.", + "mqtt-use-json-format-for-default-downlink-topics": "Bruk JSON-format for standard nedlink-temaer", + "mqtt-use-json-format-for-default-downlink-topics-hint": "Når aktivert, vil plattformen bruke JSON til å sende attributter og RPC over temaene: v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, osv. Påvirker ikke abonnementer via nye v2-temaer.", + "mqtt-send-ack-on-validation-exception": "Send PUBACK ved valideringsfeil", + "mqtt-send-ack-on-validation-exception-hint": "Som standard lukkes MQTT-økten ved valideringsfeil. Hvis aktivert, sendes isteden en bekreftelse.", + "snmp-add-mapping": "Legg til SNMP-mapping", + "snmp-mapping-not-configured": "Ingen mapping for OID til tidsserie/telemetri konfigurert", + "snmp-timseries-or-attribute-name": "Navn på tidsserie/attributt for mapping", + "snmp-timseries-or-attribute-type": "Type på tidsserie/attributt for mapping", + "snmp-method-pdu-type-get-request": "GetRequest", + "snmp-method-pdu-type-get-next-request": "GetNextRequest", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "Nyttelasttype er påkrevd.", + "coap-device-type": "CoAP enhetstype", + "coap-device-payload-type": "CoAP enhetsnyttelast", + "coap-device-type-required": "CoAP enhetstype er påkrevd.", + "coap-device-type-default": "Standard", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "Enkle [+] og fler-nivå [#] jokertegn støttes.", + "telemetry-topic-filter": "Telemetri temafilter", + "telemetry-topic-filter-required": "Telemetri temafilter er påkrevd.", + "attributes-topic-filter": "Publiseringstema for attributter", + "attributes-subscribe-topic-filter": "Abonnementstema for attributter", + "attributes-topic-filter-required": "Publiseringstema for attributter er påkrevd.", + "attributes-subscribe-topic-filter-required": "Abonnementstema for attributter er påkrevd", + "telemetry-proto-schema": "Telemetri proto skjema", + "telemetry-proto-schema-required": "Telemetri proto skjema er påkrevd.", + "attributes-proto-schema": "Attributter proto skjema", + "attributes-proto-schema-required": "Attributter proto skjema er påkrevd.", + "rpc-response-proto-schema": "RPC svar proto skjema", + "rpc-response-proto-schema-required": "RPC svar proto skjema er påkrevd.", + "rpc-response-topic-filter": "RPC svar temafilter", + "rpc-response-topic-filter-required": "RPC svar temafilter er påkrevd.", + "rpc-request-proto-schema": "RPC forespørsel proto skjema", + "rpc-request-proto-schema-required": "RPC forespørsel proto skjema er påkrevd.", + "rpc-request-proto-schema-hint": "RPC forespørsel må alltid ha feltene: string method = 1; int32 requestId = 2; og params = 3 av hvilken som helst datatype.", + "not-valid-pattern-topic-filter": "Ugyldig temafiltermønster", + "not-valid-single-character": "Ugyldig bruk av ett-nivås jokertegn", + "not-valid-multi-character": "Ugyldig bruk av fler-nivås jokertegn", + "single-level-wildcards-hint": "[+] passer for ethvert nivå i temafilteret. Eksempler: v1/devices/+/telemetry eller +/devices/+/attributes.", + "multi-level-wildcards-hint": "[#] kan erstatte hele temafilteret og må være siste tegn. Eksempler: # eller v1/devices/me/#.", + "alarm-rules": "Alarmregler", + "alarm-rules-with-count": "Alarmregler ({{count}})", + "no-alarm-rules": "Ingen alarmregler konfigurert", + "add-alarm-rule": "Legg til alarmregel", + "edit-alarm-rule": "Rediger alarmregel", + "alarm-type": "Alarmtype", + "alarm-type-required": "Alarmtype er påkrevd.", + "alarm-type-unique": "Alarmtype må være unik innenfor enhetsprofilens alarmregler.", + "alarm-type-max-length": "Alarmtype må være kortere enn 256 tegn", + "create-alarm-pattern": "Opprett {{alarmType}} alarm", + "create-alarm-rules": "Opprett alarmregler", + "no-create-alarm-rules": "Ingen opprett-betingelser konfigurert", + "add-create-alarm-rule-prompt": "Vennligst legg til alarmopprettingsregel", + "clear-alarm-rule": "Fjern alarmregel", + "no-clear-alarm-rule": "Ingen fjern-betingelse konfigurert", + "add-create-alarm-rule": "Legg til opprett-betingelse", + "add-clear-alarm-rule": "Legg til fjern-betingelse", + "select-alarm-severity": "Velg alarmalvorlighet", + "alarm-severity-required": "Alarmalvorlighet er påkrevd.", + "condition-duration": "Varighet for betingelse", + "condition-duration-value": "Varighetsverdi", + "condition-duration-time-unit": "Tidsenhet", + "condition-duration-value-range": "Varighetsverdi må være mellom 1 og 2147483647.", + "condition-duration-value-pattern": "Varighetsverdi må være heltall.", + "condition-duration-value-required": "Varighetsverdi er påkrevd.", + "condition-duration-time-unit-required": "Tidsenhet er påkrevd.", + "advanced-settings": "Avanserte innstillinger", + "alarm-rule-additional-info": "Tilleggsinformasjon", + "edit-alarm-rule-additional-info": "Rediger tilleggsinformasjon", + "alarm-rule-additional-info-placeholder": "Vennligst legg til kommentarer og justeringer som skal vises under Alarmdetaljer > Tilleggsinformasjon", + "alarm-rule-additional-info-hint": "Tips: bruk ${keyName} for å sette inn verdier fra attributter eller telemetri i alarmbetingelsen.", + "alarm-rule-mobile-dashboard": "Mobildashboard", + "alarm-rule-mobile-dashboard-hint": "Brukes av mobilappen som dashbord for alarmdetaljer", + "alarm-rule-no-mobile-dashboard": "Ingen dashbord valgt", + "propagate-alarm": "Propager alarm til relaterte enheter", + "alarm-rule-relation-types-list": "Relasjonstyper", + "alarm-rule-relation-types-list-hint": "Definerer relasjonstyper for filtrering. Hvis tomt, propageres til alle relaterte enheter.", + "propagate-alarm-to-owner": "Propager alarm til enhetens eier (Kunde eller Leietaker)", + "propagate-alarm-to-tenant": "Propager alarm til leietaker", + "alarm-rule-condition": "Alarmregelbetingelse", + "enter-alarm-rule-condition-prompt": "Vennligst legg til alarmregelbetingelse", + "edit-alarm-rule-condition": "Rediger alarmregelbetingelse", + "device-provisioning": "Enhetsklargjøring", + "provision-strategy": "Klargjøringsstrategi", + "provision-strategy-required": "Klargjøringsstrategi er påkrevd.", + "provision-strategy-disabled": "Deaktivert", + "provision-strategy-created-new": "Tillat å opprette nye enheter", + "provision-strategy-check-pre-provisioned": "Sjekk for forhåndsklargjorte enheter", + "provision-device-key": "Klargjøringsnøkkel", + "provision-device-key-required": "Klargjøringsnøkkel er påkrevd.", + "copy-provision-key": "Kopier klargjøringsnøkkel", + "provision-key-copied-message": "Klargjøringsnøkkel er kopiert til utklippstavlen", + "provision-device-secret": "Klargjøringshemmelighet", + "provision-device-secret-required": "Klargjøringshemmelighet er påkrevd.", + "copy-provision-secret": "Kopier klargjøringshemmelighet", + "provision-secret-copied-message": "Klargjøringshemmelighet er kopiert til utklippstavlen", + "provision-strategy-x509": { + "certificate-chain": "X509-sertifikatkjede", + "certificate-chain-hint": "X.509-sertifikatstrategi brukes for å klargjøre enheter med klientsertifikater i toveis TLS-kommunikasjon.", + "allow-create-new-devices": "Opprett nye enheter", + "allow-create-new-devices-hint": "Hvis valgt, vil nye enheter opprettes og klientsertifikatet vil brukes som enhetens legitimasjon.", + "certificate-value": "Sertifikat i PEM-format", + "certificate-value-required": "Sertifikat i PEM-format er påkrevd", + "cn-regex-variable": "CN Regulært uttrykksvariabel", + "cn-regex-variable-required": "CN Regulært uttrykksvariabel er påkrevd", + "cn-regex-variable-hint": "Brukes for å hente enhetsnavn fra enhetens X509-sertifikats Common Name." + }, + "condition": "Betingelse", + "condition-type": "Betingelsestype", + "condition-type-simple": "Enkel", + "condition-type-duration": "Varighet", + "condition-during": "Under {{during}}", + "condition-during-dynamic": "Under \"{{ attribute }}\" ({{during}})", + "condition-type-repeating": "Gjentas", + "condition-type-required": "Betingelsestype er påkrevd.", + "condition-repeating-value": "Antall hendelser", + "condition-repeating-value-range": "Antall hendelser må være mellom 1 og 2147483647.", + "condition-repeating-value-pattern": "Antall hendelser må være heltall.", + "condition-repeating-value-required": "Antall hendelser er påkrevd.", + "condition-repeat-times": "Gjentas { count, plural, =1 {1 gang} other {# ganger} }", + "condition-repeat-times-dynamic": "Gjentas \"{ attribute }\" ({ count, plural, =1 {1 gang} other {# ganger} })", + "schedule-type": "Tidsplanstype", + "schedule-type-required": "Tidsplanstype er påkrevd.", + "schedule": "Tidsplan", + "edit-schedule": "Rediger alarmtidsplan", + "schedule-any-time": "Aktiv hele tiden", + "schedule-specific-time": "Aktiv på spesifisert tid", + "schedule-custom": "Tilpasset", + "schedule-day": { + "monday": "Mandag", + "tuesday": "Tirsdag", + "wednesday": "Onsdag", + "thursday": "Torsdag", + "friday": "Fredag", + "saturday": "Lørdag", + "sunday": "Søndag" + }, + "schedule-days": "Dager", + "schedule-time": "Tid", + "schedule-time-from": "Fra", + "schedule-time-to": "Til", + "schedule-days-of-week-required": "Minst én ukedag må være valgt.", + "create-device-profile": "Opprett ny enhetsprofil", + "import": "Importer enhetsprofil", + "export": "Eksporter enhetsprofil", + "export-failed-error": "Kan ikke eksportere enhetsprofil: {{error}}", + "device-profile-file": "Enhetsprofilfil", + "invalid-device-profile-file-error": "Kan ikke importere enhetsprofil: Ugyldig datastruktur.", + "power-saving-mode": "Strømsparingsmodus", + "power-saving-mode-type": { + "default": "Bruk enhetsprofilens strømsparingsmodus", + "psm": "Strømsparingsmodus (PSM)", + "drx": "Diskontinuerlig mottak (DRX)", + "edrx": "Utvidet diskontinuerlig mottak (eDRX)" + }, + "edrx-cycle": "eDRX-syklus", + "edrx-cycle-required": "eDRX-syklus er påkrevd.", + "edrx-cycle-pattern": "eDRX-syklus må være et positivt heltall.", + "edrx-cycle-min": "Minimum eDRX-syklus er {{ min }} sekunder.", + "paging-transmission-window": "Paging-overføringsvindu", + "paging-transmission-window-required": "Paging-overføringsvindu er påkrevd.", + "paging-transmission-window-pattern": "Paging-overføringsvindu må være et positivt heltall.", + "paging-transmission-window-min": "Minimum paging-overføringsvindu er {{ min }} sekunder.", + "psm-activity-timer": "PSM aktivitetstimer", + "psm-activity-timer-required": "PSM aktivitetstimer er påkrevd.", + "psm-activity-timer-pattern": "PSM aktivitetstimer må være et positivt heltall.", + "psm-activity-timer-min": "Minimum PSM aktivitetstimer er {{ min }} sekunder.", + "lwm2m": { + "object-list": "Objektliste", + "object-list-empty": "Ingen objekter valgt.", + "no-objects-found": "Ingen objekter funnet.", + "no-objects-matching": "Ingen objekter som samsvarer med '{{object}}' ble funnet.", + "model-tab": "LWM2M-modell", + "add-new-instances": "Legg til nye instanser", + "instances-list": "Instansliste", + "instances-list-required": "Instansliste er påkrevd.", + "instance-id-pattern": "Instans-ID må være et positivt heltall.", + "instance-id-max": "Maksimal verdi for instans-ID er {{max}}.", + "instance": "Instans", + "resource-label": "#ID Ressursnavn", + "observe-label": "Observer", + "attribute-label": "Attributt", + "telemetry-label": "Telemetri", + "edit-observe-select": "For å redigere observasjon, velg telemetri eller attributt", + "edit-attributes-select": "For å redigere attributter, velg telemetri eller attributt", + "no-attributes-set": "Ingen attributter satt", + "key-name": "Nøkkelnavn", + "key-name-required": "Nøkkelnavn er påkrevd", + "attribute-name": "Attributtnavn", + "attribute-name-required": "Attributtnavn er påkrevd.", + "attribute-value": "Attributtverdi", + "attribute-value-required": "Attributtverdi er påkrevd.", + "attribute-value-pattern": "Attributtverdi må være et positivt heltall.", + "edit-attributes": "Rediger attributter: {{ name }}", + "view-attributes": "Vis attributter: {{ name }}", + "add-attribute": "Legg til attributt", + "edit-attribute": "Rediger attributt", + "view-attribute": "Vis attributt", + "remove-attribute": "Fjern attributt", + "delete-server-text": "Vær forsiktig, etter bekreftelse vil serverkonfigurasjonen ikke kunne gjenopprettes.", + "delete-server-title": "Er du sikker på at du vil slette serveren?", + "mode": "Sikkerhetskonfigurasjonsmodus", + "bootstrap-tab": "Bootstrap", + "bootstrap-server-legend": "Bootstrap-server (ShortId...)", + "lwm2m-server-legend": "LwM2M-server (ShortId...)", + "server": "Server", + "short-id": "Kort server-ID", + "short-id-tooltip": "Kort server-ID. Brukes for å koble til serverobjektinstans.\nDenne identifikatoren identifiserer hver LwM2M-server for klienten.\nRessursen MÅ være satt hvis Bootstrap-Server Ressursen har verdien 'false'.\nID:0 og ID:65535 MÅ IKKE brukes.", + "short-id-tooltip-bootstrap": "Kort server-ID. Brukes for å koble til serverobjektinstans.\nDenne identifikatoren identifiserer hver LwM2M-server for klienten.\nRessursen MÅ være satt hvis Bootstrap-Server Ressursen har verdien 'false'.", + "short-id-required": "Kort server-ID er påkrevd.", + "short-id-range": "Kort server-ID må være mellom {{ min }} og {{ max }}.", + "short-id-pattern": "Kort server-ID må være et positivt heltall.", + "lifetime": "Klientens registreringsvarighet", + "lifetime-required": "Klientens registreringsvarighet er påkrevd.", + "lifetime-pattern": "Registreringsvarighet må være et positivt heltall.", + "default-min-period": "Minste periode mellom to varslinger (s)", + "default-min-period-tooltip": "Standardverdi for Minimum Periode for Observasjon hvis parameteren ikke er satt.", + "default-min-period-required": "Minimumsperiode er påkrevd.", + "default-min-period-pattern": "Minimumsperiode må være et positivt heltall.", + "notification-storing": "Lagring av varsling når deaktivert eller frakoblet", + "binding": "Binding", + "binding-type": { + "u": "U: Klienten er alltid tilgjengelig via UDP.", + "m": "M: Klienten er alltid tilgjengelig via MQTT.", + "h": "H: Klienten er alltid tilgjengelig via HTTP.", + "t": "T: Klienten er alltid tilgjengelig via TCP.", + "s": "S: Klienten er alltid tilgjengelig via SMS.", + "n": "N: Klienten MÅ sende svar via Non-IP binding (støttes fra LWM2M 1.1).", + "uq": "UQ: UDP i kømodus (ikke støttet siden LWM2M 1.1)", + "uqs": "UQS: Både UDP og SMS aktive; UDP i kømodus, SMS i standardmodus (ikke støttet siden LWM2M 1.1)", + "tq": "TQ: TCP i kømodus (ikke støttet siden LWM2M 1.1)", + "tqs": "TQS: Både TCP og SMS aktive; TCP i kømodus, SMS i standardmodus (ikke støttet siden LWM2M 1.1)", + "sq": "SQ: SMS i kømodus (ikke støttet siden LWM2M 1.1)" + }, + "binding-tooltip": "Liste over bindingsmodus i LwM2M serverobjektet - /1/x/7.\nAngir støttede bindingsmodi i klienten.\nBør matche verdien i “Supported Binding and Modes” i Enhetsobjektet (/3/0/16).\nKun én transportbinding brukes per økt.", + "bootstrap-server": "Bootstrap-server", + "lwm2m-server": "LwM2M-server", + "include-bootstrap-server": "Inkluder Bootstrap-serveroppdateringer", + "bootstrap-update-title": "Bootstrap-serveren er allerede konfigurert. Er du sikker på at du vil ekskludere oppdateringene?", + "bootstrap-update-text": "Vær forsiktig, etter bekreftelse vil Bootstrap-serverkonfigurasjonen ikke kunne gjenopprettes.", + "server-host": "Vert", + "server-host-required": "Vert er påkrevd.", + "server-port": "Port", + "server-port-required": "Port er påkrevd.", + "server-port-pattern": "Port må være et positivt heltall.", + "server-port-range": "Port bør være i området 1 til 65535.", + "server-public-key": "Serverens offentlige nøkkel", + "server-public-key-required": "Serverens offentlige nøkkel er påkrevd.", + "client-hold-off-time": "Hold-av-tid", + "client-hold-off-time-required": "Hold-av-tid er påkrevd.", + "client-hold-off-time-pattern": "Hold-av-tid må være et positivt heltall.", + "client-hold-off-time-tooltip": "Klientens Hold-av-tid kun for bruk med Bootstrap-server", + "account-after-timeout": "Fortsett etter timeout", + "account-after-timeout-required": "Fortsett etter timeout er påkrevd.", + "account-after-timeout-pattern": "Verdien må være et positivt heltall.", + "account-after-timeout-tooltip": "Bootstrap-server fortsett etter timeout-verdien som angitt av denne ressursen.", + "server-type": "Servertype", + "add-new-server-title": "Legg til ny serverkonfigurasjon", + "add-server-config": "Legg til serverkonfigurasjon", + "add-lwm2m-server-config": "Legg til LwM2M-server", + "no-config-servers": "Ingen servere konfigurert", + "others-tab": "Andre innstillinger", + "client-strategy": "Klientstrategi ved tilkobling", + "client-strategy-label": "Strategi", + "client-strategy-only-observe": "Kun Observer-forespørsel til klienten etter første tilkobling", + "client-strategy-read-all": "Les alle ressurser og send Observer-forespørsel etter registrering", + "fw-update": "Firmware-oppdatering", + "fw-update-strategy": "Firmware-oppdateringsstrategi", + "fw-update-strategy-data": "Send firmware som binærfil via Objekt 19 og Ressurs 0 (Data)", + "fw-update-strategy-package": "Send firmware som binærfil via Objekt 5 og Ressurs 0 (Pakke)", + "fw-update-strategy-package-uri": "Generer unik CoAP-URL og send firmware via Objekt 5 og Ressurs 1 (Pakke URI)", + "sw-update": "Programvareoppdatering", + "sw-update-strategy": "Programvareoppdateringsstrategi", + "sw-update-strategy-package": "Send binærfil via Objekt 9 og Ressurs 2 (Pakke)", + "sw-update-strategy-package-uri": "Generer unik CoAP-URL og send programvare via Objekt 9 og Ressurs 3 (Pakke URI)", + "fw-update-resource": "Firmware-oppdatering CoAP-ressurs", + "fw-update-resource-required": "Firmware-oppdatering CoAP-ressurs er påkrevd.", + "sw-update-resource": "Programvareoppdatering CoAP-ressurs", + "sw-update-resource-required": "Programvareoppdatering CoAP-ressurs er påkrevd.", + "config-json-tab": "JSON-konfigurasjon for enhet", + "attributes-name": { + "min-period": "Minimumsperiode", + "max-period": "Maksimumsperiode", + "greater-than": "Større enn", + "less-than": "Mindre enn", + "step": "Steg", + "min-evaluation-period": "Minimum evalueringsperiode", + "max-evaluation-period": "Maksimum evalueringsperiode" + }, + "default-object-id": "Standard objektversjon (Attributt)", + "default-object-id-ver": { + "v1-0": "1.0", + "v1-1": "1.1" + } + }, + "snmp": { + "add-communication-config": "Legg til kommunikasjonskonfigurasjon", + "add-mapping": "Legg til tilordning", + "authentication-passphrase": "Autentiseringsfrase", + "authentication-passphrase-required": "Autentiseringsfrase er påkrevd.", + "authentication-protocol": "Autentiseringsprotokoll", + "authentication-protocol-required": "Autentiseringsprotokoll er påkrevd.", + "communication-configs": "Kommunikasjonskonfigurasjoner", + "community": "Community-streng", + "community-required": "Community-streng er påkrevd.", + "context-name": "Kontekstnavn", + "data-key": "Datanøkkel", + "data-key-required": "Datanøkkel er påkrevd.", + "data-type": "Datatype", + "data-type-required": "Datatype er påkrevd.", + "engine-id": "Engine-ID", + "host": "Vert", + "host-required": "Vert er påkrevd.", + "oid": "OID", + "oid-pattern": "Ugyldig OID-format.", + "oid-required": "OID er påkrevd.", + "please-add-communication-config": "Vennligst legg til kommunikasjonskonfigurasjon", + "please-add-mapping-config": "Vennligst legg til tilordningskonfigurasjon", + "port": "Port", + "port-format": "Ugyldig portformat.", + "port-required": "Port er påkrevd.", + "privacy-passphrase": "Personvernfrase", + "privacy-passphrase-required": "Personvernfrase er påkrevd.", + "privacy-protocol": "Personvernprotokoll", + "privacy-protocol-required": "Personvernprotokoll er påkrevd.", + "protocol-version": "Protokollversjon", + "protocol-version-required": "Protokollversjon er påkrevd.", + "querying-frequency": "Forespørselsfrekvens, ms", + "querying-frequency-invalid-format": "Forespørselsfrekvens må være et positivt heltall.", + "querying-frequency-required": "Forespørselsfrekvens er påkrevd.", + "retries": "Forsøk", + "retries-invalid-format": "Forsøk må være et positivt heltall.", + "retries-required": "Antall forsøk er påkrevd.", + "scope": "Omfang", + "scope-required": "Omfang er påkrevd.", + "security-name": "Sikkerhetsnavn", + "security-name-required": "Sikkerhetsnavn er påkrevd.", + "timeout-ms": "Tidsavbrudd, ms", + "timeout-ms-invalid-format": "Tidsavbrudd må være et positivt heltall.", + "timeout-ms-required": "Tidsavbrudd er påkrevd.", + "user-name": "Brukernavn", + "user-name-required": "Brukernavn er påkrevd." + } + }, + "dialog": { + "close": "Lukk dialog", + "error-message-title": "Feilmelding:", + "error-details-title": "Feildetaljer" + }, + "direction": { + "column": "Kolonne", + "row": "Rad" + }, + "edge": { + "edge": "Edge", + "edge-instances": "Edge-installasjoner", + "instances": "Installasjoner", + "edge-file": "Edge-fil", + "name-max-length": "Navn må være mindre enn 256 tegn", + "label-max-length": "Etikett må være mindre enn 256 tegn", + "type-max-length": "Type må være mindre enn 256 tegn", + "management": "Edge-administrasjon", + "no-edges-matching": "Ingen edge-enheter som samsvarer med '{{entity}}' ble funnet.", + "add": "Legg til edge", + "no-edges-text": "Ingen edge-enheter funnet", + "edge-details": "Edge-detaljer", + "add-edge-text": "Legg til ny edge", + "delete": "Slett edge", + "delete-edge-title": "Er du sikker på at du vil slette edge '{{edgeName}}'?", + "delete-edge-text": "Vær forsiktig, etter bekreftelse vil edge og alle relaterte data bli uopprettelige.", + "delete-edges-title": "Er du sikker på at du vil slette { count, plural, =1 {1 edge} other {# edges} }?", + "delete-edges-text": "Vær forsiktig, etter bekreftelse vil alle valgte edge-enheter bli fjernet og alle relaterte data bli uopprettelige.", + "name": "Navn", + "name-starts-with": "Edge-navn starter med", + "name-required": "Navn er påkrevd.", + "description": "Beskrivelse", + "details": "Detaljer", + "events": "Hendelser", + "copy-id": "Kopier Edge-ID", + "id-copied-message": "Edge-ID har blitt kopiert til utklippstavlen", + "sync": "Synkroniser Edge", + "edge-required": "Edge er påkrevd", + "edge-type": "Edge-type", + "edge-type-required": "Edge-type er påkrevd.", + "event-action": "Hendelseshandling", + "entity-id": "Enhets-ID", + "select-edge-type": "Velg edge-type", + "assign-to-customer": "Tildel til kunde", + "assign-to-customer-text": "Vennligst velg kunden du vil tildele edge-enhet(er)", + "assign-edge-to-customer": "Tildel edge-enhet(er) til kunde", + "assign-edge-to-customer-text": "Vennligst velg edge-enheter du vil tildele til kunden", + "assignedToCustomer": "Tildelt kunde", + "edge-public": "Edge er offentlig", + "assigned-to-customer": "Tildelt til: {{customerTitle}}", + "unassign-from-customer": "Fjern tildeling fra kunde", + "unassign-edge-title": "Er du sikker på at du vil fjerne tildelingen av edge '{{edgeName}}'?", + "unassign-edge-text": "Etter bekreftelse vil edge bli fjernet og ikke lenger være tilgjengelig for kunden.", + "unassign-edges-title": "Er du sikker på at du vil fjerne tildelingen av { count, plural, =1 {1 edge} other {# edges} }?", + "unassign-edges-text": "Etter bekreftelse vil alle valgte edge-enheter bli fjernet og ikke lenger være tilgjengelig for kunden.", + "make-public": "Gjør edge offentlig", + "make-public-edge-title": "Er du sikker på at du vil gjøre edge '{{edgeName}}' offentlig?", + "make-public-edge-text": "Etter bekreftelse vil edge og alle tilhørende data bli offentlig og tilgjengelig for andre.", + "make-private": "Gjør edge privat", + "public": "Offentlig", + "make-private-edge-title": "Er du sikker på at du vil gjøre edge '{{edgeName}}' privat?", + "make-private-edge-text": "Etter bekreftelse vil edge og alle tilhørende data bli private og ikke tilgjengelige for andre.", + "import": "Importer edge", + "install-connect-instructions": "Installer og koble til-instruksjoner", + "install-connect-instructions-edge-created": "Edge opprettet! Se Installer og koble til-instruksjoner", + "loading-edge-instructions": "Laster edge-instruksjoner...", + "label": "Etikett", + "load-entity-error": "Kunne ikke laste data. Enheten er slettet.", + "assign-new-edge": "Tildel ny edge", + "unassign-from-edge": "Fjern tildeling fra edge", + "edge-key": "Edge-nøkkel", + "copy-edge-key": "Kopier Edge-nøkkel", + "edge-key-copied-message": "Edge-nøkkel har blitt kopiert til utklippstavlen", + "edge-secret": "Edge-hemmelighet", + "copy-edge-secret": "Kopier Edge-hemmelighet", + "edge-secret-copied-message": "Edge-hemmelighet har blitt kopiert til utklippstavlen", + "manage-assets": "Administrer eiendeler", + "manage-devices": "Administrer enheter", + "manage-entity-views": "Administrer enhetsvisninger", + "manage-dashboards": "Administrer dashbord", + "manage-rulechains": "Administrer regelkjeder", + "assets": "Edge-eiendeler", + "devices": "Edge-enheter", + "entity-views": "Edge-enhetsvisninger", + "dashboard": "Edge-dashbord", + "dashboards": "Edge-dashbord", + "rulechain-templates": "Regelkjedemaler", + "edge-rulechain-templates": "Edge regelkjedemaler", + "rulechains": "Edge regelkjeder", + "search": "Søk edge-enheter", + "selected-edges": "{ count, plural, =1 {1 edge} other {# edges} } valgt", + "any-edge": "Enhver edge", + "no-edge-types-matching": "Ingen edge-typer som samsvarer med '{{entitySubtype}}' ble funnet.", + "edge-type-list-empty": "Ingen edge-typer valgt.", + "edge-types": "Edge-typer", + "enter-edge-type": "Angi edge-type", + "deployed": "Distribuert", + "pending": "Ventende", + "downlinks": "Nedlinker", + "no-downlinks-prompt": "Ingen nedlinker funnet", + "sync-process-started-successfully": "Synkroniseringsprosess startet!", + "missing-related-rule-chains-title": "Edge mangler relaterte regelkjeder", + "missing-related-rule-chains-text": "Tildelte edge-regelkjeder bruker noder som videresender meldinger til regelkjeder som ikke er tildelt denne edge-enheten.

Manglende regelkjeder:
{{missingRuleChains}}", + "widget-datasource-error": "Denne widgeten støtter kun EDGE-enhetsdatakilde", + "upgrade-instructions": "Oppgraderingsinstruksjoner", + "connected": "Tilkoblet", + "disconnected": "Frakoblet" + }, + "edge-event": { + "type-dashboard": "Dashbord", + "type-asset": "Eiendel", + "type-device": "Enhet", + "type-device-profile": "Enhetsprofil", + "type-asset-profile": "Eiendelsprofil", + "type-entity-view": "Enhetsvisning", + "type-alarm": "Alarm", + "type-rule-chain": "Regelkjede", + "type-rule-chain-metadata": "Regelkjedemetadata", + "type-edge": "Edge", + "type-user": "Bruker", + "type-tenant": "Leietaker", + "type-tenant-profile": "Leietakerprofil", + "type-customer": "Kunde", + "type-relation": "Relasjon", + "type-widgets-bundle": "Widget-pakke", + "type-widgets-type": "Widget-type", + "type-admin-settings": "Admininnstillinger", + "type-ota-package": "OTA-pakke", + "type-queue": "Kø", + "action-type-added": "Lagt til", + "action-type-deleted": "Slettet", + "action-type-updated": "Oppdatert", + "action-type-post-attributes": "Send attributter", + "action-type-attributes-updated": "Attributter oppdatert", + "action-type-attributes-deleted": "Attributter slettet", + "action-type-timeseries-updated": "Tidsserie oppdatert", + "action-type-credentials-updated": "Legitimasjon oppdatert", + "action-type-assigned-to-customer": "Tildelt kunde", + "action-type-unassigned-from-customer": "Fratatt kunde", + "action-type-relation-add-or-update": "Relasjon lagt til eller oppdatert", + "action-type-relation-deleted": "Relasjon slettet", + "action-type-rpc-call": "RPC-kall", + "action-type-alarm-ack": "Alarm bekreftet", + "action-type-alarm-clear": "Alarm fjernet", + "action-type-alarm-assigned": "Alarm tildelt", + "action-type-alarm-unassigned": "Alarm fratatt", + "action-type-assigned-to-edge": "Tildelt Edge", + "action-type-unassigned-from-edge": "Fratatt Edge", + "action-type-credentials-request": "Legitimasjonsforespørsel", + "action-type-entity-merge-request": "Forespørsel om sammenslåing" + }, + "error": { + "unable-to-connect": "Kan ikke koble til serveren! Vennligst sjekk internettforbindelsen.", + "unhandled-error-code": "Ubehandlet feilkode: {{errorCode}}", + "unknown-error": "Ukjent feil" + }, + "entity": { + "entity": "Enhet", + "entities": "Enheter", + "entities-count": "Antall enheter", + "alarms-count": "Antall alarmer", + "aliases": "Enhetsaliaser", + "aliases-short": "Aliaser", + "entity-alias": "Enhetsalias", + "unable-delete-entity-alias-title": "Kan ikke slette enhetsalias", + "unable-delete-entity-alias-text": "Enhetsaliaset '{{entityAlias}}' kan ikke slettes fordi det brukes av følgende widget(er):
{{widgetsList}}", + "duplicate-alias-error": "Duplikat alias funnet '{{alias}}'.
Enhetsaliaser må være unike innenfor dashbordet.", + "missing-entity-filter-error": "Filter mangler for alias '{{alias}}'.", + "configure-alias": "Konfigurer aliaset '{{alias}}'", + "alias": "Alias", + "alias-required": "Enhetsalias er påkrevd.", + "remove-alias": "Fjern enhetsalias", + "add-alias": "Legg til enhetsalias", + "edit-alias": "Rediger enhetsalias", + "entity-list": "Enhetsliste", + "entity-type": "Enhetstype", + "entity-types": "Enhetstyper", + "entity-type-list": "Liste over enhetstyper", + "any-entity": "Enhver enhet", + "add-entity-type": "Legg til enhetstype", + "enter-entity-type": "Skriv inn enhetstype", + "no-entities-matching": "Ingen enheter som samsvarer med '{{entity}}' ble funnet.", + "no-entities-text": "Ingen enheter funnet", + "no-entity-types-matching": "Ingen enhetstyper som samsvarer med '{{entityType}}' ble funnet.", + "name-starts-with": "Navnuttrykk", + "help-text": "Bruk '%' etter behov: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter": "Bruk filter", + "entity-list-empty": "Ingen enheter valgt.", + "entity-type-list-required": "Minst én enhetstype må velges.", + "entity-name-filter-required": "Enhetsnavnfilter er påkrevd.", + "entity-name-filter-no-entity-matched": "Ingen enheter som starter med '{{entity}}' ble funnet.", + "all-subtypes": "Alle", + "select-entities": "Velg enheter", + "no-aliases-found": "Ingen aliaser funnet.", + "no-alias-matching": "'{{alias}}' ble ikke funnet.", + "create-new-alias": "Opprett en ny!", + "create-new": "Opprett ny", + "key": "Nøkkel", + "key-name": "Nøkkelnavn", + "no-keys-found": "Ingen nøkler funnet.", + "no-key-matching": "'{{key}}' ble ikke funnet.", + "create-new-key": "Opprett en ny!", + "type": "Type", + "type-required": "Enhetstype er påkrevd.", + "type-device": "Enhet", + "type-devices": "Enheter", + "list-of-devices": "{ count, plural, =1 {Én enhet} other {Liste over # enheter} }", + "device-name-starts-with": "Enheter som starter med '{{prefix}}'", + "type-device-profile": "Enhetsprofil", + "type-device-profiles": "Enhetsprofiler", + "clear-selected-profiles": "Fjern valgte profiler", + "list-of-device-profiles": "{ count, plural, =1 {Én enhetsprofil} other {Liste over # enhetsprofiler} }", + "device-profile-name-starts-with": "Enhetsprofiler som starter med '{{prefix}}'", + "type-asset-profile": "Eiendelsprofil", + "type-asset-profiles": "Eiendelsprofiler", + "list-of-asset-profiles": "{ count, plural, =1 {Én eiendelsprofil} other {Liste over # eiendelsprofiler} }", + "asset-profile-name-starts-with": "Eiendelsprofiler som starter med '{{prefix}}'", + "type-asset": "Eiendel", + "type-assets": "Eiendeler", + "list-of-assets": "{ count, plural, =1 {Én eiendel} other {Liste over # eiendeler} }", + "asset-name-starts-with": "Eiendeler som starter med '{{prefix}}'", + "type-entity-view": "Enhetsvisning", + "type-entity-views": "Enhetsvisninger", + "list-of-entity-views": "{ count, plural, =1 {Én enhetsvisning} other {Liste over # enhetsvisninger} }", + "entity-view-name-starts-with": "Enhetsvisninger som starter med '{{prefix}}'", + "type-rule": "Regel", + "type-rules": "Regler", + "list-of-rules": "{ count, plural, =1 {Én regel} other {Liste over # regler} }", + "rule-name-starts-with": "Regler som starter med '{{prefix}}'", + "type-plugin": "Plugin", + "type-plugins": "Plugins", + "list-of-plugins": "{ count, plural, =1 {Én plugin} other {Liste over # plugins} }", + "plugin-name-starts-with": "Plugins som starter med '{{prefix}}'", + "type-tenant": "Leietaker", + "type-tenants": "Leietakere", + "list-of-tenants": "{ count, plural, =1 {Én leietaker} other {Liste over # leietakere} }", + "tenant-name-starts-with": "Leietakere hvis navn starter med '{{prefix}}'", + "type-tenant-profile": "Leietakerprofil", + "type-tenant-profiles": "Leietakerprofiler", + "list-of-tenant-profiles": "{ count, plural, =1 {Én leietakerprofil} other {Liste over # leietakerprofiler} }", + "tenant-profile-name-starts-with": "Leietakerprofiler hvis navn starter med '{{prefix}}'", + "type-customer": "Kunde", + "type-customers": "Kunder", + "list-of-customers": "{ count, plural, =1 {Én kunde} other {Liste over # kunder} }", + "customer-name-starts-with": "Kunder hvis navn starter med '{{prefix}}'", + "type-user": "Bruker", + "type-users": "Brukere", + "list-of-users": "{ count, plural, =1 {Én bruker} other {Liste over # brukere} }", + "user-name-starts-with": "Brukere hvis navn starter med '{{prefix}}'", + "type-dashboard": "Dashbord", + "type-dashboards": "Dashbord", + "list-of-dashboards": "{ count, plural, =1 {Én dashbord} other {Liste over # dashbord} }", + "dashboard-name-starts-with": "Dashbord som starter med '{{prefix}}'", + "type-alarm": "Alarm", + "type-alarms": "Alarmer", + "list-of-alarms": "{ count, plural, =1 {Én alarm} other {Liste over # alarmer} }", + "alarm-name-starts-with": "Alarmer som starter med '{{prefix}}'", + "type-rulechain": "Regelkjede", + "type-rulechains": "Regelkjeder", + "list-of-rulechains": "{ count, plural, =1 {Én regelkjede} other {Liste over # regelkjeder} }", + "rulechain-name-starts-with": "Regelkjeder som starter med '{{prefix}}'", + "type-rulenode": "Regelnode", + "type-rulenodes": "Regelnoder", + "list-of-rulenodes": "{ count, plural, =1 {Én regelnode} other {Liste over # regelnoder} }", + "rulenode-name-starts-with": "Regelnoder som starter med '{{prefix}}'", + "type-current-customer": "Gjeldende kunde", + "type-current-tenant": "Gjeldende leietaker", + "type-current-user": "Gjeldende bruker", + "type-current-user-owner": "Gjeldende brukereier", + "type-calculated-field": "Beregnet felt", + "type-calculated-fields": "Beregnete felt", + "type-widgets-bundle": "Widget-pakke", + "type-widgets-bundles": "Widget-pakker", + "list-of-widgets-bundles": "{ count, plural, =1 {Én widget-pakke} other {Liste over # widget-pakker} }", + "type-widget": "Widget", + "type-widgets": "Widgeter", + "list-of-widgets": "{ count, plural, =1 {Én widget} other {Liste over # widgeter} }", + "search": "Søk i enheter", + "selected-entities": "{ count, plural, =1 {1 enhet} other {# enheter} } valgt", + "entity-name": "Enhetsnavn", + "entity-label": "Enhetsetikett", + "details": "Enhetsdetaljer", + "no-entities-prompt": "Ingen enheter funnet", + "no-data": "Ingen data å vise", + "columns-to-display": "Kolonner å vise", + "type-api-usage-state": "API-bruksstatus", + "type-edge": "Edge", + "type-edges": "Edge-enheter", + "list-of-edges": "{ count, plural, =1 {Én edge} other {Liste over # edge-enheter} }", + "edge-name-starts-with": "Edge-enheter som starter med '{{prefix}}'", + "version-conflict": { + "message": "Vil du overskrive eksisterende versjon eller forkaste endringer og laste inn den nyeste versjonen?", + "link": "Du kan laste ned din versjon av {{entityType}} ved å bruke denne", + "overwrite": "Overskriv versjon", + "discard": "Forkast endringer" + }, + "type-tb-resource": "Ressurs", + "type-tb-resources": "Ressurser", + "list-of-tb-resources": "{ count, plural, =1 {Én ressurs} other {Liste over # ressurser} }", + "type-ota-package": "OTA-pakke", + "type-rpc": "RPC", + "type-queue": "Kø", + "type-queue-stats": "Køstatistikk", + "type-queues-stats": "Statistikk for køer", + "type-notification": "Varsling", + "type-notification-rule": "Varslingsregel", + "type-notification-rules": "Varslingsregler", + "list-of-notification-rules": "{ count, plural, =1 {Én varslingsregel} other {Liste over # varslingsregler} }", + "type-notification-target": "Varslingsmottaker", + "type-notification-targets": "Varslingsmottakere", + "list-of-notification-targets": "{ count, plural, =1 {Én varslingsmottaker} other {Liste over # varslingsmottakere} }", + "type-notification-request": "Varslingsforespørsel", + "type-notification-template": "Varslingsmal", + "type-notification-templates": "Varslingsmaler", + "list-of-notification-templates": "{ count, plural, =1 {Én varslingsmal} other {Liste over # varslingsmaler} }", + "link": "lenke", + "type-oauth2-client": "OAuth 2.0-klient", + "type-oauth2-clients": "OAuth 2.0-klienter", + "list-of-oauth2-clients": "{ count, plural, =1 {Én OAuth 2.0-klient} other {Liste over # OAuth 2.0-klienter} }", + "type-domain": "Domene", + "type-domains": "Domener", + "list-of-domains": "{ count, plural, =1 {Én domene} other {Liste over # domener} }", + "type-mobile-app": "Mobilapplikasjon", + "type-mobile-apps": "Mobilapplikasjoner", + "list-of-mobile-apps": "{ count, plural, =1 {Én mobilapplikasjon} other {Liste over # mobilapplikasjoner} }", + "type-mobile-app-bundle": "Mobilpakke", + "type-mobile-app-bundles": "Mobilpakker", + "list-of-mobile-app-bundles": "{ count, plural, =1 {Én mobilpakke} other {Liste over # mobilpakker} }" + }, + "entity-field": { + "created-time": "Opprettelsestid", + "name": "Navn", + "type": "Type", + "first-name": "Fornavn", + "last-name": "Etternavn", + "email": "E-post", + "title": "Tittel", + "country": "Land", + "state": "Stat/Fylke", + "city": "By", + "address": "Adresse", + "address2": "Adresse 2", + "zip": "Postnummer", + "phone": "Telefon", + "label": "Etikett", + "queue-name": "Kønavn", + "service-id": "Tjeneste-ID", + "owner-name": "Eiernavn", + "owner-type": "Eiertype" + }, + "entity-view": { + "entity-view": "Entitetsvisning", + "entity-view-required": "Entitetsvisning er påkrevd.", + "entity-views": "Entitetsvisninger", + "management": "Administrasjon av entitetsvisning", + "view-entity-views": "Vis entitetsvisninger", + "entity-view-alias": "Alias for entitetsvisning", + "aliases": "Aliaser for entitetsvisning", + "no-alias-matching": "'{{alias}}' ble ikke funnet.", + "no-aliases-found": "Ingen aliaser funnet.", + "no-key-matching": "'{{key}}' ble ikke funnet.", + "no-keys-found": "Ingen nøkler funnet.", + "create-new-alias": "Opprett en ny!", + "create-new-key": "Opprett en ny!", + "duplicate-alias-error": "Duplikat alias funnet '{{alias}}'.
Aliaser for entitetsvisning må være unike innenfor dashbordet.", + "configure-alias": "Konfigurer aliaset '{{alias}}'", + "no-entity-views-matching": "Ingen entitetsvisninger som matcher '{{entity}}' ble funnet.", + "public": "Offentlig", + "alias": "Alias", + "alias-required": "Alias for entitetsvisning er påkrevd.", + "remove-alias": "Fjern alias for entitetsvisning", + "add-alias": "Legg til alias for entitetsvisning", + "name-starts-with": "Navnuttrykk for entitetsvisning", + "help-text": "Bruk '%' etter behov: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list": "Liste over entitetsvisninger", + "use-entity-view-name-filter": "Bruk filter", + "entity-view-list-empty": "Ingen entitetsvisninger valgt.", + "entity-view-name-filter-required": "Navnfilter for entitetsvisning er påkrevd.", + "entity-view-name-filter-no-entity-view-matched": "Ingen entitetsvisninger som starter med '{{entityView}}' ble funnet.", + "add": "Legg til entitetsvisning", + "entity-view-public": "Entitetsvisningen er offentlig", + "assign-to-customer": "Tildel til kunde", + "assign-entity-view-to-customer": "Tildel entitetsvisning(er) til kunde", + "assign-entity-view-to-customer-text": "Velg entitetsvisningene som skal tildeles kunden", + "no-entity-views-text": "Ingen entitetsvisninger funnet", + "assign-to-customer-text": "Velg kunden for å tildele entitetsvisning(en)", + "entity-view-details": "Detaljer om entitetsvisning", + "add-entity-view-text": "Legg til ny entitetsvisning", + "delete": "Slett entitetsvisning", + "assign-entity-views": "Tildel entitetsvisninger", + "assign-entity-views-text": "Tildel { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } til kunde", + "delete-entity-views": "Slett entitetsvisninger", + "make-public": "Gjør entitetsvisning offentlig", + "make-private": "Gjør entitetsvisning privat", + "unassign-from-customer": "Fjern tildeling fra kunde", + "unassign-entity-views": "Fjern tildeling av entitetsvisninger", + "unassign-entity-views-action-title": "Fjern tildeling av { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } fra kunde", + "assign-new-entity-view": "Tildel ny entitetsvisning", + "delete-entity-view-title": "Er du sikker på at du vil slette entitetsvisningen '{{entityViewName}}'?", + "delete-entity-view-text": "Vær forsiktig, etter bekreftelsen vil entitetsvisningen og alle relaterte data ikke kunne gjenopprettes.", + "delete-entity-views-title": "Er du sikker på at du vil slette { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }?", + "delete-entity-views-action-title": "Slett { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }", + "delete-entity-views-text": "Vær forsiktig, etter bekreftelsen vil alle valgte entitetsvisninger bli fjernet og alle relaterte data ikke kunne gjenopprettes.", + "make-public-entity-view-title": "Er du sikker på at du vil gjøre entitetsvisningen '{{entityViewName}}' offentlig?", + "make-public-entity-view-text": "Etter bekreftelsen vil entitetsvisningen og alle dens data bli gjort offentlige og tilgjengelige for andre.", + "make-private-entity-view-title": "Er du sikker på at du vil gjøre entitetsvisningen '{{entityViewName}}' privat?", + "make-private-entity-view-text": "Etter bekreftelsen vil entitetsvisningen og alle dens data bli gjort private og ikke være tilgjengelige for andre.", + "unassign-entity-view-title": "Er du sikker på at du vil fjerne tildelingen av entitetsvisningen '{{entityViewName}}'?", + "unassign-entity-view-text": "Etter bekreftelsen vil entitetsvisningen bli fjernet og ikke være tilgjengelig for kunden.", + "unassign-entity-view": "Fjern tildeling av entitetsvisning", + "unassign-entity-views-title": "Er du sikker på at du vil fjerne tildelingen av { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }?", + "unassign-entity-views-text": "Etter bekreftelsen vil alle valgte entitetsvisninger bli fjernet og ikke være tilgjengelige for kunden.", + "entity-view-type": "Entitetsvisningstype", + "entity-view-type-required": "Entitetsvisningstype er påkrevd.", + "select-entity-view-type": "Velg entitetsvisningstype", + "enter-entity-view-type": "Skriv inn entitetsvisningstype", + "any-entity-view": "Enhver entitetsvisning", + "no-entity-view-types-matching": "Ingen entitetsvisningstyper som matcher '{{entitySubtype}}' ble funnet.", + "entity-view-type-list-empty": "Ingen entitetsvisningstyper valgt.", + "entity-view-types": "Entitetsvisningstyper", + "created-time": "Opprettelsestid", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "name-max-length": "Navn må være mindre enn 256 tegn", + "type-max-length": "Type for entitetsvisning må være mindre enn 256 tegn", + "description": "Beskrivelse", + "events": "Hendelser", + "details": "Detaljer", + "copyId": "Kopier entitetsvisningens ID", + "idCopiedMessage": "Entitetsvisningens ID er kopiert til utklippstavlen", + "assignedToCustomer": "Tildelt til kunde", + "unable-entity-view-device-alias-title": "Kan ikke slette alias for entitetsvisning", + "unable-entity-view-device-alias-text": "Enhetsalias '{{entityViewAlias}}' kan ikke slettes da det brukes av følgende widget(er):
{{widgetsList}}", + "select-entity-view": "Velg entitetsvisning", + "start-ts": "Starttid", + "end-ts": "Sluttid", + "date-limits": "Datobegrensninger", + "client-attributes": "Klientattributter", + "shared-attributes": "Delte attributter", + "server-attributes": "Serverattributter", + "timeseries": "Tidsserier", + "client-attributes-placeholder": "Klientattributter", + "shared-attributes-placeholder": "Delte attributter", + "server-attributes-placeholder": "Serverattributter", + "timeseries-placeholder": "Tidsserier", + "target-entity": "Målenhet", + "attributes-propagation": "Attribusjonsdistribusjon", + "attributes-propagation-hint": "Entitetsvisning vil automatisk kopiere spesifiserte attributter fra målenheten hver gang du lagrer eller oppdaterer denne entitetsvisningen. Av ytelsesgrunner blir attributtene til målenheten ikke kopiert ved hver endring. Du kan aktivere automatisk kopiering ved å konfigurere \"copy to view\"-regelnode i regelkjeden og koble \"Post attributes\" og \"Attributes Updated\"-meldinger til den nye regelnoden.", + "timeseries-data": "Tidsseriedata", + "timeseries-data-hint": "Konfigurer tidsserienøkler for målenheten som skal være tilgjengelige for entitetsvisningen. Disse dataene er skrivebeskyttet.", + "search": "Søk etter entitetsvisninger", + "selected-entity-views": "{ count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } valgt", + "assign-entity-view-to-edge": "Tildel entitetsvisning(er) til Edge", + "assign-entity-view-to-edge-text": "Vennligst velg entitetsvisningene som skal tildeles til Edge", + "unassign-entity-view-from-edge-title": "Er du sikker på at du vil fjerne tildelingen av entitetsvisningen '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text": "Etter bekreftelsen vil entitetsvisningen bli fjernet og ikke være tilgjengelig for Edge.", + "unassign-entity-views-from-edge-action-title": "Fjern tildeling av { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} } fra Edge", + "unassign-entity-view-from-edge": "Fjern tildeling av entitetsvisning", + "unassign-entity-views-from-edge-title": "Er du sikker på at du vil fjerne tildelingen av { count, plural, =1 {1 entitetsvisning} other {# entitetsvisninger} }?", + "unassign-entity-views-from-edge-text": "Etter bekreftelsen vil alle valgte entitetsvisninger bli fjernet og ikke være tilgjengelige for Edge." + }, + "event": { + "event-type": "Hendelsestype", + "events-filter": "Hendelsesfilter", + "clean-events": "Fjern hendelser", + "type-error": "Feil", + "type-lc-event": "Livssyklus-hendelse", + "type-stats": "Statistikk", + "type-debug-rule-node": "Feilsøking", + "type-debug-rule-chain": "Feilsøking", + "type-debug-calculated-field": "Feilsøking", + "arguments": "Argumenter", + "result": "Resultat", + "no-events-prompt": "Ingen hendelser funnet", + "error": "Feil", + "alarm": "Alarm", + "event-time": "Hendelsestid", + "server": "Server", + "body": "Innhold", + "method": "Metode", + "type": "Type", + "metadata": "Metadata", + "message": "Melding", + "message-id": "Meldings-ID", + "copy-message-id": "Kopier meldings-ID", + "message-type": "Meldingstype", + "data-type": "Datatype", + "relation-type": "Relasjonstype", + "data": "Data", + "event": "Hendelse", + "status": "Status", + "success": "Vellykket", + "failed": "Mislykket", + "messages-processed": "Behandlede meldinger", + "max-messages-processed": "Maksimalt antall behandlede meldinger", + "min-messages-processed": "Minimum antall behandlede meldinger", + "errors-occurred": "Feil oppstod", + "max-errors-occurred": "Maksimalt antall feil", + "min-errors-occurred": "Minimum antall feil", + "min-value": "Minimumsverdi er 0.", + "all-events": "Alle", + "has-error": "Har feil", + "entity-id": "Enhets-ID", + "copy-entity-id": "Kopier enhets-ID", + "entity-type": "Enhetstype", + "clear-filter": "Fjern filter", + "clear-request-title": "Fjern alle hendelser", + "clear-request-text": "Er du sikker på at du vil fjerne alle hendelser?", + "started": "Startet", + "updated": "Oppdatert", + "stopped": "Stoppet" + }, + "extension": { + "extensions": "Utvidelser", + "selected-extensions": "{ count, plural, =1 {1 utvidelse} other {# utvidelser} } valgt", + "type": "Type", + "key": "Nøkkel", + "value": "Verdi", + "id": "Id", + "extension-id": "Utvidelses-ID", + "extension-type": "Utvidelsestype", + "transformer-json": "JSON *", + "unique-id-required": "Gjeldende utvidelses-ID finnes allerede.", + "delete": "Slett utvidelse", + "add": "Legg til utvidelse", + "edit": "Rediger utvidelse", + "delete-extension-title": "Er du sikker på at du vil slette utvidelsen '{{extensionId}}'?", + "delete-extension-text": "Vær forsiktig, etter bekreftelse vil utvidelsen og alle relaterte data bli permanent fjernet.", + "delete-extensions-title": "Er du sikker på at du vil slette { count, plural, =1 {1 utvidelse} other {# utvidelser} }?", + "delete-extensions-text": "Vær forsiktig, etter bekreftelse vil alle valgte utvidelser bli fjernet.", + "converters": "Konverterere", + "converter-id": "Konverterings-ID", + "configuration": "Konfigurasjon", + "converter-configurations": "Konverteringskonfigurasjoner", + "token": "Sikkerhetstoken", + "add-converter": "Legg til konverterer", + "add-config": "Legg til konfigurasjon", + "device-name-expression": "Uttrykksmønster for enhetsnavn", + "device-type-expression": "Uttrykksmønster for enhetstype", + "custom": "Tilpasset", + "to-double": "Til desimaltall", + "transformer": "Transformer", + "json-required": "Transformer JSON er påkrevd.", + "json-parse": "Kan ikke tolke transformer JSON.", + "attributes": "Attributter", + "add-attribute": "Legg til attributt", + "add-map": "Legg til mapping-element", + "timeseries": "Tidsserie", + "add-timeseries": "Legg til tidsserie", + "field-required": "Felt er påkrevd", + "brokers": "Meklere", + "add-broker": "Legg til megler", + "host": "Vert", + "port": "Port", + "port-range": "Port bør være i området fra 1 til 65535.", + "ssl": "SSL", + "credentials": "Legitimasjon", + "username": "Brukernavn", + "password": "Passord", + "retry-interval": "Forsøksintervall i millisekunder", + "anonymous": "Anonym", + "basic": "Grunnleggende", + "pem": "PEM", + "ca-cert": "CA-sertifikatfil *", + "private-key": "Privat nøkkelfil *", + "cert": "Sertifikatfil *", + "no-file": "Ingen fil valgt.", + "drop-file": "Slipp en fil eller klikk for å velge en fil for opplasting.", + "mapping": "Mapping", + "topic-filter": "Emnefilter", + "converter-type": "Konverteringstype", + "converter-json": "Json", + "json-name-expression": "JSON-uttrykk for enhetsnavn", + "topic-name-expression": "Emneuttrykk for enhetsnavn", + "json-type-expression": "JSON-uttrykk for enhetstype", + "topic-type-expression": "Emneuttrykk for enhetstype", + "attribute-key-expression": "Uttrykk for attributtnøkkel", + "attr-json-key-expression": "JSON-uttrykk for attributtnøkkel", + "attr-topic-key-expression": "Emneuttrykk for attributtnøkkel", + "request-id-expression": "Uttrykk for forespørsels-ID", + "request-id-json-expression": "JSON-uttrykk for forespørsels-ID", + "request-id-topic-expression": "Emneuttrykk for forespørsels-ID", + "response-topic-expression": "Uttrykk for svartemne", + "value-expression": "Verdiuttrykk", + "topic": "Emne", + "timeout": "Tidsavbrudd i millisekunder", + "converter-json-required": "Konverterings-JSON er påkrevd.", + "converter-json-parse": "Kan ikke tolke konverterings-JSON.", + "filter-expression": "Filteruttrykk", + "connect-requests": "Tilkoblingsforespørsler", + "add-connect-request": "Legg til tilkoblingsforespørsel", + "disconnect-requests": "Frakoblingsforespørsler", + "add-disconnect-request": "Legg til frakoblingsforespørsel", + "attribute-requests": "Attributtforespørsler", + "add-attribute-request": "Legg til attributtforespørsel", + "attribute-updates": "Attributtoppdateringer", + "add-attribute-update": "Legg til attributtoppdatering", + "server-side-rpc": "RPC på serverside", + "add-server-side-rpc-request": "Legg til RPC-forespørsel på serverside", + "device-name-filter": "Filter for enhetsnavn", + "attribute-filter": "Attributtfilter", + "method-filter": "Metodefilter", + "request-topic-expression": "Uttrykk for forespørselsemne", + "response-timeout": "Svarfrist i millisekunder", + "topic-expression": "Emneuttrykk", + "client-scope": "Klientområde", + "add-device": "Legg til enhet", + "opc-server": "Servere", + "opc-add-server": "Legg til server", + "opc-add-server-prompt": "Vennligst legg til server", + "opc-application-name": "Applikasjonsnavn", + "opc-application-uri": "Applikasjons-URI", + "opc-scan-period-in-seconds": "Skanningsperiode i sekunder", + "opc-security": "Sikkerhet", + "opc-identity": "Identitet", + "opc-keystore": "Nøkkellager", + "opc-type": "Type", + "opc-keystore-type": "Type", + "opc-keystore-location": "Plassering *", + "opc-keystore-password": "Passord", + "opc-keystore-alias": "Alias", + "opc-keystore-key-password": "Nøkkelpassord", + "opc-device-node-pattern": "Mønstergjenkjenning for enhetsnode", + "opc-device-name-pattern": "Mønstergjenkjenning for enhetsnavn", + "modbus-server": "Servere/slaver", + "modbus-add-server": "Legg til server/slave", + "modbus-add-server-prompt": "Vennligst legg til server/slave", + "modbus-transport": "Transport", + "modbus-tcp-reconnect": "Koble automatisk til igjen", + "modbus-rtu-over-tcp": "RTU over TCP", + "modbus-port-name": "Navn på serielt port", + "modbus-encoding": "Koding", + "modbus-parity": "Paritet", + "modbus-baudrate": "Baudrate", + "modbus-databits": "Databiter", + "modbus-stopbits": "Stoppbiter", + "modbus-databits-range": "Databiter skal være mellom 7 og 8.", + "modbus-stopbits-range": "Stoppbiter skal være mellom 1 og 2.", + "modbus-unit-id": "Enhets-ID", + "modbus-unit-id-range": "Enhets-ID skal være mellom 1 og 247.", + "modbus-device-name": "Enhetsnavn", + "modbus-poll-period": "Spørringsperiode (ms)", + "modbus-attributes-poll-period": "Spørringsperiode for attributter (ms)", + "modbus-timeseries-poll-period": "Spørringsperiode for tidsserier (ms)", + "modbus-poll-period-range": "Spørringsperiode skal være en positiv verdi.", + "modbus-tag": "Tagg", + "modbus-function": "Funksjon", + "modbus-register-address": "Registeradresse", + "modbus-register-address-range": "Registeradresse skal være mellom 0 og 65535.", + "modbus-register-bit-index": "Bitindeks", + "modbus-register-bit-index-range": "Bitindeks skal være mellom 0 og 15.", + "modbus-register-count": "Registerantall", + "modbus-register-count-range": "Registerantall skal være en positiv verdi.", + "modbus-byte-order": "Byte-rekkefølge", + "sync": { + "status": "Status", + "sync": "Synkroniser", + "not-sync": "Ikke synkronisert", + "last-sync-time": "Siste synkroniseringstid", + "not-available": "Ikke tilgjengelig" + }, + "export-extensions-configuration": "Eksporter utvidelseskonfigurasjon", + "import-extensions-configuration": "Importer utvidelseskonfigurasjon", + "import-extensions": "Importer utvidelser", + "import-extension": "Importer utvidelse", + "export-extension": "Eksporter utvidelse", + "file": "Utvidelsesfil", + "invalid-file-error": "Ugyldig utvidelsesfil" + }, + "feature": { + "advanced-features": "Avanserte funksjoner" + }, + "filter": { + "add": "Legg til filter", + "edit": "Rediger filter", + "name": "Filternavn", + "name-required": "Filternavn er påkrevd.", + "duplicate-filter": "Filter med samme navn finnes allerede.", + "filters": "Filtre", + "unable-delete-filter-title": "Kan ikke slette filter", + "unable-delete-filter-text": "Filteret '{{filter}}' kan ikke slettes da det brukes av følgende widget(er):
{{widgetsList}}", + "duplicate-filter-error": "Duplikatfilter funnet '{{filter}}'.
Filtre må være unike innenfor dashbordet.", + "missing-key-filters-error": "Nøkkelfiltre mangler for filter '{{filter}}'.", + "filter": "Filter", + "editable": "Redigerbar", + "no-filters-found": "Ingen filtre funnet.", + "no-filter-text": "Ingen filter spesifisert", + "add-filter-prompt": "Vennligst legg til filter", + "no-filter-matching": "'{{filter}}' ikke funnet.", + "create-new-filter": "Opprett en ny!", + "create-new": "Opprett ny", + "filter-required": "Filter er påkrevd.", + "operation": { + "operation": "Operasjon", + "equal": "lik", + "not-equal": "ikke lik", + "starts-with": "starter med", + "ends-with": "slutter med", + "contains": "inneholder", + "not-contains": "inneholder ikke", + "greater": "større enn", + "less": "mindre enn", + "greater-or-equal": "større eller lik", + "less-or-equal": "mindre eller lik", + "and": "og", + "or": "eller", + "in": "i", + "not-in": "ikke i" + }, + "ignore-case": "ignorér store/små bokstaver", + "value": "Verdi", + "remove-filter": "Fjern filter", + "duplicate-filter-action": "Dupliser filter", + "preview": "Filterforhåndsvisning", + "no-filters": "Ingen filtre konfigurert", + "add-filter": "Legg til filter", + "add-complex-filter": "Legg til komplekst filter", + "add-complex": "Legg til komplekst", + "complex-filter": "Komplekst filter", + "edit-complex-filter": "Rediger komplekst filter", + "edit-filter-user-params": "Rediger brukerparametere for filterpredikat", + "filter-user-params": "Brukerparametere for filterpredikat", + "user-parameters": "Brukerparametere", + "display-label": "Etikett som vises", + "order-priority": "Prioritet for feltrekkefølge", + "key-filter": "Nøkkelfilter", + "key-filters": "Nøkkelfiltre", + "key-name": "Nøkkelnavn", + "key-name-required": "Nøkkelnavn er påkrevd.", + "key-type": { + "key-type": "Nøkkeltype", + "attribute": "Attributt", + "timeseries": "Tidsserie", + "entity-field": "Enhetsfelt", + "constant": "Konstant", + "client-attribute": "Klientattributt", + "server-attribute": "Serverattributt", + "shared-attribute": "Delt attributt" + }, + "value-type": { + "value-type": "Verditype", + "string": "Streng", + "numeric": "Numerisk", + "boolean": "Boolsk", + "date-time": "Dato og tid" + }, + "value-type-required": "Nøkkelverditype er påkrevd.", + "key-value-type-change-title": "Er du sikker på at du vil endre nøkkelverditype?", + "key-value-type-change-message": "Hvis du bekrefter ny verditype, vil alle angitte nøkkelfiltre bli fjernet.", + "no-key-filters": "Ingen nøkkelfiltre konfigurert", + "add-key-filter": "Legg til nøkkelfilter", + "remove-key-filter": "Fjern nøkkelfilter", + "edit-key-filter": "Rediger nøkkelfilter", + "date": "Dato", + "time": "Tid", + "current-tenant": "Nåværende leietaker", + "current-customer": "Nåværende kunde", + "current-user": "Nåværende bruker", + "current-device": "Nåværende enhet", + "default-value": "Standardverdi", + "default-comma-separated-values": "Standard kommaseparerte verdier", + "dynamic-source-type": "Dynamisk kildetype", + "dynamic-value": "Dynamisk verdi", + "no-dynamic-value": "Ingen dynamisk verdi", + "source-attribute": "Kildeattributt", + "switch-to-dynamic-value": "Bytt til dynamisk verdi", + "switch-to-default-value": "Bytt til standardverdi", + "inherit-owner": "Arv fra eier", + "source-attribute-not-set": "Hvis kildeattributt ikke er angitt" + }, + "fullscreen": { + "expand": "Utvid til fullskjerm", + "exit": "Avslutt fullskjerm", + "toggle": "Bytt fullskjermmodus", + "fullscreen": "Fullskjerm" + }, + "function": { + "function": "Funksjon" + }, + "gateway": { + "gateway-name": "Gateway-navn", + "gateway-name-required": "Gateway-navn er påkrevd.", + "gateways": "Gatewayer", + "create-new-gateway": "Opprett ny gateway", + "create-new-gateway-text": "Er du sikker på at du vil opprette en ny gateway med navn: '{{gatewayName}}'?", + "launch-command": "Startkommando", + "no-gateway-found": "Ingen gateway funnet.", + "no-gateway-matching": "'{{item}}' ikke funnet." + }, + "grid": { + "delete-item-title": "Er du sikker på at du vil slette dette elementet?", + "delete-item-text": "Vær forsiktig, etter bekreftelse vil dette elementet og alle relaterte data bli permanent fjernet.", + "delete-items-title": "Er du sikker på at du vil slette { count, plural, =1 {1 element} other {# elementer} }?", + "delete-items-action-title": "Slett { count, plural, =1 {1 element} other {# elementer} }", + "delete-items-text": "Vær forsiktig, etter bekreftelse vil alle valgte elementer bli fjernet og tilknyttede data gå tapt.", + "add-item-text": "Legg til nytt element", + "no-items-text": "Ingen elementer funnet", + "item-details": "Elementdetaljer", + "delete-item": "Slett element", + "delete-items": "Slett elementer", + "scroll-to-top": "Rull til toppen" + }, + "help": { + "goto-help-page": "Gå til hjelpeside", + "show-help": "Vis hjelp" + }, + "home": { + "home": "Hjem", + "profile": "Profil", + "logout": "Logg ut", + "menu": "Meny", + "avatar": "Avatar", + "open-user-menu": "Åpne brukermeny" + }, + "file-input": { + "browse-file": "Bla gjennom fil", + "browse-files": "Bla gjennom filer" + }, + "image": { + "gallery": "Bildegalleri", + "search": "Søk bilde", + "selected-images": "{ count, plural, =1 {1 bilde} other {# bilder} } valgt", + "created-time": "Opprettelsestid", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "resolution": "Oppløsning", + "size": "Størrelse", + "system": "System", + "download-image": "Last ned bilde", + "export-image": "Eksporter bilde til JSON", + "import-image": "Importer bilde fra JSON", + "upload-image": "Last opp bilde", + "edit-image": "Rediger bilde", + "image-details": "Bildedetaljer", + "no-images": "Ingen bilder funnet", + "delete-image": "Slett bilde", + "delete-image-title": "Er du sikker på at du vil slette bildet '{{imageTitle}}'?", + "delete-image-text": "Vær forsiktig, etter bekreftelse vil bildet ikke kunne gjenopprettes.", + "delete-images-title": "Er du sikker på at du vil slette { count, plural, =1 {1 bilde} other {# bilder} }?", + "delete-images-text": "Vær forsiktig, etter bekreftelse vil alle valgte bilder bli fjernet og data gå tapt.", + "list-mode": "Listevisning", + "grid-mode": "Rutenettvisning", + "image-preview": "Forhåndsvisning av bilde", + "update-image": "Oppdater bilde", + "export-failed-error": "Kan ikke eksportere bilde: {{error}}", + "image-json-file": "Bilde JSON-fil", + "invalid-image-json-file-error": "Kan ikke importere bilde fra JSON: Ugyldig bilde JSON-datastruktur.", + "image-is-in-use": "Bildet brukes av andre enheter", + "images-are-in-use": "Bildene brukes av andre enheter", + "image-is-in-use-text": "Bildet '{{title}}' ble ikke slettet fordi det brukes av følgende enheter:", + "images-are-in-use-text": "Ikke alle bilder ble slettet fordi de er i bruk av andre enheter.
Du kan vise refererte enheter ved å klikke Referanser-knappen i bildepanelet.
Hvis du fortsatt vil slette bildene, velg dem i tabellen nedenfor og klikk Slett valgte.", + "delete-image-in-use-text": "Hvis du fortsatt vil slette bildet, klikk Slett uansett.", + "system-entities": "Systemenheter:", + "entities": "enheter:", + "references": "Referanser", + "include-system-images": "Inkluder systembilder", + "clear-image": "Fjern bilde", + "no-image": "Ingen bilde", + "no-image-selected": "Ingen bilde valgt", + "browse-from-gallery": "Bla gjennom galleriet", + "set-link": "Angi lenke", + "image-link": "Bildelenke", + "link": "Lenke", + "copy-image-link": "Kopier bildelenke", + "embed-image": "Bygg inn bilde", + "embed-to-html": "Bygg inn i HTML", + "embed-to-html-hint": "Denne funksjonen vil gjøre lenken tilgjengelig for uautoriserte brukere.", + "embed-to-html-text": "Ved å bruke følgende kodebit, kan du bygge inn et bilde i komponenter basert på vanlig HTML.
Disse inkluderer HTML-kort-widgets, celleinnhold-funksjoner osv.", + "embed-to-angular-template": "Bygg inn i Angular HTML-mal", + "embed-to-angular-template-text": "Ved å bruke følgende kodebit, kan du bygge inn et bilde i Angular HTML-mal for komponenter.
Dette inkluderer Markdown-widgeten, HTML-seksjoner i widget-editoren, egendefinerte handlinger osv." + }, + "image-input": { + "drop-images-or": "Dra og slipp bilder eller", + "drag-and-drop": "Dra og slipp", + "or": "eller", + "browse": "Bla gjennom", + "no-images": "Ingen bilder valgt", + "images": "bilder" + }, + "import": { + "no-file": "Ingen fil valgt", + "drop-file": "Slipp en JSON-fil eller klikk for å velge en fil å laste opp.", + "drop-json-file-or": "Dra og slipp en JSON-fil eller", + "drop-file-csv": "Slipp en CSV-fil eller klikk for å velge en fil å laste opp.", + "drop-file-csv-or": "Dra og slipp en CSV-fil eller", + "column-value": "Verdi", + "column-title": "Tittel", + "column-example": "Eksempelverdi", + "column-key": "Attributt/telemetrinøkkel", + "credentials": "Legitimasjon", + "csv-delimiter": "CSV-avgrenser", + "csv-first-line-header": "Første linje inneholder kolonnenavn", + "csv-update-data": "Oppdater attributter/telemetri", + "details": "Detaljer", + "import-csv-number-columns-error": "En fil må inneholde minst to kolonner", + "import-csv-invalid-format-error": "Ugyldig filformat. Linje: '{{line}}'", + "column-type": { + "name": "Navn", + "type": "Type", + "label": "Etikett", + "column-type": "Kolonnetype", + "client-attribute": "Klientattributt", + "shared-attribute": "Delt attributt", + "server-attribute": "Serverattributt", + "timeseries": "Tidsserie", + "entity-field": "Enhetsfelt", + "access-token": "Tilgangstoken", + "x509": "X.509", + "mqtt": { + "client-id": "MQTT-klient-ID", + "user-name": "MQTT-brukernavn", + "password": "MQTT-passord" + }, + "lwm2m": { + "client-endpoint": "LwM2M endepunktklientnavn", + "security-config-mode": "LwM2M sikkerhetskonfigurasjonsmodus", + "client-identity": "LwM2M klientidentitet", + "client-key": "LwM2M klientnøkkel", + "client-cert": "LwM2M klient offentlig nøkkel", + "bootstrap-server-security-mode": "LwM2M bootstrap server sikkerhetsmodus", + "bootstrap-server-secret-key": "LwM2M bootstrap server hemmelig nøkkel", + "bootstrap-server-public-key-id": "LwM2M bootstrap server offentlig nøkkel eller ID", + "lwm2m-server-security-mode": "LwM2M server sikkerhetsmodus", + "lwm2m-server-secret-key": "LwM2M server hemmelig nøkkel", + "lwm2m-server-public-key-id": "LwM2M server offentlig nøkkel eller ID" + }, + "snmp": { + "host": "SNMP-vert", + "port": "SNMP-port", + "version": "SNMP-versjon (v1, v2c eller v3)", + "community-string": "SNMP fellesskapsstreng" + }, + "isgateway": "Er gateway", + "activity-time-from-gateway-device": "Aktivitetstid fra gateway-enhet", + "description": "Beskrivelse", + "routing-key": "Edge-nøkkel", + "secret": "Edge-hemmelighet" + }, + "stepper-text": { + "select-file": "Velg en fil", + "configuration": "Importkonfigurasjon", + "column-type": "Velg kolonnetype", + "creat-entities": "Oppretter nye enheter" + }, + "message": { + "create-entities": "{{count}} nye enheter ble opprettet.", + "update-entities": "{{count}} enheter ble oppdatert.", + "error-entities": "Det oppsto en feil ved opprettelse av {{count}} enheter." + } + }, + "scada": { + "symbols": "SCADA-symboler", + "search": "Søk symbol", + "selected-symbols": "{ count, plural, =1 {1 symbol} other {# symboler} } valgt", + "download-symbol": "Last ned SCADA-symbol", + "export-symbol": "Eksporter SCADA-symbol til JSON", + "import-symbol": "Importer SCADA-symbol fra JSON", + "upload-symbol": "Last opp SCADA-symbol", + "update-symbol": "Oppdater SCADA-symbol", + "edit-symbol": "Rediger SCADA-symbol", + "symbol-details": "SCADA-symboldetaljer", + "mode-svg": "SVG", + "mode-xml": "XML", + "no-symbols": "Ingen symboler funnet", + "show-hidden-elements": "Vis skjulte elementer", + "hide-hidden-elements": "Skjul skjulte elementer", + "delete-symbol": "Slett SCADA-symbol", + "delete-symbol-title": "Er du sikker på at du vil slette SCADA-symbolet '{{imageTitle}}'?", + "delete-symbol-text": "Vær forsiktig, etter bekreftelse vil SCADA-symbolet ikke kunne gjenopprettes.", + "delete-symbols-title": "Er du sikker på at du vil slette { count, plural, =1 {1 SCADA-symbol} other {# SCADA-symboler} }?", + "delete-symbols-text": "Vær forsiktig, etter bekreftelse vil alle valgte SCADA-symboler og all tilhørende data bli slettet permanent.", + "include-system-symbols": "Inkluder systemsymboler", + "symbol-preview": "Forhåndsvisning av symbol", + "general": "Generelt", + "tags": "Tagger", + "properties": "Egenskaper", + "title": "Tittel", + "description": "Beskrivelse", + "search-tags": "Søk tagger", + "widget-size": "Widget-størrelse", + "cols": "kolonner", + "rows": "rader", + "state-render-function": "Tilstands-gjengivelsesfunksjon", + "preview": "Forhåndsvisning", + "preview-widget-action-text": "Widget-handling '{{type}}' ble utført!", + "no-symbol": "Ingen SCADA-symbol", + "no-symbol-selected": "Ingen SCADA-symbol valgt", + "clear-symbol": "Fjern SCADA-symbol", + "browse-symbol-from-gallery": "Bla gjennom SCADA-symbol fra galleri", + "zoom-in": "Zoom inn", + "zoom-out": "Zoom ut", + "create-widget": "Opprett widget", + "create-widget-from-symbol": "Opprett widget fra SCADA-symbol", + "hidden": "skjult", + "tag": { + "tag": "Tag", + "on-click-action": "Ved klikk-handling", + "no-tags": "Ingen tagger konfigurert", + "delete-tag-text": "Er du sikker på at du vil slette taggen
{{tag}} fra {{elementType}}-elementet?", + "update-tag": "Oppdater tag", + "enter-tag": "Skriv inn tag", + "tag-settings": "Tag-innstillinger", + "remove-tag": "Fjern tag", + "add-tag": "Legg til tag" + }, + "behavior": { + "behavior": "Oppførsel", + "id": "ID", + "name": "Navn", + "type": "Type", + "no-behaviors": "Ingen oppførsler konfigurert", + "add-behavior": "Legg til oppførsel", + "type-action": "Handling", + "type-value": "Verdi", + "type-widget-action": "Widget-handling", + "behavior-settings": "Oppførselsinnstillinger", + "remove-behavior": "Fjern oppførsel", + "hint": "Hint", + "group-title": "Gruppetittel", + "value-type": "Verditype", + "default-value": "Standardverdi", + "true-label": "Etikett for sann", + "false-label": "Etikett for falsk", + "state-label": "Tilstandsetikett", + "default-payload": "Standard nyttelast", + "not-unique-behavior-ids-error": "Oppførsels-ID-er må være unike!", + "default-settings": "Standardinnstillinger" + }, + "symbol": { + "symbol": "SCADA-symbol", + "fluid-presence": "Væsketilstedeværelse", + "fluid-presence-hint": "Indikerer om væske er tilstede i røret.", + "fluid-present": "Væske tilstede", + "present": "Tilstede", + "absent": "Fraværende", + "flow-presence": "Strømningstilstedeværelse", + "flow-presence-hint": "Indikerer om væsken strømmer i røret.", + "flow-present": "Strømning tilstede", + "flow-direction": "Strømningsretning", + "flow-direction-hint": "Indikerer væskens strømningsretning.", + "forward": "Fremover", + "reverse": "Bakover", + "flow-animation-speed": "Strømningsanimasjonshastighet", + "flow-animation-speed-hint": "Desimaltall som angir animasjonshastigheten. 1 - normal hastighet, 0 - ingen animasjon, < 1 - saktere animasjon, > 1 - raskere animasjon.", + "leak": "Lekkasjer", + "leak-hint": "Indikerer om det er lekkasje i røret.", + "leak-present": "Lekkasjer tilstede", + "fluid-color": "Væskefarge", + "pipe-color": "Rørfarge", + "horizontal-pipe": "Horisontalt rør", + "vertical-pipe": "Vertikalt rør", + "horizontal-fluid-color": "Horisontal væskefarge", + "vertical-fluid-color": "Vertikal væskefarge", + "left-pipe": "Venstre rør", + "right-pipe": "Høyre rør", + "top-pipe": "Øvre rør", + "bottom-pipe": "Nedre rør", + "left-fluid-color": "Venstre væskefarge", + "right-fluid-color": "Høyre væskefarge", + "top-fluid-color": "Øvre væskefarge", + "bottom-fluid-color": "Nedre væskefarge", + "display": "Skjerm", + "display-format": "Skjermformat", + "value": "Verdi", + "decimals": "Desimaler", + "units": "Enheter", + "flow-meter-value-hint": "Desimaltall som vises på gjennomstrømningsmåleren", + "value-hint": "Desimaltall som indikerer nåverdi", + "running": "I drift", + "running-hint": "Indikerer om komponenten er i drift.", + "warning-state": "Varseltilstand", + "warning": "Advarsel", + "warning-click": "Advarsel klikk", + "warning-state-hint": "Indikerer om komponenten er i varseltilstand.", + "critical-state": "Kritisk tilstand", + "critical": "Kritisk", + "critical-click": "Kritisk klikk", + "critical-state-hint": "Indikerer om komponenten er i kritisk tilstand.", + "critical-state-animation": "Kritisk tilstandsanimasjon", + "critical-state-animation-hint": "Angir om blinkende animasjon aktiveres i kritisk tilstand.", + "warning-critical-state-animation": "Varsel/kritisk tilstandsanimasjon", + "warning-critical-state-animation-hint": "Angir om blinkende animasjon aktiveres i varsel eller kritisk tilstand.", + "animation": "Animasjon", + "broken": "Ødelagt", + "broken-hint": "Indikerer om komponenten er ødelagt.", + "on-display-click": "Ved skjermklikk", + "on-display-click-hint": "Handling som utløses når brukeren klikker på skjermen.", + "pipe": "Rør", + "default-border-color": "Standard kantfarge", + "active-border-color": "Aktiv kantfarge", + "warning-border-color": "Advarselskantfarge", + "critical-border-color": "Kritisk kantfarge", + "background-color": "Bakgrunnsfarge", + "rotation-animation-speed": "Rotasjonsanimasjonshastighet", + "rotation-animation-speed-hint": "Desimaltall som angir rotasjonshastighet. 1 - normal, 0 - ingen animasjon, < 1 - saktere, > 1 - raskere.", + "on-click": "Ved klikk", + "on-click-hint": "Handling som utløses når brukeren klikker på komponenten.", + "connectors-positions": "Koblingsposisjoner", + "right-connector": "Høyre kobling", + "right-top-connector": "Øverst til høyre kobling", + "right-bottom-connector": "Nederst til høyre kobling", + "left-connector": "Venstre kobling", + "left-top-connector": "Øverst til venstre kobling", + "left-bottom-connector": "Nederst til venstre kobling", + "top-left-connector": "Øvre venstre kobling", + "top-right-connector": "Øvre høyre kobling", + "top-connector": "Øvre kobling", + "bottom-connector": "Nedre kobling", + "running-color": "Driftsfarge", + "stopped-color": "Stoppet farge", + "stopped": "Stoppet", + "warning-color": "Advarselsfarge", + "critical-color": "Kritisk farge", + "opened": "Åpnet", + "opened-hint": "Indikerer om komponenten er i åpen tilstand.", + "open": "Åpne", + "open-hint": "Handling som utløses når brukeren klikker for å åpne komponenten.", + "close": "Lukk", + "close-hint": "Handling som utløses når brukeren klikker for å lukke komponenten.", + "close-state-animation": "Lukket tilstandsanimasjon", + "close-state-animation-hint": "Om blinkende animasjon skal aktiveres når komponenten er i lukket tilstand.", + "opened-color": "Åpen farge", + "closed-color": "Lukket farge", + "opened-rotation-angle": "Rotasjonsvinkel åpen", + "closed-rotation-angle": "Rotasjonsvinkel lukket", + "tank-capacity": "Tankkapasitet", + "tank-capacity-hint": "Desimaltall som angir total kapasitet i tanken.", + "current-volume": "Nåværende volum", + "current-volume-hint": "Desimaltall som angir gjeldende fylt volum.", + "tank-color": "Tankfarge", + "value-box": "Verdiboks", + "value-text": "Verdi tekst", + "scale": "Skala", + "transparent-mode": "Gjennomsiktig modus", + "major-ticks": "Hovedmarkeringer", + "intervals": "Intervaller", + "major-ticks-color": "Farge for hovedmarkeringer", + "normal": "Normal", + "minor-ticks": "Delmarkeringer", + "minor-ticks-color": "Farge for delmarkeringer", + "temperature": "Temperatur", + "temperature-hint": "Desimaltall som angir nåværende temperatur.", + "update-temperature": "Oppdater temperatur", + "update-temperature-hint": "Handling som aktiveres når brukeren klikker for å endre temperaturen.", + "run": "Kjør", + "run-hint": "Handling som aktiveres når brukeren klikker for å starte komponenten.", + "stop": "Stopp", + "stop-hint": "Handling som aktiveres når brukeren klikker for å stoppe komponenten.", + "temperature-step": "Temperaturtrinn", + "heat-pump-color": "Farge for varmepumpe", + "power-button-background": "Bakgrunn for strømbryter", + "value-box-background": "Bakgrunn for verdiboks", + "value-units": "Verdi-enheter", + "filtration-mode": "Filtreringsmodus", + "filtration-mode-hint": "Heltallsverdi som angir gjeldende filtreringsmodus.", + "filtration-mode-update": "Oppdater filtreringsmodus", + "filtration-mode-update-hint": "Handling som aktiveres når brukeren klikker for å endre filtreringsmodus.", + "filter-mode": "Filter", + "waste-mode": "Avfall", + "backwash-mode": "Tilbakespyling", + "recirculate-mode": "Resirkulering", + "rinse-mode": "Skylling", + "closed-mode": "Lukket", + "sand-filter-color": "Farge for sandfilter", + "mode-box-background": "Modusboks bakgrunn", + "border-color": "Kantfarge", + "label-color": "Etikettfarge", + "water-leak-hint": "Indikerer om det er lekkasje.", + "default-color": "Standardfarge", + "leak-color": "Lekkasjefarge", + "full-value": "Full verdi", + "full-value-hint": "Desimaltall som angir full verdi.", + "label": "Etikett", + "icon": "Ikon", + "button-color": "Knappfarge", + "on-label": "Tekst for 'På'", + "off-label": "Tekst for 'Av'", + "arrow-presence": "Pil tilstedeværelse", + "arrow-presence-hint": "Indikerer om pil er tilstede i koblingen.", + "arrow-present": "Pil tilstede", + "arrow-direction": "Strømningsretning", + "arrow-direction-hint": "Indikerer strømningsretning.", + "flow-animation": "Strømningsnærvær", + "flow-animation-hint": "Indikerer om væsken strømmer i koblingen.", + "flow": "Strøm", + "flow-line": "Linje", + "flow-line-style": "Linjestil", + "flow-style-hint": "Angi verdier for Dash og Gap slik at summen er delelig med 100 for perfekt synkronisering av animasjon.", + "flow-dash-cap": "Dash-kant", + "dash-cap-butt": "Rett", + "dash-cap-round": "Rund", + "dash-cap-square": "Kvadratisk", + "dash": "Strek", + "gap": "Mellomrom", + "main-line": "Hovedlinje", + "line": "Linje", + "line-color": "Linjefarge", + "arrow-color": "Pilfarge", + "target-value": "Målverdi", + "target-value-hint": "Angir målpunkt på skalaen.", + "min-max-value": "Min og maks verdi", + "min-value": "Min", + "max-value": "Maks", + "progress-bar": "Fremdriftsindikator", + "progress-arrow": "Fremdriftspil", + "warning-scale-color": "Advarselsskala farge", + "critical-scale-color": "Kritisk skala farge", + "scale-color": "Skalafarge", + "target": "Mål", + "high-warning-state": "Høy advarselstilstand", + "show-high-warning-scale": "Vis høy advarselsskala", + "high-warning-scale": "Høy advarselsskala", + "high-warning-state-hint": "Desimaltall angir et høyt advarselsområde opp til høy kritisk eller maksimal verdi.", + "low-warning-state": "Lav advarselstilstand", + "show-low-warning-scale": "Vis lav advarselsskala", + "low-warning-scale": "Lav advarselsskala", + "low-warning-state-hint": "Desimaltall angir et lavt advarselsområde ned til lav kritisk eller minimumsverdi.", + "high-critical-state": "Høy kritisk tilstand", + "show-high-critical-scale": "Vis høy kritisk skala", + "high-critical-scale": "Høy kritisk skala", + "high-critical-state-hint": "Desimaltall angir et høyt kritisk område opp til maksimal skala.", + "low-critical-state": "Lav kritisk tilstand", + "show-low-critical-scale": "Vis lav kritisk skala", + "low-critical-scale": "Lav kritisk skala", + "low-critical-state-hint": "Desimaltall angir et lavt kritisk område ned til minimumsskala.", + "filter-color": "Filterfarge", + "colors": "Farger", + "indicator-colors": "Indikatorfarger", + "enabled": "Aktivert", + "disabled": "Deaktivert", + "on": "PÅ", + "off": "AV", + "on-off-state": "På/Av-tilstand", + "on-off-state-hint": "Angir om komponenten er i På- eller Av-tilstand.", + "on-update-state": "Oppdater til På", + "on-update-state-hint": "Handling ved klikk for å oppdatere til På-tilstand.", + "off-update-state": "Oppdater til Av", + "off-update-state-hint": "Handling ved klikk for å oppdatere til Av-tilstand.", + "voltage": "Spenning", + "input-voltage": "Inngangsspenning", + "input-voltage-hint": "Desimaltall angir inngangsspenning.", + "output-voltage": "Utgangsspenning", + "output-voltage-hint": "Desimaltall angir utgangsspenning.", + "first-phase-voltage": "Første fase spenning", + "second-phase-voltage": "Andre fase spenning", + "third-phase-voltage": "Tredje fase spenning", + "phase-voltage-hint": "Desimaltall angir spenningsverdi for gjeldende fase", + "voltage-hint": "Desimaltall angir nåværende spenning", + "current-voltage-color": "Farge for nåværende spenning", + "phase-indicator-color": "Faseindikatorfarge", + "measured": "Målt", + "measured-hint": "Desimaltall angir energibruk i kilowattimer", + "day-rate": "Dagtakst", + "night-rate": "Nattakst", + "off-peak-rate": "Lavtrafikktakst", + "peak-rate": "Topptakst", + "export-rate": "Eksporttakst", + "operating-mode": "Driftsmodus", + "bypass-mode": "Forbikobling", + "operating-mode-hint": "Heltallsverdi som angir gjeldende driftsmodus (0 - AV, 1 - PÅ, 2 - FORBIKOBLING)", + "connected": "Tilkoblet", + "connected-hint": "Angir om komponenten er i tilkoblet tilstand.", + "disconnected": "Frakoblet", + "indicator": "Indikator", + "operation-mode": "Operasjonsmodus", + "operation-mode-hint": "Angir om inverter er i Nettdrift eller Invertermodus.", + "operation-mode-indicators-color": "Farge på driftsmodusindikatorer", + "mains-on-mode": "Nettdrift aktiv", + "inverter-on-mode": "Inverter aktiv", + "charging-mode": "Lademodus", + "charging-mode-hint": "Heltallsverdi som angir gjeldende lademodus (1 - Bulk, 2 - Absorpsjon, 3 - Vedlikehold)", + "charging-mode-indicators-color": "Farge på lademodusindikatorer", + "inverter-faults": "Feil", + "inverter-fault-indicators-color": "Farge på feilindikatorer", + "overload-fault": "Overbelastning", + "overload-fault-hint": "Angir om inverter er overbelastet.", + "low-battery-fault": "Lavt batteri", + "low-battery-fault-hint": "Angir om batteriet er for mye utladet.", + "temperature-fault": "Temperatur", + "temperature-fault-hint": "Angir høy temperatur i inverter.", + "triangle": "Trekant", + "socket": "Stikkontakt", + "left-button": "Venstre knapp", + "right-button": "Høyre knapp", + "alarm-colors": "Alarmfarger", + "hook-color": "Krokfarge" + } + }, + "item": { + "selected": "Valgt" + }, + "js-func": { + "no-return-error": "Funksjonen må returnere en verdi!", + "return-type-mismatch": "Funksjonen må returnere en verdi av typen '{{type}}'!", + "tidy": "Rydd", + "mini": "Mini", + "modules": "Moduler", + "remove-module": "Fjern modul", + "no-modules": "Ingen moduler konfigurert", + "add-module": "Legg til modul", + "module-alias": "Alias", + "invalid-module-alias-name": "Ugyldig aliasnavn", + "module-resource": "JS modulressurs", + "not-unique-module-aliases-error": "Modulaliaser må være unike!", + "show-module-info": "Vis modulinfo", + "show-module-source-code": "Vis modulens kildekode", + "module-members": "Modulmedlemmer", + "module-no-members": "Modulen har ingen eksporterte medlemmer", + "module-load-error": "Feil ved lasting av modul", + "source-code": "Kildekode", + "source-code-load-error": "Feil ved lasting av kildekode", + "no-js-module-text": "Ingen JS-moduler funnet", + "no-js-module-matching": "Ingen JS-moduler som matcher '{{module}}' ble funnet." + }, + "key-val": { + "key": "Nøkkel", + "value": "Verdi", + "remove-entry": "Fjern oppføring", + "add-entry": "Legg til oppføring", + "no-data": "Ingen oppføringer" + }, + "layout": { + "layout": "Oppsett", + "layouts": "Oppsett", + "manage": "Administrer oppsett", + "settings": "Oppsettinnstillinger", + "color": "Farge", + "main": "Hoved", + "right": "Høyre", + "left": "Venstre", + "select": "Velg måloppsett", + "percentage-width": "Prosentvis bredde (%)", + "fixed-width": "Fast bredde (px)", + "left-width": "Venstre kolonne (%)", + "right-width": "Høyre kolonne (%)", + "pick-fixed-side": "Fast side:", + "layout-fixed-width": "Fast bredde (px)", + "value-min-error": "Verdien må være større enn {{min}}{{unit}}", + "value-max-error": "Verdien må være mindre enn {{max}}{{unit}}", + "layout-fixed-width-required": "Fast bredde er påkrevd", + "right-width-percentage-required": "Prosentandel for høyre er påkrevd", + "left-width-percentage-required": "Prosentandel for venstre er påkrevd", + "divider": "Skillelinje", + "right-side": "Oppsett for høyre side", + "left-side": "Oppsett for venstre side", + "add-new-breakpoint": "Legg til nytt brytpunkt", + "breakpoint": "Brytpunkt", + "breakpoints": "Brytpunkter", + "copy-from": "Kopier fra", + "size": "Størrelse", + "delete-breakpoint-title": "Er du sikker på at du vil slette brytpunktet '{{name}}'?", + "delete-breakpoint-text": "Vennligst merk at etter bekreftelse vil brytpunktet ikke kunne gjenopprettes, og innstillingene vil tilbakestilles til standard brytpunkt." + }, + "legend": { + "direction": "Retning", + "position": "Posisjon", + "show-values": "Vis verdier", + "min-option": "Min", + "max-option": "Maks", + "average-option": "Gjennomsnitt", + "total-option": "Total", + "latest-option": "Siste", + "sort-legend": "Sorter datanøkler i forklaringen", + "show-max": "Vis maksverdi", + "show-min": "Vis minverdi", + "show-avg": "Vis gjennomsnittsverdi", + "show-total": "Vis totalverdi", + "show-latest": "Vis siste verdi", + "settings": "Forklaringsinnstillinger", + "min": "min", + "max": "maks", + "avg": "gj.sn", + "total": "total", + "latest": "siste", + "Min": "Min", + "Max": "Maks", + "Avg": "Gj.sn", + "Total": "Total", + "Latest": "Siste", + "comparison-time-ago": { + "previousInterval": "(forrige intervall)", + "customInterval": "(tilpasset intervall)", + "days": "(dag siden)", + "weeks": "(uke siden)", + "months": "(måned siden)", + "years": "(år siden)" + }, + "column-title": "Kolonnetittel", + "label": "Etikett", + "value": "Verdi" + }, + "login": { + "login": "Logg inn", + "request-password-reset": "Be om tilbakestilling av passord", + "reset-password": "Tilbakestill passord", + "create-password": "Opprett passord", + "two-factor-authentication": "Tofaktorautentisering", + "passwords-mismatch-error": "Passordene må være like!", + "password-again": "Gjenta passord", + "sign-in": "Vennligst logg inn", + "username": "Brukernavn (epost)", + "remember-me": "Husk meg", + "forgot-password": "Glemt passord?", + "password-reset": "Tilbakestilling av passord", + "expired-password-reset-message": "Dine legitimasjonsopplysninger er utløpt! Vennligst opprett et nytt passord.", + "new-password": "Nytt passord", + "new-password-again": "Bekreft nytt passord", + "password-link-sent-message": "Tilbakestillingslenke er sendt", + "email": "Epost", + "invalid-email-format": "Ugyldig epostformat.", + "login-with": "Logg inn med {{name}}", + "or": "eller", + "error": "Innloggingsfeil", + "verify-your-identity": "Bekreft din identitet", + "select-way-to-verify": "Velg en måte å verifisere på", + "resend-code": "Send koden på nytt", + "resend-code-wait": "Send ny kode om { time, plural, =1 {1 sekund} other {# sekunder} }", + "try-another-way": "Prøv en annen metode", + "totp-auth-description": "Skriv inn sikkerhetskoden fra autentiseringsappen din.", + "totp-auth-placeholder": "Kode", + "sms-auth-description": "En sikkerhetskode er sendt til telefonen din på {{contact}}.", + "sms-auth-placeholder": "SMS-kode", + "email-auth-description": "En sikkerhetskode er sendt til epostadressen din på {{contact}}.", + "email-auth-placeholder": "Epostkode", + "backup-code-auth-description": "Skriv inn en av backup-kodene dine.", + "backup-code-auth-placeholder": "Backup-kode", + "activation-link-expired": "Aktiveringslenken er utløpt", + "activation-link-expired-message": "Lenken for å aktivere profilen din er utløpt. Du kan gå tilbake til innloggingssiden for å motta en ny epost.", + "reset-password-link-expired": "Lenken for tilbakestilling av passord er utløpt", + "reset-password-link-expired-message": "Lenken for å tilbakestille passordet ditt er utløpt. Du kan gå tilbake til innloggingssiden for å motta en ny epost." + }, + "mobile": { + "add-application": "Legg til applikasjon", + "app-id": "App-ID", + "app-id-required": "App-ID er påkrevd", + "app-id-pattern": "Ugyldig format for App-ID", + "app-store-link": "App Store-lenke", + "app-store-link-required": "App Store-lenke er påkrevd", + "application-details": "Applikasjonsdetaljer", + "application-package": "Applikasjonspakke", + "application-secret": "Applikasjonsnøkkel", + "application-secret-required": "Applikasjonsnøkkel er påkrevd", + "application": "Applikasjon", + "applications": "Applikasjoner", + "copy-app-id": "Kopier App-ID", + "copy-app-store-link": "Kopier App Store-lenke", + "copy-application-package": "Kopier applikasjonspakke", + "copy-application-secret": "Kopier applikasjonsnøkkel", + "copy-google-play-link": "Kopier Google Play-lenke", + "copy-sha256-certificate-fingerprints": "Kopier SHA256-sertifikatfingeravtrykk", + "delete-application": "Slett applikasjon", + "delete-application-button-text": "Jeg forstår konsekvensene, slett applikasjonen", + "delete-application-text": "Denne handlingen kan ikke angres. Dette vil permanent slette applikasjonen din.
Hvis du ikke ønsker å slette den permanent, kan du midlertidig suspendere applikasjonen.
For å slette applikasjonen uansett, skriv \"{{phrase}}\" for å bekrefte.", + "delete-application-title-short": "Er du sikker på at du vil slette applikasjonen '{{name}}'?", + "delete-application-text-short": "Vær forsiktig, etter bekreftelsen vil applikasjonen og all relatert data være uopprettelig.", + "delete-application-phrase": "slett applikasjon", + "delete-applications-bundle-text": "Vær forsiktig, etter bekreftelsen vil mobilpakken og all relatert data være uopprettelig.", + "delete-applications-bundle-title": "Er du sikker på at du vil slette mobilpakken '{{bundleName}}'?", + "generate-application-secret": "Generer applikasjonsnøkkel", + "google-play-link": "Google Play-lenke", + "google-play-link-required": "Google Play-lenke er påkrevd", + "latest-version": "Nyeste versjon", + "min-version": "Minimum versjon", + "invalid-version-pattern": "Ugyldig versjonsformat. Bruk formatet: hoved.minor.patch (f.eks. 1.0.0).", + "mobile-center": "Mobil-senter", + "mobile-package": "Applikasjonspakke", + "mobile-package-max-length": "Applikasjonspakken må være under 256 tegn", + "mobile-package-required": "Applikasjonspakke er påkrevd.", + "mobile-package-pattern": "Ugyldig format for applikasjonspakke", + "no-application": "Ingen applikasjoner funnet", + "no-bundles": "Ingen pakker funnet", + "platform-type": "Plattformtype", + "search-application": "Søk applikasjoner", + "search-bundles": "Søk pakker", + "set": "Angi", + "sha256-certificate-fingerprints": "SHA256-sertifikatfingeravtrykk", + "sha256-certificate-fingerprints-required": "SHA256-sertifikatfingeravtrykk er påkrevd", + "sha256-certificate-fingerprints-pattern": "Ugyldig format for SHA256-sertifikatfingeravtrykk", + "show-hidden-pages": "Vis skjulte sider", + "status": "Status", + "status-type": { + "deprecated": "Utdatert", + "draft": "Utkast", + "published": "Publisert", + "suspended": "Suspendert" + }, + "store-information": "Butikkinformasjon", + "version-information": "Versjonsinformasjon", + "min-version-release-notes": "Utgivelsesnotater for minimum versjon", + "latest-version-release-notes": "Utgivelsesnotater for nyeste versjon", + "bundle": "Pakke", + "bundles": "Pakker", + "add-bundle": "Legg til pakke", + "title": "Tittel", + "title-required": "Tittel er påkrevd", + "title-cannot-contain-only-spaces": "Tittel kan ikke kun inneholde mellomrom", + "title-max-length": "Tittel må være under 256 tegn", + "oauth-clients": "OAuth 2.0-klienter", + "android-app": "Android-app", + "android-application": "Android-applikasjon", + "ios-app": "iOS-app", + "ios-application": "iOS-applikasjon", + "invalid-store-link": "Ugyldig butikkslenke", + "enable-oauth": "Aktiver OAuth 2.0", + "enable-self-registration": "Aktiver selvregistrering", + "edit-bundle": "Rediger pakke", + "description": "Beskrivelse", + "basic-settings": "Grunninnstillinger", + "no-application-matching": "Ingen applikasjon som matcher '{{entity}}' ble funnet.", + "no-bundle-matching": "Ingen pakke som matcher '{{entity}}' ble funnet.", + "application-required": "Applikasjon er påkrevd.", + "bundle-required": "Pakke er påkrevd.", + "no-application-text": "Ingen applikasjoner funnet", + "no-bundle-text": "Ingen pakke funnet", + "layout": "Oppsett", + "pages": "Sider", + "hide-all-pages": "Skjul alle sider", + "reset-to-default-pages": "Tilbakestill til standard sider", + "add-specific-page": "Legg til spesifikk side", + "visible": "Synlig", + "hidden": "Skjult", + "reset-to-page-default": "Tilbakestill side til standard", + "mobile-599": "Mobil (maks 599px)", + "tablet-959": "Nettbrett (maks 959px)", + "max-element-number": "Maks antall elementer", + "page-name": "Sidenavn", + "page-name-required": "Sidenavn er påkrevd.", + "page-name-cannot-contain-only-spaces": "Sidenavn kan ikke kun inneholde mellomrom.", + "page-name-max-length": "Sidenavn må være mindre enn 256 tegn", + "page-type": "Sidetype", + "pages-types": { + "dashboard": "Dashbord", + "web-view": "Webvisning", + "custom": "Egendefinert" + }, + "url": "URL", + "invalid-url-format": "Ugyldig URL-format", + "path": "Sti", + "invalid-path-format": "Ugyldig sti-format", + "custom-page": "Egendefinert side", + "edit-page": "Rediger side", + "edit-custom-page": "Rediger egendefinert side", + "delete-page": "Slett side", + "qr-code-widget": "QR-kode-widget", + "type-here": "Skriv her", + "configuration-dialog": "Konfigurasjonsdialog", + "configuration-app": "Konfigurasjonsapp", + "configuration-step": { + "prepare-environment-title": "Forbered utviklingsmiljø", + "prepare-environment-text": "Flutter ThingsBoard Mobile Application krever Flutter SDK. Følg instruksjonene for å sette opp Flutter SDK.", + "get-source-code-title": "Hent kildekode for appen", + "get-source-code-text": "Du kan hente kildekoden til Flutter ThingsBoard Mobile Application ved å klone den fra GitHub-repositoriet:", + "configure-api-title": "Konfigurer ThingsBoard API-endepunkt", + "configure-api-text": "Åpne prosjektet flutter_thingsboard_pe_app i editoren/IDE-en din. Rediger:", + "configure-api-hint": "Angi verdien av konstanten thingsBoardApiEndpoint slik at den samsvarer med API-endepunktet til din ThingsBoard-serverinstans. Ikke bruk \"localhost\" eller \"127.0.0.1\" som vertsnavn.", + "run-app-title": "Kjør appen", + "run-app-text": "Kjør appen som beskrevet i din IDE.\nHvis du bruker terminalen, kjør appen med følgende kommando:", + "more-information": "Detaljert informasjon finnes i vår Kom-i-gang dokumentasjon.", + "getting-started": "Kom i gang", + "configure-package-title": "Konfigurer applikasjonspakke", + "configure-package-text": "Du kan manuelt endre applikasjonspakken eller bruke tredjeparts CLI-verktøy.", + "configure-package-text-install": "For å installere Rename CLI-verktøyet, kjør følgende kommando:", + "configure-package-run-commands": "Kjør disse kommandoene i prosjektets rotmappe:" + } + }, + "notification": { + "action-button": "Handlingsknapp", + "action-type": "Handlingstype", + "active": "Aktiv", + "add-notification-recipients-group": "Legg til varslingsmottakergruppe", + "add-notification-template": "Legg til varslingsmal", + "add-recipient": "Legg til mottaker", + "add-recipients": "Legg til mottakere", + "add-rule": "Legg til regel", + "add-stage": "Legg til trinn", + "add-template": "Legg til mal", + "after": "Etter", + "alarm-assignment-trigger-settings": "Innstillinger for utløser ved alarmtilordning", + "alarm-comment-trigger-settings": "Innstillinger for utløser ved alarmkommentar", + "alarm-trigger-settings": "Innstillinger for alarmutløser", + "all": "Alle", + "api-feature-hint": "Hvis feltet er tomt, vil utløseren gjelde for alle API-funksjoner", + "api-usage-trigger-settings": "Innstillinger for API-bruksutløser", + "new-platform-version-trigger-settings": "Innstillinger for ny plattformversjonsutløser", + "rate-limits-trigger-settings": "Innstillinger for utløser ved overskredet hastighetsgrense", + "task-processing-failure-trigger-settings": "Innstillinger for utløser ved feil i oppgavebehandling", + "at-least-one-should-be-selected": "Minst én må velges", + "basic-settings": "Grunnleggende innstillinger", + "button-text": "Knappetekst", + "button-text-required": "Knappetekst er påkrevd", + "button-text-max-length": "Knappetekst må være mindre enn eller lik {{ length }} tegn", + "compose": "Skriv", + "conversation": "Samtale", + "conversation-required": "Samtale er påkrevd", + "copy-notification-template": "Kopier varslingsmal", + "copy-rule": "Kopier regel", + "copy-template": "Kopier mal", + "create-new": "Opprett ny", + "created": "Opprettet", + "customize-messages": "Tilpass meldinger", + "delete-notification-text": "Vær forsiktig, etter bekreftelse vil varslingen ikke kunne gjenopprettes.", + "delete-notification-title": "Er du sikker på at du vil slette varslingen?", + "delete-notifications-text": "Vær forsiktig, etter bekreftelse vil varslingene ikke kunne gjenopprettes.", + "delete-notifications-title": "Er du sikker på at du vil slette { count, plural, =1 {1 varsling} other {# varslinger} }?", + "delete-recipient-text": "Vær forsiktig, etter bekreftelse vil mottakeren ikke kunne gjenopprettes.", + "delete-recipient-title": "Er du sikker på at du vil slette mottakeren '{{recipientName}}'?", + "delete-recipients-text": "Vær forsiktig, etter bekreftelse vil mottakerne ikke kunne gjenopprettes.", + "delete-recipients-title": "Er du sikker på at du vil slette { count, plural, =1 {1 mottaker} other {# mottakere} }?", + "delete-request-text": "Vær forsiktig, etter bekreftelse vil forespørselen ikke kunne gjenopprettes.", + "delete-request-title": "Er du sikker på at du vil slette forespørselen?", + "delete-requests-text": "Vær forsiktig, etter bekreftelse vil forespørslene ikke kunne gjenopprettes.", + "delete-requests-title": "Er du sikker på at du vil slette { count, plural, =1 {1 forespørsel} other {# forespørsler} }?", + "delete-rule-text": "Vær forsiktig, etter bekreftelse vil regelen ikke kunne gjenopprettes.", + "delete-rule-title": "Er du sikker på at du vil slette regelen '{{ruleName}}'?", + "delete-rules-text": "Vær forsiktig, etter bekreftelse vil reglene ikke kunne gjenopprettes.", + "delete-rules-title": "Er du sikker på at du vil slette { count, plural, =1 {1 regel} other {# regler} }?", + "delete-template-text": "Vær forsiktig, etter bekreftelse vil malen ikke kunne gjenopprettes.", + "delete-template-title": "Er du sikker på at du vil slette malen '{{templateName}}'?", + "delete-templates-text": "Vær forsiktig, etter bekreftelse vil malene ikke kunne gjenopprettes.", + "delete-templates-title": "Er du sikker på at du vil slette { count, plural, =1 {1 mal} other {# maler} }?", + "deleted": "Slettet", + "delivery-method": { + "delivery-method": "Leveringsmetode", + "email": "E-post", + "email-preview": "Forhåndsvisning av e-postvarsel", + "slack": "Slack", + "slack-preview": "Forhåndsvisning av Slack-varsel", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Forhåndsvisning av Microsoft Teams-varsel", + "sms": "SMS", + "sms-preview": "Forhåndsvisning av SMS-varsel", + "web": "Web", + "web-preview": "Forhåndsvisning av nettvarsel", + "mobile-app": "Mobilapp", + "mobile-app-preview": "Forhåndsvisning av mobilapp-varsel" + }, + "delivery-method-not-configure-click": "Leveringsmetode er ikke konfigurert. Klikk for å sette opp.", + "delivery-method-not-configure-contact": "Leveringsmetode er ikke konfigurert. Kontakt systemadministratoren din.", + "delivery-methods": "Leveringsmetoder", + "description": "Beskrivelse", + "device-activity-trigger-settings": "Innstillinger for utløser ved enhetsaktivitet", + "device-list-rule-hint": "Hvis feltet er tomt, vil utløseren gjelde for alle enheter", + "device-profiles-list-rule-hint": "Hvis feltet er tomt, vil utløseren gjelde for alle enhetsprofiler", + "disabled": "Deaktivert", + "edge-trigger-settings": "Innstillinger for Edge-utløser", + "edge-list-rule-hint": "Hvis feltet er tomt, vil utløseren gjelde for alle edge-instanser", + "edit-notification-recipients-group": "Rediger varslingsmottakergruppe", + "edit-notification-template": "Rediger varslingsmal", + "edit-rule": "Rediger regel", + "edit-template": "Rediger mal", + "enabled": "Aktivert", + "entities-limit-trigger-settings": "Innstillinger for utløser basert på entitetsgrense", + "entity-action-trigger-settings": "Innstillinger for entitetshandlingsutløser", + "entity-type": "Enhetstype", + "escalation-chain": "Eskaleringskjede", + "failed-send": "Sendingsfeil", + "fails": "{ count, plural, =1 {1 feil} other {# feil} }", + "filter": "Filter", + "first-recipient": "Første mottaker", + "inactive": "Inaktiv", + "inbox": "Innboks", + "notification-inbox": "Varslinger / Innboks", + "input-field-support-templatization": "Inndatafelt støtter templatmaler.", + "input-fields-support-templatization": "Inndatafelt støtter templatmaler.", + "link": "Lenke", + "link-required": "Lenke er påkrevd", + "link-type": { + "dashboard": "Åpne dashbord", + "link": "Åpne URL-lenke" + }, + "loading-notifications": "Laster varsler...", + "management": "Varslingsadministrasjon", + "mark-all-as-read": "Merk alle som lest", + "mark-as-read": "Merk som lest", + "message": "Melding", + "message-required": "Melding er påkrevd", + "message-max-length": "Meldingen må være mindre enn eller lik {{ length }} tegn", + "name": "Navn", + "name-required": "Navn er påkrevd", + "new-notification": "Nytt varsel", + "no-inbox-notification": "Ingen varsler funnet", + "no-notification-request": "Ingen varslingsforespørsel", + "no-notification-templates": "Ingen varslingsmaler funnet", + "no-notifications-yet": "Ingen varsler ennå", + "no-recipients-notification": "Ingen mottakere til varsel", + "no-recipients-matching": "Ingen mottakere som samsvarer med '{{entity}}' ble funnet.", + "no-recipients-text": "Ingen mottakere funnet", + "no-rule": "Ingen regel konfigurert", + "no-rules-notification": "Ingen regler til varsel", + "no-severity-found": "Ingen alvorlighetsgrad funnet", + "no-severity-matching": "'{{severity}}' ble ikke funnet.", + "no-template-matching": "Ingen ressurser som samsvarer med '{{template}}' ble funnet.", + "not-found-slack-recipient": "Slack-mottaker ikke funnet", + "notification": "Varsel", + "notification-center": "Varslingssenter", + "notification-tap-action": "Handling ved trykk på varsel", + "notification-tap-action-hint": "Hvis deaktivert, vil standard alarmdashbord brukes", + "notify": "Varsle", + "notify-again": "Varsle igjen", + "notify-alarm-action": { + "acknowledged": "Alarm bekreftet", + "assigned": "Alarm tilordnet", + "cleared": "Alarm fjernet", + "created": "Alarm opprettet", + "severity-changed": "Alarm alvorlighetsgrad endret", + "unassigned": "Alarm fjernet fra tilordning" + }, + "notify-on": "Varsle ved", + "notify-on-comment-update": "Varsle ved kommentaroppdatering", + "notify-on-required": "Varsle ved er påkrevd", + "notify-on-unassign": "Varsle ved fjernet tilordning", + "notify-only-user-comments": "Varsle kun brukers kommentarer", + "only-rule-chain-lifecycle-failures": "Kun regelkjedens livssyklusfeil", + "only-rule-node-lifecycle-failures": "Kun regelnodens livssyklusfeil", + "platform-users": "Plattformbrukere", + "rate-limits": "Hastighetsgrenser", + "rate-limits-hint": "Hvis feltet er tomt, vil utløseren gjelde for alle hastighetsgrenser", + "recipient": "Mottaker", + "recipient-group": "Mottakergruppe", + "recipient-type": { + "affected-tenant-administrators": "Berørte leietakeradministratorer", + "affected-user": "Berørt bruker", + "all-users": "Alle brukere", + "customer-users": "Kundebrukere", + "system-administrators": "Systemadministratorer", + "tenant-administrators": "Leietakeradministratorer", + "user-filters": "Brukerfilter", + "user-list": "Brukerliste", + "users-entity-owner": "Brukere til entitetens eier" + }, + "recipients": "Mottakere", + "notification-recipient": "Varslingsmottaker", + "notification-recipient-required": "Varslingsmottaker er påkrevd.", + "notification-recipients": "Varslinger / Mottakere", + "recipients-count": "{ count, plural, =1 {1 mottaker} other {# mottakere} }", + "recipients-required": "Mottakere er påkrevd", + "refresh-allow-delivery-method": "Oppdater tillatt leveringsmetode", + "request-search": "Forespørselsøk", + "request-status": { + "processing": "Behandler", + "scheduled": "Planlagt", + "sent": "Sendt" + }, + "review": "Gjennomgå", + "rule": "Regel", + "rule-chain-list-rule-hint": "Hvis feltet er tomt, vil utløseren gjelde for alle regelkjeder", + "rule-engine-events-trigger-settings": "Innstillinger for regelmotorens hendelsesutløser", + "rule-engine-filter": "Regelmotorfilter", + "rule-name": "Regelnavn", + "rule-name-required": "Navn er påkrevd", + "rule-disable": "Deaktiver varslingsregel", + "rule-enable": "Aktiver varslingsregel", + "rule-node-filter": "Regelnodenfilter", + "rules": "Regler", + "notification-rules": "Varslinger / Regler", + "scheduler-later": "Planlegg for senere", + "search-notification": "Søk varsler", + "search-recipients": "Søk mottakere", + "search-rules": "Søk regler", + "search-templates": "Søk maler", + "see-documentation": "Se dokumentasjon", + "selected-notifications": "{ count, plural, =1 {1 varsel} other {# varsler} } valgt", + "selected-recipients": "{ count, plural, =1 {1 mottaker} other {# mottakere} } valgt", + "selected-requests": "{ count, plural, =1 {1 forespørsel} other {# forespørsler} } valgt", + "selected-rules": "{ count, plural, =1 {1 regel} other {# regler} } valgt", + "selected-template": "{ count, plural, =1 {1 mal} other {# maler} } valgt", + "send-notification": "Send varsel", + "sent": "Sendt", + "setup": "Oppsett", + "notification-sent": "Varslinger / Sendt", + "set-entity-from-notification": "Angi enhet fra varsel til dashbordtilstand", + "slack-chanel-type": "Slack-kanaltype", + "slack-chanel-types": { + "direct": "Direktemelding", + "private-channel": "Privat kanal", + "public-channel": "Offentlig kanal" + }, + "start-from-scratch": "Start fra bunnen", + "status": "Status", + "stop-escalation-alarm-status-become": "Stopp eskalering når alarmstatus blir:", + "subject": "Emne", + "subject-required": "Emne er påkrevd", + "subject-max-length": "Emnet må være mindre enn eller lik {{ length }} tegn", + "template": "Mal", + "template-name": "Malnavn", + "template-required": "Mal er påkrevd", + "template-type": { + "alarm": "Alarm", + "alarm-assignment": "Alarmtilordning", + "alarm-comment": "Alarmkommentar", + "api-usage-limit": "API-bruksgrense", + "device-activity": "Enhetsaktivitet", + "entities-limit": "Enhetsgrense", + "entity-action": "Enhetshandling", + "general": "Generelt", + "rule-engine-lifecycle-event": "Regelmotor livssyklushendelse", + "rule-node": "Regelnode", + "new-platform-version": "Ny plattformversjon", + "rate-limits": "Overskredet grense", + "edge-communication-failure": "Edge kommunikasjonsfeil", + "edge-connection": "Edge-tilkobling", + "task-processing-failure": "Feil ved oppgavebehandling" + }, + "templates": "Maler", + "notification-templates": "Varslinger / Maler", + "tenant-profiles-list-rule-hint": "Hvis feltet er tomt, vil utløseren gjelde for alle leietakerprofiler", + "tenants-list-rule-hint": "Hvis feltet er tomt, vil utløseren gjelde for alle leietakere", + "threshold": "Terskel", + "theme-color": "Temafarge", + "time": "Tid", + "track-rule-node-events": "Spor regelnodehendelser", + "trigger": { + "alarm": "Alarm", + "alarm-assignment": "Alarmtilordning", + "alarm-comment": "Alarmkommentar", + "api-usage-limit": "API-bruksgrense", + "device-activity": "Enhetsaktivitet", + "entities-limit": "Enhetsgrense", + "entity-action": "Enhetshandling", + "rule-engine-lifecycle-event": "Regelmotor livssyklus-hendelse", + "new-platform-version": "Ny plattformversjon", + "rate-limits": "Overskredet grense", + "edge-connection": "Edge-tilkobling", + "edge-communication-failure": "Edge kommunikasjonsfeil", + "task-processing-failure": "Feil ved oppgavebehandling", + "trigger": "Utløser", + "trigger-required": "Utløser er påkrevd" + }, + "type": "Type", + "unread": "Ulest", + "updated": "Oppdatert", + "use-deprecated-webhook-connectors": "Bruk utdaterte Webhook-koblinger", + "use-old-api": "Bruk gammel API", + "use-template": "Bruk mal", + "view-all": "Vis alle", + "warning": "Advarsel", + "webhook-url": "Webhook-URL", + "webhook-url-required": "Webhook-URL er påkrevd", + "workflow-url": "Arbeidsflyt-URL", + "workflow-url-required": "Arbeidsflyt-URL er påkrevd", + "channel-name": "Kanalnavn", + "channel-name-required": "Kanalnavn er påkrevd", + "settings": { + "notification-settings": "Varslingsinnstillinger", + "reset-all": "Tilbakestill alle innstillinger", + "reset-all-title": "Er du sikker på at du vil tilbakestille skjemaet?", + "reset-all-text": "Etter bekreftelse vil innstillingsskjemaet tilbakestilles til standardverdien og lagres.", + "type": "Type", + "enable-all": "Aktiver alle", + "disable-all": "Deaktiver alle", + "delivery-not-configured": "Leveringsmetode er ikke konfigurert" + } + }, + "ota-update": { + "add": "Legg til pakke", + "assign-firmware": "Tildelt fastvare", + "assign-firmware-required": "Tildelt fastvare er påkrevd", + "assign-software": "Tildelt programvare", + "assign-software-required": "Tildelt programvare er påkrevd", + "auto-generate-checksum": "Generer sjekksum automatisk", + "checksum": "Sjekksum", + "checksum-hint": "Hvis sjekksummen er tom, genereres den automatisk", + "checksum-algorithm": "Sjekksumalgoritme", + "checksum-copied-message": "Pakkesjekksummen er kopiert til utklippstavlen", + "change-firmware": "Endring av fastvaren kan føre til oppdatering av { count, plural, =1 {1 enhet} other {# enheter} }.", + "change-software": "Endring av programvaren kan føre til oppdatering av { count, plural, =1 {1 enhet} other {# enheter} }.", + "chose-compatible-device-profile": "Den opplastede pakken vil bare være tilgjengelig for enheter med valgt profil.", + "chose-firmware-distributed-device": "Velg fastvare som skal distribueres til enhetene", + "chose-software-distributed-device": "Velg programvare som skal distribueres til enhetene", + "content-type": "Innholdstype", + "copy-checksum": "Kopier sjekksum", + "copy-direct-url": "Kopier direkte URL", + "copyId": "Kopier pakke-ID", + "copied": "Kopiert!", + "delete": "Slett pakke", + "delete-ota-update-text": "Vær forsiktig, etter bekreftelse vil OTA-oppdateringen ikke kunne gjenopprettes.", + "delete-ota-update-title": "Er du sikker på at du vil slette OTA-oppdateringen '{{title}}'?", + "delete-ota-updates-text": "Vær forsiktig, etter bekreftelse vil alle valgte OTA-oppdateringer bli fjernet.", + "delete-ota-updates-title": "Er du sikker på at du vil slette { count, plural, =1 {1 OTA-oppdatering} other {# OTA-oppdateringer} }?", + "description": "Beskrivelse", + "direct-url": "Direkte URL", + "direct-url-copied-message": "Pakkens direkte URL er kopiert til utklippstavlen", + "direct-url-required": "Direkte URL er påkrevd", + "download": "Last ned pakke", + "drop-file": "Slipp en pakkefil eller klikk for å velge en fil for opplasting.", + "drop-package-file-or": "Dra og slipp en pakkefil eller", + "file-name": "Filnavn", + "file-size": "Filstørrelse", + "file-size-bytes": "Filstørrelse i bytes", + "idCopiedMessage": "Pakke-ID er kopiert til utklippstavlen", + "no-firmware-matching": "Ingen kompatible fastvare-OTA-oppdateringspakker som matcher '{{entity}}' ble funnet.", + "no-firmware-text": "Ingen kompatible fastvare-OTA-oppdateringspakker tilgjengelig.", + "no-packages-text": "Ingen pakker funnet", + "no-software-matching": "Ingen kompatible programvare-OTA-oppdateringspakker som matcher '{{entity}}' ble funnet.", + "no-software-text": "Ingen kompatible programvare-OTA-oppdateringspakker tilgjengelig.", + "ota-update": "OTA-oppdatering", + "ota-update-details": "Detaljer om OTA-oppdatering", + "ota-updates": "OTA-oppdateringer", + "package-file": "Pakkefil", + "package-type": "Pakketype", + "packages-repository": "Pakkelager", + "search": "Søk i pakker", + "selected-package": "{ count, plural, =1 {1 pakke} other {# pakker} } valgt", + "title": "Tittel", + "title-required": "Tittel er påkrevd.", + "title-max-length": "Tittelen må være kortere enn 256 tegn", + "types": { + "firmware": "Fastvare", + "software": "Programvare" + }, + "upload-binary-file": "Last opp binærfil", + "use-external-url": "Bruk ekstern URL", + "version": "Versjon", + "version-required": "Versjon er påkrevd.", + "version-tag": "Versjonsmerke", + "version-tag-hint": "Egendefinert merke må samsvare med versjonen rapportert av enheten din.", + "version-max-length": "Versjonen må være kortere enn 256 tegn", + "warning-after-save-no-edit": "Når pakken er lastet opp, vil du ikke kunne endre tittel, versjon, enhetsprofil eller pakketype." + }, + "position": { + "top": "Topp", + "bottom": "Bunn", + "left": "Venstre", + "right": "Høyre" + }, + "profile": { + "profile": "Profil", + "last-login-time": "Siste innlogging", + "change-password": "Bytt passord", + "current-password": "Nåværende passord", + "copy-jwt-token": "Kopier JWT-token", + "jwt-token": "JWT-token", + "token-valid-till": "Tokenet er gyldig til", + "tokenCopiedSuccessMessage": "JWT-token er kopiert til utklippstavlen", + "tokenCopiedWarnMessage": "JWT-tokenet er utløpt! Vennligst oppdater siden." + }, + "profiles": { + "profiles": "Profiler" + }, + "security": { + "security": "Sikkerhet", + "general-settings": "Generelle sikkerhetsinnstillinger", + "access-token": "Tilgangstoken", + "access-token-required": "Tilgangstoken er påkrevd", + "clientId": "Klient-ID", + "clientId-required": "Klient-ID er påkrevd", + "username": "Brukernavn", + "username-required": "Brukernavn er påkrevd", + "ca-cert": "CA-sertifikat", + "2fa": { + "2fa": "Tofaktorautentisering", + "2fa-description": "Tofaktorautentisering beskytter kontoen din mot uautorisert tilgang. Alt du trenger å gjøre er å skrive inn en sikkerhetskode når du logger inn.", + "authenticate-with": "Du kan autentisere med:", + "disable-2fa-provider-text": "Deaktivering av {{name}} vil gjøre kontoen din mindre sikker", + "disable-2fa-provider-title": "Er du sikker på at du vil deaktivere {{name}}?", + "get-new-code": "Få ny kode", + "main-2fa-method": "Bruk som hovedmetode for tofaktorautentisering", + "dialog": { + "activation-step-description-email": "Neste gang du logger inn, vil du bli bedt om å oppgi sikkerhetskoden som sendes til e-postadressen din.", + "activation-step-description-sms": "Neste gang du logger inn, vil du bli bedt om å oppgi sikkerhetskoden som sendes til telefonnummeret.", + "activation-step-description-totp": "Neste gang du logger inn, må du oppgi en tofaktorautentiseringskode.", + "activation-step-label": "Aktivering", + "backup-code-description": "Skriv ut kodene slik at du har dem tilgjengelig når du trenger dem for å logge inn. Du kan bruke hver kode én gang.", + "backup-code-warn": "Når du forlater denne siden, kan disse kodene ikke vises igjen. Lagre dem sikkert ved hjelp av alternativene nedenfor.", + "download-txt": "Last ned (txt)", + "email-step-description": "Skriv inn en e-postadresse som skal brukes som autentisator.", + "email-step-label": "E-post", + "enable-email-title": "Aktiver e-postautentisator", + "enable-sms-title": "Aktiver SMS-autentisator", + "enable-totp-title": "Aktiver autentiseringsapp", + "enter-verification-code": "Skriv inn den 6-sifrede koden her", + "get-backup-code-title": "Få reservekode", + "next": "Neste", + "scan-qr-code": "Skann denne QR-koden med autentiseringsappen din", + "send-code": "Send kode", + "sms-step-description": "Skriv inn et telefonnummer som skal brukes som autentisator.", + "sms-step-label": "Telefonnummer", + "success": "Suksess!", + "totp-step-description-install": "Du kan installere apper som Google Authenticator, Authy eller Duo.", + "totp-step-description-open": "Åpne autentiseringsappen på mobiltelefonen din.", + "totp-step-label": "Hent app", + "verification-code": "6-sifret kode", + "verification-code-invalid": "Ugyldig format for verifiseringskode", + "verification-code-incorrect": "Verifiseringskoden er feil", + "verification-code-many-request": "For mange forespørsler, sjekk verifiseringskode", + "verification-step-description": "Skriv inn den 6-sifrede koden vi nettopp sendte til {{address}}", + "verification-step-label": "Verifisering" + }, + "provider": { + "email": "E-post", + "email-description": "Bruk en sikkerhetskode sendt til e-postadressen din for å autentisere.", + "email-hint": "Autentiseringskoder sendes via e-post til {{ info }}", + "sms": "SMS", + "sms-description": "Bruk telefonen din for å autentisere. Vi sender deg en sikkerhetskode via SMS når du logger inn.", + "sms-hint": "Autentiseringskoder sendes via tekstmelding til {{ info }}", + "totp": "Autentiseringsapp", + "totp-description": "Bruk apper som Google Authenticator, Authy eller Duo på telefonen din for å autentisere. Den vil generere en sikkerhetskode for innlogging.", + "totp-hint": "Autentiseringsapp er satt opp for kontoen din", + "backup_code": "Reservekode", + "backup-code-description": "Disse utskrivbare engangskodene lar deg logge inn når du ikke har tilgang til telefonen din, for eksempel når du reiser.", + "backup-code-hint": "{{ info }} engangskoder er aktive for øyeblikket" + } + }, + "password-requirement": { + "at-least": "Minst:", + "character": "{ count, plural, =1 {1 tegn} other {# tegn} }", + "digit": "{ count, plural, =1 {1 siffer} other {# sifre} }", + "incorrect-password-try-again": "Feil passord. Prøv igjen", + "lowercase-letter": "{ count, plural, =1 {1 liten bokstav} other {# små bokstaver} }", + "new-passwords-not-match": "Nytt passord samsvarer ikke", + "password-should-not-contain-spaces": "Passordet ditt bør ikke inneholde mellomrom", + "password-not-meet-requirements": "Passordet oppfyller ikke kravene", + "password-requirements": "Passordkrav", + "password-should-difference": "Nytt passord bør være forskjellig fra nåværende", + "special-character": "{ count, plural, =1 {1 spesialtegn} other {# spesialtegn} }", + "uppercase-letter": "{ count, plural, =1 {1 stor bokstav} other {# store bokstaver} }", + "at-most": "Maksimalt:" + } + }, + "relation": { + "relations": "Relasjoner", + "direction": "Retning", + "clear-relation-type": "Fjern relasjonstype", + "search-direction": { + "FROM": "Fra", + "TO": "Til" + }, + "direction-type": { + "FROM": "fra", + "TO": "til" + }, + "from-relations": "Utgående relasjoner", + "to-relations": "Innkommende relasjoner", + "selected-relations": "{ count, plural, =1 {1 relasjon} other {# relasjoner} } valgt", + "type": "Type", + "to-entity-type": "Til enhetstype", + "to-entity-name": "Til enhetsnavn", + "from-entity-type": "Fra enhetstype", + "from-entity-name": "Fra enhetsnavn", + "to-entity": "Til enhet", + "from-entity": "Fra enhet", + "delete": "Slett relasjon", + "relation-type": "Relasjonstype", + "relation-type-required": "Relasjonstype er påkrevd.", + "relation-type-max-length": "Relasjonstype skal være mindre enn 256 tegn", + "any-relation-type": "Alle typer", + "add": "Legg til relasjon", + "edit": "Rediger relasjon", + "delete-to-relation-title": "Er du sikker på at du vil slette relasjon til enheten '{{entityName}}'?", + "delete-to-relation-text": "Vær oppmerksom, etter bekreftelse vil enheten '{{entityName}}' ikke lenger være relatert til den nåværende enheten.", + "delete-to-relations-title": "Er du sikker på at du vil slette { count, plural, =1 {1 relasjon} other {# relasjoner} }?", + "delete-to-relations-text": "Vær oppmerksom, etter bekreftelse vil alle valgte relasjoner bli fjernet og tilknyttede enheter vil ikke lenger være relatert til den nåværende enheten.", + "delete-from-relation-title": "Er du sikker på at du vil slette relasjon fra enheten '{{entityName}}'?", + "delete-from-relation-text": "Vær oppmerksom, etter bekreftelse vil nåværende enhet ikke lenger være relatert til enheten '{{entityName}}'.", + "delete-from-relations-title": "Er du sikker på at du vil slette { count, plural, =1 {1 relasjon} other {# relasjoner} }?", + "delete-from-relations-text": "Vær oppmerksom, etter bekreftelse vil alle valgte relasjoner bli fjernet og nåværende enhet vil ikke lenger være relatert til tilknyttede enheter.", + "remove-relation-filter": "Fjern relasjonsfilter", + "remove-filter": "Fjern filter", + "add-relation-filter": "Legg til relasjonsfilter", + "any-relation": "Enhver relasjon", + "relation-filters": "Relasjonsfiltre", + "additional-info": "Tilleggsinfo (JSON)", + "invalid-additional-info": "Kan ikke tolke tilleggsinfo som gyldig JSON.", + "no-relations-text": "Ingen relasjoner funnet", + "not": "Ikke" + }, + "resource": { + "add": "Legg til ressurs", + "all-types": "Alle", + "copyId": "Kopier ressurs-ID", + "delete": "Slett ressurs", + "delete-resource-text": "Vær oppmerksom, etter bekreftelse vil ressursen ikke kunne gjenopprettes.", + "delete-resource-title": "Er du sikker på at du vil slette ressursen '{{resourceTitle}}'?", + "delete-resources-action-title": "Slett { count, plural, =1 {1 ressurs} other {# ressurser} }", + "delete-resources-text": "Merk at de valgte ressursene, selv om de brukes i enhetsprofiler, vil bli slettet.", + "delete-resources-title": "Er du sikker på at du vil slette { count, plural, =1 {1 ressurs} other {# ressurser} }?", + "download": "Last ned ressurs", + "drop-file": "Slipp en ressursfil eller klikk for å velge en fil som skal lastes opp.", + "drop-resource-file-or": "Dra og slipp en ressursfil eller", + "empty": "Ressurs er tom", + "file-name": "Filnavn", + "idCopiedMessage": "Ressurs-ID har blitt kopiert til utklippstavlen", + "no-resource-matching": "Ingen ressurser som samsvarer med '{{widgetsBundle}}' ble funnet.", + "no-resource-text": "Ingen ressurser funnet", + "open-widgets-bundle": "Åpne widget-pakke", + "resource": "Ressurs", + "resource-file": "Ressursfil", + "resource-files": "Ressursfiler", + "resource-library-details": "Ressursdetaljer", + "resource-type": "Resursstype", + "resources-library": "Ressursbibliotek", + "search": "Søk etter ressurser", + "selected-resources": "{ count, plural, =1 {1 ressurs} other {# ressurser} } valgt", + "system": "System", + "title": "Tittel", + "title-required": "Tittel er påkrevd.", + "title-max-length": "Tittel skal være mindre enn 256 tegn", + "type": { + "jks": "JKS", + "js-module": "JS-modul", + "lwm2m-model": "LWM2M-modell", + "pkcs-12": "PKCS #12" + }, + "resource-sub-type": "Undertype", + "sub-type": { + "image": "bilde", + "scada-symbol": "Scada-symbol", + "extension": "Utvidelse", + "module": "Modul" + } + }, + "javascript": { + "add": "Legg til JavaScript-ressurs", + "delete": "Slett JavaScript-ressurs", + "delete-javascript-resource-text": "Vær oppmerksom, etter bekreftelse vil JavaScript-ressursen ikke kunne gjenopprettes.", + "delete-javascript-resource-title": "Er du sikker på at du vil slette JavaScript-ressursen '{{resourceTitle}}'?", + "delete-javascript-resources-action-title": "Slett JavaScript { count, plural, =1 {1 ressurs} other {# ressurser} }", + "delete-javascript-resources-text": "Merk at valgte JavaScript-ressurser, selv om de brukes i JavaScript-funksjoner, vil bli slettet.", + "delete-javascript-resources-title": "Er du sikker på at du vil slette JavaScript { count, plural, =1 {1 ressurs} other {# ressurser} }?", + "delete-javascript-resource-in-use-text": "Hvis du fortsatt vil slette JavaScript-ressursen, klikk på Slett uansett-knappen.", + "download": "Last ned JavaScript-ressurs", + "upload-from-file": "Last opp JavaScript fra fil", + "resource-file": "JavaScript-ressursfil", + "drop-file": "Slipp en JavaScript-fil eller klikk for å velge en fil for opplasting.", + "drop-resource-file-or": "Dra og slipp en JavaScript-fil eller", + "javascript-library": "JavaScript-bibliotek", + "javascript-type": "JavaScript-type", + "javascript-resource-details": "JavaScript-ressursdetaljer", + "javascript-resource-is-in-use": "JavaScript-ressurs brukes av andre enheter", + "javascript-resources-are-in-use": "JavaScript-ressurser brukes av andre enheter", + "javascript-resource-is-in-use-text": "JavaScript-ressursen '{{title}}' ble ikke slettet fordi den brukes av følgende enheter:", + "javascript-resources-are-in-use-text": "Ikke alle JavaScript-ressurser ble slettet fordi de brukes av andre enheter.
Du kan vise refererte enheter ved å klikke på Referanser-knappen i den aktuelle ressursraden.
Hvis du fortsatt vil slette disse JavaScript-ressursene, velg dem i tabellen nedenfor og klikk Slett valgte-knappen.", + "search": "Søk JavaScript-ressurser", + "selected-javascript-resources": "{ count, plural, =1 {1 JavaScript-ressurs} other {# JavaScript-ressurser} } valgt", + "no-javascript-resource-text": "Ingen JavaScript-ressurser funnet", + "all-types": "Alle", + "module-script": "Modulskript" + }, + "rpc": { + "error": { + "target-device-is-not-set": "Målenheten er ikke satt!", + "invalid-target-entity": "RPC-kommandoer støttes ikke av {{entityType}}-enheten.", + "failed-to-resolve-target-device": "Klarte ikke å finne målenheten!", + "request-timeout": "Forespørselen utløp", + "rpc-http-error": "Feil: {{status}} - {{statusText}}" + } + }, + "rulechain": { + "rulechain": "Regelkjede", + "rulechain-events": "Regelkjedebegivenheter", + "rulechains": "Regelkjeder", + "root": "Rot", + "delete": "Slett regelkjede", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "name-max-length": "Navn skal være mindre enn 256 tegn", + "description": "Beskrivelse", + "add": "Legg til regelkjede", + "set-root": "Gjør regelkjede til rot", + "set-root-rulechain-title": "Er du sikker på at du vil gjøre regelkjeden '{{ruleChainName}}' til rot?", + "set-root-rulechain-text": "Etter bekreftelse vil regelkjeden bli rot og håndtere alle innkommende transportmeldinger.", + "delete-rulechain-title": "Er du sikker på at du vil slette regelkjeden '{{ruleChainName}}'?", + "delete-rulechain-text": "Vær oppmerksom, etter bekreftelse vil regelkjeden og alle relaterte data ikke kunne gjenopprettes.", + "delete-rulechains-title": "Er du sikker på at du vil slette { count, plural, =1 {1 regelkjede} other {# regelkjeder} }?", + "delete-rulechains-action-title": "Slett { count, plural, =1 {1 regelkjede} other {# regelkjeder} }", + "delete-rulechains-text": "Vær oppmerksom, etter bekreftelse vil alle valgte regelkjeder bli fjernet og alle relaterte data ikke kunne gjenopprettes.", + "add-rulechain-text": "Legg til ny regelkjede", + "no-rulechains-text": "Ingen regelkjeder funnet", + "rulechain-details": "Regelkjededetaljer", + "details": "Detaljer", + "events": "Hendelser", + "system": "System", + "import": "Importer regelkjede", + "export": "Eksporter regelkjede", + "export-failed-error": "Kan ikke eksportere regelkjede: {{error}}", + "create-new-rulechain": "Opprett ny regelkjede", + "rulechain-file": "Regelkjedefil", + "invalid-rulechain-file-error": "Kan ikke importere regelkjede: Ugyldig datastruktur.", + "copyId": "Kopier regelkjede-ID", + "idCopiedMessage": "Regelkjede-ID er kopiert til utklippstavlen", + "select-rulechain": "Velg regelkjede", + "no-rulechains-matching": "Ingen regelkjeder som samsvarer med '{{entity}}' ble funnet.", + "rulechain-required": "Regelkjede er påkrevd", + "management": "Regelhåndtering", + "debug-mode": "Feilsøkingsmodus", + "search": "Søk regelkjeder", + "selected-rulechains": "{ count, plural, =1 {1 regelkjede} other {# regelkjeder} } valgt", + "open-rulechain": "Åpne regelkjede", + "edge-template-root": "Malrot", + "assign-to-edge": "Tildel til edge", + "edge-rulechain": "Edge regelkjede", + "unassign-rulechain-from-edge-text": "Etter bekreftelse vil regelkjeden bli fjernet og ikke lenger være tilgjengelig for edge.", + "unassign-rulechains-from-edge-title": "Er du sikker på at du vil fjerne tildelingen av { count, plural, =1 {1 regelkjede} other {# regelkjeder} }?", + "unassign-rulechains-from-edge-text": "Etter bekreftelse vil alle valgte regelkjeder bli fjernet og ikke lenger være tilgjengelige for edge.", + "assign-rulechain-to-edge-title": "Tildel regelkjede(r) til edge", + "assign-rulechain-to-edge-text": "Vennligst velg regelkjeder som skal tildeles edge", + "set-edge-template-root-rulechain": "Gjør regelkjede til edge-malrot", + "set-edge-template-root-rulechain-title": "Er du sikker på at du vil gjøre regelkjeden '{{ruleChainName}}' til edge-malrot?", + "set-edge-template-root-rulechain-text": "Etter bekreftelse vil regelkjeden bli edge-malrot og være rot-regelkjede for nylig opprettede edges.", + "invalid-rulechain-type-error": "Kan ikke importere regelkjede: Ugyldig regelkjedetype. Forventet type er {{expectedRuleChainType}}.", + "set-auto-assign-to-edge": "Tildel regelkjede til edge(r) ved opprettelse", + "set-auto-assign-to-edge-title": "Er du sikker på at du vil tildele edge-regelkjeden '{{ruleChainName}}' til edge(r) ved opprettelse?", + "set-auto-assign-to-edge-text": "Etter bekreftelse vil edge-regelkjeden automatisk bli tildelt edge(r) ved opprettelse.", + "unset-auto-assign-to-edge": "Ikke tildel regelkjede til edge(r) ved opprettelse", + "unset-auto-assign-to-edge-title": "Er du sikker på at du ikke vil tildele edge-regelkjeden '{{ruleChainName}}' til edge(r) ved opprettelse?", + "unset-auto-assign-to-edge-text": "Etter bekreftelse vil edge-regelkjeden ikke lenger bli automatisk tildelt edge(r) ved opprettelse.", + "unassign-rulechain-title": "Er du sikker på at du vil fjerne tildelingen av regelkjeden '{{ruleChainName}}'?", + "unassign-rulechains": "Fjern tildeling av regelkjeder" + }, + "rulenode": { + "rule-node-events": "Regelnodehendelser", + "details": "Detaljer", + "events": "Hendelser", + "search": "Søk noder", + "open-node-library": "Åpne nodebibliotek", + "close-node-library": "Lukk nodebibliotek", + "add": "Legg til regelnode", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "name-max-length": "Navn skal være mindre enn 256 tegn", + "type": "Type", + "rule-node-description": "Regelnodebeskrivelse", + "delete": "Slett regelnode", + "select-all-objects": "Velg alle noder og forbindelser", + "deselect-all-objects": "Opphev valg av alle noder og forbindelser", + "delete-selected-objects": "Slett valgte noder og forbindelser", + "delete-selected": "Slett valgte", + "create-nested-rulechain": "Opprett nestet regelkjede", + "select-all": "Velg alle", + "copy-selected": "Kopier valgte", + "deselect-all": "Opphev alle valg", + "rulenode-details": "Regelnodedetaljer", + "debug-mode": "Feilsøkingsmodus", + "singleton": "Singleton", + "configuration": "Konfigurasjon", + "link": "Lenke", + "link-details": "Detaljer for regelnodelenke", + "add-link": "Legg til lenke", + "link-label": "Lenketikett", + "link-label-required": "Lenketikett er påkrevd.", + "custom-link-label": "Egendefinert lenketikett", + "custom-link-label-required": "Egendefinert lenketikett er påkrevd.", + "link-labels": "Lenketiketter", + "link-labels-required": "Lenketiketter er påkrevd.", + "no-link-labels-found": "Ingen lenketiketter funnet", + "no-link-label-matching": "'{{label}}' ble ikke funnet.", + "create-new-link-label": "Opprett en ny!", + "type-filter": "Filter", + "type-filter-details": "Filtrer innkommende meldinger med konfigurerte betingelser", + "type-enrichment": "Berikelse", + "type-enrichment-details": "Legg til tilleggsinformasjon i meldingsmetadata", + "type-transformation": "Transformasjon", + "type-transformation-details": "Endre meldingsinnhold og metadata", + "type-action": "Handling", + "type-action-details": "Utfør spesialhandling", + "type-external": "Ekstern", + "type-external-details": "Samhandler med eksternt system", + "type-rule-chain": "Regelkjede", + "type-rule-chain-details": "Sender innkommende meldinger til spesifisert regelkjede", + "type-flow": "Flyt", + "type-flow-details": "Organiserer meldingsflyt", + "type-input": "Inndata", + "type-input-details": "Logisk inndata for regelkjede, sender meldinger videre til neste relaterte regelnode", + "type-unknown": "Ukjent", + "type-unknown-details": "Uløst regelnode", + "directive-is-not-loaded": "Definert konfigurasjonsdirektiv '{{directiveName}}' er ikke tilgjengelig.", + "ui-resources-load-error": "Kunne ikke laste konfigurasjonsressurser for brukergrensesnitt.", + "invalid-target-rulechain": "Kan ikke løse målet regelkjede!", + "test-script-function": "Test skriptfunksjon", + "script-lang-java-script": "JavaScript", + "script-lang-tbel": "TBEL", + "message": "Melding", + "message-type": "Meldingstype", + "select-message-type": "Velg meldingstype", + "message-type-required": "Meldingstype er påkrevd", + "metadata": "Metadata", + "metadata-required": "Metadataoppføringer kan ikke være tomme.", + "output": "Utdata", + "test": "Test", + "help": "Hjelp", + "reset-debug-settings": "Tilbakestill feilsøkingsinnstillinger i alle noder", + "test-with-this-message": "{{test}} med denne meldingen", + "queue-hint": "Velg en kø for å sende meldingen til en annen kø. 'Hoved'-kø brukes som standard.", + "queue-singleton-hint": "Velg en kø for å sende meldingen i flerinstansmiljøer. 'Hoved'-kø brukes som standard." + }, + "rule-node-config": { + "id": "Id", + "additional-info": "Tilleggsinformasjon", + "advanced-settings": "Avanserte innstillinger", + "create-entity-if-not-exists": "Opprett ny enhet hvis den ikke finnes", + "create-entity-if-not-exists-hint": "Hvis aktivert, vil en ny enhet med spesifiserte parametere bli opprettet med mindre den allerede finnes. Eksisterende enheter vil bli brukt som de er for relasjoner.", + "select-device-connectivity-event": "Velg enhetstilkoblingshendelse", + "entity-name-pattern": "Navnemønster", + "device-name-pattern": "Enhetsnavn", + "asset-name-pattern": "Eiendelsnavn", + "entity-view-name-pattern": "Entity view-navn", + "customer-title-pattern": "Kundens tittel", + "dashboard-name-pattern": "Dashboardtittel", + "user-name-pattern": "Brukerens e-post", + "edge-name-pattern": "Edge-navn", + "entity-name-pattern-required": "Navnemønster er påkrevd", + "entity-name-pattern-hint": "Navnemønsterfelt støtter templatization. Bruk $[messageKey] for å hente verdi fra meldingen og ${metadataKey} for å hente verdi fra metadata.", + "copy-message-type": "Kopier meldingstype", + "entity-type-pattern": "Typemønster", + "entity-type-pattern-required": "Typemønster er påkrevd", + "message-type-value": "Meldingstypeverdi", + "message-type-value-required": "Meldingstypeverdi er påkrevd", + "message-type-value-max-length": "Meldingstypeverdi skal være mindre enn 256", + "output-message-type": "Utgangsmeldingstype", + "entity-cache-expiration": "Utløpstid for enhetsbuffer (sek)", + "entity-cache-expiration-hint": "Angir maksimal tidsperiode for lagring av funnede enhetsoppføringer. 0 betyr at oppføringer aldri utløper.", + "entity-cache-expiration-required": "Utløpstid for enhetsbuffer er påkrevd.", + "entity-cache-expiration-range": "Utløpstid for enhetsbuffer må være større enn eller lik 0.", + "customer-name-pattern": "Kundens tittel", + "customer-name-pattern-required": "Kundens tittel er påkrevd", + "customer-name-pattern-hint": "Bruk $[messageKey] for å hente verdi fra meldingen og ${metadataKey} for å hente verdi fra metadata.", + "create-customer-if-not-exists": "Opprett ny kunde hvis den ikke finnes", + "unassign-from-customer": "Fjern tildeling fra spesifikk kunde hvis opphav er dashboard", + "unassign-from-customer-tooltip": "Bare dashboards kan tildeles flere kunder samtidig.\nHvis meldingens opphav er et dashboard, må du eksplisitt spesifisere kundens tittel for å fjerne tildeling.", + "customer-cache-expiration": "Utløpstid for kundebuffer (sek)", + "customer-cache-expiration-hint": "Angir maksimal tidsperiode for lagring av funnede kundeoppføringer. 0 betyr at oppføringer aldri utløper.", + "customer-cache-expiration-required": "Utløpstid for kundebuffer er påkrevd.", + "customer-cache-expiration-range": "Utløpstid for kundebuffer må være større enn eller lik 0.", + "interval-start": "Start på intervall", + "interval-end": "Slutt på intervall", + "time-unit": "Tidsenhet", + "fetch-mode": "Hentemodus", + "order-by-timestamp": "Sorter etter tidsstempel", + "limit": "Begrensning", + "limit-hint": "Minste verdi er 2, maks - 1000. For å hente én oppføring, velg hentemodus 'Første' eller 'Siste'.", + "limit-required": "Begrensning er påkrevd.", + "limit-range": "Begrensning må være mellom 2 og 1000.", + "time-unit-milliseconds": "Millisekunder", + "time-unit-seconds": "Sekunder", + "time-unit-minutes": "Minutter", + "time-unit-hours": "Timer", + "time-unit-days": "Dager", + "time-value-range": "Tillatt område fra 1 til 2147483647.", + "start-interval-value-required": "Start på intervall er påkrevd.", + "end-interval-value-required": "Slutt på intervall er påkrevd.", + "filter": "Filter", + "switch": "Bryter", + "math-templatization-tooltip": "Dette feltet støtter templatization. Bruk $[messageKey] for å hente verdi fra meldingen og ${metadataKey} for metadata.", + "add-message-type": "Legg til meldingstype", + "select-message-types-required": "Minst én meldingstype må være valgt.", + "select-message-types": "Velg meldingstyper", + "no-message-types-found": "Ingen meldingstyper funnet", + "no-message-type-matching": "'{{messageType}}' ble ikke funnet.", + "create-new-message-type": "Opprett en ny.", + "message-types-required": "Meldingstyper er påkrevd.", + "client-attributes": "Klientattributter", + "shared-attributes": "Delte attributter", + "server-attributes": "Serverattributter", + "attributes-keys": "Attributtnøkler", + "attributes-keys-required": "Attributtnøkler er påkrevd", + "attributes-scope": "Attributtområde", + "attributes-scope-value": "Verdi for attributtområde", + "attributes-scope-value-copy": "Kopier verdi for attributtområde", + "attributes-scope-hint": "Bruk metadata-nøkkelen 'scope' for å sette attributtområdet dynamisk per melding. Dette overstyrer konfigurasjonsverdi hvis satt.", + "notify-device": "Tving varsling til enhet", + "send-attributes-updated-notification": "Send varsel om oppdaterte attributter", + "send-attributes-updated-notification-hint": "Send varsling om oppdaterte attributter som en egen melding til regelmotorens kø.", + "send-attributes-deleted-notification": "Send varsel om slettede attributter", + "send-attributes-deleted-notification-hint": "Send varsling om slettede attributter som en egen melding til regelmotorens kø.", + "update-attributes-only-on-value-change": "Lagre attributter kun ved endring", + "update-attributes-only-on-value-change-hint": "Oppdaterer attributtene ved hver innkommende melding uavhengig av verdiendring. Øker API-bruk og reduserer ytelse.", + "update-attributes-only-on-value-change-hint-enabled": "Oppdaterer attributtene kun hvis deres verdi har endret seg. Hvis verdien ikke er endret, sendes ingen tidsstempeloppdatering eller endringsvarsel.", + "fetch-credentials-to-metadata": "Hent legitimasjon til metadata", + "notify-device-on-update-hint": "Hvis aktivert, tving varsling til enheten om oppdatering av delte attributter. Hvis deaktivert, styres varslingsatferden av parameteren 'notifyDevice' fra metadataen i den innkommende meldingen. For å deaktivere varslingen må meldingsmetadata inneholde parameteren 'notifyDevice' satt til 'false'. Alle andre tilfeller vil utløse varsling til enheten.", + "notify-device-on-delete-hint": "Hvis aktivert, tving varsling til enheten om fjerning av delte attributter. Hvis deaktivert, styres varslingsatferden av parameteren 'notifyDevice' fra metadataen i den innkommende meldingen. For å aktivere varslingen må meldingsmetadata inneholde parameteren 'notifyDevice' satt til 'true'. I alle andre tilfeller vil varslingen ikke bli utløst.", + "latest-timeseries": "Siste tidsseriedataknøkler", + "timeseries-keys": "Tidsserienøkler", + "timeseries-keys-required": "Minst én tidsserienøkkel må velges.", + "add-timeseries-key": "Legg til tidsserienøkkel", + "add-message-field": "Legg til meldingsfelt", + "relation-search-parameters": "Søkeparametere for relasjoner", + "relation-parameters": "Relasjonsparametere", + "add-metadata-field": "Legg til metadatafelt", + "data-keys": "Meldingsfeltnavn", + "copy-from": "Kopier fra", + "data-to-metadata": "Data til metadata", + "metadata-to-data": "Metadata til data", + "use-regular-expression-hint": "Bruk regulære uttrykk for å kopiere nøkler etter mønster.\n\nTips & triks:\nTrykk 'Enter' for å fullføre inntasting av feltnavn.\nTrykk 'Backspace' for å slette feltnavn. Flere feltnavn støttes.", + "interval": "Intervall", + "interval-required": "Intervall er påkrevd", + "interval-hint": "Dedupliseringsintervall i sekunder.", + "interval-min-error": "Minste tillatte verdi er 1", + "max-pending-msgs": "Maks antall ventende meldinger", + "max-pending-msgs-hint": "Maksimalt antall meldinger som lagres i minnet per unik dedupliserings-ID.", + "max-pending-msgs-required": "Maks antall ventende meldinger er påkrevd", + "max-pending-msgs-max-error": "Maks tillatte verdi er 1000", + "max-pending-msgs-min-error": "Minste tillatte verdi er 1", + "max-retries": "Maks antall forsøk", + "max-retries-required": "Maks antall forsøk er påkrevd", + "max-retries-hint": "Maksimalt antall forsøk på å sende dedupliserte meldinger til køen. Det er 10 sekunders forsinkelse mellom hvert forsøk.", + "max-retries-max-error": "Maks tillatte verdi er 100", + "max-retries-min-error": "Minste tillatte verdi er 0", + "strategy": "Strategi", + "strategy-required": "Strategi er påkrevd", + "strategy-all-hint": "Returner alle meldinger som kom i løpet av dedupliseringsperioden som én enkelt JSON-tabellmelding. Hver oppføring representerer et objekt med indre egenskaper 'msg' og 'metadata'.", + "strategy-first-hint": "Returner første melding som kom i løpet av dedupliseringsperioden.", + "strategy-last-hint": "Returner siste melding som kom i løpet av dedupliseringsperioden.", + "first": "Første", + "last": "Siste", + "all": "Alle", + "output-msg-type-hint": "Meldingstypen for dedupliseringsresultatet.", + "queue-name-hint": "Navnet på køen hvor dedupliseringsresultatet publiseres.", + "keys": "Nøkler", + "keys-required": "Nøkler er påkrevd", + "rename-keys-in": "Gi nytt navn til nøkler i", + "data": "Data", + "message": "Melding", + "metadata": "Metadata", + "current-key-name": "Nåværende nøkkelnavn", + "key-name-required": "Nøkkelnavn er påkrevd", + "new-key-name": "Nytt nøkkelnavn", + "new-key-name-required": "Nytt nøkkelnavn er påkrevd", + "metadata-keys": "Metadatafeltnavn", + "json-path-expression": "JSON path-uttrykk", + "json-path-expression-required": "JSON path-uttrykk er påkrevd", + "json-path-expression-hint": "JSONPath spesifiserer en sti til et element eller en gruppe elementer i en JSON-struktur. '$' representerer rotnivået i objektet eller arrayen.", + "relations-query": "Relasjonsspørring", + "device-relations-query": "Enhetsrelasjonsspørring", + "max-relation-level": "Maks relasjonsnivå", + "max-relation-level-error": "Verdien må være større enn 0 eller ikke spesifisert.", + "max-relation-level-invalid": "Verdien må være et heltall.", + "relation-type": "Relasjonstype", + "relation-type-pattern": "Relasjonstypemønster", + "relation-type-pattern-required": "Relasjonstypemønster er påkrevd", + "relation-types-list": "Relasjonstyper som skal videreføres", + "relation-types-list-hint": "Hvis ingen relasjonstyper for videreføring er valgt, vil alarmer bli videreført uten filtrering på relasjonstype.", + "unlimited-level": "Ubegrenset nivå", + "latest-telemetry": "Siste telemetri", + "add-telemetry-key": "Legg til telemetrinøkkel", + "delete-from": "Slett fra", + "use-regular-expression-delete-hint": "Bruk regulære uttrykk for å slette nøkler etter mønster.\n\nTips & triks:\nTrykk 'Enter' for å fullføre inntasting av feltnavn.\nTrykk 'Backspace' for å slette feltnavn.\nFlere feltnavn støttes.", + "fetch-into": "Hent til", + "attr-mapping": "Attributttilordning:", + "source-attribute": "Kildeattributtnøkkel", + "source-attribute-required": "Kildeattributtnøkkel er påkrevd.", + "source-telemetry": "Kildetelemetrinøkkel", + "source-telemetry-required": "Kildetelemetrinøkkel er påkrevd.", + "target-key": "Målnøkkel", + "target-key-required": "Målnøkkel er påkrevd.", + "attr-mapping-required": "Minst én tilordning må spesifiseres.", + "fields-mapping": "Felttilordning", + "fields-mapping-hint": "Hvis meldingsfeltet er satt til $entityId, vil opprinnelsesenhetens ID bli lagret i den angitte tabellkolonnen.", + "relations-query-config-direction-suffix": "opphav", + "profile-name": "Profilnavn", + "fetch-circle-parameter-info-from-metadata-hint": "Metadatafeltet '{{perimeterKeyName}}' må være definert i følgende format: {"latitude":48.196, "longitude":24.6532, "radius":100.0, "radiusUnit":"METER"}", + "fetch-poligon-parameter-info-from-metadata-hint": "Metadatafeltet '{{perimeterKeyName}}' må være definert i følgende format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]", + "short-templatization-tooltip": "Bruk $[messageKey] for å hente verdi fra meldingen og ${metadataKey} for å hente verdi fra metadata.", + "fields-mapping-required": "Minst én felttilordning må spesifiseres.", + "at-least-one-field-required": "Minst ett inndatafelt må ha angitte verdier.", + "originator-fields-sv-map-hint": "Målnøkkelfeltene støtter templatization. Bruk $[messageKey] for å hente verdi fra meldingen og ${metadataKey} for å hente verdi fra metadata.", + "sv-map-hint": "Kun målnøkkelfeltene støtter templatization. Bruk $[messageKey] for å hente verdi fra meldingen og ${metadataKey} for å hente verdi fra metadata.", + "source-field": "Kildefelt", + "source-field-required": "Kildefelt er påkrevd.", + "originator-source": "Opprinnelseskilde", + "new-originator": "Ny opprinnelsesenhet", + "originator-customer": "Kunde", + "originator-tenant": "Leietaker", + "originator-related": "Relatert enhet", + "originator-alarm-originator": "Alarmens opprinnelse", + "originator-entity": "Enhet etter navnemønster", + "clone-message": "Klon melding", + "transform": "Transformer", + "default-ttl": "Standard TTL", + "default-ttl-required": "Standard TTL er påkrevd.", + "default-ttl-hint": "Regelnode henter TTL (Time-to-Live) verdi fra meldingsmetadata. Hvis verdien ikke er tilstede, brukes den konfigurerte TTL. Hvis verdien er satt til 0, benyttes TTL fra leietakerprofilen.", + "default-ttl-zero-hint": "TTL vil ikke bli brukt hvis verdien er satt til 0.", + "min-default-ttl-message": "Kun minimumsverdi 0 er tillatt for TTL.", + "generation-parameters": "Generasjonsparametere", + "message-count": "Grense for genererte meldinger (0 - ubegrenset)", + "message-count-required": "Grense for genererte meldinger er påkrevd.", + "min-message-count-message": "Kun minimumsverdi 0 er tillatt for antall meldinger.", + "period-seconds": "Periode i sekunder", + "period-seconds-required": "Periode er påkrevd.", + "generation-frequency-seconds": "Genereringsfrekvens i sekunder", + "generation-frequency-required": "Genereringsfrekvens er påkrevd.", + "min-generation-frequency-message": "Minimum 60 sekunder er påkrevd.", + "script-lang-tbel": "TBEL", + "script-lang-js": "JS", + "use-metadata-period-in-seconds-patterns": "Bruk periode i sekunder-mønster", + "use-metadata-period-in-seconds-patterns-hint": "Hvis valgt, vil regelnode bruke periodeintervall i sekunder fra meldingsmetadata eller -data, forutsatt at intervaller er oppgitt i sekunder.", + "period-in-seconds-pattern": "Mønster for periode i sekunder", + "period-in-seconds-pattern-required": "Mønster for periode i sekunder er påkrevd", + "min-period-seconds-message": "Minimum 60 sekunder periode er tillatt.", + "originator": "Opprinnelsesenhet", + "message-body": "Meldingsinnhold", + "message-metadata": "Meldingsmetadata", + "generate": "Generer", + "current-rule-node": "Gjeldende Regelnode", + "current-tenant": "Gjeldende Leietaker", + "generator-function": "Genereringsfunksjon", + "test-generator-function": "Test genereringsfunksjon", + "generator": "Generator", + "test-filter-function": "Test filterfunksjon", + "test-switch-function": "Test byttefunksjon", + "test-transformer-function": "Test transformasjonsfunksjon", + "transformer": "Transformer", + "alarm-create-condition": "Betingelse for å opprette alarm", + "test-condition-function": "Test betingelsesfunksjon", + "alarm-clear-condition": "Betingelse for å fjerne alarm", + "alarm-details-builder": "Alarmdetaljbygger", + "test-details-function": "Test detaljfunksjon", + "alarm-type": "Alarmtype", + "select-entity-types": "Velg enhetstyper", + "alarm-type-required": "Alarmtype er påkrevd.", + "alarm-severity": "Alarmeringsgrad", + "alarm-severity-required": "Alarmeringsgrad er påkrevd", + "alarm-severity-pattern": "Alarmeringsgradsmønster", + "alarm-status-filter": "Alarmstatusfilter", + "alarm-status-list-empty": "Listen over alarmstatuser er tom", + "no-alarm-status-matching": "Ingen matchende alarmstatus funnet.", + "propagate": "Videreformidle alarm til relaterte enheter", + "propagate-to-owner": "Videreformidle alarm til enhetseier (Kunde eller Leietaker)", + "propagate-to-tenant": "Videreformidle alarm til Leietaker", + "condition": "Betingelse", + "details": "Detaljer", + "to-string": "Til tekst", + "test-to-string-function": "Test konvertering til tekst", + "from-template": "Fra", + "from-template-required": "Fra-feltet er påkrevd", + "message-to-metadata": "Melding til metadata", + "metadata-to-message": "Metadata til melding", + "from-message": "Fra melding", + "from-metadata": "Fra metadata", + "to-template": "Til", + "to-template-required": "Til-mal er påkrevd", + "mail-address-list-template-hint": "Kommaseparert adresseliste, bruk ${metadataKey} for verdi fra metadata, $[messageKey] for verdi fra meldingsinnholdet", + "cc-template": "Kopi (Cc)", + "bcc-template": "Blindkopi (Bcc)", + "subject-template": "Emne", + "subject-template-required": "Emnemal er påkrevd", + "body-template": "Brødtekst", + "body-template-required": "Brødtekstmal er påkrevd", + "dynamic-mail-body-type": "Dynamisk e-post brødteksttype", + "mail-body-type": "E-post brødteksttype", + "body-type-template": "Brødteksttype-mal", + "reply-routing-configuration": "Konfigurasjon for svarruting", + "rpc-reply-routing-configuration-hint": "Disse konfigurasjonsparametrene angir metadata-nøkkelnavnene som brukes for å identifisere tjeneste, økt og forespørsel ved sending av svar tilbake.", + "reply-routing-configuration-hint": "Disse konfigurasjonsparametrene angir metadata-nøkkelnavnene som brukes for å identifisere tjeneste og forespørsel ved sending av svar tilbake.", + "request-id-metadata-attribute": "Forespørsels-ID", + "service-id-metadata-attribute": "Tjeneste-ID", + "session-id-metadata-attribute": "Økt-ID", + "timeout-sec": "Tidsavbrudd i sekunder", + "timeout-required": "Tidsavbrudd er påkrevd", + "min-timeout-message": "Kun minimumsverdi 0 er tillatt for tidsavbrudd.", + "endpoint-url-pattern": "Endepunkt URL-mønster", + "endpoint-url-pattern-required": "Endepunkt URL-mønster er påkrevd", + "request-method": "Forespørselsmetode", + "use-simple-client-http-factory": "Bruk enkel HTTP-klientfabrikk", + "ignore-request-body": "Uten forespørselsinnhold", + "parse-to-plain-text": "Parse til ren tekst", + "parse-to-plain-text-hint": "Hvis valgt, vil meldingsinnholdet i forespørselens kropp bli transformert fra JSON-streng til ren tekst, f.eks. msg = \"Hello,\\t\"world\"\" vil bli tolket som Hello, \"world\"", + "read-timeout": "Lesetidsavbrudd i millisekunder", + "read-timeout-hint": "Verdien 0 betyr uendelig tidsavbrudd", + "max-parallel-requests-count": "Maksimalt antall parallelle forespørsler", + "max-parallel-requests-count-hint": "Verdien 0 spesifiserer ingen begrensning i parallell prosessering", + "max-response-size": "Maksimal svarstørrelse (i KB)", + "max-response-size-hint": "Maksimalt minne allokert for å buffre data ved dekoding eller koding av HTTP-meldinger, som f.eks. JSON eller XML-payloads", + "headers": "Overskrifter", + "headers-hint": "Bruk ${metadataKey} for verdi fra metadata, $[messageKey] for verdi fra meldingsinnholdet i header/verdi-feltene", + "header": "Overskrift", + "header-required": "Overskrift er påkrevd", + "value": "Verdi", + "value-required": "Verdi er påkrevd", + "topic-pattern": "Emnemønster", + "key-pattern": "Nøkkelmønster", + "key-pattern-hint": "Valgfritt. Hvis et gyldig partisjonsnummer er spesifisert, vil det bli brukt ved sending av posten. Hvis ingen partisjon er spesifisert, brukes nøkkelen i stedet. Hvis ingen av delene er spesifisert, vil en partisjon bli tilordnet i rundgang.", + "topic-pattern-required": "Emnemønster er påkrevd", + "topic": "Emne", + "topic-required": "Emne er påkrevd", + "bootstrap-servers": "Bootstrap-servere", + "bootstrap-servers-required": "Verdi for bootstrap-servere er påkrevd", + "other-properties": "Andre egenskaper", + "key": "Nøkkel", + "key-required": "Nøkkel er påkrevd", + "retries": "Automatisk antall forsøk ved feil", + "min-retries-message": "Kun minimum 0 forsøk er tillatt.", + "batch-size-bytes": "Batch-størrelse produsert i byte", + "min-batch-size-bytes-message": "Kun minimum batch-størrelse 0 er tillatt.", + "linger-ms": "Bufferlokaltid (ms)", + "min-linger-ms-message": "Kun minimum 0 ms er tillatt.", + "buffer-memory-bytes": "Maks bufferstørrelse for klient i byte", + "min-buffer-memory-message": "Kun minimum 0 bufferstørrelse er tillatt.", + "memory-buffer-size-range": "Minnebufferstørrelse må være mellom 0 og {{max}} KB", + "acks": "Antall bekreftelser", + "topic-arn-pattern": "Emne ARN-mønster", + "topic-arn-pattern-required": "Emne ARN-mønster er påkrevd", + "aws-access-key-id": "AWS Access Key ID", + "aws-access-key-id-required": "AWS Access Key ID er påkrevd", + "aws-secret-access-key": "AWS Secret Access Key", + "aws-secret-access-key-required": "AWS Secret Access Key er påkrevd", + "aws-region": "AWS Region", + "aws-region-required": "AWS Region er påkrevd", + "exchange-name-pattern": "Byttemønster", + "routing-key-pattern": "Ruter-nøkkelmønster", + "message-properties": "Meldingsegenskaper", + "host": "Vert", + "host-required": "Vert er påkrevd", + "port": "Port", + "port-required": "Port er påkrevd", + "port-range": "Port må være i området 1 til 65535.", + "virtual-host": "Virtuell vert", + "username": "Brukernavn", + "password": "Passord", + "automatic-recovery": "Automatisk gjenoppretting", + "connection-timeout-ms": "Tilkobling tidsavbrudd (ms)", + "min-connection-timeout-ms-message": "Kun minimumsverdi 0 ms er tillatt.", + "handshake-timeout-ms": "Håndtrykk tidsavbrudd (ms)", + "min-handshake-timeout-ms-message": "Kun minimumsverdi 0 ms er tillatt.", + "client-properties": "Klientegenskaper", + "queue-url-pattern": "Kø-URL-mønster", + "queue-url-pattern-required": "Kø-URL-mønster er påkrevd", + "delay-seconds": "Forsinkelse (sekunder)", + "min-delay-seconds-message": "Kun minimum 0 sekunder er tillatt.", + "max-delay-seconds-message": "Kun maksimum 900 sekunder er tillatt.", + "name": "Navn", + "name-required": "Navn er påkrevd", + "queue-type": "Køtype", + "sqs-queue-standard": "Standard", + "sqs-queue-fifo": "FIFO", + "gcp-project-id": "GCP prosjekt-ID", + "gcp-project-id-required": "GCP prosjekt-ID er påkrevd", + "gcp-service-account-key": "GCP tjenestekonto nøkkelfil", + "gcp-service-account-key-required": "GCP tjenestekonto nøkkelfil er påkrevd", + "pubsub-topic-name": "Emnenavn", + "pubsub-topic-name-required": "Emnenavn er påkrevd", + "message-attributes": "Meldingsegenskaper", + "message-attributes-hint": "Bruk ${metadataKey} for verdi fra metadata, $[messageKey] for verdi fra meldingsinnholdet i navn/verdi-feltene", + "connect-timeout": "Tilkobling tidsavbrudd (sek)", + "connect-timeout-required": "Tilkobling tidsavbrudd er påkrevd.", + "connect-timeout-range": "Tilkobling tidsavbrudd må være mellom 1 og 200.", + "client-id": "Klient-ID", + "client-id-hint": "Valgfritt. La stå tomt for automatisk generert klient-ID. Vær forsiktig når du angir klient-ID. De fleste MQTT-meglere tillater ikke flere tilkoblinger med samme klient-ID. For å koble til slike meglere må klient-ID-en din være unik. Når plattformen kjører i mikrotjenestemodus, startes en kopi av regelnoden i hver mikrotjeneste. Dette vil automatisk føre til flere MQTT-klienter med samme ID og kan forårsake feil i regelnoden. For å unngå slike feil, aktiver alternativet \"Legg til tjeneste-ID som suffiks til klient-ID\" nedenfor.", + "append-client-id-suffix": "Legg til tjeneste-ID som suffiks til klient-ID", + "client-id-suffix-hint": "Valgfritt. Brukes når \"Klient-ID\" er spesifisert. Hvis valgt, vil tjeneste-ID bli lagt til klient-ID som et suffiks. Hjelper med å unngå feil i mikrotjenestemodus.", + "device-id": "Enhets-ID", + "device-id-required": "Enhets-ID er påkrevd.", + "clean-session": "Ren økt", + "enable-ssl": "Aktiver SSL", + "credentials": "Legitimasjon", + "credentials-type": "Legitimasjonstype", + "credentials-type-required": "Legitimasjonstype er påkrevd.", + "credentials-anonymous": "Anonym", + "credentials-basic": "Grunnleggende", + "credentials-pem": "PEM", + "credentials-pem-hint": "Minst serverens CA-sertifikatfil eller et par med klientsertifikat og privat nøkkelfil er påkrevd", + "credentials-sas": "Delt tilgangssignatur", + "sas-key": "SAS-nøkkel", + "sas-key-required": "SAS-nøkkel er påkrevd.", + "hostname": "Vertsnavn", + "hostname-required": "Vertsnavn er påkrevd.", + "azure-ca-cert": "CA-sertifikatfil", + "username-required": "Brukernavn er påkrevd.", + "password-required": "Passord er påkrevd.", + "ca-cert": "Serverens CA-sertifikatfil", + "private-key": "Klientens private nøkkelfil", + "cert": "Klientsertifikatfil", + "no-file": "Ingen fil valgt.", + "drop-file": "Slipp en fil eller klikk for å velge en fil for opplasting.", + "private-key-password": "Passord for privat nøkkel", + "use-system-smtp-settings": "Bruk systemets SMTP-innstillinger", + "use-metadata-dynamic-interval": "Bruk dynamisk intervall", + "metadata-dynamic-interval-hint": "Feltene for start og slutt på intervall støtter templatization. Den substituerte verdien må være angitt i millisekunder. Bruk $[messageKey] for verdi fra meldingen og ${metadataKey} for verdi fra metadata.", + "use-metadata-interval-patterns-hint": "Hvis valgt, bruker regelnoden start- og sluttintervallmønstre fra meldingsmetadata eller data, forutsatt at intervallene er i millisekunder.", + "use-message-alarm-data": "Bruk alarmdata fra meldingen", + "overwrite-alarm-details": "Overskriv alarmdetaljer", + "use-alarm-severity-pattern": "Bruk alvorlighetsmønster for alarm", + "check-all-keys": "Sjekk at alle spesifiserte felt er til stede", + "check-all-keys-hint": "Hvis valgt, kontrolleres det at alle spesifiserte nøkler finnes i meldingsdata og metadata.", + "check-relation-to-specific-entity": "Sjekk relasjon til spesifikk enhet", + "check-relation-to-specific-entity-tooltip": "Hvis aktivert, sjekkes relasjon med en spesifikk enhet. Ellers sjekkes relasjon med hvilken som helst enhet. I begge tilfeller er oppslag basert på konfigurert retning og type.", + "check-relation-hint": "Sjekker om det eksisterer en relasjon til en spesifikk eller hvilken som helst enhet basert på retning og relasjonstype.", + "delete-relation-with-specific-entity": "Slett relasjon med spesifikk enhet", + "delete-relation-with-specific-entity-hint": "Hvis aktivert, slettes kun relasjonen med én spesifikk enhet. Ellers slettes relasjonen med alle samsvarende enheter.", + "delete-relation-hint": "Sletter relasjon fra avsenderen av innkommende melding til spesifisert enhet eller enhetsliste basert på retning og type.", + "remove-current-relations": "Fjern nåværende relasjoner", + "remove-current-relations-hint": "Fjerner nåværende relasjoner fra avsenderen av meldingen basert på retning og type.", + "change-originator-to-related-entity": "Endre avsender til relatert enhet", + "change-originator-to-related-entity-hint": "Brukes til å behandle innsendt melding som en melding fra en annen enhet.", + "start-interval": "Start på intervall", + "end-interval": "Slutt på intervall", + "start-interval-required": "Start på intervall er påkrevd.", + "end-interval-required": "Slutt på intervall er påkrevd.", + "smtp-protocol": "Protokoll", + "smtp-host": "SMTP-vert", + "smtp-host-required": "SMTP-vert er påkrevd.", + "smtp-port": "SMTP-port", + "smtp-port-required": "Du må oppgi en SMTP-port.", + "smtp-port-range": "SMTP-port må være i området 1 til 65535.", + "timeout-msec": "Tidsavbrudd (ms)", + "min-timeout-msec-message": "Kun minimumsverdi 0 ms er tillatt.", + "enter-username": "Skriv inn brukernavn", + "enter-password": "Skriv inn passord", + "enable-tls": "Aktiver TLS", + "tls-version": "TLS-versjon", + "enable-proxy": "Aktiver proxy", + "use-system-proxy-properties": "Bruk systemets proxy-innstillinger", + "proxy-host": "Proxy-vert", + "proxy-host-required": "Proxy-vert er påkrevd.", + "proxy-port": "Proxy-port", + "proxy-port-required": "Proxy-port er påkrevd.", + "proxy-port-range": "Proxy-port må være i området 1 til 65535.", + "proxy-user": "Proxy-bruker", + "proxy-password": "Proxy-passord", + "proxy-scheme": "Proxy-skjema", + "numbers-to-template": "Telefonnumre til mal", + "numbers-to-template-required": "Telefonnumre til mal er påkrevd", + "numbers-to-template-hint": "Kommaseparerte telefonnumre, bruk ${metadataKey} for verdi fra metadata, $[messageKey] for verdi fra meldingsinnholdet", + "sms-message-template": "SMS-meldingsmal", + "sms-message-template-required": "SMS-meldingsmal er påkrevd", + "use-system-sms-settings": "Bruk systemets SMS-leverandørinnstillinger", + "min-period-0-seconds-message": "Kun minimumsverdi 0 sekunder er tillatt.", + "max-pending-messages": "Maksimalt antall ventende meldinger", + "max-pending-messages-required": "Maksimalt antall ventende meldinger er påkrevd.", + "max-pending-messages-range": "Maksimalt antall ventende meldinger må være i området 1 til 100000.", + "originator-types-filter": "Filter for opphavstyper", + "interval-seconds": "Intervall i sekunder", + "interval-seconds-required": "Intervall er påkrevd.", + "int-range": "Verdien må ikke overstige maksimumsgrensen for heltall (2147483648)", + "min-interval-seconds-message": "Bare minimumsintervall på 1 sekund er tillatt.", + "output-timeseries-key-prefix": "Prefiks for utgående tidsserienøkkel", + "output-timeseries-key-prefix-required": "Prefiks for utgående tidsserienøkkel er påkrevd.", + "separator-hint": "Du må trykke \"Enter\" for å fullføre feltinput.", + "select-details": "Velg detaljer", + "entity-details-id": "ID", + "entity-details-title": "Tittel", + "entity-details-country": "Land", + "entity-details-state": "Fylke", + "entity-details-city": "By", + "entity-details-zip": "Postnummer", + "entity-details-address": "Adresse", + "entity-details-address2": "Adresse 2", + "entity-details-additional_info": "Tilleggsinformasjon", + "entity-details-phone": "Telefon", + "entity-details-email": "E-post", + "email-sender": "Avsender av e-post", + "fields-to-check": "Felt som skal kontrolleres", + "add-detail": "Legg til detalj", + "check-all-keys-tooltip": "Hvis aktivert, kontrolleres tilstedeværelse av alle felt som er oppført i meldingen og metadataens feltnavn i innkommende melding og dens metadata.", + "fields-to-check-hint": "Trykk \"Enter\" for å fullføre feltinput. Flere feltnavn støttes.", + "entity-details-list-empty": "Minst én detalj må velges.", + "alarm-status": "Alarmstatus", + "alarm-required": "Minst én alarmstatus må velges.", + "no-entity-details-matching": "Ingen samsvarende enhetsdetaljer funnet.", + "custom-table-name": "Egendefinert tabellnavn", + "custom-table-name-required": "Tabellnavn er påkrevd", + "custom-table-hint": "Tabellen må være opprettet i din Cassandra-klynge og navnet må starte med prefikset 'cs_tb_' for å unngå innsending til TBs felles tabeller. Angi tabellnavnet uten prefikset 'cs_tb_'.", + "message-field": "Meldingsfelt", + "message-field-required": "Meldingsfelt er påkrevd.", + "table-col": "Tabellkolonne", + "table-col-required": "Tabellkolonne er påkrevd.", + "latitude-field-name": "Felt for breddegrad", + "longitude-field-name": "Felt for lengdegrad", + "latitude-field-name-required": "Felt for breddegrad er påkrevd.", + "longitude-field-name-required": "Felt for lengdegrad er påkrevd.", + "fetch-perimeter-info-from-metadata": "Hent perimeterinformasjon fra metadata", + "fetch-perimeter-info-from-metadata-tooltip": "Hvis perimeter-typen er satt til 'Polygon' vil verdien i metadataparameteren '{{perimeterKeyName}}' brukes direkte som perimeterdefinisjon. Hvis typen er 'Circle', vil feltet '{{perimeterKeyName}}' tolkes for å hente ut 'latitude', 'longitude', 'radius' og 'radiusUnit'.", + "perimeter-key-name": "Perimeternøkkelnavn", + "perimeter-key-name-hint": "Navnet på metadatafeltet som inneholder perimeterinformasjon.", + "perimeter-key-name-required": "Perimeternøkkelnavn er påkrevd.", + "perimeter-circle": "Sirkel", + "perimeter-polygon": "Polygon", + "perimeter-type": "Perimetertype", + "circle-center-latitude": "Senter breddegrad", + "circle-center-latitude-required": "Senter breddegrad er påkrevd.", + "circle-center-longitude": "Senter lengdegrad", + "circle-center-longitude-required": "Senter lengdegrad er påkrevd.", + "range-unit-meter": "Meter", + "range-unit-kilometer": "Kilometer", + "range-unit-foot": "Fot", + "range-unit-mile": "Mil", + "range-unit-nautical-mile": "Nautisk mil", + "range-units": "Avstandsenheter", + "range-units-required": "Avstandsenheter er påkrevd.", + "range": "Rekkevidde", + "range-required": "Rekkevidde er påkrevd.", + "polygon-definition": "Polygondefinisjon", + "polygon-definition-required": "Polygondefinisjon er påkrevd.", + "polygon-definition-hint": "Bruk følgende format for manuell definisjon av polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].", + "min-inside-duration": "Minste varighet innenfor", + "min-inside-duration-value-required": "Minste varighet innenfor er påkrevd", + "min-inside-duration-time-unit": "Tidsenhet for minste varighet innenfor", + "min-outside-duration": "Minste varighet utenfor", + "min-outside-duration-value-required": "Minste varighet utenfor er påkrevd", + "min-outside-duration-time-unit": "Tidsenhet for minste varighet utenfor", + "tell-failure-if-absent": "Rapporter feil", + "tell-failure-if-absent-hint": "Hvis minst én valgt nøkkel ikke eksisterer, vil utgående melding rapportere \"Feil\".", + "get-latest-value-with-ts": "Hent tidsstempel for siste telemetriverdier", + "get-latest-value-with-ts-hint": "Hvis valgt, vil de nyeste telemetriverdiene også inkludere tidsstempel, f.eks: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"", + "ignore-null-strings": "Ignorer tomme tekstverdier", + "ignore-null-strings-hint": "Hvis valgt, vil regelnoden ignorere enhetsfelt med tomme verdier.", + "add-metadata-key-values-as-kafka-headers": "Legg til metadata nøkkel-verdi par som Kafka header", + "add-metadata-key-values-as-kafka-headers-hint": "Hvis valgt, legges nøkkel-verdi-par fra meldingsmetadata til utgående header som byte-array med forhåndsdefinert tegnkoding.", + "charset-encoding": "Tegnkoding", + "charset-encoding-required": "Tegnkoding er påkrevd.", + "charset-us-ascii": "US-ASCII", + "charset-iso-8859-1": "ISO-8859-1", + "charset-utf-8": "UTF-8", + "charset-utf-16be": "UTF-16BE", + "charset-utf-16le": "UTF-16LE", + "charset-utf-16": "UTF-16", + "select-queue-hint": "Kønavnet kan velges fra en nedtrekksliste eller angi et egendefinert navn.", + "device-profile-node-hint": "Nyttig dersom du har varighet- eller gjentakelsesbetingelser for å sikre kontinuitet i alarmstatusvurderingen.", + "persist-alarm-rules": "Lagre status for alarmregler", + "persist-alarm-rules-hint": "Hvis aktivert, vil regelnoden lagre behandlingsstatus i databasen.", + "fetch-alarm-rules": "Hent status for alarmregler", + "fetch-alarm-rules-hint": "Hvis aktivert, vil regelnoden gjenopprette behandlingsstatus ved initiering og sikre at alarmer reises også etter serveromstarter. Ellers gjenopprettes status ved første melding fra enheten.", + "input-value-key": "Nøkkel for inndata", + "input-value-key-required": "Nøkkel for inndata er påkrevd.", + "output-value-key": "Nøkkel for utdata", + "output-value-key-required": "Nøkkel for utdata er påkrevd.", + "number-of-digits-after-floating-point": "Antall desimaler", + "number-of-digits-after-floating-point-range": "Antall desimaler må være i området 0 til 15.", + "failure-if-delta-negative": "Rapporter feil hvis differansen er negativ", + "failure-if-delta-negative-tooltip": "Regelnoden tvinger behandlingsfeil dersom differanseverdien er negativ.", + "use-caching": "Bruk caching", + "use-caching-tooltip": "Regelnoden cacher verdien til \"{{inputValueKey}}\" fra innkommende melding for bedre ytelse. Merk at cachen ikke oppdateres hvis verdien endres andre steder.", + "add-time-difference-between-readings": "Legg til tidsforskjell mellom \"{{inputValueKey}}\" målinger", + "add-time-difference-between-readings-tooltip": "Hvis aktivert, legges \"{{periodValueKey}}\" til utgående melding.", + "period-value-key": "Tidsperiode-nøkkel", + "period-value-key-required": "Tidsperiode-nøkkel er påkrevd.", + "general-pattern-hint": "Bruk ${metadataKey} for verdi fra metadata, $[messageKey] for verdi fra meldingskropp.", + "alarm-severity-pattern-hint": "Bruk ${metadataKey} for verdi fra metadata, $[messageKey] for verdi fra meldingsinnholdet. Alarmalvorlighetsgrad bør være system (CRITICAL, MAJOR osv.)", + "output-node-name-hint": "Regelnodens navn tilsvarer relasjonstype for utgående melding og brukes for å videresende meldinger til andre regelnoder i samme regelkjede.", + "use-server-ts": "Bruk serverens tidsstempel", + "use-server-ts-hint": "Bruk serverens nåværende tidsstempel for tidsseriedata uten eksplisitt stempel. Hjelper med å opprettholde riktig rekkefølge ved behandling av meldinger fra flere kilder.", + "kv-map-pattern-hint": "Alle inndatafelt støtter templatisering. Bruk $[messageKey] for verdi fra melding og ${metadataKey} for verdi fra metadata.", + "kv-map-single-pattern-hint": "Feltet støtter templatisering. Bruk $[messageKey] for verdi fra melding og ${metadataKey} for verdi fra metadata.", + "shared-scope": "Delt omfang", + "server-scope": "Serveromfang", + "client-scope": "Klientomfang", + "attribute-type": "Attributt", + "attribute-type-description": "Hent attributtverdi fra databasen", + "attribute-type-result-description": "Lagre resultat som enhetsattributt i databasen", + "constant-type": "Konstant", + "constant-type-description": "Definer konstant verdi", + "time-series-type": "Tidsserie", + "time-series-type-description": "Hent siste tidsserieverdi fra databasen", + "time-series-type-result-description": "Lagre resultat som enhetens tidsserie i databasen", + "message-body-type": "Melding", + "message-body-type-description": "Hent argumentverdi fra innkommende melding", + "message-body-type-result-description": "Legg til resultat i utgående melding", + "message-metadata-type": "Metadata", + "message-metadata-type-description": "Hent argumentverdi fra innkommende meldingsmetadata", + "message-metadata-result-description": "Legg til resultat i utgående meldingsmetadata", + "argument-tile": "Argumenter", + "no-arguments-prompt": "Ingen argumenter konfigurert", + "result-title": "Resultat", + "functions-field-input": "Funksjoner", + "no-option-found": "Ingen alternativ funnet", + "argument-source-field-input": "Kilde", + "argument-source-field-input-required": "Argumentkilde er påkrevd.", + "argument-key-field-input": "Nøkkel", + "argument-key-field-input-required": "Argumentnøkkel er påkrevd.", + "constant-value-field-input": "Konstant verdi", + "constant-value-field-input-required": "Konstant verdi er påkrevd.", + "attribute-scope-field-input": "Attributtomfang", + "attribute-scope-field-input-required": "Attributtomfang er påkrevd.", + "default-value-field-input": "Standardverdi", + "type-field-input": "Type", + "type-field-input-required": "Type er påkrevd.", + "key-field-input": "Nøkkel", + "add-entity-type": "Legg til enhetstype", + "add-device-profile": "Legg til enhetsprofil", + "key-field-input-required": "Nøkkel er påkrevd.", + "number-floating-point-field-input": "Antall desimaler", + "number-floating-point-field-input-hint": "Bruk 0 for å konvertere resultatet til heltall", + "add-to-message-field-input": "Legg til i melding", + "add-to-metadata-field-input": "Legg til i metadata", + "custom-expression-field-input": "Matematisk uttrykk", + "custom-expression-field-input-required": "Matematisk uttrykk er påkrevd", + "custom-expression-field-input-hint": "Angi et matematisk uttrykk som skal evalueres. Standarduttrykket viser hvordan man konverterer Fahrenheit til Celsius", + "retained-message": "Beholdt", + "attributes-mapping": "Attributtkartlegging", + "latest-telemetry-mapping": "Nyeste telemetrikartlegging", + "add-mapped-attribute-to": "Legg til kartlagte attributter i", + "add-mapped-latest-telemetry-to": "Legg til nyeste kartlagte telemetri i", + "add-mapped-fields-to": "Legg til kartlagte felt i", + "add-selected-details-to": "Legg til valgte detaljer i", + "clear-selected-types": "Fjern valgte typer", + "clear-selected-details": "Fjern valgte detaljer", + "clear-selected-fields": "Fjern valgte felt", + "clear-selected-keys": "Fjern valgte nøkler", + "geofence-configuration": "Geogjerde-konfigurasjon", + "coordinate-field-names": "Koordinatfeltnavn", + "coordinate-field-hint": "Regelnoden forsøker å hente de angitte feltene fra meldingen. Hvis de ikke finnes, blir de hentet fra metadata.", + "presence-monitoring-strategy": "Tilstedeværelsesstrategi", + "presence-monitoring-strategy-on-first-message": "Ved første melding", + "presence-monitoring-strategy-on-each-message": "Ved hver melding", + "presence-monitoring-strategy-on-first-message-hint": "Rapporterer status 'Inside' eller 'Outside' på første melding etter at konfigurert minimumsvarighet har passert siden forrige 'Entered' eller 'Left' statusoppdatering.", + "presence-monitoring-strategy-on-each-message-hint": "Rapporterer status 'Inside' eller 'Outside' på hver melding etter status 'Entered' eller 'Left'.", + "fetch-credentials-to": "Hent legitimasjon til", + "add-originator-attributes-to": "Legg til avsenders attributter i", + "originator-attributes": "Avsenders attributter", + "fetch-latest-telemetry-with-timestamp": "Hent siste telemetri med tidsstempel", + "fetch-latest-telemetry-with-timestamp-tooltip": "Hvis valgt, legges siste telemetriverdier til utgående metadata med tidsstempel, f.eks: \"{{latestTsKeyName}}\": \"{\"ts\":1574329385897, \"value\":42}\"", + "tell-failure": "Rapporter feil hvis noen attributter mangler", + "tell-failure-tooltip": "Hvis minst én valgt nøkkel ikke eksisterer, vil utgående melding rapportere \"Feil\".", + "created-time": "Opprettelsestid", + "chip-help": "Trykk 'Enter' for å fullføre {{inputName}} inntasting.\nTrykk 'Backspace' for å slette {{inputName}}.\nFlere verdier støttes.", + "detail": "detalj", + "field-name": "feltnavn", + "device-profile": "enhetsprofil", + "entity-type": "enhetstype", + "message-type": "meldingstype", + "timeseries-key": "tidsserienøkkel", + "type": "Type", + "first-name": "Fornavn", + "last-name": "Etternavn", + "label": "Etikett", + "originator-fields-mapping": "Kartlegging av avsenders felt", + "add-mapped-originator-fields-to": "Legg til kartlagte avsenderfelt i", + "fields": "Felt", + "skip-empty-fields": "Hopp over tomme felt", + "skip-empty-fields-tooltip": "Felt med tomme verdier vil ikke bli lagt til i utgående melding/metadata.", + "fetch-interval": "Henteintervall", + "fetch-strategy": "Hentestrategi", + "fetch-timeseries-from-to": "Hent tidsserie fra {{startInterval}} {{startIntervalTimeUnit}} siden til {{endInterval}} {{endIntervalTimeUnit}} siden.", + "fetch-timeseries-from-to-invalid": "Ugyldig tidsseriehenting (\"Startintervall\" må være mindre enn \"Sluttintervall\").", + "use-metadata-dynamic-interval-tooltip": "Hvis valgt, vil regelnoden bruke dynamisk start og slutt på intervallet basert på melding og metadata.", + "all-mode-hint": "Ved valg av hentemodus \"Alle\" vil regelnoden hente telemetri innenfor angitt intervall med konfigurerbare parametere.", + "first-mode-hint": "Ved valg av hentemodus \"Første\" vil regelnoden hente telemetri nærmest startpunktet for intervallet.", + "last-mode-hint": "Ved valg av hentemodus \"Siste\" vil regelnoden hente telemetri nærmest sluttpunktet for intervallet.", + "ascending": "Stigende", + "descending": "Synkende", + "min": "Minimum", + "max": "Maksimum", + "average": "Gjennomsnitt", + "sum": "Sum", + "count": "Antall", + "none": "Ingen", + "last-level-relation-tooltip": "Begrenser søk etter relaterte enheter til nivået definert i maks relasjonsnivå.", + "last-level-device-relation-tooltip": "Begrenser søk etter relaterte enheter til nivået definert i maks relasjonsnivå.", + "data-to-fetch": "Data som skal hentes", + "mapping-of-customers": "Kundekartlegging", + "map-fields-required": "Alle kartleggingsfelter er påkrevd.", + "attributes": "Attributter", + "related-device-attributes": "Relaterte enheters attributter", + "add-selected-attributes-to": "Legg til valgte attributter i", + "device-profiles": "Enhetsprofiler", + "mapping-of-tenant": "Leietakerkartlegging", + "add-attribute-key": "Legg til attributtnøkkel", + "message-template": "Meldingsmal", + "message-template-required": "Meldingsmal er påkrevd", + "use-system-slack-settings": "Bruk systemets Slack-innstillinger", + "slack-api-token": "Slack API-token", + "slack-api-token-required": "Slack API-token er påkrevd", + "keys-mapping": "Nøkkel-kartlegging", + "add-key": "Legg til nøkkel", + "recipients": "Mottakere", + "message-subject-and-content": "Emne og innhold for melding", + "template-rules-hint": "Begge felter støtter templatizering. Bruk $[messageKey] for verdier fra melding og ${metadataKey} for metadata.", + "originator-customer-desc": "Bruk kunde fra meldingens avsender som ny avsender.", + "originator-tenant-desc": "Bruk gjeldende leietaker som ny avsender.", + "originator-related-entity-desc": "Bruk relatert enhet som ny avsender. Basert på konfigurerte relasjonstyper og retning.", + "originator-alarm-originator-desc": "Bruk alarmens avsender som ny avsender. Kun hvis meldingsavsender er en alarm.", + "originator-entity-by-name-pattern-desc": "Bruk enhet fra databasen basert på navnmønster som ny avsender.", + "email-from-template-hint": "Bruk $[messageKey] for melding og ${metadataKey} for metadata.", + "recipients-block-main-hint": "Komma-separert adresseliste. Alle felt støtter templatizering.", + "forward-msg-default-rule-chain": "Videresend melding til avsenders standard regelkjede", + "forward-msg-default-rule-chain-tooltip": "Hvis aktivert, videresendes meldingen til regelkjede fra profil, eller standard regelkjede hvis definert.", + "exclude-zero-deltas": "Ekskluder null-deltaer fra utgående melding", + "exclude-zero-deltas-hint": "Legger kun til \"{{outputValueKey}}\" hvis verdien er ulik null.", + "exclude-zero-deltas-time-difference-hint": "Legger kun til \"{{outputValueKey}}\" og \"{{periodValueKey}}\" hvis \"{{outputValueKey}}\" ikke er null.", + "search-direction-from": "Fra avsender til målenhet", + "search-direction-to": "Fra målenhet til avsender", + "del-relation-direction-from": "Fra avsender", + "del-relation-direction-to": "Til avsender", + "target-entity": "Målenhet", + "function-configuration": "Funksjonskonfigurasjon", + "function-name": "Funksjonsnavn", + "function-name-required": "Funksjonsnavn er påkrevd.", + "qualifier": "Kvalifikator", + "qualifier-hint": "Standard kvalifikator \"$LATEST\" brukes hvis ikke spesifisert.", + "aws-credentials": "AWS-legitimasjon", + "connection-timeout": "Tilkoblingstimeout", + "connection-timeout-required": "Tilkoblingstimeout er påkrevd.", + "connection-timeout-min": "Minimum er 0.", + "connection-timeout-hint": "Ventetid i sekunder for opprettelse av tilkobling. 0 betyr uendelig og frarådes.", + "request-timeout": "Forespørselstimeout", + "request-timeout-required": "Forespørselstimeout er påkrevd", + "request-timeout-min": "Minimum er 0", + "request-timeout-hint": "Ventetid i sekunder for forespørsel før timeout. 0 betyr uendelig og frarådes.", + "units": "Enheter", + "tell-failure-aws-lambda": "Rapporter feil ved feil i AWS Lambda-funksjon", + "tell-failure-aws-lambda-hint": "Tvinger feilmelding hvis AWS Lambda-funksjonen kaster unntak.", + "basic-mode": "Grunnleggende", + "advanced-mode": "Avansert", + "save-time-series": { + "processing-settings": "Behandlingsinnstillinger", + "processing-settings-hint": "Definer hvordan innkommende meldinger behandles. Grunnleggende behandlingsinnstillinger lar deg velge forhåndskonfigurerte strategier, mens avanserte innstillinger lar deg velge individuelle behandlingsstrategier for hver handling.", + "advanced-settings-hint": "Vær forsiktig når du konfigurerer behandlingsstrategier. Enkelte kombinasjoner kan føre til uventet atferd.", + "strategy": "Strategi", + "deduplication-interval": "Dedupliseringsintervall", + "deduplication-interval-required": "Dedupliseringsintervall er påkrevd", + "deduplication-interval-min-max-range": "Dedupliseringsintervall må være minst 1 sekund og maks 1 dag", + "strategy-type": { + "every-message": "Ved hver melding", + "skip": "Hopp over", + "deduplicate": "Dedupliser", + "web-sockets-only": "Kun WebSockets" + }, + "time-series": "Tidsserie", + "latest": "Siste verdier", + "web-sockets": "WebSockets", + "calculated-fields": "Beregnete felt" + }, + "save-attribute": { + "processing-settings": "Behandlingsinnstillinger", + "processing-settings-hint": "Definer hvordan innkommende meldinger behandles. Grunnleggende behandlingsinnstillinger lar deg velge forhåndskonfigurerte strategier, mens avanserte innstillinger lar deg velge individuelle behandlingsstrategier for hver handling.", + "advanced-settings-hint": "Vær forsiktig når du konfigurerer behandlingsstrategier. Enkelte kombinasjoner kan føre til uventet atferd.", + "strategy": "Strategi", + "deduplication-interval": "Dedupliseringsintervall", + "deduplication-interval-required": "Dedupliseringsintervall er påkrevd", + "deduplication-interval-min-max-range": "Dedupliseringsintervall må være minst 1 sekund og maks 1 dag", + "scope": "Omfang", + "strategy-type": { + "every-message": "Ved hver melding", + "skip": "Hopp over", + "deduplicate": "Dedupliser", + "web-sockets-only": "Kun WebSockets" + }, + "attributes": "Attributter" + }, + "key-val": { + "key": "Nøkkel", + "value": "Verdi", + "see-examples": "Se eksempler.", + "remove-entry": "Fjern oppføring", + "remove-mapping-entry": "Fjern kartleggingsoppføring", + "add-mapping-entry": "Legg til kartlegging", + "add-entry": "Legg til oppføring", + "copy-key-values-from": "Kopier nøkkel-verdi-par fra", + "delete-key-values": "Slett nøkkel-verdi-par", + "delete-key-values-from": "Slett nøkkel-verdi-par fra", + "at-least-one-key-error": "Minst én nøkkel må være valgt.", + "unique-key-value-pair-error": "'{{keyText}}' må være forskjellig fra '{{valText}}'!" + }, + "mail-body-types": { + "plain-text": "Ren tekst", + "html": "HTML", + "dynamic": "Dynamisk", + "use-body-type-template": "Bruk mal for kroppstype", + "plain-text-description": "Enkel, uformatert tekst uten spesiell stil eller formatering.", + "html-text-description": "Lar deg bruke HTML-tag'er for formatering, lenker og bilder i meldingskroppen.", + "dynamic-text-description": "Lar deg bruke ren tekst eller HTML dynamisk basert på templatization-funksjon.", + "after-template-evaluation-hint": "Etter evaluering må verdien være true for HTML og false for ren tekst." + } + }, + "timezone": { + "timezone": "Tidssone", + "select-timezone": "Velg tidssone", + "no-timezones-matching": "Ingen tidssoner som samsvarer med '{{timezone}}' ble funnet.", + "timezone-required": "Tidssone er påkrevd.", + "browser-time": "Nettlesertid" + }, + "queue": { + "queue-name": "Kø", + "no-queues-found": "Ingen køer funnet.", + "no-queues-matching": "Ingen køer som samsvarer med '{{queue}}' ble funnet.", + "select-name": "Velg kønavn", + "name": "Navn", + "name-required": "Kønavn er påkrevd!", + "name-unique": "Kønavnet er ikke unikt!", + "name-pattern": "Kønavnet inneholder tegn som ikke er ASCII-bokstaver, '.', '_' eller '-'!", + "queue-required": "Kø er påkrevd!", + "topic-required": "Køens emne er påkrevd!", + "poll-interval-required": "Spørringsintervall er påkrevd!", + "poll-interval-min-value": "Spørringsintervall kan ikke være mindre enn 1", + "partitions-required": "Partisjoner er påkrevd!", + "partitions-min-value": "Antall partisjoner kan ikke være mindre enn 1", + "pack-processing-timeout-required": "Behandlingstid er påkrevd", + "pack-processing-timeout-min-value": "Behandlingstid kan ikke være mindre enn 1", + "batch-size-required": "Batchstørrelse er påkrevd!", + "batch-size-min-value": "Batchstørrelse kan ikke være mindre enn 1", + "retries-required": "Antall forsøk er påkrevd!", + "retries-min-value": "Antall forsøk kan ikke være negativt", + "failure-percentage-required": "Feilprosent er påkrevd!", + "failure-percentage-min-value": "Feilprosent kan ikke være mindre enn 0", + "failure-percentage-max-value": "Feilprosent kan ikke være høyere enn 100", + "pause-between-retries-required": "Pause mellom forsøk er påkrevd!", + "pause-between-retries-min-value": "Pause mellom forsøk kan ikke være mindre enn 1", + "max-pause-between-retries-required": "Maks pause mellom forsøk er påkrevd!", + "max-pause-between-retries-min-value": "Maks pause mellom forsøk kan ikke være mindre enn 1", + "submit-strategy-type-required": "Sendingsstrategi er påkrevd!", + "processing-strategy-type-required": "Behandlingsstrategi er påkrevd!", + "queues": "Køer", + "selected-queues": "{ count, plural, =1 {1 kø} other {# køer} } valgt", + "delete-queue-title": "Er du sikker på at du vil slette køen '{{queueName}}'?", + "delete-queues-title": "Er du sikker på at du vil slette { count, plural, =1 {1 kø} other {# køer} }?", + "delete-queue-text": "Vær forsiktig, etter bekreftelsen vil køen og alle relaterte data ikke kunne gjenopprettes.", + "delete-queues-text": "Etter bekreftelsen vil alle valgte køer bli slettet og ikke være tilgjengelige.", + "search": "Søk i kø", + "add": "Legg til kø", + "details": "Kødetaljer", + "topic": "Emne", + "submit-settings": "Sendingsinnstillinger", + "submit-strategy": "Strategitype *", + "grouping-parameter": "Grupperingsparameter", + "processing-settings": "Behandlingsinnstillinger for forsøk", + "processing-strategy": "Behandlingstype *", + "retries-settings": "Innstillinger for forsøk", + "polling-settings": "Spørringsinnstillinger", + "batch-processing": "Batchbehandling", + "poll-interval": "Spørringsintervall", + "partitions": "Partisjoner", + "immediate-processing": "Umiddelbar behandling", + "consumer-per-partition": "Send spørring for hver forbruker", + "consumer-per-partition-hint": "Aktiver separat forbruker per partisjon", + "duplicate-msg-to-all-partitions": "Dupliser melding til alle partisjoner", + "processing-timeout": "Behandling innen, ms", + "batch-size": "Batchstørrelse", + "retries": "Antall forsøk (0 – ubegrenset)", + "failure-percentage": "Feilmeldinger for å hoppe over forsøk, %", + "pause-between-retries": "Nytt forsøk innen, sek", + "max-pause-between-retries": "Ekstra nytt forsøk innen, sek", + "delete": "Slett kø", + "copyId": "Kopier kø-ID", + "idCopiedMessage": "Kø-ID er kopiert til utklippstavlen", + "description": "Beskrivelse", + "description-hint": "Denne teksten vises i købeskrivelsen i stedet for valgt strategi", + "alt-description": "Sendingsstrategi: {{submitStrategy}}, Behandlingsstrategi: {{processingStrategy}}", + "custom-properties": "Egendefinerte egenskaper", + "custom-properties-hint": "Egendefinerte egenskaper for oppretting av kø (emne), f.eks. 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies": { + "sequential-by-originator-label": "Sekvensiell per opprinnelse", + "sequential-by-originator-hint": "Ny melding for f.eks. enhet A sendes ikke før forrige melding for enhet A er bekreftet", + "sequential-by-tenant-label": "Sekvensiell per leietaker", + "sequential-by-tenant-hint": "Ny melding for f.eks. leietaker A sendes ikke før forrige melding for leietaker A er bekreftet", + "sequential-label": "Sekvensiell", + "sequential-hint": "Ny melding sendes ikke før forrige melding er bekreftet", + "burst-label": "Burst", + "burst-hint": "Alle meldinger sendes til regelkjeder i den rekkefølgen de ankommer", + "batch-label": "Batch", + "batch-hint": "Ny batch sendes ikke før forrige batch er bekreftet", + "skip-all-failures-label": "Hopp over alle feil", + "skip-all-failures-hint": "Ignorer alle feil", + "skip-all-failures-and-timeouts-label": "Hopp over alle feil og tidsavbrudd", + "skip-all-failures-and-timeouts-hint": "Ignorer alle feil og tidsavbrudd", + "retry-all-label": "Prøv alle på nytt", + "retry-all-hint": "Prøv alle meldinger fra behandlingspakken på nytt", + "retry-failed-label": "Prøv mislykkede på nytt", + "retry-failed-hint": "Prøv alle mislykkede meldinger fra behandlingspakken på nytt", + "retry-timeout-label": "Prøv tidsavbrudd på nytt", + "retry-timeout-hint": "Prøv alle tidsavbrutte meldinger fra behandlingspakken på nytt", + "retry-failed-and-timeout-label": "Prøv mislykkede og tidsavbrudd på nytt", + "retry-failed-and-timeout-hint": "Prøv alle mislykkede og tidsavbrutte meldinger fra behandlingspakken på nytt" + } + }, + "queue-statistics": { + "queue-statistics": "Køstatistikk", + "no-queue-statistics-matching": "Ingen køstatistikk som samsvarer med '{{entity}}' ble funnet.", + "queue-statistics-required": "Køstatistikk er påkrevd.", + "list-of-queue-statistics": "{ count, plural, =1 {Én køstatistikk} other {Liste over # køstatistikker} }", + "selected-queue-statistics": "{ count, plural, =1 {1 køstatistikk} other {# køstatistikker} } valgt", + "no-queue-statistics-text": "Ingen køstatistikk funnet", + "queue-statistics-starts-with": "Køstatistikk med navn som starter med '{{prefix}}'" + }, + "server-error": { + "general": "Generell serverfeil", + "authentication": "Autentiseringsfeil", + "jwt-token-expired": "JWT-token utløpt", + "tenant-trial-expired": "Leietakerens prøveperiode er utløpt", + "credentials-expired": "Legitimasjon utløpt", + "permission-denied": "Tillatelse nektet", + "invalid-arguments": "Ugyldige argumenter", + "bad-request-params": "Feil forespørselsparametere", + "item-not-found": "Element ikke funnet", + "too-many-requests": "For mange forespørsler", + "too-many-updates": "For mange oppdateringer" + }, + "tenant": { + "tenant": "Leietaker", + "tenants": "Leietakere", + "management": "Leietakeradministrasjon", + "add": "Legg til leietaker", + "admins": "Administratorer", + "manage-tenant-admins": "Administrer leietakeradministratorer", + "delete": "Slett leietaker", + "add-tenant-text": "Legg til ny leietaker", + "no-tenants-text": "Ingen leietakere funnet", + "tenant-details": "Leietakerdetaljer", + "title-max-length": "Tittel må være mindre enn 256 tegn", + "delete-tenant-title": "Er du sikker på at du vil slette leietakeren '{{tenantTitle}}'?", + "delete-tenant-text": "Vær forsiktig, etter bekreftelsen vil leietakeren og alle relaterte data ikke kunne gjenopprettes.", + "delete-tenants-title": "Er du sikker på at du vil slette { count, plural, =1 {1 leietaker} other {# leietakere} }?", + "delete-tenants-action-title": "Slett { count, plural, =1 {1 leietaker} other {# leietakere} }", + "delete-tenants-text": "Vær forsiktig, etter bekreftelsen vil alle valgte leietakere bli fjernet og alle relaterte data vil ikke kunne gjenopprettes.", + "title": "Tittel", + "title-required": "Tittel er påkrevd.", + "description": "Beskrivelse", + "details": "Detaljer", + "events": "Hendelser", + "copyId": "Kopier leietaker-ID", + "idCopiedMessage": "Leietaker-ID er kopiert til utklippstavlen", + "select-tenant": "Velg leietaker", + "no-tenants-matching": "Ingen leietakere som samsvarer med '{{entity}}' ble funnet.", + "tenant-required": "Leietaker er påkrevd", + "search": "Søk etter leietakere", + "selected-tenants": "{ count, plural, =1 {1 leietaker} other {# leietakere} } valgt", + "isolated-tb-rule-engine": "Bruk isolerte ThingsBoard Rule Engine-køer", + "isolated-tb-rule-engine-details": "Hver leietaker får dedikerte Rule Engine-køer" + }, + "tenant-profile": { + "tenant-profile": "Leietakerprofil", + "tenant-profiles": "Leietakerprofiler", + "add": "Legg til leietakerprofil", + "add-profile": "Legg til profil", + "debug": "Feilsøk", + "edit": "Rediger leietakerprofil", + "tenant-profile-details": "Detaljer for leietakerprofil", + "no-tenant-profiles-text": "Ingen leietakerprofiler funnet", + "name-max-length": "Navn må være mindre enn 256 tegn", + "search": "Søk leietakerprofiler", + "selected-tenant-profiles": "{ count, plural, =1 {1 leietakerprofil} other {# leietakerprofiler} } valgt", + "no-tenant-profiles-matching": "Ingen leietakerprofiler som samsvarer med '{{entity}}' ble funnet.", + "tenant-profile-required": "Leietakerprofil er påkrevd", + "idCopiedMessage": "Leietakerprofil-ID er kopiert til utklippstavlen", + "set-default": "Gjør leietakerprofil til standard", + "delete": "Slett leietakerprofil", + "copyId": "Kopier leietakerprofil-ID", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "data": "Profildata", + "profile-configuration": "Profilkonfigurasjon", + "description": "Beskrivelse", + "default": "Standard", + "delete-tenant-profile-title": "Er du sikker på at du vil slette leietakerprofilen '{{tenantProfileName}}'?", + "delete-tenant-profile-text": "Vær forsiktig, etter bekreftelse vil leietakerprofilen og alle relaterte data ikke kunne gjenopprettes.", + "delete-tenant-profiles-title": "Er du sikker på at du vil slette { count, plural, =1 {1 leietakerprofil} other {# leietakerprofiler} }?", + "delete-tenant-profiles-text": "Vær forsiktig, etter bekreftelse vil alle valgte leietakerprofiler bli fjernet og alle relaterte data vil ikke kunne gjenopprettes.", + "set-default-tenant-profile-title": "Er du sikker på at du vil gjøre leietakerprofilen '{{tenantProfileName}}' til standard?", + "set-default-tenant-profile-text": "Etter bekreftelse vil leietakerprofilen bli markert som standard og vil bli brukt for nye leietakere uten angitt profil.", + "no-tenant-profiles-found": "Ingen leietakerprofiler funnet.", + "create-new-tenant-profile": "Opprett en ny!", + "create-tenant-profile": "Opprett ny leietakerprofil", + "import": "Importer leietakerprofil", + "export": "Eksporter leietakerprofil", + "export-failed-error": "Kan ikke eksportere leietakerprofil: {{error}}", + "tenant-profile-file": "Leietakerprofilfil", + "invalid-tenant-profile-file-error": "Kan ikke importere leietakerprofil: Ugyldig datastruktur for leietakerprofil.", + "advanced-settings": "Avanserte innstillinger", + "entities": "Enheter", + "rule-engine": "Regelmotor", + "time-to-live": "Levetid", + "calculated-fields": "Beregnete felt", + "alarms-and-notifications": "Alarmer og varsler", + "ota-files-in-bytes": "Filer", + "ws-title": "WS", + "unlimited": "(0 - ubegrenset)", + "maximum-devices": "Maksimalt antall enheter", + "maximum-devices-required": "Maksimalt antall enheter er påkrevd.", + "maximum-devices-range": "Maksimalt antall enheter kan ikke være negativt", + "maximum-assets": "Maksimalt antall eiendeler", + "maximum-assets-required": "Maksimalt antall eiendeler er påkrevd.", + "maximum-assets-range": "Maksimalt antall eiendeler kan ikke være negativt", + "maximum-customers": "Maksimalt antall kunder", + "maximum-customers-required": "Maksimalt antall kunder er påkrevd.", + "maximum-customers-range": "Maksimalt antall kunder kan ikke være negativt", + "maximum-users": "Maksimalt antall brukere", + "maximum-users-required": "Maksimalt antall brukere er påkrevd.", + "maximum-users-range": "Maksimalt antall brukere kan ikke være negativt", + "maximum-dashboards": "Maksimalt antall dashbord", + "maximum-dashboards-required": "Maksimalt antall dashbord er påkrevd.", + "maximum-dashboards-range": "Maksimalt antall dashbord kan ikke være negativt", + "maximum-edges": "Maksimalt antall kanter", + "maximum-edges-required": "Maksimalt antall kanter er påkrevd.", + "maximum-edges-range": "Maksimalt antall kanter kan ikke være negativt", + "maximum-rule-chains": "Maksimalt antall regelkjeder", + "maximum-rule-chains-required": "Maksimalt antall regelkjeder er påkrevd.", + "maximum-rule-chains-range": "Maksimalt antall regelkjeder kan ikke være negativt", + "maximum-resources-sum-data-size": "Maksimal total størrelse på ressursfiler (bytes)", + "maximum-resources-sum-data-size-required": "Maksimal total størrelse på ressursfiler er påkrevd.", + "maximum-resources-sum-data-size-range": "Maksimal total størrelse på ressursfiler kan ikke være negativ", + "maximum-resource-size": "Maksimal størrelse på ressursfil (bytes)", + "maximum-resource-size-required": "Maksimal størrelse på ressursfil er påkrevd", + "maximum-resource-size-range": "Maksimal størrelse på ressursfil kan ikke være negativ", + "maximum-ota-packages-sum-data-size": "Maksimal total størrelse på OTA-pakker (bytes)", + "maximum-ota-package-sum-data-size-required": "Maksimal total størrelse på OTA-pakker er påkrevd.", + "maximum-ota-package-sum-data-size-range": "Maksimal total størrelse på OTA-pakker kan ikke være negativ", + "maximum-debug-duration-min": "Maksimal feilsøkingsvarighet (min)", + "maximum-debug-duration-min-range": "Maksimal feilsøkingsvarighet kan ikke være negativ", + "rest-requests-for-tenant": "REST-forespørsler for leietaker", + "transport-tenant-telemetry-msg-rate-limit": "Transport telemetrimeldinger for leietaker", + "transport-tenant-telemetry-data-points-rate-limit": "Transport datapunkter for leietaker", + "transport-device-msg-rate-limit": "Transport enhetsmeldinger", + "transport-device-telemetry-msg-rate-limit": "Transport enhetstelemetrimeldinger", + "transport-device-telemetry-data-points-rate-limit": "Transport enhetstelemetridatapunkter", + "transport-gateway-msg-rate-limit": "Transport gateway-meldinger", + "transport-gateway-telemetry-msg-rate-limit": "Transport gateway-telemetrimeldinger", + "transport-gateway-telemetry-data-points-rate-limit": "Transport gateway-telemetridatapunkter", + "transport-gateway-device-msg-rate-limit": "Transport gateway-enhetsmeldinger", + "transport-gateway-device-telemetry-msg-rate-limit": "Transport gateway-enhetstelemetrimeldinger", + "transport-gateway-device-telemetry-data-points-rate-limit": "Transport gateway-enhetstelemetridatapunkter", + "tenant-entity-export-rate-limit": "Oppretting av enhetsversjon", + "tenant-entity-import-rate-limit": "Lasting av enhetsversjon", + "tenant-notification-request-rate-limit": "Varslingsforespørsler", + "tenant-notification-requests-per-rule-rate-limit": "Varslingsforespørsler per regel", + "max-calculated-fields": "Maksimalt antall beregnede felt per enhet", + "max-calculated-fields-range": "Maksimalt antall beregnede felt per enhet kan ikke være negativt", + "max-calculated-fields-required": "Maksimalt antall beregnede felt per enhet er påkrevd", + "max-data-points-per-rolling-arg": "Maksimalt antall datapunkter i rullerende argumenter", + "max-data-points-per-rolling-arg-range": "Maksimalt antall datapunkter i rullerende argumenter kan ikke være negativt", + "max-data-points-per-rolling-arg-required": "Maksimalt antall datapunkter i rullerende argumenter er påkrevd", + "max-arguments-per-cf": "Maksimalt antall argumenter per beregnet felt", + "max-arguments-per-cf-range": "Maksimalt antall argumenter per beregnet felt kan ikke være negativt", + "max-arguments-per-cf-required": "Maksimalt antall argumenter per beregnet felt er påkrevd", + "max-state-size": "Maksimal tilstandsstørrelse i KB", + "max-state-size-range": "Maksimal tilstandsstørrelse i KB kan ikke være negativ", + "max-state-size-required": "Maksimal tilstandsstørrelse i KB er påkrevd", + "max-value-argument-size": "Maksimal størrelse på enkeltverdiargument i KB", + "max-value-argument-size-range": "Maksimal størrelse på enkeltverdiargument i KB kan ikke være negativ", + "max-value-argument-size-required": "Maksimal størrelse på enkeltverdiargument i KB er påkrevd", + "max-transport-messages": "Maksimalt antall transportmeldinger", + "max-transport-messages-required": "Maksimalt antall transportmeldinger er påkrevd.", + "max-transport-messages-range": "Maksimalt antall transportmeldinger kan ikke være negativt", + "max-transport-data-points": "Maksimalt antall transportdatapunkter", + "max-transport-data-points-required": "Maksimalt antall transportdatapunkter er påkrevd.", + "max-transport-data-points-range": "Maksimalt antall transportdatapunkter kan ikke være negativt", + "max-r-e-executions": "Maksimalt antall regelmotorutførelser", + "max-r-e-executions-required": "Maksimalt antall regelmotorutførelser er påkrevd.", + "max-r-e-executions-range": "Maksimalt antall regelmotorutførelser kan ikke være negativt", + "max-j-s-executions": "Maksimalt antall JavaScript-utførelser", + "max-j-s-executions-required": "Maksimalt antall JavaScript-utførelser er påkrevd.", + "max-j-s-executions-range": "Maksimalt antall JavaScript-utførelser kan ikke være negativt", + "max-tbel-executions": "Maksimalt antall TBEL-utførelser", + "max-tbel-executions-required": "Maksimalt antall TBEL-utførelser er påkrevd.", + "max-tbel-executions-range": "Maksimalt antall TBEL-utførelser kan ikke være negativt", + "max-d-p-storage-days": "Maksimalt antall dager for lagring av datapunkter", + "max-d-p-storage-days-required": "Maksimalt antall dager for lagring av datapunkter er påkrevd.", + "max-d-p-storage-days-range": "Maksimalt antall dager for lagring av datapunkter kan ikke være negativt", + "default-storage-ttl-days": "Standard lagring TTL dager", + "default-storage-ttl-days-required": "Standard lagring TTL dager er påkrevd.", + "default-storage-ttl-days-range": "Standard lagring TTL dager kan ikke være negativt", + "alarms-ttl-days": "Alarmer TTL dager", + "alarms-ttl-days-required": "Alarmer TTL dager er påkrevd", + "alarms-ttl-days-days-range": "Alarmer TTL dager kan ikke være negativt", + "rpc-ttl-days": "RPC TTL dager", + "rpc-ttl-days-required": "RPC TTL dager er påkrevd", + "rpc-ttl-days-days-range": "RPC TTL dager kan ikke være negativt", + "queue-stats-ttl-days": "TTL-dager for køstatistikk", + "queue-stats-ttl-days-required": "TTL-dager for køstatistikk er påkrevd", + "queue-stats-ttl-days-range": "TTL-dager for køstatistikk kan ikke være negativt", + "rule-engine-exceptions-ttl-days": "TTL-dager for regelmotorunntak", + "rule-engine-exceptions-ttl-days-required": "TTL-dager for regelmotorunntak er påkrevd", + "rule-engine-exceptions-ttl-days-range": "TTL-dager for regelmotorunntak kan ikke være negativt", + "max-rule-node-executions-per-message": "Maksimalt antall regelnodeutførelser per melding", + "max-rule-node-executions-per-message-required": "Maksimalt antall regelnodeutførelser per melding er påkrevd.", + "max-rule-node-executions-per-message-range": "Maksimalt antall regelnodeutførelser per melding kan ikke være negativt", + "max-emails": "Maksimalt antall sendte e-poster", + "max-emails-required": "Maksimalt antall sendte e-poster er påkrevd.", + "max-emails-range": "Maksimalt antall sendte e-poster kan ikke være negativt", + "sms-enabled": "SMS aktivert", + "max-sms": "Maksimalt antall sendte SMS", + "max-sms-required": "Maksimalt antall sendte SMS er påkrevd.", + "max-sms-range": "Maksimalt antall sendte SMS kan ikke være negativt", + "max-created-alarms": "Maksimalt antall opprettede alarmer", + "max-created-alarms-required": "Maksimalt antall opprettede alarmer er påkrevd.", + "max-created-alarms-range": "Maksimalt antall opprettede alarmer kan ikke være negativt", + "no-queue": "Ingen kø konfigurert", + "add-queue": "Legg til kø", + "queues-with-count": "Køer ({{count}})", + "tenant-rest-limits": "REST-forespørsler for leietaker", + "customer-rest-limits": "REST-forespørsler for kunde", + "incorrect-pattern-for-rate-limits": "Formatet er kommaseparerte par av kapasitet og periode (i sekunder) med kolon mellom, f.eks. 100:1,2000:60", + "too-small-value-zero": "Verdien må være større enn 0", + "too-small-value-one": "Verdien må være større enn 1", + "queue-size-is-limited-by-system-configuration": "Størrelsen på køen er også begrenset av systemkonfigurasjonen.", + "cassandra-tenant-limits-configuration": "Cassandra-spørring for leietaker", + "ws-limit-max-sessions-per-tenant": "Maksimalt antall økter per leietaker", + "ws-limit-max-sessions-per-customer": "Maksimalt antall økter per kunde", + "ws-limit-max-sessions-per-regular-user": "Maksimalt antall økter per vanlig bruker", + "ws-limit-max-sessions-per-public-user": "Maksimalt antall økter per offentlig bruker", + "ws-limit-queue-per-session": "Maksimal størrelse på meldingskø per økt", + "ws-limit-max-subscriptions-per-tenant": "Maksimalt antall abonnementer per leietaker", + "ws-limit-max-subscriptions-per-customer": "Maksimalt antall abonnementer per kunde", + "ws-limit-max-subscriptions-per-regular-user": "Maksimalt antall abonnementer per vanlig bruker", + "ws-limit-max-subscriptions-per-public-user": "Maksimalt antall abonnementer per offentlig bruker", + "ws-limit-updates-per-session": "WS-oppdateringer per økt", + "rate-limits": { + "add-limit": "Legg til grense", + "advanced-settings": "Avanserte innstillinger", + "edit-limit": "Rediger grense", + "but-less-than": "men mindre enn", + "calculated-field-debug-event-rate-limit": "Feilsøkingshendelser for beregnet felt", + "edit-calculated-field-debug-event-rate-limit": "Rediger grense for feilsøkingshendelser for beregnet felt", + "edit-transport-tenant-msg-title": "Rediger grense for transportmeldinger for leietaker", + "edit-transport-tenant-telemetry-msg-title": "Rediger grense for transporttelemetrimedlinger for leietaker", + "edit-transport-tenant-telemetry-data-points-title": "Rediger grense for transporttelemetridatapunkter for leietaker", + "edit-transport-device-msg-title": "Rediger grense for transportmeldinger for enhet", + "edit-transport-device-telemetry-msg-title": "Rediger grense for telemetrimedlinger for enhet", + "edit-transport-device-telemetry-data-points-title": "Rediger grense for telemetridatapunkter for enhet", + "edit-transport-gateway-msg-title": "Rediger grense for transportmeldinger for gateway", + "edit-transport-gateway-telemetry-msg-title": "Rediger grense for telemetrimedlinger for gateway", + "edit-transport-gateway-telemetry-data-points-title": "Rediger grense for telemetridatapunkter for gateway", + "edit-transport-gateway-device-msg-title": "Rediger grense for gateway-enhetsmeldinger", + "edit-transport-gateway-device-telemetry-msg-title": "Rediger grense for gateway-enhetstelemetrimedlinger", + "edit-transport-gateway-device-telemetry-data-points-title": "Rediger grense for gateway-enhetstelemetridatapunkter", + "edit-tenant-rest-limits-title": "Rediger REST-forespørsler for leietaker", + "edit-customer-rest-limits-title": "Rediger REST-forespørsler for kunde", + "edit-ws-limit-updates-per-session-title": "Rediger grense for WS-oppdateringer per økt", + "edit-cassandra-tenant-limits-configuration-title": "Rediger Cassandra-spørringsgrenser for leietaker", + "edit-tenant-entity-export-rate-limit-title": "Rediger grense for eksport av enhetsversjoner", + "edit-tenant-entity-import-rate-limit-title": "Rediger grense for import av enhetsversjoner", + "edit-tenant-notification-request-rate-limit-title": "Rediger grense for varslingsforespørsler", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "Rediger grense for varslingsforespørsler per regel", + "edit-edge-events-rate-limit": "Rediger grense for edge-hendelser", + "edit-edge-events-per-edge-rate-limit": "Rediger grense for edge-hendelser per edge", + "edge-events-rate-limit": "Edge-hendelser", + "edge-events-per-edge-rate-limit": "Edge-hendelser per edge", + "edit-edge-uplink-messages-rate-limit": "Rediger grense for opplinks-meldinger fra edge", + "edit-edge-uplink-messages-per-edge-rate-limit": "Rediger grense for opplinks-meldinger per edge", + "edge-uplink-messages-rate-limit": "Opplinks-meldinger fra edge", + "edge-uplink-messages-per-edge-rate-limit": "Opplinks-meldinger per edge", + "messages-per": "meldinger per", + "not-set": "Ikke angitt", + "number-of-messages": "Antall meldinger", + "number-of-messages-required": "Antall meldinger er påkrevd.", + "number-of-messages-min": "Minimumsverdi er 1.", + "preview": "Forhåndsvisning", + "per-seconds": "Per sekunder", + "per-seconds-required": "Tidstakt er påkrevd.", + "per-seconds-min": "Minimumsverdi er 1.", + "rate-limits": "Hastighetsgrenser", + "remove-limit": "Fjern grense", + "transport-tenant-msg": "Transportmeldinger for leietaker", + "transport-tenant-telemetry-msg": "Transporttelemetrimedlinger for leietaker", + "transport-tenant-telemetry-data-points": "Transporttelemetridatapunkter for leietaker", + "transport-device-msg": "Transportmeldinger for enhet", + "transport-device-telemetry-msg": "Telemetrimedlinger for enhet", + "transport-device-telemetry-data-points": "Telemetridatapunkter for enhet", + "transport-gateway-msg": "Transportmeldinger for gateway", + "transport-gateway-telemetry-msg": "Telemetrimedlinger for gateway", + "transport-gateway-telemetry-data-points": "Telemetridatapunkter for gateway", + "transport-gateway-device-msg": "Meldinger for gateway-enhet", + "transport-gateway-device-telemetry-msg": "Telemetrimedlinger for gateway-enhet", + "transport-gateway-device-telemetry-data-points": "Telemetridatapunkter for gateway-enhet", + "sec": "sek" + } + }, + "timeinterval": { + "seconds-interval": "{ seconds, plural, =1 {1 sekund} other {# sekunder} }", + "minutes-interval": "{ minutes, plural, =1 {1 minutt} other {# minutter} }", + "hours-interval": "{ hours, plural, =1 {1 time} other {# timer} }", + "days-interval": "{ days, plural, =1 {1 dag} other {# dager} }", + "days": "Dager", + "hours": "Timer", + "minutes": "Minutter", + "seconds": "Sekunder", + "advanced": "Avansert", + "custom": "Egendefinert", + "predefined": { + "yesterday": "I går", + "day-before-yesterday": "Dagen før i går", + "this-day-last-week": "Denne dagen forrige uke", + "previous-week": "Forrige uke (søn - lør)", + "previous-week-iso": "Forrige uke (man - søn)", + "previous-month": "Forrige måned", + "previous-quarter": "Forrige kvartal", + "previous-half-year": "Forrige halvår", + "previous-year": "Forrige år", + "current-hour": "Nåværende time", + "current-day": "Nåværende dag", + "current-day-so-far": "Nåværende dag så langt", + "current-week": "Nåværende uke (søn - lør)", + "current-week-iso": "Nåværende uke (man - søn)", + "current-week-so-far": "Nåværende uke så langt (søn - lør)", + "current-week-iso-so-far": "Nåværende uke så langt (man - søn)", + "current-month": "Nåværende måned", + "current-month-so-far": "Nåværende måned så langt", + "current-quarter": "Nåværende kvartal", + "current-quarter-so-far": "Nåværende kvartal så langt", + "current-half-year": "Nåværende halvår", + "current-half-year-so-far": "Nåværende halvår så langt", + "current-year": "Nåværende år", + "current-year-so-far": "Nåværende år så langt" + }, + "type": { + "week": "Uke (søn - lør)", + "week-iso": "Uke (man - søn)", + "month": "Måned", + "quarter": "Kvartal" + } + }, + "timeunit": { + "milliseconds": "Millisekunder", + "seconds": "Sekunder", + "minutes": "Minutter", + "hours": "Timer", + "days": "Dager" + }, + "timewindow": { + "timewindow": "Tidsvindu", + "timewindow-settings": "Innstillinger for tidsvindu", + "years": "{ years, plural, =1 { år } other {# år } }", + "years-short": "{{ years }}å", + "months": "{ months, plural, =1 { måned } other {# måneder } }", + "months-short": "{{ months }}M", + "weeks": "{ weeks, plural, =1 { uke } other {# uker } }", + "weeks-short": "{{ weeks }}u", + "days": "{ days, plural, =1 { dag } other {# dager } }", + "days-short": "{{ days }}d", + "hours": "{ hours, plural, =0 { time } =1 {1 time } other {# timer } }", + "hr": "{{ hr }} t", + "hr-short": "{{ hr }}t", + "minutes": "{ minutes, plural, =0 { minutt } =1 {1 minutt } other {# minutter } }", + "min": "{{ min }} min", + "min-short": "{{ min }}m", + "seconds": "{ seconds, plural, =0 { sekund } =1 {1 sekund } other {# sekunder } }", + "sec": "{{ sec }} sek", + "sec-short": "{{ sec }}s", + "short": { + "years": "{ years, plural, =1 {1 år } other {# år } }", + "days": "{ days, plural, =1 {1 dag } other {# dager } }", + "hours": "{ hours, plural, =1 {1 time } other {# timer } }", + "minutes": "{{minutes}} min", + "seconds": "{{seconds}} sek" + }, + "realtime": "Sanntid", + "history": "Historikk", + "last-prefix": "siste", + "period": "fra {{ startTime }} til {{ endTime }}", + "edit": "Rediger tidsvindu", + "date-range": "Datoperiode", + "for-all-time": "For all tid", + "last": "Siste", + "time-period": "Tidsperiode", + "hide": "Skjul", + "interval": "Intervall", + "just-now": "Akkurat nå", + "just-now-lower": "akkurat nå", + "ago": "siden", + "style": "Tidsvindu stil", + "icon": "Ikon", + "icon-position": "Ikonposisjon", + "icon-position-left": "Venstre", + "icon-position-right": "Høyre", + "font": "Skrifttype", + "color": "Farge", + "displayTypePrefix": "Vis prefiks for sanntid/historikk", + "preview": "Forhåndsvisning", + "relative": "Relativ", + "range": "Område", + "hide-timewindow-section": "Skjul tidsvindu-seksjonen for sluttbrukere", + "hide-last-interval": "Skjul siste intervall for sluttbrukere", + "hide-relative-interval": "Skjul relativt intervall for sluttbrukere", + "hide-fixed-interval": "Skjul fast intervall for sluttbrukere", + "hide-aggregation": "Skjul aggregering for sluttbrukere", + "hide-group-interval": "Skjul grupperingsintervall for sluttbrukere", + "hide-max-values": "Skjul maksverdier for sluttbrukere", + "hide-timezone": "Skjul tidssone for sluttbrukere", + "disable-custom-interval": "Deaktiver egendefinert intervallvalg", + "edit-aggregation-functions-list": "Rediger liste over aggregeringsfunksjoner", + "edit-aggregation-functions-list-hint": "Tilgjengelige alternativer kan spesifiseres.", + "allowed-aggregation-functions": "Tillatte aggregeringsfunksjoner", + "edit-intervals-list": "Rediger liste over intervaller", + "allowed-agg-intervals": "Grupperingsintervaller", + "default-agg-interval": "Standard grupperingsintervall", + "edit-intervals-list-hint": "Liste over tilgjengelige intervallalternativer kan spesifiseres.", + "edit-grouping-intervals-list-hint": "Det er mulig å konfigurere listen over grupperingsintervaller og standardintervall.", + "all": "Alle" + }, + "tooltip": { + "trigger": "Utløser", + "trigger-point": "Punkt", + "trigger-axis": "Akse", + "label": "Etikett", + "value": "Verdi", + "date": "Dato", + "show-date-time-interval": "Vis dato- og tidsintervall", + "show-date-time-interval-hint": "Vis dato- og tidsintervall i henhold til dataaggregeringen.", + "background-color": "Bakgrunnsfarge", + "background-blur": "Bakgrunnsuskarphet" + }, + "unit": { + "millimeter": "Millimeter", + "centimeter": "Centimeter", + "angstrom": "Ångström", + "nanometer": "Nanometer", + "micrometer": "Mikrometer", + "meter": "Meter", + "kilometer": "Kilometer", + "inch": "Tommer", + "foot": "Fot", + "yard": "Yard", + "mile": "Mil", + "nautical-mile": "Nautisk mil", + "astronomical-unit": "Astronomisk enhet", + "reciprocal-metre": "Invers meter", + "meter-per-meter": "Meter per meter", + "steradian": "Steradian", + "thou": "Thou", + "barleycorn": "Byggkorn", + "hand": "Hånd", + "chain": "Kjede", + "furlong": "Furlong", + "league": "League", + "fathom": "Favn", + "cable": "Kabel", + "link": "Lenke", + "rod": "Stang", + "nanogram": "Nanogram", + "microgram": "Mikrogram", + "milligram": "Milligram", + "gram": "Gram", + "kilogram": "Kilogram", + "tonne": "Tonn", + "ounce": "Unse", + "pound": "Pund", + "stone": "Stein", + "hundredweight-count": "Hundreweight (USA)", + "short-tons": "Kort tonn", + "dalton": "Dalton", + "grain": "Korn", + "drachm": "Drachm", + "quarter": "Kvartal", + "slug": "Slug", + "carat": "Karat", + "cubic-millimeter": "Kubikkmillimeter", + "cubic-centimeter": "Kubikkcentimeter", + "cubic-meter": "Kubikkmeter", + "cubic-kilometer": "Kubikkilometer", + "microliter": "Mikroliter", + "milliliter": "Milliliter", + "liter": "Liter", + "hectoliter": "Hektoliter", + "cubic-inch": "Kubikktommer", + "cubic-foot": "Kubikkfot", + "cubic-yard": "Kubikkyard", + "fluid-ounce": "Fluid unse", + "pint": "Pint", + "quart": "Kvart", + "gallon": "Gallon", + "oil-barrels": "Oljetønne", + "cubic-meter-per-kilogram": "Kubikkmeter per kilogram", + "gill": "Gill", + "hogshead": "Hogshead", + "teaspoon": "Teskje", + "tablespoon": "Spiseskje", + "cup": "Kopp", + "celsius": "Celsius", + "kelvin": "Kelvin", + "rankine": "Rankine", + "fahrenheit": "Fahrenheit", + "percent": "Prosent", + "meter-per-second": "Meter per sekund", + "kilometer-per-hour": "Kilometer i timen", + "foot-per-second": "Fot per sekund", + "mile-per-hour": "Mil i timen", + "knot": "Knop", + "millimeters-per-minute": "Millimeter per minutt", + "kilometer-per-hour-squared": "Kilometer i timen i kvadrat", + "foot-per-second-squared": "Fot per sekund i kvadrat", + "pascal": "Pascal", + "kilopascal": "Kilopascal", + "megapascal": "Megapascal", + "gigapascal": "Gigapascal", + "millibar": "Millibar", + "bar": "Bar", + "kilobar": "Kilobar", + "newton": "Newton", + "newton-meter": "Newtonmeter", + "foot-pounds": "Fot-pund", + "inch-pounds": "Tommer-pund", + "newton-per-meter": "Newton per meter", + "atmospheres": "Atmosfærer", + "pounds-per-square-inch": "Pund per kvadrattomme", + "torr": "Torr", + "inches-of-mercury": "Tommer kvikksølv", + "pascal-per-square-meter": "Pascal per kvadratmeter", + "pound-per-square-inch": "Pund per kvadrattomme", + "newton-per-square-meter": "Newton per kvadratmeter", + "kilogram-force-per-square-meter": "Kilogram-kraft per kvadratmeter", + "pascal-per-square-centimeter": "Pascal per kvadratcentimeter", + "ton-force-per-square-inch": "Tonn-kraft per kvadrattomme", + "kilonewton-per-square-meter": "Kilonewton per kvadratmeter", + "newton-per-square-millimeter": "Newton per kvadratmillimeter", + "microjoule": "Mikrojoule", + "millijoule": "Millijoule", + "joule": "Joule", + "kilojoule": "Kilojoule", + "megajoule": "Megajoule", + "gigajoule": "Gigajoule", + "watt-hour": "Wattime", + "kilowatt-hour": "Kilowattime", + "electron-volts": "Elektronvolt", + "joules-per-coulomb": "Joule per Coulomb", + "british-thermal-unit": "Britisk termisk enhet", + "foot-pound": "Fot-pund", + "calorie": "Kalori", + "small-calorie": "Liten kalori", + "kilocalorie": "Kilokalori", + "joule-per-kelvin": "Joule per Kelvin", + "joule-per-kilogram-kelvin": "Joule per kilogram-Kelvin", + "joule-per-kilogram": "Joule per kilogram", + "watt-per-meter-kelvin": "Watt per meter-Kelvin", + "joule-per-cubic-meter": "Joule per kubikkmeter", + "therm": "Therm", + "electric-dipole-moment": "Elektrisk dipolmoment", + "magnetic-dipole-moment": "Magnetisk dipolmoment", + "debye": "Debye", + "coulomb-per-square-meter-per-volt": "Coulomb per kvadratmeter per volt", + "milliwatt": "Milliwatt", + "microwatt": "Mikrowatt", + "watt": "Watt", + "kilowatt": "Kilowatt", + "megawatt": "Megawatt", + "gigawatt": "Gigawatt", + "metric-horsepower": "Metrisk hestekraft", + "milliwatt-per-square-centimeter": "Milliwatt per kvadratcentimeter", + "watt-per-square-centimeter": "Watt per kvadratcentimeter", + "kilowatt-per-square-centimeter": "Kilowatt per kvadratcentimeter", + "milliwatt-per-square-meter": "Milliwatt per kvadratmeter", + "watt-per-square-meter": "Watt per kvadratmeter", + "kilowatt-per-square-meter": "Kilowatt per kvadratmeter", + "watt-per-square-inch": "Watt per kvadrattomme", + "kilowatt-per-square-inch": "Kilowatt per kvadrattomme", + "horsepower": "Hestekraft", + "btu-per-hour": "Britiske termiske enheter/time", + "coulomb": "Coulomb", + "millicoulomb": "Millicoulomb", + "microcoulomb": "Mikrocoulomb", + "picocoulomb": "Picocoulomb", + "coulomb-per-meter": "Coulomb per meter", + "coulomb-per-cubic-meter": "Coulomb per kubikkmeter", + "coulomb-per-square-meter": "Coulomb per kvadratmeter", + "square-millimeter": "Kvadratmillimeter", + "square-centimeter": "Kvadratcentimeter", + "square-meter": "Kvadratmeter", + "hectare": "Hektar", + "square-kilometer": "Kvadratkilometer", + "square-inch": "Kvadrattomme", + "square-foot": "Kvadratfot", + "square-yard": "Kvadratyard", + "acre": "Akre", + "square-mile": "Kvadratmil", + "are": "Ar", + "barn": "Barn", + "circular-inch": "Sirkulær tomme", + "milliampere-hour": "Milliampere-time", + "ampere-hours": "Ampere-timer", + "kiloampere-hours": "Kiloampere-timer", + "nanoampere": "Nanoampere", + "picoampere": "Picoampere", + "microampere": "Mikroampere", + "milliampere": "Milliampere", + "ampere": "Ampere", + "microampere-per-square-centimeter": "Mikroampere per kvadratcentimeter", + "ampere-per-square-meter": "Ampere per kvadratmeter", + "ampere-per-meter": "Ampere per meter", + "oersted": "Oersted", + "bohr-magneton": "Bohr-magneton", + "ampere-meter-squared": "Ampere-meter kvadrert", + "nanovolt": "Nanovolt", + "picovolt": "Picovolt", + "volt": "Volt", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "Volt-meter", + "kilovolt-meter": "Kilovolt-meter", + "megavolt-meter": "Megavolt-meter", + "microvolt-meter": "Mikrovolt-meter", + "millivolt-meter": "Millivolt-meter", + "nanovolt-meter": "Nanovolt-meter", + "ohm": "Ohm", + "microohm": "Mikroohm", + "milliohm": "Milliohm", + "kilohm": "Kiloohm", + "megohm": "Megohm", + "gigohm": "Gigohm", + "hertz": "Hertz", + "kilohertz": "Kilohertz", + "megahertz": "Megahertz", + "gigahertz": "Gigahertz", + "rpm": "Omdreininger per minutt", + "candela-per-square-meter": "Candela per kvadratmeter", + "candela": "Candela", + "lumen": "Lumen", + "lux": "Lux", + "foot-candle": "Fot-candela", + "lumen-per-square-meter": "Lumen per kvadratmeter", + "lux-second": "Lux-sekund", + "lumen-second": "Lumen-sekund", + "lumens-per-watt": "Lumen per watt", + "mole": "Mol", + "nanomole": "Nanomol", + "micromole": "Mikromol", + "millimole": "Millimol", + "kilomole": "Kilomol", + "mole-per-cubic-meter": "Mol per kubikkmeter", + "rssi": "RSSI", + "ppm": "Deler per million", + "ppb": "Deler per milliard", + "micrograms-per-cubic-meter": "Mikrogram per kubikkmeter", + "aqi": "Luftkvalitetsindeks (AQI)", + "gram-per-cubic-meter": "Gram per kubikkmeter", + "gram-per-kilogram": "Spesifikk fuktighet", + "millimeters-per-second": "Millimeter per sekund", + "neper": "Neper", + "bel": "Bel", + "decibel": "Desibel", + "meters-per-second-squared": "Meter per sekund kvadrert", + "becquerel": "Becquerel", + "curie": "Curie", + "gray": "Gray", + "sievert": "Sievert", + "roentgen": "Røntgen", + "cps": "Antall per sekund", + "rad": "Rad", + "rem": "Rem", + "dps": "Desintegrasjoner per sekund", + "rutherford": "Rutherford", + "coulombs-per-kilogram": "Coulomb per kilogram", + "becquerels-per-cubic-meter": "Becquerel per kubikkmeter", + "curies-per-liter": "Curie per liter", + "becquerels-per-second": "Becquerel per sekund", + "curies-per-second": "Curie per sekund", + "gy-per-second": "Gray per sekund", + "watt-per-steradian": "Watt per steradian", + "watt-per-square-metre-steradian": "Watt per kvadratmeter-steradian", + "ph-level": "pH-nivå", + "turbidity": "Turbiditet", + "mg-per-liter": "Milligram per liter", + "microsiemens-per-centimeter": "Mikrosiemens per centimeter", + "millisiemens-per-meter": "Millisimens per meter", + "siemens-per-meter": "Siemens per meter", + "kilogram-per-cubic-meter": "Kilogram per kubikkmeter", + "gram-per-cubic-centimeter": "Gram per kubikkcentimeter", + "kilogram-per-square-meter": "Kilogram per kvadratmeter", + "milligram-per-milliliter": "Milligram per milliliter", + "milligram-per-cubic-meter": "Milligram per kubikkmeter", + "pound-per-cubic-foot": "Pund per kubikkfot", + "ounces-per-cubic-inch": "Unse per kubikktomme", + "tons-per-cubic-yard": "Tonn per kubikkyard", + "particle-density": "Partikkeltetthet", + "kilometers-per-liter": "Kilometer per liter", + "miles-per-gallon": "Miles per gallon", + "liters-per-100-km": "Liter per 100 km", + "gallons-per-mile": "Galloner per mile", + "liters-per-hour": "Liter per time", + "gallons-per-hour": "Galloner per time", + "beats-per-minute": "Slag per minutt", + "millimeters-of-mercury": "Millimeter kvikksølv", + "milligrams-per-deciliter": "Milligram per desiliter", + "g-force": "G-kraft", + "kilonewton": "Kilonewton", + "kilogram-force": "Kilogramkraft", + "pound-force": "Pundkraft", + "kilopound-force": "Kilopundkraft", + "dyne": "Dyne", + "poundal": "Poundal", + "kip": "Kip", + "gal": "Gal", + "gravity": "Gravitasjon", + "hectopascal": "Hektopascal", + "atmosphere": "Atmosfære", + "millibars": "Millibar", + "inch-of-mercury": "Tommer kvikksølv", + "richter-scale": "Richterskala", + "second": "Sekund", + "minute": "Minutt", + "hour": "Time", + "day": "Dag", + "week": "Uke", + "month": "Måned", + "year": "År", + "cubic-foot-per-minute": "Kubikkfot per minutt", + "cubic-meters-per-hour": "Kubikkmeter per time", + "cubic-meters-per-second": "Kubikkmeter per sekund", + "liter-per-second": "Liter per sekund", + "liter-per-minute": "Liter per minutt", + "gallons-per-minute": "Galloner per minutt", + "cubic-foot-per-second": "Kubikkfot per sekund", + "milliliters-per-minute": "Milliliter per minutt", + "bit": "Bit", + "byte": "Byte", + "kilobyte": "Kilobyte", + "megabyte": "Megabyte", + "gigabyte": "Gigabyte", + "terabyte": "Terabyte", + "petabyte": "Petabyte", + "exabyte": "Eksabyte", + "zettabyte": "Zettabyte", + "yottabyte": "Yottabyte", + "bit-per-second": "Bit per sekund", + "kilobit-per-second": "Kilobit per sekund", + "megabit-per-second": "Megabit per sekund", + "gigabit-per-second": "Gigabit per sekund", + "terabit-per-second": "Terabit per sekund", + "byte-per-second": "Byte per sekund", + "kilobyte-per-second": "Kilobyte per sekund", + "megabyte-per-second": "Megabyte per sekund", + "gigabyte-per-second": "Gigabyte per sekund", + "degree": "Grad", + "radian": "Radian", + "gradian": "Gradian", + "revolution": "Revolusjon", + "siemens": "Siemens", + "millisiemens": "Millisiemens", + "microsiemens": "Mikrosiemens", + "kilosiemens": "Kilosiemens", + "megasiemens": "Megasiemens", + "gigasiemens": "Gigasiemens", + "farad": "Farad", + "millifarad": "Millifarad", + "microfarad": "Mikrofarad", + "nanofarad": "Nanofarad", + "picofarad": "Pikofarad", + "kilofarad": "Kilofarad", + "megafarad": "Megafarad", + "gigafarad": "Gigafarad", + "terfarad": "Terfarad", + "farad-per-meter": "Farad per meter", + "tesla": "Tesla", + "gauss": "Gauss", + "kilogauss": "Kilogauss", + "millitesla": "Millitesla", + "microtesla": "Mikrotesla", + "nanotesla": "Nanotesla", + "kilotesla": "Kilotesla", + "megatesla": "Megatesla", + "millitesla-square-meters": "Millitesla kvadratmeter", + "gamma": "Gamma", + "lambda": "Lambda", + "square-meter-per-second": "Kvadratmeter per sekund", + "square-centimeter-per-second": "Kvadratcentimeter per sekund", + "stoke": "Stoke", + "centistokes": "Centistokes", + "square-foot-per-second": "Kvadratfot per sekund", + "square-inch-per-second": "Kvadrattomme per sekund", + "pascal-second": "Pascal-sekund", + "centipoise": "Centipoise", + "poise": "Poise", + "reynolds": "Reynolds", + "pound-per-foot-hour": "Pund per fot-time", + "newton-second-per-square-meter": "Newton-sekund per kvadratmeter", + "dyne-second-per-square-centimeter": "Dyne-sekund per kvadratcentimeter", + "kilogram-per-meter-second": "Kilogram per meter-sekund", + "tesla-square-meters": "Tesla kvadratmeter", + "maxwell": "Maxwell", + "tesla-per-meter": "Tesla per meter", + "gauss-per-centimeter": "Gauss per centimeter", + "weber": "Weber", + "microweber": "Mikroweber", + "milliweber": "Milliweber", + "gauss-square-centimeter": "Gauss-kvadratcentimeter", + "kilogauss-square-centimeter": "Kilogauss-kvadratcentimeter", + "henry": "Henry", + "millihenry": "Millihenry", + "microhenry": "Mikrohenry", + "nanohenry": "Nanohenry", + "henry-per-meter": "Henry per meter", + "tesla-meter-per-ampere": "Tesla meter per ampere", + "gauss-per-oersted": "Gauss per Oersted", + "kilogram-per-mole": "Kilogram per mol", + "gram-per-mole": "Gram per mol", + "milligram-per-mole": "Milligram per mol", + "joule-per-mole": "Joule per mol", + "joule-per-mole-kelvin": "Joule per mol-Kelvin", + "millivolts-per-meter": "Millivolt per meter", + "volts-per-meter": "Volt per meter", + "kilovolts-per-meter": "Kilovolt per meter", + "radian-per-second": "Radian per sekund", + "radian-per-second-squared": "Radian per sekund i kvadrat", + "revolutions-per-minute-per-second": "Vinkelakselerasjon", + "deg-per-second": "grader/s", + "degrees-brix": "Grader Brix", + "katal": "Katal", + "katal-per-cubic-metre": "Katal per kubikkmeter" + }, + "user": { + "user": "Bruker", + "users": "Brukere", + "customer-users": "Kundebrukere", + "tenant-admins": "Leietakeradministratorer", + "sys-admin": "Systemadministrator", + "tenant-admin": "Leietakeradministrator", + "customer": "Kunde", + "anonymous": "Anonym", + "add": "Legg til bruker", + "delete": "Slett bruker", + "add-user-text": "Legg til ny bruker", + "no-users-text": "Ingen brukere funnet", + "user-details": "Brukerdetaljer", + "delete-user-title": "Er du sikker på at du vil slette brukeren '{{userEmail}}'?", + "delete-user-text": "Vær forsiktig, etter bekreftelsen vil brukeren og alle relaterte data bli ugjenopprettelige.", + "delete-users-title": "Er du sikker på at du vil slette { count, plural, =1 {1 bruker} other {# brukere} }?", + "delete-users-action-title": "Slett { count, plural, =1 {1 bruker} other {# brukere} }", + "delete-users-text": "Vær forsiktig, etter bekreftelsen vil alle valgte brukere bli fjernet og alle relaterte data bli ugjenopprettelige.", + "activation-email-sent-message": "Aktiverings-e-post ble sendt!", + "resend-activation": "Send aktivering på nytt", + "email": "E-post", + "email-required": "E-post er påkrevd.", + "invalid-email-format": "Ugyldig e-postformat.", + "first-name": "Fornavn", + "last-name": "Etternavn", + "description": "Beskrivelse", + "default-dashboard": "Standard dashbord", + "always-fullscreen": "Alltid fullskjerm", + "select-user": "Velg bruker", + "no-users-matching": "Ingen brukere som samsvarer med '{{entity}}' ble funnet.", + "user-required": "Bruker er påkrevd", + "activation-method": "Aktiveringsmetode", + "display-activation-link": "Vis aktiveringslenke", + "send-activation-mail": "Send aktiveringsmail", + "activation-link": "Brukeraktiveringslenke", + "activation-link-text": "For å aktivere brukeren, bruk følgende aktiveringslenke (utløper om {{activationLinkTtl}}) :", + "copy-activation-link": "Kopier aktiveringslenke", + "activation-link-copied-message": "Brukerens aktiveringslenke er kopiert til utklippstavlen", + "details": "Detaljer", + "login-as-tenant-admin": "Logg inn som leietakeradministrator", + "login-as-customer-user": "Logg inn som kundebruker", + "search": "Søk brukere", + "selected-users": "{ count, plural, =1 {1 bruker} other {# brukere} } valgt", + "disable-account": "Deaktiver brukerkonto", + "enable-account": "Aktiver brukerkonto", + "enable-account-message": "Brukerkontoen ble aktivert!", + "disable-account-message": "Brukerkontoen ble deaktivert!", + "copyId": "Kopier bruker-ID", + "idCopiedMessage": "Bruker-ID er kopiert til utklippstavlen", + "user-list": "Brukerliste", + "user-list-required": "Brukerliste er påkrevd" + }, + "value": { + "type": "Verditype", + "string": "Streng", + "string-value": "Strengverdi", + "string-value-required": "Strengverdi er påkrevd", + "integer": "Heltall", + "integer-value": "Heltallsverdi", + "integer-value-required": "Heltallsverdi er påkrevd", + "invalid-integer-value": "Ugyldig heltallsverdi", + "double": "Desimaltall", + "double-value": "Desimaltallsverdi", + "double-value-required": "Desimaltallsverdi er påkrevd", + "boolean": "Boolsk", + "boolean-value": "Boolsk verdi", + "false": "Usann", + "true": "Sann", + "long": "Langt heltall", + "json": "JSON", + "json-value": "JSON-verdi", + "json-value-invalid": "JSON-verdi har et ugyldig format", + "json-value-required": "JSON-verdi er påkrevd." + }, + "version-control": { + "version-control": "Versjonskontroll", + "management": "Håndtering av versjonskontroll", + "search": "Søk i versjoner", + "branch": "Gren", + "default": "Standard", + "select-branch": "Velg gren", + "branch-required": "Gren er påkrevd", + "create-entity-version": "Opprett entitetsversjon", + "version-name": "Versjonsnavn", + "version-name-required": "Versjonsnavn er påkrevd", + "author": "Forfatter", + "export-relations": "Eksporter relasjoner", + "export-attributes": "Eksporter attributter", + "export-credentials": "Eksporter legitimasjon", + "export-calculated-fields": "Eksporter beregnede felt", + "entity-versions": "Entitetsversjoner", + "versions": "Versjoner", + "created-time": "Opprettet tidspunkt", + "version-id": "Versjons-ID", + "no-entity-versions-text": "Ingen entitetsversjoner funnet", + "no-versions-text": "Ingen versjoner funnet", + "copy-full-version-id": "Kopier full versjons-ID", + "create-version": "Opprett versjon", + "creating-version": "Oppretter versjon... Vennligst vent", + "nothing-to-commit": "Ingen endringer å forplikte", + "restore-version": "Gjenopprett versjon", + "restore-entity-from-version": "Gjenopprett entitet fra versjon '{{versionName}}'", + "restoring-entity-version": "Gjenoppretter entitetsversjon... Vennligst vent", + "load-relations": "Last inn relasjoner", + "load-attributes": "Last inn attributter", + "load-credentials": "Last inn legitimasjon", + "load-calculated-fields": "Last inn beregnede felt", + "compare-with-current": "Sammenlign med nåværende", + "diff-entity-with-version": "Forskjell med entitetsversjon '{{versionName}}'", + "previous-difference": "Forrige forskjell", + "next-difference": "Neste forskjell", + "current": "Nåværende", + "differences": "{ count, plural, =1 {1 forskjell} other {# forskjeller} }", + "create-entities-version": "Opprett versjon for entiteter", + "default-sync-strategy": "Standard synkroniseringsstrategi", + "sync-strategy-merge": "Flett", + "sync-strategy-overwrite": "Overskriv", + "entities-to-export": "Entiteter å eksportere", + "entities-to-restore": "Entiteter å gjenopprette", + "sync-strategy": "Synkroniseringsstrategi", + "all-entities": "Alle entiteter", + "no-entities-to-export-prompt": "Vennligst spesifiser entiteter å eksportere", + "no-entities-to-restore-prompt": "Vennligst spesifiser entiteter å gjenopprette", + "add-entity-type": "Legg til entitetstype", + "remove-all": "Fjern alle", + "version-create-result": "{ added, plural, =0 {Ingen entiteter} =1 {1 entitet} other {# entiteter} } lagt til.
{ modified, plural, =0 {Ingen entiteter} =1 {1 entitet} other {# entiteter} } endret.
{ removed, plural, =0 {Ingen entiteter} =1 {1 entitet} other {# entiteter} } fjernet.", + "remove-other-entities": "Fjern andre entiteter", + "find-existing-entity-by-name": "Finn eksisterende entitet etter navn", + "restore-entities-from-version": "Gjenopprett entiteter fra versjon '{{versionName}}'", + "restoring-entities-from-version": "Gjenoppretter entiteter... Vennligst vent", + "no-entities-restored": "Ingen entiteter gjenopprettet", + "created": "{{created}} opprettet", + "updated": "{{updated}} oppdatert", + "deleted": "{{deleted}} slettet", + "remove-other-entities-confirm-text": "Vær forsiktig! Dette vil permanent slette alle nåværende entiteter
som ikke finnes i versjonen du ønsker å gjenopprette.

Vennligst skriv \"remove other entities\" for å bekrefte.", + "auto-commit-to-branch": "auto-commit til {{ branch }} gren", + "default-create-entity-version-name": "{{entityName}} oppdatering", + "sync-strategy-merge-hint": "Oppretter eller oppdaterer valgte entiteter i arkivet. Alle andre entiteter i arkivet endres ikke.", + "sync-strategy-overwrite-hint": "Oppretter eller oppdaterer valgte entiteter i arkivet. Alle andre entiteter i arkivet slettet.", + "device-credentials-conflict": "Klarte ikke å laste inn enheten med ekstern ID {{entityId}}
på grunn av at samme legitimasjon allerede finnes i databasen for en annen enhet.
Vurder å deaktivere last inn legitimasjon innstillingen i gjenopprettingsskjemaet.", + "missing-referenced-entity": "Klarte ikke å laste inn {{sourceEntityTypeName}} med ekstern ID {{sourceEntityId}}
fordi den refererer til manglende {{targetEntityTypeName}} med ID {{targetEntityId}}.", + "runtime-failed": "Feilet: {{message}}", + "auto-commit-settings-read-only-hint": "Auto-commit-funksjonen fungerer ikke med skrivebeskyttet alternativ aktivert i arkivinnstillingene.", + "rollback-on-error": "Tilbakestill ved feil", + "rollback-on-error-hint": "Hvis du har et stort antall entiteter å gjenopprette, vurder å deaktivere dette alternativet for å øke ytelsen.\n Merk at hvis en feil oppstår under versjonslasting, vil allerede lagrede entiteter (med relasjoner, attributter osv.) forbli som de er" + }, + "widget": { + "widget-library": "Widget-bibliotek", + "widget-bundle": "Widget-pakke", + "all-bundles": "Alle pakker", + "select-widgets-bundle": "Velg widget-pakke", + "widgets": "Widgeter", + "all-widgets": "Alle widgeter", + "widget": "Widget", + "select-widget": "Velg widget", + "no-widgets-matching": "Ingen widgeter som matcher '{{entity}}' ble funnet.", + "no-widgets": "Ingen widgeter ennå", + "no-widgets-text": "Ingen widgeter funnet", + "management": "Widget-administrasjon", + "editor": "Widget-redigerer", + "confirm-to-exit-editor-html": "Du har ulagrede widget-innstillinger.
Er du sikker på at du vil forlate denne siden?", + "widget-type-not-found": "Problem med å laste widget-konfigurasjon.
Sannsynligvis er den tilknyttede widget-typen fjernet.", + "widget-type-load-error": "Widgeten ble ikke lastet på grunn av følgende feil:", + "remove": "Fjern widget", + "delete": "Slett widget", + "edit": "Rediger widget", + "remove-widget-title": "Er du sikker på at du vil fjerne widgeten '{{widgetTitle}}'?", + "remove-widget-text": "Etter bekreftelse vil widgeten og alle relaterte data bli uopprettelige.", + "replace-reference-with-widget-copy": "Erstatt referanse med widget-kopi", + "timeseries": "Tidsserie", + "search-data": "Søk etter data", + "no-data-found": "Ingen data funnet", + "latest": "Siste verdier", + "rpc": "Kontroll-widget", + "alarm": "Alarm-widget", + "static": "Statisk widget", + "timeseries-short": "serie", + "latest-short": "siste", + "rpc-short": "kontroll", + "alarm-short": "alarm", + "static-short": "statisk", + "select-widget-type": "Velg widget-type", + "missing-widget-title-error": "Widget-tittel må spesifiseres!", + "widget-saved": "Widget lagret", + "unable-to-save-widget-error": "Kan ikke lagre widget! Widget har feil!", + "save": "Lagre widget", + "saveAs": "Lagre widget som", + "move": "Flytt widget", + "save-widget-as": "Lagre widget som", + "save-widget-as-text": "Vennligst skriv inn ny widget-tittel", + "toggle-fullscreen": "Bytt til fullskjerm", + "run": "Kjør widget", + "widget-title": "Widget-tittel", + "title": "Tittel", + "title-required": "Widget-tittel er påkrevd.", + "title-max-length": "Tittelen må være kortere enn 256 tegn", + "system": "System", + "type": "Widget-type", + "resources": "Ressurser", + "resource-url": "JavaScript/CSS-URL", + "resource-is-extension": "Er utvidelse", + "remove-resource": "Fjern ressurs", + "add-resource": "Legg til ressurs", + "html": "HTML", + "tidy": "Rydd opp", + "css": "CSS", + "settings-form": "Innstillingsskjema", + "data-key-settings-form": "Datatastenes innstillingsskjema", + "latest-data-key-settings-form": "Siste datatast-innstillingsskjema", + "widget-settings": "Widget-innstillinger", + "description": "Beskrivelse", + "tags": "Tagger", + "image-preview": "Bildevisning", + "settings-form-selector": "Velger for innstillingsskjema", + "data-key-settings-form-selector": "Velger for datatast-innstillingsskjema", + "latest-data-key-settings-form-selector": "Velger for siste datatast-innstillingsskjema", + "all": "Alle", + "actual": "Faktisk", + "scada": "SCADA-symbol", + "deprecated": "Foreldet", + "has-basic-mode": "Har grunnleggende modus", + "basic-mode-form-selector": "Velger for grunnleggende modus-skjema", + "basic-mode": "Grunnleggende", + "advanced-mode": "Avansert", + "javascript": "JavaScript", + "js": "JS", + "delete-widget-title": "Er du sikker på at du vil slette widgeten '{{widgetName}}'?", + "delete-widget-text": "Etter bekreftelse vil widgeten og alle relaterte data bli uopprettelige.", + "delete-widgets-title": "Er du sikker på at du vil slette { count, plural, =1 {1 widget} other {# widgeter} }?", + "delete-widgets-text": "Vær forsiktig, etter bekreftelse vil alle valgte widgeter bli fjernet og alle relaterte data vil bli uopprettelige.", + "delete-widget": "Slett widget", + "widget-template-load-failed-error": "Kunne ikke laste widget-mal!", + "details": "Detaljer", + "widget-details": "Widget-detaljer", + "add": "Legg til widget", + "add-existing-widget": "Legg til eksisterende widget", + "add-new-widget": "Legg til ny widget", + "search-widgets": "Søk i widgeter", + "selected-widgets": "{ count, plural, =1 {1 widget} other {# widgeter} } valgt", + "undo": "Angre widget-endringer", + "export": "Eksporter widget", + "export-prompt": "Bygg inn widget-bilder og ressurser", + "export-widgets": "Eksporter widgeter", + "export-widgets-prompt": "Bygg inn widget-bilder og ressurser", + "import": "Importer widget", + "no-data": "Ingen data å vise i widget", + "data-overflow": "Widget viser {{count}} av {{total}} enheter", + "alarm-data-overflow": "Widget viser alarmer for {{allowedEntities}} (maksimalt tillatt) enheter av totalt {{totalEntities}} enheter", + "search": "Søk widget", + "filter": "Widget-filtertype", + "loading-widgets": "Laster widgeter...", + "widget-template-error": "Ugyldig HTML-mal for widget.", + "reference": "Referanse" + }, + "widget-action": { + "header-button": "Widget-hodeknapp", + "do-nothing": "Gjør ingenting", + "open-dashboard-state": "Naviger til ny dashbordtilstand", + "update-dashboard-state": "Oppdater gjeldende dashbordtilstand", + "open-dashboard": "Naviger til annet dashbord", + "custom": "Egendefinert handling", + "custom-pretty": "Egendefinert handling (med HTML-mal)", + "custom-pretty-error-title": "Egendefinert dialogfeil", + "custom-pretty-template-error": "Ugyldig egendefinert dialogmal.", + "custom-pretty-controller-error": "Feil oppstod ved evaluering av egendefinert dialogfunksjon.", + "mobile-action": "Mobilhandling", + "target-dashboard-state": "Mål-dashbordtilstand", + "target-dashboard-state-required": "Mål-dashbordtilstand er påkrevd", + "set-entity-from-widget": "Sett enhet fra widget", + "target-dashboard": "Mål-dashbord", + "select-target-dashboard": "Velg mål-dashbord", + "target-dashboard-required": "Mål-dashbord er påkrevd.", + "open-right-layout": "Åpne høyre dashbordoppsett (mobilvisning)", + "state-display-type": "Visningsvalg for dashbordtilstand", + "open-normal": "Normal", + "open-in-separate-dialog": "Åpne i separat dialog", + "open-in-popover": "Åpne i popover", + "dialog-title": "Dialogtittel", + "dialog-hide-dashboard-toolbar": "Skjul dashbordverktøylinje i dialog", + "dialog-width": "Dialogbredde i prosent relativt til visningsbredde", + "dialog-height": "Dialoghøyde i prosent relativt til visningshøyde", + "dialog-size-range-error": "Dialogstørrelsen i prosent må være mellom 1 og 100.", + "popover-preferred-placement": "Foretrukket popover-plassering", + "popover-placement-top": "Topp", + "popover-placement-topLeft": "Øverst til venstre", + "popover-placement-topRight": "Øverst til høyre", + "popover-placement-right": "Høyre", + "popover-placement-rightTop": "Høyre topp", + "popover-placement-rightBottom": "Høyre bunn", + "popover-placement-bottom": "Bunn", + "popover-placement-bottomLeft": "Nederst til venstre", + "popover-placement-bottomRight": "Nederst til høyre", + "popover-placement-left": "Venstre", + "popover-placement-leftTop": "Venstre topp", + "popover-placement-leftBottom": "Venstre bunn", + "popover-hide-on-click-outside": "Skjul popover ved klikk utenfor", + "popover-hide-dashboard-toolbar": "Skjul dashbordverktøylinje i popover", + "popover-width": "Popover-bredde", + "popover-height": "Popover-høyde", + "popover-style": "Popover-stil", + "open-new-browser-tab": "Åpne i ny nettleserfane", + "open-URL": "Åpne URL", + "URL": "URL", + "url-required": "URL er påkrevd.", + "mobile": { + "device-provision": "Enhetsprovisjon", + "action-type": "Mobilhandlingstype", + "select-action-type": "Velg mobilhandlingstype", + "action-type-required": "Mobilhandlingstype er påkrevd", + "take-picture-from-gallery": "Velg bilde fra galleri", + "take-photo": "Ta bilde", + "map-direction": "Åpne kartveibeskrivelse", + "map-location": "Åpne kartplassering", + "scan-qr-code": "Skann QR-kode", + "make-phone-call": "Ring telefonsamtale", + "get-location": "Hent telefonens posisjon", + "take-screenshot": "Ta skjermbilde" + }, + "custom-action-function": "Egendefinert handlingsfunksjon", + "custom-pretty-function": "Egendefinert handling (med HTML-mal) funksjon", + "map-item-type": "Kartobjekttype", + "map-item": { + "marker": "Markør", + "polygon": "Polygon", + "rectangle": "Rektangel", + "circle": "Sirkel" + }, + "place-map-item": "Plasser kartobjekt", + "map-item-tooltip": { + "customize-map-item-tooltips": "Tilpass kartobjektverktøytips", + "place-marker": "Plasser markør", + "start-draw-rectangle": "Start å tegne rektangel", + "finish-draw-rectangle": "Fullfør tegning av rektangel", + "start-draw-polygon": "Start å tegne polygon", + "continue-draw-polygon": "Fortsett å tegne polygon", + "finish-draw-polygon": "Fullfør tegning av polygon", + "start-draw-circle": "Start å tegne sirkel", + "finish-draw-circle": "Fullfør tegning av sirkel" + } + }, + "widgets-bundle": { + "current": "Gjeldende pakke", + "widgets-bundles": "Widgetpakker", + "widgets-bundle-widgets": "Widgets i pakken", + "add": "Legg til widgetpakke", + "delete": "Slett widgetpakke", + "title": "Tittel", + "title-required": "Tittel er påkrevd.", + "title-max-length": "Tittelen må være kortere enn 256 tegn", + "description": "Beskrivelse", + "image-preview": "Bildeeksempel", + "scada": "SCADA-widgetpakke", + "order": "Rekkefølge", + "add-widgets-bundle-text": "Legg til ny widgetpakke", + "no-widgets-bundles-text": "Ingen widgetpakker funnet", + "empty": "Widgetpakken er tom", + "details": "Detaljer", + "widgets-bundle-details": "Widgetpakkedetaljer", + "delete-widgets-bundle-title": "Er du sikker på at du vil slette widgetpakken '{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text": "Vær forsiktig, etter bekreftelse vil widgetpakken og alle relaterte data bli permanent slettet.", + "delete-widgets-bundles-title": "Er du sikker på at du vil slette { count, plural, =1 {1 widgetpakke} other {# widgetpakker} }?", + "delete-widgets-bundles-action-title": "Slett { count, plural, =1 {1 widgetpakke} other {# widgetpakker} }", + "delete-widgets-bundles-text": "Vær forsiktig, etter bekreftelse vil alle valgte widgetpakker og tilhørende data bli permanent slettet.", + "no-widgets-bundles-matching": "Ingen widgetpakker som samsvarer med '{{widgetsBundle}}' ble funnet.", + "widgets-bundle-required": "Widgetpakke er påkrevd.", + "system": "System", + "import": "Importer widgetpakke", + "export": "Eksporter widgetpakke", + "export-widgets-bundle-widgets-prompt": "Inkluder widgets i pakken i eksporterte data (ellers eksporteres kun refererte widget-FQN-er)", + "export-failed-error": "Kan ikke eksportere widgetpakke: {{error}}", + "create-new-widgets-bundle": "Opprett ny widgetpakke", + "widgets-bundle-file": "Widgetpakkefil", + "invalid-widgets-bundle-file-error": "Kan ikke importere widgetpakke: Ugyldig datastruktur.", + "search": "Søk i widgetpakker", + "selected-widgets-bundles": "{ count, plural, =1 {1 widgetpakke} other {# widgetpakker} } valgt", + "open-widgets-bundle": "Åpne widgetpakke", + "loading-widgets-bundles": "Laster widgetpakker...", + "create-new": "Opprett ny widgetpakke" + }, + "widget-config": { + "data": "Data", + "settings": "Innstillinger", + "advanced": "Avansert", + "appearance": "Utseende", + "widget-card": "Widget-kort", + "mobile": "Mobil", + "title": "Tittel", + "title-tooltip": "Tittelverktøytips", + "general-settings": "Generelle innstillinger", + "display-title": "Vis widget-tittel", + "card-title": "Korttittel", + "drop-shadow": "Skyggeeffekt", + "enable-fullscreen": "Aktiver fullskjerm", + "background-color": "Bakgrunnsfarge", + "text-color": "Tekstfarge", + "border-radius": "Hjørneavrunding", + "padding": "Polstring", + "margin": "Marg", + "widget-style": "Widget-stil", + "widget-css": "Widget CSS", + "title-style": "Tittelstil", + "mobile-mode-settings": "Mobilmodus", + "order": "Rekkefølge", + "height": "Høyde", + "mobile-hide": "Skjul widget i mobilmodus", + "desktop-hide": "Skjul widget i skrivebordsmodus", + "units": "Spesialsymbol for visning ved siden av verdi", + "units-by-default": "Enheter som standard", + "decimals": "Antall sifre etter desimaltegn", + "decimals-by-default": "Desimaler som standard", + "default-data-key-parameter-hint": "Denne parameteren gjelder for alle widget-verdier med mindre den overstyres av datanøkkelkonfigurasjon", + "units-short": "Enheter", + "decimals-short": "Desimaler", + "decimals-suffix": "desimaler", + "digits-suffix": "sifre", + "timewindow": "Tidsvindu", + "use-dashboard-timewindow": "Bruk dashbordets tidsvindu", + "use-widget-timewindow": "Bruk widgetens tidsvindu", + "display-timewindow": "Vis tidsvindu", + "legend": "Forklaring", + "display-legend": "Vis forklaring", + "datasources": "Datakilder", + "datasource": "Datakilde", + "maximum-datasources": "Maksimalt { count, plural, =1 {1 datakilde er tillatt.} other {# datakilder er tillatt} }", + "timeseries-key-error": "Minst én tidsseriedatanøkkel må spesifiseres", + "datasource-type": "Type", + "datasource-parameters": "Parametere", + "remove-datasource": "Fjern datakilde", + "add-datasource": "Legg til datakilde", + "target-device": "Målenhet", + "alarm-source": "Alarmkilde", + "actions": "Handlinger", + "action": "Handling", + "add-action": "Legg til handling", + "search-actions": "Søk i handlinger", + "no-actions-text": "Ingen handlinger funnet", + "action-source": "Handlingskilde", + "select-action-source": "Velg handlingskilde", + "action-source-required": "Handlingskilde er påkrevd.", + "column-index": "Kolonneindeks", + "select-column-index": "Velg kolonneindeks", + "column-index-required": "Kolonneindeks er påkrevd.", + "not-set": "Ikke angitt", + "action-name": "Navn", + "action-name-required": "Handlingsnavn er påkrevd.", + "action-name-not-unique": "En annen handling med samme navn eksisterer allerede.\nHandlingsnavn må være unikt innen samme handlingskilde.", + "action-icon": "Ikon", + "header-button": { + "button-settings": "Knappinnstillinger", + "button-type": "Knapptype", + "button-type-basic": "Grunnleggende", + "button-type-raised": "Hevet", + "button-type-stroked": "Streket", + "button-type-flat": "Flat", + "button-type-icon": "Ikon", + "button-type-mini-fab": "FAB", + "colors": "Farger", + "color": "Farge", + "background": "Bakgrunn", + "border": "Kantlinje", + "advanced-button-style": "Avansert knappestil", + "button-style": "Knappestil" + }, + "show-hide-action-using-function": "Vis/skjul handling ved bruk av funksjon", + "show-action-function": "Vis handlingsfunksjon", + "action-type": "Type", + "action-type-required": "Handlingstype er påkrevd.", + "edit-action": "Rediger handling", + "delete-action": "Slett handling", + "delete-action-title": "Slett widget-handling", + "delete-action-text": "Er du sikker på at du vil slette widget-handlingen med navnet '{{actionName}}'?", + "title-icon": "Tittelikon", + "display-icon": "Vis tittelikon", + "card-icon": "Kortikon", + "icon": "Ikon", + "icon-color": "Ikonfarge", + "icon-size": "Ikonstørrelse", + "advanced-settings": "Avanserte innstillinger", + "data-settings": "Datainnstillinger", + "limits": "Begrensninger", + "no-data-display-message": "Alternativ melding for \"Ingen data å vise\"", + "data-page-size": "Maksimalt antall enheter per datakilde", + "settings-component-not-found": "Innstillingsskjema-komponent ikke funnet for velger '{{selector}}'", + "preview": "Forhåndsvisning", + "set": "Sett", + "set-message": "Angi melding", + "advanced-title-style": "Avansert titteldesign", + "card-style": "Kortdesign", + "text": "Tekst", + "background": "Bakgrunn", + "advanced-widget-style": "Avansert widget-stil", + "card-buttons": "Kortknapper", + "show-card-buttons": "Vis kortknapper", + "card-border-radius": "Hjørneavrunding for kort", + "card-padding": "Polstring for kort", + "card-appearance": "Kortutseende", + "color": "Farge", + "tooltip": "Verktøytips", + "units-required": "Enhet er påkrevd.", + "list-layout": "Listeoppsett", + "layout": "Oppsett", + "resize-options": "Alternativer for størrelseendring", + "resizable": "Justerbar", + "preserve-aspect-ratio": "Bevar størrelsesforhold" + }, + "widget-type": { + "import": "Importer widgettype", + "export": "Eksporter widgettype", + "export-failed-error": "Kan ikke eksportere widget: {{error}}", + "widget-file": "Widgetfil", + "invalid-widget-file-error": "Kan ikke importere widget: Ugyldig widgetdatastruktur." + }, + "markdown": { + "edit": "Rediger", + "preview": "Forhåndsvis", + "copy-code": "Klikk for å kopiere", + "copied": "Kopiert!" + }, + "widgets": { + "mobile-app-qr-code": { + "configuration-hint": "Konfigurasjonen avhenger av widgeten for QR-kode for mobilapp i plattformens hovedinnstillinger", + "get-it-on-google-play": "Få den på Google Play", + "download-on-the-app-store": "Last ned på App Store" + }, + "action-button": { + "behavior": "Oppførsel", + "on-click": "Ved klikk", + "on-click-hint": "Handling utløst når knappen klikkes", + "first-button-click": "Første knappetrykk", + "first-button-click-hint": "Handling ved trykk på første knapp.", + "second-button-click": "Andre knappetrykk", + "second-button-click-hint": "Handling ved trykk på andre knapp.", + "button-click-hint": "Handling ved trykk på widget." + }, + "command-button": { + "behavior": "Oppførsel", + "on-click": "Ved klikk", + "on-click-hint": "Handling utført når knappen klikkes." + }, + "power-button": { + "behavior": "Oppførsel", + "power-on": "Strøm 'På'", + "power-on-hint": "Handling for å slå PÅ komponenten.", + "power-off": "Strøm 'Av'", + "power-off-hint": "Handling for å slå AV komponenten.", + "on-label": "På", + "off-label": "Av", + "layout": "Oppsett", + "layout-default": "Standard", + "layout-simplified": "Forenklet", + "layout-outlined": "Med ramme", + "layout-default-volume": "Standard.Volum", + "layout-simplified-volume": "Forenklet.Volum", + "layout-outlined-volume": "Med ramme.Volum", + "layout-default-icon": "Standard.Ikon", + "layout-simplified-icon": "Forenklet.Ikon", + "layout-outlined-icon": "Med ramme.Ikon", + "main": "Hoved", + "background": "Bakgrunn", + "button-icon-on": "Knappikon 'På'", + "button-icon-off": "Knappikon 'Av'", + "power-on-colors": "Farger for 'På'", + "power-off-colors": "Farger for 'Av'", + "disabled-colors": "Deaktiverte farger", + "button": "Knapp" + }, + "toggle-button": { + "behavior": "Oppførsel", + "checked": "Avkrysset", + "unchecked": "Ikke avkrysset", + "check": "Kryss av", + "check-hint": "Handling for å krysse av komponenten.", + "uncheck": "Fjern avkrysning", + "uncheck-hint": "Handling for å fjerne avkrysning fra komponenten.", + "auto-scale": "Automatisk skalering", + "horizontal-fill": "Horisontal fylling", + "vertical-fill": "Vertikal fylling", + "button-appearance": "Knapputseende" + }, + "segmented-button": { + "layout": "Oppsett", + "layout-squared": "Firkantet", + "layout-rounded": "Avrundet", + "card-border": "Kortkant", + "button-appearance": "Knapputseende", + "first": "Første", + "second": "Andre", + "color-styles": "Fargestiler", + "selected": "Valgt", + "unselected": "Ikke valgt" + }, + "button": { + "layout": "Oppsett", + "outlined": "Med ramme", + "filled": "Fylt", + "underlined": "Understreket", + "basic": "Grunnleggende", + "auto-scale": "Automatisk skalering", + "label": "Etikett", + "icon": "Ikon", + "border-radius": "Kantavrunding", + "color-palette": "Fargepalett", + "main": "Hoved", + "background": "Bakgrunn", + "border": "Kant", + "custom-styles": "Egendefinerte stiler", + "clear-style": "Fjern stil", + "shadow": "Skygge", + "enabled": "Aktivert", + "disabled": "Deaktivert", + "preview": "Forhåndsvisning", + "copy-style-from": "Kopier stil fra" + }, + "value-stepper": { + "behavior": "Oppførsel", + "simplified": "Forenklet", + "filled": "Fylt", + "outlined": "Med ramme", + "volume": "Volum", + "initial-state": "Starttilstand", + "initial-state-hint": "Handling for å hente startverdi.", + "disabled-state": "Deaktivert tilstand", + "disabled-state-hint": "Konfigurer betingelse for når komponenten er deaktivert.", + "right-button-click": "Høyre knappetrykk", + "right-button-click-hint": "Handling ved trykk på høyre knapp.", + "left-button-click": "Venstre knappetrykk", + "left-button-click-hint": "Handling ved trykk på venstre knapp.", + "auto-scale": "Automatisk skalering", + "value-range": "Område", + "min-range": "Min", + "max-range": "Maks", + "value-increment-decrement-step": "Trinn for verdiøkning/-reduksjon", + "value": "Verdi", + "value-box-background": "Bakgrunn for verdiboks", + "border": "Kant", + "button-appearance": "Knapputseende", + "left": "Venstre", + "right": "Høyre", + "left-button": "Venstre knapp", + "right-button": "Høyre knapp", + "icon": "Ikon", + "color-palette": "Fargepalett", + "main": "Hoved", + "background": "Bakgrunn", + "button-icon-on": "Knappikon 'På'", + "button-on-colors": "Farger for 'På'", + "disabled-colors": "Farger for deaktivert" + }, + "button-state": { + "activated-state": "Aktivert tilstand", + "activated-state-hint": "Konfigurer betingelse for når knappen er aktiv.", + "disabled-state": "Deaktivert tilstand", + "disabled-state-hint": "Konfigurer betingelse for når knappen er deaktivert.", + "selected-state": "Valgt knapp", + "selected-state-hint": "Konfigurer betingelse for når knappen er valgt.", + "enabled": "Aktivert", + "hovered": "Holdt over", + "pressed": "Trykket", + "activated": "Aktivert", + "disabled": "Deaktivert", + "initial": "Første knapp", + "first": "Første", + "second": "Andre" + }, + "background": { + "background": "Bakgrunn", + "background-settings": "Bakgrunnsinnstillinger", + "background-type-image": "Bilde", + "background-type-color": "Farge", + "image-url": "Bilde-URL", + "overlay": "Overlegg", + "enable-overlay": "Aktiver overlegg", + "blur": "Uklarhet", + "preview": "Forhåndsvisning" + }, + "bar-chart": { + "bar-appearance": "Stolpeutseende", + "label-on-bar": "Etikett på stolpe", + "value-on-bar": "Verdi på stolpe", + "bar-chart-style": "Stolpediagramstil", + "bar-axis": "Stolpeakse" + }, + "polar-area-chart": { + "polar-axis": "Polarakse", + "start-angle": "Startvinkel", + "polar-area-chart-style": "Polardiagramstil" + }, + "battery-level": { + "layout": "Oppsett", + "layout-vertical-solid": "Vertikal. Hel", + "layout-horizontal-solid": "Horisontal. Hel", + "layout-vertical-divided": "Vertikal. Delt", + "layout-horizontal-divided": "Horisontal. Delt", + "icon": "Ikon", + "value": "Verdi", + "auto-scale": "Automatisk skalering", + "battery-level-color": "Batterinivåfarge", + "battery-shape-color": "Batteriformfarge", + "battery-level-card-style": "Stil for batterinivåkort", + "sections-count": "Antall seksjoner" + }, + "signal-strength": { + "value": "Verdi", + "last-update": "Siste oppdatering", + "no-signal": "Ingen signal", + "layout": "Oppsett", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "Mobilstolpe", + "icon": "Ikon", + "date": "Dato", + "active-bars-color": "Farge for aktive signalstolper", + "inactive-bars-color": "Farge for inaktive signalstolper", + "signal-strength-card-style": "Stil for signalstyrkekort", + "no-signal-rssi-value": "\"Ingen signal\" rssi-verdi" + }, + "status-widget": { + "behavior": "Oppførsel", + "layout": "Oppsett", + "layout-default": "Standard", + "layout-center": "Sentrert", + "layout-icon": "Ikon", + "on": "På", + "off": "Av", + "label": "Etikett", + "status": "Status", + "icon": "Ikon", + "color-palette": "Fargepalett", + "disabled-color-palette": "Fargepalett for deaktivert", + "primary": "Primær", + "primary-color-hint": "Farge på ikon og etikett", + "secondary": "Sekundær", + "secondary-color-hint": "Farge på status", + "background": "Bakgrunn" + }, + "chart": { + "common-settings": "Felles innstillinger", + "enable-stacking-mode": "Aktiver stablemodus", + "selection": "Tidsområdevalg", + "enable-selection-mode": "Aktiver valgmodus", + "line-shadow-size": "Skyggestørrelse for linje", + "display-smooth-lines": "Vis glatte (buede) linjer", + "default-bar-width": "Standard stolpebredde for ikke-aggregerte data (millisekunder)", + "bar-alignment": "Justering av stolper", + "bar-alignment-left": "Venstre", + "bar-alignment-right": "Høyre", + "bar-alignment-center": "Senter", + "default-font": "Standard skrift", + "default-font-size": "Standard skriftstørrelse", + "default-font-color": "Standard skriftfarge", + "thresholds-line-width": "Standard linjebredde for alle terskler", + "tooltip-settings": "Verktøytipsinnstillinger", + "tooltip": "Verktøytips", + "show-tooltip": "Vis verktøytips", + "hover-individual-points": "Hold over individuelle punkter", + "show-cumulative-values": "Vis kumulative verdier i stablemodus", + "hide-zero-false-values": "Skjul null-/false-verdier i verktøytips", + "tooltip-value-format-function": "Funksjon for format av verktøytipsverdi", + "grid-settings": "Rutenettinnstillinger", + "show-vertical-lines": "Vis vertikale linjer", + "show-horizontal-lines": "Vis horisontale linjer", + "grid-outline-border-width": "Rutenettramme-/kantbredde (px)", + "primary-color": "Primærfarge", + "background-color": "Bakgrunnsfarge", + "ticks-color": "Farge på aksemarkeringer", + "xaxis-settings": "Innstillinger for X-akse", + "axis-title": "Aksetittel", + "xaxis-tick-labels-settings": "Innstillinger for X-akseetiketter", + "show-tick-labels": "Vis akseetiketter", + "yaxis-settings": "Innstillinger for Y-akse", + "min-scale-value": "Minimumsverdi på skala", + "max-scale-value": "Maksimumsverdi på skala", + "yaxis-tick-labels-settings": "Innstillinger for Y-akseetiketter", + "tick-step-size": "Trinnstørrelse mellom markeringer", + "number-of-decimals": "Antall desimaler som vises", + "ticks-formatter-function": "Formatteringsfunksjon for markeringer", + "comparison-settings": "Sammenligningsinnstillinger", + "enable-comparison": "Aktiver sammenligning", + "time-for-comparison": "Sammenligningsperiode", + "time-for-comparison-previous-interval": "Forrige intervall (standard)", + "time-for-comparison-days": "Dager siden", + "time-for-comparison-weeks": "Uker siden", + "time-for-comparison-months": "Måneder siden", + "time-for-comparison-years": "År siden", + "time-for-comparison-custom-interval": "Egendefinert intervall", + "custom-interval-value": "Egendefinert intervallverdi (ms)", + "comparison-x-axis-settings": "Innstillinger for sammenlignings-X-akse", + "axis-position": "Akseposisjon", + "axis-position-top": "Topp (standard)", + "axis-position-bottom": "Bunn", + "custom-legend-settings": "Egendefinerte tegnforklaringsinnstillinger", + "enable-custom-legend": "Aktiver egendefinert tegnforklaring (lar deg bruke attributt-/tidsseriedata i nøkkelnavn)", + "key-name": "Nøkkelnavn", + "key-name-required": "Nøkkelnavn er påkrevd", + "key-type": "Nøkkeltype", + "key-type-attribute": "Attributt", + "key-type-timeseries": "Tidsserie", + "label-keys-list": "Nøkkelliste brukt i etiketter", + "no-label-keys": "Ingen nøkkel er konfigurert", + "add-label-key": "Legg til ny nøkkel", + "line-width": "Linjebredde", + "color": "Farge", + "data-is-hidden-by-default": "Data er skjult som standard", + "disable-data-hiding": "Deaktiver dataskjuling", + "remove-from-legend": "Fjern datanøkkel fra tegnforklaring", + "exclude-from-stacking": "Ekskluder fra stabling (tilgjengelig i \"Stabling\"-modus)", + "line-settings": "Linjens innstillinger", + "show-line": "Vis linje", + "fill-line": "Fyll linje", + "fill-line-opacity": "Fyll opasitet", + "points-settings": "Punktinnstillinger", + "show-points": "Vis punkter", + "points-line-width": "Linjebredde for punkter", + "points-radius": "Radius for punkter", + "point-shape": "Punktform", + "point-shape-circle": "Sirkel", + "point-shape-cross": "Kryss", + "point-shape-diamond": "Diamant", + "point-shape-square": "Firkant", + "point-shape-triangle": "Trekant", + "point-shape-custom": "Egendefinert funksjon", + "point-shape-draw-function": "Tegnefunksjon for punktform", + "show-separate-axis": "Vis separat akse", + "axis-position-left": "Venstre", + "axis-position-right": "Høyre", + "thresholds": "Terskler", + "no-thresholds": "Ingen terskler konfigurert", + "add-threshold": "Legg til terskel", + "show-values-for-comparison": "Vis historiske verdier for sammenligning", + "comparison-values-label": "Etikett for historiske verdier", + "comparison-line-color": "Farge på sammenligningslinje", + "threshold-settings": "Terskelinnstillinger", + "use-as-threshold": "Bruk nøkkelverdi som terskel", + "threshold-line-width": "Linjebredde for terskel", + "threshold-color": "Terskelfarge", + "common-pie-settings": "Felles sektordiagraminnstillinger", + "radius": "Radius", + "inner-radius": "Indre radius", + "tilt": "Helling", + "common-pie-settings-range-error": "Verdien må være mellom 0 og 1", + "stroke-settings": "Strekinnstillinger", + "width-pixels": "Bredde (piksler)", + "show-labels": "Vis etiketter", + "animation-settings": "Animasjonsinnstillinger", + "animated-pie": "Aktiver sektordiagramanimasjon (eksperimentell)", + "border-settings": "Kantinnstillinger", + "border-width": "Kantbredde", + "border-color": "Kantfarge", + "legend-settings": "Tegnforklaringsinnstillinger", + "display-legend": "Vis tegnforklaring", + "labels-font-color": "Skriftfarge for etiketter", + "series": "Serier", + "add-series": "Legg til serie", + "series-settings": "Serieinnstillinger", + "remove-series": "Fjern serie", + "no-series": "Ingen serier konfigurert", + "no-series-error": "Minst én serie må angis", + "chart-appearance": "Diagramutseende", + "vertical-grid-lines": "Vertikale rutenettlinjer", + "horizontal-grid-lines": "Horisontale rutenettlinjer", + "chart-background": "Diagrambakgrunn", + "grid-lines-color": "Farge på rutenettlinjer", + "border": "Kant", + "axis": "Akse", + "vertical-axis": "Vertikal akse", + "ticks": "Aksemarkeringer", + "horizontal-axis": "Horisontal akse", + "shape-empty-circle": "Tom sirkel", + "shape-circle": "Sirkel", + "shape-rect": "Rektangel", + "shape-round-rect": "Avrundet rektangel", + "shape-triangle": "Trekant", + "shape-diamond": "Diamant", + "shape-pin": "Stift", + "shape-arrow": "Pil", + "shape-none": "Ingen", + "line-type-solid": "Heltrukken", + "line-type-dashed": "Stiplet", + "line-type-dotted": "Prikket", + "label-position-top": "Topp", + "label-position-bottom": "Bunn", + "label-position-outside": "Utenfor", + "label-position-inside": "Innenfor", + "fill": "Fyll", + "fill-type-none": "Ingen", + "fill-type-solid": "Heltrukken", + "fill-type-opacity": "Gjennomsiktighet", + "fill-type-gradient": "Gradient", + "background": "Bakgrunn", + "opacity": "Gjennomsiktighet", + "gradient-stops": "Gradientstopp", + "gradient-start": "start", + "gradient-end": "slutt", + "animation": { + "animation": "Animasjon", + "animation-threshold": "Animasjonsterskel", + "animation-duration": "Animasjonsvarighet", + "animation-easing": "Animasjonsovergang", + "animation-delay": "Animasjonsforsinkelse", + "update-animation-duration": "Oppdater animasjonsvarighet", + "update-animation-easing": "Oppdater animasjonsovergang", + "update-animation-delay": "Oppdater animasjonsforsinkelse" + }, + "chart-axis": { + "scale": "Skala", + "scale-min": "min", + "scale-max": "maks", + "scale-auto": "Auto" + }, + "bar": { + "show-border": "Vis kant", + "border-width": "Kantbredde", + "border-radius": "Kantavrunding", + "bar-width": "Stolpebredde", + "label": "Etikett", + "label-hint": "Vis etikett over stolpen.", + "series-label-hint": "Vis etikett med verdi over stolpen.", + "label-background": "Etikettbakgrunn" + } + }, + "color": { + "color-settings": "Fargeinnstillinger", + "color-type-constant": "Konstant", + "color-type-gradient": "Gradient", + "color-type-range": "Område", + "color-type-function": "Funksjon", + "color": "Farge", + "value-range": "Verdiområde", + "from": "Fra", + "to": "Til", + "color-function": "Fargefunksjon", + "copy-color-settings-from": "Kopier fargeinnstillinger fra", + "copy-from": "Kopier fra", + "settings-type": "Innstillingstype", + "basic-mode": "Grunnleggende", + "advanced-mode": "Avansert", + "entity-alias": "Enhetsalias", + "entity-attribute": "Enhetsegenskap", + "gradient-color": "Gradientfarge", + "gradient-color-min": "Farge", + "gradient-start": "Startfarge for gradient", + "gradient-start-min": "Start", + "gradient-end": "Sluttfarge for gradient", + "gradient-end-min": "Slutt", + "start-value": "Startverdi", + "end-value": "Sluttverdi", + "gradient-type": "Gradienttype" + }, + "dashboard-state": { + "dashboard-state-settings": "Innstillinger for dashbordtilstand", + "dashboard-state": "Dashbordtilstands-ID", + "autofill-state-layout": "Fyll automatisk høyde for tilstandsoppsett", + "default-margin": "Standard marg for widgeter", + "default-background-color": "Standard bakgrunnsfarge", + "sync-parent-state-params": "Synkroniser tilstandsparametere med overordnet dashbord" + }, + "date-range-navigator": { + "date-range-picker-settings": "Innstillinger for datoperiodevelger", + "hide-date-range-picker": "Skjul datoperiodevelger", + "picker-one-panel": "Datoperiodevelger med ett panel", + "picker-auto-confirm": "Automatisk bekreftelse i datoperiodevelger", + "picker-show-template": "Vis mal for datoperiodevelger", + "first-day-of-week": "Første ukedag", + "interval-settings": "Innstillinger for intervall", + "hide-interval": "Skjul intervall", + "initial-interval": "Startintervall", + "interval-hour": "Time", + "interval-day": "Dag", + "interval-week": "Uke", + "interval-two-weeks": "2 uker", + "interval-month": "Måned", + "interval-three-months": "3 måneder", + "interval-six-months": "6 måneder", + "step-settings": "Innstillinger for trinn", + "hide-step-size": "Skjul trinnstørrelse", + "initial-step-size": "Startverdi for trinnstørrelse", + "hide-labels": "Skjul etiketter", + "use-session-storage": "Bruk sesjonslagring", + "localizationMap": { + "Sun": "Søn", + "Mon": "Man", + "Tue": "Tir", + "Wed": "Ons", + "Thu": "Tor", + "Fri": "Fre", + "Sat": "Lør", + "Jan": "Jan", + "Feb": "Feb", + "Mar": "Mar", + "Apr": "Apr", + "May": "Mai", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Aug", + "Sep": "Sep", + "Oct": "Okt", + "Nov": "Nov", + "Dec": "Des", + "January": "Januar", + "February": "Februar", + "March": "Mars", + "April": "April", + "June": "Juni", + "July": "Juli", + "August": "August", + "September": "September", + "October": "Oktober", + "November": "November", + "December": "Desember", + "Custom Date Range": "Egendefinert datoperiode", + "Date Range Template": "Mal for datoperiode", + "Today": "I dag", + "Yesterday": "I går", + "This Week": "Denne uken", + "Last Week": "Forrige uke", + "This Month": "Denne måneden", + "Last Month": "Forrige måned", + "Year": "År", + "This Year": "Dette året", + "Last Year": "Forrige år", + "Date picker": "Datovelger", + "Hour": "Time", + "Day": "Dag", + "Week": "Uke", + "2 weeks": "2 uker", + "Month": "Måned", + "3 months": "3 måneder", + "6 months": "6 måneder", + "Custom interval": "Egendefinert intervall", + "Interval": "Intervall", + "Step size": "Trinnstørrelse", + "Ok": "OK" + } + }, + "doughnut": { + "doughnut-appearance": "Smultring-diagram utseende", + "layout": "Layout", + "layout-default": "Standard", + "layout-with-total": "Med total", + "central-total-value": "Sentral totalverdi", + "doughnut-card-style": "Stilkort for smultring-diagram" + }, + "entities-hierarchy": { + "hierarchy-data-settings": "Innstillinger for hierarkidata", + "relations-query-function": "Relasjonsspørringsfunksjon for node", + "has-children-function": "Funksjon for å sjekke om node har barn", + "node-state-settings": "Innstillinger for nodetilstand", + "node-opened-function": "Funksjon for åpnet node som standard", + "node-disabled-function": "Funksjon for deaktivert node", + "display-settings": "Visningsinnstillinger", + "node-icon-function": "Funksjon for nodeikon", + "node-text-function": "Funksjon for nodetekst", + "sort-settings": "Sorteringsinnstillinger", + "nodes-sort-function": "Funksjon for sortering av noder" + }, + "edge": { + "display-default-title": "Vis standard tittel" + }, + "gateway": { + "general-settings": "Generelle innstillinger", + "widget-title": "Widget-tittel", + "default-archive-file-name": "Standard filnavn for arkiv", + "device-type-for-new-gateway": "Enhetstype for ny gateway", + "messages-settings": "Innstillinger for meldinger", + "save-config-success-message": "Meldingstekst om at gateway-konfigurasjonen ble lagret", + "device-name-exists-message": "Meldingstekst når en enhet med angitt navn allerede finnes", + "gateway-title": "Gateway-skjema", + "read-only": "Kun lesbar", + "events-title": "Tittel for gateway-hendelser", + "events-filter": "Filter for hendelser", + "event-key-contains": "Hendelsesnøkkel inneholder...", + "show-connector": "Vis for tilkoblingen", + "connector-state-param-key": "Parameter for tilkoblingstilstand", + "message": "Melding", + "level": "Nivå", + "created-time": "Opprettet tidspunkt" + }, + "gauge": { + "default-color": "Standardfarge", + "radial-gauge-settings": "Innstillinger for radialmåler", + "ticks-settings": "Innstillinger for inndelinger", + "min-value": "Minimumsverdi", + "max-value": "Maksimumsverdi", + "min-value-short": "min", + "max-value-short": "maks", + "start-ticks-angle": "Startvinkel for inndelinger", + "ticks-angle": "Vinkel for inndelinger", + "major-ticks": "Hovedinndelinger", + "major-ticks-count": "Antall hovedinndelinger", + "major-ticks-color": "Farge for hovedinndelinger", + "minor-ticks": "Mellominndelinger", + "minor-ticks-count": "Antall mellominndelinger", + "minor-ticks-color": "Farge for mellominndelinger", + "tick-numbers-font": "Skrift for tall på inndelinger", + "unit-title-settings": "Innstillinger for enhetstittel", + "show-unit-title": "Vis enhetstittel", + "unit-title": "Enhetstittel", + "title-font": "Skrift for titteltekst", + "units-settings": "Innstillinger for enheter", + "units-font": "Skrift for enhetstekst", + "value-box-settings": "Innstillinger for verdiboks", + "show-value-box": "Vis verdiboks", + "value-box": "Verdiboks", + "value-int": "Antall sifre i heltallsdelen av verdien", + "value-text": "Verdi tekst", + "value-text-shadow": "Skygge på verditekst", + "value-font": "Skrift for verditekst", + "rect-stroke-color-start": "Startfarge for rektangelkontur (gradient)", + "rect-stroke-color-end": "Sluttfarge for rektangelkontur (gradient)", + "background-color": "Bakgrunnsfarge", + "shadow-color": "Skyggefarge", + "value-box-rect-stroke-color": "Konturfarge for verdiboks", + "value-box-rect-stroke-color-end": "Sluttfarge for verdiboks-kontur (gradient)", + "value-box-background-color": "Bakgrunnsfarge for verdiboks", + "value-box-shadow-color": "Skyggefarge for verdiboks", + "plate-settings": "Platinntillinger", + "show-plate-border": "Plateramme", + "plate-color": "Platefarge", + "needle-settings": "Innstillinger for nål", + "needle-circle-size": "Størrelse på nålsirkel", + "needle-color": "Nålfarge", + "needle-color-start": "Nålfarge – start gradient", + "needle-color-end": "Nålfarge – slutt gradient", + "needle-color-shadow-up": "Skyggefarge øvre del av nål", + "needle-color-shadow-down": "Skyggefarge under (drop shadow)", + "highlights-settings": "Innstillinger for høydepunkter", + "highlights-width": "Bredde på høydepunkter", + "highlights": "Høydepunkter", + "highlight-from": "Fra", + "highlight-to": "Til", + "highlight-color": "Farge", + "no-highlights": "Ingen høydepunkter konfigurert", + "add-highlight": "Legg til høydepunkt", + "animation-settings": "Animasjonsinnstillinger", + "enable-animation": "Animasjon", + "animation-duration-rule": "Varighet og regel for animasjon", + "animation-duration": "Varighet på animasjon", + "animation-rule": "Animasjonsregel", + "animation-linear": "Lineær", + "animation-quad": "Kvadratisk", + "animation-quint": "Kvintisk", + "animation-cycle": "Syklus", + "animation-bounce": "Sprett", + "animation-elastic": "Elastisk", + "animation-dequad": "Avtagende kvadratisk", + "animation-dequint": "Avtagende kvintisk", + "animation-decycle": "Avtagende syklus", + "animation-debounce": "Avtagende sprett", + "animation-delastic": "Avtagende elastisk", + "linear-gauge-settings": "Innstillinger for lineærmåler", + "bar-stroke": "Strek for søyle", + "bar-stroke-width": "Strebredde for søyle", + "bar-stroke-color": "Strekfarge for søyle", + "bar-background-color": "Bakgrunnsfarge for søyle – start gradient", + "bar-background-color-end": "Bakgrunnsfarge for søyle – slutt gradient", + "progress-bar-color": "Farge på fremdriftsindikator", + "progress-bar": "Fremdriftsindikator", + "progress-bar-color-start": "Startfarge for fremdriftsindikator (gradient)", + "progress-bar-color-end": "Sluttfarge for fremdriftsindikator (gradient)", + "major-ticks-names": "Navn på hovedinndelinger", + "show-stroke-ticks": "Vis konturlinjer på inndelinger", + "major-ticks-font": "Skrift for hovedinndelinger", + "border-color": "Rammefarge", + "border-width": "Rammebredde", + "needle-circle": "Nålsirkel", + "needle-circle-color": "Farge på nålsirkel", + "animation-target": "Animasjonsmål", + "animation-target-needle": "Nål", + "animation-target-plate": "Plate", + "common-settings": "Vanlige innstillinger for måler", + "gauge-type": "Type måler", + "gauge-type-arc": "Bue", + "gauge-type-donut": "Smultring", + "gauge-type-horizontal-bar": "Horisontal søyle", + "gauge-type-vertical-bar": "Vertikal søyle", + "donut-start-angle": "Startvinkel (grader)", + "bar-settings": "Innstillinger for målersøyle", + "relative-bar-width": "Relativ søylebredde", + "neon-glow-brightness": "Lysstyrke for neon-glød effekt (0–100)", + "neon-glow-brightness-hint": "0 - deaktiver effekt", + "stripes-thickness": "Tykkelse på striper", + "stripes-thickness-hint": "0 - ingen striper", + "rounded-line-cap": "Avrundet linjeende", + "bar-color-settings": "Fargeinnstillinger for søyle", + "use-precise-level-color-values": "Bruk presise fargenivåer", + "bar-colors": "Søylefarger, fra lav til høy", + "color": "Farge", + "no-bar-colors": "Ingen søylefarger konfigurert", + "add-bar-color": "Legg til søylefarge", + "from": "Fra", + "to": "Til", + "fixed-level-colors": "Søylefarger ved hjelp av grenseverdier", + "gauge-title-settings": "Innstillinger for målertittel", + "show-gauge-title": "Vis målertittel", + "gauge-title": "Målertittel", + "gauge-title-font": "Skrift for målertittel", + "unit-title-and-timestamp-settings": "Innstillinger for enhetstittel og tidsstempel", + "show-timestamp": "Tidsstempel", + "timestamp-format": "Format for tidsstempel", + "label-font": "Skrift for etikett under verdi", + "value-settings": "Innstillinger for verdi", + "show-value": "Vis verditekst", + "min-max-settings": "Innstillinger for min/maks etiketter", + "show-min-max": "Vis minimum og maksimum verdier", + "min-max-font": "Skrift for minimum og maksimum etiketter", + "show-ticks": "Vis inndelinger", + "tick-width": "Bredde på inndelinger", + "tick-color": "Farge på inndelinger", + "tick-values": "Verdier for inndelinger", + "no-tick-values": "Ingen verdier for inndelinger konfigurert", + "add-tick-value": "Legg til inndelingsverdi", + "gauge-appearance": "Utseende på måler", + "units-title": "Tittel for enheter", + "value": "Verdi", + "ticks": "Inndelinger", + "arrow-and-scale-color": "Standardfarge for pil og skala", + "scale-settings": "Skalainnstillinger", + "scale": "Skala", + "scale-color": "Skalafarger", + "compass-appearance": "Utseende på kompass", + "label": "Etikett", + "labels": "Etiketter", + "label-style": "Stil på etikett", + "simple-gauge-type": "Type", + "gauge-bar-background": "Bakgrunn for målersøyle", + "bar-color": "Søylefarge", + "min-and-max-value": "Min og maks verdi", + "min-and-max-label": "Etikett for min og maks", + "font": "Skrift", + "tick-width-and-color": "Bredde og farge på inndelinger", + "min-max-validation-text": "Maksverdi må være større enn minverdi" + }, + "gpio": { + "pin": "Pin", + "label": "Etikett", + "row": "Rad", + "column": "Kolonne", + "color": "Farge", + "panel-settings": "Panelinnstillinger", + "background-color": "Bakgrunnsfarge", + "gpio-switches": "GPIO-brytere", + "no-gpio-switches": "Ingen GPIO-brytere konfigurert", + "add-gpio-switch": "Legg til GPIO-bryter", + "gpio-status-request": "Forespørsel om GPIO-status", + "method-name": "Metodenavn", + "method-body": "Metodeinnhold", + "gpio-status-change-request": "Forespørsel om endring i GPIO-status", + "parse-gpio-status-function": "Funksjon for parsing av GPIO-status", + "gpio-leds": "GPIO-lysdioder", + "no-gpio-leds": "Ingen GPIO-lysdioder konfigurert", + "add-gpio-led": "Legg til GPIO-lysdioder" + }, + "html-card": { + "html": "HTML", + "css": "CSS" + }, + "input-widgets": { + "attribute-not-allowed": "Attributtparameter kan ikke brukes i denne widgeten", + "blocked-location": "Geolokasjon er blokkert i nettleseren din", + "claim-device": "Krev enhet", + "claim-failed": "Klarte ikke å kreve enheten!", + "claim-not-found": "Enhet ikke funnet!", + "claim-successful": "Enheten ble krevd med suksess!", + "date": "Dato", + "device-name": "Enhetsnavn", + "device-name-required": "Enhetsnavn er påkrevd", + "discard-changes": "Forkast endringer", + "entity-attribute-required": "Enhetens attributt er påkrevd", + "entity-coordinate-required": "Begge feltene, breddegrad og lengdegrad, er påkrevd", + "entity-timeseries-required": "Tidsserie for enhet er påkrevd", + "get-location": "Hent gjeldende posisjon", + "invalid-date": "Ugyldig dato", + "latitude": "Breddegrad", + "longitude": "Lengdegrad", + "min-value-error": "Minimumsverdi er {{value}}", + "max-value-error": "Maksimumsverdi er {{value}}", + "not-allowed-entity": "Valgt enhet kan ikke ha delte attributter", + "no-attribute-selected": "Ingen attributt valgt", + "no-datakey-selected": "Ingen datanøkkel valgt", + "no-coordinate-specified": "Datanøkkel for bredde-/lengdegrad er ikke angitt", + "no-entity-selected": "Ingen enhet valgt", + "no-image": "Ingen bilde", + "no-support-geolocation": "Nettleseren din støtter ikke geolokasjon", + "no-support-web-camera": "Nettleseren din støtter ikke kameraer", + "enable-https-use-widget": "Vennligst aktiver HTTPS for å bruke denne widgeten", + "no-found-your-camera": "Finner ikke kameraet ditt", + "no-permission-camera": "Tillatelse ble avslått av brukeren / Dette nettstedet har ikke tillatelse til å bruke kameraet", + "no-timeseries-selected": "Ingen tidsserie valgt", + "secret-key": "Hemmelig nøkkel", + "secret-key-required": "Hemmelig nøkkel er påkrevd", + "switch-attribute-value": "Bytt enhetens attributtverdi", + "switch-camera": "Bytt kamera", + "switch-timeseries-value": "Bytt enhetens tidsserieverdi", + "take-photo": "Ta bilde", + "time": "Tid", + "timeseries-not-allowed": "Tidsserieparameter kan ikke brukes i denne widgeten", + "update-failed": "Oppdatering mislyktes", + "update-successful": "Oppdatering vellykket", + "update-attribute": "Oppdater attributt", + "update-timeseries": "Oppdater tidsserie", + "value": "Verdi", + "general-settings": "Generelle innstillinger", + "widget-title": "Widget-tittel", + "claim-button-label": "Etikett for kravknapp", + "show-secret-key-field": "Vis feltet 'Hemmelig nøkkel'", + "labels-settings": "Etikettinnstillinger", + "show-labels": "Vis etiketter", + "device-name-label": "Etikett for inputfelt for enhetsnavn", + "secret-key-label": "Etikett for inputfelt for hemmelig nøkkel", + "messages-settings": "Meldingsinnstillinger", + "claim-device-success-message": "Meldingstekst for vellykket krav av enhet", + "claim-device-not-found-message": "Meldingstekst når enhet ikke ble funnet", + "claim-device-failed-message": "Meldingstekst for mislykket krav av enhet", + "claim-device-name-required-message": "Feilmelding for 'Enhetsnavn påkrevd'", + "claim-device-secret-key-required-message": "Feilmelding for 'Hemmelig nøkkel påkrevd'", + "show-label": "Vis etikett", + "label": "Etikett", + "required": "Påkrevd", + "required-error-message": "Feilmelding for 'Påkrevd'", + "show-result-message": "Vis resultatmelding", + "integer-field-settings": "Innstillinger for heltallfelt", + "min-value": "Minimumsverdi", + "max-value": "Maksimumsverdi", + "double-field-settings": "Innstillinger for desimalfelt", + "text-field-settings": "Innstillinger for tekstfelt", + "min-length": "Minimumslengde", + "max-length": "Maksimumslengde", + "checkbox-settings": "Innstillinger for avkrysningsboks", + "true-label": "Etikett for avkrysset", + "false-label": "Etikett for ikke avkrysset", + "image-input-settings": "Innstillinger for bildefelt", + "display-preview": "Vis forhåndsvisning", + "display-clear-button": "Vis klareringsknapp", + "display-apply-button": "Vis bruk-knapp", + "display-discard-button": "Vis forkast-knapp", + "datetime-field-settings": "Innstillinger for dato/tid-felt", + "display-time-input": "Vis tidsinput", + "latitude-key-name": "Nøkkelnavn for breddegrad", + "longitude-key-name": "Nøkkelnavn for lengdegrad", + "show-get-location-button": "Vis knappen 'Hent gjeldende posisjon'", + "use-high-accuracy": "Bruk høy nøyaktighet", + "location-fields-settings": "Innstillinger for posisjonsfelt", + "latitude-label": "Etikett for breddegrad", + "longitude-label": "Etikett for lengdegrad", + "input-fields-alignment": "Justering av inputfelt", + "input-fields-alignment-column": "Kolonne (standard)", + "input-fields-alignment-row": "Rad", + "layout": "Oppsett", + "row-gap": "Avstand mellom rader i piksler", + "column-gap": "Avstand mellom kolonner i piksler", + "latitude-field-required": "Breddegradsfeltet er påkrevd", + "longitude-field-required": "Lengdegradsfeltet er påkrevd", + "attribute-settings": "Attributtinnstillinger", + "widget-mode": "Widget-modus", + "widget-mode-update-attribute": "Oppdater attributt", + "widget-mode-update-timeseries": "Oppdater tidsserie", + "attribute-scope": "Attributtomfang", + "attribute-scope-server": "Serverattributt", + "attribute-scope-shared": "Delt attributt", + "value-required": "Verdi påkrevd", + "image-settings": "Bildeinnstillinger", + "image-format": "Bildeformat", + "image-format-jpeg": "JPEG", + "image-format-png": "PNG", + "image-format-webp": "WEBP", + "image-quality": "Bildekvalitet som bruker tapskomprimering, som JPEG og WEBP", + "max-image-width": "Maksimal bildebredde", + "max-image-height": "Maksimal bildehøyde", + "action-buttons": "Handlingsknapper", + "show-action-buttons": "Vis handlingsknapper", + "update-all-values": "Oppdater alle verdier, ikke bare endrede", + "save-button-label": "Etikett for 'LAGRE'-knapp", + "reset-button-label": "Etikett for 'ANGRE'-knapp", + "group-settings": "Gruppeinnstillinger", + "show-group-title": "Vis tittel for gruppe av felt relatert til forskjellige enheter", + "group-title": "Gruppetittel", + "fields-alignment": "Feltsjustering", + "fields-alignment-row": "Rad (standard)", + "fields-alignment-column": "Kolonne", + "fields-in-row": "Antall felt i raden", + "option-value": "Verdi (skriv 'null' for å opprette tomt alternativ)", + "option-label": "Etikett", + "hide-input-field": "Skjul inputfelt", + "datakey-type": "Datanøkkeltype", + "datakey-type-server": "Serverattributt (standard)", + "datakey-type-shared": "Delt attributt", + "datakey-type-timeseries": "Tidsserie", + "datakey-value-type": "Datanøkkelverditype", + "datakey-value-type-string": "Streng", + "datakey-value-type-double": "Desimaltall", + "datakey-value-type-integer": "Heltall", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "Boolsk (avkrysningsboks)", + "datakey-value-type-boolean-switch": "Boolsk (bryter)", + "datakey-value-type-date-time": "Dato og tid", + "datakey-value-type-date": "Dato", + "datakey-value-type-time": "Tid", + "datakey-value-type-select": "Velg", + "datakey-value-type-radio": "Radioknapp", + "datakey-value-type-color": "Farge", + "value-is-required": "Verdi er påkrevd", + "ability-to-edit-attribute": "Mulighet til å redigere attributt", + "ability-to-edit-attribute-editable": "Redigerbar (standard)", + "ability-to-edit-attribute-disabled": "Deaktivert", + "ability-to-edit-attribute-readonly": "Skrivebeskyttet", + "disable-on-datakey-name": "Deaktiver ved falsk verdi fra annen datanøkkel (angi navn på datanøkkel)", + "field-appearance": "Feltutseende", + "appearance-fill": "Fyll", + "appearance-outline": "Kontur", + "subscript-sizing": "Størrelse på underskrift", + "subscript-sizing-fixed": "Fast", + "subscript-sizing-dynamic": "Dynamisk", + "slide-toggle-settings": "Innstillinger for bryter", + "slide-toggle-label-position": "Plassering av etikett for bryter", + "slide-toggle-label-position-after": "Etter", + "slide-toggle-label-position-before": "Før", + "select-options": "Velg alternativer", + "no-select-options": "Ingen valgalternativer konfigurert", + "add-select-option": "Legg til valgalternativ", + "numeric-field-settings": "Innstillinger for numerisk felt", + "step-interval": "Stegintervall mellom verdier", + "error-messages": "Feilmeldinger", + "min-value-error-message": "Feilmelding for 'Min verdi'", + "max-value-error-message": "Feilmelding for 'Maks verdi'", + "invalid-date-error-message": "Feilmelding for 'Ugyldig dato'", + "invalid-JSON-error-message": "Feilmelding for 'Ugyldig JSON'", + "icon-settings": "Ikoninnstillinger", + "dialog-editor-settings": "Innstillinger for dialogredigering", + "use-custom-icon": "Bruk tilpasset ikon", + "input-cell-icon": "Ikon som vises før inputfelt", + "value-conversion-settings": "Innstillinger for verdikonvertering", + "get-value-settings": "Hent verdi-innstillinger", + "use-get-value-function": "Bruk getValue-funksjon", + "get-value-function": "getValue-funksjon", + "set-value-settings": "Angi verdi-innstillinger", + "use-set-value-function": "Bruk setValue-funksjon", + "set-value-function": "setValue-funksjon", + "json-invalid": "JSON-verdien har et ugyldig format", + "title": "Tittel", + "cancel-button-label": "Etikett for 'Avbryt'-knapp", + "radio-button-settings": "Innstillinger for radioknapper", + "color": "Farge", + "columns": "Kolonner", + "radio-options": "Radioknappealternativer", + "no-radio-options": "Ingen radioknappealternativer konfigurert", + "add-radio-option": "Legg til radioknappealternativ", + "radio-label-position": "Etikettplassering", + "radio-label-position-before": "Før", + "radio-label-position-after": "Etter" + }, + "invalid-qr-code-text": "Ugyldig inputtekst for QR-kode. Input må være av typen streng", + "qr-code": { + "use-qr-code-text-function": "Bruk QR-kodetekstfunksjon", + "qr-code-text-pattern": "QR-kodetekstmønster (for eksempel '${entityName} | ${keyName} - noe tekst.')", + "qr-code-text-pattern-hint": "QR-kodetekstmønster bruker verdien til den første funnet nøkkelen i enhetene i enhetsaliaset.", + "qr-code-text-pattern-required": "QR-kodetekstmønster er påkrevd.", + "qr-code-text-function": "QR-kodetekstfunksjon" + }, + "label-widget": { + "label-pattern": "Mønster", + "label-pattern-hint": "Hint: for eksempel 'Tekst ${keyName} enheter.' eller ${#<key index>} enheter", + "label-pattern-required": "Mønster er påkrevd", + "label-position": "Posisjon (prosent i forhold til bakgrunn)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Bakgrunnsfarge", + "font-settings": "Skriftinnstillinger", + "background-image": "Bakgrunnsbilde", + "labels": "Etiketter", + "no-labels": "Ingen etiketter konfigurert", + "add-label": "Legg til etikett" + }, + "navigation": { + "title": "Tittel", + "navigation-path": "Navigasjonssti", + "filter-type": "Filtertype", + "filter-type-all": "Alle elementer", + "filter-type-include": "Inkluder elementer", + "filter-type-exclude": "Ekskluder elementer", + "items": "Elementer", + "enter-urls-to-filter": "Skriv inn URL-er for å filtrere..." + }, + "persistent-table": { + "rpc-id": "RPC-ID", + "message-type": "Meldingstype", + "method": "Metode", + "params": "Parametere", + "created-time": "Opprettelsestid", + "expiration-time": "Utløpstid", + "retries": "Forsøk", + "status": "Status", + "filter": "Filter", + "refresh": "Oppdater", + "add": "Legg til RPC-forespørsel", + "details": "Detaljer", + "delete": "Slett", + "delete-request-title": "Slett vedvarende RPC-forespørsel", + "delete-request-text": "Er du sikker på at du vil slette forespørselen?", + "details-title": "Detaljer RPC-ID: ", + "additional-info": "Tilleggsinformasjon", + "response": "Respons", + "any-status": "Enhver status", + "rpc-status-list": "RPC-statusliste", + "no-request-prompt": "Ingen forespørsler å vise", + "send-request": "Send forespørsel", + "add-title": "Opprett vedvarende RPC-forespørsel", + "method-error": "Metode er påkrevd.", + "timeout-error": "Minimum tidsavbrudd er 5000 (5 sekunder).", + "white-space-error": "Mellomrom er ikke tillatt.", + "rpc-status": { + "QUEUED": "I KØ", + "SENT": "SENDT", + "DELIVERED": "LEVERT", + "SUCCESSFUL": "VELLYKKET", + "TIMEOUT": "TIDSAVBRUDD", + "EXPIRED": "UTLØPT", + "FAILED": "FEILET" + }, + "rpc-search-status-all": "ALLE", + "message-types": { + "false": "To-veis", + "true": "En-veis" + }, + "general-settings": "Generelle innstillinger", + "enable-filter": "Aktiver filter", + "enable-sticky-header": "Vis overskrift ved rulling", + "enable-sticky-action": "Vis handlingskolonne ved rulling", + "display-request-details": "Vis forespørselsdetaljer", + "allow-send-request": "Tillat å sende RPC-forespørsel", + "allow-delete-request": "Tillat sletting av forespørsel", + "columns-settings": "Kolonneinnstillinger", + "display-columns": "Kolonner som skal vises", + "column": "Kolonne", + "no-columns-found": "Ingen kolonner funnet", + "no-columns-matching": "'{{column}}' ble ikke funnet." + }, + "range-chart": { + "chart": "Diagram", + "data-zoom": "Datazoom", + "range-chart-appearance": "Utseende for områdediagram", + "range-colors": "Områdefarger", + "out-of-range-color": "Farge utenfor område", + "show-range-thresholds": "Vis områdegrenser", + "range-thresholds-settings": "Innstillinger for områdegrenser", + "fill-area": "Fyll område", + "fill-area-opacity": "Opasitet for fylt område", + "range-chart-style": "Stil for områdediagram" + }, + "rpc": { + "value-settings": "Verdiinnstillinger", + "initial-value": "Startverdi", + "retrieve-value-settings": "Innhent på/av-verdiinnstillinger", + "retrieve-value-method": "Hent verdi ved hjelp av metode", + "retrieve-value-method-none": "Ikke hent", + "retrieve-value-method-rpc": "Kall RPC-metode for å hente verdi", + "retrieve-value-method-attribute": "Abonner på attributt", + "retrieve-value-method-timeseries": "Abonner på tidsserier", + "attribute-value-key": "Attributtnøkkel", + "timeseries-value-key": "Tidsserienøkkel", + "get-value-method": "RPC get-verdimetode", + "parse-value-function": "Parse-verdi-funksjon", + "update-value-settings": "Oppdater verdiinnstillinger", + "set-value-method": "RPC set-verdimetode", + "convert-value-function": "Konverter verdi-funksjon", + "rpc-settings": "RPC-innstillinger", + "request-timeout": "Tidsavbrudd for RPC-forespørsel (ms)", + "persistent-rpc-settings": "Vedvarende RPC-innstillinger", + "request-persistent": "Vedvarende RPC-forespørsel", + "persistent-polling-interval": "Polling-intervall (ms) for å hente respons på vedvarende RPC-kommando", + "common-settings": "Vanlige innstillinger", + "switch-title": "Tittel for bryter", + "show-on-off-labels": "Vis på/av-etiketter", + "slide-toggle-label": "Skyvebryteretikett", + "label-position": "Etikettposisjon", + "label-position-before": "Før", + "label-position-after": "Etter", + "slider-color": "Farge på skyvebryter", + "slider-color-primary": "Primær", + "slider-color-accent": "Fremhevet", + "slider-color-warn": "Advarsel", + "button-style": "Knappestil", + "button-raised": "Hevet knapp", + "button-primary": "Primærfarge", + "button-background-color": "Bakgrunnsfarge på knapp", + "button-text-color": "Tekstfarge på knapp", + "widget-title": "Widget-tittel", + "button-label": "Knappetikett", + "device-attribute-scope": "Omfang for enhetsattributt", + "server-attribute": "Serverattributt", + "shared-attribute": "Delt attributt", + "device-attribute-parameters": "Enhetsattributtparametere", + "is-one-way-command": "Er enveis kommando", + "rpc-method": "RPC-metode", + "rpc-method-params": "RPC-metodeparametere", + "show-rpc-error": "Vis feil ved RPC-kommandoutførelse", + "led-title": "LED-tittel", + "led-color": "LED-farge", + "check-status-settings": "Statuskontrollinnstillinger", + "perform-rpc-status-check": "Utfør RPC-enhetsstatuskontroll", + "retrieve-led-status-value-method": "Hent LED-statusverdi ved hjelp av metode", + "led-status-value-attribute": "Enhetsattributt som inneholder LED-statusverdi", + "led-status-value-timeseries": "Enhetstidsserie som inneholder LED-statusverdi", + "check-status-method": "RPC-metode for statuskontroll", + "parse-led-status-value-function": "Parse LED-statusverdi-funksjon", + "knob-title": "Knappetittel", + "min-value": "Minimumsverdi", + "max-value": "Maksimumsverdi" + }, + "maps": { + "map-type": { + "type": "Karttype", + "map": "Kart", + "image": "Bilde" + }, + "image": { + "image-source": "Bildekilde", + "image-source-image": "Bilde", + "image-source-entity-key": "Enhetsnøkkel", + "source-entity-alias": "Alias for kildeenhet", + "image-url-key": "URL-nøkkel for bilde", + "image-url-key-required": "URL-nøkkel for bilde er påkrevd" + }, + "control": { + "map-controls": "Kartkontroller", + "position": "Posisjon", + "position-topleft": "Øverst til venstre", + "position-topright": "Øverst til høyre", + "position-bottomleft": "Nederst til venstre", + "position-bottomright": "Nederst til høyre", + "zoom-actions": "Zoom-handlinger", + "zoom-scroll": "Rull", + "zoom-double-click": "Dobbeltklikk", + "zoom-control-buttons": "Kontrollknapper", + "scale": "Skala", + "scale-metric": "Metrisk", + "scale-imperial": "Imperial", + "switch-to-drag-mode-using-button": "Bytt til dra-modus ved hjelp av knapp" + }, + "timeline": { + "control-panel": "Tidslinjekontrollpanel", + "time-step": "Tidsintervall", + "speed-options": "Hastighetsalternativer", + "timestamp": "Tidsstempel", + "snap-to-real-location": "Fest til reell posisjon", + "location-snap-filter-function": "Filterfunksjon for posisjonsjustering", + "no-trips-data-available": "Ingen turdata tilgjengelig" + }, + "map-action": { + "map-action-buttons": "Kart-handlingsknapper", + "label": "Etikett", + "icon": "Ikon", + "color": "Farge", + "action": "Handling", + "add-button": "Legg til knapp", + "no-action-buttons-configured": "Ingen handlingsknapper konfigurert", + "remove-action-button": "Fjern handlingsknapp", + "map-action-button": "Kart-handlingsknapp", + "button-requires": "Knappen krever etikett eller ikon" + }, + "common": { + "common-map-settings": "Vanlige kartinnstillinger", + "fit-map-bounds": "Tilpass kartgrenser for å dekke alle markører", + "default-map-center-position": "Standard kart midtposisjon", + "default-map-zoom-level": "Standard zoomnivå for kart", + "entities-limit": "Grense for antall enheter som skal lastes" + }, + "layer": { + "label": "Etikett", + "layer": "Lag", + "layers": "Lag", + "map-layers": "Kartlag", + "add-layer": "Legg til lag", + "layer-settings": "Laginnstillinger", + "remove-layer": "Fjern lag", + "no-layers": "Ingen lag konfigurert", + "roadmap": "Veikart", + "satellite": "Satellitt", + "hybrid": "Hybrid", + "reference": { + "reference-layer": "Referanselag", + "no-layer": "Ingen lag", + "openstreetmap-hybrid": "OpenStreetMap Hybrid", + "world-edition-hybrid": "Verdensutgave Hybrid", + "enhanced-contrast-hybrid": "Forbedret Kontrast Hybrid" + }, + "provider": { + "provider": "Leverandør", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Veikart", + "satellite": "Satellitt", + "hybrid": "Hybrid", + "terrain": "Terreng" + }, + "here": { + "title": "HERE", + "normal-day": "Normal dag", + "normal-night": "Normal natt", + "hybrid-day": "Hybrid dag", + "terrain-day": "Terreng dag" + }, + "tencent": { + "title": "Tencent", + "normal": "Normal", + "satellite": "Satellitt", + "terrain": "Terreng" + }, + "custom": { + "title": "Egendefinert", + "tile-url": "Flis-URL" + } + }, + "credentials": { + "credentials": "Legitimasjon", + "api-key": "API-nøkkel" + } + }, + "overlays": { + "overlays": "Overlegg", + "overlays-hint": "Konfigurer datakilder, utseende, atferd, redigeringsalternativer og gruppering for kartenheter", + "trips": "Turer", + "markers": "Markører", + "polygons": "Polygoner", + "circles": "Sirkler" + }, + "data-layer": { + "source": "Kilde", + "filter": "Filter", + "additional-data-keys": "Ekstra datanøkler", + "additional-datasources": "Ekstra datakilder", + "additional-datasources-hint": "Datakilde for tilgang til attributter eller telemetri fra enheter som ikke vises på kartet, kan brukes i kartoverleggfunksjoner.", + "more-datasources": "Flere datakilder", + "data-keys": "Datanøkler", + "add-datasource": "Legg til datakilde", + "no-datasources": "Ingen datakilder konfigurert", + "remove-datasource": "Fjern datakilde", + "behavior": "Oppførsel", + "on-click": "Ved klikk", + "on-click-hint": "Handling som utføres når brukeren klikker på elementet i kartet.", + "groups": "Grupper", + "groups-hint": "Liste over gruppenavn som er tilknyttet overlegget, brukes til å styre synlighet på kartet.", + "color": "Farge", + "color-settings": "Fargeinnstillinger", + "color-type-constant": "Konstant", + "color-type-range": "Område", + "color-type-function": "Funksjon", + "color-range-source-key": "Kildenøkkel for fargeområde", + "color-range-source-key-required": "Kildenøkkel for fargeområde er påkrevd", + "color-range": "Fargeområde", + "color-function": "Fargefunksjon", + "label": "Etikett", + "tooltip": "Verktøytips", + "pattern-type-pattern": "Mønster", + "pattern-type-function": "Funksjon", + "label-pattern": "Etikett (mønstereksempler: '${entityName}', '${entityName}: (Tekst ${keyName} enheter.)')", + "label-function": "Etikettfunksjon", + "tooltip-pattern": "Verktøytips (f.eks. 'Tekst ${keyName} enheter.' eller Lenketekst)", + "tooltip-function": "Verktøytipsfunksjon", + "tooltip-trigger": "Utløser for verktøytips", + "tooltip-trigger-click": "Vis verktøytips ved klikk", + "tooltip-trigger-hover": "Vis verktøytips ved hover", + "auto-close-tooltips": "Lukk verktøytips automatisk", + "tooltip-offset": "Forskyvning av verktøytips", + "tooltip-offset-horizontal": "Horisontal", + "tooltip-offset-vertical": "Vertikal", + "tooltip-tag-actions": "Tagg-handlinger", + "add-tooltip-tag-action": "Legg til tagg-handling", + "edit-tooltip-tag-action": "Rediger tagg-handling", + "remove-tooltip-tag-action": "Fjern tagg-handling", + "action-add": "Legg til", + "action-edit": "Rediger", + "action-move": "Flytt", + "action-remove": "Fjern", + "edit-instruments": "Verktøy", + "persist-location-attribute-scope": "Omfang for attributt til lagring av posisjon", + "enable-snapping": "Aktiver snapping til andre hjørner for presis tegning", + "enable-snapping-hint": "Justerer automatisk nye punkter med eksisterende former for enklere og mer nøyaktig tegning.", + "drag-drop-mode": "Dra-og-slipp-modus", + "trip": { + "no-trips": "Ingen turer konfigurert", + "add-trip": "Legg til tur", + "trip-configuration": "Turkonfigurasjon", + "remove-trip": "Fjern tur" + }, + "marker": { + "marker": "Markør", + "latitude-key": "Latitude-nøkkel", + "longitude-key": "Longitude-nøkkel", + "x-pos-key": "X-posisjonsnøkkel", + "y-pos-key": "Y-posisjonsnøkkel", + "latitude-key-required": "Latitude-nøkkel er påkrevd", + "longitude-key-required": "Longitude-nøkkel er påkrevd", + "x-pos-key-required": "X-posisjonsnøkkel er påkrevd", + "y-pos-key-required": "Y-posisjonsnøkkel er påkrevd", + "no-markers": "Ingen markører konfigurert", + "add-marker": "Legg til markør", + "marker-configuration": "Markørkonfigurasjon", + "remove-marker": "Fjern markør", + "marker-type": "Markørtype", + "marker-type-shape": "Form", + "marker-type-icon": "Ikon", + "marker-type-image": "Bilde", + "shape": "Form", + "icon": "Ikon", + "image": "Bilde", + "marker-shapes": "Markørformer", + "marker-icon": "Markørikon", + "marker-appearance": "Markørutseende", + "marker-image": "Markørbilde", + "marker-image-type-image": "Bilde", + "marker-image-type-function": "Funksjon", + "custom-marker-image-size": "Egendefinert markørbildestørrelse", + "marker-image-function": "Markørbildets funksjon", + "marker-images": "Markørbilder", + "marker-offset": "Markørforskyvning", + "offset-horizontal": "Horisontal", + "offset-vertical": "Vertikal", + "rotate-marker": "Roter markør", + "offset-angle": "Vinkelavvik", + "position-conversion": "Posisjonskonvertering", + "position-conversion-function": "Posisjonskonverteringsfunksjon, skal returnere x,y-koordinater som desimaler fra 0 til 1", + "clustering": { + "use-map-markers-clustering": "Bruk markørklynger", + "zoom-on-cluster-click": "Zoom inn ved klikking på klynge", + "max-zoom": "Maksimal zoomnivå når en markør kan være en del av en klynge (0 - 18)", + "max-radius": "Maks radius som en klynge dekker", + "zoom-animation": "Animér markører ved zooming", + "bounds-on-cluster-mouse-over": "Vis markørgrenser ved musepeker over klynge", + "spiderfy-max-zoom-level": "Utvid til enkeltmarkører ved maks zoomnivå", + "load-optimization": "Lastoptimalisering", + "chunked-load": "Bruk blokker for å legge til markører for å unngå at siden fryser", + "lazy-load": "Bruk treg lasting for markører", + "use-cluster-marker-color-function": "Bruk fargefunksjon for klyngemarkører", + "marker-color-function": "Markørfargefunksjon" + }, + "edit": "Rediger markør", + "remove-marker-for": "Fjern markør for '{{entityName}}'", + "place-marker": "Plasser markør", + "place-marker-hint": "Klikk for å plassere markør", + "place-marker-hint-with-entity": "Klikk for å plassere '{{entityName}}'-enhet" + }, + "path": { + "path": "Sti", + "path-decorator": "Stidekorator", + "decorator-symbol": "Dekorsymbol", + "decorator-symbol-arrow-head": "Pil", + "decorator-symbol-dash": "Strek", + "decorator-arrangement": "Dekorarrangement", + "decorator-offset": "Start", + "decorator-end-offset": "Slutt", + "decorator-repeat": "Gjenta" + }, + "points": { + "points": "Punkter", + "point-tooltip": "Punkt-verktøytips" + }, + "shape": { + "fill": "Fyll", + "fill-type-color": "Farge", + "fill-type-stripe": "Striper", + "fill-type-image": "Bilde", + "color": "Farge", + "stripe": "Stripe", + "image": "Bilde", + "stroke": "Strek", + "fill-image": "Fyllbilde", + "fill-image-type-image": "Bilde", + "fill-image-type-function": "Funksjon", + "preserve-aspect-ratio": "Bevar høyde-bredde-forhold", + "opacity": "Opasitet", + "angle": "Rotasjonsvinkel", + "scale": "Skala", + "fill-image-function": "Formfyllbilde-funksjon", + "fill-images": "Formfyllbilder", + "stripe-pattern": "Stripe-mønster", + "first-stripe": "Første stripe", + "second-stripe": "Andre stripe" + }, + "polygon": { + "polygon-key": "Polygon-nøkkel", + "polygon-key-required": "Polygon-nøkkel er påkrevd", + "no-polygons": "Ingen polygoner konfigurert", + "add-polygon": "Legg til polygon", + "polygon-configuration": "Polygonkonfigurasjon", + "remove-polygon": "Fjern polygon", + "edit": "Rediger polygon", + "remove-polygon-for": "Fjern polygon for '{{entityName}}'", + "cut": "Klipp polygonområde", + "rotate": "Roter polygon", + "draw-rectangle": "Tegn rektangel", + "draw-polygon": "Tegn polygon", + "polygon-place-first-point-cut-hint": "Klikk for å plassere første punkt", + "continue-polygon-cut-hint": "Klikk for å fortsette tegningen", + "finish-polygon-cut-hint": "Klikk på første punkt for å avslutte og lagre", + "polygon-place-first-point-hint": "Polygon: klikk for å plassere første punkt", + "polygon-place-first-point-hint-with-entity": "Polygon for '{{entityName}}': klikk for å plassere første punkt", + "continue-polygon-hint": "Polygon: klikk for å fortsette tegningen", + "continue-polygon-hint-with-entity": "Polygon for '{{entityName}}': klikk for å fortsette tegningen", + "finish-polygon-hint": "Polygon: klikk første punkt for å avslutte tegning", + "finish-polygon-hint-with-entity": "Polygon for '{{entityName}}': klikk første punkt for å avslutte og lagre", + "rectangle-place-first-point-hint": "Rektangel: klikk for å plassere første punkt", + "rectangle-place-first-point-hint-with-entity": "Rektangel for '{{entityName}}': klikk for å plassere første punkt", + "finish-rectangle-hint": "Rektangel: klikk for å avslutte tegningen", + "finish-rectangle-hint-with-entity": "Rektangel for '{{entityName}}': klikk for å avslutte og lagre" + }, + "circle": { + "circle-key": "Sirkel-nøkkel", + "circle-key-required": "Sirkel-nøkkel er påkrevd", + "no-circles": "Ingen sirkler konfigurert", + "add-circle": "Legg til sirkel", + "circle-configuration": "Sirkelkonfigurasjon", + "remove-circle": "Fjern sirkel", + "edit": "Rediger sirkel", + "remove-circle-for": "Fjern sirkel for '{{entityName}}'", + "draw-circle": "Tegn sirkel", + "place-circle-center-hint-with-entity": "Sirkel for '{{entityName}}': klikk for å plassere sentrum", + "place-circle-center-hint": "Sirkel: klikk for å plassere sentrum", + "finish-circle-hint-with-entity": "Sirkel for '{{entityName}}': klikk for å avslutte og lagre sirkel", + "finish-circle-hint": "Sirkel: klikk for å avslutte tegning" + }, + "select-entity": "Velg enhet", + "select-entity-hint": "Tips: etter valg, klikk på kartet for å angi posisjon" + }, + "select-entity": "Velg enhet", + "select-entity-hint": "Tips: etter valg, klikk på kartet for å angi posisjon", + "tooltips": { + "placeMarker": "Klikk for å plassere '{{entityName}}' enhet", + "firstVertex": "Polygon for '{{entityName}}': klikk for å plassere første punkt", + "firstVertex-cut": "Klikk for å plassere første punkt", + "continueLine": "Polygon for '{{entityName}}': klikk for å fortsette tegningen", + "continueLine-cut": "Klikk for å fortsette tegningen", + "finishLine": "Klikk på eksisterende markør for å avslutte", + "finishPoly": "Polygon for '{{entityName}}': klikk på første punkt for å avslutte og lagre", + "finishPoly-cut": "Klikk på første punkt for å avslutte og lagre", + "finishRect": "Polygon for '{{entityName}}': klikk for å avslutte og lagre", + "startCircle": "Sirkel for '{{entityName}}': klikk for å plassere sentrum", + "finishCircle": "Sirkel for '{{entityName}}': klikk for å avslutte sirkelen", + "placeCircleMarker": "Klikk for å plassere sirkelmarkør" + }, + "actions": { + "finish": "Fullfør", + "cancel": "Avbryt", + "removeLastVertex": "Fjern siste punkt" + }, + "buttonTitles": { + "drawMarkerButton": "Plasser enhet", + "drawPolyButton": "Lag polygon", + "drawLineButton": "Lag linje", + "drawCircleButton": "Lag sirkel", + "drawRectButton": "Lag rektangel", + "editButton": "Redigeringsmodus", + "dragButton": "Dra og slipp-modus", + "cutButton": "Klipp polygonområde", + "deleteButton": "Fjern", + "drawCircleMarkerButton": "Lag sirkelmarkør", + "rotateButton": "Roter polygon" + }, + "map-provider-settings": "Kartleverandør-innstillinger", + "map-provider": "Kartleverandør", + "map-provider-google": "Google Maps", + "map-provider-openstreet": "OpenStreet Maps", + "map-provider-here": "HERE Maps", + "map-provider-image": "Bildekart", + "map-provider-tencent": "Tencent Maps", + "openstreet-provider": "OpenStreet kartleverandør", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (Standard)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery": "Esri.WorldImagery", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Bruk tilpasset leverandør", + "custom-provider-tile-url": "Tilpasset leverandørens flis-URL", + "google-maps-api-key": "Google Maps API-nøkkel", + "default-map-type": "Standard karttype", + "google-map-type-roadmap": "Veikart", + "google-map-type-satelite": "Satellitt", + "google-map-type-hybrid": "Hybrid", + "google-map-type-terrain": "Terreng", + "map-layer": "Kartlag", + "here-map-normal-day": "HERE.normalDay (Standard)", + "here-map-normal-night": "HERE.normalNight", + "here-map-hybrid-day": "HERE.hybridDay", + "here-map-terrain-day": "HERE.terrainDay", + "credentials": "Legitimasjon", + "here-app-id": "HERE app ID", + "here-app-code": "HERE app-kode", + "here-api-key": "HERE API-nøkkel", + "here-use-new-version-api-3": "Bruk API versjon 3", + "tencent-maps-api-key": "Tencent Maps API-nøkkel", + "tencent-map-type-roadmap": "Veikart", + "tencent-map-type-satelite": "Satellitt", + "tencent-map-type-hybrid": "Hybrid", + "image-map-background": "Bakgrunnsbilde for kart", + "image-map-background-from-entity-attribute": "Hent kartbakgrunn fra enhetens attributt", + "image-url-source-entity-alias": "Bilde-URL kilde enhetsalias", + "image-url-source-entity-attribute": "Bilde-URL kilde enhetsattributt", + "common-map-settings": "Felles kartinnstillinger", + "x-pos-key-name": "X-posisjonsnøkkelnavn", + "y-pos-key-name": "Y-posisjonsnøkkelnavn", + "latitude-key-name": "Breddegrad-nøkkelnavn", + "longitude-key-name": "Lengdegrad-nøkkelnavn", + "default-map-zoom-level": "Standard kart-zoomnivå (0 - 20)", + "default-map-center-position": "Standard kart-senterposisjon (0,0)", + "disable-scroll-zooming": "Deaktiver zooming med rulling", + "disable-double-click-zooming": "Deaktiver zooming med dobbeltklikk", + "disable-zoom-control-buttons": "Deaktiver zoomkontrollknapper", + "fit-map-bounds": "Tilpass kartgrenser for å dekke alle markører", + "use-default-map-center-position": "Bruk standard kart-senterposisjon", + "entities-limit": "Grense for antall enheter som skal lastes", + "markers-settings": "Innstillinger for markører", + "marker-offset-x": "X-forskyvning for markør relativt til posisjon multiplisert med markørens bredde", + "marker-offset-y": "Y-forskyvning for markør relativt til posisjon multiplisert med markørens høyde", + "position-function": "Posisjonskonverteringsfunksjon, skal returnere x,y-koordinater som desimaltall fra 0 til 1 hver", + "draggable-marker": "Flyttbar markør", + "label": "Etikett", + "show-label": "Vis etikett", + "use-label-function": "Bruk etikettfunksjon", + "label-pattern": "Etikett (eksempel på mønster: '${entityName}', '${entityName}: (Tekst ${keyName} enheter.)')", + "label-function": "Etikettfunksjon", + "tooltip": "Verktøytips", + "show-tooltip": "Vis verktøytips", + "show-tooltip-action": "Handling for å vise verktøytips", + "show-tooltip-action-click": "Vis verktøytips ved klikk (Standard)", + "show-tooltip-action-hover": "Vis verktøytips ved hover", + "auto-close-tooltips": "Lukk verktøytips automatisk", + "use-tooltip-function": "Bruk verktøytipsfunksjon", + "tooltip-pattern": "Verktøytips (f.eks. 'Tekst ${keyName} enheter.' eller Lenketekst)", + "tooltip-function": "Verktøytipsfunksjon", + "tooltip-offset-x": "X-forskyvning for verktøytips relativt til markørens anker multiplisert med markørens bredde", + "tooltip-offset-y": "Y-forskyvning for verktøytips relativt til markørens anker multiplisert med markørens høyde", + "color": "Farge", + "use-color-function": "Bruk fargefunksjon", + "color-function": "Fargefunksjon", + "marker-image": "Markørbilde", + "use-marker-image-function": "Bruk markørbildefunksjon", + "custom-marker-image": "Egendefinert markørbilde", + "custom-marker-image-size": "Egendefinert markørbildestørrelse (px)", + "marker-image-function": "Markørbildefunksjon", + "marker-images": "Markørbilder", + "polygon-settings": "Innstillinger for polygon", + "show-polygon": "Vis polygon", + "polygon-key-name": "Polygon-nøkkelnavn", + "enable-polygon-edit": "Aktiver redigering av polygon", + "polygon-label": "Polygonetikett", + "show-polygon-label": "Vis polygonetikett", + "use-polygon-label-function": "Bruk polygonetikettfunksjon", + "polygon-label-pattern": "Polygonetikett (eksempel på mønster: '${entityName}', '${entityName}: (Tekst ${keyName} enheter.)')", + "polygon-label-function": "Polygonetikettfunksjon", + "polygon-tooltip": "Polygonverktøytips", + "show-polygon-tooltip": "Vis polygonverktøytips", + "auto-close-polygon-tooltips": "Lukk polygonverktøytips automatisk", + "use-polygon-tooltip-function": "Bruk polygonverktøytipsfunksjon", + "polygon-tooltip-pattern": "Verktøytips (f.eks. 'Tekst ${keyName} enheter.' eller Lenketekst)", + "polygon-tooltip-function": "Polygonverktøytipsfunksjon", + "polygon-color": "Polygonfarge", + "polygon-opacity": "Polygonopasitet", + "use-polygon-color-function": "Bruk polygonfargefunksjon", + "polygon-color-function": "Polygonfargefunksjon", + "polygon-stroke": "Polygonomriss", + "stroke-color": "Omrissfarge", + "stroke-opacity": "Omrissopasitet", + "stroke-weight": "Omrissvekt", + "use-polygon-stroke-color-function": "Bruk polygon omrissfargefunksjon", + "polygon-stroke-color-function": "Polygon omrissfargefunksjon", + "circle-settings": "Innstillinger for sirkel", + "show-circle": "Vis sirkel", + "circle-key-name": "Sirkel-nøkkelnavn", + "enable-circle-edit": "Aktiver redigering av sirkel", + "circle-label": "Sirkeletikett", + "show-circle-label": "Vis sirkeletikett", + "use-circle-label-function": "Bruk sirkeletikettfunksjon", + "circle-label-pattern": "Sirkeletikett (eksempel på mønster: '${entityName}', '${entityName}: (Tekst ${keyName} enheter.)')", + "circle-label-function": "Sirkeletikettfunksjon", + "circle-tooltip": "Sirkelverktøytips", + "show-circle-tooltip": "Vis sirkelverktøytips", + "auto-close-circle-tooltips": "Lukk sirkelverktøytips automatisk", + "use-circle-tooltip-function": "Bruk sirkelverktøytipsfunksjon", + "circle-tooltip-pattern": "Verktøytips (f.eks. 'Tekst ${keyName} enheter.' eller Lenketekst)", + "circle-tooltip-function": "Sirkelverktøytipsfunksjon", + "circle-fill-color": "Sirkel fyllfarge", + "circle-fill-color-opacity": "Sirkel fyllfarge opasitet", + "use-circle-fill-color-function": "Bruk sirkel fyllfargefunksjon", + "circle-fill-color-function": "Sirkel fyllfargefunksjon", + "circle-stroke": "Sirkelomriss", + "use-circle-stroke-color-function": "Bruk sirkel omrissfargefunksjon", + "circle-stroke-color-function": "Sirkel omrissfargefunksjon", + "markers-clustering-settings": "Innstillinger for markørklynging", + "use-map-markers-clustering": "Bruk kartmarkørklynging", + "zoom-on-cluster-click": "Zoom inn ved klikk på en klynge", + "max-cluster-zoom": "Maksimalt zoomnivå hvor en markør kan være del av en klynge (0 - 18)", + "max-cluster-radius-pixels": "Maksimal radius som en klynge dekker i piksler", + "cluster-zoom-animation": "Vis animasjon på markører ved zooming", + "show-markers-bounds-on-cluster-mouse-over": "Vis markørgrenser ved hover over en klynge", + "spiderfy-max-zoom-level": "Spred markører på maksimal zoomnivå (for å vise alle markører i en klynge)", + "load-optimization": "Optimalisering av lasting", + "cluster-chunked-loading": "Bruk blokker for å legge til markører slik at siden ikke fryser", + "cluster-markers-lazy-load": "Bruk treg lasting for å legge til markører", + "editor-settings": "Innstillinger for redigering", + "enable-snapping": "Aktiver snapping til andre punkter for presis tegning", + "init-draggable-mode": "Start kart i dra-modus", + "hide-all-edit-buttons": "Skjul alle redigeringsknapper", + "hide-draw-buttons": "Skjul tegneknapper", + "hide-edit-buttons": "Skjul redigeringsknapper", + "hide-remove-button": "Skjul fjern-knapp", + "route-map-settings": "Innstillinger for rutekart", + "trip-animation-settings": "Innstillinger for reiseanimasjon", + "normalization-step": "Normaliseringssteg for data (ms)", + "tooltip-background-color": "Bakgrunnsfarge for verktøytips", + "tooltip-font-color": "Skriftfarge for verktøytips", + "tooltip-opacity": "Opasitet for verktøytips (0-1)", + "auto-close-tooltip": "Lukk verktøytips automatisk", + "rotation-angle": "Sett ekstra rotasjonsvinkel for markør (grader)", + "path-settings": "Innstillinger for sti", + "path-color": "Stifarge", + "use-path-color-function": "Bruk stifargefunksjon", + "path-color-function": "Stifargefunksjon", + "path-decorator": "Stidekoratør", + "use-path-decorator": "Bruk stidekoratør", + "decorator-symbol": "Dekoratørsymbol", + "decorator-symbol-arrow-head": "Pil", + "decorator-symbol-dash": "Strek", + "decorator-symbol-size": "Størrelse på dekoratørsymbol (px)", + "use-path-decorator-custom-color": "Bruk egendefinert farge for stidekoratør", + "decorator-custom-color": "Egendefinert farge for dekoratør", + "decorator-offset": "Dekoratørforskyvning", + "end-decorator-offset": "Sluttforskyvning for dekoratør", + "decorator-repeat": "Gjenta dekoratør", + "points-settings": "Innstillinger for punkter", + "show-points": "Vis punkter", + "point-color": "Punktfarge", + "point-size": "Punktstørrelse (px)", + "use-point-color-function": "Bruk punktfargefunksjon", + "point-color-function": "Punktfargefunksjon", + "use-point-as-anchor": "Bruk punkt som anker", + "point-as-anchor-function": "Punkt som anker-funksjon", + "independent-point-tooltip": "Uavhengig punktverktøytips", + "clustering-markers": "Markørklynging", + "use-icon-create-function": "Bruk markørfargefunksjon", + "marker-color-function": "Markørfargefunksjon" + }, + "markdown": { + "use-markdown-text-function": "Bruk markdown/HTML-verdifunksjon", + "markdown-text-function": "Markdown/HTML-verdifunksjon", + "markdown-text-pattern": "Markdown/HTML-mønster (markdown eller HTML med variabler, for eksempel '${entityName} eller ${keyName} - noe tekst.')", + "apply-default-markdown-style": "Bruk standard markdown-stil", + "markdown-css": "Markdown/HTML CSS" + }, + "simple-card": { + "label": "Etikett", + "label-position": "Etikettposisjon", + "label-position-left": "Venstre", + "label-position-top": "Topp" + }, + "single-switch": { + "behavior": "Oppførsel", + "layout": "Oppsett", + "layout-right": "Høyre", + "layout-left": "Venstre", + "layout-centered": "Sentrert", + "auto-scale": "Automatisk skalering", + "label": "Etikett", + "icon": "Ikon", + "switch-color": "Farge på bryter", + "on": "På", + "off": "Av", + "disabled": "Deaktivert", + "tumbler-color": "Farge på bryterknapp", + "on-label": "På-etikett", + "off-label": "Av-etikett", + "switch": "Bryter" + }, + "slider": { + "behavior": "Oppførsel", + "initial-value": "Startverdi", + "initial-value-hint": "Handling for å hente startverdi for glidebryteren.", + "on-value-change": "Ved verdiendring", + "on-value-change-hint": "Handling som utløses når glidebryterens verdi endres.", + "layout": "Oppsett", + "layout-default": "Standard", + "layout-extended": "Utvidet", + "layout-simplified": "Forenklet", + "auto-scale": "Automatisk skalering", + "icon": "Ikon", + "value": "Verdi", + "range": "Område", + "min": "min", + "max": "maks", + "range-ticks": "Områdemarkeringer", + "tick-marks": "Merkestreker", + "colors": "Farger", + "main": "Hoved", + "background": "Bakgrunn", + "left-icon": "Venstre ikon", + "right-icon": "Høyre ikon", + "slider": "Glidebryter" + }, + "value-card": { + "layout": "Oppsett", + "layout-square": "Kvadratisk", + "layout-vertical": "Vertikalt", + "layout-centered": "Sentrert", + "layout-simplified": "Forenklet", + "layout-horizontal": "Horisontalt", + "layout-horizontal-reversed": "Horisontalt omvendt", + "label": "Etikett", + "icon": "Ikon", + "value": "Verdi", + "date": "Dato", + "value-card-style": "Stil for verdikort", + "auto-scale": "Automatisk skalering" + }, + "label-card": { + "auto-scale": "Automatisk skalering", + "label": "Etikett", + "icon": "Ikon", + "label-card-style": "Stil for etikettkort" + }, + "label-value-card": { + "value": "Verdi", + "label-value-card-style": "Stil for etikett og verdi-kort" + }, + "liquid-level-card": { + "layout-simple": "Enkel", + "layout-percentage": "Prosent", + "layout-absolute": "Absolutt", + "layout": "Oppsett", + "background-overlay": "Bakgrunnsoverlegg for verdi", + "total-volume": "Totalvolum", + "total-volume-units": "Enheter for totalvolum", + "tank": "Tank", + "shape": "Form", + "datasource-units": "Kildeenheter", + "widget-units": "Widget-enheter", + "decimals": "Desimaler", + "liquid": "Væske", + "liquid-color": "Væskefarge", + "value": "Verdi", + "value-font": "Skrift for verdi", + "level": "Nivå", + "last-update": "Siste oppdatering", + "shape-by-attribute": "Angi tankform via attributtnavn", + "tooltip-background": "Bakgrunnsfarge", + "background-blur": "Bakgrunnsuskarphet", + "tank-color": "Tankfarge", + "static": "Statisk", + "see-examples": "Se eksempler", + "attribute": "Attributt", + "shape-type": "Type", + "v-oval": "Vertikal oval", + "v-cylinder": "Vertikal sylinder", + "v-capsule": "Vertikal kapsel", + "rectangle": "Rektangel", + "h-oval": "Horisontal oval", + "h-ellipse": "Horisontal ellipse", + "h-dish-ends": "Horisontale bueender", + "h-cylinder": "Horisontal sylinder", + "h-capsule": "Horisontal kapsel", + "h-elliptical_2_1": "Horisontal 2:1 elliptisk", + "icon": "Kortikon", + "title": "Korttittel", + "units": "Enheter", + "color-and-font": "Farge og skrift", + "shape-attribute-name": "Attributtnavn", + "total-volume-required": "Totalvolum er påkrevd.", + "attribute-name-required": "Attributtnavn er påkrevd.", + "attribute-key-not-set": "Attributtnøkkel for '{{attributeName}}' er ikke satt", + "attribute-key-invalid": "Attributtnøkkel for '{{attributeName}}' er ugyldig" + }, + "aggregated-value-card": { + "subtitle": "Undertittel", + "chart": "Diagram", + "values": "Verdier", + "value-appearance": "Utseende for verdi", + "position": "Posisjon", + "position-center": "Senter", + "position-right-top": "Høyre øverst", + "position-right-bottom": "Høyre nederst", + "position-left-top": "Venstre øverst", + "position-left-bottom": "Venstre nederst", + "font": "Skrift", + "color": "Farge", + "arrow": "Pil", + "display-up-down-arrow": "Vis opp-/ned-pil", + "add-value": "Legg til verdi", + "remove-value": "Fjern verdi", + "no-values": "Ingen verdier konfigurert", + "aggregation": "Aggregering", + "aggregated-value-card-style": "Stil for aggregert verdikort", + "auto-scale": "Automatisk skalering" + }, + "value-chart-card": { + "layout": "Oppsett", + "layout-left": "Venstre", + "layout-right": "Høyre", + "auto-scale": "Automatisk skalering", + "icon": "Ikon", + "value": "Verdi", + "chart": "Diagram", + "value-chart-card-style": "Stil for verdidiagramkort" + }, + "progress-bar": { + "layout": "Oppsett", + "layout-default": "Standard", + "layout-simplified": "Forenklet", + "auto-scale": "Automatisk skalering", + "icon": "Ikon", + "value": "Verdi", + "range": "Område", + "min": "min", + "max": "maks", + "range-ticks": "Område-tikk", + "bar": "Stolpe", + "bar-color": "Stolpefarge", + "bar-background": "Stolpebakgrunn", + "progress-bar-card-style": "Stil for fremdriftsindikator" + }, + "notification": { + "max-notification-display": "Maksimalt antall varsler som vises", + "counter": "Teller", + "counter-hint": "Teller vises hvis «Widget-tittel» er aktivert", + "icon": "Ikon", + "counter-value": "Verdi", + "counter-color": "Farge", + "notification-button": "Varslingsknapper", + "button-view-all": "Vis alle", + "button-filter": "Filtrer", + "type-filter": "Typefilter", + "button-mark-read": "Merk alle som lest", + "notification-types": "Varslingstyper", + "notification-type": "Varslingstype", + "search-type": "Søk etter type", + "any-type": "Alle typer" + }, + "alarm-count": { + "alarm-count-card-style": "Stil for alarmantallkort" + }, + "entity-count": { + "entity-count-card-style": "Stil for enhetsantallkort" + }, + "count": { + "layout": "Oppsett", + "layout-column": "Kolonne", + "layout-row": "Rad", + "label": "Etikett", + "icon": "Ikon", + "icon-background": "Ikonbakgrunn", + "value": "Verdi", + "chevron": "Chevron", + "auto-scale": "Automatisk skalering" + }, + "table": { + "common-table-settings": "Vanlige tabellinnstillinger", + "enable-search": "Aktiver søk", + "enable-sticky-header": "Vis alltid topptekst", + "enable-sticky-action": "Vis alltid handlingskolonne", + "hidden-cell-button-display-mode": "Visningsmodus for skjulte celleknapper", + "show-empty-space-hidden-action": "Vis tomt område i stedet for skjult cellehandling", + "dont-reserve-space-hidden-action": "Ikke reserver plass til skjulte handlingsknapper", + "display-timestamp": "Tidsstempel", + "display-pagination": "Vis paginering", + "default-page-size": "Standard sidestørrelse", + "page-step-settings": "Sideinnstillinger", + "page-step-count": "Antall trinn", + "page-step-increment": "Trinnøkning", + "page-step-count-format-message": "Må være et heltall mellom 1 og 100.", + "page-step-increment-format-message": "Må være et heltall større enn eller lik 1.", + "use-entity-label-tab-name": "Bruk enhetens etikett i fanetittelen", + "hide-empty-lines": "Skjul tomme linjer", + "row-style": "Radstil", + "use-row-style-function": "Bruk radstilfunksjon", + "row-style-function": "Radstilfunksjon", + "cell-style": "Cellestil", + "use-cell-style-function": "Bruk cellestilfunksjon", + "cell-style-function": "Cellestilfunksjon", + "cell-content": "Celleinnhold", + "use-cell-content-function": "Bruk celleinnholdsfunksjon", + "cell-content-function": "Celleinnholdsfunksjon", + "show-latest-data-column": "Vis siste data-kolonne", + "latest-data-column-order": "Rekkefølge på siste data-kolonne", + "entities-table-title": "Tittel for enhetstabell", + "enable-select-column-display": "Aktiver valg av kolonner som skal vises", + "display-entity-name": "Vis kolonne for enhetsnavn", + "entity-name-column-title": "Tittel på enhetsnavn-kolonne", + "display-entity-label": "Vis kolonne for enhetsetikett", + "entity-label-column-title": "Tittel på enhetsetikett-kolonne", + "display-entity-type": "Vis kolonne for enhetstype", + "default-sort-order": "Standard sorteringsrekkefølge", + "custom-title": "Egendefinert overskrift", + "column-width": "Kolonnebredde (px eller %)", + "default-column-visibility": "Standard kolonne-synlighet", + "column-visibility-visible": "Synlig", + "column-visibility-hidden": "Skjult", + "column-visibility-hidden-mobile": "Skjult i mobilmodus", + "column-selection-to-display": "Kolonnevalg i «Kolonner som skal vises»", + "column-selection-to-display-enabled": "Aktivert", + "column-selection-to-display-disabled": "Deaktivert", + "alarms-table-title": "Tittel for alarmtabell", + "enable-alarms-selection": "Aktiver valg av alarmer", + "enable-alarms-search": "Aktiver alarmsøk", + "enable-alarm-filter": "Aktiver alarmfilter", + "display-alarm-details": "Vis alarmdetaljer", + "allow-alarms-ack": "Tillat bekreftelse av alarmer", + "allow-alarms-clear": "Tillat fjerning av alarmer", + "display-alarm-activity": "Vis alarmaktivitet", + "allow-alarms-assign": "Tillat tildeling av alarmer", + "columns": "Kolonner", + "column-settings": "Kolonneinnstillinger", + "remove-column": "Fjern kolonne", + "add-column": "Legg til kolonne", + "no-columns": "Ingen kolonner konfigurert", + "columns-to-display": "Kolonner som skal vises", + "table-header": "Tabelloverskrift", + "header-buttons": "Overskriftsknapper", + "table-buttons": "Tabellknapper", + "pagination": "Paginering", + "rows": "Rader", + "timeseries-column-error": "Minst én tidsseriekolonne må angis", + "alarm-column-error": "Minst én alarmkolonne må angis", + "table-tabs": "Tabellfaner", + "show-cell-actions-menu-mobile": "Vis rullegardinmeny for cellehandlinger i mobilmodus", + "disable-sorting": "Deaktiver sortering" + }, + "latest-chart": { + "total": "Totalt", + "auto-scale": "Auto-skala", + "clockwise-layout": "Med klokken", + "sort-series": "Sorter serier etter etikett", + "tooltip-value-type-absolute": "Absolutt", + "tooltip-value-type-percentage": "Prosent" + }, + "pie-chart": { + "pie-chart-appearance": "Kakediagramutseende", + "label": "Etikett", + "border": "Kantlinje", + "radius": "Radius", + "pie-chart-card-style": "Kakediagramkortstil" + }, + "radar-chart": { + "radar-appearance": "Radarutseende", + "shape": "Form", + "shape-polygon": "Polygon", + "shape-circle": "Sirkel", + "color": "Farge", + "line": "Linje", + "points": "Punkter", + "points-label": "Punktetikett", + "radar-axis": "Radarakse", + "axis-label": "Aksesetikett", + "ticks-label": "Tikkeetikett", + "radar-chart-style": "Radar-diagramstil" + }, + "time-series-chart": { + "chart": "Diagram", + "chart-style": "Diagramstil", + "data-zoom": "Datainnzooming", + "stack-mode": "Stablemodus", + "stack-mode-hint": "Stabler serier i diagrammet. Serier med samme enhet blir stablet oppå hverandre.", + "axes": "Akser", + "y-axes": "Y-akser", + "line-type": "Linjetype", + "line-width": "Linjebredde", + "type-line": "Linje", + "type-bar": "Søyle", + "type-point": "Punkt", + "no-aggregation-bar-width-strategy": "Strategi for søylebredde for ikke-aggregert data", + "no-aggregation-bar-width-strategy-group": "Grupper", + "no-aggregation-bar-width-strategy-separate": "Separate", + "bar-group-width": "Gruppebredde for søyler", + "bar-width": "Søylebredde", + "bar-width-relative": "Prosentandel av tidsvinduet", + "bar-width-absolute": "Absolutt (ms)", + "comparison": { + "comparison": "Sammenligning", + "comparison-hint": "Sammenligning fungerer kun med historiske data!", + "show": "Vis", + "settings": "Innstillinger for sammenligning", + "show-values-for-comparison": "Vis historiske data for sammenligning", + "comparison-values-label": "Etikett for sammenligningsnøkkel", + "comparison-values-label-auto": "Auto", + "comparison-data-color": "Farge for sammenligningsdata" + }, + "threshold": { + "thresholds": "Terskler", + "source": "Kilde", + "key-value": "Nøkkel / Verdi", + "no-thresholds": "Ingen terskler konfigurert", + "add-threshold": "Legg til terskel", + "type-constant": "Konstant", + "type-latest-key": "Nøkkel", + "type-entity": "Enhet", + "threshold-settings": "Terskelinnstillinger", + "remove-threshold": "Fjern terskel", + "threshold-value-required": "Terskelverdi er påkrevd.", + "key-required": "Nøkkel er påkrevd.", + "entity-key-required": "Enhetsnøkkel er påkrevd.", + "line-appearance": "Linjeutseende", + "line-color": "Linjefarge", + "start-symbol": "Startsymbol", + "end-symbol": "Sluttsymbol", + "symbol-size": "Størrelse", + "label": "Etikett", + "label-position-start": "Start", + "label-position-middle": "Midt", + "label-position-end": "Slutt", + "label-position-inside-start": "Innsiden start", + "label-position-inside-start-top": "Innsiden start topp", + "label-position-inside-start-bottom": "Innsiden start bunn", + "label-position-inside-middle": "Innsiden midt", + "label-position-inside-middle-top": "Innsiden midt topp", + "label-position-inside-middle-bottom": "Innsiden midt bunn", + "label-position-inside-end": "Innsiden slutt", + "label-position-inside-end-top": "Innsiden slutt topp", + "label-position-inside-end-bottom": "Innsiden slutt bunn", + "label-background": "Bakgrunn for etikett" + }, + "state": { + "states": "Tilstander", + "label": "Etikett", + "ticks-value": "Tikkverdier", + "source": "Kilde", + "value-range": "Verdi / Område", + "no-states": "Ingen tilstander konfigurert", + "add-state": "Legg til tilstand", + "type-constant": "Konstant", + "type-range": "Område", + "from": "Fra", + "to": "Til", + "remove-state": "Fjern tilstand" + }, + "grid": { + "grid": "Rutenett", + "background-color": "Bakgrunnsfarge", + "border": "Kantlinje" + }, + "axis": { + "axes": "Akser", + "x-axis": "X-akse", + "y-axis": "Y-akse", + "y-axis-settings": "Y-akseinnstillinger", + "comparison-x-axis-settings": "Sammenligningsinnstillinger for X-akse", + "remove-y-axis": "Fjern Y-akse", + "id": "ID", + "label": "Etikett", + "position": "Posisjon", + "position-left": "Venstre", + "position-right": "Høyre", + "position-top": "Topp", + "position-bottom": "Bunn", + "tick-labels": "Tikketiketter", + "ticks-formatter-function": "Formatfunksjon for tikker", + "ticks-generator-function": "Generatorfunksjon for tikker", + "show-ticks": "Vis tikker", + "show-line": "Vis linje", + "show-split-lines": "Vis delingslinjer", + "show-split-lines-x-axis-hint": "Hvis aktivert, vises vertikale linjer i diagrammet.", + "show-split-lines-y-axis-hint": "Hvis aktivert, vises horisontale linjer i diagrammet.", + "ticks-interval": "Tikkerintervall", + "ticks-interval-hint": "Tving angitt segmenteringsintervall for aksen.", + "split-number": "Antall inndelinger", + "split-number-hint": "Antall segmenter aksen deles i.", + "min": "Min", + "max": "Maks", + "show": "Vis", + "add-y-axis": "Legg til Y-akse" + }, + "series": { + "legend-settings": "Innstillinger for tegnforklaring", + "show-in-legend": "Vis i tegnforklaring", + "show-in-legend-hint": "Vis serienavn og data i tegnforklaringen.", + "hidden-by-default": "Skjult som standard", + "hidden-by-default-hint": "Gjør serien skjult i tegnforklaringen som standard.", + "series-type": "Serietype", + "type": "Type", + "type-line": "Linje", + "type-bar": "Søyle", + "line": { + "line": "Linje", + "show-line": "Vis linje", + "step-line": "Trappetrinn-linje", + "step-type-start": "Start", + "step-type-middle": "Midt", + "step-type-end": "Slutt", + "smooth-line": "Jevn linje" + }, + "point": { + "points": "Punkter", + "show-points": "Vis punkter", + "point-label": "Punktetikett", + "point-label-hint": "Vis etikett med verdi over punktet i serien.", + "point-label-background": "Bakgrunn for punktetikett", + "point-shape": "Punktform", + "point-size": "Punktstørrelse" + } + } + }, + "wind-speed-direction": { + "layout": "Oppsett", + "layout-default": "Standard", + "layout-advanced": "Avansert", + "layout-simplified": "Forenklet", + "values": "Verdier", + "wind-direction": "Vindretning", + "center-value": "Senterverdi", + "icon": "Ikon", + "arrow": "Pil", + "ticks": "Tikkmerker", + "labels-type": "Etikettype", + "directional-names": "Retningsnavn", + "degrees": "Grader", + "major-ticks": "Hovedtikkmerker", + "minor-ticks": "Småtikkmerker", + "wind-speed-direction-card-style": "Stil for kort for vindhastighet og retning", + "ticks-color": "Farge på tikkmerker", + "ticks-labels-type": "Tikkmerkeetikettstype", + "arrow-color": "Pilfarge" + }, + "value-source": { + "value-source": "Verdikilde", + "predefined-value": "Konstant", + "entity-attribute": "Enhetsattributt", + "value": "Verdi", + "value-required": "Verdi er påkrevd.", + "key-required": "Nøkkel er påkrevd.", + "entity-key-required": "Enhetsnøkkel er påkrevd.", + "source-entity-alias": "Alias for kildeenhet", + "source-entity-attribute": "Attributt for kildeenhet", + "type-constant": "Konstant", + "type-latest-key": "Nøkkel", + "type-entity": "Enhet" + }, + "rpc-state": { + "initial-state": "Initial tilstand", + "initial-state-hint": "Handling for å hente komponentens starttilstand (På/Av).", + "disabled-state": "Deaktivert tilstand", + "disabled-state-hint": "Konfigurer betingelse som deaktiverer komponenten.", + "turn-on": "Slå 'På'", + "turn-on-hint": "Handling utløst når bryteren slås til 'På'", + "turn-off": "Slå 'Av'", + "turn-off-hint": "Handling utløst når bryteren slås til 'Av'", + "on": "På", + "off": "Av", + "disabled": "Deaktivert" + }, + "value-action": { + "do-nothing": "Ikke gjør noe", + "execute-rpc": "Utfør RPC", + "get-attribute": "Hent attributt", + "set-attribute": "Angi attributt", + "get-time-series": "Hent tidsserie", + "get-alarm-status": "Hent alarmstatus", + "get-dashboard-state": "Hent dashboard state ID", + "get-dashboard-state-object": "Hent dashboard state-objekt", + "add-time-series": "Legg til tidsserie", + "execute-rpc-text": "Utfør RPC-metode '{{methodName}}'", + "get-time-series-text": "Bruk tidsserie '{{key}}'", + "get-attribute-text": "Bruk attributt '{{key}}'", + "get-alarm-status-text": "Bruk alarmstatus", + "get-dashboard-state-text": "Bruk dashboard state", + "get-dashboard-state-object-text": "Bruk dashboard state-objekt", + "when-dashboard-state-is-text": "Når dashboard state ID er '{{state}}'", + "when-dashboard-state-function-is-text": "Når f(dashboard state ID) er '{{state}}'", + "when-dashboard-state-object-function-is-text": "Når f(dashboard state-objekt) er '{{state}}'", + "set-attribute-to-value-text": "Angi attributt '{{key}}' til: {{value}}", + "add-time-series-value-text": "Legg til tidsserieverdi '{{key}}': {{value}}", + "set-attribute-text": "Angi attributt '{{key}}'", + "add-time-series-text": "Legg til tidsserie '{{key}}'", + "action": "Handling", + "value": "Verdi", + "init-value-hint": "Verdi som angis frem til enheten sender data.", + "method": "Metode", + "method-name-required": "Metodenavn er påkrevd.", + "request-timeout-ms": "Timeout for RPC-forespørsel (ms)", + "request-timeout-required": "Timeout for forespørsel er påkrevd.", + "min-request-timeout-error": "Timeout må være større enn eller lik 5000 ms (5 sekunder).", + "request-persistent": "Vedvarende RPC-forespørsel", + "persistent-polling-interval": "Pollingintervall for vedvarende RPC (ms)", + "persistent-polling-interval-hint": "Pollingintervall (ms) for å hente respons fra vedvarende RPC-kommando", + "persistent-polling-interval-required": "Pollingintervall er påkrevd.", + "min-persistent-polling-interval-error": "Pollingintervall må være større enn eller lik 1000 ms (1 sekund).", + "attribute-scope": "Attributtomfang", + "attribute-key": "Attributtnøkkel", + "attribute-key-required": "Attributtnøkkel er påkrevd.", + "time-series-key": "Tidsserienøkkel", + "time-series-key-required": "Tidsserienøkkel er påkrevd.", + "action-result-converter": "Omformer for handlingsresultat", + "converter-none": "Ingen", + "converter-function": "Funksjon", + "converter-constant": "Konstant", + "converter-value": "Verdi", + "parse-value-function": "Funksjon for å tolke verdi", + "state-when-result-is": "'{{state}}' når resultatet er", + "parameters": "Parametere", + "convert-value-function": "Funksjon for å konvertere verdi", + "error": { + "target-entity-is-not-set": "Målenhet er ikke angitt!", + "failed-to-perform-action": "Kunne ikke utføre {{ actionLabel }}-handlingen.", + "invalid-attribute-scope": "Omfanget '{{scope}}' støttes ikke av enhetstypen {{entityType}}." + } + }, + "widget-font": { + "font-settings": "Skriftinnstillinger", + "font-family": "Skriftfamilie", + "size": "Størrelse", + "relative-font-size": "Relativ skriftstørrelse (prosent)", + "font-style": "Stil", + "font-style-normal": "Normal", + "font-style-italic": "Kursiv", + "font-style-oblique": "Skrå", + "font-weight": "Tykkelse", + "font-weight-normal": "Normal", + "font-weight-bold": "Fet", + "font-weight-bolder": "Fetere", + "font-weight-lighter": "Tynnere", + "color": "Farge", + "shadow-color": "Skyggefarge", + "preview": "Forhåndsvisning", + "line-height": "Linjehøyde", + "auto": "Auto" + }, + "home": { + "no-data-available": "Ingen data tilgjengelig" + }, + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "Disk", + "cpu-warning-text": "Høyt CPU-forbruk. For å unngå systemfeil, optimaliser systemytelsen.", + "cpu-critical-text": "Kritisk høyt CPU-forbruk. For å unngå systemfeil, optimaliser systemytelsen.", + "ram-warning-text": "Lite tilgjengelig RAM. For å unngå systemfeil, optimaliser ytelsen eller øk RAM-størrelsen.", + "ram-critical-text": "Kritisk lite tilgjengelig RAM. For å unngå systemfeil, optimaliser ytelsen eller øk RAM-størrelsen.", + "disk-warning-text": "Lite tilgjengelig diskplass. For å unngå datatap, frigjør eller utvid diskplassen.", + "disk-critical-text": "Kritisk lite diskplass. For å unngå datatap, frigjør eller utvid diskplassen." + }, + "cluster-info": { + "service-id": "Tjeneste-ID", + "service-type": "Tjenestetype", + "no-data": "Ingen data" + }, + "transport-messages": { + "title": "Transportmeldinger", + "info": "Alle meldinger mottatt fra enheter" + }, + "activity": { + "title": "Aktivitet" + }, + "documentation": { + "title": "Dokumentasjon", + "add-link": "Legg til lenke", + "add-link-title": "Legg til dokumentasjonslenke", + "name": "Navn", + "name-required": "Navn er påkrevd.", + "link": "Lenke", + "link-required": "Lenke er påkrevd.", + "columns": "Kolonner" + }, + "quick-links": { + "title": "Hurtiglenker", + "add-link": "Legg til lenke", + "add-link-title": "Legg til hurtiglenke", + "quick-link": "Hurtiglenke", + "quick-link-required": "Hurtiglenke er påkrevd.", + "no-links-matching": "Ingen lenker samsvarer med '{{name}}'.", + "columns": "Kolonner" + }, + "recent-dashboards": { + "title": "Dashbord", + "last": "Sist vist", + "starred": "Favorittmerket", + "name": "Navn", + "last-viewed": "Sist vist", + "no-last-viewed-dashboards": "Ingen nylig viste dashbord" + }, + "configured-features": { + "title": "Konfigurerte funksjoner", + "info": "Status for funksjoner som krever konfigurasjon", + "email-feature": "E-post", + "sms-feature": "SMS", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "feature-configured": "Funksjon er konfigurert.\nKlikk for å sette opp", + "feature-not-configured": "Funksjon er ikke konfigurert.\nKlikk for å sette opp" + }, + "version-info": { + "title": "Versjon", + "contact-us": "Kontakt oss", + "current-version": "Gjeldende versjon", + "current": "Nåværende", + "available-version": "Tilgjengelig versjon", + "available": "Tilgjengelig", + "upgrade": "Oppgrader", + "version-is-up-to-date": "Versjonen er oppdatert" + }, + "usage-info": { + "title": "Bruk", + "entities": "Enheter", + "api-calls": "API-kall" + }, + "functions": { + "title": "Funksjoner", + "pe-feature-tooltip": "Kun i ThingsBoard\nProfessional Edition", + "switch-to-pe": "Bytt til PE", + "alarms": "Alarmer", + "dashboards": "Dashbord", + "entities-and-relations": "Enheter og relasjoner", + "profiles": "Profiler", + "advanced-features": "Avanserte funksjoner", + "notification-center": "Varslingssenter", + "api-usage": "API-bruk", + "customers": "Kunder", + "customers-hierarchy": "Kundehierarki", + "roles-and-permissions": "Roller og tillatelser", + "groups": "Grupper", + "integrations": "Integrasjoner", + "solution-templates": "Løsningsmaler", + "scheduler": "Planlegger", + "white-labeling": "White-labeling" + }, + "devices": { + "view-docs": "Vis dokumentasjon", + "inactive": "Inaktiv", + "active": "Aktiv", + "total": "Totalt" + }, + "alarms": { + "critical": "Kritisk", + "assigned-to-me": "Tildelt meg", + "total": "Totalt" + }, + "getting-started": { + "get-started": "Kom i gang", + "finish": "Fullfør", + "done-welcome-title": "Velkommen om bord", + "done-welcome-text": "Du klarte det flott!", + "sys-admin": { + "step1": { + "title": "Opprett leietaker og leietakeradministrator", + "content": "

En leietaker er en person eller organisasjon som eier eller produserer enheter og eiendeler. Leietakeren kan ha flere leietakeradministratorer, kunder, enheter og eiendeler.

Leietakeradministratoren kan opprette og administrere enheter, eiendeler, kunder og dashbord i leietakeren sin konto.

Følg dokumentasjonen for hvordan du gjør det:

", + "how-to-create-tenant": "Hvordan opprette leietaker og leietakeradministrator" + }, + "step2": { + "title": "Konfigurer funksjon: E-postserver", + "content": "

Konfigurasjon av e-postserver er viktig for aktivering av brukere, gjenoppretting av passord og levering av alarmvarsler.

Følg dokumentasjonen for hvordan du gjør det:

", + "how-to-configure-mail-server": "Hvordan konfigurere e-postserver" + }, + "step3": { + "title": "Konfigurer funksjon: SMS-leverandør", + "content": "

Konfigurer SMS-leverandører for å varsle kunder om alarmer via SMS.

Følg dokumentasjonen for hvordan du gjør det:

", + "how-to-configure-sms-provider": "Hvordan konfigurere SMS-leverandør" + }, + "step4": { + "title": "Konfigurer funksjon: White-labeling", + "content": "

Tilpass logo og fargepalett for ditt firma eller produkt uten koding og uten å måtte restarte tjenesten.

Følg dokumentasjonen for hvordan du gjør det:

" + }, + "step5": { + "title": "Konfigurer funksjon: 2FA", + "content": "

Forbedre sikkerheten på plattformens kontoer med tofaktorautentisering.

Følg dokumentasjonen for hvordan du gjør det:

" + }, + "step6": { + "title": "Konfigurer funksjon: OAuth 2", + "content": "

Forenkle innloggingen for leietakere og kundebrukere med Single Sign-On via OAuth 2.0.

Følg dokumentasjonen for hvordan du gjør det:

" + } + }, + "tenant-admin": { + "step1": { + "title": "Opprett enhet", + "content": "

La oss provisjonere din første enhet til plattformen via brukergrensesnittet. Følg dokumentasjonen for hvordan du gjør det:

", + "how-to-create-device": "Hvordan opprette enhet" + }, + "step2": { + "title": "Koble til enhet", + "content-before": "

For å koble til enheten må du hente legitimasjon. Vi anbefaler å bruke automatisk genererte legitimasjonsdata – tilgangstoken – i denne veiledningen.

  • Gå til enhetstabellen
  • Klikk på enheten for å åpne detaljene
  • Trykk på knappen «Kopier tilgangstoken»

Bruk enkle kommandoer for å publisere data via HTTP. Husk å erstatte $ACCESS_TOKEN med tilgangstokenet for enheten:

", + "ubuntu": { + "install-curl": "Installer cURL for Ubuntu:" + }, + "macos": { + "install-curl": "Installer cURL for MacOS:" + }, + "windows": { + "install-curl": "Fra og med Windows 10 b17063 er cURL tilgjengelig som standard." + }, + "replace-access-token": "Erstatt $ACCESS_TOKEN med din enhets token:", + "content-after": "

Du kan også bruke andre protokoller som MQTT, CoAP osv.

Følg dokumentasjonen for hvordan du gjør det:

", + "how-to-connect-device": "Hvordan koble til enhet" + }, + "step3": { + "title": "Opprett dashbord", + "content": "

Opprett et dashbord for å visualisere data fra enheter, eiendeler osv.

Følg dokumentasjonen for hvordan du gjør det:

", + "how-to-create-dashboard": "Hvordan opprette dashbord" + }, + "step4": { + "title": "Konfigurer alarmregler", + "alarm-rules": "Alarmregler", + "content": "

La oss utløse en alarm når temperaturen når 25°C. Følg dokumentasjonen for hvordan du gjør det:

", + "how-to-configure-alarm-rules": "Hvordan konfigurere alarmregler" + }, + "step5": { + "title": "Opprett alarm", + "content-before": "

For å utløse alarmen, send en ny telemetrverdi på 26°C eller høyere.

", + "replace-access-token": "Erstatt $ACCESS_TOKEN med din enhets token:", + "content-after": "

Følg dokumentasjonen for hvordan du gjør det:

", + "how-to-create-alarm": "Hvordan opprette alarm" + }, + "step6": { + "title": "Opprett kunde og del dashbord", + "content": "

Ved å opprette dashbord for sluttbruker, kan kundebrukeren bare se sine egne enheter og data. Data fra andre kunder vil være skjult.

Følg dokumentasjonen for hvordan du gjør det:

" + } + } + } + }, + "icon": { + "icon": "Ikon", + "icons": "Ikoner", + "select-icon": "Velg ikon", + "material-icons": "Materialikoner", + "show-all": "Vis alle ikoner", + "search-icon": "Søk ikon", + "no-icons-found": "Ingen ikoner funnet for '{{iconSearch}}'" + }, + "phone-input": { + "phone-input-label": "Telefonnummer", + "phone-input-required": "Telefonnummer er påkrevd", + "phone-input-validation": "Telefonnummeret er ugyldig eller ikke mulig", + "phone-input-pattern": "Ugyldig telefonnummer. Skal være i E.164-format, f.eks. {{phoneNumber}}", + "phone-input-hint": "Telefonnummer i E.164-format, f.eks. {{phoneNumber}}" + }, + "custom": { + "widget-action": { + "action-cell-button": "Handlingscelleknapp", + "row-click": "Ved radklikk", + "cell-click": "Ved celleklikk", + "polygon-click": "Ved polygonklikk", + "marker-click": "Ved markørklikk", + "circle-click": "Ved sirkelklikk", + "tooltip-tag-action": "Verktøytips tag-handling", + "node-selected": "Ved valg av node", + "element-click": "Ved HTML-elementklikk", + "pie-slice-click": "Ved sektorklikk", + "row-double-click": "Ved dobbeltklikk på rad", + "cell-double-click": "Ved dobbeltklikk på celle", + "card-click": "Ved kortklikk", + "click": "Ved klikk" + } + }, + "paginator": { + "items-per-page": "Elementer per side:", + "first-page-label": "Første side", + "last-page-label": "Siste side", + "next-page-label": "Neste side", + "previous-page-label": "Forrige side", + "items-per-page-separator": "av" + }, + "language": { + "language": "Språk" + } +} From 6257bb7d54d40280cfeecb0cd7d18b7eac58848e Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 3 Jun 2025 11:26:38 +0300 Subject: [PATCH 269/335] UI: Fixed SCADA connectors arrow color --- .../data/json/system/scada_symbols/horizontal-connector-hp.svg | 2 +- .../json/system/scada_symbols/long-horizontal-connector-hp.svg | 2 +- .../json/system/scada_symbols/long-vertical-connector-hp.svg | 2 +- .../data/json/system/scada_symbols/vertical-connector-hp.svg | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg index f2fc9fb2e0..fc3fbc9ec7 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg @@ -7,7 +7,7 @@ "tags": [ { "tag": "arrow", - "stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}", + "stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n element.stroke(ctx.properties.lineColor).fill(ctx.properties.lineColor);\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg index f5f68c4934..c4bd3ce804 100644 --- a/application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg @@ -7,7 +7,7 @@ "tags": [ { "tag": "arrow", - "stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}", + "stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n element.stroke(ctx.properties.lineColor).fill(ctx.properties.lineColor);\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg index f344823082..b51c91bf92 100644 --- a/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg @@ -7,7 +7,7 @@ "tags": [ { "tag": "arrow", - "stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}", + "stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n element.stroke(ctx.properties.lineColor).fill(ctx.properties.lineColor);\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg index 87c3ef23ee..a47689be2b 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg @@ -7,7 +7,7 @@ "tags": [ { "tag": "arrow", - "stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}", + "stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n element.stroke(ctx.properties.lineColor).fill(ctx.properties.lineColor);\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}", "actions": null }, { From 46a58ca82bb17c4434de39c52e34203dfd8fd417 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 3 Jun 2025 12:47:55 +0300 Subject: [PATCH 270/335] Edqs - VersionStore - Use local cache instead of caffeine to reduce memory heap size --- .../server/edqs/processor/EdqsProcessor.java | 1 + .../server/edqs/util/VersionsStore.java | 47 +++++++++++++++---- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/processor/EdqsProcessor.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/processor/EdqsProcessor.java index 510d2c3a41..0e74cb98fa 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/processor/EdqsProcessor.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/processor/EdqsProcessor.java @@ -277,6 +277,7 @@ public class EdqsProcessor implements TbQueueHandler, eventConsumer.awaitStop(); responseTemplate.stop(); stateService.stop(); + versionsStore.shutdown(); } } diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java index ba3263eec2..9d4c67c4c2 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java @@ -15,31 +15,35 @@ */ package org.thingsboard.server.edqs.util; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.edqs.EdqsObjectKey; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @Slf4j public class VersionsStore { - private final Cache versions; + private final ConcurrentMap> versions = new ConcurrentHashMap<>(); + private final long expirationMillis; + private final ScheduledExecutorService cleaner = Executors.newSingleThreadScheduledExecutor(); public VersionsStore(int ttlMinutes) { - this.versions = Caffeine.newBuilder() - .expireAfterWrite(ttlMinutes, TimeUnit.MINUTES) - .build(); + this.expirationMillis = TimeUnit.MINUTES.toMillis(ttlMinutes); + startCleanupTask(); } public boolean isNew(EdqsObjectKey key, Long version) { AtomicBoolean isNew = new AtomicBoolean(false); - versions.asMap().compute(key, (k, prevVersion) -> { - if (prevVersion == null || prevVersion <= version) { + versions.compute(key, (k, prevVersion) -> { + if (prevVersion == null || prevVersion.value <= version) { isNew.set(true); - return version; + return new TimedValue<>(version); } else { log.debug("[{}] Version {} is outdated, the latest is {}", key, version, prevVersion); return prevVersion; @@ -48,4 +52,29 @@ public class VersionsStore { return isNew.get(); } + private void startCleanupTask() { + cleaner.scheduleAtFixedRate(() -> { + long now = System.currentTimeMillis(); + for (Map.Entry> entry : versions.entrySet()) { + if (now - entry.getValue().lastUpdated > expirationMillis) { + versions.remove(entry.getKey(), entry.getValue()); + } + } + }, expirationMillis, expirationMillis, TimeUnit.MILLISECONDS); + } + + public void shutdown() { + cleaner.shutdown(); + } + + private static class TimedValue { + private final long lastUpdated; + private final V value; + + public TimedValue(V value) { + this.value = value; + this.lastUpdated = System.currentTimeMillis(); + } + } + } From ccdcbc635043bfb05628612d0179e03cfd9bfb18 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 3 Jun 2025 15:47:19 +0300 Subject: [PATCH 271/335] VersionsStore - use long intead of Long to decrease heap size --- .../thingsboard/server/edqs/util/VersionsStore.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java index 9d4c67c4c2..c8c8f76761 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java @@ -29,7 +29,7 @@ import java.util.concurrent.atomic.AtomicBoolean; @Slf4j public class VersionsStore { - private final ConcurrentMap> versions = new ConcurrentHashMap<>(); + private final ConcurrentMap versions = new ConcurrentHashMap<>(); private final long expirationMillis; private final ScheduledExecutorService cleaner = Executors.newSingleThreadScheduledExecutor(); @@ -43,7 +43,7 @@ public class VersionsStore { versions.compute(key, (k, prevVersion) -> { if (prevVersion == null || prevVersion.value <= version) { isNew.set(true); - return new TimedValue<>(version); + return new TimedValue(version); } else { log.debug("[{}] Version {} is outdated, the latest is {}", key, version, prevVersion); return prevVersion; @@ -55,7 +55,7 @@ public class VersionsStore { private void startCleanupTask() { cleaner.scheduleAtFixedRate(() -> { long now = System.currentTimeMillis(); - for (Map.Entry> entry : versions.entrySet()) { + for (Map.Entry entry : versions.entrySet()) { if (now - entry.getValue().lastUpdated > expirationMillis) { versions.remove(entry.getKey(), entry.getValue()); } @@ -67,11 +67,11 @@ public class VersionsStore { cleaner.shutdown(); } - private static class TimedValue { + private static class TimedValue { private final long lastUpdated; - private final V value; + private final long value; - public TimedValue(V value) { + public TimedValue(long value) { this.value = value; this.lastUpdated = System.currentTimeMillis(); } From c9a4a07e98d21611007cb1e99614cdc59544087b Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 3 Jun 2025 15:17:21 +0300 Subject: [PATCH 272/335] UI: Fixed full screen mode in tinymce editor --- .../home/pages/mobile/common/editor-panel.component.ts | 4 ++-- ui-ngx/src/styles.scss | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts index 3c4981f816..0b5c93ff59 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts @@ -47,10 +47,10 @@ export class EditorPanelComponent implements OnInit { tinyMceOptions: Partial = { base_url: '/assets/tinymce', suffix: '.min', - plugins: ['link', 'table', 'image', 'imagetools', 'lists'], + plugins: ['link', 'table', 'image', 'imagetools', 'lists', 'fullscreen'], menubar: 'edit insert view format', toolbar: ['fontfamily fontsize | bold italic underline strikethrough forecolor backcolor', - 'alignleft aligncenter alignright alignjustify | bullist | link table image'], + 'alignleft aligncenter alignright alignjustify | bullist | link table image | fullscreen'], toolbar_mode: 'sliding', height: 400, autofocus: false, diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index 539a3b1960..d3cc76b687 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -1292,6 +1292,12 @@ pre.tb-highlight { background: rgba(0, 0, 0, 0.06); } } + + &.tox-fullscreen { + mat-sidenav.tb-site-sidenav { + z-index: 1; + } + } } /*************** From 1d5c4ac7ab5f978fb05339cc6e897d40c2e8fbc1 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 3 Jun 2025 15:48:26 +0300 Subject: [PATCH 273/335] VersionsStore - added try/catch for cleanup task --- .../thingsboard/server/edqs/util/VersionsStore.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java index c8c8f76761..f348e9cf9e 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/VersionsStore.java @@ -54,11 +54,15 @@ public class VersionsStore { private void startCleanupTask() { cleaner.scheduleAtFixedRate(() -> { - long now = System.currentTimeMillis(); - for (Map.Entry entry : versions.entrySet()) { - if (now - entry.getValue().lastUpdated > expirationMillis) { - versions.remove(entry.getKey(), entry.getValue()); + try { + long now = System.currentTimeMillis(); + for (Map.Entry entry : versions.entrySet()) { + if (now - entry.getValue().lastUpdated > expirationMillis) { + versions.remove(entry.getKey(), entry.getValue()); + } } + } catch (Exception e) { + log.error("Cleanup task failed", e); } }, expirationMillis, expirationMillis, TimeUnit.MILLISECONDS); } From 9b09c6542bdd41bbdcb88bd555dec4a32c12e662 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Wed, 4 Jun 2025 11:00:36 +0300 Subject: [PATCH 274/335] fixed error when json passed as argument --- .../cf/ctx/state/SingleValueArgumentEntry.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java index bfe9eed24f..bdbda2309e 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java @@ -16,9 +16,11 @@ package org.thingsboard.server.service.cf.ctx.state; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.type.TypeReference; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; import org.thingsboard.server.common.data.kv.AttributeKvEntry; @@ -90,7 +92,14 @@ public class SingleValueArgumentEntry implements ArgumentEntry { @Override public TbelCfArg toTbelCfArg() { - return new TbelCfSingleValueArg(ts, kvEntryValue.getValue()); + Object value; + try { + value = JacksonUtil.readValue(kvEntryValue.getValueAsString(), new TypeReference<>() { + }); + } catch (Exception e) { + value = kvEntryValue.getValue(); + } + return new TbelCfSingleValueArg(ts, value); } @Override From 7fb1a4f20a114694fae28c3efaa9a715bd15fb6b Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Wed, 4 Jun 2025 12:33:35 +0300 Subject: [PATCH 275/335] added check for jsonDataEntry --- .../cf/ctx/state/SingleValueArgumentEntry.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java index bdbda2309e..3b858e81b2 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java @@ -25,6 +25,7 @@ import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicKvEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.util.ProtoUtils; @@ -92,12 +93,13 @@ public class SingleValueArgumentEntry implements ArgumentEntry { @Override public TbelCfArg toTbelCfArg() { - Object value; - try { - value = JacksonUtil.readValue(kvEntryValue.getValueAsString(), new TypeReference<>() { - }); - } catch (Exception e) { - value = kvEntryValue.getValue(); + Object value = kvEntryValue.getValue(); + if (kvEntryValue instanceof JsonDataEntry) { + try { + value = JacksonUtil.readValue(kvEntryValue.getValueAsString(), new TypeReference<>() { + }); + } catch (Exception e) { + } } return new TbelCfSingleValueArg(ts, value); } From 49b3081d416ca42f016fe82f13c70ebc26d7d613 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 4 Jun 2025 12:45:17 +0300 Subject: [PATCH 276/335] Proper rate limit exception for Cassandra queries --- .../service/ws/DefaultWebSocketService.java | 4 ++-- .../dao/timeseries/BaseTimeseriesService.java | 8 ++++---- .../util/AbstractBufferedRateExecutor.java | 11 ++++++----- .../dao/util/TenantRateLimitException.java | 19 ------------------- 4 files changed, 12 insertions(+), 30 deletions(-) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/util/TenantRateLimitException.java diff --git a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java index cbe6663663..283e3baf76 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java @@ -36,6 +36,7 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.exception.RateLimitExceededException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -52,7 +53,6 @@ import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.server.dao.util.TenantRateLimitException; import org.thingsboard.server.exception.UnauthorizedException; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -742,7 +742,7 @@ public class DefaultWebSocketService implements WebSocketService { @Override public void onFailure(Throwable e) { - if (e instanceof TenantRateLimitException || e.getCause() instanceof TenantRateLimitException) { + if (e instanceof RateLimitExceededException || e.getCause() instanceof RateLimitExceededException) { log.trace("[{}] Tenant rate limit detected for subscription: [{}]:{}", sessionRef.getSecurityCtx().getTenantId(), entityId, cmd); } else { log.info(FAILED_TO_FETCH_DATA, e); diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java index 9eefcaae1e..cecf4ab587 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java @@ -205,8 +205,8 @@ public class BaseTimeseriesService implements TimeseriesService { ListenableFuture dpsFuture = saveTs ? Futures.transform(Futures.allAsList(tsFutures), SUM_ALL_INTEGERS, MoreExecutors.directExecutor()) : Futures.immediateFuture(0); ListenableFuture> versionsFuture = saveLatest ? Futures.allAsList(latestFutures) : Futures.immediateFuture(null); return Futures.whenAllComplete(dpsFuture, versionsFuture).call(() -> { - Integer dataPoints = Futures.getUnchecked(dpsFuture); - List versions = Futures.getUnchecked(versionsFuture); + Integer dataPoints = dpsFuture.get(); + List versions = versionsFuture.get(); return TimeseriesSaveResult.of(dataPoints, versions); }, MoreExecutors.directExecutor()); } @@ -298,13 +298,13 @@ public class BaseTimeseriesService implements TimeseriesService { long interval = query.getInterval(); if (interval < 1) { throw new IncorrectParameterException("Invalid TsKvQuery: 'interval' must be greater than 0, but got " + interval + - ". Please check your query parameters and ensure 'endTs' is greater than 'startTs' or increase 'interval'."); + ". Please check your query parameters and ensure 'endTs' is greater than 'startTs' or increase 'interval'."); } long step = Math.max(interval, 1000); long intervalCounts = (query.getEndTs() - query.getStartTs()) / step; if (intervalCounts > maxTsIntervals || intervalCounts < 0) { throw new IncorrectParameterException("Incorrect TsKvQuery. Number of intervals is to high - " + intervalCounts + ". " + - "Please increase 'interval' parameter for your query or reduce the time range of the query."); + "Please increase 'interval' parameter for your query or reduce the time range of the query."); } } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java index cbcf3e81ec..4d691db31d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java @@ -32,6 +32,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.cache.limits.RateLimitService; +import org.thingsboard.server.common.data.exception.RateLimitExceededException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.msg.queue.ServiceType; @@ -66,7 +67,7 @@ public abstract class AbstractBufferedRateExecutor> queue; private final ExecutorService dispatcherExecutor; private final ExecutorService callbackExecutor; @@ -124,7 +125,7 @@ public abstract class AbstractBufferedRateExecutor 0 - || rateLimitedTenantsCount > 0 - || concurrencyLevel.get() > 0 - || stats.getStatsCounters().stream().anyMatch(counter -> counter.get() > 0) + || rateLimitedTenantsCount > 0 + || concurrencyLevel.get() > 0 + || stats.getStatsCounters().stream().anyMatch(counter -> counter.get() > 0) ) { StringBuilder statsBuilder = new StringBuilder(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/TenantRateLimitException.java b/dao/src/main/java/org/thingsboard/server/dao/util/TenantRateLimitException.java deleted file mode 100644 index 3d79af980d..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/util/TenantRateLimitException.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright © 2016-2025 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. - */ -package org.thingsboard.server.dao.util; - -public class TenantRateLimitException extends Exception { -} From 5ba732d80b36aaee087724ee3b843f129bc07643 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 4 Jun 2025 13:09:48 +0300 Subject: [PATCH 277/335] UI: Fixed LWM2M Bootstrap configured doesn't display after saving --- .../device-profile/device-profile-tabs.component.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts index c7d1ddc9d0..1e4b735ff7 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, DestroyRef } from '@angular/core'; +import { Component, DestroyRef, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; @@ -31,7 +31,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; templateUrl: './device-profile-tabs.component.html', styleUrls: [] }) -export class DeviceProfileTabsComponent extends EntityTabsComponent { +export class DeviceProfileTabsComponent extends EntityTabsComponent implements OnInit { deviceTransportTypes = Object.values(DeviceTransportType); @@ -55,4 +55,9 @@ export class DeviceProfileTabsComponent extends EntityTabsComponent Date: Wed, 4 Jun 2025 13:13:29 +0300 Subject: [PATCH 278/335] KafkaEdqsStateService - added versionsStore.shutdown() --- .../org/thingsboard/server/edqs/state/KafkaEdqsStateService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/state/KafkaEdqsStateService.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/state/KafkaEdqsStateService.java index 66bbb7a68a..7e2e99e662 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/state/KafkaEdqsStateService.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/state/KafkaEdqsStateService.java @@ -224,6 +224,7 @@ public class KafkaEdqsStateService implements EdqsStateService { stateConsumer.awaitStop(); eventsToBackupConsumer.stop(); stateProducer.stop(); + versionsStore.shutdown(); } } From cf4ab4fd09ba68535bacb39dd0ba8b7e612f3009 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Wed, 4 Jun 2025 13:45:02 +0300 Subject: [PATCH 279/335] added tests for toTbelCfArg method --- .../state/SingleValueArgumentEntryTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java index 2c48ed9167..5d035efb26 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java @@ -17,8 +17,15 @@ package org.thingsboard.server.service.cf.ctx.state; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.thingsboard.script.api.tbel.TbelCfArg; +import org.thingsboard.script.api.tbel.TbelCfSingleValueArg; +import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -73,4 +80,34 @@ public class SingleValueArgumentEntryTest { void testUpdateEntryWhenValueWasNotChanged() { assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 11L), 364L))).isTrue(); } + + @Test + void testToTbelCfArgWhenJsonIsObject() { + entry = new SingleValueArgumentEntry(ts, new JsonDataEntry("key", "{\"test\": 10}"), 370L); + TbelCfArg tbelCfArg = entry.toTbelCfArg(); + assertThat(tbelCfArg).isNotNull(); + assertThat(tbelCfArg).isInstanceOf(TbelCfSingleValueArg.class); + + TbelCfSingleValueArg singleValueArg = (TbelCfSingleValueArg) tbelCfArg; + + assertThat(singleValueArg.getValue()).isInstanceOf(Map.class); + Map expectedMap = Map.of("test", 10); + assertThat(singleValueArg.getValue()).isEqualTo(expectedMap); + } + + @Test + void testToTbelCfArgWhenJsonIsArray() { + entry = new SingleValueArgumentEntry(ts, new JsonDataEntry("key", "[{\"test\": 10}, {\"test2\": 20}]"), 371L); + TbelCfArg tbelCfArg = entry.toTbelCfArg(); + assertThat(tbelCfArg).isNotNull(); + assertThat(tbelCfArg).isInstanceOf(TbelCfSingleValueArg.class); + + TbelCfSingleValueArg singleValueArg = (TbelCfSingleValueArg) tbelCfArg; + + assertThat(singleValueArg.getValue()).isInstanceOf(List.class); + List> expectedList = new ArrayList<>(); + expectedList.add(Map.of("test", 10)); + expectedList.add(Map.of("test2", 20)); + assertThat(singleValueArg.getValue()).isEqualTo(expectedList); + } } \ No newline at end of file From 6727a3c9eac98fe6e6a5a66bac50d689ac3cefca Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Wed, 4 Jun 2025 13:51:35 +0300 Subject: [PATCH 280/335] added new line to the end of the file --- .../service/cf/ctx/state/SingleValueArgumentEntryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java index 5d035efb26..50cac8a6fe 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java @@ -110,4 +110,4 @@ public class SingleValueArgumentEntryTest { expectedList.add(Map.of("test2", 20)); assertThat(singleValueArg.getValue()).isEqualTo(expectedList); } -} \ No newline at end of file +} From 16d204632a0710e931cf7cac78e2dffa53dfc759 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 4 Jun 2025 14:23:39 +0300 Subject: [PATCH 281/335] Add backward compatibility for RateLimitsNotificationInfo --- .../org/thingsboard/server/common/data/limit/LimitedApi.java | 1 + .../server/dao/notification/DefaultNotifications.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java index ef839247ab..3dc063ccca 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java @@ -43,6 +43,7 @@ public enum LimitedApi { RateLimitUtil.merge( DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantCoreRateLimits, DefaultTenantProfileConfiguration::getCassandraWriteQueryTenantRuleEngineRateLimits), "Monolith telemetry Cassandra write queries", true), + CASSANDRA_QUERIES(null, true), // left for backward compatibility with RateLimitsNotificationInfo EDGE_EVENTS(DefaultTenantProfileConfiguration::getEdgeEventRateLimits, "Edge events", true), EDGE_EVENTS_PER_EDGE(DefaultTenantProfileConfiguration::getEdgeEventRateLimitsPerEdge, "Edge events per edge", false), EDGE_UPLINK_MESSAGES(DefaultTenantProfileConfiguration::getEdgeUplinkMessagesRateLimits, "Edge uplink messages", true), diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index 9b8b8b255e..efd69a4e61 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -33,7 +33,6 @@ import org.thingsboard.server.common.data.notification.rule.DefaultNotificationR import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.ResourcesShortageTrigger.Resource; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig; From 957965b351427d92cd73e5c2df06df36170c91ca Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 4 Jun 2025 15:39:14 +0300 Subject: [PATCH 282/335] Improvements for task processing --- .../server/service/job/JobManagerTest.java | 6 +++--- .../server/common/data/job/task/DummyTaskResult.java | 11 +++++++---- .../server/common/data/job/task/TaskResult.java | 10 ++++++---- .../thingsboard/server/queue/task/TaskProcessor.java | 2 ++ .../thingsboard/server/dao/job/DefaultJobService.java | 10 ++++++++-- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index b23722afd5..8da1be43f1 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -89,7 +89,7 @@ public class JobManagerTest extends AbstractControllerTest { @Test public void testSubmitJob_allTasksSuccessful() { - int tasksCount = 5; + int tasksCount = 7; JobId jobId = submitJob(DummyJobConfiguration.builder() .successfulTasksCount(tasksCount) .taskProcessingTimeMs(1000) @@ -154,10 +154,10 @@ public class JobManagerTest extends AbstractControllerTest { @Test public void testCancelJob_whileRunning() throws Exception { - int tasksCount = 100; + int tasksCount = 200; JobId jobId = submitJob(DummyJobConfiguration.builder() .successfulTasksCount(tasksCount) - .taskProcessingTimeMs(100) + .taskProcessingTimeMs(50) .build()).getId(); Thread.sleep(500); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java index 1988f13eb0..5b913af3e5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/DummyTaskResult.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.job.task; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -25,22 +26,25 @@ import org.thingsboard.server.common.data.job.JobType; @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor -@SuperBuilder @ToString(callSuper = true) public class DummyTaskResult extends TaskResult { private DummyTaskFailure failure; + @Builder + private DummyTaskResult(boolean success, boolean discarded, DummyTaskFailure failure) { + super(success, discarded); + this.failure = failure; + } + public static DummyTaskResult success(DummyTask task) { return DummyTaskResult.builder() - .key(task.getKey()) .success(true) .build(); } public static DummyTaskResult failed(DummyTask task, Throwable error) { return DummyTaskResult.builder() - .key(task.getKey()) .failure(DummyTaskFailure.builder() .error(error.getMessage()) .number(task.getNumber()) @@ -51,7 +55,6 @@ public class DummyTaskResult extends TaskResult { public static DummyTaskResult discarded(DummyTask task) { return DummyTaskResult.builder() - .key(task.getKey()) .discarded(true) .build(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java index 21303a55fe..da3c8252eb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/job/task/TaskResult.java @@ -20,16 +20,12 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; import org.thingsboard.server.common.data.job.JobType; @Data -@AllArgsConstructor @NoArgsConstructor -@SuperBuilder @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "jobType") @JsonSubTypes({ @@ -40,6 +36,12 @@ public abstract class TaskResult { private String key; private boolean success; private boolean discarded; + private long finishTs; + + protected TaskResult(boolean success, boolean discarded) { + this.success = success; + this.discarded = discarded; + } @JsonIgnore public abstract JobType getJobType(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java index 62ca19a05f..33c52859ca 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/task/TaskProcessor.java @@ -232,6 +232,8 @@ public abstract class TaskProcessor, R extends TaskResult> { } private void reportTaskResult(T task, R result) { + result.setKey(task.getKey()); + result.setFinishTs(System.currentTimeMillis()); statsService.reportTaskResult(task.getTenantId(), task.getJobId(), result); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java index 153e95a404..360aa0063b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/job/DefaultJobService.java @@ -69,7 +69,6 @@ public class DefaultJobService extends AbstractEntityService implements JobServi job.setStatus(QUEUED); } else { job.setStatus(PENDING); - job.getResult().setStartTs(System.currentTimeMillis()); } return saveJob(tenantId, job, true, null); } @@ -125,6 +124,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi } boolean publishEvent = false; + long lastFinishTs = 0; for (TaskResult taskResult : jobStats.getTaskResults()) { if (!taskResult.getKey().equals(job.getConfiguration().getTasksKey())) { log.debug("Ignoring task result {} with outdated key {}", taskResult, job.getConfiguration().getTasksKey()); @@ -140,6 +140,9 @@ public class DefaultJobService extends AbstractEntityService implements JobServi publishEvent = true; } } + if (taskResult.getFinishTs() > lastFinishTs) { + lastFinishTs = taskResult.getFinishTs(); + } } if (job.getStatus() == RUNNING) { @@ -153,7 +156,7 @@ public class DefaultJobService extends AbstractEntityService implements JobServi job.setStatus(COMPLETED); publishEvent = true; } - result.setFinishTs(System.currentTimeMillis()); + result.setFinishTs(lastFinishTs); job.getConfiguration().setToReprocess(null); } } @@ -166,6 +169,9 @@ public class DefaultJobService extends AbstractEntityService implements JobServi if (!Job.SUPPORTED_ENTITY_TYPES.contains(job.getEntityId().getEntityType())) { throw new IllegalArgumentException("Unsupported entity type " + job.getEntityId().getEntityType()); } + if (job.getStatus() == PENDING) { + job.getResult().setStartTs(System.currentTimeMillis()); + } job = jobDao.save(tenantId, job); if (publishEvent) { From 5c32cf582ca6cc9dba8278174751b63c67282473 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 4 Jun 2025 15:48:53 +0300 Subject: [PATCH 283/335] UI: Fixed XSS vulnerability when delete state name --- ...manage-dashboard-states-dialog.component.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts index 511abff8e5..107d5c2686 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.ts @@ -14,7 +14,16 @@ /// limitations under the License. /// -import { AfterViewInit, Component, ElementRef, Inject, OnInit, SkipSelf, ViewChild } from '@angular/core'; +import { + AfterViewInit, + Component, + ElementRef, + Inject, + OnInit, + SecurityContext, + SkipSelf, + ViewChild +} from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; @@ -42,6 +51,7 @@ import { } from '@home/components/dashboard-page/states/dashboard-state-dialog.component'; import { UtilsService } from '@core/services/utils.service'; import { Widget } from '@shared/models/widget.models'; +import { DomSanitizer } from '@angular/platform-browser'; export interface ManageDashboardStatesDialogData { states: {[id: string]: DashboardState }; @@ -87,7 +97,8 @@ export class ManageDashboardStatesDialogComponent private translate: TranslateService, private dialogs: DialogService, private utils: UtilsService, - private dialog: MatDialog) { + private dialog: MatDialog, + private sanitizer: DomSanitizer) { super(store, router, dialogRef); this.states = this.data.states; @@ -148,7 +159,8 @@ export class ManageDashboardStatesDialogComponent } const title = this.translate.instant('dashboard.delete-state-title'); const content = this.translate.instant('dashboard.delete-state-text', {stateName: state.name}); - this.dialogs.confirm(title, content, this.translate.instant('action.no'), + const safeContent = this.sanitizer.sanitize(SecurityContext.HTML, content); + this.dialogs.confirm(title, safeContent, this.translate.instant('action.no'), this.translate.instant('action.yes')).subscribe( (res) => { if (res) { From 5edd35dc92312cf3dac141c0a8c6a98a6066cdba Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 4 Jun 2025 16:39:38 +0300 Subject: [PATCH 284/335] UI: Add missing validation for notification length message. --- ...ion-action-button-configuration.component.html | 6 ++++++ ...ation-action-button-configuration.component.ts | 2 +- ...fication-template-configuration.component.html | 15 +++++++++++++++ ...tification-template-configuration.component.ts | 6 +++--- .../src/assets/locale/locale.constant-en_US.json | 1 + 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.html b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.html index bb15320c4d..3eb330bb50 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.html @@ -62,6 +62,12 @@ *ngIf="actionButtonConfigForm.get('link').hasError('required')"> {{ 'notification.link-required' | translate }} + + {{ 'notification.link-max-length' | translate : + {length: actionButtonConfigForm.get('link').getError('maxlength').requiredLength} + }} + {{ 'notification.subject-required' | translate }} + + {{'notification.subject-max-length' | translate : + {length: templateConfigurationForm.get('WEB.subject').getError('maxlength').requiredLength} + }} + notification.message @@ -56,6 +61,11 @@ {{ 'notification.message-required' | translate }} + + {{ 'notification.message-max-length' | translate : + {length: templateConfigurationForm.get('WEB.body').getError('maxlength').requiredLength} + }} +
@@ -194,6 +204,11 @@ {{ 'notification.subject-required' | translate }} + + {{'notification.subject-max-length' | translate : + {length: templateConfigurationForm.get('EMAIL.subject').getError('maxlength').requiredLength} + }} + Date: Wed, 4 Jun 2025 18:37:33 +0300 Subject: [PATCH 285/335] UI: Fixed details panel button freeze midway in firefox --- ui-ngx/src/scss/animations.scss | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/scss/animations.scss b/ui-ngx/src/scss/animations.scss index b4f9d83d41..865e70fd35 100644 --- a/ui-ngx/src/scss/animations.scss +++ b/ui-ngx/src/scss/animations.scss @@ -16,15 +16,23 @@ @keyframes tbMoveFromTopFade { from { opacity: 0; - transform: translate(0, -100%); } + + to { + opacity: 1; + transform: translate(0, 0); + } } @keyframes tbMoveToTopFade { + from { + opacity: 1; + transform: translate(0, 0); + } + to { opacity: 0; - transform: translate(0, -100%); } } @@ -32,15 +40,23 @@ @keyframes tbMoveFromBottomFade { from { opacity: 0; - transform: translate(0, 100%); } + + to { + opacity: 1; + transform: translate(0, 0); + } } @keyframes tbMoveToBottomFade { + from { + opacity: 1; + transform: translate(0, 0); + } + to { opacity: 0; - transform: translate(0, 150%); } } From 2bbf3414b6936d55ded71aa0296a464ea31f9e1f Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Wed, 4 Jun 2025 23:03:23 +0300 Subject: [PATCH 286/335] UI: Fixed visible elements behind widget preview --- .../home/components/dashboard-page/edit-widget.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss index 69cbeb7f5f..2ff02a1f16 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss @@ -21,7 +21,7 @@ right: 0; bottom: 0; background: #fff; - z-index: 5; + z-index: 100; } .widget-preview-section { position: absolute; From b520dec8b23d28a6c9b2343f62dba888985fef2d Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Wed, 4 Jun 2025 23:53:42 +0300 Subject: [PATCH 287/335] UI: Fixed lwm2m device profile object configuration checkbox alignment --- .../lwm2m-observe-attr-telemetry-instances.component.scss | 6 ++++++ .../lwm2m-observe-attr-telemetry-resources.component.html | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.scss index d86864d1f3..9ce049107b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.scss @@ -31,4 +31,10 @@ .mat-expansion-panel-header-title { margin-right: 0; } + + &::ng-deep { + .mat-content.mat-content-hide-toggle { + margin-right: 0; + } + } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.html index 06b451a1f8..63a5dfbd1f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.html @@ -49,7 +49,7 @@
- From 7eaa16e66f8f5394fe32539d9ee4c8419170b9bd Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 5 Jun 2025 11:08:50 +0300 Subject: [PATCH 288/335] UI: Fixed advanced button style on edit action --- .../components/widget/action/widget-action-dialog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts index 3eabf71899..627a4a4dbc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts @@ -137,7 +137,7 @@ export class WidgetActionDialogComponent extends DialogComponent Date: Thu, 5 Jun 2025 11:20:49 +0300 Subject: [PATCH 289/335] UI: Ref --- .../components/widget/action/widget-action-dialog.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts index 627a4a4dbc..bfe14832c4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts @@ -137,7 +137,7 @@ export class WidgetActionDialogComponent extends DialogComponent Date: Thu, 5 Jun 2025 13:29:20 +0300 Subject: [PATCH 290/335] UI: Fixed not detect change device profile transport configuration --- .../device/device-profile-transport-configuration.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts index 1a696072eb..951042bb2d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts @@ -103,7 +103,7 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu delete configuration.type; } setTimeout(() => { - this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); + this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: this.isAdd}); }, 0); } From 11fc6358b4b5226ca9ec092c7a33d3b47ae36e5e Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 5 Jun 2025 15:03:19 +0300 Subject: [PATCH 291/335] Fix EdqsState.isApiReady --- .../org/thingsboard/server/common/data/edqs/EdqsState.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsState.java b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsState.java index 3df7fc92fe..1e890da961 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsState.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/EdqsState.java @@ -20,7 +20,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.apache.commons.lang3.BooleanUtils; + +import static org.apache.commons.lang3.BooleanUtils.toBooleanDefaultIfNull; @Getter @NoArgsConstructor @@ -34,14 +35,14 @@ public class EdqsState { private EdqsApiMode apiMode; public boolean updateEdqsReady(boolean ready) { - boolean changed = BooleanUtils.toBooleanDefaultIfNull(this.edqsReady, false) != ready; + boolean changed = toBooleanDefaultIfNull(this.edqsReady, false) != ready; this.edqsReady = ready; return changed; } @JsonIgnore public boolean isApiReady() { - return edqsReady && syncStatus == EdqsSyncStatus.FINISHED; + return toBooleanDefaultIfNull(edqsReady, false) && syncStatus == EdqsSyncStatus.FINISHED; } @JsonIgnore From 134663621381bb86bbca75a0180977602c1535c3 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 5 Jun 2025 15:54:23 +0300 Subject: [PATCH 292/335] UI: Add unit converter support in scada system --- .../3-phase-voltage-relay-hp.svg | 11 +- .../scada_symbols/bottom-flow-meter.svg | 7 +- .../system/scada_symbols/conical-tank.svg | 116 +----- .../system/scada_symbols/cylindrical-tank.svg | 24 +- .../dynamic-horizontal-scale-hp.svg | 314 +++------------ .../dynamic-vertical-scale-hp.svg | 323 +++------------- .../system/scada_symbols/elevated-tank.svg | 23 +- .../system/scada_symbols/energy-meter-hp.svg | 10 +- .../four-rate-energy-meter-hp.svg | 25 +- .../system/scada_symbols/heat-pump-hp.svg | 139 ++----- .../horizontal-inline-flow-meter.svg | 5 +- .../scada_symbols/horizontal-tank-hp.svg | 30 +- .../system/scada_symbols/horizontal-tank.svg | 22 +- .../scada_symbols/large-conical-tank.svg | 116 +----- .../scada_symbols/large-cylindrical-tank.svg | 22 +- .../large-stand-cylindrical-tank.svg | 26 +- .../large-stand-vertical-tank.svg | 22 +- .../scada_symbols/large-vertical-tank.svg | 22 +- .../system/scada_symbols/left-flow-meter.svg | 7 +- .../system/scada_symbols/left-heat-pump.svg | 185 ++------- .../data/json/system/scada_symbols/meter.svg | 14 +- .../json/system/scada_symbols/pool-hp.svg | 30 +- .../data/json/system/scada_symbols/pool.svg | 93 +---- .../system/scada_symbols/right-flow-meter.svg | 7 +- .../system/scada_symbols/right-heat-pump.svg | 185 ++------- .../scada_symbols/short-vertical-tank-hp.svg | 28 +- .../simple-horizontal-scale-hp.svg | 362 ++++-------------- .../simple-vertical-scale-hp.svg | 362 ++++-------------- .../scada_symbols/small-cylindrical-tank.svg | 30 +- .../system/scada_symbols/small-left-meter.svg | 14 +- .../json/system/scada_symbols/small-meter.svg | 26 +- .../scada_symbols/small-right-center.svg | 24 +- .../scada_symbols/small-spherical-tank.svg | 30 +- .../system/scada_symbols/spherical-tank.svg | 30 +- .../scada_symbols/stand-cylindrical-tank.svg | 22 +- .../scada_symbols/stand-horizontal-tank.svg | 22 +- .../stand-vertical-short-tank.svg | 22 +- .../scada_symbols/stand-vertical-tank.svg | 26 +- .../three-rate-energy-meter-hp.svg | 20 +- .../system/scada_symbols/top-flow-meter.svg | 7 +- .../two-rate-energy-meter-hp.svg | 31 +- .../vertical-inline-flow-meter.svg | 5 +- .../scada_symbols/vertical-short-tank.svg | 23 +- .../system/scada_symbols/vertical-tank-hp.svg | 30 +- .../system/scada_symbols/vertical-tank.svg | 22 +- .../system/scada_symbols/voltage-relay-hp.svg | 5 +- .../widget/lib/scada/scada-symbol.models.ts | 53 ++- .../scada-symbol-editor.models.ts | 56 ++- .../assets/locale/locale.constant-en_US.json | 1 + 49 files changed, 934 insertions(+), 2095 deletions(-) diff --git a/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg b/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg index f6d881b172..1152103805 100644 --- a/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg @@ -1,6 +1,6 @@ { "title": "HP 3 phase voltage relay", - "description": "Three phase voltage relay with various states and inications.", + "description": "Three phase voltage relay with various states and indications.", "searchTags": [ "energy", "power", @@ -39,7 +39,7 @@ }, { "tag": "firstPhaseValue", - "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.firstPhaseVoltage, 0, null, 0));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.firstPhaseVoltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, id: 0}));\n} else {\n element.hide();\n}", "actions": null }, { @@ -49,7 +49,7 @@ }, { "tag": "secondPhaseValue", - "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.secondPhaseVoltage, 0, null, 0));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.secondPhaseVoltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, id: 1}));\n} else {\n element.hide();\n}", "actions": null }, { @@ -59,12 +59,12 @@ }, { "tag": "thirdPhaseValue", - "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.thirdPhaseVoltage, 0, null, 0));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.thirdPhaseVoltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, id: 2}));\n} else {\n element.hide();\n}", "actions": null }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { @@ -488,6 +488,7 @@ "name": "{i18n:scada.symbol.units}", "type": "units", "default": "V", + "supportsUnitConversion": true, "disabled": false, "visible": true }, diff --git a/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg index 794e6e61d9..e003565dd2 100644 --- a/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -66,7 +66,7 @@ }, { "tag": "valueUnits", - "stateRenderFunction": "var units = ctx.properties.valueUnits;\nctx.api.text(element, units || '');\n", + "stateRenderFunction": "var units = ctx.api.unitSymbol(ctx.properties.valueUnits);\nctx.api.text(element, units || '');\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -464,8 +464,7 @@ "type": "units", "default": "m³/hr", "fieldClass": "medium-width", - "disabled": false, - "visible": true + "supportsUnitConversion": true }, { "id": "valueDecimals", diff --git a/application/src/main/data/json/system/scada_symbols/conical-tank.svg b/application/src/main/data/json/system/scada_symbols/conical-tank.svg index 6b37adc52e..592508ee30 100644 --- a/application/src/main/data/json/system/scada_symbols/conical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/conical-tank.svg @@ -159,96 +159,51 @@ "name": "Stand", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "tankColor", "name": "{i18n:scada.symbol.tank-color}", "type": "color", "default": "#E5E5E5", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "fluidColor", "name": "{i18n:scada.symbol.fluid-color}", "type": "color", "default": "#1EC1F480", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueBox", "name": "{i18n:scada.symbol.value-box}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueBoxColor", "name": "{i18n:scada.symbol.value-box}", "type": "color", "default": "#F3F3F3", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueUnits", "name": "{i18n:scada.symbol.value-text}", "type": "units", "default": "gal", - "required": null, "subLabel": "{i18n:scada.symbol.units}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "valueTextFont", @@ -261,64 +216,35 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueTextColor", "name": "{i18n:scada.symbol.value-text}", "type": "color", "default": "#0000008A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "scale", "name": "{i18n:scada.symbol.scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "scale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] } diff --git a/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg index acb895cd5a..5675818fe7 100644 --- a/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -312,8 +312,8 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", - "disabled": true, + "supportsUnitConversion": true, + "disabled": false, "visible": true }, { @@ -368,6 +368,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -390,16 +398,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg index b3506ae54f..5dc0de1372 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg @@ -43,7 +43,7 @@ }, { "tag": "maxValue", - "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.maxValue);\n}", + "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.api.convertUnitValue(ctx.properties.maxValue, ctx.properties.units).toFixed(0));\n}", "actions": null }, { @@ -53,7 +53,7 @@ }, { "tag": "minValue", - "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.minValue);\n}", + "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.api.convertUnitValue(ctx.properties.minValue, ctx.properties.units).toFixed(0));\n}", "actions": null }, { @@ -73,12 +73,12 @@ }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false}));\n} else {\n element.hide();\n}", "actions": null }, { @@ -329,16 +329,8 @@ "name": "{i18n:scada.symbol.min-max-value}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "minValue", @@ -347,14 +339,9 @@ "default": 0, "required": true, "subLabel": "{i18n:scada.symbol.min-value}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "maxValue", @@ -363,30 +350,17 @@ "default": 100, "required": true, "subLabel": "{i18n:scada.symbol.max-value}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "value", "name": "{i18n:scada.symbol.value}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueDecimals", @@ -395,14 +369,12 @@ "default": 0, "required": true, "subLabel": "Decimals", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "value", - "rowClass": "", - "fieldClass": "", "min": 0, "max": 10, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "valueFont", @@ -415,64 +387,36 @@ "weight": "400", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "value", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueColor", "name": "{i18n:scada.symbol.value}", "type": "color", "default": "#002878", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "value", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "label", "name": "{i18n:scada.symbol.label}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelText", "name": "{i18n:scada.symbol.label}", "type": "text", "default": "Outdoor", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "label", - "rowClass": "", "fieldClass": "flex", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelFont", @@ -485,64 +429,35 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "label", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelColor", "name": "{i18n:scada.symbol.label}", "type": "color", "default": "#000000DE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "label", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showUnits", "name": "{i18n:scada.symbol.units}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "units", "name": "{i18n:scada.symbol.units}", "type": "units", "default": "°C", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": "showUnits", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "unitsFont", @@ -555,224 +470,119 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showUnits", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "unitsColor", "name": "{i18n:scada.symbol.units}", "type": "color", "default": "#000000DE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showUnits", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "arrowColor", "name": "{i18n:scada.symbol.arrow-color}", "type": "color", "default": "#666666", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showTarget", "name": "{i18n:scada.symbol.target}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "targetColor", "name": "{i18n:scada.symbol.target}", "type": "color", "default": "#DEDEDE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showTarget", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": true, + "visible": true }, { "id": "showHighCriticalScale", "name": "{i18n:scada.symbol.show-high-critical-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showHighWarningScale", "name": "{i18n:scada.symbol.show-high-warning-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showLowWarningScale", "name": "{i18n:scada.symbol.show-low-warning-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showLowCriticalScale", "name": "{i18n:scada.symbol.show-low-critical-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "scaleColor", "name": "{i18n:scada.symbol.scale-color}", "type": "color", "default": "#C8DFF7", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "defaultWarningScaleColor", "name": "{i18n:scada.symbol.warning-scale-color}", "type": "color", "default": "#EBEBEB", - "required": null, "subLabel": "Default", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "activeWarningScaleColor", "name": "{i18n:scada.symbol.warning-scale-color}", "type": "color", "default": "#FAA405", - "required": null, "subLabel": "Active", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "defaultCriticalScaleColor", "name": "{i18n:scada.symbol.critical-scale-color}", "type": "color", "default": "#666666", - "required": null, "subLabel": "Default", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "activeCriticalScaleColor", "name": "{i18n:scada.symbol.critical-scale-color}", "type": "color", "default": "#D12730", - "required": null, "subLabel": "Active", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] }]]> diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg index 3c52dae1c3..020874f35d 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg @@ -43,7 +43,7 @@ }, { "tag": "maxValue", - "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.maxValue);\n}", + "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.api.convertUnitValue(ctx.properties.maxValue, ctx.properties.units).toFixed(0));\n}", "actions": null }, { @@ -53,7 +53,7 @@ }, { "tag": "minValue", - "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.minValue);\n}", + "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.api.convertUnitValue(ctx.properties.minValue, ctx.properties.units).toFixed(0));\n}", "actions": null }, { @@ -73,12 +73,12 @@ }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false}));\n} else {\n element.hide();\n}", "actions": null }, { @@ -329,16 +329,8 @@ "name": "{i18n:scada.symbol.min-max-value}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "minValue", @@ -347,14 +339,9 @@ "default": 0, "required": true, "subLabel": "{i18n:scada.symbol.min-value}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "maxValue", @@ -363,30 +350,17 @@ "default": 100, "required": true, "subLabel": "{i18n:scada.symbol.max-value}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "value", "name": "{i18n:scada.symbol.value}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueDecimals", @@ -395,14 +369,11 @@ "default": 0, "required": true, "subLabel": "Decimals", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 10, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "valueFont", @@ -415,64 +386,33 @@ "weight": "400", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueColor", "name": "{i18n:scada.symbol.value}", "type": "color", "default": "#002878", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "label", "name": "{i18n:scada.symbol.label}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelText", "name": "{i18n:scada.symbol.label}", "type": "text", "default": "Outdoor", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", "fieldClass": "flex", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelFont", @@ -485,64 +425,34 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelColor", "name": "{i18n:scada.symbol.label}", "type": "color", "default": "#000000DE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showUnits", "name": "{i18n:scada.symbol.units}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "units", "name": "{i18n:scada.symbol.units}", "type": "units", "default": "°C", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "required": false, + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "unitsFont", @@ -555,224 +465,117 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "unitsColor", "name": "{i18n:scada.symbol.units}", "type": "color", "default": "#000000DE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "arrowColor", "name": "{i18n:scada.symbol.arrow-color}", "type": "color", "default": "#666666", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showTarget", "name": "{i18n:scada.symbol.target}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "targetColor", "name": "{i18n:scada.symbol.target}", "type": "color", "default": "#DEDEDE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showTarget", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": true, + "visible": true }, { "id": "showHighCriticalScale", "name": "{i18n:scada.symbol.show-high-critical-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showHighWarningScale", "name": "{i18n:scada.symbol.show-high-warning-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showLowWarningScale", "name": "{i18n:scada.symbol.show-low-warning-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showLowCriticalScale", "name": "{i18n:scada.symbol.show-low-critical-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "scaleColor", "name": "{i18n:scada.symbol.scale-color}", "type": "color", "default": "#C8DFF7", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "defaultWarningScaleColor", "name": "{i18n:scada.symbol.warning-scale-color}", "type": "color", "default": "#EBEBEB", - "required": null, "subLabel": "Default", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "activeWarningScaleColor", "name": "{i18n:scada.symbol.warning-scale-color}", "type": "color", "default": "#FAA405", - "required": null, "subLabel": "Active", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "defaultCriticalScaleColor", "name": "{i18n:scada.symbol.critical-scale-color}", "type": "color", "default": "#666666", - "required": null, "subLabel": "Default", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "activeCriticalScaleColor", "name": "{i18n:scada.symbol.critical-scale-color}", "type": "color", "default": "#D12730", - "required": null, "subLabel": "Active", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] }]]> diff --git a/application/src/main/data/json/system/scada_symbols/elevated-tank.svg b/application/src/main/data/json/system/scada_symbols/elevated-tank.svg index 48668e9851..0d82a10b41 100644 --- a/application/src/main/data/json/system/scada_symbols/elevated-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/elevated-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 265;\n var majorIntervalLength = 895 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(825, y, 857, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 815, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(837, minorY, 857, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 265;\n var majorIntervalLength = 895 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(825, y, 857, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 815, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(837, minorY, 857, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -308,7 +308,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -373,6 +373,15 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disableOnProperty": "scale", + "disabled": false, + "visible": true + }, { "id": "majorIntervals", "name": "{i18n:scada.symbol.major-ticks}", @@ -386,16 +395,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg index 606eb1153d..51966854a1 100644 --- a/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg @@ -38,12 +38,12 @@ }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { "tag": "value", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.measured, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.measured, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false}));", "actions": null }, { @@ -353,6 +353,7 @@ "name": "{i18n:scada.symbol.label}", "type": "text", "default": "T1", + "disableOnProperty": "showLabel", "fieldClass": "medium-width", "disabled": false, "visible": true @@ -368,6 +369,7 @@ "weight": "400", "style": "normal" }, + "disableOnProperty": "showLabel", "disabled": false, "visible": true }, @@ -376,6 +378,7 @@ "name": "{i18n:scada.symbol.label}", "type": "color", "default": "#000", + "disableOnProperty": "showLabel", "disabled": false, "visible": true }, @@ -392,6 +395,7 @@ "name": "{i18n:scada.symbol.units}", "type": "units", "default": "kWh", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -406,6 +410,7 @@ "weight": "500", "style": "normal" }, + "disableOnProperty": "showUnits", "disabled": false, "visible": true }, @@ -414,6 +419,7 @@ "name": "{i18n:scada.symbol.units}", "type": "color", "default": "#000", + "disableOnProperty": "showUnits", "disabled": false, "visible": true }, diff --git a/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg index bba67e5fe3..5e43858202 100644 --- a/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "export-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.exportValueFont, ctx.properties.exportValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.exportRate, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.exportValueFont, ctx.properties.exportValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.exportRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 3}));", "actions": null }, { @@ -48,7 +48,7 @@ }, { "tag": "night-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 1}));", "actions": null }, { @@ -58,7 +58,7 @@ }, { "tag": "off-peak-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 0}));", "actions": null }, { @@ -68,12 +68,12 @@ }, { "tag": "peak-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 2}));", "actions": null }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { @@ -533,6 +533,7 @@ "group": "{i18n:scada.symbol.off-peak-rate}", "type": "text", "default": "T1", + "disableOnProperty": "showOffPeakLabel", "fieldClass": "medium-width", "disabled": false, "visible": true @@ -549,6 +550,7 @@ "weight": "normal", "style": "normal" }, + "disableOnProperty": "showOffPeakLabel", "disabled": false, "visible": true }, @@ -558,6 +560,7 @@ "group": "{i18n:scada.symbol.off-peak-rate}", "type": "color", "default": "#000000", + "disableOnProperty": "showOffPeakLabel", "disabled": false, "visible": true }, @@ -609,6 +612,7 @@ "group": "{i18n:scada.symbol.night-rate}", "type": "text", "default": "T2", + "disableOnProperty": "showNightLabel", "fieldClass": "medium-width", "disabled": false, "visible": true @@ -625,6 +629,7 @@ "weight": "normal", "style": "normal" }, + "disableOnProperty": "showNightLabel", "disabled": false, "visible": true }, @@ -634,6 +639,7 @@ "group": "{i18n:scada.symbol.night-rate}", "type": "color", "default": "#000", + "disableOnProperty": "showNightLabel", "disabled": false, "visible": true }, @@ -685,6 +691,7 @@ "group": "{i18n:scada.symbol.peak-rate}", "type": "text", "default": "T3", + "disableOnProperty": "showPeakLabel", "fieldClass": "medium-width", "disabled": false, "visible": true @@ -701,6 +708,7 @@ "weight": "normal", "style": "normal" }, + "disableOnProperty": "showPeakLabel", "disabled": false, "visible": true }, @@ -710,6 +718,7 @@ "group": "{i18n:scada.symbol.peak-rate}", "type": "color", "default": "#000", + "disableOnProperty": "showPeakLabel", "disabled": false, "visible": true }, @@ -761,6 +770,7 @@ "group": "{i18n:scada.symbol.export-rate}", "type": "text", "default": "Export", + "disableOnProperty": "showExportLabel", "fieldClass": "medium-width", "disabled": false, "visible": true @@ -777,6 +787,7 @@ "weight": "normal", "style": "normal" }, + "disableOnProperty": "showExportLabel", "disabled": false, "visible": true }, @@ -786,6 +797,7 @@ "group": "{i18n:scada.symbol.export-rate}", "type": "color", "default": "#000", + "disableOnProperty": "showExportLabel", "disabled": false, "visible": true }, @@ -835,6 +847,7 @@ "name": "{i18n:scada.symbol.units}", "type": "units", "default": "kWh", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -849,6 +862,7 @@ "weight": "normal", "style": "normal" }, + "disableOnProperty": "showUnits", "disabled": false, "visible": true }, @@ -857,6 +871,7 @@ "name": "{i18n:scada.symbol.units}", "type": "color", "default": "#000", + "disableOnProperty": "showUnits", "disabled": false, "visible": true } diff --git a/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg b/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg index eed54682cd..b1e643698c 100644 --- a/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg @@ -62,7 +62,7 @@ }, { "tag": "value-text", - "stateRenderFunction": "var valueTextFont = ctx.properties.valueTextFont;\nvar valueTextColor = ctx.properties.valueTextColor;\nvar units = ctx.properties.valueUnits ? ctx.properties.units : null;\nvar currentVolume = ctx.values.temperature;\nvar decimals = Math.floor(ctx.properties.temperatureStep) === ctx.properties.temperatureStep;\nvar valueText = ctx.api.formatValue(currentVolume, decimals ? 0 : 1, units, !decimals);\nctx.api.font(element, valueTextFont, valueTextColor);\nctx.api.text(element, valueText);", + "stateRenderFunction": "var valueTextFont = ctx.properties.valueTextFont;\nvar valueTextColor = ctx.properties.valueTextColor;\nvar currentVolume = ctx.values.temperature;\nvar decimals = Math.floor(ctx.properties.temperatureStep) === ctx.properties.temperatureStep;\nvar valueText = ctx.api.formatValue(currentVolume, {units: ctx.properties.units, decimals: decimals ? 0 : 1, ignoreUnitSymbol: !ctx.properties.valueUnits});\nctx.api.font(element, valueTextFont, valueTextColor);\nctx.api.text(element, valueText);", "actions": null }, { @@ -366,80 +366,49 @@ "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#FFFFFF", - "required": null, "subLabel": "{i18n:scada.symbol.running}", "divider": true, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "stoppedColor", "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#666666", - "required": null, "subLabel": "{i18n:scada.symbol.stopped}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "warningColor", "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", - "required": null, "subLabel": "{i18n:scada.symbol.warning}", "divider": true, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "criticalColor", "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", - "required": null, "subLabel": "{i18n:scada.symbol.critical}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "minTemperature", "name": "{i18n:scada.symbol.temperature}", "type": "number", "default": 10, - "required": null, "subLabel": "Min", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, "rowClass": "column-xs", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "maxTemperature", @@ -448,14 +417,9 @@ "default": 45, "required": true, "subLabel": "Max", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "temperatureStep", @@ -463,31 +427,17 @@ "type": "number", "default": 1, "required": true, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 0.5 + "step": 0.5, + "disabled": false, + "visible": true }, { "id": "valueTextColor", "name": "{i18n:scada.symbol.value-text}", "type": "color", "default": "#002878", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueTextFont", @@ -500,64 +450,33 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueUnits", "name": "{i18n:scada.symbol.value-units}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "units", "name": "{i18n:scada.symbol.value-units}", "type": "units", "default": "°C", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "valueBoxBackground", "name": "{i18n:scada.symbol.value-box-background}", "type": "color", "default": "#FFFFFF", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] }]]> diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg index 5e0c781941..555293931e 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -66,7 +66,7 @@ }, { "tag": "valueUnits", - "stateRenderFunction": "var units = ctx.properties.valueUnits;\nctx.api.text(element, units || '');\n", + "stateRenderFunction": "var units = ctx.api.unitSymbol(ctx.properties.valueUnits);\nctx.api.text(element, units || '');\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -464,6 +464,7 @@ "type": "units", "default": "m³/hr", "fieldClass": "medium-width", + "supportsUnitConversion": true, "disabled": false, "visible": true }, diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg b/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg index bb6f5e55a9..6b0df69f0d 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 3;\n var majorIntervalLength = 592 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(208, y, 240, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n if (i === 0) {\n majorTickText.attr({x: 198, y: y + 10, 'text-anchor': 'end', class: 'majorTickText'});\n } else if (i === majorIntervals) {\n majorTickText.attr({x: 198, y: y - 5, 'text-anchor': 'end', class: 'majorTickText'});\n } else {\n majorTickText.attr({x: 198, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n }\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(220, minorY, 240, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 3;\n var majorIntervalLength = 592 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(208, y, 240, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.majorUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n if (i === 0) {\n majorTickText.attr({x: 198, y: y + 10, 'text-anchor': 'end', class: 'majorTickText'});\n } else if (i === majorIntervals) {\n majorTickText.attr({x: 198, y: y - 5, 'text-anchor': 'end', class: 'majorTickText'});\n } else {\n majorTickText.attr({x: 198, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n }\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(220, minorY, 240, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -346,6 +346,24 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "majorUnits", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "units", + "subLabel": "{i18n:scada.symbol.units}", + "divider": false, + "supportsUnitConversion": true, + "disabled": false, + "visible": true + }, { "id": "majorIntervals", "name": "{i18n:scada.symbol.major-ticks}", @@ -359,16 +377,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg b/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg index b6b6eede7b..c02da77258 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -312,7 +312,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -368,6 +368,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -390,16 +398,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/large-conical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-conical-tank.svg index c8b40477ef..6789f8f7c9 100644 --- a/application/src/main/data/json/system/scada_symbols/large-conical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-conical-tank.svg @@ -160,96 +160,51 @@ "name": "Stand", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "tankColor", "name": "{i18n:scada.symbol.tank-color}", "type": "color", "default": "#E5E5E5", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "fluidColor", "name": "{i18n:scada.symbol.fluid-color}", "type": "color", "default": "#1EC1F480", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueBox", "name": "{i18n:scada.symbol.value-box}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueBoxColor", "name": "{i18n:scada.symbol.value-box}", "type": "color", "default": "#F3F3F3", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueUnits", "name": "{i18n:scada.symbol.value-text}", "type": "units", "default": "gal", - "required": null, "subLabel": "{i18n:scada.symbol.units}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "valueTextFont", @@ -262,64 +217,35 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueTextColor", "name": "{i18n:scada.symbol.value-text}", "type": "color", "default": "#0000008A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "scale", "name": "{i18n:scada.symbol.scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "scale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] }]]> diff --git a/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg index 69360fbe9c..c9d9361d7d 100644 --- a/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 60;\n var majorIntervalLength = 910 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(656, y, 688, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 646, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(668, minorY, 688, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 60;\n var majorIntervalLength = 910 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(656, y, 688, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 646, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(668, minorY, 688, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -312,7 +312,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -368,6 +368,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -390,16 +398,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg index 09c0e2a9e1..8e5c057209 100644 --- a/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 60;\n var majorIntervalLength = 910 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(656, y, 688, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 646, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(668, minorY, 688, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 60;\n var majorIntervalLength = 910 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(656, y, 688, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 646, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(668, minorY, 688, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -313,7 +313,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -365,7 +365,17 @@ "value": false, "label": "Absolute" } - ] + ], + "disabled": false, + "visible": true + }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true }, { "id": "transparent", @@ -389,16 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg index be8b1207a0..9b6763e0ea 100644 --- a/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 203;\n var majorIntervalLength = 763 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(676, y, 708, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 666, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(688, minorY, 708, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 203;\n var majorIntervalLength = 763 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(676, y, 708, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 666, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(688, minorY, 708, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -313,7 +313,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -369,6 +369,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -391,16 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg index cc168915ad..75ff5ef979 100644 --- a/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 203;\n var majorIntervalLength = 763 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(676, y, 708, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 666, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(688, minorY, 708, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 203;\n var majorIntervalLength = 763 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(676, y, 708, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 666, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(688, minorY, 708, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -312,7 +312,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -368,6 +368,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -390,16 +398,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg index 1c29a79479..55c60de9ea 100644 --- a/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -66,7 +66,7 @@ }, { "tag": "valueUnits", - "stateRenderFunction": "var units = ctx.properties.valueUnits;\nctx.api.text(element, units || '');\n", + "stateRenderFunction": "var units = ctx.api.unitSymbol(ctx.properties.valueUnits);\nctx.api.text(element, units || '');\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -463,7 +463,8 @@ "name": "{i18n:scada.symbol.units}", "type": "units", "default": "m³/hr", - "fieldClass": "medium-width" + "fieldClass": "medium-width", + "supportsUnitConversion": true }, { "id": "valueDecimals", diff --git a/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg b/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg index f1e982bac0..6232f3dda3 100644 --- a/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg @@ -72,7 +72,7 @@ }, { "tag": "value-text", - "stateRenderFunction": "var valueTextFont = ctx.properties.valueTextFont;\nvar valueTextColor = ctx.properties.valueTextColor;\nvar units = ctx.properties.valueUnits ? ctx.properties.units : null;\nvar currentVolume = ctx.values.temperature;\nvar decimals = Math.floor(ctx.properties.temperatureStep) === ctx.properties.temperatureStep;\nvar valueText = ctx.api.formatValue(currentVolume, decimals ? 0 : 1, units, !decimals);\nctx.api.font(element, valueTextFont, valueTextColor);\nctx.api.text(element, valueText);", + "stateRenderFunction": "var valueTextFont = ctx.properties.valueTextFont;\nvar valueTextColor = ctx.properties.valueTextColor;\nvar currentVolume = ctx.values.temperature;\nvar decimals = Math.floor(ctx.properties.temperatureStep) === ctx.properties.temperatureStep;\nvar valueText = ctx.api.formatValue(currentVolume, {units: ctx.properties.units, decimals: decimals ? 0 : 1, ignoreUnitSymbol: !ctx.properties.valueUnits});\nctx.api.font(element, valueTextFont, valueTextColor);\nctx.api.text(element, valueText);", "actions": null } ], @@ -432,13 +432,11 @@ "required": true, "subLabel": "Min", "divider": true, - "fieldSuffix": null, - "disableOnProperty": null, "rowClass": "column-xs", - "fieldClass": "", "min": 0, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "maxTemperature", @@ -447,14 +445,10 @@ "default": 45, "required": true, "subLabel": "Max", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "temperatureStep", @@ -462,31 +456,17 @@ "type": "number", "default": 1, "required": true, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 0.5 + "step": 0.5, + "disabled": false, + "visible": true }, { "id": "valueTextColor", "name": "{i18n:scada.symbol.value-text}", "type": "color", "default": "#000000C2", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueTextFont", @@ -499,192 +479,101 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueUnits", "name": "{i18n:scada.symbol.value-units}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "units", "name": "{i18n:scada.symbol.value-units}", "type": "units", "default": "°C", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "valueBoxBackground", "name": "{i18n:scada.symbol.value-box-background}", "type": "color", "default": "#FFFFFF", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "powerButtonBackground", "name": "{i18n:scada.symbol.power-button-background}", "type": "color", "default": "#1ABB48", - "required": null, "subLabel": "Enabled", "divider": true, - "fieldSuffix": null, - "disableOnProperty": null, "rowClass": "column-xs", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "disabledPowerButtonBackground", "name": "{i18n:scada.symbol.power-button-background}", "type": "color", "default": "#FFFFFF", - "required": null, "subLabel": "Disabled", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "runningColor", "name": "{i18n:scada.symbol.running-color}", "type": "color", "default": "#1C943E", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "stoppedColor", "name": "{i18n:scada.symbol.stopped-color}", "type": "color", "default": "#696969", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "warningColor", "name": "{i18n:scada.symbol.warning-color}", "type": "color", "default": "#FAA405", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "criticalColor", "name": "{i18n:scada.symbol.critical-color}", "type": "color", "default": "#D12730", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "heatPumpColor", "name": "{i18n:scada.symbol.heat-pump-color}", "type": "color", "default": "#E5E5E5", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "pipeColor", "name": "{i18n:scada.symbol.pipe-color}", "type": "color", "default": "#FFFFFF", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] }]]> diff --git a/application/src/main/data/json/system/scada_symbols/meter.svg b/application/src/main/data/json/system/scada_symbols/meter.svg index d5fa9f7cd7..6fdd27d2fa 100644 --- a/application/src/main/data/json/system/scada_symbols/meter.svg +++ b/application/src/main/data/json/system/scada_symbols/meter.svg @@ -51,7 +51,7 @@ }, { "tag": "scale", - "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n \n var start = 11;\n var end = ctx.properties.valueBox ? 328 : 365;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(63, end+11, 63, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(51, y, 63, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 45, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(57, minorY, 63, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.api.convertUnitValue(ctx.properties.minValue, ctx.properties.valueUnits);\n var maxValue = ctx.api.convertUnitValue(ctx.properties.maxValue, ctx.properties.valueUnits);\n \n var start = 11;\n var end = ctx.properties.valueBox ? 328 : 365;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(63, end+11, 63, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(51, y, 63, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n if (ctx.properties.enableUnitScale) {\n majorText = majorText + ctx.api.unitSymbol(ctx.properties.valueUnits);\n }\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 45, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(57, minorY, 63, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -433,7 +433,7 @@ "type": "units", "default": "%", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -493,6 +493,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "majorIntervals", "name": "{i18n:scada.symbol.major-ticks}", @@ -510,7 +518,7 @@ "name": "{i18n:scada.symbol.major-ticks}", "type": "font", "default": { - "size": 12, + "size": 10, "sizeUnit": "px", "family": "Roboto", "weight": "500", diff --git a/application/src/main/data/json/system/scada_symbols/pool-hp.svg b/application/src/main/data/json/system/scada_symbols/pool-hp.svg index 0ce78af7d9..03865a040c 100644 --- a/application/src/main/data/json/system/scada_symbols/pool-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/pool-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 3;\n var majorIntervalLength = 792 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(298, y, 330, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n if (i === 0) {\n majorTickText.attr({x: 288, y: y + 10, 'text-anchor': 'end', class: 'majorTickText'});\n } else if (i === majorIntervals) {\n majorTickText.attr({x: 288, y: y - 5, 'text-anchor': 'end', class: 'majorTickText'});\n } else {\n majorTickText.attr({x: 288, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n }\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(310, minorY, 330, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 3;\n var majorIntervalLength = 792 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(298, y, 330, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.majorUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n if (i === 0) {\n majorTickText.attr({x: 288, y: y + 10, 'text-anchor': 'end', class: 'majorTickText'});\n } else if (i === majorIntervals) {\n majorTickText.attr({x: 288, y: y - 5, 'text-anchor': 'end', class: 'majorTickText'});\n } else {\n majorTickText.attr({x: 288, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n }\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(310, minorY, 330, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -346,6 +346,24 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "majorUnits", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "units", + "subLabel": "{i18n:scada.symbol.units}", + "divider": false, + "supportsUnitConversion": true, + "disabled": false, + "visible": true + }, { "id": "majorIntervals", "name": "{i18n:scada.symbol.major-ticks}", @@ -394,16 +412,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "minorColor", "name": "{i18n:scada.symbol.minor-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/pool.svg b/application/src/main/data/json/system/scada_symbols/pool.svg index 6f8b12737c..75f21e9457 100644 --- a/application/src/main/data/json/system/scada_symbols/pool.svg +++ b/application/src/main/data/json/system/scada_symbols/pool.svg @@ -148,80 +148,43 @@ "name": "{i18n:scada.symbol.tank-color}", "type": "color", "default": "#E5E5E5", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "fluidColor", "name": "{i18n:scada.symbol.fluid-color}", "type": "color", "default": "#1EC1F480", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueBox", "name": "{i18n:scada.symbol.value-box}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueBoxColor", "name": "{i18n:scada.symbol.value-box}", "type": "color", "default": "#F3F3F3", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueUnits", "name": "{i18n:scada.symbol.value-text}", "type": "units", "default": "gal", - "required": null, "subLabel": "{i18n:scada.symbol.units}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "valueTextFont", @@ -234,48 +197,26 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueTextColor", "name": "{i18n:scada.symbol.value-text}", "type": "color", "default": "#0000008A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "valueBox", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] }]]> diff --git a/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg index 6a201111ef..53a2585fd0 100644 --- a/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -66,7 +66,7 @@ }, { "tag": "valueUnits", - "stateRenderFunction": "var units = ctx.properties.valueUnits;\nctx.api.text(element, units || '');\n", + "stateRenderFunction": "var units = ctx.api.unitSymbol(ctx.properties.valueUnits);\nctx.api.text(element, units || '');\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -463,7 +463,8 @@ "name": "{i18n:scada.symbol.units}", "type": "units", "default": "m³/hr", - "fieldClass": "medium-width" + "fieldClass": "medium-width", + "supportsUnitConversion": true }, { "id": "valueDecimals", diff --git a/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg b/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg index b1aa081b82..a0802c6e66 100644 --- a/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg @@ -72,7 +72,7 @@ }, { "tag": "value-text", - "stateRenderFunction": "var valueTextFont = ctx.properties.valueTextFont;\nvar valueTextColor = ctx.properties.valueTextColor;\nvar units = ctx.properties.valueUnits ? ctx.properties.units : null;\nvar currentVolume = ctx.values.temperature;\nvar decimals = Math.floor(ctx.properties.temperatureStep) === ctx.properties.temperatureStep;\nvar valueText = ctx.api.formatValue(currentVolume, decimals ? 0 : 1, units, !decimals);\nctx.api.font(element, valueTextFont, valueTextColor);\nctx.api.text(element, valueText);", + "stateRenderFunction": "var valueTextFont = ctx.properties.valueTextFont;\nvar valueTextColor = ctx.properties.valueTextColor;\nvar currentVolume = ctx.values.temperature;\nvar decimals = Math.floor(ctx.properties.temperatureStep) === ctx.properties.temperatureStep;\nvar valueText = ctx.api.formatValue(currentVolume, {units: ctx.properties.units, decimals: decimals ? 0 : 1, ignoreUnitSymbol: !ctx.properties.valueUnits});\nctx.api.font(element, valueTextFont, valueTextColor);\nctx.api.text(element, valueText);", "actions": null } ], @@ -432,13 +432,11 @@ "required": true, "subLabel": "Min", "divider": true, - "fieldSuffix": null, - "disableOnProperty": null, "rowClass": "column-xs", - "fieldClass": "", "min": 0, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "maxTemperature", @@ -447,14 +445,10 @@ "default": 45, "required": true, "subLabel": "Max", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "temperatureStep", @@ -462,31 +456,17 @@ "type": "number", "default": 1, "required": true, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 0.5 + "step": 0.5, + "disabled": false, + "visible": true }, { "id": "valueTextColor", "name": "{i18n:scada.symbol.value-text}", "type": "color", "default": "#000000C2", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueTextFont", @@ -499,192 +479,101 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueUnits", "name": "{i18n:scada.symbol.value-units}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "units", "name": "{i18n:scada.symbol.value-units}", "type": "units", "default": "°C", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "valueBoxBackground", "name": "{i18n:scada.symbol.value-box-background}", "type": "color", "default": "#FFFFFF", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "powerButtonBackground", "name": "{i18n:scada.symbol.power-button-background}", "type": "color", "default": "#1ABB48", - "required": null, "subLabel": "Enabled", "divider": true, - "fieldSuffix": null, - "disableOnProperty": null, "rowClass": "column-xs", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "disabledPowerButtonBackground", "name": "{i18n:scada.symbol.power-button-background}", "type": "color", "default": "#FFFFFF", - "required": null, "subLabel": "Disabled", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "runningColor", "name": "{i18n:scada.symbol.running-color}", "type": "color", "default": "#1C943E", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "stoppedColor", "name": "{i18n:scada.symbol.stopped-color}", "type": "color", "default": "#696969", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "warningColor", "name": "{i18n:scada.symbol.warning-color}", "type": "color", "default": "#FAA405", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "criticalColor", "name": "{i18n:scada.symbol.critical-color}", "type": "color", "default": "#D12730", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "heatPumpColor", "name": "{i18n:scada.symbol.heat-pump-color}", "type": "color", "default": "#E5E5E5", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "pipeColor", "name": "{i18n:scada.symbol.pipe-color}", "type": "color", "default": "#FFFFFF", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] }]]> diff --git a/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg b/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg index cd15a16577..d078450c9c 100644 --- a/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 3;\n var majorIntervalLength = 594 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(170, y, 202, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n if (i === 0) {\n majorTickText.attr({x: 160, y: y + 10, 'text-anchor': 'end', class: 'majorTickText'});\n } else if (i === majorIntervals) {\n majorTickText.attr({x: 160, y: y - 5, 'text-anchor': 'end', class: 'majorTickText'});\n } else {\n majorTickText.attr({x: 160, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n }\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(182, minorY, 202, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 3;\n var majorIntervalLength = 594 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(170, y, 202, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.majorUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n if (i === 0) {\n majorTickText.attr({x: 160, y: y + 10, 'text-anchor': 'end', class: 'majorTickText'});\n } else if (i === majorIntervals) {\n majorTickText.attr({x: 160, y: y - 5, 'text-anchor': 'end', class: 'majorTickText'});\n } else {\n majorTickText.attr({x: 160, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n }\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(182, minorY, 202, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -346,6 +346,22 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "majorUnits", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "units", + "subLabel": "{i18n:scada.symbol.units}", + "divider": false, + "supportsUnitConversion": true + }, { "id": "majorIntervals", "name": "{i18n:scada.symbol.major-ticks}", @@ -359,16 +375,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg index 1b91a0cd4a..bd29ac0c4b 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg @@ -43,7 +43,7 @@ }, { "tag": "maxValue", - "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.maxValue);\n}", + "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.api.convertUnitValue(ctx.properties.maxValue, ctx.properties.units).toFixed(0));\n}", "actions": null }, { @@ -53,7 +53,7 @@ }, { "tag": "minValue", - "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.minValue);\n}", + "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.api.convertUnitValue(ctx.properties.minValue, ctx.properties.units).toFixed(0));\n}", "actions": null }, { @@ -73,12 +73,12 @@ }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false}));\n} else {\n element.hide();\n}", "actions": null }, { @@ -193,16 +193,8 @@ "name": "{i18n:scada.symbol.min-max-value}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "minValue", @@ -211,14 +203,9 @@ "default": 0, "required": true, "subLabel": "{i18n:scada.symbol.min-value}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "maxValue", @@ -227,158 +214,89 @@ "default": 100, "required": true, "subLabel": "{i18n:scada.symbol.max-value}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "showHighCriticalScale", "name": "{i18n:scada.symbol.high-critical-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "highCriticalScale", "name": "{i18n:scada.symbol.high-critical-scale}", "type": "number", "default": 85, - "required": null, - "subLabel": "", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showHighCriticalScale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "showHighWarningScale", "name": "{i18n:scada.symbol.high-warning-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "highWarningScale", "name": "{i18n:scada.symbol.high-warning-scale}", "type": "number", "default": 70, - "required": null, - "subLabel": "", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showHighWarningScale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "showLowWarningScale", "name": "{i18n:scada.symbol.low-warning-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "lowWarningScale", "name": "{i18n:scada.symbol.low-warning-scale}", "type": "number", "default": 30, - "required": null, - "subLabel": "", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showLowWarningScale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "showLowCriticalScale", "name": "{i18n:scada.symbol.low-critical-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "lowCriticalScale", "name": "{i18n:scada.symbol.low-critical-scale}", "type": "number", "default": 15, - "required": null, - "subLabel": "", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showLowCriticalScale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "value", "name": "{i18n:scada.symbol.value}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueDecimals", @@ -387,14 +305,12 @@ "default": 0, "required": true, "subLabel": "Decimals", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "value", - "rowClass": "", - "fieldClass": "", "min": 0, "max": 10, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "valueFont", @@ -407,64 +323,36 @@ "weight": "400", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "value", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueColor", "name": "{i18n:scada.symbol.value}", "type": "color", "default": "#002878", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "value", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "label", "name": "{i18n:scada.symbol.label}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelText", "name": "{i18n:scada.symbol.label}", "type": "text", "default": "Outdoor", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "label", - "rowClass": "", "fieldClass": "flex", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelFont", @@ -477,64 +365,35 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "label", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelColor", "name": "{i18n:scada.symbol.label}", "type": "color", "default": "#000000DE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "label", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showUnits", "name": "{i18n:scada.symbol.units}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "units", "name": "{i18n:scada.symbol.units}", "type": "units", "default": "°C", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": "showUnits", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "unitsFont", @@ -547,160 +406,87 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showUnits", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "unitsColor", "name": "{i18n:scada.symbol.units}", "type": "color", "default": "#000000DE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showUnits", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "arrowColor", "name": "{i18n:scada.symbol.arrow-color}", "type": "color", "default": "#666666", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showTarget", "name": "{i18n:scada.symbol.target}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "targetColor", "name": "{i18n:scada.symbol.target}", "type": "color", "default": "#DEDEDE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showTarget", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": true, + "visible": true }, { "id": "scaleColor", "name": "{i18n:scada.symbol.scale-color}", "type": "color", "default": "#C8DFF7", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "defaultWarningScaleColor", "name": "{i18n:scada.symbol.warning-scale-color}", "type": "color", "default": "#EBEBEB", - "required": null, "subLabel": "Default", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "activeWarningScaleColor", "name": "{i18n:scada.symbol.warning-scale-color}", "type": "color", "default": "#FAA405", - "required": null, "subLabel": "Active", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "defaultCriticalScaleColor", "name": "{i18n:scada.symbol.critical-scale-color}", "type": "color", "default": "#666666", - "required": null, "subLabel": "Default", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "activeCriticalScaleColor", "name": "{i18n:scada.symbol.critical-scale-color}", "type": "color", "default": "#D12730", - "required": null, "subLabel": "Active", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] }]]> diff --git a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg index 7b8e7299b8..ed2eb8ec07 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg @@ -43,7 +43,7 @@ }, { "tag": "maxValue", - "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.maxValue);\n}", + "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.api.convertUnitValue(ctx.properties.maxValue, ctx.properties.units).toFixed(0));\n}", "actions": null }, { @@ -53,7 +53,7 @@ }, { "tag": "minValue", - "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.minValue);\n}", + "stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.api.convertUnitValue(ctx.properties.minValue, ctx.properties.units).toFixed(0));\n}", "actions": null }, { @@ -73,12 +73,12 @@ }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false}));\n} else {\n element.hide();\n}", "actions": null }, { @@ -193,16 +193,8 @@ "name": "{i18n:scada.symbol.min-max-value}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "minValue", @@ -211,14 +203,9 @@ "default": 0, "required": true, "subLabel": "{i18n:scada.symbol.min-value}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "maxValue", @@ -227,158 +214,89 @@ "default": 100, "required": true, "subLabel": "{i18n:scada.symbol.max-value}", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "showHighCriticalScale", "name": "{i18n:scada.symbol.high-critical-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "highCriticalScale", "name": "{i18n:scada.symbol.high-critical-scale}", "type": "number", "default": 85, - "required": null, - "subLabel": "", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showHighCriticalScale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "showHighWarningScale", "name": "{i18n:scada.symbol.high-warning-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "highWarningScale", "name": "{i18n:scada.symbol.high-warning-scale}", "type": "number", "default": 70, - "required": null, - "subLabel": "", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showHighWarningScale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "showLowWarningScale", "name": "{i18n:scada.symbol.low-warning-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "lowWarningScale", "name": "{i18n:scada.symbol.low-warning-scale}", "type": "number", "default": 30, - "required": null, - "subLabel": "", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showLowWarningScale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "showLowCriticalScale", "name": "{i18n:scada.symbol.low-critical-scale}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "lowCriticalScale", "name": "{i18n:scada.symbol.low-critical-scale}", "type": "number", "default": 15, - "required": null, - "subLabel": "", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showLowCriticalScale", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "value", "name": "{i18n:scada.symbol.value}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueDecimals", @@ -387,14 +305,12 @@ "default": 0, "required": true, "subLabel": "Decimals", - "divider": null, - "fieldSuffix": null, "disableOnProperty": "value", - "rowClass": "", - "fieldClass": "", "min": 0, "max": 10, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "valueFont", @@ -407,64 +323,36 @@ "weight": "400", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "value", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "valueColor", "name": "{i18n:scada.symbol.value}", "type": "color", "default": "#002878", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "value", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "label", "name": "{i18n:scada.symbol.label}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelText", "name": "{i18n:scada.symbol.label}", "type": "text", "default": "Outdoor", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "label", - "rowClass": "", "fieldClass": "flex", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelFont", @@ -477,64 +365,35 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "label", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "labelColor", "name": "{i18n:scada.symbol.label}", "type": "color", "default": "#000000DE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "label", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showUnits", "name": "{i18n:scada.symbol.units}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "units", "name": "{i18n:scada.symbol.units}", "type": "units", "default": "°C", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": "showUnits", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "supportsUnitConversion": true, + "disabled": false, + "visible": true }, { "id": "unitsFont", @@ -547,160 +406,87 @@ "weight": "500", "style": "normal" }, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showUnits", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "unitsColor", "name": "{i18n:scada.symbol.units}", "type": "color", "default": "#000000DE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showUnits", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "arrowColor", "name": "{i18n:scada.symbol.arrow-color}", "type": "color", "default": "#666666", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "showTarget", "name": "{i18n:scada.symbol.target}", "type": "switch", "default": false, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "targetColor", "name": "{i18n:scada.symbol.target}", "type": "color", "default": "#DEDEDE", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, "disableOnProperty": "showTarget", - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": true, + "visible": true }, { "id": "scaleColor", "name": "{i18n:scada.symbol.scale-color}", "type": "color", "default": "#C8DFF7", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "defaultWarningScaleColor", "name": "{i18n:scada.symbol.warning-scale-color}", "type": "color", "default": "#EBEBEB", - "required": null, "subLabel": "Default", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "activeWarningScaleColor", "name": "{i18n:scada.symbol.warning-scale-color}", "type": "color", "default": "#FAA405", - "required": null, "subLabel": "Active", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "defaultCriticalScaleColor", "name": "{i18n:scada.symbol.critical-scale-color}", "type": "color", "default": "#666666", - "required": null, "subLabel": "Default", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "activeCriticalScaleColor", "name": "{i18n:scada.symbol.critical-scale-color}", "type": "color", "default": "#D12730", - "required": null, "subLabel": "Active", - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true } ] }]]> diff --git a/application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg index 1d6785d0c1..95c4fd3eb5 100644 --- a/application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 45;\n var majorIntervalLength = 525 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 45;\n var majorIntervalLength = 525 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -304,7 +304,7 @@ "type": "color", "default": "#F3F3F3", "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -313,8 +313,8 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", - "disabled": true, + "supportsUnitConversion": true, + "disabled": false, "visible": true }, { @@ -329,7 +329,7 @@ "style": "normal" }, "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -338,7 +338,7 @@ "type": "color", "default": "#0000008A", "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -369,6 +369,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -391,16 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/small-left-meter.svg b/application/src/main/data/json/system/scada_symbols/small-left-meter.svg index 129006ffd7..3d5d6fdcb9 100644 --- a/application/src/main/data/json/system/scada_symbols/small-left-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/small-left-meter.svg @@ -51,7 +51,7 @@ }, { "tag": "scale", - "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n \n var start = 11;\n var end = ctx.properties.valueBox ? 134 : 167;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(50, end+11, 50, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(38, y, 50, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 32, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(44, minorY, 50, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.api.convertUnitValue(ctx.properties.minValue, ctx.properties.valueUnits);\n var maxValue = ctx.api.convertUnitValue(ctx.properties.maxValue, ctx.properties.valueUnits);\n \n var start = 11;\n var end = ctx.properties.valueBox ? 134 : 167;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(50, end+11, 50, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(38, y, 50, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n if (ctx.properties.enableUnitScale) {\n majorText = majorText + ctx.api.unitSymbol(ctx.properties.valueUnits);\n }\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 32, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(44, minorY, 50, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -433,7 +433,7 @@ "type": "units", "default": "%", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -493,6 +493,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "majorIntervals", "name": "{i18n:scada.symbol.major-ticks}", @@ -510,7 +518,7 @@ "name": "{i18n:scada.symbol.major-ticks}", "type": "font", "default": { - "size": 12, + "size": 10, "sizeUnit": "px", "family": "Roboto", "weight": "500", diff --git a/application/src/main/data/json/system/scada_symbols/small-meter.svg b/application/src/main/data/json/system/scada_symbols/small-meter.svg index 551c8d520a..a639475227 100644 --- a/application/src/main/data/json/system/scada_symbols/small-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/small-meter.svg @@ -51,7 +51,7 @@ }, { "tag": "scale", - "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n \n var start = 11;\n var end = ctx.properties.valueBox ? 132 : 167;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(63, end+11, 63, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(51, y, 63, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 45, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(57, minorY, 63, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.api.convertUnitValue(ctx.properties.minValue, ctx.properties.valueUnits);\n var maxValue = ctx.api.convertUnitValue(ctx.properties.maxValue, ctx.properties.valueUnits);\n \n var start = 11;\n var end = ctx.properties.valueBox ? 132 : 167;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(63, end+11, 63, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(51, y, 63, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n if (ctx.properties.enableUnitScale) {\n majorText = majorText + ctx.api.unitSymbol(ctx.properties.valueUnits);\n }\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 45, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(57, minorY, 63, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -424,7 +424,7 @@ "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" }, "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -433,8 +433,8 @@ "type": "units", "default": "%", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", - "disabled": true, + "supportsUnitConversion": true, + "disabled": false, "visible": true }, { @@ -449,7 +449,7 @@ "style": "normal" }, "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -490,7 +490,15 @@ "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" }, "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, + "visible": true + }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, "visible": true }, { @@ -510,14 +518,12 @@ "name": "{i18n:scada.symbol.major-ticks}", "type": "font", "default": { - "size": 12, + "size": 10, "sizeUnit": "px", "family": "Roboto", "weight": "500", "style": "normal" - }, - "disabled": false, - "visible": true + } }, { "id": "majorColor", diff --git a/application/src/main/data/json/system/scada_symbols/small-right-center.svg b/application/src/main/data/json/system/scada_symbols/small-right-center.svg index de1bee5acd..8afe7bc88e 100644 --- a/application/src/main/data/json/system/scada_symbols/small-right-center.svg +++ b/application/src/main/data/json/system/scada_symbols/small-right-center.svg @@ -51,7 +51,7 @@ }, { "tag": "scale", - "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.properties.minValue;\n var maxValue = ctx.properties.maxValue;\n \n var start = 11;\n var end = ctx.properties.valueBox ? 134 : 167;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(73, end+11, 73, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(61, y, 73, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 55, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(67, minorY, 73, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "var scaleSet = element.remember('scaleSet');\nif (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n var minValue = ctx.api.convertUnitValue(ctx.properties.minValue, ctx.properties.valueUnits);\n var maxValue = ctx.api.convertUnitValue(ctx.properties.maxValue, ctx.properties.valueUnits);\n \n var start = 11;\n var end = ctx.properties.valueBox ? 134 : 167;\n var majorIntervalLength = end / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n element.add(ctx.svg.line(73, end+11, 73, 11).stroke({ width: 1 }).attr({class: 'majorTick'}));\n for (var i = 0; i < majorIntervals+1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(61, y, 73, y).stroke({ width: 1 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (maxValue - ((maxValue - (minValue)) / (majorIntervals) * i)).toFixed(0);\n if (ctx.properties.enableUnitScale) {\n majorText = majorText + ctx.api.unitSymbol(ctx.properties.valueUnits);\n }\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 55, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n}\n\nvar majorFont = ctx.properties.majorFont;\nvar majorColor = ctx.properties.majorColor;\nvar minorColor = ctx.properties.minorColor;\nif (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n} else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n}\n\nvar majorTicks = element.find('line.majorTick');\nmajorTicks.forEach(t => t.attr({stroke: majorColor}));\n\nvar majorTicksText = element.find('text.majorTickText');\nctx.api.font(majorTicksText, majorFont, majorColor);\n\nvar minorTicks = element.find('line.minorTick');\nminorTicks.forEach(t => t.attr({stroke: minorColor}));\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(67, minorY, 73, minorY).stroke({ width: 1 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -375,7 +375,7 @@ "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" }, "disableOnProperty": "progressArrow", - "disabled": false, + "disabled": true, "visible": true }, { @@ -424,7 +424,7 @@ "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" }, "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -433,8 +433,8 @@ "type": "units", "default": "%", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", - "disabled": true, + "supportsUnitConversion": true, + "disabled": false, "visible": true }, { @@ -449,7 +449,7 @@ "style": "normal" }, "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -490,7 +490,15 @@ "colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';" }, "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, + "visible": true + }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, "visible": true }, { @@ -510,7 +518,7 @@ "name": "{i18n:scada.symbol.major-ticks}", "type": "font", "default": { - "size": 12, + "size": 10, "sizeUnit": "px", "family": "Roboto", "weight": "500", diff --git a/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg b/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg index b8e3987188..8b0a1a20b0 100644 --- a/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 560 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(268, y, 300, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 258, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(280, minorY, 300, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 560 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(268, y, 300, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 258, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(280, minorY, 300, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -304,7 +304,7 @@ "type": "color", "default": "#F3F3F3", "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -313,8 +313,8 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", - "disabled": true, + "supportsUnitConversion": true, + "disabled": false, "visible": true }, { @@ -329,7 +329,7 @@ "style": "normal" }, "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -338,7 +338,7 @@ "type": "color", "default": "#0000008A", "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -369,6 +369,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -391,16 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/spherical-tank.svg b/application/src/main/data/json/system/scada_symbols/spherical-tank.svg index f5d679fa96..44cd98e6f9 100644 --- a/application/src/main/data/json/system/scada_symbols/spherical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/spherical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 960 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(458, y, 490, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 448, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(470, minorY, 490, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 960 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(458, y, 490, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 448, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(470, minorY, 490, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -304,7 +304,7 @@ "type": "color", "default": "#F3F3F3", "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -313,8 +313,8 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", - "disabled": true, + "supportsUnitConversion": true, + "disabled": false, "visible": true }, { @@ -329,7 +329,7 @@ "style": "normal" }, "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -338,7 +338,7 @@ "type": "color", "default": "#0000008A", "disableOnProperty": "valueBox", - "disabled": true, + "disabled": false, "visible": true }, { @@ -369,6 +369,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -391,16 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg index f5b8e8a892..9666f987d7 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -313,7 +313,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -369,6 +369,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -391,16 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg index 63a21bcf08..03995acbd5 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -313,7 +313,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -369,6 +369,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -391,16 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg index 0d56901bfe..b448d24463 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg @@ -35,7 +35,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 137;\n var majorIntervalLength = 442 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(523, y, 555, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 513, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(535, minorY, 555, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 137;\n var majorIntervalLength = 442 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(523, y, 555, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 513, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(535, minorY, 555, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -314,7 +314,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -370,6 +370,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -392,16 +400,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg index 0d0e368b9b..c4dcc662fc 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -313,7 +313,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -365,7 +365,17 @@ "value": false, "label": "Absolute" } - ] + ], + "disabled": false, + "visible": true + }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true }, { "id": "transparent", @@ -389,16 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg index f2b7bf4d54..11e27b2af8 100644 --- a/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "night-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 1}));", "actions": null }, { @@ -48,7 +48,7 @@ }, { "tag": "off-peak-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 0}));", "actions": null }, { @@ -58,12 +58,12 @@ }, { "tag": "peak-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 2}));", "actions": null }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { @@ -480,6 +480,7 @@ "group": "{i18n:scada.symbol.off-peak-rate}", "type": "text", "default": "T1", + "disableOnProperty": "showOffPeakLabel", "fieldClass": "medium-width", "disabled": false, "visible": true @@ -496,6 +497,7 @@ "weight": "normal", "style": "normal" }, + "disableOnProperty": "showOffPeakLabel", "disabled": false, "visible": true }, @@ -505,6 +507,7 @@ "group": "{i18n:scada.symbol.off-peak-rate}", "type": "color", "default": "#000000", + "disableOnProperty": "showOffPeakLabel", "disabled": false, "visible": true }, @@ -556,6 +559,7 @@ "group": "{i18n:scada.symbol.night-rate}", "type": "text", "default": "T2", + "disableOnProperty": "showNightLabel", "fieldClass": "medium-width", "disabled": false, "visible": true @@ -572,6 +576,7 @@ "weight": "normal", "style": "normal" }, + "disableOnProperty": "showNightLabel", "disabled": false, "visible": true }, @@ -581,6 +586,7 @@ "group": "{i18n:scada.symbol.night-rate}", "type": "color", "default": "#000", + "disableOnProperty": "showNightLabel", "disabled": false, "visible": true }, @@ -632,6 +638,7 @@ "group": "{i18n:scada.symbol.peak-rate}", "type": "text", "default": "T3", + "disableOnProperty": "showPeakLabel", "fieldClass": "medium-width", "disabled": false, "visible": true @@ -648,6 +655,7 @@ "weight": "normal", "style": "normal" }, + "disableOnProperty": "showPeakLabel", "disabled": false, "visible": true }, @@ -657,6 +665,7 @@ "group": "{i18n:scada.symbol.peak-rate}", "type": "color", "default": "#000", + "disableOnProperty": "showPeakLabel", "disabled": false, "visible": true }, @@ -706,6 +715,7 @@ "name": "{i18n:scada.symbol.units}", "type": "units", "default": "kWh", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -720,6 +730,7 @@ "weight": "normal", "style": "normal" }, + "disableOnProperty": "showUnits", "disabled": false, "visible": true }, @@ -728,6 +739,7 @@ "name": "{i18n:scada.symbol.units}", "type": "color", "default": "#000", + "disableOnProperty": "showUnits", "disabled": false, "visible": true } diff --git a/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg index 9c09a6067e..9f7824b4ae 100644 --- a/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -66,7 +66,7 @@ }, { "tag": "valueUnits", - "stateRenderFunction": "var units = ctx.properties.valueUnits;\nctx.api.text(element, units || '');\n", + "stateRenderFunction": "var units = ctx.api.unitSymbol(ctx.properties.valueUnits);\nctx.api.text(element, units || '');\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -464,8 +464,7 @@ "type": "units", "default": "m³/hr", "fieldClass": "medium-width", - "disabled": false, - "visible": true + "supportsUnitConversion": true }, { "id": "valueDecimals", diff --git a/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg index 7adc401836..444a41cb08 100644 --- a/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "day-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.dayValueFont, ctx.properties.dayValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.dayRate, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.dayValueFont, ctx.properties.dayValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.dayRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 0}));", "actions": null }, { @@ -48,12 +48,12 @@ }, { "tag": "night-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, 0, null, 0));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 1}));", "actions": null }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { @@ -427,8 +427,9 @@ "group": "{i18n:scada.symbol.day-rate}", "type": "text", "default": "T1", + "disableOnProperty": "showDayLabel", "fieldClass": "medium-width", - "disabled": false, + "disabled": true, "visible": true }, { @@ -443,7 +444,8 @@ "weight": "normal", "style": "normal" }, - "disabled": false, + "disableOnProperty": "showDayLabel", + "disabled": true, "visible": true }, { @@ -452,7 +454,8 @@ "group": "{i18n:scada.symbol.day-rate}", "type": "color", "default": "#000000", - "disabled": false, + "disableOnProperty": "showDayLabel", + "disabled": true, "visible": true }, { @@ -503,8 +506,9 @@ "group": "{i18n:scada.symbol.night-rate}", "type": "text", "default": "T2", + "disableOnProperty": "showNightLabel", "fieldClass": "medium-width", - "disabled": false, + "disabled": true, "visible": true }, { @@ -519,7 +523,8 @@ "weight": "normal", "style": "normal" }, - "disabled": false, + "disableOnProperty": "showNightLabel", + "disabled": true, "visible": true }, { @@ -528,7 +533,8 @@ "group": "{i18n:scada.symbol.night-rate}", "type": "color", "default": "#000", - "disabled": false, + "disableOnProperty": "showNightLabel", + "disabled": true, "visible": true }, { @@ -577,6 +583,7 @@ "name": "{i18n:scada.symbol.units}", "type": "units", "default": "kWh", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -591,7 +598,8 @@ "weight": "normal", "style": "normal" }, - "disabled": false, + "disableOnProperty": "showUnits", + "disabled": true, "visible": true }, { @@ -599,7 +607,8 @@ "name": "{i18n:scada.symbol.units}", "type": "color", "default": "#000", - "disabled": false, + "disableOnProperty": "showUnits", + "disabled": true, "visible": true } ] diff --git a/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg index 8f9a30e576..544a83dc06 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -66,7 +66,7 @@ }, { "tag": "valueUnits", - "stateRenderFunction": "var units = ctx.properties.valueUnits;\nctx.api.text(element, units || '');\n", + "stateRenderFunction": "var units = ctx.api.unitSymbol(ctx.properties.valueUnits);\nctx.api.text(element, units || '');\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" @@ -464,6 +464,7 @@ "type": "units", "default": "m³/hr", "fieldClass": "medium-width", + "supportsUnitConversion": true, "disabled": false, "visible": true }, diff --git a/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg b/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg index 57a79d72d7..5d8aa42ab5 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 137;\n var majorIntervalLength = 442 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(523, y, 555, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 513, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(535, minorY, 555, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 137;\n var majorIntervalLength = 442 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(523, y, 555, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 513, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(535, minorY, 555, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -313,7 +313,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -369,6 +369,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -391,17 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "required": false, - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg b/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg index 9dee6cc2af..d7f0cb11d9 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 3;\n var majorIntervalLength = 994 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(160, y, 192, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n if (i === 0) {\n majorTickText.attr({x: 150, y: y + 10, 'text-anchor': 'end', class: 'majorTickText'});\n } else if (i === majorIntervals) {\n majorTickText.attr({x: 150, y: y - 5, 'text-anchor': 'end', class: 'majorTickText'});\n } else {\n majorTickText.attr({x: 150, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n }\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(172, minorY, 192, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 3;\n var majorIntervalLength = 994 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(160, y, 192, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.majorUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n if (i === 0) {\n majorTickText.attr({x: 150, y: y + 10, 'text-anchor': 'end', class: 'majorTickText'});\n } else if (i === majorIntervals) {\n majorTickText.attr({x: 150, y: y - 5, 'text-anchor': 'end', class: 'majorTickText'});\n } else {\n majorTickText.attr({x: 150, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n }\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(172, minorY, 192, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -346,6 +346,24 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, + { + "id": "majorUnits", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "units", + "subLabel": "{i18n:scada.symbol.units}", + "divider": false, + "supportsUnitConversion": true, + "disabled": false, + "visible": true + }, { "id": "majorIntervals", "name": "{i18n:scada.symbol.major-ticks}", @@ -359,16 +377,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/vertical-tank.svg index 99fa9b649c..5e51330ded 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = ctx.api.formatValue((tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0), 0, ctx.properties.majorUnits, false);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n var tankCapacity = ctx.properties.scaleDisplayFormat ? 100 : (ctx.values.tankCapacity || 100);\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var currentVolume = (tankCapacity - i * (tankCapacity/majorIntervals)).toFixed(0);\n var majorText = ctx.properties.scaleDisplayFormat ? currentVolume : ctx.api.formatValue(currentVolume, {units: ctx.properties.valueUnits, decimals: 0, ignoreUnitSymbol: !ctx.properties.enableUnitScale});\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -312,7 +312,7 @@ "type": "units", "default": "gal", "subLabel": "{i18n:scada.symbol.units}", - "disableOnProperty": "valueBox", + "supportsUnitConversion": true, "disabled": false, "visible": true }, @@ -369,6 +369,14 @@ "disabled": false, "visible": true }, + { + "id": "enableUnitScale", + "name": "{i18n:scada.symbol.enable-units-scale}", + "type": "switch", + "default": false, + "disabled": false, + "visible": true + }, { "id": "transparent", "name": "{i18n:scada.symbol.transparent-mode}", @@ -391,16 +399,6 @@ "disabled": false, "visible": true }, - { - "id": "majorUnits", - "name": "{i18n:scada.symbol.major-ticks}", - "type": "units", - "subLabel": "{i18n:scada.symbol.units}", - "divider": true, - "disableOnProperty": "scale", - "disabled": false, - "visible": true - }, { "id": "majorFont", "name": "{i18n:scada.symbol.major-ticks}", diff --git a/application/src/main/data/json/system/scada_symbols/voltage-relay-hp.svg b/application/src/main/data/json/system/scada_symbols/voltage-relay-hp.svg index eaa0b02455..fa27214864 100644 --- a/application/src/main/data/json/system/scada_symbols/voltage-relay-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/voltage-relay-hp.svg @@ -34,12 +34,12 @@ }, { "tag": "units", - "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.api.unitSymbol(ctx.properties.units));\n} else {\n element.hide();\n}", "actions": null }, { "tag": "value", - "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.voltage, 0, null, 0));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.voltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));\n} else {\n element.hide();\n}", "actions": null }, { @@ -379,6 +379,7 @@ "name": "{i18n:scada.symbol.units}", "type": "units", "default": "V", + "supportsUnitConversion": true, "disabled": false, "visible": true }, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts index 7bd835de88..aa7669156a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts @@ -43,7 +43,6 @@ import { import { createLabelFromSubscriptionEntityInfo, deepClone, - formatValue, guid, isDefinedAndNotNull, isFirefox, @@ -58,7 +57,13 @@ import { import { BehaviorSubject, forkJoin, Observable, Observer, of, Subject } from 'rxjs'; import { ValueAction, ValueGetter, ValueSetter } from '@home/components/widget/lib/action/action-widget.models'; import { WidgetContext } from '@home/models/widget-component.models'; -import { ColorProcessor, constantColor, Font } from '@shared/models/widget-settings.models'; +import { + ColorProcessor, + constantColor, + Font, + ValueFormatProcessor, + ValueFormatSettings +} from '@shared/models/widget-settings.models'; import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; import { WidgetAction, WidgetActionType, widgetActionTypeTranslationMap } from '@shared/models/widget.models'; @@ -72,10 +77,12 @@ import { FormProperty, FormPropertyType } from '@shared/models/dynamic-form.models'; +import { TbUnit } from '@shared/models/unit.models'; export interface ScadaSymbolApi { generateElementId: () => string; - formatValue: (value: any, dec?: number, units?: string, showZeroDecimals?: boolean) => string | undefined; + formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined; + formatValue(value: any, settings: ValueFormatIdSettings): string; text: (element: Element | Element[], text: string) => void; font: (element: Element | Element[], font: Font, color: string) => void; icon: (element: Element | Element[], icon: string, size?: number, color?: string, center?: boolean) => void; @@ -91,6 +98,8 @@ export interface ScadaSymbolApi { enable: (element: Element | Element[]) => void; callAction: (event: Event, behaviorId: string, value?: any, observer?: Partial>) => void; setValue: (valueId: string, value: any) => void; + unitSymbol: (unit: TbUnit) => string; + convertUnitValue: (value: any, unit: TbUnit) => number; } export interface ScadaSymbolContext { @@ -175,6 +184,10 @@ export interface ScadaSymbolMetadata { properties: FormProperty[]; } +interface ValueFormatIdSettings extends ValueFormatSettings { + id?: number; +} + export const emptyMetadata = (width?: number, height?: number): ScadaSymbolMetadata => ({ title: '', widgetSizeX: width ? Math.max(Math.round(width/100), 1) : 3, @@ -502,6 +515,8 @@ export class ScadaSymbolObject { private stateValueSubjects: {[id: string]: BehaviorSubject} = {}; + private valueProcessor: {[id: string]: ValueFormatProcessor} = {}; + private readonly shapeResize$: ResizeObserver; private readonly destroy$ = new Subject(); @@ -615,7 +630,7 @@ export class ScadaSymbolObject { this.context = { api: { generateElementId: () => generateElementId(), - formatValue, + formatValue: this.formatValue.bind(this), text: this.setElementText.bind(this), font: this.setElementFont.bind(this), icon: this.setElementIcon.bind(this), @@ -631,6 +646,8 @@ export class ScadaSymbolObject { enable: this.enableElement.bind(this), callAction: this.callAction.bind(this), setValue: this.setValue.bind(this), + unitSymbol: this.unitSymbol.bind(this), + convertUnitValue: this.convertUnitValue.bind(this), }, tags: {}, properties: {}, @@ -810,6 +827,34 @@ export class ScadaSymbolObject { } } + private unitSymbol(unit: TbUnit): string { + return this.ctx.$scope.$injector.get(this.ctx.servicesMap.get('unitService')).getTargetUnitSymbol(unit); + } + + private convertUnitValue(value: number, unit: TbUnit): number { + return this.ctx.$scope.$injector.get(this.ctx.servicesMap.get('unitService')).convertUnitValue(value, unit); + } + + private formatValue(value: any, settings: ValueFormatIdSettings): string; + private formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined; + private formatValue(value: any, settingsOrDec?: ValueFormatIdSettings | number, units?: string, showZeroDecimals?: boolean): string { + const id = (settingsOrDec as ValueFormatIdSettings)?.id || 0; + if (!this.valueProcessor[id]) { + let valueFormatSettings: ValueFormatSettings; + if (typeof settingsOrDec === 'object') { + valueFormatSettings = deepClone(settingsOrDec, ['id']); + } else { + valueFormatSettings = { + units, + decimals: settingsOrDec, + showZeroDecimals + } + } + this.valueProcessor[id] = ValueFormatProcessor.fromSettings(this.ctx.$injector, valueFormatSettings); + } + return this.valueProcessor[id].format(value); + } + private onStateValueChanged(id: string, value: any) { if (this.context.values[id] !== value) { this.context.values[id] = value; diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts index 56d2801670..a9edc152e9 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts @@ -1304,7 +1304,7 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags }, formatValue: { meta: 'function', - description: 'Formats numeric value according to specified decimals and units', + description: 'Formats a numeric value according to specified settings or individual parameters for decimals and units, using a ValueFormatProcessor instance.', args: [ { name: 'value', @@ -1312,27 +1312,26 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags type: 'any' }, { - name: 'dec', - description: 'Number of decimal digits', - type: 'number', + name: 'settingsOrDec', + description: 'Either a ValueFormatIdSettings object containing formatting settings or the number of decimal digits. ValueFormatIdSettings includes: decimals (number of decimal digits, optional), units (unit specification as string or TbUnitMapping, optional), showZeroDecimals (whether to keep zero decimal digits, optional), ignoreUnitSymbol (whether to exclude unit symbol from output, optional), and id (unique identifier for the processor, optional).', type: 'ValueFormatIdSettings | number', optional: true }, { name: 'units', - description: 'Units to append to the formatted value', + description: 'Units to append to the formatted value, applied only if settingsOrDec is a number', type: 'string', optional: true }, { name: 'showZeroDecimals', - description: 'Whether to keep zero decimal digits', + description: 'Whether to keep zero decimal digits, applied only if settingsOrDec is a number', type: 'boolean', optional: true } ], return: { - type: 'string', - description: 'Formatted value' + description: 'The formatted value as a string. Returns undefined if the value cannot be formatted and settingsOrDec is not an object.', + type: 'string | undefined' } }, text: { @@ -1479,6 +1478,47 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags type: 'any' } ] + }, + unitSymbol: { + meta: 'function', + description: 'Retrieves the target unit symbol based on the current unit system or the provided unit.', + args: [ + { + name: 'unit', + description: 'Unit specification, either a string or a TbUnitMapping object defining unit mappings for different systems.', + type: 'TbUnit' + } + ], + return: { + description: 'The target unit symbol as a string, or the \'from\' unit if no mapping is found for the current unit system.', + type: 'string' + } + }, + convertUnitValue: { + meta: 'function', + description: 'Converts a numeric value from one unit to another, either using a TbUnit mapping or explicit from/to units. Returns the original value on conversion failure.', + args: [ + { + name: 'value', + description: 'Numeric value to be converted', + type: 'number' + }, + { + name: 'unit', + description: 'Unit specification, either a string representing the source unit or a TbUnitMapping object for system-based conversion', + type: 'TbUnit' + }, + { + name: 'to', + description: 'Optional target unit to convert to. If not provided, the target unit is derived from the TbUnitMapping and current unit system.', + type: 'string', + optional: true + } + ], + return: { + description: 'The converted numeric value. Returns the original value if conversion fails or no conversion is needed.', + type: 'number' + } } } }, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 55e136e031..28fbe52c5c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3442,6 +3442,7 @@ "power-button-background": "Power button background", "value-box-background": "Value box background", "value-units": "Value units", + "enable-units-scale": "Enable units on scale", "filtration-mode": "Filtration mode", "filtration-mode-hint": "Integer value indication the current filtration mode.", "filtration-mode-update": "Filtration mode update state", From 2dadf7207c67fc0a19cbcf130394cc8dca037788 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 5 Jun 2025 17:15:56 +0300 Subject: [PATCH 293/335] UI: Add "Confirm OTA Update" title to OTA update confirmation dialog --- ui-ngx/src/app/core/http/ota-package.service.ts | 11 ++++++----- ui-ngx/src/assets/locale/locale.constant-en_US.json | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts index 09f887c038..0ddbfc44de 100644 --- a/ui-ngx/src/app/core/http/ota-package.service.ts +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -129,15 +129,16 @@ export class OtaPackageService { } return forkJoin(tasks).pipe( mergeMap(([deviceFirmwareUpdate, deviceSoftwareUpdate]) => { - let text = ''; + const lines: string[] = []; if (deviceFirmwareUpdate > 0) { - text += this.translate.instant('ota-update.change-firmware', {count: deviceFirmwareUpdate}); + lines.push(this.translate.instant('ota-update.change-firmware', {count: deviceFirmwareUpdate})); } if (deviceSoftwareUpdate > 0) { - text += text.length ? ' ' : ''; - text += this.translate.instant('ota-update.change-software', {count: deviceSoftwareUpdate}); + lines.push(this.translate.instant('ota-update.change-software', {count: deviceSoftwareUpdate})); } - return text !== '' ? this.dialogService.confirm('', text, null, this.translate.instant('common.proceed')) : of(true); + return lines.length + ? this.dialogService.confirm(this.translate.instant('ota-update.change-ota-setting-title'), lines.join('
'), null, this.translate.instant('common.proceed')) + : of(true); }) ); } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 55e136e031..7591386c1a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4167,6 +4167,7 @@ "checksum-copied-message": "Package checksum has been copied to clipboard", "change-firmware": "Change of the firmware may cause update of { count, plural, =1 {1 device} other {# devices} }.", "change-software": "Change of the software may cause update of { count, plural, =1 {1 device} other {# devices} }.", + "change-ota-setting-title": "Are you sure you want to change OTA settings?", "chose-compatible-device-profile": "The uploaded package will be available only for devices with the chosen profile.", "chose-firmware-distributed-device": "Choose firmware that will be distributed to the devices", "chose-software-distributed-device": "Choose software that will be distributed to the devices", From d4c83b7fc332fdc49e242702490d718f5f4ca860 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 6 Jun 2025 11:22:49 +0300 Subject: [PATCH 294/335] Improve notification processing strategy. Fix tests --- .../DefaultNotificationRuleProcessor.java | 35 +++++++++++++------ .../ResourcesShortageTriggerProcessor.java | 7 +++- .../system/DefaultSystemInfoService.java | 25 +++++++++---- .../notification/NotificationRuleApiTest.java | 4 +++ .../server/common/data/SystemInfoData.java | 1 + .../ResourcesShortageNotificationInfo.java | 6 +++- .../EdgeCommunicationFailureTrigger.java | 9 +++-- .../rule/trigger/EdgeConnectionTrigger.java | 9 +++-- .../trigger/NewPlatformVersionTrigger.java | 9 +++-- .../rule/trigger/NotificationRuleTrigger.java | 11 ++++-- .../rule/trigger/RateLimitsTrigger.java | 8 +++-- .../trigger/ResourcesShortageTrigger.java | 8 +++-- .../RemoteNotificationRuleProcessor.java | 3 +- .../notification/DefaultNotifications.java | 2 +- .../en_US/notification/resources_shortage.md | 2 ++ 15 files changed, 104 insertions(+), 35 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java index 62455117e0..2ed96fabd5 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.notification.rule; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.context.event.EventListener; @@ -35,6 +36,7 @@ import org.thingsboard.server.common.data.notification.NotificationRequestStatus import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger.DeduplicationStrategy; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -66,8 +68,8 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess private final NotificationDeduplicationService deduplicationService; private final PartitionService partitionService; private final RateLimitService rateLimitService; - @Autowired @Lazy - private NotificationCenter notificationCenter; + @Lazy + private final NotificationCenter notificationCenter; private final NotificationExecutorService notificationExecutor; private final Map triggerProcessors = new EnumMap<>(NotificationRuleTriggerType.class); @@ -82,14 +84,11 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess if (enabledRules.isEmpty()) { return; } - if (trigger.deduplicate()) { - enabledRules = new ArrayList<>(enabledRules); - enabledRules.removeIf(rule -> deduplicationService.alreadyProcessed(trigger, rule)); - } - final List rules = enabledRules; - for (NotificationRule rule : rules) { + + List rulesToProcess = filterNotificationRules(trigger, enabledRules); + for (NotificationRule rule : rulesToProcess) { try { - processNotificationRule(rule, trigger); + processNotificationRule(rule, trigger, DeduplicationStrategy.ONLY_MATCHING.equals(trigger.getDeduplicationStrategy())); } catch (Throwable e) { log.error("Failed to process notification rule {} for trigger type {} with trigger object {}", rule.getId(), rule.getTriggerType(), trigger, e); } @@ -100,7 +99,21 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess }); } - private void processNotificationRule(NotificationRule rule, NotificationRuleTrigger trigger) { + @NotNull + private List filterNotificationRules(NotificationRuleTrigger trigger, List enabledRules) { + List rulesToProcess = new ArrayList<>(enabledRules); + rulesToProcess.removeIf(rule -> switch (trigger.getDeduplicationStrategy()) { + case ONLY_MATCHING -> { + boolean matched = matchesFilter(trigger, rule.getTriggerConfig()); + yield !matched || deduplicationService.alreadyProcessed(trigger, rule); + } + case ALL -> deduplicationService.alreadyProcessed(trigger, rule); + default -> false; + }); + return rulesToProcess; + } + + private void processNotificationRule(NotificationRule rule, NotificationRuleTrigger trigger, boolean alreadyMatched) { NotificationRuleTriggerConfig triggerConfig = rule.getTriggerConfig(); log.debug("Processing notification rule '{}' for trigger type {}", rule.getName(), rule.getTriggerType()); @@ -114,7 +127,7 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess return; } - if (matchesFilter(trigger, triggerConfig)) { + if (alreadyMatched || matchesFilter(trigger, triggerConfig)) { if (!rateLimitService.checkRateLimit(LimitedApi.NOTIFICATION_REQUESTS_PER_RULE, rule.getTenantId(), rule.getId())) { log.debug("[{}] Rate limit for notification requests per rule was exceeded (rule '{}')", rule.getTenantId(), rule.getName()); return; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java index aefb628d2d..09c8d76eca 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ResourcesShortageTriggerProcessor.java @@ -39,7 +39,12 @@ public class ResourcesShortageTriggerProcessor implements NotificationRuleTrigge @Override public RuleOriginatedNotificationInfo constructNotificationInfo(ResourcesShortageTrigger trigger) { - return ResourcesShortageNotificationInfo.builder().resource(trigger.getResource().name()).usage(trigger.getUsage()).build(); + return ResourcesShortageNotificationInfo.builder() + .resource(trigger.getResource().name()) + .usage(trigger.getUsage()) + .serviceId(trigger.getServiceId()) + .serviceType(trigger.getServiceType()) + .build(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index adc87957d3..a38c2721c1 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -185,9 +185,14 @@ public class DefaultSystemInfoService extends TbApplicationEventListener clusterSystemData = getSystemData(serviceInfoProvider.getServiceInfo()); clusterSystemData.forEach(data -> { - notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(data.getCpuUsage()).build()); - notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(data.getMemoryUsage()).build()); - notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(data.getDiscUsage()).build()); + Arrays.stream(Resource.values()).forEach(resource -> { + notificationRuleProcessor.process(ResourcesShortageTrigger.builder() + .resource(resource) + .serviceId(data.getServiceId()) + .serviceType(data.getServiceType()) + .usage(extractResourceUsage(data, resource)) + .build()); + }); }); BasicTsKvEntry clusterDataKv = new BasicTsKvEntry(ts, new JsonDataEntry("clusterSystemData", JacksonUtil.toString(clusterSystemData))); doSave(Arrays.asList(new BasicTsKvEntry(ts, new BooleanDataEntry("clusterMode", true)), clusterDataKv)); @@ -200,17 +205,17 @@ public class DefaultSystemInfoService extends TbApplicationEventListener { long value = (long) v; tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuUsage", value))); - notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(value).build()); + notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.CPU).usage(value).serviceId(serviceInfoProvider.getServiceId()).serviceType(serviceInfoProvider.getServiceType()).build()); }); getMemoryUsage().ifPresent(v -> { long value = (long) v; tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("memoryUsage", value))); - notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(value).build()); + notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.RAM).usage(value).serviceId(serviceInfoProvider.getServiceId()).serviceType(serviceInfoProvider.getServiceType()).build()); }); getDiscSpaceUsage().ifPresent(v -> { long value = (long) v; tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("discUsage", value))); - notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(value).build()); + notificationRuleProcessor.process(ResourcesShortageTrigger.builder().resource(Resource.STORAGE).usage(value).serviceId(serviceInfoProvider.getServiceId()).serviceType(serviceInfoProvider.getServiceType()).build()); }); getCpuCount().ifPresent(v -> tsList.add(new BasicTsKvEntry(ts, new LongDataEntry("cpuCount", (long) v)))); @@ -258,6 +263,14 @@ public class DefaultSystemInfoService extends TbApplicationEventListener info.getCpuUsage(); + case RAM -> info.getMemoryUsage(); + case STORAGE -> info.getDiscUsage(); + }; + } + @PreDestroy private void destroy() { if (scheduler != null) { diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 282e959fcd..aea43fa4f4 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -825,6 +825,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { notificationRuleProcessor.process(ResourcesShortageTrigger.builder() .resource(Resource.RAM) .usage(15L) + .serviceType("serviceType") + .serviceId("serviceId") .build()); TimeUnit.MILLISECONDS.sleep(300); } @@ -837,6 +839,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { notificationRuleProcessor.process(ResourcesShortageTrigger.builder() .resource(Resource.RAM) .usage(5L) + .serviceType("serviceType") + .serviceId("serviceId") .build()); await("").atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne()); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SystemInfoData.java b/common/data/src/main/java/org/thingsboard/server/common/data/SystemInfoData.java index c979fbffd1..afbad6e3a2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SystemInfoData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SystemInfoData.java @@ -20,6 +20,7 @@ import lombok.Data; @Data public class SystemInfoData { + @Schema(description = "Service Id.") private String serviceId; @Schema(description = "Service type.") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java index 24cb21febd..c05a10ef8f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/ResourcesShortageNotificationInfo.java @@ -30,12 +30,16 @@ public class ResourcesShortageNotificationInfo implements RuleOriginatedNotifica private String resource; private Long usage; + private String serviceId; + private String serviceType; @Override public Map getTemplateData() { return Map.of( "resource", resource, - "usage", String.valueOf(usage) + "usage", String.valueOf(usage), + "serviceId", serviceId, + "serviceType", serviceType ); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java index 4124eb04f8..5672b2c98c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java @@ -23,12 +23,16 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import java.io.Serial; import java.util.concurrent.TimeUnit; @Data @Builder public class EdgeCommunicationFailureTrigger implements NotificationRuleTrigger { + @Serial + private static final long serialVersionUID = 2918443863787603524L; + private final TenantId tenantId; private final CustomerId customerId; private final EdgeId edgeId; @@ -37,8 +41,8 @@ public class EdgeCommunicationFailureTrigger implements NotificationRuleTrigger private final String error; @Override - public boolean deduplicate() { - return true; + public DeduplicationStrategy getDeduplicationStrategy() { + return DeduplicationStrategy.ALL; } @Override @@ -60,4 +64,5 @@ public class EdgeCommunicationFailureTrigger implements NotificationRuleTrigger public EntityId getOriginatorEntityId() { return edgeId; } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java index fc3b69e697..0da465ec09 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java @@ -23,12 +23,16 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import java.io.Serial; import java.util.concurrent.TimeUnit; @Data @Builder public class EdgeConnectionTrigger implements NotificationRuleTrigger { + @Serial + private static final long serialVersionUID = -261939829962721957L; + private final TenantId tenantId; private final CustomerId customerId; private final EdgeId edgeId; @@ -36,8 +40,8 @@ public class EdgeConnectionTrigger implements NotificationRuleTrigger { private final String edgeName; @Override - public boolean deduplicate() { - return true; + public DeduplicationStrategy getDeduplicationStrategy() { + return DeduplicationStrategy.ALL; } @Override @@ -59,4 +63,5 @@ public class EdgeConnectionTrigger implements NotificationRuleTrigger { public EntityId getOriginatorEntityId() { return edgeId; } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java index 204ae15e57..50ee0768d9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java @@ -22,10 +22,15 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import java.io.Serial; + @Data @Builder public class NewPlatformVersionTrigger implements NotificationRuleTrigger { + @Serial + private static final long serialVersionUID = 3298785969736390092L; + private final UpdateMessage updateInfo; @Override @@ -45,8 +50,8 @@ public class NewPlatformVersionTrigger implements NotificationRuleTrigger { @Override - public boolean deduplicate() { - return true; + public DeduplicationStrategy getDeduplicationStrategy() { + return DeduplicationStrategy.ALL; } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java index 31940d6ac8..f6cf398b44 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java @@ -29,9 +29,8 @@ public interface NotificationRuleTrigger extends Serializable { EntityId getOriginatorEntityId(); - - default boolean deduplicate() { - return false; + default DeduplicationStrategy getDeduplicationStrategy() { + return DeduplicationStrategy.NONE; } default String getDeduplicationKey() { @@ -43,4 +42,10 @@ public interface NotificationRuleTrigger extends Serializable { return 0; } + enum DeduplicationStrategy { + NONE, + ALL, + ONLY_MATCHING + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RateLimitsTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RateLimitsTrigger.java index 39d570a9e0..37e984c6f5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RateLimitsTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RateLimitsTrigger.java @@ -22,12 +22,16 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import java.io.Serial; import java.util.concurrent.TimeUnit; @Data @Builder public class RateLimitsTrigger implements NotificationRuleTrigger { + @Serial + private static final long serialVersionUID = -4423112145409424886L; + private final TenantId tenantId; private final LimitedApi api; private final EntityId limitLevel; @@ -45,8 +49,8 @@ public class RateLimitsTrigger implements NotificationRuleTrigger { @Override - public boolean deduplicate() { - return true; + public DeduplicationStrategy getDeduplicationStrategy() { + return DeduplicationStrategy.ALL; } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java index f12c80d5db..be2485c959 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ResourcesShortageTrigger.java @@ -33,6 +33,8 @@ public class ResourcesShortageTrigger implements NotificationRuleTrigger { private Resource resource; private Long usage; + private String serviceId; + private String serviceType; @Override public TenantId getTenantId() { @@ -45,13 +47,13 @@ public class ResourcesShortageTrigger implements NotificationRuleTrigger { } @Override - public boolean deduplicate() { - return true; + public DeduplicationStrategy getDeduplicationStrategy() { + return DeduplicationStrategy.ONLY_MATCHING; } @Override public String getDeduplicationKey() { - return resource.name(); + return String.join(":", resource.name(), serviceId, serviceType); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/notification/RemoteNotificationRuleProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/notification/RemoteNotificationRuleProcessor.java index 194f0ce962..a410ac5d04 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/notification/RemoteNotificationRuleProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/notification/RemoteNotificationRuleProcessor.java @@ -22,6 +22,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.JavaSerDesUtil; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger.DeduplicationStrategy; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; @@ -47,7 +48,7 @@ public class RemoteNotificationRuleProcessor implements NotificationRuleProcesso @Override public void process(NotificationRuleTrigger trigger) { try { - if (trigger.deduplicate() && deduplicationService.alreadyProcessed(trigger)) { + if (!DeduplicationStrategy.NONE.equals(trigger.getDeduplicationStrategy()) && deduplicationService.alreadyProcessed(trigger)) { return; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index efd69a4e61..7cee3d04ae 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -376,7 +376,7 @@ public class DefaultNotifications { public static final DefaultNotification resourcesShortage = DefaultNotification.builder() .name("Resources shortage notification") .type(NotificationType.RESOURCES_SHORTAGE) - .subject("Warning: ${resource} shortage") + .subject("Warning: ${resource} shortage for ${serviceId}") .text("${resource} usage is at ${usage}%.") .icon("warning") .rule(DefaultRule.builder() diff --git a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md index 9b469c0f85..8d34ad57e7 100644 --- a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md +++ b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md @@ -11,6 +11,8 @@ Available template parameters: * `resource` - the resource name; * `usage` - the resource usage value; +* `serviceId` - the service id (convenient in cluster); +* `serviceType` - the service type (convenient in cluster); Parameter names must be wrapped using `${...}`. For example: `${resource}`. You may also modify the value of the parameter with one of the suffixes: From acb7a0d770b164efa6ab914be2912143d1b5acef Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 6 Jun 2025 11:25:37 +0300 Subject: [PATCH 295/335] Minor --- .../src/assets/help/en_US/notification/resources_shortage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md index 8d34ad57e7..4655796c69 100644 --- a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md +++ b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md @@ -11,8 +11,8 @@ Available template parameters: * `resource` - the resource name; * `usage` - the resource usage value; -* `serviceId` - the service id (convenient in cluster); -* `serviceType` - the service type (convenient in cluster); +* `serviceId` - the service id (convenient in cluster setup); +* `serviceType` - the service type (convenient in cluster setup); Parameter names must be wrapped using `${...}`. For example: `${resource}`. You may also modify the value of the parameter with one of the suffixes: From 19bd50ec0fc9e21e5b177359b41cf073b3f8df3d Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 6 Jun 2025 11:27:17 +0300 Subject: [PATCH 296/335] Fix resource_shortage md to be in sync with PE --- .../src/assets/help/en_US/notification/resources_shortage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md index 4655796c69..6ea03a514c 100644 --- a/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md +++ b/ui-ngx/src/assets/help/en_US/notification/resources_shortage.md @@ -9,8 +9,8 @@ See the available types and parameters below: Available template parameters: -* `resource` - the resource name; -* `usage` - the resource usage value; +* `resource` - the resource name (e.g., "CPU", "RAM", "STORAGE"); +* `usage` - the current usage value of the resource; * `serviceId` - the service id (convenient in cluster setup); * `serviceType` - the service type (convenient in cluster setup); From b22ca3f87362bc30bbfbdc4b26a5617351e5c00e Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 6 Jun 2025 13:43:51 +0300 Subject: [PATCH 297/335] UI: Fixed show value after unit converted in gauges and range chart --- .../components/widget/lib/analogue-gauge.models.ts | 6 +++--- .../widget/lib/chart/range-chart-widget.component.ts | 11 +++++++++-- .../widget/lib/chart/range-chart-widget.models.ts | 7 ++++--- .../home/components/widget/lib/digital-gauge.ts | 1 + 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts index c6b523edc6..167946b45e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts @@ -285,10 +285,10 @@ function getValueDec(ctx: WidgetContext, _settings: AnalogueGaugeSettings): numb if (ctx.data && ctx.data[0]) { dataKey = ctx.data[0].dataKey; } - if (dataKey && isDefined(dataKey.decimals)) { + if (dataKey && isDefinedAndNotNull(dataKey.decimals)) { return dataKey.decimals; } else { - return isDefinedAndNotNull(ctx.decimals) ? ctx.decimals : 0; + return ctx.decimals ?? 0; } } @@ -300,6 +300,6 @@ function getUnits(ctx: WidgetContext, settings: AnalogueGaugeSettings): TbUnit { if (dataKey?.units) { return dataKey.units; } else { - return isDefinedAndNotNull(settings.units) ? settings.units : ctx.units; + return settings.units ?? ctx.units; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts index a9295e98e4..1d1257c2e8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts @@ -32,7 +32,8 @@ import { ComponentStyle, getDataKey, overlayStyle, - textStyle + textStyle, + ValueFormatProcessor } from '@shared/models/widget-settings.models'; import { isDefinedAndNotNull } from '@core/utils'; import { @@ -113,11 +114,17 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn this.units = unitService.getTargetUnitSymbol(units); this.unitConvertor = unitService.geUnitConverter(units); + const valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, { + units, + decimals: this.decimals, + ignoreUnitSymbol: true + }); + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); this.overlayStyle = overlayStyle(this.settings.background.overlay); this.padding = this.settings.background.overlay.enabled ? undefined : this.settings.padding; - this.rangeItems = toRangeItems(this.settings.rangeColors, this.unitConvertor); + this.rangeItems = toRangeItems(this.settings.rangeColors, valueFormat); this.visibleRangeItems = this.rangeItems.filter(item => item.visible); this.showLegend = this.settings.showLegend && !!this.rangeItems.length; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index 7134a924fe..f4210e2203 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -22,6 +22,7 @@ import { Font, simpleDateFormat, sortedColorRange, + ValueFormatProcessor, ValueSourceType } from '@shared/models/widget-settings.models'; import { LegendPosition } from '@shared/models/widget.models'; @@ -291,21 +292,21 @@ export const rangeChartTimeSeriesKeySettings = (settings: RangeChartWidgetSettin } }); -export const toRangeItems = (colorRanges: Array, convertValue: (x: number) => number): RangeItem[] => { +export const toRangeItems = (colorRanges: Array, valueFormat: ValueFormatProcessor): RangeItem[] => { const rangeItems: RangeItem[] = []; let counter = 0; const ranges = sortedColorRange(filterIncludingColorRanges(colorRanges)).filter(r => isNumber(r.from) || isNumber(r.to)); for (let i = 0; i < ranges.length; i++) { const range = ranges[i]; let from = range.from; - const to = isDefinedAndNotNull(range.to) ? convertValue(range.to) : range.to; + const to = isDefinedAndNotNull(range.to) ? Number(valueFormat.format(range.to)) : range.to; if (i > 0) { const prevRange = ranges[i - 1]; if (isNumber(prevRange.to) && isNumber(from) && from < prevRange.to) { from = prevRange.to; } } - from = isDefinedAndNotNull(from) ? convertValue(from) : from; + from = isDefinedAndNotNull(from) ? Number(valueFormat.format(from)) : from; rangeItems.push( { index: counter++, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts index 81ab5d6bd9..f88be8783b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts @@ -125,6 +125,7 @@ export class TbCanvasDigitalGauge { this.barColorProcessor = ColorProcessor.fromSettings(settings.barColor, this.ctx); this.valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, { units: this.localSettings.units, + decimals: this.localSettings.decimals, ignoreUnitSymbol: true }); From 65934b01e7d98c5f7f315e31971c993b628e7881 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 6 Jun 2025 15:11:31 +0300 Subject: [PATCH 298/335] Add test for only-matching strategy for resource shortage --- .../notification/NotificationRuleApiTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index aea43fa4f4..bab70ea505 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -845,6 +845,42 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { await("").atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne()); } + @Test + public void testNotificationsResourcesShortage_whenThresholdChangeToMatchingFilter_thenSendNotification() throws Exception { + loginSysAdmin(); + ResourcesShortageNotificationRuleTriggerConfig triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder() + .ramThreshold(1f) + .cpuThreshold(1f) + .storageThreshold(1f) + .build(); + NotificationRule rule = createNotificationRule(triggerConfig, "Warning: ${resource} shortage", "${resource} shortage", createNotificationTarget(tenantAdminUserId).getId()); + loginTenantAdmin(); + + Method method = DefaultSystemInfoService.class.getDeclaredMethod("saveCurrentMonolithSystemInfo"); + method.setAccessible(true); + method.invoke(systemInfoService); + + TimeUnit.SECONDS.sleep(5); + assertThat(getMyNotifications(false, 100)).size().isZero(); + + loginSysAdmin(); + triggerConfig = ResourcesShortageNotificationRuleTriggerConfig.builder() + .ramThreshold(0.01f) + .cpuThreshold(1f) + .storageThreshold(1f) + .build(); + rule.setTriggerConfig(triggerConfig); + saveNotificationRule(rule); + loginTenantAdmin(); + + method.invoke(systemInfoService); + + await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); + Notification notification = getMyNotifications(false, 100).get(0); + assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); + assertThat(notification.getText()).isEqualTo("RAM shortage"); + } + @Test public void testNotificationRuleDisabling() throws Exception { EntityActionNotificationRuleTriggerConfig triggerConfig = new EntityActionNotificationRuleTriggerConfig(); From 60dd648d54e0847863439686961d8790af006e58 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 6 Jun 2025 15:16:00 +0300 Subject: [PATCH 299/335] Slavik skazav vidaliti notnull --- .../notification/rule/DefaultNotificationRuleProcessor.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java index 2ed96fabd5..d6cee80ebf 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.notification.rule; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.context.event.EventListener; @@ -99,7 +98,6 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess }); } - @NotNull private List filterNotificationRules(NotificationRuleTrigger trigger, List enabledRules) { List rulesToProcess = new ArrayList<>(enabledRules); rulesToProcess.removeIf(rule -> switch (trigger.getDeduplicationStrategy()) { From d41b5f569ec48ae1f20b441ed4b336af83109ec3 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Jun 2025 16:33:29 +0300 Subject: [PATCH 300/335] UI: Fixed tooltip with string false and empty tooltip --- .../widget/lib/chart/time-series-chart-tooltip.models.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts index 713e7b9373..22c16c0219 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts @@ -17,9 +17,7 @@ import { isFunction } from '@core/utils'; import { FormattedData } from '@shared/models/widget.models'; import { DateFormatProcessor, DateFormatSettings, Font } from '@shared/models/widget-settings.models'; -import { - TimeSeriesChartDataItem, -} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { TimeSeriesChartDataItem } from '@home/components/widget/lib/chart/time-series-chart.models'; import { Renderer2, SecurityContext } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { CallbackDataParams } from 'echarts/types/dist/shared'; @@ -104,6 +102,9 @@ export class TimeSeriesChartTooltip { if (!tooltipParams.items.length && !tooltipParams.comparisonItems.length) { return null; } + if (this.settings.tooltipHideZeroFalse && !tooltipParams.items.some(value => value.param.value[1] && value.param.value[1] !== 'false')) { + return undefined; + } const tooltipElement: HTMLElement = this.renderer.createElement('div'); this.renderer.setStyle(tooltipElement, 'display', 'flex'); @@ -130,7 +131,7 @@ export class TimeSeriesChartTooltip { this.renderer.appendChild(tooltipItemsElement, this.constructTooltipDateElement(items[0].param, interval)); } for (const item of items) { - if (!this.settings.tooltipHideZeroFalse || item.param.value[1]) { + if (!this.settings.tooltipHideZeroFalse || (item.param.value[1] && item.param.value[1] !== 'false')) { this.renderer.appendChild(tooltipItemsElement, this.constructTooltipSeriesElement(item)); } } From 5f66cd041b78ec6b7aed535f008272a615b27734 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Jun 2025 16:36:40 +0300 Subject: [PATCH 301/335] UI: Hide zero false tooltip for rule engine statistics --- .../main/data/json/demo/dashboards/rule_engine_statistics.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json index 0cfcebef5d..d167e4087c 100644 --- a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json +++ b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json @@ -564,6 +564,7 @@ }, "tooltipDateColor": "rgba(0, 0, 0, 0.76)", "tooltipDateInterval": true, + "tooltipHideZeroFalse": true, "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", "tooltipBackgroundBlur": 4, "animation": { @@ -977,6 +978,7 @@ }, "tooltipDateColor": "rgba(0, 0, 0, 0.76)", "tooltipDateInterval": true, + "tooltipHideZeroFalse": true, "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", "tooltipBackgroundBlur": 4, "animation": { From 347bdbdb71d20b5a0c1514120878d7eeacc3dcac Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Jun 2025 17:32:30 +0300 Subject: [PATCH 302/335] UI: Update api usage dashboard --- ui-ngx/src/assets/dashboard/api_usage.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui-ngx/src/assets/dashboard/api_usage.json b/ui-ngx/src/assets/dashboard/api_usage.json index 5564223512..9f14aad306 100644 --- a/ui-ngx/src/assets/dashboard/api_usage.json +++ b/ui-ngx/src/assets/dashboard/api_usage.json @@ -7885,6 +7885,7 @@ }, "tooltipDateColor": "rgba(0, 0, 0, 0.76)", "tooltipDateInterval": true, + "tooltipHideZeroFalse": true, "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", "tooltipBackgroundBlur": 4, "animation": { @@ -8293,6 +8294,7 @@ }, "tooltipDateColor": "rgba(0, 0, 0, 0.76)", "tooltipDateInterval": true, + "tooltipHideZeroFalse": true, "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", "tooltipBackgroundBlur": 4, "animation": { From 8dc9a68c625b34ad7523aea6bdfe5817fffc4b12 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Fri, 6 Jun 2025 18:47:45 +0300 Subject: [PATCH 303/335] Update cached activity status only after a successful database save --- .../state/DefaultDeviceStateService.java | 97 ++-- .../DefaultTelemetrySubscriptionService.java | 4 +- .../telemetry/InternalTelemetryService.java | 4 +- .../src/main/resources/thingsboard.yml | 2 + .../state/DefaultDeviceStateServiceTest.java | 527 ++++++++---------- 5 files changed, 283 insertions(+), 351 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index cc476d377d..f46323f702 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -27,11 +27,10 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; -import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.Pair; +import org.checkerframework.checker.nullness.qual.NonNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; @@ -170,35 +169,22 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService> stats = new HashMap<>(); for (DeviceStateData stateData : deviceStates.values()) { @@ -587,13 +572,12 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService { + stateData.getState().setActive(active); + pushRuleEngineMessage(stateData, active ? TbMsgType.ACTIVITY_EVENT : TbMsgType.INACTIVITY_EVENT); + TbMsgMetaData metaData = stateData.getMetaData(); + notificationRuleProcessor.process(DeviceActivityTrigger.builder() + .tenantId(tenantId) + .customerId(stateData.getCustomerId()) + .deviceId(deviceId) + .active(active) + .deviceName(metaData.getValue("deviceName")) + .deviceType(metaData.getValue("deviceType")) + .deviceLabel(metaData.getValue("deviceLabel")) + .build()); + }, deviceStateCallbackExecutor); } boolean cleanDeviceStateIfBelongsToExternalPartition(TenantId tenantId, final DeviceId deviceId) { @@ -634,8 +625,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService save(TenantId tenantId, DeviceId deviceId, String key, long value) { + return save(tenantId, deviceId, new LongDataEntry(key, value), getCurrentTimeMillis()); } - private void save(TenantId tenantId, DeviceId deviceId, String key, boolean value) { - save(tenantId, deviceId, new BooleanDataEntry(key, value), getCurrentTimeMillis()); + private ListenableFuture save(TenantId tenantId, DeviceId deviceId, String key, boolean value) { + return save(tenantId, deviceId, new BooleanDataEntry(key, value), getCurrentTimeMillis()); } - private void save(TenantId tenantId, DeviceId deviceId, KvEntry kvEntry, long ts) { + private ListenableFuture save(TenantId tenantId, DeviceId deviceId, KvEntry kvEntry, long ts) { + ListenableFuture future; if (persistToTelemetry) { - tsSubService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() + future = tsSubService.saveTimeseriesInternal(TimeseriesSaveRequest.builder() .tenantId(tenantId) .entityId(deviceId) .entry(new BasicTsKvEntry(ts, kvEntry)) @@ -895,7 +889,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService(deviceId, kvEntry)) .build()); } else { - tsSubService.saveAttributes(AttributesSaveRequest.builder() + future = tsSubService.saveAttributesInternal(AttributesSaveRequest.builder() .tenantId(tenantId) .entityId(deviceId) .scope(AttributeScope.SERVER_SCOPE) @@ -903,20 +897,14 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService(deviceId, kvEntry)) .build()); } + return Futures.transform(future, __ -> null, MoreExecutors.directExecutor()); } long getCurrentTimeMillis() { return System.currentTimeMillis(); } - private static class TelemetrySaveCallback implements FutureCallback { - private final DeviceId deviceId; - private final KvEntry kvEntry; - - TelemetrySaveCallback(DeviceId deviceId, KvEntry kvEntry) { - this.deviceId = deviceId; - this.kvEntry = kvEntry; - } + private record TelemetrySaveCallback(DeviceId deviceId, KvEntry kvEntry) implements FutureCallback { @Override public void onSuccess(@Nullable T result) { @@ -924,9 +912,10 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService> saveAttributesInternal(AttributesSaveRequest request) { TenantId tenantId = request.getTenantId(); EntityId entityId = request.getEntityId(); AttributesSaveRequest.Strategy strategy = request.getStrategy(); @@ -228,6 +227,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer if (strategy.sendWsUpdate()) { addWsCallback(resultFuture, success -> onAttributesUpdate(tenantId, entityId, request.getScope().name(), request.getEntries())); } + return resultFuture; } private static boolean shouldSendSharedAttributesUpdatedNotification(AttributesSaveRequest request) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java index 8a76aa1d14..79f0beab41 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java @@ -23,6 +23,8 @@ import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; import org.thingsboard.server.common.data.kv.TimeseriesSaveResult; +import java.util.List; + /** * Created by ashvayka on 27.03.18. */ @@ -30,7 +32,7 @@ public interface InternalTelemetryService extends RuleEngineTelemetryService { ListenableFuture saveTimeseriesInternal(TimeseriesSaveRequest request); - void saveAttributesInternal(AttributesSaveRequest request); + ListenableFuture> saveAttributesInternal(AttributesSaveRequest request); void deleteTimeseriesInternal(TimeseriesDeleteRequest request); diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 6d27b9bd7e..09820a81cd 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -898,6 +898,8 @@ state: # Used only when state.persistToTelemetry is set to 'true' and Cassandra is used for timeseries data. # 0 means time-to-live mechanism is disabled. telemetryTtl: "${STATE_TELEMETRY_TTL:0}" + # Number of device records to fetch per batch when initializing device activity states + initFetchPackSize: "${TB_DEVICE_STATE_INIT_FETCH_PACK_SIZE:50000}" # Configuration properties for rule nodes related to device activity state rule: node: diff --git a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java index 26e913eacc..cbf7363441 100644 --- a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java @@ -16,6 +16,9 @@ package org.thingsboard.server.service.state; import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,13 +34,12 @@ import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.DeviceIdInfo; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityTrigger; -import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; @@ -50,20 +52,22 @@ import org.thingsboard.server.dao.sql.query.EntityQueryRepository; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.PartitionService; -import org.thingsboard.server.queue.discovery.QueueKey; -import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.usagestats.DefaultTbApiUsageReportClient; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; +import java.time.Duration; import java.util.Collections; +import java.util.HashSet; import java.util.List; -import java.util.Map; +import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -77,8 +81,8 @@ import static org.mockito.BDDMockito.then; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -90,7 +94,10 @@ import static org.thingsboard.server.service.state.DefaultDeviceStateService.LAS import static org.thingsboard.server.service.state.DefaultDeviceStateService.LAST_DISCONNECT_TIME; @ExtendWith(MockitoExtension.class) -public class DefaultDeviceStateServiceTest { +class DefaultDeviceStateServiceTest { + + ListeningExecutorService deviceStateExecutor; + ListeningExecutorService deviceStateCallbackExecutor; @Mock DeviceService deviceService; @@ -113,25 +120,48 @@ public class DefaultDeviceStateServiceTest { @Mock DefaultTbApiUsageReportClient defaultTbApiUsageReportClient; - TenantId tenantId = new TenantId(UUID.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112")); - DeviceId deviceId = DeviceId.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112"); - TopicPartitionInfo tpi; + long defaultInactivityTimeoutMs = Duration.ofMinutes(10L).toMillis(); + + TenantId tenantId = TenantId.fromUUID(UUID.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112")); + DeviceId deviceId = DeviceId.fromString("c209f718-42e5-11f0-9fe2-0242ac120002"); + TopicPartitionInfo tpi = TopicPartitionInfo.builder() + .topic("tb_core") + .partition(0) + .myPartition(true) + .build(); DefaultDeviceStateService service; @BeforeEach - public void setUp() { + void setUp() { service = spy(new DefaultDeviceStateService(deviceService, attributesService, tsService, clusterService, partitionService, entityQueryRepository, null, defaultTbApiUsageReportClient, notificationRuleProcessor)); ReflectionTestUtils.setField(service, "tsSubService", telemetrySubscriptionService); + ReflectionTestUtils.setField(service, "defaultInactivityTimeoutMs", defaultInactivityTimeoutMs); ReflectionTestUtils.setField(service, "defaultStateCheckIntervalInSec", 60); ReflectionTestUtils.setField(service, "defaultActivityStatsIntervalInSec", 60); - ReflectionTestUtils.setField(service, "initFetchPackSize", 10); + ReflectionTestUtils.setField(service, "initFetchPackSize", 50000); + + deviceStateExecutor = MoreExecutors.newDirectExecutorService(); + ReflectionTestUtils.setField(service, "deviceStateExecutor", deviceStateExecutor); + + deviceStateCallbackExecutor = MoreExecutors.newDirectExecutorService(); + ReflectionTestUtils.setField(service, "deviceStateCallbackExecutor", deviceStateCallbackExecutor); + + lenient().when(partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId)).thenReturn(tpi); + + ConcurrentMap> partitionedEntities = new ConcurrentHashMap<>(); + partitionedEntities.put(tpi, new HashSet<>()); + ReflectionTestUtils.setField(service, "partitionedEntities", partitionedEntities); + } - tpi = TopicPartitionInfo.builder().myPartition(true).build(); + @AfterEach + void cleanup() { + deviceStateExecutor.shutdownNow(); + deviceStateCallbackExecutor.shutdownNow(); } @Test - public void givenDeviceBelongsToExternalPartition_whenOnDeviceConnect_thenCleansStateAndDoesNotReportConnect() { + void givenDeviceBelongsToExternalPartition_whenOnDeviceConnect_thenCleansStateAndDoesNotReportConnect() { // GIVEN doReturn(true).when(service).cleanDeviceStateIfBelongsToExternalPartition(tenantId, deviceId); @@ -149,7 +179,7 @@ public class DefaultDeviceStateServiceTest { @ParameterizedTest @ValueSource(longs = {Long.MIN_VALUE, -100, -1}) - public void givenNegativeLastConnectTime_whenOnDeviceConnect_thenSkipsThisEvent(long negativeLastConnectTime) { + void givenNegativeLastConnectTime_whenOnDeviceConnect_thenSkipsThisEvent(long negativeLastConnectTime) { // GIVEN doReturn(false).when(service).cleanDeviceStateIfBelongsToExternalPartition(tenantId, deviceId); @@ -166,7 +196,7 @@ public class DefaultDeviceStateServiceTest { @ParameterizedTest @MethodSource("provideOutdatedTimestamps") - public void givenOutdatedLastConnectTime_whenOnDeviceDisconnect_thenSkipsThisEvent(long outdatedLastConnectTime, long currentLastConnectTime) { + void givenOutdatedLastConnectTime_whenOnDeviceDisconnect_thenSkipsThisEvent(long outdatedLastConnectTime, long currentLastConnectTime) { // GIVEN doReturn(false).when(service).cleanDeviceStateIfBelongsToExternalPartition(tenantId, deviceId); @@ -188,7 +218,7 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceBelongsToMyPartition_whenOnDeviceConnect_thenReportsConnect() { + void givenDeviceBelongsToMyPartition_whenOnDeviceConnect_thenReportsConnect() { // GIVEN var deviceStateData = DeviceStateData.builder() .tenantId(tenantId) @@ -202,11 +232,13 @@ public class DefaultDeviceStateServiceTest { service.deviceStates.put(deviceId, deviceStateData); long lastConnectTime = System.currentTimeMillis(); + mockSuccessfulSaveAttributes(); + // WHEN service.onDeviceConnect(tenantId, deviceId, lastConnectTime); // THEN - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getTenantId().equals(tenantId) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(LAST_CONNECT_TIME) && @@ -221,7 +253,7 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceBelongsToExternalPartition_whenOnDeviceDisconnect_thenCleansStateAndDoesNotReportDisconnect() { + void givenDeviceBelongsToExternalPartition_whenOnDeviceDisconnect_thenCleansStateAndDoesNotReportDisconnect() { // GIVEN doReturn(true).when(service).cleanDeviceStateIfBelongsToExternalPartition(tenantId, deviceId); @@ -238,7 +270,7 @@ public class DefaultDeviceStateServiceTest { @ParameterizedTest @ValueSource(longs = {Long.MIN_VALUE, -100, -1}) - public void givenNegativeLastDisconnectTime_whenOnDeviceDisconnect_thenSkipsThisEvent(long negativeLastDisconnectTime) { + void givenNegativeLastDisconnectTime_whenOnDeviceDisconnect_thenSkipsThisEvent(long negativeLastDisconnectTime) { // GIVEN doReturn(false).when(service).cleanDeviceStateIfBelongsToExternalPartition(tenantId, deviceId); @@ -254,7 +286,7 @@ public class DefaultDeviceStateServiceTest { @ParameterizedTest @MethodSource("provideOutdatedTimestamps") - public void givenOutdatedLastDisconnectTime_whenOnDeviceDisconnect_thenSkipsThisEvent(long outdatedLastDisconnectTime, long currentLastDisconnectTime) { + void givenOutdatedLastDisconnectTime_whenOnDeviceDisconnect_thenSkipsThisEvent(long outdatedLastDisconnectTime, long currentLastDisconnectTime) { // GIVEN doReturn(false).when(service).cleanDeviceStateIfBelongsToExternalPartition(tenantId, deviceId); @@ -275,7 +307,7 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceBelongsToMyPartition_whenOnDeviceDisconnect_thenReportsDisconnect() { + void givenDeviceBelongsToMyPartition_whenOnDeviceDisconnect_thenReportsDisconnect() { // GIVEN var deviceStateData = DeviceStateData.builder() .tenantId(tenantId) @@ -289,11 +321,13 @@ public class DefaultDeviceStateServiceTest { service.deviceStates.put(deviceId, deviceStateData); long lastDisconnectTime = System.currentTimeMillis(); + mockSuccessfulSaveAttributes(); + // WHEN service.onDeviceDisconnect(tenantId, deviceId, lastDisconnectTime); // THEN - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getTenantId().equals(tenantId) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(LAST_DISCONNECT_TIME) && @@ -308,7 +342,7 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceBelongsToExternalPartition_whenOnDeviceInactivity_thenCleansStateAndDoesNotReportInactivity() { + void givenDeviceBelongsToExternalPartition_whenOnDeviceInactivity_thenCleansStateAndDoesNotReportInactivity() { // GIVEN doReturn(true).when(service).cleanDeviceStateIfBelongsToExternalPartition(tenantId, deviceId); @@ -325,7 +359,7 @@ public class DefaultDeviceStateServiceTest { @ParameterizedTest @ValueSource(longs = {Long.MIN_VALUE, -100, -1}) - public void givenNegativeLastInactivityTime_whenOnDeviceInactivity_thenSkipsThisEvent(long negativeLastInactivityTime) { + void givenNegativeLastInactivityTime_whenOnDeviceInactivity_thenSkipsThisEvent(long negativeLastInactivityTime) { // GIVEN doReturn(false).when(service).cleanDeviceStateIfBelongsToExternalPartition(tenantId, deviceId); @@ -341,7 +375,7 @@ public class DefaultDeviceStateServiceTest { @ParameterizedTest @MethodSource("provideOutdatedTimestamps") - public void givenReceivedInactivityTimeIsLessThanOrEqualToCurrentInactivityTime_whenOnDeviceInactivity_thenSkipsThisEvent( + void givenReceivedInactivityTimeIsLessThanOrEqualToCurrentInactivityTime_whenOnDeviceInactivity_thenSkipsThisEvent( long outdatedLastInactivityTime, long currentLastInactivityTime ) { // GIVEN @@ -365,7 +399,7 @@ public class DefaultDeviceStateServiceTest { @ParameterizedTest @MethodSource("provideOutdatedTimestamps") - public void givenReceivedInactivityTimeIsLessThanOrEqualToCurrentActivityTime_whenOnDeviceInactivity_thenSkipsThisEvent( + void givenReceivedInactivityTimeIsLessThanOrEqualToCurrentActivityTime_whenOnDeviceInactivity_thenSkipsThisEvent( long outdatedLastInactivityTime, long currentLastActivityTime ) { // GIVEN @@ -398,7 +432,7 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceBelongsToMyPartition_whenOnDeviceInactivity_thenReportsInactivity() { + void givenDeviceBelongsToMyPartition_whenOnDeviceInactivity_thenReportsInactivity() { // GIVEN var deviceStateData = DeviceStateData.builder() .tenantId(tenantId) @@ -412,17 +446,19 @@ public class DefaultDeviceStateServiceTest { service.deviceStates.put(deviceId, deviceStateData); long lastInactivityTime = System.currentTimeMillis(); + mockSuccessfulSaveAttributes(); + // WHEN service.onDeviceInactivity(tenantId, deviceId, lastInactivityTime); // THEN - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getTenantId().equals(tenantId) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && request.getEntries().get(0).getValue().equals(lastInactivityTime) )); - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getTenantId().equals(tenantId) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && @@ -445,7 +481,7 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenInactivityTimeoutReached_whenUpdateInactivityStateIfExpired_thenReportsInactivity() { + void givenInactivityTimeoutReached_whenUpdateInactivityStateIfExpired_thenReportsInactivity() { // GIVEN var deviceStateData = DeviceStateData.builder() .tenantId(tenantId) @@ -456,16 +492,18 @@ public class DefaultDeviceStateServiceTest { given(partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId)).willReturn(tpi); + mockSuccessfulSaveAttributes(); + // WHEN service.updateInactivityStateIfExpired(System.currentTimeMillis(), deviceId, deviceStateData); // THEN - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getTenantId().equals(tenantId) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) )); - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getTenantId().equals(tenantId) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && @@ -488,7 +526,7 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceIdFromDeviceStatesMap_whenGetOrFetchDeviceStateData_thenNoStackOverflow() { + void givenDeviceIdFromDeviceStatesMap_whenGetOrFetchDeviceStateData_thenNoStackOverflow() { service.deviceStates.put(deviceId, deviceStateDataMock); DeviceStateData deviceStateData = service.getOrFetchDeviceStateData(deviceId); assertThat(deviceStateData).isEqualTo(deviceStateDataMock); @@ -496,7 +534,7 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceIdWithoutDeviceStateInMap_whenGetOrFetchDeviceStateData_thenFetchDeviceStateData() { + void givenDeviceIdWithoutDeviceStateInMap_whenGetOrFetchDeviceStateData_thenFetchDeviceStateData() { service.deviceStates.clear(); willReturn(deviceStateDataMock).given(service).fetchDeviceStateDataUsingSeparateRequests(deviceId); DeviceStateData deviceStateData = service.getOrFetchDeviceStateData(deviceId); @@ -504,172 +542,18 @@ public class DefaultDeviceStateServiceTest { verify(service).fetchDeviceStateDataUsingSeparateRequests(deviceId); } - private void initStateService(long timeout) throws InterruptedException { - service.stop(); - reset(service, telemetrySubscriptionService); - service.setDefaultInactivityTimeoutMs(timeout); - service.init(); - when(partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId)).thenReturn(tpi); - when(entityQueryRepository.findEntityDataByQueryInternal(any())).thenReturn(new PageData<>()); - var deviceIdInfo = new DeviceIdInfo(tenantId.getId(), null, deviceId.getId()); - when(deviceService.findDeviceIdInfos(any())) - .thenReturn(new PageData<>(List.of(deviceIdInfo), 0, 1, false)); - PartitionChangeEvent event = new PartitionChangeEvent(this, ServiceType.TB_CORE, Map.of( - new QueueKey(ServiceType.TB_CORE), Collections.singleton(tpi) - ), Collections.emptyMap()); - service.onApplicationEvent(event); - Thread.sleep(100); - } - - @Test - public void increaseInactivityForInactiveDeviceTest() throws Exception { - final long defaultTimeout = 1; - initStateService(defaultTimeout); - DeviceState deviceState = DeviceState.builder().build(); - DeviceStateData deviceStateData = DeviceStateData.builder() - .tenantId(tenantId) - .deviceId(deviceId) - .state(deviceState) - .metaData(new TbMsgMetaData()) - .build(); - - service.deviceStates.put(deviceId, deviceStateData); - service.getPartitionedEntities(tpi).add(deviceId); - - service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); - activityVerify(true); - Thread.sleep(defaultTimeout); - service.checkStates(); - activityVerify(false); - - reset(telemetrySubscriptionService); - - long increase = 100; - long newTimeout = System.currentTimeMillis() - deviceState.getLastActivityTime() + increase; - - service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - activityVerify(true); - Thread.sleep(increase); - service.checkStates(); - activityVerify(false); - - reset(telemetrySubscriptionService); - - service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); - activityVerify(true); - Thread.sleep(newTimeout + 5); - service.checkStates(); - activityVerify(false); - } - - @Test - public void increaseInactivityForActiveDeviceTest() throws Exception { - final long defaultTimeout = 1000; - initStateService(defaultTimeout); - DeviceState deviceState = DeviceState.builder().build(); - DeviceStateData deviceStateData = DeviceStateData.builder() - .tenantId(tenantId) - .deviceId(deviceId) - .state(deviceState) - .metaData(new TbMsgMetaData()) - .build(); - - service.deviceStates.put(deviceId, deviceStateData); - service.getPartitionedEntities(tpi).add(deviceId); - - service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); - activityVerify(true); - - reset(telemetrySubscriptionService); - - long increase = 100; - long newTimeout = System.currentTimeMillis() - deviceState.getLastActivityTime() + increase; - - service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request -> - request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) - )); - Thread.sleep(defaultTimeout + increase); - service.checkStates(); - activityVerify(false); - - reset(telemetrySubscriptionService); - - service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); - activityVerify(true); - Thread.sleep(newTimeout); - service.checkStates(); - activityVerify(false); - } + @MethodSource + @ParameterizedTest + void testOnDeviceInactivityTimeoutUpdate(boolean initialActivityStatus, long newInactivityTimeout, boolean expectedActivityStatus) { + // GIVEN + doReturn(200L).when(service).getCurrentTimeMillis(); - @Test - public void increaseSmallInactivityForInactiveDeviceTest() throws Exception { - final long defaultTimeout = 1; - initStateService(defaultTimeout); - DeviceState deviceState = DeviceState.builder().build(); - DeviceStateData deviceStateData = DeviceStateData.builder() - .tenantId(tenantId) - .deviceId(deviceId) - .state(deviceState) - .metaData(new TbMsgMetaData()) + var deviceState = DeviceState.builder() + .active(initialActivityStatus) + .lastActivityTime(100L) .build(); - service.deviceStates.put(deviceId, deviceStateData); - service.getPartitionedEntities(tpi).add(deviceId); - - service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); - activityVerify(true); - Thread.sleep(defaultTimeout); - service.checkStates(); - activityVerify(false); - - reset(telemetrySubscriptionService); - - long newTimeout = 1; - Thread.sleep(newTimeout); - verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request -> - request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) - )); - } - - @Test - public void decreaseInactivityForActiveDeviceTest() throws Exception { - final long defaultTimeout = 1000; - initStateService(defaultTimeout); - DeviceState deviceState = DeviceState.builder().build(); - DeviceStateData deviceStateData = DeviceStateData.builder() - .tenantId(tenantId) - .deviceId(deviceId) - .state(deviceState) - .metaData(new TbMsgMetaData()) - .build(); - - service.deviceStates.put(deviceId, deviceStateData); - service.getPartitionedEntities(tpi).add(deviceId); - - service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); - activityVerify(true); - - long newTimeout = 1; - Thread.sleep(newTimeout); - - service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - activityVerify(false); - reset(telemetrySubscriptionService); - - service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, defaultTimeout); - activityVerify(true); - Thread.sleep(defaultTimeout); - service.checkStates(); - activityVerify(false); - } - - @Test - public void decreaseInactivityForInactiveDeviceTest() throws Exception { - final long defaultTimeout = 1000; - initStateService(defaultTimeout); - DeviceState deviceState = DeviceState.builder().build(); - DeviceStateData deviceStateData = DeviceStateData.builder() + var deviceStateData = DeviceStateData.builder() .tenantId(tenantId) .deviceId(deviceId) .state(deviceState) @@ -679,31 +563,44 @@ public class DefaultDeviceStateServiceTest { service.deviceStates.put(deviceId, deviceStateData); service.getPartitionedEntities(tpi).add(deviceId); - service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); - activityVerify(true); - Thread.sleep(defaultTimeout); - service.checkStates(); - activityVerify(false); - reset(telemetrySubscriptionService); + mockSuccessfulSaveAttributes(); - long newTimeout = 1; + // WHEN + service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newInactivityTimeout); - service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); - verify(telemetrySubscriptionService, never()).saveAttributes(argThat(request -> - request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) - )); + // THEN + long expectedInactivityTimeout = newInactivityTimeout != 0 ? newInactivityTimeout : defaultInactivityTimeoutMs; + assertThat(deviceState.getInactivityTimeout()).isEqualTo(expectedInactivityTimeout); + + assertThat(deviceState.isActive()).isEqualTo(expectedActivityStatus); + if (initialActivityStatus != expectedActivityStatus) { + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> { + AttributeKvEntry entry = request.getEntries().get(0); + return request.getEntityId().equals(deviceId) && entry.getKey().equals(ACTIVITY_STATE) && entry.getValue().equals(expectedActivityStatus); + })); + } } - private void activityVerify(boolean isActive) { - verify(telemetrySubscriptionService).saveAttributes(argThat(request -> - request.getEntityId().equals(deviceId) && - request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && - request.getEntries().get(0).getValue().equals(isActive) - )); + // to simplify test, these arguments assume that the current time is 200 and the last activity time is 100 + private static Stream testOnDeviceInactivityTimeoutUpdate() { + return Stream.of( + Arguments.of(true, 1L, false), + Arguments.of(true, 50L, false), + Arguments.of(true, 99L, false), + Arguments.of(true, 100L, false), + Arguments.of(true, 101L, true), + Arguments.of(true, 0L, true), // should use default inactivity timeout of 10 minutes + Arguments.of(false, 1L, false), + Arguments.of(false, 50L, false), + Arguments.of(false, 99L, false), + Arguments.of(false, 100L, false), + Arguments.of(false, 101L, true), + Arguments.of(false, 0L, true) // should use default inactivity timeout of 10 minutes + ); } @Test - public void givenStateDataIsNull_whenUpdateActivityState_thenShouldCleanupDevice() { + void givenStateDataIsNull_whenUpdateActivityState_thenShouldCleanupDevice() { // GIVEN service.deviceStates.put(deviceId, deviceStateDataMock); @@ -719,7 +616,7 @@ public class DefaultDeviceStateServiceTest { @ParameterizedTest @MethodSource("provideParametersForUpdateActivityState") - public void givenTestParameters_whenUpdateActivityState_thenShouldBeInTheExpectedStateAndPerformExpectedActions( + void givenTestParameters_whenUpdateActivityState_thenShouldBeInTheExpectedStateAndPerformExpectedActions( boolean activityState, long previousActivityTime, long lastReportedActivity, long inactivityAlarmTime, long expectedInactivityAlarmTime, boolean shouldSetInactivityAlarmTimeToZero, boolean shouldUpdateActivityStateToActive @@ -739,13 +636,15 @@ public class DefaultDeviceStateServiceTest { .metaData(new TbMsgMetaData()) .build(); + mockSuccessfulSaveAttributes(); + // WHEN service.updateActivityState(deviceId, deviceStateData, lastReportedActivity); // THEN assertThat(deviceState.isActive()).isEqualTo(true); assertThat(deviceState.getLastActivityTime()).isEqualTo(lastReportedActivity); - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(LAST_ACTIVITY_TIME) && request.getEntries().get(0).getValue().equals(lastReportedActivity) @@ -753,7 +652,7 @@ public class DefaultDeviceStateServiceTest { assertThat(deviceState.getLastInactivityAlarmTime()).isEqualTo(expectedInactivityAlarmTime); if (shouldSetInactivityAlarmTimeToZero) { - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && request.getEntries().get(0).getValue().equals(0L) @@ -761,7 +660,7 @@ public class DefaultDeviceStateServiceTest { } if (shouldUpdateActivityStateToActive) { - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(true) @@ -809,59 +708,8 @@ public class DefaultDeviceStateServiceTest { ); } - @ParameterizedTest - @MethodSource("provideParametersForDecreaseInactivityTimeout") - public void givenTestParameters_whenOnDeviceInactivityTimeout_thenShouldBeInTheExpectedStateAndPerformExpectedActions( - boolean activityState, long newInactivityTimeout, long timeIncrement, boolean expectedActivityState - ) throws Exception { - // GIVEN - long defaultInactivityTimeout = 10000; - initStateService(defaultInactivityTimeout); - - var currentTime = new AtomicLong(System.currentTimeMillis()); - - DeviceState deviceState = DeviceState.builder() - .active(activityState) - .lastActivityTime(currentTime.get()) - .inactivityTimeout(defaultInactivityTimeout) - .build(); - - DeviceStateData deviceStateData = DeviceStateData.builder() - .tenantId(tenantId) - .deviceId(deviceId) - .state(deviceState) - .metaData(new TbMsgMetaData()) - .build(); - - service.deviceStates.put(deviceId, deviceStateData); - service.getPartitionedEntities(tpi).add(deviceId); - - given(service.getCurrentTimeMillis()).willReturn(currentTime.addAndGet(timeIncrement)); - - // WHEN - service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newInactivityTimeout); - - // THEN - assertThat(deviceState.getInactivityTimeout()).isEqualTo(newInactivityTimeout); - assertThat(deviceState.isActive()).isEqualTo(expectedActivityState); - if (activityState && !expectedActivityState) { - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> - request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && - request.getEntries().get(0).getValue().equals(false) - )); - } - } - - private static Stream provideParametersForDecreaseInactivityTimeout() { - return Stream.of( - Arguments.of(true, 1, 0, true), - - Arguments.of(true, 1, 1, false) - ); - } - @Test - public void givenStateDataIsNull_whenUpdateInactivityTimeoutIfExpired_thenShouldCleanupDevice() { + void givenStateDataIsNull_whenUpdateInactivityTimeoutIfExpired_thenShouldCleanupDevice() { // GIVEN service.deviceStates.put(deviceId, deviceStateDataMock); @@ -875,7 +723,7 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenNotMyPartition_whenUpdateInactivityTimeoutIfExpired_thenShouldCleanupDevice() { + void givenNotMyPartition_whenUpdateInactivityTimeoutIfExpired_thenShouldCleanupDevice() { // GIVEN long currentTime = System.currentTimeMillis(); @@ -911,7 +759,7 @@ public class DefaultDeviceStateServiceTest { @ParameterizedTest @MethodSource("provideParametersForUpdateInactivityStateIfExpired") - public void givenTestParameters_whenUpdateInactivityStateIfExpired_thenShouldBeInTheExpectedStateAndPerformExpectedActions( + void givenTestParameters_whenUpdateInactivityStateIfExpired_thenShouldBeInTheExpectedStateAndPerformExpectedActions( boolean activityState, long ts, long lastActivityTime, long lastInactivityAlarmTime, long inactivityTimeout, long deviceCreationTime, boolean expectedActivityState, long expectedLastInactivityAlarmTime, boolean shouldUpdateActivityStateToInactive ) { @@ -933,6 +781,7 @@ public class DefaultDeviceStateServiceTest { if (shouldUpdateActivityStateToInactive) { given(partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId)).willReturn(tpi); + mockSuccessfulSaveAttributes(); } // WHEN @@ -943,7 +792,7 @@ public class DefaultDeviceStateServiceTest { assertThat(state.getLastInactivityAlarmTime()).isEqualTo(expectedLastInactivityAlarmTime); if (shouldUpdateActivityStateToInactive) { - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(false) )); @@ -961,7 +810,7 @@ public class DefaultDeviceStateServiceTest { assertThat(actualNotification.getDeviceId()).isEqualTo(deviceId); assertThat(actualNotification.isActive()).isFalse(); - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getTenantId().equals(tenantId) && request.getEntityId().equals(deviceId) && request.getScope().equals(AttributeScope.SERVER_SCOPE) && request.getEntries().get(0).getKey().equals(INACTIVITY_ALARM_TIME) && @@ -1033,7 +882,80 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenConcurrentAccess_whenGetOrFetchDeviceStateData_thenFetchDeviceStateDataInvokedOnce() { + void givenInactiveDevice_whenActivityStatusChangesToActiveButFailedToSaveUpdatedActivityStatus_thenShouldNotUpdateCache() { + // GIVEN + doReturn(200L).when(service).getCurrentTimeMillis(); + + var deviceState = DeviceState.builder() + .active(false) + .lastActivityTime(100L) + .inactivityTimeout(50L) + .build(); + + var deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .state(deviceState) + .metaData(TbMsgMetaData.EMPTY) + .build(); + + service.deviceStates.put(deviceId, deviceStateData); + service.getPartitionedEntities(tpi).add(deviceId); + + when(telemetrySubscriptionService.saveAttributesInternal(any(AttributesSaveRequest.class))) + .thenAnswer(invocation -> { + AttributesSaveRequest request = invocation.getArgument(0); + AttributeKvEntry entry = request.getEntries().get(0); + return entry.getKey().equals(ACTIVITY_STATE) ? + Futures.immediateFailedFuture(new RuntimeException("failed to save")) : + Futures.immediateFuture(generateRandomVersions(1)); + }); + + // WHEN + service.onDeviceActivity(tenantId, deviceId, 220L); + + // THEN + assertThat(deviceState.isActive()).isFalse(); + } + + @Test + void givenActiveDevice_whenActivityStatusChangesToInactiveButFailedToSaveUpdatedActivityStatus_thenShouldNotUpdateCache() { + // GIVEN + var deviceState = DeviceState.builder() + .active(true) + .lastActivityTime(100L) + .inactivityTimeout(50L) + .build(); + + var deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .state(deviceState) + .metaData(TbMsgMetaData.EMPTY) + .build(); + + service.deviceStates.put(deviceId, deviceStateData); + service.getPartitionedEntities(tpi).add(deviceId); + + when(telemetrySubscriptionService.saveAttributesInternal(any(AttributesSaveRequest.class))) + .thenAnswer(invocation -> { + AttributesSaveRequest request = invocation.getArgument(0); + AttributeKvEntry entry = request.getEntries().get(0); + return entry.getKey().equals(ACTIVITY_STATE) ? + Futures.immediateFailedFuture(new RuntimeException("failed to save")) : + Futures.immediateFuture(generateRandomVersions(1)); + }); + + // WHEN + doReturn(200L).when(service).getCurrentTimeMillis(); + service.checkStates(); + + // THEN + assertThat(deviceState.isActive()).isTrue(); + } + + @Test + void givenConcurrentAccess_whenGetOrFetchDeviceStateData_thenFetchDeviceStateDataInvokedOnce() { doAnswer(invocation -> { Thread.sleep(100); return deviceStateDataMock; @@ -1069,10 +991,8 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceAdded_whenOnQueueMsg_thenShouldCacheAndSaveActivityToFalse() throws InterruptedException { + void givenDeviceAdded_whenOnQueueMsg_thenShouldCacheAndSaveActivityToFalse() { // GIVEN - final long defaultTimeout = 1000; - initStateService(defaultTimeout); given(deviceService.findDeviceById(any(TenantId.class), any(DeviceId.class))).willReturn(new Device(deviceId)); given(attributesService.find(any(TenantId.class), any(EntityId.class), any(AttributeScope.class), anyCollection())).willReturn(Futures.immediateFuture(Collections.emptyList())); @@ -1086,13 +1006,15 @@ public class DefaultDeviceStateServiceTest { .setDeleted(false) .build(); + mockSuccessfulSaveAttributes(); + // WHEN service.onQueueMsg(proto, TbCallback.EMPTY); // THEN await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(false); - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(false) )); @@ -1100,14 +1022,12 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceActivityEventHappenedAfterAdded_whenOnDeviceActivity_thenShouldCacheAndSaveActivityToTrue() throws InterruptedException { + void givenDeviceActivityEventHappenedAfterAdded_whenOnDeviceActivity_thenShouldCacheAndSaveActivityToTrue() { // GIVEN - final long defaultTimeout = 1000; - initStateService(defaultTimeout); long currentTime = System.currentTimeMillis(); DeviceState deviceState = DeviceState.builder() .active(false) - .inactivityTimeout(service.getDefaultInactivityTimeoutInSec()) + .inactivityTimeout(defaultInactivityTimeoutMs) .build(); DeviceStateData stateData = DeviceStateData.builder() .tenantId(tenantId) @@ -1118,12 +1038,14 @@ public class DefaultDeviceStateServiceTest { .build(); service.deviceStates.put(deviceId, stateData); + mockSuccessfulSaveAttributes(); + // WHEN service.onDeviceActivity(tenantId, deviceId, currentTime); // THEN ArgumentCaptor attributeRequestCaptor = ArgumentCaptor.forClass(AttributesSaveRequest.class); - then(telemetrySubscriptionService).should(times(2)).saveAttributes(attributeRequestCaptor.capture()); + then(telemetrySubscriptionService).should(times(2)).saveAttributesInternal(attributeRequestCaptor.capture()); await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); @@ -1151,15 +1073,14 @@ public class DefaultDeviceStateServiceTest { } @Test - public void givenDeviceActivityEventHappenedBeforeAdded_whenOnQueueMsg_thenShouldSaveActivityStateUsingValueFromCache() throws InterruptedException { + void givenDeviceActivityEventHappenedBeforeAdded_whenOnQueueMsg_thenShouldSaveActivityStateUsingValueFromCache() { // GIVEN - final long defaultTimeout = 1000; - initStateService(defaultTimeout); given(deviceService.findDeviceById(any(TenantId.class), any(DeviceId.class))).willReturn(new Device(deviceId)); given(attributesService.find(any(TenantId.class), any(EntityId.class), any(AttributeScope.class), anyCollection())).willReturn(Futures.immediateFuture(Collections.emptyList())); long currentTime = System.currentTimeMillis(); - DeviceState deviceState = DeviceState.builder() + + var deviceState = DeviceState.builder() .active(true) .lastConnectTime(currentTime - 8000) .lastActivityTime(currentTime - 4000) @@ -1167,16 +1088,20 @@ public class DefaultDeviceStateServiceTest { .lastInactivityAlarmTime(0) .inactivityTimeout(3000) .build(); - DeviceStateData stateData = DeviceStateData.builder() + + var stateData = DeviceStateData.builder() .tenantId(tenantId) .deviceId(deviceId) .deviceCreationTime(currentTime - 10000) .state(deviceState) .build(); + service.deviceStates.put(deviceId, stateData); + mockSuccessfulSaveAttributes(); + // WHEN - TransportProtos.DeviceStateServiceMsgProto proto = TransportProtos.DeviceStateServiceMsgProto.newBuilder() + var proto = TransportProtos.DeviceStateServiceMsgProto.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) @@ -1190,11 +1115,25 @@ public class DefaultDeviceStateServiceTest { // THEN await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(service.deviceStates.get(deviceId).getState().isActive()).isEqualTo(true); - then(telemetrySubscriptionService).should().saveAttributes(argThat(request -> + then(telemetrySubscriptionService).should().saveAttributesInternal(argThat(request -> request.getEntityId().equals(deviceId) && request.getEntries().get(0).getKey().equals(ACTIVITY_STATE) && request.getEntries().get(0).getValue().equals(true) )); }); } + private void mockSuccessfulSaveAttributes() { + lenient().when(telemetrySubscriptionService.saveAttributesInternal(any())).thenAnswer(invocation -> { + AttributesSaveRequest request = invocation.getArgument(0); + return Futures.immediateFuture(generateRandomVersions(request.getEntries().size())); + }); + } + + private static List generateRandomVersions(int n) { + return ThreadLocalRandom.current() + .longs(n) + .boxed() + .toList(); + } + } From d917c72d5198f4223fcf52b1e0e0a0f8904d74e6 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Fri, 6 Jun 2025 18:58:14 +0300 Subject: [PATCH 304/335] Use a callback to log errors on failure for improved error reporting --- .../state/DefaultDeviceStateService.java | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index f46323f702..13745ae6ab 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -604,19 +604,27 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService { - stateData.getState().setActive(active); - pushRuleEngineMessage(stateData, active ? TbMsgType.ACTIVITY_EVENT : TbMsgType.INACTIVITY_EVENT); - TbMsgMetaData metaData = stateData.getMetaData(); - notificationRuleProcessor.process(DeviceActivityTrigger.builder() - .tenantId(tenantId) - .customerId(stateData.getCustomerId()) - .deviceId(deviceId) - .active(active) - .deviceName(metaData.getValue("deviceName")) - .deviceType(metaData.getValue("deviceType")) - .deviceLabel(metaData.getValue("deviceLabel")) - .build()); + Futures.addCallback(save(tenantId, deviceId, ACTIVITY_STATE, active), new FutureCallback<>() { + @Override + public void onSuccess(Void success) { + stateData.getState().setActive(active); + pushRuleEngineMessage(stateData, active ? TbMsgType.ACTIVITY_EVENT : TbMsgType.INACTIVITY_EVENT); + TbMsgMetaData metaData = stateData.getMetaData(); + notificationRuleProcessor.process(DeviceActivityTrigger.builder() + .tenantId(tenantId) + .customerId(stateData.getCustomerId()) + .deviceId(deviceId) + .active(active) + .deviceName(metaData.getValue("deviceName")) + .deviceType(metaData.getValue("deviceType")) + .deviceLabel(metaData.getValue("deviceLabel")) + .build()); + } + + @Override + public void onFailure(@NonNull Throwable t) { + log.error("[{}][{}] Failed to change device activity status to '{}'. Device state data: {}", tenantId, deviceId, active, stateData, t); + } }, deviceStateCallbackExecutor); } From baaa9f723501e94b3c09c4e2d9792178b98fa2fc Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Fri, 6 Jun 2025 20:25:00 +0300 Subject: [PATCH 305/335] Update last inactivity alarm time after a successful database save; improve tests --- .../state/DefaultDeviceStateService.java | 25 ++++++--- .../state/DefaultDeviceStateServiceTest.java | 51 +++++++++---------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 13745ae6ab..23fdc6b8c0 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -340,7 +340,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService() { + @Override + public void onSuccess(Void success) { + stateData.getState().setLastInactivityAlarmTime(ts); + onDeviceActivityStatusChange(false, stateData); + } + + @Override + public void onFailure(@NonNull Throwable t) { + log.error("[{}][{}] Failed to update device last inactivity alarm time to '{}'. Device state data: {}", tenantId, deviceId, ts, stateData, t); + } + }, deviceStateCallbackExecutor); } private static boolean isActive(long ts, DeviceState state) { diff --git a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java index cbf7363441..0fe29eef57 100644 --- a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java @@ -882,10 +882,8 @@ class DefaultDeviceStateServiceTest { } @Test - void givenInactiveDevice_whenActivityStatusChangesToActiveButFailedToSaveUpdatedActivityStatus_thenShouldNotUpdateCache() { + void givenInactiveDevice_whenActivityStatusChangesToActiveButFailedToSaveUpdatedActivityStatus_thenShouldNotUpdateCache2() { // GIVEN - doReturn(200L).when(service).getCurrentTimeMillis(); - var deviceState = DeviceState.builder() .active(false) .lastActivityTime(100L) @@ -902,20 +900,21 @@ class DefaultDeviceStateServiceTest { service.deviceStates.put(deviceId, deviceStateData); service.getPartitionedEntities(tpi).add(deviceId); - when(telemetrySubscriptionService.saveAttributesInternal(any(AttributesSaveRequest.class))) - .thenAnswer(invocation -> { - AttributesSaveRequest request = invocation.getArgument(0); - AttributeKvEntry entry = request.getEntries().get(0); - return entry.getKey().equals(ACTIVITY_STATE) ? - Futures.immediateFailedFuture(new RuntimeException("failed to save")) : - Futures.immediateFuture(generateRandomVersions(1)); - }); + // WHEN-THEN - // WHEN - service.onDeviceActivity(tenantId, deviceId, 220L); + // simulating short DB outage + given(telemetrySubscriptionService.saveAttributesInternal(any())).willReturn(Futures.immediateFailedFuture(new RuntimeException("failed to save"))); + doReturn(200L).when(service).getCurrentTimeMillis(); + service.onDeviceActivity(tenantId, deviceId, 180L); + assertThat(deviceState.isActive()).isFalse(); // still inactive - // THEN - assertThat(deviceState.isActive()).isFalse(); + // 10 millis pass... and new activity message it received + + // this time DB save is successful + when(telemetrySubscriptionService.saveAttributesInternal(any())).thenReturn(Futures.immediateFuture(generateRandomVersions(1))); + doReturn(210L).when(service).getCurrentTimeMillis(); + service.onDeviceActivity(tenantId, deviceId, 190L); + assertThat(deviceState.isActive()).isTrue(); } @Test @@ -937,21 +936,21 @@ class DefaultDeviceStateServiceTest { service.deviceStates.put(deviceId, deviceStateData); service.getPartitionedEntities(tpi).add(deviceId); - when(telemetrySubscriptionService.saveAttributesInternal(any(AttributesSaveRequest.class))) - .thenAnswer(invocation -> { - AttributesSaveRequest request = invocation.getArgument(0); - AttributeKvEntry entry = request.getEntries().get(0); - return entry.getKey().equals(ACTIVITY_STATE) ? - Futures.immediateFailedFuture(new RuntimeException("failed to save")) : - Futures.immediateFuture(generateRandomVersions(1)); - }); + // WHEN-THEN (assuming periodic activity states check is done every 100 millis) - // WHEN + // simulating short DB outage + given(telemetrySubscriptionService.saveAttributesInternal(any())).willReturn(Futures.immediateFailedFuture(new RuntimeException("failed to save"))); doReturn(200L).when(service).getCurrentTimeMillis(); service.checkStates(); + assertThat(deviceState.isActive()).isTrue(); // still active - // THEN - assertThat(deviceState.isActive()).isTrue(); + // waiting 100 millis... periodic activity states check is triggered again + + // this time DB save is successful + when(telemetrySubscriptionService.saveAttributesInternal(any())).thenReturn(Futures.immediateFuture(generateRandomVersions(1))); + doReturn(300L).when(service).getCurrentTimeMillis(); + service.checkStates(); + assertThat(deviceState.isActive()).isFalse(); } @Test From b5e7ff6b57a87198cb796cb66e1fedecf99f99e8 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Jun 2025 10:35:13 +0300 Subject: [PATCH 306/335] UI: Rename time series chart tooltip hide zero parametr --- .../data/json/demo/dashboards/rule_engine_statistics.json | 4 ++-- .../chart/time-series-chart-basic-config.component.html | 4 ++-- .../chart/time-series-chart-basic-config.component.ts | 8 ++++---- .../widget/lib/chart/time-series-chart-tooltip.models.ts | 6 +++--- .../time-series-chart-widget-settings.component.html | 4 ++-- .../chart/time-series-chart-widget-settings.component.ts | 6 +++--- ui-ngx/src/assets/dashboard/api_usage.json | 4 ++-- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json index d167e4087c..272de5848f 100644 --- a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json +++ b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json @@ -564,7 +564,7 @@ }, "tooltipDateColor": "rgba(0, 0, 0, 0.76)", "tooltipDateInterval": true, - "tooltipHideZeroFalse": true, + "tooltipHideZeroValues": true, "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", "tooltipBackgroundBlur": 4, "animation": { @@ -978,7 +978,7 @@ }, "tooltipDateColor": "rgba(0, 0, 0, 0.76)", "tooltipDateInterval": true, - "tooltipHideZeroFalse": true, + "tooltipHideZeroValues": true, "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", "tooltipBackgroundBlur": 4, "animation": { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html index 8c29f2d034..5e88f8e794 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html @@ -314,8 +314,8 @@
- - {{ 'tooltip.hide-zero-false-tooltip-values' | translate }} + + {{ 'tooltip.hide-zero-tooltip-values' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts index c0dd95913c..bfaca01a06 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts @@ -188,7 +188,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon tooltipDateFont: [settings.tooltipDateFont, []], tooltipDateColor: [settings.tooltipDateColor, []], tooltipDateInterval: [settings.tooltipDateInterval, []], - tooltipHideZeroFalse: [settings.tooltipHideZeroFalse ,[]], + tooltipHideZeroValues: [settings.tooltipHideZeroValues ,[]], tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], @@ -265,7 +265,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon this.widgetConfig.config.settings.tooltipDateFont = config.tooltipDateFont; this.widgetConfig.config.settings.tooltipDateColor = config.tooltipDateColor; this.widgetConfig.config.settings.tooltipDateInterval = config.tooltipDateInterval; - this.widgetConfig.config.settings.tooltipHideZeroFalse = config.tooltipHideZeroFalse; + this.widgetConfig.config.settings.tooltipHideZeroValues = config.tooltipHideZeroValues; this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; @@ -359,7 +359,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon this.timeSeriesChartWidgetConfigForm.get('tooltipValueFont').enable(); this.timeSeriesChartWidgetConfigForm.get('tooltipValueColor').enable(); this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').enable({emitEvent: false}); - this.timeSeriesChartWidgetConfigForm.get('tooltipHideZeroFalse').enable({emitEvent: false}); + this.timeSeriesChartWidgetConfigForm.get('tooltipHideZeroValues').enable({emitEvent: false}); this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundColor').enable(); this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundBlur').enable(); if (tooltipShowDate) { @@ -384,7 +384,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon this.timeSeriesChartWidgetConfigForm.get('tooltipDateFont').disable(); this.timeSeriesChartWidgetConfigForm.get('tooltipDateColor').disable(); this.timeSeriesChartWidgetConfigForm.get('tooltipDateInterval').disable(); - this.timeSeriesChartWidgetConfigForm.get('tooltipHideZeroFalse').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipHideZeroValues').disable(); this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundColor').disable(); this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundBlur').disable(); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts index 22c16c0219..f8ead65e30 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts @@ -37,7 +37,7 @@ export interface TimeSeriesChartTooltipWidgetSettings { tooltipValueFormatter?: string | TimeSeriesChartTooltipValueFormatFunction; tooltipShowDate: boolean; tooltipDateInterval?: boolean; - tooltipHideZeroFalse?: boolean; + tooltipHideZeroValues?: boolean; tooltipDateFormat: DateFormatSettings; tooltipDateFont: Font; tooltipDateColor: string; @@ -102,7 +102,7 @@ export class TimeSeriesChartTooltip { if (!tooltipParams.items.length && !tooltipParams.comparisonItems.length) { return null; } - if (this.settings.tooltipHideZeroFalse && !tooltipParams.items.some(value => value.param.value[1] && value.param.value[1] !== 'false')) { + if (this.settings.tooltipHideZeroValues && !tooltipParams.items.some(value => value.param.value[1] && value.param.value[1] !== 'false')) { return undefined; } @@ -131,7 +131,7 @@ export class TimeSeriesChartTooltip { this.renderer.appendChild(tooltipItemsElement, this.constructTooltipDateElement(items[0].param, interval)); } for (const item of items) { - if (!this.settings.tooltipHideZeroFalse || (item.param.value[1] && item.param.value[1] !== 'false')) { + if (!this.settings.tooltipHideZeroValues || (item.param.value[1] && item.param.value[1] !== 'false')) { this.renderer.appendChild(tooltipItemsElement, this.constructTooltipSeriesElement(item)); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html index a433f3af02..f1554fef22 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html @@ -238,8 +238,8 @@
- - {{ 'tooltip.hide-zero-false-tooltip-values' | translate }} + + {{ 'tooltip.hide-zero-tooltip-values' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts index 45a65b875f..cad2287745 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts @@ -163,7 +163,7 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon tooltipDateFont: [settings.tooltipDateFont, []], tooltipDateColor: [settings.tooltipDateColor, []], tooltipDateInterval: [settings.tooltipDateInterval, []], - tooltipHideZeroFalse: [settings.tooltipHideZeroFalse ,[]], + tooltipHideZeroValues: [settings.tooltipHideZeroValues ,[]], tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], @@ -224,7 +224,7 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon this.timeSeriesChartWidgetSettingsForm.get('tooltipValueColor').enable(); this.timeSeriesChartWidgetSettingsForm.get('tooltipValueFormatter').enable(); this.timeSeriesChartWidgetSettingsForm.get('tooltipShowDate').enable({emitEvent: false}); - this.timeSeriesChartWidgetSettingsForm.get('tooltipHideZeroFalse').enable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipHideZeroValues').enable(); this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundColor').enable(); this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundBlur').enable(); if (tooltipShowDate) { @@ -250,7 +250,7 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon this.timeSeriesChartWidgetSettingsForm.get('tooltipDateFont').disable(); this.timeSeriesChartWidgetSettingsForm.get('tooltipDateColor').disable(); this.timeSeriesChartWidgetSettingsForm.get('tooltipDateInterval').disable(); - this.timeSeriesChartWidgetSettingsForm.get('tooltipHideZeroFalse').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipHideZeroValues').disable(); this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundColor').disable(); this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundBlur').disable(); } diff --git a/ui-ngx/src/assets/dashboard/api_usage.json b/ui-ngx/src/assets/dashboard/api_usage.json index 9f14aad306..9738e9ac0f 100644 --- a/ui-ngx/src/assets/dashboard/api_usage.json +++ b/ui-ngx/src/assets/dashboard/api_usage.json @@ -7885,7 +7885,7 @@ }, "tooltipDateColor": "rgba(0, 0, 0, 0.76)", "tooltipDateInterval": true, - "tooltipHideZeroFalse": true, + "tooltipHideZeroValues": true, "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", "tooltipBackgroundBlur": 4, "animation": { @@ -8294,7 +8294,7 @@ }, "tooltipDateColor": "rgba(0, 0, 0, 0.76)", "tooltipDateInterval": true, - "tooltipHideZeroFalse": true, + "tooltipHideZeroValues": true, "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", "tooltipBackgroundBlur": 4, "animation": { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5afc040daf..646e5cd669 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5885,7 +5885,7 @@ "date": "Date", "show-date-time-interval": "Show date time interval", "show-date-time-interval-hint": "Show date time interval according to the data aggregation.", - "hide-zero-false-tooltip-values": "Hide zero/false values", + "hide-zero-tooltip-values": "Hide zero values", "background-color": "Background color", "background-blur": "Background blur" }, From 66ae5d7ff1beffb2ee25ae00a9a429afec3ace14 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 9 Jun 2025 11:37:13 +0300 Subject: [PATCH 307/335] Handle ExecutionException in controllers --- .../org/thingsboard/server/controller/BaseController.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 2443037ca0..8daee6800b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -199,6 +199,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ExecutionException; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; @@ -420,7 +421,7 @@ public abstract class BaseController { return handleException(exception, true); } - private ThingsboardException handleException(Exception exception, boolean logException) { + private ThingsboardException handleException(Throwable exception, boolean logException) { if (logException && logControllerErrorStackTrace) { try { SecurityUser user = getCurrentUser(); @@ -431,6 +432,9 @@ public abstract class BaseController { } Throwable cause = exception.getCause(); + if (exception instanceof ExecutionException) { + exception = cause; + } if (exception instanceof ThingsboardException) { return (ThingsboardException) exception; } else if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException From 2e1e6922f638d9819f3bdd397954d26d627622e0 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Jun 2025 12:36:17 +0300 Subject: [PATCH 308/335] UI: unitService to ctx, ref id from settings to objectHashCode and remove showZeroDecimals --- .../3-phase-voltage-relay-hp.svg | 6 +-- .../scada_symbols/bottom-flow-meter.svg | 2 +- .../dynamic-horizontal-scale-hp.svg | 2 +- .../dynamic-vertical-scale-hp.svg | 2 +- .../system/scada_symbols/energy-meter-hp.svg | 2 +- .../four-rate-energy-meter-hp.svg | 8 ++-- .../horizontal-inline-flow-meter.svg | 2 +- .../system/scada_symbols/left-flow-meter.svg | 2 +- .../system/scada_symbols/right-flow-meter.svg | 2 +- .../simple-horizontal-scale-hp.svg | 2 +- .../simple-vertical-scale-hp.svg | 2 +- .../three-rate-energy-meter-hp.svg | 6 +-- .../system/scada_symbols/top-flow-meter.svg | 2 +- .../two-rate-energy-meter-hp.svg | 4 +- .../vertical-inline-flow-meter.svg | 2 +- .../widget/dynamic-widget.component.ts | 2 + .../widget/lib/scada/scada-symbol.models.ts | 38 +++++++++---------- .../home/models/widget-component.models.ts | 2 + .../scada-symbol-editor.models.ts | 3 +- 19 files changed, 46 insertions(+), 45 deletions(-) diff --git a/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg b/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg index 1152103805..8e1a46978e 100644 --- a/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/3-phase-voltage-relay-hp.svg @@ -39,7 +39,7 @@ }, { "tag": "firstPhaseValue", - "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.firstPhaseVoltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, id: 0}));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.firstPhaseVoltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));\n} else {\n element.hide();\n}", "actions": null }, { @@ -49,7 +49,7 @@ }, { "tag": "secondPhaseValue", - "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.secondPhaseVoltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, id: 1}));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.secondPhaseVoltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));\n} else {\n element.hide();\n}", "actions": null }, { @@ -59,7 +59,7 @@ }, { "tag": "thirdPhaseValue", - "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.thirdPhaseVoltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, id: 2}));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.values.running) {\n element.show();\n ctx.api.font(element, ctx.properties.currentVoltageFont, ctx.properties.currentVoltageColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.thirdPhaseVoltage, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));\n} else {\n element.hide();\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg index e003565dd2..f3c20f2324 100644 --- a/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg index 5dc0de1372..7b11968223 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg @@ -78,7 +78,7 @@ }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false}));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true}));\n} else {\n element.hide();\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg index 020874f35d..37ba3df7c2 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg @@ -78,7 +78,7 @@ }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false}));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true}));\n} else {\n element.hide();\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg index 51966854a1..3855d66ec8 100644 --- a/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/energy-meter-hp.svg @@ -43,7 +43,7 @@ }, { "tag": "value", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.measured, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.measured, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg index 5e43858202..e095c1417f 100644 --- a/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/four-rate-energy-meter-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "export-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.exportValueFont, ctx.properties.exportValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.exportRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 3}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.exportValueFont, ctx.properties.exportValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.exportRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { @@ -48,7 +48,7 @@ }, { "tag": "night-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 1}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { @@ -58,7 +58,7 @@ }, { "tag": "off-peak-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 0}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { @@ -68,7 +68,7 @@ }, { "tag": "peak-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 2}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg index 555293931e..a2ca312730 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" diff --git a/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg index 55c60de9ea..e121adaffd 100644 --- a/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" diff --git a/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg index 53a2585fd0..3984083640 100644 --- a/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" diff --git a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg index bd29ac0c4b..9425ed48f2 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg @@ -78,7 +78,7 @@ }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false}));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true}));\n} else {\n element.hide();\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg index ed2eb8ec07..4e14130ef3 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg @@ -78,7 +78,7 @@ }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false}));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, {units: ctx.properties.units, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true}));\n} else {\n element.hide();\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg index 11e27b2af8..526f6aa719 100644 --- a/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/three-rate-energy-meter-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "night-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 1}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { @@ -48,7 +48,7 @@ }, { "tag": "off-peak-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 0}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.offPeakValueFont, ctx.properties.offPeakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.offPeakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { @@ -58,7 +58,7 @@ }, { "tag": "peak-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 2}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.peakValueFont, ctx.properties.peakValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.peakRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg index 9f7824b4ae..ae9b6701af 100644 --- a/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" diff --git a/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg b/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg index 444a41cb08..e87548f059 100644 --- a/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/two-rate-energy-meter-hp.svg @@ -38,7 +38,7 @@ }, { "tag": "day-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.dayValueFont, ctx.properties.dayValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.dayRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 0}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.dayValueFont, ctx.properties.dayValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.dayRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { @@ -48,7 +48,7 @@ }, { "tag": "night-rate", - "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true, showZeroDecimals: false, id: 1}));", + "stateRenderFunction": "ctx.api.font(element, ctx.properties.nightValueFont, ctx.properties.nightValueColor);\nctx.api.text(element, ctx.api.formatValue(ctx.values.nightRate, {units: ctx.properties.units, decimals: 0, ignoreUnitSymbol: true}));", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg index 544a83dc06..e270024f8b 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg @@ -57,7 +57,7 @@ }, { "tag": "value", - "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true, showZeroDecimals: false});\nctx.api.text(element, value);\n", + "stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, {units: ctx.properties.valueUnits, decimals: ctx.properties.valueDecimals, ignoreUnitSymbol: true});\nctx.api.text(element, value);\n", "actions": { "click": { "actionFunction": "ctx.api.callAction(event, 'displayClick');" diff --git a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts index b6919ec670..8458419ed8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts @@ -52,6 +52,7 @@ import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time- import { UserSettingsService } from '@core/http/user-settings.service'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { UtilsService } from '@core/services/utils.service'; +import { UnitService } from '@core/services/unit.service'; @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix @@ -92,6 +93,7 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid this.ctx.userSettingsService = this.$injector.get(UserSettingsService); this.ctx.utilsService = this.$injector.get(UtilsService); this.ctx.telemetryWsService = this.$injector.get(TelemetryWebsocketService); + this.ctx.unitService = this.$injector.get(UnitService); this.ctx.date = this.$injector.get(DatePipe); this.ctx.imagePipe = this.$injector.get(ImagePipe); this.ctx.milliSecondsToTimeString = this.$injector.get(MillisecondsToTimeStringPipe); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts index aa7669156a..ee46dd6ad4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts @@ -51,7 +51,7 @@ import { isUndefined, isUndefinedOrNull, mergeDeep, - mergeDeepIgnoreArray, + mergeDeepIgnoreArray, objectHashCode, parseFunction } from '@core/utils'; import { BehaviorSubject, forkJoin, Observable, Observer, of, Subject } from 'rxjs'; @@ -82,7 +82,7 @@ import { TbUnit } from '@shared/models/unit.models'; export interface ScadaSymbolApi { generateElementId: () => string; formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined; - formatValue(value: any, settings: ValueFormatIdSettings): string; + formatValue(value: any, settings: ValueFormatSettings): string; text: (element: Element | Element[], text: string) => void; font: (element: Element | Element[], font: Font, color: string) => void; icon: (element: Element | Element[], icon: string, size?: number, color?: string, center?: boolean) => void; @@ -184,10 +184,6 @@ export interface ScadaSymbolMetadata { properties: FormProperty[]; } -interface ValueFormatIdSettings extends ValueFormatSettings { - id?: number; -} - export const emptyMetadata = (width?: number, height?: number): ScadaSymbolMetadata => ({ title: '', widgetSizeX: width ? Math.max(Math.round(width/100), 1) : 3, @@ -828,28 +824,28 @@ export class ScadaSymbolObject { } private unitSymbol(unit: TbUnit): string { - return this.ctx.$scope.$injector.get(this.ctx.servicesMap.get('unitService')).getTargetUnitSymbol(unit); + return this.ctx.unitService.getTargetUnitSymbol(unit); } private convertUnitValue(value: number, unit: TbUnit): number { - return this.ctx.$scope.$injector.get(this.ctx.servicesMap.get('unitService')).convertUnitValue(value, unit); + return this.ctx.unitService.convertUnitValue(value, unit); } - private formatValue(value: any, settings: ValueFormatIdSettings): string; + private formatValue(value: any, settings: ValueFormatSettings): string; private formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined; - private formatValue(value: any, settingsOrDec?: ValueFormatIdSettings | number, units?: string, showZeroDecimals?: boolean): string { - const id = (settingsOrDec as ValueFormatIdSettings)?.id || 0; - if (!this.valueProcessor[id]) { - let valueFormatSettings: ValueFormatSettings; - if (typeof settingsOrDec === 'object') { - valueFormatSettings = deepClone(settingsOrDec, ['id']); - } else { - valueFormatSettings = { - units, - decimals: settingsOrDec, - showZeroDecimals - } + private formatValue(value: any, settingsOrDec?: ValueFormatSettings | number, units?: string, showZeroDecimals?: boolean): string { + let valueFormatSettings: ValueFormatSettings; + if (typeof settingsOrDec === 'object') { + valueFormatSettings = deepClone(settingsOrDec); + } else { + valueFormatSettings = { + units, + decimals: settingsOrDec, + showZeroDecimals } + } + const id = objectHashCode(valueFormatSettings) + ''; + if (!this.valueProcessor[id]) { this.valueProcessor[id] = ValueFormatProcessor.fromSettings(this.ctx.$injector, valueFormatSettings); } return this.valueProcessor[id].format(value); diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 8d2622ebea..4a22129ba8 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -118,6 +118,7 @@ import { CompiledTbFunction } from '@shared/models/js-function.models'; import { FormProperty } from '@shared/models/dynamic-form.models'; import { ExportableEntity } from '@shared/models/base-data'; import { TbUnit } from '@shared/models/unit.models'; +import { UnitService } from '@core/services/unit.service'; export interface IWidgetAction { name: string; @@ -225,6 +226,7 @@ export class WidgetContext { userSettingsService: UserSettingsService; utilsService: UtilsService; telemetryWsService: TelemetryWebsocketService; + unitService: UnitService; telemetrySubscribers?: Array; date: DatePipe; imagePipe: ImagePipe; diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts index a9edc152e9..e7a4380c4a 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts @@ -1313,7 +1313,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags }, { name: 'settingsOrDec', - description: 'Either a ValueFormatIdSettings object containing formatting settings or the number of decimal digits. ValueFormatIdSettings includes: decimals (number of decimal digits, optional), units (unit specification as string or TbUnitMapping, optional), showZeroDecimals (whether to keep zero decimal digits, optional), ignoreUnitSymbol (whether to exclude unit symbol from output, optional), and id (unique identifier for the processor, optional).', type: 'ValueFormatIdSettings | number', + description: 'Either a ValueFormatSettings object containing formatting settings or the number of decimal digits. ValueFormatSettings includes: decimals (number of decimal digits, optional), units (unit specification as string or TbUnitMapping, optional), showZeroDecimals (whether to keep zero decimal digits, optional), ignoreUnitSymbol (whether to exclude unit symbol from output, optional).', + type: 'ValueFormatSettings | number', optional: true }, { From cbf5eabf3a670e007458fe2e015c2e75e97bb013 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Jun 2025 13:00:57 +0300 Subject: [PATCH 309/335] UI: optimize import --- .../home/components/widget/lib/scada/scada-symbol.models.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts index ee46dd6ad4..802668ed95 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts @@ -51,7 +51,8 @@ import { isUndefined, isUndefinedOrNull, mergeDeep, - mergeDeepIgnoreArray, objectHashCode, + mergeDeepIgnoreArray, + objectHashCode, parseFunction } from '@core/utils'; import { BehaviorSubject, forkJoin, Observable, Observer, of, Subject } from 'rxjs'; From 0d4b9442552929641b84f350c2ab00efd15df756 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Jun 2025 15:06:28 +0300 Subject: [PATCH 310/335] UI: Dynamicly update slider on input value change --- .../rule/rule-notification-dialog.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html index 68122875c0..3dbbea4277 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html @@ -484,7 +484,7 @@ notification.threshold
- + notification.cpu-threshold
- + notification.ram-threshold
- + notification.storage-threshold
- + Date: Mon, 9 Jun 2025 15:47:52 +0300 Subject: [PATCH 311/335] Return `AttributesSaveResult` instead of just `List` when saving attributes --- .../cf/CalculatedFieldQueueService.java | 3 +- .../DefaultCalculatedFieldQueueService.java | 12 +++--- .../device/DeviceProvisionServiceImpl.java | 12 +++--- .../service/edge/rpc/EdgeGrpcSession.java | 16 ++++---- .../DefaultSystemDataLoaderService.java | 9 +++-- .../DefaultTelemetrySubscriptionService.java | 8 ++-- .../telemetry/InternalTelemetryService.java | 5 +-- .../service/entitiy/EntityServiceTest.java | 25 ++++++------ .../state/DefaultDeviceStateServiceTest.java | 5 ++- ...faultTelemetrySubscriptionServiceTest.java | 34 +++++++++++------ .../dao/attributes/AttributesService.java | 5 ++- .../common/data/kv/AttributesSaveResult.java | 32 ++++++++++++++++ .../dao/attributes/BaseAttributesService.java | 27 +++++++------ .../attributes/CachedAttributesService.java | 38 +++++++++---------- 14 files changed, 140 insertions(+), 91 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/kv/AttributesSaveResult.java diff --git a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldQueueService.java b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldQueueService.java index eb86220361..f9ec8087ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldQueueService.java @@ -21,6 +21,7 @@ import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineCalculatedFieldQueueService; import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.TimeseriesSaveResult; import java.util.List; @@ -35,7 +36,7 @@ public interface CalculatedFieldQueueService extends RuleEngineCalculatedFieldQu */ void pushRequestToQueue(TimeseriesSaveRequest request, TimeseriesSaveResult result, FutureCallback callback); - void pushRequestToQueue(AttributesSaveRequest request, List result, FutureCallback callback); + void pushRequestToQueue(AttributesSaveRequest request, AttributesSaveResult result, FutureCallback callback); void pushRequestToQueue(AttributesDeleteRequest request, List result, FutureCallback callback); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java index 8289e4db42..c3185738f9 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.TimeseriesSaveResult; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -96,7 +97,7 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS } @Override - public void pushRequestToQueue(AttributesSaveRequest request, List result, FutureCallback callback) { + public void pushRequestToQueue(AttributesSaveRequest request, AttributesSaveResult result, FutureCallback callback) { var tenantId = request.getTenantId(); var entityId = request.getEntityId(); checkEntityAndPushToQueue(tenantId, entityId, cf -> cf.matches(request.getEntries(), request.getScope()), cf -> cf.linkMatches(entityId, request.getEntries(), request.getScope()), @@ -186,17 +187,18 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS return msg.build(); } - private ToCalculatedFieldMsg toCalculatedFieldTelemetryMsgProto(AttributesSaveRequest request, List versions) { + private ToCalculatedFieldMsg toCalculatedFieldTelemetryMsgProto(AttributesSaveRequest request, AttributesSaveResult result) { ToCalculatedFieldMsg.Builder msg = ToCalculatedFieldMsg.newBuilder(); CalculatedFieldTelemetryMsgProto.Builder telemetryMsg = buildTelemetryMsgProto(request.getTenantId(), request.getEntityId(), request.getPreviousCalculatedFieldIds(), request.getTbMsgId(), request.getTbMsgType()); telemetryMsg.setScope(AttributeScopeProto.valueOf(request.getScope().name())); + List entries = request.getEntries(); + List versions = result.versions(); + for (int i = 0; i < entries.size(); i++) { AttributeValueProto.Builder attrProtoBuilder = ProtoUtils.toProto(entries.get(i)).toBuilder(); - if (versions != null) { - attrProtoBuilder.setVersion(versions.get(i)); - } + attrProtoBuilder.setVersion(versions.get(i)); telemetryMsg.addAttrData(attrProtoBuilder.build()); } msg.setTelemetryMsg(telemetryMsg.build()); diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java index 173ba742af..0778d61ee7 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.msg.TbMsgType; @@ -62,8 +63,6 @@ import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; -import java.util.Collections; -import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; @@ -240,10 +239,11 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { return deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials); } - private ListenableFuture> saveProvisionStateAttribute(Device device) { - return attributesService.save(device.getTenantId(), device.getId(), AttributeScope.SERVER_SCOPE, - Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(DEVICE_PROVISION_STATE, PROVISIONED_STATE), - System.currentTimeMillis()))); + private ListenableFuture saveProvisionStateAttribute(Device device) { + return attributesService.save( + device.getTenantId(), device.getId(), AttributeScope.SERVER_SCOPE, + new BaseAttributeKvEntry(new StringDataEntry(DEVICE_PROVISION_STATE, PROVISIONED_STATE), System.currentTimeMillis()) + ); } private DeviceCredentials getDeviceCredentials(Device device) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 4a9b68fc6d..8922fa6008 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -42,7 +43,6 @@ import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.notification.rule.trigger.EdgeCommunicationFailureTrigger; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg; @@ -582,10 +582,10 @@ public abstract class EdgeGrpcSession implements Closeable { @Override public void onSuccess(@Nullable Pair newStartTsAndSeqId) { if (newStartTsAndSeqId != null) { - ListenableFuture> updateFuture = updateQueueStartTsAndSeqId(newStartTsAndSeqId); + ListenableFuture updateFuture = updateQueueStartTsAndSeqId(newStartTsAndSeqId); Futures.addCallback(updateFuture, new FutureCallback<>() { @Override - public void onSuccess(@Nullable List list) { + public void onSuccess(@Nullable AttributesSaveResult saveResult) { log.debug("[{}][{}] queue offset was updated [{}]", tenantId, sessionId, newStartTsAndSeqId); boolean newEventsAvailable; if (fetcher.isSeqIdNewCycleStarted()) { @@ -646,8 +646,7 @@ public abstract class EdgeGrpcSession implements Closeable { log.trace("[{}][{}] entity message processed [{}]", tenantId, sessionId, downlinkMsg); } } - case ATTRIBUTES_UPDATED, POST_ATTRIBUTES, ATTRIBUTES_DELETED, TIMESERIES_UPDATED -> - downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edge, edgeEvent); + case ATTRIBUTES_UPDATED, POST_ATTRIBUTES, ATTRIBUTES_DELETED, TIMESERIES_UPDATED -> downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edge, edgeEvent); default -> log.warn("[{}][{}] Unsupported action type [{}]", tenantId, sessionId, edgeEvent.getAction()); } } catch (Exception e) { @@ -723,13 +722,14 @@ public abstract class EdgeGrpcSession implements Closeable { return startSeqId; } - private ListenableFuture> updateQueueStartTsAndSeqId(Pair pair) { + private ListenableFuture updateQueueStartTsAndSeqId(Pair pair) { newStartTs = pair.getFirst(); newStartSeqId = pair.getSecond(); log.trace("[{}] updateQueueStartTsAndSeqId [{}][{}][{}]", sessionId, edge.getId(), newStartTs, newStartSeqId); - List attributes = Arrays.asList( + List attributes = List.of( new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis()), - new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_SEQ_ID_ATTR_KEY, newStartSeqId), System.currentTimeMillis())); + new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_SEQ_ID_ATTR_KEY, newStartSeqId), System.currentTimeMillis()) + ); return ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), AttributeScope.SERVER_SCOPE, attributes); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index e9ef8c5ace..d580175aa0 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -64,6 +64,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; @@ -581,9 +582,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), 0L); addTsCallback(saveFuture, new TelemetrySaveCallback<>(deviceId, key, value)); } else { - ListenableFuture> saveFuture = attributesService.save(TenantId.SYS_TENANT_ID, deviceId, AttributeScope.SERVER_SCOPE, - Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value) - , System.currentTimeMillis()))); + ListenableFuture saveFuture = attributesService.save( + TenantId.SYS_TENANT_ID, deviceId, AttributeScope.SERVER_SCOPE, new BaseAttributeKvEntry(new BooleanDataEntry(key, value), System.currentTimeMillis()) + ); addTsCallback(saveFuture, new TelemetrySaveCallback<>(deviceId, key, value)); } } @@ -611,7 +612,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { } private void addTsCallback(ListenableFuture saveFuture, final FutureCallback callback) { - Futures.addCallback(saveFuture, new FutureCallback() { + Futures.addCallback(saveFuture, new FutureCallback<>() { @Override public void onSuccess(@Nullable S result) { callback.onSuccess(result); diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 4640b9339f..0ff2d42f15 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TimeseriesSaveResult; import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -62,7 +63,6 @@ import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -190,16 +190,16 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } @Override - public ListenableFuture> saveAttributesInternal(AttributesSaveRequest request) { + public ListenableFuture saveAttributesInternal(AttributesSaveRequest request) { TenantId tenantId = request.getTenantId(); EntityId entityId = request.getEntityId(); AttributesSaveRequest.Strategy strategy = request.getStrategy(); - ListenableFuture> resultFuture; + ListenableFuture resultFuture; if (strategy.saveAttributes()) { resultFuture = attrService.save(tenantId, entityId, request.getScope(), request.getEntries()); } else { - resultFuture = Futures.immediateFuture(Collections.emptyList()); + resultFuture = Futures.immediateFuture(AttributesSaveResult.EMPTY); } addMainCallback(resultFuture, result -> { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java index 79f0beab41..2d3be5a0ba 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java @@ -21,10 +21,9 @@ import org.thingsboard.rule.engine.api.AttributesSaveRequest; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TimeseriesDeleteRequest; import org.thingsboard.rule.engine.api.TimeseriesSaveRequest; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.TimeseriesSaveResult; -import java.util.List; - /** * Created by ashvayka on 27.03.18. */ @@ -32,7 +31,7 @@ public interface InternalTelemetryService extends RuleEngineTelemetryService { ListenableFuture saveTimeseriesInternal(TimeseriesSaveRequest request); - ListenableFuture> saveAttributesInternal(AttributesSaveRequest request); + ListenableFuture saveAttributesInternal(AttributesSaveRequest request); void deleteTimeseriesInternal(TimeseriesDeleteRequest request); diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/EntityServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/EntityServiceTest.java index 264ac70443..d8bcd42bc8 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/EntityServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/EntityServiceTest.java @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; @@ -395,7 +396,7 @@ public class EntityServiceTest extends AbstractControllerTest { List highTemperatures = new ArrayList<>(); createTestHierarchy(tenantId, assets, devices, new ArrayList<>(), new ArrayList<>(), temperatures, highTemperatures); - List>> attributeFutures = new ArrayList<>(); + List> attributeFutures = new ArrayList<>(); for (int i = 0; i < devices.size(); i++) { Device device = devices.get(i); attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), AttributeScope.CLIENT_SCOPE)); @@ -545,7 +546,7 @@ public class EntityServiceTest extends AbstractControllerTest { List highTemperatures = new ArrayList<>(); createTestHierarchy(tenantId, assets, devices, new ArrayList<>(), new ArrayList<>(), temperatures, highTemperatures); - List>> attributeFutures = new ArrayList<>(); + List> attributeFutures = new ArrayList<>(); for (int i = 0; i < devices.size(); i++) { Device device = devices.get(i); attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), AttributeScope.CLIENT_SCOPE)); @@ -599,7 +600,7 @@ public class EntityServiceTest extends AbstractControllerTest { List highConsumptions = new ArrayList<>(); createTestHierarchy(tenantId, assets, devices, consumptions, highConsumptions, new ArrayList<>(), new ArrayList<>()); - List>> attributeFutures = new ArrayList<>(); + List> attributeFutures = new ArrayList<>(); for (int i = 0; i < assets.size(); i++) { Asset asset = assets.get(i); attributeFutures.add(saveLongAttribute(asset.getId(), "consumption", consumptions.get(i), AttributeScope.SERVER_SCOPE)); @@ -1506,7 +1507,7 @@ public class EntityServiceTest extends AbstractControllerTest { } } - List>> attributeFutures = new ArrayList<>(); + List> attributeFutures = new ArrayList<>(); for (int i = 0; i < devices.size(); i++) { Device device = devices.get(i); for (AttributeScope currentScope : AttributeScope.values()) { @@ -1578,7 +1579,7 @@ public class EntityServiceTest extends AbstractControllerTest { } } - List>> attributeFutures = new ArrayList<>(); + List> attributeFutures = new ArrayList<>(); for (int i = 0; i < devices.size(); i++) { Device device = devices.get(i); attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), AttributeScope.CLIENT_SCOPE)); @@ -1808,7 +1809,7 @@ public class EntityServiceTest extends AbstractControllerTest { } } - List>> attributeFutures = new ArrayList<>(); + List> attributeFutures = new ArrayList<>(); for (int i = 0; i < devices.size(); i++) { Device device = devices.get(i); attributeFutures.add(saveStringAttribute(device.getId(), "attributeString", attributeStrings.get(i), AttributeScope.CLIENT_SCOPE)); @@ -2269,16 +2270,16 @@ public class EntityServiceTest extends AbstractControllerTest { return filter; } - private ListenableFuture> saveLongAttribute(EntityId entityId, String key, long value, AttributeScope scope) { + private ListenableFuture saveLongAttribute(EntityId entityId, String key, long value, AttributeScope scope) { KvEntry attrValue = new LongDataEntry(key, value); AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L); - return attributesService.save(tenantId, entityId, scope, Collections.singletonList(attr)); + return attributesService.save(tenantId, entityId, scope, List.of(attr)); } - private ListenableFuture> saveStringAttribute(EntityId entityId, String key, String value, AttributeScope scope) { + private ListenableFuture saveStringAttribute(EntityId entityId, String key, String value, AttributeScope scope) { KvEntry attrValue = new StringDataEntry(key, value); AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L); - return attributesService.save(tenantId, entityId, scope, Collections.singletonList(attr)); + return attributesService.save(tenantId, entityId, scope, List.of(attr)); } private ListenableFuture saveTimeseries(EntityId entityId, String key, Double value) { @@ -2294,8 +2295,8 @@ public class EntityServiceTest extends AbstractControllerTest { } protected void createMultiRootHierarchy(List buildings, List apartments, - Map> entityNameByTypeMap, - Map childParentRelationMap) throws InterruptedException { + Map> entityNameByTypeMap, + Map childParentRelationMap) throws InterruptedException { for (int k = 0; k < 3; k++) { Asset building = new Asset(); building.setTenantId(tenantId); diff --git a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java index 0fe29eef57..5bf87137ff 100644 --- a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityTrigger; import org.thingsboard.server.common.msg.TbMsg; @@ -911,7 +912,7 @@ class DefaultDeviceStateServiceTest { // 10 millis pass... and new activity message it received // this time DB save is successful - when(telemetrySubscriptionService.saveAttributesInternal(any())).thenReturn(Futures.immediateFuture(generateRandomVersions(1))); + when(telemetrySubscriptionService.saveAttributesInternal(any())).thenReturn(Futures.immediateFuture(AttributesSaveResult.of(generateRandomVersions(1)))); doReturn(210L).when(service).getCurrentTimeMillis(); service.onDeviceActivity(tenantId, deviceId, 190L); assertThat(deviceState.isActive()).isTrue(); @@ -947,7 +948,7 @@ class DefaultDeviceStateServiceTest { // waiting 100 millis... periodic activity states check is triggered again // this time DB save is successful - when(telemetrySubscriptionService.saveAttributesInternal(any())).thenReturn(Futures.immediateFuture(generateRandomVersions(1))); + when(telemetrySubscriptionService.saveAttributesInternal(any())).thenReturn(Futures.immediateFuture(AttributesSaveResult.of(generateRandomVersions(1)))); doReturn(300L).when(service).getCurrentTimeMillis(); service.checkStates(); assertThat(deviceState.isActive()).isFalse(); diff --git a/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java index 2b4d9f38e5..153228a865 100644 --- a/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionServiceTest.java @@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; @@ -472,7 +473,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(saveAttributes, sendWsUpdate, processCalculatedFields)) .build(); - lenient().when(attrService.save(tenantId, entityId, request.getScope(), request.getEntries())).thenReturn(immediateFuture(listOfNNumbers(request.getEntries().size()))); + lenient().when(attrService.save(tenantId, entityId, request.getScope(), request.getEntries())) + .thenReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(request.getEntries().size())))); // WHEN telemetryService.saveAttributes(request); @@ -547,7 +549,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, deviceId, request.getScope(), entries)).willReturn(immediateFuture(listOfNNumbers(entries.size()))); + given(attrService.save(tenantId, deviceId, request.getScope(), entries)) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(entries.size())))); // WHEN telemetryService.saveAttributes(request); @@ -581,7 +584,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, nonDeviceId, request.getScope(), entries)).willReturn(immediateFuture(listOfNNumbers(entries.size()))); + given(attrService.save(tenantId, nonDeviceId, request.getScope(), entries)) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(entries.size())))); // WHEN telemetryService.saveAttributes(request); @@ -613,7 +617,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, deviceId, request.getScope(), entries)).willReturn(immediateFuture(listOfNNumbers(entries.size()))); + given(attrService.save(tenantId, deviceId, request.getScope(), entries)) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(entries.size())))); // WHEN telemetryService.saveAttributes(request); @@ -640,7 +645,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, deviceId, request.getScope(), entries)).willReturn(immediateFuture(listOfNNumbers(entries.size()))); + given(attrService.save(tenantId, deviceId, request.getScope(), entries)) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(entries.size())))); // WHEN telemetryService.saveAttributes(request); @@ -715,7 +721,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())).willReturn(immediateFuture(listOfNNumbers(request.getEntries().size()))); + given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(request.getEntries().size())))); // WHEN telemetryService.saveAttributes(request); @@ -764,7 +771,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, nonDeviceId, request.getScope(), request.getEntries())).willReturn(immediateFuture(listOfNNumbers(request.getEntries().size()))); + given(attrService.save(tenantId, nonDeviceId, request.getScope(), request.getEntries())) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(request.getEntries().size())))); // WHEN telemetryService.saveAttributes(request); @@ -792,7 +800,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())).willReturn(immediateFuture(listOfNNumbers(request.getEntries().size()))); + given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(request.getEntries().size())))); // WHEN telemetryService.saveAttributes(request); @@ -815,7 +824,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())).willReturn(immediateFuture(listOfNNumbers(request.getEntries().size()))); + given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(request.getEntries().size())))); // WHEN telemetryService.saveAttributes(request); @@ -843,7 +853,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())).willReturn(immediateFuture(listOfNNumbers(request.getEntries().size()))); + given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(request.getEntries().size())))); // WHEN telemetryService.saveAttributes(request); @@ -870,7 +881,8 @@ class DefaultTelemetrySubscriptionServiceTest { .strategy(new AttributesSaveRequest.Strategy(true, false, false)) .build(); - given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())).willReturn(immediateFuture(listOfNNumbers(request.getEntries().size()))); + given(attrService.save(tenantId, deviceId, request.getScope(), request.getEntries())) + .willReturn(immediateFuture(AttributesSaveResult.of(listOfNNumbers(request.getEntries().size())))); // WHEN telemetryService.saveAttributes(request); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java index 718c574c3c..0d5d3dcd13 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import java.util.Collection; import java.util.List; @@ -37,9 +38,9 @@ public interface AttributesService { ListenableFuture> findAll(TenantId tenantId, EntityId entityId, AttributeScope scope); - ListenableFuture> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes); + ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes); - ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute); + ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute); ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributeKeys); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/AttributesSaveResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/AttributesSaveResult.java new file mode 100644 index 0000000000..711a3e06a4 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/AttributesSaveResult.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2025 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. + */ +package org.thingsboard.server.common.data.kv; + +import java.util.Collections; +import java.util.List; + +public record AttributesSaveResult(List versions) { + + public static final AttributesSaveResult EMPTY = new AttributesSaveResult(Collections.emptyList()); + + public static AttributesSaveResult of(List versions) { + if (versions == null) { + return EMPTY; + } + return new AttributesSaveResult(versions); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index 777a77d054..9803670d4b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.edqs.EdqsService; import org.thingsboard.server.dao.service.Validator; @@ -41,7 +42,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import static org.thingsboard.server.dao.attributes.AttributeUtils.validate; @@ -101,26 +101,29 @@ public class BaseAttributesService implements AttributesService { } @Override - public ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute) { + public ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute) { validate(entityId, scope); AttributeUtils.validate(attribute, valueNoXssValidation); - return doSave(tenantId, entityId, scope, attribute); + return doSave(tenantId, entityId, scope, List.of(attribute)); } @Override - public ListenableFuture> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { + public ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { validate(entityId, scope); AttributeUtils.validate(attributes, valueNoXssValidation); - List> saveFutures = attributes.stream().map(attribute -> doSave(tenantId, entityId, scope, attribute)).collect(Collectors.toList()); - return Futures.allAsList(saveFutures); + return doSave(tenantId, entityId, scope, attributes); } - private ListenableFuture doSave(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute) { - ListenableFuture future = attributesDao.save(tenantId, entityId, scope, attribute); - return Futures.transform(future, version -> { - edqsService.onUpdate(tenantId, ObjectType.ATTRIBUTE_KV, new AttributeKv(entityId, scope, attribute, version)); - return version; - }, MoreExecutors.directExecutor()); + private ListenableFuture doSave(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { + List> futures = new ArrayList<>(attributes.size()); + for (AttributeKvEntry attribute : attributes) { + ListenableFuture future = Futures.transform(attributesDao.save(tenantId, entityId, scope, attribute), version -> { + edqsService.onUpdate(tenantId, ObjectType.ATTRIBUTE_KV, new AttributeKv(entityId, scope, attribute, version)); + return version; + }, MoreExecutors.directExecutor()); + futures.add(future); + } + return Futures.transform(Futures.allAsList(futures), AttributesSaveResult::of, MoreExecutors.directExecutor()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java index 559828911f..d99413f13a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.AttributesSaveResult; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.edqs.EdqsService; @@ -56,7 +57,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import static org.thingsboard.server.dao.attributes.AttributeUtils.validate; @@ -150,7 +150,7 @@ public class CachedAttributesService implements AttributesService { List cachedAttributes = wrappedCachedAttributes.values().stream() .map(TbCacheValueWrapper::get) .filter(Objects::nonNull) - .collect(Collectors.toList()); + .toList(); if (wrappedCachedAttributes.size() == attributeKeys.size()) { log.trace("[{}][{}] Found all attributes from cache: {}", entityId, scope, attributeKeys); return Futures.immediateFuture(cachedAttributes); @@ -159,8 +159,6 @@ public class CachedAttributesService implements AttributesService { Set notFoundAttributeKeys = new HashSet<>(attributeKeys); notFoundAttributeKeys.removeAll(wrappedCachedAttributes.keySet()); - List notFoundKeys = notFoundAttributeKeys.stream().map(k -> new AttributeCacheKey(scope, entityId, k)).collect(Collectors.toList()); - // DB call should run in DB executor, not in cache-related executor return jpaExecutorService.submit(() -> { log.trace("[{}][{}] Lookup attributes from db: {}", entityId, scope, notFoundAttributeKeys); @@ -222,33 +220,31 @@ public class CachedAttributesService implements AttributesService { } @Override - public ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute) { + public ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute) { validate(entityId, scope); AttributeUtils.validate(attribute, valueNoXssValidation); - return doSave(tenantId, entityId, scope, attribute); + return doSave(tenantId, entityId, scope, List.of(attribute)); } @Override - public ListenableFuture> save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { + public ListenableFuture save(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { validate(entityId, scope); AttributeUtils.validate(attributes, valueNoXssValidation); + return doSave(tenantId, entityId, scope, attributes); + } + private ListenableFuture doSave(TenantId tenantId, EntityId entityId, AttributeScope scope, List attributes) { List> futures = new ArrayList<>(attributes.size()); for (var attribute : attributes) { - futures.add(doSave(tenantId, entityId, scope, attribute)); + ListenableFuture future = Futures.transform(attributesDao.save(tenantId, entityId, scope, attribute), version -> { + BaseAttributeKvEntry attributeKvEntry = new BaseAttributeKvEntry(((BaseAttributeKvEntry) attribute).getKv(), attribute.getLastUpdateTs(), version); + put(entityId, scope, attributeKvEntry); + edqsService.onUpdate(tenantId, ObjectType.ATTRIBUTE_KV, new AttributeKv(entityId, scope, attributeKvEntry, version)); + return version; + }, cacheExecutor); + futures.add(future); } - - return Futures.allAsList(futures); - } - - private ListenableFuture doSave(TenantId tenantId, EntityId entityId, AttributeScope scope, AttributeKvEntry attribute) { - ListenableFuture future = attributesDao.save(tenantId, entityId, scope, attribute); - return Futures.transform(future, version -> { - BaseAttributeKvEntry attributeKvEntry = new BaseAttributeKvEntry(((BaseAttributeKvEntry) attribute).getKv(), attribute.getLastUpdateTs(), version); - put(entityId, scope, attributeKvEntry); - edqsService.onUpdate(tenantId, ObjectType.ATTRIBUTE_KV, new AttributeKv(entityId, scope, attributeKvEntry, version)); - return version; - }, cacheExecutor); + return Futures.transform(Futures.allAsList(futures), AttributesSaveResult::of, MoreExecutors.directExecutor()); } private void put(EntityId entityId, AttributeScope scope, AttributeKvEntry attribute) { @@ -270,7 +266,7 @@ public class CachedAttributesService implements AttributesService { edqsService.onDelete(tenantId, ObjectType.ATTRIBUTE_KV, new AttributeKv(entityId, scope, key, version)); } return key; - }, cacheExecutor)).collect(Collectors.toList())); + }, cacheExecutor)).toList()); } @Override From 429f9c7cc9174b9ce9ae6ba00bcc79cd58419d56 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 9 Jun 2025 15:41:27 +0300 Subject: [PATCH 312/335] =?UTF-8?q?UI:=20Prevent=20deleting=20an=20entity?= =?UTF-8?q?=20alias/filter=20that=E2=80=99s=20still=20used=20in=20map=20wi?= =?UTF-8?q?dgets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/services/dashboard-utils.service.ts | 12 +++++- .../app/core/services/item-buffer.service.ts | 6 +-- .../alias/entity-aliases-dialog.component.ts | 14 +++---- .../filter/filters-dialog.component.ts | 20 +++++----- ...port.models.ts => map-model.definition.ts} | 40 +++++++++++++++++-- ...t.models.ts => widget-model.definition.ts} | 15 +++---- 6 files changed, 74 insertions(+), 33 deletions(-) rename ui-ngx/src/app/shared/models/widget/maps/{map-export.models.ts => map-model.definition.ts} (81%) rename ui-ngx/src/app/shared/models/widget/{widget-export.models.ts => widget-model.definition.ts} (68%) diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index dca6dd45eb..1511840c57 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -31,7 +31,8 @@ import { DashboardLayoutsInfo, DashboardState, DashboardStateLayouts, - GridSettings, LayoutType, + GridSettings, + LayoutType, WidgetLayout } from '@shared/models/dashboard.models'; import { deepClone, isDefined, isDefinedAndNotNull, isNotEmptyStr, isString, isUndefined } from '@core/utils'; @@ -61,6 +62,7 @@ import { MediaBreakpoints } from '@shared/models/constants'; import { TranslateService } from '@ngx-translate/core'; import { DashboardPageLayout } from '@home/components/dashboard-page/dashboard-page.models'; import { maxGridsterCol, maxGridsterRow } from '@home/models/dashboard-component.models'; +import { findWidgetModelDefinition } from '@shared/models/widget/widget-model.definition'; @Injectable({ providedIn: 'root' @@ -398,6 +400,14 @@ export class DashboardUtilsService { return datasources; } + public getWidgetDatasources(widget: Widget): Datasource[] { + const widgetDefinition = findWidgetModelDefinition(widget); + if (widgetDefinition) { + return widgetDefinition.datasources(widget); + } + return this.validateAndUpdateDatasources(widget.config.datasources); + } + public createDefaultLayoutData(): DashboardLayout { return { widgets: {}, diff --git a/ui-ngx/src/app/core/services/item-buffer.service.ts b/ui-ngx/src/app/core/services/item-buffer.service.ts index cef9bbe7a3..d525bee677 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -35,7 +35,7 @@ import { FcRuleNode, ruleNodeTypeDescriptors } from '@shared/models/rule-node.mo import { RuleChainService } from '@core/http/rule-chain.service'; import { RuleChainImport } from '@shared/models/rule-chain.models'; import { Filter, FilterInfo, Filters, FiltersInfo, getFilterId } from '@shared/models/query/query.models'; -import { getWidgetExportDefinition } from '@shared/models/widget/widget-export.models'; +import { findWidgetModelDefinition } from '@shared/models/widget/widget-model.definition'; const WIDGET_ITEM = 'widget_item'; const WIDGET_REFERENCE = 'widget_reference'; @@ -142,7 +142,7 @@ export class ItemBufferService { } } let widgetExportInfo: any; - const exportDefinition = getWidgetExportDefinition(widget); + const exportDefinition = findWidgetModelDefinition(widget); if (exportDefinition) { widgetExportInfo = exportDefinition.prepareExportInfo(dashboard, widget); } @@ -270,7 +270,7 @@ export class ItemBufferService { let callFilterUpdateFunction = false; let newEntityAliases: EntityAliases; let newFilters: Filters; - const exportDefinition = getWidgetExportDefinition(widget); + const exportDefinition = findWidgetModelDefinition(widget); if (exportDefinition && widgetExportInfo || aliasesInfo) { newEntityAliases = deepClone(dashboard.configuration.entityAliases); } diff --git a/ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts index 235a41876d..f7bb87455a 100644 --- a/ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, DestroyRef, Inject, OnInit, SkipSelf } from '@angular/core'; +import { Component, DestroyRef, Inject, SkipSelf } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; @@ -60,7 +60,7 @@ export interface EntityAliasesDialogData { styleUrls: ['./entity-aliases-dialog.component.scss'] }) export class EntityAliasesDialogComponent extends DialogComponent - implements OnInit, ErrorStateMatcher { + implements ErrorStateMatcher { title: string; disableAdd: boolean; @@ -107,8 +107,7 @@ export class EntityAliasesDialogComponent extends DialogComponent { + this.dashboardUtils.getWidgetDatasources(widget).forEach((datasource) => { if ([DatasourceType.entity, DatasourceType.entityCount, DatasourceType.alarmCount].includes(datasource.type) && datasource.entityAliasId) { this.addWidgetTitleToWidgetsMap(datasource.entityAliasId, widget.config.title); @@ -143,7 +142,9 @@ export class EntityAliasesDialogComponent extends DialogComponent - implements OnInit, ErrorStateMatcher { + implements ErrorStateMatcher { title: string; disableAdd: boolean; @@ -96,15 +96,16 @@ export class FiltersDialogComponent extends DialogComponent { - const datasources = this.dashboardUtils.validateAndUpdateDatasources(widget.config.datasources); - datasources.forEach((datasource) => { - if (datasource.type === DatasourceType.entity && datasource.filterId) { + this.dashboardUtils.getWidgetDatasources(widget).forEach((datasource) => { + if (datasource.type !== DatasourceType.function && datasource.filterId) { widgetsTitleList = this.filterToWidgetsMap[datasource.filterId]; if (!widgetsTitleList) { widgetsTitleList = []; this.filterToWidgetsMap[datasource.filterId] = widgetsTitleList; } - widgetsTitleList.push(widget.config.title); + if (!widgetsTitleList.includes(widget.config.title)) { + widgetsTitleList.push(widget.config.title); + } } }); }); @@ -140,9 +141,6 @@ export class FiltersDialogComponent extends DialogComponent = { +export const MapModelDefinition: WidgetModelDefinition = { testWidget(widget: Widget): boolean { if (widget?.config?.settings) { const settings = widget.config.settings; @@ -103,6 +104,26 @@ export const MapExportDefinition: WidgetExportDefinition = { if (info?.additionalDataSources) { updateMapDatasourceFromExportInfo(entityAliases, filters, settings.additionalDataSources, info.additionalDataSources); } + }, + datasources(widget: Widget): Datasource[] { + const settings: BaseMapSettings = widget.config.settings as BaseMapSettings; + const datasources: Datasource[] = []; + if (settings.trips?.length) { + datasources.push(...getMapDataLayersDatasources(settings.trips)); + } + if (settings.markers?.length) { + datasources.push(...getMapDataLayersDatasources(settings.markers)); + } + if (settings.polygons?.length) { + datasources.push(...getMapDataLayersDatasources(settings.polygons)); + } + if (settings.circles?.length) { + datasources.push(...getMapDataLayersDatasources(settings.circles)); + } + if (settings.additionalDataSources?.length) { + datasources.push(...getMapDataLayersDatasources(settings.additionalDataSources)); + } + return datasources; } }; @@ -189,3 +210,16 @@ const prepareAliasAndFilterPair = (dashboard: Dashboard, settings: MapDataSource return null; } } + +const getMapDataLayersDatasources = (settings: MapDataLayerSettings[] | MapDataSourceSettings[]): Datasource[] => { + const datasources: Datasource[] = []; + settings.forEach((dsSettings) => { + datasources.push(mapDataSourceSettingsToDatasource(dsSettings)); + if ((dsSettings as MapDataLayerSettings).additionalDataSources?.length) { + (dsSettings as MapDataLayerSettings).additionalDataSources.forEach((ds) => { + datasources.push(mapDataSourceSettingsToDatasource(ds)); + }); + } + }); + return datasources; +}; diff --git a/ui-ngx/src/app/shared/models/widget/widget-export.models.ts b/ui-ngx/src/app/shared/models/widget/widget-model.definition.ts similarity index 68% rename from ui-ngx/src/app/shared/models/widget/widget-export.models.ts rename to ui-ngx/src/app/shared/models/widget/widget-model.definition.ts index 5b47010285..1b54240c09 100644 --- a/ui-ngx/src/app/shared/models/widget/widget-export.models.ts +++ b/ui-ngx/src/app/shared/models/widget/widget-model.definition.ts @@ -14,22 +14,23 @@ /// limitations under the License. /// -import { Widget } from '@shared/models/widget.models'; +import { Datasource, Widget } from '@shared/models/widget.models'; import { Dashboard } from '@shared/models/dashboard.models'; import { EntityAliases } from '@shared/models/alias.models'; import { Filters } from '@shared/models/query/query.models'; -import { MapExportDefinition } from '@shared/models/widget/maps/map-export.models'; +import { MapModelDefinition } from '@shared/models/widget/maps/map-model.definition'; -export interface WidgetExportDefinition { +export interface WidgetModelDefinition { testWidget(widget: Widget): boolean; prepareExportInfo(dashboard: Dashboard, widget: Widget): T; updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: T): void; + datasources(widget: Widget): Datasource[]; } -const widgetExportDefinitions: WidgetExportDefinition[] = [ - MapExportDefinition +const widgetModelRegistry: WidgetModelDefinition[] = [ + MapModelDefinition ]; -export const getWidgetExportDefinition = (widget: Widget): WidgetExportDefinition => { - return widgetExportDefinitions.find(def => def.testWidget(widget)); +export const findWidgetModelDefinition = (widget: Widget): WidgetModelDefinition => { + return widgetModelRegistry.find(def => def.testWidget(widget)); } From 8899a750b17e439c9a0e70b995eae372f40853e4 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 9 Jun 2025 18:04:20 +0300 Subject: [PATCH 313/335] Fix LwM2M client for monitoring --- monitoring/pom.xml | 1 - .../thingsboard/monitoring/client/Lwm2mClient.java | 13 +++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 15708fbdbf..d72efb3aa6 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -41,7 +41,6 @@ ${project.build.directory}/windows ThingsBoard Monitoring Service org.thingsboard.monitoring.ThingsboardMonitoringApplication - diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/client/Lwm2mClient.java b/monitoring/src/main/java/org/thingsboard/monitoring/client/Lwm2mClient.java index 599fe29525..893c9ec4b4 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/client/Lwm2mClient.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/client/Lwm2mClient.java @@ -56,7 +56,6 @@ import org.thingsboard.monitoring.util.ResourceUtils; import javax.security.auth.Destroyable; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -73,7 +72,7 @@ public class Lwm2mClient extends BaseInstanceEnabler implements Destroyable { @Setter private LeshanClient leshanClient; - private static final List supportedResources = Collections.singletonList(0); + private static final List supportedResources = List.of(0, 16); private String data = ""; @@ -158,6 +157,7 @@ public class Lwm2mClient extends BaseInstanceEnabler implements Destroyable { @Override public void onBootstrapFailure(LwM2mServer bsserver, BootstrapRequest request, ResponseCode responseCode, String errorMessage, Exception cause) { + log.debug("onBootstrapFailure [{}] [{}] [{}]", request.getEndpointName(), responseCode, errorMessage); // No implementation needed } @@ -245,10 +245,11 @@ public class Lwm2mClient extends BaseInstanceEnabler implements Destroyable { @Override public ReadResponse read(LwM2mServer server, int resourceId) { - if (supportedResources.contains(resourceId)) { - return ReadResponse.success(resourceId, data); - } - return super.read(server, resourceId); + return switch (resourceId) { + case 0 -> ReadResponse.success(0, data); + case 16 -> ReadResponse.success(16, "U"); + default -> super.read(server, resourceId); + }; } @SneakyThrows From 779e2461d88311e33b7318c2e513932c2cc23b53 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 9 Jun 2025 18:04:30 +0300 Subject: [PATCH 314/335] Fix CoAP monitoring --- .../service/transport/impl/CoapTransportHealthChecker.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/impl/CoapTransportHealthChecker.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/impl/CoapTransportHealthChecker.java index 57ecce2e33..0446060bb8 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/impl/CoapTransportHealthChecker.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/impl/CoapTransportHealthChecker.java @@ -20,6 +20,7 @@ import org.eclipse.californium.core.CoapClient; import org.eclipse.californium.core.CoapResponse; import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.eclipse.californium.elements.config.SystemConfig; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -35,6 +36,10 @@ import java.io.IOException; @Slf4j public class CoapTransportHealthChecker extends TransportHealthChecker { + static { + SystemConfig.register(); + } + private CoapClient coapClient; protected CoapTransportHealthChecker(CoapTransportMonitoringConfig config, TransportMonitoringTarget target) { From 28ee8f7609aa5fbd65e660eaedc35a15e595f604 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Mon, 9 Jun 2025 18:42:05 +0300 Subject: [PATCH 315/335] UI: not apply range previously selected by user if range option was not applied (another last/relative option selected) --- .../app/shared/components/time/timewindow-panel.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index b35d81b10d..d7a0d44013 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -294,7 +294,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O disabled: hideAggInterval }], fixedTimewindow: [{ - value: isDefined(history?.fixedTimewindow) ? history.fixedTimewindow : null, + value: isDefined(history?.fixedTimewindow) && this.timewindow.selectedTab === TimewindowType.HISTORY + && history.historyType === HistoryWindowType.FIXED ? history.fixedTimewindow : null, disabled: history.hideInterval || history.hideFixedInterval }], quickInterval: [{ From da90d8f727c567fc535ed7cd983c618e17a5a39d Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Tue, 10 Jun 2025 09:24:49 +0300 Subject: [PATCH 316/335] changed msgTs to latestTs --- .../controller/CalculatedFieldController.java | 4 +- .../DefaultCalculatedFieldQueueService.java | 2 +- .../ctx/state/BaseCalculatedFieldState.java | 8 ++- .../cf/ctx/state/CalculatedFieldState.java | 2 +- .../ctx/state/ScriptCalculatedFieldState.java | 2 +- .../ctx/state/SimpleCalculatedFieldState.java | 6 +- .../cf/CalculatedFieldIntegrationTest.java | 64 ++++++++++++++++++- .../script/api/tbel/TbelCfCtx.java | 6 +- .../calculated-field-dialog.component.scss | 2 +- .../shared/models/calculated-field.models.ts | 8 +-- .../en_US/calculated-field/expression_fn.md | 8 +-- .../assets/locale/locale.constant-en_US.json | 4 +- 12 files changed, 90 insertions(+), 26 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java index 3257b31ca4..2dcb32cf39 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java @@ -244,7 +244,7 @@ public class CalculatedFieldController extends BaseController { ); Object[] args = new Object[ctxAndArgNames.size()]; - args[0] = new TbelCfCtx(arguments, getLastUpdateTimestamp(arguments)); + args[0] = new TbelCfCtx(arguments, getLatestTimestamp(arguments)); for (int i = 1; i < ctxAndArgNames.size(); i++) { var arg = arguments.get(ctxAndArgNames.get(i)); if (arg instanceof TbelCfSingleValueArg svArg) { @@ -267,7 +267,7 @@ public class CalculatedFieldController extends BaseController { return result; } - private long getLastUpdateTimestamp(Map arguments) { + private long getLatestTimestamp(Map arguments) { long lastUpdateTimestamp = -1; for (TbelCfArg entry : arguments.values()) { if (entry instanceof TbelCfSingleValueArg singleValueArg) { diff --git a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java index 8289e4db42..81f19e1d1a 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldQueueService.java @@ -176,7 +176,7 @@ public class DefaultCalculatedFieldQueueService implements CalculatedFieldQueueS for (int i = 0; i < entries.size(); i++) { TsKvProto.Builder tsProtoBuilder = toTsKvProto(entries.get(i)).toBuilder(); - if (result != null) { + if (versions != null && !versions.isEmpty() && versions.get(i) != null) { tsProtoBuilder.setVersion(versions.get(i)); } telemetryMsg.addTsData(tsProtoBuilder.build()); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java index e4b03b4cab..e21d56b6d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java @@ -35,7 +35,7 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { protected Map arguments; protected boolean sizeExceedsLimit; - protected long lastUpdateTimestamp = -1; + protected long latestTimestamp = -1; public BaseCalculatedFieldState(List requiredArguments) { this.requiredArguments = requiredArguments; @@ -110,12 +110,14 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState { protected abstract void validateNewEntry(ArgumentEntry newEntry); private void updateLastUpdateTimestamp(ArgumentEntry entry) { + long newTs = this.latestTimestamp; if (entry instanceof SingleValueArgumentEntry singleValueArgumentEntry) { - this.lastUpdateTimestamp = singleValueArgumentEntry.getTs(); + newTs = singleValueArgumentEntry.getTs(); } else if (entry instanceof TsRollingArgumentEntry tsRollingArgumentEntry) { Map.Entry lastEntry = tsRollingArgumentEntry.getTsRecords().lastEntry(); - this.lastUpdateTimestamp = (lastEntry != null) ? lastEntry.getKey() : System.currentTimeMillis(); + newTs = (lastEntry != null) ? lastEntry.getKey() : System.currentTimeMillis(); } + this.latestTimestamp = Math.max(this.latestTimestamp, newTs); } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java index 6eac3358ba..0de354bbb0 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java @@ -42,7 +42,7 @@ public interface CalculatedFieldState { Map getArguments(); - long getLastUpdateTimestamp(); + long getLatestTimestamp(); void setRequiredArguments(List requiredArguments); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java index 65ef40330c..84dce627ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java @@ -66,7 +66,7 @@ public class ScriptCalculatedFieldState extends BaseCalculatedFieldState { args.add(arg); } } - args.set(0, new TbelCfCtx(arguments, getLastUpdateTimestamp())); + args.set(0, new TbelCfCtx(arguments, getLatestTimestamp())); ListenableFuture resultFuture = ctx.getCalculatedFieldScriptEngine().executeJsonAsync(args.toArray()); Output output = ctx.getOutput(); return Futures.transform(resultFuture, diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java index d0eba5031c..c8cdc7b4c0 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java @@ -87,10 +87,10 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { ObjectNode valuesNode = JacksonUtil.newObjectNode(); valuesNode.set(outputName, JacksonUtil.valueToTree(result)); - long lastTimestamp = getLastUpdateTimestamp(); - if (preserveMsgTs && lastTimestamp != -1) { + long latestTs = getLatestTimestamp(); + if (preserveMsgTs && latestTs != -1) { ObjectNode resultNode = JacksonUtil.newObjectNode(); - resultNode.put("ts", lastTimestamp); + resultNode.put("ts", latestTs); resultNode.set("values", valuesNode); return resultNode; } else { diff --git a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java index f65f6bc629..9742c7f618 100644 --- a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java @@ -505,6 +505,68 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes }); } + @Test + public void testSimpleCalculatedFieldWhenPreserveMsgTsIsTrueAndTelemetryBeforeLatest() throws Exception { + Device testDevice = createDevice("Test device", "1234567890"); + long ts = System.currentTimeMillis(); + + long tsA = ts - 300000L; + doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"a\":1}}", tsA))); + + long tsB = ts - 300L; + doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"b\":5}}", tsB))); + + CalculatedField calculatedField = new CalculatedField(); + calculatedField.setEntityId(testDevice.getId()); + calculatedField.setType(CalculatedFieldType.SIMPLE); + calculatedField.setName("a + b"); + calculatedField.setDebugSettings(DebugSettings.all()); + calculatedField.setConfigurationVersion(1); + + SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); + + Argument argument1 = new Argument(); + ReferencedEntityKey refEntityKey1 = new ReferencedEntityKey("a", ArgumentType.TS_LATEST, null); + argument1.setRefEntityKey(refEntityKey1); + Argument argument2 = new Argument(); + ReferencedEntityKey refEntityKey2 = new ReferencedEntityKey("b", ArgumentType.TS_LATEST, null); + argument2.setRefEntityKey(refEntityKey2); + config.setArguments(Map.of("a", argument1, "b", argument2)); + config.setExpression("a + b"); + + Output output = new Output(); + output.setName("c"); + output.setType(OutputType.TIME_SERIES); + config.setOutput(output); + + config.setPreserveMsgTs(true); + + calculatedField.setConfiguration(config); + + CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); + + await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode c = getLatestTelemetry(testDevice.getId(), "c"); + assertThat(c).isNotNull(); + assertThat(c.get("c").get(0).get("ts").asText()).isEqualTo(Long.toString(tsB)); + assertThat(c.get("c").get(0).get("value").asText()).isEqualTo("6.0"); + }); + + long tsABeforeTsB = tsB - 300L; + doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"b\":10}}", tsABeforeTsB))); + + await().alias("update telemetry with ts less than latest -> save result with latest ts").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) + .untilAsserted(() -> { + ObjectNode c = getLatestTelemetry(testDevice.getId(), "c"); + assertThat(c).isNotNull(); + assertThat(c.get("c").get(0).get("ts").asText()).isEqualTo(Long.toString(tsB));// also tsB, since this is the latest timestamp + assertThat(c.get("c").get(0).get("value").asText()).isEqualTo("11.0"); + }); + } + @Test public void testScriptCalculatedFieldWhenUsedMsgTsInScript() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); @@ -524,7 +586,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes ReferencedEntityKey refEntityKey = new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null); argument.setRefEntityKey(refEntityKey); config.setArguments(Map.of("T", argument)); - config.setExpression("return {\"ts\": ctx.msgTs, \"values\": {\"fahrenheitTemp\": (T * 1.8) + 32}};"); + config.setExpression("return {\"ts\": ctx.latestTs, \"values\": {\"fahrenheitTemp\": (T * 1.8) + 32}};"); Output output = new Output(); output.setType(OutputType.TIME_SERIES); diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java index 7515cb5269..c6023154ea 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfCtx.java @@ -25,11 +25,11 @@ public class TbelCfCtx implements TbelCfObject { @Getter private final Map args; @Getter - private final long msgTs; + private final long latestTs; - public TbelCfCtx(Map args, long lastUpdateTs) { + public TbelCfCtx(Map args, long latestTs) { this.args = Collections.unmodifiableMap(args); - this.msgTs = lastUpdateTs != -1 ? lastUpdateTs : System.currentTimeMillis(); + this.latestTs = latestTs != -1 ? latestTs : System.currentTimeMillis(); } @Override diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss index efcd62efd4..e192e3ccc0 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.scss @@ -45,7 +45,7 @@ &-key { color: #c24c1a; } - &-time-window, &-values, &-func, &-value, &-ts, &-msgTs { + &-time-window, &-values, &-func, &-value, &-ts, &-latestTs { color: #7214D0; } &-start-ts, &-end-ts { diff --git a/ui-ngx/src/app/shared/models/calculated-field.models.ts b/ui-ngx/src/app/shared/models/calculated-field.models.ts index 6fade1d193..76b775b2fd 100644 --- a/ui-ngx/src/app/shared/models/calculated-field.models.ts +++ b/ui-ngx/src/app/shared/models/calculated-field.models.ts @@ -526,10 +526,10 @@ export const getCalculatedFieldArgumentsEditorCompleter = (argumentsObj: Record< description: 'Calculated field context arguments.', children: {} }, - msgTs: { + latestTs: { meta: 'constant', type: 'number', - description: 'Timestamp (ms) of the telemetry message that triggered the calculated field execution.' + description: 'Latest timestamp (ms) of the arguments telemetry.' } } } @@ -582,8 +582,8 @@ const calculatedFieldArgumentsContextValueHighlightRules: AceHighlightRules = { next: 'calculatedFieldCtxArgs' }, { - token: 'tb.calculated-field-msgTs', - regex: /msgTs/, + token: 'tb.calculated-field-latestTs', + regex: /latestTs/, next: 'no_regex' }, endGroupHighlightRule diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md index 4c54b01499..1d45dea3c8 100644 --- a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md +++ b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md @@ -1,7 +1,7 @@ ## Calculated Field TBEL Script Function The **calculate()** function is a user-defined script that enables custom calculations using [TBEL](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) on telemetry and attribute data. -It receives arguments configured in the calculated field setup, along with an additional `ctx` object that stores `msgTs` and provides access to all arguments. +It receives arguments configured in the calculated field setup, along with an additional `ctx` object that stores `latestTs` and provides access to all arguments. ### Function Signature @@ -216,14 +216,14 @@ The return format depends on the output type configured in the calculated field ### Message timestamp -The `ctx` object also includes property `msgTs`, which represents the timestamp of the incoming telemetry message that triggered the calculated field execution in milliseconds. +The `ctx` object also includes property `latestTs`, which represents the latest timestamp of the arguments telemetry in milliseconds. -You can use `ctx.msgTs` to set the timestamp of the resulting output explicitly when returning a time series object. +You can use `ctx.latestTs` to set the timestamp of the resulting output explicitly when returning a time series object. ```javascript var temperatureC = (temperatureF - 32) / 1.8; return { - ts: ctx.msgTs, + ts: ctx.latestTs, values: { "temperatureC": toFixed(temperatureC, 2) } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5afc040daf..a0bb9c6052 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1069,7 +1069,7 @@ "delete-multiple-title": "Are you sure you want to delete { count, plural, =1 {1 calculated field} other {# calculated fields} }?", "delete-multiple-text": "Be careful, after the confirmation all selected calculated fields will be removed and all related data will become unrecoverable.", "test-with-this-message": "Test with this message", - "use-message-timestamp": "Use message timestamp", + "use-message-timestamp": "Use latest timestamp", "hint": { "arguments-simple-with-rolling": "Simple type calculated field should not contain keys with time series rolling type.", "arguments-empty": "Arguments should not be empty.", @@ -1086,7 +1086,7 @@ "decimals-range": "Decimals by default should be a number between 0 and 15.", "expression": "Default expression demonstrates how to transform a temperature from Fahrenheit to Celsius.", "arguments-entity-not-found": "Argument target entity not found.", - "use-message-timestamp": "If enabled, the calculated value will be persisted using the timestamp of the telemetry that triggered the calculation, instead of the server time." + "use-message-timestamp": "If enabled, the calculated value will be persisted using the most recent timestamp from the arguments telemetry, instead of the server time." } }, "confirm-on-exit": { From cce9b8f24d948f34a4bd6f3ed7ffd14ff8147bc1 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 10 Jun 2025 19:19:38 +0300 Subject: [PATCH 317/335] EdgeGrpcService - destroy if previous session exists --- .../server/service/edge/rpc/EdgeGrpcService.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index 5c95a0e98f..5671ffb2ab 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -335,6 +335,9 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i Edge edge = edgeGrpcSession.getEdge(); TenantId tenantId = edge.getTenantId(); log.info("[{}][{}] edge [{}] connected successfully.", tenantId, edgeGrpcSession.getSessionId(), edgeId); + if (sessions.containsKey(edgeId)) { + destroySession(sessions.get(edgeId)); + } sessions.put(edgeId, edgeGrpcSession); final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock()); newEventLock.lock(); @@ -503,7 +506,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i } finally { newEventLock.unlock(); } - toRemove.destroy(); + destroySession(toRemove); TenantId tenantId = toRemove.getEdge().getTenantId(); save(tenantId, edgeId, ACTIVITY_STATE, false); long lastDisconnectTs = System.currentTimeMillis(); @@ -516,6 +519,12 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i edgeIdServiceIdCache.evict(edgeId); } + private void destroySession(EdgeGrpcSession session) { + try (session) { + session.destroy(); + } + } + private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { log.debug("[{}][{}] Updating long edge telemetry [{}] [{}]", tenantId, edgeId, key, value); if (persistToTelemetry) { From 1692c9b92ab1f843db7e41becc94574b07a73683 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 10 Jun 2025 19:22:00 +0300 Subject: [PATCH 318/335] EDQS - fixed relations query in case multiple previous path are present --- .../query/processor/AbstractRelationQueryProcessor.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractRelationQueryProcessor.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractRelationQueryProcessor.java index 2842d57ff0..193f3bb22e 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractRelationQueryProcessor.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractRelationQueryProcessor.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.edqs.query.processor; +import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import org.thingsboard.server.common.data.permission.QueryContext; import org.thingsboard.server.common.data.query.EntityFilter; @@ -106,7 +107,7 @@ public abstract class AbstractRelationQueryProcessor ext private Set> getEntitiesSet(RelationsRepo relations) { Set> result = new HashSet<>(); - Set processed = new HashSet<>(); + Set processed = new HashSet<>(); Queue tasks = new LinkedList<>(); int maxLvl = getMaxLevel() == 0 ? MAXIMUM_QUERY_LEVEL : Math.max(1, getMaxLevel()); for (UUID uuid : getRootEntities()) { @@ -114,7 +115,7 @@ public abstract class AbstractRelationQueryProcessor ext } while (!tasks.isEmpty()) { RelationSearchTask task = tasks.poll(); - if (processed.add(task.entityId)) { + if (processed.add(task)) { var entityLvl = task.lvl + 1; Set entities = EntitySearchDirection.FROM.equals(getDirection()) ? relations.getFrom(task.entityId) : relations.getTo(task.entityId); if (isFetchLastLevelOnly() && entities.isEmpty() && task.previous != null && check(task.previous)) { @@ -157,6 +158,7 @@ public abstract class AbstractRelationQueryProcessor ext protected abstract boolean check(RelationInfo relationInfo); @RequiredArgsConstructor + @EqualsAndHashCode private static class RelationSearchTask { private final UUID entityId; private final int lvl; From e21850f5713547da7ebd86733d61cea7936a7008 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Tue, 10 Jun 2025 19:29:16 +0300 Subject: [PATCH 319/335] EDQS - added SQL like style for filter contains/starts with/ends with --- ...stractEntityProfileNameQueryProcessor.java | 2 +- .../AbstractEntityProfileQueryProcessor.java | 2 +- .../processor/EntityNameQueryProcessor.java | 2 +- .../server/edqs/util/RepositoryUtils.java | 60 +++++++++++++------ .../server/edqs/repo/RepositoryUtilsTest.java | 40 ++++++++++++- 5 files changed, 84 insertions(+), 22 deletions(-) diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileNameQueryProcessor.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileNameQueryProcessor.java index b78e49879e..f881d616ef 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileNameQueryProcessor.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileNameQueryProcessor.java @@ -36,7 +36,7 @@ public abstract class AbstractEntityProfileNameQueryProcessor(getProfileNames(this.filter)); - pattern = RepositoryUtils.toSqlLikePattern(getEntityNameFilter(filter)); + pattern = RepositoryUtils.toContainsSqlLikePattern(getEntityNameFilter(filter)); } protected abstract String getEntityNameFilter(T filter); diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileQueryProcessor.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileQueryProcessor.java index 301ead7c63..9d043ff6fc 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileQueryProcessor.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileQueryProcessor.java @@ -43,7 +43,7 @@ public abstract class AbstractEntityProfileQueryProcessor value.equals(predicateValue); - case STARTS_WITH -> value.startsWith(predicateValue); - case ENDS_WITH -> value.endsWith(predicateValue); + case STARTS_WITH -> toStartsWithSqlLikePattern(predicateValue).matcher(value).matches(); + case ENDS_WITH -> toEndsWithSqlLikePattern(predicateValue).matcher(value).matches(); case NOT_EQUAL -> !value.equals(predicateValue); - case CONTAINS -> value.contains(predicateValue); - case NOT_CONTAINS -> !value.contains(predicateValue); + case CONTAINS -> toContainsSqlLikePattern(predicateValue).matcher(value).matches(); + case NOT_CONTAINS -> !toContainsSqlLikePattern(predicateValue).matcher(value).matches(); case IN -> equalsAny(value, splitByCommaWithoutQuotes(predicateValue)); case NOT_IN -> !equalsAny(value, splitByCommaWithoutQuotes(predicateValue)); }; @@ -304,6 +304,15 @@ public class RepositoryUtils { return true; } else if (filterPredicates.getOperation() == OR) { for (KeyFilterPredicate filterPredicate : filterPredicates.getPredicates()) { + + // Emulate the SQL-like behavior of ThingsBoard's Entity Data Query service: + // for COMPLEX filters, return no results if filter value is empty + if (filterPredicate instanceof StringFilterPredicate stringFilterPredicate) { + if (StringUtils.isEmpty(stringFilterPredicate.getValue().getValue())) { + continue; + } + } + if (simpleKeyFilter.check(value, filterPredicate)) { return true; } @@ -314,23 +323,38 @@ public class RepositoryUtils { } } - public static Pattern toSqlLikePattern(String nameFilter) { - if (StringUtils.isNotBlank(nameFilter)) { - boolean percentSymbolOnStart = nameFilter.startsWith("%"); - boolean percentSymbolOnEnd = nameFilter.endsWith("%"); - if (percentSymbolOnStart) { - nameFilter = nameFilter.substring(1); - } - if (percentSymbolOnEnd) { - nameFilter = nameFilter.substring(0, nameFilter.length() - 1); - } - if (percentSymbolOnStart || percentSymbolOnEnd) { - return Pattern.compile((percentSymbolOnStart ? ".*" : "") + Pattern.quote(nameFilter) + (percentSymbolOnEnd ? ".*" : ""), Pattern.CASE_INSENSITIVE); + public static Pattern toContainsSqlLikePattern(String filter) { + if (StringUtils.isNotBlank(filter)) { + return toSqlLikePattern(filter, ".*", ".*"); + } + return null; + } + + private static Pattern toStartsWithSqlLikePattern(String filter) { + return toSqlLikePattern(filter, "^", ".*"); + } + + private static Pattern toEndsWithSqlLikePattern(String filter) { + return toSqlLikePattern(filter, ".*", "$"); + } + + private static Pattern toSqlLikePattern(String value, String prefix, String suffix ) { + if (value.contains("%") || value.contains("_")) { + String regexValue = value + .replace("_", ".") + .replace("%", ".*"); + String regex; + if ("^".equals(prefix)) { + regex = "^" + regexValue + (regexValue.endsWith(".*") ? "" : ".*"); + } else if ("$".equals(suffix)) { + regex = (regexValue.startsWith(".*") ? "" : ".*") + regexValue + "$"; } else { - return Pattern.compile(Pattern.quote(nameFilter) + ".*", Pattern.CASE_INSENSITIVE); + regex = (regexValue.startsWith(".*") ? "" : ".*") + regexValue + (regexValue.endsWith(".*") ? "" : ".*"); } + return Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + } else { + return Pattern.compile(prefix + Pattern.quote(value) + suffix, Pattern.CASE_INSENSITIVE); } - return null; } @FunctionalInterface diff --git a/edqs/src/test/java/org/thingsboard/server/edqs/repo/RepositoryUtilsTest.java b/edqs/src/test/java/org/thingsboard/server/edqs/repo/RepositoryUtilsTest.java index 6c7444c92a..fa3784ca19 100644 --- a/edqs/src/test/java/org/thingsboard/server/edqs/repo/RepositoryUtilsTest.java +++ b/edqs/src/test/java/org/thingsboard/server/edqs/repo/RepositoryUtilsTest.java @@ -70,7 +70,45 @@ public class RepositoryUtilsTest { Arguments.of("loranet 123", getNameFilter(StringOperation.IN, "loranet 123, loranet 124"), true), Arguments.of("loranet 123", getNameFilter(StringOperation.IN, "loranet 125, loranet 126"), false), Arguments.of("loranet 123", getNameFilter(StringOperation.NOT_IN, "loranet 125, loranet 126"), true), - Arguments.of("loranet 123", getNameFilter(StringOperation.NOT_IN, "loranet 123, loranet 126"), false) + Arguments.of("loranet 123", getNameFilter(StringOperation.NOT_IN, "loranet 123, loranet 126"), false), + + // Basic CONTAINS + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%loranet"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "loranet%"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%ranet%"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%123"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%loranx%"), false), + + // Basic STARTS_WITH + Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "loranet%"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "lora%"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "lorax%"), false), + + // Basic ENDS_WITH + Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "%123"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "%23"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "%124"), false), + + // CONTAINS with _ + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "loranet_123"), true), // '_' = ' ' + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "loranet_12_"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "loran_t%"), true), + + // STARTS_WITH with _ + Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "loranet_"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "lora__t%"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "lor_net%"), true), + + // ENDS_WITH with _ + Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "_23"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "_2_"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "_3"), true), + + // Mixed patterns + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "lora__t 1%"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "lora%net%3"), true), + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%o_anet%2_3"), false), + Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "lora___ ___"), true) ); } From 58aa930d1ddf9a9fcd1525171a207f04ba4a9ff2 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Wed, 11 Jun 2025 08:17:40 +0300 Subject: [PATCH 320/335] renamed property --- .../service/cf/ctx/state/CalculatedFieldCtx.java | 4 ++-- .../cf/ctx/state/SimpleCalculatedFieldState.java | 6 +++--- .../server/cf/CalculatedFieldIntegrationTest.java | 10 +++++----- .../SimpleCalculatedFieldConfiguration.java | 2 +- .../dialog/calculated-field-dialog.component.html | 2 +- .../dialog/calculated-field-dialog.component.ts | 14 +++++++------- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java index a3fdae319d..dff715a6e8 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java @@ -62,7 +62,7 @@ public class CalculatedFieldCtx { private final List argNames; private Output output; private String expression; - private boolean preserveMsgTs; + private boolean useLatestTs; private TbelInvokeService tbelInvokeService; private CalculatedFieldScriptEngine calculatedFieldScriptEngine; private ThreadLocal customExpression; @@ -96,7 +96,7 @@ public class CalculatedFieldCtx { this.argNames = new ArrayList<>(arguments.keySet()); this.output = configuration.getOutput(); this.expression = configuration.getExpression(); - this.preserveMsgTs = CalculatedFieldType.SIMPLE.equals(calculatedField.getType()) && ((SimpleCalculatedFieldConfiguration) configuration).isPreserveMsgTs(); + this.useLatestTs = CalculatedFieldType.SIMPLE.equals(calculatedField.getType()) && ((SimpleCalculatedFieldConfiguration) configuration).isUseLatestTs(); this.tbelInvokeService = tbelInvokeService; this.maxDataPointsPerRollingArg = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getMaxDataPointsPerRollingArg); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java index c8cdc7b4c0..111624882b 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java @@ -68,7 +68,7 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { Output output = ctx.getOutput(); Object result = formatResult(expressionResult, output.getDecimalsByDefault()); - JsonNode outputResult = createResultJson(ctx.isPreserveMsgTs(), output.getName(), result); + JsonNode outputResult = createResultJson(ctx.isUseLatestTs(), output.getName(), result); return Futures.immediateFuture(new CalculatedFieldResult(output.getType(), output.getScope(), outputResult)); } @@ -83,12 +83,12 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { return TbUtils.toFixed(expressionResult, decimals); } - private JsonNode createResultJson(boolean preserveMsgTs, String outputName, Object result) { + private JsonNode createResultJson(boolean useLatestTs, String outputName, Object result) { ObjectNode valuesNode = JacksonUtil.newObjectNode(); valuesNode.set(outputName, JacksonUtil.valueToTree(result)); long latestTs = getLatestTimestamp(); - if (preserveMsgTs && latestTs != -1) { + if (useLatestTs && latestTs != -1) { ObjectNode resultNode = JacksonUtil.newObjectNode(); resultNode.put("ts", latestTs); resultNode.set("values", valuesNode); diff --git a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java index 9742c7f618..8214a03616 100644 --- a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java @@ -464,7 +464,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes } @Test - public void testSimpleCalculatedFieldWhenPreserveMsgTsIsTrue() throws Exception { + public void testSimpleCalculatedFieldWhenUseLatestTsIsTrue() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); long ts = System.currentTimeMillis() - 300000L; doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"temperature\":30}}", ts))); @@ -489,7 +489,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes output.setType(OutputType.TIME_SERIES); config.setOutput(output); - config.setPreserveMsgTs(true); + config.setUseLatestTs(true); calculatedField.setConfiguration(config); @@ -506,7 +506,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes } @Test - public void testSimpleCalculatedFieldWhenPreserveMsgTsIsTrueAndTelemetryBeforeLatest() throws Exception { + public void testSimpleCalculatedFieldWhenUseLatestTsIsTrueAndTelemetryBeforeLatest() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); long ts = System.currentTimeMillis(); @@ -539,7 +539,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes output.setType(OutputType.TIME_SERIES); config.setOutput(output); - config.setPreserveMsgTs(true); + config.setUseLatestTs(true); calculatedField.setConfiguration(config); @@ -568,7 +568,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes } @Test - public void testScriptCalculatedFieldWhenUsedMsgTsInScript() throws Exception { + public void testScriptCalculatedFieldWhenUsedLatestTsInScript() throws Exception { Device testDevice = createDevice("Test device", "1234567890"); long ts = System.currentTimeMillis() - 300000L; doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"temperature\":30}}", ts))); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java index af3cb4d5cd..86c7b9e9b6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; @EqualsAndHashCode(callSuper = true) public class SimpleCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration { - private boolean preserveMsgTs; + private boolean useLatestTs; @Override public CalculatedFieldType getType() { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html index 60f33ff005..47463e8da6 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html @@ -190,7 +190,7 @@
- +
calculated-fields.use-message-timestamp
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts index 4aa4eca425..975744b2c6 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts @@ -77,7 +77,7 @@ export class CalculatedFieldDialogComponent extends DialogComponent Date: Wed, 11 Jun 2025 11:28:25 +0300 Subject: [PATCH 321/335] renamed keys --- .../components/dialog/calculated-field-dialog.component.html | 4 ++-- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html index 47463e8da6..7b69d26a60 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html @@ -191,8 +191,8 @@
-
- calculated-fields.use-message-timestamp +
+ calculated-fields.use-latest-timestamp
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 990979d405..a98b2aecf9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1069,7 +1069,7 @@ "delete-multiple-title": "Are you sure you want to delete { count, plural, =1 {1 calculated field} other {# calculated fields} }?", "delete-multiple-text": "Be careful, after the confirmation all selected calculated fields will be removed and all related data will become unrecoverable.", "test-with-this-message": "Test with this message", - "use-message-timestamp": "Use latest timestamp", + "use-latest-timestamp": "Use latest timestamp", "hint": { "arguments-simple-with-rolling": "Simple type calculated field should not contain keys with time series rolling type.", "arguments-empty": "Arguments should not be empty.", @@ -1086,7 +1086,7 @@ "decimals-range": "Decimals by default should be a number between 0 and 15.", "expression": "Default expression demonstrates how to transform a temperature from Fahrenheit to Celsius.", "arguments-entity-not-found": "Argument target entity not found.", - "use-message-timestamp": "If enabled, the calculated value will be persisted using the most recent timestamp from the arguments telemetry, instead of the server time." + "use-latest-timestamp": "If enabled, the calculated value will be persisted using the most recent timestamp from the arguments telemetry, instead of the server time." } }, "confirm-on-exit": { From aaead182634cb583dfcd79ad11d808d1bb9ee1c7 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Wed, 11 Jun 2025 12:16:56 +0300 Subject: [PATCH 322/335] Fixed case sensitive matcher --- .../org/thingsboard/server/edqs/util/RepositoryUtils.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/RepositoryUtils.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/RepositoryUtils.java index 1a47c4814f..1550c1df30 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/util/RepositoryUtils.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/util/RepositoryUtils.java @@ -54,7 +54,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -338,7 +337,7 @@ public class RepositoryUtils { return toSqlLikePattern(filter, ".*", "$"); } - private static Pattern toSqlLikePattern(String value, String prefix, String suffix ) { + private static Pattern toSqlLikePattern(String value, String prefix, String suffix) { if (value.contains("%") || value.contains("_")) { String regexValue = value .replace("_", ".") @@ -351,9 +350,9 @@ public class RepositoryUtils { } else { regex = (regexValue.startsWith(".*") ? "" : ".*") + regexValue + (regexValue.endsWith(".*") ? "" : ".*"); } - return Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + return Pattern.compile(regex); } else { - return Pattern.compile(prefix + Pattern.quote(value) + suffix, Pattern.CASE_INSENSITIVE); + return Pattern.compile(prefix + Pattern.quote(value) + suffix); } } From cba1972109c173d1c29797b943ed48b9223bf40e Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 11 Jun 2025 14:43:22 +0300 Subject: [PATCH 323/335] fixed null pointer exception --- .../service/sync/ie/importing/impl/ResourceImportService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java index 96fcdd5425..37639f6a90 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/ResourceImportService.java @@ -66,7 +66,7 @@ public class ResourceImportService extends BaseEntityImportService Date: Wed, 11 Jun 2025 16:38:44 +0300 Subject: [PATCH 324/335] fixed updating entry with old telemetry --- .../service/cf/ctx/state/SingleValueArgumentEntry.java | 2 +- .../service/cf/ctx/state/SingleValueArgumentEntryTest.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java index 3b858e81b2..1585c9b2a9 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntry.java @@ -107,7 +107,7 @@ public class SingleValueArgumentEntry implements ArgumentEntry { @Override public boolean updateEntry(ArgumentEntry entry) { if (entry instanceof SingleValueArgumentEntry singleValueEntry) { - if (singleValueEntry.getTs() == this.ts) { + if (singleValueEntry.getTs() <= this.ts) { return false; } diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java index 50cac8a6fe..5ea3808468 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SingleValueArgumentEntryTest.java @@ -53,7 +53,7 @@ public class SingleValueArgumentEntryTest { } @Test - void testUpdateEntryWithThaSameTs() { + void testUpdateEntryWithTheSameTs() { assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts, new LongDataEntry("key", 13L), 363L))).isFalse(); } @@ -81,6 +81,11 @@ public class SingleValueArgumentEntryTest { assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts + 18, new LongDataEntry("key", 11L), 364L))).isTrue(); } + @Test + void testUpdateEntryWithOldTs() { + assertThat(entry.updateEntry(new SingleValueArgumentEntry(ts - 10, new LongDataEntry("key", 14L), 365L))).isFalse(); + } + @Test void testToTbelCfArgWhenJsonIsObject() { entry = new SingleValueArgumentEntry(ts, new JsonDataEntry("key", "{\"test\": 10}"), 370L); From 1042ab8d16956ded6615691ec8aabd9a1a27a674 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Wed, 11 Jun 2025 19:29:59 +0300 Subject: [PATCH 325/335] UI: fixed updating timewindow configuration --- ui-ngx/src/app/shared/models/time/time.models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index acb62d9d2a..c204cb6867 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -431,7 +431,7 @@ export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalO if (isDefined(value.history.quickInterval)) { model.history.quickInterval = value.history.quickInterval; } - if (isDefined(value.history.fixedTimewindow)) { + if (isDefinedAndNotNull(value.history.fixedTimewindow)) { if (isDefined(value.history.fixedTimewindow.startTimeMs)) { model.history.fixedTimewindow.startTimeMs = value.history.fixedTimewindow.startTimeMs; } From 866f03cd7139a6eed9f818750bfee3cd85129e72 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 12 Jun 2025 07:48:29 +0300 Subject: [PATCH 326/335] fixed test --- .../thingsboard/server/cf/CalculatedFieldIntegrationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java index 8214a03616..c8b8b0244b 100644 --- a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java @@ -555,7 +555,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes }); long tsABeforeTsB = tsB - 300L; - doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"b\":10}}", tsABeforeTsB))); + doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(String.format("{\"ts\": %s, \"values\": {\"a\":10}}", tsABeforeTsB))); await().alias("update telemetry with ts less than latest -> save result with latest ts").atMost(TIMEOUT, TimeUnit.SECONDS) .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) @@ -563,7 +563,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes ObjectNode c = getLatestTelemetry(testDevice.getId(), "c"); assertThat(c).isNotNull(); assertThat(c.get("c").get(0).get("ts").asText()).isEqualTo(Long.toString(tsB));// also tsB, since this is the latest timestamp - assertThat(c.get("c").get(0).get("value").asText()).isEqualTo("11.0"); + assertThat(c.get("c").get(0).get("value").asText()).isEqualTo("15.0"); }); } From a51600f30ea6bdb53c3ddbe004da7944d771534c Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 13 Jun 2025 11:21:42 +0300 Subject: [PATCH 327/335] UI: Fixed knob and digital gauge decimals setting --- .../gauge/digital-simple-gauge-basic-config.component.ts | 7 +++++-- .../home/components/widget/lib/rpc/knob.component.ts | 4 ++-- .../control/knob-control-widget-settings.component.ts | 2 +- .../gauge/digital-gauge-widget-settings.component.ts | 6 +++++- .../app/shared/models/widget/rpc/knob.component.models.ts | 4 ++-- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts index 67497b0b6e..9e2c46ec13 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts @@ -30,7 +30,7 @@ import { getTimewindowConfig, setTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component'; -import { formatValue, isUndefined } from '@core/utils'; +import { formatValue, isDefinedAndNotNull, isUndefined } from '@core/utils'; import { Component } from '@angular/core'; import { convertLevelColorsSettingsToColorProcessor, @@ -115,7 +115,7 @@ export class DigitalSimpleGaugeBasicConfigComponent extends BasicWidgetConfigCom minMaxColor: [settings.minMaxFont?.color, []], showValue: [settings.showValue, []], - decimals: [configData.config.decimals, []], + decimals: [configData.config.decimals || settings.decimals, []], units: [configData.config.units, []], valueFont: [settings.valueFont, []], valueColor: [settings.valueFont?.color, []], @@ -157,6 +157,9 @@ export class DigitalSimpleGaugeBasicConfigComponent extends BasicWidgetConfigCom this.widgetConfig.config.settings.showValue = config.showValue; this.widgetConfig.config.units = config.units; this.widgetConfig.config.decimals = config.decimals; + if (isDefinedAndNotNull(this.widgetConfig.config.settings.decimals)) { + this.widgetConfig.config.settings.decimals = null; + } this.widgetConfig.config.settings.valueFont = config.valueFont; this.widgetConfig.config.settings.valueFont.color = config.valueColor; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts index 8c1fc3ab2a..1b93538b6c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts @@ -145,7 +145,7 @@ export class KnobComponent extends BasicActionWidgetComponent implements OnInit, this.valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, { units: this.ctx.units, - decimals: this.ctx.decimals, + decimals: this.ctx.decimals || 0, showZeroDecimals: true }); @@ -299,7 +299,7 @@ export class KnobComponent extends BasicActionWidgetComponent implements OnInit, } private turn(ratio: number) { - this.newValue = Number((this.minValue + (this.maxValue - this.minValue) * ratio).toFixed(this.ctx.decimals)); + this.newValue = Number((this.minValue + (this.maxValue - this.minValue) * ratio).toFixed(this.ctx.decimals || 0)); if (this.canvasBar.value !== this.newValue) { this.canvasBar.value = this.newValue; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts index e5c57474c2..26a5d6cd4e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts @@ -57,7 +57,7 @@ export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { const knobSettings = prepareKnobSettings(deepClone(settings) as any) as WidgetSettings; - knobSettings.valueDecimals = this.widgetConfig?.config?.decimals ?? 2; + knobSettings.valueDecimals = this.widgetConfig?.config?.decimals; knobSettings.valueUnits = deepClone(this.widgetConfig?.config?.units); return super.prepareInputSettings(knobSettings); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts index 7276c0e27d..9f095cdff0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts @@ -37,7 +37,7 @@ import { digitalGaugeLayoutTranslations, DigitalGaugeType } from '@home/components/widget/lib/digital-gauge.models'; -import { formatValue } from '@core/utils'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; import { ColorSettings, ColorType, @@ -247,6 +247,10 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent settings.titleFont.color = this.digitalGaugeWidgetSettingsForm.get('titleColor').value; settings.labelFont.color = this.digitalGaugeWidgetSettingsForm.get('labelColor').value; + if (isDefinedAndNotNull(settings.decimals)) { + settings.decimals = null; + } + return settings; } diff --git a/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts b/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts index 47b73f5a08..3ac15bf70c 100644 --- a/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts +++ b/ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts @@ -45,7 +45,7 @@ export const knobWidgetDefaultSettings: KnobSettings = { defaultValue: 50, executeRpc: { method: 'getValue', - requestTimeout: 500, + requestTimeout: 5000, requestPersistent: false, persistentPollingInterval: 5000 }, @@ -70,7 +70,7 @@ export const knobWidgetDefaultSettings: KnobSettings = { action: SetValueAction.EXECUTE_RPC, executeRpc: { method: 'setValue', - requestTimeout: 500, + requestTimeout: 5000, requestPersistent: false, persistentPollingInterval: 5000 }, From f7bc348513aee3247941c93931d676d1bcd9481b Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 13 Jun 2025 11:53:33 +0300 Subject: [PATCH 328/335] UI: delete unused settings --- .../gauge/digital-simple-gauge-basic-config.component.ts | 6 +++--- .../home/components/widget/lib/rpc/knob.component.ts | 6 ++++-- .../gauge/digital-gauge-widget-settings.component.ts | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts index 9e2c46ec13..aa93a56a4c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts @@ -30,7 +30,7 @@ import { getTimewindowConfig, setTimewindowConfig } from '@home/components/widget/config/timewindow-config-panel.component'; -import { formatValue, isDefinedAndNotNull, isUndefined } from '@core/utils'; +import { formatValue, isDefined, isUndefined } from '@core/utils'; import { Component } from '@angular/core'; import { convertLevelColorsSettingsToColorProcessor, @@ -157,8 +157,8 @@ export class DigitalSimpleGaugeBasicConfigComponent extends BasicWidgetConfigCom this.widgetConfig.config.settings.showValue = config.showValue; this.widgetConfig.config.units = config.units; this.widgetConfig.config.decimals = config.decimals; - if (isDefinedAndNotNull(this.widgetConfig.config.settings.decimals)) { - this.widgetConfig.config.settings.decimals = null; + if (isDefined(this.widgetConfig.config.settings.decimals)) { + delete this.widgetConfig.config.settings.decimals; } this.widgetConfig.config.settings.valueFont = config.valueFont; this.widgetConfig.config.settings.valueFont.color = config.valueColor; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts index 1b93538b6c..f28b5e8f92 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts @@ -57,6 +57,7 @@ export class KnobComponent extends BasicActionWidgetComponent implements OnInit, maxValue: number; newValue = 0; + private decimals: number; private startDeg = -1; private currentDeg = 0; private rotation = 0; @@ -143,9 +144,10 @@ export class KnobComponent extends BasicActionWidgetComponent implements OnInit, actionLabel: this.ctx.translate.instant('widgets.slider.on-value-change')}; this.valueSetter = this.createValueSetter(valueChangeSettings); + this.decimals = isDefined(this.ctx.decimals) ? this.ctx.decimals : 0; this.valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, { units: this.ctx.units, - decimals: this.ctx.decimals || 0, + decimals: this.decimals, showZeroDecimals: true }); @@ -299,7 +301,7 @@ export class KnobComponent extends BasicActionWidgetComponent implements OnInit, } private turn(ratio: number) { - this.newValue = Number((this.minValue + (this.maxValue - this.minValue) * ratio).toFixed(this.ctx.decimals || 0)); + this.newValue = Number((this.minValue + (this.maxValue - this.minValue) * ratio).toFixed(this.decimals)); if (this.canvasBar.value !== this.newValue) { this.canvasBar.value = this.newValue; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts index 9f095cdff0..1f3ae36089 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts @@ -37,7 +37,7 @@ import { digitalGaugeLayoutTranslations, DigitalGaugeType } from '@home/components/widget/lib/digital-gauge.models'; -import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { formatValue, isDefined } from '@core/utils'; import { ColorSettings, ColorType, @@ -247,8 +247,8 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent settings.titleFont.color = this.digitalGaugeWidgetSettingsForm.get('titleColor').value; settings.labelFont.color = this.digitalGaugeWidgetSettingsForm.get('labelColor').value; - if (isDefinedAndNotNull(settings.decimals)) { - settings.decimals = null; + if (isDefined(settings.decimals)) { + delete settings.decimals; } return settings; From 722aa6a77bf7b1fc8361806e639392a814410845 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 13 Jun 2025 11:59:08 +0300 Subject: [PATCH 329/335] UI: Fixed set decimals value --- .../basic/gauge/digital-simple-gauge-basic-config.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts index aa93a56a4c..7d348662ae 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/gauge/digital-simple-gauge-basic-config.component.ts @@ -115,7 +115,7 @@ export class DigitalSimpleGaugeBasicConfigComponent extends BasicWidgetConfigCom minMaxColor: [settings.minMaxFont?.color, []], showValue: [settings.showValue, []], - decimals: [configData.config.decimals || settings.decimals, []], + decimals: [isDefined(configData.config.decimals) ? configData.config.decimals : settings.decimals, []], units: [configData.config.units, []], valueFont: [settings.valueFont, []], valueColor: [settings.valueFont?.color, []], From 7ba8e1366cbbe80981a70bb9d91f61471038335a Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 13 Jun 2025 12:06:22 +0300 Subject: [PATCH 330/335] fixed server failure when cf configuration is invalid --- .../service/cf/DefaultCalculatedFieldCache.java | 1 - .../configuration/CalculatedFieldConfiguration.java | 2 ++ .../cf/DefaultNativeCalculatedFieldRepository.java | 12 ++++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java index ed35d96cb7..3988cb38db 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java @@ -70,7 +70,6 @@ public class DefaultCalculatedFieldCache implements CalculatedFieldCache { @AfterStartUp(order = AfterStartUp.CF_READ_CF_SERVICE) public void init() { - //TODO: move to separate place to avoid circular references with the ActorSystemContext (@Lazy for tsSubService) PageDataIterable cfs = new PageDataIterable<>(calculatedFieldService::findAllCalculatedFields, initFetchPackSize); cfs.forEach(cf -> { calculatedFields.putIfAbsent(cf.getId(), cf); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java index c53f1fe5f1..ad3d4373ad 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.cf.configuration; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.thingsboard.server.common.data.cf.CalculatedFieldLink; @@ -36,6 +37,7 @@ import java.util.Map; @JsonSubTypes.Type(value = SimpleCalculatedFieldConfiguration.class, name = "SIMPLE"), @JsonSubTypes.Type(value = ScriptCalculatedFieldConfiguration.class, name = "SCRIPT") }) +@JsonIgnoreProperties(ignoreUnknown = true) public interface CalculatedFieldConfiguration { @JsonIgnore diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java index e59ff3f4e6..a01ad517b8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.page.PageData; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; @@ -79,7 +80,6 @@ public class DefaultNativeCalculatedFieldRepository implements NativeCalculatedF JsonNode configuration = JacksonUtil.toJsonNode((String) row.get("configuration")); long version = row.get("version") != null ? (long) row.get("version") : 0; String debugSettings = (String) row.get("debug_settings"); - Object externalIdObj = row.get("external_id"); CalculatedField calculatedField = new CalculatedField(); calculatedField.setId(new CalculatedFieldId(id)); @@ -89,12 +89,17 @@ public class DefaultNativeCalculatedFieldRepository implements NativeCalculatedF calculatedField.setType(type); calculatedField.setName(name); calculatedField.setConfigurationVersion(configurationVersion); - calculatedField.setConfiguration(JacksonUtil.treeToValue(configuration, CalculatedFieldConfiguration.class)); + try { + calculatedField.setConfiguration(JacksonUtil.treeToValue(configuration, CalculatedFieldConfiguration.class)); + } catch (Exception e) { + log.error("Invalid configuration for CalculatedField [{}]. Skipping.", id, e); + return null; + } calculatedField.setVersion(version); calculatedField.setDebugSettings(JacksonUtil.fromString(debugSettings, DebugSettings.class)); return calculatedField; - }).collect(Collectors.toList()); + }).filter(Objects::nonNull).collect(Collectors.toList()); return new PageData<>(data, totalPages, totalElements, hasNext); }); } @@ -118,7 +123,6 @@ public class DefaultNativeCalculatedFieldRepository implements NativeCalculatedF EntityType entityType = EntityType.valueOf((String) row.get("entity_type")); UUID entityId = (UUID) row.get("entity_id"); UUID calculatedFieldId = (UUID) row.get("calculated_field_id"); - JsonNode configuration = JacksonUtil.toJsonNode((String) row.get("configuration")); CalculatedFieldLink calculatedFieldLink = new CalculatedFieldLink(); calculatedFieldLink.setId(new CalculatedFieldLinkId(id)); From 43429524364e1e7904157924e2cf8b3fa609f944 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 13 Jun 2025 12:17:11 +0300 Subject: [PATCH 331/335] UI: Change mime type for csv export file --- ui-ngx/src/app/shared/import-export/import-export.models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/import-export/import-export.models.ts b/ui-ngx/src/app/shared/import-export/import-export.models.ts index 2f20a421c3..60df84c737 100644 --- a/ui-ngx/src/app/shared/import-export/import-export.models.ts +++ b/ui-ngx/src/app/shared/import-export/import-export.models.ts @@ -160,7 +160,7 @@ export const ZIP_TYPE: FileType = { }; export const CSV_TYPE: FileType = { - mimeType: 'attachament/csv', + mimeType: 'text/csv', extension: 'csv' }; From edb3ab4ca020b6b03e93a9ab1d5fe0d56d34f3c3 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 13 Jun 2025 12:25:16 +0300 Subject: [PATCH 332/335] removed links to the doc --- .../src/assets/help/en_US/rulenode/switch_node_script_fn.md | 6 ------ .../help/en_US/rulenode/tbel/switch_node_script_fn.md | 6 ------ 2 files changed, 12 deletions(-) diff --git a/ui-ngx/src/assets/help/en_US/rulenode/switch_node_script_fn.md b/ui-ngx/src/assets/help/en_US/rulenode/switch_node_script_fn.md index c4092455fa..49f0dd0a2b 100644 --- a/ui-ngx/src/assets/help/en_US/rulenode/switch_node_script_fn.md +++ b/ui-ngx/src/assets/help/en_US/rulenode/switch_node_script_fn.md @@ -91,11 +91,5 @@ return []; {:copy-code} ``` -
- -You can see real life example, how to use this node in this tutorial: - -- [Data function based on telemetry from 2 devices{:target="_blank"}](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/rule-engine-2-0/tutorials/function-based-on-telemetry-from-two-devices#delta-temperature-rule-chain) -

diff --git a/ui-ngx/src/assets/help/en_US/rulenode/tbel/switch_node_script_fn.md b/ui-ngx/src/assets/help/en_US/rulenode/tbel/switch_node_script_fn.md index 2461f4cace..77da936c73 100644 --- a/ui-ngx/src/assets/help/en_US/rulenode/tbel/switch_node_script_fn.md +++ b/ui-ngx/src/assets/help/en_US/rulenode/tbel/switch_node_script_fn.md @@ -91,11 +91,5 @@ return []; {:copy-code} ``` -
- -You can see real life example, how to use this node in this tutorial: - -- [Data function based on telemetry from 2 devices{:target="_blank"}](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/rule-engine-2-0/tutorials/function-based-on-telemetry-from-two-devices#delta-temperature-rule-chain) -

From 8ae39d4e9416d204453c91d66e04e2713711c1b3 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 13 Jun 2025 12:59:25 +0300 Subject: [PATCH 333/335] moved check to higher level --- .../server/service/cf/DefaultCalculatedFieldCache.java | 6 ++++-- .../dao/sql/cf/DefaultNativeCalculatedFieldRepository.java | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java index 3988cb38db..78042ffc0e 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldCache.java @@ -72,8 +72,10 @@ public class DefaultCalculatedFieldCache implements CalculatedFieldCache { public void init() { PageDataIterable cfs = new PageDataIterable<>(calculatedFieldService::findAllCalculatedFields, initFetchPackSize); cfs.forEach(cf -> { - calculatedFields.putIfAbsent(cf.getId(), cf); - actorSystemContext.tell(new CalculatedFieldInitMsg(cf.getTenantId(), cf)); + if (cf != null) { + calculatedFields.putIfAbsent(cf.getId(), cf); + actorSystemContext.tell(new CalculatedFieldInitMsg(cf.getTenantId(), cf)); + } }); calculatedFields.values().forEach(cf -> { entityIdCalculatedFields.computeIfAbsent(cf.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(cf); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java index a01ad517b8..bbce4e2721 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java @@ -38,7 +38,6 @@ import org.thingsboard.server.common.data.page.PageData; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; @@ -99,7 +98,7 @@ public class DefaultNativeCalculatedFieldRepository implements NativeCalculatedF calculatedField.setDebugSettings(JacksonUtil.fromString(debugSettings, DebugSettings.class)); return calculatedField; - }).filter(Objects::nonNull).collect(Collectors.toList()); + }).collect(Collectors.toList()); return new PageData<>(data, totalPages, totalElements, hasNext); }); } From aea21e8dad84a23318128941f4c974f42c4298e3 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 16 Jun 2025 16:15:20 +0300 Subject: [PATCH 334/335] UI: make credentials expansion paned expanded by default --- .../rule-node/common/credentials-config.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html index e0d1714e2c..7f7d50db3e 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/common/credentials-config.component.html @@ -16,7 +16,7 @@ -->
- + rule-node-config.credentials From 8b641cb973cfc82c2ff57eaf5d2ff58740b2d6fc Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 18 Jun 2025 07:14:38 +0300 Subject: [PATCH 335/335] Fix testCancelJob_whileRunning --- .../java/org/thingsboard/server/service/job/JobManagerTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java index 8da1be43f1..af499ade31 100644 --- a/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java +++ b/application/src/test/java/org/thingsboard/server/service/job/JobManagerTest.java @@ -165,7 +165,6 @@ public class JobManagerTest extends AbstractControllerTest { await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { Job job = findJobById(jobId); assertThat(job.getStatus()).isEqualTo(JobStatus.CANCELLED); - assertThat(job.getResult().getSuccessfulCount()).isBetween(1, tasksCount - 1); assertThat(job.getResult().getDiscardedCount()).isBetween(1, tasksCount - 1); assertThat(job.getResult().getTotalCount()).isEqualTo(tasksCount); assertThat(job.getResult().getCompletedCount()).isEqualTo(tasksCount);